301xl.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger, Sean Riddle
/*******************************************************************************

Yeno 301 XL

NOTE: It triggers an IRQ when the power switch is changed from ON to SAVE.
If this is not done, NVRAM won't save properly.

It's by the same programmer as Chess King Triomphe / Counter Gambit, also using
the TRAP interrupt for the beeper routine. The ROM data contains (C)1988Bray,
it appears that Intelligent Chess Software went defunct around that time.

Hardware notes:
- PCB label: WSE 8108A
- Hitachi HD63B01X0P, 8MHz XTAL
- 8*8 chessboard buttons, 16+1 LEDs, piezo

Yeno 309 XT is on the same PCB, and has the same MCU ROM.

TODO:
- if/when MAME supports an exit callback, hook up power-off switch to that

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

#include "emu.h"

#include "cpu/m6800/m6801.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "yeno_301xl.lh"


namespace {

class y301xl_state : public driver_device
{
public:
	y301xl_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(power_off);

	void y301xl(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<hd6301x0_cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<2> m_inputs;

	emu_timer *m_standbytimer;
	u16 m_inp_mux = 0;

	// I/O handlers
	u8 input_r();
	void control_w(u8 data);
	void board_w(u8 data);

	TIMER_CALLBACK_MEMBER(set_standby);
};

void y301xl_state::machine_start()
{
	m_standbytimer = timer_alloc(FUNC(y301xl_state::set_standby), this);

	// register for savestates
	save_item(NAME(m_inp_mux));
}



/*******************************************************************************
    Power
*******************************************************************************/

void y301xl_state::machine_reset()
{
	m_maincpu->set_input_line(HD6301_IRQ1_LINE, CLEAR_LINE);
	m_maincpu->set_input_line(M6801_STBY_LINE, CLEAR_LINE);
}

TIMER_CALLBACK_MEMBER(y301xl_state::set_standby)
{
	m_maincpu->set_input_line(M6801_STBY_LINE, ASSERT_LINE);
}

INPUT_CHANGED_MEMBER(y301xl_state::power_off)
{
	if (newval && !m_maincpu->standby())
	{
		// IRQ1 when power switch is set to SAVE, followed by STBY after a short delay
		m_maincpu->set_input_line(HD6301_IRQ1_LINE, ASSERT_LINE);
		m_standbytimer->adjust(attotime::from_msec(50));
	}
}



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

u8 y301xl_state::input_r()
{
	// P20-P27: multiplexed inputs
	u8 data = 0;

	// read buttons
	for (int i = 0; i < 2; i++)
		if (BIT(m_inp_mux, i + 8))
			data |= m_inputs[i]->read();

	// read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_file(i);

	return ~data;
}

void y301xl_state::control_w(u8 data)
{
	// P40,P41: input mux (buttons)
	m_inp_mux = (m_inp_mux & 0xff) | (~data << 8 & 0x300);

	// P42: green led (direct)
	// P44,P45: led select
	m_display->write_my(bitswap<3>(~data,2,5,4));

	// P47: speaker out
	m_dac->write(BIT(~data, 7));
}

void y301xl_state::board_w(u8 data)
{
	// P60-P67: input mux (chessboard), led data
	m_inp_mux = (m_inp_mux & 0x300) | (data ^ 0xff);
	m_display->write_mx(~data);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( y301xl )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("King")
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Sound")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("Play")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Set Up")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("Black")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("White")

	PORT_START("POWER") // needs to be triggered for nvram to work
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(y301xl_state::power_off), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void y301xl_state::y301xl(machine_config &config)
{
	// basic machine hardware
	HD6301X0(config, m_maincpu, 8_MHz_XTAL);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(hd6301x0_cpu_device::nvram_set_battery));
	m_maincpu->standby_cb().append([this](int state) { if (state) m_display->clear(); });
	m_maincpu->in_p2_cb().set(FUNC(y301xl_state::input_r));
	m_maincpu->out_p4_cb().set(FUNC(y301xl_state::control_w));
	m_maincpu->out_p6_cb().set(FUNC(y301xl_state::board_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(2+1, 8);
	config.set_default_layout(layout_yeno_301xl);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( y301xl )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("yeno_301_xl_hd63b01x0c70p", 0x0000, 0x1000, CRC(6da5ee23) SHA1(f37298c56c5130783c86fb03fab78d86c20467e5) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1988, y301xl, 0,      0,      y301xl,  y301xl, y301xl_state, empty_init, "Yeno / Bray Research", "301 XL (Yeno)", MACHINE_SUPPORTS_SAVE )



3do.cpp
<---------------------------------------------------------------------->
// license:LGPL-2.1+
// copyright-holders:Angelo Salese, Wilbert Pol
/***************************************************************************

  3do.cpp

  Driver file to handle emulation of the 3DO systems

Hardware descriptions:

Processors:
- 32bit 12.5MHZ RISC CPU (ARM60 - ARM6 core)
- Separate BUS for video refresh updates (VRAM is dual ported)
- Super Fast BUS Speed (50 Megabytes per second)
- Math Co-Processor custom designed by NTG for accelerating fixed-point
  matrix operations (_not_ the ARM FPA)
- Multitaking 32-bit operating system

Resolution:
- 640x480 pixel resolution
- 16.7 million colors

Two accelerated video co-processors:
- 25MHZ clock rate (NTSC), 29.5MHZ clock rate (PAL)
- Capable of producing 9-16 million real pixels per second (36-64 Mpix/sec
  interpolated), distorted, scaled, rotated and texture mapped.
- able to map a rectangular bitmap onto any arbitrary 4-point polygon.
- texturemap source bitmaps can be 1, 2, 4, 6, 8, or 16 bits per pixel and
  are RLE compressed for a maximum combination of both high resolution and
  small storage space.
- supports transparency, translucency, and color-shading effects.

Custom 16bit DSP:
- specifically designed for mixing, manipulating, and synthesizing CD quality
  sound.
- can decompress sound 2:1 or 4:1 on the fly saving memory and bus bandwidth.
- 25MHz clock rate.
- pipelined CISC architecture
- 16bit register size
- 17 separate 16bit DMA channels to and from system memory.
- on chip instruction SRAM and register memory.
- 20bit internal processing.
- special filtering capable of creating effects such as 3D sound.

Sound:
- 16bit stereo sound
- 44.1 kHz sound sampling rate
- Fully support Dolby(tm) Surround Sound

Memory:
- 2 megabytes of DRAM
- 1 megabyte of VRAM (also capable of holding/executing code and data)
- 1 megabyte of ROM
- 32KB battery backed SRAM

CD-ROM drive:
- 320ms access time
- double speed 300kbps data transfer
- 32KB RAM buffer

Ports:
- 2 expansion ports:
  - 1 high-speed 68 pin x 1 AV I/O port (for FMV cartridge)
  - 1 high-speed 30 pin x 1 I/O expansion port
- 1 control port, capable of daisy chaining together up to 8 peripherals

Models:
- Panasonic FZ-1 R.E.A.L. 3DO Interactive Multiplayer (Japan, Asia, North America, Europe)
- Panasonic FZ-10 R.E.A.L. 3DO Interactive Multiplayer (Japan, North America, Europe)
- Goldstar 3DO Interactive Multiplayer (South Korea, North America, Europe)
- Goldstar 3DO ALIVE II (South Korea)
- Sanyo TRY 3DO Interactive Multiplayer (Japan)
- Creative 3DO Blaster - PC Card (ISA)

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

Part list of Goldstar 3DO Interactive Multiplayer

- X1 = 50.0000 MHz KONY 95-08 50.0000 KCH089C
- X2 = 59.0000 MHz KONY 95-21 59.0000 KCH089C (NTSC would use 49.09MHz)
- IC303 BOB = 3DO BOB ADG 00919-001-IC 517A4611 - 100 pins
- IC1 ANVIL = 3DO Anvil rev4 00745-004-02 521U5L36 - 304 pins
- IC302 DSP = SONY CXD2500BQ 447HE5V - 80 pins
- IC601 ADAC = BB PCM1710U 9436 GG2553 - 28 pins
- X601 16.934MHz = 16.93440 KONY
- IC101/102/103/104 DRAM = Goldstar GM71C4800AJ70 9520 KOREA - 28 pins
- IC105/106/107/108 VRAM = Toshiba TC528267J-70 9513HBK - 40 pins
- IC3 ROM = Goldstar [202M] GM23C8000AFW-325 9524 - 32 pins
- IC4 SRAM = Goldstar GM76C256ALLFW70 - 28 pins
- IC2 ARM = ARM P60ARMCP 9516C - 100 pins
- IC6 = Philips 74HCT14D 974230Q - 14 pins
- IC301 u-COM = MC68HSC 705C8ACFB 3E20T HLAH9446 - 44 pins

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

#include "emu.h"
#include "3do.h"

#include "cpu/arm/arm.h"
#include "cpu/arm7/arm7.h"
#include "imagedev/cdromimg.h"


#define X2_CLOCK_PAL    59000000
#define X2_CLOCK_NTSC   49090000
#define X601_CLOCK      XTAL(16'934'400)


void _3do_state::main_mem(address_map &map)
{
	map(0x00000000, 0x001FFFFF).bankrw(m_bank1);                                       /* DRAM */
	map(0x00200000, 0x003FFFFF).ram().share(m_vram);                                   /* VRAM */
	map(0x03000000, 0x030FFFFF).rom().region("bios", 0);                               /* BIOS */
	map(0x03100000, 0x0313FFFF).ram();                                                 /* Brooktree? */
	map(0x03140000, 0x0315FFFF).rw(FUNC(_3do_state::nvarea_r), FUNC(_3do_state::nvarea_w)).umask32(0x000000ff);                /* NVRAM */
	map(0x03180000, 0x031BFFFF).rw(FUNC(_3do_state::slow2_r), FUNC(_3do_state::slow2_w));               /* Slow bus - additional expansion */
	map(0x03200000, 0x0320FFFF).rw(FUNC(_3do_state::svf_r), FUNC(_3do_state::svf_w));                   /* special vram access1 */
	map(0x03300000, 0x033FFFFF).rw(FUNC(_3do_state::madam_r), FUNC(_3do_state::madam_w));               /* address decoder */
	map(0x03400000, 0x034FFFFF).rw(FUNC(_3do_state::clio_r), FUNC(_3do_state::clio_w));                 /* io controller */
}


static INPUT_PORTS_START( 3do )
	PORT_START("P1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START1 )
INPUT_PORTS_END

void _3do_state::machine_start()
{
	m_nvram->set_base(&m_nvmem, sizeof(m_nvmem));

	/* configure overlay */
	// TODO: can overlay at 0-0x1FFFFF even be written to, or writes go to dram in any case?
	m_bank1->configure_entry(0, m_dram);
	m_bank1->configure_entry(1, memregion("overlay")->base());

	m_slow2_init();
	m_madam_init();
	m_clio_init();
}

void _3do_state::machine_reset()
{
	/* start with overlay enabled */
	m_bank1->set_entry(1);

	m_clio.cstatbits = 0x01; /* bit 0 = reset of clio caused by power on */
}

void _3do_state::_3do(machine_config &config)
{
	/* Basic machine hardware */
	ARM7_BE(config, m_maincpu, XTAL(50'000'000)/4);
	m_maincpu->set_addrmap(AS_PROGRAM, &_3do_state::main_mem);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	TIMER(config, "timer_x16").configure_periodic(FUNC(_3do_state::timer_x16_cb), attotime::from_hz(12000)); // TODO: timing

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(X2_CLOCK_NTSC / 2, 1592, 254, 1534, 263, 22, 262);
	m_screen->set_screen_update(FUNC(_3do_state::screen_update));

	CDROM(config, "cdrom");
}


void _3do_state::_3do_pal(machine_config &config)
{
	/* Basic machine hardware */
	ARM7_BE(config, m_maincpu, XTAL(50'000'000)/4);
	m_maincpu->set_addrmap(AS_PROGRAM, &_3do_state::main_mem);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	TIMER(config, "timer_x16").configure_periodic(FUNC(_3do_state::timer_x16_cb), attotime::from_hz(12000)); // TODO: timing

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(X2_CLOCK_PAL / 2, 1592, 254, 1534, 263, 22, 262); // TODO: proper params
	m_screen->set_screen_update(FUNC(_3do_state::screen_update));

	CDROM(config, "cdrom");
}

#if 0
#define NTSC_BIOS \
	ROM_REGION32_BE( 0x200000, "bios", 0 ) \
	ROM_SYSTEM_BIOS( 0, "panafz10", "Panasonic FZ-10 R.E.A.L. 3DO Interactive Multiplayer" ) \
	ROMX_LOAD( "panafz10.bin", 0x000000, 0x100000, CRC(58242cee) SHA1(3c912300775d1ad730dc35757e279c274c0acaad), ROM_BIOS(0) ) \
	ROM_SYSTEM_BIOS( 1, "goldstar", "Goldstar 3DO Interactive Multiplayer v1.01m" ) \
	ROMX_LOAD( "goldstar.bin", 0x000000, 0x100000, CRC(b6f5028b) SHA1(c4a2e5336f77fb5f743de1eea2cda43675ee2de7), ROM_BIOS(1) ) \
	ROM_SYSTEM_BIOS( 2, "panafz1", "Panasonic FZ-1 R.E.A.L. 3DO Interactive Multiplayer" ) \
	ROMX_LOAD( "panafz1.bin", 0x000000, 0x100000, CRC(c8c8ff89) SHA1(34bf189111295f74d7b7dfc1f304d98b8d36325a), ROM_BIOS(2) ) \
	ROM_SYSTEM_BIOS( 3, "gsalive2", "Goldstar 3DO Alive II" ) \
	ROMX_LOAD( "gsalive2.bin", 0x000000, 0x100000, NO_DUMP, ROM_BIOS(3) ) \
	ROM_SYSTEM_BIOS( 4, "sanyotry", "Sanyo TRY 3DO Interactive Multiplayer" ) \
	ROMX_LOAD( "sanyotry.bin", 0x000000, 0x100000, CRC(d5cbc509) SHA1(b01c53da256dde43ffec4ad3fc3adfa8d635e943), ROM_BIOS(4) )
#else
#define NTSC_BIOS \
	ROM_REGION32_BE( 0x200000, "bios", 0 ) \
	ROM_SYSTEM_BIOS( 0, "panafz10", "Panasonic FZ-10 R.E.A.L. 3DO Interactive Multiplayer" ) \
	ROMX_LOAD( "panafz10.bin", 0x000000, 0x100000, CRC(58242cee) SHA1(3c912300775d1ad730dc35757e279c274c0acaad), ROM_BIOS(0) ) \
	ROM_SYSTEM_BIOS( 1, "goldstar", "Goldstar 3DO Interactive Multiplayer v1.01m" ) \
	ROMX_LOAD( "goldstar.bin", 0x000000, 0x100000, CRC(b6f5028b) SHA1(c4a2e5336f77fb5f743de1eea2cda43675ee2de7), ROM_BIOS(1) ) \
	ROM_SYSTEM_BIOS( 2, "panafz1", "Panasonic FZ-1 R.E.A.L. 3DO Interactive Multiplayer" ) \
	ROMX_LOAD( "panafz1.bin", 0x000000, 0x100000, CRC(c8c8ff89) SHA1(34bf189111295f74d7b7dfc1f304d98b8d36325a), ROM_BIOS(2) ) \
	ROM_SYSTEM_BIOS( 3, "sanyotry", "Sanyo TRY 3DO Interactive Multiplayer" ) \
	ROMX_LOAD( "sanyotry.bin", 0x000000, 0x100000, CRC(d5cbc509) SHA1(b01c53da256dde43ffec4ad3fc3adfa8d635e943), ROM_BIOS(3) ) \
	ROM_REGION32_BE( 0x200000, "overlay", 0 ) \
	ROM_COPY( "bios", 0, 0, 0x200000 )
#endif

ROM_START(3do)
	NTSC_BIOS
ROM_END

ROM_START(3dobios)
	NTSC_BIOS
ROM_END

ROM_START(3do_pal)
	ROM_REGION32_BE( 0x200000, "bios", 0 )
	ROM_SYSTEM_BIOS( 0, "panafz10", "Panasonic FZ-10 R.E.A.L. 3DO Interactive Multiplayer" )
	ROMX_LOAD( "panafz10.bin", 0x000000, 0x100000, CRC(58242cee) SHA1(3c912300775d1ad730dc35757e279c274c0acaad), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "goldstar", "Goldstar 3DO Interactive Multiplayer v1.01m" )
	ROMX_LOAD( "goldstar.bin", 0x000000, 0x100000, CRC(b6f5028b) SHA1(c4a2e5336f77fb5f743de1eea2cda43675ee2de7), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "panafz1", "Panasonic FZ-1 R.E.A.L. 3DO Interactive Multiplayer" )
	ROMX_LOAD( "panafz1.bin", 0x000000, 0x100000, CRC(c8c8ff89) SHA1(34bf189111295f74d7b7dfc1f304d98b8d36325a), ROM_BIOS(2) )

	ROM_REGION32_BE( 0x200000, "overlay", 0 )
	ROM_COPY( "bios", 0, 0, 0x200000 )
ROM_END

ROM_START(orbatak)
	NTSC_BIOS

	DISK_REGION( "cdrom" )
	DISK_IMAGE_READONLY( "orbatak", 0, SHA1(25cb3b889cf09dbe5faf2b0ca4aae5e03453da00) )
ROM_END

#define ALG_BIOS \
	ROM_REGION32_BE( 0x200000, "bios", 0 ) \
	/* TC544000AF-150, 1xxxxxxxxxxxxxxxxxx = 0xFF */ \
	ROM_LOAD( "saot_rom2.bin", 0x000000, 0x80000, CRC(b832da9a) SHA1(520d3d1b5897800af47f92efd2444a26b7a7dead) )  \
	ROM_REGION32_BE( 0x200000, "overlay", 0 ) \
	ROM_COPY( "bios", 0, 0, 0x200000 )


ROM_START(alg3do)
	ALG_BIOS
ROM_END


ROM_START(md23do)
	ALG_BIOS

	DISK_REGION( "cdrom" )
	DISK_IMAGE_READONLY( "mad dog ii", 0, SHA1(0117c1fd279f42e942648ca55fa75dd45da37a4f) )
ROM_END

ROM_START(sht3do)
	ALG_BIOS

	DISK_REGION( "cdrom" )
	DISK_IMAGE_READONLY( "shootout at old tucson", 0, SHA1(bd42213c6b460b5b6153a8b2b41d0a114171e86e) )
ROM_END


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

  Game driver(s)

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

/*    YEAR  NAME     PARENT  COMPAT  MACHINE     INPUT   STATE       INIT        COMPANY            FULLNAME      FLAGS */
// console section
CONS( 1993, 3do,     0,      0,      _3do,       3do,    _3do_state, empty_init, "The 3DO Company", "3DO (NTSC)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
CONS( 1993, 3do_pal, 3do,    0,      _3do_pal,   3do,    _3do_state, empty_init, "The 3DO Company", "3DO (PAL)",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND )

/*    YEAR  NAME     PARENT   MACHINE  INPUT  STATE       INIT        MONITOR   COMPANY                 FULLNAME               FLAGS */
// Misc 3do Arcade games
GAME( 1993, 3dobios, 0,       _3do,    3do,   _3do_state, empty_init, ROT0,     "The 3DO Company",      "3DO BIOS",            MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_IS_BIOS_ROOT )

GAME( 1995, orbatak, 3dobios, _3do,    3do,   _3do_state, empty_init, ROT0,     "American Laser Games", "Orbatak (prototype)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
// Beavis and Butthead (prototype)


// American Laser Games uses its own BIOS (with additional protection according to serial output?)
GAME( 1993, alg3do, 0,       _3do,    3do,   _3do_state, empty_init, ROT0,     "American Laser Games / The 3DO Company", "ALG 3DO BIOS",            MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_IS_BIOS_ROOT )

GAME( 199?, md23do,  alg3do, _3do,    3do,   _3do_state, empty_init, ROT0,     "American Laser Games", "Mad Dog II: The Lost Gold (3DO hardware)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
GAME( 1994, sht3do,  alg3do, _3do,    3do,   _3do_state, empty_init, ROT0,     "American Laser Games", "Shootout at Old Tucson (3DO hardware)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )




4000_260.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    ADDS 4000/260

    ASCII/ANSI terminal

    Hardware:
    - P-80C32-16
    - KM622560LP-7L (32k RAM)
    - CY6225LL-70 (32k RAM)
    - LSI Victor 006-9802760 REV B
    - KM622560LP-7L x2 (32k RAM x2)
    - 16 MHz XTAL, 44.976 MHz XTAL

    TODO:
    - Everything

    Notes:
    - Other models in this line: 4000/260C, 4000/260LF, 4000/260LFC
    - Sold under the ADDS brand, but ADDS was part of SunRiver Data Systems
      by then, which became Boundless Technologies.

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

#include "emu.h"

#include "cpu/mcs51/mcs51.h"


namespace {


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

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

	void _4000_260(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<i80c32_device> m_maincpu;

	void mem_map(address_map &map) ATTR_COLD;
};


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

void _4000_260_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).rom();
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( _4000_260 )
INPUT_PORTS_END


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

void _4000_260_state::machine_start()
{
}

void _4000_260_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void _4000_260_state::_4000_260(machine_config &config)
{
	I80C32(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &_4000_260_state::mem_map);
}


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

ROM_START( 4000_260 )
	ROM_REGION(0x40000, "maincpu", 0)
	// 598-0010669 3.16 SunRiver Data Systems 1995
	ROM_LOAD("4000_260.bin", 0x00000, 0x40000, CRC(b957cd1d) SHA1(1b1185174ba95dca004169e4e1b51b05c8991c43))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME      PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY  FULLNAME    FLAGS
COMP( 1995, 4000_260, 0,      0,      _4000_260, _4000_260, _4000_260_state, empty_init, "ADDS",  "4000/260", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



4004clk.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

        4004 Nixie Clock

        03/08/2009 Initial driver

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

#include "emu.h"

#include "cpu/mcs40/mcs40.h"
#include "machine/clock.h"
#include "sound/dac.h"

#include "speaker.h"

#include "4004clk.lh"


namespace {

class nixieclock_state : public driver_device
{
public:
	nixieclock_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_test_line(*this, "test_line")
		, m_nixie_out(*this, "nixie%u", 0U)
		, m_neon_out(*this, "neon%u", 0U)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(switch_hz) { m_test_line->set_clock(newval ? 60 : 50); }
	void _4004clk(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<i4004_cpu_device> m_maincpu;
	required_device<clock_device> m_test_line;
	output_finder<6> m_nixie_out;
	output_finder<4> m_neon_out;

	void nixie_w(offs_t offset, uint8_t data);
	void neon_w(uint8_t data);

	void _4004clk_mem(address_map &map) ATTR_COLD;
	void _4004clk_mp(address_map &map) ATTR_COLD;
	void _4004clk_rom(address_map &map) ATTR_COLD;
	void _4004clk_rp(address_map &map) ATTR_COLD;
	void _4004clk_stat(address_map &map) ATTR_COLD;

	static constexpr uint8_t nixie_to_num(uint16_t val)
	{
		return
				(BIT(val, 0)) ? 0 :
				(BIT(val, 1)) ? 1 :
				(BIT(val, 2)) ? 2 :
				(BIT(val, 3)) ? 3 :
				(BIT(val, 4)) ? 4 :
				(BIT(val, 5)) ? 5 :
				(BIT(val, 6)) ? 6 :
				(BIT(val, 7)) ? 7 :
				(BIT(val, 8)) ? 8 :
				(BIT(val, 9)) ? 9 :
				10;
	}

	uint16_t m_nixie[16];
};

void nixieclock_state::machine_start()
{
	m_nixie_out.resolve();
	m_neon_out.resolve();

	std::fill(std::begin(m_nixie), std::end(m_nixie), 0);
	save_item(NAME(m_nixie));
}


// I/O handlers

void nixieclock_state::nixie_w(offs_t offset, uint8_t data)
{
	m_nixie[offset >> 4] = data;
	m_nixie_out[5] = nixie_to_num(((m_nixie[2] & 3) << 8) | (m_nixie[1] << 4) | m_nixie[0]);
	m_nixie_out[4] = nixie_to_num((m_nixie[4] << 6) | (m_nixie[3] << 2) | (m_nixie[2] >> 2));
	m_nixie_out[3] = nixie_to_num(((m_nixie[7] & 3) << 8) | (m_nixie[6] << 4) | m_nixie[5]);
	m_nixie_out[2] = nixie_to_num((m_nixie[9] << 6) | (m_nixie[8] << 2) | (m_nixie[7] >> 2));
	m_nixie_out[1] = nixie_to_num(((m_nixie[12] & 3) << 8) | (m_nixie[11] << 4) | m_nixie[10]);
	m_nixie_out[0] = nixie_to_num((m_nixie[14] << 6) | (m_nixie[13] << 2) | (m_nixie[12] >> 2));
}

void nixieclock_state::neon_w(uint8_t data)
{
	m_neon_out[0] = BIT(data,3);
	m_neon_out[1] = BIT(data,2);
	m_neon_out[2] = BIT(data,1);
	m_neon_out[3] = BIT(data,0);
}


// Address maps

void nixieclock_state::_4004clk_rom(address_map &map)
{
	map(0x0000, 0x0fff).rom().region("maincpu", 0);
}

void nixieclock_state::_4004clk_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x007f).ram();
}

void nixieclock_state::_4004clk_stat(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x001f).ram();
}

void nixieclock_state::_4004clk_rp(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x000f).mirror(0x0700).portr("INPUT");
	map(0x0000, 0x00ef).mirror(0x0700).w(FUNC(nixieclock_state::nixie_w));
	map(0x00f0, 0x00ff).mirror(0x0700).w(FUNC(nixieclock_state::neon_w));
}

void nixieclock_state::_4004clk_mp(address_map &map)
{
	map(0x00, 0x00).w("dac", FUNC(dac_bit_interface::data_w));
}


// Input ports

static INPUT_PORTS_START( 4004clk )
	PORT_START("INPUT")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Select") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Browse") PORT_CODE(KEYCODE_2)

	PORT_CONFNAME( 0x04, 0x00, "Tick-Tock")
	PORT_CONFSETTING( 0x00, "ON" )
	PORT_CONFSETTING( 0x04, "OFF" )
	PORT_CONFNAME( 0x08, 0x08, "50/60 Hz") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(nixieclock_state::switch_hz), 0)
	PORT_CONFSETTING( 0x00, "50 Hz" )
	PORT_CONFSETTING( 0x08, "60 Hz" )
INPUT_PORTS_END


// Machine config

void nixieclock_state::_4004clk(machine_config &config)
{
	// basic machine hardware
	I4004(config, m_maincpu, 5_MHz_XTAL / 8);
	m_maincpu->set_rom_map(&nixieclock_state::_4004clk_rom);
	m_maincpu->set_ram_memory_map(&nixieclock_state::_4004clk_mem);
	m_maincpu->set_rom_ports_map(&nixieclock_state::_4004clk_rp);
	m_maincpu->set_ram_status_map(&nixieclock_state::_4004clk_stat);
	m_maincpu->set_ram_ports_map(&nixieclock_state::_4004clk_mp);

	CLOCK(config, m_test_line, 60).signal_handler().set_inputline(m_maincpu, I4004_TEST_LINE);

	// video hardware
	config.set_default_layout(layout_4004clk);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, "dac", 0).add_route(ALL_OUTPUTS, "speaker", 0.25);
}


// ROM definition

ROM_START( 4004clk )
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "clock.u30", 0x0000, 0x0100, CRC(3d8608a5) SHA1(47fa0a91779e1afc34592f91068f8af2e74593d4))
	ROM_LOAD( "clock.u31", 0x0100, 0x0100, CRC(c83af4bc) SHA1(c8bd7ff1322e5ad280364ed87fc9e2ed38120c21))
	ROM_LOAD( "clock.u32", 0x0200, 0x0100, CRC(72442ae7) SHA1(dc25269cf9db7a9e70a86fededeb01efecf80fe7))
	ROM_LOAD( "clock.u33", 0x0300, 0x0100, CRC(74789383) SHA1(ca5ef2c42dae1761599ead56498fb0bfa0db80a5))
	ROM_LOAD( "clock.u34", 0x0400, 0x0100, CRC(d817cc54) SHA1(a54e744465dde20d37ba54bf2bdb1c4708235f9a))
	ROM_LOAD( "clock.u35", 0x0500, 0x0100, CRC(ece36d21) SHA1(c8cf7e08e90463e910ed2d21d8e562e73d7e0b08))
	ROM_LOAD( "clock.u36", 0x0600, 0x0100, CRC(65aa3cb1) SHA1(9b134bd46747a1bccc004c347b3162e9dc846426))
	ROM_LOAD( "clock.u37", 0x0700, 0x0100, CRC(4c2a2632) SHA1(5f75c2d67571ffcfb98f37944f7f4bc7f531c109))
	ROM_LOAD( "clock.u38", 0x0800, 0x0100, CRC(133da0d6) SHA1(08863a287471c0e77f27cea087cb4a3b372d49c1))
	ROM_LOAD( "clock.u39", 0x0900, 0x0100, CRC(0628593c) SHA1(34249753056cd425e0d48c188c830d64464006c9))
	ROM_LOAD( "clock.u40", 0x0a00, 0x0100, CRC(1c2e94b5) SHA1(89e6c70b936fd9882a229de671dcada7c39b9e8e))
	ROM_LOAD( "clock.u41", 0x0b00, 0x0100, CRC(48b4510d) SHA1(17c5eedc36b469bfae23e204c614ccc01bd4df02))
	ROM_LOAD( "clock.u42", 0x0c00, 0x0100, CRC(4b768675) SHA1(8862b9911bd5907e679539c1e98921f0686b8a76))
	ROM_LOAD( "clock.u43", 0x0d00, 0x0100, CRC(df8db80f) SHA1(34ef8e9ae9fd4e88659e1e14759a2baf1cf589a5))
	ROM_LOAD( "clock.u44", 0x0e00, 0x0100, CRC(23037c71) SHA1(87702bdf5985fa58d4cabcc0d4530e229bfebcbb))
	ROM_LOAD( "clock.u45", 0x0f00, 0x0100, CRC(a8d419ef) SHA1(86742a5ad410c027e9cf9a95e2006dc1128715e5))
ROM_END

} // anonymous namespace


// Driver

//    YEAR  NAME     PARENT  COMPAT  MACHINE   INPUT    CLASS             INIT        COMPANY             FULLNAME            FLAGS
SYST( 2008, 4004clk, 0,      0,      _4004clk, 4004clk, nixieclock_state, empty_init, "John L. Weinrich", "4004 Nixie Clock", MACHINE_SUPPORTS_SAVE )



416xl.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Yeno 416 XL

The chess engine is by Frans Morsch, similar to Mephisto Europa.

NOTE: Turn the power off (SAVE) before exiting MAME, otherwise NVRAM won't save
properly.

Hardware notes:
- PCB label: CHESS3 YENO 416XL
- Hitachi HD6301Y0P @ ~8MHz (LC oscillator)
- 8*8 chessboard buttons, 16+7 LEDs, piezo

TODO:
- if/when MAME supports an exit callback, hook up power-off switch to that

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

#include "emu.h"

#include "cpu/m6800/m6801.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "yeno_416xl.lh"


namespace {

class y416xl_state : public driver_device
{
public:
	y416xl_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void y416xl(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(power_off) { if (newval) m_power = false; }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override { m_power = true; }

private:
	// devices/pointers
	required_device<hd6301y0_cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<2> m_inputs;

	bool m_power = false;
	u16 m_inp_mux = 0;

	// I/O handlers
	template <int N> void leds_w(u8 data);
	u8 p2_r();
	void p2_w(u8 data);
	u8 p5_r();
	void p6_w(u8 data);
};

void y416xl_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_power));
	save_item(NAME(m_inp_mux));
}



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

template <int N>
void y416xl_state::leds_w(u8 data)
{
	// P10-P17, P30-P37, P40-P47: leds (direct)
	m_display->write_row(N, ~data);
}

u8 y416xl_state::p2_r()
{
	// P22: power switch
	return m_power ? 0xff : 0xfb;
}

void y416xl_state::p2_w(u8 data)
{
	// P20,P21: input mux (buttons)
	m_inp_mux = (m_inp_mux & 0xff) | (~data << 8 & 0x300);

	// P23: speaker out
	m_dac->write(BIT(data, 3));
}

u8 y416xl_state::p5_r()
{
	// P50-P57: multiplexed inputs
	u8 data = 0;

	// read buttons
	for (int i = 0; i < 3; i++)
		if (BIT(m_inp_mux, i + 8))
			data |= m_inputs[i]->read();

	// read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_file(i);

	return ~data;
}

void y416xl_state::p6_w(u8 data)
{
	// P60-P67: input mux (chessboard)
	m_inp_mux = (m_inp_mux & 0x300) | (data ^ 0xff);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( y416xl )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("Play")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Set Up")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Multimove")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Change Color")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Sound / Change Direction")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Learning")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Show Move")

	PORT_START("POWER") // needs to be triggered for nvram to work
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(y416xl_state::power_off), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void y416xl_state::y416xl(machine_config &config)
{
	// basic machine hardware
	HD6301Y0(config, m_maincpu, 8'000'000); // approximation, no XTAL
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(hd6301y0_cpu_device::nvram_set_battery));
	m_maincpu->standby_cb().append([this](int state) { if (state) m_display->clear(); });
	m_maincpu->out_p1_cb().set(FUNC(y416xl_state::leds_w<0>));
	m_maincpu->in_p2_cb().set(FUNC(y416xl_state::p2_r));
	m_maincpu->out_p2_cb().set(FUNC(y416xl_state::p2_w));
	m_maincpu->out_p3_cb().set(FUNC(y416xl_state::leds_w<1>));
	m_maincpu->out_p4_cb().set(FUNC(y416xl_state::leds_w<2>));
	m_maincpu->in_p5_cb().set(FUNC(y416xl_state::p5_r));
	m_maincpu->out_p6_cb().set(FUNC(y416xl_state::p6_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(3, 8);
	config.set_default_layout(layout_yeno_416xl);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( y416xl )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("yeno_ic-890529_hd6301y0k79p.u1", 0x0000, 0x4000, CRC(85b4368d) SHA1(9dc12a6538141efe1ebcf8162ac7348ddc2b33ae) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1989, y416xl, 0,      0,      y416xl,  y416xl, y416xl_state, empty_init, "Yeno", "416 XL (Yeno)", MACHINE_SUPPORTS_SAVE )



532xl.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Mr. Lars
/*******************************************************************************

Yeno 532 XL

This is the only Yeno chesscomputer that has a common CPU with external EPROM,
others are based on a 1-chip MCU. The 532 XL chess engine is by Ulf Rathsman.

Hardware notes:
- R65C02P4 @ 4 MHz
- 32KB ROM (MBM27C256H-10)
- 8KB battery-backed RAM (HY6264LP-10)
- HD61603, 2 4*7seg LCD screens
- TTL, piezo, 8*8+7 LEDs, button sensors

The software searches for an opening book ROM in 0x4000-0x7fff, it looks like
it's compatible with Conchess L16 / Mephisto HG240. Though the hardware does
not have an edge connector or empty ROM socket for it.

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

#include "emu.h"

#include "cpu/m6502/r65c02.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/hd61603.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "yeno_532xl.lh"


namespace {

class y532xl_state : public driver_device
{
public:
	y532xl_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_lcd(*this, "lcd"),
		m_display(*this, "display"),
		m_board(*this, "board"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_out_digit(*this, "digit%u", 0U),
		m_out_lcd(*this, "lcd%u", 0U)
	{ }

	// machine configs
	void y532xl(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<hd61603_device> m_lcd;
	required_device<pwm_display_device> m_display;
	required_device<sensorboard_device> m_board;
	required_device<dac_2bit_ones_complement_device> m_dac;
	required_ioport_array<3> m_inputs;
	output_finder<8> m_out_digit;
	output_finder<64> m_out_lcd;

	u8 m_led_data = 0;
	u8 m_cb_mux = 0xff;
	u8 m_control = 0xff;

	// address maps
	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_display();
	void lcd_seg_w(u64 data);
	u8 input_r();
	void cb_w(u8 data);
	void led_w(u8 data);
	void control_w(offs_t offset, u8 data);
};

void y532xl_state::machine_start()
{
	m_out_digit.resolve();
	m_out_lcd.resolve();

	// register for savestates
	save_item(NAME(m_led_data));
	save_item(NAME(m_cb_mux));
	save_item(NAME(m_control));
}



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

void y532xl_state::update_display()
{
	m_display->matrix(~(m_control << 8 | m_cb_mux), m_led_data);
}

void y532xl_state::lcd_seg_w(u64 data)
{
	// output individual segments
	for (int i = 0; i < 64; i++)
		m_out_lcd[i] = BIT(data, i);

	// output digits
	for (int i = 0; i < 8; i++)
	{
		u8 digit_data = bitswap<8>(data,7,3,2,4,5,6,0,1);
		m_out_digit[i] = (i & 3) ? digit_data : digit_data & 0x7f; // no DP for last digit
		data >>= 8;
	}
}

u8 y532xl_state::input_r()
{
	u8 data = 0;

	// read side panel buttons
	for (int i = 0; i < 3; i++)
		if (BIT(~m_control, i))
			data |= m_inputs[i]->read();

	// read chessboard sensors
	for (int i = 0; i < 8; i++)
		if (BIT(~m_cb_mux, i))
			data |= m_board->read_file(i);

	return data;
}

void y532xl_state::cb_w(u8 data)
{
	// d0-d3: lcd data (see control_w)
	// d0-d7: chessboard input mux
	m_cb_mux = data;
	update_display();
}

void y532xl_state::led_w(u8 data)
{
	// d0-d7: led data
	m_led_data = data;
	update_display();
}

void y532xl_state::control_w(offs_t offset, u8 data)
{
	u8 prev = m_control;

	// a0-a2,d0: 74259
	u8 mask = 1 << offset;
	m_control = (m_control & ~mask) | ((data & 1) ? mask : 0);

	// Q0-Q2: button input mux
	// Q0: select side leds
	update_display();

	// Q3,Q4: speaker out
	m_dac->write(m_control >> 3 & 3);

	// Q7 rising edge: write to lcd
	if (~prev & m_control & 0x80)
		m_lcd->data_w(m_cb_mux & 0xf);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void y532xl_state::main_map(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("nvram");
	map(0x2000, 0x2000).w(FUNC(y532xl_state::cb_w));
	map(0x2080, 0x2080).w(FUNC(y532xl_state::led_w));
	map(0x2100, 0x2100).r(FUNC(y532xl_state::input_r));
	map(0x2180, 0x2187).w(FUNC(y532xl_state::control_w));
	map(0x8000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( y532xl )
	PORT_START("IN.0")
	PORT_BIT(0x3f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Option")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Show Move")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("Play")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game / Clear Board")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Set Up")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Multi Move")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Teach")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9 / Take Back")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0 / Forward")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("A / 1 / Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("B / 2 / Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("C / 3 / Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("D / 4 / Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("E / 5 / Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("F / 6 / King")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("G / 7 / Black")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("H / 8 / White")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void y532xl_state::y532xl(machine_config &config)
{
	// basic machine hardware
	R65C02(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &y532xl_state::main_map);

	const attotime nmi_period = attotime::from_hz(4_MHz_XTAL / 0x2000);
	m_maincpu->set_periodic_int(FUNC(y532xl_state::nmi_line_pulse), nmi_period);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	HD61603(config, m_lcd, 0);
	m_lcd->write_segs().set(FUNC(y532xl_state::lcd_seg_w));

	PWM_DISPLAY(config, m_display).set_size(9, 8);
	config.set_default_layout(layout_yeno_532xl);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_2BIT_ONES_COMPLEMENT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.125);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( y532xl )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("532xl_00674.u9", 0x8000, 0x8000, CRC(8ded1df6) SHA1(6bcbebb8e1fde472e1ccd0036cb3f00b27f72d89) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1989, y532xl, 0,      0,      y532xl,  y532xl, y532xl_state, empty_init, "Yeno", "532 XL (Yeno)", MACHINE_SUPPORTS_SAVE )



600cat.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/***********************************************************************************************************************************

    Skeleton driver for Wavetek 600 Cellular Activation Tester.

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

#include "emu.h"

#include "cpu/m6800/m6801.h"
#include "machine/timekpr.h"
#include "video/hd44780.h"

#include "emupal.h"
#include "screen.h"

#define VERBOSE (0)
#include "logmacro.h"

namespace {

class _600cat_state : public driver_device
{
public:
	_600cat_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "lcdc")
		, m_screen(*this, "screen")
		, m_palette(*this, "palette")
		, m_rtc(*this, "rtc")
	{ }

	void _600cat(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	HD44780_PIXEL_UPDATE(lcd_pixel_update);

	void lcd_palette(palette_device &palette) const;

	void p1_w(u8 data);
	void p2_w(u8 data);
	void p3_w(u8 data);
	void p4_w(u8 data);
	void sc2_w(int state);
	void ser_tx_w(int state);

	required_device<hd6303r_cpu_device> m_maincpu;
	required_device<hd44780_device> m_lcdc;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<ds1643_device> m_rtc;
};

HD44780_PIXEL_UPDATE(_600cat_state::lcd_pixel_update)
{
	// char size is 5x8
	if (x > 4 || y > 7)
		return;

	if (pos < 40)
	{
		if (pos >= 20)
		{
			line += 2;
			pos -= 20;
		}

		if (line < 4)
			bitmap.pix(line * (8 + 1) + y, pos * 6 + x) = state ? 1 : 2;
	}
}

void _600cat_state::lcd_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148)); // background
	palette.set_pen_color(1, rgb_t( 92,  83,  88)); // lcd pixel on
	palette.set_pen_color(2, rgb_t(131, 136, 139)); // lcd pixel off
}

void _600cat_state::p1_w(u8 data)
{
	LOG("p1_w: %02x\n", data);
}

void _600cat_state::p2_w(u8 data)
{
	LOG("p2_w: %02x\n", data);
}

void _600cat_state::p3_w(u8 data)
{
	LOG("p3_w: %02x\n", data);
}

void _600cat_state::p4_w(u8 data)
{
	LOG("p4_w: %02x\n", data);
}

void _600cat_state::sc2_w(int state)
{
	LOG("sc2_w: %d\n", state);
}

void _600cat_state::ser_tx_w(int state)
{
	LOG("ser_tx_w: %d\n", state);
}

void _600cat_state::mem_map(address_map &map)
{
	map(0x0380, 0x0381).rw(m_lcdc, FUNC(hd44780_device::read), FUNC(hd44780_device::write));
	map(0x0400, 0x1fff).lrw8(NAME([this](offs_t offset) { return m_rtc->read(offset + 0x400); }), NAME([this](offs_t offset, u8 data) { m_rtc->write(offset + 0x400, data); }));
	map(0x2000, 0xffff).rom().region("maincpu", 0x2000);
}

static INPUT_PORTS_START(_600cat)
INPUT_PORTS_END

void _600cat_state::_600cat(machine_config &config)
{
	HD6303R(config, m_maincpu, 4'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &_600cat_state::mem_map);
	m_maincpu->out_p1_cb().set(FUNC(_600cat_state::p1_w));
	m_maincpu->out_p2_cb().set(FUNC(_600cat_state::p2_w));
	m_maincpu->out_p3_cb().set(FUNC(_600cat_state::p3_w));
	m_maincpu->out_p4_cb().set(FUNC(_600cat_state::p4_w));
	m_maincpu->out_sc2_cb().set(FUNC(_600cat_state::sc2_w));
	m_maincpu->out_ser_tx_cb().set(FUNC(_600cat_state::ser_tx_w));

	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	m_screen->set_size(120, 36);
	m_screen->set_visarea_full();
	m_screen->set_screen_update(m_lcdc, FUNC(hd44780_device::screen_update));
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(_600cat_state::lcd_palette), 3);

	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(4, 20);
	m_lcdc->set_pixel_update_cb(FUNC(_600cat_state::lcd_pixel_update));

	DS1643(config, m_rtc);
}

ROM_START( 600cat )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD( "600cat.bin", 0x00000, 0x10000, CRC(7cd375f2) SHA1(611a3b8f9d2b4d54f60db40556847b229a34a309) )
ROM_END

} // anonymous namespace

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY    FULLNAME                              FLAGS
COMP( 199?, 600cat, 0,      0,      _600cat, _600cat, _600cat_state, empty_init, "Wavetek", "600 Cellular Activation Tester", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



68ksbc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

    Skeleton driver for 68k Single Board Computer

    29/03/2011

    http://www.kmitl.ac.th/~kswichit/68k/68k.html

    TODO:
    - Add RTC (type DS12887)

    All of the address and i/o decoding is done by a pair of XC9536
    mask-programmed custom devices.

    There are some chips used for unclear purposes (GPI, GPO, LCD).

    This computer has no sound, and no facility for saving or loading programs.

    When started, press ? key for a list of commands.

    Has 1MB of flash (which is actually just 12k of program),
    and 1MB of RAM. Memory map should probably be corrected.


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

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
#include "machine/6850acia.h"
#include "machine/clock.h"


namespace {

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

	void c68ksbc(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};

void c68ksbc_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x002fff).rom();
	map(0x003000, 0x5fffff).ram();
	map(0x600000, 0x600003).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write)).umask16(0x00ff);
}


/* Input ports */
static INPUT_PORTS_START( c68ksbc )
INPUT_PORTS_END


void c68ksbc_state::c68ksbc(machine_config &config)
{
	M68000(config, m_maincpu, 8000000); // text says 8MHz, schematic says 10MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &c68ksbc_state::mem_map);

	acia6850_device &acia(ACIA6850(config, "acia", 0));
	acia.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	acia.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("acia", FUNC(acia6850_device::write_rxd));
	rs232.cts_handler().set("acia", FUNC(acia6850_device::write_cts));

	clock_device &acia_clock(CLOCK(config, "acia_clock", 153600));
	acia_clock.signal_handler().set("acia", FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append("acia", FUNC(acia6850_device::write_rxc));
}

/* ROM definition */
ROM_START( 68ksbc )
	ROM_REGION(0x3000, "maincpu", 0)
	ROM_LOAD( "t68k.bin", 0x0000, 0x2f78, CRC(20a8d0d0) SHA1(544fd8bd8ed017115388c8b0f7a7a59a32253e43) )
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY             FULLNAME                     FLAGS */
COMP( 2002, 68ksbc, 0,      0,      c68ksbc, c68ksbc, c68ksbc_state, empty_init, "Wichit Sirichote", "68k Single Board Computer", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



a2600.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nathan Woods, Wilbert Pol, David Shah
/***************************************************************************

  Atari VCS 2600 driver

TODO:
- Move the 2 32-in-1 rom dumps into their own driver
- Add 128-in-1 driver


  Systema TV Boy

TODO:
- The TV Boy II and Super TV Boy units did not have joystick ports but a builtin D-pad
- Find, dump and add the other variants. There are many. Systema and Akor released PAL TV Boys.
  NICS released NTSC TV Boys in the US and Japan. The US NICS unit is also seen with the name Mega-Game.
  Mega Boy WS-11 is another rebranded model.
  There's also the TV Boy 3, Portable Walkiecom PTG-2600, Compact Master Boy, Game Link 2001, etc.


  Atari A2600 Point of Purchase Display Unit

TODO:
- Does the reset by pressing game select buttons only work on the infinite
  timer mode or also in the other modes?
- Asteroids is an 8KB software title with banking. Does the display unit
  use a special 4KB version or is there banking hardware installed?
- 2 joysticks and 2 paddles are hooked up to the unit; unknown how.


Selection buttons on the kiosk unit:
2 "Press to select game number" buttons
1 "Press to start" button


Switch A8 controls the gameplay duration. Factory default is one minute
gameplay duration.
1 - 2 minute gameplay
2 - 1 minute gameplay
3 - 30 second gameplay
4 - Infinite gameplay (gameplay allowed until end of game or until a "Press
     to select game number" button is pressed)

This seems to be completely hardware controlled; it is never queried or set up
by the game selection rom.


The POP unit comes with the following 42 games installed:
Location Game
D6 Prog ROM 1 ADVENTURE (CX2613)
E6 Prog ROM 2 AIR-SEA BATTLE (CX2602)
F6 Prog ROM 3 ASTEROIDS (CX2649)
J6 Prog ROM 4 BACKGAMMON (CX2617)
K6 Prog ROM 5 BASKETBALL (CX2624)
L6 Prog ROM 6 BOWLING (CX2628)
M6 Prog ROM 7 BREAKOUT (CX2622)
C5 Prog ROM 8 CANYON BOMBER (CX2607)
D5 Prog ROM 9 CASINO (CX2652)
E5 Prog ROM 10 CIRCUS ATARI (CX2630)
F5 Prog ROM 11 COMBAT (CX2601)
J5 Prog ROM 12 DODGE'EM (CX2637)
K5 Prog ROM 13 FOOTBALL (CX2625)
L5 Prog ROM 14 GOLF (CX2634)
M5 Prog ROM 15 HANGMAN (CX2662)
C4 Prog ROM 16 HOMERUN (CX2623)
D4 Prog ROM 17 H. CANNONBALL (CX2627)
E4 Prog ROM 18 MAZE CRAZE (CX2635)
F4 Prog ROM 19 MISSILE CMD (CX2638)
J4 Prog ROM 20 NIGHT DRIVER (CX2633)
K4 Prog ROM 21 OTHELLO (CX2639)
L4 Prog ROM 22 OUTLAW (CX2605)
M4 Prog ROM 23 SOCCER (CX2616)
C3 Prog ROM 24 SKYDIVER (CX2629)
D3 Prog ROM 25 sLOT RAcERS (CX2606)
E3 Prog ROM 26 SPACE INVADERS (CX2632)
F3 Prog ROM 27 STREET RACER (CX2612)
J3 Prog ROM 28 SUPERMAN (CX2631)
K3 Prog ROM 29 3D TIC-TAC-TOE (CX2618)
L3 Prog ROM 30 VIDEO CHECKERS (CX2636)
M3 Prog ROM 31 VIDEO CHESS (CX2645)
C2 Prog ROM 32 VIDEO OLYMPICS (CX2621)
D2 Prog ROM 33 VIDEO PINBALL (CX2648)
E2 Prog ROM 34 WAR LORDS (CX2610)
F2 Prog ROM 35 BERZERK (CX2650)
J2 Prog ROM 36 HAUNTED HOUSE (CX2654)
K2 Prog ROM 37 MATH GRAND PRIX (CX2658)
L2 Prog ROM 38 DEFENDER (CX2609)
M2 Prog ROM 39 YARS' REVENGE (CX2655)
C1 Prog ROM 40 PAC-MAN (CX2646)
D1 Prog ROM 41 SUPER BREAKOUT (CX2608)
E1 Prog ROM 42 DEMON/DIAMOND (CX2615)

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

#include "emu.h"


#include "bus/vcs/compumat.h"
#include "bus/vcs/dpc.h"
#include "bus/vcs/harmony_melody.h"
#include "bus/vcs/rom.h"
#include "bus/vcs/scharger.h"
#include "bus/vcs/vcs_slot.h"
#include "bus/vcs_ctrl/ctrl.h"
#include "cpu/m6502/m6507.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "sound/tiaintf.h"
#include "speaker.h"
#include "tia.h"

#include "machine/mos6530.h"

//#define VERBOSE (LOG_GENERAL)
#include "logmacro.h"


namespace {

static const uint16_t supported_screen_heights[4] = { 262, 312, 328, 342 };


class a2600_base_state : public driver_device
{
protected:
	a2600_base_state(const machine_config &mconfig, device_type type, const char *tag, XTAL xtal) :
		driver_device(mconfig, type, tag),
		m_riot_ram(*this, "riot_ram"),
		m_tia(*this, "tia_video"),
		m_maincpu(*this, "maincpu"),
		m_riot(*this, "riot"),
		m_joy1(*this, "joyport1"),
		m_joy2(*this, "joyport2"),
		m_screen(*this, "screen"),
		m_xtal(xtal)
	{ }

	virtual void machine_start() override ATTR_COLD;

	void a2600_mem(address_map &map) ATTR_COLD;

	void a2600_base_ntsc(machine_config &config);
	void a2600_base_pal(machine_config &config);

	void switch_A_w(uint8_t data);
	uint8_t switch_A_r();
	void switch_B_w(uint8_t data);
	void irq_callback(int state);
	uint16_t a2600_read_input_port(offs_t offset);
	uint8_t a2600_get_databus_contents(offs_t offset);
	void a2600_tia_vsync_callback(uint16_t data);

	required_shared_ptr<uint8_t> m_riot_ram;
	required_device<tia_video_device> m_tia;
	required_device<m6507_device> m_maincpu;
	required_device<mos6532_device> m_riot;
	required_device<vcs_control_port_device> m_joy1;
	required_device<vcs_control_port_device> m_joy2;
	required_device<screen_device> m_screen;

private:
	uint16_t m_current_screen_height = 0U;
	XTAL m_xtal;
};


class a2600_cons_state : public a2600_base_state
{
public:
	a2600_cons_state(const machine_config &mconfig, device_type type, const char *tag, XTAL xtal) :
		a2600_base_state(mconfig, type, tag, xtal),
		m_cartslot(*this, "cartslot")
	{ }

protected:
	void a2600_cartslot(machine_config &config);

private:
	required_device<vcs_cart_slot_device> m_cartslot;
};


class a2600_state : public a2600_cons_state
{
public:
	a2600_state(const machine_config &mconfig, device_type type, const char *tag) :
		a2600_cons_state(mconfig, type, tag, 3.579575_MHz_XTAL)
	{ }

	void a2600(machine_config &config);
};


class a2600p_state : public a2600_cons_state
{
public:
	a2600p_state(const machine_config &mconfig, device_type type, const char *tag) :
		a2600_cons_state(mconfig, type, tag, 3.546894_MHz_XTAL)
	{ }

	void a2600p(machine_config &config);
};


class a2600_pop_state : public a2600_base_state
{
public:
	a2600_pop_state(const machine_config &mconfig, device_type type, const char *tag)
		: a2600_base_state(mconfig, type, tag, 3.579545_MHz_XTAL)
		, m_bank(*this, "bank")
		, m_a8(*this, "A8")
		, m_swb(*this, "SWB")
	{ }

	void a2600_pop(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void memory_map(address_map &map) ATTR_COLD;

	uint8_t rom_switch_r(offs_t offset);
	void rom_switch_w(offs_t offset, uint8_t data);
	TIMER_CALLBACK_MEMBER(reset_timer_callback);
	TIMER_CALLBACK_MEMBER(game_select_button_timer_callback);

	required_memory_bank m_bank;
	required_ioport m_a8;
	required_ioport m_swb;
	emu_timer *m_reset_timer = nullptr;
	emu_timer *m_game_select_button_timer = nullptr;
};

class tvboy_state : public a2600_base_state
{
public:
	tvboy_state(const machine_config &mconfig, device_type type, const char *tag)
		: a2600_base_state(mconfig, type, tag, 3.546894_MHz_XTAL)
		, m_crom(*this, "crom")
		, m_rom(*this, "mainrom")
	{ }

	void tvboy(machine_config &config);
	void tvboyn(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void bank_write(offs_t offset, uint8_t data);

	void tvboy_mem(address_map &map) ATTR_COLD;

	required_memory_bank m_crom;
	required_region_ptr<uint8_t> m_rom;
};


void a2600_base_state::a2600_mem(address_map &map) // 6507 has 13-bit address space, 0x0000 - 0x1fff
{
	map(0x0000, 0x007f).mirror(0x0f00).rw(m_tia, FUNC(tia_video_device::read), FUNC(tia_video_device::write));
	map(0x0080, 0x00ff).mirror(0x0d00).ram().share("riot_ram");
	map(0x0280, 0x029f).mirror(0x0d00).m("riot", FUNC(mos6532_device::io_map));
}

void a2600_pop_state::memory_map(address_map &map) // 6507 has 13-bit address space, 0x0000 - 0x1fff
{
	a2600_base_state::a2600_mem(map);
	map(0x0800, 0x0800).rw(FUNC(a2600_pop_state::rom_switch_r), FUNC(a2600_pop_state::rom_switch_w));
	map(0x1000, 0x1fff).bankr(m_bank);
}

void tvboy_state::tvboy_mem(address_map &map)
{ // 6507 has 13-bit address space, 0x0000 - 0x1fff
	a2600_base_state::a2600_mem(map);
	map(0x1800, 0x1fff).w(FUNC(tvboy_state::bank_write));
	map(0x1000, 0x1fff).bankr(m_crom);
}


//  read returns number of empty game rom slots
uint8_t a2600_pop_state::rom_switch_r(offs_t offset)
{
	return 5;   // Max 47 games, 5 empty slots => 42 games
}


//  Rom switch
void a2600_pop_state::rom_switch_w(offs_t offset, uint8_t data)
{
	m_bank->set_entry(data & 0x7f);
	if (data & 0x80)
	{
		// (re)start reset timer?
		// Unknown what happens when multiple dipswitches are set
		uint8_t a8 = m_a8->read();
		attotime reset_time = attotime::never;
		if (a8 & 0x08)
		{
			// infinite
			// enable reset by pressing game selection button when on infinite timer
			m_game_select_button_timer->adjust(attotime::from_hz(60), 0, attotime::from_hz(60));
		}
		else if (a8 & 0x04)
		{
			// 30 seconds
			reset_time = attotime::from_seconds(30);
		}
		else if (a8 & 0x02)
		{
			// 1 minute
			reset_time = attotime::from_seconds(60);
		}
		else if (a8 & 0x01)
		{
			// 2 minutes
			reset_time = attotime::from_seconds(120);
		}
		m_reset_timer->adjust(reset_time);
	}
}

void tvboy_state::bank_write(offs_t offset, uint8_t data)
{
	LOG("banking (?) write %04x, %02x\n", offset, data);
	m_crom->set_entry(data);
}


TIMER_CALLBACK_MEMBER(a2600_pop_state::reset_timer_callback)
{
	machine().schedule_soft_reset();
}


TIMER_CALLBACK_MEMBER(a2600_pop_state::game_select_button_timer_callback)
{
	if ((m_swb->read() & 0x14) != 0x14) {
		machine().schedule_soft_reset();
	}
}


void a2600_base_state::switch_A_w(uint8_t data)
{
	/* Left controller port */
	m_joy1->joy_w( data >> 4 );

	/* Right controller port */
	m_joy2->joy_w( data & 0x0f );

//  switch( ioport("CONTROLLERS")->read() % 16 )
//  {
//  case 0x0a:  /* KidVid voice module */
//      m_cassette->change_state(( data & 0x02 ) ? CASSETTE_MOTOR_DISABLED : (CASSETTE_MOTOR_ENABLED | CASSETTE_PLAY), CASSETTE_MOTOR_DISABLED );
//      break;
//  }
}

uint8_t a2600_base_state::switch_A_r()
{
	uint8_t val = 0;

	// Left controller port PINs 1-4 ( 4321 )
	val |= (m_joy1->read_joy() & 0x0f) << 4;

	// Right controller port PINs 1-4 ( 4321 )
	val |= m_joy2->read_joy() & 0x0f;

	return val;
}

void a2600_base_state::switch_B_w(uint8_t data)
{
}

void a2600_base_state::irq_callback(int state)
{
}

uint16_t a2600_base_state::a2600_read_input_port(offs_t offset)
{
	switch (offset)
	{
	case 0: // Left controller port PIN 5
		return m_joy1->read_pot_x();

	case 1: // Left controller port PIN 9
		return m_joy1->read_pot_y();

	case 2: // Right controller port PIN 5
		return m_joy2->read_pot_x();

	case 3: // Right controller port PIN 9
		return m_joy2->read_pot_y();

	case 4: // Left controller port PIN 6
		return (m_joy1->read_joy() & 0x20) ? 0xff : 0x7f;

	case 5: // Right controller port PIN 6
		return (m_joy2->read_joy() & 0x20) ? 0xff : 0x7f;
	}
	return 0xff;
}

/* There are a few games that do an LDA ($80-$FF),Y instruction.
   The contents off the databus then depend on whatever was read
   from the RAM. To do this really properly the 6502 core would
   need to keep track of the last databus contents so we can query
   that. For now this is a quick hack to determine that value anyway.
   Examples:
   Q-Bert's Qubes (NTSC,F6) at 0x1594
   Berzerk at 0xF093.
*/
uint8_t a2600_base_state::a2600_get_databus_contents(offs_t offset)
{
	uint16_t  last_address, prev_address;
	uint8_t   last_byte, prev_byte;
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);

	last_address = m_maincpu->pc() + 1;
	if ( ! ( last_address & 0x1080 ) )
	{
		return offset;
	}
	last_byte = prog_space.read_byte(last_address );
	if ( last_byte < 0x80 || last_byte == 0xFF )
	{
		return last_byte;
	}
	prev_address = last_address - 1;
	if ( ! ( prev_address & 0x1080 ) )
	{
		return last_byte;
	}
	prev_byte = prog_space.read_byte(prev_address );
	if ( prev_byte == 0xB1 )
	{   /* LDA (XX),Y */
		return prog_space.read_byte(last_byte + 1 );
	}
	return last_byte;
}

#if 0
static const rectangle visarea[4] = {
	{ 26, 26 + 160 + 16, 24, 24 + 192 + 31 },   /* 262 */
	{ 26, 26 + 160 + 16, 32, 32 + 228 + 31 },   /* 312 */
	{ 26, 26 + 160 + 16, 45, 45 + 240 + 31 },   /* 328 */
	{ 26, 26 + 160 + 16, 48, 48 + 240 + 31 }    /* 342 */
};
#endif

void a2600_base_state::a2600_tia_vsync_callback(uint16_t data)
{
	for (int i = 0; i < std::size(supported_screen_heights); i++)
	{
		if (data >= supported_screen_heights[i] - 3 && data <= supported_screen_heights[i] + 3)
		{
			if (supported_screen_heights[i] != m_current_screen_height)
			{
				m_current_screen_height = supported_screen_heights[i];
//              m_screen->configure(228, m_current_screen_height, visarea[i], HZ_TO_ATTOSECONDS(m_xtal) * 228 * m_current_screen_height);
			}
		}
	}
}

void a2600_base_state::machine_start()
{
	m_current_screen_height = m_screen->height();
	memset(m_riot_ram, 0x00, 0x80);

	save_item(NAME(m_current_screen_height));
}

void tvboy_state::machine_start()
{
	a2600_base_state::machine_start();
	m_crom->configure_entries(0, m_rom.bytes() / 0x1000, &m_rom[0], 0x1000);
}

void a2600_pop_state::machine_start()
{
	a2600_base_state::machine_start();
	m_bank->configure_entries(0, 48, memregion("maincpu")->base(), 0x1000);
	m_bank->set_entry(0);
	m_reset_timer = timer_alloc(FUNC(a2600_pop_state::reset_timer_callback), this);
	m_game_select_button_timer = timer_alloc(FUNC(a2600_pop_state::game_select_button_timer_callback), this);
}


void tvboy_state::machine_reset()
{
	m_crom->set_entry(0);
	a2600_base_state::machine_reset();
}

void a2600_pop_state::machine_reset()
{
	a2600_base_state::machine_reset();

	m_bank->set_entry(0);
	m_reset_timer->adjust(attotime::never);
	m_game_select_button_timer->adjust(attotime::never);
}


static INPUT_PORTS_START( a2600 )
	PORT_START("SWB")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Reset Game") PORT_CODE(KEYCODE_2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Select Game") PORT_CODE(KEYCODE_1)
	PORT_BIT ( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_CONFNAME( 0x08, 0x08, "TV Type" ) PORT_CODE(KEYCODE_C) PORT_TOGGLE
	PORT_CONFSETTING(    0x08, "Color" )
	PORT_CONFSETTING(    0x00, "B&W" )
	PORT_BIT ( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT ( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_CONFNAME( 0x40, 0x00, "Left Diff. Switch" ) PORT_CODE(KEYCODE_3) PORT_TOGGLE
	PORT_CONFSETTING(    0x40, "A" )
	PORT_CONFSETTING(    0x00, "B" )
	PORT_CONFNAME( 0x80, 0x00, "Right Diff. Switch" ) PORT_CODE(KEYCODE_4) PORT_TOGGLE
	PORT_CONFSETTING(    0x80, "A" )
	PORT_CONFSETTING(    0x00, "B" )
INPUT_PORTS_END

static INPUT_PORTS_START(a2600_pop)
	PORT_START("SWB")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Reset Game") PORT_CODE(KEYCODE_2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Select Game") PORT_CODE(KEYCODE_1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Tens") PORT_CODE(KEYCODE_3)  // Tens
	PORT_CONFNAME( 0x08, 0x08, "TV Type" ) PORT_CODE(KEYCODE_C) PORT_TOGGLE
	PORT_CONFSETTING(    0x08, "Color" )
	PORT_CONFSETTING(    0x00, "B&W" )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Ones")  PORT_CODE(KEYCODE_4) // Ones
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Start") PORT_CODE(KEYCODE_5) // Start
	// Left difficulty switch not present
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	// Right difficulty switch not present
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("A8")
	PORT_DIPNAME(0x0f, 0x02, "Gameplay duration")
	PORT_DIPSETTING(0x01, "2 minutes")
	PORT_DIPSETTING(0x02, "1 minute")
	PORT_DIPSETTING(0x04, "30 seconds")
	PORT_DIPSETTING(0x08, "Infinite")
INPUT_PORTS_END

static INPUT_PORTS_START( tvboy )
	PORT_INCLUDE( a2600 )

	PORT_MODIFY("SWB")
	PORT_BIT ( 0x08, IP_ACTIVE_LOW, IPT_UNUSED ) // No TV Type switch, hard coded to Color
	PORT_BIT ( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED ) // No Left Diff. switch, hard coded to B
	PORT_BIT ( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // No Right Diff. switch, hard coded to B
INPUT_PORTS_END


static void a2600_cart(device_slot_interface &device)
{
	device.option_add("a26_2k_4k", A26_ROM_2K_4K);
	device.option_add("a26_f4",    A26_ROM_F4);
	device.option_add("a26_f6",    A26_ROM_F6);
	device.option_add("a26_f8",    A26_ROM_F8);
	device.option_add("a26_f8sw",  A26_ROM_F8_SW);
	device.option_add("a26_fa",    A26_ROM_FA);
	device.option_add("a26_fe",    A26_ROM_FE);
	device.option_add("a26_3e",    A26_ROM_3E);
	device.option_add("a26_3f",    A26_ROM_3F);
	device.option_add("a26_e0",    A26_ROM_E0);
	device.option_add("a26_e7",    A26_ROM_E7);
	device.option_add("a26_ua",    A26_ROM_UA);
	device.option_add("a26_cv",    A26_ROM_CV);
	device.option_add("a26_dc",    A26_ROM_DC);
	device.option_add("a26_fv",    A26_ROM_FV);
	device.option_add("a26_jvp",   A26_ROM_JVP);
	device.option_add("a26_cm",    A26_ROM_COMPUMATE);
	device.option_add("a26_ss",    A26_ROM_SUPERCHARGER);
	device.option_add("a26_dpc",   A26_ROM_DPC);
	device.option_add("a26_4in1",  A26_ROM_4IN1);
	device.option_add("a26_8in1",  A26_ROM_8IN1);
	device.option_add("a26_32in1", A26_ROM_32IN1);
	device.option_add("a26_x07",   A26_ROM_X07);
	device.option_add("a26_harmony",   A26_ROM_HARMONY);
}

void a2600_cons_state::a2600_cartslot(machine_config &config)
{
	VCS_CART_SLOT(config, m_cartslot, a2600_cart, nullptr).set_must_be_loaded(true);
	m_cartslot->set_address_space(m_maincpu, AS_PROGRAM);

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("a2600");
	SOFTWARE_LIST(config, "cass_list").set_original("a2600_cass");
}

void a2600_base_state::a2600_base_ntsc(machine_config &config)
{
	/* basic machine hardware */
	M6507(config, m_maincpu, m_xtal / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &a2600_base_state::a2600_mem);

	/* video hardware */
	TIA_NTSC_VIDEO(config, m_tia, 0, "tia");
	m_tia->read_input_port_callback().set(FUNC(a2600_state::a2600_read_input_port));
	m_tia->databus_contents_callback().set(FUNC(a2600_state::a2600_get_databus_contents));
	m_tia->vsync_callback().set(FUNC(a2600_state::a2600_tia_vsync_callback));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(m_xtal, 228, 26, 26 + 160 + 16, 262, 24 , 24 + 192 + 31);
	m_screen->set_screen_update("tia_video", FUNC(tia_video_device::screen_update));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	TIA(config, "tia", m_xtal/114).add_route(ALL_OUTPUTS, "mono", 0.90);

	/* devices */
	MOS6532(config, m_riot, m_xtal / 3);
	m_riot->pa_rd_callback().set(FUNC(a2600_state::switch_A_r));
	m_riot->pa_wr_callback().set(FUNC(a2600_state::switch_A_w));
	m_riot->pb_rd_callback().set_ioport("SWB");
	m_riot->pb_wr_callback().set(FUNC(a2600_state::switch_B_w));
	m_riot->irq_wr_callback().set(FUNC(a2600_state::irq_callback));

	VCS_CONTROL_PORT(config, m_joy1, vcs_control_port_devices, "joy");
	VCS_CONTROL_PORT(config, m_joy2, vcs_control_port_devices, "joy");
}


void a2600_base_state::a2600_base_pal(machine_config &config)
{
	/* basic machine hardware */
	M6507(config, m_maincpu, m_xtal / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &a2600_state::a2600_mem);

	/* video hardware */
	TIA_PAL_VIDEO(config, m_tia, 0, "tia");
	m_tia->read_input_port_callback().set(FUNC(a2600_state::a2600_read_input_port));
	m_tia->databus_contents_callback().set(FUNC(a2600_state::a2600_get_databus_contents));
	m_tia->vsync_callback().set(FUNC(a2600_state::a2600_tia_vsync_callback));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(m_xtal, 228, 26, 26 + 160 + 16, 312, 32, 32 + 228 + 31);
	m_screen->set_screen_update("tia_video", FUNC(tia_video_device::screen_update));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	TIA(config, "tia", m_xtal/114).add_route(ALL_OUTPUTS, "mono", 0.90);

	/* devices */
	MOS6532(config, m_riot, m_xtal / 3);
	m_riot->pa_rd_callback().set(FUNC(a2600_state::switch_A_r));
	m_riot->pa_wr_callback().set(FUNC(a2600_state::switch_A_w));
	m_riot->pb_rd_callback().set_ioport("SWB");
	m_riot->pb_wr_callback().set(FUNC(a2600_state::switch_B_w));
	m_riot->irq_wr_callback().set(FUNC(a2600_state::irq_callback));

	VCS_CONTROL_PORT(config, m_joy1, vcs_control_port_devices, "joy");
	VCS_CONTROL_PORT(config, m_joy2, vcs_control_port_devices, "joy");
}


void a2600_state::a2600(machine_config &config)
{
	a2600_base_ntsc(config);
	a2600_cartslot(config);
	subdevice<software_list_device>("cart_list")->set_filter("NTSC");
}

void a2600p_state::a2600p(machine_config &config)
{
	a2600_base_pal(config);
	a2600_cartslot(config);
	subdevice<software_list_device>("cart_list")->set_filter("PAL");
}


void a2600_pop_state::a2600_pop(machine_config &config)
{
	a2600_base_ntsc(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &a2600_pop_state::memory_map);
}


void tvboy_state::tvboy(machine_config &config)
{
	a2600_base_pal(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &tvboy_state::tvboy_mem);
}

void tvboy_state::tvboyn(machine_config &config)
{
	a2600_base_ntsc(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &tvboy_state::tvboy_mem);
}


ROM_START(a2600)
	ROM_REGION(0x2000, "maincpu", ROMREGION_ERASEFF)
ROM_END

#define rom_a2600p rom_a2600

ROM_START(a2600_pop)
	ROM_REGION(0x30000, "maincpu", 0)
	// Boot/Game selection
	ROM_LOAD("136003_101.c6", 0x000, 0x800, CRC(9d3cfba6) SHA1(b84c34aaedd84fca30b6e8223ee062439acf4fe0))
	ROM_COPY("maincpu", 0x000, 0x800, 0x800)
	// Games
	// Hashes taken from the software list. These all need to be verified.
	// Judging by ROM labels from the parts list there is the possibility
	// that AIR-SEA BATTLE, ASTEROIDS, DODGE'EM, and SUPERMAN may have been
	// modified for the point of purchase unit.
	ROM_LOAD("c012013.d6", 0x1000, 0x1000, BAD_DUMP CRC(a6db4b3a) SHA1(e07e48d463d30321239a8acc00c490f27f1f7422)) // Prog ROM 1 ADVENTURE (CX2613)
	ROM_LOAD("c011202a.e6", 0x2000, 0x800, BAD_DUMP CRC(1fc6c0f1) SHA1(a746fdc82b336a9d499bf17f50b41e0193ba595e)) // Prog ROM 2 AIR-SEA BATTLE (CX2602)
	ROM_COPY("maincpu", 0x2000, 0x2800, 0x800)
	// Original game is 8KB, special 4KB version or is there banking hardware for this game?
	ROM_LOAD("c016449a.f6", 0x3000, 0x1000, NO_DUMP) // Prog ROM 3 ASTEROIDS (CX2649)
	ROM_LOAD("c012017.j6", 0x4000, 0x1000, BAD_DUMP CRC(e2a7ffa6) SHA1(9b1da7fbd0bf6fcadf1b60c11eeb31b6a61a03c3)) // Prog ROM 4 BACKGAMMON (CX2617)
	ROM_LOAD("c011224.k6", 0x5000, 0x800, BAD_DUMP CRC(711a2340) SHA1(9375c5c5298e81b37224dbde9bc5af151181ca27)) // Prog ROM 5 BASKETBALL (CX2624)
	ROM_COPY("maincpu", 0x5000, 0x5800, 0x800)
	ROM_LOAD("c011228.l6", 0x6000, 0x800, BAD_DUMP CRC(fea0d6d5) SHA1(cf6ce244b3edaad7ad5e9ca5f01668135c2f93d0)) // Prog ROM 6 BOWLING (CX2628)
	ROM_COPY("maincpu", 0x6000, 0x6800, 0x800)
	ROM_LOAD("c011222.m6", 0x7000, 0x800, BAD_DUMP CRC(3037638c) SHA1(8d473b87b70e26890268e6c417c0bb7f01e402eb)) // Prog ROM 7 BREAKOUT (CX2622)
	ROM_COPY("maincpu", 0x7000, 0x7800, 0x800)
	ROM_LOAD("c011207.c5", 0x8000, 0x800, BAD_DUMP CRC(e914b8ca) SHA1(b89443a0029e765c2716774fe2582be37650115c)) // Prog ROM 8 CANYON BOMBER (CX2607)
	ROM_COPY("maincpu", 0x8000, 0x8800, 0x800)
	ROM_LOAD("c012052.d5", 0x9000, 0x1000, BAD_DUMP CRC(420b8248) SHA1(08598101e38756916613f37581ef1b61c719016f)) // Prog ROM 9 CASINO (CX2652)
	ROM_LOAD("c012030.e5", 0xa000, 0x1000, BAD_DUMP CRC(a4b9a830) SHA1(6821c334d9cb85da17fa2a960636620966b91f96)) // Prog ROM 10 CIRCUS ATARI (CX2630)
	ROM_LOAD("c011201.f5", 0xb000, 0x800, BAD_DUMP CRC(9c326a97) SHA1(ce7580059e8b41cb4a1e734c9b35ce3774bf777a)) // Prog ROM 11 COMBAT (CX2601)
	ROM_COPY("maincpu", 0xb000, 0xb800, 0x800)
	ROM_LOAD("c012037a.j5", 0xc000, 0x1000, BAD_DUMP CRC(bc3602b5) SHA1(0ffc02c54190e9dd51ac1e52c911d3d7d730a40a)) // Prog ROM 12 DODGE'EM (CX2637)
	ROM_LOAD("c011225.k5", 0xd000, 0x800, BAD_DUMP CRC(3b73ee02) SHA1(c6fe4ce24bc1ebd538258d98cfe829963323acca)) // Prog ROM 13 FOOTBALL (CX2625)
	ROM_COPY("maincpu", 0xd000, 0xd800, 0x800)
	ROM_LOAD("c011234.l5", 0xe000, 0x800, BAD_DUMP CRC(46a9f200) SHA1(a25d52770408314dec6f41aaf5f9f0a2a3e2c18f)) // Prog ROM 14 GOLF (CX2634)
	ROM_COPY("maincpu", 0xe000, 0xe800, 0x800)
	ROM_LOAD("c012062.m5", 0xf000, 0x1000, BAD_DUMP CRC(c2bcc789) SHA1(561bccf508e162bc70c42d85c170cf0d1d4691a3)) // Prog ROM 15 HANGMAN (CX2662)
	ROM_LOAD("c011223.c4", 0x10000, 0x800, BAD_DUMP CRC(45ace998) SHA1(f362d2b3a50e5ae3c2b412b6c08ecdcfee47a688)) // Prog ROM 16 HOMERUN (CX2623)
	ROM_COPY("maincpu", 0x10000, 0x10800, 0x800)
	ROM_LOAD("c011227.d4", 0x11000, 0x800, BAD_DUMP CRC(f05a41e1) SHA1(d4b0b2aa379893356c72414ee0065a3a91cf9f97)) // Prog ROM 17 H. CANNONBALL (CX2627)
	ROM_COPY("maincpu", 0x11000, 0x11800, 0x800)
	ROM_LOAD("c012035.e4", 0x12000, 0x1000, BAD_DUMP CRC(0098e428) SHA1(aba25089d87cd6fee8d206b880baa5d938aae255)) // Prog ROM 18 MAZE CRAZE (CX2635)
	ROM_LOAD("c012038.f4", 0x13000, 0x1000, BAD_DUMP CRC(cff14904) SHA1(faa06bb0643dbf556b13591c31917d277a83110b)) // Prog ROM 19 MISSILE CMD (CX2638)
	ROM_LOAD("c011233.j4", 0x14000, 0x800, BAD_DUMP CRC(600d8a96) SHA1(372771aeb4e2fb2cd1dead5497e3821e4236d5fc)) // Prog ROM 20 NIGHT DRIVER (CX2633)
	ROM_COPY("maincpu", 0x14000, 0x14800, 0x800)
	ROM_LOAD("c011239.k4", 0x15000, 0x800, BAD_DUMP CRC(171ae72f) SHA1(cbecf1a32d9366a3dd4ad643916cd59cdc820a8b)) // Prog ROM 21 OTHELLO (CX2639)
	ROM_COPY("maincpu", 0x15000, 0x15800, 0x800)
	ROM_LOAD("c011205.l4", 0x16000, 0x800, BAD_DUMP CRC(68dd7acd) SHA1(f8eeaaf4635ac39b4bdf7ded1348bce46313ef9f)) // Prog ROM 22 OUTLAW (CX2605)
	ROM_COPY("maincpu", 0x16000, 0x16800, 0x800)
	ROM_LOAD("c012016.m4", 0x17000, 0x1000, BAD_DUMP CRC(3f59bb60) SHA1(832283530f5dee332f29cf8c4854dd554f2030a0)) // Prog ROM 23 SOCCER (CX2616)
	ROM_LOAD("c011229.c3", 0x18000, 0x800, BAD_DUMP CRC(00312ea9) SHA1(4dde18d4abc139562fdd7a9d2fd49a1f00a9e64a)) // Prog ROM 24 SKYDIVER (CX2629)
	ROM_COPY("maincpu", 0x18000, 0x18800, 0x800)
	ROM_LOAD("c011206.d3", 0x19000, 0x800, BAD_DUMP CRC(177dbdf8) SHA1(a2b13017d759346174e3d8dd53b6347222d3b85d)) // Prog ROM 25 SLOT RACERS (CX2606)
	ROM_COPY("maincpu", 0x19000, 0x19800, 0x800)
	ROM_LOAD("c012032.e3", 0x1a000, 0x1000, BAD_DUMP CRC(a6e867b3) SHA1(31d9668fe5812c3d2e076987ca327ac6b2e280bf)) // Prog ROM 26 SPACE INVADERS (CX2632)
	ROM_LOAD("c011212.f3", 0x1b000, 0x800, BAD_DUMP CRC(47592880) SHA1(bffb3d41916c83398624151eb00aa2a3acd23ab8)) // Prog ROM 27 STREET RACER (CX2612)
	ROM_COPY("maincpu", 0x1b000, 0x1b800, 0x800)
	ROM_LOAD("c012031b.j3", 0x1c000, 0x1000, BAD_DUMP CRC(39562bd7) SHA1(b9dee027c8d7dd2a46be111ab0b8363c1becc081)) // Prog ROM 28 SUPERMAN (CX2631)
	ROM_LOAD("c011218.k3", 0x1d000, 0x800, BAD_DUMP CRC(58805709) SHA1(21d983f2f52b84c22ecae84b0943678ae2c31c10)) // Prog ROM 29 3D TIC-TAC-TOE (CX2618)
	ROM_COPY("maincpu", 0x1d000, 0x1d800, 0x800)
	ROM_LOAD("c012036.l3", 0x1e000, 0x1000, BAD_DUMP CRC(3df33335) SHA1(babae88a832b76d8c5af6ea63b8f10a0da5bb992)) // Prog ROM 30 VIDEO CHECKERS (CX2636)
	ROM_LOAD("c012045.m3", 0x1f000, 0x1000, BAD_DUMP CRC(b6226a54) SHA1(043ef523e4fcb9fc2fc2fda21f15671bf8620fc3)) // Prog ROM 31 VIDEO CHESS (CX2645)
	ROM_LOAD("c011221.c2", 0x20000, 0x800, BAD_DUMP CRC(e4bc89c4) SHA1(1ffe89d79d55adabc0916b95cc37e18619ef7830)) // Prog ROM 32 VIDEO OLYMPICS (CX2621)
	ROM_COPY("maincpu", 0x20000, 0x20800, 0x800)
	ROM_LOAD("c012048.d2", 0x21000, 0x1000, BAD_DUMP CRC(10d95426) SHA1(2c16c1a6374c8e22275d152d93dd31ffba26271f)) // Prog ROM 33 VIDEO PINBALL (CX2648)
	ROM_LOAD("c012010.e2", 0x22000, 0x1000, BAD_DUMP CRC(cf174b57) SHA1(2d7563d337cbc0cdf4fc14f69853ab6757697788)) // Prog ROM 34 WAR LORDS (CX2610)
	ROM_LOAD("c012050.f2", 0x23000, 0x1000, BAD_DUMP CRC(2e8b4b5f) SHA1(08bcbc8954473e8f0242b881315b0af4466998ae)) // Prog ROM 35 BERZERK (CX2650)
	ROM_LOAD("c012054.j2", 0x24000, 0x1000, BAD_DUMP CRC(aa62d961) SHA1(1476c869619075b551b20f2c7f95b11e0d16aec1)) // Prog ROM 36 HAUNTED HOUSE (CX2654)
	ROM_LOAD("c012058.k2", 0x25000, 0x1000, BAD_DUMP CRC(ccc90c98) SHA1(18fac606400c08a0469aebd9b071ae3aec2a3cf2)) // Prog ROM 37 MATH GRAND PRIX (CX2658)
	ROM_LOAD("c012009.l2", 0x26000, 0x1000, BAD_DUMP CRC(0df43d8e) SHA1(79facc1bf70e642685057999f5c2b8e94b102439)) // Prog ROM 38 DEFENDER (CX2609)
	ROM_LOAD("c012055.m2", 0x27000, 0x1000, BAD_DUMP CRC(dfa1c825) SHA1(e2cd8996c1cf929e29130690024d1ec23d3b0bde)) // Prog ROM 39 YARS' REVENGE (CX2655)
	ROM_LOAD("c012046.c1", 0x28000, 0x1000, BAD_DUMP CRC(ddc9a881) SHA1(0940fea7f04cdb6d4b90c5ad1a7e344e68f6dbb1)) // Prog ROM 40 PAC-MAN (CX2646)
	ROM_LOAD("c012008.d1", 0x29000, 0x1000, BAD_DUMP CRC(b0f20d31) SHA1(ac2aad2196c155c1d87d6f42fa88891825f4fde6)) // Prog ROM 41 SUPER BREAKOUT (CX2608)
	ROM_LOAD("c012015.e1", 0x2a000, 0x1000, BAD_DUMP CRC(9b97c3da) SHA1(b45582de81c48b04c2bb758d69021e8088c70ce7)) // Prog ROM 42 DEMON/DIAMOND (CX2615)
	// empty slots f1, j1, k1, l1, m1 ?
ROM_END

ROM_START(tvboy)
	ROM_REGION(0x2000, "maincpu", ROMREGION_ERASEFF)

	ROM_REGION(0x80000, "mainrom", 0)
	ROM_LOAD("tvboy.bin", 0x00000, 0x80000, CRC(2f3d1d52) SHA1(fb26778434fade4cec28f82c53db4cc2f23b8b2b))
ROM_END

ROM_START(tvboyn)
	ROM_REGION(0x2000, "maincpu", ROMREGION_ERASEFF)

	ROM_REGION(0x80000, "mainrom", 0)
	ROM_LOAD("ns-31_n_tv-bot_127g-nics.59874.bin", 0x00000, 0x80000, CRC(96744687) SHA1(47e7a01e635156d2dd6c7e1059653f286370537d))
ROM_END

ROM_START(tvboyii)
	ROM_REGION(0x2000, "maincpu", ROMREGION_ERASEFF)

	ROM_REGION(0x80000, "mainrom", 0)
	// Games 57, 64, and 119 differ from tvboy with crc 2f3d1d52.
	/* Game 29, Enduro, differs only in one LDA instruction at 0x179 (and then some shifted code).
	   Here the LDA uses zero page while the original ROM and all other known versions use absolute addressing.
	   Does this cause a timing issue? It appears bugged in MAME. Does it work on hardware? */
	ROM_LOAD("hy23400p.bin", 0x00000, 0x80000, CRC(f8485173) SHA1(cafbaa0c5437f192cb4fb49f9a672846aa038870))
ROM_END

ROM_START( stvboy )
	ROM_REGION(0x2000, "maincpu", ROMREGION_ERASEFF)

	ROM_REGION(0x80000, "mainrom", 0)
	// Only game 91 differs from tvboyii with crc f8485173. Otherwise the dumps are identical. Game 29, Enduro, has the same issue mentioned above.
	ROM_LOAD("supertvboy.bin", 0x00000, 0x80000, CRC(af2e73e8) SHA1(04b9ddc3b30b0e5b81b9f868d455e902a0151491))
ROM_END

} // anonymous namespace

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS         INIT        COMPANY     FULLNAME
CONS( 1977, a2600,  0,      0,      a2600,   a2600, a2600_state,  empty_init, "Atari",    "Atari 2600 (NTSC)",  MACHINE_SUPPORTS_SAVE )
CONS( 1978, a2600p, a2600,  0,      a2600p,  a2600, a2600p_state, empty_init, "Atari",    "Atari 2600 (PAL)",   MACHINE_SUPPORTS_SAVE )

// Released in 1981/1982
// Games 35-42 are copyright 1982 and looking at the game list they seem to be
// added later.
GAME( 198?, a2600_pop, 0,      a2600_pop, a2600_pop, a2600_pop_state, empty_init, ROT0, "Atari",    "Atari 2600 Point of Purchase Display",   MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )

// Clones
CONS( 199?, tvboy,   0,     0,      tvboy,   tvboy, tvboy_state,  empty_init, "Systema?", "TV Boy (PAL)",       MACHINE_SUPPORTS_SAVE ) // It's unknown what unit this came from. It could be Akor instead?
CONS( 199?, tvboyn,  tvboy, 0,      tvboyn,  tvboy, tvboy_state,  empty_init, "Nics",     "TV Boy (Nics, NTSC)",MACHINE_SUPPORTS_SAVE )
CONS( 199?, tvboyii, tvboy, 0,      tvboy,   tvboy, tvboy_state,  empty_init, "Systema",  "TV Boy II (PAL)",    MACHINE_SUPPORTS_SAVE )
CONS( 1995, stvboy,  0,     0,      tvboy,   tvboy, tvboy_state,  empty_init, "Akor",     "Super TV Boy (PAL)", MACHINE_SUPPORTS_SAVE )



a5105.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
/***************************************************************************

A5105

2009-05-12 Skeleton driver.

http://www.robotrontechnik.de/index.htm?/html/computer/a5105.htm
http://www.sax.de/~zander/bic/bic_bw.html

- this looks like "somehow" inspired by the MSX1 machine?

Cassette commands: CSAVE "name" ; CLOAD


ToDo:
- Cassette (coded per schematic, but doesn't work)


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

#include "emu.h"

#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "machine/ram.h"
#include "machine/upd765.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "sound/beep.h"
#include "video/upd7220.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "formats/a5105_dsk.h"


namespace {

class a5105_state : public driver_device
{
public:
	a5105_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_hgdc(*this, "upd7220")
		, m_cass(*this, "cassette")
		, m_beep(*this, "beeper")
		, m_fdc(*this, "upd765a")
		, m_floppy(*this, "upd765a:%u", 0U)
		, m_video_ram(*this, "video_ram")
		, m_ram(*this, RAM_TAG)
		, m_gfxdecode(*this, "gfxdecode")
		, m_palette(*this, "palette")
	{ }

	void a5105(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

private:
	uint8_t a5105_memsel_r();
	uint8_t key_r();
	uint8_t key_mux_r();
	uint8_t pio_pb_r();
	void key_mux_w(uint8_t data);
	void a5105_ab_w(uint8_t data);
	void a5105_memsel_w(uint8_t data);
	void a5105_upd765_w(uint8_t data);
	void pcg_addr_w(uint8_t data);
	void pcg_val_w(uint8_t data);
	void a5105_palette(palette_device &palette) const;
	static void floppy_formats(format_registration &fr);
	UPD7220_DISPLAY_PIXELS_MEMBER( hgdc_display_pixels );
	UPD7220_DRAW_TEXT_LINE_MEMBER( hgdc_draw_text );

	void a5105_io(address_map &map) ATTR_COLD;
	void a5105_mem(address_map &map) ATTR_COLD;
	void upd7220_map(address_map &map) ATTR_COLD;

	uint8_t *m_ram_base = 0;
	uint8_t *m_rom_base = 0;
	uint8_t *m_char_ram = 0;
	uint16_t m_pcg_addr = 0U;
	uint16_t m_pcg_internal_addr = 0U;
	uint8_t m_key_mux = 0U;
	uint8_t m_memsel[4]{};
	required_device<z80_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<upd7220_device> m_hgdc;
	required_device<cassette_image_device> m_cass;
	required_device<beep_device> m_beep;
	required_device<upd765a_device> m_fdc;
	required_device_array<floppy_connector, 4> m_floppy;
	required_shared_ptr<uint16_t> m_video_ram;
	required_device<ram_device> m_ram;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<palette_device> m_palette;
};

/* TODO */
UPD7220_DISPLAY_PIXELS_MEMBER( a5105_state::hgdc_display_pixels )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();

	int const gfx = m_video_ram[(address & 0xffff)];

	for (int xi = 0; xi < 16; xi++)
	{
		int const pen = ((gfx >> xi) & 1) ? 7 : 0;

		bitmap.pix(y, x + xi) = palette[pen];
	}
}

UPD7220_DRAW_TEXT_LINE_MEMBER( a5105_state::hgdc_draw_text )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();

	for (int x = 0; x < pitch; x++)
	{
		int const tile = (m_video_ram[(((addr+x)*2) & 0x1ffff) >> 1] & 0xff);
		int const color = ((m_video_ram[(((addr+x)*2) & 0x1ffff) >> 1] >> 8) & 0x0f);

		for (int yi = 0; yi < lr; yi++)
		{
			uint8_t tile_data = m_char_ram[(tile*8+yi) & 0x7ff];

			if (cursor_on && cursor_addr == addr+x && m_screen->frame_number() & 0x10)
				tile_data ^= 0xff;

			for (int xi = 0; xi < 8; xi++)
			{
				int pen = (tile_data >> xi) & 1 ? color : 0;

				int const res_x = x * 8 + xi;
				int const res_y = y + yi;

				if (yi >= 8)
					pen = 0;

				if (m_screen->visible_area().contains(res_x+0, res_y))
					bitmap.pix(res_y, res_x) = palette[pen];
			}
		}
	}
}

void a5105_state::a5105_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).bankr("bank1");
	map(0x4000, 0x7fff).bankr("bank2");
	map(0x8000, 0xbfff).bankrw("bank3");
	map(0xc000, 0xffff).bankrw("bank4");
}

uint8_t a5105_state::pio_pb_r()
{
	/*

	    PIO Channel B

	    0  R    PAR12
	    1  W    SER1
	    2  W    SER2
	    3  R    SER3
	    4  R    SER4
	    5  W    JOY2
	    6  W    /JOYEN
	    7  R    Cassette Data

	*/

	uint8_t data = 0x7f;

	// cassette data
	data |= (m_cass->input() > 0) ? 0x80 : 0;

	return data;
}

void  a5105_state::pcg_addr_w(uint8_t data)
{
	m_pcg_addr = data << 3;
	m_pcg_internal_addr = 0;
}

void a5105_state::pcg_val_w(uint8_t data)
{
	m_char_ram[m_pcg_addr | m_pcg_internal_addr] = data;

	m_gfxdecode->gfx(0)->mark_dirty(m_pcg_addr >> 3);

	m_pcg_internal_addr++;
	m_pcg_internal_addr&=7;
}

uint8_t a5105_state::key_r()
{
	static const char *const keynames[] = { "KEY0", "KEY1", "KEY2", "KEY3",
											"KEY4", "KEY5", "KEY6", "KEY7",
											"KEY8", "KEY9", "KEYA", "UNUSED",
											"UNUSED", "UNUSED", "UNUSED", "UNUSED" };

	return ioport(keynames[m_key_mux & 0x0f])->read();
}

uint8_t a5105_state::key_mux_r()
{
	return m_key_mux;
}

void a5105_state::key_mux_w(uint8_t data)
{
	/*
	    xxxx ---- unknown
	    ---- xxxx keyboard mux
	*/

	m_key_mux = data;
}

void a5105_state::a5105_ab_w(uint8_t data)
{
/*port $ab
        ---- 100x tape motor, active low
        ---- 101x tape data
        ---- 110x led (color green)
        ---- 111x key click, active high
*/
	switch (data & 6)
	{
	case 0:
		if (BIT(data, 0))
			m_cass->change_state(CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR);
		else
			m_cass->change_state(CASSETTE_MOTOR_ENABLED,CASSETTE_MASK_MOTOR);
		break;

	case 2:
		m_cass->output( BIT(data, 0) ? -1.0 : +1.0);
		break;

	case 4:
		//led
		break;

	case 6:
		m_beep->set_state(BIT(data, 0));
		break;
	}
}

uint8_t a5105_state::a5105_memsel_r()
{
	uint8_t res;

	res = (m_memsel[0] & 3) << 0;
	res|= (m_memsel[1] & 3) << 2;
	res|= (m_memsel[2] & 3) << 4;
	res|= (m_memsel[3] & 3) << 6;

	return res;
}

void a5105_state::a5105_memsel_w(uint8_t data)
{
	address_space &prog = m_maincpu->space( AS_PROGRAM );

	if (m_memsel[0] != ((data & 0x03) >> 0))
	{
		m_memsel[0] = (data & 0x03) >> 0;

		switch (m_memsel[0])
		{
		case 0:
			membank("bank1")->set_base(m_rom_base);
			prog.install_read_bank(0x0000, 0x3fff, membank("bank1"));
			prog.unmap_write(0x0000, 0x3fff);
			break;
		case 2:
			membank("bank1")->set_base(m_ram_base);
			prog.install_readwrite_bank(0x0000, 0x3fff, membank("bank1"));
			break;
		default:
			prog.unmap_readwrite(0x0000, 0x3fff);
			break;
		}
	}

	if (m_memsel[1] != ((data & 0x0c) >> 2))
	{
		m_memsel[1] = (data & 0x0c) >> 2;

		switch (m_memsel[1])
		{
		case 0:
			membank("bank2")->set_base(m_rom_base + 0x4000);
			prog.install_read_bank(0x4000, 0x7fff, membank("bank2"));
			prog.unmap_write(0x4000, 0x4000);
			break;
		case 1:
			membank("bank2")->set_base(memregion("k5651")->base());
			prog.install_read_bank(0x4000, 0x7fff, membank("bank2"));
			prog.unmap_write(0x4000, 0x4000);
			break;
		case 2:
			membank("bank2")->set_base(m_ram_base + 0x4000);
			prog.install_readwrite_bank(0x4000, 0x7fff, membank("bank2"));
			break;
		default:
			prog.unmap_readwrite(0x4000, 0x7fff);
			break;
		}
	}

	if (m_memsel[2] != ((data & 0x30) >> 4))
	{
		m_memsel[2] = (data & 0x30) >> 4;

		switch (m_memsel[2])
		{
		case 0:
			membank("bank3")->set_base(m_rom_base + 0x8000);
			prog.install_read_bank(0x8000, 0xbfff, membank("bank3"));
			prog.unmap_write(0x8000, 0xbfff);
			break;
		case 2:
			membank("bank3")->set_base(m_ram_base + 0x8000);
			prog.install_readwrite_bank(0x8000, 0xbfff, membank("bank3"));
			break;
		default:
			prog.unmap_readwrite(0x8000, 0xbfff);
			break;
		}
	}

	if (m_memsel[3] != ((data & 0xc0) >> 6))
	{
		m_memsel[3] = (data & 0xc0) >> 6;

		switch (m_memsel[3])
		{
		case 2:
			membank("bank4")->set_base(m_ram_base + 0xc000);
			prog.install_readwrite_bank(0xc000, 0xffff, membank("bank4"));
			break;
		default:
			prog.unmap_readwrite(0xc000, 0xffff);
			break;
		}
	}

	//printf("Memsel change to %02x %02x %02x %02x\n",m_memsel[0],m_memsel[1],m_memsel[2],m_memsel[3]);
}

void a5105_state::a5105_upd765_w(uint8_t data)
{
	for (int n = 0; n < 4; n++)
		m_floppy[n]->get_device()->mon_w(!BIT(data,n));

	m_fdc->tc_w(BIT(data, 4));
}

void a5105_state::a5105_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x40, 0x41).m(m_fdc, FUNC(upd765a_device::map));
	map(0x48, 0x4f).w(FUNC(a5105_state::a5105_upd765_w));

	map(0x80, 0x83).rw("z80ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x90, 0x93).rw("z80pio", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x98, 0x99).rw(m_hgdc, FUNC(upd7220_device::read), FUNC(upd7220_device::write));

	map(0x9c, 0x9c).w(FUNC(a5105_state::pcg_val_w));
//  map(0x9d, 0x9d) crtc area (ff-based), palette routes here
	map(0x9e, 0x9e).w(FUNC(a5105_state::pcg_addr_w));

//  map(0xa0, 0xa1) ay8910?
	map(0xa8, 0xa8).rw(FUNC(a5105_state::a5105_memsel_r), FUNC(a5105_state::a5105_memsel_w));
	map(0xa9, 0xa9).r(FUNC(a5105_state::key_r));
	map(0xaa, 0xaa).rw(FUNC(a5105_state::key_mux_r), FUNC(a5105_state::key_mux_w));
	map(0xab, 0xab).w(FUNC(a5105_state::a5105_ab_w)); //misc output, see above
}

/* Input ports */
static INPUT_PORTS_START( a5105 )
	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('=')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('\\')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('/')

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR('<') PORT_CHAR('>')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('+') PORT_CHAR('*')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(U'ö') PORT_CHAR(U'Ö')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(U'ä') PORT_CHAR(U'Ä')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(U'ü') PORT_CHAR(U'Ü')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('#') PORT_CHAR('^')

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('\'') PORT_CHAR('`')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('?') PORT_CHAR(U'ß')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR(';')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(':')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED) // appears to do nothing
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')

	PORT_START("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')

	PORT_START("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')

	PORT_START("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) /* gives keyclick but does nothing */PORT_CODE(KEYCODE_LCONTROL)    PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("GRAPH") PORT_CODE(KEYCODE_PGUP) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CAPS") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CODE") PORT_CODE(KEYCODE_PGDN) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))

	PORT_START("KEY7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)      PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)      PORT_CHAR(9)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("STOP") PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)    PORT_CHAR(8)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SELECT") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)        PORT_CHAR(13)

	PORT_START("KEY8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)        PORT_CHAR(' ')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME)     PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT)       PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)     PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)       PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)     PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)        PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

	PORT_START("KEY9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK)     PORT_CHAR(UCHAR_MAMEKEY(ASTERISK))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD)     PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD)    PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)        PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)        PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)        PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)        PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)        PORT_CHAR(UCHAR_MAMEKEY(4_PAD))

	PORT_START("KEYA")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)        PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)        PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)        PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)        PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)        PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD)    PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD)    PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD)      PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))

	PORT_START("UNUSED")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END


void a5105_state::machine_reset()
{
	a5105_ab_w(9); // turn motor off

	m_ram_base = (uint8_t*)m_ram->pointer();
	m_rom_base = (uint8_t*)memregion("maincpu")->base();

	membank("bank1")->set_base(m_rom_base);
	membank("bank2")->set_base(m_rom_base + 0x4000);
	membank("bank3")->set_base(m_ram_base);
	membank("bank4")->set_base(m_ram_base + 0x4000);
}


static const gfx_layout a5105_chars_8x8 =
{
	8,8,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};

static GFXDECODE_START( gfx_a5105 )
	GFXDECODE_ENTRY( "pcg", 0x0000, a5105_chars_8x8, 0, 8 )
GFXDECODE_END


void a5105_state::a5105_palette(palette_device &palette) const
{
	for (int i = 0; i < 16; i++)
	{
		int const x = (i & 8) ? 0xaa : 0xff;
		int const r = (i & 4) ? x : 0x00;
		int const g = (i & 2) ? x : 0x00;
		int const b = (i & 1) ? x : 0x00;

		palette.set_pen_color(i, rgb_t(r, g, b));
	}
}

void a5105_state::video_start()
{
	// find memory regions
	m_char_ram = memregion("pcg")->base();
}

void a5105_state::upd7220_map(address_map &map)
{
	map.global_mask(0x1ffff);
	map(0x00000, 0x1ffff).ram().share("video_ram");
}

void a5105_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_A5105_FORMAT);
}

static void a5105_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

static const z80_daisy_config a5105_daisy_chain[] =
{
	{ "z80ctc" },
	{ "z80pio" },
	{ nullptr }
};

void a5105_state::a5105(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(15'000'000) / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &a5105_state::a5105_mem);
	m_maincpu->set_addrmap(AS_IO, &a5105_state::a5105_io);
	m_maincpu->set_daisy_config(a5105_daisy_chain);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_screen_update("upd7220", FUNC(upd7220_device::screen_update));
	m_screen->set_size(40*8, 32*8);
	m_screen->set_visarea(0, 40*8-1, 0, 25*8-1);
	GFXDECODE(config, m_gfxdecode, m_palette, gfx_a5105);
	PALETTE(config, m_palette, FUNC(a5105_state::a5105_palette), 16);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, "beeper", 500).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* Devices */
	UPD7220(config, m_hgdc, XTAL(15'000'000) / 16); // unk clock
	m_hgdc->set_addrmap(0, &a5105_state::upd7220_map);
	m_hgdc->set_display_pixels(FUNC(a5105_state::hgdc_display_pixels));
	m_hgdc->set_draw_text(FUNC(a5105_state::hgdc_draw_text));

	z80ctc_device& ctc(Z80CTC(config, "z80ctc", XTAL(15'000'000) / 4));
	ctc.intr_callback().set_inputline(m_maincpu, 0);
	ctc.zc_callback<0>().set("z80ctc", FUNC(z80ctc_device::trg2));
	ctc.zc_callback<2>().set("z80ctc", FUNC(z80ctc_device::trg3));

	z80pio_device& pio(Z80PIO(config, "z80pio", XTAL(15'000'000) / 4));
	pio.in_pb_callback().set(FUNC(a5105_state::pio_pb_r));
	pio.out_int_callback().set_inputline(m_maincpu, 0);

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	UPD765A(config, m_fdc, 8'000'000, true, true);
	FLOPPY_CONNECTOR(config, "upd765a:0", a5105_floppies, "525qd", a5105_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "upd765a:1", a5105_floppies, "525qd", a5105_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "upd765a:2", a5105_floppies, "525qd", a5105_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "upd765a:3", a5105_floppies, "525qd", a5105_state::floppy_formats);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("64K");
}

/* ROM definition */
ROM_START( a5105 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "k1505_00.rom", 0x0000, 0x8000, CRC(0ed5f556) SHA1(5c33139db9f59e50da5c76729752f8e653ae34ae))
	ROM_LOAD( "k1505_80.rom", 0x8000, 0x2000, CRC(350e4958) SHA1(7e5b587c59676e8549561117ea0b70234f439a94))

	ROM_REGION( 0x800, "pcg", ROMREGION_ERASE00 )

	ROM_REGION( 0x4000, "k5651", ROMREGION_ERASEFF )
	ROM_LOAD( "k5651_40.rom", 0x0000, 0x2000, CRC(f4ad4739) SHA1(9a7bbe6f0d180dd513c7854f441cee986c8d9765))
	ROM_LOAD( "k5651_60.rom", 0x2000, 0x2000, CRC(c77dde3f) SHA1(7c16226be6c4c71013e8008fba9d2e9c5640b6a7))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY         FULLNAME     FLAGS
COMP( 1989, a5105, 0,      0,      a5105,   a5105, a5105_state, empty_init, "VEB Robotron", "BIC A5105", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



a51xx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

    A51xx

    12/05/2009 Skeleton driver.

    http://www.robotrontechnik.de/index.htm?/html/computer/a5120.htm
    http://www.robotrontechnik.de/index.htm?/html/computer/a5130.htm

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

#include "emu.h"
#include "cpu/z80/z80.h"

#include "emupal.h"
#include "screen.h"


namespace {

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

	void a5130(machine_config &config);
	void a5120(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	DECLARE_MACHINE_RESET(a5130);
	DECLARE_VIDEO_START(a5130);
	uint32_t screen_update_a5120(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	uint32_t screen_update_a5130(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	required_device<cpu_device> m_maincpu;
	void a5120_io(address_map &map) ATTR_COLD;
	void a5120_mem(address_map &map) ATTR_COLD;
	void a5130_io(address_map &map) ATTR_COLD;
	void a5130_mem(address_map &map) ATTR_COLD;
};


void a51xx_state::a5120_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x03ff).rom();
	map(0x0400, 0xffff).ram();
}

void a51xx_state::a5120_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
}

void a51xx_state::a5130_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).rom();
	map(0x1000, 0xffff).ram();
}

void a51xx_state::a5130_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
}


/* Input ports */
static INPUT_PORTS_START( a5120 )
INPUT_PORTS_END


void a51xx_state::machine_reset()
{
}

void a51xx_state::machine_start()
{
}

uint32_t a51xx_state::screen_update_a5120(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}


/* Input ports */
static INPUT_PORTS_START( a5130 )
INPUT_PORTS_END


MACHINE_RESET_MEMBER(a51xx_state,a5130)
{
}

VIDEO_START_MEMBER(a51xx_state,a5130)
{
}

uint32_t a51xx_state::screen_update_a5130(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}

/* F4 Character Displayer */
static const gfx_layout a51xx_charlayout =
{
	8, 16,                  /* 8 x 16 characters */
	128,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 1024*8, 1025*8, 1026*8, 1027*8, 1028*8, 1029*8, 1030*8, 1031*8 },
	8*8                 /* every char takes 2 x 8 bytes */
};

static GFXDECODE_START( gfx_a51xx )
	GFXDECODE_ENTRY( "chargen", 0x0000, a51xx_charlayout, 0, 1 )
GFXDECODE_END

void a51xx_state::a5120(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &a51xx_state::a5120_mem);
	m_maincpu->set_addrmap(AS_IO, &a51xx_state::a5120_io);


	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 480);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(a51xx_state::screen_update_a5120));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_a51xx);

	PALETTE(config, "palette", palette_device::MONOCHROME);
}

void a51xx_state::a5130(machine_config &config)
{
	a5120(config);
	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &a51xx_state::a5130_mem);
	m_maincpu->set_addrmap(AS_IO, &a51xx_state::a5130_io);

	MCFG_MACHINE_RESET_OVERRIDE(a51xx_state,a5130)

	/* video hardware */
	subdevice<screen_device>("screen")->set_screen_update(FUNC(a51xx_state::screen_update_a5130));

	MCFG_VIDEO_START_OVERRIDE(a51xx_state,a5130)
}

/* ROM definition */
ROM_START( a5120 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "v1", "v1" )
	ROMX_LOAD( "a5120_v1.rom", 0x0000, 0x0400, CRC(b2b3fee0) SHA1(6198513b263d8a7a867f1dda368b415bb37fcdae), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v2", "v2" )
	ROMX_LOAD( "a5120_v2.rom", 0x0000, 0x0400, CRC(052386c2) SHA1(e545d30a0882cb7ee7acdbea842b57440552e4a6), ROM_BIOS(1))

	ROM_REGION(0x0800, "chargen",0)
	ROM_LOAD( "bab47_1_lat.bin", 0x0000, 0x0400, CRC(93220886) SHA1(a5a1ab4e2e06eabc58c84991caa6a1cf55f1462d))
	ROM_LOAD( "bab46_2_lat.bin", 0x0400, 0x0400, CRC(7a578ec8) SHA1(d17d3f1c182c23e9e9fd4dd56f3ac3de4c18fb1a))
ROM_END

/* ROM definition */
ROM_START( a5130 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "dzr5130.rom", 0x0000, 0x1000, CRC(4719beb7) SHA1(09295a658b8c5b75b20faea57ad925f69f07a9b5))

	ROM_REGION(0x0800, "chargen",0)
	ROM_LOAD( "bab47_1_lat.bin", 0x0000, 0x0400, CRC(93220886) SHA1(a5a1ab4e2e06eabc58c84991caa6a1cf55f1462d))
	ROM_LOAD( "bab46_2_lat.bin", 0x0400, 0x0400, CRC(7a578ec8) SHA1(d17d3f1c182c23e9e9fd4dd56f3ac3de4c18fb1a))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY         FULLNAME  FLAGS
COMP( 1982, a5120, 0,      0,      a5120,   a5120, a51xx_state, empty_init, "VEB Robotron", "A5120",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )
COMP( 1983, a5130, a5120,  0,      a5130,   a5130, a51xx_state, empty_init, "VEB Robotron", "A5130",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



a7150.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Sergey Svishchev
/***************************************************************************

Robotron A7150

2009-10-04 Skeleton driver.

http://www.robotrontechnik.de/index.htm?/html/computer/a7150.htm

http://www.tiffe.de/Robotron/MMS16/
- Confidence test is documented in A7150_Rechner...pdf, pp. 112-119
- Internal test of KGS -- in KGS-K7070.pdf, pp. 19-23

After about a minute, the self-test will appear.

To do:
- native keyboard -- K7637 and/or K7672
- interrupts
- ABS K7071 (text-only video)
- ABG K7075 (CGA compatible video)
- KES K5170 media controller (derived from iSBC 215A but not 100% compatible)
- boot BOS (iRMX clone)
- boot DCP (DOS clone)
- boot MUTOS (V7 UNIX) to multiuser
- boot SCP (CP/M clone)
- A7100 model (KES at 0x100 etc.)

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

#include "emu.h"

#include "isbc_215g.h"

#include "bus/multibus/multibus.h"
#include "bus/multibus/robotron_k7070.h"
#include "bus/multibus/robotron_k7071.h"
#include "bus/rs232/rs232.h"
#include "cpu/i86/i86.h"
#include "machine/i8087.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/keyboard.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"


namespace {


class a7150_state : public driver_device
{
public:
	a7150_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_bus(*this, "bus")
		, m_maincpu(*this, "maincpu")
		, m_uart8251(*this, "uart8251")
		, m_pit8253(*this, "pit8253")
		, m_pic8259(*this, "pic8259")
		, m_rs232(*this, "rs232")
	{ }

	void a7150(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	void ppi_c_w(uint8_t data);

	bool m_ifss_loopback = 0;

	required_device<multibus_device> m_bus;
	required_device<i8086_cpu_device> m_maincpu;
	required_device<i8251_device> m_uart8251;
	required_device<pit8253_device> m_pit8253;
	required_device<pic8259_device> m_pic8259;
	required_device<rs232_port_device> m_rs232;

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	u8 bus_pio_r(offs_t offset) { return m_bus->space(AS_IO).read_byte(offset); }
	void bus_pio_w(offs_t offset, u8 data) { m_bus->space(AS_IO).write_byte(offset, data); }
};


void a7150_state::ppi_c_w(uint8_t data)
{
	// b0 -- INTR(B)
	// b1 -- /OBF(B)
	// m_centronics->write_ack(BIT(data, 2));
	// m_centronics->write_strobe(BIT(data, 3));
	// b7 -- /NMIDIS
}

void a7150_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0xf7fff).ram();
	map(0xf8000, 0xfffff).rom().region("maincpu", 0);
}

void a7150_state::io_map(address_map &map)
{
	map.unmap_value_high();
	// map PIO to Multibus by default
	map(0x0000, 0xffff).rw(FUNC(a7150_state::bus_pio_r), FUNC(a7150_state::bus_pio_w));
//  map(0x0000, 0x0003).unmaprw(); // memory parity 1-2
//  map(0x0040, 0x0043).unmaprw(); // memory parity 3-4
	map(0x004a, 0x004a).w("isbc_215g", FUNC(isbc_215g_device::write)); // KES board
	map(0x00c0, 0x00c3).rw(m_pic8259, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x00c8, 0x00cf).rw("ppi8255", FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
	map(0x00d0, 0x00d7).rw(m_pit8253, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0x00d8, 0x00db).rw(m_uart8251, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
//  map(0x0300, 0x031f).unmaprw(); // ASP board #1
//  map(0x0320, 0x033f).unmaprw(); // ASP board #2
}

static DEVICE_INPUT_DEFAULTS_START( kbd_rs232_defaults )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_28800 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END

void a7150_state::machine_reset()
{
	m_ifss_loopback = false;
}

void a7150_state::machine_start()
{
	save_item(NAME(m_ifss_loopback));
}

static void a7150_cards(device_slot_interface &device)
{
	device.option_add("kgs", ROBOTRON_K7070);
	device.option_add("abs", ROBOTRON_K7071);
}

/*
 * K2771.30 ZRE - processor board
 * K3571    OPS - 256KB RAM board (x4)
 * K7070    KGS - graphics terminal, running firmware from A7100
 * K7072    ABG - dumb grayscale framebuffer
 * K5170    KES - media controller
 */
void a7150_state::a7150(machine_config &config)
{
	MULTIBUS(config, m_bus, 10_MHz_XTAL); // FIXME: clock driven by bus master
	m_bus->int_callback<1>().set(m_pic8259, FUNC(pic8259_device::ir1_w));
	m_bus->int_callback<6>().set(m_pic8259, FUNC(pic8259_device::ir6_w));
	m_bus->int_callback<7>().set(m_pic8259, FUNC(pic8259_device::ir7_w));
	MULTIBUS_SLOT(config, "slot1", m_bus, a7150_cards, "kgs", false);

	// ZRE board

	I8086(config, m_maincpu, XTAL(9'832'000) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &a7150_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &a7150_state::io_map);
	m_maincpu->set_irq_acknowledge_callback("pic8259", FUNC(pic8259_device::inta_cb));
	m_maincpu->esc_opcode_handler().set("i8087", FUNC(i8087_device::insn_w));
	m_maincpu->esc_data_handler().set("i8087", FUNC(i8087_device::addr_w));

	i8087_device &i8087(I8087(config, "i8087", XTAL(9'832'000) / 2));
	i8087.set_space_86(m_maincpu, AS_PROGRAM);
	i8087.irq().set(m_pic8259, FUNC(pic8259_device::ir0_w));
	i8087.busy().set_inputline("maincpu", INPUT_LINE_TEST);

	PIC8259(config, m_pic8259, 0);
	m_pic8259->out_int_callback().set_inputline(m_maincpu, 0);

	// IFSP port (IRQ 4)
	i8255_device &ppi(I8255(config, "ppi8255"));
//  ppi.in_pa_callback().set("cent_status_in", FUNC(input_buffer_device::read));
//  ppi.out_pb_callback().set("cent_data_out", output_latch_device::write));
	ppi.out_pc_callback().set(FUNC(a7150_state::ppi_c_w));

	PIT8253(config, m_pit8253, 0);
	m_pit8253->set_clk<0>(14.7456_MHz_XTAL / 4);
	m_pit8253->out_handler<0>().set(m_pic8259, FUNC(pic8259_device::ir2_w));
	m_pit8253->set_clk<1>(14.7456_MHz_XTAL / 4);
	m_pit8253->set_clk<2>(14.7456_MHz_XTAL / 4);
	m_pit8253->out_handler<2>().set([this] (bool state) { m_uart8251->write_rxc(state); m_uart8251->write_txc(state); });

	// IFSS port -- keyboard runs at 28800 8N2
	I8251(config, m_uart8251, 0);
	m_uart8251->txd_handler().set([this] (bool state) { if (m_ifss_loopback) m_uart8251->write_rxd(state); else m_rs232->write_txd(state); });
	m_uart8251->dtr_handler().set([this] (bool state) { if (m_ifss_loopback) m_uart8251->write_dsr(state); else m_rs232->write_dtr(state); });
	m_uart8251->rts_handler().set([this] (bool state) { m_ifss_loopback = !state; });
	m_uart8251->rxrdy_handler().set(m_pic8259, FUNC(pic8259_device::ir6_w));

	RS232_PORT(config, m_rs232, default_rs232_devices, "keyboard");
	m_rs232->rxd_handler().set(m_uart8251, FUNC(i8251_device::write_rxd));
	m_rs232->cts_handler().set(m_uart8251, FUNC(i8251_device::write_cts));
	m_rs232->dsr_handler().set(m_uart8251, FUNC(i8251_device::write_dsr));
	m_rs232->set_option_device_input_defaults("keyboard", DEVICE_INPUT_DEFAULTS_NAME(kbd_rs232_defaults));

	// KES board

	ISBC_215G(config, "isbc_215g", 0, 0x4a, m_maincpu).irq_callback().set(m_pic8259, FUNC(pic8259_device::ir5_w));
}

/* ROM definition */
ROM_START( a7150 )
	ROM_REGION16_LE( 0x8000, "maincpu", 0 )
	ROM_DEFAULT_BIOS("2.3")

	// A7100
	ROM_SYSTEM_BIOS(0, "1.1", "ACT 1.1")
	ROMX_LOAD("q259.bin", 0x4001, 0x2000, CRC(fb5b547b) SHA1(1d17fcededa91cad321a7b237a46a308142d902b), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("q260.bin", 0x0001, 0x2000, CRC(b51f8ed6) SHA1(9aa6291bf8ab49a343741717366992649e2957b3), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("q261.bin", 0x4000, 0x2000, CRC(43c08ea3) SHA1(ea697180b415b71d834968be84431a6efe9490c2), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("q262.bin", 0x0000, 0x2000, CRC(9df1c396) SHA1(a627889e1162e5b2fe95804de52bb78e41aaf7cc), ROM_BIOS(0) | ROM_SKIP(1))

	// A7150
	ROM_SYSTEM_BIOS(1, "2.1", "ACT 2.1") // requires K7075 video card
	ROMX_LOAD("265.bin",  0x4001, 0x2000, CRC(a5fb5f35) SHA1(9d9501441cad0ef724dec7b5ffb52b17a678a9f8), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("266.bin",  0x0001, 0x2000, CRC(f5898eb7) SHA1(af3fd82813fbea7883dea4d7e23a9b5e5b2b844a), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("267.bin",  0x4000, 0x2000, CRC(c1873a01) SHA1(77f15cc217cd854732fbe33d395e1ea9867fedd7), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("268.bin",  0x0000, 0x2000, CRC(e3f09213) SHA1(1e2d69061f8e84697440b219181e0b870fe21835), ROM_BIOS(1) | ROM_SKIP(1))

	ROM_SYSTEM_BIOS(2, "2.2", "ACT 2.2")
	ROMX_LOAD("269.bin",  0x4001, 0x2000, CRC(f137f94b) SHA1(7cb79f332db48cb66dae04c1ce1bdd169a6ab561), ROM_BIOS(2) | ROM_SKIP(1))
	ROMX_LOAD("270.bin",  0x0001, 0x2000, CRC(1ea44a33) SHA1(f5708d1f6a9dc109979a9a91a80f2a4e4956d1eb), ROM_BIOS(2) | ROM_SKIP(1))
	ROMX_LOAD("271.bin",  0x4000, 0x2000, CRC(de2222c9) SHA1(e02225c93b49f0380dfb2d996b63370141359199), ROM_BIOS(2) | ROM_SKIP(1))
	ROMX_LOAD("272.bin",  0x0000, 0x2000, CRC(5001c528) SHA1(ce67c35326fbfd17f086a37ffe81b79aefaef0cb), ROM_BIOS(2) | ROM_SKIP(1))

	ROM_SYSTEM_BIOS(3, "2.3", "ACT 2.3")
	ROMX_LOAD("273.rom",  0x4001, 0x2000, CRC(67ca9b78) SHA1(bcb6221f6df28b24b602846b149ac12e93b5e356), ROM_BIOS(3) | ROM_SKIP(1))
	ROMX_LOAD("274.rom",  0x0001, 0x2000, CRC(6fa68834) SHA1(49abe48bbb5ae151f977a9c63b27336c15e8a08d), ROM_BIOS(3) | ROM_SKIP(1))
	ROMX_LOAD("275.rom",  0x4000, 0x2000, CRC(0da54426) SHA1(7492caff98b1d1a896c5964942b17beadf996b60), ROM_BIOS(3) | ROM_SKIP(1))
	ROMX_LOAD("276.rom",  0x0000, 0x2000, CRC(5924192a) SHA1(eb494d9f96a0b3ea69f4b9cb2b7add66a8c16946), ROM_BIOS(3) | ROM_SKIP(1))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY         FULLNAME  FLAGS
COMP( 1986, a7150, 0,      0,      a7150,   0,     a7150_state, empty_init, "VEB Robotron", "A7150",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



a7800.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Dan Boris, Fabio Priuli, Mike Saarna, Robert Tuccitto
/***************************************************************************

  Driver file to handle emulation of the Atari 7800.

  Dan Boris

    2002/05/13 kubecj   added more banks for bankswitching
                        added PAL machine description
                        changed clock to be precise
                        improved cart emulation (in machine/)

    2012/10/25 Robert Tuccitto  NTSC Color Generator utilized for
                color palette with hue shift/start
                based on observation of several
                systems across multiple displays

    2012/11/09 Robert Tuccitto  Fixed 3 degree hue begin point
                miscalculation of color palette

    2012/12/05 Robert Tuccitto  Implemented proper IRE and phase
               value to the palette

    2012/12/14 Robert Tuccitto  Adjusted colorburst/tint/hue of entire
               palette to closer reflect default hardware configuration
               setting of ~180 degrees.  Palette settings now correspond
               documented and calculated settings as follows:

               Contrast = 0.0526 --> 0.05
               Brightness = 0.0 --> 0.00
               Color = 0.2162 --> 0.22
               Phase = 25.714 --> 25.7
               Colorburst/Hue = 180 degrees

    2013/02/27 Robert Tuccitto  Palette rebuild due to misaligned
               data references.  Corrected PAL color sequence order.

    2013/03/19 Robert Tuccitto  Stripped palette to raw video output
               values removing YIQ/YUV infer data.

    2013/04/02 Robert Tuccitto  Corrected rotation values and errors
               including duplicate entries for palette.

    2013/04/07 Robert Tuccitto  Address map locations for the XBOARD
               added.

    2013/05/01 Robert Tuccitto  Red and Blue miscalculated proportions
               fixed.

    2013/08/04 Robert Tuccitto  Green miscalculated proportions fixed.

    2013/08/13 Robert Tuccitto  Normalized contrast and brightness,
               providing a standardize grayscale and adjusted color values.

    2013/09/02 Robert Tuccitto  Stored data for 26.7 & 27.7 phase shifts
               with corrections and label for 25.7 values. Made 26.7
               (medium) default. Phase shifting falls outside the realm of
               video controls and hope to implement a selectable toggle
               hardware option similar to Donkey Kong TKG02/TKG04.

    2013/09/19 Robert Tuccitto  Cleanup of Address Maps, high score maps
               added.

    2013/10/16 Robert Tuccitto  Added Phase Shifts 24.7, 25.2, 26.2, 27.2.
               Phase Shifts 24.7 through 27.7 degrees with 0.5 degree
               increments documented. Phase Shift 26.2 degrees made active.
               Fixed typo under 26.7 7$.

    2013/10/27 Robert Tuccitto  Modernized screen parameters for NTSC & PAL.

    2013/11/03 Robert Tuccitto  Fixed correctly typo under 26.7 7$.

    2013/11/23 Robert Tuccitto  Added NTSC Palette Notes.

    2014/01/02 Robert Tuccitto  Corrected joystick buttons assignment & minor
                                palette notes cleanup.

    2014/01/09 Robert Tuccitto  Positional description for difficulty
                                switches added.

    2014/02/15 Robert Tuccitto  Added more details and clarification
                                regarding the potentiometer.

    2014/03/25 Mike Saarna  Fixed Riot Timer

    2014/04/04 Mike Saarna  Fix to controller button RIOT behavior

    2014/05/06 Mike Saarna/Robert Tuccitto Brought initial Maria cycle counts
               inline from measurements taken with logic analyzer and tests.

    2014/08/25 Fabio Priuli Converted carts to be slot devices and cleaned
               up the driver (removed the pokey, cleaned up rom regions, etc.)

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

#include "emu.h"

#include "maria.h"

#include "bus/a7800/a78_carts.h"
#include "cpu/m6502/m6502.h"
#include "machine/mos6530.h"
#include "machine/timer.h"
#include "sound/tiaintf.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class a7800_state : public driver_device
{
public:
	a7800_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_tia(*this, "tia"),
		m_maria(*this, "maria"),
		m_riot(*this, "riot"),
		m_io_joysticks(*this, "joysticks"),
		m_io_buttons(*this, "buttons"),
		m_io_console_buttons(*this, "console_buttons"),
		m_cart(*this, "cartslot"),
		m_screen(*this, "screen"),
		m_bios(*this, "maincpu")
	{
	}

protected:
	void a7800_common(machine_config &config, uint32_t clock) ATTR_COLD;

	uint8_t bios_or_cart_r(offs_t offset);
	uint8_t tia_r(offs_t offset);
	void tia_w(offs_t offset, uint8_t data);
	virtual void a7800_palette(palette_device &palette) const ATTR_COLD;
	TIMER_DEVICE_CALLBACK_MEMBER(interrupt);
	TIMER_CALLBACK_MEMBER(maria_startdma);
	uint8_t riot_joystick_r();
	uint8_t riot_console_button_r();
	void riot_button_pullup_w(uint8_t data);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	void a7800_mem(address_map &map) ATTR_COLD;

	int m_lines;
	int m_ispal;

	int m_ctrl_lock;
	int m_ctrl_reg;
	int m_maria_flag;
	int m_p1_one_button;
	int m_p2_one_button;
	int m_bios_enabled;

	emu_timer *m_dma_start_timer = nullptr;

	required_device<cpu_device> m_maincpu;
	required_device<tia_device> m_tia;
	required_device<atari_maria_device> m_maria;
	required_device<mos6532_device> m_riot;
	required_ioport m_io_joysticks;
	required_ioport m_io_buttons;
	required_ioport m_io_console_buttons;
	required_device<a78_cart_slot_device> m_cart;
	required_device<screen_device> m_screen;
	required_region_ptr<uint8_t> m_bios;
};

class a7800_ntsc_state : public a7800_state
{
public:
	using a7800_state::a7800_state;
	void init_a7800_ntsc() ATTR_COLD;
	void a7800_ntsc(machine_config &config) ATTR_COLD;
};

class a7800_pal_state : public a7800_state
{
public:
	using a7800_state::a7800_state;
	void init_a7800_pal() ATTR_COLD;
	void a7800_pal(machine_config &config) ATTR_COLD;

protected:
	virtual void a7800_palette(palette_device &palette) const override ATTR_COLD;
};


/***************************************************************************
 MEMORY HANDLERS
 ***************************************************************************/

// RIOT
uint8_t a7800_state::riot_joystick_r()
{
	return m_io_joysticks->read();
}

uint8_t a7800_state::riot_console_button_r()
{
	return m_io_console_buttons->read();
}

void a7800_state::riot_button_pullup_w(uint8_t data)
{
	// pin 6 of the controller port is held high by the riot chip when reading two-button controllers (from schematic)
	m_p1_one_button = data & 0x04;
	m_p2_one_button = data & 0x10;
}

uint8_t a7800_state::tia_r(offs_t offset)
{
	switch (offset & 0x0f)
	{
	case 0x00:
	case 0x01:
	case 0x02:
	case 0x03:
	case 0x04:
	case 0x05:
	case 0x06:
	case 0x07:
		/* Even though the 7800 doesn't use the TIA graphics the collision registers should
		 still return a reasonable value */
		return 0x00;
	case 0x08:
		return ((m_io_buttons->read() & 0x02) << 6);
	case 0x09:
		return ((m_io_buttons->read() & 0x08) << 4);
	case 0x0a:
		return ((m_io_buttons->read() & 0x01) << 7);
	case 0x0b:
		return ((m_io_buttons->read() & 0x04) << 5);
	case 0x0c:
		if (((m_io_buttons->read() & 0x08) || (m_io_buttons->read() & 0x02)) && m_p1_one_button)
			return 0x00;
		else
			return 0x80;
	case 0x0d:
		if (((m_io_buttons->read() & 0x01) || (m_io_buttons->read() & 0x04)) && m_p2_one_button)
			return 0x00;
		else
			return 0x80;
	default:
		logerror("undefined TIA read %x\n",offset);
	}
	return 0xff;
}

// TIA
void a7800_state::tia_w(offs_t offset, uint8_t data)
{
	if (offset < 0x20)
	{ //INPTCTRL covers TIA registers 0x00-0x1F until locked
		if (data & 0x01)
		{
			if (m_ctrl_lock && offset == 0x01)
				m_maria_flag = 1;
			else if (!m_ctrl_lock)
				m_maria_flag = 1;
		}
		if (!m_ctrl_lock)
		{
			m_ctrl_lock = data & 0x01;
			m_ctrl_reg = data;
		}
	}
	m_tia->tia_sound_w(offset, data);
}


// TIMERS

TIMER_DEVICE_CALLBACK_MEMBER(a7800_state::interrupt)
{
	// DMA Begins 7 cycles after hblank
	m_dma_start_timer->adjust(m_maincpu->cycles_to_attotime(7));
	m_maria->interrupt(m_lines);
}

TIMER_CALLBACK_MEMBER(a7800_state::maria_startdma)
{
	m_maria->startdma(m_lines);
}



// ROM
uint8_t a7800_state::bios_or_cart_r(offs_t offset)
{
	if (!(m_ctrl_reg & 0x04))
		return m_bios[offset];
	else
		return m_cart->read_40xx(offset + 0x8000);
}

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

void a7800_state::a7800_mem(address_map &map)
{
	map(0x0000, 0x01ff).mirror(0x2000).ram(); // 0x40-0xff, 0x140-0x1ff are mirrors of second 6116 chip
	map(0x0000, 0x001f).mirror(0x300).rw(FUNC(a7800_state::tia_r), FUNC(a7800_state::tia_w));
	map(0x0020, 0x003f).mirror(0x300).rw(m_maria, FUNC(atari_maria_device::read), FUNC(atari_maria_device::write));
	map(0x0280, 0x029f).mirror(0x160).m(m_riot, FUNC(mos6532_device::io_map));
	map(0x0480, 0x04ff).mirror(0x100).m(m_riot, FUNC(mos6532_device::ram_map));
	map(0x1800, 0x1fff).ram();
	map(0x2200, 0x27ff).ram();  // 0x2000-0x21ff, installed in mirror above
								// According to the official Software Guide, the RAM at 0x2000 is
								// repeatedly mirrored up to 0x3fff, but this is evidently incorrect
								// because the High Score Cartridge maps ROM at 0x3000-0x3fff
								// Hardware tests show that only the page at 0x2700 appears at
								// 0x2800, and only on some hardware (MARIA? motherboard?) revisions,
								// and even then with inconsistent and unreliable results.
	map(0x4000, 0xffff).w(m_cart, FUNC(a78_cart_slot_device::write_40xx));
	map(0x4000, 0xbfff).r(m_cart, FUNC(a78_cart_slot_device::read_40xx));
	map(0xc000, 0xffff).r(FUNC(a7800_state::bios_or_cart_r));    // here also the BIOS can be accessed
}


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

static INPUT_PORTS_START( a7800 )
	PORT_START("joysticks")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)    PORT_PLAYER(2) PORT_8WAY
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)  PORT_PLAYER(2) PORT_8WAY
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)  PORT_PLAYER(2) PORT_8WAY
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)    PORT_PLAYER(1) PORT_8WAY
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)  PORT_PLAYER(1) PORT_8WAY
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)  PORT_PLAYER(1) PORT_8WAY
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(1) PORT_8WAY

	PORT_START("buttons")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON2)       PORT_PLAYER(2)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON2)       PORT_PLAYER(1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_BUTTON1)       PORT_PLAYER(2)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_BUTTON1)       PORT_PLAYER(1)
	PORT_BIT(0xF0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("console_buttons")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER)  PORT_NAME("Reset")         PORT_CODE(KEYCODE_U)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER)  PORT_NAME("Select")        PORT_CODE(KEYCODE_I)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER)  PORT_NAME(DEF_STR(Pause))  PORT_CODE(KEYCODE_O)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_DIPNAME(0x40, 0x40, "Left Difficulty Switch")
	PORT_DIPSETTING(0x40, "A - Right Position" )
	PORT_DIPSETTING(0x00, "B - Left Position" )
	PORT_DIPNAME(0x80, 0x80, "Right Difficulty Switch")
	PORT_DIPSETTING(0x80, "A - Right Position" )
	PORT_DIPSETTING(0x00, "B - Left Position" )
INPUT_PORTS_END

/***************************************************************************
Atari 7800 NTSC Palette Notes:

Palette on a modern flat panel display (LCD, LED, Plasma, etc.) appears
different from a traditional CRT. The most outstanding difference is Hue 1x,
the hue begin point. Hue 1x looks very 'green' (~-60 to -45 degrees -
depending on how poor or well it handles the signal conversion and its
calibration) on a modern flat panel display, as opposed to 'gold' (~-33
degrees) on a CRT.

The potentiometer (pot adjustment) for the 7800 modifies the delay line
regarding colors it will exhibit and is extremely sensitive.  The slightest
turn can have a significant impact.

A system whose potentiometer is not properly calibrated via
'CPS 7800 Diagnostic Test Cartridge' or/and just slightly off from the
desired factory settings may exhibit consequences such as too much blue in
place of green (Pot adjusted slightly too far left) or washed out reddish
tones in place of where most other systems display a darker reddish tones
(Pot adjusted slightly too far right).

This is a result of the phase shifting of lesser degrees (Pot adjusted more
to the left) or phase shifting of greater degrees (Pot adjusted more to the
right).

Turning the pot adjustment to the right, it can be observed that the values
of the higher end of the scale will match the lower end of the scale.
For example, after some turning to the right, the values of Dx, Ex, Fx,
can be set to match 1x, 2x, 3x.

After further turning to the right, now the palette can be brought to make
Ax, Bx, Cx, Dx, Ex, Fx will match 1x, 2x, 3x, 4x, 5x, 6x.

Ultimately though, too much turning to the right results in all colors being
wiped from the scale, excluding the hue begin point 1x (Which remains
unchanged while tweaking the potentiometer either left or right).

Continuously turning the pot adjustment to the left, red and blue become the
most dominant two colors encompassing the palette with only a slight
influence of green at the highest end of the palette (Fx), once turned all
the way leftward.

The degree range for adjustment of the phase shifting on the 7800 appears
to be as low as approximately 15 degrees when tuned all the way left, and
seems to be able to achieve as high as approximately 45 degrees when turned
right before losing all color (Excluding 1x) from the palette scale.

For even a properly calibrated system at power on, the system's phase
shift appears as low as ~23 degrees and after a considerable consistent
runtime ('warm-up'), can be as high as ~28 degrees.

In general, the low end of ~23 degrees lasts for maybe several seconds,
whereas higher values such as ~25-27 degrees is the most dominant during
system run time.  180 degrees colorburst takes place at ~25.7 degrees (A
near exact match of Hue 1x and 15x - To the naked eye they appear to be
the same).

However, consistent system run time causes Hue 15x (F$) to become
stronger/darker gold (More brown then ultimately red-brown); as well
as leans Hue 14x (E$) more brown than green.  Once achieving a phase shift
of 27.7, Hue 14x (E$) and Hue 15x (F$) near-exact match Hue 1x and 2x
respectively.

Therefore, an ideal phase shift while accounting for the reality of
shifting while warming up, as well as maintaining differences between 1x,
2x and 14x, 15x, would likely fall between a 25.7 and 27.7. Phase shifts
26.2 degrees and 26.7 degrees places Hue 15x (F$) between Hue 1x and
Hue 2x, having 26.2 degrees leaning closer to Hue 1x and 26.7 degrees
leaning closer to Hue 2x.

The above notion would also harmonize with what has been documented for
the colors of 1x, 2x, 14x, 15x on the 7800.  1x = Gold, 2x = Orange,
14x (E$) = Orange-Green. 15x (F$) = Light Orange.  Color descriptions are
best measured in the middle of the brightness scale.  It should be
mentioned that Green-Yellow is referenced at Hue 13x (D$), nowhere near
Hue 1x.  A Green-Yellow Hue 1x is how the palette is manipulated and
modified (in part) under a modern flat panel display.

Additionally, the blue to red (And consequently blue to green) ratio
proportions may appear different on a modern flat panel display than a CRT
in some instances for the Atari 7800 system.  Furthermore, you may have
some variation of proportions even within the same display type.

One side effect of this on the console's palette is that some values of
red may appear too pinkish - Too much blue to red.  This is not the same
as a traditional tint-hue control adjustment; rather, can be demonstrated
by changing the blue ratio values via MAME HLSL settings.

Lastly, the Atari 2600 & 5200 NTSC color palettes hold the same hue
structure order and have similar appearance differences that are dependent
upon display type.
***************************************************************************/

/***************************************************************************
    PALETTE - 25.7 PHASE SHIFT
***************************************************************************/

#define NTSC_GREY \
	rgb_t(0x00,0x00,0x00), rgb_t(0x11,0x11,0x11), rgb_t(0x22,0x22,0x22), rgb_t(0x33,0x33,0x33), \
	rgb_t(0x44,0x44,0x44), rgb_t(0x55,0x55,0x55), rgb_t(0x66,0x66,0x66), rgb_t(0x77,0x77,0x77), \
	rgb_t(0x88,0x88,0x88), rgb_t(0x99,0x99,0x99), rgb_t(0xAA,0xAA,0xAA), rgb_t(0xBB,0xBB,0xBB), \
	rgb_t(0xCC,0xCC,0xCC), rgb_t(0xDD,0xDD,0xDD), rgb_t(0xEE,0xEE,0xEE), rgb_t(0xFF,0xFF,0xFF   )

#define NTSC_GOLD \
	rgb_t(0x1A,0x07,0x00), rgb_t(0x2B,0x18,0x00), rgb_t(0x3C,0x29,0x00), rgb_t(0x4D,0x3A,0x00), \
	rgb_t(0x5E,0x4B,0x00), rgb_t(0x6F,0x5C,0x00), rgb_t(0x80,0x6D,0x00), rgb_t(0x91,0x7E,0x09), \
	rgb_t(0xA2,0x8F,0x1A), rgb_t(0xB3,0xA0,0x2B), rgb_t(0xC4,0xB1,0x3C), rgb_t(0xD5,0xC2,0x4D), \
	rgb_t(0xE6,0xD3,0x5E), rgb_t(0xF7,0xE4,0x6F), rgb_t(0xFF,0xF5,0x83), rgb_t(0xFF,0xF7,0x97   )

#define NTSC_ORANGE \
	rgb_t(0x31,0x00,0x00), rgb_t(0x42,0x06,0x00), rgb_t(0x53,0x17,0x00), rgb_t(0x64,0x28,0x00), \
	rgb_t(0x75,0x39,0x00), rgb_t(0x86,0x4A,0x00), rgb_t(0x97,0x5B,0x0A), rgb_t(0xA8,0x6C,0x1B), \
	rgb_t(0xB9,0x7D,0x2C), rgb_t(0xCA,0x8E,0x3D), rgb_t(0xDB,0x9F,0x4E), rgb_t(0xEC,0xB0,0x5F), \
	rgb_t(0xFD,0xC1,0x70), rgb_t(0xFF,0xD2,0x85), rgb_t(0xFF,0xE3,0x9C), rgb_t(0xFF,0xF4,0xB2   )

#define NTSC_RED_ORANGE \
	rgb_t(0x3E,0x00,0x00), rgb_t(0x4F,0x00,0x00), rgb_t(0x60,0x08,0x00), rgb_t(0x71,0x19,0x00), \
	rgb_t(0x82,0x2A,0x0D), rgb_t(0x93,0x3B,0x1E), rgb_t(0xA4,0x4C,0x2F), rgb_t(0xB5,0x5D,0x40), \
	rgb_t(0xC6,0x6E,0x51), rgb_t(0xD7,0x7F,0x62), rgb_t(0xE8,0x90,0x73), rgb_t(0xF9,0xA1,0x83), \
	rgb_t(0xFF,0xB2,0x98), rgb_t(0xFF,0xC3,0xAE), rgb_t(0xFF,0xD4,0xC4), rgb_t(0xFF,0xE5,0xDA   )

#define NTSC_PINK \
	rgb_t(0x3F,0x00,0x03), rgb_t(0x50,0x00,0x0F), rgb_t(0x61,0x00,0x1B), rgb_t(0x72,0x0F,0x2B), \
	rgb_t(0x83,0x20,0x3C), rgb_t(0x94,0x31,0x4D), rgb_t(0xA5,0x42,0x5E), rgb_t(0xB6,0x53,0x6F), \
	rgb_t(0xC7,0x64,0x80), rgb_t(0xD8,0x75,0x91), rgb_t(0xE9,0x86,0xA2), rgb_t(0xFA,0x97,0xB3), \
	rgb_t(0xFF,0xA8,0xC8), rgb_t(0xFF,0xB9,0xDE), rgb_t(0xFF,0xCA,0xEF), rgb_t(0xFF,0xDB,0xF4   )

#define NTSC_PURPLE \
	rgb_t(0x33,0x00,0x35), rgb_t(0x44,0x00,0x41), rgb_t(0x55,0x00,0x4C), rgb_t(0x66,0x0C,0x5C), \
	rgb_t(0x77,0x1D,0x6D), rgb_t(0x88,0x2E,0x7E), rgb_t(0x99,0x3F,0x8F), rgb_t(0xAA,0x50,0xA0), \
	rgb_t(0xBB,0x61,0xB1), rgb_t(0xCC,0x72,0xC2), rgb_t(0xDD,0x83,0xD3), rgb_t(0xEE,0x94,0xE4), \
	rgb_t(0xFF,0xA5,0xE4), rgb_t(0xFF,0xB6,0xE9), rgb_t(0xFF,0xC7,0xEE), rgb_t(0xFF,0xD8,0xF3   )

#define NTSC_PURPLE_BLUE \
	rgb_t(0x1D,0x00,0x5C), rgb_t(0x2E,0x00,0x68), rgb_t(0x40,0x00,0x74), rgb_t(0x51,0x10,0x84), \
	rgb_t(0x62,0x21,0x95), rgb_t(0x73,0x32,0xA6), rgb_t(0x84,0x43,0xB7), rgb_t(0x95,0x54,0xC8), \
	rgb_t(0xA6,0x65,0xD9), rgb_t(0xB7,0x76,0xEA), rgb_t(0xC8,0x87,0xEB), rgb_t(0xD9,0x98,0xEB), \
	rgb_t(0xE9,0xA9,0xEC), rgb_t(0xFB,0xBA,0xEB), rgb_t(0xFF,0xCB,0xEF), rgb_t(0xFF,0xDC,0xF4   )

#define NTSC_BLUE1 \
	rgb_t(0x02,0x00,0x71), rgb_t(0x13,0x00,0x7D), rgb_t(0x24,0x0B,0x8C), rgb_t(0x35,0x1C,0x9D), \
	rgb_t(0x46,0x2D,0xAE), rgb_t(0x57,0x3E,0xBF), rgb_t(0x68,0x4F,0xD0), rgb_t(0x79,0x60,0xE1), \
	rgb_t(0x8A,0x71,0xF2), rgb_t(0x9B,0x82,0xF7), rgb_t(0xAC,0x93,0xF7), rgb_t(0xBD,0xA4,0xF7), \
	rgb_t(0xCE,0xB5,0xF7), rgb_t(0xDF,0xC6,0xF7), rgb_t(0xF0,0xD7,0xF7), rgb_t(0xFF,0xE8,0xF8   )

#define NTSC_BLUE2 \
	rgb_t(0x00,0x00,0x68), rgb_t(0x00,0x0A,0x7C), rgb_t(0x08,0x1B,0x90), rgb_t(0x19,0x2C,0xA1), \
	rgb_t(0x2A,0x3D,0xB2), rgb_t(0x3B,0x4E,0xC3), rgb_t(0x4C,0x5F,0xD4), rgb_t(0x5D,0x70,0xE5), \
	rgb_t(0x6E,0x81,0xF6), rgb_t(0x7F,0x92,0xFF), rgb_t(0x90,0xA3,0xFF), rgb_t(0xA1,0xB4,0xFF), \
	rgb_t(0xB2,0xC5,0xFF), rgb_t(0xC3,0xD6,0xFF), rgb_t(0xD4,0xE7,0xFF), rgb_t(0xE5,0xF8,0xFF   )

#define NTSC_LIGHT_BLUE \
	rgb_t(0x00,0x0A,0x4D), rgb_t(0x00,0x1B,0x63), rgb_t(0x00,0x2C,0x79), rgb_t(0x02,0x3D,0x8F), \
	rgb_t(0x13,0x4E,0xA0), rgb_t(0x24,0x5F,0xB1), rgb_t(0x35,0x70,0xC2), rgb_t(0x46,0x81,0xD3), \
	rgb_t(0x57,0x92,0xE4), rgb_t(0x68,0xA3,0xF5), rgb_t(0x79,0xB4,0xFF), rgb_t(0x8A,0xC5,0xFF), \
	rgb_t(0x9B,0xD6,0xFF), rgb_t(0xAC,0xE7,0xFF), rgb_t(0xBD,0xF8,0xFF), rgb_t(0xCE,0xFF,0xFF   )

#define NTSC_TURQUOISE \
	rgb_t(0x00,0x1A,0x26), rgb_t(0x00,0x2B,0x3C), rgb_t(0x00,0x3C,0x52), rgb_t(0x00,0x4D,0x68), \
	rgb_t(0x06,0x5E,0x7C), rgb_t(0x17,0x6F,0x8D), rgb_t(0x28,0x80,0x9E), rgb_t(0x39,0x91,0xAF), \
	rgb_t(0x4A,0xA2,0xC0), rgb_t(0x5B,0xB3,0xD1), rgb_t(0x6C,0xC4,0xE2), rgb_t(0x7D,0xD5,0xF3), \
	rgb_t(0x8E,0xE6,0xFF), rgb_t(0x9F,0xF7,0xFF), rgb_t(0xB0,0xFF,0xFF), rgb_t(0xC1,0xFF,0xFF   )

#define NTSC_GREEN_BLUE \
	rgb_t(0x00,0x24,0x0B), rgb_t(0x00,0x35,0x10), rgb_t(0x00,0x46,0x22), rgb_t(0x00,0x57,0x38), \
	rgb_t(0x05,0x68,0x4D), rgb_t(0x16,0x79,0x5E), rgb_t(0x27,0x8A,0x6F), rgb_t(0x38,0x9B,0x80), \
	rgb_t(0x49,0xAC,0x91), rgb_t(0x5A,0xBD,0xA2), rgb_t(0x6B,0xCE,0xB3), rgb_t(0x7C,0xDF,0xC4), \
	rgb_t(0x8D,0xF0,0xD5), rgb_t(0x9E,0xFF,0xE5), rgb_t(0xAF,0xFF,0xF1), rgb_t(0xC0,0xFF,0xFD   )

#define NTSC_GREEN \
	rgb_t(0x00,0x27,0x0C), rgb_t(0x00,0x38,0x11), rgb_t(0x00,0x49,0x16), rgb_t(0x00,0x5A,0x1B), \
	rgb_t(0x10,0x6B,0x1B), rgb_t(0x21,0x7C,0x2C), rgb_t(0x32,0x8D,0x3D), rgb_t(0x43,0x9E,0x4E), \
	rgb_t(0x54,0xAF,0x5F), rgb_t(0x65,0xC0,0x70), rgb_t(0x76,0xD1,0x81), rgb_t(0x87,0xE2,0x92), \
	rgb_t(0x98,0xF3,0xA3), rgb_t(0xA9,0xFF,0xB3), rgb_t(0xBA,0xFF,0xBF), rgb_t(0xCB,0xFF,0xCB   )

#define NTSC_YELLOW_GREEN \
	rgb_t(0x00,0x23,0x0A), rgb_t(0x00,0x34,0x10), rgb_t(0x04,0x45,0x13), rgb_t(0x15,0x56,0x13), \
	rgb_t(0x26,0x67,0x13), rgb_t(0x37,0x78,0x13), rgb_t(0x48,0x89,0x14), rgb_t(0x59,0x9A,0x25), \
	rgb_t(0x6A,0xAB,0x36), rgb_t(0x7B,0xBC,0x47), rgb_t(0x8C,0xCD,0x58), rgb_t(0x9D,0xDE,0x69), \
	rgb_t(0xAE,0xEF,0x7A), rgb_t(0xBF,0xFF,0x8B), rgb_t(0xD0,0xFF,0x97), rgb_t(0xE1,0xFF,0xA3   )

#define NTSC_ORANGE_GREEN \
	rgb_t(0x00,0x17,0x07), rgb_t(0x0E,0x28,0x08), rgb_t(0x1F,0x39,0x08), rgb_t(0x30,0x4A,0x08), \
	rgb_t(0x41,0x5B,0x08), rgb_t(0x52,0x6C,0x08), rgb_t(0x63,0x7D,0x08), rgb_t(0x74,0x8E,0x0D), \
	rgb_t(0x85,0x9F,0x1E), rgb_t(0x96,0xB0,0x2F), rgb_t(0xA7,0xC1,0x40), rgb_t(0xB8,0xD2,0x51), \
	rgb_t(0xC9,0xE3,0x62), rgb_t(0xDA,0xF4,0x73), rgb_t(0xEB,0xFF,0x82), rgb_t(0xFC,0xFF,0x8E   )

#define NTSC_LIGHT_ORANGE \
	rgb_t(0x19,0x07,0x00), rgb_t(0x2A,0x18,0x00), rgb_t(0x3B,0x29,0x00), rgb_t(0x4C,0x3A,0x00), \
	rgb_t(0x5D,0x4B,0x00), rgb_t(0x6E,0x5C,0x00), rgb_t(0x7F,0x6D,0x00), rgb_t(0x90,0x7E,0x09), \
	rgb_t(0xA1,0x8F,0x1A), rgb_t(0xB2,0xA0,0x2B), rgb_t(0xC3,0xB1,0x3C), rgb_t(0xD4,0xC2,0x4D), \
	rgb_t(0xE5,0xD3,0x5E), rgb_t(0xF6,0xE4,0x6F), rgb_t(0xFF,0xF5,0x82), rgb_t(0xFF,0xFF,0x96   )

static constexpr rgb_t a7800_colors[256] =
{
	NTSC_GREY,
	NTSC_GOLD,
	NTSC_ORANGE,
	NTSC_RED_ORANGE,
	NTSC_PINK,
	NTSC_PURPLE,
	NTSC_PURPLE_BLUE,
	NTSC_BLUE1,
	NTSC_BLUE2,
	NTSC_LIGHT_BLUE,
	NTSC_TURQUOISE,
	NTSC_GREEN_BLUE,
	NTSC_GREEN,
	NTSC_YELLOW_GREEN,
	NTSC_ORANGE_GREEN,
	NTSC_LIGHT_ORANGE
};

static constexpr rgb_t a7800p_colors[256] =
{
	NTSC_GREY,
	NTSC_ORANGE_GREEN,
	NTSC_GOLD,
	NTSC_ORANGE,
	NTSC_RED_ORANGE,
	NTSC_PINK,
	NTSC_PURPLE,
	NTSC_PURPLE_BLUE,
	NTSC_BLUE1,
	NTSC_BLUE2,
	NTSC_LIGHT_BLUE,
	NTSC_TURQUOISE,
	NTSC_GREEN_BLUE,
	NTSC_GREEN,
	NTSC_YELLOW_GREEN,
	NTSC_ORANGE_GREEN
};

/***************************************************************************
    PALETTE - 24.7 PHASE SHIFT


define NTSC_GREY
    rgb_t(0x00,0x00,0x00), rgb_t(0x11,0x11,0x11), rgb_t(0x22,0x22,0x22), rgb_t(0x33,0x33,0x33), \
    rgb_t(0x44,0x44,0x44), rgb_t(0x55,0x55,0x55), rgb_t(0x66,0x66,0x66), rgb_t(0x77,0x77,0x77), \
    rgb_t(0x88,0x88,0x88), rgb_t(0x99,0x99,0x99), rgb_t(0xAA,0xAA,0xAA), rgb_t(0xBB,0xBB,0xBB), \
    rgb_t(0xCC,0xCC,0xCC), rgb_t(0xDD,0xDD,0xDD), rgb_t(0xEE,0xEE,0xEE), rgb_t(0xFF,0xFF,0xFF   )

define NTSC_GOLD
    rgb_t(0x1A,0x07,0x00), rgb_t(0x2B,0x18,0x00), rgb_t(0x3C,0x29,0x00), rgb_t(0x4D,0x3A,0x00), \
    rgb_t(0x5E,0x4B,0x00), rgb_t(0x6F,0x5C,0x00), rgb_t(0x80,0x6D,0x00), rgb_t(0x91,0x7E,0x09), \
    rgb_t(0xA2,0x8F,0x1A), rgb_t(0xB3,0xA0,0x2B), rgb_t(0xC4,0xB1,0x3C), rgb_t(0xD5,0xC2,0x4D), \
    rgb_t(0xE6,0xD3,0x5E), rgb_t(0xF7,0xE4,0x6F), rgb_t(0xFF,0xF5,0x83), rgb_t(0xFF,0xF7,0x97   )

define NTSC_ORANGE
    rgb_t(0x30,0x00,0x00), rgb_t(0x41,0x07,0x00), rgb_t(0x52,0x18,0x00), rgb_t(0x63,0x29,0x00), \
    rgb_t(0x74,0x3A,0x00), rgb_t(0x85,0x4B,0x00), rgb_t(0x96,0x5C,0x0A), rgb_t(0xA7,0x6D,0x1B), \
    rgb_t(0xB8,0x7E,0x2C), rgb_t(0xC9,0x8F,0x3D), rgb_t(0xDA,0xA0,0x4E), rgb_t(0xEB,0xB1,0x5F), \
    rgb_t(0xFC,0xC2,0x70), rgb_t(0xFF,0xD3,0x85), rgb_t(0xFF,0xE4,0x9B), rgb_t(0xFF,0xF5,0xB1   )

define NTSC_RED_ORANGE
    rgb_t(0x3D,0x00,0x00), rgb_t(0x4E,0x00,0x00), rgb_t(0x5F,0x09,0x00), rgb_t(0x70,0x1A,0x00), \
    rgb_t(0x81,0x2B,0x09), rgb_t(0x92,0x3C,0x1A), rgb_t(0xA3,0x4D,0x2B), rgb_t(0xB4,0x5E,0x3C), \
    rgb_t(0xC5,0x6F,0x4D), rgb_t(0xD6,0x80,0x5E), rgb_t(0xE7,0x91,0x6F), rgb_t(0xF8,0xA2,0x80), \
    rgb_t(0xFF,0xB3,0x94), rgb_t(0xFF,0xC4,0xAA), rgb_t(0xFF,0xD5,0xC0), rgb_t(0xFF,0xE6,0xD6   )

define NTSC_PINK
    rgb_t(0x3F,0x00,0x00), rgb_t(0x50,0x00,0x09), rgb_t(0x61,0x00,0x15), rgb_t(0x72,0x10,0x26), \
    rgb_t(0x83,0x21,0x37), rgb_t(0x94,0x32,0x48), rgb_t(0xA5,0x43,0x59), rgb_t(0xB6,0x54,0x6A), \
    rgb_t(0xC7,0x65,0x7B), rgb_t(0xD8,0x76,0x8C), rgb_t(0xE9,0x87,0x9D), rgb_t(0xFA,0x98,0xAE), \
    rgb_t(0xFF,0xA9,0xC2), rgb_t(0xFF,0xBA,0xD8), rgb_t(0xFF,0xCB,0xEE), rgb_t(0xFF,0xDC,0xF4   )

define NTSC_PURPLE
    rgb_t(0x36,0x00,0x2E), rgb_t(0x47,0x00,0x3A), rgb_t(0x58,0x00,0x46), rgb_t(0x69,0x0C,0x55), \
    rgb_t(0x7A,0x1D,0x66), rgb_t(0x8B,0x2E,0x77), rgb_t(0x9C,0x3F,0x88), rgb_t(0xAD,0x50,0x99), \
    rgb_t(0xBE,0x61,0xAA), rgb_t(0xCF,0x72,0xBB), rgb_t(0xE0,0x83,0xCC), rgb_t(0xF1,0x94,0xDD), \
    rgb_t(0xFF,0xA5,0xE4), rgb_t(0xFF,0xB6,0xE9), rgb_t(0xFF,0xC7,0xEE), rgb_t(0xFF,0xD8,0xF3   )

define NTSC_PURPLE_BLUE
    rgb_t(0x23,0x00,0x55), rgb_t(0x34,0x00,0x61), rgb_t(0x45,0x00,0x6D), rgb_t(0x56,0x0F,0x7E), \
    rgb_t(0x67,0x20,0x8F), rgb_t(0x78,0x31,0xA0), rgb_t(0x89,0x42,0xB1), rgb_t(0x9A,0x53,0xC2), \
    rgb_t(0xAB,0x64,0xD3), rgb_t(0xBC,0x75,0xE4), rgb_t(0xCD,0x86,0xEA), rgb_t(0xDE,0x97,0xEA), \
    rgb_t(0xEF,0xA8,0xEA), rgb_t(0xFF,0xB9,0xEA), rgb_t(0xFF,0xCA,0xEF), rgb_t(0xFF,0xDB,0xF4   )

define NTSC_BLUE1
    rgb_t(0x09,0x00,0x6E), rgb_t(0x1A,0x00,0x7A), rgb_t(0x2B,0x08,0x88), rgb_t(0x3C,0x19,0x99), \
    rgb_t(0x4D,0x2A,0xAA), rgb_t(0x5E,0x3B,0xBB), rgb_t(0x6F,0x4C,0xCC), rgb_t(0x80,0x5D,0xDD), \
    rgb_t(0x91,0x6E,0xEE), rgb_t(0xA2,0x7F,0xF4), rgb_t(0xB3,0x90,0xF4), rgb_t(0xC4,0xA1,0xF4), \
    rgb_t(0xD5,0xB2,0xF4), rgb_t(0xE6,0xC3,0xF4), rgb_t(0xF7,0xD4,0xF4), rgb_t(0xFF,0xE5,0xF7   )

define NTSC_BLUE2
    rgb_t(0x00,0x00,0x6D), rgb_t(0x00,0x05,0x80), rgb_t(0x10,0x16,0x91), rgb_t(0x21,0x27,0xA2), \
    rgb_t(0x32,0x38,0xB3), rgb_t(0x43,0x49,0xC4), rgb_t(0x54,0x5A,0xD5), rgb_t(0x65,0x6B,0xE6), \
    rgb_t(0x76,0x7C,0xF7), rgb_t(0x87,0x8D,0xFF), rgb_t(0x98,0x9E,0xFF), rgb_t(0xA9,0xAF,0xFF), \
    rgb_t(0xBA,0xC0,0xFF), rgb_t(0xCB,0xD1,0xFF), rgb_t(0xDC,0xE2,0xFF), rgb_t(0xED,0xF3,0xFF   )

define NTSC_LIGHT_BLUE
    rgb_t(0x00,0x05,0x57), rgb_t(0x00,0x16,0x6E), rgb_t(0x00,0x27,0x84), rgb_t(0x09,0x38,0x97), \
    rgb_t(0x1A,0x49,0xA8), rgb_t(0x2B,0x5A,0xB9), rgb_t(0x3C,0x6B,0xCA), rgb_t(0x4D,0x7C,0xDB), \
    rgb_t(0x5E,0x8D,0xEC), rgb_t(0x6F,0x9E,0xFD), rgb_t(0x80,0xAF,0xFF), rgb_t(0x91,0xC0,0xFF), \
    rgb_t(0xA2,0xD1,0xFF), rgb_t(0xB3,0xE2,0xFF), rgb_t(0xC4,0xF3,0xFF), rgb_t(0xD5,0xFF,0xFF   )

define NTSC_TURQUOISE
    rgb_t(0x00,0x15,0x34), rgb_t(0x00,0x26,0x4A), rgb_t(0x00,0x37,0x60), rgb_t(0x00,0x48,0x77), \
    rgb_t(0x0A,0x59,0x8A), rgb_t(0x1B,0x6A,0x9B), rgb_t(0x2C,0x7B,0xAC), rgb_t(0x3D,0x8C,0xBD), \
    rgb_t(0x4E,0x9D,0xCE), rgb_t(0x5F,0xAE,0xDF), rgb_t(0x70,0xBF,0xF0), rgb_t(0x81,0xD0,0xFF), \
    rgb_t(0x92,0xE1,0xFF), rgb_t(0xA3,0xF2,0xFF), rgb_t(0xB4,0xFF,0xFF), rgb_t(0xC5,0xFF,0xFF   )

define NTSC_GREEN_BLUE
    rgb_t(0x00,0x21,0x0A), rgb_t(0x00,0x32,0x1F), rgb_t(0x00,0x43,0x35), rgb_t(0x00,0x54,0x4B), \
    rgb_t(0x04,0x65,0x60), rgb_t(0x15,0x76,0x71), rgb_t(0x26,0x87,0x82), rgb_t(0x37,0x98,0x93), \
    rgb_t(0x48,0xA9,0xA4), rgb_t(0x59,0xBA,0xB5), rgb_t(0x6A,0xCB,0xC6), rgb_t(0x7B,0xDC,0xD7), \
    rgb_t(0x8C,0xED,0xE8), rgb_t(0x9D,0xFE,0xF9), rgb_t(0xAE,0xFF,0xFF), rgb_t(0xBF,0xFF,0xFF   )

define NTSC_GREEN
    rgb_t(0x00,0x27,0x0C), rgb_t(0x00,0x38,0x11), rgb_t(0x00,0x49,0x16), rgb_t(0x00,0x5A,0x1D), \
    rgb_t(0x0A,0x6B,0x30), rgb_t(0x1B,0x7C,0x41), rgb_t(0x2C,0x8D,0x52), rgb_t(0x3D,0x9E,0x63), \
    rgb_t(0x4E,0xAF,0x74), rgb_t(0x5F,0xC0,0x85), rgb_t(0x70,0xD1,0x96), rgb_t(0x81,0xE2,0xA7), \
    rgb_t(0x92,0xF3,0xB8), rgb_t(0xA3,0xFF,0xC8), rgb_t(0xB4,0xFF,0xD3), rgb_t(0xC5,0xFF,0xDF   )

define NTSC_YELLOW_GREEN
    rgb_t(0x00,0x26,0x0B), rgb_t(0x00,0x37,0x10), rgb_t(0x00,0x48,0x16), rgb_t(0x0A,0x59,0x18), \
    rgb_t(0x1B,0x6A,0x18), rgb_t(0x2C,0x7B,0x18), rgb_t(0x3D,0x8C,0x27), rgb_t(0x4E,0x9D,0x38), \
    rgb_t(0x5F,0xAE,0x49), rgb_t(0x70,0xBF,0x5A), rgb_t(0x81,0xD0,0x6B), rgb_t(0x92,0xE1,0x7C), \
    rgb_t(0xA3,0xF2,0x8D), rgb_t(0xB4,0xFF,0x9C), rgb_t(0xC5,0xFF,0xA8), rgb_t(0xD6,0xFF,0xB4   )

define NTSC_ORANGE_GREEN
    rgb_t(0x00,0x1E,0x09), rgb_t(0x00,0x2F,0x0E), rgb_t(0x11,0x40,0x0E), rgb_t(0x22,0x51,0x0E), \
    rgb_t(0x33,0x62,0x0E), rgb_t(0x44,0x73,0x0E), rgb_t(0x55,0x84,0x0E), rgb_t(0x66,0x95,0x17), \
    rgb_t(0x77,0xA6,0x28), rgb_t(0x88,0xB7,0x39), rgb_t(0x99,0xC8,0x4A), rgb_t(0xAA,0xD9,0x5B), \
    rgb_t(0xBB,0xEA,0x6C), rgb_t(0xCC,0xFB,0x7D), rgb_t(0xDD,0xFF,0x8A), rgb_t(0xEE,0xFF,0x96   )

define NTSC_LIGHT_ORANGE
    rgb_t(0x0A,0x11,0x02), rgb_t(0x1B,0x22,0x02), rgb_t(0x2C,0x33,0x02), rgb_t(0x3D,0x44,0x02), \
    rgb_t(0x4E,0x55,0x02), rgb_t(0x5F,0x66,0x02), rgb_t(0x70,0x77,0x02), rgb_t(0x81,0x88,0x09), \
    rgb_t(0x92,0x99,0x1A), rgb_t(0xA3,0xAA,0x2B), rgb_t(0xB4,0xBB,0x3C), rgb_t(0xC5,0xCC,0x4D), \
    rgb_t(0xD6,0xDD,0x5E), rgb_t(0xE7,0xEE,0x6F), rgb_t(0xF8,0xFF,0x80), rgb_t(0xFF,0xFF,0x8F   )
***************************************************************************/


/***************************************************************************
    PALETTE - 25.2 PHASE SHIFT


define NTSC_GREY
    rgb_t(0x00,0x00,0x00), rgb_t(0x11,0x11,0x11), rgb_t(0x22,0x22,0x22), rgb_t(0x33,0x33,0x33), \
    rgb_t(0x44,0x44,0x44), rgb_t(0x55,0x55,0x55), rgb_t(0x66,0x66,0x66), rgb_t(0x77,0x77,0x77), \
    rgb_t(0x88,0x88,0x88), rgb_t(0x99,0x99,0x99), rgb_t(0xAA,0xAA,0xAA), rgb_t(0xBB,0xBB,0xBB), \
    rgb_t(0xCC,0xCC,0xCC), rgb_t(0xDD,0xDD,0xDD), rgb_t(0xEE,0xEE,0xEE), rgb_t(0xFF,0xFF,0xFF   )

define NTSC_GOLD
    rgb_t(0x1A,0x07,0x00), rgb_t(0x2B,0x18,0x00), rgb_t(0x3C,0x29,0x00), rgb_t(0x4D,0x3A,0x00), \
    rgb_t(0x5E,0x4B,0x00), rgb_t(0x6F,0x5C,0x00), rgb_t(0x80,0x6D,0x00), rgb_t(0x91,0x7E,0x09), \
    rgb_t(0xA2,0x8F,0x1A), rgb_t(0xB3,0xA0,0x2B), rgb_t(0xC4,0xB1,0x3C), rgb_t(0xD5,0xC2,0x4D), \
    rgb_t(0xE6,0xD3,0x5E), rgb_t(0xF7,0xE4,0x6F), rgb_t(0xFF,0xF5,0x83), rgb_t(0xFF,0xF7,0x97   )

define NTSC_ORANGE
    rgb_t(0x30,0x00,0x00), rgb_t(0x41,0x07,0x00), rgb_t(0x52,0x18,0x00), rgb_t(0x63,0x29,0x00), \
    rgb_t(0x74,0x3A,0x00), rgb_t(0x85,0x4B,0x00), rgb_t(0x96,0x5C,0x0A), rgb_t(0xA7,0x6D,0x1B), \
    rgb_t(0xB8,0x7E,0x2C), rgb_t(0xC9,0x8F,0x3D), rgb_t(0xDA,0xA0,0x4E), rgb_t(0xEB,0xB1,0x5F), \
    rgb_t(0xFC,0xC2,0x70), rgb_t(0xFF,0xD3,0x85), rgb_t(0xFF,0xE4,0x9B), rgb_t(0xFF,0xF5,0xB1   )

define NTSC_RED_ORANGE
    rgb_t(0x3E,0x00,0x00), rgb_t(0x4F,0x00,0x00), rgb_t(0x60,0x09,0x00), rgb_t(0x71,0x1A,0x00), \
    rgb_t(0x82,0x2B,0x0B), rgb_t(0x93,0x3C,0x1C), rgb_t(0xA4,0x4D,0x2D), rgb_t(0xB5,0x5E,0x3E), \
    rgb_t(0xC6,0x6F,0x4F), rgb_t(0xD7,0x80,0x60), rgb_t(0xE8,0x91,0x71), rgb_t(0xF9,0xA2,0x82), \
    rgb_t(0xFF,0xB3,0x96), rgb_t(0xFF,0xC4,0xAC), rgb_t(0xFF,0xD5,0xC2), rgb_t(0xFF,0xE6,0xD8   )

define NTSC_PINK
    rgb_t(0x3F,0x00,0x00), rgb_t(0x50,0x00,0x0C), rgb_t(0x61,0x00,0x18), rgb_t(0x72,0x0F,0x28), \
    rgb_t(0x83,0x20,0x39), rgb_t(0x94,0x31,0x4A), rgb_t(0xA5,0x42,0x5B), rgb_t(0xB6,0x53,0x6C), \
    rgb_t(0xC7,0x64,0x7D), rgb_t(0xD8,0x75,0x8E), rgb_t(0xE9,0x86,0x9F), rgb_t(0xFA,0x97,0xB0), \
    rgb_t(0xFF,0xA8,0xC5), rgb_t(0xFF,0xB9,0xDB), rgb_t(0xFF,0xCA,0xEF), rgb_t(0xFF,0xDB,0xF4   )

define NTSC_PURPLE
    rgb_t(0x35,0x00,0x31), rgb_t(0x46,0x00,0x3D), rgb_t(0x57,0x00,0x49), rgb_t(0x68,0x0C,0x58), \
    rgb_t(0x79,0x1D,0x69), rgb_t(0x8A,0x2E,0x7A), rgb_t(0x9B,0x3F,0x8B), rgb_t(0xAC,0x50,0x9C), \
    rgb_t(0xBD,0x61,0xAD), rgb_t(0xCE,0x72,0xBE), rgb_t(0xDF,0x83,0xCF), rgb_t(0xF0,0x94,0xE0), \
    rgb_t(0xFF,0xA5,0xE4), rgb_t(0xFF,0xB6,0xE9), rgb_t(0xFF,0xC7,0xEE), rgb_t(0xFF,0xD8,0xF3   )

define NTSC_PURPLE_BLUE
    rgb_t(0x20,0x00,0x59), rgb_t(0x31,0x00,0x65), rgb_t(0x42,0x00,0x71), rgb_t(0x53,0x10,0x82), \
    rgb_t(0x64,0x21,0x93), rgb_t(0x75,0x32,0xA4), rgb_t(0x86,0x43,0xB5), rgb_t(0x97,0x54,0xC6), \
    rgb_t(0xA8,0x65,0xD7), rgb_t(0xB9,0x76,0xE8), rgb_t(0xCA,0x87,0xEB), rgb_t(0xDB,0x98,0xEB), \
    rgb_t(0xEC,0xA9,0xEB), rgb_t(0xFD,0xBA,0xEB), rgb_t(0xFF,0xCB,0xEF), rgb_t(0xFF,0xDC,0xF4   )

define NTSC_BLUE1
    rgb_t(0x05,0x00,0x70), rgb_t(0x16,0x00,0x7C), rgb_t(0x27,0x09,0x8B), rgb_t(0x38,0x1A,0x9C), \
    rgb_t(0x49,0x2B,0xAD), rgb_t(0x5A,0x3C,0xBE), rgb_t(0x6B,0x4D,0xCF), rgb_t(0x7C,0x5E,0xE0), \
    rgb_t(0X8D,0x6F,0xF1), rgb_t(0x9E,0x80,0xF6), rgb_t(0xAF,0x91,0xF6), rgb_t(0xC0,0xA2,0xF6), \
    rgb_t(0xD1,0xB3,0xF6), rgb_t(0xE2,0xC4,0xF6), rgb_t(0xF3,0xD5,0xF6), rgb_t(0xFF,0xE6,0xF7   )

define NTSC_BLUE2
    rgb_t(0x00,0x00,0x6B), rgb_t(0x00,0x08,0x7E), rgb_t(0x0C,0x19,0x91), rgb_t(0x1D,0x2A,0xA2), \
    rgb_t(0x2E,0x3B,0xB3), rgb_t(0x3F,0x4C,0xC4), rgb_t(0x50,0x5D,0xD5), rgb_t(0x61,0x6E,0xE6), \
    rgb_t(0x72,0x7F,0xF7), rgb_t(0x83,0x90,0xFF), rgb_t(0x94,0xA1,0xFF), rgb_t(0xA5,0xB2,0xFF), \
    rgb_t(0xB6,0xC3,0xFF), rgb_t(0xC7,0xD4,0xFF), rgb_t(0xD8,0xE5,0xFF), rgb_t(0xE9,0xF6,0xFF   )

define NTSC_LIGHT_BLUE
    rgb_t(0x00,0x08,0x52), rgb_t(0x00,0x19,0x68), rgb_t(0x00,0x2A,0x7F), rgb_t(0x05,0x3B,0x93), \
    rgb_t(0x16,0x4C,0xA4), rgb_t(0x27,0x5D,0xB5), rgb_t(0x38,0x6E,0xC6), rgb_t(0x49,0x7F,0xD7), \
    rgb_t(0x5A,0x90,0xE8), rgb_t(0x6B,0xA1,0xF9), rgb_t(0x7C,0xB2,0xFF), rgb_t(0x8D,0xC3,0xFF), \
    rgb_t(0x9E,0xD4,0xFF), rgb_t(0xAF,0xE5,0xFF), rgb_t(0xC0,0xF6,0xFF), rgb_t(0xD1,0xFF,0xFF   )

define NTSC_TURQUOISE
    rgb_t(0x00,0x17,0x2D), rgb_t(0x00,0x28,0x43), rgb_t(0x00,0x39,0x59), rgb_t(0x00,0x4A,0x6F), \
    rgb_t(0x08,0x5B,0x83), rgb_t(0x19,0x6C,0x94), rgb_t(0x2A,0x7D,0xA5), rgb_t(0x3B,0x8E,0xB6), \
    rgb_t(0x4C,0x9F,0xC7), rgb_t(0x5D,0xB0,0xD8), rgb_t(0x6E,0xC1,0xE9), rgb_t(0x7F,0xD2,0xFA), \
    rgb_t(0x90,0xE3,0xFF), rgb_t(0xA1,0xF4,0xFF), rgb_t(0xB2,0xFF,0xFF), rgb_t(0xC3,0xFF,0xFF   )

define NTSC_GREEN_BLUE
    rgb_t(0x00,0x23,0x0A), rgb_t(0x00,0x34,0x15), rgb_t(0x00,0x45,0x2B), rgb_t(0x00,0x56,0x41), \
    rgb_t(0x04,0x67,0x56), rgb_t(0x15,0x78,0x67), rgb_t(0x26,0x89,0x78), rgb_t(0x37,0x9A,0x89), \
    rgb_t(0x48,0xAB,0x9A), rgb_t(0x59,0xBC,0xAB), rgb_t(0x6A,0xCD,0xBC), rgb_t(0x7B,0xDE,0xCD), \
    rgb_t(0x8C,0xEF,0xDE), rgb_t(0x9D,0xFF,0xEE), rgb_t(0xAE,0xFF,0xFA), rgb_t(0xBF,0xFF,0xFF   )

define NTSC_GREEN
    rgb_t(0x00,0x27,0x0C), rgb_t(0x00,0x38,0x11), rgb_t(0x00,0x49,0x16), rgb_t(0x00,0x5A,0x1B), \
    rgb_t(0x0D,0x6B,0x25), rgb_t(0x1E,0x7C,0x36), rgb_t(0x2F,0x8D,0x47), rgb_t(0x40,0x9E,0x58), \
    rgb_t(0x51,0xAF,0x69), rgb_t(0x62,0xC0,0x7A), rgb_t(0x73,0xD1,0x8B), rgb_t(0x84,0xE2,0x9C), \
    rgb_t(0x95,0xF3,0xAD), rgb_t(0xA6,0xFF,0xBD), rgb_t(0xB7,0xFF,0xC9), rgb_t(0xC8,0xFF,0xD4   )

define NTSC_YELLOW_GREEN
    rgb_t(0x00,0x24,0x0B), rgb_t(0x00,0x35,0x10), rgb_t(0x00,0x46,0x15), rgb_t(0x10,0x57,0x15), \
    rgb_t(0x21,0x68,0x15), rgb_t(0x32,0x79,0x15), rgb_t(0x43,0x8A,0x1C), rgb_t(0x54,0x9B,0x2D), \
    rgb_t(0x65,0xAC,0x3E), rgb_t(0x76,0xBD,0x4F), rgb_t(0x87,0xCE,0x60), rgb_t(0x98,0xDF,0x71), \
    rgb_t(0xA9,0xF0,0x82), rgb_t(0xBA,0xFF,0x93), rgb_t(0xCB,0xFF,0x9F), rgb_t(0xDC,0xFF,0xAA   )

define NTSC_ORANGE_GREEN
    rgb_t(0x00,0x1B,0x08), rgb_t(0x08,0x2C,0x0B), rgb_t(0x19,0x3D,0x0B), rgb_t(0x2A,0x4E,0x0B), \
    rgb_t(0x3B,0x5F,0x0B), rgb_t(0x4C,0x70,0x0B), rgb_t(0x5D,0x81,0x0B), rgb_t(0x6E,0x92,0x11), \
    rgb_t(0x7F,0xA3,0x22), rgb_t(0x90,0xB4,0x33), rgb_t(0xA1,0xC5,0x44), rgb_t(0xB2,0xD6,0x55), \
    rgb_t(0xC3,0xE7,0x66), rgb_t(0xD4,0xF8,0x77), rgb_t(0xE5,0xFF,0x85), rgb_t(0xF6,0xFF,0x91   )

define NTSC_LIGHT_ORANGE
    rgb_t(0x12,0x0C,0x00), rgb_t(0x23,0x1D,0x00), rgb_t(0x34,0x2E,0x00), rgb_t(0x45,0x3F,0x00), \
    rgb_t(0x56,0x50,0x00), rgb_t(0x67,0x61,0x00), rgb_t(0x78,0x72,0x00), rgb_t(0x89,0x83,0x08), \
    rgb_t(0x9A,0x94,0x19), rgb_t(0xAB,0xA5,0x2A), rgb_t(0xBC,0xB6,0x3B), rgb_t(0xCD,0xC7,0x4C), \
    rgb_t(0xDE,0xD8,0x5D), rgb_t(0xEF,0xE9,0x6E), rgb_t(0xFF,0xFA,0x80), rgb_t(0xFF,0xFF,0x92   )
***************************************************************************/


/***************************************************************************
    PALETTE - 25.7 PHASE SHIFT


define NTSC_GREY
    rgb_t(0x00,0x00,0x00), rgb_t(0x11,0x11,0x11), rgb_t(0x22,0x22,0x22), rgb_t(0x33,0x33,0x33), \
    rgb_t(0x44,0x44,0x44), rgb_t(0x55,0x55,0x55), rgb_t(0x66,0x66,0x66), rgb_t(0x77,0x77,0x77), \
    rgb_t(0x88,0x88,0x88), rgb_t(0x99,0x99,0x99), rgb_t(0xAA,0xAA,0xAA), rgb_t(0xBB,0xBB,0xBB), \
    rgb_t(0xCC,0xCC,0xCC), rgb_t(0xDD,0xDD,0xDD), rgb_t(0xEE,0xEE,0xEE), rgb_t(0xFF,0xFF,0xFF   )

define NTSC_GOLD
    rgb_t(0x1A,0x07,0x00), rgb_t(0x2B,0x18,0x00), rgb_t(0x3C,0x29,0x00), rgb_t(0x4D,0x3A,0x00), \
    rgb_t(0x5E,0x4B,0x00), rgb_t(0x6F,0x5C,0x00), rgb_t(0x80,0x6D,0x00), rgb_t(0x91,0x7E,0x09), \
    rgb_t(0xA2,0x8F,0x1A), rgb_t(0xB3,0xA0,0x2B), rgb_t(0xC4,0xB1,0x3C), rgb_t(0xD5,0xC2,0x4D), \
    rgb_t(0xE6,0xD3,0x5E), rgb_t(0xF7,0xE4,0x6F), rgb_t(0xFF,0xF5,0x83), rgb_t(0xFF,0xF7,0x97   )

define NTSC_ORANGE
    rgb_t(0x31,0x00,0x00), rgb_t(0x42,0x06,0x00), rgb_t(0x53,0x17,0x00), rgb_t(0x64,0x28,0x00), \
    rgb_t(0x75,0x39,0x00), rgb_t(0x86,0X4A,0x00), rgb_t(0x97,0x5B,0x0A), rgb_t(0xA8,0x6C,0x1B), \
    rgb_t(0xB9,0x7D,0x2C), rgb_t(0xCA,0x8E,0x3D), rgb_t(0xDB,0x9F,0x4E), rgb_t(0xEC,0xB0,0x5F), \
    rgb_t(0xFD,0xC1,0x70), rgb_t(0xFF,0xD2,0x85), rgb_t(0xFF,0xE3,0x9C), rgb_t(0xFF,0xF4,0xB2   )

define NTSC_RED_ORANGE
    rgb_t(0x3E,0x00,0x00), rgb_t(0x4F,0x00,0x00), rgb_t(0x60,0x08,0x00), rgb_t(0x71,0x19,0x00), \
    rgb_t(0x82,0x2A,0x0D), rgb_t(0x93,0x3B,0x1E), rgb_t(0xA4,0x4C,0x2F), rgb_t(0xB5,0x5D,0x40), \
    rgb_t(0xC6,0x6E,0x51), rgb_t(0xD7,0x7F,0x62), rgb_t(0xE8,0x90,0x73), rgb_t(0xF9,0xA1,0x83), \
    rgb_t(0xFF,0xB2,0x98), rgb_t(0xFF,0xC3,0xAE), rgb_t(0xFF,0xD4,0xC4), rgb_t(0xFF,0xE5,0xDA   )

define NTSC_PINK
    rgb_t(0x3F,0x00,0x03), rgb_t(0x50,0x00,0x0F), rgb_t(0x61,0x00,0x1B), rgb_t(0x72,0x0F,0x2B), \
    rgb_t(0x83,0x20,0x3C), rgb_t(0x94,0x31,0x4D), rgb_t(0xA5,0x42,0x5E), rgb_t(0xB6,0x53,0x6F), \
    rgb_t(0xC7,0x64,0x80), rgb_t(0xD8,0x75,0x91), rgb_t(0xE9,0x86,0xA2), rgb_t(0xFA,0x97,0xB3), \
    rgb_t(0xFF,0xA8,0xC8), rgb_t(0xFF,0xB9,0xDE), rgb_t(0xFF,0xCA,0xEF), rgb_t(0xFF,0xDB,0xF4   )

define NTSC_PURPLE
    rgb_t(0x33,0x00,0x35), rgb_t(0x44,0x00,0x41), rgb_t(0x55,0x00,0x4C), rgb_t(0x66,0x0C,0x5C), \
    rgb_t(0x77,0x1D,0x6D), rgb_t(0x88,0x2E,0x7E), rgb_t(0x99,0x3F,0x8F), rgb_t(0xAA,0x50,0xA0), \
    rgb_t(0xBB,0x61,0xB1), rgb_t(0xCC,0x72,0xC2), rgb_t(0xDD,0x83,0xD3), rgb_t(0xEE,0x94,0xE4), \
    rgb_t(0xFF,0xA5,0xE4), rgb_t(0xFF,0xB6,0xE9), rgb_t(0xFF,0xC7,0xEE), rgb_t(0xFF,0xD8,0xF3   )

define NTSC_PURPLE_BLUE
    rgb_t(0x1D,0x00,0x5C), rgb_t(0x2E,0x00,0x68), rgb_t(0x40,0x00,0x74), rgb_t(0x51,0x10,0x84), \
    rgb_t(0x62,0x21,0x95), rgb_t(0x73,0x32,0xA6), rgb_t(0x84,0x43,0xB7), rgb_t(0x95,0x54,0xC8), \
    rgb_t(0xA6,0x65,0xD9), rgb_t(0xB7,0x76,0xEA), rgb_t(0xC8,0x87,0xEB), rgb_t(0xD9,0x98,0xEB), \
    rgb_t(0xE9,0xA9,0xEC), rgb_t(0xFB,0xBA,0xEB), rgb_t(0xFF,0xCB,0xEF), rgb_t(0xFF,0xDC,0xF4   )

define NTSC_BLUE1
    rgb_t(0x02,0x00,0x71), rgb_t(0x13,0x00,0x7D), rgb_t(0x24,0x0B,0x8C), rgb_t(0x35,0x1C,0x9D), \
    rgb_t(0x46,0x2D,0xAE), rgb_t(0x57,0x3E,0xBF), rgb_t(0x68,0x4F,0xD0), rgb_t(0x79,0x60,0xE1), \
    rgb_t(0x8A,0x71,0xF2), rgb_t(0x9B,0x82,0xF7), rgb_t(0xAC,0x93,0xF7), rgb_t(0xBD,0xA4,0xF7), \
    rgb_t(0xCE,0xB5,0xF7), rgb_t(0xDF,0xC6,0xF7), rgb_t(0xF0,0xD7,0xF7), rgb_t(0xFF,0xE8,0xF8   )

define NTSC_BLUE2
    rgb_t(0x00,0x00,0x68), rgb_t(0x00,0x0A,0x7C), rgb_t(0x08,0x1B,0x90), rgb_t(0x19,0x2C,0xA1), \
    rgb_t(0x2A,0x3D,0xB2), rgb_t(0x3B,0x4E,0xC3), rgb_t(0x4C,0x5F,0xD4), rgb_t(0x5D,0x70,0xE5), \
    rgb_t(0x6E,0x81,0xF6), rgb_t(0x7F,0x92,0xFF), rgb_t(0x90,0xA3,0xFF), rgb_t(0xA1,0xB4,0xFF), \
    rgb_t(0xB2,0xC5,0xFF), rgb_t(0xC3,0xD6,0xFF), rgb_t(0xD4,0xE7,0xFF), rgb_t(0xE5,0xF8,0xFF   )

define NTSC_LIGHT_BLUE
    rgb_t(0x00,0x0A,0x4D), rgb_t(0x00,0x1B,0x63), rgb_t(0x00,0x2C,0x79), rgb_t(0x02,0x3D,0x8F), \
    rgb_t(0x13,0x4E,0xA0), rgb_t(0x24,0x5F,0xB1), rgb_t(0x35,0x70,0xC2), rgb_t(0x46,0x81,0xD3), \
    rgb_t(0x57,0x92,0xE4), rgb_t(0x68,0xA3,0xF5), rgb_t(0x79,0xB4,0xFF), rgb_t(0x8A,0xC5,0xFF), \
    rgb_t(0x9B,0xD6,0xFF), rgb_t(0xAC,0xE7,0xFF), rgb_t(0xBD,0xF8,0xFF), rgb_t(0xCE,0xFF,0xFF   )

define NTSC_TURQUOISE
    rgb_t(0x00,0x1A,0x26), rgb_t(0x00,0x2B,0x3C), rgb_t(0x00,0x3C,0x52), rgb_t(0x00,0x4D,0x68), \
    rgb_t(0x06,0x5E,0x7C), rgb_t(0x17,0x6F,0x8D), rgb_t(0x28,0x80,0x9E), rgb_t(0x39,0x91,0xAF), \
    rgb_t(0x4A,0xA2,0xC0), rgb_t(0x5B,0xB3,0xD1), rgb_t(0x6C,0xC4,0xE2), rgb_t(0x7D,0xD5,0xF3), \
    rgb_t(0x8E,0xE6,0xFF), rgb_t(0x9F,0xF7,0xFF), rgb_t(0xB0,0xFF,0xFF), rgb_t(0xC1,0xFF,0xFF   )

define NTSC_GREEN_BLUE
    rgb_t(0x00,0x24,0x0B), rgb_t(0x00,0x35,0x10), rgb_t(0x00,0x46,0x22), rgb_t(0x00,0x57,0x38), \
    rgb_t(0x05,0x68,0x4D), rgb_t(0x16,0x79,0x5E), rgb_t(0x27,0x8A,0x6F), rgb_t(0x38,0x9B,0x80), \
    rgb_t(0x49,0xAC,0x91), rgb_t(0x5A,0xBD,0xA2), rgb_t(0x6B,0xCE,0xB3), rgb_t(0x7C,0xDF,0xC4), \
    rgb_t(0x8D,0xF0,0xD5), rgb_t(0x9E,0xFF,0xE5), rgb_t(0xAF,0xFF,0xF1), rgb_t(0xC0,0xFF,0xFD   )

define NTSC_GREEN
    rgb_t(0x00,0x27,0x0C), rgb_t(0x00,0x38,0x11), rgb_t(0x00,0x49,0x16), rgb_t(0x00,0x5A,0x1B), \
    rgb_t(0x10,0x6B,0x1B), rgb_t(0x21,0x7C,0x2C), rgb_t(0x32,0x8D,0x3D), rgb_t(0x43,0x9E,0x4E), \
    rgb_t(0x54,0xAF,0x5F), rgb_t(0x65,0xC0,0x70), rgb_t(0x76,0xD1,0x81), rgb_t(0x87,0xE2,0x92), \
    rgb_t(0x98,0xF3,0xA3), rgb_t(0xA9,0xFF,0xB3), rgb_t(0xBA,0xFF,0xBF), rgb_t(0xCB,0xFF,0xCB   )

define NTSC_YELLOW_GREEN
    rgb_t(0x00,0x23,0x0A), rgb_t(0x00,0x34,0x10), rgb_t(0x04,0x45,0x13), rgb_t(0x15,0x56,0x13), \
    rgb_t(0x26,0x67,0x13), rgb_t(0x37,0x78,0x13), rgb_t(0x48,0x89,0x14), rgb_t(0x59,0x9A,0x25), \
    rgb_t(0x6A,0xAB,0x36), rgb_t(0x7B,0xBC,0x47), rgb_t(0x8C,0xCD,0x58), rgb_t(0x9D,0xDE,0x69), \
    rgb_t(0xAE,0xEF,0x7A), rgb_t(0xBF,0xFF,0x8B), rgb_t(0xD0,0xFF,0x97), rgb_t(0xE1,0xFF,0xA3   )

define NTSC_ORANGE_GREEN
    rgb_t(0x00,0x17,0x07), rgb_t(0x0E,0x28,0x08), rgb_t(0x1F,0x39,0x08), rgb_t(0x30,0x4A,0x08), \
    rgb_t(0x41,0x5B,0x08), rgb_t(0x52,0x6C,0x08), rgb_t(0x63,0x7D,0x08), rgb_t(0x74,0x8E,0x0D), \
    rgb_t(0x85,0x9F,0x1E), rgb_t(0x96,0xB0,0x2F), rgb_t(0xA7,0xC1,0x40), rgb_t(0xB8,0xD2,0x51), \
    rgb_t(0xC9,0xE3,0x62), rgb_t(0xDA,0xF4,0x73), rgb_t(0xEB,0xFF,0x82), rgb_t(0xFC,0xFF,0x8E   )

define NTSC_LIGHT_ORANGE
    rgb_t(0x19,0x07,0x00), rgb_t(0x2A,0x18,0x00), rgb_t(0x3B,0x29,0x00), rgb_t(0x4C,0x3A,0x00), \
    rgb_t(0x5D,0x4B,0x00), rgb_t(0x6E,0x5C,0x00), rgb_t(0x7F,0x6D,0x00), rgb_t(0x90,0x7E,0x09), \
    rgb_t(0xA1,0x8F,0x1A), rgb_t(0xB2,0xA0,0x2B), rgb_t(0xC3,0xB1,0x3C), rgb_t(0xD4,0xC2,0x4D), \
    rgb_t(0xE5,0xD3,0x5E), rgb_t(0xF6,0xE4,0x6F), rgb_t(0xFF,0xF5,0x82), rgb_t(0xFF,0xFF,0x96   )
***************************************************************************/


/***************************************************************************
    PALETTE - 26.2 PHASE SHIFT


#define NTSC_GREY \
    rgb_t(0x00,0x00,0x00), rgb_t(0x11,0x11,0x11), rgb_t(0x22,0x22,0x22), rgb_t(0x33,0x33,0x33), \
    rgb_t(0x44,0x44,0x44), rgb_t(0x55,0x55,0x55), rgb_t(0x66,0x66,0x66), rgb_t(0x77,0x77,0x77), \
    rgb_t(0x88,0x88,0x88), rgb_t(0x99,0x99,0x99), rgb_t(0xAA,0xAA,0xAA), rgb_t(0xBB,0xBB,0xBB), \
    rgb_t(0xCC,0xCC,0xCC), rgb_t(0xDD,0xDD,0xDD), rgb_t(0xEE,0xEE,0xEE), rgb_t(0xFF,0xFF,0xFF   )

#define NTSC_GOLD \
    rgb_t(0x1A,0x07,0x00), rgb_t(0x2B,0x18,0x00), rgb_t(0x3C,0x29,0x00), rgb_t(0x4D,0x3A,0x00), \
    rgb_t(0x5E,0x4B,0x00), rgb_t(0x6F,0x5C,0x00), rgb_t(0x80,0x6D,0x00), rgb_t(0x91,0x7E,0x09), \
    rgb_t(0xA2,0x8F,0x1A), rgb_t(0xB3,0xA0,0x2B), rgb_t(0xC4,0xB1,0x3C), rgb_t(0xD5,0xC2,0x4D), \
    rgb_t(0xE6,0xD3,0x5E), rgb_t(0xF7,0xE4,0x6F), rgb_t(0xFF,0xF5,0x83), rgb_t(0xFF,0xF7,0x97   )

#define NTSC_ORANGE \
    rgb_t(0x31,0x00,0x00), rgb_t(0x42,0x06,0x00), rgb_t(0x53,0x17,0x00), rgb_t(0x64,0x28,0x00), \
    rgb_t(0x75,0x39,0x00), rgb_t(0x86,0X4A,0x00), rgb_t(0x97,0x5B,0x0B), rgb_t(0xA8,0x6C,0x1C), \
    rgb_t(0xB9,0x7D,0x2D), rgb_t(0xCA,0x8E,0x3E), rgb_t(0xDB,0x9F,0x4F), rgb_t(0xEC,0xB0,0x60), \
    rgb_t(0xFD,0xC1,0x71), rgb_t(0xFF,0xD2,0x86), rgb_t(0xFF,0xE3,0x9D), rgb_t(0xFF,0xF4,0xB3   )

#define NTSC_RED_ORANGE \
    rgb_t(0x3E,0x00,0x00), rgb_t(0x4F,0x00,0x00), rgb_t(0x60,0x08,0x00), rgb_t(0x71,0x19,0x00), \
    rgb_t(0x82,0x2A,0x0F), rgb_t(0x93,0x3B,0x20), rgb_t(0xA4,0x4C,0x31), rgb_t(0xB5,0x5D,0x42), \
    rgb_t(0xC6,0x6E,0x53), rgb_t(0xD7,0x7F,0x64), rgb_t(0xE8,0x90,0x75), rgb_t(0xF9,0xA1,0x86), \
    rgb_t(0xFF,0xB2,0x9A), rgb_t(0xFF,0xC3,0xB0), rgb_t(0xFF,0xD4,0xC6), rgb_t(0xFF,0xE5,0xDC   )

#define NTSC_PINK \
    rgb_t(0x3E,0x00,0x06), rgb_t(0x4F,0x00,0x12), rgb_t(0x60,0x00,0x1E), rgb_t(0x71,0x0E,0x2E), \
    rgb_t(0x82,0x1F,0x3F), rgb_t(0x93,0x30,0x50), rgb_t(0xA4,0x41,0x61), rgb_t(0xB5,0x52,0x72), \
    rgb_t(0xC6,0x63,0x83), rgb_t(0xD7,0x74,0x94), rgb_t(0xE8,0x85,0xA5), rgb_t(0xF9,0x96,0xB6), \
    rgb_t(0xFF,0xA7,0xCB), rgb_t(0xFF,0xB8,0xE1), rgb_t(0xFF,0xC9,0xEF), rgb_t(0xFF,0xDA,0xF4   )

#define NTSC_PURPLE \
    rgb_t(0x32,0x00,0x38), rgb_t(0x43,0x00,0x44), rgb_t(0x54,0x00,0x50), rgb_t(0x65,0x0C,0x5F), \
    rgb_t(0x76,0x1D,0x70), rgb_t(0x87,0x2E,0x81), rgb_t(0x98,0x3F,0x92), rgb_t(0xA9,0x50,0xA3), \
    rgb_t(0xBA,0x61,0xB4), rgb_t(0xCB,0x72,0xC5), rgb_t(0xDC,0x83,0xD6), rgb_t(0xED,0x94,0xE4), \
    rgb_t(0xFE,0xA5,0xE4), rgb_t(0xFF,0xB6,0xE9), rgb_t(0xFF,0xC7,0xEE), rgb_t(0xFF,0xD8,0xF3   )

#define NTSC_PURPLE_BLUE \
    rgb_t(0x1B,0x00,0x5F), rgb_t(0x2C,0x00,0x6B), rgb_t(0x3D,0x00,0x77), rgb_t(0x4E,0x11,0x88), \
    rgb_t(0x5F,0x22,0x99), rgb_t(0x70,0x33,0xAA), rgb_t(0x81,0x44,0xBB), rgb_t(0x92,0x55,0xCC), \
    rgb_t(0xA3,0x66,0xDD), rgb_t(0xB4,0x77,0xED), rgb_t(0xC5,0x88,0xED), rgb_t(0xD6,0x99,0xED), \
    rgb_t(0xE7,0xAA,0xED), rgb_t(0xF8,0xBB,0xED), rgb_t(0xFF,0xCC,0xF0), rgb_t(0xFF,0xDD,0xF5   )

#define NTSC_BLUE1 \
    rgb_t(0x00,0x00,0x72), rgb_t(0x10,0x00,0x7E), rgb_t(0x21,0x0D,0x8E), rgb_t(0x32,0x1E,0x9F), \
    rgb_t(0x43,0x2F,0xB0), rgb_t(0x54,0x40,0xC1), rgb_t(0x65,0x51,0xD2), rgb_t(0x76,0x62,0xE3), \
    rgb_t(0x87,0x73,0xF4), rgb_t(0x98,0x84,0xF9), rgb_t(0xA9,0x95,0xF9), rgb_t(0xBA,0xA6,0xF9), \
    rgb_t(0xCB,0xB7,0xF9), rgb_t(0xDC,0xC8,0xF9), rgb_t(0xED,0xD9,0xF9), rgb_t(0xFE,0xEA,0xF9   )

#define NTSC_BLUE2 \
    rgb_t(0x00,0x00,0x65), rgb_t(0x00,0x0C,0x7A), rgb_t(0x05,0x1D,0x8E), rgb_t(0x16,0x2E,0x9F), \
    rgb_t(0x27,0x3F,0xB0), rgb_t(0x38,0x50,0xC1), rgb_t(0x49,0x61,0xD2), rgb_t(0x5A,0x72,0xE3), \
    rgb_t(0x6B,0x83,0xF4), rgb_t(0x7C,0x94,0xFF), rgb_t(0x8D,0xA5,0xFF), rgb_t(0x9E,0xB6,0xFF), \
    rgb_t(0xAF,0xC7,0xFF), rgb_t(0xC0,0xD8,0xFF), rgb_t(0xD1,0xE9,0xFF), rgb_t(0xE2,0xFA,0xFF   )

#define NTSC_LIGHT_BLUE \
    rgb_t(0x00,0x0D,0x48), rgb_t(0x00,0x1E,0x5E), rgb_t(0x00,0x2F,0x74), rgb_t(0x00,0x40,0x8A), \
    rgb_t(0x11,0x51,0x9B), rgb_t(0x22,0x62,0xAC), rgb_t(0x33,0x73,0xBD), rgb_t(0x44,0x84,0xCE), \
    rgb_t(0x55,0x95,0xDF), rgb_t(0x66,0xA6,0xF0), rgb_t(0x77,0xB7,0xFF), rgb_t(0x88,0xC8,0xFF), \
    rgb_t(0x99,0xD9,0xFF), rgb_t(0xAA,0xEA,0xFF), rgb_t(0xBB,0xFB,0xFF), rgb_t(0xCC,0xFF,0xFF   )

#define NTSC_TURQUOISE \
    rgb_t(0x00,0x1C,0x1C), rgb_t(0x00,0x2D,0x32), rgb_t(0x00,0x3E,0x49), rgb_t(0x00,0x4F,0x5F), \
    rgb_t(0x05,0x60,0x73), rgb_t(0x16,0x71,0x84), rgb_t(0x27,0x82,0x95), rgb_t(0x38,0x93,0xA6), \
    rgb_t(0x49,0xA4,0xB7), rgb_t(0x5A,0xB5,0xC8), rgb_t(0x6B,0xC6,0xD9), rgb_t(0x7C,0xD7,0xEA), \
    rgb_t(0x8D,0xE8,0xFB), rgb_t(0x9E,0xF9,0xFF), rgb_t(0xAF,0xFF,0xFF), rgb_t(0xC0,0xFF,0xFF   )

#define NTSC_GREEN_BLUE \
    rgb_t(0x00,0x25,0x0B), rgb_t(0x00,0x36,0x10), rgb_t(0x00,0x47,0x18), rgb_t(0x00,0x58,0x2E), \
    rgb_t(0x07,0x69,0x42), rgb_t(0x18,0x7A,0x53), rgb_t(0x29,0x8B,0x64), rgb_t(0x3A,0x9C,0x75), \
    rgb_t(0x4B,0xAD,0x86), rgb_t(0x5C,0xBE,0x97), rgb_t(0x6D,0xCF,0xA8), rgb_t(0x7E,0xE0,0xB9), \
    rgb_t(0x8F,0xF1,0xCA), rgb_t(0xA0,0xFF,0xDA), rgb_t(0xB1,0xFF,0xE6), rgb_t(0xC2,0xFF,0xF2   )

#define NTSC_GREEN \
    rgb_t(0x00,0x27,0x0C), rgb_t(0x00,0x38,0x11), rgb_t(0x00,0x49,0x16), rgb_t(0x04,0x5A,0x1A), \
    rgb_t(0x15,0x6B,0x1A), rgb_t(0x26,0x7C,0x22), rgb_t(0x37,0x8D,0x33), rgb_t(0x48,0x9E,0x44), \
    rgb_t(0x59,0xAF,0x55), rgb_t(0x6A,0xC0,0x66), rgb_t(0x7B,0xD1,0x77), rgb_t(0x8C,0xE2,0x88), \
    rgb_t(0x9D,0xF3,0x99), rgb_t(0xAE,0xFF,0xA8), rgb_t(0xBF,0xFF,0xB4), rgb_t(0xD0,0xFF,0xC0   )

#define NTSC_YELLOW_GREEN \
    rgb_t(0x00,0x21,0x0A), rgb_t(0x00,0x32,0x0F), rgb_t(0x0A,0x43,0x11), rgb_t(0x1B,0x54,0x11), \
    rgb_t(0x2C,0x65,0x11), rgb_t(0x3D,0x76,0x11), rgb_t(0x4E,0x87,0x11), rgb_t(0x5F,0x98,0x1E), \
    rgb_t(0x70,0xA9,0x2F), rgb_t(0x81,0xBA,0x40), rgb_t(0x92,0xCB,0x51), rgb_t(0xA3,0xDC,0x62), \
    rgb_t(0xB4,0xED,0x73), rgb_t(0xC5,0xFE,0x84), rgb_t(0xD6,0xFF,0x90), rgb_t(0xE7,0xFF,0x9C   )

#define NTSC_ORANGE_GREEN \
    rgb_t(0x05,0x13,0x04), rgb_t(0x16,0x24,0x04), rgb_t(0x27,0x35,0x04), rgb_t(0x38,0x46,0x04), \
    rgb_t(0x49,0x57,0x04), rgb_t(0x5A,0x68,0x04), rgb_t(0x6B,0x79,0x04), rgb_t(0x7C,0x8A,0x09), \
    rgb_t(0x8D,0x9B,0x1A), rgb_t(0x9E,0xAC,0x2B), rgb_t(0xAF,0xBD,0x3C), rgb_t(0xC0,0xCE,0x4D), \
    rgb_t(0xD1,0xDF,0x5E), rgb_t(0xE2,0xF0,0x6F), rgb_t(0xF3,0xFF,0x80), rgb_t(0xFF,0xFF,0x8D   )

#define NTSC_LIGHT_ORANGE \
    rgb_t(0x21,0x02,0x00), rgb_t(0x32,0x13,0x00), rgb_t(0x43,0x24,0x00), rgb_t(0x54,0x35,0x00), \
    rgb_t(0x65,0x46,0x00), rgb_t(0x76,0x57,0x00), rgb_t(0x87,0x68,0x00), rgb_t(0x98,0x79,0x0C), \
    rgb_t(0xA9,0x8A,0x1D), rgb_t(0xBA,0x9B,0x2E), rgb_t(0xCB,0xAC,0x3F), rgb_t(0xDC,0xBD,0x50), \
    rgb_t(0xED,0xCE,0x61), rgb_t(0xFE,0xDF,0x72), rgb_t(0xFF,0xF0,0x87), rgb_t(0xFF,0xFF,0x9D   )
***************************************************************************/


/***************************************************************************
    PALETTE - 26.7 PHASE SHIFT


define NTSC_GREY
    rgb_t(0x00,0x00,0x00), rgb_t(0x11,0x11,0x11), rgb_t(0x22,0x22,0x22), rgb_t(0x33,0x33,0x33), \
    rgb_t(0x44,0x44,0x44), rgb_t(0x55,0x55,0x55), rgb_t(0x66,0x66,0x66), rgb_t(0x77,0x77,0x77), \
    rgb_t(0x88,0x88,0x88), rgb_t(0x99,0x99,0x99), rgb_t(0xAA,0xAA,0xAA), rgb_t(0xBB,0xBB,0xBB), \
    rgb_t(0xCC,0xCC,0xCC), rgb_t(0xDD,0xDD,0xDD), rgb_t(0xEE,0xEE,0xEE), rgb_t(0xFF,0xFF,0xFF   )

define NTSC_GOLD
    rgb_t(0x1A,0x07,0x00), rgb_t(0x2B,0x18,0x00), rgb_t(0x3C,0x29,0x00), rgb_t(0x4D,0x3A,0x00), \
    rgb_t(0x5E,0x4B,0x00), rgb_t(0x6F,0x5C,0x00), rgb_t(0x80,0x6D,0x00), rgb_t(0x91,0x7E,0x09), \
    rgb_t(0xA2,0x8F,0x1A), rgb_t(0xB3,0xA0,0x2B), rgb_t(0xC4,0xB1,0x3C), rgb_t(0xD5,0xC2,0x4D), \
    rgb_t(0xE6,0xD3,0x5E), rgb_t(0xF7,0xE4,0x6F), rgb_t(0xFF,0xF5,0x83), rgb_t(0xFF,0xFF,0x97   )

define NTSC_ORANGE
    rgb_t(0x32,0x00,0x00), rgb_t(0x43,0x06,0x00), rgb_t(0x54,0x17,0x00), rgb_t(0x65,0x28,0x00), \
    rgb_t(0x79,0x39,0x00), rgb_t(0x87,0x4A,0x00), rgb_t(0x98,0x5B,0x0C), rgb_t(0xA9,0x6C,0x1D), \
    rgb_t(0xBA,0x7D,0x2E), rgb_t(0xCB,0x8E,0x3F), rgb_t(0xDC,0x9F,0x50), rgb_t(0xED,0xB0,0x61), \
    rgb_t(0xFE,0xC1,0x72), rgb_t(0xFF,0xD2,0x87), rgb_t(0xFF,0xE3,0x9E), rgb_t(0xFF,0xF4,0xB4   )

define NTSC_RED_ORANGE
    rgb_t(0x3E,0x00,0x00), rgb_t(0x4F,0x00,0x00), rgb_t(0x60,0x07,0x00), rgb_t(0x71,0x18,0x00), \
    rgb_t(0x82,0x29,0x10), rgb_t(0x93,0x3A,0x21), rgb_t(0xA4,0x4B,0x32), rgb_t(0xB5,0x5C,0x43), \
    rgb_t(0xC6,0x6D,0x54), rgb_t(0xD7,0x7E,0x65), rgb_t(0xE8,0x8F,0x76), rgb_t(0xF9,0xA0,0x87), \
    rgb_t(0xFF,0xB1,0x9C), rgb_t(0xFF,0xC2,0xB2), rgb_t(0xFF,0xD3,0xC8), rgb_t(0xFF,0xE4,0xDE   )

define NTSC_PINK
    rgb_t(0x3E,0x00,0x09), rgb_t(0x4F,0x00,0x15), rgb_t(0x60,0x00,0x21), rgb_t(0x71,0x0E,0x31), \
    rgb_t(0x82,0x1F,0x42), rgb_t(0x93,0x30,0x53), rgb_t(0xA4,0x41,0x64), rgb_t(0xB5,0x52,0x75), \
    rgb_t(0xC6,0x63,0x86), rgb_t(0xD7,0x74,0x97), rgb_t(0xE8,0x85,0xA8), rgb_t(0xF9,0x96,0xB9), \
    rgb_t(0xFF,0xA7,0xCE), rgb_t(0xFF,0xB8,0xE4), rgb_t(0xFF,0xC9,0xEF), rgb_t(0xFF,0xDA,0xF4   )

define NTSC_PURPLE
    rgb_t(0x30,0x00,0x3D), rgb_t(0x41,0x00,0x48), rgb_t(0x52,0x00,0x54), rgb_t(0x63,0x0C,0x64), \
    rgb_t(0x74,0x1D,0x75), rgb_t(0x85,0x2E,0x86), rgb_t(0x96,0x3F,0x97), rgb_t(0xA7,0x50,0xA8), \
    rgb_t(0xB8,0x61,0xB9), rgb_t(0xC9,0x72,0xCA), rgb_t(0xDA,0x83,0xDB), rgb_t(0xEB,0x94,0xE5), \
    rgb_t(0xFC,0xA5,0xE5), rgb_t(0xFF,0xB6,0xE9), rgb_t(0xFF,0xC7,0xEE), rgb_t(0xFF,0xD8,0xF3   )

define NTSC_PURPLE_BLUE
    rgb_t(0x18,0x00,0x62), rgb_t(0x29,0x00,0x6E), rgb_t(0x3A,0x01,0x7A), rgb_t(0x4B,0x12,0x8B), \
    rgb_t(0x5C,0x23,0x9C), rgb_t(0x6D,0x34,0xAD), rgb_t(0x7E,0x45,0xBE), rgb_t(0x8F,0x56,0xCF), \
    rgb_t(0xA0,0x67,0xE0), rgb_t(0xB1,0x78,0xEE), rgb_t(0xC2,0x89,0xEE), rgb_t(0xD3,0x9A,0xEE), \
    rgb_t(0xE4,0xAB,0xEE), rgb_t(0xF5,0xBC,0xEE), rgb_t(0xFF,0xCD,0xE0), rgb_t(0xFF,0xDE,0xF5   )

define NTSC_BLUE1
    rgb_t(0x00,0x00,0x72), rgb_t(0x0C,0x00,0x7F), rgb_t(0x1D,0x0E,0x8F), rgb_t(0x2E,0x1F,0xA0), \
    rgb_t(0x3F,0x30,0xB1), rgb_t(0x50,0x41,0xC2), rgb_t(0x61,0x52,0xD3), rgb_t(0x72,0x63,0xE4), \
    rgb_t(0x83,0x74,0xF5), rgb_t(0x94,0x85,0xFA), rgb_t(0xA5,0x96,0xFA), rgb_t(0xB6,0xA7,0xFA), \
    rgb_t(0xC7,0xB8,0xFA), rgb_t(0xD8,0xC9,0xFA), rgb_t(0xE9,0xDA,0xFA), rgb_t(0xFA,0xE8,0xFA   )

define NTSC_BLUE2
    rgb_t(0x00,0x00,0x62), rgb_t(0x00,0x0F,0x77), rgb_t(0x01,0x20,0x8D), rgb_t(0x12,0x31,0x9E), \
    rgb_t(0x23,0x42,0xAF), rgb_t(0x34,0x53,0xC0), rgb_t(0x45,0x64,0xD1), rgb_t(0x56,0x75,0xE2), \
    rgb_t(0x67,0x86,0xF3), rgb_t(0x78,0x97,0xFF), rgb_t(0x89,0xA8,0xFF), rgb_t(0x9A,0xB9,0xFF), \
    rgb_t(0xAB,0xCA,0xFF), rgb_t(0xBC,0xDB,0xFF), rgb_t(0xCD,0xEC,0xFF), rgb_t(0xDE,0xFD,0xFF   )

define NTSC_LIGHT_BLUE
    rgb_t(0x00,0x10,0x42), rgb_t(0x00,0x21,0x58), rgb_t(0x00,0x32,0x6E), rgb_t(0x00,0x43,0x84), \
    rgb_t(0x0E,0x54,0x96), rgb_t(0x1F,0x65,0xA7), rgb_t(0x30,0x76,0xB8), rgb_t(0x41,0x87,0xC9), \
    rgb_t(0x52,0x98,0xDA), rgb_t(0x63,0xA9,0xEB), rgb_t(0x74,0xBA,0xFC), rgb_t(0x85,0xCB,0xFF), \
    rgb_t(0x96,0xDC,0xFF), rgb_t(0xA7,0xED,0xFF), rgb_t(0xB8,0xFE,0xFF), rgb_t(0xC9,0xFF,0xFF   )

define NTSC_TURQUOISE
    rgb_t(0x00,0x1E,0x14), rgb_t(0x00,0x2F,0x2A), rgb_t(0x00,0x40,0x40), rgb_t(0x00,0x51,0x56), \
    rgb_t(0x04,0x62,0x6B), rgb_t(0x15,0x73,0x7C), rgb_t(0x26,0x84,0x8D), rgb_t(0x37,0x95,0x9E), \
    rgb_t(0x48,0xA6,0xAF), rgb_t(0x59,0xB7,0xC0), rgb_t(0x6A,0xC8,0xD1), rgb_t(0x7B,0xD9,0xE2), \
    rgb_t(0x8C,0xEA,0xF3), rgb_t(0x9D,0xFB,0xFF), rgb_t(0xAE,0xFF,0xFF), rgb_t(0xBF,0xFF,0xFF   )

define NTSC_GREEN_BLUE
    rgb_t(0x00,0x26,0x0B), rgb_t(0x00,0x37,0x10), rgb_t(0x00,0x48,0x16), rgb_t(0x00,0x59,0x25), \
    rgb_t(0x08,0x6A,0x38), rgb_t(0x19,0x7B,0x49), rgb_t(0x2A,0x8C,0x5A), rgb_t(0x3B,0x9D,0x6B), \
    rgb_t(0x4C,0xAE,0x7C), rgb_t(0x5D,0xBF,0x8D), rgb_t(0x6E,0xD0,0x9E), rgb_t(0x7F,0xE1,0xAF), \
    rgb_t(0x90,0xF2,0xC0), rgb_t(0xA1,0xFF,0xD0), rgb_t(0xB2,0xFF,0xDC), rgb_t(0xC3,0xFF,0xE8   )

define NTSC_GREEN
    rgb_t(0x00,0x26,0x0B), rgb_t(0x00,0x37,0x10), rgb_t(0x00,0x48,0x16), rgb_t(0x08,0x59,0x18), \
    rgb_t(0x19,0x6A,0x18), rgb_t(0x2A,0x7B,0x18), rgb_t(0x3B,0x8C,0x29), rgb_t(0x4C,0x9D,0x3A), \
    rgb_t(0x5D,0xAE,0x4B), rgb_t(0x6E,0xBF,0x5C), rgb_t(0x7F,0xD0,0x6D), rgb_t(0x90,0xE1,0x7E), \
    rgb_t(0xA1,0xF2,0x8F), rgb_t(0xB2,0xFF,0x9F), rgb_t(0xC3,0xFF,0xAB), rgb_t(0xD4,0xFF,0xB7   )

define NTSC_YELLOW_GREEN
    rgb_t(0x00,0x1E,0x09), rgb_t(0x00,0x2F,0x0E), rgb_t(0x11,0x40,0x0E), rgb_t(0x22,0x51,0x0E), \
    rgb_t(0x33,0x62,0x0E), rgb_t(0x44,0x73,0x0E), rgb_t(0x55,0x84,0x0E), rgb_t(0x66,0x95,0x17), \
    rgb_t(0x77,0xA6,0x28), rgb_t(0x88,0xB7,0x39), rgb_t(0x99,0xC8,0x4A), rgb_t(0xAA,0xD9,0x5B), \
    rgb_t(0xBB,0xEA,0x6C), rgb_t(0xCC,0xFB,0x7D), rgb_t(0xDD,0xFF,0x8A), rgb_t(0xEE,0xFF,0x96   )

define NTSC_ORANGE_GREEN
    rgb_t(0x0D,0x0F,0x01), rgb_t(0x1E,0x20,0x01), rgb_t(0x2F,0x31,0x01), rgb_t(0x40,0x42,0x01), \
    rgb_t(0x51,0x53,0x01), rgb_t(0x62,0x64,0x01), rgb_t(0x73,0x75,0x01), rgb_t(0x84,0x86,0x08), \
    rgb_t(0x95,0x97,0x19), rgb_t(0xA6,0xA8,0x2A), rgb_t(0xB7,0xB9,0x3B), rgb_t(0xC8,0xCA,0x4C), \
    rgb_t(0xD9,0xDB,0x5D), rgb_t(0xEA,0xEC,0x6E), rgb_t(0xFB,0xFD,0x7F), rgb_t(0xFF,0xFF,0x8F   )

define NTSC_LIGHT_ORANGE
    rgb_t(0x28,0x00,0x00), rgb_t(0x39,0x0E,0x00), rgb_t(0x4A,0x1F,0x00), rgb_t(0x5B,0x30,0x00), \
    rgb_t(0x6C,0x41,0x00), rgb_t(0x7D,0x52,0x00), rgb_t(0x8E,0x63,0x00), rgb_t(0x9F,0x74,0x10), \
    rgb_t(0xB0,0x85,0x21), rgb_t(0xC1,0x96,0x32), rgb_t(0xD2,0xA7,0x43), rgb_t(0xE3,0xB8,0x54), \
    rgb_t(0xF4,0xC9,0x65), rgb_t(0xFF,0xDA,0x78), rgb_t(0xFF,0xEB,0x8E), rgb_t(0xFF,0xFC,0xA4   )
***************************************************************************/


/***************************************************************************
    PALETTE - 27.2 PHASE SHIFT


define NTSC_GREY
    rgb_t(0x00,0x00,0x00), rgb_t(0x11,0x11,0x11), rgb_t(0x22,0x22,0x22), rgb_t(0x33,0x33,0x33), \
    rgb_t(0x44,0x44,0x44), rgb_t(0x55,0x55,0x55), rgb_t(0x66,0x66,0x66), rgb_t(0x77,0x77,0x77), \
    rgb_t(0x88,0x88,0x88), rgb_t(0x99,0x99,0x99), rgb_t(0xAA,0xAA,0xAA), rgb_t(0xBB,0xBB,0xBB), \
    rgb_t(0xCC,0xCC,0xCC), rgb_t(0xDD,0xDD,0xDD), rgb_t(0xEE,0xEE,0xEE), rgb_t(0xFF,0xFF,0xFF   )

define NTSC_GOLD
    rgb_t(0x1A,0x07,0x00), rgb_t(0x2B,0x18,0x00), rgb_t(0x3C,0x29,0x00), rgb_t(0x4D,0x3A,0x00), \
    rgb_t(0x5E,0x4B,0x00), rgb_t(0x6F,0x5C,0x00), rgb_t(0x80,0x6D,0x00), rgb_t(0x91,0x7E,0x09), \
    rgb_t(0xA2,0x8F,0x1A), rgb_t(0xB3,0xA0,0x2B), rgb_t(0xC4,0xB1,0x3C), rgb_t(0xD5,0xC2,0x4D), \
    rgb_t(0xE6,0xD3,0x5E), rgb_t(0xF7,0xE4,0x6F), rgb_t(0xFF,0xF5,0x83), rgb_t(0xFF,0xF7,0x97   )

define NTSC_ORANGE
    rgb_t(0x32,0x00,0x00), rgb_t(0x43,0x05,0x00), rgb_t(0x54,0x16,0x00), rgb_t(0x65,0x27,0x00), \
    rgb_t(0x76,0x38,0x00), rgb_t(0x87,0X49,0x00), rgb_t(0x98,0x5A,0x0C), rgb_t(0xA9,0x6B,0x1D), \
    rgb_t(0xBA,0x7C,0x2E), rgb_t(0xCB,0x8D,0x3F), rgb_t(0xDC,0x9E,0x50), rgb_t(0xED,0xAF,0x61), \
    rgb_t(0xFE,0xC0,0x72), rgb_t(0xFF,0xD1,0x88), rgb_t(0xFF,0xE2,0x9E), rgb_t(0xFF,0xF3,0xB4   )

define NTSC_RED_ORANGE
    rgb_t(0x3F,0x00,0x00), rgb_t(0x50,0x00,0x00), rgb_t(0x61,0x07,0x00), rgb_t(0x72,0x18,0x01), \
    rgb_t(0x83,0x29,0x12), rgb_t(0x94,0x3A,0x23), rgb_t(0xA5,0x4B,0x34), rgb_t(0xB6,0x5C,0x45), \
    rgb_t(0xC7,0x6D,0x56), rgb_t(0xD8,0x7E,0x67), rgb_t(0xE9,0x8F,0x78), rgb_t(0xFA,0xA0,0x89), \
    rgb_t(0xFF,0xB1,0x9E), rgb_t(0xFF,0xC2,0xB4), rgb_t(0xFF,0xD3,0xCA), rgb_t(0xFF,0xE4,0xE0   )

define NTSC_PINK
    rgb_t(0x3E,0x00,0x0C), rgb_t(0x4F,0x00,0x18), rgb_t(0x60,0x00,0x24), rgb_t(0x71,0x0E,0x34), \
    rgb_t(0x82,0x1F,0x45), rgb_t(0x93,0x30,0x56), rgb_t(0xA4,0x41,0x67), rgb_t(0xB5,0x52,0x78), \
    rgb_t(0xC6,0x63,0x89), rgb_t(0xD7,0x74,0x9A), rgb_t(0xE8,0x85,0xAB), rgb_t(0xF9,0x96,0xB6), \
    rgb_t(0xFF,0xA7,0xD1), rgb_t(0xFF,0xB8,0xE7), rgb_t(0xFF,0xC9,0xEF), rgb_t(0xFF,0xDA,0xF4   )

define NTSC_PURPLE
    rgb_t(0x2F,0x00,0x3F), rgb_t(0x40,0x00,0x4B), rgb_t(0x51,0x00,0x57), rgb_t(0x62,0x0C,0x66), \
    rgb_t(0x73,0x1D,0x77), rgb_t(0x84,0x2E,0x88), rgb_t(0x95,0x3F,0x99), rgb_t(0xA6,0x50,0xAA), \
    rgb_t(0xB7,0x61,0xBB), rgb_t(0xC8,0x72,0xCC), rgb_t(0xD9,0x83,0xDD), rgb_t(0xEA,0x94,0xE5), \
    rgb_t(0xFB,0xA5,0xE5), rgb_t(0xFF,0xB6,0xE9), rgb_t(0xFF,0xC7,0xEE), rgb_t(0xFF,0xD8,0xF3   )

define NTSC_PURPLE_BLUE
    rgb_t(0x16,0x00,0x64), rgb_t(0x27,0x00,0x70), rgb_t(0x38,0x02,0x7D), rgb_t(0x49,0x13,0x8E), \
    rgb_t(0x5A,0x24,0x9F), rgb_t(0x6B,0x35,0xB0), rgb_t(0x7C,0x46,0xC1), rgb_t(0x8D,0x57,0xD2), \
    rgb_t(0x9E,0x68,0xE3), rgb_t(0xAF,0x79,0xEF), rgb_t(0xC0,0x8A,0xEF), rgb_t(0xD1,0x9D,0xEF), \
    rgb_t(0xE2,0xAC,0xEF), rgb_t(0xF3,0xBD,0xEF), rgb_t(0xFF,0xCE,0xF0), rgb_t(0xFF,0xDF,0xF5   )

define NTSC_BLUE1
    rgb_t(0x00,0x00,0x71), rgb_t(0x09,0x00,0x7F), rgb_t(0x1A,0x10,0x90), rgb_t(0x2B,0x21,0xA1), \
    rgb_t(0x3C,0x32,0xB2), rgb_t(0x4D,0x43,0xC3), rgb_t(0x5E,0x54,0xD4), rgb_t(0x6F,0x65,0xE5), \
    rgb_t(0x80,0x76,0xF6), rgb_t(0x91,0x87,0xFC), rgb_t(0xA2,0x98,0xFC), rgb_t(0xB3,0xA9,0xFC), \
    rgb_t(0xC4,0xBA,0xFC), rgb_t(0xD5,0xCB,0xFC), rgb_t(0xE6,0xDC,0xFC), rgb_t(0xF7,0xED,0xFC   )

define NTSC_BLUE2
    rgb_t(0x00,0x00,0x5E), rgb_t(0x00,0x11,0x74), rgb_t(0x00,0x22,0x8A), rgb_t(0x0F,0x33,0x9C), \
    rgb_t(0x20,0x44,0xAD), rgb_t(0x31,0x55,0xBE), rgb_t(0x42,0x66,0xCF), rgb_t(0x53,0x77,0xE0), \
    rgb_t(0x64,0x88,0xF1), rgb_t(0x75,0x99,0xFF), rgb_t(0x86,0xAA,0xFF), rgb_t(0x97,0xBB,0xFF), \
    rgb_t(0xA8,0xCC,0xFF), rgb_t(0xB9,0xDD,0xFF), rgb_t(0xCA,0xEE,0xFF), rgb_t(0xDB,0xFF,0xFF   )

define NTSC_LIGHT_BLUE
    rgb_t(0x00,0x12,0x3B), rgb_t(0x00,0x23,0x51), rgb_t(0x00,0x34,0x68), rgb_t(0x00,0x45,0x7E), \
    rgb_t(0x0C,0x56,0x90), rgb_t(0x1D,0x67,0xA1), rgb_t(0x2E,0x78,0xB2), rgb_t(0x3F,0x89,0xC3), \
    rgb_t(0x50,0x9A,0xD4), rgb_t(0x61,0xAB,0xE5), rgb_t(0x72,0xBC,0xF6), rgb_t(0x83,0xCD,0xFF), \
    rgb_t(0x94,0xDE,0xFF), rgb_t(0xA5,0xEF,0xFF), rgb_t(0xB6,0xFF,0xFF), rgb_t(0xC7,0xFF,0xFF   )

define NTSC_TURQUOISE
    rgb_t(0x00,0x20,0x0C), rgb_t(0x00,0x31,0x22), rgb_t(0x00,0x42,0x38), rgb_t(0x00,0x53,0x4E), \
    rgb_t(0x04,0x64,0x63), rgb_t(0x15,0x75,0x74), rgb_t(0x26,0x86,0x85), rgb_t(0x37,0x97,0x96), \
    rgb_t(0x48,0xA8,0xA7), rgb_t(0x59,0xB9,0xB8), rgb_t(0x6A,0xCA,0xC9), rgb_t(0x7B,0xDB,0xDA), \
    rgb_t(0x8C,0xEC,0xEB), rgb_t(0x9D,0xFD,0xFC), rgb_t(0xAE,0xFF,0xFF), rgb_t(0xBF,0xFF,0xFF   )

define NTSC_GREEN_BLUE
    rgb_t(0x00,0x27,0x0C), rgb_t(0x00,0x38,0x11), rgb_t(0x00,0x49,0x16), rgb_t(0x00,0x5A,0x1C), \
    rgb_t(0x0B,0x6B,0x2F), rgb_t(0x1C,0x7C,0x40), rgb_t(0x2D,0x8D,0x51), rgb_t(0x3E,0x9E,0x62), \
    rgb_t(0x4F,0xAF,0x73), rgb_t(0x60,0xC0,0x84), rgb_t(0x71,0xD1,0x95), rgb_t(0x82,0xE2,0xA6), \
    rgb_t(0x93,0xF3,0xB7), rgb_t(0xA4,0xFF,0xC6), rgb_t(0xB5,0xFF,0xD2), rgb_t(0xC6,0xFF,0xDE   )

define NTSC_GREEN
    rgb_t(0x00,0x25,0x0B), rgb_t(0x00,0x36,0x10), rgb_t(0x00,0x47,0x15), rgb_t(0x0D,0x58,0x16), \
    rgb_t(0x1E,0x69,0x16), rgb_t(0x2F,0x7A,0x16), rgb_t(0x40,0x8B,0x21), rgb_t(0x51,0x9C,0x32), \
    rgb_t(0x62,0xAD,0x43), rgb_t(0x73,0xBE,0x54), rgb_t(0x84,0xCF,0x65), rgb_t(0x95,0xE0,0x76), \
    rgb_t(0xA6,0xF1,0x87), rgb_t(0xB7,0xFF,0x98), rgb_t(0xC8,0xFF,0xA3), rgb_t(0xD9,0xFF,0xAF   )

define NTSC_YELLOW_GREEN
    rgb_t(0x00,0x1B,0x08), rgb_t(0x06,0x2C,0x0B), rgb_t(0x17,0x3D,0x0B), rgb_t(0x28,0x4E,0x0B), \
    rgb_t(0x39,0x5F,0x0B), rgb_t(0x4A,0x70,0x0B), rgb_t(0x5B,0x81,0x0B), rgb_t(0x6C,0x92,0x12), \
    rgb_t(0x7D,0xA3,0x23), rgb_t(0x8E,0xB4,0x34), rgb_t(0x9F,0xC5,0x45), rgb_t(0xB0,0xD6,0x56), \
    rgb_t(0xC1,0xE7,0x67), rgb_t(0xD2,0xF8,0x78), rgb_t(0xE3,0xFF,0x86), rgb_t(0xF4,0xFF,0x92   )

define NTSC_ORANGE_GREEN
    rgb_t(0x13,0x0B,0x00), rgb_t(0x24,0x1C,0x00), rgb_t(0x35,0x2D,0x00), rgb_t(0x46,0x3E,0x00), \
    rgb_t(0x57,0x4F,0x00), rgb_t(0x68,0x60,0x00), rgb_t(0x79,0x71,0x00), rgb_t(0x8A,0x82,0x08), \
    rgb_t(0x9B,0x93,0x19), rgb_t(0xAC,0xA4,0x2A), rgb_t(0xBD,0xB5,0x3B), rgb_t(0xCE,0xC6,0x4C), \
    rgb_t(0xDF,0xD7,0x5D), rgb_t(0xF0,0xE8,0x6E), rgb_t(0xFF,0xF9,0x7F), rgb_t(0xFF,0xFF,0x92   )

define NTSC_LIGHT_ORANGE
    rgb_t(0x2D,0x00,0x00), rgb_t(0x3E,0x0A,0x00), rgb_t(0x4F,0x1B,0x00), rgb_t(0x60,0x2C,0x00), \
    rgb_t(0x71,0x3D,0x00), rgb_t(0x82,0x4E,0x00), rgb_t(0x93,0x5F,0x05), rgb_t(0xA4,0x70,0x16), \
    rgb_t(0xB5,0x81,0x27), rgb_t(0xC4,0x90,0x37), rgb_t(0xD7,0xA3,0x49), rgb_t(0xE8,0xB4,0x5A), \
    rgb_t(0xF9,0xC5,0x6B), rgb_t(0xFF,0xD6,0x80), rgb_t(0xFF,0xE7,0x96), rgb_t(0xFF,0xF8,0xAC   )
***************************************************************************/


/***************************************************************************
    PALETTE - 27.7 PHASE SHIFT


define NTSC_GREY
    rgb_t(0x00,0x00,0x00), rgb_t(0x11,0x11,0x11), rgb_t(0x22,0x22,0x22), rgb_t(0x33,0x33,0x33), \
    rgb_t(0x44,0x44,0x44), rgb_t(0x55,0x55,0x55), rgb_t(0x66,0x66,0x66), rgb_t(0x77,0x77,0x77), \
    rgb_t(0x88,0x88,0x88), rgb_t(0x99,0x99,0x99), rgb_t(0xAA,0xAA,0xAA), rgb_t(0xBB,0xBB,0xBB), \
    rgb_t(0xCC,0xCC,0xCC), rgb_t(0xDD,0xDD,0xDD), rgb_t(0xEE,0xEE,0xEE), rgb_t(0xFF,0xFF,0xFF   )

define NTSC_GOLD
    rgb_t(0x1A,0x07,0x00), rgb_t(0x2B,0x18,0x00), rgb_t(0x3C,0x29,0x00), rgb_t(0x4D,0x3A,0x00), \
    rgb_t(0x5E,0x4B,0x00), rgb_t(0x6F,0x5C,0x00), rgb_t(0x80,0x6D,0x00), rgb_t(0x91,0x7E,0x09), \
    rgb_t(0xA2,0x8F,0x1A), rgb_t(0xB3,0xA0,0x2B), rgb_t(0xC4,0xB1,0x3C), rgb_t(0xD5,0xC2,0x4D), \
    rgb_t(0xE6,0xD3,0x5E), rgb_t(0xF7,0xE4,0x6F), rgb_t(0xFF,0xF5,0x83), rgb_t(0xFF,0xFF,0x97   )

define NTSC_ORANGE
    rgb_t(0x32,0x00,0x00), rgb_t(0x43,0x05,0x00), rgb_t(0x54,0x16,0x00), rgb_t(0x65,0x27,0x00), \
    rgb_t(0x76,0x38,0x00), rgb_t(0x87,0x49,0x00), rgb_t(0x98,0x5A,0x0C), rgb_t(0xA9,0x6B,0x1D), \
    rgb_t(0xBA,0x7C,0x2E), rgb_t(0xCB,0x8D,0x3F), rgb_t(0xDC,0x9E,0x50), rgb_t(0xED,0xAF,0x61), \
    rgb_t(0xFE,0xC0,0x72), rgb_t(0xFF,0xD1,0x88), rgb_t(0xFF,0xE2,0x9E), rgb_t(0xFF,0xF3,0xB4   )

define NTSC_RED_ORANGE
    rgb_t(0x3F,0x00,0x00), rgb_t(0x50,0x00,0x00), rgb_t(0x61,0x06,0x00), rgb_t(0x72,0x17,0x03), \
    rgb_t(0x83,0x28,0x14), rgb_t(0x94,0x39,0x25), rgb_t(0xA5,0x4A,0x36), rgb_t(0xB6,0x5B,0x47), \
    rgb_t(0xC7,0x6C,0x58), rgb_t(0xD8,0x7D,0x69), rgb_t(0xE9,0x8E,0x7A), rgb_t(0xFA,0x9F,0x8B), \
    rgb_t(0xFF,0xB0,0x9F), rgb_t(0xFF,0xC1,0xB5), rgb_t(0xFF,0xD2,0xCB), rgb_t(0xFF,0xE3,0xE1   )

define NTSC_PINK
    rgb_t(0x3D,0x00,0x10), rgb_t(0x4E,0x00,0x1C), rgb_t(0x5F,0x00,0x27), rgb_t(0x70,0x0D,0x37), \
    rgb_t(0x81,0x1E,0x48), rgb_t(0x92,0x2F,0x59), rgb_t(0xA3,0x40,0x6A), rgb_t(0xB4,0x51,0x7B), \
    rgb_t(0xC5,0x62,0x8C), rgb_t(0xD6,0x73,0x9D), rgb_t(0xE7,0x84,0xAE), rgb_t(0xF8,0x95,0xBF), \
    rgb_t(0xFF,0xA6,0xD3), rgb_t(0xFF,0xB7,0xE9), rgb_t(0xFF,0xC8,0xEE), rgb_t(0xFF,0xD9,0xF4   )

define NTSC_PURPLE
    rgb_t(0x2D,0x00,0x42), rgb_t(0x3E,0x00,0x4E), rgb_t(0x4F,0x00,0x5A), rgb_t(0x60,0x0C,0x6A), \
    rgb_t(0x71,0x1D,0x7B), rgb_t(0x82,0x2E,0x8C), rgb_t(0x93,0x3F,0x9D), rgb_t(0xA4,0x50,0xAE), \
    rgb_t(0xB5,0x61,0xBF), rgb_t(0xC6,0x72,0xD0), rgb_t(0xD7,0x83,0xE1), rgb_t(0xE8,0x94,0xE6), \
    rgb_t(0xF9,0xA5,0xE6), rgb_t(0xFF,0xB6,0xE9), rgb_t(0xFF,0xC7,0xEE), rgb_t(0xFF,0xD8,0xF3   )

define NTSC_PURPLE_BLUE
    rgb_t(0x13,0x00,0x67), rgb_t(0x24,0x00,0x73), rgb_t(0x35,0x03,0x80), rgb_t(0x46,0x14,0x91), \
    rgb_t(0x57,0x25,0xA2), rgb_t(0x68,0x36,0xB3), rgb_t(0x79,0x47,0xC4), rgb_t(0x8A,0x58,0xD5), \
    rgb_t(0x9B,0x69,0xE6), rgb_t(0xAC,0x7A,0xF0), rgb_t(0xBD,0x8B,0xF0), rgb_t(0xCE,0x9C,0xF0), \
    rgb_t(0xDF,0xAD,0xF0), rgb_t(0xF0,0xBE,0xF0), rgb_t(0xFF,0xCF,0xF1), rgb_t(0xFF,0xE0,0xF6   )

define NTSC_BLUE1
    rgb_t(0x00,0x00,0x70), rgb_t(0x05,0x01,0x80), rgb_t(0x16,0x12,0x91), rgb_t(0x27,0x23,0xA2), \
    rgb_t(0x38,0x34,0xB3), rgb_t(0x49,0x45,0xC4), rgb_t(0x5A,0x56,0xD5), rgb_t(0x6B,0x67,0xE6), \
    rgb_t(0x7C,0x78,0xF7), rgb_t(0x8D,0x89,0xFE), rgb_t(0x9E,0x9A,0xFE), rgb_t(0xAF,0xAB,0xFE), \
    rgb_t(0xC0,0xBC,0xFE), rgb_t(0xD1,0xCD,0xFE), rgb_t(0xE2,0xDE,0xFE), rgb_t(0xF3,0xEF,0xFE   )

define NTSC_BLUE2
    rgb_t(0x00,0x03,0x5B), rgb_t(0x00,0x14,0x71), rgb_t(0x00,0x25,0x87), rgb_t(0x0C,0x36,0x9A), \
    rgb_t(0x1D,0x47,0xAB), rgb_t(0x2E,0x58,0xBC), rgb_t(0x3F,0x69,0xCD), rgb_t(0x50,0x7A,0xDE), \
    rgb_t(0x61,0x8B,0xEF), rgb_t(0x72,0x9C,0xFF), rgb_t(0x83,0xAD,0xFF), rgb_t(0x94,0xBE,0xFF), \
    rgb_t(0xA5,0xCF,0xFF), rgb_t(0xB6,0xE0,0xFF), rgb_t(0xC7,0xF1,0xFF), rgb_t(0xD8,0xFF,0xFF   )

define NTSC_LIGHT_BLUE
    rgb_t(0x00,0x15,0x35), rgb_t(0x00,0x26,0x4B), rgb_t(0x00,0x37,0x61), rgb_t(0x00,0x48,0x78), \
    rgb_t(0x0A,0x59,0x8B), rgb_t(0x1B,0x6A,0x9C), rgb_t(0x2C,0x7B,0xAD), rgb_t(0x3D,0x8C,0xBE), \
    rgb_t(0x4E,0x9D,0xCF), rgb_t(0x5F,0xAE,0xE0), rgb_t(0x70,0xBF,0xF1), rgb_t(0x81,0xD0,0xFF), \
    rgb_t(0x92,0xE1,0xFF), rgb_t(0xA3,0xF2,0xFF), rgb_t(0xB4,0xFF,0xFF), rgb_t(0xC5,0xFF,0xFF   )

define NTSC_TURQUOISE
    rgb_t(0x00,0x22,0x0A), rgb_t(0x00,0x33,0x19), rgb_t(0x00,0x44,0x2F), rgb_t(0x00,0x55,0x45), \
    rgb_t(0x04,0x66,0x5A), rgb_t(0x15,0x77,0x6B), rgb_t(0x26,0x88,0x7C), rgb_t(0x37,0x99,0x8D), \
    rgb_t(0x48,0xAA,0x9E), rgb_t(0x59,0xBB,0xAF), rgb_t(0x6A,0xCC,0xC0), rgb_t(0x7B,0xDD,0xD1), \
    rgb_t(0x8C,0xEE,0xE2), rgb_t(0x9D,0xFF,0xF3), rgb_t(0xAE,0xFF,0xFF), rgb_t(0xBF,0xFF,0xFF   )

define NTSC_GREEN_BLUE
    rgb_t(0x00,0x27,0x0C), rgb_t(0x00,0x38,0x11), rgb_t(0x00,0x49,0x16), rgb_t(0x00,0x5A,0x1B), \
    rgb_t(0x0D,0x6B,0x25), rgb_t(0x1E,0x7C,0x36), rgb_t(0x2F,0x8D,0x47), rgb_t(0x40,0x9E,0x58), \
    rgb_t(0x51,0xAF,0x69), rgb_t(0x62,0xC0,0x7A), rgb_t(0x73,0xD1,0x8B), rgb_t(0x84,0xE2,0x9C), \
    rgb_t(0x95,0xF3,0xAD), rgb_t(0xA6,0xFF,0xBD), rgb_t(0xB7,0xFF,0xC9), rgb_t(0xC8,0xFF,0xD4   )

define NTSC_GREEN
    rgb_t(0x00,0x24,0x0B), rgb_t(0x00,0x35,0x10), rgb_t(0x01,0x46,0x15), rgb_t(0x12,0x57,0x15), \
    rgb_t(0x23,0x68,0x15), rgb_t(0x34,0x79,0x15), rgb_t(0x45,0x8A,0x19), rgb_t(0x56,0x9B,0x2A), \
    rgb_t(0x67,0xAC,0x3B), rgb_t(0x78,0xBD,0x4C), rgb_t(0x89,0xCE,0x5D), rgb_t(0x9A,0xDF,0x6E), \
    rgb_t(0xAB,0xF0,0x7F), rgb_t(0xBC,0xFF,0x8F), rgb_t(0xCD,0xFF,0x9B), rgb_t(0xDE,0xFF,0xA7   )

define NTSC_YELLOW_GREEN
    rgb_t(0x00,0x18,0x07), rgb_t(0x00,0x29,0x0C), rgb_t(0x1E,0x3A,0x08), rgb_t(0x2F,0x4B,0x08), \
    rgb_t(0x40,0x5C,0x08), rgb_t(0x51,0x6D,0x08), rgb_t(0x62,0x7E,0x08), rgb_t(0x73,0x8F,0x0D), \
    rgb_t(0x84,0xA0,0x1E), rgb_t(0x95,0xB1,0x2F), rgb_t(0xA6,0xC2,0x40), rgb_t(0xB7,0xD3,0x51), \
    rgb_t(0xC8,0xE4,0x62), rgb_t(0xD9,0xF5,0x73), rgb_t(0xEA,0xFF,0x82), rgb_t(0xFB,0xFF,0x8E   )

define NTSC_ORANGE_GREEN
    rgb_t(0x1B,0x07,0x00), rgb_t(0x2C,0x18,0x00), rgb_t(0x3D,0x29,0x00), rgb_t(0x4E,0x3A,0x00), \
    rgb_t(0x5F,0x4B,0x00), rgb_t(0x70,0x5C,0x00), rgb_t(0x81,0x6D,0x00), rgb_t(0x92,0x7E,0x09), \
    rgb_t(0xA3,0x8F,0x1A), rgb_t(0xB4,0xA0,0x2B), rgb_t(0xC5,0xB1,0x3C), rgb_t(0xD6,0xC2,0x4D), \
    rgb_t(0xE7,0xD3,0x5E), rgb_t(0xF8,0xE4,0x6F), rgb_t(0xFF,0xF5,0x83), rgb_t(0xFF,0xFF,0x97   )

define NTSC_LIGHT_ORANGE
    rgb_t(0x33,0x00,0x00), rgb_t(0x44,0x05,0x00), rgb_t(0x55,0x16,0x00), rgb_t(0x66,0x27,0x00), \
    rgb_t(0x77,0x38,0x00), rgb_t(0x88,0x49,0x00), rgb_t(0x99,0x5A,0x0D), rgb_t(0xAA,0x6B,0x1E), \
    rgb_t(0xBB,0x7C,0x2F), rgb_t(0xCC,0x8D,0x40), rgb_t(0xDD,0x9E,0x51), rgb_t(0xEE,0xAF,0x62), \
    rgb_t(0xFF,0xC0,0x73), rgb_t(0xFF,0xD1,0x89), rgb_t(0xFF,0xE2,0x9F), rgb_t(0xFF,0xF3,0xB5   )
***************************************************************************/

/* Initialise the palette */
void a7800_state::a7800_palette(palette_device &palette) const
{
	palette.set_pen_colors(0, a7800_colors);
}


void a7800_pal_state::a7800_palette(palette_device &palette) const
{
	palette.set_pen_colors(0, a7800p_colors);
}


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

void a7800_state::machine_start()
{
	save_item(NAME(m_p1_one_button));
	save_item(NAME(m_p2_one_button));
	save_item(NAME(m_bios_enabled));
	save_item(NAME(m_ctrl_lock));
	save_item(NAME(m_ctrl_reg));
	save_item(NAME(m_maria_flag));

	// install additional handlers, if needed
	if (m_cart->exists())
	{
		switch (m_cart->get_cart_type())
		{
		case A78_HSC:
			// ROM+NVRAM accesses for HiScore
			m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x1000, 0x17ff, read8sm_delegate(*m_cart, FUNC(a78_cart_slot_device::read_10xx)), write8sm_delegate(*m_cart, FUNC(a78_cart_slot_device::write_10xx)));
			m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x3000, 0x3fff, read8sm_delegate(*m_cart, FUNC(a78_cart_slot_device::read_30xx)), write8sm_delegate(*m_cart, FUNC(a78_cart_slot_device::write_30xx)));
			break;
		case A78_XB_BOARD:
		case A78_TYPE0_POK450:
		case A78_TYPE1_POK450:
		case A78_TYPE6_POK450:
		case A78_TYPEA_POK450:
		case A78_VERSA_POK450:
			// POKEY and RAM regs at 0x400-0x47f
			m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x0400, 0x047f, read8sm_delegate(*m_cart, FUNC(a78_cart_slot_device::read_04xx)), write8sm_delegate(*m_cart, FUNC(a78_cart_slot_device::write_04xx)));
			break;
		case A78_XM_BOARD:
			// POKEY and RAM and YM regs at 0x400-0x47f
			m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x0400, 0x047f, read8sm_delegate(*m_cart, FUNC(a78_cart_slot_device::read_04xx)), write8sm_delegate(*m_cart, FUNC(a78_cart_slot_device::write_04xx)));
			// ROM+NVRAM accesses for HiScore
			m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x1000, 0x17ff, read8sm_delegate(*m_cart, FUNC(a78_cart_slot_device::read_10xx)), write8sm_delegate(*m_cart, FUNC(a78_cart_slot_device::write_10xx)));
			m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x3000, 0x3fff, read8sm_delegate(*m_cart, FUNC(a78_cart_slot_device::read_30xx)), write8sm_delegate(*m_cart, FUNC(a78_cart_slot_device::write_30xx)));
			break;
		}
	}

	m_dma_start_timer = timer_alloc(FUNC(a7800_state::maria_startdma), this);
}

void a7800_state::machine_reset()
{
	m_ctrl_lock = 0;
	m_ctrl_reg = 0;
	m_maria_flag = 0;
	m_bios_enabled = 0;
}

void a7800_state::a7800_common(machine_config &config, uint32_t clock)
{
	// basic machine hardware
	M6502(config, m_maincpu, clock/8); // NTSC 1.79 MHz, PAL 1.77 MHz (switches to 1.19 MHz on TIA or RIOT access)
	m_maincpu->set_addrmap(AS_PROGRAM, &a7800_state::a7800_mem);
	TIMER(config, "scantimer").configure_scanline(FUNC(a7800_state::interrupt), "screen", 0, 1);

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_screen_update(m_maria, FUNC(atari_maria_device::screen_update));
	m_screen->set_palette("palette");

	PALETTE(config, "palette", FUNC(a7800_state::a7800_palette), std::size(a7800_colors));

	ATARI_MARIA(config, m_maria, 0);
	m_maria->set_dmacpu_tag(m_maincpu);
	m_maria->set_screen_tag(m_screen);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	TIA(config, m_tia, clock/4/114).add_route(ALL_OUTPUTS, "mono", 1.00);

	// devices
	MOS6532(config, m_riot, clock/8);
	m_riot->pa_rd_callback().set(FUNC(a7800_state::riot_joystick_r));
	m_riot->pb_rd_callback().set(FUNC(a7800_state::riot_console_button_r));
	m_riot->pb_wr_callback().set(FUNC(a7800_state::riot_button_pullup_w));

	A78_CART_SLOT(config, m_cart, clock/8, a7800_cart, nullptr);
}

void a7800_ntsc_state::a7800_ntsc(machine_config &config)
{
	a7800_common(config, 14'318'180); // from schematics

	// basic machine hardware
	m_screen->set_raw(14'318'180/2, 454, 0, 320, 263, 27, 27 + 192 + 32);

	m_cart->set_must_be_loaded(true);

	// software lists
	SOFTWARE_LIST(config, "cart_list").set_original("a7800").set_filter("NTSC");
}

void a7800_pal_state::a7800_pal(machine_config &config)
{
	a7800_common(config, 14'187'576); // from hardware tests (and label?)

	// basic machine hardware
	m_screen->set_raw(14'187'576/2, 454, 0, 320, 313, 35, 35 + 228 + 32);

	// software lists
	SOFTWARE_LIST(config, "cart_list").set_original("a7800").set_filter("PAL");
}


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

ROM_START( a7800 )
	ROM_REGION(0x4000, "maincpu", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS( 0, "a7800", "Atari 7800" )
	// TODO: c024922b-29usa.u7, c024922b-29.u7 and c024922b-01.u7 have been seen on pics, which one is this? Or are they all the same?
	ROMX_LOAD("7800.u7", 0x3000, 0x1000, CRC(5d13730c) SHA1(d9d134bb6b36907c615a594cc7688f7bfcef5b43), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "a7800pr", "Atari 7800 (prototype with Asteroids)" ) // TODO: is this really a prototype? notice the ROM code is almost the same as the released PAL version
	ROMX_LOAD("c300558-001a.u7", 0x0000, 0x4000, CRC(a0e10edf) SHA1(14584b1eafe9721804782d4b1ac3a4a7313e455f), ROM_BIOS(1))
ROM_END

ROM_START( a7800p )
	ROM_REGION(0x4000, "maincpu", ROMREGION_ERASEFF)
	ROM_LOAD("c300558-001b.u7", 0x0000, 0x4000, CRC(d5b61170) SHA1(5a140136a16d1d83e4ff32a19409ca376a8df874))
ROM_END


/***************************************************************************
 DRIVER INIT
 ***************************************************************************/

void a7800_ntsc_state::init_a7800_ntsc()
{
	m_ispal = false;
	m_lines = 263;
	m_p1_one_button = 1;
	m_p2_one_button = 1;
}


void a7800_pal_state::init_a7800_pal()
{
	m_ispal = true;
	m_lines = 313;
	m_p1_one_button = 1;
	m_p2_one_button = 1;
}

} // anonymous namespace


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

//    YEAR  NAME    PARENT  COMPAT  MACHINE     INPUT  CLASS             INIT             COMPANY   FULLNAME             FLAGS
CONS( 1986, a7800,  0,      0,      a7800_ntsc, a7800, a7800_ntsc_state, init_a7800_ntsc, "Atari",  "Atari 7800 (NTSC)", 0 )
CONS( 1986, a7800p, a7800,  0,      a7800_pal,  a7800, a7800_pal_state,  init_a7800_pal,  "Atari",  "Atari 7800 (PAL)",  0 )



aa310.cpp
<---------------------------------------------------------------------->
// license:LGPL-2.1+
// copyright-holders:Angelo Salese, R. Belmont, Juergen Buchmueller, Nigel Barnes
/******************************************************************************
 *
 *  Acorn Archimedes
 *
 *  AKB10 - Archimedes 305
 *  AKB15 - Archimedes 310
 *  AKB20 - Archimedes 440
 *  AKB26 - Archimedes 410 (advertised but not known to be produced)
 *  AKB01 - BBC A3000
 *  AKB40 - Archimedes 410/1
 *  AKB42 - Archimedes 420/1
 *  AKB44 - Archimedes 440/1
 *  AKB50 - Archimedes 540
 *  ALB22 - Acorn A5000 2MB HD 80
 *  ALB24 - Acorn A5000 4MB HD 120
 *  AKB62 - Acorn A4 2MB FD
 *  AKB64 - Acorn A4 4MB HD 60
 *  AGB11 - Acorn A3010
 *  AGB22 - Acorn A3020 FD
 *  AGB23 - Acorn A3020 HD 60
 *  AGB33 - Acorn A3020 HD 80
 *  AGC10 - Acorn A4000
 *  AGC20 - Acorn A4000 2MB HD 80
 *  UNX11 - R140 Workstation
 *  UNX22 - R225 Discless Workstation
 *  UNX26 - R260 8MB Workstation
 *
 *  Supplied IDE Hard Drives:
 *   60MB Connor CP2064
 *   85MB Conner CP30084E
 *   99MB Fujitsu M2616ET
 *  105MB Connor CFS105A
 *  210MB Connor CFS210A
 *
 * Notes:
 * - Hold DEL down during boot reset the CMOS memory to the default values.
 * - default NVRAM is plainly wrong. Use the status/configure commands to set up properly
 *   (Scroll Lock is currently mapped with Right SHIFT, use this to move to next page of status).
 *   In order to load a floppy, you need at very least:
 *   - configure floppies 1
 *   - configure filesystem adfs
 *   - configure monitortype 12
 *   Then reboot / reset the machine, and use cat to (attempt) to load a floppy contents.
 *
 *  TODO:
 *  - RISC OS Draw app uses unimplemented copro instructions
 *  - Add ABORT line support to the ARM core.
 *  - HD63463 Hard disc controller.
 *
=======================================================================================
 *
 *      Memory map (from http://b-em.bbcmicro.com/arculator/archdocs.txt)
 *
 *  0000000 - 1FFFFFF - logical RAM (32 meg)
 *  2000000 - 2FFFFFF - physical RAM (supervisor only - max 16MB - requires quad MEMCs)
 *  3000000 - 33FFFFF - IOC (IO controllers - supervisor only)
 *  3310000 - FDC - WD1772
 *  33A0000 - Econet - 6854
 *  33B0000 - Serial - 6551
 *  3240000 - 33FFFFF - internal expansion cards
 *  32D0000 - hard disc controller (not IDE) - HD63463
 *  3350010 - printer
 *  3350018 - latch A
 *  3350040 - latch B
 *  3270000 - external expansion cards
 *
 *  3400000 - 3FFFFFF - ROM (read - 12 meg - Arthur and RiscOS 2 512k, RiscOS 3 2MB)
 *  3400000 - 37FFFFF - Low ROM  (4 meg, I think this is expansion ROMs)
 *  3800000 - 3FFFFFF - High ROM (main OS ROM)
 *
 *  3400000 - 35FFFFF - VICD10 (write - supervisor only)
 *  3600000 - 3FFFFFF - MEMC (write - supervisor only)
 *
=======================================================================================
 *
 *  Archimedes IOC interrupts:
 *     IL0    Podule FIQ
 *     IL1    Sound Empty
 *     IL2    Serial
 *     IL3    HDD
 *     IL4    Disc Change
 *     IL5    Podule IRQ
 *     IL6    Printer Busy
 *     IL7    Serial Ring
 *     IF     Printer Ack
 *     IR     VBL
 *     POR    Reset
 *     FH0    Floppy DRQ
 *     FH1    Floppy IRQ
 *     FL     Econet
 *
 *****************************************************************************/
/*
    DASM of code (bios 2 / RISC OS 2)
    0x380d4e0: MEMC: control to 0x10c (page size 32 kbytes, DRAM ram refresh only during flyback)
    0x380d4f0: VIDC: params (screen + sound frequency)
    0x380d51c: IOC: sets control to 0xff, clear IRQA and FIQ masks, sets IRQB mask to 0x80 (keyboard receive full irq)
    0x380d530: IOC: sets timer 0 to 0x4e20, go command
        0x380e0a8: work RAM physical check, max size etc.
    0x380e1f8: IOC: Disables DRAM ram refresh, sets timer 1 to 0x7ffe, go command, then it tests the latch of this timer, enables DRAM refresh
        0x380d00c: Set up default logical space
        0x380d16c: Set up case by case logical space
*/


#include "emu.h"

#include "bus/archimedes/econet/slot.h"
#include "bus/archimedes/podule/slot.h"
#include "bus/centronics/ctronics.h"
#include "bus/centronics/spjoy.h"
#include "bus/econet/econet.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "bus/nscsi/devices.h"
#include "bus/rs232/rs232.h"
#include "cpu/arm/arm.h"
#include "imagedev/floppy.h"
#include "imagedev/harddriv.h"
#include "machine/acorn_bmu.h"
#include "machine/acorn_ioc.h"
#include "machine/acorn_lc.h"
#include "machine/acorn_memc.h"
#include "machine/acorn_vidc.h"
#include "machine/archimedes_keyb.h"
#include "machine/ds2401.h"
//#include "machine/hd63463.h"
#include "machine/i2cmem.h"
#include "machine/mc6854.h"
#include "machine/mos6551.h"
#include "machine/pcf8573.h"
#include "machine/pcf8583.h"
#include "machine/ram.h"
#include "machine/upc82c710.h"
#include "machine/upc82c711.h"
#include "machine/wd_fdc.h"
#include "machine/wd33c9x.h"
#include "machine/z80scc.h"

#include "formats/acorn_dsk.h"
#include "formats/apd_dsk.h"
#include "formats/hxchfe_dsk.h"
#include "formats/jfd_dsk.h"
#include "formats/st_dsk.h"

#include "screen.h"
#include "softlist.h"
#include "speaker.h"

#define LOG_POST (1U << 1)

#define VERBOSE (LOG_POST)
#include "logmacro.h"


namespace {

class aabase_state : public driver_device
{
public:
	aabase_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ioc(*this, "ioc")
		, m_memc(*this, "memc")
		, m_vidc(*this, "vidc")
		, m_exp(*this, "exp")
		, m_podule(*this, "podule%u", 0U)
		, m_ram(*this, RAM_TAG)
		, m_joy(*this, "joy_p%u", 1U)
		, m_selected_floppy(nullptr)
	{ }

	void aabase(machine_config &config);

	void init_r225();
	void init_flop();
	void init_hd();
	void init_scsi();
	void init_ide();
	void init_a4();

protected:
	u32 dram_r(offs_t offset, u32 mem_mask = ~0);
	void dram_w(offs_t offset, u32 data, u32 mem_mask = ~0);

	virtual void machine_start() override ATTR_COLD;

	static void floppy_formats(format_registration &fr);

	void arm_map(address_map &map) ATTR_COLD;
	virtual void memc_map(address_map &map) ATTR_COLD;

	void post_debug(int post_state);

	required_device<arm_cpu_device> m_maincpu;
	required_device<acorn_ioc_device> m_ioc;
	required_device<acorn_memc_device> m_memc;
	required_device<acorn_vidc10_device> m_vidc;
	required_device<archimedes_exp_device> m_exp;
	optional_device_array<archimedes_podule_slot_device, 4> m_podule;
	required_device<ram_device> m_ram;
	optional_ioport_array<2> m_joy;

	floppy_image_device *m_selected_floppy;
	std::unique_ptr<u32[]> m_dram;
};


class aa500_state : public aabase_state
{
public:
	aa500_state(const machine_config &mconfig, device_type type, const char *tag)
		: aabase_state(mconfig, type, tag)
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0U)
		//, m_hdc(*this, "hdc")
		, m_adlc(*this, "adlc")
		, m_centronics(*this, "centronics")
		, m_cent_data_out(*this, "cent_data_out")
		, m_i2cmem(*this, "i2cmem")
		, m_rtc(*this, "rtc")
	{ }

	void aa500(machine_config &config);
	void aa500d(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;

	u32 peripheral5_r(offs_t offset);
	void peripheral5_w(offs_t offset, u32 data);
	void peripheral6_w(offs_t offset, u32 data);

private:
	required_device<fd1793_device> m_fdc;
	optional_device_array<floppy_connector, 2> m_floppy;
	//required_device<hd63463_device> m_hdc;
	required_device<mc6854_device> m_adlc;
	required_device<centronics_device> m_centronics;
	required_device<output_latch_device> m_cent_data_out;
	required_device<i2c_pcf8570_device> m_i2cmem;
	required_device<pcf8573_device> m_rtc;
};


class aa310_state : public aabase_state
{
public:
	aa310_state(const machine_config &mconfig, device_type type, const char *tag)
		: aabase_state(mconfig, type, tag)
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0U)
		//, m_hdc(*this, "hdc")
		, m_centronics(*this, "centronics")
		, m_cent_data_out(*this, "cent_data_out")
	{ }

	void aa305(machine_config &config);
	void aa310(machine_config &config);
	void aa440(machine_config &config);
	void aa4101(machine_config &config);
	void aa4201(machine_config &config);
	void aa4401(machine_config &config);
	void ar140(machine_config &config);
	void aa540(machine_config &config);
	void ar225(machine_config &config);
	void ar260(machine_config &config);
	void aa3000(machine_config &config);
	void av20dev(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;

	u32 peripheral5_r(offs_t offset);
	void peripheral5_w(offs_t offset, u32 data);

private:
	required_device<wd1772_device> m_fdc;
	optional_device_array<floppy_connector, 4> m_floppy;
	//optional_device<hd63463_device> m_hdc;
	required_device<centronics_device> m_centronics;
	required_device<output_latch_device> m_cent_data_out;
};


class aa680_state : public aabase_state
{
public:
	aa680_state(const machine_config &mconfig, device_type type, const char *tag)
		: aabase_state(mconfig, type, tag)
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0U)
		, m_scsi(*this, "scsi:7:wd33c93a")
		, m_centronics(*this, "centronics")
		, m_cent_data_out(*this, "cent_data_out")
		, m_cent_ctrl_out(*this, "cent_ctrl_out")
		, m_cent_status_in(*this, "cent_status_in")
		, m_hex_display(0)
	{ }

	void am4(machine_config &config);
	void aa680(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;

	virtual void memc_map(address_map &map) override ATTR_COLD;

	u32 peripheral5_r(offs_t offset);
	void peripheral5_w(offs_t offset, u32 data);

private:
	required_device<fd1793_device> m_fdc;
	optional_device_array<floppy_connector, 2> m_floppy;
	required_device<wd33c93a_device> m_scsi;
	required_device<centronics_device> m_centronics;
	required_device<output_latch_device> m_cent_data_out;
	required_device<output_latch_device> m_cent_ctrl_out;
	required_device<input_buffer_device> m_cent_status_in;

	u8 m_hex_display;
};


class aa4000_state : public aabase_state
{
public:
	aa4000_state(const machine_config &mconfig, device_type type, const char *tag)
		: aabase_state(mconfig, type, tag)
		, m_i2cmem(*this,"i2cmem")
		, m_floppy(*this, "upc:fdc:%u", 0U)
		, m_ioeb_control(0)
	{ }

	void aa3010(machine_config &config);
	void aa3020(machine_config &config);
	void aa4000(machine_config &config);

	required_device<pcf8583_device> m_i2cmem;

protected:
	virtual void machine_reset() override ATTR_COLD;

	virtual void memc_map(address_map &map) override ATTR_COLD;

	u8 ioeb_r(offs_t offset);
	void ioeb_w(offs_t offset, u8 data);

	optional_device_array<floppy_connector, 4> m_floppy;

	u8 m_ioeb_control;

};


class aa5000_state : public aa4000_state
{
public:
	aa5000_state(const machine_config &mconfig, device_type type, const char *tag)
		: aa4000_state(mconfig, type, tag)
		, m_ext_rom(*this, "extrom")
		, m_ext_region(*this, "extension")
	{ }

	void aa5000(machine_config &config);
	void aa5000a(machine_config &config);

protected:
	virtual void memc_map(address_map &map) override ATTR_COLD;

	u8 extension_r(offs_t offset);

	required_device<generic_slot_device> m_ext_rom;
	required_memory_region m_ext_region;
};

class aa4_state : public aa5000_state
{
public:
	aa4_state(const machine_config &mconfig, device_type type, const char *tag)
		: aa5000_state(mconfig, type, tag)
		, m_lc(*this, "lc")
		, m_bmu(*this, "bmu")
	{ }

	void aa4(machine_config &config);

protected:
	virtual void memc_map(address_map &map) override ATTR_COLD;

private:
	required_device<acorn_lc_device> m_lc;
	required_device<acorn_bmu_device> m_bmu;

};


void aabase_state::init_r225()
{
	u8 *cmos = memregion("i2cmem")->base();

	cmos[0x30] = 0x20; // *Configure Autoboot On
	cmos[0x36] = 0x01; // *Configure Netboot On
	cmos[0x45] = 0x17; // *Configure FileSystem Ram
	cmos[0x50] = 0x90; // *Configure Boot
	cmos[0xb4] = 0x24; // *Configure Romboard 1 1 1024  *Configure Romboard 1 2 1024
	cmos[0xc7] = 0x00; // *Configure Floppies 0
	cmos[0xd0] = 0x08; // *Configure RamFSSize 256K
}

void aabase_state::init_flop()
{
	u8 *cmos = memregion("i2cmem")->base();

	cmos[0xc7] = 0x01; // *Configure Floppies 1
}

void aabase_state::init_hd()
{
	//u8 *cmos = memregion("i2cmem")->base();

	//cmos[0x4b] = 0x54; // *Configure Drive 4
	//cmos[0x50] = 0x90; // *Configure Boot
	//cmos[0xc7] = 0x09; // *Configure HardDiscs 1
}

void aabase_state::init_scsi()
{
	//u8 *cmos = memregion("i2cmem")->base();

	//cmos[0x4b] = 0x54; // *Configure Drive 4
	//cmos[0xc7] = 0x08; // *Configure SCSIFSdiscs 1
}

void aabase_state::init_ide()
{
	u8 *cmos = memregion("i2cmem")->base();

	cmos[0x4b] = 0x54; // *Configure Drive 4
	cmos[0xc7] = 0x41; // *Configure IDEDiscs 1
}

void aabase_state::init_a4()
{
	u8 *cmos = memregion("i2cmem")->base();

	cmos[0x28] = 0x01; // !BatMgr
	cmos[0x4b] = 0x54; // *Configure Drive 4
	cmos[0xc7] = 0x41; // *Configure IDEDiscs 1
}


u32 aa310_state::peripheral5_r(offs_t offset)
{
	switch ((offset << 2) & 0xfc)
	{
	case 0x20: // HD63463
	case 0x24:
		return 0; //m_hdc->read(offset);

	case 0x08: // HD63463 DACK
	case 0x0c:
		return 0; //m_hdc->dma_r();

	case 0x18: // FDC latch B
		return 0x00;

	case 0x40: // FDC latch A
		return 0x00;

	case 0x50: // IOEB not present
		return 0x00;

	case 0x70: // Monitor Type, TBD
		return 0x0f;

	case 0x78: // Joystick (A3010 only)
		return 0xff;
	}

	return 0xffffffff;
}

void aa310_state::peripheral5_w(offs_t offset, u32 data)
{
	switch ((offset << 2) & 0xfc)
	{
	case 0x00:  // HD63463
	case 0x04:
		//m_hdc->write(offset, data);
		break;

	case 0x28:  // HD63463 DACK
	case 0x2c:
		//m_hdc->dma_w(data);
		break;

	case 0x10:  // Printer port
		m_cent_data_out->write(data & 0xff);
		break;

	case 0x18:  // Latch B
				// xxx- ---- Not used
				// ---x ---- Printer Strobe
				// ---- x--- FDC Reset
				// ---- --x- FDC DDEN
		m_fdc->dden_w(BIT(data, 1));
		m_fdc->mr_w(BIT(data, 3));
		m_centronics->write_strobe(BIT(data, 4));
		break;

	case 0x40:  // Latch A
				// x--- ---- Not used
				// -x-- ---- Floppy disc INUSE
				// --x- ---- Floppy motor
				// ---x ---- Side select
				// ---- xxxx Floppy disc select
		m_selected_floppy = nullptr;
		if (!BIT(data, 0)) m_selected_floppy = m_floppy[0]->get_device();
		if (!BIT(data, 1)) m_selected_floppy = m_floppy[1]->get_device();
		if (!BIT(data, 2)) m_selected_floppy = m_floppy[2]->get_device();
		if (!BIT(data, 3)) m_selected_floppy = m_floppy[3]->get_device();

		m_fdc->set_floppy(m_selected_floppy);

		if (m_selected_floppy)
		{
			m_selected_floppy->mon_w(BIT(data, 5));
			m_selected_floppy->ss_w(!BIT(data, 4));
		}

		// Debug code to display RISC OS 3 POST failures
		//if (BIT(data, 1))
		//{
			post_debug(BIT(data, 0));
		//}
		break;
	}
}

u32 aa500_state::peripheral5_r(offs_t offset)
{
	switch ((offset << 2) & 0xfc)
	{
	case 0x20: // HD63463
	case 0x24:
		return 0; //m_hdc->read(offset);

	case 0x08: // HD63463 DACK
	case 0x0c:
		return 0; //m_hdc->dma_r();

	case 0x18: // External Latch B
		return 0x00;
	}

	return 0xffffffff;
}

void aa500_state::peripheral5_w(offs_t offset, u32 data)
{
	switch ((offset << 2) & 0xfc)
	{
	case 0x00:  // HD63463
	case 0x04:
		//m_hdc->write(offset, data);
		break;

	case 0x28:  // HD63463 DACK
	case 0x2c:
		//m_hdc->dma_w(data);
		break;

	case 0x10:  // Printer Data
		m_cent_data_out->write(data & 0xff);
		break;

	case 0x18:  // External Latch B
				// x--- ---- Head Select 3
				// -x-- ---- analogue output Mute
				// --x- ---- analogue input Mute
				// ---x ---- Printer Strobe
				// ---- x--- FDC Controller reset
				// ---- -x-- FDC clock frequency control
				// ---- --xx Data Separator control
		m_fdc->dden_w(BIT(data, 1));
		//m_fdc->set_unscaled_clock(24_MHz_XTAL / (BIT(data, 2) ? 24 : 12));
		m_fdc->mr_w(BIT(data, 3));
		m_centronics->write_strobe(BIT(data, 4));
		break;
	}
}

void aa500_state::peripheral6_w(offs_t offset, u32 data)
{
	switch ((offset << 2) & 0xfc)
	{
	case 0x00:  // External Latch A
				// x--- ---- Disc Eject/Change Reset
				// -x-- ---- In Use control
				// --x- ---- Motor on/off control
				// ---x ---- Side Select
				// ---- xxxx Floppy Disc select
		m_selected_floppy = nullptr;
		if (!BIT(data, 0)) m_selected_floppy = m_floppy[0]->get_device();
		if (!BIT(data, 1)) m_selected_floppy = m_floppy[1]->get_device();
		if (!BIT(data, 2)) m_selected_floppy = nullptr;
		if (!BIT(data, 3)) m_selected_floppy = nullptr;

		m_fdc->set_floppy(m_selected_floppy);

		if (m_selected_floppy)
		{
			m_selected_floppy->mon_w(BIT(data, 5));
			m_selected_floppy->ss_w(!BIT(data, 4));
		}
		break;
	}
}


u32 aa680_state::peripheral5_r(offs_t offset)
{
	switch ((offset << 2) & 0xfc)
	{
	case 0x18:  // Printer Control
				// x--- ---- Select
				// -x-- ---- Printer error
				// --x- ---- Printer reset
				// ---x ---- Busy
				// ---- x--- Acknowledge
				// ---- -x-- Auto-line feed
				// ---- --x- Printer direction
				// ---- ---x Paper error
		return m_cent_status_in->read();
	}

	return 0xffffffff;
}

void aa680_state::peripheral5_w(offs_t offset, u32 data)
{
	switch ((offset << 2) & 0xfc)
	{
	case 0x10: // Printer Data
		m_cent_data_out->write(data & 0xff);
		break;

	case 0x18:  // Printer Control
				// xx-- ---- Reserved write zeros
				// --x- ---- Printer reset
				// ---x ---- Clear busy
				// ---- x--- Acknowledge
				// ---- -x-- Auto-line feed
				// ---- --x- Printer port direction
				// ---- ---x Printer strobe
		m_cent_ctrl_out->write(data ^ 0xff);
		break;

	case 0x40:  // Floppy Latch 1
				// x--- ---- reset
				// -x-- ---- Floppy disc INUSE
				// --x- ---- Floppy motor
				// ---x ---- Side select
				// ---- x--- Density
				// ---- -x-- Not used
				// ---- --xx Floppy disc select
		m_selected_floppy = nullptr;
		if (!BIT(data, 0)) m_selected_floppy = m_floppy[0]->get_device();
		if (!BIT(data, 1)) m_selected_floppy = m_floppy[1]->get_device();

		m_fdc->dden_w(BIT(data, 3));

		m_fdc->set_floppy(m_selected_floppy);

		if (m_selected_floppy)
		{
			m_selected_floppy->mon_w(BIT(data, 5));
			m_selected_floppy->ss_w(!BIT(data, 4));
		}

		m_fdc->mr_w(BIT(data, 7));
		break;
	}
}


u8 aa4000_state::ioeb_r(offs_t offset)
{
	static constexpr struct
	{
		u8 id;
		u8 hs;
	}
	mid[6] =
	{
		{0xe, 0x1}, // Normal
		{0xb, 0x4}, // Multiscan
		{0xe, 0x0}, // VGA
		{0xe, 0x0}, // Super VGA
		{0xf, 0x0}, // Hi-res mono
		{0xf, 0x0}  // LCD
	};

	u8 data = 0xff;
	int hs = 0;

	switch ((offset << 2) & 0xf8)
	{
	case 0x50: // ID register
		data = 0xf5;
		break;

	case 0x58: // Interrupt latch
		logerror("ioeb_r: interrupt latch\n");
		break;

	case 0x70: // MID register (monitor)
		//if (BIT(m_ioeb_control, 2))
		//  hs = !vidc_get_hs();
		//else
		//  hs = vidc_get_hs();

		if (hs)
			data = 0xf0 | mid[1].id | mid[1].hs;
		else
			data = 0xf0 | mid[1].id;
		break;

	case 0x78: // Joystick (A3010 only)
		data = m_joy[offset & 1].read_safe(0xff);
		break;

	default:
		logerror("ioeb_r: unknown %04x\n", offset << 2);
	}

	return data;
}

void aa4000_state::ioeb_w(offs_t offset, u8 data)
{
	switch ((offset << 2) & 0xf8)
	{
	case 0x48: // Control register
		switch (BIT(data, 0, 2))
		{
		case 0:
			m_vidc->set_unscaled_clock(24_MHz_XTAL);
			break;
		case 1:
			m_vidc->set_unscaled_clock(25.175_MHz_XTAL);
			break;
		case 2:
			m_vidc->set_unscaled_clock(36_MHz_XTAL);
			break;
		case 3:
			m_vidc->set_unscaled_clock(24_MHz_XTAL);
			break;
		}
		m_ioeb_control = data & 0x0f;
		break;

	case 0x58: // Interrupt latch
		//logerror("ioeb_w: interrupt latch %02x\n", data);
		break;

	default:
		logerror("ioeb_w: unknown %04x %04x\n", offset << 2, data);
	}
}


u8 aa5000_state::extension_r(offs_t offset)
{
	// test for valid extension rom loaded
	if (m_ext_rom->read_rom(3) == 0x87)
		return m_ext_rom->read_rom(offset & 0xffff);
	else
		return m_ext_region->base()[offset & 0xffff];
}


u32 aabase_state::dram_r(offs_t offset, u32 mem_mask)
{
	return m_dram[offset];
}

void aabase_state::dram_w(offs_t offset, u32 data, u32 mem_mask)
{
	COMBINE_DATA(&m_dram[offset]);
}


void aabase_state::post_debug(int state)
{
	static attotime last_time(0, 0);
	static int bitpos = 0;

	if (state && bitpos <= 32)
	{
		bool bit = (machine().time() - last_time) > m_maincpu->clocks_to_attotime(2000000);

		switch (32 - bitpos)
		{
		// Status bits
		case  0: LOGMASKED(LOG_POST, "00000001  %-4s  Self-test due to power on\n"                , bit ? "Yes" : "No"); break;
		case  1: LOGMASKED(LOG_POST, "00000002  %-4s  Self-test due to interface hardware\n"      , bit ? "Yes" : "No"); break;
		case  2: LOGMASKED(LOG_POST, "00000004  %-4s  Self-test due to test link\n"               , bit ? "Yes" : "No"); break;
		case  3: LOGMASKED(LOG_POST, "00000008  %-4s  Long memory test performed\n"               , bit ? "Yes" : "No"); break;
		case  4: LOGMASKED(LOG_POST, "00000010  %-4s  ARM 3 fitted\n"                             , bit ? "Yes" : "No"); break;
		case  5: LOGMASKED(LOG_POST, "00000020  %-4s  Long memory test disabled\n"                , bit ? "Yes" : "No"); break;
		case  6: LOGMASKED(LOG_POST, "00000040  %-4s  PC-style IO world detected\n"               , bit ? "Yes" : "No"); break;
		case  7: LOGMASKED(LOG_POST, "00000080  %-4s  VRAM detected\n"                            , bit ? "Yes" : "No"); break;

		// Fault bits
		case  8: LOGMASKED(LOG_POST, "00000100  %-4s  CMOS RAM checksum error\n"                  , bit ? "Fail" : "Pass"); break;
		case  9: LOGMASKED(LOG_POST, "00000200  %-4s  ROM failed checksum test\n"                 , bit ? "Fail" : "Pass"); break;
		case 10: LOGMASKED(LOG_POST, "00000400  %-4s  MEMC CAM mapping failed\n"                  , bit ? "Fail" : "Pass"); break;
		case 11: LOGMASKED(LOG_POST, "00000800  %-4s  MEMC protection failed\n"                   , bit ? "Fail" : "Pass"); break;
		case 12: LOGMASKED(LOG_POST, "00001000  %-4s  IOC register test failed\n"                 , bit ? "Fail" : "Pass"); break;
		case 14: LOGMASKED(LOG_POST, "00004000  %-4s  VIDC (Virq interrupt) timing failed\n"      , bit ? "Fail" : "Pass"); break;
		case 15: LOGMASKED(LOG_POST, "00008000  %-4s  Sound (Sirq interrupt) timing failed\n"     , bit ? "Fail" : "Pass"); break;
		case 16: LOGMASKED(LOG_POST, "00010000  %-4s  CMOS RAM (clock/calendar chip) unreadable\n", bit ? "Fail" : "Pass"); break;
		case 17: LOGMASKED(LOG_POST, "00020000  %-4s  RAM control line failure\n"                 , bit ? "Fail" : "Pass"); break;
		case 18: LOGMASKED(LOG_POST, "00040000  %-4s  Long RAM test failure\n"                    , bit ? "Fail" : "Pass"); break;
		}

		bitpos++;
	}

	last_time = machine().time();
}


void aabase_state::machine_start()
{
	// LK14 and LK15 are used to configure the installed RAM, different configurations
	// create different memory mirrors that RISC OS uses to detect the available RAM.
	int dram_size = m_ram->size() / 1024;
	m_dram = nullptr;
	switch (dram_size)
	{
		case 512:   // 512 kB
		case 1024:  // 1 MB
			// for configurations with less than 2 MB it is necessary to use a custom handler in order to emulate the correct memory mirrors.
			m_memc->output_dram_rowcol(true);
			m_memc->space(0).install_readwrite_handler(0x02000000, 0x021fffff, dram_size == 512 ? 0x000ff7ff : 0x001ff7ff, 0x00e00000, 0,
				read32s_delegate(*this, FUNC(aabase_state::dram_r)),
				write32s_delegate(*this, FUNC(aabase_state::dram_w)));

			// for better performance we allocate 2 MB and mask out unused lines in the handlers
			m_dram = std::make_unique<u32[]>(2048 * 1024 / 4);
			save_pointer(NAME(m_dram), 2048 * 1024 / 4);
			break;
		case 2048:  // 2 MB
			m_memc->space(0).install_ram(0x02000000, 0x021fffff, 0x00e00000, m_ram->pointer());
			break;
		case 4096:  // 4 MB
			m_memc->space(0).install_ram(0x02000000, 0x023fffff, 0x00c00000, m_ram->pointer());
			break;
		case 4096 * 2:  // 8 MB
			m_memc->space(0).install_ram(0x02000000, 0x027fffff, 0x00800000, m_ram->pointer());
			break;
		case 4096 * 3:  // 12 MB
			m_memc->space(0).install_ram(0x02000000, 0x02bfffff, 0x00000000, m_ram->pointer());
			break;
		case 4096 * 4:  // 16 MB
			m_memc->space(0).install_ram(0x02000000, 0x02ffffff, 0x00000000, m_ram->pointer());
			break;
		default:
			fatalerror("Archimedes %d kB RAM not supported", dram_size);
			break;
	}
}


void aa500_state::machine_reset()
{
	m_selected_floppy = m_floppy[0]->get_device();
}


void aa310_state::machine_reset()
{
	m_selected_floppy = m_floppy[0]->get_device();
}


void aa680_state::machine_reset()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	// install hexadecimal display
	program.install_read_tap(0x3400000, 0x3420003, "rom_shadow_bank_r", [this](offs_t offset, u32 &data, u32 mem_mask)
		{
			if (!machine().side_effects_disabled())
			{
				switch (offset & 0xfff00)
				{
				case 0x00000:
					if (((offset >> 4) & 0x0f) != m_hex_display)
					{
						m_hex_display = (offset >> 4) & 0x0f;
						logerror("Hexadecimal display: %X\n", m_hex_display);
					}
					break;

				case 0x20000:
					if (m_hex_display != 0x00)
					{
						m_hex_display = 0x00;
						logerror("Hexadecimal display: BLANK\n");
					}
					break;
				}
			}

			// return the original data
			return data;
		});

	m_selected_floppy = m_floppy[0]->get_device();
	m_hex_display = 0x00;
}


void aa4000_state::machine_reset()
{
	m_selected_floppy = m_floppy[0]->get_device();
	m_ioeb_control = 0x00;
}


void aabase_state::arm_map(address_map &map)
{
	map(0x00000000, 0x01ffffff).rw(m_memc, FUNC(acorn_memc_device::logical_r), FUNC(acorn_memc_device::logical_w));
	map(0x02000000, 0x03ffffff).rw(m_memc, FUNC(acorn_memc_device::high_mem_r), FUNC(acorn_memc_device::high_mem_w));
}

void aabase_state::memc_map(address_map &map)
{
	map(0x00000000, 0x01ffffff).rw(m_memc, FUNC(acorn_memc_device::logical_r), FUNC(acorn_memc_device::logical_w));
	map(0x02000000, 0x02ffffff).noprw(); // physical ram installed in machine_start
	map(0x03000000, 0x033fffff).m(m_ioc, FUNC(acorn_ioc_device::map));
	map(0x03000000, 0x0300ffff).rw(m_exp, FUNC(archimedes_exp_device::ms_r), FUNC(archimedes_exp_device::ms_w)).umask32(0x0000ffff);
	map(0x03400000, 0x035fffff).nopr().w(m_vidc, FUNC(acorn_vidc10_device::write));
	map(0x03600000, 0x037fffff).nopr().w(m_memc, FUNC(acorn_memc_device::registers_w));
	map(0x03800000, 0x039fffff).mirror(0x600000).rom().region("maincpu", 0).w(m_memc, FUNC(acorn_memc_device::page_w));
}

void aa680_state::memc_map(address_map &map)
{
	aabase_state::memc_map(map);
	map(0x03100000, 0x0313ffff).ram(); // scsi buffer
	map(0x031e0000, 0x031e0007).rw(m_scsi, FUNC(wd33c93a_device::indir_r), FUNC(wd33c93a_device::indir_w)).umask32(0x000000ff);
	//map(0x031e0200, 0x031e03ff).lw8(NAME([this](u8 data) { m_scsi_ctrl = data; })).umask32(0x000000ff);
	//map(0x031e0400, 0x031e05ff).lw16(NAME([this](u16 data) { m_scsi_addr = data; })).umask32(0x0000ffff);
	map(0x03400000, 0x035fffff).rom().region("maincpu", 0);
}

void aa4000_state::memc_map(address_map &map)
{
	aabase_state::memc_map(map);
	map(0x03010000, 0x03011fff).select(0x00180000).rw("upc", FUNC(upc82c711_device::io_r), FUNC(upc82c711_device::io_w)).umask32(0x0000ffff);
	map(0x03012000, 0x03029fff).select(0x00180000).rw("upc", FUNC(upc82c711_device::dack_tc0_r), FUNC(upc82c711_device::dack_tc0_w)).umask32(0x000000ff);
	map(0x0302a000, 0x0302bfff).select(0x00180000).rw("upc", FUNC(upc82c711_device::dack_tc1_r), FUNC(upc82c711_device::dack_tc1_w)).umask32(0x000000ff);
}

void aa5000_state::memc_map(address_map &map)
{
	aabase_state::memc_map(map);
	map(0x03010000, 0x03011fff).select(0x00180000).rw("upc", FUNC(upc82c710_device::io_r), FUNC(upc82c710_device::io_w)).umask32(0x0000ffff);
	map(0x03012000, 0x03029fff).select(0x00180000).rw("upc", FUNC(upc82c710_device::dack_tc0_r), FUNC(upc82c710_device::dack_tc0_w)).umask32(0x000000ff);
	map(0x0302a000, 0x0302bfff).select(0x00180000).rw("upc", FUNC(upc82c710_device::dack_tc1_r), FUNC(upc82c710_device::dack_tc1_w)).umask32(0x000000ff);
	map(0x03400000, 0x037fffff).r(FUNC(aa5000_state::extension_r)).umask32(0x000000ff); // 5th column ROM
}

void aa4_state::memc_map(address_map &map)
{
	aabase_state::memc_map(map);
	map(0x03010000, 0x03011fff).select(0x00180000).rw("upc", FUNC(upc82c711_device::io_r), FUNC(upc82c711_device::io_w)).umask32(0x0000ffff);
	map(0x03012000, 0x03029fff).select(0x00180000).rw("upc", FUNC(upc82c711_device::dack_tc0_r), FUNC(upc82c711_device::dack_tc0_w)).umask32(0x000000ff);
	map(0x0302a000, 0x0302bfff).select(0x00180000).rw("upc", FUNC(upc82c711_device::dack_tc1_r), FUNC(upc82c711_device::dack_tc1_w)).umask32(0x000000ff);
	map(0x0302c000, 0x0302ffff).select(0x00180000).rw(m_lc, FUNC(acorn_lc_device::read), FUNC(acorn_lc_device::write)).umask32(0x000000ff);
	map(0x03400000, 0x037fffff).r(FUNC(aa4_state::extension_r)).umask32(0x000000ff); // 5th column ROM
}


//static INPUT_PORTS_START( aa4 )
//  PORT_START("MONITOR")
//  PORT_CONFNAME( 0x07, 0x07, "Monitor Type" )
//  PORT_CONFSETTING( 0x02, "Colour SVGA" )
//  PORT_CONFSETTING( 0x05, "Mono VGA" )
//  PORT_CONFSETTING( 0x06, "Colour VGA" )
//  PORT_CONFSETTING( 0x07, "LCD" )
//INPUT_PORTS_END


static INPUT_PORTS_START( aa3010 )
	// A3010 joystick ports
	PORT_START("joy_p1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW,  IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW,  IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW,  IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW,  IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_LOW,  IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x60, IP_ACTIVE_LOW,  IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("joy_p2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW,  IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW,  IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW,  IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW,  IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_LOW,  IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x60, IP_ACTIVE_LOW,  IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


void aabase_state::floppy_formats(format_registration &fr)
{
	fr.add_pc_formats();
	fr.add(FLOPPY_HFE_FORMAT);
	//fr.add(FLOPPY_HFE3_FORMAT);
	// Archimedes formats
	fr.add(FLOPPY_ACORN_ADFS_NEW_FORMAT);
	fr.add(FLOPPY_APD_FORMAT);
	fr.add(FLOPPY_JFD_FORMAT);
	// BBC Micro formats
	fr.add(FLOPPY_ACORN_ADFS_OLD_FORMAT);
	fr.add(FLOPPY_ACORN_SSD_FORMAT);
	fr.add(FLOPPY_ACORN_DSD_FORMAT);
	// Atari ST formats
	fr.add(FLOPPY_ST_FORMAT);
	fr.add(FLOPPY_MSA_FORMAT);
}

static void aa310_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
	device.option_add("35hd", FLOPPY_35_HD);
	device.option_add("525sd", FLOPPY_525_SD);
	device.option_add("525qd", FLOPPY_525_QD);
}


void aabase_state::aabase(machine_config &config)
{
	ARM(config, m_maincpu, 24_MHz_XTAL / 3); // ARM2
	m_maincpu->set_addrmap(AS_PROGRAM, &aabase_state::arm_map);
	m_maincpu->set_copro_type(arm_cpu_device::copro_type::VL86C020);

	ACORN_MEMC(config, m_memc, 24_MHz_XTAL / 3, m_vidc);
	m_memc->set_addrmap(0, &aabase_state::memc_map);
	m_memc->sirq_w().set(m_ioc, FUNC(acorn_ioc_device::il1_w));
	//m_memc->abort_w().set_inputline(m_maincpu, ARM_ABORT_LINE);

	ACORN_IOC(config, m_ioc, 24_MHz_XTAL / 3);
	m_ioc->fiq_w().set_inputline(m_maincpu, ARM_FIRQ_LINE);
	m_ioc->irq_w().set_inputline(m_maincpu, ARM_IRQ_LINE);
	m_ioc->kout_w().set("keyboard", FUNC(archimedes_keyboard_device::kin_w));
	m_ioc->peripheral_r<4>().set(m_exp, FUNC(archimedes_exp_device::ps4_r));
	m_ioc->peripheral_w<4>().set(m_exp, FUNC(archimedes_exp_device::ps4_w));
	m_ioc->peripheral_r<6>().set(m_exp, FUNC(archimedes_exp_device::ps6_r));
	m_ioc->peripheral_w<6>().set(m_exp, FUNC(archimedes_exp_device::ps6_w));

	ARCHIMEDES_KEYBOARD(config, "keyboard").kout().set(m_ioc, FUNC(acorn_ioc_device::kin_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.screen_vblank().set(m_ioc, FUNC(acorn_ioc_device::ir_w));

	ACORN_VIDC1A(config, m_vidc, 24_MHz_XTAL);
	m_vidc->set_screen("screen");
	m_vidc->vblank().set(m_memc, FUNC(acorn_memc_device::vidrq_w));
	m_vidc->sound_drq().set(m_memc, FUNC(acorn_memc_device::sndrq_w));

	RAM(config, m_ram).set_default_size("1M");

	SOFTWARE_LIST(config, "flop_list").set_original("archimedes");
	SOFTWARE_LIST(config, "hdd_list").set_original("archimedes_hdd");
	SOFTWARE_LIST(config, "rom_list").set_original("archimedes_rom").set_filter("ARC");

	ARCHIMEDES_EXPANSION_BUS(config, m_exp, 24_MHz_XTAL / 3);
	m_exp->out_fiq_callback().set(m_ioc, FUNC(acorn_ioc_device::il0_w));
	m_exp->out_irq_callback().set(m_ioc, FUNC(acorn_ioc_device::il5_w));
}

void aa500_state::aa500(machine_config &config)
{
	aabase(config);

	m_ioc->baud_w().set("acia", FUNC(mos6551_device::write_rxc));
	m_ioc->peripheral_r<1>().set([this](offs_t offset) { return m_fdc->read(offset & 0x03); });
	m_ioc->peripheral_w<1>().set([this](offs_t offset, u32 data) { m_fdc->write(offset & 0x03, data & 0xff); });
	m_ioc->peripheral_r<2>().set([this](offs_t offset) { return m_adlc->read(offset & 0x03); });
	m_ioc->peripheral_w<2>().set([this](offs_t offset, u32 data) { m_adlc->write(offset & 0x03, data & 0xff); });
	m_ioc->peripheral_r<3>().set("acia", FUNC(mos6551_device::read));
	m_ioc->peripheral_w<3>().set("acia", FUNC(mos6551_device::write));
	m_ioc->peripheral_r<5>().set(FUNC(aa500_state::peripheral5_r));
	m_ioc->peripheral_w<5>().set(FUNC(aa500_state::peripheral5_w));
	m_ioc->peripheral_r<6>().set_constant(0xff);
	m_ioc->peripheral_w<6>().set(FUNC(aa500_state::peripheral6_w));
	m_ioc->gpio_r<0>().set([this]() { return m_i2cmem->read_sda() & m_rtc->sda_r(); });
	m_ioc->gpio_w<0>().set(m_i2cmem, FUNC(i2c_pcf8570_device::write_sda));
	m_ioc->gpio_w<0>().append(m_rtc, FUNC(pcf8573_device::sda_w));
	m_ioc->gpio_w<1>().set(m_i2cmem, FUNC(i2c_pcf8570_device::write_scl));
	m_ioc->gpio_w<1>().append(m_rtc, FUNC(pcf8573_device::scl_w));
	//m_ioc->gpio_r<2>().set("rtc", FUNC(pcf8573_device::min_r));
	//m_ioc->gpio_r<3>().set("rtc", FUNC(pcf8573_device::sec_r));
	m_ioc->gpio_r<4>().set([this] { return m_selected_floppy ? m_selected_floppy->dskchg_r() : 1; });
	//m_ioc->gpio_w<5>().set([this] (int state) { logerror("%s: Sound Mute %d\n", machine().describe_context(), state); });

	ACORN_VIDC1(config.replace(), m_vidc, 24_MHz_XTAL);
	m_vidc->set_screen("screen");
	m_vidc->vblank().set(m_memc, FUNC(acorn_memc_device::vidrq_w));
	m_vidc->sound_drq().set(m_memc, FUNC(acorn_memc_device::sndrq_w));

	// TODO: implement A500 keyboard, uses M6500/1 MCU

	m_ram->set_default_size("4M");

	// PCF8570 and PCF8573 (pre PCF8583)
	I2C_PCF8570(config, m_i2cmem);
	PCF8573(config, m_rtc, 32.768_kHz_XTAL);

	FD1793(config, m_fdc, 24_MHz_XTAL / 24);
	m_fdc->intrq_wr_callback().set(m_ioc, FUNC(acorn_ioc_device::fh1_w));
	m_fdc->drq_wr_callback().set(m_ioc, FUNC(acorn_ioc_device::fh0_w));
	FLOPPY_CONNECTOR(config, m_floppy[0], aa310_floppies, "35dd", aabase_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], aa310_floppies, nullptr, aabase_state::floppy_formats).enable_sound(true);

	CENTRONICS(config, m_centronics, centronics_devices, nullptr);
	m_centronics->busy_handler().set(m_ioc, FUNC(acorn_ioc_device::il6_w));
	m_centronics->ack_handler().set(m_ioc, FUNC(acorn_ioc_device::if_w));

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	MC6854(config, m_adlc);
	m_adlc->out_txd_cb().set("network", FUNC(econet_device::host_data_w));
	m_adlc->out_irq_cb().set(m_ioc, FUNC(acorn_ioc_device::fl_w));
	//m_adlc->out_rts_cb().

	econet_device &econet(ECONET(config, "network", 0));
	econet.clk_wr_callback().set(m_adlc, FUNC(mc6854_device::txc_w));
	econet.clk_wr_callback().append(m_adlc, FUNC(mc6854_device::rxc_w));
	econet.data_wr_callback().set(m_adlc, FUNC(mc6854_device::set_rx));

	mos6551_device &acia(MOS6551(config, "acia", 0));
	acia.set_xtal(1.8432_MHz_XTAL);
	acia.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	acia.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	acia.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	acia.irq_handler().set(m_ioc, FUNC(acorn_ioc_device::il2_w));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set("acia", FUNC(mos6551_device::write_rxd));
	rs232.dcd_handler().set("acia", FUNC(mos6551_device::write_dcd));
	rs232.cts_handler().set("acia", FUNC(mos6551_device::write_cts));
	rs232.dsr_handler().set("acia", FUNC(mos6551_device::write_dsr));

	//HD63463(config, m_hdc, 24_MHz_XTAL / 3);
	//m_hdc->usel_callback().set([](u8 data) { return data - 1; });
	//m_hdc->irq_callback().set(m_ioc, FUNC(acorn_ioc_device::il3_w));
	//m_hdc->dreq_callback().set(m_ioc, FUNC(acorn_ioc_device::il4_w));

	//HARDDISK(config, "hdc:0", "st506_hdd"); // 20MB HDD
	//HARDDISK(config, "hdc:1", "st506_hdd");

	// expansion slots - 4-card backplane
	ARCHIMEDES_PODULE_SLOT(config, m_podule[0], m_exp, archimedes_exp_devices, nullptr);
	ARCHIMEDES_PODULE_SLOT(config, m_podule[1], m_exp, archimedes_exp_devices, nullptr);
	ARCHIMEDES_PODULE_SLOT(config, m_podule[2], m_exp, archimedes_exp_devices, nullptr);
	ARCHIMEDES_PODULE_SLOT(config, m_podule[3], m_exp, archimedes_exp_devices, nullptr);
}

void aa500_state::aa500d(machine_config &config)
{
	aa500(config);

	// expansion slots - 4-card backplane
	m_podule[0]->set_default_option(nullptr);     // RGB conversion card
	m_podule[1]->set_default_option(nullptr);     // prototype SCSI interface for Philips VP415
	m_podule[2]->set_default_option(nullptr);
	m_podule[3]->set_default_option(nullptr);
}

void aa310_state::aa310(machine_config &config)
{
	aabase(config);

	m_ioc->baud_w().set("acia", FUNC(mos6551_device::write_rxc));
	m_ioc->peripheral_r<1>().set([this](offs_t offset) { return m_fdc->read(offset & 0x03); });
	m_ioc->peripheral_w<1>().set([this](offs_t offset, u32 data) { m_fdc->write(offset & 0x03, data & 0xff); });
	m_ioc->peripheral_r<2>().set("econet", FUNC(archimedes_econet_slot_device::read));
	m_ioc->peripheral_w<2>().set("econet", FUNC(archimedes_econet_slot_device::write));
	m_ioc->peripheral_r<3>().set("acia", FUNC(mos6551_device::read));
	m_ioc->peripheral_w<3>().set("acia", FUNC(mos6551_device::write));
	m_ioc->peripheral_r<5>().set(FUNC(aa310_state::peripheral5_r));
	m_ioc->peripheral_w<5>().set(FUNC(aa310_state::peripheral5_w));
	m_ioc->gpio_r<0>().set("i2cmem", FUNC(pcf8583_device::sda_r));
	m_ioc->gpio_w<0>().set("i2cmem", FUNC(pcf8583_device::sda_w));
	m_ioc->gpio_w<1>().set("i2cmem", FUNC(pcf8583_device::scl_w));
	m_ioc->gpio_r<2>().set([this] { return m_selected_floppy ? !m_selected_floppy->ready_r() : 0; });
	//m_ioc->gpio_r<3>().set([this] () { logerror("%s: Reserved\n", machine().describe_context()); return 0; });
	//m_ioc->gpio_r<4>().set([this] () { logerror("%s: Aux IO connector\n", machine().describe_context()); return 0; });
	//m_ioc->gpio_w<5>().set([this] (int state) { logerror("%s: Sound Mute %s\n", machine().describe_context(), state); });

	m_ram->set_default_size("1M");

	PCF8583(config, "i2cmem", 32.768_kHz_XTAL);

	WD1772(config, m_fdc, 24_MHz_XTAL / 3);
	m_fdc->set_disable_motor_control(true);
	m_fdc->intrq_wr_callback().set(m_ioc, FUNC(acorn_ioc_device::fh1_w));
	m_fdc->drq_wr_callback().set(m_ioc, FUNC(acorn_ioc_device::fh0_w));
	FLOPPY_CONNECTOR(config, m_floppy[0], aa310_floppies, "35dd", aabase_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], aa310_floppies, nullptr, aabase_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[2], aa310_floppies, nullptr, aabase_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[3], aa310_floppies, nullptr, aabase_state::floppy_formats).enable_sound(true);

	CENTRONICS(config, m_centronics, centronics_devices, nullptr);
	m_centronics->option_add("spjoy", SERIAL_PORT_JOYSTICK);
	m_centronics->busy_handler().set(m_ioc, FUNC(acorn_ioc_device::il6_w));
	m_centronics->ack_handler().set(m_ioc, FUNC(acorn_ioc_device::if_w));

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	mos6551_device &acia(MOS6551(config, "acia", 0));
	acia.set_xtal(1.8432_MHz_XTAL);
	acia.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	acia.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	acia.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	acia.irq_handler().set(m_ioc, FUNC(acorn_ioc_device::il2_w));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set("acia", FUNC(mos6551_device::write_rxd));
	rs232.dcd_handler().set("acia", FUNC(mos6551_device::write_dcd));
	rs232.cts_handler().set("acia", FUNC(mos6551_device::write_cts));
	rs232.dsr_handler().set("acia", FUNC(mos6551_device::write_dsr));

	// expansion slots - 2-card backplane
	ARCHIMEDES_PODULE_SLOT(config, m_podule[0], m_exp, archimedes_exp_devices, nullptr);
	ARCHIMEDES_PODULE_SLOT(config, m_podule[2], m_exp, archimedes_exp_devices, nullptr);

	archimedes_econet_slot_device &econet(ARCHIMEDES_ECONET_SLOT(config, "econet", archimedes_econet_devices, nullptr));
	econet.efiq_handler().set(m_ioc, FUNC(acorn_ioc_device::fl_w));
}

void aa310_state::aa305(machine_config &config)
{
	aa310(config);
	m_ram->set_default_size("512K").set_extra_options("1M");
}

void aa310_state::aa440(machine_config &config)
{
	aa310(config);
	m_ram->set_default_size("4M");

	//HD63463(config, m_hdc, 24_MHz_XTAL / 3);
	//m_hdc->usel_callback().set([](u8 data) { return data - 1; });
	//m_hdc->irq_callback().set(m_ioc, FUNC(acorn_ioc_device::il3_w));
	//m_hdc->dreq_callback().set(m_ioc, FUNC(acorn_ioc_device::il4_w));

	//HARDDISK(config, "hdc:0", "st506_hdd"); // 20MB HDD
	//HARDDISK(config, "hdc:1", "st506_hdd");

	// expansion slots - 4-card backplane
	ARCHIMEDES_PODULE_SLOT(config, m_podule[1], m_exp, archimedes_exp_devices, nullptr);
	ARCHIMEDES_PODULE_SLOT(config, m_podule[3], m_exp, archimedes_exp_devices, nullptr);
}

void aa680_state::am4(machine_config &config)
{
	aa680(config);

	// expansion slots - 4-card backplane
	m_podule[0]->set_default_option("tube"); // Tube podule to load bootstrap from BBC Micro
	m_podule[1]->set_default_option(nullptr);
	m_podule[2]->set_default_option(nullptr);
	m_podule[3]->set_default_option(nullptr);
}

void aa680_state::aa680(machine_config &config)
{
	aabase(config);

	m_ioc->baud_w().set("scc", FUNC(scc8530_device::txca_w));
	m_ioc->baud_w().append("scc", FUNC(scc8530_device::txcb_w));
	m_ioc->peripheral_r<1>().set([this](offs_t offset) { return m_fdc->read(offset & 0x03); });
	m_ioc->peripheral_w<1>().set([this](offs_t offset, u32 data) { m_fdc->write(offset & 0x03, data & 0xff); });
	m_ioc->peripheral_r<2>().set_constant(0xff);
	m_ioc->peripheral_w<2>().set_nop();
	m_ioc->peripheral_r<3>().set("scc", FUNC(scc8530_device::ab_dc_r));
	m_ioc->peripheral_w<3>().set("scc", FUNC(scc8530_device::ab_dc_w));
	m_ioc->peripheral_r<5>().set(FUNC(aa680_state::peripheral5_r));
	m_ioc->peripheral_w<5>().set(FUNC(aa680_state::peripheral5_w));
	m_ioc->gpio_r<0>().set("i2cmem", FUNC(pcf8583_device::sda_r));
	m_ioc->gpio_w<0>().set("i2cmem", FUNC(pcf8583_device::sda_w));
	m_ioc->gpio_w<1>().set("i2cmem", FUNC(pcf8583_device::scl_w));
	m_ioc->gpio_r<2>().set([this] { return m_selected_floppy ? !m_selected_floppy->ready_r() : 0; });

	m_ram->set_default_size("8M");

	PCF8583(config, "i2cmem", 32.768_kHz_XTAL);

	FD1793(config, m_fdc, 96_MHz_XTAL / 96);
	m_fdc->intrq_wr_callback().set(m_ioc, FUNC(acorn_ioc_device::il4_w));
	m_fdc->drq_wr_callback().set(m_ioc, FUNC(acorn_ioc_device::fh0_w));
	FLOPPY_CONNECTOR(config, m_floppy[0], aa310_floppies, "35dd", aabase_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], aa310_floppies, nullptr, aabase_state::floppy_formats).enable_sound(true);

	CENTRONICS(config, m_centronics, centronics_devices, nullptr);
	m_centronics->perror_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit0));
	m_centronics->ack_handler().set(m_ioc, FUNC(acorn_ioc_device::if_w));
	m_centronics->ack_handler().append(m_cent_status_in, FUNC(input_buffer_device::write_bit3));
	m_centronics->busy_handler().set(m_ioc, FUNC(acorn_ioc_device::il6_w));
	m_centronics->busy_handler().append(m_cent_status_in, FUNC(input_buffer_device::write_bit4));
	m_centronics->fault_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit6));
	m_centronics->select_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit7));

	INPUT_BUFFER(config, m_cent_status_in);

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	OUTPUT_LATCH(config, m_cent_ctrl_out);
	m_cent_ctrl_out->bit_handler<0>().set(m_centronics, FUNC(centronics_device::write_strobe));
	m_cent_ctrl_out->bit_handler<1>().set(m_centronics, FUNC(centronics_device::write_select_in));
	m_cent_ctrl_out->bit_handler<1>().append(m_cent_status_in, FUNC(input_buffer_device::write_bit1));
	m_cent_ctrl_out->bit_handler<2>().set(m_centronics, FUNC(centronics_device::write_autofd));
	m_cent_ctrl_out->bit_handler<2>().append(m_cent_status_in, FUNC(input_buffer_device::write_bit2));
	m_cent_ctrl_out->bit_handler<3>().set(m_centronics, FUNC(centronics_device::write_ack));
	m_cent_ctrl_out->bit_handler<4>().set(m_centronics, FUNC(centronics_device::write_busy));

	scc8530_device &scc(SCC8530(config, "scc", 3.6864_MHz_XTAL));
	scc.out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
	scc.out_dtra_callback().set("rs232a", FUNC(rs232_port_device::write_dtr));
	scc.out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts));
	scc.out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
	scc.out_dtrb_callback().set("rs232b", FUNC(rs232_port_device::write_dtr));
	scc.out_rtsb_callback().set("rs232b", FUNC(rs232_port_device::write_rts));
	scc.out_int_callback().set(m_ioc, FUNC(acorn_ioc_device::il2_w));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set("scc", FUNC(scc8530_device::rxa_w));
	rs232a.dcd_handler().set("scc", FUNC(scc8530_device::dcda_w));
	rs232a.cts_handler().set("scc", FUNC(scc8530_device::ctsa_w));
	rs232a.dsr_handler().set("scc", FUNC(scc8530_device::synca_w));
	rs232a.ri_handler().set(m_ioc, FUNC(acorn_ioc_device::il7_w));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set("scc", FUNC(scc8530_device::rxb_w));
	rs232b.dcd_handler().set("scc", FUNC(scc8530_device::dcdb_w));
	rs232b.cts_handler().set("scc", FUNC(scc8530_device::ctsb_w));
	rs232b.dsr_handler().set("scc", FUNC(scc8530_device::syncb_w));

	// scsi 70MB HDD
	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", default_scsi_devices, "harddisk", true); // Internal Hard Disc Drive
	NSCSI_CONNECTOR(config, "scsi:1", default_scsi_devices, nullptr, false);   // External Hard Disc Drive #1
	NSCSI_CONNECTOR(config, "scsi:2", default_scsi_devices, nullptr, false);   // External Hard Disc Drive #2
	NSCSI_CONNECTOR(config, "scsi:3", default_scsi_devices, nullptr, false);   // External Hard Disc Drive #3
	NSCSI_CONNECTOR(config, "scsi:4", default_scsi_devices, nullptr, false);   // External Hard Disc Drive #4 / Streamer #2
	NSCSI_CONNECTOR(config, "scsi:5", default_scsi_devices, nullptr, false);   // Streamer #1
	NSCSI_CONNECTOR(config, "scsi:6", default_scsi_devices, nullptr, false);   // Processor / Printer Device
	NSCSI_CONNECTOR(config, "scsi:7").option_set("wd33c93a", WD33C93A).clock(96_MHz_XTAL / 12)
		.machine_config([this](device_t *device)
		{
			wd33c93a_device &wd33c93(downcast<wd33c93a_device &>(*device));
			wd33c93.irq_cb().set(m_ioc, FUNC(acorn_ioc_device::il3_w));
			//wd33c93.drq_cb().set(*this, FUNC(aa680_state::scsi_drq));
		});

	// expansion slots - 4-card backplane - pre-installed podules would make it a Technical Publishing System
	ARCHIMEDES_PODULE_SLOT(config, m_podule[0], m_exp, archimedes_exp_devices, nullptr);
	ARCHIMEDES_PODULE_SLOT(config, m_podule[1], m_exp, archimedes_exp_devices, nullptr);  // Acorn AKA30 SCSI
	ARCHIMEDES_PODULE_SLOT(config, m_podule[2], m_exp, archimedes_exp_devices, nullptr);  // Acorn Laser Printer
	ARCHIMEDES_PODULE_SLOT(config, m_podule[3], m_exp, archimedes_exp_devices, "ether1"); // Acorn Lance Ethernet
}

void aa310_state::aa3000(machine_config &config)
{
	aa310(config);

	m_ram->set_default_size("1M").set_extra_options("2M");

	// expansion slots - mini
	ARCHIMEDES_PODULE_SLOT(config, m_podule[1], m_exp, archimedes_mini_exp_devices, nullptr);
	config.device_remove("podule2");
}

void aa310_state::aa4101(machine_config &config)
{
	aa310(config);
	m_ram->set_default_size("1M").set_extra_options("2M,4M");

	//HD63463(config, m_hdc, 24_MHz_XTAL / 3);
	//m_hdc->usel_callback().set([](u8 data) { return data - 1; });
	//m_hdc->irq_callback().set(m_ioc, FUNC(acorn_ioc_device::il3_w));
	//m_hdc->dreq_callback().set(m_ioc, FUNC(acorn_ioc_device::il3_w));

	//HARDDISK(config, "hdc:0", "st506_hdd");
	//HARDDISK(config, "hdc:1", "st506_hdd");

	// expansion slots - 4-card backplane
	ARCHIMEDES_PODULE_SLOT(config, m_podule[1], m_exp, archimedes_exp_devices, nullptr);
	ARCHIMEDES_PODULE_SLOT(config, m_podule[3], m_exp, archimedes_exp_devices, nullptr);
}

void aa310_state::aa4201(machine_config &config)
{
	aa4101(config);
	m_ram->set_default_size("2M").set_extra_options("4M");
}

void aa310_state::aa4401(machine_config &config)
{
	aa4201(config);
	m_ram->set_default_size("4M").set_extra_options("8M");
}

void aa310_state::ar140(machine_config &config)
{
	aa440(config);

	m_ram->set_default_size("4M").set_extra_options("8M");

	// expansion slots - 4-card backplane
	m_podule[0]->set_default_option("ether1"); // Acorn AKA25 Ethernet
	m_podule[1]->set_default_option(nullptr);
	m_podule[2]->set_default_option(nullptr);
	m_podule[3]->set_default_option(nullptr);
}

void aa310_state::aa540(machine_config &config)
{
	aa310(config);

	m_maincpu->set_clock(52_MHz_XTAL / 2); // ARM3
	m_maincpu->set_copro_type(arm_cpu_device::copro_type::VL86C020);

	m_ram->set_default_size("16M").set_extra_options("8M,16M");

	// expansion slots - 4-card backplane
	ARCHIMEDES_PODULE_SLOT(config, m_podule[1], m_exp, archimedes_exp_devices, nullptr); // Acorn AKA31 SCSI 100MB HDD
	ARCHIMEDES_PODULE_SLOT(config, m_podule[3], m_exp, archimedes_exp_devices, nullptr);
}

void aa310_state::ar225(machine_config &config)
{
	aa540(config);

	m_ram->set_default_size("4M").set_extra_options("8M,16M");

	// discless
	m_floppy[0]->set_default_option(nullptr);
	m_floppy[1]->set_default_option(nullptr);

	// expansion slots - 4-card backplane
	m_podule[0]->set_default_option("ether1");       // Acorn AKA25 Ethernet
	m_podule[1]->set_default_option("rom_r225boot"); // Acorn AKA05 ROM (with DiscLess Bootstrap support)
	m_podule[2]->set_default_option(nullptr);
	m_podule[3]->set_default_option(nullptr);
}

void aa310_state::ar260(machine_config &config)
{
	aa540(config);

	m_ram->set_default_size("8M").set_extra_options("16M");

	// expansion slots - 4-card backplane
	m_podule[0]->set_default_option("ether1");     // Acorn AKA25 Ethernet
	m_podule[1]->set_default_option(nullptr);      // Acorn AKA31 SCSI
	m_podule[2]->set_default_option(nullptr);
	m_podule[3]->set_default_option(nullptr);
}

void aa310_state::av20dev(machine_config &config)
{
	// This is an internal development machine based on a R225/A540, used for testing
	// the new VIDC20 video controller.
	aa540(config);

	ARM_VIDC20(config.replace(), m_vidc, 24_MHz_XTAL);
	m_vidc->set_screen("screen");
	m_vidc->vblank().set(m_memc, FUNC(acorn_memc_device::vidrq_w));
	m_vidc->sound_drq().set(m_memc, FUNC(acorn_memc_device::sndrq_w));

	m_ram->set_default_size("4M").set_extra_options("8M,16M");

	// expansion slots - 4-card backplane
	m_podule[0]->set_default_option(nullptr);
	m_podule[1]->set_default_option(nullptr);
	m_podule[2]->set_default_option(nullptr);
	m_podule[3]->set_default_option(nullptr);
}

void aa4000_state::aa3010(machine_config &config)
{
	aabase(config);

	m_maincpu->set_clock(72_MHz_XTAL / 6); // ARM250
	m_maincpu->set_copro_type(arm_cpu_device::copro_type::VL86C020);

	m_ioc->baud_w().set("upc:serial1", FUNC(ns16450_device::clock_w));
	m_ioc->peripheral_r<1>().set([this] () { logerror("%s: IOC: Peripheral Select 1 R\n", machine().describe_context()); return 0; });
	m_ioc->peripheral_w<1>().set([this] (int state) { logerror("%s: IOC: Peripheral Select 1 W %d\n", machine().describe_context(), state); });
	m_ioc->peripheral_r<2>().set("econet", FUNC(archimedes_econet_slot_device::read));
	m_ioc->peripheral_w<2>().set("econet", FUNC(archimedes_econet_slot_device::write));
	m_ioc->peripheral_r<3>().set([this] () { logerror("%s: IOC: Peripheral Select 3 R\n", machine().describe_context()); return 0; });
	m_ioc->peripheral_w<3>().set([this] (int state) { logerror("%s: IOC: Peripheral Select 3 W %d\n", machine().describe_context(), state); });
	m_ioc->peripheral_r<5>().set(FUNC(aa4000_state::ioeb_r));
	m_ioc->peripheral_w<5>().set(FUNC(aa4000_state::ioeb_w));
	m_ioc->gpio_r<0>().set(m_i2cmem, FUNC(pcf8583_device::sda_r));
	m_ioc->gpio_w<0>().set(m_i2cmem, FUNC(pcf8583_device::sda_w));
	m_ioc->gpio_w<1>().set(m_i2cmem, FUNC(pcf8583_device::scl_w));
	m_ioc->gpio_r<2>().set_constant(1); // FDHden
	m_ioc->gpio_r<3>().set("idrom", FUNC(ds2401_device::read));
	m_ioc->gpio_w<3>().set("idrom", FUNC(ds2401_device::write));
	m_ioc->gpio_r<4>().set_constant(1); // Sintr
	//m_ioc->gpio_w<5>().set([this] (int state) { logerror("%s: Sound Mute %d\n", machine().describe_context(), state); });

	m_ram->set_default_size("1M").set_extra_options("2M");

	PCF8583(config, m_i2cmem, 32.768_kHz_XTAL);

	DS2401(config, "idrom", 0); // DS2400

	upc82c711_device &upc(UPC82C711(config, "upc", 24_MHz_XTAL));
	upc.irq4().set(m_ioc, FUNC(acorn_ioc_device::il2_w));
	upc.fintr().set(m_ioc, FUNC(acorn_ioc_device::il4_w));
	upc.pintr().set(m_ioc, FUNC(acorn_ioc_device::il6_w));
	upc.fdrq().set(m_ioc, FUNC(acorn_ioc_device::fh0_w));
	upc.txd1().set("rs232", FUNC(rs232_port_device::write_txd));
	upc.rts1().set("rs232", FUNC(rs232_port_device::write_rts));
	upc.dtr1().set("rs232", FUNC(rs232_port_device::write_dtr));

	FLOPPY_CONNECTOR(config, m_floppy[0], aa310_floppies, "35hd", aabase_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], aa310_floppies, nullptr, aabase_state::floppy_formats).enable_sound(true);

	subdevice<upd765_family_device>("upc:fdc")->idx_wr_callback().set(m_ioc, FUNC(acorn_ioc_device::if_w));
	subdevice<ata_interface_device>("upc:ide")->irq_handler().set(m_ioc, FUNC(acorn_ioc_device::il3_w));
	subdevice<centronics_device>("upc:parallel:centronics")->option_add("spjoy", SERIAL_PORT_JOYSTICK);

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set("upc:serial1", FUNC(ns16450_device::rx_w));
	rs232.cts_handler().set("upc:serial1", FUNC(ns16450_device::cts_w));
	rs232.dsr_handler().set("upc:serial1", FUNC(ns16450_device::dsr_w));
	rs232.dcd_handler().set("upc:serial1", FUNC(ns16450_device::dcd_w));
	rs232.ri_handler().set("upc:serial1", FUNC(ns16450_device::ri_w));

	// expansion slots - mini
	ARCHIMEDES_PODULE_SLOT(config, m_podule[1], m_exp, archimedes_mini_exp_devices, nullptr);

	archimedes_econet_slot_device &econet(ARCHIMEDES_ECONET_SLOT(config, "econet", archimedes_econet_devices, nullptr));
	econet.efiq_handler().set(m_ioc, FUNC(acorn_ioc_device::fl_w));
}

void aa4000_state::aa3020(machine_config &config)
{
	aa3010(config);
	m_maincpu->set_clock(72_MHz_XTAL / 6); // ARM250

	m_ram->set_default_size("2M").set_extra_options("4M");

	// ide 60MB HDD
	subdevice<ata_interface_device>("upc:ide")->options(ata_devices, "hdd", nullptr, false);
}

void aa4000_state::aa4000(machine_config &config)
{
	aa3010(config);
	m_maincpu->set_clock(72_MHz_XTAL / 6); // ARM250

	m_ram->set_default_size("2M").set_extra_options("4M");

	// ide 80MB HDD
	subdevice<ata_interface_device>("upc:ide")->options(ata_devices, "hdd", nullptr, false);
}

void aa5000_state::aa5000(machine_config &config)
{
	aabase(config);

	m_maincpu->set_clock(50_MHz_XTAL / 2); // ARM3
	m_maincpu->set_copro_type(arm_cpu_device::copro_type::VL86C020);

	m_ioc->baud_w().set("upc:serial", FUNC(ns16450_device::clock_w));
	m_ioc->peripheral_r<1>().set([this] () { logerror("%s: IOC: Peripheral Select 1 R\n", machine().describe_context()); return 0; });
	m_ioc->peripheral_w<1>().set([this] (int state) { logerror("%s: IOC: Peripheral Select 1 W %d\n", machine().describe_context(), state); });
	m_ioc->peripheral_r<2>().set("econet", FUNC(archimedes_econet_slot_device::read));
	m_ioc->peripheral_w<2>().set("econet", FUNC(archimedes_econet_slot_device::write));
	m_ioc->peripheral_r<3>().set([this] () { logerror("%s: IOC: Peripheral Select 3 R\n", machine().describe_context()); return 0; });
	m_ioc->peripheral_w<3>().set([this] (int state) { logerror("%s: IOC: Peripheral Select 3 W %d\n", machine().describe_context(), state); });
	m_ioc->peripheral_r<5>().set(FUNC(aa5000_state::ioeb_r));
	m_ioc->peripheral_w<5>().set(FUNC(aa5000_state::ioeb_w));
	m_ioc->gpio_r<0>().set("i2cmem", FUNC(pcf8583_device::sda_r));
	m_ioc->gpio_w<0>().set("i2cmem", FUNC(pcf8583_device::sda_w));
	m_ioc->gpio_w<1>().set("i2cmem", FUNC(pcf8583_device::scl_w));
	m_ioc->gpio_r<2>().set_constant(1); // FDHden
	m_ioc->gpio_r<3>().set("idrom", FUNC(ds2401_device::read));
	m_ioc->gpio_w<3>().set("idrom", FUNC(ds2401_device::write));
	m_ioc->gpio_r<4>().set_constant(1); // Sintr
	//m_ioc->gpio_w<5>().set([this] (int state) { logerror("%s: Sound Mute %d\n", machine().describe_context(), state); });

	m_ram->set_default_size("2M").set_extra_options("4M");

	PCF8583(config, "i2cmem", 32.768_kHz_XTAL);

	DS2401(config, "idrom", 0); // DS2400

	upc82c710_device &upc(UPC82C710(config, "upc", 24_MHz_XTAL));
	upc.sintr().set(m_ioc, FUNC(acorn_ioc_device::il2_w));
	//upc.index().set(m_ioc, FUNC(acorn_ioc_device::if_w));
	upc.fintr().set(m_ioc, FUNC(acorn_ioc_device::il4_w));
	upc.pintr().set(m_ioc, FUNC(acorn_ioc_device::il6_w));
	upc.fdrq().set(m_ioc, FUNC(acorn_ioc_device::fh0_w));
	upc.txd().set("rs232", FUNC(rs232_port_device::write_txd));
	upc.rts().set("rs232", FUNC(rs232_port_device::write_rts));
	upc.dtr().set("rs232", FUNC(rs232_port_device::write_dtr));

	FLOPPY_CONNECTOR(config, m_floppy[0], aa310_floppies, "35hd", aabase_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], aa310_floppies, nullptr, aabase_state::floppy_formats).enable_sound(true);

	subdevice<upd765_family_device>("upc:fdc")->idx_wr_callback().set(m_ioc, FUNC(acorn_ioc_device::if_w));
	subdevice<ata_interface_device>("upc:ide")->irq_handler().set(m_ioc, FUNC(acorn_ioc_device::il3_w));
	subdevice<centronics_device>("upc:parallel:centronics")->option_add("spjoy", SERIAL_PORT_JOYSTICK);

	// ide 80MB HDD
	subdevice<ata_interface_device>("upc:ide")->options(ata_devices, "hdd", nullptr, false);

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set("upc:serial", FUNC(ns16450_device::rx_w));
	rs232.cts_handler().set("upc:serial", FUNC(ns16450_device::cts_w));
	rs232.dsr_handler().set("upc:serial", FUNC(ns16450_device::dsr_w));
	rs232.dcd_handler().set("upc:serial", FUNC(ns16450_device::dcd_w));
	rs232.ri_handler().set("upc:serial", FUNC(ns16450_device::ri_w));

	// expansion slots - 4-card backplane
	ARCHIMEDES_PODULE_SLOT(config, m_podule[0], m_exp, archimedes_exp_devices, nullptr);
	ARCHIMEDES_PODULE_SLOT(config, m_podule[1], m_exp, archimedes_exp_devices, nullptr);
	ARCHIMEDES_PODULE_SLOT(config, m_podule[2], m_exp, archimedes_exp_devices, nullptr);
	ARCHIMEDES_PODULE_SLOT(config, m_podule[3], m_exp, archimedes_exp_devices, nullptr);

	archimedes_econet_slot_device &econet(ARCHIMEDES_ECONET_SLOT(config, "econet", archimedes_econet_devices, nullptr));
	econet.efiq_handler().set(m_ioc, FUNC(acorn_ioc_device::fl_w));

	// extension rom
	GENERIC_SOCKET(config, m_ext_rom, generic_linear_slot, "ext_rom", "bin,rom");
	subdevice<software_list_device>("rom_list")->set_filter("A5000,ARC");
}

void aa4_state::aa4(machine_config &config)
{
	aa3010(config);

	m_maincpu->set_clock(24_MHz_XTAL); // ARM3
	m_maincpu->set_copro_type(arm_cpu_device::copro_type::VL86C020);

	ACORN_BMU(config, m_bmu, 4.194304_MHz_XTAL);
	//bmu.battlo_callback().set(m_ioc, FUNC(acorn_ioc_device::il7_w));

	//m_ioc->peripheral_r<2>().set("lc", FUNC(lc_device::read));
	//m_ioc->peripheral_w<2>().set("lc", FUNC(lc_device::write));
	m_ioc->gpio_r<0>().set([this]() { return m_i2cmem->sda_r() & m_bmu->sda_r(); });
	m_ioc->gpio_w<0>().append(m_bmu, FUNC(acorn_bmu_device::sda_w));
	m_ioc->gpio_w<1>().append(m_bmu, FUNC(acorn_bmu_device::scl_w));

	// video hardware
	screen_device &screen(SCREEN(config.replace(), "screen", SCREEN_TYPE_LCD));
	screen.screen_vblank().set(m_ioc, FUNC(acorn_ioc_device::ir_w));

	ACORN_LC(config, m_lc, 24_MHz_XTAL);

	//ACORN_VIDC1A_LCD(config.replace(), m_vidc, 24_MHz_XTAL);
	//m_vidc->set_screen("screen");
	//m_vidc->vblank().set(m_memc, FUNC(acorn_memc_device::vidrq_w));
	//m_vidc->sound_drq().set(m_memc, FUNC(acorn_memc_device::sndrq_w));

	m_ram->set_default_size("2M").set_extra_options("4M");

	// ide 60MB HDD
	subdevice<ata_interface_device>("upc:ide")->options(ata_devices, "hdd", nullptr, false);

	// expansion slots - none
	config.device_remove("podule1");

	// extension rom
	GENERIC_SOCKET(config, m_ext_rom, generic_linear_slot, "ext_rom", "bin,rom");
	subdevice<software_list_device>("rom_list")->set_filter("A4,ARC");
}

void aa5000_state::aa5000a(machine_config &config)
{
	aa5000(config);
	m_maincpu->set_clock(33_MHz_XTAL); // ARM3
}


ROM_START( aa500 )
	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "sn104", "#104: RISC OS 2.00 (12 Oct 1988)" ) // serial #41 has same ROMs
	ROMX_LOAD( "a500_riscos206_0.ic24", 0x000000, 0x20000, CRC(60910286) SHA1(9bede102207d45dda07b4282a4cc4b4d2212704a), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "a500_riscos206_1.ic25", 0x000001, 0x20000, CRC(3e1aaa54) SHA1(c648c691e083117f9bb2459e4675401824a851b0), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "a500_riscos206_2.ic26", 0x000002, 0x20000, CRC(3ae4e522) SHA1(030c6b2c0796655ad46732ce230b6f811f224684), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "a500_riscos206_3.ic27", 0x000003, 0x20000, CRC(8b60c990) SHA1(976f2b24913866abef9c6751591c2355ab472f87), ROM_BIOS(0) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 1, "sn46", "#46: RISC OS 2.00 (15 Sep 1988)" ) // ex Acorn, Paul Fellows' development machine - missing HDD image to soft load modules
	ROMX_LOAD( "a500_riscos200_0.ic24", 0x000000, 0x20000, CRC(9afa7fca) SHA1(a52f03e2ac3dd594fe0979c46d06601df5f690e7), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "a500_riscos200_1.ic25", 0x000001, 0x20000, CRC(4c0bd304) SHA1(36fa1c7c3a634494912581b6a71e7dd4ccdfc514), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "a500_riscos200_2.ic26", 0x000002, 0x20000, CRC(e47e98d4) SHA1(8430e0d9a4402c1a76c7c6d57826d914be49f130), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "a500_riscos200_3.ic27", 0x000003, 0x20000, CRC(e2c7d11d) SHA1(fa2d763566f7166009c04166fe6d330644201014), ROM_BIOS(1) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 2, "sn47", "#47: RISC OS 3.10 (30 Apr 1992)" )
	ROMX_LOAD( "a500_ros310_rom0.ic24", 0x000000, 0x80000, CRC(ad8e0873) SHA1(69a55e024a9f1f663b1bc0664d4d1631ed3899ee), ROM_BIOS(2) | ROM_SKIP(3) )
	ROMX_LOAD( "a500_ros310_rom1.ic25", 0x000001, 0x80000, CRC(0be31dfc) SHA1(c2a7aa6737931171507950025c3bc1008800e0f7), ROM_BIOS(2) | ROM_SKIP(3) )
	ROMX_LOAD( "a500_ros310_rom2.ic26", 0x000002, 0x80000, CRC(081c5825) SHA1(e42a356bfaa77a81ee9e7e2a8d58ff0f301583c2), ROM_BIOS(2) | ROM_SKIP(3) )
	ROMX_LOAD( "a500_ros310_rom3.ic27", 0x000003, 0x80000, CRC(c9bd793b) SHA1(534428a338b10d49b4fc2d9d6ab4c2463e3c3db5), ROM_BIOS(2) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 3, "sn63", "#63: RISC OS 2.00 (12 Oct 1988)" )
	ROMX_LOAD( "a500_ros200_rom0.ic24", 0x000000, 0x10000, CRC(e69849a6) SHA1(80a0c849025a24d626804f57fe6ca992088d96e2), ROM_BIOS(3) | ROM_SKIP(3) )
	ROMX_LOAD( "a500_ros200_rom1.ic25", 0x000001, 0x10000, CRC(3ad7e1c0) SHA1(427560f567fa5f8a118db64c8e4bc2756cb2a457), ROM_BIOS(3) | ROM_SKIP(3) )
	ROMX_LOAD( "a500_ros200_rom2.ic26", 0x000002, 0x10000, CRC(e0f9466c) SHA1(017b658057e608b490f7e09ad5ef021687d9a79e), ROM_BIOS(3) | ROM_SKIP(3) )
	ROMX_LOAD( "a500_ros200_rom3.ic27", 0x000003, 0x10000, CRC(7876cc6c) SHA1(99c008aa1eae1977545ab0cebf2d8b2f09879818), ROM_BIOS(3) | ROM_SKIP(3) )

	ROM_REGION( 0x100, "i2cmem", ROMREGION_ERASE00 )
	ROMX_LOAD( "cmos_riscos2.bin", 0x0000, 0x0100, CRC(1ecf3369) SHA1(96163285797e0d54017d8d4ae87835328a4658bd), ROM_BIOS(0) )
	ROMX_LOAD( "cmos_riscos2.bin", 0x0000, 0x0100, CRC(1ecf3369) SHA1(96163285797e0d54017d8d4ae87835328a4658bd), ROM_BIOS(1) )
	ROMX_LOAD( "cmos_riscos3.bin", 0x0000, 0x0100, CRC(96ed59b2) SHA1(9dab30b4c3305e1142819687889fca334b532679), ROM_BIOS(2) )
	ROMX_LOAD( "cmos_riscos2.bin", 0x0000, 0x0100, CRC(1ecf3369) SHA1(96163285797e0d54017d8d4ae87835328a4658bd), ROM_BIOS(3) )
ROM_END

ROM_START( aa500d )
	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "s249", "#249: Arthur 1.20 (25 Sep 1987)" ) // ex Logica, developers of Domesday software
	ROMX_LOAD( "a500_arthur_12_0.ic24", 0x000000, 0x10000, CRC(3d61a13c) SHA1(90b70a30a81c22ba7510cfec62730f77d6c7414a), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "a500_arthur_12_1.ic25", 0x000001, 0x10000, CRC(829d2856) SHA1(454847cafd9d6d37a756205e0109c3e8c463ab92), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "a500_arthur_12_2.ic26", 0x000002, 0x10000, CRC(a68bfab4) SHA1(db55995aeb18a7d73a8beee3bc7388e23cd8a02d), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "a500_arthur_12_3.ic27", 0x000003, 0x10000, CRC(8c56de3f) SHA1(a8b8d1d6638488fe2a328814ab4f2f4566b663b6), ROM_BIOS(0) | ROM_SKIP(3) )

	ROM_REGION( 0x100, "i2cmem", ROMREGION_ERASE00 )
	ROMX_LOAD( "cmos_arthur.bin",  0x0000, 0x0100, CRC(4fc66ddc) SHA1(f0eae9a535505d82ba3488ddb7895434df940d73), ROM_BIOS(0) )

	//DISK_REGION( "hdc:0:harddisk" )
	//DISK_IMAGE( "a500_domesday", 0, SHA1(094ff299d6564d7b04bed4562f52aaa4cfd503b3) )
ROM_END

ROM_START( aa305 )
	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "030", "Arthur 0.30 (17 Jun 1987)" )
	ROMX_LOAD( "0276,322-01.rom", 0x000000, 0x20000, CRC(e6862d4c) SHA1(13d8470f1cb2c1d15530bc7fa8a95ecc4a371cf3), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "0276,323-01.rom", 0x000001, 0x20000, CRC(a9aeb4cf) SHA1(f37e744ba0e48861815683b24612b0dd69d6ea8b), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "0276,324-01.rom", 0x000002, 0x20000, CRC(7a175186) SHA1(efdee41d9811b05a726af9ba135e0178bb1749e5), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "0276,325-01.rom", 0x000003, 0x20000, CRC(7a8811b8) SHA1(7a6b5d7bb94ff5e2a31469ab024c8b2d4d295709), ROM_BIOS(0) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 1, "120", "Arthur 1.20 (25 Sep 1987)" )
	ROMX_LOAD( "0277,022-02.rom", 0x000000, 0x20000, CRC(03bfe550) SHA1(e4f3f1e37b84e716d75a32aa291a9189371daa1c), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "0277,023-02.rom", 0x000001, 0x20000, CRC(89ece77c) SHA1(e1979a8d3586c006e3837ff721cfa5439e6394bc), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "0277,024-02.rom", 0x000002, 0x20000, CRC(2302db86) SHA1(9fb3761571141bd51936daf03fa4f94fca177356), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "0277,025-02.rom", 0x000003, 0x20000, CRC(1546a0da) SHA1(078ef64aa8a7196d0ee4bca9926a74ade0a36173), ROM_BIOS(1) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 2, "200", "RISC OS 2.00 (05 Oct 1988)" )
	ROMX_LOAD( "0283,022-01.rom", 0x000000, 0x20000, CRC(24291ebf) SHA1(758adaf6f73b4041a680cdf9a0b2107da12ca5a0), ROM_BIOS(2) | ROM_SKIP(3) )
	ROMX_LOAD( "0283,023-01.rom", 0x000001, 0x20000, CRC(44a134f1) SHA1(2db7f06e692c3191b2e131d55a1cf997e226c7c6), ROM_BIOS(2) | ROM_SKIP(3) )
	ROMX_LOAD( "0283,024-01.rom", 0x000002, 0x20000, CRC(997f42b6) SHA1(779fcb13ce4107c27a003cdc848e8a5b7e039268), ROM_BIOS(2) | ROM_SKIP(3) )
	ROMX_LOAD( "0283,025-01.rom", 0x000003, 0x20000, CRC(6335dba2) SHA1(0e0631ee43acf3d005f24a4d5c11c5c60e6f29a2), ROM_BIOS(2) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 3, "201", "RISC OS 2.01 (05 Jul 1990)" )
	ROMX_LOAD( "0270,601-01.rom", 0x000000, 0x20000, CRC(29e2890b) SHA1(2ccdbda7494824180426d66cd38659f6ee55a045), ROM_BIOS(3) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,602-01.rom", 0x000001, 0x20000, CRC(dd1e4893) SHA1(2d39a5027fd164fd9409e38074c68731622406bb), ROM_BIOS(3) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,603-01.rom", 0x000002, 0x20000, CRC(985a8703) SHA1(87376fba36757b311f6c4178c2ac04d8a4ad063a), ROM_BIOS(3) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,604-01.rom", 0x000003, 0x20000, CRC(f23e4c8d) SHA1(27595da90d76d3b25b55e176e0688740c5ce39de), ROM_BIOS(3) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 4, "test", "Diagnostic Test ROMs" ) // Usage described in Archimedes 300 Series Service Manual
	ROMX_LOAD( "0276,146-01.rom", 0x000000, 0x10000, CRC(9c45283c) SHA1(9eb5bd7ad0958f194a3416d79d7e01e4c45741e1), ROM_BIOS(4) | ROM_SKIP(3) )
	ROMX_LOAD( "0276,147-01.rom", 0x000001, 0x10000, CRC(ad94e17f) SHA1(1c8e39c69d4ae1b674e0f732aaa62a4403998f41), ROM_BIOS(4) | ROM_SKIP(3) )
	ROMX_LOAD( "0276,148-01.rom", 0x000002, 0x10000, CRC(1ab02f2d) SHA1(dd7d216967524e64d1a03076a6081461ec8528c3), ROM_BIOS(4) | ROM_SKIP(3) )
	ROMX_LOAD( "0276,149-01.rom", 0x000003, 0x10000, CRC(5fd6a406) SHA1(790af8a4c74d0f6714d528f7502443ce5898a618), ROM_BIOS(4) | ROM_SKIP(3) )

	ROM_REGION( 0x100, "i2cmem", ROMREGION_ERASE00 )
	ROMX_LOAD( "cmos_arthur.bin",  0x0000, 0x0100, CRC(4fc66ddc) SHA1(f0eae9a535505d82ba3488ddb7895434df940d73), ROM_BIOS(0) )
	ROMX_LOAD( "cmos_arthur.bin",  0x0000, 0x0100, CRC(4fc66ddc) SHA1(f0eae9a535505d82ba3488ddb7895434df940d73), ROM_BIOS(1) )
	ROMX_LOAD( "cmos_riscos2.bin", 0x0000, 0x0100, CRC(1ecf3369) SHA1(96163285797e0d54017d8d4ae87835328a4658bd), ROM_BIOS(2) )
	ROMX_LOAD( "cmos_riscos2.bin", 0x0000, 0x0100, CRC(1ecf3369) SHA1(96163285797e0d54017d8d4ae87835328a4658bd), ROM_BIOS(3) )
ROM_END

ROM_START( aa310 )
	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "120", "Arthur 1.20 (25 Sep 1987)" )
	ROMX_LOAD( "0277,022-02.rom", 0x000000, 0x20000, CRC(03bfe550) SHA1(e4f3f1e37b84e716d75a32aa291a9189371daa1c), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "0277,023-02.rom", 0x000001, 0x20000, CRC(89ece77c) SHA1(e1979a8d3586c006e3837ff721cfa5439e6394bc), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "0277,024-02.rom", 0x000002, 0x20000, CRC(2302db86) SHA1(9fb3761571141bd51936daf03fa4f94fca177356), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "0277,025-02.rom", 0x000003, 0x20000, CRC(1546a0da) SHA1(078ef64aa8a7196d0ee4bca9926a74ade0a36173), ROM_BIOS(0) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 1, "030", "Arthur 0.30 (17 Jun 1987)" )
	ROMX_LOAD( "0276,322-01.rom", 0x000000, 0x20000, CRC(e6862d4c) SHA1(13d8470f1cb2c1d15530bc7fa8a95ecc4a371cf3), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "0276,323-01.rom", 0x000001, 0x20000, CRC(a9aeb4cf) SHA1(f37e744ba0e48861815683b24612b0dd69d6ea8b), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "0276,324-01.rom", 0x000002, 0x20000, CRC(7a175186) SHA1(efdee41d9811b05a726af9ba135e0178bb1749e5), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "0276,325-01.rom", 0x000003, 0x20000, CRC(7a8811b8) SHA1(7a6b5d7bb94ff5e2a31469ab024c8b2d4d295709), ROM_BIOS(1) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 2, "200", "RISC OS 2.00 (05 Oct 1988)" )
	ROMX_LOAD( "0283,022-01.rom", 0x000000, 0x20000, CRC(24291ebf) SHA1(758adaf6f73b4041a680cdf9a0b2107da12ca5a0), ROM_BIOS(2) | ROM_SKIP(3) )
	ROMX_LOAD( "0283,023-01.rom", 0x000001, 0x20000, CRC(44a134f1) SHA1(2db7f06e692c3191b2e131d55a1cf997e226c7c6), ROM_BIOS(2) | ROM_SKIP(3) )
	ROMX_LOAD( "0283,024-01.rom", 0x000002, 0x20000, CRC(997f42b6) SHA1(779fcb13ce4107c27a003cdc848e8a5b7e039268), ROM_BIOS(2) | ROM_SKIP(3) )
	ROMX_LOAD( "0283,025-01.rom", 0x000003, 0x20000, CRC(6335dba2) SHA1(0e0631ee43acf3d005f24a4d5c11c5c60e6f29a2), ROM_BIOS(2) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 3, "201", "RISC OS 2.01 (05 Jul 1990)" )
	ROMX_LOAD( "0270,601-01.rom", 0x000000, 0x20000, CRC(29e2890b) SHA1(2ccdbda7494824180426d66cd38659f6ee55a045), ROM_BIOS(3) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,602-01.rom", 0x000001, 0x20000, CRC(dd1e4893) SHA1(2d39a5027fd164fd9409e38074c68731622406bb), ROM_BIOS(3) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,603-01.rom", 0x000002, 0x20000, CRC(985a8703) SHA1(87376fba36757b311f6c4178c2ac04d8a4ad063a), ROM_BIOS(3) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,604-01.rom", 0x000003, 0x20000, CRC(f23e4c8d) SHA1(27595da90d76d3b25b55e176e0688740c5ce39de), ROM_BIOS(3) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 4, "300", "RISC OS 3.00 (25 Sep 1991)" )
	ROMX_LOAD( "0270,251-01.rom", 0x000000, 0x80000, CRC(023115a9) SHA1(d3233f76d5750e04ef2bc39d5b2dfd96e6a03c45), ROM_BIOS(4) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,252-01.rom", 0x000001, 0x80000, CRC(6db01129) SHA1(4b801dcce4d268d5e4c2680efa23acb29e4f907f), ROM_BIOS(4) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,253-01.rom", 0x000002, 0x80000, CRC(d749a9f2) SHA1(c53c35b847d300989163f9e779590d1853e8adaf), ROM_BIOS(4) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,254-01.rom", 0x000003, 0x80000, CRC(5b13c523) SHA1(b815bdf31dd99e5b4f2d99790cc28ee8cd907b43), ROM_BIOS(4) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 5, "310", "RISC OS 3.10 (30 Apr 1992)" )
	ROMX_LOAD( "0296,041-01.rom", 0x000000, 0x80000, CRC(b7499ef8) SHA1(4ab53a53c531bfbecdd441c82d9f4c0517682dde), ROM_BIOS(5) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,042-01.rom", 0x000001, 0x80000, CRC(d55a854c) SHA1(c9308cee92cca2a626d8577ec99485ad58b8da2a), ROM_BIOS(5) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,043-01.rom", 0x000002, 0x80000, CRC(19bc549a) SHA1(88b02bd3df94b56284ffad1c24fa140249b7cb63), ROM_BIOS(5) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,044-01.rom", 0x000003, 0x80000, CRC(bf86f497) SHA1(a200dca6dbee7c0be25a7e5363a6a3e6455a3bf3), ROM_BIOS(5) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 6, "311", "RISC OS 3.11 (29 Sep 1992)" )
	ROMX_LOAD( "0296,041-02.rom", 0x000000, 0x80000, CRC(84185879) SHA1(2740312b32e9cb8ca6cba9f7b33b68dc0dfab810), ROM_BIOS(6) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,042-02.rom", 0x000001, 0x80000, CRC(c7584553) SHA1(144f8f55f06d6d0752f2f989f4f5c7cec38a43ea), ROM_BIOS(6) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,043-02.rom", 0x000002, 0x80000, CRC(ff5acf17) SHA1(f9c9d4eb2f465b44353257594e631d0e3706f651), ROM_BIOS(6) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,044-02.rom", 0x000003, 0x80000, CRC(e2a3480e) SHA1(5b48e8b66ba86568e2225d60f34e201dd5f5d52a), ROM_BIOS(6) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 7, "test", "Diagnostic Test ROMs" ) // Usage described in Archimedes 300 Series Service Manual
	ROMX_LOAD( "0276,146-01.rom", 0x000000, 0x10000, CRC(9c45283c) SHA1(9eb5bd7ad0958f194a3416d79d7e01e4c45741e1), ROM_BIOS(7) | ROM_SKIP(3) )
	ROMX_LOAD( "0276,147-01.rom", 0x000001, 0x10000, CRC(ad94e17f) SHA1(1c8e39c69d4ae1b674e0f732aaa62a4403998f41), ROM_BIOS(7) | ROM_SKIP(3) )
	ROMX_LOAD( "0276,148-01.rom", 0x000002, 0x10000, CRC(1ab02f2d) SHA1(dd7d216967524e64d1a03076a6081461ec8528c3), ROM_BIOS(7) | ROM_SKIP(3) )
	ROMX_LOAD( "0276,149-01.rom", 0x000003, 0x10000, CRC(5fd6a406) SHA1(790af8a4c74d0f6714d528f7502443ce5898a618), ROM_BIOS(7) | ROM_SKIP(3) )

	ROM_REGION( 0x100, "i2cmem", ROMREGION_ERASE00 )
	ROMX_LOAD( "cmos_arthur.bin",  0x0000, 0x0100, CRC(4fc66ddc) SHA1(f0eae9a535505d82ba3488ddb7895434df940d73), ROM_BIOS(0) )
	ROMX_LOAD( "cmos_arthur.bin",  0x0000, 0x0100, CRC(4fc66ddc) SHA1(f0eae9a535505d82ba3488ddb7895434df940d73), ROM_BIOS(1) )
	ROMX_LOAD( "cmos_riscos2.bin", 0x0000, 0x0100, CRC(1ecf3369) SHA1(96163285797e0d54017d8d4ae87835328a4658bd), ROM_BIOS(2) )
	ROMX_LOAD( "cmos_riscos2.bin", 0x0000, 0x0100, CRC(1ecf3369) SHA1(96163285797e0d54017d8d4ae87835328a4658bd), ROM_BIOS(3) )
	ROMX_LOAD( "cmos_riscos3.bin", 0x0000, 0x0100, CRC(96ed59b2) SHA1(9dab30b4c3305e1142819687889fca334b532679), ROM_BIOS(4) )
	ROMX_LOAD( "cmos_riscos3.bin", 0x0000, 0x0100, CRC(96ed59b2) SHA1(9dab30b4c3305e1142819687889fca334b532679), ROM_BIOS(5) )
	ROMX_LOAD( "cmos_riscos3.bin", 0x0000, 0x0100, CRC(96ed59b2) SHA1(9dab30b4c3305e1142819687889fca334b532679), ROM_BIOS(6) )
ROM_END

#define rom_aa440 rom_aa310

ROM_START( am4 )
	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD32_BYTE( "m4_arf_0.bin", 0x000000, 0x10000, CRC(b54544c2) SHA1(f75d6b4c8506d1f14f9583b4175af0c8accd8562) )
	ROM_LOAD32_BYTE( "m4_arf_1.bin", 0x000001, 0x10000, CRC(cd6fe9be) SHA1(07acf52a9cc81939998f52836be477285571f332) )
	ROM_LOAD32_BYTE( "m4_arf_2.bin", 0x000002, 0x10000, CRC(575ffc0a) SHA1(816008aa5bd5cfbace07ae4b5e4691951beccf76) )
	ROM_LOAD32_BYTE( "m4_arf_3.bin", 0x000003, 0x10000, CRC(78feaa86) SHA1(476d9c0006b857b51fa272eef819ecfc8b4257d8) )

	ROM_REGION( 0x100, "i2cmem", ROMREGION_ERASE00 )
ROM_END

ROM_START( aa680 )
	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD32_BYTE( "0274,200-c_boot_rom_0.ic150", 0x000000, 0x20000, CRC(b04c206c) SHA1(9f83c20ba738c3a7dc63ded45151108fd75975bd) )
	ROM_LOAD32_BYTE( "0274,201-c_boot_rom_1.ic151", 0x000001, 0x20000, CRC(baf57404) SHA1(cf4ea48007f57f4e7d7ac3ae782d462fa34d04bf) )
	ROM_LOAD32_BYTE( "0274,202-c_boot_rom_2.ic152", 0x000002, 0x20000, CRC(c9adf722) SHA1(77c613c6b1b4bd49069a18713166ca8d1c926e02) )
	ROM_LOAD32_BYTE( "0274,203-c_boot_rom_3.ic153", 0x000003, 0x20000, CRC(a5d9eb4b) SHA1(2c521c7500dae27d4192067c3e4758180699f0eb) )

	ROM_REGION( 0x100, "i2cmem", ROMREGION_ERASE00 )
ROM_END

ROM_START( aa3000 )
	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "200", "RISC OS 2.00 (05 Oct 1988)" )
	ROMX_LOAD( "0283,022-01.rom", 0x000000, 0x20000, CRC(24291ebf) SHA1(758adaf6f73b4041a680cdf9a0b2107da12ca5a0), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "0283,023-01.rom", 0x000001, 0x20000, CRC(44a134f1) SHA1(2db7f06e692c3191b2e131d55a1cf997e226c7c6), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "0283,024-01.rom", 0x000002, 0x20000, CRC(997f42b6) SHA1(779fcb13ce4107c27a003cdc848e8a5b7e039268), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "0283,025-01.rom", 0x000003, 0x20000, CRC(6335dba2) SHA1(0e0631ee43acf3d005f24a4d5c11c5c60e6f29a2), ROM_BIOS(0) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 1, "201", "RISC OS 2.01 (05 Jul 1990)" )
	ROMX_LOAD( "0270,601-01.rom", 0x000000, 0x20000, CRC(29e2890b) SHA1(2ccdbda7494824180426d66cd38659f6ee55a045), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,602-01.rom", 0x000001, 0x20000, CRC(dd1e4893) SHA1(2d39a5027fd164fd9409e38074c68731622406bb), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,603-01.rom", 0x000002, 0x20000, CRC(985a8703) SHA1(87376fba36757b311f6c4178c2ac04d8a4ad063a), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,604-01.rom", 0x000003, 0x20000, CRC(f23e4c8d) SHA1(27595da90d76d3b25b55e176e0688740c5ce39de), ROM_BIOS(1) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 2, "300", "RISC OS 3.00 (25 Sep 1991)" )
	ROMX_LOAD( "0270,251-01.rom", 0x000000, 0x80000, CRC(023115a9) SHA1(d3233f76d5750e04ef2bc39d5b2dfd96e6a03c45), ROM_BIOS(2) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,252-01.rom", 0x000001, 0x80000, CRC(6db01129) SHA1(4b801dcce4d268d5e4c2680efa23acb29e4f907f), ROM_BIOS(2) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,253-01.rom", 0x000002, 0x80000, CRC(d749a9f2) SHA1(c53c35b847d300989163f9e779590d1853e8adaf), ROM_BIOS(2) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,254-01.rom", 0x000003, 0x80000, CRC(5b13c523) SHA1(b815bdf31dd99e5b4f2d99790cc28ee8cd907b43), ROM_BIOS(2) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 3, "310", "RISC OS 3.10 (30 Apr 1992)" )
	ROMX_LOAD( "0296,041-01.rom", 0x000000, 0x80000, CRC(b7499ef8) SHA1(4ab53a53c531bfbecdd441c82d9f4c0517682dde), ROM_BIOS(3) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,042-01.rom", 0x000001, 0x80000, CRC(d55a854c) SHA1(c9308cee92cca2a626d8577ec99485ad58b8da2a), ROM_BIOS(3) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,043-01.rom", 0x000002, 0x80000, CRC(19bc549a) SHA1(88b02bd3df94b56284ffad1c24fa140249b7cb63), ROM_BIOS(3) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,044-01.rom", 0x000003, 0x80000, CRC(bf86f497) SHA1(a200dca6dbee7c0be25a7e5363a6a3e6455a3bf3), ROM_BIOS(3) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 4, "311", "RISC OS 3.11 (29 Sep 1992)" )
	ROMX_LOAD( "0296,041-02.rom", 0x000000, 0x80000, CRC(84185879) SHA1(2740312b32e9cb8ca6cba9f7b33b68dc0dfab810), ROM_BIOS(4) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,042-02.rom", 0x000001, 0x80000, CRC(c7584553) SHA1(144f8f55f06d6d0752f2f989f4f5c7cec38a43ea), ROM_BIOS(4) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,043-02.rom", 0x000002, 0x80000, CRC(ff5acf17) SHA1(f9c9d4eb2f465b44353257594e631d0e3706f651), ROM_BIOS(4) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,044-02.rom", 0x000003, 0x80000, CRC(e2a3480e) SHA1(5b48e8b66ba86568e2225d60f34e201dd5f5d52a), ROM_BIOS(4) | ROM_SKIP(3) )

	ROM_REGION( 0x100, "i2cmem", ROMREGION_ERASE00 )
	ROMX_LOAD( "cmos_riscos2.bin", 0x0000, 0x0100, CRC(1ecf3369) SHA1(96163285797e0d54017d8d4ae87835328a4658bd), ROM_BIOS(0) )
	ROMX_LOAD( "cmos_riscos2.bin", 0x0000, 0x0100, CRC(1ecf3369) SHA1(96163285797e0d54017d8d4ae87835328a4658bd), ROM_BIOS(1) )
	ROMX_LOAD( "cmos_riscos3.bin", 0x0000, 0x0100, CRC(96ed59b2) SHA1(9dab30b4c3305e1142819687889fca334b532679), ROM_BIOS(2) )
	ROMX_LOAD( "cmos_riscos3.bin", 0x0000, 0x0100, CRC(96ed59b2) SHA1(9dab30b4c3305e1142819687889fca334b532679), ROM_BIOS(3) )
	ROMX_LOAD( "cmos_riscos3.bin", 0x0000, 0x0100, CRC(96ed59b2) SHA1(9dab30b4c3305e1142819687889fca334b532679), ROM_BIOS(4) )
ROM_END

#define rom_aa4101 rom_aa3000
#define rom_aa4201 rom_aa3000
#define rom_aa4401 rom_aa3000

ROM_START( ar140 )
	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "200", "RISC OS 2.00 (05 Oct 1988)" )
	ROMX_LOAD( "0283,022-01.rom", 0x000000, 0x20000, CRC(24291ebf) SHA1(758adaf6f73b4041a680cdf9a0b2107da12ca5a0), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "0283,023-01.rom", 0x000001, 0x20000, CRC(44a134f1) SHA1(2db7f06e692c3191b2e131d55a1cf997e226c7c6), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "0283,024-01.rom", 0x000002, 0x20000, CRC(997f42b6) SHA1(779fcb13ce4107c27a003cdc848e8a5b7e039268), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "0283,025-01.rom", 0x000003, 0x20000, CRC(6335dba2) SHA1(0e0631ee43acf3d005f24a4d5c11c5c60e6f29a2), ROM_BIOS(0) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 1, "201", "RISC OS 2.01 (05 Jul 1990)" )
	ROMX_LOAD( "0270,601-01.rom", 0x000000, 0x20000, CRC(29e2890b) SHA1(2ccdbda7494824180426d66cd38659f6ee55a045), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,602-01.rom", 0x000001, 0x20000, CRC(dd1e4893) SHA1(2d39a5027fd164fd9409e38074c68731622406bb), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,603-01.rom", 0x000002, 0x20000, CRC(985a8703) SHA1(87376fba36757b311f6c4178c2ac04d8a4ad063a), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,604-01.rom", 0x000003, 0x20000, CRC(f23e4c8d) SHA1(27595da90d76d3b25b55e176e0688740c5ce39de), ROM_BIOS(1) | ROM_SKIP(3) )

	ROM_REGION( 0x100, "i2cmem", ROMREGION_ERASE00 )
	ROMX_LOAD( "cmos_riscos2.bin", 0x0000, 0x0100, CRC(1ecf3369) SHA1(96163285797e0d54017d8d4ae87835328a4658bd), ROM_BIOS(0) )
	ROMX_LOAD( "cmos_riscos2.bin", 0x0000, 0x0100, CRC(1ecf3369) SHA1(96163285797e0d54017d8d4ae87835328a4658bd), ROM_BIOS(1) )
ROM_END

ROM_START( aa540 )
	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "201", "RISC OS 2.01 (05 Jul 1990)" )
	ROMX_LOAD( "0270,601-01.rom", 0x000000, 0x20000, CRC(29e2890b) SHA1(2ccdbda7494824180426d66cd38659f6ee55a045), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,602-01.rom", 0x000001, 0x20000, CRC(dd1e4893) SHA1(2d39a5027fd164fd9409e38074c68731622406bb), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,603-01.rom", 0x000002, 0x20000, CRC(985a8703) SHA1(87376fba36757b311f6c4178c2ac04d8a4ad063a), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,604-01.rom", 0x000003, 0x20000, CRC(f23e4c8d) SHA1(27595da90d76d3b25b55e176e0688740c5ce39de), ROM_BIOS(0) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 1, "300", "RISC OS 3.00 (25 Sep 1991)" )
	ROMX_LOAD( "0270,251-01.rom", 0x000000, 0x80000, CRC(023115a9) SHA1(d3233f76d5750e04ef2bc39d5b2dfd96e6a03c45), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,252-01.rom", 0x000001, 0x80000, CRC(6db01129) SHA1(4b801dcce4d268d5e4c2680efa23acb29e4f907f), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,253-01.rom", 0x000002, 0x80000, CRC(d749a9f2) SHA1(c53c35b847d300989163f9e779590d1853e8adaf), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,254-01.rom", 0x000003, 0x80000, CRC(5b13c523) SHA1(b815bdf31dd99e5b4f2d99790cc28ee8cd907b43), ROM_BIOS(1) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 2, "310", "RISC OS 3.10 (30 Apr 1992)" )
	ROMX_LOAD( "0296,041-01.rom", 0x000000, 0x80000, CRC(b7499ef8) SHA1(4ab53a53c531bfbecdd441c82d9f4c0517682dde), ROM_BIOS(2) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,042-01.rom", 0x000001, 0x80000, CRC(d55a854c) SHA1(c9308cee92cca2a626d8577ec99485ad58b8da2a), ROM_BIOS(2) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,043-01.rom", 0x000002, 0x80000, CRC(19bc549a) SHA1(88b02bd3df94b56284ffad1c24fa140249b7cb63), ROM_BIOS(2) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,044-01.rom", 0x000003, 0x80000, CRC(bf86f497) SHA1(a200dca6dbee7c0be25a7e5363a6a3e6455a3bf3), ROM_BIOS(2) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 3, "311", "RISC OS 3.11 (29 Sep 1992)" )
	ROMX_LOAD( "0296,041-02.rom", 0x000000, 0x80000, CRC(84185879) SHA1(2740312b32e9cb8ca6cba9f7b33b68dc0dfab810), ROM_BIOS(3) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,042-02.rom", 0x000001, 0x80000, CRC(c7584553) SHA1(144f8f55f06d6d0752f2f989f4f5c7cec38a43ea), ROM_BIOS(3) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,043-02.rom", 0x000002, 0x80000, CRC(ff5acf17) SHA1(f9c9d4eb2f465b44353257594e631d0e3706f651), ROM_BIOS(3) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,044-02.rom", 0x000003, 0x80000, CRC(e2a3480e) SHA1(5b48e8b66ba86568e2225d60f34e201dd5f5d52a), ROM_BIOS(3) | ROM_SKIP(3) )

	ROM_REGION( 0x100, "i2cmem", ROMREGION_ERASE00 )
	ROMX_LOAD( "cmos_riscos2.bin", 0x0000, 0x0100, CRC(1ecf3369) SHA1(96163285797e0d54017d8d4ae87835328a4658bd), ROM_BIOS(0) )
	ROMX_LOAD( "cmos_riscos3.bin", 0x0000, 0x0100, CRC(96ed59b2) SHA1(9dab30b4c3305e1142819687889fca334b532679), ROM_BIOS(1) )
	ROMX_LOAD( "cmos_riscos3.bin", 0x0000, 0x0100, CRC(96ed59b2) SHA1(9dab30b4c3305e1142819687889fca334b532679), ROM_BIOS(2) )
	ROMX_LOAD( "cmos_riscos3.bin", 0x0000, 0x0100, CRC(96ed59b2) SHA1(9dab30b4c3305e1142819687889fca334b532679), ROM_BIOS(3) )
ROM_END

#define rom_ar225 rom_aa540
#define rom_ar260 rom_aa540

ROM_START( aa5000 )
	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS("311")
	ROM_SYSTEM_BIOS( 0, "300", "RISC OS 3.00 (25 Sep 1991)" )
	ROMX_LOAD( "0270,251-01.rom", 0x000000, 0x80000, CRC(023115a9) SHA1(d3233f76d5750e04ef2bc39d5b2dfd96e6a03c45), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,252-01.rom", 0x000001, 0x80000, CRC(6db01129) SHA1(4b801dcce4d268d5e4c2680efa23acb29e4f907f), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,253-01.rom", 0x000002, 0x80000, CRC(d749a9f2) SHA1(c53c35b847d300989163f9e779590d1853e8adaf), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "0270,254-01.rom", 0x000003, 0x80000, CRC(5b13c523) SHA1(b815bdf31dd99e5b4f2d99790cc28ee8cd907b43), ROM_BIOS(0) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 1, "310", "RISC OS 3.10 (30 Apr 1992)" )
	ROMX_LOAD( "0296,041-01.rom", 0x000000, 0x80000, CRC(b7499ef8) SHA1(4ab53a53c531bfbecdd441c82d9f4c0517682dde), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,042-01.rom", 0x000001, 0x80000, CRC(d55a854c) SHA1(c9308cee92cca2a626d8577ec99485ad58b8da2a), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,043-01.rom", 0x000002, 0x80000, CRC(19bc549a) SHA1(88b02bd3df94b56284ffad1c24fa140249b7cb63), ROM_BIOS(1) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,044-01.rom", 0x000003, 0x80000, CRC(bf86f497) SHA1(a200dca6dbee7c0be25a7e5363a6a3e6455a3bf3), ROM_BIOS(1) | ROM_SKIP(3) )
	ROM_SYSTEM_BIOS( 2, "311", "RISC OS 3.11 (29 Sep 1992)" )
	ROMX_LOAD( "0296,041-02.rom", 0x000000, 0x80000, CRC(84185879) SHA1(2740312b32e9cb8ca6cba9f7b33b68dc0dfab810), ROM_BIOS(2) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,042-02.rom", 0x000001, 0x80000, CRC(c7584553) SHA1(144f8f55f06d6d0752f2f989f4f5c7cec38a43ea), ROM_BIOS(2) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,043-02.rom", 0x000002, 0x80000, CRC(ff5acf17) SHA1(f9c9d4eb2f465b44353257594e631d0e3706f651), ROM_BIOS(2) | ROM_SKIP(3) )
	ROMX_LOAD( "0296,044-02.rom", 0x000003, 0x80000, CRC(e2a3480e) SHA1(5b48e8b66ba86568e2225d60f34e201dd5f5d52a), ROM_BIOS(2) | ROM_SKIP(3) )

	ROM_REGION( 0x10000, "extension", ROMREGION_ERASE00 )

	ROM_REGION( 0x100, "i2cmem", ROMREGION_ERASE00 )
	ROM_LOAD( "cmos_riscos3.bin", 0x0000, 0x0100, CRC(96ed59b2) SHA1(9dab30b4c3305e1142819687889fca334b532679) )

	DISK_REGION( "upc:ide:0:hdd" )
	DISK_IMAGE( "riscos311_apps", 0, SHA1(d69e2fb15d82f83d32786a29dd3321a37a9dbb36) )
ROM_END

#define rom_aa5000a rom_aa5000

ROM_START( aa4 )
	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASEFF )
	// RISC OS 3.10 (30 Apr 1992)
	ROM_LOAD32_WORD( "0296,061-01.ic4",  0x000000, 0x100000, CRC(b77fe215) SHA1(57b19ea4b97a9b6a240aa61211c2c134cb295aa0) )
	ROM_LOAD32_WORD( "0296,062-01.ic15", 0x000002, 0x100000, CRC(d42e196e) SHA1(64243d39d1bca38b10761f66a8042c883bde87a4) )

	ROM_REGION( 0x10000, "extension", ROMREGION_ERASE00 )
	// Power Management
	ROM_LOAD( "0296,063-01.ic38", 0x00000, 0x10000, CRC(9ca3a6be) SHA1(75905b031f49960605d55c3e7350d309559ed440) )

	ROM_REGION( 0x100, "i2cmem", ROMREGION_ERASE00 )
	ROM_LOAD( "cmos_riscos3.bin", 0x0000, 0x0100, CRC(96ed59b2) SHA1(9dab30b4c3305e1142819687889fca334b532679) )

	DISK_REGION( "upc:ide:0:hdd" )
	DISK_IMAGE( "riscos311_apps", 0, SHA1(d69e2fb15d82f83d32786a29dd3321a37a9dbb36) )
ROM_END

ROM_START( aa3010 )
	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASEFF )
	// RISC OS 3.11 (29 Sep 1992)
	ROM_LOAD32_WORD( "0296,061-02.ic17", 0x000000, 0x100000, CRC(552fc3aa) SHA1(b2f1911e53d7377f2e69e1a870139745d3df494b) )
	ROM_LOAD32_WORD( "0296,062-02.ic18", 0x000002, 0x100000, CRC(308d5a4a) SHA1(b309e1dd85670a06d77ec504dbbec6c42336329f) )

	ROM_REGION( 0x100, "i2cmem", ROMREGION_ERASE00 )
	ROM_LOAD( "cmos_riscos3.bin", 0x0000, 0x0100, CRC(96ed59b2) SHA1(9dab30b4c3305e1142819687889fca334b532679) )
ROM_END

ROM_START( aa3010_de )
	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASEFF )
	// RISC OS 3.19 (09 Jun 1993)
	ROM_LOAD32_WORD( "0296,241-01.ic17", 0x000000, 0x100000, CRC(8aaf7ff3) SHA1(bc00d90842f40259a48d8f0627d4129e2fa766fe) )
	ROM_LOAD32_WORD( "0296,242-01.ic18", 0x000002, 0x100000, CRC(0ddc807e) SHA1(b0fdb33869cc593123a04fe959c1528f76aac0b9) )

	ROM_REGION( 0x100, "i2cmem", ROMREGION_ERASE00 )
	ROM_LOAD( "cmos_riscos3.bin", 0x0000, 0x0100, CRC(96ed59b2) SHA1(9dab30b4c3305e1142819687889fca334b532679) )
ROM_END

ROM_START( aa3020 )
	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASEFF )
	// RISC OS 3.11 (29 Sep 1992)
	ROM_LOAD32_WORD( "0296,061-02.ic17", 0x000000, 0x100000, CRC(552fc3aa) SHA1(b2f1911e53d7377f2e69e1a870139745d3df494b) )
	ROM_LOAD32_WORD( "0296,062-02.ic18", 0x000002, 0x100000, CRC(308d5a4a) SHA1(b309e1dd85670a06d77ec504dbbec6c42336329f) )

	ROM_REGION( 0x100, "i2cmem", ROMREGION_ERASE00 )
	ROM_LOAD( "cmos_riscos3.bin", 0x0000, 0x0100, CRC(96ed59b2) SHA1(9dab30b4c3305e1142819687889fca334b532679) )

	DISK_REGION( "upc:ide:0:hdd" )
	DISK_IMAGE( "riscos311_apps", 0, SHA1(d69e2fb15d82f83d32786a29dd3321a37a9dbb36) )
ROM_END

#define rom_aa4000 rom_aa3020

ROM_START( av20dev )
	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "320", "RISC OS 3.20 (10 Sep 1992)" )
	ROMX_LOAD( "riscos_vidc20-2_0.rom", 0x000000, 0x80000, CRC(2cdaa10b) SHA1(172bc66124da68e0556878945bfc7e6611cde38f), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "riscos_vidc20-2_1.rom", 0x000001, 0x80000, CRC(2dd7404e) SHA1(a4be1d6874650815435a7c02fb3c681f04b05a4d), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "riscos_vidc20-2_2.rom", 0x000002, 0x80000, CRC(7e9e307a) SHA1(c64d6bda19aedf1e009da6537c019b02666155ff), ROM_BIOS(0) | ROM_SKIP(3) )
	ROMX_LOAD( "riscos_vidc20-2_3.rom", 0x000003, 0x80000, CRC(dc59924c) SHA1(ebd0bdc07ef200640b39b90bd9f89c15ca396089), ROM_BIOS(0) | ROM_SKIP(3) )

	ROM_REGION( 0x100, "i2cmem", ROMREGION_ERASE00 )
	ROMX_LOAD( "cmos_riscos3.bin", 0x0000, 0x0100, CRC(96ed59b2) SHA1(9dab30b4c3305e1142819687889fca334b532679), ROM_BIOS(0) )
ROM_END

} // anonymous namespace


//    YEAR  NAME       PARENT  COMPAT  MACHINE  INPUT     CLASS         INIT        COMPANY            FULLNAME                                  FLAGS
COMP( 1986, aa500,     0,      0,      aa500,   0,        aa500_state,  init_hd,    "Acorn Computers", "Acorn A500 Development System",          MACHINE_NOT_WORKING )
COMP( 1986, aa500d,    aa500,  0,      aa500d,  0,        aa500_state,  init_hd,    "Acorn Computers", "Acorn A500 Domesday Development System", MACHINE_NOT_WORKING )
COMP( 1987, aa305,     aa310,  0,      aa305,   0,        aa310_state,  init_flop,  "Acorn Computers", "Archimedes 305",                         MACHINE_NOT_WORKING )
COMP( 1987, aa310,     0,      0,      aa310,   0,        aa310_state,  init_flop,  "Acorn Computers", "Archimedes 310",                         MACHINE_NOT_WORKING )
COMP( 1987, aa440,     aa310,  0,      aa440,   0,        aa310_state,  init_hd,    "Acorn Computers", "Archimedes 440",                         MACHINE_NOT_WORKING )
COMP( 1988, am4,       0,      0,      am4,     0,        aa680_state,  empty_init, "Acorn Computers", "Acorn M4",                               MACHINE_NOT_WORKING )
COMP( 1988, aa680,     0,      0,      aa680,   0,        aa680_state,  empty_init, "Acorn Computers", "Acorn A680 UNIX Evaluation System",      MACHINE_NOT_WORKING )
COMP( 1989, aa3000,    aa310,  0,      aa3000,  0,        aa310_state,  init_flop,  "Acorn Computers", "BBC A3000",                              MACHINE_NOT_WORKING )
COMP( 1989, aa4101,    aa310,  0,      aa4101,  0,        aa310_state,  init_flop,  "Acorn Computers", "Archimedes 410/1",                       MACHINE_NOT_WORKING )
COMP( 1989, aa4201,    aa310,  0,      aa4201,  0,        aa310_state,  init_flop,  "Acorn Computers", "Archimedes 420/1",                       MACHINE_NOT_WORKING )
COMP( 1989, aa4401,    aa310,  0,      aa4401,  0,        aa310_state,  init_hd,    "Acorn Computers", "Archimedes 440/1",                       MACHINE_NOT_WORKING )
COMP( 1989, ar140,     aa310,  0,      ar140,   0,        aa310_state,  init_hd,    "Acorn Computers", "Acorn R140",                             MACHINE_NOT_WORKING )
COMP( 1990, aa540,     aa310,  0,      aa540,   0,        aa310_state,  init_scsi,  "Acorn Computers", "Archimedes 540",                         MACHINE_NOT_WORKING )
COMP( 1990, ar225,     aa310,  0,      ar225,   0,        aa310_state,  init_r225,  "Acorn Computers", "Acorn R225",                             MACHINE_NOT_WORKING )
COMP( 1990, ar260,     aa310,  0,      ar260,   0,        aa310_state,  init_scsi,  "Acorn Computers", "Acorn R260",                             MACHINE_NOT_WORKING )
COMP( 1992, av20dev,   aa310,  0,      av20dev, 0,        aa310_state,  init_scsi,  "Acorn Computers", "Acorn V20 (Development)",                MACHINE_NOT_WORKING )
COMP( 1991, aa5000,    0,      0,      aa5000,  0,        aa5000_state, init_ide,   "Acorn Computers", "Acorn A5000",                            MACHINE_NOT_WORKING )
COMP( 1992, aa4,       aa5000, 0,      aa4,     0,        aa4_state,    init_a4,    "Acorn Computers", "Acorn A4",                               MACHINE_NOT_WORKING )
COMP( 1992, aa3010,    0,      0,      aa3010,  aa3010,   aa4000_state, init_flop,  "Acorn Computers", "Acorn A3010",                            MACHINE_NOT_WORKING )
COMP( 1993, aa3010_de, aa3010, 0,      aa3010,  aa3010,   aa4000_state, init_flop,  "Acorn Computers", "Acorn A3010 (German)",                   MACHINE_NOT_WORKING )
COMP( 1992, aa3020,    aa3010, 0,      aa3020,  0,        aa4000_state, init_ide,   "Acorn Computers", "Acorn A3020",                            MACHINE_NOT_WORKING )
COMP( 1992, aa4000,    aa3010, 0,      aa4000,  0,        aa4000_state, init_ide,   "Acorn Computers", "Acorn A4000",                            MACHINE_NOT_WORKING )
COMP( 1993, aa5000a,   aa5000, 0,      aa5000a, 0,        aa5000_state, init_ide,   "Acorn Computers", "Acorn A5000 Alpha",                      MACHINE_NOT_WORKING )



aaa.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

Skeleton driver for Ann Arbor Ambassador terminal.

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

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/74259.h"
#include "machine/input_merger.h"
#include "machine/scn_pci.h"
#include "machine/nvram.h"

#include "screen.h"


namespace {

class aaa_state : public driver_device
{
public:
	aaa_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_usart(*this, "usart%u", 0U)
		, m_screen(*this, "screen")
		, m_key_row(*this, "KSL%u", 0U)
		, m_font(*this, "font")
		, m_display_ram(*this, "display")
		, m_linecount(*this, "linecount")
	{ }

	void aaa(machine_config &config);

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void display_ram_w(offs_t offset, u8 data);
	template<int N> u8 usart_r(offs_t offset);
	template<int N> void usart_w(offs_t offset, u8 data);
	u8 keyboard_r(offs_t offset);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device_array<scn2651_device, 2> m_usart;
	required_device<screen_device> m_screen;
	optional_ioport_array<16> m_key_row;
	required_region_ptr<u8> m_font;
	required_shared_ptr<u8> m_display_ram;
	required_shared_ptr<u8> m_linecount;
};

u32 aaa_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void aaa_state::display_ram_w(offs_t offset, u8 data)
{
	// 6 bits wide (2 words per character)
	m_display_ram[offset] = data | 0xc0;
}

template<int N>
u8 aaa_state::usart_r(offs_t offset)
{
	return m_usart[N]->read(offset >> 1);
}

template<int N>
void aaa_state::usart_w(offs_t offset, u8 data)
{
	m_usart[N]->write(offset >> 1, data);
}

u8 aaa_state::keyboard_r(offs_t offset)
{
	// TODO: key click
	return m_key_row[offset & 15].read_safe(0xff);
}

void aaa_state::mem_map(address_map &map)
{
	map(0x0000, 0x1fff).rom().region("loprog", 0);
	map(0x2000, 0x27ff).mirror(0x800).ram();
	map(0x3f00, 0x3f03).writeonly().share("linecount"); // LS670 at 7B on main board
	map(0x4000, 0x43ff).ram().share("nvram");
	map(0x8000, 0x9fff).rom().region("hiprog", 0);
	map(0xc000, 0xffff).ram().w(FUNC(aaa_state::display_ram_w)).share("display");
}

void aaa_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x00).select(6).r(FUNC(aaa_state::usart_r<0>));
	map(0x01, 0x01).select(6).w(FUNC(aaa_state::usart_w<0>));
	map(0x40, 0x40).select(6).r(FUNC(aaa_state::usart_r<1>));
	map(0x41, 0x41).select(6).w(FUNC(aaa_state::usart_w<1>));
	map(0x80, 0x87).w("ctrllatch", FUNC(ls259_device::write_d7));
	map(0xc0, 0xff).r(FUNC(aaa_state::keyboard_r));
}

static INPUT_PORTS_START(aaa)
	// KR0, KR1, KR2, KR3, KR4, KR5, KR6, KR7 = connector pins W, X, Y, Z, AA, BB, CC, DD

	PORT_START("KSL2") // connector pin T
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('[') PORT_CHAR('{') PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('a') PORT_CHAR('A') PORT_CODE(KEYCODE_A)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('b') PORT_CHAR('B') PORT_CODE(KEYCODE_B)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('c') PORT_CHAR('C') PORT_CODE(KEYCODE_C)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('d') PORT_CHAR('D') PORT_CODE(KEYCODE_D)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('e') PORT_CHAR('E') PORT_CODE(KEYCODE_E)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('f') PORT_CHAR('F') PORT_CODE(KEYCODE_F)

	PORT_START("KSL3") // connector pin S
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('g') PORT_CHAR('G') PORT_CODE(KEYCODE_G)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('h') PORT_CHAR('H') PORT_CODE(KEYCODE_H)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('i') PORT_CHAR('I') PORT_CODE(KEYCODE_I)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('j') PORT_CHAR('J') PORT_CODE(KEYCODE_J)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('k') PORT_CHAR('K') PORT_CODE(KEYCODE_K)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('l') PORT_CHAR('L') PORT_CODE(KEYCODE_L)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_M)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('n') PORT_CHAR('N') PORT_CODE(KEYCODE_N)

	PORT_START("KSL4") // connector pin N
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('o') PORT_CHAR('O') PORT_CODE(KEYCODE_O)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('p') PORT_CHAR('P') PORT_CODE(KEYCODE_P)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('q') PORT_CHAR('Q') PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('r') PORT_CHAR('R') PORT_CODE(KEYCODE_R)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('s') PORT_CHAR('S') PORT_CODE(KEYCODE_S)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('t') PORT_CHAR('T') PORT_CODE(KEYCODE_T)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('u') PORT_CHAR('U') PORT_CODE(KEYCODE_U)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('v') PORT_CHAR('V') PORT_CODE(KEYCODE_V)

	PORT_START("KSL5") // connector pin M
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('w') PORT_CHAR('W') PORT_CODE(KEYCODE_W)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('x') PORT_CHAR('X') PORT_CODE(KEYCODE_X)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('y') PORT_CHAR('Y') PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('z') PORT_CHAR('Z') PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('`') PORT_CHAR('~') PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\'') PORT_CHAR('"') PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\\') PORT_CHAR('|') PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Pause") PORT_CODE(KEYCODE_LALT)

	PORT_START("KSL6") // connector pin L
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Move Up") PORT_CHAR(UCHAR_MAMEKEY(PGUP)) PORT_CODE(KEYCODE_PGUP)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR('@') PORT_CODE(KEYCODE_2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR('$') PORT_CODE(KEYCODE_4)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR('^') PORT_CODE(KEYCODE_6)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR('&') PORT_CODE(KEYCODE_7)

	PORT_START("KSL7") // connector pin K
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('8') PORT_CHAR('*') PORT_CODE(KEYCODE_8)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('9') PORT_CHAR('(') PORT_CODE(KEYCODE_9)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('-') PORT_CHAR('_') PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHAR(':') PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',') PORT_CHAR('<') PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('=') PORT_CHAR('+') PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('.') PORT_CHAR('>') PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('/') PORT_CHAR('?') PORT_CODE(KEYCODE_SLASH)

	PORT_START("KSL8") // connector pin J
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD)) PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD)) PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Move Down") PORT_CHAR(UCHAR_MAMEKEY(PGDN)) PORT_CODE(KEYCODE_PGDN)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0') PORT_CHAR(')') PORT_CODE(KEYCODE_0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad 1  SSA") PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad 3  ESA") PORT_CHAR(UCHAR_MAMEKEY(3_PAD)) PORT_CODE(KEYCODE_3_PAD)

	PORT_START("KSL9") // connector pin H
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD)) PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD)) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad 7  T-Clr") PORT_CHAR(UCHAR_MAMEKEY(7_PAD)) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad 9  T-Set") PORT_CHAR(UCHAR_MAMEKEY(9_PAD)) PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Reset")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Setup")

	PORT_START("KSL10") // connector pin F
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PF1") PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PF2") PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PF3") PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Back Space") PORT_CHAR(0x08) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PF4") PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Break")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PF5") PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PF6") PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_CODE(KEYCODE_F6)

	PORT_START("KSL11") // connector pin E
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PF7") PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_CODE(KEYCODE_F7)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PF8") PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_CODE(KEYCODE_F8)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PF9") PORT_CHAR(UCHAR_MAMEKEY(F9)) PORT_CODE(KEYCODE_F9)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PF10") PORT_CHAR(UCHAR_MAMEKEY(F10)) PORT_CODE(KEYCODE_F10)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PF11") PORT_CHAR(UCHAR_MAMEKEY(F11)) PORT_CODE(KEYCODE_F11)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PF12") PORT_CHAR(UCHAR_MAMEKEY(F12)) PORT_CODE(KEYCODE_F12)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Erase")

	PORT_START("KSL12") // connector pin D
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Edit")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Line Feed") PORT_CHAR(0x0a) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Delete") PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(']') PORT_CHAR('}') PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Insert") PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Print")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CHAR(0x0d) PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Send")

	PORT_START("KSL13") // connector pin C
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KSL14") // connector pin B
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KSL15") // connector pin A
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space Bar") PORT_CHAR(' ') PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab \u2192  Tab \u2190") PORT_CHAR(0x09) PORT_CODE(KEYCODE_TAB) // Tab →  Tab ←
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CHAR(0x1b) PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CHAR(UCHAR_MAMEKEY(LSHIFT)) PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT) // possibly the other way around
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

void aaa_state::aaa(machine_config &config)
{
	Z80(config, m_maincpu, 18.414_MHz_XTAL / 5);
	m_maincpu->set_addrmap(AS_PROGRAM, &aaa_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &aaa_state::io_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // NEC D444C x2 + 3V lithium battery

	LS259(config, "ctrllatch"); // 1B on main board

	input_merger_device &usartint(INPUT_MERGER_ANY_HIGH(config, "usartint")); // open collector
	usartint.output_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	SCN2651(config, m_usart[0], 5.0688_MHz_XTAL); // 16F on I/O board
	m_usart[0]->txrdy_handler().set("usartint", FUNC(input_merger_device::in_w<0>));
	m_usart[0]->rxrdy_handler().set("usartint", FUNC(input_merger_device::in_w<1>));
	m_usart[0]->txemt_dschg_handler().set("usartint", FUNC(input_merger_device::in_w<2>));
	m_usart[0]->txd_handler().set("printer", FUNC(rs232_port_device::write_txd));
	m_usart[0]->rts_handler().set("printer", FUNC(rs232_port_device::write_rts));
	m_usart[0]->dtr_handler().set("printer", FUNC(rs232_port_device::write_dtr));

	SCN2651(config, m_usart[1], 5.0688_MHz_XTAL); // 18F on I/O board
	m_usart[1]->txrdy_handler().set("usartint", FUNC(input_merger_device::in_w<3>));
	m_usart[1]->rxrdy_handler().set("usartint", FUNC(input_merger_device::in_w<4>));
	m_usart[1]->txemt_dschg_handler().set("usartint", FUNC(input_merger_device::in_w<5>));
	m_usart[1]->txd_handler().set("computer", FUNC(rs232_port_device::write_txd));
	m_usart[1]->rts_handler().set("computer", FUNC(rs232_port_device::write_rts));
	m_usart[1]->dtr_handler().set("computer", FUNC(rs232_port_device::write_dtr));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(18.414_MHz_XTAL, 990, 0, 800, 310, 0, 240);
	m_screen->set_screen_update(FUNC(aaa_state::screen_update));

	rs232_port_device &computer(RS232_PORT(config, "computer", default_rs232_devices, nullptr));
	computer.rxd_handler().set(m_usart[1], FUNC(scn2651_device::rxd_w));
	computer.cts_handler().set(m_usart[1], FUNC(scn2651_device::cts_w));
	computer.dsr_handler().set(m_usart[1], FUNC(scn2651_device::dsr_w));

	rs232_port_device &printer(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	printer.rxd_handler().set(m_usart[0], FUNC(scn2651_device::rxd_w));
	printer.cts_handler().set(m_usart[0], FUNC(scn2651_device::cts_w));
	printer.dsr_handler().set(m_usart[0], FUNC(scn2651_device::dsr_w));
}

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

Ann Arbor Ambassador.
Chips: Z80A, M58725P-15, 6x MK4116N-36P, 2x SCN2651C, nvram, button-battery
Crystals: 18.414, 5.0688

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

ROM_START( aaa )
	ROM_REGION(0x2000, "loprog", 0)
	ROM_LOAD( "459_1.bin",    0x0000, 0x1000, CRC(55fb3e3b) SHA1(349cd257b1468827e1b389be7c989d0e4a13a5f1) )
	ROM_LOAD( "459_3.bin",    0x1000, 0x1000, CRC(e1e84ca4) SHA1(42dc5f4211beee79178f0c03bb45c66833119eae) )

	ROM_REGION(0x2000, "hiprog", 0)
	ROM_LOAD( "459_4.bin",    0x0000, 0x2000, CRC(4038aa89) SHA1(caf33c1f87aa396860324b9c73b35e4221f03d2e) )

	ROM_REGION(0x1000, "font", 0)
	ROM_LOAD( "202510b.bin",  0x0000, 0x1000, CRC(deda4aa4) SHA1(0bce5a8dc260ba51f3e431d8da408eac1f41acf7) )
ROM_END

} // anonymous namespace


COMP( 1981, aaa, 0, 0, aaa, aaa, aaa_state, empty_init, "Ann Arbor", "Ambassador", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



abc1600.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

    Luxor ABC 1600

    How to create HDD image:
    ------------------------
    ./chdman createhd -chs 615,4,17 -ss 512 -o necd5126a.chd
    ./chdman createhd -chs 1024,8,17 -ss 512 -o micr1325a.chd

    How to format HDD:
    ------------------
    mf(2,0)
    mf(2,0)
    sas/format/format
    sa(40,0)
    y
    5
    micr1325a

    How to install OS:
    ------------------
    mf(2,0)
    mf(2,0)
    abcenix
    loadsys1
    <enter>
    <enter>

    ABCenix <= D-NIX <= AT&T Unix System V

*/

/*

    TODO:

    - abcenix panics while booting after commit 78661e9aa92c7e43c9a96039e7dfb3dabc79a287
    - systest1600 failures
        - CIO timer (works if CIO clock is 4219000)
        - DMA (expects to read 0xff from 0x18000..)
    - loadsys1 core dump (/etc/mkfs -b 1024 -v 69000 /dev/sa40)
    - crashes after reset
    - connect RS-232 printer port
    - Z80 SCC/DART interrupt chain
    - [:2a:chb] - TX FIFO is full, discarding data
        [:] SCC write 000003
        [:2a:chb] void z80scc_channel::data_write(uint8_t): Data Register Write: 17 ' '

*/

#include "emu.h"
#include "abc1600.h"

#include "machine/74259.h"
#include "softlist_dev.h"


//**************************************************************************
//  CONSTANTS / MACROS
//**************************************************************************

#define VERBOSE 0
#include "logmacro.h"


#define A1          BIT(offset, 1)
#define A2          BIT(offset, 2)
#define A4          BIT(offset, 4)
#define X11         BIT(offset, 11)
#define A1_A2       ((A1 << 1) | A2)
#define A2_A1       ((offset >> 1) & 0x03)


// external I/O
enum
{
	INP = 0,
	STAT,
	OPS
};

enum
{
	OUT = 0,
	C1 = 2,
	C2,
	C3,
	C4
};




//**************************************************************************
//  READ/WRITE HANDLERS
//**************************************************************************

//-------------------------------------------------
//  bus_r -
//-------------------------------------------------

uint8_t abc1600_state::bus_r(offs_t offset)
{
	uint8_t data = 0;

	// card select pulse
	uint8_t cs = (m_cs7 << 7) | ((offset >> 5) & 0x3f);

	m_bus0i->write_cs(cs);
	m_bus0x->write_cs(cs);
	m_bus1->write_cs(cs);
	m_bus2->write_cs(cs);

	// card select b?
	m_csb = m_bus2->csb_r();
	m_csb |= m_bus1->csb_r() << 1;
	m_csb |= m_bus0x->xcsb2_r() << 2;
	m_csb |= m_bus0x->xcsb3_r() << 3;
	m_csb |= m_bus0x->xcsb4_r() << 4;
	m_csb |= m_bus0x->xcsb5_r() << 5;
	m_csb |= m_bus0x->csb_r() << 6;
	m_csb |= m_bus0i->csb_r() << 7;

	m_bus0 = !((m_csb & 0xfc) == 0xfc);

	if (X11)
	{
		if (A4)
		{
			// EXP
			data = m_bus0x->exp_r();

			LOG("%s EXP %02x: %02x\n", machine().describe_context(), cs, data);
		}
		else
		{
			// RCSB
			if (m_bus0)
			{
				/*

				    bit     description

				    0       1
				    1       1
				    2       LXCSB2*
				    3       LXCSB3*
				    4       LXCSB4*
				    5       LXCSB5*
				    6       LCSB*-0
				    7       LCSB*-0I

				*/

				data = (m_csb & 0xfc) | 0x03;
			}
			else
			{
				/*

				    bit     description

				    0       LCSB*-2
				    1       LCSB*-1
				    2       1
				    3       1
				    4       1
				    5       1
				    6       1
				    7       1

				*/

				data = 0xfc | (m_csb & 0x03);
			}

			LOG("%s RCSB %02x\n", machine().describe_context(), data);
		}
	}
	else
	{
		data = 0xff;

		switch ((offset >> 1) & 0x07)
		{
		case INP:
			if (m_bus0)
			{
				data &= m_bus0i->read_inp();
				data &= m_bus0x->read_inp();
			}
			else
			{
				data &= m_bus1->read_inp();
				data &= m_bus2->read_inp();
			}

			LOG("%s INP %02x: %02x\n", machine().describe_context(), cs, data);
			break;

		case STAT:
			if (m_bus0)
			{
				data &= m_bus0i->read_stat();
				data &= m_bus0x->read_stat();
			}
			else
			{
				data &= m_bus1->read_stat();
				data &= m_bus2->read_stat();
			}

			LOG("%s STAT %02x: %02x\n", machine().describe_context(), cs, data);
			break;

		case OPS:
			if (m_bus0)
			{
				data &= m_bus0i->ops_r();
				data &= m_bus0x->ops_r();
			}
			else
			{
				data &= m_bus1->ops_r();
				data &= m_bus2->ops_r();
			}

			LOG("%s OPS %02x: %02x\n", machine().describe_context(), cs, data);
			break;

		default:
			LOG("%s Unmapped read from virtual I/O %06x\n", machine().describe_context(), offset);
		}
	}

	return data;
}


//-------------------------------------------------
//  bus_w -
//-------------------------------------------------

void abc1600_state::bus_w(offs_t offset, uint8_t data)
{
	uint8_t cs = (m_cs7 << 7) | ((offset >> 5) & 0x3f);

	m_bus0i->write_cs(cs);
	m_bus0x->write_cs(cs);
	m_bus1->write_cs(cs);
	m_bus2->write_cs(cs);

	switch ((offset >> 1) & 0x07)
	{
	case OUT:
		LOG("%s OUT %02x: %02x\n", machine().describe_context(), cs, data);

		if (m_bus0)
		{
			m_bus0i->write_out(data);
			m_bus0x->write_out(data);
		}
		else
		{
			m_bus1->write_out(data);
			m_bus2->write_out(data);
		}
		break;

	case C1:
		LOG("%s C1 %02x: %02x\n", machine().describe_context(), cs, data);

		if (m_bus0)
		{
			m_bus0i->write_c1(data);
			m_bus0x->write_c1(data);
		}
		else
		{
			m_bus1->write_c1(data);
			m_bus2->write_c1(data);
		}
		break;

	case C2:
		LOG("%s C2 %02x: %02x\n", machine().describe_context(), cs, data);

		if (m_bus0)
		{
			m_bus0i->write_c2(data);
			m_bus0x->write_c2(data);
		}
		else
		{
			m_bus1->write_c2(data);
			m_bus2->write_c2(data);
		}
		break;

	case C3:
		LOG("%s C3 %02x: %02x\n", machine().describe_context(), cs, data);

		if (m_bus0)
		{
			m_bus0i->write_c3(data);
			m_bus0x->write_c3(data);
		}
		else
		{
			m_bus1->write_c3(data);
			m_bus2->write_c3(data);
		}
		break;

	case C4:
		LOG("%s C4 %02x: %02x\n", machine().describe_context(), cs, data);

		if (m_bus0)
		{
			m_bus0i->write_c4(data);
			m_bus0x->write_c4(data);
		}
		else
		{
			m_bus1->write_c4(data);
			m_bus2->write_c4(data);
		}
		break;

	default:
		LOG("%s Unmapped write %02x to virtual I/O %06x\n", machine().describe_context(), data, offset);
	}
}


//-------------------------------------------------
//  fw0_w -
//-------------------------------------------------

void abc1600_state::fw0_w(uint8_t data)
{
	/*

	    bit     description

	    0       SEL1
	    1       SEL2
	    2       SEL3
	    3       MOTOR
	    4       LC/PC
	    5       LC/PC
	    6
	    7

	*/

	LOG("%s FW0 %02x\n", machine().describe_context(), data);

	// drive select
	floppy_image_device *floppy = nullptr;

	for (int n = 0; n < 3; n++)
		if (BIT(data, n))
			floppy = m_floppy[n]->get_device();

	m_fdc->set_floppy(floppy);

	// floppy motor
	if (floppy) floppy->mon_w(!BIT(data, 3));
}


//-------------------------------------------------
//  fw1_w -
//-------------------------------------------------

void abc1600_state::fw1_w(uint8_t data)
{
	/*

	    bit     description

	    0       MR (FD1797)
	    1       DDEN (FD1797, 9229B)
	    2       HLT (FD1797)
	    3       MINI (9229B)
	    4       HLD (9229B)
	    5       P0 (9229B)
	    6       P1 (9229B)
	    7       P2 (9229B)

	*/

	LOG("%s FW1 %02x\n", machine().describe_context(), data);

	// FDC master reset
	m_fdc->mr_w(BIT(data, 0));

	// density select
	m_fdc->dden_w(BIT(data, 1));
}


//-------------------------------------------------
//  cs7_w - CS7 output handler
//-------------------------------------------------

void abc1600_state::cs7_w(int state)
{
	LOG("%s CS7 %d\n", machine().describe_context(), state);

	m_cs7 = state;
}


//-------------------------------------------------
//  btce_w - _BTCE output handler
//-------------------------------------------------

void abc1600_state::btce_w(int state)
{
	LOG("%s _BTCE %d\n", machine().describe_context(), state);

	m_btce = state;
}


//-------------------------------------------------
//  atce_w - _ATCE output handler
//-------------------------------------------------

void abc1600_state::atce_w(int state)
{
	LOG("%s _ATCE %d\n", machine().describe_context(), state);

	m_atce = state;
}


//-------------------------------------------------
//  dmadis_w - _DMADIS output handler
//-------------------------------------------------

void abc1600_state::dmadis_w(int state)
{
	LOG("%s _DMADIS %d\n", machine().describe_context(), state);

	m_dmadis = state;
}


//-------------------------------------------------
//  sysscc_w - SYSSCC output handler
//-------------------------------------------------

void abc1600_state::sysscc_w(int state)
{
	LOG("%s SYSSCC %d\n", machine().describe_context(), state);

	m_sysscc = state;

	m_cio->pb5_w(!state);

	update_drdy1(0);
}


//-------------------------------------------------
//  sysfs_w - SYSFS output handler
//-------------------------------------------------

void abc1600_state::sysfs_w(int state)
{
	LOG("%s SYSFS %d\n", machine().describe_context(), state);

	m_sysfs = state;

	m_cio->pb6_w(!state);

	update_drdy0(0);
}



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

//-------------------------------------------------
//  ADDRESS_MAP( abc1600_mem )
//-------------------------------------------------

void abc1600_state::abc1600_mem(address_map &map)
{
	map(0x00000, 0xfffff).rw(ABC1600_MAC_TAG, FUNC(abc1600_mac_device::read), FUNC(abc1600_mac_device::write));
}


//-------------------------------------------------
//  ADDRESS_MAP( mac_mem )
//-------------------------------------------------

void abc1600_state::mac_mem(address_map &map)
{
	map(0x000000, 0x0fffff).ram();
	map(0x100000, 0x17ffff).m(ABC1600_MOVER_TAG, FUNC(abc1600_mover_device::vram_map));
	map(0x1fe000, 0x1fefff).rw(FUNC(abc1600_state::bus_r), FUNC(abc1600_state::bus_w));
	map(0x1ff000, 0x1ff000).mirror(0xf9).rw(m_fdc, FUNC(fd1797_device::status_r), FUNC(fd1797_device::cmd_w));
	map(0x1ff002, 0x1ff002).mirror(0xf9).rw(m_fdc, FUNC(fd1797_device::track_r), FUNC(fd1797_device::track_w));
	map(0x1ff004, 0x1ff004).mirror(0xf9).rw(m_fdc, FUNC(fd1797_device::sector_r), FUNC(fd1797_device::sector_w));
	map(0x1ff006, 0x1ff006).mirror(0xf9).rw(m_fdc, FUNC(fd1797_device::data_r), FUNC(fd1797_device::data_w));
	map(0x1ff100, 0x1ff101).mirror(0xfe).m(ABC1600_MOVER_TAG, FUNC(abc1600_mover_device::crtc_map));
	map(0x1ff200, 0x1ff207).mirror(0xf8).rw(FUNC(abc1600_state::dart_r), FUNC(abc1600_state::dart_w));
	map(0x1ff300, 0x1ff300).mirror(0xff).rw(m_dma0, FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	map(0x1ff400, 0x1ff400).mirror(0xff).rw(m_dma1, FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	map(0x1ff500, 0x1ff500).mirror(0xff).rw(m_dma2, FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	map(0x1ff600, 0x1ff607).mirror(0xf8).rw(FUNC(abc1600_state::scc_r), FUNC(abc1600_state::scc_w));
	map(0x1ff700, 0x1ff707).mirror(0xf8).rw(FUNC(abc1600_state::cio_r), FUNC(abc1600_state::cio_w));
	map(0x1ff800, 0x1ff8ff).m(ABC1600_MOVER_TAG, FUNC(abc1600_mover_device::iowr0_map));
	map(0x1ff900, 0x1ff9ff).m(ABC1600_MOVER_TAG, FUNC(abc1600_mover_device::iowr1_map));
	map(0x1ffa00, 0x1ffaff).m(ABC1600_MOVER_TAG, FUNC(abc1600_mover_device::iowr2_map));
	map(0x1ffb00, 0x1ffb00).mirror(0x7e).w(FUNC(abc1600_state::fw0_w));
	map(0x1ffb01, 0x1ffb01).mirror(0x7e).w(FUNC(abc1600_state::fw1_w));
	map(0x1ffd00, 0x1ffd07).mirror(0xf8).w(ABC1600_MAC_TAG, FUNC(abc1600_mac_device::dmamap_w));
	map(0x1ffe00, 0x1ffe00).mirror(0xff).w("spec_contr_reg", FUNC(ls259_device::write_nibble_d3));
}



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

//-------------------------------------------------
//  INPUT_CHANGED_MEMBER( reset )
//-------------------------------------------------

INPUT_CHANGED_MEMBER( abc1600_state::reset )
{
	if (!oldval && newval)
	{
		machine_reset();
	}

	m_mac->rstbut_w(newval);
}


//-------------------------------------------------
//  INPUT_PORTS( abc1600 )
//-------------------------------------------------

static INPUT_PORTS_START( abc1600 )
	// keyboard inputs defined in machine/abc99.cpp

	PORT_START("RESET")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Reset") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(abc1600_state::reset), 0)
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  Z80DMA 0
//-------------------------------------------------

void abc1600_state::update_br()
{
	// _BR = !_DMADIS || (_DBRQ0 || _DBRQ1 || _DBRQ2)
	// _IOC = IORQ delayed by 1 clock, or IORQ preceded by 1 clock on MINT2 or MINT5
	// _BGACK = !_BR && !_BG && _IOC
	// DMA0.BAI = _BGACK

	// workaround for floppy DMA, this should use the 68000 BR line instead
	m_dma0->bai_w(!m_dmadis || m_dbrq0 || m_dbrq1 || m_dbrq2);
}

void abc1600_state::update_pren0(int state)
{
	if (m_sysfs)
	{
		// floppy
		m_dma0->iei_w(0);
	}
	else
	{
		// BUS0I/BUS0X
		bool pren0 = m_bus0i->pren_r() && m_bus0x->pren_r();

		m_dma0->iei_w(!pren0);
	}
}

void abc1600_state::update_drdy0(int state)
{
	if (m_sysfs)
	{
		// floppy
		m_dma0->rdy_w(!m_fdc->drq_r());
	}
	else
	{
		// BUS0I/BUS0X
		int trrq0 = m_bus0i->trrq_r() && m_bus0x->trrq_r();

		m_dma0->rdy_w(trrq0);
	}
}


//-------------------------------------------------
//  Z80DMA 1
//-------------------------------------------------

void abc1600_state::update_pren1(int state)
{
	if (m_sysscc)
	{
		// SCC
		m_dma1->iei_w(1);
	}
	else
	{
		// BUS1
		m_dma1->iei_w(!m_bus1->pren_r());
	}
}

void abc1600_state::update_drdy1(int state)
{
	if (m_sysscc)
	{
		// SCC
		m_dma1->rdy_w(m_sccrq_a && m_sccrq_b);
	}
	else
	{
		// BUS1
		m_dma1->rdy_w(m_bus1->trrq_r());
	}
}

//-------------------------------------------------
//  Z80DART
//-------------------------------------------------

uint8_t abc1600_state::dart_r(offs_t offset)
{
	return m_dart->ba_cd_r(A2_A1 ^ 0x03);
}

void abc1600_state::dart_w(offs_t offset, uint8_t data)
{
	m_dart->ba_cd_w(A2_A1 ^ 0x03, data);
}

//-------------------------------------------------
//  SCC8530
//-------------------------------------------------

uint8_t abc1600_state::scc_r(offs_t offset)
{
	return m_scc->ab_dc_r(A2_A1);
}

void abc1600_state::scc_w(offs_t offset, uint8_t data)
{
	m_scc->ab_dc_w(A2_A1, data);
}


//-------------------------------------------------
//  Z8536
//-------------------------------------------------

uint8_t abc1600_state::cio_r(offs_t offset)
{
	return m_cio->read(A2_A1);
}

void abc1600_state::cio_w(offs_t offset, uint8_t data)
{
	m_cio->write(A2_A1, data);
}

uint8_t abc1600_state::cio_pa_r()
{
	/*

	    bit     description

	    PA0     BUS2
	    PA1     BUS1
	    PA2     BUS0X*2
	    PA3     BUS0X*3
	    PA4     BUS0X*4
	    PA5     BUS0X*5
	    PA6     BUS0X
	    PA7     BUS0I

	*/

	uint8_t data = 0;

	data |= m_bus2->irq_r();
	data |= m_bus1->irq_r() << 1;
	data |= m_bus0x->xint2_r() << 2;
	data |= m_bus0x->xint3_r() << 3;
	data |= m_bus0x->xint4_r() << 4;
	data |= m_bus0x->xint5_r() << 5;
	data |= m_bus0x->irq_r() << 6;
	data |= m_bus0i->irq_r() << 7;

	return data;
}

uint8_t abc1600_state::cio_pb_r()
{
	/*

	    bit     description

	    PB0
	    PB1     POWERFAIL
	    PB2
	    PB3
	    PB4     MINT
	    PB5     _PREN-1
	    PB6     _PREN-0
	    PB7     FINT

	*/

	uint8_t data = 0;

	data |= !m_sysscc << 5;
	data |= !m_sysfs << 6;

	// floppy interrupt
	data |= m_fdc->intrq_r() << 7;

	return data;
}

void abc1600_state::cio_pb_w(uint8_t data)
{
	/*

	    bit     description

	    PB0     PRBR
	    PB1
	    PB2
	    PB3
	    PB4
	    PB5
	    PB6
	    PB7

	*/

	// printer baudrate
	int prbr = BIT(data, 0);

	m_dart->txca_w(prbr);
	m_dart->rxca_w(prbr);
}

uint8_t abc1600_state::cio_pc_r()
{
	/*

	    bit     description

	    PC0     1
	    PC1     DATA IN
	    PC2     1
	    PC3     1

	*/

	uint8_t data = 0x0d;

	// data in
	data |= (m_rtc->dio_r() || m_nvram->do_r()) << 1;

	return data;
}

void abc1600_state::cio_pc_w(uint8_t data)
{
	/*

	    bit     description

	    PC0     CLOCK
	    PC1     DATA OUT
	    PC2     RTC CS
	    PC3     NVRAM CS

	*/

	int clock = BIT(data, 0);
	int data_out = BIT(data, 1);
	int rtc_cs = BIT(data, 2);
	int nvram_cs = BIT(data, 3);

	LOG("CLK %u DATA %u RTC %u NVRAM %u\n", clock, data_out, rtc_cs, nvram_cs);

	m_rtc->cs_w(rtc_cs);
	m_rtc->dio_w(data_out);
	m_rtc->clk_w(clock);

	m_nvram->cs_w(nvram_cs);
	m_nvram->di_w(data_out);
	m_nvram->sk_w(clock);
}

static void abc1600_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

void abc1600_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_ABC1600_FORMAT);
}



//-------------------------------------------------
//  ABC1600BUS_INTERFACE( abcbus_intf )
//-------------------------------------------------

void abc1600_state::nmi_w(int state)
{
	if (state == ASSERT_LINE)
	{
		m_maincpu->set_input_line(M68K_IRQ_7, ASSERT_LINE);
	}
}


//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

void abc1600_state::cpu_space_map(address_map &map)
{
	map(0xffff0, 0xfffff).m(m_maincpu, FUNC(m68008_device::autovectors_map));
	map(0xffff5, 0xffff5).lr8(NAME([this]() -> u8 { return m_cio->intack_r(); }));
	map(0xffffb, 0xffffb).lr8(NAME([this]() -> u8 { return m_dart->m1_r(); }));
	map(0xfffff, 0xfffff).lr8(NAME([this]() -> u8 { m_maincpu->set_input_line(M68K_IRQ_7, CLEAR_LINE); return m68008_device::autovector(7); }));
}

void abc1600_state::machine_start()
{
	// state saving
	save_item(NAME(m_dmadis));
	save_item(NAME(m_sysscc));
	save_item(NAME(m_sysfs));
	save_item(NAME(m_dbrq0));
	save_item(NAME(m_dbrq1));
	save_item(NAME(m_dbrq2));
	save_item(NAME(m_cs7));
	save_item(NAME(m_bus0));
	save_item(NAME(m_csb));
	save_item(NAME(m_atce));
	save_item(NAME(m_btce));
	save_item(NAME(m_sccrq_a));
	save_item(NAME(m_sccrq_b));
	save_item(NAME(m_scc_irq));
	save_item(NAME(m_dart_irq));
}


void abc1600_state::machine_reset()
{
	// clear floppy registers
	fw0_w(0);
	fw1_w(0);

	// clear NMI
	m_maincpu->set_input_line(M68K_IRQ_7, CLEAR_LINE);
}



//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  machine_config( abc1600 )
//-------------------------------------------------

void abc1600_state::abc1600(machine_config &config)
{
	// basic machine hardware
	M68008(config, m_maincpu, 64_MHz_XTAL / 8);
	m_maincpu->set_addrmap(AS_PROGRAM, &abc1600_state::abc1600_mem);
	m_maincpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &abc1600_state::cpu_space_map);
	//m_maincpu->out_bg_callback().set(FUNC(abc1600_state::bg_w));

	// video hardware
	ABC1600_MOVER(config, ABC1600_MOVER_TAG, 0);

	// devices
	ABC1600_MAC(config, m_mac, 0);
	m_mac->set_addrmap(AS_PROGRAM, &abc1600_state::mac_mem);
	m_mac->set_cpu(m_maincpu);
	m_mac->in_tren0_cb().set(m_bus0i, FUNC(abcbus_slot_device::read_tren)); // TODO bus0x
	m_mac->out_tren0_cb().set(m_bus0i, FUNC(abcbus_slot_device::write_tren)); // TODO bus0x
	m_mac->in_tren1_cb().set(m_bus1, FUNC(abcbus_slot_device::read_tren));
	m_mac->out_tren1_cb().set(m_bus1, FUNC(abcbus_slot_device::write_tren));
	m_mac->in_tren2_cb().set(m_bus2, FUNC(abcbus_slot_device::read_tren));
	m_mac->out_tren2_cb().set(m_bus2, FUNC(abcbus_slot_device::write_tren));

	ls259_device &spec_contr_reg(LS259(config, "spec_contr_reg")); // Special Control Register @ 13E
	spec_contr_reg.q_out_cb<0>().set(FUNC(abc1600_state::cs7_w));
	spec_contr_reg.q_out_cb<2>().set(FUNC(abc1600_state::btce_w));
	spec_contr_reg.q_out_cb<3>().set(FUNC(abc1600_state::atce_w));
	spec_contr_reg.q_out_cb<4>().set(m_mac, FUNC(abc1600_mac_device::partst_w));
	spec_contr_reg.q_out_cb<5>().set(FUNC(abc1600_state::dmadis_w));
	spec_contr_reg.q_out_cb<6>().set(FUNC(abc1600_state::sysscc_w));
	spec_contr_reg.q_out_cb<7>().set(FUNC(abc1600_state::sysfs_w));

	Z80DMA(config, m_dma0, 64_MHz_XTAL / 16);
	m_dma0->out_busreq_callback().set(FUNC(abc1600_state::dbrq0_w));
	m_dma0->out_bao_callback().set(m_dma1, FUNC(z80dma_device::bai_w));
	m_dma0->in_mreq_callback().set(m_mac, FUNC(abc1600_mac_device::dma0_mreq_r));
	m_dma0->out_mreq_callback().set(m_mac, FUNC(abc1600_mac_device::dma0_mreq_w));
	m_dma0->out_ieo_callback().set([this](int state) { m_bus0i->prac_w(state); m_bus0x->prac_w(state); }).exor(1);
	m_dma0->in_iorq_callback().set(m_mac, FUNC(abc1600_mac_device::dma0_iorq_r));
	m_dma0->out_iorq_callback().set(m_mac, FUNC(abc1600_mac_device::dma0_iorq_w));

	Z80DMA(config, m_dma1, 64_MHz_XTAL / 16);
	m_dma1->out_busreq_callback().set(FUNC(abc1600_state::dbrq1_w));
	m_dma1->out_bao_callback().set(m_dma2, FUNC(z80dma_device::bai_w));
	m_dma1->in_mreq_callback().set(m_mac, FUNC(abc1600_mac_device::dma1_mreq_r));
	m_dma1->out_mreq_callback().set(m_mac, FUNC(abc1600_mac_device::dma1_mreq_w));
	m_dma1->out_ieo_callback().set(m_bus1, FUNC(abcbus_slot_device::prac_w)).exor(1);
	m_dma1->in_iorq_callback().set(m_mac, FUNC(abc1600_mac_device::dma1_iorq_r));
	m_dma1->out_iorq_callback().set(m_mac, FUNC(abc1600_mac_device::dma1_iorq_w));

	Z80DMA(config, m_dma2, 64_MHz_XTAL / 16);
	m_dma2->out_busreq_callback().set(FUNC(abc1600_state::dbrq2_w));
	m_dma2->in_mreq_callback().set(m_mac, FUNC(abc1600_mac_device::dma2_mreq_r));
	m_dma2->out_mreq_callback().set(m_mac, FUNC(abc1600_mac_device::dma2_mreq_w));
	m_dma2->out_ieo_callback().set(m_bus2, FUNC(abcbus_slot_device::prac_w)).exor(1);
	m_dma2->in_iorq_callback().set(m_mac, FUNC(abc1600_mac_device::dma2_iorq_r));
	m_dma2->out_iorq_callback().set(m_mac, FUNC(abc1600_mac_device::dma2_iorq_w));

	Z80DART(config, m_dart, 64_MHz_XTAL / 16);
	m_dart->out_int_callback().set(FUNC(abc1600_state::dart_irq_w));
	m_dart->out_txda_callback().set(RS232_PR_TAG, FUNC(rs232_port_device::write_txd));
	//m_dart->out_dtra_callback().set(RS232_PR_TAG, FUNC(rs232_port_device::write_dcd));
	//m_dart->out_rtsa_callback().set(RS232_PR_TAG, FUNC(rs232_port_device::write_cts));
	m_dart->out_txdb_callback().set(ABC_KEYBOARD_PORT_TAG, FUNC(abc_keyboard_port_device::txd_w));

	abc_keyboard_port_device &kb(ABC_KEYBOARD_PORT(config, ABC_KEYBOARD_PORT_TAG, abc_keyboard_devices, "abc99"));
	kb.out_rx_handler().set(m_dart, FUNC(z80dart_device::rxb_w));
	kb.out_trxc_handler().set(m_dart, FUNC(z80dart_device::rxtxcb_w));
	kb.out_keydown_handler().set(m_dart, FUNC(z80dart_device::dcdb_w));

	rs232_port_device &rs232pr(RS232_PORT(config, RS232_PR_TAG, default_rs232_devices, nullptr));
	rs232pr.rxd_handler().set(m_dart, FUNC(z80dart_device::rxa_w));
	//rs232pr.rts_handler().set(m_dart, FUNC(z80dart_device::ctsa_w));
	//rs232pr.dtr_handler().set(m_dart, FUNC(z80dart_device::dcda_w));

	SCC8530(config, m_scc, 64_MHz_XTAL / 16);
	m_scc->out_int_callback().set(FUNC(abc1600_state::scc_irq_w));
	m_scc->out_wreqa_callback().set(FUNC(abc1600_state::sccrq_a_w));
	m_scc->out_wreqb_callback().set(FUNC(abc1600_state::sccrq_b_w));
	m_scc->out_txda_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_txd));
	m_scc->out_dtra_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_dtr));
	m_scc->out_rtsa_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_rts));
	m_scc->out_txdb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_txd));
	m_scc->out_dtrb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_dtr));
	m_scc->out_rtsb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232a(RS232_PORT(config, RS232_A_TAG, default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_scc, FUNC(scc8530_device::rxa_w));
	rs232a.cts_handler().set(m_scc, FUNC(scc8530_device::ctsa_w));
	rs232a.dcd_handler().set(m_scc, FUNC(scc8530_device::dcda_w));
	rs232a.ri_handler().set(m_scc, FUNC(scc8530_device::synca_w));

	rs232_port_device &rs232b(RS232_PORT(config, RS232_B_TAG, default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_scc, FUNC(scc8530_device::rxb_w));
	rs232b.cts_handler().set(m_scc, FUNC(scc8530_device::ctsb_w));
	rs232b.dcd_handler().set(m_scc, FUNC(scc8530_device::dcdb_w));
	rs232b.ri_handler().set(m_scc, FUNC(scc8530_device::syncb_w));

	Z8536(config, m_cio, 64_MHz_XTAL / 16);
	m_cio->irq_wr_cb().set_inputline(MC68008P8_TAG, M68K_IRQ_2);
	m_cio->pa_rd_cb().set(FUNC(abc1600_state::cio_pa_r));
	m_cio->pb_rd_cb().set(FUNC(abc1600_state::cio_pb_r));
	m_cio->pb_wr_cb().set(FUNC(abc1600_state::cio_pb_w));
	m_cio->pc_rd_cb().set(FUNC(abc1600_state::cio_pc_r));
	m_cio->pc_wr_cb().set(FUNC(abc1600_state::cio_pc_w));

	NMC9306(config, m_nvram, 0);

	E0516(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->outsel_rd_cb().set_constant(0);

	FD1797(config, m_fdc, 64_MHz_XTAL / 64); // clocked by 9229B
	m_fdc->intrq_wr_callback().set(m_cio, FUNC(z8536_device::pb7_w));
	m_fdc->drq_wr_callback().set(FUNC(abc1600_state::update_drdy0));

	FLOPPY_CONNECTOR(config, m_floppy[0], abc1600_floppies, nullptr, abc1600_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], abc1600_floppies, nullptr, abc1600_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[2], abc1600_floppies, "525qd", abc1600_state::floppy_formats).enable_sound(true);

	ABCBUS_SLOT(config, m_bus0i, 64_MHz_XTAL / 16, abc1600bus_cards, nullptr);
	m_bus0i->irq_callback().set(m_cio, FUNC(z8536_device::pa7_w));
	m_bus0i->pren_callback().set(FUNC(abc1600_state::update_pren0));
	m_bus0i->trrq_callback().set(FUNC(abc1600_state::update_drdy0));

	ABCBUS_SLOT(config, m_bus0x, 64_MHz_XTAL / 16, abc1600bus_cards, nullptr);
	m_bus0x->irq_callback().set(m_cio, FUNC(z8536_device::pa6_w));
	m_bus0x->nmi_callback().set(FUNC(abc1600_state::nmi_w));
	m_bus0x->xint2_callback().set(m_cio, FUNC(z8536_device::pa2_w));
	m_bus0x->xint3_callback().set(m_cio, FUNC(z8536_device::pa3_w));
	m_bus0x->xint4_callback().set(m_cio, FUNC(z8536_device::pa4_w));
	m_bus0x->xint5_callback().set(m_cio, FUNC(z8536_device::pa5_w));
	m_bus0x->pren_callback().set(FUNC(abc1600_state::update_pren0));
	m_bus0x->trrq_callback().set(FUNC(abc1600_state::update_drdy0));

	ABCBUS_SLOT(config, m_bus1, 64_MHz_XTAL / 16, abc1600bus_cards, nullptr);
	m_bus1->irq_callback().set(m_cio, FUNC(z8536_device::pa1_w));
	m_bus1->pren_callback().set(FUNC(abc1600_state::update_pren1));
	m_bus1->trrq_callback().set(FUNC(abc1600_state::update_drdy1));

	ABCBUS_SLOT(config, m_bus2, 64_MHz_XTAL / 16, abc1600bus_cards, "4105");
	m_bus2->irq_callback().set(m_cio, FUNC(z8536_device::pa0_w));
	m_bus2->pren_callback().set(m_dma2, FUNC(z80dma_device::iei_w)).exor(1);
	m_bus2->trrq_callback().set(m_dma2, FUNC(z80dma_device::rdy_w));

	// internal ram
	RAM(config, RAM_TAG).set_default_size("1M");

	// software list
	SOFTWARE_LIST(config, "flop_list").set_original("abc1600_flop");
	SOFTWARE_LIST(config, "hdd_list").set_original("abc1600_hdd");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( abc1600 )
//-------------------------------------------------

ROM_START( abc1600 )
	ROM_REGION( 0x860, "plds", 0 )
	ROM_LOAD( "1020 6490349-01.8b",  0x104, 0x104, CRC(1fa065eb) SHA1(20a95940e39fa98e97e59ea1e548ac2e0c9a3444) ) // BUS INTERFACE X35
	ROM_LOAD( "1021 6490350-01.5d",  0x208, 0x104, CRC(96f6f44b) SHA1(12d1cd153dcc99d1c4a6c834122f370d49723674) ) // X36 BOOT DECODE, MAP ACK PLUS INT PRIO
	ROM_LOAD( "1023 6490352-01.11e", 0x410, 0x104, CRC(a2f350ac) SHA1(77e08654a197080fa2111bc3031cd2c7699bf82b) ) // X36 IO INTERFACE STROBE HANDLER
	ROM_LOAD( "1024 6490353-01.12e", 0x514, 0x104, CRC(67f1328a) SHA1(b585495fe14a7ae2fbb29f722dca106d59325002) ) // X36 CHANNEL SELECT TIMER
	ROM_LOAD( "1025 6490354-01.6e",  0x618, 0x104, CRC(9bda0468) SHA1(ad373995dcc18532274efad76fa80bd13c23df25) ) // X36 Z80A-DMA INTERFACER
	ROM_LOAD( "1031", 0x71c, 0x144, CRC(0aedc9fc) SHA1(2cbbc7d5cb16b410d296062feb77ed26ff01af24) ) // NS32081 IN ABC1600

	ROM_REGION( 0x20, NMC9306_TAG, 0 )
	ROM_LOAD( "nmc9306.14c", 0x00, 0x20, CRC(1cb59b6e) SHA1(3c955a667034db86fa1b848f0c0317157a3a48f6) )
ROM_END



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY  FULLNAME    FLAGS
COMP( 1985, abc1600, 0,      0,      abc1600, abc1600, abc1600_state, empty_init, "Luxor", "ABC 1600", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



abc80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

Luxor ABC 80

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

55 10470-02

          CN1               CN2                                                CN5
  SW1   |-----|  |------------------------|                                  |-----|
|-------|     |--|                        |----------------------------------|     |---|
|                                                             CN3       CN4            |
|                                                    7912                              |
|            MC1488                                                                    |
|   MC1489                                           7812                              |
|            LS245                     LS138                                           |
|                                                   |-------|                          |
|   |-----CN6-----|   LS241   LS241    LS138  LS32  |SN76477| LS04    LM339            |
|                                                   |-------|                          |
|   |--------------|  |------------|   PROM0  LS132   LS273   7406             LS08    |
|   |   Z80A PIO   |  |    Z80A    |                                                   |
|   |--------------|  |------------|   LS04   LS74A   LS86    LS161   LS166    74393   |
|                                                                                      |
|     ROM0   LS107    4116    4116     LS10   LS257   LS74A   LS08    LS107    PROM2   |
|                                                                                      |
|     ROM2   LS257    4116    4116     LS139  74393   LS107   LS32    LS175    74393   |
|                                                                                      |
|     ROM1   LS257    4116    4116     LS08   LS283   LS10    LS32    PROM1    74393   |
|                                                                                      |
|     ROM3   LS257    4116    4116     LS257  74393   LS375   74S263  LS145    PROM4   |
|                                                                                      |
|     SB1    SB2      4045    4045     LS257  LS245   LS375   LS273   LS166    PROM3   |
|--------------------------------------------------------------------------------------|

Notes:
    All IC's shown.

    ROM0-3  - Texas Instruments TMS4732 4Kx8 General Purpose Mask Programmable ROM
    PROM0-2 - Philips Semiconductors N82S129 256x4 TTL Bipolar PROM
    PROM3-4 - Philips Semiconductors N82S131 512x4 TTL Bipolar PROM
    4116    - Texas Instruments TMS4116-25 16Kx1 Dynamic RAM
    4045    - Texas Instruments TMS4045-15 1Kx4 General Purpose Static RAM with Multiplexed I/O
    Z80A    - Sharp LH0080A Z80A CPU
    Z80APIO - SGS-Thomson Z8420AB1 Z80A PIO
    SN76477 - Texas Instruments SN76477N Complex Sound Generator
    74S263  - Texas Instruments SN74S263N Row Output Character Generator
    MC1488  - Texas Instruments MC1488 Quadruple Line Driver
    MC1489  - Texas Instruments MC1489 Quadruple Line Receiver
    CN1     - RS-232 connector
    CN2     - ABC bus connector (DIN 41612)
    CN3     - video connector
    CN4     - cassette motor connector
    CN5     - cassette connector
    CN6     - keyboard connector
    SW1     - reset switch
    SB1     - solder bridge for A11/CS1 to ROM1/ROM3
    SB2     - solder bridge for A11/CS1 to ROM0/ROM2


Calculate ROM checksum:

10 FOR I%=0% TO 16383%
20 A%=A%+PEEK(I%)
30 NEXT I%
40 ;A%
RUN

*/

/*

Luxor ABC 80 with TKN 80

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

55 10470-02

          CN1               CN2                                                CN5
  SW1   |-----|  |------------------------|                                  |-----|
|-------|     |--|                        |----------------------------------|     |---|
|                                                             CN3       CN4            |
|                                                    7912                              |
|            MC1488                                                                    |
|   MC1489                                           7812                              |
|            LS245                     LS138                                           |
|                                                   |-------|                          |
|   |-----CN6-----|   LS241   LS241    LS138  LS32  |SN76477| LS04    LM339            |
|                                                   |-------|                          |
|   |--------------|  |------------|   PROM0  LS132   LS273   7406             LS08    |
|   |   Z80A PIO   |  |    Z80A    |                                                   |
|   |--------------|  |------------|   LS04   LS74A   LS86    LS161   LS166    74393   |
|                                                                                      |
|     ROM0   LS107    4116    4116     LS10   LS257   LS74A   LS08    LS107    PROM2   |
|                                                                                      |
|     ROM2   LS257    4116    4116     LS139  74393   LS107   LS32    LS175    74393   |
|                                                                                      |
|     ROM1   LS257    4116    4116     LS08   LS283   LS10    LS32    PROM1    74393   |
|-----------|                                                                          |
|     ROM3  |       |------------------------------------------------|LS145    PROM4   |
|           |-------|                                                |                 |
|     LS32   LS00 LS257  LS257  LS257  LS00 LS04  LS02  LS51  LS374  |LS166    PROM3   |
|                                                                    |-----------------|
|     ROM4   LS373    LS244   6116    LS74A LS74A LS163 LS163 LS374  |
|                                                             LS374  |
|--------------------------------------------------------------------|

Notes:
    All IC's shown.

    PROM0   - MMI 63S141N 512x4 TTL Bipolar PROM
    ROM4    - 4Kx8 EPROM "TKN80-III"
    6116    - Hitachi HM6116P-4 2Kx8 Static RAM


Switch to 40 column mode:

;INP(3);CHR$(12)

Switch to 80 column mode:

;INP(4);CHR$(12)

*/

/*

    TODO:

    - PWM sound in ABC-klubben/abc80/grafik/flagga.bac
    - proper keyboard controller emulation
    - GeJo 80-column card
    - 64K RAM expansions
        - Mikrodatorn
        - MYAB UNI-80
    - Metric ABC CAD 1000

*/

#include "emu.h"
#include "abc80.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


//**************************************************************************
//  CONSTANTS
//**************************************************************************

#define LOG 0



//**************************************************************************
//  MEMORY MANAGEMENT
//**************************************************************************

//-------------------------------------------------
//  read -
//-------------------------------------------------

u8 abc80_state::read(offs_t offset)
{
	u8 data = 0xff;
	u8 mmu = m_mmu_rom->base()[0x40 | (offset >> 10)];

	if (!(mmu & MMU_XM))
	{
		data = m_bus->xmemfl_r(offset);
	}
	else if (!(mmu & MMU_ROM))
	{
		data = m_rom->base()[offset & 0x3fff];
	}
	else if (mmu & MMU_VRAMS)
	{
		data = m_video_ram[offset & 0x3ff];
	}
	else if (!(mmu & MMU_RAM))
	{
		data = m_ram->pointer()[offset & 0x3fff];
	}

	return data;
}

u8 tkn80_state::read(offs_t offset)
{
	/*

	    TKN 000-3ff -> ZA3506 000-3ff (9913/10042)
	    TKN 400-7ff -> ZA3507 000-3ff (9913/10042)
	    TKN 800-cff -> ZA3506 000-3ff (11273)
	    TKN c00-FFF -> ZA3507 000-3ff (11273)

	*/

	u8 data = 0xff;
	u8 mmu = m_mmu_rom->base()[0x40 | (offset >> 10)];

	if (offset < 0x400)
	{
		if (m_80)
			data = m_rom_e->base()[m_rom_offset | (offset & 0x3ff)];
		else
			data = m_rom->base()[offset & 0x3fff];
	}
	else if (offset >= 0x400 && offset < 0x2000)
	{
		data = m_rom->base()[offset & 0x3fff];
	}
	else if (offset >= 0x2000 && offset < 0x2400)
	{
		if (m_80)
			data = m_rom_e->base()[m_rom_offset | 0x400 | (offset & 0x3ff)];
		else
			data = m_rom->base()[offset & 0x3fff];
	}
	else if (offset >= 0x2400 && offset < 0x4000)
	{
		data = m_rom->base()[offset & 0x3fff];
	}
	else if (offset >= 0x5800 && offset < 0x6000)
	{
		data = m_char_ram[offset & 0x7ff];
	}
	else if (offset >= 0x7c00 && offset < 0x8000)
	{
		data = m_char_ram[offset & 0x7ff];
	}
	else if (!(mmu & MMU_XM))
	{
		data = m_bus->xmemfl_r(offset);
	}
	else if (!(mmu & MMU_RAM))
	{
		data = m_ram->pointer()[offset & 0x3fff];
	}

	return data;
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

void abc80_state::write(offs_t offset, u8 data)
{
	u8 mmu = m_mmu_rom->base()[0x40 | (offset >> 10)];

	if (!(mmu & MMU_XM))
	{
		m_bus->xmemw_w(offset, data);
	}
	else if (mmu & MMU_VRAMS)
	{
		m_video_ram[offset & 0x3ff] = data;
	}
	else if (!(mmu & MMU_RAM))
	{
		m_ram->pointer()[offset & 0x3fff] = data;
	}
}

void tkn80_state::write(offs_t offset, u8 data)
{
	u8 mmu = m_mmu_rom->base()[0x40 | (offset >> 10)];

	if (offset >= 0x5800 && offset < 0x6000)
	{
		m_char_ram[offset & 0x7ff] = data;
	}
	else if (offset >= 0x7c00 && offset < 0x8000)
	{
		m_char_ram[offset & 0x7ff] = data;
	}
	else if (!(mmu & MMU_XM))
	{
		m_bus->xmemw_w(offset, data);
	}
	else if (!(mmu & MMU_RAM))
	{
		m_ram->pointer()[offset & 0x3fff] = data;
	}
}



//**************************************************************************
//  SOUND
//**************************************************************************

//-------------------------------------------------
//  csg_w -
//-------------------------------------------------

void abc80_state::csg_w(u8 data)
{
	m_csg->enable_w(!BIT(data, 0));
	m_csg->vco_voltage_w(BIT(data, 1) ? 2.5 : 0);
	m_csg->vco_w(BIT(data, 2));
	m_csg->mixer_b_w(BIT(data, 3));
	m_csg->mixer_a_w(BIT(data, 4));
	m_csg->mixer_c_w(BIT(data, 5));
	m_csg->envelope_2_w(BIT(data, 6));
	m_csg->envelope_1_w(BIT(data, 7));
}



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

//-------------------------------------------------
//  ADDRESS_MAP( abc80_mem )
//-------------------------------------------------

void abc80_state::abc80_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(abc80_state::read), FUNC(abc80_state::write));
}


//-------------------------------------------------
//  ADDRESS_MAP( abc80_io )
//-------------------------------------------------

void abc80_state::abc80_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x17);
	map(0x00, 0x00).rw(m_bus, FUNC(abcbus_slot_device::inp_r), FUNC(abcbus_slot_device::out_w));
	map(0x01, 0x01).rw(m_bus, FUNC(abcbus_slot_device::stat_r), FUNC(abcbus_slot_device::cs_w));
	map(0x02, 0x02).w(m_bus, FUNC(abcbus_slot_device::c1_w));
	map(0x03, 0x03).w(m_bus, FUNC(abcbus_slot_device::c2_w));
	map(0x04, 0x04).w(m_bus, FUNC(abcbus_slot_device::c3_w));
	map(0x05, 0x05).w(m_bus, FUNC(abcbus_slot_device::c4_w));
	map(0x06, 0x06).w(FUNC(abc80_state::csg_w));
	map(0x07, 0x07).r(m_bus, FUNC(abcbus_slot_device::rst_r));
	map(0x10, 0x13).mirror(0x04).rw(m_pio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
}


//-------------------------------------------------
//  ADDRESS_MAP( tkn80_io )
//-------------------------------------------------

void tkn80_state::tkn80_io(address_map &map)
{
	abc80_io(map);
	map(0x03, 0x03).r(FUNC(tkn80_state::in3_r));
	map(0x04, 0x04).r(FUNC(tkn80_state::in4_r));
}



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

//-------------------------------------------------
//  INPUT_PORTS( tkn80 )
//-------------------------------------------------

static INPUT_PORTS_START( tkn80 )
	PORT_START("CONFIG")
	PORT_CONFNAME( 0x01, 0x01, "Columns" )
	PORT_CONFSETTING(    0x00, "40" )
	PORT_CONFSETTING(    0x01, "80" )
	PORT_CONFNAME( 0x02, 0x02, "Cursor" )
	PORT_CONFSETTING(    0x00, "Static" )
	PORT_CONFSETTING(    0x02, "Blinking" )
	PORT_CONFNAME( 0x04, 0x00, "ROM Checksum" )
	PORT_CONFSETTING(    0x00, "9913/10042" )
	PORT_CONFSETTING(    0x04, "11273" )
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  Z80PIO
//-------------------------------------------------

u8 abc80_state::pio_pa_r()
{
	/*

	    PIO Port A

	    bit     description

	    0       keyboard data
	    1       keyboard data
	    2       keyboard data
	    3       keyboard data
	    4       keyboard data
	    5       keyboard data
	    6       keyboard data
	    7       keyboard strobe

	*/

	u8 data = 0;

	//data |= m_kb->data_r();
	data |= m_key_data;
	data |= (m_key_strobe << 7);

	return data;
}

u8 abc80_state::pio_pb_r()
{
	/*

	    PIO Channel B

	    0  R    RS-232C RxD
	    1  R    RS-232C _CTS
	    2  R    RS-232C _DCD
	    3  W    RS-232C TxD
	    4  W    RS-232C _RTS
	    5  W    Cassette Motor
	    6  W    Cassette Data
	    7  R    Cassette Data

	*/

	u8 data = 0;

	// receive data
	data |= m_rs232->rxd_r();

	// clear to send
	data |= m_rs232->cts_r() << 1;

	// data carrier detect
	data |= m_rs232->dcd_r() << 2;

	// cassette data
	data |= m_tape_in_latch << 7;

	if (LOG) logerror("%s %s read tape latch %u\n", machine().time().as_string(), machine().describe_context(), m_tape_in_latch);

	return data;
}

void abc80_state::pio_pb_w(u8 data)
{
	/*

	    PIO Channel B

	    0  R    RS-232C RxD
	    1  R    RS-232C _CTS
	    2  R    RS-232C _DCD
	    3  W    RS-232C TxD
	    4  W    RS-232C _RTS
	    5  W    Cassette Motor
	    6  W    Cassette Data
	    7  R    Cassette Data

	*/

	// transmit data
	m_rs232->write_txd(BIT(data, 3));

	// request to send
	m_rs232->write_rts(BIT(data, 4));

	// cassette motor
	if (BIT(data, 5))
	{
		if (!m_motor) if (LOG) logerror("%s %s started cassette motor\n", machine().time().as_string(), machine().describe_context());
		m_cassette->change_state(CASSETTE_MOTOR_ENABLED, CASSETTE_MASK_MOTOR);
		m_motor = true;
	}
	else
	{
		if (m_motor) if (LOG) logerror("%s %s stopped cassette motor\n", machine().time().as_string(), machine().describe_context());
		m_cassette->change_state(CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
		m_motor = false;
	}

	// cassette data
	m_cassette->output(BIT(data, 6) ? -1.0 : +1.0);

	// cassette input latch
	if (BIT(data, 6))
	{
		if (LOG) logerror("%s %s clear tape in latch\n", machine().time().as_string(), machine().describe_context());

		m_tape_in_latch = 1;

		m_pio->pb7_w(m_tape_in_latch);
	}
}


//-------------------------------------------------
//  Z80 Daisy Chain
//-------------------------------------------------

static const z80_daisy_config abc80_daisy_chain[] =
{
	{ Z80PIO_TAG },
	{ nullptr }
};


//-------------------------------------------------
//  ABC80_KEYBOARD_INTERFACE
//-------------------------------------------------

void abc80_state::keydown_w(int state)
{
	m_key_strobe = state;

	m_pio->port_a_write(m_key_strobe << 7);
}

void abc80_state::kbd_w(u8 data)
{
	m_key_data = data;
	m_key_strobe = 1;

	u8 pio_data = 0x80 | data;
	m_pio->port_a_write(pio_data);

	m_keyboard_clear_timer->adjust(attotime::from_msec(50));
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  timer events
//-------------------------------------------------

TIMER_CALLBACK_MEMBER(abc80_state::scanline_tick)
{
	draw_scanline(m_bitmap, m_screen->vpos());

	m_pio_astb = !m_pio_astb;

	m_pio->strobe_a(m_pio_astb);
}

TIMER_CALLBACK_MEMBER(abc80_state::cassette_update)
{
	if (!m_motor)
		return;

	int tape_in = m_cassette->input() > 0;

	if (m_tape_in != tape_in)
		if (LOG) logerror("%s tape flank %u\n", machine().time().as_string(), tape_in);

	if (m_tape_in_latch && (m_tape_in != tape_in))
	{
		if (LOG) logerror("%s set tape in latch\n", machine().time().as_string());
		m_tape_in_latch = 0;

		m_pio->port_b_write(m_tape_in_latch << 7);
	}

	m_tape_in = tape_in;
}

TIMER_CALLBACK_MEMBER(abc80_state::blink_tick)
{
	m_blink = !m_blink;
}

TIMER_CALLBACK_MEMBER(abc80_state::vsync_on)
{
	if (LOG) logerror("%s vsync 1\n", machine().time().as_string());
	m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
}

TIMER_CALLBACK_MEMBER(abc80_state::vsync_off)
{
	if (LOG) logerror("%s vsync 0\n", machine().time().as_string());
	m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
}

TIMER_CALLBACK_MEMBER(abc80_state::clear_keyboard)
{
	m_pio->port_a_write(m_key_data);
	m_key_strobe = 0;
	m_key_data = 0;
}


//-------------------------------------------------
//  machine_start
//-------------------------------------------------

void abc80_state::machine_start()
{
	// start timers
	m_cassette_timer = timer_alloc(FUNC(abc80_state::cassette_update), this);
	m_cassette_timer->adjust(attotime::from_hz(44100), 0, attotime::from_hz(44100));
	m_keyboard_clear_timer = timer_alloc(FUNC(abc80_state::clear_keyboard), this);

	// register for state saving
	save_item(NAME(m_key_data));
	save_item(NAME(m_key_strobe));
	save_item(NAME(m_pio_astb));
	save_item(NAME(m_latch));
	save_item(NAME(m_blink));
	save_item(NAME(m_motor));
	save_item(NAME(m_tape_in));
	save_item(NAME(m_tape_in_latch));

	// zero-fill
	m_key_data = 0;
	m_key_strobe = 0;
	m_blink = 0;
}


void tkn80_state::machine_start()
{
	abc80_state::machine_start();

	// register for state saving
	save_item(NAME(m_80));
	save_item(NAME(m_rom_offset));
}


//-------------------------------------------------
//  machine_reset
//-------------------------------------------------

void tkn80_state::machine_reset()
{
	u8 config = m_config->read();

	m_rom_offset = BIT(config, 2) << 11;
}


//-------------------------------------------------
//  QUICKLOAD_LOAD_MEMBER( quickload_cb )
//-------------------------------------------------

QUICKLOAD_LOAD_MEMBER(abc80_state::quickload_cb)
{
	address_space &space = m_maincpu->space(AS_PROGRAM);

	offs_t address = space.read_byte(BOFA + 1) << 8 | space.read_byte(BOFA);
	if (LOG) logerror("BOFA %04x\n",address);

	int quickload_size = image.length();
	std::vector<u8> data(quickload_size);
	image.fread(&data[0], quickload_size);
	for (int i = 1; i < quickload_size; i++)
		space.write_byte(address++, data[i]);

	offs_t eofa = address;
	space.write_byte(EOFA, eofa & 0xff);
	space.write_byte(EOFA + 1, eofa >> 8);
	if (LOG) logerror("EOFA %04x\n",address);

	offs_t head = address + 1;
	space.write_byte(HEAD, head & 0xff);
	space.write_byte(HEAD + 1, head >> 8);
	if (LOG) logerror("HEAD %04x\n",address);

	return std::make_pair(std::error_condition(), std::string());
}



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

//-------------------------------------------------
//  machine_config( abc80_common )
//-------------------------------------------------

void abc80_state::abc80_common(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, XTAL(11'980'800)/2/2); // 2.9952 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &abc80_state::abc80_mem);
	m_maincpu->set_addrmap(AS_IO, &abc80_state::abc80_io);
	m_maincpu->set_daisy_config(abc80_daisy_chain);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SN76477(config, m_csg);
	m_csg->set_noise_params(RES_K(47), RES_K(330), CAP_P(390));
	m_csg->set_decay_res(RES_K(47));
	m_csg->set_attack_params(CAP_U(10), RES_K(2.2));
	m_csg->set_amp_res(RES_K(33));
	m_csg->set_feedback_res(RES_K(10));
	m_csg->set_vco_params(0, CAP_N(10), RES_K(100));
	m_csg->set_pitch_voltage(0);
	m_csg->set_slf_params(CAP_U(1), RES_K(220));
	m_csg->set_oneshot_params(CAP_U(0.1), RES_K(330));
	m_csg->add_route(ALL_OUTPUTS, "mono", 0.25);

	// devices
	Z80PIO(config, m_pio, XTAL(11'980'800)/2/2);
	m_pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio->in_pa_callback().set(FUNC(abc80_state::pio_pa_r));
	m_pio->in_pb_callback().set(FUNC(abc80_state::pio_pb_r));
	m_pio->out_pb_callback().set(FUNC(abc80_state::pio_pb_w));

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("abc80_cass");

	ABC80_KEYBOARD(config, m_kb, 0);
	m_kb->keydown_wr_callback().set(FUNC(abc80_state::keydown_w));

	ABCBUS_SLOT(config, m_bus, XTAL(11'980'800)/2/2, abc80_cards, "abcexp");

	RS232_PORT(config, RS232_TAG, default_rs232_devices, nullptr);
	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "generic_kb", 0));
	keyboard.set_keyboard_callback(FUNC(abc80_state::kbd_w));

	QUICKLOAD(config, "quickload", "bac", attotime::from_seconds(2)).set_load_callback(FUNC(abc80_state::quickload_cb));

	// internal ram
	RAM(config, RAM_TAG).set_default_size("16K");

	// software list
	SOFTWARE_LIST(config, "cass_list").set_original("abc80_cass");
	SOFTWARE_LIST(config, "flop_list").set_original("abc80_flop");
	SOFTWARE_LIST(config, "rom_list").set_original("abc80_rom");
}

void abc80_state::abc80(machine_config &config)
{
	abc80_state::abc80_common(config);

	// video hardware
	abc80_video(config);
}


//-------------------------------------------------
//  machine_config( tkn80 )
//-------------------------------------------------

void tkn80_state::tkn80(machine_config &config)
{
	abc80_state::abc80_common(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_IO, &tkn80_state::tkn80_io);

	// video hardware
	tkn80_video(config);
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( abc80 )
//-------------------------------------------------

ROM_START( abc80 )
	ROM_REGION( 0x4000, Z80_TAG, 0 )
	ROM_DEFAULT_BIOS("9913")
	ROM_SYSTEM_BIOS( 0, "11273", "Checksum 11273" )
	ROMX_LOAD( "za3506_11273.a5", 0x0000, 0x1000, CRC(7c004fb6) SHA1(9aee1d085122f4537c3e6ecdab9d799bd429ef52), ROM_BIOS(0) )
	ROMX_LOAD( "za3507_11273.a3", 0x1000, 0x1000, CRC(d1850a84) SHA1(f7719f3af9173601a2aa23ae38ae00de1a387ad8), ROM_BIOS(0) )
	ROMX_LOAD( "za3508_11273.a4", 0x2000, 0x1000, CRC(b55528e9) SHA1(3e5017e8cacad1f13215242f1bbd89d1d3eee131), ROM_BIOS(0) )
	ROMX_LOAD( "za3509_11273.a2", 0x3000, 0x1000, CRC(659cab1e) SHA1(181db748cef22cdcccd311a60aa6189c85343db7), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "10042", "Checksum 10042" )
	ROMX_LOAD( "za3506_9913.a5", 0x0000, 0x1000, CRC(e2afbf48) SHA1(9883396edd334835a844dcaa792d29599a8c67b9), ROM_BIOS(1) )
	ROMX_LOAD( "za3507_9913.a3", 0x1000, 0x1000, CRC(d224412a) SHA1(30968054bba7c2aecb4d54864b75a446c1b8fdb1), ROM_BIOS(1) )
	ROMX_LOAD( "za3508_9913.a4", 0x2000, 0x1000, CRC(1502ba5b) SHA1(5df45909c2c4296e5701c6c99dfaa9b10b3a729b), ROM_BIOS(1) )
	ROMX_LOAD( "za3509_10042.a2", 0x3000, 0x1000, CRC(346f0cdb) SHA1(4262137cff9dfc82c5bd5727994ed5f9b7d22395), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "9913", "Checksum 9913" )
	ROMX_LOAD( "za3506_9913.a5", 0x0000, 0x1000, CRC(e2afbf48) SHA1(9883396edd334835a844dcaa792d29599a8c67b9), ROM_BIOS(2) )
	ROMX_LOAD( "za3507_9913.a3", 0x1000, 0x1000, CRC(d224412a) SHA1(30968054bba7c2aecb4d54864b75a446c1b8fdb1), ROM_BIOS(2) )
	ROMX_LOAD( "za3508_9913.a4", 0x2000, 0x1000, CRC(1502ba5b) SHA1(5df45909c2c4296e5701c6c99dfaa9b10b3a729b), ROM_BIOS(2) )
	ROMX_LOAD( "za3509_9913.a2", 0x3000, 0x1000, CRC(bc8860b7) SHA1(28b6cf7f5a4f81e017c2af091c3719657f981710), ROM_BIOS(2) )

	ROM_REGION( 0x100, "hsync", 0 )
	ROM_LOAD( "abc80_11.k5", 0x0000, 0x0100, CRC(e4f7e018) SHA1(63e718a39537f37286ea183e6469808c271dbfa5) ) // "64 40029-01" 82S129 256x4 horizontal sync

	ROM_REGION( 0x200, "vsync", 0 )
	ROM_LOAD( "abc80_21.k2", 0x0000, 0x0200, CRC(445a45b9) SHA1(bcc1c4fafe68b3500b03de785ca32abd63cea252) ) // "64 40030-01" 82S131 512x4 vertical sync

	ROM_REGION( 0x100, "attr", 0 )
	ROM_LOAD( "abc80_12.j3", 0x0000, 0x0100, CRC(6c46811c) SHA1(2d3bdf2d3a2a88ddb1c0c637967e1b2b9541a928) ) // "64 40056-01" 82S129 256x4 attribute

	ROM_REGION( 0x200, "line", 0 )
	ROM_LOAD( "abc80_22.k1", 0x0000, 0x0200, CRC(74de7a0b) SHA1(96f37b0ca65aa8af4242bad38124f410b7f657fe) ) // "64 40058-01" 82S131 512x4 chargen 74S263 row address

	ROM_REGION( 0x100, "mmu", 0 )
	ROM_LOAD( "abc80_13.e7", 0x0000, 0x0100, CRC(f7738834) SHA1(b02df3e678fb50c9cb75b4a97615222d3b4577a3) ) // "64 40057-01" 82S129 256x4 address decoder
ROM_END


//-------------------------------------------------
//  ROM( tkn80 )
//-------------------------------------------------

ROM_START( tkn80 )
	ROM_REGION( 0x4000, Z80_TAG, 0 )
	ROM_DEFAULT_BIOS("9913")
	ROM_SYSTEM_BIOS( 0, "11273", "Checksum 11273" )
	ROMX_LOAD( "za3506_11273.a5", 0x0000, 0x1000, CRC(7c004fb6) SHA1(9aee1d085122f4537c3e6ecdab9d799bd429ef52), ROM_BIOS(0) )
	ROMX_LOAD( "za3507_11273.a3", 0x1000, 0x1000, CRC(d1850a84) SHA1(f7719f3af9173601a2aa23ae38ae00de1a387ad8), ROM_BIOS(0) )
	ROMX_LOAD( "za3508_11273.a4", 0x2000, 0x1000, CRC(b55528e9) SHA1(3e5017e8cacad1f13215242f1bbd89d1d3eee131), ROM_BIOS(0) )
	ROMX_LOAD( "za3509_11273.a2", 0x3000, 0x1000, CRC(659cab1e) SHA1(181db748cef22cdcccd311a60aa6189c85343db7), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "10042", "Checksum 10042" )
	ROMX_LOAD( "za3506_9913.a5", 0x0000, 0x1000, CRC(e2afbf48) SHA1(9883396edd334835a844dcaa792d29599a8c67b9), ROM_BIOS(1) )
	ROMX_LOAD( "za3507_9913.a3", 0x1000, 0x1000, CRC(d224412a) SHA1(30968054bba7c2aecb4d54864b75a446c1b8fdb1), ROM_BIOS(1) )
	ROMX_LOAD( "za3508_9913.a4", 0x2000, 0x1000, CRC(1502ba5b) SHA1(5df45909c2c4296e5701c6c99dfaa9b10b3a729b), ROM_BIOS(1) )
	ROMX_LOAD( "za3509_10042.a2", 0x3000, 0x1000, CRC(346f0cdb) SHA1(4262137cff9dfc82c5bd5727994ed5f9b7d22395), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "9913", "Checksum 9913" )
	ROMX_LOAD( "za3506_9913.a5", 0x0000, 0x1000, CRC(e2afbf48) SHA1(9883396edd334835a844dcaa792d29599a8c67b9), ROM_BIOS(2) )
	ROMX_LOAD( "za3507_9913.a3", 0x1000, 0x1000, CRC(d224412a) SHA1(30968054bba7c2aecb4d54864b75a446c1b8fdb1), ROM_BIOS(2) )
	ROMX_LOAD( "za3508_9913.a4", 0x2000, 0x1000, CRC(1502ba5b) SHA1(5df45909c2c4296e5701c6c99dfaa9b10b3a729b), ROM_BIOS(2) )
	ROMX_LOAD( "za3509_9913.a2", 0x3000, 0x1000, CRC(bc8860b7) SHA1(28b6cf7f5a4f81e017c2af091c3719657f981710), ROM_BIOS(2) )

	ROM_REGION( 0x1000, "tkn80", 0 )
	ROM_LOAD( "tkn80-iii.e", 0x0000, 0x1000, CRC(f0d2e4fa) SHA1(b0263c65db39667a6fe62e61f73fe591ea10f14b) )

	ROM_REGION( 0x100, "hsync", 0 )
	ROM_LOAD( "abc80_11.k5", 0x0000, 0x0100, CRC(e4f7e018) SHA1(63e718a39537f37286ea183e6469808c271dbfa5) ) // "64 40029-01" 82S129 256x4 horizontal sync

	ROM_REGION( 0x200, "vsync", 0 )
	ROM_LOAD( "abc80_21.k2", 0x0000, 0x0200, CRC(445a45b9) SHA1(bcc1c4fafe68b3500b03de785ca32abd63cea252) ) // "64 40030-01" 82S131 512x4 vertical sync

	ROM_REGION( 0x100, "attr", 0 )
	ROM_LOAD( "abc80_12.j3", 0x0000, 0x0100, CRC(6c46811c) SHA1(2d3bdf2d3a2a88ddb1c0c637967e1b2b9541a928) ) // "64 40056-01" 82S129 256x4 attribute

	ROM_REGION( 0x200, "line", 0 )
	ROM_LOAD( "abc80_22.k1", 0x0000, 0x0200, CRC(74de7a0b) SHA1(96f37b0ca65aa8af4242bad38124f410b7f657fe) ) // "64 40058-01" 82S131 512x4 chargen 74S263 row address

	ROM_REGION( 0x100, "mmu", 0 )
	ROM_LOAD( "tkn80-13.e7", 0x0000, 0x0100, CRC(28bfdf62) SHA1(209c4a29628168a27445b75f4c44aba2c7c49dbb) )
ROM_END



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS         INIT        COMPANY             FULLNAME             FLAGS
COMP( 1978, abc80, 0,      0,      abc80,   0,     abc80_state,  empty_init, "Luxor Datorer AB", "ABC 80",            MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )
COMP( 198?, tkn80, abc80,  0,      tkn80,   tkn80, tkn80_state,  empty_init, "MYAB",             "ABC 80 with TKN80", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )



abc80x.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

Luxor ABC 800C/800M

Main PCB Layout
---------------

|-----------------------|-----------------------------------|
|   4116    4116        |                                   |
|   4116    4116        |                                   |
|   4116    4116        |                                   |
|   4116    4116        |           Z80         Z80CTC      |
|   4116    4116        |CN1                                |
|   4116    4116        |                                   |
|   4116    4116        |                       Z80DART     |
|                       |                       CN6         |
|   ROM3    ROM7        |                                   |
|                       |                       Z80SIO2     |
|   ROM2    ROM6        |-----------------------------------|
|                                                           |
|   ROM1    ROM5                                            |
|                                                       CN2 |
|   ROM0    ROM4                                            |
|                       CN7                                 |
|-------------------------------------------|           CN3 |
|                                           |               |
|                                           |               |
|                                           |           CN4 |
|                                           |               |
|               VIDEO BOARD                 |               |
|                                           |           CN5 |
|                                           |               |
|                                           |               |
|                                           |               |
|-------------------------------------------|---------------|

Notes:
    Relevant IC's shown.

    Z80     - ? Z80A CPU (labeled "Z80A (800) KASS.")
    Z80CTC  - Sharp LH0082A Z80A-CTC
    Z80DART - SGS Z8470AB1 Z80A-DART
    Z8OSIO2 - SGS Z8442AB1 Z80A-SIO/2
    4116    - Toshiba TMS4116-20NL 1Kx8 RAM
    ROM0    - NEC D2732D 4Kx8 EPROM "ABC M-12"
    ROM1    - NEC D2732D 4Kx8 EPROM "ABC 1-12"
    ROM2    - NEC D2732D 4Kx8 EPROM "ABC 2-12"
    ROM3    - NEC D2732D 4Kx8 EPROM "ABC 3-12"
    ROM4    - NEC D2732D 4Kx8 EPROM "ABC 4-12"
    ROM5    - NEC D2732D 4Kx8 EPROM "ABC 5-12"
    ROM6    - Hitachi HN462732G 4Kx8 EPROM "ABC 6-52"
    ROM7    - NEC D2732D 4Kx8 EPROM "ABC 7-22"
    CN1     - ABC bus connector
    CN2     - tape connector
    CN3     - RS-232 channel B connector
    CN4     - RS-232 channel A connector
    CN5     - video connector
    CN6     - keyboard connector
    CN7     - video board connector


Video PCB Layout (Color)
------------------------

55 10789-01

|-------------------------------------------|
|                                           |
|                                           |
|               2114                        |
|                                           |
|               2114        TROM            |
|                                           |
|       12MHz               TIC             |
|                                           |
|                                           |
|-------------------------------------------|

Notes:
    Relevant IC's shown.

    2114    - GTE Microcircuits 2114-2CB 1Kx4 RAM
    TIC     - Philips SAA5020 Teletext Timing Chain
    TROM    - Philips SAA5052 Teletext Character Generator w/Swedish Character Set


Video PCB Layout (Monochrome)
-----------------------------

55 10790-02

|-------------------------------------------|
|                                           |
|               2114                        |
|               2114                        |
|                                      12MHz|
|               2114                        |
|               2114                        |
|                                           |
|               CRTC            ROM0        |
|                                           |
|-------------------------------------------|

Notes:
    Relevant IC's shown.

    2114    - Toshiba TMM314AP-1 1Kx4 RAM
    CRTC    - Hitachi HD46505SP CRTC
    ROM0    - NEC D2716D 2Kx8 EPROM "VUM SE"


Video PCB Layout (High Resolution)
----------------------------------

55 10791-01

|-------------------------------------------|
|                                           |
|   4116        PROM1                       |
|   4116                                    |
|   4116                                    |
|   4116                                    |
|   4116                                    |
|   4116                                    |
|   4116                                    |
|   4116                PROM0               |
|-------------------------------------------|

Notes:
    Relevant IC's shown.

    4116    - Motorola MCM4116AC25 1Kx8 RAM
    PROM0   - Philips 82S123 32x8 Bipolar PROM "HRU I"
    PROM1   - Philips 82S131 512x4 Bipolar PROM "HRU II"

*/

/*

    TODO:

    - abc806 30K banking
    - cassette
    - abc800 video card bus

*/

#include "emu.h"
#include "abc80x.h"
#include "screen.h"
#include "softlist_dev.h"

//#define VERBOSE 1
#include "logmacro.h"


#define ABCBUS_TAG "bus"


//**************************************************************************
//  HIGH RESOLUTION GRAPHICS
//**************************************************************************

//-------------------------------------------------
//  hrs_w - high resolution scanline write
//-------------------------------------------------

void abc800_state::hrs_w(uint8_t data)
{
	m_hrs = data;
}


//-------------------------------------------------
//  hrc_w - high resolution color write
//-------------------------------------------------

void abc800_state::hrc_w(uint8_t data)
{
	m_fgctl = data;
}



//**************************************************************************
//  ABC 800 COLOR
//**************************************************************************

//-------------------------------------------------
//  hr_update - high resolution screen update
//-------------------------------------------------

void abc800c_state::hr_update(bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const pen_t *pen = m_palette->pens();

	uint16_t addr = 0;

	for (int y = m_hrs; y < std::min(cliprect.max_y + 1, m_hrs + 480); y += 2)
	{
		int x = 0;

		for (int sx = 0; sx < 64; sx++)
		{
			uint8_t data = m_video_ram[addr++];

			for (int dot = 0; dot < 4; dot++)
			{
				uint16_t fgctl_addr = ((m_fgctl & 0x7f) << 2) | ((data >> 6) & 0x03);
				uint8_t fgctl = m_fgctl_prom->base()[fgctl_addr];
				int color = fgctl & 0x07;

				if (color)
				{
					bool black = bitmap.pix(y, x) == rgb_t::black();
					bool opaque = !BIT(fgctl, 3);

					if (black || opaque)
					{
						bitmap.pix(y, x) = pen[color];
						bitmap.pix(y, x + 1) = pen[color];

						bitmap.pix(y + 1, x) = pen[color];
						bitmap.pix(y + 1, x + 1) = pen[color];
					}
				}

				data <<= 2;
				x += 2;
			}
		}
	}
}


//-------------------------------------------------
//  SCREEN_UPDATE( abc800c )
//-------------------------------------------------

uint32_t abc800c_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	// clear screen
	bitmap.fill(rgb_t::black(), cliprect);

	// draw text
	if (!BIT(m_fgctl, 7))
	{
		m_trom->screen_update(screen, bitmap, cliprect);
	}

	// draw HR graphics
	hr_update(bitmap, cliprect);

	return 0;
}


//-------------------------------------------------
//  SAA5050_INTERFACE( trom_intf )
//-------------------------------------------------

uint8_t abc800c_state::char_ram_r(offs_t offset)
{
	int row = offset / 40;
	int col = offset % 40;

	offset = ((row & 0x07) * 0x80) + col;

	if (row & 0x08) offset += 0x28;
	if (row & 0x10) offset += 0x50;

	return m_char_ram[offset];
}


//-------------------------------------------------
//  PALETTE_INIT( abc800c )
//-------------------------------------------------

void abc800c_state::abc800c_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t::black());
	palette.set_pen_color(1, rgb_t(0xff, 0x00, 0x00)); // red
	palette.set_pen_color(2, rgb_t(0x00, 0xff, 0x00)); // green
	palette.set_pen_color(3, rgb_t(0xff, 0xff, 0x00)); // yellow
	palette.set_pen_color(4, rgb_t(0x00, 0x00, 0xff)); // blue
	palette.set_pen_color(5, rgb_t(0xff, 0x00, 0xff)); // magenta
	palette.set_pen_color(6, rgb_t(0x00, 0xff, 0xff)); // cyan
	palette.set_pen_color(7, rgb_t::white());
}


//-------------------------------------------------
//  machine_config( abc800c_video )
//-------------------------------------------------

void abc800c_state::abc800c_video(machine_config &config)
{
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_screen_update(FUNC(abc800c_state::screen_update));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.set_size(480, 480);
	screen.set_visarea(0, 480-1, 0, 480-1);

	PALETTE(config, m_palette, FUNC(abc800c_state::abc800c_palette), 8);

	SAA5052(config, m_trom, XTAL(12'000'000)/2);
	m_trom->d_cb().set(FUNC(abc800c_state::char_ram_r));
	m_trom->set_screen_size(40, 24, 40);
}



//**************************************************************************
//  ABC 800 MONOCHROME
//**************************************************************************

//-------------------------------------------------
//  hr_update - high resolution screen update
//-------------------------------------------------

void abc800m_state::hr_update(bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	// this is needed because the MC6845 emulation does
	// not position the active display area correctly
	constexpr int HORIZONTAL_PORCH_HACK = 115;

	uint16_t addr = 0;

	const pen_t *pen = m_palette->pens();

	for (int y = m_hrs; y < std::min(cliprect.max_y + 1, m_hrs + 240); y++)
	{
		int x = HORIZONTAL_PORCH_HACK;

		for (int sx = 0; sx < 64; sx++)
		{
			uint8_t data = m_video_ram[addr++];

			for (int dot = 0; dot < 4; dot++)
			{
				uint16_t fgctl_addr = ((m_fgctl & 0x7f) << 2) | ((data >> 6) & 0x03);
				int color = (m_fgctl_prom->base()[fgctl_addr] & 0x07) ? 1 : 0;

				bitmap.pix(y, x++) = pen[color];
				bitmap.pix(y, x++) = pen[color];

				data <<= 2;
			}
		}
	}
}


//-------------------------------------------------
//  MC6845_UPDATE_ROW( abc800m_update_row )
//-------------------------------------------------

MC6845_UPDATE_ROW( abc800m_state::abc800m_update_row )
{
	int column;
	rgb_t fgpen = m_palette->pen(1);

	y += vbp;

	for (column = 0; column < x_count; column++)
	{
		int bit;

		uint16_t address = (m_char_ram[(ma + column) & 0x7ff] << 4) | (ra & 0x0f);
		uint8_t data = (m_char_rom->base()[address & 0x7ff] & 0x3f);

		if (column == cursor_x)
		{
			data = 0x3f;
		}

		data <<= 2;

		for (bit = 0; bit < ABC800_CHAR_WIDTH; bit++)
		{
			int x = hbp + (column * ABC800_CHAR_WIDTH) + bit;

			if (BIT(data, 7) && de)
			{
				bitmap.pix(y, x) = fgpen;
			}

			data <<= 1;
		}
	}
}


//-------------------------------------------------
//  SCREEN_UPDATE( abc800m )
//-------------------------------------------------

uint32_t abc800m_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	// clear screen
	bitmap.fill(rgb_t::black(), cliprect);

	// draw HR graphics
	hr_update(bitmap, cliprect);

	// draw text
	if (!BIT(m_fgctl, 7))
	{
		m_crtc->screen_update(screen, bitmap, cliprect);
	}

	return 0;
}


void abc800_state::video_start()
{
	// register for state saving
	save_item(NAME(m_hrs));
	save_item(NAME(m_fgctl));
}



//-------------------------------------------------
//  machine_config( abc800m_video )
//-------------------------------------------------

void abc800m_state::abc800m_video(machine_config &config)
{
	mc6845_device &mc6845(MC6845(config, MC6845_TAG, ABC800_CCLK));
	mc6845.set_screen(SCREEN_TAG);
	mc6845.set_show_border_area(true);
	mc6845.set_char_width(ABC800_CHAR_WIDTH);
	mc6845.set_update_row_callback(FUNC(abc800m_state::abc800m_update_row));
	mc6845.out_vsync_callback().set(m_dart, FUNC(z80dart_device::rib_w)).invert();

	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER, rgb_t(0xff, 0xff, 0x00)));
	screen.set_screen_update(FUNC(abc800m_state::screen_update));
	screen.set_raw(XTAL(12'000'000), 0x300, 0, 0x1e0, 0x13a, 0, 0xf0);

	PALETTE(config, m_palette, palette_device::MONOCHROME);
}



//-------------------------------------------------
//  MC6845_UPDATE_ROW( abc802_update_row )
//-------------------------------------------------

MC6845_UPDATE_ROW( abc802_state::abc802_update_row )
{
	/*

	    PAL16R4 equation:

	    IF (VCC)    *OS   = FC + RF / RC
	                *RG:  = HS / *RG + *ATE / *RG + ATD / *RG + LL /
	                        *RG + AT1 / *RG + AT0 / ATE + *ATD + *LL +
	                        *AT1 + *AT0
	                *RI:  = *RI + *INV / *RI + LL / *INV + *LL
	                *RF:  = HS / *RF + *ATE / *RF + ATD / *RF + LL /
	                        *RF + AT1 / *RF + AT0 / ATE + *ATD + *LL +
	                        *AT1 + AT0
	                *RC:  = HS / *RC + *ATE / *RC + *ATD / *RC + LL /
	                        *RC + *ATI / *RC + AT0 / ATE + *LL + *AT1 +
	                        *AT0
	    IF (VCC)    *O0   = *CUR + *AT0 / *CUR + ATE
	                *O1   = *CUR + *AT1 / *CUR + ATE


	    + = AND
	    / = OR
	    * = Inverted

	    ATD     Attribute data
	    ATE     Attribute enable
	    AT0,AT1 Attribute address
	    CUR     Cursor
	    FC      FLSH clock
	    HS      Horizontal sync
	    INV     Inverted signal input
	    LL      Load when Low
	    OEL     Output Enable when Low
	    RC      Row clear
	    RF      Row flash
	    RG      Row graphic
	    RI      Row inverted

	*/

	const pen_t *pen = m_palette->pens();

	int rf = 0, rc = 0, rg = 0;

	y += vbp;

	for (int column = 0; column < x_count; column++)
	{
		uint8_t code = m_char_ram[(ma + column) & 0x7ff];
		uint16_t address = code << 4;
		uint8_t ra_latch = ra;
		uint8_t data;

		int ri = (code & ABC802_INV) ? 1 : 0;

		if (column == cursor_x)
		{
			ra_latch = 0x0f;
		}

		if ((m_flshclk && rf) || rc)
		{
			ra_latch = 0x0e;
		}

		if (rg)
		{
			address |= 0x800;
		}

		data = m_char_rom->base()[(address + ra_latch) & 0xfff];

		if (data & ABC802_ATE)
		{
			int attr = data & 0x03;
			int value = (data & ABC802_ATD) ? 1 : 0;

			switch (attr)
			{
			case 0x00:
				// Row Graphic
				rg = value;
				break;

			case 0x01:
				// Row Flash
				rf = value;
				break;

			case 0x02:
				// Row Clear
				rc = value;
				break;

			case 0x03:
				// undefined
				break;
			}
		}
		else
		{
			data <<= 2;

			if (m_80_40_mux)
			{
				for (int bit = 0; bit < ABC800_CHAR_WIDTH; bit++)
				{
					int x = hbp + ((column + 3) * ABC800_CHAR_WIDTH) + bit;
					int color = (BIT(data, 7) ^ ri) && de;

					bitmap.pix(y, x) = pen[color];

					data <<= 1;
				}
			}
			else
			{
				for (int bit = 0; bit < ABC800_CHAR_WIDTH; bit++)
				{
					int x = hbp + ((column + 3) * ABC800_CHAR_WIDTH) + (bit << 1);
					int color = (BIT(data, 7) ^ ri) && de;

					bitmap.pix(y, x) = pen[color];
					bitmap.pix(y, x + 1) = pen[color];

					data <<= 1;
				}

				column++;
			}
		}
	}
}


//-------------------------------------------------
//  vs_w - vertical sync write
//-------------------------------------------------

void abc802_state::vs_w(int state)
{
	if (!state)
	{
		// flash clock
		if (m_flshclk_ctr & 0x20)
		{
			m_flshclk = !m_flshclk;
			m_flshclk_ctr = 0;
		}
		else
		{
			m_flshclk_ctr++;
		}
	}
}


void abc802_state::video_start()
{
	// register for state saving
	save_item(NAME(m_flshclk_ctr));
	save_item(NAME(m_flshclk));
	save_item(NAME(m_80_40_mux));
}


//-------------------------------------------------
//  machine_config( abc802_video )
//-------------------------------------------------

void abc802_state::abc802_video(machine_config &config)
{
	mc6845_device &mc6845(MC6845(config, MC6845_TAG, ABC800_CCLK));
	mc6845.set_screen(SCREEN_TAG);
	mc6845.set_show_border_area(true);
	mc6845.set_char_width(ABC800_CHAR_WIDTH);
	mc6845.set_update_row_callback(FUNC(abc802_state::abc802_update_row));
	mc6845.out_vsync_callback().set(FUNC(abc802_state::vs_w));
	mc6845.out_vsync_callback().append(m_dart, FUNC(z80dart_device::rib_w)).invert();

	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER, rgb_t::amber()));
	screen.set_screen_update(MC6845_TAG, FUNC(mc6845_device::screen_update));
	screen.set_raw(XTAL(12'000'000), 0x300, 0, 0x1e0, 0x13a, 0, 0xf0);

	PALETTE(config, m_palette, palette_device::MONOCHROME);
}


//-------------------------------------------------
//  hrs_w - high resolution memory banking
//-------------------------------------------------

void abc806_state::hrs_w(uint8_t data)
{
	/*

	    bit     signal  description

	    0       VM15    visible screen memory area bit 0
	    1       VM16    visible screen memory area bit 1
	    2       VM17    visible screen memory area bit 2
	    3       VM18    visible screen memory area bit 3
	    4       F15     cpu accessible screen memory area bit 0
	    5       F16     cpu accessible screen memory area bit 1
	    6       F17     cpu accessible screen memory area bit 2
	    7       F18     cpu accessible screen memory area bit 3

	*/

	LOG("%s HRS %02x\n", machine().describe_context(), data);

	m_hrs = data;
}


//-------------------------------------------------
//  hrc_w - high resolution color write
//-------------------------------------------------

void abc806_state::hrc_w(offs_t offset, uint8_t data)
{
	int reg = (offset >> 8) & 0x0f;

	m_hrc[reg] = data;
}


//-------------------------------------------------
//  charram_r - character RAM read
//-------------------------------------------------

uint8_t abc806_state::charram_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		m_attr_data = m_attr_ram[offset];

	return m_char_ram[offset];
}


//-------------------------------------------------
//  charram_w - character RAM write
//-------------------------------------------------

void abc806_state::charram_w(offs_t offset, uint8_t data)
{
	if (!machine().side_effects_disabled())
		m_attr_ram[offset] = m_attr_data;

	m_char_ram[offset] = data;
}


//-------------------------------------------------
//  ami_r - attribute memory read
//-------------------------------------------------

uint8_t abc806_state::ami_r()
{
	return m_attr_data;
}


//-------------------------------------------------
//  amo_w - attribute memory write
//-------------------------------------------------

void abc806_state::amo_w(uint8_t data)
{
	m_attr_data = data;
}


//-------------------------------------------------
//  cli_r - palette PROM read
//-------------------------------------------------

uint8_t abc806_state::cli_r(offs_t offset)
{
	/*

	    bit     description

	    0       HRU II data bit 0
	    1       HRU II data bit 1
	    2       HRU II data bit 2
	    3       HRU II data bit 3
	    4
	    5
	    6
	    7       RTC data output

	*/

	uint16_t hru2_addr = (m_hru2_a8 << 8) | (offset >> 8);
	uint8_t data = m_hru2_prom->base()[hru2_addr] & 0x0f;

	LOG("HRU II %03x : %01x\n", hru2_addr, data);

	data |= m_rtc->dio_r() << 7;

	return data;
}


//-------------------------------------------------
//  sti_r - protection device read
//-------------------------------------------------

uint8_t abc806_state::sti_r()
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4
	    5
	    6
	    7       PROT DOUT

	*/

	return 0x7f;
}


//-------------------------------------------------
//  sto_w -
//-------------------------------------------------

void abc806_state::sto_w(uint8_t data)
{
	m_sto->write_bit(data & 0x07, BIT(data, 7));
}


//-------------------------------------------------
//  eme_w - external memory enable
//-------------------------------------------------

void abc806_state::eme_w(int state)
{
	LOG("%s EME %u\n", machine().describe_context(), state);
	m_eme = state;
}


//-------------------------------------------------
//  _40_w - 40/80 column display
//-------------------------------------------------

void abc806_state::_40_w(int state)
{
	m_40 = state;
}


//-------------------------------------------------
//  hru2_a8_w - HRU II address line 8, PROT A0
//-------------------------------------------------

void abc806_state::hru2_a8_w(int state)
{
	m_hru2_a8 = state;
}


//-------------------------------------------------
//  prot_ini_w - PROT INI
//-------------------------------------------------

void abc806_state::prot_ini_w(int state)
{
}


//-------------------------------------------------
//  txoff_w - text display enable
//-------------------------------------------------

void abc806_state::txoff_w(int state)
{
	m_txoff = state;
}


//-------------------------------------------------
//  prot_din_w - PROT DIN
//-------------------------------------------------

void abc806_state::prot_din_w(int state)
{
}


//-------------------------------------------------
//  sso_w - sync offset write
//-------------------------------------------------

void abc806_state::sso_w(uint8_t data)
{
	m_sync = data & 0x3f;
}


//-------------------------------------------------
//  MC6845_UPDATE_ROW( abc806_update_row )
//-------------------------------------------------

MC6845_UPDATE_ROW( abc806_state::abc806_update_row )
{
	const pen_t *pen = m_palette->pens();

	int fg_color = 7;
	int bg_color = 0;
	int underline = 0;
	int flash = 0;
	int e5 = m_40;
	int e6 = m_40;
	int th = 0;

	y += m_sync + vbp;

	for (int column = 0; column < x_count; column++)
	{
		uint8_t data = m_char_ram[(ma + column) & 0x7ff];
		uint8_t attr = m_attr_ram[(ma + column) & 0x7ff];
		uint8_t rad_data;

		if ((attr & 0x07) == ((attr >> 3) & 0x07))
		{
			// special case
			switch (attr >> 6)
			{
			case 0:
				// use previously selected attributes
				break;

			case 1:
				// reserved for future use
				break;

			case 2:
				// blank
				fg_color = 0;
				bg_color = 0;
				underline = 0;
				flash = 0;
				break;

			case 3:
				// double width
				e5 = BIT(attr, 0);
				e6 = BIT(attr, 1);

				// read attributes from next byte
				attr = m_attr_ram[(ma + column + 1) & 0x7ff];

				if (attr != 0x00)
				{
					fg_color = attr & 0x07;
					bg_color = (attr >> 3) & 0x07;
					underline = BIT(attr, 6);
					flash = BIT(attr, 7);
				}
				break;
			}
		}
		else
		{
			// normal case
			fg_color = attr & 0x07;
			bg_color = (attr >> 3) & 0x07;
			underline = BIT(attr, 6);
			flash = BIT(attr, 7);
			e5 = m_40;
			e6 = m_40;
		}

		if (column == cursor_x)
		{
			rad_data = 0x0f;
		}
		else
		{
			uint16_t rad_addr = (e6 << 8) | (e5 << 7) | (flash << 6) | (underline << 4) | (m_flshclk << 5) | (ra & 0x0f);
			rad_data = m_rad_prom->base()[rad_addr] & 0x0f;
		}

		uint16_t chargen_addr = (th << 12) | (data << 4) | rad_data;
		uint8_t chargen_data = m_char_rom->base()[chargen_addr & 0xfff] << 2;
		int x = hbp + (column + 4) * ABC800_CHAR_WIDTH;

		for (int bit = 0; bit < ABC800_CHAR_WIDTH; bit++)
		{
			int color = BIT(chargen_data, 7) ? fg_color : bg_color;
			if (!de) color = 0;

			bitmap.pix(y, x++) = pen[color];

			if (e5 || e6)
			{
				bitmap.pix(y, x++) = pen[color];
			}

			chargen_data <<= 1;
		}

		if (e5 || e6)
		{
			column++;
		}
	}
}


//-------------------------------------------------
//  hs_w - horizontal sync write
//-------------------------------------------------

void abc806_state::hs_w(int state)
{
	int vsync;

	if (!state)
	{
		m_v50_addr++;

		// clock current vsync value into the shift register
		m_vsync_shift <<= 1;
		m_vsync_shift |= m_vsync;

		vsync = BIT(m_vsync_shift, m_sync);

		if (!m_d_vsync && vsync)
		{
			// clear V50 address
			m_v50_addr = 0;
		}
		else if (m_d_vsync && !vsync)
		{
			// flash clock
			if (m_flshclk_ctr & 0x20)
			{
				m_flshclk = !m_flshclk;
				m_flshclk_ctr = 0;
			}
			else
			{
				m_flshclk_ctr++;
			}
		}

		if (m_d_vsync != vsync)
		{
			// signal _DEW to DART
			m_dart->rib_w(!vsync);
		}

		m_d_vsync = vsync;
	}
}


//-------------------------------------------------
//  vs_w - vertical sync write
//-------------------------------------------------

void abc806_state::vs_w(int state)
{
	m_vsync = state;
}


//-------------------------------------------------
//  hr_update - high resolution screen update
//-------------------------------------------------

void abc806_state::hr_update(bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	constexpr int HORIZONTAL_PORCH_HACK   = 109;
	constexpr int VERTICAL_PORCH_HACK     = 27;

	const pen_t *pen = m_palette->pens();

	uint32_t addr = (m_hrs & 0x0f) << 15;

	for (int y = m_sync + VERTICAL_PORCH_HACK; y < std::min(cliprect.max_y + 1, m_sync + VERTICAL_PORCH_HACK + 240); y++)
	{
		for (int sx = 0; sx < 128; sx++)
		{
			uint8_t data = m_video_ram[addr++];
			uint16_t dot = (m_hrc[data >> 4] << 8) | m_hrc[data & 0x0f];

			for (int pixel = 0; pixel < 4; pixel++)
			{
				int x = HORIZONTAL_PORCH_HACK + (ABC800_CHAR_WIDTH * 4) - 16 + (sx * 4) + pixel;

				if (BIT(dot, 15) || (bitmap.pix(y, x) == rgb_t::black()))
				{
					bitmap.pix(y, x) = pen[(dot >> 12) & 0x07];
				}

				dot <<= 4;
			}
		}
	}
}


void abc806_state::video_start()
{
	// register for state saving
	save_item(NAME(m_txoff));
	save_item(NAME(m_40));
	save_item(NAME(m_flshclk_ctr));
	save_item(NAME(m_flshclk));
	save_item(NAME(m_attr_data));
	save_item(NAME(m_hrs));
	save_item(NAME(m_hrc));
	save_item(NAME(m_sync));
	save_item(NAME(m_v50_addr));
	save_item(NAME(m_hru2_a8));
	save_item(NAME(m_vsync_shift));
	save_item(NAME(m_vsync));
	save_item(NAME(m_d_vsync));

	// initialize variables
	for (auto & elem : m_hrc)
	{
		elem = 0;
	}

	m_sync = 10;
	m_d_vsync = 1;
	m_vsync = 1;
	m_40 = 1;
}


//-------------------------------------------------
//  SCREEN_UPDATE( abc806 )
//-------------------------------------------------

uint32_t abc806_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	// clear screen
	bitmap.fill(rgb_t::black(), cliprect);

	if (!m_txoff)
	{
		// draw text
		m_crtc->screen_update(screen, bitmap, cliprect);
	}

	// draw HR graphics
	hr_update(bitmap, cliprect);

	return 0;
}


//-------------------------------------------------
//  PALETTE_INIT( abc806 )
//-------------------------------------------------

void abc806_state::abc806_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t::black());
	palette.set_pen_color(1, rgb_t(0xff, 0x00, 0x00)); // red
	palette.set_pen_color(2, rgb_t(0x00, 0xff, 0x00)); // green
	palette.set_pen_color(3, rgb_t(0xff, 0xff, 0x00)); // yellow
	palette.set_pen_color(4, rgb_t(0x00, 0x00, 0xff)); // blue
	palette.set_pen_color(5, rgb_t(0xff, 0x00, 0xff)); // magenta
	palette.set_pen_color(6, rgb_t(0x00, 0xff, 0xff)); // cyan
	palette.set_pen_color(7, rgb_t::white());
}


//-------------------------------------------------
//  machine_config( abc806_video )
//-------------------------------------------------

void abc806_state::abc806_video(machine_config &config)
{
	MC6845(config, m_crtc, ABC800_CCLK);
	m_crtc->set_screen(SCREEN_TAG);
	m_crtc->set_show_border_area(true);
	m_crtc->set_char_width(ABC800_CHAR_WIDTH);
	m_crtc->set_update_row_callback(FUNC(abc806_state::abc806_update_row));
	m_crtc->out_hsync_callback().set(FUNC(abc806_state::hs_w));
	m_crtc->out_vsync_callback().set(FUNC(abc806_state::vs_w));

	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_screen_update(FUNC(abc806_state::screen_update));
	screen.set_raw(XTAL(12'000'000), 0x300, 0, 0x1e0, 0x13a, 0, 0xfa);

	PALETTE(config, m_palette, FUNC(abc806_state::abc806_palette), 8);
}


//**************************************************************************
//  SOUND
//**************************************************************************

//-------------------------------------------------
//  DISCRETE_SOUND( abc800 )
//-------------------------------------------------

static DISCRETE_SOUND_START( abc800_discrete )
	DISCRETE_INPUT_LOGIC(NODE_01)
	DISCRETE_LOGIC_JKFLIPFLOP(NODE_02, 1,1, NODE_01, 1, 1) // 74LS393 @ 7C (input _CP0, output Q0)
	DISCRETE_OUTPUT(NODE_02, 5000)
DISCRETE_SOUND_END


//-------------------------------------------------
//  pling_r - speaker read
//-------------------------------------------------

uint8_t abc800_state::pling_r()
{
	if (!machine().side_effects_disabled())
	{
		m_discrete->write(NODE_01, 0);
		m_discrete->write(NODE_01, 1);
	}

	return 0xff;
}



//**************************************************************************
//  MEMORY BANKING
//**************************************************************************

uint8_t abc800_state::read(offs_t offset)
{
	uint8_t data = 0xff;

	if (offset < 0x4000 && (!m_keydtr || m_fetch_charram))
	{
		data = m_video_ram[offset];
	}
	else if (offset < 0x7800)
	{
		data = m_rom->base()[offset];
	}
	else if (offset < 0x8000 && m_fetch_charram)
	{
		data = m_rom->base()[offset];
	}
	else if (offset < 0x8000)
	{
		data = m_char_ram[offset & (m_char_ram_size - 1)];
	}
	else
	{
		data = m_ram->pointer()[offset & m_ram->mask()];
	}

	return data;
}

void abc800_state::write(offs_t offset, uint8_t data)
{
	if (offset < 0x4000 && (!m_keydtr || m_fetch_charram))
	{
		m_video_ram[offset] = data;
	}
	else if (offset >= 0x7800 && offset < 0x8000)
	{
		m_char_ram[offset & (m_char_ram_size - 1)] = data;
	}
	else if (offset >= 0x8000)
	{
		m_ram->pointer()[offset & m_ram->mask()] = data;
	}
}

uint8_t abc802_state::read(offs_t offset)
{
	uint8_t data = 0xff;

	if (offset < 0x8000)
	{
		if (!m_lrs)
		{
			data = m_ram->pointer()[offset];
		}
		else
		{
			if (offset < 0x7800)
			{
				data = m_rom->base()[offset];
			}
			else
			{
				if (m_fetch_charram)
				{
					data = m_rom->base()[offset];
				}
				else
				{
					data = m_char_ram[offset & (m_char_ram_size - 1)];
				}
			}
		}
	}
	else
	{
		data = m_ram->pointer()[offset];
	}

	return data;
}

void abc802_state::write(offs_t offset, uint8_t data)
{
	if (offset < 0x8000)
	{
		if (!m_lrs)
		{
			m_ram->pointer()[offset] = data;
		}
		else if (offset >= 0x7800)
		{
			m_char_ram[offset & (m_char_ram_size - 1)] = data;
		}
	}
	else
	{
		m_ram->pointer()[offset] = data;
	}
}

void abc806_state::read_pal_p4(offs_t offset, bool m1l, bool xml, offs_t &m, bool &romd, bool &ramd, bool &hre, bool &vr)
{
	uint8_t map = m_map[offset >> 12] ^ 0xff;
	bool enl = BIT(map, 7);

	/*
	uint16_t input = 1 << 14 | m_keydtr << 12 | xml << 9 | enl << 8 | m_eme << 7 | m1l << 6 | BIT(offset, 11) << 5 | BIT(offset, 12) << 4 | BIT(offset, 13) << 3 | BIT(offset, 14) << 2 | BIT(offset, 15) << 1 | 1;
	int palout = m_pal->read(input);

	romd = BIT(palout, 0);
	ramd = BIT(palout, 7);
	hre = BIT(palout, 4);
	bool mux = BIT(palout, 6);
	*/

	romd = offset >= 0x8000;
	ramd = offset < 0x8000;
	hre = 0;
	vr = (offset & 0xf800) != 0x7800;
	bool mux = 0;

	if (!m_keydtr && offset < 0x8000)
	{
		romd = 1;
		hre = 1;
		vr = 1;
		mux = 0;
	}

	if (m_eme && !enl)
	{
		romd = 1;
		ramd = 1;
		hre = 1;
		vr = 1;
		mux = 1;
	}

	if (!m1l)
	{
		vr = 1;
	}
/*
    if (!m1l && (offset < 0x7800)
    {
        TODO 0..30k read from videoram if fetch opcode from 7800-7fff
        romd = 1;
        hre = 1;
        mux = 0;
    }
*/
	size_t videoram_mask = m_ram->size() - 0x8001;

	m = (mux ? ((map & 0x7f) << 12 | (offset & 0xfff)) : ((m_hrs & 0xf0) << 11 | (offset & 0x7fff))) & videoram_mask;
}

uint8_t abc806_state::read(offs_t offset)
{
	uint8_t data = 0xff;

	offs_t m = 0;
	bool m1l = 1, xml = 1, romd = 0, ramd = 0, hre = 0, vr = 1;
	read_pal_p4(offset, m1l, xml, m, romd, ramd, hre, vr);

	if (!romd)
	{
		data = m_rom->base()[offset & 0x7fff];
	}

	if (!ramd)
	{
		data = m_ram->pointer()[offset & 0x7fff];
	}

	if (hre)
	{
		data = m_video_ram[m];
	}

	if (!vr)
	{
		data = charram_r(offset & 0x7ff);
	}

	return data;
}

uint8_t abc806_state::m1_r(offs_t offset)
{
	uint8_t data = 0xff;

	offs_t m = 0;
	bool m1l = 0, xml = 1, romd = 0, ramd = 0, hre = 0, vr = 1;
	read_pal_p4(offset, m1l, xml, m, romd, ramd, hre, vr);

	if (!romd)
	{
		data = m_rom->base()[offset & 0x7fff];
	}

	if (!ramd)
	{
		data = m_ram->pointer()[offset & 0x7fff];
	}

	if (hre)
	{
		data = m_video_ram[m];
	}

	if (!vr)
	{
		data = charram_r(offset & 0x7ff);
	}

	return data;
}

void abc806_state::write(offs_t offset, uint8_t data)
{
	offs_t m = 0;
	bool m1l = 1, xml = 1, romd = 0, ramd = 0, hre = 0, vr = 1;
	read_pal_p4(offset, m1l, xml, m, romd, ramd, hre, vr);

	if (!ramd)
	{
		m_ram->pointer()[offset & 0x7fff] = data;
	}

	if (hre)
	{
		m_video_ram[m] = data;
	}

	if (!vr)
	{
		charram_w(offset & 0x7ff, data);
	}
}


//-------------------------------------------------
//  m1_r - opcode read
//-------------------------------------------------

uint8_t abc800_state::m1_r(offs_t offset)
{
	if (offset >= 0x7800 && offset < 0x8000)
	{
		m_fetch_charram = true;

		return m_rom->base()[offset];
	}

	m_fetch_charram = false;

	return m_maincpu->space(AS_PROGRAM).read_byte(offset);
}


//-------------------------------------------------
//  mai_r - memory bank map read
//-------------------------------------------------

uint8_t abc806_state::mai_r(offs_t offset)
{
	int bank = offset >> 12;

	return m_map[bank];
}


//-------------------------------------------------
//  mao_w - memory bank map write
//-------------------------------------------------

void abc806_state::mao_w(offs_t offset, uint8_t data)
{
	/*

	    bit     description

	    0       physical block address bit 0
	    1       physical block address bit 1
	    2       physical block address bit 2
	    3       physical block address bit 3
	    4       physical block address bit 4
	    5
	    6
	    7       allocate block

	*/

	int bank = offset >> 12;

	LOG("MAO %04x %02x %02x\n",offset,bank,data);

	m_map[bank] = data;
}



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

//-------------------------------------------------
//  ADDRESS_MAP( abc800_m1 )
//-------------------------------------------------

void abc800_state::abc800_m1(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(abc800_state::m1_r));
}


//-------------------------------------------------
//  ADDRESS_MAP( abc800_mem )
//-------------------------------------------------

void abc800_state::abc800_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(abc800_state::read), FUNC(abc800_state::write));
}


//-------------------------------------------------
//  ADDRESS_MAP( abc800_io )
//-------------------------------------------------

void abc800_state::abc800_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x00).mirror(0xff18).rw(ABCBUS_TAG, FUNC(abcbus_slot_device::inp_r), FUNC(abcbus_slot_device::out_w));
	map(0x01, 0x01).mirror(0xff18).rw(ABCBUS_TAG, FUNC(abcbus_slot_device::stat_r), FUNC(abcbus_slot_device::cs_w));
	map(0x02, 0x02).mirror(0xff18).w(ABCBUS_TAG, FUNC(abcbus_slot_device::c1_w));
	map(0x03, 0x03).mirror(0xff18).w(ABCBUS_TAG, FUNC(abcbus_slot_device::c2_w));
	map(0x04, 0x04).mirror(0xff18).w(ABCBUS_TAG, FUNC(abcbus_slot_device::c3_w));
	map(0x05, 0x05).mirror(0xff18).w(ABCBUS_TAG, FUNC(abcbus_slot_device::c4_w));
	map(0x05, 0x05).mirror(0xff18).r(FUNC(abc800_state::pling_r));
	map(0x07, 0x07).mirror(0xff18).r(ABCBUS_TAG, FUNC(abcbus_slot_device::rst_r));
	map(0x20, 0x23).mirror(0xff0c).rw(m_dart, FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
	map(0x40, 0x43).mirror(0xff1c).rw(m_sio, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x60, 0x63).mirror(0xff1c).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
}


//-------------------------------------------------
//  ADDRESS_MAP( abc800c_io )
//-------------------------------------------------

void abc800_state::abc800c_io(address_map &map)
{
	abc800_io(map);

	map(0x06, 0x06).mirror(0xff18).w(FUNC(abc800_state::hrs_w));
	map(0x07, 0x07).mirror(0xff18).w(FUNC(abc800_state::hrc_w));
}


//-------------------------------------------------
//  ADDRESS_MAP( abc800m_io )
//-------------------------------------------------

void abc800_state::abc800m_io(address_map &map)
{
	abc800c_io(map);

	map(0x31, 0x31).mirror(0xff06).r(MC6845_TAG, FUNC(mc6845_device::register_r));
	map(0x38, 0x38).mirror(0xff06).w(MC6845_TAG, FUNC(mc6845_device::address_w));
	map(0x39, 0x39).mirror(0xff06).w(MC6845_TAG, FUNC(mc6845_device::register_w));
}


//-------------------------------------------------
//  ADDRESS_MAP( abc802_mem )
//-------------------------------------------------

void abc802_state::abc802_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(abc802_state::read), FUNC(abc802_state::write));
}


//-------------------------------------------------
//  ADDRESS_MAP( abc802_io )
//-------------------------------------------------

void abc802_state::abc802_io(address_map &map)
{
	abc800_io(map);

	map(0x31, 0x31).mirror(0xff06).r(MC6845_TAG, FUNC(mc6845_device::register_r));
	map(0x38, 0x38).mirror(0xff06).w(MC6845_TAG, FUNC(mc6845_device::address_w));
	map(0x39, 0x39).mirror(0xff06).w(MC6845_TAG, FUNC(mc6845_device::register_w));
}


//-------------------------------------------------
//  ADDRESS_MAP( abc806_mem )
//-------------------------------------------------

void abc806_state::abc806_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(abc806_state::read), FUNC(abc806_state::write));
}


//-------------------------------------------------
//  ADDRESS_MAP( abc806_io )
//-------------------------------------------------

void abc806_state::abc806_io(address_map &map)
{
	abc800m_io(map);

	map(0x06, 0x06).mirror(0xff18).w(FUNC(abc806_state::hrs_w));
	map(0x07, 0x07).mirror(0xff18).w(FUNC(abc806_state::hrc_w));
	map(0x34, 0x34).select(0xff00).rw(FUNC(abc806_state::mai_r), FUNC(abc806_state::mao_w));
	map(0x35, 0x35).mirror(0xff00).rw(FUNC(abc806_state::ami_r), FUNC(abc806_state::amo_w));
	map(0x36, 0x36).mirror(0xff00).rw(FUNC(abc806_state::sti_r), FUNC(abc806_state::sto_w));
	map(0x37, 0x37).select(0xff00).rw(FUNC(abc806_state::cli_r), FUNC(abc806_state::sso_w));
}



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

//-------------------------------------------------
//  INPUT_PORTS( abc800 )
//-------------------------------------------------

INPUT_PORTS_START( abc800 )
	PORT_START("SB")
	PORT_DIPNAME( 0xff, 0xaa, "Serial Communications" ) PORT_DIPLOCATION("SB:1,2,3,4,5,6,7,8")
	PORT_DIPSETTING(    0xaa, "Asynchronous, Single Speed" )
	PORT_DIPSETTING(    0x2e, "Asynchronous, Split Speed" )
	PORT_DIPSETTING(    0x50, "Synchronous" )
	PORT_DIPSETTING(    0x8b, "ABC NET" )
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( abc802 )
//-------------------------------------------------

static INPUT_PORTS_START( abc802 )
	PORT_INCLUDE(abc800)

	PORT_START("CONFIG")
	PORT_DIPNAME( 0x01, 0x00, "Clear Screen Time Out" ) PORT_DIPLOCATION("S1:1")
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) ) PORT_DIPLOCATION("S2:1")
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, "Characters Per Line" ) PORT_DIPLOCATION("S3:1")
	PORT_DIPSETTING(    0x00, "40" )
	PORT_DIPSETTING(    0x04, "80" )
	PORT_DIPNAME( 0x08, 0x08, "Frame Frequency" )
	PORT_DIPSETTING(    0x00, "60 Hz" )
	PORT_DIPSETTING(    0x08, "50 Hz" )
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( abc806 )
//-------------------------------------------------

static INPUT_PORTS_START( abc806 )
	PORT_INCLUDE(abc800)
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  cassette
//-------------------------------------------------

TIMER_DEVICE_CALLBACK_MEMBER( abc800_state::cassette_input_tick )
{
	if (m_cassette == nullptr) return;

	int dfd_in = m_cassette->input() > 0;

	if (m_dfd_in && !dfd_in)
	{
		m_sio->rxb_w(!(m_tape_ctr == 15));
	}

	if (!dfd_in && (m_tape_ctr == 15))
	{
		m_tape_ctr = 4;
	}

	m_dfd_in = dfd_in;
}

void abc800_state::cassette_output_tick(int state)
{
	if (m_cassette == nullptr) return;

	if (m_ctc_z0 && !state)
	{
		m_sio_txcb = !m_sio_txcb;
		m_sio->txcb_w(!m_sio_txcb);

		if (m_sio_txdb || m_sio_txcb)
		{
			m_dfd_out = !m_dfd_out;
			m_cassette->output((m_dfd_out & m_sio_rtsb) ? +1.0 : -1.0);
		}

		if (m_tape_ctr < 15)
		{
			m_tape_ctr++;
		}

		m_sio->rxcb_w(m_tape_ctr == 15);
	}

	m_ctc_z0 = state;
}


//-------------------------------------------------
//  Z80CTC
//-------------------------------------------------

TIMER_DEVICE_CALLBACK_MEMBER( abc800_state::ctc_tick )
{
	m_ctc->trg0(1);
	m_ctc->trg0(0);

	m_ctc->trg1(1);
	m_ctc->trg1(0);

	m_ctc->trg2(1);
	m_ctc->trg2(0);
}

void abc800_state::ctc_z0_w(int state)
{
	if (BIT(m_sb, 2))
	{
		m_sio->txca_w(state);
		m_ctc->trg3(state);
	}

	cassette_output_tick(state);
}

void abc800_state::ctc_z1_w(int state)
{
	if (BIT(m_sb, 3))
	{
		m_sio->rxca_w(state);
	}

	if (BIT(m_sb, 4))
	{
		m_sio->txca_w(state);
		m_ctc->trg3(state);
	}
}


//-------------------------------------------------
//  Z80SIO
//-------------------------------------------------

void abc800_state::sio_txdb_w(int state)
{
	m_sio_txdb = state;
}

void abc800_state::sio_dtrb_w(int state)
{
	if (m_cassette == nullptr) return;

	if (state)
	{
		m_cassette->change_state(CASSETTE_MOTOR_ENABLED, CASSETTE_MASK_MOTOR);
	}
	else
	{
		m_cassette->change_state(CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
	}
}

void abc800_state::sio_rtsb_w(int state)
{
	if (m_cassette == nullptr) return;

	m_sio_rtsb = state;

	if (!m_sio_rtsb)
	{
		m_cassette->output(-1.0);
	}
}


//-------------------------------------------------
//  Z80DART abc800
//-------------------------------------------------

void abc800_state::keydtr_w(int state)
{
	LOG("%s KEYDTR %u\n",machine().describe_context(),state);

	m_keydtr = state;
}


//-------------------------------------------------
//  Z80DART abc802
//-------------------------------------------------

void abc802_state::lrs_w(int state)
{
	LOG("%s LRS %u\n",machine().describe_context(),state);

	m_lrs = state;
}

void abc802_state::mux80_40_w(int state)
{
	LOG("%s 80/40 MUX %u\n",machine().describe_context(),state);

	m_80_40_mux = state;
}


//-------------------------------------------------
//  z80_daisy_config abc800_daisy_chain
//-------------------------------------------------

static const z80_daisy_config abc800_daisy_chain[] =
{
	{ Z80CTC_TAG },
	{ Z80SIO_TAG },
	{ Z80DART_TAG },
	{ nullptr }
};



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  abc800
//-------------------------------------------------

void abc800_state::machine_start()
{
	// register for state saving
	save_item(NAME(m_fetch_charram));
	save_item(NAME(m_sb));
	save_item(NAME(m_ctc_z0));
	save_item(NAME(m_sio_txcb));
	save_item(NAME(m_sio_txdb));
	save_item(NAME(m_sio_rtsb));
	save_item(NAME(m_dfd_out));
	save_item(NAME(m_dfd_in));
	save_item(NAME(m_tape_ctr));
}

void abc800_state::machine_reset()
{
	m_sb = m_io_sb->read();

	m_dart->ria_w(1);

	// 50/60 Hz
	m_dart->ctsb_w(0); // 0 = 50Hz, 1 = 60Hz

	m_dfd_in = 0;
}


//-------------------------------------------------
//  abc802
//-------------------------------------------------

void abc802_state::machine_start()
{
	// register for state saving
	save_item(NAME(m_sb));
	save_item(NAME(m_ctc_z0));
	save_item(NAME(m_sio_txcb));
	save_item(NAME(m_sio_txdb));
	save_item(NAME(m_sio_rtsb));
	save_item(NAME(m_dfd_out));
	save_item(NAME(m_dfd_in));
	save_item(NAME(m_tape_ctr));
	save_item(NAME(m_lrs));
}

void abc802_state::machine_reset()
{
	uint8_t config = m_config->read();
	m_sb = m_io_sb->read();

	// memory banking
	m_lrs = 1;

	// clear screen time out (S1)
	m_sio->dcdb_w(BIT(config, 0));

	// unknown (S2)
	m_sio->ctsb_w(BIT(config, 1));

	// 40/80 char (S3)
	m_dart->ria_w(BIT(config, 2)); // 0 = 40, 1 = 80

	// 50/60 Hz
	m_dart->ctsb_w(BIT(config, 3)); // 0 = 50Hz, 1 = 60Hz

	m_dfd_in = 0;
}


//-------------------------------------------------
//  abc806
//-------------------------------------------------

void abc806_state::machine_start()
{
	// register for state saving
	save_item(NAME(m_fetch_charram));
	save_item(NAME(m_sb));
	save_item(NAME(m_ctc_z0));
	save_item(NAME(m_sio_txcb));
	save_item(NAME(m_sio_txdb));
	save_item(NAME(m_sio_rtsb));
	save_item(NAME(m_dfd_out));
	save_item(NAME(m_dfd_in));
	save_item(NAME(m_tape_ctr));
	save_item(NAME(m_keydtr));
	save_item(NAME(m_eme));
	save_item(NAME(m_map));
}

void abc806_state::machine_reset()
{
	m_sb = m_io_sb->read();

	m_dart->ria_w(1);

	// 50/60 Hz
	m_dart->ctsb_w(0); // 0 = 50Hz, 1 = 60Hz

	m_dfd_in = 0;

	m_hrs = 0;
}


//-------------------------------------------------
//  bac quickload
//-------------------------------------------------

QUICKLOAD_LOAD_MEMBER(abc800_state::quickload_cb)
{
	address_space &space = m_maincpu->space(AS_PROGRAM);

	size_t quickload_size = image.length();
	std::vector<uint8_t> data(quickload_size);
	image.fread(&data[0], quickload_size);

	uint8_t prstat = data[2];
	uint16_t prgsz = (data[5] << 8) | data[4];
	uint16_t varsz = (data[7] << 8) | data[6];
	uint16_t varad = (data[9] << 8) | data[8];
	uint16_t comsz = (data[13] << 8) | data[12];
	uint16_t comcs = (data[14] << 8) | data[15];
	uint16_t comtop = 0x8000 + comsz;
	uint16_t vartb = comtop;

	uint16_t heap = 0x8000 + comsz + varad;
	uint16_t bofa = 0xf169 - prgsz - 8;
	uint16_t eofa = bofa;

	for (int i = 0; i < prgsz; i++) {
		space.write_byte(eofa++, data[i]);
	}
	eofa--;

	for (int i = prgsz; i < quickload_size; i++) {
		space.write_byte(heap++, data[i]);
	}
	heap = 0x8000 + comsz + varsz;

	space.write_byte(0xff06, bofa & 0xff);
	space.write_byte(0xff07, bofa >> 8);

	space.write_byte(0xff08, eofa & 0xff);
	space.write_byte(0xff09, eofa >> 8);

	space.write_byte(0xff0a, heap & 0xff);
	space.write_byte(0xff0b, heap >> 8);

	space.write_byte(0xff26, prstat);

	space.write_byte(0xff2c, vartb & 0xff);
	space.write_byte(0xff2d, vartb >> 8);

	space.write_byte(0xff30, comtop & 0xff);
	space.write_byte(0xff31, comtop >> 8);

	space.write_byte(0xff32, comcs & 0xff);
	space.write_byte(0xff33, comcs >> 8);

	return std::make_pair(std::error_condition(), std::string());
}

static void printer_devices(device_slot_interface &device)
{
	device.option_add("printer", SERIAL_PRINTER);
	device.option_add("teletex800", TELETEX_800);
}



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

//-------------------------------------------------
//  machine_config( common )
//-------------------------------------------------

void abc800_state::common(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, ABC800_X01/2/2);
	m_maincpu->set_daisy_config(abc800_daisy_chain);
	m_maincpu->set_addrmap(AS_OPCODES, &abc800_state::abc800_m1);

	// peripheral hardware
	Z80CTC(config, m_ctc, ABC800_X01/2/2);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->zc_callback<0>().set(FUNC(abc800_state::ctc_z0_w));
	m_ctc->zc_callback<1>().set(FUNC(abc800_state::ctc_z1_w));
	m_ctc->zc_callback<2>().set(m_dart, FUNC(z80dart_device::rxca_w));
	m_ctc->zc_callback<2>().append(m_dart, FUNC(z80dart_device::txca_w));
	TIMER(config, TIMER_CTC_TAG).configure_periodic(FUNC(abc800_state::ctc_tick), attotime::from_hz(ABC800_X01/2/2/2));

	Z80SIO(config, m_sio, ABC800_X01/2/2);
	m_sio->out_txda_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_txd));
	m_sio->out_dtra_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_dtr));
	m_sio->out_rtsa_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_rts));
	m_sio->out_txdb_callback().set(FUNC(abc800_state::sio_txdb_w));
	m_sio->out_dtrb_callback().set(FUNC(abc800_state::sio_dtrb_w));
	m_sio->out_rtsb_callback().set(FUNC(abc800_state::sio_rtsb_w));
	m_sio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80DART(config, m_dart, ABC800_X01/2/2);
	m_dart->out_txda_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_txd));
	m_dart->out_dtra_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_dtr));
	m_dart->out_rtsa_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_rts));
	m_dart->out_txdb_callback().set(ABC_KEYBOARD_PORT_TAG, FUNC(abc_keyboard_port_device::txd_w));
	m_dart->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	DISCRETE(config, m_discrete, abc800_discrete).add_route(ALL_OUTPUTS, "mono", 0.80);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->set_interface("abc800_cass");
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	TIMER(config, TIMER_CASSETTE_TAG).configure_periodic(FUNC(abc800_state::cassette_input_tick), attotime::from_hz(44100));

	rs232_port_device &rs232a(RS232_PORT(config, RS232_A_TAG, printer_devices, nullptr));
	rs232a.rxd_handler().set(m_dart, FUNC(z80dart_device::rxa_w));
	rs232a.dcd_handler().set(m_dart, FUNC(z80dart_device::dcda_w));
	rs232a.cts_handler().set(m_dart, FUNC(z80dart_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, RS232_B_TAG, default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_sio, FUNC(z80sio_device::rxa_w));
	rs232b.dcd_handler().set(m_sio, FUNC(z80sio_device::dcda_w));
	rs232b.cts_handler().set(m_sio, FUNC(z80sio_device::ctsa_w));

	ABCBUS_SLOT(config, ABCBUS_TAG, ABC800_X01/2/2, abcbus_cards, nullptr);

	// software list
	SOFTWARE_LIST(config, "flop_list_830").set_original("abc830_flop");
	SOFTWARE_LIST(config, "flop_list_832").set_original("abc832_flop");
	SOFTWARE_LIST(config, "flop_list_838").set_original("abc838_flop");
	SOFTWARE_LIST(config, "hdd_list").set_original("abc800_hdd");

	// quickload
	QUICKLOAD(config, m_quickload, "bac", attotime::from_seconds(2)).set_load_callback(FUNC(abc800_state::quickload_cb));
	m_quickload->set_interface("abc800_quik");
}


//-------------------------------------------------
//  machine_config( abc800c )
//-------------------------------------------------

void abc800c_state::abc800c(machine_config &config)
{
	common(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &abc800_state::abc800_mem);
	m_maincpu->set_addrmap(AS_IO, &abc800c_state::abc800c_io);

	// video hardware
	abc800c_video(config);

	// peripheral hardware
	m_dart->out_dtrb_callback().set(FUNC(abc800_state::keydtr_w));

	abc_keyboard_port_device &kb(ABC_KEYBOARD_PORT(config, ABC_KEYBOARD_PORT_TAG, abc800_keyboard_devices, "abc800"));
	kb.out_rx_handler().set(m_dart, FUNC(z80dart_device::rxb_w));
	kb.out_trxc_handler().set(m_dart, FUNC(z80dart_device::rxtxcb_w));
	kb.out_keydown_handler().set(m_dart, FUNC(z80dart_device::dcdb_w));

	subdevice<abcbus_slot_device>(ABCBUS_TAG)->set_default_option("abc830");

	// internal ram
	RAM(config, RAM_TAG).set_default_size("32K");
}


//-------------------------------------------------
//  machine_config( abc800m )
//-------------------------------------------------

void abc800m_state::abc800m(machine_config &config)
{
	common(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &abc800_state::abc800_mem);
	m_maincpu->set_addrmap(AS_IO, &abc800m_state::abc800m_io);

	// video hardware
	abc800m_video(config);

	// peripheral hardware
	m_dart->out_dtrb_callback().set(FUNC(abc800_state::keydtr_w));

	abc_keyboard_port_device &kb(ABC_KEYBOARD_PORT(config, ABC_KEYBOARD_PORT_TAG, abc800_keyboard_devices, "abc800"));
	kb.out_rx_handler().set(m_dart, FUNC(z80dart_device::rxb_w));
	kb.out_trxc_handler().set(m_dart, FUNC(z80dart_device::rxtxcb_w));
	kb.out_keydown_handler().set(m_dart, FUNC(z80dart_device::dcdb_w));

	subdevice<abcbus_slot_device>(ABCBUS_TAG)->set_default_option("abc830");

	// internal ram
	RAM(config, RAM_TAG).set_default_size("32K");
}


//-------------------------------------------------
//  machine_config( abc802 )
//-------------------------------------------------

void abc802_state::abc802(machine_config &config)
{
	common(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &abc802_state::abc802_mem);
	m_maincpu->set_addrmap(AS_IO, &abc802_state::abc802_io);

	// video hardware
	abc802_video(config);

	// peripheral hardware
	m_dart->out_dtrb_callback().set(FUNC(abc802_state::lrs_w));
	m_dart->out_rtsb_callback().set(FUNC(abc802_state::mux80_40_w));

	abc_keyboard_port_device &kb(ABC_KEYBOARD_PORT(config, ABC_KEYBOARD_PORT_TAG, abc_keyboard_devices, "abc55"));
	kb.out_rx_handler().set(m_dart, FUNC(z80dart_device::rxb_w));
	kb.out_trxc_handler().set(m_dart, FUNC(z80dart_device::rxtxcb_w));
	kb.out_keydown_handler().set(m_dart, FUNC(z80dart_device::dcdb_w));

	subdevice<abcbus_slot_device>(ABCBUS_TAG)->set_default_option("abc834");

	// internal ram
	RAM(config, RAM_TAG).set_default_size("64K");
}


//-------------------------------------------------
//  machine_config( abc806 )
//-------------------------------------------------

void abc806_state::abc806(machine_config &config)
{
	common(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &abc806_state::abc806_mem);
	m_maincpu->set_addrmap(AS_IO, &abc806_state::abc806_io);

	LS259(config, m_sto); // 74ALS259 @ 13G
	m_sto->q_out_cb<0>().set(FUNC(abc806_state::eme_w));
	m_sto->q_out_cb<1>().set(FUNC(abc806_state::_40_w));
	m_sto->q_out_cb<2>().set(FUNC(abc806_state::hru2_a8_w));
	m_sto->q_out_cb<3>().set(FUNC(abc806_state::prot_ini_w));
	m_sto->q_out_cb<4>().set(FUNC(abc806_state::txoff_w));
	m_sto->q_out_cb<5>().set(m_rtc, FUNC(e0516_device::cs_w)).invert();
	m_sto->q_out_cb<6>().set(m_rtc, FUNC(e0516_device::clk_w));
	m_sto->q_out_cb<7>().set(m_rtc, FUNC(e0516_device::dio_w)); // 74LS125A buffer as fake open collector
	m_sto->q_out_cb<7>().append(FUNC(abc806_state::prot_din_w));

	// video hardware
	abc806_video(config);

	// peripheral hardware
	m_dart->out_dtrb_callback().set(FUNC(abc800_state::keydtr_w));

	E0516(config, m_rtc, ABC806_X02);
	m_rtc->outsel_rd_cb().set_constant(1);

	abc_keyboard_port_device &kb(ABC_KEYBOARD_PORT(config, ABC_KEYBOARD_PORT_TAG, abc_keyboard_devices, "abc77"));
	kb.out_rx_handler().set(m_dart, FUNC(z80dart_device::rxb_w));
	kb.out_trxc_handler().set(m_dart, FUNC(z80dart_device::rxtxcb_w));
	kb.out_keydown_handler().set(m_dart, FUNC(z80dart_device::dcdb_w));

	subdevice<abcbus_slot_device>(ABCBUS_TAG)->set_default_option("abc832");

	// internal ram
	RAM(config, RAM_TAG).set_default_size("160K").set_extra_options("544K");

	// software list
	SOFTWARE_LIST(config, "flop_list_806").set_original("abc806_flop");
}



//**************************************************************************
//  ROMS
//**************************************************************************

/*

    ABC800 DOS ROMs

    Label       Drive Type
    ----------------------------
    ABC 6-1X    ABC830,DD82,DD84
    800 8"      DD88
    ABC 6-2X    ABC832
    ABC 6-3X    ABC838
    ABC 6-5X    UFD dos
    ABC 6-52    UFD dos with HD
    UFD 6.XX    Winchester


    Floppy Controllers

    Art N/O
    --------
    55 10761-01     "old" controller
    55 10828-01     "old" controller
    55 20900-0x
    55 21046-11     Luxor Conkort   25 pin D-sub connector
    55 21046-21     Luxor Conkort   34 pin FDD connector
    55 21046-41     Luxor Conkort   both of the above

*/

//-------------------------------------------------
//  ROM( abc800c )
//-------------------------------------------------

ROM_START( abc800c )
	ROM_REGION( 0x8000, Z80_TAG, 0 )
	ROM_DEFAULT_BIOS("6-52")
	ROM_LOAD( "abc c-1.1m", 0x0000, 0x1000, CRC(37009d29) SHA1(a1acd817ff8c2ed93935a163c5cf3d83d8bd6fb5) )
	ROM_LOAD( "abc 1-1.1l", 0x1000, 0x1000, CRC(ff15a28d) SHA1(bb4523ab1d190bc787f1923567b86795f66c26e2) )
	ROM_LOAD( "abc 2-1.1k", 0x2000, 0x1000, CRC(6ff2f528) SHA1(32d1639769c4a20b7a45c44f4c6993f77397f7a1) )
	ROM_LOAD( "abc 3-1.1j", 0x3000, 0x1000, CRC(9a43c47a) SHA1(964eb28e3d9779d1b13e0e938495133bbdb3aed3) )
	ROM_LOAD( "abc 4-1.2m", 0x4000, 0x1000, CRC(ba18db9c) SHA1(0c4543766fe9b10bee333f3f832f6f2209e449f8) )
	ROM_LOAD( "abc 5-1.2l", 0x5000, 0x1000, CRC(abbeb026) SHA1(44ebe417e3fa8d7878c23b56782aac8b443f1bfc) )
	ROM_SYSTEM_BIOS( 0, "6-1", "ABC-DOS (1981-01-12)" )
	ROMX_LOAD( "abc 6-1.2k", 0x6000, 0x1000, CRC(4bd5e808) SHA1(5ca0a60571de6cfa3d6d166e0cde3c78560569f3), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "6-23", "ABC-DOS (1982-03-24)" )
	ROMX_LOAD( "abc 6-23.2k", 0x6000, 0x1000, CRC(63a5646c) SHA1(e97c94d97ea19821e4c576e9006ed1f1f5e3d7cb), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "6-13", "ABC-DOS (1982-07-19)" )
	ROMX_LOAD( "abc 6-13.2k", 0x6000, 0x1000, CRC(6fa71fb6) SHA1(b037dfb3de7b65d244c6357cd146376d4237dab6), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "6-11", "UFD-DOS v.19 (1983-05-31)" )
	ROMX_LOAD( "abc 6-11.2k", 0x6000, 0x1000, CRC(2a45be80) SHA1(bf08a18a74e8bdaee2656a3c8246c0122398b58f), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "6-52", "UFD-DOS v.20 (1984-03-02)" )
	ROMX_LOAD( "abc 6-52.2k", 0x6000, 0x1000, CRC(c311b57a) SHA1(4bd2a541314e53955a0d53ef2f9822a202daa485), ROM_BIOS(4) )
	ROM_LOAD_OPTIONAL( "abc 7-2.2j", 0x7000, 0x1000, CRC(fd137866) SHA1(3ac914d90db1503f61397c0ea26914eb38725044) )
	ROM_LOAD_OPTIONAL( "abc 7-21.2j", 0x7000, 0x1000, CRC(4d2b589f) SHA1(8772119e002943d9891f77eb59d1d2491a834ccd) )
	ROM_LOAD( "abc 7-22.2j", 0x7000, 0x1000, CRC(774511ab) SHA1(5171e43213a402c2d96dee33453c8306ac1aafc8) )

	ROM_REGION( 0x20, "hru", 0 )
	ROM_LOAD( "hru i.4g", 0x00, 0x20, CRC(d970a972) SHA1(c47fdd61fccc68368d42f03a01c7af90ab1fe1ab) )

	ROM_REGION( 0x200, "hru2", 0 )
	ROM_LOAD( "hru ii.3a", 0x000, 0x200, CRC(8e9d7cdc) SHA1(4ad16dc0992e31cdb2e644c7be81d334a56f7ad6) )
ROM_END


//-------------------------------------------------
//  ROM( abc800m )
//-------------------------------------------------

ROM_START( abc800m )
	ROM_REGION( 0x8000, Z80_TAG, 0 )
	ROM_DEFAULT_BIOS("6-52")
	ROM_LOAD( "abc m-12.1m", 0x0000, 0x1000, CRC(f85b274c) SHA1(7d0f5639a528d8d8130a22fe688d3218c77839dc) )
	ROM_LOAD( "abc 1-12.1l", 0x1000, 0x1000, CRC(1e99fbdc) SHA1(ec6210686dd9d03a5ed8c4a4e30e25834aeef71d) )
	ROM_LOAD( "abc 2-12.1k", 0x2000, 0x1000, CRC(ac196ba2) SHA1(64fcc0f03fbc78e4c8056e1fa22aee12b3084ef5) )
	ROM_LOAD( "abc 3-12.1j", 0x3000, 0x1000, CRC(3ea2b5ee) SHA1(5a51ac4a34443e14112a6bae16c92b5eb636603f) )
	ROM_LOAD( "abc 4-12.2m", 0x4000, 0x1000, CRC(695cb626) SHA1(9603ce2a7b2d7b1cbeb525f5493de7e5c1e5a803) )
	ROM_LOAD( "abc 5-12.2l", 0x5000, 0x1000, CRC(b4b02358) SHA1(95338efa3b64b2a602a03bffc79f9df297e9534a) )
	ROM_SYSTEM_BIOS( 0, "6-1", "ABC-DOS (1981-01-12)" )
	ROMX_LOAD( "abc 6-1.2k", 0x6000, 0x1000, CRC(4bd5e808) SHA1(5ca0a60571de6cfa3d6d166e0cde3c78560569f3), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "6-23", "ABC-DOS (1982-03-24)" )
	ROMX_LOAD( "abc 6-23.2k", 0x6000, 0x1000, CRC(63a5646c) SHA1(e97c94d97ea19821e4c576e9006ed1f1f5e3d7cb), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "6-13", "ABC-DOS (1982-07-19)" )
	ROMX_LOAD( "abc 6-13.2k", 0x6000, 0x1000, CRC(6fa71fb6) SHA1(b037dfb3de7b65d244c6357cd146376d4237dab6), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "6-11", "UFD-DOS v.19 (1983-05-31)" )
	ROMX_LOAD( "abc 6-11.2k", 0x6000, 0x1000, CRC(2a45be80) SHA1(bf08a18a74e8bdaee2656a3c8246c0122398b58f), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "6-52", "UFD-DOS v.20 (1984-03-02)" )
	ROMX_LOAD( "abc 6-52.2k", 0x6000, 0x1000, CRC(c311b57a) SHA1(4bd2a541314e53955a0d53ef2f9822a202daa485), ROM_BIOS(4) )
	ROM_LOAD_OPTIONAL( "abc 7-2.2j", 0x7000, 0x1000, CRC(fd137866) SHA1(3ac914d90db1503f61397c0ea26914eb38725044) )
	ROM_LOAD_OPTIONAL( "abc 7-21.2j", 0x7000, 0x1000, CRC(4d2b589f) SHA1(8772119e002943d9891f77eb59d1d2491a834ccd) )
	ROM_LOAD( "abc 7-22.2j", 0x7000, 0x1000, CRC(774511ab) SHA1(5171e43213a402c2d96dee33453c8306ac1aafc8) )

	ROM_REGION( 0x800, MC6845_TAG, 0 )
	ROM_LOAD( "vum se.7c", 0x000, 0x800, CRC(f9152163) SHA1(997313781ddcbbb7121dbf9eb5f2c6b4551fc799) )

	ROM_REGION( 0x20, "hru", 0 )
	ROM_LOAD( "hru i.4g", 0x00, 0x20, CRC(d970a972) SHA1(c47fdd61fccc68368d42f03a01c7af90ab1fe1ab) )

	ROM_REGION( 0x200, "hru2", 0 )
	ROM_LOAD( "hru ii.3a", 0x000, 0x200, CRC(8e9d7cdc) SHA1(4ad16dc0992e31cdb2e644c7be81d334a56f7ad6) )
ROM_END


//-------------------------------------------------
//  ROM( dtc )
//-------------------------------------------------

ROM_START( dtc )
	ROM_REGION( 0x8000, Z80_TAG, 0 )
	ROM_LOAD( "abc m-12.1m", 0x0000, 0x1000, CRC(f85b274c) SHA1(7d0f5639a528d8d8130a22fe688d3218c77839dc) )
	ROM_LOAD( "abc 1-12.1l", 0x1000, 0x1000, CRC(1e99fbdc) SHA1(ec6210686dd9d03a5ed8c4a4e30e25834aeef71d) )
	ROM_LOAD( "abc 2-12.1k", 0x2000, 0x1000, CRC(ac196ba2) SHA1(64fcc0f03fbc78e4c8056e1fa22aee12b3084ef5) )
	ROM_LOAD( "facit 3-13.1j", 0x3000, 0x1000, CRC(f5e4a43c) SHA1(f7c33906cd3a9b6ebfb4faa6c49e9813d610b0a1) )
	ROM_LOAD( "abc 4-12.2m", 0x4000, 0x1000, CRC(695cb626) SHA1(9603ce2a7b2d7b1cbeb525f5493de7e5c1e5a803) )
	ROM_LOAD( "abc 5-12.2l", 0x5000, 0x1000, CRC(b4b02358) SHA1(95338efa3b64b2a602a03bffc79f9df297e9534a) )
	ROM_LOAD( "abc 6-52.2k", 0x6000, 0x1000, CRC(c311b57a) SHA1(4bd2a541314e53955a0d53ef2f9822a202daa485) )
	ROM_LOAD( "abc 7-22.2j", 0x7000, 0x1000, CRC(774511ab) SHA1(5171e43213a402c2d96dee33453c8306ac1aafc8) )

	ROM_REGION( 0x800, MC6845_TAG, 0 )
	ROM_LOAD( "vum se.7c", 0x000, 0x800, CRC(f9152163) SHA1(997313781ddcbbb7121dbf9eb5f2c6b4551fc799) )

	ROM_REGION( 0x20, "hru", 0 )
	ROM_LOAD( "hru i.4g", 0x00, 0x20, CRC(d970a972) SHA1(c47fdd61fccc68368d42f03a01c7af90ab1fe1ab) )

	ROM_REGION( 0x200, "hru2", 0 )
	ROM_LOAD( "hru ii.3a", 0x000, 0x200, CRC(8e9d7cdc) SHA1(4ad16dc0992e31cdb2e644c7be81d334a56f7ad6) )
ROM_END


//-------------------------------------------------
//  ROM( abc802 )
//-------------------------------------------------

ROM_START( abc802 )
	ROM_REGION( 0x8000, Z80_TAG, 0 )
	ROM_DEFAULT_BIOS("ufd20")
	ROM_LOAD( "abc 02-11.9f",  0x0000, 0x2000, CRC(b86537b2) SHA1(4b7731ef801f9a03de0b5acd955f1e4a1828355d) )
	ROM_LOAD( "abc 12-11.11f", 0x2000, 0x2000, CRC(3561c671) SHA1(f12a7c0fe5670ffed53c794d96eb8959c4d9f828) )
	ROM_LOAD( "abc 22-11.12f", 0x4000, 0x2000, CRC(8dcb1cc7) SHA1(535cfd66c84c0370fd022d6edf702d3d1ad1b113) )
	ROM_SYSTEM_BIOS( 0, "abc", "ABC-DOS (1983-05-31)" )
	ROMX_LOAD( "abc 32-12.14f", 0x6000, 0x2000, CRC(23cd0f43) SHA1(639daec4565dcdb4de408b808d0c6cd97baa35d2), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "ufd19", "UFD-DOS v.19 (1984-03-02)" )
	ROMX_LOAD( "abc 32-21.14f", 0x6000, 0x2000, CRC(57050b98) SHA1(b977e54d1426346a97c98febd8a193c3e8259574), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "ufd20", "UFD-DOS v.20 (1984-04-03)" )
	ROMX_LOAD( "abc 32-31.14f", 0x6000, 0x2000, CRC(fc8be7a8) SHA1(a1d4cb45cf5ae21e636dddfa70c99bfd2050ad60), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "mica620", "MICA DOS v.20 (1984-03-02)" )
	ROMX_LOAD( "mica820.14f",   0x6000, 0x2000, CRC(edf998af) SHA1(daae7e1ff6ef3e0ddb83e932f324c56f4a98f79b), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "luxnet01", "LUXNET node 1" )
	ROMX_LOAD( "322n01.14f",   0x6000, 0x2000, CRC(0911bc92) SHA1(bf58b3be40ce07638eb265aa2dd97c5562a0c41b), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "luxnet02", "LUXNET node 2" )
	ROMX_LOAD( "322n02.14f",   0x6000, 0x2000, CRC(2384baec) SHA1(8ae0371242c201913b2d33a75f670d2bccf29582), ROM_BIOS(5) )

	ROM_REGION( 0x1000, MC6845_TAG, 0 )
	ROM_LOAD( "abc t02-1.3g", 0x0000, 0x1000, CRC(4d54eed8) SHA1(04cb5fc5f3d7ba9b9a5ae0ec94241d1fe83647f7) ) // 64 90191-01

	ROM_REGION( 0x400, "plds", 0 )
	/*
	    1   CLK
	    2   CUR
	    3   FC
	    4   IHS
	    5   LL
	    6   ATE
	    7   ATD
	    8   AT0
	    9   AT1
	    10  GND
	    11  GND
	    12  >O1
	    13  >O0
	    14
	    15
	    16  >RI
	    17  >RG
	    18  INV
	    19  >D
	    20  Vcc
	*/
	ROM_LOAD( "abc p2-1.2g", 0x000, 0x400, NO_DUMP ) // PAL16R4
ROM_END


//-------------------------------------------------
//  ROM( abc806 )
//-------------------------------------------------

ROM_START( abc806 )
	ROM_REGION( 0x10000, Z80_TAG, 0 )
	ROM_DEFAULT_BIOS("ufd20")
	ROM_LOAD( "abc 06-11.1m",  0x0000, 0x1000, CRC(27083191) SHA1(9b45592273a5673e4952c6fe7965fc9398c49827) ) // BASIC PROM ABC 06-11 "64 90231-02"
	ROM_LOAD( "abc 16-11.1l",  0x1000, 0x1000, CRC(eb0a08fd) SHA1(f0b82089c5c8191fbc6a3ee2c78ce730c7dd5145) ) // BASIC PROM ABC 16-11 "64 90232-02"
	ROM_LOAD( "abc 26-11.1k",  0x2000, 0x1000, CRC(97a95c59) SHA1(013bc0a2661f4630c39b340965872bf607c7bd45) ) // BASIC PROM ABC 26-11 "64 90233-02"
	ROM_LOAD( "abc 36-11.1j",  0x3000, 0x1000, CRC(b50e418e) SHA1(991a59ed7796bdcfed310012b2bec50f0b8df01c) ) // BASIC PROM ABC 36-11 "64 90234-02"
	ROM_LOAD( "abc 46-11.2m",  0x4000, 0x1000, CRC(17a87c7d) SHA1(49a7c33623642b49dea3d7397af5a8b9dde8185b) ) // BASIC PROM ABC 46-11 "64 90235-02"
	ROM_LOAD( "abc 56-11.2l",  0x5000, 0x1000, CRC(b4b02358) SHA1(95338efa3b64b2a602a03bffc79f9df297e9534a) ) // BASIC PROM ABC 56-11 "64 90236-02"
	ROM_SYSTEM_BIOS( 0, "ufd19", "UFD-DOS v.19 (1984-03-02)" )
	ROMX_LOAD( "abc 66-21.2k", 0x6000, 0x1000, CRC(c311b57a) SHA1(4bd2a541314e53955a0d53ef2f9822a202daa485), ROM_BIOS(0) ) // DOS PROM ABC 66-21 "64 90314-01"
	ROM_SYSTEM_BIOS( 1, "ufd20", "UFD-DOS v.20 (1984-04-03)" )
	ROMX_LOAD( "abc 66-31.2k", 0x6000, 0x1000, CRC(a2e38260) SHA1(0dad83088222cb076648e23f50fec2fddc968883), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "mica20", "MICA DOS v.20 (1984-04-03)" )
	ROMX_LOAD( "mica2006.2k",  0x6000, 0x1000, CRC(58bc2aa8) SHA1(0604bd2396f7d15fcf3d65888b4b673f554037c0), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "catnet", "CAT-NET CMD 8.5" )
	ROMX_LOAD( "cmd8_5.2k",    0x6000, 0x1000, CRC(25430ef7) SHA1(03a36874c23c215a19b0be14ad2f6b3b5fb2c839), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "luxnet", "LUXNET" )
	ROMX_LOAD( "ln806.2k",    0x6000, 0x1000, CRC(034b5991) SHA1(ba7f8653f4e516687a4399abef450e361f2bfd20), ROM_BIOS(4) )
	ROM_LOAD_OPTIONAL( "abc 76-11.2j",  0x7000, 0x1000, CRC(3eb5f6a1) SHA1(02d4e38009c71b84952eb3b8432ad32a98a7fe16) ) // Options-PROM ABC 76-11 "64 90238-02"
	ROM_LOAD( "abc 76-xx.2j",  0x7000, 0x1000, CRC(b364cc49) SHA1(9a2c373778856a31902cdbd2ae3362c200a38e24) ) // Enhanced Options-PROM

	ROM_REGION( 0x1000, MC6845_TAG, 0 )
	ROM_LOAD( "abc t6-11.7c", 0x0000, 0x1000, CRC(b17c51c5) SHA1(e466e80ec989fbd522c89a67d274b8f0bed1ff72) ) // 64 90243-01

	ROM_REGION( 0x200, "rad", 0 )
	ROM_LOAD( "60 90241-01.9b", 0x000, 0x200, CRC(ad549ebb) SHA1(4fe228ce3b84ed6f0ffd5431f2f33b94c3e5268b) ) // "RAD" 7621/7643 (82S131/82S137), character line address

	ROM_REGION( 0x20, "hru", 0 )
	ROM_LOAD( "60 90128-01.6e", 0x00, 0x20, CRC(d970a972) SHA1(c47fdd61fccc68368d42f03a01c7af90ab1fe1ab) ) // "HRU I" 7603 (82S123), HR horizontal timing and video memory access

	ROM_REGION( 0x200, "hru2", 0 )
	ROM_LOAD( "60 90127-01.12g", 0x000, 0x200, CRC(8e9d7cdc) SHA1(4ad16dc0992e31cdb2e644c7be81d334a56f7ad6) ) // "HRU II" 7621 (82S131), ABC800C HR compatibility mode palette

	ROM_REGION( 0x200, "v50", 0 )
	ROM_LOAD( "60 90242-01.7e", 0x000, 0x200, CRC(788a56d8) SHA1(d81e55cdddc36f5d41bf0a33104c75fac590b764) ) // "V50" 7621 (82S131), HR vertical timing 50Hz

	//ROM_REGION( 0x200, "v60", 0 )
	//ROM_LOAD( "60 90319-01.7e", 0x000, 0x200, NO_DUMP ) // "V60" 7621 (82S131), HR vertical timing 60Hz

	//ROM_REGION( 0x400, "att_hand", 0 )
	/*
	    1   E6P (RAD A8)
	    2   THP (chargen A12)
	    3   CCLK
	    4   B0 (TX ATT 6)
	    5   B1 (TX ATT 7)
	    6   CONDP (40/80)
	    7   B2 (TX ATT 0)
	    8   B3 (TX ATT 1)
	    9   ULP (RAD A5)
	    10  FLP (RAD A6)
	    11  F2P (RTF)
	    12  GND
	    13  F3P (GTF)
	    14  F4P (BTF)
	    15  B5P (RTB)
	    16  B4 (TX ATT 2)
	    17  B5 (TX ATT 3)
	    18  B6 (TX ATT 4)
	    19  B7 (TX ATT 5)
	    20  LP (*DEN+3)
	    21  B6P (GTB)
	    22  B7P (BTB)
	    23  E5P (RAD A7)
	    24  Vcc
	*/
	//ROM_LOAD( "60 90225-01.11c", 0x000, 0x400, NO_DUMP ) // "VIDEO ATTRIBUTE" 40033A (?)

	ROM_REGION( 0x104, "abc_p3", 0 )
	/*
	    1   12MHz
	    2   DOT
	    3   RTF
	    4   GTF
	    5   BTF
	    6   RTB
	    7   GTB
	    8   BTB
	    9   SFG
	    10  GND
	    11  GND
	    12  RFG
	    13  GFG
	    14  >YL
	    15  >BL
	    16  >GL
	    17  >RL
	    18  BFG
	    19  >FGE
	    20  Vcc
	*/
	ROM_LOAD( "60 90239-01.1b",  0x000, 0x104, CRC(f3d0ba00) SHA1(bcc0ee26ecac0028aef6bf5cb308133b509bb360) ) // "ABC P3-11" PAL16R4, color encoder

	ROM_REGION( 0x104, "abc_p4", 0 )
	/*
	    1   I3
	    2   A15
	    3   A14
	    4   B13
	    5   B12
	    6   B11
	    7   M1L
	    8   EME
	    9   ENL
	    10  GND
	    11  XML
	    12  >ROMD
	    13  HRAL
	    14  HRBL
	    15  KDL
	    16  >HRE
	    17  RKDL
	    18  >MUX
	    19  >RAMD
	    20  Vcc
	*/
	ROM_LOAD( "60 90240-01.2d",  0x000, 0x104, CRC(3cc5518d) SHA1(343cf951d01c9d361b695bb4e80eaadf0820b6bc) ) // "ABC P4-11" PAL16L8, memory mapper
ROM_END



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT   CLASS          INIT        COMPANY             FULLNAME        FLAGS
COMP( 1981, abc800c, 0,       0,      abc800c, abc800, abc800c_state, empty_init, "Luxor Datorer AB", "ABC 800 C/HR", MACHINE_SUPPORTS_SAVE )
COMP( 1981, abc800m, abc800c, 0,      abc800m, abc800, abc800m_state, empty_init, "Luxor Datorer AB", "ABC 800 M/HR", MACHINE_SUPPORTS_SAVE )
COMP( 1981, dtc,     abc800c, 0,      abc800m, abc800, abc800m_state, empty_init, "Facit",            "DTC",          MACHINE_SUPPORTS_SAVE )
COMP( 1983, abc802,  0,       0,      abc802,  abc802, abc802_state,  empty_init, "Luxor Datorer AB", "ABC 802",      MACHINE_SUPPORTS_SAVE )
COMP( 1983, abc806,  0,       0,      abc806,  abc806, abc806_state,  empty_init, "Luxor Datorer AB", "ABC 806",      MACHINE_SUPPORTS_SAVE )



ac1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

        AC1 video driver by Miodrag Milanovic

        15/01/2009 Preliminary driver.

        24/02/2011 Added cassette support ('ac1' and 'ac1_32' only)

        Note that Z command will get you into BASIC, and BYE command
        takes you back to the Monitor.

        S xxxx yyyy = to save memory to tape.
        L  = to load it back in.

        Since there is no motor control, type in L then mount the tape.

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

#include "emu.h"
#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "machine/z80pio.h"
#include "machine/z80ctc.h"
#include "screen.h"
#include "speaker.h"
#include "sound/spkrdev.h"
#include "emupal.h"


namespace {

class ac1_state : public driver_device
{
public:
	ac1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_cassette(*this, "cassette")
		, m_maincpu(*this, "maincpu")
		, m_vram(*this, "videoram")
		, m_gfxdecode(*this, "gfxdecode")
		, m_palette(*this, "palette")
		, m_speaker(*this, "speaker")
		, m_io_line(*this, "LINE.%u", 0U)
	{ }

	void ac1(machine_config &config);
	void ac1_32(machine_config &config);
	void ac1scch(machine_config &config);
	void init_upper();
	void init_lower();

private:
	u32 screen_update_ac1(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	u32 screen_update_ac1_32(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	u8 ac1_port_b_r();
	u8 ac1_port_a_r();
	bool has_lowercase = 0;
	void machine_start() override ATTR_COLD;
	void ac1_port_a_w(u8 data);
	void ac1_port_b_w(u8 data);

	void ac1_32_mem(address_map &map) ATTR_COLD;
	void ac1_io(address_map &map) ATTR_COLD;
	void ac1scch_io(address_map &map) ATTR_COLD;
	void ac1_mem(address_map &map) ATTR_COLD;

	required_device<cassette_image_device> m_cassette;
	required_device<cpu_device> m_maincpu;
	required_shared_ptr<u8> m_vram;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<palette_device> m_palette;
	required_device<speaker_sound_device> m_speaker;
	required_ioport_array<7> m_io_line;
};


u8 ac1_state::ac1_port_b_r()
{
	u8 data = 0x7f;

	if (m_cassette->input() > 0.03)
		data |= 0x80;

	return data;
}

// standard ascii keyboard with bit 7 high
u8 ac1_state::ac1_port_a_r()
{
	u8 line[7] = { 0,0,0,0,0,0,0 };

	line[6] = m_io_line[6]->read();
	if (BIT(line[6], 2,3))
	{
		if (BIT(line[6], 2))
			return 0xa0;  // space
		if (BIT(line[6], 3))
			return 0x8d;  // enter
		if (BIT(line[6], 4))
			return 0x88;  // backspace
	}

	bool SH = BIT(line[6],0);
	u8 i;

	line[0] = m_io_line[0]->read();
	if (line[0])
		for (i = 0; i < 8; i++)
			if (BIT(line[0], i))
				return (SH) ? 0xa0+i : 0xb0+i;

	line[1] = m_io_line[1]->read();
	if (line[1])
	{
		for (i = 0; i < 4; i++)
			if (BIT(line[1], i))
				return (SH) ? 0xa8+i : 0xb8+i;
		for (i = 4; i < 8; i++)
			if (BIT(line[1], i))
				return (SH) ? 0xb8+i : 0xa8+i;
	}

	bool CTRL = BIT(line[6],1);

	line[2] = m_io_line[2]->read();
	line[3] = m_io_line[3]->read();
	line[4] = m_io_line[4]->read();
	line[5] = m_io_line[5]->read();

	if (!has_lowercase)
	{
		SH = false;
		CTRL = false;
	}

	for (u8 j = 2; j < 6; j++)
		for (i = 0; i < 8; i++)
			if (BIT(line[j], i))
			{
				u8 k = 0x70 + i + j*8;
				return (CTRL) ? k : ((SH) ? k+0x60 : k+0x40);
			}

	return 0; // no key pressed
}

void ac1_state::ac1_port_a_w(u8 data)
{
}

void ac1_state::ac1_port_b_w(u8 data)
{
	/*

	    bit     description

	    0
	    1       RTTY receive
	    2       RTTY transmit
	    3       RTTY PTT
	    4
	    5
	    6       cassette out
	    7       cassette in

	*/
	m_speaker->level_w(BIT(data, 0));
	m_cassette->output(BIT(data, 6) ? -1.0 : +1.0);
}

/* Address maps */
void ac1_state::ac1_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).rom();  // Monitor
	map(0x0800, 0x0fff).rom();  // BASIC
	map(0x1000, 0x17ff).ram().share("videoram");
	map(0x1800, 0x1fff).ram();
}

void ac1_state::ac1_32_mem(address_map &map)
{
	ac1_mem(map);
	map(0x1800, 0xffff).ram();
}

void ac1_state::ac1_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));  // on the board, but ignored by standard bios
	map(0x04, 0x07).rw("pio", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
}

void ac1_state::ac1scch_io(address_map &map)
{
	map.global_mask(0xff);
	ac1_io(map);
	map(0x08, 0x0b).rw("pio2", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	// more...
}

/* Input ports */
static INPUT_PORTS_START( ac1 )
	PORT_START("LINE.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("LINE.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("LINE.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')

	PORT_START("LINE.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')

	PORT_START("LINE.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')

	PORT_START("LINE.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_END) PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_CHAR('_') PORT_CHAR(127)

	PORT_START("LINE.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Ctrl")  PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
INPUT_PORTS_END


const gfx_layout charlayout =
{
	6, 8,               /* 6x8 characters */
	256,                /* 256 characters */
	1,                  /* 1 bits per pixel */
	{0},                /* no bitplanes; 1 bit per pixel */
	{7, 6, 5, 4, 3, 2},
	{0 * 8, 1 * 8, 2 * 8, 3 * 8, 4 * 8, 5 * 8, 6 * 8, 7 * 8},
	8*8                 /* size of one char */
};

static GFXDECODE_START( gfx_ac1 )
	GFXDECODE_ENTRY( "gfx1", 0x0000, charlayout, 0, 1 )
GFXDECODE_END

u32 ac1_state::screen_update_ac1(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (u8 y = 0; y < 16; y++ )
		for (u8 x = 0; x < 64; x++ )
			m_gfxdecode->gfx(0)->opaque(bitmap,cliprect, m_vram[x + y*64], 0, 0, 0, 63*6-x*6, 15*8-y*8);

	return 0;
}

u32 ac1_state::screen_update_ac1_32(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (u8 y = 0; y < 32; y++ )
		for (u8 x = 0; x < 64; x++ )
			m_gfxdecode->gfx(0)->opaque(bitmap,cliprect, m_vram[x + y*64], 0, 0, 0, 63*6-x*6, 31*8-y*8);

	return 0;
}

void ac1_state::machine_start()
{
	save_item(NAME(has_lowercase));
}

void ac1_state::init_upper()
{
	has_lowercase = false;
}

void ac1_state::init_lower()
{
	has_lowercase = true;
}


void ac1_state::ac1(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(8'000'000) / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &ac1_state::ac1_mem);
	m_maincpu->set_addrmap(AS_IO, &ac1_state::ac1_io);

	z80pio_device& pio(Z80PIO(config, "pio", XTAL(8'000'000)/4));
	pio.in_pa_callback().set(FUNC(ac1_state::ac1_port_a_r));
	pio.out_pa_callback().set(FUNC(ac1_state::ac1_port_a_w));
	pio.in_pb_callback().set(FUNC(ac1_state::ac1_port_b_r));
	pio.out_pb_callback().set(FUNC(ac1_state::ac1_port_b_w));

	Z80CTC(config, "ctc", 8'000'000 / 4); // all connections go external

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(64*6, 16*8);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(ac1_state::screen_update_ac1));
	screen.set_palette(m_palette);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_ac1);

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
}

void ac1_state::ac1_32(machine_config &config)
{
	ac1(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &ac1_state::ac1_32_mem);

	screen_device &screen(*subdevice<screen_device>("screen"));
	screen.set_size(64*6, 32*8);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(ac1_state::screen_update_ac1_32));
}

void ac1_state::ac1scch(machine_config &config)
{
	ac1_32(config);
	m_maincpu->set_addrmap(AS_IO, &ac1_state::ac1scch_io);

	Z80PIO(config, "pio2", XTAL(8'000'000)/4);  // for V24 and printer
	// more...
}


/* ROM definition */
ROM_START( ac1 )
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v1", "Version 3.1 (orig)" )
	ROMX_LOAD("mon_v31_16.bin",  0x0000, 0x0800, CRC(1ba65e4d) SHA1(3382b8d03f31166a56aea49fd1ec1e82a7108300), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v2", "Version 3.1 (fixed)" )
	ROMX_LOAD("mon_v31_16_v2.bin",  0x0000, 0x0800, CRC(8904beb4) SHA1(db8d00a2537ac3a662e3c91e55eb2bf824a72062), ROM_BIOS(1))
	// from Funkamateur 01/85
	ROM_LOAD("minibasic.bin",   0x0800, 0x0800, CRC(06782639) SHA1(3fd57b3ae3f538374b0d794d8aa15d06bcaaddd8))

	ROM_REGION(0x0800, "gfx1",0)
	// 64 chars - U402 BM513
	ROM_LOAD("u402.bin", 0x0000, 0x0200, CRC(cfb67f28) SHA1(e3a62a3a8bce0d098887e31fd16410f38832fd18))
	ROM_COPY("gfx1", 0x0000, 0x0200, 0x0200)
	ROM_COPY("gfx1", 0x0000, 0x0400, 0x0200)
	ROM_COPY("gfx1", 0x0000, 0x0600, 0x0200)
ROM_END

ROM_START( ac1_32 )
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD("mon_v31_32.bin",  0x0000, 0x0800, CRC(bea78b1a) SHA1(8a3e2ac2033aa0bb016be742cfea7e4b09c0813b))
	// from Funkamateur 01/85
	ROM_LOAD("minibasic.bin",   0x0800, 0x0800, CRC(06782639) SHA1(3fd57b3ae3f538374b0d794d8aa15d06bcaaddd8))

	ROM_REGION(0x0800, "gfx1",0)
	ROM_SYSTEM_BIOS( 0, "128", "128 chars" )
	// 128 chars - U555 or 2708 from Funkamateur 06/86 128 including pseudo graphics
	ROMX_LOAD("zg_128.bin", 0x0000, 0x0400, CRC(0a6f7796) SHA1(64d77639b1ea23f45b4bd38c251851acb2d03822), ROM_BIOS(0))
	ROMX_LOAD("zg_128.bin", 0x0400, 0x0400, CRC(0a6f7796) SHA1(64d77639b1ea23f45b4bd38c251851acb2d03822), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "256", "256 chars" )
	// 256 chars - 2716 from Computerclub Dessau  including pseudo graphics
	ROMX_LOAD("zg_256.bin", 0x0000, 0x0800, CRC(b4171df5) SHA1(abdec4e00257f86b1a57e02b9c6b4d2df2a2a2db), ROM_BIOS(1))
ROM_END

ROM_START( ac1scch )
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v7", "Version 7" )
	ROMX_LOAD("mon_v7.bin",  0x0000, 0x1000, CRC(fd17b0cf) SHA1(e47113025bd9dadc1522425e21703f43e584b00f), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v8", "Version 8" )
	ROMX_LOAD("mon_v8.bin",  0x0000, 0x1000, CRC(5af68da5) SHA1(e760d4400b9c937e7e789d52b8ec975ff253a122), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "v10", "Version 10" )
	ROMX_LOAD("mon_v10.bin",  0x0000, 0x1000, CRC(f8e67ecb) SHA1(7953676fc8c22824ceff464c7177e9ac0343b8ce), ROM_BIOS(2)) // not working
	ROM_SYSTEM_BIOS( 3, "v1088", "Version 10/88" )
	ROMX_LOAD("mon_v1088.bin",  0x0000, 0x1000, CRC(bbb0a6df) SHA1(de9389e142541a8b5ff238b59e98bf571c794bef), ROM_BIOS(3))

	ROM_REGION(0x0800, "gfx1",0)
	ROM_LOAD("zg_scch.bin", 0x0000, 0x0800, CRC(fbfaf5da) SHA1(667568c5909e9a17675cf09dfbce2fc090c420ab))
ROM_END

} // anonymous namespace


// Driver
//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT      COMPANY         FULLNAME                                 FLAGS
COMP( 1984, ac1,     0,      0,      ac1,     ac1,   ac1_state, init_upper, "Frank Heyder", "Amateurcomputer AC1 Berlin",            MACHINE_SUPPORTS_SAVE )
COMP( 1984, ac1_32,  ac1,    0,      ac1_32,  ac1,   ac1_state, init_lower, "Frank Heyder", "Amateurcomputer AC1 Berlin (32 lines)", MACHINE_SUPPORTS_SAVE )
COMP( 1984, ac1scch, ac1,    0,      ac1scch, ac1,   ac1_state, init_lower, "Frank Heyder", "Amateurcomputer AC1 SCCH", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
// ac1_2010
// ac1_2017




academy.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco, hap
/*******************************************************************************

Mephisto Academy

Hardware notes:
- PCB label HGS 10 130 01
- VL65NC02-04PC @ 4.91MHz
- 2*32KB ROM(TC57256AD-12), 1st ROM half-unused (A14 = VCC)
- 8KB battery-backed RAM(TC5564APL-15)
- HD44100H, HD44780, 2*16 chars LCD screen
- 8 tri-color leds (not fully used: always outputs 6 red, 2 green)
- magnets chessboard with leds, piezo

Since the program is on an external module, it appears it was meant to be
a modular chesscomputer. However, no extra modules were sold separately.
Module PCB is the same as Super Mondial II College, label HGS 10 116 05.

The T+T version is (presumably the first version of) Polgar ported over to
Academy hardware. Much unused data remains, and the TRAIN button doesn't work.

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

#include "emu.h"

#include "mmboard.h"
#include "mmdisplay2.h"

#include "cpu/m6502/w65c02.h"
#include "machine/74259.h"
#include "machine/nvram.h"
#include "video/pwm.h"

// internal artwork
#include "mephisto_academy.lh"


namespace {

class academy_state : public driver_device
{
public:
	academy_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_led_pwm(*this, "led_pwm"),
		m_outlatch(*this, "outlatch"),
		m_keys(*this, "KEY")
	{ }

	void academy(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	required_device<mephisto_board_device> m_board;
	required_device<pwm_display_device> m_led_pwm;
	required_device<hc259_device> m_outlatch;
	required_ioport m_keys;

	void main_map(address_map &map) ATTR_COLD;

	void led_w(u8 data);
	u8 input_r();
};



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

void academy_state::led_w(u8 data)
{
	// d0-d3: keypad led select
	// d4-d7: keypad led data
	m_led_pwm->matrix(data & 0xf, ~data >> 4 & 0xf);
}

u8 academy_state::input_r()
{
	// 74259 Q1 selects keypad
	u8 data = m_outlatch->q1_r() ? 0 : m_keys->read();
	return ~m_board->input_r() | data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void academy_state::main_map(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("nvram");
	map(0x2400, 0x2400).r(FUNC(academy_state::input_r));
	map(0x2800, 0x2800).w(m_board, FUNC(mephisto_board_device::mux_w));
	map(0x2c00, 0x2c00).w(m_board, FUNC(mephisto_board_device::led_w));
	map(0x3000, 0x3007).w("outlatch", FUNC(hc259_device::write_d7));
	map(0x3400, 0x3400).w(FUNC(academy_state::led_w));
	map(0x3800, 0x3801).rw("display:hd44780", FUNC(hd44780_device::read), FUNC(hd44780_device::write));
	map(0x4000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( academy )
	PORT_START("KEY")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("TRAIN")  PORT_CODE(KEYCODE_T)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("INFO")   PORT_CODE(KEYCODE_I)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("MEM")    PORT_CODE(KEYCODE_M)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("POS")    PORT_CODE(KEYCODE_P)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("LEV")    PORT_CODE(KEYCODE_L)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("FCT")    PORT_CODE(KEYCODE_F)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("ENT")    PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CODE(KEYCODE_F1) // combine for NEW GAME
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("CL")     PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_F1) // "
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void academy_state::academy(machine_config &config)
{
	// basic machine hardware
	W65C02(config, m_maincpu, 4.9152_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &academy_state::main_map);

	const attotime nmi_period = attotime::from_hz(4.9152_MHz_XTAL / 0x2000);
	m_maincpu->set_periodic_int(FUNC(academy_state::nmi_line_pulse), nmi_period);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	HC259(config, m_outlatch); // SN74HC259N
	m_outlatch->parallel_out_cb().set("display:dac", FUNC(dac_2bit_ones_complement_device::write)).rshift(2).mask(0x03);

	MEPHISTO_SENSORS_BOARD(config, "board"); // internal
	MEPHISTO_DISPLAY_MODULE2(config, "display"); // internal

	// video hardware
	PWM_DISPLAY(config, m_led_pwm).set_size(4, 4);
	config.set_default_layout(layout_mephisto_academy);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( academy )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("academy_engl.16k", 0x0000, 0x8000, CRC(ebda6674) SHA1(2ca3ad697cb9de2873e4ef9d52c5701278456acb) )
	ROM_LOAD("academy_engl.32k", 0x8000, 0x8000, CRC(a967922b) SHA1(1327903ff89bf96d72c930c400f367ae19e3ec68) )
ROM_END

ROM_START( academyg )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("academy_16k_6.3.89", 0x0000, 0x8000, CRC(463a2106) SHA1(cc10c1ec78e20063926ed025dab08c3276499141) )
	ROM_LOAD("academy_32k_6.3.89", 0x8000, 0x8000, CRC(e313d084) SHA1(ced5712d34fcc81bedcd741b7ac9e2ba17bf5235) )
ROM_END

ROM_START( academyga )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("academy_16k_04.10.88", 0x0000, 0x8000, CRC(85c3b076) SHA1(338b165f051e9142364d344b518ff13732de404e) )
	ROM_LOAD("academy_32k_04.10.88", 0x8000, 0x8000, CRC(478155db) SHA1(d363ab6d5bc0f47a6cdfa5132b77535ef8da8256) )
ROM_END

ROM_START( academygb )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("academy_16k_27.8.88", 0x0000, 0x8000, CRC(9530def8) SHA1(376a6263477dd2c36723871acefa2939643747a5) )
	ROM_LOAD("academy_32k_27.8.88", 0x8000, 0x8000, CRC(bb9e3dc8) SHA1(98cd9bf830de1eeef339a3e1a8604d40e0ac2334) )
ROM_END

ROM_START( academyd )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("academy_hollandisch_16k_4.10.88", 0x0000, 0x8000, CRC(a8ebdff4) SHA1(a25015bc10c0c3b4dcd726b6ef2495ced188ee2e) )
	ROM_LOAD("academy_hollandisch_32k_4.10.88", 0x8000, 0x8000, CRC(ec92358e) SHA1(27bd542ac39ded5a6c7d3b8547c1a79c7221c5a7) )
ROM_END

ROM_START( academytt )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("boek", 0x4000, 0x4000, CRC(5503fbb2) SHA1(4dd584b065207d6db408cf02eae9aca825c21f7d) ) // A14 = GND?
	ROM_CONTINUE(    0x0000, 0x4000 )
	ROM_LOAD("v-11", 0x8000, 0x8000, CRC(6bc144d5) SHA1(c0c9be144ed0d1fe9ef601c57f38ff9103f2bc64) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT   COMPAT  MACHINE   INPUT    CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1989, academy,   0,       0,      academy,  academy, academy_state, empty_init, "Hegener + Glaser", "Mephisto Academy (English, 04-10-88)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, academyg,  academy, 0,      academy,  academy, academy_state, empty_init, "Hegener + Glaser", "Mephisto Academy (German, 06-03-89)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, academyga, academy, 0,      academy,  academy, academy_state, empty_init, "Hegener + Glaser", "Mephisto Academy (German, 04-10-88)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, academygb, academy, 0,      academy,  academy, academy_state, empty_init, "Hegener + Glaser", "Mephisto Academy (German, 27-08-88)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, academyd,  academy, 0,      academy,  academy, academy_state, empty_init, "Hegener + Glaser", "Mephisto Academy (Dutch, 04-10-88)", MACHINE_SUPPORTS_SAVE )

SYST( 1989, academytt, academy, 0,      academy,  academy, academy_state, empty_init, "Hegener + Glaser", "Mephisto Academy T+T (Dutch, prototype)", MACHINE_SUPPORTS_SAVE )



accomm.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont, Nigel Barnes
/***************************************************************************

    Acorn Communicator

    Driver by R. Belmont

    Main CPU:
      65C816

    Other chips:
      6850 UART
      6522 VIA
      SAA5240 (Teletext)
      MC68B54 (Econet)
      AM7910  (Modem)
      PCD3312 (Tone Generator)
      PCF0335 (Pulse Dialler?)
      PCF8573 (RTC)
      SCN2641 (RS423)

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

#include "emu.h"
#include "electron_ula.h"

#include "bus/centronics/ctronics.h"
#include "bus/econet/econet.h"
#include "bus/rs232/rs232.h"
#include "cpu/g65816/g65816.h"
#include "machine/6522via.h"
#include "machine/6850acia.h"
#include "machine/clock.h"
#include "machine/input_merger.h"
#include "machine/mc6854.h"
#include "machine/nvram.h"
#include "machine/pcf8573.h"
#include "machine/ram.h"
#include "machine/scn_pci.h"
#include "sound/pcd3311.h"
#include "video/saa5240.h"

#include "screen.h"
#include "speaker.h"

#include "accomm.lh"


namespace {

class accomm_state : public driver_device
{
public:
	accomm_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_maincpu_region(*this, "maincpu"),
		m_irqs(*this, "irqs"),
		m_screen(*this, "screen"),
		m_cct(*this, "saa5240"),
		m_dtmf(*this, "dtmf"),
		m_ram(*this, RAM_TAG),
		m_ula(*this, "ula"),
		m_rtc(*this, "rtc"),
		m_via(*this, "via6522"),
		m_acia(*this, "acia"),
		m_acia_clock(*this, "acia_clock"),
		m_scn2641(*this, "aci"),
		m_adlc(*this, "mc6854"),
		m_vram(*this, "vram", 0x8000, ENDIANNESS_LITTLE),
		m_keybd{ { *this, "LINE1.%u", 0 }, { *this, "LINE2.%u", 0 } },
		m_shiftlock_led(*this, "shiftlock_led"),
		m_capslock_led(*this, "capslock_led"),
		m_ch00rom_enabled(true),
		m_ttx_enabled(false)
	{ }

	void accomm(machine_config &config);
	void accommi(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(trigger_reset);

protected:
	// driver_device overrides
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<g65816_device> m_maincpu;
	required_memory_region m_maincpu_region;
	required_device<input_merger_device> m_irqs;
	required_device<screen_device> m_screen;
	required_device<saa5240_device> m_cct;
	required_device<pcd3311_device> m_dtmf;
	required_device<ram_device> m_ram;
	required_device<electron_ula_device> m_ula;
	required_device<pcf8573_device> m_rtc;
	required_device<via6522_device> m_via;
	required_device<acia6850_device> m_acia;
	required_device<clock_device> m_acia_clock;
	required_device<scn2641_device> m_scn2641;
	required_device<mc6854_device> m_adlc;
	memory_share_creator<uint8_t> m_vram;
	required_ioport_array<13> m_keybd[2];
	output_finder<> m_shiftlock_led;
	output_finder<> m_capslock_led;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void set_cpu_clock(offs_t offset);
	void ch00switch();
	uint8_t kbd_r(offs_t offset);
	uint8_t ram_r(offs_t offset);
	void ram_w(offs_t offset, uint8_t data);
	uint8_t via_pb_r();
	void via_pb_w(uint8_t data);

	void mem_map(address_map &map) ATTR_COLD;

	bool m_ch00rom_enabled;
	bool m_ttx_enabled;
};


uint8_t accomm_state::kbd_r(offs_t offset)
{
	uint8_t data = 0;

	for (int i = 0; i < 13; i++)
	{
		if (!BIT(offset, i))
			data |= m_keybd[BIT(offset, 13)][i]->read();
	}

	return data;
}

void accomm_state::machine_reset()
{
	m_ch00rom_enabled = true;
}

void accomm_state::machine_start()
{
	m_shiftlock_led.resolve();
	m_capslock_led.resolve();

	m_ula->set_ram(m_vram);

	m_maincpu->space(AS_PROGRAM).install_readwrite_tap(0x000000, 0xffffff, "cpu_clock_tap",
		[this](offs_t offset, uint8_t &data, uint8_t mem_mask) { if (!machine().side_effects_disabled()) set_cpu_clock(offset); },
		[this](offs_t offset, uint8_t &data, uint8_t mem_mask) { if (!machine().side_effects_disabled()) set_cpu_clock(offset); });

	save_item(NAME(m_ch00rom_enabled));
	save_item(NAME(m_ttx_enabled));
}

void accomm_state::set_cpu_clock(offs_t offset)
{
	switch (offset & 0xc80000)
	{
	case 0x400000:
		// TODO: this is not verified but the ULA RAM can only be accessed at 1MHz
		m_maincpu->set_clock_scale(0.5);
		break;

	default:
		m_maincpu->set_clock_scale(1.0);
		break;
	}
}

void accomm_state::ch00switch()
{
	if (!machine().side_effects_disabled())
		m_ch00rom_enabled = false;
}


uint32_t accomm_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if (m_ttx_enabled)
		m_cct->screen_update(screen, bitmap, cliprect);
	else
		m_ula->screen_update(screen, bitmap, cliprect);

	return 0;
}


uint8_t accomm_state::ram_r(offs_t offset)
{
	if (m_ch00rom_enabled && (offset < 0x10000))
		return m_maincpu_region->base()[offset];
	else
		return m_ram->pointer()[offset & m_ram->mask()];
}

void accomm_state::ram_w(offs_t offset, uint8_t data)
{
	m_ram->pointer()[offset & m_ram->mask()] = data;
}


uint8_t accomm_state::via_pb_r()
{
	return 0xfe | (m_rtc->sda_r() & m_cct->read_sda());
}

void accomm_state::via_pb_w(uint8_t data)
{
	data ^= 0xff;

	m_rtc->sda_w(BIT(data, 1));
	m_rtc->scl_w(BIT(data, 2));

	m_cct->write_sda(BIT(data, 1));
	m_cct->write_scl(BIT(data, 2));

	if (BIT(data, 6) != m_ttx_enabled)
	{
		m_ttx_enabled = BIT(data, 6);
		if (m_ttx_enabled)
			m_screen->set_visible_area(0, 480-1, 0, 250-1);
		else
			m_screen->set_visible_area(0, 640-1, 0, 256-1);
	}
}


void accomm_state::mem_map(address_map &map)
{
	map(0x000000, 0x1fffff).rw(FUNC(accomm_state::ram_r), FUNC(accomm_state::ram_w));               /* System RAM */
	map(0x200000, 0x3fffff).noprw();                                                                /* External expansion RAM */
	map(0x400000, 0x400001).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write));  /* MODEM */
	map(0x410000, 0x410000).mirror(0xffff).ram();                                                                  /* Econet ID */
	map(0x420000, 0x42000f).mirror(0xfff0).m(m_via, FUNC(via6522_device::map));                                    /* 6522 VIA (printer etc) */
	map(0x430000, 0x430003).mirror(0xfffc).rw(m_scn2641, FUNC(scn2641_device::read), FUNC(scn2641_device::write)); /* 2641 ACIA (RS423) */
	map(0x440000, 0x440000).mirror(0xffff).lr8(NAME([this]() { ch00switch(); return 0x44; }));                     /* CH00SWITCH */
	map(0x440000, 0x440000).mirror(0xffff).lw8(NAME([this](u8 data) { ch00switch(); }));                           /* CH00SWITCH */
	map(0x450000, 0x45ffff).rw(m_ula, FUNC(electron_ula_device::read), FUNC(electron_ula_device::write));          /* Video ULA */
	map(0x460000, 0x467fff).mirror(0x8000).ram().share("nvram");                                                   /* CMOS RAM */
	map(0x470000, 0x470003).mirror(0xfffc).rw(m_adlc, FUNC(mc6854_device::read), FUNC(mc6854_device::write));      /* 68B54 (Econet) */
	map(0x480000, 0x7fffff).noprw();                                                                /* Reserved */
	map(0x800000, 0xbfffff).noprw();                                                                /* External expansion IO   */
	map(0xc00000, 0xefffff).rom().region("ext", 0);                                                 /* External expansion ROM  */
	map(0xf00000, 0xf0ffff).rom().region("maincpu", 0x000000);                                      /* ROM bank 0 (ROM Slot 0) */
	map(0xf10000, 0xf1ffff).rom().region("maincpu", 0x010000);                                      /* ROM bank 1 (ROM Slot 0) */
	map(0xf80000, 0xf9ffff).rom().region("maincpu", 0x060000);                                      /* Empty      (ROM Slot 3) */
	map(0xfa0000, 0xfbffff).rom().region("maincpu", 0x040000);                                      /* ROM bank 4 (ROM Slot 2) */
	map(0xfc0000, 0xfcffff).rom().region("maincpu", 0x030000);                                      /* ROM bank 3 (ROM Slot 1) */
	map(0xfd0000, 0xfdffff).rom().region("maincpu", 0x020000);                                      /* ROM bank 2 (ROM Slot 1) */
	map(0xfe0000, 0xfeffff).rom().region("maincpu", 0x000000);                                      /* ROM bank 0 (ROM Slot 0) */
	map(0xff0000, 0xffffff).rom().region("maincpu", 0x010000);                                      /* ROM bank 1 (ROM Slot 0) */
}

INPUT_CHANGED_MEMBER(accomm_state::trigger_reset)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}

static INPUT_PORTS_START( accomm )
	PORT_START("LINE1.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('_') PORT_CHAR('@')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-') PORT_CHAR('=')

	PORT_START("LINE1.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')')

	PORT_START("LINE1.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)          PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)          PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)          PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)          PORT_CHAR('5') PORT_CHAR('%')

	PORT_START("LINE1.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)      PORT_CHAR(UCHAR_MAMEKEY(TILDE))     PORT_NAME("Function")

	PORT_START("LINE1.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("LINE1.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)          PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)          PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)          PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('#')

	PORT_START("LINE1.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT)       PORT_CHAR(UCHAR_MAMEKEY(LALT))      PORT_NAME("Shift Lock")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)        PORT_CHAR(9)                        PORT_NAME("Tab")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')

	PORT_START("LINE1.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)          PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)          PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("LINE1.8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)      PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)      PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD)  PORT_CHAR(UCHAR_MAMEKEY(EQUALS_PAD))
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)      PORT_CHAR(UCHAR_MAMEKEY(3_PAD))

	PORT_START("LINE1.9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)        PORT_CHAR(UCHAR_MAMEKEY(BACKSPACE)) PORT_NAME("Del CE")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_END)        PORT_CHAR(UCHAR_MAMEKEY(END))       PORT_NAME("Copy EE")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(10)                       PORT_NAME(u8"\u2193  +") // U+2193 = ↓
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME)       PORT_CHAR(UCHAR_MAMEKEY(HOME))      PORT_NAME("Home  %")

	PORT_START("LINE1.10")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD)    PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))   PORT_NAME("Keypad .")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)      PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)      PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)      PORT_CHAR(UCHAR_MAMEKEY(1_PAD))

	PORT_START("LINE1.11")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')  PORT_CHAR('}')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9)         PORT_CHAR(UCHAR_MAMEKEY(F9))        PORT_NAME("Phone")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(UCHAR_MAMEKEY(ESC))       PORT_NAME("Escape")

	PORT_START("LINE1.12")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)         PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)         PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)         PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)         PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_START("LINE2.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR('+')

	PORT_START("LINE2.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)          PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_CHAR('0') PORT_CHAR(0xa3)   // £
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)          PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)          PORT_CHAR('k') PORT_CHAR('K')

	PORT_START("LINE2.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)          PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)          PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)          PORT_CHAR('f') PORT_CHAR('F')

	PORT_START("LINE2.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)        PORT_NAME("Help")

	PORT_START("LINE2.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_NAME("Ctrl")

	PORT_START("LINE2.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)          PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)          PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)          PORT_CHAR('s') PORT_CHAR('S')

	PORT_START("LINE2.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK)   PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))

	PORT_START("LINE2.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)          PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)          PORT_CHAR('h') PORT_CHAR('H')

	PORT_START("LINE2.8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("LINE2.9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))    PORT_NAME(u8"\u2192  -") // U+2192 = →
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT)     PORT_CHAR(UCHAR_MAMEKEY(INSERT))   PORT_NAME(u8"Insert  ÷")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))       PORT_NAME(u8"\u2191  ×") // U+2191 = ↑
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))     PORT_NAME(u8"\u2190  AC") // U+2190 = ←

	PORT_START("LINE2.10")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)      PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)      PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)      PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)      PORT_CHAR(UCHAR_MAMEKEY(7_PAD))

	PORT_START("LINE2.11")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10)        PORT_CHAR(UCHAR_MAMEKEY(F10))      PORT_NAME("Comp")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11)        PORT_CHAR(UCHAR_MAMEKEY(F11))      PORT_NAME("Calc")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)

	PORT_START("LINE2.12")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)         PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)         PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)         PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)         PORT_CHAR(UCHAR_MAMEKEY(F3))

	PORT_START("STOP")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F12)        PORT_CHAR(UCHAR_MAMEKEY(F12))      PORT_NAME("Stop") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(accomm_state::trigger_reset), 0)
INPUT_PORTS_END


void accomm_state::accomm(machine_config &config)
{
	G65816(config, m_maincpu, 16_MHz_XTAL / 8);
	m_maincpu->set_addrmap(AS_PROGRAM, &accomm_state::mem_map);

	INPUT_MERGER_ANY_HIGH(config, m_irqs).output_handler().set_inputline(m_maincpu, G65816_LINE_IRQ);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(16_MHz_XTAL, 1024, 0, 640, 312, 0, 256);
	m_screen->set_screen_update(FUNC(accomm_state::screen_update));
	m_screen->set_video_attributes(VIDEO_UPDATE_SCANLINE);

	config.set_default_layout(layout_accomm);

	RAM(config, RAM_TAG).set_default_size("512K").set_extra_options("1M");

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SPEAKER(config, "mono").front_center();

	ELECTRON_ULA(config, m_ula, 16_MHz_XTAL);
	m_ula->set_cpu_tag("maincpu");
	m_ula->kbd_cb().set(FUNC(accomm_state::kbd_r));
	m_ula->caps_lock_cb().set([this](int state) { m_capslock_led = state; });
	m_ula->cas_mo_cb().set([this](int state) { m_shiftlock_led = !state; });
	m_ula->add_route(ALL_OUTPUTS, "mono", 0.5);
	m_ula->irq_cb().set_inputline(m_maincpu, 0);

	PCF8573(config, m_rtc, 32.768_kHz_XTAL);
	//m_rtc->comp_cb().set(m_via, FUNC(via6522_device::write_cb1));

	SAA5240A(config, m_cct, 6_MHz_XTAL);
	m_cct->set_ram_size(0x800);

	MOS6522(config, m_via, 16_MHz_XTAL / 16);
	m_via->writepa_handler().set("cent_data_out", FUNC(output_latch_device::write));
	m_via->ca2_handler().set("centronics", FUNC(centronics_device::write_strobe));
	m_via->readpb_handler().set(FUNC(accomm_state::via_pb_r));
	m_via->writepb_handler().set(FUNC(accomm_state::via_pb_w));
	m_via->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<0>));

	SCN2641(config, m_scn2641, 3.6864_MHz_XTAL);
	m_scn2641->txd_handler().set("rs423", FUNC(rs232_port_device::write_txd));
	m_scn2641->rts_handler().set("rs423", FUNC(rs232_port_device::write_rts));
	m_scn2641->intr_handler().set(m_irqs, FUNC(input_merger_device::in_w<1>));

	rs232_port_device &rs423(RS232_PORT(config, "rs423", default_rs232_devices, nullptr));
	rs423.rxd_handler().set(m_scn2641, FUNC(scn2641_device::rxd_w));
	rs423.dcd_handler().set(m_scn2641, FUNC(scn2641_device::dcd_w));
	rs423.cts_handler().set(m_scn2641, FUNC(scn2641_device::cts_w));

	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set("modem", FUNC(rs232_port_device::write_txd));
	m_acia->rts_handler().set("modem", FUNC(rs232_port_device::write_rts));
	m_acia->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<2>));

	rs232_port_device &modem(RS232_PORT(config, "modem", default_rs232_devices, "null_modem")); // AM7910 (internal modem)
	modem.rxd_handler().set(m_acia, FUNC(acia6850_device::write_rxd));
	modem.dcd_handler().set(m_acia, FUNC(acia6850_device::write_dcd));
	modem.cts_handler().set(m_acia, FUNC(acia6850_device::write_cts));

	CLOCK(config, m_acia_clock, 16_MHz_XTAL / 13);
	m_acia_clock->signal_handler().set(m_acia, FUNC(acia6850_device::write_txc));
	m_acia_clock->signal_handler().append(m_acia, FUNC(acia6850_device::write_rxc));

	PCD3311(config, m_dtmf, 3.57864_MHz_XTAL).add_route(ALL_OUTPUTS, "mono", 0.25); // PCD3312

	MC6854(config, m_adlc);
	m_adlc->out_txd_cb().set("econet", FUNC(econet_device::host_data_w));
	m_adlc->out_irq_cb().set_inputline(m_maincpu, G65816_LINE_NMI);

	econet_device &econet(ECONET(config, "econet", 0));
	econet.clk_wr_callback().set(m_adlc, FUNC(mc6854_device::txc_w));
	econet.clk_wr_callback().append(m_adlc, FUNC(mc6854_device::rxc_w));
	econet.data_wr_callback().set(m_adlc, FUNC(mc6854_device::set_rx));

	ECONET_SLOT(config, "econet254", "econet", econet_devices);

	centronics_device &centronics(CENTRONICS(config, "centronics", centronics_devices, "printer"));
	centronics.ack_handler().set(m_via, FUNC(via6522_device::write_ca1));
	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	centronics.set_output_latch(cent_data_out);
}


void accomm_state::accommi(machine_config &config)
{
	accomm(config);

	SAA5240B(config.replace(), m_cct, 6_MHz_XTAL);
	m_cct->set_ram_size(0x800);
}


ROM_START(accomm)
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_DEFAULT_BIOS("100")
	ROM_SYSTEM_BIOS(0, "100", "MOS v1.00 13/Nov/86") /* MOS: Version 1.00 13/Nov/86 (C)1986 */
	ROMX_LOAD("romv100-0.rom", 0x000000, 0x010000, CRC(6d22950d) SHA1(d4cbdccf8d2bc836fb81182b2ed344d7134fe5c9), ROM_BIOS(0))
	ROM_RELOAD(                0x010000, 0x010000)
	ROMX_LOAD("romv100-1.rom", 0x020000, 0x010000, CRC(adc6a073) SHA1(3e87f21fafc1d69f33c5b541a20a98e82aacbfab), ROM_BIOS(0))
	ROM_RELOAD(                0x030000, 0x010000)
	ROMX_LOAD("romv100-2.rom", 0x040000, 0x010000, CRC(3438adee) SHA1(cd9d5522d9430cb2e1936210b77d2edd280f9419), ROM_BIOS(0))
	ROM_RELOAD(                0x050000, 0x010000)
	ROMX_LOAD("romv100-3.rom", 0x060000, 0x010000, CRC(bd87a157) SHA1(b9b9ed1aab9ffef2de988b2cfeac293afa11448a), ROM_BIOS(0))
	ROM_RELOAD(                0x070000, 0x010000)

	ROM_REGION(0x300000, "ext", ROMREGION_ERASEFF)
ROM_END

ROM_START(accommp)
	ROM_REGION(0x80000, "maincpu", 0)
	/* ROM labels on both evaluation prototypes were hand written A, B, C, D */
	ROM_DEFAULT_BIOS("011-1985")
	/* Serial B01-PPC01-0000004 (owned by Acorn co-founder Chris Curry) */
	ROM_SYSTEM_BIOS(0, "011-1985", "CMOS v0.11 1985") /* CMOS version 0.11 October (C)1985 */
	ROMX_LOAD("004-a.rom", 0x000000, 0x008000, CRC(d0d4d5e3) SHA1(67710e349235ed5c71380b5a7d4b570ce355b10e), ROM_BIOS(0))
	ROM_RELOAD(            0x008000, 0x008000)
	ROM_RELOAD(            0x010000, 0x008000)
	ROM_RELOAD(            0x018000, 0x008000)
	ROMX_LOAD("004-b.rom", 0x020000, 0x010000, CRC(e2fcef94) SHA1(fd065bcdb6c48bee39db9f71b8d193ee228557f7), ROM_BIOS(0))
	ROM_RELOAD(            0x030000, 0x010000)
	ROMX_LOAD("004-c.rom", 0x040000, 0x008000, CRC(348c0018) SHA1(9681b6b9eefa9ba294fac6a41dec12ba203e5142), ROM_BIOS(0))
	ROM_RELOAD(            0x048000, 0x008000)
	ROM_RELOAD(            0x050000, 0x008000)
	ROM_RELOAD(            0x058000, 0x008000)
	ROMX_LOAD("004-d.rom", 0x060000, 0x010000, CRC(1379eb9f) SHA1(8d57bc7e279c5f17c6f0e4d1d5fa7f784aadd549), ROM_BIOS(0))
	ROM_RELOAD(            0x070000, 0x010000)
	/* Serial ending 094 */
	ROM_SYSTEM_BIOS(1, "011-1986", "CMOS v0.11 1986") /* CMOS version 0.11 October (C)1985 */
	ROMX_LOAD("094-a.rom", 0x008000, 0x008000, CRC(d0d4d5e3) SHA1(67710e349235ed5c71380b5a7d4b570ce355b10e), ROM_BIOS(1))
	ROM_RELOAD(            0x008000, 0x008000)
	ROM_RELOAD(            0x010000, 0x008000)
	ROM_RELOAD(            0x018000, 0x008000)
	ROMX_LOAD("094-b.rom", 0x020000, 0x008000, CRC(8d793909) SHA1(392028386f831dfae3353e0b7b51a608798e89c6), ROM_BIOS(1))
	ROM_RELOAD(            0x028000, 0x008000)
	ROM_RELOAD(            0x030000, 0x008000)
	ROM_RELOAD(            0x038000, 0x008000)
	ROMX_LOAD("094-c.rom", 0x040000, 0x008000, CRC(e544e849) SHA1(31cd2dcd2a50880a97b12d61ef144f7d7f112345), ROM_BIOS(1))
	ROM_RELOAD(            0x048000, 0x008000)
	ROM_RELOAD(            0x050000, 0x008000)
	ROM_RELOAD(            0x058000, 0x008000)
	ROMX_LOAD("094-d-view+castoff.rom", 0x060000, 0x010000, CRC(8027df77) SHA1(51751bfdcf68683c092b6442fb22f11cb565898c), ROM_BIOS(1))
	ROM_RELOAD(            0x070000, 0x010000)

	ROM_REGION(0x300000, "ext", ROMREGION_ERASEFF)
ROM_END

ROM_START(accommb)
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_DEFAULT_BIOS("170")
	ROM_SYSTEM_BIOS(0, "170", "MOS v1.70 04/Jun/87") /* MOS: Version 1.70 04/Jun/87 (C)1987 */
	ROMX_LOAD("0252.200-1-rom0-v1.00.rom", 0x000000, 0x010000, CRC(6d22950d) SHA1(d4cbdccf8d2bc836fb81182b2ed344d7134fe5c9), ROM_BIOS(0))
	ROM_RELOAD(                            0x010000, 0x010000)
	ROMX_LOAD("0252.201-1-rom1-v1.00.rom", 0x020000, 0x010000, CRC(adc6a073) SHA1(3e87f21fafc1d69f33c5b541a20a98e82aacbfab), ROM_BIOS(0))
	ROM_RELOAD(                            0x030000, 0x010000)
	ROMX_LOAD("0252.202-1-rom2-v1.00.rom", 0x040000, 0x010000, CRC(3438adee) SHA1(cd9d5522d9430cb2e1936210b77d2edd280f9419), ROM_BIOS(0))
	ROM_RELOAD(                            0x050000, 0x010000)
	ROMX_LOAD("0252.203-1-rom3-v1.00.rom", 0x060000, 0x010000, CRC(bd87a157) SHA1(b9b9ed1aab9ffef2de988b2cfeac293afa11448a), ROM_BIOS(0))
	ROM_RELOAD(                            0x070000, 0x010000)

	/* Expansion board: Acorn Computer 0167,000 Issue 1 Spectar II */
	/* Contains 8 slots for ASTRON Data Cards */
	ROM_REGION(0x300000, "ext", ROMREGION_ERASEFF)
	ROM_LOAD("spectar-v1.0-0267-200-03.ic1", 0x000000, 0x010000, CRC(71ad0491) SHA1(c3ace8cdd2383e97eb58d64d011444da678d537c))
ROM_END

ROM_START(accommi)
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_DEFAULT_BIOS("300")
	ROM_SYSTEM_BIOS(0, "300", "MOS v3.00 13/gen/88") /* MOS: Versione 3.00 13/gen/88 (C)1988 */
	ROMX_LOAD("rom0.rom",        0x000000, 0x020000, CRC(841bd984) SHA1(2c3bc77178e5bf0342e0410f6c398bb3ac40d0c4), ROM_BIOS(0))
	ROMX_LOAD("252216-iss1.rom", 0x020000, 0x020000, CRC(40767d31) SHA1(258f4ed92d74523aaaa4aa250db5a99428aaf960), ROM_BIOS(0))
	ROMX_LOAD("rom2.rom",        0x040000, 0x010000, CRC(e3511af8) SHA1(88a5654a5e84a31078a0a64139fe84db08196c2a), ROM_BIOS(0))
	ROM_RELOAD(                  0x050000, 0x010000)

	ROM_REGION(0x300000, "ext", ROMREGION_ERASEFF)
ROM_END

} // anonymous namespace


/*    YEAR  NAME     PARENT  COMPAT MACHINE  INPUT   CLASS         INIT        COMPANY            FULLNAME                          FLAGS */
COMP( 1986, accomm,  0,      0,     accomm,  accomm, accomm_state, empty_init, "Acorn Computers", "Acorn Communicator",             MACHINE_NOT_WORKING )
COMP( 1985, accommp, accomm, 0,     accomm,  accomm, accomm_state, empty_init, "Acorn Computers", "Acorn Communicator (prototype)", MACHINE_NOT_WORKING )
COMP( 1987, accommb, accomm, 0,     accomm,  accomm, accomm_state, empty_init, "Acorn Computers", "Acorn Briefcase Communicator",   MACHINE_NOT_WORKING )
COMP( 1988, accommi, accomm, 0,     accommi, accomm, accomm_state, empty_init, "Acorn Computers", "Acorn Communicator (Italian)",   MACHINE_NOT_WORKING )



accord.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Novag Accord (model 875)

NOTE: It triggers an NMI at power-off (or power-failure). If this isn't done,
NVRAM won't work properly.

Hardware notes (Alto):
- PCB label: 100088/100089
- Hitachi HD6301X0P @ ~8MHz (LC oscillator)
- 8*8 chessboard buttons, 16+4 leds, piezo

The I/O is almost the same as on Constellation Junior, just using other pins.
But since the MCU is different, it's awkward to put in the same driver (can't
use the standard cpu_device here).

Accord and Alto have the same MCU ROM.

TODO:
- if/when MAME supports an exit callback, hook up power-off switch to that

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

#include "emu.h"

#include "cpu/m6800/m6801.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "novag_accord.lh"


namespace {

class accord_state : public driver_device
{
public:
	accord_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.0")
	{ }

	void accord(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(power_off);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<hd6301x0_cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport m_inputs;

	emu_timer *m_standbytimer;
	u16 m_inp_mux = 0;

	// I/O handlers
	void control_w(u8 data);
	u8 input_r();
	void board_w(u8 data);
	void ledsel_w(u8 data);

	TIMER_CALLBACK_MEMBER(set_standby);
};

void accord_state::machine_start()
{
	m_standbytimer = timer_alloc(FUNC(accord_state::set_standby), this);

	// register for savestates
	save_item(NAME(m_inp_mux));
}



/*******************************************************************************
    Power
*******************************************************************************/

void accord_state::machine_reset()
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
	m_maincpu->set_input_line(M6801_STBY_LINE, CLEAR_LINE);
}

TIMER_CALLBACK_MEMBER(accord_state::set_standby)
{
	m_maincpu->set_input_line(M6801_STBY_LINE, ASSERT_LINE);
}

INPUT_CHANGED_MEMBER(accord_state::power_off)
{
	if (newval && !m_maincpu->standby())
	{
		// NMI when power goes off, followed by STBY after a short delay
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
		m_standbytimer->adjust(attotime::from_msec(10));
	}
}



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

void accord_state::control_w(u8 data)
{
	// P26: speaker out
	m_dac->write(BIT(~data, 6));

	// P27: input mux (buttons)
	m_inp_mux = (m_inp_mux & 0xff) | (~data << 1 & 0x100);
}

u8 accord_state::input_r()
{
	// P30-P37: multiplexed inputs
	u8 data = 0;

	// read buttons
	if (m_inp_mux & 0x100)
		data |= m_inputs->read();

	// read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_rank(i, true);

	return ~data;
}

void accord_state::board_w(u8 data)
{
	// P60-P67: input mux (chessboard), led data
	m_inp_mux = (m_inp_mux & 0x100) | (data ^ 0xff);
	m_display->write_mx(~data);
}

void accord_state::ledsel_w(u8 data)
{
	// P70-P72: led select
	m_display->write_my(~data & 7);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( accord )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_G) PORT_NAME("Go")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back / King")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_S) PORT_NAME("Sound / Queen")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_L) PORT_NAME("Set Level / Bishop")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game / Knight")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_U) PORT_NAME("Set Up / Rook")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_V) PORT_NAME("Verify / Pawn")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_B) PORT_NAME("Black/White")

	PORT_START("POWER") // needs to be triggered for nvram to work
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(accord_state::power_off), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void accord_state::accord(machine_config &config)
{
	// basic machine hardware
	HD6301X0(config, m_maincpu, 8'000'000); // approximation, no XTAL
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(hd6301v1_cpu_device::nvram_set_battery));
	m_maincpu->standby_cb().append([this](int state) { if (state) m_display->clear(); });
	m_maincpu->out_p2_cb().set(FUNC(accord_state::control_w));
	m_maincpu->in_p3_cb().set(FUNC(accord_state::input_r));
	m_maincpu->out_p6_cb().set(FUNC(accord_state::board_w));
	m_maincpu->out_p7_cb().set(FUNC(accord_state::ledsel_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(3, 8);
	config.set_default_layout(layout_novag_accord);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( accord )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("novag_875_31x0b87p", 0x0000, 0x1000, CRC(4d77b7db) SHA1(21b38448fcf08aed8acc1b6e3bee2164ee638c9b) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1987, accord, 0,      0,      accord,  accord, accord_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Accord", MACHINE_SUPPORTS_SAVE )



acd.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************************

2017-10-28 Skeleton

Advanced Computer Design computer. CPU is WD9000. Some details at bitsavers.

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

#include "emu.h"


namespace {

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

	void acd(machine_config &config);
private:
	//  required_device<cpu_device> m_maincpu;
};

static INPUT_PORTS_START( acd )
INPUT_PORTS_END

void acd_state::acd(machine_config &config)
{
}

ROM_START( acd )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "d9_cpu_c4_rev_2.4.2.bin", 0x000000, 0x000200, CRC(481b8d65) SHA1(b766b22282cd321c707c18137ddb4c133fd0ede4) )
	ROM_LOAD( "d9_cpu_c5.bin", 0x000000, 0x000200, CRC(481b8d65) SHA1(b766b22282cd321c707c18137ddb4c133fd0ede4) )
	ROM_LOAD( "dr.bin",       0x000000, 0x000020, CRC(a0baab49) SHA1(50b91bf00390c7923b8e03975c1c6fea3c153cd7) )
	ROM_LOAD( "btwph.bin",    0x000000, 0x000020, CRC(a48a80d1) SHA1(60490d1dbd01441ebb9a9860a98131d628d5e3f9) )
	ROM_LOAD( "btwpl.bin",    0x000000, 0x000020, CRC(0f6fb759) SHA1(7ab376865faf57841960bfcfd3e038219b5b66b6) )
	ROM_LOAD( "d7_cpu_b14_rev_1.2.bin", 0x000000, 0x000200, CRC(51b2bb18) SHA1(533ef263146c1e03e5ef30e792a669293f31a1d3) )
	ROM_LOAD( "d7_cpu_c4_rev_2.4.2.bin", 0x000000, 0x000200, CRC(b1b59b5f) SHA1(5c199836a59da78ab404de663d306d0cc10aaf83) )
	ROM_LOAD( "d7_cpu_c5.bin", 0x000000, 0x000200, CRC(7db9ecca) SHA1(535a34608e59c72d13434e3fc2892db9841a0f60) )
	ROM_LOAD( "d9_cpu_b14_rev_1.2.bin", 0x000000, 0x000200, CRC(5572ff8b) SHA1(9e8158c338b2798f7fb1d9c4fa6dd99592d2fae3) )
ROM_END

} // anonymous namespace


COMP( 198?, acd, 0, 0, acd, acd, acd_state, empty_init, "Advanced Computer Design", "unknown ACD computer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



aceex.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Joakim Larsson Edstrom
/*

 This is a driver for the Aceex DM2814 28.8kbps modem containing the following chips

 U1-U3   JRC 4558D Dual op amps
 U4      SN74LS374N
 U5      SN74LS04N
 U7      Rockwell RC288DPi integrated modem chip marked Mexico (40.320MHz TXC crystal Y1 nearby)
 U8      HD74LS153P
 U9      SN75C189N
 U10,U12 SN75C188N
 U11     HD74LS244P
 U13     SN74LS374N
 U14     Winbond W78C31B which is a 8031 derivate (45.342720MHz TXC crystal Y2 nearby)
 U15     SN74LS138N (inside the socket of U14)
 U16     Macronix MX27C512 64KB EPROM
 U17     HD74LS32P (inside the socket of U16)
 U18     LM386N
 U19     CSI CAT93C46P 1KB eeproom
 U20     Winbond W24257 32KB static RAM
 U21     SN74LS373N
 LED1-11 Front leds
 SP1     Speaker
 SW1     On/Off switch

 +------------------------------------------------------+
 |                                                      |
 |    U1    U2    U3                                PHONE
 |      U4      U5                                      |
 |LED1                                               LINE
 |LED2                                                  = R
 |LED3           U7         U8             U9      U10  D S
 |LED4    Y1                U11     SP1                 2 2
 |LED5                      U13                    U12  5 3
 |LED6                                                  = 2
 |LED7      U14           U16       U18                 |
 |LED8                                                 PWR
 |LED9-11                 U20       U21                SW1
 +------------------------------------------------------+

 The U16 EPROM contains the following string

  COPYWRITE BY TSAI CHIH-HWA

*/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"


namespace {

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

	void aceex2814(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	void aceex2814_map(address_map &map) ATTR_COLD;
};

void aceex2814_state::aceex2814_map(address_map &map)
{
	map(0x0000, 0x7fff).rom();
}

static INPUT_PORTS_START( aceex2814 )
INPUT_PORTS_END

void aceex2814_state::machine_start()
{
}

void aceex2814_state::machine_reset()
{
}

#define Y1_CLOCK 40320000
#define Y2_CLOCK 45342720
void aceex2814_state::aceex2814(machine_config &config)
{

	/* basic machine hardware */
	I80C31(config, m_maincpu, Y2_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &aceex2814_state::aceex2814_map);
}

ROM_START( aceex2814 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "dm2814u16-194.bin", 0x00000, 0x10000, CRC(36dc423d) SHA1(0f350b7c533eb5270a72587ab3e050e5fe453006) )
ROM_END

} // anonymous namespace


//    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY              FULLNAME      FLAGS
COMP( 1995, aceex2814, 0,      0,      aceex2814, aceex2814, aceex2814_state, empty_init, "Aceex Corporation", "Aceex 2814", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



acr20.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Kawai ACR-20 Digital Accompaniment Center.

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

#include "emu.h"
#include "cpu/tlcs900/tmp96c141.h"

namespace {

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

	void acr20(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<tmp96c141_device> m_maincpu;
};


void acr20_state::mem_map(address_map &map)
{
	map(0x000000, 0x01ffff).rom().region("firmware", 0);
	map(0x400000, 0x4017ff).ram();
	map(0x402000, 0x402067).ram();
	map(0x800000, 0x800000).lr8(NAME([] { return 0x80; }));
	map(0x880000, 0x880043).nopw();
	map(0x89fe00, 0x89ffff).ram();
}


static INPUT_PORTS_START(acr20)
INPUT_PORTS_END

void acr20_state::acr20(machine_config &config)
{
	TMP96C141(config, m_maincpu, 10'000'000); // clock unknown
	m_maincpu->set_addrmap(AS_PROGRAM, &acr20_state::mem_map);
}

ROM_START(acr20)
	ROM_REGION16_LE(0x20000, "firmware", 0)
	ROM_LOAD("kp148a.bin", 0x00000, 0x20000, CRC(a73ca17b) SHA1(e0baf24a33bead76af08b1653e3034f4b5b1c888)) // TMS27C210A
	// ROM also contains strings for DRP-10 Digital Recorder Playback
ROM_END

} // anonymous namespace

SYST(199?, acr20, 0, 0, acr20, acr20, acr20_state, empty_init, "Kawai Musical Instruments Manufacturing", "ACR-20 Digital Accompaniment Center", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



acrnsys.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/***************************************************************************

    Acorn System 2, 3, 4, and 5

    Acorn System 2: 6502, VDU 40x25, 4K RAM, 4K ROM (BASIC), Cassette
    http://chrisacorns.computinghistory.org.uk/Computers/System2.html

    Acorn System 3: 6502, VDU 40x25, 16K RAM, 4K ROM (BASIC), FDC (1 disc)
    http://chrisacorns.computinghistory.org.uk/Computers/System3.html

    Acorn System 3: 6809, VDU 40x25, 16K RAM, FDC (1 disc)
    http://chrisacorns.computinghistory.org.uk/8bit_Upgrades/Acorn_6809_CPU.html

    Acorn System 4: 6502, VDU 40x25, 16K RAM, FDC (2 discs)
    http://chrisacorns.computinghistory.org.uk/Computers/System4.html

    Acorn System 5: 6502A, VDU 80x25, 32K RAM, FDC
    http://chrisacorns.computinghistory.org.uk/Computers/System5.html

  TODO:
    - 4K BASIC ROM is undumped for System 2/3


  6809 Monitor Commands:-
    Modify memory:
    M  - Modify starting at specified address
    MR - Modify registers
    MG - Modify from Go address
    MV - Modify from breakpoint address
    MP - Modify from saved Program counter

    Execute programs:
    G - Go to specified address
    P - Proceed from saved Program counter past specified number of breakpoints

    Debugging aids:
    R - Display registers
    V - Insert/delete breakpoint
    T - Trace one, or more, instructions
    . - Do trace, displaying register contents at each step

    Cassette interface:
    S - Save memory to named file
    L - Load named file, with optional offset
    F - Finish loading - no name search

    Printer interface:
    C - Copy to parallel printer

    Disk interface:
    D - Disk bootstrap

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

#include "emu.h"

#include "acrnsys_kbd.h"

#include "bus/acorn/bus.h"
#include "bus/centronics/ctronics.h"
#include "cpu/m6502/m6502.h"
#include "cpu/m6809/m6809.h"
#include "machine/6522via.h"
#include "machine/input_merger.h"
#include "machine/ins8154.h"

#include "softlist_dev.h"


namespace {

class acrnsys_state : public driver_device
{
public:
	acrnsys_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_irqs(*this, "irqs")
		, m_kbd(*this, "kbd")
		, m_bus(*this, "bus")
		, m_via6522(*this, "via6522")
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(trigger_reset);

	void a6502(machine_config &config);
	void a6809(machine_config &config);
	void a6502a(machine_config &config);

	void acrnsys2(machine_config &config);
	void acrnsys3(machine_config &config);
	void acrnsys3_6809(machine_config &config);
	void acrnsys4(machine_config &config);
	void acrnsys5(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void rst_w(int state);

	void a6502_mem(address_map &map) ATTR_COLD;
	void a6809_mem(address_map &map) ATTR_COLD;
	void a6502a_mem(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<input_merger_device> m_irqs;
	required_device<acrnsys_keyboard_device> m_kbd;
	required_device<acorn_bus_device> m_bus;
	optional_device<via6522_device> m_via6522;
};


void acrnsys_state::a6502_mem(address_map &map)
{
	map.unmap_value_low();
	map(0x0000, 0xffff).rw(m_bus, FUNC(acorn_bus_device::read), FUNC(acorn_bus_device::write));
	map(0x0000, 0x03ff).ram();
	map(0x0e00, 0x0e7f).mirror(0x100).rw("ins8154", FUNC(ins8154_device::read_io), FUNC(ins8154_device::write_io));
	map(0xf000, 0xffff).rom().region("maincpu", 0);
}

void acrnsys_state::a6809_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(m_bus, FUNC(acorn_bus_device::read), FUNC(acorn_bus_device::write));
	map(0x0000, 0x03ff).ram();
	map(0x0980, 0x098f).mirror(0x70).m(m_via6522, FUNC(via6522_device::map));
	map(0xf000, 0xffff).rom().region("maincpu", 0);
}

void acrnsys_state::a6502a_mem(address_map &map)
{
	map.unmap_value_low();
	map(0x0000, 0xffff).rw(m_bus, FUNC(acorn_bus_device::read), FUNC(acorn_bus_device::write));
	map(0x0000, 0x03ff).ram();
	map(0x0e00, 0x0e0f).mirror(0x1f0).m(m_via6522, FUNC(via6522_device::map));
	map(0xe000, 0xffff).rom().region("maincpu", 0);
}


void acrnsys_state::machine_start()
{
}


void acrnsys_state::rst_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? CLEAR_LINE : ASSERT_LINE);

	if (state)
	{
		machine().schedule_soft_reset();
	}
}


/***************************************************************************
    6502 CPU Board - Part No. 200,000
***************************************************************************/

void acrnsys_state::a6502(machine_config &config)
{
	M6502(config, m_maincpu, 24_MHz_XTAL / 24); /* 1MHz 6502 CPU */
	m_maincpu->set_addrmap(AS_PROGRAM, &acrnsys_state::a6502_mem);

	INPUT_MERGER_ANY_HIGH(config, m_irqs).output_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	ins8154_device &ins8154(INS8154(config, "ins8154"));
	//ins8154.in_a().set(FUNC(acrnsys_state::ins8154_pa_r));
	//ins8154.out_a().set(FUNC(acrnsys_state::ins8154_pa_w));
	ins8154.in_b().set([this]() { return m_kbd->data_r() | (m_kbd->strobe_r() << 7); });

	ACRNSYS_KEYBOARD(config, m_kbd);
	m_kbd->rst_handler().set(FUNC(acrnsys_state::rst_w));
}


/***************************************************************************
    6502A CPU Board - Part No. 200,005
***************************************************************************/

void acrnsys_state::a6502a(machine_config &config)
{
	M6502(config, m_maincpu, 24_MHz_XTAL / 12); /* 2MHz 6502A CPU */
	m_maincpu->set_addrmap(AS_PROGRAM, &acrnsys_state::a6502a_mem);

	INPUT_MERGER_ANY_HIGH(config, m_irqs).output_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	MOS6522(config, m_via6522, 4_MHz_XTAL / 4);
	m_via6522->readpa_handler().set([this]() { return m_kbd->data_r() | (m_kbd->strobe_r() << 7); });
	//m_via6522->cb2_handler().set(FUNC(acrnsys_state::cass_w));
	m_via6522->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<0>));

	ACRNSYS_KEYBOARD(config, m_kbd);
	m_kbd->blank_handler().set("via6522", FUNC(via6522_device::write_ca1));
	m_kbd->rst_handler().set(FUNC(acrnsys_state::rst_w));
}


/***************************************************************************
    6809 CPU Board - Part No. 200,012
***************************************************************************/

void acrnsys_state::a6809(machine_config &config)
{
	MC6809(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &acrnsys_state::a6809_mem);

	INPUT_MERGER_ANY_HIGH(config, m_irqs).output_handler().set_inputline(m_maincpu, M6809_IRQ_LINE);

	MOS6522(config, m_via6522, 4_MHz_XTAL / 4);
	m_via6522->writepa_handler().set("cent_data_out", FUNC(output_latch_device::write));
	m_via6522->readpb_handler().set(m_kbd, FUNC(acrnsys_keyboard_device::data_r));
	m_via6522->ca2_handler().set("centronics", FUNC(centronics_device::write_strobe));
	//m_via6522->cb2_handler().set(FUNC(acrnsys_state::cass_w));
	m_via6522->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<0>));

	centronics_device &centronics(CENTRONICS(config, "centronics", centronics_devices, "printer"));
	centronics.ack_handler().set(m_via6522, FUNC(via6522_device::write_ca1));
	centronics.busy_handler().set(m_via6522, FUNC(via6522_device::write_pa7));
	output_latch_device &latch(OUTPUT_LATCH(config, "cent_data_out"));
	centronics.set_output_latch(latch);

	ACRNSYS_KEYBOARD(config, m_kbd);
	m_kbd->strobe_handler().set("via6522", FUNC(via6522_device::write_cb1));
	m_kbd->rst_handler().set(FUNC(acrnsys_state::rst_w));
}

/***************************************************************************
    DEFAULT CARD SETTINGS
***************************************************************************/

static DEVICE_INPUT_DEFAULTS_START(8k_ram0000)
	DEVICE_INPUT_DEFAULTS("LINKS", 0x07, 0x00)
DEVICE_INPUT_DEFAULTS_END

static DEVICE_INPUT_DEFAULTS_START(8k_ram2000)
	DEVICE_INPUT_DEFAULTS("LINKS", 0x07, 0x01)
DEVICE_INPUT_DEFAULTS_END

static DEVICE_INPUT_DEFAULTS_START(8k_ramc000)
	DEVICE_INPUT_DEFAULTS("LINKS", 0x07, 0x06)
DEVICE_INPUT_DEFAULTS_END

static DEVICE_INPUT_DEFAULTS_START(32k_ram32k)
	DEVICE_INPUT_DEFAULTS("LINKS", 0x07, 0x00)
DEVICE_INPUT_DEFAULTS_END

static DEVICE_INPUT_DEFAULTS_START(32k_ram16k)
	DEVICE_INPUT_DEFAULTS("LINKS", 0x07, 0x01)
DEVICE_INPUT_DEFAULTS_END

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

void acrnsys_state::acrnsys2(machine_config &config)
{
	/* 6502 CPU Board */
	a6502(config);

	/* Acorn Bus - 8 Slot Backplane */
	ACORN_BUS(config, m_bus, 0);
	m_bus->out_irq_callback().set(m_irqs, FUNC(input_merger_device::in_w<1>));
	m_bus->out_nmi_callback().set_inputline(m_maincpu, M6502_NMI_LINE);
	ACORN_BUS_SLOT(config, "bus1", m_bus, acorn_bus_devices, "8k").set_option_device_input_defaults("8k", DEVICE_INPUT_DEFAULTS_NAME(8k_ram2000)); // 0x2000-0x3fff
	ACORN_BUS_SLOT(config, "bus2", m_bus, acorn_bus_devices, "vdu40");
	ACORN_BUS_SLOT(config, "bus3", m_bus, acorn_bus_devices, "cass");
	ACORN_BUS_SLOT(config, "bus4", m_bus, acorn_bus_devices, nullptr);
	ACORN_BUS_SLOT(config, "bus5", m_bus, acorn_bus_devices, nullptr);
	ACORN_BUS_SLOT(config, "bus6", m_bus, acorn_bus_devices, nullptr);
	ACORN_BUS_SLOT(config, "bus7", m_bus, acorn_bus_devices, nullptr);

	/* Software lists */
	SOFTWARE_LIST(config, "flop_list").set_original("acrnsys_flop");
	SOFTWARE_LIST(config, "rom_list").set_original("acrnsys_rom");
}


void acrnsys_state::acrnsys3(machine_config &config)
{
	/* 6502 CPU Board */
	a6502(config);

	/* Acorn Bus - 8 Slot Backplane */
	ACORN_BUS(config, m_bus, 0);
	m_bus->out_irq_callback().set(m_irqs, FUNC(input_merger_device::in_w<1>));
	m_bus->out_nmi_callback().set_inputline(m_maincpu, M6502_NMI_LINE);
	ACORN_BUS_SLOT(config, "bus1", m_bus, acorn_bus_devices, "8k").set_option_device_input_defaults("8k", DEVICE_INPUT_DEFAULTS_NAME(8k_ram2000)); // 0x2000-0x3fff
	ACORN_BUS_SLOT(config, "bus2", m_bus, acorn_bus_devices, "8k").set_option_device_input_defaults("8k", DEVICE_INPUT_DEFAULTS_NAME(8k_ramc000)); // 0xc000-0xdfff
	ACORN_BUS_SLOT(config, "bus3", m_bus, acorn_bus_devices, "vdu40");
	ACORN_BUS_SLOT(config, "bus4", m_bus, acorn_bus_devices, "fdc");
	ACORN_BUS_SLOT(config, "bus5", m_bus, acorn_bus_devices, nullptr);
	ACORN_BUS_SLOT(config, "bus6", m_bus, acorn_bus_devices, nullptr);
	ACORN_BUS_SLOT(config, "bus7", m_bus, acorn_bus_devices, nullptr);

	/* Software lists */
	SOFTWARE_LIST(config, "flop_list").set_original("acrnsys_flop");
	SOFTWARE_LIST(config, "rom_list").set_original("acrnsys_rom");
}


void acrnsys_state::acrnsys3_6809(machine_config &config)
{
	/* 6809 CPU Board */
	a6809(config);

	/* Acorn Bus - 8 Slot Backplane */
	ACORN_BUS(config, m_bus, 0);
	m_bus->out_irq_callback().set(m_irqs, FUNC(input_merger_device::in_w<1>));
	m_bus->out_nmi_callback().set_inputline(m_maincpu, M6502_NMI_LINE);
	ACORN_BUS_SLOT(config, "bus1", m_bus, acorn_bus_devices, "8k").set_option_device_input_defaults("8k", DEVICE_INPUT_DEFAULTS_NAME(8k_ram0000)); // 0x0000-0x1fff
	ACORN_BUS_SLOT(config, "bus2", m_bus, acorn_bus_devices, "8k").set_option_device_input_defaults("8k", DEVICE_INPUT_DEFAULTS_NAME(8k_ram2000)); // 0x2000-0x3fff
	ACORN_BUS_SLOT(config, "bus3", m_bus, acorn_bus_devices, "8k").set_option_device_input_defaults("8k", DEVICE_INPUT_DEFAULTS_NAME(8k_ramc000)); // 0xc000-0xdfff
	ACORN_BUS_SLOT(config, "bus4", m_bus, acorn_bus_devices, "vdu40");
	ACORN_BUS_SLOT(config, "bus5", m_bus, acorn_bus_devices, "cass");
	ACORN_BUS_SLOT(config, "bus6", m_bus, acorn_bus_devices, "fdc");
	ACORN_BUS_SLOT(config, "bus7", m_bus, acorn_bus_devices, nullptr);

	/* Software lists */
	SOFTWARE_LIST(config, "flop_list").set_original("acrnsys_flop");
}


void acrnsys_state::acrnsys4(machine_config &config)
{
	/* 6502 CPU Board */
	a6502(config);

	/* Acorn Bus - 14 Slot Backplane */
	ACORN_BUS(config, m_bus, 0);
	m_bus->out_irq_callback().set(m_irqs, FUNC(input_merger_device::in_w<1>));
	m_bus->out_nmi_callback().set_inputline(m_maincpu, M6502_NMI_LINE);
	ACORN_BUS_SLOT(config, "bus1", m_bus, acorn_bus_devices, "8k").set_option_device_input_defaults("8k", DEVICE_INPUT_DEFAULTS_NAME(8k_ram2000)); // 0x2000-0x3fff
	ACORN_BUS_SLOT(config, "bus2", m_bus, acorn_bus_devices, "8k").set_option_device_input_defaults("8k", DEVICE_INPUT_DEFAULTS_NAME(8k_ramc000)); // 0xc000-0xdfff
	ACORN_BUS_SLOT(config, "bus3", m_bus, acorn_bus_devices, "vdu40");
	ACORN_BUS_SLOT(config, "bus4", m_bus, acorn_bus_devices, "fdc");
	ACORN_BUS_SLOT(config, "bus5", m_bus, acorn_bus_devices, nullptr);
	ACORN_BUS_SLOT(config, "bus6", m_bus, acorn_bus_devices, nullptr);
	ACORN_BUS_SLOT(config, "bus7", m_bus, acorn_bus_devices, nullptr);
	ACORN_BUS_SLOT(config, "bus8", m_bus, acorn_bus_devices, nullptr);
	ACORN_BUS_SLOT(config, "bus9", m_bus, acorn_bus_devices, nullptr);
	ACORN_BUS_SLOT(config, "bus10", m_bus, acorn_bus_devices, nullptr);
	ACORN_BUS_SLOT(config, "bus11", m_bus, acorn_bus_devices, nullptr);
	ACORN_BUS_SLOT(config, "bus12", m_bus, acorn_bus_devices, nullptr);
	ACORN_BUS_SLOT(config, "bus13", m_bus, acorn_bus_devices, nullptr);

	/* Software lists */
	SOFTWARE_LIST(config, "flop_list").set_original("acrnsys_flop");
	SOFTWARE_LIST(config, "rom_list").set_original("acrnsys_rom");
}


void acrnsys_state::acrnsys5(machine_config &config)
{
	/* 6502A CPU Board */
	a6502a(config);

	/* Acorn Bus - 7 Slot Backplane */
	ACORN_BUS(config, m_bus, 0);
	m_bus->out_irq_callback().set(m_irqs, FUNC(input_merger_device::in_w<1>));
	m_bus->out_nmi_callback().set_inputline(m_maincpu, M6502_NMI_LINE);
	ACORN_BUS_SLOT(config, "bus1", m_bus, acorn_bus_devices, "32k").set_option_device_input_defaults("32k", DEVICE_INPUT_DEFAULTS_NAME(32k_ram32k)); // 32K
	ACORN_BUS_SLOT(config, "bus2", m_bus, acorn_bus_devices, "32k").set_option_device_input_defaults("32k", DEVICE_INPUT_DEFAULTS_NAME(32k_ram16k)); // 16K
	ACORN_BUS_SLOT(config, "bus3", m_bus, acorn_bus_devices, "vdu80");
	ACORN_BUS_SLOT(config, "bus4", m_bus, acorn_bus_devices, "fdc");
	ACORN_BUS_SLOT(config, "bus5", m_bus, acorn_bus_devices, "econet");
	ACORN_BUS_SLOT(config, "bus6", m_bus, acorn_bus_devices, nullptr);

	/* Software lists */
	SOFTWARE_LIST(config, "flop_list").set_original("acrnsys_flop");
	SOFTWARE_LIST(config, "rom_list").set_original("acrnsys_rom");
}


/***************************************************************************
    ROM definitions
***************************************************************************/

ROM_START( acrnsys2 )
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("cos.ic7", 0x0800, 0x0800, CRC(38f59dc7) SHA1(c587da5dcc6878dcd0bc823c508472d38296003e)) // Acorn COS
ROM_END

ROM_START( acrnsys3 )
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("tosdos-s3.ic7", 0x0000, 0x1000, CRC(9b1fbec4) SHA1(4cb322dadcfba9c452797d6cc2096f0c92e8792c)) // Acorn DOS
ROM_END

ROM_START( acrnsys3_6809 )
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("acorn6809.ic4", 0x0800, 0x0800, CRC(5fa5b632) SHA1(b14a884bf82a7a8c23bc03c2e112728dd1a74896))

	ROM_REGION(0x100, "proms", 0)
	ROM_LOAD("acorn6809.ic11", 0x0000, 0x0100, CRC(7908317d) SHA1(e0f1e5bd3a8598d3b62bc432dd1f3892ed7e66d8)) // address decoder
ROM_END

#define rom_acrnsys4 rom_acrnsys3

ROM_START( acrnsys5 )
	/* 6502A CPU board can take 4K, 8K, 16K ROMs */
	ROM_REGION(0x2000, "maincpu", 0)
	/* References suggest models 5A-5E also exist, this is 5F */
	ROM_LOAD("sys5f_iss1.ic11", 0x0000, 0x2000, CRC(cd80418d) SHA1(e588298239b5360b5d1e15d5cd9f7fe2b1693e5d)) // 201,625
ROM_END

} // anonymous namespace


//    YEAR  NAME           PARENT    COMPAT  MACHINE        INPUT    CLASS          INIT        COMPANY            FULLNAME                     FLAGS
COMP( 1980, acrnsys2,      acrnsys3, 0,      acrnsys2,      0,       acrnsys_state, empty_init, "Acorn Computers", "Acorn System 2",            MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1980, acrnsys3,      0,        0,      acrnsys3,      0,       acrnsys_state, empty_init, "Acorn Computers", "Acorn System 3 (6502 CPU)", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1980, acrnsys3_6809, acrnsys3, 0,      acrnsys3_6809, 0,       acrnsys_state, empty_init, "Acorn Computers", "Acorn System 3 (6809 CPU)", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1980, acrnsys4,      acrnsys3, 0,      acrnsys4,      0,       acrnsys_state, empty_init, "Acorn Computers", "Acorn System 4",            MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1982, acrnsys5,      0,        0,      acrnsys5,      0,       acrnsys_state, empty_init, "Acorn Computers", "Acorn System 5",            MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



acrnsys1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Dirk Best, Robbbert
/******************************************************************************

 Acorn System 1 (Microcomputer Kit)

http://speleotrove.com/acorn/

-   (modify) Memory display and modification    l   (load) Reads a block of bytes from tape
X   (go) Run program starting at an address     r   (return) Resume after a breakpoint
p   (point) Inserts or removes breakpoint       (up) Increment displayed address
s   (store) Writes a block of bytes to tape     (down) Decrement displayed address

Pasting:
        0-F : as is
        (inc) : ^
        (dec) : V
        M (memory) : -
        G (Go) : X

Test Paste:
        -0100^11^22^33^44^55^66^77^88^99^-0100^
        Now press up-arrow to confirm the data has been entered.


Example usage: Turn on. Press -. Mode letter will show 'A'. Type in an address
               (example FE00). Press - (or any command key). Contents will show
               on the right. Use Up & Down keys to cycle through addresses.

To save a tape, press S then enter start address, press S, enter end address+1,
               start recording and press S. The save only takes a few seconds.

To load a tape, the display must just have dots, (reset if necessary), start
               playing tape and immediately press L. The last digit will flicker
               as the bytes load. At the end, the dots will show again. There's
               no error checking, so if it doesn't work, reset and try again.

Note that left-most digit is not wired up, and therefore will always be blank.

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

#include "emu.h"
#include "cpu/m6502/m6502.h"
#include "machine/ins8154.h"
#include "machine/74145.h"
#include "machine/timer.h"
#include "imagedev/cassette.h"
#include "speaker.h"

#include "acrnsys1.lh"


namespace {

class acrnsys1_state : public driver_device
{
public:
	acrnsys1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ttl74145(*this, "ic8_7445")
		, m_cass(*this, "cassette")
		, m_keyboard(*this, "X%u", 0U)
		, m_display(*this, "digit%u", 0U)
	{ }

	void acrnsys1(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<ttl74145_device> m_ttl74145;
	required_device<cassette_image_device> m_cass;
	required_ioport_array<8> m_keyboard;
	output_finder<9> m_display;

	uint8_t ins8154_b1_port_a_r();
	void ins8154_b1_port_a_w(uint8_t data);
	void acrnsys1_led_segment_w(uint8_t data);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_w);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);
	void mem_map(address_map &map) ATTR_COLD;

	uint8_t m_digit = 0;
	uint8_t m_cass_data[4]{};
	bool m_cassbit = 0;
	bool m_cassold = 0;
};



void acrnsys1_state::machine_start()
{
	m_display.resolve();

	save_item(NAME(m_digit));
	save_item(NAME(m_cass_data));
	save_item(NAME(m_cassbit));
	save_item(NAME(m_cassold));
}


/***************************************************************************
    KEYBOARD HANDLING
***************************************************************************/
// bit 7 is cassin
uint8_t acrnsys1_state::ins8154_b1_port_a_r()
{
	uint8_t data = 0x7f, i, key_line = m_ttl74145->read();

	for (i = 0; i < 8; i++)
	{
		if (BIT(key_line, i))
		{
			data = (m_keyboard[i]->read() & 0x38) | m_digit;
			break;
		}
	}
	data |= m_cass_data[2];
	return data;
}

// bit 6 is cassout
void acrnsys1_state::ins8154_b1_port_a_w(uint8_t data)
{
	m_digit = data & 0x47;
	m_ttl74145->write(m_digit & 7);
	m_cassbit = BIT(data, 6);
}

TIMER_DEVICE_CALLBACK_MEMBER(acrnsys1_state::kansas_w)
{
	m_cass_data[3]++;

	if (m_cassbit != m_cassold)
	{
		m_cass_data[3] = 0;
		m_cassold = m_cassbit;
	}

	if (m_cassbit)
		m_cass->output(BIT(m_cass_data[3], 0) ? -1.0 : +1.0); // 2400Hz
	else
		m_cass->output(BIT(m_cass_data[3], 1) ? -1.0 : +1.0); // 1200Hz
}

TIMER_DEVICE_CALLBACK_MEMBER(acrnsys1_state::kansas_r)
{
	/* cassette - turn 1200/2400Hz to a bit */
	m_cass_data[1]++;
	uint8_t cass_ws = (m_cass->input() > +0.03) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		m_cass_data[2] = ((m_cass_data[1] < 12) ? 128 : 0);
		m_cass_data[1] = 0;
	}
}

/***************************************************************************
    LED DISPLAY
***************************************************************************/

void acrnsys1_state::acrnsys1_led_segment_w(uint8_t data)
{
	uint16_t const key_line = m_ttl74145->read();

	for (unsigned i = 0U; 9U > i; ++i)
		if (BIT(key_line, i))
			m_display[i] = data;
}


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

void acrnsys1_state::mem_map(address_map &map)
{
	map(0x0000, 0x03ff).ram();
	map(0x0e00, 0x0e7f).mirror(0x100).rw("b1", FUNC(ins8154_device::read_io), FUNC(ins8154_device::write_io));
	map(0x0e80, 0x0eff).mirror(0x100).rw("b1", FUNC(ins8154_device::read_ram), FUNC(ins8154_device::write_ram));
	map(0xf800, 0xf9ff).mirror(0x600).rom().region("maincpu",0);
}


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

static INPUT_PORTS_START( acrnsys1 )
	PORT_START("X0")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0xc7, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X1")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0xc7, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X2")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0xc7, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X3")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0xc7, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X4")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0xc7, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X5")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0xc7, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X6")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2191") PORT_CODE(KEYCODE_UP) PORT_CHAR('^') // U+2191 = ↑
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0xc7, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X7")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2193") PORT_CODE(KEYCODE_DOWN) PORT_CHAR('V') // U+2193 = ↓
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT(0xc7, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("reset")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RST") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("switch")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Switch") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("config")
	PORT_CONFNAME( 0x03, 0x00, "Switch connected to")
	PORT_CONFSETTING( 0x00, "IRQ" )
	PORT_CONFSETTING( 0x01, "NMI" )
	PORT_CONFSETTING( 0x02, "RST" )
INPUT_PORTS_END


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

void acrnsys1_state::acrnsys1(machine_config &config)
{
	M6502(config, m_maincpu, 1.008_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &acrnsys1_state::mem_map);

	config.set_default_layout(layout_acrnsys1);

	SPEAKER(config, "mono").front_center();

	ins8154_device &b1(INS8154(config, "b1"));
	b1.in_a().set(FUNC(acrnsys1_state::ins8154_b1_port_a_r));
	b1.out_a().set(FUNC(acrnsys1_state::ins8154_b1_port_a_w));
	b1.out_b().set(FUNC(acrnsys1_state::acrnsys1_led_segment_w));

	TTL74145(config, m_ttl74145, 0);

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	TIMER(config, "kansas_w").configure_periodic(FUNC(acrnsys1_state::kansas_w), attotime::from_hz(4800));
	TIMER(config, "kansas_r").configure_periodic(FUNC(acrnsys1_state::kansas_r), attotime::from_hz(40000));
}


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

ROM_START( acrnsys1 )
	ROM_REGION(0x0200, "maincpu", 0)
	// usually a pair of 74S571 PROMs in IC5 (Blue) and IC6 (Yellow)
	ROM_LOAD("acrnsys1.bin", 0x0000, 0x0200, CRC(43dcfc16) SHA1(b987354c55beb5e2ced761970c3339b895a8c09d))
ROM_END

} // anonymous namespace


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

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY            FULLNAME          FLAGS
COMP( 1979, acrnsys1, 0,      0,      acrnsys1, acrnsys1, acrnsys1_state, empty_init, "Acorn Computers", "Acorn System 1", MACHINE_SUPPORTS_SAVE )



actions_atj2279b.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/******************************************************************************

    System is fairly barebones
    Main PCB has the SoC, RAM, ROM
    connects to other PCBs with various types of slots (USB, SD etc.)
    (ADD FULL DETAILS)

    Specifications (incomplete and unconfirmed):
    CPU: 450 MHz ATJ227X with intergrated GPU
    SDRAM: 256MB
    (source: http://wecmuseum.org/index.php?title=Retro-Bit_Generations)

    TODO: everything - emulate the SoC

    Presumably has an internal bootstrap (at least) to boot from the NAND

    --

    reviews for this device were less than favourable with many comments
    about how the SNES and Arcade games often did not run at full speed
    and how the overall quality of emulation was unremarkable

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

#include "emu.h"

#include "cpu/arm7/arm7.h"

#include "speaker.h"
#include "screen.h"


namespace {

class actions_atj2279b_state : public driver_device
{
public:
	actions_atj2279b_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "arm7")
	{ }

	void actions_atj2279b(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);


	void atj2279b_map(address_map &map) ATTR_COLD;

	required_device<arm7_cpu_device> m_maincpu;
};

void actions_atj2279b_state::atj2279b_map(address_map &map)
{
}

void actions_atj2279b_state::machine_start()
{
}

void actions_atj2279b_state::machine_reset()
{
}

static INPUT_PORTS_START( actions_atj2279b )
INPUT_PORTS_END


uint32_t actions_atj2279b_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void actions_atj2279b_state::actions_atj2279b(machine_config &config)
{
	ARM7_BE(config, m_maincpu, 450'000'000); // Probably ATJ227X 450MHz, but this needs to be checked more closely
	m_maincpu->set_addrmap(AS_PROGRAM, &actions_atj2279b_state::atj2279b_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(0));
	screen.set_size(1280, 720);
	screen.set_visarea(0, 1280-1, 0, 720-1); // resolution unconfirmed (possibly 1080p as well, but this is unlikely)
	screen.set_screen_update(FUNC(actions_atj2279b_state::screen_update));

	SPEAKER(config, "speaker", 2).front();
}

ROM_START( rbitgen )
	ROM_REGION32_BE( 0x21000000, "nand", 0 ) // NAND dump
	//TOSHIBA
	// TC58NVG2S3?A00  (there is a yellow stripe on the label before A00 obscuring the text)
	ROM_LOAD16_WORD_SWAP( "nand.bin", 0x000000, 0x21000000, CRC(92576add) SHA1(1fe61ef1d2dd24e5b5d48c477846ef0c83ec6568) )
ROM_END

} // anonymous namespace


//    year, name,         parent,  compat, machine,      input,        class,              init,       company,  fullname,                             flags
CONS( 2016, rbitgen,      0,       0,      actions_atj2279b, actions_atj2279b, actions_atj2279b_state, empty_init, "Retro-Bit", "Generations (Retro-Bit)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



acvirus.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    acvirus.cpp - Access Virus series

    Skeleton driver by R. Belmont

    Hardware in brief:
        Virus A: SAB 80C535-N    (12 MHz), DSP56303 @ 66 MHz
        Virus B: SAB 80C535-N    (12 MHz), DSP56311 @ ??? MHz (illegible on PCB photo I've seen)
        Virus C: SAF 80C515-L24N (24 MHz), DSP56362 @ 120 MHz

        Virus Rack is same h/w as B, Rack XL is the same h/w as C.
        Virus Classic is supposed to be the same h/w as B but not proven.

    The various 80C5xx chips are i8051-based SoCs with additional I/O ports,
    256 bytes of internal RAM like the 8052, and an analog/digital converter.

    The top 4 bits of port P5 select the bank at 0x8000.  P5 is not implemented in
    any of the MCS-51 variants we support yet.

    Hardware Notes:
    The DSP has three SRAM chips, probably 128 kbyte each
    for a total of 128 kwords, mapped to address 0x20000. All three DSP
    buses (P, X, Y) point to the same external memory. There's another 128
    kbyte of battery backed SRAM for the 8051.

    The firmware image fits exactly in an AM29F040-120PC flash chip, and is
    bank switched into the 8051 program address space. The lower 0x8000
    bytes of the address space always points to the first 0x8000 bytes of
    flash (except during firmware upgrade, as I assume the programming
    routine has do run from RAM). The upper 0x8000 bytes of the address
    space can point to any 0x8000 sized bank in flash. A bank switch routine
    is at 0x64B8, and will switch to e.g. bank 2 (offset 0x10000) when A =
    0x20. The low nibble is usually zero, but not always, and I don't know
    how it's interpreted.

    Banks 0-2 contain OS code and data, banks 3-6 contain DSP code and data,
    and banks 8-14 seem to contain factory default settings. There are flash
    programming routines at the beginning of banks 7 and 15, and two at the
    end of bank 6. Not sure why there are so many, and not all are
    identical, so there's probably additional bank switching logic to match.
    All display a charming "DO NOT TOUCH ME" message while programming. :)

    The same bank switching also seems to affect external memory, but I'm
    not sure how the smaller SRAM is mapped. Some external memory locations
    are used for other tasks, like communicating with the DSP.

    The initial DSP program and data upload routine is at 0x1FAA. After
    setting up the bus, it churns out all the 24-bit words in banks 3-6
    (except for headers) as one stream. The DSP will interpret the first
    word as a length, the second as address, and the following "length"
    words will be stored at that address in program memory before execution
    starts there. This is just a very short bootstrap program, which takes
    care of receiving the remaining words in chunks. Each chunks starts with
    three words - a command, an address, and optionally length. Commands 0-2
    store data in P, X, or Y memory respectively. Command 3 splits each
    24-bit word into two 12-bit values and store each of them as a 24-bit
    word in Y memory. Command 4 starts execution at the specified address,
    and doesn't have a length.

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

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/intelfsh.h"
#include "speaker.h"


namespace {

class acvirus_state : public driver_device
{
public:
	acvirus_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_rombank(*this, "rombank")
	{ }

	void virus(machine_config &config);

	void init_virus();

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void virus_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_memory_bank m_rombank;
};

void acvirus_state::machine_start()
{
	m_rombank->configure_entries(0, 15, memregion("maincpu")->base(), 0x8000);
	m_rombank->set_entry(3);
}

void acvirus_state::machine_reset()
{
}

void acvirus_state::virus_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("maincpu", 0); // fixed 32K of flash image
	map(0x8000, 0xffff).bankr("rombank");
}

void acvirus_state::virus(machine_config &config)
{
	SAB80C535(config, m_maincpu, XTAL(12'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &acvirus_state::virus_map);

	SPEAKER(config, "speaker", 2).front();
}

static INPUT_PORTS_START( virus )
INPUT_PORTS_END

ROM_START( virusa )
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_LOAD( "virus_a_28.bin", 0x000000, 0x080000, CRC(087cd808) SHA1(fe3310a165c208473822455c75ee5b2a6de34bc8) )
ROM_END

ROM_START( virusb )
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_LOAD( "virus_bt_490x049.bin", 0x000000, 0x080000, CRC(4ffc928a) SHA1(ee4b83e2eb1f01c73e37e2ff1d2edd653a0dcf5b) )
ROM_END

ROM_START( virusc )
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_LOAD( "virus_c_650x352.bin", 0x000000, 0x080000, CRC(d44a9468) SHA1(fad9b896b39a43a1d46acb1d780b78b775a609b8) )
ROM_END

ROM_START( virusrck )
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_LOAD( "virus_rt_210x071.bin", 0x000000, 0x080000, CRC(62b2bcc1) SHA1(241467bcb563736472a6e61f6c9c532590664500) )
ROM_END

ROM_START( virusrckxl )
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_LOAD( "virus_xl_650x079.bin", 0x000000, 0x080000, CRC(d0721c46) SHA1(b7c292b66ba3690a4a50592e17321b9c4147621d) )
ROM_END

ROM_START( viruscl )
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_LOAD( "virus_cl_061_release.bin", 0x000000, 0x080000, CRC(a202e443) SHA1(33d5f4ebbacc817ab1e5dd572e8dc755f6c5e253) )
ROM_END

} // anonymous namespace


CONS( 1997, virusa,     0, 0, virus, virus, acvirus_state, empty_init, "Access", "Virus A", MACHINE_NOT_WORKING|MACHINE_NO_SOUND )
CONS( 1999, virusb,     0, 0, virus, virus, acvirus_state, empty_init, "Access", "Virus B (Ver. T)", MACHINE_NOT_WORKING|MACHINE_NO_SOUND )
CONS( 2002, virusc,     0, 0, virus, virus, acvirus_state, empty_init, "Access", "Virus C", MACHINE_NOT_WORKING|MACHINE_NO_SOUND )
CONS( 2001, virusrck,   0, 0, virus, virus, acvirus_state, empty_init, "Access", "Virus Rack (Ver. T)", MACHINE_NOT_WORKING|MACHINE_NO_SOUND )
CONS( 2002, virusrckxl, 0, 0, virus, virus, acvirus_state, empty_init, "Access", "Virus Rack XL", MACHINE_NOT_WORKING|MACHINE_NO_SOUND )
CONS( 2004, viruscl,    0, 0, virus, virus, acvirus_state, empty_init, "Access", "Virus Classic", MACHINE_NOT_WORKING|MACHINE_NO_SOUND )



adacp150.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Preliminary driver for Adacom IBM 3287 ASCII printer adapters.

    Currently this does not much more than pass the extensive self-test.

    TODO: make this a bus device in the eventuality of synchronous
    communications and a 3270-compatible host both being emulated.

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

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/bcp/dp8344.h"
#include "cpu/z80/z80.h"
#include "machine/nvram.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"


namespace {

class adacp150_state : public driver_device
{
public:
	adacp150_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_bcp(*this, "bcp")
		, m_lcdc(*this, "lcdc")
		, m_leds(*this, "led%u", 0U)
		, m_bcp_cmd(false)
	{
	}

	void adacp150(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	HD44780_PIXEL_UPDATE(pixel_update);
	void palette_init(palette_device &palette);

	u8 bcp_ram_r(offs_t offset);
	void bcp_ram_w(offs_t offset, u8 data);
	void output_control_w(u8 data);

	void z80_mem_map(address_map &map) ATTR_COLD;
	void z80_io_map(address_map &map) ATTR_COLD;
	void bcp_prog_map(address_map &map) ATTR_COLD;
	void bcp_data_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_device<dp8344_device> m_bcp;
	required_device<hd44780_device> m_lcdc;
	output_finder<4> m_leds;

	bool m_bcp_cmd;
};

void adacp150_state::machine_start()
{
	m_leds.resolve();

	m_lcdc->rw_w(0);

	save_item(NAME(m_bcp_cmd));
}

HD44780_PIXEL_UPDATE(adacp150_state::pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 16)
		bitmap.pix(line * 8 + y, pos * 6 + x) = state;
}


u8 adacp150_state::bcp_ram_r(offs_t offset)
{
	if (m_bcp_cmd)
		return m_bcp->cmd_r();
	else
		return m_bcp->remote_read(offset | 0x4000);
}

void adacp150_state::bcp_ram_w(offs_t offset, u8 data)
{
	if (m_bcp_cmd)
		m_bcp->cmd_w(data);
	else
		m_bcp->remote_write(offset | 0x4000, data);
}

void adacp150_state::output_control_w(u8 data)
{
	for (int n = 0; n < 4; n++)
		m_leds[n] = BIT(data, n);

	m_bcp_cmd = BIT(data, 4);
	m_lcdc->rs_w(BIT(data, 6));
	m_lcdc->e_w(BIT(data, 5));
}


void adacp150_state::z80_mem_map(address_map &map)
{
	map(0x0000, 0xbfff).rom().region("program", 0);
	map(0xc000, 0xffff).rw(FUNC(adacp150_state::bcp_ram_r), FUNC(adacp150_state::bcp_ram_w));
}

void adacp150_state::z80_io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).rw("pio", FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x04, 0x07).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x08, 0x0b).rw("sio", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x80, 0x80).portr("IN0");
	map(0xa0, 0xa0).portr("IN1");
	map(0xc0, 0xc0).w(FUNC(adacp150_state::output_control_w));
	map(0xe0, 0xe0).w(m_lcdc, FUNC(hd44780_device::db_w));
}

void adacp150_state::bcp_prog_map(address_map &map)
{
	map(0x0000, 0x07ff).ram(); // 2x CXK5814P-35L
}

void adacp150_state::bcp_data_map(address_map &map)
{
	map(0x0000, 0x7fff).ram().share("nvram");
}


static INPUT_PORTS_START(adacp150)
	PORT_START("IN0") // not verified
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Save")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Select")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Roll")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Set-Up")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Reset")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Cancel")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Reprint")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PA 1")

	PORT_START("IN1") // not verified
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PA 2")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("LF")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("FF")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Hold")
	PORT_BIT(0xf0, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

void adacp150_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(131, 136, 139));
	palette.set_pen_color(1, rgb_t( 92,  83,  88));
}

static const z80_daisy_config daisy_chain[] =
{
	{ "ctc" },
	{ "sio" },
	{ "pio" },
	{ nullptr }
};

void adacp150_state::adacp150(machine_config &config)
{
	auto MAIN_CLOCK = 18.8696_MHz_XTAL / 6;

	Z80(config, m_maincpu, MAIN_CLOCK); // Zilog Z84C0006PEC
	m_maincpu->set_addrmap(AS_PROGRAM, &adacp150_state::z80_mem_map);
	m_maincpu->set_addrmap(AS_IO, &adacp150_state::z80_io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	DP8344A(config, m_bcp, 18.8696_MHz_XTAL); // DP8344AV
	m_bcp->set_addrmap(AS_PROGRAM, &adacp150_state::bcp_prog_map);
	m_bcp->set_addrmap(AS_DATA, &adacp150_state::bcp_data_map);
	m_bcp->set_auto_start(false);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // CXK58257P-10L + battery

	z80ctc_device &ctc(Z80CTC(config, "ctc", MAIN_CLOCK)); // TODO: part of Zilog Z84C9008VSC Z80 KIO
	ctc.set_clk<0>(2.4576_MHz_XTAL / 2);
	ctc.set_clk<1>(2.4576_MHz_XTAL / 2);
	ctc.zc_callback<0>().set("sio", FUNC(z80sio_device::rxca_w));
	ctc.zc_callback<0>().append("sio", FUNC(z80sio_device::txca_w));
	ctc.zc_callback<1>().set("sio", FUNC(z80sio_device::rxcb_w));
	ctc.zc_callback<1>().append("sio", FUNC(z80sio_device::txcb_w));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80pio_device &pio(Z80PIO(config, "pio", MAIN_CLOCK)); // TODO: part of Zilog Z84C9008VSC Z80 KIO
	pio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80sio_device &sio(Z80SIO(config, "sio", MAIN_CLOCK)); // TODO: part of Zilog Z84C9008VSC Z80 KIO
	sio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	sio.out_txda_callback().set("printer", FUNC(rs232_port_device::write_txd));
	sio.out_txdb_callback().set("host", FUNC(rs232_port_device::write_txd));

	rs232_port_device &printer(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	printer.rxd_handler().set("sio", FUNC(z80sio_device::rxa_w));

	rs232_port_device &host(RS232_PORT(config, "host", default_rs232_devices, nullptr));
	host.rxd_handler().set("sio", FUNC(z80sio_device::rxb_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_screen_update(m_lcdc, FUNC(hd44780_device::screen_update));
	screen.set_size(16*6, 16);
	screen.set_visarea(0, 16*6-1, 0, 16-1);
	screen.set_palette("palette");

	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 20);
	m_lcdc->set_pixel_update_cb(FUNC(adacp150_state::pixel_update));

	PALETTE(config, "palette", FUNC(adacp150_state::palette_init), 2);
}

ROM_START(adacp150p)
	ROM_REGION(0xc000, "program", 0)
	ROM_LOAD("pa_2436__rev-4.52.u11", 0x0000, 0x8000, CRC(a381674c) SHA1(1d3cb4ca3ead40da67a353efe7553ea953fa929d)) // Intel D27C256
	ROM_LOAD("pa_2437__rev-4.52.u22", 0x8000, 0x4000, CRC(eb468ad0) SHA1(881a90a6aa89d7e289d7adbec46d007a8cfa5351)) // Intel D27C128
ROM_END

} // anonymous namespace


SYST(1989, adacp150p, 0, 0, adacp150, adacp150, adacp150_state, empty_init, "Adacom", "CP-150 Plus", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)



adam.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/***************************************************************************

    Coleco Adam

    The Coleco ADAM is a Z80-based micro with all peripheral devices
    attached to an internal serial serial bus (ADAMnet) managed by 6801
    microcontrollers (processor, internal RAM, internal ROM, serial port).Each
    device had its own 6801, and the ADAMnet was managed by a "master" 6801 on
    the ADAM motherboard.  Each device was allotted a block of 21 bytes in Z80
    address space; device control was accomplished by poking function codes into
    the first byte of each device control block (hereafter DCB) after setup of
    other DCB locations with such things as:  buffer address in Z80 space, # of
    bytes to transfer, block # to access if it was a block device like a tape
    or disk drive, etc.  The master 6801 would interpret this data, and pass
    along internal ADAMnet requests to the desired peripheral, which would then
    send/receive its data and return the status of the operation.  The status
    codes were left in the same byte of the DCB as the function request, and
    certain bits of the status byte would reflect done/working on it/error/
    not present, and error codes were left in another DCB byte for things like
    CRC error, write protected disk, missing block, etc.

    ADAM's ROM operating system, EOS (Elementary OS), was constructed
    similar to CP/M in that it provided both a filesystem (like BDOS) and raw
    device interface (BIOS).  At the file level, sequential files could be
    created, opened, read, written, appended, closed, and deleted.  Forward-
    only random access was implemented (you could not move the R/W pointer
    backward, except clear to the beginning!), and all files had to be
    contiguous on disk/tape.  Directories could be initialized or searched
    for a matching filename (no wildcards allowed).  At the device level,
    individual devices could be read/written by block (for disks/tapes) or
    character-by-character (for printer, keyboard, and a prototype serial
    board which was never released).  Devices could be checked for their
    ADAMnet status, and reset if necessary.  There was no function provided
    to do low-level formatting of disks/tapes.

     At system startup, the EOS was loaded from ROM into the highest
    8K of RAM, a function call made to initialize the ADAMnet, and then
    any disks or tapes were checked for a boot medium; if found, block 0 of
    the medium was loaded in, and a jump made to the start of the boot code.
    The boot block would take over loading in the rest of the program.  If no
    boot media were found, a jump would be made to a ROM word processor (called
    SmartWriter).

     Coleco designed the ADAMnet to have up to 15 devices attached.
    Before they went bankrupt, Coleco had released a 64K memory expander and
    a 300-baud internal modem, but surprisingly neither of these was an
    ADAMnet device.  Disassembly of the RAMdisk drivers in ADAM CP/M 2.2, and
    of the ADAMlink terminal program revealed that these were simple port I/O
    devices, banks of XRAM being accessed by a special memory switch port not
    documented as part of the EOS.  The modem did not even use the interrupt
    capabilities of the Z80--it was simply polled.  A combination serial/
    parallel interface, each port of which *was* an ADAMnet device, reached the
    prototype stage, as did a 5MB hard disk, but neither was ever released to
    the public.  (One prototype serial/parallel board is still in existence,
    but the microcontroller ROMs have not yet been succcessfully read.)  So
    when Coleco finally bailed out of the computer business, a maximum ADAM
    system consisted of a daisy wheel printer, a keyboard, 2 tape drives, and
    2 disk drives (all ADAMnet devices), a 64K expander and a 300-baud modem
    (which were not ADAMnet devices).

     Third-party vendors reverse-engineered the modem (which had a
    2651 UART at its heart) and made a popular serial interface board.  It was
    not an ADAMnet device, however, because nobody knew how to make a new ADAMnet
    device (no design specs were ever released), and the 6801 microcontrollers
    had unreadable mask ROMs.  Disk drives, however, were easily upgraded from
    160K to as high as 1MB because, for some unknown reason, the disk controller
    boards used a separate microprocessor and *socketed* EPROM (which was
    promptly disassembled and reworked).  Hard drives were cobbled together from
    a Kaypro-designed board and accessed as standard I/O port devices.  A parallel
    interface card was similarly set up at its own I/O port.

      Devices (15 max):
        Device 0 = Master 6801 ADAMnet controller (uses the adam_pcb as DCB)
        Device 1 = Keyboard
        Device 2 = ADAM printer
        Device 3 = Copywriter (projected)
        Device 4 = Disk drive 1
        Device 5 = Disk drive 2
        Device 6 = Disk drive 3 (third party)
        Device 7 = Disk drive 4 (third party)
        Device 8 = Tape drive 1
        Device 9 = Tape drive 3 (projected)
        Device 10 = Unused
        Device 11 = Non-ADAMlink modem
        Device 12 = Hi-resolution monitor
        Device 13 = ADAM parallel interface (never released)
        Device 14 = ADAM serial interface (never released)
        Device 15 = Gateway
        Device 24 = Tape drive 2 (share DCB with Tape1)
        Device 25 = Tape drive 4 (projected, may have share DCB with Tape3)
        Device 26 = Expansion RAM disk drive (third party ID, not used by Coleco)

      Terminology:
        EOS = Elementary Operating System
        DCB = Device Control Block Table (21bytes each DCB, DCB+16=dev#, DCB+0=Status Byte) (0xFD7C)

               0     Status byte
             1-2     Buffer start address (lobyte, hibyte)
             3-4     Buffer length (lobyte, hibyte)
             5-8     Block number accessed (loword, hiword in lobyte, hibyte format)
               9     High nibble of device number
            10-15    Always zero (unknown purpose)
              16     Low nibble of device number
            17-18    Maximum block length (lobyte, hibyte)
              19     Device type (0 for block device, 1 for character device)
              20     Node type

            - Writing to byte0 requests the following operations:
                1     Return current status
                2     Soft reset
                3     Write
                4     Read


        FCB = File Control Block Table (32bytes, 2 max each application) (0xFCB0)
        OCB = Overlay Control Block Table
        adam_pcb = Processor Control Block Table, 4bytes (adam_pcb+3 = Number of valid DCBs) (0xFEC0 relocatable), current adam_pcb=[0xFD70]
                adam_pcb+0 = Status, 0=Request Status of Z80 -> must return 0x81..0x82 to sync Master 6801 clk with Z80 clk
                adam_pcb+1,adam_pcb+2 = address of adam_pcb start
                adam_pcb+3 = device #

                - Writing to byte0:
                    1   Synchronize the Z80 clock (should return 0x81)
                    2   Synchronize the Master 6801 clock (should return 0x82)
                    3   Relocate adam_pcb

                - Status values:
                    0x80 -> Success
                    0x81 -> Z80 clock in sync
                    0x82 -> Master 6801 clock in sync
                    0x83 -> adam_pcb relocated
                    0x9B -> Time Out

        DEV_ID = Device id


        The ColecoAdam I/O map is contolled by the MIOC (Memory Input Output Controller):

                20-3F (W) = Adamnet Writes
                20-3F (R) = Adamnet Reads

                42-42 (W) = Expansion RAM page selection, only useful if expansion greater than 64k

                40-40 (W) = Printer Data Out
                40-40 (R) = Printer (Returns 0x41)

                5E-5E (RW)= Modem Data I/O
                5F-5F (RW)= Modem Data Control Status

                60-7F (W) = Set Memory configuration
                60-7F (R) = Read Memory configuration

                80-9F (W) = Set both controllers to keypad mode
                80-9F (R) = Not Connected

                A0-BF (W) = Video Chip (TMS9928A), A0=0 -> Write Register 0 , A0=1 -> Write Register 1
                A0-BF (R) = Video Chip (TMS9928A), A0=0 -> Read Register 0 , A0=1 -> Read Register 1

                C0-DF (W) = Set both controllers to joystick mode
                C0-DF (R) = Not Connected

                E0-FF (W) = Sound Chip (SN76489A)
                E0-FF (R) = Read Controller data, A1=0 -> read controller 1, A1=1 -> read controller 2
*/

/*
                       Detailed Coleco ADAM Computer I/O Address Map

Port #    Device                        Input                    Output
__________________________________________________________________________________________

00        Powermate SASI Hard Drive     Input Data               Output Data
01        Powermate SASI Hard Drive     Status Register          Command Register
01        MIB2 RESET line               * Not Used on MIB2 *     Bit 3 = 1 for MIB2 RESET
01        Powermate IDE Hard Drive      Error Register           * Not Used on IDE HD *
02        Powermate IDE Hard Drive      Sector Count Register    Sector Count Register
03        Powermate IDE Hard Drive      Sector Number Register   Sector Number Register
04        Powermate IDE Hard Drive      Cylinder Low Register    Cylinder Low Register
05        Powermate IDE Hard Drive      Cylinder High Register   Cylinder High Register
06        Powermate IDE Hard Drive      SDH Register             SDH Register
07        Powermate IDE Hard Drive      Status Register          Command Register
08        Bonafide Sys MIDI Interface
09        Bonafide Sys MIDI Interface
0A        Bonafide Sys MIDI Interface
0B        Bonafide Sys MIDI Interface
0C        Bonafide Sys MIDI Interface
0D        Bonafide Sys MIDI Interface
0E        Bonafide Sys MIDI Interface
0F        Bonafide Sys MIDI Interface
10        Powermate Serial ports        Mode Register A          Mode Register A
11        Powermate Serial ports        Status Register A        Clock Select Reg A
12        Powermate Serial ports        * DO NOT USE *           Command Register A
13        Powermate Serial ports        RX Holding Register A    TX Holding Reg A
14        Powermate Serial ports        Input Port Change Reg    Aux Control Register
15        Powermate Serial ports        Interrupt Status Reg     Interrupt Mask Reg
16        Powermate Serial ports        Read Counter Upper       Set C/T Upper Register
17        Powermate Serial ports        Read Counter Lower       Set C/T Lower Register
18        Powermate Serial ports        Mode Register B          Mode Register B
19        Powermate Serial ports        Status Register B        Clock Select Reg B
1A        Powermate Serial ports        * DO NOT USE *           Command Register B
1B        Powermate Serial ports        RX Holding Register B    TX Holding Register B
1C        Powermate Serial ports        * Reserved (note 5) *    MIB3 Serial Port RESET
1D        Powermate Serial ports        Read Input Port Bits     Output Port Config Reg
1E        Coleco AutoDialer             ??                       ??
1E        Powermate Serial ports        Start Counter Cmd Port   Set Output Port Bits
1F        Powermate Serial ports        Stop Counter Cmd Port    Reset Output Port Bits
20-3F     AdamNet Reset                 Input MAY be available   Output is NOT available
40        Parallel Printer interface    Printer status           Output Data
41        May be unused (see note 1)    Input may NOT be avail   Output MAY be available
42        Expansion Memory              * Not Used *             Bank Number
43        May be unused (see note 1)    Input may NOT be avail   Output MAY be available
44-47     Eve/Orphanware Serial Port
48-4B     Eve Speech Synth/Clock Card
4C-4F     Orphanware Serial Port 2      (Standard Eve 80 column terminal ports)
4F        Coleco Steering controller    (Listed in Hackers guide as Expansion conn #2)
50-53     Super Game Module
54-57     Orphanware Serial Port 3      (Standard Orphanware 80 column terminal ports)
58        Powermate IDE Hard Disk       Input Data Lower 8 bits  Output Data Lower 8 bits
59        Powermate IDE Hard Disk       Input Data Upper 8 bits  Output Data Upper 8 bits
5A        Powermate IDE Hard Disk       Alternate Status Reg     Fixed Disk Control Reg
5B        Powermate IDE Hard Disk       Digital Input Register   ** Not Used by IDE HD **
5C-5F     Orphanware Serial Port 4
5E        Adamlink Modem                Input Data               Output Data
5F        Adamlink Modem                Status                   Control
60-7F     Memory Bank Switch Port       Input MAY be available   Output is NOT available
80-8F     *** Unused ***                (see note 2)             STA (?)
90-9F     Orphanware Hard Drive                                  STA (?)
A0-BF     Video Display Processor
C0        Strobe Reset                                           STB (?)
C1-DF     *** Unused ***                (see note 2)             STB (?)
EO-FF     Sound Chip (Out only)
FC        Joystick #1 (In only)
FE        Joystick #2 (In only)


Notes:

1)   Port 41 or port 43 is used by the Eve 80 column unit as a keyboard input port.
2)   Not useable from expansion card slots (can't read or write data to or from ports) -
     may be available on side port.
3)   Powermate IDE hard disk drive will not interfere with Powermate serial ports.
4)   Powermate serial ports will probably interfere with autodialer.
5)   Reserved ports in Powermate serial port map:  Input ports 12 and 1A - screw up serial
     ports if used; Input port 1C doesn't bother anything but the 2681 drives the bus;
6)   Orphanware serial port number 4 probably interferes with the ADAMlink modem.
     7)   According to my analysis of circuit U6 in the ADAM computer, all of upper I/O address
     space is decoded (by an LS138).  However, not all outputs appear to be used.  The
     circuit description follows.  Please correct any misassumptions I've made.  Note that
     if my analysis is correct, then the Orphanware hard disk should be interfering with
     the signal STA\ (which is associated with the joysticks in some way).



                      U6
                    74LS138             A6   A5   WR\
               |--------------|
   WR\    -----|A           Y0|o----    0    0    0    80-9F Write    (STA\)
               |              |
    A5    -----|B           Y1|o----    0    0    1    80-9F Read     (Not Used)
               |              |
    A6    -----|C           Y2|o----    0    1    0    A0-BF Write    (VDP CSW\)
               |              |
    A7    -----|G1          Y3|o----    0    1    1    A0-BF Read     (VDP CSR\)
               |              |
IORQ\    ----o|G2A         Y4|o----    1    0    0    C0-DF Write    (STB\)
               |              |
WAIT\    ----o|G2B         Y5|o----    1    0    1    C0-DF Read     (Not Used)
               |              |
               |            Y6|o----    1    1    0    E0-FF Write    (Sound CE\)
               |              |
               |            Y7|o----    1    1    1    E0-FF Read     (Joystick Enables)
               |--------------|

Conventions:

1)   The "o" symbol next to an input or an output implies that the pin requires an active
     low signal.
2)   The "\" symbol following a signal mnemonic indicates that the signal is active low.


Rev. 3
8/30/92
Mark Gordon
*/

/*

    TODO:

    - spinner INT
    - printer
    - SPI

    http://drushel.cwru.edu/atm/atm.html
    http://rich.dirocco.org/Coleco/adam/ADAM.htm
    http://users.stargate.net/~drushel/pub/coleco/twwmca/index.html

*/

#include "emu.h"
#include "adam.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

enum
{
	LO_SMARTWRITER = 0,
	LO_INTERNAL_RAM,
	LO_RAM_EXPANSION,
	LO_OS7_ROM_INTERNAL_RAM
};


enum
{
	HI_INTERNAL_RAM = 0,
	HI_ROM_EXPANSION,
	HI_RAM_EXPANSION,
	HI_CARTRIDGE_ROM
};


enum
{
	ADAMNET_MASTER = 0,
	ADAMNET_KEYBOARD,
	ADAMNET_DDP,
	ADAMNET_PRINTER,
	ADAMNET_FDC
};



//**************************************************************************
//  MEMORY BANKING
//**************************************************************************

//-------------------------------------------------
//  mreq_r - memory request read
//-------------------------------------------------

uint8_t adam_state::mreq_r(offs_t offset)
{
	int bmreq = 0, biorq = 1, eos_enable = 1, boot_rom_cs = 1, aux_decode_1 = 1, aux_rom_cs = 1, cas1 = 1, cas2 = 1, cs1 = 1, cs2 = 1, cs3 = 1, cs4 = 1;

	uint8_t data = 0;

	if (offset < 0x8000)
	{
		switch (m_mioc & 0x03)
		{
		case LO_SMARTWRITER:
			boot_rom_cs = 0;
			eos_enable = BIT(m_an, 1);
			break;

		case LO_INTERNAL_RAM:
			cas1 = 0;
			break;

		case LO_RAM_EXPANSION:
			cas2 = 0;
			break;

		case LO_OS7_ROM_INTERNAL_RAM:
			if (offset < 0x2000)
			{
				aux_decode_1 = 0;
			}
			else
			{
				cas1 = 0;
			}
			break;
		}
	}
	else
	{
		switch ((m_mioc >> 2) & 0x03)
		{
		case HI_INTERNAL_RAM:
			cas1 = 0;
			break;

		case HI_ROM_EXPANSION:
			aux_rom_cs = 0;
			break;

		case HI_RAM_EXPANSION:
			if (m_game)
			{
				aux_decode_1 = 0;
			}
			else
			{
				cas2 = 0;
			}
			break;

		case HI_CARTRIDGE_ROM:
			aux_decode_1 = 0;
			break;
		}
	}

	if (!cas1)
	{
		data = m_ram->pointer()[offset];
	}

	if (!boot_rom_cs)
	{
		if (offset < 0x6000)
		{
			data = m_boot_rom->base()[offset];
		}
		else
		{
			data = m_boot_rom->base()[(eos_enable << 13) + offset];
		}
	}

	if (!aux_decode_1)
	{
		switch (offset >> 13)
		{
		case 0: // U2
			data = m_os7_rom->base()[offset];
			break;

		case 1: break;
		case 2: break;

		case 4:
			cs1 = 0;
			break;

		case 5:
			cs2 = 0;
			break;

		case 6:
			cs3 = 0;
			break;

		case 7:
			cs4 = 0;
			break;
		}
	}

	data &= m_cart->read(offset & 0x7fff, cs1, cs2, cs3, cs4);
	data = m_slot[0]->bd_r(offset & 0xff, data, 1, biorq, 1, 1, 1);
	data = m_slot[1]->bd_r(offset, data, bmreq, biorq, aux_rom_cs, 1, cas2);
	data = m_slot[2]->bd_r(offset, data, 1, 1, 1, cas1, cas2);

	return data;
}


//-------------------------------------------------
// mreq_w - memory request write
//-------------------------------------------------

void adam_state::mreq_w(offs_t offset, uint8_t data)
{
	int bmreq = 0, biorq = 1, aux_rom_cs = 1, cas1 = 1, cas2 = 1;

	if (offset < 0x8000)
	{
		switch (m_mioc & 0x03)
		{
		case LO_INTERNAL_RAM:
			cas1 = 0;
			break;

		case LO_RAM_EXPANSION:
			cas2 = 0;
			break;

		case LO_OS7_ROM_INTERNAL_RAM:
			if (offset >= 0x2000)
			{
				cas1 = 0;
			}
			break;
		}
	}
	else
	{
		switch ((m_mioc >> 2) & 0x03)
		{
		case HI_INTERNAL_RAM:
			cas1 = 0;
			break;

		case HI_RAM_EXPANSION:
			if (!m_game)
			{
				cas2 = 0;
			}
			break;
		}
	}

	if (!cas1)
	{
		m_ram->pointer()[offset] = data;
	}

	// TODO: cartridge slot write
	m_slot[0]->bd_w(offset & 0xff, data, 1, biorq, 1, 1, 1);
	m_slot[1]->bd_w(offset, data, bmreq, biorq, aux_rom_cs, 1, cas2);
	m_slot[2]->bd_w(offset, data, 1, 1, 1, cas1, cas2);
}


//-------------------------------------------------
//  iorq_r - I/O request read
//-------------------------------------------------

uint8_t adam_state::iorq_r(offs_t offset)
{
	int bmreq = 1, biorq = 0, aux_rom_cs = 1, cas1 = 1, cas2 = 1;

	uint8_t data = 0;

	switch ((offset >> 5) & 0x07)
	{
	case 1:
		data = adamnet_r();
		break;

	case 3:
		data = mioc_r();
		break;

	case 5:
		if (BIT(offset, 0))
			data = m_vdc->register_read();
		else
			data = m_vdc->vram_read();
		break;

	case 7:
		if (BIT(offset, 1))
			data = m_joy2->read();
		else
			data = m_joy1->read();
		break;
	}

	data = m_slot[0]->bd_r(offset & 0xff, data, 1, biorq, 1, 1, 1);
	data = m_slot[1]->bd_r(offset, data, bmreq, biorq, aux_rom_cs, 1, cas2);
	data = m_slot[2]->bd_r(offset, data, 1, 1, 1, cas1, cas2);

	return data;
}


//-------------------------------------------------
//  iorq_w - I/O request write
//-------------------------------------------------

void adam_state::iorq_w(offs_t offset, uint8_t data)
{
	int bmreq = 1, biorq = 0, aux_rom_cs = 1, cas1 = 1, cas2 = 1;

	switch ((offset >> 5) & 0x07)
	{
	case 1:
		adamnet_w(data);
		break;

	case 3:
		mioc_w(data);
		break;

	case 4:
		m_joy1->common0_w(1);
		m_joy1->common1_w(0);
		m_joy2->common0_w(1);
		m_joy2->common1_w(0);
		break;

	case 5:
		if (BIT(offset, 0))
			m_vdc->register_write(data);
		else
			m_vdc->vram_write(data);
		break;

	case 6:
		m_joy1->common0_w(0);
		m_joy1->common1_w(1);
		m_joy2->common0_w(0);
		m_joy2->common1_w(1);
		break;

	case 7:
		m_psg->write(data);
		break;
	}

	m_slot[0]->bd_w(offset & 0xff, data, 1, biorq, 1, 1, 1);
	m_slot[1]->bd_w(offset, data, bmreq, biorq, aux_rom_cs, 1, cas2);
	m_slot[2]->bd_w(offset, data, 1, 1, 1, cas1, cas2);
}


//-------------------------------------------------
//  mioc_r -
//-------------------------------------------------

uint8_t adam_state::mioc_r()
{
	return m_mioc & 0x0f;
}


//-------------------------------------------------
//  mioc_w -
//-------------------------------------------------

void adam_state::mioc_w(uint8_t data)
{
	/*

	    bit     description

	    0       Lower memory option 0
	    1       Lower memory option 1
	    2       Upper memory option 0
	    3       Upper memory option 1
	    4
	    5
	    6
	    7

	*/

	m_mioc = data;
}



//**************************************************************************
//  ADAMNET
//**************************************************************************

//-------------------------------------------------
//  adamnet_r -
//-------------------------------------------------

uint8_t adam_state::adamnet_r()
{
	return m_an & 0x0f;
}


//-------------------------------------------------
//  adamnet_w -
//-------------------------------------------------

void adam_state::adamnet_w(uint8_t data)
{
	/*

	    bit     description

	    0       Network reset
	    1       EOS enable
	    2
	    3
	    4
	    5
	    6
	    7

	*/

	if (BIT(m_an, 0) && !BIT(data, 0))
	{
		// network reset
		m_adamnet->reset_w(ASSERT_LINE);
		m_adamnet->reset_w(CLEAR_LINE);
	}

	m_an = data;
}


//-------------------------------------------------
//  m6801_p1_w -
//-------------------------------------------------

void adam_state::m6801_p1_w(uint8_t data)
{
	/*

	    bit     description

	    0       BA8
	    1       BA9
	    2       BA10
	    3       BA11
	    4       BA12
	    5       BA13
	    6       BA14
	    7       BA15

	*/

	m_ba = (data << 8) | (m_ba & 0xff);
}


//-------------------------------------------------
//  m6801_p2_r -
//-------------------------------------------------

uint8_t adam_state::m6801_p2_r()
{
	/*

	    bit     description

	    0       M6801 mode bit 0
	    1       M6801 mode bit 1
	    2       M6801 mode bit 2
	    3       NET RXD
	    4

	*/

	uint8_t data = M6801_MODE_7;

	// NET RXD
	data |= m_adamnet->rxd_r() << 3;

	return data;
}


//-------------------------------------------------
//  m6801_p2_w -
//-------------------------------------------------

void adam_state::m6801_p2_w(uint8_t data)
{
	/*

	    bit     description

	    0       _DMA
	    1
	    2       _BWR
	    3
	    4       NET TXD

	*/

	// DMA
	m_dma = BIT(data, 0);

	// write
	m_bwr = BIT(data, 2);

	// NET TXD
	m_adamnet->txd_w(BIT(data, 4));
}


//-------------------------------------------------
//  m6801_p3_r -
//-------------------------------------------------

uint8_t adam_state::m6801_p3_r()
{
	/*

	    bit     description

	    0       BD0
	    1       BD1
	    2       BD2
	    3       BD3
	    4       BD4
	    5       BD5
	    6       BD6
	    7       BD7

	*/

	return m_data_out;
}


//-------------------------------------------------
//  m6801_p3_w -
//-------------------------------------------------

void adam_state::m6801_p3_w(uint8_t data)
{
	/*

	    bit     description

	    0       BD0
	    1       BD1
	    2       BD2
	    3       BD3
	    4       BD4
	    5       BD5
	    6       BD6
	    7       BD7

	*/

	m_data_in = data;
}


//-------------------------------------------------
//  m6801_p4_w -
//-------------------------------------------------

void adam_state::m6801_p4_w(uint8_t data)
{
	/*

	    bit     description

	    0       BA0
	    1       BA1
	    2       BA2
	    3       BA3
	    4       BA4
	    5       BA5
	    6       BA6
	    7       BA7

	*/

	m_ba = (m_ba & 0xff00) | data;
}



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

//-------------------------------------------------
//  ADDRESS_MAP( adam_mem )
//-------------------------------------------------

void adam_state::adam_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(adam_state::mreq_r), FUNC(adam_state::mreq_w));
}


//-------------------------------------------------
//  ADDRESS_MAP( adam_io )
//-------------------------------------------------

void adam_state::adam_io(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(adam_state::iorq_r), FUNC(adam_state::iorq_w));
}



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

//-------------------------------------------------
//  INPUT_PORTS( adam )
//-------------------------------------------------

static INPUT_PORTS_START( adam )
	// defined in bus/adamnet/kb.c
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  TMS9928A_INTERFACE( vdc_intf )
//-------------------------------------------------

void adam_state::vdc_int_w(int state)
{
	if (state && !m_vdp_nmi)
	{
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
	}

	m_vdp_nmi = state;
}

//-------------------------------------------------
//  M6801_INTERFACE( m6801_intf )
//-------------------------------------------------

void adam_state::os3_w(int state)
{
	if (state && !m_dma)
	{
		if (!m_bwr)
		{
			//logerror("Master 6801 write to %04x data %02x\n", m_ba, m_data_in);

			m_ram->pointer()[m_ba] = m_data_in;
		}
		else
		{
			m_data_out = m_ram->pointer()[m_ba];

			//logerror("Master 6801 read from %04x data %02x\n", m_ba, m_data_out);

			m_netcpu->set_input_line(M6801_IS3_LINE, ASSERT_LINE);
			m_netcpu->set_input_line(M6801_IS3_LINE, CLEAR_LINE);
		}
	}
}


void adam_state::joy1_irq_w(int state)
{
	// TODO
}

void adam_state::joy2_irq_w(int state)
{
	// TODO
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( adam )
//-------------------------------------------------

void adam_state::machine_start()
{
	// state saving
	save_item(NAME(m_mioc));
	save_item(NAME(m_game));
	save_item(NAME(m_an));
	save_item(NAME(m_ba));
	save_item(NAME(m_dma));
	save_item(NAME(m_bwr));
	save_item(NAME(m_data_in));
	save_item(NAME(m_data_out));
	save_item(NAME(m_spindis));
	save_item(NAME(m_vdp_nmi));
}


void adam_state::machine_reset()
{
	if (m_cart->exists())
	{
		// game reset
		m_game = 1;
		m_mioc = (HI_CARTRIDGE_ROM << 2) | LO_OS7_ROM_INTERNAL_RAM;
	}
	else
	{
		// computer reset
		m_game = 0;
		m_mioc = 0;
	}

	m_an = 0;

	m_maincpu->reset();
	m_netcpu->reset();
}



//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  DEVICE_INPUT_DEFAULTS( drive2 )
//-------------------------------------------------

DEVICE_INPUT_DEFAULTS_START( drive2 )
	DEVICE_INPUT_DEFAULTS("SW3", 0x01, 0x01)
DEVICE_INPUT_DEFAULTS_END


//-------------------------------------------------
//  adam machine configuration
//-------------------------------------------------

void adam_state::adam(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, XTAL(7'159'090)/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &adam_state::adam_mem);
	m_maincpu->set_addrmap(AS_IO, &adam_state::adam_io);

	M6801(config, m_netcpu, XTAL(4'000'000));
	m_netcpu->out_p1_cb().set(FUNC(adam_state::m6801_p1_w));
	m_netcpu->in_p2_cb().set(FUNC(adam_state::m6801_p2_r));
	m_netcpu->out_p2_cb().set(FUNC(adam_state::m6801_p2_w));
	m_netcpu->in_p3_cb().set(FUNC(adam_state::m6801_p3_r));
	m_netcpu->out_p3_cb().set(FUNC(adam_state::m6801_p3_w));
	m_netcpu->out_p4_cb().set(FUNC(adam_state::m6801_p4_w));
	m_netcpu->out_sc2_cb().set(FUNC(adam_state::os3_w));
	config.set_perfect_quantum(m_netcpu);

	// video hardware
	TMS9928A(config, m_vdc, XTAL(10'738'635)).set_screen("screen");
	m_vdc->set_vram_size(0x4000);
	m_vdc->int_callback().set(FUNC(adam_state::vdc_int_w));
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SN76489A(config, m_psg, XTAL(7'159'090)/2);
	m_psg->add_route(ALL_OUTPUTS, "mono", 1.00);
	// TODO: enable when Z80 has better WAIT pin emulation
	//m_psg->ready_cb().set_inputline(m_maincpu, Z80_INPUT_LINE_WAIT).invert();

	// devices
	ADAMNET(config, m_adamnet, 0);
	ADAMNET_SLOT(config, "net1", m_adamnet, adamnet_devices, "kb");
	ADAMNET_SLOT(config, "net2", m_adamnet, adamnet_devices, "prn");
	ADAMNET_SLOT(config, "net3", m_adamnet, adamnet_devices, "ddp");
	ADAMNET_SLOT(config, "net4", m_adamnet, adamnet_devices, "fdc");
	ADAMNET_SLOT(config, "net5", m_adamnet, adamnet_devices, "fdc").set_option_device_input_defaults("fdc", device_iptdef_drive2);
	ADAMNET_SLOT(config, "net6", m_adamnet, adamnet_devices, nullptr);
	ADAMNET_SLOT(config, "net7", m_adamnet, adamnet_devices, nullptr);
	ADAMNET_SLOT(config, "net8", m_adamnet, adamnet_devices, nullptr);
	ADAMNET_SLOT(config, "net9", m_adamnet, adamnet_devices, nullptr);
	ADAMNET_SLOT(config, "net10", m_adamnet, adamnet_devices, nullptr);
	ADAMNET_SLOT(config, "net11", m_adamnet, adamnet_devices, nullptr);
	ADAMNET_SLOT(config, "net12", m_adamnet, adamnet_devices, nullptr);
	ADAMNET_SLOT(config, "net13", m_adamnet, adamnet_devices, nullptr);
	ADAMNET_SLOT(config, "net14", m_adamnet, adamnet_devices, nullptr);
	ADAMNET_SLOT(config, "net15", m_adamnet, adamnet_devices, nullptr);

	COLECOVISION_CARTRIDGE_SLOT(config, m_cart, colecovision_cartridges, nullptr);
	ADAM_EXPANSION_SLOT(config, m_slot[0], XTAL(7'159'090)/2, adam_slot1_devices, "adamlink"); // left
	m_slot[0]->irq().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	ADAM_EXPANSION_SLOT(config, m_slot[1], XTAL(7'159'090)/2, adam_slot2_devices, nullptr); // center
	m_slot[1]->irq().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	ADAM_EXPANSION_SLOT(config, m_slot[2], XTAL(7'159'090)/2, adam_slot3_devices, "ram"); // right

	COLECOVISION_CONTROL_PORT(config, m_joy1, colecovision_control_port_devices, "hand");
	m_joy1->irq().set(FUNC(adam_state::joy1_irq_w));
	COLECOVISION_CONTROL_PORT(config, m_joy2, colecovision_control_port_devices, nullptr);
	m_joy2->irq().set(FUNC(adam_state::joy2_irq_w));

	// internal ram
	RAM(config, m_ram).set_default_size("64K");

	// software lists
	SOFTWARE_LIST(config, "colec_cart_list").set_original("coleco");
	SOFTWARE_LIST(config, "colec_hb_list").set_original("coleco_homebrew");
	SOFTWARE_LIST(config, "adam_cart_list").set_original("adam_cart");
	SOFTWARE_LIST(config, "cass_list").set_original("adam_cass");
	SOFTWARE_LIST(config, "flop_list").set_original("adam_flop");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( adam )
//-------------------------------------------------

ROM_START( adam )
	ROM_REGION( 0x2000, "os7", 0)
	ROM_LOAD( "os7.u2", 0x0000, 0x2000, CRC(3aa93ef3) SHA1(45bedc4cbdeac66c7df59e9e599195c778d86a92) )

	ROM_REGION( 0xa000, "boot", 0)
	ROM_LOAD( "alf @1 rev 57 e3d5.u8",  0x0000, 0x2000, CRC(565b364a) SHA1(ebdafad6e268e7ed1674c1fb89607622748a5b36) )
	ROM_LOAD( "alf @2 rev 57 ae6a.u20", 0x2000, 0x2000, CRC(44a1cff4) SHA1(661cdf36d9699d6c21c5f9e205ebc41c707359dd) )
	ROM_LOAD( "alf @3 rev 57 8534.u21", 0x4000, 0x2000, CRC(77657b90) SHA1(d25d32ab6c8fafbc21b4b925b3e644fa26d111f7) )
	ROM_LOAD( "eos 6 rev 57 08dd.u22",  0x8000, 0x2000, CRC(ef6403c5) SHA1(28c7616cd02e4286f9b4c1c4a8b8850832b49fcb) )
	ROM_CONTINUE(                       0x6000, 0x2000 )
	ROM_LOAD( "wp_r80.rom",             0x0000, 0x8000, BAD_DUMP CRC(58d86a2a) SHA1(d4aec4efe1431e56fe52d83baf9118542c525255) ) // should be separate 8/16K ROMs

	ROM_REGION( 0x800, M6801_TAG, 0 )
	ROM_LOAD( "master rev a 174b.u6", 0x000, 0x800, CRC(035a7a3d) SHA1(0426e6eaf18c2be9fe08066570c214ab5951ee14) )
ROM_END



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY   FULLNAME  FLAGS
COMP( 1982, adam, 0,      coleco, adam,    adam,  adam_state, empty_init, "Coleco", "Adam",   MACHINE_SUPPORTS_SAVE )



adds2020.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

    Skeleton driver for ADDS 2020 terminal.

    The PCB has a 68-pin PLCC ASIC at the top right corner, next to the 36.000 (Y4), 24.0000 (Y5), 4.9152 MHz (Y6) XTALs:

        ©NCR 1986
        006-0130001 PT
        200-87700
        M823270 8701A

    This presumably does the video encoding, and possibly also contains the character graphics (not present in any dumped ROMs).

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

#include "emu.h"
//#include "bus/rs232/rs232.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/input_merger.h"
#include "machine/nvram.h"
#include "machine/scn_pci.h"
#include "video/scn2674.h"
#include "screen.h"

namespace {

class adds2020_state : public driver_device
{
public:
	adds2020_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_epci(*this, "epci")
		, m_avdc(*this, "avdc")
		, m_ram(*this, "nvram")
	{
	}

	void adds2020(machine_config &config);

private:
	SCN2674_DRAW_CHARACTER_MEMBER(draw_character);

	u8 char_r(offs_t offset);

	void unknown_0000_w(u8 data);
	void unknown_9800_w(offs_t offset, u8 data);

	void prog_map(address_map &map) ATTR_COLD;
	void ext_map(address_map &map) ATTR_COLD;
	void char_map(address_map &map) ATTR_COLD;

	required_device<mcs51_cpu_device> m_maincpu;
	required_device<scn_pci_device> m_epci;
	required_device<scn2674_device> m_avdc;
	required_shared_ptr<u8> m_ram;
};

SCN2674_DRAW_CHARACTER_MEMBER(adds2020_state::draw_character)
{
}

u8 adds2020_state::char_r(offs_t offset)
{
	return m_ram[offset & 0x1fff];
}

void adds2020_state::unknown_0000_w(u8 data)
{
	logerror("%s: Writing %02Xh to 0000h\n", machine().describe_context(), data);
}

void adds2020_state::unknown_9800_w(offs_t offset, u8 data)
{
	logerror("%s: Writing %02Xh to %04Xh\n", machine().describe_context(), data, offset + 0x9800);
}

void adds2020_state::prog_map(address_map &map)
{
	map(0x0000, 0xbfff).rom().region("firmware", 0);
}

void adds2020_state::ext_map(address_map &map)
{
	map(0x0000, 0x0000).w(FUNC(adds2020_state::unknown_0000_w));
	map(0x4000, 0x5fff).ram().share("nvram");
	map(0x6000, 0x67ff).mirror(0x1800).ram(); // code suggests one HW variant may have a full 8K here
	map(0x8000, 0x8000).select(6).lr8([this](offs_t offset) { return m_epci->read(offset >> 1); }, "epci_r");
	map(0x8001, 0x8001).select(6).lw8([this](offs_t offset, u8 data) { m_epci->write(offset >> 1, data); }, "epci_w");
	map(0x8800, 0x8807).rw(m_avdc, FUNC(scn2674_device::read), FUNC(scn2674_device::write));
	map(0x9800, 0x9801).w(FUNC(adds2020_state::unknown_9800_w));
}

void adds2020_state::char_map(address_map &map)
{
	map.global_mask(0x7ff);
	map(0x000, 0x7ff).ram();
}

static INPUT_PORTS_START(adds2020)
INPUT_PORTS_END

void adds2020_state::adds2020(machine_config &config)
{
	I8031(config, m_maincpu, 10.92_MHz_XTAL); // P8031AH
	m_maincpu->set_addrmap(AS_PROGRAM, &adds2020_state::prog_map);
	m_maincpu->set_addrmap(AS_IO, &adds2020_state::ext_map);

	INPUT_MERGER_ANY_HIGH(config, "mainint").output_handler().set_inputline(m_maincpu, MCS51_INT1_LINE);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1); // NEC D4464C-15L + 3.5V battery (only first 3K is actually saved)

	SCN2661B(config, m_epci, 4.9152_MHz_XTAL);
	m_epci->rxrdy_handler().set("mainint", FUNC(input_merger_device::in_w<0>));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	//screen.set_raw(24_MHz_XTAL, 960, 0, 800, 361, 0, 338);
	screen.set_raw(36_MHz_XTAL, 1440, 0, 1188, 361, 0, 338);
	screen.set_screen_update(m_avdc, FUNC(scn2674_device::screen_update));

	SCN2674(config, m_avdc, 36_MHz_XTAL / 9); // SCN2674B
	m_avdc->set_character_width(9); // cell width guessed (10 in 80-column mode?)
	m_avdc->set_screen("screen");
	m_avdc->set_addrmap(0, &adds2020_state::char_map);
	m_avdc->set_display_callback(FUNC(adds2020_state::draw_character));
	m_avdc->mbc_char_callback().set(FUNC(adds2020_state::char_r));
	m_avdc->breq_callback().set_inputline(m_maincpu, MCS51_T0_LINE);
	m_avdc->intr_callback().set("mainint", FUNC(input_merger_device::in_w<1>));

	// TODO: speaker
}

ROM_START(adds2020)
	ROM_REGION(0xc000, "firmware", ROMREGION_ERASEFF)
	ROM_LOAD("2020_v2.5_ua5.bin", 0x0000, 0x4000, CRC(2d9e3397) SHA1(9aec8fdfe064618c20b4ba85dd8b71a44e29bbd3))
	ROM_LOAD("2020_v2.5_ua6.bin", 0x4000, 0x2000, CRC(f4cbda6f) SHA1(efe03a15d7eab4038de1e8118c6891d2078166fd))
	ROM_LOAD("2020_v2.5_ua8.bin", 0x8000, 0x4000, CRC(4a8ac850) SHA1(ce02a0e904ed07930a891360f1e5779fbee8eaac))
ROM_END

} // anonymous namespace

COMP(1986, adds2020, 0, 0, adds2020, adds2020, adds2020_state, empty_init, "Applied Digital Data Systems", "ADDS 2020", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



adi_vt52.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    ADI VT52

    VT52 compatible terminal

    Hardware:
    - SY6502
    - R6532-11
    - HD46505
    - SYU6551A
    - MC6850P
    - MB8128-15 (2k RAM)
    - 16.5888 MHz XTAL
    - 2x10 position DIP switch

    TODO:
    - VRAM bit 7 is unknown
    - Unknown DIP switch settings
    - Replace HLE serial keyboard
    - 6850 ACIA clock rate and source
    - AUX port

    Notes:
    - Displays "ERROR 8" - this means "keyboard error". The keyboard needs to
      send 0xfe on startup
    - Everything here is a guess, only a PCB picture is available
    - PCB marked "A. D. I. 304-029-01" and date code 841210

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

#include "emu.h"

#include "adi_unk_kbd.h" // only here so that it doesn't bitrot

#include "bus/rs232/keyboard.h"
#include "bus/rs232/rs232.h"
#include "cpu/m6502/m6502.h"
#include "machine/6850acia.h"
#include "machine/clock.h"
#include "machine/input_merger.h"
#include "machine/mos6530.h"
#include "machine/mos6551.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"


namespace {


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

class adi_vt52_state : public driver_device
{
public:
	adi_vt52_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_riot(*this, "riot"),
		m_crtc(*this, "crtc"),
		m_palette(*this, "palette"),
		m_mos6551(*this, "mos6551"),
		m_rs232(*this, "rs232"),
		m_mc6850(*this, "mc6850"),
		m_vram(*this, "vram"),
		m_chargen(*this, "chargen")
	{ }

	void adi_vt52(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<m6502_device> m_maincpu;
	required_device<mos6532_device> m_riot;
	required_device<hd6845s_device> m_crtc;
	required_device<palette_device> m_palette;
	required_device<mos6551_device> m_mos6551;
	required_device<rs232_port_device> m_rs232;
	required_device<acia6850_device> m_mc6850;
	required_shared_ptr<uint8_t> m_vram;
	required_region_ptr<uint8_t> m_chargen;

	void mem_map(address_map &map) ATTR_COLD;

	MC6845_UPDATE_ROW(crtc_update_row);
};


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

void adi_vt52_state::mem_map(address_map &map)
{
	map.global_mask(0x7fff);
	map(0x0000, 0x0003).rw(m_mos6551, FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x0040, 0x0040).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x0041, 0x0041).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x0080, 0x00ff).mirror(0x100).m(m_riot, FUNC(mos6532_device::ram_map));
	map(0x0280, 0x029f).m(m_riot, FUNC(mos6532_device::io_map));
	map(0x2000, 0x27ff).mirror(0x800).ram().share("vram");
	map(0x4000, 0x4001).rw(m_mc6850, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0x7000, 0x7fff).rom().region("maincpu", 0);
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( adi_vt52 )
	PORT_START("dip1") // might be dip2
	PORT_DIPUNKNOWN_DIPLOC(0x01, IP_ACTIVE_LOW, "DIP1:1") PORT_NAME("DIP1-1")
	PORT_DIPUNKNOWN_DIPLOC(0x02, IP_ACTIVE_LOW, "DIP1:2") PORT_NAME("DIP1-2")
	PORT_DIPUNKNOWN_DIPLOC(0x04, IP_ACTIVE_LOW, "DIP1:3") PORT_NAME("DIP1-3") // when set, vram bit 7 set for unwritten areas
	PORT_DIPUNKNOWN_DIPLOC(0x08, IP_ACTIVE_LOW, "DIP1:4") PORT_NAME("DIP1-4")
	PORT_DIPUNKNOWN_DIPLOC(0x10, IP_ACTIVE_LOW, "DIP1:5") PORT_NAME("DIP1-5")
	PORT_DIPUNKNOWN_DIPLOC(0x20, IP_ACTIVE_LOW, "DIP1:6") PORT_NAME("DIP1-6")
	PORT_DIPNAME(0x40, 0x00, "Local Echo") PORT_DIPLOCATION("DIP1:7")
	PORT_DIPSETTING(0x00, DEF_STR( On ))
	PORT_DIPSETTING(0x40, DEF_STR( Off ))
	PORT_DIPUNKNOWN_DIPLOC(0x80, IP_ACTIVE_LOW, "DIP1:8") PORT_NAME("DIP1-8")

	PORT_START("dip2") // might be dip1
	PORT_DIPNAME(0x0f, 0x0e, "Baud Rate") PORT_DIPLOCATION("DIP2:1,2,3,4")
	PORT_DIPSETTING(0x00, "9600 (duplicate?)")
	PORT_DIPSETTING(0x01, "50")
	PORT_DIPSETTING(0x02, "75")
	PORT_DIPSETTING(0x03, "110")
	PORT_DIPSETTING(0x04, "134.5")
	PORT_DIPSETTING(0x05, "150")
	PORT_DIPSETTING(0x06, "300")
	PORT_DIPSETTING(0x07, "600")
	PORT_DIPSETTING(0x08, "1200")
	PORT_DIPSETTING(0x09, "1800")
	PORT_DIPSETTING(0x0a, "2400")
	PORT_DIPSETTING(0x0b, "3600")
	PORT_DIPSETTING(0x0c, "4800")
	PORT_DIPSETTING(0x0d, "7200")
	PORT_DIPSETTING(0x0e, "9600")
	PORT_DIPSETTING(0x0f, "19200")
	PORT_DIPUNKNOWN(0x10, IP_ACTIVE_LOW) PORT_NAME("DIP2-5") PORT_DIPLOCATION("DIP2:5") // 50/60 hz switch?
	PORT_DIPNAME(0x20, 0x00, "Stop Bits") PORT_DIPLOCATION("DIP2:6")
	PORT_DIPSETTING(0x00, "1")
	PORT_DIPSETTING(0x20, "2")
	PORT_DIPNAME(0x40, 0x40, "Parity") PORT_DIPLOCATION("DIP2:7")
	PORT_DIPSETTING(0x00, "Odd")
	PORT_DIPSETTING(0x40, "Even")
	PORT_DIPUNKNOWN_DIPLOC(0x80, IP_ACTIVE_LOW, "DIP2:8") PORT_NAME("DIP2-8")
INPUT_PORTS_END


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

MC6845_UPDATE_ROW(adi_vt52_state::crtc_update_row)
{
	const pen_t *const pen = m_palette->pens();

	for (unsigned x = 0; x < x_count; x++)
	{
		uint8_t code = m_vram[(ma + x) & 0x7ff];
		uint8_t data = m_chargen[((code & 0x7f) << 4) | ra];

		if (x == cursor_x)
			data = 0xff;

		// draw 8 pixels of the character
		for (int i = 0; i < 8; i++)
			bitmap.pix(y, x * 8 + i) = pen[BIT(data, 7 - i)];
	}
}

static const gfx_layout char_layout =
{
	8, 12,
	128,
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8*16
};

static GFXDECODE_START( chars )
	GFXDECODE_ENTRY( "chargen", 0, char_layout, 0, 1 )
GFXDECODE_END


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

void adi_vt52_state::machine_start()
{
}

void adi_vt52_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

static DEVICE_INPUT_DEFAULTS_START( rs232 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void adi_vt52_keyboard_devices(device_slot_interface &device)
{
	device.option_add("keyboard", SERIAL_KEYBOARD);
}

static DEVICE_INPUT_DEFAULTS_START( keyboard )
	DEVICE_INPUT_DEFAULTS("RS232_TXBAUD",   0xff, RS232_BAUD_9600)
	DEVICE_INPUT_DEFAULTS("RS232_DATABITS", 0xff, RS232_DATABITS_8)
	DEVICE_INPUT_DEFAULTS("RS232_PARITY",   0xff, RS232_PARITY_NONE)
	DEVICE_INPUT_DEFAULTS("RS232_STOPBITS", 0xff, RS232_STOPBITS_1)
DEVICE_INPUT_DEFAULTS_END

void adi_vt52_state::adi_vt52(machine_config &config)
{
	M6502(config, m_maincpu, 16.5888_MHz_XTAL / 16); // divider guessed
	m_maincpu->set_addrmap(AS_PROGRAM, &adi_vt52_state::mem_map);

	input_merger_any_high_device &irq_merger(INPUT_MERGER_ANY_HIGH(config, "irq_merger"));
	irq_merger.output_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	MOS6532(config, m_riot, 16.5888_MHz_XTAL / 16);
	m_riot->pa_rd_callback().set_ioport("dip1");
	m_riot->pb_rd_callback().set_ioport("dip2");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_raw(16.5888_MHz_XTAL, 800, 0, 640, 307, 0, 288);
	screen.set_screen_update(m_crtc, FUNC(mc6845_device::screen_update));

	HD6845S(config, m_crtc, 16.5888_MHz_XTAL / 8);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8); // might be 9
	m_crtc->set_update_row_callback(FUNC(adi_vt52_state::crtc_update_row));
	m_crtc->out_vsync_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);

	GFXDECODE(config, "gfxdecode", m_palette, chars);

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	MOS6551(config, m_mos6551, 16.5888_MHz_XTAL / 16);
	m_mos6551->set_xtal(16.5888_MHz_XTAL / 9);
	m_mos6551->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_mos6551->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr)); // unknown if connected
	m_mos6551->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts)); // unknown if connected
	m_mos6551->irq_handler().set("irq_merger", FUNC(input_merger_any_high_device::in_w<0>));

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set(m_mos6551, FUNC(mos6551_device::write_rxd));
	m_rs232->dsr_handler().set(m_mos6551, FUNC(mos6551_device::write_dsr)); // unknown if connected
	m_rs232->cts_handler().set(m_mos6551, FUNC(mos6551_device::write_cts)); // unknown if connected
	m_rs232->set_option_device_input_defaults("null_modem", DEVICE_INPUT_DEFAULTS_NAME(rs232));

	ACIA6850(config, m_mc6850, 16.5888_MHz_XTAL / 16); // for keyboard
	m_mc6850->irq_handler().set("irq_merger", FUNC(input_merger_any_high_device::in_w<1>));

	// might be clocked externally by the keyboard
	clock_device &kbd_clock(CLOCK(config, "kbd_clock", 9600 * 64));
	kbd_clock.signal_handler().set(m_mc6850, FUNC(acia6850_device::write_rxc));

	rs232_port_device &kbd(RS232_PORT(config, "kbd", adi_vt52_keyboard_devices, "keyboard"));
	kbd.rxd_handler().set(m_mc6850, FUNC(acia6850_device::write_rxd));
	kbd.set_option_device_input_defaults("keyboard", DEVICE_INPUT_DEFAULTS_NAME(keyboard));
}


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

ROM_START( adi_vt52 )
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("vt52_rev0.u6", 0x0000, 0x1000, CRC(ddeb77ef) SHA1(46ee82b22ac1ed1693189305b49001d4daed8f46))

	ROM_REGION(0x800, "chargen", 0)
	ROM_LOAD("vt52_cg.u16", 0x0000, 0x0800, CRC(af2be51d) SHA1(055a7c0de842a17a60924e17153241e823b2a7f1))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY  FULLNAME  FLAGS
COMP( 1984, adi_vt52, 0,      0,      adi_vt52, adi_vt52, adi_vt52_state, empty_init, "ADI",   "VT52",   MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



adm11.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for ADM 11 and related terminals by Lear Siegler, Inc.

    * ADM 11 (June 1983): LSI's first terminal to use the "High Touch"
      enclosure shared by the rest of its family and other LSI terminals.
      Its detachable typewriter-style keyboard has only 4 function keys
      (8 with shifting). Display is one 24x80 page plus a status line.
      Font supports "business graphics" as well as ASCII characters.
    * ADM 12 (March 1984): Expanded version of ADM 11 implementing block
      mode and many more editing functions. 2 pages of text are available
      in 80x24 mode, and there are also 80x48 and 158x24 modes.
    * ADM 11G & ADM 12G (March 1984): ADM 11 and ADM 12 enhanced with
      Tektronix Plot 10-like graphics.
    * ADM 1178 (March 1984): Emulates the keyboard and communications of the
      IBM 3278, though an external protocol converter is also required for
      network compatibility.
    * ADM 1278 (March 1984): Block mode version of the ADM 1178.
    * ADM 12plus (February 1985): Later version of the ADM 12, with 132-
      column and horizontal split display options. Up to 4 pages of display
      memory are supported.
    * ADM 11plus (June 1985): Later version of ADM 11 with more function
      keys and character and line insertion/deletion.

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

#include "emu.h"
//#include "bus/rs232/rs232.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/eeprompar.h"
#include "video/scn2674.h"
#include "screen.h"


namespace {

class adm11_state : public driver_device
{
public:
	adm11_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_avdc(*this, "avdc")
		, m_charram(*this, "charram")
		, m_attrram(*this, "attrram")
		, m_chargen(*this, "chargen")
		, m_mbc(false)
		, m_latched_attr(0)
	{
	}

	void adm12(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void prog_map(address_map &map) ATTR_COLD;
	void ext_map(address_map &map) ATTR_COLD;
	void char_map(address_map &map) ATTR_COLD;
	void attr_map(address_map &map) ATTR_COLD;

	SCN2674_DRAW_CHARACTER_MEMBER(draw_character);
	void mbc_w(int state);

	u8 p1_r();
	void p1_w(u8 data);
	void char_latched_attr_w(offs_t offset, u8 data);
	void attr_latch_w(u8 data);

	required_device<mcs51_cpu_device> m_maincpu;
	required_device<scn2674_device> m_avdc;
	required_shared_ptr<u8> m_charram;
	required_shared_ptr<u8> m_attrram;
	required_region_ptr<u8> m_chargen;

	bool m_mbc;
	u8 m_latched_attr;
};


void adm11_state::machine_start()
{
	save_item(NAME(m_mbc));
}

SCN2674_DRAW_CHARACTER_MEMBER(adm11_state::draw_character)
{
	u16 dots = m_chargen[charcode << 4 | linecount];
	dots |= (dots & 0x80) << 1;

	if (BIT(attrcode, 2))
		dots ^= 0x1ff;

	for (int i = 0; i < 9; i++)
	{
		bitmap.pix(y, x++) = BIT(dots, 8) ? rgb_t::white() : rgb_t::black();
		dots <<= 1;
	}
}

void adm11_state::mbc_w(int state)
{
	m_mbc = state;
}

u8 adm11_state::p1_r()
{
	// Bit 0 low = display busy?
	return 0xfe | (m_mbc ? 0 : 1);
}

void adm11_state::p1_w(u8 data)
{
}

void adm11_state::char_latched_attr_w(offs_t offset, u8 data)
{
	m_charram[offset] = data;
	m_attrram[offset] = m_latched_attr;
}

void adm11_state::attr_latch_w(u8 data)
{
	m_latched_attr = data;
}

void adm11_state::prog_map(address_map &map)
{
	map(0x0000, 0x1fff).rom().region("program", 0);
}

void adm11_state::ext_map(address_map &map)
{
	map(0x0000, 0x07ff).ram();
	map(0x2000, 0x21ff).rw("eeprom", FUNC(eeprom_parallel_28xx_device::read), FUNC(eeprom_parallel_28xx_device::write));
	map(0x4000, 0x4fff).mirror(0x1000).ram().share("charram");
	map(0x6000, 0x6fff).mirror(0x1000).w(FUNC(adm11_state::char_latched_attr_w));
	map(0x8000, 0x8fff).mirror(0x1000).ram().share("attrram");
	map(0xa000, 0xa000).mirror(0x1fff).w(FUNC(adm11_state::attr_latch_w));
	map(0xe000, 0xe007).rw(m_avdc, FUNC(scn2674_device::read), FUNC(scn2674_device::write));
}

void adm11_state::char_map(address_map &map)
{
	map.global_mask(0x0fff);
	map(0x0000, 0x0fff).ram().share("charram");
}

void adm11_state::attr_map(address_map &map)
{
	map.global_mask(0x0fff);
	map(0x0000, 0x0fff).ram().share("attrram");
}


static INPUT_PORTS_START(adm12)
INPUT_PORTS_END

void adm11_state::adm12(machine_config &config)
{
	I8031(config, m_maincpu, 11.0592_MHz_XTAL); // P8031AH
	m_maincpu->set_addrmap(AS_PROGRAM, &adm11_state::prog_map);
	m_maincpu->set_addrmap(AS_IO, &adm11_state::ext_map);
	m_maincpu->port_in_cb<1>().set(FUNC(adm11_state::p1_r));
	m_maincpu->port_out_cb<1>().set(FUNC(adm11_state::p1_w));
	// TODO: RXD/TXD are serial communications; INT0 is serial keyboard data

	EEPROM_2804(config, "eeprom"); // X2804AP

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_raw(15.93_MHz_XTAL, 900, 0, 720, 295, 0, 275); // 17.7 kHz horizontal
	screen.set_screen_update("avdc", FUNC(scn2674_device::screen_update));

	SCN2674(config, m_avdc, 15.93_MHz_XTAL / 9); // SCN2674B
	m_avdc->set_screen("screen");
	m_avdc->set_character_width(9); // 6 in 158-column mode?
	m_avdc->set_addrmap(0, &adm11_state::char_map);
	m_avdc->set_addrmap(1, &adm11_state::attr_map);
	m_avdc->set_display_callback(FUNC(adm11_state::draw_character));
	m_avdc->intr_callback().set_inputline(m_maincpu, MCS51_INT1_LINE);
	m_avdc->mbc_callback().set(FUNC(adm11_state::mbc_w));
}

// "MEM2764" silkscreened next to all memories, only one of which is an actual 2764 (most are SY2128-4 SRAMs)
ROM_START(adm12)
	ROM_REGION(0x2000, "program", 0)
	ROM_LOAD("u13.bin", 0x0000, 0x2000, CRC(3c928176) SHA1(dd741c620da2ced9979456296c2af0387461cdf1)) // MBM2764-30

	// Keyboard MCU might be a COP420, as with the ADM 11

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("u35.bin", 0x0000, 0x1000, CRC(66d7bc44) SHA1(cd839839f29657207098d85900cb570285be91a6)) // HN462732-P
ROM_END

} // anonymous namespace


COMP(1984, adm12, 0, 0, adm12, adm12, adm11_state, empty_init, "Lear Siegler", "ADM 12 Video Display Terminal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



adm23.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for ADM 23 terminal.

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

#include "emu.h"
#include "cpu/z8/z8.h"
#include "video/mc6845.h"
#include "screen.h"


namespace {

class adm23_state : public driver_device
{
public:
	adm23_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_chargen(*this, "chargen")
	{
	}

	void adm23(machine_config &config);

private:
	MC6845_UPDATE_ROW(update_row);
	MC6845_ON_UPDATE_ADDR_CHANGED(addr_changed);

	void mem_map(address_map &map) ATTR_COLD;

	required_region_ptr<u8> m_chargen;
};

MC6845_UPDATE_ROW(adm23_state::update_row)
{
}

MC6845_ON_UPDATE_ADDR_CHANGED(adm23_state::addr_changed)
{
}

void adm23_state::mem_map(address_map &map)
{
	map(0x0800, 0x1fff).rom().region("program", 0x0800);
	map(0x2020, 0x2020).nopr();
	map(0x2080, 0x2080).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x2081, 0x2081).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0xc080, 0xc080).nopw();
	map(0xd080, 0xd080).nopr();
}


static INPUT_PORTS_START(adm23)
INPUT_PORTS_END


void adm23_state::adm23(machine_config &config)
{
	z8_device &maincpu(Z8682(config, "maincpu", 14.7428_MHz_XTAL / 2));
	maincpu.set_addrmap(AS_PROGRAM, &adm23_state::mem_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(14.7428_MHz_XTAL, 873, 0, 720, 281, 0, 264);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	mc6845_device &crtc(SY6545_1(config, "crtc", 14.7428_MHz_XTAL / 9));
	crtc.set_char_width(9);
	crtc.set_show_border_area(false);
	crtc.set_screen("screen");
	crtc.set_update_row_callback(FUNC(adm23_state::update_row));
	crtc.set_on_update_addr_change_callback(FUNC(adm23_state::addr_changed));
}

ROM_START(adm23)
	ROM_REGION(0x2000, "program", 0) // CPU is clearly a Z8682, though its location is labeled "Z8 8681"
	ROM_LOAD("136261-083_u9.bin", 0x0000, 0x2000, CRC(85da07e7) SHA1(a1305d0f06c7e3c6075b05ca0f11c53a901b5013))

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD("chr_u10.bin", 0x0000, 0x0800, CRC(cd053232) SHA1(6b4136a91d0dcd9cb5df92c54c9c30b1cb5f1974))

	ROM_REGION(0x0020, "proms", 0)
	ROM_LOAD("129253-61_u3.bin", 0x0000, 0x0020, NO_DUMP) // N82S123N at location labeled "S288"
ROM_END

} // anonymous namespace


COMP(1982, adm23, 0, 0, adm23, adm23, adm23_state, empty_init, "Lear Siegler", "ADM 23 Smart Terminal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



adm31.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for ADM-31 terminal.

    The ADM-31 and ADM-42 Data Display Terminals were Lear Siegler, Inc.'s
    first microprocessor-based video terminals, introduced in 1978 as the
    respective successors to their earlier ADM-1A and ADM-2 "smart"
    terminals. The original ADM-31 model was apparently rebranded in 1980
    as the ADM-31 Intermediate Terminal, and the ADM-32 was released a few
    months later.

    While the ADM-31 and ADM-32 only support 2 pages of display memory, the
    ADM-42 could be upgraded to 8. Enhancements over the ADM-31 offered by
    both the ADM-42 and ADM-32 include a status line, a larger monitor and
    a detachable keyboard. Several other expansion options were offered for
    the ADM-42, including synchronous serial and parallel printer ports.

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

#include "emu.h"
//#include "bus/rs232/rs232.h"
#include "cpu/m6800/m6800.h"
#include "machine/com8116.h"
#include "machine/input_merger.h"
#include "machine/6821pia.h"
#include "machine/6850acia.h"
#include "video/tms9927.h"
#include "screen.h"


namespace {

class adm31_state : public driver_device
{
public:
	adm31_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_acia(*this, "acia%u", 1U)
		, m_brg(*this, "brg")
		, m_vtac(*this, "vtac")
		, m_chargen(*this, "chargen")
		, m_keys(*this, "R%u", 0U)
		, m_baud(*this, "BAUD")
		, m_caps_lamp(*this, "caps_lamp")
	{
	}

	void adm31(machine_config &mconfig);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void kbd_scan_w(u8 data);
	u8 kbd_scan_r();
	void kbd_status_w(u8 data);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device_array<acia6850_device, 2> m_acia;
	required_device<com8116_device> m_brg;
	required_device<crt5027_device> m_vtac;
	required_region_ptr<u8> m_chargen;
	optional_ioport_array<16> m_keys;
	required_ioport m_baud;
	output_finder<> m_caps_lamp;

	u8 m_kbd_scan = 0;
};


void adm31_state::machine_start()
{
	m_caps_lamp.resolve();

	save_item(NAME(m_kbd_scan));
}

void adm31_state::machine_reset()
{
	// Baud rate switches read by CPU on ADM-42, but not on ADM-31?
	m_brg->stt_str_w(m_baud->read());
}


u32 adm31_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}


void adm31_state::kbd_scan_w(u8 data)
{
	m_kbd_scan = data & 0x7f;
}

u8 adm31_state::kbd_scan_r()
{
	return BIT(m_keys[(m_kbd_scan & 0x78) >> 3].read_safe(0xff), m_kbd_scan & 0x07) ? 0x7f : 0xff;
}

void adm31_state::kbd_status_w(u8 data)
{
	m_caps_lamp = BIT(data, 0);
}

void adm31_state::mem_map(address_map &map)
{
	map(0x0000, 0x03ff).ram();
	map(0x7000, 0x7000).nopw();
	map(0x7800, 0x7803).rw("pia", FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x7900, 0x7900).portr("S5");
	map(0x7a00, 0x7a01).rw(m_acia[0], FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0x7c00, 0x7c01).rw(m_acia[1], FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0x7d00, 0x7d00).portr("S6");
	map(0x7e00, 0x7e00).portr("S4");
	map(0x7f00, 0x7f0f).rw(m_vtac, FUNC(crt5027_device::read), FUNC(crt5027_device::write));
	map(0x8000, 0x8fff).ram();
	map(0xe000, 0xffff).rom().region("program", 0);
}


static INPUT_PORTS_START(adm31)
	PORT_START("R0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key D1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key D7")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key C5")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key D2")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key D4/F4")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key D9/F9")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR('"') PORT_CODE(KEYCODE_2)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CHAR(0x1b) PORT_CODE(KEYCODE_F7)

	PORT_START("R1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Rub") PORT_CHAR(0x7f) PORT_CODE(KEYCODE_F8)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 9E/9F")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key D0/F0")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key B4/B6")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 92/93")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('q') PORT_CHAR('Q') PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cap Lock") PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_CODE(KEYCODE_CAPSLOCK)

	PORT_START("R2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR('$') PORT_CODE(KEYCODE_4)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR('&') PORT_CODE(KEYCODE_6)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR('\'') PORT_CODE(KEYCODE_7)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('8') PORT_CHAR('(') PORT_CODE(KEYCODE_8)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('9') PORT_CHAR(')') PORT_CODE(KEYCODE_9)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0') PORT_CHAR('_') PORT_CODE(KEYCODE_0)

	PORT_START("R3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('-') PORT_CHAR('=') PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('^') PORT_CHAR('~') PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\\') PORT_CHAR('|') PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab  Back Tab") PORT_CHAR(0x09) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD)) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD)) PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) PORT_CODE(KEYCODE_MINUS_PAD)

	PORT_START("R4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('w') PORT_CHAR('W') PORT_CODE(KEYCODE_W)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('e') PORT_CHAR('E') PORT_CODE(KEYCODE_E)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('r') PORT_CHAR('R') PORT_CODE(KEYCODE_R)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('t') PORT_CHAR('T') PORT_CODE(KEYCODE_T)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('y') PORT_CHAR('Y') PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('u') PORT_CHAR('U') PORT_CODE(KEYCODE_U)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('i') PORT_CHAR('I') PORT_CODE(KEYCODE_I)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('o') PORT_CHAR('O') PORT_CODE(KEYCODE_O)

	PORT_START("R5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('p') PORT_CHAR('P') PORT_CODE(KEYCODE_P)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('[') PORT_CHAR('{') PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(']') PORT_CHAR('}') PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CHAR(0x0d) PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD)) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD)) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(TAB_PAD)) PORT_CODE(KEYCODE_PLUS_PAD)

	PORT_START("R6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('a') PORT_CHAR('A') PORT_CODE(KEYCODE_A)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('s') PORT_CHAR('S') PORT_CODE(KEYCODE_S)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('d') PORT_CHAR('D') PORT_CODE(KEYCODE_D)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('f') PORT_CHAR('F') PORT_CODE(KEYCODE_F)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('g') PORT_CHAR('G') PORT_CODE(KEYCODE_G)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('h') PORT_CHAR('H') PORT_CODE(KEYCODE_H)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('j') PORT_CHAR('J') PORT_CODE(KEYCODE_J)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('k') PORT_CHAR('K') PORT_CODE(KEYCODE_K)

	PORT_START("R7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('l') PORT_CHAR('L') PORT_CODE(KEYCODE_L)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHAR('+') PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(':') PORT_CHAR('*') PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('@') PORT_CHAR('`') PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Line Feed") PORT_CHAR(0x0a) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD)) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) PORT_CODE(KEYCODE_1_PAD)

	PORT_START("R8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Clear") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('z') PORT_CHAR('Z') PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('x') PORT_CHAR('X') PORT_CODE(KEYCODE_X)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('c') PORT_CHAR('C') PORT_CODE(KEYCODE_C)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('v') PORT_CHAR('V') PORT_CODE(KEYCODE_V)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('b') PORT_CHAR('B') PORT_CODE(KEYCODE_B)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('n') PORT_CHAR('N') PORT_CODE(KEYCODE_N)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_M)

	PORT_START("R9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',') PORT_CHAR('<') PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('.') PORT_CHAR('>') PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('/') PORT_CHAR('?') PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(' ') PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("New Line  New Page") PORT_CODE(KEYCODE_END)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD)) PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD)) PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad Return") PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_CODE(KEYCODE_ENTER_PAD)

	PORT_START("R10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 9D/94")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key E3/40")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD)) PORT_CODE(KEYCODE_COMMA_PAD)

	PORT_START("SHIFT")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CHAR(UCHAR_SHIFT_2) PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CHAR(UCHAR_SHIFT_1) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x1d, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("S4")
	PORT_DIPNAME(0x01, 0x00, "Break Key") PORT_DIPLOCATION("S4:1")
	PORT_DIPSETTING(0x01, "Disable")
	PORT_DIPSETTING(0x00, "Enable")
	PORT_DIPNAME(0x02, 0x02, "Refresh Rate") PORT_DIPLOCATION("S4:2")
	PORT_DIPSETTING(0x00, "50 Hz")
	PORT_DIPSETTING(0x02, "60 Hz")
	PORT_DIPNAME(0x1c, 0x14, "Modem Port") PORT_DIPLOCATION("S4:3,4,5")
	PORT_DIPSETTING(0x00, "7 DB, EP, 2 SB")
	PORT_DIPSETTING(0x04, "7 DB, OP, 2 SB")
	PORT_DIPSETTING(0x08, "7 DB, EP, 1 SB")
	PORT_DIPSETTING(0x0c, "7 DB, OP, 1 SB")
	PORT_DIPSETTING(0x10, "8 DB, NP, 2 SB")
	PORT_DIPSETTING(0x14, "8 DB, NP, 1 SB")
	PORT_DIPSETTING(0x18, "8 DB, EP, 1 SB")
	PORT_DIPSETTING(0x00, "8 DB, OP, 1 SB")
	PORT_DIPNAME(0x20, 0x00, DEF_STR(Unused)) PORT_DIPLOCATION("S4:6")
	PORT_DIPSETTING(0x20, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPNAME(0x40, 0x00, "Transmission") PORT_DIPLOCATION("S4:7")
	PORT_DIPSETTING(0x00, "Block Mode")
	PORT_DIPSETTING(0x40, "Conversation Mode")
	PORT_DIPNAME(0x80, 0x00, "Duplex") PORT_DIPLOCATION("S4:8")
	PORT_DIPSETTING(0x00, "Full Duplex")
	PORT_DIPSETTING(0x80, "Half Duplex")

	PORT_START("S5")
	PORT_DIPNAME(0x01, 0x00, DEF_STR(Unused)) PORT_DIPLOCATION("S5:1")
	PORT_DIPSETTING(0x01, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPNAME(0x02, 0x00, DEF_STR(Unused)) PORT_DIPLOCATION("S5:2")
	PORT_DIPSETTING(0x02, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPNAME(0x1c, 0x14, "Printer Port") PORT_DIPLOCATION("S5:3,4,5")
	PORT_DIPSETTING(0x00, "7 DB, EP, 2 SB")
	PORT_DIPSETTING(0x04, "7 DB, OP, 2 SB")
	PORT_DIPSETTING(0x08, "7 DB, EP, 1 SB")
	PORT_DIPSETTING(0x0c, "7 DB, OP, 1 SB")
	PORT_DIPSETTING(0x10, "8 DB, NP, 2 SB")
	PORT_DIPSETTING(0x14, "8 DB, NP, 1 SB")
	PORT_DIPSETTING(0x18, "8 DB, EP, 1 SB")
	PORT_DIPSETTING(0x00, "8 DB, OP, 1 SB")
	PORT_DIPNAME(0x20, 0x00, DEF_STR(Unused)) PORT_DIPLOCATION("S5:6")
	PORT_DIPSETTING(0x20, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPNAME(0x40, 0x00, DEF_STR(Unused)) PORT_DIPLOCATION("S5:7")
	PORT_DIPSETTING(0x40, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPNAME(0x80, 0x00, "Printer Port Buffer") PORT_DIPLOCATION("S5:8")
	PORT_DIPSETTING(0x80, "Disable")
	PORT_DIPSETTING(0x00, "Enable")

	PORT_START("S6")
	PORT_DIPNAME(0x7f, 0x00, "Polling Address") PORT_DIPLOCATION("S6:1,2,3,4,5,6,7")
	PORT_DIPSETTING(0x00, "00")
	PORT_DIPSETTING(0x01, "01")
	PORT_DIPSETTING(0x02, "02")
	PORT_DIPSETTING(0x03, "03")
	PORT_DIPSETTING(0x04, "04")
	PORT_DIPSETTING(0x05, "05")
	PORT_DIPSETTING(0x06, "06")
	PORT_DIPSETTING(0x07, "07")
	PORT_DIPSETTING(0x08, "08")
	PORT_DIPSETTING(0x09, "09")
	PORT_DIPSETTING(0x0a, "0A")
	PORT_DIPSETTING(0x0b, "0B")
	PORT_DIPSETTING(0x0c, "0C")
	PORT_DIPSETTING(0x0d, "0D")
	PORT_DIPSETTING(0x0e, "0E")
	PORT_DIPSETTING(0x0f, "0F")
	PORT_DIPSETTING(0x10, "10")
	PORT_DIPSETTING(0x11, "11")
	PORT_DIPSETTING(0x12, "12")
	PORT_DIPSETTING(0x13, "13")
	PORT_DIPSETTING(0x14, "14")
	PORT_DIPSETTING(0x15, "15")
	PORT_DIPSETTING(0x16, "16")
	PORT_DIPSETTING(0x17, "17")
	PORT_DIPSETTING(0x18, "18")
	PORT_DIPSETTING(0x19, "19")
	PORT_DIPSETTING(0x1a, "1A")
	PORT_DIPSETTING(0x1b, "1B")
	PORT_DIPSETTING(0x1c, "1C")
	PORT_DIPSETTING(0x1d, "1D")
	PORT_DIPSETTING(0x1e, "1E")
	PORT_DIPSETTING(0x1f, "1F")
	PORT_DIPSETTING(0x20, "20")
	PORT_DIPSETTING(0x21, "21")
	PORT_DIPSETTING(0x22, "22")
	PORT_DIPSETTING(0x23, "23")
	PORT_DIPSETTING(0x24, "24")
	PORT_DIPSETTING(0x25, "25")
	PORT_DIPSETTING(0x26, "26")
	PORT_DIPSETTING(0x27, "27")
	PORT_DIPSETTING(0x28, "28")
	PORT_DIPSETTING(0x29, "29")
	PORT_DIPSETTING(0x2a, "2A")
	PORT_DIPSETTING(0x2b, "2B")
	PORT_DIPSETTING(0x2c, "2C")
	PORT_DIPSETTING(0x2d, "2D")
	PORT_DIPSETTING(0x2e, "2E")
	PORT_DIPSETTING(0x2f, "2F")
	PORT_DIPSETTING(0x30, "30")
	PORT_DIPSETTING(0x31, "31")
	PORT_DIPSETTING(0x32, "32")
	PORT_DIPSETTING(0x33, "33")
	PORT_DIPSETTING(0x34, "34")
	PORT_DIPSETTING(0x35, "35")
	PORT_DIPSETTING(0x36, "36")
	PORT_DIPSETTING(0x37, "37")
	PORT_DIPSETTING(0x38, "38")
	PORT_DIPSETTING(0x39, "39")
	PORT_DIPSETTING(0x3a, "3A")
	PORT_DIPSETTING(0x3b, "3B")
	PORT_DIPSETTING(0x3c, "3C")
	PORT_DIPSETTING(0x3d, "3D")
	PORT_DIPSETTING(0x3e, "3E")
	PORT_DIPSETTING(0x3f, "3F")
	PORT_DIPSETTING(0x40, "40")
	PORT_DIPSETTING(0x41, "41")
	PORT_DIPSETTING(0x42, "42")
	PORT_DIPSETTING(0x43, "43")
	PORT_DIPSETTING(0x44, "44")
	PORT_DIPSETTING(0x45, "45")
	PORT_DIPSETTING(0x46, "46")
	PORT_DIPSETTING(0x47, "47")
	PORT_DIPSETTING(0x48, "48")
	PORT_DIPSETTING(0x49, "49")
	PORT_DIPSETTING(0x4a, "4A")
	PORT_DIPSETTING(0x4b, "4B")
	PORT_DIPSETTING(0x4c, "4C")
	PORT_DIPSETTING(0x4d, "4D")
	PORT_DIPSETTING(0x4e, "4E")
	PORT_DIPSETTING(0x4f, "4F")
	PORT_DIPSETTING(0x50, "50")
	PORT_DIPSETTING(0x51, "51")
	PORT_DIPSETTING(0x52, "52")
	PORT_DIPSETTING(0x53, "53")
	PORT_DIPSETTING(0x54, "54")
	PORT_DIPSETTING(0x55, "55")
	PORT_DIPSETTING(0x56, "56")
	PORT_DIPSETTING(0x57, "57")
	PORT_DIPSETTING(0x58, "58")
	PORT_DIPSETTING(0x59, "59")
	PORT_DIPSETTING(0x5a, "5A")
	PORT_DIPSETTING(0x5b, "5B")
	PORT_DIPSETTING(0x5c, "5C")
	PORT_DIPSETTING(0x5d, "5D")
	PORT_DIPSETTING(0x5e, "5E")
	PORT_DIPSETTING(0x5f, "5F")
	PORT_DIPSETTING(0x60, "60")
	PORT_DIPSETTING(0x61, "61")
	PORT_DIPSETTING(0x62, "62")
	PORT_DIPSETTING(0x63, "63")
	PORT_DIPSETTING(0x64, "64")
	PORT_DIPSETTING(0x65, "65")
	PORT_DIPSETTING(0x66, "66")
	PORT_DIPSETTING(0x67, "67")
	PORT_DIPSETTING(0x68, "68")
	PORT_DIPSETTING(0x69, "69")
	PORT_DIPSETTING(0x6a, "6A")
	PORT_DIPSETTING(0x6b, "6B")
	PORT_DIPSETTING(0x6c, "6C")
	PORT_DIPSETTING(0x6d, "6D")
	PORT_DIPSETTING(0x6e, "6E")
	PORT_DIPSETTING(0x6f, "6F")
	PORT_DIPSETTING(0x70, "70")
	PORT_DIPSETTING(0x71, "71")
	PORT_DIPSETTING(0x72, "72")
	PORT_DIPSETTING(0x73, "73")
	PORT_DIPSETTING(0x74, "74")
	PORT_DIPSETTING(0x75, "75")
	PORT_DIPSETTING(0x76, "76")
	PORT_DIPSETTING(0x77, "77")
	PORT_DIPSETTING(0x78, "78")
	PORT_DIPSETTING(0x79, "79")
	PORT_DIPSETTING(0x7a, "7A")
	PORT_DIPSETTING(0x7b, "7B")
	PORT_DIPSETTING(0x7c, "7C")
	PORT_DIPSETTING(0x7d, "7D")
	PORT_DIPSETTING(0x7e, "7E")
	PORT_DIPSETTING(0x7f, "7F")
	PORT_DIPNAME(0x80, 0x80, "Polling Option") PORT_DIPLOCATION("S6:8")
	PORT_DIPSETTING(0x80, "Disable")
	PORT_DIPSETTING(0x00, "Enable")

	PORT_START("BAUD")
	PORT_DIPNAME(0x0f, 0x0e, "Modem Baud Rate") PORT_DIPLOCATION("BS1:1,2,3,4")
	PORT_DIPSETTING(0x00, "50")
	PORT_DIPSETTING(0x01, "75")
	PORT_DIPSETTING(0x02, "110")
	PORT_DIPSETTING(0x03, "134.5")
	PORT_DIPSETTING(0x04, "150")
	PORT_DIPSETTING(0x05, "300")
	PORT_DIPSETTING(0x06, "600")
	PORT_DIPSETTING(0x07, "1200")
	PORT_DIPSETTING(0x08, "1800")
	PORT_DIPSETTING(0x09, "2000")
	PORT_DIPSETTING(0x0a, "2400")
	PORT_DIPSETTING(0x0b, "3600")
	PORT_DIPSETTING(0x0c, "4800")
	PORT_DIPSETTING(0x0d, "7200")
	PORT_DIPSETTING(0x0e, "9600")
	PORT_DIPNAME(0xf0, 0x70, "Printer Baud Rate") PORT_DIPLOCATION("BS2:1,2,3,4")
	PORT_DIPSETTING(0x00, "50")
	PORT_DIPSETTING(0x10, "75")
	PORT_DIPSETTING(0x20, "110")
	PORT_DIPSETTING(0x30, "134.5")
	PORT_DIPSETTING(0x40, "150")
	PORT_DIPSETTING(0x50, "300")
	PORT_DIPSETTING(0x60, "600")
	PORT_DIPSETTING(0x70, "1200")
	PORT_DIPSETTING(0x80, "1800")
	PORT_DIPSETTING(0x90, "2000")
	PORT_DIPSETTING(0xa0, "2400")
	PORT_DIPSETTING(0xb0, "3600")
	PORT_DIPSETTING(0xc0, "4800")
	PORT_DIPSETTING(0xd0, "7200")
	PORT_DIPSETTING(0xe0, "9600")
INPUT_PORTS_END

void adm31_state::adm31(machine_config &config)
{
	M6800(config, m_maincpu, 19.584_MHz_XTAL / 20);
	m_maincpu->set_addrmap(AS_PROGRAM, &adm31_state::mem_map);

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, M6800_IRQ_LINE);

	pia6821_device &pia(PIA6821(config, "pia"));
	pia.writepa_handler().set(FUNC(adm31_state::kbd_scan_w));
	pia.readpa_handler().set(FUNC(adm31_state::kbd_scan_r));
	pia.writepb_handler().set(FUNC(adm31_state::kbd_status_w));
	pia.readpb_handler().set_ioport("SHIFT");

	ACIA6850(config, m_acia[0]);
	m_acia[0]->irq_handler().set("mainirq", FUNC(input_merger_device::in_w<0>));

	ACIA6850(config, m_acia[1]);
	m_acia[0]->irq_handler().set("mainirq", FUNC(input_merger_device::in_w<1>));

	COM8116(config, m_brg, 5.0688_MHz_XTAL);
	m_brg->fr_handler().set(m_acia[0], FUNC(acia6850_device::write_rxc));
	m_brg->fr_handler().append(m_acia[0], FUNC(acia6850_device::write_txc));
	m_brg->ft_handler().set(m_acia[1], FUNC(acia6850_device::write_rxc));
	m_brg->ft_handler().append(m_acia[1], FUNC(acia6850_device::write_txc));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(19.584_MHz_XTAL, 1020, 0, 800, 320, 0, 288);
	screen.set_screen_update(FUNC(adm31_state::screen_update));

	CRT5027(config, m_vtac, 19.584_MHz_XTAL / 10);
	m_vtac->set_screen("screen");
	m_vtac->set_char_width(10);
}


ROM_START(adm31)
	ROM_REGION(0x2000, "program", 0)
	ROM_LOAD("adm-31-u62.bin", 0x0000, 0x0800, CRC(57e557a5) SHA1(cb3021ab570c279cbaa673b5de8fa1ca9eb48188))
	ROM_LOAD("adm-31-u63.bin", 0x0800, 0x0800, CRC(1268a59c) SHA1(f0cd8562e0d5faebf84d8decaa848ff28f2ac637))
	ROM_LOAD("adm-31-u64.bin", 0x1000, 0x0800, CRC(8939fa00) SHA1(00f6a8a49e51a9501cd9d1e2aae366fb070a5a1d))
	ROM_LOAD("adm-31-u65.bin", 0x1800, 0x0800, CRC(53e4e2f1) SHA1(bf30241815c790de3354e1acfe84e760c889cbb1))

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD("chargen.bin", 0x0000, 0x0800, NO_DUMP)
ROM_END

} // anonymous namespace


COMP(1978, adm31, 0, 0, adm31, adm31, adm31_state, empty_init, "Lear Siegler", "ADM-31 Data Display Terminal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



adm36.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/************************************************************************************************************

    Skeleton driver for ADM 36 Video Display Terminal.

    The detachable keyboard has not been dumped. It is controlled serially through the PIO.

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

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/er1400.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"
#include "video/tms9927.h"
#include "screen.h"


namespace {

class adm36_state : public driver_device
{
public:
	adm36_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_earom(*this, "earom")
		, m_vtac(*this, "vtac")
		, m_chargen(*this, "chargen")
		, m_vram(*this, "vram%u", 0U)
	{
	}

	void adm36(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void pio_pa_w(u8 data);
	u8 pio_pb_r();
	void pio_pb_w(u8 data);
	void vsyn_w(int state);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_device<er1400_device> m_earom;
	required_device<crt5037_device> m_vtac;
	required_region_ptr<u8> m_chargen;
	required_shared_ptr_array<u8, 2> m_vram;

	bool m_vsyn = false;
};


u32 adm36_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void adm36_state::machine_start()
{
	m_vsyn = false;

	save_item(NAME(m_vsyn));
}

void adm36_state::machine_reset()
{
}

void adm36_state::pio_pa_w(u8 data)
{
	m_earom->data_w(BIT(data, 0));
}

u8 adm36_state::pio_pb_r()
{
	return (m_earom->data_r() ? 0 : 1) | (m_vsyn ? 0x20 : 0);
}

void adm36_state::pio_pb_w(u8 data)
{
	m_earom->clock_w(BIT(data, 4));
	m_earom->c3_w(BIT(data, 3));
	m_earom->c2_w(BIT(data, 2));
	m_earom->c1_w(BIT(data, 1));
}

void adm36_state::vsyn_w(int state)
{
	m_vsyn = bool(state);
}

void adm36_state::mem_map(address_map &map)
{
	map(0x0000, 0x2fff).rom().region("maincpu", 0);
	map(0x6000, 0x67ff).ram(); // MK4802J-3 (U76)
	map(0xe000, 0xefff).ram().share("vram0"); // 4x MK4118AN-1 (U66-U69)
	map(0xf000, 0xffff).ram().share("vram1"); // 4x MK4118AN-1 (U81-U84)
}

void adm36_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).mirror(0x0c).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x10, 0x13).mirror(0x0c).rw("sio", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
	map(0x20, 0x23).mirror(0x0c).rw("pio", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x40, 0x4f).rw(m_vtac, FUNC(crt5037_device::read), FUNC(crt5037_device::write));
}


static INPUT_PORTS_START(adm36)
INPUT_PORTS_END


static const z80_daisy_config daisy_chain[] =
{
	{ "ctc" },
	{ "sio" },
	{ "pio" },
	{ nullptr }
};

void adm36_state::adm36(machine_config &config)
{
	Z80(config, m_maincpu, 14.728_MHz_XTAL / 6);
	m_maincpu->set_addrmap(AS_PROGRAM, &adm36_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &adm36_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	z80pio_device &pio(Z80PIO(config, "pio", 14.728_MHz_XTAL / 6));
	pio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	pio.out_pa_callback().set(FUNC(adm36_state::pio_pa_w));
	pio.in_pb_callback().set(FUNC(adm36_state::pio_pb_r));
	pio.out_pb_callback().set(FUNC(adm36_state::pio_pb_w));

	ER1400(config, m_earom);

	//F3870(config, "keybcpu", 3.579545_MHz_XTAL);

	z80ctc_device &ctc(Z80CTC(config, "ctc", 14.728_MHz_XTAL / 6));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	ctc.set_clk<0>(14.728_MHz_XTAL / 12);
	ctc.set_clk<1>(14.728_MHz_XTAL / 12);
	ctc.set_clk<2>(14.728_MHz_XTAL / 12);
	ctc.set_clk<3>(14.728_MHz_XTAL / 12);
	ctc.zc_callback<0>().set("sio", FUNC(z80sio_device::txca_w));
	ctc.zc_callback<1>().set("sio", FUNC(z80sio_device::rxca_w));
	ctc.zc_callback<2>().set("sio", FUNC(z80sio_device::txcb_w));
	ctc.zc_callback<2>().append("sio", FUNC(z80sio_device::rxcb_w));

	z80sio_device &sio(Z80SIO(config, "sio", 14.728_MHz_XTAL / 6)); // MK3887N
	sio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	sio.out_txda_callback().set("modem", FUNC(rs232_port_device::write_txd));
	sio.out_rtsa_callback().set("modem", FUNC(rs232_port_device::write_rts));
	sio.out_dtra_callback().set("modem", FUNC(rs232_port_device::write_dtr));
	sio.out_txdb_callback().set("printer", FUNC(rs232_port_device::write_txd));
	sio.out_dtrb_callback().set("printer", FUNC(rs232_port_device::write_rts));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(14.728_MHz_XTAL, 936, 0, 720, 262, 0, 240);
	//screen.set_raw(24.3_MHz_XTAL, 1548, 0, 1188, 262, 0, 240);
	screen.set_screen_update(FUNC(adm36_state::screen_update));

	CRT5037(config, m_vtac, 14.728_MHz_XTAL / 9);
	m_vtac->set_char_width(9);
	m_vtac->set_screen("screen");
	m_vtac->vsyn_callback().set(FUNC(adm36_state::vsyn_w));

	rs232_port_device &modem(RS232_PORT(config, "modem", default_rs232_devices, nullptr)); // RS-232C, RS-422A or 20mA current loop
	modem.rxd_handler().set("sio", FUNC(z80sio_device::rxa_w));
	modem.cts_handler().set("sio", FUNC(z80sio_device::ctsa_w));
	modem.dcd_handler().set("sio", FUNC(z80sio_device::dcda_w));
	modem.dsr_handler().set("pio", FUNC(z80pio_device::pb6_w));

	rs232_port_device &printer(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	printer.rxd_handler().set("sio", FUNC(z80sio_device::rxb_w));
	printer.cts_handler().set("sio", FUNC(z80sio_device::ctsa_w));
	printer.dcd_handler().set("sio", FUNC(z80sio_device::dcda_w));
}


ROM_START(adm36)
	ROM_REGION(0x3000, "maincpu", 0)
	ROM_LOAD("u71_136261-012.bin", 0x0000, 0x2000, CRC(f08315c7) SHA1(3943a5fc587e690df81aa9694e6e452673ec5513))
	ROM_LOAD("u72_131671-015.bin", 0x2000, 0x1000, CRC(c397f4e2) SHA1(21513472fe4237bda8448a2ad85496757d4ece12))
	// U73-U75 are empty sockets

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("u56.bin", 0x0000, 0x1000, CRC(70e46897) SHA1(85b4360912fc05243b3b2df29bde5a3def94086b))
ROM_END

} // anonymous namespace


COMP(1981, adm36, 0, 0, adm36, adm36, adm36_state, empty_init, "Lear Siegler", "ADM 36 Video Display Terminal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



advision.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Dan Boris, hap
/*******************************************************************************

Entex Adventure Vision, tabletop game console

Hardware notes:
- INS8048-11 @ 11MHz (1KB internal ROM)
- COP411 for the sound, 1-bit speaker with volume control
- Molex socket for 4KB cartridges
- 1KB external RAM (2*MM2114N)
- 40 small rectangular red LEDs, a motor with a fast spinning mirror gives the
  illusion of a 150*40 screen
- 4-way joystick, 8 buttons (other than having buttons 2/4 swapped, left and
  right button panels are electronically the same)
- expansion port (unused)

The cartridge slot is different from what is common on other consoles. A game
cartridge is basically an EPROM chip wearing a jacket. Only 4 games were released
in total.

The mirror rotates at around 7Hz, the motor speed is not controlled by software,
and it differs a bit per console. This can be adjusted after enabling -cheat.
There's a mirror on both sides so the display refreshes at around 14Hz. A similar
technology was later used in the Nintendo Virtual Boy.

The display is faked in MAME. On the real thing, the picture is not as stable. The
width of 150 is specified by the BIOS, but it's possible to update the leds at a
different rate, hence MAME configures a larger screen. In fact, the homebrew demo
Code Red doesn't use the BIOS for it, and runs at 50*40 to save some RAM.

TODO:
- display refresh is actually ~14Hz, but doing that will make MAME very sluggish

BTANB:
- 2 thin vertical seams (do a hyperspace in defender to see them more clearly)
- glitches at the right edge during gameplay in scobra, and also during some
  explosion sounds in defender
- scobra screen is shifted to the right when the game scrolls

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

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/cop400/cop400.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/timer.h"
#include "sound/dac.h"
#include "sound/flt_vol.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

namespace {

class advision_state : public driver_device
{
public:
	advision_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_soundcpu(*this, "soundcpu"),
		m_dac(*this, "dac"),
		m_volume(*this, "volume"),
		m_screen(*this, "screen"),
		m_mirror_sync(*this, "mirror_sync"),
		m_led_update(*this, "led_update"),
		m_led_off(*this, "led_off"),
		m_joy(*this, "JOY"),
		m_conf(*this, "CONF")
	{ }

	void advision(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(set_screensize);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<i8048_device> m_maincpu;
	required_device<cop411_cpu_device> m_soundcpu;
	required_device<dac_1bit_device> m_dac;
	required_device<filter_volume_device> m_volume;
	required_device<screen_device> m_screen;
	required_device<timer_device> m_mirror_sync;
	required_device<timer_device> m_led_update;
	required_device<timer_device> m_led_off;
	required_ioport m_joy;
	required_ioport m_conf;

	static constexpr u32 DISPLAY_WIDTH = 0x400;

	bool m_video_strobe = false;
	bool m_video_enable = false;
	u8 m_video_bank = 0;
	u32 m_video_hpos = 0;
	u8 m_led_output[5] = { };
	u8 m_led_latch[5] = { };
	std::unique_ptr<u8[]> m_display;

	std::vector<u8> m_ext_ram;
	u16 m_rambank = 0;
	u8 m_sound_cmd = 0;

	void io_map(address_map &map) ATTR_COLD;
	void program_map(address_map &map) ATTR_COLD;

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void av_control_w(u8 data);
	void vblank(int state);
	TIMER_DEVICE_CALLBACK_MEMBER(led_update);
	TIMER_DEVICE_CALLBACK_MEMBER(led_off);
	int vsync_r();

	TIMER_CALLBACK_MEMBER(sound_cmd_sync);
	u8 sound_cmd_r();
	void sound_g_w(u8 data);
	void sound_d_w(u8 data);

	void bankswitch_w(u8 data);
	u8 ext_ram_r(offs_t offset);
	void ext_ram_w(offs_t offset, u8 data);
	u8 controller_r();
};



/*******************************************************************************
    Video
*******************************************************************************/

INPUT_CHANGED_MEMBER(advision_state::set_screensize)
{
	// reconfigure screen size when settings changed
	const int width = 9600 / (m_conf->read() & 0x3f);
	const int height = 40 * 4 + 3 - (m_conf->read() >> 6 & 3);
	m_screen->set_visible_area(0, width - 1, 0, height - 1);
}

u32 advision_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	u8 led_height = (m_conf->read() >> 6 & 3) + 1;

	bitmap.fill(0, cliprect);

	for (int y = 0; y < 40; y++)
	{
		u8 *src = &m_display[y * DISPLAY_WIDTH];

		for (int x = cliprect.left(); x <= cliprect.right(); x++)
		{
			u8 red = src[x] ? 0xff : 0;
			u8 green = red / 16;
			u8 blue = red / 12;

			int dy = y * 4 + (4 - led_height);

			for (int i = 0; i < led_height; i++)
			{
				if (cliprect.contains(x, dy + i))
					bitmap.pix(dy + i, x) = red << 16 | green << 8 | blue;
			}
		}
	}

	return 0;
}

void advision_state::av_control_w(u8 data)
{
	// P25-P27: led latch select
	m_video_bank = data >> 5 & 7;

	// disable led outputs (there is some delay before it takes effect)
	// see for example codered twister and anime girl, gaps between the 'pixels' should be visible but minimal
	if (m_video_bank == 0 && m_led_off->expire().is_never())
		m_led_off->adjust(attotime::from_usec(39));

	// P24 rising edge: transfer led latches to outputs
	if (!m_video_strobe && bool(data & 0x10))
	{
		std::copy_n(m_led_latch, std::size(m_led_output), m_led_output);
		m_led_off->adjust(attotime::never);
		m_video_enable = true;
	}
	m_video_strobe = bool(data & 0x10);

	// sync soundlatch (using gen_latch device here would give lots of logerror)
	machine().scheduler().synchronize(timer_expired_delegate(FUNC(advision_state::sound_cmd_sync), this), data >> 4);
}

void advision_state::vblank(int state)
{
	if (state && (m_screen->frame_number() & 3) == 0)
	{
		// starting a new frame
		std::fill_n(m_display.get(), DISPLAY_WIDTH * 40, 0);

		m_mirror_sync->adjust(attotime::from_usec(100));

		m_video_hpos = 0;
		m_led_update->adjust(attotime::zero);
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(advision_state::led_update)
{
	// write current leds to display buffer
	for (int y = 0; y < 8; y++)
	{
		for (int b = 0; b < 5; b++)
		{
			int pixel = m_video_enable ? BIT(m_led_output[b], y ^ 7) : 0;
			m_display[(b * 8 + y) * DISPLAY_WIDTH + m_video_hpos] = pixel;
		}
	}

	if (m_video_hpos < DISPLAY_WIDTH - 1)
	{
		// for official games, 1 'pixel' is 60us, but there are two spots that have
		// a longer duration: at x=50 and x=100 (see BTANB note about seams)
		m_led_update->adjust(attotime::from_usec(m_conf->read() & 0x3f));
		m_video_hpos++;
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(advision_state::led_off)
{
	m_video_enable = false;
}

int advision_state::vsync_r()
{
	// T1: mirror sync pulse (half rotation)
	return (m_mirror_sync->enabled()) ? 0 : 1;
}



/*******************************************************************************
    Sound
*******************************************************************************/

TIMER_CALLBACK_MEMBER(advision_state::sound_cmd_sync)
{
	m_sound_cmd = param;
}

u8 advision_state::sound_cmd_r()
{
	// L0-L3: sound command
	return m_sound_cmd;
}

void advision_state::sound_g_w(u8 data)
{
	// G0: speaker out
	m_dac->write(data & 1);
}

void advision_state::sound_d_w(u8 data)
{
	// D0: speaker volume
	m_volume->set_gain((data & 1) ? 0.5 : 1.0);
}



/*******************************************************************************
    Memory
*******************************************************************************/

void advision_state::bankswitch_w(u8 data)
{
	// P10,P11: RAM bank
	m_rambank = data & 3;

	// P12: 8048 EA pin
	m_maincpu->set_input_line(MCS48_INPUT_EA, BIT(data, 2) ? ASSERT_LINE : CLEAR_LINE);
}

u8 advision_state::ext_ram_r(offs_t offset)
{
	// read from external RAM
	u8 data = m_ext_ram[m_rambank << 8 | offset];
	if (machine().side_effects_disabled())
		return data;

	// transfer data to led latch
	if (m_video_bank > 0 && m_video_bank < 6)
		m_led_latch[5 - m_video_bank] = data ^ 0xff;

	// reset sound cpu
	else if (m_video_bank == 6)
		m_soundcpu->set_input_line(INPUT_LINE_RESET, (data & 1) ? CLEAR_LINE : ASSERT_LINE);

	return data;
}

void advision_state::ext_ram_w(offs_t offset, u8 data)
{
	// write to external RAM
	m_ext_ram[m_rambank << 8 | offset] = data;
}

void advision_state::program_map(address_map &map)
{
	map(0x0000, 0x0fff).r("cartslot", FUNC(generic_slot_device::read_rom));
}

void advision_state::io_map(address_map &map)
{
	map(0x00, 0xff).rw(FUNC(advision_state::ext_ram_r), FUNC(advision_state::ext_ram_w));
}



/*******************************************************************************
    Inputs
*******************************************************************************/

u8 advision_state::controller_r()
{
	u8 data = m_joy->read();

	// some of the buttons share the same bitmask as joystick directions
	if (~data & 0x01) data &= 0xcf; // button 1: down + up
	if (~data & 0x02) data &= 0xaf; // button 2: down + right
	if (~data & 0x04) data &= 0x6f; // button 4: down + left

	return data | 0x07;
}

static INPUT_PORTS_START( advision )
	PORT_START("JOY")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) // u
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) // r
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON4 ) // l
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON3 ) // d
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_4WAY
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_4WAY
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_4WAY
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_4WAY

	PORT_START("CONF")
	PORT_CONFNAME( 0x3f, 20, "Screen Width" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(advision_state::set_screensize), 0) // factors of 60
	PORT_CONFSETTING(    60, "160" )
	PORT_CONFSETTING(    30, "320" )
	PORT_CONFSETTING(    20, "480" )
	PORT_CONFSETTING(    15, "640" )
	PORT_CONFSETTING(    12, "800" )
	PORT_CONFSETTING(    10, "960" )
	PORT_CONFNAME( 0xc0, 0x80, "LED Height" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(advision_state::set_screensize), 0)
	PORT_CONFSETTING(    0x00, "25%" )
	PORT_CONFSETTING(    0x40, "50%" )
	PORT_CONFSETTING(    0x80, "75%" )
	PORT_CONFSETTING(    0xc0, "100%" )
INPUT_PORTS_END



/*******************************************************************************
    Machine Initialization
*******************************************************************************/

void advision_state::machine_start()
{
	// allocate display buffer
	m_display = std::make_unique<u8 []>(DISPLAY_WIDTH * 40);
	std::fill_n(m_display.get(), DISPLAY_WIDTH * 40, 0);
	save_pointer(NAME(m_display), DISPLAY_WIDTH * 40);

	// allocate external RAM
	m_ext_ram.resize(0x400);
	save_item(NAME(m_ext_ram));

	save_item(NAME(m_led_output));
	save_item(NAME(m_led_latch));

	save_item(NAME(m_rambank));
	save_item(NAME(m_video_strobe));
	save_item(NAME(m_video_enable));
	save_item(NAME(m_video_bank));
	save_item(NAME(m_sound_cmd));
	save_item(NAME(m_video_hpos));
}

void advision_state::machine_reset()
{
	m_video_hpos = 0;
	m_led_update->adjust(attotime::never);

	// reset sound CPU
	m_soundcpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
}



/*******************************************************************************
    Machine Config
*******************************************************************************/

void advision_state::advision(machine_config &config)
{
	// basic machine hardware
	I8048(config, m_maincpu, 11_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &advision_state::program_map);
	m_maincpu->set_addrmap(AS_IO, &advision_state::io_map);
	m_maincpu->p1_in_cb().set(FUNC(advision_state::controller_r));
	m_maincpu->p1_out_cb().set(FUNC(advision_state::bankswitch_w));
	m_maincpu->p2_out_cb().set(FUNC(advision_state::av_control_w));
	m_maincpu->t1_in_cb().set(FUNC(advision_state::vsync_r));

	COP411(config, m_soundcpu, 200000); // COP411L-KCN/N, R11=82k, C8=56pF
	m_soundcpu->set_config(COP400_CKI_DIVISOR_4, COP400_CKO_RAM_POWER_SUPPLY, false);
	m_soundcpu->read_l().set(FUNC(advision_state::sound_cmd_r));
	m_soundcpu->write_g().set(FUNC(advision_state::sound_g_w));
	m_soundcpu->write_d().set(FUNC(advision_state::sound_d_w));

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(4 * 14); // see notes
	m_screen->set_vblank_time(0);
	m_screen->set_size(960, 40 * 4 + 4);
	m_screen->set_visarea(0, 480 - 1, 0, 40 * 4 - 0); // default setting
	m_screen->set_screen_update(FUNC(advision_state::screen_update));
	m_screen->screen_vblank().set(FUNC(advision_state::vblank));

	TIMER(config, "mirror_sync").configure_generic(nullptr);
	TIMER(config, "led_update").configure_generic(FUNC(advision_state::led_update));
	TIMER(config, "led_off").configure_generic(FUNC(advision_state::led_off));

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "volume", 0.25);
	FILTER_VOLUME(config, m_volume).add_route(ALL_OUTPUTS, "speaker", 1.0);

	// cartridge
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "advision_cart");
	SOFTWARE_LIST(config, "cart_list").set_original("advision");
}



/*******************************************************************************
    ROMs
*******************************************************************************/

ROM_START( advision )
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "ins8048-11kdp_n.u5", 0x000, 0x400, CRC(279e33d1) SHA1(bf7b0663e9125c9bfb950232eab627d9dbda8460) ) // "<natsemi logo> /B8225 \\ INS8048-11KDP/N"

	ROM_REGION( 0x200, "soundcpu", 0 )
	ROM_LOAD( "cop411l-kcn_n.u8", 0x000, 0x200, CRC(81e95975) SHA1(8b6f8c30dd3e9d8e43f1ea20fba2361b383790eb) ) // "<natsemi logo> /B8223 \\ COP411L-KCN/N"
ROM_END

} // anonymous namespace



/*******************************************************************************
    Driver
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY  FULLNAME            FLAGS
SYST( 1982, advision, 0,      0,      advision, advision, advision_state, empty_init, "Entex", "Adventure Vision", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )



agat.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont, Sergey Svishchev
/***************************************************************************

    agat.cpp

    Driver for Agat series of Soviet Apple II non-clones

    These are similar to Apple II (same bus architecture, keyboard
    and floppy interface), but native video controllers are different.

    agat7 supports Apple (40col, HGR and DHR) video modes with add-on
    card; agat9 has built-in support for 40col and HGR.  Palette in
    Apple modes is different and pixel stretching is not done.

    To do (common):
    - video: use palette rom
    - video: text modes use 7x8 character cell and 224x256 raster
    - video: vertical raster splits (used at least by Rapira)
    - what is the state of devices at init and reset?
    - ignore debugger reads -- use side_effects_disabled()
    - softlists

    To do (agat7):
    - hw variant: 16 colors
    - hw variant: 256-char chargen
    - hw variant: 64K and 128K onboard memory
    - "500hz" interrupt breakage
    - what does write to C009 do? (basedos7.nib)

    To do (agat9):
    - accurate lc emulation (what do writes do? etc.)
    - 840k floppy: support writing, motor off, the rest of .aim format
    - memory expansion boards, a2bus_inh_w
    - hw variant: video status readback ("Moscow")
    - hw variant: agat9a model (via http://agatcomp.ru/Images/case.shtml)
    - mouse via parallel port
    - does cassette output work?
    - what do floating bus reads do?
    - why prodos does not boot?
    - why spriteos does not see printer card?

    Slot devices -- 1st party:
    - agat7: apple2 video card -- decimal 3.089.121 -- http://agatcomp.ru/Images/new_j121.shtml
    - agat7: Serial-parallel card -- decimal 3.089.106
    - agat9: Printer card -- decimal 3.089.174

    Slot devices -- 3rd party:
    - agat7: Mymrin's color expansion (p. 254-255, port C112)
    - IEEE-488, etc. via via http://agatcomp.ru/Reading/period/1s1995_35-usn1.shtml
    - HDC, etc. via http://agatcomp.ru/Hard/losthard.shtml
    - network cards -- via http://agatcomp.ru/Images/new_net.shtml
    - sound cards (5-voice synth card, MIDI i/o card) via http://agatcomp.ru/Images/new_sound.shtml
    - Nippel Mouse -- http://agatcomp.ru/Images/new_mouse.shtml
    - Nippel ADC (digital oscilloscope) -- via http://agatcomp.ru/Images/new_IO.shtml
    - Nippel Co-processor (R65C02 clone + dual-ported RAM) -- via http://agatcomp.ru/Images/new_IO.shtml
    - Sprite Card-93 (fd1793)
    - Sprite Programmable I/O (8035 + 8251 + 8253 + 8255) -- via http://agatcomp.ru/Images/new_IO.shtml
    - Sprite Communication Extension (8251 + 8253 + mc146818) -- via http://agatcomp.ru/Images/new_misc.shtml

    References for Agat-7:
    - http://agatcomp.ru/Reading/docs_txt.shtml (first set of links) (1987)
    - http://agatcomp.ru/Reading/docs_shtat.shtml (1989) -- in particular,
      http://agatcomp.ru/Reading/docs/TO4_5-Hi.djvu

    References for Agat-9:
    - http://agatcomp.ru/Reading/docs_shtat.shtml#l2

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

#include "emu.h"
#include "agatkeyb.h"
#include "agat7.h"
#include "agat9.h"

#include "bus/a2bus/a2diskiing.h"
#include "bus/a2bus/agat7langcard.h"
#include "bus/a2bus/agat7ports.h"
#include "bus/a2bus/agat7ram.h"
#include "bus/a2bus/agat840k_hle.h"
#include "bus/a2bus/agat_fdc.h"
#include "bus/a2bus/nippelclock.h"
#include "cpu/m6502/m6502.h"
#include "cpu/m6502/r65c02.h"
#include "imagedev/cassette.h"
#include "machine/bankdev.h"
#include "machine/timer.h"
#include "sound/spkrdev.h"

#include "screen.h"
#include "softlist.h"
#include "speaker.h"


namespace {

#define A7_CPU_TAG "maincpu"
#define A7_SPEAKER_TAG "speaker"
#define A7_CASSETTE_TAG "tape"
#define A7_UPPERBANK_TAG "inhbank"
#define A7_VIDEO_TAG "a7video"
#define A9_VIDEO_TAG "a9video"


class agat_base_state : public driver_device
{
public:
	agat_base_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, A7_CPU_TAG)
		, m_ram(*this, RAM_TAG)
		, m_a2bus(*this, "a2bus")
		, m_joy1x(*this, "joystick_1_x")
		, m_joy1y(*this, "joystick_1_y")
		, m_joy2x(*this, "joystick_2_x")
		, m_joy2y(*this, "joystick_2_y")
		, m_joybuttons(*this, "joystick_buttons")
		, m_speaker(*this, A7_SPEAKER_TAG)
		, m_cassette(*this, A7_CASSETTE_TAG)
		, m_upperbank(*this, A7_UPPERBANK_TAG)
	{ }

	INTERRUPT_GEN_MEMBER(agat_vblank);

	uint8_t c080_r(offs_t offset);
	void c080_w(offs_t offset, uint8_t data);
	uint8_t c100_r(offs_t offset);
	void c100_w(offs_t offset, uint8_t data);
	uint8_t c800_r(offs_t offset);
	void c800_w(offs_t offset, uint8_t data);
	uint8_t inh_r(offs_t offset);
	void inh_w(offs_t offset, uint8_t data);
	void a2bus_irq_w(int state);
	void a2bus_nmi_w(int state);
	void a2bus_inh_w(int state);

	uint8_t agat7_membank_r(offs_t offset);
	void agat7_membank_w(offs_t offset, uint8_t data);
	uint8_t agat7_ram_r(offs_t offset);
	void agat7_ram_w(offs_t offset, uint8_t data);
	uint8_t keyb_strobe_r();
	void keyb_strobe_w(uint8_t data);
	uint8_t cassette_toggle_r();
	void cassette_toggle_w(uint8_t data);
	uint8_t speaker_toggle_r();
	void speaker_toggle_w(uint8_t data);
	uint8_t interrupts_on_r();
	void interrupts_on_w(uint8_t data);
	uint8_t interrupts_off_r();
	void interrupts_off_w(uint8_t data);
	uint8_t flags_r(offs_t offset);
	uint8_t controller_strobe_r();
	void controller_strobe_w(uint8_t data);

	void kbd_put(u8 data);
	void kbd_meta(int state);

protected:
	required_device<cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<a2bus_device> m_a2bus;
	required_ioport m_joy1x, m_joy1y, m_joy2x, m_joy2y, m_joybuttons;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cassette;
	required_device<address_map_bank_device> m_upperbank;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

protected:
	int m_speaker_state;
	int m_cassette_state;

	double m_joystick_x1_time;
	double m_joystick_y1_time;
	double m_joystick_x2_time;
	double m_joystick_y2_time;

	uint8_t m_transchar, m_strobe;
	bool m_meta;

	int m_inh_slot;
	int m_cnxx_slot;

	uint8_t *m_ram_ptr;
	int m_ram_size;

	int m_inh_bank;

	double m_x_calibration, m_y_calibration;

	device_a2bus_card_interface *m_slotdevice[8];

	bool m_agat_interrupts;
	int m_agat7_membank;
	int m_agat7_ram_slot;

	uint8_t read_floatingbus();
};

class agat7_state : public agat_base_state
{
public:
	agat7_state(const machine_config &mconfig, device_type type, const char *tag)
		: agat_base_state(mconfig, type, tag)
		, m_video(*this, A7_VIDEO_TAG)
	{ }

	void agat7(machine_config &config);

	optional_device<agat7video_device> m_video;

	TIMER_DEVICE_CALLBACK_MEMBER(timer_irq);
	uint8_t keyb_data_r();

	void agat7_map(address_map &map) ATTR_COLD;
	void inhbank_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
};

class agat9_state : public agat_base_state
{
public:
	agat9_state(const machine_config &mconfig, device_type type, const char *tag)
		: agat_base_state(mconfig, type, tag)
		, m_video(*this, A9_VIDEO_TAG)
	{ }

	void agat9(machine_config &config);

	optional_device<agat9video_device> m_video;

	TIMER_DEVICE_CALLBACK_MEMBER(timer_irq);
	uint8_t keyb_data_r();

	void agat9_map(address_map &map) ATTR_COLD;
	void inhbank_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint8_t c090_r(offs_t offset);
	void c090_w(offs_t offset, uint8_t data);
	uint8_t c200_r(offs_t offset);
	void c200_w(offs_t offset, uint8_t data);
	void apple_w(offs_t offset, uint8_t data);

	uint8_t agat9_membank_r(offs_t offset);
	void agat9_membank_w(offs_t offset, uint8_t data);
	uint8_t agat9_upperbank_r(offs_t offset);
	void agat9_upperbank_w(offs_t offset, uint8_t data);

private:
	int m_agat9_membank[16]{}; // 8 physical banks, but ram chip has 16 locations
	int m_agat9_upperbank = 0;
	int m_agat9_lcbank = 0;
	bool m_agat9_prewrite = false;
	bool m_apple = false;
};

/***************************************************************************
    PARAMETERS
***************************************************************************/

#define JOYSTICK_DELTA          80
#define JOYSTICK_SENSITIVITY    50
#define JOYSTICK_AUTOCENTER     80

void agat_base_state::a2bus_irq_w(int state)
{
	m_maincpu->set_input_line(M6502_IRQ_LINE, state);
}

void agat_base_state::a2bus_nmi_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, state);
}

// This code makes a ton of assumptions because we can guarantee a pre-IIe machine!
void agat_base_state::a2bus_inh_w(int state)
{
	if (state == ASSERT_LINE)
	{
		// assume no cards are pulling /INH
		m_inh_slot = -1;
		m_agat7_ram_slot = -1;

		// scan the slots to figure out which card(s) are INHibiting stuff
		for (int i = 0; i <= 7; i++)
		{
			if (m_slotdevice[i])
			{
				// this driver only can inhibit from 0xd000-0xffff
				if ((m_slotdevice[i]->inh_start() == 0xd000) &&
					(m_slotdevice[i]->inh_end() == 0xffff))
				{
					if ((m_slotdevice[i]->inh_type() & INH_READ) == INH_READ)
					{
						if (m_inh_bank != 1)
						{
							m_upperbank->set_bank(1);
							m_inh_bank = 1;
						}
					}
					else
					{
						if (m_inh_bank != 0)
						{
							m_upperbank->set_bank(0);
							m_inh_bank = 0;
						}
					}

					m_inh_slot = i;
					// break;
				}

				if ((m_slotdevice[i]->inh_start() == 0x8000) &&
					(m_slotdevice[i]->inh_end() == 0xbfff))
				{
					if ((m_slotdevice[i]->inh_type() & INH_READ) == INH_READ)
					{
						m_agat7_ram_slot = i;
					}
				}
			}
		}

		// if no slots are inhibiting, make sure ROM is fully switched in
		if ((m_inh_slot == -1) && (m_inh_bank != 0))
		{
			m_upperbank->set_bank(0);
			m_inh_bank = 0;
		}
	}
}

/***************************************************************************
    START/RESET
***************************************************************************/

void agat_base_state::machine_start()
{
	m_ram_ptr = m_ram->pointer();
	m_ram_size = m_ram->size();
	m_speaker_state = 0;
	m_speaker->level_w(m_speaker_state);
	m_cassette_state = 0;
	m_cassette->output(-1.0f);
	m_inh_bank = 0;
	m_transchar = 0;
	m_strobe = 0;
	m_meta = false;

	// precalculate joystick time constants
	m_x_calibration = attotime::from_usec(12).as_double();
	m_y_calibration = attotime::from_usec(13).as_double();

	// cache slot devices
	for (int i = 0; i <= 7; i++)
	{
		m_slotdevice[i] = m_a2bus->get_a2bus_card(i);
	}

	m_inh_slot = -1;
	m_cnxx_slot = -1;
	m_agat7_ram_slot = -1;

	// setup save states
	save_item(NAME(m_speaker_state));
	save_item(NAME(m_cassette_state));
	save_item(NAME(m_joystick_x1_time));
	save_item(NAME(m_joystick_y1_time));
	save_item(NAME(m_joystick_x2_time));
	save_item(NAME(m_joystick_y2_time));
	save_item(NAME(m_strobe));
	save_item(NAME(m_transchar));
	save_item(NAME(m_inh_slot));
	save_item(NAME(m_inh_bank));
	save_item(NAME(m_cnxx_slot));
}

void agat7_state::machine_start()
{
	agat_base_state::machine_start();

	m_upperbank->set_bank(0);
}

void agat9_state::machine_start()
{
	agat_base_state::machine_start();
	logerror("start apple %d\n", m_apple);

	m_upperbank->set_bank(1);
	m_agat9_upperbank = 0;
	m_agat9_lcbank = 0;
	m_agat9_prewrite = false;
	// reset only on power on
	m_apple = false;
}


void agat_base_state::machine_reset()
{
	m_agat_interrupts = false;
}

void agat7_state::machine_reset()
{
	agat_base_state::machine_reset();

	m_agat7_membank = 0;
	m_upperbank->set_bank(0);
}

void agat9_state::machine_reset()
{
	agat_base_state::machine_reset();
	logerror("reset apple %d\n", m_apple);

	if (!m_apple)
	{
		m_agat9_membank[0] = 0x00;
		m_agat9_membank[1] = 0x11;
		m_agat9_membank[2] = 0x22;
		m_agat9_membank[3] = 0x33;
		m_agat9_membank[4] = 0x44;
		m_agat9_membank[5] = 0x55;
		m_agat9_membank[6] = 0x66;
		m_agat9_membank[7] = 0x77;

		membank("bank0")->set_base(m_ram_ptr + 0x0000);
		membank("bank1")->set_base(m_ram_ptr + 0x2000);
		membank("bank2")->set_base(m_ram_ptr + 0x4000);
		membank("bank3")->set_base(m_ram_ptr + 0x6000);
		membank("bank4")->set_base(m_ram_ptr + 0x8000);
		membank("bank5")->set_base(m_ram_ptr + 0xa000);
		membank("bank6")->set_base(m_ram_ptr + 0xc000);
		membank("bank6a")->set_base(m_ram_ptr + 0xc000);
		membank("bank7")->set_base(m_ram_ptr + 0xe000);
		membank("bank7a")->set_base(m_ram_ptr + 0xe000);

		m_upperbank->set_bank(1);
	}
}

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

void agat_base_state::kbd_put(u8 data)
{
	if (!m_strobe)
	{
		m_transchar = data;
		m_strobe = 0x80;
	}
}

void agat_base_state::kbd_meta(int state)
{
	m_meta = state;
}

uint8_t agat7_state::keyb_data_r()
{
	return m_strobe ? (m_transchar | m_strobe) : 0;
}

uint8_t agat9_state::keyb_data_r()
{
	return m_transchar | m_strobe;
}

uint8_t agat_base_state::keyb_strobe_r()
{
	// reads any key down, clears strobe
	uint8_t rv = m_strobe;
	if (!machine().side_effects_disabled())
		m_strobe = 0;
	return rv;
}

void agat_base_state::keyb_strobe_w(uint8_t data)
{
	// clear keyboard latch
	m_strobe = 0;
}

uint8_t agat_base_state::cassette_toggle_r()
{
	if (!machine().side_effects_disabled())
		cassette_toggle_w(0);
	return read_floatingbus();
}

void agat_base_state::cassette_toggle_w(uint8_t data)
{
	m_cassette_state ^= 1;
	m_cassette->output(m_cassette_state ? 1.0f : -1.0f);
}

uint8_t agat_base_state::speaker_toggle_r()
{
	if (!machine().side_effects_disabled())
		speaker_toggle_w(0);
	return read_floatingbus();
}

void agat_base_state::speaker_toggle_w(uint8_t data)
{
	m_speaker_state ^= 1;
	m_speaker->level_w(m_speaker_state);
}

uint8_t agat_base_state::interrupts_on_r()
{
	if (!machine().side_effects_disabled())
		interrupts_on_w(0);
	return read_floatingbus();
}

void agat_base_state::interrupts_on_w(uint8_t data)
{
	m_agat_interrupts = true;
}

uint8_t agat_base_state::interrupts_off_r()
{
	if (!machine().side_effects_disabled())
		interrupts_off_w(0);
	return read_floatingbus();
}

void agat_base_state::interrupts_off_w(uint8_t data)
{
	m_agat_interrupts = false;
}

uint8_t agat_base_state::flags_r(offs_t offset)
{
	switch (offset)
	{
	case 0: // cassette in
		return m_cassette->input() > 0.0 ? 0x80 : 0;

	case 1: // button 0
		return (m_joybuttons->read() & 0x10) ? 0x80 : 0;

	case 2: // button 1
		return (m_joybuttons->read() & 0x20) ? 0x80 : 0;

	case 3: // meta key
		return m_meta ? 0 : 0x80;

	case 4: // joy 1 X axis
		return (machine().time().as_double() < m_joystick_x1_time) ? 0x80 : 0;

	case 5: // joy 1 Y axis
		return (machine().time().as_double() < m_joystick_y1_time) ? 0x80 : 0;

	case 6: // joy 2 X axis
		return (machine().time().as_double() < m_joystick_x2_time) ? 0x80 : 0;

	case 7: // joy 2 Y axis
		return (machine().time().as_double() < m_joystick_y2_time) ? 0x80 : 0;
	}

	// this is never reached
	return 0;
}

uint8_t agat_base_state::controller_strobe_r()
{
	if (!machine().side_effects_disabled())
		controller_strobe_w(0);
	return read_floatingbus();
}

void agat_base_state::controller_strobe_w(uint8_t data)
{
	// 555 monostable one-shot timers; a running timer cannot be restarted
	if (machine().time().as_double() >= m_joystick_x1_time)
	{
		m_joystick_x1_time = machine().time().as_double() + m_x_calibration * m_joy1x->read();
	}
	if (machine().time().as_double() >= m_joystick_y1_time)
	{
		m_joystick_y1_time = machine().time().as_double() + m_y_calibration * m_joy1y->read();
	}
	if (machine().time().as_double() >= m_joystick_x2_time)
	{
		m_joystick_x2_time = machine().time().as_double() + m_x_calibration * m_joy2x->read();
	}
	if (machine().time().as_double() >= m_joystick_y2_time)
	{
		m_joystick_y2_time = machine().time().as_double() + m_y_calibration * m_joy2y->read();
	}

}

uint8_t agat_base_state::c080_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
	{
		int slot;

		offset &= 0x7F;
		slot = offset / 0x10;

		if (m_slotdevice[slot] != nullptr)
		{
			u8 data = m_slotdevice[slot]->read_c0nx(offset % 0x10);
			logerror("%s: c080_r %04X (slot %d) == %02X\n", machine().describe_context(), offset + 0xc080, slot, data);
			return data;
		}

	}

	return read_floatingbus();
}

void agat_base_state::c080_w(offs_t offset, uint8_t data)
{
	int slot;

	offset &= 0x7F;
	slot = offset / 0x10;

	logerror("%s: c080_w %04X (slot %d) <- %02X\n", machine().describe_context(), offset + 0xc080, slot, data);

	if (m_slotdevice[slot] != nullptr)
	{
		m_slotdevice[slot]->write_c0nx(offset % 0x10, data);
	}
}

uint8_t agat_base_state::c100_r(offs_t offset)
{
	int slotnum;

	slotnum = ((offset >> 8) & 0xf) + 1;

	if (m_slotdevice[slotnum] != nullptr)
	{
//      u8 data;

		if ((m_slotdevice[slotnum]->take_c800()) && (!machine().side_effects_disabled()))
		{
			m_cnxx_slot = slotnum;
		}

//      logerror("%s: c100_r %04X (slot %d) == %02X\n", machine().describe_context(), offset+0xc100, slotnum, data);

		return m_slotdevice[slotnum]->read_cnxx(offset & 0xff);
	}

	return read_floatingbus();
}

void agat_base_state::c100_w(offs_t offset, uint8_t data)
{
	int slotnum;

	slotnum = ((offset >> 8) & 0xf) + 1;

	logerror("%s: c100_w %04X (slot %d) <- %02X\n", machine().describe_context(), offset + 0xc100, slotnum, data);

	if (m_slotdevice[slotnum] != nullptr)
	{
		if ((m_slotdevice[slotnum]->take_c800()) && (!machine().side_effects_disabled()))
		{
			m_cnxx_slot = slotnum;
		}

		m_slotdevice[slotnum]->write_cnxx(offset & 0xff, data);
	}
}

uint8_t agat9_state::c090_r(offs_t offset)
{
	return c080_r(offset + 0x10);
}

void agat9_state::c090_w(offs_t offset, uint8_t data)
{
	c080_w(offset + 0x10, data);
}

uint8_t agat9_state::c200_r(offs_t offset)
{
	return c100_r(offset + 0x0100);
}

void agat9_state::c200_w(offs_t offset, uint8_t data)
{
	c100_w(offset + 0x0100, data);
}

uint8_t agat_base_state::c800_r(offs_t offset)
{
	if (offset == 0x7ff)
	{
		if (!machine().side_effects_disabled())
		{
			m_cnxx_slot = -1;
		}

		return 0xff;
	}

	if ((m_cnxx_slot != -1) && (m_slotdevice[m_cnxx_slot] != nullptr))
	{
		return m_slotdevice[m_cnxx_slot]->read_c800(offset & 0xfff);
	}

	return read_floatingbus();
}

void agat_base_state::c800_w(offs_t offset, uint8_t data)
{
	if (offset == 0x7ff)
	{
		if (!machine().side_effects_disabled())
		{
			m_cnxx_slot = -1;
		}

		return;
	}

	if ((m_cnxx_slot != -1) && (m_slotdevice[m_cnxx_slot] != nullptr))
	{
		m_slotdevice[m_cnxx_slot]->write_c800(offset & 0xfff, data);
	}
}

void agat9_state::apple_w(offs_t offset, uint8_t data)
{
	logerror("%s: c0f0_w %04X <- %02X (apple mode set)\n", machine().describe_context(), offset + 0xc0f0, data);

	m_apple = true;
	m_upperbank->set_bank(5); // FIXME: get current bank
}

uint8_t agat_base_state::inh_r(offs_t offset)
{
	if (m_inh_slot != -1)
	{
		return m_slotdevice[m_inh_slot]->read_inh_rom(offset + 0xd000);
	}

	assert(0); // hitting inh_r with invalid m_inh_slot should not be possible
	return read_floatingbus();
}

void agat_base_state::inh_w(offs_t offset, uint8_t data)
{
	if (m_inh_slot != -1)
	{
		m_slotdevice[m_inh_slot]->write_inh_rom(offset + 0xd000, data);
	}
}

uint8_t agat_base_state::read_floatingbus()
{
	return 0xff;
}

/***************************************************************************
    ADDRESS MAP
***************************************************************************/

/* onboard memory banking on Agat-7 */

uint8_t agat_base_state::agat7_membank_r(offs_t offset)
{
	logerror("%s: c0f0_r %04X == %02X\n", machine().describe_context(), offset + 0xc0f0, 0xff);

	if (!machine().side_effects_disabled())
	{
		m_agat7_membank = offset;
	}

	return 0xff;
}

void agat_base_state::agat7_membank_w(offs_t offset, uint8_t data)
{
	logerror("%s: c0f0_w %04X <- %02X\n", machine().describe_context(), offset + 0xc0f0, data);

	m_agat7_membank = offset;
}

uint8_t agat_base_state::agat7_ram_r(offs_t offset)
{
	if (offset < 32768)
	{
		if (offset < m_ram_size)
		{
			return m_ram_ptr[offset];
		}
	}
	else
	{
		if (m_agat7_ram_slot != -1)
			return m_slotdevice[m_agat7_ram_slot]->read_inh_rom(offset + 0x8000);
		if (offset < m_ram_size)
		{
			if (m_agat7_membank == 0)
				return m_ram_ptr[offset];
			else if (m_agat7_membank == 1)
				return m_ram_ptr[offset + 16384];
		}
	}

	return 0xff;
}

void agat_base_state::agat7_ram_w(offs_t offset, uint8_t data)
{
	if (offset < 32768)
	{
		if (offset < m_ram_size)
		{
			m_ram_ptr[offset] = data;
		}
	}
	else
	{
		if (m_agat7_ram_slot != -1)
			m_slotdevice[m_agat7_ram_slot]->write_inh_rom(offset + 0x8000, data);
		else if (offset < m_ram_size)
		{
			if (m_agat7_membank == 0)
				m_ram_ptr[offset] = data;
			else if (m_agat7_membank == 1)
				m_ram_ptr[offset + 16384] = data;
		}
	}
}

/*
 * onboard memory banking on Agat-9
 *
 * native mode: writes do the bankswitch, reads return current mapping mode
 * apple2 mode: writes ignored ??, reads do language card emulation
 * and return current mapping mode
 */

uint8_t agat9_state::agat9_upperbank_r(offs_t offset)
{
	if (machine().side_effects_disabled())
	{
		return read_floatingbus();
	}

	if (!m_apple)
	{
		logerror("%s: c080_r %04X == %02X\n", machine().describe_context(), 0xc080 + offset, m_agat9_upperbank);

		return m_agat9_upperbank;
	}

	int rc;

#if 0
	if (BIT(offset, 2)) // select physical memory banks for LC emulation
	{
		int newbank = bitswap<8>(offset,7,6,5,4,3,1,0,2) & 14;
		rc = newbank == m_agat9_lcbank ? (0xc0 + m_agat9_upperbank) : 0xc0;
		m_agat9_lcbank = newbank;

		membank("bank6")->set_base(m_ram_ptr + (newbank * 8192) + (BIT(m_agat9_upperbank, 3) * 4096));
		membank("bank7")->set_base(m_ram_ptr + (newbank + 1) * 8192);

		logerror("%s: c080_r %04X == %02X: select new map %d (mode %d d000 %s)\n", machine().describe_context(), offset + 0xc080,
			newbank, 0xc0 + m_agat9_upperbank, m_agat9_upperbank & 8 ? "upper" : "lower");
	}
	else // select mapping modes
#endif
	{
		m_agat9_upperbank = BIT(offset, 0) ? offset : (offset ^ 2);
		rc = 0xc0 + m_agat9_upperbank;

		// taken from ramcard16k.cpp
		if (BIT(offset, 0))
		{
			if (m_agat9_prewrite == false)
			{
				m_agat9_prewrite = true;
				m_upperbank->set_bank(4 + ((offset + 1) & 3)); // writes disabled
			}
			else
			{
				m_upperbank->set_bank(4 + (offset & 3));
			}
		}
		else
		{
			m_agat9_prewrite = false;
			m_upperbank->set_bank(4 + (offset & 3));
		}

		membank("bank6a")->set_base(m_ram_ptr + (m_agat9_membank[14] & 15) * 8192 + BIT(m_agat9_upperbank, 3) * 4096);

		logerror("%s: c080_r %04X == %02X: select new mode %d (map %d d000 %s)\n", machine().describe_context(), offset + 0xc080,
			rc, offset & 3, m_agat9_lcbank, m_agat9_upperbank & 8 ? "upper" : "lower");
	}

	return rc;
}

void agat9_state::agat9_upperbank_w(offs_t offset, uint8_t data)
{
	logerror("%s: c080_w %04X <- %02X: select new mode %d (d000 %s)\n", machine().describe_context(), offset + 0xc080, data,
		offset & 3, offset & 8 ? "upper" : "lower");

	if (m_apple)
	{
		m_agat9_prewrite = false;
	}
	else
	{
		m_upperbank->set_bank(offset & 3);
		m_agat9_upperbank = (offset & 3) ? (offset & 0xb) : (2 | (offset & 0xb));
		m_agat9_upperbank |= 0x80; // ikp2-19176.aim passes

		membank("bank6")->set_base(m_ram_ptr + (m_agat9_membank[6] & 15) * 8192 + BIT(m_agat9_upperbank, 3) * 4096);
	}
}

uint8_t agat9_state::agat9_membank_r(offs_t offset)
{
	u8 data = 0;

	data = m_agat9_membank[(offset >> 4)];

	logerror("%s: c100_r %04X == %02X\n", machine().describe_context(), offset + 0xc100, data);

	return data;
}

void agat9_state::agat9_membank_w(offs_t offset, uint8_t data)
{
	logerror("%s: c100_w %04X <- %02X: bank %d va %04X = page %02d pa %05X (apple %d)\n", machine().describe_context(), offset + 0xc100, data,
		(offset >> 4), 0x2000 * (offset >> 4), (offset & 15), (offset & 15) * 8192, m_apple);

	m_agat9_membank[(offset >> 4)] = offset;

	if (m_apple)
		return;

	int newbank = (offset & 15) * 8192;

	switch (offset >> 4)
	{
	case 0:
		membank("bank0")->set_base(m_ram_ptr + newbank);
		break;

	case 1:
		membank("bank1")->set_base(m_ram_ptr + newbank);
		break;

	case 2:
		membank("bank2")->set_base(m_ram_ptr + newbank);
		break;

	case 3:
		membank("bank3")->set_base(m_ram_ptr + newbank);
		break;

	case 4:
		membank("bank4")->set_base(m_ram_ptr + newbank);
		break;

	case 5:
		membank("bank5")->set_base(m_ram_ptr + newbank);
		break;

	case 6:
		membank("bank6")->set_base(m_ram_ptr + newbank + (BIT(m_agat9_upperbank, 3) * 4096));
		break;

	case 7:
		membank("bank7")->set_base(m_ram_ptr + newbank);
		break;

	case 14:
		membank("bank6a")->set_base(m_ram_ptr + newbank + (BIT(m_agat9_upperbank, 3) * 4096));
		break;

	case 15:
		membank("bank7a")->set_base(m_ram_ptr + newbank);
		break;

	default:
		logerror("membank %d UNIMP\n", (offset >> 4));
		break;
	}
}


/*
 * agat7: onboard RAM is at least 32K, up to 128K.
 * first 32K of onboard RAM are always mapped at 0.
 * standard add-on RAM cards hold 32K (possibly up to 128K?)
 * and are supported only on motherboards with 32K onboard.
 * all extra RAM (onboard or addon) is accessible via 16K window at 0x8000.
 */
void agat7_state::agat7_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xbfff).rw(FUNC(agat7_state::agat7_ram_r), FUNC(agat7_state::agat7_ram_w));
	map(0xc000, 0xc000).mirror(0xf).r(FUNC(agat7_state::keyb_data_r)).nopw();
	map(0xc010, 0xc010).mirror(0xf).rw(FUNC(agat7_state::keyb_strobe_r), FUNC(agat7_state::keyb_strobe_w));
	map(0xc020, 0xc020).mirror(0xf).rw(FUNC(agat7_state::cassette_toggle_r), FUNC(agat7_state::cassette_toggle_w));
	map(0xc030, 0xc030).mirror(0xf).rw(FUNC(agat7_state::speaker_toggle_r), FUNC(agat7_state::speaker_toggle_w));
	map(0xc040, 0xc040).mirror(0xf).rw(FUNC(agat7_state::interrupts_on_r), FUNC(agat7_state::interrupts_on_w));
	map(0xc050, 0xc050).mirror(0xf).rw(FUNC(agat7_state::interrupts_off_r), FUNC(agat7_state::interrupts_off_w));
	map(0xc060, 0xc067).mirror(0x8).r(FUNC(agat7_state::flags_r)).nopw();
	map(0xc070, 0xc070).mirror(0xf).rw(FUNC(agat7_state::controller_strobe_r), FUNC(agat7_state::controller_strobe_w));
	map(0xc080, 0xc0ef).rw(FUNC(agat7_state::c080_r), FUNC(agat7_state::c080_w));
	map(0xc0f0, 0xc0ff).rw(FUNC(agat7_state::agat7_membank_r), FUNC(agat7_state::agat7_membank_w));
	map(0xc100, 0xc6ff).rw(FUNC(agat7_state::c100_r), FUNC(agat7_state::c100_w));
	map(0xc700, 0xc7ff).rw(A7_VIDEO_TAG, FUNC(agat7video_device::read), FUNC(agat7video_device::write));
	map(0xc800, 0xcfff).rw(FUNC(agat7_state::c800_r), FUNC(agat7_state::c800_w));
	map(0xd000, 0xffff).m(m_upperbank, FUNC(address_map_bank_device::amap8));
}

void agat7_state::inhbank_map(address_map &map)
{
	map(0x0000, 0x2fff).rom().region("maincpu", 0x1000).w(FUNC(agat7_state::inh_w));
	map(0x3000, 0x5fff).rw(FUNC(agat7_state::inh_r), FUNC(agat7_state::inh_w));
}

/*
 * agat9: onboard RAM is always 128K.  language card emulation is built in.
 * standard add-on RAM cards hold 128K, up to 4 cards are supported.
 * both onboard and add-on RAM is split into 16K banks.
 */
void agat9_state::agat9_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).bankrw("bank0");
	map(0x2000, 0x3fff).bankrw("bank1");
	map(0x4000, 0x5fff).bankrw("bank2");
	map(0x6000, 0x7fff).bankrw("bank3");
	map(0x8000, 0x9fff).bankrw("bank4");
	map(0xa000, 0xbfff).bankrw("bank5");
	map(0xc000, 0xc000).mirror(0xf).r(FUNC(agat9_state::keyb_data_r)).nopw();
	map(0xc010, 0xc010).mirror(0xf).rw(FUNC(agat9_state::keyb_strobe_r), FUNC(agat9_state::keyb_strobe_w));
	map(0xc020, 0xc020).mirror(0xf).rw(FUNC(agat9_state::interrupts_off_r), FUNC(agat9_state::interrupts_off_w));
	map(0xc030, 0xc030).mirror(0xf).rw(FUNC(agat9_state::speaker_toggle_r), FUNC(agat9_state::speaker_toggle_w));
	map(0xc040, 0xc040).mirror(0xf).rw(FUNC(agat9_state::interrupts_on_r), FUNC(agat9_state::interrupts_on_w));
	map(0xc050, 0xc05f).rw(A9_VIDEO_TAG, FUNC(agat9video_device::apple_read), FUNC(agat9video_device::apple_write));
	map(0xc060, 0xc067).mirror(0x8).r(FUNC(agat9_state::flags_r)).nopw();
	map(0xc070, 0xc070).mirror(0xf).rw(FUNC(agat9_state::controller_strobe_r), FUNC(agat9_state::controller_strobe_w));
	map(0xc080, 0xc08f).rw(FUNC(agat9_state::agat9_upperbank_r), FUNC(agat9_state::agat9_upperbank_w));
	map(0xc090, 0xc0ef).rw(FUNC(agat9_state::c090_r), FUNC(agat9_state::c090_w));
	map(0xc0f0, 0xc0ff).w(FUNC(agat9_state::apple_w));
	map(0xc100, 0xc1ff).rw(FUNC(agat9_state::agat9_membank_r), FUNC(agat9_state::agat9_membank_w));
	map(0xc200, 0xc6ff).rw(FUNC(agat9_state::c200_r), FUNC(agat9_state::c200_w));
	map(0xc700, 0xc7ff).rw(A9_VIDEO_TAG, FUNC(agat9video_device::read), FUNC(agat9video_device::write));
	map(0xc800, 0xcfff).rw(FUNC(agat9_state::c800_r), FUNC(agat9_state::c800_w));
	map(0xd000, 0xffff).m(m_upperbank, FUNC(address_map_bank_device::amap8));
}

void agat9_state::inhbank_map(address_map &map)
{
	// native mode

	// map 0 -- C080/C088
	map(0x00000, 0x00fff).bankr("bank6").nopw();
	map(0x01000, 0x02fff).bankr("bank7").nopw();

	// map 1 -- C081/C089
	map(0x10000, 0x10fff).rom().region("maincpu", 0x1000).bankw("bank6");
	map(0x11000, 0x12fff).rom().region("maincpu", 0x2000).bankw("bank7");

	// map 2 -- C082/C08A -- only in apple mode (same as map 0 in agat mode)
	map(0x20000, 0x20fff).bankr("bank6").nopw();
	map(0x21000, 0x22fff).bankr("bank7").nopw();

	// map 3 -- C083/C08B
	map(0x30000, 0x30fff).bankrw("bank6");
	map(0x31000, 0x32fff).bankrw("bank7");

	// apple mode -- ROM is simulated by onboard RAM (logical banks 14 and 15)
	// language card emulation can use onboard or add-on card memory

	// map 0 -- C080/C088 -- rc C2/CA -- bank_r mapped to LC area
	map(0x40000, 0x40fff).bankr("bank6a").nopw();
	map(0x41000, 0x42fff).bankr("bank7a").nopw();

	// map 1 -- C081/C089 -- rc C1/C9 -- bank_r mapped to apple ROM area, bank_w to LC area
	map(0x50000, 0x50fff).bankr("bank6").bankw("bank6a");
	map(0x51000, 0x52fff).bankr("bank7").bankw("bank7a");

	// map 2 -- C082/C08A -- rc C0/C8 -- bank_r mapped to apple ROM area
	map(0x60000, 0x60fff).bankr("bank6").nopw();
	map(0x61000, 0x62fff).bankr("bank7").nopw();

	// map 3 -- C083/C08B -- rc C3/CB -- banks mapped to LC area
	map(0x70000, 0x70fff).bankrw("bank6a");
	map(0x71000, 0x72fff).bankrw("bank7a");
}

/***************************************************************************
    KEYBOARD
***************************************************************************/

INTERRUPT_GEN_MEMBER(agat_base_state::agat_vblank)
{
	if (m_agat_interrupts)
	{
		m_maincpu->pulse_input_line(M6502_NMI_LINE, attotime::zero);
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(agat7_state::timer_irq)
{
	if (m_agat_interrupts)
	{
		switch (param & 0x3f)
		{
		case 0:
			m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE);
			break;

		case 0x20:
			m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE);
			break;
		}
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(agat9_state::timer_irq)
{
	if (m_agat_interrupts)
	{
		switch (param & 0xf)
		{
		case 0:
			m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE);
			break;

		case 8:
			m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE);
			break;
		}
	}
}

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

static INPUT_PORTS_START( agat7_joystick )
	PORT_START("joystick_1_x")      /* Joystick 1 X Axis */
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_X) PORT_NAME("P1 Joystick X")
	PORT_SENSITIVITY(JOYSTICK_SENSITIVITY)
	PORT_KEYDELTA(JOYSTICK_DELTA)
	PORT_CENTERDELTA(JOYSTICK_AUTOCENTER)
	PORT_MINMAX(0,0xff) PORT_PLAYER(1)
	PORT_CODE_DEC(KEYCODE_4_PAD)    PORT_CODE_INC(KEYCODE_6_PAD)
	PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH)    PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH)

	PORT_START("joystick_1_y")      /* Joystick 1 Y Axis */
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_Y) PORT_NAME("P1 Joystick Y")
	PORT_SENSITIVITY(JOYSTICK_SENSITIVITY)
	PORT_KEYDELTA(JOYSTICK_DELTA)
	PORT_CENTERDELTA(JOYSTICK_AUTOCENTER)
	PORT_MINMAX(0,0xff) PORT_PLAYER(1)
	PORT_CODE_DEC(KEYCODE_8_PAD)    PORT_CODE_INC(KEYCODE_2_PAD)
	PORT_CODE_DEC(JOYCODE_Y_UP_SWITCH)      PORT_CODE_INC(JOYCODE_Y_DOWN_SWITCH)

	PORT_START("joystick_2_x")      /* Joystick 2 X Axis */
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_X) PORT_NAME("P2 Joystick X")
	PORT_SENSITIVITY(JOYSTICK_SENSITIVITY)
	PORT_KEYDELTA(JOYSTICK_DELTA)
	PORT_CENTERDELTA(JOYSTICK_AUTOCENTER)
	PORT_MINMAX(0,0xff) PORT_PLAYER(2)
	PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH)    PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH)

	PORT_START("joystick_2_y")      /* Joystick 2 Y Axis */
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_Y) PORT_NAME("P2 Joystick Y")
	PORT_SENSITIVITY(JOYSTICK_SENSITIVITY)
	PORT_KEYDELTA(JOYSTICK_DELTA)
	PORT_CENTERDELTA(JOYSTICK_AUTOCENTER)
	PORT_MINMAX(0,0xff) PORT_PLAYER(2)
	PORT_CODE_DEC(JOYCODE_Y_UP_SWITCH)      PORT_CODE_INC(JOYCODE_Y_DOWN_SWITCH)

	PORT_START("joystick_buttons")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1)  PORT_PLAYER(1)            PORT_CODE(KEYCODE_0_PAD)    PORT_CODE(JOYCODE_BUTTON1) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON2)  PORT_PLAYER(1)            PORT_CODE(KEYCODE_ENTER_PAD)PORT_CODE(JOYCODE_BUTTON2) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON1)  PORT_PLAYER(2)            PORT_CODE(JOYCODE_BUTTON1)
INPUT_PORTS_END

static INPUT_PORTS_START(agat7)
	/* other devices */
	PORT_INCLUDE(agat7_joystick)
INPUT_PORTS_END

static void agat7_cards(device_slot_interface &device)
{
	device.option_add("a7lang", A2BUS_AGAT7LANGCARD); // Agat-7 RAM Language Card -- decimal 3.089.119
	device.option_add("a7ram", A2BUS_AGAT7RAM); // Agat-7 32K RAM Card -- decimal 3.089.119-01, KR565RU6D chips
	device.option_add("a7fdc", A2BUS_AGAT7_FDC); // Disk II clone -- decimal 3.089.105
	device.option_add("a9fdchle", A2BUS_AGAT840K_HLE); // 840K floppy controller -- decimal 7.104.351 or 3.089.023?
	device.option_add("a9fdc", A2BUS_AGAT_FDC); // 840K floppy controller LLE
	device.option_add("a7ports", A2BUS_AGAT7_PORTS); // Serial-parallel card -- decimal 3.089.106
}

static void agat9_cards(device_slot_interface &device)
{
//  device.option_add("a9ram", A2BUS_AGAT9RAM); // Agat-9 128K RAM Card -- decimal 3.089.170
	device.option_add("diskiing", A2BUS_DISKIING);  /* Disk II Controller Card */
	device.option_add("a9fdc140", A2BUS_AGAT9_FDC); // Disk II clone -- decimal 3.089.173 (reworked for agat9)
	device.option_add("a9fdchle", A2BUS_AGAT840K_HLE); // 840K floppy controller -- decimal 7.104.351 or 3.089.023?
	device.option_add("a9fdc", A2BUS_AGAT_FDC); // 840K floppy controller LLE
	device.option_add("a7ports", A2BUS_AGAT7_PORTS); // Serial-parallel card -- decimal 3.089.106
	device.option_add("nclock", A2BUS_NIPPELCLOCK); // Nippel Clock (mc146818)
}

void agat7_state::agat7(machine_config &config)
{
	M6502(config, m_maincpu, XTAL(14'300'000) / 14);
	m_maincpu->set_addrmap(AS_PROGRAM, &agat7_state::agat7_map);
	m_maincpu->set_vblank_int(A7_VIDEO_TAG ":a7screen", FUNC(agat_base_state::agat_vblank));

	TIMER(config, "scantimer").configure_scanline(FUNC(agat7_state::timer_irq), A7_VIDEO_TAG ":a7screen", 0, 1);

	AGAT7VIDEO(config, m_video, RAM_TAG, "gfx1");

	RAM(config, m_ram).set_default_size("32K").set_default_value(0);//.set_extra_options("64K,128K");

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);

	/* /INH banking */
	ADDRESS_MAP_BANK(config, m_upperbank).set_map(&agat7_state::inhbank_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x3000);

	agat_keyboard_device &keyboard(AGAT_KEYBOARD(config, "keyboard", 0));
	keyboard.out_callback().set(FUNC(agat_base_state::kbd_put));
	keyboard.out_meta_callback().set(FUNC(agat_base_state::kbd_meta));
	keyboard.out_reset_callback().set([this](bool state) { m_maincpu->reset(); });

	/*
	 * slot 0 is reserved for SECAM encoder or Apple II compat card.
	 * slot 1 always holds the CPU card.
	 * most of the software expects a7lang in slot 2 and a7ram in slot 6.
	 */
	A2BUS(config, m_a2bus, 0);
	m_a2bus->set_space(m_maincpu, AS_PROGRAM);
	m_a2bus->irq_w().set(FUNC(agat_base_state::a2bus_irq_w));
	m_a2bus->nmi_w().set(FUNC(agat_base_state::a2bus_nmi_w));
	m_a2bus->inh_w().set(FUNC(agat_base_state::a2bus_inh_w));
	m_a2bus->dma_w().set_inputline(m_maincpu, INPUT_LINE_HALT);
	A2BUS_SLOT(config, "sl2", XTAL(14'300'000) / 2, m_a2bus, agat7_cards, "a7lang");
	A2BUS_SLOT(config, "sl3", XTAL(14'300'000) / 2, m_a2bus, agat7_cards, "a7fdc");
	A2BUS_SLOT(config, "sl4", XTAL(14'300'000) / 2, m_a2bus, agat7_cards, "a7ports");
	A2BUS_SLOT(config, "sl5", XTAL(14'300'000) / 2, m_a2bus, agat7_cards, nullptr);
	A2BUS_SLOT(config, "sl6", XTAL(14'300'000) / 2, m_a2bus, agat7_cards, "a7ram");

	CASSETTE(config,m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
}

void agat9_state::agat9(machine_config &config)
{
	R65C02(config, m_maincpu, XTAL(14'300'000) / 14);
	m_maincpu->set_addrmap(AS_PROGRAM, &agat9_state::agat9_map);
	m_maincpu->set_vblank_int(A9_VIDEO_TAG ":a9screen", FUNC(agat_base_state::agat_vblank));

	TIMER(config, "scantimer").configure_scanline(FUNC(agat9_state::timer_irq), A9_VIDEO_TAG ":a9screen", 0, 1);

	AGAT9VIDEO(config, m_video, RAM_TAG, "gfx1");

	RAM(config, m_ram).set_default_size("128K").set_default_value(0);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);

	/* /INH banking */
	ADDRESS_MAP_BANK(config, m_upperbank).set_map(&agat9_state::inhbank_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x10000);

	agat_keyboard_device &keyboard(AGAT_KEYBOARD(config, "keyboard", 0));
	keyboard.out_callback().set(FUNC(agat_base_state::kbd_put));
	keyboard.out_meta_callback().set(FUNC(agat_base_state::kbd_meta));
	keyboard.out_reset_callback().set([this](bool state) { m_maincpu->reset(); });

	A2BUS(config, m_a2bus, 0);
	m_a2bus->set_space(m_maincpu, AS_PROGRAM);
	m_a2bus->irq_w().set(FUNC(agat_base_state::a2bus_irq_w));
	m_a2bus->nmi_w().set(FUNC(agat_base_state::a2bus_nmi_w));
	m_a2bus->inh_w().set(FUNC(agat_base_state::a2bus_inh_w));
	m_a2bus->dma_w().set_inputline(m_maincpu, INPUT_LINE_HALT);
	// slot 0 does not exist
	A2BUS_SLOT(config, "sl1", XTAL(14'300'000) / 2, m_a2bus, agat9_cards, nullptr);
	A2BUS_SLOT(config, "sl2", XTAL(14'300'000) / 2, m_a2bus, agat9_cards, nullptr); // a9ram
	A2BUS_SLOT(config, "sl3", XTAL(14'300'000) / 2, m_a2bus, agat9_cards, nullptr); // printer->mouse
	A2BUS_SLOT(config, "sl4", XTAL(14'300'000) / 2, m_a2bus, agat9_cards, nullptr); // printer
	A2BUS_SLOT(config, "sl5", XTAL(14'300'000) / 2, m_a2bus, agat9_cards, "a9fdc");
	A2BUS_SLOT(config, "sl6", XTAL(14'300'000) / 2, m_a2bus, agat9_cards, "a9fdc140");

	CASSETTE(config,m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED);
}


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

  Game driver(s)

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

ROM_START( agat7 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS("v1")

	ROM_SYSTEM_BIOS( 0, "v1", "Version 1" ) // original?
	ROMX_LOAD("monitor7.rom", 0x3800, 0x0800, CRC(071fda0b) SHA1(6089d46b7addc4e2ae096b2cf81124681bd2b27a), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v2", "Version 2" ) // modded by author of agatcomp.ru
	ROMX_LOAD("agat_pzu.bin", 0x3800, 0x0800, CRC(c605163d) SHA1(b30fd1b264a347a9de69bb9e3105483254994d06), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "debug", "Debug" )  // written by author of agatcomp.ru
	ROMX_LOAD("debug-sysmon7.bin", 0x3800, 0x0800, CRC(d26f18a4) SHA1(2862c13a82e2f4dfc757aa2eeab11fe71c570c12), ROM_BIOS(2))

	// 140KB floppy controller
	ROM_LOAD( "shugart7.rom", 0x4500, 0x0100, CRC(c6e4850c) SHA1(71626d3d2d4bbeeac2b77585b45a5566d20b8d34))
	// 840KB floppy controller
	ROM_LOAD( "teac.rom",     0x4500, 0x0100, CRC(94266928) SHA1(5d369bad6cdd6a70b0bb16480eba69640de87a2e))

	ROM_REGION(0x0800,"gfx1",0)
	ROM_LOAD( "agathe7.fnt", 0x0000, 0x0800, CRC(fcffb490) SHA1(0bda26ae7ad75f74da835c0cf6d9928f9508844c))
ROM_END

ROM_START( agat9 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )

	ROM_SYSTEM_BIOS( 0, "v1", "Version 1" )
	ROMX_LOAD("monitor9.rom", 0x3000, 0x0800, CRC(b90bb66a) SHA1(02217f0785913b41fc25eabcff70fa814799c69a), ROM_BIOS(0))
	ROMX_LOAD("monitor9.rom", 0x3800, 0x0800, CRC(b90bb66a) SHA1(02217f0785913b41fc25eabcff70fa814799c69a), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v2", "Version 2" )
	ROMX_LOAD("monitor91.rom", 0x3000, 0x0800, CRC(89b10fc1) SHA1(7fe1ede32b5525255f82597ca9c3c2034c5996fa), ROM_BIOS(1))
	ROMX_LOAD("monitor91.rom", 0x3800, 0x0800, CRC(89b10fc1) SHA1(7fe1ede32b5525255f82597ca9c3c2034c5996fa), ROM_BIOS(1))

	// Floppy controllers
	ROM_LOAD( "shugart9.rom", 0x4500, 0x0100, CRC(964a0ce2) SHA1(bf955189ebffe874c20ef649a3db8177dc16af61))
	ROM_LOAD( "teac.rom",     0x4500, 0x0100, CRC(94266928) SHA1(5d369bad6cdd6a70b0bb16480eba69640de87a2e))

	// Printer card
	ROM_LOAD( "cm6337.rom", 0x8000, 0x0100, CRC(73be16ec) SHA1(ead1abbef5b86f1def0b956147d5b267f0d544b5))
	ROM_LOAD( "cm6337p.rom", 0x8100, 0x0800, CRC(9120f11f) SHA1(78107653491e88d5ea12e07367c4c028771a4aca))

	ROM_REGION(0x0800,"gfx1",0)
	ROM_LOAD( "agathe9.fnt", 0x0000, 0x0800, CRC(8c55c984) SHA1(5a5a202000576b88b4ae2e180dd2d1b9b337b594))
ROM_END

} // anonymous namespace


//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME  FLAGS
COMP( 1983, agat7, apple2, 0,      agat7,   agat7, agat7_state, empty_init, "Agat",  "Agat-7", MACHINE_IMPERFECT_GRAPHICS)
COMP( 1988, agat9, apple2, 0,      agat9,   agat7, agat9_state, empty_init, "Agat",  "Agat-9", MACHINE_NOT_WORKING)



agvision.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: tim lindner
/***************************************************************************

    Elanco AgVision and Radio Shack VideoTex terminals

    Dynamic RAM (16 or 4k) starts at $0000.
    ROM (2K) starts at $A000 and mirrors up to $BFFF
    Static RAM starts at $C000 for 128 bytes
    The PIA data port a is at $FF1C
         control port a is at $FF1D
            data port b is at $FF1E
         control port b is at $FF1F

    The Control Register (flip flop) is at $FF20
    The SAM starts at $FFC0

    PIA:
        Port A and B make up the keyboard matrix
        PA7  - RS-232 receive.
        CA1  - The horizontal sync from the VDG.
        CA2  - Unconnected, not verified.
        CB1  - Modem status, carrier detect.
        CB2  - Modem control (unverified).
        IRQA - Connected to the 6809 FIRQ.
        IRQB - Unconnected, not verified.

    Control Register:
        Bit 7 - RS-232 transmit.
        Bit 6 - RS-232 DTR, or activity LED (unsure which).
        Bit 5 - unconnected (not verified).
        Bit 3 - VDG GM0.
        Bit 2 - VDG GM1.
        Bit 1 - VDG GM2.
        Bit 0 - VDG A* / G.

    Reverse engineering used with permission of George Phillips of the
    trs80gp emulator.

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

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/m6809/m6809.h"
#include "machine/6821pia.h"
#include "machine/6883sam.h"
#include "machine/ram.h"
#include "video/mc6847.h"
#include "screen.h"

//-------------------------------------------------
//  TYPE DEFINITIONS
//-------------------------------------------------

namespace
{

class agvision_state : public driver_device
{
public:
	agvision_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_ram(*this, "ram")
		, m_pia_0(*this, "pia0")
		, m_sam(*this, "sam")
		, m_vdg(*this, "vdg")
		, m_rs232(*this, "rs232")
		, m_keyboard(*this, "row%u", 0)
	{}

	void agvision(machine_config &config);
	DECLARE_INPUT_CHANGED_MEMBER(hang_up);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	static constexpr int CD_DELAY = 250;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<ram_device> m_ram;
	required_device<pia6821_device> m_pia_0;
	required_device<sam6883_device> m_sam;
	required_device<mc6847_base_device> m_vdg;
	required_device<rs232_port_device> m_rs232;
	required_ioport_array<7> m_keyboard;

	emu_timer *m_timer; // Carrier Detect Timer
	uint8_t sam_read(offs_t offset);
	void mem_map(address_map &map) ATTR_COLD;
	void rom_map(address_map &map) ATTR_COLD;
	void static_ram_map(address_map &map) ATTR_COLD;
	void io0_map(address_map &map) ATTR_COLD;
	void io1_map(address_map &map) ATTR_COLD;
	void boot_map(address_map &map) ATTR_COLD;
	void ff20_write(offs_t offset, uint8_t data);
	void configure_sam();
	uint8_t pia0_pa_r();
	void pia0_cb2_w(int state);
	TIMER_CALLBACK_MEMBER(timer_elapsed);

	int m_cd;
};

//-------------------------------------------------
//  INPUT_PORTS( agvision )
//-------------------------------------------------

static INPUT_PORTS_START( agvision )
	PORT_START("row0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')

	PORT_START("row1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("row2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')

	PORT_START("row3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("UP") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DOWN") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN), 10)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LEFT") PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(UCHAR_MAMEKEY(LEFT), 8) PORT_CHAR('[')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RIGHT") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT), 9) PORT_CHAR(']')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("row4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("row5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("row6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CLEAR") PORT_CODE(KEYCODE_HOME) PORT_CHAR(12, UCHAR_SHIFT_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BREAK") PORT_CODE(KEYCODE_END) PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x78, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("hup")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("HUP") PORT_CODE(KEYCODE_TAB) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(agvision_state::agvision_state::hang_up), 0)
INPUT_PORTS_END

//-------------------------------------------------
//  DEVICE_INPUT_DEFAULTS_START( ag_modem )
//-------------------------------------------------

static DEVICE_INPUT_DEFAULTS_START( ag_modem )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_300 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_300 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

//-------------------------------------------------
//  machine_config
//-------------------------------------------------

void agvision_state::agvision(machine_config &config)
{
	// basic machine hardware
	MC6809E(config, m_maincpu, XTAL(14'318'181) / 16);
	m_maincpu->set_addrmap(AS_PROGRAM, &agvision_state::mem_map);

	PIA6821(config, m_pia_0);
	m_pia_0->readpa_handler().set(FUNC(agvision_state::pia0_pa_r));
	m_pia_0->irqa_handler().set_inputline(m_maincpu, M6809_FIRQ_LINE);
	m_pia_0->cb2_handler().set(FUNC(agvision_state::pia0_cb2_w));

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);

	MC6847(config, m_vdg, XTAL(14'318'181) / 4); // VClk output from MC6883
	m_vdg->set_screen(m_screen);
	m_vdg->hsync_wr_callback().set(m_pia_0, FUNC(pia6821_device::ca1_w));
	m_vdg->input_callback().set(FUNC(agvision_state::sam_read));

	// memory controller
	SAM6883(config, m_sam, XTAL(14'318'181), m_maincpu);
	m_sam->set_addrmap(2, &agvision_state::rom_map);            // ROM at $A000
	m_sam->set_addrmap(3, &agvision_state::static_ram_map);     // RAM at $C000
	m_sam->set_addrmap(4, &agvision_state::io0_map);            //  IO at $FF00
	m_sam->set_addrmap(5, &agvision_state::io1_map);            //  IO at $FF20
	m_sam->set_addrmap(7, &agvision_state::boot_map);           //  IO at $FF60

	RS232_PORT(config, m_rs232, default_rs232_devices, "null_modem");
	m_rs232->set_option_device_input_defaults("null_modem", DEVICE_INPUT_DEFAULTS_NAME(ag_modem));

	// internal ram
	RAM(config, m_ram).set_default_size("16K").set_extra_options("4K");
}

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

void agvision_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).rw(m_sam, FUNC(sam6883_device::read), FUNC(sam6883_device::write));
}

void agvision_state::rom_map(address_map &map)
{
	// $A000-$BFFF
	map(0x0000, 0x07ff).rom().region("maincpu", 0x0000).nopw().mirror(0x1800);
}

void agvision_state::static_ram_map(address_map &map)
{
	// $C000-$FEFF
	map(0x0000, 0x0080).ram();
}

void agvision_state::io0_map(address_map &map)
{
	// $FF00-$FF1F
	map(0x1c, 0x1f).rw("pia0", FUNC(pia6821_device::read), FUNC(pia6821_device::write));
}

void agvision_state::io1_map(address_map &map)
{
	// $FF20-$FF3F
	map(0x00, 0x00).w(FUNC(agvision_state::ff20_write));
}

void agvision_state::boot_map(address_map &map)
{
	// $FF60-$FFEF
	map(0x60, 0x7f).nopw(); // SAM Registers
}

//-------------------------------------------------
//  device_start
//-------------------------------------------------

void agvision_state::machine_start()
{
	configure_sam();

	save_item(NAME(m_cd));
	m_pia_0->cb1_w(0);
	m_timer = timer_alloc(FUNC(agvision_state::timer_elapsed), this);
	m_cd = 0;
}

//-------------------------------------------------
//  sam_read
//-------------------------------------------------

uint8_t agvision_state::sam_read(offs_t offset)
{
	uint8_t data = m_sam->display_read(offset);
	m_vdg->as_w(BIT(data,7));
	m_vdg->inv_w(BIT(data,6));
	return data;
}

//-------------------------------------------------
//  ff20_write
//-------------------------------------------------

void agvision_state::ff20_write(offs_t offset, uint8_t data)
{
	m_rs232->write_txd(BIT(data,7));
	m_vdg->gm0_w(BIT(data,3));
	m_vdg->gm1_w(BIT(data,2));
	m_vdg->gm2_w(BIT(data,1));
	m_vdg->ag_w(BIT(data,0));
}

//-------------------------------------------------
//  configure_sam
//-------------------------------------------------

void agvision_state::configure_sam()
{
	offs_t ramsize = m_ram->size();
	m_sam->space(0).install_ram(0, ramsize - 1, m_ram->pointer());
	if (ramsize < 65536)
		m_sam->space(0).nop_readwrite(ramsize, 0xffff);
}

//-------------------------------------------------
//  timer_elapsed
//-------------------------------------------------

TIMER_CALLBACK_MEMBER(agvision_state::timer_elapsed)
{
	m_timer->adjust(attotime::from_usec(CD_DELAY));
	m_pia_0->cb1_w(m_cd);
	m_cd = !m_cd;
}

//-------------------------------------------------
//  pia0_pa_r - keyboard handler
//-------------------------------------------------

uint8_t agvision_state::pia0_pa_r()
{
	uint8_t pia0_pb = m_pia_0->b_output();

	uint8_t pia0_pa = 0x7f;

	/* poll the keyboard, and update PA6-PA0 accordingly*/
	for (unsigned i = 0; i < m_keyboard.size(); i++)
	{
		int value = m_keyboard[i]->read();
		if ((value | pia0_pb) != 0xff)
		{
			pia0_pa &= ~(0x01 << i);
		}
	}

	pia0_pa |= (m_rs232->rxd_r() ? 0x80 : 0);

	return pia0_pa;
}

//-------------------------------------------------
//  hang_up
//-------------------------------------------------

INPUT_CHANGED_MEMBER(agvision_state::hang_up)
{
	if (newval == 1)
	{
		m_timer->adjust(attotime::never);
	}
}

//-------------------------------------------------
//  pia0_cb2_w
//-------------------------------------------------

void agvision_state::pia0_cb2_w(int state)
{
	if (state == 1)
	{
		m_timer->adjust(attotime::from_usec(CD_DELAY*8000));
	}
	else
	{
		m_timer->adjust(attotime::never);
	}
}

//**************************************************************************
//  ROMS
//**************************************************************************

ROM_START(agvision)
	ROM_REGION(0xa000,"maincpu",0)
	ROM_LOAD("8041716-1.1-agvision.u13", 0x0000, 0x0800, CRC(5b11b13e) SHA1(896e68f90ed57e0921887e717fc92eda067a210d))
ROM_END

ROM_START(trsvidtx)
	ROM_REGION(0xa000,"maincpu",0)
	ROM_LOAD("8041716-1.1-videotex.u13", 0x0000, 0x0800, CRC(821a59bb) SHA1(e3643f27fcf8287c0bc0f66b21554dc988ded9c1))
ROM_END

} // anonymous namespace

//    YEAR  NAME      PARENT COMPAT MACHINE   INPUT     CLASS           INIT        COMPANY              FULLNAME    FLAGS
COMP( 1979, agvision, 0,     0,     agvision, agvision, agvision_state, empty_init, "Elanco",            "AgVision", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1980, trsvidtx, 0,     0,     agvision, agvision, agvision_state, empty_init, "Tandy Radio Shack", "Videotex", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )



aid80f.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    AID-80F was Mostek's development system for the Z80 and 3870 families.
    It was later advertised as the development system for the MATRIX-80
    (MDX) STD bus microcomputer.

    Boards available for this modular system included:
    * SDB-80E/OEM-80E CPU Module
    * FLP-80E Floppy Controller
    * CRT-80E Video Terminal Controller
    * PPG-8/16 2708/2758/2716 PROM Programmer
    * AIM-80 Z80 Application Interface Module
      — later replaced by AIM-Z80AE (4 MHz) and AIM-Z80BE (6 MHz)
    * AIM-72 3870 Application Interface Module
      — later replaced by AIM-7XE

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

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/ram.h"
#include "machine/wd_fdc.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"


namespace {

class aid80f_state : public driver_device
{
public:
	aid80f_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_monitor(*this, "monitor")
	{
	}

	void aid80f(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u8 ram_r(offs_t offset);
	void ram_w(offs_t offset, u8 data);
	u8 monitor_r(offs_t offset);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_region_ptr<u8> m_monitor;

	bool m_ram_enabled = false;
};

void aid80f_state::machine_start()
{
	save_item(NAME(m_ram_enabled));
}

void aid80f_state::machine_reset()
{
	m_ram_enabled = false;
}

u8 aid80f_state::ram_r(offs_t offset)
{
	if (m_ram_enabled)
		return m_ram->read(offset);
	else
		return m_monitor[offset & 0xfff];
}

void aid80f_state::ram_w(offs_t offset, u8 data)
{
	if (m_ram_enabled)
		m_ram->write(offset, data);
}

u8 aid80f_state::monitor_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		m_ram_enabled = true;
	return m_monitor[offset];
}

void aid80f_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(aid80f_state::ram_r), FUNC(aid80f_state::ram_w));
	map(0xe000, 0xefff).r(FUNC(aid80f_state::monitor_r));
}

void aid80f_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0xd8, 0xdb).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0xdc, 0xdf).rw("pio", FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0xe4, 0xe7).rw("fdc", FUNC(fd1771_device::read), FUNC(fd1771_device::write));
}

static INPUT_PORTS_START(aid80f)
INPUT_PORTS_END

static const z80_daisy_config daisy_chain[] =
{
	{ "ctc" },
	{ "pio" },
	{ "sio" },
	{ nullptr }
};

void aid80f_state::aid80f(machine_config &config)
{
	Z80(config, m_maincpu, 4.9152_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &aid80f_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &aid80f_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	RAM(config, m_ram).set_default_size("64K");

	Z80CTC(config, "ctc", 4.9152_MHz_XTAL / 2);
	Z80PIO(config, "pio", 4.9152_MHz_XTAL / 2);
	Z80SIO(config, "sio", 4.9152_MHz_XTAL / 2);

	FD1771(config, "fdc", 4_MHz_XTAL / 4);
}

ROM_START(aid80f)
	ROM_REGION(0x1000, "monitor", 0)
	ROM_LOAD("ddt1.u48", 0x0000, 0x0400, CRC(c9e5dc59) SHA1(3143bca7472900e7255ec0e39ca4121d1cb74c5f))
	ROM_LOAD("ddt2.u49", 0x0400, 0x0400, CRC(dd37f628) SHA1(6da130a3678fd84070dd4ae3487c5212db5604ef))
	ROM_LOAD("ddt3.u50", 0x0800, 0x0400, CRC(646d5fd2) SHA1(b9d0a4b3658835c5d724d709ca3cd70d69474caa))
	ROM_LOAD("ddt4.u51", 0x0c00, 0x0400, CRC(c78e34c2) SHA1(5f3a4631d0b806a077b817f566ebbddd77ad7ba5))
	// U52 socket is empty
ROM_END

} // anonymous namespace


COMP(1978, aid80f, 0, 0, aid80f, aid80f, aid80f_state, empty_init, "Mostek", "AID-80F Development System", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



aim65.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Peter Trauner, Dan Boris, Dirk Best, Robbbert
/******************************************************************************
 PeT mess@utanet.at Nov 2000
Updated by Dan Boris, 2000-04-03
Rewrite in progress, Dirk Best, 2007-07-31
Updated by Robbbert 2019-04-14

Since 0.226, if you want to use the TTY, you must do these things:
- Use the KB/TTY dipswitch to choose TTY
- Use the TAB menu to choose "Keyboard Mode"
- Make sure that both keyboards are "Enabled"
- Quit to save the settings, then restart
- Press DELETE to start using the terminal
- For subsequent runs, just press DELETE to get started.

ToDo:
- Implement punchtape reader/writer
- Front panel Run/Step switch (switch S2)


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

#include "emu.h"
#include "aim65.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "aim65.lh"


/** R6502 Clock.
 *
 * The R6502 on AIM65 operates at 1 MHz. The frequency reference is a 4 MHz
 * crystal controlled oscillator. Dual D-type flip-flop Z10 divides the 4 MHz
 * signal by four to drive the R6502 phase 0 (O0) input with a 1 MHz clock.
 */
static constexpr XTAL AIM65_CLOCK(4_MHz_XTAL / 4);


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

void aim65_state::mem_map(address_map &map)
{
	map(0x1000, 0x3fff).noprw(); // User available expansions
	map(0x4000, 0x7fff).rom(); // 4 ROM sockets in 16K PROM/ROM module
	map(0x8000, 0x9fff).noprw(); // User available expansions
	map(0xa000, 0xa00f).mirror(0x3f0).m(m_via1, FUNC(via6522_device::map)); // user VIA
	map(0xa400, 0xa47f).m(m_riot, FUNC(mos6532_device::ram_map));
	map(0xa480, 0xa497).m(m_riot, FUNC(mos6532_device::io_map));
	map(0xa498, 0xa7ff).noprw(); // Not available
	map(0xa800, 0xa80f).mirror(0x3f0).m(m_via0, FUNC(via6522_device::map)); // system VIA
	map(0xac00, 0xac03).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xac04, 0xac43).ram(); // PIA RAM
	map(0xac44, 0xafff).noprw(); // Not available
	map(0xb000, 0xffff).rom(); // 5 ROM sockets
}


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

static INPUT_PORTS_START( aim65 )
	PORT_START("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space")       PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(32)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".  >")        PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M")           PORT_CODE(KEYCODE_M)          PORT_CHAR('m')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B")           PORT_CODE(KEYCODE_B)          PORT_CHAR('b')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C")           PORT_CODE(KEYCODE_C)          PORT_CHAR('c')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z")           PORT_CODE(KEYCODE_Z)          PORT_CHAR('z')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LF  @")       PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(10)  PORT_CHAR('@')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L")           PORT_CODE(KEYCODE_L)          PORT_CHAR('l')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J")           PORT_CODE(KEYCODE_J)          PORT_CHAR('j')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G")           PORT_CODE(KEYCODE_G)          PORT_CHAR('g')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D")           PORT_CODE(KEYCODE_D)          PORT_CHAR('d')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A")           PORT_CODE(KEYCODE_A)          PORT_CHAR('a')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Print")       PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(UCHAR_MAMEKEY(PRTSCR))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P")           PORT_CODE(KEYCODE_P)          PORT_CHAR('p')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I")           PORT_CODE(KEYCODE_I)          PORT_CHAR('i')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y")           PORT_CODE(KEYCODE_Y)          PORT_CHAR('y')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R")           PORT_CODE(KEYCODE_R)          PORT_CHAR('r')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W")           PORT_CODE(KEYCODE_W)          PORT_CHAR('w')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc")         PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC))

	PORT_START("KEY.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return")      PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-  =")        PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O")           PORT_CODE(KEYCODE_O)          PORT_CHAR('o')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U")           PORT_CODE(KEYCODE_U)          PORT_CHAR('u')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T")           PORT_CODE(KEYCODE_T)          PORT_CHAR('t')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E")           PORT_CODE(KEYCODE_E)          PORT_CHAR('e')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q")           PORT_CODE(KEYCODE_Q)          PORT_CHAR('q')

	PORT_START("KEY.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl")        PORT_CODE(KEYCODE_CAPSLOCK)   PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(":  *")        PORT_CODE(KEYCODE_MINUS)      PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9  )")        PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7  '")        PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5  %")        PORT_CODE(KEYCODE_5)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3  #")        PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1  !")        PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')

	PORT_START("KEY.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left Shift")  PORT_CODE(KEYCODE_LSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0")           PORT_CODE(KEYCODE_0)          PORT_CHAR('0')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8  (")        PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6  &")        PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4  $")        PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2  \"")       PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3")          PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR(UCHAR_MAMEKEY(F3))

	PORT_START("KEY.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del")         PORT_CODE(KEYCODE_TILDE)      PORT_CHAR(8)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";  +")        PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K")           PORT_CODE(KEYCODE_K)          PORT_CHAR('k')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H")           PORT_CODE(KEYCODE_H)          PORT_CHAR('h')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F")           PORT_CODE(KEYCODE_F)          PORT_CHAR('f')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S")           PORT_CODE(KEYCODE_S)          PORT_CHAR('s')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2")          PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(UCHAR_MAMEKEY(F2))

	PORT_START("KEY.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/  ?")        PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",  <")        PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N")           PORT_CODE(KEYCODE_N)          PORT_CHAR('n')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V")           PORT_CODE(KEYCODE_V)          PORT_CHAR('v')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X")           PORT_CODE(KEYCODE_X)          PORT_CHAR('x')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1")          PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_START("switches")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RST") PORT_CODE(KEYCODE_LALT) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(aim65_state::reset_button), 0)
	PORT_DIPNAME(0x08, 0x08, "KB/TTY") PORT_DIPLOCATION("S3:1")
	PORT_DIPSETTING(0x00, "TTY")
	PORT_DIPSETTING(0x08, "KB")
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(aim65_state::reset_button)
{
	// Reset all devices
	// If you're using TTY, you must press DEL after the reset.
	if (newval)
	{
		m_via0->reset();
		m_via1->reset();
		m_pia->reset();
		m_riot->reset();
	}
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}

void aim65_state::aim65_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(0x20, 0x02, 0x05));
	palette.set_pen_color(1, rgb_t(0xc0, 0x00, 0x00));
}


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

std::pair<std::error_condition, std::string> aim65_state::load_cart(
		device_image_interface &image,
		generic_slot_device *slot,
		const char *slot_tag)
{
	uint32_t size = slot->common_get_size(slot_tag);

	if (size > 0x1000)
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported ROM size (must be no more than 4K)");

	if (image.loaded_through_softlist() && image.get_software_region(slot_tag) == nullptr)
	{
		// FIXME: error message seems to be outdated - actual error seems to be incorrect region name in software item
		return std::make_pair(
				image_error::UNSUPPORTED,
				util::string_format("Unsupported file name extension (socket '%1$s' only accepts files with '.%1$s' extension", slot_tag));
	}

	slot->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	slot->common_load_rom(slot->get_rom_base(), size, slot_tag);

	return std::make_pair(std::error_condition(), std::string());
}

// TTY terminal settings. To use, turn KB/TTY switch to TTY, reset, press DEL. All input to be in UPPERCASE.
static DEVICE_INPUT_DEFAULTS_START( serial_term )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_1200 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_1200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_ODD )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void aim65_state::aim65(machine_config &config)
{
	// basic machine hardware
	M6502(config, m_maincpu, AIM65_CLOCK); // 1 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &aim65_state::mem_map);

	config.set_default_layout(layout_aim65);

	// alpha-numeric display
	DL1416T(config, m_ds[0], u32(0));
	m_ds[0]->update().set(FUNC(aim65_state::update_ds<1>));
	DL1416T(config, m_ds[1], u32(0));
	m_ds[1]->update().set(FUNC(aim65_state::update_ds<2>));
	DL1416T(config, m_ds[2], u32(0));
	m_ds[2]->update().set(FUNC(aim65_state::update_ds<3>));
	DL1416T(config, m_ds[3], u32(0));
	m_ds[3]->update().set(FUNC(aim65_state::update_ds<4>));
	DL1416T(config, m_ds[4], u32(0));
	m_ds[4]->update().set(FUNC(aim65_state::update_ds<5>));

	// pseudo-"screen" for the thermal printer. Index 0.
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // not accurate
	screen.set_screen_update(FUNC(aim65_state::screen_update));
	screen.set_size(160, 200);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, m_palette, FUNC(aim65_state::aim65_palette), 2);

	// Sound - wave sound only
	SPEAKER(config, "mono").front_center();

	// other devices
	MOS6532(config, m_riot, AIM65_CLOCK);
	m_riot->pa_wr_callback().set([this] (u8 data) { m_riot_port_a = data; });
	m_riot->pb_rd_callback().set([this] () { return aim65_state::z33_pb_r(); });
	m_riot->irq_wr_callback().set_inputline(m_maincpu, M6502_IRQ_LINE);

	MOS6522(config, m_via0, AIM65_CLOCK);
	m_via0->readpb_handler().set([this] () { return aim65_state::z32_pb_r(); });
	m_via0->writepa_handler().set([this] (u8 data) { aim65_state::z32_pa_w(data); });
	m_via0->writepb_handler().set([this] (u8 data) { aim65_state::z32_pb_w(data); });
	// in CA1 printer ready?
	// out CA2 cass control (H=in)
	m_via0->ca2_handler().set([this] (bool state) { m_ca2 = state; });
	// out CB1 printer start
	//m_via0->cb1_handler().set(FUNC(aim65_state::z32_cb1_w));
	// out CB2 turn printer on
	m_via0->cb2_handler().set([this] (bool state) { aim65_state::z32_cb2_w(state); });
	m_via0->irq_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	MOS6522(config, m_via1, AIM65_CLOCK);
	m_via1->irq_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	PIA6821(config, m_pia);
	m_pia->writepa_handler().set([this] (u8 data) { aim65_state::u1_pa_w(data); });
	m_pia->writepb_handler().set([this] (u8 data) { aim65_state::u1_pb_w(data); });

	CASSETTE(config, m_cassette1);
	m_cassette1->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette1->add_route(ALL_OUTPUTS, "mono", 0.1);
	CASSETTE(config, m_cassette2);
	m_cassette2->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette2->add_route(ALL_OUTPUTS, "mono", 0.1);

	// Screen for TTY interface. Index 1.
	RS232_PORT(config, m_rs232, default_rs232_devices, "terminal");
	//m_rs232->rxd_handler().set(m_via0, FUNC(via6522_device::write_pb6));  // function disabled in 6522via.cpp
	m_rs232->set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(serial_term));

	GENERIC_SOCKET(config, "z26", generic_plain_slot, "aim65_z26_cart", "z26").set_device_load(FUNC(aim65_state::z26_load));
	GENERIC_SOCKET(config, "z25", generic_plain_slot, "aim65_z25_cart", "z25").set_device_load(FUNC(aim65_state::z25_load));
	GENERIC_SOCKET(config, "z24", generic_plain_slot, "aim65_z24_cart", "z24").set_device_load(FUNC(aim65_state::z24_load));

	// PROM/ROM module sockets
	GENERIC_SOCKET(config, "z12", generic_plain_slot, "rm65_z12_cart", "z12").set_device_load(FUNC(aim65_state::z12_load));
	GENERIC_SOCKET(config, "z13", generic_plain_slot, "rm65_z13_cart", "z13").set_device_load(FUNC(aim65_state::z13_load));
	GENERIC_SOCKET(config, "z14", generic_plain_slot, "rm65_z14_cart", "z14").set_device_load(FUNC(aim65_state::z14_load));
	GENERIC_SOCKET(config, "z15", generic_plain_slot, "rm65_z15_cart", "z15").set_device_load(FUNC(aim65_state::z15_load));

	// internal ram
	RAM(config, RAM_TAG).set_default_size("4K").set_extra_options("1K,2K,3K");

	// Software lists
	SOFTWARE_LIST(config, "cart_list").set_original("aim65_cart");
}


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

ROM_START( aim65 )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_SYSTEM_BIOS(0, "aim65",   "Rockwell AIM-65")
	ROMX_LOAD("aim65mon.z23", 0xe000, 0x1000, CRC(90e44afe) SHA1(78e38601edf6bfc787b58750555a636b0cf74c5c), ROM_BIOS(0))
	ROMX_LOAD("aim65mon.z22", 0xf000, 0x1000, CRC(d01914b0) SHA1(e5b5ddd4cd43cce073a718ee4ba5221f2bc84eaf), ROM_BIOS(0))

	/* DRAC/DRAC-1 is an industrial control computer from the Spanish company Comelta (more info: https://www.oldcomputers.es/drac-1/).
	   It's based on a standard Rockwell AIM 65 PCB, but can be expanded with several cards and accessories made by Comelta, from CPU and
	   memory modules to control or interface cards (more info and manuals with schematics: https://www.oldcomputers.es/drac-1-placas-cr/).
	*/
	ROM_SYSTEM_BIOS(1, "drac1",   "Comelta DRAC-1")
	ROMX_LOAD("crosaim_v1.3_b_mone_2b_moni_01_e000.z23", 0xe000, 0x1000, CRC(ae83ba08) SHA1(4ee4157fe6cafda6c763547183be18859bdabc36), ROM_BIOS(1))
	ROMX_LOAD("crosaim_v1.3_b_monf_2b_f000.z22",         0xf000, 0x1000, CRC(047c2ca8) SHA1(1877be29f7b725ee4fec7f21aa679d857391514b), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "dynatem", "Dynatem AIM-65")
	ROMX_LOAD("dynaim65.z23", 0xe000, 0x1000, CRC(90e44afe) SHA1(78e38601edf6bfc787b58750555a636b0cf74c5c), ROM_BIOS(2))
	ROMX_LOAD("dynaim65.z22", 0xf000, 0x1000, CRC(83e1c6e7) SHA1(444134043edd83385bd70434cb100269901c4417), ROM_BIOS(2))

	ROM_SYSTEM_BIOS(3, "spc100",  "Siemens PC100")
	ROMX_LOAD("pc100.z23",    0xe000, 0x1000, CRC(90e44afe) SHA1(78e38601edf6bfc787b58750555a636b0cf74c5c), ROM_BIOS(3))
	ROMX_LOAD("pc100.z22",    0xf000, 0x1000, CRC(aa07742a) SHA1(3b9bee24a00cf23b7b50cee97ccc12e3fa9da1ea), ROM_BIOS(3))
ROM_END


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

//   YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY     FULLNAME  FLAGS
COMP(1977, aim65, 0,      0,      aim65,   aim65, aim65_state, empty_init, "Rockwell", "AIM 65", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW)



aim65_40.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Dirk Best
/***************************************************************************

    Rockwell AIM-65/40

    Skeleton driver

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

/*

Attached are the three main aim-65/40 roms, plus one unlabeled expansion rom for the 65/40 which someone
had accidentally stuck into the aim-65 non/40 I was restoring on campus. (The rom pinout is different between
the two systems, iirc.). I have no idea what that rom does.
The .zxx location on the rom filename (labeled R2332LP // R3224-11 // 8224) is hence wrong; it should probably
be .z70 (at 0xc000-cfff) or .z72 (0xe000-0xefff)


The aim-65/40 has the following stuff on it:
XTALS: 16Mhz, 1.8432Mhz
6522 VIA ('Keyboard VIA' which reads the keyboard)
6522 VIA ('System VIA' which does the display and printer, and probably the cassette interface)
6522 VIA ('User VIA' which is in charge of the parallel port and expansion I/O)
6502 processor (clock is some division of the 16Mhz XTAL)
6551 ACIA (marked JACIA on board; used for serial port, has its own 1.8432Mhz XTAL)
Onboard beeper/speaker
Other chips: MC3242A, MC3480P, SN74159N, mc1488/1489 pair (for rs232)
ROM: 8 sockets meant for 2332 mask roms/mcm68732 eproms, or, if only 4 sockets are used, 2364 mask roms/mcm68764
eproms, selectable which mode by jumper. (normal 2732s won't work due to an inverted vpp pin i think, but there's
jumpers on the board which will probably allow it), each socket addresses 0x1000 (or 0x2000) of space:
z63 is 0x8000-0x8fff, (see below about possible expansion ram here instead of rom)
z64 is 0x9000-0x9fff, (see below about possible expansion ram here instead of rom)
z65 is 0xa000-0xafff (first monitor rom lives here on my board),(see below about possible expansion ram here instead of rom)
z66 is 0xb000-0xbfff (second monitor rom lives here on my board),(see below about possible expansion ram here instead of rom)
z70 is 0xc000-0xcfff
z71 is 0xd000-0xdfff
z72 is 0xe000-0xefff
z73 is 0xf000-0xffff (i/o rom always lives here)
RAM:
The aim-65/40 comes standard with anywhere from 4k to 16k of ram at 0x0000-0x3fff, and can be populated in 4k
increments with another 16k at 0x4000-0x7fff and another 16k at 0x8000-0xbfff (if installed, this last 16k prevents
the first four rom sockets from being used since they address the same place, and the monitor roms must be moved to
the z70 and z71, or z71 and z72 sockets. The i/o rom knows to search on 4k boundaries for the monitor roms)
The vias and acia are memory mapped over top of a blank area in the i/o rom from 0xff80 to 0xffe0, as such:
ff80-ff9f = unknown, open bus?
ffa0-ffaf = user via
ffb0-ffbf = system via
ffc0-ffcf = keyboars via
ffd0-ffd3 (mirrored at ffd4, ffd8, ffdc probably) = acia

I have a copy of the official 'green book' Aim 65/40 i/o rom listing/asm source code, which is very informative about
how the hardware works. However it is going to be a major pain in the ass to scan, owing to its small size and failing binding.
The source code there implies that *maybe* ff7e and ff7f are also open bus.

One web page says that the display and printer have their own 65c02 cpus, making them intelligent devices. Are the roms for
those included?

Unable to locate any manuals so unable to proceed.

*/

#include "emu.h"
#include "cpu/m6502/m6502.h"
#include "machine/6522via.h"
#include "machine/mos6551.h"
#include "bus/rs232/rs232.h"
#include "aim65_40.lh"


namespace {

//**************************************************************************
//  MACROS/CONSTANTS
//**************************************************************************

#define M6502_TAG       "m6502"
#define M6522_0_TAG     "m6522_0"
#define M6522_1_TAG     "m6522_1"
#define M6522_2_TAG     "m6522_2"
#define M6551_TAG       "m6551"


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

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

	void aim65_40(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;
};


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

void aim65_40_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).ram();
	map(0xa000, 0xcfff).rom().region("roms", 0);
	map(0xf000, 0xffff).rom().region("roms", 0x3000);
	map(0xffa0, 0xffaf).m(M6522_0_TAG, FUNC(via6522_device::map));
	map(0xffb0, 0xffbf).m(M6522_1_TAG, FUNC(via6522_device::map));
	map(0xffc0, 0xffcf).m(M6522_2_TAG, FUNC(via6522_device::map));
	map(0xffd0, 0xffd3).rw(M6551_TAG, FUNC(mos6551_device::read), FUNC(mos6551_device::write));
}

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

static INPUT_PORTS_START( aim65_40 )
INPUT_PORTS_END

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

void aim65_40_state::aim65_40(machine_config &config)
{
	/* basic machine hardware */
	m6502_device &cpu(M6502(config, M6502_TAG, 1000000));
	cpu.set_addrmap(AS_PROGRAM, &aim65_40_state::mem_map);

	/* video hardware */
	config.set_default_layout(layout_aim65_40);

	/* sound hardware */

	/* devices */
	MOS6522(config, M6522_0_TAG, 1000000);
	MOS6522(config, M6522_1_TAG, 1000000);
	MOS6522(config, M6522_2_TAG, 1000000);

	mos6551_device &acia(MOS6551(config, M6551_TAG, 0));
	acia.set_xtal(1.8432_MHz_XTAL);
	acia.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	acia.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	acia.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(M6551_TAG, FUNC(mos6551_device::write_rxd));
	rs232.dcd_handler().set(M6551_TAG, FUNC(mos6551_device::write_dcd));
	rs232.dsr_handler().set(M6551_TAG, FUNC(mos6551_device::write_dsr));
	rs232.cts_handler().set(M6551_TAG, FUNC(mos6551_device::write_cts));
}

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

ROM_START( aim65_40 )
	ROM_REGION( 0x4000, "roms", 0 )
	ROM_LOAD( "mon a v1.0 r32u5-11.z65", 0x0000, 0x1000, CRC(8c970c67) SHA1(5c8aecb2155a10777a57d4f0f2d16b7ba7b1fb45) )
	ROM_LOAD( "mon b v1.0 r32u6-11.z66", 0x1000, 0x1000, CRC(38a1e0cd) SHA1(37c34e32ad25d27e9590ee3f325801ca311be7b1) )
	ROM_LOAD( "r2332lp r3224-11.z70",    0x2000, 0x1000, CRC(0878b399) SHA1(483e92b57d64be51643a9f6490521a8572aa2f68) ) // Assembler
	ROM_LOAD( "i-of v1.0 r32t3-12.z73",  0x3000, 0x1000, CRC(a62bec4a) SHA1(a2fc69a33dc3b7684bf3399beff7b22eaf05c843) )
ROM_END

} // anonymous namespace


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

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY     FULLNAME     FLAGS
COMP( 1981, aim65_40, 0,      0,      aim65_40, aim65_40, aim65_40_state, empty_init, "Rockwell", "AIM-65/40", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



airbase99.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for AirBase 99 drum machine by JoMoX GmbH.

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

#include "emu.h"
#include "cpu/pic17/pic17c4x.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"

namespace {

class airbase99_state : public driver_device
{
public:
	airbase99_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_slavecpu(*this, "slavecpu")
	{
	}

	void airbase99(machine_config &config);

private:
	HD44780_PIXEL_UPDATE(pixel_update);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<pic17c4x_device> m_maincpu;
	required_device<pic17c4x_device> m_slavecpu;
};


HD44780_PIXEL_UPDATE(airbase99_state::pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 16)
		bitmap.pix(line * 8 + y, pos * 6 + x) = state;
}

void airbase99_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("firmware", 0);
	map(0x8000, 0x8008).nopw();
	map(0x8008, 0x8008).nopr();
	map(0x8010, 0x8011).rw("lcdc", FUNC(hd44780_device::control_r), FUNC(hd44780_device::write)).umask16(0x00ff);
}

static INPUT_PORTS_START(airbase99)
INPUT_PORTS_END

void airbase99_state::airbase99(machine_config &config)
{
	PIC17C43(config, m_maincpu, 16'000'000); // PIC17C43-16/P (XTAL unreadable)
	m_maincpu->set_mode(pic17c43_device::mode::MICROPROCESSOR);
	m_maincpu->set_addrmap(AS_PROGRAM, &airbase99_state::mem_map);

	PIC17C43(config, m_slavecpu, 16'000'000); // PIC17C43-33/P (XTAL unreadable)
	m_slavecpu->set_mode(pic17c43_device::mode::MICROCONTROLLER);
	m_slavecpu->set_disable();

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6*16, 8*2);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	hd44780_device &lcdc(HD44780(config, "lcdc", 270'000)); // TODO: clock not measured, datasheet typical clock used
	lcdc.set_lcd_size(2, 20);
	lcdc.set_pixel_update_cb(FUNC(airbase99_state::pixel_update));
}

ROM_START(airbase99)
	ROM_REGION16_LE(0x20000, "firmware", 0)
	ROM_LOAD16_BYTE("airbase99 r1_09l.bin", 0x00000, 0x10000, CRC(218ec7cc) SHA1(8eeffa22f927d5e17937f9c04dbebf5367f520df)) // second half blank
	ROM_LOAD16_BYTE("airbase99 r1_09h.bin", 0x00001, 0x10000, CRC(f3e7a25f) SHA1(8978326615661238aa07304bd4aaf8471b7bf449)) // second half blank

	ROM_REGION(0x2000, "slavecpu", 0)
	ROM_LOAD("airbase_slave.bin", 0x0000, 0x2000, NO_DUMP)

	ROM_REGION(0x80000, "samples", 0)
	ROM_LOAD("rom0.bin", 0x00000, 0x80000, NO_DUMP) // M27C4001
ROM_END

} // anonymous namespace

SYST(1998, airbase99, 0, 0, airbase99, airbase99, airbase99_state, empty_init, "JoMoX", "AiRBase 99", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



akaiax80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    akaiax80.cpp - Akai AX80 8-voice polyphonic synthesizer

    Skeleton driver by R. Belmont
    ROMs provided by Arashikage

    Hardware:
        CPU: uPD7811 with internal ROM
        Other H/W: 8253 PIT (x6), 8255 PPI (x2), 8279 keyboard/display controller
        Voices (x8):
            NJM4558D sawtooth generator (x2)
            TC4011BP wave shaper
            TC4013 sub oscillator
            CEM 3372 VCA/VCF
        Final out:
            TL082CP low-pass filter

    Service manual incl. schematics at:
    https://archive.org/download/AkaiAX80ServiceManual/Akai%20AX80%20Service%20Manual.pdf

    There's a 2nd UPD7811 (IC1) to scan the keyboard. Its internal rom is undumped.
    Ports A/B/C go to the keyboard, Port D (d0-6) connects 1-to-1 to Port A of IC2
    (Main CPU). Ports F and AN are unused. PD7 goes to /INT2 of IC2, /NMI goes to PA7
    of IC2.

    Both CPUs are hardwired as MODE 1, and both share a CSA120MT crystal.

    RAM is backed up by a CR2430-T battery.

    A crystal, type HC-16, (6,554,800Hz) generates various frequencies to drive the PITs
    and the 8279. Later production used a 6,553,600Hz instead.


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

#include "emu.h"
#include "cpu/upd7810/upd7810.h"
#include "machine/pit8253.h"
#include "machine/i8255.h"
#include "machine/i8279.h"


namespace {

#define PIT0_TAG "pit0"
#define PIT1_TAG "pit1"
#define PIT2_TAG "pit2"
#define PIT3_TAG "pit3"
#define PIT4_TAG "pit4"
#define PIT5_TAG "pit5"
#define PPI0_TAG "ppi0"
#define PPI1_TAG "ppi1"

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

	void ax80(machine_config &config);

private:
	void ax80_map(address_map &map) ATTR_COLD;

	virtual void machine_reset() override ATTR_COLD;
	required_device<upd7810_device> m_maincpu;
};

void ax80_state::machine_reset()
{
}

void ax80_state::ax80_map(address_map &map)
{
	//map(0x0000, 0x0fff).rom().region("maincpu", 0); // internal ROM
	map(0x1000, 0x1003).mirror(0x000c).rw(PIT0_TAG, FUNC(pit8253_device::read), FUNC(pit8253_device::write)); // IC20
	map(0x1010, 0x1013).mirror(0x000c).rw(PIT1_TAG, FUNC(pit8253_device::read), FUNC(pit8253_device::write)); // IC21
	map(0x1020, 0x1023).mirror(0x000c).rw(PIT2_TAG, FUNC(pit8253_device::read), FUNC(pit8253_device::write)); // IC22
	map(0x1030, 0x1033).mirror(0x000c).rw(PIT3_TAG, FUNC(pit8253_device::read), FUNC(pit8253_device::write)); // IC23
	map(0x1040, 0x1043).mirror(0x000c).rw(PIT4_TAG, FUNC(pit8253_device::read), FUNC(pit8253_device::write)); // IC24
	map(0x1050, 0x1053).mirror(0x000c).rw(PIT5_TAG, FUNC(pit8253_device::read), FUNC(pit8253_device::write)); // IC25
	map(0x1060, 0x1061).mirror(0x000e).rw("kdc", FUNC(i8279_device::read), FUNC(i8279_device::write));   // IC11
	map(0x1070, 0x1073).mirror(0x000c).rw(PPI1_TAG, FUNC(i8255_device::read), FUNC(i8255_device::write));   // IC10
	//map(0x2000, 0x2001).mirror(0x0dfe).rw(PPI0_TAG, FUNC(i8255_device::read), FUNC(i8255_device::write));   // IC9 - A9 connects to A1-pin
	//map(0x2200, 0x2201).mirror(0x0dfe).rw(PPI0_TAG, FUNC(i8255_device::read), FUNC(i8255_device::write));   // IC9 - A9 connects to A1-pin
	//map(0x3000, 0x3fff) // steers audio to the various voice channels
	map(0x4000, 0x5fff).mirror(0x2000).rom().region("program", 0);    // external program EPROM
	map(0x8000, 0x87ff).mirror(0x3800).ram();
	map(0xc000, 0xc7ff).mirror(0x3800).ram();
}

void ax80_state::ax80(machine_config &config)
{
	UPD7811(config, m_maincpu, XTAL(12'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &ax80_state::ax80_map);
	//m_maincpu->set_addrmap(AS_IO, &ax80_state::ax80_io);

	PIT8253(config, PIT0_TAG, 0);
	PIT8253(config, PIT1_TAG, 0);
	PIT8253(config, PIT2_TAG, 0);
	PIT8253(config, PIT3_TAG, 0);
	PIT8253(config, PIT4_TAG, 0);
	PIT8253(config, PIT5_TAG, 0);

	I8255A(config, PPI0_TAG);
	I8255A(config, PPI1_TAG);

	I8279(config, "kdc", 6554800 / 8); // Keyboard/Display Controller
	//kdc.out_irq_calback().set_inputline("maincpu", UPD7810_INTF1);    // irq
	//kdc.out_sl_callback().set(FUNC(ax80_state::scanlines_w));         // scan SL lines
	//kdc.out_disp_callback().set(FUNC(ax80_state::digit_w));           // display A&B
	//kdc.in_rl_callback().set(FUNC(ax80_state::kbd_r))                 // kbd RL lines
	//kdc.in_shift_callback().set_constant(1);                          // not connected
	//kdc.in_ctrl_callback().set_constant(1);                           // not connected
}

static INPUT_PORTS_START( ax80 )
INPUT_PORTS_END

ROM_START( ax80 )
	ROM_REGION(0x1000, "maincpu", 0) // CPU internal mask
	ROM_LOAD( "akai ax80 main cpu mask rom.ic2", 0x000000, 0x001000, CRC(241c078f) SHA1(7f5d0d718f2d03ec446568ae440beaff0aac6bfd) )

	ROM_REGION(0x2000, "program", 0) // external program EPROM
	ROM_SYSTEM_BIOS( 0, "k", "REV.K" )
	ROMX_LOAD( "ax-80k.ic4", 0x000000, 0x002000, CRC(a2f95ccf) SHA1(4e5f2c4c9a08ec1d38146cae786b400261a3dbb7), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "l", "REV.L" )
	ROMX_LOAD( "ax-80l.ic4", 0x000000, 0x002000, CRC(bc3d21bd) SHA1(d6730ec33b28e705a0ff88946b7860fadcc37793), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "i", "REV.I" )
	ROMX_LOAD( "ax-80i.ic4", 0x000000, 0x002000, CRC(d616e435) SHA1(84820522e6a96fc29966f82e76254e54df15d7e6), ROM_BIOS(2) )
ROM_END

} // anonymous namespace


CONS( 1984, ax80, 0, 0, ax80, ax80, ax80_state, empty_init, "Akai", "AX80", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



akaivx600.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for Akai VX600 analog synthesizer.

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

#include "emu.h"
//#include "bus/midi/midi.h"
#include "cpu/upd78k/upd78k3.h"
#include "cpu/upd7810/upd7810.h"
#include "machine/nvram.h"
#include "machine/pit8253.h"
//#include "screen.h"


namespace {

class akaivx600_state : public driver_device
{
public:
	akaivx600_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_subcpu(*this, "subcpu")
	{
	}

	void vx600(machine_config &config);

private:
	void main_map(address_map &map) ATTR_COLD;
	void sub_map(address_map &map) ATTR_COLD;

	required_device<upd78310_device> m_maincpu;
	required_device<upd78c11_device> m_subcpu;
};


void akaivx600_state::main_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("program1", 0);
	map(0xa000, 0xbfff).ram();
	map(0xc000, 0xc7ff).ram().share("nvram");
	map(0xf020, 0xf023).w("pit", FUNC(pit8253_device::write));
}

void akaivx600_state::sub_map(address_map &map)
{
	map(0x4000, 0x5fff).rom().region("program2", 0);
}


static INPUT_PORTS_START(vx600)
INPUT_PORTS_END

void akaivx600_state::vx600(machine_config &config)
{
	UPD78310(config, m_maincpu, 12_MHz_XTAL); // μPD78310G
	m_maincpu->set_addrmap(AS_PROGRAM, &akaivx600_state::main_map);

	UPD78C11(config, m_subcpu, 12_MHz_XTAL); // μPD78C11G-044
	m_subcpu->set_addrmap(AS_PROGRAM, &akaivx600_state::sub_map);

	NVRAM(config, "nvram"); // CXK5816-PN12L + BR2032

	PIT8253(config, "pit"); // μPD8253-2

	//LC7981(config, "lcdc");
}

ROM_START(vx600)
	ROM_REGION(0x8000, "program1", 0)
	ROM_LOAD("vx600_-1-_v1.2.ic45", 0x0000, 0x8000, CRC(19a32cc4) SHA1(9190cb47456f12959272f1b160c73298da638ba2)) // 27C256

	ROM_REGION(0x1000, "subcpu", 0)
	ROM_LOAD("upd78c11g-044-36.ic46", 0x000, 0x1000, NO_DUMP)
	ROM_FILL(0x0000, 1, 0x54) // dummy reset vector
	ROM_FILL(0x0001, 1, 0x00)
	ROM_FILL(0x0002, 1, 0x40)
	ROM_FILL(0x0018, 1, 0x54) // dummy interrupt vector
	ROM_FILL(0x0019, 1, 0x18)
	ROM_FILL(0x001a, 1, 0x40)
	ROM_FILL(0x0090, 1, 0xca) // dummy CALT vectors
	ROM_FILL(0x0091, 1, 0x41)
	ROM_FILL(0x0092, 1, 0xca)
	ROM_FILL(0x0093, 1, 0x41)
	ROM_FILL(0x0094, 1, 0xca)
	ROM_FILL(0x0095, 1, 0x41)
	ROM_FILL(0x0096, 1, 0xca)
	ROM_FILL(0x0097, 1, 0x41)
	ROM_FILL(0x0098, 1, 0xca)
	ROM_FILL(0x0099, 1, 0x41)
	ROM_FILL(0x009a, 1, 0xca)
	ROM_FILL(0x009b, 1, 0x41)

	ROM_REGION(0x2000, "program2", 0)
	ROM_LOAD("vx600_-2-_v1.12.ic47", 0x0000, 0x2000, CRC(1b3ee178) SHA1(20a319392824f9f3074f370d9d9eb2f312d69ac4)) // 27C64
ROM_END

} // anonymous namespace


SYST(1988, vx600, 0, 0, vx600, vx600, akaivx600_state, empty_init, "Akai", "VX600 Programmable Matrix Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)




al8800b.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for MITS Altair 8800b.

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

#include "emu.h"
//#include "bus/s100/s100.h"
#include "cpu/i8085/i8085.h"

namespace {

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

	void al8800b(machine_config &config);

private:
	required_device<i8080a_cpu_device> m_maincpu;
};

static INPUT_PORTS_START(al8800b)
INPUT_PORTS_END

void al8800b_state::al8800b(machine_config &config)
{
	I8080A(config, m_maincpu, 18_MHz_XTAL / 9);
}

ROM_START(al8800b)
	ROM_REGION(0x100, "panel", 0)
	// This 1702A EPROM is not mapped into the 8080 memory space. It contains custom microcode implementing front panel functions.
	ROM_LOAD("8800b front panel.bin", 0x000, 0x100, CRC(8b462c1b) SHA1(3c13b1cc941225b16580655c15a61b8e8418b052))
ROM_END

} // anonymous namespace

COMP(1976, al8800b, 0, 0, al8800b, al8800b, al8800b_state, empty_init, "MITS", "Altair 8800b", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)



alcat7100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Skeleton driver for Alcatel 7100 terminal.

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

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/am9517a.h"
#include "machine/ay31015.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "video/mc6845.h"
#include "screen.h"


namespace {

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

	void alcat7100(machine_config &config);

private:
	MC6845_UPDATE_ROW(update_row);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
};


MC6845_UPDATE_ROW(alcat7100_state::update_row)
{
}


void alcat7100_state::mem_map(address_map &map)
{
	map(0x0000, 0x0fff).rom().region("maincpu", 0);
	map(0x7800, 0x7fff).ram();
	map(0xc800, 0xcfff).ram();
	map(0xf800, 0xffff).ram();
}

void alcat7100_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x09, 0x09).nopw();
	map(0x0c, 0x0f).rw("pio1", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x20, 0x23).rw("pio2", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x24, 0x24).w("crtc", FUNC(mc6845_device::address_w));
	map(0x28, 0x2b).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x25, 0x25).w("crtc", FUNC(mc6845_device::register_w));
	map(0x30, 0x3f).rw("dmac", FUNC(am9517a_device::read), FUNC(am9517a_device::write));
}


static INPUT_PORTS_START(alcat7100)
INPUT_PORTS_END


static const z80_daisy_config daisy_chain[] =
{
	{ "pio1" },
	{ "pio2" },
	{ "ctc" },
	{ nullptr }
};

void alcat7100_state::alcat7100(machine_config &config)
{
	Z80(config, m_maincpu, 24_MHz_XTAL / 6); // MK3880N-4 (clock not verified)
	m_maincpu->set_addrmap(AS_PROGRAM, &alcat7100_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &alcat7100_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	AM9517A(config, "dmac", 24_MHz_XTAL / 6); // P8237A-5

	z80pio_device &pio1(Z80PIO(config, "pio1", 24_MHz_XTAL / 6)); // Z8420A
	pio1.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80pio_device &pio2(Z80PIO(config, "pio2", 24_MHz_XTAL / 6)); // Z8420A
	pio2.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	pio2.in_pb_callback().set_constant(0x80); // ?

	z80ctc_device &ctc(Z80CTC(config, "ctc", 24_MHz_XTAL / 6));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(15'900'000, 1200, 0, 960, 265, 0, 240);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	mc6845_device &crtc(MC6845(config, "crtc", 15'900'000 / 12));
	crtc.set_screen("screen");
	crtc.set_char_width(12);
	crtc.set_show_border_area(false);
	crtc.set_update_row_callback(FUNC(alcat7100_state::update_row));

	AY31015(config, "uart"); // AMI S1602P
}


ROM_START(alcat7100) // Z80  // 256k ram // b&w  // looks like it needs a boot floppy to start
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD("rom.u117",               0x0000, 0x0800, CRC(9c0debf7) SHA1(a042db34090656224ede41d8190f22f719d1a634))
	ROM_LOAD("906_513601_012gd2.u110", 0x0800, 0x0800, CRC(9346a41c) SHA1(6f7a2946494adac4d34874da9d5e475c99457000)) // keyboard?

	ROM_REGION(0x1000, "chargen", 0) // first half blank
	ROM_LOAD("906_513301_rev00_ba6d.u20", 0x0000, 0x1000, CRC(143cfdfc) SHA1(4d924d1f16c30d72e1fdbb786488156bb9961442))
ROM_END

} // anonymous namespace


COMP(1984, alcat7100, 0, 0, alcat7100, alcat7100, alcat7100_state, empty_init, "Alcatel", "Terminal 7100", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



alesis.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

    Alesis HR-16 and SR-16 drum machines

    06/04/2010 Skeleton driver.

    http://www.vintagesynth.com/misc/hr16.php
    http://www.vintagesynth.com/misc/sr16.php
    http://www.vintagesynth.com/misc/mmt8.php

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

#include "emu.h"
#include "alesis.h"
#include "sr16.lh"
#include "screen.h"


void alesis_state::kb_matrix_w(uint8_t data)
{
	m_kb_matrix = data;
}

uint8_t alesis_state::kb_r()
{
	uint8_t data = 0xff;

	for (int i = 0; i < 6; i++)
		if (!BIT(m_kb_matrix, i))
			data &= m_col[i]->read();

	return data;
}

void alesis_state::led_w(uint8_t data)
{
	m_patt_led      = BIT(data, 0) ? 1 : 0;
	m_song_led      = BIT(data, 0) ? 0 : 1;
	m_play_led      = BIT(data, 1) ? 0 : 1;
	m_record_led    = BIT(data, 2) ? 0 : 1;
	m_voice_led     = BIT(data, 3) ? 0 : 1;
	m_tune_led      = BIT(data, 4) ? 0 : 1;
	m_mix_led       = BIT(data, 5) ? 0 : 1;
	m_tempo_led     = BIT(data, 6) ? 0 : 1;
	m_midi_led      = BIT(data, 7) ? 0 : 1;
}

uint8_t alesis_state::p3_r()
{
	uint8_t data = 0xff;

	data &= ~(m_cassette->input() > 0.01 ? 0x00 : 0x08);

	return data;
}

void alesis_state::p3_w(uint8_t data)
{
	m_cassette->output(data & 0x04 ? -1.0 : +1.0);
}

void alesis_state::sr16_lcd_w(uint8_t data)
{
	m_lcdc->write(BIT(m_kb_matrix,7), data);
}

void alesis_state::mmt8_led_w(uint8_t data)
{
	m_play_led      = BIT(data, 0) ? 0 : 1;
	m_record_led    = BIT(data, 1) ? 0 : 1;
	m_part_led      = BIT(data, 2) ? 0 : 1;
	m_edit_led      = BIT(data, 3) ? 0 : 1;
	m_song_led      = BIT(data, 4) ? 0 : 1;
	m_echo_led      = BIT(data, 5) ? 0 : 1;
	m_loop_led      = BIT(data, 6) ? 0 : 1;

	m_leds = data;
}

uint8_t alesis_state::mmt8_led_r()
{
	return m_leds;
}

void alesis_state::track_led_w(uint8_t data)
{
	for (int i=0; i < 8; i++)
		m_track_led[i] = BIT(data, i);
}

uint8_t alesis_state::mmt8_p3_r()
{
	// ---- -x--   Tape in
	// ---- x---   Start/Stop input
	uint8_t data = 0xff;

	data &= ~(m_cassette->input() > 0.01 ? 0x00 : 0x04);

	return data;
}

void alesis_state::mmt8_p3_w(uint8_t data)
{
	// ---x ----   Tape out
	// --x- ----   Click out

	m_cassette->output(data & 0x10 ? -1.0 : +1.0);
}

void alesis_state::hr16_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).mirror(0x8000).rom();
}

void alesis_state::hr16_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0000).r(FUNC(alesis_state::kb_r));
	map(0x0002, 0x0002).w("dm3ag", FUNC(alesis_dm3ag_device::write));
	map(0x0004, 0x0004).w(FUNC(alesis_state::led_w));
	map(0x0006, 0x0007).rw(m_lcdc, FUNC(hd44780_device::read), FUNC(hd44780_device::write));
	map(0x0008, 0x0008).w(FUNC(alesis_state::kb_matrix_w));
	map(0x8000, 0xffff).ram().share("nvram");   // 32Kx8 SRAM, (battery-backed)
}

void alesis_state::sr16_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rom();
}

void alesis_state::sr16_io(address_map &map)
{
	//map.unmap_value_high();
	map(0x0000, 0x0000).mirror(0xff).w("dm3ag", FUNC(alesis_dm3ag_device::write));
	map(0x0200, 0x0200).mirror(0xff).w(FUNC(alesis_state::sr16_lcd_w));
	map(0x0300, 0x0300).mirror(0xff).w(FUNC(alesis_state::kb_matrix_w));
	map(0x0400, 0x0400).mirror(0xff).r(FUNC(alesis_state::kb_r));
	map(0x8000, 0xffff).ram().share("nvram");   // 32Kx8 SRAM, (battery-backed)
}

void alesis_state::mmt8_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).ram().share("nvram");   // 2x32Kx8 SRAM, (battery-backed)
	map(0xff02, 0xff02).w(FUNC(alesis_state::track_led_w));
	map(0xff04, 0xff04).rw(FUNC(alesis_state::mmt8_led_r), FUNC(alesis_state::mmt8_led_w));
	map(0xff06, 0xff06).w(FUNC(alesis_state::kb_matrix_w));
	map(0xff08, 0xff09).rw(m_lcdc, FUNC(hd44780_device::read), FUNC(hd44780_device::write));
	map(0xff0e, 0xff0e).nopr();
}

/* Input ports */
static INPUT_PORTS_START( hr16 )
	PORT_START("COL1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("COPY")   PORT_CODE(KEYCODE_C)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("OFFSET") PORT_CODE(KEYCODE_O)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SWING")  PORT_CODE(KEYCODE_W)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("QUANT")  PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LENGTH") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PATT")   PORT_CODE(KEYCODE_P)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MIDI/UTIL") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TEMPO")  PORT_CODE(KEYCODE_T)
	PORT_START("COL2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DELETE") PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("INSERT") PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SONG")   PORT_CODE(KEYCODE_S)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1")      PORT_CODE(KEYCODE_1)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2")      PORT_CODE(KEYCODE_2)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3")      PORT_CODE(KEYCODE_3)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4")      PORT_CODE(KEYCODE_4)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5")      PORT_CODE(KEYCODE_5)
	PORT_START("COL3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ERASE")  PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TAPE")   PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("FILL")   PORT_CODE(KEYCODE_F)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6")      PORT_CODE(KEYCODE_6)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7")      PORT_CODE(KEYCODE_7)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8")      PORT_CODE(KEYCODE_8)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9")      PORT_CODE(KEYCODE_9)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0")      PORT_CODE(KEYCODE_0)
	PORT_START("COL4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PLAY")  PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("STOP/CONTINUE") PORT_CODE(KEYCODE_END)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RECORD") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("< -")    PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("> +")    PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("VOICE")  PORT_CODE(KEYCODE_V)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TUNE")   PORT_CODE(KEYCODE_H)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MIX")    PORT_CODE(KEYCODE_M)
	PORT_START("COL5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TOM 1")  PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TOM 2")  PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TOM 3")  PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TOM 4")  PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RIDE")   PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CRASH")  PORT_CODE(KEYCODE_F6)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PERC 1") PORT_CODE(KEYCODE_F7)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PERC 2") PORT_CODE(KEYCODE_F8)
	PORT_START("COL6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("KICK")   PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SNARE")  PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CLOSED HAT") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MID HAT")    PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("OPEN HAT")   PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CLAPS")  PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PERC 3") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PERC 4") PORT_CODE(KEYCODE_8_PAD)

	PORT_START("SELECT")
	PORT_BIT(0xff, 0x00, IPT_DIAL) PORT_NAME("SELECT Slider") PORT_SENSITIVITY(50) PORT_KEYDELTA(1) PORT_CODE_DEC(KEYCODE_DOWN) PORT_CODE_INC(KEYCODE_UP)
INPUT_PORTS_END

static INPUT_PORTS_START( mmt8 )
	PORT_START("COL1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("<<")     PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(">>")     PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ERASE")  PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TRANS")  PORT_CODE(KEYCODE_R)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PLAY")   PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("STOP/CONTINUE") PORT_CODE(KEYCODE_END)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("COPY")   PORT_CODE(KEYCODE_C)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RECORD")  PORT_CODE(KEYCODE_HOME)
	PORT_START("COL2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TRACK 1") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TRACK 2") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TRACK 3") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TRACK 4") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TRACK 5") PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TRACK 6") PORT_CODE(KEYCODE_F6)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TRACK 7") PORT_CODE(KEYCODE_F7)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TRACK 8") PORT_CODE(KEYCODE_F8)
	PORT_START("COL3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TEMPO")  PORT_CODE(KEYCODE_T)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-")      PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("+")      PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x38, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PAGE DOWN")  PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PAGE UP")    PORT_CODE(KEYCODE_UP)
	PORT_START("COL4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CLICK")  PORT_CODE(KEYCODE_G)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6")      PORT_CODE(KEYCODE_6)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7")      PORT_CODE(KEYCODE_7)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8")      PORT_CODE(KEYCODE_8)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9")      PORT_CODE(KEYCODE_9)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0")      PORT_CODE(KEYCODE_0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MIDI CHAN") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TAPE")   PORT_CODE(KEYCODE_Y)
	PORT_START("COL5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CLOCK")  PORT_CODE(KEYCODE_K)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1")      PORT_CODE(KEYCODE_1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2")      PORT_CODE(KEYCODE_2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3")      PORT_CODE(KEYCODE_3)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4")      PORT_CODE(KEYCODE_4)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5")      PORT_CODE(KEYCODE_5)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SONG")   PORT_CODE(KEYCODE_S)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MERGE")  PORT_CODE(KEYCODE_M)
	PORT_START("COL6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MIDI FILTER") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MIDI ECHO")   PORT_CODE(KEYCODE_H)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LOOP")   PORT_CODE(KEYCODE_J)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("QUANT")  PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LENGTH") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PART")   PORT_CODE(KEYCODE_P)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("EDIT")   PORT_CODE(KEYCODE_E)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NAME")   PORT_CODE(KEYCODE_N)
INPUT_PORTS_END

static INPUT_PORTS_START( sr16 )
	PORT_START("COL1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PRESET/USER")  PORT_CODE(KEYCODE_U)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PATTERN/SONG") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1")      PORT_CODE(KEYCODE_1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2")      PORT_CODE(KEYCODE_2)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3")      PORT_CODE(KEYCODE_3)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4")      PORT_CODE(KEYCODE_4)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5")      PORT_CODE(KEYCODE_5)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("UP")     PORT_CODE(KEYCODE_UP)
	PORT_START("COL2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RECORD SETUP") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DRUM SET") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6")      PORT_CODE(KEYCODE_6)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7")      PORT_CODE(KEYCODE_7)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8")      PORT_CODE(KEYCODE_8)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9")      PORT_CODE(KEYCODE_9)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0")      PORT_CODE(KEYCODE_0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DOWN")   PORT_CODE(KEYCODE_DOWN)
	PORT_START("COL3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BACKUP") PORT_CODE(KEYCODE_K)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ERASE")  PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PLAY")   PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("STOP")   PORT_CODE(KEYCODE_END)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A")      PORT_CODE(KEYCODE_A)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B")      PORT_CODE(KEYCODE_B)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("FILL")   PORT_CODE(KEYCODE_F)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("COPY")   PORT_CODE(KEYCODE_C)
	PORT_START("COL4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MIDI SETUP") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TEMPO/PAGE UP") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TOM 1")  PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TOM 2")  PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TOM 3")  PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RIDE")   PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CRASH")  PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PERC 1") PORT_CODE(KEYCODE_6_PAD)
	PORT_START("COL5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PERFORM/COMPOSE") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TEMPO/PAGE DOWN") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("KICK")   PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SNARE")  PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CLOSED HAT") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("OPEN HAT")   PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CLAPS")  PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PERC 2") PORT_CODE(KEYCODE_F6)
	PORT_START("COL6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END


void alesis_state::alesis_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}

void alesis_state::machine_start()
{
	m_digit.resolve();
	m_pattern.resolve();
	m_track_led.resolve();
	m_patt_led.resolve();
	m_song_led.resolve();
	m_play_led.resolve();
	m_record_led.resolve();
	m_voice_led.resolve();
	m_tune_led.resolve();
	m_mix_led.resolve();
	m_tempo_led.resolve();
	m_midi_led.resolve();
	m_part_led.resolve();
	m_edit_led.resolve();
	m_echo_led.resolve();
	m_loop_led.resolve();
	m_a_next.resolve();
	m_b_next.resolve();
	m_fill_next.resolve();
	m_user_next.resolve();
	m_play.resolve();
	m_record.resolve();
	m_compose.resolve();
	m_perform.resolve();
	m_song.resolve();
	m_b.resolve();
	m_a.resolve();
	m_fill.resolve();
	m_user.resolve();
	m_edited.resolve();
	m_set.resolve();
	m_drum.resolve();
	m_press_play.resolve();
	m_metronome.resolve();
	m_tempo.resolve();
	m_page.resolve();
	m_step_edit.resolve();
	m_swing_off.resolve();
	m_swing_62.resolve();
	m_click_l1.resolve();
	m_click_note.resolve();
	m_click_l2.resolve();
	m_click_3.resolve();
	m_backup.resolve();
	m_drum_set.resolve();
	m_swing.resolve();
	m_swing_58.resolve();
	m_click_off.resolve();
	m_click.resolve();
	m_quantize_off.resolve();
	m_quantize_3.resolve();
	m_midi_setup.resolve();
	m_record_setup.resolve();
	m_quantize.resolve();
	m_swing_54.resolve();
	m_quantize_l1.resolve();
	m_quantize_l2.resolve();
	m_quantize_l3.resolve();
	m_quantize_note.resolve();
	m_setup.resolve();
}

void alesis_state::machine_reset()
{
	m_kb_matrix = 0xff;
	m_leds = 0;
	memset(m_lcd_digits, 0, sizeof(m_lcd_digits));
}


HD44780_PIXEL_UPDATE(alesis_state::sr16_pixel_update)
{
	if (line == 1 && pos >= 6 && pos < 8)  // last 2 characters of the second line are used to control the LCD symbols
		update_lcd_symbols(bitmap, pos, y, x, state);
	else if (pos < 8)
		bitmap.pix(line*9 + y, pos*6 + x) = state;
}

void alesis_state::hr16(machine_config &config)
{
	/* basic machine hardware */
	I80C31(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &alesis_state::hr16_mem);
	m_maincpu->set_addrmap(AS_IO, &alesis_state::hr16_io);
	m_maincpu->port_in_cb<1>().set_ioport("SELECT");
	m_maincpu->port_in_cb<3>().set(FUNC(alesis_state::p3_r));
	m_maincpu->port_out_cb<3>().set(FUNC(alesis_state::p3_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(6*16, 9*2);
	screen.set_visarea_full();
	screen.set_screen_update("hd44780", FUNC(hd44780_device::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(alesis_state::alesis_palette), 2);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED);
	m_cassette->set_interface("hr16_cass");

	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 16);

	/* sound hardware */
	ALESIS_DM3AG(config, "dm3ag", 12_MHz_XTAL/2);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);
}

void alesis_state::sr16(machine_config &config)
{
	hr16(config);

	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &alesis_state::sr16_mem);
	m_maincpu->set_addrmap(AS_IO, &alesis_state::sr16_io);
	m_maincpu->port_in_cb<1>().set_constant(0);

	/* video hardware */
	screen_device &screen(*subdevice<screen_device>("screen"));
	screen.set_size(6*8, 9*2);
	screen.set_visarea_full();

	config.set_default_layout(layout_sr16);

	m_lcdc->set_lcd_size(2, 8);
	m_lcdc->set_pixel_update_cb(FUNC(alesis_state::sr16_pixel_update));
}

void alesis_state::mmt8(machine_config &config)
{
	hr16(config);

	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_IO, &alesis_state::mmt8_io);
	m_maincpu->port_in_cb<1>().set(FUNC(alesis_state::kb_r));
	m_maincpu->port_in_cb<3>().set(FUNC(alesis_state::mmt8_p3_r));
	m_maincpu->port_out_cb<3>().set(FUNC(alesis_state::mmt8_p3_w));

	config.device_remove("dm3ag");
}

/* ROM definition */
ROM_START( hr16 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )

	ROM_REGION( 0x10000, "user1", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS("v109")
	ROM_SYSTEM_BIOS(0, "v106", "ver 1.06")
	ROMX_LOAD("hr16-v1.06.bin",  0x0000, 0x8000, CRC(f0cdb899) SHA1(f21cd87af15ad5a0bfec992e38131c4f4e4c5102), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v107", "ver 1.07")
	ROMX_LOAD("2-19-0256-v107.u11",  0x0000, 0x8000, CRC(2582b6a2) SHA1(f1f135335578c938be63b37ed207e82b7a0e13be), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v109", "ver 1.09")
	ROMX_LOAD("2-19-0256-v109.u11",  0x0000, 0x8000, CRC(a9bdbf20) SHA1(229b4230c7b5380efbfd42fa95645723d3fd6d55), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v200", "ver 2.00")
	ROMX_LOAD("hr16-v2.0.bin",  0x0000, 0x8000, CRC(a3fcba12) SHA1(4c94be7e94e5a1d86443571cd4d375158a6e7b65), ROM_BIOS(3))

	ROM_REGION( 0x100000, "dm3ag", 0 )
	ROM_LOAD("2-27-0004.u16", 0x00000, 0x80000, CRC(8e103536) SHA1(092e1cf649fbef171cfaf91e20707d89998b7a1e))
	ROM_LOAD("2-27-0003.u15", 0x80000, 0x80000, CRC(82e9b78c) SHA1(89728cb38ae172b5e347a03018617c94a087dce0))
ROM_END

ROM_START( hr16b )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )

	ROM_REGION( 0x10000, "user1", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "v200", "ver 2.00")
	ROMX_LOAD("2-19-0256-v200.u11",0x0000,  0x8000, CRC(19cf0fce) SHA1(f8b3786b32d68e3627a654b8b3916befbe9bc540), ROM_BIOS(0))

	ROM_REGION( 0x100000, "dm3ag", 0 )
	ROM_LOAD("2-27-0008.u16", 0x00000, 0x80000, CRC(11ca930e) SHA1(2f57fdd02f9b2146a551370a74cab1fa800145ab))
	ROM_LOAD("2-27-0007.u15", 0x80000, 0x80000, CRC(319746db) SHA1(46b32a3ab2fbad67fb4566f607f578a2e9defd63))
ROM_END

ROM_START( mmt8 )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "v111", "ver 1.11")
	ROMX_LOAD("mt8v1-11.bin", 0x00000, 0x08000, CRC(c9951946) SHA1(149bc5ea46466537de4074820c66a2296ea43bc1), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v109", "ver 1.09")
	ROMX_LOAD("mt8v1-09.bin", 0x00000, 0x08000, CRC(0ec41dec) SHA1(2c283965e510b586a08f0290df4dd357e6b19b62), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v108", "ver 1.08")
	ROMX_LOAD("mt8v1-08.bin", 0x00000, 0x08000, CRC(a0615455) SHA1(77395c837b356b34d6b96f6f46eca8c89b57434e), ROM_BIOS(2))
ROM_END

ROM_START( sr16 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "v104", "ver 1.04")
	ROMX_LOAD( "sr16_v1_04.bin", 0x0000, 0x10000, CRC(d049af6e) SHA1(0bbeb4bd25e33a9eca64d5a31480f96a0040617e), ROM_BIOS(0))

	ROM_REGION( 0x100000, "dm3ag", ROMREGION_ERASEFF )
	ROM_LOAD( "sr16.u6", 0x00000, 0x80000, CRC(6da96987) SHA1(3ec8627d440bc73841e1408a19def09a8b0b77f7))
	ROM_LOAD( "sr16.u5", 0x80000, 0x80000, CRC(8bb25cfa) SHA1(273ad59d017b54a7e8d5e1ec61c8cd807a0e4af3))
ROM_END


void alesis_state::init_hr16()
{
	uint8_t *ROM = memregion("maincpu")->base();
	uint8_t *orig = memregion("user1")->base();
	for (int i = 0; i < 0x8000; i++)
	{
		ROM[bitswap<16>(i,15,14,13,12,11,10,9,8,0,1,2,3,4,5,6,7)] = orig[i];
	}
}

/* Driver */
/*    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS         INIT        COMPANY   FULLNAME          FLAGS */
SYST( 1987, hr16,  0,      0,      hr16,    hr16,  alesis_state, init_hr16,  "Alesis", "HR-16",          MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
SYST( 1987, mmt8,  0,      0,      mmt8,    mmt8,  alesis_state, empty_init, "Alesis", "MMT-8",          MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
SYST( 1989, hr16b, hr16,   0,      hr16,    hr16,  alesis_state, init_hr16,  "Alesis", "HR-16B",         MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
SYST( 1990, sr16,  0,      0,      sr16,    sr16,  alesis_state, empty_init, "Alesis", "SR-16 (Alesis)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



alesis_qs.cpp
<---------------------------------------------------------------------->
// license:GPL2+
// copyright-holders:Felipe Sanches
/******************************************************************

 Alesis QS-series keyboards

 Currently this skeleton covers only the Alesis QS-7
 unit, but other keyboards in this series have similar
 hardware caracteristics.

 Author: Felipe Correa da Silva Sanches <juca@members.fsf.org>

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

#include "emu.h"
#include "cpu/h8500/h8510.h"
//#include "sound/alesis_qs.h"


namespace {

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

	void qs7(machine_config &config);

private:
	void qs7_prog_map(address_map &map) ATTR_COLD;

	required_device<h8510_device> m_maincpu;
};

/* Input ports */
static INPUT_PORTS_START( qs7 )
//        PORT_START("COL1")
//        PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_)
//        PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_)
//        PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_)
//        PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_)
//        PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_)
//        PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_)
//        PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_)
//        PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_)
INPUT_PORTS_END

void qs_state::qs7_prog_map(address_map &map)
{
	map(0x00000, 0x3ffff).mirror(0x40000).rom().region("program", 0);
}

void qs_state::qs7(machine_config &config)
{
	/* basic machine hardware */
	HD6415108(config, m_maincpu, 20_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &qs_state::qs7_prog_map);

	//ALESIS_KEYSCAN(config, "keyscan", 20_MHz_XTAL / 2 / 8);

	/* Alesis Sound Generator ASIC */
	//ALESIS_SG(config, "sndgen");

	/* Alesis Sound Effects Processor ASIC */
	//ALESIS_FXCHIP(config, "sfx", 7.056_MHz_XTAL);

	/* video hardware */
	//TODO: add LCD display controller here

	/* sound hardware */
	//SPEAKER(config, "speaker", 2).front();
	//alesis_qs_series_device &sound(ALESIS_QS_SERIES(config, "sound", SND_CLOCK));
	//sound.add_route(0, "speaker", 1.0, 0);
	//sound.add_route(1, "speaker", 1.0, 1);

		/* Interfaces */
		//PCMCIA
		//MIDI
		//RS232
}

// XTALs: 20 MHz (H8/510), 7.056 MHz (FXCHIP), 14.7456 MHz
ROM_START( alesqs7 )
	ROM_REGION16_BE( 0x80000, "program", 0 )
	ROM_LOAD16_WORD_SWAP( "2-31-0069_q7_v1.02_alesis_sp_09_12_96_cs_dbcc.u18", 0x00000, 0x80000, CRC(6e5404cb) SHA1(f00598b66ab7a83b16105cbb73e09c66ce3493a7) )

//  ROM_REGION( 0x200000, "sound", 0 ) /* Samples ROMs (2Mbyte each) */
//  ROM_LOAD( "?.u?", 0x00000, 0x200000, NO_DUMP )
//  ROM_LOAD( "?.u?", 0x00000, 0x200000, NO_DUMP )
//  ROM_LOAD( "?.u?", 0x00000, 0x200000, NO_DUMP )
//  ROM_LOAD( "?.u?", 0x00000, 0x200000, NO_DUMP )
//  ROM_LOAD( "?.u?", 0x00000, 0x200000, NO_DUMP )
//  ROM_LOAD( "?.u?", 0x00000, 0x200000, NO_DUMP )
//  ROM_LOAD( "?.u?", 0x00000, 0x200000, NO_DUMP )
//  ROM_LOAD( "?.u?", 0x00000, 0x200000, NO_DUMP )
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT  CLASS     INIT        COMPANY   FULLNAME                       FLAGS
COMP( 1996, alesqs7, 0,      0,      qs7,     qs7,   qs_state, empty_init, "Alesis", "Alesis QS7 musical keyboard", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



alfaskop41xx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Joakim Larsson Edström
/***************************************************************************

Alfaskop 41 series

This driver is a part of a revivel project for Alfaskop 41 series where
no known working system exists today because of its distributed nature.
All parts network boots over SS3 (SDLC) from a Floppy Disk unit and nothing
works unless there is a floppy in that unit. These floppies are rare and
many parts have been discarded because they are useless stand alone.

The goal is to emulate missing parts so a full system can be demonstrated again.

Links and credits
-----------------
Project home page: https://github.com/MattisLind/alfaskop_emu
Dalby Datormusem - http://www.datormuseum.se/peripherals/terminals/alfaskop
Bitsavers - http://bitsavers.org/pdf/ericsson/alfaskop/
Dansk Datahistorisk Forening - http://datamuseum.dk/

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

#include "emu.h"

#include "cpu/m6800/m6800.h"
#include "machine/6821pia.h"
#include "machine/6850acia.h"
#include "machine/input_merger.h"
#include "machine/mc6844.h"
#include "machine/mc6854.h"
#include "machine/output_latch.h"
#include "machine/pla.h"
#include "video/mc6845.h"

#include "screen.h"

//#include "bus/rs232/rs232.h"
//#include "machine/clock.h"

#define LOG_IO    (1U << 1)
#define LOG_NVRAM (1U << 2)
#define LOG_MIC   (1U << 3)
#define LOG_DIA   (1U << 4)
#define LOG_DMA   (1U << 5)
#define LOG_IRQ   (1U << 6)
#define LOG_ADLC  (1U << 7)

//#define VERBOSE (LOG_MIC|LOG_ADLC|LOG_IRQ|LOG_DMA|LOG_IO)
//#define LOG_OUTPUT_STREAM std::cout

#include "logmacro.h"

#define LOGIO(...)    LOGMASKED(LOG_IO,    __VA_ARGS__)
#define LOGNVRAM(...) LOGMASKED(LOG_NVRAM, __VA_ARGS__)
#define LOGMIC(...)   LOGMASKED(LOG_MIC,   __VA_ARGS__)
#define LOGDIA(...)   LOGMASKED(LOG_DIA,   __VA_ARGS__)
#define LOGDMA(...)   LOGMASKED(LOG_DMA,   __VA_ARGS__)
#define LOGIRQ(...)   LOGMASKED(LOG_IRQ,   __VA_ARGS__)
#define LOGADLC(...)  LOGMASKED(LOG_ADLC,  __VA_ARGS__)


namespace {

#define PLA1_TAG "ic50"
#define PLA1_INUSE 0 // 0=disabled until a PLA converter between DATAIO and MAXLOADER (mame format) exists

class alfaskop4110_state : public driver_device
{
public:
	alfaskop4110_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_kbd_acia(*this, "kbd_acia")
		, m_mic_pia(*this, "mic_pia")
		, m_dia_pia(*this, "dia_pia")
		, m_crtc(*this, "crtc")
		, m_screen(*this, "screen")
		, m_vram(*this, "vram")
		, m_pla(*this, PLA1_TAG)
		, m_chargen(*this, "chargen")
		, m_tia_adlc(*this, "tia_adlc")
		, m_tia_dma(*this, "tia_dma")
		, m_irq(0)
		, m_imsk(0)
	{ }

	void alfaskop4110(machine_config &config);
private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<acia6850_device> m_kbd_acia;
	required_device<pia6821_device> m_mic_pia;
	required_device<pia6821_device> m_dia_pia;
	required_device<mc6845_device> m_crtc;
	required_device<screen_device> m_screen;
	required_shared_ptr<uint8_t> m_vram;
	required_device<pls100_device> m_pla;

	/* Video controller */
	required_region_ptr<uint8_t> m_chargen;
	MC6845_UPDATE_ROW(crtc_update_row);

	/* TIA */
	required_device<mc6854_device> m_tia_adlc;
	required_device<mc6844_device> m_tia_dma;

	/* Interrupt handling */
	template <unsigned N> void irq_w(int state);
	uint8_t m_irq;
	uint8_t m_imsk;

	/* Debug stuff */
	/* Timer callbacks */
	TIMER_CALLBACK_MEMBER(poll_start);
	TIMER_CALLBACK_MEMBER(poll_bit);

	// DEBUG stuff, will be removed when hooked up towards remote peer
	/* zero extended SDLC poll message frame to feed into receiver as a test
	   0 1 1 1 1 1 1 0   ; opening flag 0x7e
	   0 0 0 0 0 0 0 0   ; 0x00
	   1 1 1 1 1 0 1 1 1 ; 0xff <- a zero needs to be inserted, done by test code
	   0 0 0 0 0 0 1 1   ; 0xc0
	   0 0 0 0 0 1 0 1   ; 0xa0
	   1 0 1 1 0 0 0 1   ; CRC 0x8d
	   1 0 1 0 1 0 1 0   ; CRC 0x55
	   0 1 1 1 1 1 1 0   ; closing flag 0x7e
	*/
	uint8_t txBuf[10] = {0x7e, 0x00, 0xff, 0xc0, 0xa0, 0x8d, 0x55, 0x7e};
	emu_timer *m_poll_start_timer = nullptr;
	emu_timer *m_poll_bit_timer = nullptr;
	int index = 0;
	int pos   = 0;
	int ones  = 0;
	bool flank = false;
};

class alfaskop4120_state : public driver_device
{
public:
	alfaskop4120_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mic_pia(*this, "mic_pia")
		, m_fdapia(*this, "dia_pia")
	{ }

	void alfaskop4120(machine_config &config);
private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<pia6821_device> m_mic_pia;
	required_device<pia6821_device> m_fdapia;
};

class alfaskop4101_state : public driver_device
{
public:
	alfaskop4101_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mic_pia(*this, "mic_pia")
	{ }

	void alfaskop4101(machine_config &config);
private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<pia6821_device> m_mic_pia;
};

void alfaskop4110_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).ram();
	map(0x7800, 0x7fff).ram().share(m_vram); // TODO: Video RAM base address is configurable via NVRAM - this is the default
	map(0x8000, 0xefff).ram();

	// NVRAM
	map(0xf600, 0xf6ff).lrw8(NAME([this](offs_t offset) -> uint8_t { LOGNVRAM("nvram_r %04x: %02x\n", offset, 0); return (uint8_t) 0; }),
				 NAME( [this](offs_t offset, uint8_t data) {    LOGNVRAM("nvram_w %04x: %02x\n", offset, data); }));
	// TIA board
	map(0xf700, 0xf71f).mirror(0x00).lrw8(NAME([this](offs_t offset) -> uint8_t    { LOGDMA("TIA DMA_r %04x: %02x\n", offset, 0); return m_tia_dma->read(offset); }),
						  NAME([this](offs_t offset, uint8_t data) { LOGDMA("TIA DMA_w %04x: %02x\n", offset, data); m_tia_dma->write(offset, data); }));
	map(0xf720, 0xf723).mirror(0x04).lrw8(NAME([this](offs_t offset) -> uint8_t    { LOGADLC("TIA ADLC_r %04x: %02x\n", offset, 0); return m_tia_adlc->read(offset); }),
						  NAME([this](offs_t offset, uint8_t data) { LOGADLC("TIA ADLC_w %04x: %02x\n", offset, data); m_tia_adlc->write(offset, data); }));

	// Main PCB
	map(0xf7d9, 0xf7d9).mirror(0x06).lrw8(NAME([this](offs_t offset) -> uint8_t    { LOGIO("CRTC reg r %04x: %02x\n", offset, 0); return m_crtc->register_r(); }),
						  NAME([this](offs_t offset, uint8_t data) { LOGIO("CRTC reg w %04x: %02x\n", offset, data); m_crtc->register_w(data);}));
	map(0xf7d8, 0xf7d8).mirror(0x06).lw8(NAME([this](offs_t offset, uint8_t data) { LOGIO("CRTC adr w %04x: %02x\n", offset, data); m_crtc->address_w(data); }));
	map(0xf7d0, 0xf7d3).mirror(0x04).lrw8(NAME([this](offs_t offset) -> uint8_t    { LOGDIA("DIA pia_r %04x: %02x\n", offset, 0); return m_dia_pia->read(offset & 3); }),
						  NAME([this](offs_t offset, uint8_t data) { LOGDIA("DIA pia_w %04x: %02x\n", offset, data); m_dia_pia->write(offset & 3, data); }));
	map(0xf7c4, 0xf7c7).mirror(0x00).lrw8(NAME([this](offs_t offset) -> uint8_t    { uint8_t tmp = m_mic_pia->read(offset & 3); LOGMIC("\nMIC pia_r %04x: %02x\n", offset, tmp); return tmp; }),
						  NAME([this](offs_t offset, uint8_t data) { LOGMIC("\nMIC pia_w %04x: %02x\n", offset, data); m_mic_pia->write(offset & 3, data); }));
	map(0xf7c0, 0xf7c1).mirror(0x02).lrw8(NAME([this](offs_t offset) -> uint8_t    { LOGIO("KBD acia_r %04x: %02x\n", offset, 0); return m_kbd_acia->read(offset & 1); }),
						  NAME([this](offs_t offset, uint8_t data) { LOGIO("KBD acia_w %04x: %02x\n", offset, data); m_kbd_acia->write(offset & 1, data); }));

	map(0xf7fc, 0xf7fc).mirror(0x00).lr8(NAME([this](offs_t offset) -> uint8_t { LOGIO("Address Switch 0-7\n"); return 0; }));

#if PLA1_INUSE
	map(0xf800, 0xffe7).rom().region("roms", 0);

	// IRQ mask setting
	map(0xffe8, 0xfff7).rom().lrw8( NAME([this](offs_t offset) -> uint8_t
					{
						if (!machine().side_effects_disabled()) LOGIO("AMSK read set %04x\n", offset >> 1);
						m_imsk = (offset >> 1) & 7;
						return ((uint8_t *) memregion("roms")->base() + 0x7e8)[offset];
					}),
					NAME([this](offs_t offset, uint8_t data)
					{
						if (!machine().side_effects_disabled()) LOGIO("AMSK write set %04x\n", offset);
						m_imsk = (offset >> 1) & 7;
					}));

	// Address modification
	map(0xfff8, 0xfff9).rom().lrw8( NAME([this](offs_t offset) -> uint8_t
					{
						uint16_t ploffs = (~m_irq & 0xff) | ((m_imsk & 0x07) << 8) | 0x000 | (0x18 << 11);
						uint8_t tmp =  ((uint8_t *) memregion("roms")->base())[0x7e0 | offset | ((m_pla->read(ploffs) & 0xf0) >> 3)];
						if (!machine().side_effects_disabled())
						{
							LOGIO("AMOD read %04x: %02x\n", offset, tmp);
							LOGIO("AMOD pla read %04x: %02x ==> %04x\n", ploffs, m_pla->read(ploffs), (0xffe0 | offset | ((m_pla->read(ploffs) & 0xf0) >> 3)));
						}
						return tmp;
					}),
					NAME([this](offs_t offset, uint8_t data) // TODO: Check what a write does if anything
					{
						if (!machine().side_effects_disabled()) LOGIO("AMOD write %04x\n", offset);
					}));

	map(0xfffa, 0xffff).rom().region("roms", 0x7fa);
#else
	map(0xf800, 0xffff).rom().region("roms", 0);
#endif
}

void alfaskop4120_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xefff).ram();
	map(0xf600, 0xf6ff).lrw8(NAME([this](offs_t offset) -> uint8_t { LOGNVRAM("nvram_r %04x: %02x\n", offset, 0); return (uint8_t) 0; }), // TODO: Move to MRO board
				 NAME([this](offs_t offset, uint8_t data) { LOGNVRAM("nvram_w %04x: %02x\n", offset, data); }));
	map(0xf740, 0xf743).mirror(0x0c).lrw8(NAME([this](offs_t offset) -> uint8_t    { LOGIO("FDA pia_r %04x: %02x\n", offset, 0); return m_fdapia->read(offset & 3); }),
						  NAME([this](offs_t offset, uint8_t data) { LOGIO("FDA pia_w %04x: %02x\n", offset, data); m_fdapia->write(offset & 3, data); }));
	map(0xf7c4, 0xf7c7).mirror(0x00).lrw8(NAME([this](offs_t offset) -> uint8_t    { LOGMIC("MIC pia_r %04x: %02x\n", offset, 0); return m_mic_pia->read(offset & 3); }),
						  NAME([this](offs_t offset, uint8_t data) { LOGMIC("MIC pia_w %04x: %02x\n", offset, data); m_mic_pia->write(offset & 3, data); }));
	map(0xf800, 0xffff).rom().region("roms", 0);
}

void alfaskop4101_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xefff).ram();
	map(0xf600, 0xf6ff).lrw8(NAME([this](offs_t offset) -> uint8_t { LOGNVRAM("nvram_r %04x: %02x\n", offset, 0); return (uint8_t) 0; }),
				 NAME([this](offs_t offset, uint8_t data) { LOGNVRAM("nvram_w %04x: %02x\n", offset, data); }));
	map(0xf7c4, 0xf7c7).mirror(0x00).lrw8(NAME([this](offs_t offset) -> uint8_t    { LOGMIC("MIC pia_r %04x: %02x\n", offset, 0); return m_mic_pia->read(offset & 3); }),
						  NAME([this](offs_t offset, uint8_t data) { LOGMIC("MIC pia_w %04x: %02x\n", offset, data); m_mic_pia->write(offset & 3, data); }));
	map(0xf800, 0xffff).rom().region("roms", 0);
}

/* Input ports */
static INPUT_PORTS_START( alfaskop4110 )
INPUT_PORTS_END

static INPUT_PORTS_START( alfaskop4120 )
INPUT_PORTS_END

static INPUT_PORTS_START( alfaskop4101 )
INPUT_PORTS_END

/* Interrupt handling - vector address modifyer, irq prioritizer and irq mask */
template <unsigned N> void alfaskop4110_state::irq_w(int state)
{
	m_irq = (m_irq & ~(1 << N)) | ((state ? 1 : 0) << N);
	LOGIRQ("4110 IRQ %d: %d ==> %02x\n", N, state, m_irq);
	m_maincpu->set_input_line(M6800_IRQ_LINE, state);
}

/* Simplified chargen, no attributes or special formats/features yet  */
MC6845_UPDATE_ROW( alfaskop4110_state::crtc_update_row )
{
	offs_t const base = ma + 0x4000;
	u32 *px = &bitmap.pix(y);

	for (int i = 0; i < x_count; i++)
	{
		u8 chr = m_vram[(base + i) & 0x07ff] & 0x7f;
		rgb_t const bg = rgb_t::white();
		rgb_t const fg = rgb_t::black();

		u8 dots = m_chargen[chr * 16 + ra];

		for (int n = 8; n > 0; n--, dots <<= 1)
			*px++ = BIT(dots, 7) ? fg : bg;
	}
}

void alfaskop4110_state::alfaskop4110(machine_config &config)
{
	/* basic machine hardware */
	M6800(config, m_maincpu, XTAL(19'170'000) / 18); // Verified from service manual
	m_maincpu->set_addrmap(AS_PROGRAM, &alfaskop4110_state::mem_map);

	/* Interrupt controller and address modifier PLA */
	/*
	 * 82S100 data sheet
	 * -----------------
	 *
	 * The 82S100 is a bipolar, fuse-link programmable logic array. It uses the
	 * standard AND/OR/invert architecture to directly implement custom
	 * um-of-product logic equations.
	 *
	 * Each device consists of 16 dedicated inputs and 8 dedicated outputs. Each
	 * output is capable of being actively controlled by any or all of the 48
	 * product terms. The true, complement, or don't care condition of each of the
	 * 16 inputs ANDed together comprise one P-Term. All 48 P-Terms are then OR-d
	 * to each output. The user must then only select which P-Terms will activate
	 * an output by disconnecting terms which do not affect the output. In addition,
	 * each output can be fused as active high or active low.
	 *
	 * The 82S100 is fully TTL compatible and includes chip-enable control for
	 * expansion of input variables and output inhibit. It features three state
	 * outputs.
	 *
	 * Field programmable Ni-Cr links
	 * 16 inputs
	 * 8 outputs
	 * 48 product terms
	 * Commercial verion - N82S100 - 50ns max address access time
	 * Power dissipation - 600mW typ
	 * Input loading - 100uA max
	 * Chip enable input
	 * Three state outputs
	 *
	 *
	 */
	/*                   _____   _____
	 *        nc FE   1 |*    \_/     | 28  Vcc
	 *      IRQ7 I7   2 |             | 27  I8  mask 1
	 *      IRQ6 I6   3 |             | 26  I9  mask 2
	 *      IRQ5 I5   4 |             | 25  I10 mask 3
	 *      IRQ4 I4   5 |             | 24  I11 Address &== 1111 1111 111x xxxx
	 *      IRQ3 I3   6 |    82S100   | 23  I12 AI 1 A1
	 *      IRQ2 I2   7 |             | 22  I13 AI 2 A2
	 *      IRQ1 I1   8 |     IC50    | 21  I14 AI 3 A3
	 *      IRQ0 I0   9 |             | 20  I15 AI 4 A4
	 *        P4 F7  10 |  Interrupt  | 19  _CE
	 *   mask P3 F6  11 |  Controller | 18  F0   IRQ
	 *   mask P2 F5  12 |     PLA     | 17  F1   mask register
	 *   mask P1 F4  13 |             | 16  F2   interrupt latch
	 *          GND  14 |_____________| 15  F3   nc
	 */
	PLS100(config, m_pla);

	MC6845(config, m_crtc, XTAL(19'170'000) / 9);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(alfaskop4110_state::crtc_update_row));
	// VSYNC should goto IRQ1 through some logic involving MIC PIA CRA bits 0 ( 1 == enable) & 1 (1 == positive edge)
	//m_crtc->out_vsync_callback().set(FUNC(alfaskop4110_state::crtc_vsync);
	//m_crtc->out_vsync_callback().set([this](bool state) { LOGIRQ("CRTC VSYNC: %d\n", state); });
	//m_crtc->out_vsync_callback().set("irq1", FUNC(input_merger_device::in_w<1>));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(19'170'000, 80 * 8, 0, 80 * 8, 400, 0, 400);
	m_screen->set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	PIA6821(config, m_mic_pia); // Main board PIA
	m_mic_pia->cb1_w(0);
	m_mic_pia->cb2_handler().set([this](offs_t offset, uint8_t data) { LOGMIC("->MIC PIA: CB2 write %d\n", data); });

	/*
	 * MIC PIA interface
	 *
	 * Port A (DDRA=0x7a)
	 * 0 - PA0 input  - not used
	 * 1 - PA1 output - KB reset P11 pin 23 at connector  1 == KB reset           0 == no KB reset
	 * 2 - PA2 input  - MCP test mode                     1 == no test mode       0 == in test mode,
	 * 3 - PA3 output - not used (in DTC)
	 * 4 - PA4 output - not used (in DTC)
	 * 5 - PA5 output - Interrupt enable                  1 == Int. out on P1:7   0 == no Int. out
	 * 6 - PA6 output - I4 latch enable                   1 == I4 will be latched 0 == no I4 latch
	 * 7 - PA7 input  - Button/MCP NMI                    1 == NMI from DU button 0 == NMI from MCP P4:1=low
	 * Note: At initialization a KB reset pulse will be sent as DDRA contains all zeros: PA I functions as a
	 *       high impedance input: "active level" for KB reset generation.
	 *
	 * Port B (DDRB=0xff)
	 * 0 - PB0 output - Reset PC-error                    1 == Reset PC error FF  0 == Memory PC used
	 *                                                         or PC not used
	 * 1 - PB1 output - VMAX/VMA 1 MPU                    1 == VMAX gen by MPU    0 == VMA 1 gen by MPU
	 * 2 - PB2 output - VMAX/VMA 1 DMA                    1 == VMAX gen by DMA    0 == VMA 1 gen by DMA
	 * 3 - PB3 output - Display Memory                    1 == 4KB Display Memory 0 == 8KB Display Memory
	 * 4 - PB4 output - Option Character Generator        1 == Enabled to MIC bus 0 == Disabled from MIC bus
	 * 5 - PB5 output - MPU Addr                          1 == Mode 1             0 == Mode 0
	 * 6 - PB6 output - Reset                             1 == Reset all but MPU  0 == No reset
	 *                                                         and MIC PIA
	 * 7 - PB7 output - not used
	 */
	m_mic_pia->writepa_handler().set([this](offs_t offset, uint8_t data)
					{
						LOGMIC("->MIC PIA: Port A write %02x\n", data);
						LOGMIC(" PA1 - KBD reset %s\n", BIT(data, 1) ? "active" : "inactive");
						LOGMIC(" PA5 - Int out %s\n", BIT(data, 5) ? "enabled": "disabled");
						LOGMIC(" PA6 - I4 latch %s\n", BIT(data, 6) ? "enabled": "disabled");
					});

	m_mic_pia->writepb_handler().set([this](offs_t offset, uint8_t data)
					{
						LOGMIC("->MIC PIA: Port B write %02x\n", data);
						LOGMIC(" PB0 - Reset PC-error %s\n", BIT(data, 0) ? "active" : "inactive");
						LOGMIC(" PB1 - %s generated by MPU\n", BIT(data, 1) ? "VMAX" : "VMA 1");
						LOGMIC(" PB2 - %s generated by DMA\n", BIT(data, 2) ? "VMAX" : "VMA 1");
						LOGMIC(" PB3 - %sKB Display Memory\n", BIT(data, 3) ? "4" : "8");
						LOGMIC(" PB4 - Option Char Generator %s\n", BIT(data, 4) ? "enabled" : "disabled");
						LOGMIC(" PB5 - MPU Address Mode %s\n", BIT(data, 5) ? "1" : "0");
						LOGMIC(" PB6 - Reset of devices %s\n", BIT(data, 6) ? "active" : "inactive");
					});

	m_mic_pia->readpa_handler().set([this](offs_t offset) -> uint8_t
					{
						uint8_t data = (1U << 2); // MCU is not in test mode
						LOGMIC("<-MIC PIA: Port A read\n");
						LOGMIC(" PA2 - MCU test mode %s\n", BIT(data, 2) ? "inactive" : "active");
						return 0;
					});
	m_mic_pia->readpb_handler().set([this](offs_t offset) -> uint8_t { LOGMIC("<-MIC PIA: Port B read\n"); return 0;});
	m_mic_pia->ca1_w(0);
	m_mic_pia->ca2_w(0);

	PIA6821(config, m_dia_pia); // Display PIA, controls how the CRTC accesses memory etc
	m_dia_pia->cb1_w(0);
	m_dia_pia->cb2_handler().set([this](offs_t offset, uint8_t data) { LOGDIA("DIA PIA: CB2_w %d\n", data); });
	m_dia_pia->writepa_handler().set([this](offs_t offset, uint8_t data) { LOGDIA("DIA PIA: PA_w %02x\n", data); });
	m_dia_pia->writepb_handler().set([this](offs_t offset, uint8_t data) { LOGDIA("DIA PIA: PB_w %02x\n", data); });
	m_dia_pia->readpa_handler().set([this](offs_t offset) -> uint8_t { LOGDIA("DIA PIA: PA_r\n"); return 0;});
	m_dia_pia->readpb_handler().set([this](offs_t offset) -> uint8_t { LOGDIA("DIA PIA: PB_r\n"); return 0;});
	m_dia_pia->ca1_w(0);
	m_dia_pia->ca2_w(0);

	ACIA6850(config, m_kbd_acia, 0);
	//CLOCK(config, "acia_clock", ACIA_CLOCK).signal_handler().set(FUNC(alfaskop4110_state::write_acia_clock));
	m_kbd_acia->irq_handler().set("irq3", FUNC(input_merger_device::in_w<3>));

	MC6854(config, m_tia_adlc, XTAL(19'170'000) / 18); // TODO: attach IRQ by IRQ 7 through descrete interrupt prioritization instead
	//m_tia_adlc->out_irq_cb().set([this](bool state){ LOGDMA("TIA ADLC IRQ: %s\n", state == ASSERT_LINE ? "asserted" : "cleared"); m_maincpu->set_input_line(M6800_IRQ_LINE, state); });
	//m_tia_adlc->out_irq_cb().set([this](bool state){ LOGDMA("TIA ADLC IRQ: %s\n", state == ASSERT_LINE ? "asserted" : "cleared"); m_maincpu->set_input_line(M6800_IRQ_LINE, state); });
	m_tia_adlc->out_irq_cb().set("irq7", FUNC(input_merger_device::in_w<7>));
	m_tia_adlc->out_rdsr_cb().set([this](bool state){ LOGDMA("TIA ADLC RDSR: %d\n", state); m_tia_dma->dreq_w<1>(state); });
	m_tia_adlc->out_tdsr_cb().set([this](bool state){ LOGDMA("TIA ADLC TDSR: %d\n", state); m_tia_dma->dreq_w<0>(state); });

	MC6844(config, m_tia_dma, XTAL(19'170'000) / 18);
	//m_tia_dma->out_int_callback().set([this](bool state){ LOGDMA("TIA DMA IRQ: %d\n", state); }); // Used as DEND (end of dma) towards the ADLC through some logic
	m_tia_dma->out_drq1_callback().set([this](bool state){ LOGDMA("TIA DMA DRQ1: %d\n", state); m_tia_dma->dgrnt_w(state); });
	//m_tia_dma->out_drq2_callback().set([this](bool state){ LOGDMA("TIA DMA DRQ2: %d\n", state); }); // Not connected
	m_tia_dma->in_ior_callback<1>().set([this](offs_t offset) -> uint8_t { return m_tia_adlc->dma_r(); });
	m_tia_dma->out_memw_callback().set([this](offs_t offset, uint8_t data) { m_maincpu->space(AS_PROGRAM).write_byte(offset, data); });

	/* 74LS273 latch inputs of interruptt sources */
	INPUT_MERGER_ANY_HIGH(config, "irq0").output_handler().set(FUNC(alfaskop4110_state::irq_w<0>));
	INPUT_MERGER_ANY_HIGH(config, "irq1").output_handler().set(FUNC(alfaskop4110_state::irq_w<1>));
	INPUT_MERGER_ANY_HIGH(config, "irq2").output_handler().set(FUNC(alfaskop4110_state::irq_w<2>));
	INPUT_MERGER_ANY_HIGH(config, "irq3").output_handler().set(FUNC(alfaskop4110_state::irq_w<3>));
	INPUT_MERGER_ANY_HIGH(config, "irq4").output_handler().set(FUNC(alfaskop4110_state::irq_w<4>));
	INPUT_MERGER_ANY_HIGH(config, "irq5").output_handler().set(FUNC(alfaskop4110_state::irq_w<5>));
	INPUT_MERGER_ANY_HIGH(config, "irq6").output_handler().set(FUNC(alfaskop4110_state::irq_w<6>));
	INPUT_MERGER_ANY_HIGH(config, "irq7").output_handler().set(FUNC(alfaskop4110_state::irq_w<7>));
}

void alfaskop4110_state::machine_start()
{
	save_item(NAME(m_irq));
	save_item(NAME(m_imsk));

	m_poll_start_timer = timer_alloc(FUNC(alfaskop4110_state::poll_start), this);
	m_poll_start_timer->adjust(attotime::from_msec(5000));

	m_poll_bit_timer = timer_alloc(FUNC(alfaskop4110_state::poll_bit), this);
	m_poll_bit_timer->adjust(attotime::never);
}

// Debug - inserts a poll SDLC frame through the ADLC, it ends up at address 0x140 in RAM through DMA
TIMER_CALLBACK_MEMBER(alfaskop4110_state::poll_start)
{
	/* The serial transfer of 8 bits is complete. Now trigger INT7. */
	LOGADLC("Starting poll message\n");
	m_tia_adlc->set_rx(0);
	m_poll_bit_timer->adjust(attotime::from_hz(300000));
}

TIMER_CALLBACK_MEMBER(alfaskop4110_state::poll_bit)
{
	if (flank)
	{
		if (index != 0 && index != 7 && BIT(txBuf[index], (pos % 8)) && ones == 5)
		{
			LOGADLC("%d%c", 2, (pos % 8) == 7 ? '\n' : ' ');
			m_tia_adlc->set_rx(0);
			ones = 0;
		}
		else
		{
			LOGADLC("%d%c", BIT(txBuf[index], (pos % 8)), (pos % 8) == 7 ? '\n' : ' ');
			m_tia_adlc->set_rx(BIT(txBuf[index], (pos % 8)));
			if (index != 0 && index != 7 && BIT(txBuf[index], (pos % 8)))
				ones++;
			else
				ones = 0;
			pos++;
			index = pos / 8;
		}
	}
	m_tia_adlc->rxc_w(flank ? 1 : 0);
	if (index < 8)
		m_poll_bit_timer->adjust(attotime::from_hz(300000) / 2);
	flank = !flank;
}

void alfaskop4110_state::machine_reset()
{
	m_irq = 0x00;
}

void alfaskop4120_state::alfaskop4120(machine_config &config)
{
	/* basic machine hardware */
	M6800(config, m_maincpu, XTAL(19'170'000) / 18); // Verified from service manual
	m_maincpu->set_addrmap(AS_PROGRAM, &alfaskop4120_state::mem_map);

	PIA6821(config, m_mic_pia); // Main Board PIA
	PIA6821(config, m_fdapia); // Floppy Disk PIA
}

void alfaskop4101_state::alfaskop4101(machine_config &config)
{
	/* basic machine hardware */
	M6800(config, m_maincpu, XTAL(19'170'000) / 18); // Verified from service manual
	m_maincpu->set_addrmap(AS_PROGRAM, &alfaskop4101_state::mem_map);

	PIA6821(config, m_mic_pia); // Main board PIA
}

/* ROM definitions */
ROM_START( alfaskop4110 ) // Display Unit
	ROM_REGION( 0x800, "roms", ROMREGION_ERASEFF )
	ROM_LOAD( "e3405870205201.bin", 0x0000, 0x0800, CRC(23f20f7f) SHA1(6ed008e309473ab966c6b0d42a4f87c76a7b1d6e))
	ROM_REGION( 0x800, "chargen", ROMREGION_ERASEFF )
	ROM_LOAD( "e3405972067500.bin", 0x0000, 0x0400, CRC(fb12b549) SHA1(53783f62c5e51320a53e053fbcf8b3701d8a805f))
	ROM_LOAD( "e3405972067600.bin", 0x0400, 0x0400, CRC(c7069d65) SHA1(587efcbee036d4c0c5b936cc5d7b1f97b6fe6dba))

	ROM_REGION( 0xff, PLA1_TAG, 0 )
	ROM_LOAD( "dtc_a_e34062_0100_ic50_e3405970303601.bin", 0x00, 0xfa, CRC(16339b7a) SHA1(9b313a7526460dc9bcedfda25bece91c924f0ddc) ) // Signetics_N82S100N.bin DATAIO format
ROM_END

ROM_START( alfaskop4120 ) // Flexible Disk Unit
	ROM_REGION( 0x800, "roms", ROMREGION_ERASEFF )
	ROM_LOAD( "alfaskop4120.bin", 0x0000, 0x0800, NO_DUMP)
ROM_END

ROM_START( alfaskop4101 ) // Communication Processor Unit
	ROM_REGION( 0x800, "roms", ROMREGION_ERASEFF )
	ROM_LOAD( "alfaskop4101.bin", 0x0000, 0x0800, NO_DUMP)
ROM_END

} // anonymous namespace


/* Driver(S) */

// Only 4101 may exist as a driver in the end making the 4110 and 4120 as slots devices on the SS3 bus, time will tell

//    YEAR  NAME          PARENT  COMPAT  MACHINE       INPUT         CLASS               INIT        COMPANY      FULLNAME       FLAGS
COMP( 1984, alfaskop4110, 0,      0,      alfaskop4110, alfaskop4110, alfaskop4110_state, empty_init, "Ericsson",  "Alfaskop Display Unit 4110", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP( 1984, alfaskop4120, 0,      0,      alfaskop4120, alfaskop4120, alfaskop4120_state, empty_init, "Ericsson",  "Alfaskop Flexible Disk Unit 4120", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP( 1984, alfaskop4101, 0,      0,      alfaskop4101, alfaskop4101, alfaskop4101_state, empty_init, "Ericsson",  "Alfaskop Communication Processor 4101", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



alphajuno.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Roland αJuno/SynthPlus 10 synthesizers.

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

#include "emu.h"
//#include "bus/midi/midi.h"
#include "cpu/mcs51/mcs51.h"
#include "mb62h195.h"
#include "mb63h149.h"
#include "machine/nvram.h"
#include "machine/rescap.h"
#include "machine/upd7001.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"


namespace {

class alphajuno_state : public driver_device
{
public:
	alphajuno_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "lcdc")
	{
	}

	void ajuno1(machine_config &config);
	void ajuno2(machine_config &config);
	void mks50(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	HD44780_PIXEL_UPDATE(lcd_pixel_update);

	void lcd_w(offs_t offset, u8 data);

	void prog_map(address_map &map) ATTR_COLD;
	void ajuno1_ext_map(address_map &map) ATTR_COLD;
	void ajuno2_ext_map(address_map &map) ATTR_COLD;
	void mks50_ext_map(address_map &map) ATTR_COLD;

	void palette_init(palette_device &palette);

	required_device<mcs51_cpu_device> m_maincpu;
	required_device<hd44780_device> m_lcdc;
};

void alphajuno_state::machine_reset()
{
}

HD44780_PIXEL_UPDATE(alphajuno_state::lcd_pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 8)
		bitmap.pix(y, (line * 8 + pos) * 6 + x) = state;
}


void alphajuno_state::lcd_w(offs_t offset, u8 data)
{
	if (offset == 0)
		m_lcdc->control_w(data);
	else
		m_lcdc->data_w(data);
}

void alphajuno_state::prog_map(address_map &map)
{
	map(0x0000, 0x3fff).rom().region("program", 0);
}

void alphajuno_state::ajuno1_ext_map(address_map &map)
{
	map(0x2000, 0x27ff).mirror(0xd800).ram().share("nvram");
	map(0x8000, 0x8008).w(FUNC(alphajuno_state::lcd_w));
}

void alphajuno_state::ajuno2_ext_map(address_map &map)
{
	map(0x2000, 0x27ff).mirror(0xd800).ram().share("nvram");
	map(0x5000, 0x57ff).mirror(0x800).rw("keyscan", FUNC(mb63h149_device::read), FUNC(mb63h149_device::write));
	map(0x8000, 0x8008).w(FUNC(alphajuno_state::lcd_w));
}

void alphajuno_state::mks50_ext_map(address_map &map)
{
	map(0x8000, 0x8008).w(FUNC(alphajuno_state::lcd_w));
	map(0xe000, 0xffff).ram().share("nvram");
}

static INPUT_PORTS_START(ajuno1)
INPUT_PORTS_END

static INPUT_PORTS_START(ajuno2)
INPUT_PORTS_END

static INPUT_PORTS_START(mks50)
INPUT_PORTS_END

void alphajuno_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(131, 136, 139));
	palette.set_pen_color(1, rgb_t( 92,  83,  88));
}

void alphajuno_state::ajuno1(machine_config &config)
{
	I8032(config, m_maincpu, 12_MHz_XTAL); // P8032AH
	m_maincpu->set_addrmap(AS_PROGRAM, &alphajuno_state::prog_map);
	m_maincpu->set_addrmap(AS_IO, &alphajuno_state::ajuno1_ext_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // TC5517APL + battery

	mb62h195_device &io(MB62H195(config, "io"));
	io.lc_callback().set(m_lcdc, FUNC(hd44780_device::write));
	io.sout_callback().set("adc", FUNC(upd7001_device::si_w));
	io.sck_callback().set("adc", FUNC(upd7001_device::sck_w));
	io.sin_callback().set("adc", FUNC(upd7001_device::so_r));
	io.adc_callback().set("adc", FUNC(upd7001_device::cs_w));

	//MB87123(config, "dco", 12_MHz_XTAL);

	UPD7001(config, "adc", RES_K(27), CAP_P(47));

	// LCD: LM16155A or LM16155B
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6*16, 8*1);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(alphajuno_state::palette_init), 2);

	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 8);
	m_lcdc->set_pixel_update_cb(FUNC(alphajuno_state::lcd_pixel_update));
	m_lcdc->set_busy_factor(0.005f);
}

void alphajuno_state::ajuno2(machine_config &config)
{
	ajuno1(config);
	m_maincpu->set_addrmap(AS_IO, &alphajuno_state::ajuno2_ext_map);

	mb63h149_device &keyscan(MB63H149(config, "keyscan", 12_MHz_XTAL));
	keyscan.int_callback().set_inputline(m_maincpu, MCS51_INT0_LINE);
}

void alphajuno_state::mks50(machine_config &config)
{
	I80C31(config, m_maincpu, 12_MHz_XTAL); // MSM80C31P
	m_maincpu->set_addrmap(AS_PROGRAM, &alphajuno_state::prog_map);
	m_maincpu->set_addrmap(AS_IO, &alphajuno_state::mks50_ext_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // TC5564PL-20 + battery

	mb62h195_device &io(MB62H195(config, "io"));
	io.lc_callback().set(m_lcdc, FUNC(hd44780_device::write));

	//MB87123(config, "dco", 12_MHz_XTAL);

	// LCD Unit: DM011Z-1DL3
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6*16, 8*1);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(alphajuno_state::palette_init), 2);

	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 8);
	m_lcdc->set_pixel_update_cb(FUNC(alphajuno_state::lcd_pixel_update));
	m_lcdc->set_busy_factor(0.05f);
}

// Original EPROM labels specify major and minor revisions with punch grids; "U" (update?) tag is separate.
// Version strings may be inconsistent with EPROM labels.
ROM_START(ajuno1)
	ROM_REGION(0x4000, "program", 0)
	//ROM_SYSTEM_BIOS(0, "v26", "Version 2.6")
	ROM_LOAD("u__ju-1_2_6.ic10", 0x0000, 0x4000, CRC(9797fd5b) SHA1(0d2e24f8c5f646279985a34ac8bf7c0b9354d32b)) // M5L27128K-2
ROM_END

ROM_START(ajuno2)
	ROM_REGION(0x4000, "program", 0)
	ROM_SYSTEM_BIOS(0, "v25", "Version 2.5")
	ROMX_LOAD("ju-2_2_5__u.ic24", 0x0000, 0x4000, CRC(13b9e68e) SHA1(28a8207a5cd63ababd61d7a46df102ea7116a898), ROM_BIOS(0)) // NEC D27128D-2
	ROM_SYSTEM_BIOS(1, "v25oled", "Version 2.5 (OLED Display Mod)") // http://wp.visuanetics.nl/oled-display-for-alpha-juno-2/
	ROMX_LOAD("ju2-2_5-modified-for-oled-final.bin", 0x0000, 0x4000, CRC(1bca5bc6) SHA1(22e9c71af4b5f3e185f767740e61e5332c0a979f), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v24", "Version 2.4")
	ROMX_LOAD("ju-2_2_4.ic24", 0x0000, 0x4000, CRC(bfedda17) SHA1(27eee472befdbc7d7ed0caaf359775d8ff3c836a), ROM_BIOS(2)) // M5M27C128
ROM_END

ROM_START(hs80)
	ROM_REGION(0x4000, "program", 0)
	ROM_LOAD("roland hs-80 v5.0 eprom -dom- - 27128", 0x0000, 0x4000, CRC(94e85807) SHA1(128eec37fb6dcac6ec73d3ca544c28dbf7dbf9b2))
ROM_END

ROM_START(mks50)
	ROM_REGION(0x4000, "program", 0)
	ROM_LOAD("mks-50_v1.02.ic7", 0x0000, 0x4000, CRC(a342f90e) SHA1(8eed986051abfdf55167c179dc7c7f0822a3ba0c))
ROM_END

} // anonymous namespace


SYST(1985, ajuno1, 0, 0, ajuno1, ajuno1, alphajuno_state, empty_init, "Roland", "Alpha Juno-1 (JU-1) Programmable Polyphonic Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
//SYST(1985, hs10, ajuno1, 0, ajuno1, ajuno1, alphajuno_state, empty_init, "Roland", "SynthPlus 10 (HS-10) Programmable Polyphonic Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1986, ajuno2, 0, 0, ajuno2, ajuno2, alphajuno_state, empty_init, "Roland", "Alpha Juno-2 (JU-2) Programmable Polyphonic Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1986, hs80, ajuno2, 0, ajuno2, ajuno2, alphajuno_state, empty_init, "Roland", "HS-80 Programmable Polyphonic Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1987, mks50, 0, 0, mks50, mks50, alphajuno_state, empty_init, "Roland", "MKS-50 Synthesizer Module", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



alphasma.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

        AlphaSmart Pro

        driver by Sandro Ronco

    TODO:
    - ADB and PS/2
    - charset ROM is wrong
    - asma2k reads from nonexistent internal register at 0x0001
      (probably a bug, since the same code exists in both BIOSes)

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

#include "emu.h"
#include "cpu/mc68hc11/mc68hc11.h"
#include "machine/nvram.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"


namespace {

class alphasmart_state : public driver_device
{
public:
	alphasmart_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "ks0066_%u", 0U)
		, m_nvram(*this, "nvram")
		, m_nvram_base(*this, "nvram", 0x20000, ENDIANNESS_BIG)
		, m_rambank(*this, "rambank")
		, m_keyboard(*this, "COL.%u", 0)
		, m_battery_status(*this, "BATTERY")
	{
	}

	void alphasmart(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(kb_irq);

protected:
	required_device<mc68hc11_cpu_device> m_maincpu;
	required_device_array<hd44780_device, 2> m_lcdc;
	required_device<nvram_device> m_nvram;
	memory_share_creator<uint8_t> m_nvram_base;
	required_memory_bank m_rambank;
	required_ioport_array<16> m_keyboard;
	required_ioport m_battery_status;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	void alphasmart_palette(palette_device &palette) const;
	virtual uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	uint8_t kb_r();
	void kb_matrixl_w(uint8_t data);
	void kb_matrixh_w(uint8_t data);
	uint8_t port_a_r();
	virtual void port_a_w(uint8_t data);
	uint8_t port_d_r();
	void port_d_w(uint8_t data);
	void update_lcdc(bool lcdc0, bool lcdc1);

	void alphasmart_mem(address_map &map) ATTR_COLD;

	uint8_t           m_matrix[2];
	uint8_t           m_port_a;
	uint8_t           m_port_d;
	std::unique_ptr<bitmap_ind16> m_tmp_bitmap;
};

class asma2k_state : public alphasmart_state
{
public:
	asma2k_state(const machine_config &mconfig, device_type type, const char *tag)
		: alphasmart_state(mconfig, type, tag)
		, m_io_view(*this, "io")
		, m_dictbank(*this, "dictbank")
	{
	}

	void asma2k(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void lcd_ctrl_w(uint8_t data);
	virtual void port_a_w(uint8_t data) override;

	void asma2k_mem(address_map &map) ATTR_COLD;

	memory_view m_io_view;
	required_memory_bank m_dictbank;

	uint8_t m_lcd_ctrl;
};

INPUT_CHANGED_MEMBER(alphasmart_state::kb_irq)
{
	// IRQ on every key transition
	m_maincpu->set_input_line(MC68HC11_IRQ_LINE, ASSERT_LINE);
}

uint8_t alphasmart_state::kb_r()
{
	uint16_t matrix = (m_matrix[1]<<8) | m_matrix[0];
	uint8_t data = 0xff;

	for(int i=0; i<16; i++)
		if (!(matrix & (1<<i)))
			data &= m_keyboard[i]->read();

	return data;
}

void alphasmart_state::kb_matrixl_w(uint8_t data)
{
	m_matrix[0] = data;
	m_maincpu->set_input_line(MC68HC11_IRQ_LINE, CLEAR_LINE);
}

void alphasmart_state::kb_matrixh_w(uint8_t data)
{
	m_matrix[1] = data;
}

uint8_t alphasmart_state::port_a_r()
{
	return (m_port_a & 0xfd) | (m_battery_status->read() << 1);
}

void alphasmart_state::update_lcdc(bool lcdc0, bool lcdc1)
{
	if (m_matrix[1] & 0x04)
	{
		uint8_t lcdc_data = 0;

		if (lcdc0)
			lcdc_data |= m_lcdc[0]->read(BIT(m_matrix[1], 1));

		if (lcdc1)
			lcdc_data |= m_lcdc[1]->read(BIT(m_matrix[1], 1));

		m_port_d = (m_port_d & 0xc3) | (lcdc_data>>2);
	}
	else
	{
		uint8_t lcdc_data = (m_port_d<<2) & 0xf0;

		if (lcdc0)
			m_lcdc[0]->write(BIT(m_matrix[1], 1), lcdc_data);

		if (lcdc1)
			m_lcdc[1]->write(BIT(m_matrix[1], 1), lcdc_data);
	}
}

void alphasmart_state::port_a_w(uint8_t data)
{
	uint8_t changed = (m_port_a ^ data) & data;
	update_lcdc(changed & 0x80, changed & 0x20);
	m_rambank->set_entry(((data>>3) & 0x01) | ((data>>4) & 0x02));
	m_port_a = data;
}

uint8_t alphasmart_state::port_d_r()
{
	return m_port_d;
}

void alphasmart_state::port_d_w(uint8_t data)
{
	m_port_d = data;
}


void alphasmart_state::alphasmart_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).bankrw("rambank");
	map(0x8000, 0xffff).rom().region("maincpu", 0);
	map(0x8000, 0x8000).rw(FUNC(alphasmart_state::kb_r), FUNC(alphasmart_state::kb_matrixh_w));
	map(0xc000, 0xc000).w(FUNC(alphasmart_state::kb_matrixl_w));
}

void asma2k_state::lcd_ctrl_w(uint8_t data)
{
	uint8_t changed = (m_lcd_ctrl ^ data) & data;
	update_lcdc(changed & 0x01, changed & 0x02);
	m_dictbank->set_entry((m_port_a & 0x30) >> 3 | (data & 0x80) >> 7);
	m_lcd_ctrl = data;
}

void asma2k_state::port_a_w(uint8_t data)
{
	m_io_view.select(BIT(data, 6));

	m_rambank->set_entry(((data>>4) & 0x03));
	m_dictbank->set_entry((data & 0x30) >> 3 | (m_lcd_ctrl & 0x80) >> 7);
	m_port_a = data;
}


void asma2k_state::asma2k_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).view(m_io_view);
	m_io_view[0](0x2000, 0x2000).rw(FUNC(asma2k_state::kb_r), FUNC(asma2k_state::kb_matrixh_w));
	m_io_view[0](0x4000, 0x4000).w(FUNC(asma2k_state::lcd_ctrl_w));
	m_io_view[0](0x4000, 0x7fff).bankr("dictbank");
	m_io_view[1](0x0000, 0x7fff).bankrw("rambank");
	map(0x8000, 0xffff).rom().region("maincpu", 0);
	map(0x9000, 0x9000).w(FUNC(asma2k_state::kb_matrixl_w));
}

/* Input ports */
static INPUT_PORTS_START( alphasmart )
	PORT_START("COL.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)    PORT_CHAR(UCHAR_MAMEKEY(F8))   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)    PORT_CHAR(UCHAR_MAMEKEY(F7))   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)     PORT_CHAR('l') PORT_CHAR('L')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)     PORT_CHAR('9') PORT_CHAR('(')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)     PORT_CHAR('o') PORT_CHAR('O')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)  PORT_CHAR('.') PORT_CHAR('>')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_START("COL.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)     PORT_CHAR('5') PORT_CHAR('%')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)     PORT_CHAR('g') PORT_CHAR('G')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)     PORT_CHAR('t') PORT_CHAR('T')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)     PORT_CHAR('b') PORT_CHAR('B')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)     PORT_CHAR('f') PORT_CHAR('F')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)     PORT_CHAR('4') PORT_CHAR('$')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)     PORT_CHAR('r') PORT_CHAR('R')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)     PORT_CHAR('v') PORT_CHAR('V')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_START("COL.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)     PORT_CHAR('6') PORT_CHAR('^')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)     PORT_CHAR('h') PORT_CHAR('H')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)     PORT_CHAR('y') PORT_CHAR('Y')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)     PORT_CHAR('n') PORT_CHAR('N')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)     PORT_CHAR('j') PORT_CHAR('J')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)     PORT_CHAR('7') PORT_CHAR('&')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)     PORT_CHAR('u') PORT_CHAR('U')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)     PORT_CHAR('m') PORT_CHAR('M')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_START("COL.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'')    PORT_CHAR('\"') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)     PORT_CHAR('0') PORT_CHAR(')')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)     PORT_CHAR('p') PORT_CHAR('P')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("COL.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("COL.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_END)  PORT_CHAR(UCHAR_MAMEKEY(END))    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_START("COL.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LWIN) PORT_CODE(KEYCODE_PGUP)  PORT_NAME("Left Command") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RWIN) PORT_CODE(KEYCODE_PGDN) PORT_NAME("Right Command") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)//
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)  PORT_NAME("Clear File") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("COL.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F12) PORT_NAME("Send") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)   PORT_CHAR(UCHAR_MAMEKEY(UP))     PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT)   PORT_CHAR(UCHAR_MAMEKEY(INSERT)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_START("COL.8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("COL.9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT) PORT_NAME("Left Alt/Option") PORT_CHAR(UCHAR_MAMEKEY(LALT))   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RALT) PORT_NAME("Right Alt/Option") PORT_CHAR(UCHAR_MAMEKEY(LALT))   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_START("COL.10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=')    PORT_CHAR('+') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)   PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')    PORT_CHAR('}') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)    PORT_CHAR('k')  PORT_CHAR('K')   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)    PORT_CHAR('8')  PORT_CHAR('*')   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)    PORT_CHAR('i')  PORT_CHAR('I')   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_START("COL.11")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9)   PORT_NAME("Pause") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)        PORT_CHAR(UCHAR_MAMEKEY(F5))     PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Backspace") PORT_CHAR(UCHAR_MAMEKEY(BACKSPACE)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)     PORT_CHAR(' ') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10)  PORT_NAME("ScrLk") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)   PORT_NAME("Return") PORT_CHAR(13) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_START("COL.12")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)   PORT_CHAR(UCHAR_MAMEKEY(F2))    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)   PORT_CHAR(UCHAR_MAMEKEY(F4))    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)   PORT_CHAR(UCHAR_MAMEKEY(F3))    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)    PORT_CHAR('d')  PORT_CHAR('D')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)    PORT_CHAR('3')  PORT_CHAR('#')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)    PORT_CHAR('e')  PORT_CHAR('E')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)    PORT_CHAR('c')  PORT_CHAR('C')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_START("COL.13")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)   PORT_CHAR(UCHAR_MAMEKEY(F1))    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)    PORT_CHAR('s')  PORT_CHAR('S')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)    PORT_CHAR('2')  PORT_CHAR('@')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)    PORT_CHAR('w')  PORT_CHAR('W')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)    PORT_CHAR('x')  PORT_CHAR('X')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_START("COL.14")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('`') PORT_CHAR('~')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)  PORT_CHAR(UCHAR_MAMEKEY(ESC))   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)  PORT_CHAR('\t')   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)    PORT_CHAR('a')  PORT_CHAR('A')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)    PORT_CHAR('1')  PORT_CHAR('!')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)    PORT_CHAR('q')  PORT_CHAR('Q')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)    PORT_CHAR('z')  PORT_CHAR('Z')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_START("COL.15")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_MAMEKEY(RSHIFT)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)

	PORT_START("BATTERY")
	PORT_CONFNAME(0x01, 0x01, "Battery status")
	PORT_CONFSETTING (0x00, DEF_STR(Low))
	PORT_CONFSETTING (0x01, DEF_STR(Normal))
INPUT_PORTS_END

static INPUT_PORTS_START( asma2k )
	PORT_START("COL.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)   PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_NAME("F6 (File 6)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)     PORT_CHAR('k') PORT_CHAR('k')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)     PORT_CHAR('i') PORT_CHAR('I')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('_') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)     PORT_CHAR('8') PORT_CHAR('*')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("COL.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT) PORT_NAME("Left Alt/Option") PORT_CHAR(UCHAR_MAMEKEY(LALT))   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RCONTROL) PORT_NAME("Right Alt/Option") PORT_CHAR(UCHAR_MAMEKEY(RALT))   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("COL.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)   PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_NAME("F7 (File 7)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)     PORT_CHAR('l') PORT_CHAR('L')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)     PORT_CHAR('o') PORT_CHAR('O')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)   PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_NAME("F8 (File 8)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)     PORT_CHAR('9') PORT_CHAR('(')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)  PORT_CHAR('.') PORT_CHAR('>')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("COL.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'')    PORT_CHAR('\"') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)     PORT_CHAR('p') PORT_CHAR('P')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)     PORT_CHAR('0') PORT_CHAR(')')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_START("COL.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LWIN) PORT_CODE(KEYCODE_PGUP)  PORT_NAME("Command") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)  PORT_CHAR(UCHAR_MAMEKEY(NUMLOCK)) PORT_NAME("Clear File") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(ESC))    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_START("COL.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_END)  PORT_CHAR(UCHAR_MAMEKEY(END))    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_START("COL.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_START("COL.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11)  PORT_CHAR(UCHAR_MAMEKEY(F11)) PORT_NAME("Find") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)   PORT_CHAR(UCHAR_MAMEKEY(UP))     PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_START("COL.8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("COL.9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Delete") PORT_CHAR(UCHAR_MAMEKEY(BACKSPACE)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)   PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_NAME("F5 (File 5)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9)   PORT_CHAR(UCHAR_MAMEKEY(F9)) PORT_NAME("Print") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10)  PORT_CHAR(UCHAR_MAMEKEY(F10)) PORT_NAME("Spell Check") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)   PORT_NAME("Return") PORT_CHAR(13) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)     PORT_CHAR(' ') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_START("COL.10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)   PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_NAME("F3 (File 3)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)   PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_NAME("F4 (File 4)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)    PORT_CHAR('d')  PORT_CHAR('D')   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)    PORT_CHAR('e')  PORT_CHAR('E')   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)   PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_NAME("F2 (File 2)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)    PORT_CHAR('3')  PORT_CHAR('#')   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)    PORT_CHAR('c')  PORT_CHAR('C')   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("COL.11")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)    PORT_CHAR('s')  PORT_CHAR('S')   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)    PORT_CHAR('w')  PORT_CHAR('W')   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)   PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_NAME("F1 (File 1)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)    PORT_CHAR('2')  PORT_CHAR('@')   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)    PORT_CHAR('x')  PORT_CHAR('X')   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("COL.12")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)  PORT_CHAR('\t')   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)    PORT_CHAR('a')  PORT_CHAR('A')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)    PORT_CHAR('q')  PORT_CHAR('Q')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('`') PORT_CHAR('~')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)    PORT_CHAR('1')  PORT_CHAR('!')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)    PORT_CHAR('z')  PORT_CHAR('z')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_START("COL.13")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)    PORT_CHAR('t')  PORT_CHAR('T')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)    PORT_CHAR('g')  PORT_CHAR('G')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)    PORT_CHAR('f')  PORT_CHAR('F')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)    PORT_CHAR('r')  PORT_CHAR('R')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)    PORT_CHAR('5')  PORT_CHAR('%')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)    PORT_CHAR('4')  PORT_CHAR('$')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)    PORT_CHAR('v')  PORT_CHAR('V')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)    PORT_CHAR('b')  PORT_CHAR('B')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_START("COL.14")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_MAMEKEY(RSHIFT)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("COL.15")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)    PORT_CHAR('y')  PORT_CHAR('Y')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)    PORT_CHAR('h')  PORT_CHAR('H')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)    PORT_CHAR('j')  PORT_CHAR('J')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)    PORT_CHAR('u')  PORT_CHAR('U')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)    PORT_CHAR('6')  PORT_CHAR('^')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)    PORT_CHAR('7')  PORT_CHAR('&')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)    PORT_CHAR('m')  PORT_CHAR('M')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)    PORT_CHAR('n')  PORT_CHAR('N')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphasmart_state::kb_irq), 0)

	PORT_START("BATTERY")
	PORT_CONFNAME(0x01, 0x01, "Battery status")
	PORT_CONFSETTING (0x00, DEF_STR(Low))
	PORT_CONFSETTING (0x01, DEF_STR(Normal))
INPUT_PORTS_END

void alphasmart_state::alphasmart_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}

uint32_t alphasmart_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	m_lcdc[0]->screen_update(screen, *m_tmp_bitmap, cliprect);
	copybitmap(bitmap, *m_tmp_bitmap, 0, 0, 0, 0, cliprect);
	m_lcdc[1]->screen_update(screen, *m_tmp_bitmap, cliprect);
	copybitmap(bitmap, *m_tmp_bitmap, 0, 0, 0, 18,cliprect);
	return 0;
}

void alphasmart_state::machine_start()
{
	m_rambank->configure_entries(0, 4, &m_nvram_base[0], 0x8000);

	m_tmp_bitmap = std::make_unique<bitmap_ind16>(6 * 40, 9 * 4);
}

void asma2k_state::machine_start()
{
	alphasmart_state::machine_start();

	m_dictbank->configure_entries(0, 8, memregion("spellcheck")->base(), 0x4000);
	m_dictbank->set_entry(0);
}

void alphasmart_state::machine_reset()
{
	m_rambank->set_entry(0);
	m_matrix[0] = m_matrix[1] = 0;
	m_port_a = 0;
	m_port_d = 0;
}

void alphasmart_state::alphasmart(machine_config &config)
{
	/* basic machine hardware */
	MC68HC11D0(config, m_maincpu, 8_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &alphasmart_state::alphasmart_mem);
	m_maincpu->in_pa_callback().set(FUNC(alphasmart_state::port_a_r));
	m_maincpu->out_pa_callback().set(FUNC(alphasmart_state::port_a_w));
	m_maincpu->in_pd_callback().set(FUNC(alphasmart_state::port_d_r));
	m_maincpu->out_pd_callback().set(FUNC(alphasmart_state::port_d_w));

	for (auto &lcdc : m_lcdc)
	{
		KS0066(config, lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
		lcdc->set_default_bios_tag("f05");
		lcdc->set_lcd_size(2, 40);
	}

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(alphasmart_state::screen_update));
	screen.set_size(6*40, 9*4);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(alphasmart_state::alphasmart_palette), 2);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);
}

void asma2k_state::asma2k(machine_config &config)
{
	alphasmart(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &asma2k_state::asma2k_mem);
}

// MCU: MC68HC11D0P
// NVRAM: KM681000ALP-7L (or TC551001BPL-85L) + CR2032 battery
// XTAL: 8.000MHz
// LCD: 2x KS0066F05 + 8x HD44100H
ROM_START( asmapro )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD( "alphasmartpro212.rom",  0x0000, 0x8000, CRC(896ddf1c) SHA1(c3c6a421c9ced92db97431d04b4a3f09a39de716) )   // Checksum 8D24 on label
ROM_END

// MCU: MC68HC11D0FN
// NVRAM: NEC D431000ACW-70LL + battery
// XTAL: SB8.000
// LCD: 2x KS0066F05 + 4x KS0063B
// Optional IrDA sub board
ROM_START( asma2k )
	ROM_REGION( 0x10000, "maincpu", 0 )
	/*
	    These dumps 33,253 bytes each, probably contain 32768 bytes of rom,
	    plus the remaining area is pal data for the mapper/io pal, all of
	    which is integrated onto one plcc44 chip called a zpsd211r.
	*/
	ROM_SYSTEM_BIOS( 0, "v314", "v3.14" )
	ROMX_LOAD( "alphasmart__2000__v3.1.4__h4.zpsd211r.plcc44.bin",  0x0000, 0x81e5, CRC(49487f6d) SHA1(e0b777dc68c671c31ba808e214fb9d2573b9a853), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v308", "v3.08" )
	ROMX_LOAD( "alphasmart__2000__v3.0.8.zpsd211r.plcc44.bin",  0x0000, 0x81e5, CRC(0b3b1a0c) SHA1(97878819188a1ec40052fbce9d5a5059728d5aec), ROM_BIOS(1) )

	ROM_REGION( 0x20000, "spellcheck", 0 )
	ROM_LOAD( "dictrom__v1.stm_m27c1001-1501.plcc32.bin", 0x00000, 0x20000, CRC(a143949c) SHA1(033094bb850c614008b4ecc2eefbcb01b8a2bcda) )
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT  COMPAT  MACHINE     INPUT       CLASS             INIT        COMPANY                           FULLNAME           FLAGS
COMP( 1995, asmapro, 0,      0,      alphasmart, alphasmart, alphasmart_state, empty_init, "Intelligent Peripheral Devices", "AlphaSmart Pro" , MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1997, asma2k,  0,      0,      asma2k,     asma2k,     asma2k_state,     empty_init, "Intelligent Peripheral Devices", "AlphaSmart 2000", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



alphasma3k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***************************************************************************************************

AlphaSmart 3000

TODO:
- SW traps asap after writing to uninitialized A0, going off the rails.
- Snippet at PC=400148 is supposed to transfer the rest of the IPL to main RAM,
  but it fails looping at PC=40016c cmpa.w A0, A1 (DragonBall-EZ core bug?)

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

PCB:
            ___________                                                                _____________
           /    ::    |_______________________________________________________________/             \
   _______/ ::  ::                              _________      _________              ________       \_____________
  ||RS232|      ::   28F008B3                  |________|     |________| PCB REV 2.8 |_______|  BATT        | USB |
  ||_____|      ::                 74HC574                                                    CR2032   XTAL |_____|
  |__                  HY62U8200                       HC30M                                          A120I0E     |
   __| SP3223ECA                                                                                                  |
 _|_                     DragonBall EZ                                                                 PDIUSBD11D |
|___|<-Power             MC68EZ328PU16V                                    :)                              _______|
  |                                                                   Rise and Shout                      |
  |                            XTAL                                   the AlphaSmart's                    |
  |__   __      _____                     SW        HC132A             Out!!!                             |
     | |__|    /     | HC132A     HC74A  on/off                                       ____________________|
     |________/      |_______________________________________________________________/

The later AlphaSmart models' firmware can be updated using the Manager application (Windows / Mac) and a USB cable.
Each update comprises two files, the "os" and the "smallos". Those files do not include the full Operating System image.
Two version updaters known:
- System 3 Neo Jul 11 2013, 09:44:53 + OS 3KNeo Small ROM, included with Manager 3.93
- System 3 Neo Jan 27 2010, 13:44:00 + OS 3KNeo Small ROM, included with Manager 3.60

    TODO:
    - Everything

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

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "machine/mc68328.h"
#include "machine/ram.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"

namespace
{

class alphasmart3k_state : public driver_device
{
public:
	alphasmart3k_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc0(*this, "ks0066_0")
		, m_lcdc1(*this, "ks0066_1")
		, m_ram(*this, RAM_TAG)
		, m_ipl(*this, "ipl")
	{
	}

	void alphasmart3k(machine_config &config);

protected:
	required_device<cpu_device> m_maincpu;
	required_device<hd44780_device> m_lcdc0;
	required_device<hd44780_device> m_lcdc1;
	required_device<ram_device> m_ram;
	required_region_ptr<u16> m_ipl;

	std::unique_ptr<bitmap_ind16> m_tmp_bitmap;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void main_map(address_map &map) ATTR_COLD;
};

void alphasmart3k_state::machine_start()
{
	address_space &space = m_maincpu->space(AS_PROGRAM);
	space.install_ram(0x00000000, m_ram->size() - 1, m_ram->pointer());
}

void alphasmart3k_state::machine_reset()
{
	// TODO: expects specific initialized 68k values, including registers?
	// dc.w 1111 is vector $2c
	memcpy(m_ram->pointer(), memregion("ipl")->base(), 0x100);
}

void alphasmart3k_state::main_map(address_map &map)
{
//  map(0x0000'0000, 0x0003'ffff).ram().share("ram");
	map(0x0040'0000, 0x004f'ffff).rom().region("ipl", 0);
}

static INPUT_PORTS_START( alphasmart3k )
INPUT_PORTS_END



void alphasmart3k_state::alphasmart3k(machine_config &config)
{
	// Basic machine hardware
	MC68EZ328(config, m_maincpu, 16'000'000); // MC68EZ328PU16V, clock unverified
	m_maincpu->set_addrmap(AS_PROGRAM, &alphasmart3k_state::main_map);

	// Values from AlphaSmart 2000, not confirmed for AlphaSmart 3000
	// AlphaSmart 3000 uses a Data Image CM4040 LCD display, LCD is 40x4 according to ref
	KS0066(config, m_lcdc0, 270'000); // TODO: Possibly wrong device type, needs confirmation; clock not measured, datasheet typical clock used
	m_lcdc0->set_default_bios_tag("f05");
	m_lcdc0->set_lcd_size(4, 40);
	KS0066(config, m_lcdc1, 270'000); // TODO: Possibly wrong device type, needs confirmation; clock not measured, datasheet typical clock used
	m_lcdc1->set_default_bios_tag("f05");
	m_lcdc1->set_lcd_size(4, 40);

	RAM(config, RAM_TAG).set_default_size("256K");

	SOFTWARE_LIST(config, "kapps_list").set_original("alphasmart_kapps");
}

// ROM definitions

ROM_START( asma3k )
	ROM_REGION16_BE( 0x100000, "ipl", 0 )
	ROM_LOAD16_WORD( "28f008b3.u1", 0x000000, 0x100000, CRC(73a24834) SHA1(a47e6a6d286feaba4e671a6373632222113f9276) )
ROM_END

} // anonymous namespace

//    YEAR  NAME    PARENT COMPAT MACHINE       INPUT         CLASS               INIT        COMPANY             FULLNAME           FLAGS
COMP( 2000, asma3k, 0,     0,     alphasmart3k, alphasmart3k, alphasmart3k_state, empty_init, "AlphaSmart, Inc.", "AlphaSmart 3000", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



alphatan.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/**********************************************************************

    Tantel AlphaTantel


    Hardware:
    ---------
    CPU:    R6504

    RAM:    M6810
            M5101 - battery backed
            2114 x 2 - video RAM

    Video: SAA5020 VTC
           SAA5050 VCG
           SAA5070 VIOP
           TEA1002

    TODO:
    - implement SAA5070 (datasheet required)
    - keyboard matrix (probably via SAA5070)
    - cassette interface (via SAA5070)
    - program mode switch

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

#include "emu.h"
#include "cpu/m6502/m6504.h"
#include "machine/nvram.h"
#include "machine/output_latch.h"
#include "video/tea1002.h"
#include "video/saa5050.h"
#include "bus/centronics/ctronics.h"
#include "imagedev/cassette.h"

#include "emupal.h"
#include "screen.h"


namespace {

class alphatan_state : public driver_device
{
public:
	alphatan_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_videoram(*this, "videoram")
		, m_nvram(*this, "nvram")
		, m_screen(*this, "screen")
		, m_tea1002(*this, "encoder")
		, m_palette(*this, "palette")
		, m_trom(*this, "saa5050")
		, m_centronics(*this, "centronics")
		, m_cent_data_out(*this, "cent_data_out")
		, m_cassette(*this, "cassette")
		, m_config(*this, "CONFIG")
		{ }

	void alphatan(machine_config &config);

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_shared_ptr<uint8_t> m_videoram;
	required_device<nvram_device> m_nvram;
	required_device<screen_device> m_screen;
	required_device<tea1002_device> m_tea1002;
	required_device<palette_device> m_palette;
	required_device<saa5050_device> m_trom;
	required_device<centronics_device> m_centronics;
	required_device<output_latch_device> m_cent_data_out;
	required_device<cassette_image_device> m_cassette;
	required_ioport m_config;

	void alphatan_map(address_map &map) ATTR_COLD;

	uint8_t saa5070_r(offs_t offset);
	void saa5070_w(offs_t offset, uint8_t data);
	void write400(offs_t offset, uint8_t data);
	void write600(offs_t offset, uint8_t data);

	void palette_init(palette_device &palette) const;
};

void alphatan_state::alphatan_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x007f).ram().mirror(0x0180); // MCM6810
	map(0x0200, 0x027f).ram().share("nvram"); // MCM5101
	map(0x0300, 0x0300).rw(FUNC(alphatan_state::saa5070_r), FUNC(alphatan_state::saa5070_w)); // write 0x20, 0x14, 0x0c, 0x18, 0x40, 0x90, 0x1f, 0xd4, 0x0f
	map(0x0400, 0x0400).w(FUNC(alphatan_state::write400)); // write 0x03, 0x06, 0x08, 0x00, 0x04, 0x07, 0x01, 0x05
	map(0x0500, 0x0500).w(m_cent_data_out, FUNC(output_latch_device::write));
	map(0x0600, 0x0600).w(FUNC(alphatan_state::write600)); // unknown, writes only 0x01, 0x02, or 0x03
	map(0x0700, 0x0700).portr("CONFIG");
	map(0x0800, 0x0fff).ram().share("videoram"); // 2x2114
	map(0x1000, 0x1fff).rom().region("maincpu", 0);
}


static INPUT_PORTS_START( alphatan )
	PORT_START("CONFIG")
	PORT_CONFNAME(0x10, 0x10, "Banner")
	PORT_CONFSETTING(0x00, "Granada")
	PORT_CONFSETTING(0x10, "Tantel")
INPUT_PORTS_END


uint8_t alphatan_state::saa5070_r(offs_t offset)
{
	uint8_t data = 0x00;

	//logerror("saa5070_r: %02x\n", data);

	return data;
}

void alphatan_state::saa5070_w(offs_t offset, uint8_t data)
{
	//logerror("saa5070_w: %02x\n", data);
}

void alphatan_state::write400(offs_t offset, uint8_t data)
{
	//logerror("write400: %02x\n", data);
}

void alphatan_state::write600(offs_t offset, uint8_t data)
{
	//logerror("write600: %02x\n", data);
}


void alphatan_state::palette_init(palette_device &palette) const
{
	for (int i = 0; i < 8; i++)
		palette.set_indirect_color(i, m_tea1002->color(i));
}

uint32_t alphatan_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const rgb_t *palette = m_palette->palette()->entry_list_raw();

	m_trom->dew_w(1);
	m_trom->dew_w(0);

	for (int y = 0; y < 24 * 20; y++)
	{
		int sy = y / 20;
		int x = 0;

		m_trom->lose_w(1);
		m_trom->lose_w(0);

		int ssy = m_trom->tlc_r() ? sy : sy - 1;
		offs_t video_ram_addr = ssy * 64;

		for (int sx = 0; sx < 40; sx++)
		{
			uint8_t code = m_videoram[video_ram_addr++];

			m_trom->write(code & 0x7f);

			m_trom->f1_w(1);
			m_trom->f1_w(0);

			for (int bit = 0; bit < 12; bit++)
			{
				m_trom->tr6_w(1);
				m_trom->tr6_w(0);

				int color = m_trom->get_rgb();

				if (BIT(code, 7)) color ^= 0x07;

				bitmap.pix(y, x++) = palette[color];
			}
		}
	}

	return 0;
}


void alphatan_state::machine_start()
{
}


void alphatan_state::alphatan(machine_config &config)
{
	M6504(config, m_maincpu, 8.86_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &alphatan_state::alphatan_map);

	NVRAM(config, m_nvram, nvram_device::DEFAULT_ALL_0);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	//m_screen->set_raw(6_MHz_XTAL, 768, 0, 480, 625, 0, 480);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	m_screen->set_size(768, 480);
	m_screen->set_visarea(0, 480-1, 0, 480-1);
	m_screen->set_screen_update(FUNC(alphatan_state::screen_update));

	TEA1002(config, m_tea1002, 8.86_MHz_XTAL);
	PALETTE(config, m_palette, FUNC(alphatan_state::palette_init), 8, 8);

	SAA5050(config, m_trom, 6_MHz_XTAL);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED);
}


ROM_START(alphatan)
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("tantel2732.bin", 0x0000, 0x1000, CRC(875c1ce9) SHA1(76e874cbc5d1341b09f57424181f24bdb31da210))
ROM_END

} // anonymous namespace


//    YEAR  NAME       PARENT  COMPAT  MACHINE     INPUT      CLASS           INIT        COMPANY    FULLNAME        FLAGS
COMP( 1981, alphatan,  0,      0,      alphatan,   alphatan,  alphatan_state, empty_init, "Tantel",  "AlphaTantel",  MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )



alphatpc16.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Carl

/******************************************************************************************
The Alphatronic PC 16 was modeled after the earlier 8 bit Alphatronic PC – on the outside
a typical "breadbox" home computer case with an integrated keyboard and external floppy disk drives.
Coming from a typewriter company, it had ambitions for office use - its high price and mismatched
intended use and design coupled with its release date gave it little success. It was released
in the early phase of DOS computers, when IBM’s standard was not yet set into stone, but would
vanquish all "incompatible" solutions within short time.
The start screen allows the selection
of functions by entering the first (two) letters of the command or using the F-key as shown on the screen.

CPU: 8088@5MHz - RAM: 64KB, can be extended to 128KB - ROM: 64KB (monitor and BASIC)

Keyboard: ALP: the key marked with a Greek alpha character makes the computer/printer combo a
          typewriter, everything is typed "through".
          BRK: BREAK stops a BASIC program or acts like CTRL-C
          GRAPH: allows the entering of semigraphics characters, locks into place like CAPSLK
          numeric and BTX function keys:
          UP, DWN, LFT and RWT are marked with their respective arrows and move the cursor
          DEL is marked (R) for BTX mode and reloads the page
          CLR is marked (i) in BTX mode for “info” and loads the start page
          = is marked with a page that’s being turned and is a "reveal" key, e.g. for quizzes
          <-/ is marked with an envelope, it loads BTX’s messaging service
          the + and – keys aren’t defined yet in BTX mode, they are marked with a page and an arrow each
         * shows a screen with a black/white contrast and turns BTX screen attributes on and off
          / has a telephone receiver and three lines and is the online/offline/dialing key

                                                  [RST] [F1 ] [F2] [F3 ] [F4 ] [F5 ] [F6 ]

[ESC] [1 !] [2 ”] [3 §] [4 $] [5 %] [6 &] [7 /] [8 (][9 )] [0 =] [ß ?] [´ `] [BRK] [GRAPH]   [ 7 ] [ 8 ] [ 9 ] [ * ] [ / ]
[TAB  ] [ Q ] [ W ] [ E ] [ R ] [ T ] [ Z ] [ U ] [ I ] [ O ] [ P ] [ Ü ] [+ *] [CTR] [| ]   [ 4 ] [ 5 ] [ 6 ] [ + ] [ - ]
[CAPSLK]  [ A ] [ S ] [ D ] [ F ] [ G ] [ H ] [ J ] [ K ] [ L ] [ Ö ] [ Ä ] [^ #] [  <-- ]   [ 1 ] [ 2 ] [ 3 ] [ = ] [<-/]
[SHFT] [< >] [ Y ] [ X ] [ C ] [ V ] [ B ] [ N ] [ M ] [, ;] [: .] [- _] [  SHFT  ]  [ALP]   [ 0 ] [UP ] [ . ] [DEL] [CLR]
                   [                      LEER                   ]                           [LFT] [DWN] [RGT] [INS] [HOM]

Graphics options: Standard monitor cassette (Cinch for bw and DIN for SCART/RGB connectors) 40/80x21/25
characters, Full graphics cassette (512x256 pixels, 16 colours, vector graphics, 64K video RAM),
BTX cassette (compatible with the standard monitor cassette but includes a modem for BTX functionality)
Floppy: 1 or 2 5.25” DSDD 40 tracks x 5 sectors x 1024 bytes, external via SCSI, z80+wd1770+sa455
Connectors: joystick, cassette recorder (600/1200 BD) FSK, printer (recommended: TRD 7020 or
GABRIELE 9009 typewriter, V24), floppy, module slot
Options: 2 versions of an autonomous processor PCB (Z8671 based, programmable in TinyBasic
via the PC 16 Terminal, operates independently after programming), connects to the printer port
******************************************************************************************/

#include "emu.h"

#include "cpu/i86/i86.h"
#include "imagedev/floppy.h"
#include "machine/wd_fdc.h"
#include "video/ef9345.h"
#include "machine/z80sio.h"
#include "machine/pic8259.h"
#include "machine/timer.h"
#include "machine/ram.h"
#include "sound/beep.h"
#include "cpu/mcs48/mcs48.h"
#include "cpu/z80/z80.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class alphatpc16_state : public driver_device
{
public:
	alphatpc16_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_pic8259(*this, "pic8259"),
		m_ef9345(*this, "ef9345"),
		m_beep(*this, "beeper"),
		m_wdfdc(*this, "wdfdc"),
		m_ram(*this, RAM_TAG),
		m_z80(*this, "z80"),
		m_flop(*this, "wdfdc:%u", 0),
		m_keys(*this, "KEYS.%u", 0)
	{ }

public:
	void alphatpc16(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void apc16_io(address_map &map) ATTR_COLD;
	void apc16_map(address_map &map) ATTR_COLD;
	void apc16_z80_io(address_map &map) ATTR_COLD;
	void apc16_z80_map(address_map &map) ATTR_COLD;
	void ef9345(address_map &map) ATTR_COLD;

	u8 p1_r();
	void p1_w(u8 data);
	u8 p2_r();
	void p2_w(u8 data);
	u8 host_scsi_r(offs_t offset);
	void host_scsi_w(offs_t offset, u8 data);
	u8 flop_scsi_r(offs_t offset);
	void flop_scsi_w(offs_t offs_t, u8 data);
	u8 p00_r();
	void p40_w(u8 data);

	required_device<cpu_device> m_maincpu;
	required_device<pic8259_device> m_pic8259;
	required_device<ef9345_device> m_ef9345;
	required_device<beep_device> m_beep;
	required_device<wd1770_device> m_wdfdc;
	required_device<ram_device> m_ram;
	required_device<cpu_device> m_z80;
	required_device_array<floppy_connector, 4> m_flop;
	required_ioport_array<8> m_keys;

	u8 m_p1 = 0, m_p2 = 0, m_data = 0, m_p40 = 0;
	bool m_bsy = false, m_req = false, m_ack = false, m_cd = false, m_io = false, m_sel = false;
};

void alphatpc16_state::machine_start()
{
	m_maincpu->space(AS_PROGRAM).install_ram(0, m_ram->size() - 1, m_ram->pointer());
	m_wdfdc->set_floppy(m_flop[0]->get_device());

	m_bsy = false;
	m_req = false;
	m_ack = false;
	m_cd = false;
	m_io = false;
	m_sel = false;
}

void alphatpc16_state::p1_w(u8 data)
{
	if((data == 0xff) && !BIT(m_p2, 7))
		m_p2 &= ~3; //??
	m_p1 = data;
}

u8 alphatpc16_state::p1_r()
{
	return m_p1;
}

void alphatpc16_state::p2_w(u8 data)
{
	m_beep->set_state(!BIT(data, 3));
	m_pic8259->ir0_w(BIT(data, 5));
	m_p2 = data;
}

u8 alphatpc16_state::p2_r()
{
	bool key = false;
	if(m_p1 < 0x80)
		key = BIT(m_keys[m_p1 >> 4]->read(), m_p1 & 0xf);
	return (m_p2 | 0x40) & ~(key ? (m_p1 < 0x40 ? 2 : 1) : 0);
}

u8 alphatpc16_state::p00_r()
{
	u8 ret = 0;
	switch(m_p40 & 0xf0)
	{
		case 0x00:
			ret |= m_flop[0]->get_device()->exists() << 3;
			break;
		case 0x10:
			ret |= m_flop[1]->get_device()->exists() << 3;
			break;
		case 0x20:
			ret |= m_flop[2]->get_device()->exists() << 3;
			break;
		case 0x40:
			ret |= m_flop[3]->get_device()->exists() << 3;
			break;
	}
	return ret;
}

void alphatpc16_state::p40_w(u8 data)
{
	switch(data & 0xf0)
	{
		case 0x00:
			m_wdfdc->set_floppy(m_flop[0]->get_device());
			m_flop[0]->get_device()->ss_w(BIT(data, 2));
			break;
		case 0x10:
			m_wdfdc->set_floppy(m_flop[1]->get_device());
			m_flop[1]->get_device()->ss_w(BIT(data, 2));
			break;
		case 0x20:
			m_wdfdc->set_floppy(m_flop[2]->get_device());
			m_flop[2]->get_device()->ss_w(BIT(data, 2));
			break;
		case 0x40:
			m_wdfdc->set_floppy(m_flop[3]->get_device());
			m_flop[3]->get_device()->ss_w(BIT(data, 2));
			break;
	}
	m_p40 = data;
}

// this scsi emulation is an unrealistic mess
void alphatpc16_state::host_scsi_w(offs_t offset, u8 data)
{
	switch(offset)
	{
		case 0:
			m_ack = true;
			m_req = false;
			m_sel = false;
			m_data = data;
			m_z80->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
			m_z80->set_input_line(Z80_INPUT_LINE_WAIT, CLEAR_LINE);
			break;
		case 2:
			m_ack = false;
			m_sel = true;
			m_data = data;
			m_z80->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
			m_z80->set_input_line(Z80_INPUT_LINE_WAIT, CLEAR_LINE);
			break;
		case 3: //rst ?
			if(data)
				m_z80->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
			else
			{
				m_z80->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
				m_bsy = false;
				m_cd = false;
				m_io = false;
			}
			m_z80->set_input_line(Z80_INPUT_LINE_WAIT, CLEAR_LINE);
			break;
	}
	logerror("%s, data %x bsy %d sel %d req %d ack %d cd %d io %d\n", machine().describe_context(), m_data, m_bsy, m_sel, m_req, m_ack, m_cd, m_io);
}

u8 alphatpc16_state::host_scsi_r(offs_t offset)
{
	u8 ret = 0;
	switch(offset)
	{
		case 0:
			if(!m_req && (m_maincpu->state_int(I8086_IP) == 0x2f46))
			{
				m_maincpu->set_state_int(I8086_IP, m_maincpu->state_int(I8086_IP) - 2);
				break;
			}
			m_ack = true;
			m_req = false;
			ret = m_data;
			m_z80->set_input_line(Z80_INPUT_LINE_WAIT, CLEAR_LINE);
			logerror("%s, data %x bsy %d sel %d req %d ack %d cd %d io %d\n", machine().describe_context(), m_data, m_bsy, m_sel, m_req, m_ack, m_cd, m_io);
			break;
		case 1:
			ret = m_req | (m_bsy << 1) | (m_cd << 3) | (m_io << 5); // bit 2 msg?
			break;
	}
	return ret;
}

void alphatpc16_state::flop_scsi_w(offs_t offset, u8 data)
{
	switch(offset)
	{
		case 0:
			if(m_z80->state_int(STATE_GENPC) == 0xcd)
				m_z80->set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE);
			m_req = true;
			m_ack = false;
			m_data = data;
			break;
		case 1:
			m_bsy = !BIT(data, 0);
			m_cd = !BIT(data, 2);
			m_io = !BIT(data, 3);
			break;
	}
	logerror("%s, data %x bsy %d sel %d req %d ack %d cd %d io %d\n", machine().describe_context(), m_data, m_bsy, m_sel, m_req, m_ack, m_cd, m_io);
}

u8 alphatpc16_state::flop_scsi_r(offs_t offset)
{
	u8 ret = 0;
	switch(offset)
	{
		case 0:
			if(m_z80->state_int(STATE_GENPC) == 0xbc)
				m_z80->set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE);
			m_ack = false;
			m_req = false;
			ret = m_data;
			logerror("%s, data %x bsy %d sel %d req %d ack %d cd %d io %d\n", machine().describe_context(), m_data, m_bsy, m_sel, m_req, m_ack, m_cd, m_io);
			break;
		case 1:
			ret = m_bsy | m_sel << 1 | !m_ack << 2;
			m_ack = false;
			break;
	}
	return ret;
}

void alphatpc16_state::apc16_map(address_map &map)
{
	map(0x80020, 0x8002f).rw(m_ef9345, FUNC(ef9345_device::data_r), FUNC(ef9345_device::data_w));
	map(0x82000, 0x82001).rw("i8741", FUNC(upi41_cpu_device::upi41_master_r), FUNC(upi41_cpu_device::upi41_master_w));
	map(0x84000, 0x84003).rw("z80dart", FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
	map(0x86000, 0x86001).rw(m_pic8259, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x8a000, 0x8a003).rw(FUNC(alphatpc16_state::host_scsi_r), FUNC(alphatpc16_state::host_scsi_w));
	map(0xf0000, 0xfffff).rom().region("bios", 0);
}

void alphatpc16_state::apc16_io(address_map &map)
{
}

void alphatpc16_state::apc16_z80_map(address_map &map)
{
	map(0x0000, 0x3fff).rom().region("flop", 0);
	map(0x4000, 0x47ff).ram();
}

void alphatpc16_state::apc16_z80_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x00).r(FUNC(alphatpc16_state::p00_r));
	map(0x20, 0x23).rw(m_wdfdc, FUNC(wd1770_device::read), FUNC(wd1770_device::write));
	map(0x40, 0x40).w(FUNC(alphatpc16_state::p40_w));
	map(0x62, 0x63).rw(FUNC(alphatpc16_state::flop_scsi_r), FUNC(alphatpc16_state::flop_scsi_w));
}

// not sure the actual layout of the rows
static INPUT_PORTS_START( alphatpc16 )
	PORT_START("KEYS.0")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Unknown 0x7e")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('A')
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('B')
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('C')
	PORT_START("KEYS.1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('=')
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('@')
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_START("KEYS.2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('/')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('{') PORT_CHAR('[')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('|') PORT_CHAR('\\')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('}') PORT_CHAR(']')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR(';')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(':')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("~ ?") PORT_CHAR('~') PORT_CHAR('?')
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('+') PORT_CHAR('*')
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('`') PORT_CHAR('\'')
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PAUSE) PORT_NAME("BREAK")
	PORT_START("KEYS.3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("GRAPH")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(u8"Ü")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("# ^") PORT_CHAR('#') PORT_CHAR('^')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("KEYS.4")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(u8"Ö")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Unknown 0x7f")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR('.')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR('\r')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("< >") PORT_CHAR('<') PORT_CHAR('>')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2')
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6')
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR('*')
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR('+')
	PORT_START("KEYS.5")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Numpad =") PORT_CHAR('=')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0')
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR('/')
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR('-')
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR('\r')
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CLEAR")
	PORT_START("KEYS.6")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Unknown 0x61")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_MAMEKEY(LSHIFT))
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("KEYS.7")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Unknown 0x78")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static void atpc16_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD); // sa455
}

void alphatpc16_state::alphatpc16(machine_config &config)
{
	/* basic machine hardware */
	I8088(config, m_maincpu, 15_MHz_XTAL / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &alphatpc16_state::apc16_map);
	m_maincpu->set_addrmap(AS_IO, &alphatpc16_state::apc16_io);
	m_maincpu->set_irq_acknowledge_callback(m_pic8259, FUNC(pic8259_device::inta_cb));

	PIC8259(config, m_pic8259, 0);
	m_pic8259->out_int_callback().set_inputline(m_maincpu, 0);

	z80dart_device &dart(Z80DART(config, "z80dart", 15_MHz_XTAL / 3)); // clock?
	dart.out_int_callback().set(m_pic8259, FUNC(pic8259_device::ir7_w));

	Z80(config, m_z80, 8_MHz_XTAL / 2);
	m_z80->set_addrmap(AS_PROGRAM, &alphatpc16_state::apc16_z80_map);
	m_z80->set_addrmap(AS_IO, &alphatpc16_state::apc16_z80_io);
	WD1770(config, m_wdfdc, 8_MHz_XTAL);
	FLOPPY_CONNECTOR(config, m_flop[0], atpc16_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);
	dynamic_cast<device_slot_interface *>(m_flop[0].target())->set_fixed(true);
	FLOPPY_CONNECTOR(config, m_flop[1], atpc16_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_flop[2], atpc16_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_flop[3], atpc16_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);

	i8741a_device& i8741(I8741A(config, "i8741", 4.608_MHz_XTAL));
	i8741.p1_in_cb().set(FUNC(alphatpc16_state::p1_r));
	i8741.p1_out_cb().set(FUNC(alphatpc16_state::p1_w));
	i8741.p2_in_cb().set(FUNC(alphatpc16_state::p2_r));
	i8741.p2_out_cb().set(FUNC(alphatpc16_state::p2_w));

	SPEAKER(config, "mono").front_center();
	BEEP(config, "beeper", 1000).add_route(ALL_OUTPUTS, "mono", 1.00); // Unknown freq

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_screen_update(m_ef9345, FUNC(ef9345_device::screen_update));
	screen.set_size(492, 270);
	screen.set_visarea(00, 492-1, 00, 270-1);
	PALETTE(config, "palette").set_entries(8);

	EF9345(config, m_ef9345, 0);
	m_ef9345->set_palette_tag("palette");

	TIMER(config, "scanline").configure_scanline(NAME([this](timer_device &t, s32 p){m_ef9345->update_scanline((uint16_t)p);}), screen, 0, 10);

	// these are supported by the bios, they may not have been available on real hardware
	RAM(config, m_ram).set_default_size("64K").set_extra_options("128K,192K,256K,384K,448K,512K");
}

ROM_START( alphatpc16 )
	ROM_REGION(0x10000,"bios", 0)
	ROM_LOAD("cdae03_04_16.bin", 0x0000, 0x8000, CRC(0ff5b549) SHA1(f5612e7864c06da586087645fed97c78e84a5d04))
	ROM_LOAD("cdae02_07_15.bin", 0x8000, 0x8000, CRC(22fd3acb) SHA1(ddab380dd15326ca699d6b4b7f4bf7c1a9d498ea))

	ROM_REGION(0x400, "i8741", 0)
	ROM_LOAD("d8741ad.bin", 0x000, 0x400, CRC(e71d5d9f) SHA1(deda490491d3ee08f47bd23bb29dc92b3806d3f2))

	ROM_REGION(0x4000, "flop", 0)
	ROM_LOAD("cdae04_03_21.bin", 0x0000, 0x4000, CRC(11bc6551) SHA1(28c1f02fdc040035aba249c4ad21de9b5ec95298))

	ROM_REGION( 0x4000, "ef9345", 0 )
	ROM_LOAD( "charset.rom", 0x0000, 0x2000, BAD_DUMP CRC(b2f49eb3) SHA1(d0ef530be33bfc296314e7152302d95fdf9520fc) )                // from dcvg5k
ROM_END

} // Anonymous namespace


COMP( 1985, alphatpc16,  0, 0, alphatpc16, alphatpc16, alphatpc16_state, empty_init, "Triumph-Adler", "alphatronic PC-16",  MACHINE_NOT_WORKING)




alphatpx.cpp
<---------------------------------------------------------------------->
// license: GPL-2.0+
// copyright-holders: Dirk Best
/***************************************************************************

    Triumph-Adler Alphatronic Px series
    ===================================

    The Px series was designed by SKS (Steinmetz-Krischke Systemtechnik), like the ITT3030 and the SKS Nano,
    the boards are closely related.

    Keyboard and floppy stuff was copypasted from ITT3030 and adapted to the best of knowledge.

    P1, P2 and P2S: no paging
    Lower 16K for P1, P2 and P2 S:

    0x0000 - 0x17ff ROM monitor (MOS)
    0x1800 - 0x1bff 1K RAM
    0x1c00 - 0x1fff reserved
    0x2000 - 0x2fff reserved, belonging to the video card's memory space (video ROM?)
    0x3000 - 0x3fff actual video memory

    P1
    ==
    Upper 32K:
    0x4000 - 0x400a reserved
    0x4010 - 0xc000 32K RAM
    1x 160K, single sided, 40 tracks, 16 sectors/track, 256 bytes/sector floppy disk drive

    P2, P2S
    =======
    Upper 48K:
    0x4000 - 0x400a reserved
    0x4010 - 0xfff 48K RAM
    P2: 2x 160K, single sided, 40 tracks, 16 sectors/track, 256 bytes/sector floppy disk drives
    There is a non-standard CP/M for the P2 and P2S with the program origin at 0x4300 instead of 0x0100

    P2U
    ===
    For paging via port 0x78A, a 16K RAM card with RAM at 0x0000 and 0x3fff and the banking logic (see above) is added to the standard 48K memory card.
    P2S, P2U: 2x 320K, double sided, 40 tracks, 16 sectors/track, 256 bytes/sector floppy disk drives

    P3, P4
    ======
    0x0000 - 0x0fff ROM monitor (MOS)
    0x1000 - 0x17ff free
    0x1800 - 0x1bff monitor stack (RAM)
    0x1c00 - 0x2fff free
    0x3000 - 0x3fff video memory
    0x4000 - 0xffff RAM
    P3: 2x785K, double sided, 80 tracks, 5 sectors/track, 1024 bytes/sector floppy disk drives
    P4: 1x785K, double sided, 80 tracks, 5 sectors/track, 1024 bytes/sector floppy disk drive, 1x harddisk 360 tracks, 4 heads, 17 sectors/track, 512 bytes/sector

    P2U and P3 support regular CP/M use with a full 64K RAM complement.
    Still, the video RAM is at 0x3000 and 0x3ffff even for these machines, and from what I've read they also use the routine present in the ROM monitor, the MOS.
    That means, that in order to update the video RAM and probably other I/O the lower 16K (page 0) are constantly paged in and paged out.
    This is accomplished by writing 2FH to port 0x78 in order to switch in the ROM (and assorted, see below) area and by writing 63H to port 0x78 in order to swap the full 64K RAM back in.

    P30 and P40
    ===========
    Those were P3 and P4's with an additional 8088 card, a 128K RAM card (some with an extra 32K graphics extension) to support MS-DOS.


    comments, testing, modification: rfka01, helwie44

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

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "cpu/i86/i86.h"
#include "cpu/mcs48/mcs48.h"
#include "imagedev/floppy.h"
#include "machine/bankdev.h"
#include "machine/i8251.h"
#include "machine/wd_fdc.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "video/tms9927.h"
#include "sound/beep.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"
#include "debugger.h"


namespace {

//**************************************************************************
//  TYPE DEFINITIONS - Alphatronic P1, P2, P2S, P2U and Hell 2069
//**************************************************************************

class alphatp_12_state : public driver_device
{
public:
	alphatp_12_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_bankdev(*this, "bankdev"),
		m_kbdmcu(*this, "kbdmcu"),
		m_crtc(*this, "crtc"),
		m_fdc (*this, "fdc"),
		m_floppy(*this, "fdc:%u", 0),
		m_beep(*this, "beeper"),
		m_keycols(*this, "COL.%u", 0),
		m_palette(*this, "palette"),
		m_vram(*this, "vram"),
		m_gfx(*this, "gfx"),
		m_ram(*this, "ram")
	{ }
	void alphatp1(machine_config &config);
	void alphatp2(machine_config &config);
	void alphatp2u(machine_config &config);
protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	int kbd_matrix_r();
	void kbd_matrix_w(u8 data);
	u8 kbd_port2_r();
	void kbd_port2_w(u8 data);

	u8 fdc_r(offs_t offset);
	void fdc_w(offs_t offset, u8 data);
	u8 fdc_stat_r();
	void fdc_cmd_w(u8 data);

	void fdcirq_w(int state);
	void fdcdrq_w(int state);
	void fdchld_w(int state);
	void beep_w(u8 data);
	void bank_w(u8 data);

	void alphatp2_io(address_map &map) ATTR_COLD;
	void alphatp2_map(address_map &map) ATTR_COLD;
	void alphatp2_mem(address_map &map) ATTR_COLD;

	required_device<address_map_bank_device> m_bankdev;
	required_device<i8041a_device> m_kbdmcu;
	required_device<crt5027_device> m_crtc;
	required_device<fd1791_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<beep_device> m_beep;
	required_ioport_array<16> m_keycols;

	u8 m_kbdclk = 0, m_kbdread = 0, m_kbdport2 = 0;
	required_device<palette_device> m_palette;
	required_shared_ptr<u8> m_vram;
	required_region_ptr<u8> m_gfx;
	required_shared_ptr<u8> m_ram;
	floppy_image_device *m_curfloppy = nullptr;
	bool m_fdc_irq = false, m_fdc_drq = false, m_fdc_hld = false;
};

//**************************************************************************
//  TYPE DEFINITIONS - Alphatronic P3, P4, P30 and P4
//**************************************************************************

class alphatp_34_state : public driver_device
{
public:
	alphatp_34_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_bankdev(*this, "bankdev"),
		m_kbdmcu(*this, "kbdmcu"),
		m_crtc(*this, "crtc"),
		m_fdc (*this, "fdc"),
		m_floppy(*this, "fdc:%u", 0),
		m_i8088(*this, "i8088"),
		m_beep(*this, "beeper"),
		m_pic(*this, "pic8259"),
		m_keycols(*this, "COL.%u", 0),
		m_scncfg(*this, "SCREEN"),
		m_palette(*this, "palette"),
		m_gfxdecode(*this, "gfxdecode"),
		m_vram(*this, "vram"),
		m_gfx(*this, "gfx"),
		m_ram(*this, "ram")
	{ }

	void alphatp3(machine_config &config);
	void alphatp30(machine_config &config);
protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
private:

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	int kbd_matrix_r();
	void kbd_matrix_w(u8 data);
	u8 kbd_port2_r();
	void kbd_port2_w(u8 data);

	u8 fdc_r(offs_t offset);
	void fdc_w(offs_t offset, u8 data);
	u8 fdc_stat_r();
	void fdc_cmd_w(u8 data);

	void fdcirq_w(int state);
	void fdcdrq_w(int state);
	void fdchld_w(int state);
	void beep_w(u8 data);
	void bank_w(u8 data);
	u8 start88_r(offs_t offset);
	u8 comm85_r(offs_t offset);
	void comm85_w(u8 data);
	u8 comm88_r(offs_t offset);
	void comm88_w(u8 data);
	u8 gfxext_r(offs_t offset);
	void gfxext_w(offs_t offset, u8 data);
	void gfxext1_w(u8 data);
	void gfxext2_w(u8 data);
	void gfxext3_w(offs_t offset, u8 data);

	u8* vramext_addr_xlate(offs_t offset);

	void alphatp30_8088_io(address_map &map) ATTR_COLD;
	void alphatp30_8088_map(address_map &map) ATTR_COLD;
	void alphatp3_io(address_map &map) ATTR_COLD;
	void alphatp3_map(address_map &map) ATTR_COLD;
	void alphatp3_mem(address_map &map) ATTR_COLD;

	required_device<address_map_bank_device> m_bankdev;
	required_device<i8041a_device> m_kbdmcu;
	required_device<crt5037_device> m_crtc;
	required_device<fd1791_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	optional_device<cpu_device> m_i8088;
	required_device<beep_device> m_beep;
	optional_device<pic8259_device> m_pic;
	required_ioport_array<16> m_keycols;
	required_ioport m_scncfg;

	u8 m_kbdclk, m_kbdread, m_kbdport2;
	required_device<palette_device> m_palette;
	required_device<gfxdecode_device> m_gfxdecode;
	required_shared_ptr<u8> m_vram;
	required_region_ptr<u8> m_gfx;
	required_shared_ptr<u8> m_ram;
	floppy_image_device *m_curfloppy;
	bool m_fdc_irq, m_fdc_drq, m_fdc_hld;
	u8 m_85_data, m_88_data;
	bool m_88_da, m_85_da, m_88_started;
	u8 m_gfxext1, m_gfxext2, m_vramlatch;
	u16 m_gfxext3;
	u8 m_vramext[371*80];
	u8 m_vramchr[256*12];  // these are one 32K ram region but with complex mapping
};

//**************************************************************************
//  ADDRESS MAPS - Alphatronic P1, P2, P2S, P2U and Hell 2069
//**************************************************************************

void alphatp_12_state::alphatp2_mem(address_map &map)
{
	map(0x0000, 0xffff).m(m_bankdev, FUNC(address_map_bank_device::amap8));
}

void alphatp_12_state::alphatp2_map(address_map &map)
{
	map(0x00000, 0x0ffff).bankrw("ram_0000");
	map(0x00000, 0x017ff).rom().region("boot", 0);  // P2  0x0000 , 0x17ff -hw 6kB, P3 only 4 kB
	map(0x01800, 0x01c00).ram(); // boot rom variables
	map(0x03000, 0x03bff).writeonly().share("vram"); // test  2017 hw, MOS directly writes to display RAM
	map(0x03FF0, 0x03fff).w(m_crtc, FUNC(crt5027_device::write)); //test hw, mem-mapped registers, cursor position can be determined through this range

	map(0x10000, 0x1ffff).ram().share("ram");
}

void alphatp_12_state::alphatp2_io(address_map &map)
{
	map.unmap_value_high();
	map(0x04, 0x05).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x10, 0x11).rw(m_kbdmcu, FUNC(i8041a_device::upi41_master_r), FUNC(i8041a_device::upi41_master_w));
	map(0x12, 0x12).w(FUNC(alphatp_12_state::beep_w));
	map(0x50, 0x53).rw(FUNC(alphatp_12_state::fdc_r), FUNC(alphatp_12_state::fdc_w));
	map(0x54, 0x54).rw(FUNC(alphatp_12_state::fdc_stat_r), FUNC(alphatp_12_state::fdc_cmd_w));
	map(0x78, 0x78).w(FUNC(alphatp_12_state::bank_w));
}


void alphatp_12_state::bank_w(u8 data)
{
	m_bankdev->set_bank(BIT(data, 6));
}


//**************************************************************************
//  ADDRESS MAPS - Alphatronic P3, P4, P30 and P40
//**************************************************************************

void alphatp_34_state::alphatp3_mem(address_map &map)
{
	map(0x0000, 0xffff).m(m_bankdev, FUNC(address_map_bank_device::amap8));
}

void alphatp_34_state::alphatp3_map(address_map &map)
{
	map(0x00000, 0x0ffff).bankrw("ram_0000");
	map(0x00000, 0x017ff).rom().region("boot", 0);  // P2  0x0000 , 0x17ff -hw 6kB, P3 only 4 kB
	map(0x01800, 0x01c00).ram(); // boot rom variables
	map(0x03000, 0x03bff).writeonly().share("vram"); // test  2017 hw, MOS directly writes to display RAM
	map(0x03FF0, 0x03fff).w(m_crtc, FUNC(crt5037_device::write)); //test hw, mem-mapped registers, cursor position can be determined through this range

	map(0x10000, 0x1ffff).ram().share("ram");
}

void alphatp_34_state::alphatp3_io(address_map &map)
{
	map.unmap_value_high();
	//map(0x00, 0x00).r(FUNC(alphatp_34_state::)); // unknown
	map(0x04, 0x05).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x08, 0x09).rw(FUNC(alphatp_34_state::comm88_r), FUNC(alphatp_34_state::comm88_w));
	map(0x10, 0x11).rw(m_kbdmcu, FUNC(i8041a_device::upi41_master_r), FUNC(i8041a_device::upi41_master_w));
	map(0x12, 0x12).w(FUNC(alphatp_34_state::beep_w));
	map(0x40, 0x41).r(FUNC(alphatp_34_state::start88_r));
	//map(0x42, 0x42).w(FUNC(alphatp_34_state::)); // unknown
	map(0x50, 0x53).rw(FUNC(alphatp_34_state::fdc_r), FUNC(alphatp_34_state::fdc_w));
	map(0x54, 0x54).rw(FUNC(alphatp_34_state::fdc_stat_r), FUNC(alphatp_34_state::fdc_cmd_w));
	map(0x78, 0x78).w(FUNC(alphatp_34_state::bank_w));
}

void alphatp_34_state::alphatp30_8088_map(address_map &map)
{
	map(0x00000, 0x1ffff).ram();
	map(0xe0000, 0xeffff).rw(FUNC(alphatp_34_state::gfxext_r), FUNC(alphatp_34_state::gfxext_w));
	map(0xfe000, 0xfffff).rom().region("16bit", 0);
}

void alphatp_34_state::alphatp30_8088_io(address_map &map)
{
	//map(0x008a, 0x008a).r(FUNC(alphatp_34_state::)); // unknown
	map(0xf800, 0xf800).w(FUNC(alphatp_34_state::gfxext1_w));
	map(0xf900, 0xf900).w(FUNC(alphatp_34_state::gfxext2_w));
	map(0xfa00, 0xfa01).w(FUNC(alphatp_34_state::gfxext3_w));
	//map(0xfb00, 0xfb0f).w(FUNC(alphatp_34_state::)); // unknown possibly gfx ext
	map(0xffe0, 0xffe1).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0xffe4, 0xffe7).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xffe9, 0xffea).rw(FUNC(alphatp_34_state::comm85_r), FUNC(alphatp_34_state::comm85_w));
}

u8 alphatp_34_state::start88_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
	{
		if (!offset)
		{
			if (m_i8088 && !m_88_started)
			{
				m_i8088->resume(SUSPEND_REASON_DISABLE);
				m_88_started = true;
			}
			m_i8088->set_input_line(INPUT_LINE_TEST, ASSERT_LINE);
		}
		else
			m_i8088->set_input_line(INPUT_LINE_TEST, CLEAR_LINE);
	}
	return 0;
}

void alphatp_34_state::bank_w(u8 data)
{
	m_bankdev->set_bank(BIT(data, 6));
}

u8 alphatp_34_state::comm88_r(offs_t offset)
{
	if (!offset)
		return (m_85_da ? 0 : 1) | (m_88_da ? 0 : 0x80);
	if (!machine().side_effects_disabled())
	{
		if (m_i8088)
			m_i8088->set_input_line(INPUT_LINE_TEST, ASSERT_LINE);
		m_85_da = false;
	}
	return m_85_data;
}

void alphatp_34_state::comm88_w(u8 data)
{
	m_88_data = data;
	if (m_pic)
		m_pic->ir2_w(ASSERT_LINE);
	m_88_da = true;
}

u8 alphatp_34_state::comm85_r(offs_t offset)
{
	if (!offset)
		return m_88_da ? 0 : 1;
	if (!machine().side_effects_disabled())
	{
		m_pic->ir2_w(CLEAR_LINE);
		m_88_da = false;
	}
	return m_88_data;
}

void alphatp_34_state::comm85_w(u8 data)
{
	m_85_data = data;
	m_85_da = true;
	m_i8088->set_input_line(INPUT_LINE_TEST, CLEAR_LINE);
}

void alphatp_34_state::gfxext1_w(u8 data)
{
	m_gfxext1 = data;
}

void alphatp_34_state::gfxext2_w(u8 data)
{
	m_gfxext2 = data;
}

void alphatp_34_state::gfxext3_w(offs_t offset, u8 data)
{
	u16 mask = 0xff << (offset ? 0 : 8);
	m_gfxext3 = (m_gfxext3 & mask) | (data << (offset * 8));
}

u8* alphatp_34_state::vramext_addr_xlate(offs_t offset)
{
	offset = offset >> 3;
	int bank = offset >> 7;
	int offs = offset & 0x7f;
	if (offs < 80)
		return &m_vramext[(((((m_gfxext2 & 0xf8) << 2) + bank) * 80) + offs) % (371*80)];
	else
		return &m_vramchr[(((((m_gfxext2 & 0x8) << 2) ^ bank) * 48) + (offs - 80)) % (256*12)];
}

u8 alphatp_34_state::gfxext_r(offs_t offset)
{
	switch (m_gfxext1)
	{
		case 0x33:
			if (!machine().side_effects_disabled())
				m_vramlatch = *vramext_addr_xlate(offset);
			return 0;
	}
	if (!machine().side_effects_disabled())
		logerror("gfxext read offset %x %x\n", offset, m_gfxext1);
	return 0;
}

void alphatp_34_state::gfxext_w(offs_t offset, u8 data)
{
	switch (m_gfxext1)
	{
		case 5:
		{
			if (m_gfxext3 == 0xe0f)
				data = ~data;
			u8 mask = 1 << (offset & 7);
			u8 *addr = vramext_addr_xlate(offset);
			*addr &= ~mask;
			*addr |= data & mask;
			break;
		}
		case 6:
			*vramext_addr_xlate(offset) ^= 1 << (offset & 7);
			break;
		case 0x33:
			*vramext_addr_xlate(offset) = m_vramlatch;
			break;
		default:
			logerror("gfxext write offset %x %x %x\n", offset, data, m_gfxext1);
	}
	if ((offset & 0x3ff) > 0x280)
		m_gfxdecode->gfx(1)->mark_dirty(((uintptr_t)vramext_addr_xlate(offset) - (uintptr_t)m_vramchr) / 12);
}

//**************************************************************************
//  INPUTS -  Alphatronic P1, P2, P2S, P2U and Hell 2069
//**************************************************************************

int alphatp_12_state::kbd_matrix_r()
{
	return m_kbdread;
}

void alphatp_12_state::kbd_matrix_w(u8 data)
{
	if ((data & 0x80) && (!m_kbdclk))
	{
		const ioport_value tmp_read = m_keycols[(data >> 3) & 0xf]->read() & (1 << (data & 0x7));
		m_kbdread = (tmp_read != 0) ? 1 : 0;
	}

	m_kbdclk = (data & 0x80) ? 1 : 0;

}

// bit 2 is UPI-41 host IRQ to Z80
void alphatp_12_state::kbd_port2_w(u8 data)
{
	m_kbdport2 = data;

}

u8 alphatp_12_state::kbd_port2_r()
{
	return m_kbdport2;
}

//**************************************************************************
//  INPUTS - Alphatronic P3, P4, P30 and P40
//**************************************************************************

int alphatp_34_state::kbd_matrix_r()
{
	return m_kbdread;
}

void alphatp_34_state::kbd_matrix_w(u8 data)
{
	if (data & 0x80)
	{
		data--; // FIXME: the p30 kbc program doesn't clock the keyboard but gets the wrong value from t0
		const ioport_value tmp_read = m_keycols[(data >> 3) & 0xf]->read() & (1 << (data & 0x7));
		m_kbdread = (tmp_read != 0) ? 1 : 0;
	}

	m_kbdclk = (data & 0x80) ? 1 : 0;

}

// bit 2 is UPI-41 host IRQ to Z80
void alphatp_34_state::kbd_port2_w(u8 data)
{
	m_kbdport2 = data;

}

u8 alphatp_34_state::kbd_port2_r()
{
	return m_kbdport2;
}


//**************************************************************************
//  KEYBOARD - Alphatronic P1, P2, P2S, P2U and Hell 2069
//**************************************************************************

/*
P2 keyboard:

[   ][ ! ][ " ][ § ][ $ ][ % ][ & ][ / ][ ( ][ ) ][ = ][ ? ][ ` ][ F ][ F ][ F ][ F ][ F ][ F ]
[_SM][_1_][_2_][_3_][_4_][_5_][_6_][_7_][_8_][_9_][_0_][_ß_][_´_][_1_][_2_][_3_][_4_][_5_][_6_]
[     ][   ][   ][   ][   ][   ][   ][   ][   ][   ][   ][   ][ * ][   ]   [   ][   ][   ][   ]
[_(C)_][_Q_][_W_][_E_][_R_][_T_][_Z_][_U_][_I_][_O_][_P_][_Ü_][_+_][_TB]   [_7_][_8_][_9_][_/_]
[      ][   ][   ][   ][   ][   ][   ][   ][   ][   ][   ][   ][ ^ ][   | ][   ][   ][   ][   ]
[__CL__][_A_][_S_][_D_][_F_][_G_][_H_][_J_][_K_][_L_][_Ö_][_Ä_][_#_][_<=| ][_4_][_5_][_6_][_x_]
[    ][ > ][   ][   ][   ][   ][   ][   ][   ][ ; ][ : ][ _ ][    ][[] ]   [   ][   ][   ][   ]
[_SH_][ < ][_Y_][_X_][_C_][_V_][_B_][_N_][_M_][_,_][_._][_-_][_SH_][ []]   [_1_][_2_][_3_][_-_]
[   ][   ][   ][                                      ][   ][   ][   ][   ][        ][   ][   ]
[(R)][_UP][LFT][_______________SPACE__________________][RGH][DWN][PS1][ C ][____0___][_._][_+_]

The SH(ift), C(aps)L(ock) and Space keys are unmarked, to the right of + / * is unmarked as well.
SM is "Schreibmaschinen-Modus", typewriter mode
The direction keys and Pos1 are marked with arrows
<- moves the cursor left one position and deletes the character
-> moves the cursor right one position
(C) has a red LED and is the RESET key
The C key is called "Korrektur" in the manual. You have to press it if the keyboard locks up, e.g. when characters have been
entered too quickly.
[]
 [] is two overlapping squares, the manual calls it "Kontroll-Taste", i.e. CTRL
TB is the TAB key and is unmarked
*/

// translation table at offset 0xdc0 in the main rom
static INPUT_PORTS_START( alphatp2 )

PORT_START("COL.0")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W")           PORT_CODE(KEYCODE_W)        PORT_CHAR('w')      PORT_CHAR('W')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S")           PORT_CODE(KEYCODE_S)        PORT_CHAR('s')      PORT_CHAR('S')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X")           PORT_CODE(KEYCODE_X)        PORT_CHAR('x')      PORT_CHAR('X')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LEFT")        PORT_CODE(KEYCODE_LEFT)     PORT_CHAR(8)                    // left arrow works as backspace
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q")           PORT_CODE(KEYCODE_Q)        PORT_CHAR('q')      PORT_CHAR('Q')
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1")           PORT_CODE(KEYCODE_1)        PORT_CHAR('1')      PORT_CHAR('!')

PORT_START("COL.1")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E")           PORT_CODE(KEYCODE_E)        PORT_CHAR('e')      PORT_CHAR('E')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D")           PORT_CODE(KEYCODE_D)        PORT_CHAR('d')      PORT_CHAR('D')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C")           PORT_CODE(KEYCODE_C)        PORT_CHAR('c')      PORT_CHAR('C')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SPACE")       PORT_CODE(KEYCODE_SPACE)    PORT_CHAR(' ')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F5")          PORT_CODE(KEYCODE_F5)       PORT_CHAR(UCHAR_MAMEKEY(F5)) // SCAN:=0Dh ->8Ah-funct F5 ok
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2")           PORT_CODE(KEYCODE_2)        PORT_CHAR('2')      PORT_CHAR('"')

PORT_START("COL.2")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R")           PORT_CODE(KEYCODE_R)        PORT_CHAR('r')      PORT_CHAR('R')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F")           PORT_CODE(KEYCODE_F)        PORT_CHAR('f')      PORT_CHAR('F')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V")           PORT_CODE(KEYCODE_V)        PORT_CHAR('v')      PORT_CHAR('V')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RIGHT")       PORT_CODE(KEYCODE_RIGHT)    PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) // 0x82
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F6")          PORT_CODE(KEYCODE_F6)       PORT_CHAR(UCHAR_MAMEKEY(F6)) // scan:=15h 8Ch-> F6 ok
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3")           PORT_CODE(KEYCODE_3)        PORT_CHAR('3')      PORT_CHAR(0x00a7)

PORT_START("COL.3")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T")           PORT_CODE(KEYCODE_T)        PORT_CHAR('t')      PORT_CHAR('T')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G")           PORT_CODE(KEYCODE_G)        PORT_CHAR('g')      PORT_CHAR('G')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B")           PORT_CODE(KEYCODE_B)        PORT_CHAR('b')      PORT_CHAR('B')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DOWN")        PORT_CODE(KEYCODE_DOWN)     PORT_CHAR(UCHAR_MAMEKEY(DOWN))      // 0x8b
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A")           PORT_CODE(KEYCODE_A)        PORT_CHAR('a')      PORT_CHAR('A')
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4")           PORT_CODE(KEYCODE_4)        PORT_CHAR('4')      PORT_CHAR('$')

PORT_START("COL.4")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z")           PORT_CODE(KEYCODE_Y)        PORT_CHAR('z')      PORT_CHAR('Z')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H")           PORT_CODE(KEYCODE_H)        PORT_CHAR('h')      PORT_CHAR('H')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N")           PORT_CODE(KEYCODE_N)        PORT_CHAR('n')      PORT_CHAR('N')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CAPS")        PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))  // scan:=25h ->0xc0 ?capslock ?
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5")           PORT_CODE(KEYCODE_5)        PORT_CHAR('5')      PORT_CHAR('%')

PORT_START("COL.5")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U")           PORT_CODE(KEYCODE_U)        PORT_CHAR('u')      PORT_CHAR('U')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J")           PORT_CODE(KEYCODE_J)        PORT_CHAR('j')      PORT_CHAR('J')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M")           PORT_CODE(KEYCODE_M)        PORT_CHAR('m')      PORT_CHAR('M')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UP")          PORT_CODE(KEYCODE_UP)       PORT_CHAR(UCHAR_MAMEKEY(UP))        // 0x89
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y")           PORT_CODE(KEYCODE_Z)        PORT_CHAR('y')      PORT_CHAR('Y')
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6")           PORT_CODE(KEYCODE_6)        PORT_CHAR('6')      PORT_CHAR('&')

PORT_START("COL.6")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I")           PORT_CODE(KEYCODE_I)        PORT_CHAR('i')      PORT_CHAR('I')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K")           PORT_CODE(KEYCODE_K)        PORT_CHAR('k')      PORT_CHAR('k')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(", ;")         PORT_CODE(KEYCODE_COMMA)    PORT_CHAR(',')      PORT_CHAR(';')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("<>")          PORT_CODE(KEYCODE_BACKSLASH2)PORT_CHAR('<')     PORT_CHAR('>')
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7")           PORT_CODE(KEYCODE_7)        PORT_CHAR('7')      PORT_CHAR('/')

PORT_START("COL.7")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O")           PORT_CODE(KEYCODE_O)        PORT_CHAR('o')      PORT_CHAR('O')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L")           PORT_CODE(KEYCODE_L)        PORT_CHAR('l')      PORT_CHAR('L')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(". :")         PORT_CODE(KEYCODE_STOP)     PORT_CHAR('.')      PORT_CHAR(':')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Pos 1")       PORT_CODE(KEYCODE_HOME)     PORT_CHAR(UCHAR_MAMEKEY(HOME))      // 0x8f
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L_SHIFT")     PORT_CODE(KEYCODE_LSHIFT)   PORT_CHAR(UCHAR_MAMEKEY(LSHIFT))    // 3Dh ->C1h-function P3 key left
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8")           PORT_CODE(KEYCODE_8)        PORT_CHAR('8')      PORT_CHAR('(')

PORT_START("COL.8")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P")           PORT_CODE(KEYCODE_P)        PORT_CHAR('p')      PORT_CHAR('P')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"ö Ö")       PORT_CODE(KEYCODE_COLON)    PORT_CHAR(0x00f6)   PORT_CHAR(0x00d6)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("- _")         PORT_CODE(KEYCODE_SLASH)    PORT_CHAR('-')      PORT_CHAR('_')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R_CTRL")      PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))  // 44h ->84h clear ?
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENTER Pad")   PORT_CODE(KEYCODE_ENTER_PAD)PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9")           PORT_CODE(KEYCODE_9)        PORT_CHAR('9')      PORT_CHAR(')')

PORT_START("COL.9")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"ü Ü")       PORT_CODE(KEYCODE_OPENBRACE)PORT_CHAR(0x00fc)   PORT_CHAR(0x00dc)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"ä Ä")       PORT_CODE(KEYCODE_QUOTE)    PORT_CHAR(0x00e4)   PORT_CHAR(0x00c4)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R_SHIFT")     PORT_CODE(KEYCODE_RSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0 Pad")       PORT_CODE(KEYCODE_0_PAD)    PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0")           PORT_CODE(KEYCODE_0)        PORT_CHAR('0')      PORT_CHAR('=')

PORT_START("COL.10")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+ *")         PORT_CODE(KEYCODE_CLOSEBRACE)PORT_CHAR('+')     PORT_CHAR('*')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("# ^")         PORT_CODE(KEYCODE_BACKSLASH)PORT_CHAR('#')      PORT_CHAR('^')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("[][]/ESC")    PORT_CODE(KEYCODE_ESC)      PORT_CHAR(UCHAR_MAMEKEY(ESC))           // Esc test this work ?!
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DEL Pad")     PORT_CODE(KEYCODE_DEL_PAD)  PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"ß ?")       PORT_CODE(KEYCODE_MINUS)    PORT_CHAR(0x00df)   PORT_CHAR('?')      // ß and ?

PORT_START("COL.11")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TAB")         PORT_CODE(KEYCODE_TAB)      PORT_CHAR('\t')                         // TAB key
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENTER")       PORT_CODE(KEYCODE_ENTER)    PORT_CHAR(13)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1 Pad")       PORT_CODE(KEYCODE_1_PAD)    PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+ Pad")       PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"´ `")       PORT_CODE(KEYCODE_EQUALS)   PORT_CHAR(0x00b4,'\'') PORT_CHAR(0x0060)

PORT_START("COL.12")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7 Pad")       PORT_CODE(KEYCODE_7_PAD)    PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4 Pad")       PORT_CODE(KEYCODE_4_PAD)    PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2 Pad")       PORT_CODE(KEYCODE_2_PAD)    PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F4")          PORT_CODE(KEYCODE_F4)       PORT_CHAR(UCHAR_MAMEKEY(F4))             // scan 68h -> 88h func. F4

PORT_START("COL.13")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/ Pad")       PORT_CODE(KEYCODE_SLASH_PAD)PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD))
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("*")           PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(ASTERISK))  // test ?
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"® /Ctrl")   PORT_CODE(KEYCODE_LCONTROL)   PORT_CODE(KEYCODE_LCONTROL)         // scan 6Bh -> C2h funct.
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_UNKNOWN)                  // 0xc2 ??
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F3")          PORT_CODE(KEYCODE_F3)       PORT_CHAR(UCHAR_MAMEKEY(F3)) // scan:=68h 88h-> F3 ok

PORT_START("COL.14")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9 Pad")       PORT_CODE(KEYCODE_9_PAD)    PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6 Pad")       PORT_CODE(KEYCODE_6_PAD)    PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("- Pad")       PORT_CODE(KEYCODE_MINUS_PAD)PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F2")          PORT_CODE(KEYCODE_F2)       PORT_CHAR(UCHAR_MAMEKEY(F2))    // 70h -> 87h func.F2 ok

PORT_START("COL.15")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8 Pad")       PORT_CODE(KEYCODE_8_PAD)    PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5 Pad")       PORT_CODE(KEYCODE_5_PAD)    PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3 Pad")       PORT_CODE(KEYCODE_3_PAD)    PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F1")          PORT_CODE(KEYCODE_F1)       PORT_CHAR(UCHAR_MAMEKEY(F1))    // 7Dh -> 85H func. F1 ok
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SM")          PORT_CODE(KEYCODE_NUMLOCK)  PORT_CHAR(UCHAR_MAMEKEY(NUMLOCK))// SM (typewriter) mode key */
INPUT_PORTS_END

//**************************************************************************
//  KEYBOARD - Alphatronic P3, P4, P30 and P40
//**************************************************************************

/*
P3 keyboard:

 [RST]
 [___]

      [ ! ][ " ][ § ][ $ ][ % ][ & ][ / ][ ( ][ ) ][ = ][ ? ][ ` ][ SM  ]       [(1)][(2)]  [(1)][(2)][(3)][(4)]
      [_1_][_2_][_3_][_4_][_5_][_6_][_7_][_8_][_9_][_0_][_ß_][_´_][_____]       [___][___]  [___][___][___][___]
 [     ][   ][   ][   ][   ][   ][   ][   ][   ][   ][   ][   ][ * ][[] ][ | ]  [        ]  [   ][   ][   ][ / ]
 [_==>_][_Q_][_W_][_E_][_R_][_T_][_Z_][_U_][_I_][_O_][_P_][_Ü_][_+_][_[]][ | ]  [__POS1__]  [_7_][_8_][_9_][_-_]
  [     ][   ][   ][   ][   ][   ][   ][   ][   ][   ][   ][   ][   ][   <=| ]  [        ]  [   ][   ][   ][ x ]
  [_CL__][_A_][_S_][_D_][_F_][_G_][_G_][_H_][_J_][_K_][_L_][_Ö_][_Ä_][_______]  [___UP___]  [_4_][_5_][_6_][_+_]
[     ][ > ][   ][   ][   ][   ][   ][   ][   ][ ; ][ : ][ _ ][     ]           [   ][   ]  [   ][   ][   ][  |]
[__SH_][_<_][_Y_][_X_][_C_][_V_][_B_][_N_][_M_][_,_][_._][_-_][__SH_]           [_L_][ R ]  [_1_][_2_][_3_][  |]
     [   ][                                            ][   ]                   [        ]  [        ][   ][<=|]
     [(R)][___________________SPACE____________________][ C ]                   [__DOWN__]  [____0___][ . ][___]

RST is a hard reset, the key has a red status LED
The SH(ift), C(aps)L(ock) and Space keys are unmarked
(1)-(6) are function keys with the number in a circle
(R) R is in a circle, pressing (R) repeats the previous key as long as you press it under CP/M
[]
 [] is two overlapping squares
SM is an abbreviation of the German "Schreibmaschinen-Modus", typewriter mode. It has a green status LED.
Up, Down, Left, Right and Pos1 are marked with arrows
The C key is called "Korrektur" in the manual. You have to press it if the keyboard locks up, e.g. when characters have been
entered too quickly.


P30 keyboard differences:
(R) is now marked CTRL for use under DOS on the P30
[]
 [] is now marked ESC
C can be used as ALT(ernate) in DOS


*/

// translation table at offset 0xdc0 in the main rom
static INPUT_PORTS_START( alphatp3 )

PORT_START("COL.0")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W")           PORT_CODE(KEYCODE_W)        PORT_CHAR('w')      PORT_CHAR('W')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S")           PORT_CODE(KEYCODE_S)        PORT_CHAR('s')      PORT_CHAR('S')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X")           PORT_CODE(KEYCODE_X)        PORT_CHAR('x')      PORT_CHAR('X')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LEFT")        PORT_CODE(KEYCODE_LEFT)     PORT_CHAR(8)                    // left arrow works as backspace
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q")           PORT_CODE(KEYCODE_Q)        PORT_CHAR('q')      PORT_CHAR('Q')
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1")           PORT_CODE(KEYCODE_1)        PORT_CHAR('1')      PORT_CHAR('!')

PORT_START("COL.1")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E")           PORT_CODE(KEYCODE_E)        PORT_CHAR('e')      PORT_CHAR('E')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D")           PORT_CODE(KEYCODE_D)        PORT_CHAR('d')      PORT_CHAR('D')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C")           PORT_CODE(KEYCODE_C)        PORT_CHAR('c')      PORT_CHAR('C')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SPACE")       PORT_CODE(KEYCODE_SPACE)    PORT_CHAR(' ')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F5")          PORT_CODE(KEYCODE_F5)       PORT_CHAR(UCHAR_MAMEKEY(F5)) // SCAN:=0Dh ->8Ah-funct F5 ok
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2")           PORT_CODE(KEYCODE_2)        PORT_CHAR('2')      PORT_CHAR('"')

PORT_START("COL.2")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R")           PORT_CODE(KEYCODE_R)        PORT_CHAR('r')      PORT_CHAR('R')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F")           PORT_CODE(KEYCODE_F)        PORT_CHAR('f')      PORT_CHAR('F')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V")           PORT_CODE(KEYCODE_V)        PORT_CHAR('v')      PORT_CHAR('V')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RIGHT")       PORT_CODE(KEYCODE_RIGHT)    PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) // 0x82
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F6")          PORT_CODE(KEYCODE_F6)       PORT_CHAR(UCHAR_MAMEKEY(F6)) // scan:=15h 8Ch-> F6 ok
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3")           PORT_CODE(KEYCODE_3)        PORT_CHAR('3')      PORT_CHAR(0x00a7)

PORT_START("COL.3")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T")           PORT_CODE(KEYCODE_T)        PORT_CHAR('t')      PORT_CHAR('T')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G")           PORT_CODE(KEYCODE_G)        PORT_CHAR('g')      PORT_CHAR('G')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B")           PORT_CODE(KEYCODE_B)        PORT_CHAR('b')      PORT_CHAR('B')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DOWN")        PORT_CODE(KEYCODE_DOWN)     PORT_CHAR(UCHAR_MAMEKEY(DOWN))      // 0x8b
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A")           PORT_CODE(KEYCODE_A)        PORT_CHAR('a')      PORT_CHAR('A')
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4")           PORT_CODE(KEYCODE_4)        PORT_CHAR('4')      PORT_CHAR('$')

PORT_START("COL.4")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z")           PORT_CODE(KEYCODE_Y)        PORT_CHAR('z')      PORT_CHAR('Z')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H")           PORT_CODE(KEYCODE_H)        PORT_CHAR('h')      PORT_CHAR('H')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N")           PORT_CODE(KEYCODE_N)        PORT_CHAR('n')      PORT_CHAR('N')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CAPS")        PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))  // scan:=25h ->0xc0 ?capslock ?
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5")           PORT_CODE(KEYCODE_5)        PORT_CHAR('5')      PORT_CHAR('%')

PORT_START("COL.5")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U")           PORT_CODE(KEYCODE_U)        PORT_CHAR('u')      PORT_CHAR('U')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J")           PORT_CODE(KEYCODE_J)        PORT_CHAR('j')      PORT_CHAR('J')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M")           PORT_CODE(KEYCODE_M)        PORT_CHAR('m')      PORT_CHAR('M')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UP")          PORT_CODE(KEYCODE_UP)       PORT_CHAR(UCHAR_MAMEKEY(UP))        // 0x89
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y")           PORT_CODE(KEYCODE_Z)        PORT_CHAR('y')      PORT_CHAR('Y')
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6")           PORT_CODE(KEYCODE_6)        PORT_CHAR('6')      PORT_CHAR('&')

PORT_START("COL.6")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I")           PORT_CODE(KEYCODE_I)        PORT_CHAR('i')      PORT_CHAR('I')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K")           PORT_CODE(KEYCODE_K)        PORT_CHAR('k')      PORT_CHAR('k')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(", ;")         PORT_CODE(KEYCODE_COMMA)    PORT_CHAR(',')      PORT_CHAR(';')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("<>")          PORT_CODE(KEYCODE_BACKSLASH2)PORT_CHAR('<')     PORT_CHAR('>')
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7")           PORT_CODE(KEYCODE_7)        PORT_CHAR('7')      PORT_CHAR('/')

PORT_START("COL.7")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O")           PORT_CODE(KEYCODE_O)        PORT_CHAR('o')      PORT_CHAR('O')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L")           PORT_CODE(KEYCODE_L)        PORT_CHAR('l')      PORT_CHAR('L')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(". :")         PORT_CODE(KEYCODE_STOP)     PORT_CHAR('.')      PORT_CHAR(':')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Pos 1")       PORT_CODE(KEYCODE_HOME)     PORT_CHAR(UCHAR_MAMEKEY(HOME))      // 0x8f
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L_SHIFT")     PORT_CODE(KEYCODE_LSHIFT)   PORT_CHAR(UCHAR_MAMEKEY(LSHIFT))    // 3Dh ->C1h-function P3 key left
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8")           PORT_CODE(KEYCODE_8)        PORT_CHAR('8')      PORT_CHAR('(')

PORT_START("COL.8")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P")           PORT_CODE(KEYCODE_P)        PORT_CHAR('p')      PORT_CHAR('P')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"ö Ö")       PORT_CODE(KEYCODE_COLON)    PORT_CHAR(0x00f6)   PORT_CHAR(0x00d6)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("- _")         PORT_CODE(KEYCODE_SLASH)    PORT_CHAR('-')      PORT_CHAR('_')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R_CTRL")      PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))  // 44h ->84h clear ?
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENTER Pad")   PORT_CODE(KEYCODE_ENTER_PAD)PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9")           PORT_CODE(KEYCODE_9)        PORT_CHAR('9')      PORT_CHAR(')')

PORT_START("COL.9")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"ü Ü")       PORT_CODE(KEYCODE_OPENBRACE)PORT_CHAR(0x00fc)   PORT_CHAR(0x00dc)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"ä Ä")       PORT_CODE(KEYCODE_QUOTE)    PORT_CHAR(0x00e4)   PORT_CHAR(0x00c4)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R_SHIFT")     PORT_CODE(KEYCODE_RSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0 Pad")       PORT_CODE(KEYCODE_0_PAD)    PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0")           PORT_CODE(KEYCODE_0)        PORT_CHAR('0')      PORT_CHAR('=')

PORT_START("COL.10")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+ *")         PORT_CODE(KEYCODE_CLOSEBRACE)PORT_CHAR('+')     PORT_CHAR('*')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("# ^")         PORT_CODE(KEYCODE_BACKSLASH)PORT_CHAR('#')      PORT_CHAR('^')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("[][]/ESC")    PORT_CODE(KEYCODE_ESC)      PORT_CHAR(UCHAR_MAMEKEY(ESC))           // Esc test this work ?!
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DEL Pad")     PORT_CODE(KEYCODE_DEL_PAD)  PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"ß ?")       PORT_CODE(KEYCODE_MINUS)    PORT_CHAR(0x00df)   PORT_CHAR('?')      // ß and ?

PORT_START("COL.11")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TAB")         PORT_CODE(KEYCODE_TAB)      PORT_CHAR('\t')                         // TAB key
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENTER")       PORT_CODE(KEYCODE_ENTER)    PORT_CHAR(13)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1 Pad")       PORT_CODE(KEYCODE_1_PAD)    PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+ Pad")       PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"´ `")       PORT_CODE(KEYCODE_EQUALS)   PORT_CHAR(0x00b4,'\'') PORT_CHAR(0x0060)

PORT_START("COL.12")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7 Pad")       PORT_CODE(KEYCODE_7_PAD)    PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4 Pad")       PORT_CODE(KEYCODE_4_PAD)    PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2 Pad")       PORT_CODE(KEYCODE_2_PAD)    PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F4")          PORT_CODE(KEYCODE_F4)       PORT_CHAR(UCHAR_MAMEKEY(F4))             // scan 68h -> 88h func. F4

PORT_START("COL.13")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/ Pad")       PORT_CODE(KEYCODE_SLASH_PAD)PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD))
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("*")           PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(ASTERISK))  // test ?
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"® /Ctrl")   PORT_CODE(KEYCODE_LCONTROL)   PORT_CODE(KEYCODE_LCONTROL)         // scan 6Bh -> C2h funct.
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_UNKNOWN)                  // 0xc2 ??
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F3")          PORT_CODE(KEYCODE_F3)       PORT_CHAR(UCHAR_MAMEKEY(F3)) // scan:=68h 88h-> F3 ok

PORT_START("COL.14")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9 Pad")       PORT_CODE(KEYCODE_9_PAD)    PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6 Pad")       PORT_CODE(KEYCODE_6_PAD)    PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("- Pad")       PORT_CODE(KEYCODE_MINUS_PAD)PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F2")          PORT_CODE(KEYCODE_F2)       PORT_CHAR(UCHAR_MAMEKEY(F2))    // 70h -> 87h func.F2 ok

PORT_START("COL.15")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8 Pad")       PORT_CODE(KEYCODE_8_PAD)    PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5 Pad")       PORT_CODE(KEYCODE_5_PAD)    PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3 Pad")       PORT_CODE(KEYCODE_3_PAD)    PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F1")          PORT_CODE(KEYCODE_F1)       PORT_CHAR(UCHAR_MAMEKEY(F1))    // 7Dh -> 85H func. F1 ok
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SM")          PORT_CODE(KEYCODE_NUMLOCK)  PORT_CHAR(UCHAR_MAMEKEY(NUMLOCK))// SM (typewriter) mode key */

PORT_START("SCREEN")
	PORT_CONFNAME(0x01, 0x00, "Screen")
	PORT_CONFSETTING(0x00, "Main")
	PORT_CONFSETTING(0x01, "Extension")

INPUT_PORTS_END

//**************************************************************************
//  VIDEO - Alphatronic Px
//**************************************************************************

static const gfx_layout charlayout =
{
	8, 12,
	RGN_FRAC(1,1),
	1,
	{ RGN_FRAC(0,1) },
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8*16
};

static const gfx_layout extcharlayout =
{
	8, 12,
	256,
	1,
	{ 0 },
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8*12
};

//**************************************************************************
//  VIDEO - Alphatronic P1, P2, P2S, P2U and Hell 2069
//**************************************************************************

u32 alphatp_12_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	pen_t const *const pen = m_palette->pens();
	int const start = m_crtc->upscroll_offset();
	rectangle cursor;
	m_crtc->cursor_bounds(cursor);
	int left = cliprect.left() / 8;
	int right = cliprect.right() / 8;
	for (int y = cliprect.top(); y <= cliprect.bottom(); y++)
	{
		int const vramy = (start + y/12) % 24;
		int const line = y % 12;
		for (int x = left; x <= right; x++)
		{
			u8 code = m_vram[(vramy * 128) + x];   // helwie44 must be 128d is 080h physical display-ram step line
			bool const cursoren = cursor.contains(x * 8, y);
			u8 data = m_gfx[((code & 0x7f) * 16) + line];
			if (cursoren)
				data ^= 0xff;
			bitmap.pix(y, x * 8 + 0) = pen[BIT(data, 0) ^ BIT(code, 7)];
			bitmap.pix(y, x * 8 + 1) = pen[BIT(data, 1) ^ BIT(code, 7)];
			bitmap.pix(y, x * 8 + 2) = pen[BIT(data, 2) ^ BIT(code, 7)];
			bitmap.pix(y, x * 8 + 3) = pen[BIT(data, 3) ^ BIT(code, 7)];
			bitmap.pix(y, x * 8 + 4) = pen[BIT(data, 4) ^ BIT(code, 7)];
			bitmap.pix(y, x * 8 + 5) = pen[BIT(data, 5) ^ BIT(code, 7)];
			bitmap.pix(y, x * 8 + 6) = pen[BIT(data, 6) ^ BIT(code, 7)];
			bitmap.pix(y, x * 8 + 7) = pen[BIT(data, 7) ^ BIT(code, 7)];
		}
	}

	return 0;
}

//**************************************************************************
//  VIDEO - Alphatronic P3, P4, P30 and P40
//**************************************************************************

static GFXDECODE_START( gfx_alphatp3 )
	GFXDECODE_ENTRY("gfx", 0, charlayout, 0, 1)
GFXDECODE_END

u32 alphatp_34_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	pen_t const *const pen = m_palette->pens();
	int const start = m_crtc->upscroll_offset();
	rectangle cursor;
	m_crtc->cursor_bounds(cursor);
	int left = cliprect.left() / 8;
	int right = cliprect.right() / 8;
	bool const scrext = m_scncfg->read() ? true : false;
	for (int y = cliprect.top(); y <= cliprect.bottom(); y++)
	{
		int const vramy = (start + y/12) % 24;
		int const line = y % 12;
		for (int x = left; x <= right; x++)
		{
			u8 code = m_vram[(vramy * 128) + x];   // helwie44 must be 128d is 080h physical display-ram step line
			bool const cursoren = cursor.contains(x * 8, y);
			u8 data = 0;
			if (scrext)
			{
				offs_t offset = (((vramy * 12) + line) * 80) + x;
				if (offset < (371 * 80))
					data = m_vramext[offset];
				code = 0;
			}
			else
			{
				data = m_gfx[((code & 0x7f) * 16) + line];
				if (cursoren)
					data ^= 0xff;
			}
			bitmap.pix(y, x * 8 + 0) = pen[BIT(data, 0) ^ BIT(code, 7)];
			bitmap.pix(y, x * 8 + 1) = pen[BIT(data, 1) ^ BIT(code, 7)];
			bitmap.pix(y, x * 8 + 2) = pen[BIT(data, 2) ^ BIT(code, 7)];
			bitmap.pix(y, x * 8 + 3) = pen[BIT(data, 3) ^ BIT(code, 7)];
			bitmap.pix(y, x * 8 + 4) = pen[BIT(data, 4) ^ BIT(code, 7)];
			bitmap.pix(y, x * 8 + 5) = pen[BIT(data, 5) ^ BIT(code, 7)];
			bitmap.pix(y, x * 8 + 6) = pen[BIT(data, 6) ^ BIT(code, 7)];
			bitmap.pix(y, x * 8 + 7) = pen[BIT(data, 7) ^ BIT(code, 7)];
		}
	}

	return 0;
}

//**************************************************************************
//  SOUND - Alphatronic P1, P2, P2S, P2U and Hell 2069
//**************************************************************************

void alphatp_12_state::beep_w(u8 data)
{
	m_beep->set_state(data&1);
}

//**************************************************************************
//  SOUND - Alphatronic P3, P4, P30 and P40
//**************************************************************************

void alphatp_34_state::beep_w(u8 data)
{
	m_beep->set_state(data&1);
}

//**************************************************************************
//  FLOPPY - Alphatronic P1, P2, P2S, P2U and Hell 2069
//**************************************************************************

void alphatp_12_state::fdcirq_w(int state)
{
	m_fdc_irq = state;
}

void alphatp_12_state::fdcdrq_w(int state)
{
	m_fdc_drq = state;
}

void alphatp_12_state::fdchld_w(int state)
{
	m_fdc_hld = state;
}

u8 alphatp_12_state::fdc_stat_r()
{
	u8 res = 0;
	floppy_image_device *floppy1,*floppy2;
	floppy1 = floppy2 = nullptr;

	floppy1 = m_floppy[0] ? m_floppy[0]->get_device() : nullptr;
	floppy2 = m_floppy[1] ? m_floppy[1]->get_device() : nullptr;

	res = m_fdc_drq ? 0x80 : 0x00;
	res |= m_fdc_irq ? 0x40 : 0x00;
	res |= m_fdc_hld ? 0x00 : 0x20;

	if (floppy2) res |= !floppy2->ready_r() ? 0x08 : 0;
	if (floppy1) res |= !floppy1->ready_r() ? 0x04 : 0;
	if (m_curfloppy) res |= m_curfloppy->wpt_r() ? 0x02 : 0;

	return res;
}

u8 alphatp_12_state::fdc_r(offs_t offset)
{
	return m_fdc->read(offset) ^ 0xff;
}

void alphatp_12_state::fdc_w(offs_t offset, u8 data)
{
	m_fdc->write(offset, data ^ 0xff);
}


void alphatp_12_state::fdc_cmd_w(u8 data)
{
	floppy_image_device *floppy = nullptr;

	//logerror("%02x to fdc_cmd_w: motor %d side %d\n", data, (data & 0x10)>>4, (data & 4)>>2);

	// select drive
	if (!(data & 0x80))
	{
		floppy = m_floppy[0] ? m_floppy[0]->get_device() : nullptr;
	}
	else if (!(data & 0x40))
	{
		floppy = m_floppy[1] ? m_floppy[1]->get_device() : nullptr;
	}

	// selecting a new drive?
	if (floppy != m_curfloppy)
	{
		m_fdc->set_floppy(floppy);
		m_curfloppy = floppy;
	}

	if (floppy != nullptr)
	{
		// side select
		floppy->ss_w((data & 4) ? 0 : 1);

		// motor control (active low)
		floppy->mon_w((data & 0x10) ? 1 : 0);
	}
}

//**************************************************************************
//  FLOPPY - Alphatronic P3, P4, P30 and P40
//**************************************************************************

void alphatp_34_state::fdcirq_w(int state)
{
	m_fdc_irq = state;
}

void alphatp_34_state::fdcdrq_w(int state)
{
	m_fdc_drq = state;
}

void alphatp_34_state::fdchld_w(int state)
{
	m_fdc_hld = state;
}

u8 alphatp_34_state::fdc_stat_r()
{
	u8 res = 0;
	floppy_image_device *floppy1 = m_floppy[0]->get_device();
	floppy_image_device *floppy2 = m_floppy[1]->get_device();

	res = m_fdc_drq ? 0x80 : 0x00;
	res |= m_fdc_irq ? 0x40 : 0x00;
	res |= m_fdc_hld ? 0x00 : 0x20;
	if (floppy2) res |= floppy2->ready_r() ? 0x08 : 0;
	if (floppy1) res |= floppy1->ready_r() ? 0x04 : 0;
	if (m_curfloppy) res |= m_curfloppy->wpt_r() ? 0x02 : 0;

	return res;
}

u8 alphatp_34_state::fdc_r(offs_t offset)
{
	return m_fdc->read(offset) ^ 0xff;
}

void alphatp_34_state::fdc_w(offs_t offset, u8 data)
{
	m_fdc->write(offset, data ^ 0xff);
}


void alphatp_34_state::fdc_cmd_w(u8 data)
{
	floppy_image_device *floppy = nullptr;

	//logerror("%02x to fdc_cmd_w: motor %d side %d\n", data, (data & 0x10)>>4, (data & 4)>>2);

	// select drive
	if (!(data & 0x80))
		floppy = m_floppy[0]->get_device();
	else if (!(data & 0x40))
		floppy = m_floppy[1]->get_device();

	// selecting a new drive?
	if (floppy != m_curfloppy)
	{
		m_fdc->set_floppy(floppy);
		m_curfloppy = floppy;
	}

	if (floppy != nullptr)
	{
		// side select
		floppy->ss_w((data & 4) ? 0 : 1);

		// motor control (active low)
		floppy->mon_w((data & 0x10) ? 1 : 0);
	}
}

//**************************************************************************
//  FLOPPY - Drive definitions
//**************************************************************************

static void alphatp2_floppies(device_slot_interface &device) // two BASF 2471 drives
{
	device.option_add("525ssdd", FLOPPY_525_SSDD);
}

static void alphatp2su_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

static void alphatp3_floppies(device_slot_interface &device) // P3:  two BASF 6106 drives
{
	device.option_add("525qd", FLOPPY_525_QD);       // P30: two Shugart SA465-3AA drives
}

//**************************************************************************
//  MACHINE - Alphatronic P1, P2, P2S, P2U and Hell 2069
//**************************************************************************

void alphatp_12_state::machine_start()
{
	// setup banking
	membank("ram_0000")->set_base(m_ram.target());


	m_kbdclk = 0;   // must be initialized here b/c mcs48_reset() causes write of 0xff to all ports
}

void alphatp_12_state::machine_reset()
{
	m_kbdread = 1;
	m_kbdclk = 1;   m_fdc_irq = m_fdc_drq = m_fdc_hld = 0;
	m_curfloppy = nullptr;
}

void alphatp_12_state::alphatp2(machine_config &config)
{
	i8085a_cpu_device &maincpu(I8085A(config, "maincpu", 6_MHz_XTAL));
	maincpu.set_addrmap(AS_PROGRAM, &alphatp_12_state::alphatp2_mem);
	maincpu.set_addrmap(AS_IO, &alphatp_12_state::alphatp2_io);

	config.set_perfect_quantum("maincpu");

	I8041A(config, m_kbdmcu, 12.8544_MHz_XTAL / 2);
	m_kbdmcu->t0_in_cb().set(FUNC(alphatp_12_state::kbd_matrix_r));
	m_kbdmcu->p1_out_cb().set(FUNC(alphatp_12_state::kbd_matrix_w));
	m_kbdmcu->p2_in_cb().set(FUNC(alphatp_12_state::kbd_port2_r));
	m_kbdmcu->p2_out_cb().set(FUNC(alphatp_12_state::kbd_port2_w));

	ADDRESS_MAP_BANK(config, "bankdev").set_map(&alphatp_12_state::alphatp2_map).set_options(ENDIANNESS_LITTLE, 8, 18, 0x10000);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_raw(12.8544_MHz_XTAL, 824, 0, 640, 312, 0, 288);
	screen.set_screen_update(FUNC(alphatp_12_state::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	CRT5027(config, m_crtc, 12.8544_MHz_XTAL / 8);
	m_crtc->set_char_width(8);
	m_crtc->hsyn_callback().set_inputline("maincpu", I8085_RST55_LINE);
	m_crtc->vsyn_callback().set_inputline("maincpu", I8085_RST65_LINE).exor(1);
	m_crtc->set_screen("screen");

	GFXDECODE(config, "gfxdecode", m_palette, gfx_alphatp3);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 1060).add_route(ALL_OUTPUTS, "mono", 1.00);

	I8251(config, "uart", 0);
	// 4.9152_MHz_XTAL serial clock

	FD1791(config, m_fdc, 4_MHz_XTAL / 4);
	m_fdc->intrq_wr_callback().set(FUNC(alphatp_12_state::fdcirq_w));
	m_fdc->drq_wr_callback().set(FUNC(alphatp_12_state::fdcdrq_w));
	m_fdc->hld_wr_callback().set(FUNC(alphatp_12_state::fdchld_w));
	FLOPPY_CONNECTOR(config, "fdc:0", alphatp2_floppies, "525ssdd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", alphatp2_floppies, "525ssdd", floppy_image_device::default_mfm_floppy_formats);
}

void alphatp_12_state::alphatp2u(machine_config &config)
{
	alphatp2(config);
	config.device_remove("fdc:0");
	config.device_remove("fdc:1");
	FLOPPY_CONNECTOR(config, "fdc:0", alphatp2su_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", alphatp2su_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);
}


//**************************************************************************
//  MACHINE - Alphatronic P3, P4, P30 and P40
//**************************************************************************

void alphatp_34_state::machine_start()
{
	// setup banking
	membank("ram_0000")->set_base(m_ram.target());
	save_item(NAME(m_vramext));

	m_kbdclk = 0;   // must be initialized here b/c mcs48_reset() causes write of 0xff to all ports
	if (m_i8088)
		m_gfxdecode->set_gfx(1, std::make_unique<gfx_element>(m_palette, extcharlayout, &m_vramchr[0], 0, 1, 0));
}

void alphatp_34_state::machine_reset()
{
	m_kbdread = 1;
	m_kbdclk = 1;   m_fdc_irq = m_fdc_drq = m_fdc_hld = 0;
	m_curfloppy = nullptr;
	m_88_da = m_85_da = m_88_started = false;
}
void alphatp_34_state::alphatp3(machine_config &config)
{
	i8085a_cpu_device &maincpu(I8085A(config, "maincpu", 6_MHz_XTAL));
	maincpu.set_addrmap(AS_PROGRAM, &alphatp_34_state::alphatp3_mem);
	maincpu.set_addrmap(AS_IO, &alphatp_34_state::alphatp3_io);

	config.set_perfect_quantum("maincpu");

	I8041A(config, m_kbdmcu, 12.8544_MHz_XTAL / 2);
	m_kbdmcu->t0_in_cb().set(FUNC(alphatp_34_state::kbd_matrix_r));
	m_kbdmcu->p1_out_cb().set(FUNC(alphatp_34_state::kbd_matrix_w));
	m_kbdmcu->p2_in_cb().set(FUNC(alphatp_34_state::kbd_port2_r));
	m_kbdmcu->p2_out_cb().set(FUNC(alphatp_34_state::kbd_port2_w));

	ADDRESS_MAP_BANK(config, "bankdev").set_map(&alphatp_34_state::alphatp3_map).set_options(ENDIANNESS_LITTLE, 8, 18, 0x10000);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_raw(12.8544_MHz_XTAL, 824, 0, 640, 312, 0, 288);
	screen.set_screen_update(FUNC(alphatp_34_state::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	CRT5037(config, m_crtc, 12.8544_MHz_XTAL / 8);
	m_crtc->set_char_width(8);
	m_crtc->vsyn_callback().set_inputline("maincpu", I8085_RST65_LINE).exor(1);
	m_crtc->set_screen("screen");

	GFXDECODE(config, "gfxdecode", m_palette, gfx_alphatp3);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 1060).add_route(ALL_OUTPUTS, "mono", 1.00);

	I8251(config, "uart", 0);
	// 4.9152_MHz_XTAL serial clock

	FD1791(config, m_fdc, 4_MHz_XTAL / 4);
	m_fdc->intrq_wr_callback().set(FUNC(alphatp_34_state::fdcirq_w));
	m_fdc->drq_wr_callback().set(FUNC(alphatp_34_state::fdcdrq_w));
	m_fdc->hld_wr_callback().set(FUNC(alphatp_34_state::fdchld_w));
	FLOPPY_CONNECTOR(config, "fdc:0", alphatp3_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", alphatp3_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
}

void alphatp_34_state::alphatp30(machine_config &config)
{
	alphatp3(config);
	I8088(config, m_i8088, 6000000); // unknown clock
	m_i8088->set_addrmap(AS_PROGRAM, &alphatp_34_state::alphatp30_8088_map);
	m_i8088->set_addrmap(AS_IO, &alphatp_34_state::alphatp30_8088_io);
	m_i8088->set_irq_acknowledge_callback("pic8259", FUNC(pic8259_device::inta_cb));
	m_i8088->set_disable();

	PIC8259(config, m_pic, 0);
	m_pic->out_int_callback().set_inputline(m_i8088, 0);
	m_pic->in_sp_callback().set_constant(0);

	pit8253_device &pit(PIT8253(config, "pit", 0));
	pit.set_clk<0>(100000);  // 15Mhz osc with unknown divisor
	pit.set_clk<1>(100000);
	pit.set_clk<2>(100000);
	pit.out_handler<0>().set(m_pic, FUNC(pic8259_device::ir0_w));
}

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

// Alphatronic P1
ROM_START( alphatp1 )
	ROM_REGION(0x1800, "boot", 0)
	ROM_LOAD("p1mos1.bin", 0x0000, 0x0800, CRC(9317a694) SHA1(3b51a6b72d2ccae2459ddb2e16fbd21b19dfa2b8) )
	ROM_LOAD("p1mos2.bin", 0x0800, 0x0800, CRC(f38113a3) SHA1(078405ad202e26b7bac7132b06682fb01270af63) )
	ROM_LOAD("p1mos3.bin", 0x1000, 0x0800, CRC(fb5ae050) SHA1(ba55553764326dfda3fbd35237761c3fb6fde18a) )

	ROM_REGION(0x400, "kbdmcu", 0)
	ROM_LOAD("p2_keyboard_ip8041a_8278.bin",  0x000, 0x400, CRC(5db00d85) SHA1(0dc8e274a5aece261ef60494901601c0d8b1eb51))   // P1 keyboard driver is contained in a MF-1702AR on the keyboard
																															// needs to be dumped
	ROM_REGION(0x800, "gfx", 0)
	ROM_LOAD("p1chargen.bin", 0x000, 0x800, CRC(51ea8a7e) SHA1(c514df7ab3761490af4a16c9106d08540f0d7352))
ROM_END

// Alphatronic P2
ROM_START( alphatp2 ) // P2 ROM space 0x1800
	ROM_REGION(0x1800, "boot", 0)
	ROM_SYSTEM_BIOS(0, "caap94-96", "caap94-96")
	ROM_SYSTEM_BIOS(1, "caap04-06", "caap04-06")
	ROM_SYSTEM_BIOS(2, "p2_es", "p2_es")

	ROMX_LOAD("caap_96_00_5a.bin", 0x0000, 0x0800, CRC(cb137796) SHA1(876bd0762faffc7b74093922d8fbf1c72ec70060), ROM_BIOS(0) ) // earlier P2, three 16K RAM boards
	ROMX_LOAD("caap_05_02_12.bin", 0x0800, 0x0800, CRC(14f19693) SHA1(7ecb66818a3e352fede1857a18cd12bf742603a9), ROM_BIOS(0) )
	ROMX_LOAD("caap_94_01_50.bin", 0x1000, 0x0800, CRC(fda8d4a4) SHA1(fa91e6e8504e7f84cf69d86f72826ad5405fd82d), ROM_BIOS(0) )

	ROMX_LOAD("caap_06_00_17.bin", 0x0000, 0x0800, CRC(cb137796) SHA1(876bd0762faffc7b74093922d8fbf1c72ec70060), ROM_BIOS(1) ) // later P2, one 48K RAM board
	ROMX_LOAD("caap_05_01_12.bin", 0x0800, 0x0800, CRC(98c5ec7a) SHA1(b170e9a73b0b64d4111fa1170af6e333793da479), ROM_BIOS(1) )
	ROMX_LOAD("caap_04_01_03.bin", 0x1000, 0x0800, CRC(f304c3aa) SHA1(92213e77e4e6de12a4eaee25a9c1ec0ab54e93d4), ROM_BIOS(1) )

	ROMX_LOAD("caap_p2_es_1.bin", 0x0000, 0x0800, CRC(91b58eb3) SHA1(a4467cf9a14366198ee5f676b9471734e820522d), ROM_BIOS(2) )   // P2 Spain
	ROMX_LOAD("caap_p2_es_2.bin", 0x0800, 0x0800, CRC(f4dfac82) SHA1(266d1fdaef875515d9c68ae3e67ec88831bb55cb), ROM_BIOS(2) )
	ROMX_LOAD("caap_p2_es_3.bin", 0x1000, 0x0800, CRC(6f6918ba) SHA1(8dc9f5e337df8abf42e5b55f5f1a1a9d61c42d78), ROM_BIOS(2) )

	ROM_REGION(0x400, "kbdmcu", 0)                                                                                          // same across all dumped P2 and P3 machines so far
	ROM_LOAD("p2_keyboard_ip8041a_8278.bin",  0x000, 0x400, CRC(5db00d85) SHA1(0dc8e274a5aece261ef60494901601c0d8b1eb51))   // needs to be checked with P2 sks and Spain

	ROM_REGION(0x800, "gfx", 0)
	ROMX_LOAD("cajp_97_00_5a.bin", 0x000, 0x800, CRC(aa5eab85) SHA1(2718924f5520e7e9aad635786847e78e3096b285), ROM_BIOS(0)) // came with caap94-96
	ROMX_LOAD("cajp_01_01_28.bin", 0x000, 0x800, CRC(d6248209) SHA1(21689703de7183ecffb410eb8a6d516efe27da9d), ROM_BIOS(1)) // came with caap04-06
	ROMX_LOAD("cajp_01_01_28.bin", 0x000, 0x800, CRC(d6248209) SHA1(21689703de7183ecffb410eb8a6d516efe27da9d), ROM_BIOS(2)) // sks KISS chargen not dumped yet
	// FIXME: no BIOS option for selecting this ROMX_LOAD("cajp_01_01_28.bin", 0x000, 0x800, CRC(d6248209) SHA1(21689703de7183ecffb410eb8a6d516efe27da9d), ROM_BIOS(3)) // spanish P2 chargen not dumped yet
ROM_END

// Alphatronic P2U
ROM_START( alphatp2u )
	ROM_REGION(0x1800, "boot", 0)
	ROM_LOAD("mos3-p2_sks_1", 0x0000, 0x0800, CRC(c98d2982) SHA1(11e98ae441b7a9c8dfd22795f8208667959f1d1c) )
	ROM_LOAD("mos3-p2_sks_2", 0x0800, 0x0800, CRC(14f19693) SHA1(7ecb66818a3e352fede1857a18cd12bf742603a9) )
	ROM_LOAD("mos3-p2_sks_3", 0x1000, 0x0800, CRC(f304c3aa) SHA1(92213e77e4e6de12a4eaee25a9c1ec0ab54e93d4) )

	ROM_REGION(0x400, "kbdmcu", 0)                                                                                          // same across all dumped P2 and P3 machines so far
	ROM_LOAD("p2_keyboard_ip8041a_8278.bin",  0x000, 0x400, CRC(5db00d85) SHA1(0dc8e274a5aece261ef60494901601c0d8b1eb51))   // needs to be checked for P2U

	ROM_REGION(0x800, "gfx", 0)
	ROM_LOAD("cajp_01_01_28.bin", 0x000, 0x800, CRC(d6248209) SHA1(21689703de7183ecffb410eb8a6d516efe27da9d))   // P2U chargen needs to be dumped
ROM_END

// Alphatronic P3
ROM_START( alphatp3 )
	ROM_REGION(0x1800, "boot", 0) // P3 ROM space 0x1000
	ROM_SYSTEM_BIOS(0, "gx347", "gx347") // earlier P3, separate 48K and 16K RAM boards
	ROM_SYSTEM_BIOS(1, "lb352", "lb352") // later P3, one 64K RAM board

	ROM_LOAD("caap36_02_19.bin", 0x0000, 0x1000, CRC(23df6666) SHA1(5ea04cd299dec9951425eb91ecceb4818c4c6378) ) // identical between earlier and later P3
																												// BIOS names taken from CPU card labels
	ROM_REGION(0x400, "kbdmcu", 0)
	ROM_LOAD("p3_keyboard_8278.bin",  0x000, 0x400, CRC(5db00d85) SHA1(0dc8e274a5aece261ef60494901601c0d8b1eb51) )

	ROM_REGION(0x800, "gfx", 0)
	ROMX_LOAD("cajp08_00_15.bin", 0x000, 0x800, CRC(d6248209) SHA1(21689703de7183ecffb410eb8a6d516efe27da9d), ROM_BIOS(0) )
	ROMX_LOAD("cajp08_01_15.bin", 0x000, 0x800, CRC(4ed11dac) SHA1(9db9b8e0edf471faaddbb5521d6223121146bab8), ROM_BIOS(1) )
ROM_END

// Alphatronic P30
ROM_START( alphatp30 ) // P30 add-on card with 8088 needs to be emulated to boot DOS
	ROM_REGION(0x1800, "boot", 0)
	ROM_LOAD("hasl17.07.84.bin", 0x0000, 0x1000, CRC(6a91701b) SHA1(8a4f925d0fabab37852a54d04e06deb2aeaa349c))  // ...wait for INT6.5 or INT5.5 with RIM to write char in hsync or hsync GAP-time !!

	ROM_REGION(0x400, "kbdmcu", 0)
	ROM_LOAD("caju_01_01_01.bin",  0x000, 0x400, CRC(e9b4359f) SHA1(835f4a580b4c108ef2f239039b765324adc7f078))

	ROM_REGION(0x800, "gfx", 0)
	ROM_LOAD("cajp08_01_15.bin", 0x000, 0x800, CRC(4ed11dac) SHA1(9db9b8e0edf471faaddbb5521d6223121146bab8))

	ROM_REGION(0x4000, "16bit", 0)
	ROM_LOAD("caxp_02_02_13.bin", 0x00000, 0x2000, CRC(e6bf6dd5) SHA1(dc87210bbcd96f3c1370565174a45199e3c1bc70)) // P30 ROM from 8088 card
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME       PARENT    COMPAT   MACHINE    INPUT     CLASS             INIT        COMPANY          FULLNAME           FLAGS
COMP( 198?, alphatp1,  alphatp2, 0,       alphatp2,  alphatp2, alphatp_12_state, empty_init, "Triumph-Adler", "alphatronic P1",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 198?, alphatp2,  0,        0,       alphatp2,  alphatp2, alphatp_12_state, empty_init, "Triumph-Adler", "alphatronic P2",  MACHINE_NOT_WORKING )
COMP( 198?, alphatp2u, alphatp2, 0,       alphatp2u, alphatp3, alphatp_12_state, empty_init, "Triumph-Adler", "alphatronic P2U", MACHINE_NOT_WORKING )
COMP( 1982, alphatp3,  0,        0,       alphatp3,  alphatp3, alphatp_34_state, empty_init, "Triumph-Adler", "alphatronic P3",  MACHINE_NOT_WORKING )
COMP( 198?, alphatp30, alphatp3, 0,       alphatp30, alphatp3, alphatp_34_state, empty_init, "Triumph-Adler", "alphatronic P30", MACHINE_NOT_WORKING )



alphatro.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Barry Rodewald, Robbbert, R. Belmont, Carl
/***************************************************************************

    Triumph-Adler (or Royal) Alphatronic PC

    Driver by Barry Rodewald, Robbbert, R. Belmont and Carl

    Z80 + HD46505SP as a CRTC

    Other chips: 8251, 8257, 8259
    Crystals: 16MHz, 17.73447MHz
    Floppy format: 13cm, 2 sides, 40 tracks, 16 sectors, 256 bytes = 320kb.
    FDC (uPD765) is in a plug-in module, there is no ROM on the module.

    A configuration switch determines if the FDC is present.

    Has a socket for monochrome (to the standard amber monitor),
    and another for RGB. We emulate this with a configuration switch.

    Natural keyboard is set up for bios 1.

    TODO:
    - UKC romset should boot to a blue screen

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

#include "emu.h"
#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/ram.h"
#include "machine/bankdev.h"
#include "machine/upd765.h"
#include "machine/i8257.h"
#include "machine/pic8259.h"
#include "bus/centronics/ctronics.h"
#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "bus/rs232/rs232.h"
#include "sound/beep.h"
#include "video/mc6845.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class alphatro_state : public driver_device
{
public:
	alphatro_state(const machine_config &mconfig, device_type type, const char *tag, bool is_ntsc, bool is_bicom)
		: driver_device(mconfig, type, tag)
		, m_is_ntsc(is_ntsc)
		, m_is_bicom(is_bicom)
		, m_ram(*this, RAM_TAG)
		, m_p_videoram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
		, m_maincpu(*this, "maincpu")
		, m_crtc(*this, "crtc")
		, m_usart(*this, "usart")
		, m_cass(*this, "cassette")
		, m_beep(*this, "beeper")
		, m_palette(*this, "palette")
		, m_lowbank(*this, "lowbank")
		, m_cartbank(*this, "cartbank")
		, m_monbank(*this, "monbank")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0U)
		, m_dmac(*this, "dmac")
		, m_pic(*this, "pic")
		, m_centronics(*this, "centronics")
		, m_config(*this, "CONFIG")
		, m_cart(*this, "cartslot")
	{ }

	void alphatro(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(alphatro_break);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	uint8_t bicom_r(offs_t offset);
	void bicom_w(offs_t offset, uint8_t data);
	uint8_t port10_r();
	void port10_w(uint8_t data);
	void port20_w(uint8_t data);
	uint8_t port30_r();
	uint8_t portf0_r();
	void portf0_w(uint8_t data);
	void hrq_w(int state);
	void alphatro_palette(palette_device &palette) const;
	void kansas_r(int state);
	void kansas_w(int state);
	MC6845_UPDATE_ROW(crtc_update_row);

	std::pair<std::error_condition, std::string> load_cart(device_image_interface &image, generic_slot_device *slot);
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load) { return load_cart(image, m_cart); }

	void alphatro_io(address_map &map) ATTR_COLD;
	void alphatro_map(address_map &map) ATTR_COLD;
	void cartbank_map(address_map &map) ATTR_COLD;
	void monbank_map(address_map &map) ATTR_COLD;
	void rombank_map(address_map &map) ATTR_COLD;
	void update_banking();

	const bool m_is_ntsc;
	const bool m_is_bicom;
	uint8_t *m_ram_ptr;
	required_device<ram_device> m_ram;
	required_shared_ptr<u8> m_p_videoram;
	u8 m_flashcnt = 0U;
	u8 m_cass_data[4]{};
	u8 m_port_10 = 0U, m_port_20 = 0U, m_port_f0 = 0U;
	bool m_cassbit = 0;
	bool m_cassold = 0, m_fdc_irq = 0;
	required_region_ptr<u8> m_p_chargen;
	required_device<cpu_device> m_maincpu;
	required_device<mc6845_device> m_crtc;
	required_device<i8251_device> m_usart;
	required_device<cassette_image_device> m_cass;
	required_device<beep_device> m_beep;
	required_device<palette_device> m_palette;
	required_device<address_map_bank_device> m_lowbank, m_cartbank, m_monbank;
	required_device<upd765a_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<i8257_device> m_dmac;
	required_device<pic8259_device> m_pic;
	required_device<centronics_device> m_centronics;
	required_ioport m_config;
	required_device<generic_slot_device> m_cart;

	int m_centronics_ack = 0;
	int m_centronics_busy = 0;

	std::unique_ptr<uint8_t[]> m_bicom_ram;
	uint16_t m_bicom_addr = 0U;
	uint8_t m_bicom_en = 0U;
};

class alphatro_pal_state : public alphatro_state
{
public:
	alphatro_pal_state(const machine_config &mconfig, device_type type, const char *tag)
		: alphatro_state(mconfig, type, tag, false, false)
	{ }
};

class alphatro_ntsc_state : public alphatro_state
{
public:
	alphatro_ntsc_state(const machine_config &mconfig, device_type type, const char *tag)
		: alphatro_state(mconfig, type, tag, true, false)
	{ }
};

class alphatro_bicom_state : public alphatro_state
{
public:
	alphatro_bicom_state(const machine_config &mconfig, device_type type, const char *tag)
		: alphatro_state(mconfig, type, tag, false, true)
	{ }
};

void alphatro_state::update_banking()
{
	if (BIT(m_port_10, 7))   // ROM access enable at 0000
	{
		m_lowbank->set_bank(1);
	}
	else
	{
		m_lowbank->set_bank(0);
	}

	if (BIT(m_port_10, 6))   // ROM pack enable at A000
	{
		m_cartbank->set_bank(0);
	}
	else
	{
		m_cartbank->set_bank(1);
	}

	if (BIT(m_port_20, 3))   // select V-RAM at F000
	{
		m_monbank->set_bank(0);
	}
	else
	{
		if (BIT(m_port_20, 6))   // Monitor ROM address select
		{
			m_monbank->set_bank(2);
		}
		else
		{
			m_monbank->set_bank(1);
		}
	}
}

uint8_t alphatro_state::bicom_r(offs_t offset)
{
	u8 retval = 0x00;

	switch (offset & 0x03)
	{
	case 0:
		// color code
		retval = m_bicom_ram[m_bicom_addr];
		break;
	case 1:
		// address low
		retval = m_bicom_addr & 0x00ff;
		break;
	case 2:
		// address high
		retval = (m_bicom_addr & 0xff00) >> 8;
		break;
	case 3:
		// graphic enable
		retval = m_bicom_en;
		break;
	}

	return retval;
}

void alphatro_state::bicom_w(offs_t offset, uint8_t data)
{
	switch (offset & 0x03)
	{
	case 0:
		// color code
		m_bicom_ram[m_bicom_addr] = data;
		break;
	case 1:
		// address low
		m_bicom_addr = (m_bicom_addr & 0xff00) | data;
		break;
	case 2:
		// address high
		m_bicom_addr = (m_bicom_addr & 0x00ff) | (data << 8);
		break;
	case 3:
		// graphic enable
		m_bicom_en = data;
		break;
	}
}

uint8_t alphatro_state::port10_r()
{
// Bit 0 -> 1 = FDC is installed, 0 = not
// Bit 1 -> 1 = Graphic Board is installed, 0 = not
// Bits 2-4 = Country select: 0 = Intl, 1 = German, 2 = US
// Bit 5 -> 1 = BASIC LPRINT is RS-232, 0 = BASIC LPRINT is Centronics
// Bit 6 -> 1 = NTSC, 0 = PAL
// Bit 7 -> 1 = vblank or hblank, 0 = active display area

	u8 retval = m_is_ntsc ? 0x40 : 0x00;

	// we'll get "FDC present" from the config switches
	retval |= (m_config->read() & 0x3d);
	retval |= m_is_bicom ? 0x02 : 0x00;

	if (!m_crtc->de_r())
	{
		retval |= 0x80;
	}

	return retval;
}

void alphatro_state::port10_w(uint8_t data)
{
// Bit 0 -> 0 = 40 cols; 1 = 80 cols
// Bit 1 -> 0 = display enable, 1 = display inhibit
// Bit 2 -> 0 = USART is connected to cassette, 1 = RS232 port
// Bit 3 -> 0 = cassette motor off, 1 = cassette motor on
// Bit 4 -> 0 = beeper off, 1 = beeper on
// Bit 5 -> always 0
// Bit 6 -> 1 = select ROM pack at A000, 0 = RAM at A000
// Bit 7 -> 0 = ROM enabled at 0, 1 = RAM enabled

	if (BIT(data ^ m_port_10, 0))
	{
		if (BIT(data, 0))
		{
			if (m_is_bicom)
				m_crtc->set_unscaled_clock(14.318181_MHz_XTAL / 8);
			else
				m_crtc->set_unscaled_clock(16_MHz_XTAL / 8);
		}
		else if (m_is_ntsc || m_is_bicom)
		{
			m_crtc->set_unscaled_clock(14.318181_MHz_XTAL / 16);
		}
		else
		{
			m_crtc->set_unscaled_clock(17.73447_MHz_XTAL / 16);
		}
	}

	m_port_10 = data;

	m_beep->set_state(BIT(data, 4));

	m_cass->change_state( BIT(data, 3) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);

	if (BIT(data,2))
		m_cassbit = 1;

	update_banking();
}

void alphatro_state::port20_w(uint8_t data)
{
// Bit 0 -> 0 = CRTC reset release, 1 = CRTC reset enable
// Bit 1 -> 0 = Centronics reset release, 1 = Centronics reset enable
// Bit 2 -> 0 = no Centronics strobe, 1 = Centronics strobe active
// Bit 3 -> 0 = Monitor ROM at F000, 1 = VRAM at F000
// Bit 4 -> 0 = Graphic LED off, 1 = Graphic LED on
// Bit 5 -> 0 = Shift Lock LED off, 1 = Shift Lock LED on
// Bit 6 -> 0 = Lower 4K of Monitor at F000, 1 = Upper 4K of Monitor at F000
// Bit 7 -> N/A

	m_centronics->write_strobe(BIT(data, 2));

	m_port_20 = data;

	update_banking();
}

uint8_t alphatro_state::port30_r()
{
// Bit 0 -> SIOC
// Bit 1 -> 1 = vsync, 0 = not
// Bit 2 -> 1 = Centronics ACK, 0 = not
// Bit 3 -> 1 = Centronics BUSY, 0 = not

	u8 retval = 0;

	if (m_crtc->vsync_r()) retval |= 0x02;
	if (m_centronics_ack)  retval |= 0x04;
	if (m_centronics_busy) retval |= 0x08;

	return retval;
}

uint8_t alphatro_state::portf0_r()
{
	return m_fdc_irq << 6;
}

void alphatro_state::portf0_w(uint8_t data)
{
	if ((data & 0x1) && !(m_port_f0))
	{
		m_fdc->reset();

		for (auto &con : m_floppy)
		{
			floppy_image_device *floppy = con->get_device();
			if (floppy)
			{
				floppy->mon_w(0);
				m_fdc->set_rate(250000);
			}
		}
	}

	m_port_f0 = data;
}


MC6845_UPDATE_ROW( alphatro_state::crtc_update_row )
{
	rgb_t const *const pens = m_palette->palette()->entry_list_raw();
	bool palette = BIT(m_config->read(), 7);
	if (y==0) m_flashcnt++;
	u32 *p = &bitmap.pix(y);
	u16 gfx_mem = 0x1d92 + (y * 228); // address of graphics pixel at (0,y)

	for (u16 x = 0; x < x_count; x++)
	{
		bool inv = (x == cursor_x);
		u16 chr_mem = (ma + x) & 0x7ff;
		u8 chr = m_p_videoram[chr_mem];
		u8 attr = m_p_videoram[chr_mem | 0x800];
		u8 fg = (palette) ? 8 : BIT(attr, 0, 3); // amber or RGB
		u8 bg = (palette) ? 0 : BIT(attr, 3, 3);

		if (BIT(attr, 7)) // reverse video
		{
			inv ^= 1;
			chr &= 0x7f;
		}

		if (BIT(attr, 6) & BIT(m_flashcnt, 4)) // flashing
		{
			inv ^= 1;
		}

		/* get pattern of pixels for that character scanline */
		u8 gfx = m_p_chargen[(chr<<4) | ra];

		if (inv)
		{
			u8 t = bg;
			bg = fg;
			fg = t;
		}

		/* Display a scanline of a character (8 pixels) */
		for (int pixel = 0; pixel < 8; pixel++)
		{
			u8 chr_col = BIT(gfx, pixel ^ 7) ? fg : bg;
			u8 gfx_col = 0;

			/* BiCom graphics installed and enabled */
			if (m_is_bicom && m_bicom_en)
			{
				u8 data = m_bicom_ram[gfx_mem & 0xffff];
				/* odd pixels = high nibble, even pixels = low nibble (accounting for double width pixels in 80 column mode) */
				int gfx_nibble = !BIT(pixel >> BIT(m_port_10, 0), 0);
				gfx_col = BIT(data, gfx_nibble << 2, 3);
				if (palette && gfx_col != 0) gfx_col = 8; // amber

				/* increment graphics pixel every pixel in 40 column mode, or every other pixel in 80 column mode */
				if (!BIT(m_port_10, 0) || (pixel & 1))
				{
					if (gfx_nibble & 1) gfx_mem++;
				}
			}

			*p++ = pens[chr_col ^ gfx_col];
		}
	}
}

INPUT_CHANGED_MEMBER( alphatro_state::alphatro_break )
{
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, HOLD_LINE);
}

void alphatro_state::alphatro_map(address_map &map)
{
	map(0x0000, 0xffff).lrw8(NAME([this](offs_t offset) { return m_ram_ptr[offset]; }), NAME([this](offs_t offset, u8 data) { m_ram_ptr[offset] = data; }));
	map(0x0000, 0x5fff).m("lowbank", FUNC(address_map_bank_device::amap8));
	map(0xa000, 0xdfff).m("cartbank", FUNC(address_map_bank_device::amap8));
	map(0xf000, 0xffff).m("monbank", FUNC(address_map_bank_device::amap8));
}

void alphatro_state::rombank_map(address_map &map)
{
	map(0x0000, 0x5fff).rom().region("roms", 0x0000).lw8(NAME([this](offs_t offset, u8 data) { m_ram_ptr[offset] = data; }));
	map(0x6000, 0xbfff).lrw8(NAME([this](offs_t offset) { return m_ram_ptr[offset]; }), NAME([this](offs_t offset, u8 data) { m_ram_ptr[offset] = data; }));
}

void alphatro_state::cartbank_map(address_map &map)
{
	map(0x0000, 0x3fff).r(m_cart, FUNC(generic_slot_device::read_rom)).lw8(NAME([this](offs_t offset, u8 data) { m_ram_ptr[offset + 0xa000] = data; }));
	map(0x4000, 0x7fff).lrw8(NAME([this](offs_t offset) { return m_ram_ptr[offset + 0xa000]; }), NAME([this](offs_t offset, u8 data) { m_ram_ptr[offset + 0xa000] = data; }));
}

void alphatro_state::monbank_map(address_map &map)
{
	map(0x0000, 0x0fff).ram().share("videoram");
	map(0x1000, 0x1fff).rom().region("roms", 0x8000);
	map(0x2000, 0x2fff).rom().region("roms", 0x9000);
}

void alphatro_state::alphatro_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x03).rw(FUNC(alphatro_state::bicom_r), FUNC(alphatro_state::bicom_w));
	map(0x10, 0x10).rw(FUNC(alphatro_state::port10_r), FUNC(alphatro_state::port10_w));
	map(0x20, 0x20).portr("X0").w(FUNC(alphatro_state::port20_w));
	map(0x21, 0x21).portr("X1");
	map(0x22, 0x22).portr("X2");
	map(0x23, 0x23).portr("X3");
	map(0x24, 0x24).portr("X4");
	map(0x25, 0x25).portr("X5");
	map(0x26, 0x26).portr("X6");
	map(0x27, 0x27).portr("X7");
	map(0x28, 0x28).portr("X8");
	map(0x29, 0x29).portr("X9");
	map(0x2a, 0x2a).portr("XA");
	map(0x2b, 0x2b).portr("XB");
	map(0x30, 0x30).r(FUNC(alphatro_state::port30_r)).w("cent_data_out", FUNC(output_latch_device::write));
	// USART for cassette reading and writing
	map(0x40, 0x41).rw(m_usart, FUNC(i8251_device::read), FUNC(i8251_device::write));
	// CRTC - HD46505 / HD6845SP
	map(0x50, 0x50).w(m_crtc, FUNC(mc6845_device::address_w));
	map(0x51, 0x51).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	// 8257 DMAC
	map(0x60, 0x68).rw(m_dmac, FUNC(i8257_device::read), FUNC(i8257_device::write));
	// 8259 PIT
	map(0x70, 0x71).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0xf0, 0xf0).rw(FUNC(alphatro_state::portf0_r), FUNC(alphatro_state::portf0_w));
	map(0xf8, 0xf8).rw(m_fdc, FUNC(upd765a_device::fifo_r), FUNC(upd765a_device::fifo_w));
	map(0xf9, 0xf9).r(m_fdc, FUNC(upd765a_device::msr_r));
}

static INPUT_PORTS_START( alphatro )
	PORT_START("X0")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad 0") PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad 1") PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad 2") PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad 3") PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad 4") PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad 5") PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad 6") PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad 7") PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))

	PORT_START("X1")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad 8") PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad 9") PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad .") PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad - /") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad =") PORT_CODE(KEYCODE_ASTERISK) // keypad equals
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad + x") PORT_CODE(KEYCODE_PLUS_PAD)

	PORT_START("X2")
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')

	PORT_START("X3")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("X4")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')

	PORT_START("X5")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')

	PORT_START("X6")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('_')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR(0xa3)
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("X7")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("- =") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("^ ~") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') // bios 1 only
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("@ `") PORT_CODE(KEYCODE_DEL) PORT_CHAR('@') PORT_CHAR('`') // bios 1 only
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("[ {") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{') // bios 1 only
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("] }") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}') // bios 1 only
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(": *") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')

	PORT_START("X8")
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(32)
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\\ |") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|') // bios 1 only
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("; +") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')

	PORT_START("X9")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("(C)")      // reserved for future use with CP/M
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)

	PORT_START("XA")
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("INS DEL") PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Graph") PORT_CODE(KEYCODE_PGUP)
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Clear Home") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)

	PORT_START("XB")
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F6") PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_START("other")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(alphatro_state::alphatro_break), 0)

	PORT_START("CONFIG")
	PORT_CONFNAME(0x01, 0x00, "FDD Unit installed")
	PORT_DIPSETTING(    0x00, DEF_STR( No ))
	PORT_DIPSETTING(    0x01, DEF_STR( Yes ))

	PORT_DIPNAME(0x1c, 0x00, "Character Set") PORT_DIPLOCATION("SW-102:1,2,3")
	PORT_DIPSETTING(   0x00, "International")
	PORT_DIPSETTING(   0x04, "German")
	PORT_DIPSETTING(   0x08, "USA")
	PORT_DIPSETTING(   0x0c, "French")
	PORT_DIPSETTING(   0x10, "English")
	PORT_DIPSETTING(   0x14, "Italian")
	PORT_DIPSETTING(   0x18, "Spanish")
	PORT_DIPSETTING(   0x1c, "reserved")

	PORT_DIPNAME(0x20, 0x00, "Printer Interface (LPRINT)") PORT_DIPLOCATION("SW-102:4")
	PORT_DIPSETTING(   0x00, "Parallel")
	PORT_DIPSETTING(   0x20, "Serial")

	PORT_CONFNAME(0x80, 0x00, "Monitor")
	PORT_CONFSETTING(   0x00, "RGB")
	PORT_CONFSETTING(   0x80, "Amber")
INPUT_PORTS_END

static const gfx_layout charlayout =
{
	8,16,
	RGN_FRAC(1,1),
	1,
	{ RGN_FRAC(0,1) },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16
};

static GFXDECODE_START( gfx_alphatro )
	GFXDECODE_ENTRY( "chargen", 0, charlayout, 0, 4 )
GFXDECODE_END

void alphatro_state::machine_start()
{
	m_port_10 = 0x01;

	m_bicom_ram = make_unique_clear<uint8_t[]>(0x10000);

	save_item(NAME(m_port_10));
	save_item(NAME(m_port_20));
	save_item(NAME(m_cass_data));
	save_item(NAME(m_cassbit));
	save_item(NAME(m_cassold));
	save_item(NAME(m_bicom_addr));
	save_item(NAME(m_bicom_en));
	save_pointer(NAME(m_bicom_ram), 0x10000);
}

void alphatro_state::machine_reset()
{
	m_ram_ptr = m_ram->pointer();
	port10_w(0);
	m_port_20 = 0;
	update_banking();

	m_cass_data[0] = m_cass_data[1] = m_cass_data[2] = m_cass_data[3] = 0;
	m_cassbit = 1;
	m_cassold = 1;
	m_fdc_irq = 0;
	m_usart->write_rxd(1);
	m_usart->write_cts(0);
	m_beep->set_state(0);
	m_bicom_en = 0;
}

std::pair<std::error_condition, std::string> alphatro_state::load_cart(device_image_interface &image, generic_slot_device *slot)
{
	uint32_t const size = slot->common_get_size("rom");

	if ((size != 0x4000) && (size != 0x2000))
		return std::make_pair(image_error::INVALIDLENGTH, "Invalid cartridge size (must be 8K or 16K)");

	slot->rom_alloc(0x4000, GENERIC_ROM8_WIDTH, ENDIANNESS_BIG);

	if (size == 0x4000) // 16K ROMs come in at 0xA000
	{
		slot->common_load_rom(slot->get_rom_base(), size, "rom");
	}
	else    // load 8K ROMs at an offset of 8K so they end up at 0xC000
	{
		slot->common_load_rom(slot->get_rom_base()+0x2000, size, "rom");
	}

	return std::make_pair(std::error_condition(), std::string());
}

void alphatro_state::alphatro_palette(palette_device &palette) const
{
	// RGB colours
	palette.set_pen_color(0, 0x00, 0x00, 0x00);
	palette.set_pen_color(1, 0x00, 0x00, 0xff);
	palette.set_pen_color(2, 0xff, 0x00, 0x00);
	palette.set_pen_color(3, 0xff, 0x00, 0xff);
	palette.set_pen_color(4, 0x00, 0xff, 0x00);
	palette.set_pen_color(5, 0x00, 0xff, 0xff);
	palette.set_pen_color(6, 0xff, 0xff, 0x00);
	palette.set_pen_color(7, 0xff, 0xff, 0xff);
	// Amber
	palette.set_pen_color(8, 0xf7, 0xaa, 0x00);
}


void alphatro_state::kansas_w(int state)
{
	// incoming @19230Hz
	u8 twobit = m_cass_data[3] & 3;

	// relay off - exit, but wait for last bit to completely write
	if ((!BIT(m_port_10, 3)) && (twobit == 0))
	{
		m_cass->output(0);
		m_cass_data[3] = 0;     // reset waveforms
		return;
	}

	if (state)
	{
		if (twobit == 0)
			m_cassold = m_cassbit;

		if (m_cassold)
			m_cass->output(BIT(m_cass_data[3], 2) ? -1.0 : +1.0); // 2400Hz
		else
			m_cass->output(BIT(m_cass_data[3], 3) ? -1.0 : +1.0); // 1200Hz

		m_cass_data[3]++;
	}

	m_usart->write_txc(state);
}

void alphatro_state::kansas_r(int state)
{
	if (!BIT(m_port_10, 3))
	{
		m_usart->write_rxd(1);
		m_cass_data[0] = m_cass_data[1] = 0;
		return;
	}

	/* cassette - turn 1200/2400Hz to a bit */
	m_cass_data[1]++;
	uint8_t cass_ws = (m_cass->input() > +0.04) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		m_usart->write_rxd((m_cass_data[1] < 12) ? 1 : 0);
		m_cass_data[1] = 0;
	}
	m_usart->write_rxc(state);
}

void alphatro_state::hrq_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state);
	m_dmac->hlda_w(state);
}

static void alphatro_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

static DEVICE_INPUT_DEFAULTS_START(printer)
	DEVICE_INPUT_DEFAULTS("RS232_RXBAUD", 0xff, RS232_BAUD_4800)
	DEVICE_INPUT_DEFAULTS("RS232_DATABITS", 0xff, RS232_DATABITS_7)
	DEVICE_INPUT_DEFAULTS("RS232_PARITY", 0xff, RS232_PARITY_EVEN)
	DEVICE_INPUT_DEFAULTS("RS232_STOPBITS", 0xff, RS232_STOPBITS_2)
DEVICE_INPUT_DEFAULTS_END

void alphatro_state::alphatro(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &alphatro_state::alphatro_map);
	m_maincpu->set_addrmap(AS_IO, &alphatro_state::alphatro_io);
	m_maincpu->set_irq_acknowledge_callback(m_pic, FUNC(pic8259_device::inta_cb));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	if (m_is_ntsc)
		screen.set_raw(16_MHz_XTAL, 1016, 0, 640, 271, 0, 216);
	else if (m_is_bicom)
		screen.set_raw(14.318181_MHz_XTAL, 912, 0, 640, 312, 0, 264);
	else
		screen.set_raw(16_MHz_XTAL, 1016, 0, 640, 314, 0, 240);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	GFXDECODE(config, "gfxdecode", m_palette, gfx_alphatro);
	PALETTE(config, m_palette, FUNC(alphatro_state::alphatro_palette), 9); // 8 colours + amber

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, "beeper", 16_MHz_XTAL / 4 / 13 / 128).add_route(ALL_OUTPUTS, "mono", 1.00); // nominally 2.4 kHz

	/* Devices */
	UPD765A(config, m_fdc, 16_MHz_XTAL / 2, true, true); // clocked through SED-9420C
	m_fdc->intrq_wr_callback().set([this] (int state){ m_fdc_irq = !state; });
	m_fdc->drq_wr_callback().set(m_dmac, FUNC(i8257_device::dreq2_w));
	FLOPPY_CONNECTOR(config, "fdc:0", alphatro_floppies, "525dd", floppy_image_device::default_pc_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", alphatro_floppies, "525dd", floppy_image_device::default_pc_floppy_formats);
	SOFTWARE_LIST(config, "flop_list").set_original("alphatro_flop");

	I8257(config, m_dmac, 16_MHz_XTAL / 4);
	m_dmac->out_hrq_cb().set(FUNC(alphatro_state::hrq_w));
	m_dmac->in_memr_cb().set([this](offs_t offset) { return m_ram_ptr[offset]; });
	m_dmac->out_memw_cb().set([this](offs_t offset, u8 data) { m_ram_ptr[offset] = data; });
	m_dmac->in_ior_cb<2>().set(m_fdc, FUNC(upd765a_device::dma_r));
	m_dmac->out_iow_cb<2>().set(m_fdc, FUNC(upd765a_device::dma_w));
	m_dmac->out_tc_cb().set(m_fdc, FUNC(upd765a_device::tc_line_w));

	PIC8259(config, m_pic, 0);
	m_pic->in_sp_callback().set_constant(1);
	m_pic->out_int_callback().set_inputline(m_maincpu, 0);

	HD6845S(config, m_crtc, 16_MHz_XTAL / 8);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(alphatro_state::crtc_update_row));
	m_crtc->out_vsync_callback().set(m_pic, FUNC(pic8259_device::ir7_w));

	I8251(config, m_usart, 16_MHz_XTAL / 4);
	m_usart->txd_handler().set([this] (bool state) { m_cassbit = state; });
	m_usart->txd_handler().append("serial", FUNC(rs232_port_device::write_txd));
	m_usart->dtr_handler().set("serial", FUNC(rs232_port_device::write_dtr));
	m_usart->rts_handler().set("serial", FUNC(rs232_port_device::write_rts));
	m_usart->rxrdy_handler().set(m_pic, FUNC(pic8259_device::ir1_w));
	m_usart->txrdy_handler().set(m_pic, FUNC(pic8259_device::ir2_w));

	clock_device &cass_clock(CLOCK(config, "cass_clock", 16_MHz_XTAL / 4 / 13 / 16)); // 19.2 kHz
	cass_clock.signal_handler().set(FUNC(alphatro_state::kansas_w));
	cass_clock.signal_handler().append(FUNC(alphatro_state::kansas_r));

	clock_device &serial_clock(CLOCK(config, "serial_clock", 16_MHz_XTAL / 4 / 13 / 4)); // 76.8 kHz 4800 baud (can be set with jumpers)
	serial_clock.signal_handler().append(m_usart, FUNC(i8251_device::write_txc));
	serial_clock.signal_handler().append(m_usart, FUNC(i8251_device::write_rxc));

	rs232_port_device &serial(RS232_PORT(config, "serial", default_rs232_devices, nullptr));
	serial.rxd_handler().set(m_usart, FUNC(i8251_device::write_rxd));
	serial.cts_handler().set(m_usart, FUNC(i8251_device::write_cts));
	serial.dsr_handler().set(m_usart, FUNC(i8251_device::write_dsr));
	serial.set_option_device_input_defaults("printer", DEVICE_INPUT_DEFAULTS_NAME(printer));

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cass->set_interface("alphatro_cass");
	SOFTWARE_LIST(config, "cass_list").set_original("alphatro_cass");

	CENTRONICS(config, m_centronics, centronics_devices, nullptr);
	m_centronics->ack_handler().set([this](int state) { m_centronics_ack = !state; });
	m_centronics->busy_handler().set([this](int state) { m_centronics_busy = state; });
	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	RAM(config, "ram").set_default_size("64K");

	/* cartridge */
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "alphatro_cart", "bin").set_device_load(FUNC(alphatro_state::cart_load));
	SOFTWARE_LIST(config, "cart_list").set_original("alphatro_cart");

	/* 0000 banking */
	ADDRESS_MAP_BANK(config, "lowbank").set_map(&alphatro_state::rombank_map).set_options(ENDIANNESS_BIG, 8, 32, 0x6000);

	/* A000 banking */
	ADDRESS_MAP_BANK(config, "cartbank").set_map(&alphatro_state::cartbank_map).set_options(ENDIANNESS_BIG, 8, 32, 0x4000);

	/* F000 banking */
	ADDRESS_MAP_BANK(config, "monbank").set_map(&alphatro_state::monbank_map).set_options(ENDIANNESS_BIG, 8, 32, 0x1000);
}


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

  Game driver(s)

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

ROM_START( alphatro )
	ROM_REGION( 0xa000, "roms", ROMREGION_ERASE00)
	ROM_SYSTEM_BIOS( 0, "pcb-ig", "Alphatronic PC PCB-IG" ) // correctly displays German Umlauts
	ROMX_LOAD( "0_b4-6_ic1038.bin", 0x8000, 0x2000, CRC(e337db3b) SHA1(6010bade6a21975636383179903b58a4ca415e49), ROM_BIOS(0) )
	ROMX_LOAD( "1_b4-3_ic1058.bin", 0x0000, 0x2000, CRC(1509b15a) SHA1(225c36411de680eb8f4d6b58869460a58e60c0cf), ROM_BIOS(0) )
	ROMX_LOAD( "2_b4-3_ic1046.bin", 0x2000, 0x2000, CRC(998a865d) SHA1(294fe64e839ae6c4032d5db1f431c35e0d80d367), ROM_BIOS(0) )
	ROMX_LOAD( "3_b4-3_ic1037.bin", 0x4000, 0x2000, CRC(55cbafef) SHA1(e3376b92f80d5a698cdcb2afaa0f3ef4341dd624), ROM_BIOS(0) )

	ROM_SYSTEM_BIOS( 1, "pcb-ii", "Alphatronic PC PCB-II")
	ROMX_LOAD( "613256.ic-1058", 0x0000, 0x6000, CRC(ceea4cb3) SHA1(b332dea0a2d3bb2978b8422eb0723960388bb467), ROM_BIOS(1) )
	ROMX_LOAD( "2764.ic-1038",   0x8000, 0x2000, CRC(e337db3b) SHA1(6010bade6a21975636383179903b58a4ca415e49), ROM_BIOS(1) )

	ROM_SYSTEM_BIOS( 2, "term", "UKC Terminal") // University of Kent Terminal Emulator
	ROMX_LOAD( "ukc-0.ic-1038",    0x8000, 0x2000, CRC(e337db3b) SHA1(6010bade6a21975636383179903b58a4ca415e49), ROM_BIOS(2) )
	ROMX_LOAD( "ukc-term.ic-1058", 0x0000, 0x2000, CRC(a1bc0926) SHA1(492ddd9088c0211355e9999e66dce0cd47c73d77), ROM_BIOS(2) )

	ROM_REGION( 0x1000, "chargen", 0 )
	ROMX_LOAD( "4_b4-0_ic1067.bin", 0x0000, 0x1000, CRC(00796934) SHA1(8e70f77cfe3eb2ec2051f660518da5c9d409119a), ROM_BIOS(0) )
	ROMX_LOAD( "2732.ic-1067",      0x0000, 0x1000, CRC(61f38814) SHA1(35ba31c58a10d5bd1bdb202717792ca021dbe1a8), ROM_BIOS(1) )
	ROMX_LOAD( "charset.ic-1067",   0x0000, 0x1000, CRC(96a51551) SHA1(b4461e63d0b7ff405c9fabeb6e47f933ee18bd48), ROM_BIOS(2) )
ROM_END

#define rom_alphatron rom_alphatro

ROM_START( alphatrob )
	ROM_REGION( 0xa000, "roms", ROMREGION_ERASE00)
	ROM_SYSTEM_BIOS( 0, "bicom", "Alphatronic PC PCB-II w/BiCom Graphics") // correctly displays German Umlauts
	ROMX_LOAD( "613256_ic1058.bin",  0x0000, 0x8000, CRC(7fc5aa41) SHA1(f4727d1c5f1e5a2ce3a72e38aa37680d23b8e050), ROM_BIOS(0) )
	ROMX_LOAD( "tapcgv2_ic1038.bin", 0x8000, 0x2000, CRC(446b4235) SHA1(ef835ae46b3fdfe6a6f394971396a577528e7b5a), ROM_BIOS(0) )

	ROM_REGION( 0x1000, "chargen", 0 )
	ROMX_LOAD( "b40r_ic1067.bin",    0x0000, 0x1000, CRC(543e3ee8) SHA1(3e6c6f8c85d3a5d0735edfec52709c5670ff1646), ROM_BIOS(0) )
ROM_END

} // anonymous namespace


COMP( 1983, alphatro,  0,        0, alphatro, alphatro, alphatro_pal_state,   empty_init, "Triumph-Adler", "Alphatronic PC (PAL)",            MACHINE_SUPPORTS_SAVE )
COMP( 1983, alphatron, alphatro, 0, alphatro, alphatro, alphatro_ntsc_state,  empty_init, "Triumph-Adler", "Alphatronic PC (NTSC)",           MACHINE_SUPPORTS_SAVE )
COMP( 1984, alphatrob, alphatro, 0, alphatro, alphatro, alphatro_bicom_state, empty_init, "Triumph-Adler", "Alphatronic PC w/BiCom Graphics", MACHINE_SUPPORTS_SAVE )



altair.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************

MITS Altair 8800b Turnkey

2009-12-04 Initial driver by Miodrag Milanovic

Supposedly introduced October 1977.

Commands:
 All commands must be in uppercase. Address and data is
 specified in Octal format (not hex).

 Press space to input your command line (not return).

D - Memory Dump
J - Jump to address
M - Modify memory

Reference:
 http://www.computercloset.org/MITSAltair8800bt.htm

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

#include "emu.h"
#include "bus/rs232/rs232.h"
//#include "bus/s100/s100.h"
#include "cpu/i8085/i8085.h"
#include "machine/6850acia.h"
#include "machine/f4702.h"
#include "imagedev/snapquik.h"

namespace {

class altair_state : public driver_device
{
public:
	altair_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, "mainram")
	{ }

	void altair(machine_config &config);

private:
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);

	virtual void machine_reset() override ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_shared_ptr<uint8_t> m_ram;
};



void altair_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xfcff).ram().share("mainram");
	map(0xfd00, 0xfdff).rom().region("maincpu",0);
	map(0xff00, 0xffff).rom().region("maincpu",0x100);
}

void altair_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	// TODO: Remove mirror() and use SIO address S0-S7
	map(0x00, 0x01).mirror(0x10).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
}

/* Input ports */
static INPUT_PORTS_START( altair )
	PORT_START("BAUD")
	PORT_DIPNAME(0xf, 0x8, "Bit Rate") PORT_DIPLOCATION("S3-S0:4,3,2,1")
	PORT_DIPSETTING(0x0, "External Rate")
	PORT_DIPSETTING(0x2, "50")
	PORT_DIPSETTING(0x3, "75")
	PORT_DIPSETTING(0xf, "110")
	PORT_DIPSETTING(0x4, "134.5")
	PORT_DIPSETTING(0xe, "150")
	PORT_DIPSETTING(0x5, "200")
	PORT_DIPSETTING(0xd, "300")
	PORT_DIPSETTING(0x6, "600")
	PORT_DIPSETTING(0xb, "1200")
	PORT_DIPSETTING(0xa, "1800")
	PORT_DIPSETTING(0x7, "2400")
	PORT_DIPSETTING(0x9, "4800")
	PORT_DIPSETTING(0x8, "9600")
INPUT_PORTS_END


QUICKLOAD_LOAD_MEMBER(altair_state::quickload_cb)
{
	int const quick_length = image.length();
	if (quick_length >= 0xfd00)
		return std::make_pair(image_error::INVALIDLENGTH, std::string());
	int const read_ = image.fread(m_ram, quick_length);
	if (read_ != quick_length)
		return std::make_pair(image_error::UNSPECIFIED, std::string());

	return std::make_pair(std::error_condition(), std::string());
}

void altair_state::machine_reset()
{
	// Set startup address done by turn-key
	m_maincpu->set_state_int(i8080_cpu_device::I8085_PC, 0xFD00);
}

void altair_state::altair(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, 2_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &altair_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &altair_state::io_map);

	/* video hardware */
	acia6850_device &acia(ACIA6850(config, "acia", 0));
	acia.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	acia.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("acia", FUNC(acia6850_device::write_rxd));
	rs232.dcd_handler().set("acia", FUNC(acia6850_device::write_dcd));
	rs232.cts_handler().set("acia", FUNC(acia6850_device::write_cts));
	rs232.txc_handler().set("brg", FUNC(f4702_device::im_w)); // molex pin 7 to be connected to cable pin 15

	f4702_device &brg(F4702(config, "brg", 2.4576_MHz_XTAL));
	brg.s_callback().set_ioport("BAUD");
	brg.z_callback().set("acia", FUNC(acia6850_device::write_txc));
	brg.z_callback().append("acia", FUNC(acia6850_device::write_rxc));

	/* quickload */
	QUICKLOAD(config, "quickload", "bin").set_load_callback(FUNC(altair_state::quickload_cb));
}

/* ROM definition */
ROM_START( al8800bt )
	ROM_REGION( 0x0200, "maincpu", 0 )
	ROM_LOAD( "turnmon.bin",  0x0000, 0x0100, CRC(5c629294) SHA1(125c76216954b681721fff84a3aca05094b21a28))
	ROM_LOAD( "88dskrom.bin", 0x0100, 0x0100, CRC(7c5232f3) SHA1(24f940ad70ad2829e1bc800c6790b6e993e6ebf6))
ROM_END

} // Anonymous namespace

//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT    CLASS         INIT        COMPANY  FULLNAME         FLAGS
COMP( 1977, al8800bt, 0,      0,      altair,  altair,  altair_state, empty_init, "MITS",  "Altair 8800bt", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



alto1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************************

2017-11-05 Skeleton

Xerox Alto I

Similar in architecture to the Alto II and the Lilith, with 74181 ALUs and unique CPU, this was the first computer with a GUI.


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

#include "emu.h"


namespace {

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

	void alto1(machine_config &config);
private:
	//  required_device<cpu_device> m_maincpu;
};


static INPUT_PORTS_START( alto1 )
INPUT_PORTS_END

void alto1_state::alto1(machine_config &config)
{
}

ROM_START( alto1 )
	ROM_REGION( 0x4000, "roms", 0 )
	ROM_LOAD( "00_23.bin",    0x0000, 0x0100, CRC(56cc822f) SHA1(b337ff7abf5da92008c72cf9585faadf552ede32) )
	ROM_LOAD( "01_23.bin",    0x0100, 0x0100, CRC(37ea8320) SHA1(85a60869f49b73cbf80392566253aa86a3b5767c) )
	ROM_LOAD( "02_23.bin",    0x0200, 0x0100, CRC(c8e5966d) SHA1(d519d9b3e08f5b0032a1b3487a4ee389764895b5) )
	ROM_LOAD( "03_23.bin",    0x0300, 0x0100, CRC(1d894299) SHA1(a7cd3071caa22d5efbd9b3a2368e1d131b66481b) )
	ROM_LOAD( "04_23.bin",    0x0400, 0x0100, CRC(e0870437) SHA1(075129edaa4992bd61b530cdc0b8ce8b363e5144) )
	ROM_LOAD( "05_23.bin",    0x0500, 0x0100, CRC(734f2bfc) SHA1(31a05a01ba635f2c58ae88acf54efc86f78fc621) )
	ROM_LOAD( "06_23.bin",    0x0600, 0x0100, CRC(2ee8f9ee) SHA1(5a5c0d464a268954ad458b905c0170ca95a2c2ac) )
	ROM_LOAD( "07_23.bin",    0x0700, 0x0100, CRC(c4f64e7a) SHA1(837333b8b2d9c8ce24571914f5e38c2b3491e7ce) )
	ROM_LOAD( "10_23.bin",    0x0800, 0x0100, CRC(ddc71e20) SHA1(813d503627e4d65b44af2f6038db30a81ff2d99f) )
	ROM_LOAD( "11_23.bin",    0x0900, 0x0100, CRC(cc5cca76) SHA1(6beac0676d11411325bb61d0be2e842a3b4d8dd0) )
	ROM_LOAD( "12_23.bin",    0x0a00, 0x0100, CRC(8d4fa6c0) SHA1(9c1d5f661c34678e65e09a2b1961818a98098c47) )
	ROM_LOAD( "13_23.bin",    0x0b00, 0x0100, CRC(f73d5e3b) SHA1(e4b2a33e6b8a54133d298e3d8e4856f85c84764c) )
	ROM_LOAD( "14_23.bin",    0x0c00, 0x0100, CRC(8eee7d4a) SHA1(c7917f096b8e56a5393c1901ecb0d5248e5592e1) )
	ROM_LOAD( "15_23.bin",    0x0d00, 0x0100, CRC(b648751c) SHA1(bb192751521e2d9948501cc3a68d92a15cf5eae8) )
	ROM_LOAD( "16_23.bin",    0x0e00, 0x0100, CRC(011a309e) SHA1(2914519695fc65a09f797203881c42d2e5abc782) )
	ROM_LOAD( "17_23.bin",    0x0f00, 0x0100, CRC(6ccb4acb) SHA1(c307ee5bb5c72a37bab389eb7668f9367811ec2d) )
	ROM_LOAD( "20_23.bin",    0x1000, 0x0100, CRC(00846711) SHA1(330a4cfd1d15b31cf361db72fc44172887af7db5) )
	ROM_LOAD( "21_23.bin",    0x1100, 0x0100, CRC(f08f993b) SHA1(803a872c192a081423f2a65364afd8d0531dfa64) )
	ROM_LOAD( "22_23.bin",    0x1200, 0x0100, CRC(0a15cca5) SHA1(c1ee6f77dfe830cb470a6929ab74d1dce52f4129) )
	ROM_LOAD( "23_23.bin",    0x1300, 0x0100, CRC(197adfe9) SHA1(18b37eb95d7300399adb01f003cbea611a4b48ad) )
	ROM_LOAD( "24_23.bin",    0x1400, 0x0100, CRC(4fe970f0) SHA1(d9791fc60a11d00989cf3b167530593461edb659) )
	ROM_LOAD( "25_23.bin",    0x1500, 0x0100, CRC(b2638af5) SHA1(7b789731af5b390ff51de113149f1af47ed1f4a2) )
	ROM_LOAD( "26_23.bin",    0x1600, 0x0100, CRC(3920d097) SHA1(3a2717368f1a0afb021789165c5c032dce2369dc) )
	ROM_LOAD( "27_23.bin",    0x1700, 0x0100, CRC(cb425e98) SHA1(69573461633a702c284df7a2e931588c289c8529) )
	ROM_LOAD( "30_23.bin",    0x1800, 0x0100, CRC(95a4763a) SHA1(1a28b04d51b9125f92f40f914bf43957383a1edc) )
	ROM_LOAD( "31_23.bin",    0x1900, 0x0100, CRC(0cb672a1) SHA1(20a1cf8295fbe7884fbb1dd26c61f122a43e030f) )
	ROM_LOAD( "32_23.bin",    0x1a00, 0x0100, CRC(46f51eb0) SHA1(5b4a0f94a9c133da0f734ae44b09e0f8bab86058) )
	ROM_LOAD( "33_23.bin",    0x1b00, 0x0100, CRC(491db151) SHA1(1c1a816609abcafe00c80731148140ff68ecac58) )
	ROM_LOAD( "34_23.bin",    0x1c00, 0x0100, CRC(ce6f062f) SHA1(fe8751aa3884c8d2cefc28fb116336912264b962) )
	ROM_LOAD( "35_23.bin",    0x1d00, 0x0100, CRC(d7f5eb01) SHA1(300f42e251eaa2a4ad37e17376c46bd272018d92) )
	ROM_LOAD( "36_23.bin",    0x1e00, 0x0100, CRC(d4b335f5) SHA1(ed4ed14ab8e8f50cc07ef8d394490ce168bd47be) )
	ROM_LOAD( "37_23.bin",    0x1f00, 0x0100, CRC(320eaae9) SHA1(44bd5bd1e4de054c6feb52dbc0dc4e848bab085e) )
	ROM_LOAD( "c0_23.bin",    0x2000, 0x0100, CRC(ef5634be) SHA1(912dc906069dfbed1c1a846bbe50ef1ed6dfe081) )
	ROM_LOAD( "c1_23.bin",    0x2100, 0x0100, CRC(b698f779) SHA1(ef33c9aefbf3e650bea33d42179a9c9743dfbe4d) )
	ROM_LOAD( "c2_23.bin",    0x2200, 0x0100, CRC(4dfc72e8) SHA1(56ac873d13cc1f351537145a586609a34f79d6af) )
	ROM_LOAD( "c3_23.bin",    0x2300, 0x0100, CRC(cd0e855b) SHA1(75d1bad4c5bded95703be358834a969aa2a56942) )
	ROM_LOAD( "ctr.bin",      0x2400, 0x0020, CRC(fc51b1d1) SHA1(e36c2a12a5da377394264899b5ae504e2ffda46e) )
	ROM_LOAD( "dp14.bin",     0x2500, 0x0100, CRC(8f54203b) SHA1(158d6c8b54d5cc0de7a902001a9ee207ed3358a0) )
	ROM_LOAD( "pram.bin",     0x2600, 0x0100, CRC(8087140e) SHA1(e17d9756150d41d6ff614afa86808a9c77516749) )
ROM_END

} // anonymous namespace


COMP( 1973, alto1, 0, 0, alto1, alto1, alto1_state, empty_init, "Xerox", "Alto I", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



alto2.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Juergen Buchmueller
/***************************************************************************
 *   Xerox AltoII driver for MESS
 *
 ***************************************************************************/

#include "emu.h"
#include "cpu/alto2/alto2cpu.h"
#include "machine/diablo_hd.h"
#include "sound/spkrdev.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

namespace {

class alto2_state : public driver_device
{
public:
	alto2_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_speaker(*this, "speaker"),
		m_io_row(*this, "ROW%u", 0U),
		m_io_config(*this, "CONFIG")
	{ }

	void alto2(machine_config &config);

protected:
	virtual void driver_start() override;

	TIMER_CALLBACK_MEMBER(handle_vblank);

private:
	u16 kb_r(offs_t offset);
	void utilout_w(u16 data);

	required_device<alto2_cpu_device> m_maincpu;
	required_device<speaker_sound_device> m_speaker;
	required_ioport_array<8> m_io_row;
	optional_ioport m_io_config;
	emu_timer* m_vblank_timer;
};

/* Input Ports */

#define PORT_KEY(_bit,_code,_char1,_char2,_name) \
	PORT_BIT(_bit, IP_ACTIVE_LOW, IPT_KEYBOARD) \
	PORT_CODE(_code) PORT_NAME(_name) \
	PORT_CHAR(_char1) PORT_CHAR(_char2)
#define SPACING "     "

static INPUT_PORTS_START( alto2 )
	PORT_START("ROW0")
	PORT_KEY(A2_KEY_5,          KEYCODE_5,          '5',            '%',          "5" SPACING "%")  //!< normal: 5    shifted: %
	PORT_KEY(A2_KEY_4,          KEYCODE_4,          '4',            '$',          "4" SPACING "$")  //!< normal: 4    shifted: $
	PORT_KEY(A2_KEY_6,          KEYCODE_6,          '6',            '~',          "6" SPACING "~")  //!< normal: 6    shifted: ~
	PORT_KEY(A2_KEY_E,          KEYCODE_E,          'e',            'E',          "e" SPACING "E")  //!< normal: e    shifted: E
	PORT_KEY(A2_KEY_7,          KEYCODE_7,          '7',            '&',          "7" SPACING "&")  //!< normal: 7    shifted: &
	PORT_KEY(A2_KEY_D,          KEYCODE_D,          'd',            'D',          "d" SPACING "D")  //!< normal: d    shifted: D
	PORT_KEY(A2_KEY_U,          KEYCODE_U,          'u',            'U',          "u" SPACING "U")  //!< normal: u    shifted: U
	PORT_KEY(A2_KEY_V,          KEYCODE_V,          'v',            'V',          "v" SPACING "V")  //!< normal: v    shifted: V
	PORT_KEY(A2_KEY_0,          KEYCODE_0,          '0',            ')',          "0" SPACING ")")  //!< normal: 0    shifted: )
	PORT_KEY(A2_KEY_K,          KEYCODE_K,          'k',            'K',          "k" SPACING "K")  //!< normal: k    shifted: K
	PORT_KEY(A2_KEY_MINUS,      KEYCODE_MINUS,      '-',            '_',          "-" SPACING "_")  //!< normal: -    shifted: _
	PORT_KEY(A2_KEY_P,          KEYCODE_P,          'p',            'P',          "p" SPACING "P")  //!< normal: p    shifted: P
	PORT_KEY(A2_KEY_SLASH,      KEYCODE_SLASH,      '/',            '?',          "/" SPACING "?")  //!< normal: /    shifted: ?
	PORT_KEY(A2_KEY_BACKSLASH,  KEYCODE_BACKSLASH,  '\\',           '|',         "\\" SPACING "|")  //!< normal: \    shifted: |
	PORT_KEY(A2_KEY_LF,         KEYCODE_DOWN,       10,             10,           "LF"           )  //!< normal: LF   shifted: ?
	PORT_KEY(A2_KEY_BS,         KEYCODE_BACKSPACE,  8,              8,            "BS"           )  //!< normal: BS   shifted: ?

	PORT_START("ROW1")
	PORT_KEY(A2_KEY_3,          KEYCODE_3,          '3',            '#',          "3" SPACING "#")  //!< normal: 3    shifted: #
	PORT_KEY(A2_KEY_2,          KEYCODE_2,          '2',            '@',          "2" SPACING "@")  //!< normal: 2    shifted: @
	PORT_KEY(A2_KEY_W,          KEYCODE_W,          'w',            'W',          "w" SPACING "W")  //!< normal: w    shifted: W
	PORT_KEY(A2_KEY_Q,          KEYCODE_Q,          'q',            'Q',          "q" SPACING "Q")  //!< normal: q    shifted: Q
	PORT_KEY(A2_KEY_S,          KEYCODE_S,          's',            'S',          "s" SPACING "S")  //!< normal: s    shifted: S
	PORT_KEY(A2_KEY_A,          KEYCODE_A,          'a',            'A',          "a" SPACING "A")  //!< normal: a    shifted: A
	PORT_KEY(A2_KEY_9,          KEYCODE_9,          '9',            '(',          "9" SPACING "(")  //!< normal: 9    shifted: (
	PORT_KEY(A2_KEY_I,          KEYCODE_I,          'i',            'I',          "i" SPACING "I")  //!< normal: i    shifted: I
	PORT_KEY(A2_KEY_X,          KEYCODE_X,          'x',            'X',          "x" SPACING "X")  //!< normal: x    shifted: X
	PORT_KEY(A2_KEY_O,          KEYCODE_O,          'o',            'O',          "o" SPACING "O")  //!< normal: o    shifted: O
	PORT_KEY(A2_KEY_L,          KEYCODE_L,          'l',            'L',          "l" SPACING "L")  //!< normal: l    shifted: L
	PORT_KEY(A2_KEY_COMMA,      KEYCODE_COMMA,      ',',            '<',          "," SPACING "<")  //!< normal: ,    shifted: <
	PORT_KEY(A2_KEY_QUOTE,      KEYCODE_QUOTE,      39,             34,           "'" SPACING "\"") //!< normal: '    shifted: "
	PORT_KEY(A2_KEY_RBRACKET,   KEYCODE_CLOSEBRACE, ']',            '}',          "]" SPACING "}")  //!< normal: ]    shifted: }
	PORT_KEY(A2_KEY_BLANK_MID,  KEYCODE_END,        0,              0,            "MID"          )  //!< middle blank key
	PORT_KEY(A2_KEY_BLANK_TOP,  KEYCODE_PGUP,       0,              0,            "TOP"          )  //!< top blank key

	PORT_START("ROW2")
	PORT_KEY(A2_KEY_1,          KEYCODE_1,          '1',            '!',          "1" SPACING "!")  //!< normal: 1    shifted: !
	PORT_KEY(A2_KEY_ESCAPE,     KEYCODE_ESC,        27,             0,            "ESC"          )  //!< normal: ESC  shifted: ?
	PORT_KEY(A2_KEY_TAB,        KEYCODE_TAB,        9,              0,            "TAB"          )  //!< normal: TAB  shifted: ?
	PORT_KEY(A2_KEY_F,          KEYCODE_F,          'f',            'F',          "f" SPACING "F")  //!< normal: f    shifted: F
	PORT_KEY(A2_KEY_CTRL,       KEYCODE_LCONTROL,   0,              0,            "CTRL"         )  //!< CTRL
	PORT_KEY(A2_KEY_C,          KEYCODE_C,          'c',            'C',          "c" SPACING "C")  //!< normal: c    shifted: C
	PORT_KEY(A2_KEY_J,          KEYCODE_J,          'j',            'J',          "j" SPACING "J")  //!< normal: j    shifted: J
	PORT_KEY(A2_KEY_B,          KEYCODE_B,          'b',            'B',          "b" SPACING "B")  //!< normal: b    shifted: B
	PORT_KEY(A2_KEY_Z,          KEYCODE_Z,          'z',            'Z',          "z" SPACING "Z")  //!< normal: z    shifted: Z
	PORT_KEY(A2_KEY_LSHIFT,     KEYCODE_LSHIFT,     UCHAR_SHIFT_1,  0,            "LSHIFT"       )  //!< LSHIFT
	PORT_KEY(A2_KEY_PERIOD,     KEYCODE_STOP,       '.',            '>',          "." SPACING ">")  //!< normal: .    shifted: >
	PORT_KEY(A2_KEY_SEMICOLON,  KEYCODE_COLON,      ';',            ':',          ";" SPACING ":")  //!< normal: ;    shifted: :
	PORT_KEY(A2_KEY_RETURN,     KEYCODE_ENTER,      13,             0,            "RETURN"       )  //!< RETURN
	PORT_KEY(A2_KEY_LEFTARROW,  KEYCODE_LEFT,       0,              0,            "???" SPACING "???")  //!< normal: left arrow   shifted: up arrow (caret)
	PORT_KEY(A2_KEY_DEL,        KEYCODE_DEL,        UCHAR_MAMEKEY(DEL), 0,        "DEL"          )  //!< normal: DEL  shifted: ?
	PORT_KEY(A2_KEY_MSW_2_17,   KEYCODE_MENU,       0,              0,            "MSW2/17"      )  //!< unused on Microswitch KDB

	PORT_START("ROW3")
	PORT_KEY(A2_KEY_R,          KEYCODE_R,          'r',            'R',          "r" SPACING "R")  //!< normal: r    shifted: R
	PORT_KEY(A2_KEY_T,          KEYCODE_T,          't',            'T',          "t" SPACING "T")  //!< normal: t    shifted: T
	PORT_KEY(A2_KEY_G,          KEYCODE_G,          'g',            'G',          "g" SPACING "G")  //!< normal: g    shifted: G
	PORT_KEY(A2_KEY_Y,          KEYCODE_Y,          'y',            'Y',          "y" SPACING "Y")  //!< normal: y    shifted: Y
	PORT_KEY(A2_KEY_H,          KEYCODE_H,          'h',            'H',          "h" SPACING "H")  //!< normal: h    shifted: H
	PORT_KEY(A2_KEY_8,          KEYCODE_8,          '8',            '*',          "8" SPACING "*")  //!< normal: 8    shifted: *
	PORT_KEY(A2_KEY_N,          KEYCODE_N,          'n',            'N',          "n" SPACING "N")  //!< normal: n    shifted: N
	PORT_KEY(A2_KEY_M,          KEYCODE_M,          'm',            'M',          "m" SPACING "M")  //!< normal: m    shifted: M
	PORT_KEY(A2_KEY_LOCK,       KEYCODE_SCRLOCK,    0,              0,            "LOCK"         )  //!< LOCK
	PORT_KEY(A2_KEY_SPACE,      KEYCODE_SPACE,      32,             0,            "SPACE"        )  //!< SPACE
	PORT_KEY(A2_KEY_LBRACKET,   KEYCODE_OPENBRACE,  '[',            '{',          "[" SPACING "{")  //!< normal: [    shifted: {
	PORT_KEY(A2_KEY_EQUALS,     KEYCODE_EQUALS,     '=',            '+',          "=" SPACING "+")  //!< normal: =    shifted: +
	PORT_KEY(A2_KEY_RSHIFT,     KEYCODE_RSHIFT,     UCHAR_SHIFT_2,  0,            "RSHIFT"       )  //!< RSHIFT
	PORT_KEY(A2_KEY_BLANK_BOT,  KEYCODE_PGDN,       0,              0,            "BOT"          )  //!< bottom blank key
	PORT_KEY(A2_KEY_MSW_3_16,   KEYCODE_HOME,       0,              0,            "MSW3/16"      )  //!< unused on Microswitch KDB
	PORT_KEY(A2_KEY_MSW_3_17,   KEYCODE_INSERT,     0,              0,            "MSW3/17"      )  //!< unused on Microswitch KDB

	PORT_START("ROW4")
	PORT_KEY(A2_KEY_FR2,        KEYCODE_F6,         0,              0,            "FR2"          )  //!< ADL right function key 2
	PORT_KEY(A2_KEY_FL2,        KEYCODE_F2,         0,              0,            "FL2"          )  //!< ADL left function key 2

	PORT_START("ROW5")
	PORT_KEY(A2_KEY_FR4,        KEYCODE_F8,         0,              0,            "FR4"          )  //!< ADL right function key 4
	PORT_KEY(A2_KEY_BW,         KEYCODE_F10,        0,              0,            "BW"           )  //!< ADL BW (?)

	PORT_START("ROW6")
	PORT_KEY(A2_KEY_FR3,        KEYCODE_F7,         0,              0,            "FR3"          )  //!< ADL right function key 3
	PORT_KEY(A2_KEY_FL1,        KEYCODE_F1,         0,              0,            "FL1"          )  //!< ADL left function key 1
	PORT_KEY(A2_KEY_FL3,        KEYCODE_F3,         0,              0,            "FL3"          )  //!< ADL left function key 3

	PORT_START("ROW7")
	PORT_KEY(A2_KEY_FR1,        KEYCODE_F5,         0,              0,            "FR1"          )  //!< ADL right function key 1
	PORT_KEY(A2_KEY_FL4,        KEYCODE_F4,         0,              0,            "FL4"          )  //!< ADL left function key 4
	PORT_KEY(A2_KEY_FR5,        KEYCODE_F9,         0,              0,            "FR5"          )  //!< ADL right function key 5

	PORT_START("mouseb0")   // Mouse button 0
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("Mouse RED (left)")      PORT_PLAYER(1) PORT_CODE(MOUSECODE_BUTTON1) PORT_CHANGED_MEMBER(":maincpu", FUNC(alto2_cpu_device::mouse_button_0), 0)
	PORT_START("mouseb1")   // Mouse button 1
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("Mouse BLUE (right)")    PORT_PLAYER(1) PORT_CODE(MOUSECODE_BUTTON2) PORT_CHANGED_MEMBER(":maincpu", FUNC(alto2_cpu_device::mouse_button_1), 0)
	PORT_START("mouseb2")   // Mouse button 2
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON3) PORT_NAME("Mouse YELLOW (middle)") PORT_PLAYER(1) PORT_CODE(MOUSECODE_BUTTON3) PORT_CHANGED_MEMBER(":maincpu", FUNC(alto2_cpu_device::mouse_button_2), 0)

	PORT_START("mousex")    // Mouse - X AXIS
	PORT_BIT( 0xffff, 0, IPT_MOUSE_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_CHANGED_MEMBER(":maincpu", FUNC(alto2_cpu_device::mouse_motion_x), 0)

	PORT_START("mousey")    // Mouse - Y AXIS
	PORT_BIT( 0xffff, 0, IPT_MOUSE_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_CHANGED_MEMBER(":maincpu", FUNC(alto2_cpu_device::mouse_motion_y), 0)

	PORT_START("CONFIG")    /* Memory switch on AIM board */
	PORT_CONFNAME( 0x01, 0x01, "Memory switch")
	PORT_CONFSETTING( 0x00, "on")
	PORT_CONFSETTING( 0x01, "off")
	PORT_CONFNAME( 0x06, 0x02, "CROM/CRAM configuration")
	PORT_CONFSETTING( 0x00, "Invalid (no CROM/CRAM)")
	PORT_CONFSETTING( 0x02, "1K CROM, 1K CRAM")
	PORT_CONFSETTING( 0x04, "2K CROM, 1K CRAM")
	PORT_CONFSETTING( 0x06, "1K CROM, 3K CRAM")
	PORT_CONFNAME( 0x70, 0x00, "Ethernet breath-of-life")
	PORT_CONFSETTING( 0x00, "off")
	PORT_CONFSETTING( 0x10, "5 seconds")
	PORT_CONFSETTING( 0x20, "10 seconds")
	PORT_CONFSETTING( 0x30, "15 seconds")
	PORT_CONFSETTING( 0x40, "30 seconds")
	PORT_CONFSETTING( 0x50, "60 seconds")
	PORT_CONFSETTING( 0x60, "90 seconds")
	PORT_CONFSETTING( 0x70, "120 seconds")

	PORT_START("ETHERID")
	PORT_DIPNAME( 0377, 0042, "Ethernet ID")
	PORT_DIPSETTING( 0000, "No ether") PORT_DIPSETTING( 0001, "ID 001")   PORT_DIPSETTING( 0002, "ID 002")   PORT_DIPSETTING( 0003, "ID 003")
	PORT_DIPSETTING( 0004, "ID 004")   PORT_DIPSETTING( 0005, "ID 005")   PORT_DIPSETTING( 0006, "ID 006")   PORT_DIPSETTING( 0007, "ID 007")
	PORT_DIPSETTING( 0010, "ID 010")   PORT_DIPSETTING( 0011, "ID 011")   PORT_DIPSETTING( 0012, "ID 012")   PORT_DIPSETTING( 0013, "ID 013")
	PORT_DIPSETTING( 0014, "ID 014")   PORT_DIPSETTING( 0015, "ID 015")   PORT_DIPSETTING( 0016, "ID 016")   PORT_DIPSETTING( 0017, "ID 017")
	PORT_DIPSETTING( 0020, "ID 020")   PORT_DIPSETTING( 0021, "ID 021")   PORT_DIPSETTING( 0022, "ID 022")   PORT_DIPSETTING( 0023, "ID 023")
	PORT_DIPSETTING( 0024, "ID 024")   PORT_DIPSETTING( 0025, "ID 025")   PORT_DIPSETTING( 0026, "ID 026")   PORT_DIPSETTING( 0027, "ID 027")
	PORT_DIPSETTING( 0030, "ID 030")   PORT_DIPSETTING( 0031, "ID 031")   PORT_DIPSETTING( 0032, "ID 032")   PORT_DIPSETTING( 0033, "ID 033")
	PORT_DIPSETTING( 0034, "ID 034")   PORT_DIPSETTING( 0035, "ID 035")   PORT_DIPSETTING( 0036, "ID 036")   PORT_DIPSETTING( 0037, "ID 037")
	PORT_DIPSETTING( 0040, "ID 040")   PORT_DIPSETTING( 0041, "ID 041")   PORT_DIPSETTING( 0042, "ID 042")   PORT_DIPSETTING( 0043, "ID 043")
	PORT_DIPSETTING( 0044, "ID 044")   PORT_DIPSETTING( 0045, "ID 045")   PORT_DIPSETTING( 0046, "ID 046")   PORT_DIPSETTING( 0047, "ID 047")
	PORT_DIPSETTING( 0050, "ID 050")   PORT_DIPSETTING( 0051, "ID 051")   PORT_DIPSETTING( 0052, "ID 052")   PORT_DIPSETTING( 0053, "ID 053")
	PORT_DIPSETTING( 0054, "ID 054")   PORT_DIPSETTING( 0055, "ID 055")   PORT_DIPSETTING( 0056, "ID 056")   PORT_DIPSETTING( 0057, "ID 057")
	PORT_DIPSETTING( 0060, "ID 060")   PORT_DIPSETTING( 0061, "ID 061")   PORT_DIPSETTING( 0062, "ID 062")   PORT_DIPSETTING( 0063, "ID 063")
	PORT_DIPSETTING( 0064, "ID 064")   PORT_DIPSETTING( 0065, "ID 065")   PORT_DIPSETTING( 0066, "ID 066")   PORT_DIPSETTING( 0067, "ID 067")
	PORT_DIPSETTING( 0070, "ID 070")   PORT_DIPSETTING( 0071, "ID 071")   PORT_DIPSETTING( 0072, "ID 072")   PORT_DIPSETTING( 0073, "ID 073")
	PORT_DIPSETTING( 0074, "ID 074")   PORT_DIPSETTING( 0075, "ID 075")   PORT_DIPSETTING( 0076, "ID 076")   PORT_DIPSETTING( 0077, "ID 077")
	PORT_DIPSETTING( 0100, "ID 100")   PORT_DIPSETTING( 0101, "ID 101")   PORT_DIPSETTING( 0102, "ID 102")   PORT_DIPSETTING( 0103, "ID 103")
	PORT_DIPSETTING( 0104, "ID 104")   PORT_DIPSETTING( 0105, "ID 105")   PORT_DIPSETTING( 0106, "ID 106")   PORT_DIPSETTING( 0107, "ID 107")
	PORT_DIPSETTING( 0110, "ID 110")   PORT_DIPSETTING( 0111, "ID 111")   PORT_DIPSETTING( 0112, "ID 112")   PORT_DIPSETTING( 0113, "ID 113")
	PORT_DIPSETTING( 0114, "ID 114")   PORT_DIPSETTING( 0115, "ID 115")   PORT_DIPSETTING( 0116, "ID 116")   PORT_DIPSETTING( 0117, "ID 117")
	PORT_DIPSETTING( 0120, "ID 120")   PORT_DIPSETTING( 0121, "ID 121")   PORT_DIPSETTING( 0122, "ID 122")   PORT_DIPSETTING( 0123, "ID 123")
	PORT_DIPSETTING( 0124, "ID 124")   PORT_DIPSETTING( 0125, "ID 125")   PORT_DIPSETTING( 0126, "ID 126")   PORT_DIPSETTING( 0127, "ID 127")
	PORT_DIPSETTING( 0130, "ID 130")   PORT_DIPSETTING( 0131, "ID 131")   PORT_DIPSETTING( 0132, "ID 132")   PORT_DIPSETTING( 0133, "ID 133")
	PORT_DIPSETTING( 0134, "ID 134")   PORT_DIPSETTING( 0135, "ID 135")   PORT_DIPSETTING( 0136, "ID 136")   PORT_DIPSETTING( 0137, "ID 137")
	PORT_DIPSETTING( 0140, "ID 140")   PORT_DIPSETTING( 0141, "ID 141")   PORT_DIPSETTING( 0142, "ID 142")   PORT_DIPSETTING( 0143, "ID 143")
	PORT_DIPSETTING( 0144, "ID 144")   PORT_DIPSETTING( 0145, "ID 145")   PORT_DIPSETTING( 0146, "ID 146")   PORT_DIPSETTING( 0147, "ID 147")
	PORT_DIPSETTING( 0150, "ID 150")   PORT_DIPSETTING( 0151, "ID 151")   PORT_DIPSETTING( 0152, "ID 152")   PORT_DIPSETTING( 0153, "ID 153")
	PORT_DIPSETTING( 0154, "ID 154")   PORT_DIPSETTING( 0155, "ID 155")   PORT_DIPSETTING( 0156, "ID 156")   PORT_DIPSETTING( 0157, "ID 157")
	PORT_DIPSETTING( 0160, "ID 160")   PORT_DIPSETTING( 0161, "ID 161")   PORT_DIPSETTING( 0162, "ID 162")   PORT_DIPSETTING( 0163, "ID 163")
	PORT_DIPSETTING( 0164, "ID 164")   PORT_DIPSETTING( 0165, "ID 165")   PORT_DIPSETTING( 0166, "ID 166")   PORT_DIPSETTING( 0167, "ID 167")
	PORT_DIPSETTING( 0170, "ID 170")   PORT_DIPSETTING( 0171, "ID 171")   PORT_DIPSETTING( 0172, "ID 172")   PORT_DIPSETTING( 0173, "ID 173")
	PORT_DIPSETTING( 0174, "ID 174")   PORT_DIPSETTING( 0175, "ID 175")   PORT_DIPSETTING( 0176, "ID 176")   PORT_DIPSETTING( 0177, "ID 177")
	PORT_DIPSETTING( 0200, "ID 200")   PORT_DIPSETTING( 0201, "ID 201")   PORT_DIPSETTING( 0202, "ID 202")   PORT_DIPSETTING( 0203, "ID 203")
	PORT_DIPSETTING( 0204, "ID 204")   PORT_DIPSETTING( 0205, "ID 205")   PORT_DIPSETTING( 0206, "ID 206")   PORT_DIPSETTING( 0207, "ID 207")
	PORT_DIPSETTING( 0210, "ID 210")   PORT_DIPSETTING( 0211, "ID 211")   PORT_DIPSETTING( 0212, "ID 212")   PORT_DIPSETTING( 0213, "ID 213")
	PORT_DIPSETTING( 0214, "ID 214")   PORT_DIPSETTING( 0215, "ID 215")   PORT_DIPSETTING( 0216, "ID 216")   PORT_DIPSETTING( 0217, "ID 217")
	PORT_DIPSETTING( 0220, "ID 220")   PORT_DIPSETTING( 0221, "ID 221")   PORT_DIPSETTING( 0222, "ID 222")   PORT_DIPSETTING( 0223, "ID 223")
	PORT_DIPSETTING( 0224, "ID 224")   PORT_DIPSETTING( 0225, "ID 225")   PORT_DIPSETTING( 0226, "ID 226")   PORT_DIPSETTING( 0227, "ID 227")
	PORT_DIPSETTING( 0230, "ID 230")   PORT_DIPSETTING( 0231, "ID 231")   PORT_DIPSETTING( 0232, "ID 232")   PORT_DIPSETTING( 0233, "ID 233")
	PORT_DIPSETTING( 0234, "ID 234")   PORT_DIPSETTING( 0235, "ID 235")   PORT_DIPSETTING( 0236, "ID 236")   PORT_DIPSETTING( 0237, "ID 237")
	PORT_DIPSETTING( 0240, "ID 240")   PORT_DIPSETTING( 0241, "ID 241")   PORT_DIPSETTING( 0242, "ID 242")   PORT_DIPSETTING( 0243, "ID 243")
	PORT_DIPSETTING( 0244, "ID 244")   PORT_DIPSETTING( 0245, "ID 245")   PORT_DIPSETTING( 0246, "ID 246")   PORT_DIPSETTING( 0247, "ID 247")
	PORT_DIPSETTING( 0250, "ID 250")   PORT_DIPSETTING( 0251, "ID 251")   PORT_DIPSETTING( 0252, "ID 252")   PORT_DIPSETTING( 0253, "ID 253")
	PORT_DIPSETTING( 0254, "ID 254")   PORT_DIPSETTING( 0255, "ID 255")   PORT_DIPSETTING( 0256, "ID 256")   PORT_DIPSETTING( 0257, "ID 257")
	PORT_DIPSETTING( 0260, "ID 260")   PORT_DIPSETTING( 0261, "ID 261")   PORT_DIPSETTING( 0262, "ID 262")   PORT_DIPSETTING( 0263, "ID 263")
	PORT_DIPSETTING( 0264, "ID 264")   PORT_DIPSETTING( 0265, "ID 265")   PORT_DIPSETTING( 0266, "ID 266")   PORT_DIPSETTING( 0267, "ID 267")
	PORT_DIPSETTING( 0270, "ID 270")   PORT_DIPSETTING( 0271, "ID 271")   PORT_DIPSETTING( 0272, "ID 272")   PORT_DIPSETTING( 0273, "ID 273")
	PORT_DIPSETTING( 0274, "ID 274")   PORT_DIPSETTING( 0275, "ID 275")   PORT_DIPSETTING( 0276, "ID 276")   PORT_DIPSETTING( 0277, "ID 277")
	PORT_DIPSETTING( 0300, "ID 300")   PORT_DIPSETTING( 0301, "ID 301")   PORT_DIPSETTING( 0302, "ID 302")   PORT_DIPSETTING( 0303, "ID 303")
	PORT_DIPSETTING( 0304, "ID 304")   PORT_DIPSETTING( 0305, "ID 305")   PORT_DIPSETTING( 0306, "ID 306")   PORT_DIPSETTING( 0307, "ID 307")
	PORT_DIPSETTING( 0310, "ID 310")   PORT_DIPSETTING( 0311, "ID 311")   PORT_DIPSETTING( 0312, "ID 312")   PORT_DIPSETTING( 0313, "ID 313")
	PORT_DIPSETTING( 0314, "ID 314")   PORT_DIPSETTING( 0315, "ID 315")   PORT_DIPSETTING( 0316, "ID 316")   PORT_DIPSETTING( 0317, "ID 317")
	PORT_DIPSETTING( 0320, "ID 320")   PORT_DIPSETTING( 0321, "ID 321")   PORT_DIPSETTING( 0322, "ID 322")   PORT_DIPSETTING( 0323, "ID 323")
	PORT_DIPSETTING( 0324, "ID 324")   PORT_DIPSETTING( 0325, "ID 325")   PORT_DIPSETTING( 0326, "ID 326")   PORT_DIPSETTING( 0327, "ID 327")
	PORT_DIPSETTING( 0330, "ID 330")   PORT_DIPSETTING( 0331, "ID 331")   PORT_DIPSETTING( 0332, "ID 332")   PORT_DIPSETTING( 0333, "ID 333")
	PORT_DIPSETTING( 0334, "ID 334")   PORT_DIPSETTING( 0335, "ID 335")   PORT_DIPSETTING( 0336, "ID 336")   PORT_DIPSETTING( 0337, "ID 337")
	PORT_DIPSETTING( 0340, "ID 340")   PORT_DIPSETTING( 0341, "ID 341")   PORT_DIPSETTING( 0342, "ID 342")   PORT_DIPSETTING( 0343, "ID 343")
	PORT_DIPSETTING( 0344, "ID 344")   PORT_DIPSETTING( 0345, "ID 345")   PORT_DIPSETTING( 0346, "ID 346")   PORT_DIPSETTING( 0347, "ID 347")
	PORT_DIPSETTING( 0350, "ID 350")   PORT_DIPSETTING( 0351, "ID 351")   PORT_DIPSETTING( 0352, "ID 352")   PORT_DIPSETTING( 0353, "ID 353")
	PORT_DIPSETTING( 0354, "ID 354")   PORT_DIPSETTING( 0355, "ID 355")   PORT_DIPSETTING( 0356, "ID 356")   PORT_DIPSETTING( 0357, "ID 357")
	PORT_DIPSETTING( 0360, "ID 360")   PORT_DIPSETTING( 0361, "ID 361")   PORT_DIPSETTING( 0362, "ID 362")   PORT_DIPSETTING( 0363, "ID 363")
	PORT_DIPSETTING( 0364, "ID 364")   PORT_DIPSETTING( 0365, "ID 365")   PORT_DIPSETTING( 0366, "ID 366")   PORT_DIPSETTING( 0367, "ID 367")
	PORT_DIPSETTING( 0370, "ID 370")   PORT_DIPSETTING( 0371, "ID 371")   PORT_DIPSETTING( 0372, "ID 372")   PORT_DIPSETTING( 0373, "ID 373")
	PORT_DIPSETTING( 0374, "ID 374")   PORT_DIPSETTING( 0375, "ID 375")
INPUT_PORTS_END

u16 alto2_state::kb_r(offs_t offset)
{
	return m_io_row[offset]->read();
}

void alto2_state::utilout_w(u16 data)
{
	// FIXME: write printer data
	// printer_write();
	m_speaker->level_w(data ^ 0177777 ? 1 : 0);
}

/* ROM */
ROM_START( alto2 )
	// dummy region for the maincpu - this is not used in any way
	ROM_REGION( 0400, "maincpu", 0 )
	ROM_FILL(0, 0400, ALTO2_UCODE_INVERTED)
ROM_END

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

void alto2_state::alto2(machine_config &config)
{
	// Basic machine hardware
	// SYSCLK is Display Control part A51 (tagged 29.4MHz) divided by 5(?)
	// 5.8MHz according to de.wikipedia.org/wiki/Xerox_Alto
	ALTO2(config, m_maincpu, XTAL(29'491'200)/5);
	m_maincpu->kb_read_callback().set(FUNC(alto2_state::kb_r));
	m_maincpu->utilout_callback().set(FUNC(alto2_state::utilout_w));
	m_maincpu->set_diablo(0, DIABLO_HD_0);
	m_maincpu->set_diablo(1, DIABLO_HD_1);

	// Video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::white());
	screen.set_physical_aspect(3, 4); // Portrait CRT
	screen.set_raw(XTAL(20'160'000), A2_DISP_TOTAL_WIDTH, 0, A2_DISP_WIDTH, A2_DISP_TOTAL_HEIGHT, 0, A2_DISP_HEIGHT);
	screen.set_refresh_hz(30); // Two interlaced fields at 60Hz => 30Hz frame rate
	screen.set_screen_update("maincpu", FUNC(alto2_cpu_device::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	// Sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.80);

	DIABLO_HD(config, DIABLO_HD_0, 3333333);
	DIABLO_HD(config, DIABLO_HD_1, 3333333);
}

void alto2_state::driver_start()
{
	// Create a timer which fires twice per frame, once for each field
	m_vblank_timer = timer_alloc(FUNC(alto2_state::handle_vblank), this);
	m_vblank_timer->adjust(attotime::from_hz(30*2), 0, attotime::from_hz(30*2));
}

TIMER_CALLBACK_MEMBER(alto2_state::handle_vblank)
{
	m_maincpu->screen_vblank();
}

} // Anonymous namespace

/* Game Drivers */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME   FLAGS
COMP( 1977, alto2, 0,      0,      alto2,   alto2, alto2_state, empty_init, "Xerox", "Alto-II", 0 )



altos2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

2017-11-03 Skeleton

Altos II terminal. Green screen.

Chips: Z80A, 2x Z80DART, Z80CTC, X2210D, 2x CRT9006, CRT9007, CRT9021A, 8x 6116

Other: Beeper.  Crystals: 4.9152, 8.000, 40.000

Keyboard: P8035L CPU, undumped 2716 labelled "358_2758", XTAL marked "4608-300-107 KSS4C"

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

#include "emu.h"
#include "altos2_kbd.h"
#include "cpu/z80/z80.h"
#include "machine/z80daisy.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "machine/x2212.h"
#include "sound/beep.h"
//#include "video/crt9006.h"
#include "video/crt9007.h"
//#include "video/crt9021.h"
#include "screen.h"
#include "speaker.h"


namespace {

class altos2_state : public driver_device
{
public:
	altos2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_novram(*this, "novram")
		, m_vpac(*this, "vpac")
		, m_bell(*this, "bell")
		, m_p_chargen(*this, "chargen")
		, m_p_videoram(*this, "videoram")
	{ }

	void altos2(machine_config &config);

private:
	u8 vpac_r(offs_t offset);
	void vpac_w(offs_t offset, u8 data);
	void video_mode_w(u8 data);

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	virtual void machine_reset() override ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_device<x2210_device> m_novram;
	required_device<crt9007_device> m_vpac;
	required_device<beep_device> m_bell;
	required_region_ptr<u8> m_p_chargen;
	required_shared_ptr<u8> m_p_videoram;
};

void altos2_state::machine_reset()
{
	m_novram->recall(ASSERT_LINE);
	m_novram->recall(CLEAR_LINE);
}

u8 altos2_state::vpac_r(offs_t offset)
{
	return m_vpac->read(offset >> 1);
}

void altos2_state::vpac_w(offs_t offset, u8 data)
{
	m_vpac->write(offset >> 1, data);
}

void altos2_state::video_mode_w(u8 data)
{
	if (BIT(data, 5))
	{
		// D5 = 1 for 132-column mode
		m_vpac->set_unscaled_clock(40_MHz_XTAL / 12);
		m_vpac->set_character_width(6);
	}
	else
	{
		// D5 = 0 for 80-column mode
		m_vpac->set_unscaled_clock(40_MHz_XTAL / 20);
		m_vpac->set_character_width(10);
	}

	m_bell->set_state(BIT(data, 4));

	//logerror("Writing %02X to mode register at %s\n", data, machine().describe_context());
}

u32 altos2_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void altos2_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).rom().region("roms", 0);
	map(0xc000, 0xffff).ram().share("videoram");
}

void altos2_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x03).rw("dart1", FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
	map(0x04, 0x07).rw("dart2", FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
	map(0x08, 0x0b).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x0c, 0x0c).w(FUNC(altos2_state::video_mode_w));
	map(0x40, 0x7f).rw(m_novram, FUNC(x2210_device::read), FUNC(x2210_device::write));
	map(0x80, 0xff).rw(FUNC(altos2_state::vpac_r), FUNC(altos2_state::vpac_w));
}

static INPUT_PORTS_START(altos2)
INPUT_PORTS_END

static const z80_daisy_config daisy_chain[] =
{
	{ "dart1" },
	{ "dart2" },
	{ "ctc" },
	{ nullptr }
};

void altos2_state::altos2(machine_config &config)
{
	Z80(config, m_maincpu, 8_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &altos2_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &altos2_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	z80ctc_device &ctc(Z80CTC(config, "ctc", 8_MHz_XTAL / 2));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	ctc.set_clk<0>(4.9152_MHz_XTAL / 4);
	ctc.set_clk<1>(4.9152_MHz_XTAL / 4);
	ctc.set_clk<2>(4.9152_MHz_XTAL / 4);
	ctc.zc_callback<0>().set("dart1", FUNC(z80dart_device::txca_w));
	ctc.zc_callback<0>().append("dart1", FUNC(z80dart_device::rxca_w));
	ctc.zc_callback<1>().set("dart2", FUNC(z80dart_device::rxca_w));
	ctc.zc_callback<1>().append("dart2", FUNC(z80dart_device::txca_w));
	ctc.zc_callback<2>().set("dart1", FUNC(z80dart_device::rxtxcb_w));

	z80dart_device &dart1(Z80DART(config, "dart1", 8_MHz_XTAL / 2));
	dart1.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	dart1.out_txdb_callback().set("keyboard", FUNC(altos2_keyboard_device::rxd_w));

	z80dart_device &dart2(Z80DART(config, "dart2", 8_MHz_XTAL / 2)); // channel B not used for communications
	dart2.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	dart2.out_dtrb_callback().set(m_novram, FUNC(x2210_device::store)).invert(); // FIXME: no inverter should be needed

	ALTOS2_KEYBOARD(config, "keyboard").txd_callback().set("dart1", FUNC(z80dart_device::rxb_w));

	X2210(config, m_novram);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(40_MHz_XTAL / 2, 960, 0, 800, 347, 0, 325);
	screen.set_screen_update(FUNC(altos2_state::screen_update));

	CRT9007(config, m_vpac, 40_MHz_XTAL / 20);
	m_vpac->set_screen("screen");
	m_vpac->set_character_width(10);
	m_vpac->int_callback().set("ctc", FUNC(z80ctc_device::trg3));

	SPEAKER(config, "mono").front_center();
	BEEP(config, m_bell, 1000).add_route(ALL_OUTPUTS, "mono", 0.50);
}

ROM_START( altos2 )
	ROM_REGION( 0x4000, "roms", 0 )
	ROM_LOAD( "us_v1.2_15732.u32", 0x0000, 0x2000, CRC(a85f7be0) SHA1(3cfa954c916258d86f7f745d10ec2ff5e33261b3) )
	ROM_LOAD( "us_v1.2_15733.u19", 0x2000, 0x2000, CRC(45ebe88a) SHA1(33f16b382a2b365122ebf5e5f7312f8afa45ad15) )

	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "us_v1.1_14410.u34", 0x0000, 0x2000, CRC(0ebb78bf) SHA1(96a1f7d34ff35037cbbc93049c0e2b9c9f11f1db) )
ROM_END

} // anonymous namespace


COMP(1983, altos2, 0, 0, altos2, altos2, altos2_state, empty_init, "Altos Computer Systems", "Altos II Terminal", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS)



altos486.cpp
<---------------------------------------------------------------------->
// License: BSD-3-Clause
// copyright-holders:Carl

// Altos 486, very incomplete

#include "emu.h"
#include "cpu/i86/i186.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/i8255.h"
#include "machine/pic8259.h"
#include "machine/z80sio.h"
#include "machine/pit8253.h"
#include "machine/upd765.h"
#include "machine/i8255.h"
#include "bus/rs232/rs232.h"


namespace {

class altos486_state : public driver_device
{
public:
	altos486_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ram(*this, "main_ram"),
		m_rom(*this, "bios")
	{ }

	void altos486(machine_config &config);

private:
	uint8_t read_rmx_ack(offs_t offset);

	uint16_t mmu_ram_r(offs_t offset);
	uint16_t mmu_io_r(offs_t offset);
	void mmu_ram_w(offs_t offset, uint16_t data);
	void mmu_io_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

	void altos486_io(address_map &map) ATTR_COLD;
	void altos486_mem(address_map &map) ATTR_COLD;
	void altos486_z80_io(address_map &map) ATTR_COLD;
	void altos486_z80_mem(address_map &map) ATTR_COLD;

	required_device<i80186_cpu_device> m_maincpu;
	required_shared_ptr<uint16_t> m_ram;
	required_memory_region m_rom;

	bool m_sys_mode;
	uint8_t m_prot[256];
};

uint8_t altos486_state::read_rmx_ack(offs_t offset)
{
	if(offset == 4)
		return m_maincpu->int_callback(*this, 0);

	return 0;
}

uint16_t altos486_state::mmu_ram_r(offs_t offset)
{
	if (offset < 0x7e000)
		return m_ram[offset]; // TODO
	else
		return m_rom->as_u16(offset - 0x7e000);
}

uint16_t altos486_state::mmu_io_r(offs_t offset)
{
	if (!m_sys_mode)
	{
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
		return 0;
	}
	if(offset < 0x100)
		return m_prot[offset];
	return 0; // TODO
}

void altos486_state::mmu_ram_w(offs_t offset, uint16_t data)
{
	//uint16_t entry = m_prot[offset >> 11];
	//if(!m_sys_mode)
}

void altos486_state::mmu_io_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if(!m_sys_mode)
	{
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
		return;
	}
	if(offset < 0x100)
	{
		if(mem_mask != 0xff00)
			m_prot[offset] = data & 0xf;
	}
}

static void altos486_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

void altos486_state::altos486_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0xfffff).rw(FUNC(altos486_state::mmu_ram_r), FUNC(altos486_state::mmu_ram_w)).share("main_ram");
}

void altos486_state::altos486_io(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(altos486_state::mmu_io_r), FUNC(altos486_state::mmu_io_w));
}

void altos486_state::altos486_z80_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).rom().region("iocpu", 0);
	map(0x1000, 0x17ff).ram();
	//map(0x8000, 0xffff).rw(FUNC(altos486_state::z80_shared_r), FUNC(altos486_state::z80_shared_w)):
}

void altos486_state::altos486_z80_io(address_map &map)
{
	//map(0x00, 0x03).rw("sio0", FUNC(z80sio_device::read), FUNC(z80sio_device::write));
	//map(0x04, 0x07).rw("sio1", FUNC(z80sio_device::read), FUNC(z80sio_device::write));
	//map(0x08, 0x0b).rw("sio2", FUNC(z80sio_device::read), FUNC(z80sio_device::write));
}

void altos486_state::altos486(machine_config &config)
{
	I80186(config, m_maincpu, 32_MHz_XTAL / 2); // divided by 2 externally and by 2 again internally to operate at 8 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &altos486_state::altos486_mem);
	m_maincpu->set_addrmap(AS_IO, &altos486_state::altos486_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259", FUNC(pic8259_device::inta_cb)); // yes, really

	z80_device &iocpu(Z80(config, "iocpu", 32_MHz_XTAL / 8));
	iocpu.set_addrmap(AS_PROGRAM, &altos486_state::altos486_z80_mem);
	iocpu.set_addrmap(AS_IO, &altos486_state::altos486_z80_io);

	pic8259_device &pic8259(PIC8259(config, "pic8259", 0));
	pic8259.out_int_callback().set(m_maincpu, FUNC(i80186_cpu_device::int0_w));
	pic8259.in_sp_callback().set_constant(1);
	pic8259.read_slave_ack_callback().set(FUNC(altos486_state::read_rmx_ack));

	I8255(config, "ppi8255");

	UPD765A(config, "fdc", 32_MHz_XTAL / 4, false, false);
	FLOPPY_CONNECTOR(config, "fdc:0", altos486_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).set_fixed(true);

	z80sio_device& sio0(Z80SIO(config, "sio0", 32_MHz_XTAL / 8)); // Z8440APS
	sio0.out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
	sio0.out_dtra_callback().set("rs232a", FUNC(rs232_port_device::write_dtr));
	sio0.out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts));
	sio0.out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
	sio0.out_dtrb_callback().set("rs232b", FUNC(rs232_port_device::write_dtr));
	sio0.out_rtsb_callback().set("rs232b", FUNC(rs232_port_device::write_rts));
	//sio0.out_int_callback().set(FUNC(altos486_state::sio_interrupt));

	z80sio_device& sio1(Z80SIO(config, "sio1", 32_MHz_XTAL / 8)); // Z8440APS
	sio1.out_txda_callback().set("rs232c", FUNC(rs232_port_device::write_txd));
	sio1.out_dtra_callback().set("rs232c", FUNC(rs232_port_device::write_dtr));
	sio1.out_rtsa_callback().set("rs232c", FUNC(rs232_port_device::write_rts));
	sio1.out_txdb_callback().set("rs232d", FUNC(rs232_port_device::write_txd));
	sio1.out_dtrb_callback().set("rs232d", FUNC(rs232_port_device::write_dtr));
	sio1.out_rtsb_callback().set("rs232d", FUNC(rs232_port_device::write_rts));
	//sio1.out_int_callback().set(FUNC(altos486_state::sio_interrupt));

	z80sio_device& sio2(Z80SIO(config, "sio2", 32_MHz_XTAL / 8)); // Z8440APS
	sio2.out_txda_callback().set("rs232_lp", FUNC(rs232_port_device::write_txd));
	sio2.out_dtra_callback().set("rs232_lp", FUNC(rs232_port_device::write_dtr));
	sio2.out_rtsa_callback().set("rs232_lp", FUNC(rs232_port_device::write_rts));
	//sio2.out_int_callback().set(FUNC(altos486_state::sio_interrupt));

	i8274_device& i8274(I8274(config, "i8274", 32_MHz_XTAL / 8));
	i8274.out_txda_callback().set("rs422_wn", FUNC(rs232_port_device::write_txd));
	i8274.out_dtra_callback().set("rs422_wn", FUNC(rs232_port_device::write_dtr));
	i8274.out_rtsa_callback().set("rs422_wn", FUNC(rs232_port_device::write_rts));
	//i8274.out_int_callback().set(FUNC(altos486_state::sio_interrupt));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, "terminal"));
	rs232a.rxd_handler().set("sio0", FUNC(z80sio_device::rxa_w));
	rs232a.dcd_handler().set("sio0", FUNC(z80sio_device::dcda_w));
	rs232a.cts_handler().set("sio0", FUNC(z80sio_device::ctsa_w));
	//rs232a.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(altos486_terminal));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set("sio0", FUNC(z80sio_device::rxb_w));
	rs232b.dcd_handler().set("sio0", FUNC(z80sio_device::dcdb_w));
	rs232b.cts_handler().set("sio0", FUNC(z80sio_device::ctsb_w));

	rs232_port_device &rs232c(RS232_PORT(config, "rs232c", default_rs232_devices, nullptr));
	rs232c.rxd_handler().set("sio1", FUNC(z80sio_device::rxa_w));
	rs232c.dcd_handler().set("sio1", FUNC(z80sio_device::dcda_w));
	rs232c.cts_handler().set("sio1", FUNC(z80sio_device::ctsa_w));

	rs232_port_device &rs232d(RS232_PORT(config, "rs232d", default_rs232_devices, nullptr));
	rs232d.rxd_handler().set("sio1", FUNC(z80sio_device::rxb_w));
	rs232d.dcd_handler().set("sio1", FUNC(z80sio_device::dcdb_w));
	rs232d.cts_handler().set("sio1", FUNC(z80sio_device::ctsb_w));

	rs232_port_device &rs232_lp(RS232_PORT(config, "rs232_lp", default_rs232_devices, nullptr));
	rs232_lp.rxd_handler().set("sio2", FUNC(z80sio_device::rxa_w));
	rs232_lp.dcd_handler().set("sio2", FUNC(z80sio_device::dcda_w));
	rs232_lp.cts_handler().set("sio2", FUNC(z80sio_device::ctsa_w));

	rs232_port_device &rs422_wn(RS232_PORT(config, "rs422_wn", default_rs232_devices, nullptr));
	rs422_wn.rxd_handler().set("i8274", FUNC(i8274_device::rxa_w));
	rs422_wn.dcd_handler().set("i8274", FUNC(i8274_device::dcda_w));
	rs422_wn.cts_handler().set("i8274", FUNC(i8274_device::ctsa_w));

	pit8253_device &pit0(PIT8253(config, "pit0", 0));
	pit0.set_clk<0>(XTAL(22'118'400)/18); // FIXME
	pit0.set_clk<1>(XTAL(22'118'400)/144); // FIXME
	pit0.set_clk<2>(XTAL(22'118'400)/18); // FIXME

	pit8253_device &pit1(PIT8253(config, "pit1", 0));
	pit1.set_clk<0>(XTAL(22'118'400)/18); // FIXME
	pit1.set_clk<1>(XTAL(22'118'400)/144); // FIXME
	pit1.set_clk<2>(XTAL(22'118'400)/18); // FIXME
}


ROM_START( altos486 )
	ROM_REGION( 0x4000, "bios", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v11", "Altos 486 v1.1")
	ROMX_LOAD("16577_lo_v1.1.bin",   0x0000, 0x2000, CRC(65a9db18) SHA1(3ac2b87f1fc0b28ed4907c9b4091aaa170609674), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("16576_hi_v1.1.bin",   0x0001, 0x2000, CRC(cea4cd8d) SHA1(f9f49828bd5e3281bd7cc34d4460ca1b677530b0), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v12", "Altos 486 v1.2")
	ROMX_LOAD("16577-003_4d_v1.2.bin",   0x0000, 0x2000, CRC(e2ac806b) SHA1(9b358246e26b3e85a6dff418899a180370884537), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("16576-003_3d_v1.2.bin",   0x0001, 0x2000, CRC(912f4c12) SHA1(df5088e8610513b577926b0c752e3b54bc880167), ROM_SKIP(1) | ROM_BIOS(1))

	ROM_REGION( 0x1000, "iocpu", 0 )
	ROM_LOAD("16019_z80.bin", 0x0000, 0x1000, CRC(68b1b2e1) SHA1(5d83609a465029212d5e3f72ac9c520b3dbed838))

	ROM_REGION( 0x0020, "proms", 0 )
	ROM_LOAD( "15020.bin",    0x0000, 0x0020, CRC(6a2bd961) SHA1(e9a9ed235574c9871dc32a80ff5ca4df6bd531e1) )
ROM_END

} // anonymous namespace


COMP( 1984, altos486, 0, 0, altos486, 0, altos486_state, empty_init, "Altos Computer Systems", "Altos 486", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



altos5.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

    Altos 5-15

    ToDo:
    - When running MP/M, dir command crashes the system

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

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "imagedev/snapquik.h"
#include "machine/clock.h"
#include "machine/wd_fdc.h"
#include "machine/z80ctc.h"
#include "machine/z80daisy.h"
#include "machine/z80dma.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"

#include "softlist_dev.h"


namespace {

class altos5_state : public driver_device
{
public:
	altos5_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pio0(*this, "pio0")
		, m_dma (*this, "dma")
		, m_fdc (*this, "fdc")
		, m_p_prom(*this, "proms")
		, m_floppy0(*this, "fdc:0")
		, m_floppy1(*this, "fdc:1")
		, m_bankr(*this, "bankr%x", 0)
		, m_bankw(*this, "bankw%x", 0)
	{ }

	void altos5(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);
	uint8_t memory_read_byte(offs_t offset);
	void memory_write_byte(offs_t offset, uint8_t data);
	uint8_t io_read_byte(offs_t offset);
	void io_write_byte(offs_t offset, uint8_t data);
	uint8_t port08_r();
	uint8_t port09_r();
	void port08_w(uint8_t data);
	void port09_w(uint8_t data);
	void port14_w(uint8_t data);
	void setup_banks(uint8_t source);
	uint8_t convert(offs_t offset, bool state);
	void busreq_w(int state);
	void fdc_intrq_w(int state);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	uint8_t m_port08 = 0U;
	uint8_t m_port09 = 0U;
	bool m_ipl = 0;
	offs_t m_curr_bank = 0;
	floppy_image_device *m_floppy = 0;
	std::unique_ptr<u8[]> m_ram;  // main ram 192k
	std::unique_ptr<u8[]> m_dummy;  // for wrpt
	required_device<z80_device> m_maincpu;
	required_device<z80pio_device> m_pio0;
	required_device<z80dma_device> m_dma;
	required_device<fd1797_device> m_fdc;
	required_region_ptr<u8> m_p_prom;
	required_device<floppy_connector> m_floppy0;
	required_device<floppy_connector> m_floppy1;
	required_memory_bank_array<16> m_bankr;
	required_memory_bank_array<16> m_bankw;
};


void altos5_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).bankr(m_bankr[0x0]).bankw(m_bankw[0x0]);
	map(0x1000, 0x1fff).bankr(m_bankr[0x1]).bankw(m_bankw[0x1]);
	map(0x2000, 0x2fff).bankr(m_bankr[0x2]).bankw(m_bankw[0x2]);
	map(0x3000, 0x3fff).bankr(m_bankr[0x3]).bankw(m_bankw[0x3]);
	map(0x4000, 0x4fff).bankr(m_bankr[0x4]).bankw(m_bankw[0x4]);
	map(0x5000, 0x5fff).bankr(m_bankr[0x5]).bankw(m_bankw[0x5]);
	map(0x6000, 0x6fff).bankr(m_bankr[0x6]).bankw(m_bankw[0x6]);
	map(0x7000, 0x7fff).bankr(m_bankr[0x7]).bankw(m_bankw[0x7]);
	map(0x8000, 0x8fff).bankr(m_bankr[0x8]).bankw(m_bankw[0x8]);
	map(0x9000, 0x9fff).bankr(m_bankr[0x9]).bankw(m_bankw[0x9]);
	map(0xa000, 0xafff).bankr(m_bankr[0xa]).bankw(m_bankw[0xa]);
	map(0xb000, 0xbfff).bankr(m_bankr[0xb]).bankw(m_bankw[0xb]);
	map(0xc000, 0xcfff).bankr(m_bankr[0xc]).bankw(m_bankw[0xc]);
	map(0xd000, 0xdfff).bankr(m_bankr[0xd]).bankw(m_bankw[0xd]);
	map(0xe000, 0xefff).bankr(m_bankr[0xe]).bankw(m_bankw[0xe]);
	map(0xf000, 0xffff).bankr(m_bankr[0xf]).bankw(m_bankw[0xf]);
}

void altos5_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).rw(m_dma, FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	map(0x04, 0x07).rw(m_fdc, FUNC(fd1797_device::read), FUNC(fd1797_device::write));
	map(0x08, 0x0b).rw(m_pio0, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x0c, 0x0f).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x10, 0x13).rw("pio1", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x14, 0x17).w(FUNC(altos5_state::port14_w));
	map(0x1c, 0x1f).rw("dart", FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
	//map(0x20, 0x23) // Hard drive
	map(0x2c, 0x2f).rw("sio", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
}

/* Input ports */
static INPUT_PORTS_START( altos5 )
INPUT_PORTS_END

uint8_t altos5_state::convert(offs_t offset, bool state)
{
	uint8_t data = m_p_prom[offset];

	// if IPL and /A12, point at rom
	if (!state && m_ipl && !BIT(offset, 0))
		data = 0x31;
	else
	// if WPRT point at nothing
	if (state && BIT(data, 7))
		data = 0x30;

	// mask off wprt (no longer needed)
	// normalise bank number (4x becomes 0x; 2x and 1x are already ok)
	return data & 0x3f;
}

void altos5_state::setup_banks(uint8_t source)
{
	offs_t offs,temp;
	// WPRT | template | dma bank / cpu bank

	if (source == 1) // use DMA banks only if BUSACK is asserted
		offs = ((bitswap<8>(m_port09, 0, 0, 0, 5, 1, 2, 7, 6) << 4) & 0x1f0) ^ 0x100;
	else
		offs = ((bitswap<8>(m_port09, 0, 0, 0, 5, 1, 2, 4, 3) << 4) & 0x1f0) ^ 0x100;

	temp = offs;
	if ((source == 2) || (temp != m_curr_bank))
	{
		for (auto &bank : m_bankr)
			bank->set_entry(convert(offs++, 0));

		offs = temp;
		for (auto &bank : m_bankw)
			bank->set_entry(convert(offs++, 1));
	}

	m_curr_bank = temp;
}

void altos5_state::machine_reset()
{
	m_curr_bank = 0;
	m_port08 = 0;
	m_port09 = 0;
	m_ipl = 1;
	setup_banks(2);
	m_floppy = nullptr;
	m_maincpu->reset();
}

static const z80_daisy_config daisy_chain_intf[] =
{
	{ "dma" },
	{ "pio0" },
	{ "pio1" },
	{ "ctc" },
	{ "dart" },
	{ "sio" },
	{ nullptr }
};


// turns off IPL mode, removes boot rom from memory map
void altos5_state::port14_w(uint8_t data)
{
	m_ipl = 0;
	setup_banks(2);
}

uint8_t altos5_state::memory_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	return prog_space.read_byte(offset);
}

void altos5_state::memory_write_byte(offs_t offset, uint8_t data)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	prog_space.write_byte(offset, data);
}

uint8_t altos5_state::io_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_IO);
	return prog_space.read_byte(offset);
}

void altos5_state::io_write_byte(offs_t offset, uint8_t data)
{
	address_space& prog_space = m_maincpu->space(AS_IO);
	prog_space.write_byte(offset, data);
}

void altos5_state::busreq_w(int state)
{
	m_maincpu->set_input_line(Z80_INPUT_LINE_BUSRQ, state);
	setup_banks(state); // adjust banking for dma or cpu
}

/*
d0: L = a HD is present
d1: L = a 2nd hard drive is present
d2: unused configuration input (must be H to skip HD boot)
d3: selected floppy is single(L) or double sided(H)
d7: IRQ from FDC
*/
uint8_t altos5_state::port08_r()
{
	uint8_t data = m_port08 | 0x87;
	if (m_floppy)
		data |= ((uint8_t)m_floppy->twosid_r() << 3); // get number of sides
	return data;
}

/*
d0: HD IRQ
*/
uint8_t altos5_state::port09_r()
{
	return m_port09 & 0xfe;
}

/*
d4: DDEN (H = double density)
d5: DS (H = drive 2)
d6: SS (H = side 2)
*/
void altos5_state::port08_w(uint8_t data)
{
	m_port08 = data & 0x70;

	m_floppy = nullptr;
	if (BIT(data, 5))
		m_floppy = m_floppy1->get_device();
	else
		m_floppy = m_floppy0->get_device();

	m_fdc->set_floppy(m_floppy);

	if (m_floppy)
	{
		m_floppy->mon_w(0);
		m_floppy->ss_w(BIT(data, 6));
		m_fdc->dden_w(!BIT(data, 4));
	}
}

/*
d1, 2: Memory Map template selection (0 = diag; 1 = oasis; 2 = mp/m)
d3, 4: CPU bank select
d5:    H = Write protect of common area
d6, 7: DMA bank select (not emulated)
*/
void  altos5_state::port09_w(uint8_t data)
{
	m_port09 = data;
	setup_banks(2);
}


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

    Quickload

    This loads a .COM file to address 0x100 then jumps
    there. Sometimes .COM has been renamed to .CPM to
    prevent windows going ballistic. These can be loaded
    as well.

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

QUICKLOAD_LOAD_MEMBER(altos5_state::quickload_cb)
{
	setup_banks(2);

	address_space& prog_space = m_maincpu->space(AS_PROGRAM);

	// Avoid loading a program if CP/M-80 is not in memory
	if ((prog_space.read_byte(0) != 0xc3) || (prog_space.read_byte(5) != 0xc3))
	{
		machine_reset();
		return std::make_pair(image_error::UNSUPPORTED, "CP/M must already be running");
	}

	const int mem_avail = 256 * prog_space.read_byte(7) + prog_space.read_byte(6) - 512;
	if (mem_avail < image.length())
		return std::make_pair(image_error::UNSPECIFIED, "Insufficient memory available");

	// Load image to the TPA (Transient Program Area)
	uint16_t quickload_size = image.length();
	for (uint16_t i = 0; i < quickload_size; i++)
	{
		uint8_t data;
		if (image.fread(&data, 1) != 1)
			return std::make_pair(image_error::UNSPECIFIED, "Problem reading the image at offset " + std::to_string(i));
		prog_space.write_byte(i + 0x100, data);
	}

	// clear out command tail
	prog_space.write_byte(0x80, 0);
	prog_space.write_byte(0x81, 0);

	// Roughly set SP basing on the BDOS position
	m_maincpu->set_state_int(Z80_SP, mem_avail + 384);
	m_maincpu->set_pc(0x100); // start program

	return std::make_pair(std::error_condition(), std::string());
}


static void altos5_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

void altos5_state::fdc_intrq_w(int state)
{
	uint8_t data = m_port08 | ((uint8_t)(state) << 7);
	m_pio0->port_a_write(data);
}

void altos5_state::machine_start()
{
	m_ram = make_unique_clear<u8[]>(0x30000);
	m_dummy = std::make_unique<u8[]>(0x1000);

	u8 *r = m_ram.get();
	u8 *d = m_dummy.get();
	u8 *m = memregion("maincpu")->base();

	for (auto &bank : m_bankr)
		bank->configure_entries(0, 48, r, 0x1000);
	for (auto &bank : m_bankw)
		bank->configure_entries(0, 48, r, 0x1000);
	for (auto &bank : m_bankr)
		bank->configure_entry(48, d);
	for (auto &bank : m_bankw)
		bank->configure_entry(48, d);
	for (auto &bank : m_bankr)
		bank->configure_entry(49, m);
	for (auto &bank : m_bankw)
		bank->configure_entry(49, m);

	save_pointer(NAME(m_ram), 0x30000);
	save_item(NAME(m_port08));
	save_item(NAME(m_port09));
	save_item(NAME(m_ipl));
	save_item(NAME(m_curr_bank));
}

void altos5_state::altos5(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 8_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &altos5_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &altos5_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain_intf);
	m_maincpu->busack_cb().set(m_dma, FUNC(z80dma_device::bai_w));

	/* devices */
	Z80DMA(config, m_dma, 8_MHz_XTAL / 2);
	m_dma->out_busreq_callback().set(FUNC(altos5_state::busreq_w));
	m_dma->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	// BAO, not used
	m_dma->in_mreq_callback().set(FUNC(altos5_state::memory_read_byte));
	m_dma->out_mreq_callback().set(FUNC(altos5_state::memory_write_byte));
	m_dma->in_iorq_callback().set(FUNC(altos5_state::io_read_byte));
	m_dma->out_iorq_callback().set(FUNC(altos5_state::io_write_byte));

	Z80PIO(config, m_pio0, 8_MHz_XTAL / 2);
	m_pio0->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio0->in_pa_callback().set(FUNC(altos5_state::port08_r));
	m_pio0->out_pa_callback().set(FUNC(altos5_state::port08_w));
	m_pio0->in_pb_callback().set(FUNC(altos5_state::port09_r));
	m_pio0->out_pb_callback().set(FUNC(altos5_state::port09_w));

	z80pio_device& pio1(Z80PIO(config, "pio1", 8_MHz_XTAL / 2));
	pio1.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80dart_device& dart(Z80DART(config, "dart", 8_MHz_XTAL / 2));
	// Channel A - console #3
	// Channel B - printer
	dart.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80sio_device& sio(Z80SIO(config, "sio", 8_MHz_XTAL / 2));
	// Channel A - console #2
	// WRDY connects to (altos5_state, fdc_intrq_w)
	// Channel B - console #1
	sio.out_txdb_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	sio.out_dtrb_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	sio.out_rtsb_callback().set("rs232", FUNC(rs232_port_device::write_rts));
	sio.out_int_callback().set_inputline("maincpu", INPUT_LINE_IRQ0);

	z80ctc_device &ctc(Z80CTC(config, "ctc", 8_MHz_XTAL / 2));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	ctc.set_clk<0>(8_MHz_XTAL / 4); // 2MHz
	ctc.set_clk<1>(8_MHz_XTAL / 4); // 2MHz
	ctc.set_clk<2>(8_MHz_XTAL / 4); // 2MHz
	ctc.zc_callback<0>().set("sio", FUNC(z80sio_device::rxtxcb_w));    // SIO Ch B
	ctc.zc_callback<1>().set("dart", FUNC(z80dart_device::txca_w));    // Z80DART Ch A, SIO Ch A
	ctc.zc_callback<1>().append("dart", FUNC(z80dart_device::rxca_w));
	ctc.zc_callback<1>().append("sio", FUNC(z80sio_device::txca_w));
	ctc.zc_callback<1>().append("sio", FUNC(z80sio_device::rxca_w));
	ctc.zc_callback<2>().set("dart", FUNC(z80dart_device::rxtxcb_w));  // Z80DART Ch B

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("sio", FUNC(z80sio_device::rxb_w));
	rs232.dcd_handler().set("sio", FUNC(z80sio_device::dcdb_w));
	rs232.cts_handler().set("sio", FUNC(z80sio_device::ctsb_w));

	FD1797(config, m_fdc, 8_MHz_XTAL / 8);
	m_fdc->intrq_wr_callback().set(FUNC(altos5_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(m_dma, FUNC(z80dma_device::rdy_w));
	FLOPPY_CONNECTOR(config, "fdc:0", altos5_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", altos5_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	SOFTWARE_LIST(config, "flop_list").set_original("altos5");
	QUICKLOAD(config, "quickload", "com,cpm", attotime::from_seconds(3)).set_load_callback(FUNC(altos5_state::quickload_cb));
}


/* ROM definition */
ROM_START( altos5 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("2732.bin",   0x0000, 0x1000, CRC(15fdc7eb) SHA1(e15bdf5d5414ad56f8c4bb84edc6f967a5f01ba9)) // bios

	ROM_REGION( 0x200, "proms", 0 )
	ROM_LOAD("82s141.bin", 0x0000, 0x0200, CRC(35c8078c) SHA1(dce24374bfcc5d23959e2c03485d82a119c0c3c9)) // banking control
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT         COMPANY  FULLNAME      FLAGS */
COMP( 1982, altos5, 0,      0,      altos5,  altos5, altos5_state, empty_init, "Altos", "Altos 5-15", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



altos586.cpp
<---------------------------------------------------------------------->
// license:BSD-2-Clause
// copyright-holders:Lubomir Rintel

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

    Altos 586 computer emulation

    Work in progress. The current goal is to iron our the overall flaws
    caused by my inexperience with C++ and MAME. The ultimate goal is to
    make this complete enough for XENIX to run. I guess at that point
    it would imply CP/M and OASIS working too.

    At this point I've not tried to boot anything but the diags floppy,
    and it's unlikely to work.

    The tests on the diags floppy work well, including serial, clock,
    memory management, hard disk & floppy diags and maintenance.
    What the diags disk doesn't exercise are interrupts, access control of
    bus access from IOP/HDC and system call machinery.

    Literature:

    [1] 586T/986T System Reference Manual, P/N 690-15813-002, April 1985
        This describes a slightly different system.
        690-15813-002_Altos_586T_986T_System_Reference_Apr85.pdf

    [2] Notes on the Altos 586 Computer & Firmware disassembly
        https://github.com/lkundrak/altos586/

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

#include "emu.h"

#include "altos586_hdc.h"

#include "bus/rs232/rs232.h"

#include "cpu/i86/i86.h"
#include "cpu/z80/z80.h"

#include "imagedev/floppy.h"

#include "machine/clock.h"
#include "machine/mm58167.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/ram.h"
#include "machine/wd_fdc.h"
#include "machine/z80dma.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"

// TODO: Should this be a separate device? It is on a same board.
// I've split this so that I've got two device_memory_interface-s --
// the board object provides the maps and unmanaged memory and I/O
// spaces, while the MMU provides the managed ones for the bus
// peripherals (IOP and HDC) via its AS_PROGRAM/AS_IO spaces and
// cpu_{mem,io}_{r,w} routines for the access from the main CPU.
// Could there be a better way to structure this?
class altos586_mmu_device : public device_t, public device_memory_interface
{
public:
	using violation_delegate = device_delegate<void ()>;

	altos586_mmu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	template <typename T>
	altos586_mmu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&board_tag)
		: altos586_mmu_device(mconfig, tag, owner, clock)
	{
		m_board.set_tag(std::forward<T>(board_tag));
	}

	template <typename... T> void set_violation_callback(T &&... args) { m_violation_callback.set(std::forward<T>(args)...); }
	auto syscall_handler() { return m_syscall_handler.bind(); }

	// Managed access from the main board CPU
	u16 cpu_io_r(offs_t offset, u16 mem_mask);
	void cpu_io_w(offs_t offset, u16 data, u16 mem_mask);
	u16 cpu_mem_r(offs_t offset, u16 mem_mask);
	void cpu_mem_w(offs_t offset, u16 data, u16 mem_mask);

	// MMU control registers
	u16 err_addr1_r(offs_t offset) { return m_err_addr1; }
	u16 err_addr2_r(offs_t offset) { return m_err_addr2; }
	u16 clr_violation_r(offs_t offset) { return m_violation = 0xffff; }
	void clr_violation_w(offs_t offset, u16 data) { clr_violation_r(offset); }
	u16 violation_r(offs_t offset) { return m_violation; /* | jumpers */ }

	// Page table SRAM (should this be a ram device instead?
	u16 map_ram_r(offs_t offset) { return m_map_ram[offset & 0xff]; }
	void map_ram_w(offs_t offset, u16 data, u16 mem_mask) { m_map_ram[offset & 0xff] = data; }

	// Control/Mode
	void set_system_mode();
	void check_user_mode();
	void clr_syscall_w(offs_t offset, u8 data);
	u16 control_r(offs_t offset);
	void control_w(offs_t offset, u16 data);
	void cpu_if_w(int state);

protected:
	// device_t implementation
	virtual void device_start() override ATTR_COLD;

	// device_memory_interface implementation
	virtual space_config_vector memory_space_config() const override;

private:
	// Managed memory access from the bus (IOP and HDC)
	void bus_mem(address_map &map) ATTR_COLD;
	u16 bus_mem_r(offs_t offset, u16 mem_mask);
	void bus_mem_w(offs_t offset, u16 data, u16 mem_mask);

	// Managed I/O access from the bus (IOP and HDC)
	void bus_io(address_map &map) ATTR_COLD;
	u16 bus_io_r(offs_t offset, u16 mem_mask);
	void bus_io_w(offs_t offset, u16 data, u16 mem_mask);

	// Memory translation and address checking
	offs_t phys_mem_addr(offs_t offset);
	void signal_violation(u16 violation_bits);
	bool check_mem_violation(offs_t offset, int access_bit, int access_bit_set, u16 violation_bits);

	violation_delegate m_violation_callback;
	devcb_write_line m_syscall_handler;

	// Access to board's unmanaged address spaces
	required_device<device_memory_interface> m_board;
	memory_access<20, 1, 0, ENDIANNESS_LITTLE>::specific m_mem;
	memory_access<16, 1, 0, ENDIANNESS_LITTLE>::specific m_io;

	// Configuration for managed address spaces we provide
	address_space_config m_program_config;
	address_space_config m_io_config;

	// User or System mode
	bool m_user;
	bool m_cpu_if;
	u16 m_control;

	u16 m_err_addr1;
	u16 m_err_addr2;

	enum : u16 {
		INVALID_INSN        = 0x0001,   // Invalid Instruction
		END_OF_STACK        = 0x0008,   // End of Stack Warning
		SYS_W_VIOLATION     = 0x0010,   // System write Violation
		USER_W_VIOLATION    = 0x0080,   // User Mode Write Violation
		IOP_W_VIOLATION     = 0x0400,   // I/O Processor Write Violation
		USER_ACC_VIOLATION  = 0x0800,   // User Mode Access Violation
	};
	u16 m_violation;

	enum {
		IOP_W           = 11,       // Allow I/O Processor Write
		SYS_W           = 12,       // Allow System Write
		STACK_BOUND     = 13,       // Stack Boundary Page
		USER_ACC        = 14,       // Allow User Access
		USER_W          = 15,       // Allow User Write
	};
	u16 m_map_ram[256];
};

DEFINE_DEVICE_TYPE(ALTOS586_MMU, altos586_mmu_device, "altos586_mmu", "ALTOS586 MMU")

altos586_mmu_device::altos586_mmu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, ALTOS586_MMU, tag, owner, clock)
	, device_memory_interface(mconfig, *this)
	, m_violation_callback(*this)
	, m_syscall_handler(*this)
	, m_board(*this, finder_base::DUMMY_TAG)
	, m_program_config("program", ENDIANNESS_LITTLE, 16, 20, 0, address_map_constructor(FUNC(altos586_mmu_device::bus_mem), this))
	, m_io_config("io", ENDIANNESS_LITTLE, 16, 16, 0, address_map_constructor(FUNC(altos586_mmu_device::bus_io), this))
{
}

u16 altos586_mmu_device::cpu_mem_r(offs_t offset, u16 mem_mask)
{
	if (!machine().side_effects_disabled() && m_user && check_mem_violation(offset, USER_ACC, 1, USER_ACC_VIOLATION)) {
		return 0xffff;
	} else {
		return m_mem.read_word(phys_mem_addr(offset), mem_mask);
	}
}

void altos586_mmu_device::cpu_mem_w(offs_t offset, u16 data, u16 mem_mask)
{
	if (m_user) {
		if (check_mem_violation(offset, USER_W, 1, USER_W_VIOLATION))
			return;
		if (check_mem_violation(offset, USER_ACC, 1, USER_ACC_VIOLATION))
			return;
	} else {
		if (check_mem_violation(offset, SYS_W, 1, SYS_W_VIOLATION))
			return;
	}

	if ((offset & 0x7ff) < 0x40) {
		// Check the stack boundary watermark so that XENIX could map
		// some more memory, but don't abort the write cycle.
		check_mem_violation(offset, STACK_BOUND, 0, END_OF_STACK);
	}

	m_mem.write_word(phys_mem_addr(offset), data, mem_mask);
}

u16 altos586_mmu_device::cpu_io_r(offs_t offset, u16 mem_mask)
{
	if (!machine().side_effects_disabled() && m_user) {
		m_syscall_handler(ASSERT_LINE);
		return 0xffff;
	} else {
		return m_io.read_word(offset << 1, mem_mask);
	}
}

void altos586_mmu_device::cpu_io_w(offs_t offset, u16 data, u16 mem_mask)
{
	if (m_user) {
		// XENIX user programs do outb 8800 to trigger the interrupt
		// TODO: I have not tested if I got syscall handling right.
		m_syscall_handler(ASSERT_LINE);
	} else {
		m_io.write_word(offset << 1, data, mem_mask);
	}
}

void altos586_mmu_device::bus_mem(address_map &map)
{
	map(0x00000, 0xfffff).rw(FUNC(altos586_mmu_device::bus_mem_r), FUNC(altos586_mmu_device::bus_mem_w));
}

u16 altos586_mmu_device::bus_mem_r(offs_t offset, u16 mem_mask)
{
	return m_mem.read_word(phys_mem_addr(offset), mem_mask);
}

void altos586_mmu_device::bus_mem_w(offs_t offset, u16 data, u16 mem_mask)
{
	if (check_mem_violation(offset, IOP_W, 1, IOP_W_VIOLATION)) {
		return;
	} else {
		m_mem.write_word(phys_mem_addr(offset), data, mem_mask);
	}
}

void altos586_mmu_device::bus_io(address_map &map)
{
	// As per the manual, I/O addresses up to 0x3ff are available only to CPU, not other bus masters
	map(0x0400, 0xffff).rw(FUNC(altos586_mmu_device::bus_io_r), FUNC(altos586_mmu_device::bus_io_w));
}

u16 altos586_mmu_device::bus_io_r(offs_t offset, u16 mem_mask)
{
	return m_io.read_word(offset, mem_mask);
}

void altos586_mmu_device::bus_io_w(offs_t offset, u16 data, u16 mem_mask)
{
	m_io.write_word(offset, data, mem_mask);
}

void altos586_mmu_device::device_start()
{
	set_system_mode();
	m_cpu_if = false;

	m_board->space(AS_PROGRAM).specific(m_mem);
	m_board->space(AS_IO).specific(m_io);

	m_violation_callback.resolve_safe();

	save_item(NAME(m_user));
	save_item(NAME(m_cpu_if));
	save_item(NAME(m_control));
	save_item(NAME(m_err_addr1));
	save_item(NAME(m_err_addr2));
	save_item(NAME(m_violation));
	save_item(NAME(m_map_ram));
}

device_memory_interface::space_config_vector altos586_mmu_device::memory_space_config() const
{
	return space_config_vector {
			std::make_pair(AS_PROGRAM, &m_program_config),
			std::make_pair(AS_IO, &m_io_config) };
}

offs_t altos586_mmu_device::phys_mem_addr(offs_t offset)
{
	return ((m_map_ram[offset >> 11] & 0xff) << 12) | ((offset & 0x7ff) << 1);
}

void altos586_mmu_device::signal_violation(u16 violation_bits)
{
	u16 old_violation = m_violation;
	m_violation &= ~violation_bits;
	if (old_violation == 0xffff && BIT(m_control, 2)) {
		// TODO: Is this (user mode request) bit actually cleared on real hw?
		// Is this the right place to do that?
		m_control &= ~1;
		m_violation_callback();
	}
}

bool altos586_mmu_device::check_mem_violation(offs_t offset, int access_bit, int access_bit_set, u16 violation_bits)
{
	u16 acc = m_map_ram[offset >> 11];

	if (BIT(acc, access_bit) == access_bit_set) {
		return false;
	} else {
		if (m_violation == 0xffff) {
			// TODO: Would it be less clumsy to move setting
			// of m_err_addr* into signal_violation()?
			m_err_addr1 = offset << 1;
			m_err_addr2 = (offset >> 3) & 0xf000;

			if (m_user)
				m_err_addr2 |= 0x0100;
			// TODO: Warm start bit. When is it set?
			m_err_addr2 |= 0x0200;
			if (BIT(m_control, 2))
				m_err_addr2 |= 0x0800; // NMI enabled
		}
		signal_violation(violation_bits);
		return true;
	}
}

void altos586_mmu_device::set_system_mode()
{
	m_user = false;
}

void altos586_mmu_device::check_user_mode()
{
	if (m_cpu_if && BIT(m_control, 0))
		m_user = true;
}

void altos586_mmu_device::clr_syscall_w(offs_t offset, u8 data)
{
	m_syscall_handler(CLEAR_LINE);
}

u16 altos586_mmu_device::control_r(offs_t offset)
{
	// TODO: Is this register actually readable?
	return m_control;
}

void altos586_mmu_device::control_w(offs_t offset, u16 data)
{
	m_control = data;
	check_user_mode();
}

void altos586_mmu_device::cpu_if_w(int state)
{
	if (state == ASSERT_LINE) {
		m_cpu_if = true;
		check_user_mode();
	} else if (m_user) {
		if (m_violation == 0xffff) {
			m_err_addr1 = 0;
			m_err_addr2 = 0;
		}
		signal_violation(INVALID_INSN);
	}
}

namespace {

class altos586_state : public driver_device, public device_memory_interface {
public:
	altos586_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, device_memory_interface(mconfig, *this)
		, m_program_config("program", ENDIANNESS_LITTLE, 16, 20, 0, address_map_constructor(FUNC(altos586_state::mem_map), this))
		, m_io_config("io", ENDIANNESS_LITTLE, 16, 16, 0, address_map_constructor(FUNC(altos586_state::io_map), this))
		, m_cpu(*this, "cpu")
		, m_mmu(*this, "mmu")
		, m_ram(*this, RAM_TAG)
		, m_pic(*this, "pic8259")
		, m_pit(*this, "pit")
		, m_iop(*this, "iop")
		, m_fdc(*this, "fd1797")
		, m_floppy(*this, "fd1797:%u", 0)
	{
	}

	void altos586(machine_config &config);

protected:
	// driver_device implementation
	virtual void machine_start() override ATTR_COLD;

	// device_memory_interface implementation
	virtual space_config_vector memory_space_config() const override;

private:
	void mmu_violation();

	// IOP interfacing.
	void iop_attn_w(offs_t offset, u16 data);
	u8 hostram_r(offs_t offset);
	void hostram_w(offs_t offset, u8 data);
	void hiaddr_w(u8 data);
	u8 pio_pa_r(offs_t offset);
	void pio_pa_w(offs_t offset, u8 data);
	void pio_pb_w(offs_t offset, u8 data);

	IRQ_CALLBACK_MEMBER(inta_cb);

	// Maps for the 8086 & Z80 processors
	void cpu_mem(address_map &map) ATTR_COLD;
	void cpu_io(address_map &map) ATTR_COLD;
	void iop_mem(address_map &map) ATTR_COLD;
	void iop_io(address_map &map) ATTR_COLD;

	// Maps and physical spaces used by the MMU
	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	address_space_config m_program_config;
	address_space_config m_io_config;

	required_device<i8086_cpu_device> m_cpu;
	required_device<altos586_mmu_device> m_mmu;
	required_device<ram_device> m_ram;
	required_device<pic8259_device> m_pic;
	required_device<pit8254_device> m_pit;
	required_device<z80_device> m_iop;
	required_device<fd1797_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;

	u8 m_hiaddr;

	memory_access<20, 1, 0, ENDIANNESS_LITTLE>::specific m_mmu_mem;
};

void altos586_state::mem_map(address_map &map)
{
	// ROM needs to be there for IOP bootup
	map(0xfc000, 0xfffff).rom().region("bios", 0);
}

void altos586_state::io_map(address_map &map)
{
	map(0x0040, 0x0047).w(m_mmu, FUNC(altos586_mmu_device::clr_syscall_w));
	// TODO: Manual reads: 0048H to 004FH Channel attention 0
	// (reserved for future bus master channel attentions)
	map(0x0050, 0x0057).w(FUNC(altos586_state::iop_attn_w));
	map(0x0058, 0x005f).rw(m_mmu, FUNC(altos586_mmu_device::control_r), FUNC(altos586_mmu_device::control_w));
	map(0x0060, 0x0067).r(m_mmu, FUNC(altos586_mmu_device::err_addr2_r));
	map(0x0068, 0x006F).r(m_mmu, FUNC(altos586_mmu_device::err_addr1_r));
	map(0x0070, 0x0077).rw(m_mmu, FUNC(altos586_mmu_device::clr_violation_r), FUNC(altos586_mmu_device::clr_violation_w));
	map(0x0078, 0x007f).r(m_mmu, FUNC(altos586_mmu_device::violation_r));

	// These are wired funnily
	map(0x0080, 0x00ff).lrw16(
			NAME([this] (offs_t offset) { return m_pic->read(~offset & 1); }),
			NAME([this] (offs_t offset, u16 data) { m_pic->write(~offset & 1, data); }));
	map(0x0100, 0x01ff).lrw16(
			NAME([this] (offs_t offset) { return m_pit->read(~offset) << 8; }),
			NAME([this] (offs_t offset, u16 data) { m_pit->write(~offset, data >> 8); }));

	map(0x0200, 0x03ff).rw(m_mmu, FUNC(altos586_mmu_device::map_ram_r), FUNC(altos586_mmu_device::map_ram_w));

	// Addresses 0400H to FFFFH are, unlike the above, accessible from
	// other bus masters than the main CPU. Peripherals are expected to
	// hook there. Documented as "Reserved for system bus I/O"

	// Bus peripherals
	map(0xff00, 0xff01).w("hdc", FUNC(altos586_hdc_device::attn_w));
}

void altos586_state::cpu_mem(address_map &map)
{
	map(0x00000, 0xfffff).rw(m_mmu, FUNC(altos586_mmu_device::cpu_mem_r), FUNC(altos586_mmu_device::cpu_mem_w));
	// ROM is always mapped at these addresses
	map(0xfc000, 0xfffff).rom().region("bios", 0);
}

void altos586_state::cpu_io(address_map &map)
{
	map(0x0000, 0xffff).rw(m_mmu, FUNC(altos586_mmu_device::cpu_io_r), FUNC(altos586_mmu_device::cpu_io_w));
}

void altos586_state::iop_mem(address_map &map)
{
	map(0x0000, 0x1fff).rom().region("iop", 0);
	map(0x2000, 0x27ff).ram();
	map(0x8000, 0xffff).rw(FUNC(altos586_state::hostram_r), FUNC(altos586_state::hostram_w));
}

void altos586_state::iop_io(address_map &map)
{
	map.global_mask(0xff);

	map(0x00, 0x00).w(FUNC(altos586_state::hiaddr_w));                      // 0x00 Address Latch
	map(0x20, 0x23).rw("iop_pit0", FUNC(pit8254_device::read), FUNC(pit8254_device::write));    // 0x20 PIT 0
	map(0x24, 0x27).rw("iop_pit1", FUNC(pit8254_device::read), FUNC(pit8254_device::write));    // 0x24 PIT 1
	map(0x28, 0x2b).rw("iop_sio0", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w)); // 0x28 SIO 0
	map(0x2c, 0x2f).rw("iop_sio1", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w)); // 0x2C SIO 1
	map(0x30, 0x33).rw("iop_sio2", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w)); // 0x30 SIO 2
	map(0x34, 0x37).rw("iop_pio", FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));   // 0x34 PIO
	map(0x38, 0x3b).rw(m_fdc, FUNC(fd1797_device::read), FUNC(fd1797_device::write));       // 0x38 FDC
	map(0x3c, 0x3f).rw("iop_dma", FUNC(z80dma_device::read), FUNC(z80dma_device::write));       // 0x3C DMA
	map(0x40, 0x40).noprw();                                    // 0x40 DMA - Clear carrier sense and parity error bit
	map(0x80, 0x9f).rw("iop_rtc", FUNC(mm58167_device::read), FUNC(mm58167_device::write));     // 0x80 RTC - Counter - thousandths of seconds
													// 0x60 586T Generate MULTIBUS interrupt
}

void altos586_state::iop_attn_w(offs_t offset, u16 data)
{
	m_iop->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
	m_iop->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
}

u8 altos586_state::hostram_r(offs_t offset)
{
	return m_mmu_mem.read_byte((m_hiaddr << 15) | (offset & 0x7fff));
}

void altos586_state::hostram_w(offs_t offset, u8 data)
{
	m_mmu_mem.write_byte((m_hiaddr << 15) | (offset & 0x7fff), data);
}

void altos586_state::hiaddr_w(u8 data)
{
	m_hiaddr = data;
}

u8 altos586_state::pio_pa_r(offs_t offset)
{
	u8 data = 0x00;

	data |= 0 << 0; // Positions 2-4 in E1 jumper (present on my machine, pulling to GND)
	data |= 1 << 1; // Positions 1-2 in E1 jumper (absent, pull-up resistor)
	data |= 1 << 2; // Positions 7-8 in E1 jumper (absent, pull-up resistor)
	data |= 0 << 3; // Pulled to GND
	data |= 1 << 4; // Pin 18 of 74LS74 at position G9
	data |= 0 << 5; // TODO: pin 22 (IR4) of 8259 PIC. Remember what we set it to.
	data |= 1 << 6; // Pin 5 of 74LS38 at position H9
	data |= m_fdc->intrq_r() << 7;
	return data;
}

void altos586_state::pio_pa_w(offs_t offset, u8 data)
{
	// TODO: Is there an interrerrupt ack pin somewhere? PA4 or PA6 perhaps?
	m_pic->ir4_w(BIT(data, 5));
}

void altos586_state::pio_pb_w(offs_t offset, u8 data)
{
	floppy_image_device *floppy;

	if (!BIT(data, 3))
		floppy = m_floppy[0]->get_device();
	else if (!BIT(data, 4))
		floppy = m_floppy[1]->get_device();
	else
		floppy = nullptr;

	if (floppy) {
		floppy->mon_w(0);
		floppy->ss_w(BIT(data, 5));
	}

	m_fdc->set_floppy(floppy);
	m_fdc->dden_w(BIT(data, 6));
}

IRQ_CALLBACK_MEMBER(altos586_state::inta_cb)
{
	m_mmu->set_system_mode();
	return m_pic->acknowledge();
}

void altos586_state::machine_start()
{
	u8 *romdata = memregion("bios")->base();
	int romlen = memregion("bios")->bytes();

	space(AS_PROGRAM).install_ram(0, m_ram->size() - 1, m_ram->pointer());

	// The address lines to the ROMs are reversed
	std::reverse(romdata, romdata + romlen);

	m_mmu->space(AS_PROGRAM).specific(m_mmu_mem);

	save_item(NAME(m_hiaddr));
}

device_memory_interface::space_config_vector altos586_state::memory_space_config() const
{
	// Spaces we provide the MMU
	return space_config_vector {
			std::make_pair(AS_PROGRAM, &m_program_config),
			std::make_pair(AS_IO, &m_io_config) };
}

static void altos586_floppies(device_slot_interface &device)
{
	device.option_add("525hd", FLOPPY_525_HD);
}

static DEVICE_INPUT_DEFAULTS_START(altos586_terminal)
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void altos586_state::mmu_violation()
{
	m_mmu->set_system_mode();
	m_cpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
	m_cpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
}

void altos586_state::altos586(machine_config &config)
{
	ALTOS586_MMU(config, m_mmu, 0, *this);
	m_mmu->set_violation_callback(FUNC(altos586_state::mmu_violation));
	m_mmu->syscall_handler().set(m_pic, FUNC(pic8259_device::ir0_w));

	I8086(config, m_cpu, 30_MHz_XTAL / 3);
	m_cpu->set_addrmap(AS_PROGRAM, &altos586_state::cpu_mem);
	m_cpu->set_addrmap(AS_IO, &altos586_state::cpu_io);
	m_cpu->set_irq_acknowledge_callback(FUNC(altos586_state::inta_cb));
	m_cpu->if_handler().set(m_mmu, FUNC(altos586_mmu_device::cpu_if_w));

	RAM(config, RAM_TAG).set_default_size("512K").set_extra_options("1M");

	PIC8259(config, m_pic);
	m_pic->out_int_callback().set_inputline(m_cpu, INPUT_LINE_IRQ0);

	PIT8254(config, m_pit);
	m_pit->set_clk<0>(30_MHz_XTAL/6);
	m_pit->out_handler<0>().set("iop_sio2", FUNC(z80sio_device::rxcb_w));
	m_pit->out_handler<0>().append("iop_sio2", FUNC(z80sio_device::txcb_w));
	m_pit->set_clk<1>(30_MHz_XTAL/6);
	m_pit->set_clk<2>(62'500);
	m_pit->out_handler<2>().set(m_pic, FUNC(pic8259_device::ir1_w));

	Z80(config, m_iop, 8_MHz_XTAL / 2);
	m_iop->set_addrmap(AS_PROGRAM, &altos586_state::iop_mem);
	m_iop->set_addrmap(AS_IO, &altos586_state::iop_io);

	pit8254_device &pit0(PIT8254(config, "iop_pit0", 0));
	pit0.set_clk<0>(30_MHz_XTAL);
	pit0.out_handler<0>().set("iop_sio0", FUNC(z80sio_device::rxca_w));
	pit0.out_handler<0>().append("iop_sio0", FUNC(z80sio_device::txca_w));
	pit0.set_clk<1>(30_MHz_XTAL/6);
	pit0.out_handler<1>().set("iop_sio0", FUNC(z80sio_device::rxcb_w));
	pit0.out_handler<1>().append("iop_sio0", FUNC(z80sio_device::txcb_w));
	pit0.set_clk<2>(30_MHz_XTAL/6);
	pit0.out_handler<2>().set("iop_sio1", FUNC(z80sio_device::rxca_w));
	pit0.out_handler<2>().append("iop_sio1", FUNC(z80sio_device::txca_w));

	pit8254_device &pit1(PIT8254(config, "iop_pit1", 0));
	pit1.set_clk<0>(30_MHz_XTAL/6);
	pit1.out_handler<0>().set("iop_sio1", FUNC(z80sio_device::rxcb_w));
	pit1.out_handler<0>().append("iop_sio1", FUNC(z80sio_device::txcb_w));
	pit1.set_clk<1>(30_MHz_XTAL/6);
	pit1.out_handler<1>().set("iop_sio2", FUNC(z80sio_device::rxca_w));
	pit1.out_handler<1>().append("iop_sio2", FUNC(z80sio_device::txca_w));

	z80sio_device &sio0(Z80SIO(config, "iop_sio0", 8_MHz_XTAL / 2));
	sio0.out_txda_callback().set("rs232_port3", FUNC(rs232_port_device::write_txd));
	sio0.out_dtra_callback().set("rs232_port3", FUNC(rs232_port_device::write_dtr));
	sio0.out_rtsa_callback().set("rs232_port3", FUNC(rs232_port_device::write_rts));
	sio0.out_txdb_callback().set("rs232_port4", FUNC(rs232_port_device::write_txd));
	sio0.out_dtrb_callback().set("rs232_port4", FUNC(rs232_port_device::write_dtr));
	sio0.out_rtsb_callback().set("rs232_port4", FUNC(rs232_port_device::write_rts));
	sio0.out_int_callback().set_inputline(m_iop, INPUT_LINE_IRQ0);
	sio0.set_cputag(m_iop);

	rs232_port_device &rs232_port3(RS232_PORT(config, "rs232_port3", default_rs232_devices, nullptr));
	rs232_port3.rxd_handler().set("iop_sio0", FUNC(z80sio_device::rxa_w));
	rs232_port3.dcd_handler().set("iop_sio0", FUNC(z80sio_device::dcda_w));
	rs232_port3.cts_handler().set("iop_sio0", FUNC(z80sio_device::ctsa_w));

	rs232_port_device &rs232_port4(RS232_PORT(config, "rs232_port4", default_rs232_devices, nullptr));
	rs232_port4.rxd_handler().set("iop_sio0", FUNC(z80sio_device::rxb_w));
	rs232_port4.dcd_handler().set("iop_sio0", FUNC(z80sio_device::dcdb_w));
	rs232_port4.cts_handler().set("iop_sio0", FUNC(z80sio_device::ctsb_w));

	z80sio_device &sio1(Z80SIO(config, "iop_sio1", 8_MHz_XTAL / 2));
	sio1.out_txda_callback().set("rs232_port1", FUNC(rs232_port_device::write_txd));
	sio1.out_dtra_callback().set("rs232_port1", FUNC(rs232_port_device::write_dtr));
	sio1.out_rtsa_callback().set("rs232_port1", FUNC(rs232_port_device::write_rts));
	sio1.out_txdb_callback().set("rs232_port2", FUNC(rs232_port_device::write_txd));
	sio1.out_dtrb_callback().set("rs232_port2", FUNC(rs232_port_device::write_dtr));
	sio1.out_rtsb_callback().set("rs232_port2", FUNC(rs232_port_device::write_rts));
	sio1.out_int_callback().set_inputline(m_iop, INPUT_LINE_IRQ0);
	sio1.set_cputag(m_iop);

	rs232_port_device &rs232_port1(RS232_PORT(config, "rs232_port1", default_rs232_devices, "terminal"));
	rs232_port1.rxd_handler().set("iop_sio1", FUNC(z80sio_device::rxa_w));
	rs232_port1.dcd_handler().set("iop_sio1", FUNC(z80sio_device::dcda_w));
	rs232_port1.cts_handler().set("iop_sio1", FUNC(z80sio_device::ctsa_w));
	rs232_port1.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(altos586_terminal));

	rs232_port_device &rs232_port2(RS232_PORT(config, "rs232_port2", default_rs232_devices, nullptr));
	rs232_port2.rxd_handler().set("iop_sio1", FUNC(z80sio_device::rxb_w));
	rs232_port2.dcd_handler().set("iop_sio1", FUNC(z80sio_device::dcdb_w));
	rs232_port2.cts_handler().set("iop_sio1", FUNC(z80sio_device::ctsb_w));

	z80sio_device &sio2(Z80SIO(config, "iop_sio2", 8_MHz_XTAL/2));
	sio2.out_txda_callback().set("rs232_port5", FUNC(rs232_port_device::write_txd));
	sio2.out_dtra_callback().set("rs232_port5", FUNC(rs232_port_device::write_dtr));
	sio2.out_rtsa_callback().set("rs232_port5", FUNC(rs232_port_device::write_rts));
	sio2.out_txdb_callback().set("rs232_port6", FUNC(rs232_port_device::write_txd));
	sio2.out_dtrb_callback().set("rs232_port6", FUNC(rs232_port_device::write_dtr));
	sio2.out_rtsb_callback().set("rs232_port6", FUNC(rs232_port_device::write_rts));
	sio2.set_cputag(m_iop);

	rs232_port_device &rs232_port5(RS232_PORT(config, "rs232_port5", default_rs232_devices, nullptr));
	rs232_port5.rxd_handler().set("iop_sio2", FUNC(z80sio_device::rxa_w));
	rs232_port5.dcd_handler().set("iop_sio2", FUNC(z80sio_device::dcda_w));
	rs232_port5.cts_handler().set("iop_sio2", FUNC(z80sio_device::ctsa_w));

	rs232_port_device &rs232_port6(RS232_PORT(config, "rs232_port6", default_rs232_devices, nullptr));
	rs232_port6.rxd_handler().set("iop_sio2", FUNC(z80sio_device::rxb_w));
	rs232_port6.dcd_handler().set("iop_sio2", FUNC(z80sio_device::dcdb_w));
	rs232_port6.cts_handler().set("iop_sio2", FUNC(z80sio_device::ctsb_w));

	z80pio_device &pio(Z80PIO(config, "iop_pio", 8_MHz_XTAL / 2));
	pio.in_pa_callback().set(FUNC(altos586_state::pio_pa_r));
	pio.out_pa_callback().set(FUNC(altos586_state::pio_pa_w));
	pio.out_pb_callback().set(FUNC(altos586_state::pio_pb_w));

	FD1797(config, m_fdc, 1'000'000); // TODO: Check clock
	m_fdc->drq_wr_callback().set("iop_dma", FUNC(z80dma_device::rdy_w)).invert();
	FLOPPY_CONNECTOR(config, m_floppy[0], altos586_floppies, "525hd", floppy_image_device::default_mfm_floppy_formats); // TODO: Sound?
	FLOPPY_CONNECTOR(config, m_floppy[1], altos586_floppies, "525hd", floppy_image_device::default_mfm_floppy_formats);

	z80dma_device &dma(Z80DMA(config, "iop_dma", 8_MHz_XTAL / 2));
	dma.in_mreq_callback().set([this] (offs_t offset) { return m_iop->space(AS_PROGRAM).read_byte(offset); });
	dma.out_mreq_callback().set([this] (offs_t offset, u8 data) { m_iop->space(AS_PROGRAM).write_byte(offset, data); });
	dma.in_iorq_callback().set([this] (offs_t offset) { return m_iop->space(AS_IO).read_byte(offset); });
	dma.out_iorq_callback().set([this] (offs_t offset, u8 data) { m_iop->space(AS_IO).write_byte(offset, data); });

	// TODO: The RTC seems to run approx. 2 times slower. Why?
	MM58167(config, "iop_rtc", 32.768_kHz_XTAL);

	// TODO: Could a multibus-like bus interface be implemented?
	// This sits on a such bus, but the "backplane" are two 50-pin
	// ribbon cables w/o a fixed number of slots.
	ALTOS586_HDC(config, "hdc", 30_MHz_XTAL / 3, m_mmu);

	SOFTWARE_LIST(config, "flop_list").set_original("altos586");
}

ROM_START(altos586)
	ROM_REGION16_LE(0x4000, "bios", 0)
	ROMX_LOAD("altos586-v13-g1.rom", 0x0001, 0x1000, CRC(29fdcb40) SHA1(34700603d6034f0ccc599d74432cef332459987b) , ROM_SKIP(1))
	ROMX_LOAD("altos586-v13-g2.rom", 0x0000, 0x1000, CRC(de22003a) SHA1(ce25a45cd4fb0ff63e5064fb74347b978c3a78f1) , ROM_SKIP(1))
	ROM_COPY("bios", 0, 0x2000, 0x2000) // v1.3 Firmware is 2x4K, but the board accepts 2x8K

	ROM_REGION(0x2000, "iop", 0)
	ROM_LOAD("altos586-v56-iop.rom", 0x0000, 0x2000, CRC(411ca183) SHA1(f2af03d361dddbf6aae055e69210c994ead281d8))
ROM_END

} // anonymous namespace

COMP( 1984, altos586, 0, 0, altos586, 0, altos586_state, empty_init, "Altos Computer Systems", "ACS586", MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING)



altos8600.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Carl
// 20MB HDD image CHS 512,5,17

#include "emu.h"
#include "cpu/i86/i86.h"
#include "cpu/i8089/i8089.h"
#include "machine/ram.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/i8255.h"
#include "machine/z80sio.h"
#include "machine/wd_fdc.h"
#include "acs8600_ics.h"
#include "imagedev/floppy.h"
#include "imagedev/harddriv.h"
#include "bus/rs232/rs232.h"


namespace {

class altos8600_state : public driver_device
{
public:
	altos8600_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_dmac(*this, "dmac"),
		m_pic(*this, "pic8259%u", 1U),
		m_uart8274(*this, "uart8274"),
		m_fdc(*this, "fd1797"),
		m_ram(*this, RAM_TAG),
		m_ics(*this, "ics"),
		m_hdd(*this, "hdd"),
		m_bios(*this, "bios")
	{}

	void altos8600(machine_config &config);

private:
	uint16_t cpuram_r(offs_t offset, u16 mem_mask = ~0);
	void cpuram_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	uint16_t stkram_r(offs_t offset, u16 mem_mask = ~0);
	void stkram_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	uint16_t coderam_r(offs_t offset, u16 mem_mask = ~0);
	void coderam_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	uint16_t xtraram_r(offs_t offset, u16 mem_mask = ~0);
	void xtraram_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	uint16_t cpuio_r(offs_t offset, u16 mem_mask = ~0);
	void cpuio_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	uint16_t nmi_r(offs_t offset, u16 mem_mask = ~0);
	void nmi_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	uint16_t dmacram_r(offs_t offset, u16 mem_mask = ~0);
	void dmacram_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	u16 mmuaddr_r(offs_t offset);
	void mmuaddr_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	u16 mmuflags_r(offs_t offset);
	void mmuflags_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	u8 get_slave_ack(offs_t offset);
	u16 fault_r();
	u16 errlo_r();
	u16 errhi_r();
	void clear_w(u16 data);
	void cattn_w(offs_t offset, u8 data);
	u8 hd_r(offs_t offset);
	void hd_w(offs_t offset, u8 data);
	u8 romport_r(offs_t offset);
	void romport_w(offs_t offset, u8 data);
	void clrsys_w(u8 data);
	void mode_w(u16 data);
	void cpuif_w(int state);
	void fddrq_w(int state);
	void sintr1_w(int state);
	void ics_attn_w(offs_t offset, u8 data);
	IRQ_CALLBACK_MEMBER(inta);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	void code_mem(address_map &map) ATTR_COLD;
	void cpu_io(address_map &map) ATTR_COLD;
	void cpu_mem(address_map &map) ATTR_COLD;
	void dmac_io(address_map &map) ATTR_COLD;
	void dmac_mem(address_map &map) ATTR_COLD;
	void extra_mem(address_map &map) ATTR_COLD;
	void stack_mem(address_map &map) ATTR_COLD;

	u16 xlate_r(offs_t offset, u16 mem_mask, int permbit);
	void xlate_w(offs_t offset, u16 data, u16 mem_mask, int permbit);
	void seterr(offs_t offset, u16 mem_mask, u16 err_mask);
	bool find_sector();
	bool write_sector(u8 data);
	u8 read_sector();
	void format_sector();
	required_device<i8086_cpu_device> m_maincpu;
	required_device<i8089_device> m_dmac;
	required_device_array<pic8259_device, 3> m_pic;
	required_device<i8274_device> m_uart8274;
	required_device<fd1797_device> m_fdc;
	required_device<ram_device> m_ram;
	required_device<acs8600_ics_device> m_ics;
	required_device<harddisk_image_device> m_hdd;
	required_memory_region m_bios;
	u8 m_mmuaddr[256], m_romport[4], m_dmamplex;
	u16 m_mmuflags[256], m_mmuerr, m_mode, m_mmueaddr[2];
	bool m_cpuif, m_user, m_nmiinh, m_nmistat;
	u32 m_lba;
	u16 m_head, m_sect, m_cyl, m_curcyl;
	int m_secoff;
	u8 m_cmd, m_stat;
	bool m_cylhi, m_sechi;
	const struct hard_disk_file::info* m_geom;
	u8 m_sector[512];
};

void altos8600_state::machine_start()
{
	m_mode = 0;
	save_item(NAME(m_mmuaddr));
	save_item(NAME(m_romport));
	save_item(NAME(m_dmamplex));
	save_item(NAME(m_mmuflags));
	save_item(NAME(m_mmuerr));
	save_item(NAME(m_mode));
	save_item(NAME(m_mmueaddr));
	save_item(NAME(m_cpuif));
	save_item(NAME(m_user));
	save_item(NAME(m_nmiinh));
	save_item(NAME(m_nmistat));
	save_item(NAME(m_lba));
	save_item(NAME(m_head));
	save_item(NAME(m_sect));
	save_item(NAME(m_cyl));
	save_item(NAME(m_curcyl));
	save_item(NAME(m_secoff));
	save_item(NAME(m_cmd));
	save_item(NAME(m_stat));
	save_item(NAME(m_cylhi));
	save_item(NAME(m_sechi));
	save_item(NAME(m_sector));

	if(m_hdd->exists())
		m_geom = &m_hdd->get_info();
	else
		m_geom = nullptr;
}

void altos8600_state::machine_reset()
{
	m_mode = (m_mode & 0x10) | 2;
	m_romport[0] = 0x80;
	m_cpuif = false;
	m_user = false;
	m_nmiinh = true;
	m_nmistat = false;
	m_cylhi = m_sechi = false;
	m_stat = 0;
	if(m_hdd->exists())
		m_geom = &m_hdd->get_info();
	else
		m_geom = nullptr;
}

bool altos8600_state::find_sector()
{
	u8 head = m_head >> 4;

	if(!m_geom)
		return false;

	if(m_cyl != m_curcyl)
		return false;

	if(m_curcyl > m_geom->cylinders)
		return false;

	if(head > m_geom->heads)
		return false;

	if(m_sect > m_geom->sectors)
		return false;

	m_lba = (m_cyl * m_geom->heads + head) * m_geom->sectors + m_sect;
	return true;
}

u8 altos8600_state::read_sector()
{
	int secoff = m_secoff;
	m_secoff++;
	if(m_cmd == 1)
	{
		switch(secoff)
		{
			case 0:
				return m_curcyl;
			case 1:
				return (m_head & 0xf0) | (m_curcyl >> 8);
			case 2:
				return m_sect;
		}
		secoff -= 3;
	}
	if(!secoff)
		m_hdd->read(m_lba, m_sector);
	if(secoff >= 511)
	{
		m_dmac->drq1_w(CLEAR_LINE);
		m_pic[1]->ir0_w(ASSERT_LINE);
		m_stat &= ~1;
		m_stat |= 2;
	}
	if(secoff >= 512)
		return 0;
	return m_sector[secoff];
}

bool altos8600_state::write_sector(u8 data)
{
	if(m_secoff >= 512)
		return true;
	m_sector[m_secoff++] = data;
	if(m_secoff == 512)
	{
		m_stat &= ~1;
		m_stat |= 2;
		m_hdd->write(m_lba, m_sector);
		m_dmac->drq1_w(CLEAR_LINE);
		m_pic[1]->ir0_w(ASSERT_LINE);
		return true;
	}
	return false;
}

u8 altos8600_state::hd_r(offs_t offset)
{
	switch(offset)
	{
		case 1:
			if(BIT(m_stat, 0) && (m_cmd & 1))
				return read_sector();
			break;
		case 3:
			m_pic[1]->ir0_w(CLEAR_LINE);
			return m_stat;
	}
	return 0;
}

void altos8600_state::hd_w(offs_t offset, u8 data)
{
	switch(offset)
	{
		case 0:
			m_head = data;
			if((m_head & 3) == 1)
				m_stat |= 0x80;
			else
				m_stat &= ~0x80;
			break;
		case 1:
			if(BIT(m_stat, 0))
			{
				switch(m_cmd)
				{
					case 2:
						write_sector(data);
						break;
					case 4:
						m_secoff++;
						if(m_secoff == 4)
						{
							m_dmac->drq1_w(CLEAR_LINE);
							m_stat &= ~1;
							m_stat |= 2;
						}
						break;
				}
				break;
			}
			if(m_sechi)
			{
				m_sechi = false;
				m_sect |= (data & 7) << 8;
			}
			else
			{
				m_sechi = true;
				m_sect = data;
			}
			break;
		case 2:
			if(m_cylhi)
			{
				m_cylhi = false;
				m_cyl |= (data & 7) << 8;
			}
			else
			{
				m_cylhi = true;
				m_cyl = data;
			}
			break;
		case 3:
			m_cmd = data;
			m_cylhi = false;
			m_sechi = false;
			m_dmac->drq1_w(CLEAR_LINE);
			if(!BIT(m_stat, 7))
				break;
			m_stat &= 0x80;
			switch(m_cmd)
			{
				case 0x10:
					m_curcyl = m_cyl;
					m_stat |= 2;
					break;
				case 0x20:
					m_curcyl = 0;
					m_stat |= 2;
					break;
				case 0x1:
				case 0x2:
				case 0x9:
					if(!find_sector())
					{
						m_stat |= 0xa;
						break;
					}
					[[fallthrough]];
				case 0x4:
					m_secoff = 0;
					m_stat |= 1;
					m_dmac->drq1_w(ASSERT_LINE);
					break;
			}
			m_pic[1]->ir0_w(ASSERT_LINE);
			break;
	}
}

void altos8600_state::cpuif_w(int state)
{
	if(m_user)
	{
		seterr(0, 0, 1);
		return;
	}
	m_cpuif = state ? true : false;
	if(state && BIT(m_mode, 0))
		m_user = true;
}

void altos8600_state::fddrq_w(int state)
{
	if(!m_dmamplex)
		m_dmac->drq2_w(state);
}

void altos8600_state::sintr1_w(int state)
{
	if(state)
	{
		m_dmac->drq1_w(CLEAR_LINE);
		m_pic[1]->ir0_w(ASSERT_LINE);
		m_pic[1]->ir3_w(ASSERT_LINE);
		m_stat &= ~1;
		m_stat |= 2;
	}
	else
		m_pic[1]->ir3_w(CLEAR_LINE);
}

u16 altos8600_state::fault_r()
{
	return m_mmuerr;
}

u16 altos8600_state::errlo_r()
{
	return m_mmueaddr[0];
}

u16 altos8600_state::errhi_r()
{
	return m_mmueaddr[1];
}

void altos8600_state::clear_w(u16 data)
{
	m_mmuerr = 0xffff;
	m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
	m_nmistat = false;
}

u16 altos8600_state::mmuaddr_r(offs_t offset)
{
	return m_mmuaddr[offset & 0xff];
}

void altos8600_state::mmuaddr_w(offs_t offset, u16 data, u16 mem_mask)
{
	if(mem_mask & 0xff)
		m_mmuaddr[offset & 0xff] = data & 0xff;
}

u16 altos8600_state::mmuflags_r(offs_t offset)
{
	return m_mmuflags[offset & 0xff] | (m_user ? 1 : 0) | (m_mode & 2);
}

void altos8600_state::mmuflags_w(offs_t offset, u16 data, u16 mem_mask)
{
	data &= ~0x17;
	if(mem_mask == 0xff)
		data |= 0xff00;
	else if(mem_mask == 0xff00)
		return;
	m_mmuflags[offset & 0xff] = data;
}

void altos8600_state::cattn_w(offs_t offset, u8 data)
{
	m_dmac->sel_w(offset & 1 ? ASSERT_LINE : CLEAR_LINE);
	m_dmac->ca_w(ASSERT_LINE);
	m_dmac->ca_w(CLEAR_LINE);
}

uint8_t altos8600_state::romport_r(offs_t offset)
{
	if(offset & 1)
		return m_romport[offset >> 1];
	return 0;
}

void altos8600_state::romport_w(offs_t offset, u8 data)
{
	const char *fdc = nullptr;
	switch(offset)
	{
		case 1:
			//m_romport[0] = data;
			break;
		case 3:
			m_romport[1] = data;
			if(!BIT(data, 0))
				fdc = "0";
			else if(!BIT(data, 1))
				fdc = "1";
			else if(!BIT(data, 2))
				fdc = "2";
			else if(!BIT(data, 3))
				fdc = "3";

			if(fdc != nullptr)
				m_fdc->set_floppy(m_fdc->subdevice<floppy_connector>(fdc)->get_device());
			else
				m_fdc->set_floppy(nullptr);

			if(m_nmistat && m_nmiinh && !BIT(data, 4))
			{
				m_mode &= ~1;
				m_user = false;
				m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
			}
			else if(BIT(data, 4) && m_nmistat)
				m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
			m_nmiinh = BIT(data, 4) ? true : false;
			break;
		case 5:
			m_romport[2] = data;
			m_fdc->mr_w(!BIT(data, 3));
			m_fdc->dden_w(!BIT(data, 2));
			m_dmamplex = (data >> 4) & 3;
			break;
		case 7:
			m_romport[3] = data;
			break;
	}
}

void altos8600_state::clrsys_w(u8 data)
{
	m_pic[0]->ir0_w(CLEAR_LINE);
}

void altos8600_state::ics_attn_w(offs_t offset, u8 data)
{
	if(!offset)
	{
		m_ics->attn_w(ASSERT_LINE);
		m_ics->attn_w(CLEAR_LINE);
	}
}

void altos8600_state::mode_w(u16 data)
{
	m_mode = data;
	if(m_cpuif && BIT(m_mode, 0))
		m_user = true;
}

u8 altos8600_state::get_slave_ack(offs_t offset)
{
	if(offset == 2)
		return m_pic[1]->acknowledge();
	else if(offset == 3)
		return m_pic[2]->acknowledge();
	return 0x00;
}

void altos8600_state::seterr(offs_t offset, u16 mem_mask, u16 err_mask)
{
	if(machine().side_effects_disabled())
		return;
	logerror("Fault at %05x type %04x\n", offset << 1, err_mask);
	if(!m_nmiinh)
	{
		m_mode &= ~1;
		m_user = false;
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
	}
	m_nmistat = true;
	m_mmuerr &= ~err_mask;
	m_mmueaddr[0] = (offset << 1) | (!(mem_mask & 0xff) ? 1 : 0);
	m_mmueaddr[1] = (m_user ? 0x100 : 0) | (m_mode & 2 ? 0x200 : 0) | ((offset >> 3) & 0xf000);
}

u16 altos8600_state::xlate_r(offs_t offset, u16 mem_mask, int permbit)
{
	u8 page = m_mmuaddr[offset >> 11];
	u16 flags = m_mmuflags[offset >> 11];
	if((offset >= 0x7f000) && BIT(m_mode, 1))
		return m_bios->as_u16(offset & 0xfff);
	if(m_user && !BIT(flags, 11))
		seterr(offset, mem_mask, 0x800);
	else if(m_user && !BIT(flags, permbit))
		seterr(offset, mem_mask, 1 << permbit);
	return ((u16 *)(m_ram->pointer()))[(page << 11) | (offset & 0x7ff)];

}

void altos8600_state::xlate_w(offs_t offset, u16 data, u16 mem_mask, int permbit)
{
	u8 page = m_mmuaddr[offset >> 11];
	u16 flags = m_mmuflags[offset >> 11];
	if(m_user && !BIT(flags, 7))
	{
		seterr(offset, mem_mask, 0x80);
		return;
	}
	else if(!m_user && !BIT(flags, 8))
	{
		seterr(offset, mem_mask, 0x10);
		return;
	}
	else if(m_user && !BIT(flags, permbit))
		seterr(offset, mem_mask, 1 << permbit);
	else if(m_user && BIT(flags, 3) && ((offset & 0x7ff) < 64))
		seterr(offset, mem_mask, 8);
	COMBINE_DATA(&((u16 *)(m_ram->pointer()))[(page << 11) | (offset & 0x7ff)]);
	m_mmuflags[offset >> 11] |= 4;
}

u16 altos8600_state::cpuram_r(offs_t offset, u16 mem_mask)
{
	return xlate_r(offset, mem_mask, 14);
}

void altos8600_state::cpuram_w(offs_t offset, u16 data, u16 mem_mask)
{
	xlate_w(offset, data, mem_mask, 14);
}

u16 altos8600_state::stkram_r(offs_t offset, u16 mem_mask)
{
	return xlate_r(offset, mem_mask, 13);
}

void altos8600_state::stkram_w(offs_t offset, u16 data, u16 mem_mask)
{
	xlate_w(offset, data, mem_mask, 13);
}

u16 altos8600_state::coderam_r(offs_t offset, u16 mem_mask)
{
	return xlate_r(offset, mem_mask, 15);
}

void altos8600_state::coderam_w(offs_t offset, u16 data, u16 mem_mask)
{
	xlate_w(offset, data, mem_mask, 15);
}

u16 altos8600_state::xtraram_r(offs_t offset, u16 mem_mask)
{
	return xlate_r(offset, mem_mask, 12);
}

void altos8600_state::xtraram_w(offs_t offset, u16 data, u16 mem_mask)
{
	xlate_w(offset, data, mem_mask, 12);
}

u16 altos8600_state::cpuio_r(offs_t offset, u16 mem_mask)
{
	if(m_user && !machine().side_effects_disabled())
	{
		m_pic[0]->ir0_w(ASSERT_LINE);
		return 0;
	}
	return m_dmac->space(AS_IO).read_word_unaligned(offset << 1, mem_mask);
}

void altos8600_state::cpuio_w(offs_t offset, u16 data, u16 mem_mask)
{
	if(m_user && !machine().side_effects_disabled())
	{
		m_pic[0]->ir0_w(ASSERT_LINE);
		return;
	}
	m_dmac->space(AS_IO).write_word_unaligned(offset << 1, data, mem_mask);
}

u16 altos8600_state::dmacram_r(offs_t offset, u16 mem_mask)
{
	u8 page = m_mmuaddr[offset >> 11];
	u16 flags = m_mmuflags[offset >> 11];
	if((offset >= 0x7f000) && BIT(m_mode, 1))
		return m_bios->as_u16(offset & 0xfff);
	if(!BIT(flags, 10))
		seterr(offset, mem_mask, 0x400);
	return ((u16 *)(m_ram->pointer()))[(page << 11) | (offset & 0x7ff)];

}

void altos8600_state::dmacram_w(offs_t offset, u16 data, u16 mem_mask)
{
	u8 page = m_mmuaddr[offset >> 11];
	u16 flags = m_mmuflags[offset >> 11];
	if(!BIT(flags, 6))
	{
		seterr(offset, mem_mask, 0x40);
		return;
	}
	COMBINE_DATA(&((u16 *)(m_ram->pointer()))[(page << 11) | (offset & 0x7ff)]);
	m_mmuflags[offset >> 11] |= 4;
}

u16 altos8600_state::nmi_r(offs_t offset, u16 mem_mask)
{
	seterr(offset, mem_mask, 0x100);
	return 0;
}

void altos8600_state::nmi_w(offs_t offset, u16 data, u16 mem_mask)
{
	seterr(offset, mem_mask, 0x100);
}

IRQ_CALLBACK_MEMBER(altos8600_state::inta)
{
	m_user = false;
	m_mode &= ~1;
	return m_pic[0]->acknowledge();
}

void altos8600_state::cpu_mem(address_map &map)
{
	map(0x00000, 0xfffff).rw(FUNC(altos8600_state::cpuram_r), FUNC(altos8600_state::cpuram_w));
}

void altos8600_state::stack_mem(address_map &map)
{
	map(0x00000, 0xfffff).rw(FUNC(altos8600_state::stkram_r), FUNC(altos8600_state::stkram_w));
}

void altos8600_state::code_mem(address_map &map)
{
	map(0x00000, 0xfffff).rw(FUNC(altos8600_state::coderam_r), FUNC(altos8600_state::coderam_w));
}

void altos8600_state::extra_mem(address_map &map)
{
	map(0x00000, 0xfffff).rw(FUNC(altos8600_state::xtraram_r), FUNC(altos8600_state::xtraram_w));
}

void altos8600_state::cpu_io(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(altos8600_state::cpuio_r), FUNC(altos8600_state::cpuio_w));
}

void altos8600_state::dmac_mem(address_map &map)
{
	map(0x00000, 0xfffff).rw(FUNC(altos8600_state::dmacram_r), FUNC(altos8600_state::dmacram_w));
}

void altos8600_state::dmac_io(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(altos8600_state::nmi_r), FUNC(altos8600_state::nmi_w));
	map(0x0000, 0x0007).r(FUNC(altos8600_state::fault_r));
	map(0x0008, 0x000f).w(FUNC(altos8600_state::clear_w));
	map(0x0010, 0x0017).r(FUNC(altos8600_state::errlo_r));
	map(0x0018, 0x001f).r(FUNC(altos8600_state::errhi_r));
	map(0x0020, 0x0027).rw(FUNC(altos8600_state::hd_r), FUNC(altos8600_state::hd_w)).umask16(0x00ff);
	map(0x0030, 0x0037).w(FUNC(altos8600_state::mode_w));
	map(0x0038, 0x003f).w(FUNC(altos8600_state::cattn_w));
	map(0x0040, 0x0047).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
	map(0x0040, 0x0047).rw(m_fdc, FUNC(fd1797_device::read), FUNC(fd1797_device::write)).umask16(0xff00);
	map(0x0048, 0x004f).rw(m_uart8274, FUNC(i8274_device::cd_ba_r), FUNC(i8274_device::cd_ba_w)).umask16(0x00ff);
	map(0x0048, 0x004f).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0xff00);
	map(0x0050, 0x0057).rw(FUNC(altos8600_state::romport_r), FUNC(altos8600_state::romport_w));
	map(0x0058, 0x005f).rw(m_pic[0], FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x0058, 0x005f).w(FUNC(altos8600_state::clrsys_w)).umask16(0xff00);
	map(0x0060, 0x0067).rw(m_pic[1], FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x0068, 0x006f).rw(m_pic[2], FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x0070, 0x0077).noprw();
	map(0x0078, 0x0079).w(FUNC(altos8600_state::ics_attn_w));
	map(0x0200, 0x03ff).rw(FUNC(altos8600_state::mmuflags_r), FUNC(altos8600_state::mmuflags_w));
	map(0x0400, 0x05ff).rw(FUNC(altos8600_state::mmuaddr_r), FUNC(altos8600_state::mmuaddr_w));
}

static void altos8600_floppies(device_slot_interface &device)
{
	device.option_add("8dd", FLOPPY_8_DSDD);
}

void altos8600_state::altos8600(machine_config &config)
{
	I8086(config, m_maincpu, 5_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &altos8600_state::cpu_mem);
	m_maincpu->set_addrmap(AS_IO, &altos8600_state::cpu_io);
	m_maincpu->set_addrmap(AS_OPCODES, &altos8600_state::code_mem);
	m_maincpu->set_addrmap(i8086_cpu_device::AS_STACK, &altos8600_state::stack_mem);
	m_maincpu->set_addrmap(i8086_cpu_device::AS_CODE, &altos8600_state::code_mem);
	m_maincpu->set_addrmap(i8086_cpu_device::AS_EXTRA, &altos8600_state::extra_mem);
	m_maincpu->set_irq_acknowledge_callback(FUNC(altos8600_state::inta));
	m_maincpu->if_handler().set(FUNC(altos8600_state::cpuif_w));

	I8089(config, m_dmac, 5_MHz_XTAL);
	m_dmac->set_addrmap(AS_PROGRAM, &altos8600_state::dmac_mem);
	m_dmac->set_addrmap(AS_IO, &altos8600_state::dmac_io);
	m_dmac->set_data_width(16);
	m_dmac->sintr1().set(FUNC(altos8600_state::sintr1_w));
	m_dmac->sintr2().set(m_pic[1], FUNC(pic8259_device::ir4_w));

	PIC8259(config, m_pic[0], 0);
	m_pic[0]->out_int_callback().set_inputline(m_maincpu, 0);
	m_pic[0]->in_sp_callback().set_constant(1);
	m_pic[0]->read_slave_ack_callback().set(FUNC(altos8600_state::get_slave_ack));

	PIC8259(config, m_pic[1], 0);
	m_pic[1]->out_int_callback().set(m_pic[0], FUNC(pic8259_device::ir2_w));
	m_pic[1]->in_sp_callback().set_constant(0);

	PIC8259(config, m_pic[2], 0);
	m_pic[2]->out_int_callback().set(m_pic[0], FUNC(pic8259_device::ir3_w));
	m_pic[2]->in_sp_callback().set_constant(0);

	RAM(config, RAM_TAG).set_default_size("1M");//.set_extra_options("512K");

	I8274(config, m_uart8274, 16_MHz_XTAL/4);
	m_uart8274->out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
	m_uart8274->out_dtra_callback().set("rs232a", FUNC(rs232_port_device::write_dtr));
	m_uart8274->out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts));
	m_uart8274->out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
	m_uart8274->out_dtrb_callback().set("rs232b", FUNC(rs232_port_device::write_dtr));
	m_uart8274->out_rtsb_callback().set("rs232b", FUNC(rs232_port_device::write_rts));
	m_uart8274->out_int_callback().set(m_pic[0], FUNC(pic8259_device::ir7_w));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_uart8274, FUNC(i8274_device::rxa_w));
	rs232a.dcd_handler().set(m_uart8274, FUNC(i8274_device::dcda_w));
	rs232a.cts_handler().set(m_uart8274, FUNC(i8274_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_uart8274, FUNC(i8274_device::rxb_w));
	rs232b.dcd_handler().set(m_uart8274, FUNC(i8274_device::dcdb_w));
	rs232b.cts_handler().set(m_uart8274, FUNC(i8274_device::ctsb_w));

	I8255A(config, "ppi", 0);

	pit8253_device &pit(PIT8253(config, "pit", 0));
	pit.set_clk<0>(1228800);
	pit.out_handler<0>().set(m_uart8274, FUNC(i8274_device::rxca_w));
	pit.out_handler<0>().append(m_uart8274, FUNC(i8274_device::txca_w));
	pit.set_clk<1>(1228800);
	pit.out_handler<1>().set(m_uart8274, FUNC(i8274_device::rxtxcb_w));
	pit.set_clk<2>(1228800);
	pit.out_handler<1>().set(m_pic[0], FUNC(pic8259_device::ir1_w));

	FD1797(config, m_fdc, 2000000);
	m_fdc->intrq_wr_callback().set(m_pic[1], FUNC(pic8259_device::ir1_w));
	m_fdc->drq_wr_callback().set(FUNC(altos8600_state::fddrq_w));
	FLOPPY_CONNECTOR(config, "fd1797:0", altos8600_floppies, "8dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fd1797:1", altos8600_floppies, "8dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fd1797:2", altos8600_floppies, "8dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fd1797:3", altos8600_floppies, "8dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	ACS8600_ICS(config, m_ics, 0);
	m_ics->set_host_space(m_dmac, AS_PROGRAM); // TODO: fixme
	m_ics->irq1_callback().set(m_pic[0], FUNC(pic8259_device::ir5_w));
	m_ics->irq2_callback().set(m_pic[0], FUNC(pic8259_device::ir6_w));

	HARDDISK(config, "hdd", 0);

	SOFTWARE_LIST(config, "flop_list").set_original("altos8600");
}

ROM_START(altos8600)
	ROM_REGION(0x2000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "bios", "bios")
	ROMX_LOAD("11753_1.5_lo.bin", 0x0000, 0x1000, CRC(dfa7bf0e) SHA1(6628fd7c579423b51d2642aeaa7fc0405a989252), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("11753_1.5_hi.bin", 0x0001, 0x1000, CRC(9b5e812c) SHA1(c2ef24859edd48d2096db47e16855c9bc01dae75), ROM_SKIP(1) | ROM_BIOS(0))
ROM_END

} // anonymous namespace


COMP( 1981, altos8600, 0, 0, altos8600, 0, altos8600_state, empty_init, "Altos Computer Systems", "ACS8600", MACHINE_NO_SOUND_HW)



am1000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Alpha Micro AM-1000 system.

    This system was sold in various configurations beginning in 1982.

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

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "cpu/z80/z80.h"
//#include "machine/6840ptm.h"
//#include "machine/6850acia.h"
//#include "machine/com8116.h"
//#include "machine/msm5832.h"


namespace {

class am1000_state : public driver_device
{
public:
	am1000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_subcpu(*this, "subcpu")
		, m_mainprom(*this, "mainprom")
		, m_mainram(*this, "mainram")
	{
	}

	void am1000(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u16 rom_ram_r(offs_t offset, u16 mem_mask);
	void control_w(u8 data);

	void main_map(address_map &map) ATTR_COLD;
	void sub_map(address_map &map) ATTR_COLD;

	required_device<m68000_device> m_maincpu;
	required_device<z80_device> m_subcpu;
	required_region_ptr<u16> m_mainprom;
	required_shared_ptr<u16> m_mainram;

	bool m_ram_enabled = false;
};


void am1000_state::machine_start()
{
	// assumes it can make an address mask from m_mainprom.length() - 1
	assert(!(m_mainprom.length() & (m_mainprom.length() - 1)));

	save_item(NAME(m_ram_enabled));
}

void am1000_state::machine_reset()
{
	m_ram_enabled = false;
}


u16 am1000_state::rom_ram_r(offs_t offset, u16 mem_mask)
{
	if (m_ram_enabled)
		return m_mainram[offset];
	else
		return m_mainprom[offset & (m_mainprom.length() - 1)];
}

void am1000_state::control_w(u8 data)
{
	m_ram_enabled = true;
}


void am1000_state::main_map(address_map &map)
{
	map(0x000000, 0x07ffff).r(FUNC(am1000_state::rom_ram_r)).writeonly().share(m_mainram); // TMM41256P-12 x16
	map(0x800000, 0x801fff).rom().region("mainprom", 0);
	map(0xfffe00, 0xfffe00).w(FUNC(am1000_state::control_w));
}

void am1000_state::sub_map(address_map &map)
{
	map(0x0000, 0x07ff).rom().region("subprom", 0);
	map(0x4000, 0x4fff).ram(); // NEC D4016C-2 x2
}


static INPUT_PORTS_START(am1000)
INPUT_PORTS_END


void am1000_state::am1000(machine_config &config)
{
	M68000(config, m_maincpu, 16_MHz_XTAL / 2); // MK68000P-8B
	m_maincpu->set_addrmap(AS_PROGRAM, &am1000_state::main_map);

	Z80(config, m_subcpu, 16_MHz_XTAL / 4); // NEC D780C-1
	m_subcpu->set_addrmap(AS_PROGRAM, &am1000_state::sub_map);
}


ROM_START(am1000)
	ROM_REGION16_BE(0x2000, "mainprom", 0) // "© COPYRIGHT 1988 ALPHA MICRO" on labels; "COPR. 1985 ALMI" in byte-swapped data
	ROM_LOAD16_BYTE("169-13_c00.u83", 0x0000, 0x1000, CRC(1d9cc8c0) SHA1(fced7bde6dbee4b54050bc7a91fc8eb74a659823))
	ROM_LOAD16_BYTE("169-12_c00.u82", 0x0001, 0x1000, CRC(72bca8ae) SHA1(67d7a13ea2d630af8e6aefeb1a30b2de8cf6012a))
	// Adjacent 24-pin sockets at U100 and U101 are both unpopulated

	ROM_REGION(0x800, "subprom", 0)
	ROM_LOAD("169-05_b00.u162", 0x000, 0x800, CRC(e0f8bb40) SHA1(ac2531994eb90447d320401948af07723e51c8f9))
ROM_END

} // anonymous namespace


COMP(1988, am1000, 0, 0, am1000, am1000, am1000_state, empty_init, "Alpha Micro", "AM-1000", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



amico2k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Fabio Priuli, Curt Coder
/***************************************************************************

    A.S.EL. Amico 2000

    07/2009 Skeleton driver.

    IC9 - Monitor PROM, handwritten from book listing by Davide
    IC10 - Recorder PROM, yet to be found
    IC6/IC7 - PROMs reconstructed by Luigi Serrantoni

    To Do:
     * Basically everything, in particular implement PROM (described in details
       at the link below) and i8255

    http://www.computerhistory.it/index.php?option=com_content&task=view&id=85&Itemid=117

    Pasting:
        0-F : as is
        ^ (inc) : ^
        AD : -
        DA : =
        GO : X

    Test Paste:
        =11^22^33^44^55^66^77^88^99^-0000
        Now press up-arrow to confirm the data has been entered.

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


#include "emu.h"
#include "cpu/m6502/m6502.h"
#include "machine/i8255.h"
#include "utf8.h"

#include "amico2k.lh"


namespace {

class amico2k_state : public driver_device
{
public:
	amico2k_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_digits(*this, "digit%u", 0U)
	{ }

	void amico2k(machine_config &config);

private:
	void machine_start() override ATTR_COLD;

	uint8_t ppi_pa_r();
	void ppi_pa_w(uint8_t data);
	uint8_t ppi_pb_r();
	void ppi_pb_w(uint8_t data);

	// timers
	emu_timer *m_led_refresh_timer = nullptr;
	TIMER_CALLBACK_MEMBER(led_refresh);
	void amico2k_mem(address_map &map) ATTR_COLD;

	int m_ls145_p = 0;
	uint8_t m_segment = 0U;
	required_device<cpu_device> m_maincpu;
	output_finder<6> m_digits;
};


void amico2k_state::amico2k_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x03ff).ram();
//  map(0x0400, 0x07ff).ram(); // optional expansion RAM
	map(0xfb00, 0xfcff).rom();
	map(0xfd00, 0xfd03).rw("i8255", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xfe00, 0xffff).rom();
}

/* Input ports */
static INPUT_PORTS_START( amico2k )
	PORT_START("Q0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Q1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Q2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PC") PORT_CODE(KEYCODE_P)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RUN") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DA") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("AD") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

TIMER_CALLBACK_MEMBER(amico2k_state::led_refresh)
{
	if (m_ls145_p > 3)
	{
		m_digits[m_ls145_p - 4] = m_segment;
	}
}

uint8_t amico2k_state::ppi_pa_r()
{
	/*

	    bit     description

	    PA0     keyboard data 0
	    PA1     keyboard data 1
	    PA2     keyboard data 2
	    PA3     keyboard data 3
	    PA4     keyboard data 4
	    PA5     keyboard data 5
	    PA6     keyboard data 6
	    PA7     reg out

	*/

	switch (m_ls145_p)
	{
	case 0:     return ioport("Q0")->read();
	case 1:     return ioport("Q1")->read();
	case 2:     return ioport("Q2")->read();
	default:    return 0xff;
	}
}

void amico2k_state::ppi_pa_w(uint8_t data)
{
	/*

	    bit     description

	    PA0     LED segment A
	    PA1     LED segment B
	    PA2     LED segment C
	    PA3     LED segment D
	    PA4     LED segment E
	    PA5     LED segment F
	    PA6     LED segment G
	    PA7

	*/

	m_segment = data;
	m_led_refresh_timer->adjust(attotime::from_usec(70));
}

uint8_t amico2k_state::ppi_pb_r()
{
	/*

	    bit     description

	    PB0     reg out
	    PB1
	    PB2
	    PB3
	    PB4
	    PB5
	    PB6
	    PB7

	*/

	return 0;
}

void amico2k_state::ppi_pb_w(uint8_t data)
{
	/*

	    bit     description

	    PB0
	    PB1     LS145 P0
	    PB2     LS145 P1
	    PB3     LS145 P2
	    PB4     LS145 P3
	    PB5     reg in
	    PB6     reg in
	    PB7     led output enable

	*/

	m_ls145_p = (data >> 1) & 0x0f;
}

void amico2k_state::machine_start()
{
	m_digits.resolve();
	m_led_refresh_timer = timer_alloc(FUNC(amico2k_state::led_refresh), this);

	// state saving
	save_item(NAME(m_ls145_p));
	save_item(NAME(m_segment));
}

void amico2k_state::amico2k(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, 1000000); /* 1MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &amico2k_state::amico2k_mem);

	/* video hardware */
	config.set_default_layout(layout_amico2k);

	i8255_device &ppi(I8255(config, "i8255"));
	ppi.in_pa_callback().set(FUNC(amico2k_state::ppi_pa_r));
	ppi.out_pa_callback().set(FUNC(amico2k_state::ppi_pa_w));
	ppi.in_pb_callback().set(FUNC(amico2k_state::ppi_pb_r));
	ppi.out_pb_callback().set(FUNC(amico2k_state::ppi_pb_w));
}


/* ROM definition */
// not sure the ROMs are loaded correctly
ROM_START( amico2k )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "prom.ic10", 0xfb00, 0x200, NO_DUMP )     // cassette recorder ROM, not published anywhere. a board is needed!
	ROM_LOAD( "prom.ic9",  0xfe00, 0x200, CRC(86449f7c) SHA1(fe7deca86e90ab89aae23f11e9dbaf343b4242dc) )

	ROM_REGION( 0x200, "proms", ROMREGION_ERASEFF )
	ROM_LOAD( "prom.ic6",  0x000, 0x100, CRC(4005f760) SHA1(7edcd85feb5a576f6da1bbb723b3cf668cf3df45) )
	ROM_LOAD( "prom.ic7",  0x100, 0x100, CRC(8785d864) SHA1(d169c3b5f5690664083030948db9f33571b08656) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY     FULLNAME      FLAGS
COMP( 1978, amico2k, 0,      0,      amico2k, amico2k, amico2k_state, empty_init, "A.S.E.L.", "Amico 2000", MACHINE_NO_SOUND_HW)



amiga.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Dirk Best
/***************************************************************************

    Commodore Amiga

    Notes:
    - On Kickstart 2.0 onward holding down both port 1 fire buttons
      will bring you to the Amiga Early Startup Control Screen.
      This gives you several diagnostic options, including booting from
      non-DF0 drive, switch video modes and test expansion boards.

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

#include "emu.h"

#include "amiga.h"
#include "gayle.h"

#include "bus/amiga/cpuslot/cpuslot.h"
#include "bus/amiga/keyboard/keyboard.h"
#include "bus/amiga/zorro/zorro.h"
#include "bus/ata/ataintf.h"
#include "bus/pccard/sram.h"
#include "cpu/m68000/m68000.h"
#include "cpu/m6502/m6502.h"
#include "machine/6525tpi.h"
#include "machine/mos6526.h"
#include "machine/dmac.h"
#include "machine/nvram.h"
#include "machine/i2cmem.h"
#include "machine/cr511b.h"
#include "machine/rp5c01.h"

#include "softlist.h"
#include "speaker.h"


//**************************************************************************
//  PRIVATE DEVICES
//**************************************************************************

/*
The keyboard reset/power-on reset circuit for the Amiga 2000 is built
around the LM339 at U805 (top left on sheet 3 of the schematic).  To
simplify things, we assume all components are ideal.

In stead idle state:
* /KBCLK is high
* U805 pin 1 is driven low, holding C813 discharged
* U805 pin 2 is not driven, allowing C814 to remain charged
* U805 pin 13 is driven low setting thresholds to 2.0V and 1.0V
* U805 pin 14 is not driven, leaving /KBRST deasserted

When /KBCLK is asserted, C814 is allowed to charge.  A short pulse will
not give it sufficient time to charge past the 2.0V threshold on pin 5
and the circuit will remain in idle.

If /KBCLK is asserted for over 112ms:
* U805 pin 1 is not driven, allowing C813 to charge past 2.0V
* U805 pin 2 is driven low, discharging C814
* U805 pin 13 is not driven, raising thresholds to 2.86V and 3.57V
* U805 pin 14 is driven low, asserting /KBRST

The thresholds changing will cause U805 pin 2 to float, allowing C814 to
begin charging.  If /KBCLK is asserted for a further 74 milliseconds,
U805 pin 2 will be driven low keeping C814 discharged until /KBCLK is
deasserted.

C814 (22µF) will charge via R805 (47kΩ) until it reaches the 3.57V
threshold, ensuring the minimum length of a reset pulse is 1.294s.

The power-on reset signal is also allowed to discharge C814, but we
ignore this for simplicity.  Earlier boards use a 1N4148 signal diode,
while later boards replace it with a PST518B at XU1.

The equivalent circuit in the Amiga 1000 works similarly, but has
different components:
* 10µF/22kΩ (C104/R62) for timing /KBCLK pulse
* 10µF/100kΩ (C105/R63) for minimum /KBRESET pulse duration
* Thresholds of 2.499/2.501V and 2.474V/2.526V
* BAS32L diode for power-on reset
* This gives delays of 152ms, 176µs, and 704ms

The equivalent circuit in the Amiga CDTV has the same thresholds as the
Amiga 2000, but uses 1kΩ resistors for timing.  This gives delays of
11.2ms, 7.43ms, and 27.5ms.

*/

DECLARE_DEVICE_TYPE(A1000_KBRESET, a1000_kbreset_device)

class a1000_kbreset_device : public device_t
{
public:
	a1000_kbreset_device(machine_config const &config, char const *tag, device_t *owner, u32 clock = 0U) :
		device_t(config, A1000_KBRESET, tag, owner, clock),
		m_kbrst_cb(*this)
	{
	}

	auto kbrst_cb() { return m_kbrst_cb.bind(); }

	a1000_kbreset_device &set_delays(attotime detect, attotime stray, attotime output)
	{
		m_detect_time = detect;
		m_stray_time = stray;
		m_output_time = output;
		return *this;
	}

	void kbclk_w(int state)
	{
		if (bool(state) != bool(m_kbclk))
		{
			m_kbclk = state ? 1U : 0U;
			if (state)
			{
				// U805 pin 1 driven low - discharges C813
				m_c813_level = 0U;
				m_c813_timer->reset();

				// U805 pin 2 floating - allows C814 to charge
				if (!m_c814_charging)
				{
					m_c814_charging = 1U;
					m_c814_timer->adjust(m_output_time); // 0V to 3.57V
				}
			}
			else
			{
				// U805 pin 1 floating - allows C813 to charge
				assert(0U == m_c813_level);
				m_c813_timer->adjust(m_detect_time); // 0V to 2V
			}
		}
	}

protected:
	virtual void device_start() override
	{
		// allocate resources
		m_c813_timer = timer_alloc(FUNC(a1000_kbreset_device::c813_charged), this);
		m_c814_timer = timer_alloc(FUNC(a1000_kbreset_device::c814_charged), this);

		// start in idle state
		m_kbclk = 1U;
		m_kbrst = 1U;
		m_c813_level = 0U;
		m_c814_charging = 1U;

		// always better to save state
		save_item(NAME(m_kbclk));
		save_item(NAME(m_kbrst));
		save_item(NAME(m_c813_level));
		save_item(NAME(m_c814_charging));
	}

private:
	TIMER_CALLBACK_MEMBER(c813_charged)
	{
		assert(2U > m_c813_level);
		if (2U > ++m_c813_level)
			m_c813_timer->adjust(m_stray_time); // 2V to 2.86V

		if ((m_kbrst ? 0U : 1U) < m_c813_level)
		{
			// U805 pin 2 driven low - discharges C814
			if (2U > m_c813_level)
			{
				assert(m_c814_charging);
				m_c814_timer->adjust(m_output_time); // 0V to 3.57V
			}
			else
			{
				m_c814_charging = 0U;
				m_c814_timer->reset();
			}
			if (m_kbrst)
				m_kbrst_cb(m_kbrst = 0U);
		}
	}

	TIMER_CALLBACK_MEMBER(c814_charged)
	{
		// C814 above high threshold - /KBRST deasserted
		assert(m_c814_charging);
		assert(!m_kbrst);
		m_kbrst_cb(m_kbrst = 1U);

		// if C813 is between 2.0V and 2.86V, lowering the threshold will discharge C814
		assert(2U > m_c813_level);
		if (0U < m_c813_level)
		{
			// threshold is bumped back up
			m_kbrst_cb(m_kbrst = 0U);
			m_c814_timer->adjust(m_output_time); // 0V to 3.57V
		}
	}

	attotime m_detect_time = attotime::from_msec(112);
	attotime m_stray_time = attotime::from_msec(74);
	attotime m_output_time = attotime::from_msec(1294);

	devcb_write_line m_kbrst_cb;

	emu_timer *m_c813_timer = nullptr; // C813 = 22µF, R802 = 10kΩ
	emu_timer *m_c814_timer = nullptr; // C814 = 22µF, R805 = 47kΩ

	u8 m_kbclk = 1U; // /KBCLK input
	u8 m_kbrst = 1U; // /KBRST output
	u8 m_c813_level = 0U; // 0 = 0V-2V, 1 = 2V - 2.86V, 2 = 2.86V - 5V
	u8 m_c814_charging = 1U; // U805 pin 2
};

DEFINE_DEVICE_TYPE(A1000_KBRESET, a1000_kbreset_device, "a1000kbrst", "Amiga 1000/2000/CDTV keyboard reset circuit")


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

class a1000_state : public amiga_state
{
public:
	a1000_state(const machine_config &mconfig, device_type type, const char *tag)
		: amiga_state(mconfig, type, tag)
		, m_bootrom(*this, "bootrom")
		, m_wom(*this, "wom")
	{ }

	void init_pal();
	void init_ntsc();

	void write_protect_w(u16 data);

	void a1000(machine_config &config);
	void a1000n(machine_config &config);
	void a1000_bootrom_map(address_map &map) ATTR_COLD;
	void a1000_mem(address_map &map) ATTR_COLD;
	void a1000_overlay_map(address_map &map) ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<address_map_bank_device> m_bootrom;
	required_memory_bank m_wom;
	std::vector<u16> m_wom_ram;
};

class a2000_state : public amiga_state
{
public:
	a2000_state(const machine_config &mconfig, device_type type, const char *tag)
		: amiga_state(mconfig, type, tag)
		, m_rtc(*this, "u65")
		, m_cpuslot(*this, "cpuslot")
		, m_zorro(*this, "zorro2")
		, m_zorro2_int2(0)
		, m_zorro2_int6(0)
	{ }

	void init_pal();
	void init_ntsc();

	void cpuslot_ovr_w(int state);
	void cpuslot_int2_w(int state);
	void cpuslot_int6_w(int state);
	void zorro2_int2_w(int state);
	void zorro2_int6_w(int state);

	u16 clock_r(offs_t offset);
	void clock_w(offs_t offset, u16 data);

	void a2000(machine_config &config);
	void a2000n(machine_config &config);
	void a2000_mem(address_map &map) ATTR_COLD;

protected:
	virtual void machine_reset() override ATTR_COLD;

	// amiga_state overrides
	virtual bool int2_pending() override;
	virtual bool int6_pending() override;

private:
	// devices
	required_device<msm6242_device> m_rtc;
	required_device<amiga_cpuslot_device> m_cpuslot;
	required_device<zorro2_bus_device> m_zorro;

	// internal state
	int m_cpuslot_int2;
	int m_cpuslot_int6;
	int m_zorro2_int2;
	int m_zorro2_int6;
};

class a500_state : public amiga_state
{
public:
	a500_state(const machine_config &mconfig, device_type type, const char *tag)
		: amiga_state(mconfig, type, tag)
		, m_side(*this, "side")
		, m_side_int2(0)
		, m_side_int6(0)
	{ }

	void init_pal();
	void init_ntsc();

	void side_ovr_w(int state);
	void side_int2_w(int state);
	void side_int6_w(int state);

	void a500n(machine_config &config);
	void a500(machine_config &config);
	void a500_mem(address_map &map) ATTR_COLD;

protected:
	virtual void machine_reset() override ATTR_COLD;

	// amiga_state overrides
	virtual bool int2_pending() override;
	virtual bool int6_pending() override;

private:
	// devices
	required_device<amiga_cpuslot_device> m_side;

	// internal state
	int m_side_int2;
	int m_side_int6;
};

class cdtv_state : public amiga_state
{
public:
	cdtv_state(const machine_config &mconfig, device_type type, const char *tag)
		: amiga_state(mconfig, type, tag)
		, m_rtc(*this, "u61")
		, m_dmac(*this, "u36")
		, m_tpi(*this, "u32")
		, m_cdrom(*this, "cdrom")
		, m_dmac_irq(0)
		, m_tpi_irq(0)
	{ }

	void init_pal();
	void init_ntsc();

	u16 clock_r(offs_t offset);
	void clock_w(offs_t offset, u16 data);

	void cdtv(machine_config &config);
	void cdtvn(machine_config &config);
	void cdtv_mem(address_map &map) ATTR_COLD;
	void cdtv_rc_mem(address_map &map) ATTR_COLD;

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// amiga_state overrides
	virtual bool int2_pending() override;
	virtual bool int6_pending() override;

private:
	void dmac_int_w(int state);

	void tpi_portb_w(uint8_t data);
	void tpi_int_w(int state);

	void sten_w(int state);
	void drq_w(int state);

	required_device<msm6242_device> m_rtc;
	required_device<amiga_dmac_rev2_device> m_dmac;
	required_device<tpi6525_device> m_tpi;
	required_device<cr511b_device> m_cdrom;

	// internal state
	int m_dmac_irq;
	int m_tpi_irq;
	bool m_sten;
};

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

	u32 scsi_r(offs_t offset, u32 mem_mask = ~0);
	void scsi_w(offs_t offset, u32 data, u32 mem_mask = ~0);
	u32 motherboard_r(offs_t offset, u32 mem_mask = ~0);
	void motherboard_w(offs_t offset, u32 data, u32 mem_mask = ~0);

	void init_pal();
	void init_ntsc();

	void a3000(machine_config &config);
	void a3000n(machine_config &config);
	void a3000_mem(address_map &map) ATTR_COLD;

protected:

private:
};

class a500p_state : public amiga_state
{
public:
	a500p_state(const machine_config &mconfig, device_type type, const char *tag)
		: amiga_state(mconfig, type, tag)
		, m_side(*this, "side")
		, m_rtc(*this, "u9")
		, m_side_int2(0)
		, m_side_int6(0)
	{ }

	u16 clock_r(offs_t offset);
	void clock_w(offs_t offset, u16 data);

	void init_pal();
	void init_ntsc();

	void side_ovr_w(int state);
	void side_int2_w(int state);
	void side_int6_w(int state);

	void a500pn(machine_config &config);
	void a500p(machine_config &config);
	void a500p_mem(address_map &map) ATTR_COLD;

protected:
	virtual void machine_reset() override ATTR_COLD;

	// amiga_state overrides
	virtual bool int2_pending() override;
	virtual bool int6_pending() override;

private:
	// devices
	required_device<amiga_cpuslot_device> m_side;
	required_device<msm6242_device> m_rtc;

	// internal state
	int m_side_int2;
	int m_side_int6;
};

class a600_state : public amiga_state
{
public:
	a600_state(const machine_config &mconfig, device_type type, const char *tag)
		: amiga_state(mconfig, type, tag)
		, m_gayle(*this, "gayle")
		, m_pcmcia(*this, "pcmcia")
		, m_gayle_int2(0)
	{ }

	void gayle_int2_w(int state);
	void gayle_int6_w(int state);

	void init_pal();
	void init_ntsc();

	static const u8 GAYLE_ID = 0xd0;

	void a600n(machine_config &config);
	void a600(machine_config &config);
	void a600_mem(address_map &map) ATTR_COLD;

protected:
	// amiga_state overrides
	virtual bool int2_pending() override;
	virtual bool int6_pending() override;

private:
	required_device<gayle_device> m_gayle;
	required_device<pccard_slot_device> m_pcmcia;

	int m_gayle_int2;
	int m_gayle_int6;
};

class a1200_state : public amiga_state
{
public:
	a1200_state(const machine_config &mconfig, device_type type, const char *tag)
		: amiga_state(mconfig, type, tag)
		, m_gayle(*this, "gayle")
		, m_pcmcia(*this, "pcmcia")
		, m_gayle_int2(0)
	{ }

	void gayle_int2_w(int state);
	void gayle_int6_w(int state);

	void init_pal();
	void init_ntsc();

	static const u8 GAYLE_ID = 0xd1;

	void a1200(machine_config &config);
	void a1200n(machine_config &config);
	void a1200_mem(address_map &map) ATTR_COLD;

protected:
	// amiga_state overrides
	virtual bool int2_pending() override;
	virtual bool int6_pending() override;

private:
	required_device<gayle_device> m_gayle;
	required_device<pccard_slot_device> m_pcmcia;

	int m_gayle_int2;
	int m_gayle_int6;
};

class a4000_state : public amiga_state
{
public:
	a4000_state(const machine_config &mconfig, device_type type, const char *tag)
		: amiga_state(mconfig, type, tag)
		, m_ata(*this, "ata")
		, m_ramsey_config(0)
		, m_gary_coldboot(1)
		, m_gary_timeout(0)
		, m_gary_toenb(0)
		, m_ide_interrupt(0)
	{ }

	u32 scsi_r(offs_t offset, u32 mem_mask = ~0);
	void scsi_w(offs_t offset, u32 data, u32 mem_mask = ~0);
	u16 ide_r(offs_t offset, u16 mem_mask = ~0);
	void ide_w(offs_t offset, u16 data, u16 mem_mask);
	void ide_interrupt_w(int state);
	u32 motherboard_r(offs_t offset, u32 mem_mask = ~0);
	void motherboard_w(offs_t offset, u32 data, u32 mem_mask = ~0);

	void init_pal();
	void init_ntsc();

	void a400030n(machine_config &config);
	void a4000tn(machine_config &config);
	void a4000t(machine_config &config);
	void a4000n(machine_config &config);
	void a4000(machine_config &config);
	void a400030(machine_config &config);
	void a400030_mem(address_map &map) ATTR_COLD;
	void a4000_mem(address_map &map) ATTR_COLD;
	void a4000t_mem(address_map &map) ATTR_COLD;

protected:

private:
	required_device<ata_interface_device> m_ata;

	int m_ramsey_config;
	int m_gary_coldboot;
	int m_gary_timeout;
	int m_gary_toenb;
	int m_ide_interrupt;
};

class cd32_state : public amiga_state
{
public:
	cd32_state(const machine_config &mconfig, device_type type, const char *tag)
		: amiga_state(mconfig, type, tag)
		, m_player_ports(*this, {"p1_cd32_buttons", "p2_cd32_buttons"})
		, m_cdda(*this, "akiko:cdda")
	{ }

	void akiko_int_w(int state);
	void akiko_cia_0_port_a_write(uint8_t data);

	void handle_joystick_cia(u8 pra, u8 dra);
	u16 handle_joystick_potgor(u16 potgor);

	ioport_value cd32_input();
	template <int P> int cd32_sel_mirror_input();

	void init_pal();
	void init_ntsc();

	required_ioport_array<2> m_player_ports;

	int m_oldstate[2]{};
	int m_cd32_shifter[2]{};
	u16 m_potgo_value = 0;

	void cd32n(machine_config &config);
	void cd32(machine_config &config);
	void cd32_mem(address_map &map) ATTR_COLD;

protected:
	// amiga_state overrides
	virtual void potgo_w(u16 data) override;

private:
	required_device<cdda_device> m_cdda;
};


//**************************************************************************
//  REAL TIME CLOCK
//**************************************************************************

u16 cdtv_state::clock_r(offs_t offset)
{
	return m_rtc->read(offset / 2);
}

void cdtv_state::clock_w(offs_t offset, u16 data)
{
	m_rtc->write(offset / 2, data);
}

u16 a2000_state::clock_r(offs_t offset)
{
	return m_rtc->read(offset / 2);
}

void a2000_state::clock_w(offs_t offset, u16 data)
{
	m_rtc->write(offset / 2, data);
}

u16 a500p_state::clock_r(offs_t offset)
{
	return m_rtc->read(offset / 2);
}

void a500p_state::clock_w(offs_t offset, u16 data)
{
	m_rtc->write(offset / 2, data);
}


//**************************************************************************
//  DRIVER INIT
//**************************************************************************

// ocs chipset (agnus with support for 512k or 1mb chip ram, denise)
void a1000_state::init_pal()
{
	m_agnus_id = AGNUS_PAL;     // 8367
	m_denise_id = DENISE;       // 8362
}

void a1000_state::init_ntsc()
{
	m_agnus_id = AGNUS_NTSC;    // 8361
	m_denise_id = DENISE;       // 8362
}

void a2000_state::init_pal()
{
	m_agnus_id = AGNUS_PAL;     // 8371 (later versions 8372A)
	m_denise_id = DENISE;       // 8362
}

void a2000_state::init_ntsc()
{
	m_agnus_id = AGNUS_NTSC;    // 8370 (later versions 8372A)
	m_denise_id = DENISE;       // 8362
}

void a500_state::init_pal()
{
	m_agnus_id = AGNUS_PAL;     // 8371 (later versions 8372A)
	m_denise_id = DENISE;       // 8362
}

void a500_state::init_ntsc()
{
	m_agnus_id = AGNUS_NTSC;    // 8370 (later versions 8372A)
	m_denise_id = DENISE;       // 8362
}

void cdtv_state::init_pal()
{
	m_agnus_id = AGNUS_HR_PAL;  // 8372A
	m_denise_id = DENISE;       // 8362
}

void cdtv_state::init_ntsc()
{
	m_agnus_id = AGNUS_HR_NTSC; // 8372A
	m_denise_id = DENISE;       // 8362
}

// ecs chipset (agnus with support for 2mb chip ram, super denise)
void a3000_state::init_pal()
{
	m_agnus_id = AGNUS_HR_PAL_NEW;  // 8372B (early versions: 8372AB)
	m_denise_id = DENISE_HR;        // 8373
}

void a3000_state::init_ntsc()
{
	m_agnus_id = AGNUS_HR_NTSC_NEW; // 8372B (early versions: 8372AB)
	m_denise_id = DENISE_HR;        // 8373
}

void a500p_state::init_pal()
{
	m_agnus_id = AGNUS_HR_PAL;  // 8375 (390544-01)
	m_denise_id = DENISE_HR;    // 8373
}

void a500p_state::init_ntsc()
{
	m_agnus_id = AGNUS_HR_NTSC; // 8375 (390544-02)
	m_denise_id = DENISE_HR;    // 8373
}

void a600_state::init_pal()
{
	m_agnus_id = AGNUS_HR_PAL;  // 8375 (390544-01)
	m_denise_id = DENISE_HR;    // 8373
}

void a600_state::init_ntsc()
{
	m_agnus_id = AGNUS_HR_NTSC; // 8375 (390544-02)
	m_denise_id = DENISE_HR;    // 8373
}

// aga chipset (alice and lisa)
void a1200_state::init_pal()
{
	m_agnus_id = ALICE_PAL_NEW;
	m_denise_id = LISA;
}

void a1200_state::init_ntsc()
{
	m_agnus_id = ALICE_NTSC_NEW;
	m_denise_id = LISA;
}

void a4000_state::init_pal()
{
	m_agnus_id = ALICE_PAL_NEW;
	m_denise_id = LISA;
}

void a4000_state::init_ntsc()
{
	m_agnus_id = ALICE_NTSC_NEW;
	m_denise_id = LISA;
}

void cd32_state::init_pal()
{
	m_agnus_id = ALICE_PAL_NEW;
	m_denise_id = LISA;
}

void cd32_state::init_ntsc()
{
	m_agnus_id = ALICE_NTSC_NEW;
	m_denise_id = LISA;
}


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

void a1000_state::machine_start()
{
	// start base machine
	amiga_state::machine_start();

	// allocate 256kb for wom
	m_wom_ram.resize(256 * 1024 / 2);
	m_wom->set_base(&m_wom_ram[0]);
}

void a1000_state::machine_reset()
{
	// base reset
	amiga_state::machine_reset();

	// bootrom visible, wom writable
	m_bootrom->set_bank(0);
	m_maincpu->space(AS_PROGRAM).install_write_bank(0xfc0000, 0xffffff, m_wom);
}

// any write to this area will write protect the wom and disable the bootrom
void a1000_state::write_protect_w(u16 data)
{
	m_bootrom->set_bank(1);
	m_maincpu->space(AS_PROGRAM).nop_write(0xfc0000, 0xffffff);
}

void a2000_state::machine_reset()
{
	// base reset
	amiga_state::machine_reset();

	// reset cpuslot
	m_cpuslot->rst_w(0);
	m_cpuslot->rst_w(1);

	// reset zorro devices
	m_zorro->busrst_w(0);
	m_zorro->busrst_w(1);
}

void a2000_state::cpuslot_ovr_w(int state)
{
	if (state == 0)
		m_maincpu->space(AS_PROGRAM).unmap_readwrite(0x000000, 0x1fffff);
	else
		m_maincpu->space(AS_PROGRAM).install_device(0x000000, 0x1fffff, *m_overlay, &address_map_bank_device::amap16);
}

void a2000_state::cpuslot_int2_w(int state)
{
	m_cpuslot_int2 = state;
	update_int2();
}

void a2000_state::cpuslot_int6_w(int state)
{
	m_cpuslot_int6 = state;
	update_int6();
}

void a2000_state::zorro2_int2_w(int state)
{
	m_zorro2_int2 = state;
	update_int2();
}

void a2000_state::zorro2_int6_w(int state)
{
	m_zorro2_int6 = state;
	update_int6();
}

bool a2000_state::int2_pending()
{
	return m_cia_0_irq || m_cpuslot_int2 || m_zorro2_int2;
}

bool a2000_state::int6_pending()
{
	return m_cia_1_irq || m_cpuslot_int6 || m_zorro2_int6;
}

void a500_state::machine_reset()
{
	// base reset
	amiga_state::machine_reset();

	// reset side expansion
	m_side->rst_w(0);
	m_side->rst_w(1);

	// start autoconfig
	m_side->cfgin_w(0);
	m_side->cfgin_w(1);
}

void a500_state::side_ovr_w(int state)
{
	if (state == 0)
		m_maincpu->space(AS_PROGRAM).unmap_readwrite(0x000000, 0x1fffff);
	else
		m_maincpu->space(AS_PROGRAM).install_device(0x000000, 0x1fffff, *m_overlay, &address_map_bank_device::amap16);
}

void a500_state::side_int2_w(int state)
{
	m_side_int2 = state;
	update_int2();
}

void a500_state::side_int6_w(int state)
{
	m_side_int6 = state;
	update_int6();
}

bool a500_state::int2_pending()
{
	return m_cia_0_irq || m_side_int2;
}

bool a500_state::int6_pending()
{
	return m_cia_1_irq || m_side_int6;
}

void cdtv_state::machine_start()
{
	// start base machine
	amiga_state::machine_start();

	// setup dmac
	m_dmac->set_address_space(&m_maincpu->space(AS_PROGRAM));
	m_dmac->ramsz_w(0);
}

void cdtv_state::machine_reset()
{
	amiga_state::machine_reset();

	// start autoconfig
	m_dmac->configin_w(0);
	m_dmac->configin_w(1);
}

bool cdtv_state::int2_pending()
{
	return m_cia_0_irq || m_dmac_irq || m_tpi_irq;
}

bool cdtv_state::int6_pending()
{
	return m_cia_1_irq;
}

void cdtv_state::dmac_int_w(int state)
{
	m_dmac_irq = state;
	update_int2();
}

void cdtv_state::tpi_portb_w(uint8_t data)
{
	m_cdrom->enable_w(BIT(data, 1));
	m_cdrom->cmd_w(BIT(data, 0));
}

void cdtv_state::tpi_int_w(int state)
{
	m_tpi_irq = state;
	update_int2();
}

void cdtv_state::sten_w(int state)
{
	m_sten = bool(state);
}

void cdtv_state::drq_w(int state)
{
	if (m_sten)
		m_dmac->xdreq_w(state);
}

u32 a3000_state::scsi_r(offs_t offset, u32 mem_mask)
{
	u32 data = 0xffffffff;
	logerror("scsi_r(%06x): %08x & %08x\n", offset, data, mem_mask);
	return data;
}

void a3000_state::scsi_w(offs_t offset, u32 data, u32 mem_mask)
{
	logerror("scsi_w(%06x): %08x & %08x\n", offset, data, mem_mask);
}

u32 a3000_state::motherboard_r(offs_t offset, u32 mem_mask)
{
	u32 data = 0xffffffff;
	logerror("motherboard_r(%06x): %08x & %08x\n", offset, data, mem_mask);
	return data;
}

void a3000_state::motherboard_w(offs_t offset, u32 data, u32 mem_mask)
{
	logerror("motherboard_w(%06x): %08x & %08x\n", offset, data, mem_mask);
}

void a500p_state::machine_reset()
{
	// base reset
	amiga_state::machine_reset();

	// reset side expansion
	m_side->rst_w(0);
	m_side->rst_w(1);

	// start autoconfig
	m_side->cfgin_w(0);
	m_side->cfgin_w(1);
}

void a500p_state::side_ovr_w(int state)
{
	if (state == 0)
		m_maincpu->space(AS_PROGRAM).unmap_readwrite(0x000000, 0x1fffff);
	else
		m_maincpu->space(AS_PROGRAM).install_device(0x000000, 0x1fffff, *m_overlay, &address_map_bank_device::amap16);
}

void a500p_state::side_int2_w(int state)
{
	m_side_int2 = state;
	update_int2();
}

void a500p_state::side_int6_w(int state)
{
	m_side_int6 = state;
	update_int6();
}

bool a500p_state::int2_pending()
{
	return m_cia_0_irq || m_side_int2;
}

bool a500p_state::int6_pending()
{
	return m_cia_1_irq || m_side_int6;
}

bool a600_state::int2_pending()
{
	return m_cia_0_irq || m_gayle_int2;
}

bool a600_state::int6_pending()
{
	return m_cia_1_irq || m_gayle_int6;
}

void a600_state::gayle_int2_w(int state)
{
	m_gayle_int2 = state;
	update_int2();
}

void a600_state::gayle_int6_w(int state)
{
	m_gayle_int6 = state;
	update_int6();
}

bool a1200_state::int2_pending()
{
	return m_cia_0_irq || m_gayle_int2;
}

bool a1200_state::int6_pending()
{
	return m_cia_1_irq || m_gayle_int6;
}

void a1200_state::gayle_int2_w(int state)
{
	m_gayle_int2 = state;
	update_int2();
}

void a1200_state::gayle_int6_w(int state)
{
	m_gayle_int6 = state;
	update_int6();
}

u32 a4000_state::scsi_r(offs_t offset, u32 mem_mask)
{
	u16 data = 0xffff;
	logerror("scsi_r(%06x): %08x & %08x\n", offset, data, mem_mask);
	return data;
}

void a4000_state::scsi_w(offs_t offset, u32 data, u32 mem_mask)
{
	logerror("scsi_w(%06x): %08x & %08x\n", offset, data, mem_mask);
}

u16 a4000_state::ide_r(offs_t offset, u16 mem_mask)
{
	// ide interrupt register
	if (offset == 0x1010)
		return m_ide_interrupt << 15;

	// this very likely doesn't respond to all the addresses, figure out which ones
	if (BIT(offset, 12))
		return m_ata->cs1_swap_r((offset >> 1) & 0x07, mem_mask);
	else
		return m_ata->cs0_swap_r((offset >> 1) & 0x07, mem_mask);
}

void a4000_state::ide_w(offs_t offset, u16 data, u16 mem_mask)
{
	// ide interrupt register, read only
	if (offset == 0x1010)
		return;

	// this very likely doesn't respond to all the addresses, figure out which ones
	if (BIT(offset, 12))
		m_ata->cs1_swap_w((offset >> 1) & 0x07, data, mem_mask);
	else
		m_ata->cs0_swap_w((offset >> 1) & 0x07, data, mem_mask);
}

void a4000_state::ide_interrupt_w(int state)
{
	m_ide_interrupt = state;
}

u32 a4000_state::motherboard_r(offs_t offset, u32 mem_mask)
{
	u32 data = 0;

	if (offset == 0)
	{
		if (ACCESSING_BITS_0_7)
			data |= m_ramsey_config & 0xff;
		if (ACCESSING_BITS_8_15)
			data |= (m_gary_coldboot << 7 | 0x7f) << 8;
		if (ACCESSING_BITS_16_23)
			data |= (m_gary_toenb << 7 | 0x7f) << 16;
		if (ACCESSING_BITS_24_31)
			data |= (m_gary_timeout << 7 | 0x7f) << 24;
	}
	else
		data = 0xffffffff;

	logerror("motherboard_r(%06x): %08x & %08x\n", offset, data, mem_mask);

	return data;
}

void a4000_state::motherboard_w(offs_t offset, u32 data, u32 mem_mask)
{
	if (offset == 0)
	{
		if (ACCESSING_BITS_0_7)
			m_ramsey_config = data & 0xff;
		if (ACCESSING_BITS_8_15)
			m_gary_coldboot = BIT(data, 7);
		if (ACCESSING_BITS_16_23)
			m_gary_toenb = BIT(data, 7);
		if (ACCESSING_BITS_24_31)
			m_gary_timeout = BIT(data, 7);
	}

	logerror("motherboard_w(%06x): %08x & %08x\n", offset, data, mem_mask);
}

void cd32_state::akiko_int_w(int state)
{
	set_interrupt(INTENA_SETCLR | INTENA_PORTS);
}

void cd32_state::potgo_w(u16 data)
{
	int i;

	m_potgo_value = m_potgo_value & 0x5500;
	m_potgo_value |= data & 0xaa00;

	for (i = 0; i < 8; i += 2)
	{
		u16 dir = 0x0200 << i;
		if (data & dir)
		{
			u16 d = 0x0100 << i;
			m_potgo_value &= ~d;
			m_potgo_value |= data & d;
		}
	}
	for (i = 0; i < 2; i++)
	{
		u16 p5dir = 0x0200 << (i * 4); /* output enable P5 */
		u16 p5dat = 0x0100 << (i * 4); /* data P5 */
		if ((m_potgo_value & p5dir) && (m_potgo_value & p5dat))
			m_cd32_shifter[i] = 8;
	}
}

void cd32_state::handle_joystick_cia(u8 pra, u8 dra)
{
	for (int i = 0; i < 2; i++)
	{
		u8 but = 0x40 << i;
		u16 p5dir = 0x0200 << (i * 4); /* output enable P5 */
		u16 p5dat = 0x0100 << (i * 4); /* data P5 */

		if (!(m_potgo_value & p5dir) || !(m_potgo_value & p5dat))
		{
			if ((dra & but) && (pra & but) != m_oldstate[i])
			{
				if (!(pra & but))
				{
					m_cd32_shifter[i]--;
					if (m_cd32_shifter[i] < 0)
						m_cd32_shifter[i] = 0;
				}
			}
		}
		m_oldstate[i] = pra & but;
	}
}

u16 cd32_state::handle_joystick_potgor(u16 potgor)
{
	for (int i = 0; i < 2; i++)
	{
		u16 p9dir = 0x0800 << (i * 4); /* output enable P9 */
		u16 p9dat = 0x0400 << (i * 4); /* data P9 */
		u16 p5dir = 0x0200 << (i * 4); /* output enable P5 */
		u16 p5dat = 0x0100 << (i * 4); /* data P5 */

		/* p5 is floating in input-mode */
		potgor &= ~p5dat;
		potgor |= m_potgo_value & p5dat;
		if (!(m_potgo_value & p9dir))
			potgor |= p9dat;
		/* P5 output and 1 -> shift register is kept reset (Blue button) */
		if ((m_potgo_value & p5dir) && (m_potgo_value & p5dat))
			m_cd32_shifter[i] = 8;
		/* shift at 1 == return one, >1 = return button states */
		if (m_cd32_shifter[i] == 0)
			potgor &= ~p9dat; /* shift at zero == return zero */
		if (m_cd32_shifter[i] >= 2 && ((m_player_ports[i])->read() & (1 << (m_cd32_shifter[i] - 2))))
			potgor &= ~p9dat;
	}
	return potgor;
}

ioport_value cd32_state::cd32_input()
{
	return handle_joystick_potgor(m_potgo_value) >> 8;
}

template <int P>
int cd32_state::cd32_sel_mirror_input()
{
	u8 bits = m_player_ports[P]->read();
	return (bits & 0x20)>>5;
}

void cd32_state::akiko_cia_0_port_a_write(uint8_t data)
{
	// bit 0, cd audio mute
	m_cdda->set_output_gain(0, BIT(data, 0) ? 0.0 : 1.0);

	// bit 1, power led
	m_power_led = BIT(~data, 1);

	handle_joystick_cia(data, m_cia_0->read(2));
}


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

// The first Amiga systems used a PAL to decode chip selects, later systems
// switched to the "Gary" chip, the A3000 and A4000 used the "Super Gary"
// chip. The A600 and A1200 use the Gayle chip, while the CD32 uses its
// Akiko custom chip.

#if 0
void a1000_state::a1000_overlay_map(address_map &map)
{
	map(0x000000, 0x03ffff).mirror(0x1c0000).ram().share("chip_ram");
	map(0x200000, 0x20ffff).mirror(0x030000).rom().region("bootrom", 0);
	map(0x280000, 0x2bffff).mirror(0x040000).ram().share("chip_ram");
	map(0x300000, 0x33ffff).mirror(0x040000).ram().share("chip_ram");
	map(0x380000, 0x38ffff).mirror(0x030000).rom().region("bootrom", 0);
}
#endif

void a1000_state::a1000_overlay_map(address_map &map)
{
	map(0x000000, 0x07ffff).mirror(0x180000).ram().share("chip_ram");
	map(0x200000, 0x20ffff).mirror(0x030000).rom().region("bootrom", 0);
	map(0x280000, 0x2fffff).ram().share("chip_ram");
	map(0x300000, 0x37ffff).ram().share("chip_ram");
	map(0x380000, 0x38ffff).mirror(0x030000).rom().region("bootrom", 0);
}

void a1000_state::a1000_bootrom_map(address_map &map)
{
	map(0x000000, 0x00ffff).mirror(0x30000).rom().region("bootrom", 0).w(FUNC(a1000_state::write_protect_w));
	map(0x040000, 0x07ffff).bankr("wom");
}

void a1000_state::a1000_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x1fffff).m(m_overlay, FUNC(address_map_bank_device::amap16));
	map(0xa00000, 0xbfffff).rw(FUNC(a1000_state::cia_r), FUNC(a1000_state::cia_w));
	map(0xc00000, 0xdfffff).m(m_chipset, FUNC(address_map_bank_device::amap16));
	map(0xe00000, 0xe7ffff).nopw().r(FUNC(a1000_state::rom_mirror_r));
	map(0xe80000, 0xefffff).noprw(); // autoconfig space (installed by devices)
	map(0xf80000, 0xfbffff).m(m_bootrom, FUNC(address_map_bank_device::amap16));
	map(0xfc0000, 0xffffff).bankrw("wom");
}

// Gary/Super Gary/Gayle with 512KB chip RAM
void amiga_state::overlay_512kb_map(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x07ffff).mirror(0x180000).ram().share("chip_ram");
	map(0x200000, 0x27ffff).rom().region("kickstart", 0);
}

// Gary/Super Gary/Gayle with 1MB chip RAM
void amiga_state::overlay_1mb_map(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x0fffff).mirror(0x100000).ram().share("chip_ram");
	map(0x200000, 0x27ffff).rom().region("kickstart", 0);
}

// Gary/Super Gary/Gayle with 1MB chip RAM (32 bit system)
void amiga_state::overlay_1mb_map32(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x0fffff).mirror(0x100000).ram().share("chip_ram");
	map(0x200000, 0x27ffff).rom().region("kickstart", 0);
}

// Gary/Super Gary/Gayle with 2MB chip RAM (16 bit system)
void amiga_state::overlay_2mb_map16(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x1fffff).ram().share("chip_ram");
	map(0x200000, 0x27ffff).rom().region("kickstart", 0);
}

// Gary/Super Gary/Gayle with 2MB chip RAM (32 bit system)
void amiga_state::overlay_2mb_map32(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x1fffff).ram().share("chip_ram");
	map(0x200000, 0x27ffff).rom().region("kickstart", 0);
}

// 512KB chip RAM, 512KB slow RAM, RTC
void a2000_state::a2000_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x1fffff).m(m_overlay, FUNC(address_map_bank_device::amap16));
	map(0x200000, 0x9fffff).rw(m_zorro, FUNC(zorro2_bus_device::mem_r), FUNC(zorro2_bus_device::mem_w));
	map(0xa00000, 0xbfffff).rw(FUNC(a2000_state::cia_r), FUNC(a2000_state::cia_w));
	map(0xc00000, 0xc7ffff).ram();
	map(0xc80000, 0xd7ffff).m(m_chipset, FUNC(address_map_bank_device::amap16));
	map(0xd80000, 0xdbffff).noprw();
	map(0xdc0000, 0xdc7fff).rw(FUNC(a2000_state::clock_r), FUNC(a2000_state::clock_w));
	map(0xdc8000, 0xddffff).noprw();
	map(0xde0000, 0xdeffff).m(m_chipset, FUNC(address_map_bank_device::amap16));
	map(0xdf0000, 0xdfffff).m(m_chipset, FUNC(address_map_bank_device::amap16));
	map(0xe00000, 0xe7ffff).nopw().r(FUNC(a2000_state::rom_mirror_r));
	map(0xe80000, 0xefffff).rw(m_zorro, FUNC(zorro2_bus_device::io_r), FUNC(zorro2_bus_device::io_w));
	map(0xf00000, 0xf7ffff).noprw(); // cartridge space
	map(0xf80000, 0xffffff).rom().region("kickstart", 0);
}

// 512KB chip RAM and no clock
void a500_state::a500_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x1fffff).m(m_overlay, FUNC(address_map_bank_device::amap16));
	map(0xa00000, 0xbfffff).rw(FUNC(a500_state::cia_r), FUNC(a500_state::cia_w));
	map(0xc00000, 0xd7ffff).m(m_chipset, FUNC(address_map_bank_device::amap16));
	map(0xd80000, 0xddffff).noprw();
	map(0xde0000, 0xdeffff).m(m_chipset, FUNC(address_map_bank_device::amap16));
	map(0xdf0000, 0xdfffff).m(m_chipset, FUNC(address_map_bank_device::amap16));
	map(0xe00000, 0xe7ffff).nopw().r(FUNC(a500_state::rom_mirror_r));
	map(0xe80000, 0xefffff).noprw(); // autoconfig space (installed by devices)
	map(0xf00000, 0xf7ffff).noprw(); // cartridge space
	map(0xf80000, 0xffffff).rom().region("kickstart", 0);
}

// 1MB chip RAM, RTC and CDTV specific hardware
void cdtv_state::cdtv_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x1fffff).m(m_overlay, FUNC(address_map_bank_device::amap16));
	map(0xa00000, 0xbfffff).rw(FUNC(cdtv_state::cia_r), FUNC(cdtv_state::cia_w));
	map(0xc00000, 0xd7ffff).m(m_chipset, FUNC(address_map_bank_device::amap16));
	map(0xd80000, 0xdbffff).noprw();
	map(0xdc0000, 0xdc7fff).rw(FUNC(cdtv_state::clock_r), FUNC(cdtv_state::clock_w));
	map(0xdc8000, 0xdc87ff).mirror(0x7800).ram().share("sram");
	map(0xdd0000, 0xddffff).noprw();
	map(0xde0000, 0xdeffff).m(m_chipset, FUNC(address_map_bank_device::amap16));
	map(0xdf0000, 0xdfffff).m(m_chipset, FUNC(address_map_bank_device::amap16));
	map(0xe00000, 0xe3ffff).mirror(0x40000).ram().share("memcard");
	map(0xe80000, 0xefffff).noprw(); // autoconfig space (installed by devices)
	map(0xf00000, 0xf3ffff).mirror(0x40000).rom().region("cdrom", 0);
	map(0xf80000, 0xffffff).rom().region("kickstart", 0);
}

void cdtv_state::cdtv_rc_mem(address_map &map)
{
	map(0x0800, 0x0fff).rom().region("rcmcu", 0);
}

void a3000_state::a3000_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000000, 0x001fffff).m(m_overlay, FUNC(address_map_bank_device::amap32));
	map(0x00b80000, 0x00bfffff).rw(FUNC(a3000_state::cia_r), FUNC(a3000_state::cia_w));
	map(0x00c00000, 0x00cfffff).m(m_chipset, FUNC(address_map_bank_device::amap16));
	map(0x00d00000, 0x00dbffff).noprw();
	map(0x00dc0000, 0x00dcffff).rw("rtc", FUNC(rp5c01_device::read), FUNC(rp5c01_device::write)).umask32(0x000000ff);
	map(0x00dd0000, 0x00ddffff).rw(FUNC(a3000_state::scsi_r), FUNC(a3000_state::scsi_w));
	map(0x00de0000, 0x00deffff).rw(FUNC(a3000_state::motherboard_r), FUNC(a3000_state::motherboard_w));
	map(0x00df0000, 0x00dfffff).m(m_chipset, FUNC(address_map_bank_device::amap16));
	map(0x00e80000, 0x00efffff).noprw(); // autoconfig space (installed by devices)
	map(0x00f00000, 0x00f7ffff).noprw(); // cartridge space
	map(0x00f80000, 0x00ffffff).rom().region("kickstart", 0);
	map(0x07f00000, 0x07ffffff).ram(); // motherboard ram (up to 16mb), grows downward
	map(0xfff80000, 0xffffffff).rom().region("kickstart", 0);
}

// 1MB chip RAM and RTC
void a500p_state::a500p_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x1fffff).m(m_overlay, FUNC(address_map_bank_device::amap16));
	map(0xa00000, 0xbfffff).rw(FUNC(a500p_state::cia_r), FUNC(a500p_state::cia_w));
	map(0xc00000, 0xc7ffff).ram();
	map(0xc80000, 0xd7ffff).m(m_chipset, FUNC(address_map_bank_device::amap16));
	map(0xd80000, 0xdbffff).noprw();
	map(0xdc0000, 0xdc7fff).rw(FUNC(a500p_state::clock_r), FUNC(a500p_state::clock_w));
	map(0xdc8000, 0xddffff).noprw();
	map(0xde0000, 0xdeffff).m(m_chipset, FUNC(address_map_bank_device::amap16));
	map(0xdf0000, 0xdfffff).m(m_chipset, FUNC(address_map_bank_device::amap16));
	map(0xe00000, 0xe7ffff).nopw().r(FUNC(a500p_state::rom_mirror_r));
	map(0xe80000, 0xefffff).noprw(); // autoconfig space (installed by devices)
	map(0xf80000, 0xffffff).rom().region("kickstart", 0);
}

// 1MB chip RAM, IDE and PCMCIA
void a600_state::a600_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x1fffff).m(m_overlay, FUNC(address_map_bank_device::amap16));
	map(0x200000, 0x5fffff).noprw();
	map(0x600000, 0x9fffff).rw(m_pcmcia, FUNC(pccard_slot_device::read_memory_swap), FUNC(pccard_slot_device::write_memory_swap));
	map(0xa00000, 0xa1ffff).rw(m_pcmcia, FUNC(pccard_slot_device::read_reg_swap), FUNC(pccard_slot_device::write_reg_swap));
	//map(0xa20000, 0xa3ffff) credit card i/o
	//map(0xa40000, 0xa5ffff) credit card bits
	//map(0xa60000, 0xa7ffff) credit card pc i/o
	map(0xa80000, 0xafffff).nopw().r(FUNC(a600_state::rom_mirror_r));
	map(0xb00000, 0xb7ffff).nopw().r(FUNC(a600_state::rom_mirror_r));
	map(0xb80000, 0xbeffff).noprw(); // reserved (cdtv)
	map(0xbf0000, 0xbfffff).rw(FUNC(a600_state::cia_r), FUNC(a600_state::gayle_cia_w));
	map(0xc00000, 0xd7ffff).noprw(); // slow mem
	map(0xd80000, 0xd8ffff).noprw(); // spare chip select
	map(0xd90000, 0xd9ffff).noprw(); // arcnet chip select
	map(0xda0000, 0xdaffff).m("gayle", FUNC(gayle_device::register_map));
	map(0xdb0000, 0xdbffff).noprw(); // reserved (external ide)
	map(0xdc0000, 0xdcffff).noprw(); // rtc
	map(0xdd0000, 0xddffff).noprw(); // reserved (dma controller)
	map(0xde0000, 0xdeffff).rw("gayle", FUNC(gayle_device::gayle_id_r), FUNC(gayle_device::gayle_id_w));
	map(0xdf0000, 0xdfffff).m(m_chipset, FUNC(address_map_bank_device::amap16));
	map(0xe00000, 0xe7ffff).nopw().r(FUNC(a600_state::rom_mirror_r));
	map(0xe80000, 0xefffff).noprw(); // autoconfig space (installed by devices)
	map(0xf00000, 0xf7ffff).noprw(); // cartridge space
	map(0xf80000, 0xffffff).rom().region("kickstart", 0);
}

// 2MB chip RAM, IDE and PCMCIA
void a1200_state::a1200_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x1fffff).m(m_overlay, FUNC(address_map_bank_device::amap32));
	map(0x200000, 0x5fffff).noprw();
	map(0x600000, 0x9fffff).rw(m_pcmcia, FUNC(pccard_slot_device::read_memory_swap), FUNC(pccard_slot_device::write_memory_swap));
	map(0xa00000, 0xa1ffff).rw(m_pcmcia, FUNC(pccard_slot_device::read_reg_swap), FUNC(pccard_slot_device::write_reg_swap));
	//map(0xa20000, 0xa3ffff) credit card i/o
	//map(0xa40000, 0xa5ffff) credit card bits
	//map(0xa60000, 0xa7ffff) credit card pc i/o
	map(0xa80000, 0xafffff).nopw().r(FUNC(a1200_state::rom_mirror32_r));
	map(0xb00000, 0xb7ffff).nopw().r(FUNC(a1200_state::rom_mirror32_r));
	map(0xb80000, 0xbeffff).noprw(); // reserved (cdtv)
	map(0xbf0000, 0xbfffff).rw(FUNC(a1200_state::cia_r), FUNC(a1200_state::gayle_cia_w));
	map(0xc00000, 0xd7ffff).noprw(); // slow mem
	map(0xd80000, 0xd8ffff).noprw(); // spare chip select
	map(0xd90000, 0xd9ffff).noprw(); // arcnet chip select
	map(0xda0000, 0xdaffff).m("gayle", FUNC(gayle_device::register_map));
	map(0xdb0000, 0xdbffff).noprw(); // reserved (external ide)
	map(0xdc0000, 0xdcffff).noprw(); // rtc
	map(0xdd0000, 0xddffff).noprw(); // reserved (dma controller)
	map(0xde0000, 0xdeffff).rw("gayle", FUNC(gayle_device::gayle_id_r), FUNC(gayle_device::gayle_id_w));
	map(0xdf0000, 0xdfffff).m(m_chipset, FUNC(address_map_bank_device::amap32));
	map(0xe00000, 0xe7ffff).nopw().r(FUNC(a1200_state::rom_mirror32_r));
	map(0xe80000, 0xefffff).noprw(); // autoconfig space (installed by devices)
	map(0xf00000, 0xf7ffff).noprw(); // cartridge space
	map(0xf80000, 0xffffff).rom().region("kickstart", 0);
}

// 2MB chip RAM, 4 MB fast RAM, RTC and IDE
void a4000_state::a4000_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000000, 0x001fffff).m(m_overlay, FUNC(address_map_bank_device::amap32));
	map(0x00200000, 0x009fffff).noprw(); // zorro2 expansion
	map(0x00a00000, 0x00b7ffff).noprw();
	map(0x00b80000, 0x00beffff).noprw();
	map(0x00bf0000, 0x00bfffff).rw(FUNC(a4000_state::cia_r), FUNC(a4000_state::cia_w));
	map(0x00c00000, 0x00cfffff).m(m_chipset, FUNC(address_map_bank_device::amap32));
	map(0x00d00000, 0x00d9ffff).noprw();
	map(0x00da0000, 0x00dbffff).noprw();
	map(0x00dc0000, 0x00dcffff).rw("rtc", FUNC(rp5c01_device::read), FUNC(rp5c01_device::write)).umask32(0x000000ff);
	map(0x00dd0000, 0x00dd0fff).noprw();
	map(0x00dd1000, 0x00dd3fff).rw(FUNC(a4000_state::ide_r), FUNC(a4000_state::ide_w));
	map(0x00dd4000, 0x00ddffff).noprw();
	map(0x00de0000, 0x00deffff).rw(FUNC(a4000_state::motherboard_r), FUNC(a4000_state::motherboard_w));
	map(0x00df0000, 0x00dfffff).m(m_chipset, FUNC(address_map_bank_device::amap32));
	map(0x00e00000, 0x00e7ffff).nopw().r(FUNC(a4000_state::rom_mirror32_r));
	map(0x00e80000, 0x00efffff).noprw(); // zorro2 autoconfig space (installed by devices)
	map(0x00f00000, 0x00f7ffff).noprw(); // cartridge space
	map(0x00f80000, 0x00ffffff).rom().region("kickstart", 0);
	map(0x01000000, 0x017fffff).noprw(); // reserved (8 mb chip ram)
	map(0x01800000, 0x06ffffff).noprw(); // reserved (motherboard fast ram expansion)
	map(0x07000000, 0x07bfffff).noprw(); // motherboard ram
	map(0x07c00000, 0x07ffffff).ram(); // motherboard ram (up to 16mb), grows downward
	map(0xfff80000, 0xffffffff).rom().region("kickstart", 0);
}

// 2MB chip RAM, 2 MB fast RAM, RTC and IDE
void a4000_state::a400030_mem(address_map &map)
{
	map.unmap_value_high();
	a4000_mem(map);
	map(0x07000000, 0x07dfffff).noprw(); // Drop the first 2Mb
}

// 2MB chip RAM and CD-ROM
void cd32_state::cd32_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x1fffff).m(m_overlay, FUNC(address_map_bank_device::amap32));
	map(0xb80000, 0xb8003f).rw("akiko", FUNC(akiko_device::read), FUNC(akiko_device::write));
	map(0xbf0000, 0xbfffff).rw(FUNC(cd32_state::cia_r), FUNC(cd32_state::gayle_cia_w));
	map(0xc00000, 0xdfffff).m(m_chipset, FUNC(address_map_bank_device::amap32));
	map(0xe00000, 0xe7ffff).rom().region("kickstart", 0x80000);
	map(0xe80000, 0xf7ffff).noprw();
	map(0xf80000, 0xffffff).rom().region("kickstart", 0);
}

// 2 MB chip RAM, IDE, RTC and SCSI
void a4000_state::a4000t_mem(address_map &map)
{
	map.unmap_value_high();
	a4000_mem(map);
	map(0x00dd0000, 0x00dd0fff).rw(FUNC(a4000_state::scsi_r), FUNC(a4000_state::scsi_w));
}


//**************************************************************************
//  INPUTS
//**************************************************************************

template <int P>
ioport_value amiga_state::amiga_joystick_convert()
{
	uint8_t bits = m_joy_ports[P].read_safe(0xff);

	int up = (bits >> 0) & 1;
	int down = (bits >> 1) & 1;
	int left = (bits >> 2) & 1;
	int right = (bits >> 3) & 1;

	if (left) up ^= 1;
	if (right) down ^= 1;

	return down | (right << 1) | (up << 8) | (left << 9);
}

static INPUT_PORTS_START( amiga )
	PORT_START("input")
	PORT_CONFNAME(0x10, 0x00, "Game Port 0 Device")
	PORT_CONFSETTING(0x00, "Mouse")
	PORT_CONFSETTING(0x10, DEF_STR(Joystick))
	PORT_CONFNAME(0x20, 0x20, "Game Port 1 Device")
	PORT_CONFSETTING(0x00, "Mouse")
	PORT_CONFSETTING(0x20, DEF_STR(Joystick) )

	PORT_START("cia_0_port_a")
	PORT_BIT(0x3f, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_CUSTOM_MEMBER(FUNC(amiga_state::floppy_drive_status))
	PORT_BIT(0x40, IP_ACTIVE_LOW,  IPT_BUTTON1) PORT_PLAYER(1)
	PORT_BIT(0x80, IP_ACTIVE_LOW,  IPT_BUTTON1) PORT_PLAYER(2)

	PORT_START("joy_0_dat")
	PORT_BIT(0x0303, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_CUSTOM_MEMBER(FUNC(amiga_state::amiga_joystick_convert<0>))
	PORT_BIT(0xfcfc, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("joy_1_dat")
	PORT_BIT(0x0303, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_CUSTOM_MEMBER(FUNC(amiga_state::amiga_joystick_convert<1>))
	PORT_BIT(0xfcfc, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("potgo")
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_BUTTON3) PORT_PLAYER(1)
	PORT_BIT(0x0400, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_PLAYER(1)
	PORT_BIT(0x1000, IP_ACTIVE_LOW, IPT_BUTTON3) PORT_PLAYER(2)
	PORT_BIT(0x4000, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_PLAYER(2)
	PORT_BIT(0xaaff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("p1_joy")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP)    PORT_PLAYER(1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN)  PORT_PLAYER(1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT)  PORT_PLAYER(1)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_PLAYER(1)

	PORT_START("p2_joy")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP)    PORT_PLAYER(2)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN)  PORT_PLAYER(2)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT)  PORT_PLAYER(2)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_PLAYER(2)

	PORT_START("p1_mouse_x")
	PORT_BIT(0xff, 0x00, IPT_MOUSE_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(5) PORT_MINMAX(0, 255) PORT_PLAYER(1)

	PORT_START("p1_mouse_y")
	PORT_BIT(0xff, 0x00, IPT_MOUSE_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(5) PORT_MINMAX(0, 255) PORT_PLAYER(1)

	PORT_START("p2_mouse_x")
	PORT_BIT(0xff, 0x00, IPT_MOUSE_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(5) PORT_MINMAX(0, 255) PORT_PLAYER(2)

	PORT_START("p2_mouse_y")
	PORT_BIT(0xff, 0x00, IPT_MOUSE_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(5) PORT_MINMAX(0, 255) PORT_PLAYER(2)
INPUT_PORTS_END

INPUT_PORTS_START( cd32 )
	PORT_INCLUDE(amiga)

	PORT_MODIFY("cia_0_port_a")
	PORT_BIT( 0x3f, IP_ACTIVE_LOW, IPT_CUSTOM )
	// this is the regular port for reading a single button joystick on the Amiga, many CD32 games require this to mirror the pad start button!
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(cd32_state::cd32_sel_mirror_input<0>))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(cd32_state::cd32_sel_mirror_input<1>))

	PORT_MODIFY("joy_0_dat")
	PORT_BIT( 0x0303, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(amiga_state::amiga_joystick_convert<0>))
	PORT_BIT( 0xfcfc, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_MODIFY("joy_1_dat")
	PORT_BIT( 0x0303, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(amiga_state::amiga_joystick_convert<1>))
	PORT_BIT( 0xfcfc, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_MODIFY("potgo")
	PORT_BIT( 0xff00, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(cd32_state::cd32_input))
	PORT_BIT( 0x00ff, IP_ACTIVE_HIGH, IPT_UNUSED )

	// CD32 '11' button pad (4 dpad directions + 7 buttons), not read directly
	PORT_START("p1_cd32_buttons")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("P1 Play/Pause")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("P1 Left Trigger/Rewind")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("P1 Right Trigger/Forward")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("P1 Green/Stop")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_PLAYER(1) PORT_NAME("P1 Yellow/Shuffle")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_PLAYER(1) PORT_NAME("P1 Red/Select")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_PLAYER(1) PORT_NAME("P1 Blue/Loop")

	// CD32 '11' button pad (4 dpad directions + 7 buttons), not read directly
	PORT_START("p2_cd32_buttons")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 Play/Pause")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 Left Trigger/Rewind")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("P2 Right Trigger/Forward")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("P2 Green/Stop")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_PLAYER(2) PORT_NAME("P2 Yellow/Shuffle")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_PLAYER(2) PORT_NAME("P2 Red/Select")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_PLAYER(2) PORT_NAME("P2 Blue/Loop")
INPUT_PORTS_END


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

static void amiga_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
}

static void pcmcia_devices(device_slot_interface &device)
{
	device.option_add("melcard_1m", PCCARD_SRAM_MITSUBISHI_1M);
	device.option_add("sram_1m", PCCARD_SRAM_CENTENNIAL_1M);
	device.option_add("sram_2m", PCCARD_SRAM_CENTENNIAL_2M);
	device.option_add("sram_4m", PCCARD_SRAM_CENTENNIAL_4M);
}

// basic elements common to all amigas
void amiga_state::amiga_base(machine_config &config)
{
	// video
	pal_video(config);

	PALETTE(config, m_palette, FUNC(amiga_state::amiga_palette), 4096);

	MCFG_VIDEO_START_OVERRIDE(amiga_state, amiga)

	// cia
	MOS8520(config, m_cia_0, amiga_state::CLK_E_PAL);
	m_cia_0->irq_wr_callback().set(FUNC(amiga_state::cia_0_irq));
	m_cia_0->pa_rd_callback().set_ioport("cia_0_port_a");
	m_cia_0->pa_wr_callback().set(FUNC(amiga_state::cia_0_port_a_write));
	m_cia_0->pb_rd_callback().set("cent_data_in", FUNC(input_buffer_device::read));
	m_cia_0->pb_wr_callback().set("cent_data_out", FUNC(output_latch_device::write));
	m_cia_0->pc_wr_callback().set(m_centronics, FUNC(centronics_device::write_strobe));
	m_cia_0->sp_wr_callback().set("kbd", FUNC(amiga_keyboard_bus_device::kdat_in_w)).invert();

	MOS8520(config, m_cia_1, amiga_state::CLK_E_PAL);
	m_cia_1->irq_wr_callback().set(FUNC(amiga_state::cia_1_irq));
	m_cia_1->pa_rd_callback().set(FUNC(amiga_state::cia_1_port_a_read));
	m_cia_1->pa_wr_callback().set(FUNC(amiga_state::cia_1_port_a_write));
	m_cia_1->pb_wr_callback().set(m_fdc, FUNC(paula_fdc_device::ciaaprb_w));

	// audio
	SPEAKER(config, "speaker", 2).front();
	PAULA_8364(config, m_paula, amiga_state::CLK_C1_PAL);
	m_paula->add_route(0, "speaker", 0.50, 0);
	m_paula->add_route(1, "speaker", 0.50, 1);
	m_paula->add_route(2, "speaker", 0.50, 1);
	m_paula->add_route(3, "speaker", 0.50, 0);
	m_paula->mem_read_cb().set(FUNC(amiga_state::chip_ram_r));
	m_paula->int_cb().set(FUNC(amiga_state::paula_int_w));

	// floppy drives
	PAULA_FDC(config, m_fdc, amiga_state::CLK_7M_PAL);
	m_fdc->index_callback().set(m_cia_1, FUNC(mos8520_device::flag_w));
	m_fdc->read_dma_callback().set(FUNC(amiga_state::chip_ram_r));
	m_fdc->write_dma_callback().set(FUNC(amiga_state::chip_ram_w));
	m_fdc->dskblk_callback().set(FUNC(amiga_state::fdc_dskblk_w));
	m_fdc->dsksyn_callback().set(FUNC(amiga_state::fdc_dsksyn_w));
	FLOPPY_CONNECTOR(config, "fdc:0", amiga_floppies, "35dd", paula_fdc_device::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", amiga_floppies, nullptr, paula_fdc_device::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:2", amiga_floppies, nullptr, paula_fdc_device::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:3", amiga_floppies, nullptr, paula_fdc_device::floppy_formats).enable_sound(true);

	// TODO: shouldn't have a clock
	// (finite state machine, controlled by Agnus beams)
	AGNUS_COPPER(config, m_copper, amiga_state::CLK_7M_PAL);
	m_copper->set_host_cpu_tag(m_maincpu);
	m_copper->mem_read_cb().set(FUNC(amiga_state::chip_ram_r));
	m_copper->set_ecs_mode(false);

	// rs232
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(FUNC(amiga_state::rs232_rx_w));
	rs232.dcd_handler().set(FUNC(amiga_state::rs232_dcd_w));
	rs232.dsr_handler().set(FUNC(amiga_state::rs232_dsr_w));
	rs232.ri_handler().set(FUNC(amiga_state::rs232_ri_w));
	rs232.cts_handler().set(FUNC(amiga_state::rs232_cts_w));

	// centronics
	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->set_data_input_buffer("cent_data_in");
	m_centronics->ack_handler().set(FUNC(amiga_state::centronics_ack_w));
	m_centronics->busy_handler().set(FUNC(amiga_state::centronics_busy_w));
	m_centronics->perror_handler().set(FUNC(amiga_state::centronics_perror_w));
	m_centronics->select_handler().set(FUNC(amiga_state::centronics_select_w));

	INPUT_BUFFER(config, "cent_data_in");

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	// software
	SOFTWARE_LIST(config, "wb_list").set_original("amiga_workbench");
	SOFTWARE_LIST(config, "hardware_list").set_original("amiga_hardware");
	SOFTWARE_LIST(config, "apps_list").set_original("amiga_apps");
	SOFTWARE_LIST(config, "flop_list").set_original("amiga_flop");
	SOFTWARE_LIST(config, "ocs_list").set_original("amigaocs_flop");
	SOFTWARE_LIST(config, "demos_list").set_original("amiga_demos");
	SOFTWARE_LIST(config, "amigacd_list").set_original("amiga_cd");
	// CD32 should support this off the bat, Aminet Photo CD packages available anyway.
	SOFTWARE_LIST(config, "photocd_list").set_compatible("photo_cd");
}

void a1000_state::a1000(machine_config &config)
{
	// main cpu
	M68000(config, m_maincpu, amiga_state::CLK_7M_PAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &a1000_state::a1000_mem);
	m_maincpu->set_cpu_space(AS_PROGRAM);
	m_maincpu->reset_cb().set(FUNC(amiga_state::m68k_reset));

	amiga_base(config);

	// keyboard
	auto &kbd(AMIGA_KEYBOARD_INTERFACE(config, "kbd", amiga_keyboard_devices, "a1000_us"));
	kbd.kclk_handler().set("cia_0", FUNC(mos8520_device::cnt_w));
	kbd.kclk_handler().append("kbrst", FUNC(a1000_kbreset_device::kbclk_w));
	kbd.kdat_handler().set("cia_0", FUNC(mos8520_device::sp_w));
	A1000_KBRESET(config, "kbrst")
			.set_delays(attotime::from_msec(152), attotime::from_usec(176), attotime::from_msec(704))
			.kbrst_cb().set(FUNC(a1000_state::kbreset_w));

	ADDRESS_MAP_BANK(config, m_overlay).set_map(&a1000_state::a1000_overlay_map).set_options(ENDIANNESS_BIG, 16, 22, 0x200000);
	ADDRESS_MAP_BANK(config, "bootrom").set_map(&a1000_state::a1000_bootrom_map).set_options(ENDIANNESS_BIG, 16, 19, 0x40000);
	ADDRESS_MAP_BANK(config, m_chipset).set_map(&a1000_state::ocs_map).set_options(ENDIANNESS_BIG, 16, 9, 0x200);
}

void a1000_state::a1000n(machine_config &config)
{
	a1000(config);

	m_maincpu->set_clock(amiga_state::CLK_7M_NTSC);
	config.device_remove("screen");
	ntsc_video(config);
	m_paula->set_clock(amiga_state::CLK_C1_NTSC);
	m_cia_0->set_clock(amiga_state::CLK_E_NTSC);
	m_cia_1->set_clock(amiga_state::CLK_E_NTSC);
	m_fdc->set_clock(amiga_state::CLK_7M_NTSC);
}

void a2000_state::a2000(machine_config &config)
{
	// main cpu
	M68000(config, m_maincpu, amiga_state::CLK_7M_PAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &a2000_state::a2000_mem);
	m_maincpu->set_cpu_space(AS_PROGRAM);
	m_maincpu->reset_cb().set(FUNC(amiga_state::m68k_reset));

	amiga_base(config);

	// keyboard
	auto &kbd(AMIGA_KEYBOARD_INTERFACE(config, "kbd", amiga_keyboard_devices, "a2000_us"));
	kbd.kclk_handler().set("cia_0", FUNC(mos8520_device::cnt_w));
	kbd.kclk_handler().append("kbrst", FUNC(a1000_kbreset_device::kbclk_w));
	kbd.kdat_handler().set("cia_0", FUNC(mos8520_device::sp_w));
	A1000_KBRESET(config, "kbrst")
			.set_delays(attotime::from_msec(112), attotime::from_msec(74), attotime::from_msec(1294))
			.kbrst_cb().set(FUNC(a2000_state::kbreset_w));

	ADDRESS_MAP_BANK(config, m_overlay).set_map(&a2000_state::overlay_512kb_map).set_options(ENDIANNESS_BIG, 16, 22, 0x200000);
	ADDRESS_MAP_BANK(config, m_chipset).set_map(&a2000_state::ecs_map).set_options(ENDIANNESS_BIG, 16, 9, 0x200);
	m_copper->set_ecs_mode(true);

	// real-time clock
	MSM6242(config, m_rtc, XTAL(32'768));

	// cpu slot
	AMIGA_CPUSLOT(config, m_cpuslot, a2000_cpuslot_cards, nullptr);
	m_cpuslot->set_space(m_maincpu, AS_PROGRAM);
	m_cpuslot->ovr_cb().set(FUNC(a2000_state::cpuslot_ovr_w));
	m_cpuslot->int2_cb().set(FUNC(a2000_state::cpuslot_int2_w));
	m_cpuslot->int6_cb().set(FUNC(a2000_state::cpuslot_int6_w));
	m_cpuslot->ipl7_cb().set([this](int state) { m_maincpu->set_input_line(7, state); });

	// zorro2 slots
	ZORRO2_BUS(config, m_zorro, 0);
	m_zorro->int2_handler().set(FUNC(a2000_state::zorro2_int2_w));
	m_zorro->int6_handler().set(FUNC(a2000_state::zorro2_int6_w));
	ZORRO2_SLOT(config, "zorro2:1", zorro2_cards, nullptr);
	ZORRO2_SLOT(config, "zorro2:2", zorro2_cards, nullptr);
	ZORRO2_SLOT(config, "zorro2:3", zorro2_cards, nullptr);
	ZORRO2_SLOT(config, "zorro2:4", zorro2_cards, nullptr);
	ZORRO2_SLOT(config, "zorro2:5", zorro2_cards, nullptr);
}

void a2000_state::a2000n(machine_config &config)
{
	a2000(config);

	m_maincpu->set_clock(amiga_state::CLK_7M_NTSC);
	config.device_remove("screen");
	ntsc_video(config);
	m_paula->set_clock(amiga_state::CLK_C1_NTSC);
	m_cia_0->set_clock(amiga_state::CLK_E_NTSC);
	m_cia_1->set_clock(amiga_state::CLK_E_NTSC);
	m_fdc->set_clock(amiga_state::CLK_7M_NTSC);
}

void a500_state::a500(machine_config &config)
{
	// main cpu
	M68000(config, m_maincpu, amiga_state::CLK_7M_PAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &a500_state::a500_mem);
	m_maincpu->set_cpu_space(AS_PROGRAM);
	m_maincpu->reset_cb().set(FUNC(amiga_state::m68k_reset));

	amiga_base(config);

	// keyboard
	auto &kbd(AMIGA_KEYBOARD_INTERFACE(config, "kbd", a500_keyboard_devices, "a500_us"));
	kbd.kclk_handler().set("cia_0", FUNC(mos8520_device::cnt_w));
	kbd.kdat_handler().set("cia_0", FUNC(mos8520_device::sp_w));
	kbd.krst_handler().set(FUNC(amiga_state::kbreset_w));

	ADDRESS_MAP_BANK(config, m_overlay).set_map(&a500_state::overlay_1mb_map).set_options(ENDIANNESS_BIG, 16, 22, 0x200000);
	ADDRESS_MAP_BANK(config, m_chipset).set_map(&a500_state::ocs_map).set_options(ENDIANNESS_BIG, 16, 9, 0x200);

	// left side cpu slot
	AMIGA_CPUSLOT(config, m_side, a500_cpuslot_cards, nullptr);
	m_side->set_space(m_maincpu, AS_PROGRAM);
	m_side->ovr_cb().set(FUNC(a500_state::side_ovr_w));
	m_side->int2_cb().set(FUNC(a500_state::side_int2_w));
	m_side->int6_cb().set(FUNC(a500_state::side_int6_w));
	m_side->ipl7_cb().set([this](int state) { m_maincpu->set_input_line(7, state); });
}

void a500_state::a500n(machine_config &config)
{
	a500(config);

	m_maincpu->set_clock(amiga_state::CLK_7M_NTSC);
	config.device_remove("screen");
	ntsc_video(config);
	m_paula->set_clock(amiga_state::CLK_C1_NTSC);
	m_cia_0->set_clock(amiga_state::CLK_E_NTSC);
	m_cia_1->set_clock(amiga_state::CLK_E_NTSC);
	m_fdc->set_clock(amiga_state::CLK_7M_NTSC);
}

void cdtv_state::cdtv(machine_config &config)
{
	// main cpu
	M68000(config, m_maincpu, amiga_state::CLK_7M_PAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &cdtv_state::cdtv_mem);
	m_maincpu->set_cpu_space(AS_PROGRAM);
	m_maincpu->reset_cb().set(FUNC(amiga_state::m68k_reset));

	amiga_base(config);

	// keyboard
	auto &kbd(AMIGA_KEYBOARD_INTERFACE(config, "kbd", amiga_keyboard_devices, "a2000_us"));
	kbd.kclk_handler().set("cia_0", FUNC(mos8520_device::cnt_w));
	kbd.kclk_handler().append("kbrst", FUNC(a1000_kbreset_device::kbclk_w));
	kbd.kdat_handler().set("cia_0", FUNC(mos8520_device::sp_w));
	A1000_KBRESET(config, "kbrst")
			.set_delays(attotime::from_usec(11238), attotime::from_usec(7432), attotime::from_usec(27539))
			.kbrst_cb().set(FUNC(a1000_state::kbreset_w));

	// remote control input converter
	m6502_device &u75(M6502(config, "u75", XTAL(3'000'000)));
	u75.set_addrmap(AS_PROGRAM, &cdtv_state::cdtv_rc_mem);
	u75.set_disable();

	// lcd controller
#if 0
	lc6554_device &u62(LC6554(config, "u62", XTAL(4'000'000))); // device isn't emulated yet
	u62.set_addrmap(AS_PROGRAM, &cdtv_state::lcd_mem);
#endif

	ADDRESS_MAP_BANK(config, m_overlay).set_map(&cdtv_state::overlay_1mb_map).set_options(ENDIANNESS_BIG, 16, 22, 0x200000);
	// FIXME: CDTV is actually ECS Agnus but OCS Denise
	ADDRESS_MAP_BANK(config, m_chipset).set_map(&cdtv_state::ecs_map).set_options(ENDIANNESS_BIG, 16, 9, 0x200);
	m_copper->set_ecs_mode(true);

	// standard sram
	NVRAM(config, "sram", nvram_device::DEFAULT_ALL_0);

	// 256kb memory card
	NVRAM(config, "memcard", nvram_device::DEFAULT_ALL_0);

	MSM6242(config, m_rtc, XTAL(32'768));

	AMIGA_DMAC_REV2(config, m_dmac, amiga_state::CLK_7M_PAL);
	m_dmac->int_cb().set(FUNC(cdtv_state::dmac_int_w));
	m_dmac->csx0_read_cb().set(m_cdrom, FUNC(cr511b_device::read));
	m_dmac->csx0_write_cb().set(m_cdrom, FUNC(cr511b_device::write));
	m_dmac->csx0_a4_read_cb().set(m_tpi, FUNC(tpi6525_device::read));
	m_dmac->csx0_a4_write_cb().set(m_tpi, FUNC(tpi6525_device::write));
	m_dmac->xdack_read_cb().set(m_cdrom, FUNC(cr511b_device::read));

	TPI6525(config, m_tpi, 0);
	m_tpi->out_irq_cb().set(FUNC(cdtv_state::tpi_int_w));
	m_tpi->out_pb_cb().set(FUNC(cdtv_state::tpi_portb_w));

	CR511B(config, m_cdrom, 0);
	m_cdrom->add_route(0, "speaker", 1.0, 0);
	m_cdrom->add_route(1, "speaker", 1.0, 1);
	m_cdrom->scor_cb().set(m_tpi, FUNC(tpi6525_device::i1_w)).invert();
	m_cdrom->stch_cb().set(m_tpi, FUNC(tpi6525_device::i2_w)).invert();
	m_cdrom->sten_cb().set(m_tpi, FUNC(tpi6525_device::i3_w));
	m_cdrom->sten_cb().append(FUNC(cdtv_state::sten_w));
	m_cdrom->drq_cb().set(m_tpi, FUNC(tpi6525_device::i4_w));
	m_cdrom->drq_cb().append(FUNC(cdtv_state::drq_w));

	SOFTWARE_LIST(config, "cd_list").set_original("cdtv");
}

void cdtv_state::cdtvn(machine_config &config)
{
	cdtv(config);
	m_maincpu->set_clock(amiga_state::CLK_7M_NTSC);
	config.device_remove("screen");
	ntsc_video(config);
	m_paula->set_clock(amiga_state::CLK_C1_NTSC);
	m_cia_0->set_clock(amiga_state::CLK_E_NTSC);
	m_cia_1->set_clock(amiga_state::CLK_E_NTSC);
	m_dmac->set_clock(amiga_state::CLK_7M_NTSC);
	m_fdc->set_clock(amiga_state::CLK_7M_NTSC);
}

void a3000_state::a3000(machine_config &config)
{
	// main cpu
	M68030(config, m_maincpu, XTAL(32'000'000) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &a3000_state::a3000_mem);
	m_maincpu->set_cpu_space(AS_PROGRAM);
	m_maincpu->reset_cb().set(FUNC(amiga_state::m68k_reset));

	amiga_base(config);

	// keyboard
	auto &kbd(AMIGA_KEYBOARD_INTERFACE(config, "kbd", amiga_keyboard_devices, "a2000_us"));
	kbd.kclk_handler().set("cia_0", FUNC(mos8520_device::cnt_w));
	kbd.kdat_handler().set("cia_0", FUNC(mos8520_device::sp_w));

	ADDRESS_MAP_BANK(config, m_overlay).set_map(&a3000_state::overlay_1mb_map32).set_options(ENDIANNESS_BIG, 32, 22, 0x200000);
	ADDRESS_MAP_BANK(config, m_chipset).set_map(&a3000_state::ecs_map).set_options(ENDIANNESS_BIG, 32, 9, 0x200);
	m_copper->set_ecs_mode(true);

	// real-time clock
	RP5C01(config, "rtc", XTAL(32'768));

	// TODO: zorro3 slots, super dmac, scsi

	// software
	SOFTWARE_LIST(config, "amix_list").set_original("amiga_amix");
	SOFTWARE_LIST(config, "ecs_list").set_original("amigaecs_flop");
}

void a3000_state::a3000n(machine_config &config)
{
	a3000(config);
	config.device_remove("screen");
	ntsc_video(config);
	m_cia_0->set_clock(amiga_state::CLK_E_NTSC);
	m_cia_1->set_clock(amiga_state::CLK_E_NTSC);
	m_fdc->set_clock(amiga_state::CLK_7M_NTSC);
}

void a500p_state::a500p(machine_config &config)
{
	// main cpu
	M68000(config, m_maincpu, amiga_state::CLK_7M_PAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &a500p_state::a500p_mem);
	m_maincpu->set_cpu_space(AS_PROGRAM);
	m_maincpu->reset_cb().set(FUNC(amiga_state::m68k_reset));

	amiga_base(config);

	// keyboard
	auto &kbd(AMIGA_KEYBOARD_INTERFACE(config, "kbd", a500_keyboard_devices, "a500_us"));
	kbd.kclk_handler().set("cia_0", FUNC(mos8520_device::cnt_w));
	kbd.kdat_handler().set("cia_0", FUNC(mos8520_device::sp_w));
	kbd.krst_handler().set(FUNC(a500p_state::kbreset_w));

	ADDRESS_MAP_BANK(config, m_overlay).set_map(&a500p_state::overlay_1mb_map).set_options(ENDIANNESS_BIG, 16, 22, 0x200000);
	ADDRESS_MAP_BANK(config, m_chipset).set_map(&a500p_state::ecs_map).set_options(ENDIANNESS_BIG, 16, 9, 0x200);
	m_copper->set_ecs_mode(true);

	// real-time clock
	MSM6242(config, m_rtc, XTAL(32'768));

	// left side cpu slot
	AMIGA_CPUSLOT(config, m_side, a500_cpuslot_cards, nullptr);
	m_side->set_space(m_maincpu, AS_PROGRAM);
	m_side->ovr_cb().set(FUNC(a500p_state::side_ovr_w));
	m_side->int2_cb().set(FUNC(a500p_state::side_int2_w));
	m_side->int6_cb().set(FUNC(a500p_state::side_int6_w));
	m_side->ipl7_cb().set([this](int state) { m_maincpu->set_input_line(7, state); });

	// software
	SOFTWARE_LIST(config, "ecs_list").set_original("amigaecs_flop");
}

void a500p_state::a500pn(machine_config &config)
{
	a500p(config);
	m_maincpu->set_clock(amiga_state::CLK_7M_NTSC);
	config.device_remove("screen");
	ntsc_video(config);
	m_paula->set_clock(amiga_state::CLK_C1_NTSC);
	m_cia_0->set_clock(amiga_state::CLK_E_NTSC);
	m_cia_1->set_clock(amiga_state::CLK_E_NTSC);
	m_fdc->set_clock(amiga_state::CLK_7M_NTSC);
}

void a600_state::a600(machine_config &config)
{
	// main cpu
	M68000(config, m_maincpu, amiga_state::CLK_7M_PAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &a600_state::a600_mem);
	m_maincpu->set_cpu_space(AS_PROGRAM);
	m_maincpu->reset_cb().set(FUNC(amiga_state::m68k_reset));

	amiga_base(config);

	// keyboard
	auto &kbd(AMIGA_KEYBOARD_INTERFACE(config, "kbd", a600_keyboard_devices, "a600_us"));
	kbd.kclk_handler().set("cia_0", FUNC(mos8520_device::cnt_w));
	kbd.kdat_handler().set("cia_0", FUNC(mos8520_device::sp_w));
	kbd.krst_handler().set(FUNC(a600_state::kbreset_w));

	ADDRESS_MAP_BANK(config, m_overlay).set_map(&a600_state::overlay_2mb_map16).set_options(ENDIANNESS_BIG, 16, 22, 0x200000);
	ADDRESS_MAP_BANK(config, m_chipset).set_map(&a600_state::ecs_map).set_options(ENDIANNESS_BIG, 16, 9, 0x200);
	m_copper->set_ecs_mode(true);

	gayle_device &gayle(GAYLE(config, "gayle", amiga_state::CLK_28M_PAL / 2));
	gayle.set_id(a600_state::GAYLE_ID);
	gayle.int2_handler().set(FUNC(a600_state::gayle_int2_w));
	gayle.int6_handler().set(FUNC(a600_state::gayle_int6_w));
	gayle.rst_handler().set(FUNC(a600_state::kbreset_w)); // not really kbreset, but use it for now
	gayle.ide_cs_r_cb<0>().set("ata", FUNC(ata_interface_device::cs0_swap_r));
	gayle.ide_cs_r_cb<1>().set("ata", FUNC(ata_interface_device::cs1_swap_r));
	gayle.ide_cs_w_cb<0>().set("ata", FUNC(ata_interface_device::cs0_swap_w));
	gayle.ide_cs_w_cb<1>().set("ata", FUNC(ata_interface_device::cs1_swap_w));

	ata_interface_device &ata(ATA_INTERFACE(config, "ata").options(ata_devices, "hdd", nullptr, false));
	ata.irq_handler().set("gayle", FUNC(gayle_device::ide_interrupt_w));

	PCCARD_SLOT(config, m_pcmcia, pcmcia_devices, nullptr);
	m_pcmcia->cd1().set("gayle", FUNC(gayle_device::cc_cd1_w));
	m_pcmcia->cd2().set("gayle", FUNC(gayle_device::cc_cd2_w));
	m_pcmcia->bvd1().set("gayle", FUNC(gayle_device::cc_bvd1_w));
	m_pcmcia->bvd2().set("gayle", FUNC(gayle_device::cc_bvd2_w));
	m_pcmcia->wp().set("gayle", FUNC(gayle_device::cc_wp_w));

	// software
	SOFTWARE_LIST(config, "ecs_list").set_original("amigaecs_flop");
	SOFTWARE_LIST(config, "hdd_list").set_original("amiga_hdd");
}

void a600_state::a600n(machine_config &config)
{
	a600(config);
	m_maincpu->set_clock(amiga_state::CLK_7M_NTSC);
	subdevice<gayle_device>("gayle")->set_clock(amiga_state::CLK_28M_NTSC / 2);
	config.device_remove("screen");
	ntsc_video(config);
	m_paula->set_clock(amiga_state::CLK_C1_NTSC);
	m_cia_0->set_clock(amiga_state::CLK_E_NTSC);
	m_cia_1->set_clock(amiga_state::CLK_E_NTSC);
	m_fdc->set_clock(amiga_state::CLK_7M_NTSC);
}

void a1200_state::a1200(machine_config &config)
{
	// main cpu
	M68EC020(config, m_maincpu, amiga_state::CLK_28M_PAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &a1200_state::a1200_mem);
	m_maincpu->set_cpu_space(AS_PROGRAM);
	m_maincpu->reset_cb().set(FUNC(amiga_state::m68k_reset));

	amiga_base(config);

	ADDRESS_MAP_BANK(config, m_overlay).set_map(&a1200_state::overlay_2mb_map32).set_options(ENDIANNESS_BIG, 32, 22, 0x200000);
	ADDRESS_MAP_BANK(config, m_chipset).set_map(&a1200_state::aga_map).set_options(ENDIANNESS_BIG, 32, 9, 0x200);
	m_copper->set_ecs_mode(true);

	// keyboard
	// FIXME: replace with Amiga 1200 devices when we have mask ROM dump
	auto &kbd(AMIGA_KEYBOARD_INTERFACE(config, "kbd", amiga_keyboard_devices, "a1200_us"));
	kbd.kclk_handler().set("cia_0", FUNC(mos8520_device::cnt_w));
	kbd.kdat_handler().set("cia_0", FUNC(mos8520_device::sp_w));
	kbd.krst_handler().set(FUNC(a1200_state::kbreset_w));

	m_screen->set_screen_update(FUNC(amiga_state::screen_update));

	MCFG_VIDEO_START_OVERRIDE(amiga_state, amiga_aga)

	gayle_device &gayle(GAYLE(config, "gayle", amiga_state::CLK_28M_PAL / 2));
	gayle.set_id(a1200_state::GAYLE_ID);
	gayle.int2_handler().set(FUNC(a1200_state::gayle_int2_w));
	gayle.int6_handler().set(FUNC(a1200_state::gayle_int6_w));
	gayle.rst_handler().set(FUNC(a1200_state::kbreset_w)); // not really kbreset, but use it for now
	gayle.ide_cs_r_cb<0>().set("ata", FUNC(ata_interface_device::cs0_swap_r));
	gayle.ide_cs_r_cb<1>().set("ata", FUNC(ata_interface_device::cs1_swap_r));
	gayle.ide_cs_w_cb<0>().set("ata", FUNC(ata_interface_device::cs0_swap_w));
	gayle.ide_cs_w_cb<1>().set("ata", FUNC(ata_interface_device::cs1_swap_w));

	ata_interface_device &ata(ATA_INTERFACE(config, "ata").options(ata_devices, "hdd", nullptr, false));
	ata.irq_handler().set("gayle", FUNC(gayle_device::ide_interrupt_w));

	// keyboard
#if 0
	subdevice<amiga_keyboard_bus_device>("kbd").set_default_option("a1200_us");
#endif

	PCCARD_SLOT(config, m_pcmcia, pcmcia_devices, nullptr);
	m_pcmcia->cd1().set("gayle", FUNC(gayle_device::cc_cd1_w));
	m_pcmcia->cd2().set("gayle", FUNC(gayle_device::cc_cd2_w));
	m_pcmcia->bvd1().set("gayle", FUNC(gayle_device::cc_bvd1_w));
	m_pcmcia->bvd2().set("gayle", FUNC(gayle_device::cc_bvd2_w));
	m_pcmcia->wp().set("gayle", FUNC(gayle_device::cc_wp_w));

	// software
	SOFTWARE_LIST(config, "aga_list").set_original("amigaaga_flop");
	SOFTWARE_LIST(config, "ecs_list").set_original("amigaecs_flop");
	SOFTWARE_LIST(config, "hdd_list").set_original("amiga_hdd");
}

void a1200_state::a1200n(machine_config &config)
{
	a1200(config);
	m_maincpu->set_clock(amiga_state::CLK_28M_NTSC / 2);
	subdevice<gayle_device>("gayle")->set_clock(amiga_state::CLK_28M_NTSC / 2);
	config.device_remove("screen");
	ntsc_video(config);
	m_screen->set_screen_update(FUNC(amiga_state::screen_update));
	m_paula->set_clock(amiga_state::CLK_C1_NTSC);
	m_cia_0->set_clock(amiga_state::CLK_E_NTSC);
	m_cia_1->set_clock(amiga_state::CLK_E_NTSC);
	m_fdc->set_clock(amiga_state::CLK_7M_NTSC);
}

void a4000_state::a4000(machine_config &config)
{
	// main cpu
	M68040(config, m_maincpu, XTAL(50'000'000) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &a4000_state::a4000_mem);
	m_maincpu->set_cpu_space(AS_PROGRAM);
	m_maincpu->reset_cb().set(FUNC(amiga_state::m68k_reset));

	amiga_base(config);

	ADDRESS_MAP_BANK(config, m_overlay).set_map(&a4000_state::overlay_2mb_map32).set_options(ENDIANNESS_BIG, 32, 22, 0x200000);
	ADDRESS_MAP_BANK(config, m_chipset).set_map(&a4000_state::aga_map).set_options(ENDIANNESS_BIG, 32, 9, 0x200);
	m_copper->set_ecs_mode(true);

	// keyboard
	auto &kbd(AMIGA_KEYBOARD_INTERFACE(config, "kbd", amiga_keyboard_devices, "a2000_us"));
	kbd.kclk_handler().set("cia_0", FUNC(mos8520_device::cnt_w));
	kbd.kdat_handler().set("cia_0", FUNC(mos8520_device::sp_w));

	m_screen->set_screen_update(FUNC(amiga_state::screen_update));

	MCFG_VIDEO_START_OVERRIDE(amiga_state, amiga_aga)

	// real-time clock
	RP5C01(config, "rtc", XTAL(32'768));

	// ide
	ata_interface_device &ata(ATA_INTERFACE(config, "ata").options(ata_devices, "hdd", nullptr, false));
	ata.irq_handler().set(FUNC(a4000_state::ide_interrupt_w));

	// TODO: zorro3

	// software
	SOFTWARE_LIST(config, "aga_list").set_original("amigaaga_flop");
	SOFTWARE_LIST(config, "ecs_list").set_original("amigaecs_flop");
	SOFTWARE_LIST(config, "hdd_list").set_original("amiga_hdd");
}

void a4000_state::a4000n(machine_config &config)
{
	a4000(config);

	config.device_remove("screen");
	ntsc_video(config);
	m_screen->set_screen_update(FUNC(amiga_state::screen_update));
	m_paula->set_clock(amiga_state::CLK_C1_NTSC);
	m_cia_0->set_clock(amiga_state::CLK_E_NTSC);
	m_cia_1->set_clock(amiga_state::CLK_E_NTSC);
	m_fdc->set_clock(amiga_state::CLK_7M_NTSC);
}

void a4000_state::a400030(machine_config &config)
{
	a4000(config);
	// main cpu
	M68EC030(config.replace(), m_maincpu, XTAL(50'000'000) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &a4000_state::a400030_mem);
	m_maincpu->set_cpu_space(AS_PROGRAM);

	// TODO: ide
}

void a4000_state::a400030n(machine_config &config)
{
	a400030(config);
	config.device_remove("screen");
	ntsc_video(config);
	m_screen->set_screen_update(FUNC(amiga_state::screen_update));
	m_paula->set_clock(amiga_state::CLK_C1_NTSC);
	m_cia_0->set_clock(amiga_state::CLK_E_NTSC);
	m_cia_1->set_clock(amiga_state::CLK_E_NTSC);
	m_fdc->set_clock(amiga_state::CLK_7M_NTSC);
}

void cd32_state::cd32(machine_config &config)
{
	// main cpu
	M68EC020(config, m_maincpu, amiga_state::CLK_28M_PAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &cd32_state::cd32_mem);
	m_maincpu->set_cpu_space(AS_PROGRAM);
	m_maincpu->reset_cb().set(FUNC(amiga_state::m68k_reset));

	amiga_base(config);

	ADDRESS_MAP_BANK(config, m_overlay).set_map(&cd32_state::overlay_2mb_map32).set_options(ENDIANNESS_BIG, 32, 22, 0x200000);
	ADDRESS_MAP_BANK(config, m_chipset).set_map(&cd32_state::aga_map).set_options(ENDIANNESS_BIG, 32, 9, 0x200);
	m_copper->set_ecs_mode(true);

	// disable floppy as default (available only via back port as expansion)
	subdevice<floppy_connector>("fdc:0")->set_default_option(nullptr);

	I2C_24C08(config, "i2cmem", 0); // AT24C08N

	akiko_device &akiko(AKIKO(config, "akiko", 0));
	akiko.mem_r_callback().set(FUNC(amiga_state::chip_ram_r));
	akiko.mem_w_callback().set(FUNC(amiga_state::chip_ram_w));
	akiko.int_callback().set(FUNC(cd32_state::akiko_int_w));
	akiko.scl_callback().set("i2cmem", FUNC(i2cmem_device::write_scl));
	akiko.sda_r_callback().set("i2cmem", FUNC(i2cmem_device::read_sda));
	akiko.sda_w_callback().set("i2cmem", FUNC(i2cmem_device::write_sda));

	m_screen->set_screen_update(FUNC(amiga_state::screen_update));

	MCFG_VIDEO_START_OVERRIDE(amiga_state, amiga_aga)

	m_cia_0->pa_wr_callback().set(FUNC(cd32_state::akiko_cia_0_port_a_write));
	m_cia_0->sp_wr_callback().set_nop();

	SOFTWARE_LIST(config, "cd32_list").set_original("cd32");
	SOFTWARE_LIST(config, "cd_list").set_compatible("cdtv");
}

void cd32_state::cd32n(machine_config &config)
{
	cd32(config);

	m_maincpu->set_clock(amiga_state::CLK_28M_NTSC / 2);
	config.device_remove("screen");
	ntsc_video(config);
	m_screen->set_screen_update(FUNC(amiga_state::screen_update));
	m_paula->set_clock(amiga_state::CLK_C1_NTSC);
	m_cia_0->set_clock(amiga_state::CLK_E_NTSC);
	m_cia_1->set_clock(amiga_state::CLK_E_NTSC);
	m_fdc->set_clock(amiga_state::CLK_7M_NTSC);
}

void a4000_state::a4000t(machine_config &config)
{
	a4000(config);
	// main cpu
	M68040(config.replace(), m_maincpu, XTAL(50'000'000) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &a4000_state::a4000t_mem);
	m_maincpu->set_cpu_space(AS_PROGRAM);

	// TODO: ide, zorro3, scsi, super dmac
}

void a4000_state::a4000tn(machine_config &config)
{
	a4000(config);

	config.device_remove("screen");
	ntsc_video(config);
	m_screen->set_screen_update(FUNC(amiga_state::screen_update));
	m_paula->set_clock(amiga_state::CLK_C1_NTSC);
	m_cia_0->set_clock(amiga_state::CLK_E_NTSC);
	m_cia_1->set_clock(amiga_state::CLK_E_NTSC);
	m_fdc->set_clock(amiga_state::CLK_7M_NTSC);
}


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

// Amiga 1000
//
// Shipped with a small bootrom to load kickstart from disk because the
// Kickstart wasn't finished in time. ROM type is 23256, but only the
// first 4kb of it are used.

ROM_START( a1000 )
	ROM_REGION16_BE(0x10000, "bootrom", 0)
	ROM_LOAD16_BYTE("252179-01.u5n", 0x0000, 0x8000, CRC(76bd46ec) SHA1(2155b4887f064c5e01e0a2ebb4a0cc2a3e88d9e8))
	ROM_LOAD16_BYTE("252180-01.u5p", 0x0001, 0x8000, CRC(dd516b6d) SHA1(2c307d02f10ad332a479b50767fd0463efc2844b))

	// PALs, all of type PAL16L8
	ROM_REGION(0x104, "dpalen", 0)
	ROM_LOAD("252128-01.u4t", 0, 0x104, CRC(28209ff2) SHA1(20c03b6b8e7254231f4b3014dc2c4d9274d469d2))
	ROM_REGION(0x104, "dpalcas", 0)
	ROM_LOAD("252128-02.u6p", 0, 0x104, CRC(b928efd2) SHA1(430794a544d9160e1b786e97e0dec5f25502a00a))
	ROM_REGION(0x104, "daugen", 0)
	ROM_LOAD("252128-03.u4s", 0, 0x104, CRC(87747964) SHA1(00d72ec707c582363525fde56176973c7327b1d7))
	ROM_REGION(0x104, "daugcas", 0)
	ROM_LOAD("252128-04.u6n", 0, 0x104, CRC(f903adb4) SHA1(4c8fb696fd1aaf9bb8c9efddeac24bb36f119c5f))
ROM_END

#define rom_a1000n  rom_a1000

// Amiga 2000 and Amiga 500
//
// Early models shipped with Kickstart 1.2, later versions with Kickstart 1.3.
// Kickstart 2.04 and 3.1 upgrade available. The Kickstart 2.04 upgrade was also
// available as a special version that included a jumper wire, which was needed
// for some early motherboard revisions (P/N: 363968-01).

ROM_START( a2000 )
	ROM_REGION16_BE(0x80000, "kickstart", 0)
	ROM_DEFAULT_BIOS("kick13")
	ROM_SYSTEM_BIOS(0, "kick12",  "Kickstart 1.2 (33.180)")
	ROMX_LOAD("315093-01.u2", 0x00000, 0x40000, CRC(a6ce1636) SHA1(11f9e62cf299f72184835b7b2a70a16333fc0d88), ROM_GROUPWORD | ROM_BIOS(0))
	ROM_COPY("kickstart", 0x00000, 0x40000, 0x40000)
	ROM_SYSTEM_BIOS(1, "kick13",  "Kickstart 1.3 (34.5)")
	ROMX_LOAD("315093-02.u2", 0x00000, 0x40000, CRC(c4f0f55f) SHA1(891e9a547772fe0c6c19b610baf8bc4ea7fcb785), ROM_GROUPWORD | ROM_BIOS(1))
	ROM_COPY("kickstart", 0x00000, 0x40000, 0x40000)
	ROM_SYSTEM_BIOS(2, "kick204", "Kickstart 2.04 (37.175)")
	ROMX_LOAD("390979-01.u2", 0x00000, 0x80000, CRC(c3bdb240) SHA1(c5839f5cb98a7a8947065c3ed2f14f5f42e334a1), ROM_GROUPWORD | ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "kick31",  "Kickstart 3.1 (40.63)")
	ROMX_LOAD("kick40063.u2", 0x00000, 0x80000, CRC(fc24ae0d) SHA1(3b7f1493b27e212830f989f26ca76c02049f09ca), ROM_GROUPWORD | ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "logica2", "Logica Diagnostic 2.0")
	ROMX_LOAD("logica2.u2",   0x00000, 0x80000, CRC(8484f426) SHA1(ba10d16166b2e2d6177c979c99edf8462b21651e), ROM_GROUPWORD | ROM_BIOS(4))
#if 0 // not enabled yet, kickstart 3.2 is new and actively sold
	ROM_SYSTEM_BIOS(5, "kick32",  "Kickstart 3.2 (47.96)")
	ROMX_LOAD("kick47096.u2", 0x00000, 0x80000, CRC(8173d7b6) SHA1(b88e364daf23c9c9920e548b0d3d944e65b1031d), ROM_GROUPWORD | ROM_BIOS(5))
	ROM_SYSTEM_BIOS(6, "kick321", "Kickstart 3.2 (47.102)")
	ROMX_LOAD("kick47102.u2", 0x00000, 0x80000, CRC(4f078456) SHA1(8f64ada68a7f128ba782e8dc9fa583344171590a), ROM_GROUPWORD | ROM_BIOS(6))
	ROM_SYSTEM_BIOS(7, "kick322", "Kickstart 3.2 (47.111)")
	ROMX_LOAD("kick47111.u2", 0x00000, 0x80000, CRC(e4458462) SHA1(7d5ebe686b69d59a863cc77a36b2cd60359a9ed2), ROM_GROUPWORD | ROM_BIOS(7))
#endif
ROM_END

// Amiga 2000CR chip location: U500
#define rom_a2000n  rom_a2000

// Amiga 500 chip location: U6
#define rom_a500   rom_a2000
#define rom_a500n  rom_a2000

// Amiga 500+
//
// Shipped with Kickstart 2.04. Kickstart 3.1 upgrade available.

ROM_START( a500p )
	ROM_REGION16_BE(0x80000, "kickstart", 0)
	ROM_DEFAULT_BIOS("kick204")
	ROM_SYSTEM_BIOS(0, "kick204", "Kickstart 2.04 (37.175)")
	ROMX_LOAD("390979-01.u6", 0x00000, 0x80000, CRC(c3bdb240) SHA1(c5839f5cb98a7a8947065c3ed2f14f5f42e334a1), ROM_GROUPWORD | ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "kick31",  "Kickstart 3.1 (40.63)")
	ROMX_LOAD("kick40063.u6", 0x00000, 0x80000, CRC(fc24ae0d) SHA1(3b7f1493b27e212830f989f26ca76c02049f09ca), ROM_GROUPWORD | ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "logica2", "Logica Diagnostic 2.0")
	ROMX_LOAD("logica2.u6",   0x00000, 0x80000, CRC(8484f426) SHA1(ba10d16166b2e2d6177c979c99edf8462b21651e), ROM_GROUPWORD | ROM_BIOS(2))
ROM_END

#define rom_a500pn  rom_a500p

// Commodore CDTV
//
// Shipped with a standard Kickstart 1.3 and the needed additional drivers
// in two extra chips.

ROM_START( cdtv )
	// cd-rom driver
	ROM_REGION16_BE(0x40000, "cdrom", 0)
	ROM_LOAD16_BYTE("391008-01.u34", 0x00000, 0x20000, CRC(791cb14b) SHA1(277a1778924496353ffe56be68063d2a334360e4))
	ROM_LOAD16_BYTE("391009-01.u35", 0x00001, 0x20000, CRC(accbbc2e) SHA1(41b06d1679c6e6933c3378b7626025f7641ebc5c))

	// standard amiga kickstart 1.3
	ROM_REGION16_BE(0x80000, "kickstart", 0)
	ROMX_LOAD("315093-02.u13", 0x00000, 0x40000, CRC(c4f0f55f) SHA1(891e9a547772fe0c6c19b610baf8bc4ea7fcb785), ROM_GROUPWORD)
	ROM_COPY("kickstart", 0x00000, 0x40000, 0x40000)

	// remote control input converter, mos 6500/1 mcu
	ROM_REGION(0x1000, "rcmcu", 0)
	ROM_LOAD("252609-02.u75", 0x000, 0x800, NO_DUMP) // internal ROM of the final version hasn't been dumped yet
	ROM_LOAD("v1.3-1990-10-01", 0x0000, 0x1000, CRC(3c7cb7bb) SHA1(958e799897ac044fcc0f0c74c3cb5d83f3edd0c7)) // this was dumped from a pre-production CD-1000 player which had the program in external EPROM

	// lcd controller, sanyo lc6554h
	ROM_REGION(0x2000, "lcd", 0)
	ROM_LOAD("252608-01.u62", 0x0000, 0x2000, NO_DUMP) // internal ROM of the final version hasn't been dumped yet
	ROM_LOAD("v1.20-1990-09-26", 0x0000, 0x2000, CRC(9d69c439) SHA1(74354818ffc4d897801be705ae223717f522f8d4)) // this was dumped from a pre-production CD-1000 player which had the program in external EPROM
ROM_END

#define rom_cdtvn  rom_cdtv

// Amiga 3000
//
// Early models have a special version of Kickstart 1.4/2.0 that boots
// Kickstart 1.3 or 2.0 from hard disk or floppy. Later versions have
// Kickstart 2.04 installed as ROM. Upgrade available for
// Kickstart 3.1.

ROM_START( a3000 )
	ROM_REGION32_BE(0x80000, "kickstart", 0)
	ROM_DEFAULT_BIOS("kick20")
	ROM_SYSTEM_BIOS(0, "kick14", "Kickstart 1.4 (3312.20085?)")
	ROMX_LOAD("390629-01.u182", 0x00000, 0x40000, NO_DUMP, ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(0))
	ROMX_LOAD("390630-01.u183", 0x00002, 0x40000, NO_DUMP, ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "kick20", "Kickstart 2.0 (36.16)")
	// COPYRIGHT 1990 CAI // ALL RIGHTS RESERVED // ALPHA 5 ROM 0 CS=9713
	ROMX_LOAD("390629-02.u182", 0x00000, 0x40000, CRC(58327536) SHA1(d1713d7f31474a5948e6d488e33686061cf3d1e2), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(1))
	// COPYRIGHT 1990 CAI // ALL RIGHTS RESERVED // ALPHA 5 ROM 1 CS=9B21
	ROMX_LOAD("390630-02.u183", 0x00002, 0x40000, CRC(fe2f7fb9) SHA1(c05c9c52d014c66f9019152b3f2a2adc2c678794), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "kick204", "Kickstart 2.04 (37.175)")
	ROMX_LOAD("390629-03.u182", 0x00000, 0x40000, CRC(a245dbdf) SHA1(83bab8e95d378b55b0c6ae6561385a96f638598f), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(2))
	ROMX_LOAD("390630-03.u183", 0x00002, 0x40000, CRC(7db1332b) SHA1(48f14b31279da6757848df6feb5318818f8f576c), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "kick31", "Kickstart 3.1 (40.68)")
	ROMX_LOAD("kick31.u182",    0x00000, 0x40000, CRC(286b9a0d) SHA1(6763a2258ec493f7408cf663110dae9a17803ad1), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(3))
	ROMX_LOAD("kick31.u183",    0x00002, 0x40000, CRC(0b8cde6a) SHA1(5f02e97b48ebbba87d516a56b0400c6fc3434d8d), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "logica2", "Logica Diagnostic 2.0")
	ROMX_LOAD("logica2.u182",    0x00000, 0x40000, CRC(566bc3f9) SHA1(891d3b7892843517d800d24593168b1d8f1646ca), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(4))
	ROMX_LOAD("logica2.u183",    0x00002, 0x40000, CRC(aac94759) SHA1(da8a4f9ae1aa84f5e2a5dcc5c9d7e4378a9698b7), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(4))
ROM_END

#define rom_a3000n  rom_a3000


// Amiga 600
//
// According to Greg Donner's Workbench page, very early models shipped with
// Kickstart 2.04.
//
// Kickstart 2.05 differences:
// - 2.05 37.299: No HDD support
// - 2.05 37.300: HDD support
// - 2.05 37.350: HDD size limits removed
//
// Kickstart 3.1 upgrade available.
//
// The keyboard controller is included on the motherboard, still based on the
// 6500/1.

ROM_START( a600 )
	ROM_REGION16_BE(0x80000, "kickstart", 0)
	ROM_DEFAULT_BIOS("kick205-350")
	ROM_SYSTEM_BIOS(0, "kick204", "Kickstart 2.04 (37.175)")
	ROMX_LOAD("390979-01.u6", 0x00000, 0x80000, CRC(c3bdb240) SHA1(c5839f5cb98a7a8947065c3ed2f14f5f42e334a1), ROM_GROUPWORD | ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "kick205-299", "Kickstart 2.05 (37.299)")
	ROMX_LOAD("391388-01.u6", 0x00000, 0x80000, CRC(83028fb5) SHA1(87508de834dc7eb47359cede72d2e3c8a2e5d8db), ROM_GROUPWORD | ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "kick205-300", "Kickstart 2.05 (37.300)")
	ROMX_LOAD("391304-01.u6", 0x00000, 0x80000, CRC(64466c2a) SHA1(f72d89148dac39c696e30b10859ebc859226637b), ROM_GROUPWORD | ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "kick205-350", "Kickstart 2.05 (37.350)")
	ROMX_LOAD("391304-02.u6", 0x00000, 0x80000, CRC(43b0df7b) SHA1(02843c4253bbd29aba535b0aa3bd9a85034ecde4), ROM_GROUPWORD | ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "kick31",  "Kickstart 3.1 (40.63)")
	ROMX_LOAD("kick40063.u6", 0x00000, 0x80000, CRC(fc24ae0d) SHA1(3b7f1493b27e212830f989f26ca76c02049f09ca), ROM_GROUPWORD | ROM_BIOS(4))
	ROM_SYSTEM_BIOS(5, "logica2", "Logica Diagnostic 2.0")
	ROMX_LOAD("logica2.u6",   0x00000, 0x80000, CRC(8484f426) SHA1(ba10d16166b2e2d6177c979c99edf8462b21651e), ROM_GROUPWORD | ROM_BIOS(5))

	ROM_REGION(0x800, "keyboard", 0)
	ROM_LOAD("391079-01.u13", 0x000, 0x800, NO_DUMP)
ROM_END

#define rom_a600n  rom_a600

// Amiga 1200
//
// Early models shipped with Kickstart 3.0, later versions with
// Kickstart 3.1. Keyboard controller is included on the motherboard,
// but was changed to a 68HC05 core.

ROM_START( a1200 )
	ROM_REGION32_BE(0x80000, "kickstart", 0)
	ROM_DEFAULT_BIOS("kick31")
	ROM_SYSTEM_BIOS(0, "kick30", "Kickstart 3.0 (39.106)")
	ROMX_LOAD("391523-01.u6a", 0x00000, 0x40000, CRC(c742a412) SHA1(999eb81c65dfd07a71ee19315d99c7eb858ab186), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(0))
	ROMX_LOAD("391524-01.u6b", 0x00002, 0x40000, CRC(d55c6ec6) SHA1(3341108d3a402882b5ef9d3b242cbf3c8ab1a3e9), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "kick31", "Kickstart 3.1 (40.68)")
	ROMX_LOAD("391773-01.u6a", 0x00000, 0x40000, CRC(08dbf275) SHA1(b8800f5f909298109ea69690b1b8523fa22ddb37), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(1))
	ROMX_LOAD("391774-01.u6b", 0x00002, 0x40000, CRC(16c07bf8) SHA1(90e331be1970b0e53f53a9b0390b51b59b3869c2), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "logica2", "Logica Diagnostic 2.0")
	ROMX_LOAD("logica2.u6a", 0x00000, 0x40000, CRC(566bc3f9) SHA1(891d3b7892843517d800d24593168b1d8f1646ca), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(2))
	ROMX_LOAD("logica2.u6b", 0x00002, 0x40000, CRC(aac94759) SHA1(da8a4f9ae1aa84f5e2a5dcc5c9d7e4378a9698b7), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(2))
ROM_END

#define rom_a1200n  rom_a1200

// Amiga 4000
//
// Shipped with Kickstart 3.0, upgradable to Kickstart 3.1.

ROM_START( a4000 )
	ROM_REGION32_BE(0x80000, "kickstart", 0)
	ROM_DEFAULT_BIOS("kick30")
	ROM_SYSTEM_BIOS(0, "kick30", "Kickstart 3.0 (39.106)")
	ROMX_LOAD("391513-02.u175", 0x00000, 0x40000, CRC(36f64dd0) SHA1(196e9f3f9cad934e181c07da33083b1f0a3c702f), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(0))
	ROMX_LOAD("391514-02.u176", 0x00002, 0x40000, CRC(17266a55) SHA1(42fbed3453d1f11ccbde89a9826f2d1175cca5cc), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "kick31-68", "Kickstart 3.1 (40.68)")
	ROMX_LOAD("kick40068.u175", 0x00000, 0x40000, CRC(b2af34f8) SHA1(24e52b5efc02049517387ab7b1a1475fc540350e), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(1))
	ROMX_LOAD("kick40068.u176", 0x00002, 0x40000, CRC(e65636a3) SHA1(313c7cbda5779e56f19a41d34e760f517626d882), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "kick31-70", "Kickstart 3.1 (40.70)")
	ROMX_LOAD("kick40070.u175", 0x00000, 0x40000, CRC(f9cbecc9) SHA1(138d8cb43b8312fe16d69070de607469b3d4078e), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(2))
	ROMX_LOAD("kick40070.u176", 0x00002, 0x40000, CRC(f8248355) SHA1(c23795479fae3910c185512ca268b82f1ae4fe05), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "logica2", "Logica Diagnostic 2.0")
	ROMX_LOAD("logica2.u6a",    0x00000, 0x40000, CRC(566bc3f9) SHA1(891d3b7892843517d800d24593168b1d8f1646ca), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(3))
	ROMX_LOAD("logica2.u6b",    0x00002, 0x40000, CRC(aac94759) SHA1(da8a4f9ae1aa84f5e2a5dcc5c9d7e4378a9698b7), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(3))
ROM_END

#define rom_a4000n    rom_a4000
#define rom_a400030   rom_a4000
#define rom_a400030n  rom_a4000

// Amiga 4000T
//
// Shipped with Kickstart 3.1 (40.70).

ROM_START( a4000t )
	ROM_REGION32_BE(0x80000, "kickstart", 0)
	ROM_DEFAULT_BIOS("kick31")
	ROM_SYSTEM_BIOS(0, "kick31", "Kickstart 3.1 (40.70)")
	ROMX_LOAD("391657-01.u175", 0x00000, 0x40000, CRC(0ca94f70) SHA1(b3806edacb3362fc16a154ce1eeec5bf5bc24789), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(0))
	ROMX_LOAD("391658-01.u176", 0x00002, 0x40000, CRC(dfe03120) SHA1(cd7a706c431b04d87814d3a2d8b397100cf44c0c), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "logica2", "Logica Diagnostic 2.0")
	ROMX_LOAD("logica2.u6a",    0x00000, 0x40000, CRC(566bc3f9) SHA1(891d3b7892843517d800d24593168b1d8f1646ca), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(1))
	ROMX_LOAD("logica2.u6b",    0x00002, 0x40000, CRC(aac94759) SHA1(da8a4f9ae1aa84f5e2a5dcc5c9d7e4378a9698b7), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(1))
ROM_END

#define rom_a4000tn  rom_a4000t

// Amiga CD32
//
// Shipped with Kickstart 3.1 and additional software interleaved in a 1MB rom chip.

ROM_START( cd32 )
	ROM_REGION32_BE(0x100000, "kickstart", 0)
	// TODO: this is the real dump
//  ROM_LOAD16_WORD("391640-03.u6a", 0x000000, 0x100000, CRC(a4fbc94a) SHA1(816ce6c5077875850c7d43452230a9ba3a2902db))
	ROM_LOAD16_WORD("391640-03.u6a", 0x000000, 0x100000, CRC(d3837ae4) SHA1(06807db3181637455f4d46582d9972afec8956d9))
ROM_END

#define rom_cd32n  rom_cd32


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

// OCS Chipset
COMP( 1985, a1000,    0,      0, a1000,    amiga, a1000_state, init_pal,  "Commodore", "Amiga 1000 (PAL)",      MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1985, a1000n,   a1000,  0, a1000n,   amiga, a1000_state, init_ntsc, "Commodore", "Amiga 1000 (NTSC)",     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1987, a2000,    0,      0, a2000,    amiga, a2000_state, init_pal,  "Commodore", "Amiga 2000 (PAL)",      MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1987, a2000n,   a2000,  0, a2000n,   amiga, a2000_state, init_ntsc, "Commodore", "Amiga 2000 (NTSC)",     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1987, a500,     0,      0, a500,     amiga, a500_state,  init_pal,  "Commodore", "Amiga 500 (PAL)",       MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1987, a500n,    a500,   0, a500n,    amiga, a500_state,  init_ntsc, "Commodore", "Amiga 500 (NTSC)",      MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1990, cdtv,     0,      0, cdtv,     amiga, cdtv_state,  init_pal,  "Commodore", "CDTV (PAL)",            MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1990, cdtvn,    cdtv,   0, cdtvn,    amiga, cdtv_state,  init_ntsc, "Commodore", "CDTV (NTSC)",           MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )

// ECS Chipset
COMP( 1990, a3000,    0,      0, a3000,    amiga, a3000_state, init_pal,  "Commodore", "Amiga 3000 (PAL)",      MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1990, a3000n,   a3000,  0, a3000n,   amiga, a3000_state, init_ntsc, "Commodore", "Amiga 3000 (NTSC)",     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1992, a500p,    0,      0, a500p,    amiga, a500p_state, init_pal,  "Commodore", "Amiga 500 Plus (PAL)",  MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1992, a500pn,   a500p,  0, a500pn,   amiga, a500p_state, init_ntsc, "Commodore", "Amiga 500 Plus (NTSC)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1992, a600,     0,      0, a600,     amiga, a600_state,  init_pal,  "Commodore", "Amiga 600 (PAL)",       MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1992, a600n,    a600,   0, a600n,    amiga, a600_state,  init_ntsc, "Commodore", "Amiga 600 (NTSC)",      MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )

// AGA Chipset
COMP( 1992, a1200,    0,      0, a1200,    amiga, a1200_state, init_pal,  "Commodore", "Amiga 1200 (PAL)",      MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1992, a1200n,   a1200,  0, a1200n,   amiga, a1200_state, init_ntsc, "Commodore", "Amiga 1200 (NTSC)",     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1992, a4000,    0,      0, a4000,    amiga, a4000_state, init_pal,  "Commodore", "Amiga 4000/040 (PAL)",  MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1992, a4000n,   a4000,  0, a4000n,   amiga, a4000_state, init_ntsc, "Commodore", "Amiga 4000/040 (NTSC)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1993, a400030,  a4000,  0, a400030,  amiga, a4000_state, init_pal,  "Commodore", "Amiga 4000/030 (PAL)",  MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1993, a400030n, a4000,  0, a400030n, amiga, a4000_state, init_ntsc, "Commodore", "Amiga 4000/030 (NTSC)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1993, cd32,     0,      0, cd32,     cd32,  cd32_state,  init_pal,  "Commodore", "Amiga CD32 (PAL)",      MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1993, cd32n,    cd32,   0, cd32n,    cd32,  cd32_state,  init_ntsc, "Commodore", "Amiga CD32 (NTSC)",     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1994, a4000t,   0,      0, a4000t,   amiga, a4000_state, init_pal,  "Commodore", "Amiga 4000T (PAL)",     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1994, a4000tn,  a4000t, 0, a4000tn,  amiga, a4000_state, init_ntsc, "Commodore", "Amiga 4000T (NTSC)",    MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )



ampex.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

2017-11-05 Skeleton

Ampex Dialogue 80 terminal

Chips: CRT-5037, COM8017, SMC (COM)5016-5, MK3880N (Z80), SN74LS424N (TIM8224)
Crystals: 4.9152, 23.814
Other: Beeper, 5x 10sw-dips.

The program code seems to have been designed with a 8080 CPU in mind, using no
Z80-specific opcodes. This impression is reinforced by the IC types present on
the PCB, which go so far as to include the standard 8224 clock generator.

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

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/ay31015.h"
#include "machine/com8116.h"
#include "video/tms9927.h"
#include "screen.h"


namespace {

#define AMPEX_CH_WIDTH 7

class ampex_state : public driver_device
{
public:
	ampex_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_vtac(*this, "vtac")
		, m_uart(*this, "uart")
		, m_dbrg(*this, "dbrg")
		, m_p_chargen(*this, "chargen")
	{ }

	void ampex(machine_config &config);

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void write_54xx(u8 data);
	u8 read_5840();
	void write_5840(u8 data);
	u8 read_5841();
	void write_5841(u8 data);
	u8 read_5842();
	void write_5843(u8 data);
	u8 read_5846();
	u8 read_5847();

	u8 page_r(offs_t offset);
	void page_w(offs_t offset, u8 data);

	void vsyn_w(int state);
	void so_w(int state);
	void dav_w(int state);

	void mem_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;

	u8 m_page = 0;
	u8 m_attr = 0;
	bool m_attr_readback = false;
	bool m_uart_loopback = false;
	std::unique_ptr<u16[]> m_paged_ram{};

	required_device<cpu_device> m_maincpu;
	required_device<crt5037_device> m_vtac;
	required_device<ay31015_device> m_uart;
	required_device<com8116_device> m_dbrg;
	required_region_ptr<u8> m_p_chargen;
};

u32 ampex_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void ampex_state::write_54xx(u8 data)
{
	// Written during keyboard polling
}

u8 ampex_state::read_5840()
{
	logerror("%s: Read from 5840\n", machine().describe_context());
	return 0;
}

void ampex_state::write_5840(u8 data)
{
	m_page = (data & 0x30) >> 4;

	logerror("%s: Write %02X to 5840\n", machine().describe_context(), data);
}

u8 ampex_state::read_5841()
{
	u8 result = m_uart->dav_r() << 3;
	result |= m_uart->or_r() << 4;
	result |= m_uart->pe_r() << 5;
	result |= m_uart->fe_r() << 6;
	return result;
}

void ampex_state::write_5841(u8 data)
{
	m_uart_loopback = BIT(data, 7);
	m_attr_readback = BIT(data, 5);
}

u8 ampex_state::read_5842()
{
	//logerror("%s: Read from 5842\n", machine().describe_context());
	return 0;
}

void ampex_state::write_5843(u8 data)
{
	//logerror("%s: Write %02X to 5843\n", machine().describe_context(), data);
	m_attr = (data & 0x78) >> 3;
}

u8 ampex_state::read_5846()
{
	// probably acknowledges RST 6 interrupt (value not used)
	return 0;
}

u8 ampex_state::read_5847()
{
	// acknowledges RST 4/5 interrupt (value not used)
	return 0;
}

u8 ampex_state::page_r(offs_t offset)
{
	if (m_attr_readback)
		return 0x87 | m_paged_ram[m_page * 0x1800 + offset] >> 5;
	else
		return 0xff & m_paged_ram[m_page * 0x1800 + offset];
}

void ampex_state::page_w(offs_t offset, u8 data)
{
	m_paged_ram[m_page * 0x1800 + offset] = data | m_attr << 8;
}

void ampex_state::vsyn_w(int state)
{
	// should generate RST 6 interrupt
}

void ampex_state::so_w(int state)
{
	if (m_uart_loopback)
		m_uart->write_si(state);
}

void ampex_state::dav_w(int state)
{
	// DAV should generate RST 7
}

void ampex_state::mem_map(address_map &map)
{
	map(0x0000, 0x2fff).rom().region("roms", 0);
	map(0x4000, 0x43ff).ram(); // main RAM
	map(0x4400, 0x53ff).ram(); // expansion RAM
	map(0x5400, 0x5400).portr("IN0");
	map(0x5401, 0x5401).portr("IN1");
	map(0x5402, 0x5402).portr("IN2");
	map(0x5403, 0x5403).portr("IN3");
	map(0x5404, 0x5404).portr("IN4");
	map(0x5405, 0x5405).portr("IN5");
	map(0x5406, 0x5406).portr("IN6");
	map(0x5407, 0x5407).portr("IN7");
	map(0x5408, 0x5408).portr("IN8");
	map(0x5409, 0x5409).portr("IN9");
	map(0x540a, 0x540a).portr("INA");
	map(0x540b, 0x540b).portr("INB");
	map(0x540c, 0x540c).portr("INC");
	map(0x5400, 0x54ff).w(FUNC(ampex_state::write_54xx));
	map(0x5840, 0x5840).rw(FUNC(ampex_state::read_5840), FUNC(ampex_state::write_5840));
	map(0x5841, 0x5841).rw(FUNC(ampex_state::read_5841), FUNC(ampex_state::write_5841));
	map(0x5842, 0x5842).r(FUNC(ampex_state::read_5842)).w(m_uart, FUNC(ay31015_device::transmit));
	map(0x5843, 0x5843).r(m_uart, FUNC(ay31015_device::receive)).w(FUNC(ampex_state::write_5843));
	map(0x5846, 0x5846).r(FUNC(ampex_state::read_5846));
	map(0x5847, 0x5847).r(FUNC(ampex_state::read_5847));
	map(0x5c00, 0x5c0f).rw(m_vtac, FUNC(crt5037_device::read), FUNC(crt5037_device::write));
	map(0x8000, 0x97ff).rw(FUNC(ampex_state::page_r), FUNC(ampex_state::page_w));
	map(0xc000, 0xcfff).ram(); // video RAM
}

static INPUT_PORTS_START( ampex )
	PORT_START("IN0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('\\') PORT_CHAR('|') PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR('"') PORT_CODE(KEYCODE_2)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR('$') PORT_CODE(KEYCODE_4)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR('&') PORT_CODE(KEYCODE_6)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR('\'') PORT_CODE(KEYCODE_7)

	PORT_START("IN1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(0x1b) PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('q') PORT_CHAR('Q') PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('w') PORT_CHAR('W') PORT_CODE(KEYCODE_W)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('e') PORT_CHAR('E') PORT_CODE(KEYCODE_E)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('r') PORT_CHAR('R') PORT_CODE(KEYCODE_R)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('t') PORT_CHAR('T') PORT_CODE(KEYCODE_T)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('y') PORT_CHAR('Y') PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('u') PORT_CHAR('U') PORT_CODE(KEYCODE_U)

	PORT_START("IN2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CHAR(UCHAR_SHIFT_2) PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('a') PORT_CHAR('A') PORT_CODE(KEYCODE_A)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('s') PORT_CHAR('S') PORT_CODE(KEYCODE_S)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('d') PORT_CHAR('D') PORT_CODE(KEYCODE_D)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('f') PORT_CHAR('F') PORT_CODE(KEYCODE_F)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('g') PORT_CHAR('G') PORT_CODE(KEYCODE_G)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('h') PORT_CHAR('H') PORT_CODE(KEYCODE_H)

	PORT_START("IN3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNKNOWN) // 0x83 (Break?)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift (Left)") PORT_CHAR(UCHAR_SHIFT_1) PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('z') PORT_CHAR('Z') PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('x') PORT_CHAR('X') PORT_CODE(KEYCODE_X)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('c') PORT_CHAR('C') PORT_CODE(KEYCODE_C)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('v') PORT_CHAR('V') PORT_CODE(KEYCODE_V)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('b') PORT_CHAR('B') PORT_CODE(KEYCODE_B)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('n') PORT_CHAR('N') PORT_CODE(KEYCODE_N)

	PORT_START("IN4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_M)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(',') PORT_CHAR('<') PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('.') PORT_CHAR('>') PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('/') PORT_CHAR('?') PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift (Right)") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(' ') PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('j') PORT_CHAR('J') PORT_CODE(KEYCODE_J)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('k') PORT_CHAR('K') PORT_CODE(KEYCODE_K)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('l') PORT_CHAR('L') PORT_CODE(KEYCODE_L)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHAR('+') PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('[') PORT_CHAR('{') PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(']') PORT_CHAR('}') PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNKNOWN) // 0x1f (shifted: 0x8a) (Page New Line?)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNKNOWN) // 0x94 (Line Ins?)

	PORT_START("IN6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('i') PORT_CHAR('I') PORT_CODE(KEYCODE_I)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('o') PORT_CHAR('O') PORT_CODE(KEYCODE_O)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('p') PORT_CHAR('P') PORT_CODE(KEYCODE_P)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('^') PORT_CHAR('~') PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(0x0a) PORT_CODE(KEYCODE_RALT) PORT_NAME("Line Feed")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(0x0d) PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNKNOWN) // 0xd4 (shifted: 0xf4)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNKNOWN) // 0xd9 (shifted: 0xf9)

	PORT_START("IN7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('8') PORT_CHAR('(') PORT_CODE(KEYCODE_8)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('9') PORT_CHAR(')') PORT_CODE(KEYCODE_9)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('0') PORT_CHAR('_') PORT_CODE(KEYCODE_0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(':') PORT_CHAR('*') PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('-') PORT_CHAR('=') PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('@') PORT_CHAR('`') PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(0x09) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNKNOWN) // 0x90 (shifted: 0x84)

	PORT_START("IN8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD)) PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD)) PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD)) PORT_CODE(KEYCODE_COMMA_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNKNOWN) // 0x97 (Char Del?)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNKNOWN) // 0x96 (Char Ins?)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNKNOWN) // 0x0d (top half of keypad Enter?)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD)) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Del") PORT_CODE(KEYCODE_RCONTROL) // 0x7f
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("INA")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(TAB_PAD)) PORT_CODE(KEYCODE_TAB_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD)) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD)) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNKNOWN) // 0x95 (Line Del?)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("INB")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD)) PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD)) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNKNOWN) // 0x8b (shifted: 0x8c) (Clear?)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNKNOWN) // 0xd0 (Page Erase?)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNKNOWN) // 0xb4 (shifted: 0xb6) (Line Erase?)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("INC")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNKNOWN) // 0x87 (shifted: 0x86)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNKNOWN) // 0x85 (shifted: 0x84)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNKNOWN) // 0x89 (shifted: 0x88)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNKNOWN) // 0x92 (shifted: 0x93)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNKNOWN) // 0x91
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNKNOWN) // 0xb5 (shifted: 0xb7)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

void ampex_state::machine_start()
{
	m_page = 0;
	m_attr = 0;
	m_attr_readback = false;
	m_uart_loopback = false;
	m_paged_ram = std::make_unique<u16[]>(0x1800 * 4);

	m_uart->write_swe(0);

	// Are rates hardwired to DIP switches? They don't seem to be software-controlled...
	m_dbrg->str_w(0xe);
	m_dbrg->stt_w(0xe);

	// Make up some settings for the UART (probably also actually controlled by DIP switches)
	m_uart->write_nb1(1);
	m_uart->write_nb2(1);
	m_uart->write_np(1);
	m_uart->write_eps(1);
	m_uart->write_tsb(0);
	m_uart->write_cs(1);

	save_item(NAME(m_page));
	save_item(NAME(m_attr));
	save_item(NAME(m_attr_readback));
	save_item(NAME(m_uart_loopback));
	save_pointer(NAME(m_paged_ram), 0x1800 * 4);
}

void ampex_state::ampex(machine_config &config)
{
	Z80(config, m_maincpu, 23.814_MHz_XTAL / 9); // clocked by 8224?
	m_maincpu->set_addrmap(AS_PROGRAM, &ampex_state::mem_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(23.814_MHz_XTAL / 2, 105 * AMPEX_CH_WIDTH, 0, 80 * AMPEX_CH_WIDTH, 270, 0, 250);
	screen.set_screen_update(FUNC(ampex_state::screen_update));

	CRT5037(config, m_vtac, 23.814_MHz_XTAL / 2 / AMPEX_CH_WIDTH);
	m_vtac->set_char_width(AMPEX_CH_WIDTH);
	m_vtac->vsyn_callback().set(FUNC(ampex_state::vsyn_w));
	m_vtac->set_screen("screen");

	AY31015(config, m_uart, 0); // COM8017, actually
	m_uart->write_so_callback().set(FUNC(ampex_state::so_w));
	m_uart->write_dav_callback().set(FUNC(ampex_state::dav_w));
	m_uart->set_auto_rdav(true);

	COM5016_5(config, m_dbrg, 4.9152_MHz_XTAL);
	m_dbrg->fr_handler().set(m_uart, FUNC(ay31015_device::write_rcp));
	m_dbrg->ft_handler().set(m_uart, FUNC(ay31015_device::write_tcp));
}

ROM_START( dialog80 )
	ROM_REGION( 0x3000, "roms", 0 )
	ROM_LOAD( "3505240-01.u102", 0x0000, 0x0800, CRC(c5315780) SHA1(f2a8924f277d04bf4407f9b71b8d2788df0b1dc2) )
	ROM_LOAD( "3505240-02.u104", 0x0800, 0x0800, CRC(3fefa114) SHA1(d83c00605ae6c02d3aac7b572eb2bf615f0d4f3a) )
	ROM_LOAD( "3505240-03.u103", 0x1000, 0x0800, CRC(03abbcb2) SHA1(e5d382eefc3baff8f3e4d6b13219cb5eb1ca32f2) )
	ROM_LOAD( "3505240-04.u105", 0x1800, 0x0800, CRC(c051e15f) SHA1(16a066c39743ddf9a7da54bb8c03e2090d461862) )
	ROM_LOAD( "3505240-05.u100", 0x2000, 0x0800, CRC(6db6365b) SHA1(a68c83e554c2493645287e369749a07474723452) )
	ROM_LOAD( "3505240-06.u101", 0x2800, 0x0800, CRC(8f9a4969) SHA1(f9cd434f8d287c584cda429b45ca2537fdfb436b) )

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "3505240-07.u69",  0x0000, 0x0800, CRC(838a16cb) SHA1(4301324b9fe9453c2d277972f9464c4214c6793d) )

	ROM_REGION( 0x0200, "proms", 0 ) // unknown TI 16-pin DIPs
	ROM_LOAD( "417129-010.u16",  0x0000, 0x0100, NO_DUMP )
	ROM_LOAD( "417129-010.u87",  0x0100, 0x0100, NO_DUMP )
ROM_END

} // anonymous namespace


COMP( 1980, dialog80, 0, 0, ampex, ampex, ampex_state, empty_init, "Ampex", "Dialogue 80", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



ampex210.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for Ampex 210(+) and 230(+) video display terminals.

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

#include "emu.h"
#include "cpu/z80/z80.h"
#include "ampex210_kbd.h"
#include "machine/mos6551.h"
#include "machine/nvram.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "video/scn2674.h"
#include "screen.h"


namespace {

class ampex210_state : public driver_device
{
public:
	ampex210_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_chargen(*this, "chargen")
	{ }

	void ampex210p(machine_config &config);
	void ampex230p(machine_config &config);

private:
	void common_video(machine_config &config);

	SCN2672_DRAW_CHARACTER_MEMBER(draw_character);

	u8 keyboard_r();
	u8 keyboard_reset_r();
	u8 keyboard_extra_r();
	void flags1_w(u8 data);
	void flags2_w(u8 data);
	void flags3_w(u8 data);
	u8 modem_r();
	void modem_w(u8 data);

	void ampex210_mem(address_map &map) ATTR_COLD;
	void ampex230_mem(address_map &map) ATTR_COLD;
	void ampex210_io(address_map &map) ATTR_COLD;
	void ampex230_io(address_map &map) ATTR_COLD;
	void vram_map(address_map &map) ATTR_COLD;
	void vram2_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_region_ptr<u8> m_chargen;
};


SCN2672_DRAW_CHARACTER_MEMBER(ampex210_state::draw_character)
{
}

u8 ampex210_state::keyboard_r()
{
	return 0xff;
}

u8 ampex210_state::keyboard_reset_r()
{
	// data ignored
	return 0xff;
}

u8 ampex210_state::keyboard_extra_r()
{
	return 0;
}

void ampex210_state::flags1_w(u8 data)
{
}

void ampex210_state::flags2_w(u8 data)
{
}

void ampex210_state::flags3_w(u8 data)
{
}

u8 ampex210_state::modem_r()
{
	return 0;
}

void ampex210_state::modem_w(u8 data)
{
}

void ampex210_state::ampex210_mem(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("maincpu", 0);
	map(0x8000, 0x87ff).ram().share("nvram");
	map(0x8800, 0x9fff).ram();
	map(0xc000, 0xc000).r(FUNC(ampex210_state::modem_r));
}

void ampex210_state::ampex230_mem(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("maincpu", 0);
	map(0x8000, 0x8007).rw("pvtc", FUNC(scn2672_device::read), FUNC(scn2672_device::write));
	map(0xa000, 0xbfff).ram();
	map(0xc000, 0xdfff).ram().share("nvram");
}

void ampex210_state::ampex210_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x07).rw("pvtc", FUNC(scn2672_device::read), FUNC(scn2672_device::write));
	map(0x44, 0x47).rw("acia", FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x80, 0x80).w(FUNC(ampex210_state::modem_w));
	map(0xc0, 0xc0).w("pvtc", FUNC(scn2672_device::buffer_w));
	map(0xc1, 0xc1).r("pvtc", FUNC(scn2672_device::buffer_r));
	map(0xc2, 0xc2).r(FUNC(ampex210_state::keyboard_r));
	map(0xc3, 0xc3).r(FUNC(ampex210_state::keyboard_reset_r));
	map(0xc4, 0xc4).w(FUNC(ampex210_state::flags1_w));
	map(0xc5, 0xc5).w(FUNC(ampex210_state::flags2_w));
	map(0xc6, 0xc6).r(FUNC(ampex210_state::keyboard_extra_r));
	map(0xc7, 0xc7).w(FUNC(ampex210_state::flags3_w));
}

void ampex210_state::ampex230_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x10, 0x10).w("pvtc", FUNC(scn2672_device::buffer_w));
	map(0x11, 0x11).r("pvtc", FUNC(scn2672_device::buffer_r));
	map(0x12, 0x12).w("pvtc", FUNC(scn2672_device::attr_buffer_w));
	map(0x13, 0x13).r("pvtc", FUNC(scn2672_device::attr_buffer_r));
	map(0x15, 0x15).r(FUNC(ampex210_state::keyboard_extra_r));
	map(0x16, 0x16).r(FUNC(ampex210_state::keyboard_r));
	map(0x17, 0x17).r(FUNC(ampex210_state::keyboard_reset_r));
	map(0x20, 0x20).w(FUNC(ampex210_state::flags1_w));
	map(0x21, 0x21).w(FUNC(ampex210_state::flags3_w));
	map(0x22, 0x22).w(FUNC(ampex210_state::flags2_w));
	map(0x30, 0x33).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x40, 0x43).rw("dart", FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
}

void ampex210_state::vram_map(address_map &map)
{
	map(0x0000, 0x1fff).ram(); // MB8464A-10L (second half unused?)
}

void ampex210_state::vram2_map(address_map &map)
{
	map(0x0000, 0x1fff).ram();
}


static INPUT_PORTS_START(ampex210p)
INPUT_PORTS_END


void ampex210_state::ampex210p(machine_config &config)
{
	Z80(config, m_maincpu, 3.6864_MHz_XTAL); // Z80ACPU; clock uncertain
	m_maincpu->set_addrmap(AS_PROGRAM, &ampex210_state::ampex210_mem);
	m_maincpu->set_addrmap(AS_IO, &ampex210_state::ampex210_io);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // UM6116-2

	mos6551_device &acia(MOS6551(config, "acia", 3.6864_MHz_XTAL / 2)); // AMI S6551AP
	acia.irq_handler().set_inputline("maincpu", INPUT_LINE_IRQ0);

	scn2672_device &pvtc(SCN2672(config, "pvtc", 19.602_MHz_XTAL / 9)); // MC2672B4P
	pvtc.intr_callback().set_inputline("maincpu", INPUT_LINE_NMI);
	pvtc.set_character_width(9);
	pvtc.set_display_callback(FUNC(ampex210_state::draw_character));
	pvtc.set_addrmap(0, &ampex210_state::vram_map);
	pvtc.set_screen("screen");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::amber());
	screen.set_raw(19.602_MHz_XTAL, 900, 0, 720, 363, 0, 300);
	//screen.set_raw(32.147_MHz_XTAL, 1476, 0, 1188, 363, 0, 300);
	screen.set_screen_update("pvtc", FUNC(scn2672_device::screen_update));
}

void ampex210_state::ampex230p(machine_config &config)
{
	Z80(config, m_maincpu, 3.6864_MHz_XTAL); // Z80ACPU; clock uncertain
	m_maincpu->set_addrmap(AS_PROGRAM, &ampex210_state::ampex230_mem);
	m_maincpu->set_addrmap(AS_IO, &ampex210_state::ampex230_io);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	z80ctc_device &ctc(Z80CTC(config, "ctc", 3.6864_MHz_XTAL)); // Z8430AB1
	ctc.set_clk<0>(3.6864_MHz_XTAL / 2);
	ctc.set_clk<1>(3.6864_MHz_XTAL / 2);
	ctc.set_clk<2>(3.6864_MHz_XTAL / 2);
	ctc.zc_callback<0>().set("dart", FUNC(z80dart_device::rxca_w));
	ctc.zc_callback<1>().set("dart", FUNC(z80dart_device::txca_w));
	ctc.zc_callback<2>().set("dart", FUNC(z80dart_device::rxtxcb_w));

	z80dart_device &dart(Z80DART(config, "dart", 3.6864_MHz_XTAL)); // Z80847004PSC
	dart.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0); // IM 1 autovectored

	AMPEX230_KEYBOARD(config, "keyboard");

	scn2672_device &pvtc(SCN2672(config, "pvtc", 19.602_MHz_XTAL / 9)); // MC2672B4P
	pvtc.intr_callback().set_inputline("maincpu", INPUT_LINE_NMI);
	pvtc.set_character_width(9);
	pvtc.set_display_callback(FUNC(ampex210_state::draw_character));
	pvtc.set_addrmap(0, &ampex210_state::vram_map);
	pvtc.set_addrmap(1, &ampex210_state::vram2_map);
	pvtc.set_screen("screen");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::amber());
	screen.set_raw(19.602_MHz_XTAL, 900, 0, 720, 363, 0, 312);
	//screen.set_raw(32.147_MHz_XTAL, 1476, 0, 1188, 363, 0, 312);
	screen.set_screen_update("pvtc", FUNC(scn2672_device::screen_update));
}


ROM_START(ampex210p) // Z80 (+6551,MC2672,3515260-01, 3 xtals, speaker) // 8k ram // amber
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD("35-5960-03.u30", 0x0000, 0x8000, CRC(d3f86117) SHA1(f8a9b66899117b362b195bfc94c75bc902fb1376))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("35-526-01.u3", 0x0000, 0x1000, CRC(4659bcd2) SHA1(554574f55ed875baba0a6133648c44df763cc5c4))
ROM_END

ROM_START(ampex230p) // EPROMs stickered "© 1989 AMPEX CORP."
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD("230_u11.bin", 0x0000, 0x8000, CRC(c8f93719) SHA1(81019b42245ca60c7de3ee5d3194c4d22fd38a8d))

	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD("230_u2.bin", 0x0000, 0x2000, CRC(7143b773) SHA1(616d3c0c1a1f7a00bf16857324043955ab842994))
ROM_END

} // anonymous namespace


COMP(1988, ampex210p, 0, 0, ampex210p, ampex210p, ampex210_state, empty_init, "Ampex", "Ampex 210 plus Terminal (v3.0)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP(1988, ampex230p, 0, 0, ampex230p, ampex210p, ampex210_state, empty_init, "Ampex", "Ampex 230 plus Terminal (v4.0)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



ampro.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Ampro Little Z80 Board

2013-09-02 Skeleton driver.

Chips: Z80A @4MHz, Z80CTC, Z80DART, WD1770/2, NCR5380. Crystal: 16 MHz

This is a complete CP/M single-board computer that could be mounted
on top of a standard 13cm floppy drive. You needed to supply your own
power supply and serial terminal.

The later versions included a SCSI chip (NCR5380) enabling the use
of a hard drive of up to 88MB.

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

#include "emu.h"
#include "bus/centronics/ctronics.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/ncr5380.h"
#include "bus/nscsi/devices.h"
#include "machine/output_latch.h"
#include "machine/z80daisy.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "machine/wd_fdc.h"
#include "machine/timer.h"
#include "softlist_dev.h"


namespace {

class ampro_state : public driver_device
{
public:
	ampro_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_bank1(*this, "bank1")
		, m_dart(*this, "dart")
		, m_ctc(*this, "ctc")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0U)
		, m_ncr(*this, "scsi:7:ncr")
		, m_printer(*this, "printer")
	{ }

	void ampro(machine_config &config);

private:
	TIMER_DEVICE_CALLBACK_MEMBER(ctc_tick);
	void port00_w(uint8_t data);
	void set_strobe(uint8_t data);
	void clear_strobe(uint8_t data);
	uint8_t ctc_r(offs_t offset);
	uint8_t dart_r(offs_t offset);
	void ctc_w(offs_t offset, uint8_t data);
	void dart_w(offs_t offset, uint8_t data);
	void machine_reset() override ATTR_COLD;
	void machine_start() override ATTR_COLD;

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_memory_bank    m_bank1;
	required_device<z80dart_device> m_dart;
	required_device<z80ctc_device> m_ctc;
	required_device<wd1772_device> m_fdc;
	required_device_array<floppy_connector, 4> m_floppy;
	required_device<ncr5380_device> m_ncr;
	required_device<centronics_device> m_printer;
};

/*
d0..d3 Drive select 0-3
d4     Side select 0=side0
d5     /DDEN
d6     Banking 0=rom
d7     FDC master clock 0=8MHz 1=16MHz (for 20cm disks, not emulated)
*/
void ampro_state::port00_w(uint8_t data)
{
	m_bank1->set_entry(BIT(~data, 6));
	m_fdc->dden_w(BIT(data, 5));
	floppy_image_device *floppy = nullptr;
	for (int n = 0; n < 4; n++)
		if (BIT(data, n))
			floppy = m_floppy[n]->get_device();
	m_fdc->set_floppy(floppy);
	if (floppy)
		floppy->ss_w(BIT(data, 4));
}

void ampro_state::set_strobe(uint8_t data)
{
	m_printer->write_strobe(0);
}

void ampro_state::clear_strobe(uint8_t data)
{
	m_printer->write_strobe(1);
}

uint8_t ampro_state::ctc_r(offs_t offset)
{
	return m_ctc->read(offset>>4);
}

uint8_t ampro_state::dart_r(offs_t offset)
{
	return m_dart->ba_cd_r(offset>>2);
}

void ampro_state::ctc_w(offs_t offset, uint8_t data)
{
	m_ctc->write(offset>>4, data);
}

void ampro_state::dart_w(offs_t offset, uint8_t data)
{
	m_dart->ba_cd_w(offset>>2, data);
}

void ampro_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).ram().share("mainram");
	map(0x0000, 0x0fff).bankr("bank1");
}

void ampro_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x00).w(FUNC(ampro_state::port00_w)); // system
	map(0x01, 0x01).w("pio", FUNC(output_latch_device::write)); // printer data
	map(0x02, 0x02).w(FUNC(ampro_state::set_strobe)); // printer strobe
	map(0x03, 0x03).w(FUNC(ampro_state::clear_strobe)); // printer strobe
	map(0x20, 0x27).rw(m_ncr, FUNC(ncr5380_device::read), FUNC(ncr5380_device::write)); // scsi chip
	map(0x28, 0x28).rw(m_ncr, FUNC(ncr5380_device::dma_r), FUNC(ncr5380_device::dma_w)); // scsi control
	map(0x29, 0x29).portr("ID"); // ID port
	map(0x40, 0x7f).rw(FUNC(ampro_state::ctc_r), FUNC(ampro_state::ctc_w));
	map(0x80, 0x8f).rw(FUNC(ampro_state::dart_r), FUNC(ampro_state::dart_w));
	map(0xc0, 0xc3).w(m_fdc, FUNC(wd1772_device::write));
	map(0xc4, 0xc7).r(m_fdc, FUNC(wd1772_device::read));
}

static const z80_daisy_config daisy_chain_intf[] =
{
	{ "ctc" },
	{ "dart" },
	{ nullptr }
};

static void ampro_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
	device.option_add("525qd", FLOPPY_525_QD);
}

/* Input ports */
static INPUT_PORTS_START( ampro )
	PORT_START("ID")
	PORT_DIPUNUSED_DIPLOC(0x80, 0x80, "J9:1") // actually pin pair 1-2
	PORT_DIPUNUSED_DIPLOC(0x40, 0x40, "J9:2") // actually pin pair 3-4
	PORT_DIPUNUSED_DIPLOC(0x20, 0x20, "J9:3") // actually pin pair 5-6
	PORT_DIPUNUSED_DIPLOC(0x10, 0x10, "J9:4") // actually pin pair 7-8
	PORT_DIPUNUSED_DIPLOC(0x08, 0x08, "J9:5") // actually pin pair 9-10
	PORT_DIPNAME(0x07, 0x07, "SCSI ID") PORT_DIPLOCATION("J9:8,7,6") // actually pin pairs 15-16, 13-14, 11-12
	PORT_DIPSETTING(0x00, "0")
	PORT_DIPSETTING(0x01, "1")
	PORT_DIPSETTING(0x02, "2")
	PORT_DIPSETTING(0x03, "3")
	PORT_DIPSETTING(0x04, "4")
	PORT_DIPSETTING(0x05, "5")
	PORT_DIPSETTING(0x06, "6")
	PORT_DIPSETTING(0x07, "7")
INPUT_PORTS_END

void ampro_state::machine_start()
{
	m_bank1->configure_entry(0, m_ram);
	m_bank1->configure_entry(1, m_rom);
}

void ampro_state::machine_reset()
{
	m_bank1->set_entry(1);

	port00_w(0);
	clear_strobe(0);
}

void ampro_state::ampro(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &ampro_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &ampro_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain_intf);

	/* Devices */
	Z80CTC(config, m_ctc, 16_MHz_XTAL / 4);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->set_clk<0>(16_MHz_XTAL / 8); // 2MHz
	m_ctc->set_clk<1>(16_MHz_XTAL / 8); // 2MHz
	m_ctc->zc_callback<0>().set(m_dart, FUNC(z80dart_device::txca_w));    // Z80DART Ch A, SIO Ch A
	m_ctc->zc_callback<0>().append(m_dart, FUNC(z80dart_device::rxca_w));
	m_ctc->zc_callback<1>().set(m_dart, FUNC(z80dart_device::rxtxcb_w));   // SIO Ch B

	Z80DART(config, m_dart, 16_MHz_XTAL / 4);
	m_dart->out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
	m_dart->out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts));
	m_dart->out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
	m_dart->out_rtsb_callback().set("rs232b", FUNC(rs232_port_device::write_rts));
	m_dart->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, "terminal"));
	rs232a.rxd_handler().set(m_dart, FUNC(z80dart_device::rxa_w));
	rs232a.cts_handler().set(m_dart, FUNC(z80dart_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_dart, FUNC(z80dart_device::rxb_w));
	rs232b.cts_handler().set(m_dart, FUNC(z80dart_device::ctsb_w));

	output_latch_device &pio(OUTPUT_LATCH(config, "pio"));
	CENTRONICS(config, m_printer, centronics_devices, nullptr);
	m_printer->busy_handler().set(m_dart, FUNC(z80dart_device::rib_w));
	m_printer->set_output_latch(pio);

	WD1772(config, m_fdc, 16_MHz_XTAL / 2);
	//m_fdc->intrq_wr_callback().set(m_ctc, FUNC(z80ctc_device::trg3)); // only if JMP2-3 shorted
	//m_fdc->drq_wr_callback().set(m_dart, FUNC(z80dart_device::ria_w)); // only if JMP7 shorted
	m_fdc->ready_wr_callback().set(m_dart, FUNC(z80dart_device::dcdb_w)); // actually from the drive, and not used by the FDC at all
	for (auto &floppy : m_floppy)
		FLOPPY_CONNECTOR(config, floppy, ampro_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	SOFTWARE_LIST(config, "flop_list").set_original("ampro");

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:1", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr", NCR5380).machine_config([] (device_t *device) {
		//downcast<ncr5380_device &>(*device).irq_handler().set(m_ctc, FUNC(z80ctc_device::trg2)); // only if JMP3 shorted
		//downcast<ncr5380_device &>(*device).drq_handler().set(m_dart, FUNC(z80dart_device::dcda_w)); // only if JMP8 shorted
	});
}

/* ROM definition */
ROM_START( ampro )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "mntr", "Monitor")
	ROMX_LOAD( "mntr", 0x0000, 0x1000, CRC(d59d0909) SHA1(936410f414b1e71445253840eea0045545e4ff0b), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "boot", "Boot")
	ROMX_LOAD( "boot", 0x0000, 0x1000, CRC(b3524046) SHA1(5466f7d28c1a04cfbf328095cb35ad1525e91f44), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "scsi", "SCSI Boot")
	ROMX_LOAD( "scsi", 0x0000, 0x1000, CRC(8eb20e5d) SHA1(0ab1ff65cf6d3c1a713a8ac5c1ee4c662ac3da0c), ROM_BIOS(2))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME            FLAGS
COMP( 1980, ampro, 0,      0,      ampro,   ampro, ampro_state, empty_init, "Ampro", "Little Z80 Board", MACHINE_SUPPORTS_SAVE )



ampscarp.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************************

2017-10-29 Skeleton

Motorola AMPS Car Phone.

Nothing is really known about the hardware. The dump contains MC68HC11 code, but has no vector table. It seems likely that
whatever MCU type this uses boots from an internal ROM/PROM/EPROM but can also execute a large bankswitched external program.

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

#include "emu.h"
#include "cpu/mc68hc11/mc68hc11.h"


namespace {

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

	void ampscarp(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;
	required_device<cpu_device> m_maincpu;
};

void ampscarp_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).rom().region("maincpu", 0);
}

static INPUT_PORTS_START( ampscarp )
INPUT_PORTS_END

void ampscarp_state::ampscarp(machine_config &config)
{
	MC68HC11A1(config, m_maincpu, 8'000'000); // type and clock unknown
	m_maincpu->set_addrmap(AS_PROGRAM, &ampscarp_state::mem_map);
}

ROM_START( ampscarp )
	ROM_REGION( 0x20000, "maincpu", 0 )
	ROM_LOAD( "motorola_amps_car_phone_dump.bin", 0x0000, 0x20000, CRC(677ec85e) SHA1(219611b6c4b16461705e2df61d79a0f7ac8f529f) )
ROM_END

} // anonymous namespace


COMP( 1998, ampscarp, 0, 0, ampscarp, ampscarp, ampscarp_state, empty_init, "Motorola", "AMPS Car Phone", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



amsterdam.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Dirk Verwiebe, Cowering, hap
/*******************************************************************************

Mephisto Amsterdam (2-ROM hardware version)

Hardware notes:

Amsterdam:
- same as Glasgow, but 2*27C256 EPROMs

Dallas 68020:
- MC68020RC12B @ 14MHz
- 64KB ROM(27C512), 64KB RAM(8*M5M5165P-10L)
- rest is similar to 16-bit version

TODO:
- does it have DTACK wait states? surely the PCB supports LDS/UDS wait states
  just like Glasgow, but it's probably disabled due to faster EPROMs

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

#include "emu.h"

#include "mmboard.h"
#include "mmdisplay1.h"

#include "cpu/m68000/m68000.h"
#include "cpu/m68000/m68020.h"
#include "sound/dac.h"

#include "speaker.h"

// internal artwork
#include "mephisto_amsterdam.lh"


namespace {

class amsterdam_state : public driver_device
{
public:
	amsterdam_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_keys(*this, "KEY.%u", 0),
		m_reset(*this, "RESET")
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(reset_button);

	void amsterdam(machine_config &config);
	void dallas32(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	required_device<mephisto_board_device> m_board;
	required_device<mephisto_display1_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<2> m_keys;
	required_ioport m_reset;

	void amsterd_mem(address_map &map) ATTR_COLD;
	void dallas32_mem(address_map &map) ATTR_COLD;

	void led_w(offs_t offset, u8 data);
	void dac_w(u8 data);
	u8 keys_r();
};



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

INPUT_CHANGED_MEMBER(amsterdam_state::reset_button)
{
	// RES buttons in serial tied to CPU RESET
	if (m_reset->read() == 3)
		m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
}

void amsterdam_state::led_w(offs_t offset, u8 data)
{
	// d0-d7: board leds
	m_board->led_w(data);

	// a8: lcd common
	m_display->common_w(BIT(offset, 7));
}

void amsterdam_state::dac_w(u8 data)
{
	// d0: speaker out
	m_dac->write(BIT(data, 0));
}

u8 amsterdam_state::keys_r()
{
	// lcd common is shared with keypad select
	return m_keys[m_display->common_r()]->read();
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void amsterdam_state::amsterd_mem(address_map &map)
{
	map(0x000000, 0x00ffff).rom();
	map(0x800002, 0x800002).w(m_display, FUNC(mephisto_display1_device::data_w));
	map(0x800004, 0x800004).w(FUNC(amsterdam_state::dac_w));
	map(0x800008, 0x800009).nopr(); // clr.b
	map(0x800008, 0x800008).select(0x80).w(FUNC(amsterdam_state::led_w));
	map(0x800010, 0x800010).w(m_board, FUNC(mephisto_board_device::mux_w));
	map(0x800020, 0x800020).r(m_board, FUNC(mephisto_board_device::input_r));
	map(0x800040, 0x800040).r(FUNC(amsterdam_state::keys_r));
	map(0xffc000, 0xffffff).ram(); // 16KB
}

void amsterdam_state::dallas32_mem(address_map &map)
{
	map(0x000000, 0x00ffff).rom();
	map(0x010000, 0x01ffff).ram(); // 64KB
	map(0x800002, 0x800002).w(m_display, FUNC(mephisto_display1_device::data_w));
	map(0x800004, 0x800004).w(FUNC(amsterdam_state::dac_w));
	map(0x800008, 0x800008).select(0x80).w(FUNC(amsterdam_state::led_w));
	map(0x800010, 0x800010).w(m_board, FUNC(mephisto_board_device::mux_w));
	map(0x800020, 0x800020).r(m_board, FUNC(mephisto_board_device::input_r));
	map(0x800040, 0x800040).r(FUNC(amsterdam_state::keys_r));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( amsterdam )
	PORT_START("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("INFO") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("POS") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("LEV") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("MEM") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("CL") PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("ENT") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("G / 7 / King") PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("H / 8") PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)

	PORT_START("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("A / 1") PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("B / 2 / Pawn") PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("C / 3 / Knight") PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("D / 4 / Bishop") PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("E / 5 / Rook") PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("F / 6 / Queen") PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Left / Black / 9") PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Right / White / 0") PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_RIGHT)

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("RES 1") PORT_CODE(KEYCODE_Z) PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(amsterdam_state::reset_button), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("RES 2") PORT_CODE(KEYCODE_X) PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(amsterdam_state::reset_button), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void amsterdam_state::amsterdam(machine_config &config)
{
	// basic machine hardware
	M68000(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_periodic_int(FUNC(amsterdam_state::irq5_line_hold), attotime::from_hz(50));
	m_maincpu->set_addrmap(AS_PROGRAM, &amsterdam_state::amsterd_mem);

	MEPHISTO_SENSORS_BOARD(config, m_board);
	m_board->set_delay(attotime::from_msec(200));

	// video hardware
	MEPHISTO_DISPLAY_MODULE1(config, m_display);
	config.set_default_layout(layout_mephisto_amsterdam);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void amsterdam_state::dallas32(machine_config &config)
{
	amsterdam(config);

	// basic machine hardware
	M68020(config.replace(), m_maincpu, 14_MHz_XTAL);
	m_maincpu->set_periodic_int(FUNC(amsterdam_state::irq4_line_hold), attotime::from_hz(50));
	m_maincpu->set_addrmap(AS_PROGRAM, &amsterdam_state::dallas32_mem);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( amsterd )
	ROM_REGION16_BE( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("amsterda-u.bin", 0x00000, 0x08000, CRC(0a75514e) SHA1(27daf78b0aba4d7a293b96b3c1fa92f6ee9bcb59) )
	ROM_LOAD16_BYTE("amsterda-l.bin", 0x00001, 0x08000, CRC(6e17d8fa) SHA1(e0f9e57aaa445f6ff7cbe868658ed7bcfa7e31fb) )
ROM_END


ROM_START( dallas32 ) // serial 06053xx
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("dallas_68020", 0x00000, 0x10000, CRC(00ab8e11) SHA1(5e0a2f5e6b5a65d4997d6a999f23f9c30460f2e3) ) // MBM27C512-25
ROM_END

ROM_START( dallas32a )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("dallas32a.bin", 0x00000, 0x10000, CRC(83b9ff3f) SHA1(97bf4cb3c61f8ec328735b3c98281bba44b30a28) )
ROM_END

ROM_START( dallas16 )
	ROM_REGION16_BE( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("dallas-u.bin", 0x00000, 0x08000, CRC(70b741f7) SHA1(23d55ed0fea127b727d725c367ee1932ff5af39f) )
	ROM_LOAD16_BYTE("dallas-l.bin", 0x00001, 0x08000, CRC(69300ad3) SHA1(57ec1b955b1ddfe722011ff5da68a0cd71af9251) )
ROM_END


ROM_START( roma32 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("roma32.bin", 0x00000, 0x10000, CRC(587d03bf) SHA1(504e9ff958084700076d633f9c306fc7baf64ffd) )
ROM_END

ROM_START( roma16 )
	ROM_REGION16_BE( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("roma16-u.bin", 0x00000, 0x08000, CRC(111d030f) SHA1(e027f7e7018d28ab794e7730392506056809db6b) )
	ROM_LOAD16_BYTE("roma16-l.bin", 0x00001, 0x08000, CRC(736e1c8d) SHA1(3a71f9185406ab5237c912ec8563b88b01ad50e8) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT    COMPAT  MACHINE     INPUT      CLASS            INIT        COMPANY, FULLNAME, FLAGS
SYST( 1985, amsterd,   0,        0,      amsterdam,  amsterdam, amsterdam_state, empty_init, "Hegener + Glaser", "Mephisto Amsterdam", MACHINE_SUPPORTS_SAVE )

SYST( 1986, dallas32,  0,        0,      dallas32,   amsterdam, amsterdam_state, empty_init, "Hegener + Glaser", "Mephisto Dallas 68020 (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1986, dallas32a, dallas32, 0,      dallas32,   amsterdam, amsterdam_state, empty_init, "Hegener + Glaser", "Mephisto Dallas 68020 (set 2)", MACHINE_SUPPORTS_SAVE )
SYST( 1986, dallas16,  dallas32, 0,      amsterdam,  amsterdam, amsterdam_state, empty_init, "Hegener + Glaser", "Mephisto Dallas 68000", MACHINE_SUPPORTS_SAVE )

SYST( 1987, roma32,    0,        0,      dallas32,   amsterdam, amsterdam_state, empty_init, "Hegener + Glaser", "Mephisto Roma 68020", MACHINE_SUPPORTS_SAVE )
SYST( 1987, roma16,    roma32,   0,      amsterdam,  amsterdam, amsterdam_state, empty_init, "Hegener + Glaser", "Mephisto Roma 68000", MACHINE_SUPPORTS_SAVE )



amstr_pc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/***************************************************************************

    drivers/amstr_pc.cpp

    Driver file for Amstrad PC1512 and related machines.

    PC-XT memory map

    00000-9FFFF   RAM
    A0000-AFFFF   NOP       or videoram EGA/VGA
    B0000-B7FFF   videoram  MDA, page #0
    B8000-BFFFF   videoram  CGA and/or MDA page #1, T1T mapped RAM
    C0000-C7FFF   NOP       or ROM EGA/VGA
    C8000-C9FFF   ROM       XT HDC #1
    CA000-CBFFF   ROM       XT HDC #2
    D0000-EFFFF   NOP       or 'adapter RAM'
    F0000-FDFFF   NOP       or ROM Basic + other Extensions
    FE000-FFFFF   ROM

Amstrad PC1640
==============

More information can be found at http://www.seasip.info/AmstradXT/1640tech/index.html

Amstrad PC5086
==============

- Chips & Technologies F82C100 chipset
- Chips & Technologies F82C451 on-board VGA chip
- DS12887A CMOS
- 640KB RAM

***************************************************************************

Amstrad PPC512/PPC640, Amstrad 1987
Hardware info by Guru
-----------------------------------

The Amstrad PPC512/PPC640 is an XT-class portable PC with a built in
mono 9" LCD screen supporting CGA and MDA.

The specification includes:
- NEC V30 (8086-compatible) 16-bit CPU at 8MHz (Sony CXQ70116P-8)
- For PPC640, 640k RAM & 64k video memory
  For PPC512, 512k RAM & 64k video memory. Main RAM can be expanded to 640k
  by inserting the correct type of DRAMs into the 6 unpopulated locations on the motherboard
- Integrated display adapter compatible with CGA and MDA
- Built in mono 640 x 200 pixel 9" LCD screen, tiltable with contrast adjustment
- 25 pin parallel printer port
- 25 pin serial port
- Internal 2400bps modem (PPC640 only, via optional plug-in PCB)
- Enhanced AT-type keyboard with 101 or 102 keys
- Single or twin 720k 3 1/2" floppy drive
- MSDOS 3.3 (bundled with the unit)
- Speaker with volume control
- Battery-backed real time clock
- Socket for 8087 Math Co-processor
- Socket for external expansion box to accept IBM-compatible expansion cards and hard disk
- Compartment for 10x alkaline 'C' cells for up to 8 hours of use


CPU PCB
-------

MC0051D
Z70850
|-------------------------------------------------------------------|
|   VIDEO     PARALLEL    SERIAL         EXP-B        EXP-A         |
|                    LK2   1489                                     |
|DIPSW(6)            LK3          28.63636MHz    24MHz              |
|                    LK1  6845R      |------|        CX112          |
|         1.8432MHz                  | 40104|                  CX902|
|            INS82C50                |------|      |-------|        |
|                                                  |40039  |        |
|            1489  1488                            |       |        |
|                           LC3664   |------|      |       |        |
|      6MHz                 LC3664   |T4750 |      |-------|   CX901|
|   40112    LCD-CONN                |------|                       |
|KEYB1       KEYB2          40109.IC159                             |
|            KEYB3    LA4140    LK6 LK7                     8087    |
| LED   CONT           SPEAKER  VOLUME      CY903           V30     |
|-------------------------------------------------------------------|
Notes:
      VIDEO       - 9 pin D-sub video port
      PARALLEL    - 25 pin D-sub parallel port
      SERIAL      - 25 pin D-sub serial port
      EXP-B       - Expansion port B
      EXP-A       - Expansion port A
      DIPSW       - 6 position DIP switch (up is off, down is on. * = Default)
                    SW1: Display Use
                         ON  - Internal LCD *
                         OFF - External monitor
                    SW2: Video Emulation Mode
                         ON  - CGA *
                         OFF - MDA
                    SW3: Video Adapter Enable
                         ON  - Use internal video adapter *
                         OFF - Use external video adapter in external expansion box
                    SW4,5:  Initial Video Mode
                            OFF, OFF - External video card
                            OFF, ON  - CGA 40 column
                            ON , OFF - CGA 80 column *
                            ON , ON  - MDA 80 column
                    SW6 - Not Used
      1488        - MC1488 Quad Line EIA232 Driver
      1489        - MC1489 Quad Line EIA232 Receiver
      INS82C50    - 82C50 UART marked as Amstrad 40049 custom chip. Clock input 1.8432MHz
      CX112       - Jumper to configure 40039 gate array to address either 512k or 640k RAM (sometimes marked LK5)
      6845R       - UMC UM6845R CRT Controller. Clock input on pin 21 unknown (comes from the 40104 custom chip)
      40104       - VDU/LCD custom gate array LCD controller
      40039       - Bus controller custom gate array marked as Amstrad 40039 custom chip. Clock input 24.000MHz. This chip generates the 8MHz for the CPU.
      T4750       - DMA/Programmable Interrupt Controller/Programmable Timer custom chip
      40112       - Keyboard controller (likely i8049/i8051) marked as Amstrad 40012 custom chip. Clock input 6.000MHz
      LCD-CONN    - Multi pin connector for LCD screen
      KEYB1       - 5 pin connector for keyboard caps lock, scroll lock and num lock keys, plus VCC and GND
      KEYB2       - Multi pin connector for keyboard matrix
      KEYB3       - Multi pin connector for keyboard matrix
      CX901/902   - Connectors joining to optional modem PCB
      LED         - Connector for power-on LEDs
      CONT        - Connector for LCD screen contrast adjustment
      LA4140      - Sanyo LA4140 0.5W audio power amplifier IC
      SPEAKER     - Connector for PC speaker
      VOLUME      - Connector for audio volume knob
      LC3664      - Sanyo LC3664 8kx8-bit Static RAM
      40109.IC159 - 8kx8-bit character ROM. Compatible with 2764 EPROM
                    Font selection is set with LK6 & LK7
      CY903       - Multi pin connector for connection to memory/power PCB
      8087        - Socket for i8087-2 Numeric Data Processor (math coprocessor). Clock input 8.000MHz
      V30         - NEC V30 CPU (Sony CXQ70116P-8). Clock input 8.000MHz
      LK1/LK2/LK3 - Jumpers to set language selection (off = open, on = short)
                    LK1  LK2  LK3
                    OFF  OFF  OFF  English
                    OFF  OFF  ON   German
                    OFF  ON   OFF  French
                    OFF  ON   ON   Spanish
                    ON   OFF  OFF  Danish
                    ON   OFF  ON   Swedish
                    ON   ON   OFF  Italian
                    ON   ON   ON   Diagnostic Mode
      LK6/LK7     - Jumpers to set font selection in ROM (off = open, on = short)
                    LK6  LK7
                    OFF  OFF Normal    : Codepage 437
                    OFF  ON  Norwegian : Codepage 865
                    ON   OFF Portugese : Codepage 860
                    ON   ON  Greek     : Codepage unknown but will be 869 or 851 or 737


MEMORY / POWER PCB
------------------

MC0052D
Z70851
|----------------------------------------------------------------|
|                         NE556   KBSW  BATSW            POWER   |
|                            41256  41256    X    41256  41256   |
|                                                                |
|                            41256  41256    X    41256  41256   |
|                                                                |
|FD   UM8272A                41256  41256    X    41256  41256   |
|                                                                |
|            16MHz           41256  41256    X    41256  41256   |
|    SED9420                                                     |
|                                            LK4         41256Y  |
|---------------|              |-------|                         |
                |              |40040  |     40108.IC129   Y     |
                |              |       |                         |
                |              |       |     40107.IC132 41256Y  |
                |   32.768kHz  |-------|                         |
                | MC146818                                 Y     |
                |     FDDLED          CX903                      |
                |------------------------------------------------|
Notes:
      FD          - 34 pin floppy drive cable
      FDDLED      - Connector for floppy drive activity LED for drives A: and B:
      POWER       - Connector for mains power input
      SED9420     - EPSON SED9420 data separator for FDD. Clock input 16.000MHz
      UM8272A     - UMC UM8272A floppy drive controller (pin compatible with uPD765)
      41256       - Samsung KM41256AP-15 256kx1-bit DRAM
      41256Y      - Samsung KM41256AP-15 256kx1-bit DRAM used as parity RAM
      X           - Unpopulated location for 64kx4-bit DRAM (additional 128k expansion to give 640kb main RAM for PPC512)
      Y           - Unpopulated location for 256kx1-bit DRAM (additional parity RAM to support the additional 128k RAM)
      LK4         - Jumper to set physical main RAM present on the PCB to either 512k or 640k
      40108.IC129 - BIOS ROM. Compatible with 2764 EPROM
      40107.IC132 - BIOS ROM. Compatible with 2764 EPROM
      CX903       - Multi pin connector for connection to CPU PCB. The connector is located on the solder side of this PCB.
      40040       - Memory controller gate array marked as Amstrad 40040 custom chip
      NE556       - NE556 Dual Timer
      MC146818    - Motorola MC146818 Real Time Clock
      KBSW        - Connector for keyboard open/close detect switch
      BATSW       - Connector for battery/mains power detect switch

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

#include "emu.h"
#include "cpu/nec/nec.h"
#include "cpu/i86/i86.h"

#include "machine/mc146818.h"
#include "machine/genpc.h"
#include "bus/isa/isa.h"
#include "bus/isa/isa_cards.h"
#include "bus/pc_joy/pc_joy.h"
#include "bus/rs232/rs232.h"

#include "machine/pckeybrd.h"
#include "machine/pc_lpt.h"
#include "machine/ram.h"

#include "softlist_dev.h"


namespace {

class amstrad_pc_state : public driver_device
{
public:
	amstrad_pc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ram(*this, RAM_TAG),
		m_mb(*this, "mb"),
		m_keyboard(*this, "pc_keyboard"),
		m_lpt1(*this, "lpt_1"),
		m_lpt2(*this, "lpt_2"),
		m_mouse{ 0, 0 }
	{
	}

	void pc200(machine_config &config);
	void pc2086(machine_config &config);
	void ppc640(machine_config &config);
	void ppc512(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<pc_noppi_mb_device> m_mb;
	required_device<pc_keyboard_device> m_keyboard;
	required_device<pc_lpt_device> m_lpt1;
	required_device<pc_lpt_device> m_lpt2;

	uint8_t pc1640_port60_r(offs_t offset);
	void pc1640_port60_w(offs_t offset, uint8_t data);

	uint8_t pc1640_mouse_x_r();
	uint8_t pc1640_mouse_y_r();
	void pc1640_mouse_x_w(uint8_t data);
	void pc1640_mouse_y_w(uint8_t data);

	uint8_t pc200_port378_r(offs_t offset);
	uint8_t pc200_port278_r(offs_t offset);
	[[maybe_unused]] uint8_t pc1640_port378_r(offs_t offset);
	[[maybe_unused]] uint8_t pc1640_port3d0_r(offs_t offset);
	[[maybe_unused]] uint8_t pc1640_port4278_r(offs_t offset);
	[[maybe_unused]] uint8_t pc1640_port278_r(offs_t offset);

	struct {
		uint8_t x = 0, y = 0; //byte clipping needed
	} m_mouse;

	// 64 system status register?
	uint8_t m_port60 = 0;
	uint8_t m_port61 = 0;
	uint8_t m_port62 = 0;
	uint8_t m_port65 = 0;

	uint8_t m_dipstate = 0;
	static void cfg_com(device_t *device);
	static void cfg_fdc(device_t *device);

	void pc200_io(address_map &map) ATTR_COLD;
	void pc2086_map(address_map &map) ATTR_COLD;
	void ppc512_io(address_map &map) ATTR_COLD;
	void ppc640_map(address_map &map) ATTR_COLD;
};

void amstrad_pc_state::ppc640_map(address_map &map)
{
	map(0xf0000, 0xfffff).rom().region("bios", 0);
}

void amstrad_pc_state::pc2086_map(address_map &map)
{
	map(0xc0000, 0xc9fff).rom().region("bios", 0);
	map(0xf0000, 0xfffff).rom().region("bios", 0x10000);
}

void amstrad_pc_state::pc200_io(address_map &map)
{
	map(0x0000, 0x00ff).m(m_mb, FUNC(pc_noppi_mb_device::map));
	map(0x0060, 0x0065).rw(FUNC(amstrad_pc_state::pc1640_port60_r), FUNC(amstrad_pc_state::pc1640_port60_w));
	map(0x0078, 0x0079).rw(FUNC(amstrad_pc_state::pc1640_mouse_x_r), FUNC(amstrad_pc_state::pc1640_mouse_x_w));
	map(0x007a, 0x007b).rw(FUNC(amstrad_pc_state::pc1640_mouse_y_r), FUNC(amstrad_pc_state::pc1640_mouse_y_w));
	map(0x0200, 0x0207).rw("pc_joy", FUNC(pc_joy_device::joy_port_r), FUNC(pc_joy_device::joy_port_w));
	map(0x0278, 0x027b).r(FUNC(amstrad_pc_state::pc200_port278_r));
	map(0x0278, 0x027b).w(m_lpt2, FUNC(pc_lpt_device::write)).umask16(0x00ff);
	map(0x0378, 0x037b).r(FUNC(amstrad_pc_state::pc200_port378_r));
	map(0x0378, 0x037b).w(m_lpt1, FUNC(pc_lpt_device::write)).umask16(0x00ff);
	map(0x03bc, 0x03bf).rw("lpt_0", FUNC(pc_lpt_device::read), FUNC(pc_lpt_device::write)).umask16(0x00ff);
}

void amstrad_pc_state::ppc512_io(address_map &map)
{
	pc200_io(map);
	map(0x0070, 0x0070).w("rtc", FUNC(mc146818_device::address_w));
	map(0x0071, 0x0071).rw("rtc", FUNC(mc146818_device::data_r), FUNC(mc146818_device::data_w));
}

void amstrad_pc_state::machine_start()
{
	m_port60 = 0;
	m_port61 = 0;
	m_port62 = 0;
	m_port65 = 0;
	m_dipstate = 0;

	save_item(NAME(m_port60));
	save_item(NAME(m_port61));
	save_item(NAME(m_port62));
	save_item(NAME(m_port65));
	save_item(NAME(m_dipstate));
	save_item(NAME(m_mouse.x));
	save_item(NAME(m_mouse.y));
}

/* pc20 (v2)
   fc078
   fc102 color/mono selection
   fc166
   fc1b4
   fd841 (output something)
   ff17c (output something, read monitor type inputs)
   fc212
   fc26c
   fc2df
   fc3fe
   fc0f4
   fc432
   fc49f
   fc514
   fc566
   fc5db
   fc622 in 3de

port 0379 read
port 03de write/read
 */

/* pc1512 (v1)
   fc1b5
   fc1f1
   fc264
   fc310
   fc319
   fc385
   fc436
   fc459
   fc4cb
   fc557
   fc591
   fc624
   fc768
   fc818
   fc87d display amstrad ..
    fca17 keyboard check
     fca69
     fd680
      fd7f9
     fca7b !keyboard interrupt routine for this check
 */


/* pc1640 (v3)
   bios power up self test
   important addresses

   fc0c9
   fc0f2
   fc12e
   fc193
   fc20e
   fc2c1
   fc2d4


   fc375
   fc3ba
   fc3e1
   fc412
   fc43c
   fc47d
   fc48f
   fc51f
   fc5a2
   fc5dd mouse

   fc1c0
   fc5fa
    the following when language selection not 0 (test for presence of 0x80000..0x9ffff ram)
    fc60e
    fc667
   fc678
   fc6e5
   fc72e
   fc78f
   fc7cb ; coprocessor related
   fc834
    feda6 no problem with disk inserted

   fca2a

   cmos ram 28 0 amstrad pc1512 integrated cga
   cmos ram 23 dipswitches?
*/

/* test sequence in bios
 write 00 to 65
 write 30 to 61
 read 62 and (0x10)
 write 34 to 61
 read 62 and (0x0f)
 return or of the 2 62 reads

 allows set of the original ibm pc "dipswitches"!!!!

 66 write gives reset?
*/

/* mouse x counter at 0x78 (read- writable)
   mouse y counter at 0x7a (read- writable)

   mouse button 1,2 keys
   joystick (4 directions, 2 buttons) keys
   these get value from cmos ram
   74 15 00 enter
   70 17 00 forward del
   77 1b 00 joystick button 1
   78 19 00 joystick button 2


   79 00 4d right
   7a 00 4b left
   7b 00 50 down
   7c 00 48 up

   7e 00 01 mouse button left
   7d 01 01 mouse button right
*/

void amstrad_pc_state::pc1640_port60_w(offs_t offset, uint8_t data)
{
	switch (offset) {
	case 1:
		m_port61=data;
		if (data==0x30) m_port62=(m_port65&0x10)>>4;
		else if (data==0x34) m_port62=m_port65&0xf;
		m_mb->m_pit8253->write_gate2(BIT(data, 0));
		m_mb->pc_speaker_set_spkrdata( data & 0x02 );
		m_keyboard->enable(data&0x40);
		if(data & 0x80)
			m_mb->m_pic8259->ir1_w(0);

		break;
	case 4:
		if (data&0x80) {
			m_port60=data^0x8d;
		} else {
			m_port60=data;
		}
		break;
	case 5:
		// stores the configuration data for port 62 configuration dipswitch emulation
		m_port65=data;
		break;
	}

	logerror("pc1640 write %.2x %.2x\n",offset,data);
}


uint8_t amstrad_pc_state::pc1640_port60_r(offs_t offset)
{
	int data=0;
	switch (offset) {
	case 0:
		if (m_port61&0x80)
			data=m_port60;
		else
			data = m_keyboard->read();
		break;

	case 1:
		data = m_port61;
		break;

	case 2:
		data = m_port62;
		if (m_mb->pit_out2())
			data |= 0x20;
		break;
	}
	return data;
}

uint8_t amstrad_pc_state::pc200_port378_r(offs_t offset)
{
	uint8_t data = m_lpt1->read(offset);

	if (offset == 1)
		data = (data & ~7) | (ioport("DSW0")->read() & 7);
	if (offset == 2)
		data = (data & ~0xe0) | (ioport("DSW0")->read() & 0xc0);

	return data;
}

uint8_t amstrad_pc_state::pc200_port278_r(offs_t offset)
{
	uint8_t data = m_lpt2->read(offset);

	if (offset == 1)
		data = (data & ~7) | (ioport("DSW0")->read() & 7);
	if (offset == 2)
		data = (data & ~0xe0) | (ioport("DSW0")->read() & 0xc0);

	return data;
}


uint8_t amstrad_pc_state::pc1640_port378_r(offs_t offset)
{
	uint8_t data = m_lpt1->read(offset);

	if (offset == 1)
		data=(data & ~7) | (ioport("DSW0")->read() & 7);
	if (offset == 2)
	{
		switch (m_dipstate)
		{
		case 0:
			data = (data&~0xe0) | (ioport("DSW0")->read() & 0xe0);
			break;
		case 1:
			data = (data&~0xe0) | ((ioport("DSW0")->read() & 0xe000)>>8);
			break;
		case 2:
			data = (data&~0xe0) | ((ioport("DSW0")->read() & 0xe00)>>4);
			break;

		}
	}
	return data;
}

uint8_t amstrad_pc_state::pc1640_port3d0_r(offs_t offset)
{
	if (offset==0xa) m_dipstate=0;
	return m_maincpu->space(AS_PROGRAM).read_byte(0x3d0+offset);
}

uint8_t amstrad_pc_state::pc1640_port4278_r(offs_t offset)
{
	if (offset==2) m_dipstate=1;
	// read parallelport
	return 0;
}

uint8_t amstrad_pc_state::pc1640_port278_r(offs_t offset)
{
	if ((offset==2)||(offset==0)) m_dipstate=2;
	// read parallelport
	return 0;
}

uint8_t amstrad_pc_state::pc1640_mouse_x_r()
{
	return m_mouse.x - ioport("pc_mouse_x")->read();
}

uint8_t amstrad_pc_state::pc1640_mouse_y_r()
{
	return m_mouse.y - ioport("pc_mouse_y")->read();
}

void amstrad_pc_state::pc1640_mouse_x_w(uint8_t data)
{
	m_mouse.x = data + ioport("pc_mouse_x")->read();
}

void amstrad_pc_state::pc1640_mouse_y_w(uint8_t data)
{
	m_mouse.y = data + ioport("pc_mouse_y")->read();
}

static INPUT_PORTS_START( pc200 ) // TODO: PPC512/PPC640 DSW differ, see readme at the top of the file
	PORT_START("DSW0") /* IN1 */
	PORT_DIPNAME( 0x07, 0x07, "Name/Language")
	PORT_DIPSETTING(    0x00, "English/less checks" )
	PORT_DIPSETTING(    0x01, DEF_STR( Italian ) ) //prego attendere
	PORT_DIPSETTING(    0x02, u8"V.g. vänta" )
	PORT_DIPSETTING(    0x03, "Vent et cjeblik" ) // seldom c
	PORT_DIPSETTING(    0x04, DEF_STR( Spanish ) ) //Por favor
	PORT_DIPSETTING(    0x05, DEF_STR( French ) ) //patientez
	PORT_DIPSETTING(    0x06, DEF_STR( German ) ) // bitte warten
	PORT_DIPSETTING(    0x07, DEF_STR( English ) ) // please wait
	PORT_DIPNAME( 0x08, 0x00, "37a 0x40")
	PORT_DIPSETTING(    0x00, "0x00" )
	PORT_DIPSETTING(    0x08, "0x08" )
/* 2008-05 FP: This Dip Switch overlaps the next one.
Since pc200 is anyway NOT_WORKING, I comment out this one */
/*  PORT_DIPNAME( 0x10, 0x00, "37a 0x80")
    PORT_DIPSETTING(    0x00, "0x00" )
    PORT_DIPSETTING(    0x10, "0x10" ) */
	PORT_DIPNAME( 0x30, 0x00, "Integrated Graphics Adapter")
	PORT_DIPSETTING(    0x00, "CGA 1" )
	PORT_DIPSETTING(    0x10, "CGA 2" )
	PORT_DIPSETTING(    0x20, "external" )
	PORT_DIPSETTING(    0x30, "MDA" )
	PORT_DIPNAME( 0xc0, 0x80, "Startup Mode")
	PORT_DIPSETTING(    0x00, "external Color 80 Columns" )
	PORT_DIPSETTING(    0x40, "Color 40 Columns" )
	PORT_DIPSETTING(    0x80, "Color 80 Columns" )
	PORT_DIPSETTING(    0xc0, DEF_STR( Mono ) )

	PORT_START("DSW1") /* IN2 */
	PORT_BIT ( 0x80, 0x80,   IPT_UNUSED ) // com 1 on motherboard
	PORT_DIPNAME( 0x40, 0x40, "COM2: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x20, 0x00, "COM3: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x10, 0x00, "COM4: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x08, 0x08, "LPT1: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Yes ) )
	PORT_BIT ( 0x04, 0x04,   IPT_UNUSED ) // lpt 1 on motherboard
	PORT_DIPNAME( 0x02, 0x00, "LPT3: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x01, 0x00, "Game port enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x01, DEF_STR( Yes ) )

	PORT_START("DSW2") /* IN3 */
	PORT_DIPNAME( 0x08, 0x08, "HDC1 (C800:0 port 320-323)")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x04, 0x04, "HDC2 (CA00:0 port 324-327)")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Yes ) )
	PORT_BIT( 0x02, 0x02,   IPT_UNUSED ) /* no turbo switch */
	PORT_BIT( 0x01, 0x01,   IPT_UNUSED )
INPUT_PORTS_END


void amstrad_pc_state::cfg_com(device_t *device)
{
	/* has its own mouse */
	device->subdevice<rs232_port_device>("serport0")->set_default_option(nullptr);
}

void amstrad_pc_state::cfg_fdc(device_t *device)
{
	/* all machines have an internal 3.5" drive */
	auto fdc0 = dynamic_cast<device_slot_interface *>(device->subdevice("fdc:0"));
	fdc0->set_default_option("35dd");
	fdc0->set_fixed(true);

	auto fdc1 = dynamic_cast<device_slot_interface *>(device->subdevice("fdc:1"));
	fdc1->set_default_option("35dd");
}

void amstrad_pc_state::pc200(machine_config &config)
{
	/* basic machine hardware */
	I8086(config, m_maincpu, 8000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &amstrad_pc_state::ppc640_map);
	m_maincpu->set_addrmap(AS_IO, &amstrad_pc_state::pc200_io);
	m_maincpu->set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	PCNOPPI_MOTHERBOARD(config, m_mb, 0).set_cputag(m_maincpu);
	m_mb->int_callback().set_inputline(m_maincpu, 0);
	m_mb->nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);

	// FIXME: determine ISA bus clock
	ISA8_SLOT(config, "aga", 0, "mb:isa", pc_isa8_cards, "aga_pc200", true);
	ISA8_SLOT(config, "fdc", 0, "mb:isa", pc_isa8_cards, "fdc_xt", true).set_option_machine_config("fdc_xt", cfg_fdc);
	ISA8_SLOT(config, "com", 0, "mb:isa", pc_isa8_cards, "com", true).set_option_machine_config("com", cfg_com);

	ISA8_SLOT(config, "isa1", 0, "mb:isa", pc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa2", 0, "mb:isa", pc_isa8_cards, nullptr, false);

	/* printer */
	pc_lpt_device &lpt0(PC_LPT(config, "lpt_0"));
	lpt0.irq_handler().set("mb:pic8259", FUNC(pic8259_device::ir7_w));

	PC_LPT(config, m_lpt1);
	m_lpt1->irq_handler().set("mb:pic8259", FUNC(pic8259_device::ir7_w));

	PC_LPT(config, m_lpt2);
	m_lpt2->irq_handler().set("mb:pic8259", FUNC(pic8259_device::ir5_w));

	PC_JOY(config, "pc_joy");

	PC_KEYB(config, m_keyboard);
	m_keyboard->keypress().set("mb:pic8259", FUNC(pic8259_device::ir1_w));

	/* internal ram */
	RAM(config, m_ram).set_default_size("640K").set_extra_options("512K");

	/* software lists */
	SOFTWARE_LIST(config, "disk_list").set_original("pc200");
}

void amstrad_pc_state::pc2086(machine_config &config)
{
	pc200(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &amstrad_pc_state::pc2086_map);
}

void amstrad_pc_state::ppc640(machine_config &config)
{
	pc200(config);
	V30(config.replace(), m_maincpu, 8000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &amstrad_pc_state::ppc640_map);
	m_maincpu->set_addrmap(AS_IO, &amstrad_pc_state::ppc512_io);
	m_maincpu->set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	config.device_remove("isa1");
	config.device_remove("isa2");

	MC146818(config, "rtc", 32.768_kHz_XTAL);
}

void amstrad_pc_state::ppc512(machine_config &config)
{
	ppc640(config);
	m_ram->set_default_size("512K").set_extra_options("640K");
}

/*
Sinclair PC200 ROMs (from a v1.2 PC200):

40109.ic159     -- Character set, the same as in the 1.5 PC200. Label:

            AMSTRAD
            40109
            8827 B

40184.ic132 -- BIOS v1.2.
40185.ic129    Labels are:

            AMSTRAD         AMSTRAD
            40184           40185
            V1.2:5EA8       V1.2:A058
*/
ROM_START( pc200 )
//  ROM_REGION(0x100000,"bios", 0)
	ROM_REGION16_LE(0x10000,"bios", 0)
	// special bios at 0xe0000 !?
	ROM_SYSTEM_BIOS(0, "v15", "v1.5")
	ROMX_LOAD("40185-2.ic129", 0xc001, 0x2000, CRC(41302eb8) SHA1(8b4b2afea543b96b45d6a30365281decc15f2932), ROM_SKIP(1) | ROM_BIOS(0)) // v2
	ROMX_LOAD("40184-2.ic132", 0xc000, 0x2000, CRC(71b84616) SHA1(4135102a491b25fc659d70b957e07649f3eacf24), ROM_SKIP(1) | ROM_BIOS(0)) // v2
	ROM_SYSTEM_BIOS(1, "v13", "v1.3")
	ROMX_LOAD("40185v13.ic129", 0xc001, 0x2000, CRC(f082f08e) SHA1(b332db419033588a7380bfecdf46104974347341), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("40184v13.ic132", 0xc000, 0x2000, CRC(5daf6068) SHA1(93a2ccfb0e29c8f2c98f06c64bb0ea0b3acafb13), ROM_SKIP(1) | ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v12", "v1.2")
	ROMX_LOAD("40185.ic129", 0xc001, 0x2000, CRC(c2b4eeac) SHA1(f11015fadf0c16d86ce2c5047be3e6a4782044f7), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD("40184.ic132", 0xc000, 0x2000, CRC(b22704a6) SHA1(dadd573db6cd34f339f2f0ae55b07537924c024a), ROM_SKIP(1) | ROM_BIOS(2))
	// also mapped to f0000, f4000, f8000
	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD( "40112.ic801", 0x000, 0x800, CRC(842a954c) SHA1(93ca6badf20e0215025fe109959eddead8c52f38) )
ROM_END


ROM_START( pc20 )
//  ROM_REGION(0x100000,"bios", 0)
	ROM_REGION16_LE(0x10000,"bios", 0)

	// special bios at 0xe0000 !?
	// This is probably referring to a check for the Amstrad RP5-2 diagnostic
	// card, which can be plugged into an Amstrad XT for troubleshooting purposes.
	// - John Elliott
	ROM_LOAD16_BYTE("pc20v2.0", 0xc001, 0x2000, CRC(41302eb8) SHA1(8b4b2afea543b96b45d6a30365281decc15f2932)) // v2
	ROM_LOAD16_BYTE("pc20v2.1", 0xc000, 0x2000, CRC(71b84616) SHA1(4135102a491b25fc659d70b957e07649f3eacf24)) // v2
	// also mapped to f0000, f4000, f8000
ROM_END


ROM_START( ppc512 )
//  ROM_REGION(0x100000,"bios", 0)
	ROM_REGION16_LE(0x10000,"bios", 0)
	// special bios at 0xe0000 !?
	ROM_LOAD16_BYTE("40107_v1.ic132", 0xc000, 0x2000, CRC(4e37e769) SHA1(88be3d3375ec3b0a7041dbcea225b197e50d4bfe)) // v1.9
	ROM_LOAD16_BYTE("40108_v1.ic129", 0xc001, 0x2000, CRC(4f0302d9) SHA1(e4d69ca98c3b98f3705a2902b16746360043f039)) // v1.9
	// also mapped to f0000, f4000, f8000
	ROM_REGION( 0x800, "keyboard", 0 ) // PPC512 / PPC640 / PC200 102-key keyboard
	ROM_LOAD( "40112.ic801", 0x000, 0x800, CRC(842a954c) SHA1(93ca6badf20e0215025fe109959eddead8c52f38) )
ROM_END


ROM_START( ppc640 )
//  ROM_REGION(0x100000,"bios", 0)
	ROM_REGION16_LE(0x10000,"bios", 0)
	// special bios at 0xe0000 !?
	ROM_LOAD16_BYTE("40107.v2", 0xc000, 0x2000, CRC(0785b63e) SHA1(4dbde6b9e9500298bb6241a8daefd85927f1ad28)) // v2.1
	ROM_LOAD16_BYTE("40108.v2", 0xc001, 0x2000, CRC(5351cf8c) SHA1(b4dbf11b39378ab4afd2107d3fe54a99fffdedeb)) // v2.1
	// also mapped to f0000, f4000, f8000
	ROM_REGION(0x2000,"subcpu", 0)
	ROM_LOAD("40135.ic192", 0x00000, 0x2000, CRC(75d99199) SHA1(a76d39fda3d5140e1fb9ce70fcddbdfb8f891dc6))

	ROM_REGION( 0x800, "keyboard", 0 ) // PPC512 / PPC640 / PC200 102-key keyboard
	ROM_LOAD( "40112.ic801", 0x000, 0x800, CRC(842a954c) SHA1(93ca6badf20e0215025fe109959eddead8c52f38) )
ROM_END


ROM_START( pc2086 )
	ROM_REGION16_LE( 0x20000, "bios", 0 )
	ROM_LOAD(        "40186.ic171", 0x00000, 0x8000, CRC(959f00ba) SHA1(5df1efe4203cd076292a7713bd7ebd1196dca577) )
	ROM_LOAD16_BYTE( "40179.ic129", 0x1c000, 0x2000, CRC(003605e4) SHA1(b882e97ee81b9ba0e7d969c63da3f2052f23b4b9) )
	ROM_LOAD16_BYTE( "40180.ic132", 0x1c001, 0x2000, CRC(28ee5e58) SHA1(93e045609466fcec74e2bb72578bb7405281cf7b) )

	ROM_REGION( 0x800, "keyboard", 0 ) // PC2086 / PC3086 102-key keyboard
	ROM_LOAD( "40178.ic801", 0x000, 0x800, CRC(f72f1c2e) SHA1(34897e78b3d10f96b36d81778e97c4a9a1b8618b) )
ROM_END


ROM_START( pc3086 )
	ROM_REGION16_LE( 0x20000, "bios", 0 )
	ROM_LOAD( "c000.bin", 0x00000, 0x8000, CRC(5a6c38e9) SHA1(382d2028e0dc5515a68843829563ce29018edb08) )
	ROM_LOAD( "c800.bin", 0x08000, 0x2000, CRC(3329c6d5) SHA1(982e852278185d69acde47a4f3942bc09ed76777) )
	ROM_LOAD( "fc00.bin", 0x1c000, 0x4000, CRC(b5630753) SHA1(98c344831cc4dc59ebb39bbb1961964a8d39fe20) )

	ROM_REGION( 0x800, "keyboard", 0 ) // PC2086 / PC3086 102-key keyboard
	ROM_LOAD( "40178.ic801", 0x000, 0x800, CRC(f72f1c2e) SHA1(34897e78b3d10f96b36d81778e97c4a9a1b8618b) )
ROM_END


ROM_START( pc5086 )
	ROM_REGION16_LE( 0x20000, "bios", 0 )
	ROM_LOAD( "c000.bin", 0x00000, 0x08000, CRC(5a8c640d) SHA1(7e5731f0febbad8228f758c6deceb550356c3b13) )
	ROM_LOAD( "c800.bin", 0x08000, 0x02000, CRC(217ac584) SHA1(088aeb4bb389086c127274ddd3cde3048173cc8a) )
	ROM_LOAD( "sys_rom.bin", 0x10000, 0x10000, CRC(d69b0d48) SHA1(3184d2a8107927631414bdcc4863e22b5a282def) )

	ROM_REGION( 0x800, "keyboard", 0 ) // PC2086 / PC3086 102-key keyboard
	ROM_LOAD( "40178.ic801", 0x000, 0x800, CRC(f72f1c2e) SHA1(34897e78b3d10f96b36d81778e97c4a9a1b8618b) )
ROM_END

} // Anonymous namespace


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

  Game driver(s)

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

/*    YEAR  NAME    PARENT   COMPAT  MACHINE  INPUT  CLASS             INIT        COMPANY         FULLNAME */
COMP( 1987, ppc512, ibm5150, 0,      ppc512,  pc200, amstrad_pc_state, empty_init, "Amstrad plc",  "Amstrad PPC512", MACHINE_NOT_WORKING )
COMP( 1987, ppc640, ibm5150, 0,      ppc640,  pc200, amstrad_pc_state, empty_init, "Amstrad plc",  "Amstrad PPC640", MACHINE_NOT_WORKING )
COMP( 1988, pc20,   ibm5150, 0,      pc200,   pc200, amstrad_pc_state, empty_init, "Amstrad plc",  "Amstrad PC20" , MACHINE_NOT_WORKING )
COMP( 1988, pc200,  ibm5150, 0,      pc200,   pc200, amstrad_pc_state, empty_init, "Sinclair Research Ltd",  "PC200 Professional Series", MACHINE_NOT_WORKING )
COMP( 1988, pc2086, ibm5150, 0,      pc2086,  pc200, amstrad_pc_state, empty_init, "Amstrad plc",  "Amstrad PC2086", MACHINE_NOT_WORKING )
COMP( 1990, pc3086, ibm5150, 0,      pc2086,  pc200, amstrad_pc_state, empty_init, "Amstrad plc",  "Amstrad PC3086", MACHINE_NOT_WORKING )
COMP( 199?, pc5086, ibm5150, 0,      pc2086,  pc200, amstrad_pc_state, empty_init, "Amstrad plc",  "Amstrad PC5086", MACHINE_NOT_WORKING ) // dies with error message 010



amstrad.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Kevin Thacker, Barry Rodewald
/******************************************************************************

    amstrad.cpp
    system driver

        Amstrad Hardware:
            - 8255 connected to AY-3-8912 sound generator,
                keyboard, cassette, printer, crtc vsync output,
                and some PCB links
            - 6845 (either HD6845S, UM6845R or M6845) crtc graphics display
              controller
            - UPD765 floppy disc controller (CPC664,CPC6128)
            - Z80 CPU running at 4 MHz (slowed by wait states on memory
              access)
            - custom ASIC "Gate Array" controlling rom paging, ram paging,
                current display mode and colour palette

    Kevin Thacker [MESS driver]

  May-June 2004 - Yoann Courtois (aka Papagayo/ex GMC/ex PARADOX) - rewriting some code with hardware documentation from http://www.cepece.info/amstrad/

  June 2006  - Very preliminary CPC+ support.  CPR cart image handling, secondary ROM register, ASIC unlock detection
               Supported:
               12-bit palette,
               12-bit hardware sprites (largely works from what I've seen, some games have display issues),
               Programmable Raster Interrupt (seems to work),
               Split screen registers,
               Soft scroll registers (only byte-by-byre horizontally for now),
               Analogue controls (may well be completely wrong, I have no idea on how these should work),
               Vectored interrupts for Z80 interrupt mode 2 (used by Pang),
               DMA sound channels (may still be some issues, noticeable in Navy Seals and Copter 271)
               04/07/06:  Added interrupt vector support for IM 2.
                          Added soft scroll register implementation.  Vertical adjustments are a bit shaky.
               05/07/06:  Fixed hardware sprite offsets
               14/07/06:  Added basic analogue control support.
               04/08/06:  Fixed PRI and Split screen scanline offsets (based on code in Arnold ;))
                          Implemented DMA sound channels
               06/08/06:  Fixed CRTC palette if the ASIC was re-locked after already being unlocked and used.
                          This fixes Klax, which is now playable.
               08/08/06:  Fixed up vertical soft scroll, now we just need to get a finer detail on horizontal soft scroll
                          (Only works on a byte level for now)
                          Fixed DMA pause function when the prescaler is set to 0.

               Tested with the Arnold 5 Diagnostic Cartridge.  Mostly works fine, but the soft scroll test is
               noticeably wrong.

               Known issues with some games (as at 12/01/08):
               Robocop 2:  playable, but sprites should be cut off outside the playing area (partial updates should be able to fix this).
               Navy Seals:  Playable, but sound effects cut out at times (probably DMA related).
               Dick Tracy:  Sprite visibility issues
               Switchblade:  has some slowdown when numerous enemies are on screen (normal?)
               Epyx World of Sports: doesn't start at all.
               Tennis Cup II:  controls don't seem to work.
               Fire and Forget II:  playable, but the top half of the screen flickers
               Crazy Cars II:  playable, with slight shaking of horizon
               No Exit:  Display is wrong, but usable, uses demo-like techniques.


   January 2008 - added preliminary Aleste 520EX support
               The Aleste 520EX is a Russian clone of the CPC6128, that expands on existing video modes, and can run MSX-DOS.
               It also adds an MC146818 RTC/NVRAM, an Intel 8253 timer, and the "Magic Sound" board, a 4-channel DMA-based
               sample player.  Also includes a software emulation of the MSX2 VDP, used in the ports of MSX games.

               Known issues:
                - The RTC doesn't always update in setup.  It expects bit 4 of register C (Update End Interrupt) to be high.
                - Title screens appear then disappear quickly (probably because the 8253 hasn't been plugged in yet).
                - Some video modes display wrong (still needs some work here).
                - Vampire Killer crashes after collecting a certain key part way through stage 1.
                - Magic Sound isn't emulated.


   January 2009 - changed drivers to use the mc6845 device implementation
               To get rid of duplicated code the drivers have been changed to use the new mc6845 device
               implementation. As a result the (runtime) selection of CRTC type has been removed.

    July 2011 - added basic expansion port interface, with support for both the Amstrad SSA-1 and DK'Tronics
                speech synthesisers.


Some bugs left :
----------------
    - CRTC all type support (0,1,2,3,4) ?
    - Gate Array and CRTC aren't synchronised. (The Gate Array can change the color every microseconds?) So the multi-rasters in one line aren't supported (see yao demo p007's part)!
    - Implement full Asic for CPC+ emulation.  Soft scroll is rather dodgy.
    - The KC Compact should not reuse the gate array functionality. Instead z8536 support should be added. (bug #42)

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

/* Core includes */
#include "emu.h"
#include "amstrad.h"

/* Components */
#include "machine/i8255.h"     // for 8255 ppi
#include "cpu/z80/z80.h"       // for cycle tables
#include "video/mc6845.h"      // CRTC
#include "machine/upd765.h"    // for floppy disc controller
#include "sound/ay8910.h"
#include "machine/mc146818.h"  // Aleste RTC
#include "bus/centronics/ctronics.h"

/* Devices */
#include "imagedev/snapquik.h"
#include "imagedev/cassette.h"
#include "formats/tzx_cas.h"
#include "formats/msx_dsk.h"
#include "formats/ipf_dsk.h"

#include "machine/ram.h"
#include "softlist.h"
#include "speaker.h"
#include "utf8.h"

#define MANUFACTURER_NAME 0x07
#define TV_REFRESH_RATE 0x10

#define SYSTEM_CPC    0
#define SYSTEM_PLUS   1
#define SYSTEM_GX4000 2


/* Memory is banked in 16k blocks. However, the multiface pages the memory in 8k blocks!
   The ROM can be paged into bank 0 and bank 3. */
void amstrad_state::amstrad_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(amstrad_state::amstrad_cpc_mem_r), FUNC(amstrad_state::amstrad_cpc_mem_w));
}

/* I've handled the I/O ports in this way, because the ports
are not fully decoded by the CPC h/w. Doing it this way means
I can decode it myself and a lot of  software should work */
void amstrad_state::amstrad_io(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(amstrad_state::amstrad_cpc_io_r), FUNC(amstrad_state::amstrad_cpc_io_w));
}


/*************************************
 *
 *  Input ports
 *
 *************************************/

static INPUT_PORTS_START( amstrad_keyboard )
	PORT_START("kbrow.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_UP)                 PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT)              PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN)               PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad 9")              PORT_CODE(KEYCODE_9_PAD)      PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad 6")              PORT_CODE(KEYCODE_6_PAD)      PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad 3")              PORT_CODE(KEYCODE_3_PAD)      PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad Enter")          PORT_CODE(KEYCODE_ENTER_PAD)  PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad .")              PORT_CODE(KEYCODE_DEL_PAD)    PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))

	PORT_START("kbrow.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT)               PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Copy")                  PORT_CODE(KEYCODE_END)        PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad 7")              PORT_CODE(KEYCODE_7_PAD)      PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad 8")              PORT_CODE(KEYCODE_8_PAD)      PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad 5")              PORT_CODE(KEYCODE_5_PAD)      PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad 1")              PORT_CODE(KEYCODE_1_PAD)      PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad 2")              PORT_CODE(KEYCODE_2_PAD)      PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad 0")              PORT_CODE(KEYCODE_0_PAD)      PORT_CHAR(UCHAR_MAMEKEY(0_PAD))

	PORT_START("kbrow.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Clr")                   PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[")                     PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter")                 PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]")                     PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad 4")              PORT_CODE(KEYCODE_4_PAD)      PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift")                 PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\")                    PORT_CODE(KEYCODE_RCONTROL)   PORT_CHAR('\\') PORT_CHAR('`')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl")                  PORT_CODE(KEYCODE_RALT)       PORT_CHAR(UCHAR_SHIFT_2)

	PORT_START("kbrow.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2191 £")            PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('^') PORT_CHAR(U'£') // U+2191 = ↑
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('@') PORT_CHAR(U'¦','|')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_COLON)      PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR('>')

	PORT_START("kbrow.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_0)          PORT_CHAR('0') PORT_CHAR('_')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_O)          PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_L)          PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_K)          PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_M)          PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("kbrow.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_H)          PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_J)          PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_N)          PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space")                 PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(32)

	PORT_START("kbrow.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_5)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_R)          PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_T)          PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_G)          PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_F)          PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_B)          PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_V)          PORT_CHAR('v') PORT_CHAR('V')

	PORT_START("kbrow.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_E)          PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_W)          PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_S)          PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_D)          PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_C)          PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_X)          PORT_CHAR('x') PORT_CHAR('X')

	PORT_START("kbrow.8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('\"') PORT_CHAR('~')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc")                   PORT_CODE(KEYCODE_TILDE)      PORT_CHAR(27)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_Q)          PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab")                   PORT_CODE(KEYCODE_TAB)        PORT_CHAR(9)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_A)          PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Caps Lock")             PORT_CODE(KEYCODE_CAPSLOCK)   PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) // No physical toggle
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_Z)          PORT_CHAR('z') PORT_CHAR('Z')

	PORT_START("kbrow.9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)    PORT_PLAYER(1) PORT_8WAY PORT_CONDITION("controller_type", 0x01, EQUALS, 0x01)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)  PORT_PLAYER(1) PORT_8WAY PORT_CONDITION("controller_type", 0x01, EQUALS, 0x01)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)  PORT_PLAYER(1) PORT_8WAY PORT_CONDITION("controller_type", 0x01, EQUALS, 0x01)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(1) PORT_8WAY PORT_CONDITION("controller_type", 0x01, EQUALS, 0x01)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_BUTTON1)        PORT_PLAYER(1) PORT_CONDITION("controller_type", 0x01, EQUALS, 0x01)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_BUTTON2)        PORT_PLAYER(1) PORT_CONDITION("controller_type", 0x01, EQUALS, 0x01)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
/* The bit for the third button is officially undocumented: Amstrad joysticks actually
   use only two buttons. The only device that reads this bit is the AMX mouse, which uses
   dedicated hardware to connect to the joystick port.
*/
//  PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_BUTTON3)        PORT_PLAYER(1)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del")                   PORT_CODE(KEYCODE_INSERT)     PORT_CHAR(8)

/* Second joystick port need to be defined: the second joystick is daisy-chained on the back of the first one
   Joystick 2 is mapped to keyboard line 6.  */

/* The CPC supports at least two different brands of lightpens, probably connected to the expansion port */

INPUT_PORTS_END


/* Steph 2000-10-27 I remapped the 'Machine Name' Dip Switches (easier to understand) */
INPUT_CHANGED_MEMBER(amstrad_state::cpc_monitor_changed)
{
	if ((m_io_green_display->read()) & 0x01)
		amstrad_cpc_green_palette(*m_palette);
	else
		amstrad_cpc_palette(*m_palette);
}


static INPUT_PORTS_START( crtc_links )

/* the following are solder links on the circuit board. The links are open or closed when the
 * PCB is made, and are set depending on which country the Amstrad system was to go to
 * (reference: http://amstrad.cpc.free.fr/article.php?sid=26)

lk1 lk2 lk3 Manufacturer Name (CPC and CPC+ only):

0   0   0   Isp
0   0   1   Triumph (UK?)
0   1   0   Saisho (UK: Saisho is Dixons brand name for their electronic goods)
0   1   1   Solavox
1   0   0   Awa (Australia)
1   0   1   Schneider (Germany)
1   1   0   Orion (Japan?)
1   1   1   Amstrad (UK)

lk4     Frequency
0       60 Hz
1       50 Hz
*/
	PORT_START("solder_links")
	PORT_CONFNAME(0x07, 0x07, "Manufacturer Name")
	PORT_CONFSETTING(0x00, "Isp")
	PORT_CONFSETTING(0x01, "Triumph")
	PORT_CONFSETTING(0x02, "Saisho")
	PORT_CONFSETTING(0x03, "Solavox")
	PORT_CONFSETTING(0x04, "Awa")
	PORT_CONFSETTING(0x05, "Schneider")
	PORT_CONFSETTING(0x06, "Orion")
	PORT_CONFSETTING(0x07, "Amstrad")
	PORT_CONFNAME(0x10, 0x10, "TV Refresh Rate")
	PORT_CONFSETTING(0x00, "60 Hz")
	PORT_CONFSETTING(0x10, "50 Hz")

/* Part number Manufacturer Type number
   UM6845      UMC          0
   HD6845S     Hitachi      0
   UM6845R     UMC          1
   MC6845      Motorola     2
   AMS40489    Amstrad      3
   Pre-ASIC??? Amstrad?     4 In the "cost-down" CPC6128, the CRTC functionality is integrated into a single ASIC IC. This ASIC is often referred to as the "Pre-ASIC" because it preceded the CPC+ ASIC
As far as I know, the KC compact used HD6845S only.
*/
//  PORT_START("crtc")
//  PORT_CONFNAME( 0xFF, M6845_PERSONALITY_UM6845R, "CRTC Type")
//  PORT_CONFSETTING(M6845_PERSONALITY_UM6845, "Type 0 - UM6845")
//  PORT_CONFSETTING(M6845_PERSONALITY_HD6845S, "Type 0 - HD6845S")
//  PORT_CONFSETTING(M6845_PERSONALITY_UM6845R, "Type 1 - UM6845R")
//  PORT_CONFSETTING(M6845_PERSONALITY_GENUINE, "Type 2 - MC6845")
//  PORT_CONFSETTING(M6845_PERSONALITY_AMS40489, "Type 3 - AMS40489")
//  PORT_CONFSETTING(M6845_PERSONALITY_PREASIC, "Type 4 - Pre-ASIC")

	PORT_START("green_display")
	PORT_CONFNAME( 0x01, 0x00, "Monitor" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(amstrad_state::cpc_monitor_changed), 0)
	PORT_CONFSETTING(0x00, "CTM640 Colour Monitor" )
	PORT_CONFSETTING(0x01, "GT64 Green Monitor" )

INPUT_PORTS_END

static INPUT_PORTS_START( amx_mouse )
	PORT_START("mouse_input1")
	PORT_BIT(0xff , 0, IPT_MOUSE_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_PLAYER(1) PORT_CONDITION("controller_type", 0x02, EQUALS, 0x02)

	PORT_START("mouse_input2")
	PORT_BIT(0xff , 0, IPT_MOUSE_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_PLAYER(1) PORT_CONDITION("controller_type", 0x02, EQUALS, 0x02)

	PORT_START("mouse_input3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON4) PORT_NAME("Left mouse button")   PORT_CODE(MOUSECODE_BUTTON1) PORT_CONDITION("controller_type", 0x02, EQUALS, 0x02)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON5) PORT_NAME("Right mouse button")  PORT_CODE(MOUSECODE_BUTTON2) PORT_CONDITION("controller_type", 0x02, EQUALS, 0x02)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON6) PORT_NAME("Middle mouse button") PORT_CODE(MOUSECODE_BUTTON3) PORT_CONDITION("controller_type", 0x02, EQUALS, 0x02)

	PORT_START("controller_type")
	PORT_CONFNAME( 0x07, 0x01, "Joystick port device" )
	PORT_CONFSETTING(0x00, "Nothing" )
	PORT_CONFSETTING(0x01, "2-button joystick" )
	PORT_CONFSETTING(0x02, "AMX mouse interface" )
	PORT_CONFSETTING(0x04, "Cheetah 125 Special rotational joystick" )

INPUT_PORTS_END

static INPUT_PORTS_START( cheetah_125_special )
	PORT_START("cheetah")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)    PORT_PLAYER(1) PORT_8WAY PORT_NAME("Cheetah 125 Up") PORT_CONDITION("controller_type", 0x04, EQUALS, 0x04)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)  PORT_PLAYER(1) PORT_8WAY PORT_NAME("Cheetah 125 Down") PORT_CONDITION("controller_type", 0x04, EQUALS, 0x04)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)  PORT_PLAYER(1) PORT_8WAY PORT_NAME("Cheetah 125 Left") PORT_CONDITION("controller_type", 0x04, EQUALS, 0x04)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(1) PORT_8WAY PORT_NAME("Cheetah 125 Right") PORT_CONDITION("controller_type", 0x04, EQUALS, 0x04)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_BUTTON1)        PORT_PLAYER(1) PORT_NAME("Cheetah 125 Fire") PORT_CONDITION("controller_type", 0x04, EQUALS, 0x04)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_BUTTON2)        PORT_PLAYER(1) PORT_NAME("Cheetah 125 Rotate Left") PORT_CONDITION("controller_type", 0x04, EQUALS, 0x04)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_BUTTON3)        PORT_PLAYER(1) PORT_NAME("Cheetah 125 Rotate Right") PORT_CONDITION("controller_type", 0x04, EQUALS, 0x04)
INPUT_PORTS_END

static INPUT_PORTS_START( cpc464 )
	PORT_INCLUDE(amstrad_keyboard)
	PORT_INCLUDE(crtc_links)
	PORT_INCLUDE(amx_mouse)
	PORT_INCLUDE(cheetah_125_special)
INPUT_PORTS_END


static INPUT_PORTS_START( cpc664 )
	PORT_INCLUDE(amstrad_keyboard)

	PORT_MODIFY("kbrow.0")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad f9")             PORT_CODE(KEYCODE_9_PAD)      PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad f6")             PORT_CODE(KEYCODE_6_PAD)      PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad f3")             PORT_CODE(KEYCODE_3_PAD)      PORT_CHAR(UCHAR_MAMEKEY(3_PAD))

	PORT_MODIFY("kbrow.1")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad f7")             PORT_CODE(KEYCODE_7_PAD)      PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad f8")             PORT_CODE(KEYCODE_8_PAD)      PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad f5")             PORT_CODE(KEYCODE_5_PAD)      PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad f1")             PORT_CODE(KEYCODE_1_PAD)      PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad f2")             PORT_CODE(KEYCODE_2_PAD)      PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad f0")             PORT_CODE(KEYCODE_0_PAD)      PORT_CHAR(UCHAR_MAMEKEY(0_PAD))

	PORT_MODIFY("kbrow.2")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad f4")             PORT_CODE(KEYCODE_4_PAD)      PORT_CHAR(UCHAR_MAMEKEY(4_PAD))

	PORT_INCLUDE(crtc_links)
	PORT_INCLUDE(amx_mouse)
	PORT_INCLUDE(cheetah_125_special)
INPUT_PORTS_END


static INPUT_PORTS_START( cpc6128 )
	PORT_INCLUDE(amstrad_keyboard)

	PORT_MODIFY("kbrow.0")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f9")                    PORT_CODE(KEYCODE_9_PAD)      PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f6")                    PORT_CODE(KEYCODE_6_PAD)      PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f3")                    PORT_CODE(KEYCODE_3_PAD)      PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter")                 PORT_CODE(KEYCODE_RALT)       PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".")                     PORT_CODE(KEYCODE_DEL_PAD)    PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))

	PORT_MODIFY("kbrow.1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Copy")                  PORT_CODE(KEYCODE_LALT)       PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f7")                    PORT_CODE(KEYCODE_7_PAD)      PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f8")                    PORT_CODE(KEYCODE_8_PAD)      PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f5")                    PORT_CODE(KEYCODE_5_PAD)      PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f1")                    PORT_CODE(KEYCODE_1_PAD)      PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f2")                    PORT_CODE(KEYCODE_2_PAD)      PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f0")                    PORT_CODE(KEYCODE_0_PAD)      PORT_CHAR(UCHAR_MAMEKEY(0_PAD))

	PORT_MODIFY("kbrow.2")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return")                PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f4")                    PORT_CODE(KEYCODE_4_PAD)      PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_RCONTROL)   PORT_CHAR('\\') PORT_CHAR('`')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Control")               PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_SHIFT_2)

	PORT_INCLUDE(crtc_links)
	PORT_INCLUDE(amx_mouse)
	PORT_INCLUDE(cheetah_125_special)
INPUT_PORTS_END


/* This CPC6128 sold in France has the AZERTY layout. Reference: http://amstrad.cpc.free.fr/amstrad/cpc6128.htm */
static INPUT_PORTS_START( cpc6128f )
	PORT_INCLUDE(amstrad_keyboard)

	PORT_MODIFY("kbrow.0")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f9")                    PORT_CODE(KEYCODE_9_PAD)      PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f6")                    PORT_CODE(KEYCODE_6_PAD)      PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f3")                    PORT_CODE(KEYCODE_3_PAD)      PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter")                 PORT_CODE(KEYCODE_RALT)       PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".")                     PORT_CODE(KEYCODE_DEL_PAD)    PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))

	PORT_MODIFY("kbrow.1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Copy")                  PORT_CODE(KEYCODE_LALT)       PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f7")                    PORT_CODE(KEYCODE_7_PAD)      PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f8")                    PORT_CODE(KEYCODE_8_PAD)      PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f5")                    PORT_CODE(KEYCODE_5_PAD)      PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f1")                    PORT_CODE(KEYCODE_1_PAD)      PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f2")                    PORT_CODE(KEYCODE_2_PAD)      PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f0")                    PORT_CODE(KEYCODE_0_PAD)      PORT_CHAR(UCHAR_MAMEKEY(0_PAD))

	PORT_MODIFY("kbrow.2")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('*') PORT_CHAR('<')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return")                PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('#') PORT_CHAR('>')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f4")                    PORT_CODE(KEYCODE_4_PAD)      PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_RCONTROL)   PORT_CHAR('$') PORT_CHAR('@') PORT_CHAR('\\')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Control")               PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_SHIFT_2)

	PORT_MODIFY("kbrow.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_MINUS)      PORT_CHAR(')') PORT_CHAR('[')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('^') PORT_CHAR(U'¦','|')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(U'ù') PORT_CHAR('%')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_COLON)      PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_STOP)       PORT_CHAR(':') PORT_CHAR('/')

	PORT_MODIFY("kbrow.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_0)          PORT_CHAR(U'à') PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_9)          PORT_CHAR(U'ç') PORT_CHAR('9')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_M)          PORT_CHAR(',') PORT_CHAR('?')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(';') PORT_CHAR('.')

	PORT_MODIFY("kbrow.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_8)          PORT_CHAR('!') PORT_CHAR('8')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_7)          PORT_CHAR(U'è') PORT_CHAR('7')

	PORT_MODIFY("kbrow.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_6)          PORT_CHAR(']') PORT_CHAR('6')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_5)          PORT_CHAR('(') PORT_CHAR('5')

	PORT_MODIFY("kbrow.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_4)          PORT_CHAR('\'') PORT_CHAR('4')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_3)          PORT_CHAR('\"') PORT_CHAR('3')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_W)          PORT_CHAR('z') PORT_CHAR('Z')

	PORT_MODIFY("kbrow.8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_1)          PORT_CHAR('&') PORT_CHAR('1')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_2)          PORT_CHAR(U'é') PORT_CHAR('2') PORT_CHAR('~')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_Q)          PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_A)          PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_Z)          PORT_CHAR('w') PORT_CHAR('W')

	PORT_INCLUDE(crtc_links)
	PORT_INCLUDE(amx_mouse)
	PORT_INCLUDE(cheetah_125_special)
INPUT_PORTS_END

static INPUT_PORTS_START( cpc6128s )
	PORT_INCLUDE(cpc6128)

	PORT_MODIFY("kbrow.2")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(U'Ü')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR(U'É')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_RCONTROL)   PORT_CHAR('/') PORT_CHAR('?')

	PORT_MODIFY("kbrow.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('+') PORT_CHAR('*')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR(U'Å')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(U'Ä')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_COLON)      PORT_CHAR(U'Ö')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('<') PORT_CHAR('>')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR(':')

	PORT_MODIFY("kbrow.4")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR(';')
INPUT_PORTS_END

static INPUT_PORTS_START( cpc6128sp )
	PORT_INCLUDE(cpc6128)

	PORT_MODIFY("kbrow.2")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[")                     PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]")                     PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR(']') PORT_CHAR('+')

	PORT_MODIFY("kbrow.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('^') PORT_CHAR(U'\u20a7') // U+20A7 = ₧ (Peseta)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_COLON)      PORT_CHAR(U'ñ') PORT_CHAR(U'Ñ')
INPUT_PORTS_END

/*
 * The BIOS of the KC Compact would be able to recognize the keypresses
 * generated by F5-F9. Unfortunately these keys are not present on the
 * keyboard! Reference: http://www.cepece.info/amstrad/docs/kcc/kcc01.jpg
 *
 */
static INPUT_PORTS_START( kccomp )
	PORT_INCLUDE(amstrad_keyboard)

	PORT_MODIFY("kbrow.0")
	PORT_BIT(0x08, 0x08, IPT_UNUSED)
	PORT_BIT(0x10, 0x10, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f3")                    PORT_CODE(KEYCODE_3_PAD)      PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter")                 PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".")                     PORT_CODE(KEYCODE_DEL_PAD)    PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))

	PORT_MODIFY("kbrow.1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Copy")                  PORT_CODE(KEYCODE_RALT)       PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT(0x04, 0x04, IPT_UNUSED)
	PORT_BIT(0x08, 0x08, IPT_UNUSED)
	PORT_BIT(0x10, 0x10, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f1")                    PORT_CODE(KEYCODE_1_PAD)      PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f2")                    PORT_CODE(KEYCODE_2_PAD)      PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f0")                    PORT_CODE(KEYCODE_0_PAD)      PORT_CHAR(UCHAR_MAMEKEY(0_PAD))

	PORT_MODIFY("kbrow.2")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return")                PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f4")                    PORT_CODE(KEYCODE_4_PAD)      PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_RCONTROL)   PORT_CHAR('\\') PORT_CHAR('`')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Control")               PORT_CODE(KEYCODE_LALT)       PORT_CHAR(UCHAR_SHIFT_2)

	PORT_INCLUDE(crtc_links)
	PORT_INCLUDE(amx_mouse)
	PORT_INCLUDE(cheetah_125_special)
INPUT_PORTS_END


static INPUT_PORTS_START( plus )
	PORT_INCLUDE(cpc664)

	/* The CPC+ and GX4000 adds support for analogue controllers.
	   Up to two joysticks or four paddles can be used, although the ASIC supports twice that.
	   Read at &6808-&680f in ASIC RAM
	   I am unsure if these are even close to correct.

	UPDATE: the analog port supports (probably with an Y-cable) up to two analog joysticks
	with two buttons each or four paddles with one button each. The CPC+/GX4000 have also an
	AUX port which supports a lightgun/lightpen with two buttons.
	Since all these devices have their dedicated connector, two digital joystick, two analog joysticks
	and one lightgun can be connected at the same moment.

	The connectors' description for both CPCs and CPC+'s can be found at http://www.hardwarebook.info/Category:Computer */

	PORT_START("analog.0")
	PORT_BIT(0x3f , 0, IPT_AD_STICK_X)
	PORT_SENSITIVITY(100)
	PORT_KEYDELTA(10)
	PORT_PLAYER(1)

	PORT_START("analog.1")
	PORT_BIT(0x3f , 0, IPT_AD_STICK_Y)
	PORT_SENSITIVITY(100)
	PORT_KEYDELTA(10)
	PORT_PLAYER(1)

	PORT_START("analog.2")
	PORT_BIT(0x3f , 0, IPT_AD_STICK_X)
	PORT_SENSITIVITY(100)
	PORT_KEYDELTA(10)
	PORT_PLAYER(2)

	PORT_START("analog.3")
	PORT_BIT(0x3f , 0, IPT_AD_STICK_Y)
	PORT_SENSITIVITY(100)
	PORT_KEYDELTA(10)
	PORT_PLAYER(2)

// Not used, but are here for completeness
	PORT_START("analog.4")
	PORT_BIT(0x3f , 0, IPT_TRACKBALL_X)
	PORT_SENSITIVITY(100)
	PORT_KEYDELTA(10)
	PORT_PLAYER(3)

	PORT_START("analog.5")
	PORT_BIT(0x3f , 0, IPT_TRACKBALL_Y)
	PORT_SENSITIVITY(100)
	PORT_KEYDELTA(10)
	PORT_PLAYER(3)

	PORT_START("analog.6")
	PORT_BIT(0x3f , 0, IPT_TRACKBALL_X)
	PORT_SENSITIVITY(100)
	PORT_KEYDELTA(10)
	PORT_PLAYER(4)

	PORT_START("analog.7")
	PORT_BIT(0x3f , 0, IPT_TRACKBALL_Y)
	PORT_SENSITIVITY(100)
	PORT_KEYDELTA(10)
	PORT_PLAYER(4)
INPUT_PORTS_END


static INPUT_PORTS_START( gx4000 )

	// The GX4000 is a console, so no keyboard access other than the joysticks.
	PORT_START("kbrow.0")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kbrow.1")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kbrow.2")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kbrow.3")
	PORT_BIT(0x07, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME(DEF_STR(Pause)) PORT_CODE(KEYCODE_1)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kbrow.4")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kbrow.5")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kbrow.6")  // Joystick 2
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)    PORT_PLAYER(2) PORT_8WAY
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)  PORT_PLAYER(2) PORT_8WAY
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)  PORT_PLAYER(2) PORT_8WAY
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_BUTTON1)        PORT_PLAYER(2)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_BUTTON2)        PORT_PLAYER(2)
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kbrow.7")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kbrow.8")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kbrow.9")  // Joystick 1
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)    PORT_PLAYER(1) PORT_8WAY
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)  PORT_PLAYER(1) PORT_8WAY
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)  PORT_PLAYER(1) PORT_8WAY
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_BUTTON1)        PORT_PLAYER(1)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_BUTTON2)        PORT_PLAYER(1)
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_INCLUDE(crtc_links)  // included to keep the driver happy

	PORT_START("analog.0")
	PORT_BIT(0x3f , 0, IPT_TRACKBALL_X)
	PORT_SENSITIVITY(100)
	PORT_KEYDELTA(10)
	PORT_PLAYER(1)

	PORT_START("analog.1")
	PORT_BIT(0x3f , 0, IPT_TRACKBALL_Y)
	PORT_SENSITIVITY(100)
	PORT_KEYDELTA(10)
	PORT_PLAYER(1)

	PORT_START("analog.2")
	PORT_BIT(0x3f , 0, IPT_TRACKBALL_X)
	PORT_SENSITIVITY(100)
	PORT_KEYDELTA(10)
	PORT_PLAYER(2)

	PORT_START("analog.3")
	PORT_BIT(0x3f , 0, IPT_TRACKBALL_Y)
	PORT_SENSITIVITY(100)
	PORT_KEYDELTA(10)
	PORT_PLAYER(2)

// Not used, but are here for completeness
	PORT_START("analog.4")
	PORT_BIT(0x3f , 0, IPT_TRACKBALL_X)
	PORT_SENSITIVITY(100)
	PORT_KEYDELTA(10)
	PORT_PLAYER(3)

	PORT_START("analog.5")
	PORT_BIT(0x3f , 0, IPT_TRACKBALL_Y)
	PORT_SENSITIVITY(100)
	PORT_KEYDELTA(10)
	PORT_PLAYER(3)

	PORT_START("analog.6")
	PORT_BIT(0x3f , 0, IPT_TRACKBALL_X)
	PORT_SENSITIVITY(100)
	PORT_KEYDELTA(10)
	PORT_PLAYER(4)

	PORT_START("analog.7")
	PORT_BIT(0x3f , 0, IPT_TRACKBALL_Y)
	PORT_SENSITIVITY(100)
	PORT_KEYDELTA(10)
	PORT_PLAYER(4)
INPUT_PORTS_END


static INPUT_PORTS_START( aleste )
	PORT_INCLUDE(amstrad_keyboard)

	PORT_MODIFY( "kbrow.9" )
	/* Documentation marks this input as "R/L", it's purpose is unknown - I can't even find it on the keyboard */
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R  L")      PORT_CODE(KEYCODE_PGUP)

	PORT_START( "kbrow.10" )
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1  F6")    PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2  F7")    PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3  F8")    PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F4  F9")    PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F5  F10")   PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("HELP")      PORT_CODE(KEYCODE_PGDN)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("INS")       PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ъ  _")    PORT_CODE(KEYCODE_HOME)

	PORT_INCLUDE(crtc_links)
	PORT_INCLUDE(amx_mouse)
	PORT_INCLUDE(cheetah_125_special)
INPUT_PORTS_END



/*************************************
 *
 *  Machine drivers
 *
 *************************************/

/* actual clock to CPU is 4 MHz, but it is slowed by memory
accessess. A HALT is used for every memory access by the CPU.
This stretches the timing for opcodes, and gives an effective
speed of 3.8 MHz */

/* Info about structures below:

    The Amstrad has a CPU running at 4 MHz, slowed with wait states.
    I have measured 19968 NOP instructions per frame, which gives,
    50.08 fps as the tv refresh rate.

  There are 312 lines on a PAL screen, giving 64us per line.

  There is only 50us visible per line, and 35*8 lines visible on the screen.

  This is the reason why the displayed area is not the same as the visible area.
 */


static void amstrad_floppies(device_slot_interface &device)
{
	device.option_add("3ssdd", FLOPPY_3_SSDD);
	device.option_add("35ssdd", FLOPPY_35_DD);
}

void amstrad_state::amstrad_floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_IPF_FORMAT);
}

static void aleste_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
}

void amstrad_state::aleste_floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_MSX_FORMAT);
}

void amstrad_state::cpcplus_cartslot(machine_config &config)
{
	generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "gx4000_cart", "bin,cpr"));
	cartslot.set_must_be_loaded(true);
	cartslot.set_device_load(FUNC(amstrad_state::amstrad_plus_cartridge));

	SOFTWARE_LIST(config, "cart_list").set_original("gx4000");
}

void cpc464_exp_cards(device_slot_interface &device)
{
	device.option_add("ddi1", CPC_DDI1);
	device.option_add("ssa1", CPC_SSA1);
	device.option_add("dkspeech", CPC_DKSPEECH);
	device.option_add("rom", CPC_ROM);
	device.option_add("multiface2", CPC_MFACE2);
	device.option_add("pds", CPC_PDS);
	device.option_add("rs232", CPC_RS232);
	device.option_add("amsrs232", CPC_RS232_AMS);
	device.option_add("sf2", CPC_SYMBIFACE2);
	device.option_add("amdrum", CPC_AMDRUM);
	device.option_add("playcity", CPC_PLAYCITY);
	device.option_add("smartwatch", CPC_SMARTWATCH);
	device.option_add("brunword4", CPC_BRUNWORD_MK4);
	device.option_add("hd20", CPC_HD20);
	device.option_add("doubler", CPC_DOUBLER);
	device.option_add("transtape", CPC_TRANSTAPE);
	device.option_add("musicmachine", CPC_MUSICMACHINE);
}

void cpc_exp_cards(device_slot_interface &device)
{
	device.option_add("ssa1", CPC_SSA1);
	device.option_add("dkspeech", CPC_DKSPEECH);
	device.option_add("rom", CPC_ROM);
	device.option_add("multiface2", CPC_MFACE2);
	device.option_add("pds", CPC_PDS);
	device.option_add("rs232", CPC_RS232);
	device.option_add("amsrs232", CPC_RS232_AMS);
	device.option_add("sf2", CPC_SYMBIFACE2);
	device.option_add("amdrum", CPC_AMDRUM);
	device.option_add("playcity", CPC_PLAYCITY);
	device.option_add("smartwatch", CPC_SMARTWATCH);
	device.option_add("brunword4", CPC_BRUNWORD_MK4);
	device.option_add("hd20", CPC_HD20);
	device.option_add("doubler", CPC_DOUBLER);
	device.option_add("transtape", CPC_TRANSTAPE);
	device.option_add("musicmachine", CPC_MUSICMACHINE);
}

void cpcplus_exp_cards(device_slot_interface &device)
{
	device.option_add("ssa1", CPC_SSA1);
	device.option_add("dkspeech", CPC_DKSPEECH);
	device.option_add("rom", CPC_ROM);
	device.option_add("pds", CPC_PDS);
	device.option_add("rs232", CPC_RS232);
	device.option_add("amsrs232", CPC_RS232_AMS);
	device.option_add("sf2", CPC_SYMBIFACE2);
	device.option_add("amdrum", CPC_AMDRUM);
	device.option_add("playcity", CPC_PLAYCITY);
	device.option_add("smartwatch", CPC_SMARTWATCH);
	device.option_add("hd20", CPC_HD20);
	device.option_add("doubler", CPC_DOUBLER);
	device.option_add("transtape", CPC_TRANSTAPE);  // Plus compatible?
	device.option_add("musicmachine", CPC_MUSICMACHINE);
}

void aleste_exp_cards(device_slot_interface &device)
{
	device.option_add("ssa1", CPC_SSA1);
	device.option_add("dkspeech", CPC_DKSPEECH);
	device.option_add("rom", CPC_ROM);
	device.option_add("multiface2", CPC_MFACE2);
	device.option_add("pds", CPC_PDS);
	device.option_add("rs232", CPC_RS232);
	device.option_add("amsrs232", CPC_RS232_AMS);
	device.option_add("sf2", CPC_SYMBIFACE2);
	device.option_add("amdrum", CPC_AMDRUM);
	device.option_add("playcity", CPC_PLAYCITY);
	device.option_add("smartwatch", CPC_SMARTWATCH);
	device.option_add("brunword4", CPC_BRUNWORD_MK4);
	device.option_add("hd20", CPC_HD20);
	device.option_add("doubler", CPC_DOUBLER);
	device.option_add("transtape", CPC_TRANSTAPE);
	device.option_add("musicmachine", CPC_MUSICMACHINE);
	device.option_add("magicsound", AL_MAGICSOUND);
}

void amstrad_state::amstrad_base(machine_config &config)
{
	/* Machine hardware */
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &amstrad_state::amstrad_mem);
	m_maincpu->set_addrmap(AS_IO, &amstrad_state::amstrad_io);
	m_maincpu->set_irq_acknowledge_callback(FUNC(amstrad_state::amstrad_cpu_acknowledge_int));

	config.set_maximum_quantum(attotime::from_hz(60));

	MCFG_MACHINE_START_OVERRIDE(amstrad_state, amstrad )
	MCFG_MACHINE_RESET_OVERRIDE(amstrad_state, amstrad )

	i8255_device &ppi(I8255(config, "ppi8255"));
	ppi.in_pa_callback().set(FUNC(amstrad_state::amstrad_ppi_porta_r));
	ppi.out_pa_callback().set(FUNC(amstrad_state::amstrad_ppi_porta_w));
	ppi.in_pb_callback().set(FUNC(amstrad_state::amstrad_ppi_portb_r));
	ppi.out_pc_callback().set(FUNC(amstrad_state::amstrad_ppi_portc_w));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(16_MHz_XTAL, 1024, 32, 32 + 640 + 64, 312, 56 + 15, 200 + 15);
	m_screen->set_screen_update(FUNC(amstrad_state::screen_update_amstrad));
	m_screen->screen_vblank().set(FUNC(amstrad_state::screen_vblank_amstrad));
	m_screen->set_video_attributes(VIDEO_ALWAYS_UPDATE);
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(amstrad_state::amstrad_cpc_palette), 32);

	HD6845S(config, m_crtc, 16_MHz_XTAL / 16);
	m_crtc->set_screen(nullptr);
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(16);
	m_crtc->out_de_callback().set(FUNC(amstrad_state::amstrad_de_changed));
	m_crtc->out_hsync_callback().set(FUNC(amstrad_state::amstrad_hsync_changed));
	m_crtc->out_vsync_callback().set(FUNC(amstrad_state::amstrad_vsync_changed));
	m_crtc->out_cur_callback().set("exp", FUNC(cpc_expansion_slot_device::cursor_w));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	AY8912(config, m_ay, 16_MHz_XTAL / 16);
	m_ay->port_a_read_callback().set(FUNC(amstrad_state::amstrad_psg_porta_read));
	m_ay->add_route(ALL_OUTPUTS, "mono", 0.25);

	/* printer */
	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(amstrad_state::write_centronics_busy));

	/* snapshot */
	SNAPSHOT(config, "snapshot", "sna").set_load_callback(FUNC(amstrad_state::snapshot_cb));

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(cdt_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.10);
	m_cassette->set_interface("cpc_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("cpc_cass");
}

void amstrad_state::cpc464(machine_config &config)
{
	amstrad_base(config);

	cpc_expansion_slot_device &exp(CPC_EXPANSION_SLOT(config, "exp", 16_MHz_XTAL / 4, cpc464_exp_cards, nullptr));
	exp.set_cpu_tag(m_maincpu);
	exp.irq_callback().set_inputline("maincpu", 0);
	exp.nmi_callback().set_inputline("maincpu", INPUT_LINE_NMI);
	exp.romdis_callback().set(FUNC(amstrad_state::cpc_romdis));  // ROMDIS
	exp.rom_select_callback().set(FUNC(amstrad_state::rom_select));

	/* internal ram */
	RAM(config, m_ram).set_default_size("64K").set_extra_options("128K,320K,576K");
}

void amstrad_state::cpc664(machine_config &config)
{
	amstrad_base(config);
	UPD765A(config, m_fdc, 16_MHz_XTAL / 4, true, true);
	FLOPPY_CONNECTOR(config, "upd765:0", amstrad_floppies, "3ssdd", amstrad_state::amstrad_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "upd765:1", amstrad_floppies, "35ssdd", amstrad_state::amstrad_floppy_formats).enable_sound(true);
	SOFTWARE_LIST(config, "flop_list").set_original("cpc_flop");

	cpc_expansion_slot_device &exp(CPC_EXPANSION_SLOT(config, "exp", 16_MHz_XTAL / 4, cpc_exp_cards, nullptr));
	exp.set_cpu_tag(m_maincpu);
	exp.irq_callback().set_inputline("maincpu", 0);
	exp.nmi_callback().set_inputline("maincpu", INPUT_LINE_NMI);
	exp.romdis_callback().set(FUNC(amstrad_state::cpc_romdis));  // ROMDIS
	exp.rom_select_callback().set(FUNC(amstrad_state::rom_select));

	/* internal ram */
	RAM(config, m_ram).set_default_size("64K").set_extra_options("128K,320K,576K");
}

void amstrad_state::cpc6128(machine_config &config)
{
	amstrad_base(config);
	UPD765A(config, m_fdc, 16_MHz_XTAL / 4, true, true);
	FLOPPY_CONNECTOR(config, "upd765:0", amstrad_floppies, "3ssdd", amstrad_state::amstrad_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "upd765:1", amstrad_floppies, "35ssdd", amstrad_state::amstrad_floppy_formats).enable_sound(true);
	SOFTWARE_LIST(config, "flop_list").set_original("cpc_flop");

	cpc_expansion_slot_device &exp(CPC_EXPANSION_SLOT(config, "exp", 16_MHz_XTAL / 4, cpc_exp_cards, nullptr));
	exp.set_cpu_tag(m_maincpu);
	exp.irq_callback().set_inputline("maincpu", 0);
	exp.nmi_callback().set_inputline("maincpu", INPUT_LINE_NMI);
	exp.romdis_callback().set(FUNC(amstrad_state::cpc_romdis));  // ROMDIS
	exp.rom_select_callback().set(FUNC(amstrad_state::rom_select));

	/* internal ram */
	RAM(config, m_ram).set_default_size("128K").set_extra_options("320K,576K");
}


void amstrad_state::kccomp(machine_config &config)
{
	cpc6128(config);
	MCFG_MACHINE_START_OVERRIDE(amstrad_state,kccomp)
	MCFG_MACHINE_RESET_OVERRIDE(amstrad_state,kccomp)

	m_palette->set_init(FUNC(amstrad_state::kccomp_palette));
}


void amstrad_state::cpcplus(machine_config &config)
{
	/* Machine hardware */
	Z80(config, m_maincpu, 40_MHz_XTAL / 10);
	m_maincpu->set_addrmap(AS_PROGRAM, &amstrad_state::amstrad_mem);
	m_maincpu->set_addrmap(AS_IO, &amstrad_state::amstrad_io);
	m_maincpu->set_irq_acknowledge_callback(FUNC(amstrad_state::amstrad_cpu_acknowledge_int));

	config.set_maximum_quantum(attotime::from_hz(60));

	MCFG_MACHINE_START_OVERRIDE(amstrad_state, plus )
	MCFG_MACHINE_RESET_OVERRIDE(amstrad_state, plus )

	ams40489_ppi_device &ppi(AMS40489_PPI(config, "ppi8255"));
	ppi.in_pa_callback().set(FUNC(amstrad_state::amstrad_ppi_porta_r));
	ppi.out_pa_callback().set(FUNC(amstrad_state::amstrad_ppi_porta_w));
	ppi.in_pb_callback().set(FUNC(amstrad_state::amstrad_ppi_portb_r));
	ppi.out_pc_callback().set(FUNC(amstrad_state::amstrad_ppi_portc_w));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw((40_MHz_XTAL * 2) / 5, 1024, 32, 32 + 640 + 64, 312, 56 + 15, 200 + 15);
	m_screen->set_screen_update(FUNC(amstrad_state::screen_update_amstrad));
	m_screen->screen_vblank().set(FUNC(amstrad_state::screen_vblank_amstrad));
	m_screen->set_video_attributes(VIDEO_ALWAYS_UPDATE);
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(amstrad_state::amstrad_plus_palette), 4096);

	AMS40489(config, m_crtc, 40_MHz_XTAL / 40);
	m_crtc->set_screen(nullptr);
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(16);
	m_crtc->out_de_callback().set(FUNC(amstrad_state::amstrad_plus_de_changed));
	m_crtc->out_hsync_callback().set(FUNC(amstrad_state::amstrad_plus_hsync_changed));
	m_crtc->out_vsync_callback().set(FUNC(amstrad_state::amstrad_plus_vsync_changed));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	AY8912(config, m_ay, 40_MHz_XTAL / 40);
	m_ay->port_a_read_callback().set(FUNC(amstrad_state::amstrad_psg_porta_read));
	m_ay->add_route(ALL_OUTPUTS, "mono", 0.25);

	/* printer */
	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(amstrad_state::write_centronics_busy));

	/* snapshot */
	SNAPSHOT(config, "snapshot", "sna").set_load_callback(FUNC(amstrad_state::snapshot_cb));

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(cdt_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.10);
	m_cassette->set_interface("cpc_cass");
	SOFTWARE_LIST(config, "cass_list").set_original("cpc_cass");

	UPD765A(config, m_fdc, 40_MHz_XTAL / 10, true, true);

	cpcplus_cartslot(config);

	FLOPPY_CONNECTOR(config, "upd765:0", amstrad_floppies, "3ssdd", amstrad_state::amstrad_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "upd765:1", amstrad_floppies, "35ssdd", amstrad_state::amstrad_floppy_formats).enable_sound(true);
	SOFTWARE_LIST(config, "flop_list").set_original("cpc_flop");

	cpc_expansion_slot_device &exp(CPC_EXPANSION_SLOT(config, "exp", 40_MHz_XTAL / 10, cpcplus_exp_cards, nullptr));
	exp.set_cpu_tag(m_maincpu);
	exp.irq_callback().set_inputline("maincpu", 0);
	exp.nmi_callback().set_inputline("maincpu", INPUT_LINE_NMI);
	exp.romdis_callback().set(FUNC(amstrad_state::cpc_romdis));  // ROMDIS
	exp.rom_select_callback().set(FUNC(amstrad_state::rom_select));

	/* internal ram */
	RAM(config, m_ram).set_default_size("128K").set_extra_options("64K,320K,576K");
}


void amstrad_state::gx4000(machine_config &config)
{
	/* Machine hardware */
	Z80(config, m_maincpu, 40_MHz_XTAL / 10);
	m_maincpu->set_addrmap(AS_PROGRAM, &amstrad_state::amstrad_mem);
	m_maincpu->set_addrmap(AS_IO, &amstrad_state::amstrad_io);
	m_maincpu->set_irq_acknowledge_callback(FUNC(amstrad_state::amstrad_cpu_acknowledge_int));

	config.set_maximum_quantum(attotime::from_hz(60));

	MCFG_MACHINE_START_OVERRIDE(amstrad_state, gx4000 )
	MCFG_MACHINE_RESET_OVERRIDE(amstrad_state, gx4000 )

	ams40489_ppi_device &ppi(AMS40489_PPI(config, "ppi8255"));
	ppi.in_pa_callback().set(FUNC(amstrad_state::amstrad_ppi_porta_r));
	ppi.out_pa_callback().set(FUNC(amstrad_state::amstrad_ppi_porta_w));
	ppi.in_pb_callback().set(FUNC(amstrad_state::amstrad_ppi_portb_r));
	ppi.out_pc_callback().set(FUNC(amstrad_state::amstrad_ppi_portc_w));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw((40_MHz_XTAL * 2) / 5, 1024, 32, 32 + 640 + 64, 312, 56 + 15, 200 + 15);
	m_screen->set_screen_update(FUNC(amstrad_state::screen_update_amstrad));
	m_screen->screen_vblank().set(FUNC(amstrad_state::screen_vblank_amstrad));
	m_screen->set_video_attributes(VIDEO_ALWAYS_UPDATE);
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(amstrad_state::amstrad_plus_palette), 4096);

	AMS40489(config, m_crtc, 40_MHz_XTAL / 40);
	m_crtc->set_screen(nullptr);
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(16);
	m_crtc->out_de_callback().set(FUNC(amstrad_state::amstrad_plus_de_changed));
	m_crtc->out_hsync_callback().set(FUNC(amstrad_state::amstrad_plus_hsync_changed));
	m_crtc->out_vsync_callback().set(FUNC(amstrad_state::amstrad_plus_vsync_changed));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	AY8912(config, m_ay, 40_MHz_XTAL / 40);
	m_ay->port_a_read_callback().set(FUNC(amstrad_state::amstrad_psg_porta_read));
	m_ay->add_route(ALL_OUTPUTS, "mono", 0.25);

	cpcplus_cartslot(config);

	/* internal ram */
	RAM(config, m_ram).set_default_size("64K");
}


void amstrad_state::aleste(machine_config &config)
{
	cpc6128(config);
	MCFG_MACHINE_START_OVERRIDE(amstrad_state,aleste)
	MCFG_MACHINE_RESET_OVERRIDE(amstrad_state,aleste)

	AY8912(config.replace(), m_ay, 16_MHz_XTAL / 16);
	m_ay->port_a_read_callback().set(FUNC(amstrad_state::amstrad_psg_porta_read));
	m_ay->add_route(ALL_OUTPUTS, "mono", 0.25);

	m_palette->set_entries(32+64);
	m_palette->set_init(FUNC(amstrad_state::aleste_palette));

	MC146818(config, m_rtc, 4.194304_MHz_XTAL);

	I8272A(config.replace(), m_fdc, 16_MHz_XTAL / 4, true);

	cpc_expansion_slot_device &exp(CPC_EXPANSION_SLOT(config.replace(), "exp", 16_MHz_XTAL / 4, aleste_exp_cards, nullptr));
	exp.set_cpu_tag(m_maincpu);
	exp.irq_callback().set_inputline("maincpu", 0);
	exp.nmi_callback().set_inputline("maincpu", INPUT_LINE_NMI);
	exp.romdis_callback().set(FUNC(amstrad_state::cpc_romdis));  // ROMDIS
	exp.rom_select_callback().set(FUNC(amstrad_state::rom_select));

	FLOPPY_CONNECTOR(config, "upd765:0", aleste_floppies, "35dd", amstrad_state::aleste_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "upd765:1", aleste_floppies, "35dd", amstrad_state::aleste_floppy_formats).enable_sound(true);

	SOFTWARE_LIST(config.replace(), "flop_list").set_original("aleste");
	SOFTWARE_LIST(config, "cpc_list").set_compatible("cpc_flop");

	/* internal ram */
	m_ram->set_default_size("2M");
}



/*************************************
 *
 *  ROM definitions
 *
 *************************************/

// cpc6128.rom contains OS in first 16k, BASIC in second 16k
// cpcados.rom contains Amstrad DOS

// I am loading the roms outside of the Z80 memory area, because they are banked.
ROM_START( cpc6128 )
	// this defines the total memory size - 64k ram, 16k OS, 16k BASIC, 16k DOS
	ROM_REGION(0x020000, "maincpu", 0)
	/* load the os to offset 0x01000 from memory base */
	ROM_LOAD("cpc6128.rom", 0x10000, 0x8000, CRC(9e827fe1) SHA1(5977adbad3f7c1e0e082cd02fe76a700d9860c30))
	ROM_LOAD("cpcados.rom", 0x18000, 0x4000, CRC(1fe22ecd) SHA1(39102c8e9cb55fcc0b9b62098780ed4a3cb6a4bb))
ROM_END


ROM_START( cpc6128f )
	// this defines the total memory size (128kb))- 64k ram, 16k OS, 16k BASIC, 16k DOS +16k
	ROM_REGION(0x020000, "maincpu", 0)

	// load the os to offset 0x01000 from memory base
	ROM_LOAD("cpc6128f.rom", 0x10000, 0x8000, CRC(1574923b) SHA1(200d59076dfef36db061d6d7d21d80021cab1237))
	ROM_LOAD("cpcados.rom",  0x18000, 0x4000, CRC(1fe22ecd) SHA1(39102c8e9cb55fcc0b9b62098780ed4a3cb6a4bb))
ROM_END


ROM_START( cpc6128s )
	// this defines the total memory size (128kb))- 64k ram, 16k OS, 16k BASIC, 16k DOS +16k
	ROM_REGION(0x020000, "maincpu", 0)

	/* load the os to offset 0x01000 from memory base */
	ROM_LOAD("cpc6128s.rom", 0x10000, 0x8000, CRC(588b5540) SHA1(6765a91a42fed68a807325bf62a728e5ac5d622f))
	ROM_LOAD("cpcados.rom",  0x18000, 0x4000, CRC(1fe22ecd) SHA1(39102c8e9cb55fcc0b9b62098780ed4a3cb6a4bb))
ROM_END

ROM_START( cpc6128sp )
	// this defines the total memory size (128kb))- 64k ram, 16k OS, 16k BASIC, 16k DOS +16k
	ROM_REGION(0x020000, "maincpu", 0)

	/* load the os to offset 0x01000 from memory base */
	ROM_LOAD("amstrad_40038.ic103", 0x10000, 0x8000, CRC(2fa2e7d6) SHA1(1acd01c2c03424a78b32c9440f4d890fb1104815))
	ROM_LOAD("amstrad_40015.ic204",  0x18000, 0x4000, CRC(1fe22ecd) SHA1(39102c8e9cb55fcc0b9b62098780ed4a3cb6a4bb))

	ROM_REGION(0x200, "pals", 0)
	ROM_LOAD("mmi_hal16l8.ic118", 0x000, 0x104, NO_DUMP)
ROM_END

ROM_START( cpc464 )
	/* this defines the total memory size - 64k ram, 16k OS, 16k BASIC, 16k DOS */
	ROM_REGION(0x01c000, "maincpu", 0)
	/* load the os to offset 0x01000 from memory base */
	ROM_LOAD("cpc464.rom",  0x10000, 0x8000, CRC(40852f25) SHA1(56d39c463da60968d93e58b4ba0e675829412a20))
ROM_END


ROM_START( cpc664 )
	/* this defines the total memory size - 64k ram, 16k OS, 16k BASIC, 16k DOS */
	ROM_REGION(0x01c000, "maincpu", 0)
	/* load the os to offset 0x01000 from memory base */
	ROM_LOAD("cpc664.rom",  0x10000, 0x8000, CRC(9ab5a036) SHA1(073a7665527b5bd8a148747a3947dbd3328682c8))
	ROM_LOAD("cpcados.rom", 0x18000, 0x4000, CRC(1fe22ecd) SHA1(39102c8e9cb55fcc0b9b62098780ed4a3cb6a4bb))
ROM_END


ROM_START( kccomp )
	ROM_REGION(0x01c000, "maincpu", 0)
	ROM_LOAD("kccos.rom",  0x10000, 0x4000, CRC(7f9ab3f7) SHA1(f828045e98e767f737fd93df0af03917f936ad08))
	ROM_LOAD("kccbas.rom", 0x14000, 0x4000, CRC(ca6af63d) SHA1(d7d03755099d0aff501fa5fffc9c0b14c0825448))
	ROM_RELOAD(0x18000, 0x4000)  // has no AMSDOS ROM, so just reload BASIC in it's place (BASIC appears in any unused ROM slot)

	ROM_REGION(0x018000+0x0800, "proms", 0)
	ROM_LOAD("farben.rom", 0x18000, 0x0800, CRC(a50fa3cf) SHA1(2f229ac9f62d56973139dad9992c208421bc0f51))

	/* fake region - required by graphics decode structure */
	/*ROM_REGION(0x0c00, "gfx1") */
ROM_END


/* this system must have a cartridge installed to run */
ROM_START(cpc6128p)
	ROM_REGION(0x80000, "maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x04000, "user1", ROMREGION_ERASEFF)
ROM_END

#define rom_cpc464p  rom_cpc6128p
#define rom_gx4000  rom_cpc6128p


ROM_START( al520ex )
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_LOAD("al512.bin", 0x10000, 0x10000, CRC(e8c2a9a1) SHA1(ad5827582cb19eaaae1b76e67df62d96da6ad96b))

	ROM_REGION(0x20, "user2", 0)
	ROM_LOAD("af.bin", 0x00, 0x20, CRC(c81fb524) SHA1(17738d0603915a67ec1fddc4cbf7d6b98cdeb8f6))

	ROM_REGION(0x100, "user3", 0)  // RAM bank mappings
	ROM_LOAD("mapper.bin", 0x00, 0x100, CRC(0daebd80) SHA1(8633073cba752c38c5dc912ff9f6a3c89357539b))

	ROM_REGION(0x800, "user4", 0)  // Colour data
	ROM_LOAD("rfcoldat.bin", 0x00, 0x800, CRC(c6ace0e6) SHA1(2f4c51fcfaacb8deed68f6ae9388b870bc962cef))

	ROM_REGION(0x800, "user5", 0)  // Keyboard / Video
	ROM_LOAD("rfvdkey.bin", 0x00, 0x800, CRC(cf2aa4b0) SHA1(20f37da3bc3c377b1c47ae4d9ab8d150faae19a0))

	ROM_REGION(0x100, "user6", 0)
	ROM_LOAD("romram.bin", 0x00, 0x100, CRC(b3ea95d7) SHA1(1252390737a7ead4ecec988c873181798fbc291b))
ROM_END

/*************************************
 *
 *  Driver definitions
 *
 *************************************/

/*    YEAR  NAME       PARENT  COMPAT MACHINE  INPUT      CLASS          INIT        COMPANY        FULLNAME                                     FLAGS */
COMP( 1984, cpc464,    0,      0,     cpc464,  cpc464,    amstrad_state, empty_init, "Amstrad plc", "Amstrad CPC464",                            0 )
COMP( 1985, cpc664,    cpc464, 0,     cpc664,  cpc664,    amstrad_state, empty_init, "Amstrad plc", "Amstrad CPC664",                            0 )
COMP( 1985, cpc6128,   cpc464, 0,     cpc6128, cpc6128,   amstrad_state, empty_init, "Amstrad plc", "Amstrad CPC6128",                           0 )
COMP( 1985, cpc6128f,  cpc464, 0,     cpc6128, cpc6128f,  amstrad_state, empty_init, "Amstrad plc", "Amstrad CPC6128 (France, AZERTY Keyboard)", 0 )
COMP( 1985, cpc6128s,  cpc464, 0,     cpc6128, cpc6128s,  amstrad_state, empty_init, "Amstrad plc", "Amstrad CPC6128 (Sweden/Finland)",          0 )
COMP( 1985, cpc6128sp, cpc464, 0,     cpc6128, cpc6128sp, amstrad_state, empty_init, "Amstrad plc", "Amstrad CPC6128 (Spain)",                   0 )
COMP( 1990, cpc464p,   0,      0,     cpcplus, plus,      amstrad_state, empty_init, "Amstrad plc", "Amstrad CPC464+",                           0 )
COMP( 1990, cpc6128p,  0,      0,     cpcplus, plus,      amstrad_state, empty_init, "Amstrad plc", "Amstrad CPC6128+",                          0 )
CONS( 1990, gx4000,    0,      0,     gx4000,  gx4000,    amstrad_state, empty_init, "Amstrad plc", "Amstrad GX4000",                            0 )
COMP( 1989, kccomp,    cpc464, 0,     kccomp,  kccomp,    amstrad_state, empty_init, u8"VEB Mikroelektronik \"Wilhelm Pieck\" Mühlhausen",
																									"KC Compact",                                0 )
COMP( 1993, al520ex,   cpc464, 0,     aleste,  aleste,    amstrad_state, empty_init, "Patisonic",   "Aleste 520EX",                              MACHINE_IMPERFECT_SOUND )



amusement_6502.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:

/*
unknown vending machine (?) by Amusement Inc.

Main (?) PCB
on front: AMUSEMENT INC. LOGIC PCB ASSY 58-0003-002 ORG-11 MADE IN TAIWAN R.O.C.
on back: AMUSEMENT INC ORG-11 LOGIC PCB P/N57-0003-002

Main components:
UM6502
UM6116-2
AY-3-8910A
VP1000 (UM5100 compatible)
bank of 4 switches
no XTAL spotted

Plug in PCB
on front: AMUSEMENT INC. ORG-11 PROM-ASSY P/N58-0004-001 MADE IN TAIWAN R.O.C.
on back: AMUSEMENT INC ORG-11 PROM PCB P/N57-0004-001
just 2 ROMs (one for CPU/AY and one for VP1000)

A third PCB came together with the other two, but it isn't clear if they are intended
to work together or if is it for something different

Main components:
TMS5220CNL and its ROM
555

TODO:
- ROM contains a very small program, is this really a complete set or is it missing another PCB?
*/


#include "emu.h"

#include "cpu/m6502/m6502.h"
#include "sound/ay8910.h"
#include "sound/hc55516.h"
#include "sound/tms5220.h"

#include "speaker.h"


namespace {

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

	void amusement(machine_config &config) ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;

	void program_map(address_map &map) ATTR_COLD;
};

void amusement_6502_state::program_map(address_map &map)
{
	map(0x0000, 0x01ff).ram();
	//map(0x2000, 0x2000).rw("ay", FUNC(ay8910_device::data_r), FUNC(ay8910_device::data_w));
	//map(0x4000, 0x4000).w("ay", FUNC(ay8910_device::address_w));
	map(0xe000, 0xffff).rom().region("maincpu", 0);
}


static INPUT_PORTS_START( amusement )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN )
	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("DSW1")
	PORT_DIPUNKNOWN_DIPLOC(0x01, 0x01, "SW1:1")
	PORT_DIPUNKNOWN_DIPLOC(0x02, 0x02, "SW1:2")
	PORT_DIPUNKNOWN_DIPLOC(0x04, 0x04, "SW1:3")
	PORT_DIPUNKNOWN_DIPLOC(0x08, 0x08, "SW1:4")
	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 )
INPUT_PORTS_END


void amusement_6502_state::amusement(machine_config &config)
{
	M6502(config, m_maincpu, 2'000'000); // TODO: verify clock
	m_maincpu->set_addrmap(AS_PROGRAM, &amusement_6502_state::program_map);
	//m_maincpu->set_periodic_int(FUNC(amusement_6502_state::irq0_line_hold), attotime::from_hz(60));

	// sound hardware
	SPEAKER(config, "mono").front_center();

	ay8910_device &ay(AY8910(config, "ay", 1'000'000)); // TODO: verify clock
	ay.port_a_read_callback().set([this] () { logerror("%s AY port A read\n", machine().describe_context()); return 0; });
	ay.port_b_read_callback().set([this] () { logerror("%s AY port B read\n", machine().describe_context()); return 0; });
	ay.port_a_write_callback().set([this] (uint8_t data) { logerror("%s AY port A write: %02x\n", machine().describe_context(), data); });
	ay.port_b_write_callback().set([this] (uint8_t data) { logerror("%s AY port B write: %02x\n", machine().describe_context(), data); });
	ay.add_route(ALL_OUTPUTS, "mono", 0.60);

	HC55516(config, "cvsd", 0).add_route(ALL_OUTPUTS, "mono", 0.60); // TODO: actually VP1000

	TMS5220C(config, "tms", 640'000).add_route(ALL_OUTPUTS, "mono", 0.60); // TODO: verify clock
}


ROM_START( unkamus )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "music.u16", 0x0000, 0x2000, CRC(4b5274bc) SHA1(5d78ecef66c1cfab2498ec1d8da9b8a1c0ba0042) )

	ROM_REGION( 0x8000, "cvsd", 0 )
	ROM_LOAD( "generic.u15", 0x0000, 0x8000, CRC(dfc81bc3) SHA1(9e072475d5a1f1d97560e9ce14f206af844fcb2e) )

	ROM_REGION( 0x1000, "tms", 0 )
	ROM_LOAD( "u14", 0x0000, 0x1000, CRC(ce38d80f) SHA1(9646e2d964ee4e81a1184795eedeae4bfe40c99d) ) // label unreadable
ROM_END

} // anonymous namespace


SYST( 199?, unkamus, 0, 0, amusement, amusement, amusement_6502_state, empty_init, "Amusement Inc.", "unknown Amusement Inc. vending machine", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



amust.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/*****************************************************************************************

Amust Compak - also known as Amust Executive 816.

2014-03-21 Skeleton driver. [Robbbert]

An unusual-looking CP/M computer. The screen is a tiny CRT not much bigger
than a modern smartphone.

Z-80A @ 4MHz; 64KB dynamic RAM (8x 4164); 2KB video ram (6116); 2x 13cm drives;
80 track DD with data capacity of 790KB; in a lockable Samsonite briefcase.

There are no manuals or schematics known to exist.
The entire driver is guesswork.
The board has LH0080 (Z80A), 2x 8251, 2x 8255, 8253, uPD765A and a HD46505SP-2.
There is a piezo beeper. There are 3 crystals, X1 = 4.9152 (serial chips),
X2 = 16 (CPU), X3 = 14.31818 MHz (Video).
There are numerous jumpers, all of which perform unknown functions.

The keyboard is a plug-in unit, same idea as Kaypro and Zorba. It has these
chips: INS8035N-6, F74145, 74LS373N, SN75451BP, 2716 rom with label KBD-3.
Crystal: 3.579545 MHz

The main rom is identical between the 2 halves, except that the initial
crtc parameters are slightly different. I've chosen to ignore the first
half. (perhaps 50/60 Hz selectable by jumper?)

Preliminary I/O ports
---------------------
00-01 uart 1
02-03 uart 2
04-07 ppi 1
08-0b ppi 2
0d-0f crtc
10-11 fdc
14-17 pit

PIT.
Having the PIT on ports 14-17 seems to make sense. It sets counters 1 and 2
to mode 3, binary, initial count = 0x80. Counter 0 not used?


Floppy Parameters:
------------------
Double Density
Two Side
80 track
1024 byte sectors
5 sectors/track
800k capacity
128 directory entries
2k block size
Skew 1,3,5,2,4


Monitor Commands:
-----------------
B = Boot from floppy
(YES! Most useless monitor ever)


ToDo:
- Everything
- Floppy issues:
  - The loop to read a sector has no escape. The interrupt handler (which can't be found)
    needs to take another path when the sector is complete.
  - The loop uses "ini" to read a byte, but this doesn't clear DRQ, so memory rapidly fills
    up with garbage, mostly FF.
- Keyboard controller needs to be emulated.
- If booting straight to CP/M, the load message should be in the middle of the screen.
- Looks like port 5 has a row of function keys or similar. Need to be added.


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

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/pit8253.h"
#include "machine/timer.h"
#include "machine/upd765.h"
#include "sound/beep.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class amust_state : public driver_device
{
public:
	amust_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_palette(*this, "palette")
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_bank1(*this, "bank1")
		, m_p_chargen(*this, "chargen")
		, m_beep(*this, "beeper")
		, m_fdc (*this, "fdc")
		, m_floppy0(*this, "fdc:0")
		, m_floppy1(*this, "fdc:1")
		, m_beep_timer(*this, "beep_timer")
	{ }

	void amust(machine_config &config);

private:
	u8 port04_r();
	void port04_w(u8 data);
	u8 port05_r();
	u8 port06_r();
	void port06_w(u8 data);
	u8 port08_r();
	void port08_w(u8 data);
	u8 port09_r();
	u8 port0a_r();
	void port0a_w(u8 data);
	void port0d_w(u8 data);
	void hsync_w(int state);
	void vsync_w(int state);
	void drq_w(int state);
	void intrq_w(int state);
	void kbd_put(u8 data);
	MC6845_UPDATE_ROW(crtc_update_row);
	TIMER_DEVICE_CALLBACK_MEMBER(beep_timer);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	void machine_reset() override ATTR_COLD;
	void machine_start() override ATTR_COLD;
	void do_int();

	u8 m_port04 = 0U;
	u8 m_port06 = 0U;
	u8 m_port08 = 0U;
	u8 m_port09 = 0U;
	u8 m_port0a = 0U;
	u8 m_term_data = 0U;
	bool m_drq = 0;
	//bool m_intrq = 0;
	bool m_hsync = 0;
	bool m_vsync = 0;
	std::unique_ptr<u8[]> m_vram;
	memory_passthrough_handler m_rom_shadow_tap;
	required_device<palette_device> m_palette;
	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_memory_bank    m_bank1;
	required_region_ptr<u8> m_p_chargen;
	required_device<beep_device> m_beep;
	required_device<upd765a_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	required_device<floppy_connector> m_floppy1;
	required_device<timer_device> m_beep_timer;
};

TIMER_DEVICE_CALLBACK_MEMBER(amust_state::beep_timer)
{
	m_beep->set_state(0);
}

//void amust_state::port00_w(u8 data)
//{
//  membank("bankr0")->set_entry(BIT(data, 6));
//  m_fdc->dden_w(BIT(data, 5));
//  floppy_image_device *floppy = nullptr;
//  if (BIT(data, 0)) floppy = m_floppy0->get_device();
//  m_fdc->set_floppy(floppy);
//  if (floppy)
//      floppy->ss_w(BIT(data, 4));
//}

void amust_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).ram().share("mainram");
	map(0xf800, 0xffff).bankr("bank1");
}

void amust_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x01).rw("uart1", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x02, 0x03).rw("uart2", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x04, 0x07).rw("ppi1", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x08, 0x0b).rw("ppi2", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x0d, 0x0d).nopr().w(FUNC(amust_state::port0d_w));
	map(0x0e, 0x0e).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x0f, 0x0f).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x10, 0x11).m(m_fdc, FUNC(upd765a_device::map));
	map(0x14, 0x17).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
}

static void amust_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

/* Input ports */
static INPUT_PORTS_START( amust )
	PORT_START("P9")
	// bits 6,7 not used?
	// bit 5 - fdc intrq
	PORT_DIPNAME( 0x01, 0x01, "Bit0" ) // code @ FC99
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x02, 0x02, "Bit1" )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x04, 0x04, "Bit2" )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x08, 0x08, "Bit3" )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x10, 0x10, "Boot to Monitor" ) // code @ F895
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
INPUT_PORTS_END

void amust_state::do_int()
{
	bool sync = m_hsync | m_vsync;

	if ((BIT(m_port0a, 3) && sync)             // when writing to the screen, only do it during blanking
		|| (BIT(m_port0a, 5) && m_drq))        // when reading from floppy, only do it when DRQ is high.
	{
		//printf("%X,%X,%X ",m_port0a,sync,m_drq);
		m_maincpu->set_input_line_and_vector(INPUT_LINE_IRQ0, ASSERT_LINE, 0x00); // Z80
	}
	else
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
}

void amust_state::drq_w(int state)
{
	m_drq = state;
	do_int();
	m_fdc->tc_w(1);
}

void amust_state::intrq_w(int state)
{
	m_port09 = (m_port09 & 0xdf) | (state ? 0x20 : 0);
}

void amust_state::hsync_w(int state)
{
	m_hsync = state;
	do_int();
}

void amust_state::vsync_w(int state)
{
	m_vsync = state;
	do_int();
}

u8 amust_state::port04_r()
{
	return m_port04;
}

void amust_state::port04_w(u8 data)
{
	m_port04 = data;
}

u8 amust_state::port05_r()
{
	return 0;
}

u8 amust_state::port06_r()
{
	return m_port06;
}

// BIT 5 low while writing to screen
void amust_state::port06_w(u8 data)
{
	m_port06 = data;
}

u8 amust_state::port08_r()
{
	return m_port08;
}

// lower 8 bits of video address
void amust_state::port08_w(u8 data)
{
	m_port08 = data;
}

/*
d0 - something to do with type of disk
d1 -
d2 -
d3 -
d4 - H = go to monitor; L = boot from disk
d5 - status of fdc intrq; loops till NZ
d6 -
d7 -
*/
u8 amust_state::port09_r()
{
	logerror("%s\n",machine().describe_context());
	return (ioport("P9")->read() & 0x1f) | m_port09;
}

u8 amust_state::port0a_r()
{
	return m_port0a;
}

/* Bits 7,6,5,3 something to do
with selecting which device causes interrupt?
50, 58 = video sync
70 disk
D0 ?
Bit 4 low = beeper.
Lower 3 bits = upper part of video address */
void amust_state::port0a_w(u8 data)
{
	m_port0a = data;

	if (!BIT(data, 4))
	{
		m_beep->set_state(1);
		m_beep_timer->adjust(attotime::from_msec(150));
	}
	floppy_image_device *floppy = m_floppy0->get_device();
	m_fdc->set_floppy(floppy);
	if (floppy)
	{
		floppy->mon_w(0);

		//floppy->ss_w(0);   // side 0? hopefully fdc does this
	}
}

void amust_state::port0d_w(u8 data)
{
	uint16_t video_address = m_port08 | ((m_port0a & 7) << 8);
	m_vram[video_address] = data;
}

/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 8,                  /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                    /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_amust )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END

MC6845_UPDATE_ROW( amust_state::crtc_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u32 *p = &bitmap.pix(y);

	for (u16 x = 0; x < x_count; x++)
	{
		u8 inv = (x == cursor_x) ? 0xff : 0;
		u16 mem = (ma + x) & 0x7ff;
		u8 chr = m_vram[mem];
		u8 gfx;
		if (ra < 8)
			gfx = m_p_chargen[(chr<<3) | ra] ^ inv;
		else
			gfx = inv;

		/* Display a scanline of a character (8 pixels) */
		*p++ = palette[BIT(gfx, 7)];
		*p++ = palette[BIT(gfx, 6)];
		*p++ = palette[BIT(gfx, 5)];
		*p++ = palette[BIT(gfx, 4)];
		*p++ = palette[BIT(gfx, 3)];
		*p++ = palette[BIT(gfx, 2)];
		*p++ = palette[BIT(gfx, 1)];
		*p++ = palette[BIT(gfx, 0)];
	}
}

void amust_state::machine_reset()
{
	m_bank1->set_entry(1);
	m_port04 = 0;
	m_port06 = 0;
	m_port08 = 0;
	m_port09 = 0;
	m_port0a = 0;
	m_hsync = false;
	m_vsync = false;
	m_drq = false;
	m_fdc->set_ready_line_connected(1); // always ready for minifloppy; controlled by fdc for 20cm
	m_fdc->set_unscaled_clock(4000000); // 4MHz for minifloppy; 8MHz for 20cm

	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x0000, 0x07ff, m_rom);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0xf800, 0xffff,
			"rom_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// reinstall RAM over the ROM shadow
					m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x07ff, m_ram);
				}
			},
			&m_rom_shadow_tap);
}

void amust_state::machine_start()
{
	m_vram = make_unique_clear<u8[]>(0x800);
	save_pointer(NAME(m_vram), 0x800);
	save_item(NAME(m_port04));
	save_item(NAME(m_port06));
	save_item(NAME(m_port08));
	save_item(NAME(m_port09));
	save_item(NAME(m_port0a));
	save_item(NAME(m_term_data));
	save_item(NAME(m_drq));
	//save_item(NAME(m_intrq));
	save_item(NAME(m_hsync));
	save_item(NAME(m_vsync));
	m_bank1->configure_entry(0, m_ram+0xf800);
	m_bank1->configure_entry(1, m_rom);
}

void amust_state::amust(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(16'000'000) / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &amust_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &amust_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	PALETTE(config, m_palette, palette_device::MONOCHROME);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_amust);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 800).add_route(ALL_OUTPUTS, "mono", 0.50);
	TIMER(config, m_beep_timer).configure_generic(FUNC(amust_state::beep_timer));

	/* Devices */
	hd6845s_device &crtc(HD6845S(config, "crtc", XTAL(14'318'181) / 8));
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(8);
	crtc.set_update_row_callback(FUNC(amust_state::crtc_update_row));
	crtc.out_hsync_callback().set(FUNC(amust_state::hsync_w));
	crtc.out_vsync_callback().set(FUNC(amust_state::vsync_w));

	UPD765A(config, m_fdc, 8'000'000, true, true);
	m_fdc->drq_wr_callback().set(FUNC(amust_state::drq_w));
	m_fdc->intrq_wr_callback().set(FUNC(amust_state::intrq_w));
	FLOPPY_CONNECTOR(config, "fdc:0", amust_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", amust_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	clock_device &uart_clock(CLOCK(config, "uart_clock", 153600));
	uart_clock.signal_handler().set("uart1", FUNC(i8251_device::write_txc));
	uart_clock.signal_handler().append("uart1", FUNC(i8251_device::write_rxc));

	i8251_device &uart1(I8251(config, "uart1", 0));
	uart1.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	uart1.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	uart1.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "keyboard"));
	rs232.rxd_handler().set("uart1", FUNC(i8251_device::write_rxd));
	rs232.cts_handler().set("uart1", FUNC(i8251_device::write_cts));
	rs232.dsr_handler().set("uart1", FUNC(i8251_device::write_dsr));

	I8251(config, "uart2", 0);
	//uart2.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	//uart2.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	//uart2.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	PIT8253(config, "pit", 0);

	i8255_device &ppi1(I8255A(config, "ppi1"));
	ppi1.in_pa_callback().set(FUNC(amust_state::port04_r));
	ppi1.out_pa_callback().set(FUNC(amust_state::port04_w));
	ppi1.in_pb_callback().set(FUNC(amust_state::port05_r));
	ppi1.in_pc_callback().set(FUNC(amust_state::port06_r));
	ppi1.out_pc_callback().set(FUNC(amust_state::port06_w));

	i8255_device &ppi2(I8255A(config, "ppi2"));
	ppi2.in_pa_callback().set(FUNC(amust_state::port08_r));
	ppi2.out_pa_callback().set(FUNC(amust_state::port08_w));
	ppi2.in_pb_callback().set(FUNC(amust_state::port09_r));
	ppi2.in_pc_callback().set(FUNC(amust_state::port0a_r));
	ppi2.out_pc_callback().set(FUNC(amust_state::port0a_w));
}

/* ROM definition */
ROM_START( amust )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mon_h.ic25", 0x0000, 0x1000, CRC(10dceac6) SHA1(1ef80039063f7a6455563d59f1bcc23e09eca369) )

	ROM_REGION( 0x800, "chargen", 0 )
	ROM_LOAD( "cg4.ic74",   0x000, 0x800, CRC(52e7b9d8) SHA1(cc6d457634eb688ccef471f72bddf0424e64b045) )

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD( "kbd_3.rom",  0x000, 0x800, CRC(d9441b35) SHA1(ce250ab1e892a13fd75182703f259855388c6bf4) )
ROM_END

} // anonymous namespace

/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME               FLAGS
COMP( 1983, amust, 0,      0,      amust,   amust, amust_state, empty_init, "Amust", "Amust Executive 816", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



antonelli_hd6305.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for HD6305-based keyboards by Antonelli.

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

#include "emu.h"
#include "cpu/m6805/hd6305.h"
#include "machine/6850acia.h"

namespace {

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

	void antonelli(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};

void antonelli_hd6305_state::mem_map(address_map &map)
{
	map(0x0200, 0x0201).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0x1000, 0x1fff).rom().region("eprom", 0x1000);
	map(0x2000, 0x3fff).rom().region("eprom", 0);
}

static INPUT_PORTS_START(antonelli)
INPUT_PORTS_END

void antonelli_hd6305_state::antonelli(machine_config &config)
{
	HD6305Y2(config, m_maincpu, 8'000'000); // type guessed; clock unknown
	m_maincpu->set_addrmap(AS_PROGRAM, &antonelli_hd6305_state::mem_map);

	ACIA6850(config, "acia");
}

ROM_START(anto2495)
	ROM_REGION(0x2000, "eprom", 0)
	ROM_LOAD("49.bin", 0x0000, 0x2000, CRC(ee474092) SHA1(4f60b174048e29fc299ab28001203b734e3c6592)) // TI TMS 2764JL-25
ROM_END

ROM_START(anto2614)
	ROM_REGION(0x2000, "eprom", 0)
	ROM_LOAD("61.bin", 0x0000, 0x2000, CRC(9357a29b) SHA1(7f580778f1e854e71b51100cc6cd2c50de1a38b3)) // SGS M2764-4F1
ROM_END

} // anonymous namespace

SYST(198?, anto2495, 0, 0, antonelli, antonelli, antonelli_hd6305_state, empty_init, "Antonelli", "Antonelli 2495", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(198?, anto2614, 0, 0, antonelli, antonelli, antonelli_hd6305_state, empty_init, "Antonelli", "Antonelli 2614", MACHINE_NO_SOUND | MACHINE_NOT_WORKING) // stereo version



anzterm.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Vas Crabb
/**************************************************************************

    Burroughs EF315-I220 bank teller terminal

    These 8-bit terminals were used by banks before they were displaced
    by cheap PCs.  There's an external customer PIN pad with its own
    ROMs, and a keyboard for the teller.  Unfortunately the teller
    keyboard was missing from the unit that was dumped.  These terminals
    contain a hardware DES chip which was a controlled item due to trade
    restrictions on encryption technology.

    The main system consists of three large boards in a stack: D8302
    (I/O board), A8301 (memory board), and C8209 (CPU board).

    The MICR/OCR unit appears to be an off-the-shelf unit built by
    Recognition Equipment and has a single board mounted in the top
    case.  Its main CPU is a MOSTEK MK3870/42 which has an undumped
    4032-byte internal mask ROM.

    Onboard peripherals:
    - Small "green-screen" CRT display mounted on an adjustable "stalk"
    - Thermal printer
    - Card swipe
    - MICR/OCR for reading cheque numbers (swiped the same way as cards)

    Rear panel connectors:
    - J1 (25-pin D-sub male)
    - J2 (25-pin D-sub male)
    - J3 (7-pin MIL-C-5015 connector)
    - J4 (5-pin 180-degree DIN female)
    - J5 (25-pin D-sub male)
    - J6 (15-pin D-sub female)

    TODO:
    - Check designation of MICR board.
    - Does U2 on the MICR board exist?  I can't find it on the overlay.
    - Finish parts list and ASCII art for CPU board.
    - Do IC50 and IC56 on the I/O board exist?  I can't find them on the overlay.
    - Work out how clocks are derived.
    - Hook everything up.
    - Emulate DES engine.
    - Photograph the guts of the PIN pad and check ROM labels.
    - Find way to dump MK3870/42 in MICR reader.

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

/*

(Despite my ASCII art, the main boards are actually all the same size.)

C8209 CPU Board
==================

Components:
  IC1                 - SN75189N (quad EIA-232-F line receiver)
  IC3                 - unpopulated (16-pin DIP)
  IC7                 - SN75174NG (quad EIA-422-B differential line driver)
  IC8                 - SN75175N (quad EIA-422-B differential line receiver)
  IC9                 - MC6850P (Motorola ACIA)
  IC12                - SN75189N (quad EIA-232-F line receiver)
  IC13                - SN75188N (quad EIA-232-E line driver)
  IC14, IC15          - unpopulated (16-pin DIP)
  IC16                - MC6854P (Motorola ADLC)
  IC17                - MC6850P (Motorola ACIA)
  IC18                - HD46850P (Hitachi ACIA clone)
  IC19                - MC6854P (Motorola ADLC)
  IC20                - unpopulated (24-pin DIP)
  IC21                - SN74LS74AN
  IC22                - SN74LS51N
  IC23                - SN75189N (quad EIA-232-F line receiver)
  IC24                - unpopulated (16-pin DIP)
  IC25                - SN74LS08N
  IC26                - SN74LS00N
  IC27                - DM74LS04N
  IC28                - SN74LS367AN
  IC29                - SN74LS161AN
  IC30, IC31, IC32    - SN74LS367AN
  IC33                - SN74LS74AN
  IC34                - SN74LS393N
  IC36                - SN74LS367AN
  IC37                - SN74LS20N
  IC38                - SN74LS74AN
  IC39                - P8214 (Intel Priority Interrupt Control Unit)
  IC42                - SN74LS245N
  IC45                - SN74LS10N
  IC46                - SN74LS86N
  IC48                - 74LS05N
  IC50                - WD200IAH-20 (DES encryption engine)
  IC51                - DM74LS04N
  IC52                - CD4093BE
  IC53                - SN74LS393N
  IC54                - SN74LS74AN
  IC55                - SN74LS00AN
  IC56                - SN74LS367AN
  IC57                - SN74LS132AN
  IC58                - SN74LS32N
  IC60                - unpopulated (16-pin DIP)
  J7                  - System bus (50-pin DIL IDC)
  J8                  - Power (24-pin SIL)


A8301 Memory Board
==================

+------------------------------+------------J1------------+---+                    +---+
|                              +--------------------------+   +---------J2---------+   |
|         [=IC9==]  [=IC19=]                                  +--------------------+   |
|                                                                                      |
| +--J-IC8---+  +--A-IC18--+   [=IC25=] [=IC29=] [=IC34=] [=RP12=]                     |
| +----------+  +----------+                                        Q5 Q6       Q7     |
|                             +--IC23---+                                              |
| +--K-IC7---+  +--B-IC17--+  +---------+    [=IC28==]   [=IC42==]                     |
| +----------+  +----------+                                        [=IC55=] [=IC66=]  |
|                             [=IC22=]  [=RP10=] [=IC33=] [=RP11=]                     |
| +--L-IC6---+  +--C-IC16--+                                        [=IC54=] [=IC65=]  |
| +----------+  +----------+                                                           |
|                             [=HP3==]   [=IC27=]       [=IC41=]    [=IC53=] [=RP14=]  |
| +--M-IC5---+  +--D-IC15--+                                                           |
| +----------+  +----------+  [=HP2==]                             [=IC52=] [=IC64=]   |
|                                                       [=IC40=]                       |
| +--N-IC4--+ +--E-IC14-+  +---HP1---+   [=IC26=]                  [=IC51=] [=IC63=]   |
| +---------+ +---------+  +---------+                  [=IC39==]  [=IC50=] [=IC62=]   |
|                         +-----+                                  [=IC49=] [=IC61=]   |
| +--P-IC3--+ +--F-IC13-+ |     |   [=IC21=]   [=IC32=] [=IC38=]                       |
| +---------+ +---------+ | B1  |                                  [=IC48=] [=IC60=]   |
|                         | B2  |             [=IC31=]  [=IC37=]   [=IC47=] [=IC59=]   |
| +--Q-IC2--+ +--G-IC12-+ | B3  |       Q4                         [=IC46=] [=IC58=]   |
| +---------+ +---------+ | B4  |    Q2       [=IC30=]  [=IC36=]   [=IC45=] [=IC57=]   |
|                         | B5  |       Q3                                             |
| +--R-IC1--+ +--H-IC11-+ |     |    Q1                 [=IC35=]   [=IC44=] [=IC56=]   |
| +---------+ +---------+ +-----+                                                      |
|         [=RP2=]  [=IC10=]                                        [=IC43=]            |
+--------------------------------------------------------------------------------------+

Components:
  B1, B2, B3, B4, B5  - space provided for various NiCd batteries - B4 is populated (3.6V 100mAh)
  HP1                 - 24-pin DIP wired links - 1/2/16/22/24 3/4/5 6 7/8/9/10 11/12 13/14 15/17/18 19/20 21 23
  HP2                 - 16-pin DIP wired links - 1/2/3/16 4/6/7/8/14 5/9/10/12 11/13/15
  HP3                 - 16-pin DIP wired links - 1/4 2 3 5/12 6/11 7/10 8/9 13 14 15 16
  IC1, IC2            - HM6116LP-3 (2048*8 CMOS SRAM)
  IC3, IC4            - unpopulated (24-pin DIP socket)
  IC5, IC6, IC7, IC8  - HN482764G (8192*8 UV EPROM)
  IC9                 - HM6147LP-3 (4096*1 CMOS SRAM; 18-pin DIP in 20-pin socket; overlay says "CMOS PARITY 1")
  IC10                - DM74LS156N
  IC11, IC12          - unpopulated (24-pin DIP socket)
  IC13, IC14          - TMS2516JL-45 (2048*8 UV EPROM)
  IC15                - unpopulated (28-pin DIP socket)
  IC16, IC17, IC18    - HN482764G (8192*8 UV EPROM)
  IC19                - unpopulated (20-pin DIP socket; overlay says "CMOS PARITY 2")
  IC20                - location for 14-pin DIP integrated transistor pack replacing Q1/Q2/Q3/Q4
  IC21                - SN74LS03N
  IC22                - DM74LS156N
  IC23                - TBP28S166N (2048*8 BPROM)
  IC24                - location for 16-pin DIP under IC23 (for smaller address decoding ROM/PLD?)
  IC25                - SN74LS367AN
  IC26                - DM74LS156N
  IC27                - SN74LS157N
  IC28                - SN74LS273N
  IC29                - SN74LS367AN
  IC30                - SN74LS139N
  IC31                - SN74LS32N
  IC32                - SN74LS20N
  IC33                - SN74LS280N
  IC34                - SN74LS367AN
  IC35                - SN74LS32N
  IC36                - SN74LS13N
  IC37                - SN74LS163AN
  IC38                - SN74S74N
  IC39                - BELFUSE 8331 0447-0439-99 (DRAM timing device?)
  IC40                - SN74LS132N
  IC41                - SN74S02N
  IC42                - SN74LS245N
  IC43                - SN74S08N
  IC44 - IC52         - HM4864P-2 (65536*1 DRAM; DRAM BANK 1 BIT 0 - 8 and PARITY, respectively)
  IC53, IC54          - SN74LS158N
  IC55                - SN74LS393N
  IC56 - IC64         - HM4864P-2 (65536*1 DRAM; DRAM BANK 2 BIT 0 - 8 and PARITY, respectively)
  IC65, IC66          - SN74LS158N
  J1                  - System bus (50-pin DIL IDC)
  J2                  - Power (24-pin SIL)


D8302 I/O Board
==================

+----------------------------+--------------J5--------------+----+                        +----+
|                            +------------------------------+    +-----------J7-----------+    |
|      J4 |                                                      +------------------------+    |
|         |                                                                                    |
|  J1 |   |     [=IC18=]     [=RP2=]                                                           |
|     |                    [==IC26=]                                                           |
|      J3 |              +---IC25---+                                                          |
| [=IC7=] |    [=IC17=]  +----------+             [=IC45=]                                     |
|                                                 [=IC44=]                                     |
| +---IC6---+    +---IC16--+                                                                   |
| +---------+    +---------+            [==IC34=] [=IC43=]                                     |
|                                                                                              |
| [==IC5==]     +-------IC15-------+                                                          ++
|               +------------------+    [=IC33=] [=IC42=]  Q12  [=IC51=]  Q13                 ||
| [==IC4==]                                                                                   ||
|           [=IC11=]         [=IC24=]            [=IC41=]                                     ||
| [==IC3==]                            [==IC32=]                                              ||
|           [=IC10=] [=IC14=] [=IC23=]           [=IC40=]       [=IC62=]   [=IC61=]        J9 ||
| [==IC2==]                             [==IC31=]                                             ||
|                                                 +-------IC39-------+     [=IC55=]           ||
| [==IC1==]                    [=IC22=] [==IC30=] +------------------+                        ||
|          ++                            [=IC29=]  [==IC38=]    [=IC49=]                      ++
|          J2                                                              [=IC54=]  [=IC60=]  |
|          ||                   [=IC21=]     IC28 +---IC37--+                                  |
|          ++                                     +---------+   [=IC48=]   [=IC53=]  [=IC59=]  |
|              J6                        [=IC27=]   [=IC36=] [=IC63=] [=IC47=]       [=IC58=]  |
|     [=IC9==] |  [=IC13=]  [=IC20=]+-X1-+                                                     |
|     [=IC8==] |  [=IC12=]  [=IC19=]+----+ [J10]    [=IC35=] [=IC46=] [=IC52=] [=IC57=]        |
+----------------------------------------------------------------------------------------------+

Components:
  IC1, IC2            - SN74LS273N
  IC3, IC4            - SN74LS245N
  IC5                 - SN74LS273N
  IC6                 - D2716D (2048*8 UV EPROM)
  IC7                 - DM7404N
  IC8                 - SN74LS04N
  IC9                 - SN74LS74AN
  IC10                - SN74LS157N
  IC11                - SN74LS166AN
  IC12                - SN74LS21N
  IC13                - SN74LS93N
  IC14                - SN74LS157N
  IC15                - HD46505SP-1 HD68A45SP (Hitachi CRTC clone)
  IC16                - HD46850P HD6850P (Hitachi ACIA clone)
  IC17                - MC1488 75188N (quad EIA-232-E line driver)
  IC18                - SN74LS86N
  IC19                - SN74LS08N
  IC20                - SN74LS93N
  IC21                - SN74LS132N
  IC22                - SN74LS08N
  IC23                - MC1489A SN75189AN (quad EIA-232-F line receiver)
  IC24                - SN74LS157N
  IC25                - D8251AC (NEC Programmable Communications Interface)
  IC26                - SN74LS245N
  IC27                - SN7406N
  IC28                - NE555P
  IC29                - DM74LS00N
  IC30, IC31, IC32    - MCM2114P20 (1024*4 SRAM)
  IC33                - SN74LS157N
  IC34                - SN74LS273N
  IC35                - SN74LS27N
  IC36                - 74LS168
  IC37                - D2716D (2048*8 UV EPROM)
  IC38                - SN74LS273N
  IC39                - HD46821P HD6821P (Hitachi PIA clone)
  IC40                - SN74LS74AN
  IC41, IC42, IC43    - 74LS32
  IC44                - SN74LS04N
  IC45                - SN74LS367AN
  IC47                - SN74LS10N
  IC48                - SN74LS74AN
  IC49                - 74LS03
  IC51                - 74LS138N
  IC52                - 74LS02
  IC53                - 74LS00N
  IC54                - SN74121N
  IC55                - SN74LS09N
  IC57, IC58          - SN74LS74AN
  IC59                - SN74LS14N
  IC60                - 74LS132N
  IC61                - SN74LS09N
  IC62                - SN74LS08N
  IC63                - 74LS03
  J1                  - Card Swipe (6-pin SIL)
  J2                  - MICR Reader Board (10-pin DIL IDC)
  J3                  - not populated (6-pin SIL)
  J4                  - CRT (8-pin SIL)
  J5                  - System bus (50-pin DIL IDC)
  J6                  - External connector on left side of case (6-pin SIL)
  J7                  - Power (24-pin SIL)
  J9                  - Printer (30-pin double-sided female edge connector)
  J10                 - Possibly service mode - case contains a loose connector for shorting two pins (6-pin SIL)
  X1                  - CK1100AC 15974.40KHz (oscillator module)


MICR Board
==================

+-------------------------------------------------------------+
|                            +---K1---+                       |
|               +---U15----+ +--------+                       |
|  Q5   [=U23=] +----------+                       [=U1==]    |
|  Q6                                                         |
|               +---U16----+  Q8                              |
|  Q7   [=U24=] +----------+     Q10 Q3                       |
|                                                             |
|               +---U17----+ +---U11---+   +---U7----+        |
|       [=U25=] +----------+ +---------+   +---------+  U4    |
|                                                             |
|  ++   [=U26=] +---U18----+                                  |
|  ||           +----------+     [=U12=]   +-----U5------+    |
|  ||                           JP5        +-------------+ U3 |
|  ||   [=U27=] +---U19----+                            ++    |
|  ||           +----------+                            U6    |
|  ||                                      +---U8----+  ++    |
|  ++   [=U28=] +---U20----+               +---------+        |
|  Q9   [=JP==] +----------+                              ++  |
|                                                         ||  |
|       +---U29---+            +----+    Q2    Q1         ||  |
|       +---------+            +U13 |     U10             ||  |
|                       Q4     +----+                     ||  |
|        [=U31=]  [=U21=]                                 ||  |
|                         +-----U14-----+    [=U9==]      ++  |
|        [=U30=]  [=U22=] +-------------+                     |
+-------------------------------------------------------------+

Components:
  JP                  - Jumpers JP1, JP2, JP3, JP4
  K1                  - SPDT Relay
  U1                  - TL497ACN (switching voltage regulator)
  U3, U4              - DS0026CN (two-phase MOS clock driver)
  U5                  - CF11000A
  U6                  - CXO-043D 36.0000MHz 4E (oscillator module)
  U7                  - AMI 8336CK 5020056 CO4423
  U8                  - 5020884-002 853C
  U9                  - SN7414N
  U10                 - ICL8211CPA (programmable voltage detector)
  U11                 - ET2732Q-3 (4096*8 UV EPROM)
  U12                 - SN74LS00N
  U13                 - MB63303A (Fujitsu mask-programmed gate array)
  U14                 - MK3870/42 (Mostek integrated F8 - 4032 byte program mask ROM, 64 bytes program RAM, 64 byte scratchpad)
  U15                 - AM2716DC (2048*8 UV EPROM; 24-pin DIP in 28-pin socket)
  U16                 - ET2732Q-3 (4096*8 UV EPROM; 24-pin DIP in 28-pin socket)
  U17, U18, U19       - unpopulated (28-pin DIP socket)
  U20                 - ET2732Q-3 (4096*8 UV EPROM; 24-pin DIP in 28-pin socket)
  U21                 - SN74LS26N
  U22                 - SN74LS245N
  U23                 - SN7406N
  U24                 - unpopulated (14-pin DIP socket)
  U25                 - SN74LS08N
  U26, U27            - SN75189AN (quad EIA-232-F line receiver)
  U28                 - SN74LS367AN
  U29                 - TMS2732AJL-45 (4096*8 UV EPROM)
  U30                 - unpopulated (20-pin SDIP socket)
  U31                 - SN74LS374N

*/

#include "emu.h"

#include "cpu/m6809/m6809.h"
#include "machine/6850acia.h"
#include "machine/i8214.h"
#include "machine/mc6854.h"
#include "emupal.h"
#include "screen.h"


namespace {

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

	void anzterm(machine_config &config);

private:
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
	{
		return 0;
	}
	void anzterm_mem(address_map &map) ATTR_COLD;
};


gfx_layout const screenfont =
{
	8, 16,                                              // 8x16
	RGN_FRAC(1, 1),                                     // whole region
	1,                                                  // 1bpp
	{ 0 },                                              // bitplane offset
	{ 0*1,  1*1,  2*1,  3*1,  4*1,  5*1,  6*1,  7*1 },  // x offsets
	{ 0*8,  1*8,  2*8,  3*8,  4*8,  5*8,  6*8,  7*8,
	  8*8,  9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },  // y offsets
	128                                                 // stride
};

gfx_layout const printfont =
{
	8, 8,                                       // 7x8
	RGN_FRAC(1, 1),                             // whole region
	1,                                          // 1bpp
	{ 0 },                                      // bitplane offset
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 }, // x offsets
	{ 7*1, 6*1, 5*1, 4*1, 3*1, 2*1, 1*1, 0*1 }, // y offsets
	64                                          // stride
};

GFXDECODE_START( gfx_anzterm )
	GFXDECODE_ENTRY("crtc", 0x0000, screenfont, 0, 1)
	GFXDECODE_ENTRY("prnt", 0x0000, printfont,  0, 1)
GFXDECODE_END


void anzterm_state::anzterm_mem(address_map &map)
{
	// There are two battery-backed 2kB SRAM chips with a 4kb SRAM chip for parity
	// There are two 64kB DRAM banks (with parity)
	// There's also a whole lot of ROM
	map(0x0000, 0x3fff).ram();
	map(0xe000, 0xffff).rom();
}


void anzterm_state::anzterm(machine_config &config)
{
	m6809_device &maincpu(M6809(config, "maincpu", 15974400/4));
	maincpu.set_addrmap(AS_PROGRAM, &anzterm_state::anzterm_mem);

	I8214(config, "pic.ic39", 0);
	MC6854(config, "adlc.ic16", 0);
	MC6854(config, "adlc.1c19", 0);
	ACIA6850(config, "acia.ic17", 0);
	ACIA6850(config, "acia.ic18", 0);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_screen_update(FUNC(anzterm_state::screen_update));
	screen.set_palette("palette");
	screen.set_raw(15974400/4, 1024, 0, 104*8, 260, 0, 24*10); // this is totally wrong, it just stops a validation error

	PALETTE(config, "palette", palette_device::MONOCHROME);

	GFXDECODE(config, "gfxdecode", "palette", gfx_anzterm);
}


INPUT_PORTS_START( anzterm )
INPUT_PORTS_END

} // anonymous namespace


ROM_START( anzterm )
	// Main program on memory board - loading is definitely wrong
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "fj-a-25.ic18",                 0xe000, 0x2000, CRC(8c31a9dd) SHA1(864babf6c77813f17ce9082013098e8a677b0af6) )
	ROM_LOAD( "fj-b-25.ic17",                 0x2000, 0x2000, CRC(a893aeaf) SHA1(64e8a935fe37195533a0a19f00bdc4e6a2007728) )
	ROM_LOAD( "fj-c-25.ic16",                 0x4000, 0x2000, CRC(ae678bde) SHA1(b41da540b99a0c3ef9489d9cd25e5fa32e2d13f9) )
	ROM_LOAD( "fj-e-25.ic14",                 0x6000, 0x0800, CRC(786beceb) SHA1(d20b870528d12f8457e3c746b539fcfc3ded3b0b) )
	ROM_LOAD( "fj-f-25.ic13",                 0x6800, 0x0800, CRC(2890d808) SHA1(6871f4c5fd1bc7e2d1db2e663ffb342988de94b7) )
	ROM_LOAD( "fj-j-25.ic8",                  0x8000, 0x2000, CRC(23fa4b36) SHA1(b3676579b2ea4efb0bf867557b53a6ccba7cc60f) )
	ROM_LOAD( "fj-k-25.ic7",                  0xa000, 0x2000, CRC(cbd17462) SHA1(7e7460f99e7dd5c9ae113f69b67e2b6079a57c6d) )
	ROM_LOAD( "fj-l-25.ic6",                  0xc000, 0x2000, CRC(8989c2ed) SHA1(912d8152f8f67a964dcd360151d8c8438a652d58) )
	ROM_LOAD( "fj-m-25.ic5",                  0x0000, 0x2000, CRC(82762fee) SHA1(234a438abab91936e7073bd7cc62414dfae10373) )

	// BPROM on memory board - address decoding?
	ROM_REGION( 0x0800, "prom", 0 )
	ROM_LOAD( "08177-80002.ic23",             0x0000, 0x0800, CRC(3cec2386) SHA1(a1ae5e07756eac5abbb3e178e12b213770432b5f) )

	// CRTC character ROM on I/O board
	ROM_REGION( 0x0800, "crtc", 0 )
	ROM_LOAD( "crt-5080-2.ic6",               0x0000, 0x0800, CRC(cdea8532) SHA1(683743e477518695c2a1d9510bee25b7ef3f909b) )

	// Printer font ROM on I/O board
	ROM_REGION( 0x0800, "prnt", 0 )
	ROM_LOAD( "prt-04.ic37",                  0x0000, 0x0800, CRC(68870564) SHA1(06819a633dc545f103e8b843a2f553ac46a16a05) )

	// ROMs in PIN pad
	ROM_REGION( 0x1000, "pinpad", 0 )
	ROM_LOAD( "ck-a-pr01.ic4",                0x0000, 0x0800, CRC(d0981882) SHA1(b55fd313c9b3e00039501a53a53c820d98f2258a) )
	ROM_LOAD( "ck-b-pr01.ic3",                0x0000, 0x0800, CRC(96c9d90d) SHA1(400980c7a2c5306be28b74284c626ef2ed24c1a5) )

	// Undumped microcontroller ROM in MICR reader
	ROM_REGION( 0x0fc0, "micrmcu", 0 )
	ROM_LOAD( "mk3870.u14",                   0x0000, 0x0fc0, NO_DUMP )

	// MICR reader data table ROMS, no idea how this stuff is used but dumps should be preserved
	ROM_REGION( 0x5000, "micrdata", 0 )
	ROM_LOAD( "cdn1-ebb.u20",                 0x0000, 0x1000, CRC(0f9a9db3) SHA1(aedfe3ba7afb1d0a827fec5418369fca9348940f) )
	ROM_LOAD( "cdn2-ebb.u16",                 0x1000, 0x1000, CRC(648fff69) SHA1(59653d34067d9a3061857507868fd2147dadf537) )
	ROM_LOAD( "6047204005.u15",               0x2000, 0x0800, CRC(70bfac37) SHA1(84081249ead5b957d98b3bd06665ef52d0a0243c) )
	ROM_LOAD( "6048225001.u29",               0x3000, 0x1000, CRC(59c73999) SHA1(7dd12b500e13b177d19a24d148310541f7e660b4) )
	ROM_LOAD( "ebb-fea-v96-9-23-83-f43a.u11", 0x4000, 0x1000, CRC(0e572470) SHA1(966e5eeb0114589a7cab3c29a1db48cdd8634be5) )
ROM_END

COMP( 1986?, anzterm, 0, 0, anzterm, anzterm, anzterm_state, empty_init, "Burroughs", "EF315-I220 Teller Terminal (ANZ)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING ) // year comes from sticker on bottom of case, it's more likely a 1983 revision



apc.cpp
<---------------------------------------------------------------------->
// license:LGPL-2.1+
// copyright-holders:Angelo Salese
/***************************************************************************

    Advanced Personal Computer (c) 1982 NEC

    preliminary driver by Angelo Salese

    TODO:
    - video emulation (bitmap part)
    - NMI seems valid, dumps a x86 stack to vram?
    - What are exactly APU and MPU devices? They sounds scary ...
    - DMA hook-ups
    - serial ports
    - parallel ports
    - Extract info regarding Hard Disk functionality
    - Various unknown ports
    - What kind of external ROM actually maps at 0xa****?
    - Jumper settings (comms settings and display select)

============================================================================
    front ^
          |
    card
    ----
    69PFCU 7220               PFCU1R 2764
    69PTS  7220
    -
    69PFB2 8086/8087   DFBU2J PFBU2L 2732
    69SNB RAM

----------------------------------------------------------------------------
    i/o memory map (preliminary):
    0x00 - 0x1f DMA
    0x20 - 0x23 i8259 master
    0x28 - 0x2f i8259 slave (even), pit8253 (odd)
    0x30 - 0x37 serial i8251, even #1 / odd #2
    0x38 - 0x3f DMA segments
    0x40 - 0x43 upd7220, even chr / odd bitmap
    0x48 - 0x4f keyboard
    0x50 - 0x53 upd765
    0x58        rtc
    0x5a - 0x5e APU
    0x60        MPU (melody)
    0x61 - 0x67 (Mirror of pit8253?)
    0x68 - 0x6f parallel port

----------------------------------------------------------------------------
0xfe3c2: checks if the floppy has a valid string for booting (either "CP/M-86"
         or "MS-DOS"), if not, branches with the successive jne.

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


#include "emu.h"
#include "cpu/i86/i86.h"
#include "cpu/upd177x/upd177x.h"
#include "imagedev/floppy.h"
#include "machine/am9517a.h"
#include "machine/nvram.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/upd1990a.h"
#include "machine/upd765.h"
#include "video/upd7220.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
//#include "sound/ay8910.h"


namespace {

#define MAIN_CLOCK XTAL(5'000'000)

class apc_state : public driver_device
{
public:
	apc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_hgdc1(*this, "upd7220_chr"),
		m_hgdc2(*this, "upd7220_btm"),
		m_rtc(*this, "upd1990a"),
		m_cmos(*this, "cmos"),
		m_i8259_m(*this, "pic8259_master"),
		m_i8259_s(*this, "pic8259_slave"),
		m_fdc(*this, "upd765"),
		m_fdc_connector(*this, "upd765:%u", 0U),
		m_dmac(*this, "i8237"),
		m_pit(*this, "pit8253"),
		m_aux_pcg(*this, "aux_pcg"),
		m_speaker(*this, "mono"),
		m_sound(*this, "upd1771c"),
		m_video_ram_1(*this, "video_ram_1"),
		m_video_ram_2(*this, "video_ram_2"),
		m_screen(*this, "screen"),
		m_gfxdecode(*this, "gfxdecode"),
		m_palette(*this, "palette")
	{ }

	void apc(machine_config &config);

	void init_apc();

	DECLARE_INPUT_CHANGED_MEMBER(key_stroke);

private:
	// devices
	required_device<cpu_device> m_maincpu;
	required_device<upd7220_device> m_hgdc1;
	required_device<upd7220_device> m_hgdc2;
	required_device<upd1990a_device> m_rtc;
	required_device<nvram_device> m_cmos;
	required_device<pic8259_device> m_i8259_m;
	required_device<pic8259_device> m_i8259_s;
	required_device<upd765a_device> m_fdc;
	required_device_array<floppy_connector, 2> m_fdc_connector;
	required_device<am9517a_device> m_dmac;
	required_device<pit8253_device> m_pit;
	required_shared_ptr<uint16_t> m_aux_pcg;
	uint8_t *m_char_rom = nullptr;

	required_device<speaker_device> m_speaker;
	required_device<upd1771c_cpu_device> m_sound;

	required_shared_ptr<uint16_t> m_video_ram_1;
	required_shared_ptr<uint16_t> m_video_ram_2;
	required_device<screen_device> m_screen;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<palette_device> m_palette;

	// screen updates
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);


	uint8_t apc_port_28_r(offs_t offset);
	void apc_port_28_w(offs_t offset, uint8_t data);
	uint8_t apc_gdc_r(offs_t offset);
	void apc_gdc_w(offs_t offset, uint8_t data);
	uint8_t apc_kbd_r(offs_t offset);
	void apc_kbd_w(offs_t offset, uint8_t data);
	void apc_dma_segments_w(offs_t offset, uint8_t data);
	uint8_t apc_dma_r(offs_t offset);
	void apc_dma_w(offs_t offset, uint8_t data);
	void apc_irq_ack_w(uint8_t data);
	uint8_t apc_rtc_r();
	void apc_rtc_w(uint8_t data);
//  uint8_t aux_pcg_r();
//  void aux_pcg_w(uint8_t data);

	struct {
		uint8_t status = 0; //status
		uint8_t data = 0; //key data
		uint8_t sig = 0; //switch signal port
		uint8_t sh = 0; //shift switches
	}m_keyb;

	uint8_t get_slave_ack(offs_t offset);
	void apc_dma_hrq_changed(int state);
	void apc_tc_w(int state);
	void apc_dack0_w(int state);
	void apc_dack1_w(int state);
	void apc_dack2_w(int state);
	void apc_dack3_w(int state);
	uint8_t apc_dma_read_byte(offs_t offset);
	void apc_dma_write_byte(offs_t offset, uint8_t data);

	int m_dack = 0;
	uint8_t m_dma_offset[4]{};

	UPD7220_DISPLAY_PIXELS_MEMBER( hgdc_display_pixels );
	UPD7220_DRAW_TEXT_LINE_MEMBER( hgdc_draw_text );

	void apc_io(address_map &map) ATTR_COLD;
	void apc_map(address_map &map) ATTR_COLD;
	void upd7220_1_map(address_map &map) ATTR_COLD;
	void upd7220_2_map(address_map &map) ATTR_COLD;

	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual void video_start() override ATTR_COLD;
	inline void set_dma_channel(int channel, int state);
};

void apc_state::video_start()
{
	m_char_rom = memregion("gfx")->base();
}

uint32_t apc_state::screen_update( screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect )
{
	bitmap.fill(m_palette->black_pen(), cliprect);

	/* graphics */
	m_hgdc2->screen_update(screen, bitmap, cliprect);
	m_hgdc1->screen_update(screen, bitmap, cliprect);

	return 0;
}


UPD7220_DISPLAY_PIXELS_MEMBER( apc_state::hgdc_display_pixels )
{
	// ...
}

UPD7220_DRAW_TEXT_LINE_MEMBER( apc_state::hgdc_draw_text )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();

//  uint8_t interlace_on = m_video_reg[2] == 0x10; /* TODO: correct? */
	uint8_t char_size = 19;

	for(int x=0;x<pitch;x++)
	{
//      uint32_t tile_addr = addr+(x*(m_video_ff[WIDTH40_REG]+1));
		uint32_t tile_addr = addr+(x*(1));

		uint8_t tile = (m_video_ram_1[((tile_addr*2) & 0x1fff) >> 1] >> 8) & 0x00ff;
		uint8_t tile_sel = m_video_ram_1[((tile_addr*2) & 0x1fff) >> 1] & 0x00ff;
		uint8_t attr = (m_video_ram_1[((tile_addr*2 & 0x1fff) | 0x2000) >> 1] & 0x00ff);

		uint8_t u_line = attr & 0x01;
		uint8_t o_line = attr & 0x02;
		uint8_t v_line = attr & 0x04;
		uint8_t blink  = attr & 0x08;
		uint8_t reverse = attr & 0x10;
//      secret= (attr & 1) ^ 1;
		uint8_t color = (attr & 0xe0) >> 5;

		for(int yi=0;yi<lr;yi++)
		{
			int yi_trans = (yi==0)?lr-1:yi-1;
			for(int xi=0;xi<8;xi++)
			{
				int res_x = (x*8+xi);
				int res_y = y+yi;

				if(!m_screen->visible_area().contains(res_x, res_y))
					continue;

				/*
				Addr bus:   C BA98 7654 3210
				            | |||| |\\\ \\\\- character number bits 0-6
				            | |||| \--------- y' bit 0
				            | |||\----------- y' bit 1
				            | ||\------------ y' bit 2
				            | |\------------- y' bit 3
				            | \-------------- character number bit 7
				            \---------------- y' bit 4

				y to y' (assumed; this needs hardware tests since there could be one more 'blank' line between all char rows):
				y  =  0 1 2 3 ... 16 17 18
				y' = 18 0 1 2 ... 15 16 17

				Data bus: 76543210 = pixels, in left->01234567->right order
				*/
				uint8_t tile_data;
				if(tile_sel == 0x89)// Aux character RAM select TODO: correct triggering?
				{
					if(yi & 0x10)
						tile_data = 0;
					else
						tile_data = m_aux_pcg[(tile & 0xff)*0x20+yi*2];
				}
				else
					tile_data = m_char_rom[(tile & 0x7f)+((tile & 0x80)<<4)+((yi_trans & 0xf)*0x80)+((yi_trans & 0x10)<<8)];

				if(reverse) { tile_data^=0xff; }
				if(u_line && yi == lr-1) { tile_data = 0xff; }
				if(o_line && yi == 0) { tile_data = 0xff; }
				if(v_line)  { tile_data|=1; }
				if(blink && m_screen->frame_number() & 0x20) { tile_data = 0; } // TODO: rate & correct behaviour

				if(cursor_on && cursor_addr == tile_addr && m_screen->frame_number() & 0x10)
					tile_data^=0xff;

				uint8_t pen;
				if(yi >= char_size)
					pen = 0;
				else
					pen = (tile_data >> (xi) & 1) ? color : 0;

				if(pen)
					bitmap.pix(res_y, res_x) = palette[pen];
			}
		}
	}
}

uint8_t apc_state::apc_port_28_r(offs_t offset)
{
	uint8_t res;

	if(offset & 1)
		res = m_pit->read((offset & 6) >> 1);
	else
	{
		if(offset & 4)
		{
			logerror("Read undefined port %02x\n",offset+0x28);
			res = 0xff;
		}
		else
			res = m_i8259_s->read((offset & 2) >> 1);
	}

	return res;
}

void apc_state::apc_port_28_w(offs_t offset, uint8_t data)
{
	if(offset & 1)
		m_pit->write((offset & 6) >> 1, data);
	else
	{
		if(offset & 4)
			logerror("Write undefined port %02x\n",offset+0x28);
		else
			m_i8259_s->write((offset & 2) >> 1, data);
	}
}


uint8_t apc_state::apc_gdc_r(offs_t offset)
{
	uint8_t res;

	if(offset & 1)
		res = m_hgdc2->read((offset & 2) >> 1); // upd7220 bitmap port
	else
		res = m_hgdc1->read((offset & 2) >> 1); // upd7220 character port

	return res;
}

void apc_state::apc_gdc_w(offs_t offset, uint8_t data)
{
	if(offset & 1)
		m_hgdc2->write((offset & 2) >> 1,data); // upd7220 bitmap port
	else
		m_hgdc1->write((offset & 2) >> 1,data); // upd7220 character port
}

uint8_t apc_state::apc_kbd_r(offs_t offset)
{
	uint8_t res = 0;

	switch(offset & 3)
	{
		case 0: res = m_keyb.data; m_i8259_m->ir4_w(0); break; // according to the source, reading there acks the irq
		case 1: res = m_keyb.status; break;
		case 2: res = m_keyb.sig; break; // bit 0: CTRL bit 1: function key (or reversed)
		case 3: res = ioport("KEY_MOD")->read() & 0xff; break; // sh
	}

	return res;
}

void apc_state::apc_kbd_w(offs_t offset, uint8_t data)
{
	logerror("KEYB %08x %02x\n",offset,data);
}

void apc_state::apc_dma_segments_w(offs_t offset, uint8_t data)
{
	m_dma_offset[offset & 3] = data & 0x0f;
}

/*
NEC APC i8237 hook-up looks pretty weird ...

                NEC APC (shift 1) IBM PC
CH0_ADR ==      0X01     0x00       0x00  ; CH-0 address (RW)
CH1_ADR ==      0X03     0x01       0x02  ; CH-1 address (RW)
CH2_ADR ==      0X05     0x02       0x04  ; CH-2 address (RW)
CH3_ADR ==      0X07     0x03       0x06  ; CH-3 address (RW)
DMA_ST  ==      0X09     0x04       0x08  ; status register (R)
DMA_CMD ==      0X09     0x04       0x08  ; command register (W)
DMA_WSM ==      0X0B     0x05       0x0a  ; write single mask (W)
DMA_CFF ==      0X0D     0x06       0x0c  ; clear flip flop (W)

CH0_TC  ==      0X11     0x08       0x01  ; CH-0 terminal count (RW)
CH1_TC  ==      0X13     0x09       0x03  ; CH-1 terminal count (RW)
CH2_TC  ==      0X15     0x0a       0x05  ; CH-2 terminal count (RW)
CH3_TC  ==      0X17     0x0b       0x07  ; CH-3 terminal count (RW)
DMA_WRR ==      0X19     0x0c       0x09  ; write request register (W)
DMA_MODE==      0X1B     0x0d       0x0b  ; write mode (W)
DMA_RTR ==      0X1D     0x0e       0x0d? ; read temp register (R)
DMA_MC  ==      0X1D     0x0e       0x0d  ; master clear (W)
DMA_WAM ==      0X1F     0x0f       0x0f? ; write all mask (W)
CH0_EXA ==      0X38                      ; CH-0 extended address (W)
CH1_EXA ==      0X3A                      ; CH-1 extended address (W)
CH2_EXA ==      0X3C                      ; CH-2 extended address (W)
CH3_EXA ==      0X3E                      ; CH-3 extended address (W)

... apparently, they rotated right the offset, compared to normal hook-up.
*/

uint8_t apc_state::apc_dma_r(offs_t offset)
{
	return m_dmac->read(bitswap<4>(offset,2,1,0,3));
}

void apc_state::apc_dma_w(offs_t offset, uint8_t data)
{
	m_dmac->write(bitswap<4>(offset,2,1,0,3), data);
}

void apc_state::apc_irq_ack_w(uint8_t data)
{
	/*
	    x--- GDC
	    -x-- TM
	    --x- APU
	    ---x CRT
	*/
	if(data & 4)
		m_i8259_m->ir3_w(0);

	if(data & ~4)
		logerror("IRQ ACK %02x\n",data);
}

uint8_t apc_state::apc_rtc_r()
{
	/*
	bit 1 high: low battery.
	*/
	//logerror("RTC Read: %d\n", m_rtc->data_out_r());
	return m_rtc->data_out_r();
}

void apc_state::apc_rtc_w(uint8_t data)
{
/*
RTC write: 0x01 0001
RTC write: 0x03 0011
RTC write: 0x0b 1011 <- cmd: read clock to shifter latch
RTC write: 0x03 0011
RTC write: 0x01 0001
RTC write: 0x09 1001 <- cmd: begin shifting latch data out
RTC write: 0x01 0001
RTC write: 0x11
RTC write: 0x01
RTC write: 0x11
RTC write: 0x01
RTC write: 0x11
...

RTC write bits: 76543210
                |||||||\- c0
                ||||||\-- c1
                |||||\--- c2
                ||||\---- STB
                |||\----- CLK
                ||\------ DATA_IN
                |\------- "don't care"
                \-------- ///
*/
	if (data&0xc0) logerror("RTC write: 0x%02x\n", data);
	m_rtc->c0_w(BIT(data, 0)); // correct assuming theres a delay for changing command lines before stb
	m_rtc->c1_w(BIT(data, 1)); // "
	m_rtc->c2_w(BIT(data, 2)); // "
	m_rtc->stb_w(BIT(data, 3)); // seems correct assuming delay for changing command line
	m_rtc->clk_w(BIT(data, 4)); // correct for sure
	m_rtc->data_in_w(BIT(data, 5)); // ? no idea about this.
	m_rtc->oe_w(1);
}

void apc_state::apc_map(address_map &map)
{
	map(0x00000, 0x9ffff).ram();
	map(0xa0000, 0xa0fff).ram().share("cmos");
//  map(0xa1000, 0xbffff) mirror CMOS
//  map(0xc0000, 0xcffff) standard character ROM
	map(0xd8000, 0xd9fff).ram().share("aux_pcg"); // AUX character RAM
//  map(0xe0000, 0xeffff) Special Character RAM
	map(0xfe000, 0xfffff).rom().region("ipl", 0);
}

void apc_state::apc_io(address_map &map)
{
//  map.global_mask(0xff);
	map(0x00, 0x1f).rw(FUNC(apc_state::apc_dma_r), FUNC(apc_state::apc_dma_w)).umask16(0xff00);
	map(0x20, 0x23).rw(m_i8259_m, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff); // i8259
	map(0x28, 0x2f).rw(FUNC(apc_state::apc_port_28_r), FUNC(apc_state::apc_port_28_w)); // i8259 (even) / pit8253 (odd)
//  0x30, 0x37 serial port 0/1 (i8251) (even/odd)
	map(0x38, 0x3f).w(FUNC(apc_state::apc_dma_segments_w)).umask16(0x00ff);
	map(0x40, 0x43).rw(FUNC(apc_state::apc_gdc_r), FUNC(apc_state::apc_gdc_w));
	map(0x46, 0x46).w(FUNC(apc_state::apc_irq_ack_w));
	map(0x48, 0x4f).rw(FUNC(apc_state::apc_kbd_r), FUNC(apc_state::apc_kbd_w)).umask16(0x00ff);
	map(0x50, 0x53).m(m_fdc, FUNC(upd765a_device::map)).umask16(0x00ff); // upd765
	map(0x58, 0x58).rw(FUNC(apc_state::apc_rtc_r), FUNC(apc_state::apc_rtc_w));
//  0x59 CMOS enable
//  0x5a  APU data (Arithmetic Processing Unit!)
//  0x5b, Power Off
//  0x5e  APU status/command
	map(0x60, 0x60).rw(m_sound, FUNC(upd1771c_cpu_device::pa_r), FUNC(upd1771c_cpu_device::pa_w));
//  map(0x68, 0x6f) i8255 , ODA printer port (A: status (R) B: data (W) C: command (W))
//  map(0x70, 0x76).rw("upd7220_btm", FUNC(upd7220_device::read), FUNC(upd7220_device::write)).umask16(0x00ff);
//  0x71, 0x77 IDA Controller
//  0x80, 0x90 Communication Adapter
//  0xf0, 0xf6 ASOP Controller
}

/* TODO: key repeat, remove port impulse! */
INPUT_CHANGED_MEMBER(apc_state::key_stroke)
{
	if(newval && !oldval)
	{
		m_keyb.data = uint8_t(param & 0xff);
		//m_keyb.status &= ~1;
		m_i8259_m->ir4_w(1);
	}

	if(oldval && !newval)
	{
		m_keyb.data = 0xff;
		//m_keyb.status |= 1;
	}
}


static INPUT_PORTS_START( apc )
	PORT_START("KEY0")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x30)
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x31)
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x32)
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x33)
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x34)
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x35)
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x36)
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x37)

	PORT_START("KEY1")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x38)
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x39)

	PORT_START("KEY2")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x41)
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x42)
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x43)
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x44)
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x45)
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x46)
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x47)
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x48)

	PORT_START("KEY3")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x49)
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x4a)
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x4b)
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x4c)
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x4d)
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x4e)
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x4f)
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x50)

	PORT_START("KEY4")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x51)
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x52)
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x53)
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x54)
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x55)
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x56)
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x57)
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x58)

	PORT_START("KEY5")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x59)
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x5a)
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("[ / {") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x5b)
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\\ / |") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x5c)
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("] / }") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x5d)
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("(up score) / ^") PORT_CHAR('^') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x5e)
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("- / _") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x5f)
//  PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("unk6") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x26)

	PORT_START("KEY6")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x20)
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("; / :") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x3a)
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("= / +") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x2d)
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("` / ~") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('`') PORT_CHAR('~') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x40)
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("' / \"") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x3b)
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(", / <") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x2c)
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(". / >") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x2e)
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("/ / ?") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?') PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x2f)


/*
;
; Special table for screwed-up keys.  Scan codes are converted.
;
SPECTBL:
    BYTE    0X2D,0X3D
    BYTE    0X40,0X60
    BYTE    0X3A,0X3B
    BYTE    0X3B,0X27
SPECTLN ==  (.-SPECTBL)/2       ; length of table
;
; Shift case table
;
CASETBL:
    BYTE    "1!"
    BYTE    "2@"
    BYTE    "3#"
    BYTE    "4$"
    BYTE    "5%"
    BYTE    "6",0XD0
    BYTE    "7&"
    BYTE    "8*"
    BYTE    "9("
    BYTE    "0)"
    BYTE    "-_"
    BYTE    "=+"
    BYTE    "`~"
    BYTE    "[{"
    BYTE    "]}"
    BYTE    "\\|"
    BYTE    ",<"
    BYTE    ".>"
    BYTE    "/?"
    BYTE    ";:"
    BYTE    0X27,0X22
    BYTE    0X18,"^"
*/

/*
    BYTE    0X18            ; 5E - Control-X
    BYTE    "-"         ; 5F
*/

/*
    #REPEAT 0X96-0X80       ; 80 to 95 - function keys
*/
	PORT_START("KEY_PF1")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF1")  PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x80)
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF2")  PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x81)
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF3")  PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x82)
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF4")  PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x83)
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF5")  PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x84)
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF6")  PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x85)
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF7")  PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x86)
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF8")  PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x87)

	PORT_START("KEY_PF2")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF9")  PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x88)
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF10") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x89)
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF11") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x8a)
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF12") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x8b)
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF13") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x8c)
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF14") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x8d)
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF15") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x8e)
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF16") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x8f)

	PORT_START("KEY_PF3")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF17")  PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x90)
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF18")  PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x91)
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF19")  PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x92)
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF20")  PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x93)
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF21")  PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x94)
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF22")  PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x95)

/*
    BYTE    "*"         ; 6A
    BYTE    "+"         ; 6B
    BYTE    0XFF        ; 6C - undefined code
    BYTE    "-"         ; 6D
    BYTE    "."         ; 6E
    BYTE    "/"         ; 6F
    BYTE    "0"         ; 70
    BYTE    "1"         ; 71
    BYTE    "2"         ; 72
    BYTE    "3"         ; 73
    BYTE    "4"         ; 74
    BYTE    "5"         ; 75
    BYTE    "6"         ; 76
    BYTE    "7"         ; 77
    BYTE    "8"         ; 78
    BYTE    "9"         ; 79
*/

	PORT_START("KEY_PAD1")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("* (PAD)") PORT_CODE(KEYCODE_ASTERISK) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x6a)
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("+ (PAD)") PORT_CODE(KEYCODE_PLUS_PAD) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x6b)
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_UNUSED) // 0x6c
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("- (PAD)") PORT_CODE(KEYCODE_MINUS_PAD) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x6d)
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(". (PAD)") PORT_CODE(KEYCODE_DEL_PAD) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x6e)
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("/ (PAD)") PORT_CODE(KEYCODE_SLASH_PAD) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x6f)
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0 (PAD)") PORT_CODE(KEYCODE_0_PAD) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x70)
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1 (PAD)") PORT_CODE(KEYCODE_1_PAD) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x71)

	PORT_START("KEY_PAD2")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2 (PAD)") PORT_CODE(KEYCODE_2_PAD) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x72)
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3 (PAD)") PORT_CODE(KEYCODE_3_PAD) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x73)
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("4 (PAD)") PORT_CODE(KEYCODE_4_PAD) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x74)
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("5 (PAD)") PORT_CODE(KEYCODE_5_PAD) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x75)
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("6 (PAD)") PORT_CODE(KEYCODE_6_PAD) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x76)
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("7 (PAD)") PORT_CODE(KEYCODE_7_PAD) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x77)
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("8 (PAD)") PORT_CODE(KEYCODE_8_PAD) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x78)
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("9 (PAD)") PORT_CODE(KEYCODE_9_PAD) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x79)


/*  BYTE    0X00            ; 96 - break stop
    BYTE    0X0D            ; 97 - return
    BYTE    0X09            ; 98 - tab/back tab
    BYTE    0XFF            ; 99 - undefined code
    BYTE    0X1E            ; 9A - home/clear
    BYTE    0XFF            ; 9B - undefined code
    BYTE    0X08            ; 9C - back space
*/
	PORT_START("KEY_S1")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("BREAK") PORT_CHAR(0x00) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x96)
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(0x0d) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x97)
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("TAB") /*PORT_CODE(KEYCODE_TAB)*/ PORT_CHAR(0x09) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x98)
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_UNUSED) //0x99
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("HOME") PORT_CODE(KEYCODE_HOME) PORT_CHAR(0x1e) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x9a)
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_UNUSED) //0x9b
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("BACK SPACE") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(0x08) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x9c)
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("ESC") PORT_CHAR(0x1b) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0x1b)

/*
    BYTE    0X0B            ; F7 - up arrow
    BYTE    0X0A            ; F8 - down arrow
    BYTE    0X0C            ; F9 - right arrow
    BYTE    0X08            ; FA - left arrow
    BYTE    0XFF (?)        ; FB - ins
    BYTE    0X7F            ; FC - del
    BYTE    0X0D            ; FD - enter
*/
	PORT_START("KEY_S2")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0xf7)
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0xf8)
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0xf9)
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0xfa)
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("INS") PORT_CHAR(UCHAR_MAMEKEY(INSERT)) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0xfb)
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("DEL") PORT_CHAR(UCHAR_MAMEKEY(DEL)) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0xfc)
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("ENTER (PAD)") PORT_CODE(KEYCODE_ENTER_PAD) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apc_state::key_stroke), 0xfd)

	PORT_START("KEY_MOD")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("CAPS LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
INPUT_PORTS_END

void apc_state::machine_start()
{
	m_fdc->set_rate(500000);

	m_rtc->cs_w(1);
//  m_rtc->oe_w(1);
}

void apc_state::machine_reset()
{
	m_keyb.status = 0;
	m_keyb.data = 0;
	m_keyb.sig = 0;
}

static const gfx_layout charset_8x16 =
{
	8, 16,
	128,
	1,
	{ 0 },
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	{ 0*1024, 1*1024, 2*1024, 3*1024, 4*1024, 5*1024, 6*1024, 7*1024, 8*1024, 9*1024, 10*1024, 11*1024, 12*1024, 13*1024, 14*1024, 15*1024 },
	8
};

#if 0
static const gfx_layout charset_pcg =
{
	8, 16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	{ STEP16(0,16) },
	8*32
};
#endif

static GFXDECODE_START( gfx_apc )
	GFXDECODE_ENTRY( "gfx", 0x0000, charset_8x16, 0, 128 )
	GFXDECODE_ENTRY( "gfx", 0x0800, charset_8x16, 0, 128 )
	GFXDECODE_ENTRY( "gfx", 0x1000, charset_8x16, 0, 128 )
	GFXDECODE_ENTRY( "gfx", 0x1800, charset_8x16, 0, 128 )
GFXDECODE_END



void apc_state::upd7220_1_map(address_map &map)
{
	map(0x00000, 0x3ffff).ram().share(m_video_ram_1);
}

void apc_state::upd7220_2_map(address_map &map)
{
	map(0x00000, 0x3ffff).ram().share(m_video_ram_2);
}

/*
irq assignment:
(note: documentation shows ODA Printer at ir7 master, but clearly everything is shifted one place due of the
 master-slave irq comms. This is trusted also because MS-DOS effectively wants FDC irq at ir4 slave)

8259 master:
ir0 all stop (enabled at POST, unknown purpose)
ir1 Communication
ir2 Option
ir3 Timer
ir4 keyboard (almost trusted, check code at fe64a)
ir5 Option
ir6 Option
ir7 slave irq

8259 slave:
ir0 ODA Printer
ir1 Option
ir2 Option
ir3 CRT
ir4 FDD
ir5 Option
ir6 Option
ir7 APU
*/

uint8_t apc_state::get_slave_ack(offs_t offset)
{
	if (offset==7) { // IRQ = 7
		return m_i8259_s->acknowledge();
	}
	return 0x00;
}

/****************************************
*
* I8237 DMA interface
*
****************************************/

void apc_state::apc_dma_hrq_changed(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);

	m_dmac->hack_w(state);

//  logerror("%02x HLDA\n",state);
}

void apc_state::apc_tc_w(int state)
{
	/* floppy terminal count */
	m_fdc->tc_w(state);

//  logerror("TC %02x\n",state);
}

uint8_t apc_state::apc_dma_read_byte(offs_t offset)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	offs_t addr = (m_dma_offset[m_dack] << 16) | offset;

//  logerror("%08x\n",addr);

	return program.read_byte(addr);
}


void apc_state::apc_dma_write_byte(offs_t offset, uint8_t data)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	offs_t addr = (m_dma_offset[m_dack] << 16) | offset;

//  logerror("%08x %02x\n",addr,data);

	program.write_byte(addr, data);
}

inline void apc_state::set_dma_channel(int channel, int state)
{
	if (!state) m_dack = channel;
}

void apc_state::apc_dack0_w(int state) { /*logerror("%02x 0\n",state);*/ set_dma_channel(0, state); }
void apc_state::apc_dack1_w(int state) { /*logerror("%02x 1\n",state);*/ set_dma_channel(1, state); }
void apc_state::apc_dack2_w(int state) { /*logerror("%02x 2\n",state);*/ set_dma_channel(2, state); }
void apc_state::apc_dack3_w(int state) { /*logerror("%02x 3\n",state);*/ set_dma_channel(3, state); }

/*
CH0: CRT
CH1: FDC
CH2: ("reserved for future graphics expansion")
CH3: AUX
*/

static void apc_floppies(device_slot_interface &device)
{
	device.option_add("8", FLOPPY_8_DSDD);
}

void apc_state::apc(machine_config &config)
{
	/* basic machine hardware */
	I8086(config, m_maincpu, MAIN_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &apc_state::apc_map);
	m_maincpu->set_addrmap(AS_IO, &apc_state::apc_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(MAIN_CLOCK); // heartbeat IRQ
	m_pit->out_handler<0>().set(m_i8259_m, FUNC(pic8259_device::ir3_w));
	m_pit->set_clk<1>(MAIN_CLOCK); // Memory Refresh
	m_pit->set_clk<2>(MAIN_CLOCK); // RS-232c

	PIC8259(config, m_i8259_m, 0);
	m_i8259_m->out_int_callback().set_inputline(m_maincpu, 0);
	m_i8259_m->in_sp_callback().set_constant(1);
	m_i8259_m->read_slave_ack_callback().set(FUNC(apc_state::get_slave_ack));

	PIC8259(config, m_i8259_s, 0);
	m_i8259_s->out_int_callback().set(m_i8259_m, FUNC(pic8259_device::ir7_w)); // TODO: check ir7_w
	m_i8259_s->in_sp_callback().set_constant(0);

	AM9517A(config, m_dmac, MAIN_CLOCK);
	m_dmac->out_hreq_callback().set(FUNC(apc_state::apc_dma_hrq_changed));
	m_dmac->out_eop_callback().set(FUNC(apc_state::apc_tc_w));
	m_dmac->in_memr_callback().set(FUNC(apc_state::apc_dma_read_byte));
	m_dmac->out_memw_callback().set(FUNC(apc_state::apc_dma_write_byte));
	m_dmac->in_ior_callback<1>().set(m_fdc, FUNC(upd765a_device::dma_r));
	m_dmac->out_iow_callback<1>().set(m_fdc, FUNC(upd765a_device::dma_w));
	m_dmac->out_dack_callback<0>().set(FUNC(apc_state::apc_dack0_w));
	m_dmac->out_dack_callback<1>().set(FUNC(apc_state::apc_dack1_w));
	m_dmac->out_dack_callback<2>().set(FUNC(apc_state::apc_dack2_w));
	m_dmac->out_dack_callback<3>().set(FUNC(apc_state::apc_dack3_w));

	NVRAM(config, m_cmos, nvram_device::DEFAULT_ALL_1);
	UPD1990A(config, m_rtc);

	UPD765A(config, m_fdc, 8'000'000, true, true);
	m_fdc->intrq_wr_callback().set(m_i8259_s, FUNC(pic8259_device::ir4_w));
	m_fdc->drq_wr_callback().set(m_dmac, FUNC(am9517a_device::dreq1_w));
	FLOPPY_CONNECTOR(config, m_fdc_connector[0], apc_floppies, "8", floppy_image_device::default_fm_floppy_formats, "8");
	FLOPPY_CONNECTOR(config, m_fdc_connector[1], apc_floppies, "8", floppy_image_device::default_fm_floppy_formats, "8");
	SOFTWARE_LIST(config, "disk_list").set_original("apc");

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	m_screen->set_screen_update(FUNC(apc_state::screen_update));
	m_screen->set_size(640, 494);
	m_screen->set_visarea(0*8, 640-1, 0*8, 494-1);

	PALETTE(config, m_palette, palette_device::BRG_3BIT);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_apc);

	UPD7220(config, m_hgdc1, 3579545); // unk clock
	m_hgdc1->set_addrmap(0, &apc_state::upd7220_1_map);
	m_hgdc1->set_draw_text(FUNC(apc_state::hgdc_draw_text));

	UPD7220(config, m_hgdc2, 3579545); // unk clock
	m_hgdc2->set_addrmap(0, &apc_state::upd7220_2_map);
	m_hgdc2->set_display_pixels(FUNC(apc_state::hgdc_display_pixels));

	/* sound hardware */
	SPEAKER(config, m_speaker).front_center();
	UPD1771C(config, m_sound, MAIN_CLOCK).add_route(ALL_OUTPUTS, "mono", 1.00); //uPD1771C-006
}


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

  Game driver(s)

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

ROM_START( apc )
	ROM_REGION16_LE( 0x2000, "ipl", ROMREGION_ERASE00 )
	ROM_LOAD16_BYTE( "pfbu2j.bin",   0x00000, 0x001000, CRC(86970df5) SHA1(be59c5dad3bd8afc21e9f2f1404553d4371978be) )
	ROM_LOAD16_BYTE( "pfbu2l.bin",   0x00001, 0x001000, CRC(38df2e70) SHA1(a37ccaea00c2b290610d354de08b489fa897ec48) )

//  ROM_REGION( 0x10000, "file", ROMREGION_ERASE00 )
//  ROM_LOAD( "sioapc.bin", 0, 0x10000, NO_DUMP )

	ROM_REGION( 0x2000, "gfx", ROMREGION_ERASE00 )
	ROM_LOAD("pfcu1r.bin",   0x000000, 0x002000, CRC(683efa94) SHA1(43157984a1746b2e448f3236f571011af9a3aa73) )

	ROM_REGION( 0x400, "upd1771c", ROMREGION_ERASE00 )
	ROM_LOAD( "upd1771c_006", 0, 0x400, NO_DUMP )
ROM_END

void apc_state::init_apc()
{
	// ...
}

} // anonymous namespace


COMP( 1982, apc, 0, 0, apc, apc, apc_state, init_apc, "NEC", "APC", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



apexc.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Raphael Nabet, Robbbert
/*
    drivers/apexc.cpp : APEXC driver

    By Raphael Nabet

    see cpu/apexc.cpp for background and tech info
*/

#include "emu.h"
#include "apexc.h"

void apexc_state::machine_start()
{
	teletyper_init();

	m_input_timer = timer_alloc(FUNC(apexc_state::check_inputs), this);
	m_input_timer->adjust(attotime::from_hz(60), 0, attotime::from_hz(60));

	m_panel_data_reg = 0;
}

/*
    Punch a tape character
*/

void apexc_state::tape_write(uint8_t data)
{
	m_tape_puncher->write(data);
	teletyper_putchar(data & 0x1f); /* display on 'screen' */
}

/*
    APEXC control panel

    I know really little about the details, although the big picture is obvious.

    Things I know :
    * "As well as starting and stopping the machine, [it] enables information to be inserted
    manually and provides for the inspection of the contents of the memory via various
    storage registers." (Booth, p. 2)
    * "Data can be inserted manually from the control panel [into the control register]".
    (Booth, p. 3)
    * The contents of the R register can be edited, too.  A button allows to clear
    a complete X (or Y ???) field.  (forgot the reference, but must be somewhere in Booth)
    * There is no trace mode (Booth, p. 213)

    Since the control panel is necessary for the operation of the APEXC, I tried to
    implement a commonplace control panel.  I cannot tell how close the feature set and
    operation of this control panel is to the original APEXC control panel, but it
    cannot be too different in the basic principles.
*/


#if 0
/* defines for input port numbers */
enum
{
	panel_control = 0,
	panel_edit1,
	panel_edit2
};
#endif


/* defines for each bit and mask in input port panel_control */
enum
{
	/* bit numbers */
	panel_run_bit = 0,
	panel_CR_bit,
	panel_A_bit,
	panel_R_bit,
	panel_HB_bit,
	panel_ML_bit,
	panel_mem_bit,
	panel_write_bit,

	/* masks */
	panel_run = (1 << panel_run_bit),
	panel_CR  = (1 << panel_CR_bit),
	panel_A   = (1 << panel_A_bit),
	panel_R   = (1 << panel_R_bit),
	panel_HB  = (1 << panel_HB_bit),
	panel_ML  = (1 << panel_ML_bit),
	panel_mem = (1 << panel_mem_bit),
	panel_write = (1 << panel_write_bit)
};

/* fake input ports with keyboard keys */
static INPUT_PORTS_START(apexc)

	PORT_START("panel") /* 0 : panel control */
	PORT_BIT(panel_run, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Run/Stop")                PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(panel_CR,  IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Read CR")                 PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(panel_A,   IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Read A")                  PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(panel_R,   IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Read R")                  PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(panel_HB,  IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Read HB")                 PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(panel_ML,  IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Read ML")                 PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(panel_mem, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Read mem")                PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(panel_write, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Write instead of read") PORT_CODE(KEYCODE_LSHIFT)

	PORT_START("data")  /* data edit */
	PORT_BIT(0x80000000, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #1")              PORT_CODE(KEYCODE_1)
	PORT_BIT(0x40000000, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #2")              PORT_CODE(KEYCODE_2)
	PORT_BIT(0x20000000, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #3")              PORT_CODE(KEYCODE_3)
	PORT_BIT(0x10000000, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #4")              PORT_CODE(KEYCODE_4)
	PORT_BIT(0x08000000, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #5")              PORT_CODE(KEYCODE_5)
	PORT_BIT(0x04000000, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #6")              PORT_CODE(KEYCODE_6)
	PORT_BIT(0x02000000, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #7")              PORT_CODE(KEYCODE_7)
	PORT_BIT(0x01000000, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #8")              PORT_CODE(KEYCODE_8)
	PORT_BIT(0x00800000, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #9")              PORT_CODE(KEYCODE_9)
	PORT_BIT(0x00400000, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #10")             PORT_CODE(KEYCODE_0)
	PORT_BIT(0x00200000, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #11")             PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x00100000, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #12")             PORT_CODE(KEYCODE_W)
	PORT_BIT(0x00080000, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #13")             PORT_CODE(KEYCODE_E)
	PORT_BIT(0x00040000, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #14")             PORT_CODE(KEYCODE_R)
	PORT_BIT(0x00020000, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #15")             PORT_CODE(KEYCODE_T)
	PORT_BIT(0x00010000, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #16")             PORT_CODE(KEYCODE_Y)

	PORT_BIT(0x00008000, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #17")             PORT_CODE(KEYCODE_U)
	PORT_BIT(0x00004000, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #18")             PORT_CODE(KEYCODE_I)
	PORT_BIT(0x00002000, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #19")             PORT_CODE(KEYCODE_O)
	PORT_BIT(0x00001000, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #20")             PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x00000800, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #21")             PORT_CODE(KEYCODE_A)
	PORT_BIT(0x00000400, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #22")             PORT_CODE(KEYCODE_S)
	PORT_BIT(0x00000200, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #23")             PORT_CODE(KEYCODE_D)
	PORT_BIT(0x00000100, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #24")             PORT_CODE(KEYCODE_F)
	PORT_BIT(0x00000080, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #25")             PORT_CODE(KEYCODE_G)
	PORT_BIT(0x00000040, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #26")             PORT_CODE(KEYCODE_H)
	PORT_BIT(0x00000020, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #27")             PORT_CODE(KEYCODE_J)
	PORT_BIT(0x00000010, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #28")             PORT_CODE(KEYCODE_K)
	PORT_BIT(0x00000008, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #29")             PORT_CODE(KEYCODE_L)
	PORT_BIT(0x00000004, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #30")             PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x00000002, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #31")             PORT_CODE(KEYCODE_X)
	PORT_BIT(0x00000001, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Toggle bit #32")             PORT_CODE(KEYCODE_C)
INPUT_PORTS_END

TIMER_CALLBACK_MEMBER(apexc_state::check_inputs)
{
	/* read new state of edit keys */
	uint32_t edit_keys = m_data_port->read();

	/* toggle data reg according to transitions */
	m_panel_data_reg ^= edit_keys & (~m_old_edit_keys);

	/* remember new state of edit keys */
	m_old_edit_keys = edit_keys;

	/* read new state of control keys */
	int control_keys = m_panel_port->read();

	/* compute transitions */
	int control_transitions = control_keys & (~m_old_control_keys);

	/* process commands */

	if (control_transitions & panel_run)
	{   /* toggle run/stop state */
		m_maincpu->set_state_int(APEXC_STATE, ! m_maincpu->state_int(APEXC_STATE));
	}

	while (control_transitions & (panel_CR | panel_A | panel_R | panel_ML | panel_HB))
	{   /* read/write a register */
		/* note that we must take into account the possibility of simulteanous keypresses
		(which would be a goofy thing to do when reading, but a normal one when writing,
		if the user wants to clear several registers at once) */
		printf("crarmlhb\n");
		int reg_id = -1;

		/* determinate value of reg_id */
		if (control_transitions & panel_CR)
		{   /* CR register selected ? */
			control_transitions &= ~panel_CR;   /* clear so that it is ignored on next iteration */
			reg_id = APEXC_CR;          /* matching register ID */
		}
		else if (control_transitions & panel_A)
		{
			control_transitions &= ~panel_A;
			reg_id = APEXC_A;
		}
		else if (control_transitions & panel_R)
		{
			control_transitions &= ~panel_R;
			reg_id = APEXC_R;
		}
		else if (control_transitions & panel_HB)
		{
			control_transitions &= ~panel_HB;
			reg_id = APEXC_WS;
		}
		else if (control_transitions & panel_ML)
		{
			control_transitions &= ~panel_ML;
			reg_id = APEXC_ML;
		}

		if (-1 != reg_id)
		{
			/* read/write register #reg_id */
			if (control_keys & panel_write)
				/* write reg */
				m_maincpu->set_state_int(reg_id, m_panel_data_reg);
			else
				/* read reg */
				m_panel_data_reg = m_maincpu->state_int(reg_id);
		}
	}

	if (control_transitions & panel_mem)
	{   /* read/write memory */
		if (control_keys & panel_write) /* write memory */
			m_maincpu->space(AS_PROGRAM).write_dword(m_maincpu->pc(), m_panel_data_reg);
		else                            /* read memory */
			m_panel_data_reg = m_maincpu->space(AS_PROGRAM).read_dword(m_maincpu->pc());
	}

	/* remember new state of control keys */
	m_old_control_keys = control_keys;
}

enum
{
	apexc_charnum = /*96+4*/128,    /* ASCII set + 4 special characters */
									/* for whatever reason, 96+4 breaks greek characters */

	apexcfontdata_size = 8 * apexc_charnum
};

/* apexc driver init : builds a font for use by the teletyper */
void apexc_state::init_apexc()
{
	static const unsigned char fontdata6x8[apexcfontdata_size] =
	{   /* ASCII characters */
		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,
		0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0xf8,0x50,0xf8,0x50,0x00,0x00,
		0x20,0x70,0xc0,0x70,0x18,0xf0,0x20,0x00,0x40,0xa4,0x48,0x10,0x20,0x48,0x94,0x08,
		0x60,0x90,0xa0,0x40,0xa8,0x90,0x68,0x00,0x10,0x20,0x40,0x00,0x00,0x00,0x00,0x00,
		0x20,0x40,0x40,0x40,0x40,0x40,0x20,0x00,0x10,0x08,0x08,0x08,0x08,0x08,0x10,0x00,

		0x20,0xa8,0x70,0xf8,0x70,0xa8,0x20,0x00,0x00,0x20,0x20,0xf8,0x20,0x20,0x00,0x00,
		0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x60,0x00,0x00,0x00,0xf8,0x00,0x00,0x00,0x00,
		0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,
		0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x10,0x30,0x10,0x10,0x10,0x10,0x10,0x00,
		0x70,0x88,0x08,0x10,0x20,0x40,0xf8,0x00,0x70,0x88,0x08,0x30,0x08,0x88,0x70,0x00,
		0x10,0x30,0x50,0x90,0xf8,0x10,0x10,0x00,0xf8,0x80,0xf0,0x08,0x08,0x88,0x70,0x00,
		0x70,0x80,0xf0,0x88,0x88,0x88,0x70,0x00,0xf8,0x08,0x08,0x10,0x20,0x20,0x20,0x00,
		0x70,0x88,0x88,0x70,0x88,0x88,0x70,0x00,0x70,0x88,0x88,0x88,0x78,0x08,0x70,0x00,
		0x00,0x00,0x30,0x30,0x00,0x30,0x30,0x00,0x00,0x00,0x30,0x30,0x00,0x30,0x30,0x60,
		0x10,0x20,0x40,0x80,0x40,0x20,0x10,0x00,0x00,0x00,0xf8,0x00,0xf8,0x00,0x00,0x00,
		0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x00,0x70,0x88,0x08,0x10,0x20,0x00,0x20,0x00,
		0x70,0x88,0xb8,0xa8,0xb8,0x80,0x70,0x00,0x70,0x88,0x88,0xf8,0x88,0x88,0x88,0x00,
		0xf0,0x88,0x88,0xf0,0x88,0x88,0xf0,0x00,0x70,0x88,0x80,0x80,0x80,0x88,0x70,0x00,
		0xf0,0x88,0x88,0x88,0x88,0x88,0xf0,0x00,0xf8,0x80,0x80,0xf0,0x80,0x80,0xf8,0x00,
		0xf8,0x80,0x80,0xf0,0x80,0x80,0x80,0x00,0x70,0x88,0x80,0x98,0x88,0x88,0x70,0x00,
		0x88,0x88,0x88,0xf8,0x88,0x88,0x88,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,
		0x08,0x08,0x08,0x08,0x88,0x88,0x70,0x00,0x88,0x90,0xa0,0xc0,0xa0,0x90,0x88,0x00,
		0x80,0x80,0x80,0x80,0x80,0x80,0xf8,0x00,0x88,0xd8,0xa8,0x88,0x88,0x88,0x88,0x00,
		0x88,0xc8,0xa8,0x98,0x88,0x88,0x88,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00,
		0xf0,0x88,0x88,0xf0,0x80,0x80,0x80,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x08,
		0xf0,0x88,0x88,0xf0,0x88,0x88,0x88,0x00,0x70,0x88,0x80,0x70,0x08,0x88,0x70,0x00,
		0xf8,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00,
		0x88,0x88,0x88,0x88,0x88,0x50,0x20,0x00,0x88,0x88,0x88,0x88,0xa8,0xd8,0x88,0x00,
		0x88,0x50,0x20,0x20,0x20,0x50,0x88,0x00,0x88,0x88,0x88,0x50,0x20,0x20,0x20,0x00,
		0xf8,0x08,0x10,0x20,0x40,0x80,0xf8,0x00,0x30,0x20,0x20,0x20,0x20,0x20,0x30,0x00,
		0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x30,0x10,0x10,0x10,0x10,0x10,0x30,0x00,
		0x20,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfc,
		0x40,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x08,0x78,0x88,0x78,0x00,
		0x80,0x80,0xf0,0x88,0x88,0x88,0xf0,0x00,0x00,0x00,0x70,0x88,0x80,0x80,0x78,0x00,
		0x08,0x08,0x78,0x88,0x88,0x88,0x78,0x00,0x00,0x00,0x70,0x88,0xf8,0x80,0x78,0x00,
		0x18,0x20,0x70,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x78,0x88,0x88,0x78,0x08,0x70,
		0x80,0x80,0xf0,0x88,0x88,0x88,0x88,0x00,0x20,0x00,0x20,0x20,0x20,0x20,0x20,0x00,
		0x20,0x00,0x20,0x20,0x20,0x20,0x20,0xc0,0x80,0x80,0x90,0xa0,0xe0,0x90,0x88,0x00,
		0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xf0,0xa8,0xa8,0xa8,0xa8,0x00,
		0x00,0x00,0xb0,0xc8,0x88,0x88,0x88,0x00,0x00,0x00,0x70,0x88,0x88,0x88,0x70,0x00,
		0x00,0x00,0xf0,0x88,0x88,0xf0,0x80,0x80,0x00,0x00,0x78,0x88,0x88,0x78,0x08,0x08,
		0x00,0x00,0xb0,0xc8,0x80,0x80,0x80,0x00,0x00,0x00,0x78,0x80,0x70,0x08,0xf0,0x00,
		0x20,0x20,0x70,0x20,0x20,0x20,0x18,0x00,0x00,0x00,0x88,0x88,0x88,0x98,0x68,0x00,
		0x00,0x00,0x88,0x88,0x88,0x50,0x20,0x00,0x00,0x00,0xa8,0xa8,0xa8,0xa8,0x50,0x00,
		0x00,0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00,0x00,0x88,0x88,0x88,0x78,0x08,0x70,
		0x00,0x00,0xf8,0x10,0x20,0x40,0xf8,0x00,0x08,0x10,0x10,0x20,0x10,0x10,0x08,0x00,
		0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x40,0x20,0x20,0x10,0x20,0x20,0x40,0x00,
		0x00,0x68,0xb0,0x00,0x00,0x00,0x00,0x00,0x20,0x50,0x20,0x50,0xa8,0x50,0x00,0x00,

		/* theta */
		0x70,
		0x88,
		0x88,
		0xF8,
		0x88,
		0x88,
		0x70,
		0x00,

		/* Sigma */
		0xf8,
		0x40,
		0x20,
		0x10,
		0x20,
		0x40,
		0xf8,
		0x00,

		/* Phi */
		0x20,
		0x70,
		0xA8,
		0xA8,
		0xA8,
		0xA8,
		0x70,
		0x20,

		/* pi */
		0x00,
		0x00,
		0xF8,
		0x50,
		0x50,
		0x50,
		0x50,
		0x00
	};

	memcpy(m_chargen_region->base(), fontdata6x8, apexcfontdata_size);
}

static const gfx_layout fontlayout =
{
	6, 8,           /* 6*8 characters */
	apexc_charnum,  /* 96+4 characters */
	1,              /* 1 bit per pixel */
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* straightforward layout */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8 /* every char takes 8 consecutive bytes */
};

static GFXDECODE_START( gfx_apexc )
	GFXDECODE_ENTRY( "chargen", 0, fontlayout, 0, 2 )
GFXDECODE_END


void apexc_state::mem(address_map &map)
{
	map(0x0000, 0x0fff).ram().share("maincpu");
	map(0x1000, 0x7fff).noprw();
}

void apexc_state::apexc(machine_config &config)
{
	/* basic machine hardware */
	/* APEXC CPU @ 2.0 kHz (memory word clock frequency) */
	APEXC(config, m_maincpu, 2000);
	m_maincpu->set_addrmap(AS_PROGRAM, &apexc_state::mem);
	m_maincpu->tape_read().set(m_tape_reader, FUNC(apexc_tape_reader_image_device::read));
	m_maincpu->tape_punch().set(FUNC(apexc_state::tape_write));

	/* video hardware does not exist, but we display a control panel and the typewriter output */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_size(256, 192);
	m_screen->set_visarea(0, 256-1, 0, 192-1);
	m_screen->set_palette(m_palette);
	m_screen->set_screen_update(FUNC(apexc_state::screen_update));

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_apexc);

	PALETTE(config, m_palette, FUNC(apexc_state::apexc_palette), std::size(palette_table));

	APEXC_CYLINDER(config, m_cylinder);
	APEXC_TAPE_PUNCHER(config, m_tape_puncher);
	APEXC_TAPE_READER(config, m_tape_reader);

	SOFTWARE_LIST(config, "cyl_list").set_original("apexc_cyl");
}

ROM_START(apexc)
	/*CPU memory space*/
	/* Note this computer has no ROM... */

	ROM_REGION(apexcfontdata_size, "chargen", ROMREGION_ERASEFF)
		/* space filled with our font */
ROM_END

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY                FULLNAME */
//COMP( 1951, apexc53, 0,      0,      apexc53, apexc, apexc_state, init_apexc, "Andrew Donald Booth", "All Purpose Electronic X-ray Computer (as described in 1953)" , MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)
COMP( 1955, apexc,   0,      0,      apexc,   apexc, apexc_state, init_apexc, "Andrew Donald Booth", "All Purpose Electronic X-ray Computer (as described in 1957)" , MACHINE_NO_SOUND_HW)



apf.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/******************************************************************************

Driver completely rewritten by Robbbert in a process begun on 2014-01-22.

Based on the previous work done by PeT around February 2008, and possible others prior.
 added apfm1000 cartridge loading
 fixed apfm1000 pads
 added apf video mode


APF M1000/MP1000 and Imagination Machine
----------------------------------------
- The M1000 contains the video RAM, ROM, CPU, PIA0, handsets, Video and cart slot, and thus was a TV Game computer.
- The MPA-10 was a base unit containing the main keyboard, custom cassette recorder, 8k RAM and PIA1.
- When the two were joined, they formed the Imagination Machine.
- Although the BASIC cart could be plugged into the M1000, it could not be used as it needs the main keyboard.
- BB-01 Building Block - provides 4 cart slots. Includes a RS-232 cart for a printer or modem.
- R8-K 8K RAM Expansion cart.
- FI-100 Minifloppy Disk Interface Cartridge - drives 1 or 2 D100-0 floppy drives on AS-400 bus.
- D100-0 Floppy drive 5 1/4"
- A cassette program must be loaded on the same memory size it was saved from. Since the standard machine
  had 8K, almost all tapes require this exact amount of RAM to be present
- The cart-slot is physically the same as the Arcadia 2001.


RAM switch
----------
- The M1000 only had available the 1K video ram (0000-03FF)
- Space Destroyer needs more, so it includes another 1K in the cart
- The MPA-10 base includes 8K of RAM (A000-BFFF) (-ram 8K)
- A very few games need 16K which requires hacking the pcb (-ram 16K)
- Basic will work with 8K or 16K.


The monitor
-----------
From Basic, do CALL 28672 to enter the monitor - you get a * prompt. Commands:
D nnnn  - display memory
G nnnn  - goto an address
M nnnn  - modify memory
Type the command letter, then all 4 digits, no need to hit Enter.
To exit back to Basic, do G 8894 (Basic has no prompt).


Status of cart-based games
--------------------------
backgammon - works, needs offset of 0x120, bottom line is coming from 0x3E0, should be 0x380
baseball - works, needs offset of 0x200, some bad colours
basic - works in apfimag only (as designed)
blackjack - works, some bad colours
bowling - works
boxing - works
brickdown - works
columns - runs but seems to be buggy
casino - appears to work (need instructions)
catena - works
hangman - works
pinball - works
movblock - works
rocket patrol - works, some bad colours
space destroyer - works
ufo - works


ToDo:
-----
- When pasting a large program, characters can be lost
- Some bad colours or graphics
- Tape loading is not very reliable
- Add back the disk support when we can get some info on it
  (6600, 6500-6503 wd179x disc controller? 6400, 6401)
- Need to add back the disk format in the new wdc code
  (40 tracks, 1 head, 8 sectors, 256 bytes sector length, first sector id 1)
- Need disk-based software


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

#include "emu.h"

#include "cpu/m6800/m6800.h"
#include "formats/apf_apt.h"
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "machine/6821pia.h"
#include "machine/ram.h"
#include "machine/wd_fdc.h"
#include "sound/spkrdev.h"
#include "video/mc6847.h"

#include "bus/apf/slot.h"
#include "bus/apf/rom.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class apf_state : public driver_device
{
public:
	apf_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_has_cart_ram(false)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_crtc(*this, "mc6847")
		, m_speaker(*this, "speaker")
		, m_pia0(*this, "pia0")
		, m_pia1(*this, "pia1")
		, m_cass(*this, "cassette")
		, m_cart(*this, "cartslot")
		, m_fdc(*this, "fdc")
		, m_floppy0(*this, "fdc:0")
		, m_floppy1(*this, "fdc:1")
		, m_joy(*this, "joy.%u", 0U)
		, m_key(*this, "key.%u", 0U)
		, m_p_videoram(*this, "videoram")
	{ }

	void apfm1000(machine_config &config);
	void apfimag(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	uint8_t videoram_r(offs_t offset);
	uint8_t pia0_porta_r();
	void pia0_portb_w(uint8_t data);
	void pia0_ca2_w(int state);
	uint8_t pia1_porta_r();
	uint8_t pia1_portb_r();
	void pia1_portb_w(uint8_t data);
	void apf_dischw_w(offs_t offset, uint8_t data);
	uint8_t serial_r(offs_t offset);
	void serial_w(offs_t offset, uint8_t data);

	void apfimag_map(address_map &map) ATTR_COLD;
	void apfm1000_map(address_map &map) ATTR_COLD;

	uint8_t m_latch = 0U;
	uint8_t m_keyboard_data = 0U;
	uint8_t m_pad_data = 0U;
	uint8_t m_portb = 0U;
	bool m_ca2 = 0;
	bool m_has_cart_ram = 0;
	required_device<m6800_cpu_device> m_maincpu;
	optional_device<ram_device> m_ram;
	required_device<mc6847_base_device> m_crtc;
	required_device<speaker_sound_device> m_speaker;
	required_device<pia6821_device> m_pia0;
	optional_device<pia6821_device> m_pia1;
	optional_device<cassette_image_device> m_cass;
	required_device<apf_cart_slot_device> m_cart;
	optional_device<fd1771_device> m_fdc;
	optional_device<floppy_connector> m_floppy0;
	optional_device<floppy_connector> m_floppy1;
	required_ioport_array<4> m_joy;
	optional_ioport_array<8> m_key;
	required_shared_ptr<uint8_t> m_p_videoram;
};


uint8_t apf_state::videoram_r(offs_t offset)
{
	if (BIT(m_pad_data, 7)) // AG line
	{
		// Need the cpu and crtc to be locked together for proper graphics
		// This is a hack to fix Rocket Patrol and Blackjack
		if (BIT(m_pad_data, 6) && !m_has_cart_ram)
			offset -= 0x400;

		// This is a hack to fix Space Destroyer
		if (BIT(m_pad_data, 6) && m_has_cart_ram)
			offset -= 0x120;

		uint16_t part1 = offset & 0x1f;
		uint16_t part2 = (offset & 0x1e0) >> 5;
		uint16_t part3 = (offset & 0x1e00) >> 4;
		if (m_ca2) m_latch = m_p_videoram[part3 | part1]; // get chr
		m_crtc->css_w(BIT(m_latch, 6));
		uint16_t latch = (m_latch & 0x1f) << 4;
		return m_p_videoram[latch | part2 | 0x200]; // get gfx
	}
	else
	{
		uint8_t data = m_p_videoram[(offset & 0x1ff) | 0x200];
		if (m_ca2) m_crtc->css_w(BIT(data, 6));
		m_crtc->inv_w(BIT(data, 6));
		m_crtc->as_w(BIT(data, 7));
		return data;
	}
}

uint8_t apf_state::pia0_porta_r()
{
	uint8_t data = 0xff;

	for (int i = 3; i >= 0; i--)
		if (!BIT(m_pad_data, i))
			data &= m_joy[i]->read();

	return data;
}

void apf_state::pia0_portb_w(uint8_t data)
{
	/* bit 7..6 video control */
	m_crtc->ag_w(BIT(data, 7));
	m_crtc->gm0_w(BIT(data, 6));

	/* bit 3..0 keypad line select */
	m_pad_data = data;
}

void apf_state::pia0_ca2_w(int state)
{
	m_ca2 = state;
}

uint8_t apf_state::pia1_porta_r()
{
	return m_key[m_keyboard_data]->read();
}

uint8_t apf_state::pia1_portb_r()
{
	uint8_t data = m_portb;

	if (m_cass->input() > 0.0038)
		data |= 0x80;

	return data;
}


void apf_state::pia1_portb_w(uint8_t data)
{
	/* bits 2..0 = keyboard line */
	/* bit 3 = cass audio enable */
	/* bit 4 = cassette motor */
	/* bit 5 = /cass write enable */
	/* bit 6 = cass out */
	/* bit 7 = cass in */

	m_portb = data & 0x7f;
	m_keyboard_data = data & 7;

	m_cass->change_state(BIT(data, 4) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);

	if (!BIT(data, 5))
		m_cass->output(BIT(data, 6) ? -1.0 : 1.0);
}


void apf_state::machine_start()
{
	if (m_cart->exists())
	{
		switch (m_cart->get_type())
		{
			case APF_BASIC:
				m_maincpu->space(AS_PROGRAM).install_read_handler(0x6800, 0x7fff, read8sm_delegate(*m_cart, FUNC(apf_cart_slot_device::extra_rom)));
				break;
			case APF_SPACEDST:
				m_maincpu->space(AS_PROGRAM).unmap_readwrite(0x9800, 0x9fff);
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x9800, 0x9bff, read8sm_delegate(*m_cart, FUNC(apf_cart_slot_device::read_ram)), write8sm_delegate(*m_cart, FUNC(apf_cart_slot_device::write_ram)));
				m_has_cart_ram = true;
				break;
		}

		m_cart->save_ram();
	}

	save_item(NAME(m_latch));
	save_item(NAME(m_keyboard_data));
	save_item(NAME(m_pad_data));
	save_item(NAME(m_portb));
	save_item(NAME(m_ca2));
	save_item(NAME(m_has_cart_ram));

	m_latch = 0;
}


void apf_state::machine_reset()
{
	address_space &space = m_maincpu->space(AS_PROGRAM);
	m_portb = 0;
	m_ca2 = 0;

	// apfimag only
	if (m_ram)
	{
		/* if we specified 8K of RAM, delete the extended RAM */
		if (m_ram->size() < 16*1024)
			space.unmap_readwrite(0xc000, 0xdfff);
		// this is a hack to get 'columns' to work. It misbehaves if a000-a003 are all zero
		else
			space.write_byte(0xa002, 0xe5);
	}
}

void apf_state::apf_dischw_w(offs_t offset, uint8_t data)
{
	/* bit 3 is index of drive to select */
	uint8_t drive = BIT(data, 3);

	floppy_image_device *floppy = nullptr;
	if (drive)
		floppy = m_floppy1->get_device();
	else
		floppy = m_floppy0->get_device();

	m_fdc->set_floppy(floppy);

	if (floppy)
	{
		floppy->mon_w(0);
	}

	logerror("disc w %04x %04x\n",offset,data);
}

uint8_t apf_state::serial_r(offs_t offset)
{
	logerror("serial r %04x\n",offset);
	return 0;
}

void apf_state::serial_w(offs_t offset, uint8_t data)
{
	logerror("serial w %04x %04x\n",offset,data);
}

void apf_state::apfm1000_map(address_map &map)
{
	map(0x0000, 0x03ff).mirror(0x1c00).ram().share("videoram");
	map(0x2000, 0x2003).mirror(0x1ffc).rw(m_pia0, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x4000, 0x4fff).mirror(0x1000).rom().region("roms", 0);
	map(0x6800, 0x7fff).noprw(); // BASIC accesses ROM here too, but this is installed at machine_start
	map(0x8000, 0x9fff).r(m_cart, FUNC(apf_cart_slot_device::read_rom));
	map(0xe000, 0xefff).mirror(0x1000).rom().region("roms", 0);
}

void apf_state::apfimag_map(address_map &map)
{
	apfm1000_map(map);
	map(0x6000, 0x6003).mirror(0x03fc).rw(m_pia1, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	// These need to be confirmed, disk does not work
	map(0x6400, 0x64ff).rw(FUNC(apf_state::serial_r), FUNC(apf_state::serial_w));
	map(0x6500, 0x6503).rw(m_fdc, FUNC(fd1771_device::read), FUNC(fd1771_device::write));
	map(0x6600, 0x6600).w(FUNC(apf_state::apf_dischw_w));
	map(0xa000, 0xbfff).ram(); // standard
	map(0xc000, 0xdfff).ram(); // expansion
}


/* Each controller has these features:

   1 8-way joystick
   1 big red fire button on the upper side
   12-keys keypad with the following layout

   7 8 9 0
   4 5 6 Cl
   1 2 3 En

   On the control panel of the M-1000 there are two big buttons: a Reset key and the Power switch

   Reference: http://www.nausicaa.net/~lgreenf/apfpage2.htm
*/

static INPUT_PORTS_START( apfm1000 )

/*
       This simple Basic program can be used to read the joysticks and the keyboard:

       10 PRINT KEY$(n);
       20 GOTO 10

       where n = 0, 1 or 2 - 0 = keyboard, 1,2 = joysticks #1 and #2

       When reading the keyboard KEY$(0) returns the character associated to the key, with the
       following exceptions:

       Ctrl =    CHR$(1)
       Rept =    CHR$(2)
       Here Is = CHR$(4)
       Rubout =  CHR$(8)

       When reading the joysticks, KEY$() = "N", "S", "E", "W" for the directions
                                            "0" - "9" for the keypad digits
                                            "?" for "Cl"
                                            "!" for "En"


  ? player right is player 1
*/

	PORT_START("joy.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 1/RIGHT 1") PORT_CODE(KEYCODE_1) PORT_PLAYER(2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 1/RIGHT 0") PORT_CODE(KEYCODE_0) PORT_PLAYER(2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 1/RIGHT 4") PORT_CODE(KEYCODE_4) PORT_PLAYER(2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 1/RIGHT 7") PORT_CODE(KEYCODE_7) PORT_PLAYER(2)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 2/LEFT 1") PORT_CODE(KEYCODE_1_PAD) PORT_PLAYER(1)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 2/LEFT 0") PORT_CODE(KEYCODE_0_PAD) PORT_PLAYER(1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 2/LEFT 4") PORT_CODE(KEYCODE_4_PAD) PORT_PLAYER(1)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 2/LEFT 7") PORT_CODE(KEYCODE_7_PAD) PORT_PLAYER(1)

	PORT_START("joy.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN) PORT_NAME("PAD 1/RIGHT down") PORT_PLAYER(2) PORT_8WAY
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_NAME("PAD 1/RIGHT right") PORT_PLAYER(2) PORT_8WAY
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_UP) PORT_NAME("PAD 1/RIGHT up") PORT_PLAYER(2) PORT_8WAY
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT) PORT_NAME("PAD 1/RIGHT left") PORT_PLAYER(2) PORT_8WAY
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN) PORT_NAME("PAD 2/LEFT down") PORT_PLAYER(1) PORT_8WAY
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_NAME("PAD 2/LEFT right") PORT_PLAYER(1) PORT_8WAY
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_UP) PORT_NAME("PAD 2/LEFT up") PORT_PLAYER(1) PORT_8WAY
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT) PORT_NAME("PAD 2/LEFT left") PORT_PLAYER(1) PORT_8WAY

	PORT_START("joy.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 1/RIGHT 3") PORT_CODE(KEYCODE_3) PORT_PLAYER(2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 1/RIGHT clear") PORT_CODE(KEYCODE_DEL) PORT_PLAYER(2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 1/RIGHT 6") PORT_CODE(KEYCODE_6) PORT_PLAYER(2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 1/RIGHT 9") PORT_CODE(KEYCODE_9) PORT_PLAYER(2)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 2/LEFT 3") PORT_CODE(KEYCODE_3_PAD) PORT_PLAYER(1)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 2/LEFT clear") PORT_CODE(KEYCODE_DEL_PAD) PORT_PLAYER(1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 2/LEFT 6") PORT_CODE(KEYCODE_6_PAD) PORT_PLAYER(1)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 2/LEFT 9") PORT_CODE(KEYCODE_9_PAD) PORT_PLAYER(1)

	PORT_START("joy.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 1/RIGHT 2") PORT_CODE(KEYCODE_2) PORT_PLAYER(2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 1/RIGHT enter/fire") PORT_CODE(KEYCODE_ENTER) PORT_PLAYER(2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 1/RIGHT 5") PORT_CODE(KEYCODE_5) PORT_PLAYER(2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 1/RIGHT 8") PORT_CODE(KEYCODE_8) PORT_PLAYER(2)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 2/LEFT 2") PORT_CODE(KEYCODE_2_PAD) PORT_PLAYER(1)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 2/LEFT enter/fire") PORT_CODE(KEYCODE_ENTER_PAD) PORT_PLAYER(1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 2/LEFT 5") PORT_CODE(KEYCODE_5_PAD) PORT_PLAYER(1)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAD 2/LEFT 8") PORT_CODE(KEYCODE_8_PAD) PORT_PLAYER(1)
INPUT_PORTS_END


static INPUT_PORTS_START( apfimag )

	PORT_INCLUDE( apfm1000 )

	/* Reference: http://www.nausicaa.net/~lgreenf/apfpage2.htm */

	PORT_START("key.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X")               PORT_CODE(KEYCODE_X)          PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z")               PORT_CODE(KEYCODE_Z)          PORT_CHAR('Z')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q       IF")      PORT_CODE(KEYCODE_Q)          PORT_CHAR('Q')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2   \"    LET")   PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A")               PORT_CODE(KEYCODE_A)          PORT_CHAR('A')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1   !   GOSUB")   PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W       STEP")    PORT_CODE(KEYCODE_W)          PORT_CHAR('W')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S")               PORT_CODE(KEYCODE_S)          PORT_CHAR('S')

	PORT_START("key.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C")               PORT_CODE(KEYCODE_C)          PORT_CHAR('C')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V")               PORT_CODE(KEYCODE_V)          PORT_CHAR('V')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R       READ")    PORT_CODE(KEYCODE_R)          PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3   #   DATA")    PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F")               PORT_CODE(KEYCODE_F)          PORT_CHAR('F')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4   $   INPUT")   PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E       STOP")    PORT_CODE(KEYCODE_E)          PORT_CHAR('E')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D")               PORT_CODE(KEYCODE_D)          PORT_CHAR('D')

	PORT_START("key.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N   ^")           PORT_CODE(KEYCODE_N)          PORT_CHAR('N') PORT_CHAR('^')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B")               PORT_CODE(KEYCODE_B)          PORT_CHAR('B')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T       NEXT")    PORT_CODE(KEYCODE_T)          PORT_CHAR('T')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6   &   FOR")     PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G")               PORT_CODE(KEYCODE_G)          PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5   %   DIM")     PORT_CODE(KEYCODE_5)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y       PRINT")   PORT_CODE(KEYCODE_Y)          PORT_CHAR('Y')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H")               PORT_CODE(KEYCODE_H)          PORT_CHAR('H')

	PORT_START("key.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M   ]")           PORT_CODE(KEYCODE_M)          PORT_CHAR('M') PORT_CHAR(']')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",   <")           PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I       LIST")    PORT_CODE(KEYCODE_I)          PORT_CHAR('I')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7   '   RETURN")  PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K   [")           PORT_CODE(KEYCODE_K)          PORT_CHAR('K') PORT_CHAR('[')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8   (   THEN")    PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U       END")     PORT_CODE(KEYCODE_U)          PORT_CHAR('U')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J")               PORT_CODE(KEYCODE_J)          PORT_CHAR('J')

	PORT_START("key.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/   ?")           PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".   >")           PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O   _   REM")     PORT_CODE(KEYCODE_O)          PORT_CHAR('O') PORT_CHAR('_')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0       GOTO")    PORT_CODE(KEYCODE_0)          PORT_CHAR('0')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L   \\")          PORT_CODE(KEYCODE_L)          PORT_CHAR('L') PORT_CHAR('\\')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9   )   ON")      PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P   @   USING")   PORT_CODE(KEYCODE_P)          PORT_CHAR('P') PORT_CHAR('@')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";   +")           PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR('+')

	PORT_START("key.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space")           PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(32)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(":   *")           PORT_CODE(KEYCODE_MINUS)      PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return")          PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(13)
	PORT_BIT(0x08, 0x08, IPT_UNUSED)
	PORT_BIT(0x10, 0x10, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-   =   RESTORE") PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Line Feed")       PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR(10)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Rubout")          PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(8)

	PORT_START("key.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift")     PORT_CODE(KEYCODE_RSHIFT) PORT_CODE(KEYCODE_LSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc")             PORT_CODE(KEYCODE_TAB)        PORT_CHAR(27)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl")            PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Rept")            PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(UCHAR_MAMEKEY(TAB))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Break")           PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Here Is")         PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x40, 0x40, IPT_UNUSED) // another X
	PORT_BIT(0x80, 0x80, IPT_UNUSED) // another Z

	PORT_START("key.7")
	PORT_BIT(0xff, 0xff, IPT_UNUSED)
INPUT_PORTS_END


static void apf_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_SSDD);
}


static void apf_cart(device_slot_interface &device)
{
	device.option_add_internal("std",       APF_ROM_STD);
	device.option_add_internal("basic",     APF_ROM_BASIC);
	device.option_add_internal("spacedst",  APF_ROM_SPACEDST);
}


void apf_state::apfm1000(machine_config &config)
{
	/* basic machine hardware */
	M6800(config, m_maincpu, 3.579545_MHz_XTAL / 4);  // divided by 4 in external clock circuit
	m_maincpu->set_addrmap(AS_PROGRAM, &apf_state::apfm1000_map);

	/* video hardware */
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	MC6847(config, m_crtc, 3.579545_MHz_XTAL);
	m_crtc->fsync_wr_callback().set(m_pia0, FUNC(pia6821_device::cb1_w));
	m_crtc->input_callback().set(FUNC(apf_state::videoram_r));
	m_crtc->set_get_fixed_mode(mc6847_device::MODE_GM2 | mc6847_device::MODE_GM1);
	m_crtc->set_screen("screen");
	// INTEXT = GND
	// other lines not connected

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* Devices */
	PIA6821(config, m_pia0);
	m_pia0->readpa_handler().set(FUNC(apf_state::pia0_porta_r));
	m_pia0->writepb_handler().set(FUNC(apf_state::pia0_portb_w));
	m_pia0->ca2_handler().set(FUNC(apf_state::pia0_ca2_w));
	m_pia0->cb2_handler().set(m_speaker, FUNC(speaker_sound_device::level_w));
	m_pia0->irqa_handler().set_inputline(m_maincpu, M6800_IRQ_LINE);
	m_pia0->irqb_handler().set_inputline(m_maincpu, M6800_IRQ_LINE);

	APF_CART_SLOT(config, m_cart, apf_cart, nullptr);

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("apfm1000");
}

void apf_state::apfimag(machine_config &config)
{
	apfm1000(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &apf_state::apfimag_map);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("8K").set_extra_options("16K");

	PIA6821(config, m_pia1);
	m_pia1->readpa_handler().set(FUNC(apf_state::pia1_porta_r));
	m_pia1->readpb_handler().set(FUNC(apf_state::pia1_portb_r));
	m_pia1->writepb_handler().set(FUNC(apf_state::pia1_portb_w));

	CASSETTE(config, m_cass);
	m_cass->set_formats(apf_cassette_formats);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_DISABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.15);
	m_cass->set_interface("apf_cass");

	FD1771(config, m_fdc, 1000000); // guess
	FLOPPY_CONNECTOR(config, "fdc:0", apf_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", apf_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	SOFTWARE_LIST(config, "cass_list").set_original("apfimag_cass");
}


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

  Game driver(s)

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

ROM_START(apfm1000)
	ROM_REGION(0x1000,"roms", 0)
	ROM_SYSTEM_BIOS( 0, "0", "Standard" )
	ROMX_LOAD("apf_4000.rom", 0x0000, 0x0800, CRC(cc6ac840) SHA1(1110a234bcad99bd0894ad44c591389d16376ca4), ROM_BIOS(0) )
	ROM_RELOAD(0x0800, 0x0800)

	ROM_SYSTEM_BIOS( 1, "trash", "Trash II" ) // In Rocket Patrol, the ships are replaced by garbage trucks
	ROMX_LOAD("trash-ii.bin", 0x0000, 0x1000, CRC(3bd8640a) SHA1(da4cd8163990adbc5acd3eab604b41e1066bb832), ROM_BIOS(1) )

	ROM_SYSTEM_BIOS( 2, "mod", "Mod BIOS" ) // (c) 1982 W.Lunquist - In Basic, CALL 18450 to get a machine-language monitor
	ROMX_LOAD("mod_bios.bin", 0x0000, 0x1000, CRC(f320aba6) SHA1(9442349fca8b001a5765e2fe8b84db4ece7886c1), ROM_BIOS(2) )
ROM_END

#define rom_apfimag rom_apfm1000

// old rom, has a bad byte at 0087.
//ROMX_LOAD("apf_4000.rom", 0x0000, 0x0800, CRC(2a331a33) SHA1(387b90882cd0b66c192d9cbaa3bec250f897e4f1), ROM_BIOS(0) )

} // Anonymous namespace


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

  Game driver(s)

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

//    YEAR  NAME      PARENT    COMPAT  MACHINE   INPUT     CLASS      INIT        COMPANY                 FULLNAME
COMP( 1979, apfimag,  apfm1000, 0,      apfimag,  apfimag,  apf_state, empty_init, "APF Electronics Inc.", "APF Imagination Machine", MACHINE_SUPPORTS_SAVE )
CONS( 1978, apfm1000, 0,        0,      apfm1000, apfm1000, apf_state, empty_init, "APF Electronics Inc.", "APF M-1000", MACHINE_SUPPORTS_SAVE )



apogee.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

        Apogee BK-01 driver by Miodrag Milanovic

        05/06/2008 Preliminary driver.

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


#include "emu.h"
#include "radio86.h"

#include "cpu/i8085/i8085.h"
#include "machine/pit8253.h"
#include "sound/spkrdev.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/rk_cas.h"


namespace {

class apogee_state : public radio86_state
{
public:
	apogee_state(const machine_config &mconfig, device_type type, const char *tag)
		: radio86_state(mconfig, type, tag)
		, m_speaker(*this, "speaker")
	{ }

	void apogee(machine_config &config);

private:
	uint8_t m_out0 = 0U;
	uint8_t m_out1 = 0U;
	uint8_t m_out2 = 0U;
	void pit8253_out0_changed(int state);
	void pit8253_out1_changed(int state);
	void pit8253_out2_changed(int state);
	I8275_DRAW_CHARACTER_MEMBER(display_pixels);

	required_device<speaker_sound_device> m_speaker;
	void mem_map(address_map &map) ATTR_COLD;
	void machine_reset() override ATTR_COLD;
	void machine_start() override ATTR_COLD;
};


/* Address maps */
void apogee_state::mem_map(address_map &map)
{
	map(0x0000, 0xebff).ram().share("mainram");
	map(0xec00, 0xec03).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write)).mirror(0x00fc);
	map(0xed00, 0xed03).rw(m_ppi1, FUNC(i8255_device::read), FUNC(i8255_device::write)).mirror(0x00fc);
	//map(0xee00, 0xee03).rw(m_ppi2, FUNC(i8255_device::read), FUNC(i8255_device::write)).mirror(0x00fc);
	map(0xef00, 0xef01).rw("crtc", FUNC(i8275_device::read), FUNC(i8275_device::write)).mirror(0x00fe); // video
	map(0xf000, 0xf0ff).w(m_dma, FUNC(i8257_device::write));    // DMA
	map(0xf000, 0xffff).rom().region("maincpu",0);  // System ROM
}

/* Input ports */
static INPUT_PORTS_START( apogee )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PgUp") PORT_CODE(KEYCODE_PGUP)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del") PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ins") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PgDn") PORT_CODE(KEYCODE_PGDN)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Back") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR(0xA4)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')

	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')

	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')

	PORT_START("LINE7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('X')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('^')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("LINE8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Rus/Lat") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
INPUT_PORTS_END

static const double speaker_levels[] = {-1.0, -0.33333, 0.33333, 1.0};

void apogee_state::pit8253_out0_changed(int state)
{
	m_out0 = state;
	m_speaker->level_w(m_out0+m_out1+m_out2);
}

void apogee_state::pit8253_out1_changed(int state)
{
	m_out1 = state;
	m_speaker->level_w(m_out0+m_out1+m_out2);
}

void apogee_state::pit8253_out2_changed(int state)
{
	m_out2 = state;
	m_speaker->level_w(m_out0+m_out1+m_out2);
}

void apogee_state::machine_reset()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x0000, 0x0fff, m_rom+0x0800);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0xf000, 0xffff,
			"rom_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// reinstall RAM over the ROM shadow
					m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x0fff, m_ram);
				}
			},
			&m_rom_shadow_tap);
}

void apogee_state::machine_start()
{
	save_item(NAME(m_tape_value));
	save_item(NAME(m_out0));
	save_item(NAME(m_out1));
	save_item(NAME(m_out2));
}

I8275_DRAW_CHARACTER_MEMBER(apogee_state::display_pixels)
{
	using namespace i8275_attributes;
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	uint8_t const *const charmap = &m_chargen[BIT(attrcode, GPA0) ? 0x400 : 0];
	uint8_t pixels = charmap[(linecount & 7) + (charcode << 3)] ^ 0xff;
	if (BIT(attrcode, VSP))
		pixels = 0;

	if (BIT(attrcode, LTEN))
		pixels = 0xff;

	if (BIT(attrcode, RVV))
		pixels ^= 0xff;

	bool hlgt = BIT(attrcode, HLGT);
	for(int i=0;i<6;i++)
		bitmap.pix(y, x + i) = palette[(pixels >> (5-i)) & 1 ? (hlgt ? 2 : 1) : 0];
}

/* F4 Character Displayer */
static const gfx_layout apogee_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_apogee )
	GFXDECODE_ENTRY( "chargen", 0x0000, apogee_charlayout, 0, 1 )
GFXDECODE_END


/* Machine driver */
void apogee_state::apogee(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, XTAL(16'000'000) / 9);
	m_maincpu->set_addrmap(AS_PROGRAM, &apogee_state::mem_map);

	pit8253_device &pit(PIT8253(config, "pit", 0));
	pit.set_clk<0>(XTAL(16'000'000)/9);
	pit.out_handler<0>().set(FUNC(apogee_state::pit8253_out0_changed));
	pit.set_clk<1>(XTAL(16'000'000)/9);
	pit.out_handler<1>().set(FUNC(apogee_state::pit8253_out1_changed));
	pit.set_clk<2>(XTAL(16'000'000)/9);
	pit.out_handler<2>().set(FUNC(apogee_state::pit8253_out2_changed));

	I8255(config, m_ppi1);
	m_ppi1->out_pa_callback().set(FUNC(apogee_state::radio86_8255_porta_w2));
	m_ppi1->in_pb_callback().set(FUNC(apogee_state::radio86_8255_portb_r2));
	m_ppi1->in_pc_callback().set(FUNC(apogee_state::radio86_8255_portc_r2));
	m_ppi1->out_pc_callback().set(FUNC(apogee_state::radio86_8255_portc_w2));

	//I8255(config, m_ppi2);

	i8275_device &i8275(I8275(config, "crtc", XTAL(16'000'000) / 12));
	i8275.set_character_width(6);
	i8275.set_display_callback(FUNC(apogee_state::display_pixels));
	i8275.drq_wr_callback().set(m_dma, FUNC(i8257_device::dreq2_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_screen_update("crtc", FUNC(i8275_device::screen_update));
	screen.set_refresh_hz(50);
	screen.set_size(78*6, 30*10);
	screen.set_visarea(0, 78*6-1, 0, 30*10-1);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_apogee);
	PALETTE(config, m_palette, FUNC(apogee_state::radio86_palette), 3);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.75);

	I8257(config, m_dma, XTAL(16'000'000) / 9);
	m_dma->out_hrq_cb().set(FUNC(apogee_state::hrq_w));
	m_dma->in_memr_cb().set(FUNC(apogee_state::memory_read_byte));
	m_dma->out_memw_cb().set(FUNC(apogee_state::memory_write_byte));
	m_dma->out_iow_cb<2>().set("crtc", FUNC(i8275_device::dack_w));
	m_dma->set_reverse_rw_mode(1);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_formats(rka_cassette_formats);
	m_cassette->set_interface("apogee_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("apogee");
}

/* ROM definition */
ROM_START( apogee )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "apogee.rom", 0x0000, 0x1000, CRC(a47383a7) SHA1(6a868371c7980f92c2fc9ced921517209f197375))

	ROM_REGION(0x0800, "chargen",0)
	ROM_LOAD ("apogee.fnt", 0x0000, 0x0800, CRC(fe5867f0) SHA1(82c5aca63ada5e4533eb0516384aaa7b77a1f8e2))
ROM_END

} // anonymous namespace

/* Driver */

//    YEAR  NAME    PARENT   COMPAT  MACHINE  INPUT   CLASS         INIT          COMPANY      FULLNAME        FLAGS
COMP( 1989, apogee, radio86, 0,      apogee,  apogee, apogee_state, init_radio86, "Zavod BRA", "Apogee BK-01", MACHINE_SUPPORTS_SAVE )



apollo.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Hans Ostermeyer, R. Belmont
/*
 * apollo.c - APOLLO DN3500/DN3000 driver
 *
 *  Created on: May 12, 2010
 *      Author: Hans Ostermeyer
 *
 *  Adapted February 19, 2012 for general MAME/MESS standards by R. Belmont
 *
 *  TODO: Remove need for instruction hook.
 *        Convert to modern address map.
 *
 *  see also:
 *  - Domain Series 3000/Series 4000 Hardware Architecture Handbook (Order No. 007861 Rev. 02)
 *  - Domain Personal Workstations and Servers Technical Reference (Apollo Order No. 008778-A01)
 *  - Servicing the Domain Personal Workstations and Servers (Apollo Order No. 007859-A01)
 *  - http://www.bitsavers.org/pdf/apollo/002398-04_Domain_Engineering_Handbook_Rev4_Jan87.pdf
 *  - http://www.bitsavers.org/pdf/apollo/008778-03_DOMAIN_Series_3000_4000_Technical_Reference_Aug87.pdf
 *  - http://www.bitsavers.org/pdf/apollo/AEGIS_Internals_and_Data_Structures_Jan86.pdf
 *  - http://www.bitsavers.org/pdf/apollo/019411-A00_Addendum_to_Domain_Personal_Workstations_and_Servers_Hardware_Architecture_Handbook_1991.pdf
 *  - data sheets from Intel and Motorola
 */

#include "emu.h"

#define VERBOSE 0

#include "apollo.h"

#include "cpu/m68000/m68000.h"

// we use set_verbose
#include "bus/isa/omti8621.h"
#include "bus/isa/3c505.h"

#include "debugger.h"

#include "apollo_dsp.lh"


#define TERMINAL_TAG "terminal"

// we use this to prevent excessive logging (if emulation runs amok)
// error.log will be 10 MB for 100000 lines
#define APOLLO_MAX_NO_OF_LOG_LINES 1000000

// ISA/AT Bus notes
// I/O space: to get the Apollo address = take the PC I/O address, keep the low 3 bits how they are, and shift the rest left 7, inserting zeros.
// then add 0x40000 for the I/O base.
//
// example: 3c503 Ethernet is at I/O 300h on PC, which is (%1100000000 -> 1 1000 0000 0000 0000) + 0x40000 = 0x58000
//
// Memory space: addresses from 0x80000 to 0xffffff are supported, including the possibility of stock PC MDA at a0000

#define ATBUS_IO_BASE       0x040000
#define ATBUS_IO_END        0x05ffff
#define ATBUS_MEMORY_BASE   0x080000
#define ATBUS_MEMORY_END    0xffffff

#define DN3500_RAM_SIZE     16 // 8, 16 or 32 MB

#if DN3500_RAM_SIZE == 8
#define DN3500_RAM_BASE     0x1000000
#define DN3500_RAM_END      0x17fffff
#define DN3500_RAM_CONFIG_BYTE  0x64 // 4-4-0-0
#elif DN3500_RAM_SIZE == 16
#define DN3500_RAM_BASE     0x1000000
#define DN3500_RAM_END      0x1ffffff
#define DN3500_RAM_CONFIG_BYTE 0x60 // 4-4-4-4
#else /* DN3500_RAM_SIZE == 32 */
#define DN3500_RAM_BASE     0x1000000
#define DN3500_RAM_END      0x3ffffff
#define DN3500_RAM_CONFIG_BYTE 0x20 // 8-8-8-8
#endif

#define DN3000_RAM_BASE     0x100000
#define DN3000_RAM_END      0x8fffff
#define DN3000_RAM_CONFIG_8MB  0x20 // 2-2-2-2

#define DN5500_RAM_SIZE     32 // 16 or 32 MB

#if DN5500_RAM_SIZE == 16
#define DN5500_RAM_BASE     0x1000000
#define DN5500_RAM_END      0x1ffffff
#define DN5500_RAM_CONFIG_BYTE  0x14 // 8-8-0-0
#define DN5500_MEM_PRESENT_BYTE 0xAA // 8-8-0-0
#else /* DN5500_RAM_SIZE == 32 */
#define DN5500_RAM_BASE     0x1000000
#define DN5500_RAM_END      0x2ffffff
#define DN5500_RAM_CONFIG_BYTE  0x20 // 8-8-8-8
#define DN5500_MEM_PRESENT_BYTE 0x00 // 8-8-8-8
#endif

#define NODE_TYPE_DN3000 3000
#define NODE_TYPE_DN3500 3500
#define NODE_TYPE_DN5500 5500
#define NODE_TYPE_DSP3000 -3000
#define NODE_TYPE_DSP3500 -3500
#define NODE_TYPE_DSP5500 -5500

#define DEFAULT_NODE_ID 0x12345

static uint8_t cache_control_register = 0x00;
static uint8_t cache_status_register = 0xff;
static uint8_t task_alias_register = 0x00;

static offs_t parity_error_offset = 0;
static uint16_t parity_error_byte_mask = 0;
static int parity_error_handler_is_installed = 0;
static int parity_error_handler_install_counter = 0;

static uint16_t latch_page_on_parity_error_register = 0x0000;
static uint16_t master_req_register = 0x0000;

static uint32_t ram_base_address;
static uint32_t ram_end_address;

static int node_type;

// FIXME: value of ram_config_byte must match with default/selected RAM size
static uint8_t ram_config_byte;

static uint32_t log_line_counter = 0;

/***************************************************************************
 cpu_context - return a string describing which CPU is currently executing and their PC
 ***************************************************************************/

std::string apollo_cpu_context(running_machine &machine) {
	osd_ticks_t t = osd_ticks();
	int s = (t / osd_ticks_per_second()) % 3600;
	int ms = (t / (osd_ticks_per_second() / 1000)) % 1000;

	return util::string_format("%s %d.%03d", machine.describe_context().c_str(), s, ms);
}

/*-------------------------------------------------
 apollo_set_cpu_has_fpu - enable/disable the FPU
 -------------------------------------------------*/

void apollo_set_cpu_has_fpu(m68000_musashi_device *device, int onoff)
{
	if (device == nullptr || (device->type() != M68020PMMU && device->type() != M68030))
	{
		DLOG1(("set_cpu_has_fpu: unexpected CPU device"));
	}
	else
	{
		device->set_fpu_enable(onoff);
		DLOG1(("apollo_set_cpu_has_fpu: FPU has been %s", onoff ? "enabled" : "disabled"));
	}
}

/***************************************************************************
 apollo_check_log - check for excessive logging
 ***************************************************************************/

void apollo_check_log() {
	if (++log_line_counter >= APOLLO_MAX_NO_OF_LOG_LINES) {
		fatalerror("apollo_check_log: maximum number of log lines exceeded.\n");
	}
}

/***************************************************************************
 apollo_is_dn3000 - return 1 if node is DN3000 or DSP3000, 0 otherwise
 ***************************************************************************/

int apollo_is_dn3000(void) {
	return node_type == NODE_TYPE_DN3000 || node_type == NODE_TYPE_DSP3000;
}

/***************************************************************************
 apollo_is_dn5500 - return 1 if node is DN5500 or DSP5500, 0 otherwise
 ***************************************************************************/

int apollo_is_dn5500(void) {
	return node_type == NODE_TYPE_DN5500 || node_type == NODE_TYPE_DSP5500;
}

/***************************************************************************
 apollo_is_dsp3x00 - return 1 if node is DSP3x00 or DSP5500, 0 otherwise
 ***************************************************************************/

int apollo_is_dsp3x00(void) {
	switch (node_type)
	{
	case NODE_TYPE_DSP3000:
	case NODE_TYPE_DSP3500:
	case NODE_TYPE_DSP5500:
		return 1;
	}
	return 0;
}

/***************************************************************************
 apollo_get_ram_config_byte - get the ram configuration byte
 ***************************************************************************/

uint8_t apollo_get_ram_config_byte(void) {
	return ram_config_byte;
}

#if 0
/***************************************************************************
  apollo_instruction_hook
  must be called by the CPU core before executing each instruction
***************************************************************************/
uint32_t apollo_state::apollo_instruction_hook(offs_t offset)
{
	static uint16_t idle_counter = 0;

	// m_maincpu->ir still has previous instruction
	uint16_t last_ir = m_maincpu->ir;

	// get next instruction (or 0 if unavailable)
	uint16_t next_ir = (m_maincpu->pref_addr == REG_PC(m_maincpu)) ? m_maincpu->pref_data : 0;

	// check for NULLPROC:
	// 027C F8FF AND.W #F8FF,SR
	// 60FA      BRA *-4

	if ((next_ir == 0x60fa && last_ir == 0x027c) || (next_ir == 0x027c  && last_ir == 0x60fa))
	{
		// we are within the idle loop, slow down CPU to reduce power usage
		m_maincpu->remaining_cycles -= 500;

		if (apollo_config(APOLLO_CONF_IDLE_SLEEP) && apollo_is_dsp3x00() && ++idle_counter >= 1000)
		{
			// slow down even more on DSP3x00
			idle_counter -= 100;
			// sleep 1 ms
			osd_sleep(osd_ticks_per_second() / 1000);
		}
	}
	else
	{
		// we are outside of the idle loop
		idle_counter = 0;
	}

	if (!m_maincpu->get_fpu_enable() && !m_maincpu->pmmu_enabled && (m_maincpu->ir & 0xff00) == 0xf200)
	{
		// set APOLLO_CSR_SR_FP_TRAP in cpu status register for /sau7/self_test
		apollo_csr_set_status_register(APOLLO_CSR_SR_FP_TRAP, APOLLO_CSR_SR_FP_TRAP);
	}

	if (m_maincpu->t1_flag && !m_maincpu->s_flag)
	{
		// FIXME: trace emulation is disabled in m68kcpu.h; why???
		m68ki_exception_trace(m_maincpu);
	}

	return apollo_debug_instruction_hook(m_maincpu, offset);
}

#endif

/***************************************************************************
 apollo bus error
 ***************************************************************************/

void apollo_state::apollo_bus_error()
{
	m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
	m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);

	apollo_csr_set_status_register(APOLLO_CSR_SR_CPU_TIMEOUT, APOLLO_CSR_SR_CPU_TIMEOUT);
}

void apollo_state::cpu_space_map(address_map &map)
{
	map(0xfffffff2, 0xffffffff).r(FUNC(apollo_state::apollo_irq_acknowledge));
}

u16 apollo_state::apollo_irq_acknowledge(offs_t offset)
{
	m_maincpu->set_input_line(offset+1, CLEAR_LINE);

	MLOG2(("apollo_irq_acknowledge: interrupt level=%d", offset+1));

	if (offset+1 == 6)
		return apollo_pic_get_vector();
	else
		return m68000_base_device::autovector(offset+1);
}

/***************************************************************************
 DN3500 Cache Control/Status Register at 0x10200
 ***************************************************************************/

void apollo_state::cache_control_register_w(offs_t offset, uint8_t data){
	if (apollo_is_dn5500())
	{
		SLOG1(("Error: writing DN5500 Cache Status Register at offset %02x = %02x", offset, data));
	}
	else
	{
		cache_control_register = data;
		cache_status_register = (cache_status_register & 0x7f) | (cache_control_register & 0x80);
		SLOG2(("writing Cache Control Register at offset %02x = %02x", offset, data));
	}
}

uint8_t apollo_state::cache_status_register_r(offs_t offset){
	uint8_t data = cache_status_register;

	if (apollo_is_dn5500()) {
#define DN5500_CSR_NOT_HSI_PRESENT 8
#define DN5500_CSR_MEM_TIME 1
		data |= DN5500_CSR_NOT_HSI_PRESENT;
	}

	SLOG2(("reading Cache Status Register at offset %02x = %02x", offset, data));
	return data;
}

void apollo_set_cache_status_register(device_t *device,uint8_t mask, uint8_t data) {
	uint16_t new_value = (cache_status_register & ~mask) | (data & mask);
	if (new_value != cache_status_register) {
		cache_status_register = new_value;
		DLOG2(("setting Cache Status Register with data=%02x and mask=%02x to %02x",
				data, mask, cache_status_register));
	}
}

/***************************************************************************
 DN3500 Task Alias Register at 0x10300
 ***************************************************************************/

void apollo_state::task_alias_register_w(offs_t offset, uint8_t data){
	task_alias_register = data;
	apollo_set_cache_status_register(this,0x07,  data);
	SLOG(("writing Task Alias Register at offset %02x = %02x",offset, data));
}

uint8_t apollo_state::task_alias_register_r(offs_t offset){
	uint8_t data = 0xff;
	SLOG(("reading Task Alias Register at offset %02x = %02x", offset, data));
	return data;
}

/***************************************************************************
 DN3000/DN3500 Latch Page on Parity Error Register at 0x9300/0x11300
 ***************************************************************************/

void apollo_state::latch_page_on_parity_error_register_w(offs_t offset, uint16_t data){
	latch_page_on_parity_error_register = data;
	SLOG1(("writing Latch Page on Error Parity Register at offset %02x = %04x", offset*2, data));
}

uint16_t apollo_state::latch_page_on_parity_error_register_r(offs_t offset){
	uint16_t data = latch_page_on_parity_error_register;
	SLOG2(("reading Latch Page on Error Parity Register at offset %02x = %04x", offset*2, data));
	return data;
}

/***************************************************************************
 DN3500 Master REQ Register at 0x11600
 ***************************************************************************/

void apollo_state::master_req_register_w(offs_t offset, uint8_t data){
	master_req_register = data;
	SLOG2(("writing Master REQ Register at offset %02x = %02x", offset, data));
}

uint8_t apollo_state::master_req_register_r(offs_t offset){
	uint8_t data = 0xff;
	SLOG1(("reading Master REQ Register at offset %02x = %02x", offset, data));
	return data;
}

/***************************************************************************
 DN3500 Selective Clear Locations at 0x11600
 ***************************************************************************/

void apollo_state::selective_clear_locations_w(offs_t offset, uint16_t data){
	SLOG2(("writing Selective Clear Locations at offset %02x = %02x", offset*2, data));
	switch (offset * 2) {
	case 0x00: // Clear All
		apollo_csr_set_status_register(APOLLO_CSR_SR_CLEAR_ALL, 0);
		break;
	case 0x04: // clear floating-point trap
		apollo_csr_set_status_register(APOLLO_CSR_SR_FP_TRAP, 0);
		break;
	case 0x06: // clear Parity error interrupt
		apollo_csr_set_status_register(APOLLO_CSR_SR_PARITY_BYTE_MASK, 0);
		break;
	case 0x08: // clear Bus Error Status (CPU Timeout)
		apollo_csr_set_status_register(APOLLO_CSR_SR_CPU_TIMEOUT, 0);
		break;
	case 0x0e: // Clear (Flush) Cache
		break;
	}
}

uint16_t apollo_state::selective_clear_locations_r(offs_t offset){
	uint16_t data = 0xffff;
	SLOG1(("reading Selective Clear Locations at offset %02x = %02x", offset*2, data));
	return data;
}

/***************************************************************************
 DN3000/DN3500 RAM with parity (and null proc loop delay for DomainOS)
 ***************************************************************************/

uint32_t apollo_state::ram_with_parity_r(offs_t offset, uint32_t mem_mask){
	uint32_t data = m_messram_ptr[parity_error_offset+offset];

	SLOG2(("memory dword read with parity error at %08x = %08x & %08x parity_byte=%04x",
			ram_base_address + parity_error_offset*4 + offset*4,data, mem_mask, parity_error_byte_mask));

	if (parity_error_byte_mask != 0) {
		latch_page_on_parity_error_register = (ram_base_address + parity_error_offset * 4) >> 10;

		apollo_csr_set_status_register(APOLLO_CSR_CR_PARITY_BYTE_MASK,  apollo_csr_get_status_register() |parity_error_byte_mask);

		if (apollo_csr_get_control_register() & APOLLO_CSR_CR_INTERRUPT_ENABLE) {
			// force parity error (if NMI is enabled)
			m_maincpu->set_input_line(7, ASSERT_LINE);

		}
	}
	return data;
}

void apollo_state::ram_with_parity_w(offs_t offset, uint32_t data, uint32_t mem_mask){
	COMBINE_DATA(m_messram_ptr+offset);

	if (apollo_csr_get_control_register() & APOLLO_CSR_CR_FORCE_BAD_PARITY) {
		parity_error_byte_mask = (apollo_csr_get_control_register()
				& APOLLO_CSR_CR_PARITY_BYTE_MASK);

		if (!apollo_is_dn3000()) {
			parity_error_byte_mask ^= APOLLO_CSR_CR_PARITY_BYTE_MASK;
		}

		parity_error_offset = offset;

//      SLOG1(("memory dword write with parity to %08x = %08x & %08x parity_byte=%04x",
//              ram_base_address +offset * 4, data, mem_mask, parity_error_byte_mask));

		if (parity_error_handler_is_installed == 0) {
			// no more than 192 read/write handlers may be used
			// see table_assign_handler in memory.c
			if (parity_error_handler_install_counter < 40) {
				m_maincpu->space(AS_PROGRAM).install_read_handler(ram_base_address+offset*4, ram_base_address+offset*4+3, read32s_delegate(*this, FUNC(apollo_state::ram_with_parity_r)));
				parity_error_handler_is_installed = 1;
				parity_error_handler_install_counter++;
			}
		}
	} else if (parity_error_handler_is_installed && offset == parity_error_offset) {
		SLOG1(("memory dword write with parity to %08x = %08x & %08x reset %d",
				ram_base_address +parity_error_offset*4, data, mem_mask, parity_error_handler_install_counter));

		// uninstall not supported, reinstall previous read handler instead

		// memory_install_rom(space, ram_base_address, ram_end_address, messram_ptr.v);
		m_maincpu->space(AS_PROGRAM).install_rom(ram_base_address,ram_end_address,&m_messram_ptr[0]);

		parity_error_handler_is_installed = 0;
		parity_error_byte_mask = 0;
	}
}

/***************************************************************************
 DN3000/DN3500 unmapped memory
 ***************************************************************************/

uint32_t apollo_state::apollo_unmapped_r(offs_t offset, uint32_t mem_mask)
{
	offs_t address = offset * 4;

	m68000_base_device *m68k = m_maincpu;

	if ((address & 0xfff00000) == 0xfa800000 && VERBOSE < 2) {
		// ?
	} else if ((address & 0xfff00ff7) == 0xfd800000 && VERBOSE < 2) {
		// omit logging for memory sizing in FPA address space
		// strange: MD seems to search for the 3C505 Boot ROM
		// note (6.10.2010): might be color7 address space (!?!)
	} else if ((address & 0xfc03ffff) == 0x00000000 && VERBOSE < 2) {
		// omit logging for memory sizing in standalone utilities
	} else if (address == 0xfff90000 && VERBOSE < 2) {
		// omit logging for FPA trial access
	} else if (address == 0x00030000 && VERBOSE < 2) {
		// omit logging for Bus error test address in DN3500 boot prom and self_test
	} else if (address == 0x0000ac00 && VERBOSE < 2) {
		// omit logging for Bus error test address in DN3000 boot prom
	} else {
		SLOG1(("unmapped memory dword read from %08x with mask %08x (ir=%04x)", address , mem_mask, m68k->state_int(M68K_IR)));
	}

	/* unmapped; access causes a bus error */
	apollo_bus_error();
	return 0xffffffff;
}

void apollo_state::apollo_unmapped_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	SLOG(("unmapped memory dword write to %08x = %08x & %08x", offset * 4, data, mem_mask));

	/* unmapped; access causes a bus error */
	apollo_bus_error();
}

/***************************************************************************
 DN3000/DN3500 ROM write
 ***************************************************************************/

void apollo_state::apollo_rom_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	offs_t address =  offset * 4;
	offs_t pc = m_maincpu->pcbase();

	if (pc == 0x00002c1c && address == 0x00000004 && VERBOSE < 2) {
		// don't log invalid code in 3500_boot_12191_7.bin
	} else {
		SLOG1(("ROM dword write to %08x = %08x & %08x", offset * 4, data, mem_mask));
	}
}

/***************************************************************************
 DN3000/DN3500 AT Bus I/O space
 ***************************************************************************/

uint16_t apollo_state::apollo_atbus_io_r(offs_t offset, uint16_t mem_mask)
{
	uint32_t isa_addr = (offset & 3) + ((offset & ~0x1ff) >> 7);

	// Motorola CPU is MSB first, ISA Bus is LSB first
	uint16_t data = m_isa->io16_swap_r(isa_addr, mem_mask);

	SLOG2(("apollo_atbus_io_r at %08x -> %04x = %04x & %04x", ATBUS_IO_BASE + offset*2, isa_addr*2, data, mem_mask));

	return data;
}

void apollo_state::apollo_atbus_io_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	uint32_t isa_addr = (offset & 3) + ((offset & ~0x1ff) >> 7);

	SLOG2(("apollo_atbus_io_w at %08x -> %04x = %04x & %04x", ATBUS_IO_BASE + offset*2, isa_addr*2, data, mem_mask));

	// Motorola CPU is MSB first, ISA Bus is LSB first
	m_isa->io16_swap_w(isa_addr, data, mem_mask);
}

/***************************************************************************
 DN3000/DN3500 AT Bus memory space
 ***************************************************************************/

uint16_t apollo_state::apollo_atbus_memory_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t data;

	// Motorola CPU is MSB first, ISA Bus is LSB first
	data = m_isa->mem16_swap_r(offset, mem_mask);

	SLOG2(("apollo_atbus_memory_r at %08x = %04x & %04x", ATBUS_MEMORY_BASE + offset * 2, data, mem_mask));
	return data;
}

void apollo_state::apollo_atbus_memory_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	SLOG2(("apollo_atbus_memory_w at %08x = %04x & %04x", ATBUS_MEMORY_BASE + offset*2, data, mem_mask));

	// Motorola CPU is MSB first, ISA Bus is LSB first
	m_isa->mem16_swap_w(offset, data, mem_mask);
}

/***************************************************************************
 DN3000/DN3500 AT Bus unmapped read/write
 ***************************************************************************/

uint16_t apollo_state::apollo_atbus_unmap_io_r(offs_t offset, uint16_t mem_mask)
{
	// ISA bus has 0xff for unmapped addresses
	uint16_t data = 0xffff;
	uint32_t isa_addr = (offset & 3) + ((offset & ~0x1ff) >> 7);
	SLOG1(("apollo_atbus_unmap_io_r at %08x -> %04x = %04x & %04x", ATBUS_IO_BASE + offset*2, isa_addr*2, data, mem_mask));
	return data;
}

void apollo_state::apollo_atbus_unmap_io_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	uint32_t isa_addr = (offset & 3) + ((offset & ~0x1ff) >> 7);
	SLOG1(("apollo_atbus_unmap_io_w at %08x -> %04x = %04x & %04x", ATBUS_IO_BASE + offset*2, isa_addr*2, data, mem_mask));
}

uint8_t apollo_state::apollo_atbus_unmap_r(offs_t offset, uint8_t mem_mask)
{
	// ISA bus has 0xff for unmapped addresses
	uint8_t data = 0xff;
	SLOG2(("apollo_atbus_unmap_r at %08x = %02x & %02x", ATBUS_MEMORY_BASE + offset, data, mem_mask));
	return data;
}

void apollo_state::apollo_atbus_unmap_w(offs_t offset, uint8_t data, uint8_t mem_mask)
{
	SLOG1(("apollo_atbus_unmap_w at %08x = %02x & %02x", ATBUS_MEMORY_BASE + offset, data, mem_mask));
}

/***************************************************************************
 DN5500 Memory Present Register at 0x11400-0x114ff
 Strange: documented but not used
 ***************************************************************************/

void apollo_state::dn5500_memory_present_register_w(offs_t offset, uint8_t data){
	SLOG(("Error: writing DN5500 Memory Present Register at offset %02x = %02x", offset, data));
}

uint8_t apollo_state::dn5500_memory_present_register_r(offs_t offset){
	uint8_t data = DN5500_MEM_PRESENT_BYTE;
	SLOG(("reading DN5500 Memory Present Register at offset %02x = %02x", offset, data));
	return data;
}

/***************************************************************************
 DN5500 11500 Registers at 0x11500-0x115ff (undocumented, what does it do?)
 ***************************************************************************/

void apollo_state::dn5500_11500_w(offs_t offset, uint8_t data){
	SLOG1(("writing DN5500 11500 at offset %02x = %02x", offset, data));
}

uint8_t apollo_state::dn5500_11500_r(offs_t offset){
	uint8_t data = 0xff;
	SLOG1(("reading DN5500 11500 at offset %02x = %02x", offset, data));
	return data;
}

/***************************************************************************
 DN5500 I/O Protection Map at 0x7000000-0x700FFFF
 ***************************************************************************/

void apollo_state::dn5500_io_protection_map_w(offs_t offset, uint8_t data){
	// TODO
	SLOG1(("writing DN5500 I/O Protection Map at offset %02x = %02x", offset, data));
}

uint8_t apollo_state::dn5500_io_protection_map_r(offs_t offset){
	uint8_t data = 0xff;
	SLOG1(("reading DN5500 I/O Protection Map at offset %02x = %02x", offset, data));
	return data;
}

#if 0
/***************************************************************************
 DN3000/DN3500 at f8000000 - ffffffff (used by fpa and/or color7?)
 ***************************************************************************/

uint32_t apollo_state::apollo_f8_r(offs_t offset, uint32_t mem_mask){
	offs_t address = 0xf8000000 + offset * 4;
	uint32_t data = 0xffffffff;
	SLOG2(("unexpected memory dword read from %08x = %08x & %08x",
					address, data, mem_mask));
	return data;
}

void apollo_state::apollo_f8_w(offs_t offset, uint32_t data, uint32_t mem_mask){
	offs_t address = 0xf8000000 +offset * 4;

	SLOG2(("unexpected memory dword write to %08x = %08x & %08x",
					address, data, mem_mask));
}
#endif

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

void apollo_state::dn3500_map(address_map &map)
{
		map(0x00000000, 0xffffffff).rw(FUNC(apollo_state::apollo_unmapped_r), FUNC(apollo_state::apollo_unmapped_w));

		map(0x000000, 0x00ffff).rom(); /* boot ROM  */
		map(0x000000, 0x00ffff).w(FUNC(apollo_state::apollo_rom_w));
		map(0x010000, 0x0100ff).rw(FUNC(apollo_state::apollo_csr_status_register_r), FUNC(apollo_state::apollo_csr_status_register_w));
		map(0x010100, 0x0101ff).rw(FUNC(apollo_state::apollo_csr_control_register_r), FUNC(apollo_state::apollo_csr_control_register_w));
		map(0x010200, 0x0102ff).rw(FUNC(apollo_state::cache_status_register_r), FUNC(apollo_state::cache_control_register_w));
		map(0x010300, 0x0103ff).rw(FUNC(apollo_state::task_alias_register_r), FUNC(apollo_state::task_alias_register_w));
		map(0x010400, 0x0104ff).rw(m_sio, FUNC(apollo_sio::read), FUNC(apollo_sio::write));
		map(0x010500, 0x0105ff).rw(m_sio2, FUNC(apollo_sio::read), FUNC(apollo_sio::write));
		map(0x010800, 0x0108ff).rw(m_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write)).umask32(0x00ff00ff);
		map(0x010900, 0x0109ff).rw(FUNC(apollo_state::apollo_rtc_r), FUNC(apollo_state::apollo_rtc_w));
		map(0x010c00, 0x010cff).rw(FUNC(apollo_state::/*"dma1",*/apollo_dma_1_r), FUNC(apollo_state::apollo_dma_1_w));
		map(0x010d00, 0x010dff).rw(FUNC(apollo_state::/*"dma2",*/apollo_dma_2_r), FUNC(apollo_state::apollo_dma_2_w));
		map(0x011000, 0x0110ff).rw(m_pic8259_master, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
		map(0x011100, 0x0111ff).rw(m_pic8259_slave, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
		map(0x011200, 0x0112ff).rw(m_node_id, FUNC(apollo_ni::read), FUNC(apollo_ni::write));
		map(0x011300, 0x0113ff).rw(FUNC(apollo_state::latch_page_on_parity_error_register_r), FUNC(apollo_state::latch_page_on_parity_error_register_w));
		map(0x011600, 0x0116ff).rw(FUNC(apollo_state::master_req_register_r), FUNC(apollo_state::master_req_register_w));

		map(0x016400, 0x0164ff).rw(FUNC(apollo_state::selective_clear_locations_r), FUNC(apollo_state::selective_clear_locations_w));
		map(0x017000, 0x017fff).rw(FUNC(apollo_state::apollo_address_translation_map_r), FUNC(apollo_state::apollo_address_translation_map_w));

		map(ATBUS_IO_BASE, ATBUS_IO_END).rw(FUNC(apollo_state::apollo_atbus_io_r), FUNC(apollo_state::apollo_atbus_io_w));
		map(ATBUS_MEMORY_BASE, ATBUS_MEMORY_END).rw(FUNC(apollo_state::apollo_atbus_memory_r), FUNC(apollo_state::apollo_atbus_memory_w));

		// FIXME: must match with RAM size in driver/apollo_sio.c
		// map(DN3500_RAM_BASE, DN3500_RAM_END).ram(); /* 8MB RAM */
		map(DN3500_RAM_BASE, DN3500_RAM_END).ram().w(FUNC(apollo_state::ram_with_parity_w)).share(RAM_TAG);

		map(0x05d800, 0x05dc07).rw(m_graphics, FUNC(apollo_graphics_15i::apollo_mcr_r), FUNC(apollo_graphics_15i::apollo_mcr_w));
		map(0xfa0000, 0xfdffff).rw(m_graphics, FUNC(apollo_graphics_15i::apollo_mgm_r), FUNC(apollo_graphics_15i::apollo_mgm_w));

		map(0x05e800, 0x05ec07).rw(m_graphics, FUNC(apollo_graphics_15i::apollo_ccr_r), FUNC(apollo_graphics_15i::apollo_ccr_w));
		map(0x0a0000, 0x0bffff).rw(m_graphics, FUNC(apollo_graphics_15i::apollo_cgm_r), FUNC(apollo_graphics_15i::apollo_cgm_w));

//      map(0x03020000, 0x0303ffff) Cache Tag Store (DN4500 only)
//      map(0x04000000, 0x0400ffff) Cache Tag Data (DN4500 only)
//      map(0x0e000000, 0x0fffffff) FPA address space

//      map(0xf8000000, 0xffffffff).rw(FUNC(apollo_state::apollo_f8_r), FUNC(apollo_state::apollo_f8_w));
}

void apollo_state::dsp3500_map(address_map &map)
{
		map(0x00000000, 0xffffffff).rw(FUNC(apollo_state::apollo_unmapped_r), FUNC(apollo_state::apollo_unmapped_w));
		map(0x000000, 0x00ffff).rom(); /* boot ROM  */
		map(0x000000, 0x00ffff).w(FUNC(apollo_state::apollo_rom_w));
		map(0x010000, 0x0100ff).rw(FUNC(apollo_state::apollo_csr_status_register_r), FUNC(apollo_state::apollo_csr_status_register_w));
		map(0x010100, 0x0101ff).rw(FUNC(apollo_state::apollo_csr_control_register_r), FUNC(apollo_state::apollo_csr_control_register_w));
		map(0x010200, 0x0102ff).rw(FUNC(apollo_state::cache_status_register_r), FUNC(apollo_state::cache_control_register_w));
		map(0x010300, 0x0103ff).rw(FUNC(apollo_state::task_alias_register_r), FUNC(apollo_state::task_alias_register_w));
		map(0x010400, 0x0104ff).rw(m_sio, FUNC(apollo_sio::read), FUNC(apollo_sio::write));
		map(0x010500, 0x0105ff).rw(m_sio2, FUNC(apollo_sio::read), FUNC(apollo_sio::write));
		map(0x010800, 0x0108ff).rw(m_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write)).umask32(0x00ff00ff);
		map(0x010900, 0x0109ff).rw(FUNC(apollo_state::apollo_rtc_r), FUNC(apollo_state::apollo_rtc_w));
		map(0x010c00, 0x010cff).rw(FUNC(apollo_state::/*"dma1",*/apollo_dma_1_r), FUNC(apollo_state::apollo_dma_1_w));
		map(0x010d00, 0x010dff).rw(FUNC(apollo_state::/*"dma2",*/apollo_dma_2_r), FUNC(apollo_state::apollo_dma_2_w));
		map(0x011000, 0x0110ff).rw(m_pic8259_master, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
		map(0x011100, 0x0111ff).rw(m_pic8259_slave, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
		map(0x011200, 0x0112ff).rw(m_node_id, FUNC(apollo_ni::read), FUNC(apollo_ni::write));
		map(0x011300, 0x0113ff).rw(FUNC(apollo_state::latch_page_on_parity_error_register_r), FUNC(apollo_state::latch_page_on_parity_error_register_w));
		map(0x011600, 0x0116ff).rw(FUNC(apollo_state::master_req_register_r), FUNC(apollo_state::master_req_register_w));

		map(0x016400, 0x0164ff).rw(FUNC(apollo_state::selective_clear_locations_r), FUNC(apollo_state::selective_clear_locations_w));
		map(0x017000, 0x017fff).rw(FUNC(apollo_state::apollo_address_translation_map_r), FUNC(apollo_state::apollo_address_translation_map_w));

		map(ATBUS_IO_BASE, ATBUS_IO_END).rw(FUNC(apollo_state::apollo_atbus_io_r), FUNC(apollo_state::apollo_atbus_io_w));

		map(DN3500_RAM_BASE, DN3500_RAM_END).ram().w(FUNC(apollo_state::ram_with_parity_w)).share(RAM_TAG);

		map(ATBUS_MEMORY_BASE, ATBUS_MEMORY_END).rw(FUNC(apollo_state::apollo_atbus_memory_r), FUNC(apollo_state::apollo_atbus_memory_w));

//      map(0xf8000000, 0xffffffff).rw(FUNC(apollo_state::apollo_f8_r), FUNC(apollo_state::apollo_f8_w));
}

void apollo_state::dn3000_map(address_map &map)
{
		map(0x000000, 0xffffff).rw(FUNC(apollo_state::apollo_unmapped_r), FUNC(apollo_state::apollo_unmapped_w));

		map(0x000000, 0x007fff).rom(); /* boot ROM  */
		map(0x000000, 0x007fff).w(FUNC(apollo_state::apollo_rom_w));
		map(0x008000, 0x0080ff).rw(FUNC(apollo_state::apollo_csr_status_register_r), FUNC(apollo_state::apollo_csr_status_register_w));
		map(0x008100, 0x0081ff).rw(FUNC(apollo_state::apollo_csr_control_register_r), FUNC(apollo_state::apollo_csr_control_register_w));
		map(0x008400, 0x0087ff).rw(m_sio, FUNC(apollo_sio::read), FUNC(apollo_sio::write));
		map(0x008800, 0x0088ff).rw(m_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write)).umask32(0x00ff00ff);
		map(0x008900, 0x0089ff).rw(FUNC(apollo_state::apollo_rtc_r), FUNC(apollo_state::apollo_rtc_w));
		map(0x009000, 0x0090ff).rw(FUNC(apollo_state::/*"dma1",*/apollo_dma_1_r), FUNC(apollo_state::apollo_dma_1_w));
		map(0x009100, 0x0091ff).rw(FUNC(apollo_state::/*"dma2",*/apollo_dma_2_r), FUNC(apollo_state::apollo_dma_2_w));
		map(0x009200, 0x0092ff).rw(FUNC(apollo_state::apollo_dma_page_register_r), FUNC(apollo_state::apollo_dma_page_register_w));
		map(0x009300, 0x0093ff).rw(FUNC(apollo_state::latch_page_on_parity_error_register_r), FUNC(apollo_state::latch_page_on_parity_error_register_w));
		map(0x009400, 0x0094ff).rw(m_pic8259_master, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
		map(0x009500, 0x0095ff).rw(m_pic8259_slave, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
		map(0x009600, 0x0096ff).rw(m_node_id, FUNC(apollo_ni::read), FUNC(apollo_ni::write));

		map(ATBUS_IO_BASE, ATBUS_IO_END).rw(FUNC(apollo_state::apollo_atbus_io_r), FUNC(apollo_state::apollo_atbus_io_w));
		map(ATBUS_MEMORY_BASE, ATBUS_MEMORY_END).rw(FUNC(apollo_state::apollo_atbus_memory_r), FUNC(apollo_state::apollo_atbus_memory_w));

		// FIXME: must match with RAM size in driver/apollo_sio.c
		// map(DN3000_RAM_BASE, DN3000_RAM_END).ram();  /* 8MB RAM */
		map(DN3000_RAM_BASE, DN3000_RAM_END).ram().w(FUNC(apollo_state::ram_with_parity_w)).share(RAM_TAG);

		map(0x05d800, 0x05dc07).rw(m_graphics, FUNC(apollo_graphics_15i::apollo_mcr_r), FUNC(apollo_graphics_15i::apollo_mcr_w));
		map(0xfa0000, 0xfdffff).rw(m_graphics, FUNC(apollo_graphics_15i::apollo_mgm_r), FUNC(apollo_graphics_15i::apollo_mgm_w));

		map(0x05e800, 0x05ec07).rw(m_graphics, FUNC(apollo_graphics_15i::apollo_ccr_r), FUNC(apollo_graphics_15i::apollo_ccr_w));
		map(0x0a0000, 0x0bffff).rw(m_graphics, FUNC(apollo_graphics_15i::apollo_cgm_r), FUNC(apollo_graphics_15i::apollo_cgm_w));
}

void apollo_state::dsp3000_map(address_map &map)
{
		map(0x000000, 0xffffff).rw(FUNC(apollo_state::apollo_unmapped_r), FUNC(apollo_state::apollo_unmapped_w));

		map(0x000000, 0x007fff).rom(); /* boot ROM  */
		map(0x000000, 0x007fff).w(FUNC(apollo_state::apollo_rom_w));
		map(0x008000, 0x0080ff).rw(FUNC(apollo_state::apollo_csr_status_register_r), FUNC(apollo_state::apollo_csr_status_register_w));
		map(0x008100, 0x0081ff).rw(FUNC(apollo_state::apollo_csr_control_register_r), FUNC(apollo_state::apollo_csr_control_register_w));
		map(0x008400, 0x0087ff).rw(m_sio, FUNC(apollo_sio::read), FUNC(apollo_sio::write));
		map(0x008800, 0x0088ff).rw(m_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write)).umask32(0x00ff00ff);
		map(0x008900, 0x0089ff).rw(FUNC(apollo_state::apollo_rtc_r), FUNC(apollo_state::apollo_rtc_w));

		map(0x009000, 0x0090ff).rw(FUNC(apollo_state::/*"dma1",*/apollo_dma_1_r), FUNC(apollo_state::apollo_dma_1_w));
		map(0x009100, 0x0091ff).rw(FUNC(apollo_state::/*"dma2",*/apollo_dma_2_r), FUNC(apollo_state::apollo_dma_2_w));
		map(0x009200, 0x0092ff).rw(FUNC(apollo_state::apollo_dma_page_register_r), FUNC(apollo_state::apollo_dma_page_register_w));
		map(0x009300, 0x0093ff).rw(FUNC(apollo_state::latch_page_on_parity_error_register_r), FUNC(apollo_state::latch_page_on_parity_error_register_w));
		map(0x009400, 0x0094ff).rw(m_pic8259_master, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
		map(0x009500, 0x0095ff).rw(m_pic8259_slave, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
		map(0x009600, 0x0096ff).rw(m_node_id, FUNC(apollo_ni::read), FUNC(apollo_ni::write));

		map(ATBUS_IO_BASE, ATBUS_IO_END).rw(FUNC(apollo_state::apollo_atbus_io_r), FUNC(apollo_state::apollo_atbus_io_w));
		map(ATBUS_MEMORY_BASE, ATBUS_MEMORY_END).rw(FUNC(apollo_state::apollo_atbus_memory_r), FUNC(apollo_state::apollo_atbus_memory_w));

		// FIXME: must match with RAM size in driver/apollo_sio.c
		// map(DN3000_RAM_BASE, DN3000_RAM_END).ram();  /* 8MB RAM */
		map(DN3000_RAM_BASE, DN3000_RAM_END).ram().w(FUNC(apollo_state::ram_with_parity_w)).share(RAM_TAG);

}


void apollo_state::dn5500_map(address_map &map)
{
	map(0x00000000, 0xffffffff).rw(FUNC(apollo_state::apollo_unmapped_r), FUNC(apollo_state::apollo_unmapped_w));
	map(0x000000, 0x00ffff).rom(); /* boot ROM  */
	map(0x000000, 0x00ffff).w(FUNC(apollo_state::apollo_rom_w));
	map(0x010000, 0x0100ff).rw(FUNC(apollo_state::apollo_csr_status_register_r), FUNC(apollo_state::apollo_csr_status_register_w));
	map(0x010100, 0x0101ff).rw(FUNC(apollo_state::apollo_csr_control_register_r), FUNC(apollo_state::apollo_csr_control_register_w));
	map(0x010200, 0x0102ff).rw(FUNC(apollo_state::cache_status_register_r), FUNC(apollo_state::cache_control_register_w));
	map(0x010300, 0x0103ff).rw(FUNC(apollo_state::task_alias_register_r), FUNC(apollo_state::task_alias_register_w));
	map(0x010400, 0x0104ff).rw(m_sio, FUNC(apollo_sio::read), FUNC(apollo_sio::write));
	map(0x010500, 0x0105ff).rw(m_sio2, FUNC(apollo_sio::read), FUNC(apollo_sio::write));
	map(0x010800, 0x0108ff).rw(m_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write)).umask32(0x00ff00ff);
	map(0x010900, 0x0109ff).rw(FUNC(apollo_state::apollo_rtc_r), FUNC(apollo_state::apollo_rtc_w));
	map(0x010c00, 0x010cff).rw(FUNC(apollo_state::/*"dma1",*/apollo_dma_1_r), FUNC(apollo_state::apollo_dma_1_w));
	map(0x010d00, 0x010dff).rw(FUNC(apollo_state::/*"dma2",*/apollo_dma_2_r), FUNC(apollo_state::apollo_dma_2_w));
	map(0x011000, 0x0110ff).rw(m_pic8259_master, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x011100, 0x0111ff).rw(m_pic8259_slave, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x011200, 0x0112ff).rw(m_node_id, FUNC(apollo_ni::read), FUNC(apollo_ni::write));
	map(0x011300, 0x0113ff).rw(FUNC(apollo_state::latch_page_on_parity_error_register_r), FUNC(apollo_state::latch_page_on_parity_error_register_w));
	map(0x011400, 0x0114ff).rw(FUNC(apollo_state::dn5500_memory_present_register_r), FUNC(apollo_state::dn5500_memory_present_register_w));
	map(0x011500, 0x0115ff).rw(FUNC(apollo_state::dn5500_11500_r), FUNC(apollo_state::dn5500_11500_w));
	map(0x011600, 0x0116ff).rw(FUNC(apollo_state::master_req_register_r), FUNC(apollo_state::master_req_register_w));

	map(0x016400, 0x0164ff).rw(FUNC(apollo_state::selective_clear_locations_r), FUNC(apollo_state::selective_clear_locations_w));
	map(0x017000, 0x017fff).rw(FUNC(apollo_state::apollo_address_translation_map_r), FUNC(apollo_state::apollo_address_translation_map_w));

	map(ATBUS_IO_BASE, ATBUS_IO_END).rw(FUNC(apollo_state::apollo_atbus_io_r), FUNC(apollo_state::apollo_atbus_io_w));
	map(ATBUS_MEMORY_BASE, ATBUS_MEMORY_END).rw(FUNC(apollo_state::apollo_atbus_memory_r), FUNC(apollo_state::apollo_atbus_memory_w));

	// FIXME: must match with RAM size in driver/apollo_sio.c
	// map(DN3500_RAM_BASE, DN3500_RAM_END).ram();  /* 8MB RAM */
	map(DN5500_RAM_BASE, DN5500_RAM_END).ram().w(FUNC(apollo_state::ram_with_parity_w)).share(RAM_TAG);

	map(0x05d800, 0x05dc07).rw(m_graphics, FUNC(apollo_graphics_15i::apollo_mcr_r), FUNC(apollo_graphics_15i::apollo_mcr_w));
	map(0xfa0000, 0xfdffff).rw(m_graphics, FUNC(apollo_graphics_15i::apollo_mgm_r), FUNC(apollo_graphics_15i::apollo_mgm_w));

	map(0x05e800, 0x05ec07).rw(m_graphics, FUNC(apollo_graphics_15i::apollo_ccr_r), FUNC(apollo_graphics_15i::apollo_ccr_w));
	map(0x0a0000, 0x0bffff).rw(m_graphics, FUNC(apollo_graphics_15i::apollo_cgm_r), FUNC(apollo_graphics_15i::apollo_cgm_w));

//  map(0x03020000, 0x0303ffff) Cache Tag Store (DN4500 only)
//  map(0x04000000, 0x0400ffff) Cache Tag Data (DN4500 only)
	map(0x07000000, 0x0700FFFF).rw(FUNC(apollo_state::dn5500_io_protection_map_r), FUNC(apollo_state::dn5500_io_protection_map_w));
//  map(0x0e000000, 0x0fffffff) FPA address space

//  map(0xf8000000, 0xffffffff).rw(FUNC(apollo_state::apollo_f8_r), FUNC(apollo_state::apollo_f8_w));
}

void apollo_state::dsp5500_map(address_map &map)
{
	map(0x00000000, 0xffffffff).rw(FUNC(apollo_state::apollo_unmapped_r), FUNC(apollo_state::apollo_unmapped_w));
	map(0x000000, 0x00ffff).rom(); /* boot ROM  */
	map(0x000000, 0x00ffff).w(FUNC(apollo_state::apollo_rom_w));
	map(0x010000, 0x0100ff).rw(FUNC(apollo_state::apollo_csr_status_register_r), FUNC(apollo_state::apollo_csr_status_register_w));
	map(0x010100, 0x0101ff).rw(FUNC(apollo_state::apollo_csr_control_register_r), FUNC(apollo_state::apollo_csr_control_register_w));
	map(0x010200, 0x0102ff).rw(FUNC(apollo_state::cache_status_register_r), FUNC(apollo_state::cache_control_register_w));
	map(0x010300, 0x0103ff).rw(FUNC(apollo_state::task_alias_register_r), FUNC(apollo_state::task_alias_register_w));
	map(0x010400, 0x0104ff).rw(m_sio, FUNC(apollo_sio::read), FUNC(apollo_sio::write));
	map(0x010500, 0x0105ff).rw(m_sio2, FUNC(apollo_sio::read), FUNC(apollo_sio::write));
	map(0x010800, 0x0108ff).rw(m_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write)).umask32(0x00ff00ff);
	map(0x010900, 0x0109ff).rw(FUNC(apollo_state::apollo_rtc_r), FUNC(apollo_state::apollo_rtc_w));
	map(0x010c00, 0x010cff).rw(FUNC(apollo_state::/*"dma1",*/apollo_dma_1_r), FUNC(apollo_state::apollo_dma_1_w));
	map(0x010d00, 0x010dff).rw(FUNC(apollo_state::/*"dma2",*/apollo_dma_2_r), FUNC(apollo_state::apollo_dma_2_w));
	map(0x011000, 0x0110ff).rw(m_pic8259_master, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x011100, 0x0111ff).rw(m_pic8259_slave, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x011200, 0x0112ff).rw(m_node_id, FUNC(apollo_ni::read), FUNC(apollo_ni::write));
	map(0x011300, 0x0113ff).rw(FUNC(apollo_state::latch_page_on_parity_error_register_r), FUNC(apollo_state::latch_page_on_parity_error_register_w));
	map(0x011400, 0x0114ff).rw(FUNC(apollo_state::dn5500_memory_present_register_r), FUNC(apollo_state::dn5500_memory_present_register_w));
	map(0x011500, 0x0115ff).rw(FUNC(apollo_state::dn5500_11500_r), FUNC(apollo_state::dn5500_11500_w));
	map(0x011600, 0x0116ff).rw(FUNC(apollo_state::master_req_register_r), FUNC(apollo_state::master_req_register_w));

	map(0x016400, 0x0164ff).rw(FUNC(apollo_state::selective_clear_locations_r), FUNC(apollo_state::selective_clear_locations_w));
	map(0x017000, 0x017fff).rw(FUNC(apollo_state::apollo_address_translation_map_r), FUNC(apollo_state::apollo_address_translation_map_w));

	map(ATBUS_IO_BASE, ATBUS_IO_END).rw(FUNC(apollo_state::apollo_atbus_io_r), FUNC(apollo_state::apollo_atbus_io_w));
	map(ATBUS_MEMORY_BASE, ATBUS_MEMORY_END).rw(FUNC(apollo_state::apollo_atbus_memory_r), FUNC(apollo_state::apollo_atbus_memory_w));

	// FIXME: must match with RAM size in driver/apollo_sio.c
	map(DN5500_RAM_BASE, DN5500_RAM_END).ram().w(FUNC(apollo_state::ram_with_parity_w)).share(RAM_TAG);

	map(0x07000000, 0x0700FFFF).rw(FUNC(apollo_state::dn5500_io_protection_map_r), FUNC(apollo_state::dn5500_io_protection_map_w));
//  map(0xf8000000, 0xffffffff).rw(FUNC(apollo_state::apollo_f8_r), FUNC(apollo_state::apollo_f8_w));
}

/***************************************************************************
 Machine Reset
 ***************************************************************************/

void apollo_state::machine_reset()
{
	MLOG1(("machine_reset"));

	MACHINE_RESET_CALL_MEMBER(apollo);

#ifdef APOLLO_XXL
	// set configuration
	omti8621_device::set_verbose(apollo_config(APOLLO_CONF_DISK_TRACE));
#endif

	if (apollo_config(APOLLO_CONF_NODE_ID))
	{
		// set node ID from UID of logical volume 1 of logical unit 0
		m_node_id->set_node_id_from_disk();
	}

#if 0
	m_maincpu->set_instruction_hook(read32sm_delegate(*this, FUNC(apollo_state::apollo_instruction_hook)));
#endif
}

void apollo_state::apollo_reset_instr_callback(int state)
{
	MLOG1(("apollo_reset_instr_callback"));

	// reset the CPU board devices
	MACHINE_RESET_CALL_MEMBER(apollo);

	// reset the ISA bus devices
	m_isa->reset();

	if (!apollo_is_dsp3x00())
	{
		m_graphics->reset();
		m_keyboard->reset();
#ifdef APOLLO_XXL
		m_sio2->reset();
#endif
	}
}

/***************************************************************************
 Machine Start
 ***************************************************************************/

void apollo_state::machine_start(){
	memory_share *messram = memshare(RAM_TAG);
	//MLOG1(("machine_start_dn3500: ram size is %d MB", (int)messram->bytes()/(1024*1024)));

	// clear ram
	memset(messram->ptr(), 0x55, messram->bytes());

	MACHINE_START_CALL_MEMBER(apollo);

	// install nop handlers for unmapped ISA bus addresses
	m_isa->install16_device((ATBUS_IO_BASE - 0x40000) >> 7, (ATBUS_IO_END - 0x40000) >> 7, read16s_delegate(*this, FUNC(apollo_state::apollo_atbus_unmap_io_r)), write16s_delegate(*this, FUNC(apollo_state::apollo_atbus_unmap_io_w)));
	m_isa->install_memory(0, ATBUS_MEMORY_END, read8s_delegate(*this, FUNC(apollo_state::apollo_atbus_unmap_r)), write8s_delegate(*this, FUNC(apollo_state::apollo_atbus_unmap_w)));
}

/***************************************************************************
 Driver Init
 ***************************************************************************/

void apollo_state::init_dn3500()
{
//  MLOG1(("driver_init_dn3500"));

	ram_base_address = DN3500_RAM_BASE;
	ram_end_address = DN3500_RAM_END;

	node_type=  NODE_TYPE_DN3500;
	ram_config_byte= DN3500_RAM_CONFIG_BYTE;

	init_apollo();
}

void apollo_state::init_dsp3500()
{
	init_dn3500();
//  MLOG1(("driver_init_dsp3500"));
	node_type = NODE_TYPE_DSP3500;
}

void apollo_state::init_dn3000()
{
	init_dn3500();
//  MLOG1(("driver_init_dn3000"));

	ram_base_address = DN3000_RAM_BASE;
	ram_end_address = DN3000_RAM_END;

	node_type = NODE_TYPE_DN3000;
	ram_config_byte= DN3000_RAM_CONFIG_8MB;
}

void apollo_state::init_dsp3000()
{
	init_dn3000();
//  MLOG1(("driver_init_dsp3000"));
	node_type = NODE_TYPE_DSP3000;
}

void apollo_state::init_dn5500()
{
	init_dn3500();
//  MLOG1(("driver_init_dn5500"));

	ram_base_address = DN5500_RAM_BASE;
	ram_end_address = DN5500_RAM_END;

	node_type = NODE_TYPE_DN5500;
	ram_config_byte= DN5500_RAM_CONFIG_BYTE;
}

void apollo_state::init_dsp5500()
{
	init_dn5500();
//  MLOG1(("driver_init_dsp5500"));
	node_type = NODE_TYPE_DSP5500;
}

/***************************************************************************
 Input Ports
 ***************************************************************************/

static INPUT_PORTS_START( dn3500 )
	PORT_INCLUDE(apollo_config)
INPUT_PORTS_END

static INPUT_PORTS_START( dsp3500 )
	PORT_INCLUDE(apollo_config)
INPUT_PORTS_END

int apollo_state::apollo_kbd_is_german()
{
	return (apollo_config(APOLLO_CONF_GERMAN_KBD) != 0) ? ASSERT_LINE : CLEAR_LINE;
}

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

void apollo_state::dn3500(machine_config &config)
{
	/* basic machine hardware */
	M68030(config, m_maincpu, 25000000); /* 25 MHz 68030 */
	m_maincpu->set_addrmap(AS_PROGRAM, &apollo_state::dn3500_map);
	m_maincpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &apollo_state::cpu_space_map);
	m_maincpu->reset_cb().set(FUNC(apollo_state::apollo_reset_instr_callback));

	config.set_maximum_quantum(attotime::from_hz(60));

	apollo(config);

	/* internal ram */
	RAM(config, m_ram).set_default_size("8M").set_extra_options("4M,8M,16M,32M");

#ifdef APOLLO_XXL
	apollo_stdio_device &stdio(APOLLO_STDIO(config, APOLLO_STDIO_TAG, 0));
	stdio.tx_cb().set(m_sio, FUNC(apollo_sio::rx_b_w));
#endif
}

void apollo_state::dsp3500(machine_config &config)
{
	M68030(config, m_maincpu, 25000000); /* 25 MHz 68030 */
	m_maincpu->set_addrmap(AS_PROGRAM, &apollo_state::dsp3500_map);
	m_maincpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &apollo_state::cpu_space_map);
	m_maincpu->reset_cb().set(FUNC(apollo_state::apollo_reset_instr_callback));
	config.set_maximum_quantum(attotime::from_hz(60));

	apollo_terminal(config);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("8M").set_extra_options("4M,8M,16M,32M");

	/* terminal hardware */
	config.set_default_layout(layout_apollo_dsp);
}

void apollo_state::dn3500_19i(machine_config &config)
{
	dn3500(config);
	/* video hardware 19" monochrome */
	APOLLO_MONO19I(config, m_graphics, 0);
	APOLLO_KBD(config, m_keyboard, 0);
	m_keyboard->tx_cb().set(m_sio, FUNC(apollo_sio::rx_a_w));
	m_keyboard->german_cb().set(FUNC(apollo_state::apollo_kbd_is_german));
}

void apollo_state::dn3500_15i(machine_config &config)
{
	dn3500(config);
	/* video hardware is 15" monochrome or color */
	APOLLO_GRAPHICS(config, m_graphics, 0);
	APOLLO_KBD(config, m_keyboard, 0);
	m_keyboard->tx_cb().set(m_sio, FUNC(apollo_sio::rx_a_w));
	m_keyboard->german_cb().set(FUNC(apollo_state::apollo_kbd_is_german));
}

void apollo_state::dn3000(machine_config &config)
{
	dn3500(config);
	M68020PMMU(config.replace(), m_maincpu, 12000000); /* 12 MHz */
	m_maincpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &apollo_state::cpu_space_map);
	m_maincpu->set_addrmap(AS_PROGRAM, &apollo_state::dn3000_map);
	m_maincpu->reset_cb().set(FUNC(apollo_state::apollo_reset_instr_callback));
	config.device_remove( APOLLO_SIO2_TAG );
	m_ram->set_default_size("8M").set_extra_options("4M");

	// FIXME: is this interrupt really only connected on DN3000?
	m_rtc->irq().set(FUNC(apollo_state::apollo_rtc_irq_function));
}

void apollo_state::dsp3000(machine_config &config)
{
	M68020PMMU(config, m_maincpu, 12000000); /* 12 MHz */
	m_maincpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &apollo_state::cpu_space_map);
	m_maincpu->set_addrmap(AS_PROGRAM, &apollo_state::dsp3000_map);
	m_maincpu->reset_cb().set(FUNC(apollo_state::apollo_reset_instr_callback));
	config.set_maximum_quantum(attotime::from_hz(60));

	apollo_terminal(config);

	/* internal ram */
	RAM(config, m_ram).set_default_size("8M").set_extra_options("4M");

	config.device_remove( APOLLO_SIO2_TAG );

	/* terminal hardware */
	config.set_default_layout(layout_apollo_dsp);
}

void apollo_state::dn3000_19i(machine_config &config)
{
	dn3000(config);
	/* video hardware 19" monochrome */
	APOLLO_MONO19I(config, m_graphics, 0);
	APOLLO_KBD(config, m_keyboard, 0);
	m_keyboard->tx_cb().set(m_sio, FUNC(apollo_sio::rx_a_w));
	m_keyboard->german_cb().set(FUNC(apollo_state::apollo_kbd_is_german));
}

void apollo_state::dn3000_15i(machine_config &config)
{
	dn3000(config);
	/* video hardware 15" monochrome */
	APOLLO_GRAPHICS(config, m_graphics, 0);
	APOLLO_KBD(config, m_keyboard, 0);
	m_keyboard->tx_cb().set(m_sio, FUNC(apollo_sio::rx_a_w));
	m_keyboard->german_cb().set(FUNC(apollo_state::apollo_kbd_is_german));
}

void apollo_state::dn5500(machine_config &config)
{
	dn3500(config);
	M68040(config.replace(), m_maincpu, 25000000); /* 25 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &apollo_state::dn5500_map);
	m_maincpu->reset_cb().set(FUNC(apollo_state::apollo_reset_instr_callback));
}

void apollo_state::dsp5500(machine_config &config)
{
	M68040(config, m_maincpu, 25000000); /* 25 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &apollo_state::dsp5500_map);
	m_maincpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &apollo_state::cpu_space_map);
	m_maincpu->reset_cb().set(FUNC(apollo_state::apollo_reset_instr_callback));
	config.set_maximum_quantum(attotime::from_hz(60));

	apollo_terminal(config);

	/* internal ram */
	// FIXME: guess, to fix validation
	RAM(config, RAM_TAG).set_default_size("8M").set_extra_options("4M,8M,16M,32M");

	/* terminal hardware */
	config.set_default_layout(layout_apollo_dsp);
}

void apollo_state::dn5500_19i(machine_config &config)
{
	dn5500(config);
	/* video hardware 19" monochrome */
	APOLLO_MONO19I(config, m_graphics, 0);
	APOLLO_KBD(config, m_keyboard, 0);
	m_keyboard->tx_cb().set(m_sio, FUNC(apollo_sio::rx_a_w));
	m_keyboard->german_cb().set(FUNC(apollo_state::apollo_kbd_is_german));
}

void apollo_state::dn5500_15i(machine_config &config)
{
	dn5500(config);
	/* video hardware 15" monochrome */
	APOLLO_GRAPHICS(config, m_graphics, 0);
	APOLLO_KBD(config, m_keyboard, 0);
	m_keyboard->tx_cb().set(m_sio, FUNC(apollo_sio::rx_a_w));
	m_keyboard->german_cb().set(FUNC(apollo_state::apollo_kbd_is_german));
}

/***************************************************************************
 ROM Definitions
 ***************************************************************************/

ROM_START( dn3500 )
	// dn3500 boot ROM (Note: use sha1sum -b <rom file>)
	ROM_REGION( 0x0100000, MAINCPU, 0 ) /* 68000 code */

	// http://www.bitsavers.org/bits/Apollo/firmware/3500_BOOT_12191_7.bin
	// Note: file name must be converted to lower case (i.e. 3500_boot_12191_7.bin)
	// Note: this duplicates boot rom md7c-rev-8.00-1989-08-16-17-23-52.bin
	ROM_SYSTEM_BIOS( 0, "md7c-rev-8.00", "MD7C REV 8.00, 1989/08/16.17:23:52" )
	ROMX_LOAD( "3500_boot_12191_7.bin", 0x00000, 0x10000, CRC(3132067d) SHA1(36f3c83d9f2df42f2537b09ca2f051a8c9dfbfc2) , ROM_BIOS(0) )
ROM_END

ROM_START( dn5500 )
	// dn5500 boot ROM (Note: use sha1sum -b <rom file>)
	ROM_REGION( 0x0100000, MAINCPU, 0 ) /* 68000 code */

	ROM_SYSTEM_BIOS( 0, "md7c-rev-8.00", "MD7C REV 8.00, 1989/08/16.17:23:52" )
	ROMX_LOAD( "5500_boot_a1631-80046_1-30-92.bin", 0x00000, 0x10000, CRC(7b9ed610) SHA1(7315a884ec4551c44433c6079cc06509223cb02b) , ROM_BIOS(0) )
ROM_END

ROM_START( dn3000)
	ROM_REGION( 0x0090000, MAINCPU, 0 ) /* 68000 code */

	ROM_SYSTEM_BIOS( 0, "md8-rev-7.0", "MD8 REV 7.0, 1988/08/16.15:14:39" )
	ROMX_LOAD( "3000_boot_8475_7.bin",  0x00000, 0x08000, CRC(0fe2d471) SHA1(6c383d2266719a3d069b7bf015f6945179395e7a), ROM_BIOS(0) )
ROM_END

#define rom_dsp3500    rom_dn3500
#define rom_dn3500_15i rom_dn3500
#define rom_dn3500_19i rom_dn3500

#define rom_dsp3000    rom_dn3000
#define rom_dn3000_19i rom_dn3000

#define rom_dsp5500    rom_dn5500
#define rom_dn5500_15i rom_dn5500
#define rom_dn5500_19i rom_dn5500

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

#define DN_FLAGS 0
#define DSP_FLAGS 0
//#define DSP_FLAGS MACHINE_NO_SOUND

/*    YEAR  NAME        PARENT  COMPAT  MACHINE     INPUT    CLASS         INIT          COMPANY   FULLNAME                         FLAGS */
COMP( 1989, dn3500,     0,      0,      dn3500_15i, dn3500,  apollo_state, init_dn3500,  "Apollo", "Apollo DN3500",                 DN_FLAGS )
COMP( 1989, dsp3500,    dn3500, 0,      dsp3500,    dsp3500, apollo_state, init_dsp3500, "Apollo", "Apollo DSP3500",                DSP_FLAGS )
COMP( 1989, dn3500_19i, dn3500, 0,      dn3500_19i, dn3500,  apollo_state, init_dn3500,  "Apollo", "Apollo DN3500 19\" Monochrome", DN_FLAGS )

COMP( 1988, dn3000,     dn3500, 0,      dn3000_15i, dn3500,  apollo_state, init_dn3000,  "Apollo", "Apollo DN3000",                 DN_FLAGS )
COMP( 1988, dsp3000,    dn3500, 0,      dsp3000,    dsp3500, apollo_state, init_dsp3000, "Apollo", "Apollo DSP3000",                DSP_FLAGS )
COMP( 1988, dn3000_19i, dn3500, 0,      dn3000_19i, dn3500,  apollo_state, init_dn3000,  "Apollo", "Apollo DN3000 19\" Monochrome", DN_FLAGS )

COMP( 1991, dn5500,     dn3500, 0,      dn5500_15i, dn3500,  apollo_state, init_dn5500,  "Apollo", "Apollo DN5500",                 MACHINE_NOT_WORKING )
COMP( 1991, dsp5500,    dn3500, 0,      dsp5500,    dsp3500, apollo_state, init_dsp5500, "Apollo", "Apollo DSP5500",                MACHINE_NOT_WORKING )
COMP( 1991, dn5500_19i, dn3500, 0,      dn5500_19i, dn3500,  apollo_state, init_dn5500,  "Apollo", "Apollo DN5500 19\" Monochrome", MACHINE_NOT_WORKING )



apple1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    apple1.cpp - Apple I

    Next generation driver written in February 2016 by R. Belmont.
    Thanks to the original crew.

    Apple I has:
        6502 @ 1.023 MHz (~0.960 MHz with RAM refresh)
        4 or 8 KB RAM on-board
        256 byte Monitor ROM
        No IRQs, no sound, dumb terminal video
        6820 PIA for keyboard / terminal interface

    -------------------------------------------------------------------

    How to use cassettes:
    The system has no error checking or checksums, and the cassette
    has no header.
    Therefore, you must know the details, and pass these to the
    interface yourself.

    BASIC has no cassette handling. You must enter the monitor
    with: CALL -151
    then when finished, re-enter BASIC with: E2B3R

    Examples:

    A machine-language program will typically be like this:
    C100R    (enter the interface)
    0300.0FFFR  (enter the load and end addresses, then load the tape)
    You start the tape.
    When the prompt returns you stop the tape.
    0300R  (run your program)


    To Load Tape Basic:
    C100R
    E000.EFFFR
    You start the tape.
    When the prompt returns you stop the tape.
    E000R  (It must say 4C - if not, your tape is no good).
    The BASIC prompt will appear
    >@


    A BASIC program is split into two areas, one for the scratch pad,
    and one for the program proper.
    In BASIC you may have to adjust the allowed memory area, such as
    LOMEM = 768
    Then, go to the monitor: CALL -151
    C100R    (enter the interface)
    00A4.00FFR 0300.0FFFR   (load the 2 parts)
    You start the tape.
    When the prompt returns you stop the tape.
    E2B3R    (back to BASIC)
    You can LIST or RUN now.


    Saving is almost the same, when you specify the address range, enter
    W instead of R. The difficulty is finding out how long your program is.

    Insert a blank tape
    C100R
    0300.0FFFW
    Quickly press Record.
    When the prompt returns, press Stop.

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

#include "emu.h"
#include "cpu/m6502/m6502.h"
#include "machine/6821pia.h"
#include "imagedev/snapquik.h"
#include "machine/ram.h"

#include "bus/a1bus/a1bus.h"
#include "bus/a1bus/a1cassette.h"
#include "bus/a1bus/a1cffa.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"


namespace {

#define A1_CPU_TAG  "maincpu"
#define A1_PIA_TAG  "pia6821"
#define A1_BUS_TAG  "a1bus"
#define A1_BASICRAM_TAG "basicram"

class apple1_state : public driver_device
{
public:
	apple1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, A1_CPU_TAG),
		m_pia(*this, A1_PIA_TAG),
		m_ram(*this, RAM_TAG),
		m_screen(*this, "screen"),
		m_basicram(*this, A1_BASICRAM_TAG),
		m_kb0(*this, "KEY0"),
		m_kb1(*this, "KEY1"),
		m_kb2(*this, "KEY2"),
		m_kb3(*this, "KEY3"),
		m_kbspecial(*this, "KBSPECIAL")
	{ }

	void apple1(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<pia6821_device> m_pia;
	required_device<ram_device> m_ram;
	required_device<screen_device> m_screen;
	required_shared_ptr<uint8_t> m_basicram;
	required_ioport m_kb0, m_kb1, m_kb2, m_kb3, m_kbspecial;

	uint8_t *m_ram_ptr, *m_char_ptr;
	int m_ram_size, m_char_size;

	uint8_t m_vram[40*24];
	int m_cursx, m_cursy;

	bool m_reset_down;
	bool m_clear_down;

	uint8_t m_transchar;
	uint16_t m_lastports[4];

	emu_timer *m_ready_start_timer, *m_ready_end_timer, *m_kbd_strobe_timer;

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	uint8_t ram_r(offs_t offset);
	void ram_w(offs_t offset, uint8_t data);
	uint8_t pia_keyboard_r();
	void pia_display_w(uint8_t data);
	void pia_display_gate_w(int state);
	DECLARE_SNAPSHOT_LOAD_MEMBER(snapshot_cb);
	TIMER_CALLBACK_MEMBER(ready_start_cb);
	TIMER_CALLBACK_MEMBER(ready_end_cb);
	TIMER_CALLBACK_MEMBER(keyboard_strobe_cb);

	void apple1_map(address_map &map) ATTR_COLD;

	void plot_text_character(bitmap_ind16 &bitmap, int xpos, int ypos, int xscale, uint32_t code, const uint8_t *textgfx_data, uint32_t textgfx_datalen);
	void poll_keyboard();
};

static const uint8_t apple1_keymap[] =
{
	'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '=', '[', ']', ';', '\'',    // KEY0
	',', '.', '/', '\\', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',    // KEY1
	'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '\r', '_',    // KEY2
	' ', '\x1b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                              // KEY3

	')', '!', '@', '#', '$', '%', '^', '&', '*', '(', '_', '+', '[', ']', ':', '"',     // KEY0 + shift
	'<', '>', '?', '\\', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',    // KEY1 + shift
	'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '\r', '_',    // KEY2 + shift
	' ', '\x1b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                              // KEY3 + shift

	'0', '1', '\x00', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', '8', '9', '\x1f', '=', '\x1b', '\x1d', ';', '\'', // KEY0 + CTRL
	',', '.', '/', '\x1c', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x09', '\x0a', '\x0b', '\x0c',  // KEY1 + CTRL
	'\x0d', '\x0e', '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1a', '\r', '_',  // KEY2 + CTRL
	' ', '\x1b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                              // KEY3 + CTRL

};

// header is "LOAD:abcdDATA:" where abcd is the starting address
SNAPSHOT_LOAD_MEMBER(apple1_state::snapshot_cb)
{
	static const char hd1[6] = "LOAD:";
	static const char hd2[6] = "DATA:";

	// get the snapshot's size
	uint64_t snapsize = image.length();

	if (snapsize < 12)
		return std::make_pair(image_error::INVALIDLENGTH, "Snapshot is too short");

	if ((snapsize - 12) > 65535)
		return std::make_pair(image_error::INVALIDLENGTH, "Snapshot is too long");

	auto data = std::make_unique<uint8_t []>(snapsize);
	if (image.fread(data.get(), snapsize) != snapsize)
		return std::make_pair(image_error::UNSPECIFIED, "Internal error loading snapshot");

	if ((memcmp(hd1, &data[0], 5)) || (memcmp(hd2, &data[7], 5)))
		return std::make_pair(image_error::INVALIDIMAGE, "Snapshot is invalid");

	uint16_t start = (data[5]<<8) | data[6];
	uint16_t end = (snapsize - 12) + start;

	// check if this fits in RAM; load below 0xe000 must fit in RAMSIZE,
	// load at 0xe000 must fit in 4K
	if (((start < 0xe000) && (end > (m_ram_size - 1))) || (end > 0xefff))
		return std::make_pair(image_error::UNSUPPORTED, "Snapshot can't fit in RAM");

	if (start < 0xe000)
	{
		memcpy(m_ram_ptr + start, &data[12], snapsize - 12);
	}
	else if ((start >= 0xe000) && (start <= 0xefff))
	{
		memcpy(m_basicram + (start - 0xe000), &data[12], snapsize - 12);
	}
	else
	{
		return std::make_pair(
				image_error::INVALIDIMAGE,
				util::string_format("Snapshot has invalid load address %04x", start));
	}

	return std::make_pair(std::error_condition(), std::string());
}

void apple1_state::poll_keyboard()
{
	uint8_t special = m_kbspecial->read();
	uint16_t ports[4];
	int rawkey = 0;
	bool bKeypress = false;

	// handle special keys first:
	if (special & 0x10) // RESET
	{
		m_reset_down = true;
		m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
		m_pia->reset();
	}
	else if (m_reset_down)
	{
		m_reset_down = false;
		m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
	}

	if (special & 0x20) // CLEAR SCREEN
	{
		m_clear_down = true;
		memset(m_vram, 0, sizeof(m_vram));
		m_cursx = m_cursy = 0;
	}
	else
	{
		m_clear_down = false;
	}

	// lower the keyboard strobe
	m_pia->ca1_w(0);

	// cache all the rows
	ports[0] = m_kb0->read();
	ports[1] = m_kb1->read();
	ports[2] = m_kb2->read();
	ports[3] = m_kb3->read();

	for (int port = 0; port < 4; port++)
	{
		uint16_t ptread = ports[port] ^ m_lastports[port];

		for (int bit = 0; bit < 16; bit++)
		{
			// key changed?
			if (ptread & (1 << bit))
			{
				// key down?
				if (ports[port] & (1 << bit))
				{
					rawkey = (port * 16) + bit;
					m_lastports[port] |= (1 << bit);
					port = 4;   // force outer for loop to quit too
					bKeypress = true;
				}
				else    // key up
				{
					m_lastports[port] &= ~(1 << bit);
				}
				break;
			}
		}
	}

	if (bKeypress)
	{
		if ((special & 0xc) != 0)
		{
			m_transchar = apple1_keymap[rawkey + (8*16)];
		}
		else if ((special & 0x3) != 0)
		{
			m_transchar = apple1_keymap[rawkey + (4*16)];
		}
		else
		{
			m_transchar = apple1_keymap[rawkey];
		}
		// pulse the strobe line
		m_pia->ca1_w(1);
	}
}

void apple1_state::plot_text_character(bitmap_ind16 &bitmap, int xpos, int ypos, int xscale, uint32_t code,
	const uint8_t *textgfx_data, uint32_t textgfx_datalen)
{
	int fg = 1, bg = 0;
	int const charcode = (code & 0x1f) | (((code ^ 0x40) & 0x40) >> 1);

	/* look up the character data */
	uint8_t const *const chardata = &textgfx_data[(charcode * 8)];

	for (int y = 0; y < 8; y++)
	{
		for (int x = 0; x < 7; x++)
		{
			uint16_t const color = (chardata[y] & (1 << (6-x))) ? fg : bg;

			for (int i = 0; i < xscale; i++)
			{
				bitmap.pix(ypos + y, xpos + (x * xscale) + i) = color;
			}
		}
	}
}

uint32_t apple1_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	int vramad;
	int cursor_blink = 0;
	uint8_t curs_save = 0;

	poll_keyboard();

	// the cursor 555 timer counts 0.52 of a second; the cursor is ON for
	// 2 of those counts and OFF for the last one.
	if ((int(machine().time().as_double() / (0.52 / 3.0)) % 3) < 2)
	{
		curs_save = m_vram[(m_cursy * 40) + m_cursx];
		m_vram[(m_cursy * 40) + m_cursx] = 0x40;
		cursor_blink = 1;
	}

	for (int row = 0; row < cliprect.bottom(); row += 8)
	{
		for (int col = 0; col < 40; col++)
		{
			vramad = ((row/8) * 40) + col;

			plot_text_character(bitmap, col * 14, row, 2, m_vram[vramad],
				m_char_ptr, m_char_size);
		}
	}

	if (cursor_blink)
	{
		m_vram[(m_cursy * 40) + m_cursx] = curs_save;
	}

	return 0;
}

void apple1_state::machine_start()
{
	m_ram_ptr = m_ram->pointer();
	m_ram_size = m_ram->size();
	m_char_ptr = memregion("gfx1")->base();
	m_char_size = memregion("gfx1")->bytes();

	m_reset_down = m_clear_down = false;

	m_ready_start_timer = timer_alloc(FUNC(apple1_state::ready_start_cb), this);
	m_ready_end_timer = timer_alloc(FUNC(apple1_state::ready_end_cb), this);
	m_kbd_strobe_timer = timer_alloc(FUNC(apple1_state::keyboard_strobe_cb), this);

	// setup save states
	save_item(NAME(m_vram));
	save_item(NAME(m_cursx));
	save_item(NAME(m_cursy));
	save_item(NAME(m_reset_down));
	save_item(NAME(m_clear_down));
	save_item(NAME(m_transchar));
	save_item(NAME(m_lastports));
}

void apple1_state::machine_reset()
{
	memset(m_vram, 0, sizeof(m_vram));
	m_transchar = 0;
	m_cursx = m_cursy = 0;
	m_lastports[0] = m_lastports[1] = m_lastports[2] = m_lastports[3] = 0;
}

uint8_t apple1_state::ram_r(offs_t offset)
{
	if (offset < m_ram_size)
	{
		return m_ram_ptr[offset];
	}

	return 0xff;
}

void apple1_state::ram_w(offs_t offset, uint8_t data)
{
	if (offset < m_ram_size)
	{
		m_ram_ptr[offset] = data;
	}
}

void apple1_state::apple1_map(address_map &map)
{
	map(0x0000, 0xbfff).rw(FUNC(apple1_state::ram_r), FUNC(apple1_state::ram_w));
	map(0xd010, 0xd013).mirror(0x0fec).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xe000, 0xefff).ram().share(A1_BASICRAM_TAG);
	map(0xff00, 0xffff).rom().region(A1_CPU_TAG, 0);
}

uint8_t apple1_state::pia_keyboard_r()
{
	return m_transchar | 0x80;  // bit 7 is wired high, similar-ish to the Apple II
}

void apple1_state::pia_display_w(uint8_t data)
{
	data &= 0x7f;   // D7 is ignored by the video h/w

	// ignore characters if CLEAR is down
	if (m_clear_down)
	{
		return;
	}

	// video h/w rejects control characters except CR
	if ((data < 32) && (data != '\r'))
	{
		return;
	}

	if (data == '\r')
	{
		m_cursx = 0;
		m_cursy++;
	}
	else
	{
		m_vram[(m_cursy * 40) + m_cursx] = data;

		m_cursx++;
		if (m_cursx > 39)
		{
			m_cursx = 0;
			m_cursy++;
		}
	}

	// scroll the screen if we're at the bottom
	if (m_cursy > 23)
	{
		for (int sy = 0; sy < 23; sy++)
		{
			memcpy(&m_vram[sy * 40], &m_vram[(sy + 1) * 40], 40);
		}
		memset(&m_vram[23*40], 0, 40);
		m_cursy = 23;
	}
}

// CB2 here is connected two places: Port B bit 7 for CPU readback,
// and to the display hardware
void apple1_state::pia_display_gate_w(int state)
{
	m_pia->portb_w((state << 7) ^ 0x80);

	// falling edge means start the display timer
	if (state == CLEAR_LINE)
	{
		m_ready_start_timer->adjust(m_screen->time_until_pos(m_cursy, m_cursx));
	}
}

TIMER_CALLBACK_MEMBER(apple1_state::ready_start_cb)
{
	// we're ready, pulse CB1 for 3500 nanoseconds
	m_pia->cb1_w(0);
	m_ready_end_timer->adjust(attotime::from_nsec(3500));
}

TIMER_CALLBACK_MEMBER(apple1_state::ready_end_cb)
{
	m_pia->cb1_w(1);
}

TIMER_CALLBACK_MEMBER(apple1_state::keyboard_strobe_cb)
{
	m_pia->ca1_w(0);
}

static INPUT_PORTS_START( apple1 )
	PORT_START("KEY0")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)        PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)        PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)        PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)        PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)        PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)        PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)        PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)        PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)        PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)        PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)    PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)   PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)    PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)    PORT_CHAR('\'') PORT_CHAR('"')

	PORT_START("KEY1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)    PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)     PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)    PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)        PORT_CHAR('A')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)        PORT_CHAR('B')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)        PORT_CHAR('C')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)        PORT_CHAR('D')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)        PORT_CHAR('E')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)        PORT_CHAR('F')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)        PORT_CHAR('G')
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)        PORT_CHAR('H')
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)        PORT_CHAR('I')
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)        PORT_CHAR('J')
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)        PORT_CHAR('K')
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)        PORT_CHAR('L')

	PORT_START("KEY2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)        PORT_CHAR('M')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)        PORT_CHAR('N')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)        PORT_CHAR('O')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)        PORT_CHAR('P')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)        PORT_CHAR('Q')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)        PORT_CHAR('R')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)        PORT_CHAR('S')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)        PORT_CHAR('T')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)        PORT_CHAR('U')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)        PORT_CHAR('V')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)        PORT_CHAR('W')
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)        PORT_CHAR('X')
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)        PORT_CHAR('Y')
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)        PORT_CHAR('Z')
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)    PORT_CHAR(13)
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR('_')

	PORT_START("KEY3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Escape") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))

	PORT_START("KBSPECIAL")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Control") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Control") PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_F12) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Clear") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
INPUT_PORTS_END

static void apple1_cards(device_slot_interface &device)
{
	device.option_add("cassette", A1BUS_CASSETTE);
	device.option_add("cffa", A1BUS_CFFA);
}

void apple1_state::apple1(machine_config &config)
{
	M6502(config, m_maincpu, 960000);        // effective CPU speed
	m_maincpu->set_addrmap(AS_PROGRAM, &apple1_state::apple1_map);

	// video timings are identical to the Apple II, unsurprisingly
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(XTAL(14'318'181), (65*7)*2, 0, (40*7)*2, 262, 0, 192);
	m_screen->set_screen_update(FUNC(apple1_state::screen_update));
	m_screen->set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	PIA6821(config, m_pia);
	m_pia->readpa_handler().set(FUNC(apple1_state::pia_keyboard_r));
	m_pia->writepb_handler().set(FUNC(apple1_state::pia_display_w));
	m_pia->cb2_handler().set(FUNC(apple1_state::pia_display_gate_w));

	A1BUS(config, A1_BUS_TAG, 0).set_space(m_maincpu, AS_PROGRAM);
	A1BUS_SLOT(config, "exp", 0, A1_BUS_TAG, apple1_cards, "cassette");

	SNAPSHOT(config, "snapshot", "snp").set_load_callback(FUNC(apple1_state::snapshot_cb));

	SOFTWARE_LIST(config, "cass_list").set_original("apple1");

	RAM(config, RAM_TAG).set_default_size("48K").set_extra_options("4K,8K,12K,16K,20K,24K,28K,32K,36K,40K,44K");
}

ROM_START(apple1)
	ROM_REGION(0x100, A1_CPU_TAG, 0)
	ROM_LOAD_NIB_HIGH("apple-a2.a2", 0x0000, 0x0100, CRC(254bfb95) SHA1(b6468b72295b7d8ac288d104d252f24de1f1d611) )
	ROM_LOAD_NIB_LOW("apple-a1.a1", 0x0000, 0x0100, CRC(434f8ce6) SHA1(9deee2d39903209b20c3fc6b58e16372f8efece1) )
	ROM_REGION(0x0200, "gfx1",0)
	ROM_LOAD("s2513.d2", 0x0000, 0x0200, CRC(a7e567fc) SHA1(b18aae0a2d4f92f5a7e22640719bbc4652f3f4ee)) // apple1.vid
ROM_END

} // anonymous namespace


/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY           FULLNAME */
COMP( 1976, apple1, 0,      0,      apple1,  apple1, apple1_state, empty_init, "Apple Computer", "Apple I", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



apple2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    apple2.cpp - Apple II/II Plus and clones

    Next generation driver written in September/October 2014 by R. Belmont.
    Thanks to the original Apple II series driver's authors: Mike Balfour, Nathan Woods, and R. Belmont
    Special thanks to the Apple II Documentation Project/Antoine Vignau and Peter Ferrie.

II: original base model.  RAM sizes of 4, 8, 12, 16, 20, 24, 32, 36, and 48 KB possible.
    8K of ROM at $E000-$FFFF, empty sockets for $D000-$D7FF and $D800-$DFFF.
    Programmer's Aid #1 was sold by Apple for $D000-$D7FF, some third-party ROMs
    were also available.

    Revision 0 (very rare) had only 4 hi-res colors (blue and orange were missing).
    Revision 0 boards also did not include a color killer in text mode, making text
    fringey on color TVs/monitors.

    ROM contains original non-autostart Monitor and Integer BASIC; apparently
    Autostart + Integer is also possible.

II Plus: RAM options reduced to 16/32/48 KB.
    ROM expanded to 12KB from $D000-$FFFF containing Applesoft BASIC and
    the Autostart Monitor.  Applesoft is a licensed version of Microsoft's
    6502 BASIC as also found in Commodore and many other computers.


    Users of both models often connected the SHIFT key to the paddle #2 button
    (mapped to $C063) in order to inform properly written software that characters
    were to be intended upper/lower case.

    Both models commonly included a RAM "language card" in slot 0 which added 16K
    of RAM which could be banked into the $D000-$FFFF space to replace the ROMs.
    This allowed running Applesoft on a II and Integer BASIC on a II Plus.
    A II Plus with this card installed is often called a "64K Apple II"; this is
    the base configuration required to run ProDOS and some larger games.

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

#include "emu.h"

#include "apple2common.h"
#include "apple2video.h"

#include "bus/a2bus/a2bus.h"
#include "bus/a2bus/cards.h"
#include "bus/a2gameio/gameio.h"
#include "cpu/m6502/m6502.h"
#include "imagedev/cassette.h"
#include "machine/74259.h"
#include "machine/kb3600.h"
#include "machine/ram.h"
#include "machine/timer.h"

#include "sound/spkrdev.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "utf8.h"


namespace {

#define A2_CPU_TAG "maincpu"
#define A2_KBDC_TAG "ay3600"
#define A2_SPEAKER_TAG "speaker"
#define A2_CASSETTE_TAG "tape"
#define A2_UPPERBANK_TAG "inhbank"
#define A2_VIDEO_TAG "a2video"

class apple2_state : public driver_device
{
public:
	apple2_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, A2_CPU_TAG),
		m_screen(*this, "screen"),
		m_scantimer(*this, "scantimer"),
		m_ram(*this, RAM_TAG),
		m_ay3600(*this, A2_KBDC_TAG),
		m_video(*this, A2_VIDEO_TAG),
		m_a2common(*this, "a2common"),
		m_a2bus(*this, "a2bus"),
		m_gameio(*this, "gameio"),
		m_kbspecial(*this, "keyb_special"),
		m_kbrepeat(*this, "keyb_repeat"),
		m_resetdip(*this, "reset_dip"),
		m_sysconfig(*this, "a2_config"),
		m_speaker(*this, A2_SPEAKER_TAG),
		m_cassette(*this, A2_CASSETTE_TAG),
		m_softlatch(*this, "softlatch"),
		m_upperbank(*this, A2_UPPERBANK_TAG)
	{ }

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<timer_device> m_scantimer;
	required_device<ram_device> m_ram;
	required_device<ay3600_device> m_ay3600;
	required_device<a2_video_device_composite> m_video;
	required_device<apple2_common_device> m_a2common;
	required_device<a2bus_device> m_a2bus;
	required_device<apple2_gameio_device> m_gameio;
	required_ioport m_kbspecial;
	required_ioport m_kbrepeat;
	optional_ioport m_resetdip;
	required_ioport m_sysconfig;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cassette;
	required_device<addressable_latch_device> m_softlatch;
	memory_view m_upperbank;

	TIMER_DEVICE_CALLBACK_MEMBER(apple2_interrupt);
	TIMER_DEVICE_CALLBACK_MEMBER(ay3600_repeat);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	u8 ram_r(offs_t offset);
	void ram_w(offs_t offset, u8 data);
	u8 keyb_data_r();
	u8 keyb_strobe_r();
	void keyb_strobe_w(u8 data);
	u8 cassette_toggle_r();
	void cassette_toggle_w(u8 data);
	u8 speaker_toggle_r();
	void speaker_toggle_w(u8 data);
	u8 utility_strobe_r();
	void utility_strobe_w(u8 data);
	u8 switches_r(offs_t offset);
	u8 flags_r(offs_t offset);
	u8 controller_strobe_r();
	void controller_strobe_w(u8 data);
	u8 c080_r(offs_t offset);
	void c080_w(offs_t offset, u8 data);
	u8 c100_r(offs_t offset);
	void c100_w(offs_t offset, u8 data);
	u8 c800_r(offs_t offset);
	void c800_w(offs_t offset, u8 data);
	u8 inh_r(offs_t offset);
	void inh_w(offs_t offset, u8 data);
	void a2bus_irq_w(int state);
	void a2bus_nmi_w(int state);
	void a2bus_inh_w(int state);
	int ay3600_shift_r();
	int ay3600_control_r();
	void ay3600_data_ready_w(int state);
	void ay3600_ako_w(int state);

	void apple2_common(machine_config &config);
	void apple2jp(machine_config &config);
	void apple2(machine_config &config);
	void space84(machine_config &config);
	[[maybe_unused]] void dodo(machine_config &config);
	void albert(machine_config &config);
	void ivelultr(machine_config &config);
	void apple2p(machine_config &config);
	void apple2_map(address_map &map) ATTR_COLD;

private:
	int m_speaker_state, m_cassette_state;

	double m_joystick_x1_time, m_joystick_y1_time, m_joystick_x2_time, m_joystick_y2_time;

	u16 m_lastchar, m_strobe;
	u8 m_transchar;
	bool m_anykeydown;

	int m_inh_slot;
	int m_cnxx_slot;

	u8 *m_ram_ptr;
	int m_ram_size;

	int m_inh_bank;

	bool m_reset_latch;

	double m_x_calibration, m_y_calibration;

	device_a2bus_card_interface *m_slotdevice[8];

	u8 read_floatingbus();

	offs_t dasm_trampoline(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const util::disasm_interface::data_buffer &params);
};

/***************************************************************************
    PARAMETERS
***************************************************************************/

#define JOYSTICK_DELTA          80
#define JOYSTICK_SENSITIVITY    50
#define JOYSTICK_AUTOCENTER     80

offs_t apple2_state::dasm_trampoline(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const util::disasm_interface::data_buffer &params)
{
	return m_a2common->dasm_override(stream, pc, opcodes, params);
}

void apple2_state::a2bus_irq_w(int state)
{
	m_maincpu->set_input_line(M6502_IRQ_LINE, state);
}

void apple2_state::a2bus_nmi_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, state);
}

// This code makes a ton of assumptions because we can guarantee a pre-IIe machine!
void apple2_state::a2bus_inh_w(int state)
{
	if (state == ASSERT_LINE)
	{
		// assume no cards are pulling /INH
		m_inh_slot = -1;

		// scan the slots to figure out which card(s) are INHibiting stuff
		for (int i = 0; i <= 7; i++)
		{
			if (m_slotdevice[i])
			{
				// this driver only can inhibit from 0xd000-0xffff
				if ((m_slotdevice[i]->inh_start() == 0xd000) &&
					(m_slotdevice[i]->inh_end() == 0xffff))
				{
					if ((m_slotdevice[i]->inh_type() & INH_READ) == INH_READ)
					{
						if (m_inh_bank != 1)
						{
							m_upperbank.select(1);
							m_inh_bank = 1;
						}
					}
					else
					{
						if (m_inh_bank != 0)
						{
							m_upperbank.select(0);
							m_inh_bank = 0;
						}
					}

					m_inh_slot = i;
					break;
				}
			}
		}

		// if no slots are inhibiting, make sure ROM is fully switched in
		if ((m_inh_slot == -1) && (m_inh_bank != 0))
		{
			m_upperbank.select(0);
			m_inh_bank = 0;
		}
	}
}

/***************************************************************************
    START/RESET
***************************************************************************/

void apple2_state::machine_start()
{
	m_ram_ptr = m_ram->pointer();
	m_ram_size = m_ram->size();
	m_speaker_state = 0;
	m_speaker->level_w(m_speaker_state);
	m_cassette_state = 0;
	m_cassette->output(-1.0f);
	m_upperbank.select(0);
	m_inh_bank = 0;
	m_strobe = 0;
	m_transchar = 0;

	// precalculate joystick time constants
	m_x_calibration = attotime::from_nsec(10800).as_double();
	m_y_calibration = attotime::from_nsec(10800).as_double();

	// cache slot devices
	for (int i = 0; i <= 7; i++)
	{
		m_slotdevice[i] = m_a2bus->get_a2bus_card(i);
	}

	for (int adr = 0; adr < m_ram_size; adr += 2)
	{
		m_ram_ptr[adr] = 0;
		m_ram_ptr[adr+1] = 0xff;
	}

	m_joystick_x1_time = m_joystick_x2_time = m_joystick_y1_time = m_joystick_y2_time = 0;
	m_reset_latch = false;

	// setup save states
	save_item(NAME(m_speaker_state));
	save_item(NAME(m_cassette_state));
	save_item(NAME(m_joystick_x1_time));
	save_item(NAME(m_joystick_y1_time));
	save_item(NAME(m_joystick_x2_time));
	save_item(NAME(m_joystick_y2_time));
	save_item(NAME(m_lastchar));
	save_item(NAME(m_strobe));
	save_item(NAME(m_transchar));
	save_item(NAME(m_inh_slot));
	save_item(NAME(m_inh_bank));
	save_item(NAME(m_cnxx_slot));
	save_item(NAME(m_anykeydown));
	save_item(NAME(m_reset_latch));

	// setup video pointers
	m_video->set_ram_pointers(m_ram_ptr, m_ram_ptr);
	m_video->set_char_pointer(memregion("gfx1")->base(), memregion("gfx1")->bytes());
}

void apple2_state::machine_reset()
{
	m_inh_slot = 0;
	m_cnxx_slot = -1;
	m_anykeydown = false;

	// reset the cards
	m_a2bus->reset_bus();
	// reset the 6502 now as a card may have pulled /INH on the reset vector
	logerror("machine_reset\n");
	m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
	m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
}

/***************************************************************************
    VIDEO
***************************************************************************/

TIMER_DEVICE_CALLBACK_MEMBER(apple2_state::apple2_interrupt)
{
	int scanline = param;

	if (scanline == 192)
	{
		// check reset
		if ((m_resetdip.found()) && (m_resetdip->read() & 1)) // if reset DIP is present, use it
		{
			// CTRL-RESET
			if ((m_kbspecial->read() & 0x88) == 0x88)
			{
				if (!m_reset_latch)
				{
					m_reset_latch = true;
					m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
				}
			}
			else
			{
				if (m_reset_latch)
				{
					m_reset_latch = false;
					// allow cards to see reset
					m_a2bus->reset_bus();
					m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
				}
			}
		}
		else    // plain RESET
		{
			if (m_kbspecial->read() & 0x80)
			{
				if (!m_reset_latch)
				{
					m_reset_latch = true;
					m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
				}
			}
			else
			{
				if (m_reset_latch)
				{
					m_reset_latch = false;
					// allow cards to see reset
					m_a2bus->reset_bus();
					m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
				}
			}
		}
	}
}

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

u8 apple2_state::keyb_data_r()
{
	// keyboard latch
	return m_transchar | m_strobe;
}

u8 apple2_state::keyb_strobe_r()
{
	// reads floating bus, clears strobe
	if (!machine().side_effects_disabled())
	{
		m_strobe = 0;
	}
	return read_floatingbus();
}

void apple2_state::keyb_strobe_w(u8 data)
{
	// clear keyboard latch
	m_strobe = 0;
}

u8 apple2_state::cassette_toggle_r()
{
	if (!machine().side_effects_disabled())
		cassette_toggle_w(0);
	return read_floatingbus();
}

void apple2_state::cassette_toggle_w(u8 data)
{
	m_cassette_state ^= 1;
	m_cassette->output(m_cassette_state ? 1.0f : -1.0f);
}

u8 apple2_state::speaker_toggle_r()
{
	if (!machine().side_effects_disabled())
		speaker_toggle_w(0);
	return read_floatingbus();
}

void apple2_state::speaker_toggle_w(u8 data)
{
	m_speaker_state ^= 1;
	m_speaker->level_w(m_speaker_state);
}

u8 apple2_state::utility_strobe_r()
{
	if (!machine().side_effects_disabled())
		utility_strobe_w(0);
	return read_floatingbus();
}

void apple2_state::utility_strobe_w(u8 data)
{
	// low pulse on pin 5 of game I/O connector
	m_gameio->strobe_w(0);
	m_gameio->strobe_w(1);
}

u8 apple2_state::switches_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		m_softlatch->write_bit((offset & 0x0e) >> 1, offset & 0x01);
	return read_floatingbus();
}

u8 apple2_state::flags_r(offs_t offset)
{
	u8 uFloatingBus7 = read_floatingbus() & 0x7f;

	// Y output of 74LS251 at H14 read as D7
	switch (offset)
	{
	case 0: // cassette in (accidentally read at $C068 by ProDOS to attempt IIgs STATE register)
		return (m_cassette->input() > 0.0 ? 0x80 : 0) | uFloatingBus7;

	case 1:  // button 0
		return (m_gameio->sw0_r() ? 0x80 : 0) | uFloatingBus7;

	case 2:  // button 1
		return (m_gameio->sw1_r() ? 0x80 : 0) | uFloatingBus7;

	case 3:  // button 2
		// check if SHIFT key mod configured
		if (m_sysconfig->read() & 0x04)
		{
			return ((m_gameio->sw2_r() || (m_kbspecial->read() & 0x06)) ? 0x80 : 0) | uFloatingBus7;
		}
		return (m_gameio->sw2_r() ? 0x80 : 0) | (read_floatingbus() & 0x7f);

	case 4:  // joy 1 X axis
		if (!m_gameio->is_device_connected()) return 0x80 | uFloatingBus7;
		return ((machine().time().as_double() < m_joystick_x1_time) ? 0x80 : 0) | uFloatingBus7;

	case 5:  // joy 1 Y axis
		if (!m_gameio->is_device_connected()) return 0x80 | uFloatingBus7;
		return ((machine().time().as_double() < m_joystick_y1_time) ? 0x80 : 0) | uFloatingBus7;

	case 6: // joy 2 X axis
		if (!m_gameio->is_device_connected()) return 0x80 | uFloatingBus7;
		return ((machine().time().as_double() < m_joystick_x2_time) ? 0x80 : 0) | uFloatingBus7;

	case 7: // joy 2 Y axis
		if (!m_gameio->is_device_connected()) return 0x80 | uFloatingBus7;
		return ((machine().time().as_double() < m_joystick_y2_time) ? 0x80 : 0) | uFloatingBus7;
	}

	// this is never reached
	return 0;
}

u8 apple2_state::controller_strobe_r()
{
	if (!machine().side_effects_disabled())
		controller_strobe_w(0);
	return read_floatingbus();
}

void apple2_state::controller_strobe_w(u8 data)
{
	// 558 monostable one-shot timers; a running timer cannot be restarted
	if (machine().time().as_double() >= m_joystick_x1_time)
	{
		m_joystick_x1_time = machine().time().as_double() + m_x_calibration * m_gameio->pdl0_r();
	}
	if (machine().time().as_double() >= m_joystick_y1_time)
	{
		m_joystick_y1_time = machine().time().as_double() + m_y_calibration * m_gameio->pdl1_r();
	}
	if (machine().time().as_double() >= m_joystick_x2_time)
	{
		m_joystick_x2_time = machine().time().as_double() + m_x_calibration * m_gameio->pdl2_r();
	}
	if (machine().time().as_double() >= m_joystick_y2_time)
	{
		m_joystick_y2_time = machine().time().as_double() + m_y_calibration * m_gameio->pdl3_r();
	}

}

u8 apple2_state::c080_r(offs_t offset)
{
	if(!machine().side_effects_disabled())
	{
		int slot;

		offset &= 0x7F;
		slot = offset / 0x10;

		if (m_slotdevice[slot] != nullptr)
		{
			return m_slotdevice[slot]->read_c0nx(offset % 0x10);
		}
	}

	return read_floatingbus();
}

void apple2_state::c080_w(offs_t offset, u8 data)
{
	int slot;

	offset &= 0x7F;
	slot = offset / 0x10;

	if (m_slotdevice[slot] != nullptr)
	{
		m_slotdevice[slot]->write_c0nx(offset % 0x10, data);
	}
}

u8 apple2_state::c100_r(offs_t offset)
{
	int slotnum;

	slotnum = ((offset>>8) & 0xf) + 1;

	if (m_slotdevice[slotnum] != nullptr)
	{
		if ((m_slotdevice[slotnum]->take_c800()) && (!machine().side_effects_disabled()))
		{
			m_cnxx_slot = slotnum;
		}

		return m_slotdevice[slotnum]->read_cnxx(offset&0xff);
	}

	return read_floatingbus();
}

void apple2_state::c100_w(offs_t offset, u8 data)
{
	int slotnum;

	slotnum = ((offset>>8) & 0xf) + 1;

	if (m_slotdevice[slotnum] != nullptr)
	{
		if ((m_slotdevice[slotnum]->take_c800()) && (!machine().side_effects_disabled()))
		{
			m_cnxx_slot = slotnum;
		}

		m_slotdevice[slotnum]->write_cnxx(offset&0xff, data);
	}
}

u8 apple2_state::c800_r(offs_t offset)
{
	if (offset == 0x7ff)
	{
		u8 rv = 0xff;

		if ((m_cnxx_slot != -1) && (m_slotdevice[m_cnxx_slot] != nullptr))
		{
			rv = m_slotdevice[m_cnxx_slot]->read_c800(offset&0xfff);
		}

		if (!machine().side_effects_disabled())
		{
			m_cnxx_slot = -1;
		}

		return rv;
	}

	if ((m_cnxx_slot != -1) && (m_slotdevice[m_cnxx_slot] != nullptr))
	{
		return m_slotdevice[m_cnxx_slot]->read_c800(offset&0xfff);
	}

	return read_floatingbus();
}

void apple2_state::c800_w(offs_t offset, u8 data)
{
	if ((m_cnxx_slot != -1) && (m_slotdevice[m_cnxx_slot] != nullptr))
	{
		m_slotdevice[m_cnxx_slot]->write_c800(offset&0xfff, data);
	}

	if (offset == 0x7ff)
	{
		if (!machine().side_effects_disabled())
		{
			m_cnxx_slot = -1;
		}

		return;
	}
}

u8 apple2_state::inh_r(offs_t offset)
{
	if (m_inh_slot != -1)
	{
		return m_slotdevice[m_inh_slot]->read_inh_rom(offset + 0xd000);
	}

	assert(0);  // hitting inh_r with invalid m_inh_slot should not be possible
	return read_floatingbus();
}

void apple2_state::inh_w(offs_t offset, u8 data)
{
	if ((m_inh_slot != -1) && (m_slotdevice[m_inh_slot] != nullptr))
	{
		m_slotdevice[m_inh_slot]->write_inh_rom(offset + 0xd000, data);
	}
}

// floating bus code from old machine/apple2: now works reasonably well with French Touch and Deater "vapor lock" stuff
u8 apple2_state::read_floatingbus()
{
	enum
	{
		// scanner constants
		kHBurstClock      =    53, // clock when Color Burst starts
		kHBurstClocks     =     4, // clocks per Color Burst duration
		kHClock0State     =  0x18, // H[543210] = 011000
		kHClocks          =    65, // clocks per horizontal scan (including HBL)
		kHPEClock         =    40, // clock when HPE (horizontal preset enable) goes low
		kHPresetClock     =    41, // clock when H state presets
		kHSyncClock       =    49, // clock when HSync starts
		kHSyncClocks      =     4, // clocks per HSync duration
		kNTSCScanLines    =   262, // total scan lines including VBL (NTSC)
		kNTSCVSyncLine    =   224, // line when VSync starts (NTSC)
		kPALScanLines     =   312, // total scan lines including VBL (PAL)
		kPALVSyncLine     =   264, // line when VSync starts (PAL)
		kVLine0State      = 0x100, // V[543210CBA] = 100000000
		kVPresetLine      =   256, // line when V state presets
		kVSyncLines       =     4, // lines per VSync duration
		kClocksPerVSync   = kHClocks * kNTSCScanLines // FIX: NTSC only?
	};

	// vars
	//
	int i, Hires, Mixed, Page2, _80Store, ScanLines, /* VSyncLine, ScanCycles,*/
		h_clock, h_state, h_0, h_1, h_2, h_3, h_4, h_5,
		v_line, v_state, v_A, v_B, v_C, v_0, v_1, v_2, v_3, v_4, /* v_5, */
		_hires, addend0, addend1, addend2, sum, address;

	// video scanner data
	//
	i = m_maincpu->total_cycles() % kClocksPerVSync; // cycles into this VSync

	// machine state switches
	//
	Hires    = (m_video->get_hires() && m_video->get_graphics()) ? 1 : 0;
	Mixed    = m_video->get_mix() ? 1 : 0;
	Page2    = m_video->get_page2() ? 1 : 0;
	_80Store = 0;

	// calculate video parameters according to display standard
	//
	ScanLines  = 1 ? kNTSCScanLines : kPALScanLines; // FIX: NTSC only?
	// VSyncLine  = 1 ? kNTSCVSyncLine : kPALVSyncLine; // FIX: NTSC only?
	// ScanCycles = ScanLines * kHClocks;

	// calculate horizontal scanning state
	h_clock = (i + 63) % kHClocks; // which horizontal scanning clock
	h_state = kHClock0State + h_clock; // H state bits
	if (h_clock >= kHPresetClock) // check for horizontal preset
	{
		h_state -= 1; // correct for state preset (two 0 states)
	}
	h_0 = (h_state >> 0) & 1; // get horizontal state bits
	h_1 = (h_state >> 1) & 1;
	h_2 = (h_state >> 2) & 1;
	h_3 = (h_state >> 3) & 1;
	h_4 = (h_state >> 4) & 1;
	h_5 = (h_state >> 5) & 1;

	// calculate vertical scanning state
	//
	v_line  = (i / kHClocks) + 192; // which vertical scanning line
	v_state = kVLine0State + v_line; // V state bits
	if ((v_line >= kVPresetLine)) // check for previous vertical state preset
	{
		v_state -= ScanLines; // compensate for preset
	}
	v_A = (v_state >> 0) & 1; // get vertical state bits
	v_B = (v_state >> 1) & 1;
	v_C = (v_state >> 2) & 1;
	v_0 = (v_state >> 3) & 1;
	v_1 = (v_state >> 4) & 1;
	v_2 = (v_state >> 5) & 1;
	v_3 = (v_state >> 6) & 1;
	v_4 = (v_state >> 7) & 1;
	//v_5 = (v_state >> 8) & 1;

	// calculate scanning memory address
	//
	_hires = Hires;
	if (Hires && Mixed && (v_4 & v_2))
	{
		_hires = 0; // (address is in text memory)
	}

	addend0 = 0x68; // 1            1            0            1
	addend1 =              (h_5 << 5) | (h_4 << 4) | (h_3 << 3);
	addend2 = (v_4 << 6) | (v_3 << 5) | (v_4 << 4) | (v_3 << 3);
	sum     = (addend0 + addend1 + addend2) & (0x0F << 3);

	address = 0;
	address |= h_0 << 0; // a0
	address |= h_1 << 1; // a1
	address |= h_2 << 2; // a2
	address |= sum;      // a3 - aa6
	address |= v_0 << 7; // a7
	address |= v_1 << 8; // a8
	address |= v_2 << 9; // a9
	address |= ((_hires) ? v_A : (1 ^ (Page2 & (1 ^ _80Store)))) << 10; // a10
	address |= ((_hires) ? v_B : (Page2 & (1 ^ _80Store))) << 11; // a11
	if (_hires) // hires?
	{
		// Y: insert hires only address bits
		//
		address |= v_C << 12; // a12
		address |= (1 ^ (Page2 & (1 ^ _80Store))) << 13; // a13
		address |= (Page2 & (1 ^ _80Store)) << 14; // a14
	}
	else
	{
		// N: text, so no higher address bits unless Apple ][, not Apple //e
		//
		if ((kHPEClock <= h_clock) && // Y: HBL?
			(h_clock <= (kHClocks - 1)))
		{
			address |= 1 << 12; // Y: a12 (add $1000 to address!)
		}
	}

	return m_ram_ptr[address % m_ram_size];
}

/***************************************************************************
    ADDRESS MAP
***************************************************************************/

u8 apple2_state::ram_r(offs_t offset)
{
	if (offset < m_ram_size)
	{
		return m_ram_ptr[offset];
	}

	return 0xff;
}

void apple2_state::ram_w(offs_t offset, u8 data)
{
	if (offset < m_ram_size)
	{
		m_ram_ptr[offset] = data;
	}
}

void apple2_state::apple2_map(address_map &map)
{
	map(0x0000, 0xbfff).rw(FUNC(apple2_state::ram_r), FUNC(apple2_state::ram_w));
	map(0xc000, 0xc000).mirror(0xf).r(FUNC(apple2_state::keyb_data_r)).nopw();
	map(0xc010, 0xc010).mirror(0xf).rw(FUNC(apple2_state::keyb_strobe_r), FUNC(apple2_state::keyb_strobe_w));
	map(0xc020, 0xc020).mirror(0xf).rw(FUNC(apple2_state::cassette_toggle_r), FUNC(apple2_state::cassette_toggle_w));
	map(0xc030, 0xc030).mirror(0xf).rw(FUNC(apple2_state::speaker_toggle_r), FUNC(apple2_state::speaker_toggle_w));
	map(0xc040, 0xc040).mirror(0xf).rw(FUNC(apple2_state::utility_strobe_r), FUNC(apple2_state::utility_strobe_w));
	map(0xc050, 0xc05f).r(FUNC(apple2_state::switches_r)).w(m_softlatch, FUNC(addressable_latch_device::write_a0));
	map(0xc060, 0xc067).mirror(0x8).r(FUNC(apple2_state::flags_r)).nopw(); // includes IIgs STATE register, which ProDOS touches
	map(0xc070, 0xc070).mirror(0xf).rw(FUNC(apple2_state::controller_strobe_r), FUNC(apple2_state::controller_strobe_w));
	map(0xc080, 0xc0ff).rw(FUNC(apple2_state::c080_r), FUNC(apple2_state::c080_w));
	map(0xc100, 0xc7ff).rw(FUNC(apple2_state::c100_r), FUNC(apple2_state::c100_w));
	map(0xc800, 0xcfff).rw(FUNC(apple2_state::c800_r), FUNC(apple2_state::c800_w));
	map(0xd000, 0xffff).view(m_upperbank);
	m_upperbank[0](0xd000, 0xffff).rom().region("maincpu", 0x1000).w(FUNC(apple2_state::inh_w));
	m_upperbank[1](0xd000, 0xffff).rw(FUNC(apple2_state::inh_r), FUNC(apple2_state::inh_w));
}

/***************************************************************************
    KEYBOARD
***************************************************************************/

int apple2_state::ay3600_shift_r()
{
	// either shift key
	if (m_kbspecial->read() & 0x06)
	{
		return ASSERT_LINE;
	}

	return CLEAR_LINE;
}

int apple2_state::ay3600_control_r()
{
	if (m_kbspecial->read() & 0x08)
	{
		return ASSERT_LINE;
	}

	return CLEAR_LINE;
}

static const u8 a2_key_remap[0x32][4] =
{
/*    norm shft ctrl both */
	{ 0x33,0x23,0x33,0x23 },    /* 3 #     00     */
	{ 0x34,0x24,0x34,0x24 },    /* 4 $     01     */
	{ 0x35,0x25,0x35,0x25 },    /* 5 %     02     */
	{ 0x36,'&', 0x35,'&'  },    /* 6 &     03     */
	{ 0x37,0x27,0x37,0x27 },    /* 7 '     04     */
	{ 0x38,0x28,0x38,0x28 },    /* 8 (     05     */
	{ 0x39,0x29,0x39,0x29 },    /* 9 )     06     */
	{ 0x30,0x30,0x30,0x30 },    /* 0       07     */
	{ 0x3a,0x2a,0x3a,0x2a },    /* : *     08     */
	{ 0x2d,0x3d,0x2d,0x3d },    /* - =     09     */
	{ 0x51,0x51,0x11,0x11 },    /* q Q     0a     */
	{ 0x57,0x57,0x17,0x17 },    /* w W     0b     */
	{ 0x45,0x45,0x05,0x05 },    /* e E     0c     */
	{ 0x52,0x52,0x12,0x12 },    /* r R     0d     */
	{ 0x54,0x54,0x14,0x14 },    /* t T     0e     */
	{ 0x59,0x59,0x19,0x19 },    /* y Y     0f     */
	{ 0x55,0x55,0x15,0x15 },    /* u U     10     */
	{ 0x49,0x49,0x09,0x09 },    /* i I     11     */
	{ 0x4f,0x4f,0x0f,0x0f },    /* o O     12     */
	{ 0x50,0x40,0x10,0x00 },    /* p P     13     */
	{ 0x44,0x44,0x04,0x04 },    /* d D     14     */
	{ 0x46,0x46,0x06,0x06 },    /* f F     15     */
	{ 0x47,0x47,0x07,0x07 },    /* g G     16     */
	{ 0x48,0x48,0x08,0x08 },    /* h H     17     */
	{ 0x4a,0x4a,0x0a,0x0a },    /* j J     18     */
	{ 0x4b,0x4b,0x0b,0x0b },    /* k K     19     */
	{ 0x4c,0x4c,0x0c,0x0c },    /* l L     1a     */
	{ ';' ,0x2b,';' ,0x2b },    /* ; +     1b     */
	{ 0x08,0x08,0x08,0x08 },    /* Left    1c     */
	{ 0x15,0x15,0x15,0x15 },    /* Right   1d     */
	{ 0x5a,0x5a,0x1a,0x1a },    /* z Z     1e     */
	{ 0x58,0x58,0x18,0x18 },    /* x X     1f     */
	{ 0x43,0x43,0x03,0x03 },    /* c C     20     */
	{ 0x56,0x56,0x16,0x16 },    /* v V     21     */
	{ 0x42,0x42,0x02,0x02 },    /* b B     22     */
	{ 0x4e,0x5e,0x0e,0x1e },    /* n N     23     */
	{ 0x4d,0x5d,0x0d,0x1d },    /* m M     24     */
	{ 0x2c,0x3c,0x2c,0x3c },    /* , <     25     */
	{ 0x2e,0x3e,0x2e,0x3e },    /* . >     26     */
	{ 0x2f,0x3f,0x2f,0x3f },    /* / ?     27     */
	{ 0x53,0x53,0x13,0x13 },    /* s S     28     */
	{ 0x32,0x22,0x32,0x00 },    /* 2 "     29     */
	{ 0x31,0x21,0x31,0x31 },    /* 1 !     2a     */
	{ 0x1b,0x1b,0x1b,0x1b },    /* Escape  2b     */
	{ 0x41,0x41,0x01,0x01 },    /* a A     2c     */
	{ 0x20,0x20,0x20,0x20 },    /* Space   2d     */
	{ 0x00,0x00,0x20,0x00 },    /* 0x2e unused    */
	{ 0x00,0x00,0x20,0x00 },    /* 0x2f unused    */
	{ 0x00,0x00,0x20,0x00 },    /* 0x30 unused    */
	{ 0x0d,0x0d,0x0d,0x0d },    /* Enter   31     */
};

void apple2_state::ay3600_data_ready_w(int state)
{
	if (state == ASSERT_LINE)
	{
		int mod = 0;

		// if the user presses a valid key to start the driver from the info screen,
		// we will see that key.  ignore keys in the first 25,000 cycles (in my tests,
		// the unwanted key shows up at 17030 cycles)
		if (m_maincpu->total_cycles() < 25000)
		{
			return;
		}

		m_lastchar = m_ay3600->b_r();

		mod = (m_kbspecial->read() & 0x06) ? 0x01 : 0x00;
		mod |= (m_kbspecial->read() & 0x08) ? 0x02 : 0x00;

		m_transchar = a2_key_remap[m_lastchar&0x3f][mod];
		m_strobe = 0x80;
//      printf("new char = %04x (%02x)\n", m_lastchar&0x3f, m_transchar);
	}
}

void apple2_state::ay3600_ako_w(int state)
{
	m_anykeydown = (state == ASSERT_LINE) ? true : false;
}

TIMER_DEVICE_CALLBACK_MEMBER(apple2_state::ay3600_repeat)
{
	// is the key still down?
	if (m_anykeydown)
	{
		if (m_kbrepeat->read() & 1)
		{
			m_strobe = 0x80;
		}
	}
}

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

INPUT_PORTS_START( apple2_sysconfig )
	PORT_START("a2_config")
	PORT_CONFNAME(0x04, 0x04, "Shift key mod")  // default to installed
	PORT_CONFSETTING(0x00, "Not present")
	PORT_CONFSETTING(0x04, "Installed")
INPUT_PORTS_END

	/*
	  Apple II / II Plus key matrix (from "The Apple II Circuit Description")

	      | Y0  | Y1  | Y2  | Y3  | Y4  | Y5  | Y6  | Y7  | Y8  | Y9  |
	      |     |     |     |     |     |     |     |     |     |     |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X0  |  3  |  4  |  5  |  6  |  7  |  8  |  9  |  0  | :*  |  -  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X1  |  Q  |  W  |  E  |  R  |  T  |  Y  |  U  |  I  |  O  |  P  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X2  |  D  |  F  |  G  |  H  |  J  |  K  |  L  | ;+  |LEFT |RIGHT|
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X3  |  Z  |  X  |  C  |  V  |  B  |  N  |  M  | ,<  | .>  |  /? |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X4  |  S  |  2  |  1  | ESC |  A  |SPACE|     |     |     |ENTER|
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	*/

static INPUT_PORTS_START( apple2_common )
	PORT_START("X0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)  PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)  PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)  PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)  PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)  PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)  PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)  PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)  PORT_CHAR('0')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)  PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('-') PORT_CHAR('=')

	PORT_START("X1")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)  PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)  PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)  PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)  PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)  PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)  PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)  PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)  PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)  PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)  PORT_CHAR('P') PORT_CHAR('@')

	PORT_START("X2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)  PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)  PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)  PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)  PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)  PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)  PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)  PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT)      PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT)     PORT_CODE(KEYCODE_RIGHT)

	PORT_START("X3")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)  PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)  PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)  PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)  PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)  PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)  PORT_CHAR('N') PORT_CHAR('^')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)  PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)  PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)   PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)  PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("X4")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)  PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)  PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)  PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Esc")      PORT_CODE(KEYCODE_ESC)      PORT_CHAR(27)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)  PORT_CHAR(' ')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Return")   PORT_CODE(KEYCODE_ENTER)    PORT_CHAR(13)

	PORT_START("X5")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X6")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X7")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X8")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("keyb_special")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Shift")   PORT_CODE(KEYCODE_LSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Shift")  PORT_CODE(KEYCODE_RSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Control")      PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RESET")        PORT_CODE(KEYCODE_F12)
INPUT_PORTS_END

static INPUT_PORTS_START( apple2 )
	PORT_INCLUDE(apple2_common)

	PORT_START("keyb_repeat")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("REPT")         PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\')

	PORT_INCLUDE(apple2_sysconfig)
INPUT_PORTS_END

static INPUT_PORTS_START( apple2p )
	PORT_INCLUDE( apple2 )

	PORT_START("reset_dip")
	PORT_DIPNAME( 0x01, 0x01, "Reset" )
	PORT_DIPSETTING( 0x01, "CTRL-RESET" )
	PORT_DIPSETTING( 0x00, "RESET" )
INPUT_PORTS_END

void apple2_state::apple2_common(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, 1021800);
	m_maincpu->set_addrmap(AS_PROGRAM, &apple2_state::apple2_map);
	m_maincpu->set_dasm_override(FUNC(apple2_state::dasm_trampoline));

	TIMER(config, m_scantimer, 0);
	m_scantimer->configure_scanline(FUNC(apple2_state::apple2_interrupt), "screen", 0, 1);
	config.set_maximum_quantum(attotime::from_hz(60));

	APPLE2_VIDEO_COMPOSITE(config, m_video, XTAL(14'318'181)).set_screen(m_screen);
	APPLE2_COMMON(config, m_a2common, XTAL(14'318'181));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(1021800*14, (65*7)*2, 0, (40*7)*2, 262, 0, 192);
	m_screen->set_screen_update(m_video, NAME((&a2_video_device::screen_update<a2_video_device::model::II, true, true>)));
	m_screen->set_palette(m_video);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, A2_SPEAKER_TAG).add_route(ALL_OUTPUTS, "mono", 0.4);

	/* soft switches */
	F9334(config, m_softlatch); // F14 (labeled 74LS259 on some boards and in the Apple ][ Reference Manual)
	m_softlatch->q_out_cb<0>().set(m_video, FUNC(a2_video_device::txt_w));
	m_softlatch->q_out_cb<1>().set(m_video, FUNC(a2_video_device::mix_w));
	m_softlatch->q_out_cb<2>().set(m_video, FUNC(a2_video_device::scr_w));
	m_softlatch->q_out_cb<3>().set(m_video, FUNC(a2_video_device::res_w));
	m_softlatch->q_out_cb<4>().set(m_gameio, FUNC(apple2_gameio_device::an0_w));
	m_softlatch->q_out_cb<5>().set(m_gameio, FUNC(apple2_gameio_device::an1_w));
	m_softlatch->q_out_cb<6>().set(m_gameio, FUNC(apple2_gameio_device::an2_w));
	m_softlatch->q_out_cb<6>().append(m_video, FUNC(a2_video_device::an2_w));
	m_softlatch->q_out_cb<7>().set(m_gameio, FUNC(apple2_gameio_device::an3_w));

	APPLE2_GAMEIO(config, m_gameio, m_screen, apple2_gameio_device::iiandplus_options, nullptr);

	/* keyboard controller */
	AY3600(config, m_ay3600, 0);
	m_ay3600->x0().set_ioport("X0");
	m_ay3600->x1().set_ioport("X1");
	m_ay3600->x2().set_ioport("X2");
	m_ay3600->x3().set_ioport("X3");
	m_ay3600->x4().set_ioport("X4");
	m_ay3600->x5().set_ioport("X5");
	m_ay3600->x6().set_ioport("X6");
	m_ay3600->x7().set_ioport("X7");
	m_ay3600->x8().set_ioport("X8");
	m_ay3600->shift().set(FUNC(apple2_state::ay3600_shift_r));
	m_ay3600->control().set(FUNC(apple2_state::ay3600_control_r));
	m_ay3600->data_ready().set(FUNC(apple2_state::ay3600_data_ready_w));
	m_ay3600->ako().set(FUNC(apple2_state::ay3600_ako_w));

	/* repeat timer.  15 Hz from page 90 of "The Apple II Circuit Description */
	TIMER(config, "repttmr", 0).configure_periodic(FUNC(apple2_state::ay3600_repeat), attotime::from_hz(15));

	/* slot devices */
	A2BUS(config, m_a2bus, 0);
	m_a2bus->set_space(m_maincpu, AS_PROGRAM);
	m_a2bus->irq_w().set(FUNC(apple2_state::a2bus_irq_w));
	m_a2bus->nmi_w().set(FUNC(apple2_state::a2bus_nmi_w));
	m_a2bus->inh_w().set(FUNC(apple2_state::a2bus_inh_w));
	m_a2bus->dma_w().set_inputline(m_maincpu, INPUT_LINE_HALT);
	A2BUS_SLOT(config, "sl0", XTAL(14'318'181) / 2, m_a2bus, apple2_slot0_cards, "lang");
	A2BUS_SLOT(config, "sl1", XTAL(14'318'181) / 2, m_a2bus, apple2_cards, nullptr);
	A2BUS_SLOT(config, "sl2", XTAL(14'318'181) / 2, m_a2bus, apple2_cards, nullptr);
	A2BUS_SLOT(config, "sl3", XTAL(14'318'181) / 2, m_a2bus, apple2_cards, nullptr);
	A2BUS_SLOT(config, "sl4", XTAL(14'318'181) / 2, m_a2bus, apple2_cards, "mockingboard");
	A2BUS_SLOT(config, "sl5", XTAL(14'318'181) / 2, m_a2bus, apple2_cards, nullptr);
	A2BUS_SLOT(config, "sl6", XTAL(14'318'181) / 2, m_a2bus, apple2_cards, "diskiing");
	A2BUS_SLOT(config, "sl7", XTAL(14'318'181) / 2, m_a2bus, apple2_cards, nullptr);

	/* Set up the softlists: clean cracks priority, originals second, others last */
	SOFTWARE_LIST(config, "flop_a2_clean").set_original("apple2_flop_clcracked");
	SOFTWARE_LIST(config, "flop_a2_orig").set_compatible("apple2_flop_orig").set_filter("A2");
	SOFTWARE_LIST(config, "flop_a2_misc").set_compatible("apple2_flop_misc");
	SOFTWARE_LIST(config, "cass_list").set_original("apple2_cass");
	//MCFG_SOFTWARE_LIST_ADD("cass_list", "apple2_cass")

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED);
	m_cassette->set_interface("apple2_cass");
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
}

void apple2_state::apple2(machine_config &config)
{
	apple2_common(config);
	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("48K").set_extra_options("4K,8K,12K,16K,20K,24K,32K,36K,48K").set_default_value(0x00);
}

void apple2_state::apple2p(machine_config &config)
{
	apple2_common(config);
	subdevice<software_list_device>("flop_a2_orig")->set_filter("A2P"); // Filter list to compatible disks for this machine.
	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("48K").set_extra_options("16K,32K,48K").set_default_value(0x00);
}

void apple2_state::space84(machine_config &config)
{
	apple2p(config);
}

void apple2_state::apple2jp(machine_config &config)
{
	apple2p(config);
	m_screen->set_screen_update(m_video, NAME((&a2_video_device::screen_update<a2_video_device::model::II_J_PLUS, true, true>)));
}

void apple2_state::dodo(machine_config &config)
{
	apple2p(config);
	m_screen->set_screen_update(m_video, NAME((&a2_video_device::screen_update<a2_video_device::model::II, true, false>)));
}

void apple2_state::albert(machine_config &config)
{
	apple2p(config);
	m_screen->set_screen_update(m_video, NAME((&a2_video_device::screen_update<a2_video_device::model::II, false, true>)));
}

void apple2_state::ivelultr(machine_config &config)
{
	apple2p(config);
	m_screen->set_screen_update(m_video, NAME((&a2_video_device::screen_update<a2_video_device::model::IVEL_ULTRA, true, false>)));
}

#if 0
void apple2_state::laba2p(machine_config &config)
{
	apple2p(config);
	MCFG_MACHINE_START_OVERRIDE(apple2_state,laba2p)

	config.device_remove("sl0");
	config.device_remove("sl3");
	config.device_remove("sl6");

//  A2BUS_LAB_80COL("sl3", A2BUS_LAB_80COL).set_onboard(m_a2bus);
	A2BUS_IWM_FDC("sl6", A2BUS_IWM_FDC).set_onboard(m_a2bus);
}
#endif

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

  Game driver(s)

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

ROM_START(apple2) /* the classic, non-autoboot apple2 with integer basic in rom. optional card with autoboot and applesoft basic was possible but isn't yet supported */
	ROM_REGION(0x0800,"gfx1",0)
	// This is a GI RO-3-2513 on Rev. 0 Apple ][s, as per http://www.solivant.com/php/eview.php?album=appleII&filen=11 which shows serial #97
	// However, the presence of the lo-res patterns means it's a customized-mask variant, and not the same as the Apple I's 2513 that truly is stock.
	ROM_LOAD ( "a2.chr", 0x0000, 0x0800, BAD_DUMP CRC(64f415c6) SHA1(f9d312f128c9557d9d6ac03bfad6c3ddf83e5659)) /* current dump is 341-0036 which is the appleII+ character generator, not the original appleII one, whose rom number is not yet known! */

	ROM_REGION(0x4000,"maincpu",0)
	ROM_LOAD_OPTIONAL ( "341-0016-00.d0", 0x1000, 0x0800, CRC(4234e88a) SHA1(c9a81d704dc2f0c3416c20f9c4ab71fedda937ed)) /* 341-0016: Programmer's Aid #1 D0 */

	ROM_LOAD ( "341-0001-00.e0", 0x2000, 0x0800, CRC(c0a4ad3b) SHA1(bf32195efcb34b694c893c2d342321ec3a24b98f)) /* Needs verification. From eBay: Label: S7925E // C48077 // 3410001-00 // (C)APPLE78 E0 */
	ROM_LOAD ( "341-0002-00.e8", 0x2800, 0x0800, CRC(a99c2cf6) SHA1(9767d92d04fc65c626223f25564cca31f5248980)) /* Needs verification. From eBay: Label: S7916E // C48078 // 3410002-00 // (C)APPLE78 E8 */
	ROM_LOAD ( "341-0003-00.f0", 0x3000, 0x0800, CRC(62230d38) SHA1(f268022da555e4c809ca1ae9e5d2f00b388ff61c)) /* Needs verification. From eBay: Label: S7908E // C48709 // 3410003 // CAPPLE78 F0 */
	ROM_SYSTEM_BIOS(0, "default", "Original Monitor")
	ROMX_LOAD ( "341-0004-00.f8", 0x3800, 0x0800, CRC(020a86d0) SHA1(52a18bd578a4694420009cad7a7a5779a8c00226), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "autostart", "Autostart Monitor")
	ROMX_LOAD ( "341-0020-00.f8", 0x3800, 0x0800, CRC(079589c4) SHA1(a28852ff997b4790e53d8d0352112c4b1a395098), ROM_BIOS(1)) /* 341-0020-00: Autostart Monitor/Applesoft Basic $f800; Was sometimes mounted on Language card; Label(from Apple Language Card - Front.jpg): S 8115 // C68018 // 341-0020-00 */
ROM_END

ROM_START(apple2p) /* the autoboot apple2+ with applesoft (microsoft-written) basic in rom; optional card with monitor and integer basic was possible but isn't yet supported */
	ROM_REGION(0x0800,"gfx1",0)
	ROM_LOAD ( "341-0036.chr", 0x0000, 0x0800, CRC(64f415c6) SHA1(f9d312f128c9557d9d6ac03bfad6c3ddf83e5659))

	ROM_REGION(0x4000, "maincpu", ROMREGION_LE)
	ROM_LOAD ( "341-0011.d0", 0x1000, 0x0800, CRC(6f05f949) SHA1(0287ebcef2c1ce11dc71be15a99d2d7e0e128b1e))
	ROM_LOAD ( "341-0012.d8", 0x1800, 0x0800, CRC(1f08087c) SHA1(a75ce5aab6401355bf1ab01b04e4946a424879b5))
	ROM_LOAD ( "341-0013.e0", 0x2000, 0x0800, CRC(2b8d9a89) SHA1(8d82a1da63224859bd619005fab62c4714b25dd7))
	ROM_LOAD ( "341-0014.e8", 0x2800, 0x0800, CRC(5719871a) SHA1(37501be96d36d041667c15d63e0c1eff2f7dd4e9))
	ROM_LOAD ( "341-0015.f0", 0x3000, 0x0800, CRC(9a04eecf) SHA1(e6bf91ed28464f42b807f798fc6422e5948bf581))
	ROM_LOAD ( "341-0020-00.f8", 0x3800, 0x0800, CRC(079589c4) SHA1(a28852ff997b4790e53d8d0352112c4b1a395098)) /* 341-0020-00: Autostart Monitor/Applesoft Basic $f800; Was sometimes mounted on Language card; Label(from Apple Language Card - Front.jpg): S 8115 // C68018 // 341-0020-00 */
ROM_END

ROM_START(ace1000)
	ROM_REGION(0x1000,"gfx1",0)
	ROM_LOAD ( "1331409.u7", 0x0000, 0x1000, CRC(744c06e1) SHA1(e1c11495ee538f658d2918bfece8c4629f60fa13))

	ROM_REGION(0x4000, "maincpu", ROMREGION_LE)
	ROM_LOAD ( "1331401.g2", 0x3800, 0x0800, CRC(047d6fed) SHA1(83d225dc3f1a7bd6901cc24cd02287402022469f))
	ROM_LOAD ( "1331402.g3", 0x3000, 0x0800, CRC(9a04eecf) SHA1(e6bf91ed28464f42b807f798fc6422e5948bf581))
	ROM_LOAD ( "1331403.g5", 0x2800, 0x0800, CRC(5719871a) SHA1(37501be96d36d041667c15d63e0c1eff2f7dd4e9))
	ROM_LOAD ( "1331404.g7", 0x2000, 0x0800, CRC(1bee5169) SHA1(0cd57b4a2a79e0fc7f35619edc1be00952947b82))
	ROM_LOAD ( "1331405.g8", 0x1800, 0x0800, CRC(2a63f50a) SHA1(7cf424e7adbc84a4aa6f11d0132bf494bbb6a247))
	ROM_LOAD ( "1331406.g10", 0x1000, 0x0800, CRC(bfdd3cc6) SHA1(20067d27eb3b5bb03e7b560e44383e0926e39cbb))
ROM_END

ROM_START(elppa)
	ROM_REGION(0x0800,"gfx1",0)
	ROM_LOAD ( "elppa.chr", 0x0000, 0x0800, BAD_DUMP CRC(64f415c6) SHA1(f9d312f128c9557d9d6ac03bfad6c3ddf83e5659)) // Taken from 341-0036.chr used in apple2p

	ROM_REGION(0x4000,"maincpu",0)
	ROM_LOAD ( "elppa.d0", 0x1000, 0x0800, CRC(ce5b0e7e) SHA1(2c1a0aa023ae6deb2bddb8937345ee354028aeef))
	ROM_LOAD ( "elppa.d8", 0x1800, 0x0800, CRC(bd409bad) SHA1(5145d238042938efbb9b71e0a4ef9a980b0e38de))
	ROM_LOAD ( "elppa.e0", 0x2000, 0x0800, CRC(4c997c88) SHA1(70b639d8cbafcd5367d2f9dfd6890e5d1c6890f0))
	ROM_LOAD ( "elppa.e8", 0x2800, 0x0800, CRC(5719871a) SHA1(37501be96d36d041667c15d63e0c1eff2f7dd4e9))
	ROM_LOAD ( "elppa.f0", 0x3000, 0x0800, CRC(9a04eecf) SHA1(e6bf91ed28464f42b807f798fc6422e5948bf581))
	ROM_LOAD ( "elppa.f8", 0x3800, 0x0800, CRC(62c0c761) SHA1(19f28544fd5021a2d72e6015b3183c462c0e86f8))
ROM_END

ROM_START(prav82)
	ROM_REGION(0x0800,"gfx1",0)
	ROM_LOAD("pravetz82_chr.bin", 0x000000, 0x000800, CRC(c5d6bbc2) SHA1(b2074675d1890b5d1f0a14ed1758665f190ea3c7))

	ROM_REGION(0x4000,"maincpu",0)
	ROM_LOAD ( "pravetz82.d0", 0x1000, 0x0800, CRC(6f05f949) SHA1(0287ebcef2c1ce11dc71be15a99d2d7e0e128b1e))
	ROM_LOAD ( "pravetz82.d8", 0x1800, 0x0800, CRC(1f08087c) SHA1(a75ce5aab6401355bf1ab01b04e4946a424879b5))
	ROM_LOAD ( "pravetz82.e0", 0x2000, 0x0800, CRC(2b8d9a89) SHA1(8d82a1da63224859bd619005fab62c4714b25dd7))
	ROM_LOAD ( "pravetz82.e8", 0x2800, 0x0800, CRC(5719871a) SHA1(37501be96d36d041667c15d63e0c1eff2f7dd4e9))
	ROM_LOAD ( "pravetz82.f0", 0x3000, 0x0800, CRC(e26d9d35) SHA1(ce6e42e6c9a6c98e92522af7a6090cd04c56c778))
	ROM_LOAD ( "pravetz82.f8", 0x3800, 0x0800, CRC(57547818) SHA1(db30bedec98305e31a14acb9e2a92be1c4853807))
ROM_END

ROM_START(prav8m)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("pravetz8m_chr.bin", 0x000000, 0x002000, CRC(72244022) SHA1(4db7544e049bc7aeab4b4da2f8ef9fbeb3ceff24))

	ROM_REGION(0x4000,"maincpu",0)
	ROM_LOAD ( "pravetz8m.d0", 0x1000, 0x0800, CRC(6f05f949) SHA1(0287ebcef2c1ce11dc71be15a99d2d7e0e128b1e))
	ROM_LOAD ( "pravetz8m.d8", 0x1800, 0x0800, CRC(654b6f7b) SHA1(f7b1457b48fe6974c4de7e976df3a8fca6b7b661))
	ROM_LOAD ( "pravetz8m.e0", 0x2000, 0x0800, CRC(2b8d9a89) SHA1(8d82a1da63224859bd619005fab62c4714b25dd7))
	ROM_LOAD ( "pravetz8m.e8", 0x2800, 0x0800, CRC(5719871a) SHA1(37501be96d36d041667c15d63e0c1eff2f7dd4e9))
	ROM_LOAD ( "pravetz8m.f0", 0x3000, 0x0800, CRC(e26d9d35) SHA1(ce6e42e6c9a6c98e92522af7a6090cd04c56c778))
	ROM_LOAD ( "pravetz8m.f8", 0x3800, 0x0800, CRC(5bab0a46) SHA1(f6c0817ce37d2e2c43f482c339acaede0a73359b))
ROM_END

ROM_START(craft2p)
	ROM_REGION(0x1000,"gfx1",0)
	ROM_LOAD( "gc.bin",       0x000000, 0x001000, CRC(93e4a754) SHA1(25f5f5fd1cbd763d43362e80de3acc5b34a25963) )

	ROM_REGION(0x4000,"maincpu",0)
	// the d0 and e0 ROMs match the Unitron English ones, only f0 differs
	ROM_LOAD ( "unitron_en.d0", 0x1000, 0x1000, CRC(24d73c7b) SHA1(d17a15868dc875c67061c95ec53a6b2699d3a425))
	ROM_LOAD ( "unitron.e0"   , 0x2000, 0x1000, CRC(0d494efd) SHA1(a2fd1223a3ca0cfee24a6afe66ea3c4c144dd98e))
	ROM_LOAD ( "craftii-roms-f0-f7.bin", 0x3000, 0x1000, CRC(3f9dea08) SHA1(0e23bc884b8108675267d30b85b770066bdd94c9) )
ROM_END

ROM_START(uniap2pt)
	ROM_REGION(0x1000,"gfx1",0)
	ROM_LOAD ( "unitron.chr", 0x0000, 0x1000, CRC(7fdd1af6) SHA1(2f4f90d90f2f3a8c1fbea304e1072780fb22e698))

	ROM_REGION(0x4000,"maincpu",0)
	ROM_LOAD ( "unitron_pt.d0", 0x1000, 0x1000, CRC(311beae6) SHA1(f6379aba9ac982850edc314c93a393844a3349ef))
	ROM_LOAD ( "unitron.e0"   , 0x2000, 0x1000, CRC(0d494efd) SHA1(a2fd1223a3ca0cfee24a6afe66ea3c4c144dd98e))
	ROM_LOAD ( "unitron.f0"   , 0x3000, 0x1000, CRC(8e047c4a) SHA1(78c57c0e00dfce7fdec9437fe2b4c25def447e5d))
ROM_END

ROM_START(uniap2en)
	ROM_REGION(0x1000,"gfx1",0)
	ROM_LOAD ( "unitron.chr", 0x0000, 0x1000, CRC(7fdd1af6) SHA1(2f4f90d90f2f3a8c1fbea304e1072780fb22e698))

	ROM_REGION(0x4000,"maincpu",0)
	ROM_LOAD ( "unitron_en.d0", 0x1000, 0x1000, CRC(24d73c7b) SHA1(d17a15868dc875c67061c95ec53a6b2699d3a425))
	ROM_LOAD ( "unitron.e0"   , 0x2000, 0x1000, CRC(0d494efd) SHA1(a2fd1223a3ca0cfee24a6afe66ea3c4c144dd98e))
	ROM_LOAD ( "unitron.f0"   , 0x3000, 0x1000, CRC(8e047c4a) SHA1(78c57c0e00dfce7fdec9437fe2b4c25def447e5d))
ROM_END

ROM_START(uniap2ti) /* "Teclado Inteligente" means "smart keyboard" in brazilian portuguese */
	ROM_REGION(0x1000,"gfx1",0)
	ROM_LOAD ( "unitron.chr", 0x0000, 0x1000, CRC(7fdd1af6) SHA1(2f4f90d90f2f3a8c1fbea304e1072780fb22e698))

	ROM_REGION(0x4000,"maincpu",0)
	ROM_LOAD ( "unitron_pt.d0", 0x1000, 0x1000, CRC(311beae6) SHA1(f6379aba9ac982850edc314c93a393844a3349ef))
	ROM_LOAD ( "unitron.e0"   , 0x2000, 0x1000, CRC(0d494efd) SHA1(a2fd1223a3ca0cfee24a6afe66ea3c4c144dd98e))
	ROM_LOAD ( "unitron.f0"   , 0x3000, 0x1000, CRC(8e047c4a) SHA1(78c57c0e00dfce7fdec9437fe2b4c25def447e5d))

	ROM_REGION(0x4000,"keyboard",0)
	ROM_LOAD ( "unitron_apii+_keyboard.ic3", 0x0800, 0x0800, CRC(edc43205) SHA1(220cc21d86f1ab63a301ae7a9c5ff0f3f6cddb70))
ROM_END

ROM_START(microeng)
	ROM_REGION(0x0800,"gfx1",0)
	ROM_LOAD ( "microengenho_6c.bin", 0x0000, 0x0800, CRC(64f415c6) SHA1(f9d312f128c9557d9d6ac03bfad6c3ddf83e5659))

	ROM_REGION(0x4000,"maincpu",0)
	ROM_LOAD ( "microengenho_d0_d8.bin", 0x1000, 0x1000, CRC(834eabf4) SHA1(9a2385c6df16e5f5d15b79da17d21bf0f99dbd08))
	ROM_LOAD ( "microengenho_e0_e8.bin", 0x2000, 0x1000, CRC(0d494efd) SHA1(a2fd1223a3ca0cfee24a6afe66ea3c4c144dd98e))
	ROM_LOAD ( "microengenho_f0_f8.bin", 0x3000, 0x1000, CRC(588717cf) SHA1(e2a867c4a390d65e5ea181a4f933abb9992e4a63))
ROM_END

/*
    J-Plus ROM numbers confirmed by:
    http://mirrors.apple2.org.za/Apple%20II%20Documentation%20Project/Computers/Apple%20II/Apple%20II%20j-plus/Photos/Apple%20II%20j-plus%20-%20Motherboard.jpg

*/

ROM_START(apple2jp)
	ROM_REGION(0x0800,"gfx1",0)
	// probably a custom-mask variant of the Signetics 2513N or equivalent
	ROM_LOAD ( "a2jp.chr", 0x0000, 0x0800, CRC(487104b5) SHA1(0a382be58db5215c4a3de53b19a72fab660d5da2))

	ROM_REGION(0x4000,"maincpu",0)
	ROM_LOAD ( "341-0011.d0", 0x1000, 0x0800, CRC(6f05f949) SHA1(0287ebcef2c1ce11dc71be15a99d2d7e0e128b1e))
	ROM_LOAD ( "341-0012.d8", 0x1800, 0x0800, CRC(1f08087c) SHA1(a75ce5aab6401355bf1ab01b04e4946a424879b5))
	ROM_LOAD ( "341-0013.e0", 0x2000, 0x0800, CRC(2b8d9a89) SHA1(8d82a1da63224859bd619005fab62c4714b25dd7))
	ROM_LOAD ( "341-0014.e8", 0x2800, 0x0800, CRC(5719871a) SHA1(37501be96d36d041667c15d63e0c1eff2f7dd4e9))
	ROM_LOAD ( "341-0015.f0", 0x3000, 0x0800, CRC(9a04eecf) SHA1(e6bf91ed28464f42b807f798fc6422e5948bf581))
	ROM_LOAD ( "341-0047.f8", 0x3800, 0x0800, CRC(6ea8379b) SHA1(00a75ae3b58e1917ad640249366f654608589cf4))
ROM_END

ROM_START(maxxi)
	ROM_REGION(0x0800,"gfx1",0)
	ROM_LOAD ( "maxxi.chr", 0x0000, 0x0800, BAD_DUMP CRC(64f415c6) SHA1(f9d312f128c9557d9d6ac03bfad6c3ddf83e5659)) // Taken from 341-0036.chr used in apple2p

	ROM_REGION(0x4000,"maincpu",0)
	ROM_LOAD ( "maxxi.d0", 0x1000, 0x1000, CRC(7831f025) SHA1(0eb4161e5223c0dde2d140fcbace80d292ff9dc6))
	ROM_LOAD ( "maxxi.e0", 0x2000, 0x1000, CRC(0d494efd) SHA1(a2fd1223a3ca0cfee24a6afe66ea3c4c144dd98e))
	ROM_LOAD ( "maxxi.f0", 0x3000, 0x1000, CRC(34e4d01b) SHA1(44853b2d59ddd234db76c1a0d529180fb1e008ef))

	ROM_REGION(0x0800,"keyboard",0)
	ROM_LOAD ( "maxxi_teclado.rom", 0x0000, 0x0800, CRC(10c2d5b6) SHA1(226036d2f6f8fa5675303640ee1e5f0bab1135c6))
ROM_END

ROM_START(ace100)
	ROM_REGION(0x0800,"gfx1",0)
	ROM_LOAD ( "ace100.chr", 0x0000, 0x0800, BAD_DUMP CRC(64f415c6) SHA1(f9d312f128c9557d9d6ac03bfad6c3ddf83e5659)) // copy of a2.chr - real Ace chr is undumped

	ROM_REGION(0x4000,"maincpu",0)
	ROM_LOAD ( "ace100.rom", 0x1000, 0x3000, CRC(9d5ec94f) SHA1(8f2b3f2561788bebc7a805f620ec9e7ade973460))
ROM_END

ROM_START(space84)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "space 84 mobo chargen.bin", 0x0000, 0x2000, CRC(ceb98990) SHA1(8b2758da611bcfdd3d144edabc63ef1df2ca787b) )

	ROM_REGION(0x4000,"maincpu",0)
	ROM_LOAD ( "341-0011.d0",  0x1000, 0x0800, CRC(6f05f949) SHA1(0287ebcef2c1ce11dc71be15a99d2d7e0e128b1e))
	ROM_LOAD ( "341-0012.d8",  0x1800, 0x0800, CRC(1f08087c) SHA1(a75ce5aab6401355bf1ab01b04e4946a424879b5))
	ROM_LOAD ( "341-0013.e0",  0x2000, 0x0800, CRC(2b8d9a89) SHA1(8d82a1da63224859bd619005fab62c4714b25dd7))
	ROM_LOAD ( "341-0014.e8",  0x2800, 0x0800, CRC(5719871a) SHA1(37501be96d36d041667c15d63e0c1eff2f7dd4e9))
	ROM_LOAD( "space84_f.bin", 0x3000, 0x1000, CRC(4e741069) SHA1(ca1f16da9fb40e966ee4a899964cd6a7e140ab50))
ROM_END

ROM_START(am64)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "gm-2716.bin",  0x0000, 0x0800, CRC(863e657f) SHA1(cc954204c503bc545ec0d08862483aaad83805d5) )

	ROM_REGION(0x4000,"maincpu",0)
	ROM_LOAD( "am64-27128.bin", 0x0000, 0x4000, CRC(f25cdc7b) SHA1(235e72b77695938a9df8781f5bea3cbbbe1f4c76) )

	ROM_REGION(0x2000, "spares", 0)
	// parallel card ROM
	ROM_LOAD( "ap-2716.bin",  0x0000, 0x0800, CRC(c6990f08) SHA1(e7daf63639234e46738a4d78a49287d11ccaf537) )
	// i8048 keyboard MCU ROM
	ROM_LOAD( "tk10.bin",     0x0800, 0x0800, CRC(a06c5b78) SHA1(27c5160b913e0f62120f384026d24b9f1acb6970) )
ROM_END

ROM_START(ivelultr)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "ultra.chr", 0x0000, 0x1000,CRC(fed62c85) SHA1(479fb3f38a3f7332cef2e8c4856871afe8dc6017))
	ROM_LOAD( "ultra.chr", 0x1000, 0x1000,CRC(fed62c85) SHA1(479fb3f38a3f7332cef2e8c4856871afe8dc6017))

	ROM_REGION(0x4000,"maincpu",0)
	ROM_LOAD( "ultra1.bin", 0x2000, 0x1000, CRC(8ab49c1c) SHA1(b41da28a40c3a22bc10a954a86716a1a2bae04a4))
	ROM_CONTINUE(0x1000, 0x1000)
	ROM_LOAD( "ultra2.bin", 0x3000, 0x1000, CRC(1ac1e17e) SHA1(a5b8adec37da91970c303905b5e2c4d1b715ee4e))

	ROM_REGION(0x800, "kbmcu", 0)   // 6802 code for keyboard MCU (very unlike real Apples, will require some reverse-engineering)
	ROM_LOAD( "ultra4.bin", 0x0000, 0x0800, CRC(3dce51ac) SHA1(676b6e775d5159049cae5b6143398ec7b2bf437a) )
ROM_END

ROM_START(laser2c)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "g1.bin",       0x000000, 0x001000, BAD_DUMP CRC(7ad15cc4) SHA1(88c60ec0b008eccdbece09d18fe905380ddc070f) )

	ROM_REGION( 0x1000, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "g2.bin",       0x000000, 0x001000, CRC(f1d92f9c) SHA1(a54d55201f04af4c24bf94450d2cd1fa87c2c259) )

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD( "laser.bin",    0x001000, 0x002000, CRC(8b975094) SHA1(eea53530b4a3777afa00d2979abedf84fac62e08) )
	ROM_LOAD( "mon.bin",      0x003000, 0x001000, CRC(978c083f) SHA1(14e87cb717780b19db75c313004ba4d6ef20bc26) )
ROM_END

#if 0
ROM_START(laba2p) /* II Plus clone with on-board Disk II controller and Videx-compatible 80-column card, supposedly from lab equipment */
	ROM_REGION(0x1000,"gfx1",0)
	ROM_LOAD( "char.u30",     0x0000, 0x1000, CRC(2dbaef88) SHA1(9834842796132a11facd57923326d6954bcb609f) )

	ROM_REGION(0x4700,"maincpu",0)
	ROM_LOAD( "maind0.u35",   0x1000, 0x1000, CRC(24d73c7b) SHA1(d17a15868dc875c67061c95ec53a6b2699d3a425) )
	ROM_LOAD( "maine0.u34",   0x2000, 0x2000, CRC(314462ca) SHA1(5a23616dca14e59b4aca8ff6cfa0d98592a78a79) )

	ROM_REGION(0x1000, "fw80col", 0)
	ROM_LOAD( "80cfw.u3",     0x0000, 0x1000, CRC(92d2b8b0) SHA1(5149483eb3e550ece1584e85fc821bb04d068dec) )    // firmware for on-board Videx

	ROM_REGION(0x1000, "cg80col", 0)
	ROM_LOAD( "80ccgv80.u25", 0x0000, 0x1000, CRC(6d5e2707) SHA1(c56f76e8a366fee7374eb09f4866435c692490b2) )    // character generator for on-board Videx

	ROM_REGION(0x800, "diskii", 0)
	ROM_LOAD( "diskfw.u7",    0x0000, 0x0800, CRC(9207ef4e) SHA1(5fcffa4c68b16a7ef2f62651d4c7470400e5bd35) )    // firmware for on-board Disk II

	ROM_REGION(0x800, "unknown", 0)
	ROM_LOAD( "unk.u5",       0x0000, 0x0800, CRC(240a1774) SHA1(e6aeb0702dc99d76fd8c5a642fdfbe9ab896acd4) )    // unknown ROM
ROM_END
#endif

ROM_START( basis108 )
	ROM_REGION(0x4000, "maincpu", 0) // all roms overdumped
	ROM_LOAD( "d0.d83",   0x1000, 0x0800, CRC(bb4ac440) SHA1(7901203845adab588850ae35f81e4ee2a2248686) )
	ROM_IGNORE( 0x0800 )
	ROM_LOAD( "d8.d70",   0x1800, 0x0800, CRC(3e8cdbcd) SHA1(b2a418818e4130859afd6c08b5695328a3edd2c5) )
	ROM_IGNORE( 0x0800 )
	ROM_LOAD( "e0.d56",   0x2000, 0x0800, CRC(0575ba28) SHA1(938884eb3ebd0870f99df33ee7a03e93cd625ab4) )
	ROM_IGNORE( 0x0800 )
	ROM_LOAD( "e8.d40",   0x2800, 0x0800, CRC(fc7229f6) SHA1(380ffcf0dba008f0bc43a483931e98034b1d0d52) )
	ROM_IGNORE( 0x0800 )
	ROM_LOAD( "f0.d39",   0x3000, 0x0800, CRC(bae4b24d) SHA1(b5ffc9b3552b13b2f577a42196addae71289203d) )
	ROM_IGNORE( 0x0800 )
	ROM_LOAD( "f8.d25",   0x3800, 0x0800, CRC(f84efac5) SHA1(66b7eadfdb938cda0de01dbeab1b74aa88bd096c) )
	ROM_IGNORE( 0x0800 )

	ROM_REGION(0x2000, "gfx1", 0)
	ROM_LOAD( "cg.d29",   0x0000, 0x1000, CRC(120de575) SHA1(e6e4e357b3834a143df9e5834abfb4a9139457d4) )

	ROM_REGION(0x1000, "cg80col", 0)
	ROM_LOAD( "dispcard_cg.bin",         0x0000, 0x1000, CRC(cf84811c) SHA1(135f4f35607dd74941f0a3cae813227bf8a8a020) )

	ROM_REGION(0x1000, "fw80col", 0)
	ROM_LOAD( "dispcard_ctrl_17.43.bin", 0x0000, 0x0800, CRC(bf04eda4) SHA1(86047c0ec6b06d647b95304d7f95d3d116f60e4a) )

	ROM_REGION(0x800, "diskii", 0)
	ROM_LOAD( "fdccard_fdc4_slot6.bin",  0x0000, 0x0800, CRC(2bd452bb) SHA1(10ba81d34117ef713c546d748bf0e1a8c04d1ae3) )
ROM_END

// The bit1 and bit2 of each byte swap positions.
// 原机器ROM每个字节的第1位和第2位互换了位置
ROM_START(hkc8800a)
	ROM_REGION(0x0800,"gfx1",0)
	ROM_LOAD ( "341-0036.chr", 0x0000, 0x0800, CRC(64f415c6) SHA1(f9d312f128c9557d9d6ac03bfad6c3ddf83e5659))

	ROM_REGION(0x4000, "maincpu", ROMREGION_LE)
	ROM_LOAD ( "hkc8800a_c0.bin", 0x0000, 0x0800, CRC(8dceea26) SHA1(57623fd9ddef05cb56e8f0bcf0baa8902ebba2bb))
	ROM_LOAD ( "hkc8800a_c8.bin", 0x0800, 0x0800, CRC(a337c7b5) SHA1(bc3f021a85124785b78dd781fcabc66bc5645515))
	ROM_LOAD ( "341-0011.d0", 0x1000, 0x0800, CRC(6f05f949) SHA1(0287ebcef2c1ce11dc71be15a99d2d7e0e128b1e))
	ROM_LOAD ( "341-0012.d8", 0x1800, 0x0800, CRC(1f08087c) SHA1(a75ce5aab6401355bf1ab01b04e4946a424879b5))
	ROM_LOAD ( "341-0013.e0", 0x2000, 0x0800, CRC(2b8d9a89) SHA1(8d82a1da63224859bd619005fab62c4714b25dd7))
	ROM_LOAD ( "341-0014.e8", 0x2800, 0x0800, CRC(5719871a) SHA1(37501be96d36d041667c15d63e0c1eff2f7dd4e9))
	ROM_LOAD ( "341-0015.f0", 0x3000, 0x0800, CRC(9a04eecf) SHA1(e6bf91ed28464f42b807f798fc6422e5948bf581))
	ROM_LOAD ( "hkc8800a_f8.bin", 0x3800, 0x0800, CRC(f2287c5f) SHA1(0b6c2d6df11a0aa8c5737831758d9668fce11887))
ROM_END

ROM_START(albert)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "albert 95-6005_rom_2732.bin", 0x0000, 0x1000, CRC(30df7410) SHA1(cb884efb12992e8a0140fdf6368b0268b6c0df8c) )

	ROM_REGION( 0x1000, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "albert_95-6004_rom_2732.bin", 0x0000, 0x1000, CRC(6d9a435f) SHA1(ce1da16659922daff5bc0065ff45b00d271108f9) )

	ROM_REGION(0x4000,"maincpu",0)
	ROM_LOAD( "albert_main_rom_27128.bin", 0x0000, 0x4000, CRC(ccf5696b) SHA1(59504a51d91486289330266e851f2ea1719766c1) )
ROM_END

ROM_START(am100)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "nfl-asem-am100-u43.bin", 0x0000, 0x0800, CRC(863e657f) SHA1(cc954204c503bc545ec0d08862483aaad83805d5) )

	ROM_REGION( 0x1000, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "nfl-asem-am100-keyboard-u5.bin", 0x0000, 0x0800, CRC(28f5ea38) SHA1(9f24c54f7cee41f7fef41294f05c4bc89d65acfb) )

	ROM_REGION(0x4000, "maincpu",0)
	ROM_LOAD("nfl-asem-am100-u24.bin", 0x0000, 0x4000, CRC(2fb0c717) SHA1(cb4f754d3e1aec9603faebc308a4a63466242e43) )
ROM_END

ROM_START(dodo)
	ROM_REGION(0x2000,"gfx1", 0)
	ROM_LOAD( "gtac_2_charrom_um2316_a5.bin", 0x0000, 0x0800, CRC(a2dfcfeb) SHA1(adea922f950667d3b24297d2f64de697c28d6c17) )

	ROM_REGION( 0x1000, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "nfl-asem-am100-keyboard-u5.bin", 0x0000, 0x0800, CRC(28f5ea38) SHA1(9f24c54f7cee41f7fef41294f05c4bc89d65acfb) ) // borrowed from the am100

	ROM_REGION(0x4000, "maincpu",0)
	ROM_LOAD( "dodo2764.bin", 0x2000, 0x1000, CRC(4b761f87) SHA1(2e1741db8134c4c715ecae480f5bda51d58ae296) )
	ROM_CONTINUE(0x1000, 0x1000)
	ROM_LOAD( "dodo2732.bin", 0x3000, 0x1000, CRC(405cdb0c) SHA1(3ed133eb94ee33194c668c4ee3f67885dd489d13) )
ROM_END

} // anonymous namespace

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT    CLASS          INIT        COMPANY                FULLNAME
COMP( 1977, apple2,   0,      0,      apple2,   apple2,  apple2_state, empty_init, "Apple Computer",      "Apple ][", MACHINE_SUPPORTS_SAVE )
COMP( 1979, apple2p,  apple2, 0,      apple2p,  apple2p, apple2_state, empty_init, "Apple Computer",      "Apple ][+", MACHINE_SUPPORTS_SAVE )
COMP( 1980, apple2jp, apple2, 0,      apple2jp, apple2p, apple2_state, empty_init, "Apple Computer",      "Apple ][ J-Plus", MACHINE_SUPPORTS_SAVE )
COMP( 198?, elppa,    apple2, 0,      apple2p,  apple2p, apple2_state, empty_init, "Victor do Brasil",    "Elppa II+", MACHINE_SUPPORTS_SAVE )
COMP( 1982, microeng, apple2, 0,      apple2p,  apple2p, apple2_state, empty_init, "Spectrum Eletronica (SCOPUS)", "Micro Engenho", MACHINE_SUPPORTS_SAVE )
COMP( 1982, maxxi,    apple2, 0,      apple2p,  apple2p, apple2_state, empty_init, "Polymax",             "Maxxi", MACHINE_SUPPORTS_SAVE )
COMP( 1982, prav82,   apple2, 0,      apple2p,  apple2p, apple2_state, empty_init, "Pravetz",             "Pravetz 82", MACHINE_SUPPORTS_SAVE )
COMP( 1982, ace100,   apple2, 0,      apple2,   apple2p, apple2_state, empty_init, "Franklin Computer",   "Franklin ACE 100", MACHINE_SUPPORTS_SAVE )
COMP( 1982, ace1000,  apple2, 0,      apple2p,  apple2p, apple2_state, empty_init, "Franklin Computer",   "Franklin ACE 1000", MACHINE_SUPPORTS_SAVE )
COMP( 1982, uniap2en, apple2, 0,      apple2p,  apple2p, apple2_state, empty_init, "Unitron Eletronica",  "Unitron AP II (in English)", MACHINE_SUPPORTS_SAVE )
COMP( 1982, uniap2pt, apple2, 0,      apple2p,  apple2p, apple2_state, empty_init, "Unitron Eletronica",  "Unitron AP II (in Brazilian Portuguese)", MACHINE_SUPPORTS_SAVE )
COMP( 1984, uniap2ti, apple2, 0,      apple2p,  apple2p, apple2_state, empty_init, "Unitron Eletronica",  "Unitron AP II+ (Teclado Inteligente)", MACHINE_SUPPORTS_SAVE )
COMP( 1982, craft2p,  apple2, 0,      apple2p,  apple2p, apple2_state, empty_init, "Craft",               "Craft II+", MACHINE_SUPPORTS_SAVE )
// reverse font direction + wider character cell -\/
COMP( 1984, ivelultr, apple2, 0,      ivelultr, apple2p, apple2_state, empty_init, "Ivasim",              "Ivel Ultra", MACHINE_SUPPORTS_SAVE )
COMP( 1985, prav8m,   apple2, 0,      apple2p,  apple2p, apple2_state, empty_init, "Pravetz",             "Pravetz 8M", MACHINE_SUPPORTS_SAVE )
COMP( 1985, space84,  apple2, 0,      space84,  apple2p, apple2_state, empty_init, "ComputerTechnik/IBS", "Space 84",   MACHINE_NOT_WORKING )
COMP( 1985, am64,     apple2, 0,      space84,  apple2p, apple2_state, empty_init, "ASEM",                "AM 64", MACHINE_SUPPORTS_SAVE )
//COMP( 19??, laba2p,   apple2, 0,      laba2p,   apple2p, apple2_state, empty_init, "<unknown>",           "Lab equipment Apple II Plus clone", MACHINE_SUPPORTS_SAVE )
COMP( 1985, laser2c,  apple2, 0,      ivelultr, apple2p, apple2_state, empty_init, "Milmar",              "Laser //c", MACHINE_SUPPORTS_SAVE )
COMP( 1982, basis108, apple2, 0,      apple2,   apple2p, apple2_state, empty_init, "Basis",               "Basis 108", MACHINE_SUPPORTS_SAVE )
COMP( 1984, hkc8800a, apple2, 0,      apple2p,  apple2p, apple2_state, empty_init, "China HKC",           "HKC 8800A", MACHINE_SUPPORTS_SAVE )
COMP( 1984, albert,   apple2, 0,      albert,   apple2p, apple2_state, empty_init, "Albert Computers, Inc.", "Albert", MACHINE_SUPPORTS_SAVE )
COMP( 198?, am100,    apple2, 0,      apple2p,  apple2p, apple2_state, empty_init, "ASEM S.p.A.",         "AM100",     MACHINE_SUPPORTS_SAVE )
COMP( 198?, dodo,     apple2, 0,      ivelultr, apple2p, apple2_state, empty_init, "GTAC",                "Do-Do",     MACHINE_SUPPORTS_SAVE )



apple2e.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    apple2e.cpp - Apple IIe/IIc/IIc Plus and clones

    Next generation driver written in September/October 2014 by R. Belmont.
    Thanks to the original Apple II series driver's authors: Mike Balfour, Nathan Woods, and R. Belmont.
    Special thanks to the Apple II Documentation Project/Antoine Vignau and Peter Ferrie.


    IIe: base of this driver.  64K RAM, slot 0 language card emulation without the double-read requirement,
         lowercase and SHIFT key on button 2, Open and Solid Apple buttons on joy buttons 0 and 1,
         auxiliary slot, built-in 80 column support if extra RAM added.

         Physical slot 0 was eliminated thanks to the built-in language card.

         Most of the write-only softswitches gained readback locations, necessary to make interrupt-driven
         software possible.

         Base 80-column card: 1K RAM, allows 80 columns and double-lo-res,
         no double-hi-res.

         Extended 80-column card: 64K RAM (including a second language card),
         allows 80 columns, double-lo-res, and double-hi-res.

         Revision A motherboards (very rare) don't support double-hi-res; it's unclear
         if double-lo-res works or not.  We emulate the much more common Rev B or later
         board.

         Has a keyboard switch on models with localized keyboard to toggle between the
         US QWERTY and local keyboards.  US models do not have a physical keyboard switch,
         however Rev B motherboards have the DVORAK in ROM that is inaccessible to the user
         without a hardware modification  (see: Apple IIe Hardware: Dvorak
         Keyboard Layout (May 25, 1989) from the Apple Tech Info Library)

    IIe enhanced: 65C02 CPU with more instructions, MouseText in the character generator.

    IIe platinum: Like enhanced but with added numeric keypad and extended 80-column card
         included in the box.  Keypad CLEAR generates ESC by default, one hardware mod
         made it generate CTRL-X instead.  (new keyboard encoder ROM?)

    NOTE: On real IIe and IIe enhanced h/w, pressing SHIFT and paddle button 2 will
    short out the power supply and cause a safety shutdown.  (We don't emulate
    this "feature", and it was relatively rare in real life as Apple joysticks only
    had buttons 0 and 1 normally).

    IIc: IIe enhanced shrunken into a pizzabox with a Disk II compatible
         half-height drive included in the case.

     No slots, but included functionality equivalent to the following slots
     on the motherboard:
     - 2 Super Serial Cards (modem and printer ports)
     - extended 80 column card / 128K RAM
     - Disk II IWM controller
     - Apple II Mouse card (firmware entry points are compatible,
       but the hardware implementation omits the 68705 and is quite different!)

     Has a 40/80 column switch and a keyboard switch.  The keyboard switches
     between QWERTY and DVORAK layouts on models without localized keyboards.
     On models with localized keyboards, it switches between US QWERTY
     and the local keyboard layout and character set.

    IIc (UniDisk 3.5): IIc with ROM doubled to 32K and the ROMSWITCH register
         added to page between the original 16K ROM and the new added 16K.  The
         extra firmware space was dedicated to implementing the Protocol Converter,
         later renamed "SmartPort", which communicates with "smart" packet devices
         over the IWM bus.

         Partial AppleTalk code also exists in this ROM but it doesn't work and
         was not completed.

    IIc (Original Memory Expansion):
         Removes AppleTalk and adds support for a memory expansion card with up
         to 1 MB; this is identical both in hardware and firmware to the "Slinky"
         memory expansion card for the Apple IIe (a2bus/a2memexp.c).

    IIc (Revised Memory Expansion, Rev. 3):
        Fixes several nasty bugs in the Original Memory Expansion version.  Not
        currently dumped.

    IIc (Rev 4):
        Fixes memory size detection for memory cards with less than 1MB.  Fixes
        several screen hole errors introduced in Rev 3, and fixes Terminal Mode
        wherein the firmware can be put into a built-in terminal mode for simple
        tests with a modem.

    IIc Plus:
        Like IIc with memory expansion, but with licensed built-in Zip Chip which
        runs the 65C02 at 4 MHz turbo speed with a small cache RAM.

        The machine has an internal "Apple 3.5" drive plus a custom chip
        named "MIG" (Multidrive Interface Glue) which helps with the control
        of the drive.  This gets around the fact that 1 MHz isn't
        sufficient to handle direct Woz-style control of a double-density
        3.5" drive.

        External drive port allows IIgs-style daisy-chaining.

        40/80 column switch removed.

----------------------------------

TK3000 keyboard matrix:
Data bus D0-D7 is X0-X7
Address bus A0-A11 is Y0-Y11

IIc Plus CGGA speed control:
Fast: Store $08 to $C05B (Zip compatible)
Normal: Store $08 to $C05A (Zip compatible)
Lock: Store $A5 to $C05A (Zip compatible).
Unlock: Store $5A 4 times in a row to $C05A (Zip compatible).
Read state: Cached by the firmware in MIG RAM (see below).
Write state: Store speaker/slot byte at $C05C (Zip compatible).  Firmware always enables the paddle
delay at $C05F, regardless of what you pass in.

Previous versions of this driver stated that the IIc+ speaker/slot control byte was not Zip compatible; that
was a misunderstanding.

Accelerator control firmware saves/restores zero page locations 0-7 in MIG RAM page 2 locations $CE10-$CE17.
MIG RAM page 2 $CE02 is the speaker/slot bitfield and $CE03 is the paddle/accelerator bitfield.

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

#include "emu.h"

#include "apple2video.h"
#include "apple2common.h"

#include "cpu/m6502/m6502.h"
#include "cpu/m6502/w65c02.h"
#include "cpu/mcs48/mcs48.h"
#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "machine/applefdintf.h"
#include "machine/ds1215.h"
#include "machine/iwm.h"
#include "machine/kb3600.h"
#include "machine/mos6551.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "sound/spkrdev.h"

#include "bus/a2bus/cards.h"
#include "bus/a2bus/a2mockingboard.h"
#include "bus/a2bus/a2diskiing.h"
#include "bus/a2bus/a2iwm.h"
#include "bus/a2bus/laser128.h"
#include "bus/a2bus/ace2x00.h"
#include "bus/a2bus/a2eauxslot.h"
#include "bus/a2bus/a2eext80col.h"
#include "bus/a2bus/a2eramworks3.h"
#include "bus/a2bus/a2estd80col.h"
#include "bus/a2gameio/gameio.h"
#include "bus/centronics/ctronics.h"
#include "bus/rs232/rs232.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "utf8.h"


namespace {

#define A2_CPU_TAG "maincpu"
#define A2_KBDC_TAG "ay3600"
#define A2_BUS_TAG "a2bus"
#define A2_SPEAKER_TAG "speaker"
#define A2_CASSETTE_TAG "tape"
#define A2_UPPERBANK_TAG "inhbank"
#define IIC_ACIA1_TAG "acia1"
#define IIC_ACIA2_TAG "acia2"
#define LASER128_UDC_TAG "l128udc"
#define PRINTER_PORT_TAG "printer"
#define MODEM_PORT_TAG "modem"
#define A2_AUXSLOT_TAG "auxbus"
#define A2_VIDEO_TAG "a2video"

#define A2_0000_TAG "r00bank"
#define A2_0200_TAG "r02bank"
#define A2_0400_TAG "r04bank"
#define A2_0800_TAG "r08bank"
#define A2_2000_TAG "r20bank"
#define A2_4000_TAG "r40bank"
#define A2_C100_TAG "c1bank"
#define A2_C300_TAG "c3bank"
#define A2_C400_TAG "c4bank"
#define A2_C800_TAG "c8bank"
#define A2_LCBANK_TAG "lcbank"

#define MOUSE_BUTTON_TAG    "mse_button"
#define MOUSE_XAXIS_TAG     "mse_x"
#define MOUSE_YAXIS_TAG     "mse_y"

#define CNXX_UNCLAIMED  -1
#define CNXX_INTROM     -2

static constexpr int IRQ_SLOT = 0;
static constexpr int IRQ_VBL = 1;
static constexpr int IRQ_MOUSEXY = 2;

static constexpr XTAL A2BUS_7M_CLOCK = XTAL(14'318'181) / 2;

class apple2e_state : public driver_device
{
public:
	apple2e_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, A2_CPU_TAG),
		m_screen(*this, "screen"),
		m_scantimer(*this, "scantimer"),
		m_acceltimer(*this, "acceltimer"),
		m_ram(*this, RAM_TAG),
		m_rom(*this, "maincpu"),
		m_a2common(*this, "a2common"),
		m_cecbanks(*this, "cecexp"),
		m_ay3600(*this, A2_KBDC_TAG),
		m_video(*this, A2_VIDEO_TAG),
		m_a2bus(*this, A2_BUS_TAG),
		m_a2eauxslot(*this, A2_AUXSLOT_TAG),
		m_gameio(*this, "gameio"),
		m_mouseb(*this, MOUSE_BUTTON_TAG),
		m_mousex(*this, MOUSE_XAXIS_TAG),
		m_mousey(*this, MOUSE_YAXIS_TAG),
		m_kbdrom(*this, "keyboard"),
		m_kbspecial(*this, "keyb_special"),
		m_kbd_lang_sel(*this, "kbd_lang_select"),
		m_sysconfig(*this, "a2_config"),
		m_franklin_fkeys(*this, "franklin_fkeys"),
		m_speaker(*this, A2_SPEAKER_TAG),
		m_cassette(*this, A2_CASSETTE_TAG),
		m_upperbank(*this, A2_UPPERBANK_TAG),
		m_0000bank(*this, A2_0000_TAG),
		m_0200bank(*this, A2_0200_TAG),
		m_0400bank(*this, A2_0400_TAG),
		m_0800bank(*this, A2_0800_TAG),
		m_2000bank(*this, A2_2000_TAG),
		m_4000bank(*this, A2_4000_TAG),
		m_c100bank(*this, A2_C100_TAG),
		m_c300bank(*this, A2_C300_TAG),
		m_c400bank(*this, A2_C400_TAG),
		m_c800bank(*this, A2_C800_TAG),
		m_lcbank(*this, A2_LCBANK_TAG),
		m_acia1(*this, IIC_ACIA1_TAG),
		m_acia2(*this, IIC_ACIA2_TAG),
		m_iwm(*this, "fdc"),
		m_floppy(*this, "fdc:%d", 0U),
		m_ds1315(*this, "nsc"),
		m_printer_conn(*this, "parallel"),
		m_printer_out(*this, "laserprnout")
	{
		m_accel_laser = false;
		m_has_laser_mouse = false;
		m_isiic = false;
		m_isiicplus = false;
		m_iscec = false;
		m_iscecm = false;
		m_iscec2000 = false;
		m_isace500 = false;
		m_isace2200 = false;
		m_ace2200_axxx_bank = false;
		m_pal = false;
		m_cur_floppy = nullptr;
		m_devsel = 0;
		m_laser_speed = 0;
		m_laser_fdc_on = false;
	}

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<timer_device> m_scantimer, m_acceltimer;
	required_device<ram_device> m_ram;
	required_memory_region m_rom;
	required_device<apple2_common_device> m_a2common;
	optional_memory_region m_cecbanks;
	required_device<ay3600_device> m_ay3600;
	required_device<a2_video_device> m_video;
	required_device<a2bus_device> m_a2bus;
	optional_device<a2eauxslot_device> m_a2eauxslot;
	required_device<apple2_gameio_device> m_gameio;
	optional_ioport m_mouseb, m_mousex, m_mousey;
	optional_memory_region m_kbdrom;
	required_ioport m_kbspecial;
	optional_ioport m_kbd_lang_sel; // high-order nibble: keyboard selection offset - low-order nibble: character ROM area selection offset (including lo-res patterns)
	optional_ioport m_sysconfig;
	optional_ioport m_franklin_fkeys;
	required_device<speaker_sound_device> m_speaker;
	optional_device<cassette_image_device> m_cassette;
	memory_view m_upperbank, m_0000bank, m_0200bank, m_0400bank;
	memory_view m_0800bank, m_2000bank, m_4000bank, m_c100bank;
	memory_view m_c300bank, m_c400bank, m_c800bank, m_lcbank;
	optional_device<mos6551_device> m_acia1, m_acia2;
	optional_device<applefdintf_device> m_iwm;
	optional_device_array<floppy_connector, 4> m_floppy;
	required_device<ds1216e_device> m_ds1315;
	optional_device<centronics_device>      m_printer_conn;
	optional_device<output_latch_device>    m_printer_out;

	TIMER_DEVICE_CALLBACK_MEMBER(apple2_interrupt);
	TIMER_DEVICE_CALLBACK_MEMBER(accel_timer);
	TIMER_DEVICE_CALLBACK_MEMBER(ay3600_repeat);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	u8 ram0000_r(offs_t offset);
	void ram0000_w(offs_t offset, u8 data);
	u8 ram0200_r(offs_t offset);
	void ram0200_w(offs_t offset, u8 data);
	u8 ram0400_r(offs_t offset);
	void ram0400_w(offs_t offset, u8 data);
	u8 ram0800_r(offs_t offset);
	void ram0800_w(offs_t offset, u8 data);
	u8 ram2000_r(offs_t offset);
	void ram2000_w(offs_t offset, u8 data);
	u8 ram4000_r(offs_t offset);
	u8 ram4000_ace2200_r(offs_t offset);
	void ram4000_w(offs_t offset, u8 data);
	u8 cec4000_r(offs_t offset);
	u8 cec8000_r(offs_t offset);
	void ram8000_w(offs_t offset, u8 data);
	u8 auxram0000_r(offs_t offset);
	void auxram0000_w(offs_t offset, u8 data);
	u8 auxram0200_r(offs_t offset);
	void auxram0200_w(offs_t offset, u8 data);
	u8 auxram0400_r(offs_t offset);
	void auxram0400_w(offs_t offset, u8 data);
	u8 auxram0800_r(offs_t offset);
	void auxram0800_w(offs_t offset, u8 data);
	u8 auxram2000_r(offs_t offset);
	void auxram2000_w(offs_t offset, u8 data);
	u8 auxram4000_r(offs_t offset);
	void auxram4000_w(offs_t offset, u8 data);
	u8 c000_r(offs_t offset);
	void c000_w(offs_t offset, u8 data);
	u8 c000_laser_r(offs_t offset);
	void c000_laser_w(offs_t offset, u8 data);
	u8 laserprn_busy_r();
	void laserprn_w(u8 data);
	TIMER_CALLBACK_MEMBER(update_laserprn_strobe);
	u8 c000_iic_r(offs_t offset);
	void c000_iic_w(offs_t offset, u8 data);
	u8 c080_r(offs_t offset);
	void c080_w(offs_t offset, u8 data);
	u8 c100_r(offs_t offset);
	u8 c100_int_r(offs_t offset);
	u8 c100_int_bank_r(offs_t offset);
	u8 c100_cec_r(offs_t offset);
	u8 c100_cec_bank_r(offs_t offset);
	void c100_w(offs_t offset, u8 data);
	u8 c300_r(offs_t offset);
	u8 c300_int_r(offs_t offset);
	u8 c300_int_bank_r(offs_t offset);
	u8 c300_cec_r(offs_t offset);
	u8 c300_cec_bank_r(offs_t offset);
	void c300_w(offs_t offset, u8 data);
	u8 c400_r(offs_t offset);
	u8 c400_int_r(offs_t offset);
	u8 c400_int_bank_r(offs_t offset);
	u8 c400_cec_r(offs_t offset);
	u8 c400_cec_bank_r(offs_t offset);
	void c400_w(offs_t offset, u8 data);
	void c400_cec_w(offs_t offset, u8 data);
	u8 c800_r(offs_t offset);
	u8 c800_int_r(offs_t offset);
	u8 c800_cec_r(offs_t offset);
	u8 c800_cec_bank_r(offs_t offset);
	u8 c800_b2_int_r(offs_t offset);
	void c800_w(offs_t offset, u8 data);
	u8 inh_r(offs_t offset);
	void inh_w(offs_t offset, u8 data);
	u8 lc_r(offs_t offset);
	void lc_w(offs_t offset, u8 data);
	u8 lc_romswitch_r(offs_t offset);
	void lc_romswitch_w(offs_t offset, u8 data);
	u8 laser_mouse_r(offs_t offset);
	void laser_mouse_w(offs_t offset, u8 data);
	void a2bus_irq_w(int state);
	void a2bus_nmi_w(int state);
	void a2bus_inh_w(int state);
	void busy_w(int state);
	int ay3600_shift_r();
	int ay3600_control_r();
	void ay3600_data_ready_w(int state);
	void ay3600_ako_w(int state);
	u8 memexp_r(offs_t offset);
	void memexp_w(offs_t offset, u8 data);
	u8 ace500_c0bx_r(offs_t offset);
	void ace500_c0bx_w(offs_t offset, u8 data);

	void apple2cp(machine_config &config);
	void spectred(machine_config &config);
	void laser128(machine_config &config);
	void laser128o(machine_config &config);
	void laser128ex2(machine_config &config);
	void ace500(machine_config &config);
	void ace2200(machine_config &config);
	void apple2c_iwm(machine_config &config);
	void apple2c_iwm_pal(machine_config &config);
	void apple2c_mem(machine_config &config);
	void apple2c_mem_pal(machine_config &config);
	void cec(machine_config &config);
	void mprof3(machine_config &config);
	void apple2e(machine_config &config);
	void apple2epal(machine_config &config);
	void apple2ep(machine_config &config);
	void apple2eppal(machine_config &config);
	void apple2c(machine_config &config);
	void apple2cpal(machine_config &config);
	void tk3000(machine_config &config);
	void apple2ee(machine_config &config);
	void apple2eepal(machine_config &config);
	void apple2c_map(address_map &map) ATTR_COLD;
	void apple2c_memexp_map(address_map &map) ATTR_COLD;
	void base_map(address_map &map) ATTR_COLD;
	void laser128_map(address_map &map) ATTR_COLD;
	void ace500_map(address_map &map) ATTR_COLD;
	void ace2200_map(address_map &map) ATTR_COLD;
	void spectred_keyb_map(address_map &map) ATTR_COLD;
	void init_laser128();
	void init_128ex();
	void init_pal();
	void init_ace500();
	void init_ace2200();

	bool m_35sel, m_hdsel, m_intdrive;

private:
	int m_speaker_state, m_cassette_state, m_cassette_out;

	double m_joystick_x1_time, m_joystick_y1_time, m_joystick_x2_time, m_joystick_y2_time;

	u32 m_franklin_last_fkeys;
	u16 m_lastchar, m_strobe, m_franklin_strobe;
	u8 m_transchar;
	bool m_anykeydown;
	int m_repeatdelay;

	int m_inh_slot, m_cnxx_slot;

	bool m_an0, m_an1, m_an2, m_an3;

	bool m_vbl, m_vblmask;

	bool m_xy, m_x0edge, m_y0edge;
	bool m_x0, m_x1, m_y0, m_y1;
	bool m_xirq, m_yirq;
	int last_mx, last_my, count_x, count_y;

	bool m_intcxrom;
	bool m_slotc3rom;
	bool m_altzp;
	bool m_ramrd, m_ramwrt;
	bool m_lcram, m_lcram2, m_lcprewrite, m_lcwriteenable;
	bool m_ioudis;
	bool m_romswitch;
	bool m_mockingboard4c;
	bool m_intc8rom;
	bool m_reset_latch;

	bool m_isiic, m_isiicplus, m_iscec, m_iscecm, m_iscec2000, m_pal;
	u8 m_migram[0x800];
	u16 m_migpage;

	bool m_isace500, m_isace2200, m_ace_cnxx_bank, m_ace2200_axxx_bank;
	u16 m_ace500rombank;

	bool m_accel_unlocked;
	bool m_accel_fast;
	bool m_accel_present;
	bool m_accel_temp_slowdown;
	bool m_accel_laser;
	bool m_has_laser_mouse;
	bool m_laser_fdc_on;
	int m_accel_stage;
	u32 m_accel_speed;
	u8 m_accel_slotspk, m_accel_gameio, m_laser_speed;

	emu_timer *m_strobe_timer;
	u8  m_next_strobe;
	bool m_centronics_busy;

	u8 *m_ram_ptr, *m_rom_ptr, *m_cec_ptr;
	int m_ram_size;

	int m_cec_bank;

	u8 *m_aux_ptr, *m_aux_bank_ptr;
	u16 m_aux_mask;

	int m_inh_bank;

	double m_x_calibration, m_y_calibration;

	device_a2bus_card_interface *m_slotdevice[8];
	device_a2eauxslot_card_interface *m_auxslotdevice;

	int m_irqmask;

	u8 m_exp_bankhior;
	int m_exp_addrmask;
	u8 m_exp_regs[0x10];
	u8 *m_exp_ram;
	int m_exp_wptr, m_exp_liveptr;

	void do_io(int offset, bool is_iic);
	u8 read_floatingbus();
	void update_slotrom_banks();
	void lc_update(int offset, bool writing);
	u8 read_slot_rom(int slotbias, int offset);
	void write_slot_rom(int slotbias, int offset, u8 data);
	u8 read_int_rom(int slotbias, int offset);
	void auxbank_update();
	void lcrom_update();
	void cec_lcrom_update();
	void raise_irq(int irq);
	void lower_irq(int irq);
	void update_iic_mouse();
	void accel_full_speed();
	void accel_normal_speed();
	void accel_slot(int slot);
	void laser_calc_speed();

	u8 m_cec_remap[0x40000];

	u8 mig_r(u16 offset);
	void mig_w(u16 offset, u8 data);
	void phases_w(uint8_t phases);
	void sel35_w(int sel35);
	void devsel_w(uint8_t devsel);
	void hdsel_w(int hdsel);
	void recalc_active_device();

	floppy_image_device *m_cur_floppy;
	int m_devsel;

	u8 laser_motor_r(offs_t offset)
	{
		m_laser_fdc_on = (offset == 1);
		laser_calc_speed();
		return m_iwm->read(offset + 8);
	}

	void laser_motor_w(offs_t offset, u8 data)
	{
		m_laser_fdc_on = (offset == 1);
		laser_calc_speed();
		m_iwm->write(offset + 8, data);
	}

	offs_t dasm_trampoline(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const util::disasm_interface::data_buffer &params);

	void apple2e_common(machine_config &config, bool enhanced, bool rgb_option);
};


offs_t apple2e_state::dasm_trampoline(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const util::disasm_interface::data_buffer &params)
{
	return m_a2common->dasm_override(stream, pc, opcodes, params);
}

u8 apple2e_state::mig_r(u16 offset)
{
	//printf("mig_r @ %x\n", offset + 0xc00);
	// MIG RAM window
	if ((offset >= 0x200) && (offset < 0x220))
	{
		return m_migram[m_migpage + (offset & 0x1f)];
	}

	// increment MIG RAM window and return previous value
	if ((offset >= 0x220) && (offset < 0x240))
	{
		u8 rv = m_migram[m_migpage + (offset & 0x1f)];
		m_migpage += 0x20;
		m_migpage &= 0x7ff;
		return rv;
	}

	if ((offset >= 0x240) && (offset < 0x260))
	{
		m_hdsel = false;
		if (m_cur_floppy)
		{
			m_cur_floppy->ss_w(0);
		}
	}

	if ((offset >= 0x260) && (offset < 0x280))
	{
		m_hdsel = true;
		if (m_cur_floppy)
		{
			m_cur_floppy->ss_w(1);
		}
	}

	// reset MIG RAM window
	if (offset == 0x2a0)
	{
		m_migpage = 0;
	}

	return read_floatingbus();
}

void apple2e_state::mig_w(u16 offset, u8 data)
{
	//printf("mig_w %x @ %x\n", data, offset + 0xc00);

	if (offset == 0x40)
	{
		m_iwm->reset();
		return;
	}

	if ((offset >= 0x80) && (offset < 0xa0))
	{
		//printf("MIG: enable internal drive on d2\n");
		m_intdrive = true;
		recalc_active_device();
		return;
	}

	if ((offset >= 0xc0) && (offset < 0xe0))
	{
		//printf("MIG: disable internal drive\n");
		m_intdrive = false;
		recalc_active_device();
		return;
	}

	// MIG RAM window
	if ((offset >= 0x200) && (offset < 0x220))
	{
		m_migram[m_migpage + (offset & 0x1f)] = data;
		return;
	}

	// increment MIG RAM window, but write value at old location first
	if ((offset >= 0x220) && (offset < 0x240))
	{
		m_migram[m_migpage + (offset & 0x1f)] = data;
		m_migpage += 0x20;
		m_migpage &= 0x7ff; // make sure we wrap
		return;
	}

	if ((offset >= 0x240) && (offset < 0x260))
	{
		m_35sel = false;
		recalc_active_device();
		return;
	}

	if ((offset >= 0x260) && (offset < 0x280))
	{
		m_35sel = true;
		recalc_active_device();
		return;
	}

	// reset MIG RAM window
	if (offset == 0x2a0)
	{
		m_migpage = 0;
	}
}

void apple2e_state::phases_w(uint8_t phases)
{
	if (m_cur_floppy)
	{
		m_cur_floppy->seek_phase_w(phases);
	}
}

void apple2e_state::devsel_w(uint8_t devsel)
{
	m_devsel = devsel;
	recalc_active_device();
}

void apple2e_state::sel35_w(int sel35)
{
}

void apple2e_state::recalc_active_device()
{
	if (m_devsel == 1)
	{
		if (!m_35sel)
		{
			m_cur_floppy = m_floppy[0]->get_device();
		}
		else
		{
			m_cur_floppy = m_floppy[3]->get_device();
		}
	}
	else if (m_devsel == 2)
	{
		// as per http://apple2.guidero.us/doku.php/mg_notes/apple_iic/mig_chip intdrive + devsel = 2 enables
		// the internal drive, no 35sel is necessary.  If intdrive is clear, 35sel and devsel work as normal.
		if (m_intdrive)
		{
			m_cur_floppy = m_floppy[2]->get_device();
		}
		else if (!m_35sel)
		{
			m_cur_floppy = m_floppy[1]->get_device();
		}
		else    // should be external 3.5 #2, for a 3rd drive
		{
			m_cur_floppy = nullptr;
		}
	}
	else
	{
		m_cur_floppy = nullptr;
	}

	m_iwm->set_floppy(m_cur_floppy);

	if (m_cur_floppy)
	{
		m_cur_floppy->ss_w(m_hdsel ? 1 : 0);
	}
}

void apple2e_state::a2bus_irq_w(int state)
{
	if (state == ASSERT_LINE)
	{
		raise_irq(IRQ_SLOT);
	}
	else
	{
		lower_irq(IRQ_SLOT);
	}
}

void apple2e_state::a2bus_nmi_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, state);
}

// TODO: this assumes /INH only on ROM, needs expansion to support e.g. phantom-slotting cards and etc.
void apple2e_state::a2bus_inh_w(int state)
{
	if (state == ASSERT_LINE)
	{
		// assume no cards are pulling /INH
		m_inh_slot = -1;

		// scan the slots to figure out which card(s) are INHibiting stuff
		for (int i = 0; i <= 7; i++)
		{
			if (m_slotdevice[i])
			{
				// this driver only can inhibit from 0xd000-0xffff
				if ((m_slotdevice[i]->inh_start() == 0xd000) &&
					(m_slotdevice[i]->inh_end() == 0xffff))
				{
					if ((m_slotdevice[i]->inh_type() & INH_READ) == INH_READ)
					{
						if (m_inh_bank != 1)
						{
							m_upperbank.select(1);
							m_inh_bank = 1;
						}
					}
					else
					{
						if (m_inh_bank != 0)
						{
							m_upperbank.select(0);
							m_inh_bank = 0;
						}
					}

					m_inh_slot = i;
					break;
				}
			}
		}

		// if no slots are inhibiting, make sure ROM is fully switched in
		if ((m_inh_slot == -1) && (m_inh_bank != 0))
		{
			m_upperbank.select(0);
			m_inh_bank = 0;
		}
	}
}

void apple2e_state::busy_w(int state)
{
	m_centronics_busy = state;
}

u8 apple2e_state::memexp_r(offs_t offset)
{
	u8 retval = m_exp_regs[offset];

	if (!m_exp_ram)
	{
		return read_floatingbus();
	}

	if (offset == 3)
	{
		if (m_exp_liveptr <= m_exp_addrmask)
		{
			retval = m_exp_ram[m_exp_liveptr];
		}
		else
		{
			retval = 0xff;
		}
		m_exp_liveptr++;
		m_exp_regs[0] = m_exp_liveptr & 0xff;
		m_exp_regs[1] = (m_exp_liveptr>>8) & 0xff;
		m_exp_regs[2] = ((m_exp_liveptr>>16) & 0xff) | m_exp_bankhior;
	}

	return retval;
}

void apple2e_state::memexp_w(offs_t offset, u8 data)
{
	if (!m_exp_ram)
	{
		return;
	}

	switch (offset & 0xf)
	{
		case 0:
			m_exp_wptr &= ~0xff;
			m_exp_wptr |= data;
			m_exp_regs[0] = m_exp_wptr & 0xff;
			m_exp_regs[1] = (m_exp_wptr>>8) & 0xff;
			m_exp_regs[2] = ((m_exp_wptr>>16) & 0xff) | m_exp_bankhior;
			m_exp_liveptr = m_exp_wptr;
			break;

		case 1:
			m_exp_wptr &= ~0xff00;
			m_exp_wptr |= (data<<8);
			m_exp_regs[0] = m_exp_wptr & 0xff;
			m_exp_regs[1] = (m_exp_wptr>>8) & 0xff;
			m_exp_regs[2] = ((m_exp_wptr>>16) & 0xff) | m_exp_bankhior;
			m_exp_liveptr = m_exp_wptr;
			break;

		case 2:
			m_exp_wptr &= ~0xff0000;
			m_exp_wptr |= (data<<16);
			m_exp_regs[0] = m_exp_wptr & 0xff;
			m_exp_regs[1] = (m_exp_wptr>>8) & 0xff;
			m_exp_regs[2] = ((m_exp_wptr>>16) & 0xff) | m_exp_bankhior;
			m_exp_liveptr = m_exp_wptr;
			break;

		case 3:
//            printf("Write %02x to RAM[%x]\n", data, m_liveptr);
			if (m_exp_liveptr <= m_exp_addrmask)
			{
				m_exp_ram[m_exp_liveptr] = data;
			}
			m_exp_liveptr++;
			m_exp_regs[0] = m_exp_liveptr & 0xff;
			m_exp_regs[1] = (m_exp_liveptr>>8) & 0xff;
			m_exp_regs[2] = ((m_exp_liveptr>>16) & 0xff) | m_exp_bankhior;
			break;

		default:
			m_exp_regs[offset] = data;
			break;
	}
}

/***************************************************************************
    START/RESET
***************************************************************************/

void apple2e_state::machine_start()
{
	m_ram_ptr = m_ram->pointer();
	m_rom_ptr = m_rom->base();
	m_ram_size = m_ram->size();
	m_speaker_state = 0;
	m_speaker->level_w(m_speaker_state);
	m_cassette_state = 0;
	m_cassette_out = 0;
	if (m_cassette)
	{
		m_cassette->output(-1.0f);
	}
	m_upperbank.select(0);
	m_lcbank.select(0);
	m_0000bank.select(0);
	m_0200bank.select(0);
	m_0400bank.select(0);
	m_0800bank.select(0);
	m_2000bank.select(0);
	m_4000bank.select(0);
	m_inh_bank = 0;

	m_migpage = 0;
	memset(m_migram, 0, 0x200);

	// expansion RAM size
	if (m_ram_size > (128*1024))
	{
		m_exp_addrmask = m_ram_size - (128*1024) - 1;
		m_exp_ram = m_ram_ptr + (128*1024);
	}
	else    // no expansion
	{
		m_exp_addrmask = 0;
		m_exp_ram = nullptr;
	}

	// precalculate joystick time constants
	m_x_calibration = attotime::from_nsec(10800).as_double();
	m_y_calibration = attotime::from_nsec(10800).as_double();

	// cache slot devices
	for (int i = 0; i <= 7; i++)
	{
		m_slotdevice[i] = m_a2bus->get_a2bus_card(i);
	}

	// and aux slot device if any
	m_aux_ptr = nullptr;
	m_aux_bank_ptr = nullptr;
	m_aux_mask = 0xffff;
	if (m_a2eauxslot)
	{
		m_auxslotdevice = m_a2eauxslot->get_a2eauxslot_card();
		if (m_auxslotdevice)
		{
			m_aux_ptr = m_auxslotdevice->get_vram_ptr();
			m_aux_bank_ptr = m_auxslotdevice->get_auxbank_ptr();
			m_aux_mask =  m_auxslotdevice->get_auxbank_mask();
		}
	}
	else    // IIc has 128K right on the motherboard
	{
		m_auxslotdevice = nullptr;

		if (m_ram_size >= (128*1024))
		{
			m_aux_ptr = &m_ram_ptr[0x10000];
			m_aux_bank_ptr = m_aux_ptr;
		}
	}

	// setup video pointers
	m_video->set_ram_pointers(m_ram_ptr, m_aux_ptr);
	m_video->set_aux_mask(m_aux_mask);
	m_video->set_char_pointer(memregion("gfx1")->base(), memregion("gfx1")->bytes());

	int ram_size = 0x10000;
	if (m_ram_size < 0x10000)
	{
		ram_size = m_ram_size;
	}

	for (int adr = 0; adr < ram_size; adr += 2)
	{
		// invert the fill pattern order on the ACE 500 and 2200, as it interacts with
		// Franklin's monitor not returning the same values as Apple's plus some
		// bugs in DOS 3.3.
		if ((m_isace500) || (m_isace2200))
		{
			m_ram_ptr[adr] = 0xff;
			m_ram_ptr[adr+1] = 0;
		}
		else
		{
			m_ram_ptr[adr] = 0;
			m_ram_ptr[adr + 1] = 0xff;
		}

		if (m_ram_size >= (128*1024))
		{
			m_aux_ptr[adr] = 0;
			m_aux_ptr[adr+1] = 0xff;
		}
	}

	m_inh_slot = -1;
	m_cnxx_slot = CNXX_UNCLAIMED;
	m_mockingboard4c = false;

	// remap CEC banking
	if ((m_rom_ptr[0x7bb3] == 0x8d) || (m_rom_ptr[0x7bb3] == 0xea) || (m_rom_ptr[0x7bb3] == 0x06))
	{
		m_lcbank.select(3);
		m_cec_ptr = m_cecbanks->base();
		m_iscec = true;
		m_iscecm = false;
		m_iscec2000 = false;

		// CEC-M
		// write addr C0B0 change to addr C600
		if ((m_rom_ptr[0x8000+0x4600] == 0xff) && (m_rom_ptr[0x8000+0x4601] == 0xff))
		{
			m_iscecm = true;
		}

		// CEC-2000
		if (m_rom_ptr[0x7bb3] == 0x06)
		{
			m_iscec2000 = true;
		}

		// data is bit-order reversed (and byte interleaved, which the ROM loader takes care of)
		// let's do that in the modern MAME way
		for (int i=0; i<0x040000; i++)
		{
			m_cec_remap[i] = bitswap<8>(m_cec_ptr[i], 0, 1, 2, 3, 4, 5, 6, 7);
		}

		// remap cec gfx1 rom
		// for ALTCHARSET
		u8 *rom = memregion("gfx1")->base();
		for(int i=0; i<0x1000; i++)
		{
			rom[i+0x1000] = rom[i];
		}
		for(int i=0x040*8; i<0x80*8; i++)
		{
			rom[i] = rom[i+0x1000-0x040*8];
		}
	}
	else
	{
		m_iscec = false;
		m_iscecm = false;
		m_iscec2000 = false;
	}

	if ((m_has_laser_mouse) || (m_isace500) || (m_isace2200))
	{
		m_strobe_timer = timer_alloc(FUNC(apple2e_state::update_laserprn_strobe), this);
		m_next_strobe = 1U;
	}

	m_joystick_x1_time = m_joystick_x2_time = m_joystick_y1_time = m_joystick_y2_time = 0;
	m_reset_latch = false;

	last_mx = 0;
	last_my = 0;
	count_x = 0;
	count_y = 0;
	m_x0 = false;
	m_y0 = false;

	m_35sel = false;
	m_hdsel = false;
	m_intdrive = false;

	// setup save states
	save_item(NAME(m_speaker_state));
	save_item(NAME(m_cassette_state));
	save_item(NAME(m_joystick_x1_time));
	save_item(NAME(m_joystick_y1_time));
	save_item(NAME(m_joystick_x2_time));
	save_item(NAME(m_joystick_y2_time));
	save_item(NAME(m_lastchar));
	save_item(NAME(m_strobe));
	save_item(NAME(m_franklin_last_fkeys));
	save_item(NAME(m_franklin_strobe));
	save_item(NAME(m_transchar));
	save_item(NAME(m_inh_slot));
	save_item(NAME(m_inh_bank));
	save_item(NAME(m_cnxx_slot));
	save_item(NAME(m_an0));
	save_item(NAME(m_an1));
	save_item(NAME(m_an2));
	save_item(NAME(m_an3));
	save_item(NAME(m_intcxrom));
	save_item(NAME(m_slotc3rom));
	save_item(NAME(m_altzp));
	save_item(NAME(m_ramrd));
	save_item(NAME(m_ramwrt));
	save_item(NAME(m_ioudis));
	save_item(NAME(m_vbl));
	save_item(NAME(m_vblmask));
	save_item(NAME(m_romswitch));
	save_item(NAME(m_irqmask));
	save_item(NAME(m_anykeydown));
	save_item(NAME(m_repeatdelay));
	save_item(NAME(m_xy));
	save_item(NAME(m_x0edge));
	save_item(NAME(m_y0edge));
	save_item(NAME(last_mx));
	save_item(NAME(last_my));
	save_item(NAME(count_x));
	save_item(NAME(count_y));
	save_item(NAME(m_x0));
	save_item(NAME(m_x1));
	save_item(NAME(m_y0));
	save_item(NAME(m_y1));
	save_item(NAME(m_xirq));
	save_item(NAME(m_yirq));
	save_item(NAME(m_migram));
	save_item(NAME(m_migpage));
	save_item(NAME(m_exp_regs));
	save_item(NAME(m_exp_wptr));
	save_item(NAME(m_exp_liveptr));
	save_item(NAME(m_exp_bankhior));
	save_item(NAME(m_exp_addrmask));
	save_item(NAME(m_lcram));
	save_item(NAME(m_lcram2));
	save_item(NAME(m_lcprewrite));
	save_item(NAME(m_lcwriteenable));
	save_item(NAME(m_mockingboard4c));
	save_item(NAME(m_intc8rom));
	save_item(NAME(m_cec_bank));
	save_item(NAME(m_35sel));
	save_item(NAME(m_hdsel));
	save_item(NAME(m_intdrive));
	save_item(NAME(m_accel_unlocked));
	save_item(NAME(m_accel_stage));
	save_item(NAME(m_accel_fast));
	save_item(NAME(m_accel_present));
	save_item(NAME(m_accel_slotspk));
	save_item(NAME(m_accel_gameio));
	save_item(NAME(m_accel_temp_slowdown));
	save_item(NAME(m_accel_laser));
	save_item(NAME(m_accel_speed));
	save_item(NAME(m_next_strobe));
	save_item(NAME(m_centronics_busy));
	save_item(NAME(m_ace500rombank));
	save_item(NAME(m_ace_cnxx_bank));
	save_item(NAME(m_ace2200_axxx_bank));
	save_item(NAME(m_laser_speed));
	save_item(NAME(m_laser_fdc_on));
	save_item(NAME(m_reset_latch));
}

void apple2e_state::machine_reset()
{
	// All MMU switches off (80STORE, RAMRD, RAMWRT, INTCXROM, ALTZP, SLOTC3ROM, PAGE2, HIRES, INTC8ROM)
	// Sather, Fig 5.13
	m_ramrd = false;
	m_ramwrt = false;
	m_altzp = false;
	m_slotc3rom = false;
	m_intc8rom = false;

	// Certain IOU switches off (80STORE, 80COL, ALTCHR, PAGE2, HIRES, AN0, AN1, AN2, AN3)
	// Sather, Fig 7.1
	m_video->a80store_w(false);
	m_video->a80col_w(false);
	m_video->altcharset_w(false);
	m_video->page2_w(false);
	m_video->res_w(0);

	// IIe IOU
	m_an0 = m_an1 = m_an2 = m_an3 = false;
	m_gameio->an0_w(0);
	m_gameio->an1_w(0);
	m_gameio->an2_w(0);
	m_gameio->an3_w(0);

	// IIc IOU
	m_ioudis = true;
	m_romswitch = false;

	// LC resets to read ROM, write RAM, no pre-write, bank 2
	// Sather, Fig 5.13
	m_lcram = false;
	m_lcram2 = true;
	m_lcprewrite = false;
	m_lcwriteenable = true;

	m_video->monohgr_w(m_iscecm);
	m_vbl = m_vblmask = false;
	m_irqmask = 0;
	m_strobe = 0;
	m_franklin_last_fkeys = 0;
	m_franklin_strobe = 0x80;
	m_transchar = 0;
	m_anykeydown = false;
	m_repeatdelay = 10;
	m_xy = false;
	m_x0edge = false;
	m_y0edge = false;
	m_x1 = false;
	m_y1 = false;
	m_xirq = false;
	m_yirq = false;
	m_mockingboard4c = false;
	m_cec_bank = 0;
	m_accel_unlocked = false;
	m_accel_stage = 0;
	m_accel_slotspk = 0x41; // speaker and slot 6 slow
	m_accel_gameio = 0x40;  // paddle delay on
	m_accel_present = false;
	m_accel_temp_slowdown = false;
	m_accel_fast = false;
	m_centronics_busy = false;
	m_35sel = false;

	// is Zip enabled?
	if (m_sysconfig.read_safe(0) & 0x10)
	{
		m_accel_present = true;
	}

	// IIe prefers INTCXROM default to off, IIc has it always on
	if (m_rom_ptr[0x3bc0] == 0x00)
	{
		m_intcxrom = true;
		m_slotc3rom = false;
		if (!m_isace500)
		{
			m_isiic = true;
		}

		if (m_rom_ptr[0x3bbf] == 0x05)
		{
			m_isiicplus = true;
			m_accel_present = true;
		}
		else
		{
			m_isiicplus = false;
		}
	}
	else
	{
		m_intcxrom = false;
		m_isiic = false;
		m_isiicplus = false;
	}

	u8 config = m_sysconfig.read_safe(0) & 0x30;

	if (((config & 0x10) == 0x10) || (m_isiicplus))
	{
		m_accel_speed = 4000000;    // Zip speed, set if present, even if not active initially

		if (((config & 0x20) == 0x20) || (m_isiicplus))
		{
			accel_full_speed();
			m_accel_fast = true;
		}
	}

	if (m_accel_laser)
	{
		m_accel_present = true;
		m_accel_speed = 1021800;
	}

	if (m_has_laser_mouse)
	{
	   a2bus_laser128_device *printer_slot = static_cast<a2bus_laser128_device *>(m_slotdevice[1]);

	   if (m_sysconfig->read() & 0x08)
	   {
		  printer_slot->set_parallel_printer(true);
	   }
	   else
	   {
		  printer_slot->set_parallel_printer(false);
	   }

	}

	m_exp_bankhior = 0xf0;

	// sync up the banking with the variables.
	lcrom_update();
	auxbank_update();
	update_slotrom_banks();
}

// called before machine_start() so we have to be careful
void apple2e_state::init_128ex()
{
	m_accel_laser = true;
	m_has_laser_mouse = true;
}

void apple2e_state::init_laser128()
{
	m_has_laser_mouse = true;
}

void apple2e_state::init_ace500()
{
	m_isace500 = true;
	m_ace_cnxx_bank = false;
}

void apple2e_state::init_ace2200()
{
	m_isace2200 = true;
}

void apple2e_state::init_pal()
{
	m_pal = true;
}

void apple2e_state::raise_irq(int irq)
{
	m_irqmask |= (1 << irq);

	if (m_irqmask)
	{
		m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE);
	}
}

void apple2e_state::lower_irq(int irq)
{
	m_irqmask &= ~(1 << irq);

	if (!m_irqmask)
	{
		m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE);
	}
}

/***************************************************************************
    VIDEO
***************************************************************************/

TIMER_DEVICE_CALLBACK_MEMBER(apple2e_state::apple2_interrupt)
{
	int scanline = param;

	if ((m_isiic) || (m_has_laser_mouse) || (m_isace500))
	{
		update_iic_mouse();
	}

	// update character selection
	if (m_kbd_lang_sel)
	{
		u8 charset_id = m_kbd_lang_sel->read() & 0x0f;
		if (m_video->get_iie_langsw() != charset_id)
		{
			m_video->set_iie_langsw(charset_id);
		}
	}

	if (scanline == 192)
	{
		m_vbl = true;

		if (m_vblmask)
		{
			raise_irq(IRQ_VBL);
		}

		// check for ctrl-reset
		if ((m_kbspecial->read() & 0x88) == 0x88)
		{
			if (!m_reset_latch)
			{
				m_reset_latch = true;
				m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);

				// All MMU switches off (80STORE, RAMRD, RAMWRT, INTCXROM, ALTZP, SLOTC3ROM, PAGE2, HIRES, INTC8ROM)
				// Sather, Fig 5.13
				m_ramrd = false;
				m_ramwrt = false;
				m_altzp = false;
				m_slotc3rom = false;
				m_intc8rom = false;

				// reset intcxrom to default
				if ((m_isiic) || (m_isace500))
				{
					m_intcxrom = true;
				}
				else
				{
					m_intcxrom = false;
					m_slotc3rom = false;
				}

				// Certain IOU switches off (80STORE, 80COL, ALTCHR, PAGE2, HIRES, AN0, AN1, AN2, AN3)
				// Sather, Fig 7.1
				m_video->a80store_w(false);
				m_video->a80col_w(false);
				m_video->altcharset_w(false);
				m_video->page2_w(false);
				m_video->res_w(0);

				// IIe IOU
				m_an0 = m_an1 = m_an2 = m_an3 = false;
				m_gameio->an0_w(0);
				m_gameio->an1_w(0);
				m_gameio->an2_w(0);
				m_gameio->an3_w(0);

				// LC resets to read ROM, write RAM, no pre-write, bank 2
				// Sather, Fig 5.13
				m_lcram = false;
				m_lcram2 = true;
				m_lcprewrite = false;
				m_lcwriteenable = true;

				lcrom_update();
				auxbank_update();
				update_slotrom_banks();
			}
		}
		else    // user released Control-Reset
		{
			if (m_reset_latch)
			{
				m_reset_latch = false;
				// allow cards to see reset
				m_a2bus->reset_bus();
				m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
			}
		}

		// check Franklin F-keys
		if ((m_isace500) || (m_isace2200))
		{
			const u32 uFkeys = m_franklin_fkeys->read();

			if ((uFkeys ^ m_franklin_last_fkeys) && uFkeys)
			{
				m_transchar = count_leading_zeros_32(uFkeys) + 0x20;
				m_strobe = 0x80;
				m_franklin_strobe = 0;
			}
			m_franklin_last_fkeys = uFkeys;
		}
	}
}

/***************************************************************************
    I/O
***************************************************************************/
void apple2e_state::accel_full_speed()
{
	m_maincpu->set_unscaled_clock(m_accel_speed);
}

void apple2e_state::accel_normal_speed()
{
	m_maincpu->set_unscaled_clock(1021800);
}

void apple2e_state::accel_slot(int slot)
{
	if ((m_accel_present) && (m_accel_slotspk & (1<<slot)))
	{
		m_accel_temp_slowdown = true;
		m_acceltimer->adjust(attotime::from_msec(52));
		accel_normal_speed();
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(apple2e_state::accel_timer)
{
	if (m_accel_fast)
	{
		accel_full_speed();
	}
	m_accel_temp_slowdown = false;
	m_acceltimer->adjust(attotime::never);
}

void apple2e_state::auxbank_update()
{
	int ramwr = (m_ramrd ? 1 : 0) | (m_ramwrt ? 2 : 0);

	if (!m_iscec)   // real Apple II
	{
		m_0000bank.select(m_altzp ? 1 : 0);
		m_0200bank.select(ramwr);

		if (m_video->get_80store())
		{
			if (m_video->get_page2())
			{
				m_0400bank.select(3);
			}
			else
			{
				m_0400bank.select(0);
			}
		}
		else
		{
			m_0400bank.select(ramwr);
		}

		m_0800bank.select(ramwr);

		if ((m_video->get_80store()) && (m_video->get_hires()))
		{
			if (m_video->get_page2())
			{
				m_2000bank.select(3);
			}
			else
			{
				m_2000bank.select(0);
			}
		}
		else
		{
			m_2000bank.select(ramwr);
		}

		m_4000bank.select(ramwr);
	}
	else    // CEC
	{
		if (m_ramrd)
		{
			m_4000bank.select(4);    // read CEC bank, write normal RAM
		}
		else
		{
			m_4000bank.select(0);    // read/write RAM
		}
	}
}

void apple2e_state::update_slotrom_banks()
{
	if (!m_iscec)
	{
		int cxswitch = 0;

		// IIc and IIc+ have working (readable) INTCXROM/SLOTC3ROM switches, but
		// internal ROM is always present in the slots.
		if ((m_intcxrom) || (m_isiic) || (m_isace500))
		{
			if (m_romswitch)
			{
				cxswitch = 2;
			}
			else
			{
				cxswitch = 1;
			}
		}

		m_c100bank.select(cxswitch);
		m_c400bank.select(cxswitch);

		//printf("intcxrom %d intc8rom %d cnxx_slot %d isiic %d romswitch %d\n", m_intcxrom, m_intc8rom, m_cnxx_slot, m_isiic, m_romswitch);
		if ((m_intcxrom) || (m_intc8rom) || (m_isiic))
		{
			if (m_romswitch)
			{
				m_c800bank.select(2);
			}
			else
			{
				m_c800bank.select(1);
			}
		}
		else
		{
			m_c800bank.select(0);
		}

		if ((m_intcxrom) || (!m_slotc3rom) || (m_isiic))
		{
			if (m_romswitch)
			{
				m_c300bank.select(2);
			}
			else
			{
				m_c300bank.select(1);
			}
		}
		else
		{
			m_c300bank.select(0);
		}
	}
	else    // CEC has only ROM here
	{
		if (!m_intcxrom)
		{
			m_c100bank.select(3);
			m_c400bank.select(3);
			m_c800bank.select(3);
		}
		else
		{
			m_c100bank.select(4);
			m_c400bank.select(4);
			m_c800bank.select(4);
		}

		if ((m_intcxrom) || (!m_slotc3rom))
		{
			m_c300bank.select(4);
		}
		else
		{
			m_c300bank.select(3);
		}
	}
}

void apple2e_state::lc_update(int offset, bool writing)
{
	bool old_lcram = m_lcram;

	//any even access disables pre-write and writing
	if ((offset & 1) == 0)
	{
		m_lcprewrite = false;
		m_lcwriteenable = false;
	}

	//any write disables pre-write
	//has no effect on write-enable if writing was enabled already
	if (writing == true)
	{
		m_lcprewrite = false;
	}
	//first odd read enables pre-write, second one enables writing
	else if ((offset & 1) == 1)
	{
		if (m_lcprewrite == false)
		{
			m_lcprewrite = true;
		}
		else
		{
			m_lcwriteenable = true;
		}
	}

	switch (offset & 3)
	{
		case 0:
		case 3:
		{
			m_lcram = true;
			break;
		}

		case 1:
		case 2:
		{
			m_lcram = false;
			break;
		}
	}

	m_lcram2 = false;

	if (!(offset & 8))
	{
		m_lcram2 = true;
	}

	if (m_lcram != old_lcram)
	{
		lcrom_update();
	}

	#if 0
	printf("LC: new state %c%c dxxx=%04x altzp=%d (PC=%x)\n",
			m_lcram ? 'R' : 'x',
			m_lcwriteenable ? 'W' : 'x',
			m_lcram2 ? 0x1000 : 0x0000,
			m_altzp, m_maincpu->pc());
	#endif
}

void apple2e_state::lcrom_update()
{
	if (m_iscec)
	{
		cec_lcrom_update();
	}
	else
	{
		if (m_lcram)
		{
			m_lcbank.select(1);
		}
		else
		{
			if (m_romswitch)
			{
				m_lcbank.select(2);
			}
			else
			{
				m_lcbank.select(0);
			}
		}
	}
}

void apple2e_state::cec_lcrom_update()
{
	if (m_altzp)
	{
		m_lcbank.select(5);
	}
	else
	{
		if (m_lcram)
		{
			m_lcbank.select(1);
		}
		else
		{
			m_lcbank.select(3);
		}
	}
}

// most softswitches don't care about read vs write, so handle them here
void apple2e_state::do_io(int offset, bool is_iic)
{
	if(machine().side_effects_disabled()) return;

	// Handle C058-C05F according to IOUDIS
	if ((offset & 0x58) == 0x58)
	{
		// IIc-specific switches
		if (((m_isiic || m_isace500) && (!m_accel_unlocked)) && (!m_ioudis))
		{
			switch (offset)
			{
				case 0x58:  // DisXY
					m_xy = false; break;

				case 0x59:  // EnbXY
					m_xy = true; break;

				case 0x5a:  // DisVBL
					lower_irq(IRQ_VBL);
					m_vblmask = false; break;

				case 0x5b:  // EnVBL
					m_vblmask = true; break;

				case 0x5c:  // RisX0Edge
					m_x0edge = false; break;

				case 0x5d:  // FalX0Edge
					m_x0edge = true; break;

				case 0x5e:  // RisY0Edge
					if (!m_ioudis)
					{
						m_y0edge = false;
					}
					break;

				case 0x5f:  // FalY0Edge
					if (!m_ioudis)
					{
						m_y0edge = true;
					}
					break;
			}
		}

		// IIe does not have IOUDIS (ref: on-h/w tests by TomCh)
		if ((m_ioudis) || (!m_isiic && !m_isace500))
		{
			switch (offset)
			{
				case 0x5e:  // SETDHIRES
					m_video->dhires_w(0);
					break;

				case 0x5f:  // CLRDHIRES
					m_video->dhires_w(1);
					break;
			}
		}
// ComputerEyes seems to indicate that the annuciators get tickled regardless of the IOUDIS state.
	}

	if ((offset & 0xf0) == 0x30) // speaker, $C030 is really 30-3f
	{
		m_speaker_state ^= 1;
		m_speaker->level_w(m_speaker_state);
		if ((m_accel_present) && (m_accel_slotspk & 1))
		{
			m_accel_temp_slowdown = true;
			m_acceltimer->adjust(attotime::from_msec(5));
			accel_normal_speed();
		}
		return;
	}

	if ((offset & 0xf0) == 0x20) // tape out / ROM bank on IIc/IIc+
	{
		if (m_cassette)
		{
			// Officially Apple only documents this softswitch at $c020 but
			// all models with a tape interface will respond to any of the $c02x
			// addresses.
			m_cassette_state ^= 1;
			m_cassette->output(m_cassette_state ? 1.0f : -1.0f);
		}

		if (is_iic)
		{
			// Apple IIc Tech Reference 1st edition lists this softswitch at $c028 while
			// the 2nd edition lists it at $c02x.  Both the IIc and IIc Plus will respond to
			// $c02x.
			m_romswitch = !m_romswitch;
			update_slotrom_banks();
			lcrom_update();

			// MIG is reset when ROMSWITCH turns off
			if ((m_isiicplus) && !(m_romswitch))
			{
				m_migpage = 0;
				m_intdrive = false;
				m_35sel = false;
			}
		}
	}

	switch (offset)
	{
		case 0x40:  // utility strobe (not available on IIc)
			if (!is_iic)
			{
				m_gameio->strobe_w(0);
				m_gameio->strobe_w(1);
			}
			break;

		case 0x48:  // (IIc only) clear mouse X/Y interrupt flags
			m_xirq = m_yirq = false;
			lower_irq(IRQ_MOUSEXY);
			break;

		case 0x50:  // graphics mode
			m_video->txt_w(0);
			break;

		case 0x51:  // text mode
			m_video->txt_w(1);
			break;

		case 0x52:  // no mix
			m_video->mix_w(0);
			break;

		case 0x53:  // mixed mode
			m_video->mix_w(1);
			break;

		case 0x54:  // set page 1
			m_video->scr_w(0);
			auxbank_update();
			break;

		case 0x55:  // set page 2
			m_video->scr_w(1);
			auxbank_update();
			break;

		case 0x56: // select lo-res
			m_video->res_w(0);
			auxbank_update();
			break;

		case 0x57: // select hi-res
			m_video->res_w(1);
			auxbank_update();
			break;

		case 0x58: // AN0 off
			m_an0 = false;
			m_gameio->an0_w(0);
			break;

		case 0x59: // AN0 on
			m_an0 = true;
			m_gameio->an0_w(1);
			break;

		case 0x5a: // AN1 off
			m_an1 = false;
			m_gameio->an1_w(0);
			break;

		case 0x5b: // AN1 on
			m_an1 = true;
			m_gameio->an1_w(1);
			break;

		case 0x5c: // AN2 off
			m_an2 = false;
			m_gameio->an2_w(0);
			break;

		case 0x5d: // AN2 on
			m_an2 = true;
			m_gameio->an2_w(1);
			break;

		case 0x5e: // AN3 off
			m_an3 = false;
			m_gameio->an3_w(0);
			break;

		case 0x5f: // AN3 on
			m_an3 = true;
			m_gameio->an3_w(1);
			break;

		case 0x68:  // IIgs STATE register, which ProDOS touches
			break;

		// trigger joypad read
		case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
		case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f:
			if ((is_iic) || (m_isace500))
			{
				m_vbl = false;
				lower_irq(IRQ_VBL);
			}

			// Zip paddle flag
			if ((m_accel_present) && (BIT(m_accel_gameio, 6)))
			{
				m_accel_temp_slowdown = true;
				m_acceltimer->adjust(attotime::from_msec(5));
				accel_normal_speed();
			}

			// 558 monostable one-shot timers; a running timer cannot be restarted
			if (machine().time().as_double() >= m_joystick_x1_time)
			{
				m_joystick_x1_time = machine().time().as_double() + m_x_calibration * m_gameio->pdl0_r();
			}
			if (machine().time().as_double() >= m_joystick_y1_time)
			{
				m_joystick_y1_time = machine().time().as_double() + m_y_calibration * m_gameio->pdl1_r();
			}
			if (machine().time().as_double() >= m_joystick_x2_time)
			{
				m_joystick_x2_time = machine().time().as_double() + m_x_calibration * m_gameio->pdl2_r();
			}
			if (machine().time().as_double() >= m_joystick_y2_time)
			{
				m_joystick_y2_time = machine().time().as_double() + m_y_calibration * m_gameio->pdl3_r();
			}
			break;

		default:
			logerror("do_io: unknown switch $C0%02X\n", offset);
			break;
	}
}

u8 apple2e_state::c000_r(offs_t offset)
{
	if(machine().side_effects_disabled()) return read_floatingbus();
	const u8 uFloatingBus7 = read_floatingbus() & 0x7f;

	if ((offset & 0xf0) == 0x00) // keyboard latch, $C000 is really 00-0F
	{
		return m_transchar | m_strobe;
	}

	switch (offset)
	{
		case 0x10:  // read any key down, reset keyboard strobe
			{
				u8 rv = m_transchar | (m_anykeydown ? 0x80 : 0x00);
				m_strobe = 0;
				return rv;
			}

		case 0x11:  // read LCRAM2 (LC Dxxx bank)
			return (m_lcram2 ? 0x80 : 0x00) | m_transchar;

		case 0x12:  // read LCRAM (is LC readable?)
			return (m_lcram ? 0x80 : 0x00) | m_transchar;

		case 0x13:  // read RAMRD
			return (m_ramrd ? 0x80 : 0x00) | m_transchar;

		case 0x14:  // read RAMWRT
			return (m_ramwrt ? 0x80 : 0x00) | m_transchar;

		case 0x15:  // read INTCXROM
			return (m_intcxrom ? 0x80 : 0x00) | m_transchar;

		case 0x16:  // read ALTZP
			return (m_altzp ? 0x80 : 0x00) | m_transchar;

		case 0x17:  // read SLOTC3ROM
			return (m_slotc3rom ? 0x80 : 0x00) | m_transchar;

		case 0x18:  // read 80STORE
			return (m_video->get_80store() ? 0x80 : 0x00) | m_transchar;

		case 0x19:  // read VBLBAR
			return (m_screen->vblank() ? 0x00 : 0x80) | m_transchar;

		case 0x1a:  // read TEXT
			return (m_video->get_graphics() ? 0x00 : 0x80) | m_transchar;

		case 0x1b:  // read MIXED
			return (m_video->get_mix() ? 0x80 : 0x00) | m_transchar;

		case 0x1c:  // read PAGE2
			return (m_video->get_page2() ? 0x80 : 0x00) | m_transchar;

		case 0x1d:  // read HIRES
			return (m_video->get_hires() ? 0x80 : 0x00) | m_transchar;

		case 0x1e:  // read ALTCHARSET
			return (m_video->get_altcharset() ? 0x80 : 0x00) | m_transchar;

		case 0x1f:  // read 80COL
			return (m_video->get_80col() ? 0x80 : 0x00) | m_transchar;

		case 0x26:  // Ace 2x00 DIP switches
			if (m_isace2200)
			{
				return (m_sysconfig->read() & 0x80) | uFloatingBus7;
			}
			break;

		case 0x27: // Ace 2x00 F key strobe
			if (m_isace2200)
			{
				m_strobe = 0;
				const u8 rv = m_franklin_strobe;
				m_franklin_strobe = 0x80;
				return rv;
			}
			break;

		case 0x60: // cassette in
		case 0x68:
			if (m_cassette)
			{
				return (m_cassette->input() > 0.0 ? 0x80 : 0) | uFloatingBus7;
			}
			return uFloatingBus7;

		case 0x61:  // button 0 or Open Apple
		case 0x69:
			return ((m_gameio->sw0_r() || (m_kbspecial->read() & 0x10)) ? 0x80 : 0) | uFloatingBus7;

		case 0x62:  // button 1 or Solid Apple
		case 0x6a:
			return ((m_gameio->sw1_r() || (m_kbspecial->read() & 0x20)) ? 0x80 : 0) | uFloatingBus7;

		case 0x63:  // button 2 or SHIFT key
		case 0x6b:
			return ((m_gameio->sw2_r() || (m_kbspecial->read() & 0x06)) ? 0x80 : 0) | uFloatingBus7;

		case 0x64:  // joy 1 X axis
		case 0x6c:
			if (!m_gameio->is_device_connected()) return 0x80 | uFloatingBus7;
			return ((machine().time().as_double() < m_joystick_x1_time) ? 0x80 : 0) | uFloatingBus7;

		case 0x65:  // joy 1 Y axis
		case 0x6d:
			if (!m_gameio->is_device_connected()) return 0x80 | uFloatingBus7;
			return ((machine().time().as_double() < m_joystick_y1_time) ? 0x80 : 0) | uFloatingBus7;

		case 0x66: // joy 2 X axis
		case 0x6e:
			if (!m_gameio->is_device_connected()) return 0x80 | uFloatingBus7;
			return ((machine().time().as_double() < m_joystick_x2_time) ? 0x80 : 0) | uFloatingBus7;

		case 0x67: // joy 2 Y axis
		case 0x6f:
			if (!m_gameio->is_device_connected()) return 0x80 | uFloatingBus7;
			return ((machine().time().as_double() < m_joystick_y2_time) ? 0x80 : 0) | uFloatingBus7;

		case 0x78: case 0x7a: case 0x7c: case 0x7e:  // read IOUDIS
			return (m_ioudis ? 0x00 : 0x80) | uFloatingBus7;

		case 0x79: case 0x7b: case 0x7d: case 0x7f:  // read DHIRES
			return (m_video->get_dhires() ? 0x00 : 0x80) | uFloatingBus7;

		default:
			do_io(offset, false);

			if (m_accel_unlocked)
			{
				if (offset == 0x5b)
				{
					// bit 7 is a 1.0035 millisecond clock; the value changes every 0.50175 milliseconds
					const int time = machine().time().as_ticks(1.0f / 0.00050175f);
					if (time & 1)
					{
						return 0x03;
					}
					else
					{
						return 0x83;
					}
				}
				else if (offset == 0x5c)
				{
					return m_accel_slotspk;
				}
			}
			break;
	}

	return read_floatingbus();
}

u8 apple2e_state::c000_laser_r(offs_t offset)
{
	u8 uFloatingBus7 = read_floatingbus() & 0x7f;

	switch (offset)
	{
		case 0x60: // 40/80 column switch
			return ((m_sysconfig.read_safe(0) & 0x40) ? 0x80 : 0) | uFloatingBus7;

		case 0x63:  // read mouse button
			return (m_mouseb->read() ? 0 : 0x80) | uFloatingBus7;

		case 0x66:  // read mouse xdir
			return (m_x1 ? 0x80 : 0) | uFloatingBus7;

		case 0x67:  // read mouse ydir
			return (m_y1 ? 0x80 : 0) | uFloatingBus7;
	}

	return c000_r(offset);
}

void apple2e_state::laser_calc_speed()
{
	if (m_laser_fdc_on)
	{
		accel_normal_speed();
		m_accel_fast = false;
		return;
	}

	switch ((m_laser_speed & 0xc0) >> 6)
	{
		case 0:
		case 1:
			accel_normal_speed();
			m_accel_fast = false;
			break;

		case 2:
			m_accel_speed = A2BUS_7M_CLOCK.value()/3;   // 2.38 MHz
			m_accel_fast = true;
			accel_full_speed();
			break;

		case 3:
			m_accel_speed = A2BUS_7M_CLOCK.value()/2;   // 3.58 MHz
			m_accel_fast = true;
			accel_full_speed();
			break;
	}
}

void apple2e_state::c000_laser_w(offs_t offset, u8 data)
{
	if ((m_accel_laser) && (offset == 0x74))
	{
		m_laser_speed = data;
		laser_calc_speed();
	}
	else
	{
		if ((offset & 0xf0) == 0x70)
		{
			lower_irq(IRQ_VBL);
		}

		c000_w(offset, data);
	}
}

u8 apple2e_state::laserprn_busy_r()
{
	u8 retval = read_floatingbus() & 0x7f;

	if (m_centronics_busy)
	{
		retval |= 0x80;
	}

	return retval;
}

void apple2e_state::laserprn_w(u8 data)
{
	m_printer_out->write(data);

   // generate strobe pulse after one clock cycle
	m_next_strobe = 0U;
	if (!m_strobe_timer->enabled())
	{
	   m_strobe_timer->adjust(attotime::from_hz(1021800));
	}
}

TIMER_CALLBACK_MEMBER(apple2e_state::update_laserprn_strobe)
{
	m_printer_conn->write_strobe(m_next_strobe);
	if (!m_next_strobe)
	{
		m_next_strobe = 1U;
		m_strobe_timer->adjust(attotime::from_hz(1021800));
	}
}

void apple2e_state::c000_w(offs_t offset, u8 data)
{
	if(machine().side_effects_disabled()) return;

	if ((offset & 0xf0) == 0x10) // clear keyboard latch, $C010 is really 10-1F
	{
		m_strobe = 0;
		return;
	}

	switch (offset)
	{
		case 0x00:  // 80STOREOFF
			m_video->a80store_w(false);
			auxbank_update();
			break;

		case 0x01:  // 80STOREON
			m_video->a80store_w(true);
			auxbank_update();
			break;

		case 0x02:  // RAMRDOFF
			m_ramrd = false;
			auxbank_update();
			break;

		case 0x03:  // RAMRDON
			m_ramrd = true;
			auxbank_update();
			break;

		case 0x04:  // RAMWRTOFF
			m_ramwrt = false;
			auxbank_update();
			break;

		case 0x05:  // RAMWRTON
			m_ramwrt = true;
			auxbank_update();
			break;

		case 0x06:  // INTCXROMOFF
			m_intcxrom = false;
			update_slotrom_banks();
			break;

		case 0x07:  // INTCXROMON
			m_intcxrom = true;
			update_slotrom_banks();
			break;

		case 0x08:  // ALTZPOFF
			m_altzp = false;
			auxbank_update();
			if (m_iscec)
			{
				cec_lcrom_update();
			}
			break;

		case 0x09:  // ALTZPON
			m_altzp = true;
			auxbank_update();
			if (m_iscec)
			{
				cec_lcrom_update();
			}
			break;

		case 0x0a:  // SETINTC3ROM
			m_slotc3rom = false;
			update_slotrom_banks();
			break;

		case 0x0b:  // SETSLOTC3ROM
			m_slotc3rom = true;
			update_slotrom_banks();
			break;

		case 0x0c:  // 80COLOFF
			m_video->a80col_w(false);
			break;

		case 0x0d:  // 80COLON
			m_video->a80col_w(true);
			break;

		case 0x0e:  // ALTCHARSETOFF
			m_video->altcharset_w(false);
			break;

		case 0x0f:  // ALTCHARSETON
			m_video->altcharset_w(true);
			break;

		case 0x20:  // cassette output
			if (m_cassette)
			{
				m_cassette_out ^= 1;
				m_cassette->output(m_cassette_out ? 1.0f : -1.0f);
			}
			break;

		case 0x5a:  // Zip accelerator unlock
			if (m_sysconfig.read_safe(0) & 0x10)
			{
				if (data == 0x5a)
				{
					m_accel_stage++;
					if (m_accel_stage == 4)
					{
						m_accel_unlocked = true;
					}
				}
				else if (data == 0xa5)
				{
					// lock
					m_accel_unlocked = false;
					m_accel_stage = 0;
				}
				else if (m_accel_unlocked)
				{
					// disable acceleration
					m_accel_fast = false;
					accel_normal_speed();
				}
			}
			break;

		case 0x5b: // Zip full speed
			if (m_accel_unlocked)
			{
				m_accel_fast = true;
				accel_full_speed();
			}
			break;

		case 0x5c: // Zip slot/speaker flags
			if (m_accel_unlocked)
			{
				m_accel_slotspk = data;
			}
			break;

		case 0x5f: // Zip game I/O flags
			if (m_accel_unlocked)
			{
				m_accel_gameio = data;
			}
			else
			{
				do_io(offset, false);
			}
			break;

		case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
		case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d:
			if (m_isace2200)
			{
				if (offset == 0x78)
				{
					m_ace2200_axxx_bank = false;
				}
				else if (offset == 0x79)
				{
					m_ace2200_axxx_bank = true;
				}
			}
			if (m_auxslotdevice)
			{
				m_auxslotdevice->write_c07x(offset & 0xf, data);

				// card may have banked auxram; get a new bank pointer
				m_aux_bank_ptr = m_auxslotdevice->get_auxbank_ptr();
			}
			do_io(offset, false);    // make sure it also side-effect resets the paddles as documented
			break;

		case 0x7e:  // SETIOUDIS
			m_ioudis = true; break;

		case 0x7f:  // CLRIOUDIS
			m_ioudis = false; break;

		default:
			do_io(offset, false);
			break;
	}
}

u8 apple2e_state::c000_iic_r(offs_t offset)
{
	if(machine().side_effects_disabled()) return read_floatingbus();
	u8 uFloatingBus7 = read_floatingbus() & 0x7f;

	if ((offset & 0xf0) == 0x00) // keyboard latch, $C000 is really 00-0F
	{
		return m_transchar | m_strobe;
	}

	switch (offset)
	{
		case 0x10:  // read any key down, reset keyboard strobe
			{
				u8 rv = m_transchar | (m_anykeydown ? 0x80 : 0x00);
				m_strobe = 0;
				return rv;
			}

		case 0x11:  // read LCRAM2 (LC Dxxx bank), also reads like $C010 without strobe reset
			return (m_lcram2 ? 0x80 : 0x00) | m_transchar;

		case 0x12:  // read LCRAM (is LC readable?)
			return (m_lcram ? 0x80 : 0x00) | m_transchar;

		case 0x13:  // read RAMRD
			return (m_ramrd ? 0x80 : 0x00) | m_transchar;

		case 0x14:  // read RAMWRT
			return (m_ramwrt ? 0x80 : 0x00) | m_transchar;

		case 0x15:  // read & reset mouse X0 interrupt flag
			lower_irq(IRQ_MOUSEXY);
			return (m_xirq ? 0x80 : 0x00) | m_transchar;

		case 0x16:  // read ALTZP
			return (m_altzp ? 0x80 : 0x00) | m_transchar;

		case 0x17:  // read & reset mouse Y0 interrupt flag
			lower_irq(IRQ_MOUSEXY);
			return (m_yirq ? 0x80 : 0x00) | m_transchar;

		case 0x18:  // read 80STORE
			return (m_video->get_80store() ? 0x80 : 0x00) | m_transchar;

		case 0x19:  // read VBL
			return (m_vbl ? 0x80 : 0x00) | m_transchar;

		case 0x1a:  // read TEXT
			return (m_video->get_graphics() ? 0x00 : 0x80) | m_transchar;

		case 0x1b:  // read MIXED
			return (m_video->get_mix() ? 0x80 : 0x00) | m_transchar;

		case 0x1c:  // read PAGE2
			return (m_video->get_page2() ? 0x80 : 0x00) | m_transchar;

		case 0x1d:  // read HIRES
			return (m_video->get_hires() ? 0x80 : 0x00) | m_transchar;

		case 0x1e:  // read ALTCHARSET
			return (m_video->get_altcharset() ? 0x80 : 0x00) | m_transchar;

		case 0x1f:  // read 80COL
			return (m_video->get_80col() ? 0x80 : 0x00) | m_transchar;

		case 0x40:  // read XYMask (IIc only)
			return m_xy ? 0x80 : 0x00;

		case 0x41:  // read VBL mask (IIc only)
			return m_vblmask ? 0x80 : 0x00;

		case 0x42:  // read X0Edge (IIc only)
			return m_x0edge ? 0x80 : 0x00;

		case 0x43:  // read Y0Edge (IIc only)
			return m_y0edge ? 0x80 : 0x00;

		case 0x60: // 40/80 column switch (IIc and Franklin ACE 500 only)
			return ((m_sysconfig.read_safe(0) & 0x40) ? 0x80 : 0) | uFloatingBus7;

		case 0x61:  // button 0 or Open Apple or mouse button 1
		case 0x69:
			return ((m_gameio->sw0_r() || (m_kbspecial->read() & 0x10)) ? 0x80 : 0) | uFloatingBus7;

		case 0x62:  // button 1 or Solid Apple
		case 0x6a:
			return ((m_gameio->sw1_r() || (m_kbspecial->read() & 0x20)) ? 0x80 : 0) | uFloatingBus7;

		case 0x63:  // mouse button 2 (no other function on IIc)
		case 0x6b:
			return (m_mouseb->read() ? 0 : 0x80) | uFloatingBus7;

		case 0x64:  // joy 1 X axis
		case 0x6c:
			return ((machine().time().as_double() < m_joystick_x1_time) ? 0x80 : 0) | uFloatingBus7;

		case 0x65:  // joy 1 Y axis
		case 0x6d:
			return ((machine().time().as_double() < m_joystick_y1_time) ? 0x80 : 0) | uFloatingBus7;

		case 0x66: // mouse X1 (IIc only)
		case 0x6e:
			return (m_x1 ? 0x80 : 0) | uFloatingBus7;

		case 0x67: // mouse Y1 (IIc only)
		case 0x6f:
			return (m_y1 ? 0x80 : 0) | uFloatingBus7;

		case 0x78: case 0x7a: case 0x7c: case 0x7e:  // read IOUDIS
			m_vbl = false;
			lower_irq(IRQ_VBL);
			return (m_ioudis ? 0x00 : 0x80) | uFloatingBus7;

		case 0x79: case 0x7b: case 0x7d: case 0x7f:  // read DHIRES
			return (m_video->get_dhires() ? 0x00 : 0x80) | uFloatingBus7;

		default:
			do_io(offset, true);
			if ((m_accel_unlocked) && (offset == 0x5c))
			{
				return m_accel_slotspk;
			}
			break;
	}

	return read_floatingbus();
}

void apple2e_state::c000_iic_w(offs_t offset, u8 data)
{
	if(machine().side_effects_disabled()) return;

	if ((offset & 0xf0) == 0x10) // clear keyboard latch, $C010 is really 10-1F
	{
		m_strobe = 0;
		return;
	}

	switch (offset)
	{
		case 0x00:  // 80STOREOFF
			m_video->a80store_w(false);
			auxbank_update();
			break;

		case 0x01:  // 80STOREON
			m_video->a80store_w(true);
			auxbank_update();
			break;

		case 0x02:  // RAMRDOFF
			m_ramrd = false;
			auxbank_update();
			break;

		case 0x03:  // RAMRDON
			m_ramrd = true;
			auxbank_update();
			break;

		case 0x04:  // RAMWRTOFF
			m_ramwrt = false;
			auxbank_update();
			break;

		case 0x05:  // RAMWRTON
			m_ramwrt = true;
			auxbank_update();
			break;

		case 0x06:  // INTCXROMOFF
			m_intcxrom = false;
			update_slotrom_banks();
			break;

		case 0x07:  // INTCXROMON
			m_intcxrom = true;
			update_slotrom_banks();
			break;

		case 0x08:  // ALTZPOFF
			m_altzp = false;
			auxbank_update();
			break;

		case 0x09:  // ALTZPON
			m_altzp = true;
			auxbank_update();
			break;

		case 0x0a:  // SETINTC3ROM
			m_slotc3rom = false;
			update_slotrom_banks();
			break;

		case 0x0b:  // SETSLOTC3ROM
			m_slotc3rom = true;
			update_slotrom_banks();
			break;

		case 0x0c:  // 80COLOFF
			m_video->a80col_w(false);
			break;

		case 0x0d:  // 80COLON
			m_video->a80col_w(true);
			break;

		case 0x0e:  // ALTCHARSETOFF
			m_video->altcharset_w(false);
			break;

		case 0x0f:  // ALTCHARSETON
			m_video->altcharset_w(true);
			break;

		case 0x5a:  // IIC+ accelerator unlock
			if (m_isiicplus)
			{
				if (data == 0x5a)
				{
					m_accel_stage++;
					if (m_accel_stage == 4)
					{
						m_accel_unlocked = true;
					}
				}
				else if (data == 0xa5)
				{
					// lock
					m_accel_unlocked = false;
					m_accel_stage = 0;
				}
				else if (m_accel_unlocked)
				{
					m_accel_fast = false;
					accel_normal_speed();
				}
				else
				{
					do_io(offset, true);
				}
			}
			break;

		case 0x5b:  // Set fast speed on any write
			if (m_accel_unlocked)
			{
				m_accel_fast = true;
				accel_full_speed();
			}
			do_io(offset, true);
			break;

		case 0x5c:
			if (m_accel_unlocked)
			{
				m_accel_slotspk = data;
			}
			do_io(offset, true);
			break;

		case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
				if (m_auxslotdevice)
				{
					m_auxslotdevice->write_c07x(offset & 0xf, data);

					// card may have banked auxram; get a new bank pointer
					m_aux_bank_ptr = m_auxslotdevice->get_auxbank_ptr();
				}
				break;

		case 0x78:  // IIc mirror of SETIOUDIS used by the mouse firmware
		case 0x7a:
		case 0x7c:
		case 0x7e:  // SETIOUDIS
			m_ioudis = true;
			m_vbl = false;
			lower_irq(IRQ_VBL);
			break;

		case 0x79:  // IIc mirror of CLRIOUDIS used by the mouse firmware
		case 0x7b:
		case 0x7d:
		case 0x7f:  // CLRIOUDIS
			m_ioudis = false;
			m_vbl = false;
			lower_irq(IRQ_VBL);
			break;

		default:
			do_io(offset, true);
			break;
	}
}

void apple2e_state::update_iic_mouse()
{
	int new_mx, new_my;
	bool raise_mousexy_irq = false;

	// read the axes and check for changes
	new_mx = m_mousex->read();
	new_my = m_mousey->read();

	// did X change?
	if (new_mx != last_mx)
	{
		int diff = new_mx - last_mx;

		/* check for wrap */
		if (diff > 0x80)
		{
			diff -= 0x100;
		}
		else
		{
			if (diff < -0x80)
			{
				diff += 0x100;
			}
		}

		count_x += diff;
		last_mx = new_mx;
	}

	// did Y change?
	if (new_my != last_my)
	{
		int diff = new_my - last_my;

		/* check for wrap */
		if (diff > 0x80)
		{
			diff -= 0x100;
		}
		else
		{
			if (diff < -0x80)
			{
				diff += 0x100;
			}
		}

		count_y += diff;
		last_my = new_my;
	}

	if (count_x)
	{
		if (count_x < 0)
		{
			count_x++;
			m_x1 = false;
		}
		else
		{
			count_x--;
			m_x1 = true;
		}

		// x0 is going to state-flip, generate an IRQ
		if (((m_x0) && (m_x0edge)) || // falling?
			(!(m_x0) && !(m_x0edge))) // rising?
		{
			if (m_xy)
			{
				m_xirq = true;
				raise_mousexy_irq = true;
			}
		}

		m_x0 = !m_x0;
	}

	if (count_y)
	{
		if (count_y < 0)
		{
			count_y++;
			m_y1 = true;
		}
		else
		{
			count_y--;
			m_y1 = false;
		}

		// y0 is going to state-flip, generate an IRQ
		if (((m_y0) && (m_y0edge)) || // falling?
			(!(m_y0) && !(m_y0edge))) // rising?
		{
			if (m_xy)
			{
				m_yirq = true;
				raise_mousexy_irq = true;
			}
		}

		m_y0 = !m_y0;
	}

	if (raise_mousexy_irq)
	{
		raise_irq(IRQ_MOUSEXY);
	}
}

u8 apple2e_state::laser_mouse_r(offs_t offset)
{
	u8 uFloatingBus7 = read_floatingbus() & 0x7f;

	switch (offset)
	{
		case 0x8:  // read X0Edge
			return (m_x0edge ? 0x80 : 0x00) | uFloatingBus7;

		case 0x9:  // read Y0Edge
			return (m_y0edge ? 0x80 : 0x00) | uFloatingBus7;

		case 0xa:  // read XYMask
			return (m_xy ? 0x80 : 0x00) | uFloatingBus7;

		case 0xb:  // read VBL mask
			return (m_vblmask ? 0x80 : 0x00) | uFloatingBus7;

		case 0xc: // mouse X1 IRQ status
			return (m_xirq ? 0x80 : 0) | uFloatingBus7;

		case 0xd: // mouse Y1 IRQ status
			return (m_yirq ? 0x80 : 0) | uFloatingBus7;

		case 0xe: // VBL interrupt status
			return ((m_irqmask & IRQ_VBL) ? 0x80 : 0) | uFloatingBus7;
	}

	return 0xff;
}

void apple2e_state::laser_mouse_w(offs_t offset, u8 data)
{
	// these are the same as the //c mouse registers, but with bit 4 of the
	// address inverted, pretty much.
	switch (offset)
	{
		case 0x0:  // RisX0Edge
			m_x0edge = false; break;

		case 0x1:  // FalX0Edge
			m_x0edge = true; break;

		case 0x2:  // RisY0Edge
			m_y0edge = false; break;

		case 0x3:  // FalY0Edge
			m_y0edge = true; break;

		case 0x4:  // DisXY
			m_xy = false; break;

		case 0x5:  // EnbXY
			m_xy = true; break;

		case 0x6:  // DisVBL
			lower_irq(IRQ_VBL);
			m_vblmask = false; break;

		case 0x7:  // EnVBL
			m_vblmask = true; break;

		case 0xf: // clear XY interrupt
			lower_irq(IRQ_MOUSEXY);
			m_xirq = m_yirq = false;
			break;
	}
}

u8 apple2e_state::c080_r(offs_t offset)
{
	if(!machine().side_effects_disabled())
	{
		int slot;

		offset &= 0x7F;
		slot = offset / 0x10;

		if (slot == 0)
		{
			lc_update(offset & 0xf, false);
		}
		else
		{
			if (m_accel_present)
			{
				accel_slot(slot);
			}

			if ((m_isiicplus) && (slot == 6))
			{
				return m_iwm->read(offset % 0x10);
			}

			if (m_slotdevice[slot] != nullptr)
			{
				return m_slotdevice[slot]->read_c0nx(offset % 0x10);
			}
			else
			{
				if ((m_iscec) && (slot == 3))
				{
					return m_cec_bank;
				}
			}
		}
	}

	return read_floatingbus();
}

void apple2e_state::c080_w(offs_t offset, u8 data)
{
	int slot;

	offset &= 0x7F;
	slot = offset / 0x10;

	if (slot == 0)
	{
		lc_update(offset & 0xf, true);
	}
	else
	{
		if ((m_isiicplus) && (slot == 6))
		{
			m_iwm->write(offset % 0x10, data);
			return;
		}

		if (m_slotdevice[slot] != nullptr)
		{
			m_slotdevice[slot]->write_c0nx(offset % 0x10, data);
		}
		else
		{
			//if ((m_iscec) && (slot == 3))
			if ((m_iscec) && (!m_iscecm) && (slot == 3))
			{
				if (data != m_cec_bank)
				{
					m_cec_bank = data;
					if (data & 0x10)
					{
						m_video->monohgr_w(false);
					}
					else
					{
						m_video->monohgr_w(true);
					}

					auxbank_update();
					update_slotrom_banks();
				}
			}
		}
	}
}

u8 apple2e_state::read_slot_rom(int slotbias, int offset)
{
	int slotnum = ((offset>>8) & 0xf) + slotbias;

	if (m_slotdevice[slotnum] != nullptr)
	{
		if ((m_cnxx_slot == CNXX_UNCLAIMED) && (m_slotdevice[slotnum]->take_c800()) && (!machine().side_effects_disabled()))
		{
			m_cnxx_slot = slotnum;
			update_slotrom_banks();
		}

		return m_slotdevice[slotnum]->read_cnxx(offset&0xff);
	}

	return read_floatingbus();
}

void apple2e_state::write_slot_rom(int slotbias, int offset, u8 data)
{
	int slotnum = ((offset>>8) & 0xf) + slotbias;

	if ((m_iscec) && (m_iscecm) && ( slotnum == 6 ) && (!m_intcxrom) )
	{
		if (data != m_cec_bank)
		{
			m_cec_bank = data;
/*
            if (data & 0x10)
            {
                m_video->m_monohgr = false;
            }
            else
            {
                m_video->m_monohgr = true;
            }
*/
			auxbank_update();
			update_slotrom_banks();
		}
	}

	if (m_slotdevice[slotnum] != nullptr)
	{
		if ((m_cnxx_slot == CNXX_UNCLAIMED) && (m_slotdevice[slotnum]->take_c800()) && (!machine().side_effects_disabled()))
		{
			m_cnxx_slot = slotnum;
			update_slotrom_banks();
		}

		m_slotdevice[slotnum]->write_cnxx(offset&0xff, data);
	}
}

u8 apple2e_state::read_int_rom(int slotbias, int offset)
{
	int slot = ((slotbias + offset) >> 8) & 0xf;

	// slot 4 can't remap because the IRQ handler is there
	if ((m_isace500) && (m_ace_cnxx_bank) && (slot != 4))
	{
		slotbias += 0x4000;
		// even numbered slots come from $6x00 in this mode?
		if (!(slot & 1))
		{
			slotbias += 0x2000;
		}
		return m_rom_ptr[slotbias + offset];
	}

	// return data from SmartWatch if /CEO is negated
	if (m_ds1315->ceo_r())
		return m_ds1315->read(slotbias + offset);
	else
		m_ds1315->read(slotbias + offset);

	return m_rom_ptr[slotbias + offset];
}

u8 apple2e_state::c100_r(offs_t offset)  { accel_slot(1 + ((offset >> 8) & 0x7)); return read_slot_rom(1, offset); }
u8 apple2e_state::c100_int_r(offs_t offset)  { accel_slot(1 + ((offset >> 8) & 0x7)); return read_int_rom(0x100, offset); }
u8 apple2e_state::c100_int_bank_r(offs_t offset)  { accel_slot(1 + ((offset >> 8) & 0x7)); return read_int_rom(0x4100, offset); }
u8 apple2e_state::c100_cec_r(offs_t offset)  { return m_rom_ptr[0xc100 + offset]; }
u8 apple2e_state::c100_cec_bank_r(offs_t offset)  { return m_rom_ptr[0x4100 + offset]; }
void apple2e_state::c100_w(offs_t offset, u8 data) { accel_slot(1); write_slot_rom(1, offset, data); }
u8 apple2e_state::c300_r(offs_t offset)  { accel_slot(3 + ((offset >> 8) & 0x7)); return read_slot_rom(3, offset); }

u8 apple2e_state::c300_int_r(offs_t offset)
{
	accel_slot(3 + ((offset >> 8) & 0x7));
	if ((!m_slotc3rom) && !machine().side_effects_disabled())
	{
		m_intc8rom = true;
		update_slotrom_banks();
	}
	return read_int_rom(0x300, offset);
}

u8 apple2e_state::c300_int_bank_r(offs_t offset)
{
	accel_slot(3 + ((offset >> 8) & 0x7));
	if ((!m_slotc3rom) && !machine().side_effects_disabled())
	{
		m_intc8rom = true;
		update_slotrom_banks();
	}
	return read_int_rom(0x4300, offset);
}

void apple2e_state::c300_w(offs_t offset, u8 data)
{
	accel_slot(3 + ((offset >> 8) & 0x7));
	if ((!m_slotc3rom) && !machine().side_effects_disabled())
	{
		m_intc8rom = true;
		update_slotrom_banks();
	}

	write_slot_rom(3, offset, data);
}

u8 apple2e_state::c300_cec_r(offs_t offset)  { return m_rom_ptr[0xc300 + offset]; }
u8 apple2e_state::c300_cec_bank_r(offs_t offset)  { return m_rom_ptr[0x4300 + offset]; }

u8 apple2e_state::c400_r(offs_t offset)  { accel_slot(4 + ((offset >> 8) & 0x7)); return read_slot_rom(4, offset); }
u8 apple2e_state::c400_int_r(offs_t offset)
{
	accel_slot(4 + ((offset >> 8) & 0x7));
	if ((offset < 0x100) && (m_mockingboard4c))
	{
		return read_slot_rom(4, offset);
	}

	return read_int_rom(0x400, offset);
}

u8 apple2e_state::c400_int_bank_r(offs_t offset)
{
	accel_slot(4 + ((offset >> 8) & 0x7));
	if ((offset < 0x100) && (m_mockingboard4c))
	{
		return read_slot_rom(4, offset);
	}

	return read_int_rom(0x4400, offset);
}

void apple2e_state::c400_w(offs_t offset, u8 data)
{
	accel_slot(4 + ((offset >> 8) & 0x7));
	if ((m_isiic) && (offset < 0x100))
	{
		m_mockingboard4c = true;
	}

	write_slot_rom(4, offset, data);
}

u8 apple2e_state::c400_cec_r(offs_t offset)  { return m_rom_ptr[0xc400 + offset]; }
u8 apple2e_state::c400_cec_bank_r(offs_t offset)  { return m_rom_ptr[0x4400 + offset]; }

void apple2e_state::c400_cec_w(offs_t offset, u8 data)
{
	if ((m_iscecm))
	{
		write_slot_rom(4, offset, data);
	}
}

u8 apple2e_state::c800_r(offs_t offset)
{
	if ((offset == 0x7ff) && !machine().side_effects_disabled())
	{
		u8 rv = 0xff;

		if ((m_cnxx_slot > 0) && (m_slotdevice[m_cnxx_slot] != nullptr))
		{
			rv = m_slotdevice[m_cnxx_slot]->read_c800(offset&0xfff);
		}
		m_cnxx_slot = CNXX_UNCLAIMED;
		m_intc8rom = false;
		update_slotrom_banks();
		return rv;
	}

	if ((m_cnxx_slot > 0) && (m_slotdevice[m_cnxx_slot] != nullptr))
	{
		return m_slotdevice[m_cnxx_slot]->read_c800(offset&0xfff);
	}

	return read_floatingbus();
}

u8 apple2e_state::ace500_c0bx_r(offs_t offset)
{
	switch (offset)
	{
		// Printer "auto-LF" DIP switch in bit 7
		case 0x0:
			return (m_sysconfig->read() & 0x80);

		// Alt key status (0=pressed).  Reads as pressed for function keys.
		case 0x4:
			{
				m_strobe = 0;
				const u8 rv = m_franklin_strobe;
				m_franklin_strobe = 0x80;
				return rv;
			}
			break;

		// Used by the IRQ handler.  Appears to return altzp status in bit 7, same as $C016.
		case 0xc:
			return (m_altzp ? 0x80 : 0x00);

		default:
			logerror("c0bx_r @ %x (PC=%x)\n", offset, m_maincpu->pc());
			break;
	}

	return 0xff;
}


// $C0B8 - $C100-$C7FF alt bank
// $C0B9 - $C100-$C7FF main bank
// $C0BA - release $C800 force, use $CFFF mechanism
// $C0BB - force main rom bank at $C800
void apple2e_state::ace500_c0bx_w(offs_t offset, u8 data)
{
	switch (offset)
	{
		case 0x8:
			m_ace_cnxx_bank = true;
			break;

		case 0x9:
			m_ace_cnxx_bank = false;
			break;

		case 0xa:
			m_ace500rombank = 0;
			break;

		case 0xb:
			m_ace500rombank = 0x7000;
			break;

		default:
			logerror("unknown c0bx_w %x (PC=%x)\n", offset, m_maincpu->pc());
			break;
	}
}

u8 apple2e_state::c800_int_r(offs_t offset)
{
	if ((offset == 0x7ff) && !machine().side_effects_disabled())
	{
		m_cnxx_slot = CNXX_UNCLAIMED;
		m_intc8rom = false;
		update_slotrom_banks();
	}

	if (m_iscec)
	{
		return m_rom_ptr[0x4800 + offset];
	}

	if (m_isace500)
	{
		return m_rom_ptr[m_ace500rombank + offset];
	}

	return m_rom_ptr[0x800 + offset];
}

u8 apple2e_state::c800_b2_int_r(offs_t offset)
{
	if ((m_isiicplus) && (m_romswitch) && (((offset >= 0x400) && (offset < 0x500)) || ((offset >= 0x600) && (offset < 0x700))))
	{
		return mig_r(offset-0x400);
	}

	if ((offset == 0x7ff) && !machine().side_effects_disabled())
	{
		m_cnxx_slot = CNXX_UNCLAIMED;
		m_intc8rom = false;
		update_slotrom_banks();
	}

	return m_rom_ptr[0x4800 + offset];
}

void apple2e_state::c800_w(offs_t offset, u8 data)
{
	if ((m_isace500) && (offset == 0x7ff))
	{
		// TODO: use a version of our conventional CnXX handling for this
		u8 page = (m_maincpu->pc() >> 8) & 0xf;
		switch (page)
		{
			case 1:
				m_ace500rombank = 0x4800;
				break;

			case 3:
				m_ace500rombank = 0x800;
				break;

			case 5:
				m_ace500rombank = 0x5800;
				break;
		}
		return;
	}

	if ((m_isiicplus) && (m_romswitch) && (((offset >= 0x400) && (offset < 0x500)) || ((offset >= 0x600) && (offset < 0x700))))
	{
		mig_w(offset-0x400, data);
		return;
	}

	if ((m_cnxx_slot > 0) && (m_slotdevice[m_cnxx_slot] != nullptr))
	{
		m_slotdevice[m_cnxx_slot]->write_c800(offset&0xfff, data);
	}

	if (offset == 0x7ff)
	{
		if (!machine().side_effects_disabled())
		{
			m_cnxx_slot = CNXX_UNCLAIMED;
			m_intc8rom = false;
			update_slotrom_banks();
		}
	}
}

u8 apple2e_state::c800_cec_r(offs_t offset)  { return m_rom_ptr[0xc800 + offset]; }
u8 apple2e_state::c800_cec_bank_r(offs_t offset)  { return m_rom_ptr[0x4800 + offset]; }

u8 apple2e_state::inh_r(offs_t offset)
{
	if (m_inh_slot != -1)
	{
		return m_slotdevice[m_inh_slot]->read_inh_rom(offset + 0xd000);
	}

	assert(0);  // hitting inh_r with invalid m_inh_slot should not be possible
	return read_floatingbus();
}

void apple2e_state::inh_w(offs_t offset, u8 data)
{
	if (m_inh_slot != -1)
	{
		m_slotdevice[m_inh_slot]->write_inh_rom(offset + 0xd000, data);
	}
}

u8 apple2e_state::lc_romswitch_r(offs_t offset)
{
	return m_rom_ptr[0x5000 + offset];
}

void apple2e_state::lc_romswitch_w(offs_t offset, u8 data)
{
	lc_w(offset, data);
}

u8 apple2e_state::lc_r(offs_t offset)
{
	if ((m_altzp) && (!m_iscec))
	{
		if (m_aux_bank_ptr)
		{
			if (offset < 0x1000)
			{
				if (m_lcram2)
				{
					return m_aux_bank_ptr[((offset & 0xfff) + 0xd000) & m_aux_mask];
				}
				else
				{
					return m_aux_bank_ptr[((offset & 0xfff) + 0xc000) & m_aux_mask];
				}
			}

			return m_aux_bank_ptr[((offset & 0x1fff) + 0xe000) & m_aux_mask];
		}
		else
		{
			return read_floatingbus();
		}
	}
	else
	{
		if (offset < 0x1000)
		{
			if (m_lcram2)
			{
				return m_ram_ptr[(offset & 0xfff) + 0xd000];
			}
			else
			{
				return m_ram_ptr[(offset & 0xfff) + 0xc000];
			}
		}

		return m_ram_ptr[(offset & 0x1fff) + 0xe000];
	}
}

void apple2e_state::lc_w(offs_t offset, u8 data)
{
	if (!m_lcwriteenable)
	{
		return;
	}

	if ((m_altzp) && (!m_iscec))
	{
		if (m_aux_bank_ptr)
		{
			if (offset < 0x1000)
			{
				if (m_lcram2)
				{
					m_aux_bank_ptr[((offset & 0xfff) + 0xd000) & m_aux_mask] = data;
				}
				else
				{
					m_aux_bank_ptr[((offset & 0xfff) + 0xc000) & m_aux_mask] = data;
				}
				return;
			}

			m_aux_bank_ptr[((offset & 0x1fff) + 0xe000) & m_aux_mask] = data;
		}
	}
	else
	{
		if (offset < 0x1000)
		{
			if (m_lcram2)
			{
				m_ram_ptr[(offset & 0xfff) + 0xd000] = data;
			}
			else
			{
				m_ram_ptr[(offset & 0xfff) + 0xc000] = data;
			}
			return;
		}

		m_ram_ptr[(offset & 0x1fff) + 0xe000] = data;
	}
}

// floating bus code from old machine/apple2: now works reasonably well with French Touch and Deater "vapor lock" stuff
u8 apple2e_state::read_floatingbus()
{
	enum
	{
		// scanner constants
		kHBurstClock      =    53, // clock when Color Burst starts
		kHBurstClocks     =     4, // clocks per Color Burst duration
		kHClock0State     =  0x18, // H[543210] = 011000
		kHClocks          =    65, // clocks per horizontal scan (including HBL)
		kHPEClock         =    40, // clock when HPE (horizontal preset enable) goes low
		kHPresetClock     =    41, // clock when H state presets
		kHSyncClock       =    49, // clock when HSync starts
		kHSyncClocks      =     4, // clocks per HSync duration
		kNTSCScanLines    =   262, // total scan lines including VBL (NTSC)
		kNTSCVSyncLine    =   224, // line when VSync starts (NTSC)
		kPALScanLines     =   312, // total scan lines including VBL (PAL)
		kPALVSyncLine     =   264, // line when VSync starts (PAL)
		kVLine0State      = 0x100, // V[543210CBA] = 100000000
		kVPresetLine      =   256, // line when V state presets
		kVSyncLines       =     4, // lines per VSync duration
	};

	const int kClocksPerVSync = kHClocks * (m_pal ? kPALScanLines : kNTSCScanLines);

	// vars
	//
	int i, Hires, Mixed, Page2, _80Store, ScanLines, /* VSyncLine, ScanCycles,*/
		h_clock, h_state, h_0, h_1, h_2, h_3, h_4, h_5,
		v_line, v_state, v_A, v_B, v_C, v_0, v_1, v_2, v_3, v_4, /* v_5, */
		_hires, addend0, addend1, addend2, sum, address;

	// video scanner data
	//
	i = m_maincpu->total_cycles() % kClocksPerVSync; // cycles into this VSync

	// machine state switches
	//
	Hires = (m_video->get_hires() && m_video->get_graphics()) ? 1 : 0;
	Mixed = m_video->get_mix() ? 1 : 0;
	Page2 = m_video->get_page2() ? 1 : 0;
	_80Store = m_video->get_80store() ? 1 : 0;

	// calculate video parameters according to display standard
	// we call this "PAL", but it's also for SECAM
	ScanLines  = m_pal ? kPALScanLines : kNTSCScanLines;

	// calculate horizontal scanning state
	h_clock = (i + 63) % kHClocks; // which horizontal scanning clock
	h_state = kHClock0State + h_clock; // H state bits
	if (h_clock >= kHPresetClock) // check for horizontal preset
	{
		h_state -= 1; // correct for state preset (two 0 states)
	}
	h_0 = (h_state >> 0) & 1; // get horizontal state bits
	h_1 = (h_state >> 1) & 1;
	h_2 = (h_state >> 2) & 1;
	h_3 = (h_state >> 3) & 1;
	h_4 = (h_state >> 4) & 1;
	h_5 = (h_state >> 5) & 1;

	// calculate vertical scanning state
	//
	v_line  = (i / kHClocks) + 192; // which vertical scanning line (MAME starts at blank, not the beginning of the scanning area)
	v_state = kVLine0State + v_line; // V state bits
	if ((v_line >= kVPresetLine)) // check for previous vertical state preset
	{
		v_state -= ScanLines; // compensate for preset
	}
	v_A = (v_state >> 0) & 1; // get vertical state bits
	v_B = (v_state >> 1) & 1;
	v_C = (v_state >> 2) & 1;
	v_0 = (v_state >> 3) & 1;
	v_1 = (v_state >> 4) & 1;
	v_2 = (v_state >> 5) & 1;
	v_3 = (v_state >> 6) & 1;
	v_4 = (v_state >> 7) & 1;
	//v_5 = (v_state >> 8) & 1;

	// calculate scanning memory address
	//
	_hires = Hires;
	if (Hires && Mixed && (v_4 & v_2))
	{
		_hires = 0; // (address is in text memory)
	}

	addend0 = 0x68; // 1            1            0            1
	addend1 =              (h_5 << 5) | (h_4 << 4) | (h_3 << 3);
	addend2 = (v_4 << 6) | (v_3 << 5) | (v_4 << 4) | (v_3 << 3);
	sum     = (addend0 + addend1 + addend2) & (0x0F << 3);

	address = 0;
	address |= h_0 << 0; // a0
	address |= h_1 << 1; // a1
	address |= h_2 << 2; // a2
	address |= sum;      // a3 - aa6
	address |= v_0 << 7; // a7
	address |= v_1 << 8; // a8
	address |= v_2 << 9; // a9
	address |= ((_hires) ? v_A : (1 ^ (Page2 & (1 ^ _80Store)))) << 10; // a10
	address |= ((_hires) ? v_B : (Page2 & (1 ^ _80Store))) << 11; // a11
	if (_hires) // hires?
	{
		// Y: insert hires only address bits
		//
		address |= v_C << 12; // a12
		address |= (1 ^ (Page2 & (1 ^ _80Store))) << 13; // a13
		address |= (Page2 & (1 ^ _80Store)) << 14; // a14
	}

	return m_ram_ptr[address % m_ram_size];
}

/***************************************************************************
    ADDRESS MAP
***************************************************************************/

u8   apple2e_state::ram0000_r(offs_t offset)          { return m_ram_ptr[offset]; }
void apple2e_state::ram0000_w(offs_t offset, u8 data) { m_ram_ptr[offset] = data; }
u8   apple2e_state::ram0200_r(offs_t offset)          { return m_ram_ptr[offset+0x200]; }
void apple2e_state::ram0200_w(offs_t offset, u8 data) { m_ram_ptr[offset+0x200] = data; }
u8   apple2e_state::ram0400_r(offs_t offset)          { return m_ram_ptr[offset+0x400]; }
void apple2e_state::ram0400_w(offs_t offset, u8 data) { m_ram_ptr[offset+0x400] = data; }
u8   apple2e_state::ram0800_r(offs_t offset)          { return m_ram_ptr[offset+0x800]; }
void apple2e_state::ram0800_w(offs_t offset, u8 data) { m_ram_ptr[offset+0x800] = data; }
u8   apple2e_state::ram2000_r(offs_t offset)          { return m_ram_ptr[offset+0x2000]; }
void apple2e_state::ram2000_w(offs_t offset, u8 data) { m_ram_ptr[offset+0x2000] = data; }
u8   apple2e_state::ram4000_r(offs_t offset)          { return m_ram_ptr[offset+0x4000]; }
void apple2e_state::ram4000_w(offs_t offset, u8 data) { m_ram_ptr[offset+0x4000] = data; }
void apple2e_state::ram8000_w(offs_t offset, u8 data) { m_ram_ptr[offset+0x8000] = data; }

u8 apple2e_state::ram4000_ace2200_r(offs_t offset)
{
	if ((offset >= 0x6000) && (m_ace2200_axxx_bank))
	{
		return m_rom_ptr[0x4000 + (offset - 0x6000)];
	}
	return m_ram_ptr[offset+0x4000];
}

u8 apple2e_state::cec4000_r(offs_t offset)
{
	//printf("cec4000_r: ofs %x\n", offset);
	return m_cec_remap[((m_cec_bank & 0xf) << 14) + offset];
}

u8 apple2e_state::cec8000_r(offs_t offset)
{
	//printf("cec8000_r: ofs %x\n", offset);
	if (m_cec_bank & 0x20)
	{
		return m_rom_ptr[offset];
	}
	else
	{
		return m_rom_ptr[offset + 0x8000];
	}
}

u8   apple2e_state::auxram0000_r(offs_t offset)          { if (m_aux_bank_ptr) { return m_aux_bank_ptr[offset & m_aux_mask]; } else { return read_floatingbus(); } }
void apple2e_state::auxram0000_w(offs_t offset, u8 data) { if (m_aux_bank_ptr) { m_aux_bank_ptr[offset & m_aux_mask] = data; } }
u8   apple2e_state::auxram0200_r(offs_t offset)          { if (m_aux_bank_ptr) { return m_aux_bank_ptr[(offset+0x200) & m_aux_mask]; } else { return read_floatingbus(); } }
void apple2e_state::auxram0200_w(offs_t offset, u8 data) { if (m_aux_bank_ptr) { m_aux_bank_ptr[(offset+0x200) & m_aux_mask] = data; } }
u8   apple2e_state::auxram0400_r(offs_t offset)          { if (m_aux_bank_ptr) { return m_aux_bank_ptr[(offset+0x400) & m_aux_mask]; } else { return read_floatingbus(); } }
void apple2e_state::auxram0400_w(offs_t offset, u8 data) { if (m_aux_bank_ptr) { m_aux_bank_ptr[(offset+0x400) & m_aux_mask] = data; } }
u8   apple2e_state::auxram0800_r(offs_t offset)          { if (m_aux_bank_ptr) { return m_aux_bank_ptr[(offset+0x800) & m_aux_mask]; } else { return read_floatingbus(); } }
void apple2e_state::auxram0800_w(offs_t offset, u8 data) { if (m_aux_bank_ptr) { m_aux_bank_ptr[(offset+0x800) & m_aux_mask] = data; } }
u8   apple2e_state::auxram2000_r(offs_t offset)          { if (m_aux_bank_ptr) { return m_aux_bank_ptr[(offset+0x2000) & m_aux_mask]; } else { return read_floatingbus(); } }
void apple2e_state::auxram2000_w(offs_t offset, u8 data) { if (m_aux_bank_ptr) { m_aux_bank_ptr[(offset+0x2000) & m_aux_mask] = data; } }
u8   apple2e_state::auxram4000_r(offs_t offset)          { if (m_aux_bank_ptr) { return m_aux_bank_ptr[(offset+0x4000) & m_aux_mask]; } else { return read_floatingbus(); } }
void apple2e_state::auxram4000_w(offs_t offset, u8 data) { if (m_aux_bank_ptr) { m_aux_bank_ptr[(offset + 0x4000) & m_aux_mask] = data; } }

void apple2e_state::base_map(address_map &map)
{
	map(0x0000, 0x01ff).view(m_0000bank);
	m_0000bank[0](0x0000, 0x01ff).rw(FUNC(apple2e_state::ram0000_r), FUNC(apple2e_state::ram0000_w));
	m_0000bank[1](0x0000, 0x01ff).rw(FUNC(apple2e_state::auxram0000_r), FUNC(apple2e_state::auxram0000_w));

	map(0x0200, 0x03ff).view(m_0200bank);
	m_0200bank[0](0x0200, 0x03ff).rw(FUNC(apple2e_state::ram0200_r), FUNC(apple2e_state::ram0200_w));         // wr 0 rd 0
	m_0200bank[1](0x0200, 0x03ff).rw(FUNC(apple2e_state::auxram0200_r), FUNC(apple2e_state::ram0200_w));      // wr 0 rd 1
	m_0200bank[2](0x0200, 0x03ff).rw(FUNC(apple2e_state::ram0200_r), FUNC(apple2e_state::auxram0200_w));      // wr 1 rd 0
	m_0200bank[3](0x0200, 0x03ff).rw(FUNC(apple2e_state::auxram0200_r), FUNC(apple2e_state::auxram0200_w)); // wr 1 rd 1

	map(0x0400, 0x07ff).view(m_0400bank);
	m_0400bank[0](0x0400, 0x07ff).rw(FUNC(apple2e_state::ram0400_r), FUNC(apple2e_state::ram0400_w));         // wr 0 rd 0
	m_0400bank[1](0x0400, 0x07ff).rw(FUNC(apple2e_state::auxram0400_r), FUNC(apple2e_state::ram0400_w));      // wr 0 rd 1
	m_0400bank[2](0x0400, 0x07ff).rw(FUNC(apple2e_state::ram0400_r), FUNC(apple2e_state::auxram0400_w));      // wr 1 rd 0
	m_0400bank[3](0x0400, 0x07ff).rw(FUNC(apple2e_state::auxram0400_r), FUNC(apple2e_state::auxram0400_w)); // wr 1 rd 1

	map(0x0800, 0x1fff).view(m_0800bank);
	m_0800bank[0](0x0800, 0x1fff).rw(FUNC(apple2e_state::ram0800_r), FUNC(apple2e_state::ram0800_w));
	m_0800bank[1](0x0800, 0x1fff).rw(FUNC(apple2e_state::auxram0800_r), FUNC(apple2e_state::ram0800_w));
	m_0800bank[2](0x0800, 0x1fff).rw(FUNC(apple2e_state::ram0800_r), FUNC(apple2e_state::auxram0800_w));
	m_0800bank[3](0x0800, 0x1fff).rw(FUNC(apple2e_state::auxram0800_r), FUNC(apple2e_state::auxram0800_w));

	map(0x2000, 0x3fff).view(m_2000bank);
	m_2000bank[0](0x2000, 0x3fff).rw(FUNC(apple2e_state::ram2000_r), FUNC(apple2e_state::ram2000_w));
	m_2000bank[1](0x2000, 0x3fff).rw(FUNC(apple2e_state::auxram2000_r), FUNC(apple2e_state::ram2000_w));
	m_2000bank[2](0x2000, 0x3fff).rw(FUNC(apple2e_state::ram2000_r), FUNC(apple2e_state::auxram2000_w));
	m_2000bank[3](0x2000, 0x3fff).rw(FUNC(apple2e_state::auxram2000_r), FUNC(apple2e_state::auxram2000_w));

	map(0x4000, 0xbfff).view(m_4000bank);
	m_4000bank[0](0x4000, 0xbfff).rw(FUNC(apple2e_state::ram4000_r), FUNC(apple2e_state::ram4000_w));
	m_4000bank[1](0x4000, 0xbfff).rw(FUNC(apple2e_state::auxram4000_r), FUNC(apple2e_state::ram4000_w));
	m_4000bank[2](0x4000, 0xbfff).rw(FUNC(apple2e_state::ram4000_r), FUNC(apple2e_state::auxram4000_w));
	m_4000bank[3](0x4000, 0xbfff).rw(FUNC(apple2e_state::auxram4000_r), FUNC(apple2e_state::auxram4000_w));
	m_4000bank[4](0x4000, 0x7fff).rw(FUNC(apple2e_state::cec4000_r), FUNC(apple2e_state::ram4000_w));
	m_4000bank[4](0x8000, 0xbfff).rw(FUNC(apple2e_state::cec8000_r), FUNC(apple2e_state::ram8000_w));

	map(0xc000, 0xc07f).rw(FUNC(apple2e_state::c000_r), FUNC(apple2e_state::c000_w));
	map(0xc080, 0xc0ff).rw(FUNC(apple2e_state::c080_r), FUNC(apple2e_state::c080_w));

	map(0xc100, 0xc2ff).view(m_c100bank);
	m_c100bank[0](0xc100, 0xc2ff).rw(FUNC(apple2e_state::c100_r), FUNC(apple2e_state::c100_w));
	m_c100bank[1](0xc100, 0xc2ff).rw(FUNC(apple2e_state::c100_int_r), FUNC(apple2e_state::c100_w));
	m_c100bank[2](0xc100, 0xc2ff).rw(FUNC(apple2e_state::c100_int_bank_r), FUNC(apple2e_state::c100_w));
	m_c100bank[3](0xc100, 0xc2ff).r(FUNC(apple2e_state::c100_cec_r)).nopw();
	m_c100bank[4](0xc100, 0xc2ff).r(FUNC(apple2e_state::c100_cec_bank_r)).nopw();

	map(0xc300, 0xc3ff).view(m_c300bank);
	m_c300bank[0](0xc300, 0xc3ff).rw(FUNC(apple2e_state::c300_r), FUNC(apple2e_state::c300_w));
	m_c300bank[1](0xc300, 0xc3ff).rw(FUNC(apple2e_state::c300_int_r), FUNC(apple2e_state::c300_w));
	m_c300bank[2](0xc300, 0xc3ff).rw(FUNC(apple2e_state::c300_int_bank_r), FUNC(apple2e_state::c300_w));
	m_c300bank[3](0xc300, 0xc3ff).r(FUNC(apple2e_state::c300_cec_r)).nopw();
	m_c300bank[4](0xc300, 0xc3ff).r(FUNC(apple2e_state::c300_cec_bank_r)).nopw();

	map(0xc400, 0xc7ff).view(m_c400bank);
	m_c400bank[0](0xc400, 0xc7ff).rw(FUNC(apple2e_state::c400_r), FUNC(apple2e_state::c400_w));
	m_c400bank[1](0xc400, 0xc7ff).rw(FUNC(apple2e_state::c400_int_r), FUNC(apple2e_state::c400_w));
	m_c400bank[2](0xc400, 0xc7ff).rw(FUNC(apple2e_state::c400_int_bank_r), FUNC(apple2e_state::c400_w));
	m_c400bank[3](0xc400, 0xc7ff).rw(FUNC(apple2e_state::c400_cec_r), FUNC(apple2e_state::c400_cec_w));
	m_c400bank[4](0xc400, 0xc7ff).r(FUNC(apple2e_state::c400_cec_bank_r)).nopw();

	map(0xc800, 0xcfff).view(m_c800bank);
	m_c800bank[0](0xc800, 0xcfff).rw(FUNC(apple2e_state::c800_r), FUNC(apple2e_state::c800_w));
	m_c800bank[1](0xc800, 0xcfff).rw(FUNC(apple2e_state::c800_int_r), FUNC(apple2e_state::c800_w));
	m_c800bank[2](0xc800, 0xcfff).rw(FUNC(apple2e_state::c800_b2_int_r), FUNC(apple2e_state::c800_w));
	m_c800bank[3](0xc800, 0xcfff).r(FUNC(apple2e_state::c800_cec_r)).nopw();
	m_c800bank[4](0xc800, 0xcfff).r(FUNC(apple2e_state::c800_cec_bank_r)).nopw();

	map(0xd000, 0xffff).view(m_upperbank);
	m_upperbank[0](0xd000, 0xffff).view(m_lcbank);
	m_upperbank[1](0xd000, 0xffff).rw(FUNC(apple2e_state::inh_r), FUNC(apple2e_state::inh_w));

	m_lcbank[0](0x0d000, 0x0ffff).rom().region("maincpu", 0x1000).w(FUNC(apple2e_state::lc_w));
	m_lcbank[1](0x0d000, 0x0ffff).rw(FUNC(apple2e_state::lc_r), FUNC(apple2e_state::lc_w));
	m_lcbank[2](0x0d000, 0x0ffff).rw(FUNC(apple2e_state::lc_romswitch_r), FUNC(apple2e_state::lc_romswitch_w));
	m_lcbank[3](0x0d000, 0x0ffff).rom().region("maincpu", 0x5000).w(FUNC(apple2e_state::lc_w));
	m_lcbank[4](0x0d000, 0x0ffff).rw(FUNC(apple2e_state::lc_r), FUNC(apple2e_state::lc_w));
	m_lcbank[5](0x0d000, 0x1ffff).rom().region("maincpu", 0xd000).w(FUNC(apple2e_state::lc_w));
}

void apple2e_state::apple2c_map(address_map &map)
{
	base_map(map);
	map(0xc000, 0xc07f).rw(FUNC(apple2e_state::c000_iic_r), FUNC(apple2e_state::c000_iic_w));
	map(0xc098, 0xc09b).rw(m_acia1, FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0xc0a8, 0xc0ab).rw(m_acia2, FUNC(mos6551_device::read), FUNC(mos6551_device::write));
}

void apple2e_state::apple2c_memexp_map(address_map &map)
{
	base_map(map);
	map(0xc000, 0xc07f).rw(FUNC(apple2e_state::c000_iic_r), FUNC(apple2e_state::c000_iic_w));
	map(0xc098, 0xc09b).rw(m_acia1, FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0xc0a8, 0xc0ab).rw(m_acia2, FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0xc0c0, 0xc0c3).rw(FUNC(apple2e_state::memexp_r), FUNC(apple2e_state::memexp_w));
}

void apple2e_state::laser128_map(address_map &map)
{
	base_map(map);
	map(0xc000, 0xc07f).rw(FUNC(apple2e_state::c000_laser_r), FUNC(apple2e_state::c000_laser_w));
	map(0xc090, 0xc097).w(FUNC(apple2e_state::laserprn_w));
	map(0xc098, 0xc09b).rw(m_acia1, FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0xc0a8, 0xc0ab).rw(m_acia2, FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0xc0c0, 0xc0cf).rw(FUNC(apple2e_state::laser_mouse_r), FUNC(apple2e_state::laser_mouse_w));
	map(0xc0d0, 0xc0d3).rw(FUNC(apple2e_state::memexp_r), FUNC(apple2e_state::memexp_w));
	map(0xc0e0, 0xc0ef).rw(m_iwm, FUNC(applefdintf_device::read), FUNC(applefdintf_device::write));
	map(0xc0e8, 0xc0e9).rw(FUNC(apple2e_state::laser_motor_r), FUNC(apple2e_state::laser_motor_w));
	map(0xc1c1, 0xc1c1).r(FUNC(apple2e_state::laserprn_busy_r));
}

void apple2e_state::ace500_map(address_map &map)
{
	base_map(map);
	map(0xc000, 0xc07f).rw(FUNC(apple2e_state::c000_iic_r), FUNC(apple2e_state::c000_iic_w));
	map(0xc0a8, 0xc0ab).rw(m_acia1, FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0xc090, 0xc097).w(FUNC(apple2e_state::laserprn_w));
	map(0xc0b0, 0xc0bf).rw(FUNC(apple2e_state::ace500_c0bx_r), FUNC(apple2e_state::ace500_c0bx_w));
	map(0xc1c1, 0xc1c1).r(FUNC(apple2e_state::laserprn_busy_r));
}

void apple2e_state::ace2200_map(address_map &map)
{
	base_map(map);

	// change the banking here to accommodate the Ace 2x00's ROM banking in at $A000
	m_4000bank[0](0x4000, 0xbfff).rw(FUNC(apple2e_state::ram4000_ace2200_r), FUNC(apple2e_state::ram4000_w));

	map(0xc090, 0xc097).w(FUNC(apple2e_state::laserprn_w));
	map(0xc1c1, 0xc1c1).r(FUNC(apple2e_state::laserprn_busy_r));
}

void apple2e_state::spectred_keyb_map(address_map &map)
{
	map(0x0000, 0x07ff).rom();
	map(0x0800, 0x0fff).ram();
}

/***************************************************************************
    KEYBOARD
***************************************************************************/

int apple2e_state::ay3600_shift_r()
{
	// either shift key
	if (m_kbspecial->read() & 0x06)
	{
		return ASSERT_LINE;
	}

	return CLEAR_LINE;
}

int apple2e_state::ay3600_control_r()
{
	if (m_kbspecial->read() & 0x08)
	{
		return ASSERT_LINE;
	}

	return CLEAR_LINE;
}

void apple2e_state::ay3600_data_ready_w(int state)
{
	if (state == ASSERT_LINE)
	{
		u8 *decode = m_kbdrom->base();
		u16 trans;

		// if the user presses a valid key to start the driver from the info screen,
		// we will see that key.  ignore keys in the first 25,000 cycles (in my tests,
		// the unwanted key shows up at 17030 cycles)
		if (m_maincpu->total_cycles() < 25000)
		{
			return;
		}

		m_lastchar = m_ay3600->b_r();

		trans = m_lastchar & ~(0x1c0);  // clear the 3600's control/shift stuff
		trans |= (m_lastchar & 0x100)>>2;   // bring the 0x100 bit down to the 0x40 place
		trans <<= 2;                    // 4 entries per key
		if (m_iscec)
		{
			trans |= (m_kbspecial->read() & 0x06) ? 0x01 : 0x00;    // shift is bit 1 (active high)
			trans |= (m_kbspecial->read() & 0x08) ? 0x02 : 0x00;    // control is bit 2 (active high)
		}
		else
		{
			trans |= (m_kbspecial->read() & 0x06) ? 0x00 : 0x01;    // shift is bit 1 (active low)
			trans |= (m_kbspecial->read() & 0x08) ? 0x00 : 0x02;    // control is bit 2 (active low)
		}
		trans |= (m_kbspecial->read() & 0x01) ? 0x0000 : 0x0200;    // caps lock is bit 9 (active low)

		if (m_kbd_lang_sel)
		{
			u8 kbd_layout_id = (m_kbd_lang_sel->read() & 0xf0) >> 4;
			trans += kbd_layout_id * 0x400; // go to second half of the ROM (DVORAK on US IIc/IIe models) or beyond
		}

		m_transchar = decode[trans];
		m_strobe = 0x80;

		//printf("new char = %04x (%02x)\n", m_lastchar, m_transchar);
	}
}

void apple2e_state::ay3600_ako_w(int state)
{
	m_anykeydown = (state == ASSERT_LINE) ? true : false;

	if (m_anykeydown)
	{
		m_repeatdelay = 10;
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(apple2e_state::ay3600_repeat)
{
	// is the key still down?
	if (m_anykeydown)
	{
		if (m_repeatdelay)
		{
			m_repeatdelay--;
		}
		else
		{
			m_strobe = 0x80;
		}
	}
}

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

static INPUT_PORTS_START( apple2_sysconfig_accel )
	PORT_START("a2_config")
	PORT_CONFNAME(0x10, 0x00, "CPU type")
	PORT_CONFSETTING(0x00, "Standard")
	PORT_CONFSETTING(0x10, "4 MHz Zip Chip")

	PORT_CONFNAME(0x20, 0x00, "Bootup speed")
	PORT_CONFSETTING(0x00, "Standard")
	PORT_CONFSETTING(0x20, "4 MHz")

INPUT_PORTS_END

static INPUT_PORTS_START( laser128_sysconfig )
	PORT_START("a2_config")
	PORT_CONFNAME(0x08, 0x00, "Printer type")
	PORT_CONFSETTING(0x00, "Serial")
	PORT_CONFSETTING(0x08, "Parallel")
	PORT_CONFNAME(0x40, 0x40, "40/80 Columns")
	PORT_CONFSETTING(0x00, "80 columns")
	PORT_CONFSETTING(0x40, "40 columns")

	PORT_START("kbd_lang_select")
	PORT_CONFNAME(0xff, 0x00, "Keyboard")
	PORT_CONFSETTING(0x00, "QWERTY")
	PORT_CONFSETTING(0x10, "DVORAK") // Only switch keyboard offset - second half of character ROM "laser 128 video rom vt27-0706-0.bin" has French characters
INPUT_PORTS_END

static INPUT_PORTS_START( apple2c_common_config )
	PORT_START("a2_config")
	PORT_CONFNAME(0x40, 0x40, "40/80 Columns")
	PORT_CONFSETTING(0x00, "80 columns")
	PORT_CONFSETTING(0x40, "40 columns")
	PORT_CONFNAME(0x10, 0x00, "CPU type")
	PORT_CONFSETTING(0x00, "Standard")
	PORT_CONFSETTING(0x10, "4 MHz Zip Chip")
	PORT_CONFNAME(0x20, 0x00, "Bootup speed")
	PORT_CONFSETTING(0x00, "Standard")
	PORT_CONFSETTING(0x20, "4 MHz")
INPUT_PORTS_END

	/*

	  North American (NAM) Apple IIe & IIc key matrix (from "Sams ComputerFacts: Apple IIe" and "Sams ComputerFacts: Apple IIc")

	      | Y0  | Y1  | Y2  | Y3  | Y4  | Y5  | Y6  | Y7  | Y8  | Y9  |
	      |     |     |     |     |     |     |     |     |     |     |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X0  | ESC |  1  |  2  |  3  |  4  |  6  |  5  |  7  |  8  |  9  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X1  | TAB |  Q  |  W  |  E  |  R  |  Y  |  T  |  U  |  I  |  O  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X2  |  A  |  D  |  S  |  H  |  F  |  G  |  J  |  K  | ;:  |  L  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X3  |  Z  |  X  |  C  |  V  |  B  |  M  |  N  | ,<  | .>  |  /? |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X4  |     |     |     |     |     |     | \|  | +=  |  0  | -_  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X5  |     |     |     |     |     |     | `~  |  P  | [{  | ]}  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X6  |     |     |     |     |     |     |RETRN| UP  | SPC | '"  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X7  |     |     |     |     |     |     | DEL |DOWN |LEFT |RIGHT|
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|

	  The ISO Apple IIe & IIc key matrix (\| and `~ are swapped)

	      | Y0  | Y1  | Y2  | Y3  | Y4  | Y5  | Y6  | Y7  | Y8  | Y9  |
	      |     |     |     |     |     |     |     |     |     |     |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X0  | ESC |  1  |  2  |  3  |  4  |  6  |  5  |  7  |  8  |  9  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X1  | TAB |  Q  |  W  |  E  |  R  |  Y  |  T  |  U  |  I  |  O  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X2  |  A  |  D  |  S  |  H  |  F  |  G  |  J  |  K  | ;:  |  L  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X3  |  Z  |  X  |  C  |  V  |  B  |  M  |  N  | ,<  | .>  |  /? |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X4  |     |     |     |     |     |     | `~  | +=  |  0  | -_  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X5  |     |     |     |     |     |     | \|  |  P  | [{  | ]}  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X6  |     |     |     |     |     |     |RETRN| UP  | SPC | '"  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X7  |     |     |     |     |     |     | DEL |DOWN |LEFT |RIGHT|
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|

	*/

	/*
	  Original Apple IIe keypad matrix (341-0132-B - 1982, and returned to use with 341-0132-D)

	      | Y0  | Y1  | Y2  | Y3  | Y4  | Y5  |
	      |     |     |     |     |     |     |
	  ----+-----+-----+-----+-----+-----+-----+
	  X4  | KP/ |KPLFT| KP0 | KP1 | KP2 | KP3 |
	  ----+-----+-----+-----+-----+-----+-----+
	  X5  | KP) |KPEsc| KP4 | KP5 | KP6 | KP7 |
	  ----+-----+-----+-----+-----+-----+-----+
	  X6  | KP* |KPRGT| KP8 | KP9 | KP. | KP+ |
	  ----+-----+-----+-----+-----+-----+-----+
	  X7  | KP? |KPSPC| KP( | KP- |KPENT| KP, |
	  ----+-----+-----+-----+-----+-----+-----+

	      Note: KP ? is labeled as Print on the physical keypad (P/N: A2M2003), where ? is a shortcut for PRINT in Applesoft Basic

	*/

	/*
	  Revised Apple IIe keypad matrix (341-0132-C - 1983)

	      | Y0  | Y1  | Y2  | Y3  | Y4  | Y5  |
	      |     |     |     |     |     |     |
	  ----+-----+-----+-----+-----+-----+-----+
	  X4  | KP/ |KPDWN| KP0 | KP1 | KP2 | KP3 |
	  ----+-----+-----+-----+-----+-----+-----+
	  X5  | KP) |KPUP | KP4 | KP5 | KP6 | KP7 |
	  ----+-----+-----+-----+-----+-----+-----+
	  X6  | KP* |KPLFT| KP8 | KP9 | KP. | KP+ |
	  ----+-----+-----+-----+-----+-----+-----+
	  X7  |KPEsc|KPRGT| KP( | KP- |KPENT| KP, |
	  ----+-----+-----+-----+-----+-----+-----+

	      Note: It is unknown if Apple released any numeric keypads with up, down arrows
	*/

	/*
	  Apple IIe platinum key matrix

	      | Y0  | Y1  | Y2  | Y3  | Y4  | Y5  | Y6  | Y7  | Y8  | Y9  |
	      |     |     |     |     |     |     |     |     |     |     |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X0  | ESC |  1  |  2  |  3  |  4  |  6  |  5  |  7  |  8  |  9  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X1  | TAB |  Q  |  W  |  E  |  R  |  Y  |  T  |  U  |  I  |  O  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X2  |  A  |  D  |  S  |  H  |  F  |  G  |  J  |  K  | ;:  |  L  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X3  |  Z  |  X  |  C  |  V  |  B  |  M  |  N  | ,<  | .>  |  /? |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X4  | KP/ |     | KP0 | KP1 | KP2 | KP3 | \|  | +=  |  0  | -_  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X5  |     |KPEsc| KP4 | KP5 | KP6 | KP7 | `~  |  P  | [{  | ]}  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X6  | KP* |     | KP8 | KP9 | KP. | KP+ |RETRN| UP  | SPC | '"  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X7  |     |     |     | KP- |KPENT|     | DEL |DOWN |LEFT |RIGHT|
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	*/

	/*
	      Keyboard ROMs contain tables of physical key to ASCII code translation.  The tables factor in whether the lock, shift, and control keys are pressed.
	      The following Apple IIe/IIc keyboard layouts are known to exist:

	     - North American (NAM)
	     -- US English QWERTY
	     -- US English DVORAK
	     -- Western hemisphere French QWERTY
	     -- Western hemisphere Spanish QWERTY
	     --- original as documented in Apple // Supplement to the Owner's Manual (1982) - document 030-0535-A
	     --- revised as documented in Apple IIe Technical Reference Manual (1989) - document 030-1194-B
	     - ISO
	     -- US English QWERTY
	     -- UK English QWERTY
	     -- French AZERTY
	     -- Italian QZERTY
	     -- German QWERTZ
	     -- Swedish QWERTY
	     -- Spanish QWERTY

	     The table below lists keyboard ROMs believed to exist.  Notes:
	     - Good dumps are required to complete/correct this table
	     - The 342- prefix is used below, but in some cases, it could be 341, or both 342/341.  It is believed that the content is identical when both ROMs are avaiable.
	     - SL: shift lock instead of caps lock
	     - SC: shift cancels the lock when both are activated

	     ROM ID     Year Type Primary Language/Layout Alternate Language/Layout         Countries     Keypad Layout Verified
	     ========== ==== ==== ======================= =========================         ============= ============= ========
	     342-0132-A 1982 NAM  US QWERTY               ?                                 US            original      no
	     342-0132-B 1982 NAM  US QWERTY               US DVORAK (original)              US, AU        original      yes
	     342-0132-C 1983 NAM  US QWERTY               US DVORAK (revised)               US, AU        revised       yes
	     342-0132-D 1984 NAM  US QWERTY               US DVORAK (revised)               US, AU        original      yes
	     342-0150-A 1982 ISO  UK QWERTY               US QWERTY                         UK, NL, AU    original      yes
	     342-0151-A 1982 ISO  German QWERTZ           US QWERTY                         DE            original      yes
	     342-0152-A 1982 ISO  Swedish QWERTY          US QWERTY                         SE, FI        original      yes
	     342-0153-A 1982 ISO  French AZERTY           US QWERTY                         FR, BE        original      yes
	     342-0154-A 1982 ISO  Italian QZERTY          US QWERTY                         IT            original      yes
	     342-0155-A 1982 ISO  US QWERTY               Western Spanish QWERTY (original) Latin America original      no (order may be reversed)
	     342-0211-A 1982 ISO  Spanish QWERTY          US QWERTY                         ES, PT        original      yes
	     342-0283-A 1983 ISO  French AZERTY (SL)      US QWERTY                         FR            original      no
	     342-0284-A 198? ISO  Italian QZERTY (SL)     US QWERTY                         IT            original      no
	     342-0292-A 198? NAM  US QWERTY               Western French QWERTY             CA            original      no (order may be reversed)
	     342-0???-A 198? NAM  US QWERTY               Western Spanish QWERTY (revised)  Latin America original      no (order may be reversed)
	     342-0326-A 1985 ISO  French AZERTY (SL, SC)  US QWERTY                         FR, BE        original      partial (only the French half of the ROM has been located)
	     342-0327-A 1985 ISO  German QWERTZ           US QWERTY                         DE            original      no
	     342-0329-A 1985 ISO  Italian QZERTY (SL)     US QWERTY                         IT            original      no

	     The table below lists video character generator ROMs believed to exist.  Notes:
	     - Good dumps are required to complete/correct this table
	     - The 342- prefix is used below, but in some cases, it could be 341, or both 342/341.  It is believed that the content is identical when both ROMs are avaiable.
	     - Western French and Spanish ROMs have the same character sets as the European French and Spanish ROMs.  The differences may be the order of primary vs. alternate languages, but this needs confirmation.

	     ROM ID     Primary Language Alternate Language MouseText Countries     Verified
	     ========== ================ ================== ========= ============= ========
	     342-0133-A US English       none               no        US, AU        yes
	     342-0160-A UK English       US English         no        UK, NL, AU    yes
	     342-0161-A German           US English         no        DE            yes
	     342-0162-A Swedish          US English         no        SE            yes
	     342-0163-A French           US English         no        FR, BE        no
	     342-0164-A Italian          US English         no        IT            yes
	     342-0165-A US English       Spanish            no        Latin America no (order may be reversed)
	     342-0166-A US English       French             no        CA            no (order may be reversed)
	     342-0167-A US English       Spanish            no        Latin America no (order may be reversed)
	     342-0168-A US English       French             no        CA            no (order may be reversed)
	     342-0212-A Spanish          US English         no        ES, PT        yes
	     342-0265-A US English       none               yes       US, AU        yes
	     342-0273-A UK English       US English         yes       UK, NL, AU    yes
	     342-0274-A French           US English         yes       FR, BE        yes
	     342-0275-A German           US English         yes       DE            yes
	     342-0276-A Italian          US English         yes       IT            yes
	     342-0306-A US English       French             yes       CA            yes (order may be reversed)
	     342-0307-A US English       Spanish            yes       Latin America no (order may be reversed)
	     342-0328-A Swedish          US English         yes       SE            no
	     342-0nnn-A Swedish          US English         yes       SE            no (possible revision based on 342-0328 but with a tilde ~ instead of a macron ¯)
	     342-0nnn-A Spanish          US English         yes       ES, PT        no

	     References:
	         - Apple // Supplement to the Owner's Manual (1982) - document 030-0535-A
	         - Apple //e Enhancement Kit - Dealer's Installation Instructions - document 030-1142-A
	         - Apple IIe Technical Reference Manual (1989) - document 030-1194-B
	         - Apple IIc Reference Manual (1984) - document 030-1022-B
	         - Apple IIc Technical Reference Manual (1987) - document 030-1238-B
	         - Apple IIc Technical Reference - Second Edition (1989)

	*/

static INPUT_PORTS_START( apple2e_nam_us_kbd )
	PORT_START("X0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Esc")      PORT_CODE(KEYCODE_ESC)      PORT_CHAR(27)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)  PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)  PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)  PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)  PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)  PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)  PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)  PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)  PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)  PORT_CHAR('9') PORT_CHAR('(')

	PORT_START("X1")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab")      PORT_CODE(KEYCODE_TAB)      PORT_CHAR(9)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)  PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)  PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)  PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)  PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)  PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)  PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)  PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)  PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)  PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("X2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)  PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)  PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)  PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)  PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)  PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)  PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)  PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)  PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)  PORT_CHAR('l') PORT_CHAR('L')

	PORT_START("X3")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)  PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)  PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)  PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)  PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)  PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)  PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)  PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)  PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)   PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)  PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("X4")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)      PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)  PORT_CHAR('-') PORT_CHAR('_')

	PORT_START("X5")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('`') PORT_CHAR('~')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)  PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')

	PORT_START("X6")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Return")       PORT_CODE(KEYCODE_ENTER)    PORT_CHAR(13)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_UP)        PORT_CODE(KEYCODE_UP)       PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)  PORT_CHAR(' ')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)  PORT_CHAR('\'') PORT_CHAR('\"')

	PORT_START("X7")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Delete")       PORT_CODE(KEYCODE_BACKSPACE)PORT_CHAR(8)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN)      PORT_CODE(KEYCODE_DOWN)     PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT)      PORT_CODE(KEYCODE_LEFT)     PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT)     PORT_CODE(KEYCODE_RIGHT)    PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

	PORT_START("X8")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( apple2e_iso_us_kbd )
	PORT_INCLUDE( apple2e_nam_us_kbd )

	PORT_MODIFY("X4")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('`') PORT_CHAR('~')

	PORT_MODIFY("X5")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('\\') PORT_CHAR('|')
INPUT_PORTS_END

static INPUT_PORTS_START( apple2e_special )
	PORT_START("keyb_special")
	PORT_BIT( 0x01, IP_ACTIVE_LOW,  IPT_KEYBOARD) PORT_NAME("Caps Lock")    PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Shift")   PORT_CODE(KEYCODE_LSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Shift")  PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Control")      PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Open Apple")   PORT_CODE(KEYCODE_LALT)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Solid Apple")  PORT_CODE(KEYCODE_RALT)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RESET")        PORT_CODE(KEYCODE_F12)
INPUT_PORTS_END

static INPUT_PORTS_START( apple2e_special_sl ) // replace Caps Lock with Shift Lock
	PORT_MODIFY("keyb_special")
	PORT_BIT( 0x01, IP_ACTIVE_LOW,  IPT_KEYBOARD) PORT_NAME("Shift Lock")    PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
INPUT_PORTS_END

static INPUT_PORTS_START( ceci )
	PORT_START("X0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)      PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)  PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)  PORT_CHAR('\'') PORT_CHAR('\"')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)  PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)  PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)  PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_NAME("F4")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)  PORT_CHAR(' ')
	PORT_BIT(0x240, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X1")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)      PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)  PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('`') PORT_CHAR('~')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)  PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)  PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)  PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_NAME("F5")
	PORT_BIT(0x340, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)  PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)  PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)  PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)  PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)  PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)  PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_NAME("CN")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Esc")      PORT_CODE(KEYCODE_ESC)      PORT_CHAR(27)
	PORT_BIT(0x300, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X3")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)  PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)   PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)  PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)  PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)  PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab")      PORT_CODE(KEYCODE_TAB)      PORT_CHAR(9)
	PORT_BIT(0x340, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X4")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)  PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)  PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)  PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)  PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)  PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_NAME("EN")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9) PORT_NAME("STOP")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT)      PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X5")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)  PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11) PORT_NAME("TEST")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)  PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)  PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)  PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_NAME("F1")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_UP)        PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x280, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X6")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)  PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)  PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)  PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)  PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_NAME("F2")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN)      PORT_CODE(KEYCODE_DOWN)     PORT_CHAR(10)
	PORT_BIT(0x280, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X7")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)  PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)  PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)  PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)  PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)  PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_NAME("F3")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Return")   PORT_CODE(KEYCODE_ENTER)    PORT_CHAR(13)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT)     PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X8")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("keyb_special")
	PORT_BIT( 0x01, IP_ACTIVE_LOW,  IPT_KEYBOARD) PORT_NAME("Caps Lock")    PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Shift")   PORT_CODE(KEYCODE_LSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Shift")  PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Control")      PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RESET")        PORT_CODE(KEYCODE_F12)
INPUT_PORTS_END

static INPUT_PORTS_START( cecm )
	PORT_START("X0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)      PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)  PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)  PORT_CHAR('\'') PORT_CHAR('\"')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)  PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)  PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)  PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_NAME("F4")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)  PORT_CHAR(' ')
	PORT_BIT(0x240, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X1")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)      PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)  PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('`') PORT_CHAR('~')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)  PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)  PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)  PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_NAME("F5")
	PORT_BIT(0x340, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)  PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)  PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)  PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)  PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)  PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)  PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_NAME("CN")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Esc")      PORT_CODE(KEYCODE_ESC)      PORT_CHAR(27)
	PORT_BIT(0x300, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X3")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)  PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)   PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)  PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)  PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)  PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab")      PORT_CODE(KEYCODE_TAB)      PORT_CHAR(9)
	PORT_BIT(0x340, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X4")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)  PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)  PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)  PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)  PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)  PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_NAME("EN")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9) PORT_NAME("STOP")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT)      PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X5")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)  PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11) PORT_NAME("TEST")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)  PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)  PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)  PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_NAME("F1")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_UP)        PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x280, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X6")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)  PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)  PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)  PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)  PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_NAME("F2")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN)      PORT_CODE(KEYCODE_DOWN)     PORT_CHAR(10)
	PORT_BIT(0x280, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X7")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)  PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)  PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)  PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)  PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)  PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_NAME("F3")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Return")   PORT_CODE(KEYCODE_ENTER)    PORT_CHAR(13)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT)     PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X8")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("keyb_special")
	PORT_BIT( 0x01, IP_ACTIVE_LOW,  IPT_KEYBOARD) PORT_NAME("Caps Lock")    PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Shift")   PORT_CODE(KEYCODE_LSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Shift")  PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Control")      PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RESET")        PORT_CODE(KEYCODE_F12)
INPUT_PORTS_END

static INPUT_PORTS_START( zijini )
	PORT_START("X0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)      PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)  PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)  PORT_CHAR('\'') PORT_CHAR('\"')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)  PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)  PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)  PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_NAME("F4")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)  PORT_CHAR(' ')
	PORT_BIT(0x240, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X1")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)      PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)  PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('`') PORT_CHAR('~')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)  PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)  PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)  PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_NAME("EN")
	PORT_BIT(0x340, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)  PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)  PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)  PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)  PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)  PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)  PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_NAME("CN")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Esc")      PORT_CODE(KEYCODE_ESC)      PORT_CHAR(27)
	PORT_BIT(0x300, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X3")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)  PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)   PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)  PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)  PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)  PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab")      PORT_CODE(KEYCODE_TAB)      PORT_CHAR(9)
	PORT_BIT(0x340, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X4")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)  PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)  PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)  PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)  PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)  PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9) PORT_NAME("STOP")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT)      PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X5")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)  PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11) PORT_NAME("TEST")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)  PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)  PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)  PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_NAME("F1")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_UP)        PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x280, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X6")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)  PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)  PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)  PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)  PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_NAME("F2")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN)      PORT_CODE(KEYCODE_DOWN)     PORT_CHAR(10)
	PORT_BIT(0x280, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X7")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)  PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)  PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)  PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)  PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)  PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_NAME("F3")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Return")   PORT_CODE(KEYCODE_ENTER)    PORT_CHAR(13)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT)     PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X8")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("keyb_special")
	PORT_BIT( 0x01, IP_ACTIVE_LOW,  IPT_KEYBOARD) PORT_NAME("Caps Lock")    PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Shift")   PORT_CODE(KEYCODE_LSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Shift")  PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Control")      PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RESET")        PORT_CODE(KEYCODE_F12)
INPUT_PORTS_END

static INPUT_PORTS_START( apple2e )
	PORT_INCLUDE( apple2e_nam_us_kbd )
	PORT_INCLUDE( apple2e_special )
	PORT_INCLUDE( apple2_sysconfig_accel )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2c )
	PORT_INCLUDE( apple2e_special )
	PORT_INCLUDE( apple2c_common_config )

	PORT_START(MOUSE_BUTTON_TAG) /* Mouse - button */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Mouse Button") PORT_CODE(MOUSECODE_BUTTON1)

	PORT_START(MOUSE_XAXIS_TAG) /* Mouse - X AXIS */
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_X) PORT_SENSITIVITY(40) PORT_KEYDELTA(0) PORT_PLAYER(1)

	PORT_START(MOUSE_YAXIS_TAG) /* Mouse - Y AXIS */
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y) PORT_SENSITIVITY(40) PORT_KEYDELTA(0) PORT_PLAYER(1)
INPUT_PORTS_END

static INPUT_PORTS_START( apple2cus_sysconfig )
	PORT_START("kbd_lang_select")
	PORT_CONFNAME(0xff, 0x00, "Keyboard")
	PORT_CONFSETTING(0x00, "QWERTY")
	PORT_CONFSETTING(0x12, "DVORAK")
INPUT_PORTS_END

static INPUT_PORTS_START( apple2cus )
	PORT_INCLUDE( apple2e_nam_us_kbd )
	PORT_INCLUDE( apple2c )
	PORT_INCLUDE( apple2cus_sysconfig )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2euk_sysconfig )
	PORT_START("kbd_lang_select")
	PORT_CONFNAME(0xff, 0x00, "Keyboard")
	PORT_CONFSETTING(0x00, "UK English")
	PORT_CONFSETTING(0x12, "US English")
INPUT_PORTS_END

static INPUT_PORTS_START( apple2ede_sysconfig )
	PORT_START("kbd_lang_select")
	PORT_CONFNAME(0xff, 0x00, "Keyboard")
	PORT_CONFSETTING(0x00, "German")
	PORT_CONFSETTING(0x12, "US English")
INPUT_PORTS_END

static INPUT_PORTS_START( apple2ese_sysconfig )
	PORT_START("kbd_lang_select")
	PORT_CONFNAME(0xff, 0x00, "Keyboard")
	PORT_CONFSETTING(0x00, "Swedish")
	PORT_CONFSETTING(0x12, "US English")
INPUT_PORTS_END

static INPUT_PORTS_START( laser128 )
	PORT_INCLUDE( apple2e_nam_us_kbd )
	PORT_INCLUDE( laser128_sysconfig )

	PORT_START("keyb_special")
	PORT_BIT( 0x01, IP_ACTIVE_LOW,  IPT_KEYBOARD) PORT_NAME("Caps Lock")    PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Shift")   PORT_CODE(KEYCODE_LSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Shift")  PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Control")      PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Open Triangle")   PORT_CODE(KEYCODE_LALT)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Solid Triangle")  PORT_CODE(KEYCODE_RALT)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RESET")        PORT_CODE(KEYCODE_F12)

	PORT_START(MOUSE_BUTTON_TAG) /* Mouse - button */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Mouse Button") PORT_CODE(MOUSECODE_BUTTON1)

	PORT_START(MOUSE_XAXIS_TAG) /* Mouse - X AXIS */
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_X) PORT_SENSITIVITY(40) PORT_KEYDELTA(0) PORT_PLAYER(1)

	PORT_START(MOUSE_YAXIS_TAG) /* Mouse - Y AXIS */
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y) PORT_SENSITIVITY(40) PORT_KEYDELTA(0) PORT_PLAYER(1)
INPUT_PORTS_END

static INPUT_PORTS_START( ace_common )
	PORT_INCLUDE( apple2e_nam_us_kbd )

	PORT_START("kbd_lang_select")
	PORT_CONFNAME(0xff, 0x00, "Character Set") // Franklin ACE's switch changes video ROM character output without affecting the keyboard
	PORT_CONFSETTING(0x00, "Mouse")
	PORT_CONFSETTING(0x02, "Std")

	PORT_START("keyb_special")
	PORT_BIT( 0x01, IP_ACTIVE_LOW,  IPT_KEYBOARD) PORT_NAME("Caps Lock")    PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Shift")   PORT_CODE(KEYCODE_LSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Shift")  PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Control")      PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Open F")       PORT_CODE(KEYCODE_LALT)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Solid F")      PORT_CODE(KEYCODE_RALT)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RESET")        PORT_CODE(KEYCODE_F12)

	PORT_START("franklin_fkeys")
	PORT_BIT(0x80000000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)       PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x40000000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)       PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x20000000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)       PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x10000000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)       PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x08000000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)       PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x04000000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)       PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x02000000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)       PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x01000000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)       PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x00800000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9)       PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x00400000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10)      PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT(0x00200000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11)      PORT_CHAR(UCHAR_MAMEKEY(F11))
	PORT_BIT(0x00100000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F12)      PORT_CHAR(UCHAR_MAMEKEY(F12))

	PORT_START(MOUSE_BUTTON_TAG) /* Mouse - button */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Mouse Button") PORT_CODE(MOUSECODE_BUTTON1)

	PORT_START(MOUSE_XAXIS_TAG) /* Mouse - X AXIS */
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_X) PORT_SENSITIVITY(40) PORT_KEYDELTA(0) PORT_PLAYER(1)

	PORT_START(MOUSE_YAXIS_TAG) /* Mouse - Y AXIS */
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y) PORT_SENSITIVITY(40) PORT_KEYDELTA(0) PORT_PLAYER(1)
INPUT_PORTS_END

static INPUT_PORTS_START( ace2200 )
	PORT_START("a2_config")
	PORT_CONFNAME(0x80, 0x00, "Auto Line Feed for printer")
	PORT_CONFSETTING(0x80, DEF_STR(On))
	PORT_CONFSETTING(0x00, DEF_STR(Off))

	PORT_INCLUDE( ace_common )
INPUT_PORTS_END

static INPUT_PORTS_START( ace500 )
	PORT_START("a2_config")
	PORT_CONFNAME(0x40, 0x40, "40/80 Columns")
	PORT_CONFSETTING(0x00, "80 columns")
	PORT_CONFSETTING(0x40, "40 columns")
	PORT_CONFNAME(0x80, 0x00, "Auto Line Feed for printer")
	PORT_CONFSETTING(0x80, DEF_STR(On))
	PORT_CONFSETTING(0x00, DEF_STR(Off))

	PORT_INCLUDE( ace_common )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2cp )
	PORT_INCLUDE( apple2e_nam_us_kbd )
	PORT_INCLUDE( apple2e_special )
	PORT_INCLUDE( apple2cus_sysconfig )

	PORT_START(MOUSE_BUTTON_TAG) /* Mouse - button */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Mouse Button") PORT_CODE(MOUSECODE_BUTTON1)

	PORT_START(MOUSE_XAXIS_TAG) /* Mouse - X AXIS */
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_X) PORT_SENSITIVITY(40) PORT_KEYDELTA(0) PORT_PLAYER(1)

	PORT_START(MOUSE_YAXIS_TAG) /* Mouse - Y AXIS */
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y) PORT_SENSITIVITY(40) PORT_KEYDELTA(0) PORT_PLAYER(1)
INPUT_PORTS_END

static INPUT_PORTS_START( apple2eus_sysconfig )
	PORT_START("kbd_lang_select")
	PORT_CONFNAME(0xff, 0x00, "Dvorak keyboard layout mod")
	PORT_CONFSETTING(0x00, "Not installed")
	PORT_CONFSETTING(0x10, "Installed")
INPUT_PORTS_END

static INPUT_PORTS_START( apple2eus )
	PORT_INCLUDE( apple2e )
	PORT_INCLUDE( apple2eus_sysconfig )
INPUT_PORTS_END

static INPUT_PORTS_START( tk3000 )
	PORT_INCLUDE( apple2e )

	PORT_START("kbd_lang_select")
	PORT_CONFNAME(0xff, 0x00, "Character Set")
	PORT_CONFSETTING(0x00, "Portuguese")
	PORT_CONFSETTING(0x01, "US English")
INPUT_PORTS_END

static INPUT_PORTS_START( spectred )
	PORT_INCLUDE( apple2e )

	PORT_START("kbd_lang_select")
	PORT_CONFNAME(0xff, 0x00, "Character Set")
	PORT_CONFSETTING(0x00, "Portuguese without MouseText")
	PORT_CONFSETTING(0x01, "US English with MouseText")
INPUT_PORTS_END

static INPUT_PORTS_START( prav8c )
	PORT_INCLUDE( apple2e )

	PORT_START("kbd_lang_select")
	PORT_CONFNAME(0xff, 0x00, "Character Set")
	PORT_CONFSETTING(0x00, "Cyrilic")
	PORT_CONFSETTING(0x02, "US English")
INPUT_PORTS_END

static INPUT_PORTS_START( apple2e_iso_uk_kbd )
	PORT_INCLUDE( apple2e_iso_us_kbd )

	PORT_MODIFY("X0")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)  PORT_CHAR('3') PORT_CHAR(U'£')
INPUT_PORTS_END

static INPUT_PORTS_START( apple2euk_common )
	PORT_INCLUDE( apple2euk_sysconfig )
	PORT_INCLUDE( apple2e_iso_uk_kbd )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2cuk )
	PORT_INCLUDE( apple2euk_common )
	PORT_INCLUDE( apple2c )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2euk )
	PORT_INCLUDE( apple2euk_common )
	PORT_INCLUDE( apple2e_special )
	PORT_INCLUDE( apple2_sysconfig_accel )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2e_iso_de_kbd )
	PORT_INCLUDE( apple2e_iso_us_kbd )

	PORT_MODIFY("X0")
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)  PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)  PORT_CHAR('3') PORT_CHAR(U'§')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)  PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)  PORT_CHAR('7') PORT_CHAR('/')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)  PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)  PORT_CHAR('9') PORT_CHAR(')')

	PORT_MODIFY("X1")
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)  PORT_CHAR('z') PORT_CHAR('Z')

	PORT_MODIFY("X2")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(U'ö') PORT_CHAR(U'Ö')

	PORT_MODIFY("X3")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)      PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)  PORT_CHAR(',') PORT_CHAR(';')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)   PORT_CHAR('.') PORT_CHAR(':')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)  PORT_CHAR('-') PORT_CHAR('_')

	PORT_MODIFY("X4")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('#') PORT_CHAR(U'^') // (actually to the left of the return key on the ASDF row)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('\'') PORT_CHAR('`')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)      PORT_CHAR('0') PORT_CHAR('=')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)  PORT_CHAR(U'ß') PORT_CHAR('?')

	PORT_MODIFY("X5")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('<') PORT_CHAR('>')  // actually the key between left shift and Y
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR(U'ü') PORT_CHAR(U'Ü')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('+') PORT_CHAR('*')

	PORT_MODIFY("X6")
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)  PORT_CHAR(U'ä') PORT_CHAR(U'Ä')
INPUT_PORTS_END

static INPUT_PORTS_START( apple2ede_common )
	PORT_INCLUDE( apple2ede_sysconfig )
	PORT_INCLUDE( apple2e_iso_de_kbd )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2cde )
	PORT_INCLUDE( apple2ede_common )
	PORT_INCLUDE( apple2c )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2ede )
	PORT_INCLUDE( apple2ede_common )
	PORT_INCLUDE( apple2e_special )
	PORT_INCLUDE( apple2_sysconfig_accel )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2e_iso_se_kbd )
	PORT_INCLUDE( apple2e_iso_us_kbd )

	PORT_MODIFY("X0")
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)  PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)  PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)  PORT_CHAR('7') PORT_CHAR('/')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)  PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)  PORT_CHAR('9') PORT_CHAR(')')

	PORT_MODIFY("X2")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(U'ö') PORT_CHAR(U'Ö')

	PORT_MODIFY("X3")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)  PORT_CHAR(',') PORT_CHAR(';')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)   PORT_CHAR('.') PORT_CHAR(':')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)  PORT_CHAR('-') PORT_CHAR('_')

	PORT_MODIFY("X4")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('@') PORT_CHAR(U'*') // (actually to the left of the return key on the ASDF row)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('\'') PORT_CHAR('`')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)      PORT_CHAR('0') PORT_CHAR('=')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)  PORT_CHAR('+') PORT_CHAR('?')

	PORT_MODIFY("X5")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('<') PORT_CHAR('>')  // actually the key between left shift and Z
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR(U'å') PORT_CHAR(U'Å')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(U'¯') PORT_CHAR('^') // ¯ was eventually replaced with ~ when the Apple IIgs came out - TBD if any later IIe models have it

	PORT_MODIFY("X6")
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)  PORT_CHAR(U'ä') PORT_CHAR(U'Ä')
INPUT_PORTS_END

static INPUT_PORTS_START( apple2ese_common )
	PORT_INCLUDE( apple2ese_sysconfig )
	PORT_INCLUDE( apple2e_iso_se_kbd )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2cse )
	PORT_INCLUDE( apple2ese_common )
	PORT_INCLUDE( apple2c )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2ese )
	PORT_INCLUDE( apple2ese_common )
	PORT_INCLUDE( apple2e_special )
	PORT_INCLUDE( apple2_sysconfig_accel )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2ees_sysconfig )
	PORT_START("kbd_lang_select")
	PORT_CONFNAME(0xff, 0x00, "Keyboard")
	PORT_CONFSETTING(0x00, "Spanish")
	PORT_CONFSETTING(0x12, "US English")
INPUT_PORTS_END

static INPUT_PORTS_START( apple2e_iso_es_kbd )
	PORT_INCLUDE( apple2e_iso_us_kbd )

	PORT_MODIFY("X0")
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)  PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)  PORT_CHAR('3') PORT_CHAR(U'£')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)  PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)  PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)  PORT_CHAR('7') PORT_CHAR('/')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)  PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)  PORT_CHAR('9') PORT_CHAR(')')

	PORT_MODIFY("X2")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(U'ñ') PORT_CHAR(U'Ñ')

	PORT_MODIFY("X3")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)  PORT_CHAR(',') PORT_CHAR(';')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)   PORT_CHAR('.') PORT_CHAR(':')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)  PORT_CHAR('-') PORT_CHAR('_')

	PORT_MODIFY("X4")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR(U'º') PORT_CHAR(U'§')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('`') PORT_CHAR(U'¿')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)      PORT_CHAR('0') PORT_CHAR('=')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)  PORT_CHAR('\'') PORT_CHAR('?')

	PORT_MODIFY("X5")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('<') PORT_CHAR('>')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('~') PORT_CHAR('^')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('+') PORT_CHAR('*')

	PORT_MODIFY("X6")
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)  PORT_CHAR(U'ç') PORT_CHAR(U'¡')
INPUT_PORTS_END

static INPUT_PORTS_START( apple2ees_common )
	PORT_INCLUDE( apple2ees_sysconfig )
	PORT_INCLUDE( apple2e_iso_es_kbd )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2ees )
	PORT_INCLUDE( apple2ees_common )
	PORT_INCLUDE( apple2e_special )
	PORT_INCLUDE( apple2_sysconfig_accel )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2efr_sysconfig )
	PORT_START("kbd_lang_select")
	PORT_CONFNAME(0xff, 0x00, "Keyboard")
	PORT_CONFSETTING(0x00, "French")
	PORT_CONFSETTING(0x12, "US English")
INPUT_PORTS_END

static INPUT_PORTS_START( apple2e_iso_fr_kbd )   // French AZERTY keyboard (Apple uses the Belgian AZERTY layout in France also)
	PORT_INCLUDE( apple2e_iso_us_kbd )

	PORT_MODIFY("X0")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)  PORT_CHAR('&')  PORT_CHAR('1')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)  PORT_CHAR(U'é') PORT_CHAR('2')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)  PORT_CHAR('\"') PORT_CHAR('3')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)  PORT_CHAR('\'') PORT_CHAR('4')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)  PORT_CHAR('(')  PORT_CHAR('5')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)  PORT_CHAR(U'§') PORT_CHAR('6')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)  PORT_CHAR(U'è') PORT_CHAR('7')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)  PORT_CHAR('!')  PORT_CHAR('8')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)  PORT_CHAR(U'ç') PORT_CHAR('9')

	PORT_MODIFY("X1")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)  PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)  PORT_CHAR('z') PORT_CHAR('Z')

	PORT_MODIFY("X2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)  PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR('m') PORT_CHAR('M')

	PORT_MODIFY("X3")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)  PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)  PORT_CHAR(',') PORT_CHAR('?')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)  PORT_CHAR(';') PORT_CHAR('.')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)   PORT_CHAR(':') PORT_CHAR('/')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)  PORT_CHAR('=') PORT_CHAR('+')

	PORT_MODIFY("X4")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('`') PORT_CHAR(U'£') // (actually to the left of the return key on the QSDF row)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)      PORT_CHAR(U'à') PORT_CHAR('0')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)  PORT_CHAR(')') PORT_CHAR(U'°')

	PORT_MODIFY("X5")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('<') PORT_CHAR('>')  // actually the key between left shift and W
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('^') PORT_CHAR(U'¨')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('$') PORT_CHAR('*')

	PORT_MODIFY("X6")
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)  PORT_CHAR(U'ù') PORT_CHAR('%')
INPUT_PORTS_END

static INPUT_PORTS_START( apple2efr_common )
	PORT_INCLUDE( apple2efr_sysconfig )
	PORT_INCLUDE( apple2e_iso_fr_kbd )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2cfr )
	PORT_INCLUDE( apple2efr_common )
	PORT_INCLUDE( apple2c )
	PORT_INCLUDE( apple2e_special_sl )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2efr )
	PORT_INCLUDE( apple2efr_common )
	PORT_INCLUDE( apple2e_special )
	PORT_INCLUDE( apple2_sysconfig_accel )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2eefr )
	PORT_INCLUDE( apple2efr_common )
	PORT_INCLUDE( apple2e_special )
	PORT_INCLUDE( apple2e_special_sl )
	PORT_INCLUDE( apple2_sysconfig_accel )
INPUT_PORTS_END

INPUT_PORTS_START( apple2ep_keypad ) // must be included after including the main Apple IIe keyboard
	PORT_MODIFY("X4")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD)   PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD))
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)   PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)   PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)   PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)   PORT_CHAR(UCHAR_MAMEKEY(3_PAD))

	PORT_MODIFY("X5")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Keypad Esc") PORT_CODE(KEYCODE_NUMLOCK)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)   PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)   PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)   PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)   PORT_CHAR(UCHAR_MAMEKEY(7_PAD))

	PORT_MODIFY("X6")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK)    PORT_CHAR(UCHAR_MAMEKEY(ASTERISK))
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)       PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)       PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD)     PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD)    PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))

	PORT_MODIFY("X7")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD)   PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD)   PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
INPUT_PORTS_END

static INPUT_PORTS_START( apple2epus )
	PORT_INCLUDE( apple2eus )
	PORT_INCLUDE( apple2ep_keypad )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2epuk )
	PORT_INCLUDE( apple2euk )
	PORT_INCLUDE( apple2ep_keypad )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2epde )
	PORT_INCLUDE( apple2ede )
	PORT_INCLUDE( apple2ep_keypad )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2epse )
	PORT_INCLUDE( apple2ese )
	PORT_INCLUDE( apple2ep_keypad )
INPUT_PORTS_END

static INPUT_PORTS_START( apple2epfr )
	PORT_INCLUDE( apple2eefr )
	PORT_INCLUDE( apple2ep_keypad )
INPUT_PORTS_END

static void apple2eaux_cards(device_slot_interface &device)
{
	device.option_add("std80", A2EAUX_STD80COL); // Apple IIe Standard 80 Column Card
	device.option_add("ext80", A2EAUX_EXT80COL); // Apple IIe Extended 80 Column Card
	device.option_add("rw3", A2EAUX_RAMWORKS3);  // Applied Engineering RamWorks III
}

void apple2e_state::apple2e_common(machine_config &config, bool enhanced, bool rgb_option)
{
	/* basic machine hardware */
	if (enhanced)
	{
		W65C02(config, m_maincpu, 1021800);
	}
	else
	{
		M6502(config, m_maincpu, 1021800);
	}
	m_maincpu->set_addrmap(AS_PROGRAM, &apple2e_state::base_map);
	m_maincpu->set_dasm_override(FUNC(apple2e_state::dasm_trampoline));

	TIMER(config, m_scantimer, 0).configure_scanline(FUNC(apple2e_state::apple2_interrupt), "screen", 0, 1);
	config.set_maximum_quantum(attotime::from_hz(60));

	TIMER(config, m_acceltimer, 0).configure_generic(FUNC(apple2e_state::accel_timer));

	if (rgb_option)
	{
		APPLE2_VIDEO_COMPOSITE_RGB(config, m_video, XTAL(14'318'181)).set_screen(m_screen);
	}
	else
	{
		APPLE2_VIDEO_COMPOSITE(config, m_video, XTAL(14'318'181)).set_screen(m_screen);
	}

	APPLE2_COMMON(config, m_a2common, XTAL(14'318'181));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(1021800*14, (65*7)*2, 0, (40*7)*2, 262, 0, 192);
	m_screen->set_screen_update(m_video, NAME((&a2_video_device::screen_update<a2_video_device::model::IIE, false, false>)));
	m_screen->set_palette(m_video);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, A2_SPEAKER_TAG).add_route(ALL_OUTPUTS, "mono", 0.4);

	/* DS1315 for no-slot clock */
	DS1216E(config, m_ds1315);

	/* RAM */
	RAM(config, m_ram).set_default_size("64K").set_default_value(0x00);

	/* keyboard controller */
	ay3600_device &kbdc(AY3600(config, "ay3600", 0));
	kbdc.x0().set_ioport("X0");
	kbdc.x1().set_ioport("X1");
	kbdc.x2().set_ioport("X2");
	kbdc.x3().set_ioport("X3");
	kbdc.x4().set_ioport("X4");
	kbdc.x5().set_ioport("X5");
	kbdc.x6().set_ioport("X6");
	kbdc.x7().set_ioport("X7");
	kbdc.x8().set_ioport("X8");
	kbdc.shift().set(FUNC(apple2e_state::ay3600_shift_r));
	kbdc.control().set(FUNC(apple2e_state::ay3600_control_r));
	kbdc.data_ready().set(FUNC(apple2e_state::ay3600_data_ready_w));
	kbdc.ako().set(FUNC(apple2e_state::ay3600_ako_w));

	/* repeat timer.  15 Hz from page 7-15 of "Understanding the Apple IIe" */
	timer_device &timer(TIMER(config, "repttmr", 0));
	timer.configure_periodic(FUNC(apple2e_state::ay3600_repeat), attotime::from_hz(15));

	/* slot devices */
	A2BUS(config, m_a2bus, 0);
	m_a2bus->set_space(m_maincpu, AS_PROGRAM);
	m_a2bus->irq_w().set(FUNC(apple2e_state::a2bus_irq_w));
	m_a2bus->nmi_w().set(FUNC(apple2e_state::a2bus_nmi_w));
	m_a2bus->inh_w().set(FUNC(apple2e_state::a2bus_inh_w));
	m_a2bus->dma_w().set_inputline(m_maincpu, INPUT_LINE_HALT);
	A2BUS_SLOT(config, "sl1", A2BUS_7M_CLOCK, m_a2bus, apple2e_cards, nullptr);
	A2BUS_SLOT(config, "sl2", A2BUS_7M_CLOCK, m_a2bus, apple2e_cards, nullptr);
	A2BUS_SLOT(config, "sl3", A2BUS_7M_CLOCK, m_a2bus, apple2e_cards, nullptr);
	A2BUS_SLOT(config, "sl4", A2BUS_7M_CLOCK, m_a2bus, apple2e_cards, "mockingboard");
	A2BUS_SLOT(config, "sl5", A2BUS_7M_CLOCK, m_a2bus, apple2e_cards, nullptr);
	A2BUS_SLOT(config, "sl6", A2BUS_7M_CLOCK, m_a2bus, apple2e_cards, "diskiing");
	A2BUS_SLOT(config, "sl7", A2BUS_7M_CLOCK, m_a2bus, apple2e_cards, nullptr);

	A2EAUXSLOT(config, m_a2eauxslot, 0);
	m_a2eauxslot->set_space(m_maincpu, AS_PROGRAM);
	m_a2eauxslot->out_irq_callback().set(FUNC(apple2e_state::a2bus_irq_w));
	m_a2eauxslot->out_nmi_callback().set(FUNC(apple2e_state::a2bus_nmi_w));
	A2EAUXSLOT_SLOT(config, "aux", m_a2eauxslot, apple2eaux_cards, "ext80");   // default to an extended 80-column card

	APPLE2_GAMEIO(config, m_gameio, apple2_gameio_device::default_options, nullptr);

	/* softlist config for baseline A2E
	By default, filter lists where possible to compatible disks for A2E */
	SOFTWARE_LIST(config, "flop_a2_clean").set_original("apple2_flop_clcracked");
	SOFTWARE_LIST(config, "flop_a2_orig").set_compatible("apple2_flop_orig").set_filter(enhanced ? "A2EE" : "A2E");
	SOFTWARE_LIST(config, "flop_a2_misc").set_compatible("apple2_flop_misc");

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
}

void apple2e_state::apple2e(machine_config &config)
{
	apple2e_common(config, false, true);
}

void apple2e_state::apple2epal(machine_config &config)
{
	apple2e(config);
	m_maincpu->set_clock(1016966);
	m_screen->set_raw(1016966 * 14, (65 * 7) * 2, 0, (40 * 7) * 2, 312, 0, 192);
}

void apple2e_state::mprof3(machine_config &config)
{
	apple2e(config);
	/* internal ram */
	m_ram->set_default_size("128K");
}

void apple2e_state::apple2ee(machine_config &config)
{
	apple2e_common(config, true, true);
}

void apple2e_state::apple2eepal(machine_config &config)
{
	apple2ee(config);
	m_maincpu->set_clock(1016966);
	m_screen->set_raw(1016966 * 14, (65 * 7) * 2, 0, (40 * 7) * 2, 312, 0, 192);
}

void apple2e_state::spectred(machine_config &config)
{
	apple2e(config);
	i8035_device &keyb_mcu(I8035(config, "keyb_mcu", XTAL(4'000'000))); /* guessed frequency */
	keyb_mcu.set_addrmap(AS_PROGRAM, &apple2e_state::spectred_keyb_map);
	m_screen->set_screen_update(m_video, NAME((&a2_video_device::screen_update<a2_video_device::model::IIE, false, true>)));

	// TODO: implement the actual interfacing to this 8035 MCU and
	//       and then remove the keyb CPU inherited from apple2e
}

void apple2e_state::tk3000(machine_config &config)
{
	apple2e(config);
	W65C02(config.replace(), m_maincpu, 1021800);
	m_maincpu->set_addrmap(AS_PROGRAM, &apple2e_state::base_map);

//  z80_device &subcpu(Z80(config, "subcpu", 1021800));    // schematics are illegible on where the clock comes from, but it *seems* to be the same as the 65C02 clock
//  subcpu.set_addrmap(AS_PROGRAM, &apple2e_state::tk3000_kbd_map);
}

void apple2e_state::apple2ep(machine_config &config)
{
	apple2ee(config);
}

void apple2e_state::apple2eppal(machine_config &config)
{
	apple2ee(config);
	m_maincpu->set_clock(1016966);
	m_screen->set_raw(1016966 * 14, (65 * 7) * 2, 0, (40 * 7) * 2, 312, 0, 192);
}

void apple2e_state::apple2c(machine_config &config)
{
	apple2e_common(config, true, false);
	subdevice<software_list_device>("flop_a2_orig")->set_filter("A2C");  // Filter list to compatible disks for this machine.

	m_maincpu->set_addrmap(AS_PROGRAM, &apple2e_state::apple2c_map);

	// IIc and friends have no cassette port
	config.device_remove(A2_CASSETTE_TAG);

	config.device_remove("sl1");   // IIc has no slots, of course :)
	config.device_remove("sl2");
	config.device_remove("sl3");
	config.device_remove("sl4");
	config.device_remove("sl5");
	config.device_remove("sl6");
	config.device_remove("sl7");

	MOS6551(config, m_acia1, 0);
	m_acia1->set_xtal(XTAL(14'318'181) / 8); // ~1.789 MHz
	m_acia1->txd_handler().set(PRINTER_PORT_TAG, FUNC(rs232_port_device::write_txd));
	m_acia1->irq_handler().set(FUNC(apple2e_state::a2bus_irq_w));

	MOS6551(config, m_acia2, 0);
	m_acia2->set_xtal(1.8432_MHz_XTAL);   // matches SSC so modem software is compatible
	m_acia2->txd_handler().set("modem", FUNC(rs232_port_device::write_txd));
	m_acia2->irq_handler().set(FUNC(apple2e_state::a2bus_irq_w));

	rs232_port_device &printer(RS232_PORT(config, PRINTER_PORT_TAG, default_rs232_devices, nullptr));
	printer.rxd_handler().set(m_acia1, FUNC(mos6551_device::write_rxd));
	printer.dcd_handler().set(m_acia1, FUNC(mos6551_device::write_dcd));
	printer.dsr_handler().set(m_acia1, FUNC(mos6551_device::write_dsr));
	printer.cts_handler().set(m_acia1, FUNC(mos6551_device::write_cts));

	rs232_port_device &modem(RS232_PORT(config, MODEM_PORT_TAG, default_rs232_devices, nullptr));
	modem.rxd_handler().set(m_acia2, FUNC(mos6551_device::write_rxd));
	modem.dcd_handler().set(m_acia2, FUNC(mos6551_device::write_dcd));
	modem.dsr_handler().set(m_acia2, FUNC(mos6551_device::write_dsr));
	modem.cts_handler().set(m_acia2, FUNC(mos6551_device::write_cts));

	// TODO: populate the IIc's other virtual slots with ONBOARD_ADD
	A2BUS_MOCKINGBOARD(config, "sl4", A2BUS_7M_CLOCK).set_onboard(m_a2bus); // Mockingboard 4C
	A2BUS_DISKIING(config, "sl6", A2BUS_7M_CLOCK).set_onboard(m_a2bus);

	config.device_remove("aux");
	config.device_remove(A2_AUXSLOT_TAG);

	m_ram->set_default_size("128K").set_extra_options("128K");
}

void apple2e_state::apple2cpal(machine_config &config)
{
	apple2c(config);
	m_maincpu->set_clock(1016966);
	m_screen->set_raw(1016966 * 14, (65 * 7) * 2, 0, (40 * 7) * 2, 312, 0, 192);
}

void apple2e_state::apple2cp(machine_config &config)
{
	apple2c(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &apple2e_state::apple2c_memexp_map);

	config.device_remove("sl4");
	config.device_remove("sl6");

	IWM(config, m_iwm, A2BUS_7M_CLOCK, 1021800*2);
	m_iwm->phases_cb().set(FUNC(apple2e_state::phases_w));
	m_iwm->sel35_cb().set(FUNC(apple2e_state::sel35_w));
	m_iwm->devsel_cb().set(FUNC(apple2e_state::devsel_w));

	applefdintf_device::add_525(config, m_floppy[0]);
	applefdintf_device::add_525(config, m_floppy[1]);
	applefdintf_device::add_35(config, m_floppy[2]);
	applefdintf_device::add_35(config, m_floppy[3]);

	m_ram->set_default_size("128K").set_extra_options("128K, 384K, 640K, 896K, 1152K");
}

void apple2e_state::apple2c_iwm(machine_config &config)
{
	apple2c(config);

	A2BUS_IWM(config.replace(), "sl6", A2BUS_7M_CLOCK).set_onboard(m_a2bus);
}

void apple2e_state::apple2c_iwm_pal(machine_config &config)
{
	apple2c_iwm(config);
	m_maincpu->set_clock(1016966);
	m_screen->set_raw(1016966 * 14, (65 * 7) * 2, 0, (40 * 7) * 2, 312, 0, 192);
}

void apple2e_state::apple2c_mem(machine_config &config)
{
	apple2c(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &apple2e_state::apple2c_memexp_map);

	A2BUS_IWM(config.replace(), "sl6", A2BUS_7M_CLOCK).set_onboard(m_a2bus);

	m_ram->set_default_size("128K").set_extra_options("128K, 384K, 640K, 896K, 1152K");
}

void apple2e_state::apple2c_mem_pal(machine_config &config)
{
	apple2c_mem(config);
	m_maincpu->set_clock(1016966);
	m_screen->set_raw(1016966 * 14, (65 * 7) * 2, 0, (40 * 7) * 2, 312, 0, 192);
}

void apple2e_state::laser128(machine_config &config)
{
	apple2c(config);
	W65C02(config.replace(), m_maincpu, 1021800);
	m_maincpu->set_addrmap(AS_PROGRAM, &apple2e_state::laser128_map);
	m_maincpu->set_dasm_override(FUNC(apple2e_state::dasm_trampoline));

	m_screen->set_screen_update(m_video, NAME((&a2_video_device::screen_update<a2_video_device::model::IIE, true, false>)));

	IWM(config, m_iwm, A2BUS_7M_CLOCK, 1021800 * 2);
	m_iwm->phases_cb().set(FUNC(apple2e_state::phases_w));
	m_iwm->devsel_cb().set(FUNC(apple2e_state::devsel_w));

	applefdintf_device::add_525(config, m_floppy[0]);
	applefdintf_device::add_525(config, m_floppy[1]);

	config.device_remove("sl4");
	config.device_remove("sl6");

	A2BUS_LASER128(config, "sl1", A2BUS_7M_CLOCK).set_onboard(m_a2bus);
	A2BUS_LASER128(config, "sl2", A2BUS_7M_CLOCK).set_onboard(m_a2bus);
	A2BUS_LASER128(config, "sl3", A2BUS_7M_CLOCK).set_onboard(m_a2bus);
	A2BUS_LASER128(config, "sl4", A2BUS_7M_CLOCK).set_onboard(m_a2bus);
	A2BUS_SLOT(config, "sl5", A2BUS_7M_CLOCK, m_a2bus, apple2e_cards, nullptr);
	A2BUS_LASER128(config, "sl6", A2BUS_7M_CLOCK).set_onboard(m_a2bus);
	A2BUS_SLOT(config, "sl7", A2BUS_7M_CLOCK, m_a2bus, apple2e_cards, nullptr);

	CENTRONICS(config, m_printer_conn, centronics_devices, "printer");
	m_printer_conn->busy_handler().set(FUNC(apple2e_state::busy_w));
	OUTPUT_LATCH(config, m_printer_out);
	m_printer_conn->set_output_latch(*m_printer_out);

	m_ram->set_default_size("128K").set_extra_options("128K, 384K, 640K, 896K, 1152K");
}

void apple2e_state::laser128o(machine_config &config)
{
	apple2c(config);
	W65C02(config.replace(), m_maincpu, 1021800);
	m_maincpu->set_addrmap(AS_PROGRAM, &apple2e_state::laser128_map);
	m_maincpu->set_dasm_override(FUNC(apple2e_state::dasm_trampoline));

	m_screen->set_screen_update(m_video, NAME((&a2_video_device::screen_update<a2_video_device::model::IIE, true, false>)));

	IWM(config, m_iwm, A2BUS_7M_CLOCK, 1021800 * 2);
	m_iwm->phases_cb().set(FUNC(apple2e_state::phases_w));
	m_iwm->devsel_cb().set(FUNC(apple2e_state::devsel_w));

	applefdintf_device::add_525(config, m_floppy[0]);
	applefdintf_device::add_525(config, m_floppy[1]);

	config.device_remove("sl4");
	config.device_remove("sl6");

	A2BUS_LASER128_ORIG(config, "sl1", A2BUS_7M_CLOCK).set_onboard(m_a2bus);
	A2BUS_LASER128_ORIG(config, "sl2", A2BUS_7M_CLOCK).set_onboard(m_a2bus);
	A2BUS_LASER128_ORIG(config, "sl3", A2BUS_7M_CLOCK).set_onboard(m_a2bus);
	A2BUS_LASER128_ORIG(config, "sl4", A2BUS_7M_CLOCK).set_onboard(m_a2bus);
	A2BUS_SLOT(config, "sl5", A2BUS_7M_CLOCK, m_a2bus, apple2e_cards, nullptr);
	A2BUS_LASER128_ORIG(config, "sl6", A2BUS_7M_CLOCK).set_onboard(m_a2bus);
	A2BUS_SLOT(config, "sl7", A2BUS_7M_CLOCK, m_a2bus, apple2e_cards, nullptr);

	CENTRONICS(config, m_printer_conn, centronics_devices, "printer");
	m_printer_conn->busy_handler().set(FUNC(apple2e_state::busy_w));
	OUTPUT_LATCH(config, m_printer_out);
	m_printer_conn->set_output_latch(*m_printer_out);

	// original Laser 128 doesn't have the Slinky memory expansion
	m_ram->set_default_size("128K").set_extra_options("128K");
}

void apple2e_state::laser128ex2(machine_config &config)
{
	apple2c(config);
	W65C02(config.replace(), m_maincpu, 1021800);
	m_maincpu->set_addrmap(AS_PROGRAM, &apple2e_state::laser128_map);
	m_maincpu->set_dasm_override(FUNC(apple2e_state::dasm_trampoline));

	m_screen->set_screen_update(m_video, NAME((&a2_video_device::screen_update<a2_video_device::model::IIE, true, false>)));

	IWM(config, m_iwm, A2BUS_7M_CLOCK, 1021800 * 2);
	m_iwm->phases_cb().set(FUNC(apple2e_state::phases_w));
	m_iwm->devsel_cb().set(FUNC(apple2e_state::devsel_w));

	applefdintf_device::add_525(config, m_floppy[0]);
	applefdintf_device::add_525(config, m_floppy[1]);

	config.device_remove("sl4");
	config.device_remove("sl6");

	A2BUS_LASER128(config, "sl1", A2BUS_7M_CLOCK).set_onboard(m_a2bus);
	A2BUS_LASER128(config, "sl2", A2BUS_7M_CLOCK).set_onboard(m_a2bus);
	A2BUS_LASER128(config, "sl3", A2BUS_7M_CLOCK).set_onboard(m_a2bus);
	A2BUS_LASER128(config, "sl4", A2BUS_7M_CLOCK).set_onboard(m_a2bus);
	A2BUS_LASER128(config, "sl5", A2BUS_7M_CLOCK).set_onboard(m_a2bus);
	A2BUS_LASER128(config, "sl6", A2BUS_7M_CLOCK).set_onboard(m_a2bus);
	A2BUS_LASER128(config, "sl7", A2BUS_7M_CLOCK).set_onboard(m_a2bus);

	CENTRONICS(config, m_printer_conn, centronics_devices, "printer");
	m_printer_conn->busy_handler().set(FUNC(apple2e_state::busy_w));
	OUTPUT_LATCH(config, m_printer_out);
	m_printer_conn->set_output_latch(*m_printer_out);

	m_ram->set_default_size("128K").set_extra_options("128K, 384K, 640K, 896K, 1152K");
}

void apple2e_state::ace500(machine_config &config)
{
	apple2e_common(config, true, false);
	subdevice<software_list_device>("flop_a2_orig")->set_filter("A2C");  // Filter list to compatible disks for this machine.

	W65C02(config.replace(), m_maincpu, 1021800);
	m_maincpu->set_addrmap(AS_PROGRAM, &apple2e_state::ace500_map);
	m_maincpu->set_dasm_override(FUNC(apple2e_state::dasm_trampoline));

	m_screen->set_screen_update(m_video, NAME((&a2_video_device::screen_update<a2_video_device::model::IIE, true, true>)));

	CENTRONICS(config, m_printer_conn, centronics_devices, "printer");
	m_printer_conn->busy_handler().set(FUNC(apple2e_state::busy_w));
	OUTPUT_LATCH(config, m_printer_out);
	m_printer_conn->set_output_latch(*m_printer_out);

	MOS6551(config, m_acia1, 0);
	m_acia1->set_xtal(1.8432_MHz_XTAL);
	m_acia1->txd_handler().set(MODEM_PORT_TAG, FUNC(rs232_port_device::write_txd));
	m_acia1->irq_handler().set(FUNC(apple2e_state::a2bus_irq_w));

	rs232_port_device &modem(RS232_PORT(config, MODEM_PORT_TAG, default_rs232_devices, nullptr));
	modem.rxd_handler().set(m_acia1, FUNC(mos6551_device::write_rxd));
	modem.dcd_handler().set(m_acia1, FUNC(mos6551_device::write_dcd));
	modem.dsr_handler().set(m_acia1, FUNC(mos6551_device::write_dsr));
	modem.cts_handler().set(m_acia1, FUNC(mos6551_device::write_cts));

	config.device_remove(A2_CASSETTE_TAG);
	config.device_remove("sl1");
	config.device_remove("sl2");
	config.device_remove("sl3");
	config.device_remove("sl4");
	config.device_remove("sl5");
	config.device_remove("sl6");
	config.device_remove("sl7");
	config.device_remove("aux");

	A2BUS_IWM(config, "sl6", A2BUS_7M_CLOCK).set_onboard(m_a2bus);

	A2EAUX_FRANKLIN384(config, "aux", A2BUS_7M_CLOCK).set_onboard(m_a2eauxslot);

	m_ram->set_default_size("128K");
}

void apple2e_state::ace2200(machine_config &config)
{
	apple2e_common(config, false, false);
	W65C02(config.replace(), m_maincpu, 1021800);
	m_maincpu->set_addrmap(AS_PROGRAM, &apple2e_state::ace2200_map);
	m_maincpu->set_dasm_override(FUNC(apple2e_state::dasm_trampoline));

	m_screen->set_screen_update(m_video, NAME((&a2_video_device::screen_update<a2_video_device::model::IIE, true, true>)));

	// The Ace 2000 series has 3 physical slots, 2, 4/7, and 5.
	// 4/7 can be slot 4 or 7 via a jumper; we fix it to slot 7 here
	// because that's most useful (for e.g. cffa202).
	config.device_remove("sl1");
	config.device_remove("sl3");
	config.device_remove("sl4");
	config.device_remove("sl5");
	config.device_remove("sl6");

	A2BUS_ACE2X00_SLOT1(config, "sl1", A2BUS_7M_CLOCK).set_onboard(m_a2bus);
	A2BUS_SLOT(config, "sl5", A2BUS_7M_CLOCK, m_a2bus, apple2e_cards, "mockingboard");
	A2BUS_ACE2X00_SLOT6(config, "sl6", A2BUS_7M_CLOCK).set_onboard(m_a2bus);

	config.device_remove("aux");
	A2EAUX_FRANKLIN512(config, "aux", A2BUS_7M_CLOCK).set_onboard(m_a2eauxslot);

	CENTRONICS(config, m_printer_conn, centronics_devices, "printer");
	m_printer_conn->busy_handler().set(FUNC(apple2e_state::busy_w));
	OUTPUT_LATCH(config, m_printer_out);
	m_printer_conn->set_output_latch(*m_printer_out);

	m_ram->set_default_size("128K");
}

void apple2e_state::cec(machine_config &config)
{
	apple2e_common(config, false, false);

	config.device_remove("sl3");
	config.device_remove("sl6");

	A2BUS_DISKIING(config, "sl6", A2BUS_7M_CLOCK).set_onboard(m_a2bus);

	SOFTWARE_LIST(config, "flop_cec").set_original("cecflop");

	// there is no aux slot, the "aux" side of the //e is used for additional ROM
	config.device_remove("aux");
	config.device_remove(A2_AUXSLOT_TAG);

	m_ram->set_default_size("64K");
}

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

  Game driver(s)

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


ROM_START(apple2e)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD ( "342-0133-a.chr", 0x0000, 0x1000,CRC(b081df66) SHA1(7060de104046736529c1e8a687a0dd7b84f8c51b))
	ROM_LOAD ( "342-0133-a.chr", 0x1000, 0x1000,CRC(b081df66) SHA1(7060de104046736529c1e8a687a0dd7b84f8c51b))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD ( "342-0135-b.64", 0x0000, 0x2000, CRC(e248835e) SHA1(523838c19c79f481fa02df56856da1ec3816d16e))
	ROM_LOAD ( "342-0134-a.64", 0x2000, 0x2000, CRC(fc3d59d8) SHA1(8895a4b703f2184b673078f411f4089889b61c54))

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "342-0132-c.e12", 0x000, 0x800, CRC(e47045f4) SHA1(12a2e718f5f4acd69b6c33a45a4a940b1440a481) ) // 1983 US-Dvorak
ROM_END

ROM_START(apple2euk)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "341-0160-a.chr", 0x0000, 0x2000, CRC(9be77112) SHA1(48aafa9a72002c495bc1f3d28150630ff89ca47e) )

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD ( "342-0135-b.64", 0x0000, 0x2000, CRC(e248835e) SHA1(523838c19c79f481fa02df56856da1ec3816d16e))
	ROM_LOAD ( "342-0134-a.64", 0x2000, 0x2000, CRC(fc3d59d8) SHA1(8895a4b703f2184b673078f411f4089889b61c54))

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "341-0150-a.e12", 0x000, 0x800, CRC(66ffacd7) SHA1(47bb9608be38ff75429a989b930a93b47099648e) )
ROM_END

ROM_START(apple2ede)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("341-0161-a.e9", 0x0000, 0x2000, CRC(0862a145) SHA1(a6cca6c569dba80aeb789c9cb7292f93dab00c29))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD ( "342-0135-b.64", 0x0000, 0x2000, CRC(e248835e) SHA1(523838c19c79f481fa02df56856da1ec3816d16e))
	ROM_LOAD ( "342-0134-a.64", 0x2000, 0x2000, CRC(fc3d59d8) SHA1(8895a4b703f2184b673078f411f4089889b61c54))

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD("341-0151-a.f12", 0x000, 0x800, CRC(64574bb4) SHA1(c44809bbb017bfe3c07dc99e87a3a9fa7b9741c3))
ROM_END

ROM_START(apple2ese)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("341-0162-a.e9", 0x0000, 0x2000, CRC(d0a2e9e1) SHA1(78afd70b39cc151a2c84a36058b1d5d48e6c36fa))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD ( "342-0135-b.64", 0x0000, 0x2000, CRC(e248835e) SHA1(523838c19c79f481fa02df56856da1ec3816d16e))
	ROM_LOAD ( "342-0134-a.64", 0x2000, 0x2000, CRC(fc3d59d8) SHA1(8895a4b703f2184b673078f411f4089889b61c54))

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD("341-0152-a.f12", 0x000, 0x800, CRC(ace44a35) SHA1(a035ab94339fc9ee78296cd2a6c3823925c8f53b))
ROM_END

ROM_START(apple2efr)
	ROM_REGION(0x2000, "gfx1", 0)
	ROM_LOAD("341-0163-a.e9", 0x0000, 0x2000, BAD_DUMP CRC(1824d614) SHA1(a513bec3e44b8e823465720f3db6ee3f7781c1cd)) // created from "342-0274-a.e9"

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("342-0135-b.64", 0x0000, 0x2000, CRC(e248835e) SHA1(523838c19c79f481fa02df56856da1ec3816d16e))
	ROM_LOAD("342-0134-a.64", 0x2000, 0x2000, CRC(fc3d59d8) SHA1(8895a4b703f2184b673078f411f4089889b61c54))

	ROM_REGION(0x800, "keyboard", ROMREGION_ERASE00)
	ROM_LOAD("342-0153-a.f12", 0x000, 0x800, CRC(cbf1bbe7) SHA1(d1628c911ec4a0ff583f7346d3c38dc82023263e))
ROM_END

ROM_START(apple2ees)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "341-0212-a.e9", 0x000000, 0x002000, CRC(bc5575ef) SHA1(aa20c257255ef552295d32a3f56ccbb52b8716c3) )

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD ( "342-0135-b.64", 0x0000, 0x2000, CRC(e248835e) SHA1(523838c19c79f481fa02df56856da1ec3816d16e))
	ROM_LOAD ( "342-0134-a.64", 0x2000, 0x2000, CRC(fc3d59d8) SHA1(8895a4b703f2184b673078f411f4089889b61c54))

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "341-0211-a.f12", 0x000000, 0x000800, CRC(fac15d54) SHA1(abe019de22641b0647e829a1d4745759bdffe86a) )
ROM_END

ROM_START(mprof3)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD ( "mpf3.chr", 0x0000, 0x1000,CRC(2597bc19) SHA1(e114dcbb512ec24fb457248c1b53cbd78039ed20))
	ROM_LOAD ( "mpf3.chr", 0x1000, 0x1000,CRC(2597bc19) SHA1(e114dcbb512ec24fb457248c1b53cbd78039ed20))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD ( "mpf3-cd.rom", 0x0000, 0x2000, CRC(5b662e06) SHA1(aa0db775ca78986480829fcc10f00e57629e1a7c))
	ROM_LOAD ( "mpf3-ef.rom", 0x2000, 0x2000, CRC(2c5e8b92) SHA1(befeb03e04b7c3ef36ef5829948a53880df85e92))

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "342-0132-c.e12", 0x000, 0x800, BAD_DUMP CRC(e47045f4) SHA1(12a2e718f5f4acd69b6c33a45a4a940b1440a481) ) // need to dump real mprof keyboard ROM
ROM_END

ROM_START(apple2ee)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD ( "342-0265-a.chr", 0x0000, 0x1000,CRC(2651014d) SHA1(b2b5d87f52693817fc747df087a4aa1ddcdb1f10))
	ROM_LOAD ( "342-0265-a.chr", 0x1000, 0x1000,CRC(2651014d) SHA1(b2b5d87f52693817fc747df087a4aa1ddcdb1f10))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD ( "342-0304-a.e10", 0x0000, 0x2000, CRC(443aa7c4) SHA1(3aecc56a26134df51e65e17f33ae80c1f1ac93e6)) /* PCB: "CD ROM // 342-0304", 2364 mask rom */
	ROM_LOAD ( "342-0303-a.e8", 0x2000, 0x2000, CRC(95e10034) SHA1(afb09bb96038232dc757d40c0605623cae38088e)) /* PCB: "EF ROM // 342-0303", 2364 mask rom */

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD( "341-0132-d.e12", 0x000, 0x800, CRC(c506efb9) SHA1(8e14e85c645187504ec9d162b3ea614a0c421d32) )
ROM_END

ROM_START(apple2eeuk)
	ROM_REGION(0x2000, "gfx1", 0)
	ROM_LOAD( "342-0273-a.chr", 0x0000, 0x2000, CRC(9157085a) SHA1(85479a509d6c8176949a5b20720567b7022aa631) )

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD ( "342-0304-a.e10", 0x0000, 0x2000, CRC(443aa7c4) SHA1(3aecc56a26134df51e65e17f33ae80c1f1ac93e6)) /* PCB: "CD ROM // 342-0304", 2364 mask rom */
	ROM_LOAD ( "342-0303-a.e8", 0x2000, 0x2000, CRC(95e10034) SHA1(afb09bb96038232dc757d40c0605623cae38088e)) /* PCB: "EF ROM // 342-0303", 2364 mask rom */

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD( "341-0150-a.e12", 0x000, 0x800, CRC(66ffacd7) SHA1(47bb9608be38ff75429a989b930a93b47099648e) )
ROM_END

ROM_START(apple2eede)
	ROM_REGION(0x2000, "gfx1", 0)
	ROM_LOAD("342-0275-a.e9", 0x0000, 0x2000, CRC(dac59882) SHA1(882ee6921f0d5d6615cf7c52881dda2f319c2696))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD ( "342-0304-a.e10", 0x0000, 0x2000, CRC(443aa7c4) SHA1(3aecc56a26134df51e65e17f33ae80c1f1ac93e6)) /* PCB: "CD ROM // 342-0304", 2364 mask rom */
	ROM_LOAD ( "342-0303-a.e8", 0x2000, 0x2000, CRC(95e10034) SHA1(afb09bb96038232dc757d40c0605623cae38088e)) /* PCB: "EF ROM // 342-0303", 2364 mask rom */

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD("341-0151-a.f12", 0x000, 0x800, CRC(64574bb4) SHA1(c44809bbb017bfe3c07dc99e87a3a9fa7b9741c3))
ROM_END

ROM_START(apple2eese)
	ROM_REGION(0x2000, "gfx1", 0)
	ROM_LOAD("341-0328-a.e9", 0x0000, 0x2000, BAD_DUMP CRC(04b3936d) SHA1(b036b07459f119bd5777ceaf3ae9dec6add91f33))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD ( "342-0304-a.e10", 0x0000, 0x2000, CRC(443aa7c4) SHA1(3aecc56a26134df51e65e17f33ae80c1f1ac93e6)) /* PCB: "CD ROM // 342-0304", 2364 mask rom */
	ROM_LOAD ( "342-0303-a.e8", 0x2000, 0x2000, CRC(95e10034) SHA1(afb09bb96038232dc757d40c0605623cae38088e)) /* PCB: "EF ROM // 342-0303", 2364 mask rom */

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD("341-0152-a.f12", 0x000, 0x800, CRC(ace44a35) SHA1(a035ab94339fc9ee78296cd2a6c3823925c8f53b))
ROM_END

ROM_START(apple2eefr)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "342-0274-a.e9", 0x0000, 0x2000, CRC(8f342081) SHA1(c81c1bbf237e70f8c3e5eef3c8fd5bd9b9f54d1e) )

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD( "342-0304-a.e3", 0x0000, 0x2000, CRC(443aa7c4) SHA1(3aecc56a26134df51e65e17f33ae80c1f1ac93e6) )
	ROM_LOAD( "342-0303-a.e5", 0x2000, 0x2000, CRC(95e10034) SHA1(afb09bb96038232dc757d40c0605623cae38088e) )

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "342-0326-a.f12", 0x0000, 0x0800, BAD_DUMP CRC(f04970a9) SHA1(806a602195c18ffec637c03b6bb3405188b0dc1e) ) // merged French half of 341-0326-a.f12 with QWERTY 341-0150-a.e12
ROM_END

ROM_START(apple2ep)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD ( "342-0265-a.chr", 0x0000, 0x1000,CRC(2651014d) SHA1(b2b5d87f52693817fc747df087a4aa1ddcdb1f10))
	ROM_LOAD ( "342-0265-a.chr", 0x1000, 0x1000,CRC(2651014d) SHA1(b2b5d87f52693817fc747df087a4aa1ddcdb1f10))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD ("32-0349-b.128", 0x0000, 0x4000, CRC(1d70b193) SHA1(b8ea90abe135a0031065e01697c4a3a20d51198b)) /* should rom name be 342-0349-b? */

	ROM_REGION( 0x800, "keyboard", 0 )
	// chip printed markings say 342-0132-d, but internally text says "341-0132-d".  Go figure.
	ROM_LOAD( "341-0132-d.e12", 0x000, 0x800, CRC(c506efb9) SHA1(8e14e85c645187504ec9d162b3ea614a0c421d32) )
ROM_END

ROM_START(apple2epuk)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("342-0273-a.chr", 0x0000, 0x2000, CRC(9157085a) SHA1(85479a509d6c8176949a5b20720567b7022aa631))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("32-0349-b.128", 0x0000, 0x4000, CRC(1d70b193) SHA1(b8ea90abe135a0031065e01697c4a3a20d51198b)) /* should rom name be 342-0349-b? */

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD("341-0150-a.e12", 0x000, 0x800, CRC(66ffacd7) SHA1(47bb9608be38ff75429a989b930a93b47099648e))
ROM_END

ROM_START(apple2epde)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("342-0275-a.e9", 0x0000, 0x2000, CRC(dac59882) SHA1(882ee6921f0d5d6615cf7c52881dda2f319c2696))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("32-0349-b.128", 0x0000, 0x4000, CRC(1d70b193) SHA1(b8ea90abe135a0031065e01697c4a3a20d51198b)) /* should rom name be 342-0349-b? */

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD("341-0151-a.f12", 0x000, 0x800, CRC(64574bb4) SHA1(c44809bbb017bfe3c07dc99e87a3a9fa7b9741c3))
ROM_END

ROM_START(apple2epse)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("341-0328-a.e9", 0x0000, 0x2000, BAD_DUMP CRC(04b3936d) SHA1(b036b07459f119bd5777ceaf3ae9dec6add91f33))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("32-0349-b.128", 0x0000, 0x4000, CRC(1d70b193) SHA1(b8ea90abe135a0031065e01697c4a3a20d51198b)) /* should rom name be 342-0349-b? */

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD("341-0152-a.f12", 0x000, 0x800, CRC(ace44a35) SHA1(a035ab94339fc9ee78296cd2a6c3823925c8f53b))
ROM_END

ROM_START(apple2epfr)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "342-0274-a.e9", 0x0000, 0x2000, CRC(8f342081) SHA1(c81c1bbf237e70f8c3e5eef3c8fd5bd9b9f54d1e) )

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("32-0349-b.128", 0x0000, 0x4000, CRC(1d70b193) SHA1(b8ea90abe135a0031065e01697c4a3a20d51198b)) /* should rom name be 342-0349-b? */

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD( "342-0326-a.f12", 0x0000, 0x0800, BAD_DUMP CRC(f04970a9) SHA1(806a602195c18ffec637c03b6bb3405188b0dc1e) ) // merged French half of 341-0326-a.f12 with QWERTY 341-0150-a.e12
ROM_END

ROM_START(apple2c)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD ( "341-0265-a.chr", 0x0000, 0x1000,CRC(2651014d) SHA1(b2b5d87f52693817fc747df087a4aa1ddcdb1f10))
	ROM_LOAD ( "341-0265-a.chr", 0x1000, 0x1000,CRC(2651014d) SHA1(b2b5d87f52693817fc747df087a4aa1ddcdb1f10))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD ( "a2c.128", 0x0000, 0x4000, CRC(f0edaa1b) SHA1(1a9b8aca5e32bb702ddb7791daddd60a89655729)) /* should be 342-0272-A? */

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "342-0132-c.e12", 0x000, 0x800, CRC(e47045f4) SHA1(12a2e718f5f4acd69b6c33a45a4a940b1440a481) ) // 1983 US-Dvorak
ROM_END

ROM_START(apple2cuk)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("342-0273-a.chr", 0x0000, 0x2000, CRC(9157085a) SHA1(85479a509d6c8176949a5b20720567b7022aa631))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("a2c.128", 0x0000, 0x4000, CRC(f0edaa1b) SHA1(1a9b8aca5e32bb702ddb7791daddd60a89655729)) /* should be 342-0272-A? */

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD("341-0150-a.e12", 0x000, 0x800, CRC(66ffacd7) SHA1(47bb9608be38ff75429a989b930a93b47099648e))
ROM_END

ROM_START(apple2cde)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("342-0275-a.e9", 0x0000, 0x2000, CRC(dac59882) SHA1(882ee6921f0d5d6615cf7c52881dda2f319c2696))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("a2c.128", 0x0000, 0x4000, CRC(f0edaa1b) SHA1(1a9b8aca5e32bb702ddb7791daddd60a89655729)) /* should be 342-0272-A? */

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD("341-0151-a.f12", 0x000, 0x800, CRC(64574bb4) SHA1(c44809bbb017bfe3c07dc99e87a3a9fa7b9741c3))
ROM_END

ROM_START(apple2cse)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("341-0328-a.e9", 0x0000, 0x2000, BAD_DUMP CRC(04b3936d) SHA1(b036b07459f119bd5777ceaf3ae9dec6add91f33))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("a2c.128", 0x0000, 0x4000, CRC(f0edaa1b) SHA1(1a9b8aca5e32bb702ddb7791daddd60a89655729)) /* should be 342-0272-A? */

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD("341-0152-a.f12", 0x000, 0x800, CRC(ace44a35) SHA1(a035ab94339fc9ee78296cd2a6c3823925c8f53b))
ROM_END

ROM_START(apple2cfr)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "342-0274-a.e9", 0x0000, 0x2000, CRC(8f342081) SHA1(c81c1bbf237e70f8c3e5eef3c8fd5bd9b9f54d1e) )

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("a2c.128", 0x0000, 0x4000, CRC(f0edaa1b) SHA1(1a9b8aca5e32bb702ddb7791daddd60a89655729)) /* should be 342-0272-A? */

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD( "342-0326-a.f12", 0x0000, 0x0800, BAD_DUMP CRC(f04970a9) SHA1(806a602195c18ffec637c03b6bb3405188b0dc1e) ) // merged French half of 341-0326-a.f12 with QWERTY 341-0150-a.e12
ROM_END

ROM_START(spectred)
		ROM_REGION(0x8000,"gfx1",0)
		ROM_LOAD ( "spm-c_ed_06-08-85.u6", 0x0000, 0x4000, CRC(a1b9ffe4) SHA1(3cb281f19f91372e24685792b7bff778944f99ed) )
		ROM_CONTINUE(0x0000, 0x4000)    // first half of this ROM is empty

		ROM_REGION(0x10000,"maincpu",0)
		// these ROMs appear to have been dumped weirdly, or are wired weirdly in the real hardware.
		// The first 0x2000 of u51 seems to be garbage
		// u50 seems to have the halves duplicated, and D000 and E000 swapped
		ROM_LOAD ( "spm-c_ed_51-09-86.u51.h", 0x0000, 0x4000, CRC(fae8d36c) SHA1(69bed61513482ccb578b89c2fb8e7ba2258e82a5))
		ROM_COPY( "maincpu", 0x2000, 0x0000, 0x1000 )
		ROM_LOAD ( "spm-c_ed_50-09-86.u50.h", 0x2000, 0x1000, CRC(1fccaf24) SHA1(1de1438ee8789f83cbc97f75c0485d1fd0f58a38))
		ROM_CONTINUE(0x1000, 0x1000)
		ROM_CONTINUE(0x4000, 0x2000)

		ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
		ROM_LOAD( "342-0132-c.e12", 0x000, 0x800, BAD_DUMP CRC(e47045f4) SHA1(12a2e718f5f4acd69b6c33a45a4a940b1440a481) ) // copied from apple2e

		ROM_REGION(0x1000, "keyb_mcu", 0)
	ROM_LOAD( "167_8980.u5", 0x0000, 0x1000, CRC(a501f197) SHA1(136c2b562999a6e340fe0e9a3776cea8c2e3647e) )
ROM_END

// unlike the very unique TK2000, the TK3000 is a mostly stock enhanced IIe clone
ROM_START(tk3000)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "tk3000.f7",    0x000000, 0x002000, CRC(70157693) SHA1(a7922e2137f95271011042441d80466fba7bb828) )

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD( "tk3000.f4f6",  0x000000, 0x004000, CRC(5b1e8ab2) SHA1(f163e5753c18ff0e812a448e8da406f102600edf) )

	ROM_REGION(0x2000, "kbdcpu", 0)
	ROM_LOAD( "tk3000.e13",   0x000000, 0x002000, CRC(f9b860d3) SHA1(6a127f1458f43a00199d3dde94569b8928f05a53) )

	ROM_REGION(0x800, "keyboard", ROMREGION_ERASE00)
	ROM_LOAD( "342-0132-c.e12", 0x000, 0x800, BAD_DUMP CRC(e47045f4) SHA1(12a2e718f5f4acd69b6c33a45a4a940b1440a481) ) // probably not this machine's actual ROM
ROM_END

ROM_START(prav8c)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD ( "charrom.d20", 0x0000, 0x2000,CRC(935212cc) SHA1(934603a441c631bd841ea0d2ff39525474461e47))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD ( "prom_cd.d46", 0x0000, 0x2000, CRC(195d0b48) SHA1(f8c4f3722159081f6950207f03bc85da30980c08))
	ROM_LOAD ( "prom_ef.d41", 0x2000, 0x2000, CRC(ec6aa2f6) SHA1(64bce893ebf0e22cd8f22436b97ef1bfeddf692f))

	// contains slot firmware for slots 1, 2, and 6 (6 is the usual Disk II f/w)
	ROM_REGION(0x2000,"unknown",0)
	ROM_LOAD ( "eprom.d38", 0x0000, 0x2000, CRC(c8d00b19) SHA1(13d156957ea68d0e7bc4be57cb1580c8b1399981))

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "342-0132-c.e12", 0x000, 0x800, BAD_DUMP CRC(e47045f4) SHA1(12a2e718f5f4acd69b6c33a45a4a940b1440a481) ) // we don't know what this machine used
ROM_END

ROM_START(apple2c0)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD ( "341-0265-a.chr", 0x0000, 0x1000,CRC(2651014d) SHA1(b2b5d87f52693817fc747df087a4aa1ddcdb1f10))
	ROM_LOAD ( "341-0265-a.chr", 0x1000, 0x1000,CRC(2651014d) SHA1(b2b5d87f52693817fc747df087a4aa1ddcdb1f10))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("3420033a.256", 0x0000, 0x8000, CRC(c8b979b3) SHA1(10767e96cc17bad0970afda3a4146564e6272ba1))

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "342-0132-c.e12", 0x000, 0x800, CRC(e47045f4) SHA1(12a2e718f5f4acd69b6c33a45a4a940b1440a481) ) // 1983 US-Dvorak
ROM_END

ROM_START(apple2c0uk)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("342-0273-a.chr", 0x0000, 0x2000, CRC(9157085a) SHA1(85479a509d6c8176949a5b20720567b7022aa631))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("3420033a.256", 0x0000, 0x8000, CRC(c8b979b3) SHA1(10767e96cc17bad0970afda3a4146564e6272ba1))

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD("341-0150-a.e12", 0x000, 0x800, CRC(66ffacd7) SHA1(47bb9608be38ff75429a989b930a93b47099648e))
ROM_END

ROM_START(apple2c0de)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("342-0275-a.e9", 0x0000, 0x2000, CRC(dac59882) SHA1(882ee6921f0d5d6615cf7c52881dda2f319c2696))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("3420033a.256", 0x0000, 0x8000, CRC(c8b979b3) SHA1(10767e96cc17bad0970afda3a4146564e6272ba1))

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD("341-0151-a.f12", 0x000, 0x800, CRC(64574bb4) SHA1(c44809bbb017bfe3c07dc99e87a3a9fa7b9741c3))
ROM_END

ROM_START(apple2c0se)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("341-0328-a.e9", 0x0000, 0x2000, BAD_DUMP CRC(04b3936d) SHA1(b036b07459f119bd5777ceaf3ae9dec6add91f33))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("3420033a.256", 0x0000, 0x8000, CRC(c8b979b3) SHA1(10767e96cc17bad0970afda3a4146564e6272ba1))

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD("341-0152-a.f12", 0x000, 0x800, CRC(ace44a35) SHA1(a035ab94339fc9ee78296cd2a6c3823925c8f53b))
ROM_END

ROM_START(apple2c0fr)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "342-0274-a.e9", 0x0000, 0x2000, CRC(8f342081) SHA1(c81c1bbf237e70f8c3e5eef3c8fd5bd9b9f54d1e) )

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("3420033a.256", 0x0000, 0x8000, CRC(c8b979b3) SHA1(10767e96cc17bad0970afda3a4146564e6272ba1))

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "342-0326-a.f12", 0x0000, 0x0800, BAD_DUMP CRC(f04970a9) SHA1(806a602195c18ffec637c03b6bb3405188b0dc1e) ) // merged French half of 341-0326-a.f12 with QWERTY 341-0150-a.e12
ROM_END

ROM_START(apple2c3)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD ( "341-0265-a.chr", 0x0000, 0x1000,CRC(2651014d) SHA1(b2b5d87f52693817fc747df087a4aa1ddcdb1f10))
	ROM_LOAD ( "341-0265-a.chr", 0x1000, 0x1000,CRC(2651014d) SHA1(b2b5d87f52693817fc747df087a4aa1ddcdb1f10))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("342-0445-a.256", 0x0000, 0x8000, CRC(bc5a79ff) SHA1(5338d9baa7ae202457b6500fde5883dbdc86e5d3))

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "342-0132-c.e12", 0x000, 0x800, CRC(e47045f4) SHA1(12a2e718f5f4acd69b6c33a45a4a940b1440a481) ) // 1983 US-Dvorak
ROM_END

ROM_START(apple2c3uk)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("342-0273-a.chr", 0x0000, 0x2000, CRC(9157085a) SHA1(85479a509d6c8176949a5b20720567b7022aa631))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("342-0445-a.256", 0x0000, 0x8000, CRC(bc5a79ff) SHA1(5338d9baa7ae202457b6500fde5883dbdc86e5d3))

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD("341-0150-a.e12", 0x000, 0x800, CRC(66ffacd7) SHA1(47bb9608be38ff75429a989b930a93b47099648e))
ROM_END

ROM_START(apple2c3de)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("342-0275-a.e9", 0x0000, 0x2000, CRC(dac59882) SHA1(882ee6921f0d5d6615cf7c52881dda2f319c2696))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("342-0445-a.256", 0x0000, 0x8000, CRC(bc5a79ff) SHA1(5338d9baa7ae202457b6500fde5883dbdc86e5d3))

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD("341-0151-a.f12", 0x000, 0x800, CRC(64574bb4) SHA1(c44809bbb017bfe3c07dc99e87a3a9fa7b9741c3))
ROM_END

ROM_START(apple2c3se)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("341-0328-a.e9", 0x0000, 0x2000, BAD_DUMP CRC(04b3936d) SHA1(b036b07459f119bd5777ceaf3ae9dec6add91f33))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("342-0445-a.256", 0x0000, 0x8000, CRC(bc5a79ff) SHA1(5338d9baa7ae202457b6500fde5883dbdc86e5d3))

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD("341-0152-a.f12", 0x000, 0x800, CRC(ace44a35) SHA1(a035ab94339fc9ee78296cd2a6c3823925c8f53b))
ROM_END

ROM_START(apple2c3fr)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "342-0274-a.e9", 0x0000, 0x2000, CRC(8f342081) SHA1(c81c1bbf237e70f8c3e5eef3c8fd5bd9b9f54d1e) )

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("342-0445-a.256", 0x0000, 0x8000, CRC(bc5a79ff) SHA1(5338d9baa7ae202457b6500fde5883dbdc86e5d3))

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "342-0326-a.f12", 0x0000, 0x0800, BAD_DUMP CRC(f04970a9) SHA1(806a602195c18ffec637c03b6bb3405188b0dc1e) ) // merged French half of 341-0326-a.f12 with QWERTY 341-0150-a.e12
ROM_END

ROM_START(apple2c4)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD ( "341-0265-a.chr", 0x0000, 0x1000,CRC(2651014d) SHA1(b2b5d87f52693817fc747df087a4aa1ddcdb1f10))
	ROM_LOAD ( "341-0265-a.chr", 0x1000, 0x1000,CRC(2651014d) SHA1(b2b5d87f52693817fc747df087a4aa1ddcdb1f10))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("3410445b.256", 0x0000, 0x8000, CRC(06f53328) SHA1(015061597c4cda7755aeb88b735994ffd2f235ca))

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "342-0132-c.e12", 0x000, 0x800, CRC(e47045f4) SHA1(12a2e718f5f4acd69b6c33a45a4a940b1440a481) ) // 1983 US-Dvorak
ROM_END

ROM_START(apple2c4uk)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("342-0273-a.chr", 0x0000, 0x2000, CRC(9157085a) SHA1(85479a509d6c8176949a5b20720567b7022aa631))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("3410445b.256", 0x0000, 0x8000, CRC(06f53328) SHA1(015061597c4cda7755aeb88b735994ffd2f235ca))

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD("341-0150-a.e12", 0x000, 0x800, CRC(66ffacd7) SHA1(47bb9608be38ff75429a989b930a93b47099648e))
ROM_END

ROM_START(apple2c4de)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("342-0275-a.e9", 0x0000, 0x2000, CRC(dac59882) SHA1(882ee6921f0d5d6615cf7c52881dda2f319c2696))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("3410445b.256", 0x0000, 0x8000, CRC(06f53328) SHA1(015061597c4cda7755aeb88b735994ffd2f235ca))

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD("341-0151-a.f12", 0x000, 0x800, CRC(64574bb4) SHA1(c44809bbb017bfe3c07dc99e87a3a9fa7b9741c3))
ROM_END

ROM_START(apple2c4se)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("341-0328-a.e9", 0x0000, 0x2000, BAD_DUMP CRC(04b3936d) SHA1(b036b07459f119bd5777ceaf3ae9dec6add91f33))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("3410445b.256", 0x0000, 0x8000, CRC(06f53328) SHA1(015061597c4cda7755aeb88b735994ffd2f235ca))

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD("341-0152-a.f12", 0x000, 0x800, CRC(ace44a35) SHA1(a035ab94339fc9ee78296cd2a6c3823925c8f53b))
ROM_END

ROM_START(apple2c4fr)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "342-0274-a.e9", 0x0000, 0x2000, CRC(8f342081) SHA1(c81c1bbf237e70f8c3e5eef3c8fd5bd9b9f54d1e) )

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("3410445b.256", 0x0000, 0x8000, CRC(06f53328) SHA1(015061597c4cda7755aeb88b735994ffd2f235ca))

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "342-0326-a.f12", 0x0000, 0x0800, BAD_DUMP CRC(f04970a9) SHA1(806a602195c18ffec637c03b6bb3405188b0dc1e) ) // merged French half of 341-0326-a.f12 with QWERTY 341-0150-a.e12
ROM_END

ROM_START(laser128)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "laser 128 video rom vt27-0706-0.bin", 0x0800, 0x0800, CRC(7884cc0f) SHA1(693a0a66191465825b8f7b5e746b463f3000e9cc) )
	ROM_CONTINUE(0x0000, 0x0800)    // international character set (how is this selected?)
	ROM_CONTINUE(0x1000, 0x1000)    // lo-res patterns, twice

	ROM_REGION(0x10000,"maincpu",0)
	ROM_SYSTEM_BIOS(0, "871222", "v4.3")
	ROMX_LOAD( "laser 128 v4.3 871222.bin", 0x000000, 0x008000, CRC(e091af13) SHA1(3232f7036a68b996fd4126d5e19e855c4d5c64df), ROM_BIOS(0) )

	ROM_SYSTEM_BIOS(1, "870917", "v4.2")
	ROMX_LOAD( "laser 128 v4.2 870917.bin", 0x000000, 0x008000, CRC(39e59ed3) SHA1(cbd2f45c923725bfd57f8548e65cc80b13bc18da), ROM_BIOS(1) )

	ROM_SYSTEM_BIOS(2, "870724", "v4.1")
	ROMX_LOAD( "laser 128 v4.1 870724.bin", 0x000000, 0x008000, CRC(ce087911) SHA1(f6dba711f0d727f1d13b0256f10ba62bde6d7f5b), ROM_BIOS(2) )

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "342-0132-c.e12", 0x000, 0x800, BAD_DUMP CRC(e47045f4) SHA1(12a2e718f5f4acd69b6c33a45a4a940b1440a481) ) // need to dump real laser rom
ROM_END

ROM_START(laser128o)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("laser 128 video rom vt27-0706-0.bin", 0x0800, 0x0800, CRC(7884cc0f) SHA1(693a0a66191465825b8f7b5e746b463f3000e9cc))
	ROM_CONTINUE(0x0000, 0x0800) // international character set (how is this selected?)
	ROM_CONTINUE(0x1000, 0x1000) // lo-res patterns, twice

	ROM_REGION(0x10000,"maincpu",0)
	ROM_SYSTEM_BIOS(0, "871212", "v3.3")
	ROMX_LOAD( "laser 128 v3.3 871212.bin", 0x000000, 0x008000, CRC(3f5deffe) SHA1(4e7195b941c51ba83d5ef16e1f78e3f62bccd8cd), ROM_BIOS(0) )

	ROM_SYSTEM_BIOS(1, "870320", "v3.0")
	ROMX_LOAD( "laser 128 v3.0 870320.bin", 0x000000, 0x008000, CRC(145d39ff) SHA1(087e992548c2e9849d4262a0eb505548f846c7f5), ROM_BIOS(1) )

	ROM_SYSTEM_BIOS(2, "870203", "v2.9")
	ROMX_LOAD( "laser 128 v2.9 870203.bin", 0x000000, 0x008000, CRC(7e12fe93) SHA1(d7be7ba05725111354e4bdbaaef620a2a8ea65f7), ROM_BIOS(2) )

	ROM_SYSTEM_BIOS(3, "860915", "860915")
	ROMX_LOAD( "laser 128 860915.bin", 0x000000, 0x008000, CRC(8d1a181d) SHA1(8fae95776c3d8a581c621cd258e118b8dfdb0cfd), ROM_BIOS(3) )

	ROM_SYSTEM_BIOS(4, "860801", "860801")
	ROMX_LOAD( "laser 128 860801.bin", 0x000000, 0x008000, CRC(a88c2fcf) SHA1(ec163bb6e7e07cb256e0ed0f8d148cf85313e9f9), ROM_BIOS(4) )

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "342-0132-c.e12", 0x000, 0x800, BAD_DUMP CRC(e47045f4) SHA1(12a2e718f5f4acd69b6c33a45a4a940b1440a481) ) // need to dump real laser rom
ROM_END

ROM_START(las128ex)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("laser 128 video rom vt27-0706-0.bin", 0x0800, 0x0800, CRC(7884cc0f) SHA1(693a0a66191465825b8f7b5e746b463f3000e9cc))
	ROM_CONTINUE(0x0000, 0x0800) // international character set (how is this selected?)
	ROM_CONTINUE(0x1000, 0x1000) // lo-res patterns, twice

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("las128ex.256", 0x0000, 0x8000, CRC(b67c8ba1) SHA1(8bd5f82a501b1cf9d988c7207da81e514ca254b0))

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "342-0132-c.e12", 0x000, 0x800, BAD_DUMP CRC(e47045f4) SHA1(12a2e718f5f4acd69b6c33a45a4a940b1440a481) ) // need to dump real laser rom
ROM_END

ROM_START(las128e2)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("laser 128 video rom vt27-0706-0.bin", 0x0800, 0x0800, CRC(7884cc0f) SHA1(693a0a66191465825b8f7b5e746b463f3000e9cc))
	ROM_CONTINUE(0x0000, 0x0800) // international character set (how is this selected?)
	ROM_CONTINUE(0x1000, 0x1000) // lo-res patterns, twice

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD( "laser 128ex2 rom version 6.1.bin", 0x000000, 0x008000, CRC(7f911c90) SHA1(125754c1bd777d4c510f5239b96178c6f2e3236b) )

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "342-0132-c.e12", 0x000, 0x800, BAD_DUMP CRC(e47045f4) SHA1(12a2e718f5f4acd69b6c33a45a4a940b1440a481) ) // need to dump real laser rom
ROM_END

ROM_START(apple2cp)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD ( "341-0265-a.chr", 0x0000, 0x1000,CRC(2651014d) SHA1(b2b5d87f52693817fc747df087a4aa1ddcdb1f10))
	ROM_LOAD ( "341-0265-a.chr", 0x1000, 0x1000,CRC(2651014d) SHA1(b2b5d87f52693817fc747df087a4aa1ddcdb1f10))

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("341-0625-a.256", 0x0000, 0x8000, CRC(0b996420) SHA1(1a27ae26966bbafd825d08ad1a24742d3e33557c))

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "341-0132-d.e12", 0x000, 0x800, CRC(c506efb9) SHA1(8e14e85c645187504ec9d162b3ea614a0c421d32) )
ROM_END

ROM_START(ceci)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "u13.9433c-0202.rcl-zh-32.bin", 0x000000, 0x001000, CRC(816a86f1) SHA1(58ad0008df72896a18601e090ee0d58155ffa5be) )
	ROM_LOAD( "u13.9433c-0202.rcl-zh-32.bin", 0x001000, 0x001000, CRC(816a86f1) SHA1(58ad0008df72896a18601e090ee0d58155ffa5be) )

	ROM_REGION(0x10000,"maincpu",0)
	ROM_SYSTEM_BIOS(0, "default", "ver 1.21")
	ROMX_LOAD( "u7.alt", 0x000000, 0x008000, CRC(23c87b3b) SHA1(ff9673f628390e9b0fb60732acc72b580200b5ae), ROM_BIOS(0) )
	ROMX_LOAD( "u35.alt", 0x008000, 0x008000, CRC(a4f40181) SHA1(db1ed69b2ed3280b1c90f76dbd637370d5bc11b0), ROM_BIOS(0) )

	ROM_SYSTEM_BIOS(1, "older", "ver 1.1")
	ROMX_LOAD( "u7.tmm24256ap.bin", 0x000000, 0x008000, CRC(2e3f73b0) SHA1(a2967b3a9a040a1c47eb0fea9a632b833cd1c060), ROM_BIOS(1) )
	ROMX_LOAD( "u35.tmm24256ap.bin", 0x008000, 0x008000, CRC(7f3ee968) SHA1(f4fd7ceda4c9ab9bc626d6abb76b7fd2b5faf2da), ROM_BIOS(1) )

	ROM_SYSTEM_BIOS(2, "diag", "self test")
	ROMX_LOAD( "u7.selftest.bin", 0x000000, 0x008000, CRC(4b045a44) SHA1(d2c716d0eb9f1c70e108d16c6a88d44b894e39fd), ROM_BIOS(2) )
	ROMX_LOAD( "u35.tmm24256ap.bin", 0x008000, 0x008000, CRC(7f3ee968) SHA1(f4fd7ceda4c9ab9bc626d6abb76b7fd2b5faf2da), ROM_BIOS(2) )

	ROM_REGION(0x40000,"cecexp",0)
	ROM_LOAD16_BYTE( "u33.mx231024-0059.bin", 0x000000, 0x020000, CRC(a2a59f35) SHA1(01c787e150bf1378088a9333ec9338387aae0f50) )
	ROM_LOAD16_BYTE( "u34.mx231024-0060.bin", 0x000001, 0x020000, CRC(f23470ce) SHA1(dc4cbe19e202d2afb56998ff04255b3171b58e14) )

	ROM_REGION(0x800,"keyboard",0)
	ROM_LOAD( "u26.9433c-0201.rcl-zh-16.bin", 0x000000, 0x000800, CRC(f3190603) SHA1(7efdf6f4ee0ed01ff06341c601496a43d06afd6b) )

	ROM_REGION(0x100,"slot6",0)
	ROM_LOAD( "u40.m2822.bin", 0x000000, 0x000100, CRC(b72a2c70) SHA1(bc39fbd5b9a8d2287ac5d0a42e639fc4d3c2f9d4) )
ROM_END

ROM_START(cece)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "u13.9433c-0202.rcl-zh-32.bin", 0x000000, 0x001000, CRC(816a86f1) SHA1(58ad0008df72896a18601e090ee0d58155ffa5be) )
	ROM_LOAD( "u13.9433c-0202.rcl-zh-32.bin", 0x001000, 0x001000, CRC(816a86f1) SHA1(58ad0008df72896a18601e090ee0d58155ffa5be) )

	ROM_REGION(0x10000,"maincpu",0)
	//ROM_SYSTEM_BIOS(0, "default", "ver 1.0")
	//ROMX_LOAD( "u20.rom10.27256.bin", 0x000000, 0x008000, CRC(51b92e5d) SHA1(654588f15910a04934d3579b12c14cc44f8ffd47), ROM_BIOS(0) )
	//ROMX_LOAD( "u14.rom20.27256.bin", 0x008000, 0x008000, CRC(1bbc7f3a) SHA1(32d8ac440a59dc0d2096eadfca057c27c7ad9cc1), ROM_BIOS(0) )
	ROM_LOAD( "u20.rom10.27256.bin", 0x000000, 0x008000, CRC(51b92e5d) SHA1(654588f15910a04934d3579b12c14cc44f8ffd47) )
	ROM_LOAD( "u14.rom20.27256.bin", 0x008000, 0x008000, CRC(1bbc7f3a) SHA1(32d8ac440a59dc0d2096eadfca057c27c7ad9cc1) )

	ROM_REGION(0x40000,"cecexp",0)
	ROM_LOAD16_BYTE( "u4.c3001.531000.bin", 0x000000, 0x020000, CRC(d255ea01) SHA1(6ee445c0e7938e2f5de796daac369eb297915129) )
	ROM_LOAD16_BYTE( "u7.c3002.531000.bin", 0x000001, 0x020000, CRC(31963b3b) SHA1(a255f7d0756ae67510f0bf325dc88371906df59d) )

	ROM_REGION(0x800,"keyboard",0)
	ROM_LOAD( "u26.9433c-0201.rcl-zh-16.bin", 0x000000, 0x000800, CRC(f3190603) SHA1(7efdf6f4ee0ed01ff06341c601496a43d06afd6b) )

	ROM_REGION(0x100,"slot6",0)
	ROM_LOAD( "u40.m2822.bin", 0x000000, 0x000100, CRC(b72a2c70) SHA1(bc39fbd5b9a8d2287ac5d0a42e639fc4d3c2f9d4) )
ROM_END

ROM_START(cecg)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "u13.9433c-0202.rcl-zh-32.bin", 0x000000, 0x001000, CRC(816a86f1) SHA1(58ad0008df72896a18601e090ee0d58155ffa5be) )
	ROM_LOAD( "u13.9433c-0202.rcl-zh-32.bin", 0x001000, 0x001000, CRC(816a86f1) SHA1(58ad0008df72896a18601e090ee0d58155ffa5be) )

	ROM_REGION(0x10000,"maincpu",0)
	// ROM_SYSTEM_BIOS(0, "default", "ver 1.0")
	// ROMX_LOAD( "u28.rom1.27256.bin", 0x000000, 0x008000, CRC(ea0f14e1) SHA1(8d184aaa3bfa0cb5f6635f0bd063287356108f5b), ROM_BIOS(0) )
	// ROMX_LOAD( "u29.rom2.27256.bin", 0x008000, 0x008000, CRC(ae1cae24) SHA1(3cba4ffed1a34e7a50bed6cd7ba1befff150fd18), ROM_BIOS(0) )
	ROM_LOAD( "u28.rom1.27256.bin", 0x000000, 0x008000, CRC(ea0f14e1) SHA1(8d184aaa3bfa0cb5f6635f0bd063287356108f5b) )
	ROM_LOAD( "u29.rom2.27256.bin", 0x008000, 0x008000, CRC(ae1cae24) SHA1(3cba4ffed1a34e7a50bed6cd7ba1befff150fd18) )

	ROM_REGION(0x40000,"cecexp",0)
	ROM_LOAD16_BYTE( "u4.c3001.531000.bin", 0x000000, 0x020000, CRC(d255ea01) SHA1(6ee445c0e7938e2f5de796daac369eb297915129) )
	ROM_LOAD16_BYTE( "u7.c3002.531000.bin", 0x000001, 0x020000, CRC(31963b3b) SHA1(a255f7d0756ae67510f0bf325dc88371906df59d) )

	ROM_REGION(0x800,"keyboard",0)
	ROM_LOAD( "u26.9433c-0201.rcl-zh-16.bin", 0x000000, 0x000800, CRC(f3190603) SHA1(7efdf6f4ee0ed01ff06341c601496a43d06afd6b) )

	ROM_REGION(0x100,"slot6",0)
	ROM_LOAD( "u40.m2822.bin", 0x000000, 0x000100, CRC(b72a2c70) SHA1(bc39fbd5b9a8d2287ac5d0a42e639fc4d3c2f9d4) )
ROM_END

ROM_START(cecm)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "u13.9433c-0202.rcl-zh-32.bin", 0x000000, 0x001000, CRC(816a86f1) SHA1(58ad0008df72896a18601e090ee0d58155ffa5be) )
	ROM_LOAD( "u13.9433c-0202.rcl-zh-32.bin", 0x001000, 0x001000, CRC(816a86f1) SHA1(58ad0008df72896a18601e090ee0d58155ffa5be) )

	ROM_REGION(0x10000,"maincpu",0)
	//ROM_SYSTEM_BIOS(0, "default", "ver 1.0")
	// ROMX_LOAD( "u8_st_27256fi_042f.bin", 0x000000, 0x008000, CRC(7a7ed6f0) SHA1(77ad9185607d65f05f3379f01445bb3fa33dbade), ROM_BIOS(0) )
	// ROMX_LOAD( "u35_st_27256fi_a53a.bin", 0x008000, 0x008000, CRC(0ffd658b) SHA1(72fadc11a0f391973e784a2e6f8ea5156d3305eb), ROM_BIOS(0) )
	ROM_LOAD( "u8_st_27256fi_042f.bin", 0x000000, 0x008000, CRC(7a7ed6f0) SHA1(77ad9185607d65f05f3379f01445bb3fa33dbade) )
	ROM_LOAD( "u35_st_27256fi_a53a.bin", 0x008000, 0x008000, CRC(0ffd658b) SHA1(72fadc11a0f391973e784a2e6f8ea5156d3305eb) )

	ROM_REGION(0x40000,"cecexp",0)
	ROM_LOAD16_BYTE( "u33.mx231024-0059.bin", 0x000000, 0x020000, CRC(a2a59f35) SHA1(01c787e150bf1378088a9333ec9338387aae0f50) )
	ROM_LOAD16_BYTE( "u34.mx231024-0060.bin", 0x000001, 0x020000, CRC(f23470ce) SHA1(dc4cbe19e202d2afb56998ff04255b3171b58e14) )

	ROM_REGION(0x800,"keyboard",0)
	ROM_LOAD( "u26.9433c-0201.rcl-zh-16.bin", 0x000000, 0x000800, CRC(f3190603) SHA1(7efdf6f4ee0ed01ff06341c601496a43d06afd6b) )
	// ROM_LOAD( "u10_2732_0081.bin", 0x000000, 0x00800, CRC(505eed67) SHA1(4acbee7c957528d1f9fbfd54464f85fb493175d7) )
ROM_END

ROM_START(cec2000)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "u13.9433c-0202.rcl-zh-32.bin", 0x000000, 0x001000, CRC(816a86f1) SHA1(58ad0008df72896a18601e090ee0d58155ffa5be) )
	ROM_LOAD( "u13.9433c-0202.rcl-zh-32.bin", 0x001000, 0x001000, CRC(816a86f1) SHA1(58ad0008df72896a18601e090ee0d58155ffa5be) )

	ROM_REGION(0x10000,"maincpu",0)
	//ROM_SYSTEM_BIOS(0, "default", "ver 3.0")
	//ROMX_LOAD( "u34.rom1.27256.bin", 0x000000, 0x008000, CRC(d8e001a5) SHA1(55febac557de02afc6186ffa5dd2dcc33b58b0ef), ROM_BIOS(0) )
	//ROMX_LOAD( "u41.rom2.27256.bin", 0x008000, 0x008000, CRC(6d7074ce) SHA1(d271a204a782f383783376f536fcc94a06bd81f6), ROM_BIOS(0) )
	ROM_LOAD( "u34.rom1.27256.bin", 0x000000, 0x008000, CRC(d8e001a5) SHA1(55febac557de02afc6186ffa5dd2dcc33b58b0ef) )
	ROM_LOAD( "u41.rom2.27256.bin", 0x008000, 0x008000, CRC(6d7074ce) SHA1(d271a204a782f383783376f536fcc94a06bd81f6) )

	ROM_REGION(0x40000,"cecexp",0)
	ROM_LOAD16_BYTE( "u4.c3001.531000.bin", 0x000000, 0x020000, CRC(d255ea01) SHA1(6ee445c0e7938e2f5de796daac369eb297915129) )
	ROM_LOAD16_BYTE( "u7.c3002.531000.bin", 0x000001, 0x020000, CRC(31963b3b) SHA1(a255f7d0756ae67510f0bf325dc88371906df59d) )

	ROM_REGION(0x800,"keyboard",0)
	ROM_LOAD( "u26.9433c-0201.rcl-zh-16.bin", 0x000000, 0x000800, CRC(f3190603) SHA1(7efdf6f4ee0ed01ff06341c601496a43d06afd6b) )

	ROM_REGION(0x100,"slot6",0)
	ROM_LOAD( "u40.m2822.bin", 0x000000, 0x000100, CRC(b72a2c70) SHA1(bc39fbd5b9a8d2287ac5d0a42e639fc4d3c2f9d4) )
ROM_END

ROM_START(zijini)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "u13.9433c-0202.rcl-zh-32.bin", 0x000000, 0x001000, CRC(816a86f1) SHA1(58ad0008df72896a18601e090ee0d58155ffa5be) )
	ROM_LOAD( "u13.9433c-0202.rcl-zh-32.bin", 0x001000, 0x001000, CRC(816a86f1) SHA1(58ad0008df72896a18601e090ee0d58155ffa5be) )

	ROM_REGION(0x10000,"maincpu",0)
	//ROM_SYSTEM_BIOS(0, "default", "ver 1.0")
	//ROMX_LOAD( "u20.rom10.27256.bin", 0x000000, 0x008000, CRC(51b92e5d) SHA1(654588f15910a04934d3579b12c14cc44f8ffd47), ROM_BIOS(0) )
	//ROMX_LOAD( "u14.rom20.27256.bin", 0x008000, 0x008000, CRC(1bbc7f3a) SHA1(32d8ac440a59dc0d2096eadfca057c27c7ad9cc1), ROM_BIOS(0) )
	ROM_LOAD( "u20-m27256-3c91.bin", 0x000000, 0x008000, CRC(dc95ebba) SHA1(360d8f4be3ea94eb4cc37f8c489f22c1436f4a51) )
	ROM_LOAD( "u21-m27256-4e04.bin", 0x008000, 0x008000, CRC(0d7011bd) SHA1(322ed9805a76c0066170edc420419c7ef65ead8e) )

	ROM_REGION(0x40000,"cecexp",0)
	ROM_LOAD16_BYTE( "u4.c3001.531000.bin", 0x000000, 0x020000, CRC(d255ea01) SHA1(6ee445c0e7938e2f5de796daac369eb297915129) )
	ROM_LOAD16_BYTE( "u7.c3002.531000.bin", 0x000001, 0x020000, CRC(31963b3b) SHA1(a255f7d0756ae67510f0bf325dc88371906df59d) )

	ROM_REGION(0x800,"keyboard",0)
	ROM_LOAD( "u26.9433c-0201.rcl-zh-16.bin", 0x000000, 0x000800, CRC(f3190603) SHA1(7efdf6f4ee0ed01ff06341c601496a43d06afd6b) )

	ROM_REGION(0x100,"slot6",0)
	ROM_LOAD( "u40.m2822.bin", 0x000000, 0x000100, CRC(b72a2c70) SHA1(bc39fbd5b9a8d2287ac5d0a42e639fc4d3c2f9d4) )
ROM_END

ROM_START(ace2200)
	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD("franklin_ace2000_videorom.bin", 0x1000, 0x1000, CRC(545bdeea) SHA1(26ebc4b0d3080311f550090bc1b29807cb22d083))
	ROM_CONTINUE(0x0000, 0x1000)

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("franklin_ace2000_rom_u2_p2_rev6.bin", 0x000000, 0x002000, CRC(1ab6e4d3) SHA1(5bdb10089fbdadaee9772afa1f7439a51289636b))
	ROM_LOAD("franklin_ace2000_rom_u3_p1_rev6.bin", 0x002000, 0x002000, CRC(197f4936) SHA1(ec9de6da0ca6b6fd97fbef34eec64bf5c3c1b6c5))
	ROM_LOAD("franklin_ace2000_rom_u1_p3_rev6_franklinrom.bin", 0x004000, 0x002000, CRC(5cc150a7) SHA1(7ac8028bbf8cb7730f432e0bae32e364523555fb))

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "342-0132-c.e12", 0x000, 0x800, CRC(e47045f4) SHA1(12a2e718f5f4acd69b6c33a45a4a940b1440a481) ) // 1983 US-Dvorak
ROM_END

ROM_START(ace500)
	ROM_REGION(0x2000,"gfx1",0)
	// not sure if the 500 and 2000 have the same character set.  First 4K is normal charset, second 4K is MouseText
	ROM_LOAD("franklin_ace2000_videorom.bin", 0x1000, 0x1000, CRC(545bdeea) SHA1(26ebc4b0d3080311f550090bc1b29807cb22d083))
	ROM_CONTINUE(0x0000, 0x1000)

	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("franklin500-rom.bin", 0x004000, 0x004000, CRC(376c9104) SHA1(7f82706adc0bfb5f60c207c81271eb0ba8510a11))
	ROM_CONTINUE(0x0000, 0x4000)

	ROM_REGION( 0x800, "keyboard", ROMREGION_ERASE00 )
	ROM_LOAD( "342-0132-c.e12", 0x000, 0x800, CRC(e47045f4) SHA1(12a2e718f5f4acd69b6c33a45a4a940b1440a481) ) // 1983 US-Dvorak
ROM_END

} // anonymous namespace


/*    YEAR  NAME        PARENT   COMPAT  MACHINE          INPUT       CLASS          INIT           COMPANY                              FULLNAME */
COMP( 1983, apple2e,    0,       apple2, apple2e,         apple2eus,  apple2e_state, empty_init,    "Apple Computer",                    "Apple //e", MACHINE_SUPPORTS_SAVE )
COMP( 1983, apple2euk,  apple2e, 0,      apple2epal,      apple2euk,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //e (UK)", MACHINE_SUPPORTS_SAVE )
COMP( 1983, apple2ede,  apple2e, 0,      apple2epal,      apple2ede,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //e (Germany)", MACHINE_SUPPORTS_SAVE )
COMP( 1983, apple2ese,  apple2e, 0,      apple2epal,      apple2ese,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //e (Sweden)", MACHINE_SUPPORTS_SAVE )
COMP( 1983, apple2efr,  apple2e, 0,      apple2epal,      apple2efr,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //e (France)", MACHINE_SUPPORTS_SAVE )
COMP( 1983, apple2ees,  apple2e, 0,      apple2epal,      apple2ees,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //e (Spain)", MACHINE_SUPPORTS_SAVE )
COMP( 1983, mprof3,     apple2e, 0,      mprof3,          apple2e,    apple2e_state, empty_init,    "Multitech",                         "Microprofessor III", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1985, apple2ee,   apple2e, 0,      apple2ee,        apple2eus,  apple2e_state, empty_init,    "Apple Computer",                    "Apple //e (enhanced)", MACHINE_SUPPORTS_SAVE )
COMP( 1985, apple2eeuk, apple2e, 0,      apple2eepal,     apple2euk,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //e (enhanced, UK)", MACHINE_SUPPORTS_SAVE )
COMP( 1985, apple2eede, apple2e, 0,      apple2eepal,     apple2ede,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //e (enhanced, Germany)", MACHINE_SUPPORTS_SAVE )
COMP( 1985, apple2eese, apple2e, 0,      apple2eepal,     apple2ese,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //e (enhanced, Sweden)", MACHINE_SUPPORTS_SAVE )
COMP( 1985, apple2eefr, apple2e, 0,      apple2eepal,     apple2eefr, apple2e_state, init_pal,      "Apple Computer",                    "Apple //e (enhanced, France)", MACHINE_SUPPORTS_SAVE )
COMP( 1987, apple2ep,   apple2e, 0,      apple2ep,        apple2epus, apple2e_state, empty_init,    "Apple Computer",                    "Apple //e (Platinum)", MACHINE_SUPPORTS_SAVE )
COMP( 1987, apple2epuk, apple2e, 0,      apple2eppal,     apple2epuk, apple2e_state, init_pal,      "Apple Computer",                    "Apple //e (Platinum, UK)", MACHINE_SUPPORTS_SAVE )
COMP( 1987, apple2epde, apple2e, 0,      apple2eppal,     apple2epde, apple2e_state, init_pal,      "Apple Computer",                    "Apple //e (Platinum, Germany)", MACHINE_SUPPORTS_SAVE )
COMP( 1987, apple2epse, apple2e, 0,      apple2eppal,     apple2epse, apple2e_state, init_pal,      "Apple Computer",                    "Apple //e (Platinum, Sweden)", MACHINE_SUPPORTS_SAVE )
COMP( 1987, apple2epfr, apple2e, 0,      apple2eppal,     apple2epfr, apple2e_state, init_pal,      "Apple Computer",                    "Apple //e (Platinum, France)", MACHINE_SUPPORTS_SAVE )
COMP( 1984, apple2c,    0,       apple2, apple2c,         apple2cus,  apple2e_state, empty_init,    "Apple Computer",                    "Apple //c" , MACHINE_SUPPORTS_SAVE )
COMP( 1984, apple2cuk,  apple2c, 0,      apple2cpal,      apple2cuk,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //c (UK)" , MACHINE_SUPPORTS_SAVE )
COMP( 1984, apple2cde,  apple2c, 0,      apple2cpal,      apple2cde,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //c (Germany)" , MACHINE_SUPPORTS_SAVE )
COMP( 1984, apple2cse,  apple2c, 0,      apple2cpal,      apple2cse,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //c (Sweden)" , MACHINE_SUPPORTS_SAVE )
COMP( 1984, apple2cfr,  apple2c, 0,      apple2cpal,      apple2cfr,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //c (France)" , MACHINE_SUPPORTS_SAVE )
COMP( 1985?,spectred,   apple2e, 0,      spectred,        spectred,   apple2e_state, empty_init,    "Scopus/Spectrum",                   "Spectrum ED" , MACHINE_SUPPORTS_SAVE )
COMP( 1986, tk3000,     apple2c, 0,      tk3000,          tk3000,     apple2e_state, empty_init,    "Microdigital",                      "TK3000//e" , MACHINE_SUPPORTS_SAVE )
COMP( 1989, prav8c,     apple2e, 0,      apple2e,         prav8c,     apple2e_state, empty_init,    "Pravetz",                           "Pravetz 8C", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1987, laser128,   apple2c, 0,      laser128,        laser128,   apple2e_state, init_laser128, "Video Technology",                  "Laser 128", MACHINE_SUPPORTS_SAVE )
COMP( 1987, laser128o,  apple2c, 0,      laser128o,       laser128,   apple2e_state, init_laser128, "Video Technology",                  "Laser 128 (original hardware)", MACHINE_SUPPORTS_SAVE )
COMP( 1988, las128ex,   apple2c, 0,      laser128,        laser128,   apple2e_state, init_128ex,    "Video Technology",                  "Laser 128ex (version 4.5)", MACHINE_SUPPORTS_SAVE )
COMP( 1988, las128e2,   apple2c, 0,      laser128ex2,     laser128,   apple2e_state, init_128ex,    "Video Technology",                  "Laser 128ex2 (version 6.1)", MACHINE_SUPPORTS_SAVE )
COMP( 1985, apple2c0,   apple2c, 0,      apple2c_iwm,     apple2cus,  apple2e_state, empty_init,    "Apple Computer",                    "Apple //c (UniDisk 3.5)", MACHINE_SUPPORTS_SAVE )
COMP( 1985, apple2c0uk, apple2c, 0,      apple2c_iwm_pal, apple2cuk,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //c (UniDisk 3.5, UK)", MACHINE_SUPPORTS_SAVE )
COMP( 1985, apple2c0de, apple2c, 0,      apple2c_iwm_pal, apple2cde,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //c (UniDisk 3.5, Germany)", MACHINE_SUPPORTS_SAVE )
COMP( 1985, apple2c0se, apple2c, 0,      apple2c_iwm_pal, apple2cse,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //c (UniDisk 3.5, Sweden)", MACHINE_SUPPORTS_SAVE )
COMP( 1985, apple2c0fr, apple2c, 0,      apple2c_iwm_pal, apple2cfr,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //c (UniDisk 3.5, France)", MACHINE_SUPPORTS_SAVE )
COMP( 1986, apple2c3,   apple2c, 0,      apple2c_mem,     apple2cus,  apple2e_state, empty_init,    "Apple Computer",                    "Apple //c (Original Memory Expansion)", MACHINE_SUPPORTS_SAVE )
COMP( 1986, apple2c3uk, apple2c, 0,      apple2c_mem_pal, apple2cuk,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //c (Original Memory Expansion, UK)", MACHINE_SUPPORTS_SAVE )
COMP( 1986, apple2c3de, apple2c, 0,      apple2c_mem_pal, apple2cde,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //c (Original Memory Expansion, Germany)", MACHINE_SUPPORTS_SAVE )
COMP( 1986, apple2c3se, apple2c, 0,      apple2c_mem_pal, apple2cse,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //c (Original Memory Expansion, Sweden)", MACHINE_SUPPORTS_SAVE )
COMP( 1986, apple2c3fr, apple2c, 0,      apple2c_mem_pal, apple2cfr,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //c (Original Memory Expansion, France)", MACHINE_SUPPORTS_SAVE )
COMP( 1986, apple2c4,   apple2c, 0,      apple2c_mem,     apple2cus,  apple2e_state, empty_init,    "Apple Computer",                    "Apple //c (rev 4)", MACHINE_SUPPORTS_SAVE )
COMP( 1986, apple2c4uk, apple2c, 0,      apple2c_mem_pal, apple2cuk,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //c (rev 4, UK)", MACHINE_SUPPORTS_SAVE )
COMP( 1986, apple2c4de, apple2c, 0,      apple2c_mem_pal, apple2cde,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //c (rev 4, Germany)", MACHINE_SUPPORTS_SAVE )
COMP( 1986, apple2c4se, apple2c, 0,      apple2c_mem_pal, apple2cse,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //c (rev 4, Sweden)", MACHINE_SUPPORTS_SAVE )
COMP( 1986, apple2c4fr, apple2c, 0,      apple2c_mem_pal, apple2cfr,  apple2e_state, init_pal,      "Apple Computer",                    "Apple //c (rev 4, France)", MACHINE_SUPPORTS_SAVE )
COMP( 1987, ceci,       0,       apple2, cec,             ceci,       apple2e_state, empty_init,    "Shaanxi Province Computer Factory", "China Education Computer I", MACHINE_SUPPORTS_SAVE )
COMP( 1989, cece,       0,       apple2, cec,             ceci,       apple2e_state, empty_init,    "Shaanxi Province Computer Factory", "China Education Computer E", MACHINE_SUPPORTS_SAVE )
COMP( 1989, cecg,       0,       apple2, cec,             ceci,       apple2e_state, empty_init,    "Shaanxi Province Computer Factory", "China Education Computer G", MACHINE_SUPPORTS_SAVE )
COMP( 1989, cecm,       0,       apple2, cec,             cecm,       apple2e_state, empty_init,    "Shaanxi Province Computer Factory", "China Education Computer M", MACHINE_SUPPORTS_SAVE )
COMP( 1991, cec2000,    0,       apple2, cec,             ceci,       apple2e_state, empty_init,    "Shaanxi Province Computer Factory", "China Education Computer 2000", MACHINE_SUPPORTS_SAVE )
COMP( 1989, zijini,     0,       apple2, cec,             zijini,     apple2e_state, empty_init,    "Nanjing Computer Factory",          "Zi Jin I", MACHINE_SUPPORTS_SAVE )
COMP( 1988, apple2cp,   apple2c, 0,      apple2cp,        apple2cp,   apple2e_state, empty_init,    "Apple Computer",                    "Apple //c Plus", MACHINE_SUPPORTS_SAVE )
COMP( 1985, ace2200,    apple2e, 0,      ace2200,         ace2200,    apple2e_state, init_ace2200,  "Franklin Computer",                 "Franklin ACE 2200", MACHINE_SUPPORTS_SAVE)
COMP( 1986, ace500,     apple2c, 0,      ace500,          ace500,     apple2e_state, init_ace500,   "Franklin Computer",                 "Franklin ACE 500", MACHINE_SUPPORTS_SAVE)



apple2gs.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    apple2gs.cpp - Apple IIgs

    Next generation driver written June 2018 by R. Belmont.
    Thanks to the original Apple IIgs driver's authors: Nathan Woods and R. Belmont
    Thanks also to the Apple II Documentation Project/Antoine Vignau, Peter Ferrie, and Olivier Galibert.

    Unique hardware configurations:
    - ROM 00/01: original motherboard, 256K of RAM (banks 00/01/E0/E1 only), FPI chip manages fast/slow side
    - ROM 03: revised motherboard, 1M of RAM (banks 00/01/->0F/E0/E1), CYA chip replaces FPI
    - Expanded IIe: ROM 00/01 motherboard in a IIe case with a IIe keyboard rather than ADB
    - "Mark Twain" prototype: ROM 3 hardware, SWIM1 instead of IWM, built-in floppy, integrated High-Speed SCSI Card
      and internal SCSI HDD, 2 SIMM slots for RAM expansion instead of the proprietary memory slot of the previous IIgs.
      Only 5 slots: slots 5 and 7 are missing (5 for the SuperDrive, 7 for the SCSI).

    Timing in terms of the 14M 14.3181818 MHz clock (1/2 of the 28.6363636 master clock):
    - 1 2.8 MHz 65816 cycle is 5 14M clocks.
    - The Mega II 1 MHz side runs for 64 cycles at 14 14M clocks and every 65th is stretched to 16 14M clocks.
      This allows 8-bit Apple II raster demos to work.  Each scanline is (64*14) + 16 = 912 14M clocks.
      Due to this stretch, which does not occur on the fast side, the fast and 1 Mhz sides drift from each other
      and sync up every 22800 14M clocks (25 scan lines).
    - Accesses to the 1 MHz side incur a side-sync penalty (waiting for the start of the next 1 MHz cycle).
    - Every 50 14M clocks (10 65816 cycles) DRAM refresh occurs for 5 14M clocks
      * During this time, CPU accesses to ROM, Mega II side I/O, or banks E0/E1 are not penalized (but a side-sync penalty is incurred for the 1 MHz side)
      * Accesses to banks 00-7F are penalized except for I/O in banks 0/1.
    - ROM accesses always run at full speed.

    One video line is: 6 cycles of right border, 13 cycles of hblank, 6 cycles of left border, and 40 cycles of active video

    ((6*14)*2) + 560 = 728 (total for A2 modes)  htotal = 910 (65 * 14)
    ((6*16)*2) + 640 = 832 (total for SHR)       htotal = 1040 (65 * 16)

    FF6ACF is speed test in ROM
    Diags:
    A138 = scanline interrupt test (raster is too long to pass this)
    A179 = pass
    A17C = fail 1
    A0F1 = fail 2

    ZipGS notes:
    $C059 is the GS settings register
    bit 3: CPS Follow
    bit 4: Counter Delay
    bit 5: AppleTalk Delay
    bit 6: Joystick Delay (reverse logic: 0 = delay is ON)
    bit 7: C/D cache disable

    $C05D is the speed percentage:
    $F0 = 6%, $E0 = 12%, $D0 = 18%, $C0 = 25%, $B0 = 31%, $A0 = 37%, $90 = 43%, $80 = 50%,
    $70 = 56%, $60 = 62%, $50 = 68%, $40 = 75%, $30 = 81%, $20 = 87%, $10 = 93%, $00 = 100%

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

#include "emu.h"

#include "apple2video.h"

#include "apple2common.h"
// #include "machine/apple2host.h"
#include "macadb.h"
#include "macrtc.h"

#include "bus/a2bus/a2bus.h"
#include "bus/a2bus/cards.h"
#include "bus/a2gameio/gameio.h"
#include "bus/rs232/rs232.h"
#include "cpu/g65816/g65816.h"
#include "cpu/m6502/m5074x.h"
#include "machine/bankdev.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "machine/z80scc.h"
#include "sound/es5503.h"
#include "sound/spkrdev.h"

#include "machine/applefdintf.h"
#include "machine/iwm.h"
#include "machine/swim1.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "utf8.h"


namespace {

// various timing standards
#define A2GS_MASTER_CLOCK (XTAL(28'636'363))
#define A2GS_14M    (A2GS_MASTER_CLOCK/2)
#define A2GS_7M     (A2GS_MASTER_CLOCK/4)
#define A2GS_1M     (A2GS_MASTER_CLOCK/28)

#define A2GS_UPPERBANK_TAG "inhbank"
#define A2GS_AUXUPPER_TAG "inhaux"
#define A2GS_00UPPER_TAG "inh00"
#define A2GS_01UPPER_TAG "inh01"

#define A2GS_C300_TAG "c3bank"
#define A2GS_LCBANK_TAG "lcbank"
#define A2GS_LCAUX_TAG "lcaux"
#define A2GS_LC00_TAG "lc00"
#define A2GS_LC01_TAG "lc01"
#define A2GS_B0CXXX_TAG "bnk0atc"
#define A2GS_B1CXXX_TAG "bnk1atc"
#define A2GS_B00000_TAG "b0r00bank"
#define A2GS_B00200_TAG "b0r02bank"
#define A2GS_B00400_TAG "b0r04bank"
#define A2GS_B00800_TAG "b0r08bank"
#define A2GS_B02000_TAG "b0r20bank"
#define A2GS_B04000_TAG "b0r40bank"

#define A2GS_KBD_SPEC_TAG "keyb_special"

class apple2gs_state : public driver_device
{
public:
	apple2gs_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		  m_maincpu(*this, "maincpu"),
		  m_screen(*this, "screen"),
		  m_scantimer(*this, "scantimer"),
		  m_acceltimer(*this, "acceltimer"),
		  m_adbmicro(*this, "adbmicro"),
		  m_macadb(*this, "macadb"),
		  m_ram(*this, "ram"),
		  m_rom(*this, "maincpu"),
		  m_docram(*this, "docram"),
		  m_video(*this, "a2video"),
		  m_rtc(*this, "rtc"),
		  m_a2bus(*this, "a2bus"),
		  m_a2common(*this, "a2common"),
		  //      m_a2host(*this, "a2host"),
		  m_gameio(*this, "gameio"),
		  m_speaker(*this, "speaker_sound"),
		  m_upperbank(*this, A2GS_UPPERBANK_TAG),
		  m_upperaux(*this, A2GS_AUXUPPER_TAG),
		  m_upper00(*this, A2GS_00UPPER_TAG),
		  m_upper01(*this, A2GS_01UPPER_TAG),
		  m_c300bank(*this, A2GS_C300_TAG),
		  m_b0_0000bank(*this, A2GS_B00000_TAG),
		  m_b0_0200bank(*this, A2GS_B00200_TAG),
		  m_b0_0400bank(*this, A2GS_B00400_TAG),
		  m_b0_0800bank(*this, A2GS_B00800_TAG),
		  m_b0_2000bank(*this, A2GS_B02000_TAG),
		  m_b0_4000bank(*this, A2GS_B04000_TAG),
		  m_e0_0000bank(*this, "e0_0000_bank"),
		  m_e0_0200bank(*this, "e0_0200_bank"),
		  m_e0_0400bank(*this, "e0_0400_bank"),
		  m_e0_0800bank(*this, "e0_0800_bank"),
		  m_e0_2000bank(*this, "e0_2000_bank"),
		  m_e0_4000bank(*this, "e0_4000_bank"),
		  m_lcbank(*this, A2GS_LCBANK_TAG),
		  m_lcaux(*this, A2GS_LCAUX_TAG),
		  m_lc00(*this, A2GS_LC00_TAG),
		  m_lc01(*this, A2GS_LC01_TAG),
		  m_bank0_atc(*this, A2GS_B0CXXX_TAG),
		  m_bank1_atc(*this, A2GS_B1CXXX_TAG),
		  m_scc(*this, "scc"),
		  m_doc(*this, "doc"),
		  m_iwm(*this, "fdc"),
		  m_floppy(*this, "fdc:%d", 0U),
		  m_sysconfig(*this, "a2_config")

	{
		m_cur_floppy = nullptr;
		m_devsel = 0;
		m_diskreg = 0;
	}

	void apple2gs(machine_config &config);
	void apple2gsr1(machine_config &config);
	void apple2gsmt(machine_config &config);

	void rom1_init() { m_is_rom3 = false; }
	void rom3_init() { m_is_rom3 = true; }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<g65816_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<timer_device> m_scantimer, m_acceltimer;
	required_device<m5074x_device> m_adbmicro;
	required_device<macadb_device> m_macadb;
	required_device<ram_device> m_ram;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_docram;
	required_device<a2_video_device> m_video;
	required_device<rtc3430042_device> m_rtc;
	required_device<a2bus_device> m_a2bus;
	required_device<apple2_common_device> m_a2common;
//  required_device<apple2_host_device> m_a2host;
	required_device<apple2_gameio_device> m_gameio;
	required_device<speaker_sound_device> m_speaker;
	memory_view m_upperbank, m_upperaux, m_upper00, m_upper01;
	required_device<address_map_bank_device> m_c300bank;
	memory_view m_b0_0000bank, m_b0_0200bank, m_b0_0400bank, m_b0_0800bank, m_b0_2000bank, m_b0_4000bank;
	memory_view m_e0_0000bank, m_e0_0200bank, m_e0_0400bank, m_e0_0800bank, m_e0_2000bank, m_e0_4000bank;
	memory_view m_lcbank, m_lcaux, m_lc00, m_lc01, m_bank0_atc, m_bank1_atc;
	required_device<z80scc_device> m_scc;
	required_device<es5503_device> m_doc;
	required_device<applefdintf_device> m_iwm;
	required_device_array<floppy_connector, 4> m_floppy;
	required_ioport m_sysconfig;

	static constexpr int CNXX_UNCLAIMED = -1;
	static constexpr int CNXX_INTROM = -2;

	enum glu_reg_names
	{
		// these are the MCU-visible registers
		GLU_KEY_DATA = 0,   // MCU W
		GLU_COMMAND,        // MCU R
		GLU_MOUSEX,         // MCU W
		GLU_MOUSEY,         // MCU W
		GLU_KG_STATUS,      // MCU R
		GLU_ANY_KEY_DOWN,   // MCU W
		GLU_KEYMOD,         // MCU W
		GLU_DATA,           // MCU W

		GLU_C000,       // 816 R
		GLU_C010,       // 816 RW
		GLU_SYSSTAT     // 816 R/(limited) W
	};


	static constexpr u8 KGS_ANY_KEY_DOWN = 0x01;
	static constexpr u8 KGS_KEYSTROBE    = 0x10;
	static constexpr u8 KGS_DATA_FULL    = 0x20;
	static constexpr u8 KGS_COMMAND_FULL = 0x40;
	static constexpr u8 KGS_MOUSEX_FULL  = 0x80;

	static constexpr u8 GLU_STATUS_CMDFULL  = 0x01;
	static constexpr u8 GLU_STATUS_MOUSEXY  = 0x02;
	static constexpr u8 GLU_STATUS_KEYDATIRQEN = 0x04;
	static constexpr u8 GLU_STATUS_KEYDATIRQ = 0x08;
	static constexpr u8 GLU_STATUS_DATAIRQEN = 0x10;
	static constexpr u8 GLU_STATUS_DATAIRQ  = 0x20;
	static constexpr u8 GLU_STATUS_MOUSEIRQEN = 0x40;
	static constexpr u8 GLU_STATUS_MOUSEIRQ = 0x080;

	static constexpr u8 SHAD_IOLC       = 0x40; // I/O and language card inhibit for banks 00/01
	static constexpr u8 SHAD_TXTPG2     = 0x20; // inhibits text-page 2 shadowing in both banks (ROM 03 h/w only)
	static constexpr u8 SHAD_AUXHIRES   = 0x10; // inhibits bank 01 hi-res region shadowing
	static constexpr u8 SHAD_SUPERHIRES = 0x08; // inhibits bank 01 super-hi-res region shadowing
	static constexpr u8 SHAD_HIRESPG2   = 0x04; // inhibits hi-res page 2 shadowing in both banks
	static constexpr u8 SHAD_HIRESPG1   = 0x02; // inhibits hi-res page 1 shadowing in both banks
	static constexpr u8 SHAD_TXTPG1     = 0x01; // inhibits text-page 1 shadowing in both banks

	static constexpr u8 SPEED_HIGH      = 0x80; // full 2.8 MHz speed when set, Apple II 1 MHz when clear
	[[maybe_unused]] static constexpr u8 SPEED_POWERON = 0x40; // ROM 03 only; indicates machine turned on by power switch (as opposed to ?)
	static constexpr u8 SPEED_ALLBANKS  = 0x10; // enables bank 0/1 shadowing in all banks (not supported)
	[[maybe_unused]] static constexpr u8 SPEED_DISKIISL7 = 0x08; // enable Disk II motor on detect for slot 7
	[[maybe_unused]] static constexpr u8 SPEED_DISKIISL6 = 0x04; // enable Disk II motor on detect for slot 6
	[[maybe_unused]] static constexpr u8 SPEED_DISKIISL5 = 0x02; // enable Disk II motor on detect for slot 5
	[[maybe_unused]] static constexpr u8 SPEED_DISKIISL4 = 0x01; // enable Disk II motor on detect for slot 4

	static constexpr u8 DISKREG_HDSEL = 7; // select signal for 3.5" Sony drives
	static constexpr u8 DISKREG_35SEL = 6; // 1 to enable 3.5" drives, 0 to chain through to 5.25"

	enum irq_sources
	{
		IRQS_DOC        = 0,
		IRQS_SCAN       = 1,
		IRQS_ADB        = 2,
		IRQS_VBL        = 3,
		IRQS_SECOND     = 4,
		IRQS_QTRSEC     = 5,
		IRQS_SLOT       = 6,
		IRQS_SCC        = 7
	};

	static constexpr u8 INTFLAG_IRQASSERTED = 0x01;
	[[maybe_unused]] static constexpr u8 INTFLAG_M2MOUSEMOVE = 0x02;
	[[maybe_unused]] static constexpr u8 INTFLAG_M2MOUSESW   = 0x04;
	static constexpr u8 INTFLAG_VBL         = 0x08;
	static constexpr u8 INTFLAG_QUARTER     = 0x10;
	static constexpr u8 INTFLAG_AN3         = 0x20;
	[[maybe_unused]] static constexpr u8 INTFLAG_MOUSEDOWNLAST = 0x40;
	[[maybe_unused]] static constexpr u8 INTFLAG_MOUSEDOWN   = 0x80;

	[[maybe_unused]]  static constexpr u8 VGCINT_EXTERNALEN = 0x01;
	static constexpr u8 VGCINT_SCANLINEEN   = 0x02;
	static constexpr u8 VGCINT_SECONDENABLE = 0x04;
	[[maybe_unused]] static constexpr u8 VGCINT_EXTERNAL     = 0x10;
	static constexpr u8 VGCINT_SCANLINE     = 0x20;
	static constexpr u8 VGCINT_SECOND       = 0x40;
	static constexpr u8 VGCINT_ANYVGCINT    = 0x80;

	enum adbstate_t
	{
		ADBSTATE_IDLE,
		ADBSTATE_INCOMMAND,
		ADBSTATE_INRESPONSE
	};

	bool m_adb_line = false;

	address_space *m_maincpu_space = nullptr;

	TIMER_DEVICE_CALLBACK_MEMBER(apple2_interrupt);
	TIMER_DEVICE_CALLBACK_MEMBER(accel_timer);

	void palette_init(palette_device &palette);
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void apple2gs_map(address_map &map) ATTR_COLD;
	void vectors_map(address_map &map) ATTR_COLD;
	void a2gs_es5503_map(address_map &map) ATTR_COLD;
	void c300bank_map(address_map &map) ATTR_COLD;

	void phases_w(uint8_t phases);
	void sel35_w(int sel35);
	void devsel_w(uint8_t devsel);
	void hdsel_w(int hdsel);

	floppy_image_device *m_cur_floppy = nullptr;
	int m_devsel = 0;
	u8 m_diskreg = 0;

	u8 auxram0000_r(offs_t offset);
	void auxram0000_w(offs_t offset, u8 data);
	u8 b0ram0000_r(offs_t offset);
	void b0ram0000_w(offs_t offset, u8 data);
	u8 b0ram0200_r(offs_t offset);
	void b0ram0200_w(offs_t offset, u8 data);
	u8 b0ram0400_r(offs_t offset);
	void b0ram0400_w(offs_t offset, u8 data);
	u8 b0ram0800_r(offs_t offset);
	void b0ram0800_w(offs_t offset, u8 data);
	u8 b0ram2000_r(offs_t offset);
	void b0ram2000_w(offs_t offset, u8 data);
	u8 b0ram4000_r(offs_t offset);
	void b0ram4000_w(offs_t offset, u8 data);
	u8 b1ram0000_r(offs_t offset);
	void b1ram0000_w(offs_t offset, u8 data);
	u8 b1ram0200_r(offs_t offset);
	void b1ram0200_w(offs_t offset, u8 data);
	u8 b1ram0400_r(offs_t offset);
	void b1ram0400_w(offs_t offset, u8 data);
	u8 b1ram0800_r(offs_t offset);
	void b1ram0800_w(offs_t offset, u8 data);
	u8 b1ram2000_r(offs_t offset);
	void b1ram2000_w(offs_t offset, u8 data);
	u8 b1ram4000_r(offs_t offset);
	void b1ram4000_w(offs_t offset, u8 data);

	template <int Addr> u8 e0ram_r(offs_t offset);
	template <int Addr> void e0ram_w(offs_t offset, u8 data);
	template <int Addr> u8 e1ram_r(offs_t offset);
	template <int Addr> void e1ram_w(offs_t offset, u8 data);

	u8 c000_r(offs_t offset);
	void c000_w(offs_t offset, u8 data);
	u8 c080_r(offs_t offset);
	void c080_w(offs_t offset, u8 data);
	u8 c100_r(offs_t offset);
	void c100_w(offs_t offset, u8 data);
	u8 c300_r(offs_t offset);
	u8 c300_int_r(offs_t offset);
	void c300_w(offs_t offset, u8 data);
	u8 c400_r(offs_t offset);
	void c400_w(offs_t offset, u8 data);
	u8 c800_r(offs_t offset);
	void c800_w(offs_t offset, u8 data);
	u8 inh_r(offs_t offset);
	void inh_w(offs_t offset, u8 data);
	u8 lc_r(offs_t offset);
	void lc_w(offs_t offset, u8 data);
	u8 lc_aux_r(offs_t offset);
	void lc_aux_w(offs_t offset, u8 data);
	u8 lc_00_r(offs_t offset);
	void lc_00_w(offs_t offset, u8 data);
	u8 lc_01_r(offs_t offset);
	void lc_01_w(offs_t offset, u8 data);
	u8 bank0_c000_r(offs_t offset);
	void bank0_c000_w(offs_t offset, u8 data);
	u8 bank1_0000_r(offs_t offset);
	void bank1_0000_sh_w(offs_t offset, u8 data);
	u8 bank1_c000_r(offs_t offset);
	void bank1_c000_w(offs_t offset, u8 data);
	u8 expandedram_r(offs_t offset);
	void expandedram_w(offs_t offset, u8 data);
	void a2bus_irq_w(int state);
	void a2bus_nmi_w(int state);
	void a2bus_inh_w(int state);
	void doc_irq_w(int state);
	void scc_irq_w(int state);
	u8 doc_adc_read();
	u8 apple2gs_read_vector(offs_t offset);

	u8 keyglu_mcu_read(u8 offset);
	void keyglu_mcu_write(u8 offset, u8 data);
	u8 keyglu_816_read(u8 offset);
	void keyglu_816_write(u8 offset, u8 data);

	u8 m_adb_p2_last, m_adb_p3_last;
	int m_adb_reset_freeze = 0;
	void keyglu_regen_irqs();

	u8 adbmicro_p0_in();
	u8 adbmicro_p1_in();
	u8 adbmicro_p2_in();
	u8 adbmicro_p3_in();
	void adbmicro_p0_out(u8 data);
	void adbmicro_p1_out(u8 data);
	void adbmicro_p2_out(u8 data);
	void adbmicro_p3_out(u8 data);
	void set_adb_line(int linestate);

	offs_t dasm_trampoline(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const util::disasm_interface::data_buffer &params);
	void wdm_trampoline(offs_t offset, u8 data) { }; //m_a2host->wdm_w(space, offset, data); }

	bool m_is_rom3 = false;
	int m_speaker_state = 0;

	double m_joystick_x1_time = 0, m_joystick_y1_time = 0, m_joystick_x2_time = 0, m_joystick_y2_time = 0;

	int m_inh_slot = 0, m_cnxx_slot = 0;
	int m_motoroff_time = 0;

	bool m_romswitch = false;

	bool m_an0 = false, m_an1 = false, m_an2 = false, m_an3 = false;

	bool m_vbl = false;

	int m_irqmask = 0;

	bool m_intcxrom = false;
	bool m_slotc3rom = false;
	bool m_altzp = false;
	bool m_ramrd = false, m_ramwrt = false;
	bool m_lcram = false, m_lcram2 = false, m_lcprewrite = false, m_lcwriteenable = false;
	bool m_ioudis = false;
	bool m_rombank = false;

	u8 m_shadow = 0, m_speed = 0, m_textcol = 0;
	u8 m_motors_active = 0, m_slotromsel = 0, m_intflag = 0, m_vgcint = 0, m_inten = 0;

	bool m_last_speed = false;

	// Sound GLU variables
	u8 m_sndglu_ctrl = 0;
	u16 m_sndglu_addr = 0;
	int m_sndglu_dummy_read = 0;

	// Key GLU variables
	u8 m_glu_regs[12]{}, m_glu_bus = 0;
	bool m_glu_mcu_read_kgs = false, m_glu_816_read_dstat = false, m_glu_mouse_read_stat = false;
	int m_glu_kbd_y = 0;

	u8 *m_ram_ptr = nullptr;
	int m_ram_size = 0;
	u8 m_megaii_ram[0x20000]{};  // 128K of "slow RAM" at $E0/0000

	int m_inh_bank = 0;

	bool m_slot_irq = false;

	double m_x_calibration = 0, m_y_calibration = 0;

	device_a2bus_card_interface *m_slotdevice[8]{};

	u32 m_slow_counter = 0;

	// clock/BRAM
	u8 m_clkdata = 0, m_clock_control = 0;
	u8 m_clock_frame = 0;

	void lcrom_update();
	void do_io(int offset);
	u8 read_floatingbus();
	void update_slotrom_banks();
	void lc_update(int offset, bool writing);
	u8 read_slot_rom(int slotbias, int offset);
	void write_slot_rom(int slotbias, int offset, u8 data);
	u8 read_int_rom(int slotbias, int offset);
	void auxbank_update();
	void raise_irq(int irq);
	void lower_irq(int irq);
	void update_speed();
	int get_vpos();
	void process_clock();

	// ZipGS stuff
	bool m_accel_unlocked = false;
	bool m_accel_fast = false;
	bool m_accel_present = false;
	bool m_accel_temp_slowdown = false;
	int m_accel_stage = 0;
	u32 m_accel_speed = 0;
	u8 m_accel_slotspk = 0, m_accel_gsxsettings = 0, m_accel_percent = 0;

	void accel_full_speed()
	{
		bool isfast = false;

		if (m_speed & SPEED_HIGH)
		{
			isfast = true;
		}

		if ((m_motors_active & (m_speed & 0x0f)) != 0)
		{
			isfast = false;
		}

		if (isfast)
		{
			m_maincpu->set_unscaled_clock(m_accel_speed);
		}
		else
		{
			m_maincpu->set_unscaled_clock(1021800);
		}
	}

	void accel_normal_speed()
	{
		bool isfast = false;

		if (m_speed & SPEED_HIGH)
		{
			isfast = true;
		}

		if ((m_motors_active & (m_speed & 0x0f)) != 0)
		{
			isfast = false;
		}

		if (isfast)
		{
			m_maincpu->set_unscaled_clock(A2GS_14M/5);
		}
		else
		{
			m_maincpu->set_unscaled_clock(1021800);
		}
	}

	void accel_slot(int slot);
};

// FF6ACF is speed test routine in ROM 3

// slow_cycle() - take a 1 MHz cycle.  Theory: a 2.8 MHz cycle is 14M / 5.
// 1 MHz is 14M / 14.  14/5 = 2.8 * 65536 (16.16 fixed point) = 0x2cccd.
#define slow_cycle() \
{   \
	if (!machine().side_effects_disabled() && m_last_speed) \
	{\
		m_slow_counter += 0x0002cccd; \
		int cycles = (m_slow_counter >> 16) & 0xffff; \
		m_slow_counter &= 0xffff; \
		m_maincpu->adjust_icount(-cycles); \
	} \
}


offs_t apple2gs_state::dasm_trampoline(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const util::disasm_interface::data_buffer &params)
{
	return m_a2common->dasm_override_GS(stream, pc, opcodes, params);
}

void apple2gs_state::a2bus_irq_w(int state)
{
	if (state == ASSERT_LINE)
	{
		raise_irq(IRQS_SLOT);
		m_slot_irq = true;
	}
	else
	{
		lower_irq(IRQS_SLOT);
	}
}

void apple2gs_state::a2bus_nmi_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, state);
}

// TODO: this assumes /INH only on ROM, needs expansion to support e.g. phantom-slotting cards and etc.
void apple2gs_state::a2bus_inh_w(int state)
{
	if (state == ASSERT_LINE)
	{
		// assume no cards are pulling /INH
		m_inh_slot = -1;

		// scan the slots to figure out which card(s) are INHibiting stuff
		for (int i = 0; i <= 7; i++)
		{
			if (m_slotdevice[i])
			{
				// this driver only can inhibit from 0xd000-0xffff
				if ((m_slotdevice[i]->inh_start() == 0xd000) &&
					(m_slotdevice[i]->inh_end() == 0xffff))
				{
					if ((m_slotdevice[i]->inh_type() & INH_READ) == INH_READ)
					{
						if (m_inh_bank != 1)
						{
							m_upperbank.select(1);
							m_upperaux.select(1);
							m_upper00.select(1);
							m_upper01.select(1);
							m_inh_bank = 1;
						}
					}
					else
					{
						if (m_inh_bank != 0)
						{
							m_upperbank.select(0);
							m_upperaux.select(0);
							m_upper00.select(0);
							m_upper01.select(0);
							m_inh_bank = 0;
						}
					}

					m_inh_slot = i;
					break;
				}
			}
		}

		// if no slots are inhibiting, make sure ROM is fully switched in
		if ((m_inh_slot == -1) && (m_inh_bank != 0))
		{
			m_upperbank.select(0);
			m_upperaux.select(0);
			m_upper00.select(0);
			m_upper01.select(0);
			m_inh_bank = 0;
		}
	}
}

// FPI/CYA chip is connected to the VPB output of the 65816.
// this facilitates the documented behavior from the Firmware Reference.
u8 apple2gs_state::apple2gs_read_vector(offs_t offset)
{
	// when IOLC shadowing is enabled, vector fetches always go to ROM,
	// regardless of the language card config.
	if (!(m_shadow & SHAD_IOLC))
	{
		return m_maincpu->space(AS_PROGRAM).read_byte(offset | 0xFFFFE0);
	}
	else    // else vector fetches from bank 0 RAM
	{
		return m_maincpu->space(AS_PROGRAM).read_byte((offset & 0xffff) | 0xFFE0);
	}
}

/***************************************************************************
    START/RESET
***************************************************************************/

void apple2gs_state::machine_start()
{
	m_ram_ptr = m_ram->pointer();
	m_ram_size = m_ram->size();
	m_speaker_state = 0;
	m_speaker->level_w(m_speaker_state);
	m_upperbank.select(0);
	m_upperaux.select(0);
	m_upper00.select(0);
	m_upper01.select(0);
	m_lcbank.select(0);
	m_lcaux.select(0);
	m_lc00.select(0);
	m_lc01.select(0);
	m_b0_0000bank.select(0);
	m_e0_0000bank.select(0);
	m_b0_0200bank.select(0);
	m_e0_0200bank.select(0);
	m_b0_0400bank.select(0);
	m_e0_0400bank.select(0);
	m_b0_0800bank.select(0);
	m_e0_0800bank.select(0);
	m_b0_2000bank.select(0);
	m_e0_2000bank.select(0);
	m_b0_4000bank.select(0);
	m_e0_4000bank.select(0);
	m_inh_bank = 0;
	std::fill(std::begin(m_megaii_ram), std::end(m_megaii_ram), 0);

	std::fill(std::begin(m_glu_regs), std::end(m_glu_regs), 0);

	// setup speaker toggle volumes.  this should be done mathematically probably,
	// but these ad-hoc values aren't too bad.
#define LVL(x) (double(x) / 32768.0)
	static const double lvlTable[16] =
	{
		LVL(0x0000), LVL(0x03ff), LVL(0x04ff), LVL(0x05ff), LVL(0x06ff), LVL(0x07ff), LVL(0x08ff), LVL(0x09ff),
		LVL(0x0aff), LVL(0x0bff), LVL(0x0cff), LVL(0x0fff), LVL(0x1fff), LVL(0x3fff), LVL(0x5fff), LVL(0x7fff)
	};
	m_speaker->set_levels(16, lvlTable);

	// precalculate joystick time constants
	m_x_calibration = attotime::from_nsec(10800).as_double();
	m_y_calibration = attotime::from_nsec(10800).as_double();

	// cache slot devices
	for (int i = 0; i <= 7; i++)
	{
		m_slotdevice[i] = m_a2bus->get_a2bus_card(i);
	}

	// setup video pointers
	m_video->set_ram_pointers(m_megaii_ram, &m_megaii_ram[0x10000]);
	m_video->set_char_pointer(memregion("gfx1")->base(), memregion("gfx1")->bytes());
	m_video->setup_GS_graphics();

	m_textcol = 0xf2;
	m_video->set_GS_foreground((m_textcol >> 4) & 0xf);
	m_video->set_GS_background(m_textcol & 0xf);

	m_inh_slot = -1;
	m_cnxx_slot = CNXX_UNCLAIMED;

	// adjust RAM size
	if (!m_is_rom3 && m_ram_size <= 1280 * 1024)
	{
		m_ram_size -= 0x20000;  // subtract 128k so requested RAM size matches exactly
	}
	// otherwise, RAM sizes for both classes of machine no longer include the Mega II RAM

	// setup save states
	save_item(NAME(m_speaker_state));
	save_item(NAME(m_joystick_x1_time));
	save_item(NAME(m_joystick_y1_time));
	save_item(NAME(m_joystick_x2_time));
	save_item(NAME(m_joystick_y2_time));
	save_item(NAME(m_inh_slot));
	save_item(NAME(m_inh_bank));
	save_item(NAME(m_cnxx_slot));
	save_item(NAME(m_romswitch));
	save_item(NAME(m_an0));
	save_item(NAME(m_an1));
	save_item(NAME(m_an2));
	save_item(NAME(m_an3));
	save_item(NAME(m_intcxrom));
	save_item(NAME(m_rombank));
	save_item(NAME(m_slotc3rom));
	save_item(NAME(m_altzp));
	save_item(NAME(m_ramrd));
	save_item(NAME(m_ramwrt));
	save_item(NAME(m_ioudis));
	save_item(NAME(m_vbl));
	save_item(NAME(m_irqmask));
	save_item(NAME(m_lcram));
	save_item(NAME(m_lcram2));
	save_item(NAME(m_lcprewrite));
	save_item(NAME(m_lcwriteenable));
	save_item(NAME(m_shadow));
	save_item(NAME(m_speed));
	save_item(NAME(m_textcol));
	save_item(NAME(m_clock_control));
	save_item(NAME(m_clkdata));
	save_item(NAME(m_clock_frame));
	save_item(NAME(m_motors_active));
	save_item(NAME(m_slotromsel));
	save_item(NAME(m_diskreg));
	save_item(NAME(m_sndglu_ctrl));
	save_item(NAME(m_sndglu_addr));
	save_item(NAME(m_sndglu_dummy_read));
	save_item(NAME(m_last_speed));
	save_item(NAME(m_glu_regs));
	save_item(NAME(m_glu_bus));
	save_item(NAME(m_glu_mcu_read_kgs));
	save_item(NAME(m_glu_816_read_dstat));
	save_item(NAME(m_glu_mouse_read_stat));
	save_item(NAME(m_glu_kbd_y));
	save_item(NAME(m_intflag));
	save_item(NAME(m_vgcint));
	save_item(NAME(m_inten));
	save_item(NAME(m_slot_irq));
	save_item(NAME(m_slow_counter));
	save_item(NAME(m_megaii_ram));
	save_item(m_clkdata, "CLKDATA");
	save_item(m_clock_control, "CLKCTRL");
	save_item(NAME(m_adb_p2_last));
	save_item(NAME(m_adb_p3_last));
	save_item(NAME(m_adb_reset_freeze));
	save_item(NAME(m_accel_unlocked));
	save_item(NAME(m_accel_stage));
	save_item(NAME(m_accel_fast));
	save_item(NAME(m_accel_present));
	save_item(NAME(m_accel_slotspk));
	save_item(NAME(m_accel_gsxsettings));
	save_item(NAME(m_accel_percent));
	save_item(NAME(m_accel_temp_slowdown));
	save_item(NAME(m_accel_speed));
	save_item(NAME(m_motoroff_time));
}

void apple2gs_state::machine_reset()
{
	m_adb_p2_last = m_adb_p3_last = 0;
	m_adb_reset_freeze = 0;
	m_romswitch = false;
	m_video->page2_w(false);
	m_video->set_GS_border(0x02);
	m_video->set_GS_background(0x02);
	m_video->set_GS_foreground(0x0f);
	m_an0 = m_an1 = m_an2 = m_an3 = false;
	m_gameio->an0_w(0);
	m_gameio->an1_w(0);
	m_gameio->an2_w(0);
	m_gameio->an3_w(0);
	m_vbl = false;
	m_slotc3rom = false;
	m_irqmask = 0;
	m_intcxrom = false;
	m_rombank = false;
	m_video->a80store_w(false);
	m_altzp = false;
	m_ramrd = false;
	m_ramwrt = false;
	m_ioudis = true;
	m_video->set_newvideo(0x01); // verified on ROM03 hardware
	m_clock_frame = 0;
	m_slot_irq = false;
	m_clkdata = 0;
	m_clock_control = 0;

	m_shadow = 0x00;
	m_speed = 0x80;
	m_motors_active = 0;
	m_diskreg = 0;
	m_intflag = 0;
	m_vgcint = 0;
	m_inten = 0;

	m_motoroff_time = 0;

	m_slow_counter = 0;

	// always assert full speed on reset
	m_maincpu->set_unscaled_clock(A2GS_14M/5);
	m_last_speed = true;

	m_sndglu_ctrl = 0;
	m_sndglu_addr = 0;
	m_sndglu_dummy_read = 0;

	m_maincpu_space = &m_maincpu->space(AS_PROGRAM);

	m_b0_0000bank.select(0);
	m_e0_0000bank.select(0);
	m_b0_0200bank.select(0);
	m_e0_0200bank.select(0);
	m_b0_0400bank.select(0);
	m_e0_0400bank.select(0);
	m_b0_0800bank.select(0);
	m_e0_0800bank.select(0);
	m_b0_2000bank.select(0);
	m_e0_2000bank.select(0);
	m_b0_4000bank.select(0);
	m_e0_4000bank.select(0);
	m_bank0_atc.select(1);
	m_bank1_atc.select(1);

	// LC default state: read ROM, write enabled, Dxxx bank 2
	m_lcram = false;
	m_lcram2 = true;
	m_lcprewrite = false;
	m_lcwriteenable = true;

	// sync up the banking with the variables.
	// RESEARCH: how does RESET affect LC state and aux banking states?
	auxbank_update();
	update_slotrom_banks();

	// reset the slots
	m_a2bus->reset_bus();

	// Apple-specific initial state
	m_scc->ctsa_w(0);
	m_scc->dcda_w(0);

	// with all the banking reset, now reset the CPU
	m_maincpu->reset();

	// Setup ZipGS
	m_accel_unlocked = false;
	m_accel_stage = 0;
	m_accel_slotspk = 0x41; // speaker and slot 6 slow
	m_accel_gsxsettings = 0;
	m_accel_percent = 0;    // 100% speed
	m_accel_present = false;
	m_accel_temp_slowdown = false;
	m_accel_fast = false;

	// is Zip enabled?
	if (m_sysconfig->read() & 0x01)
	{
		static const int speeds[4] = { 7000000, 8000000, 12000000, 16000000 };
		m_accel_present = true;
		int idxSpeed = (m_sysconfig->read() >> 1);
		m_accel_speed = speeds[idxSpeed];
		m_accel_fast = true;
		accel_full_speed();
	}
}

void apple2gs_state::raise_irq(int irq)
{
	if (!(m_irqmask & (1 << irq)))
	{
		m_irqmask |= (1 << irq);

		//printf("raise IRQ %d (mask %x)\n", irq, m_irqmask);

		if (m_irqmask)
		{
			m_intflag |= INTFLAG_IRQASSERTED;
			m_maincpu->set_input_line(G65816_LINE_IRQ, ASSERT_LINE);
		}
	}
}


void apple2gs_state::lower_irq(int irq)
{
	if (m_irqmask & (1 << irq))
	{
		m_irqmask &= ~(1 << irq);

		//printf("lower IRQ %d (mask %x)\n", irq, m_irqmask);

		if (!m_irqmask)
		{
			m_intflag &= ~INTFLAG_IRQASSERTED;
			m_maincpu->set_input_line(G65816_LINE_IRQ, CLEAR_LINE);
		}
		else
		{
			m_maincpu->set_input_line(G65816_LINE_IRQ, ASSERT_LINE);
		}
	}
}

void apple2gs_state::update_speed()
{
	bool isfast = false;

	if (m_speed & SPEED_HIGH)
	{
		isfast = true;
	}

	if ((m_motors_active & (m_speed & 0x0f)) != 0)
	{
		isfast = false;
	}

	// prevent unnecessary reschedules by only setting this if it changed
	if (isfast != m_last_speed)
	{
		if ((m_accel_present) && (isfast))
		{
			accel_full_speed();
		}
		else
		{
			m_maincpu->set_unscaled_clock(isfast ? A2GS_14M / 5 : A2GS_1M);
		}
		m_last_speed = isfast;
	}
}

void apple2gs_state::accel_slot(int slot)
{
	if ((m_accel_present) && (m_accel_slotspk & (1 << slot)))
	{
		m_accel_temp_slowdown = true;
		m_acceltimer->adjust(attotime::from_msec(52));
		accel_normal_speed();
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(apple2gs_state::accel_timer)
{
	if (m_accel_fast)
	{
		accel_full_speed();
	}
	m_accel_temp_slowdown = false;
	m_acceltimer->adjust(attotime::never);
}

/***************************************************************************
    VIDEO
***************************************************************************/

TIMER_DEVICE_CALLBACK_MEMBER(apple2gs_state::apple2_interrupt)
{
	int scanline = m_screen->vpos();
	m_screen->update_partial(scanline);

	/* check scanline interrupt bits if we're in super hi-res and the current scanline is within the active display area */
	if ((m_video->get_newvideo() & 0x80) && (scanline >= BORDER_TOP) && (scanline < (200+BORDER_TOP)))
	{
		u8 scb;
		const int shrline = scanline - BORDER_TOP;

		if (shrline & 1)
		{
			scb = m_megaii_ram[0x19e80 + (shrline >> 1)];
		}
		else
		{
			scb = m_megaii_ram[0x15e80 + (shrline >> 1)];
		}

		if (scb & 0x40)
		{
			// scanline int flag is set even when the actual interrupt is disabled
			m_vgcint |= VGCINT_SCANLINE;

			// see if the interrupt is also enabled and trigger it if so
			if (m_vgcint & VGCINT_SCANLINEEN)
			{
				m_vgcint |= VGCINT_ANYVGCINT;
				raise_irq(IRQS_SCAN);
			}
		}
	}

	if (scanline == (192+BORDER_TOP))
	{
		m_vbl = true;

		// VBL interrupt
		if ((m_inten & 0x08) && !(m_intflag & INTFLAG_VBL))
		{
			m_intflag |= INTFLAG_VBL;
			raise_irq(IRQS_VBL);
		}

		m_adbmicro->set_input_line(m5074x_device::M5074X_INT1_LINE, ASSERT_LINE);

		m_clock_frame++;

		// quarter second?
		if ((m_clock_frame % 15) == 0)
		{
			if ((m_inten & 0x10) && !(m_intflag & INTFLAG_QUARTER))
			{
				m_intflag |= INTFLAG_QUARTER;
				raise_irq(IRQS_QTRSEC);
			}
		}

		// 3.5 motor off timeout
		if (m_motoroff_time > 0)
		{
			m_motoroff_time--;
			if (m_motoroff_time == 0)
			{
				if (m_floppy[2]->get_device()) m_floppy[2]->get_device()->tfsel_w(0);
				if (m_floppy[3]->get_device()) m_floppy[3]->get_device()->tfsel_w(0);
			}
		}

		// one second
		if (m_clock_frame >= 60)
		{
			//printf("one sec, vgcint = %02x\n", m_vgcint);
			m_clock_frame = 0;

			if ((m_vgcint & VGCINT_SECONDENABLE) && !(m_vgcint & VGCINT_SECOND))
			{
				m_vgcint |= (VGCINT_SECOND|VGCINT_ANYVGCINT);
				raise_irq(IRQS_SECOND);
			}
		}
	}
	else if (scanline == (192+BORDER_TOP+1))
	{
		m_adbmicro->set_input_line(m5074x_device::M5074X_INT1_LINE, CLEAR_LINE);
	}
}

void apple2gs_state::palette_init(palette_device &palette)
{
	static const unsigned char apple2gs_palette[] =
	{
		0x0, 0x0, 0x0,  /* Black         $0              $0000 */
		0xD, 0x0, 0x3,  /* Deep Red      $1              $0D03 */
		0x0, 0x0, 0x9,  /* Dark Blue     $2              $0009 */
		0xD, 0x2, 0xD,  /* Purple        $3              $0D2D */
		0x0, 0x7, 0x2,  /* Dark Green    $4              $0072 */
		0x5, 0x5, 0x5,  /* Dark Gray     $5              $0555 */
		0x2, 0x2, 0xF,  /* Medium Blue   $6              $022F */
		0x6, 0xA, 0xF,  /* Light Blue    $7              $06AF */
		0x8, 0x5, 0x0,  /* Brown         $8              $0850 */
		0xF, 0x6, 0x0,  /* Orange        $9              $0F60 */
		0xA, 0xA, 0xA,  /* Light Gray    $A              $0AAA */
		0xF, 0x9, 0x8,  /* Pink          $B              $0F98 */
		0x1, 0xD, 0x0,  /* Light Green   $C              $01D0 */
		0xF, 0xF, 0x0,  /* Yellow        $D              $0FF0 */
		0x4, 0xF, 0x9,  /* Aquamarine    $E              $04F9 */
		0xF, 0xF, 0xF   /* White         $F              $0FFF */
	};

	for (int i = 0; i < 16; i++)
	{
		palette.set_pen_color(i,
			apple2gs_palette[(3*i)]*17,
			apple2gs_palette[(3*i)+1]*17,
			apple2gs_palette[(3*i)+2]*17);

		m_video->set_GS_border_color(i, rgb_t(apple2gs_palette[(3*i)]*17, apple2gs_palette[(3*i)+1]*17, apple2gs_palette[(3*i)+2]*17));
	}
}

u32 apple2gs_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return m_video->screen_update_GS(screen, bitmap, cliprect);
}

/***************************************************************************
    I/O
***************************************************************************/
void apple2gs_state::auxbank_update()
{
	int ramwr = (m_ramrd ? 1 : 0) | (m_ramwrt ? 2 : 0);

	m_b0_0000bank.select(m_altzp ? 1 : 0);
	m_e0_0000bank.select(m_altzp ? 1 : 0);
	m_b0_0200bank.select(ramwr);
	m_e0_0200bank.select(ramwr);

	if (m_video->get_80store())
	{
		if (m_video->get_page2())
		{
			m_b0_0400bank.select(3);
			m_e0_0400bank.select(3);
		}
		else
		{
			m_b0_0400bank.select(0);
			m_e0_0400bank.select(0);
		}
	}
	else
	{
		m_b0_0400bank.select(ramwr);
		m_e0_0400bank.select(ramwr);
	}

	m_b0_0800bank.select(ramwr);
	m_e0_0800bank.select(ramwr);

	if ((m_video->get_80store()) && (m_video->get_hires()))
	{
		if (m_video->get_page2())
		{
			m_b0_2000bank.select(3);
			m_e0_2000bank.select(3);
		}
		else
		{
			m_b0_2000bank.select(0);
			m_e0_2000bank.select(0);
		}
	}
	else
	{
		m_b0_2000bank.select(ramwr);
		m_e0_2000bank.select(ramwr);
	}

	m_b0_4000bank.select(ramwr);
	m_e0_4000bank.select(ramwr);
}

void apple2gs_state::update_slotrom_banks()
{
	//printf("update_slotrom_banks: intcxrom %d cnxx_slot %d SLOT %02x\n", m_intcxrom, m_cnxx_slot, m_slotromsel);

	// slot 3 ROM is controlled exclusively by SLOTC3ROM
	if (!m_slotc3rom)
	{
		m_c300bank->set_bank(1);
	}
	else
	{
		m_c300bank->set_bank(0);
	}
}

void apple2gs_state::lc_update(int offset, bool writing)
{
	bool old_lcram = m_lcram;

	// any even access disables pre-write and writing
	if ((offset & 1) == 0)
	{
		m_lcprewrite = false;
		m_lcwriteenable = false;
	}

	// any write disables pre-write
	// has no effect on write-enable if writing was enabled already
	if (writing == true)
	{
		m_lcprewrite = false;
	}
	// first odd read enables pre-write, second one enables writing
	else if ((offset & 1) == 1)
	{
		if (m_lcprewrite == false)
		{
			m_lcprewrite = true;
		}
		else
		{
			m_lcwriteenable = true;
		}
	}

	switch (offset & 3)
	{
		case 0:
		case 3:
		{
			m_lcram = true;
			break;
		}

		case 1:
		case 2:
		{
			m_lcram = false;
			break;
		}
	}

	m_lcram2 = false;

	if (!(offset & 8))
	{
		m_lcram2 = true;
	}

	if (m_lcram != old_lcram)
	{
		lcrom_update();
	}

	#if 0
	printf("LC: new state %c%c dxxx=%04x altzp=%d\n",
			m_lcram ? 'R' : 'x',
			m_lcwriteenable ? 'W' : 'x',
			m_lcram2 ? 0x1000 : 0x0000,
			m_altzp);
	#endif
}

void apple2gs_state::lcrom_update()
{
	if (m_lcram)
	{
		m_lcbank.select(1);
		m_lcaux.select(1);
		m_lc00.select(1 + (m_romswitch ? 2 : 0));
		m_lc01.select(1);
	}
	else
	{
		m_lcbank.select(0);
		m_lcaux.select(0);
		m_lc00.select(0 + (m_romswitch ? 2 : 0));
		m_lc01.select(0);
	}
}

// most softswitches don't care about read vs write, so handle them here
void apple2gs_state::do_io(int offset)
{
	if(machine().side_effects_disabled()) return;

	if (m_ioudis)
	{
		switch (offset)
		{
			case 0x5e:  // SETDHIRES
				m_video->dhires_w(0);
				return;

			case 0x5f:  // CLRDHIRES
				m_video->dhires_w(1);
				return;
		}
	}

	switch (offset)
	{
		case 0x20:
			break;

		case 0x28:  // ROMSWITCH - not used by the IIgs firmware or SSW, but does exist at least on ROM 0/1 (need to test on ROM 3 hw)
			if (!m_is_rom3)
			{
				m_romswitch = !m_romswitch;
				if (m_lcram)
				{
					m_lc00.select(1 + (m_romswitch ? 2 : 0));
				}
				else
				{
					m_lc00.select(0 + (m_romswitch ? 2 : 0));
				}
			}
			break;

		case 0x30:
			m_speaker_state ^= 1;
			if (m_speaker_state)
			{
				m_speaker->level_w(m_sndglu_ctrl & 0xf);
			}
			else
			{
				m_speaker->level_w(0);
			}

			if ((m_accel_present) && (m_accel_slotspk & 1))
			{
				m_accel_temp_slowdown = true;
				m_acceltimer->adjust(attotime::from_msec(5));
				accel_normal_speed();
			}
			break;

		case 0x50:  // graphics mode
			m_video->txt_w(0);
			break;

		case 0x51:  // text mode
			m_video->txt_w(1);
			break;

		case 0x52:  // no mix
			m_video->mix_w(0);
			break;

		case 0x53:  // mixed mode
			m_video->mix_w(1);
			break;

		case 0x54:  // set page 1
			m_video->scr_w(0);
			auxbank_update();
			break;

		case 0x55:  // set page 2
			m_video->scr_w(1);
			auxbank_update();
			break;

		case 0x56: // select lo-res
			m_video->res_w(0);
			auxbank_update();
			break;

		case 0x57: // select hi-res
			m_video->res_w(1);
			auxbank_update();
			break;

		case 0x58: // AN0 off
			m_an0 = false;
			m_gameio->an0_w(0);
			break;

		case 0x59: // AN0 on
			m_an0 = true;
			m_gameio->an0_w(1);
			break;

		case 0x5a: // AN1 off
			m_an1 = false;
			m_gameio->an1_w(0);
			break;

		case 0x5b: // AN1 on
			m_an1 = true;
			m_gameio->an1_w(1);
			break;

		case 0x5c: // AN2 off
			m_an2 = false;
			m_gameio->an2_w(0);
			break;

		case 0x5d: // AN2 on
			m_an2 = true;
			m_gameio->an2_w(1);
			break;

		case 0x5e: // AN3 off
			m_an3 = false;
			m_gameio->an3_w(0);
			break;

		case 0x5f: // AN3 on
			m_an3 = true;
			m_gameio->an3_w(1);
			break;

		case 0x68:  // STATE
			break;

		// trigger joypad read
		case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
		case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f:
			// Zip paddle slowdown (does ZipGS also use the old Zip flag?)
			if ((m_accel_present) && !BIT(m_accel_gsxsettings, 6))
			{
				m_accel_temp_slowdown = true;
				m_acceltimer->adjust(attotime::from_msec(5));
				accel_normal_speed();
			}

			// 558 monostable one-shot timers; a running timer cannot be restarted
			if (machine().time().as_double() >= m_joystick_x1_time)
			{
				m_joystick_x1_time = machine().time().as_double() + m_x_calibration * m_gameio->pdl0_r();
			}
			if (machine().time().as_double() >= m_joystick_y1_time)
			{
				m_joystick_y1_time = machine().time().as_double() + m_y_calibration * m_gameio->pdl1_r();
			}
			if (machine().time().as_double() >= m_joystick_x2_time)
			{
				m_joystick_x2_time = machine().time().as_double() + m_x_calibration * m_gameio->pdl2_r();
			}
			if (machine().time().as_double() >= m_joystick_y2_time)
			{
				m_joystick_y2_time = machine().time().as_double() + m_y_calibration * m_gameio->pdl3_r();
			}
			break;

		default:
			logerror("do_io: unknown switch $C0%02X\n", offset);
			break;
	}
}

// apple2gs_get_vpos - return the correct vertical counter value for the current scanline.
int apple2gs_state::get_vpos()
{
	// as per IIgs Tech Note #39, this is simply scanline + 250 on NTSC (262 lines),
	// or scanline + 200 on PAL (312 lines)
	int vpos = m_screen->vpos() + (511 - BORDER_TOP + 6);
	if (vpos > 511)
	{
		vpos -= (511 - 250);
	}
	return vpos;
}

void apple2gs_state::process_clock()
{
	for (int i = 0; i < 8; i++)
	{
		m_rtc->clk_w(ASSERT_LINE);
		if (!BIT(m_clock_control, 6))
		{
			m_rtc->data_w(BIT(m_clkdata, 7-i));
			m_rtc->clk_w(CLEAR_LINE);
		}
		else
		{
			m_rtc->clk_w(CLEAR_LINE);
			m_clkdata <<= 1;
			m_clkdata |= m_rtc->data_r() & 1;
		}
	}
}

u8 apple2gs_state::c000_r(offs_t offset)
{
	u8 ret;

	// allow ROM window at C07x to be debugger-visible
	if ((offset < 0x70) || (offset > 0x7f))
	{
		if(machine().side_effects_disabled()) return read_floatingbus();
	}

	slow_cycle();
	u8 uFloatingBus7 = read_floatingbus() & 0x7f;
	u8 uKeyboard = keyglu_816_read(GLU_C000);
	u8 uKeyboardC010 = keyglu_816_read(GLU_C010);

	switch (offset)
	{
		// keyboard latch
		case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06:
		case 0x07: case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d:
		case 0x0e: case 0x0f:
			return uKeyboard;

		case 0x10:  // read any key down, reset keyboard strobe
			keyglu_816_write(GLU_C010, 0);
			return uKeyboardC010 | (uKeyboard & 0x7f);

		case 0x11:  // read LCRAM2 (LC Dxxx bank)
			return (uKeyboardC010 & 0x7f) | (m_lcram2 ? 0x80 : 0x00);

		case 0x12:  // read LCRAM (is LC readable?)
			return (uKeyboardC010 & 0x7f) | (m_lcram ? 0x80 : 0x00);

		case 0x13:  // read RAMRD
			return (uKeyboardC010 & 0x7f) | (m_ramrd ? 0x80 : 0x00);

		case 0x14:  // read RAMWRT
			return (uKeyboardC010 & 0x7f) | (m_ramwrt ? 0x80 : 0x00);

		case 0x15:  // read INTCXROM
			return (uKeyboardC010 & 0x7f) | (m_intcxrom ? 0x80 : 0x00);

		case 0x16:  // read ALTZP
			return (uKeyboardC010 & 0x7f) | (m_altzp ? 0x80 : 0x00);

		case 0x17:  // read SLOTC3ROM
			return (uKeyboardC010 & 0x7f) | (m_slotc3rom ? 0x80 : 0x00);

		case 0x18:  // read 80STORE
			return (uKeyboardC010 & 0x7f) | (m_video->get_80store() ? 0x80 : 0x00);

		case 0x19:  // read VBL (not VBLBAR, see Apple IIGS Technical Note #40)
			return (uKeyboardC010 & 0x7f) | (m_screen->vblank() ? 0x80 : 0x00);

		case 0x1a:  // read TEXT
			return (uKeyboardC010 & 0x7f) | (m_video->get_graphics() ? 0x00 : 0x80);

		case 0x1b:  // read MIXED
			return (uKeyboardC010 & 0x7f) | (m_video->get_mix() ? 0x80 : 0x00);

		case 0x1c:  // read PAGE2
			return (uKeyboardC010 & 0x7f) | (m_video->get_page2() ? 0x80 : 0x00);

		case 0x1d:  // read HIRES
			return (uKeyboardC010 & 0x7f) | (m_video->get_hires() ? 0x80 : 0x00);

		case 0x1e:  // read ALTCHARSET
			return (uKeyboardC010 & 0x7f) | (m_video->get_altcharset() ? 0x80 : 0x00);

		case 0x1f:  // read 80COL
			return (uKeyboardC010 & 0x7f) | (m_video->get_80col() ? 0x80 : 0x00);

		case 0x22:  // TEXTCOL
			return m_textcol;

		case 0x23:  // VGCINT
			return m_vgcint;

		case 0x24:  // MOUSEDATA */
			return keyglu_816_read(GLU_MOUSEX);

		case 0x25:  // KEYMODREG
			return keyglu_816_read(GLU_KEYMOD);

		case 0x26:  // DATAREG
			return keyglu_816_read(GLU_DATA);

		case 0x27:  // KMSTATUS
			return keyglu_816_read(GLU_SYSSTAT);

		case 0x29:  // NEWVIDEO
			return m_video->get_newvideo();

		case 0x2b:  // LANGSEL
			return m_video->get_GS_langsel();

		case 0x2d:  // SLOTROMSEL
			return m_slotromsel;

		case 0x2e:  // VERTCNT
			return get_vpos() >> 1;

		case 0x2f:  // HORIZCNT
			ret = m_screen->hpos() / 11;
			if (ret > 0)
			{
				ret += 0x40;
			}

			if (get_vpos() & 1)
			{
				ret |= 0x80;
			}
			return ret;

		case 0x31:  // DISKREG
			return m_diskreg;

		case 0x32: // VGCINTCLEAR
			return 0;

		case 0x33: // CLOCKDATA
			return m_clkdata;

		case 0x34:  // BORDERCOL
			return (m_clock_control & 0xf0) | (m_video->get_GS_border() & 0xf);

		case 0x35:  // SHADOW
			return m_shadow;

		case 0x36:  // SPEED/CYAREG
			return m_speed;

		case 0x38:  // SCCBREG
			return m_scc->cb_r(0);

		case 0x39:  // SCCAREG
			return m_scc->ca_r(0);

		case 0x3a:  // SCCBDATA
			return m_scc->db_r(0);

		case 0x3b:  // SCCADATA
			return m_scc->da_r(0);

		case 0x3c:  // SOUNDCTL
			return m_sndglu_ctrl;

		case 0x3d:  // SOUNDDATA
			ret = m_sndglu_dummy_read;

			if (m_sndglu_ctrl & 0x40)    // docram access
			{
				m_sndglu_dummy_read = m_docram[m_sndglu_addr];
			}
			else
			{
				m_sndglu_dummy_read = m_doc->read(m_sndglu_addr);
			}

			if (m_sndglu_ctrl & 0x20)    // auto-increment
			{
				m_sndglu_addr++;
			}
			return ret;

		case 0x3e:  // SOUNDADRL
			return m_sndglu_addr & 0xff;

		case 0x3f:  // SOUNDADRH
			return (m_sndglu_addr >> 8) & 0xff;

		case 0x41:  // INTEN
			return m_inten;

		case 0x46:  // INTFLAG
			return (m_an3 ? INTFLAG_AN3 : 0x00) | m_intflag;

		case 0x47:  // CLRVBLINT
			m_intflag &= ~(INTFLAG_VBL|INTFLAG_QUARTER);
			lower_irq(IRQS_VBL);
			lower_irq(IRQS_QTRSEC);
			return read_floatingbus();

		case 0x60: // button 3 on IIgs
			return (m_gameio->sw3_r() ? 0x80 : 0x00) | uFloatingBus7;

		case 0x61: // button 0 or Open Apple
			// HACK/TODO: the 65816 loses a race to the microcontroller on reset
			if (m_adb_reset_freeze > 0) m_adb_reset_freeze--;
			return ((m_gameio->sw0_r() || (m_adb_p3_last & 0x20)) ? 0x80 : 0) | uFloatingBus7;

		case 0x62: // button 1 or Option
			return ((m_gameio->sw1_r() || (m_adb_p3_last & 0x10)) ? 0x80 : 0) | uFloatingBus7;

		case 0x63: // button 2 or SHIFT key
			return (m_gameio->sw2_r() ? 0x80 : 0x00) | uFloatingBus7;

		case 0x64:  // joy 1 X axis
			if (!m_gameio->is_device_connected()) return 0x80 | uFloatingBus7;
			return ((machine().time().as_double() < m_joystick_x1_time) ? 0x80 : 0) | uFloatingBus7;

		case 0x65:  // joy 1 Y axis
			if (!m_gameio->is_device_connected()) return 0x80 | uFloatingBus7;
			return ((machine().time().as_double() < m_joystick_y1_time) ? 0x80 : 0) | uFloatingBus7;

		case 0x66: // joy 2 X axis
			if (!m_gameio->is_device_connected()) return 0x80 | uFloatingBus7;
			return ((machine().time().as_double() < m_joystick_x2_time) ? 0x80 : 0) | uFloatingBus7;

		case 0x67: // joy 2 Y axis
			if (!m_gameio->is_device_connected()) return 0x80 | uFloatingBus7;
			return ((machine().time().as_double() < m_joystick_y2_time) ? 0x80 : 0) | uFloatingBus7;

		case 0x68: // STATEREG, synthesizes all the IIe state regs
			return  (m_altzp ? 0x80 : 0x00) |
					(m_video->get_page2() ? 0x40 : 0x00) |
					(m_ramrd ? 0x20 : 0x00) |
					(m_ramwrt ? 0x10 : 0x00) |
					(m_lcram ? 0x00 : 0x08) |
					(m_lcram2 ? 0x04 : 0x00) |
					(m_rombank ? 0x02 : 0x00) |
					(m_intcxrom ? 0x01 : 0x00);

		case 0x70:  // PTRIG - triggers paddles on read or write
			if (!machine().side_effects_disabled())
			{
				// Zip paddle slowdown (does ZipGS also use the old Zip flag?)
				if ((m_accel_present) && !BIT(m_accel_gsxsettings, 6))
				{
					m_accel_temp_slowdown = true;
					m_acceltimer->adjust(attotime::from_msec(5));
					accel_normal_speed();
				}

				// 558 monostable one-shot timers; a running timer cannot be restarted
				if (machine().time().as_double() >= m_joystick_x1_time)
				{
					m_joystick_x1_time = machine().time().as_double() + m_x_calibration * m_gameio->pdl0_r();
				}
				if (machine().time().as_double() >= m_joystick_y1_time)
				{
					m_joystick_y1_time = machine().time().as_double() + m_y_calibration * m_gameio->pdl1_r();
				}
				if (machine().time().as_double() >= m_joystick_x2_time)
				{
					m_joystick_x2_time = machine().time().as_double() + m_x_calibration * m_gameio->pdl2_r();
				}
				if (machine().time().as_double() >= m_joystick_y2_time)
				{
					m_joystick_y2_time = machine().time().as_double() + m_y_calibration * m_gameio->pdl3_r();
				}

			}

			return m_rom[offset + 0x3c000];

		// The ROM IRQ vectors point here
		case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
		case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f:
			return m_rom[offset + 0x3c000];

		default:
			do_io(offset);

			if (m_accel_unlocked)
			{
				if (offset == 0x59)
				{
					return m_accel_gsxsettings;
				}
				else if (offset == 0x5a)
				{
					return m_accel_percent | 0x0f;
				}
				else if (offset == 0x5b)
				{
					// bit 7 is a 1.0035 millisecond clock; the value changes every 0.50175 milliseconds
					const int time = machine().time().as_ticks(1.0f / 0.00050175f);
					if (time & 1)
					{
						return 0x03;
					}
					else
					{
						return 0x83;
					}
				}
				else if (offset == 0x5c)
				{
					return m_accel_slotspk;
				}
			}
			break;
	}

	// assume all $C00X returns the keyboard, like on the IIe
	if ((offset & 0xf0) == 0x00)
	{
		return uKeyboard;
	}

	return read_floatingbus();
}

void apple2gs_state::c000_w(offs_t offset, u8 data)
{
	if(machine().side_effects_disabled()) return;

	slow_cycle();

	switch (offset)
	{
		case 0x00:  // 80STOREOFF
			m_video->a80store_w(false);
			auxbank_update();
			break;

		case 0x01:  // 80STOREON
			m_video->a80store_w(true);
			auxbank_update();
			break;

		case 0x02:  // RAMRDOFF
			m_ramrd = false;
			auxbank_update();
			break;

		case 0x03:  // RAMRDON
			m_ramrd = true;
			auxbank_update();
			break;

		case 0x04:  // RAMWRTOFF
			m_ramwrt = false;
			auxbank_update();
			break;

		case 0x05:  // RAMWRTON
			m_ramwrt = true;
			auxbank_update();
			break;

		case 0x06:  // INTCXROMOFF
			m_intcxrom = false;
			update_slotrom_banks();
			break;

		case 0x07:  // INTCXROMON
			m_intcxrom = true;
			update_slotrom_banks();
			break;

		case 0x08:  // ALTZPOFF
			m_altzp = false;
			auxbank_update();
			break;

		case 0x09:  // ALTZPON
			m_altzp = true;
			auxbank_update();
			break;

		case 0x0a:  // SETINTC3ROM
			m_slotc3rom = false;
			update_slotrom_banks();
			break;

		case 0x0b:  // SETSLOTC3ROM
			m_slotc3rom = true;
			update_slotrom_banks();
			break;

		case 0x0c:  // 80COLOFF
			m_video->a80col_w(false);
			break;

		case 0x0d:  // 80COLON
			m_video->a80col_w(true);
			break;

		case 0x0e:  // ALTCHARSETOFF
			m_video->altcharset_w(false);
			break;

		case 0x0f:  // ALTCHARSETON
			m_video->altcharset_w(true);
			break;

		case 0x10:  // clear keyboard latch
			keyglu_816_write(GLU_C010, data);
			break;

		case 0x20:
			break;

		case 0x21:  // MONOCHROME
			m_video->set_GS_monochrome(data);
			break;

		case 0x22:  // TEXTCOL
			if (m_textcol != data)
			{
				m_screen->update_now();
			}
			m_textcol = data;
			m_video->set_GS_foreground((data >> 4) & 0xf);
			m_video->set_GS_background(data & 0xf);
			break;

		case 0x23:  // VGCINT
			if ((m_vgcint & VGCINT_SECOND) && !(data & VGCINT_SECONDENABLE))
			{
				lower_irq(IRQS_SECOND);
				m_vgcint &= ~(VGCINT_SECOND);
			}
			if ((m_vgcint & VGCINT_SCANLINE) && !(data & VGCINT_SCANLINEEN))
			{
				lower_irq(IRQS_SCAN);
				m_vgcint &= ~(VGCINT_SCANLINE);
			}

			if (!(m_vgcint & (VGCINT_SECOND|VGCINT_SCANLINE)))
			{
				m_vgcint &= ~(VGCINT_ANYVGCINT);
			}

			m_vgcint &= 0xf0;
			m_vgcint |= (data & 0x0f);
			//printf("%02x to VGCINT, now %02x\n", data, m_vgcint);
			break;

		case 0x26:  // DATAREG
			// allow ADBuC sync if the Zip is on
			if (m_accel_present)
			{
				m_accel_temp_slowdown = true;
				m_acceltimer->adjust(attotime::from_msec(30));
				accel_normal_speed();
			}
			keyglu_816_write(GLU_COMMAND, data);
			break;

		case 0x27:  // KMSTATUS
			keyglu_816_write(GLU_SYSSTAT, data);
			break;

		case 0x29:  // NEWVIDEO
			m_video->set_newvideo(data);
			break;

		case 0x2b:  // LANGSEL
			m_video->set_GS_langsel(data);
			break;

		case 0x2d:  // SLOTROMSEL
			m_slotromsel = data;
			break;
		case 0x31:  //
			if (!BIT(data, DISKREG_35SEL))
			{
				m_motoroff_time = 30;
			}

			if ((m_cur_floppy) && (BIT(data, DISKREG_35SEL)))
			{
				m_cur_floppy->ss_w(BIT(data, DISKREG_HDSEL));
			}
			m_diskreg = data;
			break;

		case 0x32:  // VGCINTCLEAR
			//printf("%02x to VGCINTCLEAR\n", data);
			// one second
			if (m_vgcint & VGCINT_SECOND)
			{
				lower_irq(IRQS_SECOND);
				m_vgcint &= ~(VGCINT_SECOND|VGCINT_ANYVGCINT);
			}

			// scanline
			if (m_vgcint & VGCINT_SCANLINE)
			{
				lower_irq(IRQS_SCAN);
				m_vgcint &= ~(VGCINT_SCANLINE|VGCINT_ANYVGCINT);
			}

			if (m_irqmask & ((1<<IRQS_SECOND) | (1<<IRQS_SCAN)))
			{
				m_vgcint |= VGCINT_ANYVGCINT;
			}
			break;

		case 0x33:  // CLOCKDATA
			m_clkdata = data;
			break;

		case 0x34:  // CLOCKCTL
			if ((data & 0xf) != (m_video->get_GS_border() & 0xf))
			{
				m_screen->update_now();
			}
			m_clock_control = data & 0x7f;
			m_video->set_GS_border(data & 0xf);
			m_rtc->ce_w(BIT(data, 7) ^ 1);
			if (data & 0x80)
			{
				process_clock();
			}
			break;

		case 0x35:  // SHADOW
			m_shadow = data;

			// handle I/O and language card inhibit bits here
			if (m_shadow & SHAD_IOLC)
			{
				m_bank0_atc.select(0);
				m_bank1_atc.select(0);
			}
			else
			{
				m_bank0_atc.select(1);
				m_bank1_atc.select(1);

			}
			break;

		case 0x36:  // SPEED
			m_speed = data;

			if (m_speed & SPEED_ALLBANKS)
			{
				logerror("apple2gs: Driver does not support shadowing in all banks\n");
			}
			update_speed();
			break;

		case 0x38:  // SCCBREG
			m_scc->cb_w(0, data);
			break;

		case 0x39:  // SCCAREG
			m_scc->ca_w(0, data);
			break;

		case 0x3a:  // SCCBDATA
			m_scc->db_w(0, data);
			break;

		case 0x3b:  // SCCADATA
			m_scc->da_w(0, data);
			break;

		case 0x3c:  // SOUNDCTL
			m_sndglu_ctrl = data & 0x7f; // make sure DOC is never busy
			if (!(m_sndglu_ctrl & 0x40)) // clear hi byte of address pointer on DOC access
			{
				m_sndglu_addr &= 0xff;
			}
			break;

		case 0x3d:  // SOUNDDATA
			if (m_sndglu_ctrl & 0x40)    // docram access
			{
				m_docram[m_sndglu_addr] = data;
			}
			else
			{
				m_doc->write(m_sndglu_addr, data);
			}

			if (m_sndglu_ctrl & 0x20)    // auto-increment
			{
				m_sndglu_addr++;
			}
			break;

		case 0x3e:  // SOUNDADRL
			m_sndglu_addr &= 0xff00;
			m_sndglu_addr |= data;
			break;

		case 0x3f:  // SOUNDADRH
			m_sndglu_addr &= 0x00ff;
			m_sndglu_addr |= data<<8;
			break;

		case 0x41:  // INTEN
			m_inten = data & 0x1f;
			if (!(data & 0x10))
			{
				m_intflag &= ~INTFLAG_QUARTER;
				lower_irq(IRQS_QTRSEC);
			}
			if (!(data & 0x08))
			{
				m_intflag &= ~INTFLAG_VBL;
				lower_irq(IRQS_VBL);
			}
			//printf("%02x to INTEN, now %02x\n", data, m_vgcint);
			break;

		case 0x47:  // CLRVBLINT
			m_intflag &= ~(INTFLAG_VBL|INTFLAG_QUARTER);
			lower_irq(IRQS_VBL);
			lower_irq(IRQS_QTRSEC);
			break;

		case 0x59: // Zip GS-specific settings
			if (m_accel_unlocked)
			{
				m_accel_gsxsettings = data & 0xf8;
				m_accel_gsxsettings |= 0x01;    // indicate this is a GS
			}
			break;

		case 0x5a: // Zip accelerator unlock
			if (m_sysconfig->read() & 0x01)
			{
				if (data == 0x5a)
				{
					m_accel_stage++;
					if (m_accel_stage == 4)
					{
						m_accel_unlocked = true;
					}
				}
				else if (data == 0xa5)
				{
					// lock
					m_accel_unlocked = false;
					m_accel_stage = 0;
				}
				else if (m_accel_unlocked)
				{
					// disable acceleration
					accel_normal_speed();
					m_accel_unlocked = false;
					m_accel_stage = 0;
				}
			}
			do_io(offset);
			break;

		case 0x5b: // Zip full speed
			if (m_accel_unlocked)
			{
				accel_full_speed();
			}
			do_io(offset);
			break;

		case 0x5c: // Zip slot/speaker flags
			if (m_accel_unlocked)
			{
				m_accel_slotspk = data;
			}
			do_io(offset);
			break;

		case 0x5d: // Zip speed percentage
			if (m_accel_unlocked)
			{
				m_accel_percent = data;
			}
			break;

		case 0x68: // STATEREG
			m_altzp = (data & 0x80);
			m_video->page2_w(data & 0x40);
			m_ramrd = (data & 0x20);
			m_ramwrt = (data & 0x10);
			m_lcram = (data & 0x08) ? false : true;
			m_lcram2 = (data & 0x04);
			m_rombank = (data & 0x02);
			m_intcxrom = (data & 0x01);

			// update the aux state
			auxbank_update();

			// update LC state
			if (m_lcram)
			{
				m_lcbank.select(1);
				m_lcaux.select(1);
				m_lc00.select(1);
				m_lc01.select(1);
			}
			else
			{
				m_lcbank.select(0);
				m_lcaux.select(0);
				m_lc00.select(0);
				m_lc01.select(0);
			}
			break;

		case 0x70:
		case 0x71:
		case 0x72:
		case 0x73:
		case 0x74:
		case 0x75:
		case 0x76:
		case 0x77:
		case 0x78:
		case 0x79:
		case 0x7a:
		case 0x7b:
		case 0x7c:
		case 0x7d:
			do_io(offset); // make sure it also side-effect resets the paddles as documented
			break;

		case 0x7e: // SETIOUDIS
			m_ioudis = true;
			break;

		case 0x7f: // CLRIOUDIS
			m_ioudis = false;
			break;

		default:
			do_io(offset);
			break;
		}
}

u8 apple2gs_state::c080_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
	{
		int slot;

		slow_cycle();

		offset &= 0x7f;
		slot = offset / 0x10;

		if (slot == 0)
		{
			lc_update(offset & 0xf, false);
		}
		else
		{
			if (m_accel_present)
			{
				accel_slot(slot);
			}

			if (slot >= 4)
			{
				if ((offset & 0xf) == 0x9)
				{
					m_motors_active |= (1 << (slot - 4));
				}
				else if ((offset & 0xf) == 8)
				{
					m_motors_active &= ~(1 << (slot - 4));
				}

				update_speed();
			}

			// slot 3 always has I/O go to the external card
			if ((slot != 3) && ((m_slotromsel & (1 << slot)) == 0))
			{
				if (slot == 6)
				{
					return m_iwm->read(offset & 0xf);
				}
			}
			else
			{
				if (m_slotdevice[slot] != nullptr)
				{
					return m_slotdevice[slot]->read_c0nx(offset % 0x10);
				}
			}
		}
	}

	return read_floatingbus();
}

void apple2gs_state::c080_w(offs_t offset, u8 data)
{
	int slot;

	slow_cycle();

	offset &= 0x7f;
	slot = offset / 0x10;

	if (slot == 0)
	{
		lc_update(offset & 0xf, true);
	}
	else
	{
		if (slot >= 4)
		{
			if ((offset & 0xf) == 0x9)
			{
				m_motors_active |= (1 << (slot - 4));
			}
			else if ((offset & 0xf) == 8)
			{
				m_motors_active &= ~(1 << (slot - 4));
			}

			update_speed();
		}

		// slot 3 always has I/O go to the external card
		if ((slot != 3) && ((m_slotromsel & (1 << slot)) == 0))
		{
			if (slot == 6)
			{
				m_iwm->write(offset & 0xf, data);
			}
		}
		else
		{
			if (m_slotdevice[slot] != nullptr)
			{
				m_slotdevice[slot]->write_c0nx(offset % 0x10, data);
			}
		}
	}
}

u8 apple2gs_state::read_slot_rom(int slotbias, int offset)
{
	const int slotnum = ((offset>>8) & 0xf) + slotbias;

//  printf("read_slot_rom: sl %d offs %x, cnxx_slot %d\n", slotnum, offset, m_cnxx_slot);

	if (m_slotdevice[slotnum] != nullptr)
	{
//      printf("slotdevice is not null\n");
		if ((m_cnxx_slot == CNXX_UNCLAIMED) && (m_slotdevice[slotnum]->take_c800()) && (!machine().side_effects_disabled()))
		{
			m_cnxx_slot = slotnum;
			update_slotrom_banks();
		}

		return m_slotdevice[slotnum]->read_cnxx(offset&0xff);
	}

	return read_floatingbus();
}

void apple2gs_state::write_slot_rom(int slotbias, int offset, u8 data)
{
	const int slotnum = ((offset>>8) & 0xf) + slotbias;

	slow_cycle();

	if (m_slotdevice[slotnum] != nullptr)
	{
		if ((m_cnxx_slot == CNXX_UNCLAIMED) && (m_slotdevice[slotnum]->take_c800()) && (!machine().side_effects_disabled()))
		{
			m_cnxx_slot = slotnum;
			update_slotrom_banks();
		}

		m_slotdevice[slotnum]->write_cnxx(offset&0xff, data);
	}
}

u8 apple2gs_state::read_int_rom(int slotbias, int offset)
{
	if ((m_cnxx_slot == CNXX_UNCLAIMED) && (!machine().side_effects_disabled()))
	{
		m_cnxx_slot = CNXX_INTROM;
		update_slotrom_banks();
	}

	return m_rom[slotbias + offset];
}

u8 apple2gs_state::c100_r(offs_t offset)
{
	const int slot = ((offset>>8) & 0xf) + 1;

	slow_cycle();

	accel_slot(slot);

	// SETSLOTCXROM is disabled, so the $C02D SLOT register controls what's in each slot
	if (!(m_slotromsel & (1 << slot)))
	{
		return read_int_rom(0x3c100, offset);
	}

	return read_slot_rom(1, offset);
}

void apple2gs_state::c100_w(offs_t offset, u8 data)
{
	const int slot = ((offset>>8) & 0xf) + 1;

	accel_slot(slot);

	slow_cycle();

	if ((m_slotromsel & (1 << slot)))
	{
		write_slot_rom(1, offset, data);
	}
}

u8 apple2gs_state::c300_int_r(offs_t offset)  { accel_slot(3 + ((offset >> 8) & 0x7)); slow_cycle(); return read_int_rom(0x3c300, offset); }
u8 apple2gs_state::c300_r(offs_t offset)  { accel_slot(3 + ((offset >> 8) & 0x7)); slow_cycle(); return read_slot_rom(3, offset); }
void apple2gs_state::c300_w(offs_t offset, u8 data) { accel_slot(3 + ((offset >> 8) & 0x7)); slow_cycle(); write_slot_rom(3, offset, data); }

u8 apple2gs_state::c400_r(offs_t offset)
{
	const int slot = ((offset>>8) & 0xf) + 4;

	accel_slot(slot);

	slow_cycle();

	if (!(m_slotromsel & (1 << slot)))
	{
		return read_int_rom(0x3c400, offset);
	}

	return read_slot_rom(4, offset);
}

void apple2gs_state::c400_w(offs_t offset, u8 data)
{
	const int slot = ((offset>>8) & 0xf) + 4;

	accel_slot(slot);

	slow_cycle();

	if ((m_slotromsel & (1 << slot)))
	{
		write_slot_rom(4, offset, data);
	}
}

u8 apple2gs_state::c800_r(offs_t offset)
{
	slow_cycle();

	if ((offset == 0x7ff) && !machine().side_effects_disabled())
	{
		m_cnxx_slot = CNXX_UNCLAIMED;
		update_slotrom_banks();
		return 0xff;
	}

	if (m_cnxx_slot == CNXX_INTROM)
	{
		return m_rom[offset + 0x3c800];
	}

	if ((m_cnxx_slot > 0) && (m_slotdevice[m_cnxx_slot] != nullptr))
	{
		return m_slotdevice[m_cnxx_slot]->read_c800(offset&0xfff);
	}

	return 0xff;
}

void apple2gs_state::c800_w(offs_t offset, u8 data)
{
	slow_cycle();

	if ((m_cnxx_slot > 0) && (m_slotdevice[m_cnxx_slot] != nullptr))
	{
		m_slotdevice[m_cnxx_slot]->write_c800(offset&0xfff, data);
	}

	if (offset == 0x7ff)
	{
		m_cnxx_slot = CNXX_UNCLAIMED;
		update_slotrom_banks();
		return;
	}
}

/* for < 14MB RAM, returns the bank number on reads >= 8MB */
u8 apple2gs_state::expandedram_r(offs_t offset)
{
	offset += 0x020000;

	if (offset >= m_ram_size)
	{
		if (offset >= 0x800000)
		{
			return offset >> 16;
		}

		return 0;
	}

	return m_ram_ptr[offset];
}

void apple2gs_state::expandedram_w(offs_t offset, u8 data)
{
	offset += 0x20000;

	if (offset < m_ram_size)
	{
		m_ram_ptr[offset] = data;
	}
}

u8 apple2gs_state::inh_r(offs_t offset)
{
	if (m_inh_slot != -1)
	{
		return m_slotdevice[m_inh_slot]->read_inh_rom(offset + 0xd000);
	}

	assert(0);  // hitting inh_r with invalid m_inh_slot should not be possible
	return read_floatingbus();
}

void apple2gs_state::inh_w(offs_t offset, u8 data)
{
	if (m_inh_slot != -1)
	{
		m_slotdevice[m_inh_slot]->write_inh_rom(offset + 0xd000, data);
	}
}

u8 apple2gs_state::lc_r(offs_t offset)
{
	slow_cycle();
	if (m_altzp)
	{
		if (offset < 0x1000)
		{
			if (m_lcram2)
			{
				return m_megaii_ram[(offset & 0xfff) + 0x1c000];
			}
			else
			{
				return m_megaii_ram[(offset & 0xfff) + 0x1d000];
			}
		}

		return m_megaii_ram[(offset & 0x1fff) + 0x1e000];
	}
	else
	{
		if (offset < 0x1000)
		{
			if (m_lcram2)
			{
				return m_megaii_ram[(offset & 0xfff) + 0xc000];
			}
			else
			{
				return m_megaii_ram[(offset & 0xfff) + 0xd000];
			}
		}

		return m_megaii_ram[(offset & 0x1fff) + 0xe000];
	}
}

void apple2gs_state::lc_w(offs_t offset, u8 data)
{
	slow_cycle();
	if (!m_lcwriteenable)
	{
		return;
	}

	if (m_altzp)
	{
		if (offset < 0x1000)
		{
			if (m_lcram2)
			{
				m_megaii_ram[(offset & 0xfff) + 0x1c000] = data;
			}
			else
			{
				m_megaii_ram[(offset & 0xfff) + 0x1d000] = data;
			}
			return;
		}

		m_megaii_ram[(offset & 0x1fff) + 0x1e000] = data;
	}
	else
	{
		if (offset < 0x1000)
		{
			if (m_lcram2)
			{
				m_megaii_ram[(offset & 0xfff) + 0xc000] = data;
			}
			else
			{
				m_megaii_ram[(offset & 0xfff) + 0xd000] = data;
			}
			return;
		}

		m_megaii_ram[(offset & 0x1fff) + 0xe000] = data;
	}
}

u8 apple2gs_state::lc_aux_r(offs_t offset)
{
	slow_cycle();
	if (offset < 0x1000)
	{
		if (m_lcram2)
		{
			return m_megaii_ram[(offset & 0xfff) + 0x1c000];
		}
		else
		{
			return m_megaii_ram[(offset & 0xfff) + 0x1d000];
		}
	}

	return m_megaii_ram[(offset & 0x1fff) + 0x1e000];
}

void apple2gs_state::lc_aux_w(offs_t offset, u8 data)
{
	slow_cycle();
	if (!m_lcwriteenable)
	{
		return;
	}

	if (offset < 0x1000)
	{
		if (m_lcram2)
		{
			m_megaii_ram[(offset & 0xfff) + 0x1c000] = data;
		}
		else
		{
			m_megaii_ram[(offset & 0xfff) + 0x1d000] = data;
		}
		return;
	}

	m_megaii_ram[(offset & 0x1fff) + 0x1e000] = data;
}

u8 apple2gs_state::lc_00_r(offs_t offset)
{
	if (m_altzp)
	{
		if (offset < 0x1000)
		{
			if (m_lcram2)
			{
				return m_ram_ptr[(offset & 0xfff) + 0x1c000];
			}
			else
			{
				return m_ram_ptr[(offset & 0xfff) + 0x1d000];
			}
		}

		return m_ram_ptr[(offset & 0x1fff) + 0x1e000];
	}
	else
	{
		if (offset < 0x1000)
		{
			if (m_lcram2)
			{
				return m_ram_ptr[(offset & 0xfff) + 0xc000];
			}
			else
			{
				return m_ram_ptr[(offset & 0xfff) + 0xd000];
			}
		}

		return m_ram_ptr[(offset & 0x1fff) + 0xe000];
	}
}

void apple2gs_state::lc_00_w(offs_t offset, u8 data)
{
	if (!m_lcwriteenable)
	{
		return;
	}

	if (m_altzp)
	{
		if (offset < 0x1000)
		{
			if (m_lcram2)
			{
				m_ram_ptr[(offset & 0xfff) + 0x1c000] = data;
			}
			else
			{
				m_ram_ptr[(offset & 0xfff) + 0x1d000] = data;
			}
			return;
		}

		m_ram_ptr[(offset & 0x1fff) + 0x1e000] = data;
	}
	else
	{
		if (offset < 0x1000)
		{
			if (m_lcram2)
			{
				m_ram_ptr[(offset & 0xfff) + 0xc000] = data;
			}
			else
			{
				m_ram_ptr[(offset & 0xfff) + 0xd000] = data;
			}
			return;
		}

		m_ram_ptr[(offset & 0x1fff) + 0xe000] = data;
	}
}

u8 apple2gs_state::lc_01_r(offs_t offset)
{
	if (offset < 0x1000)
	{
		if (m_lcram2)
		{
			return m_ram_ptr[(offset & 0xfff) + 0x1c000];
		}
		else
		{
			return m_ram_ptr[(offset & 0xfff) + 0x1d000];
		}
	}

	return m_ram_ptr[(offset & 0x1fff) + 0x1e000];
}

void apple2gs_state::lc_01_w(offs_t offset, u8 data)
{
	if (!m_lcwriteenable)
	{
		return;
	}

	if (offset < 0x1000)
	{
		if (m_lcram2)
		{
			m_ram_ptr[(offset & 0xfff) + 0x1c000] = data;
		}
		else
		{
			m_ram_ptr[(offset & 0xfff) + 0x1d000] = data;
		}
		return;
	}

	m_ram_ptr[(offset & 0x1fff) + 0x1e000] = data;
}

// floating bus code from old machine/apple2: needs to be reworked based on real beam position to enable e.g. Bob Bishop's screen splitter
u8 apple2gs_state::read_floatingbus()
{
	enum
	{
		// scanner types
		kScannerNone = 0, kScannerApple2, kScannerApple2e,

		// scanner constants
		kHBurstClock      =    53, // clock when Color Burst starts
		kHBurstClocks     =     4, // clocks per Color Burst duration
		kHClock0State     =  0x18, // H[543210] = 011000
		kHClocks          =    65, // clocks per horizontal scan (including HBL)
		kHPEClock         =    40, // clock when HPE (horizontal preset enable) goes low
		kHPresetClock     =    41, // clock when H state presets
		kHSyncClock       =    49, // clock when HSync starts
		kHSyncClocks      =     4, // clocks per HSync duration
		kNTSCScanLines    =   262, // total scan lines including VBL (NTSC)
		kNTSCVSyncLine    =   224, // line when VSync starts (NTSC)
		kPALScanLines     =   312, // total scan lines including VBL (PAL)
		kPALVSyncLine     =   264, // line when VSync starts (PAL)
		kVLine0State      = 0x100, // V[543210CBA] = 100000000
		kVPresetLine      =   256, // line when V state presets
		kVSyncLines       =     4, // lines per VSync duration
		kClocksPerVSync   = kHClocks * kNTSCScanLines // FIX: NTSC only?
	};

	// vars
	//
	int i, Hires, Mixed, Page2, _80Store, ScanLines, /* VSyncLine, ScanCycles,*/
		h_clock, h_state, h_0, h_1, h_2, h_3, h_4, h_5,
		v_line, v_state, v_A, v_B, v_C, v_0, v_1, v_2, v_3, v_4, /* v_5, */
		_hires, addend0, addend1, addend2, sum, address;

	// video scanner data
	//
	i = m_maincpu->total_cycles() % kClocksPerVSync; // cycles into this VSync

	// machine state switches
	//
	Hires    = (m_video->get_hires() && m_video->get_graphics()) ? 1 : 0;
	Mixed    = m_video->get_mix() ? 1 : 0;
	Page2    = m_video->get_page2() ? 1 : 0;
	_80Store = m_video->get_80store() ? 1 : 0;

	// calculate video parameters according to display standard
	//
	ScanLines  = 1 ? kNTSCScanLines : kPALScanLines; // FIX: NTSC only?
	// VSyncLine  = 1 ? kNTSCVSyncLine : kPALVSyncLine; // FIX: NTSC only?
	// ScanCycles = ScanLines * kHClocks;

	// calculate horizontal scanning state
	//
	h_clock = (i + kHPEClock) % kHClocks; // which horizontal scanning clock
	h_state = kHClock0State + h_clock; // H state bits
	if (h_clock >= kHPresetClock) // check for horizontal preset
	{
		h_state -= 1; // correct for state preset (two 0 states)
	}
	h_0 = (h_state >> 0) & 1; // get horizontal state bits
	h_1 = (h_state >> 1) & 1;
	h_2 = (h_state >> 2) & 1;
	h_3 = (h_state >> 3) & 1;
	h_4 = (h_state >> 4) & 1;
	h_5 = (h_state >> 5) & 1;

	// calculate vertical scanning state
	//
	v_line  = i / kHClocks; // which vertical scanning line
	v_state = kVLine0State + v_line; // V state bits
	if ((v_line >= kVPresetLine)) // check for previous vertical state preset
	{
		v_state -= ScanLines; // compensate for preset
	}
	v_A = (v_state >> 0) & 1; // get vertical state bits
	v_B = (v_state >> 1) & 1;
	v_C = (v_state >> 2) & 1;
	v_0 = (v_state >> 3) & 1;
	v_1 = (v_state >> 4) & 1;
	v_2 = (v_state >> 5) & 1;
	v_3 = (v_state >> 6) & 1;
	v_4 = (v_state >> 7) & 1;
	//v_5 = (v_state >> 8) & 1;

	// calculate scanning memory address
	//
	_hires = Hires;
	if (Hires && Mixed && (v_4 & v_2))
	{
		_hires = 0; // (address is in text memory)
	}

	addend0 = 0x68; // 1            1            0            1
	addend1 =              (h_5 << 5) | (h_4 << 4) | (h_3 << 3);
	addend2 = (v_4 << 6) | (v_3 << 5) | (v_4 << 4) | (v_3 << 3);
	sum     = (addend0 + addend1 + addend2) & (0x0F << 3);

	address = 0;
	address |= h_0 << 0; // a0
	address |= h_1 << 1; // a1
	address |= h_2 << 2; // a2
	address |= sum;      // a3 - aa6
	address |= v_0 << 7; // a7
	address |= v_1 << 8; // a8
	address |= v_2 << 9; // a9
	address |= ((_hires) ? v_A : (1 ^ (Page2 & (1 ^ _80Store)))) << 10; // a10
	address |= ((_hires) ? v_B : (Page2 & (1 ^ _80Store))) << 11; // a11
	if (_hires) // hires?
	{
		// Y: insert hires only address bits
		//
		address |= v_C << 12; // a12
		address |= (1 ^ (Page2 & (1 ^ _80Store))) << 13; // a13
		address |= (Page2 & (1 ^ _80Store)) << 14; // a14
	}
	else
	{
		// N: text, so no higher address bits unless Apple ][, not Apple //e
		//
		if ((1) && // Apple ][? // FIX: check for Apple ][? (FB is most useful in old games)
			(kHPEClock <= h_clock) && // Y: HBL?
			(h_clock <= (kHClocks - 1)))
		{
			address |= 1 << 12; // Y: a12 (add $1000 to address!)
		}
	}

	return m_megaii_ram[address % m_ram_size]; // FIX: this seems to work, but is it right!?
}

/***************************************************************************
    ADDRESS MAP
***************************************************************************/

template <int Addr>
u8 apple2gs_state::e0ram_r(offs_t offset) { slow_cycle(); return m_megaii_ram[offset + Addr]; }

template <int Addr>
void apple2gs_state::e0ram_w(offs_t offset, u8 data) { slow_cycle(); m_megaii_ram[offset + Addr] = data; }

template <int Addr>
u8 apple2gs_state::e1ram_r(offs_t offset) { slow_cycle(); return m_megaii_ram[offset + Addr + 0x10000]; }

template <int Addr>
void apple2gs_state::e1ram_w(offs_t offset, u8 data) { slow_cycle(); m_megaii_ram[offset + Addr + 0x10000] = data; }

template u8 apple2gs_state::e0ram_r<0x0000>(offs_t offset);
template u8 apple2gs_state::e0ram_r<0x0200>(offs_t offset);
template u8 apple2gs_state::e0ram_r<0x0400>(offs_t offset);
template u8 apple2gs_state::e0ram_r<0x0800>(offs_t offset);
template u8 apple2gs_state::e0ram_r<0x2000>(offs_t offset);
template u8 apple2gs_state::e0ram_r<0x4000>(offs_t offset);

template u8 apple2gs_state::e1ram_r<0x0000>(offs_t offset);
template u8 apple2gs_state::e1ram_r<0x0200>(offs_t offset);
template u8 apple2gs_state::e1ram_r<0x0400>(offs_t offset);
template u8 apple2gs_state::e1ram_r<0x0800>(offs_t offset);
template u8 apple2gs_state::e1ram_r<0x2000>(offs_t offset);
template u8 apple2gs_state::e1ram_r<0x4000>(offs_t offset);

template void apple2gs_state::e0ram_w<0x0000>(offs_t offset, u8 data);
template void apple2gs_state::e0ram_w<0x0200>(offs_t offset, u8 data);
template void apple2gs_state::e0ram_w<0x0400>(offs_t offset, u8 data);
template void apple2gs_state::e0ram_w<0x0800>(offs_t offset, u8 data);
template void apple2gs_state::e0ram_w<0x2000>(offs_t offset, u8 data);
template void apple2gs_state::e0ram_w<0x4000>(offs_t offset, u8 data);

template void apple2gs_state::e1ram_w<0x0000>(offs_t offset, u8 data);
template void apple2gs_state::e1ram_w<0x0200>(offs_t offset, u8 data);
template void apple2gs_state::e1ram_w<0x0400>(offs_t offset, u8 data);
template void apple2gs_state::e1ram_w<0x0800>(offs_t offset, u8 data);
template void apple2gs_state::e1ram_w<0x2000>(offs_t offset, u8 data);
template void apple2gs_state::e1ram_w<0x4000>(offs_t offset, u8 data);

u8 apple2gs_state::auxram0000_r(offs_t offset)
{
	slow_cycle();
	if ((offset >= 0x2000) && (offset < 0xa000) && ((m_video->get_newvideo() & 0xc0) != 0))
	{
		if (offset & 1)
		{
			offset = ((offset - 0x2000) >> 1) + 0x6000;
		}
		else
		{
			offset = ((offset - 0x2000) >> 1) + 0x2000;
		}
	}
	return m_megaii_ram[offset+0x10000];
}

void apple2gs_state::auxram0000_w(offs_t offset, u8 data)
{
	u16 orig_addr = offset;

	slow_cycle();

	if ((offset >= 0x2000) && (offset < 0xa000) && ((m_video->get_newvideo() & 0xc0) != 0))
	{
		if (offset & 1)
		{
			offset = ((offset - 0x2000) >> 1) + 0x6000;
		}
		else
		{
			offset = ((offset - 0x2000) >> 1) + 0x2000;
		}
	}

	m_megaii_ram[offset+0x10000] = data;

	if ((orig_addr >= 0x9e00) && (orig_addr <= 0x9fff))
	{
		int color = (orig_addr - 0x9e00) >> 1;
		m_video->set_SHR_color(color, rgb_t(
			((m_megaii_ram[0x19f00 + color] >> 0) & 0x0f) * 17,
			((m_megaii_ram[0x15f00 + color] >> 4) & 0x0f) * 17,
			((m_megaii_ram[0x15f00 + color] >> 0) & 0x0f) * 17));
	}
}

u8 apple2gs_state::b0ram0000_r(offs_t offset)  { return m_ram_ptr[offset]; }
void apple2gs_state::b0ram0000_w(offs_t offset, u8 data) { m_ram_ptr[offset] = data; }
u8 apple2gs_state::b0ram0200_r(offs_t offset)  { return m_ram_ptr[offset+0x200]; }
void apple2gs_state::b0ram0200_w(offs_t offset, u8 data) { m_ram_ptr[offset+0x200] = data; }
u8 apple2gs_state::b0ram0400_r(offs_t offset)  { return m_ram_ptr[offset+0x400]; }
void apple2gs_state::b0ram0400_w(offs_t offset, u8 data)
{
	m_ram_ptr[offset+0x400] = data;
	if (!(m_shadow & SHAD_TXTPG1))
	{
		slow_cycle();
		m_megaii_ram[offset+0x0400] = data;
	}
}
u8 apple2gs_state::b0ram0800_r(offs_t offset)  { return m_ram_ptr[offset+0x800]; }
void apple2gs_state::b0ram0800_w(offs_t offset, u8 data)
{
	m_ram_ptr[offset+0x800] = data;

	if (offset < 0x400)
	{
		if ((!(m_shadow & SHAD_TXTPG2)) && (m_is_rom3))
		{
			slow_cycle();
			m_megaii_ram[offset+0x800] = data;
		}
	}
}
u8 apple2gs_state::b0ram2000_r(offs_t offset)  { return m_ram_ptr[offset+0x2000]; }
void apple2gs_state::b0ram2000_w(offs_t offset, u8 data)
{
	m_ram_ptr[offset+0x2000] = data;
	if (!(m_shadow & SHAD_HIRESPG1))
	{
		slow_cycle();
		m_megaii_ram[offset+0x2000] = data;
	}
}
u8 apple2gs_state::b0ram4000_r(offs_t offset)  { return m_ram_ptr[offset+0x4000]; }
void apple2gs_state::b0ram4000_w(offs_t offset, u8 data)
{
	m_ram_ptr[offset+0x4000] = data;
	if (offset < 0x2000)
	{
		if (!(m_shadow & SHAD_HIRESPG2))
		{
			slow_cycle();
			m_megaii_ram[offset+0x4000] = data;
		}
	}
}

u8 apple2gs_state::b1ram0000_r(offs_t offset)  { return m_ram_ptr[offset+0x10000]; }
void apple2gs_state::b1ram0000_w(offs_t offset, u8 data) { m_ram_ptr[offset+0x10000] = data; }
u8 apple2gs_state::b1ram0200_r(offs_t offset)  { return m_ram_ptr[offset+0x10200]; }
void apple2gs_state::b1ram0200_w(offs_t offset, u8 data) { m_ram_ptr[offset+0x10200] = data; }
u8 apple2gs_state::b1ram0400_r(offs_t offset)  { return m_ram_ptr[offset+0x10400]; }
void apple2gs_state::b1ram0400_w(offs_t offset, u8 data)
{
	m_ram_ptr[offset+0x10400] = data;
	if (!(m_shadow & SHAD_TXTPG1))
	{
		slow_cycle();
		m_megaii_ram[offset+0x10400] = data;
	}
}
u8 apple2gs_state::b1ram0800_r(offs_t offset) { return m_ram_ptr[offset+0x10800]; }
void apple2gs_state::b1ram0800_w(offs_t offset, u8 data)
{
	m_ram_ptr[offset+0x10800] = data;
	if (offset < 0x400)
	{
		slow_cycle();
		m_megaii_ram[offset+0x10800] = data;
	}
}
u8 apple2gs_state::b1ram2000_r(offs_t offset)  { return m_ram_ptr[offset+0x12000]; }
void apple2gs_state::b1ram2000_w(offs_t offset, u8 data)
{
	m_ram_ptr[offset+0x12000] = data;
	if ((!(m_shadow & SHAD_HIRESPG1) && !(m_shadow & SHAD_AUXHIRES)) || (!(m_shadow & SHAD_SUPERHIRES)))
	{
		auxram0000_w(offset+0x2000, data);
	}
}
u8 apple2gs_state::b1ram4000_r(offs_t offset)  { return m_ram_ptr[offset+0x14000]; }
void apple2gs_state::b1ram4000_w(offs_t offset, u8 data)
{
	m_ram_ptr[offset+0x14000] = data;
	if (offset < 0x2000)
	{
		if ((!(m_shadow & SHAD_HIRESPG2) && !(m_shadow & SHAD_AUXHIRES)) || (!(m_shadow & SHAD_SUPERHIRES)))
		{
			auxram0000_w(offset+0x4000, data);
		}
	}
	else if ((offset >= 0x2000) && (offset <= 0x5fff))
	{
		if (!(m_shadow & SHAD_SUPERHIRES))
		{
			auxram0000_w(offset+0x4000, data);
		}
	}
}

u8 apple2gs_state::bank0_c000_r(offs_t offset)
{
	if (offset & 0x2000)
	{
		offset ^= 0x1000;
	}

	if (m_ramrd)
	{
		return m_ram_ptr[offset + 0x1c000];
	}

	return m_ram_ptr[offset + 0xc000];
}

void apple2gs_state::bank0_c000_w(offs_t offset, u8 data)
{
	if (offset & 0x2000)
	{
		offset ^= 0x1000;
	}

	if (m_ramwrt)
	{
		m_ram_ptr[offset + 0x1c000] = data;
		return;
	}

	m_ram_ptr[offset + 0xc000] = data;
}

u8 apple2gs_state::bank1_0000_r(offs_t offset) { return m_ram_ptr[offset + 0x10000]; }
u8 apple2gs_state::bank1_c000_r(offs_t offset) { if (offset & 0x2000) offset ^= 0x1000; return m_ram_ptr[offset + 0x1c000]; }
void apple2gs_state::bank1_c000_w(offs_t offset, u8 data) { if (offset & 0x2000) offset ^= 0x1000; m_ram_ptr[offset + 0x1c000] = data; }
void apple2gs_state::bank1_0000_sh_w(offs_t offset, u8 data)
{
	m_ram_ptr[offset + 0x10000] = data;

	switch (offset>>8)
	{
		case 0x04:  // text page 1
		case 0x05:
		case 0x06:
		case 0x07:
			if (!(m_shadow & SHAD_TXTPG1))
			{
				slow_cycle();
				m_megaii_ram[offset + 0x10000] = data;
			}
			break;

		case 0x08:  // text page 2 (only shadowable on ROM 03)
		case 0x09:
		case 0x0a:
		case 0x0b:
			if ((!(m_shadow & SHAD_TXTPG2)) && (m_is_rom3))
			{
				slow_cycle();
				m_megaii_ram[offset + 0x10000] = data;
			}
			break;

			// hi-res page 1
		case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
		case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f:
		case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
		case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
			if ((!(m_shadow & SHAD_HIRESPG1) && !(m_shadow & SHAD_AUXHIRES)) || (!(m_shadow & SHAD_SUPERHIRES)))
			{
				auxram0000_w(offset, data);
			}
			break;

			// hi-res page 2
		case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:
		case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f:
		case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57:
		case 0x58: case 0x59: case 0x5a: case 0x5b: case 0x5c: case 0x5d: case 0x5e: case 0x5f:
			if ((!(m_shadow & SHAD_HIRESPG2) && !(m_shadow & SHAD_AUXHIRES)) || (!(m_shadow & SHAD_SUPERHIRES)))
			{
				auxram0000_w(offset, data);
			}
			break;

		default:
			if ((offset >= 0x6000) && (offset <= 0x9fff))
			{
				if (!(m_shadow & SHAD_SUPERHIRES))
				{
					auxram0000_w(offset, data);
				}
			}
			break;
	}
}

void apple2gs_state::apple2gs_map(address_map &map)
{
	/* "fast side" - runs 2.8 MHz minus RAM refresh, banks 00 and 01 usually have writes shadowed to E0/E1 where I/O lives */
	/* Banks 00 and 01 also have their own independent language cards which are NOT shadowed. */
	map(0x000000, 0x0001ff).view(m_b0_0000bank);
	m_b0_0000bank[0](0x0000, 0x01ff).rw(FUNC(apple2gs_state::b0ram0000_r), FUNC(apple2gs_state::b0ram0000_w));
	m_b0_0000bank[1](0x0000, 0x01ff).rw(FUNC(apple2gs_state::b1ram0000_r), FUNC(apple2gs_state::b1ram0000_w));

	map(0x000200, 0x0003ff).view(m_b0_0200bank);
	m_b0_0200bank[0](0x0200, 0x03ff).rw(FUNC(apple2gs_state::b0ram0200_r), FUNC(apple2gs_state::b0ram0200_w)); // wr 0 rd 0
	m_b0_0200bank[1](0x0200, 0x03ff).rw(FUNC(apple2gs_state::b1ram0200_r), FUNC(apple2gs_state::b0ram0200_w)); // wr 0 rd 1
	m_b0_0200bank[2](0x0200, 0x03ff).rw(FUNC(apple2gs_state::b0ram0200_r), FUNC(apple2gs_state::b1ram0200_w)); // wr 1 rd 0
	m_b0_0200bank[3](0x0200, 0x03ff).rw(FUNC(apple2gs_state::b1ram0200_r), FUNC(apple2gs_state::b1ram0200_w)); // wr 1 rd 1

	map(0x000400, 0x0007ff).view(m_b0_0400bank);
	m_b0_0400bank[0](0x0400, 0x07ff).rw(FUNC(apple2gs_state::b0ram0400_r), FUNC(apple2gs_state::b0ram0400_w)); // wr 0 rd 0
	m_b0_0400bank[1](0x0400, 0x07ff).rw(FUNC(apple2gs_state::b1ram0400_r), FUNC(apple2gs_state::b0ram0400_w)); // wr 0 rd 1
	m_b0_0400bank[2](0x0400, 0x07ff).rw(FUNC(apple2gs_state::b0ram0400_r), FUNC(apple2gs_state::b1ram0400_w)); // wr 1 rd 0
	m_b0_0400bank[3](0x0400, 0x07ff).rw(FUNC(apple2gs_state::b1ram0400_r), FUNC(apple2gs_state::b1ram0400_w)); // wr 1 rd 1

	map(0x000800, 0x001fff).view(m_b0_0800bank);
	m_b0_0800bank[0](0x0800, 0x1fff).rw(FUNC(apple2gs_state::b0ram0800_r), FUNC(apple2gs_state::b0ram0800_w));
	m_b0_0800bank[1](0x0800, 0x1fff).rw(FUNC(apple2gs_state::b1ram0800_r), FUNC(apple2gs_state::b0ram0800_w));
	m_b0_0800bank[2](0x0800, 0x1fff).rw(FUNC(apple2gs_state::b0ram0800_r), FUNC(apple2gs_state::b1ram0800_w));
	m_b0_0800bank[3](0x0800, 0x1fff).rw(FUNC(apple2gs_state::b1ram0800_r), FUNC(apple2gs_state::b1ram0800_w));

	map(0x002000, 0x003fff).view(m_b0_2000bank);
	m_b0_2000bank[0](0x2000, 0x3fff).rw(FUNC(apple2gs_state::b0ram2000_r), FUNC(apple2gs_state::b0ram2000_w));
	m_b0_2000bank[1](0x2000, 0x3fff).rw(FUNC(apple2gs_state::b1ram2000_r), FUNC(apple2gs_state::b0ram2000_w));
	m_b0_2000bank[2](0x2000, 0x3fff).rw(FUNC(apple2gs_state::b0ram2000_r), FUNC(apple2gs_state::b1ram2000_w));
	m_b0_2000bank[3](0x2000, 0x3fff).rw(FUNC(apple2gs_state::b1ram2000_r), FUNC(apple2gs_state::b1ram2000_w));

	map(0x004000, 0x00bfff).view(m_b0_4000bank);
	m_b0_4000bank[0](0x4000, 0xbfff).rw(FUNC(apple2gs_state::b0ram4000_r), FUNC(apple2gs_state::b0ram4000_w));
	m_b0_4000bank[1](0x4000, 0xbfff).rw(FUNC(apple2gs_state::b1ram4000_r), FUNC(apple2gs_state::b0ram4000_w));
	m_b0_4000bank[2](0x4000, 0xbfff).rw(FUNC(apple2gs_state::b0ram4000_r), FUNC(apple2gs_state::b1ram4000_w));
	m_b0_4000bank[3](0x4000, 0xbfff).rw(FUNC(apple2gs_state::b1ram4000_r), FUNC(apple2gs_state::b1ram4000_w));

	map(0x00c000, 0x00ffff).view(m_bank0_atc);
	m_bank0_atc[0](0xc000, 0xffff).rw(FUNC(apple2gs_state::bank0_c000_r), FUNC(apple2gs_state::bank0_c000_w));
	m_bank0_atc[1](0xc000, 0xffff).rw(FUNC(apple2gs_state::c000_r), FUNC(apple2gs_state::c000_w));
	m_bank0_atc[1](0xc080, 0xffff).rw(FUNC(apple2gs_state::c080_r), FUNC(apple2gs_state::c080_w));
	m_bank0_atc[1](0xc100, 0xffff).rw(FUNC(apple2gs_state::c100_r), FUNC(apple2gs_state::c100_w));
	m_bank0_atc[1](0xc300, 0xffff).m(m_c300bank, FUNC(address_map_bank_device::amap8));
	m_bank0_atc[1](0xc400, 0xffff).rw(FUNC(apple2gs_state::c400_r), FUNC(apple2gs_state::c400_w));
	m_bank0_atc[1](0xc800, 0xffff).rw(FUNC(apple2gs_state::c800_r), FUNC(apple2gs_state::c800_w));
	m_bank0_atc[1](0xd000, 0xffff).view(m_upper00);

	m_upper00[0](0xd000, 0xffff).view(m_lc00);
	m_upper00[1](0xd000, 0xffff).rw(FUNC(apple2gs_state::inh_r), FUNC(apple2gs_state::inh_w));

	m_lc00[0](0xd000, 0xffff).rom().region("maincpu", 0x3d000).w(FUNC(apple2gs_state::lc_00_w));
	m_lc00[1](0xd000, 0xffff).rw(FUNC(apple2gs_state::lc_00_r), FUNC(apple2gs_state::lc_00_w));
	m_lc00[2](0xd000, 0xffff).rom().region("maincpu", 0x39000).w(FUNC(apple2gs_state::lc_00_w));
	m_lc00[3](0xd000, 0xffff).rw(FUNC(apple2gs_state::lc_00_r), FUNC(apple2gs_state::lc_00_w));

	map(0x010000, 0x01bfff).rw(FUNC(apple2gs_state::bank1_0000_r), FUNC(apple2gs_state::bank1_0000_sh_w));

	map(0x01c000, 0x01ffff).view(m_bank1_atc);
	m_bank1_atc[0](0x1c000, 0x1ffff).rw(FUNC(apple2gs_state::bank1_c000_r), FUNC(apple2gs_state::bank1_c000_w));
	m_bank1_atc[1](0x1c000, 0x1c07f).rw(FUNC(apple2gs_state::c000_r), FUNC(apple2gs_state::c000_w));
	m_bank1_atc[1](0x1c080, 0x1c0ff).rw(FUNC(apple2gs_state::c080_r), FUNC(apple2gs_state::c080_w));
	m_bank1_atc[1](0x1c100, 0x1c2ff).rw(FUNC(apple2gs_state::c100_r), FUNC(apple2gs_state::c100_w));
	m_bank1_atc[1](0x1c300, 0x1c3ff).m(m_c300bank, FUNC(address_map_bank_device::amap8));
	m_bank1_atc[1](0x1c400, 0x1c7ff).rw(FUNC(apple2gs_state::c400_r), FUNC(apple2gs_state::c400_w));
	m_bank1_atc[1](0x1c800, 0x1cfff).rw(FUNC(apple2gs_state::c800_r), FUNC(apple2gs_state::c800_w));

	m_bank1_atc[1](0x1d000, 0x1ffff).view(m_upper01);
	m_upper01[0](0x1d000, 0x1ffff).view(m_lc01);
	m_upper01[1](0x1d000, 0x1ffff).rw(FUNC(apple2gs_state::inh_r), FUNC(apple2gs_state::inh_w));

	m_lc01[0](0x1d000, 0x1ffff).rom().region("maincpu", 0x3d000).w(FUNC(apple2gs_state::lc_01_w));
	m_lc01[1](0x1d000, 0x1ffff).rw(FUNC(apple2gs_state::lc_01_r), FUNC(apple2gs_state::lc_01_w));

	/* "Mega II side" - this is basically a 128K IIe on a chip that runs merrily at 1 MHz */
	/* Unfortunately all I/O happens here, including new IIgs-specific stuff */
	map(0xe00000, 0xe001ff).view(m_e0_0000bank);
	m_e0_0000bank[0](0xe00000, 0xe001ff).rw(FUNC(apple2gs_state::e0ram_r<0x0000>), FUNC(apple2gs_state::e0ram_w<0x0000>));
	m_e0_0000bank[1](0xe00000, 0xe001ff).rw(FUNC(apple2gs_state::e1ram_r<0x0000>), FUNC(apple2gs_state::e1ram_w<0x0000>));

	map(0xe00200, 0xe003ff).view(m_e0_0200bank);
	m_e0_0200bank[0](0xe00200, 0xe003ff).rw(FUNC(apple2gs_state::e0ram_r<0x0200>), FUNC(apple2gs_state::e0ram_w<0x0200>)); // wr 0 rd 0
	m_e0_0200bank[1](0xe00200, 0xe003ff).rw(FUNC(apple2gs_state::e1ram_r<0x0200>), FUNC(apple2gs_state::e0ram_w<0x0200>)); // wr 0 rd 1
	m_e0_0200bank[2](0xe00200, 0xe003ff).rw(FUNC(apple2gs_state::e0ram_r<0x0200>), FUNC(apple2gs_state::e1ram_w<0x0200>)); // wr 1 rd 0
	m_e0_0200bank[3](0xe00200, 0xe003ff).rw(FUNC(apple2gs_state::e1ram_r<0x0200>), FUNC(apple2gs_state::e1ram_w<0x0200>)); // wr 1 rd 1

	map(0xe00400, 0xe007ff).view(m_e0_0400bank);
	m_e0_0400bank[0](0xe00400, 0xe007ff).rw(FUNC(apple2gs_state::e0ram_r<0x0400>), FUNC(apple2gs_state::e0ram_w<0x0400>)); // wr 0 rd 0
	m_e0_0400bank[1](0xe00400, 0xe007ff).rw(FUNC(apple2gs_state::e1ram_r<0x0400>), FUNC(apple2gs_state::e0ram_w<0x0400>)); // wr 0 rd 1
	m_e0_0400bank[2](0xe00400, 0xe007ff).rw(FUNC(apple2gs_state::e0ram_r<0x0400>), FUNC(apple2gs_state::e1ram_w<0x0400>)); // wr 1 rd 0
	m_e0_0400bank[3](0xe00400, 0xe007ff).rw(FUNC(apple2gs_state::e1ram_r<0x0400>), FUNC(apple2gs_state::e1ram_w<0x0400>)); // wr 1 rd 1

	map(0xe00800, 0xe01fff).view(m_e0_0800bank);
	m_e0_0800bank[0](0xe00800, 0xe01fff).rw(FUNC(apple2gs_state::e0ram_r<0x0800>), FUNC(apple2gs_state::e0ram_w<0x0800>));
	m_e0_0800bank[1](0xe00800, 0xe01fff).rw(FUNC(apple2gs_state::e1ram_r<0x0800>), FUNC(apple2gs_state::e0ram_w<0x0800>));
	m_e0_0800bank[2](0xe00800, 0xe01fff).rw(FUNC(apple2gs_state::e0ram_r<0x0800>), FUNC(apple2gs_state::e1ram_w<0x0800>));
	m_e0_0800bank[3](0xe00800, 0xe01fff).rw(FUNC(apple2gs_state::e1ram_r<0x0800>), FUNC(apple2gs_state::e1ram_w<0x0800>));

	map(0xe02000, 0xe03fff).view(m_e0_2000bank);
	m_e0_2000bank[0](0xe02000, 0xe03fff).rw(FUNC(apple2gs_state::e0ram_r<0x2000>), FUNC(apple2gs_state::e0ram_w<0x2000>));
	m_e0_2000bank[1](0xe02000, 0xe03fff).rw(FUNC(apple2gs_state::e1ram_r<0x2000>), FUNC(apple2gs_state::e0ram_w<0x2000>));
	m_e0_2000bank[2](0xe02000, 0xe03fff).rw(FUNC(apple2gs_state::e0ram_r<0x2000>), FUNC(apple2gs_state::e1ram_w<0x2000>));
	m_e0_2000bank[3](0xe02000, 0xe03fff).rw(FUNC(apple2gs_state::e1ram_r<0x2000>), FUNC(apple2gs_state::e1ram_w<0x2000>));

	map(0xe04000, 0xe0bfff).view(m_e0_4000bank);
	m_e0_4000bank[0](0xe04000, 0xe0bfff).rw(FUNC(apple2gs_state::e0ram_r<0x4000>), FUNC(apple2gs_state::e0ram_w<0x4000>));
	m_e0_4000bank[1](0xe04000, 0xe0bfff).rw(FUNC(apple2gs_state::e1ram_r<0x4000>), FUNC(apple2gs_state::e0ram_w<0x4000>));
	m_e0_4000bank[2](0xe04000, 0xe0bfff).rw(FUNC(apple2gs_state::e0ram_r<0x4000>), FUNC(apple2gs_state::e1ram_w<0x4000>));
	m_e0_4000bank[3](0xe04000, 0xe0bfff).rw(FUNC(apple2gs_state::e1ram_r<0x4000>), FUNC(apple2gs_state::e1ram_w<0x4000>));

	map(0x020000, 0xdfffff).rw(FUNC(apple2gs_state::expandedram_r), FUNC(apple2gs_state::expandedram_w));

	map(0xe0c000, 0xe0c07f).rw(FUNC(apple2gs_state::c000_r), FUNC(apple2gs_state::c000_w));
	map(0xe0c080, 0xe0c0ff).rw(FUNC(apple2gs_state::c080_r), FUNC(apple2gs_state::c080_w));
	map(0xe0c100, 0xe0c2ff).rw(FUNC(apple2gs_state::c100_r), FUNC(apple2gs_state::c100_w));
	map(0xe0c300, 0xe0c3ff).m(m_c300bank, FUNC(address_map_bank_device::amap8));
	map(0xe0c400, 0xe0c7ff).rw(FUNC(apple2gs_state::c400_r), FUNC(apple2gs_state::c400_w));
	map(0xe0c800, 0xe0cfff).rw(FUNC(apple2gs_state::c800_r), FUNC(apple2gs_state::c800_w));

	map(0xe0d000, 0xe0ffff).view(m_upperbank);
	m_upperbank[0](0xe0d000, 0xe0ffff).view(m_lcbank);
	m_upperbank[1](0xe0d000, 0xe0ffff).rw(FUNC(apple2gs_state::inh_r), FUNC(apple2gs_state::inh_w));

	m_lcbank[0](0xe0d000, 0xe0ffff).rom().region("maincpu", 0x3d000).w(FUNC(apple2gs_state::lc_w));
	m_lcbank[1](0xe0d000, 0xe0ffff).rw(FUNC(apple2gs_state::lc_r), FUNC(apple2gs_state::lc_w));

	map(0xe10000, 0xe1bfff).rw(FUNC(apple2gs_state::auxram0000_r), FUNC(apple2gs_state::auxram0000_w));
	map(0xe1c000, 0xe1c07f).rw(FUNC(apple2gs_state::c000_r), FUNC(apple2gs_state::c000_w));
	map(0xe1c080, 0xe1c0ff).rw(FUNC(apple2gs_state::c080_r), FUNC(apple2gs_state::c080_w));
	map(0xe1c100, 0xe1c2ff).rw(FUNC(apple2gs_state::c100_r), FUNC(apple2gs_state::c100_w));
	map(0xe1c300, 0xe1c3ff).m(m_c300bank, FUNC(address_map_bank_device::amap8));
	map(0xe1c400, 0xe1c7ff).rw(FUNC(apple2gs_state::c400_r), FUNC(apple2gs_state::c400_w));
	map(0xe1c800, 0xe1cfff).rw(FUNC(apple2gs_state::c800_r), FUNC(apple2gs_state::c800_w));

	map(0xe1d000, 0xe1ffff).view(m_upperaux);
	m_upperaux[0](0xe1d000, 0xe1ffff).view(m_lcaux);
	m_upperaux[1](0xe1d000, 0xe1ffff).rw(FUNC(apple2gs_state::inh_r), FUNC(apple2gs_state::inh_w));

	m_lcaux[0](0xe1d000, 0xe1ffff).rom().region("maincpu", 0x3d000).w(FUNC(apple2gs_state::lc_aux_w));
	m_lcaux[1](0xe1d000, 0xe1ffff).rw(FUNC(apple2gs_state::lc_aux_r), FUNC(apple2gs_state::lc_aux_w));

	map(0xfc0000, 0xffffff).rom().region("maincpu", 0x00000);
}

void apple2gs_state::vectors_map(address_map &map)
{
	map(0x00, 0x1f).r(FUNC(apple2gs_state::apple2gs_read_vector));
}

void apple2gs_state::c300bank_map(address_map &map)
{
	map(0x0000, 0x00ff).rw(FUNC(apple2gs_state::c300_r), FUNC(apple2gs_state::c300_w));
	map(0x0100, 0x01ff).r(FUNC(apple2gs_state::c300_int_r)).nopw();
}

void apple2gs_state::a2gs_es5503_map(address_map &map)
{
	map(0x00000, 0x0ffff).mirror(0x10000).readonly().share("docram"); // IIgs only has 64K, top bank mirrors lower bank
}

/***************************************************************************
    ADB microcontroller + KEYGLU emulation

    Huge thanks to Neil Parker's writeup on the ADB microcontroller!
    http://www.llx.com/~nparker/a2/adb.html
***************************************************************************/

u8 apple2gs_state::adbmicro_p0_in()
{
	return m_glu_bus;
}

u8 apple2gs_state::adbmicro_p1_in()
{
	if (!m_is_rom3)
	{
		return 0xff;
	}
	else
	{
		return 0x06;
	}
}

u8 apple2gs_state::adbmicro_p2_in()
{
	u8 rv = 0;

	if (!m_is_rom3)
	{
		rv |= 0x40;     // no reset, must be 0 on ROM 3
	}
	rv |= (m_adb_line) ? 0x00 : 0x80;

	return rv;
}

u8 apple2gs_state::adbmicro_p3_in()
{
	if (m_is_rom3)
	{
		// Check the jumper to remove the Control Panel from the Control-Open Apple-Esc CDA menu
		if (m_sysconfig->read() & 0x08)
		{
			return 0x40;
		}
		return 0x00;
	}
	else
	{
		return 0x07;
	}
}

void apple2gs_state::adbmicro_p0_out(u8 data)
{
	m_glu_bus = data;
}

void apple2gs_state::adbmicro_p1_out(u8 data)
{
}

void apple2gs_state::adbmicro_p2_out(u8 data)
{
	if (!BIT(data, 5) && BIT(m_adb_p2_last, 5))
	{
		m_adb_reset_freeze = 2;
		m_a2bus->reset_bus();
		m_maincpu->reset();
		m_video->set_newvideo(0x41);

		m_lcram = false;
		m_lcram2 = true;
		m_lcprewrite = false;
		m_lcwriteenable = true;
		m_intcxrom = false;
		m_slotc3rom = false;
		m_video->a80store_w(false);
		m_altzp = false;
		m_ramrd = false;
		m_ramwrt = false;
		m_altzp = false;
		m_video->page2_w(false);
		m_video->res_w(0);

		lcrom_update();
		auxbank_update();
		update_slotrom_banks();
	}

	if (!(data & 0x10))
	{
		if (m_adbmicro->are_port_bits_output(0, 0xff))
		{
			keyglu_mcu_write(data & 7, m_glu_bus);
		}
		else    // read GLU
		{
			m_glu_bus = keyglu_mcu_read(data & 7);
		}
	}
	else
	{
		m_glu_kbd_y = data & 0xf;
	}

	m_adb_p2_last = data;
}

void apple2gs_state::adbmicro_p3_out(u8 data)
{
	if (((data & 0x08) == 0x08) != m_adb_line)
	{
		m_adb_line = (data & 0x8) ? true : false;
		m_macadb->adb_linechange_w(!m_adb_line);
	}

	if (m_adb_reset_freeze == 0)
	{
		m_adb_p3_last = data;
	}
}

void apple2gs_state::set_adb_line(int linestate)
{
	m_adb_line = (linestate == CLEAR_LINE);
}

u8 apple2gs_state::keyglu_mcu_read(u8 offset)
{
	u8 rv = m_glu_regs[offset];

//  printf("MCU reads reg %x\n", offset);

	// the command full flag is cleared by the MCU reading
	// first the KGS register and then the command register
	if ((offset == GLU_COMMAND) && (m_glu_mcu_read_kgs))
	{
		m_glu_regs[GLU_KG_STATUS] &= ~KGS_COMMAND_FULL;
		m_glu_mcu_read_kgs = false;
	}

	// prime for the next command register read to clear the command full flag
	if (offset == GLU_KG_STATUS)
	{
		m_glu_mcu_read_kgs = true;
	}

	return rv;
}

void  apple2gs_state::keyglu_mcu_write(u8 offset, u8 data)
{
	m_glu_regs[offset] = data;

	switch (offset)
	{
		case GLU_KEY_DATA:
			// if this is the first key pressed within a certain time frame, don't raise the strobe
			// so that the emulated system doesn't see the keypress used to start the emulation.
			if (m_sysconfig->read() & 0x01)
			{ // bump the cycle count way up for a 16 Mhz ZipGS
				if (m_maincpu->total_cycles() < 700000)
				{
					return;
				}
			}
			else
			{
				if (m_maincpu->total_cycles() < 25000)
				{
					return;
				}
			}

			m_glu_regs[GLU_KG_STATUS] |= KGS_KEYSTROBE;
			m_glu_regs[GLU_SYSSTAT] |= GLU_STATUS_KEYDATIRQ;
			keyglu_regen_irqs();
			break;

		case GLU_MOUSEX:
			m_glu_regs[GLU_KG_STATUS] |= KGS_MOUSEX_FULL;
			m_glu_regs[GLU_SYSSTAT] |= GLU_STATUS_MOUSEIRQ;
			keyglu_regen_irqs();
			m_glu_mouse_read_stat = false;  // signal next read will be mouse X
			break;

		case GLU_MOUSEY:
			break;

		case GLU_ANY_KEY_DOWN:  // bit 7 is the actual flag here
			if (data & 0x80)
			{
				m_glu_regs[GLU_KG_STATUS] |= KGS_ANY_KEY_DOWN;
			}
			break;

		case GLU_DATA:
			m_glu_regs[GLU_DATA] = data;
			m_glu_regs[GLU_KG_STATUS] |= KGS_DATA_FULL;
			m_glu_regs[GLU_SYSSTAT] |= GLU_STATUS_DATAIRQ;
			keyglu_regen_irqs();
			m_glu_816_read_dstat = false;
			break;
	}
}

/*
   Keyglu registers map as follows on the 816:

   C000           = key data + any key down, clears strobe
   C010           = clears keystrobe
   C024 MOUSEDATA = reads GLU mouseX and mouseY
   C025 KEYMODREG = reads GLU keymod register
   C026 DATAREG   = writes from the 816 go to COMMAND, reads from DATA
   C027 KMSTATUS  = GLU system status register
*/

u8 apple2gs_state::keyglu_816_read(u8 offset)
{
	switch (offset)
	{
		case GLU_C000:
			if (m_glu_regs[GLU_KG_STATUS] & KGS_KEYSTROBE)
			{
				return m_glu_regs[GLU_KEY_DATA] | 0x80;
			}
			return m_glu_regs[GLU_KEY_DATA];

		case GLU_C010:
			return (m_glu_regs[GLU_ANY_KEY_DOWN] & 0x80);

		case GLU_KEYMOD:
			return m_glu_regs[GLU_KEYMOD];

		case GLU_MOUSEX:
		case GLU_MOUSEY:
			if (!m_glu_mouse_read_stat)
			{
				m_glu_mouse_read_stat = true;
				return m_glu_regs[GLU_MOUSEX];
			}
			m_glu_mouse_read_stat = false;
			m_glu_regs[GLU_KG_STATUS] &= ~KGS_MOUSEX_FULL;
			m_glu_regs[GLU_SYSSTAT] &= ~GLU_STATUS_MOUSEIRQ;
			keyglu_regen_irqs();
			return m_glu_regs[GLU_MOUSEY];

		case GLU_SYSSTAT:
			{
				// regenerate sysstat bits
				u8 sysstat = m_glu_regs[GLU_SYSSTAT] & ~0xab;
				if (m_glu_regs[GLU_KG_STATUS] & KGS_COMMAND_FULL)
				{
					sysstat |= GLU_STATUS_CMDFULL;
				}
				if (m_glu_mouse_read_stat)
				{
					sysstat |= GLU_STATUS_MOUSEXY;
				}
				if (m_glu_regs[GLU_KG_STATUS] & KGS_KEYSTROBE)
				{
					sysstat |= GLU_STATUS_KEYDATIRQ;
				}
				if (m_glu_regs[GLU_KG_STATUS] & KGS_DATA_FULL)
				{
					sysstat |= GLU_STATUS_DATAIRQ;
				}
				if (m_glu_regs[GLU_KG_STATUS] & KGS_MOUSEX_FULL)
				{
					sysstat |= GLU_STATUS_MOUSEIRQ;
				}
				m_glu_816_read_dstat = true;
				//printf("Read SYSSTAT %02x (PC=%x)\n", sysstat, m_maincpu->pc());
				return sysstat;
			}

		case GLU_DATA:
			if (m_glu_816_read_dstat)
			{
				m_glu_816_read_dstat = false;
				m_glu_regs[GLU_KG_STATUS] &= ~KGS_DATA_FULL;
				keyglu_regen_irqs();
			}
			return m_glu_regs[GLU_DATA];

		default:
			return 0xff;
			break;
	}

	return 0xff;
}

void apple2gs_state::keyglu_816_write(u8 offset, u8 data)
{
	if (offset < GLU_C000)
	{
		m_glu_regs[offset&7] = data;
	}

	switch (offset)
	{
		case GLU_C010:
			m_glu_regs[GLU_SYSSTAT] &= ~GLU_STATUS_KEYDATIRQ;
			m_glu_regs[GLU_KG_STATUS] &= ~KGS_KEYSTROBE;
			keyglu_regen_irqs();
			break;

		case GLU_COMMAND:
			m_glu_regs[GLU_KG_STATUS] |= KGS_COMMAND_FULL;
			break;

		case GLU_SYSSTAT:
			m_glu_regs[GLU_SYSSTAT] &= 0xab;  // clear the non-read-only fields
			m_glu_regs[GLU_SYSSTAT] |= (data & ~0xab);
			break;
	}
}

void apple2gs_state::keyglu_regen_irqs()
{
	bool bIRQ = false;

	if ((m_glu_regs[GLU_KG_STATUS] & KGS_DATA_FULL) && (m_glu_regs[GLU_SYSSTAT] & GLU_STATUS_DATAIRQEN))
	{
		bIRQ = true;
	}

	if ((m_glu_regs[GLU_KG_STATUS] & KGS_KEYSTROBE) && (m_glu_regs[GLU_SYSSTAT] & GLU_STATUS_KEYDATIRQEN))
	{
		bIRQ = true;
	}

	if ((m_glu_regs[GLU_KG_STATUS] & KGS_MOUSEX_FULL) && (m_glu_regs[GLU_SYSSTAT] & GLU_STATUS_MOUSEIRQEN))
	{
		bIRQ = true;
	}

	if (bIRQ)
	{
		raise_irq(IRQS_ADB);
	}
	else
	{
		lower_irq(IRQS_ADB);
	}
}

void apple2gs_state::scc_irq_w(int state)
{
	if (state)
	{
		raise_irq(IRQS_SCC);
	}
	else
	{
		lower_irq(IRQS_SCC);
	}
}

/* Sound - DOC */
void apple2gs_state::doc_irq_w(int state)
{
	if (state)
	{
		raise_irq(IRQS_DOC);
	}
	else
	{
		lower_irq(IRQS_DOC);
	}
}

u8 apple2gs_state::doc_adc_read()
{
	return 0x80;
}

void apple2gs_state::phases_w(uint8_t phases)
{
	if (m_cur_floppy)
	{
		m_cur_floppy->seek_phase_w(phases);
	}
}

void apple2gs_state::devsel_w(uint8_t devsel)
{
	m_devsel = devsel;
	if (m_devsel == 1)
	{
		if (!BIT(m_diskreg, DISKREG_35SEL))
		{
			m_cur_floppy = m_floppy[0]->get_device();
		}
		else
		{
			m_cur_floppy = m_floppy[2]->get_device();
			m_motoroff_time = 0;
		}
	}
	else if (m_devsel == 2)
	{
		if (!BIT(m_diskreg, DISKREG_35SEL))
		{
			m_cur_floppy = m_floppy[1]->get_device();
		}
		else
		{
			m_cur_floppy = m_floppy[3]->get_device();
			m_motoroff_time = 0;
		}
	}
	else
	{
		m_cur_floppy = nullptr;
	}
	m_iwm->set_floppy(m_cur_floppy);

	if ((m_cur_floppy) && (BIT(m_diskreg, DISKREG_35SEL)))
	{
		m_cur_floppy->ss_w(BIT(m_diskreg, DISKREG_HDSEL));
	}
}

void apple2gs_state::sel35_w(int sel35)
{
}

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

INPUT_PORTS_START( apple2gs )
	PORT_START("a2_config")
	PORT_CONFNAME(0x07, 0x00, "CPU type")
	PORT_CONFSETTING(0x00, "Standard")
	PORT_CONFSETTING(0x01, "7 MHz ZipGS")
	PORT_CONFSETTING(0x03, "8 MHz ZipGS")
	PORT_CONFSETTING(0x05, "12 MHz ZipGS")
	PORT_CONFSETTING(0x07, "16 MHz ZipGS")
INPUT_PORTS_END

INPUT_PORTS_START( apple2gsrom3 )
	PORT_INCLUDE( apple2gs )

	PORT_MODIFY("a2_config")
	PORT_CONFNAME(0x08, 0x00, "Disable CDA Control Panel")
	PORT_CONFSETTING(0x00, DEF_STR(No))
	PORT_CONFSETTING(0x08, DEF_STR(Yes))
INPUT_PORTS_END

void apple2gs_state::apple2gs(machine_config &config)
{
	/* basic machine hardware */
	G65816(config, m_maincpu, A2GS_MASTER_CLOCK/10);
	m_maincpu->set_addrmap(AS_PROGRAM, &apple2gs_state::apple2gs_map);
	m_maincpu->set_addrmap(g65816_device::AS_VECTORS, &apple2gs_state::vectors_map);
	m_maincpu->set_dasm_override(FUNC(apple2gs_state::dasm_trampoline));
	m_maincpu->wdm_handler().set(FUNC(apple2gs_state::wdm_trampoline));
	TIMER(config, m_scantimer, 0);
	m_scantimer->configure_scanline(FUNC(apple2gs_state::apple2_interrupt), "screen", 0, 1);

	TIMER(config, m_acceltimer, 0).configure_generic(FUNC(apple2gs_state::accel_timer));

	config.set_maximum_quantum(attotime::from_hz(60));

	M50741(config, m_adbmicro, A2GS_MASTER_CLOCK/8);
	m_adbmicro->set_pullups<2>(0x20);
	m_adbmicro->read_p<0>().set(FUNC(apple2gs_state::adbmicro_p0_in));
	m_adbmicro->write_p<0>().set(FUNC(apple2gs_state::adbmicro_p0_out));
	m_adbmicro->read_p<1>().set(FUNC(apple2gs_state::adbmicro_p1_in));
	m_adbmicro->write_p<1>().set(FUNC(apple2gs_state::adbmicro_p1_out));
	m_adbmicro->read_p<2>().set(FUNC(apple2gs_state::adbmicro_p2_in));
	m_adbmicro->write_p<2>().set(FUNC(apple2gs_state::adbmicro_p2_out));
	m_adbmicro->read_p<3>().set(FUNC(apple2gs_state::adbmicro_p3_in));
	m_adbmicro->write_p<3>().set(FUNC(apple2gs_state::adbmicro_p3_out));

	MACADB(config, m_macadb, A2GS_MASTER_CLOCK/8);
	m_macadb->adb_data_callback().set(FUNC(apple2gs_state::set_adb_line));

	RTC3430042(config, m_rtc, XTAL(32'768));

	APPLE2_VIDEO(config, m_video, A2GS_14M).set_screen(m_screen);

	APPLE2_COMMON(config, m_a2common, A2GS_14M).set_GS_cputag(m_maincpu);

//  APPLE2_HOST(config, m_a2host, A2GS_14M);
//  m_a2host->set_cputag(m_maincpu);
//  m_a2host->set_space(m_maincpu, AS_PROGRAM);

	APPLE2_GAMEIO(config, m_gameio, apple2_gameio_device::default_options, nullptr);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(704, 262);  // 640+32+32 for the borders
	m_screen->set_visarea(0,703,0,230);
	m_screen->set_screen_update(FUNC(apple2gs_state::screen_update));

	PALETTE(config, "palette", FUNC(apple2gs_state::palette_init), 256);

	/* sound hardware */
	SPEAKER(config, "a2speaker").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "a2speaker", 1.00);

	SPEAKER(config, "ensoniq", 4).corners();
	ES5503(config, m_doc, A2GS_7M);
	m_doc->set_channels(4);
	m_doc->set_addrmap(0, &apple2gs_state::a2gs_es5503_map);
	m_doc->irq_func().set(FUNC(apple2gs_state::doc_irq_w));
	m_doc->adc_func().set(FUNC(apple2gs_state::doc_adc_read));
	m_doc->add_route(0, "ensoniq", 1.0, 0);
	m_doc->add_route(1, "ensoniq", 1.0, 1);
	m_doc->add_route(2, "ensoniq", 1.0, 2);
	m_doc->add_route(3, "ensoniq", 1.0, 3);

	/* RAM */
	RAM(config, m_ram).set_default_size("2M").set_extra_options("1M,3M,4M,5M,6M,7M,8M").set_default_value(0x00);

	/* C300 banking */
	ADDRESS_MAP_BANK(config, A2GS_C300_TAG).set_map(&apple2gs_state::c300bank_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x100);

	/* serial */
	SCC85C30(config, m_scc, A2GS_14M / 2);
	m_scc->configure_channels(3'686'400, 3'686'400, 3'686'400, 3'686'400);
	m_scc->out_int_callback().set(FUNC(apple2gs_state::scc_irq_w));
	m_scc->out_txda_callback().set("printer", FUNC(rs232_port_device::write_txd));
	m_scc->out_txdb_callback().set("modem", FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232a(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_scc, FUNC(z80scc_device::rxa_w));
	rs232a.dcd_handler().set(m_scc, FUNC(z80scc_device::dcda_w));
	rs232a.cts_handler().set(m_scc, FUNC(z80scc_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "modem", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_scc, FUNC(z80scc_device::rxb_w));
	rs232b.dcd_handler().set(m_scc, FUNC(z80scc_device::dcdb_w));
	rs232b.cts_handler().set(m_scc, FUNC(z80scc_device::ctsb_w));

	/* slot devices */
	A2BUS(config, m_a2bus, 0);
	m_a2bus->set_space(m_maincpu, AS_PROGRAM);
	m_a2bus->irq_w().set(FUNC(apple2gs_state::a2bus_irq_w));
	m_a2bus->nmi_w().set(FUNC(apple2gs_state::a2bus_nmi_w));
	m_a2bus->inh_w().set(FUNC(apple2gs_state::a2bus_inh_w));
	m_a2bus->dma_w().set_inputline(m_maincpu, INPUT_LINE_HALT);
	A2BUS_SLOT(config, "sl1", A2GS_7M, m_a2bus, apple2gs_cards, nullptr);
	A2BUS_SLOT(config, "sl2", A2GS_7M, m_a2bus, apple2gs_cards, nullptr);
	A2BUS_SLOT(config, "sl3", A2GS_7M, m_a2bus, apple2gs_cards, nullptr);
	A2BUS_SLOT(config, "sl4", A2GS_7M, m_a2bus, apple2gs_cards, nullptr);
	A2BUS_SLOT(config, "sl5", A2GS_7M, m_a2bus, apple2gs_cards, nullptr);
	A2BUS_SLOT(config, "sl6", A2GS_7M, m_a2bus, apple2gs_cards, nullptr);
	A2BUS_SLOT(config, "sl7", A2GS_7M, m_a2bus, apple2gs_cards, nullptr);

	IWM(config, m_iwm, A2GS_7M, A2GS_MASTER_CLOCK/14);
	m_iwm->phases_cb().set(FUNC(apple2gs_state::phases_w));
	m_iwm->sel35_cb().set(FUNC(apple2gs_state::sel35_w));
	m_iwm->devsel_cb().set(FUNC(apple2gs_state::devsel_w));

	applefdintf_device::add_525(config, m_floppy[0]);
	applefdintf_device::add_525(config, m_floppy[1]);
	applefdintf_device::add_35(config, m_floppy[2]);
	applefdintf_device::add_35(config, m_floppy[3]);

	SOFTWARE_LIST(config, "flop_gs_clean").set_original("apple2gs_flop_clcracked"); // GS-specific cleanly cracked disks
	SOFTWARE_LIST(config, "flop_gs_orig").set_compatible("apple2gs_flop_orig"); // Original disks for GS
	SOFTWARE_LIST(config, "flop_gs_misc").set_compatible("apple2gs_flop_misc"); // Legacy software list pre-June 2021 and defaced cracks
	SOFTWARE_LIST(config, "flop_a2_clean").set_compatible("apple2_flop_clcracked"); // Apple II series cleanly cracked
	SOFTWARE_LIST(config, "flop_a2_orig").set_compatible("apple2_flop_orig").set_filter("A2GS");  // Filter list to compatible disks for this machine.
	SOFTWARE_LIST(config, "flop_a2_misc").set_compatible("apple2_flop_misc");
}

void apple2gs_state::apple2gsr1(machine_config &config)
{
	apple2gs(config);

	// 256K on board + 1M in the expansion slot was common for ROM 01
	m_ram->set_default_size("1280K").set_extra_options("256K,512K,768K,1M,2M,3M,4M,5M,6M,7M,8M").set_default_value(0x00);

	M50740(config.replace(), m_adbmicro, A2GS_MASTER_CLOCK/8);
	m_adbmicro->set_pullups<2>(0x20);
	m_adbmicro->read_p<0>().set(FUNC(apple2gs_state::adbmicro_p0_in));
	m_adbmicro->write_p<0>().set(FUNC(apple2gs_state::adbmicro_p0_out));
	m_adbmicro->read_p<1>().set(FUNC(apple2gs_state::adbmicro_p1_in));
	m_adbmicro->write_p<1>().set(FUNC(apple2gs_state::adbmicro_p1_out));
	m_adbmicro->read_p<2>().set(FUNC(apple2gs_state::adbmicro_p2_in));
	m_adbmicro->write_p<2>().set(FUNC(apple2gs_state::adbmicro_p2_out));
	m_adbmicro->read_p<3>().set(FUNC(apple2gs_state::adbmicro_p3_in));
	m_adbmicro->write_p<3>().set(FUNC(apple2gs_state::adbmicro_p3_out));
}

void apple2gs_state::apple2gsmt(machine_config &config)
{
	apple2gs(config);

	// SWIM1 344S0061 confirmed on two different Mark Twain boards, with 15.6672 MHz oscillator
	SWIM1(config.replace(), m_iwm, 15.6672_MHz_XTAL);
	m_iwm->phases_cb().set(FUNC(apple2gs_state::phases_w));
	m_iwm->sel35_cb().set(FUNC(apple2gs_state::sel35_w));
	m_iwm->devsel_cb().set(FUNC(apple2gs_state::devsel_w));

	applefdintf_device::add_525(config, m_floppy[0]);
	applefdintf_device::add_525(config, m_floppy[1]);
	applefdintf_device::add_35_hd(config, m_floppy[2]);
	applefdintf_device::add_35_hd(config, m_floppy[3]);
}

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

  Game driver(s)

***************************************************************************/
ROM_START(apple2gs)
	// M50740/50741 ADB MCU inside the IIgs system unit
	ROM_REGION(0x1000, "adbmicro", 0)
	ROM_LOAD( "341s0632-2.bin", 0x000000, 0x001000, CRC(e1c11fb0) SHA1(141d18c36a617ab9dce668445440d34354be0672) )

	ROM_REGION(0x4000,"gfx1",0)
	ROM_LOAD ( "megaii.chr", 0x0000, 0x4000, BAD_DUMP CRC(ef0bfc11) SHA1(c068f3d02d442a8a38121d42318e9a962ee8913e)) /* need label/part number */

	ROM_REGION(0x80000,"maincpu",0)
	// 341-0728 is the MASK rom version while 341-0737 is the EPROM version - SAME data.
	ROM_LOAD("341-0728", 0x00000, 0x20000, CRC(8d410067) SHA1(c0f4704233ead14cb8e1e8a68fbd7063c56afd27) ) /* 341-0728: IIgs ROM03 FC-FD */
	// 341-0748 is the MASK rom version while 341-0749 is the EPROM version - SAME data.
	ROM_LOAD("341-0748", 0x30000, 0x10000, CRC(18190283) SHA1(c70576869deec92ca82c78438b1d5c686eac7480) ) /* 341-0748: IIgs ROM03 FE-FF */
	ROM_CONTINUE ( 0x20000, 0x10000) /* high address line is inverted on PCB? */
ROM_END

ROM_START(apple2gsr3p)
	ROM_REGION(0x1000, "adbmicro", 0)
	ROM_LOAD( "341s0632-2.bin", 0x000000, 0x001000, CRC(e1c11fb0) SHA1(141d18c36a617ab9dce668445440d34354be0672) )

	ROM_REGION(0x4000,"gfx1",0)
	ROM_LOAD ( "megaii.chr", 0x0000, 0x4000, BAD_DUMP CRC(ef0bfc11) SHA1(c068f3d02d442a8a38121d42318e9a962ee8913e)) /* need label/part number */

	ROM_REGION(0x80000,"maincpu",0)
	ROM_LOAD("341-0728", 0x00000, 0x20000, CRC(8d410067) SHA1(c0f4704233ead14cb8e1e8a68fbd7063c56afd27) ) /* 341-0728: IIgs ROM03 prototype FC-FD - 28 pin MASK rom */
	ROM_LOAD("341-0729", 0x20000, 0x20000, NO_DUMP) /* 341-0729: IIgs ROM03 prototype FE-FF */
ROM_END

ROM_START(apple2gsr1)
	ROM_REGION(0xc00, "adbmicro", 0)
	ROM_LOAD( "341s0345.bin", 0x000000, 0x000c00, CRC(48cd5779) SHA1(97e421f5247c00a0ca34cd08b6209df573101480) )

	ROM_REGION(0x4000,"gfx1",0)
	ROM_LOAD ( "megaii.chr", 0x0000, 0x4000, BAD_DUMP CRC(ef0bfc11) SHA1(c068f3d02d442a8a38121d42318e9a962ee8913e)) /* need label/part number */

	ROM_REGION(0x80000,"maincpu",0)
	ROM_LOAD("342-0077-b", 0x20000, 0x20000, CRC(42f124b0) SHA1(e4fc7560b69d062cb2da5b1ffbe11cd1ca03cc37)) /* 342-0077-B: IIgs ROM01 */
ROM_END

ROM_START(apple2gsr0)
	ROM_REGION(0xc00, "adbmicro", 0)
	ROM_LOAD( "341s0345.bin", 0x000000, 0x000c00, CRC(48cd5779) SHA1(97e421f5247c00a0ca34cd08b6209df573101480) )

	ROM_REGION(0x4000,"gfx1",0)
	ROM_LOAD ( "megaii.chr", 0x0000, 0x4000, BAD_DUMP CRC(ef0bfc11) SHA1(c068f3d02d442a8a38121d42318e9a962ee8913e)) /* need label/part number */

	ROM_REGION(0x80000,"maincpu",0)
	ROM_LOAD("342-0077-a", 0x20000, 0x20000, CRC(dfbdd97b) SHA1(ff0c245dd0732ec4413a934fd80efc2defd8a8e3) ) /* 342-0077-A: IIgs ROM00 */
ROM_END

ROM_START(apple2gsr0p)  // 6/19/1986 Cortland prototype
	ROM_REGION(0xc00, "adbmicro", 0)
	ROM_LOAD( "341s0345.bin", 0x000000, 0x000c00, CRC(48cd5779) SHA1(97e421f5247c00a0ca34cd08b6209df573101480) )

	ROM_REGION(0x4000,"gfx1",0)
	ROM_LOAD ( "megaii.chr", 0x0000, 0x4000, BAD_DUMP CRC(ef0bfc11) SHA1(c068f3d02d442a8a38121d42318e9a962ee8913e)) /* need label/part number */

	ROM_REGION(0x80000,"maincpu",0)
	ROM_LOAD( "rombf.bin",    0x020000, 0x020000, CRC(ab04fedf) SHA1(977589a17553956d583a21020080a39dd396df5c) )
ROM_END

ROM_START(apple2gsr0p2)  // 3/10/1986 Cortland prototype, boots as "Apple //'ing - Alpha 2.0"
	ROM_REGION(0xc00, "adbmicro", 0)
	ROM_LOAD( "341s0345.bin", 0x000000, 0x000c00, CRC(48cd5779) SHA1(97e421f5247c00a0ca34cd08b6209df573101480) )

	ROM_REGION(0x4000,"gfx1",0)
	ROM_LOAD ( "megaii.chr", 0x0000, 0x4000, BAD_DUMP CRC(ef0bfc11) SHA1(c068f3d02d442a8a38121d42318e9a962ee8913e)) /* need label/part number */

	ROM_REGION(0x80000,"maincpu",0)
	ROM_LOAD( "apple iigs alpha rom 2.0 19860310.bin", 0x020000, 0x020000, CRC(a47d275f) SHA1(c5836adcfc8be69c7351b84afa94c814e8d92b81) )
ROM_END

ROM_START(apple2gsmt)
	// 50741 ADB MCU inside the IIgs system unit
	ROM_REGION(0x1000, "adbmicro", 0)
	ROM_LOAD( "341s0632-2.bin", 0x000000, 0x001000, CRC(e1c11fb0) SHA1(141d18c36a617ab9dce668445440d34354be0672) )

	ROM_REGION(0x4000, "gfx1", 0)
	ROM_LOAD ( "megaii.chr", 0x0000, 0x4000, BAD_DUMP CRC(ef0bfc11) SHA1(c068f3d02d442a8a38121d42318e9a962ee8913e)) /* need label/part number */

	ROM_REGION(0x80000, "maincpu", 0)
	// The Mark Twain ROM is 512K, with address bit 16 inverted (same as ROM 3).
	// The first 256K is filled with 64K of 0xF0, then 0xF1, 0xF2, and 0xF3.  I'm guessing this was meant to be
	// a small ROM disk at $F00000, like the Mac Classic has.  The second 256K is the firmware we all know from
	// the "System 6.0.1" source leak.
	ROM_LOAD( "mtrom.bin", 0x040000, 0x040000, CRC(d75414c5) SHA1(7054915f5e5f9f3bb2cbecf6830d4f80793694a6) )
	ROM_CONTINUE(0x10000, 0x10000)
	ROM_CONTINUE(0x00000, 0x10000)
	ROM_CONTINUE(0x30000, 0x10000)
	ROM_CONTINUE(0x20000, 0x10000)

	// The firmware for the built-in High-Speed SCSI Card
	ROM_REGION(0x8000, "hsscsi", 0)
	ROM_LOAD( "mtscsi.bin",   0x000000, 0x008000, CRC(7426c880) SHA1(1c16310e5c180701a05089d69c6e72e9dc7434f6) )
ROM_END

} // Anonymous namespace


/*    YEAR  NAME          PARENT    COMPAT  MACHINE     INPUT         CLASS           INIT       COMPANY           FULLNAME */
COMP( 1989, apple2gs,     0,        apple2, apple2gs,   apple2gsrom3, apple2gs_state, rom3_init, "Apple Computer", "Apple IIgs (ROM03)", MACHINE_SUPPORTS_SAVE )
COMP( 198?, apple2gsr3p,  apple2gs, 0,      apple2gs,   apple2gsrom3, apple2gs_state, rom3_init, "Apple Computer", "Apple IIgs (ROM03 prototype)", MACHINE_NOT_WORKING )
COMP( 1987, apple2gsr1,   apple2gs, 0,      apple2gsr1, apple2gs,     apple2gs_state, rom1_init, "Apple Computer", "Apple IIgs (ROM01)", MACHINE_SUPPORTS_SAVE )
COMP( 1986, apple2gsr0,   apple2gs, 0,      apple2gsr1, apple2gs,     apple2gs_state, rom1_init, "Apple Computer", "Apple IIgs (ROM00)", MACHINE_SUPPORTS_SAVE )
COMP( 1986, apple2gsr0p,  apple2gs, 0,      apple2gsr1, apple2gs,     apple2gs_state, rom1_init, "Apple Computer", "Apple IIgs (ROM00 prototype 6/19/1986)", MACHINE_SUPPORTS_SAVE )
COMP( 1986, apple2gsr0p2, apple2gs, 0,      apple2gsr1, apple2gs,     apple2gs_state, rom1_init, "Apple Computer", "Apple IIgs (ROM00 prototype 3/10/1986)", MACHINE_SUPPORTS_SAVE )
COMP( 1991, apple2gsmt,   apple2gs, 0,      apple2gsmt, apple2gsrom3, apple2gs_state, rom3_init, "Apple Computer", "Apple IIgs (1991 Mark Twain prototype)", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



apple3.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nathan Woods,R. Belmont
/***************************************************************************

    drivers/apple3.c

    Apple ///

    driver by Nathan Woods and R. Belmont

    Special thanks to Chris Smolinski (author of the Sara emulator)
    for his input about this poorly known system.

    Also thanks to Washington Apple Pi for the "Apple III DVD" containing the
    technical manual, schematics, and software.

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

#include "emu.h"
#include "apple3.h"

#include "bus/a2bus/cards.h"

#include "bus/rs232/rs232.h"

#include "machine/applefdintf.h"
#include "machine/input_merger.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "utf8.h"

void apple3_state::apple3_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(apple3_state::apple3_memory_r), FUNC(apple3_state::apple3_memory_w));
}

void apple3_state::apple3(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, 14.318181_MHz_XTAL / 7);        /* 2 MHz */
	m_maincpu->sync_cb().set(FUNC(apple3_state::apple3_sync_w));
	m_maincpu->set_addrmap(AS_PROGRAM, &apple3_state::apple3_map);

	config.set_maximum_quantum(attotime::from_hz(60));

	input_merger_device &mainirq(INPUT_MERGER_ANY_HIGH(config, "mainirq"));
	mainirq.output_handler().set_inputline(m_maincpu, m6502_device::IRQ_LINE);
	mainirq.output_handler().append(m_via[1], FUNC(via6522_device::write_pa7)).invert(); // this is active low

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(14.318181_MHz_XTAL, 910, 0, 560, 262, 0, 192);
	m_screen->set_screen_update(FUNC(apple3_state::screen_update));
	m_screen->set_palette(m_palette);
	m_screen->screen_vblank().set(m_via[1], FUNC(via6522_device::write_cb1));
	m_screen->screen_vblank().append(m_via[1], FUNC(via6522_device::write_cb2));
	m_screen->screen_vblank().append(FUNC(apple3_state::vbl_w));

	PALETTE(config, m_palette, FUNC(apple3_state::palette_init), 32);

	/* keyboard controller */
	AY3600(config, m_ay3600, 0);
	m_ay3600->x0().set_ioport("X0");
	m_ay3600->x1().set_ioport("X1");
	m_ay3600->x2().set_ioport("X2");
	m_ay3600->x3().set_ioport("X3");
	m_ay3600->x4().set_ioport("X4");
	m_ay3600->x5().set_ioport("X5");
	m_ay3600->x6().set_ioport("X6");
	m_ay3600->x7().set_ioport("X7");
	m_ay3600->x8().set_ioport("X8");
	m_ay3600->shift().set(FUNC(apple3_state::ay3600_shift_r));
	m_ay3600->control().set(FUNC(apple3_state::ay3600_control_r));
	m_ay3600->data_ready().set(FUNC(apple3_state::ay3600_data_ready_w));
	m_ay3600->ako().set(FUNC(apple3_state::ay3600_ako_w));

	/* repeat timer */
	TIMER(config, m_repttimer).configure_generic(FUNC(apple3_state::ay3600_repeat));

	/* slot bus */
	A2BUS(config, m_a2bus, 0);
	m_a2bus->set_space(m_maincpu, AS_PROGRAM);
	m_a2bus->irq_w().set(FUNC(apple3_state::a2bus_irq_w));
	m_a2bus->nmi_w().set(FUNC(apple3_state::a2bus_nmi_w));
	m_a2bus->inh_w().set(FUNC(apple3_state::a2bus_inh_w));
	m_a2bus->dma_w().set_inputline(m_maincpu, INPUT_LINE_HALT);
	A2BUS_SLOT(config, "sl1", 14.318181_MHz_XTAL / 2, m_a2bus, apple3_cards, nullptr);
	A2BUS_SLOT(config, "sl2", 14.318181_MHz_XTAL / 2, m_a2bus, apple3_cards, nullptr);
	A2BUS_SLOT(config, "sl3", 14.318181_MHz_XTAL / 2, m_a2bus, apple3_cards, nullptr);
	A2BUS_SLOT(config, "sl4", 14.318181_MHz_XTAL / 2, m_a2bus, apple3_cards, nullptr);

	/* fdc */
	APPLEIII_FDC(config, m_fdc, 1021800*2);
	applefdintf_device::add_525(config, "0");
	applefdintf_device::add_525(config, "1");
	applefdintf_device::add_525(config, "2");
	applefdintf_device::add_525(config, "3");

	/* softlist for fdc */
	SOFTWARE_LIST(config, "flop525_list").set_original("apple3");

	/* acia */
	MOS6551(config, m_acia, 0);
	m_acia->set_xtal(XTAL(1'843'200)); // HACK: The schematic shows an external clock generator but using a XTAL is faster to emulate.
	m_acia->irq_handler().set("mainirq", FUNC(input_merger_device::in_w<0>));
	m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_acia->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_acia->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_acia, FUNC(mos6551_device::write_rxd));
	rs232.dcd_handler().set(m_acia, FUNC(mos6551_device::write_dcd));
	rs232.dsr_handler().set(m_acia, FUNC(mos6551_device::write_dsr));
	// TODO: remove cts kludge from machine/apple3.cpp and come up with a good way of coping with pull up resistors.

	/* paddle */
	TIMER(config, "pdltimer").configure_generic(FUNC(apple3_state::paddle_timer));

	/* rtc */
	MM58167(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->irq().set(m_via[1], FUNC(via6522_device::write_ca1));

	/* via */
	MOS6522(config, m_via[0], 14.318181_MHz_XTAL / 14);
	m_via[0]->writepa_handler().set(FUNC(apple3_state::apple3_via_0_out_a));
	m_via[0]->writepb_handler().set(FUNC(apple3_state::apple3_via_0_out_b));
	m_via[0]->irq_handler().set("mainirq", FUNC(input_merger_device::in_w<2>));

	MOS6522(config, m_via[1], 14.318181_MHz_XTAL / 14);
	m_via[1]->writepa_handler().set(FUNC(apple3_state::apple3_via_1_out_a));
	m_via[1]->writepb_handler().set(FUNC(apple3_state::apple3_via_1_out_b));
	m_via[1]->irq_handler().set("mainirq", FUNC(input_merger_device::in_w<1>));

	/* sound */
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_bell, 0).add_route(ALL_OUTPUTS, "speaker", 0.99);
	DAC_6BIT_BINARY_WEIGHTED(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.125); // 6522.b5(pb0-pb5) + 320k,160k,80k,40k,20k,10k

	TIMER(config, "c040").configure_periodic(FUNC(apple3_state::apple3_c040_tick), attotime::from_hz(2000));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("256K").set_extra_options("128K, 512K");
}


static INPUT_PORTS_START( apple3 )
	/*
	  KB3600 Keyboard matrix (KB3600 has custom layout mask ROM, Apple p/n 341-0035)

	      | Y0  | Y1  | Y2  | Y3  | Y4  | Y5  | Y6  | Y7  | Y8  | Y9  |
	      |     |     |     |     |     |     |     |     |     |     |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X0  | ESC |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  8  |  9  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X1  | TAB |  Q  |  W  |  E  |  R  |  T  |  Y  |  U  |  I  |  O  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X2  |  A  |  S  |  D  |  F  |  H  |  G  |  J  |  K  |  ;: |  L  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X3  |  Z  |  X  |  C  |  V  |  B  |  N  |  M  |  <, |  >. |  ?/ |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X4  |     | KP9 |     | KP8 |     | KP7 |  \| |  += |  0  |  -_ |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X5  |     | KP6 |     | KP5 |     | KP4 |  `~ |  ]} |  P  | [{  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X6  |     | KP3 | KP. | KP2 | KP0 | KP1 |RETRN| UP  |BACKS| '"  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X7  |     |     |KPENT|SPACE|     | KP- |RIGHT|DOWN |LEFT |     |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	*/

/*
    Esc
    0x00

      `    1    2    3    4    5    6    7    8    9    0    -   =   BACKSPACE
    0x38 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x30 0x31 0x2f 0x104

     Tab   Q    W    E    R    T    Y    U   I     O    P    [    ]    \
    0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x3a 0x3b 0x39 0x2e

      A    S    D    F    G    H    J    K    L    ;    '   ENTER
    0x14 0x15 0x16 0x17 0x19 0x18 0x1a 0x1b 0x1d 0x1c 0x105 0x102

      Z    X    C    V    B    N    M   ,     .    /
    0x1e 0x1f 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27

    SPACE   UP    LT    DN    RT   KP-   KP7  KP8  KP9
    0x109  0x103 0x10e 0x10d 0x10c 0x10b 0x2d 0x2b 0x29

    KP4  KP5  KP6   KP1  KP2  KP3  KPEN  KP0   KP.
    0x37 0x35 0x33 0x101 0x3f 0x3d 0x108 0x100 0x3e
*/
	PORT_START("X0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Esc")      PORT_CODE(KEYCODE_ESC)      PORT_CHAR(27)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)      PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)  PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)  PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)  PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)  PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)  PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)  PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)  PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)  PORT_CHAR('9') PORT_CHAR('(')

	PORT_START("X1")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab")      PORT_CODE(KEYCODE_TAB)      PORT_CHAR(9)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)  PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)  PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)  PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)  PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)  PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)  PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)  PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)  PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)  PORT_CHAR('O') PORT_CHAR('o')

	PORT_START("X2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)  PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)  PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)  PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)  PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)  PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)  PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)  PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)  PORT_CHAR('L') PORT_CHAR('l')

	PORT_START("X3")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)  PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)  PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)  PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)  PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)          PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)  PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)  PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)  PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)   PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)  PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("X4")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)       PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)       PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)   PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)      PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)  PORT_CHAR('-') PORT_CHAR('_')

	PORT_START("X5")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)   PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)   PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)   PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('`') PORT_CHAR('~')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)  PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[') PORT_CHAR('{')

	PORT_START("X6")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)   PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD)     PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)   PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)   PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)   PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Return")   PORT_CODE(KEYCODE_ENTER)    PORT_CHAR(13)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_UP)        PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Delete")   PORT_CODE(KEYCODE_BACKSPACE)PORT_CHAR(8)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)  PORT_CHAR('\'') PORT_CHAR('\"')

	PORT_START("X7")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD)   PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)  PORT_CHAR(' ')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD)   PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT)     PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN)      PORT_CODE(KEYCODE_DOWN)     PORT_CHAR(10)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT)      PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X8")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("keyb_special")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Caps Lock")    PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Shift")   PORT_CODE(KEYCODE_LSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Shift")  PORT_CODE(KEYCODE_RSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Control") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apple3_state::apple3_state::keyb_special_changed), 0) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Open Apple")   PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Solid Apple")  PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RESET") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(apple3_state::apple3_state::keyb_special_changed), 0) PORT_CODE(KEYCODE_F12)

	PORT_START("joy_1_x")      /* Joystick 1 X Axis */
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_NAME("P1 Joystick X")
	PORT_MINMAX(0, 0xff) PORT_PLAYER(1)

	PORT_START("joy_1_y")      /* Joystick 1 Y Axis */
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_NAME("P1 Joystick Y")
	PORT_MINMAX(0,0xff) PORT_PLAYER(1)

	PORT_START("joy_2_x")      /* Joystick 2 X Axis */
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_NAME("P2 Joystick X")
	PORT_MINMAX(0,0xff) PORT_PLAYER(2)

	PORT_START("joy_2_y")      /* Joystick 2 Y Axis */
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_NAME("P2 Joystick Y")
	PORT_MINMAX(0,0xff) PORT_PLAYER(2)

	PORT_START("joy_buttons")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_PLAYER(1) PORT_NAME("Joystick 1 Button 1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_PLAYER(1) PORT_NAME("Joystick 1 Button 2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_PLAYER(2) PORT_NAME("Joystick 2 Button 1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_PLAYER(2) PORT_NAME("Joystick 2 Button 2")
INPUT_PORTS_END

ROM_START(apple3)
	ROM_REGION(0x1000,"maincpu",0)
	ROM_SYSTEM_BIOS(0, "original", "Apple /// boot ROM")
	ROMX_LOAD( "apple3.rom", 0x0000, 0x1000, CRC(55e8eec9) SHA1(579ee4cd2b208d62915a0aa482ddc2744ff5e967), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "soshd", "Rob Justice SOSHDBOOT")
	ROMX_LOAD( "soshdboot.bin", 0x000000, 0x001000, CRC(fd5ac9e2) SHA1(ba466a54ddb7f618c4f18f344754343c5945b417), ROM_BIOS(1))
ROM_END

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT         COMPANY           FULLNAME */
COMP( 1980, apple3, 0,      0,      apple3,  apple3, apple3_state, init_apple3, "Apple Computer", "Apple ///", MACHINE_SUPPORTS_SAVE )



applix.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

    Applix 1616 computer

    See for docs: http://psiphi.server101.com/applix/

    First revealed to the world in December 1986 issue of Electronics Today
    International (ETI) an Australian electronics magazine which is now defunct.

    The main articles appeared in ETI February/March/April 1987, followed by
    other articles in various issues after that.

    Current Status:
    After 60 seconds, boots to the ramdisk. You can enter commands.
    If you have a floppy mounted, it will boot from the disk.

    The system could support 1 or 2 5.25 or 3.5 floppy drives, although 3.5
    was the recommended hardware. Format is similar to the PC 720kb, except
    it has 5 sectors of 1024 bytes, giving 800kb total. We only support the
    3.5-sized disks.

    TODO:
    - Cassette interface (coded but not working)
    - Use kbtro device (tried and failed)
    - Optional SCSI controller NCR5380 and hard drive (max 40mb)
    - Joystick
    - Audio: it could be better
    - DAC output is used to compare against analog inputs; core doesn't permit
      audio outputs to be used for non-speaker purposes.
    - BIOS 5 crashes MAME after scrolling about half a screen

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

#include "emu.h"

#include "bus/centronics/ctronics.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
#include "cpu/mcs51/mcs51.h"
#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "machine/6522via.h"
#include "machine/timer.h"
#include "machine/wd_fdc.h"
#include "machine/z80scc.h"
#include "sound/dac.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/applix_dsk.h"



class applix_state : public driver_device
{
public:
	applix_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_base(*this, "base")
		, m_maincpu(*this, "maincpu")
		, m_crtc(*this, "crtc")
		, m_via(*this, "via6522")
		, m_centronics(*this, "centronics")
		, m_cent_data_out(*this, "cent_data_out")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0U)
		, m_ldac(*this, "ldac")
		, m_rdac(*this, "rdac")
		, m_cass(*this, "cassette")
		, m_io_dsw(*this, "DSW")
		, m_io_fdc(*this, "FDC")
		, m_io_k0f(*this, "K0f")
		, m_io_k3x0(*this, "K3%x_0", 0U)
		, m_io_k3x1(*this, "K3%x_1", 0U)
		, m_io_k0b(*this, "K0b")
		, m_expansion(*this, "expansion")
		, m_palette(*this, "palette")
	{ }

	void applix(machine_config &config);

	void init_applix();

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	u16 applix_inputs_r();
	void palette_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	void analog_latch_w(u16 data);
	void dac_latch_w(u16 data);
	void video_latch_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	u8 applix_pb_r();
	void applix_pa_w(u8 data);
	void applix_pb_w(u8 data);
	void vsync_w(int state);
	u8 port00_r();
	u8 port08_r();
	u8 port10_r();
	u8 port18_r();
	u8 port20_r();
	u8 port60_r();
	void port08_w(u8 data);
	void port10_w(u8 data);
	void port18_w(offs_t offset, u8 data);
	void port20_w(u8 data);
	void port60_w(u8 data);
	u16 fdc_data_r();
	u16 fdc_stat_r(offs_t offset);
	void fdc_data_w(u16 data);
	void fdc_cmd_w(u16 data);
	static void floppy_formats(format_registration &fr);
	u8 internal_data_read(offs_t offset);
	void internal_data_write(offs_t offset, u8 data);
	u8 p1_read();
	void p1_write(u8 data);
	u8 p2_read();
	void p2_write(u8 data);
	u8 p3_read();
	void p3_write(u8 data);
	TIMER_DEVICE_CALLBACK_MEMBER(cass_timer);

	MC6845_UPDATE_ROW(crtc_update_row);
	MC6845_BEGIN_UPDATE(crtc_update_border);
	void applix_palette(palette_device &palette) const;

	u8 m_video_latch = 0U;
	u8 m_pa = 0U;
	u8 m_palette_latch[4]{};
	required_shared_ptr<u16> m_base;

	void main_mem(address_map &map) ATTR_COLD;
	void keytronic_pc3270_io(address_map &map) ATTR_COLD;
	void keytronic_pc3270_program(address_map &map) ATTR_COLD;
	void sub_io(address_map &map) ATTR_COLD;
	void sub_mem(address_map &map) ATTR_COLD;

	u8 m_pb = 0U;
	u8 m_analog_latch = 0U;
	u8 m_dac_latch = 0U;
	u8 m_port08 = 0U;
	u8 m_data_to_fdc = 0U;
	u8 m_data_from_fdc = 0U;
	bool m_data = 0;
	bool m_data_or_cmd = 0;
	bool m_buffer_empty = 0;
	bool m_fdc_cmd = 0;
	u8 m_clock_count = 0U;
	bool m_cp = 0;
	u8   m_p1 = 0U;
	u8   m_p1_data = 0U;
	u8   m_p2 = 0U;
	u8   m_p3 = 0U;
	u16  m_last_write_addr = 0U;
	u8 m_cass_data[4]{};
	required_device<cpu_device> m_maincpu;
	required_device<mc6845_device> m_crtc;
	required_device<via6522_device> m_via;
	required_device<centronics_device> m_centronics;
	required_device<output_latch_device> m_cent_data_out;
	required_device<wd1772_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<dac_byte_interface> m_ldac;
	required_device<dac_byte_interface> m_rdac;
	required_device<cassette_image_device> m_cass;
	required_ioport m_io_dsw;
	required_ioport m_io_fdc;
	required_ioport m_io_k0f;
	required_ioport_array<12> m_io_k3x0;
	required_ioport_array<8> m_io_k3x1;
	required_ioport m_io_k0b;
	required_shared_ptr<u16> m_expansion;

	required_device<palette_device> m_palette;
};

/*
d0,1,2 = joystick
d3     = cassette LED, low=on
d4,5,6 = audio select
d7     = cassette relay, low=on
*/
void applix_state::analog_latch_w(u16 data)
{
	data &= 0xff;
	if (data != m_analog_latch)
	{
		m_cass->change_state(
			(BIT(data,7)) ? CASSETTE_MOTOR_DISABLED : CASSETTE_MOTOR_ENABLED, CASSETTE_MASK_MOTOR);

		m_analog_latch = data;
	}
}

void applix_state::dac_latch_w(u16 data)
{
	data &= 0xff;
	m_dac_latch = data;

	if ((m_analog_latch & 0x70) == 0) // right
		m_rdac->write(m_dac_latch);
	else
	if ((m_analog_latch & 0x70) == 0x10) // left
		m_ldac->write(m_dac_latch);
}

//cent = odd, video = even
void applix_state::palette_w(offs_t offset, u16 data, u16 mem_mask)
{
	offset >>= 4;
	if (ACCESSING_BITS_0_7)
	{
		m_cent_data_out->write(data);
	}
	else
		m_palette_latch[offset] = (data >> 8) & 15;
}

void applix_state::video_latch_w(offs_t offset, u16 data, u16 mem_mask)
{
	if (ACCESSING_BITS_0_7)
		m_video_latch = data;
}

/*
d0   = dac output + external signal = analog input
d1   = cassette in
d2,3 = joystick in
d4-7 = SW2 dipswitch block
*/
u16 applix_state::applix_inputs_r()
{
	return m_io_dsw->read() | m_cass_data[2];
}

u8 applix_state::applix_pb_r()
{
	return m_pb;
}

/*
d0 = /(in) printer busy signal
d1 = /(out) printer strobe
d2 = /(out) enable cassette write IRQ
d3 = (out) H = 640 video mode
d4 = /(out) enable cassette read IRQ
d5 = /(out) clear cass IRQ and output line
d6 = /(out) reset keyboard by pulling kbd clock low
d7 = /(out) reset keyboard flipflop
*/
void applix_state::applix_pa_w(u8 data)
{
	// Reset flipflop counter
	if (!BIT(data, 7))
		m_clock_count = 0;

	// Reset keyboard
	if (!BIT(data, 6))
	{
		m_p3 = 0xff;
		m_last_write_addr = 0;
	}
	m_cass->output(BIT(data, 5) ? -1.0 : +1.0);

	// high-to-low of PA5 when reading cassette - /PRE on IC32b
	if (BIT(m_pa, 5) && !BIT(data, 5) && !BIT(data, 4))
		m_maincpu->set_input_line(M68K_IRQ_4, CLEAR_LINE);

	// low-to-high of PA2 when writing cassette - /PRE on IC49
	if (!BIT(m_pa, 2) && BIT(data, 2))
		m_maincpu->set_input_line(M68K_IRQ_4, CLEAR_LINE);

	m_centronics->write_strobe(BIT(data, 1));

	m_pa = data;
}

/*
d0-6 = user
d7   = square wave output for cassette IRQ
*/
void applix_state::applix_pb_w(u8 data)
{
	// low-to-high of PB7 when writing cassette - CLK on IC49
	if (!BIT(m_pb, 7) && BIT(data, 7))
		if (!BIT(m_pa, 2))
			m_maincpu->set_input_line(M68K_IRQ_4, ASSERT_LINE);

	m_pb = data;
}

/*
d0 = H if 68000 sent a command
d1 = H if 68000 sent a byte
d2 = H if 68000 has read last byte
d3 = test switch
*/
u8 applix_state::port00_r()
{
	return (u8)m_data_or_cmd | ((u8)m_data << 1) | ((u8)m_buffer_empty << 2) | m_io_fdc->read();
}

/*
d0 = /RDY
d1 = /DISC CHANGE
d2 = DS0
d3 = DS1
d4 = MOTORON
d5 = SIDE
d6 = BANK
d7 = MAP
*/
u8 applix_state::port08_r()
{
	return m_port08 | 3;
}

/*
d0 = /INUSE
d1 = /EJECT
d2-7 same as for port08_r
*/
void applix_state::port08_w(u8 data)
{
	m_port08 = data;
	membank("bank1")->set_entry(BIT(data, 6));

	floppy_image_device *floppy = nullptr;
	if (BIT(data, 2)) floppy = m_floppy[0]->get_device();
	if (BIT(data, 3)) floppy = m_floppy[1]->get_device();

	m_fdc->set_floppy(floppy);

	if (floppy)
	{
		floppy->mon_w(0);
		floppy->ss_w(BIT(data, 5));
	}
}

u8 applix_state::port10_r()
{
	return 0;
}

void applix_state::port10_w(u8 data)
{
}

u8 applix_state::port18_r()
{
	m_data = 0;
	return m_data_to_fdc;
}

void applix_state::port18_w(offs_t offset, u8 data)
{
	m_data_from_fdc = data;
	m_buffer_empty = 0;
	m_fdc_cmd = BIT(offset, 2);
}

u8 applix_state::port20_r()
{
	return 0;
}

void applix_state::port20_w(u8 data)
{
}

u8 applix_state::port60_r()
{
	return 0;
}

void applix_state::port60_w(u8 data)
{
}

u16 applix_state::fdc_stat_r(offs_t offset)
{
	u8 data = 0;
	switch (offset)
	{
	case 0: data = (u8)m_buffer_empty^1; break;
	case 1: data = (u8)m_data^1; break;
	default: data = (u8)m_fdc_cmd; // case 2
	}
	return data << 7;
}

u16 applix_state::fdc_data_r()
{
	m_buffer_empty = 1;
	return m_data_from_fdc;
}

void applix_state::fdc_data_w(u16 data)
{
	m_data_to_fdc = data;
	m_data = 1;
	m_data_or_cmd = 0;
}

void applix_state::fdc_cmd_w(u16 data)
{
	m_data_to_fdc = data;
	m_data = 1;
	m_data_or_cmd = 1;
}

void applix_state::main_mem(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xffffff);
	map(0x000000, 0x3fffff).ram().share("expansion"); // Expansion
	map(0x400000, 0x47ffff).ram().mirror(0x80000).share("base"); // Main ram
	map(0x500000, 0x51ffff).rom().region("maincpu", 0);
	map(0x600000, 0x60007f).w(FUNC(applix_state::palette_w));
	map(0x600080, 0x6000ff).w(FUNC(applix_state::dac_latch_w));
	map(0x600100, 0x60017f).w(FUNC(applix_state::video_latch_w)); //video latch (=border colour, high nybble; video base, low nybble) (odd)
	map(0x600180, 0x6001ff).w(FUNC(applix_state::analog_latch_w));
	map(0x700000, 0x700007).mirror(0x78).rw("scc", FUNC(scc8530_device::ab_dc_r), FUNC(scc8530_device::ab_dc_w)).umask16(0xff00).cswidth(16);
	map(0x700080, 0x7000ff).r(FUNC(applix_state::applix_inputs_r));
	map(0x700100, 0x70011f).mirror(0x60).m(m_via, FUNC(via6522_device::map)).umask16(0xff00).cswidth(16);
	map(0x700180, 0x700180).mirror(0x7c).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w)).cswidth(16);
	map(0x700182, 0x700182).mirror(0x7c).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w)).cswidth(16);
	map(0xffffc0, 0xffffc1).rw(FUNC(applix_state::fdc_data_r), FUNC(applix_state::fdc_data_w));
	//map(0xffffc2, 0xffffc3).rw(FUNC(applix_state::fdc_int_r) , FUNC(applix_state::fdc_int_w)); // optional
	map(0xffffc8, 0xffffcd).r(FUNC(applix_state::fdc_stat_r));
	map(0xffffd0, 0xffffd1).w(FUNC(applix_state::fdc_cmd_w));
	//600000, 6FFFFF  io ports and latches
	//700000, 7FFFFF  peripheral chips and devices
	//800000, FFC000  optional roms
	//FFFFC0, FFFFFF  disk controller board
}

void applix_state::sub_mem(address_map &map)
{
	map(0x0000, 0x5fff).rom();
	map(0x6000, 0x7fff).ram();
	map(0x8000, 0xffff).bankrw("bank1");
}

void applix_state::sub_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x07).r(FUNC(applix_state::port00_r)); //PORTR
	map(0x08, 0x0f).rw(FUNC(applix_state::port08_r), FUNC(applix_state::port08_w)); //Disk select
	map(0x10, 0x17).rw(FUNC(applix_state::port10_r), FUNC(applix_state::port10_w)); //IRQ
	map(0x18, 0x1f).rw(FUNC(applix_state::port18_r), FUNC(applix_state::port18_w)); //data&command
	map(0x20, 0x27).mirror(0x18).rw(FUNC(applix_state::port20_r), FUNC(applix_state::port20_w)); //SCSI NCR5380
	map(0x40, 0x43).mirror(0x1c).rw(m_fdc, FUNC(wd1772_device::read), FUNC(wd1772_device::write)); //FDC
	map(0x60, 0x63).mirror(0x1c).rw(FUNC(applix_state::port60_r), FUNC(applix_state::port60_w)); //anotherZ80SCC
}

void applix_state::keytronic_pc3270_program(address_map &map)
{
	map(0x0000, 0x0fff).rom().region("kbdcpu", 0);
}

void applix_state::keytronic_pc3270_io(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(applix_state::internal_data_read), FUNC(applix_state::internal_data_write));
}

// io priorities:
// 4 cassette
// 3 scc
// 2 via

/* Input ports */
static INPUT_PORTS_START( applix )
	PORT_START( "K0f" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_5)                                PORT_CHAR('5') PORT_CHAR('%')       /* 06 */
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_4)                                PORT_CHAR('4') PORT_CHAR('$')       /* 05 */
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_T)                                PORT_CHAR('t') PORT_CHAR('T')       /* 14 */
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_R)                                PORT_CHAR('r') PORT_CHAR('R')       /* 13 */
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_G)                                PORT_CHAR('g') PORT_CHAR('G')       /* 22 */
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_F)                                PORT_CHAR('f') PORT_CHAR('F')       /* 21 */
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("F7 (IRMA)")              /* 41 */
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("?6a?")                   /* 6a */

	PORT_START( "K30_0" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_N)                                PORT_CHAR('n') PORT_CHAR('N')       /* 31 */
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_M)                                PORT_CHAR('m') PORT_CHAR('M')       /* 32 */
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_B)                                PORT_CHAR('b') PORT_CHAR('B')       /* 30 */
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_V)                                PORT_CHAR('v') PORT_CHAR('V')       /* 2f */
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_C)                                PORT_CHAR('c') PORT_CHAR('C')       /* 2e */
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_COMMA)                            PORT_CHAR(',') PORT_CHAR('<')       /* 33 */
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START( "K30_1" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_F1)                               PORT_CHAR(UCHAR_MAMEKEY(F1))        /* 58 */
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_F2)                               PORT_CHAR(UCHAR_MAMEKEY(F2))        /* 59 */
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_F3)                               PORT_CHAR(UCHAR_MAMEKEY(F3))        /* 5a */
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_F4)                               PORT_CHAR(UCHAR_MAMEKEY(F4))        /* 5b */
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_F5)                               PORT_CHAR(UCHAR_MAMEKEY(F5))        /* 5c */
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_F6)                               PORT_CHAR(UCHAR_MAMEKEY(F6))        /* 5d */
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("?6b?")                   /* 6b */
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("F8 (IRMA)")              /* 42 */

	PORT_START( "K31_0" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_6)                                PORT_CHAR('6') PORT_CHAR('^')       /* 07 */
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_7)                                PORT_CHAR('7') PORT_CHAR('&')       /* 08 */
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_Y)                                PORT_CHAR('y') PORT_CHAR('Y')       /* 15 */
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_U)                                PORT_CHAR('u') PORT_CHAR('U')       /* 16 */
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_H)                                PORT_CHAR('h') PORT_CHAR('H')       /* 23 */
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_J)                                PORT_CHAR('j') PORT_CHAR('J')       /* 24 */
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START( "K31_1" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_F7)                               PORT_CHAR(UCHAR_MAMEKEY(F7))        /* 37 */
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_F8)                               PORT_CHAR(UCHAR_MAMEKEY(F8))        /* 5f */
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_LSHIFT)       PORT_NAME("LShift") PORT_CHAR(UCHAR_SHIFT_1)            /* 2a */
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("<")                      /* 70 */
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_Z)                                PORT_CHAR('z') PORT_CHAR('Z')       /* 2c */
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_X)                                PORT_CHAR('x') PORT_CHAR('X')       /* 2d */
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("?6c?")                   /* 6c */
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("F9 (IRMA)")              /* 43 */

	PORT_START( "K32_0" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_9)                                PORT_CHAR('9') PORT_CHAR('(')       /* 0a */
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_8)                                PORT_CHAR('8') PORT_CHAR('*')       /* 09 */
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_O)                                PORT_CHAR('o') PORT_CHAR('O')       /* 18 */
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_I)                                PORT_CHAR('i') PORT_CHAR('I')       /* 17 */
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_L)                                PORT_CHAR('l') PORT_CHAR('L')       /* 26 */
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_K)                                PORT_CHAR('k') PORT_CHAR('K')       /* 25 */
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START( "K32_1" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_F9)                               PORT_CHAR(UCHAR_MAMEKEY(F9))        /* 57 */
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_F10)                              PORT_CHAR(UCHAR_MAMEKEY(F10))       /* 1d */
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_LCONTROL)                         PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))  /* 71 */
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_LALT)                             PORT_NAME("LAlt")                   /* 38 */
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_SPACE)                            PORT_CHAR(' ')                      /* 39 */
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_RALT)                             PORT_NAME("RAlt")                   /* 38 */
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("?69?")                   /* 69 */
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("F6 (IRMA)")              /* 40 */

	PORT_START( "K33_0" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_DOWN)    PORT_NAME("KP 2")                   /* 50 */
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_END)     PORT_NAME("KP 1")                   /* 4f */
	PORT_BIT( 0x0c, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("Down")                   /* 55 */
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("Enter")                  /* 75 */
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START( "K33_1" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_1)                                PORT_CHAR('1') PORT_CHAR('!')       /* 02 */
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_TILDE)                            PORT_CHAR('`') PORT_CHAR('~')       /* 29 */
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_Q)                                PORT_CHAR('q') PORT_CHAR('Q')       /* 10 */
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_TAB)                              PORT_CHAR(9)                        /* 0f */
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_A)                                PORT_CHAR('a') PORT_CHAR('A')       /* 1e */
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_CAPSLOCK)                         PORT_NAME("Caps")                   /* 3a */
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("?68?")                   /* 68 */
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("F5 (IRMA)")              /* 3f */

	PORT_START( "K34_0" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_SLASH)                            PORT_CHAR('/') PORT_CHAR('?')       /* 35 */
	PORT_BIT( 0x0c, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_RSHIFT)                           PORT_CHAR(UCHAR_MAMEKEY(RSHIFT))    /* 36 */
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("Left")                   /* 56 */
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_STOP)                             PORT_CHAR('.') PORT_CHAR('>')       /* 34 */
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START( "K34_1" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_2)                                PORT_CHAR('2') PORT_CHAR('@')       /* 02 */
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_3)                                PORT_CHAR('3') PORT_CHAR('#')       /* 03 */
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_W)                                PORT_CHAR('w') PORT_CHAR('W')       /* 11 */
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_E)                                PORT_CHAR('e') PORT_CHAR('E')       /* 12 */
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_S)                                PORT_CHAR('s') PORT_CHAR('S')       /* 1f */
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_D)                                PORT_CHAR('d') PORT_CHAR('D')       /* 20 */
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("?67?")                   /* 67 */
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("F4 (IRMA)")              /* 3e */

	PORT_START( "K35_0" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_0)                                PORT_CHAR('0') PORT_CHAR(')')       /* 0b */
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_MINUS)                            PORT_CHAR('-') PORT_CHAR('_')       /* 0c */
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_P)                                PORT_CHAR('p') PORT_CHAR('P')       /* 19 */
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_OPENBRACE)                        PORT_CHAR('[') PORT_CHAR('{')       /* 1a */
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_COLON)                            PORT_CHAR(';') PORT_CHAR(':')       /* 27 */
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_QUOTE)                            PORT_CHAR('\'') PORT_CHAR('"')      /* 28 */
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START( "K35_1" )
	PORT_BIT( 0x3f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("?66?")                   /* 66 */
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("F3 (IRMA)")              /* 3d */

	PORT_START( "K36_0" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_BACKSPACE)                        PORT_CHAR(8)                        /* 0e */
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_EQUALS)                           PORT_CHAR('=') PORT_CHAR('+')       /* 0d */
	PORT_BIT( 0x14, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_ENTER)                            PORT_CHAR(13)                       /* 1c */
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_BACKSLASH)                        PORT_CHAR('\\') PORT_CHAR('|')      /* 2b */
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_CLOSEBRACE)                       PORT_CHAR(']') PORT_CHAR('}')       /* 1b */
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START( "K36_1" )
	PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("F2 (IRMA)")              /* 3c */

	PORT_START( "K37_0" )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("PA1")                    /* 7b */
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("|<--")                   /* 7e */
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("/a\\")                   /* 7a */
	PORT_BIT( 0x30, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_PLUS_PAD)                         PORT_NAME("KP +")                   /* 4e */
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START( "K37_1" )
	PORT_BIT( 0x3f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("?64?")                   /* 64 */
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("F1 (IRMA)")              /* 3b */

	PORT_START( "K38_0" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("SysReq")                 /* 54 */
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   /*PORT_CODE(KEYCODE_SCRLOCK)*/                      PORT_NAME("ScrLock")                /* 46 */
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("-->|")                   /* 7c */
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_PGUP)    PORT_NAME("KP 9")                   /* 49 */
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_MINUS_PAD)                        PORT_NAME("KP -")                   /* 4a */
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_RIGHT)   PORT_NAME("KP 6")                   /* 4d */
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START( "K39_0" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_ESC)                              PORT_NAME("Esc")                    /* 01 */
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_NUMLOCK)                          PORT_NAME("NumLock")                /* 45 */
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_HOME)    PORT_NAME("KP 7")                   /* 47 */
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_UP)      PORT_NAME("KP 8")                   /* 48 */
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_LEFT)    PORT_NAME("KP 4")                   /* 4b */
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_5_PAD)                            PORT_NAME("KP 5")                   /* 4c */
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("?76?")                   /* 76 */
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("?63?")                   /* 63 */

	PORT_START( "K3a_0" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("PrtSc *")                /* 6f */
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("PA2")                    /* 7f */
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("Right")                  /* 7d */
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("/a")                     /* 79 */
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("Center")                 /* 77 */
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("?6e?")                   /* 6e */
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("?62?")                   /* 62 */

	PORT_START( "K3b_0" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_PGDN)    PORT_NAME("KP 3")                   /* 51 */
	PORT_BIT( 0x06, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_INSERT)  PORT_NAME("KP 0")                   /* 52 */
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE(KEYCODE_DEL_PAD) PORT_CODE(KEYCODE_DEL)   PORT_NAME("KP .")                   /* 53 */
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("Up")                     /* 78 */
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("?6d?")                   /* 6d */
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )                                                       PORT_NAME("F10 (IRMA)")             /* 44 */

	PORT_START( "K0b" )
	PORT_DIPNAME( 0x01, 0x01, "Protocol selection" )
	PORT_DIPSETTING( 0x00, "Enhanced XT, AT and PS/2 models" )
	PORT_DIPSETTING( 0x01, "Standard PC and XT" )
	PORT_DIPNAME( 0x02, 0x00, "IRMA/Native scan code set" )
	PORT_DIPSETTING( 0x00, "Native scan code set" )
	PORT_DIPSETTING( 0x02, "IRMA Emulation" )
	PORT_DIPNAME( 0x04, 0x04, "Enhanced 101/Native scan code set" )
	PORT_DIPSETTING( 0x00, "Native scan code set" )
	PORT_DIPSETTING( 0x04, "Enhanced 101 scan code set" )
	PORT_DIPNAME( 0x08, 0x08, "Enable E0" )
	PORT_DIPSETTING( 0x00, "Enable E0" )
	PORT_DIPSETTING( 0x08, "Disable E0" )
	PORT_DIPNAME( 0x10, 0x10, "Code tables" )
	PORT_DIPSETTING( 0x00, "U.S. code tables" )
	PORT_DIPSETTING( 0x10, "International code tables" )
	PORT_BIT( 0x60, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_DIPNAME( 0x80, 0x80, "Key click" )
	PORT_DIPSETTING( 0x00, "No key click" )
	PORT_DIPSETTING( 0x80, "Key click" )

	PORT_START("DSW")
	PORT_BIT( 0xf, 0, IPT_UNUSED )
	PORT_DIPNAME( 0x10, 0x00, "Switch 0") PORT_DIPLOCATION("SW2:1")
	PORT_DIPSETTING(    0x10, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x20, 0x00, "Switch 1") PORT_DIPLOCATION("SW2:2")
	PORT_DIPSETTING(    0x20, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x40, 0x00, "Switch 2") PORT_DIPLOCATION("SW2:3")
	PORT_DIPSETTING(    0x40, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x80, 0x80, "Switch 3") PORT_DIPLOCATION("SW2:4")
	PORT_DIPSETTING(    0x80, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))

	PORT_START("FDC")
	PORT_BIT( 0xf7, 0, IPT_UNUSED )
	PORT_DIPNAME( 0x08, 0x08, "FDC Test") PORT_DIPLOCATION("SW3:1")
	PORT_DIPSETTING(    0x08, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
INPUT_PORTS_END


void applix_state::machine_reset()
{
	u8* ROM = memregion("maincpu")->base();
	memcpy(m_expansion, ROM, 8);
	membank("bank1")->set_entry(0);
	m_p3 = 0xff;
	m_last_write_addr = 0;
	m_maincpu->reset();
}

void applix_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_APPLIX_FORMAT);
}

static void applix_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
}


void applix_state::applix_palette(palette_device &palette) const
{
	// shades need to be verified - the names on the right are from the manual
	constexpr rgb_t colors[16] = {
			{ 0x00, 0x00, 0x00 },   //  0 Black
			{ 0x40, 0x40, 0x40 },   //  1 Dark Grey
			{ 0x00, 0x00, 0x80 },   //  2 Dark Blue
			{ 0x00, 0x00, 0xff },   //  3 Mid Blue
			{ 0x00, 0x80, 0x00 },   //  4 Dark Green
			{ 0x00, 0xff, 0x00 },   //  5 Green
			{ 0x00, 0xff, 0xff },   //  6 Blue Grey
			{ 0x00, 0x7f, 0x7f },   //  7 Light Blue
			{ 0x7f, 0x00, 0x00 },   //  8 Dark Red
			{ 0xff, 0x00, 0x00 },   //  9 Red
			{ 0x7f, 0x00, 0x7f },   // 10 Dark Violet
			{ 0xff, 0x00, 0xff },   // 11 Violet
			{ 0x7f, 0x7f, 0x00 },   // 12 Brown
			{ 0xff, 0xff, 0x00 },   // 13 Yellow
			{ 0xbf, 0xbf, 0xbf },   // 14 Light Grey
			{ 0xff, 0xff, 0xff } }; // 15 White

	palette.set_pen_colors(0, colors);
}


void applix_state::machine_start()
{
	std::fill(std::begin(m_palette_latch), std::end(m_palette_latch), 0);

	save_item(NAME(m_video_latch));
	save_item(NAME(m_pa));
	save_item(NAME(m_palette_latch));
	save_item(NAME(m_pb));
	save_item(NAME(m_analog_latch));
	save_item(NAME(m_dac_latch));
	save_item(NAME(m_port08));
	save_item(NAME(m_data_to_fdc));
	save_item(NAME(m_data_from_fdc));
	save_item(NAME(m_data));
	save_item(NAME(m_data_or_cmd));
	save_item(NAME(m_buffer_empty));
	save_item(NAME(m_fdc_cmd));
	save_item(NAME(m_clock_count));
	save_item(NAME(m_cp));
	save_item(NAME(m_p1));
	save_item(NAME(m_p1_data));
	save_item(NAME(m_p2));
	save_item(NAME(m_p3));
	save_item(NAME(m_last_write_addr));
	save_item(NAME(m_cass_data));
}

MC6845_UPDATE_ROW( applix_state::crtc_update_row )
{
	if (!de)
		return;
	// The display is bitmapped. 2 modes are supported here, 320x200x16 and 640x200x4.
	// There is a monochrome mode, but no info found as yet.
	// The 6845 cursor signal is not used at all.
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u32 const vidbase = (m_video_latch & 15) << 14 | (ra & 7) << 12;
	u32 *p = &bitmap.pix(y + vbp, hbp);

	for (u16 x = 0; x < x_count; x++)
	{
		u32 const mem = vidbase | ((ma + x) & 0xfff);
		u16 chr = m_base[mem];

		if (BIT(m_pa, 3))
		{
			// 640 x 200 x 4of16 mode
			for (int i = 0; i < 8; i++)
			{
				*p++ = palette[m_palette_latch[chr>>14]];
				chr <<= 2;
			}
		}
		else
		{
			// 320 x 200 x 16 mode
			for (int i = 0; i < 4; i++)
			{
				*p++ = palette[chr>>12];
				*p++ = palette[chr>>12];
				chr <<= 4;
			}
		}
	}
}

MC6845_BEGIN_UPDATE( applix_state::crtc_update_border )
{
	bitmap.fill(m_palette->pen(m_video_latch >> 4), cliprect);
}

void applix_state::vsync_w(int state)
{
	m_via->write_ca2(state);
}

TIMER_DEVICE_CALLBACK_MEMBER(applix_state::cass_timer)
{
	/* cassette - turn 2500/5000Hz to a bit */
	m_cass_data[1]++;
	u8 cass_ws = (m_cass->input() > +0.03) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		m_cass_data[2] = ((m_cass_data[1] < 12) ? 2 : 0);
		m_cass_data[1] = 0;
		// low-to-high transition when reading cassette - CLK on IC32b
		if ((cass_ws) && !BIT(m_pa, 4))
			m_maincpu->set_input_line(M68K_IRQ_4, ASSERT_LINE);
	}
}

void applix_state::applix(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, 30_MHz_XTAL / 4); // MC68000-P10 @ 7.5 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &applix_state::main_mem);

	z80_device &subcpu(Z80(config, "subcpu", 16_MHz_XTAL / 2)); // Z80H
	subcpu.set_addrmap(AS_PROGRAM, &applix_state::sub_mem);
	subcpu.set_addrmap(AS_IO, &applix_state::sub_io);

	i8051_device &kbdcpu(I8051(config, "kbdcpu", 11060250));
	kbdcpu.set_addrmap(AS_PROGRAM, &applix_state::keytronic_pc3270_program);
	kbdcpu.set_addrmap(AS_IO, &applix_state::keytronic_pc3270_io);
	kbdcpu.port_in_cb<1>().set(FUNC(applix_state::p1_read));
	kbdcpu.port_out_cb<1>().set(FUNC(applix_state::p1_write));
	kbdcpu.port_in_cb<2>().set(FUNC(applix_state::p2_read));
	kbdcpu.port_out_cb<2>().set(FUNC(applix_state::p2_write));
	kbdcpu.port_in_cb<3>().set(FUNC(applix_state::p3_read));
	kbdcpu.port_out_cb<3>().set(FUNC(applix_state::p3_write));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 200);
	screen.set_visarea_full();
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	PALETTE(config, m_palette, FUNC(applix_state::applix_palette), 16);

	/* sound hardware */
	SPEAKER(config, "speaker", 2).front();
	DAC0800(config, "ldac", 0).add_route(ALL_OUTPUTS, "speaker", 1.0, 0); // 74ls374.u20 + dac0800.u21 + 4052.u23
	DAC0800(config, "rdac", 0).add_route(ALL_OUTPUTS, "speaker", 1.0, 1); // 74ls374.u20 + dac0800.u21 + 4052.u23

	/* Devices */
	MC6845(config, m_crtc, 30_MHz_XTAL / 16); // MC6545 @ 1.875 MHz
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(true);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(applix_state::crtc_update_row));
	m_crtc->set_begin_update_callback(FUNC(applix_state::crtc_update_border));
	m_crtc->out_vsync_callback().set(FUNC(applix_state::vsync_w));

	MOS6522(config, m_via, 30_MHz_XTAL / 4 / 10); // VIA uses 68000 E clock
	m_via->readpb_handler().set(FUNC(applix_state::applix_pb_r));
	// in CB1 kbd clk
	// in CA2 vsync
	// in CB2 kdb data
	m_via->writepa_handler().set(FUNC(applix_state::applix_pa_w));
	m_via->writepb_handler().set(FUNC(applix_state::applix_pb_w));
	m_via->irq_handler().set_inputline("maincpu", M68K_IRQ_2);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set(m_via, FUNC(via6522_device::write_ca1));
	m_centronics->busy_handler().set(m_via, FUNC(via6522_device::write_pa0));

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "speaker", 0.10, 0);

	WD1772(config, m_fdc, 16_MHz_XTAL / 2); //connected to Z80H clock pin
	FLOPPY_CONNECTOR(config, m_floppy[0], applix_floppies, "35dd", applix_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], applix_floppies, "35dd", applix_state::floppy_formats).enable_sound(true);
	TIMER(config, "applix_c").configure_periodic(FUNC(applix_state::cass_timer), attotime::from_hz(100000));

	scc8530_device &scc(SCC8530(config, "scc", 30_MHz_XTAL / 8));
	scc.out_txda_callback().set("serial_a", FUNC(rs232_port_device::write_txd));
	scc.out_rtsa_callback().set("serial_a", FUNC(rs232_port_device::write_rts));
	scc.out_dtra_callback().set("serial_a", FUNC(rs232_port_device::write_dtr));
	scc.out_txdb_callback().set("serial_b", FUNC(rs232_port_device::write_txd));
	scc.out_rtsb_callback().set("serial_b", FUNC(rs232_port_device::write_rts));
	scc.out_dtrb_callback().set("serial_b", FUNC(rs232_port_device::write_dtr));
	scc.out_int_callback().set_inputline("maincpu", M68K_IRQ_3);

	rs232_port_device &serial_a(RS232_PORT(config, "serial_a", default_rs232_devices, nullptr));
	serial_a.rxd_handler().set("scc", FUNC(scc8530_device::rxa_w));
	serial_a.cts_handler().set("scc", FUNC(scc8530_device::ctsa_w));
	serial_a.cts_handler().set("scc", FUNC(scc8530_device::dcda_w));

	rs232_port_device &serial_b(RS232_PORT(config, "serial_b", default_rs232_devices, nullptr));
	serial_b.rxd_handler().set("scc", FUNC(scc8530_device::rxb_w));
	serial_b.cts_handler().set("scc", FUNC(scc8530_device::ctsb_w));
	serial_b.cts_handler().set("scc", FUNC(scc8530_device::dcdb_w));

	SOFTWARE_LIST(config, "flop_list").set_original("applix_flop");
}

/* ROM definition */
ROM_START( applix )
	ROM_REGION16_BE(0x20000, "maincpu", 0)
	ROM_SYSTEM_BIOS(0, "v4.5a", "V4.5a")
	ROMX_LOAD( "1616osl.45a", 0x00000, 0x10000, CRC(9dfb3224) SHA1(5223833a357f90b147f25826c01713269fc1945f), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "1616osh.45a", 0x00001, 0x10000, CRC(951bd441) SHA1(e0a38c8d0d38d84955c1de3f6a7d56ce06b063f6), ROM_SKIP(1) | ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "v4.4a", "V4.4a")
	ROMX_LOAD( "1616osl.44a", 0x00000, 0x10000, CRC(4a1a90d3) SHA1(4df504bbf6fc5dad76c29e9657bfa556500420a6), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "1616osh.44a", 0x00001, 0x10000, CRC(ef619994) SHA1(ff16fe9e2c99a1ffc855baf89278a97a2a2e881a), ROM_SKIP(1) | ROM_BIOS(1) )
	ROM_SYSTEM_BIOS(2, "v4.3a", "V4.3a")
	ROMX_LOAD( "1616osl.43a", 0x00000, 0x10000, CRC(c09b9ff8) SHA1(c46f2a98470d2d09cf9f9eec0f4096ab762407b5), ROM_SKIP(1) | ROM_BIOS(2) )
	ROMX_LOAD( "1616osh.43a", 0x00001, 0x10000, CRC(071a2505) SHA1(42c4cc6e3e78b6a5320f9d9c858fc9f4e6220857), ROM_SKIP(1) | ROM_BIOS(2) )
	ROM_SYSTEM_BIOS(3, "v4.0c", "V4.0c")
	ROMX_LOAD( "1616osl.40c", 0x00000, 0x10000, CRC(6a517b5d) SHA1(e0f4eba0cb8d273ba681b9d2c6d4b1beff9ef325), ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "1616osh.40c", 0x00001, 0x10000, CRC(7851651f) SHA1(d7d329aa7fe9f4418de0cdf813b61e70243e0e77), ROM_SKIP(1) | ROM_BIOS(3) )
	ROM_SYSTEM_BIOS(4, "v3.0b", "V3.0b")
	ROMX_LOAD( "1616osl.30b", 0x00000, 0x10000, CRC(fb9198c3) SHA1(e0e7a1dd176c1cbed063df1c405821c261d48f3a), ROM_SKIP(1) | ROM_BIOS(4) )
	ROMX_LOAD( "1616osh.30b", 0x00001, 0x10000, CRC(a279e1d7) SHA1(3451b2cae87a9ccee5f579fd1d49cf52d9f97b83), ROM_SKIP(1) | ROM_BIOS(4) )
	ROM_SYSTEM_BIOS(5, "v2.4a", "V2.4a")
	ROMX_LOAD( "1616osl.24a", 0x00000, 0x08000, CRC(b155830b) SHA1(b32db6a06c8a3c544210ba9faba7c49497c504fb), ROM_SKIP(1) | ROM_BIOS(5) )
	ROMX_LOAD( "1616osh.24a", 0x00001, 0x08000, CRC(6d9fc0e0) SHA1(07111f46386494ed3f426c1e50308f0209587f06), ROM_SKIP(1) | ROM_BIOS(5) )

	ROM_REGION(0x18000, "subcpu", 0)
	ROM_LOAD( "1616ssdv.022", 0x0000, 0x8000, CRC(6d8e413a) SHA1(fc27d92c34f231345a387b06670f36f8c1705856) )

	ROM_REGION(0x20000, "user1", 0)
	ROM_LOAD( "ssdcromv.22",  0x0000, 0x8000, CRC(c85c47fb) SHA1(6f0bb3753fc0d74ee5901d71d05a74ec6a4a1d05) )
	ROM_LOAD( "ssddromv.14a", 0x8000, 0x8000, CRC(8fe2db78) SHA1(487484003aba4d8960101ced6a689dc81676235d) )

	ROM_REGION(0x2000, "kbdcpu", 0)
	ROM_LOAD( "14166.bin", 0x0000, 0x2000, CRC(1aea1b53) SHA1(b75b6d4509036406052157bc34159f7039cdc72e) )
ROM_END


void applix_state::init_applix()
{
	u8 *RAM = memregion("subcpu")->base();
	membank("bank1")->configure_entries(0, 2, &RAM[0x8000], 0x8000);
}


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT         COMPANY           FULLNAME       FLAGS
COMP( 1986, applix, 0,      0,      applix,  applix, applix_state, init_applix, "Applix Pty Ltd", "Applix 1616", MACHINE_SUPPORTS_SAVE )



/**************************************************** KEYBOARD MODULE *****************************************/

u8 applix_state::internal_data_read(offs_t offset)
{
	m_via->write_cb2( BIT(offset, 8) ); // data
	bool cp = !BIT(offset, 9);  // clock pulses //TODO tidy this up with real flipflops
	if (cp != m_cp)
	{
		m_cp = cp;
		if (cp)
			m_clock_count++;
	}
	if (m_clock_count > 1)
		m_via->write_cb1( cp );

	return 0xff;
}


void applix_state::internal_data_write(offs_t offset, u8 data)
{
	/* Check for low->high transition on AD8 */
	if ( ! ( m_last_write_addr & 0x0100 ) && ( offset & 0x0100 ) )
	{
		switch (m_p1)
		{
		case 0x0e:
			break;
		case 0x0f:
			m_p1_data = m_io_k0f->read();
			break;
		case 0x30: case 0x31: case 0x32: case 0x33:
		case 0x34: case 0x35: case 0x36:
		case 0x38: case 0x39: case 0x3a: case 0x3b:
			m_p1_data = m_io_k3x0[m_p1 - 0x30]->read();
			break;
		case 0x37:
			m_p1_data = m_io_k3x0[7]->read() | (m_io_k3x0[6]->read() & 0x01);
			break;
		}
	}

	/* Check for low->high transition on AD9 */
	if ( ! ( m_last_write_addr & 0x0200 ) && ( offset & 0x0200 ) )
	{
		switch (m_p1)
		{
		case 0x0b:
			m_p1_data = m_io_k0b->read();
			break;
		case 0x30: case 0x31: case 0x32: case 0x33:
		case 0x34: case 0x35: case 0x36: case 0x37:
			m_p1_data = m_io_k3x1[m_p1 - 0x30]->read();
			break;
		case 0x38:
			m_p1_data = 0xff;
			break;
		case 0x39:
			m_p1_data = 0xff;
			break;
		case 0x3a:
			m_p1_data = 0xff;
			break;
		}
	}

	m_last_write_addr = offset;
}


u8 applix_state::p1_read()
{
	return m_p1 & m_p1_data;
}


void applix_state::p1_write(u8 data)
{
	m_p1 = data;
}


u8 applix_state::p2_read()
{
	return m_p2;
}


void applix_state::p2_write(u8 data)
{
	m_p2 = data;
}


u8 applix_state::p3_read()
{
	u8 data = m_p3;

	data &= ~0x14;

	/* -INT0 signal */
	data |= 4;

	/* T0 signal */
	data |= 0;

	return data;
}


void applix_state::p3_write(u8 data)
{
	m_p3 = data;
}



apricot.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Dirk Best
/***************************************************************************

    ACT Apricot PC/Xi

    TODO:
    - External RS232 data transfers to the Apricot are usually garbage (but
      sending to an external target works fine)
    - Dump of the keyboard MCU ROM needed (can be dumped using test mode)

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

#include "emu.h"
#include "bus/apricot/expansion/expansion.h"
#include "bus/apricot/keyboard/keyboard.h"
#include "bus/centronics/ctronics.h"
#include "bus/rs232/rs232.h"
#include "cpu/i86/i86.h"
#include "cpu/i8089/i8089.h"
#include "formats/apridisk.h"
#include "imagedev/floppy.h"
#include "machine/clock.h"
#include "machine/ram.h"
#include "machine/74153.h"
#include "machine/i8255.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/wd_fdc.h"
#include "machine/z80sio.h"
#include "sound/sn76496.h"
#include "video/mc6845.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {


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

class apricot_state : public driver_device
{
public:
	apricot_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_cpu(*this, "ic91"),
		m_iop(*this, "ic71"),
		m_ram(*this, RAM_TAG),
		m_crtc(*this, "ic30"),
		m_ppi(*this, "ic17"),
		m_pic(*this, "ic31"),
		m_pit(*this, "ic16"),
		m_sio(*this, "ic15"),
		m_rs232(*this, "rs232"),
		m_centronics(*this, "centronics"),
		m_fdc(*this, "ic68"),
		m_floppy(*this, "ic68:%u", 0U),
		m_palette(*this, "palette"),
		m_exp(*this, "exp"),
		m_screen_buffer(*this, "screen_buffer"),
		m_video_mode(0),
		m_display_on(1),
		m_display_enabled(0),
		m_centronics_fault(1),
		m_centronics_perror(1),
		m_bus_locked(0)
	{ }

	void apricot(machine_config &config);
	void apricotxi(machine_config &config);

private:
	static void floppy_formats(format_registration &fr);

	void i8086_lock_w(int state);
	void i8089_ca1_w(uint8_t data);
	void i8089_ca2_w(uint8_t data);
	void i8255_portb_w(uint8_t data);
	uint8_t i8255_portc_r();
	void i8255_portc_w(uint8_t data);
	void fdc_intrq_w(int state);
	uint8_t sio_da_r();
	uint8_t sio_ca_r();
	uint8_t sio_db_r();
	uint8_t sio_cb_r();

	void write_centronics_fault(int state);
	void write_centronics_perror(int state);

	void apricot_hd6845_de(int state) { m_display_enabled = state; };

	MC6845_UPDATE_ROW(crtc_update_row);
	uint32_t screen_update_apricot(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void apricot_io(address_map &map) ATTR_COLD;
	void apricot_mem(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;

	required_device<i8086_cpu_device> m_cpu;
	required_device<i8089_device> m_iop;
	required_device<ram_device> m_ram;
	required_device<hd6845s_device> m_crtc;
	required_device<i8255_device> m_ppi;
	required_device<pic8259_device> m_pic;
	required_device<pit8253_device> m_pit;
	required_device<z80sio_device> m_sio;
	required_device<rs232_port_device> m_rs232;
	required_device<centronics_device> m_centronics;
	required_device<wd2797_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<palette_device> m_palette;
	required_device<apricot_expansion_bus_device> m_exp;
	required_shared_ptr<uint16_t> m_screen_buffer;

	bool m_video_mode;
	bool m_display_on;

	int m_display_enabled;

	int m_centronics_fault;
	int m_centronics_perror;

	int m_bus_locked;
};


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

void apricot_state::i8089_ca1_w(uint8_t data)
{
	m_iop->sel_w(0);
	m_iop->ca_w(1);
	m_iop->ca_w(0);
}

void apricot_state::i8089_ca2_w(uint8_t data)
{
	m_iop->sel_w(1);
	m_iop->ca_w(1);
	m_iop->ca_w(0);
}

void apricot_state::write_centronics_fault(int state)
{
	m_centronics_fault = state;
	m_sio->syncb_w(state);
	m_ppi->pc2_w(state);
}

void apricot_state::write_centronics_perror(int state)
{
	m_centronics_perror = state;
}

uint8_t apricot_state::i8255_portc_r()
{
	uint8_t data = 0;

	data |= m_centronics_perror << 0;
	// schematic page 294 says pc1 is centronics pin 34, which is n/c.
	data |= m_centronics_fault << 2;
	data |= m_display_enabled << 3;

	return data;
}

void apricot_state::i8255_portb_w(uint8_t data)
{
	// 7-------  centronics transceiver direction (0 = output)
	// -6------  disk select
	// --5-----  enable disk select
	// ---4----  video mode
	// ----3---  display enabled
	// -----2--  head load
	// ------1-  not used
	// -------0  crtc reset

	m_display_on = BIT(data, 3);
	m_video_mode = BIT(data, 4);

	floppy_image_device *floppy = nullptr;

	if (!BIT(data, 5))
		floppy = m_floppy[BIT(data, 6)]->get_device();

	m_fdc->set_floppy(floppy);

	// motor on is wired to be active once a disk has been inserted
	// we just let the motor run all the time for now
	if (floppy)
		floppy->mon_w(0);

	// switch video modes
	m_crtc->set_unscaled_clock(15_MHz_XTAL / (m_video_mode ? 10 : 16));
	m_crtc->set_hpixels_per_column(m_video_mode ? 10 : 16);
}

void apricot_state::i8255_portc_w(uint8_t data)
{
//  schematic page 294 says pc4 outputs to centronics pin 13, which is the "select" output from the printer.
	m_centronics->write_strobe(BIT(data, 5));
//  schematic page 294 says pc6 outputs to centronics pin 15, which is unused
}

uint8_t apricot_state::sio_da_r()
{
	if (m_bus_locked)
		return m_sio->m1_r();

	return m_sio->da_r();
}

uint8_t apricot_state::sio_ca_r()
{
	if (m_bus_locked)
		return m_sio->m1_r();

	return m_sio->ca_r();
}

uint8_t apricot_state::sio_cb_r()
{
	if (m_bus_locked)
		return m_sio->m1_r();

	return m_sio->cb_r();
}

uint8_t apricot_state::sio_db_r()
{
	if (m_bus_locked)
		return m_sio->m1_r();

	return m_sio->db_r();
}


//**************************************************************************
//  FLOPPY
//**************************************************************************

void apricot_state::fdc_intrq_w(int state)
{
	m_pic->ir4_w(state);
	m_iop->ext1_w(state);
}

void apricot_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_APRIDISK_FORMAT);
}

static void apricot_floppies(device_slot_interface &device)
{
	device.option_add("d31v", SONY_OA_D31V);
	device.option_add("d32w", SONY_OA_D32W);
}


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

uint32_t apricot_state::screen_update_apricot(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if (!m_display_on)
		m_crtc->screen_update(screen, bitmap, cliprect);
	else
		bitmap.fill(rgb_t::black(), cliprect);

	return 0;
}

MC6845_UPDATE_ROW( apricot_state::crtc_update_row )
{
	const pen_t *pen = m_palette->pens();

	for (int i = 0; i < x_count; i++)
	{
		uint16_t code = m_screen_buffer[(ma + i) & 0x7ff];
		uint16_t offset = ((code & 0x7ff) << 5) | (ra << 1);
		uint16_t data = m_cpu->space(AS_PROGRAM).read_word(offset);

		if (m_video_mode)
		{
			int fill = 0;

			if (i == cursor_x) fill = 1; // cursor?
			if (BIT(code, 12) && BIT(data, 14)) fill = 1; // strike-through?
			if (BIT(code, 13) && BIT(data, 15)) fill = 1; // underline?

			// draw 10 pixels of the character
			for (int x = 0; x <= 10; x++)
			{
				int color = fill ? 1 : BIT(data, x);
				color ^= BIT(code, 15); // reverse?
				bitmap.pix(y, x + i*10) = pen[color ? 1 + BIT(code, 14) : 0];
			}
		}
		else
		{
			// draw 16 pixels of the cell
			for (int x = 0; x <= 16; x++)
				bitmap.pix(y, x + i*16) = pen[BIT(data, x)];
		}
	}
}

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

void apricot_state::machine_start()
{
	membank("ram")->set_base(m_ram->pointer());
}

void apricot_state::i8086_lock_w(int state)
{
	m_bus_locked = state;
}


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

void apricot_state::apricot_mem(address_map &map)
{
	map(0x00000, 0x3ffff).bankrw("ram");
	map(0xf0000, 0xf0fff).mirror(0x7000).ram().share("screen_buffer");
	map(0xf8000, 0xfbfff).mirror(0x4000).rom().region("bootstrap", 0);
}

void apricot_state::apricot_io(address_map &map)
{
	map(0x00, 0x03).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x40, 0x47).rw(m_fdc, FUNC(wd2797_device::read), FUNC(wd2797_device::write)).umask16(0x00ff);
	map(0x48, 0x4f).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
	map(0x50, 0x50).mirror(0x06).w("ic7", FUNC(sn76489_device::write));
	map(0x58, 0x5f).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0x60, 0x60).r(FUNC(apricot_state::sio_da_r)).w(m_sio, FUNC(z80sio_device::da_w)).umask16(0x00ff);
	map(0x62, 0x62).r(FUNC(apricot_state::sio_ca_r)).w(m_sio, FUNC(z80sio_device::ca_w)).umask16(0x00ff);
	map(0x64, 0x64).r(FUNC(apricot_state::sio_db_r)).w(m_sio, FUNC(z80sio_device::db_w)).umask16(0x00ff);
	map(0x66, 0x66).r(FUNC(apricot_state::sio_cb_r)).w(m_sio, FUNC(z80sio_device::cb_w)).umask16(0x00ff);
	map(0x68, 0x68).mirror(0x04).w(m_crtc, FUNC(hd6845s_device::address_w));
	map(0x6a, 0x6a).mirror(0x04).rw(m_crtc, FUNC(hd6845s_device::register_r), FUNC(hd6845s_device::register_w));
	map(0x70, 0x70).mirror(0x04).w(FUNC(apricot_state::i8089_ca1_w));
	map(0x72, 0x72).mirror(0x04).w(FUNC(apricot_state::i8089_ca2_w));
	map(0x78, 0x7f).noprw(); // unavailable
}


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

void apricot_state::apricot(machine_config &config)
{
	// main cpu
	I8086(config, m_cpu, 15_MHz_XTAL / 3);
	m_cpu->set_addrmap(AS_PROGRAM, &apricot_state::apricot_mem);
	m_cpu->set_addrmap(AS_IO, &apricot_state::apricot_io);
	m_cpu->set_irq_acknowledge_callback("ic31", FUNC(pic8259_device::inta_cb));
	m_cpu->lock_handler().set(FUNC(apricot_state::i8086_lock_w));

	// i/o cpu
	I8089(config, m_iop, 15_MHz_XTAL / 3);
	m_iop->set_addrmap(AS_PROGRAM, &apricot_state::apricot_mem);
	m_iop->set_addrmap(AS_IO, &apricot_state::apricot_io);
	m_iop->set_data_width(16);
	m_iop->sintr1().set(m_pic, FUNC(pic8259_device::ir0_w));
	m_iop->sintr2().set(m_pic, FUNC(pic8259_device::ir1_w));

	// ram
	RAM(config, RAM_TAG).set_default_size("256K");

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_raw(15_MHz_XTAL, 950, 0, 800, 426, 0, 400); // should be interlace
	screen.set_screen_update(FUNC(apricot_state::screen_update_apricot));

	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	HD6845S(config, m_crtc, 15_MHz_XTAL / 10);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(10);
	m_crtc->set_update_row_callback(FUNC(apricot_state::crtc_update_row));
	m_crtc->out_de_callback().set(FUNC(apricot_state::apricot_hd6845_de));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SN76489(config, "ic7", 4_MHz_XTAL / 2).add_route(ALL_OUTPUTS, "mono", 1.0);

	// devices
	I8255A(config, m_ppi, 0);
	m_ppi->in_pa_callback().set("cent_data_in", FUNC(input_buffer_device::read));
	m_ppi->out_pa_callback().set("cent_data_out", FUNC(output_latch_device::write));
	m_ppi->out_pb_callback().set(FUNC(apricot_state::i8255_portb_w));
	m_ppi->in_pc_callback().set(FUNC(apricot_state::i8255_portc_r));
	m_ppi->out_pc_callback().set(FUNC(apricot_state::i8255_portc_w));

	PIC8259(config, m_pic, 0);
	m_pic->out_int_callback().set_inputline(m_cpu, 0);

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(4_MHz_XTAL / 16);
	m_pit->out_handler<0>().set(m_pic, FUNC(pic8259_device::ir6_w));
	m_pit->set_clk<1>(4_MHz_XTAL / 2);
	m_pit->out_handler<1>().set("ic14", FUNC(ttl153_device::i0a_w));
	m_pit->set_clk<2>(4_MHz_XTAL / 2);
	m_pit->out_handler<2>().set("ic14", FUNC(ttl153_device::i0b_w));
	m_pit->out_handler<2>().append("ic14", FUNC(ttl153_device::i2a_w));
	m_pit->out_handler<2>().append("ic14", FUNC(ttl153_device::i2b_w));

	ttl153_device &ttl74153(TTL153(config, "ic14"));
	ttl74153.za_cb().set("ic15", FUNC(z80sio_device::rxca_w));
	ttl74153.zb_cb().set("ic15", FUNC(z80sio_device::txca_w));

	CLOCK(config, "ic15_rxtxcb", 4_MHz_XTAL / 16).signal_handler().set(m_sio, FUNC(z80sio_device::rxtxcb_w));

	Z80SIO(config, m_sio, 15_MHz_XTAL / 6);
	m_sio->set_cputag(m_cpu);
	m_sio->out_txda_callback().set(m_rs232, FUNC(rs232_port_device::write_txd));
	m_sio->out_dtra_callback().set(m_rs232, FUNC(rs232_port_device::write_dtr));
	m_sio->out_rtsa_callback().set(m_rs232, FUNC(rs232_port_device::write_rts));
	m_sio->out_wrdya_callback().set(m_iop, FUNC(i8089_device::drq2_w));
	m_sio->out_txdb_callback().set("kbd", FUNC(apricot_keyboard_bus_device::out_w));
	m_sio->out_dtrb_callback().set("ic14", FUNC(ttl153_device::s0_w));
	m_sio->out_rtsb_callback().set("ic14", FUNC(ttl153_device::s1_w));
	m_sio->out_int_callback().set(m_pic, FUNC(pic8259_device::ir5_w));

	// rs232 port
	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	// note: missing a receive clock callback to support external clock mode (i1 to 153)
	m_rs232->rxd_handler().set(m_sio, FUNC(z80sio_device::rxa_w));
	m_rs232->dcd_handler().set(m_sio, FUNC(z80sio_device::dcda_w));
	m_rs232->dsr_handler().set(m_sio, FUNC(z80sio_device::synca_w));
	m_rs232->cts_handler().set(m_sio, FUNC(z80sio_device::ctsa_w)).invert();

	// keyboard
	APRICOT_KEYBOARD_INTERFACE(config, "kbd", apricot_keyboard_devices, "hle").in_handler().set(m_sio, FUNC(z80sio_device::rxb_w));

	// centronics printer
	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->set_data_input_buffer("cent_data_in");
	m_centronics->ack_handler().set(m_sio, FUNC(z80sio_device::ctsb_w));
	m_centronics->busy_handler().set(m_sio, FUNC(z80sio_device::dcdb_w));
	m_centronics->fault_handler().set(FUNC(apricot_state::write_centronics_fault));
	m_centronics->perror_handler().set(FUNC(apricot_state::write_centronics_perror));
	//m_centronics->select_handler().set(); // schematic page 294 says this is connected to pc4, but that is an output to the printer

	INPUT_BUFFER(config, "cent_data_in");

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	// floppy
	WD2797(config, m_fdc, 4_MHz_XTAL / 2);
	m_fdc->intrq_wr_callback().set(FUNC(apricot_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(m_iop, FUNC(i8089_device::drq1_w));
	FLOPPY_CONNECTOR(config, m_floppy[0], apricot_floppies, "d32w", apricot_state::floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[1], apricot_floppies, "d32w", apricot_state::floppy_formats);

	SOFTWARE_LIST(config, "flop_list").set_original("apricot_flop");

	// expansion bus
	APRICOT_EXPANSION_BUS(config, m_exp, 0);
	m_exp->set_program_space(m_cpu, AS_PROGRAM);
	m_exp->set_io_space(m_cpu, AS_IO);
	m_exp->set_program_iop_space(m_iop, AS_PROGRAM);
	m_exp->set_io_iop_space(m_iop, AS_IO);
	m_exp->int2().set(m_pic, FUNC(pic8259_device::ir2_w));
	m_exp->int3().set(m_pic, FUNC(pic8259_device::ir3_w));
	APRICOT_EXPANSION_SLOT(config, "exp:1", apricot_expansion_cards, nullptr);
	APRICOT_EXPANSION_SLOT(config, "exp:2", apricot_expansion_cards, nullptr);
}

void apricot_state::apricotxi(machine_config &config)
{
	apricot(config);
}


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

ROM_START( apricot )
	ROM_REGION16_LE(0x4000, "bootstrap", 0)
	ROM_LOAD16_BYTE("pc_bios_lo_001.bin", 0x0000, 0x2000, CRC(0c217cc2) SHA1(0d7a2b61e17966462b555115f962a175fadcf72a))
	ROM_LOAD16_BYTE("pc_bios_hi_001.bin", 0x0001, 0x2000, CRC(7c27f36c) SHA1(c801bbf904815f76ec6463e948f57e0118a26292))
ROM_END

ROM_START( apricotxi )
	ROM_REGION16_LE(0x4000, "bootstrap", 0)
	ROM_LOAD16_BYTE("lo_ve007.u11", 0x0000, 0x2000, CRC(e74e14d1) SHA1(569133b0266ce3563b21ae36fa5727308797deee)) // LO Ve007 03.04.84
	ROM_LOAD16_BYTE("hi_ve007.u9",  0x0001, 0x2000, CRC(b04fb83e) SHA1(cc2b2392f1b4c04bb6ec8ee26f8122242c02e572)) // HI Ve007 03.04.84
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME       PARENT   COMPAT  MACHINE    INPUT  CLASS          INIT        COMPANY  FULLNAME      FLAGS
COMP( 1983, apricot,   0,       0,      apricot,   0,     apricot_state, empty_init, "ACT",   "Apricot PC", 0 )
COMP( 1984, apricotxi, apricot, 0,      apricotxi, 0,     apricot_state, empty_init, "ACT",   "Apricot Xi", 0 )



apricotf.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Angelo Salese, Curt Coder, AJR
/**************************************************************************************************

ACT Apricot F1 series

TODO:
- Need software dumps to go further;
- Convert workram pointer to ram_device (needs documented range of available configs);

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

#include "emu.h"
#include "bus/centronics/ctronics.h"
#include "bus/rs232/rs232.h"
#include "cpu/i86/i86.h"
#include "formats/apridisk.h"
#include "imagedev/floppy.h"
#include "apricotkb.h"
#include "machine/74259.h"
#include "machine/buffer.h"
#include "machine/input_merger.h"
#include "machine/wd_fdc.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "sound/spkrdev.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


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

// ======================> f1_daisy_device

class f1_daisy_device : public device_t, public z80_daisy_chain_interface
{
public:
	f1_daisy_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0);

	IRQ_CALLBACK_MEMBER(inta_cb);

protected:
	virtual void device_start() override ATTR_COLD;
};

DEFINE_DEVICE_TYPE(F1_DAISY, f1_daisy_device, "f1_daisy", "F1 daisy chain abstraction")

f1_daisy_device::f1_daisy_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: device_t(mconfig, F1_DAISY, tag, owner, clock)
	, z80_daisy_chain_interface(mconfig, *this)
{
}

void f1_daisy_device::device_start()
{
}

IRQ_CALLBACK_MEMBER(f1_daisy_device::inta_cb)
{
	device_z80daisy_interface *intf = daisy_get_irq_device();
	if (intf != nullptr)
		return intf->z80daisy_irq_ack();
	else
		return 0xff;
}

// ======================> f1_state

class f1_state : public driver_device
{
public:
	f1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ctc(*this, "ctc")
		, m_sio(*this, "sio")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0U)
		, m_centronics(*this, "centronics")
		, m_cent_data_out(*this, "cent_data_out")
		, m_irqs(*this, "irqs")
		, m_workram(*this, "workram")
		, m_paletteram(*this, "paletteram")
		, m_palette(*this, "palette")
	{ }

	void act_f1(machine_config &config);

private:
	static void floppy_formats(format_registration &fr);

	virtual void machine_start() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<z80ctc_device> m_ctc;
	required_device<z80sio_device> m_sio;
	required_device<wd2797_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<centronics_device> m_centronics;
	required_device<output_latch_device> m_cent_data_out;
	required_device<input_merger_device> m_irqs;
	required_shared_ptr<u16> m_workram;
	required_shared_ptr<u16> m_paletteram;
	required_device<palette_device> m_palette;

	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	u16 palette_r(offs_t offset);
	void palette_w(offs_t offset, u16 data, u16 mem_mask = ~0);

	void drive_select_w(int state);
	void hld_w(int state);
	void motor_on_w(int state);
	void video_lines_w(int state);
	void video_columns_w(int state);
	void led0_enable_w(int state);
	void led1_enable_w(int state);

	void m1_w(u8 data);

	int m_width80 = 0;
	int m_lines200 = 0;

	void main_io(address_map &map) ATTR_COLD;
	void main_map(address_map &map) ATTR_COLD;
};


//**************************************************************************
//  VIDEO
//**************************************************************************

u32 f1_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	int lines = m_lines200 ? 200 : 256;
	// start bases are per line, in a dedicated buffer
	// TODO: is this configurable?
	const u32 vram_table = 0x1e00 >> 1;
	const u32 vram_mask = 0x3ffff >> 1;

	for (int y = 0; y < lines; y++)
	{
		offs_t base_offs = m_workram[(y + vram_table) & vram_mask];

		for (int sx = 0; sx < 80; sx++)
		{
			u16 data = m_workram[(base_offs + sx) & vram_mask];

			if (m_width80)
			{
				for (int x = 0; x < 8; x++)
				{
					int color = (BIT(data, 15) << 1) | BIT(data, 7);

					bitmap.pix(y, (sx * 8) + x) = color;

					data <<= 1;
				}
			}
			else
			{
				for (int x = 0; x < 4; x++)
				{
					int color = (BIT(data, 15) << 3) | (BIT(data, 14) << 2) | (BIT(data, 7) << 1) | BIT(data, 6);

					bitmap.pix(y, (sx * 8) + (x * 2)) = color;
					bitmap.pix(y, (sx * 8) + (x * 2) + 1) = color;

					data <<= 2;
				}
			}
		}
	}

	return 0;
}

u16 f1_state::palette_r(offs_t offset)
{
	return m_paletteram[offset];
}

void f1_state::palette_w(offs_t offset, u16 data, u16 mem_mask)
{
	COMBINE_DATA(&m_paletteram[offset]);

	//TODO: we discard offset 0 because all BIOSes sets white there (0xf).
	// Either routed to border or genlock.
	if(ACCESSING_BITS_0_7 && offset)
	{
		u8 i = 0, r = 0, g = 0, b = 0;

		const u16 datax = m_paletteram[offset];
		i = BIT(datax, 0);
		r = (BIT(datax, 1) << 1) | i;
		g = (BIT(datax, 2) << 1) | i;
		b = (BIT(datax, 3) << 1) | i;

		m_palette->set_pen_color(offset, pal2bit(r), pal2bit(g), pal2bit(b));
	}
}


static const gfx_layout charset_8x8 =
{
	8,8,
	256,
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};


static GFXDECODE_START( gfx_act_f1 )
	GFXDECODE_ENTRY( "maincpu", 0x0800, charset_8x8, 0, 1 )
GFXDECODE_END

//**************************************************************************
//  I/Os
//**************************************************************************

void f1_state::drive_select_w(int state)
{
	m_fdc->set_floppy(m_floppy[!state]->get_device());
}

void f1_state::hld_w(int state)
{
	// TODO: drive head load
}

void f1_state::motor_on_w(int state)
{
	m_floppy[0]->get_device()->mon_w(!state);
	m_floppy[1]->get_device()->mon_w(!state);
}

void f1_state::video_lines_w(int state)
{
	// video lines (1=200, 0=256)
	m_lines200 = state;
}

void f1_state::video_columns_w(int state)
{
	// video columns (1=80, 0=40)
	m_width80 = state;
}

void f1_state::led0_enable_w(int state)
{
	// TODO: LED 0 enable
}

void f1_state::led1_enable_w(int state)
{
	// TODO: LED 1 enable
}


void f1_state::machine_start()
{
	save_item(NAME(m_width80));
	save_item(NAME(m_lines200));
}



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

//-------------------------------------------------
//  ADDRESS_MAP( main_map )
//-------------------------------------------------

void f1_state::main_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x3ffff).ram().share("workram");
	map(0xe0000, 0xe001f).rw(FUNC(f1_state::palette_r), FUNC(f1_state::palette_w)).share("paletteram");
	map(0xf8000, 0xfffff).rom().region("maincpu", 0);
}


//-------------------------------------------------
//  ADDRESS_MAP( main_io )
//-------------------------------------------------

void f1_state::main_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0000).w(m_cent_data_out, FUNC(output_latch_device::write));
	map(0x0000, 0x000f).w("syslatch", FUNC(ls259_device::write_d0)).umask16(0xff00);
	map(0x0010, 0x0017).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write)).umask16(0x00ff);
	map(0x0020, 0x0027).rw(m_sio, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w)).umask16(0x00ff);
	map(0x0030, 0x0030).w(FUNC(f1_state::m1_w));
	map(0x0040, 0x0047).rw(m_fdc, FUNC(wd2797_device::read), FUNC(wd2797_device::write)).umask16(0x00ff);
//  map(0x01e0, 0x01ff) winchester
}


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

//-------------------------------------------------
//  INPUT_PORTS( act )
//-------------------------------------------------

static INPUT_PORTS_START( act )
	// defined in act/apricotkb.cpp
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  Z80CTC
//-------------------------------------------------

void f1_state::m1_w(u8 data)
{
	m_ctc->z80daisy_decode(data);
	m_sio->z80daisy_decode(data);
}

//-------------------------------------------------
//  floppy
//-------------------------------------------------

void f1_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();

	fr.add(FLOPPY_APRIDISK_FORMAT);
}

void apricotf_floppies(device_slot_interface &device)
{
	device.option_add("d31v", SONY_OA_D31V);
	device.option_add("d32w", SONY_OA_D32W);
}

static const z80_daisy_config f1_daisy_config[] =
{
	{ "sio" },
	{ "ctc" },
	{ nullptr }
};


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

//-------------------------------------------------
//  machine_config( act_f1 )
//-------------------------------------------------

void f1_state::act_f1(machine_config &config)
{
	static constexpr auto CLK5 = 14_MHz_XTAL / 3; // nominally 5 MHz, actually 4.66 MHz
	static constexpr auto BAUDCLK = 14_MHz_XTAL / 7 / 13; // documented as 153.8 kHz

	/* basic machine hardware */
	I8086(config, m_maincpu, CLK5); // @ 10D
	m_maincpu->set_addrmap(AS_PROGRAM, &f1_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &f1_state::main_io);
	m_maincpu->set_irq_acknowledge_callback("daisy", FUNC(f1_daisy_device::inta_cb));

	F1_DAISY(config, "daisy").set_daisy_config(f1_daisy_config);

	INPUT_MERGER_ANY_HIGH(config, "irqs").output_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	ls259_device &syslatch(LS259(config, "syslatch")); // 74LS259 @ 16B
	syslatch.q_out_cb<0>().set(FUNC(f1_state::drive_select_w));
	syslatch.q_out_cb<1>().set(FUNC(f1_state::hld_w));
	syslatch.q_out_cb<2>().set(FUNC(f1_state::motor_on_w));
	syslatch.q_out_cb<3>().set(FUNC(f1_state::video_lines_w));
	syslatch.q_out_cb<4>().set(FUNC(f1_state::video_columns_w));
	syslatch.q_out_cb<5>().set(FUNC(f1_state::led0_enable_w));
	syslatch.q_out_cb<6>().set(FUNC(f1_state::led1_enable_w));
	syslatch.q_out_cb<7>().set(m_centronics, FUNC(centronics_device::write_strobe)).invert();

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(14_MHz_XTAL, 896, 0, 640, 312, 0, 256);
	//screen.set_raw(14_MHz_XTAL, 896, 0, 640, 260, 0, 200);
	screen.set_screen_update(FUNC(f1_state::screen_update));
	screen.set_palette(m_palette);
	screen.screen_vblank().set(m_ctc, FUNC(z80ctc_device::trg3)).invert();

	PALETTE(config, m_palette).set_entries(16);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_act_f1);

	/* Devices */
	APRICOT_KEYBOARD(config, APRICOT_KEYBOARD_TAG, 0);

	Z80SIO(config, m_sio, CLK5 / 2); // Z80-CTC @ 13D
	m_sio->out_txda_callback().set("speaker", FUNC(speaker_sound_device::level_w));
	m_sio->out_txdb_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	m_sio->out_rtsb_callback().set("rs232", FUNC(rs232_port_device::write_rts));
	m_sio->out_dtrb_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	m_sio->out_int_callback().set("irqs", FUNC(input_merger_device::in_w<0>));

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.5);

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_sio, FUNC(z80sio_device::rxb_w));
	rs232.dcd_handler().set(m_sio, FUNC(z80sio_device::dcdb_w));
	rs232.cts_handler().set(m_sio, FUNC(z80sio_device::ctsb_w));
	rs232.dsr_handler().set(m_sio, FUNC(z80sio_device::ctsa_w));

	Z80CTC(config, m_ctc, CLK5 / 2); // Z80-SIO/2 @ 15D
	m_ctc->intr_callback().set("irqs", FUNC(input_merger_device::in_w<1>));
	m_ctc->set_clk<1>(BAUDCLK);
	m_ctc->set_clk<2>(BAUDCLK);
	m_ctc->zc_callback<1>().set(m_sio, FUNC(z80sio_device::rxcb_w));
	m_ctc->zc_callback<1>().append(m_sio, FUNC(z80sio_device::txcb_w));
	m_ctc->zc_callback<1>().append("rs232", FUNC(rs232_port_device::write_etc));
	m_ctc->zc_callback<2>().set(m_sio, FUNC(z80sio_device::txca_w));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(m_sio, FUNC(z80sio_device::ctsa_w)).invert();

	OUTPUT_LATCH(config, m_cent_data_out); // 74LS373 @ 13C
	m_centronics->set_output_latch(*m_cent_data_out);

	// floppy
	WD2797(config, m_fdc, 14_MHz_XTAL / 7); // WD2797 @ 5F
	m_fdc->intrq_wr_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_fdc->drq_wr_callback().set_inputline(m_maincpu, INPUT_LINE_TEST);

	FLOPPY_CONNECTOR(config, m_floppy[0], apricotf_floppies, "d32w", f1_state::floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[1], apricotf_floppies, "d32w", f1_state::floppy_formats);

	// TODO: expansion port (INT lines gated onto CLK0 of CTC)
}



//**************************************************************************
//  ROM definitions
//**************************************************************************

//-------------------------------------------------
//  ROM( f1 )
//-------------------------------------------------

ROM_START( f1 )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD16_BYTE( "lo_f1_1.6.8f",  0x0000, 0x4000, CRC(be018be2) SHA1(80b97f5b2111daf112c69b3f58d1541a4ba69da0) )    // Labelled F1 - LO Vr. 1.6
	ROM_LOAD16_BYTE( "hi_f1_1.6.10f", 0x0001, 0x4000, CRC(bbba77e2) SHA1(e62bed409eb3198f4848f85fccd171cd0745c7c0) )    // Labelled F1 - HI Vr. 1.6
ROM_END

#define rom_f1e rom_f1
#define rom_f2 rom_f1


//-------------------------------------------------
//  ROM( f10 )
//-------------------------------------------------

ROM_START( f10 )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD16_BYTE( "lo_f10_3.1.1.8f",  0x0000, 0x4000, CRC(bfd46ada) SHA1(0a36ef379fa9af7af9744b40c167ce6e12093485) ) // Labelled LO-FRange Vr3.1.1
	ROM_LOAD16_BYTE( "hi_f10_3.1.1.10f", 0x0001, 0x4000, CRC(67ad5b3a) SHA1(a5ececb87476a30167cf2a4eb35c03aeb6766601) ) // Labelled HI-FRange Vr3.1.1
ROM_END


COMP( 1984, f1,   0,      0,      act_f1,  act,   f1_state, empty_init, "ACT",   "Apricot F1",  MACHINE_NOT_WORKING )
COMP( 1984, f1e,  f1,     0,      act_f1,  act,   f1_state, empty_init, "ACT",   "Apricot F1e", MACHINE_NOT_WORKING )
COMP( 1984, f2,   f1,     0,      act_f1,  act,   f1_state, empty_init, "ACT",   "Apricot F2",  MACHINE_NOT_WORKING )
COMP( 1985, f10,  f1,     0,      act_f1,  act,   f1_state, empty_init, "ACT",   "Apricot F10", MACHINE_NOT_WORKING )



apricotp.cpp
<---------------------------------------------------------------------->
// license:LGPL-2.1+
// copyright-holders:Angelo Salese
/***************************************************************************

    ACT Apricot FP


11/09/2011 - modernised. The portable doesn't seem to have
             scroll registers, and it sets the palette to black.

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

/*

    TODO:

    - devices
    - LCD
    - sound

*/

#include "emu.h"
#include "bus/centronics/ctronics.h"
#include "cpu/i86/i86.h"
#include "cpu/m6800/m6801.h"
#include "formats/apridisk.h"
#include "imagedev/floppy.h"
#include "machine/am9517a.h"
#include "apricotkb.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/ram.h"
#include "machine/wd_fdc.h"
#include "machine/z80sio.h"
#include "sound/sn76496.h"
#include "video/mc6845.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"
#include "apricotp.lh"


namespace {

//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

#define I8086_TAG       "ic7"
#define I8284_TAG       "ic30"
#define I8259A_TAG      "ic51"
#define TMS4500_TAG     "ic42"
#define MC6845_TAG      "ic69"
#define HD63B01V1_TAG   "ic29"
#define AD7574_TAG      "ic34"
#define AD1408_TAG      "ic37"
#define WD2797_TAG      "ic5"
#define SN76489AN_TAG   "ic13"
#define CENTRONICS_TAG  "centronics"
#define SCREEN_LCD_TAG  "screen0"
#define SCREEN_CRT_TAG  "screen1"

enum
{
	LED_STOP = 0,
	LED_POWER,
	LED_SHIFT_LOCK,
	LED_DISK,
	LED_VOICE,
	LED_COLOUR_SELECT,
	LED_CAPS_LOCK
};


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

// ======================> fp_state

class fp_state : public driver_device
{
public:
	fp_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, I8086_TAG)
		, m_soundcpu(*this, HD63B01V1_TAG)
		, m_dmac(*this, "ic17")
		, m_pic(*this, I8259A_TAG)
		, m_pit(*this, "ic20")
		, m_sio(*this, "ic6")
		, m_fdc(*this, WD2797_TAG)
		, m_crtc(*this, MC6845_TAG)
		, m_ram(*this, RAM_TAG)
		, m_floppy0(*this, WD2797_TAG":0")
		, m_floppy1(*this, WD2797_TAG":1")
		, m_floppy(nullptr)
		, m_centronics(*this, CENTRONICS_TAG)
		, m_video_ram(*this, "video_ram", 0x20000, ENDIANNESS_LITTLE)
	{ }

	void fp(machine_config &config);

private:
	static void floppy_formats(format_registration &fr);

	required_device<cpu_device> m_maincpu;
	required_device<cpu_device> m_soundcpu;
	required_device<am9517a_device> m_dmac;
	required_device<pic8259_device> m_pic;
	required_device<pit8253_device> m_pit;
	required_device<z80sio_device> m_sio;
	required_device<wd2797_device> m_fdc;
	required_device<mc6845_device> m_crtc;
	required_device<ram_device> m_ram;
	required_device<floppy_connector> m_floppy0;
	required_device<floppy_connector> m_floppy1;
	floppy_image_device *m_floppy;
	required_device<centronics_device> m_centronics;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual void video_start() override ATTR_COLD;
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	MC6845_UPDATE_ROW(update_row);
	uint16_t mem_r(offs_t offset);
	void mem_w(offs_t offset, uint16_t data);
	uint8_t prtr_snd_r();
	void pint_clr_w(uint8_t data);
	[[maybe_unused]] void ls_w(uint8_t data);
	void contrast_w(uint8_t data);
	void palette_w(uint8_t data);
	void video_w(uint16_t data);
	[[maybe_unused]] void lat_w(offs_t offset, uint8_t data);

	void lat_ls259_w(offs_t offset, int state);

	uint16_t *m_work_ram;

	// video state
	memory_share_creator<uint16_t> m_video_ram;
	uint8_t m_video;

	int m_centronics_busy;
	int m_centronics_select;
	int m_centronics_fault;
	int m_centronics_perror;

	void write_centronics_busy(int state);
	void write_centronics_select(int state);
	void write_centronics_fault(int state);
	void write_centronics_perror(int state);

	void fp_io(address_map &map) ATTR_COLD;
	void fp_mem(address_map &map) ATTR_COLD;
};


//**************************************************************************
//  VIDEO
//**************************************************************************

void fp_state::video_start()
{
}

MC6845_UPDATE_ROW( fp_state::update_row )
{
}

uint32_t fp_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	offs_t addr = (!BIT(m_video, 4) << 15) | (BIT(m_video, 1) << 14);

	for (int y = 0; y < 200; y++)
	{
		for (int sx = 0; sx < 40; sx++)
		{
			uint16_t data = m_video_ram[addr++];

			for (int x = 0; x < 16; x++)
			{
				int color = BIT(data, 15);

				bitmap.pix(y, (sx * 16) + x) = color;

				data <<= 1;
			}
		}
	}

	return 0;
}


static const gfx_layout charset_8x8 =
{
	8,8,
	256,
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};


static GFXDECODE_START( gfx_act_f1 )
	GFXDECODE_ENTRY( I8086_TAG, 0x0800, charset_8x8, 0, 1 )
GFXDECODE_END


uint8_t fp_state::prtr_snd_r()
{
	/*

	    bit     description

	    0       BUSY
	    1       SE
	    2       _FAULT
	    3       PE
	    4       LP23
	    5       DCNG-L
	    6       J9 1-2
	    7       J9 3-4

	*/

	uint8_t data = 0;

	// centronics
	data |= m_centronics_busy;
	data |= m_centronics_select << 1;
	data |= m_centronics_fault << 2;
	data |= m_centronics_perror << 3;

	// floppy
	data |= (m_floppy ? m_floppy->dskchg_r() : 1) << 5;

	return data;
}

void fp_state::pint_clr_w(uint8_t data)
{
	m_pic->ir6_w(CLEAR_LINE);
}


void fp_state::ls_w(uint8_t data)
{
	m_centronics->write_strobe(!BIT(data, 0));
}


void fp_state::contrast_w(uint8_t data)
{
}


void fp_state::palette_w(uint8_t data)
{
	/*

	    bit     description

	    0       B
	    1       G
	    2       R
	    3       I
	    4       index
	    5       index
	    6       index
	    7       index

	*/
}


void fp_state::video_w(uint16_t data)
{
	/*

	    bit     description

	    0       CRTRES-H
	    1       SEL1
	    2       DON-H
	    3       LCDON-H
	    4       SEL2
	    5       L3 even access
	    6       L2 odd access
	    7       L1 video RAM enable
	    8
	    9       STOP LED
	    10      POWER LED
	    11      SHIFT LOCK LED
	    12      DISK LED
	    13      VOICE LED
	    14      COLOUR SELECT LED
	    15      CAPS LOCK LED

	*/

	m_video = data & 0xff;
}

void fp_state::lat_ls259_w(offs_t offset, int state)
{
	switch (offset)
	{
	case 0:
		{
			m_floppy = nullptr;

			if (state) m_floppy = m_floppy0->get_device();
			else m_floppy = m_floppy1->get_device();

			m_fdc->set_floppy(m_floppy);

			if (m_floppy)
				m_floppy->mon_w(0);
		}
		break;
	}
}

void fp_state::lat_w(offs_t offset, uint8_t data)
{
	lat_ls259_w((offset >> 1) & 0x07, BIT(data, 0));
}


uint16_t fp_state::mem_r(offs_t offset)
{
	uint16_t data = 0xffff;

	if (offset >= 0xd0000/2 && offset < 0xf0000/2)
	{
		if (BIT(m_video, 7))
		{
			data = m_video_ram[offset - 0xd0000/2];
		}
		else
		{
			if (offset < m_ram->size()/2)
			{
				data = m_work_ram[offset];
			}
		}
	}
	else
	{
		if (offset < m_ram->size()/2)
		{
			data = m_work_ram[offset];
		}
	}

	return data;
}


void fp_state::mem_w(offs_t offset, uint16_t data)
{
	if (offset >= 0xd0000/2 && offset < 0xe0000/2)
	{
		if (BIT(m_video, 7))
		{
			m_video_ram[offset - 0xd0000/2] = data;
		}
		else
		{
			if (offset < m_ram->size()/2)
			{
				m_work_ram[offset] = data;
			}
		}
	}
	else if (offset >= 0xe0000/2 && offset < 0xf0000/2)
	{
		if (BIT(m_video, 7))
		{
			if (BIT(m_video, 5))
			{
				m_video_ram[offset - 0xd0000/2] = (data & 0xff00) | (m_video_ram[offset - 0xd0000/2] & 0x00ff);
			}

			if (BIT(m_video, 6))
			{
				m_video_ram[offset - 0xd0000/2] = (data & 0x00ff) | (m_video_ram[offset - 0xd0000/2] & 0xff00);
			}
		}
		else
		{
			if (offset < m_ram->size()/2)
			{
				m_work_ram[offset] = data;
			}
		}
	}
	else
	{
		if (offset < m_ram->size()/2)
		{
			m_work_ram[offset] = data;
		}
	}
}


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

//-------------------------------------------------
//  ADDRESS_MAP( fp_mem )
//-------------------------------------------------

void fp_state::fp_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0xf7fff).rw(FUNC(fp_state::mem_r), FUNC(fp_state::mem_w));
	map(0xf8000, 0xfffff).rom().region(I8086_TAG, 0);
}


//-------------------------------------------------
//  ADDRESS_MAP( fp_io )
//-------------------------------------------------

void fp_state::fp_io(address_map &map)
{
	map.unmap_value_high();
	map(0x000, 0x007).rw(m_fdc, FUNC(wd2797_device::read), FUNC(wd2797_device::write)).umask16(0x00ff);
	map(0x008, 0x00f).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0x018, 0x01f).rw(m_sio, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w)).umask16(0x00ff);
	map(0x020, 0x020).w("cent_data_out", FUNC(output_latch_device::write));
	map(0x022, 0x022).w(FUNC(fp_state::pint_clr_w));
	map(0x024, 0x024).r(FUNC(fp_state::prtr_snd_r));
	map(0x026, 0x026).w(SN76489AN_TAG, FUNC(sn76489a_device::write));
	map(0x028, 0x028).w(FUNC(fp_state::contrast_w));
	map(0x02a, 0x02a).w(FUNC(fp_state::palette_w));
	map(0x02e, 0x02f).w(FUNC(fp_state::video_w));
	map(0x040, 0x05f).rw(m_dmac, FUNC(am9517a_device::read), FUNC(am9517a_device::write)).umask16(0x00ff);
	map(0x068, 0x06b).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x06c, 0x06c).w(m_crtc, FUNC(mc6845_device::address_w));
	map(0x06e, 0x06e).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
}



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

//-------------------------------------------------
//  INPUT_PORTS( act )
//-------------------------------------------------

static INPUT_PORTS_START( fp )
	// defined in machine/apricotkb.c
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  pic8259_interface pic_intf
//-------------------------------------------------

/*

    INT0    TIMER
    INT1    FDC
    INT2    6301
    INT3    COMS
    INT4    USART
    INT5    COMS
    INT6    PRINT
    INT7    EOP

*/

void fp_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
	if (!state) m_pic->ir6_w(ASSERT_LINE);
}

void fp_state::write_centronics_select(int state)
{
	m_centronics_select = state;
}

void fp_state::write_centronics_fault(int state)
{
	m_centronics_fault = state;
}

void fp_state::write_centronics_perror(int state)
{
	m_centronics_perror = state;
}

//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( fp )
//-------------------------------------------------

void fp_state::machine_start()
{
	// allocate memory
	m_work_ram = reinterpret_cast<uint16_t *>(m_ram->pointer());
}


void fp_state::machine_reset()
{
	m_video = 0;

	m_fdc->dden_w(0);

	for (offs_t offset = 0; offset < 7; offset++)
	{
		lat_ls259_w(offset, 0);
	}
}



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

void fp_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();

	fr.add(FLOPPY_APRIDISK_FORMAT);
}

//-------------------------------------------------
//  machine_config( fp )
//-------------------------------------------------

void fp_state::fp(machine_config &config)
{
	/* basic machine hardware */
	I8086(config, m_maincpu, 15_MHz_XTAL / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &fp_state::fp_mem);
	m_maincpu->set_addrmap(AS_IO, &fp_state::fp_io);
	m_maincpu->set_irq_acknowledge_callback(I8259A_TAG, FUNC(pic8259_device::inta_cb));

	HD6301V1(config, m_soundcpu, 2000000);
	m_soundcpu->set_disable();

	/* video hardware */
	config.set_default_layout(layout_apricotp);

	screen_device &screen_lcd(SCREEN(config, SCREEN_LCD_TAG, SCREEN_TYPE_RASTER));
	screen_lcd.set_refresh_hz(50);
	screen_lcd.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen_lcd.set_screen_update(FUNC(fp_state::screen_update));
	screen_lcd.set_size(640, 200);
	screen_lcd.set_visarea_full();
	screen_lcd.set_palette("palette");

	screen_device &screen_crt(SCREEN(config, SCREEN_CRT_TAG, SCREEN_TYPE_RASTER));
	screen_crt.set_refresh_hz(50);
	screen_crt.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen_crt.set_screen_update(MC6845_TAG, FUNC(mc6845_device::screen_update));
	screen_crt.set_size(640, 256);
	screen_crt.set_visarea_full();

	PALETTE(config, "palette").set_entries(16);
	GFXDECODE(config, "gfxdecode", "palette", gfx_act_f1);

	MC6845(config, m_crtc, 4000000);
	m_crtc->set_screen(SCREEN_CRT_TAG);
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(fp_state::update_row));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SN76489A(config, SN76489AN_TAG, 2000000).add_route(ALL_OUTPUTS, "mono", 1.00);

	/* Devices */
	APRICOT_KEYBOARD(config, APRICOT_KEYBOARD_TAG, 0);

	AM9517A(config, m_dmac, 250000);
	m_dmac->out_eop_callback().set(m_pic, FUNC(pic8259_device::ir7_w));
	m_dmac->in_ior_callback<1>().set(m_fdc, FUNC(wd2797_device::data_r));
	m_dmac->out_iow_callback<1>().set(m_fdc, FUNC(wd2797_device::data_w));

	PIC8259(config, m_pic, 0);
	m_pic->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(2000000);
	m_pit->out_handler<0>().set(m_pic, FUNC(pic8259_device::ir0_w));
	m_pit->set_clk<1>(2000000);
	m_pit->set_clk<2>(2000000);

	Z80SIO(config, m_sio, 2500000);
	m_sio->out_int_callback().set(m_pic, FUNC(pic8259_device::ir4_w));

	WD2797(config, m_fdc, 2000000);
	m_fdc->intrq_wr_callback().set(m_pic, FUNC(pic8259_device::ir1_w));
	m_fdc->drq_wr_callback().set(m_dmac, FUNC(am9517a_device::dreq1_w));

	FLOPPY_CONNECTOR(config, m_floppy0, "d32w", SONY_OA_D32W, true,  floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy1, "d32w", SONY_OA_D32W, false, floppy_formats);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(fp_state::write_centronics_busy));
	m_centronics->select_handler().set(FUNC(fp_state::write_centronics_select));
	m_centronics->fault_handler().set(FUNC(fp_state::write_centronics_fault));
	m_centronics->perror_handler().set(FUNC(fp_state::write_centronics_perror));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("256K").set_extra_options("512K,1M");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( fp )
//-------------------------------------------------

ROM_START( fp )
	ROM_REGION( 0x8000, I8086_TAG, 0 )
	ROM_LOAD16_BYTE( "lo_fp_3.1.ic20", 0x0000, 0x4000, CRC(0572add2) SHA1(c7ab0e5ced477802e37f9232b5673f276b8f5623) )   // Labelled 11212721 F97E PORT LO VR 3.1
	ROM_LOAD16_BYTE( "hi_fp_3.1.ic9",  0x0001, 0x4000, CRC(3903674b) SHA1(8418682dcc0c52416d7d851760fea44a3cf2f914) )   // Labelled 11212721 BD2D PORT HI VR 3.1

	ROM_REGION( 0x1000, HD63B01V1_TAG, 0 )
	ROM_LOAD( "voice interface hd63b01v01.ic29", 0x0000, 0x1000, NO_DUMP )

	ROM_REGION( 0x100, "proms", 0 )
	ROM_LOAD( "tbp24s10.ic73", 0x000, 0x100, NO_DUMP ) // address decoder 256x4

	ROM_REGION( 0x100, "plds", 0 )
	ROM_LOAD( "pal1 pal12l6.ic2", 0x000, 0x100, NO_DUMP ) // ?
	ROM_LOAD( "pal2 pal10l8.ic35", 0x000, 0x100, NO_DUMP ) // address decoder
	ROM_LOAD( "pal3 pal12l6.ic77", 0x000, 0x100, NO_DUMP ) // ?
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS     INIT        COMPANY  FULLNAME                 FLAGS
COMP( 1984, fp,   0,      0,      fp,      fp,    fp_state, empty_init, "ACT",   "Apricot Portable / FP", MACHINE_NOT_WORKING )



apxen.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    ACT Apricot XEN

    Codename "Candyfloss". The system is not IBM compatible but can run
    MS-DOS (and Windows) as well as Xenix.

    Models:
    - XEN FD (512 KB RAM, 2x 720 KB floppy)
    - XEN HD (1 MB RAM, 1x 720 KB or 1.2 MB floppy, 20M HDD)
    - XEN WS (1 MB RAM, no drives)

    TODO:
    - Boot ROM disable, wrap around mode
    - Floppy
      * FDC is slightly too slow (or CPU too fast), causing Error 28 on boot
        can be fixed by setting delay_register_commit to 12 in wd_fdc.cpp
      * Issues accessing the second drive
    - DMA (verify, seems to fast)
    - Harddisk
    - XEN keyboard (currently using the Apricot Xi keyboard)
    - RS232
    - Printer
    - Colour graphics board
    - Make xen_daisy_device generic?

    Notes:
    - Two graphics cards: Mono and colour boards

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

#include "emu.h"
#include "cpu/i86/i286.h"
#include "machine/bankdev.h"
#include "machine/eepromser.h"
#include "machine/input_merger.h"
#include "machine/mm58274c.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/upd71071.h"
#include "machine/wd_fdc.h"
#include "machine/z80daisy.h"
#include "machine/z80sio.h"
#include "machine/z8536.h"
#include "sound/sn76496.h"
#include "imagedev/floppy.h"
#include "formats/apridisk.h"
#include "bus/apricot/keyboard/keyboard.h"
#include "bus/apricot/video/video.h"
#include "softlist_dev.h"
#include "speaker.h"


//**************************************************************************
//  XEN DAISY CHAIN ABSTRACTION
//**************************************************************************

class xen_daisy_device : public device_t, public z80_daisy_chain_interface
{
public:
	xen_daisy_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0);

	uint8_t acknowledge();
	IRQ_CALLBACK_MEMBER(inta_cb);

protected:
	virtual void device_start() override ATTR_COLD;
};

DEFINE_DEVICE_TYPE(XEN_DAISY, xen_daisy_device, "xen_daisy", "Apricot XEN daisy chain abstraction")

xen_daisy_device::xen_daisy_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: device_t(mconfig, XEN_DAISY, tag, owner, clock)
	, z80_daisy_chain_interface(mconfig, *this)
{
}

void xen_daisy_device::device_start()
{
}

uint8_t xen_daisy_device::acknowledge()
{
	device_z80daisy_interface *intf = daisy_get_irq_device();
	if (intf != nullptr)
		return intf->z80daisy_irq_ack();
	else
		return 0xff;
}

IRQ_CALLBACK_MEMBER(xen_daisy_device::inta_cb)
{
	return acknowledge();
}


namespace {


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

class apxen_state : public driver_device
{
public:
	apxen_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_mem(*this, "mem"),
		m_io(*this, "io"),
		m_dmac(*this, "dma"),
		m_eeprom(*this, "eeprom"),
		m_pic(*this, "pic%u", 0U),
		m_daisy(*this, "daisy"),
		m_pit(*this, "pit"),
		m_cio(*this, "cio"),
		m_sio(*this, "sio"),
		m_fdc(*this, "fdc"),
		m_floppy(*this, "fdc:%u", 0U),
		m_video(*this, "apvideo"),  // "video" causes assert in debug build
		m_cur_floppy(nullptr)
	{ }

	void apxen(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<i80286_cpu_device> m_maincpu;
	required_device<address_map_bank_device> m_mem;
	required_device<address_map_bank_device> m_io;
	required_device<upd71071_device> m_dmac;
	required_device<eeprom_serial_93cxx_device> m_eeprom;
	required_device_array<pic8259_device, 2> m_pic;
	required_device<xen_daisy_device> m_daisy;
	required_device<pit8253_device> m_pit;
	required_device<z8536_device> m_cio;
	required_device<z80sio_device> m_sio;
	required_device<wd2797_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<apricot_video_slot_device> m_video;

	void mem_map_base(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	void io_map_base(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	static void floppy_formats(format_registration &fr);

	void apvid_w(int state);

	uint16_t mem_r(offs_t offset, uint16_t mem_mask);
	void mem_w(offs_t offset, uint16_t data, uint16_t mem_mask);
	uint16_t io_r(offs_t offset, uint16_t mem_mask);
	void io_w(offs_t offset, uint16_t data, uint16_t mem_mask);

	uint8_t get_slave_ack(offs_t offset);
	void leds_w(uint8_t data);
	uint8_t cpu_control_r();
	void cpu_control_w(uint8_t data);
	void cpu_reset_w(uint8_t data);

	uint8_t cio_porta_r();
	void cio_porta_w(uint8_t data);
	void cio_portb_w(uint8_t data);
	void cio_portc_w(uint8_t data);

	floppy_image_device *m_cur_floppy = nullptr;
	bool m_apvid = false;
	uint8_t m_cpu_control = 0;
};


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

void apxen_state::mem_map_base(address_map &map)
{
	map(0x000000, 0xffffff).rw(FUNC(apxen_state::mem_r), FUNC(apxen_state::mem_w));
}

void apxen_state::mem_map(address_map &map)
{
	map(0x000000, 0x07ffff).ram();
	map(0x080000, 0x0effff).noprw(); // silence memory test
	map(0x0f0000, 0x0fffff).rom().region("bios", 0);
	map(0xff0000, 0xffffff).rom().region("bios", 0);
}

void apxen_state::io_map_base(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(apxen_state::io_r), FUNC(apxen_state::io_w));
}

void apxen_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0xc00, 0xc00).w(FUNC(apxen_state::leds_w));
//  map(0xc20, 0xc20) printer
	map(0xc30, 0xc30).w("sn", FUNC(sn76489_device::write));
	map(0xc40, 0xc47).rw(m_cio, FUNC(z8536_device::read), FUNC(z8536_device::write)).umask16(0x00ff);
	map(0xc50, 0xc57).rw(m_sio, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w)).umask16(0x00ff);
	map(0xc60, 0xc7f).rw("rtc", FUNC(mm58274c_device::read), FUNC(mm58274c_device::write)).umask16(0x00ff);
	map(0xc80, 0xc87).rw(m_fdc, FUNC(wd2797_device::read), FUNC(wd2797_device::write)).umask16(0x00ff);
//  map(0xc90, 0xc93) uPD7261 HDC
	map(0xca0, 0xca3).rw(m_pic[0], FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0xca4, 0xca7).rw(m_pic[1], FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0xcb0, 0xcb7).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0xcc0, 0xccf).rw(m_dmac, FUNC(upd71071_device::read), FUNC(upd71071_device::write));
//  map(0xcd0, 0xcdf) uPD71071 DMA 2 (optional, with XP box)
	map(0xce0, 0xce0).rw(FUNC(apxen_state::cpu_control_r), FUNC(apxen_state::cpu_control_w));
	map(0xcf0, 0xcf0).w(FUNC(apxen_state::cpu_reset_w));
}


//**************************************************************************
//  INPUT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( apxen )
INPUT_PORTS_END


//**************************************************************************
//  FLOPPY
//**************************************************************************

void apxen_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_APRIDISK_FORMAT);
}

static void apricot_floppies(device_slot_interface &device)
{
	device.option_add("d32w", SONY_OA_D32W);
}


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

void apxen_state::apvid_w(int state)
{
	m_apvid = bool(state);
}


//**************************************************************************
//  MEMORY
//**************************************************************************

uint16_t apxen_state::mem_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t data = 0xffff;

	// pc/xi compatible mode vram
	if ((offset << 1) < 0x10000 && !m_apvid)
		m_video->mem_r(offset & (0xffff >> 1), data, mem_mask);

	// pc/xi video pointer mirror
	else if  ((offset << 1) >= 0xf0000 && (offset << 1) <= 0xf0fff && !m_apvid)
		m_video->mem_r((offset & (0xffff >> 1)) | (0x0e000 >> 1), data, mem_mask);

	// xen mode vram
	else if  ((offset << 1) >= 0xe0000 && (offset << 1) <= 0xeffff && m_apvid)
		m_video->mem_r(offset & (0xffff >> 1), data, mem_mask);

	// normal access
	else
		data &= m_mem->read16(offset, mem_mask);

	return data;
}

void apxen_state::mem_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	// pc/xi compatible mode vram
	if ((offset << 1) < 0x10000 && !m_apvid)
		m_video->mem_w(offset & (0xffff >> 1), data, mem_mask);

	// pc/xi video pointer mirror
	else if  ((offset << 1) >= 0xf0000 && (offset << 1) <= 0xf0fff && !m_apvid)
		m_video->mem_w((offset & (0xffff >> 1)) | (0x0e000 >> 1), data, mem_mask);

	// xen mode vram
	else if  ((offset << 1) >= 0xe0000 && (offset << 1) <= 0xeffff && m_apvid)
		m_video->mem_w(offset & (0xffff >> 1), data, mem_mask);

	// normal access
	else
		m_mem->write16(offset, data, mem_mask);
}

uint16_t apxen_state::io_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t data = 0xffff;

	if (!m_video->io_r(offset, data, mem_mask))
		data &= m_io->read16(offset, mem_mask);

	return data;
}

void apxen_state::io_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (!m_video->io_w(offset, data, mem_mask))
		m_io->write16(offset, data, mem_mask);
}


//**************************************************************************
//  INTERRUPT HANDLING
//**************************************************************************

static const z80_daisy_config xen_daisy_chain[] =
{
	{ "cio" },
	{ "sio" },
	{ nullptr }
};

uint8_t apxen_state::get_slave_ack(offs_t offset)
{
	// z80 daisy chain
	if (offset == 1)
		return m_daisy->acknowledge();

	// slave pic
	if (offset == 2)
		return m_pic[1]->acknowledge();

	return 0x00;
}


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

void apxen_state::machine_start()
{
}

void apxen_state::machine_reset()
{
	// boot rom enabled, hard reset
	m_cpu_control = 0x00;
}

void apxen_state::leds_w(uint8_t data)
{
	logerror("leds_w: %02x\n", data);

	// 76543---  not used
	// -----2--  hdd led
	// ------1-  fdd led
	// -------0  voice led
}

uint8_t apxen_state::cpu_control_r()
{
	// only bits 0 and 1 can be read back
	return m_cpu_control & 0x03;
}

void apxen_state::cpu_control_w(uint8_t data)
{
	logerror("cpu_control_w: %02x\n", data);

	// 76543---  not used
	// -----2--  full memory (virtual mode) or 1 mb wraparound (real mode)
	// ------1-  hard/soft reset
	// -------0  boot rom enabled

	m_cpu_control = data;
}

void apxen_state::cpu_reset_w(uint8_t data)
{
	logerror("cpu_reset_w: %02x\n", data);

	// data written doesn't matter
	m_maincpu->reset();
}

uint8_t apxen_state::cio_porta_r()
{
	uint8_t data = 0x00;

	data |= m_cur_floppy ? (m_cur_floppy->dskchg_r() << 3) : 0;

	logerror("cio_porta_r: %02x\n", data);

	return data;
}

void apxen_state::cio_porta_w(uint8_t data)
{
	logerror("cio_porta_w: %02x\n", data);

	// 7-------  hdc interrupt (input)
	// -6------  floppy/tape select
	// --5-----  winchester select
	// ---4----  floppy select
	// ----3---  floppy disk change (input)
	// -----2--  floppy disk head load
	// ------10  floppy drive select

	if (BIT(data, 4) == 0)
	{
		if ((data & 0x03) < 2)
			m_cur_floppy = m_floppy[data & 0x03]->get_device();
		else
			m_cur_floppy = nullptr;

		m_fdc->set_floppy(m_cur_floppy);

		// motor always active for now
		if (m_cur_floppy)
			m_cur_floppy->mon_w(0);
	}
}

void apxen_state::cio_portb_w(uint8_t data)
{
	logerror("cio_portb_w: %02x\n", data);

	// 7-------  rs232 dts (input)
	// -65-----  rs232 baud select
	// ---4----  track 43 precomp
	// ----3---  centronics busy (input)
	// -----2--  centronics select (input)
	// ------1-  centronics fault (input)
	// -------0  centronics paper empty (input)
}

void apxen_state::cio_portc_w(uint8_t data)
{
	logerror("cio_portc_w: %02x\n", data);

	// 3---  centronics strobe
	// -2--  eeprom clock
	// --1-  winchester head select 3
	// ---0  floppy disk change reset

	if (BIT(data, 0) && m_cur_floppy)
		m_cur_floppy->dskchg_w(0);

	m_eeprom->clk_write(BIT(data, 2));
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void apxen_state::apxen(machine_config &config)
{
	I80286(config, m_maincpu, 15_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &apxen_state::mem_map_base);
	m_maincpu->set_addrmap(AS_IO, &apxen_state::io_map_base);
	m_maincpu->set_irq_acknowledge_callback(m_pic[0], FUNC(pic8259_device::inta_cb));

	ADDRESS_MAP_BANK(config, m_mem);
	m_mem->set_addrmap(AS_PROGRAM, &apxen_state::mem_map);
	m_mem->set_data_width(16);
	m_mem->set_addr_width(24);
	m_mem->set_endianness(ENDIANNESS_LITTLE);

	ADDRESS_MAP_BANK(config, m_io);
	m_io->set_addrmap(AS_PROGRAM, &apxen_state::io_map);
	m_io->set_data_width(16);
	m_io->set_addr_width(16);
	m_io->set_endianness(ENDIANNESS_LITTLE);

	UPD71071(config, m_dmac, 8000000);
	m_dmac->set_cpu_tag("maincpu");
	m_dmac->set_clock(8000000);
	m_dmac->out_eop_callback().set(m_pic[1], FUNC(pic8259_device::ir2_w));
	m_dmac->dma_read_callback<1>().set(m_fdc, FUNC(wd2797_device::data_r));
	m_dmac->dma_write_callback<1>().set(m_fdc, FUNC(wd2797_device::data_w));

	EEPROM_93C06_16BIT(config, m_eeprom); // NMC9306
	m_eeprom->do_callback().set(m_sio, FUNC(z80sio_device::ctsb_w));

	PIC8259(config, m_pic[0], 0);
	m_pic[0]->out_int_callback().set_inputline(m_maincpu, 0);
	m_pic[0]->in_sp_callback().set_constant(1);
	m_pic[0]->read_slave_ack_callback().set(FUNC(apxen_state::get_slave_ack));

	PIC8259(config, m_pic[1], 0);
	m_pic[1]->out_int_callback().set(m_pic[0], FUNC(pic8259_device::ir2_w));
	m_pic[1]->in_sp_callback().set_constant(0);

	XEN_DAISY(config, m_daisy);
	m_daisy->set_daisy_config(xen_daisy_chain);

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(2000000);
	m_pit->out_handler<0>().set(m_sio, FUNC(z80sio_device::rxca_w));
	m_pit->set_clk<1>(2000000);
	m_pit->out_handler<1>().set(m_sio, FUNC(z80sio_device::txca_w));
	m_pit->set_clk<2>(2000000);
	m_pit->out_handler<2>().set(m_sio, FUNC(z80sio_device::rxtxcb_w));

	INPUT_MERGER_ANY_HIGH(config, "z80int").output_handler().set(m_pic[0], FUNC(pic8259_device::ir1_w));

	Z8536(config, m_cio, 4000000);
	m_cio->irq_wr_cb().set("z80int", FUNC(input_merger_device::in_w<0>));
	m_cio->pa_rd_cb().set(FUNC(apxen_state::cio_porta_r));
	m_cio->pa_wr_cb().set(FUNC(apxen_state::cio_porta_w));
	m_cio->pb_wr_cb().set(FUNC(apxen_state::cio_portb_w));
	m_cio->pc_wr_cb().set(FUNC(apxen_state::cio_portc_w));

	Z80SIO(config, m_sio, 4000000);
	m_sio->out_int_callback().set("z80int", FUNC(input_merger_device::in_w<1>));
	// channel a: rs232
	m_sio->out_txdb_callback().set("kbd", FUNC(apricot_keyboard_bus_device::out_w));
	m_sio->out_dtrb_callback().set(m_eeprom, FUNC(eeprom_serial_93cxx_device::di_write));
	m_sio->out_rtsb_callback().set(m_eeprom, FUNC(eeprom_serial_93cxx_device::cs_write));

	MM58274C(config, "rtc", 32.768_kHz_XTAL);

	// floppy
	WD2797(config, m_fdc, 2000000);
	m_fdc->intrq_wr_callback().set(m_pic[1], FUNC(pic8259_device::ir1_w));
	m_fdc->drq_wr_callback().set([this](int state) { m_dmac->dmarq(state, 1); });
	FLOPPY_CONNECTOR(config, "fdc:0", apricot_floppies, "d32w", apxen_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", apricot_floppies, "d32w", apxen_state::floppy_formats);

	SOFTWARE_LIST(config, "flop_list").set_original("apxen_flop");

	// video hardware
	APRICOT_VIDEO_SLOT(config, m_video, apricot_video_cards, "mono");
	m_video->apvid_handler().set(FUNC(apxen_state::apvid_w));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SN76489(config, "sn", 2000000).add_route(ALL_OUTPUTS, "mono", 1.0);

	// keyboard
	APRICOT_KEYBOARD_INTERFACE(config, "kbd", apricot_keyboard_devices, "hle").in_handler().set(m_sio, FUNC(z80sio_device::rxb_w));
}


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

ROM_START( apxen )
	ROM_REGION16_LE(0x10000, "bios", 0)
	// LO-XEN  3.1.3 7143 (checksum matches)
	ROM_LOAD16_BYTE("lo-xen_313.ic80", 0x0000, 0x8000, CRC(c2a1fd6e) SHA1(8dfc711dd910bc3d43c1120978ba199a13463068))
	// HI-XEN  3.1.3 9BF0 (checksum matches)
	ROM_LOAD16_BYTE("hi-xen_313.ic37", 0x0001, 0x8000, CRC(72ee2f09) SHA1(da11043d40a694802f6d3d27a4359067dd19c8e6))

	// default eeprom configured with 2 floppy drives and serial no. 123456
	ROM_REGION16_LE(0x20, "eeprom", 0)
	ROM_LOAD("eeprom.nv", 0x00, 0x20, CRC(c26d455e) SHA1(ff2d7af6ca21b2fba4c5a9e90926b5049a9fdc86))

	// should probably be moved elsewhere
	ROM_REGION(0x2000, "hdd", 0)
	ROM_LOAD("rodime_ro3055.bin", 0x0000, 0x2000, CRC(61d1544a) SHA1(2177a4c6409c0ee3d3e3e6c659085adf236f8726))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME       FLAGS
COMP( 1985, apxen, 0,      0,      apxen,   apxen, apxen_state, empty_init, "ACT",   "Apricot XEN", MACHINE_NOT_WORKING )



aquaplus_piece.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Vas Crabb
/*
    Aquaplus P/ECE
*/
#include "emu.h"

#include "cpu/c33/s1c33209.h"


namespace {

class piece_state : public driver_device

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

	void piece512k(machine_config &config) ATTR_COLD;
	void piece2m(machine_config &config) ATTR_COLD;

private:
	void mem_base(address_map &map) ATTR_COLD;
	void mem_512k(address_map &map) ATTR_COLD;
	void mem_2m(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};


void piece_state::piece512k(machine_config &config)
{
	S1C33209(config, m_maincpu, 24'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &piece_state::mem_512k);
}

void piece_state::piece2m(machine_config &config)
{
	piece512k(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &piece_state::mem_2m);
}


void piece_state::mem_base(address_map &map)
{
	map(0x010'0000, 0x013'ffff).mirror(0x00c'0000).ram(); // SRAM
	// USB controller at 0x040'0000
}

void piece_state::mem_512k(address_map &map)
{
	mem_base(map);

	map(0x0c0'0000, 0x0c7'ffff).mirror(0x038'0000).rom().region("flash", 0); // load Flash as ROM just to show disassembly
}

void piece_state::mem_2m(address_map &map)
{
	mem_base(map);

	map(0x0c0'0000, 0x0df'ffff).mirror(0x020'0000).rom().region("flash", 0); // load Flash as ROM just to show disassembly
}


INPUT_PORTS_START(piece)
INPUT_PORTS_END


ROM_START(piece512k)
	ROM_DEFAULT_BIOS("v1.20")
	ROM_SYSTEM_BIOS(0, "v1.18", "PieceSystem Ver1.18")
	ROM_SYSTEM_BIOS(1, "v1.20", "PieceSystem Ver1.20")

	ROM_REGION(0x08'0000, "flash", ROMREGION_16BIT | ROMREGION_LE)
	ROMX_LOAD("ver1.18_512k.bin", 0x00'0000, 0x08'0000, CRC(c1298d20) SHA1(ca905d825f5c422b63b0cb622de779183e25bb9a), ROM_BIOS(0))
	ROMX_LOAD("ver1.20_512k.bin", 0x00'0000, 0x08'0000, CRC(0bb58345) SHA1(43b6bae56f89e3fe12aef9f82d55776fb7f16870), ROM_BIOS(1))
ROM_END

ROM_START(piece2m)
	ROM_DEFAULT_BIOS("v1.20")
	ROM_SYSTEM_BIOS(0, "v1.20", "PieceSystem Ver1.20")

	ROM_REGION(0x20'0000, "flash", ROMREGION_16BIT | ROMREGION_LE)
	ROMX_LOAD("ver1.20_2m.bin",   0x00'0000, 0x20'0000, CRC(9d83bb57) SHA1(efecdaba7837fe0fc215f5f1e9c00c050cc5e910), ROM_BIOS(0))
ROM_END

} // anonymous namespace


//   YEAR  NAME       PARENT     COMPAT  MACHINE    INPUT  CLASS        INIT        COMPANY     FULLNAME                FLAGS
SYST(2001, piece512k, 0,         0,      piece512k, piece, piece_state, empty_init, "Aquaplus", "P/ECE (512 kB Flash)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(2001, piece2m,   piece512k, 0,      piece2m,   piece, piece_state, empty_init, "Aquaplus", "P/ECE (2 MB Flash)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



aquarius.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nathan Woods,Nigel Barnes
/***************************************************************************

    Mattel Aquarius


    TODO:

    - proper video timings, seems to be some contention involved
    - floppy support (I/O 0xe6-0xe7 = drive 1, 0xea-0xeb = drive 2)
    - modem

Dick Smith catalog numbers, taken from advertisements:

X-6000 : Aquarius Computer (2K RAM)
X-6005 : Mini Expander
X-6010 : Data Recorder
X-6015 : 16K RAM cart
X-6020 : 32K RAM cart
X-6025 : Thermal Printer
X-6026 : Roll of paper for the printer

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

#include "emu.h"
#include "aquarius.h"

#include "softlist_dev.h"
#include "speaker.h"


/***************************************************************************
    READ/WRITE HANDLERS
***************************************************************************/

/*
    To read stored data from cassette the program should look at bit zero of
    the cassette input port and measure the time difference between leading
    edges or trailing edges. This is to prevent DC level shifting from altering
    pulse width of data. The program should then look for sync bytes for data
    synchronisation before data block transfer. If there is any task that must
    be performed during cassette loading, the maximum allowable time to do the
    job after one byte from cassette, must be less than 80% of the period of a
    mark cycle. Control must be returned at that time to the cassette routine
    in order to maintain data integrity.
*/
uint8_t aquarius_state::cassette_r()
{
	return ((m_cassette)->input() < +0.0) ? 0 : 1;
}


/*
    Sound and cassette port use a common pin. Therefore the signal to cassette
    will appear on audio output. Sound port is a simple one bit I/O and therefore
    it must be toggled at a specific rate under software control.
*/
void aquarius_state::cassette_w(uint8_t data)
{
	m_speaker->level_w(BIT(data, 0));
	m_cassette->output(BIT(data, 0) ? +1.0 : -1.0);
}


/*
    The current state of the vertical sync will appear on bit 0 during a read of
    this port. The waveform and timing spec is shown as follows:

        |<-    Active scan period   ->|V.sync |<-
        |      12.8 ms                |3.6 ms (PAL)
        |                             |2.8 ms (NTSC)
        +++++++++++++++++++++++++++++++       ++++++++++++
        +                             +       +
        +                             +       +
    +++++                             +++++++++
*/
uint8_t aquarius_state::vsync_r()
{
	return (m_screen->vpos() < 16 || m_screen->vpos() > 215) ? 0 : 1;
}


/*
    Bit D0 of this port controls the swapping of the lower 16K block in the memory
    map with the upper 16K. A 1 in this bit indicates swapping. This bit is reset
    after power up initialization.
*/
void aquarius_state::mapper_w(uint8_t data)
{
	m_mapper.select(BIT(data, 0));
}


/*
    Printer handshaking port (read) Port 0xFE when read, presents the clear
    to send status from PRNHASK pin at bit D0. A 1 indicates printer is ready,
    0 means not ready.
*/
uint8_t aquarius_state::printer_r()
{
	return m_printer->cts_r(); /* ready */
}


/*
    This is a single bit I/O at D0, it will perform as a serial output
    port under software control. Since timing is done by software the
    baud rate is variable. In BASIC this is a 1200 baud printer port for
    the 40 column thermal printer.
*/
void aquarius_state::printer_w(uint8_t data)
{
	m_printer->write_txd(BIT(data, 0));
}


/*
    This port is 6 bits wide, when read, it returns the row data from the
    keyboard matrix. The keyboard is usually scanned in the following manner:

    The keyboard is a 6 row by 8 column matrix. The column is connected to
    the higher order address bus A15-A8. When Z80 executes its input
    instruction sets, either the current content of the accumulator (A) or
    the content of register (B) will go to the higher order address bus.
    Therefore the keyboard can be scanned by placing a specific scanning
    pattern in (A) or (B) and reading the result returned on rows.
*/
uint8_t aquarius_state::keyboard_r(offs_t offset)
{
	uint8_t result = 0xff;

	if (!BIT(offset,  8)) result &= m_y[0]->read();
	if (!BIT(offset,  9)) result &= m_y[1]->read();
	if (!BIT(offset, 10)) result &= m_y[2]->read();
	if (!BIT(offset, 11)) result &= m_y[3]->read();
	if (!BIT(offset, 12)) result &= m_y[4]->read();
	if (!BIT(offset, 13)) result &= m_y[5]->read();
	if (!BIT(offset, 14)) result &= m_y[6]->read();
	if (!BIT(offset, 15)) result &= m_y[7]->read();

	return result;
}


/*
    Software lock: Writing this port with an 8 bit value will set the software
    scrambler pattern. The data that appears on the output side will be the
    result of the input bus EX-ORed with this pattern, bit by bit. The software
    lock is a scrambler built between the CPU and external interface. The
    scrambling pattern is contained in port 0xFF and is not readable. CPU data
    output to external bus will be XORed with this pattern in a bit by bit
    fashion to generate the real data on external bus. By the same mechanism,
    data from external bus is also XORed with this pattern and read by CPU.

    Therefore it the external device is RAM, the software lock simply has no
    effect as long as the scrambling pattern remains unchanged. For I/O
    operation the pattern is gated to 0 and thus scrambling action is nullified.

    In BASIC operation the scrambling pattern is generated by a random number
    routine. For game cartridge the lock pattern is generated from data in the
    game cartridge itself.
*/
void aquarius_state::scrambler_w(uint8_t data)
{
	m_scrambler = data;
}


/***************************************************************************
    DRIVER INIT
***************************************************************************/

void aquarius_state::machine_start()
{
	save_item(NAME(m_scrambler));
}

void aquarius_state::machine_reset()
{
	/* reset memory mapper after power up */
	m_mapper.select(0);
}


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

void aquarius_state::aquarius_mem(address_map &map)
{
	map(0x0000, 0xffff).view(m_mapper);
	/* Normal mode */
	m_mapper[0](0x0000, 0x2fff).rom().region("maincpu", 0);
	m_mapper[0](0x3000, 0x33ff).ram().w(FUNC(aquarius_state::videoram_w)).share("videoram");
	m_mapper[0](0x3400, 0x37ff).ram().w(FUNC(aquarius_state::colorram_w)).share("colorram");
	m_mapper[0](0x3800, 0x3fff).ram().share("basicram");
	m_mapper[0](0x4000, 0xbfff).lrw8(NAME([this](offs_t offset) { return m_exp->mreq_r(offset) ^ m_scrambler; }), NAME([this](offs_t offset, u8 data) { m_exp->mreq_w(offset, data ^ m_scrambler); }));
	m_mapper[0](0xc000, 0xffff).lrw8(NAME([this](offs_t offset) { return m_exp->mreq_ce_r(offset) ^ m_scrambler; }), NAME([this](offs_t offset, u8 data) { m_exp->mreq_ce_w(offset, data ^ m_scrambler); }));
	/* CP/M mode */
	m_mapper[1](0x0000, 0x3fff).lrw8(NAME([this](offs_t offset) { return m_exp->mreq_ce_r(offset) ^ m_scrambler; }), NAME([this](offs_t offset, u8 data) { m_exp->mreq_ce_w(offset, data ^ m_scrambler); }));
	m_mapper[1](0x4000, 0xbfff).lrw8(NAME([this](offs_t offset) { return m_exp->mreq_r(offset) ^ m_scrambler; }), NAME([this](offs_t offset, u8 data) { m_exp->mreq_w(offset, data ^ m_scrambler); }));
	m_mapper[1](0xc000, 0xefff).rom().region("maincpu", 0);
	m_mapper[1](0xf000, 0xf3ff).ram().w(FUNC(aquarius_state::videoram_w)).share("videoram");
	m_mapper[1](0xf400, 0xf7ff).ram().w(FUNC(aquarius_state::colorram_w)).share("colorram");
	m_mapper[1](0xf800, 0xffff).ram().share("basicram");
}

void aquarius_state::aquarius_io(address_map &map)
{
	map(0x00, 0xff).mirror(0xff00).rw(m_exp, FUNC(aquarius_cartridge_slot_device::iorq_r), FUNC(aquarius_cartridge_slot_device::iorq_w));
//  map(0x7e, 0x7f).mirror(0xff00).rw(FUNC(aquarius_state::modem_r), FUNC(aquarius_state::modem_w));
	map(0xfc, 0xfc).mirror(0xff00).rw(FUNC(aquarius_state::cassette_r), FUNC(aquarius_state::cassette_w));
	map(0xfd, 0xfd).mirror(0xff00).rw(FUNC(aquarius_state::vsync_r), FUNC(aquarius_state::mapper_w));
	map(0xfe, 0xfe).mirror(0xff00).rw(FUNC(aquarius_state::printer_r), FUNC(aquarius_state::printer_w));
	map(0xff, 0xff).select(0xff00).rw(FUNC(aquarius_state::keyboard_r), FUNC(aquarius_state::scrambler_w));
}


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

/* the 'reset' key is directly tied to the reset line of the cpu */
INPUT_CHANGED_MEMBER(aquarius_state::aquarius_reset)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? CLEAR_LINE : ASSERT_LINE);
}

static INPUT_PORTS_START( aquarius )
	PORT_START("Y0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("= +\tNEXT")   PORT_CODE(KEYCODE_EQUALS)    PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2190 \\") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)   PORT_CHAR('\\') // U+2190 = ←
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(": *\tPEEK")   PORT_CODE(KEYCODE_QUOTE)     PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RTN")         PORT_CODE(KEYCODE_ENTER)     PORT_CHAR(13)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("; @\tPOKE")   PORT_CODE(KEYCODE_COLON)     PORT_CHAR(';') PORT_CHAR('@')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(". >\tVAL")    PORT_CODE(KEYCODE_STOP)      PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("- _\tFOR")    PORT_CODE(KEYCODE_MINUS)     PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("/ ^")         PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('/') PORT_CHAR('^')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("0 ?")         PORT_CODE(KEYCODE_0)         PORT_CHAR('0') PORT_CHAR('?')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("P")           PORT_CODE(KEYCODE_P)         PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR(16)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("L\tPOINT")    PORT_CODE(KEYCODE_L)         PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR(12)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(", <\tSTR$")   PORT_CODE(KEYCODE_COMMA)     PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("9 )\tCOPY")   PORT_CODE(KEYCODE_9)         PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("O")           PORT_CODE(KEYCODE_O)         PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(15)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("K\tPRESET")   PORT_CODE(KEYCODE_K)         PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR(11)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("M")           PORT_CODE(KEYCODE_M)         PORT_CHAR('m') PORT_CHAR('M') PORT_CHAR(13)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("N\tRIGHT$")   PORT_CODE(KEYCODE_N)         PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(14)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("J\tPSET")     PORT_CODE(KEYCODE_J)         PORT_CHAR('j') PORT_CHAR('J') PORT_CHAR(10)
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("8 (\tRETURN") PORT_CODE(KEYCODE_8)         PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("I")           PORT_CODE(KEYCODE_I)         PORT_CHAR('i') PORT_CHAR('I') PORT_CHAR(9)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("7 '\tGOSUB")  PORT_CODE(KEYCODE_7)         PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("U")           PORT_CODE(KEYCODE_U)         PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(21)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("H")           PORT_CODE(KEYCODE_H)         PORT_CHAR('h') PORT_CHAR('H') PORT_CHAR(8)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("B\tMID$")     PORT_CODE(KEYCODE_B)         PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR(2)
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("6 &\tON")     PORT_CODE(KEYCODE_6)         PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Y")           PORT_CODE(KEYCODE_Y)         PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR(25)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("G\tBELL")     PORT_CODE(KEYCODE_G)         PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR(7)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("V\tLEFT$")    PORT_CODE(KEYCODE_V)         PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR(22)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C\tSTOP")     PORT_CODE(KEYCODE_C)         PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR(3)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F\tDATA")     PORT_CODE(KEYCODE_F)         PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR(6)
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("5 %\tGOTO")   PORT_CODE(KEYCODE_5)         PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("T\tINPUT")    PORT_CODE(KEYCODE_T)         PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(20)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("4 $\tTHEN")   PORT_CODE(KEYCODE_4)         PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("R\tRETYP")    PORT_CODE(KEYCODE_R)         PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR(18)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D\tREAD")     PORT_CODE(KEYCODE_D)         PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR(4)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("X\tDELINE")   PORT_CODE(KEYCODE_X)         PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(24)
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("3 #\tIF")     PORT_CODE(KEYCODE_3)         PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("E\tDIM")      PORT_CODE(KEYCODE_E)         PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(5)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("S\tSTPLST")   PORT_CODE(KEYCODE_S)         PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR(19)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Z\tCLOAD")    PORT_CODE(KEYCODE_Z)         PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(26)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPACE\tCHR$") PORT_CODE(KEYCODE_SPACE)     PORT_CHAR(32)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A\tCSAVE")    PORT_CODE(KEYCODE_A)         PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR(1)
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("2 \"\tLIST")  PORT_CODE(KEYCODE_2)         PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("W\tREM")      PORT_CODE(KEYCODE_W)         PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR(23)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("1 !\tRUN")    PORT_CODE(KEYCODE_1)         PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Q")           PORT_CODE(KEYCODE_Q)         PORT_CHAR('q') PORT_CHAR('Q') PORT_CHAR(17)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT")       PORT_CODE(KEYCODE_LSHIFT)    PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTL")         PORT_CODE(KEYCODE_LCONTROL)  PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("RESET")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RST")         PORT_CODE(KEYCODE_ESC) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(aquarius_state::aquarius_reset), 0)
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(aquarius_state::gfx_changed)
{
	m_gfx_bank = newval;
	m_tilemap->mark_all_dirty();
}

static INPUT_PORTS_START( aquarius_ar )
	PORT_INCLUDE(aquarius)

	PORT_MODIFY("Y0")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2190 \ufefb\t\u2190 \\")  PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)           PORT_CHAR('\\', 0xfefb)               // U+FEFB = ﻻ  U+2190 = ←
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0630 *\t: *\tPEEK")       PORT_CODE(KEYCODE_QUOTE)     PORT_CHAR(':', 0x0630) PORT_CHAR('*')                        // U+0630 = ذ
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0636 @\t; @\tPOKE")       PORT_CODE(KEYCODE_COLON)     PORT_CHAR(';', 0x0636) PORT_CHAR('@')                        // U+0636 = ض
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0629 >\t. >\tVAL")        PORT_CODE(KEYCODE_STOP)      PORT_CHAR('.', 0x0629) PORT_CHAR('>')                        // U+0629 = ة

	PORT_MODIFY("Y1")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0638 ^\t/ ^")             PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('/', 0x0638) PORT_CHAR('^')                        // U+0638 = ظ
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0633  \u0651\tP")         PORT_CODE(KEYCODE_P)         PORT_CHAR('p', 0x0633) PORT_CHAR('P', 0x0651) PORT_CHAR(16)  // U+0633 = س  U+0651 = shadda (combining, keep preceding space)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0648 \u0624\tL\tPOINT")   PORT_CODE(KEYCODE_L)         PORT_CHAR('l', 0x0648) PORT_CHAR('L', 0x0624) PORT_CHAR(12)  // U+0648 = و  U+0624 = ؤ
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0649 <\t, <\tSTR$")       PORT_CODE(KEYCODE_COMMA)     PORT_CHAR(',', 0x0649) PORT_CHAR('<')                        // U+0649 = ى

	PORT_MODIFY("Y2")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0639 :\tO")               PORT_CODE(KEYCODE_O)         PORT_CHAR('o', 0x0639) PORT_CHAR('O')         PORT_CHAR(15)  // U+0639 = ع
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0627 \u0623\tK\tPRESET")  PORT_CODE(KEYCODE_K)         PORT_CHAR('k', 0x0627) PORT_CHAR('K', 0x0623) PORT_CHAR(11)  // U+0627 = ا  U+0623 = أ
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u062f \u061f\tM")          PORT_CODE(KEYCODE_M)         PORT_CHAR('m', 0x062f) PORT_CHAR('M', 0x061f) PORT_CHAR(13)  // U+062F = د  U+061F = ؟
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u062b  \u064d\tN\tRIGHT$") PORT_CODE(KEYCODE_N)         PORT_CHAR('n', 0x062b) PORT_CHAR('N', 0x064d) PORT_CHAR(14)  // U+062B = ث  U+064D = kasratan (combining, keep preceding space)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0645  \u064e\tJ\tPSET")   PORT_CODE(KEYCODE_J)         PORT_CHAR('j', 0x0645) PORT_CHAR('J', 0x064e) PORT_CHAR(10)  // U+0645 = م  U+064E = fatha (combining, keep preceding space)

	PORT_MODIFY("Y3")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0630 /\tI")               PORT_CODE(KEYCODE_I)         PORT_CHAR('i', 0x0630) PORT_CHAR('I')         PORT_CHAR(9)   // U+0630 = ذ
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0641 \u061b\tU")          PORT_CODE(KEYCODE_U)         PORT_CHAR('u', 0x0641) PORT_CHAR('U', 0x061b) PORT_CHAR(21)  // U+0641 = ف  U+061B = ؛
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u062a  \u064b\tH")         PORT_CODE(KEYCODE_H)         PORT_CHAR('h', 0x062a) PORT_CHAR('H', 0x064b) PORT_CHAR(8)   // U+062A = ت  U+064B = fathatan (combining, keep preceding space)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u062e  \u0650\tB\tMID$")   PORT_CODE(KEYCODE_B)         PORT_CHAR('b', 0x062e) PORT_CHAR('B', 0x0650) PORT_CHAR(2)   // U+062e = خ  U+0650 = kasra (combining, keep preceding space)

	PORT_MODIFY("Y4")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0628 \u060c\tY")          PORT_CODE(KEYCODE_Y)         PORT_CHAR('y', 0x0628) PORT_CHAR('Y', 0x060c) PORT_CHAR(25)  // U+0628 = ب  U+060C = ،
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0646  \u0652\tG\tBELL")   PORT_CODE(KEYCODE_G)         PORT_CHAR('g', 0x0646) PORT_CHAR('G', 0x0652) PORT_CHAR(7)   // U+0646 = ن  U+0652 = sukun (combining, keep preceding space)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u063a .\tV\tLEFT$")        PORT_CODE(KEYCODE_V)         PORT_CHAR('v', 0x063a) PORT_CHAR('V')         PORT_CHAR(22)  // U+063A = غ
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0637\tC\tSTOP")           PORT_CODE(KEYCODE_C)         PORT_CHAR('c', 0x0637) PORT_CHAR('C')         PORT_CHAR(3)   // U+0637 = ط
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0631  \u064c\tF\tDATA")   PORT_CODE(KEYCODE_F)         PORT_CHAR('f', 0x0631) PORT_CHAR('F', 0x064c) PORT_CHAR(6)   // U+0631 = ر  U+064C = dammatan (combining, keep preceding space)

	PORT_MODIFY("Y5")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u062d \\\tT\tINPUT")       PORT_CODE(KEYCODE_T)         PORT_CHAR('t', 0x062d) PORT_CHAR('T')         PORT_CHAR(20)  // U+062D = ح
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0647 \u0640\tR\tRETYP")   PORT_CODE(KEYCODE_R)         PORT_CHAR('r', 0x0647) PORT_CHAR('R', 0x0640) PORT_CHAR(18)  // U+0647 = ه  U+0640 = tatweel
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0644  \u064f\tD\tREAD")   PORT_CODE(KEYCODE_D)         PORT_CHAR('d', 0x0644) PORT_CHAR('D', 0x064f) PORT_CHAR(4)   // U+0644 = ل  U+064F = damma (combining, keep preceding space)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0635\tX\tDELINE")         PORT_CODE(KEYCODE_X)         PORT_CHAR('x', 0x0635) PORT_CHAR('X')         PORT_CHAR(24)  // U+0635 = ص

	PORT_MODIFY("Y6")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0621  \u0654\tE\tDIM")    PORT_CODE(KEYCODE_E)         PORT_CHAR('e', 0x0621) PORT_CHAR('E', 0x0654) PORT_CHAR(5)   // U+0621 = ء  U+0654 = hamza above (combining, keep preceding space)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u064a \u0625\tS\tSTPLST")  PORT_CODE(KEYCODE_S)         PORT_CHAR('s', 0x064a) PORT_CHAR('S', 0x0625) PORT_CHAR(19)  // U+064A = ي  U+0625 = إ
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0634\tZ\tCLOAD")          PORT_CODE(KEYCODE_Z)         PORT_CHAR('z', 0x0634) PORT_CHAR('Z')         PORT_CHAR(26)  // U+0634 = ش
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0642 \u0622\tA\tCSAVE")   PORT_CODE(KEYCODE_A)         PORT_CHAR('a', 0x0642) PORT_CHAR('A', 0x0622) PORT_CHAR(1)   // U+0642 = ق  U+0622 = آ

	PORT_MODIFY("Y7")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u0643\tW\tREM")            PORT_CODE(KEYCODE_W)         PORT_CHAR('w', 0x0643) PORT_CHAR('W')         PORT_CHAR(23)  // U+0643 = ك
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u062c\tQ")                 PORT_CODE(KEYCODE_Q)         PORT_CHAR('q', 0x062c) PORT_CHAR('Q')         PORT_CHAR(17)  // U+062C = ج

	PORT_START("GFX")
	PORT_CONFNAME(0x01, 0x00, "Character ROM") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(aquarius_state::gfx_changed), 0)
	PORT_CONFSETTING(0x00, "(G)raphics")
	PORT_CONFSETTING(0x01, "(A)rabic")
INPUT_PORTS_END


/***************************************************************************
    CHARACTER LAYOUT
***************************************************************************/

static const gfx_layout aquarius_charlayout =
{
	8, 8,
	256,
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, },
	8 * 8
};

static const gfx_layout aquarius_ar_charlayout =
{
	8, 8,
	512,
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, },
	8 * 8
};

/* Graphics Decode Information */

static GFXDECODE_START( gfx_aquarius )
	GFXDECODE_ENTRY( "gfx1", 0x0000, aquarius_charlayout, 0, 256 )
GFXDECODE_END

static GFXDECODE_START( gfx_aquarius_ar )
	GFXDECODE_ENTRY( "gfx1", 0x0000, aquarius_ar_charlayout, 0, 256 )
GFXDECODE_END


/***************************************************************************
    DEVICE CONFIGURATION
***************************************************************************/

static DEVICE_INPUT_DEFAULTS_START(printer)
	DEVICE_INPUT_DEFAULTS("RS232_RXBAUD", 0xff, RS232_BAUD_1200)
	DEVICE_INPUT_DEFAULTS("RS232_DATABITS", 0xff, RS232_DATABITS_8)
	DEVICE_INPUT_DEFAULTS("RS232_PARITY", 0xff, RS232_PARITY_NONE)
	DEVICE_INPUT_DEFAULTS("RS232_STOPBITS", 0xff, RS232_STOPBITS_2)
DEVICE_INPUT_DEFAULTS_END

void aquarius_state::cfg_ram16(device_t* device)
{
	device->subdevice<aquarius_cartridge_slot_device>("exp2")->set_default_option("ram16");
}

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

void aquarius_state::aquarius(machine_config &config)
{
	Z80(config, m_maincpu, 7.15909_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &aquarius_state::aquarius_mem);
	m_maincpu->set_addrmap(AS_IO, &aquarius_state::aquarius_io);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(7.15909_MHz_XTAL, 458, 0, 352, 262, 0, 232);
	m_screen->set_screen_update(FUNC(aquarius_state::screen_update_aquarius));
	m_screen->set_video_attributes(VIDEO_UPDATE_SCANLINE);
	m_screen->set_palette(m_palette);
	m_screen->scanline().set([this](int scanline) { m_maincpu->adjust_icount(-4); }); // TODO: this tries to compensate for contention, needs a better understanding of video timings

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_aquarius);
	TEA1002(config, m_tea1002, 7.15909_MHz_XTAL);
	PALETTE(config, m_palette, FUNC(aquarius_state::aquarius_palette), 512, 16);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_formats(aquarius_cassette_formats);
	m_cassette->set_interface("aquarius_cass");

	RS232_PORT(config, m_printer, default_rs232_devices, "printer");
	m_printer->set_option_device_input_defaults("printer", DEVICE_INPUT_DEFAULTS_NAME(printer));

	AQUARIUS_CARTRIDGE_SLOT(config, m_exp, 7.15909_MHz_XTAL / 2, aquarius_cartridge_devices, "mini");
	m_exp->set_option_machine_config("mini", cfg_ram16);
	m_exp->irq_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_exp->nmi_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);

	SOFTWARE_LIST(config, "cart_list").set_original("aquarius_cart");
	SOFTWARE_LIST(config, "cass_list").set_original("aquarius_cass");
}

void aquarius_state::aquariusp(machine_config &config)
{
	aquarius(config);

	m_screen->set_raw(7.15909_MHz_XTAL, 458, 0, 352, 312, 0, 232);

	m_tea1002->set_unscaled_clock(8.867238_MHz_XTAL);
}

void aquarius_state::aquarius_ar(machine_config &config)
{
	aquariusp(config);

	m_gfxdecode->set_info(gfx_aquarius_ar);
}


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

ROM_START( aquarius )
	ROM_REGION(0x3000, "maincpu", ROMREGION_ERASE00)

	/* basic rom */
	ROM_DEFAULT_BIOS("s2")
	ROM_SYSTEM_BIOS(0, "s2", "S2")
	ROMX_LOAD("aq_s2.u2", 0x0000, 0x2000, CRC(5cfa5b42) SHA1(02c8ee11e911d1aa346812492d14284b6870cb3e), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "s1", "S1")
	ROMX_LOAD("aq.u2", 0x0000, 0x2000, CRC(28d0fdbd) SHA1(58019da049b611a07adc6456cc9d77d92423d62a), ROM_BIOS(1))

	/* charrom */
	ROM_REGION(0x0800, "gfx1", 0)
	ROM_LOAD("aq2.u5", 0x0000, 0x0800, CRC(e117f57c) SHA1(3588c0267c67dfbbda615bcf8dc3d3a5c5bd815a))
ROM_END

#define rom_aquariusp rom_aquarius

ROM_START( aquarius2 )
	ROM_REGION(0x3000, "maincpu", ROMREGION_ERASE00)

	/* extended basic rom */
	ROM_LOAD("aq2_1.rom", 0x0000, 0x2000, CRC(5cfa5b42) SHA1(02c8ee11e911d1aa346812492d14284b6870cb3e))
	ROM_LOAD("aq2_2.rom", 0x2000, 0x1000, CRC(c95117c6) SHA1(6ee8571a93b9b371dfdd26334ae886a69c5b3daf))

	/* charrom */
	ROM_REGION(0x0800, "gfx1", 0)
	ROM_LOAD("aq2.u5", 0x0000, 0x0800, CRC(e117f57c) SHA1(3588c0267c67dfbbda615bcf8dc3d3a5c5bd815a))
ROM_END

ROM_START( aquarius_ar )
	ROM_REGION(0x3000, "maincpu", ROMREGION_ERASE00)

	/* basic rom */
	ROM_LOAD("aq_s2.u2", 0x0000, 0x2000, CRC(5cfa5b42) SHA1(02c8ee11e911d1aa346812492d14284b6870cb3e))

	/* charrom */
	ROM_REGION(0x1000, "gfx1", 0)
	ROM_LOAD("xz-2120-1_v1.2.u5", 0x0000, 0x1000, CRC(09102213) SHA1(4919f84cb57df000910035b08c35315c732052bb))
ROM_END


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

//    YEAR  NAME         PARENT    COMPAT  MACHINE      INPUT        CLASS           INIT        COMPANY                FULLNAME              FLAGS
COMP( 1983, aquarius,    0,        0,      aquarius,    aquarius,    aquarius_state, empty_init, "Mattel Electronics",  "Aquarius (NTSC)",    0 )
COMP( 1983, aquariusp,   aquarius, 0,      aquariusp,   aquarius,    aquarius_state, empty_init, "Mattel Electronics",  "Aquarius (PAL)",     0 )
COMP( 1984, aquarius2,   aquarius, 0,      aquarius,    aquarius,    aquarius_state, empty_init, "Radofin",             "Aquarius II",        0 )
COMP( 198?, aquarius_ar, aquarius, 0,      aquarius_ar, aquarius_ar, aquarius_state, empty_init, "Ecico Electronics",   "Aquarius (Arabic)",  0 )



arcadia.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Peter Trauner
/******************************************************************************
Consolidation and enhancement of documentation by Manfred Schneider based on previous work from
 PeT mess@utanet.at and Paul Robson (autismuk@aol.com) minor updates by incog

 Schematics, manuals and anything you can desire for at http://amigan.classicgaming.gamespy.com/

 TODO: implement the RESET key on the front panel
       find a dump of the character ROM
       convert the drawing code to tilemap


 1. General
   SYSTEM
- Signetics 2650 CPU at 3.58/4 MHz(for NTSC) or at 4,433/5 (for PAL)
- 1k x 8 of RAM (physically present but only 512 byte available)
- 2 x 2 axis Analogue Joysticks
- 2 x 12 button Controllers
- 3 buttons on system unit and CPU Reset
   VIDEO
- 128 x 208 pixel resolution (alternate 128 x 104 mode available)
- 16 x 26 (can be 16x13) background display (2 colour 8 x 8 pixel characters)
- 4 x 8 x 8 Sprites (2 colour 8 x 8 pixels)
- total of 8 user defined characters available
   SOUND
- Single channel beeper


2. Memory map
The memory map of the 2001 is below.
0000 - 0FFF 4k ROM Block 1 (first 4k of a cartridge)
1000 - 13FF     mirror of $1800-$1BFF
1400 - 17FF     mirror of $1800-$1BFF
1800 - 1BFF     UVI2637 Area (detail description in video/arcadia.c)
    1800 - 18CF     Screen display , upper 13 lines, characters/palette high bits
    18D0 - 18EF Free for user programs.
    18F0 - 18F7     Sprite coordinates y0x0y1x1y2x2y3x3
    18F8 - 1908     registers of UVI
    1909 - 197F     Unmapped
    1980 - 19BF     User defined characters (8 possible, 8 bytes per character)
    19C0 - 19F7     Unmapped
    19F8 - 19FF     registers of UVI
    1A00 - 1ACF Screen display , lower 13 lines, characters/palette high bits
    1AD0 - 1AFF User memory
    1B00 - 1BFF     mirror of 1900-19FF
1C00 - 1FFF     mirror of 1800-1BFF
2000 - 2FFF 4k ROM Block 2 (for 8k carts such as Jungler)
3000 - 3FFF mirror of 1000-1FFF
4000 - 4FFF mirror of 0000-0FFF
5000 - 5FFF mirror of 1000-1FFF
6000 - 6FFF mirror of 0000-0FFF
7000 - 7FFF mirror of 1000-1FFF

The Palladium VCG memory map is as follows.
0000 - 0FFF 4k ROM Block 1 (first 4k of a cartridge)
1000 - 17FF     could be ROM or RAM but no Cartridge uses this
1800 - 1BFF     UVI2637 Area (detail description in video/arcadia.c)
    1800 - 18CF     Screen display , upper 13 lines, characters/palette high bits
    18D0 - 18EF Free for user programs.
    18F0 - 18F7     Sprite coordinates y0x0y1x1y2x2y3x3
    18F8 - 1908     registers of UVI
    1909 - 197F     Unmapped
    1980 - 19BF     User defined characters (8 possible, 8 bytes per character)
    19C0 - 19F7     Unmapped
    19F8 - 19FF     registers of UVI
    1A00 - 1ACF Screen display , lower 13 lines, characters/palette high bits
    1AD0 - 1AFF User memory
    1B00 - 1BFF     mirror of 1900-19FF
1C00 - 1FFF     could be ROM or RAM but no Cartridge uses this
2000 - 2FFF 4k ROM Block  (for 8k carts such as Jungler)
3000 - 3FFF could be ROM or RAM but no Cartridge uses this
4000 - 4FFF 4k ROM Block  (first 2K used by Golf)
5000 - 5FFF 4k ROM Block
6000 - 6FFF 4k ROM Block
7000 - 7FFF 4k ROM Block


3. ROM Images
ROM Images are loaded into 0000-0FFF. If the ROM is an 8k ROM the
second half of the Rom is located at 2000-2FFF. Except for the Golf cart
which is located from 0x0000-0x0FFF and a 2kbyte block from 0x4000 only on
Palladium VCG.


4. Controls
All key controls are indicated by a bit going to '1'. Unused bits at
the memory location are set to zero.

Keypads

1900-1902 (Player 1) 1904-1906 (Player 2)
The keypads are arranged as follows :-

        1       2       3
        4       5       6
        7       8       9
      Enter     0     Clear

Row 1/4/7 is 1900/4, Row 2/5/8/0 is 1901/5 and Row 3/6/9 is 1902/6
The topmost key is bit 3, the lowermost key is bit 0.

Location $1908 contains bit 0 Start,bit 1 Option,bit 2 Difficulty.
These keys are "latched" i.e. a press causes a logic 1 to appear
on the current frame.

The fire buttons are equivalent to Keypad #2 e.g. they are 1901 and
1905 bit 3.

Palladium has 4 additional key per keypad which are mapped at
1903 palladium player 1
1907 palladium player 2


5. Other information
Interrupts are not supported
The Read/Write 2650 CPU Port-Commands do not appear to be connected to
anything in hardware. No cartridge has been found which uses them.

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

#include "emu.h"
#include "arcadia.h"
#include "softlist_dev.h"
#include "speaker.h"

void arcadia_state::arcadia_mem(address_map &map)
{
	map(0x0000, 0x0fff).r(m_cart, FUNC(arcadia_cart_slot_device::read_rom));
	map(0x1800, 0x1aff).rw(FUNC(arcadia_state::video_r), FUNC(arcadia_state::video_w));
}

/* The Emerson Arcadia 2001 controllers have 2 fire buttons on the side,
   but actually they are wired to keypad button #2. The following definitions
   are meant to document this fact. The keypad has the following layout:

     1  2  3
     4  5  6
     7  8  9
    Cl  0 En                                                                  */

static INPUT_PORTS_START( arcadia )
	PORT_START("panel")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START )    PORT_NAME("Start")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )    PORT_NAME("Option")          PORT_CODE(KEYCODE_O)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT )   PORT_NAME("Select")
//  PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )    PORT_NAME("Reset")           PORT_CODE(KEYCODE_R)         Not implemented

	PORT_START("controller1_col1")
	PORT_BIT( 0xf0, 0xf0, IPT_UNUSED)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad Clear") PORT_CODE(KEYCODE_MINUS_PAD)

	PORT_START("controller1_col2")
	PORT_BIT( 0xf0, 0xf0, IPT_UNUSED)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 2/Button") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 0") PORT_CODE(KEYCODE_0_PAD)

	PORT_START("controller1_col3")
	PORT_BIT( 0xf0, 0xf0, IPT_UNUSED)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad Enter") PORT_CODE(KEYCODE_PLUS_PAD)

	PORT_START("controller1_extra")
	PORT_BIT( 0xff, 0xf0, IPT_UNUSED) // used in palladium

	PORT_START("controller2_col1")
	PORT_BIT( 0xf0, 0xf0, IPT_UNUSED)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 1") PORT_CODE(KEYCODE_Z)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 4") PORT_CODE(KEYCODE_A)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 7") PORT_CODE(KEYCODE_Q)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad Clear") PORT_CODE(KEYCODE_V)

	PORT_START("controller2_col2")
	PORT_BIT( 0xf0, 0xf0, IPT_UNUSED)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 2/Button") PORT_CODE(KEYCODE_X)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 5") PORT_CODE(KEYCODE_S)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 8") PORT_CODE(KEYCODE_W)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 0") PORT_CODE(KEYCODE_F)

	PORT_START("controller2_col3")
	PORT_BIT( 0xf0, 0xf0, IPT_UNUSED)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 3") PORT_CODE(KEYCODE_C)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 6") PORT_CODE(KEYCODE_D)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 9") PORT_CODE(KEYCODE_E)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad Enter") PORT_CODE(KEYCODE_R)

	PORT_START("controller2_extra")
	PORT_BIT( 0xff, 0xf0, IPT_UNUSED) // used in palladium

/* FIXME: the joystick are analog - the actual definition is merely an hack */

#if 0
	// auto centering too slow, so only using 5 bits, and scaling at videoside
	PORT_START("controller1_joy_x")
	PORT_BIT( 0x1fe,0x10,IPT_AD_STICK_X)
	PORT_SENSITIVITY(1)
	PORT_KEYDELTA(2000)
	PORT_MINMAX(0,0x1f)
	PORT_CODE_DEC(KEYCODE_LEFT)
	PORT_CODE_INC(KEYCODE_RIGHT)
	PORT_CODE_DEC(JOYCODE_1_LEFT)
	PORT_CODE_INC(JOYCODE_1_RIGHT)
	PORT_PLAYER(1)

	PORT_START("controller1_joy_y")
	PORT_BIT( 0x1fe,0x10,IPT_AD_STICK_Y)
	PORT_SENSITIVITY(1)
	PORT_KEYDELTA(2000)
	PORT_MINMAX(0,0x1f)
	PORT_CODE_DEC(KEYCODE_UP)
	PORT_CODE_INC(KEYCODE_DOWN)
	PORT_CODE_DEC(JOYCODE_1_UP)
	PORT_CODE_INC(JOYCODE_1_DOWN)
	PORT_PLAYER(1)

	PORT_START("controller2_joy_x")
	PORT_BIT( 0x1ff,0x10,IPT_AD_STICK_X)
	PORT_SENSITIVITY(100)
	PORT_KEYDELTA(10)
	PORT_MINMAX(0,0x1f)
	PORT_CODE_DEC(KEYCODE_DEL)
	PORT_CODE_INC(KEYCODE_PGDN)
	PORT_CODE_DEC(JOYCODE_2_LEFT)
	PORT_CODE_INC(JOYCODE_2_RIGHT)
	PORT_PLAYER(2)

	PORT_START("controller2_joy_y")
	PORT_BIT( 0x1ff,0x10,IPT_AD_STICK_Y)
	PORT_SENSITIVITY(100)
	PORT_KEYDELTA(10)
	PORT_MINMAX(0,0x1f)
	PORT_CODE_DEC(KEYCODE_HOME)
	PORT_CODE_INC(KEYCODE_END)
	PORT_CODE_DEC(JOYCODE_2_UP)
	PORT_CODE_INC(JOYCODE_2_DOWN)
	PORT_PLAYER(2)
#else
	PORT_START("joysticks")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2) PORT_8WAY
#endif
INPUT_PORTS_END

/* The Palladium Video-Computer-Game controllers have no fire buttons on their side
   but have a 16 keys unlabeled keypad. The keys are fully independent and layed out
   according to this weird scheme, which keeps backward compatibility to Arcadia 2001's
   electrical wiring.

        2
    13 14 15
     1 16  3
     4  5  6
     7  8  9
    10 11 12                                                                           */

static INPUT_PORTS_START( plldium )
	PORT_START("panel")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START )   PORT_NAME("Start")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_SELECT )  PORT_NAME("Selector A")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )   PORT_NAME("Selector B")    PORT_CODE(KEYCODE_O)
//  PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )    PORT_NAME("Reset")           PORT_CODE(KEYCODE_R)         Not implemented

	PORT_START("controller1_col1")
	PORT_BIT( 0xf0, 0xf0, IPT_UNUSED) // some bits must be high
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 10") PORT_CODE(KEYCODE_MINUS_PAD)

	PORT_START("controller1_col2")
	PORT_BIT( 0xf0, 0xf0, IPT_UNUSED)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 2/Button") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 11") PORT_CODE(KEYCODE_0_PAD)

	PORT_START("controller1_col3")
	PORT_BIT( 0xf0, 0xf0, IPT_UNUSED)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 12") PORT_CODE(KEYCODE_PLUS_PAD)

	PORT_START("controller1_extra")
	PORT_BIT( 0xf0, 0xf0, IPT_UNUSED)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 13") PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 14") PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 15") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 16") PORT_CODE(KEYCODE_DEL_PAD)

	PORT_START("controller2_col1")
	PORT_BIT( 0xf0, 0xf0, IPT_UNUSED)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 1") PORT_CODE(KEYCODE_Z)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 4") PORT_CODE(KEYCODE_A)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 7") PORT_CODE(KEYCODE_Q)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 10") PORT_CODE(KEYCODE_V)

	PORT_START("controller2_col2")
	PORT_BIT( 0xf0, 0xf0, IPT_UNUSED)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 2/Button") PORT_CODE(KEYCODE_X)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 5") PORT_CODE(KEYCODE_S)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 8") PORT_CODE(KEYCODE_W)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 11") PORT_CODE(KEYCODE_F)

	PORT_START("controller2_col3")
	PORT_BIT( 0xf0, 0xf0, IPT_UNUSED)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 3") PORT_CODE(KEYCODE_C)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 6") PORT_CODE(KEYCODE_D)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 9") PORT_CODE(KEYCODE_E)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 12") PORT_CODE(KEYCODE_R)

	PORT_START("controller2_extra")
	PORT_BIT( 0xf0, 0xf0, IPT_UNUSED)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 13") PORT_CODE(KEYCODE_B)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 14") PORT_CODE(KEYCODE_G)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 15") PORT_CODE(KEYCODE_T)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 16") PORT_CODE(KEYCODE_H)

/* FIXME: the joystick are analog - the actual definition is merely an hack */

#if 0
	// auto centering too slow, so only using 5 bits, and scaling at videoside
	PORT_START("controller1_joy_x")
	PORT_BIT( 0x1fe,0x10,IPT_AD_STICK_X)
	PORT_SENSITIVITY(1)
	PORT_KEYDELTA(2000)
	PORT_MINMAX(0,0x1f)
	PORT_CODE_DEC(KEYCODE_LEFT)
	PORT_CODE_INC(KEYCODE_RIGHT)
	PORT_CODE_DEC(JOYCODE_1_LEFT)
	PORT_CODE_INC(JOYCODE_1_RIGHT)
	PORT_PLAYER(1)

	PORT_START("controller1_joy_y")
	PORT_BIT( 0x1fe,0x10,IPT_AD_STICK_Y)
	PORT_SENSITIVITY(1)
	PORT_KEYDELTA(2000)
	PORT_MINMAX(0,0x1f)
	PORT_CODE_DEC(KEYCODE_UP)
	PORT_CODE_INC(KEYCODE_DOWN)
	PORT_CODE_DEC(JOYCODE_1_UP)
	PORT_CODE_INC(JOYCODE_1_DOWN)
	PORT_PLAYER(1)

	PORT_START("controller2_joy_x")
	PORT_BIT( 0x1ff,0x10,IPT_AD_STICK_X)
	PORT_SENSITIVITY(100)
	PORT_KEYDELTA(10)
	PORT_MINMAX(0,0x1f)
	PORT_CODE_DEC(KEYCODE_DEL)
	PORT_CODE_INC(KEYCODE_PGDN)
	PORT_CODE_DEC(JOYCODE_2_LEFT)
	PORT_CODE_INC(JOYCODE_2_RIGHT)
	PORT_PLAYER(2)

	PORT_START("controller2_joy_y")
	PORT_BIT( 0x1ff,0x10,IPT_AD_STICK_Y)
	PORT_SENSITIVITY(100)
	PORT_KEYDELTA(10)
	PORT_MINMAX(0,0x1f)
	PORT_CODE_DEC(KEYCODE_HOME)
	PORT_CODE_INC(KEYCODE_END)
	PORT_CODE_DEC(JOYCODE_2_UP)
	PORT_CODE_INC(JOYCODE_2_DOWN)
	PORT_PLAYER(2)
#else
	PORT_START("joysticks")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )     PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )    PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )     PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )       PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )     PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )    PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )     PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )       PORT_PLAYER(2) PORT_8WAY
#endif
INPUT_PORTS_END

static const gfx_layout arcadia_charlayout =
{
	8,                      /*width*/
	1,                      /*height*/
	256,                    /* 256 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes; 1 bit per pixel */
	{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* x offsets */
	{ 0 },                  /* y offsets */
	1*8
};

static GFXDECODE_START( gfx_arcadia )
	GFXDECODE_ENTRY( "gfx1", 0x0000, arcadia_charlayout, 0, 68 )
GFXDECODE_END

static const rgb_t arcadia_colors[] =
{
	rgb_t::white(),                /* white */
	rgb_t(0xff, 0xff, 0x00), /* yellow */
	rgb_t(0x00, 0xff, 0xff), /* cyan */
	rgb_t(0x00, 0xff, 0x00), /* green */
	rgb_t(0xff, 0x00, 0xff), /* magenta */
	rgb_t(0xff, 0x00, 0x00), /* red */
	rgb_t(0x00, 0x00, 0xff), /* blue */
	rgb_t::black()                 /* black */
};

static const unsigned short arcadia_palette[128+8] =  /* bgnd, fgnd */
{
	0,1,2,3,4,5,6,7,

	0,0, 0,1, 0,2, 0,3, 0,4, 0,5, 0,6, 0,7,
	1,0, 1,1, 1,2, 1,3, 1,4, 1,5, 1,6, 1,7,
	2,0, 2,1, 2,2, 2,3, 2,4, 2,5, 2,6, 2,7,
	3,0, 3,1, 3,2, 3,3, 3,4, 3,5, 3,6, 3,7,
	4,0, 4,1, 4,2, 4,3, 4,4, 4,5, 4,6, 4,7,
	5,0, 5,1, 5,2, 5,3, 5,4, 5,5, 5,6, 5,7,
	6,0, 6,1, 6,2, 6,3, 6,4, 6,5, 6,6, 6,7,
	7,0, 7,1, 7,2, 7,3, 7,4, 7,5, 7,6, 7,7
};

void arcadia_state::palette_init(palette_device &palette) const
{
	for (int i = 0; i < 8; i++)
		palette.set_indirect_color(i, arcadia_colors[i]);

	for (int i = 0; i < 128+8; i++)
		palette.set_pen_indirect(i, arcadia_palette[i]);
}

void arcadia_state::machine_start()
{
	if (m_cart->exists())
	{
		switch (m_cart->get_type())
		{
		case ARCADIA_STD:
			m_maincpu->space(AS_PROGRAM).install_read_handler(0x2000, 0x7fff, read8sm_delegate(*m_cart, FUNC(arcadia_cart_slot_device::extra_rom)));
			break;
		case ARCADIA_GOLF:
			m_maincpu->space(AS_PROGRAM).install_read_handler(0x4000, 0x4fff, read8sm_delegate(*m_cart, FUNC(arcadia_cart_slot_device::extra_rom)));
			break;
		}
	}
}

static void arcadia_cart(device_slot_interface &device)
{
	device.option_add_internal("std",      ARCADIA_ROM_STD);
	device.option_add_internal("golf",     ARCADIA_ROM_GOLF);
}


void arcadia_state::arcadia(machine_config &config)
{
	/* basic machine hardware */
	S2650(config, m_maincpu, 3580000/4); /* 0.895 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &arcadia_state::arcadia_mem);
	m_maincpu->sense_handler().set(FUNC(arcadia_state::vsync_r));
	m_maincpu->set_periodic_int(FUNC(arcadia_state::video_line), attotime::from_hz(262*60));

	config.set_maximum_quantum(attotime::from_hz(60));

	/* 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(128+2*XPOS, 262);
	m_screen->set_visarea(0, 2*XPOS+128-1, 0, 262-1);
	m_screen->set_screen_update(FUNC(arcadia_state::screen_update_arcadia));
	m_screen->set_palette(m_palette);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_arcadia);
	PALETTE(config, m_palette, FUNC(arcadia_state::palette_init), std::size(arcadia_palette), 8);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	ARCADIA_SOUND(config, m_custom).add_route(ALL_OUTPUTS, "mono", 1.00);

	/* cartridge */
	EA2001_CART_SLOT(config, "cartslot", arcadia_cart, nullptr).set_must_be_loaded(true);

	/* Software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("arcadia");
}


ROM_START(advsnha)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(bndarc)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(arcadia)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(tccosmos)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(dynavisn)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(ekusera)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(giglnrdo)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(hanihac)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(hmg2650)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(intmpt03)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(ixl2000)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(intervsn)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(itmcmtp3)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(lvision)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(mratlus)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(ormatu)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(plldium)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(polyvcg)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(poppympt)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(prestmpt)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(rowtrn2k)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(tvg2000)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(sheenhvc)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(soundic)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(telefevr)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(tempestm)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(tbbympt3)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(trakcvg)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(tunixha)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(tryomvgc)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(orbituvi)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END

ROM_START(vdmaster)
	ROM_REGION(0x8000,"maincpu", ROMREGION_ERASEFF)
	ROM_REGION(0x100,"gfx1", ROMREGION_ERASEFF)
ROM_END


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

  Game driver(s)

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

void arcadia_state::init_arcadia()
{
	uint8_t *gfx=memregion("gfx1")->base();
	for (int i = 0; i < 256; i++) gfx[i]=i;
#if 0
	// this is here to allow developement of some simple testroutines
	// for a real console
	{
		uint8_t *rom=memregion("maincpu")->base();
		/* this is a simple routine to display all rom characters
		   on the display for a snapshot */
		static const uint8_t prog[]={ // address 0 of course
		0x20, // eorz, 0
		0x1b, 0x01, // bctr,a $0004
		0x17, // retc a
		0x76, 0x20, // ppsu ii

		// fill screen
		0x04, 0x00, // lodi,0 0
		0x04|1, 0x00, // lodi,1 0
		0xcc|1, 0x78, 0x10, //a: stra,0 $1800,r1
		0x75,9, //cpsl wc|c
		0x84,0x41, // addi,0 0x41
		0x75,9, //cpsl wc|c
		0x84|1, 0x01, // addi,1 1
		0xe4|1, 0x40, // comi,1 40
		0x98, 0x80-15, // bcfr,0 a

		0x04, 0xff, // lodi,0 7
		0xcc, 0x18, 0xfc, // stra,0 $19f8
		0x04, 0x00, // lodi,0 7
		0xcc, 0x18, 0xfd, // stra,0 $18fd
		0x04, 0x07, // lodi,0 7
		0xcc, 0x19, 0xf9, // stra,0 $19f9

		0x04, 0x00, // lodi,0 7
		0xcc, 0x19, 0xbe, // stra,0 $19bf
		0x04, 0x00, // lodi,0 7
		0xcc, 0x19, 0xbf, // stra,0 $19bf

		//loop: 0x0021
		// print keyboards
		0x04|1, 0x00, //y:lodi,1 0
		0x0c|1, 0x79, 0x00, //x: ldra,0 1900,r1
		0x44|0, 0x0f, //andi,0 0f
		0x64|0, 0x10, //ori,0  10
		0xcc|1, 0x78, 0x01, //stra,0 1840,r1
		0x75,9, //cpsl wc|c
		0x84|1, 0x01, //addi,1 1
		0xe4|1, 0x09, //comi,1 9
		0x98, 0x80-18, //bcfr,0 x

		// cycle colors
		0x0c|1, 0x19, 0x00, //ldra,1 1900
		0x44|1, 0xf, //andi,0 0f
		0xe4|1, 1, //comi,1 1
		0x98, +10, //bcfr,0 c
		0x0c, 0x19, 0xbf,//ldra,0 19f9,0
		0x84, 1, //addi,0 1
		0xcc, 0x19, 0xbf, //stra,0 19f9,0
		0x18|3, 12, // bctr,a
		0xe4|1, 2, //c:comi,1 2
		0x98, +10, //bcfr,0 d
		0x0c, 0x19, 0xbf, //ldra,0 19f9,0
		0x84, 8, //addi,0 8
		0xcc, 0x19, 0xbf, //stra,0 19f9,0
		0x18|3, 12, // bctr,a

		// cycle colors
		0xe4|1, 4, //comi,1 4
		0x98, +10, //bcfr,0 c
		0x0c, 0x19, 0xbe,//ldra,0 19f9,0
		0x84, 1, //addi,0 1
		0xcc, 0x19, 0xbe, //stra,0 19f9,0
		0x18|3, 12, // bctr,a
		0xe4|1, 8, //c:comi,1 2
		0x98, +8+9, //bcfr,0 d
		0x0c, 0x19, 0xbe, //ldra,0 19f9,0
		0x84, 8, //addi,0 8
		0xcc, 0x19, 0xbe, //stra,0 19f9,0

		0x0c, 0x19, 0x00, //b: ldra,0 1900
		0x44|0, 0xf, //andi,0 0f
		0xe4, 0, //comi,0 0
		0x98, 0x80-9, //bcfr,0 b

		0x0c, 0x19, 0xbe, //ldra,0 19bf
		0xcc, 0x19, 0xf8, //stra,0 19f8
		0x0c, 0x19, 0xbf, //ldra,0 19bf
		0xcc, 0x19, 0xf9, //stra,0 19f8

		0x0c, 0x19, 0xbe, //ldra,0 17ff
		0x44|0, 0xf, //andi,0 7
		0x64|0, 0x10, //ori,0  10
		0xcc, 0x18, 0x0d, //stra,0 180f
		0x0c, 0x19, 0xbe, //x: ldra,0 19bf
		0x50, 0x50, 0x50, 0x50, //shr,0 4
		0x44|0, 0xf, //andi,0 7
		0x64|0, 0x10, //ori,0  10
		0xcc, 0x18, 0x0c, //stra,0 180e

		0x0c, 0x19, 0xbf, //ldra,0 17ff
		0x44|0, 0xf, //andi,0 7
		0x64|0, 0x10, //ori,0  10
		0xcc, 0x18, 0x0f, //stra,0 180f
		0x0c, 0x19, 0xbf, //x: ldra,0 19bf
		0x50, 0x50, 0x50, 0x50, //shr,0 4
		0x44|0, 0xf, //andi,0 7
		0x64|0, 0x10, //ori,0  10
		0xcc, 0x18, 0x0e, //stra,0 180e

		0x0c, 0x18, 0x00, //ldra,0 1800
		0x84, 1, //addi,0 1
		0xcc, 0x18, 0x00, //stra,0 1800

//      0x1b, 0x80-20-29-26-9-8-2 // bctr,a y
		0x1c|3, 0, 0x32, // bcta,3 loop

		// calling too many subdirectories causes cpu to reset!
		// bxa causes trap
		};
#if 1
		FILE *f = fopen("chartest.bin","wb");
		fwrite(prog, std::size(prog), sizeof(prog[0]), f);
		fclose(f);
#endif
		for (int i = 0; i < std::size(prog); i++) rom[i] = prog[i];

	}
#endif
}


/*   YEAR  NAME      PARENT    COMPAT    MACHINE  INPUT    CLASS          INIT          COMPANY               FULLNAME */
CONS(1983, advsnha,  arcadia,  0,        arcadia, arcadia, arcadia_state, init_arcadia, "Advision",           "Advision Home Arcade", MACHINE_IMPERFECT_SOUND )    /* France */
CONS(1982, bndarc,   arcadia,  0,        arcadia, arcadia, arcadia_state, init_arcadia, "Bandai",             "Arcadia (Bandai)", MACHINE_IMPERFECT_SOUND )        /* Japan */
CONS(1982, arcadia,  0,        0,        arcadia, arcadia, arcadia_state, init_arcadia, "Emerson",            "Arcadia 2001", MACHINE_IMPERFECT_SOUND )            /* U.S.A. */
CONS(198?, tccosmos, arcadia,  0,        arcadia, arcadia, arcadia_state, init_arcadia, "Mobilar?",           "Tele-Computer Cosmos", MACHINE_IMPERFECT_SOUND )    /* Spain? I have only found pictures of a German Cosmos ( http://www.pong-picture-page.de/catalog/product_info.php?products_id=2170 ) */
CONS(1982, dynavisn, intmpt03, 0,        arcadia, arcadia, arcadia_state, init_arcadia, "Yamagiwa",           "Dynavision", MACHINE_IMPERFECT_SOUND )              /* Japan */
CONS(1982, ekusera,  intmpt03, 0,        arcadia, arcadia, arcadia_state, init_arcadia, "P.I.C",              "Ekusera", MACHINE_IMPERFECT_SOUND )                 /* Japan */
CONS(1982, giglnrdo, arcadia,  0,        arcadia, arcadia, arcadia_state, init_arcadia, "GiG Electronics",    "Leonardo (GiG Electronics)", MACHINE_IMPERFECT_SOUND ) /* Italy */
CONS(1982, hanihac,  arcadia,  0,        arcadia, arcadia, arcadia_state, init_arcadia, "Hanimex",            "Hanimex Home Arcade Centre", MACHINE_IMPERFECT_SOUND )  /* UK */
CONS(1982, hmg2650,  arcadia,  0,        arcadia, arcadia, arcadia_state, init_arcadia, "Hanimex",            "HMG-2650", MACHINE_IMPERFECT_SOUND )                 /* Germany */
CONS(198?, intmpt03, 0,        arcadia,  arcadia, arcadia, arcadia_state, init_arcadia, "Intelligent Game",   "Intelligent Game MPT-03", MACHINE_IMPERFECT_SOUND )  /* U.S.A */
CONS(198?, ixl2000,  arcadia,  0,        arcadia, arcadia, arcadia_state, init_arcadia, "Intercord",          "Intercord XL 2000 System", MACHINE_IMPERFECT_SOUND ) /* Germany */
CONS(198?, intervsn, ormatu,   0,        arcadia, arcadia, arcadia_state, init_arcadia, "Intervision",        "Intervision 2001", MACHINE_IMPERFECT_SOUND )         /* Switzerland */
CONS(198?, itmcmtp3, intmpt03, 0,        arcadia, arcadia, arcadia_state, init_arcadia, "ITMC",               "ITMC MPT-03", MACHINE_IMPERFECT_SOUND )              /* France */
CONS(1982, lvision,  arcadia,  0,        arcadia, arcadia, arcadia_state, init_arcadia, "Leisure-Dynamics",   "Leisure-Vision", MACHINE_IMPERFECT_SOUND )           /* Canada */
CONS(1983, mratlus,  plldium,  0,        arcadia, arcadia, arcadia_state, init_arcadia, "H.G.S.",             "Mr. Altus Tele Brain", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING )    /* Germany */
CONS(198?, ormatu,   0,        arcadia,  arcadia, arcadia, arcadia_state, init_arcadia, "Ormatu Electronics", "Ormatu 2001", MACHINE_IMPERFECT_SOUND )              /* Netherlands */
CONS(198?, plldium,  0,        arcadia,  arcadia, plldium, arcadia_state, init_arcadia, "Neckermann",         "Palladium Video-Computer-Game", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING )      /* Germany, 16 keys instead of 12 */
CONS(1983, polyvcg,  plldium,  0,        arcadia, arcadia, arcadia_state, init_arcadia, "Polybrain",          "Polybrain Video Computer Game", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING )      /* Germany */
CONS(198?, poppympt, intmpt03, 0,        arcadia, arcadia, arcadia_state, init_arcadia, "Transonic",          "Poppy MPT-03 Tele Computer Spiel", MACHINE_IMPERFECT_SOUND )           /* Germany */
CONS(198?, prestmpt, intmpt03, 0,        arcadia, arcadia, arcadia_state, init_arcadia, "Prestige",           "Prestige Video Computer Game MPT-03", MACHINE_IMPERFECT_SOUND )        /* France */
CONS(198?, rowtrn2k, intmpt03, 0,        arcadia, arcadia, arcadia_state, init_arcadia, "Rowtron",            "Rowtron 2000", MACHINE_IMPERFECT_SOUND )               /* UK */
CONS(1982, tvg2000,  arcadia,  0,        arcadia, arcadia, arcadia_state, init_arcadia, "Schmid",             "Schmid TVG 2000", MACHINE_IMPERFECT_SOUND )            /* Germany */
CONS(198?, sheenhvc, ormatu,   0,        arcadia, arcadia, arcadia_state, init_arcadia, "Sheen",              "Sheen Home Video Centre 2001", MACHINE_IMPERFECT_SOUND )     /* Australia */
CONS(198?, soundic,  intmpt03, 0,        arcadia, arcadia, arcadia_state, init_arcadia, "Soundic",            "Soundic MPT-03", MACHINE_IMPERFECT_SOUND )             /* Finland */
CONS(198?, telefevr, arcadia,  0,        arcadia, arcadia, arcadia_state, init_arcadia, "Tchibo",             "Tele-Fever", MACHINE_IMPERFECT_SOUND )                 /* Germany */
CONS(198?, tempestm, intmpt03, 0,        arcadia, arcadia, arcadia_state, init_arcadia, "Tempest",            "Tempest MPT-03", MACHINE_IMPERFECT_SOUND )             /* Australia */
CONS(198?, tbbympt3, intmpt03, 0,        arcadia, arcadia, arcadia_state, init_arcadia, "Tobby",              "Tobby MPT-03", MACHINE_IMPERFECT_SOUND )               /* ? */
CONS(198?, trakcvg,  plldium,  0,        arcadia, arcadia, arcadia_state, init_arcadia, "Trakton",            "Trakton Computer Video Game", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING )      /* Australia */
CONS(1982, tunixha,  arcadia,  0,        arcadia, arcadia, arcadia_state, init_arcadia, "Monaco Leisure",     "Tunix Home Arcade", MACHINE_IMPERFECT_SOUND )          /* New Zealand */
CONS(198?, tryomvgc, intmpt03, 0,        arcadia, arcadia, arcadia_state, init_arcadia, "Tryom",              "Tryom Video Game Center", MACHINE_IMPERFECT_SOUND )    /* U.S.A */
CONS(198?, orbituvi, 0,        arcadia,  arcadia, arcadia, arcadia_state, init_arcadia, "Orbit Electronics",  "UVI Compu-Game", MACHINE_IMPERFECT_SOUND )             /* New Zealand */
CONS(198?, vdmaster, orbituvi, 0,        arcadia, arcadia, arcadia_state, init_arcadia, "Grandstand",         "Video Master", MACHINE_IMPERFECT_SOUND )               /* New Zealand */



argo.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************************

Argo

2011-03-16 Skeleton driver.

There are no manuals, diagrams, or anything else available afaik.
The entire driver is guesswork, however it appears to be related to UNIOR.

The monitor will only allow certain characters to be typed, thus the
modifier keys appear to do nothing. There is no need to use the enter
key; using spacebar and the correct parameters is enough.

Commands: same as UNIOR

ToDo:
- Add remaining devices, most likely 8255.
- There is no obvious evidence of sound.
- Need dump of the correct chargen.
- Find out if "m_eram" is supposed to exist, if so, what is it for?
- What character should show in the top left corner?
- our dma doesn't emulate the update_flag, so had to use a hack for the L command
- cursor is one character further to the right than it should be, used another hack

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

#include "emu.h"

#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "machine/i8251.h"
#include "machine/i8257.h"
#include "machine/pit8253.h"
#include "machine/timer.h"
//#include "sound/spkrdev.h"
#include "video/i8275.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class argo_state : public driver_device
{
public:
	argo_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_p_chargen(*this, "chargen")
		, m_uart(*this, "uart")
		, m_pit(*this, "pit")
		, m_dma(*this, "dma")
		, m_crtc(*this, "crtc")
		, m_cass(*this, "cassette")
		, m_palette(*this, "palette")
		, m_io_keyboard(*this, "X%d", 0U)
	{ }

	void argo(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	void argo_videoram_w(offs_t offset, u8 data);
	u8 argo_io_r(offs_t offset);
	void argo_io_w(offs_t offset, u8 data);
	void z0_w(int state);
	void hrq_w(int state);
	void argo_palette(palette_device &palette) const;
	u8 dma_r(offs_t offset);
	I8275_DRAW_CHARACTER_MEMBER(display_pixels);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	memory_passthrough_handler m_rom_shadow_tap;
	required_device<z80_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_region_ptr<u8> m_p_chargen;
	required_device<i8251_device> m_uart;
	required_device<pit8253_device> m_pit;
	required_device<i8257_device> m_dma;
	required_device<i8275_device> m_crtc;
	required_device<cassette_image_device> m_cass;
	required_device<palette_device> m_palette;
	required_ioport_array<11> m_io_keyboard;
	u8 m_framecnt = 0U;
	bool m_ram_ctrl = 0;
	u8 m_scroll_ctrl = 0U;
	bool m_txe = 0, m_txd = 0, m_rts = 0, m_casspol = 0;
	u8 m_cass_data[4]{};
	std::unique_ptr<u8[]> m_vram;
	std::unique_ptr<u8[]> m_eram;
};

// write to videoram if following 'out b9,61' otherwise write to the unknown 'extra' ram
void argo_state::argo_videoram_w(offs_t offset, u8 data)
{
	if (m_ram_ctrl)
		m_vram[offset] = data;
	else
		m_eram[offset] = data;
}

u8 argo_state::argo_io_r(offs_t offset)
{
	u8 low_io = offset;

	switch (low_io)
	{
	case 0xA1: // keyboard
		offset >>= 8;
		if (offset < 11)
			return m_io_keyboard[offset]->read();
		else
			return 0xff;

	case 0xA0:
	case 0xA2:
	case 0xA4:
	case 0xA6:
		offset >>= 1;
		return m_pit->read(offset&3);

	case 0xC0:
	case 0xC4:
		offset >>= 2;
		return m_crtc->read(offset&1);

	case 0xE8: // wants bit 4 low then high
		{
			u8 data = m_dma->read(8);
			data |= (m_framecnt << 4);  // hack because dma update_flag is not emulated
			return data;
		}

	default:
		logerror("%s: In %X\n",machine().describe_context(),low_io);
		return 0xff;
	}
}

void argo_state::argo_io_w(offs_t offset, u8 data)
{
	switch (offset)
	{
	case 0xA0:
	case 0xA2:
	case 0xA4:
	case 0xA6:
		offset >>= 1;
		m_pit->write(offset&3, data);
		break;

	case 0xA1: // prep scroll step 1
		m_scroll_ctrl = (data == 0x61);
		break;

	case 0xA9: // prep scroll step 2
		if ((m_scroll_ctrl == 1) && (data == 0x61))
			m_scroll_ctrl++;
		break;

	case 0xB9: // switch between videoram and extraram
		m_ram_ctrl = (data == 0x61);
		break;

	case 0xC0:
	case 0xC4:
		offset >>= 2;
		m_crtc->write(offset&1, data);
		break;

	case 0xE8: // hardware scroll
		if ((m_scroll_ctrl == 2) & (data == 0xe3))
		{
			memmove(m_vram.get(), m_vram.get()+80, 24*80);
			m_scroll_ctrl = 0;
		}
		m_dma->write(8, data);
		break;

	default:
		logerror("%s: Out %X,%X\n",machine().describe_context(),offset,data);
	}
}

TIMER_DEVICE_CALLBACK_MEMBER( argo_state::kansas_r )
{
	if (m_rts)
	{
		m_cass_data[1] = m_cass_data[2] = m_cass_data[3] = 0;
		m_casspol = 1;
		return;
	}

	m_cass_data[1]++;
	m_cass_data[2]++;

	u8 cass_ws = (m_cass->input() > +0.04) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		if (m_cass_data[1] > 13)
			m_casspol ^= 1;
		m_cass_data[1] = 0;
		m_cass_data[2] = 0;
		m_uart->write_rxd(m_casspol);
	}
	if ((m_cass_data[2] & 7)==2)
	{
		m_cass_data[3]++;
		m_uart->write_rxc(BIT(m_cass_data[3], 0));
	}
}

void argo_state::z0_w(int state)
{
	// write - incoming 2400Hz
	m_uart->write_txc(state);
	if (!m_txe)
	{
		m_cass->output((m_txd ^ state) ? -1.0 : 1.0);
	}

	// read - incoming 2514Hz
}

u8 argo_state::dma_r(offs_t offset)
{
	if (offset < 0xf800)
		return m_maincpu->space(AS_PROGRAM).read_byte(offset);
	else
		return m_vram[offset & 0x7ff];
}

void argo_state::hrq_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state);
	m_dma->hlda_w(state);
}

I8275_DRAW_CHARACTER_MEMBER(argo_state::display_pixels)
{
	if ((x == 0) && (y == 0))
		m_framecnt++;

	const rgb_t *palette = m_palette->palette()->entry_list_raw();
	u8 gfx = m_p_chargen[(linecount & 15) | (charcode << 4)];

	using namespace i8275_attributes;

	if (BIT(attrcode, VSP))
		gfx = 0;

	if (BIT(attrcode, LTEN))
	{
		gfx = 0xff;
		if (x > 6)
			x-=6; // hack to fix cursor position
	}

	if (BIT(attrcode, RVV))
		gfx ^= 0xff;

	bool hlgt = BIT(attrcode, HLGT);
	for(u8 i=0;i<7;i++)
		bitmap.pix(y, x + i) = palette[BIT(gfx, 6-i) ? (hlgt ? 2 : 1) : 0];
}

static constexpr rgb_t argo_pens[3] =
{
	{ 0x00, 0x00, 0x00 }, // black
	{ 0xa0, 0xa0, 0xa0 }, // white
	{ 0xff, 0xff, 0xff }  // highlight
};

void argo_state::argo_palette(palette_device &palette) const
{
	palette.set_pen_colors(0, argo_pens);
}


/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 9,                   /* 8 x 9 characters */
	512,                    /* 512 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_argo )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END

void argo_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xf7ff).ram().share("mainram");
	map(0xf800, 0xffff).rom().region("maincpu", 0).w(FUNC(argo_state::argo_videoram_w));
}

void argo_state::io_map(address_map &map)
{
	map(0x0000, 0xFFFF).r(FUNC(argo_state::argo_io_r));
	map(0x0000, 0x00ff).mirror(0xff00).w(FUNC(argo_state::argo_io_w));
	map(0x00c1, 0x00c1).mirror(0xff00).rw(m_uart, FUNC(i8251_device::data_r), FUNC(i8251_device::data_w));
	map(0x00c3, 0x00c3).mirror(0xff00).rw(m_uart, FUNC(i8251_device::status_r), FUNC(i8251_device::control_w));
	map(0x00e0, 0x00e7).mirror(0xff00).rw(m_dma, FUNC(i8257_device::read), FUNC(i8257_device::write));
}

/* Input ports */
static INPUT_PORTS_START( argo ) // Keyboard was worked out by trial & error;'F' keys produce symbols
	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-")           PORT_CODE(KEYCODE_MINUS_PAD)  PORT_CHAR('-')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10) // J
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED) // nothing
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED) // nothing
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LShift")      PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7")           PORT_CODE(KEYCODE_7_PAD)      PORT_CHAR('7')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8")           PORT_CODE(KEYCODE_8_PAD)      PORT_CHAR('8')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9")           PORT_CODE(KEYCODE_9_PAD)      PORT_CHAR('9')
	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc")         PORT_CODE(KEYCODE_ESC)        PORT_CHAR(27)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab")         PORT_CODE(KEYCODE_TAB)        PORT_CHAR(9)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl")        PORT_CODE(KEYCODE_LCONTROL)   PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RShift")      PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("??")          PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0")           PORT_CODE(KEYCODE_0_PAD)      PORT_CHAR('0')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".")           PORT_CODE(KEYCODE_DEL_PAD)    PORT_CHAR('.')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("+")           PORT_CODE(KEYCODE_PLUS_PAD)   PORT_CHAR('+')
	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1")           PORT_CODE(KEYCODE_1)          PORT_CHAR('1')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q")           PORT_CODE(KEYCODE_Q)          PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A")           PORT_CODE(KEYCODE_A)          PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z")           PORT_CODE(KEYCODE_Z)          PORT_CHAR('Z')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("??")          PORT_CODE(KEYCODE_F12)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1")           PORT_CODE(KEYCODE_1_PAD)      PORT_CHAR('1')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2")           PORT_CODE(KEYCODE_2_PAD)      PORT_CHAR('2')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3")           PORT_CODE(KEYCODE_3_PAD)      PORT_CHAR('3')
	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) // A
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2")           PORT_CODE(KEYCODE_2)          PORT_CHAR('2')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W")           PORT_CODE(KEYCODE_W)          PORT_CHAR('W')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S")           PORT_CODE(KEYCODE_S)          PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X")           PORT_CODE(KEYCODE_X)          PORT_CHAR('X')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4")           PORT_CODE(KEYCODE_4_PAD)      PORT_CHAR('4')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5")           PORT_CODE(KEYCODE_5_PAD)      PORT_CHAR('5')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6")           PORT_CODE(KEYCODE_6_PAD)      PORT_CHAR('6')
	PORT_START("X4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) // B
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3")           PORT_CODE(KEYCODE_3)          PORT_CHAR('3')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E")           PORT_CODE(KEYCODE_E)          PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D")           PORT_CODE(KEYCODE_D)          PORT_CHAR('M')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C")           PORT_CODE(KEYCODE_C)          PORT_CHAR('C')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11) // K
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) // H
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9) // I
	PORT_START("X5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) // C
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4")           PORT_CODE(KEYCODE_4)          PORT_CHAR('4')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R")           PORT_CODE(KEYCODE_R)          PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F")           PORT_CODE(KEYCODE_F)          PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V")           PORT_CODE(KEYCODE_V)          PORT_CHAR('V')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BS")          PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(8)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) // G
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) // F
	PORT_START("X6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5")           PORT_CODE(KEYCODE_5)          PORT_CHAR('5')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T")           PORT_CODE(KEYCODE_T)          PORT_CHAR('T')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G")           PORT_CODE(KEYCODE_G)          PORT_CHAR('G')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B")           PORT_CODE(KEYCODE_B)          PORT_CHAR('B')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space")       PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter")       PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("*")           PORT_CODE(KEYCODE_ASTERISK)   PORT_CHAR('*')
	PORT_START("X7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) // D
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6")           PORT_CODE(KEYCODE_6)          PORT_CHAR('6')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y")           PORT_CODE(KEYCODE_Y)          PORT_CHAR('Y')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H")           PORT_CODE(KEYCODE_H)          PORT_CHAR('H')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N")           PORT_CODE(KEYCODE_N)          PORT_CHAR('N')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\")          PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('\\')
	PORT_START("X8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) // E
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7")           PORT_CODE(KEYCODE_7)          PORT_CHAR('7')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U")           PORT_CODE(KEYCODE_U)          PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J")           PORT_CODE(KEYCODE_J)          PORT_CHAR('J')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M")           PORT_CODE(KEYCODE_M)          PORT_CHAR('M')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("`")           PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('`')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]")           PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("=")           PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('=')
	PORT_START("X9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8")           PORT_CODE(KEYCODE_8)          PORT_CHAR('8')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I")           PORT_CODE(KEYCODE_I)          PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K")           PORT_CODE(KEYCODE_K)          PORT_CHAR('K')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",")           PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\'")          PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR('\'')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[")           PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-")           PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-')
	PORT_START("X10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9")           PORT_CODE(KEYCODE_9)          PORT_CHAR('9')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O")           PORT_CODE(KEYCODE_O)          PORT_CHAR('O')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L")           PORT_CODE(KEYCODE_L)          PORT_CHAR('L') // if L is the first character, computer hangs
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".")           PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/")           PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";")           PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P")           PORT_CODE(KEYCODE_P)          PORT_CHAR('P')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0")           PORT_CODE(KEYCODE_0)          PORT_CHAR('0')
INPUT_PORTS_END



void argo_state::machine_reset()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x0000, 0x07ff, m_rom);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0xf800, 0xffff,
			"rom_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// reinstall RAM over the ROM shadow
					m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x07ff, m_ram);
				}
			},
			&m_rom_shadow_tap);
}

void argo_state::machine_start()
{
	m_vram = make_unique_clear<u8[]>(0x0800);
	m_eram = make_unique_clear<u8[]>(0x0800);
	save_pointer(NAME(m_vram), 0x0800);
	save_pointer(NAME(m_eram), 0x0800);
	save_item(NAME(m_framecnt));
	save_item(NAME(m_ram_ctrl));
	save_item(NAME(m_scroll_ctrl));
	save_item(NAME(m_txe));
	save_item(NAME(m_txd));
	save_item(NAME(m_rts));
	save_item(NAME(m_casspol));
	save_item(NAME(m_cass_data));
}


void argo_state::argo(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 3500000); // unknown frequency
	m_maincpu->set_addrmap(AS_PROGRAM, &argo_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &argo_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_size(640, 200);
	screen.set_visarea_full();
	screen.set_screen_update("crtc", FUNC(i8275_device::screen_update));
	GFXDECODE(config, "gfxdecode", m_palette, gfx_argo);
	PALETTE(config, m_palette, FUNC(argo_state::argo_palette), 3);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	//SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.15);
	TIMER(config, "kansas_r").configure_periodic(FUNC(argo_state::kansas_r), attotime::from_hz(38400));

	/* Devices */
	I8251(config, m_uart, 0);
	m_uart->txd_handler().set([this] (bool state) { m_txd = state; });
	m_uart->txempty_handler().set([this] (bool state) { m_txe = state; });
	m_uart->rts_handler().set([this] (bool state) { m_rts = state; });

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(1689600); // this gives the 2400Hz required by the cassette
	m_pit->out_handler<0>().set(FUNC(argo_state::z0_w));
	m_pit->set_clk<1>(0);
	m_pit->set_clk<2>(0);

	I8257(config, m_dma, XTAL(20'000'000) / 9);  // unknown frequency
	m_dma->out_hrq_cb().set(FUNC(argo_state::hrq_w));
	m_dma->in_memr_cb().set(FUNC(argo_state::dma_r));
	m_dma->out_iow_cb<2>().set("crtc", FUNC(i8275_device::dack_w));

	i8275_device &crtc(I8275(config, "crtc", XTAL(20'000'000) / 12));  // unknown frequency
	crtc.set_character_width(6);
	crtc.set_display_callback(FUNC(argo_state::display_pixels));
	crtc.drq_wr_callback().set(m_dma, FUNC(i8257_device::dreq2_w));
	crtc.set_screen("screen");
}

/* ROM definition */
ROM_START( argo )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "argo.rom", 0x0000, 0x0800, CRC(4c4c045b) SHA1(be2b97728cc190d4a8bd27262ba9423f252d31a3) )

	/* character generator not dumped, using the one from 'c10' for now */
	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "c10_char.bin", 0x0000, 0x2000, BAD_DUMP CRC(cb530b6f) SHA1(95590bbb433db9c4317f535723b29516b9b9fcbf))
ROM_END

} // anonymous namespace

/* Driver */

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT         COMPANY      FULLNAME  FLAGS */
COMP( 1986, argo, unior,  0,      argo,    argo,  argo_state, empty_init, "<unknown>", "Argo",   MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



argox.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Felipe Sanches
/***************************************************************************

  Argox Rabbit Printer
  model: 0S-214

  Skeleton driver by Felipe Correa da Silva Sanches <juca@members.fsf.org>

  OVERALL HARDWARE DESCRIPTION:

  There's a sticker labeled "V4.21"
  The board is labeled "ARGOX INFORMATION 48.20401.002 DATE:2003/03/20 REV:4.2"

  There's a soldered IC at U10 which is labeled "A511 093060006 55-20401-003 E" and "OK"
  I guess it may be another flash ROM

  External interfaces:
  * RS232 serial interface
  * Centronics port

  Connectors:
  * 4-pin labeled "PEELER" (unused)
  * 4-pin labeled "RIBBON"
  * 4-pin labeled "MEDIA"
  * 4-pin labeled "MOTOR"
  * 6-pin labeled "MOTOR" (unpopulated)
  * 4-pin labeled "CUTTER" (unused)
  * 18-pin unlabeled JP18 (with 4 unused pins) Connects to the printing subassembly
  * 9-pin labeled "KEYPAD" (unpopulated)
  * 6-pin labeled "LED/KEY" (connects to FEED button, POWER LED and READY LED)

  Jumpers:
  * set of 2 jumpers (JP1 and JP2) with a jumper inserted at JP2
  * set of 6 unlabelled with jumpers inserted at position #0 and #3

  DIP sockets:
  * u8 and u9 hold the FLASH ROM chips
  * U19 is an unpopulated DIP16 socket

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

#include "emu.h"
#include "cpu/h8/h83002.h"


namespace {

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

	void os214(machine_config &config);

	void init_os214();

private:
	required_device<cpu_device> m_maincpu;
	void os214_prg_map(address_map &map) ATTR_COLD;
};

void os214_state::os214_prg_map(address_map &map)
{
	map(0x000000, 0x07ffff).rom();
}

void os214_state::os214(machine_config &config)
{
	/* basic machine hardware */
	H83002(config, m_maincpu, XTAL(16'000'000)); /* X1 xtal value is correct,
	                                                   but there can be some clock divider perhaps ? */
	m_maincpu->set_addrmap(AS_PROGRAM, &os214_state::os214_prg_map);
}

void os214_state::init_os214()
{
}

ROM_START( os214 )
	ROM_REGION( 0x080000, "maincpu", 0 )
	ROM_LOAD16_BYTE( "u9_s2a2-4.03_argox_am.u9", 0x000000, 0x040000, CRC(3bd8b2b1) SHA1(546f9fd8d7e1f589f6e594a332a3429041b49eea) )
	ROM_LOAD16_BYTE( "u8_s2a2-4.03_argox_am.u8", 0x000001, 0x040000, CRC(d49f52af) SHA1(0ca5a70c6c3995f275226af26db965f6ba7ed123) )
ROM_END

} // anonymous namespace


//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME                         FLAGS
COMP( 1996, os214, 0,      0,      os214,   0,     os214_state, init_os214, "Argox", "Rabbit Printer (model OS-214)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



arkanoid.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Brad Oliver,Stephane Humbert
/***************************************************************************

    Arkanoid driver

    - MCU hookup needs sorting out properly.  The hookups for the different types
      is not identical.

    Japanese version support cocktail mode (DSW #7), the others don't.

    Here are the versions we have:

    arkanoid    The earlier revisions. They each differ in the country byte. These
    arkanoiduo    versions work fine both the bootleg A75-06.IC16 MCU rom and the
    arkanoidjb    genuine decapped Taito A75__06.IC16 MC68705 MCU.
    arkanoidu   USA version. MCU code properly dumped.
    arkanoidj   Japanese version. Final revision, MCU code properly dumped.
    arkanoidja  Japanese version. A later revision with level selector, MCU code properly dumped.
    arkanoidpe  Licensed to Phoenix Electronics Co. MCU code properly dumped.

    arkatour    Tournament Arkanoid, US version, MCU code properly dumped.
    arkatour2   Tournament Arkanoid, US version (newer? higher ROM numbers), MCU code not dumped.

    arkanoidjbl Bootleg of the early Japanese version. The only difference is
                  that the warning text has been replaced by "WAIT"
                  This version came with its own 68705p3 MCU ROM which is based on
                  the original Taito A75__06.IC16 one, which the pirates must have
                  extracted somehow.
    arkatayt    Another bootleg of the early Japanese one, more heavily modified
    arkblock    Another bootleg of the early Japanese one, more heavily modified
    arkbloc2    Another bootleg
    arkbl3      Another bootleg of the early Japanese one, more heavily modified
    paddle2     Another bootleg of the early Japanese one, more heavily modified
    arkangc     Game Corporation bootleg with level selector


    Most if not all Arkanoid sets have a bug in their game code. It occurs on the
    final level where the player has to dodge falling objects. The bug resides in
    the collision detection routine which sometimes reads from unmapped addresses
    above $F000. For these addresses it is vital to read zero values, or else the
    player will die for no reason.

    Some bootleg boards substitute an AY-3-8910A for the YM2149. Since the clock
    divider of the GI PSG cannot be configured, this effectively doubles the
    pitch of music and sound effects on actual hardware!

Measured Clocks:
    Z80 - 5997077Hz (6Mhz)
MC68705 - 2998533Hz (3Mhz)
 YM2149 - 2998531Hz (3Mhz)
****************************************************************************

Game Credits:
There is a nopped-out (unless there is some secret way to make this display?)
set of game credits for arkanoidj (the 2.x japan set), whose activation is described on
https://tcrf.net/Arkanoid_(Arcade)

The hidden credits (with full names, if known, and 3 letter hiscore initials):
    Directed and Programmed by: Yasumasa Sasabe "SSB"
    Director of Hardware & Co-Programmer: Toshiyuki Sanada "SND"
    Assistant Programmer: Toru. T "TOR"
    Graphic Designer: Hiroshi Tsujino(Onijust.H) "ONJ"
    Sound Composer: Hisayoshi Ogura
    Sound Effects: Tadashi Kimijima
    Pattern Designer: Akira Iwai "AKR"
    Software Analyser: Hidehiro Fujiwara(Hidegons)
    Mechanical Engineer: H. Yamaguchi
    Publicity Supervisor: Varis. I
    Game Designed by: Akira Fujita

The "Yasu" mentioned in the MCU code as a comment is probably Yasumasa Sasabe

****************************************************************************

Guru's readme

Arkanoid
Taito 1986

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

Note an original Taito Arkanoid PCB is approximately 10" square and is
painted white. The copper traces are not visible. The part type and
location of each component is printed in green on the PCB on top of the
white paint.

The following MCU images were tested on an original Arkanoid PCB using sets
'arkanoid', 'arkanoidu' and 'arkanoiduo' and work as expected.
(1) MCU image with CRC 0x389a8cfb [<- this is a deprotected copy of the original Taito A75__06 MCU code]
(2) MCU image with CRC 0x515d77b6 [<- this is a blackbox-reverse engineered bootleg MCU written by pirates]

An MCU found on a Tournament Arkanoid PCB was an unprotected type MC68705P3
and when read the CRC matched (1). So we assumed the MCUs for Arkanoid and
Tournament Arkanoid are the same.... or are at least interchangeable and work.
This turns out not to be the case, in retrospect.

"Tetris (D.R. Korea)" in MAME is a hack on an original Arkanoid PCB.
The hack can be undone and returned to Arkanoid simply by removing the mod
wires on the YM2149, replacing the ROMs with Arkanoid ROMs and replacing
the PC030CM which was removed. A working Arkanoid 68705 MCU is also required.
The above 'tested' images can be used.


J1100075A
K1100180A
K1100181A (ROMSTAR version added sticker)
  |---------------------------------------------|
  |                VOLUME  TL7700   TMM2018     |
|-|         MB3731                              |
|                                               |
|P                                              |
|O                                              |
|W          DSWA(8)  A75-09.IC22                |
|E                                              |
|R         YM2149F   A75-08.IC23                |
|                                               |
|-|                  A75-07.IC24                |
  |                                             |
  |                                             |
|-|                                 TMM2016     |
|                                               |
|          PC030CM                  TMM2016     |
|                                               |
|               JP4 JP3                         |
|2                                              |
|2         A75_06.IC14                          |
|W 48CR-1                                       |
|A                                              |
|Y                                              |
|          TMM2016                         12MHz|
|  48CR-1                                       |
|  48CR-1  A75_10.IC16    A75_05.IC62  MB112S146|
|                                               |
|-|        A75_01-1.IC17  A75_04.IC63  MB112S146|
  |48CR-1                                       |
  |                       A75_03.IC64           |
  |        Z80                                  |
  |---------------------------------------------|
Notes:
      Z80         - Zilog Z0840006 CPU. Clock input 6.000MHz (12/2)
      YM2149F     - Yamaha YM2149F software-controlled sound generator (SSG). Clock input 1.5MHz (12/8)
      A75_06.IC14 - Motorola MC68705P5 micro-controller. Clock input 3.000MHz (12/4).
      A75_*       - 27C256 EPROMs labelled 'A75 xx'. xx = 01, 03, 04, 05 etc. See ROM loading in the src for exact ROM usage.
      A75-0*      - MMI 63S241 bipolar PROMs. Compatible with MB7116, 7621, DM74S571N etc
      TMM2018     - Toshiba TMM2018 2k x8 SRAM (DIP24)
      TMM2016     - Toshiba TMM2016 2k x8 SRAM (DIP24)
      MB112S146   - Fujitsu MB112S146. Some kind of custom graphic decoder/shifter (DIP28)
      MB3731      - Fujitsu MB3731 18W BTL audio power amplifier (SIP12)
      PC030CM     - Taito custom ceramic package (SIP20)
      48CR-1      - Taito custom resistor array (SIP10)
      TL7700      - Texas Instruments TL7700CP supply voltage supervisor i.e. reset chip (DIP8)
      JP3         - 2-pin jumper. This is open but the game works even if it is closed.
      JP4         - 2-pin jumper. Must be closed to allow coin-up through PC030CM otherwise coin-up does not work.
                    Note the G connector is the 22-way edge connector.
                    The Japanese manual states (translated to English).....
                    ********
                    The coin-SW of this Main PC Board does not work without wiring coin meter to
                    coin meter pins of the G-connector.
                    You need to modify as follows in case coin meter is not connected to Main PC Board.
                    Coin System A ..... Wire jumper JP4 on Main PC Board. Coin meter not used.
                    Coin System B ..... Wire jumper JP3 on Main PC Board. Coin meter used.
                    ********

      Measured Syncs
      --------------
      HSync       - 15.625kHz
      VSync       - 59.185Hz


      POWER connector H
      -----------------
      1    Ground
      2    Ground
      3    Ground
      4    Ground
      5    +5V
      6    +5V
      7    +5V
      8    NC
      9    +12V
      10   Post
      11   NC
      12   NC


      22-way edge connector G
      -----------------------

           PARTS         SOLDER
           --------------------
                 |-----|
          GROUND | 1 A | GROUND
       VIDEO RED | 2 B | VIDEO GROUND
     VIDEO GREEN | 3 C | VIDEO BLUE
     VIDEO SYNC  | 4 D | NC
     SOUND OUT + | 5 E | SOUND OUT -
            POST | 6 F | POST
              NC | 7 H | NC
     COIN SW (A) | 8 J | COIN SW (B)
  COIN METER (A) | 9 K | COIN METER (B)
COIN LOCKOUT (A) |10 L | COIN LOCKOUT (B)
      SERVICE SW |11 M | TILT SW
         START 1 |12 N | START 2
             NC* |13 P | NC*
             NC* |14 R | NC*
        1P RIGHT |15 S | 2P RIGHT \
         1P LEFT |16 T | 2P LEFT  / Connect 15/16/S/T to the spinner left/right connections
      [1P RH UP] |17 U | [2P RH UP]
    [1P RH DOWN] |18 V | [2P RH DOWN]
   [1P RH RIGHT] |19 W | [2P RH RIGHT]
    [1P RH LEFT] |20 X | [2P RH LEFT]
   1P SERVE/FIRE |21 Y | 2P SERVE/FIRE
       [1P WARP] |22 Z | [2P WARP]
                 |-----|
[] - these are present and readable on arkanoid pcb hardware, but the game never reads or uses these
* - these NC pins are used for the main joysticks on certain other games
   (bubble bobble etc) which use the 22 pin taito connector, but are N/C and
   do not connect anywhere on the arkanoid pcb.
Note about spinner controller
-----------------------------

This game requires a geared spinner to operate correctly. A trackball or other optical
controller or home-made spinner built from a PC mouse will work but the player moves too
slowly and the game is unplayable. The Taito geared spinner moves the optical wheel *very*
fast to ensure the player moves fast enough to follow and return the ball easily. The ratio of
the control knob rotation to the optical wheel rotation is 1:20 so for one rotation of the
control knob the optical wheel rotates 20 times. The optical quadrature wheel has 24 slots.
Generally a half-turn of the control knob is enough to move the player across the full screen.

The spinner connections are....
Pin 1 - Left
Pin 2 - +5V
Pin 3 - Ground
Pin 4 - Right

These pins are listed from the Japanese Taito manual and have been tested to be correct with
the real Taito Arkanoid spinner.
The US ROMSTAR manual lists pin 4 as left and pin 1 as right. This information is probably
incorrect. Pins 2 and 3 are the same.

Spinner PCB Layout
------------------
J9000024A
K9000060A
|-----------|
|   OPTO    |
|          S|
|           |
|          S|
|           |
|  POWER    |
|-4-3-2-1---|
Notes:
      OPTO  - Optical transmitter/receiver on other side of PCB
      POWER - Power input connector. Pin 1 is on the right.
      S     - Screw positions to show orientation of the PCB with reference to the power connector pin 1


DIP Switches
+-----------------------------+--------------------------------+
|FACTORY DEFAULT = *          |  1   2   3   4   5   6   7   8 |
+----------+------------------+----+---+-----------------------+
|          |*1 COIN  1 CREDIT | OFF|OFF|                       |
|COINS     | 1 COIN  2 CREDITS| ON |OFF|                       |
|          | 2 COINS 1 CREDIT | OFF|ON |                       |
|          | 1 COIN  6 CREDITS| ON |ON |                       |
+----------+------------------+----+---+---+                   |
|LIVES     |*3                |        |OFF|                   |
|          | 5                |        |ON |                   |
+----------+------------------+--------+---+---+               |
|BONUS     |*20000 / 60000    |            |OFF|               |
|1ST/EVERY | 20000 ONLY       |            |ON |               |
+----------+------------------+------------+---+---+           |
|DIFFICULTY|*EASY             |                |OFF|           |
|          | HARD             |                |ON |           |
+----------+------------------+----------------+---+---+       |
|GAME MODE |*GAME             |                    |OFF|       |
|          | TEST             |                    |ON |       |
+----------+------------------+--------------------+---+---+   |
|SCREEN    |*NORMAL           |                        |OFF|   |
|          | INVERT           |                        |ON |   |
+----------+------------------+------------------------+---+---+
|CONTINUE  | WITHOUT          |                            |OFF|
|          |*WITH             |                            |ON |
+----------+------------------+----------------------------+---+


***************************************************************************

Stephh's notes (based on the games Z80 code and some tests) :

0) Useful addresses and routines

0a) "Game Corporation" bootlegs, Tayto bootlegs, 'arkanoidjbl', 'ark1ball'

  - Basic routines :
      * 0x2044 : BC += A;
      * 0x204a : DE += A;
      * 0x2050 : HL += A;
      * 0x2056 : HL += A; DE = (HL);
      * 0x21f1 : Display string :
                   Inputs : DE = string address in ROM
                            HL = address where the string will be displayed
                                 (most of the times in video RAM)
                            A = colour
                   String begins with the number of chars to display
                   (eg: 0x02 "OK" to display "OK")
      * 0x210d : Display 1 char (called by previous routine)
      * 0x264a : Display score :
                   Inputs : DE = score address
                            HL = address where the score will be displayed
                                 (most of the times in video RAM)
      * 0x266f : Display 1 digit (called by previous routine)
      * 0x67ae : Play sound (input : register A) - to be confirmed !

  - Level issues :
      * 0xed72 : Level (0x00-0x20)
      * 0xed83 : Breakable bricks left (gold bricks are NOT counted)
      * 0x55a0 : Display level background
      * 0x55d9 : Display level bricks :
                   Inputs : IY = index for bricks counter
                            B = number of bricks to display
      * 0x55f6 : Display 1 background "column"
      * 0x5639 : Display 1 brick with its shadow
      * 0xed6b : Hits in the head of last level (0x00-0x10)
                 This value is reset to 0x00 each time you lose a life.
      * 0x8d15 : Check if head of last level is dead

  - Lives issues :
      * 0xed71 : Current player lives
      * 0xed76 : Player 1 lives
      * 0xed7b : Player 2 lives
      * 0xef68 : When bit 0 set to 1, no more lives on score for player 1
      * 0xef69 : When bit 0 set to 1, no more lives on score for player 2
      * 0xef6a : Player 1 counter for extra lives on score
      * 0xef6b : Player 2 counter for extra lives on score
      * 0x22a0 : Draw lives at the bottom left of the screen
      * 0x2785 : Check player score for extra life

  - Score issues :
      * 0xc4d7 : Player 1 score / 10 (3 bytes, BCD coded, MSB first)
      * 0xc4db : Player 2 score / 10 (3 bytes, BCD coded, MSB first)
      * 0xc4df : Highscore / 10 (3 bytes, BCD coded, MSB first)
      * 0xef6c : Player 1 score / 1000 for next life (2 bytes, BCD coded, MSB first)
      * 0xef6e : Player 2 score / 1000 for next life (2 bytes, BCD coded, MSB first)
      * 0x2723 : Player score += DE; (BCD => 'daa' instructions)
      * 0x274a : Check player score for highscore

  - Speed issues :
      * 0xef63 : Speed counter which is increased each time the ball touches
                 your paddle, any wall (up, left or right) or a brick;
                 it is reset when speed changes or when you lose a life.
      * 0x094c : Speed table (16 bytes)
      * 0xc462 : Speed (0x01-0x0e) :
                   Speed +1 when counter is above the value in the table
                   (check code at 0x0900)
                   Speed -2 when you get the slow pill ("S")
                   (check code at 0x5423)
                   Speed is sometimes increased when up wall is hit
                   (check code at 0x1442 and table at 0x1462)

  - Pills issues :
      * 0xc658 : Falling pill (0x80 = none - 0x81-0x87)
      * 0x5474 : Pills routines table (7 * 2 bytes, LSB first) :
          . 0x53ff : "L" pill (laser)
          . 0x540d : "E" pill (enlarge)
          . 0x5418 : "C" pill (glue)
          . 0x5423 : "S" pill (slow)
          . 0x5436 : "B" pill (warp door)
          . 0x5446 : "D" pill (3 balls)
          . 0x5451 : "P" pill (extra life)
      * 0x53c8 : Check pill effect

  - Miscellaneous addresses :
      * 0xef66 : 0x00 in demo mode else 0x01
      * 0xed6f : Bit 1 determines player : 0 = player 1 - 1 = player 2
      * 0xc4ce : Warp door status : 0x00 = closed - 0x01 = opened
      * 0xc469 : Balls in play : 0x00 = 1 ball - 0x02 = 2 or 3 balls

  - Miscellaneous routines :
      * 0xa234 : Enter initials
      * 0xa343 : Check if player has entered "SEX" as initials;
                 if so, replace them with "H !" (no side effect)


1) Bootlegs with MCU

1a) 'arkanoidjbl'

  - Region = 0x76 (Japan).
  - The bootleg is based on the Japanese version.
  - The MCU is dumped and used, it is based on the real Taito MCU but with the
    MCU's security features disabled, so must have been extracted by bootleggers.
  - "(c) Taito Corporation 1986".
  - Displays the "Arkanoid" title.
  - "HARDWARE TEST" message is written, tests are performed, countdown 11 to 0.
  - "NOTICE" screen replaces by "WAIT" without any more text.
    However, the text is still in the ROM at 0x7b81 with changes at the beginning :
      * "NOTICE" -> "WAIT  "
      * 0xbe "THIS GAME IS" -> 0x01 " " 0x7b "IS" 0xde "GAME" 0x6d "IS"
    IMO these changes are made to bypass the checksums
  - You can't select your starting level
  - Known bugs : NONE !

1b) 'ark1ball'

  - Note from the dumper (f205v, 2005.09.29) : "It's a bootleg of "Arkanoid (Japan)",
    with notice screen eliminated (it only shows a black screen with a red WAIT)
    and a fix (no dips selection) 1 ball x game and NO starting level selection".
    However, there is still code in the game which tests the Dip Switches !
  - Region = 0x76 (Japan).
  - The bootleg is based on a Japanese early version we don't have.
    In fact, it is completely based on 'arkanoidjbl' :

      Z:\MAME\roms>romcmp ark1ball.zip arkanoidjbl.zip -d
      3 and 2 files
      e1.6d                   a-1.7d                  IDENTICAL
      e2.6f                   2palline.7f             99.957275%
      68705p3.6i                                      NO MATCH

      Z:\MAME\data>fc /B e2.6f 2palline.7f
      Comparing files e2.6f and 2PALLINE.7F
      000013BE: 20 60
      00001A28: 05 02
      00001A29: 03 01
      00001C80: 20 60
      00001C9C: 20 60
      00001ED9: 53 36
      00001EDA: 53 35
      00001EE0: 53 33
      00001EE7: 54 34
      00001EE9: 52 42
      00001EF7: 52 42
      00001EF9: 66 46

  - The MCU is not dumped, but the game runs with either the Taito or Pirate
    version of the A75-06 MCU. It most likely really used the latter,
    but the actual MCU used is unknown.
  - This version is supposed to be a harder version :
      * less lives (1 or 2 instead of 3 or 5)
      * 60K for 1st bonus life instead of 20K
  - Known bugs :
      * Names on highscores table are wrong
        (ingame bug to bypass the checksums)

2) "Game Corporation" bootlegs and assimilated ones.

  - Region = 0x76 (Japan).
  - All bootlegs are based on a (bootleg) version we don't have.
  - Team credits have been replaced by bootleggers code.
  - Start of levels table at 0x7bd5 (32 * 2 bytes - MSB first)

2a) 'arkangc'

  - "(c)  Game Corporation 1986".
  - Displays the "Arkanoid" title but routine to display "BLOCK" with bricks exists.
  - No hardware test and no "NOTICE" screen.
  - All reads from 0xf002 are patched.
  - No reads from 0xd008.
  - "Continue" Dip Switch has been replaced by sort of "Debug" Dip Switch :
      * affects ball speed at start of level (0x06 or 0x08)
      * affects level 2 (same as normal version or same as level 30)
  - You can select your starting level (between 1 and 30)
    but they aren't displayed like in the original Japanese set we have ('arkanoidja').
  - Level 30 differs from original Japanese version
  - There seems to be code to edit levels (check code at 0x8082), but the routines
    don't seem to be called anymore.
  - Known bugs :
      * The paddle isn't centered when starting a new life and / or level;
        it doesn't "backup" the paddle position when a life is lost as well
        (I can't tell at the moment if it's an ingame bug or not)
        So the paddle can sometimes appear in the left wall !
      * You are told be to able to select your starting level from level 1 to level 32
        (ingame bug - check code at 0x3425)

2b) 'arkangc2'

  - "(c)  Game Corporation 1986".
  - Displays the "Arkanoid" title but routine to display "BLOCK" with bricks exists.
  - No hardware test and no "NOTICE" screen.
  - No reads from 0xf002.
  - Reads bit 1 from 0xd008.
  - "Continue" Dip Switch has been replaced by sort of "Debug" Dip Switch :
      * affects ball speed at start of level (0x04 or 0x06)
      * affects level 2 (same as normal version or same as level 30)
  - You can select your starting level (between 1 and 30)
    but they aren't displayed like in the original Japanese set we have ('arkanoidja').
    No "What round do you want to start from ?" message though.
  - Level 30 differs from original Japanese version (it also differs from 'arkangc')
  - The routine to handle the paddle is completely different as in 'arkangc'
    and any other bootlegs (check code at 0x96b0)
  - There seems to be code to edit levels (check code at 0x8082), but the routines
    don't seem to be called anymore.
  - Known bugs :
      * The paddle isn't centered when starting a new life and / or level;
        it doesn't "backup" the paddle position when a life is lost as well
        (I can't tell at the moment if it's an ingame bug or not)
        So the paddle can sometimes appear in the left wall !
      * You are told be to able to select your starting level from level 1 to level 32
        (ingame bug - check code at 0x3425)
      * The "test mode" display is completely corrupted
        (ingame bug - check unused code at 0x2f00 instead of standard text)
        But you can still press the buttons and test the paddle and the Dip Switches.

2c) 'block2'

  - "       S.  P.  A.  CO.    ".
  - Displays "BLOCK II" with "bricks".
  - Colored bricks have been replaced with aliens (ripped from "Space Invaders" ?)
  - No standard hardware test and no "NOTICE" screen.
  - Specific hardware test which reads back at 0xf000 values written to 0xd018
    (check code at 0x035e); the game enters a loop until good values are returned.
  - No reads from 0xf002.
  - Reads bit 1 from 0xd008.
  - "Continue" Dip Switch has been replaced by sort of "Debug" Dip Switch as in 'arkangc';
    however, this has no effect due to newly patched code at 0x06e9 !
  - You can select your starting level (between 1 and 30)
    but they aren't displayed like in the original Japanese set we have ('arkanoidja').
  - Levels 1, 2, 3, 4, 6, 7, 11, 14, 30, 31 and 32 differ from original Japanese version;
    level 1 starts at a different offset (0x90a8 instead of 0xbf15).
  - Complerely different initials on high-scores table, but scores and rounds
    are the same as in the original Japanese set we have ('arkanoidja').
  - There seems to be code to edit levels (check code at 0x8082), but the routines
    don't seem to be called anymore.
  - Known bugs :
      * The paddle isn't centered when starting a new life and / or level;
        it doesn't "backup" the paddle position when a life is lost as well
        (I can't tell at the moment if it's an ingame bug or not)
        So the paddle can sometimes appear in the left wall !
      * The "test mode" display is completely corrupted
        (ingame bug - check unused code at 0x2f00 instead of standard text)
        But you can still press the buttons and test the paddle and the Dip Switches.

2d) 'arkblock'

  - Same as 'arkangc', the only difference is that it displays "BLOCK" with bricks
    instead of displaying the "Arkanoid" title :

      Z:\MAME\dasm>diff arkangc.asm arkblock.asm
      8421,8422c8421,8424
      < 32EF: 21 80 03      ld   hl,$0380
      < 32F2: CD D1 20      call $20D1
      ---
      > 32EF: F3            di
      > 32F0: CD 90 7C      call $7C90
      > 32F3: C9            ret
      > 32F4: 14            inc  d

2e) 'arkbloc2'

  - "(c)  Game Corporation 1986".
  - Displays "BLOCK" with bricks.
  - No hardware test and no "NOTICE" screen.
  - All reads from 0xf002 are patched.
  - Reads bit 5 from 0xd008.
  - You can select your starting level (between 1 and 30) but they aren't displayed
    like in the original Japanese set we have ('arkanoidja').
  - "Continue" Dip Switch has been replaced by sort of "Debug" Dip Switch :
      * affects ball speed at start of level (0x06 or 0x08)
      * affects level 2 (same as normal version or same as level 30)
  - You can select your starting level (between 1 and 30)
    but they aren't displayed like in the original Japanese set we have ('arkanoidja').
  - Level 30 differs from original Japanese version (same as the one from 'arkangc2')
  - Known bugs :
      * You can go from one side of the screen to the other through the walls
        (I can't tell at the moment if it's an ingame bug or not)
      * You are told be to able to select your starting level from level 1 to level 32
        (ingame bug - check code at 0x3425)

2f) 'arkgcbl'

  - "1986    ARKANOID    1986".
  - Displays the "Arkanoid" title but routine to display "BLOCK" with bricks exists.
  - No hardware test and no "NOTICE" screen.
  - Most reads from 0xf002 are patched. I need to fix the remaining ones (0x8a and 0xff).
  - Reads bits 1 and 5 from 0xd008.
  - "Continue" Dip Switch has been replaced by "Round Select" Dip Switch
    ("debug" functions from 'arkangc' have been patched).
  - Different "Bonus Lives" Dip Switch :
      * "60K 100K 60K+" or "60K" when you start a new game
      * "20K 60K 60K+"  or "20K" when you continue
  - Different "Lives" Dip Switch (check table at 0x9a28)
  - Specific coinage (always 2C_1C)
  - If Dip Switch is set, you can select your starting level (between 1 and 30)
    but they aren't displayed like in the original Japanese set we have ('arkanoidja').
  - Same level 30 as original Japanese version
  - Known bugs :
      * You can go from one side of the screen to the other through the walls
        (I can't tell at the moment if it's an ingame bug or not)
      * You are told be to able to select your starting level from level 1 to level 32
        (ingame bug - check code at 0x3425)
      * Sound in "Demo Mode" if 1 coin is inserted (ingame bug - check code at 0x0283)
      * Red square on upper middle left "led" when it is supposed to be yellow (ingame bug)

2g) 'paddle2'

  - Different title, year, and inside texts but routine to display "BLOCK" with bricks exists.
  - No hardware test and no "NOTICE" screen.
  - I need to fix ALL reads from 0xf002.
  - Reads bits 0 to 3 and 5 from 0xd008.
  - "Continue" Dip Switch has been replaced by "Round Select" Dip Switch
    ("debug" functions from 'arkangc' have been patched).
  - No more "Service Mode" Dip Switch (even if code is still there for it).
    This Dip Switch now selects how spinners are handled :
      * bit 2 = 0 => read from 0xd018 only
      * bit 2 = 1 => read from 0xd018 + read from 0xd008
    I set its default to 1 as parts of the game still branch to 0x96b0.
  - Different "Bonus Lives" Dip Switch :
      * "60K 100K 60K+" or "60K" when you start a new game
      * "20K 60K 60K+"  or "20K" when you continue
  - Different "Lives" Dip Switch (check table at 0x9a28)
  - If Dip Switch is set, you can select your starting level (between 1 and 30)
    but they aren't displayed like in the original Japanese set we have ('arkanoidja').
  - Levels are based on the ones from "Arkanoid II".
  - Known bugs :
      * You can go from one side of the screen to the other through the walls
        (I can't tell at the moment if it's an ingame bug or not)
      * You can't correctly enter your initials at the end of the game
        (ingame bug ? check code at 0xa23e and difference at 0xa273)
      * On intro and last screen, colour around main ship is yellow instead of red
        (ingame bug due to numerous patches)

3) "Tayto" bootlegs and assimilated ones.

  - Region = 0x76 (Japan).
  - All bootlegs are based on a Japanese early version we don't have.
  - Start of levels table at 0xbd75 (32 * 2 bytes - LSB first)

3a) 'arkatayt'

  - "(c) Tayto Corporation 1986" but the Taito original logo is displayed.
  - Displays the "Arkanoid" title.
  - "HARDWARE TEST" message is written, tests are performed, but no countdown.
  - "NOTICE" screen replaces by "WAIT" without any more text.
    However, the text is still in the ROM at 0x7b81 with changes at the beginning :
      * "NOTICE" -> "WAIT  "
      * 0xbe "THIS" -> 0x01 " HIS"
    IMO these changes are made to bypass the checksums
  - You can't select your starting level
  - Known bugs :
      * level 16 is corrupted with extra bricks
        (ingame bug due to extra code from 0x5042 to 0x5086)
      * level 25 is shifted 8 "columns" to the right
        (ingame bug due to bad level offset at 0xbda5 : 0xe5 instead of 0xed)

3b) 'arktayt2'

  - This version is supposed to be a harder version of 'arkatayt' :
      * less lives (2 or 3 instead of 3 or 5)
      * 60K for 1st bonus life instead of 20K
    Same as 'arkatayt' otherwise
  - Known bugs :
      * level 16 is corrupted with extra bricks
        (ingame bug due to extra code from 0x5042 to 0x5086)
      * level 25 is shifted 8 "columns" to the right
        (ingame bug due to bad level offset at 0xbda5 : 0xe5 instead of 0xed)
      * Names on highscores table are wrong
        (ingame bug to bypass the checksums)


TO DO (2006.09.12) :

  - Check the following Taito sets (addresses, routines and Dip Switches) :
      * 'arkanoid' = 'arkanoiduo'
      * 'arkanoidja'
      * 'arkanoidu'
      * 'arkatour'
  - Add more notes about main addresses and routines in the Z80
  - Try to understand the problem with the MCU in the following sets :
      * 'arkanoidjbl'
      * 'ark1ball'


Stephh's log (2006.09.05) :

  - Interverted 'arkblock' and 'arkbloc2' sets for better comparaison
  - Renamed sets :
      * 'arkbl2'   -> 'arkanoidjbl'
      * 'arkbl3'   -> 'arkgcbl'
  - Changed some games descriptions
  - Removed flags from the following sets :
      * 'arkbloc2' (old 'arkblock')
      * 'arkgcbl'  (old 'arkbl3')
      * 'paddle2'
    This way, even if emulation isn't perfect, people can try them and report bugs.


Stephh's log (2006.09.12) :

  - Renamed sets :
      * 'arkatayt' -> 'arktayt2'
  - Changed some games descriptions
  - Added sets :
      * 'ark1ball'
      * 'arkangc2'
      * 'arkatayt'
  - Removed flags from the following sets :
      * 'arkanoidjbl'
    This way, even if emulation isn't perfect, people can try them and report bugs.


Stephh's log (2009.10.07) :

  - Added set :
      * 'block2'

***************************************************************************

Stephh's notes on 'tetrsark' (based on the game Z80 code and some tests) :

  - No reads from 0xd00c, 0xd010 nor 0xd018.
  - "Cabinet" Dip Switch :
      * when set to "Upright" :
          . each player can join and play on its half screen while
            the other is already playing
          . the screen is never flipped
      * when set to "Cocktail" :
          . only one player can play : the other player has to wait until
            current player is "GAME OVER" to press the Start button
          . screen is flipped when player 2 is playing and
            it remains flipped until player 1 starts a game
            (so "demo mode" can be upside down)
  - Credits : even if display is limited to 9, the value still increases;
    so if you insert too many coins, it can be reset to 0 !
  - Routines :
      * 0x56e3 : Play sound (input : register A) - to be confirmed !
  - Addresses :
      * 0xc52b : credits
      * 0xc541 : ~(IN5) - test for coins "buttons" (code at 0x0232)
      * 0xc516 : ~(IN5)
      * 0xc517 : ~(IN4)
  - Known bugs :
      * Coins "buttons" don't work - we need to use fake BUTTON2 for each player

****************************************************************************

    HEXA

    This hardware is derived from Arkanoid's hardware.  The 1986 date found in
    the roms probably comes from Arkanoid.  This is a Columns style game and
    the original Columns wasn't released until 1990 and I find it hard to
    believe that this would pre-date Columns.

    driver by Howie Cohen

    Memory map (prelim)
    0000 7fff ROM
    8000 bfff bank switch rom space??
    c000 c7ff RAM
    e000 e7ff video ram
    e800-efff unused RAM

    read:
    d001      AY8910 read
    f000      ???????

    write:
    d000      AY8910 control
    d001      AY8910 write
    d008      bit 0   flip screen x
              bit 1   flip screen y
              bit 2   paddle player select
              bit 3   coin lockout
              bit 4   ????????
              bit 5 = graphics bank
              bit 6 = palette bank
              bit 7 = mcu reset
    d010      watchdog reset, or IRQ acknowledge, or both
    f000      ????????

    main hardware consists of.....

    sub board with Z80 x2, 2 ROMs and a scratched 18 pin chip (probably a PIC)

    main board has....
    12MHz xtal
    ay3-8910
    8 position DSW x1
    ROMs x4
    6116 SRAM x3
    82S123 PROMs x3

***************************************************************************

DIP locations verified for:
  - arkanoidja
  - arkanoid

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

#include "emu.h"
#include "arkanoid.h"

#include "cpu/z80/z80.h"
#include "machine/watchdog.h"
#include "sound/ay8910.h"

#include "speaker.h"


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

/* Memory Maps */
/*
Address maps (x = ignored; * = selects address within this range)
z80 address map:
a15 a14 a13 a12 a11 a10 a9  a8  a7  a6  a5  a4  a3  a2  a1  a0
*   *                                                               "Manual" decode logic with ic30 and ic29
0   0   *   *   *   *   *   *   *   *   *   *   *   *   *   *       R   ROM
0   1   *   *   *   *   *   *   *   *   *   *   *   *   *   *       R   ROM
1   0   *   *   *   *   *   *   *   *   *   *   *   *   *   *       R   ROM
        *   *                                                       74LS139@IC80 side '1'
1   1   0   0   x   *   *   *   *   *   *   *   *   *   *   *       RW  6116 SRAM
1   1   0   1                                                       IO AREA
                                            *   *                   74LS139@IC80 side '2'
                                            *   *                   74LS155@IC25 both sides 1 and 2
1   1   0   1   x   x   x   x   x   x   x   0   0   x   x   0       RW  YM2149 BC2 low
1   1   0   1   x   x   x   x   x   x   x   0   0   x   x   1       RW  YM2149 BC2 high
1   1   0   1   x   x   x   x   x   x   x   0   1   x   x   x       W   bank/flip/mcu reset register
1   1   0   1   x   x   x   x   x   x   x   0   1   0   x   x       R   "RH" Joystick bits (unused by game, but present on pcb)
1   1   0   1   x   x   x   x   x   x   x   0   1   1   x   x       R   "SYSTEM" Start buttons, service buttons, coin inputs and mcu semaphore bits
1   1   0   1   x   x   x   x   x   x   x   1   0   x   x   x       R   "BUTTONS" Fire buttons for p1 and p2, also unused 'warp' buttons for p1 and p2; D4-D7 are open bus!
1   1   0   1   x   x   x   x   x   x   x   1   0   x   x   x       W   Watchdog reset. Watchdog is identical to Taito SJ watchdog, counts 128 vblanks
1   1   0   1   x   x   x   x   x   x   x   1   1   x   x   x       RW  MCU Read and Write latches
1   1   1   0                                                       VIDEO AREA
1   1   1   0   *   *   *   *   *   *   *   *   *   *   *   0       RW  2016 SRAM@IC57
1   1   1   0   *   *   *   *   *   *   *   *   *   *   *   1       RW  2016 SRAM@IC58
1   1   1   1   x   x   x   x   x   x   x   x   x   x   x   x       OPEN BUS
              |               |               |
*/
void arkanoid_state::arkanoid_map(address_map &map)
{
	map(0x0000, 0xbfff).rom();
	map(0xc000, 0xc7ff).ram().mirror(0x0800);
	map(0xd000, 0xd001).w("aysnd", FUNC(ay8910_device::address_data_w)).mirror(0x0fe6);
	map(0xd001, 0xd001).r("aysnd", FUNC(ay8910_device::data_r)).mirror(0x0fe6);
	map(0xd008, 0xd008).w(FUNC(arkanoid_state::arkanoid_d008_w)).mirror(0x0fe7);  /* gfx bank, flip screen, 68705 reset, etc. */
	map(0xd008, 0xd008).portr("SYSTEM2").mirror(0x0fe3); /* unused p1 and p2 joysticks */
	map(0xd00c, 0xd00c).portr("SYSTEM").mirror(0x0fe3); /* start, service, coins, and 2 bits from the 68705 */
	map(0xd010, 0xd010).portr("BUTTONS").w("watchdog", FUNC(watchdog_timer_device::reset_w)).mirror(0x0fe7);
	map(0xd018, 0xd018).rw(m_mcuintf, FUNC(arkanoid_mcu_device_base::data_r), FUNC(arkanoid_mcu_device_base::data_w)).mirror(0x0fe7); /* input from the 68705 */
	map(0xe000, 0xe7ff).ram().w(FUNC(arkanoid_state::arkanoid_videoram_w)).share("videoram");
	map(0xe800, 0xe83f).ram().share("spriteram");
	map(0xe840, 0xefff).ram();
	map(0xf000, 0xffff).nopr(); /* fixes instant death in final level */
}

void arkanoid_state::bootleg_map(address_map &map)
{
	map(0x0000, 0xbfff).rom();
	map(0xc000, 0xc7ff).ram();
	map(0xd000, 0xd000).w("aysnd", FUNC(ay8910_device::address_w));
	map(0xd001, 0xd001).rw("aysnd", FUNC(ay8910_device::data_r), FUNC(ay8910_device::data_w));
	map(0xd008, 0xd008).w(FUNC(arkanoid_state::arkanoid_d008_w));  /* gfx bank, flip screen etc. */
	map(0xd00c, 0xd00c).portr("SYSTEM");
	map(0xd010, 0xd010).portr("BUTTONS").w("watchdog", FUNC(watchdog_timer_device::reset_w));
	map(0xd018, 0xd018).r(FUNC(arkanoid_state::input_mux_r)).nopw();
	map(0xe000, 0xe7ff).ram().w(FUNC(arkanoid_state::arkanoid_videoram_w)).share("videoram");
	map(0xe800, 0xe83f).ram().share("spriteram");
	map(0xe840, 0xefff).ram();
	map(0xf000, 0xffff).nopr(); /* fixes instant death in final level */
}

void arkanoid_state::hexa_map(address_map &map)
{
	map(0x0000, 0x7fff).rom();
	map(0x8000, 0xbfff).bankr("bank1");
	map(0xc000, 0xc7ff).ram();
	map(0xd001, 0xd001).r("aysnd", FUNC(ay8910_device::data_r));
	map(0xd000, 0xd001).w("aysnd", FUNC(ay8910_device::address_data_w));
	map(0xd008, 0xd008).w(FUNC(arkanoid_state::hexa_d008_w));
	map(0xd010, 0xd010).w("watchdog", FUNC(watchdog_timer_device::reset_w)); /* or IRQ acknowledge, or both */
	map(0xe000, 0xe7ff).ram().w(FUNC(arkanoid_state::arkanoid_videoram_w)).share("videoram");
}

uint8_t arkanoid_state::hexaa_f000_r()
{
//  return m_hexaa_from_sub;
	return machine().rand();
}

void arkanoid_state::hexaa_f000_w(uint8_t data)
{
	m_hexaa_from_main = data;
}

void arkanoid_state::hexaa_map(address_map &map)
{
	map(0x0000, 0x7fff).rom();
	map(0x8000, 0xbfff).bankr("bank1");
	map(0xc000, 0xc7ff).ram();
	map(0xd001, 0xd001).r("aysnd", FUNC(ay8910_device::data_r));
	map(0xd000, 0xd001).w("aysnd", FUNC(ay8910_device::address_data_w));
	map(0xd008, 0xd008).w(FUNC(arkanoid_state::hexa_d008_w));
	map(0xd010, 0xd010).w("watchdog", FUNC(watchdog_timer_device::reset_w)); /* or IRQ acknowledge, or both */
	map(0xe000, 0xe7ff).ram().w(FUNC(arkanoid_state::arkanoid_videoram_w)).share("videoram");
	map(0xe800, 0xefff).ram();
	map(0xf000, 0xf000).rw(FUNC(arkanoid_state::hexaa_f000_r), FUNC(arkanoid_state::hexaa_f000_w));
}

void arkanoid_state::hexaa_sub_map(address_map &map)
{
	map(0x0000, 0x0fff).rom();
}


void arkanoid_state::hexaa_sub_80_w(uint8_t data)
{
	m_hexaa_from_sub = data;
}

uint8_t arkanoid_state::hexaa_sub_90_r()
{
	return m_hexaa_from_main;
//  return machine().rand();
}

void arkanoid_state::hexaa_sub_iomap(address_map &map)
{
	map.global_mask(0x9f);
	map(0x00, 0x0f).ram(); // ?? could be communication with the other chip (protection?)
	map(0x80, 0x80).w(FUNC(arkanoid_state::hexaa_sub_80_w));
	map(0x90, 0x90).r(FUNC(arkanoid_state::hexaa_sub_90_r));
}



void arkanoid_state::brixian_map(address_map &map)
{
	map(0x0000, 0xbfff).rom();
	map(0xc000, 0xc7ff).ram().share("protram");
	map(0xd000, 0xd000).w("aysnd", FUNC(ay8910_device::address_w));
	map(0xd001, 0xd001).rw("aysnd", FUNC(ay8910_device::data_r), FUNC(ay8910_device::data_w));
	map(0xd008, 0xd008).w(FUNC(arkanoid_state::brixian_d008_w));  /* gfx bank, flip screen etc. */
	map(0xe000, 0xe7ff).ram().w(FUNC(arkanoid_state::arkanoid_videoram_w)).share("videoram");
	map(0xe800, 0xe83f).ram().share("spriteram");
	map(0xe840, 0xefff).ram();
}



/* MCU Hookup based on 'Arkanoid_TAITO_(Japan 1986) PCB.rar' from Taro and others at http://zx-pk.ru forums
NOTE and TODO: these comments have not been significantly updated since the real taito arkanoid schematics were found. beware of mistakes!
ic5 = 74ls669
ic6 = 74ls669
ic9 = 74ls257
ic21 = 74ls393 (? counter)
ic26 = 74ls74 (two semaphore latches; latch 1 is == m_Z80HasWritten and is cleared by ?; latch 2 is == m_MCUHasWritten and is set by PC3 being output low, and cleared by ?)
ic27 = 74ls374 (mcu->z80 latch)
ic28 = 74ls374 (z80->mcu latch)
ic31 = 74ls74
ic32 = 74ls273
ic43 = 74ls157
ic45 = 74ls74
ic46 = 74ls08
ic87 = 74ls74
~VCC = 'pulled to vcc through a resistor'
icxx.y = ic xx pin y
                                                                               +--------\_/--------+
                                                        GND -- =   VSS(GND) -- |  1             28 | <- /RESET  = <- ~VCC & ic32.9 (4Q) & ic26.13 (/reset2) & ic26.1 (/reset1)
                         ~VCC & ic26.6 (/1Q) & ic9.10 (I1C) -> =       /INT -> |  2             27 | <> PA7     = -> ic27.18 (8D)
                                                        +5v -- =        VCC -- |  3         M   26 | <> PA6     = -> ic27.17 (7D)
          ic43.1 (select) & ic45.11 (clock2) & ...elsewhere -> =      EXTAL -> |  4         C   25 | <> PA5     = -> ic27.14 (6D)
                                                        GND -- =       XTAL -> |  5         6   24 | <> PA4     = -> ic27.13 (5D)
                                                        +5v -- =        VPP -- |  6         8   23 | <> PA3     = -> ic27.3 (1D)
ic46.4 (A2) & ic31.4 (/preset1) & ic21.1 (1A) & ic87.9 (2Q) -> = TIMER/BOOT -> |  7         7   22 | <> PA2     = -> ic27.4 (2D)
                                                ic26.5 (1Q) -> =  (NUM) PC0 <> |  8         0   21 | <> PA1     = -> ic27.7 (3D)
                                ic26.8 (/2Q) & ic9.13 (I1D) -> =        PC1 <> |  9         5   20 | <> PA0     = <> ic27.8 (4D) & ic28.9 (4Q)
                      ~VCC & ic26.3 (clock1) & ic28.1 (/OE) <- =        PC2 <> | 10         P   19 | <> PB7     = <- ic6.11 (QD)
              ~VCC & ic26.10 (/preset2) & ic27.11 (EnableG) <- =        PC3 <> | 11         5   18 | <> PB6     = <- ic6.12 (QC)
                                                ic5.14 (QA) -> =        PB0 <> | 12             17 | <> PB5     = <- ic6.13 (QB)
                                                ic5.13 (QB) -> =        PB1 <> | 13             16 | <> PB4     = <- ic6.14 (QA)
                                                ic5.12 (QC) -> =        PB2 <> | 14             15 | <> PB3     = <- ic5.11 (QD)
                                                                               +-------------------+

The ic26 semaphores and the /int line:
ic26 is a 74ls74 with two latches with positive edge triggering:
latch 1 aka m_Z80HasWritten:
/Reset  : <- z80 d008 bit 7, also controls /reset on the MCU
Data    : tied to GND on an inner plane
Clock   : <- 68705 PC2, triggered on rising edge, this clears the bit and the /INT
/Preset : from z80, somehow.
Q       : -> 68705 PC0
/Q      : -> 68705 /INT and z80, somehow

latch 2 aka m_MCUHasWritten:
/Reset  : <- z80 d008 bit 7, also controls /reset on the MCU
Data    : tied to GND on an inner plane
Clock   : from z80, somehow; also is /oe of ic27
/Preset : <- 68705 PC3
Q       : N/C
/Q      : -> 68705 PC1 and z80, somehow

Note: despite having a signal on it (/VBLK according to the schematics), the TIMER/BOOT pin is seemingly never used by the MCU in input mode, so the signal is ignored.

Note2: PA0 is connected to bit 0 of both the z80->68705 latch and the 68705->z80 latch. The board trace from http://zx-pk.ru is known to be incomplete, so its likely all of the PAx bits go both ways.
*/


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


/* Input Ports */

static INPUT_PORTS_START( arkanoid )
	PORT_START("SYSTEM")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_START2 )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_SERVICE1 )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_TILT )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_COIN1 )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_COIN2 )
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(arkanoid_state::arkanoid_semaphore_input_r)) // Z80 and MCU Semaphores

	PORT_START("SYSTEM2") // these are the secondary "RH" joystick ports for P1 and P2; the circuitry to read them is populated on the arkanoid PCB, but the game never actually reads these.
	/*PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICKRIGHT_UP )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICKRIGHT_DOWN )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICKRIGHT_RIGHT )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICKRIGHT_LEFT )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICKRIGHT_UP ) PORT_COCKTAIL
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICKRIGHT_DOWN ) PORT_COCKTAIL
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICKRIGHT_RIGHT ) PORT_COCKTAIL
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICKRIGHT_LEFT ) PORT_COCKTAIL*/
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("BUTTONS") // button 2 for players 1 and 2 the circuitry is populated to read them, but the game never uses them
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 )
	//PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_COCKTAIL
	//PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_COCKTAIL
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("DSW")
	PORT_DIPNAME( 0x01, 0x00, DEF_STR( Allow_Continue ) )   PORT_DIPLOCATION("SW1:8")
	PORT_DIPSETTING(    0x01, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Flip_Screen ) )      PORT_DIPLOCATION("SW1:7")
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_SERVICE_DIPLOC(0x04, IP_ACTIVE_LOW, "SW1:6" )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Difficulty ) )       PORT_DIPLOCATION("SW1:5")
	PORT_DIPSETTING(    0x08, DEF_STR( Easy ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Hard ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Bonus_Life ) )       PORT_DIPLOCATION("SW1:4")
	PORT_DIPSETTING(    0x10, "20K 60K 60K+" )
	PORT_DIPSETTING(    0x00, "20K" )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Lives ) )            PORT_DIPLOCATION("SW1:3")   /* Table at 0x9a28 */
	PORT_DIPSETTING(    0x20, "3" )
	PORT_DIPSETTING(    0x00, "5" )
	PORT_DIPNAME( 0xc0, 0xc0, DEF_STR( Coinage ) )          PORT_DIPLOCATION("SW1:1,2") /* Table at 0x0328 */
	PORT_DIPSETTING(    0x40, DEF_STR( 2C_1C ) )
	PORT_DIPSETTING(    0xc0, DEF_STR( 1C_1C ) )
	PORT_DIPSETTING(    0x80, DEF_STR( 1C_2C ) )
	PORT_DIPSETTING(    0x00, DEF_STR( 1C_6C ) )

	PORT_START("UNUSED")    /* This is read in ay8910_interface */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P1")        /* Spinner Player 1 */
	PORT_BIT( 0xff, 0x00, IPT_DIAL ) PORT_SENSITIVITY(30) PORT_KEYDELTA(15)

	PORT_START("P2")        /* Spinner Player 2  */
	PORT_BIT( 0xff, 0x00, IPT_DIAL ) PORT_SENSITIVITY(30) PORT_KEYDELTA(15) PORT_COCKTAIL
INPUT_PORTS_END

/* Different coinage and additional "Cabinet" Dip Switch */
static INPUT_PORTS_START( arkanoidj )
	PORT_INCLUDE( arkanoid )

	PORT_MODIFY("DSW")
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Coinage ) )      PORT_DIPLOCATION("SW1:2") /* Table at 0x0320 */
	PORT_DIPSETTING(    0x40, DEF_STR( 1C_1C ) )
	PORT_DIPSETTING(    0x00, DEF_STR( 1C_2C ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Cabinet ) )      PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(    0x00, DEF_STR( Upright ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Cocktail ) )
INPUT_PORTS_END

static INPUT_PORTS_START( ark1ball )
	PORT_INCLUDE( arkanoidj )

	PORT_MODIFY("DSW")
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Bonus_Life ) )   PORT_DIPLOCATION("SW1:4") /* "ld a,$60" at 0x93bd and "ld a,$60" at 0x9c7f and 0x9c9b */
	PORT_DIPSETTING(    0x10, "60K 100K 60K+" )
	PORT_DIPSETTING(    0x00, "60K" )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Lives ) )        PORT_DIPLOCATION("SW1:3") /* Table at 0x9a28 */
	PORT_DIPSETTING(    0x20, "1" )
	PORT_DIPSETTING(    0x00, "2" )
INPUT_PORTS_END

/* Bootlegs do not read from the MCU */
static INPUT_PORTS_START( arkatayt )
	PORT_INCLUDE( arkanoidj )

	PORT_MODIFY("SYSTEM")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_CUSTOM )        /* Some bootlegs need it to be 1 */
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM )       /* Some bootlegs need it to be 0 */
INPUT_PORTS_END

static INPUT_PORTS_START( arkangc )
	PORT_INCLUDE( arkatayt )

	PORT_MODIFY("DSW")
	PORT_DIPNAME( 0x01, 0x01, "Ball Speed" )            PORT_DIPLOCATION("SW1:8") /* Speed at 0xc462 (code at 0x18aa) - Also affects level 2 (code at 0x7b82) */
	PORT_DIPSETTING(    0x01, DEF_STR( Normal ) )       /* 0xc462 = 0x06 - Normal level 2 */
	PORT_DIPSETTING(    0x00, "Faster" )                /* 0xc462 = 0x08 - Level 2 same as level 30 */
INPUT_PORTS_END

static INPUT_PORTS_START( arkangc2 )
	PORT_INCLUDE( arkatayt )

	PORT_MODIFY("DSW")
	PORT_DIPNAME( 0x01, 0x01, "Ball Speed" )            PORT_DIPLOCATION("SW1:8") /* Speed at 0xc462 (code at 0x18aa) - Also affects level 2 (code at 0x7b82) */
	PORT_DIPSETTING(    0x01, "Slower" )                /* 0xc462 = 0x04 - Normal level 2 */
	PORT_DIPSETTING(    0x00, DEF_STR ( Normal ) )      /* 0xc462 = 0x06 - Level 2 same as level 30 */
INPUT_PORTS_END

static INPUT_PORTS_START( block2 )
	PORT_INCLUDE( arkatayt )

	PORT_MODIFY("DSW")
	PORT_DIPUNUSED_DIPLOC( 0x01, 0x01, "SW1:8" )        /* Speed at 0xc462 (code at 0x06fc) : always = 0x06 */
INPUT_PORTS_END

static INPUT_PORTS_START( arkgcbl )
	PORT_INCLUDE( arkatayt )

	PORT_MODIFY("DSW")
	PORT_DIPNAME( 0x01, 0x00, "Round Select" )          PORT_DIPLOCATION("SW1:8") /* Check code at 0x7bc2 - Speed at 0xc462 (code at 0x18aa) */
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )          /* 0xc462 = 0x06 */
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )           /* 0xc462 = 0x06 */
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Bonus_Life ) )   PORT_DIPLOCATION("SW1:4") /* "ld a,$60" at 0x93bd and "ld a,$20" at 0x9c7f and 0x9c9b */
	PORT_DIPSETTING(    0x10, "60K 100K 60K+" )         /* But "20K 60K 60K+" when continue */
	PORT_DIPSETTING(    0x00, "60K" )                   /* But "20K" when continue */
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Lives ) )        PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(    0x00, "2" )
	PORT_DIPSETTING(    0x20, "3" )
	PORT_DIPUNUSED_DIPLOC( 0x40, 0x40, "SW1:2" )        /* Always 2C_1C - check code at 0x7d5e */
INPUT_PORTS_END

static INPUT_PORTS_START( paddle2 )
	PORT_INCLUDE( arkatayt )

	PORT_MODIFY("DSW")
	PORT_DIPNAME( 0x01, 0x00, "Round Select" )          PORT_DIPLOCATION("SW1:8") /* Check code at 0x7bc2 - Speed at 0xc462 (code at 0x18aa) */
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )          /* 0xc462 = 0x06 */
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )           /* 0xc462 = 0x06 */
	PORT_DIPNAME( 0x04, 0x04, "Controls ?" )            PORT_DIPLOCATION("SW1:6") /* Check code at 0x96a1 and read notes */
	PORT_DIPSETTING(    0x04, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Alternate ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Bonus_Life ) )   PORT_DIPLOCATION("SW1:4") /* "ld a,$60" at 0x93bd and "ld a,$20" at 0x9c7f and 0x9c9b */
	PORT_DIPSETTING(    0x10, "60K 100K 60K+" )         /* But "20K 60K 60K+" when continue */
	PORT_DIPSETTING(    0x00, "60K" )                   /* But "20K" when continue */
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Lives ) )        PORT_DIPLOCATION("SW1:3") /* Table at 0x9a28 */
	PORT_DIPSETTING(    0x00, "2" )
	PORT_DIPSETTING(    0x20, "3" )
INPUT_PORTS_END

static INPUT_PORTS_START( arktayt2 )
	PORT_INCLUDE( arkatayt )

	PORT_MODIFY("DSW")
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Bonus_Life ) )   PORT_DIPLOCATION("SW1:4") /* "ld a,$60" at 0x93bd and "ld a,$60" at 0x9c7f and 0x9c9b */
	PORT_DIPSETTING(    0x10, "60K 100K 60K+" )
	PORT_DIPSETTING(    0x00, "60K" )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Lives ) )        PORT_DIPLOCATION("SW1:3") /* Table at 0x9a28 */
	PORT_DIPSETTING(    0x20, "2" )
	PORT_DIPSETTING(    0x00, "3" )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Coinage ) )      PORT_DIPLOCATION("SW1:2") /* Table at 0x0320 */
	PORT_DIPSETTING(    0x00, DEF_STR( 2C_1C ) )
	PORT_DIPSETTING(    0x40, DEF_STR( 1C_1C ) )
INPUT_PORTS_END


static INPUT_PORTS_START( tetrsark )
	PORT_START("SYSTEM")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("BUTTONS")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	/* Inputs are read by the ay8910. For simplicity, we use tags from other sets (even if not appropriate) */
	PORT_START("DSW")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) // or up? it rotates the piece.
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Cabinet ) )      /* Also affects numbers of players - read notes */
	PORT_DIPSETTING(    0x10, DEF_STR( Upright ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Cocktail ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0xc0, 0xc0, DEF_STR( Coinage ) )      /* Table at 0x0207 */
	PORT_DIPSETTING(    0xc0, DEF_STR( 1C_1C ) )
	PORT_DIPSETTING(    0x80, DEF_STR( 1C_2C ) )
	PORT_DIPSETTING(    0x40, DEF_STR( 1C_3C ) )
	PORT_DIPSETTING(    0x00, DEF_STR( 1C_5C ) )

	PORT_START("UNUSED")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) // or up? it rotates the piece.
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_START2 )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_COIN1 )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_COIN2 )
INPUT_PORTS_END


static INPUT_PORTS_START( hexa )
	PORT_START("INPUTS")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_4WAY
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_4WAY
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_4WAY
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_4WAY
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_COIN1 )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON2 )

	PORT_START("DSW")
	PORT_DIPNAME( 0x03, 0x03, DEF_STR( Coinage ) )  PORT_DIPLOCATION("SW1:1,2")
	PORT_DIPSETTING(    0x00, DEF_STR( 3C_1C ) )
	PORT_DIPSETTING(    0x01, DEF_STR( 2C_1C ) )
	PORT_DIPSETTING(    0x03, DEF_STR( 1C_1C ) )
	PORT_DIPSETTING(    0x02, DEF_STR( 1C_2C ) )
	PORT_DIPNAME( 0x04, 0x00, "Naughty Pics" )  PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Flip_Screen ) )  PORT_DIPLOCATION("SW1:4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x30, 0x30, "Difficulty?" )  PORT_DIPLOCATION("SW1:5,6")
	PORT_DIPSETTING(    0x30, "Easy?" )
	PORT_DIPSETTING(    0x20, "Medium?" )
	PORT_DIPSETTING(    0x10, "Hard?" )
	PORT_DIPSETTING(    0x00, "Hardest?" )
	PORT_DIPNAME( 0x40, 0x40, "Pobys" )  PORT_DIPLOCATION("SW1:7")
	PORT_DIPSETTING(    0x40, "2" )
	PORT_DIPSETTING(    0x00, "4" )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Demo_Sounds ) )  PORT_DIPLOCATION("SW1:8")
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( brixian )
	PORT_START("INPUTS")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_4WAY
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_4WAY
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_4WAY
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_4WAY
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_COIN1 )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON2 )

	PORT_START("DSW")
	PORT_DIPNAME( 0x03, 0x02, "Time Left" ) PORT_DIPLOCATION("SW1:1,2")
	PORT_DIPSETTING(    0x00, "More" )
	PORT_DIPSETTING(    0x01, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x02, "Normal (dupe)" )
	PORT_DIPSETTING(    0x03, "Less" )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown )  ) PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Speed of Elevator" ) PORT_DIPLOCATION("SW1:4")
	PORT_DIPSETTING(    0x00, "Fast" )
	PORT_DIPSETTING(    0x08, "Slow" )
	PORT_DIPNAME( 0xf0, 0x10, DEF_STR( Lives ) ) PORT_DIPLOCATION("SW1:5,6,7,8")
	PORT_DIPSETTING(    0xf0, "2" )
	PORT_DIPSETTING(    0x70, "3" )
	PORT_DIPSETTING(    0x30, "4" )
	PORT_DIPSETTING(    0x10, "5" )
	PORT_DIPSETTING(    0x00, "6" )
INPUT_PORTS_END

static INPUT_PORTS_START( cruisin5 )
	PORT_INCLUDE( arkatayt )

	PORT_MODIFY("DSW")
	PORT_DIPNAME( 0x01, 0x00, DEF_STR( Unused ) ) PORT_DIPLOCATION("SW1:8") // must be on?
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNUSED_DIPLOC( 0x02, 0x02, "SW1:7" )
	PORT_DIPNAME( 0x04, 0x00, "Loop" )            PORT_DIPLOCATION("SW1:6")
	PORT_DIPSETTING(    0x04, "Auto" )
	PORT_DIPSETTING(    0x00, "Manual" )
	PORT_DIPNAME( 0x08, 0x08, "??? Animation" )   PORT_DIPLOCATION("SW1:5")
	PORT_DIPSETTING(    0x08, "Squeeze Out" )
	PORT_DIPSETTING(    0x00, "Tear Apart" )
	PORT_DIPUNUSED_DIPLOC( 0x10, 0x10, "SW1:4" )
	PORT_DIPUNUSED_DIPLOC( 0x20, 0x20, "SW1:3" )
	PORT_DIPNAME( 0x40, 0x00, "Sound Hardware" )  PORT_DIPLOCATION("SW1:2")
	PORT_DIPSETTING(    0x40, "AY-3-8910" )
	PORT_DIPSETTING(    0x00, "YM2149" )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW1:1" )
INPUT_PORTS_END


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

/* Graphics Layouts */

static const gfx_layout charlayout =
{
	8,8,    /* 8*8 characters */
	4096,   /* 4096 characters */
	3,  /* 3 bits per pixel */
	{ 2*4096*8*8, 4096*8*8, 0 },    /* the two bitplanes are separated */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8 /* every char takes 8 consecutive bytes */
};

/* Graphics Decode Information */

static GFXDECODE_START( gfx_arkanoid )
	GFXDECODE_ENTRY( "gfx1", 0, charlayout,  0, 64 )
	// sprites use the same characters above, but are 16x8
GFXDECODE_END

static GFXDECODE_START( gfx_hexa )
	GFXDECODE_ENTRY( "gfx1", 0x0000, charlayout,  0 , 32 )
GFXDECODE_END

/* Machine Drivers */

void arkanoid_state::machine_start()
{
	save_item(NAME(m_gfxbank));
	save_item(NAME(m_palettebank));

	save_item(NAME(m_paddle_select));

	save_item(NAME(m_bootleg_id));
	save_item(NAME(m_bootleg_cmd));
}

void arkanoid_state::machine_reset()
{
	m_gfxbank = 0;
	m_palettebank = 0;

	m_paddle_select = 0;

	m_bootleg_cmd = 0;
}


void arkanoid_state::arkanoid(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 12_MHz_XTAL/2); /* verified on pcb */
	m_maincpu->set_addrmap(AS_PROGRAM, &arkanoid_state::arkanoid_map);

	WATCHDOG_TIMER(config, "watchdog").set_vblank_count("screen", 128); // 74LS393 at ic21, counts 128 vblanks before firing watchdog; z80 /RESET ls08 ic19 pin 9 input comes from ls04 ic20 pin 8, ls04 ic20 pin 9 input comes from ic21 ls393 pin 8, and ls393 is set to chain both 4 bit counters together

	ARKANOID_68705P5(config, m_mcuintf, 12_MHz_XTAL / 4); // verified on PCB
	m_mcuintf->portb_r_cb().set(FUNC(arkanoid_state::input_mux_r));

	config.set_maximum_quantum(attotime::from_hz(6000)); // 100 CPU slices per second to synchronize between the MCU and the main CPU

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(12_MHz_XTAL/2, 384, 0, 256, 264, 16, 240);
	m_screen->set_screen_update(FUNC(arkanoid_state::screen_update_arkanoid));
	m_screen->set_palette(m_palette);
	m_screen->screen_vblank().set_inputline(m_maincpu, 0, HOLD_LINE);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_arkanoid);
	PALETTE(config, m_palette, palette_device::RGB_444_PROMS, "proms", 512);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	ym2149_device &aysnd(YM2149(config, "aysnd", 12_MHz_XTAL/4)); /* YM2149 clock is 3mhz, pin 26 is low so final clock is 3mhz/2, handled inside the ay core */
	aysnd.set_flags(AY8910_SINGLE_OUTPUT | YM2149_PIN26_LOW); // all outputs are tied together with no resistors, and pin 26 is low
	aysnd.port_a_read_callback().set_ioport("UNUSED");
	aysnd.port_b_read_callback().set_ioport("DSW");
	aysnd.add_route(ALL_OUTPUTS, "mono", 0.66);
}

void arkanoid_state::p3mcu(machine_config &config)
{
	arkanoid(config);

	/* unprotected MCU */
	ARKANOID_68705P3(config.replace(), m_mcuintf, 12_MHz_XTAL / 4);
	m_mcuintf->portb_r_cb().set(FUNC(arkanoid_state::input_mux_r));
}

void arkanoid_state::p3mcuay(machine_config &config)
{
	p3mcu(config);

	ay8910_device &aysnd(AY8910(config.replace(), "aysnd", 12_MHz_XTAL/4)); // AY-3-8910A
	aysnd.set_flags(AY8910_SINGLE_OUTPUT);
	aysnd.port_a_read_callback().set_ioport("UNUSED");
	aysnd.port_b_read_callback().set_ioport("DSW");
	aysnd.add_route(ALL_OUTPUTS, "mono", 0.66);
}

void arkanoid_state::bootleg(machine_config &config)
{
	arkanoid(config);

	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &arkanoid_state::bootleg_map);

	config.device_remove("mcu");
}

void arkanoid_state::aysnd(machine_config &config)
{
	bootleg(config);

	ay8910_device &aysnd(AY8910(config.replace(), "aysnd", 12_MHz_XTAL/4));
	aysnd.set_flags(AY8910_SINGLE_OUTPUT);
	aysnd.port_a_read_callback().set_ioport("UNUSED");
	aysnd.port_b_read_callback().set_ioport("DSW");
	aysnd.add_route(ALL_OUTPUTS, "mono", 0.66);
}


void arkanoid_state::hexa(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 12_MHz_XTAL/2);  /* Imported from arkanoid - correct? */
	m_maincpu->set_addrmap(AS_PROGRAM, &arkanoid_state::hexa_map);

	WATCHDOG_TIMER(config, "watchdog");

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(12_MHz_XTAL/2, 384, 0, 256, 264, 16, 240);
	m_screen->set_screen_update(FUNC(arkanoid_state::screen_update_hexa));
	m_screen->set_palette(m_palette);
	m_screen->screen_vblank().set_inputline(m_maincpu, 0, HOLD_LINE);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_hexa);
	PALETTE(config, m_palette, palette_device::RGB_444_PROMS, "proms", 256);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	ay8910_device &aysnd(AY8910(config, "aysnd", 12_MHz_XTAL/4/2)); /* Imported from arkanoid - correct? */
	aysnd.port_a_read_callback().set_ioport("INPUTS");
	aysnd.port_b_read_callback().set_ioport("DSW");
	aysnd.add_route(ALL_OUTPUTS, "mono", 0.50);
}

void arkanoid_state::hexaa(machine_config &config)
{
	hexa(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &arkanoid_state::hexaa_map);

	z80_device &subcpu(Z80(config, "subcpu", 12_MHz_XTAL/2)); // ?
	subcpu.set_addrmap(AS_PROGRAM, &arkanoid_state::hexaa_sub_map);
	subcpu.set_addrmap(AS_IO, &arkanoid_state::hexaa_sub_iomap);
}


void arkanoid_state::brixian(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 12_MHz_XTAL/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &arkanoid_state::brixian_map);

	/* there is a 68705 but it's only role appears to be to copy data to RAM at startup */
	/* the RAM is also battery backed, making the 68705 almost redundant as long as the battery doesn't die(!) */

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(12_MHz_XTAL/2, 384, 0, 256, 264, 16, 240);
	m_screen->set_screen_update(FUNC(arkanoid_state::screen_update_hexa));
	m_screen->set_palette(m_palette);
	m_screen->screen_vblank().set_inputline(m_maincpu, 0, HOLD_LINE);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_arkanoid);
	PALETTE(config, m_palette, palette_device::RGB_444_PROMS, "proms", 512);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	ay8910_device &aysnd(AY8910(config, "aysnd", 12_MHz_XTAL/4/2)); /* Imported from arkanoid - correct? */
	aysnd.port_a_read_callback().set_ioport("INPUTS");
	aysnd.port_b_read_callback().set_ioport("DSW");
	aysnd.add_route(ALL_OUTPUTS, "mono", 0.50);
}

void arkanoid_state::cruisin5(machine_config &config)
{
	bootleg(config);

	// for sprite multiplexing, it expects interrupt at vblank end instead of vblank start?
	m_screen->set_video_attributes(VIDEO_UPDATE_SCANLINE);
	m_screen->screen_vblank().set_inputline(m_maincpu, 0, HOLD_LINE).invert();
}



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

/* ROMs */
/* rom numbering, with guesses for version numbers and missing roms:
    A75 01   = Z80 code 1/2 v1.0 Japan (NOT DUMPED; it has been rumored that arkatayt
               and arkangc and maybe arkanoidjbl might actually be bootlegs of this
               undumped version, so, if true, it might be possible to 'restore' this
               version by 'de-bootlegging' those sets.)
    A75 01-1 = Z80 code 1/2 v1.1 Japan and USA/Romstar and World
    A75 02   = Z80 code 2/2 v1.0 Japan (has 'Notice: This game is for use in Japan only' screen)
    A75 03   = GFX 1/3
    A75 04   = GFX 2/3
    A75 05   = GFX 3/3
    A75 06   = MC68705P5 MCU code, v1.x Japan and v1.x USA/Romstar (DUMPED,
               verified to have crc&sha1 of 0be83647 and 625fd1e6061123df612f115ef14a06cd6009f5d1;
               the rom with crc&sha1 of 4e44b50a and c61e7d158dc8e2b003c8158053ec139b904599af
               is also probably legit as well, only differing due to a
               different fill in an unused area from the verified one )
    A75 07   = PROM red
    A75 08   = PROM green
    A75 09   = PROM blue
    A75 10   = Z80 code 2/2 v1.0 USA/Romstar (has 'Licensed to Romstar for U.S.A' notice on title)
    A75 11   = Z80 code 2/2 v1.0 World
   (A75 12 through 14 & A75 16 & A75 17 are unknown, could be another two sets of z80 code plus mc68705p5)
    A75 15   = MC68705P5 MCU code, Phoenix Electronics Co. license (not dumped)
    A75 18   = Z80 code v2.0 2/2 USA/Romstar
    A75 19   = Z80 code v2.0 1/2 USA/Romstar
    A75 20   = MC68705P5 MCU code, v2.0 USA/Romstar (verified. dumped from MCU)
    A75 21   = Z80 code v2.0 1/2 Japan w/level select
    A75 22   = Z80 code v2.0 2/2 Japan w/level select
    A75 23   = MC68705P5 MCU code, v2.0 Japan w/level select (verified. dumped from MCU)
    A75 24   = Z80 code v2.1 1/2 Japan
    A75 25   = Z80 code v2.1 2/2 Japan
    A75 26   = MC68705P5 MCU code, v2.1 Japan (verified. dumped from MCU)
    A75 27   = Z80 code 1/2 Tournament
    A75 28   = Z80 code 2/2 Tournament
    A75 29   = GFX 1/3 Tournament
    A75 30   = GFX 2/3 Tournament
    A75 31   = GFX 3/3 Tournament
    A75 32   = MC68705P5 MCU code, Tournament (verified. dumped from MCU)
    A75 33   = PROM red Tournament
    A75 34   = PROM green Tournament
    A75 35   = PROM blue Tournament
    A75 36   = Z80 code 1/2 (Tournament v2.0?)
    A75 37   = Z80 code 2/2 (Tournament v2.0?)
    A75 38   = MC68705P5 MCU code, Tournament (v2.0?) (NOT DUMPED)
*/

ROM_START( arkanoid ) // v1.0 World
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "a75__01-1.ic17", 0x0000, 0x8000, CRC(5bcda3b0) SHA1(52cadd38b5f8e8856f007a9c602d6b508f30be65) )
	ROM_LOAD( "a75__11.ic16",   0x8000, 0x8000, CRC(eafd7191) SHA1(d2f8843b716718b1de209e97a874e8ce600f3f87) ) // v1.x, region byte is 0x33

	ROM_REGION( 0x0800, "mcu:mcu", 0 ) // 2k for the microcontroller
	ROM_LOAD( "a75__06.ic14",  0x0000, 0x0800, CRC(0be83647) SHA1(625fd1e6061123df612f115ef14a06cd6009f5d1) ) // verified authentic v1.x MCU from Taito/Romstar Board

	ROM_REGION( 0x18000, "gfx1", 0 ) // Silkscreen: "IC62 27128/256", "IC63 27128/256", "IC64 27128/256"
	ROM_LOAD( "a75__03.ic64", 0x00000, 0x8000, CRC(038b74ba) SHA1(ac053cc4908b4075f918748b89570e07a0ba5116) )
	ROM_LOAD( "a75__04.ic63", 0x08000, 0x8000, CRC(71fae199) SHA1(5d253c46ccf4cd2976a5fb8b8713f0f345443d06) )
	ROM_LOAD( "a75__05.ic62", 0x10000, 0x8000, CRC(c76374e2) SHA1(7520dd48de20db60a2038f134dcaa454988e7874) )

	ROM_REGION( 0x0600, "proms", 0 ) // Silkscreen: "IC22 7621", "IC23 7621", "IC24 7621", but the actual BPROMs used are either MMI 6306-1N or Fairchild MB7116E
	ROM_LOAD( "a75-07.ic24",   0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // Chip Silkscreen: "A75-07"; red component
	ROM_LOAD( "a75-08.ic23",   0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // Chip Silkscreen: "A75-08"; green component
	ROM_LOAD( "a75-09.ic22",   0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // Chip Silkscreen: "A75-09"; blue component

	// All of these MCUs work in place of A75 06, see comments for each.
	ROM_REGION( 0x1800, "alt_mcus", 0 ) /* 2k for the microcontroller */
	ROM_LOAD( "arkanoid_mcu.ic14", 0x0000, 0x0800, CRC(4e44b50a) SHA1(c61e7d158dc8e2b003c8158053ec139b904599af) ) // Decapped: See below
	/* This matches the legitimate Taito rom, with a "Programmed By Yasu 1986"
	   string in it, but has a 0x00 fill after the end of the code instead of
	   0xFF. This matches the legit rom otherwise and may itself be legit,
	   perhaps an artifact of a 68705 programmer at Taito using a sparse
	   s-record/ihex file and not clearing the ram in the chip programmer to
	   0xFF (or 0x00?) before programming the MCU.*/
	ROM_LOAD( "a75-06__bootleg_68705.ic14", 0x0800, 0x0800, CRC(515d77b6) SHA1(a302937683d11f663abd56a2fd7c174374e4d7fb) ) // NOT decapped: See below
	/* This came from an unprotected bootleg, and used to be used by the main
	   set. It is definitely a bootleg mcu with no timer or int selftest, and
	   completely different code altogether, probably implemented by pirates
	   by black-box reverse engineering the real MCU. */
	ROM_LOAD( "arkanoid1_68705p3.ic14", 0x1000, 0x0800, CRC(1b68e2d8) SHA1(f642a7cb624ee14fb0e410de5ae1fc799d2fa1c2) ) // Decapped: See below
	/* This is the same as the bootleg 515d77b6 rom above except the bootrom
	   (0x785-0x7f7) is intact. No other difference. */
ROM_END

ROM_START( arkanoidu ) // V2.0 US/Romstar
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "a75__19.ic17",  0x0000, 0x8000, CRC(d3ad37d7) SHA1(a172a1ef5bb83ee2d8ed2842ef8968af19ad411e) )
	ROM_LOAD( "a75__18.ic16",  0x8000, 0x8000, CRC(cdc08301) SHA1(05f54353cc8333af14fa985a2764960e20e8161a) ) // v2.0 USA, region byte is 0x92

	ROM_REGION( 0x0800, "mcu:mcu", 0 ) // 2k for the microcontroller
	ROM_LOAD( "a75__20.ic14",  0x0000, 0x0800, CRC(3994ee92) SHA1(31f6577956f49ba0b0705b490ce3254033795552) ) // verified authentic v2.0 MCU from Taito/Romstar US Board

	ROM_REGION( 0x18000, "gfx1", 0 ) // Silkscreen: "IC62 27128/256", "IC63 27128/256", "IC64 27128/256"
	ROM_LOAD( "a75__03.ic64", 0x00000, 0x8000, CRC(038b74ba) SHA1(ac053cc4908b4075f918748b89570e07a0ba5116) )
	ROM_LOAD( "a75__04.ic63", 0x08000, 0x8000, CRC(71fae199) SHA1(5d253c46ccf4cd2976a5fb8b8713f0f345443d06) )
	ROM_LOAD( "a75__05.ic62", 0x10000, 0x8000, CRC(c76374e2) SHA1(7520dd48de20db60a2038f134dcaa454988e7874) )

	ROM_REGION( 0x0600, "proms", 0 ) // Silkscreen: "IC22 7621", "IC23 7621", "IC24 7621", but the actual BPROMs used are either MMI 6306-1N or Fairchild MB7116E
	ROM_LOAD( "a75-07.ic24",   0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // Chip Silkscreen: "A75-07"; red component
	ROM_LOAD( "a75-08.ic23",   0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // Chip Silkscreen: "A75-08"; green component
	ROM_LOAD( "a75-09.ic22",   0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // Chip Silkscreen: "A75-09"; blue component
ROM_END

/* Observed on a real TAITO J1100075A pcb (with K1100181A sticker), pcb is white painted, and has a "ROMSTAR(C) // All Rights Reserved // Serial No. // No 14128" sticker */
ROM_START( arkanoiduo ) // V1.0 USA/Romstar
	ROM_REGION( 0x10000, "maincpu", 0 ) /* Silkscreen: "IC17 27256" and "IC16 27256" */
	ROM_LOAD( "a75__01-1.ic17", 0x0000, 0x8000, CRC(5bcda3b0) SHA1(52cadd38b5f8e8856f007a9c602d6b508f30be65) )
	ROM_LOAD( "a75__10.ic16",   0x8000, 0x8000, CRC(a1769e15) SHA1(fbb45731246a098b29eb08de5d63074b496aaaba) ) // v1.x, region byte is 0x92

	ROM_REGION( 0x0800, "mcu:mcu", 0 ) // 2k for the microcontroller
	ROM_LOAD( "a75__06.ic14",  0x0000, 0x0800, CRC(0be83647) SHA1(625fd1e6061123df612f115ef14a06cd6009f5d1) ) // verified authentic v1.x MCU from Taito/Romstar Board

	ROM_REGION( 0x18000, "gfx1", 0 ) // Silkscreen: "IC62 27128/256", "IC63 27128/256", "IC64 27128/256"
	ROM_LOAD( "a75__03.ic64", 0x00000, 0x8000, CRC(038b74ba) SHA1(ac053cc4908b4075f918748b89570e07a0ba5116) )
	ROM_LOAD( "a75__04.ic63", 0x08000, 0x8000, CRC(71fae199) SHA1(5d253c46ccf4cd2976a5fb8b8713f0f345443d06) )
	ROM_LOAD( "a75__05.ic62", 0x10000, 0x8000, CRC(c76374e2) SHA1(7520dd48de20db60a2038f134dcaa454988e7874) )

	ROM_REGION( 0x0600, "proms", 0 ) // Silkscreen: "IC22 7621", "IC23 7621", "IC24 7621", but the actual BPROMs used are either MMI 6306-1N or Fairchild MB7116E
	ROM_LOAD( "a75-07.ic24",   0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // Chip Silkscreen: "A75-07"; red component
	ROM_LOAD( "a75-08.ic23",   0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // Chip Silkscreen: "A75-08"; green component
	ROM_LOAD( "a75-09.ic22",   0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // Chip Silkscreen: "A75-09"; blue component

	ROM_REGION( 0x8000, "altgfx", 0 )
	ROM_LOAD( "a75__03_alternate.ic64",   0x00000, 0x8000, CRC(983d4485) SHA1(603a8798d1f531a70a527a5c6122f0ffd6adcfb6) ) // See below
	/* This was found on a legit v1.0 Romstar USA pcb with serial number 29342;
	   the only difference seems to be the first 32 tiles are all 0xFF instead
	   of 0x00. Those tiles don't seem to be used by the game at all. This is
	   likely another incident of "Taito forgot to clear programmer ram before
	   burning a rom from a sparse s-record/ihex file" */
ROM_END

ROM_START( arkanoidj ) // V2.1 Japan
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "a75__24.ic17",  0x0000, 0x8000, CRC(3f2b27e9) SHA1(656035f5292d6921448e74d3e1abab57b46e7d9e) )
	ROM_LOAD( "a75__25.ic16",  0x8000, 0x8000, CRC(c13b2038) SHA1(0b8197b48e57ffe9ccad0ebbc24891d1da7c9880) ) // v2.1? JPN, region byte is 0x76

	ROM_REGION( 0x0800, "mcu:mcu", 0 ) // 2k for the microcontroller
	ROM_LOAD( "a75__26.ic14",  0x0000, 0x0800, CRC(1c4d212b) SHA1(1df0dfbb3538de6bfddabcb6195efe67719e3f77) ) // verified authentic, dumped from actual MCU

	ROM_REGION( 0x18000, "gfx1", 0 ) // Silkscreen: "IC62 27128/256", "IC63 27128/256", "IC64 27128/256"
	ROM_LOAD( "a75__03.ic64", 0x00000, 0x8000, CRC(038b74ba) SHA1(ac053cc4908b4075f918748b89570e07a0ba5116) )
	ROM_LOAD( "a75__04.ic63", 0x08000, 0x8000, CRC(71fae199) SHA1(5d253c46ccf4cd2976a5fb8b8713f0f345443d06) )
	ROM_LOAD( "a75__05.ic62", 0x10000, 0x8000, CRC(c76374e2) SHA1(7520dd48de20db60a2038f134dcaa454988e7874) )

	ROM_REGION( 0x0600, "proms", 0 ) // Silkscreen: "IC22 7621", "IC23 7621", "IC24 7621", but the actual BPROMs used are either MMI 6306-1N or Fairchild MB7116E
	ROM_LOAD( "a75-07.ic24",   0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // Chip Silkscreen: "A75-07"; red component
	ROM_LOAD( "a75-08.ic23",   0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // Chip Silkscreen: "A75-08"; green component
	ROM_LOAD( "a75-09.ic22",   0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // Chip Silkscreen: "A75-09"; blue component
ROM_END

ROM_START( arkanoidja ) // V2.0 Japan w/level select
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "a75__21.ic17",  0x0000, 0x8000, CRC(bf0455fc) SHA1(250522b84b9f491c3f4efc391bf6aa6124361369) )
	ROM_LOAD( "a75__22.ic16",  0x8000, 0x8000, CRC(3a2688d3) SHA1(9633a661352def3d85f95ca830f6d761b0b5450e) ) // v2 JPN level select?, region byte is 0x92

	ROM_REGION( 0x0800, "mcu:mcu", 0 ) // 2k for the microcontroller
	ROM_LOAD( "a75__23.ic14",  0x0000, 0x0800, CRC(35938431) SHA1(04c6a888d967af0ceba06ab3cdd2670fe3bca396)  ) // verified authentic, dumped from actual MCU

	ROM_REGION( 0x18000, "gfx1", 0 ) // Silkscreen: "IC62 27128/256", "IC63 27128/256", "IC64 27128/256"
	ROM_LOAD( "a75__03.ic64", 0x00000, 0x8000, CRC(038b74ba) SHA1(ac053cc4908b4075f918748b89570e07a0ba5116) )
	ROM_LOAD( "a75__04.ic63", 0x08000, 0x8000, CRC(71fae199) SHA1(5d253c46ccf4cd2976a5fb8b8713f0f345443d06) )
	ROM_LOAD( "a75__05.ic62", 0x10000, 0x8000, CRC(c76374e2) SHA1(7520dd48de20db60a2038f134dcaa454988e7874) )

	ROM_REGION( 0x0600, "proms", 0 ) // Silkscreen: "IC22 7621", "IC23 7621", "IC24 7621", but the actual BPROMs used are either MMI 6306-1N or Fairchild MB7116E
	ROM_LOAD( "a75-07.ic24",   0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // Chip Silkscreen: "A75-07"; red component
	ROM_LOAD( "a75-08.ic23",   0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // Chip Silkscreen: "A75-08"; green component
	ROM_LOAD( "a75-09.ic22",   0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // Chip Silkscreen: "A75-09"; blue component
ROM_END

ROM_START( arkanoidjb ) // V1.1 Japan
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "a75__01-1.ic17", 0x0000, 0x8000, CRC(5bcda3b0) SHA1(52cadd38b5f8e8856f007a9c602d6b508f30be65) )
	ROM_LOAD( "a75__02.ic16",   0x8000, 0x8000, CRC(bbc33ceb) SHA1(e9b6fef98d0d20e77c7a1c25eff8e9a8c668a258) ) // v1.x, region byte is 0x76

	ROM_REGION( 0x0800, "mcu:mcu", 0 ) // 2k for the microcontroller
	ROM_LOAD( "a75__06.ic14",  0x0000, 0x0800, CRC(0be83647) SHA1(625fd1e6061123df612f115ef14a06cd6009f5d1) ) // verified authentic v1.x MCU from Taito/Romstar Board

	ROM_REGION( 0x18000, "gfx1", 0 ) // Silkscreen: "IC62 27128/256", "IC63 27128/256", "IC64 27128/256"
	ROM_LOAD( "a75__03.ic64", 0x00000, 0x8000, CRC(038b74ba) SHA1(ac053cc4908b4075f918748b89570e07a0ba5116) )
	ROM_LOAD( "a75__04.ic63", 0x08000, 0x8000, CRC(71fae199) SHA1(5d253c46ccf4cd2976a5fb8b8713f0f345443d06) )
	ROM_LOAD( "a75__05.ic62", 0x10000, 0x8000, CRC(c76374e2) SHA1(7520dd48de20db60a2038f134dcaa454988e7874) )

	ROM_REGION( 0x0600, "proms", 0 ) // Silkscreen: "IC22 7621", "IC23 7621", "IC24 7621", but the actual BPROMs used are either MMI 6306-1N or Fairchild MB7116E
	ROM_LOAD( "a75-07.ic24",   0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // Chip Silkscreen: "A75-07"; red component
	ROM_LOAD( "a75-08.ic23",   0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // Chip Silkscreen: "A75-08"; green component
	ROM_LOAD( "a75-09.ic22",   0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // Chip Silkscreen: "A75-09"; blue component
ROM_END

/* Observed on a genuine white TAITO J1100075A pcb and had a "License Sticker // Taito // TAITO CORPORATION // No 30223" sticker */
ROM_START( arkanoidpe ) // Licensed to Phoenix Electronics Co.
	ROM_REGION( 0x10000, "maincpu", 0 ) // round green labels with numbers
	ROM_LOAD( "21.ic17",    0x0000, 0x8000, CRC(746de487) SHA1(65370ac001e601086778309c5b6dc35b0cd53131) ) // FRI,  6 JUN 1986, 15:49 string in ROM
	ROM_LOAD( "22.ic16",    0x8000, 0x8000, CRC(bf784501) SHA1(32491746f71f95ac47737761032ae8884b6d9a8d) )

	ROM_REGION( 0x0800, "mcu:mcu", 0 ) // 2k for the microcontroller
	ROM_LOAD( "a75__15.ic14",   0x0000, 0x0800, CRC(d45327a9) SHA1(02ec8ecc818571caab2ecd93e6c5744cb4b7f559) ) // verified authentic, dumped from actual MCU

	ROM_REGION( 0x18000, "gfx1", 0 ) // round green labels with numbers, data matches standard Arkanoid sets
	ROM_LOAD( "23.ic64",    0x00000, 0x8000, CRC(038b74ba) SHA1(ac053cc4908b4075f918748b89570e07a0ba5116) ) // == a75-03.ic64
	ROM_LOAD( "24.ic63",    0x08000, 0x8000, CRC(71fae199) SHA1(5d253c46ccf4cd2976a5fb8b8713f0f345443d06) ) // == a75-04.ic63
	ROM_LOAD( "25.ic62",    0x10000, 0x8000, CRC(c76374e2) SHA1(7520dd48de20db60a2038f134dcaa454988e7874) ) // == a75-05.ic62

	ROM_REGION( 0x0600, "proms", 0 ) // Silkscreen: "IC22 7621", "IC23 7621", "IC24 7621", but the actual BPROMs used are either MMI 6306-1N or Fairchild MB7116E
	ROM_LOAD( "a75-07.ic24", 0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // Chip Silkscreen: "A75-07"; red component
	ROM_LOAD( "a75-08.ic23", 0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // Chip Silkscreen: "A75-08"; green component
	ROM_LOAD( "a75-09.ic22", 0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // Chip Silkscreen: "A75-09"; blue component
ROM_END


ROM_START( arkatour ) // Tournament version
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "a75__27.ic17",  0x0000, 0x8000, CRC(e3b8faf5) SHA1(4c09478fa41881fa89ee6afb676aeb780f17ac2e) )
	ROM_LOAD( "a75__28.ic16",  0x8000, 0x8000, CRC(326aca4d) SHA1(5a194b7a0361236d471b24905dc6434372f81252) )  // region byte is 0x92

	ROM_REGION( 0x0800, "mcu:mcu", 0 ) // 2k for the microcontroller
	ROM_LOAD( "a75__32.ic14", 0x0000, 0x0800, CRC(8c20d15c) SHA1(912996bf08de318e19dc420261f554a09dacd443)  ) // verified authentic, dumped from actual MCU

	ROM_REGION( 0x18000, "gfx1", 0 ) // Silkscreen: "IC62 27128/256", "IC63 27128/256", "IC64 27128/256"
	ROM_LOAD( "a75__29.ic64", 0x00000, 0x8000, CRC(5ddea3cf) SHA1(58f16515898b7cc2697bf7663a60d9ca0db6da95) )
	ROM_LOAD( "a75__30.ic63", 0x08000, 0x8000, CRC(5fcf2e85) SHA1(f721f0afb0550cc64bff26681856a7576398d9b5) )
	ROM_LOAD( "a75__31.ic62", 0x10000, 0x8000, CRC(7b76b192) SHA1(a68aa08717646a6c322cf3455df07f50df9e9f33) )

	ROM_REGION( 0x0600, "proms", 0 ) //  BPROMs are silkscreened as 7621, actual BPROMs used are MMI 6306-1N
	ROM_LOAD( "a75-33.ic24",   0x0000, 0x0200, CRC(b4bf3c81) SHA1(519188937ac9728c653fabac877e37dc43c3f71a) ) // Chip Silkscreen: "A75-33"; red component
	ROM_LOAD( "a75-34.ic23",   0x0200, 0x0200, CRC(de85a803) SHA1(325214995996de36a0470fbfc00e4e393c0b17ad) ) // Chip Silkscreen: "A75-34"; green component
	ROM_LOAD( "a75-35.ic22",   0x0400, 0x0200, CRC(38acfd3b) SHA1(2841e9db047aa039eff8567a518b6250b355507b) ) // Chip Silkscreen: "A75-35"; blue component
ROM_END


ROM_START( arkatour2 ) // Tournament version, newer
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "a75__36.ic17",  0x0000, 0x8000, CRC(f3b1923e) SHA1(377a417ad99c9de2eadad7338bc2c7e83d08cb2f) )
	ROM_LOAD( "a75__37.ic16",  0x8000, 0x8000, CRC(7c74987b) SHA1(fb7612ea2af87a3e1bc794c25b004881930d4dc8) ) // 1xxxxxxxxxxxxxx = 0xFF

	ROM_REGION( 0x0800, "mcu:mcu", 0 ) // 2k for the microcontroller
	ROM_LOAD( "a75__38.ic14",  0x0000, 0x0800, NO_DUMP )

	ROM_REGION( 0x18000, "gfx1", 0 ) // Silkscreen: "IC62 27128/256", "IC63 27128/256", "IC64 27128/256"
	ROM_LOAD( "a75__29.ic64", 0x00000, 0x8000, CRC(5ddea3cf) SHA1(58f16515898b7cc2697bf7663a60d9ca0db6da95) )
	ROM_LOAD( "a75__30.ic63", 0x08000, 0x8000, CRC(5fcf2e85) SHA1(f721f0afb0550cc64bff26681856a7576398d9b5) )
	ROM_LOAD( "a75__31.ic62", 0x10000, 0x8000, CRC(7b76b192) SHA1(a68aa08717646a6c322cf3455df07f50df9e9f33) )

	ROM_REGION( 0x0600, "proms", 0 ) //  BPROMs are silkscreened as 7621, actual BPROMs used are MMI 6306-1N
	ROM_LOAD( "a75-33.ic24",   0x0000, 0x0200, CRC(b4bf3c81) SHA1(519188937ac9728c653fabac877e37dc43c3f71a) ) // Chip Silkscreen: "A75-33"; red component
	ROM_LOAD( "a75-34.ic23",   0x0200, 0x0200, CRC(de85a803) SHA1(325214995996de36a0470fbfc00e4e393c0b17ad) ) // Chip Silkscreen: "A75-34"; green component
	ROM_LOAD( "a75-35.ic22",   0x0400, 0x0200, CRC(38acfd3b) SHA1(2841e9db047aa039eff8567a518b6250b355507b) ) // Chip Silkscreen: "A75-35"; blue component
ROM_END

// Everything from here on is bootlegs

ROM_START( arkanoidjbl ) // bootleg with MCU copied from real Taito code, but notice screen hacked up.
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "e1.6d",        0x0000, 0x8000, CRC(dd4f2b72) SHA1(399a8636030a702dafc1da926f115df6f045bef1) ) /* Hacked up Notice warning text, no other changes from a75-01-1.ic17 */
	ROM_LOAD( "e2.6f",        0x8000, 0x8000, CRC(bbc33ceb) SHA1(e9b6fef98d0d20e77c7a1c25eff8e9a8c668a258) ) /* == A75-02.IC16 */

	ROM_REGION( 0x0800, "mcu:mcu", 0 ) // 2k for the microcontroller
	ROM_LOAD( "68705p3.6i",   0x0000, 0x0800, CRC(389a8cfb) SHA1(9530c051b61b5bdec7018c6fdc1ea91288a406bd) ) // See below
	/* This set had an unprotected mcu with a bootlegged copy of the real Taito
	   a75__06.ic14 code, unlike the other bootlegs. It has the bootstrap code
	   missing and the security bit cleared, the area after the rom filled with
	   0x00, and the verify mode disable jump removed. Otherwise it matches
	   a75__06.ic14 */

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "a75-03.rom",   0x00000, 0x8000, CRC(038b74ba) SHA1(ac053cc4908b4075f918748b89570e07a0ba5116) )
	ROM_LOAD( "a75-04.rom",   0x08000, 0x8000, CRC(71fae199) SHA1(5d253c46ccf4cd2976a5fb8b8713f0f345443d06) )
	ROM_LOAD( "a75-05.rom",   0x10000, 0x8000, CRC(c76374e2) SHA1(7520dd48de20db60a2038f134dcaa454988e7874) )

	ROM_REGION( 0x0600, "proms", 0 )
	ROM_LOAD( "a75-07.bpr",    0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // red component
	ROM_LOAD( "a75-08.bpr",    0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // green component
	ROM_LOAD( "a75-09.bpr",    0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // blue component
ROM_END

// this pcb has the coin lockout module bypassed with some resistors

ROM_START( arkanoidjbl2 ) // Bootleg with 'beta corporation' copyright, japan notice hacked out, ??? MCU, probably the a75-06__bootleg_68705.ic14 515d77b6 one
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "1.ic81", 0x0000, 0x8000, CRC(9ff93dc2) SHA1(eee0975b799a8e6717f646dd40716dc454476106) ) /* Hacked up Notice warning text, plus all TAITO in rom are patched to BETA, no other changes from a75-01-1.ic17 */
	ROM_LOAD( "2.ic82", 0x8000, 0x8000, CRC(bbc33ceb) SHA1(e9b6fef98d0d20e77c7a1c25eff8e9a8c668a258) ) /* == A75-02.IC16 */

	ROM_REGION( 0x0800, "mcu:mcu", 0 ) // 2k for the microcontroller
	ROM_LOAD( "a75-06__bootleg_68705.ic14",  0x0000, 0x0800, BAD_DUMP CRC(515d77b6) SHA1(a302937683d11f663abd56a2fd7c174374e4d7fb) ) /* Uses the bootleg MCU? Not sure what mcu is supposed to go with this set... 6.ic84? */

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "3.ic33", 0x00000, 0x8000, CRC(038b74ba) SHA1(ac053cc4908b4075f918748b89570e07a0ba5116) ) // = a75-03.ic64
	ROM_LOAD( "4.ic34", 0x08000, 0x8000, CRC(71fae199) SHA1(5d253c46ccf4cd2976a5fb8b8713f0f345443d06) ) // = a75-04.ic63
	ROM_LOAD( "5.ic35", 0x10000, 0x8000, CRC(c76374e2) SHA1(7520dd48de20db60a2038f134dcaa454988e7874) ) // = a75-05.ic62

	ROM_REGION( 0x0600, "proms", 0 ) //  BPROMs are silkscreened as 7621, actual BPROMs used are MMI 6306-1N
	ROM_LOAD( "a75-07.bpr", 0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // red component, ic75?
	ROM_LOAD( "a75-08.bpr", 0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // green component, ic74?
	ROM_LOAD( "a75-09.bpr", 0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // blue component, ic73?
ROM_END

// this pcb uses a '7-30' module to replace the PC030CM coin lockout driver
// https://r.mprd.se/MAME/pcb/ark1ball.png
ROM_START( ark1ball ) /* This set requires a MCU. No MCU rom was supplied so we use the a75-06__bootleg_68705.ic14 515d77b6 one for now */
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "a-1.7d",       0x0000, 0x8000, CRC(dd4f2b72) SHA1(399a8636030a702dafc1da926f115df6f045bef1) ) /* == e1.6d from arkanoidjbl; Hacked up Notice warning text, no other changes from a75-01-1.ic17 */
	ROM_LOAD( "ark_2__1_palline.7f",  0x8000, 0x8000, CRC(ed6b62ab) SHA1(4d4991b422756bd304fc5ef236aac1422fe1f999) ) /* "1 palline" = "1 balls"; different handwritten label from other chips on the pcb, likely an operator hack of a bootleg arkanoidjbl or jbl2 pcb */

	/* Use the current A75-06.IC14 MCU code so the game is playable */
	ROM_REGION( 0x0800, "mcu:mcu", 0 ) // 2k for the microcontroller
	ROM_LOAD( "a75-06__bootleg_68705.ic14",  0x0000, 0x0800, BAD_DUMP CRC(515d77b6) SHA1(a302937683d11f663abd56a2fd7c174374e4d7fb) ) /* Uses the bootleg MCU? Not sure what mcu is supposed to go with this set... */

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "a-3.3a",       0x00000, 0x8000, CRC(038b74ba) SHA1(ac053cc4908b4075f918748b89570e07a0ba5116) ) // = a75-03.ic64
	ROM_LOAD( "a-4.3d",       0x08000, 0x8000, CRC(71fae199) SHA1(5d253c46ccf4cd2976a5fb8b8713f0f345443d06) ) // = a75-04.ic63
	ROM_LOAD( "a-5.3f",       0x10000, 0x8000, CRC(c76374e2) SHA1(7520dd48de20db60a2038f134dcaa454988e7874) ) // = a75-05.ic62

	ROM_REGION( 0x0600, "proms", 0 )
	ROM_LOAD( "a75-07.bpr",    0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // red component
	ROM_LOAD( "a75-08.bpr",    0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // green component
	ROM_LOAD( "a75-09.bpr",    0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // blue component
ROM_END

ROM_START( arkangc ) // Game Corporation set with no mcu, d008 read after reading paddle at d018 patched out or not present
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "arkgc.1",      0x0000, 0x8000, CRC(c54232e6) SHA1(beb759cee68009a06824b755d2aa26d7d436b5b0) )
	ROM_LOAD( "arkgc.2",      0x8000, 0x8000, CRC(9f0d4754) SHA1(731c9224616a338084edd6944c754d68eabba7f2) )

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "a75-03.rom",   0x00000, 0x8000, CRC(038b74ba) SHA1(ac053cc4908b4075f918748b89570e07a0ba5116) )
	ROM_LOAD( "a75-04.rom",   0x08000, 0x8000, CRC(71fae199) SHA1(5d253c46ccf4cd2976a5fb8b8713f0f345443d06) )
	ROM_LOAD( "a75-05.rom",   0x10000, 0x8000, CRC(c76374e2) SHA1(7520dd48de20db60a2038f134dcaa454988e7874) )

	ROM_REGION( 0x0600, "proms", 0 )
	ROM_LOAD( "a75-07.bpr",    0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // red component
	ROM_LOAD( "a75-08.bpr",    0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // green component
	ROM_LOAD( "a75-09.bpr",    0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // blue component
ROM_END

ROM_START( arkangc2 ) // Game Corporation set with no mcu, has d008 read after reading paddle at d018, and bit 1 must be set; this is an older version of arkangc above, the level select is moved around a bit
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "1.81",         0x0000, 0x8000, CRC(bd6eb996) SHA1(a048ff01156166595dca0b6bee46344f7db548a8) )
	ROM_LOAD( "2.82",         0x8000, 0x8000, CRC(29dbe452) SHA1(b99cb98549bddf1e673e2e715c80664001581f9f) )

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "a75-03.rom",   0x00000, 0x8000, CRC(038b74ba) SHA1(ac053cc4908b4075f918748b89570e07a0ba5116) )
	ROM_LOAD( "a75-04.rom",   0x08000, 0x8000, CRC(71fae199) SHA1(5d253c46ccf4cd2976a5fb8b8713f0f345443d06) )
	ROM_LOAD( "a75-05.rom",   0x10000, 0x8000, CRC(c76374e2) SHA1(7520dd48de20db60a2038f134dcaa454988e7874) )

	ROM_REGION( 0x0600, "proms", 0 )
	ROM_LOAD( "a75-07.bpr",    0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // red component
	ROM_LOAD( "a75-08.bpr",    0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // green component
	ROM_LOAD( "a75-09.bpr",    0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // blue component
ROM_END

// This set (block2) and the next one (arkblock3) have the same 'space invader' scrambled block gfx, a complex protection ?device?, and an oki sample rom.
ROM_START( block2 ) // derived from 95% arkangc2, 5% arkangc
	ROM_REGION( 0x18000, "maincpu", 0 )
	ROM_LOAD( "1.bin",         0x00000, 0x8000, CRC(2b026cae) SHA1(73d1d5d3e6d65fbe378ce85ff501610573ae5e95) )
	ROM_LOAD( "2.bin",         0x08000, 0x8000, CRC(e3843fea) SHA1(8c654dcf78d9e4f4c6a7a7d384fdf622536234c1) )

	ROM_REGION( 0x8000, "unknown", 0 )  /* oki/vox format sample data, played by the same protection mess (simulated in machine/arkanoid.cpp) somehow? */
	ROM_LOAD( "3.bin",         0x00000, 0x8000, CRC(e336c219) SHA1(e1dce37727e7084a83e73f15a138312ab6224061) )

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "4.bin",   0x00000, 0x8000, CRC(6d2c6123) SHA1(26f32099d363ab2c8505722513638b827e49a8fc) )
	ROM_LOAD( "5.bin",   0x08000, 0x8000, CRC(09a1f9d9) SHA1(c7e21aba6efb51c5501aa1428f6d9a817cb86555) )
	ROM_LOAD( "6.bin",   0x10000, 0x8000, CRC(dfb9f7e2) SHA1(8d938ee6f8dcac0a564d5fa7cd5da34e0db07c71) )

	// no proms were present in this set.. assumed to be the same
	ROM_REGION( 0x0600, "proms", 0 )
	ROM_LOAD( "a75-07.bpr",    0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // red component
	ROM_LOAD( "a75-08.bpr",    0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // green component
	ROM_LOAD( "a75-09.bpr",    0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // blue component
ROM_END

// see comment for 'block2' set
ROM_START( arkbloc3 )
	ROM_REGION( 0x18000, "maincpu", 0 )
	ROM_LOAD( "blockbl.001",         0x00000, 0x8000, CRC(bf7197a0) SHA1(4fbc0cbc09d292ab0f2e4a35b30505b2f7e4dc0d) )
	ROM_LOAD( "blockbl.002",         0x08000, 0x8000, CRC(29dbe452) SHA1(b99cb98549bddf1e673e2e715c80664001581f9f) )

	ROM_REGION( 0x8000, "unknown", 0 )  /* oki/vox format sample data, played by the same protection mess (simulated in machine/arkanoid.cpp) somehow? */
	ROM_LOAD( "blockbl.006",         0x00000, 0x8000, CRC(e336c219) SHA1(e1dce37727e7084a83e73f15a138312ab6224061) )

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "blockbl.003",   0x00000, 0x8000, CRC(6d2c6123) SHA1(26f32099d363ab2c8505722513638b827e49a8fc) )
	ROM_LOAD( "blockbl.004",   0x08000, 0x8000, CRC(09a1f9d9) SHA1(c7e21aba6efb51c5501aa1428f6d9a817cb86555) )
	ROM_LOAD( "blockbl.005",   0x10000, 0x8000, CRC(dfb9f7e2) SHA1(8d938ee6f8dcac0a564d5fa7cd5da34e0db07c71) )

	// no proms were present in this set.. assumed to be the same
	ROM_REGION( 0x0600, "proms", 0 )
	ROM_LOAD( "a75-07.bpr",    0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // red component
	ROM_LOAD( "a75-08.bpr",    0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // green component
	ROM_LOAD( "a75-09.bpr",    0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // blue component
ROM_END

ROM_START( arkblock ) // no mcu, no d008/d018/f000/f002 protection, just leftover writes
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "ark-6.bin",    0x0000, 0x8000, CRC(0be015de) SHA1(f4209085b59d2c96a62ac9657c7bf097da55362b) )
	ROM_LOAD( "arkgc.2",      0x8000, 0x8000, CRC(9f0d4754) SHA1(731c9224616a338084edd6944c754d68eabba7f2) )

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "a75-03.rom",   0x00000, 0x8000, CRC(038b74ba) SHA1(ac053cc4908b4075f918748b89570e07a0ba5116) )
	ROM_LOAD( "a75-04.rom",   0x08000, 0x8000, CRC(71fae199) SHA1(5d253c46ccf4cd2976a5fb8b8713f0f345443d06) )
	ROM_LOAD( "a75-05.rom",   0x10000, 0x8000, CRC(c76374e2) SHA1(7520dd48de20db60a2038f134dcaa454988e7874) )

	ROM_REGION( 0x0600, "proms", 0 )
	ROM_LOAD( "a75-07.bpr",    0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // red component
	ROM_LOAD( "a75-08.bpr",    0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // green component
	ROM_LOAD( "a75-09.bpr",    0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // blue component
ROM_END

ROM_START( arkbloc2 ) // no mcu, no d008/d018/f000/f002 protection, just leftover writes
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "block01.bin",  0x0000, 0x8000, CRC(5be667e1) SHA1(fbc5c97d836c404a2e6c007c3836e36b52ae75a1) )
	ROM_LOAD( "block02.bin",  0x8000, 0x8000, CRC(4f883ef1) SHA1(cb090a57fc75f17a3e2ba637f0e3ec93c1d02cea) )

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "a75-03.rom",   0x00000, 0x8000, CRC(038b74ba) SHA1(ac053cc4908b4075f918748b89570e07a0ba5116) )
	ROM_LOAD( "a75-04.rom",   0x08000, 0x8000, CRC(71fae199) SHA1(5d253c46ccf4cd2976a5fb8b8713f0f345443d06) )
	ROM_LOAD( "a75-05.rom",   0x10000, 0x8000, CRC(c76374e2) SHA1(7520dd48de20db60a2038f134dcaa454988e7874) )

	ROM_REGION( 0x0600, "proms", 0 )
	ROM_LOAD( "a75-07.bpr",    0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // red component
	ROM_LOAD( "a75-08.bpr",    0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // green component
	ROM_LOAD( "a75-09.bpr",    0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // blue component
ROM_END


/* arkgcbl - dump from citylan:

Anno 1986
N.revisione 06.09.86
CPU:
1x MK3880N-4-Z80CPU (main)
1x AY-3-8910A (sound)
1x oscillator 12.000MHz
ROMs:
5x TNS27256JL
5x PROM N82S129N
1x PROM MMI63S141N
Note:
1x 28x2 EDGE connector (not Jamma)
1x trimmer (volume)
1x 8 switches dip
Dumped 19/03/2006 */

ROM_START( arkgcbl ) // similar to arkangc, but has added d008/d018/f000/f002 protection, likely using the PAL16R8
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "electric__16.6e",        0x0000, 0x8000, CRC(b0f73900) SHA1(2c9a36cc1d2a3f33ec81d63c1c325554b818d2d3) )
	ROM_LOAD( "electric__17.6f",        0x8000, 0x8000, CRC(9827f297) SHA1(697874e73e045eb5a7bf333d7310934b239c0adf) )

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "electric__18.3a",   0x00000, 0x8000, CRC(038b74ba) SHA1(ac053cc4908b4075f918748b89570e07a0ba5116) ) // = a75-03.ic64
	ROM_LOAD( "electric__19.3c",   0x08000, 0x8000, CRC(71fae199) SHA1(5d253c46ccf4cd2976a5fb8b8713f0f345443d06) ) // = a75-04.ic63
	ROM_LOAD( "electric__20.3d",   0x10000, 0x8000, CRC(c76374e2) SHA1(7520dd48de20db60a2038f134dcaa454988e7874) ) // = a75-05.ic62

	ROM_REGION( 0x0600, "proms", 0 )
	ROM_LOAD( "82s129.5k",    0x0000, 0x0100, CRC(fa70b64d) SHA1(273669d05f793cf1ee0741b175be281307fa9b5e) )    /* red component   + */
	ROM_LOAD( "82s129.5jk",   0x0100, 0x0100, CRC(cca69884) SHA1(fdcd66110c8eb901a401f8618821c7980946a511) )    /* red component   = a75-07.ic24*/
	ROM_LOAD( "82s129.5l",    0x0200, 0x0100, CRC(3e4d2bf5) SHA1(c475887302dd137d6965769070b7d55f488c1b25) )    /* green component + */
	ROM_LOAD( "82s129.5kl",   0x0300, 0x0100, CRC(085d625a) SHA1(26c96a1c1b7562fed84c31dd92fdf7829e96a9c7) )    /* green component = a75-08.ic23*/
	ROM_LOAD( "82s129.5mn",   0x0400, 0x0100, CRC(0fe0b108) SHA1(fcf27619208922345a1e42b3a219b4274f66968d) )    /* blue component  + */
	ROM_LOAD( "63s141.5m",    0x0500, 0x0100, CRC(5553f675) SHA1(c50255af8d99664b92e0bb34a527fd42ebf7e759) )    /* blue component  = a75-09.ic22*/

	ROM_REGION( 0x0200, "pal", 0 )
	ROM_LOAD( "pal16r8.5f",   0x0000, 0x0104, CRC(36471917) SHA1(d0f295a94d480b44416e66be4b480b299aad5c3c) )    /* likely used for the d008/d018/f000/f002 protection */
ROM_END

/* this one still has the original copyright intact */
ROM_START( arkgcbla ) // similar to arkangc, but has added d008/d018/f000/f002 protection, likely using the PAL16R8
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "k101.e7",        0x0000, 0x8000, CRC(892a556e) SHA1(10d1a92f8ab1b8184b05182a2de070b163a603e2) )
	ROM_LOAD( "k102.f7",        0x8000, 0x8000, CRC(d208d05c) SHA1(0aa99a0cb8211e7b90d681c91cc77aa7078a0ccc) )

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "a75-03.rom",   0x00000, 0x8000, CRC(038b74ba) SHA1(ac053cc4908b4075f918748b89570e07a0ba5116) ) // = a75-03.ic64
	ROM_LOAD( "a75-04.rom",   0x08000, 0x8000, CRC(71fae199) SHA1(5d253c46ccf4cd2976a5fb8b8713f0f345443d06) ) // = a75-04.ic63
	ROM_LOAD( "a75-05.rom",   0x10000, 0x8000, CRC(c76374e2) SHA1(7520dd48de20db60a2038f134dcaa454988e7874) ) // = a75-05.ic62

	ROM_REGION( 0x0600, "proms", 0 )
	ROM_LOAD( "82s129.5k",    0x0000, 0x0100, CRC(fa70b64d) SHA1(273669d05f793cf1ee0741b175be281307fa9b5e) )    /* red component   + */
	ROM_LOAD( "82s129.5jk",   0x0100, 0x0100, CRC(cca69884) SHA1(fdcd66110c8eb901a401f8618821c7980946a511) )    /* red component   = a75-07.bpr*/
	ROM_LOAD( "82s129.5l",    0x0200, 0x0100, CRC(3e4d2bf5) SHA1(c475887302dd137d6965769070b7d55f488c1b25) )    /* green component + */
	ROM_LOAD( "82s129.5kl",   0x0300, 0x0100, CRC(085d625a) SHA1(26c96a1c1b7562fed84c31dd92fdf7829e96a9c7) )    /* green component = a75-08.bpr*/
	ROM_LOAD( "82s129.5mn",   0x0400, 0x0100, CRC(0fe0b108) SHA1(fcf27619208922345a1e42b3a219b4274f66968d) )    /* blue component  + */
	ROM_LOAD( "63s141.5m",    0x0500, 0x0100, CRC(5553f675) SHA1(c50255af8d99664b92e0bb34a527fd42ebf7e759) )    /* blue component  = a75-09.bpr*/

	ROM_REGION( 0x0200, "pal", 0 )
	ROM_LOAD( "pal16r8.5f",   0x0000, 0x0104, CRC(36471917) SHA1(d0f295a94d480b44416e66be4b480b299aad5c3c) )    /* likely used for the d008/d018/f000/f002 protection */
ROM_END


ROM_START( paddle2 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "paddle2.16",   0x0000, 0x8000, CRC(a286333c) SHA1(0b2c9cb0df236f327413d0c541453e1ba979ea38) )
	ROM_LOAD( "paddle2.17",   0x8000, 0x8000, CRC(04c2acb5) SHA1(7ce8ba31224f705b2b6ed0200404ef5f8f688001) )

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "a75-03.rom",   0x00000, 0x8000, CRC(038b74ba) SHA1(ac053cc4908b4075f918748b89570e07a0ba5116) )
	ROM_LOAD( "a75-04.rom",   0x08000, 0x8000, CRC(71fae199) SHA1(5d253c46ccf4cd2976a5fb8b8713f0f345443d06) )
	ROM_LOAD( "a75-05.rom",   0x10000, 0x8000, CRC(c76374e2) SHA1(7520dd48de20db60a2038f134dcaa454988e7874) )

	ROM_REGION( 0x0600, "proms", 0 )
	ROM_LOAD( "a75-07.bpr",    0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // red component
	ROM_LOAD( "a75-08.bpr",    0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // green component
	ROM_LOAD( "a75-09.bpr",    0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // blue component
ROM_END

ROM_START( arkatayt )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "ic81-v.3f",   0x0000, 0x8000, CRC(154e2c6f) SHA1(dce3ae1ca83b5071ebec96f3ae18b96abe828ce5) )
	ROM_LOAD( "ic82-w.5f",   0x8000, 0x8000, CRC(4fa8cefa) SHA1(fb825834da9c8638e6a328784922b5dc23f16564) )

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "1-ic33.2c",   0x00000, 0x8000, CRC(038b74ba) SHA1(ac053cc4908b4075f918748b89570e07a0ba5116) )
	ROM_LOAD( "2-ic34.3c",   0x08000, 0x8000, CRC(71fae199) SHA1(5d253c46ccf4cd2976a5fb8b8713f0f345443d06) )
	ROM_LOAD( "3-ic35.5c",   0x10000, 0x8000, CRC(c76374e2) SHA1(7520dd48de20db60a2038f134dcaa454988e7874) )

	ROM_REGION( 0x0600, "proms", 0 )
	ROM_LOAD( "ic73.11e",    0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // red component
	ROM_LOAD( "ic74.12e",    0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // green component
	ROM_LOAD( "ic75.13e",    0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // blue component
ROM_END

ROM_START( arktayt2 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "ic81.3f",     0x0000, 0x8000, CRC(6e0a2b6f) SHA1(5227d7a944cb1e815f60ec87a67f7462870ff9fe) )
	ROM_LOAD( "ic82.5f",     0x8000, 0x8000, CRC(5a97dd56) SHA1(b71c7b5ced2b0eebbcc5996dd21a1bb1c2da4819) )

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "1-ic33.2c",   0x00000, 0x8000, CRC(038b74ba) SHA1(ac053cc4908b4075f918748b89570e07a0ba5116) )
	ROM_LOAD( "2-ic34.3c",   0x08000, 0x8000, CRC(71fae199) SHA1(5d253c46ccf4cd2976a5fb8b8713f0f345443d06) )
	ROM_LOAD( "3-ic35.5c",   0x10000, 0x8000, CRC(c76374e2) SHA1(7520dd48de20db60a2038f134dcaa454988e7874) )

	ROM_REGION( 0x0600, "proms", 0 )
	ROM_LOAD( "ic73.11e",    0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // red component
	ROM_LOAD( "ic74.12e",    0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // green component
	ROM_LOAD( "ic75.13e",    0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // blue component
ROM_END

ROM_START( arkaboot ) // most similar to arkanoidjb, with hacked out MCU protection
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "1", 0x0000, 0x8000, CRC(3234917c) SHA1(2cff8b618588a57becb76f91fc37d86e2d5e15ea) )
	ROM_LOAD( "2", 0x8000, 0x8000, CRC(d57c6bff) SHA1(0845510ad957e891bcd3c0e46eb19a9222cb5c8e) )

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "3", 0x00000, 0x8000, CRC(038b74ba) SHA1(ac053cc4908b4075f918748b89570e07a0ba5116) )
	ROM_LOAD( "4", 0x08000, 0x8000, CRC(71fae199) SHA1(5d253c46ccf4cd2976a5fb8b8713f0f345443d06) )
	ROM_LOAD( "5", 0x10000, 0x8000, CRC(c76374e2) SHA1(7520dd48de20db60a2038f134dcaa454988e7874) )

	ROM_REGION( 0x0600, "proms", 0 ) // not dumped for this set, but GFX match, so safe to assume these are the same, too
	ROM_LOAD( "ic73.11e",    0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // red component
	ROM_LOAD( "ic74.12e",    0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // green component
	ROM_LOAD( "ic75.13e",    0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // blue component
ROM_END


ROM_START( tetrsark )
	ROM_REGION( 0x18000, "maincpu", 0 )
	ROM_LOAD( "ic17.1",      0x00000, 0x8000, CRC(1a505eda) SHA1(92f171a12cf0c326d29c244514718df04b998426) )
	ROM_LOAD( "ic16.2",      0x08000, 0x8000, CRC(157bc4df) SHA1(b2c704148e7e3ca61ab51308ee0d66ea1088bff3) )

	ROM_REGION( 0x800, "mcu", 0 )
	ROM_LOAD( "14_mc68705p5_rom.bin", 0x000, 0x800, CRC(dfbc4239) SHA1(d97c44d90d09142fd00731c1e44646bcba0402ec) ) // unused, not programmed

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "ic64.3",      0x00000, 0x8000, CRC(c3e9b290) SHA1(6e99520606c654e531dbeb9a598cfbb443c24dff) )
	ROM_LOAD( "ic63.4",      0x08000, 0x8000, CRC(de9a368f) SHA1(ffbb2479200648da3f3e7ab7cebcdb604f6dfb3d) )
	ROM_LOAD( "ic62.5",      0x10000, 0x8000, CRC(c8e80a00) SHA1(4bee4c36ee768ae68ebc64e639fdc43f61c74f92) )

	ROM_REGION( 0x0600, "proms", 0 )
	ROM_LOAD( "a75-07.bpr",    0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // red component
	ROM_LOAD( "a75-08.bpr",    0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // green component
	ROM_LOAD( "a75-09.bpr",    0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // blue component
ROM_END


ROM_START( tetrsark2 )
	ROM_REGION( 0x18000, "maincpu", 0 )
	ROM_LOAD( "1",      0x00000, 0x8000, CRC(3782809c) SHA1(90d80f8a98be72c4225cb620454729f69b8e628e) )
	ROM_LOAD( "2",      0x08000, 0x8000, CRC(6ef1f8dc) SHA1(8136a6fa5f0aa0396fc01944adb70e70e652e307) ) // 1st and 2nd half identical

	ROM_REGION( 0x800, "mcu", 0 )
	ROM_LOAD( "14_mc68705p5_rom.bin", 0x000, 0x800, CRC(dfbc4239) SHA1(d97c44d90d09142fd00731c1e44646bcba0402ec) ) // not dumped for this set, but very probably same as other set

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "3",      0x00000, 0x8000, CRC(c3e9b290) SHA1(6e99520606c654e531dbeb9a598cfbb443c24dff) )
	ROM_LOAD( "4",      0x08000, 0x8000, CRC(de9a368f) SHA1(ffbb2479200648da3f3e7ab7cebcdb604f6dfb3d) )
	ROM_LOAD( "5",      0x10000, 0x8000, CRC(c8e80a00) SHA1(4bee4c36ee768ae68ebc64e639fdc43f61c74f92) )

	ROM_REGION( 0x0600, "proms", 0 ) // not dumped for this set
	ROM_LOAD( "a75-07.bpr",    0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // red component
	ROM_LOAD( "a75-08.bpr",    0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // green component
	ROM_LOAD( "a75-09.bpr",    0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // blue component
ROM_END


ROM_START( hexa )
	ROM_REGION( 0x18000, "maincpu", 0 )     /* 64k for code + 32k for banked ROM */
	ROM_LOAD( "hexa.20",      0x00000, 0x8000, CRC(98b00586) SHA1(3591a3b0486d720f0aaa9f0bf4be352cd0ffcbc7) )
	ROM_LOAD( "hexa.21",      0x10000, 0x8000, CRC(3d5d006c) SHA1(ad4eadab82024b122182eacb5a322cfd6e476a70) )

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "hexa.17",      0x00000, 0x8000, CRC(f6911dd6) SHA1(b12ea27ecddd60820a32d4346afab0cc9d06fa57) )
	ROM_LOAD( "hexa.18",      0x08000, 0x8000, CRC(6e3d95d2) SHA1(6399b7b5d088ceda08fdea9cf650f6b405f038e7) )
	ROM_LOAD( "hexa.19",      0x10000, 0x8000, CRC(ffe97a31) SHA1(f16b5d2b9ace09bcbbfe3dfb73db7fa377d1af7f) )

	ROM_REGION( 0x0300, "proms", 0 )
	ROM_LOAD( "hexa.001",     0x0000, 0x0100, CRC(88a055b4) SHA1(eee86a7930d0a251f3e5c2134532cd1dede2026c) )
	ROM_LOAD( "hexa.003",     0x0100, 0x0100, CRC(3e9d4932) SHA1(9a336dba7134400312985b9902c77b4141105853) )
	ROM_LOAD( "hexa.002",     0x0200, 0x0100, CRC(ff15366c) SHA1(7feaf1c768bfe76432fb80991585e13d95960b34) )
ROM_END

/*

Hexa (alt.)

main hardware consists of.....

sub board with Z80 x2, 2 ROMs and a scratched 18 pin chip (probably a PIC)

main board has....
12MHz xtal
ay3-8910
8 position DSW x1
ROMs x4
6116 SRAM x3
82S123 PROMs x3

*/


ROM_START( hexaa )
	ROM_REGION( 0x18000, "maincpu", 0 )     /* 64k for code + 32k for banked ROM */
	ROM_LOAD( "sub1.bin",   0x00000, 0x8000, CRC(82c091fa) SHA1(e509ab4d9372f93d81df70772a4632100081ffd7) )
	ROM_LOAD( "main4.bin",  0x10000, 0x8000, CRC(3d5d006c) SHA1(ad4eadab82024b122182eacb5a322cfd6e476a70) )

	ROM_REGION( 0x18000, "subcpu", 0 )
	ROM_LOAD( "sub2.bin",   0x00000, 0x2000, CRC(c3bb9661) SHA1(e4bccb822d6eba77bb9cba75125cddb740775a2c) ) // 1ST AND 2ND HALF IDENTICAL (contains just 0x55 bytes of code)

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "main1.bin", 0x00000, 0x8000, CRC(f6911dd6) SHA1(b12ea27ecddd60820a32d4346afab0cc9d06fa57) )
	ROM_LOAD( "main2.bin", 0x08000, 0x8000, CRC(6e3d95d2) SHA1(6399b7b5d088ceda08fdea9cf650f6b405f038e7) )
	ROM_LOAD( "main3.bin", 0x10000, 0x8000, CRC(ffe97a31) SHA1(f16b5d2b9ace09bcbbfe3dfb73db7fa377d1af7f) )

	ROM_REGION( 0x0300, "proms", 0 )
	ROM_LOAD( "hexa.001",   0x0000, 0x0100, CRC(88a055b4) SHA1(eee86a7930d0a251f3e5c2134532cd1dede2026c) )
	ROM_LOAD( "hexa.003",   0x0100, 0x0100, CRC(3e9d4932) SHA1(9a336dba7134400312985b9902c77b4141105853) )
	ROM_LOAD( "hexa.002",   0x0200, 0x0100, CRC(ff15366c) SHA1(7feaf1c768bfe76432fb80991585e13d95960b34) )
ROM_END

ROM_START( brixian )
	ROM_REGION( 0x18000, "maincpu", 0 )
	ROM_LOAD( "b1.bin",    0x00000, 0x8000, CRC(3d167d09) SHA1(1d5bd098b655b8d2f956cfcb718213915bee3e41) )
	ROM_LOAD( "e7.bin",    0x08000, 0x2000, CRC(9e3707ab) SHA1(a04fb4824239f8ed1ef1de2f3c0f9d749320b2ba) )

	ROM_REGION( 0x0800, "mcu:mcu", 0 )
	ROM_LOAD( "68705p5", 0x0000, 0x0800, NO_DUMP ) // this just provides the 0x200 bytes of code we load in the protdata region by coping it to 0xc600 on startup

	ROM_REGION( 0x200, "protdata", 0 )
	ROM_LOAD( "protdata.bin", 0x00000, 0x200, CRC(a4131c0b) SHA1(5ddbd39c26e1bc9ec5f216e399c09994a23d09a7) )

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "b4.bin",     0x00000, 0x8000, CRC(34a7a693) SHA1(793fa6dd065a158bedcd0fdc494cc8fc793ae8be) )
	ROM_LOAD( "c4.bin",     0x08000, 0x8000, CRC(d422eda5) SHA1(4874b57ec8a8aa29937f5ccc2a734ffeb7834d8a) )
	ROM_LOAD( "e4.bin",     0x10000, 0x8000, CRC(9b2e79d6) SHA1(8a40e0ef2a792efc37ea50eec01cf3fb5a3e3215) )

	ROM_REGION( 0x0600, "proms", 0 )
	ROM_LOAD( "n82s131n.6l", 0x0000, 0x0200, CRC(0fa51a5b) SHA1(8c5cb69fbff8a3ba90f945c35f72754f9cc8f18c) )
	ROM_LOAD( "n82s131n.6p", 0x0200, 0x0200, CRC(d833ad33) SHA1(a7c17c96a670916e7102afc94dc2f0cb0455f0ce) )
	ROM_LOAD( "n82s131n.6m", 0x0400, 0x0200, CRC(05297649) SHA1(35f99cf8dddd66e26e2110619eb46bd6ccff41df) )
ROM_END

ROM_START( cruisin5 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "ic81-v.3f",   0x0000, 0x8000, CRC(41ed7668) SHA1(afd1600317d210b6da2c63a65f1614be1f77854b) )
	ROM_LOAD( "ic82-w.5f",   0x8000, 0x8000, CRC(77e310cc) SHA1(e2638bf6c565df9dab98d92c857d2212b150a2d1) )

	ROM_REGION( 0x18000, "gfx1", 0 )
	ROM_LOAD( "1-ic33.2c",   0x00000, 0x8000, CRC(7fb9dafc) SHA1(74a312729f10ab4753204b41de59ff41b93e80cb) )
	ROM_LOAD( "2-ic34.3c",   0x08000, 0x8000, CRC(05229af9) SHA1(76c469506430bc23ee77bbcefbc357478437bba7) )
	ROM_LOAD( "3-ic35.5c",   0x10000, 0x8000, CRC(a4c1a25f) SHA1(29d49572c1e7c6c8ac428ad2a3e625f0f548f66c) )

	ROM_REGION( 0x0600, "proms", 0 )
	ROM_LOAD( "ic73.11e",    0x0000, 0x0200, CRC(0af8b289) SHA1(6bc589e8a609b4cf450aebedc8ce02d5d45c970f) ) // red component
	ROM_LOAD( "ic74.12e",    0x0200, 0x0200, CRC(abb002fb) SHA1(c14f56b8ef103600862e7930709d293b0aa97a73) ) // green component
	ROM_LOAD( "ic75.13e",    0x0400, 0x0200, CRC(a7c6c277) SHA1(adaa003dcd981576ea1cc5f697d709b2d6b2ea29) ) // blue component
ROM_END



/* Driver Initialization */

void arkanoid_state::arkanoid_bootleg_init()
{
	m_maincpu->space(AS_PROGRAM).install_read_handler(0xf000, 0xf000, read8smo_delegate(*this, FUNC(arkanoid_state::arkanoid_bootleg_f000_r)));
	m_maincpu->space(AS_PROGRAM).install_read_handler(0xf002, 0xf002, read8smo_delegate(*this, FUNC(arkanoid_state::arkanoid_bootleg_f002_r)));
	m_maincpu->space(AS_PROGRAM).install_write_handler(0xd018, 0xd018, write8smo_delegate(*this, FUNC(arkanoid_state::arkanoid_bootleg_d018_w)));
	m_maincpu->space(AS_PROGRAM).install_read_handler(0xd008, 0xd008, read8smo_delegate(*this, FUNC(arkanoid_state::arkanoid_bootleg_d008_r)));
}

void arkanoid_state::init_arkangc()
{
	m_bootleg_id = ARKANGC;
	arkanoid_bootleg_init();
}

void arkanoid_state::init_arkangc2()
{
	m_bootleg_id = ARKANGC2;
	arkanoid_bootleg_init();
}

void arkanoid_state::init_block2()
{
	// the graphics on this bootleg have the data scrambled
	uint8_t* srcgfx = memregion("gfx1")->base();
	std::vector<uint8_t> buffer(0x18000);

	for (int tile = 0; tile < 0x3000; tile++)
	{
		// combine these into a single swap..
		int srctile = bitswap<16>(tile,15,14,13,12,
									   11,10, 9, 8,
										7, 5, 6, 3,
										1, 2, 4, 0);

		srctile = bitswap<16>(srctile,15,14,13,12,
									  11, 9,10, 5,
									   7, 6, 8, 4,
									   3, 2, 1, 0);

		srctile = srctile ^ 0xd4;

		memcpy(&buffer[tile * 8], &srcgfx[srctile * 8], 8);
	}

	memcpy(srcgfx, &buffer[0], 0x18000);

	m_bootleg_id = BLOCK2;
	arkanoid_bootleg_init();
}

void arkanoid_state::init_arkblock()
{
	m_bootleg_id = ARKBLOCK;
	arkanoid_bootleg_init();
}

void arkanoid_state::init_arkbloc2()
{
	m_bootleg_id = ARKBLOC2;
	arkanoid_bootleg_init();
}

void arkanoid_state::init_arkgcbl()
{
	m_bootleg_id = ARKGCBL;
	arkanoid_bootleg_init();
}

void arkanoid_state::init_paddle2()
{
	m_bootleg_id = PADDLE2;
	arkanoid_bootleg_init();
}


void arkanoid_state::init_tetrsark()
{
	uint8_t *ROM = memregion("maincpu")->base();
	for (int x = 0; x < 0x8000; x++)
	{
		ROM[x] = ROM[x] ^ 0x94;
	}

	m_maincpu->space(AS_PROGRAM).install_write_handler(0xd008, 0xd008, write8smo_delegate(*this, FUNC(arkanoid_state::tetrsark_d008_w)));
}

void arkanoid_state::init_tetrsark2()
{
	m_maincpu->space(AS_PROGRAM).install_write_handler(0xd008, 0xd008, write8smo_delegate(*this, FUNC(arkanoid_state::tetrsark_d008_w)));
}


void arkanoid_state::init_hexa()
{
	uint8_t *RAM = memregion("maincpu")->base();
#if 0
	/* Hexa is not protected or anything, but it keeps writing 0x3f to register */
	/* 0x07 of the AY8910, to read the input ports. This causes clicks in the */
	/* music since the output channels are continuously disabled and reenabled. */
	/* To avoid that, we just NOP out the 0x3f write. */

	RAM[0x0124] = 0x00;
	RAM[0x0125] = 0x00;
	RAM[0x0126] = 0x00;
#endif

	membank("bank1")->configure_entries(0, 2, &RAM[0x10000], 0x4000);
}

void arkanoid_state::init_hexaa()
{
	init_hexa();

	m_hexaa_from_main = 0;
	m_hexaa_from_sub = 0;

	save_item(NAME(m_hexaa_from_main));
	save_item(NAME(m_hexaa_from_sub));
}

void arkanoid_state::init_brixian()
{
	uint8_t *RAM = memregion("protdata")->base();

	for (int i = 0x000; i < 0x200; i++)
		m_protram[i + 0x600] = RAM[i];

}

/* Game Drivers */

// original sets of Arkanoid
//    YEAR, NAME,         PARENT,   MACHINE,  INPUT,     STATE,          INIT,           MONITOR,COMPANY,                                              FULLNAME,                                      FLAGS
GAME( 1986, arkanoid,     0,        arkanoid, arkanoid,  arkanoid_state, empty_init,     ROT90, "Taito Corporation Japan",                             "Arkanoid (World, older)",                     MACHINE_SUPPORTS_SAVE )
GAME( 1986, arkanoidu,    arkanoid, arkanoid, arkanoid,  arkanoid_state, empty_init,     ROT90, "Taito America Corporation (Romstar license)",         "Arkanoid (US, newer)",                        MACHINE_SUPPORTS_SAVE )
GAME( 1986, arkanoiduo,   arkanoid, arkanoid, arkanoid,  arkanoid_state, empty_init,     ROT90, "Taito America Corporation (Romstar license)",         "Arkanoid (US, older)",                        MACHINE_SUPPORTS_SAVE )
GAME( 1986, arkanoidj,    arkanoid, arkanoid, arkanoidj, arkanoid_state, empty_init,     ROT90, "Taito Corporation",                                   "Arkanoid (Japan, newer)",                     MACHINE_SUPPORTS_SAVE )
GAME( 1986, arkanoidja,   arkanoid, arkanoid, arkanoidj, arkanoid_state, empty_init,     ROT90, "Taito Corporation",                                   "Arkanoid (Japan, newer w/level select)",      MACHINE_SUPPORTS_SAVE )
GAME( 1986, arkanoidjb,   arkanoid, arkanoid, arkanoidj, arkanoid_state, empty_init,     ROT90, "Taito Corporation",                                   "Arkanoid (Japan, older)",                     MACHINE_SUPPORTS_SAVE )
GAME( 1986, arkanoidpe,   arkanoid, arkanoid, arkanoid,  arkanoid_state, empty_init,     ROT90, "Taito Corporation (Phoenix Electronics Co. license)", "Arkanoid (Phoenix Electronics Co. license)",  MACHINE_SUPPORTS_SAVE )

// bootlegs of Arkanoid
GAME( 1986, arkanoidjbl,  arkanoid, p3mcu,    arkanoidj, arkanoid_state, empty_init,     ROT90, "bootleg",                                             "Arkanoid (bootleg with MCU, set 1)",          MACHINE_SUPPORTS_SAVE )
GAME( 1986, arkanoidjbl2, arkanoid, p3mcu,    arkanoidj, arkanoid_state, empty_init,     ROT90, "bootleg (Beta)",                                      "Arkanoid (bootleg with MCU, set 2)",          MACHINE_SUPPORTS_SAVE )
GAME( 1986, ark1ball,     arkanoid, p3mcuay,  ark1ball,  arkanoid_state, empty_init,     ROT90, "bootleg",                                             "Arkanoid (bootleg with MCU, harder)",         MACHINE_SUPPORTS_SAVE )
GAME( 1986, arkangc,      arkanoid, bootleg,  arkangc,   arkanoid_state, init_arkangc,   ROT90, "bootleg (Game Corporation)",                          "Arkanoid (Game Corporation bootleg, set 1)",  MACHINE_SUPPORTS_SAVE )
GAME( 1986, arkangc2,     arkanoid, bootleg,  arkangc2,  arkanoid_state, init_arkangc2,  ROT90, "bootleg (Game Corporation)",                          "Arkanoid (Game Corporation bootleg, set 2)",  MACHINE_SUPPORTS_SAVE )
GAME( 1986, arkblock,     arkanoid, bootleg,  arkangc,   arkanoid_state, init_arkblock,  ROT90, "bootleg (Game Corporation)",                          "Block (Game Corporation bootleg, set 1)",     MACHINE_SUPPORTS_SAVE )
GAME( 1986, arkbloc2,     arkanoid, bootleg,  arkangc,   arkanoid_state, init_arkbloc2,  ROT90, "bootleg (Game Corporation)",                          "Block (Game Corporation bootleg, set 2)",     MACHINE_SUPPORTS_SAVE )
GAME( 1986, arkbloc3,     arkanoid, bootleg,  block2,    arkanoid_state, init_block2,    ROT90, "bootleg (Game Corporation)",                          "Block (Game Corporation bootleg, set 3)",     MACHINE_SUPPORTS_SAVE ) // Both these sets (arkblock3, block2) have an extra unknown rom
GAME( 1986, block2,       arkanoid, bootleg,  block2,    arkanoid_state, init_block2,    ROT90, "bootleg (S.P.A. Co.)",                                "Block 2 (S.P.A. Co. bootleg)",                MACHINE_SUPPORTS_SAVE ) //  and scrambled gfx roms with 'space invader' themed gfx
GAME( 1986, arkgcbl,      arkanoid, aysnd,    arkgcbl,   arkanoid_state, init_arkgcbl,   ROT90, "bootleg",                                             "Arkanoid (bootleg on Block hardware, set 1)", MACHINE_SUPPORTS_SAVE )
GAME( 1986, arkgcbla,     arkanoid, aysnd,    arkgcbl,   arkanoid_state, init_arkgcbl,   ROT90, "bootleg",                                             "Arkanoid (bootleg on Block hardware, set 2)", MACHINE_SUPPORTS_SAVE )
GAME( 1988, paddle2,      arkanoid, bootleg,  paddle2,   arkanoid_state, init_paddle2,   ROT90, "bootleg",                                             "Paddle 2 (bootleg on Block hardware)",        MACHINE_SUPPORTS_SAVE )
GAME( 1986, arkatayt,     arkanoid, aysnd,    arkatayt,  arkanoid_state, empty_init,     ROT90, "bootleg (Tayto)",                                     "Arkanoid (Tayto bootleg)",                    MACHINE_SUPPORTS_SAVE )
GAME( 1986, arktayt2,     arkanoid, aysnd,    arktayt2,  arkanoid_state, empty_init,     ROT90, "bootleg (Tayto)",                                     "Arkanoid (Tayto bootleg, harder)",            MACHINE_SUPPORTS_SAVE )
GAME( 1986, arkaboot,     arkanoid, bootleg,  arkangc,   arkanoid_state, init_arkangc,   ROT90, "bootleg",                                             "Arkanoid (bootleg of version Japan, older)",  MACHINE_SUPPORTS_SAVE )

// Other games
GAME( 1987, arkatour,     0,        arkanoid, arkanoid,  arkanoid_state, empty_init,     ROT90, "Taito America Corporation (Romstar license)",         "Tournament Arkanoid (US, older)",             MACHINE_SUPPORTS_SAVE )
GAME( 1987, arkatour2,    arkatour, arkanoid, arkanoid,  arkanoid_state, empty_init,     ROT90, "Taito America Corporation (Romstar license)",         "Tournament Arkanoid (US, newer)",             MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) // same FRI,  6 JUN 1986, 15:49 string for both sets, but labels show this is newer

GAME( 19??, tetrsark,     0,        bootleg,  tetrsark,  arkanoid_state, init_tetrsark,  ROT0,  "D.R. Korea",                                          "Tetris (D.R. Korea, set 1, encrypted)",       MACHINE_SUPPORTS_SAVE )
GAME( 19??, tetrsark2,    tetrsark, bootleg,  tetrsark,  arkanoid_state, init_tetrsark2, ROT0,  "D.R. Korea",                                          "Tetris (D.R. Korea, set 2)",                  MACHINE_SUPPORTS_SAVE )

GAME( 1990, hexa,         0,        hexa,     hexa,      arkanoid_state, init_hexa,      ROT0,  "D.R. Korea",                                          "Hexa",                                        MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
GAME( 1990, hexaa,        hexa,     hexaa,    hexa,      arkanoid_state, init_hexaa,     ROT0,  "D.R. Korea",                                          "Hexa (with 2xZ80, protected)",                MACHINE_NOT_WORKING )

GAME( 1993, brixian,      0,        brixian,  brixian,   arkanoid_state, init_brixian,   ROT0,  "Cheil Computer System",                               "Brixian",                                     MACHINE_SUPPORTS_SAVE )

// demo, winner of Revision 2023 wild compo, on Arkanoid PCB with MCU stripped off
GAME( 2023, cruisin5,     0,        cruisin5, cruisin5,  arkanoid_state, empty_init,     ROT90, "Abyss",                                               "Cruisin 5: Cruise Back",                      MACHINE_SUPPORTS_SAVE )



asst128.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev

#include "emu.h"
#include "machine/genpc.h"

#include "bus/pc_joy/pc_joy.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "cpu/i86/i86.h"
#include "imagedev/floppy.h"
#include "machine/pc_fdc.h"

#include "formats/asst128_dsk.h"


DECLARE_DEVICE_TYPE(ASST128_MOTHERBOARD, asst128_mb_device)

class asst128_mb_device : public ibm5150_mb_device
{
public:
	// construction/destruction
	asst128_mb_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0)
		: ibm5150_mb_device(mconfig, ASST128_MOTHERBOARD, tag, owner, clock)
	{ }

	void map(address_map &map) ATTR_COLD;
};

void asst128_mb_device::map(address_map &map)
{
	map(0x0020, 0x002f).rw("pic8259", FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x0040, 0x004f).rw("pit8253", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x0060, 0x006f).rw("ppi8255", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x0080, 0x008f).w(FUNC(asst128_mb_device::pc_page_w));
	map(0x00a0, 0x00a1).w(FUNC(asst128_mb_device::nmi_enable_w));
}

DEFINE_DEVICE_TYPE(ASST128_MOTHERBOARD, asst128_mb_device, "asst128_mb", "ASST128_MOTHERBOARD")


namespace {

class asst128_state : public driver_device
{
public:
	// construction/destruction
	asst128_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_fdc(*this, "fdc")
	{ }

	void asst128(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	required_device<pc_fdc_xt_device> m_fdc;

	static void asst128_formats(format_registration &fr);
	void asst128_fdc_dor_w(uint8_t data);

	void machine_start() override ATTR_COLD;
	void asst128_io(address_map &map) ATTR_COLD;
	void asst128_map(address_map &map) ATTR_COLD;
};

void asst128_state::machine_start()
{
	memory_region *font = memregion(":board0:cga_mc1502:gfx1");
	memcpy(font->base(), memregion("bios")->base() + 0xfa6e, 0x0400);
	memcpy(font->base() + 0x0400, memregion("bios")->base() + 0x4000, 0x0400);
}

void asst128_state::asst128_fdc_dor_w(uint8_t data)
{
	m_fdc->tc_w((data & 0x80) == 0x80);
	m_fdc->dor_w(data);
}

void asst128_state::asst128_map(address_map &map)
{
	map.unmap_value_high();
	map(0xf0000, 0xfffff).rom().region("bios", 0);
}

void asst128_state::asst128_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m("mb", FUNC(asst128_mb_device::map));
	map(0x0200, 0x0207).rw("pc_joy", FUNC(pc_joy_device::joy_port_r), FUNC(pc_joy_device::joy_port_w));
	map(0x03f2, 0x03f3).w(FUNC(asst128_state::asst128_fdc_dor_w));
	map(0x03f4, 0x03f5).m("fdc:upd765", FUNC(upd765a_device::map));
}

static void asst128_floppies(device_slot_interface &device)
{
	device.option_add("525ssqd", FLOPPY_525_SSQD);
}

void asst128_state::asst128_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_ASST128_FORMAT);
}

static DEVICE_INPUT_DEFAULTS_START( asst128 )
	DEVICE_INPUT_DEFAULTS("DSW0", 0x30, 0x20)
DEVICE_INPUT_DEFAULTS_END

void asst128_state::asst128(machine_config &config)
{
	I8086(config, m_maincpu, 4772720);
	m_maincpu->set_addrmap(AS_PROGRAM, &asst128_state::asst128_map);
	m_maincpu->set_addrmap(AS_IO, &asst128_state::asst128_io);
	m_maincpu->set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	asst128_mb_device &mb(ASST128_MOTHERBOARD(config, "mb"));
	mb.set_cputag(m_maincpu);
	mb.int_callback().set_inputline(m_maincpu, 0);
	mb.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	mb.kbdclk_callback().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	mb.kbddata_callback().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));
	mb.set_input_default(DEVICE_INPUT_DEFAULTS_NAME(asst128));

	subdevice<cassette_image_device>("mb:cassette")->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);

	// FIXME: determine ISA bus clock
	ISA8_SLOT(config, "board0", 0, "mb:isa", pc_isa8_cards, "cga_mc1502", true);
	ISA8_SLOT(config, "board1", 0, "mb:isa", pc_isa8_cards, "lpt", true);

	pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_IBM_PC_XT_83));
	kbd.out_clock_cb().set("mb", FUNC(asst128_mb_device::keyboard_clock_w));
	kbd.out_data_cb().set("mb", FUNC(asst128_mb_device::keyboard_data_w));

	PC_FDC_XT(config, m_fdc, 0);
	m_fdc->intrq_wr_callback().set("mb:pic8259", FUNC(pic8259_device::ir6_w));
	FLOPPY_CONNECTOR(config, "fdc:0", asst128_floppies, "525ssqd", asst128_state::asst128_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", asst128_floppies, "525ssqd", asst128_state::asst128_formats);

	PC_JOY(config, "pc_joy");

	RAM(config, RAM_TAG).set_default_size("512K").set_extra_options("64K, 128K, 256K");
}

ROM_START( asst128 )
	ROM_REGION16_LE(0x10000,"bios", 0)
	ROM_LOAD( "extbios.bin",      0x4000, 0x2000, CRC(e3bf22de) SHA1(d4319edc82c0015ca0adc6c8771e887659717e62))
	ROM_LOAD( "basic.bin",        0x6000, 0x8000, CRC(a4ec66f6) SHA1(80e934986022681ccde180e92aa108e716c4f19b))
	ROM_LOAD( "mainbios.bin",     0xe000, 0x2000, CRC(8426cbf5) SHA1(41d14137ffa651977041da22aa8071c0f7854158))

	ROM_REGION(0x2000,"gfx1", ROMREGION_ERASE00)
	ROM_LOAD( "asst128cg.bin", 0, 0x2000, NO_DUMP )
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT  CLASS          INIT        COMPANY      FULLNAME         FLAGS
COMP( 198?, asst128, ibm5150, 0,      asst128, 0,     asst128_state, empty_init, "Schetmash", "Assistent 128", MACHINE_NOT_WORKING)



astrohome.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nicola Salmoria, Mike Coates, Frank Palazzolo, Aaron Giles, Dirk Best
/****************************************************************************

    Bally Astrocade consumer hardware

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

#include "emu.h"
#include "astrocde.h"

#include "cpu/z80/z80.h"
#include "machine/ram.h"
#include "sound/astrocde.h"

#include "bus/astrocde/slot.h"
#include "bus/astrocde/rom.h"
#include "bus/astrocde/exp.h"
#include "bus/astrocde/ram.h"
#include "bus/astrocde/ctrl.h"
#include "bus/astrocde/accessory.h"

#include "softlist_dev.h"
#include "speaker.h"


namespace {

class astrocde_home_state : public astrocde_state
{
public:
	astrocde_home_state(const machine_config &mconfig, device_type type, const char *tag)
		: astrocde_state(mconfig, type, tag)
		, m_cart(*this, "cartslot")
		, m_exp(*this, "exp")
		, m_ctrl(*this, "ctrl%u", 1U)
		, m_accessory(*this, "accessory")
		, m_keypad(*this, "KEYPAD%u", 0U)
	{ }

	void astrocde(machine_config &config);

	void init_astrocde();

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	uint8_t inputs_r(offs_t offset);

	void astrocade_io(address_map &map) ATTR_COLD;
	void astrocade_mem(address_map &map) ATTR_COLD;

	required_device<astrocade_cart_slot_device> m_cart;
	required_device<astrocade_exp_device> m_exp;
	required_device_array<astrocade_ctrl_port_device, 4> m_ctrl;
	required_device<astrocade_accessory_port_device> m_accessory;
	required_ioport_array<4> m_keypad;
};


/*********************************************************************************
 *
 *  Memory maps
 *
 * $0000 to $1FFF:  8K on-board ROM (could be one of three available BIOS dumps)
 * $2000 to $3FFF:  8K cartridge ROM
 * $4000 to $4FFF:  4K screen RAM
 * $5000 to $FFFF:  44K address space not available in standard machine.  With a
 * sufficiently large RAM expansion, all of this RAM can be added, and accessed
 * by an extended BASIC program.  Bally and Astrocade BASIC can access from
 * $5000 to $7FFF if available.
 *
 *********************************************************************************/

void astrocde_home_state::astrocade_mem(address_map &map)
{
	map(0x0000, 0x0fff).rom().w(FUNC(astrocde_home_state::astrocade_funcgen_w));
	map(0x1000, 0x3fff).rom(); /* Star Fortress writes in here?? */
	map(0x4000, 0x4fff).ram().share("videoram"); /* ASG */
	//map(0x5000, 0xffff).rw("exp", FUNC(astrocade_exp_device::read), FUNC(astrocade_exp_device::write));
}


void astrocde_home_state::astrocade_io(address_map &map)
{
	map(0x00, 0x0f).select(0xff00).rw(FUNC(astrocde_state::video_register_r), FUNC(astrocde_state::video_register_w));
	map(0x10, 0x1f).select(0xff00).r(m_astrocade_sound[0], FUNC(astrocade_io_device::read));
	map(0x10, 0x18).select(0xff00).w(m_astrocade_sound[0], FUNC(astrocade_io_device::write));
	map(0x19, 0x19).mirror(0xff00).w(FUNC(astrocde_state::expand_register_w));
}


/*************************************
 *
 *  Input ports
 *
 *
 *  The Astrocade has ports for four hand controllers.  Each controller has a
 *  knob on top that can be simultaneously pushed as an eight-way joystick and
 *  twisted as a paddle, in addition to a trigger button.  The knob can twist
 *  through about 270 degrees, registering 256 unique positions.  It does not
 *  autocenter.  When selecting options on the menu, twisting the knob to the
 *  right gives lower numbers, and twisting to the left gives larger numbers.
 *  Paddle games like Clowns have more intuitive behavior -- twisting to the
 *  right moves the character right.
 *
 *  There is a 24-key keypad on the system itself (6 rows, 4 columns).  It is
 *  labeled for the built-in calculator, but overlays were released for other
 *  programs, the most popular being the BASIC cartridges, which allowed a
 *  large number of inputs by making the bottom row shift buttons.  The labels
 *  below first list the calculator key, then the BASIC keys in the order of no
 *  shift, GREEN shift, RED shift, BLUE shift, WORDS shift.
 *
 *************************************/

uint8_t astrocde_home_state::inputs_r(offs_t offset)
{
	if (BIT(offset, 2))
		return m_keypad[offset & 3]->read();
	else
		return m_ctrl[offset & 3]->read_handle();
}

static INPUT_PORTS_START( astrocde )
	PORT_START("KEYPAD0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME(u8"%   ÷         [   ]   LIST") PORT_CODE(KEYCODE_O)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("/   x     J   K   L   NEXT") PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("x   -     V   W   X   IF") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("-   +     &   @   *   GOTO") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("+   =     #   %   :   PRINT") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("=   WORDS Shift") PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEYPAD1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME(u8"\u2193   HALT              RUN") PORT_CODE(KEYCODE_PGDN) // U+2193 = ↓
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("CH  9     G   H   I   STEP") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("9   6     S   T   U   RND") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME(u8"6   3     \u2191   .   \u2193   BOX") PORT_CODE(KEYCODE_6) // U+2191 = ↑, U+2193 = ↓
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("3   ERASE (   ;   )") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME(".   BLUE Shift") PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("KEYPAD2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME(u8"\u2191   PAUSE     /   \\") PORT_CODE(KEYCODE_PGUP) // U+2191 = ↑
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("MS  8     D   E   F   TO") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("8   5     P   Q   R   RETN") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME(u8"5   2     \u2190   '   \u2192   LINE") PORT_CODE(KEYCODE_5) // U+2190 = ←,  U+2192 = →
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("2   0     <   \"   >   INPUT") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("0   RED Shift") PORT_CODE(KEYCODE_0)
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("KEYPAD3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("C   GO                +10") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("MR  7     A   B   C   FOR") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("7   4     M   N   O   GOSB") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("4   1     Y   Z   !   CLEAR") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("1   SPACE $   ,   ?") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("CE  GREEN Shift") PORT_CODE(KEYCODE_E)
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END


/*************************************
 *
 *  Machine drivers
 *
 *************************************/

static void astrocade_cart(device_slot_interface &device)
{
	device.option_add_internal("rom",       ASTROCADE_ROM_STD);
	device.option_add_internal("rom_256k",  ASTROCADE_ROM_256K);
	device.option_add_internal("rom_512k",  ASTROCADE_ROM_512K);
	device.option_add_internal("rom_cass",  ASTROCADE_ROM_CASS);
}

static void astrocade_exp(device_slot_interface &device)
{
	device.option_add("blue_ram_4k",   ASTROCADE_BLUERAM_4K);
	device.option_add("blue_ram_16k",  ASTROCADE_BLUERAM_16K);
	device.option_add("blue_ram_32k",  ASTROCADE_BLUERAM_32K);
	device.option_add("viper_sys1",    ASTROCADE_VIPER_SYS1);
	device.option_add("lil_white_ram", ASTROCADE_WHITERAM);
	device.option_add("rl64_ram",      ASTROCADE_RL64RAM);
}


void astrocde_home_state::astrocde(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, ASTROCADE_CLOCK/4); /* 1.789 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &astrocde_home_state::astrocade_mem);
	m_maincpu->set_addrmap(AS_IO, &astrocde_home_state::astrocade_io);

	config.set_perfect_quantum(m_maincpu);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(ASTROCADE_CLOCK, 455, 0, 352, 262, 0, 240);
	m_screen->set_screen_update(FUNC(astrocde_state::screen_update_astrocde));
	m_screen->set_palette(m_palette);

	PALETTE(config, "palette", FUNC(astrocde_home_state::astrocade_palette), 512);

	/* control ports */
	for (uint32_t port = 0; port < 4; port++)
	{
		ASTROCADE_CTRL_PORT(config, m_ctrl[port], astrocade_controllers, port == 0 ? "joy" : nullptr);
		m_ctrl[port]->ltpen_handler().set(FUNC(astrocde_home_state::lightpen_trigger_w));
	}

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	ASTROCADE_IO(config, m_astrocade_sound[0], ASTROCADE_CLOCK/4);
	m_astrocade_sound[0]->si_cb().set(FUNC(astrocde_home_state::inputs_r));
	m_astrocade_sound[0]->pot_cb<0>().set(m_ctrl[0], FUNC(astrocade_ctrl_port_device::read_knob));
	m_astrocade_sound[0]->pot_cb<1>().set(m_ctrl[1], FUNC(astrocade_ctrl_port_device::read_knob));
	m_astrocade_sound[0]->pot_cb<2>().set(m_ctrl[2], FUNC(astrocade_ctrl_port_device::read_knob));
	m_astrocade_sound[0]->pot_cb<3>().set(m_ctrl[3], FUNC(astrocade_ctrl_port_device::read_knob));
	m_astrocade_sound[0]->add_route(ALL_OUTPUTS, "mono", 1.0);

	/* expansion port */
	ASTROCADE_EXP_SLOT(config, m_exp, astrocade_exp, nullptr);

	/* cartridge */
	ASTROCADE_CART_SLOT(config, m_cart, astrocade_cart, nullptr);

	/* cartridge */
	ASTROCADE_ACCESSORY_PORT(config, m_accessory, m_screen, astrocade_accessories, nullptr);
	m_accessory->ltpen_handler().set(FUNC(astrocde_home_state::lightpen_trigger_w));

	/* Software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("astrocde");
}


/*************************************
 *
 *  ROM definitions
 *
 *************************************/

ROM_START( astrocde )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "astro.bin",  0x0000, 0x2000, CRC(ebc77f3a) SHA1(b902c941997c9d150a560435bf517c6a28137ecc) )
ROM_END

ROM_START( astrocdl )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "ballyhlc.bin",  0x0000, 0x2000, CRC(d7c517ba) SHA1(6b2bef5d970e54ed204549f58ba6d197a8bfd3cc) )
ROM_END

ROM_START( astrocdw )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "bioswhit.bin",  0x0000, 0x2000, CRC(6eb53e79) SHA1(d84341feec1a0a0e8aa6151b649bc3cf6ef69fbf) )
ROM_END


/*************************************
 *
 *  Driver initialization
 *
 *************************************/

void astrocde_home_state::init_astrocde()
{
	m_video_config = AC_SOUND_PRESENT;
}

void astrocde_home_state::machine_start()
{
	if (m_cart->exists())
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x2000, 0x3fff, read8sm_delegate(*m_cart, FUNC(astrocade_cart_slot_device::read_rom)));

	// if no RAM is mounted and the handlers are installed, the system starts with garbage on screen and a RESET is necessary
	// thus, install RAM only if an expansion is mounted
	if (m_exp->get_card_mounted())
	{
		m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x5000, 0xffff, read8sm_delegate(*m_exp, FUNC(astrocade_exp_device::read)), write8sm_delegate(*m_exp, FUNC(astrocade_exp_device::write)));
		m_maincpu->space(AS_IO).install_readwrite_handler(0x0080, 0x00ff, 0x0000, 0x0000, 0xff00, read8sm_delegate(*m_exp, FUNC(astrocade_exp_device::read_io)), write8sm_delegate(*m_exp, FUNC(astrocade_exp_device::write_io)));
	}
}

} // Anonymous namespace


/*************************************
 *
 *  Driver definitions
 *
 *************************************/

/*    YEAR  NAME      PARENT    COMPAT  MACHINE   INPUT     CLASS                INIT           COMPANY                FULLNAME                       FLAGS */
CONS( 1978, astrocde, 0,        0,      astrocde, astrocde, astrocde_home_state, init_astrocde, "Bally Manufacturing", "Bally Professional Arcade",   MACHINE_SUPPORTS_SAVE )
CONS( 1977, astrocdl, astrocde, 0,      astrocde, astrocde, astrocde_home_state, init_astrocde, "Bally Manufacturing", "Bally Home Library Computer", MACHINE_SUPPORTS_SAVE )
CONS( 1977, astrocdw, astrocde, 0,      astrocde, astrocde, astrocde_home_state, init_astrocde, "Bally Manufacturing", "Bally Computer System",       MACHINE_SUPPORTS_SAVE )



at.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol, Miodrag Milanovic
/***************************************************************************

    IBM AT Compatibles

Commodore PC 30-III and PC 40-III
=================================
Links: http://www.richardlagendijk.nl/cip/computer/item/pc30iii/en , ftp://ftp.zimmers.net/pub/cbm-pc/firmware/pc30/
Info: The PC 30-III and PC 40-III share the same mainboard. On a PC 30-III the onboard Paradise VGA is not populated.
Form factor: Desktop PC
CPU: Siemens SAB 80286-12 (PC 30-III), Intel 80286-12 (PC 40-III)
RAM: 1MB on board
Chipset: Faraday FE3020, MOS 5720 1788 41, Faraday FE3000, FE3010B,
Bus: 3x16 bit ISA, 1x8 bit ISA
Video: PC 30-III: ATI EGA Wonder 800+, PC 40-III: Onboard Paradise VGA, 256KB
Mass storage: One HD disk drive standard, second drive optional; PC 30-III: 20MB, PC 40-III: 40MB AT-IDE HD standard, 80MB or 100MB optional
On board: Serial, Parallel, Commodore 1532 Mouse port (MS Bus mouse compatible), Keyboard, Beeper, Floppy (2 devices), AT-IDE (1 device)
Options: 80287

Sanyo MBC-28
============
Links: http://www.cc-computerarchiv.de/CC-Archiv/bc-alt/gb-san/gb-san-12_91.html
Form factor: Desktop
CPU: 80386sx-20
RAM: 1MB - 8MB on board
Mass storage: 1.44MB Floppy disk drive and 80MB IDE hard disk
On board: 2xserial, parallel, bus mouse, keyboard
To-Do: Complains about missing mouse hardware (Bus Mouse), hangs in POST

Siemens PCD-2
=============
Links: http://www.z80.eu/siemenspcd2.html , http://www.z80.eu/downloads/Siemens_PCD-2_SW-Monitor-Buchse-Belegung.pdf , https://www.computerwoche.de/a/at-klon-und-lan-ergaenzen-siemens-palette,1166395
Form factor: low profile desktop
CPU: 80286-12 on a Tandon supplied slot CPU card
RAM: 1MB - 4MB in four SIMM modules
Mass storage: 1.2MB Floppy disk drive and 20MB or 40MB MFM harddisk
Bus: Vertical passive ISA backplane with six slots
On board: 2xserial, parallel, floppy, keyboard, RTC, MFM harddisk controller piggybacked to bus extension on slot CPU
Options: 80287

Compaq Portable II
==================
Links: http://tkc8800.com/post/compaq-portable-ii-restoration , https://www.seasip.info/VintagePC/compaq2.html , https://en.wikipedia.org/wiki/Compaq_Portable_II
Form factor: Luggable
CPU: 80286-8
RAM: 256K or 640K on board, 512kB and 2048kB ISA memory cards and 512kB and 1536kB memory boards that attached to the back of the motherboard, 4.2M max.
Display: Green-screen CRT
Mass storage: one or two 5.25" floppy drives, 10MB or 20MB mfm harddisk connected via an MFM=>IDE bridgeboard
Bus: two 8bit and two 16bit ISA slots
On board: Serial, parallel
Standard cards: Floppy/IDE combo card, special Compaq CGA/MDA hybrid video card
Options: Compaq EGA card (drives internal monitor), 80287, floppy drives (360K, 1.2M, 1.44M)

Compaq Portable III
===================
Links: http://www.old-computers.com/museum/computer.asp?c=1064 , http://www.freakedenough.at/infoseiten/read.php?id=66 , http://www.1000bit.it/ad/bro/compaq/CompaqProtable3.pdf , http://oldcomputers.net/compaqiii.pdf
Info: The later Compaq Portable 386 uses the same case, screen and video adapter; Models: 1 (no), 20 (20MB) and 40 (40MB harddisk)
Form factor: Luggable
CPU: AMD N80L286-12/S 12MHz (could be downclocked to 8MHz)
RAM: 640KB, attitional RAM cards were 512KB or 2MB to give 1.1MB, 1.6MB, 2.1MB, 2.6MB, 4.6MB or 6.6MB of total RAM
Video: AT&T 6300/Olivetti M24 driver compatible "Super CGA" with a 640x400 red/amber Plasma screen
Mass storage: One 1.2MB floppy disk drive, no/20MB/40MB hard disk
On board: Serial, Parallel, RTC, RGBI (external Monitor), keyboard
Options: 80827, Expansion box with 2 ISA slots, 300/1200Baud internal Modem, Compaq EGA Board
To-Do: Emulate Graphics card fully

Ericsson/Nokia Data/ICL WS286
=============================
Links: http://oju.mbnet.fi/retro/EricssonPC_eng.html
Info: WS286 was introduced 1986 as first 8Mhz AT in the world a few weeks ahead competition, aquired by Nokia Data 1988 which in turn was aquired by ICL 1990
Form factor: Desktop PC
CPU: Intel 286, 8MHz
RAM: 640KB
Mass storage: Floppy: 5.25" 1.2Mb, HDD: 40Mb

Nixdorf 8810 M55
================
Links: https://www.computerwoche.de/a/auch-nixdorf-nun-in-der-at-clone-riege,1166613
Info: Rebadged NCR PC-8, an AT-clone in a huge desktop case
Form factor: Desktop PC
CPU: Intel 286; CPU card has a 20Mhz, a 12 MHz and a 14.31818 crystal
RAM: 512K on CPU card, 128K on a piggyback card and a memory expansion board
Bus: Passive backplane, ISA
Video: Paradise EGA on another piggyback board
Mass storage: Floppy: 5.25" 1.2MB, MFM HDD

Lion 3500C/T
==========
Info: BIOS saved according to http://mess.redump.net/dumping/dump_bios_using_debug from a 3560C machine
Form factor: notebook
CPU: Intel 486DX2-66
RAM: 2MB, 4MB, 8MB or 16MB
Chipset: ETEQ ET/486H (ET82C491 & ET82C492), 82C206, 82C712
ROM: 128K Video (E0000-EFFFF) & BIOS ROM  (F0000-FFFFF)
Video: Cirrus Logic GD-6420BF/6430 6342 internal VGA, 640x480 256 color display
Mass storage: Floppy 3.5" 1.44MB, 3.5" HDD, 120MB
Input: Trackball connected as a PS/2 mouse
Options: 100 pin expansion port for 3305 Docking station (2xISA16 slots), external keypad
Ports: External VGA, external keyboard, COM1, external keypad, COM2, LPT1, buzzer
Variants: T denotes an active 8.4" display, C a passive 9.5" color display. 3560T/C (486DX2-66), 3530T/C(486DX2-50), 3500T/C (486DX-33), 3500SXT/SXC(486SX-25)

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

#include "emu.h"

#include "bus/isa/isa_cards.h"
#include "bus/lpci/pci.h"
#include "bus/lpci/vt82c505.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "cpu/i386/i386.h"
#include "cpu/i86/i286.h"
#include "machine/at.h"
#include "machine/cs8221.h"
#include "machine/ds128x.h"
#include "machine/idectrl.h"
#include "machine/nvram.h"
#include "machine/ram.h"
#include "machine/vt82c496.h"

#include "emupal.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class at_state : public driver_device
{
public:
	at_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_mb(*this, "mb"),
		m_ram(*this, RAM_TAG)
	{ }

	void pc30iii(machine_config &config);
	void k286i(machine_config &config);
	void ibm5170(machine_config &config);
	void ct386sx(machine_config &config);
	void xb42639(machine_config &config);
	void at486l(machine_config &config);
	void comportii(machine_config &config);
	void comportiii(machine_config &config);
	void comslt286(machine_config &config);
	void dsys200(machine_config &config);
	void ibm5162(machine_config &config);
	void neat(machine_config &config);
	void at386l(machine_config &config);
	void ibm5170a(machine_config &config);
	void ec1842(machine_config &config);
	void at486(machine_config &config);
	void ficpio2(machine_config &config);
	void at386sx(machine_config &config);
	void pc40iii(machine_config &config);
	void pc45iii(machine_config &config);
	void c286lt(machine_config &config);
	void csl286(machine_config &config);
	void c386sx16(machine_config &config);
	void atturbo(machine_config &config);
	void at386(machine_config &config);
	void ncrpc8(machine_config &config);
	void n8810m15(machine_config &config);
	void n8810m55(machine_config &config);
	void ews286(machine_config &config);
	void olyport40(machine_config &config);
	void micral45(machine_config &config);
	void euroat(machine_config &config);
	void pg750(machine_config &config);

	void init_at();
	void init_atpci();

protected:
	required_device<cpu_device> m_maincpu;
	required_device<at_mb_device> m_mb;
	required_device<ram_device> m_ram;
	uint16_t ps1_unk_r(offs_t offset);
	void ps1_unk_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint8_t ps1_portb_r();

	void init_at_common(int xmsbase);
	uint16_t m_ps1_reg[2];

	static void cfg_single_360K(device_t *device);
	static void cfg_single_1200K(device_t *device);
	static void cfg_single_1440K(device_t *device);
	static void cfg_dual_1440K(device_t *device);
	void at16_io(address_map &map) ATTR_COLD;
	void at16_map(address_map &map) ATTR_COLD;
	void at16l_map(address_map &map) ATTR_COLD;
	void at32_io(address_map &map) ATTR_COLD;
	void at32_map(address_map &map) ATTR_COLD;
	void at32l_map(address_map &map) ATTR_COLD;
	void ficpio_io(address_map &map) ATTR_COLD;
	void ficpio_map(address_map &map) ATTR_COLD;
	void neat_io(address_map &map) ATTR_COLD;
	void ps1_16_io(address_map &map) ATTR_COLD;
};

class at_vrom_fix_state : public at_state
{
public:
	using at_state::at_state;

	void init_megapcpla();

	void ibmps1(machine_config &config);
	void megapcpla(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
};

void at_state::at16_map(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x09ffff).bankrw("bank10");
	map(0x0e0000, 0x0fffff).rom().region("bios", 0);
	map(0xfe0000, 0xffffff).rom().region("bios", 0);
}

void at_state::at16l_map(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x09ffff).bankrw("bank10");
	map(0x0e0000, 0x0fffff).rom().region("bios", 0x20000);
	map(0xfe0000, 0xffffff).rom().region("bios", 0x20000);
}

void at_state::at32_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000000, 0x0009ffff).bankrw("bank10");
	map(0x000e0000, 0x000fffff).rom().region("bios", 0);
	map(0x00800000, 0x00800bff).ram().share("nvram");
	map(0xfffe0000, 0xffffffff).rom().region("bios", 0);
}

void at_state::at32l_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000000, 0x0009ffff).bankrw("bank10");
	map(0x000e0000, 0x000fffff).rom().region("bios", 0x20000);
	map(0x00800000, 0x00800bff).ram().share("nvram");
	map(0xfffe0000, 0xffffffff).rom().region("bios", 0x20000);
}

void at_state::ficpio_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000000, 0x0009ffff).bankrw("bank10");
	map(0x00800000, 0x00800bff).ram().share("nvram");
	map(0xfffe0000, 0xffffffff).rom().region("isa", 0x20000);
}

void at_state::at16_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m(m_mb, FUNC(at_mb_device::map));
}

uint16_t at_state::ps1_unk_r(offs_t offset)
{
	return m_ps1_reg[offset];
}

void at_state::ps1_unk_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if((offset == 0) && (data == 0x60))
		data = 0x68;

	COMBINE_DATA(&m_ps1_reg[offset]);
}

uint8_t at_state::ps1_portb_r()
{
	uint8_t data = m_mb->portb_r();
	/* 0x10 is the dram refresh line bit, 15.085us. */
	data = (data & ~0x10) | ((machine().time().as_ticks(66291) & 1) ? 0x10 : 0);

	return data;
}

void at_state::ps1_16_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m(m_mb, FUNC(at_mb_device::map));
	map(0x0061, 0x0061).r(FUNC(at_state::ps1_portb_r));
	map(0x0102, 0x0105).rw(FUNC(at_state::ps1_unk_r), FUNC(at_state::ps1_unk_w));
}

void at_state::neat_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m(m_mb, FUNC(at_mb_device::map));
	map(0x0022, 0x0023).m("cs8221", FUNC(cs8221_device::map));
}

void at_state::at32_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m(m_mb, FUNC(at_mb_device::map));
}

void at_state::ficpio_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m(m_mb, FUNC(at_mb_device::map));
	map(0x00a8, 0x00af).rw("chipset", FUNC(vt82c496_device::read), FUNC(vt82c496_device::write));
	map(0x0170, 0x0177).rw("ide2", FUNC(ide_controller_32_device::cs0_r), FUNC(ide_controller_32_device::cs0_w));
	map(0x01f0, 0x01f7).rw("ide", FUNC(ide_controller_32_device::cs0_r), FUNC(ide_controller_32_device::cs0_w));
	map(0x0370, 0x0377).rw("ide2", FUNC(ide_controller_32_device::cs1_r), FUNC(ide_controller_32_device::cs1_w));
	map(0x03f0, 0x03f7).rw("ide", FUNC(ide_controller_32_device::cs1_r), FUNC(ide_controller_32_device::cs1_w));
	map(0x0cf8, 0x0cff).rw("pcibus", FUNC(pci_bus_device::read), FUNC(pci_bus_device::write));
}

// TODO: verify and remove this hack
void at_vrom_fix_state::init_megapcpla()
{
	uint8_t* ROM = memregion("bios")->base();

	init_at_common(0xa0000);

	// HACK: keyboard checks
	ROM[0x33c2a] = 0x45;
	// To be removed when the keyboard controller from the MegaPC is dumped
	ROM[0x3af37] = 0x45;
	ROM[0x3cf1b] = 0x54;  // this will allow the keyboard to work during the POST memory test
	ROM[0x3fffe] = 0x1c;
	ROM[0x3ffff] = 0x41;  // to correct checksum
}

/**********************************************************
 *
 * Init functions
 *
 **********************************************************/

void at_state::init_at_common(int xmsbase)
{
	address_space& space = m_maincpu->space(AS_PROGRAM);

	/* managed RAM */
	membank("bank10")->set_base(m_ram->pointer());

	if (m_ram->size() > xmsbase)
	{
		offs_t ram_limit = 0x100000 + m_ram->size() - xmsbase;
		space.install_ram(0x100000,  ram_limit - 1, m_ram->pointer() + xmsbase);
	}
}

void at_state::init_at()
{
	init_at_common(0xa0000);
}

void at_state::init_atpci()
{
	init_at_common(0x100000);
}

void at_vrom_fix_state::machine_start()
{
	at_state::machine_start();

	address_space& space = m_maincpu->space(AS_PROGRAM);
	space.install_rom(0xc0000, 0xcffff, memregion("bios")->base());
}

void at_state::cfg_single_1200K(device_t *device)
{
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:0")).set_default_option("525hd");
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:1")).set_default_option(nullptr);
}

void at_state::cfg_single_360K(device_t *device)
{
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:0")).set_default_option("525dd");
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:1")).set_default_option(nullptr);
}

void at_state::cfg_single_1440K(device_t *device)
{
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:0")).set_default_option("35hd");
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:1")).set_default_option(nullptr);
}

void at_state::cfg_dual_1440K(device_t *device)
{
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:0")).set_default_option("35hd");
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:1")).set_default_option("35hd");
}

static void pci_devices(device_slot_interface &device)
{
	device.option_add_internal("vt82c505", VT82C505);
}

void at_state::ibm5170(machine_config &config)
{
	/* basic machine hardware */
	i80286_cpu_device &maincpu(I80286(config, m_maincpu, 12_MHz_XTAL / 2));
	maincpu.set_addrmap(AS_PROGRAM, &at_state::at16_map);
	maincpu.set_addrmap(AS_IO, &at_state::at16_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259_master", FUNC(pic8259_device::inta_cb));
	maincpu.shutdown_callback().set("mb", FUNC(at_mb_device::shutdown));

	config.set_maximum_quantum(attotime::from_hz(60));

	AT_MB(config, m_mb).at_softlists(config);
	m_mb->kbd_clk().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	m_mb->kbd_data().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));

	// FIXME: determine ISA bus clock
	ISA16_SLOT(config, "isa1", 0, "mb:isabus", pc_isa16_cards, "ega", false);
	ISA16_SLOT(config, "isa2", 0, "mb:isabus", pc_isa16_cards, "fdc", false);
	ISA16_SLOT(config, "isa3", 0, "mb:isabus", pc_isa16_cards, "comat", false);
	ISA16_SLOT(config, "isa4", 0, "mb:isabus", pc_isa16_cards, "ide", false);

	pc_kbdc_device &pc_kbdc(PC_KBDC(config, "kbd", pc_at_keyboards, STR_KBD_IBM_PC_AT_84));
	pc_kbdc.out_clock_cb().set(m_mb, FUNC(at_mb_device::kbd_clk_w));
	pc_kbdc.out_data_cb().set(m_mb, FUNC(at_mb_device::kbd_data_w));

	/* internal ram */
	RAM(config, m_ram).set_default_size("1664K").set_extra_options("640K,1024K,2M,4M,8M,15M");
}

void at_state::ibm5170a(machine_config &config)
{
	ibm5170(config);
	m_maincpu->set_clock(16_MHz_XTAL / 2);
}

void at_state::ews286(machine_config &config)
{
	ibm5170(config);
	m_maincpu->set_clock(16_MHz_XTAL / 2); // Exact crystal needs to be verified, 8 MHz according to specification

	subdevice<isa16_slot_device>("isa2")->set_option_machine_config("fdc", cfg_single_1200K); // From pictures but also with a 3.5" as second floppy

	SOFTWARE_LIST(config, "ews286_disk_list").set_original("ews286_flop");

	m_ram->set_default_size("640K");
}

void at_state::ec1842(machine_config &config)
{
	ibm5170(config);
	m_maincpu->set_clock(12'000'000);
}

void at_state::ibm5162(machine_config &config)
{
	ibm5170(config);
	m_maincpu->set_clock(6'000'000);
	subdevice<isa16_slot_device>("isa1")->set_default_option("cga");
}

void at_vrom_fix_state::ibmps1(machine_config &config)
{
	ibm5170(config);

	m_maincpu->set_clock(10_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &at_vrom_fix_state::at16l_map);
	m_maincpu->set_addrmap(AS_IO, &at_vrom_fix_state::ps1_16_io);

	subdevice<isa16_slot_device>("isa1")->set_default_option("vga");
	subdevice<isa16_slot_device>("isa1")->set_fixed(true);
	subdevice<pc_kbdc_device>("kbd")->set_default_option(STR_KBD_MICROSOFT_NATURAL);
}

void at_state::atturbo(machine_config &config)
{
	ibm5170(config);
	m_maincpu->set_clock(12'000'000);
	subdevice<isa16_slot_device>("isa1")->set_default_option("svga_et4k");
	subdevice<pc_kbdc_device>("kbd")->set_default_option(STR_KBD_MICROSOFT_NATURAL);
	ISA16_SLOT(config, "isa5", 0, "mb:isabus", pc_isa16_cards, nullptr, false); // FIXME: determine ISA bus clock
}

void at_state::neat(machine_config &config)
{
	atturbo(config);
	m_maincpu->set_addrmap(AS_IO, &at_state::neat_io);

	ds12885_device &rtc(DS12885(config.replace(), "mb:rtc")); // TODO: move this into the cs8221
	rtc.irq().set("mb:pic8259_slave", FUNC(pic8259_device::ir0_w)); // this is in :mb
	rtc.set_century_index(0x32);

	CS8221(config, "cs8221", 0, "maincpu", "mb:isa", "bios");
}

void at_state::xb42639(machine_config &config)
{
	atturbo(config);
	m_maincpu->set_clock(12'500'000);
}

void at_state::k286i(machine_config &config)
{
	ibm5162(config);
	subdevice<pc_kbdc_device>("kbd")->set_default_option(STR_KBD_MICROSOFT_NATURAL);
	ISA16_SLOT(config, "isa5", 0, "mb:isabus", pc_isa16_cards, nullptr, false); // FIXME: determine ISA bus clock
	ISA16_SLOT(config, "isa6", 0, "mb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa7", 0, "mb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa8", 0, "mb:isabus", pc_isa16_cards, nullptr, false);
}

void at_state::at386(machine_config &config)
{
	i386_device &maincpu(I386(config, m_maincpu, 12'000'000));
	maincpu.set_addrmap(AS_PROGRAM, &at_state::at32_map);
	maincpu.set_addrmap(AS_IO, &at_state::at32_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259_master", FUNC(pic8259_device::inta_cb));

	config.set_maximum_quantum(attotime::from_hz(60));

	AT_MB(config, m_mb).at_softlists(config);
	m_mb->kbd_clk().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	m_mb->kbd_data().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	// on-board devices
	// FIXME: determine ISA bus clock
	ISA16_SLOT(config, "board1", 0, "mb:isabus", pc_isa16_cards, "fdc_smc", true);
	ISA16_SLOT(config, "board2", 0, "mb:isabus", pc_isa16_cards, "comat", true);
	ISA16_SLOT(config, "board3", 0, "mb:isabus", pc_isa16_cards, "ide", true);
	ISA16_SLOT(config, "board4", 0, "mb:isabus", pc_isa16_cards, "lpt", true);
	// ISA cards
	ISA16_SLOT(config, "isa1", 0, "mb:isabus", pc_isa16_cards, "svga_et4k", false);
	ISA16_SLOT(config, "isa2", 0, "mb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa3", 0, "mb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa4", 0, "mb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa5", 0, "mb:isabus", pc_isa16_cards, nullptr, false);

	pc_kbdc_device &pc_kbdc(PC_KBDC(config, "kbd", pc_at_keyboards, STR_KBD_MICROSOFT_NATURAL));
	pc_kbdc.out_clock_cb().set(m_mb, FUNC(at_mb_device::kbd_clk_w));
	pc_kbdc.out_data_cb().set(m_mb, FUNC(at_mb_device::kbd_data_w));

	/* internal ram */
	RAM(config, m_ram).set_default_size("1664K").set_extra_options("2M,4M,8M,15M,16M,32M,64M");
}

void at_state::at386l(machine_config &config)
{
	at386(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &at_state::at32l_map);
}

void at_state::at486(machine_config &config)
{
	i486_device &maincpu(I486(config, m_maincpu, 25'000'000));
	maincpu.set_addrmap(AS_PROGRAM, &at_state::at32_map);
	maincpu.set_addrmap(AS_IO, &at_state::at32_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259_master", FUNC(pic8259_device::inta_cb));

	config.set_maximum_quantum(attotime::from_hz(60));

	AT_MB(config, m_mb).at_softlists(config);
	m_mb->kbd_clk().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	m_mb->kbd_data().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	// on-board devices
	ISA16_SLOT(config, "board1", 0, "mb:isabus", pc_isa16_cards, "fdc_smc", true); // FIXME: deteremine ISA bus clock
	ISA16_SLOT(config, "board2", 0, "mb:isabus", pc_isa16_cards, "comat", true);
	ISA16_SLOT(config, "board3", 0, "mb:isabus", pc_isa16_cards, "ide", true);
	ISA16_SLOT(config, "board4", 0, "mb:isabus", pc_isa16_cards, "lpt", true);
	// ISA cards
	ISA16_SLOT(config, "isa1", 0, "mb:isabus", pc_isa16_cards, "svga_et4k", false);
	ISA16_SLOT(config, "isa2", 0, "mb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa3", 0, "mb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa4", 0, "mb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa5", 0, "mb:isabus", pc_isa16_cards, nullptr, false);

	pc_kbdc_device &pc_kbdc(PC_KBDC(config, "kbd", pc_at_keyboards, STR_KBD_MICROSOFT_NATURAL));
	pc_kbdc.out_clock_cb().set(m_mb, FUNC(at_mb_device::kbd_clk_w));
	pc_kbdc.out_data_cb().set(m_mb, FUNC(at_mb_device::kbd_data_w));

	/* internal ram */
	RAM(config, m_ram).set_default_size("4M").set_extra_options("1M,2M,8M,16M,20M,32M,64M,128M");
}

void at_state::at486l(machine_config &config)
{
	at486(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &at_state::at32l_map);
}

void at_state::at386sx(machine_config &config)
{
	atturbo(config);
	i386sx_device &maincpu(I386SX(config.replace(), m_maincpu, 16'000'000)); /* 386SX */
	maincpu.set_addrmap(AS_PROGRAM, &at_state::at16_map);
	maincpu.set_addrmap(AS_IO, &at_state::at16_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259_master", FUNC(pic8259_device::inta_cb));
}

void at_state::ct386sx(machine_config &config)
{
	at386sx(config);
	m_maincpu->set_addrmap(AS_IO, &at_state::neat_io);
	CS8221(config, "cs8221", 0, "maincpu", "mb:isa", "maincpu");
}

// Commodore PC 30-III
void at_state::pc30iii(machine_config &config)
{
	ibm5170(config);
	m_maincpu->set_clock(6'000'000); // should be 24_MHz_XTAL / 2, but doesn't post with that setting
	subdevice<isa16_slot_device>("isa1")->set_default_option("vga"); // should be ATI EGA Wonder 800+
}

// Commodore PC 40-III
void at_state::pc40iii(machine_config &config)
{
	ibm5170(config);
	m_maincpu->set_clock(6'000'000); // should be 24_MHz_XTAL / 2, but doesn't post with that setting
	subdevice<isa16_slot_device>("isa1")->set_default_option("vga"); // should be onboard Paradise VGA, see ROM declarations
}

void at_vrom_fix_state::megapcpla(machine_config &config)
{
	i486_device &maincpu(I486(config, m_maincpu, 66'000'000 / 2));  // 486SLC
	maincpu.set_addrmap(AS_PROGRAM, &at_vrom_fix_state::at32l_map);
	maincpu.set_addrmap(AS_IO, &at_vrom_fix_state::at32_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259_master", FUNC(pic8259_device::inta_cb));

	config.set_maximum_quantum(attotime::from_hz(60));

	AT_MB(config, m_mb).at_softlists(config);
	m_mb->kbd_clk().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	m_mb->kbd_data().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	// on board devices
	ISA16_SLOT(config, "board1", 0, "mb:isabus", pc_isa16_cards, "fdc_smc", true); // FIXME: determine ISA bus clock
	ISA16_SLOT(config, "board2", 0, "mb:isabus", pc_isa16_cards, "comat", true);
	ISA16_SLOT(config, "board3", 0, "mb:isabus", pc_isa16_cards, "ide", true);
	ISA16_SLOT(config, "board4", 0, "mb:isabus", pc_isa16_cards, "lpt", true);
	// ISA cards
	ISA16_SLOT(config, "isa1", 0, "mb:isabus", pc_isa16_cards, "svga_dm", false);  // closest to the CL-GD5420
	ISA16_SLOT(config, "isa2", 0, "mb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa3", 0, "mb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa4", 0, "mb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa5", 0, "mb:isabus", pc_isa16_cards, nullptr, false);

	pc_kbdc_device &pc_kbdc(PC_KBDC(config, "kbd", pc_at_keyboards, STR_KBD_MICROSOFT_NATURAL));
	pc_kbdc.out_clock_cb().set(m_mb, FUNC(at_mb_device::kbd_clk_w));
	pc_kbdc.out_data_cb().set(m_mb, FUNC(at_mb_device::kbd_data_w));

	/* internal ram */
	RAM(config, m_ram).set_default_size("4M").set_extra_options("2M,8M,15M,16M,32M,64M,128M,256M");

	/* software lists */
	SOFTWARE_LIST(config, "disk_list").set_original("megapc");
}

void at_state::ficpio2(machine_config &config)
{
	i486_device &maincpu(I486(config, m_maincpu, 25'000'000));
	maincpu.set_addrmap(AS_PROGRAM, &at_state::ficpio_map);
	maincpu.set_addrmap(AS_IO, &at_state::ficpio_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259_master", FUNC(pic8259_device::inta_cb));

	config.set_maximum_quantum(attotime::from_hz(60));

	AT_MB(config, m_mb).at_softlists(config);
	m_mb->kbd_clk().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	m_mb->kbd_data().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));

	ds12885_device &rtc(DS12885(config.replace(), "mb:rtc"));
	rtc.irq().set("mb:pic8259_slave", FUNC(pic8259_device::ir0_w)); // this is in :mb
	rtc.set_century_index(0x32);

	RAM(config, m_ram).set_default_size("4M").set_extra_options("1M,2M,8M,16M,32M,64M,128M");

	// on board devices
	ISA16_SLOT(config, "board1", 0, "mb:isabus", pc_isa16_cards, "fdc_smc", true); // FIXME: determine ISA bus clock
	ISA16_SLOT(config, "board2", 0, "mb:isabus", pc_isa16_cards, "comat", true);
	ISA16_SLOT(config, "board3", 0, "mb:isabus", pc_isa16_cards, "lpt", true);

	ide_controller_32_device &ide(IDE_CONTROLLER_32(config, "ide").options(ata_devices, "hdd", nullptr, true));
	ide.irq_handler().set("mb:pic8259_slave", FUNC(pic8259_device::ir6_w));
	ide_controller_32_device &ide2(IDE_CONTROLLER_32(config, "ide2").options(ata_devices, "cdrom", nullptr, true));
	ide2.irq_handler().set("mb:pic8259_slave", FUNC(pic8259_device::ir7_w));

	PCI_BUS(config, "pcibus", 0).set_busnum(0);
	PCI_CONNECTOR(config, "pcibus:0", pci_devices, "vt82c505", true);
	ISA16_SLOT(config, "isa1", 0, "mb:isabus", pc_isa16_cards, "svga_et4k", false);
	ISA16_SLOT(config, "isa2", 0, "mb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa3", 0, "mb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa4", 0, "mb:isabus", pc_isa16_cards, nullptr, false);

	pc_kbdc_device &pc_kbdc(PC_KBDC(config, "kbd", pc_at_keyboards, STR_KBD_MICROSOFT_NATURAL));
	pc_kbdc.out_clock_cb().set(m_mb, FUNC(at_mb_device::kbd_clk_w));
	pc_kbdc.out_data_cb().set(m_mb, FUNC(at_mb_device::kbd_data_w));

	vt82c496_device &chipset(VT82C496(config, "chipset"));
	chipset.set_cputag(m_maincpu);
	chipset.set_ramtag(m_ram);
	chipset.set_isatag("isa");
}

// Compaq Portable III
void at_state::comportiii(machine_config &config)
{
	/* basic machine hardware */
	i80286_cpu_device &maincpu(I80286(config, m_maincpu, 48_MHz_XTAL / 4 /*12000000*/));
	maincpu.set_addrmap(AS_PROGRAM, &at_state::at16_map);
	maincpu.set_addrmap(AS_IO, &at_state::at16_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259_master", FUNC(pic8259_device::inta_cb));
	maincpu.shutdown_callback().set("mb", FUNC(at_mb_device::shutdown));

	config.set_maximum_quantum(attotime::from_hz(60));

	AT_MB(config, m_mb).at_softlists(config);
	m_mb->kbd_clk().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	m_mb->kbd_data().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));

	// FIXME: determine ISA bus clock
	ISA16_SLOT(config, "board1", 0, "mb:isabus", pc_isa16_cards, "fdc", true).set_option_machine_config("fdc", cfg_single_1200K);
	ISA16_SLOT(config, "board2", 0, "mb:isabus", pc_isa16_cards, "comat", true);
	ISA16_SLOT(config, "board3", 0, "mb:isabus", pc_isa16_cards, "hdc", true);
	ISA16_SLOT(config, "board4", 0, "mb:isabus", pc_isa16_cards, "cga_cportiii", true);
	ISA16_SLOT(config, "isa1",   0, "mb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa2",   0, "mb:isabus", pc_isa16_cards, nullptr, false);

	pc_kbdc_device &pc_kbdc(PC_KBDC(config, "kbd", pc_at_keyboards, STR_KBD_IBM_PC_AT_84));
	pc_kbdc.out_clock_cb().set(m_mb, FUNC(at_mb_device::kbd_clk_w));
	pc_kbdc.out_data_cb().set(m_mb, FUNC(at_mb_device::kbd_data_w));

	/* internal ram */
	RAM(config, m_ram).set_default_size("640K").set_extra_options("1152K,1664K,2176K,2688K,4736K,6784K");
}

void at_state::comportii(machine_config &config)
{
	ibm5170(config);
	m_maincpu->set_clock(48_MHz_XTAL / 6);

	subdevice<isa16_slot_device>("isa2")->set_option_machine_config("fdc", cfg_single_360K);
	subdevice<isa16_slot_device>("isa4")->set_default_option("hdc");
	m_ram->set_default_size("640K").set_extra_options("1152K,1664K,2176K,2688K,4224K");
}

// Nixdorf 8810 M55
void at_state::n8810m15(machine_config &config)
{
	ibm5170(config);
	m_maincpu->set_clock(6'000'000);
	subdevice<isa16_slot_device>("isa1")->set_default_option("cga");
}

// Nixdorf 8810 M55
void at_state::n8810m55(machine_config &config)
{
	ibm5170(config);
	m_maincpu->set_clock(6'000'000);
	subdevice<isa16_slot_device>("isa1")->set_default_option("ega");
}

// AEG Olympia Olyport 40-21
void at_state::olyport40(machine_config &config)
{
	neat(config);
	m_maincpu->set_clock(12'000'000);
	subdevice<isa16_slot_device>("isa1")->set_default_option("cga");
}

// Bull Micral 45
void at_state::micral45(machine_config &config)
{
	atturbo(config);
	m_maincpu->set_clock(12'000'000);
	subdevice<isa16_slot_device>("isa1")->set_default_option("ega");
}

void at_state::euroat(machine_config &config)
{
	ibm5170(config);
	m_maincpu->set_clock(24_MHz_XTAL / 2); // Bus speed can be set up to CPU speed

	subdevice<isa16_slot_device>("isa2")->set_option_machine_config("fdc", cfg_single_1440K); // From pictures but also with a 3.5" as second floppy

	m_ram->set_default_size("640K");
}

// Siemens PG 750
void at_state::pg750(machine_config &config)
{
	i386_device &maincpu(I386(config, m_maincpu, 12'000'000));
	maincpu.set_addrmap(AS_PROGRAM, &at_state::at32_map);
	maincpu.set_addrmap(AS_IO, &at_state::at32_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259_master", FUNC(pic8259_device::inta_cb));

	config.set_maximum_quantum(attotime::from_hz(60));

	AT_MB(config, m_mb).at_softlists(config);
	m_mb->kbd_clk().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	m_mb->kbd_data().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	ds12885_device &rtc(DS12885(config.replace(), "mb:rtc")); // TODO: move this into the cs8221
	rtc.irq().set("mb:pic8259_slave", FUNC(pic8259_device::ir0_w)); // this is in :mb
	rtc.set_century_index(0x32);

	// on-board devices
	ISA16_SLOT(config, "board1", 0, "mb:isabus", pc_isa16_cards, "fdc", true).set_option_machine_config("fdc", cfg_dual_1440K); // FIXME: deteremine ISA bus clock
	ISA16_SLOT(config, "board2", 0, "mb:isabus", pc_isa16_cards, "comat", true);
	ISA16_SLOT(config, "board3", 0, "mb:isabus", pc_isa16_cards, "lpt", true);
	// ISA cards
	ISA16_SLOT(config, "isa1", 0, "mb:isabus", pc_isa16_cards, "ega", false);
	ISA16_SLOT(config, "isa2", 0, "mb:isabus", pc_isa16_cards, "hdc", false);
	ISA16_SLOT(config, "isa3", 0, "mb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa4", 0, "mb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa5", 0, "mb:isabus", pc_isa16_cards, nullptr, false);

	pc_kbdc_device &pc_kbdc(PC_KBDC(config, "kbd", pc_at_keyboards, STR_KBD_MICROSOFT_NATURAL));
	pc_kbdc.out_clock_cb().set(m_mb, FUNC(at_mb_device::kbd_clk_w));
	pc_kbdc.out_data_cb().set(m_mb, FUNC(at_mb_device::kbd_data_w));

	/* internal ram */
	RAM(config, m_ram).set_default_size("3712K");
}

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

//**************************************************************************
//  IBM systems
//**************************************************************************
ROM_START( ibm5170 )
	ROM_REGION16_LE(0x20000, "bios", 0) // - IBM 5170, 6 Mhz, one wait state RAM or 8 Mhz, one wait state RAM

	ROM_SYSTEM_BIOS( 0, "rev1", "IBM PC/AT 5170 01/10/84")
	ROMX_LOAD( "6181028.u27", 0x10000, 0x8000, CRC(f6573f2a) SHA1(3e52cfa6a6a62b4e8576f4fe076c858c220e6c1a), ROM_SKIP(1) | ROM_BIOS(0)) /* T 6181028 8506AAA // TMM23256P-5878 // (C)IBM CORP 1981,-1984 */
	ROMX_LOAD( "6181029.u47", 0x10001, 0x8000, CRC(7075fbb2) SHA1(a7b885cfd38710c9bc509da1e3ba9b543a2760be), ROM_SKIP(1) | ROM_BIOS(0)) /* T 6181029 8506AAA // TMM23256P-5879 // (C)IBM CORP 1981,-1984 */

	ROM_SYSTEM_BIOS( 1, "rev2", "IBM PC/AT 5170 06/10/85")  /* Another verification of these crcs would be nice */
	ROMX_LOAD( "6480090.u27", 0x10000, 0x8000, CRC(99703aa9) SHA1(18022e93a0412c8477e58f8c61a87718a0b9ab0e), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "6480091.u47", 0x10001, 0x8000, CRC(013ef44b) SHA1(bfa15d2180a1902cb6d38c6eed3740f5617afd16), ROM_SKIP(1) | ROM_BIOS(1))

	ROM_SYSTEM_BIOS( 2, "landmark", "Landmark/Supersoft diagnostic ROMs") // use Hercules or MDA
	ROMX_LOAD( "5170_even_u27_ 27256.bin", 0x10000, 0x8000, CRC(6790392d) SHA1(c4a5310341f346dd072d096152060ef5e4430a7f), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD( "5170_odd_u47_ 27256.bin", 0x10001, 0x8000, CRC(4c0f3db4) SHA1(97a0cf589b93551ed1d03bd622cbc8fd5634512f), ROM_SKIP(1) | ROM_BIOS(2))

//  ROM_SYSTEM_BIOS( 3, "atdiag", "IBM PC/AT 5170 w/Super Diagnostics")
//  ROMX_LOAD( "atdiage.bin", 0xf8000, 0x4000, CRC(e8855d0c) SHA1(c9d53e61c08da0a64f43d691bf6cadae5393843a), ROM_SKIP(1) | ROM_BIOS(3))
//  ROMX_LOAD( "atdiago.bin", 0xf8001, 0x4000, CRC(606fa71d) SHA1(165e45bae7ae2da274f1e645c763c5bfcbde027b), ROM_SKIP(1) | ROM_BIOS(3))

	/* Mainboard PALS */
	ROM_REGION( 0x2000, "pals", 0 )
	ROM_LOAD( "1501824_717750.mmipal14l4.u87.jed", 0x0000, 0x02E7, CRC(3c819a27) SHA1(d2f4889e628dbbef50b7f48cb1d1a313232bacc8)) /* MMI 1501824 717750 // (C)1983 IBM(M) */
	ROM_LOAD( "1503135_705075.mmipal14l4.u130.jed", 0x02E7, 0x02E7, CRC(aac77198) SHA1(b318da3a1fbe5402836c1b548e231e0794d0c032)) /* MMI 1503135 705075 // (C) IBM CORP 83 */
	/* P/N 6320947 Serial/Parallel ISA expansion card PAL */
	ROM_LOAD( "1503085.mmipal.u14.jed", 0x1000, 0x0800, NO_DUMP) /* MMI 1503085 8449 // (C) IBM CORP 83 */ /* Not sure of type */

	/* Mainboard PROMS */
	ROM_REGION( 0x2000, "proms", 0 )
	ROM_LOAD( "1501814.82s123an.u115", 0x0000, 0x0020, CRC(849c9217) SHA1(2955ae1705c3b59170f1373f99b3ea5c174c4544)) /* N82S123AN 8713 // SK-D 1501814 */
	ROM_LOAD( "55x8041.82s147an.u72", 0x0020, 0x0200, CRC(f2cc4fe6) SHA1(e285468516bd05083155a8a272583deef655315a)) /* S N82S147AN 8709 // V-C55X8041 */
ROM_END

ROM_START( ibm5170a )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_SYSTEM_BIOS( 0, "rev3", "IBM PC/AT 5170 11/15/85")
	ROMX_LOAD( "61x9266.u27", 0x10000, 0x8000, CRC(4995be7a) SHA1(8e8e5c863ae3b8c55fd394e345d8cca48b6e575c), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "61x9265.u47", 0x10001, 0x8000, CRC(c32713e4) SHA1(22ed4e2be9f948682891e2fd056a97dbea01203c), ROM_SKIP(1) | ROM_BIOS(0))

	ROM_SYSTEM_BIOS( 1, "3270at", "IBM 3270 PC/AT 5281 11/15/85") /* pretty much just a part string and checksum change from the 5170 rev3 */
	ROMX_LOAD( "62x0820.u27", 0x10000, 0x8000, CRC(e9cc3761) SHA1(ff9373c1a1f34a32fb6acdabc189c61b01acf9aa), ROM_SKIP(1) | ROM_BIOS(1)) /* T 62X0820-U27 8714HAK // TMM23256P-6746 // (C)IBM CORP 1981,-1985 */
	ROMX_LOAD( "62x0821.u47", 0x10001, 0x8000, CRC(b5978ccb) SHA1(2a1aeb9ae3cd7e60fc4c383ca026208b82156810), ROM_SKIP(1) | ROM_BIOS(1)) /* T 62X0821-U47 8715HAK // TMM23256P-6747 // (C)IBM CORP 1981,-1985 */

	/* Mainboard PALS */
	ROM_REGION( 0x2000, "pals", 0 )
	ROM_LOAD( "1501824_717750.mmipal14l4.u87.jed", 0x0000, 0x02E7, CRC(3c819a27) SHA1(d2f4889e628dbbef50b7f48cb1d1a313232bacc8)) /* MMI 1501824 717750 // (C)1983 IBM(M) */
	ROM_LOAD( "1503135_705075.mmipal14l4.u130.jed", 0x02E7, 0x02E7, CRC(aac77198) SHA1(b318da3a1fbe5402836c1b548e231e0794d0c032)) /* MMI 1503135 705075 // (C) IBM CORP 83 */    /* P/N 6320947 Serial/Parallel ISA expansion card PAL */
	ROM_LOAD( "1503085.mmipal.u14.jed", 0x1000, 0x0800, NO_DUMP) /* MMI 1503085 8449 // (C) IBM CORP 83 */ /* Not sure of type */

	/* Mainboard PROMS */
	ROM_REGION( 0x2000, "proms", 0 )
	ROM_LOAD( "1501814.82s123an.u115", 0x0000, 0x0020, CRC(849c9217) SHA1(2955ae1705c3b59170f1373f99b3ea5c174c4544)) /* N82S123AN 8713 // SK-D 1501814 */
	ROM_LOAD( "55x8041.82s147an.u72", 0x0020, 0x0200, CRC(f2cc4fe6) SHA1(e285468516bd05083155a8a272583deef655315a)) /* S N82S147AN 8709 // V-C55X8041 */
ROM_END


ROM_START( ibm5162 ) //MB p/n 62x1168 - IBM 5162, 6 Mhz, zero wait state RAM
	ROM_REGION16_LE(0x20000, "bios", 0)

	ROM_LOAD16_BYTE( "78x7460.u34", 0x10000, 0x8000, CRC(1db4bd8f) SHA1(7be669fbb998d8b4626fefa7cd1208d3b2a88c31)) /* 78X7460 U34 // (C) IBM CORP // 1981-1986 */
	ROM_LOAD16_BYTE( "78x7461.u35", 0x10001, 0x8000, CRC(be14b453) SHA1(ec7c10087dbd53f9c6d1174e8f14212e2aec1818)) /* 78X7461 U35 // (C) IBM CORP // 1981-1986 */

	/* Mainboard PALS */
	ROM_REGION( 0x2000, "pals", 0 )
	ROM_LOAD( "59x7599.mmipal20l8.u27.jed", 0x0000, 0x02E7, NO_DUMP) /* MMI PAL20L8ACN5 8631 // N59X7599 IBM (C)85 K3 */
	ROM_LOAD( "1503135.mmipal14l4.u81.jed", 0x02E7, 0x02E7, CRC(aac77198) SHA1(b318da3a1fbe5402836c1b548e231e0794d0c032)) /* MMI 1503135 8625 // (C) IBM CORP 83 */
	/* P/N 6320947 Serial/Parallel ISA expansion card PAL */
	ROM_LOAD( "1503085.mmipal.u14.jed", 0x1000, 0x0800, NO_DUMP) /* MMI 1503085 8449 // (C) IBM CORP 83 */ /* Not sure of type */

	/* Mainboard PROMS */
	ROM_REGION( 0x2000, "proms", 0 )
	ROM_LOAD( "1501814.82s123an.u72", 0x0000, 0x0020, CRC(849c9217) SHA1(2955ae1705c3b59170f1373f99b3ea5c174c4544)) /* N82S123AN 8623 // SK-U 1501814 */
	ROM_LOAD( "59x7594.82s147an.u90", 0x0020, 0x0200, NO_DUMP) /* S N82S147AN 8629 // VCT 59X7594 */
ROM_END


// According to http://nerdlypleasures.blogspot.com/2014/04/the-original-8-bit-ide-interface.html
// the IBM PS/1 Model 2011 use a customised version of the XTA (8-bit IDE) harddisk interface

// https://en.wikipedia.org/wiki/IBM_PS/1
// http://ps-2.kev009.com/pcpartnerinfo/ctstips/937e.htm
// https://ps1stuff.wordpress.com/documentation/ibm-ps1-model-2011/
// https://barotto.github.io/IBMulator/#download

ROM_START( ibm2011 )
	ROM_REGION16_LE( 0x40000, "bios", 0)
	// Spanish version
	ROM_SYSTEM_BIOS( 0, "2011es", "IBM PS/1 2011 ES")
	ROMX_LOAD( "ibm_1057757_24-05-90.bin", 0x00000, 0x20000, CRC(c8f81ea4) SHA1(925ed0e98f9f2997cb86554ef384bcfaf2a4ecbe), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "ibm_1057757_29-15-90.bin", 0x00001, 0x20000, CRC(c2dd6b5c) SHA1(f6b5785002dd628b6b1fb3bb101e076299eba3b6), ROM_SKIP(1) |  ROM_BIOS(0))
	// US version
	ROM_SYSTEM_BIOS( 1, "2011us", "IBM PS/1 2011 US") // constant resets
	ROMX_LOAD( "1057754.bin", 0x00000, 0x20000, CRC(648a6a61) SHA1(6cebaf9f2431e67fea37f34b06916264d6737ab6), ROM_SKIP(1) |  ROM_BIOS(1))
	ROMX_LOAD( "1057756.bin", 0x00001, 0x20000, CRC(862f94ac) SHA1(1eba7fa20301403db7c4f53032267902191ea2c7), ROM_SKIP(1) |  ROM_BIOS(1))
ROM_END

ROM_START( ibm2011rd ) // these international versions were shipped with DOS in a ROM disk and require a different memory map, they don't yet load properly
	ROM_REGION16_LE( 0x80000, "bios", 0)
	// Swedish version
	ROM_SYSTEM_BIOS( 0, "2011se", "IBM PS/1 2011 SE")
	ROMX_LOAD( "ibm2011se_f80000.bin", 0x00000, 0x40000, CRC(1b90693b) SHA1(2cdcfda55fea25a991c1568ff398d97c5e07e96d),  ROM_BIOS(0))
	ROMX_LOAD( "ibm2011se_fc0000.bin", 0x40000, 0x40000, CRC(ef7aa453) SHA1(993dd6e17c6fd5c2ef513d94383f36b1929d1936),  ROM_BIOS(0))
	// Portuguese version
	ROM_SYSTEM_BIOS( 1, "2011pt", "IBM PS/1 2011 PT")
	ROMX_LOAD( "u18_x1_1057451.bin", 0x00000, 0x20000, CRC(0484e15d) SHA1(39fb05843c8371f4b716679e6ce512bcf5a05dac), ROM_SKIP(1) |  ROM_BIOS(1))
	ROMX_LOAD( "u36_x4_1057449.bin", 0x00001, 0x20000, CRC(23d7e4fe) SHA1(9c89efa61fc77485b65fff9133d6a19caca553e9), ROM_SKIP(1) |  ROM_BIOS(1))
	ROMX_LOAD( "u23_x2_1057757.bin", 0x40000, 0x20000, CRC(c8f81ea4) SHA1(925ed0e98f9f2997cb86554ef384bcfaf2a4ecbe), ROM_SKIP(1) |  ROM_BIOS(1))
	ROMX_LOAD( "u28_x3_1057759.bin", 0x40001, 0x20000, CRC(c2dd6b5c) SHA1(f6b5785002dd628b6b1fb3bb101e076299eba3b6), ROM_SKIP(1) |  ROM_BIOS(1))
	// German version
	ROM_SYSTEM_BIOS( 2, "2011de", "IBM PS/1 2011 DE")
	ROMX_LOAD( "x1_1057866_u10.bin", 0x00000, 0x20000, CRC(ef0f0bb4) SHA1(d1e4c081f1a74732eb6e37a3bfb9403819b7d891), ROM_SKIP(1) |  ROM_BIOS(2))
	ROMX_LOAD( "x4_1057864_u36.bin", 0x00001, 0x20000, CRC(16d357ff) SHA1(6521b160bf0dd05b890ad197d9c9359d806da18a), ROM_SKIP(1) |  ROM_BIOS(2))
	ROMX_LOAD( "x2_1057757_u23.bin", 0x40000, 0x20000, CRC(c8f81ea4) SHA1(925ed0e98f9f2997cb86554ef384bcfaf2a4ecbe), ROM_SKIP(1) |  ROM_BIOS(2))
	ROMX_LOAD( "x3_1057759_u28.bin", 0x40001, 0x20000, CRC(c2dd6b5c) SHA1(f6b5785002dd628b6b1fb3bb101e076299eba3b6), ROM_SKIP(1) |  ROM_BIOS(2))
	// Italian version
	ROM_SYSTEM_BIOS( 3, "2011it", "IBM_PS/1 2011 IT")
	ROMX_LOAD( "x1-1057630-u18.bin", 0x00000, 0x20000, CRC(3843830c) SHA1(68b2f443b6ceadbc94a725fe66ad9c9685490dcb), ROM_SKIP(1) |  ROM_BIOS(3))
	ROMX_LOAD( "x4-1057628-u36.bin", 0x00001, 0x20000, CRC(1ddf3afb) SHA1(da55abaf4f775e2e3efdd952beb9f97769e3cac3), ROM_SKIP(1) |  ROM_BIOS(3))
	ROMX_LOAD( "x2_1057757_u23.bin", 0x40000, 0x20000, CRC(c8f81ea4) SHA1(925ed0e98f9f2997cb86554ef384bcfaf2a4ecbe), ROM_SKIP(1) |  ROM_BIOS(3))
	ROMX_LOAD( "x3_1057759_u28.bin", 0x40001, 0x20000, CRC(c2dd6b5c) SHA1(f6b5785002dd628b6b1fb3bb101e076299eba3b6), ROM_SKIP(1) |  ROM_BIOS(3))
	// UK version
	ROM_SYSTEM_BIOS( 4, "2011uk", "IBM_PS/1 2011 UK")
	ROMX_LOAD( "u18_x1.bin", 0x00000, 0x20000, CRC(029c4d8a) SHA1(bf2f56ac2e03098144b3dcc34f7daa09c8e08288), ROM_SKIP(1) |  ROM_BIOS(4))
	ROMX_LOAD( "u36_x4.bin", 0x00001, 0x20000, CRC(bf6c5631) SHA1(68cbff7e229cd77ae8c2e8835dbb9b3047f41e4c), ROM_SKIP(1) |  ROM_BIOS(4))
	ROMX_LOAD( "u23_x2.bin", 0x40000, 0x20000, CRC(c8f81ea4) SHA1(925ed0e98f9f2997cb86554ef384bcfaf2a4ecbe), ROM_SKIP(1) |  ROM_BIOS(4))
	ROMX_LOAD( "u28_x3.bin", 0x40001, 0x20000, CRC(c2dd6b5c) SHA1(f6b5785002dd628b6b1fb3bb101e076299eba3b6), ROM_SKIP(1) |  ROM_BIOS(4))
ROM_END

// From Wikipedia:
// Model     MB FRU    CPU                   ISA Sl.  RAM   VRAM   Hard-Drive         Serial/Modem
// 2121-C42  92F9690   Intel 80386SX @ 16 MHz  0      2 MB  256KB  95F4720  40MB IDE  2400 baud modem
// 2121-B82  92F9690   Intel 80386SX @ 16 MHz  2      2 MB  256KB  92F9943  80MB IDE  2400 baud modem
// 2121-C92            Intel 80386SX @ 16 MHz  0      2 MB  256KB          129MB IDE  2400 baud modem
// 2121-G42            Intel 80386SX @ 20 MHz  0      2 MB  256KB           40MB IDE  2400 baud modem
// 2121-A82            Intel 80386SX @ 20 MHz  2      2 MB  256KB           40MB IDE  2400 baud modem
// 2121-S92            Intel 80386SX @ 20 MHz  0      2 MB  256KB          129MB IDE  2400 baud modem
// 2121-M82            Intel 80386SX @ 20 MHz  2      2 MB  256KB           80MB IDE  2400 baud modem
// 2121-A62                                           2     256KB  56F8863 160MB IDE  2400 baud modem
// 2121-A92                                                 256KB                     serial port
// 2121-A94            Intel 80386SX @ 20 MHz  2    6 MB    256KB          129MB IDE  2400 baud modem

ROM_START( ibm2121 )
	ROM_REGION16_LE( 0x40000, "bios", 0)
	ROM_SYSTEM_BIOS( 0, "2121", "IBM PS/1 2121" )
	ROMX_LOAD( "fc0000.bin", 0x00000, 0x40000, CRC(96bbaf52) SHA1(8737d805444837023a58702279f8fe6e7f08e7ba), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "2121us", "IBM PS/1 2121 US" )
	ROMX_LOAD( "ibm2121us_fc0000.bin", 0x00000, 0x40000, CRC(817aad71) SHA1(43b7b84390fcc081a946cdb4bdce4ba7a4a88074), ROM_BIOS(1))
ROM_END

ROM_START( ibm2121rd ) // international versions shipped with ROM DOS, need a different memory map at least
	ROM_REGION16_LE( 0x80000, "bios", 0)
	ROM_SYSTEM_BIOS( 0, "2121sp", "IBM PS/1 2121 Spanish" )
	ROMX_LOAD( "ibm2121sp_f80000.bin", 0x00000, 0x40000, CRC(90505c4b) SHA1(59becaec25644820a78464d66e472a8a225d94cc), ROM_BIOS(0))
	ROMX_LOAD( "ibm2121sp_fc0000.bin", 0x40000, 0x40000, CRC(f83fac75) SHA1(a42b1b9465983392eaa0159d4bfc30620a7af499), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "2121fr", "IBM PS/1 2121 French" )
	ROMX_LOAD( "ibm2121fr_f80000.bin", 0x00000, 0x40000, CRC(9c6de65d) SHA1(6b219c9480a06bc9218e8212acc7cfd1ceaccd4b), ROM_BIOS(1))
	ROMX_LOAD( "ibm2121fr_fc0000.bin", 0x40000, 0x40000, CRC(f83fac75) SHA1(a42b1b9465983392eaa0159d4bfc30620a7af499), ROM_BIOS(1))
ROM_END

// http://ps-2.kev009.com/pcpartnerinfo/ctstips/937e.htm
ROM_START( ibm2123 )
	ROM_REGION16_LE( 0x20000, "bios", 0)
	ROM_LOAD( "ps1_2123_87f4794_rom.bin", 0x00000, 0x20000, CRC(64f921b8) SHA1(e1856bf3dd3ce21f44078aeca1f58c491b202ad2))
ROM_END

// From Wikipedia:
// 2133 Desktop case. The 3x3 references the available slots and drive bays.
// 2155 Desktop case larger than 2133. The 5x5 references the available slots and drive bays. Including a 5.25" bay.
// 2168 Tower unit. The 6x8 references the available slots and bays. Including 5.25" bays.
// Model     MB FRU      CPU                        RAM   SIMM          Video chip   VRAM   Hard-Drive          Notes
// 2133-711  93F2397  Intel 80386SX @ 25 MHz    2 MB  2×72 Pin FPM                   256KB  59G9567  85MB IDE
// 2133-811          Intel 80386SX @ 25 MHz     4 MB                                                 85MB IDE
// 2133-13   ???      Intel 80386SX @ 25 MHz    2 MB  2x72 Pin FPM                   256KB
// 2133-W13          Intel 80386SX @ 25 MHz     2 MB                                                129MB IDE
// 2133-13T  65G3766  Intel 80486SX @ 25 MHz    4 MB  2×72 Pin FPM                   256KB  93F2329 129MB IDE
// 2133-?43  34G1885  Intel 80486SX @ 20 MHz    4 MB  2×30 Pin FPM                   512KB  93F2329 129MB IDE
// 2133-?50  34G1848  Intel 80486SX @ 25 MHz    4 MB  2×30 Pin FPM                   512KB  93F2329 129MB IDE
// 2133-?53  34G1848  Intel 80486SX @ 25 MHz    4 MB  2×30 Pin FPM                   512KB  93F2329 129MB IDE
// 2133-652          Intel 80486SX @ 33 MHz     4 MB  4×72 Pin FPM  Cirrus CL-GD5424 512KB  84G3927 171MB IDE
// 2133-575          Intel 80486DX @ 33 MHz     4 MB  4×72 Pin FPM                   512KB          170MB IDE
// 2133-594          Intel 80486DX2 @66 MHz     4 MB  4×72 Pin FPM                   512KB          253MB IDE
// 2133-E11          Intel 80386SX @ 25 MHz     2 MB  2×72 Pin FPM  Cirrus CL-GD5424 512 KB          85MB IDE   Canada models, English model
// 2133-F11          Intel 80386SX @ 25 MHz     2 MB  2×72 Pin FPM  Cirrus CL-GD5424 512 KB          85MB IDE   Canada models, French model
// 2133-E43          Intel 80486SX @ 20 MHz     2 MB  8×30 Pin FPM  Tseng ET4000     512KB          129MB IDE   Canada models, English model
// 2133-F43          Intel 80486SX @ 20 MHz     2 MB  8×30 Pin FPM  Tseng ET4000     512KB          129MB IDE   Canada models, French model
// 2133-E53          Intel 80486SX @ 25 MHz     2 MB  8×30 Pin FPM  Tseng ET4000     512KB          129MB IDE   Canada models, English model
// 2133-F53          Intel 80486SX @ 25 MHz     2 MB  8×30 Pin FPM  Tseng ET4000     512KB          129MB IDE   Canada models, French model

ROM_START( ibm2133 )
	ROM_REGION32_LE( 0x20000, "bios", 0 )
	ROM_LOAD( "ps1_2133_52g2974_rom.bin", 0x00000, 0x20000, CRC(89fc7600) SHA1(758e161353f6781c39ac67f1ba293c14038b17dc))
ROM_END


//**************************************************************************
//  Apricot systems
//
// http://bbs.actapricot.org/files/ , http://insight.actapricot.org/insight/products/main.htm
//
//**************************************************************************

// Apricot XEN-S (Venus I Motherboard 286)
// BIOS-String: apricot / Phoenix ROM BIOS PLUS Version 3.10.17 / Apricot XEN-S 25th June 1992
// Gate A20 failure - MAME message: char SEL checker, contact MAMEdev
ROM_START( xb42639 )
	/* actual VGA BIOS not dumped*/
	ROM_REGION16_LE(0x20000, "bios", 0)
	// XEN-S (Venus I Motherboard)
	ROM_LOAD16_BYTE( "3-10-17i.lo", 0x10000, 0x8000, CRC(3786ca1e) SHA1(c682d7c76f234559d03bcf21010c13c4dbeafb69))
	ROM_LOAD16_BYTE( "3-10-17i.hi", 0x10001, 0x8000, CRC(d66710eb) SHA1(e8c1cd5f9ecfbd8825655e416d7ddf2ae362e69b))
ROM_END

// Apricot XEN-S (Venus II Motherboard 286)
// BIOS-String: apricot XEN-S Series Personal Computer / Phoenix ROM BIOS PLUS Version 3.10.04 / XEN-S II BIOS VR 1.2.17 16th October 1990
// Gate A20 failure
ROM_START( xb42639a )
	/* actual VGA BIOS not dumped*/
	ROM_REGION16_LE(0x20000, "bios", 0)
	// XEN-S (Venus II Motherboard)
	ROM_LOAD16_BYTE( "10217.lo", 0x10000, 0x8000, CRC(ea53406f) SHA1(2958dfdbda14de4e6b9d6a8c3781131ab1e32bef))
	ROM_LOAD16_BYTE( "10217.hi", 0x10001, 0x8000, CRC(111725cf) SHA1(f6018a45bda4476d40c5881fb0a506ff75ec1688))
ROM_END

// Apricot XEN-S (Venus I Motherboard 386)
// BIOS-String: apricot / Phoenix ROM BIOS PLUS Version 3.10.17 / Apricot XEN-S 25th June 1992
// MAME message: char SEL checker, contact MAMEdev
ROM_START( xb42664 )
	/* actual VGA BIOS not dumped */
	ROM_REGION32_LE(0x20000, "bios", 0)
	// XEN-S (Venus I Motherboard)
	ROM_LOAD16_BYTE( "3-10-17i.lo", 0x10000, 0x8000, CRC(3786ca1e) SHA1(c682d7c76f234559d03bcf21010c13c4dbeafb69))
	ROM_LOAD16_BYTE( "3-10-17i.hi", 0x10001, 0x8000, CRC(d66710eb) SHA1(e8c1cd5f9ecfbd8825655e416d7ddf2ae362e69b))
ROM_END

// Apricot XEN-S (Venus II Motherboard 386)
// BIOS-String: apricot XEN-S Series Personal Computer / Phoenix ROM BIOS PLUS VERSION 3.10.04 / XEN-S II BIOS VR 1.2.17 16th October 1990
ROM_START( xb42664a )
	/* actual VGA BIOS not dumped*/
	ROM_REGION32_LE(0x20000, "bios", 0)
	// XEN-S (Venus II Motherboard)
	ROM_LOAD16_BYTE( "10217.lo", 0x10000, 0x8000, CRC(ea53406f) SHA1(2958dfdbda14de4e6b9d6a8c3781131ab1e32bef))
	ROM_LOAD16_BYTE( "10217.hi", 0x10001, 0x8000, CRC(111725cf) SHA1(f6018a45bda4476d40c5881fb0a506ff75ec1688))
ROM_END

// Apricot Qi 300 (Rev D,E & F Motherboard) - no display
ROM_START( xb42663 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "qi310223.lo", 0x00000, 0x10000, CRC(53047f49) SHA1(7b38e533f7f27295269549c63e5477d950239167))
	ROM_LOAD16_BYTE( "qi310223.hi", 0x00001, 0x10000, CRC(4852869f) SHA1(98599d4691d40b3fac2936034c70b386ce4caf77))
ROM_END

// Apricot Qi 600 (Neptune Motherboard) - no display, beep code L-1-1-3 (Extended CMOS RAM failure)
ROM_START( qi600 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "qi610223.lo", 0x00000, 0x10000, CRC(563114a9) SHA1(62932b3bf0b5502ff708f604c21773f00afda58e))
	ROM_LOAD16_BYTE( "qi610223.hi", 0x00001, 0x10000, CRC(0ae133f6) SHA1(6039c366f7fe0ebf60b34c1a7d6b2d781b664001))
ROM_END

// Apricot Qi 900 (Scorpion Motherboard)  - no display, beep code L-1-1-3 (Extended CMOS RAM failure)
ROM_START( qi900 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "qi910224.lo", 0x00000, 0x10000, CRC(b012ad3c) SHA1(807e788a6bd03f5e983fe503af3d0b202c754b8a))
	ROM_LOAD16_BYTE( "qi910224.hi", 0x00001, 0x10000, CRC(36e66d56) SHA1(0900c5272ec3ced550f18fb08db59ab7f67a621e))
ROM_END

// Apricot FTs (Scorpion) - no display, beep code L-1-1-3 (Extended CMOS RAM failure)
ROM_START( ftsserv )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "fts10226.lo", 0x00000, 0x10000, CRC(efbd738f) SHA1(d5258760bafdaf1bf13c4a49da76d4b5e7b4ccbd))
	ROM_LOAD16_BYTE( "fts10226.hi", 0x00001, 0x10000, CRC(2460853f) SHA1(a6bba8d2f800140afd129c4d5278f7ae8fe7e63a))
	/* FT Server series Front Panel */
	ROM_REGION(0x10000,"front", 0)
	ROM_LOAD( "fp10009.bin",     0x0000, 0x8000, CRC(8aa7f718) SHA1(9ee6c6a5bb92622ea8d3805196d42ff68887d820))
ROM_END

// Apricot XEN-LS (Venus IV Motherboard) - no display
ROM_START( apxenls3 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "31020.lo", 0x10000, 0x8000, CRC(a19678d2) SHA1(d13c12fa7e94333555eabf58b81bad421e21cd91))
	ROM_LOAD16_BYTE( "31020.hi", 0x10001, 0x8000, CRC(4922e020) SHA1(64e6448323dad2209e004cd93fa181582e768ed5))
ROM_END

// Apricot LANstation (Krypton Motherboard) - no display
ROM_START( aplanst )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "31024", "BIOS 3-10-24")
	ROMX_LOAD( "31024.lo", 0x10000, 0x8000, CRC(e52b59e1) SHA1(cfcaa4d8d658df8df463108ef30695bd4ee7a617), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "31024.hi", 0x10001, 0x8000, CRC(7286aefa) SHA1(dfc0e3f4936780fa62ae9ec392ce17aa65e717cd), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "31025", "BIOS 3-10-25")
	ROMX_LOAD( "31025.lo", 0x10000, 0x8000, CRC(1aec09bc) SHA1(51d56c97c7c1674554aa89b68945329ea967a8bc), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "31025.hi", 0x10001, 0x8000, CRC(0763caa5) SHA1(48510a933dcd6efea3b14d04444f584c3e6fefeb), ROM_SKIP(1) | ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "31026", "BIOS 3-10-26i")
	ROMX_LOAD( "31026i.lo", 0x10000, 0x8000, CRC(670b6ab4) SHA1(8d61a0edf187f99b67eb58f5e11276deee801d17), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD( "31026i.hi", 0x10001, 0x8000, CRC(ef01c54f) SHA1(911f95d65ab96878e5e7ebccfc4b329db47a1351), ROM_SKIP(1) | ROM_BIOS(2))
ROM_END

// Apricot LANstation (Novell Remote Boot) - no display
ROM_START( aplannb )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "lsl31025.lo", 0x00000, 0x10000, CRC(8bb7229b) SHA1(31449d12884ec4e7752e6c1ce7ce9e0d044eadf2))
	ROM_LOAD16_BYTE( "lsh31025.hi", 0x00001, 0x10000, CRC(09e5c1b9) SHA1(d42be83b4181d3733268c29df04a4d2918370f4e))
ROM_END

// Apricot VX FT server - no display
ROM_START( apvxft )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "ft10221.lo", 0x00000, 0x10000, CRC(8f339de0) SHA1(a6542406746eaf1ff7f9e3678c5cbe5522fb314a))
	ROM_LOAD16_BYTE( "ft10221.hi", 0x00001, 0x10000, CRC(3b16bc31) SHA1(0592d1d81e7fd4715b0612083482db122d78c7f2))
ROM_END

// Apricot LS Pro (Caracal Motherboard,Chipset: VLSI VL82C483, ROM: 256KB Flash ROM, PCMCIA Type 2/3 slots)
ROM_START( aplscar )
	ROM_REGION32_LE(0x40000, "bios", 0)
	// 0: MAME exits with "Fatal error: i386: Called modrm_to_EA with modrm value C8!"
	ROM_SYSTEM_BIOS(0, "car306", "Caracal 3.06")
	ROMX_LOAD( "car306.bin",   0x00000, 0x40000, CRC(fc271dea) SHA1(6207cfd312c9957243b8157c90a952404e43b237), ROM_BIOS(0))
	// 1: no display
	ROM_SYSTEM_BIOS(1, "car307", "Caracal 3.07")
	ROMX_LOAD( "car307.bin",   0x00000, 0x40000, CRC(66a01852) SHA1(b0a68c9d67921d27ba483a1c50463406c08d3085), ROM_BIOS(1))
ROM_END

// Apricot XEN PC (A1 Motherboard) - no display
ROM_START( apxena1 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "a1-r26.bin",   0x00000, 0x20000, CRC(d29e983e) SHA1(5977df7f8d7ac2a154aa043bb6f539d96d51fcad))
ROM_END

// Apricot XEN PC (P2 Motherboard, Chipset: M1429G/31, ROM: 128KB Flash ROM, on board: graphics Cirrus Logic GD5434 (via VL))
ROM_START( apxenp2 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: ACR97E00-M00-951005-R02-G2 / BIOS V2.0 - Keyboard Interface Error - Pointing DeviceInterface Error
	// after a while the boot continues to the message "Password Violated, System Halted !"
	ROM_SYSTEM_BIOS(0, "p2r02g2", "p2r02g2")
	ROMX_LOAD( "p2r02g2.bin",   0x00000, 0x20000, CRC(311bcc5a) SHA1(be6fa144322077dcf66b065e7f4e61aab8c278b4), ROM_BIOS(0))
	// 1: BIOS-String: ACR97E00-M00-951005-R01-F0 / BIOS V2.0 (error messages as above)
	ROM_SYSTEM_BIOS(1, "lep121s", "SCSI-Enabling ROMs")
	ROMX_LOAD("p2r01f0.bin",   0x00000, 0x20000, CRC(bbc68f2e) SHA1(6954a52a7dda5521794151aff7a04225e9c7df77), ROM_BIOS(1))
ROM_END

// Apricot XEN-i 386 (Leopard Motherboard)
ROM_START( apxeni )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: Phoenix 80386 ROM BIOS PLUS Version 1.10.01 / XEN-i 386 Business Microcomputer / VR 1.2.1 22nd July 1988
	ROM_SYSTEM_BIOS(0, "lep121", "ROM BIOS 1.2.1")
	ROMX_LOAD( "lep121.bin", 0x18000, 0x8000, CRC(948c1927) SHA1(d06bdbd6292db73c815ad1060daf055293dfddf5), ROM_BIOS(0))
	// 1: Phoenix 80386 ROM BIOS PLUS Version 1.10.01 / XEN-i 386 Business Microcomputer / VR 1.2.1 22nd January 1991
	ROM_SYSTEM_BIOS(1, "lep121s", "SCSI-Enabling ROMs")
	ROMX_LOAD( "lep121s.bin", 0x18000, 0x8000, CRC(296118e4) SHA1(d1feaa9704e6ce3bc10c900bdd310d9494b02304), ROM_BIOS(1))
ROM_END

// Apricot LS Pro (Bonsai Motherboard, on board: ethernet (Intel 82596), Chipset: VLSI SCAMP VL82C311 / VL82C333, ROM: 128KB)
ROM_START( aplsbon )
	ROM_REGION32_LE(0x20000 ,"bios", 0)
	// 0: BIOS-String: Phoenix BIOS A486 Version 1.01 / LS Pro BIOS Version 1.06, 4th July 1994 - Pointer device failure
	ROM_SYSTEM_BIOS(0, "bon106", "Bonsai 1-06")
	ROMX_LOAD( "bon106.bin",   0x00000, 0x20000, CRC(98a4eb76) SHA1(e0587afa78aeb9a8803f9b9f9e457e9847b0a2b2), ROM_BIOS(0))
	// 1: flashing screen
	ROM_SYSTEM_BIOS(1, "bon203", "Bonsai 2-03")
	ROMX_LOAD( "bon203.bin",   0x00000, 0x20000, CRC(32a0e125) SHA1(a4fcbd76952599993fa8b76aa36a96386648abb2), ROM_BIOS(1))
	// 2: BIOS-String: Phoenix BIOS A486 Version 1.01 / LS Pro BIOS Version 1.07.03, 2nd February 1995
	ROM_SYSTEM_BIOS(2, "bon10703", "Bonsai 1-07-03")
	ROMX_LOAD( "bon10703.bin",   0x00000, 0x20000, CRC(0275b3c2) SHA1(55ef4cbb7f3166f678aaa478234a42049deaba5f), ROM_BIOS(2))
	// 3: flashing screen
	ROM_SYSTEM_BIOS(3, "bon20402", "Bonsai 2.03")
	ROMX_LOAD( "bon20402.bin",   0x00000, 0x20000, CRC(ac5803fb) SHA1(b8fe92711c6a38a5d9e6497e76a0929c1685c631), ROM_BIOS(3))
ROM_END

// Apricot XEN-LS II (Samurai Motherboard, on board: CD-ROM, graphics, ethernet (Intel 82596), Chipset: VLSI 82C425, VLSI 82C486)
ROM_START( apxlsam ) // no display
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "sam107", "ROM BIOS Version 1-07")
	ROMX_LOAD( "sam1-07.bin",   0x00000, 0x20000, CRC(65e05a8e) SHA1(c3cd198a129122cb05a28798e54331b06cfdd310), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "sam206", "ROM BIOS Version 2-06")
	ROMX_LOAD( "sam2-06.bin",   0x00000, 0x20000, CRC(9768bb0f) SHA1(8166b77b133072f72f23debf85984eb19578ffc1), ROM_BIOS(1))
ROM_END

// Apricot FTs (Panther Rev F 1.02.26)
ROM_START( aprpand ) // no display
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "pf10226.std", 0x00000, 0x10000, CRC(7396fb87) SHA1(a109cbad2179eec55f86c0297a59bb015461da21))
	ROM_CONTINUE( 0x00001, 0x10000 )
ROM_END

// Apricot FT//ex 486 (J3 Motherboard, Chipset: Opti 82C696)
ROM_START( aprfte ) // no display
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "1-2r2-4.486", 0x00000, 0x20000, CRC(bccc236d) SHA1(0765299363e68cf65710a688c360a087856ece8f))
ROM_END


//**************************************************************************
//  Amstrad systems
//**************************************************************************

// Amstrad MegaPC Plus (Winbond chipset)
ROM_START( megapcpla )
	ROM_REGION32_LE(0x40000, "bios", 0)
	ROM_LOAD( "megapc_bios.bin",  0x00000, 0x10000, CRC(b84938a2) SHA1(cecab72a96993db4f7c648c229b4211a8c53a380))
	ROM_CONTINUE(0x30000, 0x10000)
ROM_END

// Amstrad PC2386
ROM_START( pc2386 )
	ROM_REGION32_LE( 0x40000, "bios", 0 )
	ROM_LOAD( "c000.bin", 0x00000, 0x4000, CRC(33145bbf) SHA1(c49eaec19f656482e12c8bf282cd4ee5986d227d) )
	ROM_LOAD( "f000.bin", 0x30000, 0x10000, CRC(f54a063c) SHA1(ce70ec493053afab662f51199ef9c9304a209b8e) )
	ROM_FILL(0x3fff1, 1, 0x5b) // f000:e05b is the standard at reset vector jump address
	ROM_FILL(0x3fff2, 1, 0xe0) // why does this rom's point to nowhere sane?
	ROM_FILL(0x3fff3, 1, 0x00) // and why does the rest of the rom look okay?
	ROM_FILL(0x3fff4, 1, 0xf0)

	ROM_REGION( 0x1000, "keyboard", 0 ) // PC2286 / PC2386 102-key keyboard
	ROM_LOAD( "40211.ic801", 0x000, 0x1000, CRC(4440d981) SHA1(a76006a929f26c178e09908c66f28abc92e7744c) )
ROM_END


//**************************************************************************
//  Commodore systems
//**************************************************************************

// Commodore Laptop C286-LT - screen remains blank - CPU: AMD N80C286-12 - Chipset: OAK OTI054 (J9105), OTI055 (FOY 107), OTI053 (J9105), OTI051(J9107)
ROM_START( c286lt )
	ROM_REGION16_LE(0x20000, "bios", 0) // BIOS contains Cirrus Logic VGA firmware, rebadged Sanyo MBC-17NB
	ROM_SYSTEM_BIOS(0, "c286lt13", "C286-LT V1.3")
	ROMX_LOAD( "cbm-c286lt-bios-v1.3-390854-01-1200.bin", 0x00000, 0x20000, CRC(785e87d2) SHA1(e271500169955473d44102a60f051b5f6cfae589), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "c286v17-854", "C286-LT V1.7 390854")
	ROMX_LOAD( "cbm-c286lt-bios-v1.7-390854-04.bin", 0x00000, 0x20000, CRC(2f762ab1) SHA1(d6cb37f0dcb261df86c01d4e1eabe10a52b2070f), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "c286v17-940", "C286-LT V1.7 390940")
	ROMX_LOAD( "cbm-c286lt-bios-v1.7-390940-04.bin", 0x00000, 0x20000, CRC(22d45839) SHA1(bc7159440c52c1f69957da8fdfa76ac0a42ebd16), ROM_BIOS(2))
ROM_END

// Commodore SL 286-16 - this is the wider one, the "slimline model" has only two ISA slots on a riser, an online OTI VGA and a Headland chipset
ROM_START( csl286 ) // continuous short beeps after POST - Chipset is marked "Chips", one IC is P82C212B-12 (16MHz) - system has a WDC VGA card
	ROM_REGION16_LE(0x20000, "bios", 0) // one ISA slot with a riser providing five slots - is or is similar to a DTK PTM 1661c
	ROM_LOAD16_BYTE( "cbm-sl286-16-bios-lo-v1.02-390958-03.bin", 0x10000, 0x8000, CRC(7d0c9472) SHA1(1d614f6835a388f67ece73f40d8a9f65cca3e855))
	ROM_LOAD16_BYTE( "cbm-sl286-16-bios-hi-v1.02-390959-03.bin", 0x10001, 0x8000, CRC(b6d81ddd) SHA1(9478bb846bd1e0dc1904f21d43c6df01ecbc9c83))
ROM_END

// Commodore SL 386SX
ROM_START( c386sx16 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// actual VGA BIOS not dumped - uses a WD Paradise according to http://www.cbmhardware.de/pc/pc.php
	// complains "Time-of-day clock stopped"
	// photos of the system show one ISA16 slot for a riser card, an Acumos AVGA2 chip, a VLSI 82C311 IC, one other VLSI and an Acer chip.
	ROM_SYSTEM_BIOS(0, "c386sxv100", "SL 386SX V1.00") // Commodore 80386SX BIOS Rev. 1.00 - 390914-01/390915-01 - continuous beeps after POST
	ROMX_LOAD( "cbm-sl386sx-bios-lo-v1.0-390914-01.bin", 0x10000, 0x8000, CRC(03e00583) SHA1(8be8478cabd9de3d547a08207ffdcd39bf1bcd94), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "cbm-sl386sx-bios-hi-v1.0-390915-01.bin", 0x10001, 0x8000, CRC(cbe31594) SHA1(d6ace0b5ae4a0f63d047c2918210188f4c77c0c0), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "c386sxv101", "SL 386SX V1.01") // Rev. 1.01 - 390914-02/390915-02 - continuous beeps after POST
	ROMX_LOAD( "cbm-sl386sx-bios-lo-v1.01-390914-02-2700.bin", 0x10000, 0x8000, CRC(711f1523) SHA1(5318127cd42e60dabd221ae8dd16812726a0e889), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "cbm-sl386sx-bios-hi-v1.01-390915-02-3b00.bin", 0x10001, 0x8000, CRC(a1390cbc) SHA1(12aef4b95581e8c4489036c75697f18e9f3727b5), ROM_SKIP(1) | ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "c386sxv102", "SL 386SX V1.02") // Rev. 1.02 - 390914-03/390914-03/390915-03
	ROMX_LOAD( "cbm-sl386sx-bios-lo-v1.02-390914-03-0300.bin", 0x10000, 0x8000, CRC(301eb832) SHA1(6c599792b254b6d98dc130040d4f7858fd504f15), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD( "cbm-sl386sx-bios-hi-v1.02-390915-03-3800.bin", 0x10001, 0x8000, CRC(01815d9d) SHA1(0af291626e71ed65ff6dfee2fe4776a29f2bbb97), ROM_SKIP(1) | ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "c386sxv103", "SL 386SX V1.03") // Commodore 80386SX BIOS Rev. 1.03 -
	// this was replaced with the consistently named ROMs from http://www.zimmers.net/cbmpics/cpcs3.html, the 'hi' ROM looks like a bad dump, with its alternative the POST comes up
	// ROMX_LOAD( "390914-01.u39", 0x10000, 0x8000, CRC(8f849198) SHA1(550b04bac0d0807d6e95ec25391a81272779b41b), ROM_SKIP(1) | ROM_BIOS(3)) /* 390914-01 V1.03 CS-2100 U39 Copyright (C) 1990 CBM */
	// ROMX_LOAD( "390915-01.u38", 0x10001, 0x8000, CRC(ee4bad92) SHA1(6e02ef97a7ce336485814c06a1693bc099ce5cfb), ROM_SKIP(1) | ROM_BIOS(3)) /* 390915-01 V1.03 CS-2100 U38 Copyright (C) 1990 CBM */
	ROMX_LOAD( "cbm-sl386sx-bios-lo-v1.03-390914-03.bin", 0x10000, 0x8000, CRC(8f849198) SHA1(550b04bac0d0807d6e95ec25391a81272779b41b), ROM_SKIP(1) | ROM_BIOS(3))
	ROMX_LOAD( "cbm-sl386sx-bios-hi-v1.03-390915-03.bin", 0x10001, 0x8000, CRC(ebdd5097) SHA1(2e4d2375efb9c1ebc0ccf3bb1ff2bb64c449af32), ROM_SKIP(1) | ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "c386sxv104", "SL 386SX V1.04") // Rev. 1.04 - 390914-04/390915-04
	ROMX_LOAD( "cbm-sl386sx-bios-lo-v1.04-390914-04.bin", 0x10000, 0x8000, CRC(377a8e1c) SHA1(9a36f10ad496e44f190937426f3e7de368d6ab7b), ROM_SKIP(1) | ROM_BIOS(4))
	ROMX_LOAD( "cbm-sl386sx-bios-hi-v1.04-390915-04.bin", 0x10001, 0x8000, CRC(4149f5d9) SHA1(9a62b235ac45145ca6720d11b2cbc17b8c25704a), ROM_SKIP(1) | ROM_BIOS(4))
ROM_END

// Commodore 386SX-25 - Form factor: slimline desktop - Chipset: : VLSI 82C311, unreadable, Acer - CPU: i386sx-25, FPU socket provided - On board: Keyboard (DIN), mouse (mini DIN), par,
// 2xser, beeper, Floppy, IDE - VGA on board: acumos AVGA2-340-1 - Mass storage: 3.5" FDD, 80MB IDE HDD - RAM: 8xSIMM30 - OSC: 24.000MHz, 50.000MHz, unreadable - ISA16: 1 (for riser card)
ROM_START( c386sx25 ) // BIOS-String: Award Modular BIOS v4.20 (80386DX) Commodore 386sx-25 BIOS-Version 1.09 391443-09
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "c386sx25_f000.rom", 0x10000, 0x10000, CRC(1322064d) SHA1(6ce16a956b8454d3746ccb923fac45924e4302a4))

	ROM_REGION( 0x8000, "vga", 0)
	ROM_LOAD( "c386sx25_c000.rom", 0x0000, 0x8000, CRC(92212c6b) SHA1(61d222872bf4e21de053705c267103e409adb0ab))
ROM_END

// Commodore Laptop C386SX-LT -  screen remains blank
ROM_START( c386sxlt )
	ROM_REGION16_LE(0x20000, "bios", 0) // BIOS contains Cirrus Logic VGA firmware, rebadged Sanyo MBC-18NB, but different versions exist
	ROM_SYSTEM_BIOS(0, "c386sxlt_b400", "C386SX-LT V1.2 B400")
	ROMX_LOAD( "cbm-386lt-bios-v1.2-390981-03-b400.bin", 0x00000, 0x20000, CRC(b84f6883) SHA1(3f31060726c7c49a891b35ab024524a4239eb4d0), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "c386sxlt_cf00", "C386SX-LT V1.2 CF00")
	ROMX_LOAD( "cbm-386lt-bios-v1.2-390982-03-cf00.bin", 0x00000, 0x20000, CRC(c8cd2641) SHA1(18e55bff494c42389dfb445f2bc11e78db30e5f7), ROM_BIOS(1))
ROM_END

// Commodore DT386
ROM_START( dt386 )
	// BIOS-String: 40-0502-DG1112-00101111-070791-SOLUTION-0 / 386DX-33 BIOS V1.00 #391560-0
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "dt386vp10", "DT386 V.pre 1.0")
	ROMX_LOAD( "cbm-dt386dx-33c-bios-hi-vpre1.0-391560-01.bin", 0x10000, 0x10000, CRC(600472f4) SHA1(2513c8bdb24fe27f73c82cbca9e1a983e4a0ba10), ROM_BIOS(0))
	// BIOS-String: 40-0500-DG112-00101111-070791-SOLUTION-0 / 386DX-33 Rev.1E (091592)
	ROM_SYSTEM_BIOS(1, "dt386v1e", "DT386 V.1e")
	ROMX_LOAD( "cbm-dt386dx-33c-bios-hi-v-xxxxxx-xx.bin", 0x10000, 0x10000, CRC(dc1ca1b5) SHA1(7441cb9d5ad5ca6e6425de73295eb74d1281929f), ROM_BIOS(1))
	// BIOS-String: 40-0500-DG1112-00101111-070791-SOLUTION-0 / 386DX-33 Rev.1E (091592) / Commodore BIOS Version 1.0 391560-01
	ROM_SYSTEM_BIOS(2, "dt386v10", "DT386 V.1.0")
	ROMX_LOAD( "cbm-dt386dx-33c-bios-hi-v1.00-391560-01.bin", 0x10000, 0x10000, CRC(da1f7e6d) SHA1(b825fc015233e7eef93a3abbdfc3eeb0da096f50), ROM_BIOS(2))
	// BIOS-String: 40-0501-DG1112-00101111-070791-SOLUTION-0 / Commodore 386DX-33 BIOS Rev. 1.01 391560-02
	ROM_SYSTEM_BIOS(3, "dt386v101", "DT386 V.1.01")
	ROMX_LOAD( "cbm-dt386dx-33c-bios-hi-v1.01-391560-02.bin", 0x10000, 0x10000, CRC(b3157f57) SHA1(a1a96c8d111e3c1da8f655b4b7e1c5be4af140e9), ROM_BIOS(3))
ROM_END

// Commodore DT486 - BIOS contains Paradise VGA ROM - Keyboard error
ROM_START( dt486 ) // BIOS string: 41-0102-001283-00111111-060692-SYM_486-0 - Commodore 486DX-33 BIOS Version 1.01 391521-02
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "dt486", "DT486")
	ROM_LOAD( "cbm-dt486dx-33c-bios-u32--v1.01-391521-02.bin", 0x00000, 0x20000, BAD_DUMP CRC(a3977625) SHA1(83bc563fb41eae3dd5d260f13c6fe8979a77e99c))
ROM_END

// Commodore PC 30-III
ROM_START( pc30iii ) // Chipset: MOS 5720 1788 41, Faraday FE3010B, FE3020, FE3000A, FE3030 - ISA8: 1, ISA16: 3
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "pc30iii_v200", "PC 30-III v2.00")
	ROMX_LOAD( "pc30iii_390339-02_3e58.bin", 0x18000, 0x4000, CRC(f4a5860e) SHA1(b843744fe928bcfd8e037b0208cc85c0746535cf),ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "pc30iii_390340-02_42a8.bin",  0x18001, 0x4000, CRC(934df54a) SHA1(3b1c8916ba2b2517bc9f26dd74254586bcf0e91d),ROM_SKIP(1) | ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "pc30iii_v201", "PC 30-III v2.01")
	ROMX_LOAD( "cbm-pc30c-bios-lo-v2.01-390339-03-35c1.bin", 0x18000, 0x4000, CRC(36307aa9) SHA1(50237ffea703b867de426ab9ebc2af46bac1d0e1),ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "cbm-pc30c-bios-hi-v2.01-390340-03-3f3f.bin",  0x18001, 0x4000, CRC(41bae42d) SHA1(27d6ad9554be86359d44331f25591e3122a31519),ROM_SKIP(1) | ROM_BIOS(1) )
ROM_END

// Commodore PC 40-III
// Chipset: MOS 5720 3888 41, Faraday FE3010B, FE3000A, FE3030, FE3020 - onboard VGA is a Paradise PVGA1A - ISA8: 1, ISA16: 3
ROM_START( pc40iii )
	// VGA BIOS
	// ROM_LOAD( "pc40iii_390337-01_v2.0_f930.bin", 0x00000, 0x4000, CRC(82b210d3) SHA1(1380107deef02455c6ce4d12162fdc32e375cbde))
	// ROM_LOAD( "pc40iii_390338-01_v2.0_b6d0.bin", 0x00001, 0x4000, CRC(526d7424) SHA1(60511ca0e856b7611d556aa82219d646f96c9b94))

	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "pc40iii_v200", "PC 40-III v2.00")
	ROMX_LOAD( "pc40iii_390339-01_v2.0_473a.bin", 0x18000, 0x4000, CRC(2ad2dc0f) SHA1(b41d5988fda8cc23418c3f665d780c617aa3fc2b),ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "pc40iii_390340-01_v2.0_4bc6.bin",  0x18001, 0x4000, CRC(62dc7d93) SHA1(e741528697b1d00450fd18e3db8b925606e0bd22),ROM_SKIP(1) | ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "pc40iii_v201", "PC 40-III v2.03")
	ROMX_LOAD( "cbm-pc40c-bios-lo-v2.03-390339-04-03bc.bin", 0x18000, 0x4000, CRC(e5fd11c6) SHA1(18c21d9a4ae687eef5464b76a0d614b9dfd30ec8),ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "cbm-pc40c-bios-hi-v2.03-390340-04-3344.bin",  0x18001, 0x4000, CRC(63d6f0f7) SHA1(a88dee7694baa71913acbe76cb4e2a4e95979ad9),ROM_SKIP(1) | ROM_BIOS(1) )
ROM_END

// Commodore PC 45-III - this is a PC 40-III with a BIOS update and a bigger, 52MB, harddisk
ROM_START( pc45iii )
	ROM_REGION16_LE(0x20000, "bios", 0) // Commodore 286 BIOS Rev. 2.04 - 390339-05/390340-05
	ROM_LOAD16_BYTE( "cbm-pc45c-bios-lo-v2.04-390339-05.bin", 0x18000, 0x4000, CRC(b87b4cd1) SHA1(a6723d63a255b4010ad32b5dc9797e4724a64c14))
	ROM_LOAD16_BYTE( "cbm-pc45c-bios-hi-v2.04-390340-05.bin", 0x18001, 0x4000, CRC(b6976111) SHA1(e7c92307db3969a6a50ffd8cbc3d2ed16b4df6ad))
ROM_END

// Commodore PC 50-II - a photo of the mainboard shows four ROMs (two each for BIOS and VGA), so the 128K dumps available were probably made from a running system.
ROM_START( pc50ii ) // Chipset: Chips P82C211-12 C(16MHz), P82C212B-12 (16MHz), P82C215-12, P82C206, VLSI 8942VT - ISA8: 1, ISA16: 5
	ROM_REGION16_LE(0x20000, "bios", 0) // keyboard MCU is P8042AH MITAC V2.48 (undumped), onboard video PVGA1A-JK
	// 0: Commodore PC50-II BIOS Rev1.0 - 609200-03
	ROM_SYSTEM_BIOS(0, "pc50iiv100", "PC 50-II V1.00") // complains "Time-of-day clock stopped" and reboots
	ROMX_LOAD( "cbm-pc50b-bios-lo-v1.00-390339-01.bin", 0x10001, 0x8000, CRC(0f0e2fd6) SHA1(61a8043ac919c2a8fe668bf25e5f0b67868d11ae),ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "cbm-pc50b-bios-hi-v1.00-390340-01.bin", 0x10000, 0x8000, CRC(87008421) SHA1(cf41973a7bd439441baec1138dd63044fafe7391),ROM_SKIP(1) | ROM_BIOS(0) )
	// 1: Commodore PC50-II BIOS Rev1.01 - 609200-03
	ROM_SYSTEM_BIOS(1, "pc50iiv101", "PC 50-II V1.01") // same behaviour as above
	ROMX_LOAD( "cbm-pc50b-bios-lo-u31-v1.01-xxxxxx-xx-a800.bin", 0x10001, 0x8000, CRC(bf2c7009) SHA1(6b94df37861b30ef6a39a4ed64d4c9ac1e96043a),ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "cbm-pc50b-bios-hi-u28-v1.01-xxxxxx-xx-cd00.bin",  0x10000, 0x8000, CRC(628fcb2f) SHA1(74241cbcb4e183015d5e7a516d46b08d6f47504a),ROM_SKIP(1) | ROM_BIOS(1) )
	// 2: Commodore PC50-II BIOS Rev1.02 - 609200-03
	ROM_SYSTEM_BIOS(2, "pc50iiv102", "PC 50-II V1.02") // same behaviour as above
	ROMX_LOAD( "cbm-pc50b-bios-lo-u32-v1.02-609200-03o-9e00.bin", 0x10001, 0x8000, CRC(57225c22) SHA1(3b2ded119480ce2dd5bb7c113c5814ce47e17d4c),ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD( "cbm-pc50b-bios-hi-u27-v1.02-609200-03e-c400.bin", 0x10000, 0x8000, CRC(4ec903af) SHA1(fb70e22c0538d7310c9034626d4d9c0e4f63dfd7),ROM_SKIP(1) | ROM_BIOS(2))

	// VGA BIOS
	// ROM_LOAD( "m_pc50-ii_1bad_pvgadk_odd.bin", 0x00000, 0x8000, CRC(f36eca7e) SHA1(4335fa4a4567cbc010ff2ffeb97a536ed93b0219))
	// ROM_LOAD( "m_pc50-ii_54e3_pvgadk_even.bin", 0x00001, 0x8000, CRC(01f6b964) SHA1(799a84ddde8a7672a6df9439bad6198ec3ff98ec))
ROM_END

// Commodore PC-60-III - complaining "time-of-day-clock stopped" - Phoenix P8242 '87 keyboard BIOS
ROM_START( pc60iii ) // onboard Paradise PVGA1A-JK, 2xRS232, 1xparallel, keyboard
	ROM_REGION32_LE(0x20000, "bios", 0) // Chipset: Chips P82C301C, P82B305, P82A303, P82A304, Chips F82307, 25531/390423-1 COMBO SMC B9016, Commodore CSG, four MOSEL MS82C308-35JC, ISA16: 7, RAM card: 2 (up to 8MB, extended ISA connector)
	// 0: Commodore PC60-III 80386 BIOS Rev. 1.2 - 390473-01/390474-01
	ROM_SYSTEM_BIOS(0, "pc60iiiv12", "PC60-III V1.2")
	ROMX_LOAD( "cbm-pc60c-bios-lo_u73-v1.2-390473-01.bin", 0x00000, 0x10000, CRC(ff2cd8b3) SHA1(62e95f818c5016f4be2741872dc644999dee33ce),ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "cbm-pc60c-bios-hi_u67-v1.2-390474-01.bin", 0x00001, 0x10000, CRC(690fff4b) SHA1(adc262d40da64354c7c76b61f46d2c7ed35e9df9),ROM_SKIP(1) | ROM_BIOS(0) )
	// 1: Commodore PC-60-III 80386/25MHz BIOS Rev. 1.3 390473-02/390474-02
	ROM_SYSTEM_BIOS(1, "pc60iiiv13", "PC60-III V1.3")
	ROMX_LOAD( "cbm-pc60c-bios-lo-v1.30-390473-02.bin", 0x00000, 0x10000, CRC(3edd83e0) SHA1(3ebf393d6c33d9b8600f56c7be9eedb5aefb2645),ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "cbm-pc60c-bios-hi-v1.30-390474-02.bin", 0x00001, 0x10000, CRC(12209ac4) SHA1(76f271944894c77dde735da2b2ba065e81a99564),ROM_SKIP(1) | ROM_BIOS(1) )
	// 2: Commodore PC60-III 80386/25MHz BIOS rev.1.33 390473-04/390474-04
	ROM_SYSTEM_BIOS(2, "pc60iiiv133", "PC60-III V1.33")
	ROMX_LOAD( "cbm-pc60-bios-lo-v1.33-390473-04.bin", 0x00000, 0x10000, CRC(afd0aae0) SHA1(7fa4388c939f30e603f0fc90f9512e500b282432),ROM_SKIP(1) | ROM_BIOS(2) )
	ROMX_LOAD( "cbm-pc60-bios-hi-v1.33-390474-04.bin", 0x00001, 0x10000, CRC(7b7958db) SHA1(d542c63ec0d17e1e87403ac01735e75ce58302a9),ROM_SKIP(1) | ROM_BIOS(2) )
	// 3: Commodore PC60-III 80386-25MHz BIOS Rev.1.3.5 - 390473-06/390474-06
	ROM_SYSTEM_BIOS(3, "pc60iiiv135", "PC60-III V1.3.5")
	ROMX_LOAD( "cbm-pc60c-bios-lo-v1.35-390473-06.bin", 0x00000, 0x10000, CRC(6ff4aea9) SHA1(3fcb3a5c275dbfb93c3e55224d731f1b52343d4b),ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "cbm-pc60c-bios-hi-v1.35-390474-06.bin", 0x00001, 0x10000, CRC(5a04e3f0) SHA1(311a3ff3e578ecbce0ecd9f3b006ab772623255a),ROM_SKIP(1) | ROM_BIOS(3) )
	// 4: Commodore 80386 BIOS Rev.1.36 - 390473-07/390474-07
	ROM_SYSTEM_BIOS(4, "c386v136", "Commodore 386 V1.3.6")
	ROMX_LOAD( "cbm-pc60c-bios-lo-v1.36-390473-07-9b0e.bin", 0x00000, 0x10000, CRC(be7504f8) SHA1(a45f7690a41d416bc10ca6f583b8fdd2219a3d8a),ROM_SKIP(1) | ROM_BIOS(4) )
	ROMX_LOAD( "cbm-pc60c-bios-hi-v1.36-390474-07-ddf2.bin", 0x00001, 0x10000, CRC(d8e08ffa) SHA1(fb5fb973b01df6e486d76076d3373583758b1d01),ROM_SKIP(1) | ROM_BIOS(4) )
	// 5: Commodore 80386 BIOS Rev.1.36.03 - 390473-07/390474-07
	ROM_SYSTEM_BIOS(5, "c386v13603", "Commodore 386 V1.3.603")
	ROMX_LOAD( "cbm-pc60c-bios-lo-v1.3603-390473-07.bin", 0x00000, 0x10000, CRC(2cda07c7) SHA1(01fd6260192541dd73f88d2cc0f99fe5603efc81),ROM_SKIP(1) | ROM_BIOS(5) )
	ROMX_LOAD( "cbm-pc60c-bios-hi-v1.3603-390474-07.bin", 0x00001, 0x10000, CRC(39845b9b) SHA1(9d3cbfde4b2acc1d576aafa80126b75a49d3d8df),ROM_SKIP(1) | ROM_BIOS(5) )
ROM_END

// Commodore PC-70-III - complaining "time-of-day-clock stopped"
ROM_START( pc70iii )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: Commodore 80486 BIOS Rev.1.00 - 390934-01/390935-01
	ROM_SYSTEM_BIOS(0, "pc70v100", "PC70 V1.00")
	ROMX_LOAD("cbm-pc70c_bios-u117-lo-v1.00-390934-01.bin", 0x00000, 0x10000, CRC(3eafd811) SHA1(4deecd5dc429ab09e7c0d308250cb716f8b8e42a), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("cbm-pc70c_bios-u112-hi-v1.00-390935-01.bin", 0x00001, 0x10000, CRC(2d1dfec9) SHA1(d799b3579577108549d9d4138a8a32c35ac3ce1c), ROM_SKIP(1) | ROM_BIOS(0))
	// 1: Commodore PC70-III 80486/25MHz BIOS Rev.1.00.01 - xxxxxx - 00/xxxxxx-00
	ROM_SYSTEM_BIOS(1, "pc70v101", "PC70 V1.00.01")
	ROMX_LOAD("cbm-pc70c-bios-lo-v1.00.01-xxxxxx-00.bin", 0x00000, 0x10000, CRC(6c8bbd31) SHA1(63d1739a58a0d441ebdd543e3994984c433aedb4), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("cbm-pc70c-bios-hi-v1.00.01-xxxxxx-00.bin", 0x00001, 0x10000, CRC(ef279cdd) SHA1(d250368b2f731e842d6f280a6134f1e38846874b), ROM_SKIP(1) | ROM_BIOS(1))
ROM_END

// Commodore Tower 386
ROM_START( comt386 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// Phoenix 80386 ROM BIOS PLUS Version 1.10 22 - Twinhead International Corporation
	ROM_LOAD16_BYTE( "cbm-t386-bios-lo-v1.1022c-.bin", 0x10000, 0x8000, CRC(6857777e) SHA1(e80dbffd3523c9a1b027f57138c55768fc8328a6))
	ROM_LOAD16_BYTE( "cbm-t386-bios-hi-v1.1022c-.bin", 0x10001, 0x8000, CRC(6a321a7e) SHA1(c350fb273522f742c6008deda00ed13947a269b7))
ROM_END

// Commodore Tower 486
ROM_START( comt486 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 40-0500-DG1112-00101111-070791-SOLUTION-0 - 4D3FF Rev.D (092892)
	ROM_SYSTEM_BIOS(0, "v0", "Tower 486 V0")
	ROMX_LOAD( "cbm-t486dx-bios-v-xxxxxx-xx.bin", 0x10000, 0x10000, CRC(f51c0ca0) SHA1(2b08a606ae2f37b3e72d687f890d729a58fd3ccd), ROM_BIOS(0))
	// continuous chirps
	ROM_SYSTEM_BIOS(1, "v1", "Tower 486 V1")
	ROMX_LOAD( "cbm-t486dx-66-bios-v1.01-391566-02.bin", 0x10000, 0x10000, CRC(3d740698) SHA1(888f23d85b41c07e15e2811b76194cf478bc80cd), ROM_BIOS(1))
	// BIOS-String: 40-0103-001283-00101111-0606-SYM_486-0 - Commodore 486DX2-66 BIOS Version 1.03 391684-02
	ROM_SYSTEM_BIOS(2, "v2", "Tower 486 V2")
	ROMX_LOAD( "cbm-t486dx-66-bios-v1.03-391684-02.bin", 0x10000, 0x10000, CRC(13e8b04b) SHA1(dc5c84d228f802f7580b3f3b8e70cf8f74de5d79), ROM_BIOS(2))
	// BIOS-String: 40-0103-001283-00101111-060692-SYM_486-0 - Commodore 486DX-50 BIOS Version 1.03 391522-03
	ROM_SYSTEM_BIOS(3, "v3", "Tower 486 V3")
	ROMX_LOAD( "cbm-t486dx-50-bios-v1.03-.bin", 0x10000, 0x10000, CRC(e02bb928) SHA1(6ea121b214403390d382ca4685cfabcbcca1a28b), ROM_BIOS(3))
ROM_END


//**************************************************************************
//  80286 BIOS
//**************************************************************************

ROM_START( at )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// 0: BIOS-String: D286-0011-110387
	ROM_SYSTEM_BIOS(0, "at", "PC 286") /*(Motherboard Manufacturer: Unknown.) (BIOS release date:: 03-11-1987)*/
	ROMX_LOAD( "at110387.1", 0x10001, 0x8000, CRC(679296a7) SHA1(ae891314cac614dfece686d8e1d74f4763cf40e3),ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "at110387.0", 0x10000, 0x8000, CRC(65ae1f97) SHA1(91a29c7deecf7a9afbba330e64e0eee9aafee4d1),ROM_SKIP(1) | ROM_BIOS(0) )
	// 1: BIOS-String: S286-6181-101590-K0 - additional info from chukaev.ru54.com: UMC chipset
	ROM_SYSTEM_BIOS(1, "ami206", "AMI C 206.1")  /*(Motherboard Manufacturer: Unknown.) (BIOS release date:: 15-10-1990)*/
	ROMX_LOAD( "amic206.bin",    0x10000, 0x10000,CRC(25a67c34) SHA1(91e9d8cdc2f1b40a601a23ceaff2189fd1245f3b), ROM_BIOS(1) )
	// 2: (BIOS release date:: 07-07-1991) - Chipset: Headland HT21/E
	ROM_SYSTEM_BIOS(2, "amiht21", "AMI HT 21.1") /* as above */
	ROMX_LOAD( "ht21e.bin",    0x10000, 0x10000, CRC(e80f7fed) SHA1(62d958d98c95e9e4d1b290a6c1054ae98770f276), ROM_BIOS(2) )
	// 3: BIOS-String: D286-1430-040990-K0 - additional info from chukaev.ru54.com: Chipset: TI TACT8230... 1BPB, 2BPB, 3EPB - ISA8: 3, ISA16: 5
	ROM_SYSTEM_BIOS(3, "amip1", "AMI P.1") /*(Motherboard Manufacturer: Unknown.) (BIOS release date:: 09-04-1990)*/
	ROMX_LOAD( "poisk-h.bin",   0x10001, 0x8000, CRC(83fd3f8c) SHA1(ca94850bbd949b97b11710629886b0ee69489a81),ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "poisk-l.bin",   0x10000, 0x8000, CRC(0b2ed291) SHA1(bb51a3f317cf4d429a6cfb44a46ca0ac39d9aaa7),ROM_SKIP(1) | ROM_BIOS(3) )
	// 4: Award 286 Modular BIOS Version 3.11 - WINBOND
	ROM_SYSTEM_BIOS(4, "aw201", "Award 201") /* (BIOS release date:: 21-11-1990) */
	ROMX_LOAD( "83201-5h.bin",  0x10001, 0x8000, CRC(968d1fc0) SHA1(dc4122a6c696f0b43e7894dc1b669346eed755d5),ROM_SKIP(1) | ROM_BIOS(4) )
	ROMX_LOAD( "83201-5l.bin",  0x10000, 0x8000, CRC(bf50a89a) SHA1(2349a1db6017a7fb0673e99d3680c8753407be8d),ROM_SKIP(1) | ROM_BIOS(4) )
	// 5: Award 286 Modular BIOS V3.03 NFS 11/10/87" - T.M.C
	ROM_SYSTEM_BIOS(5, "aw303", "Award 303 NFS") /* (BIOS release date:: 15-11-1985) */
	ROMX_LOAD( "aw303-hi.bin",  0x18001, 0x4000, CRC(78f32d7e) SHA1(1c88398fb171b33b7e6191bad63704ae85bfed8b), ROM_SKIP(1) | ROM_BIOS(5) )
	ROMX_LOAD( "aw303-lo.bin",  0x18000, 0x4000, CRC(3d2a70c0) SHA1(1329113bec514ed2a6d803067b1132744ef534dd), ROM_SKIP(1) | ROM_BIOS(5) )
	// 6: Award 286 Modular BIOS Version 3.03GS
	ROM_SYSTEM_BIOS(6, "aw303gs", "Award 303GS") /* (BIOS release date:: 15-11-1985) */
	ROMX_LOAD( "aw303gs-hi.bin",  0x18001, 0x4000, CRC(82392e18) SHA1(042453b7b29933a1b72301d21fcf8fa6b293c9c9), ROM_SKIP(1) | ROM_BIOS(6) )
	ROMX_LOAD( "aw303gs-lo.bin",  0x18000, 0x4000, CRC(a4cf8ba1) SHA1(b73e34be3b2754aaed1ac06471f4441fea06c67c), ROM_SKIP(1) | ROM_BIOS(6) )
	// 7: BIOS-String: D286-6069-040990-K0
	ROM_SYSTEM_BIOS(7, "ami_200960", "AMI 200960") /* (BIOS release date:: 09-04-1990 */
	ROMX_LOAD( "ami_286_bios_sn200960_even.bin", 0x10000, 0x8000, CRC(67745815) SHA1(ca6886c7a0716a92a8720fc71ff2d95328c467a5), ROM_SKIP(1) | ROM_BIOS(7) )
	ROMX_LOAD( "ami_286_bios_sn200960_odd.bin", 0x10001, 0x8000, CRC(360a5f73) SHA1(1b1980fd99779d0cdc4764928a641e081b35ee9f), ROM_SKIP(1) | ROM_BIOS(7) )
	// 8: BIOS-String: DVL2-1160-040990-K0
	ROM_SYSTEM_BIOS(8, "dvl2", "DVL2") /* (BIOS release date:: 09-04-1990) */
	ROMX_LOAD( "ami_dvl2-1160-040990-k8_even.bin", 0x10000, 0x8000, CRC(86093016) SHA1(f60b2679c8c23a34bdd64f25d83cb5a5a337bd57), ROM_SKIP(1) | ROM_BIOS(8) )
	ROMX_LOAD( "ami_dvl2-1160-040990-k8_odd.bin", 0x10001, 0x8000, CRC(4e1c944a) SHA1(0763a0a1002baced071fea301f627d2e550878b8), ROM_SKIP(1) | ROM_BIOS(8) )
	// 9: DTK 286 BIOS Ver 3.18 07/01/88
	ROM_SYSTEM_BIOS(9, "dtk318", "DTK v3.18") /* (BIOS release date:: 11-03-1986) */
	ROMX_LOAD( "dtk_286_bios_ver3.18.bin", 0x18000, 0x8000, CRC(b4b8b59a) SHA1(73c12222f5003fdc8bbfee178b20c8dda2fe5cb4), ROM_BIOS(9) )
	// 10: BIOS-String: D286-6061-040990-K0
	ROM_SYSTEM_BIOS(10, "d286-k0", "AMI D286-K0") /* (BIOS release date:: 09-04-1990) */
	ROMX_LOAD( "ami_d286-6061-040990-k0.bin", 0x10000, 0x10000, CRC(1679c1b5) SHA1(9d95da3b40c5f13d096823f383aba099b3a77183), ROM_BIOS(10) )
	// 11: BIOS-String: S286-1169-030389-K0 for ACHIEVE MICROSYSTEMS
	ROM_SYSTEM_BIOS(11, "s286-k0", "Achieve S286-K0") /* (BIOS release date:: 03-03-1989) */
	ROMX_LOAD( "ach_s286-1169-030389-k0_ev.bin", 0x10000, 0x8000, CRC(58f1f29c) SHA1(42f5189d12b75fad5e53ff472b4603c6fcbd46cd), ROM_SKIP(1) | ROM_BIOS(11) )
	ROMX_LOAD( "ach_s286-1169-030389-k0_od.bin", 0x10001, 0x8000, CRC(84bfc180) SHA1(2daa51b09c449712c9a737793b83754951e53a41), ROM_SKIP(1) | ROM_BIOS(11) )
	// 12: Award BIOS Version 3.01B
	ROM_SYSTEM_BIOS(12, "awa301b", "Award BIOS Version 3.01B") /* (BIOS release date:: 01-01-1988) */
	ROMX_LOAD( "aw286lo.rom", 0x18000, 0x4000, CRC(5afbb4a2) SHA1(513fd75d90720820484fdd280e4a6c22a0ef238c), ROM_SKIP(1) | ROM_BIOS(12) )
	ROMX_LOAD( "aw286hi.rom", 0x18001, 0x4000, CRC(b2551251) SHA1(0c8bd12a3d54ae6d2ad0210b9ca4deca94be10ed), ROM_SKIP(1) | ROM_BIOS(12) )
	// 13: no screen display
	ROM_SYSTEM_BIOS(13, "awa286", "awa286") /* (BIOS release date:: 21-11-1990) */
	ROMX_LOAD( "awd286lo.rom", 0x18000, 0x4000, CRC(d1a9c01f) SHA1(9123c6f76d85725036a0f8b9c6480142abea478f), ROM_SKIP(1) | ROM_BIOS(13) )
	ROMX_LOAD( "awd286hi.rom", 0x18001, 0x4000, CRC(b0bde4cc) SHA1(9c3fd2c0f69dde905d4e8f3be421374ef99682df), ROM_SKIP(1) | ROM_BIOS(13) )
	// 14: DTK 286 BIOS Ver. 3.01 07/24/87 - no screen display
	ROM_SYSTEM_BIOS(14, "dtk286", "dtk286") /* (BIOS release date:: 11-03-1986) */
	ROMX_LOAD( "dtk286lo.rom", 0x18000, 0x4000, CRC(dfc70856) SHA1(39158e6ed50236d371277631e77d06f77fb0531e), ROM_SKIP(1) | ROM_BIOS(14) )
	ROMX_LOAD( "dtk286hi.rom", 0x18001, 0x4000, CRC(a98fc743) SHA1(fb9e330148cb5584f61c1febea71c53b6f9d61b7), ROM_SKIP(1) | ROM_BIOS(14) )
	// 15: Phoenix 80286 ROM BIOS Version 3.07 (R04)
	ROM_SYSTEM_BIOS(15, "mitph307", "Mitac Phoenix v3.07") /* (BIOS release date:: 30-07-1987) */
	ROMX_LOAD( "mitac_phoenix_v3.07_even.bin", 0x10000, 0x8000, CRC(1c4becc9) SHA1(bfdea3f2a248312ed8cf4765a1a7dc1a2f7cecd8), ROM_SKIP(1) | ROM_BIOS(15) )
	ROMX_LOAD( "mitac_phoenix_v3.07_odd.bin", 0x10001, 0x8000, CRC(3ee16ed1) SHA1(b77e18e10e9187a01cb55c05b2a6e5311981ab56), ROM_SKIP(1) | ROM_BIOS(15) )
	// 16: BIOS-String: Pyramid Software Development Personal Computer AT BIOS Version 2.14
	ROM_SYSTEM_BIOS(16, "precise", "Precise")  /* (no regular BIOS release date) */
	ROMX_LOAD( "precise 860407_low.bin", 0x10000, 0x8000, CRC(d839c074) SHA1(473ca7b42914ce12f2d6c91afb0b2c2e65194489), ROM_SKIP(1) | ROM_BIOS(16) )
	ROMX_LOAD( "precise 860407_high.bin", 0x10001, 0x8000, CRC(b5e13c54) SHA1(07f5806fb53d0cb7ef7b54312fd6aa163d58b9a5), ROM_SKIP(1) | ROM_BIOS(16) )
	// ROM_LOAD( "precise_860407_keyboard_mcu.bin", 0x0000, 0x800, CRC(d1faad5c) SHA1(cb315a3da632c969012c298bb8e1cf8883b70501))
	// 17:  Access Methods Inc. for Flying Triumph (AMI before they became American Megatrends) - BIOS String: Ref. no. 1406-061296
	// complains about "Channel-2 timer not funcional but boots
	ROM_SYSTEM_BIOS(17, "ami_ft", "AMI Flying Triumph") /* (BIOS release date:: 12-06-1986) */
	ROMX_LOAD( "286_access_methods_rom2_32k.bin", 0x10000, 0x8000, CRC(749c65af) SHA1(7c6e9e217afe020b7b36785549fdbfb89de8f872), ROM_SKIP(1) | ROM_BIOS(17) )
	ROMX_LOAD( "286_access_methods_rom4_32k.bin", 0x10001, 0x8000, CRC(0f15581a) SHA1(2a22635f30388ca371f0f1f31652cfa647bb322d), ROM_SKIP(1) | ROM_BIOS(17) )
	// 18: MS-0010-2 - Phoenix ROM BIOS Version 3.06
	ROM_SYSTEM_BIOS(18, "ms-0010-2", "MS-0010-2") /* (BIOS release date:: 19-01-1987) (ISA8: 3, ISA16: 5) */
	ROMX_LOAD( "286-ms0010-2-lo_32k.bin", 0x10000, 0x8000, CRC(2c381474) SHA1(94b9825d412ea39d67857102a0375852b349fcd6), ROM_SKIP(1) | ROM_BIOS(18) )
	ROMX_LOAD( "286-ms0010-2-hi_32k.bin", 0x10001, 0x8000, CRC(4fdb8c64) SHA1(c2e7f88f0ac97ee5eed0c97864b7f1810e99ea26), ROM_SKIP(1) | ROM_BIOS(18) )
	// 19: M219 V2.1 - chipset: Toshiba CHIP2 TC6154AF
	// BIOS-String: X0-0100-001437-00101111-060692-M219-0
	ROM_SYSTEM_BIOS(19, "m219", "Toshiba M219")
	ROMX_LOAD( "3tcm001.bin", 0x10000, 0x10000, CRC(146a42e9) SHA1(cf511919f271e868e34881912c0a1a859d80f91e), ROM_BIOS(19))
	// ***** Motherboards using the original Chips CS8220 chipset: P82C202, P82C201, P82A203, P82A204, P82A205
	// 20: AL-6410 (found online, no markings on the board itself), Chipset: Chips P82A204, P82A203, P82A205, P82C201, P82C202
	ROM_SYSTEM_BIOS(20, "al6410", "AL-6410") /* (BIOS-String: D286-1103-110387-K0) (BIOS release date:: 03-11-1987) (ISA8: 2, ISA16: 6) */
	ROMX_LOAD( "al-6410_ami_bios_low.bin", 0x10000, 0x8000, CRC(50c4e121) SHA1(5f9c27aabdc6bb810e90bced2053b7c21c4994dd), ROM_SKIP(1) |  ROM_BIOS(20) )
	ROMX_LOAD( "al-6410_ami_bios_high.bin", 0x10001, 0x8000, CRC(a44be083) SHA1(99f73d7ceb315eb3770c94d90228f8859cadc610), ROM_SKIP(1) | ROM_BIOS(20) )
	// 21: AT SYSTEM 6M/8M/10M - Chipset: Chips P82A205; P82C201; P82A203; P82A204 - ISA8:2, ISA16: 6
	ROM_SYSTEM_BIOS(21, "at6m8m10m", "AT SYSTEM 6M/8M/10M") // (BIOS release date:: 04-02-1987) - OSC: 20.000000MHz - MQ-14.3 - 12.000
	ROMX_LOAD( "286-at system 6m8m10m-l_32k.bin", 0x10000, 0x8000, CRC(37e0e1c1) SHA1(f5cd17658554a73bb86c5c8e630dac3e34b38e51), ROM_SKIP(1) | ROM_BIOS(21) )
	ROMX_LOAD( "286-at system 6m8m10m-r_32k.bin", 0x10001, 0x8000, CRC(c672efff) SHA1(7224bb6b4d25ef34bc0aa9d7c450baf9b47fd917), ROM_SKIP(1) | ROM_BIOS(21) )
	// 22: CDTEK - BIOS-String: DSUN-1202-042088-K0 286-BIOS AMI for CDTEK - ISA8:2, ISA16:6 - Chipset ICs plus SN76LS612N, RTC MC146818P
	ROM_SYSTEM_BIOS(22, "cdtekchips", "CDTEK 286") // ISA8:2, ISA16: 6 - OSC: 12.000, 14.31818, 16000.00KHz
	ROMX_LOAD( "286-cdtek2-even_32k.bin", 0x10000, 0x8000, CRC(94867e8d) SHA1(12e61cc8b875b57324c93276c9f6093f2bd0e277), ROM_SKIP(1) | ROM_BIOS(22) )
	ROMX_LOAD( "286-cdtek2-odd_32k.bin", 0x10001, 0x8000, CRC(153ed3bd) SHA1(10b711e0f0d79e0b6d181f24fe66544d2d72a310), ROM_SKIP(1) | ROM_BIOS(22) )
	// 23: This board looks identical to #2 but has different chips fitted: SN76LS612N = Zymos HCT612, Chips P82A204 = TACT80204FN, P82A203 = STK-5134, P82A205 = STK-5135,
	// P82C201 = STK-5132, P82C202 = STK-5133 - BIOS-String: Phoenix 80286 ROM BIOS Version 3.06
	ROM_SYSTEM_BIOS(23, "286tact", "286 TACT") // OSC: 20.0000MHz, 14.31818 - 24.000MHz
	ROMX_LOAD( "286-tact-320548-1_32k.bin", 0x10000, 0x8000, CRC(0b528d19) SHA1(15f5a94d89461655c0f74681bbae5745db009ac2), ROM_SKIP(1) | ROM_BIOS(23) )
	ROMX_LOAD( "286-tact-320548-2_32k.bin", 0x10001, 0x8000, CRC(418aa2d0) SHA1(b6af0b8aa595d8f8de6c0fc851bf1c226dcc7ca7), ROM_SKIP(1) | ROM_BIOS(23) )
	// 24: Tulip 286 CPU card - Chipset: TACT 82204FN, 82202N, Chips P82A205, P82C201, P82A203, SN74LS612N, HD146818P, 2xAM9517A-5PC, 2xP8259A
	// CPU: AND N80L286-10, FPU socket provided - OSC: 16.000 - BIOS-String:
	ROM_SYSTEM_BIOS(24, "286tu", "Tulip 286 CPU card") // no display
	ROMX_LOAD( "tc7be.bin", 0x18000, 0x4000, CRC(260c6994) SHA1(a7e28c2978faaa9c5ccab32932ef1391c1b3d35a), ROM_SKIP(1) | ROM_BIOS(24) )
	ROMX_LOAD( "tc7bo.bin", 0x18001, 0x4000, CRC(c8373edc) SHA1(77ce220914863f482a3a983b43ff8ca8c72b470c), ROM_SKIP(1) | ROM_BIOS(24) )
ROM_END

ROM_START( atturbo )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 20-0001-001223-00101111-050591-KB-8042--0 - additional info from chukaev.ru54.com: Chipset: VLSI VL82C311L-FC4, VL82C113A-FC
	ROM_SYSTEM_BIOS(0, "vl82c", "VL82C311L-FC4")/*(Motherboard Manufacturer: Biostar Microtech Corp.) (BIOS release date: 05-05-1991)*/
	ROMX_LOAD( "2vlm001.bin",     0x10000, 0x10000, CRC(f34d800a) SHA1(638aca592a0e525f957beb525e95ca666a994ee8), ROM_BIOS(0) )
	// 1: same as BIOS '1' in at
	ROM_SYSTEM_BIOS(1, "ami206", "AMI C 206.1") /*(Motherboard Manufacturer: Unknown.) (BIOS release date:: 15-10-1990)*/
	ROMX_LOAD( "amic206.bin",    0x10000, 0x10000,CRC(25a67c34) SHA1(91e9d8cdc2f1b40a601a23ceaff2189fd1245f3b), ROM_BIOS(1) )
	// 2: same as BIOS '7' in at
	ROM_SYSTEM_BIOS(2, "amiht21", "AMI HT 21.1") /* not a bad dump, sets unknown probably chipset related registers at 0x1e8 before failing post */
	ROMX_LOAD( "ht21e.bin",    0x10000, 0x10000, CRC(e80f7fed) SHA1(62d958d98c95e9e4d1b290a6c1054ae98770f276), ROM_BIOS(2) )
	// 3: same as BIOS '8' in at
	ROM_SYSTEM_BIOS(3, "amip1", "AMI P.1") /*(Motherboard Manufacturer: Unknown.) (BIOS release date:: 09-04-1990)*/
	ROMX_LOAD( "poisk-h.bin",   0x10001, 0x8000, CRC(83fd3f8c) SHA1(ca94850bbd949b97b11710629886b0ee69489a81),ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "poisk-l.bin",   0x10000, 0x8000, CRC(0b2ed291) SHA1(bb51a3f317cf4d429a6cfb44a46ca0ac39d9aaa7),ROM_SKIP(1) | ROM_BIOS(3) )
	// 4: BIOS-String: DG22-1131-040990-K0 / 286-BIOS G2 V1.1 6-28-90 - Headland GC102/GC113-PC/HT101A - CPU/FPU: N80286-12, IIT2C87-10
	ROM_SYSTEM_BIOS(4, "ami1131", "AMI-1131") /*(Motherboard Manufacturer: Elitegroup Computer Co., Ltd.) (BIOS release date:: 09-04-1990)*/
	ROMX_LOAD( "2hlm003h.bin",   0x10001, 0x8000, CRC(2babb42b) SHA1(3da6538f44b434cdec0cbdddd392ccfd34666f06),ROM_SKIP(1) | ROM_BIOS(4) )
	ROMX_LOAD( "2hlm003l.bin",   0x10000, 0x8000, CRC(317cbcbf) SHA1(1adad6280d8b07c2921fc5fc13ecaa10e6bfebdc),ROM_SKIP(1) | ROM_BIOS(4) )
	// 5: same as BIOS '0' in at
	ROM_SYSTEM_BIOS(5, "at", "PC 286") /*(Motherboard Manufacturer: Unknown.) (BIOS release date:: 03-11-1987)*/
	ROMX_LOAD( "at110387.1", 0x10001, 0x8000, CRC(679296a7) SHA1(ae891314cac614dfece686d8e1d74f4763cf40e3),ROM_SKIP(1) | ROM_BIOS(5) )
	ROMX_LOAD( "at110387.0", 0x10000, 0x8000, CRC(65ae1f97) SHA1(91a29c7deecf7a9afbba330e64e0eee9aafee4d1),ROM_SKIP(1) | ROM_BIOS(5) )
	// 6
	ROM_SYSTEM_BIOS(6, "bravo", "AST Bravo/286") // fails with keyboard controller test, probably expects specific kbdc rom
	ROMX_LOAD( "107000-704.bin", 0x10000, 0x8000, CRC(94faf87e) SHA1(abaafa6c2ae9b9fba95b244dcbcc1c752ac6c0a0),ROM_SKIP(1) | ROM_BIOS(6) )
	ROMX_LOAD( "107000-705.bin", 0x10001, 0x8000, CRC(e1263c1e) SHA1(b564f1043ef45ecbdf4f06bb500150ad992c2931),ROM_SKIP(1) | ROM_BIOS(6) )
	// ***** Motherboards using the original Chips CS8220 chipset: P82C202, P82C201, P82A203, P82A204, P82A205
	// 7 same as BIOS '20' in at
	ROM_SYSTEM_BIOS(7, "al6410", "AL-6410")
	ROMX_LOAD( "al-6410_ami_bios_low.bin", 0x10000, 0x8000, CRC(50c4e121) SHA1(5f9c27aabdc6bb810e90bced2053b7c21c4994dd), ROM_SKIP(1) |  ROM_BIOS(7) )
	ROMX_LOAD( "al-6410_ami_bios_high.bin", 0x10001, 0x8000, CRC(a44be083) SHA1(99f73d7ceb315eb3770c94d90228f8859cadc610), ROM_SKIP(1) | ROM_BIOS(7) )
	// 8: same as BIOS '21' in at
	ROM_SYSTEM_BIOS(8, "at6m8m10m", "AT SYSTEM 6M/8M/10M")
	ROMX_LOAD( "286-at system 6m8m10m-l_32k.bin", 0x10000, 0x8000, CRC(37e0e1c1) SHA1(f5cd17658554a73bb86c5c8e630dac3e34b38e51), ROM_SKIP(1) | ROM_BIOS(8) )
	ROMX_LOAD( "286-at system 6m8m10m-r_32k.bin", 0x10001, 0x8000, CRC(c672efff) SHA1(7224bb6b4d25ef34bc0aa9d7c450baf9b47fd917), ROM_SKIP(1) | ROM_BIOS(8) )
	// 9: same as BIOS '22' in at
	ROM_SYSTEM_BIOS(9, "cdtekchips", "CDTEK 286")
	ROMX_LOAD( "286-cdtek2-even_32k.bin", 0x10000, 0x8000, CRC(94867e8d) SHA1(12e61cc8b875b57324c93276c9f6093f2bd0e277), ROM_SKIP(1) | ROM_BIOS(9) )
	ROMX_LOAD( "286-cdtek2-odd_32k.bin", 0x10001, 0x8000, CRC(153ed3bd) SHA1(10b711e0f0d79e0b6d181f24fe66544d2d72a310), ROM_SKIP(1) | ROM_BIOS(9) )
	// 10: same as BIOS '23' in at
	ROM_SYSTEM_BIOS(10, "286tact", "286 TACT")
	ROMX_LOAD( "286-tact-320548-1_32k.bin", 0x10000, 0x8000, CRC(0b528d19) SHA1(15f5a94d89461655c0f74681bbae5745db009ac2), ROM_SKIP(1) | ROM_BIOS(10) )
	ROMX_LOAD( "286-tact-320548-2_32k.bin", 0x10001, 0x8000, CRC(418aa2d0) SHA1(b6af0b8aa595d8f8de6c0fc851bf1c226dcc7ca7), ROM_SKIP(1) | ROM_BIOS(10) )
	// 11: BIOS-String: DG22-6080-091991-K0 / 286-BIOS (C) 1989 American Megatrends Inc / (C) 1991 TriGem Corporation
	ROM_SYSTEM_BIOS(11, "tg286m", "TG286M")
	ROMX_LOAD( "ami.bin", 0x10000, 0x10000, CRC(5751542a) SHA1(0fa0e86a0599e8500bb9ca9efa77cfc9c82a9dc0), ROM_BIOS(11))
	// 12: BIOS-String: 286 Modular BIOS Version B3.11.03 - REFRESH TIMING ERROR
	ROM_SYSTEM_BIOS(12, "cl286", "CL286")
	ROMX_LOAD( "award.bin", 0x10000, 0x10000, CRC(839a30b3) SHA1(c40a15c2636cf734e83ddf22213f637766f6456e), ROM_BIOS(12))
	// 13: BIOS-String: 286 Modular BIOS Version 3.03 Copyright Award Software Inc. - DIGICOM
	// ID: Digicom DIGIS 286S Turbo8/10MHz - Chipset: Chips P82C201-10, P82A205, P82A204, P82C202, NS16450N, HCT612, MC146818P, 2xNEC D8237AC-5
	// FPU socket provided - RAM: 1MB (4x9xTMS4256-12) - BIOS: Award 286 BIOS - OSC: 20.000MHz, 16000.00KHz, 1.8432MHz, 14.31818 - ISA8: 3, ISA16: 4
	// Keyboard BIOS: AWARD
	ROM_SYSTEM_BIOS(13, "digis286s", "DIGICOM DIGIS 286S")
	ROMX_LOAD( "80286-286s_turbo_lo.bin", 0x10000, 0x8000, CRC(7ecc1082) SHA1(eb5231e169ab550749c44383da20ab049cdf2a6d), ROM_SKIP(1) | ROM_BIOS(13) )
	ROMX_LOAD( "80286-286s_turbo_hi.bin", 0x10001, 0x8000, CRC(bea8047e) SHA1(17eb6ab8dbc61e372acdda060b84bc4914980322), ROM_SKIP(1) | ROM_BIOS(13) )
ROM_END


//**************************************************************************
//  80286 motherboard
//**************************************************************************

// ID: Peacock computer S-286 Rev A - CPU: 80286, FPU socket provided - Chipset: TI TACT82204FN, STK5134, STK-5136, STK-5132, STK-5133
// RAM: no onboard RAM, ISA8 socket marked "RAM-1" and "RAM-4", matching card "RAM-1A" with 1MB DIP - BIOS: Award 286 BIOS Q2093374
// Keyboard BIOS: Award Q2093198 - OSC: "clock II F22/F28", "clock II F19/F29" - ISA8: 3, ISA16: 4 - DIP8: 01101000
ROM_START( peas286 ) // BIOS-String: 286 Modular BIOS Version - REFRESH TIMING ERROR
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "286-s-286-peacock-lo.bin", 0x10000, 0x08000, CRC(78efd1d5) SHA1(3b6c5c6e77a3f77de1c81c0936d7fb2eab7b2487), ROM_SKIP(1) )
	ROMX_LOAD( "286-s-286-peacock-hi.bin", 0x10001, 0x08000, CRC(5b8ba43a) SHA1(a38ebb1d21f051065ede45643ed394cc5ac1dbf2), ROM_SKIP(1) )
ROM_END

// ID: PC CHIPS M209 - Chipset: UMC UM82C206L, PC 4L50F2052 - BIOS: AMI 286 BIOS 162020 CDTEK - Keyboard BIOS: AMI KEY BOARD BIOS 162020 CDTEK
// CPU:AMD N80L286-16/S - RAM: SIPP30x4, 1MB DIP - BIOS-ID: S286-6181-101590-K0 - ISA8: 1, ISA16: 6 - OSC: 33.333MHz, 14.31818
ROM_START( pccm209 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "pcchips_m209_gq4x.bin", 0x10000, 0x08000, CRC(d1a68208) SHA1(58285d293f723507a5401e55c5e4e5d27681d824))
	ROM_CONTINUE( 0x10001, 0x08000 )
ROM_END

// ID: unknown - ASI 100B0, identified as “HAM 12 TI 286 Motherboard ZERO WAIT” - Chipset: Texas Instruments TACT82301PB, TACT82302PB, TACT82303PB (cf. at386sx)
// CPU: xxx, FPU: IIT 2C87-12 - RAM: 1MB in DIP, 4xSIPP30 - OSC: 24.000MHz, 14.31818 - ISA8: 3, ISA16: 5
ROM_START( asi100b0 ) // BIOS-String: D286-1112-040990-K0
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "asi286_signetics_s-27c256-20fa-l.bin", 0x10000, 0x08000, CRC(54505e72) SHA1(024edd1b435252db38274626c84904422cdb8787), ROM_SKIP(1) )
	ROMX_LOAD( "asi286_signetics_s-27c256-20fa-h.bin", 0x10001, 0x08000, CRC(9aff417a) SHA1(f74da97c797b0856ee6ff634c40eee6403416e4c), ROM_SKIP(1) )
ROM_END

// ID: Wearnes CL286-12/16S (CL286-12S and CL286-16S) - Chipset: Texas Instruments TACT82206FN / Micrel MIC9212CP, MIC9211CP, MIC9215CP, WD16C785-JT
// BIOS/Version: AMI 080190 - Keyboard BIOS: KB-BIOS-VER-F - Rom Type : NMC27C256Q x 2, CL286-12S 080190 EVEN CS8A11, CL286-12S 080190 ODD CSD77D
// CPU: AMD N80L286-12/S, FPU socket provided - RAM: 1MB (8xKM44C256AP-8), 4xSIMM30 - ISA8: 1, ISA18: 5 - On board: Floppy, IDE, par, 2xser
ROM_START( cl28612s ) // dies after initialising the graphics card
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "cl286-12s16s_even.bin", 0x10000, 0x08000, CRC(0e8d1b02) SHA1(43339afd2ce0acc38074359b81629658cc6936f6), ROM_SKIP(1) )
	ROMX_LOAD( "cl286-12s16s_odd.bin", 0x10001, 0x08000, CRC(09e35644) SHA1(cc5ca52cbf0b5fe4c315ce725715f759e6ca4f63), ROM_SKIP(1) )
ROM_END

// TD60C - chipset: CITYGATE D90-272 - BIOS: AMI 286 BIOS, EE265746 - Keyboard-BIOS: JETkey V3.0
// BIOS-String: 30-0101-429999-00101111-050591-D90-0 / TD60C BIOS VERSION 2.42B - ISA16: 6 - CPU: CS80C286, FPU: i287XL
ROM_START( td60c )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "2cgm001.bin", 0x10000, 0x8000, CRC(35e4898b) SHA1(7ef8e097e010ec8dff9e33c4b42a278ff736059c))
	ROM_CONTINUE( 0x10001, 0x8000 )
ROM_END

// BIOS-String: Phoenix 80286 ROM BIOS PLUS Version 3.10.01 - CPU: AMD N80L286-16/S / FPU: socket provided - RAM: 640KB DIP / 4xSIPP30
// Chipset: SUNTAC ST62??? ST62C303-A - BIOS: Phoenix - Keyboard-BIOS: NEC D8041AHC - ISA16: 4 - ISA8: 2 - OSC: [unreadable] - 32.000 MHz
ROM_START( suntac303 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "286-suntac-2055712.bin", 0x10000, 0x8000, CRC(407b89d8) SHA1(d419bdd8bfb6191c68254204efdd756c5131701c))
	ROM_CONTINUE( 0x10001, 0x8000 )
ROM_END

// Olivetti M203 motherboard - complains about "Timer Sync Error"
// on board Paradise PVGA1A VGA chip - Chipset: 2 TACT chips, one VLSI chip - one 16bit ISA "slot" in pin strip form intended for an expansion module
ROM_START( olim203 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "286-olivetti-m203-low.bin", 0x00000, 0x10000, CRC(d683dc20) SHA1(04271529139724d7a091490658b186b59a83676f), ROM_SKIP(1) )
	ROMX_LOAD( "286-olivetti-m203-high.bin", 0x00001, 0x10000, CRC(c7324ecf) SHA1(fa5ee92c21e54ec711d01b211760521a71ef424d), ROM_SKIP(1) )
ROM_END

// Snobol Mini 286 - BIOS-String: DGS2-1402-101090-K0
// Chipset: GST GOLD GS62C101 and GS62C102
ROM_START( snomi286 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "gst_286.bin", 0x10000, 0x8000, CRC(89db769b) SHA1(3996856d637dc379978c0b5eb79362f46b60a80f) )
	ROM_CONTINUE( 0x10001, 0x8000)
ROM_END

// PC-Chips M205 - Chipset: PCChips 4L50F2052 aka PCCHIP1 - ISA8: 3, ISA16: 5
// the 64K ROM has first the 32K even, then the 32K odd part
// BIOS-String: S286-6181-101590-K0
ROM_START( pccm205 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// 0: v2.25, 17 January, 1991 (C) 1990 PC Chips INC.
	ROM_SYSTEM_BIOS(0, "v225", "PC Chips v2.25")
	ROMX_LOAD( "m205.bin", 0x10000, 0x8000, CRC(6f7bc8d6) SHA1(14062505b316e0d4409fb4e502651e09fea0a4c1), ROM_SKIP(1) | ROM_BIOS(0) )
	ROM_CONTINUE( 0x10001, 0x8000)
	// 1: v2.23, 29 December, 1990 (C) 1990 EVER-SUCCESS SYSTEMS LTD.
	ROM_SYSTEM_BIOS(1, "v223", "Ever-Success Systems v2.23")
	ROMX_LOAD( "m205_odd.bin", 0x10000, 0x8000, CRC(3a8ed558) SHA1(0648a2f1674bb07a175b983f107bb72c38ac1e61), ROM_SKIP(1) | ROM_BIOS(1) )
	ROM_CONTINUE( 0x10001, 0x8000)
ROM_END

// PC-Chips M216 REV 1.2 - Chipset PC CHIPS CHIP 3 - CPU: Harris CS80C286-20, IIT 2C87-10
// BIOS: AMI ; 07/07/91; S/NO. 0245157 - ISA16: 6 - BIOS-String: 30-0000-ZZ1437-00101111-070791-PC CHIPS-8
ROM_START( pccm216 ) // no display
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "2pcm001.bin", 0x10000, 0x10000, CRC(9c7c9f05) SHA1(39cb6645d9aab846f7e64d1d44610ea3cbe52581))
ROM_END

// Unknown 80C286 motherboard (RAM: 4xSIMM30, 1MB DIP, ISA16: 6) - CPU: Harris CS80C286-16, FPU: 80287 - OSC: 32.000MHz, 14.31818
// SARC RC2015; HM6818P; 82C042 or JETkey Keyboard BIOS; 1MB onboard RAM (8x LH64256AD-80)
ROM_START( sarcpc )
	ROM_REGION16_LE(0x20000, "bios", 0) // 27C512
	// BIOS-String: 20-0300-00834-00101111-050591-SARC286 / [80286 Standard System 2V1]
	//ROM_SYSTEM_BIOS(0, "sarcrev12", "SARC Rev. 1.2")
	ROM_LOAD( "sarcrev12.bin", 0x10000, 0x10000, CRC(1c5e3f2d) SHA1(1fcc8b1b9d9383467223dd41e420f9352beca654) )
ROM_END

// Everex EV-1806 (6 16-bit ISA, 1 8-bit ISA) - OSC: 14.31818MHz, 24.000MHz, 30.000MHz - RAM: 4 banks of 9xKM41C256P-8, sockets for 1MBit chips provided
// Everex IC-00121-0 + IC-00122-0; CHIPS P82C206; Intel 8272A(?); 146818A RTC
ROM_START( ev1806 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "everex_ev-1806_rev-f1a-21_even_u62.bin", 0x18000, 0x4000, CRC(7364e49b) SHA1(e8f5f41514005da0e36792e009cf3eae51c19c20), ROM_SKIP(1) )
	ROMX_LOAD( "everex_ev-1806_rev-f1a-21_odd_u61.bin", 0x18001, 0x4000, CRC(05c87bf7) SHA1(8c2243d9ee3d2af1517dc1134a22a7d1ed11262f), ROM_SKIP(1) )
ROM_END


// MAT286 REV.D (5 16-bit ISA, 1 8-bit ISA, RAM: DIP 1MB, 2xSIPP30) - CPU: Siemens SAB 80286-16-N - OSC: 32.000MHz, 14.31818
// Headland Technology HT12P-16/A; HM6818P RTC; JETkey keyboard BIOS; unmarked 40-pin DIP (prob. 80287)
ROM_START( mat286 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// BIOS-String: DH12-1112-061390-K0 - HT-12 286 BIOS - Board is also used in Polish Optimus 286 computer with a special Hercules character ROM (also available)
	// Files separated from single BIOS64 dump (PCB photo shows split ROMs are used)
	ROMX_LOAD( "9221fkf_imp23256_ami-l.bin", 0x10000, 0x08000, CRC(55deb5c2) SHA1(19ce1a7cc985b5895c585e39211475de2e3b0dd1), ROM_SKIP(1) )
	ROMX_LOAD( "9221gjf_imp23256_ami-h.bin", 0x10001, 0x08000, CRC(04a2cec4) SHA1(564d37a8b2c0f4d0e23cd1e280a09d47c9945da8), ROM_SKIP(1) )
ROM_END

ROM_START( ec1842 )
	ROM_REGION16_LE(0x20000,"bios", 0)
	ROM_LOAD16_BYTE( "4202004.bin", 0x1c001, 0x2000, CRC(33fb5382) SHA1(35eb62328324d93e7a06f2f9d1ad0002f83fc99b))
	ROM_LOAD16_BYTE( "4202005.bin", 0x1c000, 0x2000, CRC(8e05c119) SHA1(9d81613b4fc305c14ae9fda0b1dd97a290715530))
	ROM_LOAD16_BYTE( "4202006.bin", 0x18001, 0x2000, CRC(6da537ef) SHA1(f79feb433dcf41f5cdef52b845e3550d5f0fb5c0))
	ROM_LOAD16_BYTE( "4202007.bin", 0x18000, 0x2000, CRC(d6ee0e95) SHA1(6fd4c42190e879501198fede70ae43bc420681d0))
	// EGA ROM
	//ROM_LOAD16_BYTE( "4200009.bin", 0xc0000, 0x2000, CRC(9deeb39f) SHA1(255b859d3ea05891aa65a4a742ecaba744dfc923))
	//ROM_LOAD16_BYTE( "4200010.bin", 0xc0001, 0x2000, CRC(f2c38d93) SHA1(dcb3741d06089bf1a80cb766a6b94029ad698d73))
ROM_END

ROM_START( ec1849 )
	ROM_REGION16_LE(0x20000,"bios", 0)
	ROM_LOAD16_BYTE( "cpu-card_27c256_015.rom", 0x10000, 0x8000, CRC(68eadf0a) SHA1(903a7f1c3ebc6b27c31b512b2908c483608b5c13))
	ROM_LOAD16_BYTE( "cpu-card_27c256_016.rom", 0x10001, 0x8000, CRC(bc3924d6) SHA1(596be415e6c2bc4ff30a187f146664531565712c))
	//ROM_LOAD16_BYTE( "video-card_573rf6(2764)_040.rom", 0xc0001, 0x2000, CRC(a3ece315) SHA1(e800e11c3b1b6fcaf41bfb7d4058a9d34fdd2b3f))
	//ROM_LOAD16_BYTE( "video-card_573rf6(2764)_041.rom", 0xc0000, 0x2000, CRC(b0a2ba7f) SHA1(c8160e8bc97cd391558f1dddd3fd3ec4a19d030c))
ROM_END

// Morse KP-286
// BIOS-String: DS24-1216-061390-K0
// Chipset: SUNTAC ST62C211 and ST62C203-A
ROM_START ( mkp286 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "286-morse_kp286_lo.bin", 0x10000, 0x8000, CRC(0d35d2c9) SHA1(52b366608ea25a96d8e27c5d77689688fff38609), ROM_SKIP(1) )
	ROMX_LOAD( "286-morse_kp286_hi.bin", 0x10001, 0x8000, CRC(a5f640e0) SHA1(7bbb7fce54079005cb691816d2301a3eda475a82), ROM_SKIP(1) )
ROM_END


// WYSEpc 286 - motherboard: WY-2200-01 - continuous ticks from the speaker
ROM_START( wy220001 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "wyse_tech_rev.a_250325-02_27128-15.bin", 0x18000, 0x4000, CRC(010f1c4d) SHA1(712d6ca4e4bdbc6b105c8691d612407edcfd9cf7), ROM_SKIP(1))
	ROMX_LOAD( "wyse_tech_rev.a_250326-02_27128-15.bin", 0x18001, 0x4000, CRC(37fcd62b) SHA1(ada0e232387c8ba7067168f50f8b7a89eb824c44), ROM_SKIP(1))
ROM_END


// ***** 286 mainboards using the Chips & Technologies CS8221 NEAT chipset: P82C211 + P82C212 + P82C215 + P82C206

ROM_START( neat )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// 0: BIOS-String: ENET-1131-030389-K0
	ROM_SYSTEM_BIOS(0, "neat286", "NEAT 286")
	ROMX_LOAD( "at030389.0", 0x10000, 0x8000, CRC(4c36e61d) SHA1(094e8d5e6819889163cb22a2cf559186de782582),ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "at030389.1", 0x10001, 0x8000, CRC(4e90f294) SHA1(18c21fd8d7e959e2292a9afbbaf78310f9cad12f),ROM_SKIP(1) | ROM_BIOS(0))
	// 1: Phoenix 80286 ROM BIOS PLUS Version 3.10 12 - High Performance 286 ROM BIOS Ver C.12
	ROM_SYSTEM_BIOS(1, "pb800", "Packard Bell PB800")
	ROMX_LOAD( "3.10.12-1.bin", 0x10001, 0x8000, CRC(e6bb54c5) SHA1(fa5a376dd44696c78dcc8994e18938b5e1b3e45a),ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "3.10.12-2.bin", 0x10000, 0x8000, CRC(bde46933) SHA1(c7221192f48d6f2f5b773c3c7d2a52b635cb473e),ROM_SKIP(1) | ROM_BIOS(1))
	// 2: DTK Corp. 286 Computer - DTK 286 Chipset ROM BIOS Version 3.26 - #24062890N - ISA8: 3, ISA16: 5, RAM: 1MB DIP, 4xSIMM30
	ROM_SYSTEM_BIOS(2, "ptm1632c", "UNIT PTM1632C DTK V.3.26")
	ROMX_LOAD( "ptm1632c_l.bin", 0x10000, 0x8000, CRC(df0bc27c) SHA1(f94e2decd13c285c23b6a61c035cab88fa00ba6e), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD( "ptm1632c_h.bin", 0x10001, 0x8000, CRC(a80136e0) SHA1(5edc2d387efb42cf70361197de808ce1b06d8aec), ROM_SKIP(1) | ROM_BIOS(2))
	// 3: BIOS-String: DTK Corp. 286 COMPUTER - (C) DTK NEAT BIOS Ver 3.25N2 06/06/89 - DTK PTM-1233C - Chipset: P82C211; P82C212B; P82C215 - BIOS: dtk 286E 8864 - IS8: 3 - ISA16: 5
	ROM_SYSTEM_BIOS(3, "ptm1233c", "DTK PTM-1233C")
	ROMX_LOAD( "286-dtk ptm-1233c-low_32k.bin", 0x10000, 0x8000, CRC(8909164c) SHA1(51978929a690746c1956ca6b1f0412777dc5d35b), ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "286-dtk ptm-1233c-high_32k.bin", 0x10001, 0x8000, CRC(9105968c) SHA1(737d4df8040655315a648fed8a8d574f39e7dc35), ROM_SKIP(1) | ROM_BIOS(3) )
	// 4: 286-NEAT - BIOS-String: ENET-1131-040990-K0 - NEAT V3.2 6-18-90 - ISA8: 3, ISA16: 5
	ROM_SYSTEM_BIOS(4, "286neat", "286-NEAT")
	ROMX_LOAD( "286-neat_neat012-l-verify.bin", 0x10000, 0x8000, CRC(591d226c) SHA1(7f42797ead8213022192bb2bbbe2de7f6796ac6f), ROM_SKIP(1) | ROM_BIOS(4) )
	ROMX_LOAD( "286-neat_neat012-h-verify.bin", 0x10001, 0x8000, CRC(0198e2e4) SHA1(10ced383b6dc00c2e98b7bed0782f59a9c266625), ROM_SKIP(1) | ROM_BIOS(4) )
	// 5: AUVA VIP BAM/16-11 - BIOS-String: Phoenix 80286 ROM BIOS PLUS Version 3.10 20 - ISA8:1, ISA16: 5, Memory Slot: 1
	ROM_SYSTEM_BIOS(5, "bam1611", "VIP BAM/16-11") // OSC: 18.432 - 14.318 - 32.000MHz
	ROMX_LOAD( "286-vip bam-6-11 m215100-lo_32k.bin", 0x10000, 0x8000, CRC(b51b8bc1) SHA1(a7ebbced98aca32a7f0cdf80d1b832dfeb92d5e7), ROM_SKIP(1) | ROM_BIOS(5) )
	ROMX_LOAD( "286-vip bam-6-11 m215100-hi_32k.bin", 0x10001, 0x8000, CRC(46ddd5a6) SHA1(fd4267af298c7f70e062a7c4e023caf852bbf082), ROM_SKIP(1) | ROM_BIOS(5) )
	// 6: CP-805 - BIOS-String: ENET-1138-030390-K0
	ROM_SYSTEM_BIOS(6, "cp805", "CP-805")
	ROMX_LOAD( "286-chips ami78384 even.bin", 0x10000, 0x8000, CRC(5280fee0) SHA1(25051ad6bbccddc0738861b614dbafbca5c3bff5), ROM_SKIP(1) | ROM_BIOS(6) )
	ROMX_LOAD( "286-chips ami78384 odd.bin", 0x10001, 0x8000, CRC(24526bf3) SHA1(8f8b46fe2e708fa53d0eeb44a16924cd878bdd33), ROM_SKIP(1) | ROM_BIOS(6) )
	// 7: BIOS-String: ENET-1107-040990-K0
	ROM_SYSTEM_BIOS(7, "ami211", "AMI 21.1") /*(Motherboard Manufacturer: Dataexpert Corp. Motherboard) (Neat 286 BIOS, 82c21x Chipset ) (BIOS release date:: 09-04-1990)*/
	ROMX_LOAD( "ami211.bin",     0x10000, 0x10000,CRC(a0b5d269) SHA1(44db8227d35a09e39b93ed944f85dcddb0dd0d39), ROM_BIOS(7))
	// 8: BIOS-String: ENET-1230-043089-K0
	ROM_SYSTEM_BIOS(8, "amic21", "AMI C 21.1") /* (Motherboard Manufacturer: Unknown.) (Neat 286 BIOS, 82c21x Chipset ) (BIOS release date:: 30-04-1989) */
	ROMX_LOAD( "amic21-2.bin",  0x10001, 0x8000, CRC(8ffe7752) SHA1(68215f07a170ee7bdcb3e52b370d470af1741f7e),ROM_SKIP(1) | ROM_BIOS(8) )
	ROMX_LOAD( "amic21-1.bin",  0x10000, 0x8000, CRC(a76497f6) SHA1(91b47d86967426945b2916cb40e76a8da2d31d54),ROM_SKIP(1) | ROM_BIOS(8) )
	// 9: BIOS-String: - AGC N286 - CPU: AMD N80L286-16/S, FPU socket provided - RAM: 36xTMC64C1024-80N (18pin), 36x16pin sockets provided as an alternative (2xBank 0, 2xBank 1), 4xSIPP30 (2xBank 0/2 and 2xBank 1/3)
	// Chipset: Chips P82C212B-12, P82C206 H, P82C211-12 C, P82C215-12 - OSC: 20.000, 32.000MHz, 24.0000MHz, 32.768KHz, 14.31818 - BIOS: AMI 286 BIOS PLUS CO.NO.1190
	// Keyboard-BIOS: AMI KEYBOARD BIOS PLUS CO.NO. 1190 - ISA8: 2, ISA16: 6
	ROM_SYSTEM_BIOS(9, "n286", "N286")// stops after initialising the graphics card
	ROMX_LOAD( "286-chips-ami1190-even_32k.bin", 0x10000, 0x8000, CRC(db941036) SHA1(994cced82b5fb5f8833c718b4226a7e9620b56df),ROM_SKIP(1) | ROM_BIOS(9) )
	ROMX_LOAD( "286-chips-ami1190-odd_32k.bin", 0x10001, 0x8000, CRC(71cfc2d1) SHA1(8b8cf81161aec3e2c7f653e5d3a6b4e9627663c6),ROM_SKIP(1) | ROM_BIOS(9) )
ROM_END

// Chaintech ELT-286B-160B(E) mainboards - NEAT chipset: Chips P82C206, P82C211C, P82C212B, P82C215
ROM_START( elt286b )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// 0: BIOS/Version: AWARD A2133130/21323132 - BIOS-String: 286 Modular BIOS Version 0N3.03 NFS / ELT
	// Keyboard-BIOS: AWARD A21266586 - OSC: 24.000MHz, 12.000MHz, 20.000MHz, 14(... unreadable) - ISA8: 2, ISA16: 5
	ROM_SYSTEM_BIOS(0, "160b", "ELT-286B-160B")
	ROMX_LOAD( "286-elt-286b-160b_l_32k.bin", 0x10000, 0x8000, CRC(4514a284) SHA1(0f9d4a24bdd0fb6aa15c7c1db860c4e6df632091), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "286-elt-286b-160b_h_32k.bin", 0x10001, 0x8000, CRC(109bbf7c) SHA1(88b6b1c7c08739f8b198f05adbe6edc24be35fd0), ROM_SKIP(1) | ROM_BIOS(0) )
	// 1: BIOS/Version: Phoenix 2061618  BIOS-String: Phoenix 80286 ROM BIOS PLUS Version 3.10 20 / Phoenix C&T 8221 NEAT Dual Mode BIOS / ELT
	// Keyboard-BIOS: Phoenix/Intel i8242 - ISA8: 2, ISA16: 5 - OSC: 32.000MHz, 24.000MHz, 14.31818MHz
	ROM_SYSTEM_BIOS(1, "160eb", "ELT-286B-160BE")
	ROMX_LOAD( "286-2061618 l_32k.bin", 0x10000, 0x8000, CRC(f89aabc4) SHA1(94472edc9692b9da6450fb12994d62230c8cc5c5), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "286-2061618 h_32k.bin", 0x10001, 0x8000, CRC(e23a60bf) SHA1(48af3f123d30cd2fde9e42f2c9a57eec143287b6), ROM_SKIP(1) | ROM_BIOS(1) )
ROM_END

// Advanced Logic Research PWB 7270 REV E - Chipset: Chips P82C212B-12 A, P82C211-12 C, P82C215 A 16 MHz, P62C206 H1, UMC xxx, DP8473V
// CPU: AMD N80L286-16/S, FPU: Intel D80287, Conntector for 386/486 FEATURE - RAM: 1MB (8x514256), 2xSIMM30 - OSC: 32.000MHz, 24.0000MHz, 1.8432MHz, 14.31818 MHz
// BIOS: POWER FLEX+ 3.10.09 ODD  CS0F00, POWER FLEX+ 3.10.09 EVEN CS8A00 - Keyboard BIOS: M5L8042 - On board: IDE, keyboard, 2x25pin (ser/par?) - ISA16: 5
// BIOS-String: Phoenix ROM BIOS PLUS Version 3.10 09 - Advanced Logic Research, Inc. PowerFlex PLUS 286/386sx
// Timer chip counter 2 failed, Keyboard failure
ROM_START( pwb7270e )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "alreven.bin", 0x10000, 0x08000, CRC(b6428bae) SHA1(4e6e6eec67ff62cf8b4a3ce500bd15a54ee3d5fe), ROM_SKIP(1) )
	ROMX_LOAD( "alrodd.bin", 0x10001, 0x08000, CRC(6eedbcf0) SHA1(58173b6f749d40aa294747823d4b442c8938710e), ROM_SKIP(1) )
ROM_END


// ***** 286 motherboards using the Acer (ALi) M1207 chipset

// CMP enterprise CO.LTD. Phoenix 80286 ROM BIOS Version 3.00
// ROM_SYSTEM_BIOS(26, "cmpa286", "CMP A286") /* (Chipset Acer (ALi) M1207-12) (BIOS release date:: 01-09-1986) (ISA8: 2, ISA16: 6) */
ROM_START ( cmpa286 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "286-a286-even_16k.bin", 0x18000, 0x4000, CRC(30809487) SHA1(413de43ca7e1930cdf3c006718d8baf743a9ff1e), ROM_SKIP(1) )
	ROMX_LOAD( "286-a286-odd_16k.bin", 0x18001, 0x4000, CRC(3a11aacf) SHA1(23185531ae10912b974048d3607b563e55d3fa96), ROM_SKIP(1) )
ROM_END

// AUVA VIP-M21502A BAM16-A0 - BIOS-String: DAR2-1105-061390-K0 - 286-BIOS AMI for AUVA 286, 02/08/1991 - ISA8:2, ISA16:5
ROM_START( bam16a0 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "286-vip-m21502a-lo_32k.bin", 0x10000, 0x8000, CRC(413692db) SHA1(54bf664526b137cabf974c1fc659493e76243a88), ROM_SKIP(1) )
	ROMX_LOAD( "286-vip-m21502a-hi_32k.bin", 0x10001, 0x8000, CRC(5db9db04) SHA1(8085384b943454a708be3104b47f6793d0040ab1), ROM_SKIP(1) )
ROM_END


// ***** 286 motherboards using the Chips SCAT 82C235 chipset

// Biostar MB-1212C - ISA8:2, ISA16:5
ROM_START ( mb1212c )
	// 0: BIOS-String: ESC2-1223-083090-K2 - 286 BIOS AMI for MB-1212C version 1.1
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "mb1212_1", "mb1212_1")
	ROMX_LOAD( "biostar mb-1212c.bin", 0x10000, 0x10000, CRC(153a783a) SHA1(acad4a3ffe93d3884dcb743c32d6317a132cda7b), ROM_BIOS(0) )
	// 1: CHIPS SCAT BIOS Version 125D - MB-1212C
	ROM_SYSTEM_BIOS(1, "mb1212_2", "mb1212_2")
	ROMX_LOAD( "mb-1212c.bin", 0x10000, 0x10000, CRC(4675530a) SHA1(c34b1c67ac29695e565363f484e17ab5f8ddaad5), ROM_BIOS(1) )
ROM_END


// ***** 286 motherboards using the Headland G2 chipset

// LM-103S - 1 8-bit ISA, 6 16-bit ISA - RAM: 4xSIPP30, 2 banks DIP (each bank has 4xV53C104AP80 and 2x16pin empty sockets)
// Headland Technology G2 chipset: HT101A + 2x HT102; HM6818P RTC; AMI keyboard BIOS 904189, BIOS AMI 904189
// BIOS-String: D286-1234-121589-K0 - CPU: AMD N80L286-16/S - OSC: 32.000MHz, 14.31818MHz
ROM_START( lm103s )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "ami_lm103-s_lo.bin", 0x10000, 0x8000, CRC(a24be20b) SHA1(ffc5faf6d773154bf2f037556d2e381e81a28a58), ROM_SKIP(1) )
	ROMX_LOAD( "ami_lm103-s_hi.bin", 0x10001, 0x8000, CRC(7b63e60c) SHA1(da78b95b12051b6d4701a412fdc5e7874595c188), ROM_SKIP(1) )
ROM_END

// CDTEK board with Headland G2 chipset - ISA8:1, ISA16:5
ROM_START ( cdtekg2 ) // BIOS-String: D286-1435-040990-K0 - Board is used in Polish California Access 286 with a special Hercules character ROM (also available)
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "286_cdtek headland-l_32k.bin", 0x10000, 0x8000, CRC(341fe2a3) SHA1(f8e10aea477c2b3c92b28a7e0fd0adf8ade22b9e), ROM_SKIP(1) )
	ROMX_LOAD( "286_cdtek headland-h_32k.bin", 0x10001, 0x8000, CRC(bd6fd54f) SHA1(72500ebe4041fbe635562bf55c5d3635257e38f1), ROM_SKIP(1) )
ROM_END

// Octek board with Headland G2 chipset - ISA8:2, ISA16:6
//BIOS-String: 286 Modular BIOS Version 3.03 - O.O.A.L.
ROM_START ( octekg2 ) // BIOS-String: D286-1435-040990-K0
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "286-octek-g2_u44_32k.bin", 0x10000, 0x8000, CRC(05892a08) SHA1(e12795524d87c422b0b5d660b36139592893e9c6), ROM_SKIP(1) )
	ROMX_LOAD( "286-octek-g2_u45_32k.bin", 0x10001, 0x8000, CRC(2f81de14) SHA1(952d9e35a6f8ea74eb8b4bf7ea80d7c358474cb8), ROM_SKIP(1) )
ROM_END

// Octek Fox M 286 Rev 1.1 - Chipset: Headland HT101A/2xHT102 - CPU: AMD N80L286-16/S, FPU socket provided - RAM: 514256x8 (4 empty tag sockets provided), 4xSIMM30
// BIOS: AMI 286 BIOS 212491, Keyboard BIOS: AMI Keyboard BIOS setup -  OSC: 32.000MHz, 14.31818 - ISA8: 2, ISA16: 4 - undumped PAL: PAL 090-103, 089-001, 090-011
// BIOS-String: D286-6069-040990-K0 - BIOS release date:: 09-04-1990
ROM_START( ocfoxm )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "ami_286_bios_sn200960_even.bin", 0x10000, 0x8000, CRC(67745815) SHA1(ca6886c7a0716a92a8720fc71ff2d95328c467a5), ROM_SKIP(1) )
	ROMX_LOAD( "ami_286_bios_sn200960_odd.bin", 0x10001, 0x8000, CRC(360a5f73) SHA1(1b1980fd99779d0cdc4764928a641e081b35ee9f), ROM_SKIP(1) )
ROM_END

ROM_START( headg2 )
	ROM_REGION16_LE( 0x20000, "bios", 0)
	// 0: 286 board with Headland GC101A-PC; GC102-PC chipset and Phoenix BIOS 2493119, ISA8: 2, ISA16: 5
	ROM_SYSTEM_BIOS(0, "head_ph_1", "Headland/Phoenix #1") // Phoenix 80286 ROM BIOS PLUS Version 3.10.21 ((BIOS release date:: 15-01-1988)
	ROMX_LOAD( "286-headland-lo_32k.bin", 0x10000, 0x8000, CRC(21b68bed) SHA1(1e4acda50b12ad463c169ba615805f5dcf257b18), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "286-headland-hi_32k.bin", 0x10001, 0x8000, CRC(04c8ab12) SHA1(b46c14528aca15464e4050b423c2f621a4313a85), ROM_SKIP(1) | ROM_BIOS(0) )
	// 1: BIOS-String: 286 Modular BIOS Version 3.03HL - ISA16: 5
	ROM_SYSTEM_BIOS(1, "head4530", "Headland 4530")
	ROMX_LOAD( "286-headland 4530-high_32k.bin", 0x10001, 0x8000, CRC(f84c0e75) SHA1(42dc068d1cd5105cd576b023e2ccfe0f0646d4e3), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "286-headland 4530-low_32k.bin", 0x10000, 0x8000, CRC(0856dde8) SHA1(cee5d6002c405df984f3c7fa83c4f3e034f1e586), ROM_SKIP(1) | ROM_BIOS(1) )
	// 2: Quadtel Enhanced 286 BIOS Version 3.04.02 - Headland HT101, HT102
	ROM_SYSTEM_BIOS(2, "ami101", "AMI HT 101.1") /* (Quadtel Enhanced 286 BIOS Version 3.04.02) (BIOS release date:: 09/11/1989) */
	ROMX_LOAD( "amiht-h.bin",   0x10001, 0x8000, CRC(8022545f) SHA1(42541d4392ad00b0e064b3a8ccf2786d875c7c19),ROM_SKIP(1) | ROM_BIOS(2) )
	ROMX_LOAD( "amiht-l.bin",   0x10000, 0x8000, CRC(285f6b8f) SHA1(2fce4ec53b68c9a7580858e16c926dc907820872),ROM_SKIP(1) | ROM_BIOS(2) )
ROM_END


// ***** 286 motherboards using the Headland HT12/A chipset

// Octek Fox II - Chipset: Headland HT12/A - BIOS String: DH1X-6069-113090-K0 - HT-1X 286 BIOS
ROM_START( o286foxii )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "286-fox2-even_32k.bin", 0x10000, 0x8000, CRC(54dc119f) SHA1(4bc543beef0d2201fa20eac90a0a6ca38ebf0dbf), ROM_SKIP(1))
	ROMX_LOAD( "286-fox2-odd_32k.bin", 0x10001, 0x8000, CRC(e5db7775) SHA1(2bd0572b9f7c76eff51375b551586ca8484e2a74), ROM_SKIP(1))
ROM_END

// BI-025C HT12 286 - Chipset: Headland HT12/A - BIOS-String: DH12-1103-061390-K0 - ISA8: 2, ISA16: 5
ROM_START( bi025c )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "bi-025c-ht12_even.bin", 0x10000, 0x8000, CRC(7ea7e088) SHA1(e245b3ecce39e85cacb17abf60d2cee000d1750d), ROM_SKIP(1))
	ROMX_LOAD( "bi-025c-ht12_odd.bin", 0x10001, 0x8000, CRC(f18b3eef) SHA1(e14d4b3ea0234613e60512cf79e5580c9ce7f3f6), ROM_SKIP(1))
ROM_END

ROM_START( ht12a )
	ROM_REGION16_LE(0x20000, "bios", 0)
	//0: BIOS-String: DH12-1343-061390-K0
	// Original BIOS64 dump split into even and odd bytes (matches another dump of the same BIOS)
	// ROM at U6 has sticker with AMI 253770 label; "BB012" at U8 is probably other half of BIOS, though not clear which half is which
	// Unknown motherboard (similar layout to LM-103S; 4 SIMM, 5 16-bit ISA, 2 8-bit ISA)
	// Headland HT12/A; HM6818A RTC; AMI K053770 keyboard BIOS
	// Jumpers at right edge of board are labeled "KEYLOCK" (J6), "SPEAKER" (J7), "TURBO LED" (J8), "TURBO S.W." (J9), "RESET" (J10)
	// XTALs X3 and X4 in top right corner (behind 80C287) are both unpopulated
	ROM_SYSTEM_BIOS(0, "dh12-k0", "AMI DH12-K0")
	ROMX_LOAD( "286_headland_even.bin", 0x10000, 0x8000, CRC(a2530914) SHA1(1aca289240caa6d4bf811d301c338c157b6902a1), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "286_headland_odd.bin", 0x10001, 0x8000, CRC(b5f69002) SHA1(ee9ceef1fc7a328ee82006cd504e72e16f21b3c8), ROM_SKIP(1) | ROM_BIOS(0) )
	// 1: 286 board with Headland Headland HT12/A chipset, one ROM market IQS, Phoenix BIOS 3479808 - ISA8: 1, ISA16: 6
	ROM_SYSTEM_BIOS(1, "head_ph_2", "Headland/Phoenix #2") // Phoenix BIOS A286 Version 1.01 - BIOS ID JLI01101 - IT9109 - Reference ID 01 - (BIOS release date:: 19-04-1990)
	ROMX_LOAD( "286-headland-iqs-lo_32k.bin", 0x10000, 0x8000, CRC(60424e9d) SHA1(aa813bf48939fe7fcbbfec3133e702bfdff6234e), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "286-headland-iqs-hi_32k.bin", 0x10001, 0x8000, CRC(e56212e0) SHA1(2441845d632d19adc0592e094beb5ec1fbe074f6), ROM_SKIP(1) | ROM_BIOS(1) )
	// BIOS-String: Quadtel HT12 286 BIOS Versio 3.05.03
	// 2: Same board as #1, CPU: AMD N80L286-16S, FPU socket provided, Chipset: Headland HT12 - OSC: 14.31818, 8.000, 32.000MHZ, unpopulated: ASYN BUS CLK
	// RAM: 8xHYB514256B-60 (8x20pin, alternativelx 4x18pin), Parity (2x16pin, 2x18pin) empty, 4xSIMM30 (Bank0 SIMM, Bank1 SIMM) - BIOS: Quadtel BIOS Software 286 253893
	// Keyboard-BIOS: Quadtel BIOS Software KEY 316018 (undumped) - JP7: NO ASYN CLK, JP4: CMOS RAM CLEAR, JP3: COLOR - JP: 80287CLK=ASYN CLK
	ROM_SYSTEM_BIOS(2, "quadtel", "Quadtel")
	ROMX_LOAD( "bios-lo.bin", 0x10000, 0x8000, CRC(433d8044) SHA1(3435d51fad97247b4bcfdb2f3fdb358d99b0e6ea), ROM_SKIP(1) | ROM_BIOS(2) )
	ROMX_LOAD( "bios-hi.bin", 0x10001, 0x8000, CRC(fe124da4) SHA1(b3e4e598cf1f5cada1b101d0c6434770017de3c6), ROM_SKIP(1) | ROM_BIOS(2) )
	// 3: BIOS-String: DH12-1164-083090-K0 - CPU/FPU: N80L286-16/S, P80C287-10 - ISA8: 2, ISA16: 6
	ROM_SYSTEM_BIOS(3, "head12a01", "Headland HT12/A #1")
	ROMX_LOAD( "2hlm002l.bin", 0x10000, 0x8000, CRC(345b9ea1) SHA1(868cc309e433e0dcc9f3aa147263017b7f822461), ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "2hlm002h.bin", 0x10001, 0x8000, CRC(35eed8b8) SHA1(119f2676aef038301c3e0bcdb999da6fd740e6a5), ROM_SKIP(1) | ROM_BIOS(3) )
	// 4: MBL M21 - BIOS-String: DH12-1211-061390-K0 / HT-12 286 BIOS - Chipset: Headland HT12/A
	ROM_SYSTEM_BIOS(4, "ami121", "AMI HT 12.1") /* (BIOS release date:: 13-06-1990) */
	ROMX_LOAD( "ami2od86.bin", 0x10001, 0x8000, CRC(04a2cec4) SHA1(564d37a8b2c0f4d0e23cd1e280a09d47c9945da8),ROM_SKIP(1) | ROM_BIOS(4) )
	ROMX_LOAD( "ami2ev86.bin", 0x10000, 0x8000, CRC(55deb5c2) SHA1(19ce1a7cc985b5895c585e39211475de2e3b0dd1),ROM_SKIP(1) | ROM_BIOS(4) )
	// 5: SPEC 286 rev 4a - BIOS-String: DH12-1120-061390-K0
	ROM_SYSTEM_BIOS(5, "ami122", "AMI HT 12.2") /* (BIOS release date:: 13-06-1990) */
	ROMX_LOAD( "ami2ev89.bin", 0x10000, 0x8000, CRC(705d36e0) SHA1(0c9cfb71ced4587f109b9b6dfc2a9c92302fdb99),ROM_SKIP(1) | ROM_BIOS(5) )
	ROMX_LOAD( "ami2od89.bin", 0x10001, 0x8000, CRC(7c81bbe8) SHA1(a2c7eca586f6e2e76b9101191e080a1f1cb8b833),ROM_SKIP(1) | ROM_BIOS(5) )
	// 6: BIOS-String: DH12-1112-061390-K0
	ROM_SYSTEM_BIOS(6, "ami123", "AMI HT 12.3") /*(Motherboard Manufacturer: Aquarius Systems USA Inc.) (BIOS release date:: 13-06-1990)*/
	ROMX_LOAD( "ht12h.bin", 0x10001, 0x8000, CRC(db8b471e) SHA1(7b5fa1c131061fa7719247db3e282f6d30226778),ROM_SKIP(1) | ROM_BIOS(6) )
	ROMX_LOAD( "ht12l.bin", 0x10000, 0x8000, CRC(74fd178a) SHA1(97c8283e574abbed962b701f3e8091fb82823b80),ROM_SKIP(1) | ROM_BIOS(6) )
	// 7: ID: H286-C3 158 - Chipset: Headland HT12/A3A0050 - BIOS: AMI 286 BIOS SETUP 649963 - Keyboard BIOS: JET ELECTRONICS CO LTD SN 9 0922133
	// BIOS-String: DH12-1164-083090-K - CPU: AMD N80L286-12/S, FPU socket provided - RAM: SIMM30: 4, 1MB DIP - OSC: 8.000MHZ - ISA8: 1, ISA16: 5
	ROM_SYSTEM_BIOS(7, "h286c3", "H286-C3")
	ROMX_LOAD( "h286-c3-158-hi.bin", 0x10001, 0x8000, CRC(ec0cbbba) SHA1(97d7f32cb9c622cfbd08909ac67d09a8aa734430),ROM_SKIP(1) | ROM_BIOS(7) )
	ROMX_LOAD( "h286-c3-158-lo.bin", 0x10000, 0x8000, CRC(dedcf41a) SHA1(ede2c852b3e947ce4efa54640b2e37db83355a6c),ROM_SKIP(1) | ROM_BIOS(7) )
	// 8: CPU: Harris 286-16, FPU socket provided - Chipset: Headland HT12P-16/A - BIOS: AMIBIOS 03/15/91 - BIOS-String: 20-0000-428022-00101111-031591-HT12-F
	// RAM: 1MB DIP, 4xSIMM30 - ISA8: 1, ISA16: 6 - OSC: 32.000MHz, 14.31818
	ROM_SYSTEM_BIOS(8, "8022", "8022") // no display
	ROMX_LOAD( "20-0000-428022-00101111-031591-ht12 low.bin", 0x10000, 0x8000, CRC(cb74f8e3) SHA1(fc874787f960587ba37442d59af1beebcfd798b9),ROM_SKIP(1) | ROM_BIOS(8) )
	ROMX_LOAD( "h20-0000-428022-00101111-031591-ht12 high.bin", 0x10001, 0x8000, CRC(36179ed9) SHA1(25968319bcd35ff06a0f0edac6ff0246f3f79c25),ROM_SKIP(1) | ROM_BIOS(8) )
ROM_END


// ***** motherboards using the six chip SUNTAC chipset: ST62BC001-B, ST62BC002-B, ST62BC003-B, ST62C006, ST62BC004-B1, ST62BC005-B

// Magitronic B233 (8 ISA slots)
// SUNTAC Chipset, http://toastytech.com/manuals/Magitronic%20B233%20Manual.pdf
// SUNTAC ST62BC002-B, ST62BC005-B, ST62BC003-B, ST62BC001-B, ST62C00B, ST62BC004-B1
ROM_START( magb233 )
	ROM_REGION16_LE(0x20000, "bios", 0)  // BIOS-String: DSUN-1105-043089-K0
	ROMX_LOAD( "magitronic_b233_ami_1986_286_bios_plus_even_sa027343.bin", 0x10000, 0x8000, CRC(d4a18444) SHA1(d95242104fc9b51cf26de72ef5b6c52d99ccce30), ROM_SKIP(1) )
	ROMX_LOAD( "magitronic_b233_ami_1986_286_bios_plus_odd_sa027343.bin", 0x10001, 0x8000, CRC(7ac3db56) SHA1(4340140450c4f8b4f6a19eae50a5dc5449edfdf6), ROM_SKIP(1) )
	// ROM_LOAD("magitronic_b233_ami_1986_keyboard_bios_plus_a025352.bin", 0x0000, 0x0800), CRC(84fd28fd) SHA1(43da0f49e52c921844e60b6f3d22f2a316d865cc) )
ROM_END

// Magitronic B236 (ISA8: 2, ISA16: 6) - 286 Modular BIOS Version 3.03GX
// SUNTAC ST62BC002-B, ST62BC005-B, ST62BC003-B, ST62BC001-B, ST62C00B, ST62BC004-B1
ROM_START( magb236 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "sunt-lo.rom", 0x18000, 0x4000, CRC(d05c0edf) SHA1(bfd9c68cd5dc874b9519056b3a8cc6ea504b0be3), ROM_SKIP(1) )
	ROMX_LOAD( "sunt-hi.rom", 0x18001, 0x4000, CRC(e5dce491) SHA1(282ad2da0ef47147cbc0c68295e3d4249f4147b2), ROM_SKIP(1) )
ROM_END

// AUVA COMPUTER, INC. BAM/12-S2 - VIP - Phoenix 80286 ROM BIOS PLUS Version 3.10 10
// Chipset: SUNTAC ST62BC004-B1, ST62BC001-B, ST62BC002-B, ST62BC003-B, ST62BC005-B - ISA8: 3, ISA16: 5
ROM_START( aubam12s2 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "st-ph-l.rom", 0x00001, 0x10000, CRC(7f60168c) SHA1(a6d8dafa6319753466243dbde9676fa0e402f5fe), ROM_SKIP(1))
	ROMX_LOAD( "st-ph-h.rom", 0x00000, 0x10000, CRC(5b4fd7ee) SHA1(821fe868da5c7ff28f2c7b9bae03d0b8a76af796), ROM_SKIP(1))
ROM_END

// HLB-286 MBA-009 - BIOS: 286 Modular BIOS Version 3.03 HL - HLB-286 System
// SUNTAC ST62BC002-B, ST62BC005-B, ST62BC003-B, ST62BC001-B, ST62C00B, ST62BC004-B1
ROM_START( mba009 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "hlb-286l.bin", 0x18000, 0x4000, CRC(9085b21c) SHA1(4f264612c458ab03f94dbac9852fcf9dea2065cc), ROM_SKIP(1))
	ROMX_LOAD( "hlb-286h.bin", 0x18001, 0x4000, CRC(03cdbee8) SHA1(9ea5f91a76bc8861fdc7e5381e8dc15f8fb428f5), ROM_SKIP(1))
ROM_END

// Everex EV-1815 (C & T/Suntac) - RAM: 4xSIMM30, 512KB or 1MB total (2/4 SIMMs)
ROM_START( ev1815 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// 0: Award 286 Modular BIOS Version 3.03 - GCH
	// additional info from chukaev.ru54.com: SUNTAC ST62BC... 001-B, 002-B, 003-B, 004-B1, 005 - ISA8: 3, ISA16: 5 - CPU/FPU: N80L286-10, 80287
	ROM_SYSTEM_BIOS(0, "ev1815303", "Everex EV-1815 V3.03")  /* (BIOS release date:: 15-11-1985) */
	ROMX_LOAD( "award_v3.03_ev1815_even.bin", 0x18000, 0x4000, CRC(dd64bdd6) SHA1(b3108b692d2aa03701ac894602e9418ae0779702), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "award_v3.03_ev1815_odd.bin", 0x18001, 0x4000, CRC(29f023fb) SHA1(873561bb7087483c0c763ef9cd32c1adf0f7cb5e), ROM_SKIP(1) | ROM_BIOS(0) )
	// 1: Award 286 Modular BIOS Version 3.03GS
	ROM_SYSTEM_BIOS(1, "ev1815303gs", "Everex EV-1815 V3.03GS") /* (BIOS release date:: 15-11-1985) */
	ROMX_LOAD( "award_v3.03gs_ev1815_even.bin", 0x10000, 0x8000, CRC(59489ec2) SHA1(b3c13ba53d4c4ee75a15703236a748121102ce84), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "award_v3.03gs_ev1815_odd.bin", 0x10001, 0x8000, CRC(5bcd9421) SHA1(f32e5a39da593c6982f964fb05b0802d54c3de45), ROM_SKIP(1) | ROM_BIOS(1) )
ROM_END

// Mintek BAY-1000C V1.01 - Chipset: SUNTAC ST62BC002-B, ST62C005-B, ST62BC001-B, ST62BC003-B, ST62BC004-B1, ST62C008
// CPU: AMD N80L286-12/S - RAM: 4xSIMM30, 18x18pin/16pin, 8x20pin, 4x16pin - OSC: 12.000, 14.31818, 25.000MHz - ISA8: 2, ISA16: 6
// BIOS-String:  Phoenix 80286 ROM BIOS PLUS Version 3.10 00 / LYI-CHENG ENTERPRISE CO., LTD.
ROM_START( bay1000c )
	ROM_REGION16_LE( 0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "286-suntac_bay-1000c-ic22_32k.bin", 0x10000, 0x8000, CRC(105d3257) SHA1(cf10d09db57f65fee649adcb39058d9d9aefe9e9))
	ROM_LOAD16_BYTE( "286-suntac_bay-1000c-ic23_32k.bin", 0x10001, 0x8000, CRC(3b997bb1) SHA1(70d3bb9e57624c9f64d70bc5f3c00305a08a8b2e))
ROM_END

ROM_START( suntac6 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// CPU: AMD N80L286-12/S - RAM: 36 sockets 18pin/16pin, fitted: 36x41C256P-8 - BIOS: Award 286 - Keyboard-BIOS: M5L8042-165P
	// ISA8: 2, ISA16: 6 - OSC: 12.000 MHz, 24.000, 14.31818
	// 0: BIOS-String: 286 Modular BIOS Version 3.03 01 Copyright Award Software Inc.
	ROM_SYSTEM_BIOS(0, "terraat", "Terra AT")
	ROMX_LOAD( "286-suntac-award_a2132439lo.bin", 0x18000, 0x4000, CRC(5fa269f8) SHA1(4167017aaeb63f4eedde155ba29f33ae2a94403b), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "286-suntac-award_a2132439hi.bin", 0x18001, 0x4000, CRC(114604c3) SHA1(db957783e6b16f6e1b8a831130d37fe51da84430), ROM_SKIP(1) | ROM_BIOS(0) )
	// 1: BIOS-String: 286 Modular BIOS Version 0N3.03 NFS / ELT - board is identical to #0
	ROM_SYSTEM_BIOS(1, "286suntacelt", "286 SUNTAC ELT")
	ROMX_LOAD( "286-suntac-award_a2184058lo.bin", 0x18000, 0x4000, CRC(dbf48678) SHA1(75cb7971519cf55f9bb024eed70b831af1799506), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "286-suntac-award_a2184058hi.bin", 0x18001, 0x4000, CRC(2abea425) SHA1(1183d1fc665eab11042643c4f2e0eaa0490bb3df), ROM_SKIP(1) | ROM_BIOS(1) )
	// 2: BIOS-String: DSUN-1105-043089-K0 - Keyboard-BIOS: AMI Keyboard BIOS PLUS A086031
	// CPU: Intel N80286-12 - RAM: 36x16pin/16pin, used 36xTC511000AP-80 - ISA8: 2, ISA16: 6 - BIOS: AMI 286 BIOS PLUS SA073155
	ROM_SYSTEM_BIOS(2, "sa073155", "SA073155")
	ROMX_LOAD( "286-suntac_sa073155_ic22.bin", 0x00000, 0x10000, CRC(abf6c367) SHA1(79a07b9b9af2e1963cfcae75fafc3478885237cb), ROM_SKIP(1) | ROM_BIOS(2) )
	ROMX_LOAD( "286-suntac_sa073155_ic23.bin", 0x00001, 0x10000, CRC(1d286cd9) SHA1(31be40c5008dc67cb24f4418cffaa57682e654c0), ROM_SKIP(1) | ROM_BIOS(2) )
	// 3: BIOS ROMs are marked TCI, Award 286 Modular BIOS Version 3.03HLS
	// complains about "refresh timing error, but works - BIOS release date:: 15-11-1985
	ROM_SYSTEM_BIOS(3, "tci", "TCI")
	ROMX_LOAD( "suntac_80286_lo.bin", 0x18000, 0x4000, CRC(f7bf6c49) SHA1(d8e813c264008f096006f46b90769c0927e44da9), ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "suntac_80286_hi.bin", 0x18001, 0x4000, CRC(5f382e78) SHA1(8ba222df9d7028513e37978598d8139906e8834c), ROM_SKIP(1) | ROM_BIOS(3) )
	// 4: BIOS-String: D286-5017-011388
	ROM_SYSTEM_BIOS(4, "st62m02", "ST62M-02-B")
	ROMX_LOAD( "7_st62m02-b_l.bin", 0x10000, 0x8000, CRC(fd24911f) SHA1(71ab1177d0b6b9482353e3b405f4b332cbeecfc3), ROM_SKIP(1) | ROM_BIOS(4) )
	ROMX_LOAD( "7_st62m02-b_h.bin", 0x10001, 0x8000, CRC(fca78c7b) SHA1(35e892bf52fb1cbc9bfaed7866c2ef7a31d4b762), ROM_SKIP(1) | ROM_BIOS(4) )
	// 5: BIOS-String: Phoenix ROM BIOS PLUS Version 3.10 00 - LYI-CHENG ENTERPRISE CO., LTD.
	ROM_SYSTEM_BIOS(5, "bay1000", "Bay 1000")
	ROMX_LOAD( "286-suntac-bay-1000c-ic22.bin", 0x18000, 0x4000, CRC(7f3ef79e) SHA1(d4f7086e2902d3b264b1fff76d1bea811aa58fc5), ROM_SKIP(1) | ROM_BIOS(5) )
	ROMX_LOAD( "286-suntac-bay-1000c-ic23.bin", 0x18001, 0x4000, CRC(cca972a4) SHA1(22bd712700bfacd34070e704e901623ecc37a390), ROM_SKIP(1) | ROM_BIOS(5) )
	// 6: BIOS-String: Phoenix 80286 ROM BIOS PLUS Version 3.10.22 - Personal computer 286
	ROM_SYSTEM_BIOS(6, "st2806036", "Suntac ST2806036")
	ROMX_LOAD( "286-suntac-2806036-lo.bin", 0x10000, 0x8000, CRC(907dcbf7) SHA1(8782e49926366d7a640c60b875e6c091091a2f54), ROM_SKIP(1) | ROM_BIOS(6) )
	ROMX_LOAD( "286-suntac-2806036-hi.bin", 0x10001, 0x8000, CRC(ba24e88f) SHA1(07983752c8128ae62391737f428d6db42fefdbb8), ROM_SKIP(1) | ROM_BIOS(6) )
	// 7: BIOS-String: 286 Modular BIOS Version 3.03YK2 Copyright Award Software Inc. - YOUTH KEEP ENTERPRISE CO., LTD.
	ROM_SYSTEM_BIOS(7, "youth", "Suntac Youth")
	ROMX_LOAD( "286-suntac-youth-rom1-128.bin", 0x18000, 0x4000, CRC(f232c31b) SHA1(49b9990c951a61bde10478cbb5db4b913baae1e2), ROM_SKIP(1) | ROM_BIOS(7) )
	ROMX_LOAD( "286-suntac-youth-rom3-128.bin", 0x18001, 0x4000, CRC(0735b127) SHA1(0c79cbd7d40b75dcba5fe33bf8e3a96050e12af5), ROM_SKIP(1) | ROM_BIOS(7) )
	// 8: BIOS-String: 286 Modular BIOS Version 3.01, Copyright Award Software Inc. - ECS
	// complains about Refresh Timing Error
	ROM_SYSTEM_BIOS(8, "ecs", "Suntac ECS")
	ROMX_LOAD( "st62_award_3.01_ecs_suntac_rom1.bin", 0x18000, 0x4000, CRC(17296492) SHA1(3a3bf7c20946ef56b767f54c8de45cd46d5c1167), ROM_SKIP(1) | ROM_BIOS(8) )
	ROMX_LOAD( "st62_award_3.01_ecs_suntac_rom3.bin", 0x18001, 0x4000, CRC(5f0aa2d9) SHA1(5ed5897adb4507c399f200dad9337c1c8b246a48), ROM_SKIP(1) | ROM_BIOS(8) )
	// 9: AMI 286 BIOS for MORSE Personal Computer - BIOS-String: DSUN-1216-091589-K0
	ROM_SYSTEM_BIOS(9, "stmorse", "ST-Morse")
	ROMX_LOAD( "st-morse.bio", 0x10000, 0x10000, CRC(7136e89f) SHA1(50d15f96dba855e58bb39c937ad9358fc0084b10), ROM_BIOS(9) )
	// 10:  286-BIOS (C)1987 AMI, for CDTEK - BIOS-String: DSUN-1202-043089-K0
	ROM_SYSTEM_BIOS(10, "sunami", "Suntac AMI")
	ROMX_LOAD( "suntac_ami_286_even_bios.bin", 0x10001, 0x8000, CRC(acdffb05) SHA1(180fd693bf86a6fdecc713d5873f3c0950b56c98), ROM_SKIP(1) | ROM_BIOS(10) )
	ROMX_LOAD( "suntac_ami_286_odd_bios.bin", 0x10000, 0x8000, CRC(9003d5ad) SHA1(6a2de572d11625ecdacc4ad7b5c324b160540541), ROM_SKIP(1) | ROM_BIOS(10) )
	// 11: BIOS-String: Phoenix 286 ROM BIOS PLUS Version 3.10 10 - VIP
	ROM_SYSTEM_BIOS(11, "sunphovip", "Suntac Phoenix VIP")
	ROMX_LOAD( "st-ph-l.rom", 0x10001, 0x8000, CRC(ebb6446f) SHA1(086a8f016c2c0cc56d3bd7ea4e152ae215d4e5ce), ROM_SKIP(1) | ROM_BIOS(11) )
	ROMX_LOAD( "st-ph-h.rom", 0x10000, 0x8000, CRC(1c77bd34) SHA1(0dea2dd8ba69fdfb829d152840817bcbdcc3e394), ROM_SKIP(1) | ROM_BIOS(11) )
	// 12: BIOS-String: 286-BIOS (C) AMI, for SUPERCOM - SSUN-1120-042589-K0
	ROM_SYSTEM_BIOS(12, "supercom", "Suntac Supercom")
	ROMX_LOAD( "supercom_lo.bin", 0x18000, 0x4000, CRC(6c8ce417) SHA1(fc2cdc9d23e9d75bb48d26b102873a9964871f52), ROM_SKIP(1) | ROM_BIOS(12) )
	ROMX_LOAD( "supercom_hi.bin", 0x18001, 0x4000, CRC(6c1b645d) SHA1(0def25267428338804c5858e3f536720a2b7d349), ROM_SKIP(1) | ROM_BIOS(12) )
	// 13:
	ROM_SYSTEM_BIOS(13, "suntacmr", "Suntac MR BIOS")
	ROMX_LOAD( "v000v200-1.bin", 0x10000, 0x8000, CRC(1a34d56e) SHA1(ae950de20641c6394485d891e50136b1dc5261e3), ROM_SKIP(1) | ROM_BIOS(13) )
	ROMX_LOAD( "v000b200-2.bin", 0x10001, 0x8000, CRC(2aeea8bd) SHA1(e6c306cc56dd614d704262a087dcc07b75fd9ac6), ROM_SKIP(1) | ROM_BIOS(13) )
ROM_END

// ***** 286 motherboards using the 5 chip VLSI chipset

ROM_START( vlsi5 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// 0: MG Products (Japanese) - Chipset: VLSI VL82C102A; VLSI VL82C101B; VLSI VL82C104; VLSI VL82C103A; VLSI VL82C100; (VLSI 8908BT; 8906BT; 8852BT; 8907BT; 8906BT)
	// BIOS: AMI 286 BIOS+ - BIOS-String: D286-9987-092588-K0 - ISA8: 2, ISA16: 8
	// (BIOS release date:: 25-09-1988) (ISA8: 3, ISA16: 5)
	ROM_SYSTEM_BIOS(0, "286vlsij", "Japanese 286 VLSI")
	ROMX_LOAD( "286-vlsi_japan-2-even_32k.bin", 0x10000, 0x8000, CRC(e3e64cbc) SHA1(5259e3c8686f2239a5fb0dc38aa80380ef9ec5fa), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "286-vlsi_japan-2-odd_32k.bin", 0x10001, 0x8000, CRC(aa533f39) SHA1(d88c7d4029a283b94b99e2017d29fbf9eb9105b1), ROM_SKIP(1) | ROM_BIOS(0) )
	// 1:  BIOS-String:  D286-1223-121589-K0 - 286-BIOS AMI for MBVLSI-168 - ISA8: 3, ISA16: 5
	ROM_SYSTEM_BIOS(1, "mbvlsi168", "MBVLSI-168")
	ROMX_LOAD( "286-vlsi-002350-041_32k.bin", 0x10000, 0x8000, CRC(0e0e2bc9) SHA1(0af05b15ea8141ece84fb4420e6a21720f01c7a6), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "286-vlsi-002350-042_32k.bin", 0x10001, 0x8000, CRC(5ef7b91d) SHA1(d57c7f4c8d28708f128c5f0b1251d5943c7cdf76), ROM_SKIP(1) | ROM_BIOS(1) )
ROM_END

// Uniron U3911-V3 - Chipset as above - BIOS-String: Phoenix 80286 ROM BIOS PLUS Version 3.10 00 - P/N 891012 - 80286
ROM_START( u3911v3 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "286-uniron-u13_32k.bin", 0x10001, 0x8000, CRC(c1acdf6d) SHA1(cb064dac00620588f66f850fee91ef6b47e57012), ROM_SKIP(1) )
	ROMX_LOAD( "286-uniron-u14_32k.bin", 0x10000, 0x8000, CRC(d2e9c52a) SHA1(ff6726b527b0bebed50c053a698e1b61aada3043), ROM_SKIP(1) )
ROM_END

// Toptek 286 Turbo (board name somewhat uncertain; 5x 8-bit ISA, 3x 16-bit ISA, 2 banks of onboard RAM + 2 banks expansion RAM)
// VLSI VL82C100 + VL82C101B + VL82C102A + VL82C103A + VL82C104; MC146818 or HM6818P RTC; unidentified keyboard controller
ROM_START( toptek286 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// BIOS-String: D286-1295-091589-K0
	// Original BIOS64 dump split into even and odd bytes based on available PCB info
	ROM_LOAD16_BYTE( "toptek_vlsi_even.bin", 0x10000, 0x8000, CRC(f35465e8) SHA1(c85afc2168e355120c63b68d5c11fce7770fe1b7) )
	ROM_LOAD16_BYTE( "toptek_vlsi_odd.bin", 0x10001, 0x8000, CRC(b7272729) SHA1(686c976b9b7989862846a79d00f1f9116f03bc17) )
ROM_END


// ***** 286 motherboards using the 5 chip Winbond chipset W83C201P + W83C202AP + W83C203AP + W83C204P + W83C205AP

// KT216WB5-HI Rev.2 (1 8-bit ISA, 5 16-bit ISA) - CPU: Harris CS80C286-16, FPU: 80287 - OSC: 32.000MHz, 14.31818
// Winbond W83C201P + W83C202AP + W83C203AP + W83C204P + W83C205AP; MC146818AP RTC; JETkey keyboard BIOS - RAM: 1MB DIP, 4xSIPP30
ROM_START( kt216wb5 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// BIOS-String: D286-1149-083090-K0 - WIN 286 BIOS
	ROMX_LOAD( "kt216wb5_even.bin", 0x10000, 0x8000, CRC(6b5509c0) SHA1(73b303b90cc0cd23b7e13362019193c938a2e502), ROM_SKIP(1) )
	ROMX_LOAD( "kt216wb5_odd.bin", 0x10001, 0x8000, CRC(af541ada) SHA1(26d2617dbe8c15f1b0d4782375bcb291a7923703), ROM_SKIP(1) )
ROM_END

// KMA-202F-12R - ISA16:7 - BIOS-String: 286 Modular BIOS Version 3.11
// Winbond W83C201P + W83C202AP + W83C203AP + W83C204P + W83C205AP; DS12887+ RTC; AWARD keyboard BIOS
ROM_START( kma202f )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "286_lo.bin", 0x10000, 0x8000, CRC(0ce69691) SHA1(6904ac54f30f2244058653aaa623804dd02b4332), ROM_SKIP(1) )
	ROMX_LOAD( "286_hi.bin", 0x10001, 0x8000, CRC(1330b6f2) SHA1(691bb4a51ce3d9a026ee33c3fd02fc4e13b4a184), ROM_SKIP(1) )
ROM_END


// ***** unknown chipset

// Octek XT-286 V1.1 - "Keyboard error or locked"
ROM_START( ocxt286 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "ocxt286_1", "Octek XT-286 V1.1")
	ROMX_LOAD( "xt286-328.bin", 0x1e000, 0x2000, CRC(d9caefcc) SHA1(dab3403678feb023362df614596d1306ef7f85db), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "ocxt286_2", "Hedaka HED-919")
	ROMX_LOAD( "hed919-328f.bin", 0x1e000, 0x2000, CRC(15117381) SHA1(d9dfa796edf7e94b9dacf984763ac046cf80f26d), ROM_BIOS(1))
ROM_END

//**************************************************************************
//  80286 Desktop
//**************************************************************************

// SIIG MiniSys 2000 - Motherboard ID: Labway MS101V1.2 - This is a tiny 286 system, the width of a 3.5" disk drive with an external power supply.
// A physical switch allows to change between VGA and composite video output.
// CPU: Intel 286-12 - BIOS: AMIBIOS - BIOS string: DH12-1422-061390-K0 - Chipset: Headland HT12/A, Acer M5105 - RAM: 2xSIMM30
// OSC: 14.31818MHz, 24.000MHz, 24.000, another unreadable - ISA16: 1 (on riser board) - on board: IDE, Floppy, 2xser, par, game, composite, VGA,
// ext. FDD (motherboard and I/O board connected with a riser board - Mass storage: 3.5" FDD, 3.5" HDD (e.g. Seagate ST351A/X in AT mode)
ROM_START( minisys2k )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "minisys2000_even.bin", 0x10001, 0x8000, CRC(a4c3eade) SHA1(ea6d19fa12994882f8a77f67c2c358bba57abe28), ROM_SKIP(1) )
	ROMX_LOAD( "minisys2000_odd.bin", 0x10000, 0x8000, CRC(0e904497) SHA1(a55de8fdaf0442cc7c640dfc88daa37c851fd324), ROM_SKIP(1) )
ROM_END


// ICL DRS M40 (motherboard: ICL M40/M45/915V)
// Chipset: Chips and Technologies NEAT, WD37C65C-PL - CPU: - FPU socket provided - BIOS: Acer (made by Phoenix) - Keyboard BIOS: KBC V4 82HV
// OSC: 10.000000MHz, 16.000MHz, 25.175999MHz, 32.000000MHz, 14.31818 - RAM: DIP 640KB, 4xSIMM30, 2xSIMM/SIPP30 no sockets - VGA on board:  PVGA1A-JK, 256KB RAM
// no regular ISA slots, ISA8: 1, ISA16: 1 on a riser card - On board: IDE, floppy, par, 2xser
ROM_START( icldrsm40 ) // no POST, halts
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "icl_m40_l_bin", 0x10000, 0x8000, CRC(1b493570) SHA1(2a3cee0e7a45f07439c54970513e85c9134fee32), ROM_SKIP(1) )
	ROMX_LOAD( "icl_m40_h.bin", 0x10001, 0x8000, CRC(451421af) SHA1(82d3c94cf04df1b48540fbb3c4d9ad4d6eac8823), ROM_SKIP(1) )

	ROM_REGION( 0x8000, "vga", 0) // WDC WD90C11-LR VGA BIOS
	ROMX_LOAD( "icl_m40_vga_bios_l.bin", 0x0000, 0x4000, CRC(522c5c02) SHA1(37f8299a0dcf6b028e1012313ae787bc389ed1f2), ROM_SKIP(1) )
	ROMX_LOAD( "icl_m40_vga_bios_h.bin", 0x0001, 0x4000, CRC(ed29de22) SHA1(80a508a42dc731fc33584ba2da9e478c401e5d47), ROM_SKIP(1) )
ROM_END

// Twinhead PS-286V Rev 0.1 (used in Twinhead Netstation PC) - Chipset: Twinhead TH4100, TH6260, Zilog Z0765A08PSG FDC, 2x16C450PC,
// BIOS: Phoenix NEAT - BIOS Version: 3.1003D 3462421, Keyboard BIOS: Phoenix - CPU: Intel N80286-12, FPU socket provided
// RAM: 8xiT21014-08, 8xSIMM30 - ISA16: 1 - On board: WD VGA, floppy, ide, 2xser, par - Video: WD90C11-LR, IMSG176J-50Z, Twinhead VGA BIOS V1.20, RAM: 2xIntel T21014-08
// OSC: 14.31818, 24.000MHz - DIP8: on-off-off-off-on-on-on-on
ROM_START( twinnet ) // BIOS-String: Phoenix ROM BIOS PLUS Version 3.10 03 - Twinhead International Corporation
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "ps-286v_lo.bin", 0x10000, 0x8000, CRC(71920f1b) SHA1(f6d76d10b17df7488c5c70a912403dd45f0afbc3), ROM_SKIP(1) )
	ROMX_LOAD( "ps-286v_hi.bin", 0x10001, 0x8000, CRC(d79495e3) SHA1(e5d53ae7059502b2259d575ca8e8fdff7f712389), ROM_SKIP(1) )

	ROM_REGION( 0x8000, "vga", 0) // WDC WD90C11-LR VGA BIOS
	ROM_LOAD( "wdc_vga.bin", 0x0000, 0x8000, CRC(f897048e) SHA1(3baeb553dae4f1c641fb01a16bfe4ae3ca95b13d))
ROM_END

// Zenith Z-248 - Motherboard: 85-3379-01 CPU BOARD 113087 - CPU: AMD N80L286-12/C, FPU socket provided - Chipset: Zymos Poach 1 and 2
// RAM: 6xSIMM30 - OSC: 24.000MHz, 16.000MHz, 14.31818MHz - ISA8: 2, ISA16: 5 - on board diagnostic LEDs: red: CPU D101, ROM D102, RAM D103, INT D104, DSK D105, RDY D106, green: DCOK D107
// Cards in system documented: DTC 5280 CRA MFM HD controller, Graphics card HEATH P/N150-307-3 L1A2334/Chips P82C434A (undumped), Logitech mouse/hand scanner controller, GW302 Parallel Printer Card, Chips P82C605 Dual Serial Printer Card
// "+++ ERROR: Fatal Slushware RAM Error +++" / "--- Fatal Error: Cannot Continue! ---" (cf. olyport40)
ROM_START( zdz248 ) // use CGA
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "zenith_z-248.bin", 0x10000, 0x10000, CRC(e2042dd9) SHA1(bd51cc74b0b7bd42c449bc4b5702274f766e8ea5))
ROM_END

// Samsung Deskmaster 286-12, Microfive motherboard - Chipset: Chips F82C451C, F82C235-12 286 SCAT - CPU: Intel 80286-12, FPU socket provided
// RAM: 6xSIMM40 - BIOS: Phoenix ROM BIOS PLUS 3.10 02M, Chips and Technologies VGA 411 BIOS v211 (one 27C512 EPROM) - Keyboard-BIOS: Intel P8942AN - ISA16: 1
// OSC: 14.31818, 24.000000MHz, 24.000000MHz, 25.175000MHz, 40.000000MHz - on board: FDD, IDE, beeper, keyboard, mouse, ser, par - VGA Video RAM: 8x41C464J-10 (256K)
ROM_START( samdm286 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "samsung-deskmaster-28612-rom.bin", 0x10000, 0x10000, CRC(785d3196) SHA1(214a933d8fa86bfdb633fb5e8595a18a58cdba7d))
ROM_END

// Schneider EuroAT - Uses the same case as the Schneider EuroXT, a compact desktop with room for a single floppy drive and an AT IDE harddisk (Seagate ST-142A, ST-157A)
// Mainboard: Baugr.Nr. 51513 with internal EGA, 52591 EGA components omitted (see: EURO VGA)
// Chipset: 2xHeadland GC102-PC, HT101A/B1A4924, Schneider BIGJIM 30773 (cf. EuroPC 2/EuroXT), WD37C65BJM, Siemens SAB 16C450-N
// EGA chipset (mainboard 51513): G2 GC201-PC, 64K RAM - Main RAM: 1MB
// CPU: Siemens SAB 80286-12, Keyboard-BIOS: Schneider ROM BIOS 1985, 1989 Phoenix
// Connectors: Keyboard, Printer, Serial, Floppy (can use the same external floppy disk drives as the EuroXT), EGA monitor
// OSC: 34.0000, 19.2000MHz, 24.0000, 16.000MHz
// BUS: proprietary connectors, ISA riser (ISA8x1, ISA16x1), BIOS can set CPU and BUS speed up to 12MHz
// Proprietary EURO VGA card: 256KB RAM, ATI 18800-1 chipset
// blank screen, beeps 1-2-4
ROM_START( euroat )
	ROM_REGION16_LE( 0x20000, "bios", 0 )
	ROM_SYSTEM_BIOS( 0, "v201", "V2.01" ) // also used on Tower AT
	ROMX_LOAD( "euro_at_v201a_l.bin", 0x10000, 0x8000, CRC(0f8a2688) SHA1(95db9010b1c0465f878e5036bcf242ddf0a3be6a), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "euro_at_v201a_h.bin", 0x10001, 0x8000, CRC(75b6771b) SHA1(3aa0921914ea6e24249ce3f995fdcb341124d7e9), ROM_SKIP(1) | ROM_BIOS(0) )
	// EGA ROM dump missing

	ROM_SYSTEM_BIOS( 1, "v203", "V2.03" )
	ROMX_LOAD( "80286at_bioslow_schneider_ag_id50444_v2.03.bin", 0x10000, 0x8000, CRC(de356110) SHA1(6bed59a756afa0b6181187d202b325e35afadd55), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "80286at_bioshigh_schneider_ag_id50445_v2.03.bin", 0x10001, 0x8000, CRC(c4c9c840) SHA1(09deaa659191075b6ccc58403979d61bdf990dcd), ROM_SKIP(1) | ROM_BIOS(1) )

	ROM_REGION( 0x10000, "vga", 0 )
	ROM_LOAD( "euro-vga_52255_bios_v1.02_row.bin", 0x00000, 0x10000, CRC(71d42e58) SHA1(be64990325f52128e102dfc3ed87d2d831183ddc))
ROM_END

// Schneider Tower AT 220 (other designations for the 10 MHz 80826, 512KB RAM series are 201, 202, 240), the last two digits are related to the originally installed
// number of 3.5" 720K floppy drives or the size of the MFM harddisk), Model 260 has a 60MB harddisk and can have a 12.5 MHz CPU (depending on where you look and
// probably what was available in Schneider's part bin), systems with a "mega" in the name have 1MB RAM and 1.44MB floppy drives. All have an EGA graphics card on board
// The case looks like a stack of three thinner slices, and extra modules were available that clamped on: a tape streamer, and a 5.25" 1.2MB or a 360KB drive. They were
// connected to the "External drive" port of the Tower AT, much like with Schneider's other PCs. The mainboard as such is divided between the I/O and video portion that resides on
// the backplane board and the CPU and RAM on the CPU card that also contains the keyboard connector.
// Model 220, Schneider Tower-EGA I/O: Chipset: JIM 50101-1 (cf. EuroPC), WD37C65BJM, Gemini VC-001, VLSI VL16C450-PC, Paradise Systems Inc PPC1 38302C
// 104 pin CPU card connector (ISA without the key), 3xISA16, 1xISA8 - on board: parallel, serial, bus mouse (Atari compatible), EGA, internal floppy (26pin), external floppy (DB25)
// Model 220, Schneider Tower-CPU 286 (Baugr.Nr. 50229 Rev.3B): Dallas DS1287, MBL8042H (Compatibility Software 1986/K Phoenix Technologies Ltd - 805931) - Chipset: 2x G2 GC102, G2 GC101
// OSC: 20.000MHz, 14.318180, beeper, CPU: AMD N80L286-10/S, FPU socket provided - RAM: solder pads for 4xSIMM30, 4x16pin (empty), 4x or 8x51C4256 (512KB or 1MB)
// The Tower AT was available with the Schneider VGA I/O that is described in the tower386sx section. The Tower VGA System 40 and System 70 models had the 12.5MHz CPU card.
// Its BIOS version 2.03 is undumped so far.
// blank screen, beeps 1-2-4
ROM_START( towerat2xx )
	ROM_REGION16_LE( 0x20000, "bios", 0 )
	ROM_SYSTEM_BIOS(0, "v2.02", "V2.02" ) // from a model 220
	ROM_SYSTEM_BIOS(1, "v2.01", "V2.01" )
	ROM_SYSTEM_BIOS(2, "v1.07", "V1.07" ) // seen on a model 240 "mega"
	ROM_SYSTEM_BIOS(3, "v1.06", "V1.06" ) // from a model 220
	ROM_SYSTEM_BIOS(4, "v1.05a", "V1.05a" )
	ROM_SYSTEM_BIOS(5, "v1.01a", "V1.01a" ) // from a model 220

	ROMX_LOAD( "phoenix_860376_schneider_ag_tower_at_bios_0_id.nr.50445_v2.02.bin", 0x10000, 0x8000, CRC(8566b3f2) SHA1(a12b5e9e848de123c62374f78ee1d2b4b53dd468), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "phoenix_860376_schneider_ag_tower_at_bios_1_id.nr.50445_v2.02.bin", 0x10001, 0x8000, CRC(7d8249cf) SHA1(d894332aad4c26798e6b41a5e94c471b0235bd50), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "schneider_tower_at_bios_0_low_v2.01a.bin", 0x10000, 0x8000, CRC(0f8a2688) SHA1(95db9010b1c0465f878e5036bcf242ddf0a3be6a), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "schneider_tower_at_bios_1_high_v2.01a.bin", 0x10001, 0x8000, CRC(75b6771b) SHA1(3aa0921914ea6e24249ce3f995fdcb341124d7e9), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "schneider_tower_at_bios_0_low_v1.07.bin", 0x10000, 0x8000, CRC(70a9421d) SHA1(bf6529f259d5bc7c28df19655c57ecce1c57260f), ROM_SKIP(1) | ROM_BIOS(2) )
	ROMX_LOAD( "schneider_tower_at_bios_1_high_v1.07.bin", 0x10001, 0x8000, CRC(995a62db) SHA1(42e9a866b5f02509d3094c42842eafed1d577f4e), ROM_SKIP(1) | ROM_BIOS(2) )
	ROMX_LOAD( "schneider_ag_50444_v1.06.u3", 0x10000, 0x8000, CRC(42891d5a) SHA1(d94292b14f9155b4e05c78960f9722fffca976be), ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "schneider_ag_50445_v1.06.u4", 0x10001, 0x8000, CRC(bdced2b9) SHA1(cba58c70420695ec69dbb4817d0c6b14b8bdbadd), ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "schneider_tower_at_bios_low_v1.05a.bin", 0x10000, 0x8000, CRC(94ad1628) SHA1(bf7319ed9b37a57e67b0b4bf7845d95d0f593d68), ROM_SKIP(1) | ROM_BIOS(4) )
	ROMX_LOAD( "schneider_tower_at_bios_high_v1.05a.bin", 0x10001, 0x8000, CRC(f3d48773) SHA1(9386313b6d05acb30e7ba7e1353c259deaaa77bc), ROM_SKIP(1) | ROM_BIOS(4) )
	ROMX_LOAD( "schneider_tower_at_bios_low_v1.01.bin", 0x10000, 0x8000, CRC(a94ca070) SHA1(2acca0601c00e76d510c81dfe92d33397fbeccd1), ROM_SKIP(1) | ROM_BIOS(5) )
	ROMX_LOAD( "schneider_tower_at_bios_high_v1.01.bin", 0x10001, 0x8000, CRC(d8f67320) SHA1(3ddf7fdb1370f745c4f1902101605477ee0bb392), ROM_SKIP(1) | ROM_BIOS(5) )

	// todo: find matching EGA ROMs for BIOS V2.01, 1.07 and 1.05
	ROM_REGION( 0x8000, "gfx", 0)
	ROMX_LOAD( "schneider_ag_tower_ega-bios_50477_v1.04.bin", 0x0000, 0x8000, CRC(aabd1017) SHA1(e019c21d6108a0387f7c98e92e4dbc32ab19929f), ROM_BIOS(0) ) // R1.04 matched with system BIOS V2.02
	ROMX_LOAD( "schneider_ag_tower_ega-bios_50477_v1.04.bin", 0x0000, 0x8000, CRC(aabd1017) SHA1(e019c21d6108a0387f7c98e92e4dbc32ab19929f), ROM_BIOS(1) )
	ROMX_LOAD( "schneider_ag_tower_ega_bios_id.nr._50447_r1.02.bin", 0x0000, 0x8000, CRC(1c43aaf6) SHA1(cf98dd8f0d8258761e36e70f086b1234ec703823), ROM_BIOS(2) )
	ROMX_LOAD( "schneider_ag_tower_ega_bios_id.nr._50447_r1.02.bin", 0x0000, 0x8000, CRC(1c43aaf6) SHA1(cf98dd8f0d8258761e36e70f086b1234ec703823), ROM_BIOS(3) ) // R1.02 matched with system BIOS V1.06
	ROMX_LOAD( "schneider_ag_tower_ega_bios_id.nr._50447_r1.02.bin", 0x0000, 0x8000, CRC(1c43aaf6) SHA1(cf98dd8f0d8258761e36e70f086b1234ec703823), ROM_BIOS(4) )
	ROMX_LOAD( "schneider_ega_r1.00.bin", 0x0000, 0x8000, CRC(4e14cb0a) SHA1(6cef69274a52b11201a3477631fa343a7e1a5970), ROM_BIOS(5) ) // R1.00 matched with system BIOS V1.01 */
ROM_END


// Victor V286C - a VGA version exists as well
// CPU: AMD 802L86-10/S  - one ISA16 extended to ISA8: 1, ISA16: 3 on a riser card - Keyboard-BIOS: AT-KB M5L8042
// Chipset: Kyocera AT-S.C.1 VER.A 9771A 89432EAI, Kyocera EAST-2A 9850 8938EAI, MB621103 M AT-1A 8944 Z67, MB622436 MAT-2E 8943 W02
// On board video EGA/CGA/Hercules based on a L1A2919 PARADISE PBI-38306A controller
// RAM: 640KB on board -  OSC: 48.000, 32.0000MHz, 40MHz, 18.63636MHz
// Options: Sockets for a Microsoft 900110003 Bus Mouse controller, FPU 80287 and a DS1287 RTC - 2 DIP switches with 12 positions combined
// Mass storage: 1.2MB floppy, Kyocera KC-30A harddisk on a DTC5187CRH RLL controller
ROM_START( v286c )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "system-bios_kyocera_a2-2_lo.bin", 0x18000, 0x4000, CRC(160c4759) SHA1(937c1bb9483efeba895e038b7132e0e7e5a9aaa4) )
	ROM_LOAD16_BYTE( "system-bios_kyocera_a2-2_hi.bin", 0x18001, 0x4000, CRC(cfe0cbef) SHA1(2610d727d13fa67c7bd9b3545d7846e880c3da37) )

	ROM_REGION(0x4000, "pega", 0)
	ROM_LOAD( "ega-bios_paradise_video-1.bin", 0x0000, 0x4000, CRC(2db77b0b) SHA1(d31ddbbde5be0b0603e9f569c3f924e0afc7c8e4) )
ROM_END

// Atari PC 4, PC4X motherboard - Chipset: NEAT CS8221 (P82C206, P82C211, P82C212, P82C215) - ISA8: 1, ISA16: 4 - RAM: 8XSIPP30
// Paradise Systems PVGA1A-JK, IMSG171P
// on board: external and internal floppy, digital and analog video, ser, par, keyboard, mouse
ROM_START( ataripc4 ) // initializes video, then halts
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "ami_pc4x_1.7_even.bin", 0x10000, 0x8000, CRC(9f142377) SHA1(b3e5c5dfaec133646295d9a16bc1eec54fe2bc35))
	ROM_LOAD16_BYTE( "ami_pc4x_1.7_odd.bin", 0x10001, 0x8000, CRC(ae3d4cb6) SHA1(d7915ef013462aff4f189cda8f6dc0a486777b63))

	ROM_REGION16_LE(0x10000, "pvga", 0)
	ROM_LOAD16_BYTE( "pvga_pc4x_even.bin", 0x00000, 0x8000, CRC(ff222896) SHA1(e22cdcd9c69fc4feef6b8c2903e3506c79ff531b))
	ROM_LOAD16_BYTE( "pvga_pc4x_odd.bin", 0x00001, 0x8000, CRC(8ca04b2f) SHA1(7705d866ecf366bd6ea95071bf5767877461d2d5))
ROM_END

// Atari ABC 286/30, PC4LC motherboard (low cost, compared to PC4) - BIOS-String: DNET-0000-092588-K0- ISA16: 3 - RAM: 4xSIPP30
// NEAT CS8221 (P82C206, P82C211, P82C212, P82C215) - http://www.atari-computermuseum.de/abc286.htm
// on board: internal floppy, digital video (EGA), ser, par, keyboard, mouse
ROM_START( atariabc286 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "ami_pc4x_1.x_even.bin", 0x10000, 0x8000, CRC(930447c7) SHA1(fb7212b413ff8aa056bd23baadc22691ce714f8d))
	ROM_LOAD16_BYTE( "ami_pc4x_1.x_odd.bin", 0x10001, 0x8000, CRC(0891fd25) SHA1(4722b1db1b2c985c67f9a9b807ce68c06a905232))

	// ROM_REGION(0x40000, "ega", 0)
	// ROM_LOAD( "p82c441_ega_bios_v1.0.6.bin", 0x00000, 0x08001, BAD_DUMP CRC(80c11ef2) SHA1(90852d3cbb64504c8d57b469a594c22c247c9a39))
ROM_END

// Profex PC 33 - Chipset: SUNTAC ST62BC004-B1, ST62BC003-B, ST62C008, ST62C005-B, ST62BC001-B, ST72BC002-B
// RAM: 2xSIPP30, 18x18pin/16pin, 8x20pin, 4x16pin - OSC: 12.000, 14.31818, 24.000MHz - ISA8: 2, ISA16: 6
// BIOS: Award A2245250 - Keyboard-BIOS: Award - BIOS-String: 286 Modular BIOS 3.03 Copyright Award Software Inc. / GCH
ROM_START( profpc33 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "st62-pro.bio", 0x10000, 0x10000, CRC(a42f9d0e) SHA1(384f4ddaf92307a5eeb70646a85ad991d904c2d2))
ROM_END

// Epson PC AX / Equity III+ - 102-System Board Error (according to the technical manual this means
// "TlMER SPEED CHECK: An error was detected in timer controller counter 0."
ROM_START( epsax )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "evax.bin", 0x18000, 0x4000, CRC(5f9e5fc9) SHA1(5dbe414b762494fa65df6ac0391b2281f452f3e9))
	ROM_LOAD16_BYTE( "odax.bin", 0x18001, 0x4000, CRC(e9cba352) SHA1(22be1457332a62ae789e779e9666ffd63c91d010))
ROM_END

// Epson PC AX2e
ROM_START( epsax2e ) // see epsax
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "evaxe.bin", 0x10000, 0x8000, CRC(1251f3e9) SHA1(52456943ee4d83c2a3d46e75d292f4fb57a5a2d8))
	ROM_LOAD16_BYTE( "odaxe.bin", 0x10001, 0x8000, CRC(ce08f140) SHA1(1c6e62f2ab45e9574691224dd6a6ab2a823e85cc))
ROM_END

// Bull Micral 45 - Chipset: VLSI 8842AV / R2622 / VC2730-0001, VLSI 8832VB / L81711 / VL16C452-QC, MBL8042N, MC146818AP, ???, 900110003 V1.1 1986 MICROSOFT, FDC9268, NCR 53C80,
// CPU: 286, FPU socket proided, 12MHz/8MHz RAM: 6xSIMM30 (1.152MB - 6MB) - ROM: 64KB, 16KB Video BIOS - On board video: Paradise PEGA2A, 256KB video RAM
// On board: Floppy (2xint, 2xext), SCSI, par, ser, CP8, Microsoft Inport - OSC: 16.257MHz, 48MHz, 14.318180MHz - ISA16: 1, riser card with 1xISA8 and 2xISA16
ROM_START( micral45 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "vu12", "Version U1.2")
	ROMX_LOAD( "bm45_u1.2_p665.bin", 0x10000, 0x8000, CRC(046ab44a) SHA1(06e44b0bd8ae77c12319e11f629338651d53141d), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "bm45_u1.2_p664.bin", 0x10001, 0x8000, CRC(5729c972) SHA1(40b2dbc53829384e54cf953ed8b39e5d424bbff2), ROM_SKIP(1) | ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "vu20", "Versio U2.0")
	ROMX_LOAD( "even.fil", 0x10000, 0x8000, CRC(438a7b36) SHA1(b5c9a71cfd7e87cc91453a73f17e93527c5ac7ac),  ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "odd.fil", 0x10001, 0x8000, CRC(9decd446) SHA1(dcbd305f065382f5327296391da388c50bb1b734),  ROM_SKIP(1) | ROM_BIOS(1) )
ROM_END

// Wang PC-250/16
// Phoenix 80286 ROM BIOS PLUS Version 3.10 07 / ROMBIOS Version 03.13.00 (c) Copyright Wang Laboratories, Inc. 1991
ROM_START( wpc250 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "wang_pc_250-16_bios_vers_03.13.00_chip_l47_9514rol_lo.bin", 0x10000, 0x8000, CRC(8f3a3061) SHA1(42b13f662f1f0b00748e21b4aa60cfcbc4b098c0))
	ROM_LOAD16_BYTE( "wang_pc_250-16_bios_vers_03.13.00_chip_l46_9514roh_hi.bin", 0x10001, 0x8000, CRC(3ba0cb84) SHA1(09111f9a6672fad58b11dc1f22240c78521bcc1c))

	// Wang PC-250/16 graphics card using a Chips F82C452
	ROM_REGION(0x8000, "gfx1", 0)
	ROM_LOAD( "wang_3050_bios_rom.bin", 0x0000, 0x8000, CRC(a895ad45) SHA1(afeacfffdf32f225c604d28580327e9ecfa96ea5))
ROM_END

// Philips PCD204 (PCD200 series)
// Chipset: Paradise PVGA1A-JK, Faraday FE3031-JK - BIOS: M1212 U66/U67 R1.00.01 CKS:056B/617D - Keyboard-BIOS: Phoenix 1072217
// BIOS-String: - - On board: Floppy, 1xIDE, VGA, Parallel, Serial - Slot for ISA slot adapter: 1 - HD: Maxtor 7060AT (C/H/S: 1024/7/17)
// OSC: 30.000MHz, 16.000MHz, 1.8432MHz, 28.3220MHz, 25.1750MHz, 10.000, 42.000MHz, 25.000MHz - CPU: Intel 80286-12
ROM_START( pcd204 ) // => emulation runs into hlt
	ROM_REGION16_LE(0x20000,"bios", 0)
	ROM_LOAD16_BYTE( "u66_mi212_r1.00.01.bin", 0x00001, 0x10000, CRC(e99f817a) SHA1(5cf8556fa4ef5c314d5450756c042f5e3cde09b4) )
	ROM_LOAD16_BYTE( "u67_mi212_r1.00.01.bin", 0x00000, 0x10000, CRC(d879f99f) SHA1(04c09b46c4a67701257f819d66002b8e93f0a391) )
ROM_END

// Leanord Elan High Tech 286 - Octek VGA-16 using Chips F82C451
// Chipset: Chips, passive backplane and slot CPU
// complains about "0000-55AA - Error Base RAM (64Kb) - Halt*"
ROM_START( elanht286 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "elan_s_ leanord_rom-bios_286_v3.50.bin", 0x10000, 0x10000, CRC(53dc0965) SHA1(13f352ee9eda008d8ddcc7ed06325dd2513ad378) )
ROM_END

// Kaypro 286i
ROM_START( k286i )
	ROM_REGION16_LE(0x20000,"bios", 0)
	ROM_LOAD16_BYTE( "81_1598", 0x18000, 0x4000, CRC(e25a1e43) SHA1(d00b976ac94323f3867b1c256e315839c906dd5a) )
	ROM_LOAD16_BYTE( "81_1599", 0x18001, 0x4000, CRC(08e2a17b) SHA1(a86ef116e82eb9240e60b52f76e5e510cdd393fd) )
ROM_END

// Sanyo MBC-28
ROM_START( mbc28 )
	ROM_REGION16_LE(0x20000,"bios", 0)
	ROM_LOAD16_BYTE( "mbc-28_sl-dt_ver.1620_low_din_checksum,454f00,27c256-15.bin", 0x10000, 0x8000, CRC(423b4693) SHA1(08e877baa59ebd9a1817dcdd27138c638edcbb84) )
	ROM_LOAD16_BYTE( "mbc-28_sl-dt_ver.1620_high_din_checksum,45ae00,27c256-15.bin", 0x10001, 0x8000, CRC(557b7346) SHA1(c0dca88627f8451211172441fefb4020839fb87f) )
ROM_END

// Siemens PCD-2 - Harddisk: NEC D5126
// CPU card W26361-D458-Z4-06-05, Piggyback MFM controller with WDC WD42C22A-JU PROTO chip W26361-D477-Z2-04-05
// Chips: Intel N82230-2, Intel N82231-2, WDC WD37C65BJM, VLSI 8831AM/X12012/VL16C452-QC
// VGA card: S26361-D463 GS3 using a Video Seven 458-0023
	// ROM_LOAD( "vga_nmc27c256q_435-0029-04_1988_video7_arrow.bin", 0x8000, 0x8000, CRC(0d8d7dff) SHA1(cb5b2ab78d480ec3164d16c9c75f1449fa81a0e7) ) // Video7 VGA card
	// ROM_LOAD( "vga_nmc27c256q_435-0030-04_1988_video7_arrow.bin", 0x8000, 0x4000, CRC(0935c003) SHA1(35ac571818f616b856da8bbf6a7a9172f68b3ab6) )
ROM_START( pcd2 )
	ROM_REGION16_LE(0x20000,"bios", 0)
	ROM_LOAD16_BYTE( "bios_tandon_188782-032a_rev_5.21_low.bin", 0x10000, 0x8000, CRC(a8fbffd3) SHA1(8a3ad5bc7f86ff984be10a8b1ae4542be4c80e5f) )
	ROM_LOAD16_BYTE( "bios_tandon_188782-031a_rev_5.21_high.bin", 0x10001, 0x8000, CRC(8d7dfdcc) SHA1(d1d58c0ad7db60399f9a93db48feb10e44ffd624) )

	ROM_REGION( 0x0800, "keyboard", 0 ) // reporting keyboard controller failure
	ROM_LOAD( "kbd_8742_award_upi_1.61_rev_1.01.bin", 0x000, 0x800, CRC(bb8a1979) SHA1(43d35ecf76e5e8d5ddf6c32b0f6f628a7542d6e4) ) // 8742 keyboard controller
ROM_END

// Siemens PCD-2M/-2L - Board : CPUAZ-S26361-D458-V30 + W26361-D458-Z4-09-05 (ISA16 slot CPU) - Chipset : Intel/Zymos N82230-2, Intel/Zymos N82231-2, VLSI VL16C452-QC
// BIOS : Copyright (C) 1985 Tandon Corporation, Al Rights Reserved. BIOS Version 5.2 - Keyboard BIOS: Award: UPI 1.61 REV 1.01 - CPU: Siemens SAB 80286-12-N/S, FPU socket provided
// RAM: 4xSIMM30 - DIP8: EN0, EN1, EN2, 256, 640, 1MB, MAP, COL - OSC: 90.0000, 24.0000, 14.31818, 16.0000 - On board: Floppy, 2xpar, 2xser, ISA16 piggyback connector
ROM_START( pcd2m ) // constant beeps, doesn't work
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "tandon188782-032a_rev_5.23_low.bin", 0x10000, 0x8000, CRC(1c922f7a) SHA1(f405ce0bf29c8e86efda964308e8f58f7ef0e5ca) )
	ROM_LOAD16_BYTE( "tandon_188782-031a_rev_5.23_high.bin", 0x10001, 0x8000, CRC(ee31d405) SHA1(2630d73ddd55e82857c5ff4547d69ad7f5d5d1ca) )
ROM_END

// Compaq SLT/286 - complains about "102 - System board failure" - CPU: Harris CS80C286-12
// Chips: Dallas DS1287, Compaq 109778-001/4758, Bt478KPJ35, S8852C4/DP8473V, Fujitsu 8904 Q16/109445-001, Fujitsu 8850 W00/109444-002, Compaq 19034/8846KK/10452-002
// NS16C450V, Fujitsu 8850 W73/110110-001
ROM_START( comslt286 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "compaq_slt286-bios-revision_j.2-even.bin", 0x10000, 0x8000, CRC(77e894e0) SHA1(e935e62e203ec67eaab198c15a36cc0078fd35b0))
	ROM_LOAD16_BYTE( "compaq_slt286-bios-revision_j.2-odd.bin", 0x10001, 0x8000, CRC(4a0febac) SHA1(7da5ac4bc50f25063a1d1e382b8cff9b297976f8))
ROM_END

// Dell System 200 - complains about "memory overlap at 400000" but seems to work otherwise
ROM_START( dsys200 )
	ROM_REGION16_LE( 0x20000, "bios", 0 )
	ROM_LOAD16_BYTE( "version_3.10_a12_even.bin", 0x10000, 0x8000, CRC(5aa81939) SHA1(d9029d3708c49e72f57ae2a340429c28ec39acab))
	ROM_LOAD16_BYTE( "version_3.10_a12_odd.bin", 0x10001, 0x8000, CRC(942416cb) SHA1(b321704471e159030af82556ff25ac46c27a807e))

	ROM_REGION( 0x0800, "keyboard", 0 )
	ROM_LOAD( "20575_b47-00.bin", 0x000, 0x0800, CRC(148187db) SHA1(0d7542dd0b2bc3d6724ae3618a8543cb84a30e92) )
ROM_END

//  NCR, probably PC-8 - should get a "NGA" extended CGA graphics card once it's emulated
ROM_START( ncrpc8 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "pc8main", "NCR PC-8 mainboard") // large full size AT mainboard - Setup Version 2.3
	ROMX_LOAD( "ncr_35117_u127_vers.4-2.bin", 0x10000, 0x8000, CRC(f4338669) SHA1(c1d6e714591c8d7ab966acfdbc3b463e06fbd073), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "ncr_35116_u113_vers.4-2.bin", 0x10001, 0x8000, CRC(b1b6a2e2) SHA1(5b2c0a2be59e064076ed757d84f61bf955ceca08), ROM_SKIP(1) | ROM_BIOS(0))
	// Chips: NCR 006-3500404, NCR 006-3500447D, NCR 006-3500402PT, M5L8042-235P, SN76LS612N
	ROM_SYSTEM_BIOS(1, "pc8card", "NCR PC-8 CPU card") // passive backplane and CPU card - Setup Version 2.1
	ROMX_LOAD( "ncr_u127-30_v.4.bin", 0x10000, 0x8000, CRC(33121525) SHA1(11f8d8af4dad432f558c646d7d0ff23eb642a815), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "ncr_u113-27_v.4.bin", 0x10001, 0x8000, CRC(87424492) SHA1(5b7aba5678fe55c81fee2e07730b8ae03a23160f), ROM_SKIP(1) | ROM_BIOS(1))
	ROM_REGION( 0x0800, "keyboard", 0 )
	ROM_LOAD ("ncr_keyboard_mcu_35091.bin", 0x0000, 0x800, CRC(632556cc) SHA1(b35f30bd0664fc1c2775a594f248d1e30237900a))
ROM_END

// NCR Class 3302 - CPU: AMD N80L286-12/S, FPU socket provided - Chipset: Chips & Technologies NEAT (82C206, 82C211, 82C212, 82C215), VLSI VL16C452-QC, INMOS IMSG176J-50Z
// Motherboard: PN-386XV REV R4.B - RAM: SIMM30x4, On board: 8x4C4256DJ-10, 4x41C256-10 - BIOS: NCR 3.5 - Keyboard BIOS: M5L8042-277P
// OSC: 24.000MHz, 32.000MHz, 36.000MHz, 1.8432MHz, 25.175/28.322, 14.31818, ISA16: 1 on board, used for a riser with 2 slots
ROM_START( ncr3302 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "f000-flex_drive_test.bin", 0x10000, 0x8000, CRC(09c9eb6b) SHA1(5eb00f65659cee018726e7a4122da1c42b2bbef9))
	ROM_LOAD( "f800-setup_ncr3.5-013190.bin", 0x18000, 0x8000, CRC(31e6a1ba) SHA1(2ff7dc233d167775ec3641c7a4b2d891db5f8ba7))

	// on board Paradise VGA PVGA1A-JK
	// DIP switches (x8 near the Paradise PVGA1A-JK) are undocumented. Setting switch 7 to 'open' generates VGA compatible (yet monochrome) signal, closing switch 7
	// causes 'out of range' on a fixed frequency VGA LCD - Graphics RAM: 8xD6164, 8 empty sockets (18 pin) provided
	ROM_REGION(0x8000, "video", 0)
	ROM_LOAD( "c000-wd_1987-1989-740011-003058-019c.bin", 0x0000, 0x8000, CRC(658da782) SHA1(6addcf24795c2e8004c21a8e546b53de41766420))
ROM_END

// Nixdorf 8810 M30
// Chipset: Chips P82C211-12 P82C215, P82C212B-12, Zilog Z0853006VSC, L5A0757/NC-LSI56A-SCC1, Chips P82C604A, P82C206 H1
ROM_START( n8810m30 )
	ROM_REGION16_LE(0x20000, "bios", 0 )
	ROM_LOAD( "at286bios_53889.00.0.17jr.bin", 0x00000, 0x20000, CRC(74870212) SHA1(adb3f379c9aeee6a5beb946d23af6eea706aca1d) )
ROM_END

// Nixdorf 8810 M55 - Paradise PEGA 1A383048 piggybacked onto MFM/Floppy controller card
// Chips: M5L8042-235P, NCR 006-3500402PT, 2xAMD AM9517A-5JC, NCR 006-3500447 D, NCR 006-3500404
ROM_START( n8810m55 )
	ROM_REGION16_LE(0x20000, "bios", 0 )
	ROM_LOAD16_BYTE( "150-3872_u113_27_4.5.1.bin", 0x10001, 0x8000, CRC(35ff4fba) SHA1(557f0f98c27af76f6fa6990592e7150f5fc1fc02))
	ROM_LOAD16_BYTE( "150-3873_u127_30_4.5.1.bin", 0x10000, 0x8000, CRC(5a7e6643) SHA1(f3890919a772eead7232bd227b2c8677377f6e24))
ROM_END

// Olivetti M250E aka DECStation 220 - PVGA1A on board with the ROM included in the main ROM - Board: BA241 - Chips: 16C451, 8742, WD37C65, GA99, GA80
// CPU: 12MHz 80286 - FPU: 80287-2 socket provided - RAM: 1MB, 2MB, 4MB in SIMM modules - ROM: 64KB BIOS 1.07-1.09 - Mass storage: 2x 3.5" FDD HD,
// on board: AT/RLL controller - Shadow memory: E0000-FFFFF - Bus: 3xISA8/18 on riser board - Video: PVGA1A on board, 256KB RAM - Connectors: VGA, ser, par, PS/2 keyboard, PS/2 mouse
ROM_START( m250e )
	ROM_REGION16_LE(0x20000, "bios", 0 )
	ROM_LOAD( "biosglas.bin", 0x10000, 0x10000, CRC(83c33ce4) SHA1(0f132072dca06d8c7bb51b0f0d6304a74406cc8c))
ROM_END

// Olivetti M290 - uses the Olivetti GO481 Paradise PVGA1A-JK VGA card - locks up with "Error 2" and a key symbol
// Chipset: Olivetti GA099-B/28927F74AT, Olivetti GA098-B 28909F74AS, TI TACT82206FN, Olivetti 8920K5
// Floppy/IDE card: WD37C65BJM, NS16C450V
ROM_START( m290 )
	ROM_REGION16_LE( 0x20000, "bios", 0 )
	ROM_LOAD( "m290_pep3_1.25.bin", 0x10000, 0x10000, CRC(cb57d677) SHA1(4bdf5c52567c129b413c866c63b5fb3562fccd23))

	ROM_REGION( 0x0800, "keyboard", 0 )
	ROM_LOAD( "m290_csl0_1.10.bin", 0x000, 0x0800, CRC(d767d496) SHA1(84246f7b39e0a005425948931cf93624b831e121) )
ROM_END

// Ericsson WS286
ROM_START( ews286 ) // Computer is brown/yellow-ish with Ericsson logo
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "rys_103_1002_r8a_3c00_ic-pos_71.bin", 0x18000, 0x4000, CRC(af179e56) SHA1(58b1df46d6e68eef472a0529cb9317abaf17880f)) // Last ROM set and has Nokia
	ROM_LOAD16_BYTE( "rys_103_1003_r8a_8600_ic-pos_69.bin", 0x18001, 0x4000, CRC(555502cb) SHA1(1977fe54b69c5e52731bf3eb8bdabe777aac014b)) // copyright patched in both roms
ROM_END

// Nokia Data WS286
//ROM_START(nws286 ) // Computer is grey with Nokia logo.
//  ROM_REGION(0x20000,"bios", 0)
//  ROM_LOAD16_BYTE( "rys_103_1002_r8a_3c00_ic-pos_71.bin", 0x18000, 0x4000, NO_DUMP)
//  ROM_LOAD16_BYTE( "rys_103_1003_r8a_8600_ic-pos_69.bin", 0x18001, 0x4000, NO_DUMP)
//ROM_END


//**************************************************************************
//  80286 Notebook/Laptop/Portable
//**************************************************************************

// CAF Prolite 286/16 - Chipset: CHIPS P82C06, P82C211-12, P82C215-12, P82C212B-17 16MHz, WD47C65BJM, Acer M2201 - on board: beeper, floppy (FDC1.0 undumped)
// CPU: 286, FPU: Socket provided - RAM: 8xSIMM30 - BIOS: CAF Computer Corporation AMI BIOS - Keyboard BIOS: FONTEX TECH.CORP. AMI 286 BIOS PLUS KEYBOARD 07327
// OSC: 32.768KHz, 16MHz, 9.6MHz, 39.000MHz, 18.000000MHz, 16.257MHz, 1.8432MHz, 32.00MHz, 20MHz
// Video on board:  Genoa 7017417 BIOS (undumped), GRAY1.1 (undumped), Genoa GN006001-B, GN006002-B, GN006003-A, 256KB Video RAM - DIP10: off-on-on-off-on-on-on-off-off-off
ROM_START( prolite286 ) // Initialises graphics card, then dies
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "caf_prolite_even.bin", 0x10000, 0x8000, CRC(c3f4b360) SHA1(958fde7fa12425f6ac14fee6ebfd1b1f535c66eb))
	ROM_LOAD16_BYTE( "caf_prolite_odd.bin", 0x10001, 0x8000, CRC(7c2f6f9f) SHA1(6e72f1458308e521e5715cedb83f40ebe0cc4ad7))
ROM_END

// AEG Olympia Olyport 40-21 aka Zenith SuperSport - CPU: AMD N80L286-12/8 - Chipset: Chips P82C2185, P82C211C, P82C206 F-1, P82C212B, P82C604, WD37C65BFM, Hitachi HD6305V0P
// OSC: 22.500, 24.000 - Video: CGA, LCD with 16 grey intensities - Connectors: CRT, Ext. Bus, RS232C, Printer, Ext.FDD - Mass storage: FDD 1.44MB, HD: Conner CP-323 (IDE with detached controller PCB)
ROM_START( olyport40 ) // "+++ ERROR: Fatal Slushware RAM Error +++" / "--- Fatal Error: Cannot Continue! ---" - slushware is a ROM shadowing concept cropping up in Zenith brochures
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "mbevn_v2.7b.bin", 0x10000, 0x8000, CRC(eaf0d73c) SHA1(d642afe8a72b95e5e9d9cdc8cf8db833df54eaf0))
	ROM_LOAD16_BYTE( "mbodd_v2.7b.bin", 0x10001, 0x8000, CRC(a750d652) SHA1(c9a3cca535f6e7c44b83d87efcc289afda71b62f))

	ROM_REGION( 0x0800, "keyboard", 0 )
	ROM_LOAD( "nec_d80c42c.bin", 0x000, 0x0800, CRC(49ae4c38) SHA1(d5e6463d1dbcc7d68ef6b9222ff02102918f41b7))
ROM_END

// Compaq Portable II
// Chips: Intel D8742, SN76LS612N, 2x NEC D8237AC-5, 2xIntel P8259A-2, MC146818AP, Intel P8254
// Enhanced Color Graphics board: Chips P82C431, P82C434A
ROM_START( comportii )
	ROM_REGION16_LE(0x20000,"bios", 0)
	ROM_SYSTEM_BIOS(0,"105620-001", "Ver. D (105620/105622)")
	ROMX_LOAD( "comportii_105622-001.bin", 0x18000, 0x4000, CRC(30804fa4) SHA1(204d16dac4db4df0ba23a336af62da3f66aa914c), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "comportii_105620-001.bin", 0x18001, 0x4000, CRC(45fe43e8) SHA1(f74c2e30f7bd162be4042946ebcefeb236bd2fe7), ROM_SKIP(1) | ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1,"106437-001", "Ver. F (106437/106438)")
	ROMX_LOAD( "106438-001.bin", 0x18000, 0x4000, CRC(616361de) SHA1(ce1a6f9be9d374b76a83856f176aaa993d1dd46c), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "106437-001.bin", 0x18001, 0x4000, CRC(b50881ae) SHA1(2a79b39f77b0d3e94e4f765ed6c1961746dad563), ROM_SKIP(1) | ROM_BIOS(1) )
	ROM_SYSTEM_BIOS(2,"109739-001", "Ver. P.1 (109739/109740)")
	ROMX_LOAD( "109740-001.rom", 0x18000, 0x4000, CRC(0c032f12) SHA1(3ae7833d7f92d6495e2e57caa0260b573187eb72), ROM_SKIP(1) | ROM_BIOS(2) )
	ROMX_LOAD( "109739-001.rom", 0x18001, 0x4000, CRC(83698b85) SHA1(3d3cff84a747aea3db2612a7ac3ebe9cb4700b33), ROM_SKIP(1) | ROM_BIOS(2) )
ROM_END

// Compaq Portable III
// Chipset: Fujitsu MB672318, MB672316U, 2x Intel P8237A-5, Compaq 8731KX 104111-002, Intel 8272A, 2xAMD P8259A, Graphics: M77H010
// MC146818P, Intel D8742, Fujitsu MB672322
ROM_START( comportiii )
	ROM_REGION16_LE(0x20000,"bios", 0)
	ROM_SYSTEM_BIOS(0, "106779-002", "106779-002")
	ROMX_LOAD( "cpiii_87c128_106779-002.bin", 0x18000, 0x4000, CRC(aef8f532) SHA1(b0374d5aa8766f11043cbaee007e6d311f792e44), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "cpiii_87c128_106778-002.bin", 0x18001, 0x4000, CRC(c259f628) SHA1(df0ca8aaead617114fbecb4ececbd1a3bb1d5f30), ROM_SKIP(1) | ROM_BIOS(0) )
	// ROM_LOAD( "cpiii_106436-001.bin", 0x0000, 0x0800, CRC(5acc716b) SHA1(afe166ecf99136d15269e44ebf2d66317945bf9c) ) // keyboard
	ROM_SYSTEM_BIOS(1, "109737-002", "109737-002")
	ROMX_LOAD( "109738-002.bin", 0x10000, 0x8000, CRC(db131b8a) SHA1(6a8517a771272edf16870501fc1ed94c7555ef45), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "109737-002.bin", 0x10001, 0x8000, CRC(8463cc41) SHA1(cb9801591e4a2cd13bbcc40739c9e675ba84c079), ROM_SKIP(1) | ROM_BIOS(1) )
ROM_END

// Nixdorf 8810 M15 Laptop - PC07 - boot from harddisk doesn't work
// Chipset: Faraday FE3020, FE3000A, FE3010EB, FE3030, NEC D65013GF280, Toshiba TC8566AF, MC146818A, NEC D65013GF328, D65013GF371, D65013GF356, NS16C450V, Yamaha V6366C-F, MEI DA7116AFPBW
ROM_START( n8810m15 )
	// ROM_LOAD("charagene_v1.1_daft2c2.bin", 0x00000, 0x4000, CRC(dd324efd) SHA1(67fd91277733596bfad8506dc92d9f776e563dda)) // CGA chargen

	ROM_REGION16_LE(0x20000, "bios", 0 )
	ROM_LOAD16_BYTE( "rbios_even_daft2a3.bin", 0x10000, 0x8000, CRC(790abf68) SHA1(fbdb5e628ee9a605c8c1485a3fbb67736ff03153))
	ROM_LOAD16_BYTE( "rbios_odd_daft2b3.bin", 0x10001, 0x8000, CRC(b09a812a) SHA1(c1b3321715260f9cd8c810325dc10c674ea05174))
ROM_END

// Nixdorf 8810 M16 Laptop - PC17 - CGA version - boot from harddisk doesn't work
// Chipset: Chips P82xxxx, Chips P82C211-12, P82C215, P82C212B-12, MX9007G/MX1 16C4522QC, WD37C65BJM, Yamaha V6366C-F
ROM_START( n8810m16c )
	// ROM_LOAD("201cg rev 1.0.u78", 0x00000, 0x4000, CRC(3e31143b) SHA1(489da357e0ab8a469a3fb81cce160637486c87bc)) // CGA chargen
	ROM_REGION16_LE(0x20000, "bios", 0 )
	ROM_LOAD16_BYTE( "nmc27c256.u35", 0x10000, 0x8000, CRC(51acd116) SHA1(1a0bf24af4eba48d0deb0132a523e131902d2bcd))
	ROM_LOAD16_BYTE( "nmc27c256.u36", 0x10001, 0x8000, CRC(fb47f9da) SHA1(d9bd4aea850a83764454a5c86c8da09f7c640fd6))
	ROM_REGION( 0x0800, "keyboard", 0 )
	ROM_LOAD( "d8749h.u69", 0x000, 0x0800, CRC(030051da) SHA1(91b60228452cd1d6af99786402bd3b4d3efc2f05) )
ROM_END

// Nixdorf 8810 M16 Laptop - PC17 - VGA version - boot from harddisk doesn't work
// Chipset: MX8945G/MX16C4520C, Chps P82C212B-12, P82C215, P82C206, WD37C65BJM, P82C211-12, Chips F82C455,
ROM_START( n8810m16v )
	// ROM_LOAD("8810m16vga_27c256_221vb_123g1.bin", 0x00000, 0x8000, CRC(3bc80739) SHA1(3d6d7fb01681eccbc0b560818654d5aa1e3c5230)) // C&T VGA BIOS for 82C455
	ROM_REGION16_LE(0x20000, "bios", 0 )
	ROM_LOAD16_BYTE( "8810m16vga_27c256_286bios_a2531511_a.bin", 0x10000, 0x8000, CRC(1de5e49b) SHA1(759878e13801278de96700bbef318a49cca68054))
	ROM_LOAD16_BYTE( "8810m16vga_27c256_286bios_a2531511_b.bin", 0x10001, 0x8000, CRC(a65cf1f8) SHA1(30d46b49e87f272540e24a278848122b3c40bdaf))
	ROM_REGION( 0x0800, "keyboard", 0 )
	ROM_LOAD( "8810m16vga_8749_201kb_rev3a.bin", 0x000, 0x0800, CRC(030051da) SHA1(91b60228452cd1d6af99786402bd3b4d3efc2f05) )
ROM_END


//**************************************************************************
//  80386 SX and DX BIOS
//**************************************************************************

ROM_START( at386sx )
	ROM_REGION16_LE(0x20000, "bios", 0 )
	// 0: NCR 386 CPU card - Chipset: TACT82301PB, TACT82302PB, TACT82303PB
	ROM_SYSTEM_BIOS( 0, "ncr386sx", "NCR 386sx card" ) // Upgrade card for e.g. NCR PC-8 - Setup Version 2.7.1
	ROMX_LOAD( "ncr_386sx_u12-19_7.3.bin", 0x10001, 0x8000, CRC(9e4c9a2a) SHA1(0a45d9f04f03b7ae39734916af7786bc52e5e917), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "ncr_386sx_u46-17_7.3.bin", 0x10000, 0x8000, CRC(73ad83a2) SHA1(bf6704fb4a0da37251f192cea3af2bc8cc2e9cdb), ROM_SKIP(1) | ROM_BIOS(0))
	// ROM_LOAD( "ncr_386sx_card_150-0004508_u1_v1.1.bin", 0x0000, 0x800, CRC(dd591ac1) SHA1(5bc40ca7340fa57aaf5d707be45a288f14085807))
	// 1: Dell 386SX-33 with 2x 72-pin SIMMs, ISA riser slot -  Chipset: VLSI 82C311, Cirrus Logic GD5420 - BIOS: 27C010 EPROM containing Quadtel VGA BIOS and Phoenix system BIOS 02/09/93
	// BIOS-String: Phoenix 80386 ROM BIOS PLUS Version 1.10 J01 - Copyrights by Phoenix and Dell - Jostens Learning Corporation 386/33SX - CPU: Intel 386sx
	ROM_SYSTEM_BIOS( 1, "dell386sx", "Dell 386sx" ) // emulate integrated VGA card
	ROMX_LOAD( "dell386.bin", 0x00000, 0x20000, CRC(d670f321) SHA1(72ba3a76874e0c76231dc6138eb56a8ca46b4b12), ROM_BIOS(1))
	// 2: A3286/A3886-01 COMP V4.0 - Chipset: Intel S82344A (VLSI), S82343 (VLSI) - BIOS: AMI P9 (386SX) BIOS 910520
	// BIOS-String: - 30-05T1-425004-00101111-050591-ITOPSX-0 / MULTITRONIC PERSONAL COMPUTER - Keyboard-BIOS: AMI P9(386SX) Keyboard BIOS 910520 - OSC: 8.000, 14.318180MHz, (unreadable) - CPU: Intel SMD, unreadable - ISA8: 1, ISA16: 5
	ROM_SYSTEM_BIOS( 2, "a3286a3886", "A3286/A3886-01 COMP V4.0")
	ROMX_LOAD( "386-a3286-a3886-01-even_32k.bin", 0x10000, 0x8000, CRC(56ed3332) SHA1(9d113e57228ee596c0c24eabb193d3670fb9a309), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD( "386-a3286-a3886-01-odd_32k.bin", 0x10001, 0x8000, CRC(9dbe4874) SHA1(589379055cfedd4268d8b1786491e80527f7fad5), ROM_SKIP(1) | ROM_BIOS(2))
	// 3: CPU/FPU: 386SX/486SLC - Chipset: ALD 93C308-A (93C206 ???)
	// BIOS-String: X0-0100-000000-00101111-060692-386SX-0 / CC-070794-P01
	ROM_SYSTEM_BIOS( 3, "ald93c308", "ALD 93C308" )
	ROMX_LOAD( "3ldm001.bin", 0x10000, 0x10000, CRC(56bab3c7) SHA1(6970bdc7407b4b57c8e1d493f9e3d9ae70671b9c), ROM_BIOS(3))
	// 4: BIOS: Phoenix; 01/15/88 - CPU: 386sx-16 - Chipset: Intel - BIOS-String: Phoenix 80386 ROM BIOS PLUS Version 1.10 02 / 386SX, ADI CORP.
	ROM_SYSTEM_BIOS( 4, "intel", "Intel chipset")
	ROMX_LOAD( "3iip001l.bin", 0x10000, 0x8000, CRC(f7bef447) SHA1(a6d34c3bf0de93c2b71010948c1f16354996b5ab), ROM_SKIP(1) | ROM_BIOS(4))
	ROMX_LOAD( "3iip001h.bin", 0x10001, 0x8000, CRC(f46dc8a2) SHA1(b6566fd761e2e6ec34b61ee3bb043ef62d696b5e), ROM_SKIP(1) | ROM_BIOS(4))
	// 5: BIOS-String: X0-0100-000000-00101111-060692-386sx-0 / Ver. 5.14 - continuous reset
	ROM_SYSTEM_BIOS( 5, "v514", "V. 5.14")
	ROMX_LOAD( "3zzm001.bin", 0x10000, 0x10000, CRC(f465b03d) SHA1(8294825dcaa254c606cee21db7c74f61c1394ade), ROM_BIOS(5))
ROM_END

// NEATsx chipset: Chips 82C811 CPU/Bus controller, 82C812 Page interleave/EMS memory controller, 82C215 Data/Address buffer and 82C206 Integrated Peripheral Controller
ROM_START( ct386sx )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// 0: BIOS-String: ENSX-1131-0040990-K0 / AMI 386SX-BIOS / NEATSX V1.1 05-31-90
	ROM_SYSTEM_BIOS(0, "neatsx", "NEATsx 386sx")
	ROMX_LOAD( "012l-u25.bin", 0x10000, 0x8000, CRC(4ab1862d) SHA1(d4e8d0ff43731270478ca7671a129080ff350a4f),ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "012h-u24.bin", 0x10001, 0x8000, CRC(17472521) SHA1(7588c148fe53d9dc4cb2d0ab6e0fd51a39bb5d1a),ROM_SKIP(1) | ROM_BIOS(0))
	ROM_FILL(0x1e2c9, 1, 0x00) // skip incompatible keyboard controller test
	ROM_FILL(0x1e2cb, 1, 0xbb) // fix checksum
	// 1: VIP-M345000 NPM-16 - BIOS-String: Phoenix 80386 ROM BIOS PLUS Version 1.10 20 / AUVA - Keyboard-BIOS: M5L8042-165P
	// ISA8: 2, ISA16: 5, Memory: 1 - OSC: 32.000MHz, 14.31818
	ROM_SYSTEM_BIOS(1, "m345000", "VIP-M345000 NPM-16")
	ROMX_LOAD( "386-vip-m345000 a1_32k.bin", 0x10001, 0x8000, CRC(8119667f) SHA1(343221a9729f841eb23eafe5505f1216783e5550), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "386-vip-m345000 a2_32k.bin", 0x10000, 0x8000, CRC(ada1a375) SHA1(74128270aa8fed504e8785c5d490b0fa25cc3895), ROM_SKIP(1) | ROM_BIOS(1))
	// 2: DTK Corp. 386sx COMPUTER / DTK 386sx Chipset ROM BIOS Version 4.26 / #96120590N
	ROM_SYSTEM_BIOS(2, "dtk386sx", "DTK 386sx")
	ROMX_LOAD( "3cso001.bin", 0x10000, 0x10000, CRC(8a0e26da) SHA1(94aefc745b51015426a73015ab7892b88e7c8bcf), ROM_BIOS(2))
	// 3: Chipset is labeled SOLUTIONS 88C211, 88C212, 88C215, P82C206
	// BIOS-String: ENSX-1107-040990-K0 - CPU: 386SX-16
	ROM_SYSTEM_BIOS(3, "solutions", "SOLUTIONS NEATsx")
	ROMX_LOAD( "3som001l.bin", 0x10000, 0x8000, CRC(ecec5d42) SHA1(b1aaed408fe9c3b73dff3fa8b19e62600a49cdb2), ROM_SKIP(1) | ROM_BIOS(3))
	ROMX_LOAD( "3som001h.bin", 0x10001, 0x8000, CRC(85d64a86) SHA1(528506724668ea3aef6aa0bd8d68cfcaa58bf519), ROM_SKIP(1) | ROM_BIOS(3))
	// 4: Manufacturer/Identifier: ELITEGROUP COMPUTER SYSTEMS, INC. NEATSX REV 1.0. - Chipset: NEATsx: Chips P82C206, Chips P82C215, Chips P82C811, Chips P82C812
	// CPU: Intel NC80386sx-20, FPU socket provided - RAM: DIP 1MB (DIP sockets for up to 4MB), 4xSIPP30 - BIOS EPROMs: 2x S27C256 NEATsx-013 PLUS
	// Keyboard BIOS: 1988 AMI 1131 KEYBOARD BIOS PLUS - OSC: 32.0000MHz, 40.000MHz, 14.31818 - ISA8: 3, ISA16: 5
	ROM_SYSTEM_BIOS(4, "neatsx013", "NEATsx-013 PLUS") // initializes the graphics card, then dies
	ROMX_LOAD( "neatsx-013_l.bin", 0x10000, 0x8000, CRC(7cd4d870) SHA1(c7a5b629dadb43779939043ae4adb5e78c770dc3), ROM_SKIP(1) | ROM_BIOS(4))
	ROMX_LOAD( "neatsx-013_h.bin", 0x10001, 0x8000, CRC(388587d4) SHA1(8ae6f6b14a2f53438b6a02c4f032088edb2df484), ROM_SKIP(1) | ROM_BIOS(4))
ROM_END

ROM_START( at386 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: X0-0100-000000-00101111-060692-386SX-0 / AMIBIOS Ver 5.19a
	ROM_SYSTEM_BIOS(0, "ami386", "AMI 386")
	ROMX_LOAD( "ami386.bin",  0x10000, 0x10000, CRC(3a807d7f) SHA1(8289ba36a3dfc3324333b1a834bc6b0402b546f0), ROM_BIOS(0))
	// 1: Phoenix 80386 ROM BIOS PLUS Verson 1.10 (R22)
	ROM_SYSTEM_BIOS(1, "at386", "unknown 386")  // This dump possibly comes from a MITAC INC 386 board, given that the original driver had it as manufacturer
	ROMX_LOAD( "at386.bin",  0x10000, 0x10000, CRC(3df9732a) SHA1(def71567dee373dc67063f204ef44ffab9453ead), ROM_BIOS(1))
	// 2: BIOS-String: 30-0101-429999-00101111-050591-D90-0 / AMI TD60C BIOS VERSION 2.42B
	ROM_SYSTEM_BIOS(2, "amicg", "AMI CG")
	ROMX_LOAD( "amicg.1",        0x10000, 0x10000,CRC(8408965a) SHA1(9893d3ac851e01b06a68a67d3721df36ca2c96f5), ROM_BIOS(2))
	// 3:
	ROM_SYSTEM_BIOS(3, "msi386", "MSI 386") // MSI 386 mainboard, initializes graphics card, then hangs - Chipset: Chips P82A304, P82A303, P82A302C, 2xP82B305, P82C301C, P82A306A,
	ROMX_LOAD( "ami_386_msi_02297_even.bin", 0x10000, 0x8000, CRC(768590a0) SHA1(90c5203d78591a093fd4f54ceb8d9827f1e64f39), ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "ami_386_msi_02297_odd.bin", 0x10001, 0x8000, CRC(7b1360dc) SHA1(552ccda9f90826621e88d9abdc47306b9c2b2b15), ROM_SKIP(1) | ROM_BIOS(3) )
	// 4
	ROM_SYSTEM_BIOS(4, "ami2939", "AMI2939") // no display
	ROMX_LOAD( "ami2939e.rom", 0x10000, 0x8000, CRC(65cbbd32) SHA1(d7d26b496f8e86f01722ad9f171a68f9fcdc477c), ROM_SKIP(1) | ROM_BIOS(4) )
	ROMX_LOAD( "ami2939o.rom", 0x10001, 0x8000, CRC(8db6e739) SHA1(cdd47709d6036fad4be40c15bff41752d831d4b8), ROM_SKIP(1) | ROM_BIOS(4) )
	// 5: NCR 386 slot CPU - Upgrade card for e.g. NCR PC-8 - set graphics card to CGA to see a "Timer One Error" message
	ROM_SYSTEM_BIOS(5, "ncr386", "NCR 386 CPU card") // Chipset: SN76LS612PN, 2xAM9517A-5JC, NCR 006-3500402PT M472018 8650A
	ROMX_LOAD( "ncr_386_card_04152_u44_ver5.0.bin", 0x10000, 0x10000, CRC(80e44318) SHA1(54e1d4d646a577c53c65b2292b383ed6d91b65b2), ROM_BIOS(5))
	// ROM_LOAD ("ncr_386_card_keyboard_04181_u27_ver5.6.bin", 0x0000, 0x800, CRC(6c9004e7) SHA1(0fe77f47ff77333d1ff9bfcf8d6d92193ab1f208))
	// 6: BIOS-String: Phoenix 80386 ROM BIOS PLUS Version 1.10 22
	ROM_SYSTEM_BIOS(6, "cbm386", "Commodore 386")
	ROMX_LOAD( "cbm-386-bios-lo-v1.022e-8100.bin", 0x10000, 0x8000, CRC(a054a1b8) SHA1(d952b02cc10534325c1c5aaa8b6dfb77bc20a179), ROM_SKIP(1) | ROM_BIOS(6))
	ROMX_LOAD( "cbm-386-bios-hi-v1.022e-d100.bin", 0x10001, 0x8000, CRC(b9541f3d) SHA1(e37c704521e85b07369d21b0521f4d1871c318dd), ROM_SKIP(1) | ROM_BIOS(6))
	// 7: BIOS-String: X0-0100-000000-00101111-060692-RC2018A-0 / Ver 1.4b / Texas Instruments 486 DLC [S3Q]
	ROM_SYSTEM_BIOS(7, "ti486dlc", "TI 486DLC") // board is equipped with a TI486DLC
	ROMX_LOAD( "ti_486dlc_rev.s3q.bin", 0x10000, 0x10000, CRC(39b150ed) SHA1(5fc96c6232dd3a066349d8e707e938af55893297), ROM_BIOS(7))
	// 8: BIOS-String: 305-3.2 000-00 - Chipset: TACT82206FN; Intel A82385-33 - Keyboard Controller: P/N: 191106-2 C/S E4F4 Rev. 1.4
	// Board with Tandon and Micronics stickers - BIOS: 192475-305A V305 3.2
	// ISA8: 2, ISA16: 5 - OSC: 14.31818 MHz - 66.0000 MHz, CPU: Intel 80386DX-33, FPU: Intel 80387DX-33
	ROM_SYSTEM_BIOS(8, "tanmic385", "Tandon/Micronics with 385")
	ROMX_LOAD( "386-micronics-09-00021-even_32k.bin", 0x10000, 0x8000, CRC(0d4f0093) SHA1(f66364a82c957862a0e54afc3a2f85f911adfd49), ROM_SKIP(1) | ROM_BIOS(8))
	ROMX_LOAD( "386-micronics-09-00021-odd_32k.bin", 0x10001, 0x8000, CRC(54195986) SHA1(f3536340ef1697763e5cd70d0de7bb9b2a4ecde9), ROM_SKIP(1) | ROM_BIOS(8))
	// 9: Biostar MB1325PM - Chipset: Chips P82C206 µIC MI9382 MI9381A
	// BIOS: AMI 386 BIOS - BIOS-String: 30-0101-D61223-00101111-050591-OPBC-F / MB-1325PM. - Keyboard-BIOS: AMI
	// CPU: AMD 386DX/DXL-25 - ISA8: 1, ISA16: 6, ISA8/Memory: 1
	ROM_SYSTEM_BIOS(9, "mb1325pm", "MB1325PM")
	ROMX_LOAD( "386-mb1325pm ok.bin", 0x10000, 0x10000, CRC(768689c1) SHA1(ce46b3baf3cd2586ffaccdded789a54583b73a3b), ROM_BIOS(9))
	// 10: Intel SSBC 386AT - Chipset: Intel P8237A, P8254; P8259A, SN74LS612N - BIOS: 380892-01 Rev. 1.04 U53 L - 380892-02 Rev. 1.04 U52 H
	// Keyboard BIOS: Intel 453775-001 - ISA8: 2, ISA16: 4, Memory expansion: 2 - OSC: 1.8432 MHz - 32.000 - 14.31818
	ROM_SYSTEM_BIOS(10, "ssbc386at", "Intel SSBC 386AT" ) // no display
	ROMX_LOAD( "386-intel-u53-l_32k.bin", 0x10001, 0x8000, CRC(5198a767) SHA1(03dd494e3a218c59c82ebd7b1dd16905bca30773), ROM_SKIP(1) | ROM_BIOS(10))
	ROMX_LOAD( "386-intel-u52-h_32k.bin", 0x10000, 0x8000, CRC(cedbad7a) SHA1(e1365f5a183a342fe58205679a512c4ccd2a705a), ROM_SKIP(1) | ROM_BIOS(10))
	// 11: BEK P405 clone - BIOS-String: 30-0201-428029-00101111-070791-OPWB-0 -  was found as a stray ROM - possibly from a 486 board
	ROM_SYSTEM_BIOS(11, "opwb", "OPWB")
	ROMX_LOAD( "opwb.bin",  0x10000, 0x10000, CRC(e7597fb6) SHA1(2f1eb88138b400cc3ad554d03e532b5d3b0b11ad), ROM_BIOS(11))
	// 12: unknown manufacturer, Logo could be a "J7" -  COPYRIGHT (C) 89 REV.C MADE IN TAIWAN - Chipset: Chips P82C206
	// BIOS: AMI 386 BIOS PLUS 896818 - BIOS-String: DINT-6102-091589-K0 - Keyboard-BIOS: AMI KEYBOARD BIOS PLUS 896819
	// CPU: Intel 386DX-25 FPU: i386DX-33 - OSC: 54.0000MHz, 16.000MHz, 14.31818MHz
	ROM_SYSTEM_BIOS(12, "386atj7", "386AT J7" )
	ROMX_LOAD( "386-big_ami_896818_even.bin", 0x10000, 0x8000, CRC(096e99c4) SHA1(29ff718362af4f5d7c0173f4de84290cec60dded), ROM_SKIP(1) | ROM_BIOS(12))
	ROMX_LOAD( "386-big_ami_896818_odd.bin", 0x10001, 0x8000, CRC(6f92634d) SHA1(e36d401975690043c5d5cb1f781036b319e57f37), ROM_SKIP(1) | ROM_BIOS(12))
	// 13: BIOS-String: DAMI-0000-040990-K0 - silkscreen on board: 17:35-2495-02 - 702430D - H8010-30 - ISA8:2, ISA16: 8 - OSC: 14.31818, 50.000MHz
	// Chipset:Laser 27-2024-00 4L40F1028, Laser 27-2025-00 4L40F1026, 2xKS82C37A, KS92C59A, KS82C54-10P - CPU: Intel 386DX-33, FPU socket provided
	ROM_SYSTEM_BIOS(13, "vt386vt", "VT386VT" )
	ROMX_LOAD( "vt386vt-702430d-rom0_32k.bin", 0x10000, 0x8000, CRC(00013ee6) SHA1(7fed0b176911a94e8127b01bb77445c78f283ff7), ROM_SKIP(1) | ROM_BIOS(13))
	ROMX_LOAD( "vt386vt-702430d-rom1_32k.bin", 0x10001, 0x8000, CRC(c817ec57) SHA1(acdd0e28cb4798059c02e1342da7efe3eaf2c5cb), ROM_SKIP(1) | ROM_BIOS(13))
	// 14: Micronics I-Cache 09-00021-L8949 - Chipset: Chips P82C206 - RAM: M500/385 Memory Board, Cache: 8xCXK5863P-25
	// BIOS-String: Phoenix 80386 ROM BIOS PLUS Version 1.10 10a - Keyboard-BIOS: Intel - CPU/FPU: sockets provided, empty
	// ISA8: 2, ISA16: 4, Memory: 1 - OSC: 66.0000MHz, unreadable
	ROM_SYSTEM_BIOS(14, "l8949", "L8949" )
	ROMX_LOAD( "386-micronics 09-00021-lo_32k.bin", 0x10000, 0x8000, CRC(3a8743e3) SHA1(42262f60cb655ab120d968dbf9eb03387424bf14), ROM_SKIP(1) | ROM_BIOS(14))
	ROMX_LOAD( "386-micronics 09-00021-hi_32k.bin", 0x10001, 0x8000, CRC(c7fce430) SHA1(e0d6e8dbb8b6d68bd92dab63a259d2c9293f5571), ROM_SKIP(1) | ROM_BIOS(14))
	// 15: BIOS-String: 30-0200-ZZ1453-00101111-050591-SCAT3.04-0
	ROM_SYSTEM_BIOS( 15, "kmxc02", "KMX-C-02" )
	ROMX_LOAD( "3ctm005.bin", 0x10000, 0x10000, CRC(cfba6b2a) SHA1(001642016a3c02b031d739bd8b0fcff9470e86d2), ROM_BIOS(15))
	// 16: BIOS: AMI; 11/11/92 - ISA16: 5 - CPU/FPU: Am386DX-40, IIT 3C87-40 - Chipset: FOREX FRX46C521, KS83C206Q
	// BIOS-String: 40-0G00-009999-00101111-111192-4X521-F
	ROM_SYSTEM_BIOS( 16, "frx521", "using the Forex FRX46C521" ) // no display
	ROMX_LOAD( "3fom001.bin", 0x10000, 0x10000, CRC(8fa851c8) SHA1(68ac21357558d98aee4e2ffb903791e4198e0dd0), ROM_BIOS(16))
	// 17: FOREX 386 Super DX System  S3B
	ROM_SYSTEM_BIOS( 17, "frxs3b", "Forex Super DX System S3B") // no display
	ROMX_LOAD( "3fom003.bin", 0x10000, 0x10000, CRC(4e164e0a) SHA1(dc2d08061c443a3e4ced3ab11f1fa094585cbbba), ROM_BIOS(17))
	// 18: BIOS: AMI; 06/06/92 - BIOS-String: 40-0101-001107-00001111-060692-OPWB4SXB-0 / OPTI-495SX (471WB) BIOS VER 1.0
	// cf. driver hot409 - BIOS is capable of detecting 386sx => 486DX2, this particular BIOS was sorted with the 386s on chukaev
	ROM_SYSTEM_BIOS( 18, "495sx", "OPTi 82C495SX")
	ROMX_LOAD( "3opm009.bin", 0x10000, 0x10000, CRC(2abe36eb) SHA1(d113527ebd06f0359f2decd4ac0c6202f982d45e), ROM_BIOS(18))
	// 19: BIOS-String: EEMI-0386-030891-K0 - Chipset: 88C311
	ROM_SYSTEM_BIOS( 19, "eemi", "EEMI")
	ROMX_LOAD( "3zzm002", 0x10000, 0x10000, CRC(c2a7ff22) SHA1(af2e321d3245ad839a41666917bb24cca0f7884d), ROM_BIOS(19))
	// 20: BIOS-String: 30-0300-ZZ1425-00101111-020291-ITOPDX / 23L-1-0000-00-00-0000-00-00-000-K0
	// 000-0-0000-00-00-0000-00-00-00-2
	ROM_SYSTEM_BIOS( 20, "topcat", "TOPCAT")
	ROMX_LOAD( "ami_386_vl82c330_even.bin", 0x10000, 0x8000, CRC(a6f3d881) SHA1(40672d58f79d232dbda9685b9aa20533029fbdfc), ROM_SKIP(1) | ROM_BIOS(20))
	ROM_CONTINUE( 0x10001, 0x8000 )
	// 21: BIOS-String: MR BIOS (tm) V1.35 - RAM Pattern Test Failed at 0F0000H
	ROM_SYSTEM_BIOS( 21, "mrv135", "MR BIOS V1.35")
	ROMX_LOAD( "mrbios_v1.35_opti324_4floppy.bin", 0x10000, 0x10000, CRC(9a21dcd3) SHA1(dcab673fd2df621839671ef8f6a2eff443de39df), ROM_BIOS(21))
	// 22: BIOS-String: SINT-1185-040990-K0 - Chipset: VIA SL9030, SL9010, SL9025, SL9020, SL9020, SL9350, SL9090A
	ROM_SYSTEM_BIOS( 22, "3vim002", "3VIM002")
	ROMX_LOAD( "3vim002l.bin", 0x10000, 0x8000, CRC(368b66df) SHA1(1bef1e8e1818513061f0c7cf3c731da360c8400b), ROM_SKIP(1) | ROM_BIOS(22))
	ROMX_LOAD( "3vim002h.bin", 0x10001, 0x8000, CRC(02dbb9fe) SHA1(cfce750a4a019c71e59011fb7a7d891b40f61c61), ROM_SKIP(1) | ROM_BIOS(22))
	// 23: BIOS-String: 30-0201-ZZ1343-00101111-050591-OPWB-0
	ROM_SYSTEM_BIOS( 23, "zz1343", "zz1343")
	ROMX_LOAD( "ami_zz1343.bin", 0x10000, 0x10000, CRC(f5464c1f) SHA1(cb069c3d1d322aa769d46749716a35259f78264a), ROM_BIOS(23))
	// 24: ID: AT046DX3-B2.1(PQFP) - CPU: AMD Am386DX/DXL-33, FPU socket provided - Keyboard BIOS: JETkey V3 - Chipset: ACC Micro 2168 F9217D103 SC00 333MHz
	// RAM: 8xSIMM30, Cache: 64KB - OSC: 14.31818, 66.660MHz - ISA8: 1, ISA16: 5
	// BIOS-String: 30-004-428002-00101111-070791-ACC2046-0
	ROM_SYSTEM_BIOS( 24, "acc386", "AT046DX3-B2.1")
	ROMX_LOAD( "acc386.bin", 0x10000, 0x10000, CRC(2177c9ac) SHA1(2e320dcd173137b9b0cbf92602e9b77398921aaf), ROM_BIOS(24))
ROM_END


//**************************************************************************
//  80386 SX and DX motherboard
//**************************************************************************

// 486MMBO4088 - Chipset : ETEQ ET486SLC2 A, P82C206 - BIOS : MR 386SX/86SLC BIOS V.1.61
// Keyboard-BIOS: AMI MEGA-KB-F-WP - CPU: TI 486 TX486SLC/E -33MAB / FPU: IIT XC87SLC-33 - RAM: 8xSIMM30, Cache: 3xISSI IS61C256AH-15N, 2xIS61C256A-20N
// OSC: 14.31818MHz, 66.666MHz - ISA16: 7
ROM_START( mmbo4088 ) // screen remains blank
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "eteq-mrb.bin", 0x10000, 0x10000, CRC(10267465) SHA1(9cc1e7e8b0ec6e0798229489dce5b2b0300bfbd8))
ROM_END

// ILON USA, INC. M-396B - Chipset: PC Chip (silk screen sanded off), KS82C6818A - BIOS: AMIBIOS - BIOS-string: 30-0500-ZZ1437-001001111-070791-PC CHIP-F
// Keyboard-BIOS: Regional HT6542 - CPU: i386sx-25, FPU socket provided - RAM: 4xSIMM30, 8x20pin DIP - OSC: 14.31818, 50.000MHz - ISA16: 6
ROM_START( ilm396b )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "m396b.bin", 0x10000, 0x10000, CRC(e46bd610) SHA1(e5899f126ae6478f4db238cd1db835e0d1877893))
ROM_END

// Prolink P386SX-25PW VER:2.00 - Chipset: OPTi 82C281, F82C206 - BIOS/Version: AMI V007B316 - Keyboard-BIOS: AMI KB-BIOS-VER-F - OSC: 14.31818MHz, 50.000000MHz
// CPU: AMD Am386SX-25, FPU: IIT 3C87SX-33 - RAM: 8xSIMM30, Cache: 4x28pin, 2x24pin - ISA16: 8 -
// BIOS-String: 30-0100-008003-00101111-042591-OPSX-0 / OPSX 386SX BIOS / P386SX/25 PW IVN 1.0 1991.7
ROM_START( p386sx25pw )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "p386sx25pw.bin", 0x10000, 0x10000, CRC(9cbebe61) SHA1(ef90c1a9cc7fc3accdac9738aaf519c1b3d8260d))
ROM_END

// ELT-P9 / Most likely ELT-386SX-160D - Chipset: NEC ELT3000A - CPU: Intel 80386sx-16 - RAM: 8xSIPP30, 1MB DIP - BIOS: Phoenix
// Keyboard BIOS: Intel P8242 - DIP6: 000000 - OSC: 14.31818MHz, 32.000MHz  - ISA8: 1, ISA16: 6, ISA8/RAM: 1
ROM_START( eltp9 ) // Phoenix 80386 ROM BIOS PLUS Version 1.10.20
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "386_676849_l.bin", 0x10000, 0x8000, CRC(ab477e5e) SHA1(78d784f9c320fe7206c2601c4dbb47a90e5cbf96), ROM_SKIP(1))
	ROMX_LOAD( "386_676849_h.bin", 0x10001, 0x8000, CRC(02241258) SHA1(2bd80bab573cd88829edfb85522e978e5e477806), ROM_SKIP(1))
ROM_END

// QTC-SXM KT X20T02/HI Rev.3 - Chipset: VLSI / Intel S82343 + S82344A - BIOS Version: QTC-SXN 03.05.07
// Keyboard BIOS: NEC Performance 204 (c) Quadtel 265 - CPU: i386sx-20, FPU: Intel 387sx
// RAM: 4xSIMM30, sockets for 1MB DIP - ISA8: 2, ISA16: 4 - OSC: 15.31818MHz, 16.000MHz, 40.0000
ROM_START( ktx20t02 ) // BIOS-String: Quadtel VL82C286 386SX BIOS Version 3.05.07
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "qtc-sxm-even-u3-05-07.bin", 0x10000, 0x8000, CRC(21f2ca4c) SHA1(7131de700cb95c825e61d611001bab1d3d3bb195), ROM_SKIP(1))
	ROMX_LOAD( "qtc-sxm-odd-u3-05-07.bin", 0x10001, 0x8000, CRC(1543d0f7) SHA1(12281ef81d7aabf291586f96b678074216f0c23a), ROM_SKIP(1))
ROM_END

// PC-CHIPS M317 - Chipset: PC Chip CHIP 5 (14L40F2054) / CHIP 6 (14L50F2053), UMC 82C206F - CPU: 80386, FPU socket privided
// BIOS: AMI 386 BIOS PLUS - Keyboard BIOS: AMI Keyboard BIOS Plus - RAM: 8xSIMM30 - Cache: 64K - solder pads for a memory card connector
// ISA8: 2, ISA16: 6 - OSC: 66.6670MHz, 14.318MHz
ROM_START( pccm317 ) // 40-0300-ZZ1347-00101111-031591-UMCWB-0
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "386_pcchips.bin", 0x10000, 0x10000, CRC(8a6046ea) SHA1(1e6c5dacdb5f9a36e558e44466bcd9182d849932))
ROM_END

// ZEOS 386 SX-16 - Chipset: VLSI VL82C201-16QC, 82C202, 82C203, 82C204, 82C205A, 82C100
// DVLX-6099-091589-K0 - AMI 386 BIOS for ZEOS INTERNATIONAL, LTD. / ST. PAUL, MINNESOTA 55112 USA
// (C)1987 American Megatrends Inc.386-BIOS (C) 1989 AMI, for ZEOS INTERNATIONAL - CPU: Intel 386sx-33, FPU sockets provided
// RAM: 36x16/18pin - ISA8: 2, ISA16: 6 - OSC: 64.000000, 14.31818
ROM_START( zeos386sx )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "zeos_386_16 even.bin", 0x10000, 0x8000, CRC(9897f140) SHA1(b01de83d438ceda4a80e92dc1fc7d725323427b6), ROM_SKIP(1))
	ROMX_LOAD( "zeos_386_16 odd.bin", 0x10001, 0x8000, CRC(0f930857) SHA1(c63670eb336f1998532a4f601d1845902be279e7), ROM_SKIP(1))
ROM_END

// Packard Bell PCB-303 Rev.01 - Chipset: ACC Micro 2036 25MHz, UM82C862F - CPU: i386sx-25, FPU socket provided - RAM: 6xSIMM30 - ISA16: 1
// VGA on board: OTI 512KB - on board: Floppy, IDE, ser, par, PS/2 mouse and keyboard - OSC: 50.000MHz x2
ROM_START( pcb303 ) // Phoenix 80386 ROM BIOS PLUS Version 1.10 2715 - 19920411102517 - Error 8602 - Auxiliary Device Failure
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "386-packard-pcb-303.bin", 0x00000, 0x20000, CRC(bbb18c76) SHA1(94f3785c3b96dfe7ab53a97d097bfb17c57229b7))
ROM_END

// Philips P3239 (aka Headstart / MaxStation / Magnum / Professional 1200, 48CD, 1600, 64CD, P160, SR16CDPhilips 5107-100-36154 (motherboard),
// 5107-200-35452 (CPU card) - Chipset: VLSI VL82C311-FC2, VL82C107-VC, WD37C65CJM
// CPU (on card): Intel 386sx-20, FPU socket provided - BIOS: M27C1001, 48805 P3239 - on board: 2xser, par, VGA, Floppy, IDE
// RAM: 1MB, 1xSIMM72 - VGA on board: CL-GD5325-40QC-A, MUSIC TR9CI710-50DCA, 256KB - OSC: 9.600, 1.8432MHz, 14.31818, 16.000, 40.000 (on CPU card)
// DIP4: 0001
ROM_START( php3239 ) // no display, beep code sounds like morse
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "386-philips_p3239.bin", 0x00000, 0x20000, CRC(9178e136) SHA1(e3bad39fcf028f459516d4e9a895035891eb801e))
ROM_END

// SOYO 386DX Baby-AT - Chipset: ETEQ ET82C4901 C, ET82C4903 A, Chips P82C206 H1 - CPU: Am386DX/DXL-40, FPU: Cyrix 387DX+
// RAM: 8xSIMM30, Cache: 8x5C6408-12, 1x ATT7C185P-15 - BIOS: AMI 386 BIOS 336468 - BIOS-String: 41-0100-001102-00101111-121291-BENGAUTO-F - REV: C *** 40MHZ ***
// Keyboard BIOS: AMI KB BIOS-VER-F - ISA8: 1, ISA16: 6 - OSC: 8.000MHz, 80.000MHz
ROM_START( sybaby386 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "386dx.bin", 0x10000, 0x10000, CRC(9fcfd21f) SHA1(5e7b42be55a45b0b51d2a7e8bd8d6405dbe69129))
ROM_END

// Diamond Flower International 386SX-16/20CN Rev 1.0 - Chipset : CHIPS P82C206, P82C215A, P82C812,P82C811 - BIOS: AMI
// CPU: Intel 80386SX-20, FPU socket privided - RAM: 8xSIMM30 - ISA8: 2, ISA16: 6
ROM_START( dfi386sx ) // Hangs after initialising the graphics card
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "dfi_386sx_16cn_20cn_27c256_low.bin", 0x10000, 0x8000, CRC(58456dc1) SHA1(51f7052366106fe564aedb99ddf6974f6ad4c5cc), ROM_SKIP(1))
	ROMX_LOAD( "dfi_386sx_16cn_20cn_27c256_high.bin", 0x10001, 0x8000, CRC(1ab36d4b) SHA1(640af1c333255cca85543bc369cb0cede45e1ef6), ROM_SKIP(1))
ROM_END

// 3SIUD-1.1 - CPU: AMD Am386SX/SXL-25 - Chipset: SiS 85C206, UMC (unreadable) - RAM: SIMM30x4, 8x20pin, 4x16pin
// BIOS: AMI 386SX BIOS 70167 - Keyboard-BIOS: NEC KB-BIOS VER7 - ISA8: 1, ISA16: 5 - OSC: 50.000000MHz, 14.31818MHz
// BIOS-String: 30-0200-ZZ1266-00101111-050591-UMC386SX-0 / 3SIUD-1.0
ROM_START( 3siud )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "3siud_bios.bin", 0x10000, 0x10000, CRC(24fa8491) SHA1(635ce3db872e39d84f299356b960b0a16e2cf082))
ROM_END

// AMI 386 BABY SCREAMER - BIOS: AMI MARK V BABY SCREAMER - Chipset: VLSI VL82C331-FC, VL82C332-FC, Megatrends MG-9275, Chips ??? - OSC: 14.31818, 66.666, 24.000MHz
// BIOS-String: 40-0301-000000-00101111-070791-SCREAMER-0 / BIOS RELEASE 42121691 - On board: 2xserial, parallel, floppy, 1xIDE
ROM_START( amibaby )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROMX_LOAD( "ami_mark_v_baby_screamer_even.bin", 0x00000, 0x10000, CRC(50baacb7) SHA1(c9cb6bc3ab23f35050a7f079109005331eb5de2c), ROM_SKIP(1))
	ROMX_LOAD( "ami_mark_v_baby_screamer_odd.bin", 0x00001, 0x10000, CRC(42050eed) SHA1(c5e1ed9717acb2e3adcb388ccecf90a74d495132), ROM_SKIP(1))
ROM_END

// AUVA TAM/25-P2 M31720P - Chipset: µC M19382, M19381A, Chips - CPU: 386DX 25Mhz - BIOS: DA058290 - Keyboard-BIOS: A179859
// BIOS-String: 30-0101-D81105-00101111-050591-OPBC-0 / AUVA 386 TAM/25/P2(P2,A1,A2), 01/10/1992- ISA8: 1, ISA16: 6, ISA16/Memory: 1
ROM_START( tam25p2 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "tam25-p2.bin", 0x10000, 0x10000, CRC(0ea69975) SHA1(cb7f071a36653cf4f00a8b158a4900efb8f8b8e8))
ROM_END

// Elitegroup ELT-386SX-160BE - Chips P82C206 - CPU: Intel 386sx-16, FPU: socket provided - BIOS:Phoenix 679006 - Keyboard-BIOS: Intel P8242/Phoenix
// ISA8: 2, ISA16: 5 - OSC: 14.31818MHz, 32.000MHz - BIOS-String: Phoenix 80386 ROM BIOS PLUS Version 1.10 22 / ELT-386SX(P9)
ROM_START( elt386sx )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "elt386l.bin", 0x10000, 0x8000, CRC(95fd5508) SHA1(a66cd78f52f3931c6f8486db0d39f4e55244dcea), ROM_SKIP(1))
	ROMX_LOAD( "elt386h.bin", 0x10001, 0x8000, CRC(90c0597a) SHA1(b67b39662a0bb8c0cde1635d3fd3c1f9fbaad3c0), ROM_SKIP(1))
ROM_END

// TD70N motherboard - Chipset: Citygate D100-011 - ISA16: 6 - Keyboard-BIOS: JETkey V5.0 - CPU/FPU: Am386SX/SXL-33, i387SX
ROM_START( td70n )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// 0: BIOS: AMI, Version 3.10 -  BIOS-String: 20-0100-009999-00101111-060692-CGD90-F / TD70N BIOS VERSION 3.10
	ROM_SYSTEM_BIOS( 0, "td70nv310", "TD70N V3.10" )
	ROMX_LOAD( "3cgm001.bin", 0x10000, 0x8000, CRC(8e58f42c) SHA1(56e2833457424d7176f8360470556629115493df), ROM_SKIP(1) | ROM_BIOS(0) )
	ROM_CONTINUE( 0x10001, 0x8000 )
	// 1: BIOS: AMI, Version 3.23T - BIOS-String: 20-0100-009999-00101111-060692-CGD90-F / BIOS VERSION 3.23T
	ROM_SYSTEM_BIOS( 1, "td70nv323", "TD70N V3.23T" )
	ROMX_LOAD( "3cgm002.bin", 0x10000, 0x8000, CRC(bca54fd8) SHA1(35b568c675e58965074162a93cf04918fc8d240f), ROM_SKIP(1) | ROM_BIOS(1) )
	ROM_CONTINUE( 0x10001, 0x8000 )
ROM_END

// TD70A and TD70AN motherboards - Chipset: Citygate D110-014, KS83C206Q - ISA8: 1, ISA16: 5 - Keyboard-BIOS: JETkey V5.0 - CPU: Am386SX-40
ROM_START( td70a ) // 8042 GATE-A20 ERROR - SYSTEM HALTED
	ROM_REGION16_LE(0x20000, "bios", 0)
	// BIOS: AMI, Version 2.60 - BIOS-String: 20-0100-009999-00101111-060692-CGD90-F / TD70A BIOS VERSION 2.60
	ROM_SYSTEM_BIOS( 0, "td70a", "TD70A" )
	ROMX_LOAD( "3cgm003.bin", 0x10000, 0x8000, CRC(1a92bf18) SHA1(520cd6923dd7b42544f8874813fbf81841778519), ROM_SKIP(1) | ROM_BIOS(0) )
	ROM_CONTINUE( 0x10001, 0x8000 )
	// 1: BIOS-String: 20-0100-009999-00101111-060692-CGD90-F / TD70A BIOS VERSION 2.60G
	ROM_SYSTEM_BIOS( 1, "td70an", "TD70AN")
	ROMX_LOAD( "bios.bin", 0x10000, 0x8000, CRC(0924948b) SHA1(e66b5223a7fb0b3ddb30ad0873ff099abf331262), ROM_SKIP(1) | ROM_BIOS(1) )
	ROM_CONTINUE( 0x10001, 0x8000 )
ROM_END

// MORSE KP 386SX V2.21 - Chipset: MORSE 91A300 (sticker), UMC UM82C206L - BIOS: AMI 386SX BIOS (Ver. 2.10) C-1216 - ISA8: 2, ISA16: 6
// BIOS-String: - 30-0200-ZZ1216-00101111-050591-386SX-0 - Keyboard-BIOS: AMI KEYBOARD BIOS PLUS C-1216 - CPU: AM-386SX/SXL-25, FPU: iN80287-12 - OSC: 8.000, 14.31818, 50.000 MHz
ROM_START( mokp386sx )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "386-morse_kp386sx.bin", 0x10000, 0x10000, CRC(f3a9c69f) SHA1(6e028a11f3770d7cda814dfa698f2ab5d6dba535))
ROM_END

// ID: Morse KP920121523 V2.20 - Chipset: UMC UM82C206L MORSE 91A311 91A310 - CPU: AM386DX/DXL-40, FPU socket provided - RAM: 8xSIMM30, Cache: 32K/64K/128K
// BIOS: AMI 386DX BIOS Ver. 2.11 C-1216 - Keyboard BIOS: KB-BIOS-VER F MEGATRENDS - OSC: 14.31818MHz, 80.000MHz - ISA16: 8, solder pads for a memory extension card
ROM_START( mokp386 ) // BIOS-String: 30-0200-ZZ1216-00101111-050591-KP386DX-0
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "386-morse-kp920121523.bin", 0x10000, 0x10000, CRC(9392e265) SHA1(1df163bef6cf73a1d2a40ac997b96f93d1f2f4d1))
ROM_END

// UNICHIP 386W 367C REV 1.0 - Chipset: UNIchip U4800-VLX/9351EAI/4L04F1914, HMC HM82C206 - CPU: AM386DX-40, FPU socket provided - ISA8: 1, ISA16: 5 - OSC: 14.31818
ROM_START( uni386w )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS: AMI 386C BIOS 2116295 -
	// BIOS-String: 40-0400-001107-00101111-111192-U4800VLX-0 / UNICHIP BIOS VER 2.0A 09/27/1993 - Keyboard-BIOS: AMI 386C BIOS KEYBOARD 2116295 -
	ROM_SYSTEM_BIOS(0, "ver20a", "Ver. 2.0A")
	ROMX_LOAD( "386-2116295.bin", 0x10000, 0x10000, CRC(7922a8f9) SHA1(785008e10edfd393dc39e921a12d1a07a14bac25), ROM_BIOS(0))
	// 1: AMI, 367 UNICHIP 386 BIOS VER 1.0 (1886636) / BIOS-String: 40-0300-001107-00101111-111192-U4800VLX-0
	ROM_SYSTEM_BIOS(1, "ver10", "Ver. 1.0")
	ROMX_LOAD( "3ucm002.bin", 0x10000, 0x10000, CRC(9f2e19da) SHA1(ef64c6ad9d02db849d29e3b998ca42b663656bad), ROM_BIOS(1))
ROM_END

// SCsxAIO - Chipset: Chips 82C236 (SCATsx), Acer M5105 A3E - On board: 2xCOM, Floppy, ISA
// BIOS-String: Peacock 386sx Ver. 2.0 24.03.92 30-0000-D01131-00101111-070797-SCATsx-8 - ISA16: 6
ROM_START( scsxaio )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "386-peacock_scsxaio.bin", 0x10000, 0x10000, CRC(54c3cacd) SHA1(b3c821b30052d0c771b5004a3746eb2cfd186c79))
ROM_END

// Shuttle 386SX REV 2.0A - Chipset: KU82335 SX042, Intel N82230-2 (Zymos); Intel N82231-2 (Zymos), BIOS: AMI 80386SX BIOS PLUS Ser #039747
// BIOS-String: - DINT-1216-073089-K0 / 386-BIOS AMI for MORSE 386SX Personal Computer
// Keyboard-BIOS: AMI 386 Keyboard BIOS PLUS Ser.# 039747, CPU: unreadable (SMD), FPU: empty socket - OSC: 32.000 MHz, 14.31818 - ISA8: 2, ISA16: 6
ROM_START( sh386sx20 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROMX_LOAD( "386-shuttle386sx-even_32k.bin", 0x10000, 0x8000, CRC(8b0c3d96) SHA1(73b6315928161a013cfe81b226606dfae5a8ef94), ROM_SKIP(1) )
	ROMX_LOAD( "386-shuttle386sx-odd_32k.bin", 0x10001, 0x8000, CRC(9c547735) SHA1(3cef5290324aab9d7523e98bf511eaea351e580d), ROM_SKIP(1) )
ROM_END

// Alaris Cougar - Chipset: OPTi 82C499 - ISA16: 5, ISA16/VL: 2
// BIOS: MR BIOS (r) V1.65 - CPU: 75MHz IBM Blue Lightning
ROM_START( alacou )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "cougrmrb.bin", 0x10000, 0x10000, CRC(c018f1ff) SHA1(92c4689e31b367baf42b12cad8800a851cc3e828))
ROM_END

// Alaris Tornado 2 - CPU: 486 - Chipset: Opti/EFAR/SMC - ISA16: 4, PCI: 3, ISA16/VL: 2 - On board: Floppy, 1xIDE, parallel, 2xserial
ROM_START( alator2 ) // unknown beep code LH-HL
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "tornado2.bin", 0x00000, 0x20000, CRC(2478136d) SHA1(4078960032ca983e183b1c39ae98f7cdc34735d0))
ROM_END

// DTK PEM 2530 - Chipset: VLSI 9032BT/217203/VL82C100-0C
// Board's original ROMs were damaged (Datatech dtk 386 V4.26 A1763), "original" ROMs came from another user, V3.10 ROMs from a different board
// ISA8: 2, ISA16: 5, Memory connector: 1 - OSC: 40.000 MHz - 14.31818 MHz
ROM_START( pem2530 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// Phoenix 80386 ROM BIOS PLUS Version 1.10 01 KENITEC TECHNOLOGIES
	ROM_SYSTEM_BIOS(0, "pem2530ori", "DTK PEM 2530 original")
	ROMX_LOAD( "386-dtk_pem-2530_bios-low.bin", 0x10000, 0x8000, CRC(d9aad218) SHA1(a7feaad2889820852e3543229b0b103288470732), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "386-dtk_pem-2530_bios-high.bin", 0x10001, 0x8000, CRC(550c4d77) SHA1(05aba1a98e738f9b706b5a8f09b5b6c86bd336e2), ROM_SKIP(1) | ROM_BIOS(0))
	// 80386 BIOS Version 3.10 Rev. 2.06 (BIOS not original, works in PEM 2530)
	// additional info from chukaev.ru54.com: CPUBT-S26361-D548 (Siemens?) - Chipset: VL82C320A, VL82C331, VL16C452B - CPU: NG80386SX-20, socket for 80387SX
	ROM_SYSTEM_BIOS(1, "pem2530", "DTK PEM 2530")
	ROMX_LOAD( "386-dtk pem-2530-high_32k.bin", 0x10000, 0x8000, CRC(56a822c0) SHA1(b65797c0f87a0815b393758af9c059e6d7172ae9), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "386-dtk pem-2530-low_32k.bin", 0x10001, 0x8000, CRC(8688d883) SHA1(c3034c8b343786cb89de48fb2f4992160414f89e), ROM_SKIP(1) | ROM_BIOS(1))
ROM_END

// SM 386-40F - MR BIOS (r) V1.40 - Ver: V1.40-FORX300
// Chipset: SIS 85C206, FOREX FRX36C200, FOREX FRX36C300
ROM_START( sm38640f )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "forex300.bin", 0x10000, 0x10000, CRC(8d6c20e6) SHA1(cd4944847d112a8d46612e28b97e7366aaee1eea))
ROM_END

// Soyo SY-019H and SY-019I BIOS-String: 30-0200-DH1102-00101111-070791-ETEQ386-0 / REV C3
// Chipset: SIS 85C206, ETEQ ET82C493, ET82C491
ROM_START ( sy019hi )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "ami_soyo_sy-19i.bin", 0x10000, 0x10000, CRC(369a040f) SHA1(3dbcbcb8b8a50717cae3b17f44ca1b7c394b75fc))
ROM_END

// PC-Chips M321 - RAM: 8xSIMM30, Cache: 8 sockets, 4 sockets occupied by CY71C199-25PC - CPU: AM386-DX40, FPU: socket provided - OSC: 80.000MHz, 14.31818
// Chipset: PCChips C206/306, CHIP6/4L04F1666, CHIP5/4L04F1282 (rev. 2.3, 2.5 and 2.7 boards) - ISA8: 2, ISA16: 6
ROM_START( pccm321 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// BIOS-String: 30-0201-ZZ1347-00101111-050591-M320-0
	ROM_SYSTEM_BIOS(0, "m321_23", "PCChips M321 Rev.2.3") // also on a rev. 2.5 board with C&T J38600DX-33, ULSI MathCo-DX33
	ROMX_LOAD( "pcchips_m321_rev2.3.bin", 0x10000, 0x10000, CRC(ca0542e4) SHA1(8af9f88e022f8115708178c6c0b313ea0423a2b5), ROM_BIOS(0) )
	// BIOS-String: 30-0100-001437-00101111-060692-PC CHIP-0
	ROM_SYSTEM_BIOS(1, "m321_27_1", "PCChips M321 Rev.2.7 #1")
	ROMX_LOAD( "3pcm002.bin", 0x10000, 0x10000, CRC(0525220a) SHA1(5565daea1db67fb2e6f5e7f5ddf5333569e74ab3), ROM_BIOS(1) )
	// BIOS-String: 30-0100-001437-00101111-060692-PC CHIP-0 - TRANS-AMERITECH ENTERPRISES, Inc.
	ROM_SYSTEM_BIOS(2, "m321_27_2", "PCChips M321 Rev.2.7 #2")
	ROMX_LOAD( "3pcm004.bin", 0x10000, 0x10000, CRC(d7957833) SHA1(b512d9fc404c4282fb964444aa70a9760edad7db), ROM_BIOS(2) )
	// BIOS-String: 30-0100-ZZ1437-00101111-070791-PC CHIP-0
	ROM_SYSTEM_BIOS(3, "m321_27_3", "PCChips M321 Rev.2.7 #3")
	ROMX_LOAD( "pcchips_m321_rev2.7_2.bin", 0x10000, 0x10000, CRC(1c529364) SHA1(a0cb0dc31b34377024efb3124f4167a8e1d748e6), ROM_BIOS(3) )
ROM_END

// Morse M3 V3.00 - Chipset: Morse 4L04F1282 + 4L04F1666 + 92A206S / possibly a rebranded OPTi 82C391 chipset - CPU: Am386DX/DXL-33,
// CPU (80386DX) and FPU socket (W3167) provided - RAM: 8xSIMM30, Cache: 4xMOSEL MS62256-A-25NC, 1xT6C6408-20 - BIOS: AMI 386DX BIOS PLUS Ver.3.00 386DX C-1216
// BIOS-string: 30-0201-ZZ1216-00101111-050591-386DX-0 - Keyboard-BIOS: AMI KEYBOARD BIOS PLUS C-1216 - OSC: 66.667MHz, 14.31818MHz - ISA8: 1, ISA16: 6
ROM_START( mom3v3 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "morse_m3_v3.00.bin", 0x10000, 0x10000, CRC(4748a084) SHA1(169696ea08f25e05928c48cc8328c67c51789f0d))
ROM_END

// PC-Chips M326
// Chipset: SARC RC4018A4/9324 and SARC RC6206A4/9408-AHS or SARC RC4018A4/9324 and RC4919A4-9323 (v5.5 board) or SARC RC2016A4-9320 and RC4019A4-9324 (v5.3)
ROM_START( pccm326  )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: Award Modular BIOS 4.50
	ROM_SYSTEM_BIOS(0, "pccm326", "PCChips M326 V5.2") //  BIOS reports a 66MHz 386DX original board has a TI TX486DLC/E-40PCE and IIT 4C87DLC-40 CPU/FPU combo
	ROMX_LOAD( "m326_v5.2_m601-326.bin", 0x10000, 0x10000, CRC(cca6a443) SHA1(096c8bfa000c682d6c801da27c7fd14243ebb63b), ROM_BIOS(0) )
	// 1: BIOS-String: 40-0100-001437-001001111-080893-4386-0 / Release 10/01/93 - also on an "M601 Rev. 1.3" board with a i486DX-33 (BIOS AMI AB0077440 - Keyboard-BIOS: Regional HT6542)
	ROM_SYSTEM_BIOS(1, "m326r53", "PC-Chips M326 Rev. 5.3")
	ROMX_LOAD( "m326_rev.5.3.bin", 0x10000, 0x10000, CRC(6c156064) SHA1(362ce5a2333641083706a878b807ab87537ca1e6), ROM_BIOS(1) )
	// 2: BIOS: AMI; 08/08/93; Release 10/01/93
	ROM_SYSTEM_BIOS(2, "m326", "M326") // no display
	ROMX_LOAD( "3sam005.bin", 0x10000, 0x10000, CRC(f232cd4b) SHA1(e005aa3a7d160223fb2912cf2cd5cc8af49e79a5), ROM_BIOS(2) )
ROM_END

// Elitegroup UM386 Rev. 1.1 - UMC UM82C482AF, UM82C391A, UM82C206F - OSC: 80.000MHz, 14.31818MHz
// BIOS: AMI-1131 E-91844945 - Keyboard-BIOS: AMI KEYBOARD-BIOS-VER-F / Intel P8942AHP
// RAM: SIMM30x8, Cache: 9xW2464AK-20, 1x ISA8, 7xISA16 - CPU: AM386DX/DXL-40
ROM_START( ecsum386 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// BIOS-String: 30-0500-D01131-00101111-070791-UMCWB-0 / UM386 V1.1 03-06-92
	ROM_LOAD( "ami_um386_rev1.1.bin", 0x10000, 0x10000, CRC(81fe4297) SHA1(efb2ba2be6f08cb487ee1b867a2456ed6b5975ad))
ROM_END


// ***** 386sx motherboards using the ALi 1217 chipset

ROM_START( alim1217 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	//0: BIOS-String: 30-0100-ZZ1453-00101111-070791-ACER1217-0 / CPU: 386SX-40
	ROM_SYSTEM_BIOS( 0, "m121701", "ALi M1217 #1" )
	ROMX_LOAD( "3alm005.bin", 0x10000, 0x10000, CRC(8708727c) SHA1(9be25b1af080aee863441cf0d25d0f984accb086), ROM_BIOS(0))
	// 1: BIOS-String: 30-0100-D01131-00101111-070791-ALI1217-F
	ROM_SYSTEM_BIOS( 1, "m121702", "ALi M1217 #2" )
	ROMX_LOAD( "3alm006.bin", 0x10000, 0x10000, CRC(e448c436) SHA1(dd37127920a945f1273c70c41e79e4fc70a5de01), ROM_BIOS(1))
	// 2: BIOS-String: 30-0501-D81105-00101111-070791-ACER1217-0 - 386SX NPM/33,40-A0(2) 05/12/1993
	ROM_SYSTEM_BIOS( 2, "m919a00", "386SX NPM/33,40-A0" )
	ROMX_LOAD( "m919a00_npm-40.bin", 0x10000, 0x10000, CRC(4f330d82) SHA1(08224c7bcfb2a859b682bf44ac1ac7fd9f2ade78),ROM_BIOS(2))
	// 3: GMB-386SAT - Am386SX-40, IIT 3C87SX-33 - flashing "K/B controller incorrect" - Chipset: ALi M1217-40 - ISA8: 1, ISA16: 5
	// BIOS: AMI 386SX BIOS AA1280569 - BIOS-String: 30-0100-428036-00101111-111192-ALI1217-F - Keyboard-BIOS: JETkey V5
	ROM_SYSTEM_BIOS( 3, "gmb386sat", "GMB-386SAT_V1.0" )
	ROMX_LOAD( "gmb-386sat_v1.0.bin", 0x10000, 0x10000, CRC(59ecc773) SHA1(f2007fce76b3a91f51bfb5f43c1539d5ae06d35f), ROM_BIOS(3))
	// 4: ML-765 V2 - BIOS: AMI AA0508210 - BIOS-String: 30-0103-DJ1113-00101111-070791-ACER1217-0
	ROM_SYSTEM_BIOS( 4, "ml765", "ML-765" )
	ROMX_LOAD( "3alm011.bin", 0x10000, 0x10000, CRC(27a799d4) SHA1(873cf5968923c5a655ff32f3d968b7cddcb08e76), ROM_BIOS(4))
	// 5: BIOS: AA0030659 - BIOS-String: 30-0100-428029-00101111-070791-ACER1217-0
	ROM_SYSTEM_BIOS( 5, "m121703", "ALi M1217 #3" )
	ROMX_LOAD( "3alm012.bin", 0x10000, 0x10000, CRC(5b822a2a) SHA1(e61b27f06cfec54973fbabff277bde617847b1e2), ROM_BIOS(5))
	// 6: 303N1 - Chipset: ALi M1217, M5818 - BIOS: MR BIOS MR001-SX V.1.41 - Keyboard BIOS: JETkey V3.0 - CPU: i386sx, FPU socket provided
	// RAM: 4xSIMM30 - OSC: 80.000MHz - ISA8: 1, ISA16: 5
	ROM_SYSTEM_BIOS( 6, "acer310", "Acer 310" )
	ROMX_LOAD( "3alr001.bin", 0x10000, 0x10000, CRC(b45e5c73) SHA1(81ef79faed3914ccff23b3da5e831d7a99626538), ROM_BIOS(6))
	// 7: 8517 V3.2 - Chipset : ALI M1217-40, M5818 A1 - CPU: AMD Am386SX-40, FPU: ULSI Advanced Math Coprocessor SX/SLC US83S87 - BIOS : MR 386SX BIOS V.1.40 SX M1217
	// BIOS-String: - Keyboard-BIOS: JETkey V3.0 - OSC: 80.000MHz - ISA8: 1, ISA16: 5
	ROM_SYSTEM_BIOS( 7, "8517v32", "8517 V3.2" ) // MR BIOS (r) V1.40
	ROMX_LOAD( "m1217mrb.bin", 0x10000, 0x10000, CRC(8ac66b9d) SHA1(909e5129066d1b0563b03c4834f9894c9291c287), ROM_BIOS(7))
ROM_END

// Computechnik ASC486SLC, 386sx slot CPU - Chipset: ALi M1217-40, PIC P4020 - CPU: TI 486SLC/E -33P AF - RAM: 4xSIMM30
// OSC: 66.000 - BIOS: AMI 386SX BIOS AA1330085 - Keyboard-BIOS: AMI MEGAKEY - On board: 2xser, par, Floppy, IDE
// BIOS-String: 30-0100-001588-00101111-111192-ALI1217-0 / A-03
ROM_START( asc486slc )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD("asc486slc.bin", 0x10000, 0x10000, CRC(aadbd3b6) SHA1(635df5b3cfd4525bc8ad9067703c8860dacd987a))
ROM_END

// Manufacturer/Identifier: ECS 8517 v3.3 - Chipset: ALI-M1217-40, M5818 A1 9347 TS 10 A45296 - CPU: 386sx, FPU socket provided
// RAM: 4xSIMM30 - BIOS Version: AMI 386sx BIOS AA1021639 - Keyboard BIOS: JETkey V5 - BIOS String: 40-0000-zz1218-00101111-070791-ALI1217-0
// Board marking: 8517 V3.3 and 200-03690-341-4 - OSC: 80.000MHz - ISA8: 1, ISA16: 5
ROM_START( ecs8517 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD("8517_v33.bio", 0x10000, 0x10000, CRC(c2b18ace) SHA1(fb10108a8d7a4782442f7a518e0ebab01f2e54bd))
ROM_END


// 386 motherboards using the ALi M1419 chipset

ROM_START( alim1419 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: KMC-A419-8 VER 1.0 - Chipset: M5818 A1, ALi M1421 A1, M1419 A0 - OSC: 14.31818, 80.000MHz
	// BIOS-String: 40-0100-001453-00101111-121291-ALI1419-0 / 486DLC/386DX, ISA8: 1, ISA16: 6
	ROM_SYSTEM_BIOS ( 0, "kmca419", "KMC-A419-8 VER 1.0" )
	ROMX_LOAD( "3alm010.bin", 0x10000, 0x10000, CRC(733d0704) SHA1(b5724f98047e95ea41aaa396a0374357f20cf2de), ROM_BIOS(0))
	// 1: BIOS: Award Modular BIOS v4.50 - BIOS-String: 06/22/93-ACER-M1419-214k6000-00
	ROM_SYSTEM_BIOS( 1, "alim141901", "ALi M1419 #1" )
	ROMX_LOAD( "3alw001.bin", 0x10000, 0x10000, CRC(15bd5c90) SHA1(abe2a8613d2950b27701468144fe9de8063d6e57), ROM_BIOS(1))
	// 2: 386AC P102 - CPU: Am386DX-40, FPU socket provided - Chipset: ALi M1419 A1, M1421 A1 - BIOS: AMI 386DX ISA BIOS AA1226493 -
	// Keyboard-BIOS: JETkey V3 - RAM: 8xSIMM30, Cache: 9x28pin - ISA8: 8, ISA16: 7 - OSC: 14.31818, 80.000MHz
	// BIOS-String: 40-0102-001128-00101111-121291-ALI1419-0
	ROM_SYSTEM_BIOS( 2, "386acp102", "386AC P102")
	ROMX_LOAD( "386ac_p102_ami_aa1226493.bin", 0x10000, 0x10000, CRC(43ba9775) SHA1(9f80ebf1e7ef1d7e5b7c2aad5839b4f982db75d1), ROM_BIOS(2))
	//3: ID: Unknown C3404 Rev:B - Chipset: ALI M1419 A1, M5818 A1, another unreadable (silk screen: M1421) - RAM: 8xSIMM30, Cache: 8x28pin DIP, 64K or 256K
	// CPU: AMD Am386DX-40, solder pads for Intel 80386 socket provided, FPU socket provided - BIOS: AMI 386DX ISA BIOS AA1458354
	// BIOS Version: "386DX Subversion 13.19.02 3/23/1993" - BIOS String: 40-0100-001374-00101111-121291-ALI1419-F - Keyboard BIOS: JETkey V3
	// ISA8: 1, ISA16: 6 - OSC: 14.31818, 80.000 MHz
	ROM_SYSTEM_BIOS( 3, "c3404", "C3404") // displayed BIOS-String in MAME has -0 instead of -F: this denotes the keyboard controller version http://www.idhw.com/textual/guide/inst_mobo_ami.html
	ROMX_LOAD( "386c3404.bio", 0x10000, 0x10000, CRC(8005f1f6) SHA1(1c78a7a3f134fd1299b48cd12b08b013c212fa59), ROM_BIOS(3))
ROM_END


// ***** 386 Motherboards using the Ali M1429 A1 and M1431 A2 chipsets ... they hang before initializing the graphics card

ROM_START( alim1429 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0:
	ROM_SYSTEM_BIOS( 0, "386ali", "386 board with Ali chipset" )
	ROMX_LOAD( "386_ali_ami_511767.bin", 0x10000, 0x10000, CRC(3c218db4) SHA1(785ea7c36e8be5e7410524e90170d4985cbc9c24), ROM_BIOS(0))
	// 1: Seritech SER-386AD III (written on the underside of the board) - CPU: AMD Am386DX-40 - ISA16: 5
	// BIOS : AMIBIOS 04/04/1993 Ser.# 579092 - BIOS-String : 40-0212-001133-00101111-040493-ALI1429-F - Keyboard BIOS: Regional HT6542
	ROM_SYSTEM_BIOS( 1, "ser386ad", "SER-386AD III" )
	ROMX_LOAD( "ser386ad3.bin", 0x10000, 0x10000, CRC(d80d6deb) SHA1(9f889f7464255431c13ac91d7df31b325447fef5), ROM_BIOS(1))
	// 2: ISA8: 1, ISA16: 5, BIOS-String: 40-0103-001256-00101111-080893-ALI1429-H, BIOS: AMIBIOS, 08/08/93, 386DX ISA BIOS, AA2722981
	ROM_SYSTEM_BIOS( 2, "revb", "REV:B")
	ROMX_LOAD( "3alm001.bin", 0x10000, 0x10000, CRC(56ea4d9d) SHA1(0633f78a0013a62be974233a3cad6a5d3cbe90d1), ROM_BIOS(2))
	// 3: CPU: 386DX-40 -
	ROM_SYSTEM_BIOS( 3, "alim142901", "ALi M1429 #1" )
	ROMX_LOAD( "3alm007.bin", 0x10000, 0x10000, CRC(b72d754a) SHA1(364a976eac61bc923b76ccddd13f80e0727e5df5), ROM_BIOS(3))
	// 4: REV:8 - CPU: 386DX-40
	ROM_SYSTEM_BIOS( 4, "alim142902", "ALi M1429 #2" )
	ROMX_LOAD( "3alm008.bin", 0x10000, 0x10000, CRC(4cb1052d) SHA1(995a590beb0654c5e784f10019c10bd4b0278d9b), ROM_BIOS(4))
	// 5: ??? REV 2.3, Chipset: Asaki 3A029, 3A031 (= ALi 1429) - BIOS: AMI 386 BIOS S/N: 254468
	// BIOS-String: 40-0215-001926-00101111-040493-ALI1429 - ISA8: 1, ISA16: 5
	ROM_SYSTEM_BIOS( 5, "asaki", "386 with Asaki chipset" )
	ROMX_LOAD( "3asm001.bin", 0x10000, 0x10000, CRC(57c72c4d) SHA1(934223fcd39533bca2e7e57406b1800d9e900ef0), ROM_BIOS(5))
ROM_END

// Daewoo AL486V-D Rev:1.1 - BIOS/Version: AMI v299 08/08/93, BIOS-String: 40-0100-001131-00101111-080893-ALI1429 - Keyboard-BIOS: MEGAKEY
// BIOS: AMI v1.9 299 WinPro-d S/No. E-94237376 - OSC: 14.31818
ROM_START( al486vd ) // this is a 386 class board despite the name
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "al486v-d_v299.bin", 0x10000, 0x10000, CRC(75c75d58) SHA1(50e314cdefe39e8e6f74b9b045a15cc53b3f16ba))
ROM_END


// 386 motherboards using the Chips & Technologies P82C351, P82C355, P82C356 chipset

// ABIT FU340 - 6x 16-bit ISA + 2x 8-bit ISA - RAM: SIMM30x8, Cache: 32/64/128/256KB with TAG (TULARC info)
// BIOS-String: 30-0200-D01247-00101111-050591-PEAKDM_B-0 / FU340 REV-B PAGE MODE BIOS
ROM_START( fu340 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "ami_abit_fu340.bin", 0x10000, 0x10000, CRC(9ea90d90) SHA1(091bdae7b1e36ac5168823d80d5907af2a95e583))
ROM_END

// GES 9051N-386C VER -0.01 - CPU/FPU: i386DX-33, i387DX 16-33 - Chipset: Chips F82C351, F82C355, F82C356 - BIOS: AMI 386DX ISA BIOS (AA0365368)
// BIOS-String: 30-1113-002101-00001111-050591-PEAKDM_B-0 / GES 9051N BIOS VERSION 2.0 - ISA8: 3, ISA16: 5
ROM_START( ges9051n )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "3ctm001.bin", 0x10000, 0x10000, CRC(7f03f606) SHA1(d03d5b6541bc7f41d78159f82aa8057229516c37))
ROM_END


// ***** 386sx motherboards using the Chips SCAMPSX chipset

// ANIX CH-386S-16/20/25G P/N:001-0386S-016 VER 1.0 - Chipset: CHIPS F82C836 - BIOS: AMI 386sx BIOS PLUS S/NO. 141334
// BIOS-String: 30-0100-D01425-00101111-050591-SCAMPSX-0 - Keyboard-BIOS: Intel/AMI - CPU: Intel (SMD), label not readable - FPU: socket available - ISA16: 6 - OSC: 14.31818 - 32.000 MHz
ROM_START( anch386s )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "386-ch-386s.bin", 0x10000, 0x10000, CRC(8902c64b) SHA1(3234bac6240a3a0bd05302c9ca587f5ae083f2f4))
ROM_END

// 80386SX-VH-COM - Chipset: VLSI VL82C311 VL82C113 - BIOS: Award 386SX-BIOS - BIOS-String: 386SX Modular BIOS v3.15 Copyright(c)1984-91 Award Software Inc.
// CPU: i386sx-25, FPU socket provided - RAM: 8xSIMM8 - OSC: 16MHz, 14.31818, 50.000MHz - ISA8: 2, ISA16: 6
ROM_START( 386sxvhcom )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "80386sx-vh-com.bin", 0x10000, 0x10000, CRC(65f5d279) SHA1(81c40ad1f7dde3235a074d97768ed7e09cf05f52))
ROM_END

ROM_START( scamp386sx )
	ROM_REGION16_LE(0x20000,"bios", 0)
	// 0: BIOS-String: 30-0100-D61204-00101111-050591-SCAMPSX-0 / MB-1316/20/25VST
	ROM_SYSTEM_BIOS(0, "mb386sx", "mb386sx-25spb") // VLSI SCAMPSX
	ROMX_LOAD( "386sx_bios_plus.bin", 0x10000, 0x10000, CRC(f71e5a8d) SHA1(e73fda2547d92bf578e93623d5f2349b97e22393), ROM_BIOS(0))
	// 1: BIOS-String: 30-0400-428027-00101111-070791-SCMPSX-0 / VLSI SCAMP 386SX 16/20/25MHz - seen on a PC-Chips/Amtron board
	ROM_SYSTEM_BIOS(1, "scamp01", "VLSI SCAMPSX #1")
	ROMX_LOAD( "ami_386sx_vlsi_scamp_070791.bin", 0x10000, 0x10000, CRC(082d071c) SHA1(69af9a951f138146036b3c9ac3761cc6589b6cf5), ROM_BIOS(1))
	// 2: Dataexpert (Unknown model) - Chipset: VLSI VL82C311-25FC2 (SCAMPSX), HM6818P - BIOS: AMI 05/05/1991 on a 64KB "Fairchild FM27C512"
	// BIOS-String: 30-0100-D41107-00101111-050591-SCAMPSX-0 - SCAMP 386SX WITHOUT COMBO - Keyboard BIOS: 247076 - CPU: AM386SX/SXL-25 - OSC: 50.000MHz, 16.000MHz, 14.31818 - ISA16: 7
	ROM_SYSTEM_BIOS(2, "datax386sx", "Dataexpert 386sx")
	ROMX_LOAD( "bios.bin", 0x10000, 0x10000, CRC(0ba46059) SHA1(b152796e9ace0cd17c413df14d989b9cb23aa529), ROM_BIOS(2))
	// 3: VLSI 311 386SX VER 1.0 - CPU: AM386 SX/SXL-25 - Chipset: VPL201, VPL101 - BIOS: AMI 386sx 409425 - OSC: 50.000 MHz - ISA16: 6
	// BIOS-String: 30-0400-D41107-00101111-070791-SCMPSX-0
	ROM_SYSTEM_BIOS( 3, "vlsi311", "VLSI 311")
	ROMX_LOAD( "vlsi_311_386sx_ver_1.0_bios.bin", 0x10000, 0x10000, CRC(98056235) SHA1(3a3ff07808c4d43e4935c7463741e3ed8e515af9), ROM_BIOS(3))
	// 4: BIOS-String: 30-0100-ZZ1379-00101111-050591-SCAMPSX-0 / SCAMP 386SX - Chipset: (VLSI ???) 82C310, 82C311 - CPU: 386SX-25
	ROM_SYSTEM_BIOS(4, "scamp02", "VLSI SCAMPSX #2")
	ROMX_LOAD( "3vlm002.bin", 0x10000, 0x10000, CRC(4d2b27b3) SHA1(3c67d7bd507ceb4d1762866f69c2cb94cd799a15), ROM_BIOS(4))
ROM_END


// ***** 386 Motherboards using the Chips & Technologies CS8230 chip set: P82C301C, P82C302C, P82A303, P82A304, 2x P82B305, P82A306 A, P82C206

// ECS (Elitegroup) 386A - Chipset: CHIPS P82C301/2/5 + P82A303/4/6 + P82C206 - BIOS/Version: AMI 386-BIOS / 386A V1.1 05-19-90 (2 x 27C256)
// BIOS String: EC&T-1131-040990-K8 - Keyboard BIOS: AMI Keyboard BIOS PLUS - CPU: Intel 80386DX-25, FPU: 80387DX-25 - RAM: 8xSIMM30
// ISA8: 2, ISA8/RAM: 1, ISA16: 5 - OSC: 32.0000MHz, 50.0000MHz, 14.31818
ROM_START( ecs386a ) // hangs after initialising the graphics card
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROMX_LOAD( "386a012l.bin", 0x10000, 0x8000, CRC(8a067777) SHA1(aadc84867155e4e167a0380bd409dba62fd238a1), ROM_SKIP(1))
	ROMX_LOAD( "386a012h.bin", 0x10001, 0x8000, CRC(525fc3bd) SHA1(3ab8cb5989933edc6aa9f99fcab518307b60552b), ROM_SKIP(1))
ROM_END

ROM_START( cs8230 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: EC&T-1332-040990-K0
	ROM_SYSTEM_BIOS(0, "cs823001", "CS8230 #1")
	ROMX_LOAD( "ami_386_cs8230_chipset.bin", 0x10000, 0x10000, CRC(1ee766d0) SHA1(75dba3c9817dfe6caca46f5f4f2f1d76ba88d3c7), ROM_BIOS(0) )
	// 1: BIOS-String: EC&T-1197-022589-K0
	ROM_SYSTEM_BIOS(1, "cs823002", "CS8230 #2")
	ROMX_LOAD( "3ctm004l.bin", 0x10000, 0x8000, CRC(b6efc361) SHA1(88d89bf5e7c57ffe4751e14220ac82a2d0a12994), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "3ctm004h.bin", 0x10001, 0x8000, CRC(f26c2672) SHA1(1d3a2554bbf3dc554970e0d62d9c5fad24977f55), ROM_SKIP(1) | ROM_BIOS(1) )
ROM_END

// ECS-386/32 - OSC: 40.000MHz, 32.000MHz, 14.318MHz - CPU: 386DX-20, FPU socket provided
// 8x SIMM, 5x 16-bit ISA, 2x 8-bit ISA, 1x 32-bit proprietary memory expansion slot
ROM_START( ecs38632 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: EC&T-1131-030389-K0
	ROM_SYSTEM_BIOS( 0, "030389", "030389")
	ROMX_LOAD( "ami_ecs-386_32_lo.bin", 0x10000, 0x8000, CRC(e119d6a4) SHA1(bcc6164173b44832b8ebfa1883e22efc167e2cd4), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "ami_ecs-386_32_hi.bin", 0x10001, 0x8000, CRC(e3072bf8) SHA1(74eec72e190f682cfd5ae5425ebdc854e0ba7bc9), ROM_SKIP(1) | ROM_BIOS(0) )
	// 1: BIOS-String: EC&T-1131-092588-K0
	ROM_SYSTEM_BIOS( 1, "092588", "092588")
	ROMX_LOAD( "ami_1131_bios_l.bin", 0x10000, 0x8000, CRC(145c3905) SHA1(9ee2615982e28082971bae8ef8a1f936313ac8c8), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "ami_1131_bios_h.bin", 0x10001, 0x8000, CRC(73d57778) SHA1(176c90134540d5054c99c077126c7c5a65199175), ROM_SKIP(1) | ROM_BIOS(1) )
ROM_END

// SY-012 16/25 386MB VER: 5.2 - Chipset: Chips P82C301C; P82A306; P82A303; P82C206; P82A304; P82C302; P82B305
// BIOS: AMI 386 BIOS 10084 - BIOS-String: DC&T-1102-082588-K0 - CPU: i386DX-33, ISA8: 2, ISA16: 5, Memory: 1
// OSC: 14.31818 - 20.000 MHz - 50.000 MHz - 32.000 MHz
ROM_START( sy012 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROMX_LOAD( "386-sy-012-l_32k.bin", 0x10000, 0x8000, CRC(6ab197f4) SHA1(7efd9033af3a0b36bc5be64cb28c6218cda4d13c), ROM_SKIP(1) )
	ROMX_LOAD( "386-sy-012-h_32k.bin", 0x10001, 0x8000, CRC(61aedfdb) SHA1(0f492dc8102386a1c475c5637fb7853d81d3efb6), ROM_SKIP(1) )
ROM_END

// Goldstar 611-606A - Chipset: CHIPS P82C206 P82C301 P82A303 P82C302 P82A304 2xP82A305 -
// OSC: 14.318 - 9.6000000 MHz - 40.000000 MHz - 16.000000 MHz
// BIOS: TI CMC3000 - BIOS-String: Phoenix 80386 ROM BIOS PLUS Version 1.10 01 - release 2.7B
ROM_START( gs611606a )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROMX_LOAD( "386-goldstar-e_32k.bin", 0x10000, 0x8000, CRC(3f358257) SHA1(1570f3de1955895c29c1c4240e1cd47aadff1be0), ROM_SKIP(1) )
	ROMX_LOAD( "386-goldstar-o_32k.bin", 0x10001, 0x8000, CRC(c5d75635) SHA1(70ceb4089bfd3af6853c3d6e28dbded0c43f6a40), ROM_SKIP(1) )
ROM_END

// DFI386-20.REV0 - Chipset: Chips 2xP82B305 P82A304, P82C302 P82C301 P82C206, two unreadable - initializes graphics card then hangs
// BIOS: AMI 386 BIOS PLUS Ser.#: 102856 - Keyboard BIOS: AMI 386 BIOS PLUS Ser.#:102856
// CPU: i386DX-20 - ISA8: 1, ISA16: 5, Memory: 1 - Memory card shown in photos
// OSC: OSC1: 14.31818, OSC2: 16.000MHz, OSC3: unreadable, OSC4: 40.000MHz
ROM_START( dfi386 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROMX_LOAD( "386-dfi-386-20_even_32k.bin", 0x10000, 0x8000, CRC(2d1309f8) SHA1(a75816b97d1f763dba39bdccf8e58729a58b0e56), ROM_SKIP(1) )
	ROMX_LOAD( "386-dfi-386-20_odd_32k.bin", 0x10001, 0x8000, CRC(1968fe11) SHA1(b5662daa57751859d2cfa7740f708277cbe35080), ROM_SKIP(1) )
ROM_END


// ***** 386 Motherboards using the Forex FRX36C300 + FRX46C402; SiS 85C206 chipset

// Chipset: FOREX FRX46C402 FRX36C300 SIS 85C206 SiS 85C206 - CPU: Intel 80386DX-16 - ISA16: 7, ISA16/Memory: 1 - OSC: 66.000MHz
ROM_START( frxc402 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS: AMI 386 BIOS PLUS - Ser. 006707 - BIOS-String: 30-0400-ZZ1266-00101111-070791-FORX-0 / FRX/386DX CACHE SYSTEM
	ROM_SYSTEM_BIOS(0, "frx386", "FRX/386")
	ROMX_LOAD( "386-forex.bin", 0x10000, 0x10000, CRC(4a883c14) SHA1(1c2de190ccd152ff894f9fd128e028d4fa63109a), ROM_BIOS(0))
	// 1: Chipset: Forex FRX36C300 + FRX46C402; IMP 82C206 - ISA16: 8, memory extension connector on board but not fitted
	// BIOS: AMI - BIOS-String: - 30-0400-ZZ1139-00101111-070791-FORX-0, FRX/386DX CACHE SYSTEM - Keyboard BIOS: Intel P8942HP with AMI KB-BIOS-VER-F - OSC: 14.31818MHz, 66,667MHz
	ROM_SYSTEM_BIOS(1, "frximp", "Forex 386 with IMP chip")
	ROMX_LOAD( "386-imp82c206p.bin", 0x10000, 0x10000, CRC(6f340961) SHA1(393720e1bfe3d323a34106992a65dd593284bf95), ROM_BIOS(1))
ROM_END

// RAM: 8xSIMM30, Cache: 10 sockets, 5xATT7C199P occupied - ISA8: 2, ISA16: 6 - CPU: AM386DX/DXL-40, FPU: IIT 3C87-25
// OSC: 80.000MHz, 14.318180MHz - Keyboard-BIOS: AMI 386 BIOS ZA902884
ROM_START( smih0107 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// BIOS-String: 30-0400-428005-00101111-070791-FORX-0 / BIOS ID SMIH0107 / IT9112
	ROM_LOAD( "ami_smih0107.bin", 0x10000, 0x10000, CRC(970bb0c0) SHA1(4a958887485f7239d25fa7b0c98569b97ce93800))
ROM_END


// ***** 386 Motherboards using the Forex FRX46C402 + FRX46C411 + SiS 85C206 chipset

// PT-581392 - CPU: AMD 386DX-40 FPU: ULSI Advanced Math Coprocessor DX/DLC 40MHz US83C87
// BIOS : AMI 07/07/1991, on a 27C512 type EPROM (64KB) Ser.# 007139, BIOS-String : 30-0400-ZZ1101-00101111-070791-FORX-0 FRX/386DX CACHE SYSTEM
// Keyboard-BIOS: AMI, Ser.# 007139 - OSC: 14.31818, 80.000MHz - ISA16: 8
ROM_START( pt581392 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "pt-581392 386dx.bin", 0x10000, 0x10000, CRC(389a93de) SHA1(8f1320b1d163167272cfad073f58c355e31fcf6f))
ROM_END

// Micro-Express Inc. Forex 386 Cache - Chipset: Forex FRX46C402, FRX46C411, Morse 92A206S - Keyboard BIOS: Lance LT38C41
// BIOS: EPROM, AMI 386 BIOS, #ZA605315 - CPU: AM386DX-40 - OSC: 66.6670MHz - ISA8: 2, ISA16: 5
// BIOS-String: 30-0701-001585-00101111-121291-microtel-0 / Microtel Computer products present C3HF/09/92
ROM_START( frx386c )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "forex386.bin", 0x10000, 0x10000, CRC(007b5565) SHA1(cf749fe05cacebb2230cd7493523ae55e80eea8b))
ROM_END


// ***** 386sx motherboards using the Headland HT18/C chipset
// moved here from 286, original comment: not a bad dump, sets unknown probably chipset related registers at 0x1e8 before failing post
ROM_START( ht18c )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// 0: (BIOS release date:: 07-07-1991) - Chipset: Headland HT18/C
	ROM_SYSTEM_BIOS(0, "ami181", "AMI HT 18.1")
	ROMX_LOAD( "ht18.bin", 0x10000, 0x10000, CRC(f65a6f9a) SHA1(7dfdf7d243f9f645165dc009c5097dd515f86fbb), ROM_BIOS(0) )
	// 1: CPU: 386SX-25 - BIOS: AMI; 12/12/91
	ROM_SYSTEM_BIOS(1, "ami182", "AMI HT 18.2")
	ROMX_LOAD( "3hlm001.bin", 0x10000, 0x10000, CRC(b1434d6f) SHA1(1863dd60ad2b494141b4b30fe7b02f454bec82a3), ROM_BIOS(1) )
	// 2: CPU: 386SX-25 - BIOS: AMI; 07/07/91
	ROM_SYSTEM_BIOS(2, "ami183", "AMI HT 18.3")
	ROMX_LOAD( "3hlm002.bin", 0x10000, 0x10000, CRC(10a78d11) SHA1(0500d92e2691164bdc5c71b3d6fd0a154f7279d4), ROM_BIOS(2) )
	// 3: CPU: 386SX-25 - BIOS: AMI; 04/30/91
	// BIOS-String: 30-01]1-ZZ1372-00101111-0403091-HT18SX-0
	ROM_SYSTEM_BIOS( 3, "ami184", "AMI HT 18.4") // marked as BAD_DUMP for the "]" in the BIOS string ... and because it actually runs :)
	ROMX_LOAD( "3hlm003.bin", 0x10000, 0x10000, BAD_DUMP CRC(50f7a543) SHA1(8962f7ce2fc5c60059894cae04cf5fccd6cee279), ROM_BIOS(3) )
	// 4: MBA-025 - Chipset: Headland HT18/B, HM6818A - BIOS: AMI 386SX BIOS PLUS T.B 238958 - BIOS-String: 30-0100-009999-00101111-043091-HT18SX-0
	// Keyboard-BIOS: AMI Keyboard BIOS PLUS T.B. 238958 - CPU: AMD AM386 SX/SXL-25 - FPU: empty socket - OSC: 32.000 MHz - 50.000 MHz - 14.31818 - ISA8: 1, ISA16: 6
	ROM_SYSTEM_BIOS( 4, "mba025", "MBA-025" )
	ROMX_LOAD( "386-mba-025-low_32k.bin", 0x10000, 0x8000, CRC(4df55219) SHA1(7dc1adb130ae8c3c88e2c58bde6e3b793fa0c78e), ROM_SKIP(1) | ROM_BIOS(4))
	ROMX_LOAD( "386-mba-025-high_32k.bin", 0x10001, 0x8000, CRC(0406fdc9) SHA1(ee21d584c98b0c11ec2cfb609de83c38b0a893c7), ROM_SKIP(1) | ROM_BIOS(4))
ROM_END


// ***** 386 Motherboards using the Macronix MX83C305(A)(FC), MX83C06(A)(FC) chipset

// CACHING TECH CORPORATION C386MX - Chipset: MX83C306 MX - BIOS: AMI - BIOS-ID: 31-0100-001190-00101111-121291-MXIC
// CPU: 386DX, FPU: Cyrix - RAM: 8xSIMM30 - ISA8: 2, ISA16: 6
ROM_START( ctcc386mx ) // nine short beeps, no display
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "c386mx.bio", 0x10000, 0x10000, CRC(9b24ce11) SHA1(d027da188cbdfbe34b279bd3bd84eccda75b4a5a))
ROM_END

// TAM/33/40-MA0 (CM318R00,M31-R00) - Chipset: MX83C305, MX83C306 - CPU: AMD Am386DX-40 - ISA16:8
// OSC: 80.000MHz - 14.31818 - BIOS: AMI 386 BIOS PLUS S/N OA2050592 - BIOS-String: 31-0100-001105-00101111-121291-MXIC-0 - 386DX/Cx486DLX TAM/33,30-MA0/MA01, 09/10/1992
ROM_START( tam3340ma0 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "tam_33_40-ma0.bin", 0x10000, 0x10000, CRC(56411a9f) SHA1(a6c80ea531912b758fd5b573d4fa125172cacce7))
ROM_END

// Octek Jaguar V rev.1.4 - Chipset: MX83C: MX83C305FC, MX83C306FC
// CPU: AMD 386DX-40, FPU socket provided - OSC: 80.000MHz, 14.31818
ROM_START( ocjagv )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: MR BIOS (r) V1.40
	ROM_SYSTEM_BIOS(0, "jagvmr14", "Jaguar V MR-BIOS 1.40")
	ROMX_LOAD( "bios.bin", 0x10000, 0x10000, CRC(a552d6ad) SHA1(91bae14c3ec7edbc9ef240fec1be17f3582d7ec2), ROM_BIOS(0))
	//1: AMI BIOS// BIOS: AMI 386DX ISA BIOS AA0797325 - BIOS-String: 31-0100-426069-00101111-121291-MXIC-0 MX-DIR_001
	// Keyboard-BIOS: Intel
	ROM_SYSTEM_BIOS(1, "jagvami", "Jaguar V AMI BIOS")
	ROMX_LOAD( "octek_jaguar_v_ami_bios_isa386dx.bin", 0x10000, 0x10000, CRC(f8d14914) SHA1(14e8ecc4794920dc530fc6bd12ad64494e2544e5), ROM_BIOS(1))
ROM_END

// DTK MBA-032Q TK83305-4N-D-03 - Chipset: MACRONIX MX83C305FC, MX83C306FC, HM6818A - BIOS/Version: Award v4.20, MXIC 80386 CACHE SYSTEM, S/N 0089082653
// BIOS string: 08/07/92-MX83C305/306-113b0000-00 (113B00AC) - Keyboard BIOS: Award, S/N: 008902654 - CPU: i386DX-25, solder pads for 80386, FPU socket provided
// RAM: 8xSIMM39 - OSC: 14.31818M, 80.000MHz - ISA8: 2, ISA16: 6
ROM_START( mba032q )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "mba-032q.bin", 0x10000, 0x10000, CRC(23481187) SHA1(1b6d0f54ce73853fcdd43588196bb6072b39d068))
ROM_END

ROM_START( mx83c305 )
	// 0: AMI BIOS, BIOS-String:  31-0101-009999-00101111-121291-MXIC-0 / 09/02/1992 - Keyboard-BIOS: JETkey V5.0
	// Chipset MX83C05AFC, MX8306AFC - CPU: AMD AM386DX-40, OSC: 14.31818 - ISA8: 1, ISA16: 5
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_SYSTEM_BIOS( 0, "mxic01", "MXIC #1")
	ROMX_LOAD( "mxic.bin", 0x10000, 0x10000, CRC(81853049) SHA1(d855b8d935417cfcfd6580fe3ed4ea393dd49b35), ROM_BIOS(0))
	// 1: BIOS-String: 30-0200-009999-00101111-111192-MXIC-0 / 12/15/1993
	ROM_SYSTEM_BIOS( 1, "mxic02", "MXIC #2")
	ROMX_LOAD( "3mxm001.bin", 0x10000, 0x10000, CRC(62fcd52b) SHA1(fa34c27be4627c68fe5c828451d86cbfad0ba358), ROM_BIOS(1))
ROM_END


// ***** 386sx motherboards using the Opti F82C206, Opti 82C283 chipset
ROM_START( op82c283 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// 0: Chipset: Opti F82C206, Opti 82C283 - BIOS: ARMAS AMI 386SX BIOS PLUS 9014775 - Keyboard-BIOS: NEC D80C42C
	// BIOS-String: 30-013X-D21185-00001111-031591-OPSX-0 - OSC: 50.000MHz, 14.31818MHz - CPU: AM386 SX/SXL-25 - ISA8: 2, ISA16: 6
	ROM_SYSTEM_BIOS( 0, "armas", "386sx ARMAS" )
	ROMX_LOAD( "386-opti-armas.bin", 0x10000, 0x10000, CRC(d9c696bc) SHA1(467617ab4a211ce460766daa3e5803e190368703), ROM_BIOS(0))
	// 1: 386SX MAIN BOARD REV:A1-1M/N: 3805 - Chipset: OPTi F82C206 / OPTi (unreadable) - BIOS:AMI 386SX BIOS ZZ908380
	// BIOS-String: 30-0100-DG112-00101111-031591-OPSX-0 / A10001B / 128KB RESERVED FOR RAM SHADOW.
	// Keyboard-BIOS: AMI KB-BIOS-VER-F (Intel P8942AHP) - CPU: AM386 SX/SXL-25 - OSC: 14.31818MHz, 50.000MHz - ISA8: 2, ISA16: 6
	ROM_SYSTEM_BIOS( 1, "3805", "386sx 3805" )
	ROMX_LOAD( "386sx-opti-908380.bin", 0x10000, 0x10000, CRC(38502567) SHA1(d65d272aa60642197c9b639a8679f8f41c4a697b), ROM_BIOS(1))
	// 2: CPU: 386SX-20 - BIOS: AMI; 03/15/91 - no display - unknown ASI motherboard
	// BIOS-String: 30-0100-DG1112-00101111-031591-OPSX
	ROM_SYSTEM_BIOS( 2, "c28301", "OPTi 82C283 #1")
	ROMX_LOAD( "3opm010.bin", 0x10000, 0x10000, CRC(7c2acf57) SHA1(d40605621da40204dc6370d2d00b783b3a7f8dce), ROM_BIOS(2))
ROM_END

// Octek Panther II - Chipset: OPTi 82C283, F82C206L/Chips 82C206 - CPU: 386sx - BIOS: AMI 386sx BIOS - Keyboard-BIOS: Intel/AMI
// BIOS-String: 30-0200-420000-00101111-050591-OPSX-0 / OPSX 386SX PAGE INTERLEAVE BIOS - ISA8: 2, ISA16: 4
ROM_START( ocpanii )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "octek_panther_ii_386sx.bin", 0x10000, 0x10000, CRC(866192d5) SHA1(fe6133ee3ba0d71c0d4690a0843ca82106effcf6))
ROM_END

// RYC Alaris LEOPARD LX REV D - Chipset: Opti 82C283 82C206Q - CPU: 486SLC2 (IBM 14 Q) - ISA16: 7
// BIOS: AMI 486SLC ISA BIOS AA0735388 - Keyboard-BIOS: Intel/AMI MEGA-KB-H-WP
// BIOS-String: 30-0100-006328-00101111-060692-OPSXPI-0
ROM_START( alaleolx )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "486-ryc-leopard-lx.bin", 0x10000, 0x10000, CRC(bbc7bfd2) SHA1(49833b482efb8e361be88f48e252621b147a3b1b))
ROM_END


// ***** 386 motherboards using the OPTi 82C381/382 "HiD/386 AT chipset"

// CPU: 386DX-25 - Chipset: OPTI 82C382 25MHz, 82C381P, 82C206 - BIOS: AMI; 09/15/90
// BIOS-String: EOX3-6069-083090-K0
ROM_START( op82c381 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "3opm004.bin", 0x10000, 0x10000, CRC(933c2c2b) SHA1(191a1a80c128430a0a461ff9202d27969a715d9d))
ROM_END

// Shuttle HOT-304 - Chipset: Opti F82C382, Opti (erased), UMC UM82C206L - OSC: 14.31818MHz, 50.000MHz
// BIOS: AMI, Ser.Nr. 150796 - BIOS-String: 30-0101-DK1343-00001111-050591-OPBC-0 - Keyboard BIOS: AMI Ser.Nr. 209210 - ISA8: 1, ISA16: 6, ISA16/Memory: 1
ROM_START( hot304 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "386-hot-304.bin", 0x10000, 0x10000, CRC(cd4ad4ec) SHA1(50f1b7a15096fff7442d575a47728ba4709b2f39))
ROM_END

// Arche Technologies Inc. KMA-300G-25 - Chipset: OPTi 82C381, 82C382, UMC206L - CPU: i386-25, FPU: socket provided - RAM: SIMM30x8, Cache: 8x5C6408-35
// ISA8: 1, ISA16: 6, ISA16/Memory: 1 - OSC: 50.000MHz, 14.31818 MHz
// BIOS-String: EOPG-3700-040990-K0
ROM_START( kma300g )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "bios.bin", 0x10000, 0x10000, CRC(3149a4a4) SHA1(6a027ed94568a89a800360119da0c568a2a29e19))
ROM_END


// ***** 386sx motherboards using the OPTi 82C291 chipset

ROM_START( op82c291 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// 0: MR BIOS (r) V1.43
	ROM_SYSTEM_BIOS(0, "mr", "MR")
	ROMX_LOAD( "opti_82c291_mr-bios_v143.bin", 0x10000, 0x10000, CRC(f7989a65) SHA1(cc729b6baac486ac3116f08e78eb58bb39365dd5), ROM_BIOS(0))
	// 1: CPU: 386SX-40 - BIOS: AMI - no display, nine beeps, so probably bad dump
	ROM_SYSTEM_BIOS( 1, "ami", "AMI")
	ROMX_LOAD( "3opm007.bin", 0x10000, 0x10000, CRC(eed82365) SHA1(45f5a608740d161c5a74415ff3f7b573d7e61f58), ROM_BIOS(1))

ROM_END

// DTK Computer PPM-3333P - Chipset: OPTi 82C291 - Opti F82C206 - ISA16: 6 - CPU: AMD Am386SX/SXL-33, FPU: empty socket - OSC: 14.31818 - 66.0000 MHz
ROM_START( ppm3333p )
	ROM_REGION16_LE(0x20000, "bios", 0)
	//0: Award Modular BIOS v4.20 (80386DX) / (119U906X) DTK Computer
	ROM_SYSTEM_BIOS(0, "ppmawa", "PPM-3333P Award")
	ROMX_LOAD( "386sx_opti291-award.bin", 0x10000, 0x10000, CRC(4855b394) SHA1(94dd1a38852eecac538ef4b8bf04bb7c1e4317d2), ROM_BIOS(0))
	//1: BIOS-String: 30-0200-001107-00001111-121291-OPTX 291-0 / OPTI-291WB BIOS VER 1.2
	ROM_SYSTEM_BIOS(1, "ppmami", "PPM-3333P AMI")
	ROMX_LOAD( "386sx_opti291-ami.bin", 0x10000, 0x10000, CRC(35727f8f) SHA1(3fb14cd6ea0d7a2bd545beb1586403cc36a77668), ROM_BIOS(1))
ROM_END


// ***** 386 motherboards using the Opti F82C206, 82C391B, 82C392 chipset

ROM_START( op82c391 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: CPU: 386DX - Chipset: OPTi 82C391 B2, 82C392 B - BIOS: AMI; 07/07/91; AA 0571504
	// BIOS-String: 30-0100-DK1343-00101111-070791-OPWB3/B-0
	ROM_SYSTEM_BIOS(0, "39101", "82C391 #1")
	ROMX_LOAD( "3opm001.bin", 0x10000, 0x10000, CRC(3cb65e60) SHA1(c91deaba1b34008449d6cc2aa94d115c47e0640a), ROM_BIOS(0))
	// 1: BIOS: AMI; 05/05/91; AMI 386C BIOS; #1023992
	ROM_SYSTEM_BIOS(1, "39102", "82C391 #2") // no display, nine beeps
	ROMX_LOAD( "3opm005.bin", 0x10000, 0x10000, CRC(ef3dcdde) SHA1(53a8d0af776362d5b92d1cce92d6ca8dbeb33398), ROM_BIOS(1))
	// 2: BIOS: AMI; 07/07/91 - no display
	ROM_SYSTEM_BIOS(2, "39103", "82C391 #3")
	ROMX_LOAD( "3opm011.bin", 0x10000, 0x10000, CRC(6706c85a) SHA1(70af6de83e59df3d9b74e904fde98d0b9cbdaae9), ROM_BIOS(2))
	// 3: BIOS-String: 30-0100-000000-00101111-070791-OPTi391-0
	ROM_SYSTEM_BIOS(3, "39104", "92C391 #4")
	ROMX_LOAD( "3opm12.bin", 0x10000, 0x10000, CRC(fa9592c5) SHA1(f9042163e7e2762e999687c3ec94d576f5b7c499), ROM_BIOS(3))
	// 4: BIOS-String: MR BIOS (tm) V1.30 - RAM Pattern Test Failed at 0F0000H
	ROM_SYSTEM_BIOS(4, "mrv130", "MR BIOS V1.30")
	ROMX_LOAD( "opti_82c391_386_mr_bios_v130.rom", 0x10000, 0x10000, CRC(458c9405) SHA1(845ea33e4e228aa98d1bd3b0148fa306754e3d78), ROM_BIOS(4))
ROM_END

ROM_START( op386wb )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// OPTi 386WB VER.1.0 - OSC: 66.6670MHz, 50.000MHz, 14,31818 - ISA8: 1, ISA16: 7
	// BIOS: 1006229 - Keyboard-BIOS: Intel P8942AHP
	// 0: BIOS-String: 30-0201-D41107-00101111-050591-OPWB-0
	ROM_SYSTEM_BIOS( 0, "1006229", "1006220")
	ROMX_LOAD( "386-opti-386wb-10.bin", 0x10000, 0x10000, CRC(1a5dd6b2) SHA1(9e6b556bfdf21d6f3cba6a05a3092887a71a24a8), ROM_BIOS(0))
	// 1: BIOS-String: 30-0201-D41107-00101111-050591-OPWB-0
	ROM_SYSTEM_BIOS( 1, "d41107", "D41107")
	ROMX_LOAD( "ami_d41107.bin", 0x10000, 0x10000, CRC(2b6cc50d) SHA1(69f69e0295abf5e331fdf01dc8e6b1c0c6591992), ROM_BIOS(1))

ROM_END

// Shuttle HOT-307H - BIOS-String: 30-0100-DK1343-00101111-070791-OPWB3/B-0 - CPU: 386DX - Chipset: OPTi 82C391 B2, 82C392 B - BIOS: AMI; 07/07/91; AA 0571504
ROM_START( hot307h )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "3opm001.bin", 0x10000, 0x10000, CRC(3cb65e60) SHA1(c91deaba1b34008449d6cc2aa94d115c47e0640a))
ROM_END

// ***** 386 Motherboards using the OPTi495SLC chipset => "qdi" in the 486 BIOS section uses that chipset too

ROM_START( opti495slc )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: Chipset: OPTi 82C495SLC / F82C206, BIOS: AMI 486086 - BIOS-String: 40-040A-001102-00101111-111192-OP495SLC-0
	// Keyboard-BIOS: AMI - CPU: AM386DX-40, FPU socket provided - ISA8: 1, ISA16: 5 - OSC: 14.31818
	ROM_SYSTEM_BIOS(0, "op495slc01", "OP495SLC #1")
	ROMX_LOAD( "op495slc01.bin", 0x10000, 0x10000, CRC(0b25044b) SHA1(1b585f0d73ea963dcfbf421325e7da6dd3dd918f), ROM_BIOS(0))
	// 1: BIOS-String: 40-0200-001107-00101111-111192-OP495SLC-0 - OPTI 495SLC 80386 ONLY - BIOS: AMI 386C BIOS 1605865
	// Keyboard-BIOS: AMI 386C BIOS Keyboard  ISA8: 1, ISA16: 5 - CPU: AMD AM386DX-40 - OSC: 14.3
	ROM_SYSTEM_BIOS(1, "op495slc02", "OP495SLC #2")
	ROMX_LOAD( "op495slc02.bin", 0x10000, 0x10000, CRC(4ff251a2) SHA1(e8655217bd46d50af6b30184bf462376d0e388c6), ROM_BIOS(1))
	// 2: BIOS-String: - Same board exists with an OPTi495XLC chip, possibly from A-Trend
	ROM_SYSTEM_BIOS(2, "op495slc03", "OP495SLC #3") // no display
	ROMX_LOAD( "486dlc-unknown.bin", 0x10000, 0x10000, CRC(2799e876) SHA1(ce7b421ecb27d915585c1a98bebb17cc5c2463e7), ROM_BIOS(2))
	// 3: 364 Ver. 1.1 - Chipset: OPTi 82C495SLC, F82C206Q - CPU: AMD AM386DX-40 - RAM: 8xSIMM30, Cache: 5x14pin
	// ISA8: 1, ISA16: 5 - BIOS: AMI 386C BIOS 1684393 - Keyboard-BIOS: AMI 386C BIOS KEYBOARD 1684393
	// BIOS-String: 40-040A-001107-00101111-111192-OP495SLC-0 / OPTI-495SLC BIOS VER 2.1
	ROM_SYSTEM_BIOS(3, "op495slc04", "OP495SLC #4")
	ROMX_LOAD( "386c_mobo-364-v1.1.bin", 0x10000, 0x10000, CRC(826ec2d1) SHA1(cc13e385c6614eb654ee0669f67df51f1e1fa585), ROM_BIOS(3))
ROM_END


// ***** 386 Motherboards using the OPTi495XLC chipset: OPTi 82C495XLC F82C206, BIOS: AMI 386DX BIOS Ser.#:AA2602776, Keyboard-BIOS: Lance LT38C41 - ISA8: 1, ISA16: 5

ROM_START( opti495xlc )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 40-081L-001343-00101111-080893-OP495XLC-F  / OPTi495XLC For 386
	ROM_SYSTEM_BIOS(0, "optimini", "OPTi Mini 82C495XLC")
	ROMX_LOAD( "386-opti-mini.bio", 0x10000, 0x10000, CRC(04c75e45) SHA1(d5bf92421dda3191c6da12ae2fa31c9ee7a831e1), ROM_BIOS(0) )
	// 1: MR BIOS (r) V1.60
	ROM_SYSTEM_BIOS(1, "mr495xlc", "MR BIOS for OPTi 82C495XLC") // use Hercules
	ROMX_LOAD( "mr-3dx94.rom", 0x10000, 0x10000, CRC(6925759c) SHA1(540177fe2c10e20037893c9763b0bf6e35163c9c), ROM_BIOS(1) )
	// 2: possibly from A-Trend (A1742X REV.C 94V-0), exists with an OPTi495SLC chip, see above section, ISA8: 2, ISA16: 4, ISA16/VL: 2
	// BIOS-String: X0-0804-001117-00101111-080893-OP395XLC-0 / OPTI 495XLC 3/486 BIOS VER 5.02_T 94/07/07
	ROM_SYSTEM_BIOS(2, "op82c495xlc", "82C495XLC") // this one could also be listed as a 486 board as it has solder pads and sockets for CPUs from 386 to true 486s
	ROMX_LOAD( "at080893.bin", 0x10000, 0x10000, CRC(6b49fdaa) SHA1(5b490d1d1216763ef89688c8e383c46470272005), ROM_BIOS(2) )
	// 3: BIOS: AMI; 08/08/93; AA2740000 - hangs
	ROM_SYSTEM_BIOS(3, "mao13", "MAO13 Rev. A")
	ROMX_LOAD( "3opm002.bin", 0x10000, 0x10000, CRC(2d9dcbd1) SHA1(d8b0d1411b09767e10e66b455ebc74295bd1b896), ROM_BIOS(3) )
ROM_END

// Tandy 4000

ROM_START( t4000 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "v010301", "01.03.01")
	ROMX_LOAD( "t4000_even.u32", 0x18000, 0x4000, CRC(f728ae11) SHA1(3bc151b8d704dde7c340a66072c1a418b7eab7db), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "t4000_odd.u25",  0x18001, 0x4000, CRC(6bedcf61) SHA1(df08347f163d6e45bdf9e1b64a811222bffba80a), ROM_SKIP(1) | ROM_BIOS(0))
ROM_END


// 386sx motherboards using the SARC (or CYCLONE) RC2016A5 chipset

// Pine PT-319A rev2.2a - CPU: 386sx - BIOS: AMI; 06/06/92
// BIOS-String: X0-0100-000000-00101111-060692-386SX-0 / Ver 5.20
ROM_START( pt319a )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "3sam001.bin", 0x10000, 0x10000, CRC(cad22030) SHA1(85bb6027579a87bfe7ea0f7df3676fdaa64920ac))
ROM_END


// CX Technology, Inc. Model SXD (4x SIMM30, Cache: 64/128/256KB in 2 banks plus TAG, 4x 16-bit ISA) - Chipset: SARC RC2016A5; HM6818P; CX109; LT38C41 © Lance Corp. (keyboard controller?)
// additional info from chukaev.ru54.com: Chipset: CYCLONE RC2016A5 - ISA16: 6 - ROM: CX109 340C3A62D0A - CPU/FPU: Am386SX/SXL-33, 387
ROM_START( cxsxd )
	ROM_REGION16_LE(0x20000,"bios", 0)
	// BIOS-String: 03/25/93-SARC_RC2016A-219v0000 / CX 386SX System
	ROM_LOAD( "award_cx-sxd_v4.50.srd.bin", 0x10000, 0x10000, CRC(ef1c74d7) SHA1(b40b1cb7143c4e352798bdf3b488d9342a4029a7))
ROM_END

// PC-Chips M396F VER 2.2 - CPU: 386SX, 387SX - ISA16: 6
ROM_START( pccm396f )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// 0: Chipset: PCCHIPS CHIP2 310, HT6542, HM6818A
	// BIOS-String: X0-0100-001437-00101111-060692-M396C-0 - BIOS: AMI 386SX BIOS Ver. 2.10 C-1216
	ROM_SYSTEM_BIOS(0, "chips2", "Chips 2")
	ROMX_LOAD( "3pcm003.bin", 0x10000, 0x10000, CRC(b7fc6737) SHA1(670e38b628cb71dc09742f097349ac48ccf28696), ROM_BIOS(0))
	// 1: Chipset: SARC RC2016A5 - CPU: 386SX-40/486SLC, 387SX - BIOS: AMI; 06/06/92
	// BIOS-String: X0-0100-001437-00101111-060692-M396F-0
	ROM_SYSTEM_BIOS(1, "sarc01", "SARC RC2016A5 #1")
	ROMX_LOAD( "3sam002.bin", 0x10000, 0x10000, CRC(8d5ef8e8) SHA1(5ca2b36d5bee2870f894984909aa2013b5c4d6cf), ROM_BIOS(1))
	// 2: BIOS-String: X0-0100-001437-00101111-060692-M396F-0 - CPU: 386SX-40 (ALI M1386SX A1P)
	ROM_SYSTEM_BIOS(2, "sarc02", "SARC RC2016A5 #2")
	ROMX_LOAD( "3sam003.bin", 0x10000, 0x10000, CRC(95ea08d8) SHA1(812e8488ad63ca24250e245a2f0273f1d1703fc3), ROM_BIOS(2))
ROM_END


// ***** 386 motherboards using the SIS Rabbit : 85C310 / 85C320 / 85C330 / 85C206 chipset

// ASUS ISA-386C - BIOS : AMI 05/05/1991, on a 27C512 type EPROM (64KB)
// BIOS-String : 30-0105-001292-00101111-050591-SISDFC-386 - // ISA8: 2, ISA16:5, ISA16/Memory: 1
ROM_START( isa386c )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "asus_isa-386c_bios.bin", 0x10000, 0x10000, CRC(55e6d1bb) SHA1(e1ac490a30f63b6e4d6d9d0fbaea3d132b8ff053))
ROM_END

// Chaintech 333SC - Chipset: UMC UM82C206L, three smaller SiS chips (unreadable, probably SiS Rabbit)
// CPU/FPU present - BIOS: AMI 386 BIOS - Keyboard-BIOS: AMI
// BIOS-String: ESIS-1128-040990-K0 - ISA8: 2, ISA16: 6 - OSC: 14.31818, 66.000MHz
ROM_START( chn333sc )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "bios.bin", 0x10000, 0x10000, CRC(f8b2b0bc) SHA1(2799cce621b93bf38b04deeb419d25a73f7416f4))
ROM_END

// Octek Jaguar II - Chipset: SiS 85C330, 85C320, 85C206, 85C310 - CPU: i386-33, FPU: i387-33 - RAM: 8xSIMM30, Cache: 8xIS61C64-25N
// BIOS: AMI - Keyboard-BIOS: AMI KB-BIOS-VER-F - ISA8: 2, ISA16: 5, ISA16/Memory: 1 - OSC: 66.000MHz, 14.31818
// BIOS-String: 30-0100-006069-00101111-020291-SISC / 0033P-002
ROM_START( ocjagii )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "ami_386_zz457511.bin", 0x10000, 0x10000, CRC(b5bc6a9a) SHA1(b74f592dd3bcd2978ed7d895d483f83413e0f8d5))
ROM_END


ROM_START( sisrabb )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 30-0000-D01128-00101111-070791-SISD-0
	ROM_SYSTEM_BIOS(0, "3sim001", "3sim001")
	ROMX_LOAD( "3sim001.bin", 0x10000, 0x10000, CRC(2982f552) SHA1(f1849c207d8c802faaf8ef628f88b28256e7fb31), ROM_BIOS(0))
	// 1: PLATO TECHNOLOGY CO., LTD. - Chipset: SiS 'rabbit': SIS 85C320, 85C330, 85C310, 85C206 - CPU: Intel 80386DX-33, FPU: i387DX-33
	// RAM: 16xSIMM30, Cache: 8xKM688B65P-25, 2xQS8888-25P - BIOS: AMI 386 BIOS Ser# Z403736 - Keyboard BIOS: AMI - BIOS-String: ESIS-1393-040990-K0 / SIS-B 386 BIOS
	// OSC: 50.000MHz, 66.0000MHz, 14.31818MHz - ISA8: 2, ISA16: 6
	ROM_SYSTEM_BIOS(1, "plato", "Plato")
	ROMX_LOAD( "386-sis-z403736.bin", 0x10000, 0x10000, CRC(8230f4c1) SHA1(952de1e16efe7e3a7514b68ea251b88192de3ac8), ROM_BIOS(1))
ROM_END


// ***** 386 Motherboards  using the Symphony SL82C362 SL82C461 SL82C465 chipset

// FIC 386 SC Rev A2 - Chipset: Symphony SL82C461, SL82C465, SL82C362
ROM_START( 386sc )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS: AMI 386 BIOS Ser.#: ZZ006975, BIOS-String:  30-0200-DF1211-00101111-042591-SYMP-0 / 386DX BIOS for SYMLABS SL82C360 - Keyboard-BIOS: AMI #Z357365
	// CPU: unreadable, FPU: Cyrix 387DX-25 - OSC: 40.000MHz, 14.31818 - ISA8: 1, ISA16: 7
	ROM_SYSTEM_BIOS(0, "386sc1", "386SC #1")
	ROMX_LOAD( "386_sc_symphony.bin", 0x10000, 0x10000, CRC(fabe369c) SHA1(211ff63dd874c273135d1427db3562d752c2bade), ROM_BIOS(0))
	// 1: ID: FIC 386SC REV A2 MBZ86418 - CPU: AMD 386DX/DXL-40, FPU socket provided - RAM: 8xSIMM30, Cache: 512KB
	// BIOS Version: AMI 386DX BIOS - ZZ000294 - BIOS String: 30-0200-ZZ1121-00101111-042591-SYMP-0 - 386DX BIOS FOR SYMLABS SL86C360 - ISA8: 1, ISA16: 7 - OSC: 14.31818, 80.000MHz
	ROM_SYSTEM_BIOS(1, "386sc2", "386SC #2")
	ROMX_LOAD("fic386sc.bio", 0x10000, 0x10000, CRC(6154adb7) SHA1(96c495d9a9975e1af9b42384712e609e3ffcff4e), ROM_BIOS(1))
ROM_END

ROM_START( 386sc2c )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 20-0200-DF1121-00101111-102591-SYM_386B-0 / 386DX/SX (S1A.P)
	ROM_SYSTEM_BIOS(0, "s1a", "S1A.P") // Chipset: SYMPHONY SL82C362, SL82C461, SL82C465
	ROMX_LOAD( "386-sc-2c_ami_za492668.bin", 0x10000, 0x10000, CRC(b408eeb7) SHA1(cf1974492119e1aae623fa366d5760343e827e52), ROM_BIOS(0))
	// 1: BIOS-String: 20-0200-DF1121-00001111-102591-SYM_386B-0 / 386SX/DX (S1B)
	ROM_SYSTEM_BIOS(1, "s1b", "S1B") // also on FIC 386-SC-HG
	ROMX_LOAD( "ami_386_za590821.bin", 0x10000, 0x10000, CRC(51a4c231) SHA1(4ad65408f2a401ff262934f886937a2615c08e21), ROM_BIOS(1))
ROM_END

// FIC 386-SC-HG - Chipset: SYMPHONY SL82C362, SL82C461, SL82C465 - CPU: AMD Am386DX/DXL-40, FPU socket provided - BIOS: AMI 386 BIOS ZA977287
// Keyboard BIOS: Intel/AMI - RAM: 8xSIMM30 - ISA16: 6 - OSC: 80.000MHz
ROM_START( 386schg ) // BIOS-String: 20-0200-DF1121-00101111-102591-SYM_386B-0 / 386DX/SX(S1B)
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "386-za877287-386-sc-hg.bin", 0x10000, 0x10000, CRC(fcaa06c0) SHA1(5ce6258a26311cec46e51cb16bcb66e9c68d16b2))
ROM_END

// Peacock P386DX-40 REV: 1.0 - Chipset: Symphony SL82C461, SL82C362, SL82C465, DS1287 - CPU: AMD Am386DX-40, FPU: IIT 3C87-40 - RAM: 8xSIMM30, Cache: 4xW4256AK-20, 1xTC5588P-20
// BIOS: AMI 386 BIOS #ZA22147 - OSC: 80.000MHz, 14.31818, 24.000 - on board: IDE, Floppy - ISA16: 6
ROM_START( p386dx40 ) // BIOS-String: 30-0200-DF1121-00101111-070791-SYM_AUTO-0 / Peacock 386 DX Ver. 1.0
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "386_peacock-p386dx-40-za224147.bin", 0x10000, 0x10000, CRC(f97562cf) SHA1(354b6ffd345c9f50beca0f836b0d4a92df1e7c48))
ROM_END


// ***** 386 Motherboards using the UM82C491F chipset

ROM_START( um82c491f )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: TAM/33/40-U2 - BIOS: AMI S/NO. OA 242412 - BIOS-String: 40-0102-001105-00101111-040493-UMC491F-0 / TAM/33,40-U2, 08/11/1993
	// ISA8: 1, ISA16: 5 - OSC: 80.000MHz, 14.31818
	ROM_SYSTEM_BIOS(0, "tam3340u2", "TAM/33/40-U2")
	ROMX_LOAD( "tam_umc491f.bin", 0x10000, 0x10000, CRC(718890d5) SHA1(52336cfc7cd0f0f51799c999cefcfed2b2942211), ROM_BIOS(0))
	// 1: Board is only marked "rev.0.3, looks like 386GRN - CPU: AMD AM386DX-40 - OSC: 14.31818 - ISA8: 1, ISA16: 5
	// Chipset: UMC UM82C491F - BIOS-String: 08/30/93-UMC-491-214X2000-OO - BIOS: Award 386 D2026361 - Keyboard BIOS: JETkey V3.0
	// additional info from chukaev.ru54.com: REV:0.4 board has JETkey V5.0 keyboard BIOS, uses same motherboard BIOS
	ROM_SYSTEM_BIOS( 1, "386grn", "386GRN-like board rev.03")
	ROMX_LOAD( "386dx40-27c512.bin", 0x10000, 0x10000, CRC(692a4d52) SHA1(7970a05586eacfe4bfdc575b17bbbfb7ff1c86b0), ROM_BIOS(1))
	// 2: BIOS: AMI; 04/04/93 - CPU: 386DX-40 - BIOS-String: 40-0102-001277-00101111-040493-UMC491F-0
	ROM_SYSTEM_BIOS( 2, "491f01", "UM82C491F #1")
	ROMX_LOAD( "3umm005.bin", 0x10000, 0x10000, CRC(032e78f2) SHA1(5271c4362284ec87840b3fb23542506a72a328c2), ROM_BIOS(2))
	// 3: BIOS-String: 08/30/93-UMC-491-214X2000-OO / CACHE 386/486 SYSTEM BIOS
	ROM_SYSTEM_BIOS( 3, "491f02", "UM82C491F #2")
	ROMX_LOAD( "3umw007.bin", 0x10000, 0x10000, CRC(d82c9bef) SHA1(36e8d1c7629642cbcc337721eef1c73f1f0ed92c), ROM_BIOS(3))
ROM_END


// ***** 386 Motherboards using the UMC UM82C493F/UM82C491F chipset or badge engineered varieties (BIOTEQ)

	// BIOS-String: 40-0100-001494-00101111-080893-UMC491F-0 / 11/26/93 - CPU: TX486DLC/E-40GA, IIT 4C87DLC-40 - ISA8: 1, ISA16: 5 - BIOS: AMI; 208808; 08/08/93
ROM_START( um82c493f )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_SYSTEM_BIOS( 0, "493f01", "UM82C493F #1" )
	ROMX_LOAD( "3umm007.bin", 0x10000, 0x10000, CRC(8116555a) SHA1(8f056a83de60373ed26026a226eead19868abeca), ROM_BIOS(0))
ROM_END

// 0: 386-4N-D04A
ROM_START( 4nd04a )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// BIOS-String: 40-0102-428003-00101111-080893-UMC491F-0 - UMC 4913 386 IVN 1.0 1993.12.22
	// Chipset: UMC UM82C493F, UM82C491F
	ROM_SYSTEM_BIOS( 0, "ivn10", "386-4N-D04A IVN 1.0" )
	ROMX_LOAD( "386-4n-d04a.bin", 0x10000, 0x10000, CRC(cf386b9c) SHA1(6fd4303e4f0d2ed75d4e7f36dc855037b1779e64), ROM_BIOS(0))
	// 1: 386-4N-D04A PCB V2.0 - BIOS-String: 40-0103-428003-00101111-080893-UMC491F-0 / UMC 4913 386 IVN 1.1 1994.1.31
	ROM_SYSTEM_BIOS( 1, "ivn11", "386-4N-D04A IVN 1.1" )
	ROMX_LOAD( "3umm006.bin", 0x10000, 0x10000, CRC(4056104d) SHA1(5e639e6766dc9a19296358e9a64a76ad57fc733a), ROM_BIOS(1))
	// 2: TK-82C491/493/386-4N-D04 - BIOS-String: (2c4x2u01) U-BOARD / 11/09/93-UMC-491-2C4X2U01-00 - ISA8: 1, ISA16: 5
	ROM_SYSTEM_BIOS( 2, "awa110993", "AWARD 11/09/93") // BIOS: Award; 386 BIOS; A3384454
	ROMX_LOAD( "3umw002.bin", 0x10000, 0x10000, CRC(2c510e81) SHA1(a12c672ec418cc4cd14482901f8ba34c50f319f5), ROM_BIOS(2))
	// 3: TK-82C491/493/386-4N-D04 - BIOS-String: 01/14/94-UMC-491-2C4X2U01-00 / U-BOARD
	ROM_SYSTEM_BIOS( 3, "awa011494", "AWARD 01/14/94")
	ROMX_LOAD( "3umw003.bin", 0x10000, 0x10000, CRC(64067839) SHA1(4ae3462619ef8da67f74d85ee7ab44bdb49a5728), ROM_BIOS(3))
ROM_END

// Biostar MB-1333/40PMB-CH, rev B.3 - Chipset: "Bioteq" [Atmel] AT40391, "Bioteq" G392 [Atmel AT40392], C&T P82C206
// BIOS: AMI 386 BIOS PLUS - Keyboard-BIOS: AMI - CPU: AM386-DX40 - OSC: 14.31818, <unreadable>
ROM_START( mb133340 )
	ROM_REGION32_LE(0x20000, "bios", 0) // the OPWB3 string also exists in the BIOS versions meant for the OPTI 82C391/392 chipsets
	// 0: BIOS-String: 30-0100-D61223-00101111-050591-OPWB3/B-0 / MB-1340PMA-CH, MB-1340PMB-CH, MB-1340PMD-CH, MB-1340PME-CH for B version..
	ROM_SYSTEM_BIOS(0, "opwb3b", "MB-1333/40PMB-CH OPWB3-B")
	ROMX_LOAD( "opwb3b.bin", 0x10000, 0x10000, CRC(c9cf46dd) SHA1(c9e58cb6fed770d92892672d0a910d448c507ac1), ROM_BIOS(0))
	// 1: BIOS-String: 30-0201-D61223-00101111-050591-OPWB-0 / MB-1333PMA-CH, MB-1333PMB-CH, MB-1333PMD-CH, MB-1333PME-CH
	ROM_SYSTEM_BIOS(1, "opwb", "MB-1333/40PMB-CH OPWB")
	ROMX_LOAD( "opwb.bin", 0x10000, 0x10000, CRC(9532c6d1) SHA1(48e889ed61921643147fea95224bcf42bb6e82fa), ROM_BIOS(1))
	// 2: BIOS-String: 40-0100-001223-00101111-040493-UMC491F-0 / MB-1333/40UCG-A, MB-1333/40UCG-B / MB-1433-40UDV-A, MB-1433/50UCV-C, MB6433/50UPC-A for EXT. RTC
	ROM_SYSTEM_BIOS( 2, "m21", "M21" )
	ROMX_LOAD( "3bim001.bin", 0x10000, 0x10000, CRC(9ea0ce67) SHA1(cb55a61cd43705a54e4109d816924c8820f78ae5), ROM_BIOS(2))
ROM_END


// ***** 386 motherboards using the UMC UM82C481AF, UM82C482A/B/F, 82C206F chipset

// QD-U386DX VER 1.0 - CPU/FPU: i386DX-33, IIT 3C87-33 - ISA8:2, ISA16: 5 - BIOS: AMI 386DX ISA BIOS (AA0119183)
// BIOS-String: 30-0200-428003-10101111-070791-UMC480A-F
ROM_START( qdu386dx ) // three short beeps (base 64k RAM failure)
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "3umm001.bin", 0x10000, 0x10000, CRC(5b6a7d0b) SHA1(02696eaaa5dd21fe4b3b39629aa926ae87a9a2bd))
ROM_END

// ASUS ISA-386U30 REV.2.2 - Chipset: UMC UM82C481AF, UM82C482AF, 82C206F - CPU: AM386DX-40 - OSC: 14.31818MHz, 32.000MHz - ISA8: 1, ISA16: 6
// BIOS: AMI 386DX BIOS AA0974582 - BIOS-String: - Keyboard-BIOS: AMI U2518640 MEGA-KB-F-WP
ROM_START( isa386u30 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "386-isa-386u30.bin", 0x10000, 0x10000, CRC(6d45a044) SHA1(63c06568f9db5ce12dc8dd0fb1ad1009a9fb24f6))
ROM_END

// Chicony CH-486-33C REV 1.0 - Chipset: UMC UM82C481, UM82C482, Chips P82C206 - CPU: Intel Overdrive DX2ODPR66, FPU socket provided
// RAM: 16xSIMM30, Cache: 8xW24256AK-25, P4C188-20PC - BIOS: AMI - Keyboard-BIOS: Intel/AMI
// BIOS-String: 40-0300-ZZ1116-00101111-031591-UMCWB-F - OSC: 33.000MHz - ISA8: 2, ISA16: 5
ROM_START( ch48633c )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "ch-486-33c_ami_bios_z944944_am27.bin", 0x10000, 0x10000, CRC(1ca9bf11) SHA1(bb5fb4c7544e2ccb06d423ef4da0729a6bf8b231))
ROM_END

// Elitegroup FX-3000 REV:1.0 - Chipset: UMC UM82C481BF, UM82C482AF, UM82C206F - ISA16: 6
ROM_START( ecsfx3000 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 40-0100-001131-00101111-121291-UMCAUTO-0 / FX3000 V1.3 12-17-92 - Keyboard BIOS: AMI/Intel - BIOS: FX3000-014 - CPU: AM386DX-40
	ROM_SYSTEM_BIOS(0, "v13", "V1.3")
	ROMX_LOAD( "fx-3000-bios.bin", 0x10000, 0x10000, CRC(f93c9563) SHA1(46a71e7fbc9238dd470d6d5ce3bc1e057f3aae24), ROM_BIOS(0))
	// 1: BIOS-String: 30-0500-D01131-00101111-070791-UMCWB-0 / FE386 V1.1 12-03-92 - Keyboard-BIOS: Lance LT38C41 - BIOS: AMI-1131 / S/NO. E-92488183 / FE 386-012 - CPU: Cyrix 486DLC-33GP FPU: Cyrix Cx87DLC-33QP - OSC: 66.667MHz, 14.31818
	ROM_SYSTEM_BIOS(1, "v11", "V1.1")
	ROMX_LOAD( "486-fx3000.bin", 0x10000, 0x10000, CRC(af303f08) SHA1(65dfa2541d2b08746f91012a2ae0121636402aac), ROM_BIOS(1))
ROM_END

ROM_START( um82c481af )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 40-0100-001266-00101111-121291-UMCAUTO-0 - 3DIUD-1.2
	// Chipset: // UMC UM92C206F, UM82C482AF, UM82C481BF - MB manufacturer according to BIOS is Modula Tech Co
	// ISA8: 1, ISA16: 6 - RAM: 8xSIMM30, Cache: 9x28pin, used: 4xIS61C256AH-20N, 1xW2465AK-20, CPU: AMD 386DX-40
	// CPU and FPU sockets provided - BIOS: AMI 386 BIOS, Keyboard-BIOS: AMI
	ROM_SYSTEM_BIOS(0, "3diud", "386 UMC 3DIUD")
	ROMX_LOAD( "386-umc-3flud.bin", 0x10000, 0x10000, CRC(2e795a01) SHA1(02e9e2871c1c1a542f44ab5eef66aee4b04225c1), ROM_BIOS(0))
	// 1: BIOS: Microid Research; 02/26/93 - BIOS-String: MR BIOS (r) V1.44
	ROM_SYSTEM_BIOS(1, "mr1441", "MR BIOS V1.44 #1") // resets continuously
	ROMX_LOAD( "3umr001.bin", 0x10000, 0x10000, CRC(466a115e) SHA1(077d797c653528062f1c87b03c608427c35c5505), ROM_BIOS(1))
	// 2: BIOS : MR 386DX/86DLC BIOS V.1.44 - Keyboard BIOS: JETkey V3.0 - CPU: TI 486DLC-40BGA, FPU: IIT 4C87DLC-40 - RAM: 8xSIMM30, Cache
	// OSC: 14.31818, 80.000MHz - ISA8: 1, ISA16: 6
	ROM_SYSTEM_BIOS(2, "mr1442", "MR BIOS V1.44 #2") // resets continuously
	ROMX_LOAD( "um82481m.bin", 0x10000, 0x10000, CRC(f617e1ce) SHA1(73ee80cb9f50547f26adbe0bfd2435b01728dd09), ROM_BIOS(2))
ROM_END


//**************************************************************************
//  80386 SX and DX Desktop
//**************************************************************************

// Schneider 386SX VGA System 40 (the number indicates the size of the harddisk, there were System 70 as well) - uses the same case as the Schneider Tower AT
// Schneider Tower VGA I/O: Chipset: WD37C65BJM, BIGJIM 50773 1108-0056, two other bigger chips can't be read on the photos
// 104 pin CPU card connector (ISA without the key), 4xISA16, 1xISA8 - on board: IDE, parallel, serial, bus mouse (Atari compatible), VGA, internal floppy (26pin), external floppy (DB25)
// On board graphics: ATI VGA Wonder-16 (256KB), ATI18800-1 1138-0069
// CPU card: CPU: Intel NG680386SX-16 (C-Step), FPU socket provided - Chipset: DDA14-075E, Chips P82C812, P82C811, P82C206, P82C215-12 (16MHz) - RAM: 8xSIMM30
// OSC: 20.000, 14.31818, 24.000000MHz, 32.000000MHz, - keyboard
// beeps 1-2-4
ROM_START( tower386sx )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "v103", "V1.03") // from a 386SX System 70
	ROMX_LOAD("t386s103.bin", 0x10000, 0x10000, CRC(d4e177e6) SHA1(fa11d49d629cdcac4467a9deedd25171ae499346), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v100", "V1.00") // from a 386SX System 40
	ROMX_LOAD("schneider_ag_386sx_bios_1_version_1.00a_id.nr.52504.u16", 0x10000, 0x8000, CRC(2fec2d3a) SHA1(4227da07f6652b89b9d02d7570ad0476672fd80d), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("schneider_ag_386sx_bios_0_version_1.00a_id.nr.52504.u15", 0x10001, 0x8000, CRC(b3331429) SHA1(b214bccfb62add9caea3d734885bc945b868967a), ROM_SKIP(1) |ROM_BIOS(1))

	// models upgraded to 512KB video memory were sold as "CEG" models as the memory upgrade enabled some sort of antialiasing ("continuous edge graphics")
	// in a 256 color mode with a choice from 792.096 colors.
	// according to https://archive.org/stream/byte-magazine-1991-01/1991_01_BYTE_16-01_1990_BYTE_Award_of_Excellence#page/n197/mode/2up this needs an EDSUN D/A chip, it is unknown
	// if it's contained on the platter or on the graphics upgrade piggyback card
	// The 12.5MHz version of the towerat2xx (VGA Tower System 40 or 70) used the same I/O backplane and were also offered with the CEG upgrade.
	ROM_REGION16_LE(0x10000, "vga", 0)
	ROM_LOAD16_BYTE("schneider_ag_vga_bios_low_v1.00_id.nr_51368.u13", 0x0000, 0x8000, CRC(ec4ef170) SHA1(0049ae5eab1a21838e674cf77e88994b954b1da3))
	ROM_LOAD16_BYTE("schneider_ag_vga_bios_high_v1.00_id.nr_51368.u14", 0x0001, 0x8000, CRC(5354962a) SHA1(11a503473e2011f323cc81c0b63d24f231c54c31))
ROM_END

// Atari PC 5 - American Megatrends 386XT Series-4 motherboard - on board EGA
// screen remains blank, 1 beep repeated (DRAM refresh failure)
ROM_START( ataripc5 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD32_BYTE("ami_pc5_1.00_rom0.bin", 0x00000, 0x8000, CRC(496149a6) SHA1(81033b22af830af8306abfde03a194739fe54355))
	ROM_LOAD32_BYTE("ami_pc5_1.00_rom1.bin", 0x00001, 0x8000, CRC(3c82fe66) SHA1(dd6c2c3c3635761b1d928912269b8937cbdc09ae))
	ROM_LOAD32_BYTE("ami_pc5_1.00_rom2.bin", 0x00002, 0x8000, CRC(7dc5b53b) SHA1(33e138baa84a8acc629bde5a6b54e47d0d4508f1))
	ROM_LOAD32_BYTE("ami_pc5_1.00_rom3.bin", 0x00003, 0x8000, CRC(b588b7a8) SHA1(2f2597b14e54d03cf957cce47536266f68d3aa66))
ROM_END

// Datavan Book-Size LAN Station - CPU: Am386SX/SXL-25 - Chipset: Headland HT18/C, RAM: 4xSIMM30
// BIOS: AMI 386SX BIOS PLUS S/NO. 232659 - Keyboard-BIOS: AMI KEYBOARD BIOS PLUS S/NO. 232659
// BIOS-String: 30-0100-ZZ1612-00101111-070791-HT18-F - ISA16: 1, used for LAN board - on board: 2xser, par, VGA
// Video: Realtek RTG3106 VGA, 1024K, MUSIC TR9C1710-66PCA - VGA BIOS: REALTEK / QUADTEL
// Mass storage: NEC FD1138H,  Quantum ProDrive ELS 977/10/17 - unknown DP8390DN based 8bit ISA LAN card
ROM_START( dvbslan ) // 3 short beeps =>  base memory read/write test error in the first 64 KB block of memory
	ROM_REGION16_LE( 0x20000, "bios", 0)
	ROM_LOAD( "ami_bios_386sx_bios_plus.bin", 0x10000, 0x10000, CRC(760afd89) SHA1(452f3e1b8b3b3d00df90c3501c8796de447a2184))

	ROM_REGION( 0x8000, "vga", 0) // REALTEK VGA BIOS
	ROM_LOAD( "realtek_vga.bin", 0x0000, 0x8000, CRC(4e416975) SHA1(826b7ec5494dc83cfcf25922185777bf6db46949))

	ROM_REGION( 0x2000, "lan", 0) // boot ROM from DP8390DN based LAN adapter
	ROM_LOAD( "net_isa_boot.bin", 0x0000, 0x2000, CRC(5bb527fd) SHA1(1eb7c82ef99d64eedffc3ac9c145d33b06cd8cb6))
ROM_END

// Epson PC AX3 - see epsax
ROM_START( epsax3 )
	ROM_REGION16_LE( 0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "evax3.bin", 0x10000, 0x8000, CRC(fec03cdc) SHA1(549027e6b786fa98a14912c1fb6d44c607206253))
	ROM_LOAD16_BYTE( "odax3.bin", 0x10001, 0x8000, CRC(0162d959) SHA1(f17741282e078a66e38d82ca48455b17c54e832f))
ROM_END


//**************************************************************************
//  80386 SX and DX Laptop/Notebook
//**************************************************************************

// Nixdorf N8810/20 - CPU: Intel 80386DX-20 clocked at 20MHz or 8MHz, Motherboard: FT-3 DFUP0279ZAB3
// Chipset: VLSI 9021BT 201141 VL16C452-QC, G2 GC132-PC, SED1345F, Headland HT133/A1A4324, Bt476KP50 128 9020
// RAM: 1MB, upgradeable via memory cards, this example has 4MB ROM: Phoenix keyboard ROM (missing dump)
// Mass storage: Floppy 3.5" 1.44MB, 3.5" HDD 40MB JVC JDE 3848V10-1A, Video: Paradise PVGA1A-JK
// Display: 12" bw double supertwisted LCD VGA, Ports: 2xser, Par, beeper, Bus: ISA16bit: 1, OSC: 28.6838M, 40.000MHz
// Origin: Software dump of the upper 256K
ROM_START( n8810m20 ) // "Keyboard clock line failure", but keyboard works
	ROM_REGION32_LE(0x20000, "bios", 0 )
	ROM_LOAD( "n8810m20_trim.bin", 0x00000, 0x20000, CRC(21b5394d) SHA1(691f50e65398e5c775dedbd9bda71a95ba6a6a29))
	// LCD Parameter Copyright MEI 1989 DAFT3C1 (missing dump), Keyboard-BIOS: Compability Software Phoenix Technologies LTD RBIOS Even 3ND1A4 Odd 3N1B4 (missing dump)
ROM_END

// Toshiba T3200SXC - CPU: 80386sx-20 - Floppy - 120 MB Hard disk - 1 MB RAM on board, 4 MB extra memory already installed. Can be upgraded to a total of 13 MB.
// TFT Display, 640x480, 256 colors - WD 90C21 video chip, 256 KB RAM - ISA8: 1, ISA16: 1
ROM_START( tot3200sxc ) // KBC ERROR
	ROM_REGION16_LE( 0x20000, "bios", 0)
	ROM_LOAD( "012c_t3200sxc_tc57h1024d-85.bin", 0x00000, 0x20000, CRC(5804a3da) SHA1(922dfb35b134a91a4c39e443597dad6798ce69d9))
ROM_END

// Sanyo MBC-18NB notebook - no display
ROM_START( mbc18nb )
	ROM_REGION16_LE( 0x20000, "bios", 0)
	ROM_LOAD( "sanyo_18nb.bin", 0x00000, 0x20000, CRC(64e283cf) SHA1(85ce4074c23b388d66e53cc83a8535bf7a2daf1f))
ROM_END

// Siemens-Nixdorf PCD-3Nsx notebook
// CPU: Intel NG680386SX-16 or -20 - 1MB RAM,upgradeable to 5MB, 40MB harddisk (-16 model)
// 2MB, upgradeable to 8MB, 80MB harddisk (-20 model), 9" mono VGA LCD, 1.44MB floppy drive
// Chipset: Headland HT21/E, , DP8473V, CHIPS F82C601, DS??87, unknown QFP100, ADC0833BCN (on PCU sub), CL-GD610-320C-C+CL-GD620-C
// Microcontrollers: N8042AH (KBC),  N80C51RH  (KBE)
// OSC: 14.318, 32.00024.0090
ROM_START( pcd3nsx )
	ROM_REGION16_LE( 0x20000, "bios", 0 )
	ROM_LOAD( "337-7_2400_3f_10-15-91.bin", 0x00000, 0x20000, CRC(99befce7) SHA1(150cd6a1476ca0ea970a1103b2a2c668c984433a) )

	ROM_REGION( 0x1000, "mcu", 0 )
	ROM_LOAD("33b_pcu_rev_3r.bin", 0x0000, 0x1000, CRC(d99308ec) SHA1(26621db2db37ab2bd1da972abb7398f9514329b2) )

	ROM_REGION( 0x800, "kbc", 0 )
	ROM_LOAD("kbc_c3f.bin", 0x000, 0x800, NO_DUMP)

	ROM_REGION( 0x1000, "kbe", 0 )
	ROM_LOAD("kbe_e3d.bin", 0x0000, 0x1000, NO_DUMP)
ROM_END

// Siemens-Nixdorf PCD-3Nsl - CPU: 80386SL@25MHz - 2MB RAM, upgradeable to 8MB - 85MB harddisk - 10" mono LCD VGA
// 1.44MB floppy drive
ROM_START( pcd3nsl )
	ROM_REGION16_LE( 0x20000, "bios", 0 )
	// Phoenix 80386 ROM BIOS PLUS Version 1.10.00 - failure at 100000-10FFFF - Resume memory backup failure
	ROM_SYSTEM_BIOS(0, "05-13-93", "05/13/93")
	ROMX_LOAD( "3n102l30.bin", 0x00000, 0x20000, CRC(02384c19) SHA1(552dc41b40272027e2b031187f8ab1e1513751b9), ROM_BIOS(0) )
	// Phoenix 80386 ROM BIOS PLUS Version 1.10.00 - Memory high address failure at 100000-10FFFF - Resume memory backup failure
	ROM_SYSTEM_BIOS(1, "01-26-94", "01/26/94")
	ROMX_LOAD( "3n120l40.bin", 0x00000, 0x20000, CRC(1336dd75) SHA1(80306d85f417c51a5235ac2f02ceb58bdb51205f), ROM_BIOS(1) )
ROM_END


// Toshiba T2000SX
// 1MB RAM on board, up to 9MB with 2MB, 4MB or 8MB expansion cards - 16 level grayscale VGA 640x480 display, PVGA1F display controller, 256KB VRAM
// Super integration (SI), components: DMAC 82C37Ax2, PIC 82C59Ax2, PIT 82C54, FDC TC8565, SIO TC8570 - 80C42 and 80C50 for keyboard - RTC 146818AF
// 128KB ROM, 32KB Backup RAM - GA-SYS CNT System control gate array - GA-IO CNT I/O gate array
ROM_START( t2000sx )
	ROM_REGION16_LE( 0x20000, "bios", 0 )
	ROM_LOAD( "014d.ic9", 0x00000, 0x20000, CRC(e9010b02) SHA1(75688fc8e222640fa22bcc90343c6966fe0da87f))
ROM_END


// Triumph-Adler Walkstation 386 SX - German version of the Olivetti S20
// VLSI VL82C320 + VL82C331; DP8473V
ROM_START( walk386sx )
	ROM_REGION16_LE( 0x20000, "bios", 0 ) // contains Cirrus Logic VGA BIOS
	ROM_LOAD( "cthj01_1014.bin", 0x00000, 0x20000, CRC(805084b9) SHA1(a92d78050844ccbcce53109c42603639aedd2335) )

	ROM_REGION( 0x2000, "mcu", 0 ) // MC68HC805B6FN
	ROM_LOAD( "cthj02_08_76.bin", 0x0000, 0x2000, NO_DUMP )

	ROM_REGION( 0x1000, "cop888", 0 ) // COPCL888-RDT/V
	ROM_LOAD( "s9124ab_c4_e904-34162_00.bin", 0x0000, 0x1000, NO_DUMP )
ROM_END

// Triumph-Adler Walkstation 386DX - German version of the Olivetti D33
// VLSI TOPCAT chipset: VL82C330 + VL82C331 + VL82C332 + VL82C106; Austek A38202C; DP8473V
// Video board: Cirrus Logic CL-GD610 + CL-GD620 + CL-GD63
ROM_START( walk386dx )
	ROM_REGION32_LE( 0x20000, "bios", 0 ) // contains Cirrus Logic VGA BIOS
	ROM_LOAD( "am28f010_ctaa060125rc.bin", 0x00000, 0x20000, CRC(6cc540fe) SHA1(9853793d5433bbc5efc09c7f31c4a8a8f78d4549) )

	ROM_REGION( 0x2000, "mcu", 0 )
	ROM_LOAD( "cthj02_03_76.bin", 0x0000, 0x2000, NO_DUMP )
ROM_END

// Siemens PG 750 - Luggable programmer ("Programmiergerät") for the Siemens S5 automation systems, a 486 EISA version exists as well
// Chipset: CHIPS P82C302, P82C301C, P82C206, four more "CHIPS" ICs
// ISA16: 7
// on board: V24/Mouse, V24/Modem, Printer
ROM_START( pg750 )
	ROM_REGION32_LE( 0x20000, "bios", 0 )
	// 0: Phoenix 80386 ROM BIOS PLUS Version 1.10 14 / SIEMENS PG-750
	// Time-of-day clock stopped
	// EGA/TIGA Graphics System "Highgraph II"
	ROM_DEFAULT_BIOS("v402")
	ROM_SYSTEM_BIOS(0, "v40", "v40")
	ROMX_LOAD( "pg750_4.0_386_l.bin", 0x10000, 0x8000, CRC(1e6dcd40) SHA1(2def9f729f43652a7b8b32a42e4c073f580d39ce), ROM_SKIP(1)|ROM_BIOS(0) )
	ROMX_LOAD( "pg750_4.0_386_h.bin", 0x10001, 0x8000, CRC(389c20dd) SHA1(eb0e86ba88ac9742868689b2aac9911ed7acac74), ROM_SKIP(1)|ROM_BIOS(0) )
	// 1: Phoenix 80386 ROM BIOS PLUS Version 1.10 17 / SIEMENS PG-750/730
	// Time-of-day clock stopped
	ROM_SYSTEM_BIOS(1, "v402", "v402")
	ROMX_LOAD( "pg750_4.02_386_l.bin", 0x10000, 0x8000, CRC(208aac51) SHA1(49d50b7ade8f56bda203375a9b138adf2cb5e500), ROM_SKIP(1)|ROM_BIOS(1) )
	ROMX_LOAD( "pg750_4.02_386_h.bin", 0x10001, 0x8000, CRC(c6e14fb6) SHA1(dad6ab5c5a18341ec0dce53fe712b8367340506b), ROM_SKIP(1)|ROM_BIOS(1) )
ROM_END

//**************************************************************************
//  80486 BIOS
//
//  BIOS files that show "original.tmp" near the beginning are compressed
//  and can be unpacked using, e.g., 7-ZIP.
//**************************************************************************

ROM_START( at486 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 30-0500-ZZ1130-00101111-070791-1219-0 /PAI JUNG ELECTRONIC IND. CO., LTD.
	ROM_SYSTEM_BIOS(0, "at486", "PC/AT 486")
	ROMX_LOAD( "at486.bin",   0x10000, 0x10000, CRC(31214616) SHA1(51b41fa44d92151025fc9ad06e518e906935e689), ROM_BIOS(0))
	// 1: BIOS-String: 40-0100-009999-11101111-070791-UMC480A-0 / United Microelectronics Corporation (UMC) MG-48602
	ROM_SYSTEM_BIOS(1, "mg48602", "UMC MG-48602")
	ROMX_LOAD( "mg48602.bin", 0x10000, 0x10000, CRC(45797823) SHA1(a5fab258aecabde615e1e97af5911d6cf9938c11), ROM_BIOS(1))
	// 2: BIOS-String: 40-0000-001470-00101111-060692-SIS3486-0 / 24X-VS-XX-B
	ROM_SYSTEM_BIOS(2, "ft01232", "Free Tech 01-232")
	ROMX_LOAD( "ft01232.bin", 0x10000, 0x10000, CRC(30efaf92) SHA1(665c8ef05ca052dcc06bb473c9539546bfef1e86), ROM_BIOS(2))

	/* 486 boards from FIC
	naming convention
	xxxxx101 --> for EPROM
	xxxxx701 --> for EEPROM using a Flash Utility v5.02
	xxxxBxxx --> NS 311/312 IO Core Logic
	xxxxCxxx --> NS 332 IO Core Logic
	xxxxGxxx --> Winbond W83787F IO Core Logic
	xxxxJxxx --> Winbond W83877F IO Core Logic
	*/
	// 3: BIOS-String: 06/16/97-VT82C486A-214L2000-00 / Version 3.276GN1
	/* this is the year 2000 beta bios from FIC, supports GIO-VT, GAC-V, GAC-2, VIP-IO, VIO-VP and GVT-2 */
	ROM_SYSTEM_BIOS(3, "ficy2k", "FIC 486 3.276GN1") /* includes CL-GD5429 VGA BIOS 1.00a */
	ROMX_LOAD( "3276gn1.bin",  0x00000, 0x20000, CRC(d4ff0cc4) SHA1(567b6bdbc9bff306c8c955f275e01ae4c45fd5f2), ROM_BIOS(3))
	// 4: BIOS-String: 04/29/94-VT82C486A-214L2000-00 / Award Modular BIOS v4.50
	ROM_SYSTEM_BIOS(4, "ficgac2", "FIC 486-GAC-2") /* includes CL-GD542X VGA BIOS 1.50 */
	ROMX_LOAD( "att409be.bin", 0x00000, 0x20000, CRC(c58e017b) SHA1(14c19e720ce62eb2afe28a70f4e4ebafab0f9e77), ROM_BIOS(4))
	// 5: BIOS-String: 04/08/96-VT82C486A-214L2000-00 / Version 3.27GN1
	ROM_SYSTEM_BIOS(5, "ficgacv", "FIC 486-GAC-V 3.27GN1") /* includes CL-GD542X VGA BIOS 1.41 */
	ROMX_LOAD( "327gn1.awd",   0x00000, 0x20000, CRC(017614d4) SHA1(2228c28f21a7e78033d24319449297936465b164), ROM_BIOS(5))
	// 6: BIOS-String: 05/06/94-VT82C486A-214L2000-00 / Version 3.15GN - ISA16:4, ISA/VL: 2
	ROM_SYSTEM_BIOS(6, "ficgiovp", "FIC 486-GIO-VP 3.15GN") // Chipset: VIP VT82C486A, Promise PDC20230C, one further VIA, one other unreadable
	ROMX_LOAD( "giovp315.rom", 0x10000, 0x10000, CRC(e102c3f5) SHA1(f15a7e9311cc17afe86da0b369607768b030ddec), ROM_BIOS(6))
	// 7: BIOS-String: 11/20/94-VT82C486A-214L2000-00 / Version 3.06G (11/25/94) - OSC: 24.0L3P - ISA16:3, ISA/VL: 2
	ROM_SYSTEM_BIOS(7, "ficgiovt", "FIC 486-GIO-VT 3.06G") // 1994-11-20 - Chipset: Winbond W83757AF, W83758P, VIA VT82C486A, VT8255N, VT82C482
	ROMX_LOAD( "306gcd00.awd", 0x10000, 0x10000, CRC(75f3ded4) SHA1(999d4b58204e0b0f33262d0613c855b528bf9597), ROM_BIOS(7))
	// 8: BIOS-String: 11/02/94-VT82C486A-214L2000-00 Version 3.07G - ISA8: 1, ISA16: 4, ISA/VL: 2
	ROM_SYSTEM_BIOS(8, "ficgvt2", "FIC 486-GVT-2 3.07G") // Chipset: VIA VT82C486A, VT82C482, VIA VT8255N
	ROMX_LOAD( "3073.bin",     0x10000, 0x10000, CRC(a6723863) SHA1(ee93a2f1ec84a3d67e267d0a490029f9165f1533), ROM_BIOS(8))
	// 9: BIOS-String: 06/27/95-VT82C505-2A4L4000-00 / Version 5.15S - Chipset: S3 Trio64, FDC 37665GT, VT82C496G, VT82C406MV
	ROM_SYSTEM_BIOS(9, "ficgpak2", "FIC 486-PAK-2 5.15S") /* includes Phoenix S3 TRIO64 Enhanced VGA BIOS 1.4-01 */
	ROMX_LOAD( "515sbd8a.awd", 0x00000, 0x20000, CRC(778247e1) SHA1(07d8f0f2464abf507be1e8dfa06cd88737782411), ROM_BIOS(9))
	// 10: BIOS-String: 04/01/96-VT496G-2A4L6F0IC-00 0000C-00 - runs into Award BootBlock BIOS - Chipset: VIA VT82C505, VT82C416, VT82C496G, Winbond W83787F
	ROM_SYSTEM_BIOS(10, "ficpio3g7", "FIC 486-PIO-3 1.15G705") // pnp  - ISA16: 4, PCI: 3
	ROMX_LOAD( "115g705.awd",  0x00000, 0x20000, CRC(ddb1544a) SHA1(d165c9ecdc9397789abddfe0fef69fdf954fa41b), ROM_BIOS(10))
	// 11: BIOS-String: 04/01/96-VT496G-2A4L6F0IC-00 0000C-00 - runs into Award BootBlock BIOS
	ROM_SYSTEM_BIOS(11, "ficpio3g1", "FIC 486-PIO-3 1.15G105") /* non-pnp */
	ROMX_LOAD( "115g105.awd",  0x00000, 0x20000, CRC(b327eb83) SHA1(9e1ff53e07ca035d8d43951bac345fec7131678d), ROM_BIOS(11))
	// 12: BIOS-String: 11/27/96-VT496G-2A4L6F0IC-00 0000C-00 - runs into Award BootBlock BIOS
	ROM_SYSTEM_BIOS(12, "ficpos", "FIC 486-POS")
	ROMX_LOAD( "116di6b7.bin", 0x00000, 0x20000, CRC(d1d84616) SHA1(2f2b27ce100cf784260d8e155b48db8cfbc63285), ROM_BIOS(12))
	// 13: BIOS-String: 06/27/95-VT82C505-2A4L4000-00 / Version 5.15 / Chipset: VIA VT82C496G PC/AT
	ROM_SYSTEM_BIOS(13, "ficpvt", "FIC 486-PVT 5.15") // ISA16: 6, ISA/VL: 2
	ROMX_LOAD( "5150eef3.awd", 0x00000, 0x20000, CRC(eb35785d) SHA1(1e601bc8da73f22f11effe9cdf5a84d52576142b), ROM_BIOS(13))
	// 14: BIOS-String: 10/05/95-VT82C505-2A4L4000-00 / Version 5.162W2(PCTIO)
	ROM_SYSTEM_BIOS(14, "ficpvtio", "FIC 486-PVT-IO 5.162W2")  // Chipset: VT82C406MV, VT82C496G, W83777/83787F, W83758P
	ROMX_LOAD( "5162cf37.awd", 0x00000, 0x20000, CRC(378d813d) SHA1(aa674eff5b972b31924941534c3c988f6f78dc93), ROM_BIOS(14))
	// 15: BIOS-String: 40-00AG-001247-00101111-060692-SIS3486-0 / AV4 ISA/VL-BUS SYSTEM BIOS / Chipset: SIS 85C460ATQ
	ROM_SYSTEM_BIOS(15, "ava4529j", "AVA4529J") // this is a board with two VLB slots
	ROMX_LOAD("amibios_486dx_isa_bios_aa4025963.bin", 0x10000, 0x10000, CRC(65558d9e) SHA1(2e2840665d069112a2c7169afec687ad03449295), ROM_BIOS(15))
	// 16: BIOS-String: 40-0200-001291-00101111-111192-OPT495SX-0 / 34C-OP-WBp-25/33/40/50-D5-ZZ
	// Chipset: OPTi 82C495SX - CPU: 486DX - BIOS: AMI 486DX ISA BIOS AA7524842 - ISA8: 1, ISA16: 4, ISA16/VL: 2
	ROM_SYSTEM_BIOS(16, "pat48pv", "PAT-48PV")
	ROMX_LOAD("pat48pv.bin", 0x10000, 0x10000, CRC(69e457c4) SHA1(7015b2bccb10ce6e1ad6e992eac785f9d59a7a24), ROM_BIOS(16))
	// 17: Morse P1 V3.10 - CPU: 486DX - ISA8: 2, ISA16: 6 - Chipset: Morse 91A401A- Award Modular BIOS v4.20 / V3.00 - KEYBOARD ERROR OR NO KEYBOARD PRESENT
	ROM_SYSTEM_BIOS(17, "p1", "P1")
	ROMX_LOAD("morse_p1.bin", 0x10000, 0x10000, CRC(23d99406) SHA1(b58bbf1f66af7ed56b5233cbe2eb5ab623cf9420), ROM_BIOS(17))
	// 18: Chipset: SiS 85C206 CONTAQ 82C592 82C591 - CPU/FPU: 486, socket provided - OSC: 33.333MHz, 14.31818 - BIOS: AMI 486DX ISA BIOS AA0083611 (28pin)
	// BIOS-String: 40-0700-D01508-00101111-070791-CTQ 486-0 - ISA8: 2, ISA16: 6
	ROM_SYSTEM_BIOS(18, "82c591", "82C591")
	ROMX_LOAD("486-contaq.bin", 0x10000, 0x10000, CRC(e5d2cf16) SHA1(1357a964ef78eaad6894dcc9dce62be50cdf6df5), ROM_BIOS(18))
	// 19: Chipset: PCCHIPS CHIP 16 (9430-AS), CHIP 18 (9432-AS) - CPU: i486DX2-66 - BIOS: AWARD (28pin) - ISA16: 4, ISA16/VL: 3 - OSC: 14.31818MHz
	// BIOS-String: 07/13/94--2C4X6H01-00 / Release 07/15/94'
	ROM_SYSTEM_BIOS(19, "chips", "Chips")
	ROMX_LOAD("486-pcchips.bin", 0x10000, 0x10000, CRC(4e49eca1) SHA1(2343ca9f4760037eb2ef6e7b011b9690e542d6ea), ROM_BIOS(19))
	// 20: CAM/33(50)-P8 M458(A)P80 - Chipset: Opti 82C495SX, F82C206Q 82C392SX - CPU: 486DX-33 (solder pads for 486sx and 486DX) - OSC: 14.318MHz, 33.000MHz
	// Keyboard-BIOS: AMI Keyboard BIOS PLUS A317473 - BIOS: AMI 486 BIOS PLUS 214097 (28pin) - RAM: SIMM30x8 - Cache: 1xIS61C256A, 8xUM61256BK-25 - ISA8: 1, ISA16: 6
	// BIOS-String: X0-0101-001105-00101111-060692-495SX_A-0 / 486DX/SX CAM/33,50-P8, CPM/25,33-P8, 12/14/1992
	ROM_SYSTEM_BIOS(20, "cam33", "CAM/33")
	ROMX_LOAD("486-cam.bin", 0x10000, 0x10000, CRC(d36a13ea) SHA1(14db51dbcf8decf1cb333c57a36971ef578c89b4), ROM_BIOS(20))
	// 21: 486-PIO3 1.1 - Chipset: Winbond W83787F, VIA VT82C505, VT82C416, VT82C496G - ISA16: 4, PCI:3 - BIOS: AWARD F 4825803 1.14G705 (32pin) - CPU: Socket 3
	// RAM: 2xSIMM72, Cache: 9 sockets marked SRAM 128Kx8 (2 banks +1) - On board: 2xIDE, Floppy, par, 2xser
	// BIOS-String: 02/01/96-VT496G-2A4L6F0IC-00 0000C-00 . runs into BootBlock BIOS
	ROM_SYSTEM_BIOS(21, "pio3", "486-PIO-3")
	ROMX_LOAD("486-pio3.bin", 0x00000, 0x20000, CRC(1edb5600) SHA1(36887cd08881dfa063b37c7c11a6b65c443bd741), ROM_BIOS(21))
	// 22: 486 G486IP IMS - Chipset: IMS 8848 IMS 8849 - CPU: i486DX2-66 - BIOS: AMI 486DX ISA BIOS AB5870352 - Keyboard-BIOS: MEGAKEY (AMI/Intel) - ISA8: 1, ISA16: 4, PCI: 3
	// RAM: SIMM30: 4, SIMM72: 2, Cache: 10 sockets (UM61256AK-15) - BIOS-String: 41-0000-ZZ1124-00101111-060692-IMS8849-0 / PCI BIOS, Dated JUN-16-94 / FOR G486IP
	ROM_SYSTEM_BIOS(22, "g486ip", "G486IP")
	ROMX_LOAD("g486ip_ims.bin", 0x00000, 0x20000, CRC(4431794a) SHA1(f70e8c326455229c3bb7f305c2f51c4ac11979ed), ROM_BIOS(22))
	// 23: EFA 486 UPIO
	// BIOS-String: 11/09/95-UMC-881/886A-2A4X5E39C-00 00 / N486U-PIO/A, Rev 1.03 ROM - NOT FOR SALE - boots into BootBlock BIOS
	ROM_SYSTEM_BIOS(23, "486upio", "486 UPIO")
	ROMX_LOAD("upio_103.bin", 0x00000, 0x20000, CRC(4e9139cd) SHA1(f2b00356957c712ca652c3751b31161b3110ec69), ROM_BIOS(23))
	// 24: Acer 486 Version 2.2 - Chipset: ALi M1429, M1431 - CPU: 486 - RAM: SIMM30x8
	// screen remains blank
	ROM_SYSTEM_BIOS(24, "acer48622", "Acer 486 V2.2.")
	ROMX_LOAD("4alm002.bin", 0x10000, 0x10000, CRC(88291af2) SHA1(7ff912e9f0550631377d1a4c3aa266a081e7dce9), ROM_BIOS(24))
	// 25: ACR6BE00-M00-940720-R01-E0 / BIOS V2.0 - Keyboard Interface Error, Pointing DeviceInterface Error
	ROM_SYSTEM_BIOS(25, "4alo001", "4ALO001")
	ROMX_LOAD("4alo001.bin", 0x00000, 0x20000, CRC(4afb9c50) SHA1(5e56682ba1e04bd0b074de3b2a93fb5322325d01), ROM_BIOS(25))
	// 26: dies after initialising the graphics card
	ROM_SYSTEM_BIOS(26, "4alp001", "4ALP001")
	ROMX_LOAD("4alp001.bin", 0x10000, 0x10000, CRC(9b4a2881) SHA1(f324bb0304164e9ede1dd2eebb085a76aae398be), ROM_BIOS(26))
	// 27: BIOS-String: 30-0500-ZZ1130-00101111-070791-1219-0
	ROM_SYSTEM_BIOS(27, "zz1130", "ZZ1130")
	ROMX_LOAD("4zzw001.bin", 0x10000, 0x10000, CRC(dc21c952) SHA1(affdc4efbca4dad561e4f0141463844ec84ae519), ROM_BIOS(27))
	// 28: screen remains blank
	ROM_SYSTEM_BIOS(28, "optimus", "Optimus")
	ROMX_LOAD("mb_bios_ami_930808.bin", 0x10000, 0x10000, CRC(89151d5b) SHA1(92a93cae054525adfdc6277a1236e699ea9fbc32), ROM_BIOS(28))
	// 29: 40-0100-DG1112-00101111-070791-UMC480A / Rev. 251191 UMC-486A - ASI board
	// The BIOS comes from http://www.elhvb.com/supportbios.info/Archives/BIOS/0-A/ASI/UMC-486A/index.html and contains 28 extra plain textattr
	// bytes at the end of the file. These have been lopped off, but the emulated machine complains about a ROM error, thus marked BAD_DUMP
	ROM_SYSTEM_BIOS(29, "umc486a", "UMC-486A")
	ROMX_LOAD("umc481icorr.ami", 0x10000, 0x10000, BAD_DUMP CRC(d27b2fd4) SHA1(e639dbc7d65b29ffca26701af766fa75bfe33787), ROM_BIOS(29))
	// 30: 40-0201-D41107-00101111-031591-OPBC-0
	ROM_SYSTEM_BIOS(30, "a9c11f1f", "a9c11f1f")
	ROMX_LOAD("ami_486_zz686886.bin", 0x10000, 0x10000, CRC(a9c11f1f) SHA1(2a27ecae9547ddd3d230c30a94deb83a4d6b4436), ROM_BIOS(30))
	//31: Award Modular BIOS v4.20 / Version 1.09K
	ROM_SYSTEM_BIOS(31, "109k", "1.09K")
	ROMX_LOAD("award_486dx_0097042.bin", 0x10000, 0x10000, CRC(b620534b) SHA1(d3777a82cb35639d386a1840dd5cf52527ec6f8b), ROM_BIOS(31))
	// 32: UNIC2 94V-0 9326 - OPTI chipset (3 chips)
	// BIOS-String: X0-0100-001378-00101111-060692-495SX_A
	ROM_SYSTEM_BIOS(32, "unic2", "UNIC 2 94V-0")
	ROMX_LOAD("amibios-486dx-1992-aa8707058-m27c512.bin", 0x10000, 0x10000, CRC(a2b3e326) SHA1(b29c5668fb3337893ef3a96f053f90b929bac0d6), ROM_BIOS(32))
	// 33: BIOS-String: 40-0100-001276-00101111-060692-495_X86-0
	// OPTi 82C495 SX, F82C206 - CPU: two solder pads and two CPU sockets - RAM: 8xSIMM30, Cach: 10x28pin DIP - ISA8: 2, ISA16: 3, ISA16/VLB: 3
	ROM_SYSTEM_BIOS(33, "495sx1", "495sx-1")
	ROMX_LOAD("495sx-1.bin", 0x10000, 0x10000, CRC(318a99a3) SHA1(e57380ed2a802cd1648a32317313ade5221f1213), ROM_BIOS(33))
	// 34: Chipset: marked QDI - CPU: Socket 3, 25MHz-50MHz - RAM: 4xSIMM30, 2xSIMM72, Cache: 8x32pin DIP (4x28pin DIP filled), 1x32pin DIP (filled with 28pin DIP)
	// BIOS: AMI 486DX ISA BIOS AC2849676 - OSC: 18.31818 - ISA8: 1, ISA16: 3, ISA16/VL: 3
	ROM_SYSTEM_BIOS(34, "qdi486", "QDI486") // screen remains blank
	ROMX_LOAD("qdi486.bin", 0x10000, 0x10000, CRC(f44a5a45) SHA1(57cfd7c6524eba21395642bd57b726b45eef4b6a), ROM_BIOS(34))
ROM_END


//**************************************************************************
//  80486 motherboard
//************************************************************************//

// Addtech Research 4GLX3 Green-B 4GPV3.1 aka VisionEX 4GPV3 - Chipset: Contaq 82C596A - BIOS Version: AMI 01/10/94 486 ISA BIOS AA 6729627
// Keyboard BIOS: AMI - CPU: Socket 3, solder pads for 80486DX/SX provided - RAM: 8xSIMM30, Cache: 9x28pin DIP - ISA16: 8, ISA16/VLB: 2
ROM_START( ar4glx3 ) // BIOS-String: 40-0101-006666-00101111-011094-CTQ-596A / KIM Computer by CTL Corporation / Model : [4GLX3] --- Made in U.S.A.
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "4glx3_bios.bin", 0x10000, 0x10000, CRC(3367e0b2) SHA1(8dbe58ed783c56ca2cb61ded6f603314739dcfb8))
ROM_END

// TMC PAT48AV 1.4 - ALi M1429 A1, M1431 A2 - BIOS: AMI 486DX ISA BIOS AB4179743 - Keyboard BIOS: AMI-KB-H-WP - CPU: 486DX socket
// RAM: 8xSIMM30 - Cache: 8xIS61C256AH-20N - OSC: 14.31818 - ISA16: 4, ISA16/VLB: 3
ROM_START( tmpat48av ) // screen remains blank - BIOS-String: 40-0106-001291-00101111-080893-ALI1429 / 486DX-AC-WBu-25/33/40/50-L6-ZZ
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "pat48av_bios_27c512.bin", 0x10000, 0x10000, CRC(a054b7b3) SHA1(edc2554a73aba94d586f8b49a5c5bbbe2890331c))
ROM_END

// BIOSTAR - MB-1433/50 AEA-P - V:1 - Chipset: VLSI 82C3480 & 83C3490 (marked BIOTEQ) - BIOS/Version: AMIBIOS 12/12/91 - Keyboard BIOS: MEGA-KB-F-WP
// CPU: It's ST 486Dx2-80 - RAM: 8xSIMM30, Cache: 8xW24M257AK-15, 1xW2465AK-15 - ISA8: 1, ISA16: 6 - OSC: 66.000MHz, 14.318
ROM_START( mb1433aeap ) // 40-0100-001223-00101111-121291-B82C3480-0 / M1433/50AEA-P
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "mb-1433-aea-p.bin", 0x10000, 0x10000, CRC(daac20f7) SHA1(b6889a4896f83b306e69efa87cae8d03147b6dbd))
ROM_END

// FIC ELI6-II (from Unisys ELI 46665 Desktop) - Chipset: VIA 82C486A, VT82C487, SMC FDC37C665QF P - BIOS label: ELI6-U555
// BIOS Version: Award U573 3/14/95 - CPU: Intel Overdrive DX40DPR100 in Socket 3 - RAM: 4xSIMM72, Cache: 9x256K-15 - ISA16:6 - On board: ISA, Floppy
ROM_START( ficeli6ii ) // BIOS String: 03/14/95-VT82C486A-214L2000-00 / Award Modular BIOS v4.50 /3.20G /F5 1.00 / UNISYS ELI6-II Version U573.BIN (03/14/95)
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "fic_eli6ii.bio", 0x10000, 0x10000, CRC(b61cc026) SHA1(59eae42bf1dea01ba04fecd9bb367e47d4a256d4))
ROM_END

// Mitac PWA-IH4077D - Chipset: EFAR EC802GL, EC100G, UMC UM82C863F, UM82C865F - CPU: Socket 3 - RAM: 4xSIMM72, Cache: 9xIS61C256AH-20N
// BIOS: known: R2.03/CKS: 2400H, ATT4077D BIOS R1.01.00/CKS: 3000H - Keyboard BIOS: Award KB-200 or VIA VT82C42N (on ATT4077D)
// On board: 2xser, par, Floppy, IDE - ISA16: 4, ISA17/VL: 2
ROM_START( pwaih4077d ) // BIOS-String: 04/02/98-EFAR-EC802G-B-2C403D31-00 / (IH4077D R2.08G)
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "ih4077d.208g", 0x10000, 0x10000, CRC(0f834ba2) SHA1(7e08e454dfa3cf5079845fe61b9ae74b1dcc7981))
ROM_END

// ASUS ISA-486SIO rev. 1.2 - Chipset : SiS 85C460 ATQ, Winbond W85C16B - BIOS : AMI 486DX/ISA BIOS AA2310181 - Keyboard BIOS: AMI
// CPU: Intel 80486SX-25 - RAM: 8xSIMM30, Cache: 9x28pin DIP (used: 4xM5M5276P-25, 1xUM6164BK-20) - ISA8: 1, ISA16: 6 - OSC: 25.000MHz, 14.31818
ROM_START( a486sio )// BIOS-String : 40-0104-001292-00101111-050591-I486SI-F
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "isa-486sio.bin", 0x10000, 0x10000, CRC(f339f8ff) SHA1(d53f0ff30cc7f0c70ffeeda33d16dddbeedd6098))
ROM_END

// Micronics JX30GP - Motherboard P/N: 09-00189-10 REV B1 - Chipset: MIC 471, MIC491, PC87312VF (Super I/O), KS82C6818A -
// CPU: Socket 3, solder pads for 80486QFP - RAM: 4xSIMM72, Cache: 6xUM61256FK-15, 1xW24257AK-15 - DIP4: 0000 - OSC: 14.318
// ISA16: 5, ISA16/VLB: 2 - on board: Floppy, ISA, PS/2 keyboard and mouse
ROM_START( mijx30gp ) // BIOS: Phoenix, 80486 ROM BIOS PLUS Version 0.10 GJX30G-04P, Gateway 2000 Local Bus BIOS - shows Error 8602 - Auxiliary Device Failure
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "micronics_gjx30g-04p_09-24-93.bin", 0x10000, 0x10000, CRC(66477a66) SHA1(549eecf707bbb43bcdc89715b36cc23e3cb1a074))
	ROM_IGNORE(0x10000) // the second half of the 128K ROM seems to contain BIOS source code
ROM_END

// AMI 80486 EISA Enterprise-II - Chipset: AMI/Intel 82357 - RAM: 8xSIMM30 (32MB max), 128K cache memory - CPU: i486DX 25/33, Weitek WTL4167 FPU socket provided
// on board: Floppy, PS/2 mouse, memory card socket, i/o expansion module slot - EISA: 8
// BIOS-String: 41-0001-004616-00111111-070791-AMI-EP-0 - EISA CMOS INOPERATIONAL / Software Port NMI INOPERATIONAL / Fail-safe Timer NMI INOPERATIONAL / Keyboard error
ROM_START( amient2 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "16092391.cs1", 0x00000, 0x20000, CRC(b0ae5c8d) SHA1(0d6cda74433a5d70e8fc2bec6a77ed97a09a984e))
ROM_END

// AMI Enterprise-III EISA Local Bus Series 68 - CPU: i486SX/DX/DX2 25/33/50/66MHz - RAM: 16xSIMM32 (up to 256MB RAM!!!), Cache 256K
// EISA: 6, EISA/VL: 2 - BIOS: Flash - repeated short single beeps (DRAM refresh error)
// BIOS-String: 41-0103-004668-00111111-111192-AMIS68
ROM_START( amient3 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "s68p.rom", 0x00000, 0x20000, CRC(7342b9ef) SHA1(da6b2312ccc175473443b1d562d5e4f3952cac5a))
ROM_END

// AMI ENTERPRISE-IV EISA VLB - CPU: 486SX/486SX/486DX/DX2/Pentium overdrive 25/33/50/66 MHz - RAM: 4xSIMM72 (256MB), Cache: 256KB
// EISA: 5, EISA/VL: 2 - on board: floppy, IDE, par, 2xser - screen remains blank
// BIOS-String: 41-0103-004687-00111111-111192-AMIS87
ROM_START( amient4 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "s87p.rom", 0x00000, 0x20000, CRC(ff092e7f) SHA1(4999278dca001de74dff518f1f1c9ea8212d7ed4))
ROM_END

// AMI Super Voyager PCI-II - Chipset: AMI, SMC - CPU: 486DX/DX2/DX4 - RAM: 4xSIMM72 (64MB), Cache: 5x32pin, 4x28pin+TAG (127/256K)
// BIOS: Flash - Keyboard-BIOS: AMIKEY - ISA16: 4, PCI: 3 - on board: Floppy, 2xIDE, 2xer, par - screen remains blank
ROM_START( amisvpci2 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "s724p.rom", 0x00000, 0x20000, CRC(fa9ea9b3) SHA1(b0f2206a7f0d6a00e094f7d151c16022a5292858))
ROM_END

// AMI Super Voyager VLB - screen remains blank
// BIOS-String: 40-0103-004669-00111111-111192-AMIS69B2
ROM_START( amisvvlb )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "s69b2p.rom", 0x00000, 0x20000, CRC(3a13944c) SHA1(9d7733ee50023edd5b0bf4b098c9c6c35a4dc2b0))
ROM_END

// AMI Super Voyager VLB-II - AMI Super Voyager VLB-II - CPU: 486/Overdrive - Chipset: AMI, SMC FDC37C665GT
// RAM: 4xSIMM72, Cache: 8x28pin + TAG - ISA16: 5, ISA16/VL: 2 - BIOS: 28pin in a 32pin socket - on board: Floppy, IDE
// BIOS-String: 40-0100-004682-00111111-111192-AMIS82E
ROM_START( amisvvlb2 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "s82p.rom", 0x00000, 0x20000, CRC(2e2d33ae) SHA1(349be4b29bc8a4447ab4c73fe4c695276565489f))
ROM_END

// AMI Super Voyager VLB-III - Chipset: FDC37C665GT, AMI - CPU: 486SX, 487DX, 486DX/DX2/DX4, Overdrive
// RAM: 4xSIMM72 - on board: mouse, par, 2xser, Floppy, IDE - ISA16: 5, ISA16/VL: 2 - screen remains blank, repeated single beeps
ROM_START( amisvvlb3 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "s707p.rom", 0x00000, 0x20000, CRC(5c0e55c0) SHA1(0c5232a21180d4541202ac6dd34677339b7cbecc))
ROM_END

// Edom 486VL3H MV020 - CPU: 486 - Chipset: HiNT CS8005, HMC HM82C206AQ - RAM: 8xSIMM30
// Cache: IS61C256AH-20Z (8), AE88128AK-15 - Keyboard-BIOS: JETkey V5.0 - ISA8: 2, ISA16: 3, ISA16/VLB: 3
// BIOS-String: 09/05/94-HINT-8005-214D1W00-00 / HiNT CS8005 FOR 486SX/DX/DX2 VL-BUS [MV020]
ROM_START( ed486vl3h )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "4hiw001.bin", 0x10000, 0x10000, CRC(760842fd) SHA1(2fe156f092c84cf385079da1209a8f1e06005f5e))
ROM_END

// MSI MS-4134 - Chipset: ALI M1429, M1431, M1435 - CPU: Socket 3, RAM: 4xSIMM30, 2xSIMM72, Cache: 8x32pin + TAG, used: 9xW2464AK-20
// ISA16: 3, ISA16/VL: 2, PCI: 3 - BIOS: AMI
// BIOS-String:
ROM_START( ms4134 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0:  BIOS-String: 40-0105-421169-00101111-080893-1429GPCI-0 / AL57 111594
	ROM_SYSTEM_BIOS(0, "al57", "AL57")
	ROMX_LOAD( "al57.rom", 0x00000, 0x20000, CRC(6b6c2a11) SHA1(1e40ef8a7a7b3be057ba6a121abfd0d983d5d5c9), ROM_BIOS(0))
	// 1: BIOS-String: 40-0105-421169-00101111-0890893-1429GPCI-0 / AL51 5/23/1994
	ROM_SYSTEM_BIOS(1, "al51", "AL51")
	ROMX_LOAD( "4alm001.bin", 0x00000, 0x20000, CRC(0ea9f232) SHA1(5af1a0cf047b68a7070b8c45081a80e817aade84), ROM_BIOS(1))
ROM_END

// Abit 486 EISA-AE4 - Chipset: SiS 85C406, 85C411, three other SiS chips unreadable - CPU: 486, FPU socket provided - RAM: 8xSIMM30, Cache: 8x28pin
// ISA16: 2, EISA: 6 - BIOS: Award EISA486/CU - Keyboard-BIOS: NEC KB-BIOS VER:400 JU-JET 1989 -  OSC: 50.000MHz, 14.31818MHz
// Award Modular BIOS v4.20 / AE4 EISA SYSTEM BIOS
ROM_START( abae4 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "ae4.bin", 0x10000, 0x10000, CRC(d9cbc3c6) SHA1(eeeaef7fd188598d477897f0248c99940cd1a5d7))
ROM_END

// FIC 486-KVD - Chipset: VIA VT82C485 - CPU: 486, solder pad for 486sx present - RAM: 8xSIMM30, Cache: 4xIS61C256A-20N+1xCY7C185-20PC, 4 empty sockets (28pin)
// ISA16: 5, ISA16/VL: 2 - BIOS: AMI 486DX ISA BIOS AA7211137 - Keyboard-BIOS: MEGA-KB-H-WP

ROM_START( fic486kvd )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: X0-0100-001121-00101111-021993-VIA-0
	ROM_SYSTEM_BIOS(0, "021993", "021993")
	ROMX_LOAD( "486kvd_aa72111137.bin", 0x10000, 0x10000, CRC(a1f1810f) SHA1(405afbf1635c6b41343aabfeeb3cf4cdc947a5ba), ROM_BIOS(0))
	// 1: BIOS-String: X0-0100-001121-00101111-021993-VIA-0
	ROM_SYSTEM_BIOS(1, "060692", "060692")
	ROMX_LOAD( "486-aa9615811.bin", 0x10000, 0x10000, CRC(b6b1a8e4) SHA1(be537fc27f6dedbd7fd935a7900ec075d2183837), ROM_BIOS(1))
ROM_END

// Eagle EAGLEN486 GC10A - Chipset: NEC ADC006, LGS Prime 3B 9543 - CPU: Socket 3 - RAM: 2xSIMM72, Cache: fake (not connected, marked write back)
// On board: IDE, Floppy, 2xser, par - ISA16: 4, PCI: 2 - BIOS: 32pin (sst29ee010), only the first half is occupied - // BIOS-String: Phoenix NuBIOS Version 4.04
ROM_START( gc10a )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "nec_sst29ee010_orig.bin", 0x10000, 0x10000, CRC(7b1feabb) SHA1(468734b766b9c438b2659fddf2cabcfde5a574a2))
	ROM_IGNORE(0x10000)
ROM_END

// Arstoria AS496 - Chipset: SiS 85C495, 95C497, Winbond - CPU: Socket 3 - RAM: SIMM72x4, Cache: 4+1 - BIOS: 32pin  Keyboard-BIOS: BESTKEY - ISA16: 4, PCI: 3
// BIOS-String: 09/12/96-SiS-496-497/A/B-2A4IBR2CC-00 / ARSTORIA AS496 V2 09/12/96 - boots to Boot Block BIOS
ROM_START( as496 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "as496.bin", 0x00000, 0x20000, CRC(745f8cc8) SHA1(46b9be25a7027a879482a412c9fe5687bbb28f08))
ROM_END

// Peacock PCK 486 DX DOC 50-60064-00 - Chipset: Symphony SL82C465 SL82C461 SL82C362 Chips F82C721 - CPU: i486DX-33, FPU socket privoded
// BIOS: AMI 486DX ISA BIOS AA3364567 - Keyboard-BIOS: AMI/Intel P8942AHP - On board: 2xser, Floppy, IDE, par - OSC: 33.000MHz
// BIOS-String: 40-0100-806294-00101111-060692-SYMP-0 / Peacock Computer 486 BIOS Rev. 2.0 / 30.11.92 - ISA16: 6
ROM_START( pck486dx )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "pck486dx.bin", 0x10000, 0x10000, CRC(d0edeba8) SHA1(b5b9492f32e35764c802be2b05a387a9b3aa7989))
ROM_END

// FIC 486-GIO-VT2 - Chipset: Winbond W83758P, Winbond W83757AF, VIA VT82C482, VT82C486A, VT82C461 - ISA8: 1, ISA16: 3, ISA/VL: 2
// On board: Game, 2xIDE, 2xser, par, Floppy
ROM_START( ficgiovt2 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 07/06/VT82C486A-214L2000-00 / Version  3.26G
	ROM_SYSTEM_BIOS(0, "ficgiovt2_326", "FIC 486-GIO-VT2 3.26G")
	ROMX_LOAD( "326g1c00.awd", 0x10000, 0x10000, CRC(2e729ab5) SHA1(b713f97fa0e0b62856dab917f417f5b21020b354), ROM_BIOS(0))
	// 1: BIOS-String: 06/19/95-VT82C486A-214L2000-00 / Version VBS1.08H 486-GIO-VT2
	ROM_SYSTEM_BIOS(1, "vt2vbs108","VBS1.08H 486-GVT-2")
	ROMX_LOAD( "award_486_gio_vt2.bin", 0x10000, 0x10000, CRC(58d7c7f9) SHA1(097f15ec2bd672cb3f1763298ca802c7ff26021f), ROM_BIOS(1)) // Vobis version, Highscreen boot logo
	// 2: BIOS-String: 07/17/97-VT82C486A-214L2000-00 / Version 3.276
	ROM_SYSTEM_BIOS(2, "ficgiovt2_3276", "FIC 486-GIO-VT2 3.276")
	ROMX_LOAD( "32760000.bin", 0x10000, 0x10000, CRC(ad179128) SHA1(595f67ba4a1c8eb5e118d75bf657fff3803dcf4f), ROM_BIOS(2))
	// 3: BIOS-String: 08/30/94-VT82C486A-214L2000-00 / Version VBS1.04 486-GIO-VT2 - Keyboard-BIOS: VT82C42N
	ROM_SYSTEM_BIOS(3, "vt2vbs104","VBS1.04 486-GVT-2")
	ROMX_LOAD( "486-gio-vt2.bin", 0x10000, 0x10000, CRC(7282133d) SHA1(c78606027eca509cd6d439e4689b8d50753ee80c), ROM_BIOS(3)) // Vobis version, Highscreen boot logo
ROM_END

// FIC 486-GVT - VIA VT82C486, VIA VT82C482 - AMIBIOS 08/08/93 - CPU: P24T, solder pads for 486 provided - RAM: SIMM30: 4, SIMM72: 2, Cache: 9x28pin DIP
// ISA16: 4, ISA16/VLB: 2
ROM_START( fic486gvt ) // BIOS-String: X0-0100-001121-00101111-112593-VT486N8-0
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "486gvt.bin", 0x10000, 0x10000, CRC(4c5b4bde) SHA1(04711725fe89d9c793a369d82d411a5495ae3aea))
ROM_END

// Octek Hawk REV 1.1 - BIOS: AMI AA1481746 486DX ISA BIOS 28pin - Keyboard-BIOS: Intel/AMI - Chipset: OPTi F82C206L, 82C496 - OSC: 66.667MHz, 14.31818MHz
// BIOS-String: 40-0100-000000-00101111-121291-OPTIDXBB-0 / HAWK -011 - CPU: Intel Overdrive DX2ODPR66 - ISA16: 7
ROM_START( ochawk )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "hawk.bio", 0x10000, 0x10000, CRC(365b925d) SHA1(3a1776c80540b6878ff79857c2d4e19320a2792a))
ROM_END

// Abit AB-PW4 - Chipset: Winbond W83C491F, W83C492F (SL82C491 Symphony Wagner) - BIOS/Version: Award D2144079 - CPU: i486sx-25 - ISA8: 1, ISA16: 3, ISA16/VL: 3
ROM_START( abpw4 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 03/21/95-Winbond-83C491-2C4J6A11-46 / Award v4.50G / GREEN CACHE 486 VESA SYSTEM BIOS
	ROM_SYSTEM_BIOS(0, "2c4j6a11", "2C4J6A11-46")
	ROMX_LOAD( "award_486_bios_d2144079_c1984-1995.bin",0x10000, 0x10000, CRC(c69184da) SHA1(e8a799f9a3eebfd09c1d19a909574fca17fce7a0), ROM_BIOS(0))
	// 1: BIOS-String: 09/12/95-Winbond-83C491-2C4J6A12-2E
	ROM_SYSTEM_BIOS(1, "2c4j6a12", "2C4J6A12-2E")
	ROMX_LOAD( "pw4_2e.bin", 0x10000, 0x10000, CRC(c4aeac4d) SHA1(e58f2e2d5c337f447808535629686dde54c09fab), ROM_BIOS(1))
ROM_END

// Vintage Sprite SM 486-50USC - Chipset: UM82C491F - BIOS: EPROM/MR-BIOS 1.50 - Keyboard-BIOS: JETkey V3.0
// CPU: Intel 486DX2-66 - OSC: 33.333000MHz, 14.31818MHz - ISA16: 5, ISA16/VL: 2
ROM_START( sm48650usc ) // constant reset
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "mrbios_1150usc_um82c491f.bin", 0x10000, 0x10000, CRC(b6ef1220) SHA1(94511df49713ec30467c8d9b18eb04e83fa7a809))
ROM_END

// Octek Hippo VL+ - CPU: 486 - BIOS: EPROM/MR - Keyboard-BIOS: MR/Amikey - Chipset: DCA/Octek (label stickers) - ISA16: 3, ISA16/VL: 3
// MR BIOS (r) V1.52 / 486SLC CPU 28MHz
ROM_START( ochipvlp )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0:  // reset loop
	ROM_SYSTEM_BIOS( 0, "v152", "V1.52")
	ROMX_LOAD( "vlmr152.rom", 0x10000, 0x10000, CRC(b4febf98) SHA1(a28ffa20fe772eac5fd149821d5637af63965371), ROM_BIOS(0))
	// 1: MR BIOS (r) V3.21 2GB support
	ROM_SYSTEM_BIOS( 1, "v321", "V3.21 with 2GB support")
	ROMX_LOAD( "v053b407.rom", 0x10000, 0x10000, CRC(415d92b1) SHA1(e5a9f2a677002368d20f1281e2ac3469b19079f9), ROM_BIOS(1))
ROM_END

// Octek Hippo COM - Chipset: UMC UM82C865F, UM82C863F, UM82C491F - CPU: 486sx - BIOS: EPROM/AMI 486DX ISA BIOS - Keyboard-BIOS: MEGATRENDS MEGA-KB-H-WP / Intel
// BIOS-String: 40-0102-428003-00101111-080893-UMC491F-0 / U491/3 GREEN 486 MAIN BOARD INV1.1 94.2.21 - ISA16: 4 - On board: 1xIDE, Floppy, Game, 2xserial, 1xparallel
ROM_START( ochipcom )
	ROM_REGION32_LE( 0x20000, "bios", 0)
	ROM_LOAD( "hippo_com_bios.bin", 0x10000, 0x10000, CRC(d35f65a1) SHA1(885f55f87d2070c6a846768e5cf76499dad8d15c))
ROM_END

// J-Bond A433C-C/A450C-C RAM: 8xSIMM30, Cache: 8xCY7199-25PC/2xCY7C166-20PC - 2 8-bit ISA, 6 16-bit ISA)
// Chipset: ETEQ ET82C491 + ET82C493; CHIPS P82C206; AMI KB-BIOS-VER-F P8042AHP - OSC: 33.000MHz - CPU: i486DX-33
// BIOS: AMI 486 BIOS ZZ566787 - BIOS-String: 40-0200-001353-0010111-070791-ETEQ4/1C-0 / ETEQ 486 Mar. 05, 1992
ROM_START( a433cc )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "ami_j-bond_a433c-c.bin", 0x10000, 0x10000, CRC(66031e98) SHA1(d2d1a26837d3ca943a6ef09ec3e6fbfaaa62cc46))
ROM_END

// ASUS PVI-486AP4 (Socket 3, 4xSIMM72, Cache: 128/256/512KB, 4 PCI, 4 ISA, 1 VLB)
// Intel Aries PCIset S82425EX + S82426EX; DS12887 RTC; VIA VT82C42N - BIOS: 32pin
ROM_START( a486ap4 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 07/20/94-ARIES-P/I-AP4G-00 / #401A0-0104
	ROM_SYSTEM_BIOS(0, "486ap4v104", "ASUS PVI-486AP4 V1.04")
	ROMX_LOAD( "awai0104.bin", 0x00000, 0x20000, CRC(52ea7123) SHA1(3d242ea6d1bcdddd41e32e40708133c72f2bd060), ROM_BIOS(0))
	// 1: BIOS-String: 10/21/94-ARIES-P/I-AP4G-00 / #401A0-0203
	ROM_SYSTEM_BIOS(1, "486ap4v203", "ASUS PVI-486AP4 V2.03")
	ROMX_LOAD( "awai0203.bin", 0x00000, 0x20000, CRC(68d3a3f4) SHA1(6eee0c9aed2ede028eb170f8dd7921563293b99f), ROM_BIOS(1))
	// 2: BIOS-String: 11/08/94-ARIES-P/I-AP4G-00 / #401A0-0204
	ROM_SYSTEM_BIOS(2, "486ap4v204", "ASUS PVI-486AP4 V2.04")
	ROMX_LOAD( "awai0204.bin", 0x00000, 0x20000, CRC(b62b35bb) SHA1(b6fa3d7b1c88da37ce74aca329a31d2587652d97), ROM_BIOS(2))
	// 3: BIOS-String: 11/25/97/ARIES-P/I-AP4G-00 / #401A0-0205-2
	ROM_SYSTEM_BIOS(3, "486ap4v205-2", "ASUS PVI-486AP4 V2.05-2")
	ROMX_LOAD( "0205.002", 0x00000, 0x20000, CRC(632e8ee6) SHA1(3cf57b2654b0365e41ef5f5c82f68eeadf0e7a21), ROM_BIOS(3))
ROM_END

// ASUS PCI/I-486SP3G V3.02 (Socket 3, RAM: 4xSIMM72, Cache: 128/256/512K, 1 IDE, 1 SCSI, 3 PCI, 4 ISA) - BIOS: 32pin
// Intel Saturn II chipset: 82424ZX CDC + 82423TX DPU + 82378ZB SIO; NCR 53C820; National PC87332; DS12887 RTC; VIA VT82C42N
ROM_START( a486sp3g )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 10/21/94-SATURN-II-P/I-SP3G-00 / #401A0-302
	ROM_SYSTEM_BIOS(0, "v302", "ASUS PCI/I-486SP3G V3.02")
	ROMX_LOAD( "awsg0302.bin", 0x00000, 0x20000, CRC(21e918a0) SHA1(c7f937e3e90a43d7c7f867e686625b28a9c2484c), ROM_BIOS(0))
	// 1: BIOS-String: 08/15/95-SATURN-II-P/I-SP3G-00 / #401A0-304
	ROM_SYSTEM_BIOS(1, "v304", "ASUS PCI/I-486SP3G V3.04")
	ROMX_LOAD( "awsg0304.bin", 0x00000, 0x20000, CRC(f4d830d2) SHA1(086ccd14c7b0c521be1958d58b3539c4bfe4721f), ROM_BIOS(1))
	// 2: BIOS-String: 04/21/99-SATURN-II-P/I-SP3G-00 / #401A0-0306-1
	ROM_SYSTEM_BIOS(2, "v306", "ASUS PCI/I-486SP3G V3.06")
	ROMX_LOAD( "0306.001.bin", 0x00000, 0x20000, CRC(278e1025) SHA1(75835e59cf28bb6b9258f676766633cbffa56848), ROM_BIOS(2))
ROM_END

// ASUS VL/EISA-486SV1 (8 EISA, 1 VLB) -
ROM_START( a486sv1 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// BIOS-String: 05/20/94-SIS-486/EISA-E-486SV1-00 / #401A0-0112
	//ROM_SYSTEM_BIOS(0, "v112", "Award BIOS V1.12")
	ROM_LOAD( "e4sv0112.awd", 0x10000, 0x10000, CRC(d1d42fc9) SHA1(61549bf597517bb3c33e724e32b3cca981e65000))
ROM_END

// FIC 486-VIP-IO (3 ISA, 4 PCI)
// VIA GMC chipset: VT82C505 + VT82C486A + VT82C482 + VT82C483 + VT83C461 IDE; DS12885Q RTC; National PC87332VLJ-S I/O
ROM_START( ficvipio )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 12/07/94-VT82C505-2A4L4000-00 / Version 4.26GN2(ES2) (12/07/94)
	ROM_SYSTEM_BIOS(0, "426gn2", "FIC 486-VIP-IO 4.26GN2")
	ROMX_LOAD( "426gn2.awd",   0x00000, 0x20000, CRC(5f472aa9) SHA1(9160abefae32b450e973651c052657b4becc72ba), ROM_BIOS(0))
	// 1: BIOS-String: 02/08/96-VT82C505-2A4L4000-00 / Version 4.27GN2A (02/14/96)
	ROM_SYSTEM_BIOS(1, "427gn2a", "FIC 486-VIP-IO 4.27GN2A")
	ROMX_LOAD( "427gn2a.awd",  0x00000, 0x20000, CRC(035ad56d) SHA1(0086db3eff711fc710b30e7f422fc5b4ab8d47aa), ROM_BIOS(1))
	// 2: BIOS-String: 01/18/95-VT82C505-2A4L4000-00 / Version 4.26GN2A (01/18/95)
	ROM_SYSTEM_BIOS(2, "426gn2a", "FIC 486-VIP-IO 4.26GN2A")
	ROMX_LOAD( "486-vip-io.bin", 0x00000, 0x20000, CRC(907ed412) SHA1(5d2c584a230826935f56151a7c74419baf54796b), ROM_BIOS(2))
ROM_END

// Shuttle HOT-409 (6 16-bit ISA incl. 2 VLB, 2 8-bit ISA, 8 SIMM30, Cache: 64/128/256K+Tag in 2 banks)
// OPTi 82C495SX + 82C392SX + F82C206; MEGA-KB-1-WP
ROM_START( hot409 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 40-0200-001343-00101111-111192-OPT495SX-0 / Version 2.0
	ROM_SYSTEM_BIOS(0, "hot409v20", "Shuttle HOT-409 V2.0")
	ROMX_LOAD( "ami1992.bin", 0x10000, 0x10000, CRC(a19c3fd4) SHA1(404822c98344061b60883533395a89fe4902c177), ROM_BIOS(0))
	// 1: BIOS-String: 40-0204-001343-00101111-080893-OPT495SX-0 / OPTi495SX Version 3.0
	ROM_SYSTEM_BIOS(1, "hot409lba", "Shuttle HOT-409 V3.0 with LBA")
	ROMX_LOAD( "409lba.rom", 0x10000, 0x10000, CRC(78c5e47e) SHA1(7f14a88a5548fc67dd00e73fd09745e899b93a89), ROM_BIOS(1))
	// 2: BIOS-String: 40-0200-001343-00101111-111192-OPT495SX-0 / VERSION 1.1
	ROM_SYSTEM_BIOS(2, "hot409v11", "Shuttle HOT-409 V1.1")
	ROMX_LOAD( "amibios_hot409.bin", 0x10000, 0x10000, CRC(17729ee5) SHA1(ea3f5befe16ede7e9f4be3b367624745a6935ece), ROM_BIOS(2))
ROM_END

// Siemens-Nixdorf 486 mainboards and BIOS versions
// The same mainboards were used in various case versions to get the different model lines, so an identification by the mainboard number (Dxxx) is safest
ROM_START( pcd4x )
	ROM_REGION32_LE( 0x20000, "bios", 0 )
	// D756, was used in PCD-4Lsx, contains Cirrus Logic VGA ROM
	ROM_SYSTEM_BIOS(0, "d756v320r316", "D756 BIOS V3.20 R3.16")
	ROMX_LOAD( "fts_biosupdated756noflashbiosepromv320_320316_149.bin", 0x00000, 0x20000, CRC(2ab60725) SHA1(333b64424c08ecbbaf47110c99ad0335da211489), ROM_BIOS(0) )
	// D674, was used in PCD-4M, PCD-4Msx, PCD-4RSXA/4RA - this is a CPU card that is plugged into an ISA backplane; OSC: 14.31818
	// Chipset: LSI HT342-B-07, LSI HT321-D, Intel B6842-V31 AWARD(c)SNI UPI V4.3, IS9412BL PC87311AVF US4823312 - CPU: 486sx soldered onto the mainboard, but a socket for a 486DX is present - RAM: SIMM30x8
	// the CPU card can accept 16 bit "piggyback" modules, e.g. an ET4000 graphics card or a MFM harddisk controller to save ISA slots
	// on board: IDE, Floppy, beeper, keyboard connector, parallel, 2xserial, RTC DS12887, connectors for NMI and keylock, 4 DIP switches labelled DX2, Upgrade, 25, color
	// jumper X7: skip on/off, jumper x6: drv l/h
	ROM_SYSTEM_BIOS(1, "d674v320r316", "D674 BIOS V3.20 R3.16")
	ROMX_LOAD( "fts_biosupdated674noflashbiosepromv320_320316_144.bin", 0x00000, 0x20000, CRC(1293d27c) SHA1(22f36c4a5a0912011ed54ff917244f412208ffc0), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS(2, "d674v320r304", "D674 BIOS V3.20 R3.04")
	ROMX_LOAD( "d674_27c1024_3.20.bin", 0x00000, 0x20000, CRC(dfdad89e) SHA1(6cb78d8b5c8822dc84970ba912bc66a5e7cd2fb4), ROM_BIOS(2) )
	// D802, was used in PCD-4HVL
	ROM_SYSTEM_BIOS(3, "d802v320r316", "D802 BIOS V3.20 R3.34.802")
	ROMX_LOAD( "fts_biosupdated802noflashbiosepromv320_320334_152.bin", 0x00000, 0x20000, CRC(fb1cd3d2) SHA1(98043c6f0299e1c56e5f266ea5f117ae456447ff), ROM_BIOS(3) )
	// D620
	ROM_SYSTEM_BIOS(4, "d620", "D620")
	ROMX_LOAD( "w26361-d620-z4-01-5_award_v3.10_r2.02.bin", 0x00000, 0x20000, CRC(2708cc2a) SHA1(a399c938ffeba4cb28a22e54235f3f9c5e2892f6), ROM_BIOS(4) )
ROM_END


// ***** 486 motherboards using the ALi M1487 M1489 chipset

// Abit AB-PB4 REV.:1.2 - Chipset: ALi M1487 M1489, Winbond W83787F, W83768F - On board: Floppy, 2xser, 2xIDE, par
// ISA16: 3, PCI: 3, PISA: 1 - OSC: 14.3F5P - CPU: Socket 3 - BIOS: Award D2317569, 32pin
ROM_START( abpb4 ) // both BIOS versions end up in the Boot Block BIOS
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 10/30/95-ALI-1487/89-2A4KDA12C-5E / GREEN 486 PCI SYSTEM BIOS
	ROM_SYSTEM_BIOS( 0, "pb4", "PB4")
	ROMX_LOAD( "486-ab-pb4.bin", 0x00000, 0x20000, CRC(90884abc) SHA1(1ee11b026cb783b28cc4728ab896dbeac14eb954), ROM_BIOS(0))
	// 1: BIOS-String: 07/03/96-ALI-1487/89-2A4KDA1BC-F2 / GREEN PCI/ISA SYSTEM ROM
	ROM_SYSTEM_BIOS( 1, "pb4pf2", "PB4P-F2")
	ROMX_LOAD( "pb4p_f2.bin", 0x00000, 0x20000, CRC(9ab8d277) SHA1(10e424f5dd5c98877a5a7c9ae6205b2c442ac0e0), ROM_BIOS(1))
ROM_END

// EFA 486 APIO all BIOS versions boot into BootBlock BIOS
ROM_START( 486apio )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0:
	ROM_SYSTEM_BIOS(0, "a2", "A2")
	ROMX_LOAD( "apioa2.bin", 0x00000, 0x20000, CRC(0cf343be) SHA1(3a5757c802a30fb0d8d4fd623bee02af3b91fdd7), ROM_BIOS(0))
	// 1: 07/23/96-ALI-1487/89-2A4KDE3HC-00 / N486APIO Ver 2.00 SMC665GT
	ROM_SYSTEM_BIOS(1, "20sm", "2.0SM")
	ROMX_LOAD( "apio2smc.bin", 0x00000, 0x20000, CRC(1ced0692) SHA1(8afca17f0d793a3266b04ce8d70a359a29de3af7), ROM_BIOS(1))
	// 2: BIOS-String: 03/05/96-ALI-1487/89-2A4KDE3JC-00 / N486APIO, Rev 2.1
	ROM_SYSTEM_BIOS(2, "ag2", "AG2")
	ROMX_LOAD( "1019ag2.bin", 0x00000, 0x20000, CRC(4066124e) SHA1(7adbf528d8132122da4f950ee78931abd5d949e4), ROM_BIOS(2))
ROM_END

// MSI MS-4145 - Chipset: ALi M1487, M1489, W83787F, W83758F - CPU: Socket 3 - RAM: 3xSIMM72 - Cache: 8x28/32pin + TAG - ISA16: 4, PCI: 3
// BIOS: 32pin - on board: 2xIDE, Floppy, par, 2ser
ROM_START( ms4145 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0:
	ROM_SYSTEM_BIOS(0, "ag56", "AG56")
	ROMX_LOAD( "ag56.rom", 0x00000, 0x20000, CRC(217d7258) SHA1(bd0d484607fbcf54821822e20e4bf5fcaf456591), ROM_BIOS(0))
	// 1:
	ROM_SYSTEM_BIOS(1, "ag56p", "AG56P")
	ROMX_LOAD( "ag56p.rom", 0x00000, 0x20000, CRC(f2737425) SHA1(e6058a98a7ca4c03d1c1b7d30602fe97d88bc04a), ROM_BIOS(1))
	// 2:
	ROM_SYSTEM_BIOS(2, "ag56s", "AG56S")
	ROMX_LOAD( "ag56s.rom", 0x00000, 0x20000, CRC(c015ed49) SHA1(4c447bab1cba9d38b99c2e36e0824809e876931e), ROM_BIOS(2))
ROM_END

// TMC Research Corporation PCI48AF - Chipset: ALi M1487, M1489, TMSIA 9347W/TC4069UBP, FDC37C665GT - CPU: Socket 3 - RAM: SIMM72x4, Cache: 4x32pin + TAG
// ISA16: 4, PCI: 4 - On board: 2xISA, Floppy, 2xpar, 2xser - Keyboard-BIOS: AMIKEY-2
ROM_START( pci48af )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0:
	ROM_SYSTEM_BIOS(0, "af2", "AF2")
	ROMX_LOAD( "pci48af2.rom", 0x00000, 0x20000, CRC(556113cc) SHA1(cbbbcaa300253e766bce5292ffdfedf72c76e287), ROM_BIOS(0))
	// 1: 03/25/96-ALI-1487/89-2A4KDM29C-00 / 486DX-AC-WBc-25/33/40/50/66/80/100/120/133-A2-ZG - boots to BootBlock BIOS
	ROM_SYSTEM_BIOS(1, "afa", "AFA")
	ROMX_LOAD( "pci48afa.bin", 0x00000, 0x20000, CRC(9127efb5) SHA1(cf77fcca00b6e48067caefa518bedb287f945147), ROM_BIOS(1))
	// 2:
	ROM_SYSTEM_BIOS(2, "f2a", "F2A")
	ROMX_LOAD( "48af-f2a.rom", 0x00000, 0x20000, CRC(1e0d8216) SHA1(c6e4342dea2b7feac6e239dc99aee65508f9c297), ROM_BIOS(2))
ROM_END

ROM_START( alim1489 ) // no display
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: // V1.2A (with fake cache SRAM) - Chipset: ALi M1489, M1487, UM8663AF, UM8667 - BIOS: 10/10/94 AMI AD0153466 (32pin) - ISA16: 4, PCI: 3
	// On board: 2xser, Game, par, Floppy, 2xIDE - OSC: 14.31818
	ROM_SYSTEM_BIOS(0, "ali148901", "ALi M1489 #1")
	ROMX_LOAD( "ali.bin", 0x00000, 0x20000, CRC(d894223b) SHA1(088a94d2425f0abc85fafa922a5c6792da608d28), ROM_BIOS(0))
	// 1: Chipset: ALi M1489 A1, M1487 B1, GoldStar Prime 3b - CPU: Am486DX4-100 - RAM: 2xSIMM72, Cache: 9x32pin, used: 5xCY7C199-15PC
	// BIOS: AMI 32pin - ISA16: 4, PCI: 3 - On board: 2xIDE, Floppy, 2xser, par
	ROM_SYSTEM_BIOS(1, "ali148902", "ALi M1489 #2")
	ROMX_LOAD( "ali_pci486v1-hj3.bin", 0x00000, 0x20000, CRC(fca45439) SHA1(a4bad38301c9e7f780a95a07b7062f0a277a7a10), ROM_BIOS(1))
ROM_END


// ***** 486 motherboards using the CONTAQ 82C596 chipset

// MSI MS-4125 - Chipset: CONTAQ 82C596 SiS 85C206 - ISA8: 1, ISA16: 3, ISA16/VL: 2 - BIOS: AMI 486DX ISA BIOS AA65441044 (28pin) - Keyboard-BIOS: AMI/Intel P8942AHP
// BIOS-String: 40-0104-001169-00101111-111192-CTQ596-0 / AC5E 052193
ROM_START( ms4125 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD("ms4125.bin", 0x10000, 0x10000, CRC(0e56b292) SHA1(9db26e8167b477c550d756d1ca2363283ebff3ed))
ROM_END

// Diamond Flower, Inc. (DFI) 486-CCV Rev B - Chipset: CONTAQ 82C596, KS83C206EQ - BIOS: 11/11/92 AMI AB8644083 (28pin) - Keyboard-BIOS: AMIKEY-2
// BIOS-String: 40-0100-ZZ1211-00101111-111192-CONTAQ/5-0 - OSC: 14.31818MHz - ISA8: 2, ISA16: 4, ISA16/VL: 2
ROM_START( 486ccv )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "contaq.bin", 0x10000, 0x10000, CRC(2ac46033) SHA1(a121c22ded4932e3ba8d65c2b097b898f02147c7))
ROM_END


// ***** 486 motherboards using the OPTi495SLC chipset

// QDI PX486P3 - Chipset: OPTi 82C495SLC, F82C206 - CPU: 486 - BIOS: 11/11/92 AMI (28pin)
// Keyboard-BIOS: AMIKEY - ISA8: 1, ISA16: 3, ISA16/VL: 3 (one marked MASTER/SLAVE, two marked SLAVE)
ROM_START( px486p3 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 40-0402-428003-00101111-111192-OP495SLC-0 / PX486DX33/50P3 IVN 2.0 19/11/1993
	ROM_SYSTEM_BIOS(0, "ivn20", "IVN 2.0")
	ROM_LOAD( "px486p3.bin", 0x10000, 0x10000, CRC(4d717aad) SHA1(2d84cf197845d58781f77e4d539ca994fd8733c8))
	// 1: BIOS-String: 40-0401-428003-00101111-111192-OP495SLC-0 / PX486DX33/50P3 IVN 1.0 25/06/1993
	ROM_SYSTEM_BIOS(1, "ivn10", "IVN 1.0")
	ROMX_LOAD( "qdi_px486.u23", 0x10000, 0x10000, CRC(c80ecfb6) SHA1(34cc9ef68ff719cd0771297bf184efa83a805f3e), ROM_BIOS(1))
ROM_END

// NAT48PV-1.0 VL - Chipset:82C495SLC, Chips F82C206J - RAM: 8xSIMM30, Cache: 9xIS61C256A - OSC: 14.31818 - BIOS: AMI 486DX ISA BIOS (28pin) AA5312581
// Keyboard-BIOS: MEGA-KB-F-WP P8042AHP - ISA8: 2, ISA16: 3, ISA16/VL: 2
// 40-040A-001291-00101111-111192-OP495SLC-0 / 486DX-OP-WBq-25/33/50-K2-ZZ
ROM_START( nat48pv )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "amibios_aa5312581.bin", 0x10000, 0x10000, CRC(8a788c79) SHA1(050972b7a369a463d6654ec52c0804002e9bcb37))
ROM_END


// ***** 486 motherboards using the OPTi OPTi 82C392, 82C493, 82C206 chipset

// Auva-Cam-33-P2 = See-Thru Sto486Wb - CPU: 486 - ISA8: 1, ISA16: 7 - Chipset: OPTi 82C392, 82C493, 82C206
// MR BIOS (tm) V1.30
ROM_START( sto486wb )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "opti_82c493_486_mr_bios_v130.rom", 0x10000, 0x10000, CRC(350d5495) SHA1(4f771ef5fe627e0556fb28f8972e545a0823a74d))
ROM_END

// Silicon Valley Computer, Inc. 486WB6A3.B1 - Chipset: OPTi 82C493/392, F82C206 - BIOS: AMI 486 BIOS ZZ342708 - Keyboard BIOS:AMI KB-BIOS-VER-F
// CPU: Intel 80486DX-33, secondary socket - RAM: 8xSIMM30, Cache: 9xMosel MS6264A-20NC - OSC: 33.333MHz, 14.31818 - ISA8: 1, ISA16: 6, ISA16/RAM extension: 1
ROM_START( 486wb6a3 ) // BIOS String: 40-0101-ZZ9999-00101111-060691-OPWBSX-F
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "486_ami.bin", 0x10000, 0x10000, CRC(1f5e9263) SHA1(534a6ace19ba6185614e04e3bd2d0aabe1193e2c))
ROM_END

ROM_START( op82c392 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: 486-A4865-A4866-XX V2 1 COMP - CPU: 486DX-33 - Chipset: Opti 82C392, 82C493, Opti F82C206 - BIOS: 486DX AMI (28pin) - Keyboard-BIOS: AMI
	// BIOS-String: - ECB: 1, ISA8: 2, ISA16: 5 - OSC: 14.318, 66.000000MHz - RAM: 8xSIMM30, Cache: 16 sockets +1 provided
	ROM_SYSTEM_BIOS(0, "a4865", "A4865")
	ROMX_LOAD( "a4865-a4866.bin", 0x10000, 0x10000, CRC(9c726164) SHA1(b6ad8565a489b9d5991eea37905be2e6fc59fa48), ROM_BIOS(0))
	// 1: Chipset: OPTi 82C392, 82C493, UMC UM82C206L - CPU: i486DX-33, FPU socket provided - OSC: 34.000MHz, 14.31818 - Keyboard-BIOS: AMI/Intel P8942AHP
	// BIOS: AMI 486 BIOS Z600436 - BIOS-String: 40-0131-425004-01001111-070791-OPWB493-0 / ABC COMPUTER CO., LTD. - 40-0101-DK1343-00101111-00101111-060691-OPWBSX-0 - ISA8: 2, ISA16: 6
	ROM_SYSTEM_BIOS( 1, "82c493", "82C493")
	ROMX_LOAD( "486-920087335.bin", 0x10000, 0x10000, CRC(38571ffe) SHA1(aa6048213139c88901aca9cd38251a3937b6e52d), ROM_BIOS(1))
	// 2: Chipset: OPTi 82C392, 82C493, Chips 206 - CPU: two sockets provided - RAM: 8xSIPP30, on board RAM/Cache? photo too blurry - Keyboard BIOS: Intel/AMI
	// OSC: 66.666MHz, 14.31818 - ISA8: 1 (solder pads for memory slot provided), ISA16: 7 - BIOS-String: 40-0101-009999-00101111-060691-OPWBSX-0 / OPTi-WB GW486SX/DX BIOS, July 3, 1992
	ROM_SYSTEM_BIOS( 2, "060691", "06/06/91")
	ROMX_LOAD( "gw486sxdx.bin", 0x10000, 0x10000, CRC(99ecd9ce) SHA1(616bc1192c0ffb9d90f8aa32d93a8badc45f9d56), ROM_BIOS(2))
ROM_END


// motherboards using the OPTi 82C802A, 82C602A chipset

// Edom MV035F - Chipset: OPTi 82C802A, 82C602A - CPU: TI 486DX2-80 - RAM: 4xSIMM30, 3xSIMM72, Cache: 4x32pin, 4x28pin, TAG
// BIOS: 28pin - Keyboard-BIOS: VIA - ISA16: 5, ISA16/VL: 3
// From the source: "The seller claimed that it POSTed, but all the BIOS options were grayed out. I pulled and dumped the BIOS
// chip, ran it through MODBIN and found out the OEM of the board simply set all BIOS setup options to 'SHOW-ONLY'. I set all
// except PCI CONFIGURATION to 'Enabled' (since this board doesn't have any PCI slots), blanked the EPROM and burned the modified
// BIOS to it, and now I can set all CMOS setup options normally."
// BIOS-String reflects the edit: 11/17/95-OPTi-802G-2C4UKW01-00 / Computer Spirit v1.0 (Hack 1.0 by Eep386
ROM_START( edmv035f )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "mf035fed.bin", 0x10000, 0x10000, BAD_DUMP CRC(5c1ce352) SHA1(c7944b06e4a3473bb720caa5043f4b55bccf3835))
ROM_END

// Octek Hippo DCA2 - Chipset: OPTi 802G - BIOS: 28pin - CPU: Socket 3 - ISA8: 2, ISA16: 3, ISA16/VL: 3 - RAM: 4xSIMM72, Octek claimed, Cache would be taken out of main RAM
ROM_START( ochipdca2 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 10/27/94-OPTI-802G-2C4UKO01-00 / (2C4UKO01) EVALUATION ROM - NOT FOR SALE
	ROM_SYSTEM_BIOS(0, "hv2433", "AWARD HV2433")
	ROMX_LOAD( "hv2433.awa", 0x10000, 0x10000, CRC(d6179601) SHA1(8a9c7ec959f6626268e0e242760439272fc9e28c), ROM_BIOS(0))
	// 1: beep code
	ROM_SYSTEM_BIOS(1, "h2433", "AMI H2433")
	ROMX_LOAD( "h2433.ami", 0x10000, 0x10000, CRC(a646a191) SHA1(086ae94554e3c2b292f2e32b5cb080c15dfa3e0b), ROM_BIOS(1))
	// 2: beep code L-H-H-L
	ROM_SYSTEM_BIOS(2, "mr321", "MR-BIOS 3.21") // supports AMD X5-133
	ROMX_LOAD( "095061.bin", 0x10000, 0x10000, CRC(0a58cab2) SHA1(e64d6ca0bad6eeed492260853d7d60cd2a60a222), ROM_BIOS(2))
	// 3: beep code L-H-H-L
	ROM_SYSTEM_BIOS(3, "mr31", "MR-BIOS 3.1")
	ROMX_LOAD( "dca2mr31.rom", 0x10000, 0x10000, CRC(43b7415f) SHA1(45df892d146b8e2594274773c93d1623207b40fc), ROM_BIOS(3))
ROM_END


// Motherboards using the Opti 82C895 82C602A chipset

// ExpertChip EXP4044 - CPU: Socket3 - Chipset: OPTi 82C895, 82C602 - RAM: 4xSIMM30, 2xSIMM72, Cache: 4x28pin, 4x32pin + TAG
// Keyboard-BIOS: MEGAKEY or Winbond W83C42 - ISA16: 3, ISA16/VL: 3
// BIOS-String: 06/23/94-OPTI-802G-2C4UKD01-00 / EXP4044 GREEN BIOS Ver 1.1
ROM_START( exp4044 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD("4ecw001.bin", 0x10000, 0x10000, CRC(cf186fa4) SHA1(d65cc2f2c6feaa1a537319aaef86df12b44afdec))
ROM_END

// Jetway J-403TG
ROM_START( jwj403tg )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: JETWAY J-403TG (VER.D2E) Chipset: OPTi 82C895, 85C602 - CPU: Socket 3 - Keyboard BIOS: AMIKEY-2 -
	// RAM: 4xSIMM32, 3xSIMM72, Cache: 8xW24257AK-15, 1xW24129AK - ISA16: 5, ISA16/VLB: 3 (2xMASTER, 1xSLAVE)
	ROM_SYSTEM_BIOS(0, "verd2e", "VER.D2E") // BIOS-String: 40-0101-428060-00101111-111192-UMC491C-0
	ROMX_LOAD( "ver_d2e.bin", 0x10000, 0x10000, CRC(5d3b86bb) SHA1(d7c3cfbb5b858efacc7cee872a4ef5c9666f9d06), ROM_BIOS(0))
	// 1: JETWAY J-403TG GREEN VLB ver 2.0 - Chipset: OPTi 82C895, second IC with Energy Star logo - BIOS: Award 486DX J156079 - Keyboard BIOS: VIA VT82C42N
	// CPU: Socket 3 - RAM: 4xSIMM30, 3xSIMM72, Cache: 8xW24257AK-15 - ISA8: 2, ISA16: 3, ISA16/VLB: 3
	ROM_SYSTEM_BIOS(1, "greenver20", "GREEN VLB ver 2.0") // BIOS-String: 07/21/94-OPTI-802G-2C4UKJ11-00 / V1.A
	ROMX_LOAD( "403tg.bin", 0x10000, 0x10000, CRC(41ebe3e8) SHA1(567d61c5912cfb5fbfc9a1b674e7edad09a2165c), ROM_BIOS(1))
	// 2: JETWAY J-403TG VLB Rev D - Chipset: OPTi 82C895, 82C602 - BIOS: AMI 486DX ISA BIOS AB5257763 - Keyboard BIOS: JETkey V5.0
	// CPU: 80486DX - RAM: 4xSIMM30, 3xSIMM72, Cache: 8x32pin DIP (used: 8xH61256-20) - ISA8: 2, ISA16: 3, ISA16/VLB: 3
	ROM_SYSTEM_BIOS(2, "revd", "Rev D") // BIOS-String: 40-P301-001276-00101110-121593-OPTi895-H - blank screen
	ROMX_LOAD( "b2790126", 0x10000, 0x10000, CRC(b2790126) SHA1(dad277c91dac9daffcd1e3f3e9a1a1e59c92e72e), ROM_BIOS(2))
	// 3: MR BIOS for the 82C895 chipset - MR BIOS (r) V2.02
	ROM_SYSTEM_BIOS(3, "82c895", "82C895")
	ROMX_LOAD("opt895mr.mr", 0x10000, 0x10000, CRC(516cb091) SHA1(4c5b51cd05974001da4b764b4b14987657770a45), ROM_BIOS(3))
ROM_END

// QDI V4P895P3/SMT V5.0 - Chipset: Opti 82C895 82C602A - CPU: Am486DX2-66 - ISA8: 1, ISA16: 3, ISA16/VL: 3
// RAM: 4xSIMM30, 2xSIMM72, Cache: 8xUM61256FK-15 - BIOS: AMI 486DX ISA BIOS Ac0928698 (28pin in a 32pin socket) - Keyboard-BIOS: AMIKEY-2
ROM_START( v4p895p3 ) // no display
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "486-v4p895p3-smt.bin", 0x10000, 0x10000, CRC(683f8470) SHA1(eca1c21a8f8c57389d9fdf1cd76d2dec0928524a))
ROM_END

// Shuttle HOT-419 - Chipset: OPTi 92C895A, 82C602A - CPU: Socket 3 - RAM: 4xSIMM72, Cache: 8+1 UM61256K-15 - ISA8: 2, ISA16:3, ISA16/VL: 3
// BIOS: AMI AB0585433 (28pin) - Keyboard-BIOS: AMIKEY-2
ROM_START( hot419 ) // no display
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0:
	ROM_SYSTEM_BIOS(0, "072594", "07/25/94")
	ROMX_LOAD( "hot419_original_bios.bin", 0x10000, 0x10000, CRC(ff882008) SHA1(1a98d61fd49a2a07ff4f12ccba55cba11e4fde23), ROM_BIOS(0))
	// 1:
	ROM_SYSTEM_BIOS(1, "419aip06", "419AIP06")
	ROMX_LOAD( "419aip.rom", 0x10000, 0x10000, CRC(389ca65d) SHA1(457491c60aa45499e2cd8dad9db3bf3312977a4f), ROM_BIOS(1))
ROM_END

// TMC PAT48PG4-V1.20 - BIOS Version: Award 10/13/95 - Chipset: OPTi 82C895+82C602 - EPROM Label: 486 AWARD SOFTWARE 1984-1995 T1103040
// Keyboard BIOS: AMIKEY-2 - CPU: socket for 80486 - RAM: 4xSIMM30, 2xSIMM72, Cache: 9xISSI IS61M256-15N - ISA16: 4, ISA16, VLB: 3
ROM_START( tmpat48pg4 ) // BIOS-String: 10/13/95-OPTI-802G-2C4UKM21-00.00-00 / 486DX-OP-WOe-25/33/40/50/66/80/100/120-C7-ZG
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "award.bin", 0x10000, 0x10000, CRC(c217214b) SHA1(583820b2fe96ca4bfacf9267800afe7cc76e5ffa))
ROM_END


// Motherboards using the SiS 85C461 chipset

// Abit AB-AV4 (aka VL-BUS 486) - Chipset: SiS 85C461, HM5818A - BIOS Version: AMI 11/11/92 486DX ISA BIOS AA7247480
// Keyboard BIOS: AMI-KB-H-WP - CPU: socket for 80486PGA, solder pads for QFP486sx - RAM: 8xSIMM30, Cache: 7xW24257AK-20, 1xEm81256B-20P, 1x71256S20TP
// ISA16: 5, ISA16/VLB: 3 - OSC: 14.31818
ROM_START( abav4 ) // BIOS String: 40-01BB-001247-00101111-111192-SIS461 / CACHE 486 SYSTEM BIOS
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "amibios.bin", 0x10000, 0x10000, CRC(7bf8142e) SHA1(17e09eebabc0d2c393d27db38b571af3c0ccbb41))
ROM_END

// ASUS ISA-486SV2 - Chipset: SiS 85C461 - BIOS: AMI 486DX ISA BIOS AA7892378 28pin - Keyboard-BIOS: Intel/AMI
// BIOS-String: 40-110A-001292-00101111-111192-I486SI-0 - ISA16: 5, ISA16/VL: 2 - CPU: 486DX in a blue socket (overdrive ready)
ROM_START( a486sv2 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "486-asus isa-486sv2.bin", 0x10000, 0x10000, CRC(de925130) SHA1(2e3db7a1d4645082290d6303a16446af2959f34a))
ROM_END

// GENOA TurboExpress 486 VL ASY 01-00302 - Chipset: SiS 85C407 85C461 - CPU: Socket3 - OSC: 14.31818MHz - ISA16: 4, ISA16/VL: 3 - BIOS: AMI 486DX ISA BIOS AB0562153 (28pin)
// BIOS-String: 40-0100-006156-00101111-080893-SIS461-0 / GENOA TurboExpress 486VL - 3 (Ver. C) - Keyboard-BIOS: AMIKEY
ROM_START( gete486vl )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "486-genoa_vlb.bin", 0x10000, 0x10000, CRC(9be0f329) SHA1(3b1adedd6aad40c623757e4976e0dcadb253f255))
ROM_END

// Lucky Star UCM-486V30 (aka SIS486 3-VLBUS) - BIOS/Version: AMI 01/14/1993 SUPPORT VESA, 486DX ISA BIOS AA8580239 - Keyboard BIOS: AMI MEGA-KB-H-WP
// Chipset: SIS 85C461, HM6818A - CPU: P24T socket - OSC: 14.31818 - RAM: 8xSIMM30, Cache: 9x28pin DIP
ROM_START( lsucm486v30 ) // BIOS string: 40-0100-001256-00101111-111192-SIS3486-0
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "ucm-486v30.bin", 0x10000, 0x10000, CRC(90ec73a2) SHA1(2fc17f7eed09c2f7d0139670677cc84bbc2964de))
ROM_END

// SOYO 486 VESA 025D2 - Chipset: SiS 85C461, 85C407 - BIOS: AMI - Keyboard BIOS: AMI (c)1988 - CPU: i486sx-33, full 486 socket provided
// RAM: 8xSIMM30, Cache: 8x28pin - ISA8: 1, ISA16: 4, ISA16/VL: 2 - BIOS-String: 40-0100-001102-00101111-080893-SIS461 / REV MG
ROM_START( so025d2 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "soyo-486-vesa.bin", 0x10000, 0x10000, CRC(da798d7b) SHA1(15a8c1e244ee29ef5c61e05659f8ec7f8eaa8ab7))
ROM_END

// ***** 486 motherboards using the SiS BTQ 85C401/85C402 + 85C206 chipset

// ABIT AB-AX4 - Chipset: SIS BTQ 85C401, 85C402, 85C206 - BIOS: AMIBIOS 06/06/92 - Keyboard BIOS: AMI - CPU: socket for 80486
// RAM: 8xSIMM30, Cache: 8xEtronTech Em51256A-20P, 1X AS7C256-20PC - ISA8: 1, ISA16: 6 - OSC: 33.333MHz, 14.31818
ROM_START( abax4 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: X0-01AA-001247-00101111-060692-SISAUTO-0 / AX4 ISA SYSTEM BIOS
	ROM_SYSTEM_BIOS(0, "060692", "ABIT AB-AX4 06/06/92")
	ROMX_LOAD( "486dx_dump.bin", 0x10000, 0x10000, CRC(c7af4380) SHA1(59507a0f7d929ac19c5b56334f54643127c0d2be), ROM_BIOS(0))
	// 1: BIOS-String: 30-0200-D01247-00101111-070791-SIS486 / AT486DX 33MHz BIOS
	ROM_SYSTEM_BIOS(1, "070791", "ABIT AB-AX4 07/07/91")
	ROMX_LOAD( "486-aa1177369.bin", 0x10000, 0x10000, CRC(530535de) SHA1(7f6e627a77ebcaec97f08e6c797d31e9321e26fc), ROM_BIOS(1))
ROM_END

// ASUS ISA-486 - Rev. 1.4 - Chipset: SiS BTQ 85C401/85C402 + 85C206 - BIOS/Version: AMI 486DX ISA BIOS 05/05/91 AA1258865 - CPU: Intel 80486DX-33, FPU socket for 4167 provided
// RAM: 8xSIMM30, Cache: 8x28pin DIP (4x71256 fitted) - OSC: 14.31818, 33.000MHz - DIP6: 111000 - ISA8/RAM extension: 1, ISA16: 7 -
ROM_START( a486isa ) // BIOS String: 40-0102-001292-00101111-050591-SIS-486-0
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "isa-486.bin", 0x10000, 0x10000, CRC(57375912) SHA1(8035b5d1cfe824a20a94571a57b86fdb4018f073))
ROM_END

// Mitac MBA-029 - Chipset: SIS BTQ 85C401, 85C402, 85C206 - BIOS: AMI - CPU: 486 socket - OSC: 14.34818, xxxxx (unreadable)
// RAM: 4xSIMM30, Cache: 8x28pin - ISA 8: 1 (not soldered in), ISA16: 6
ROM_START( mba029 ) // BIOS-String: 30-0200-ZZ1594-00101111-070791-SISAUTO-0
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "mba-029_bios_27c512.bin", 0x10000, 0x10000, CRC(8d660697) SHA1(6b2be9ec9a2d12c9348c26ac25514af406fa752e))
ROM_END

// ***** 486 motherboards using the SiS 85C496/85C497 chipset
// cfr. pc/pcipc_sis.cpp


// ***** 486 motherboards using the SiS 85C471 + 85C407 chipset

// 486IG-B-2-1 - CPU: 486 - RAM: 8xSIMM30, Cache: 4x32pin, 4x28pin, TAG - Chipset: SIS85C471/407 - BIOS: AMI WIN BIOS 07/25/94
// Keyboard-BIOS: AMIKEY - BIOS-String: 40-0002-428020-00101111-080893-SIS471B-F - ISA8: 1, ISA16: 3, ISA16/VL: 3
ROM_START( 486igb21 ) // display remains blank
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "486igb21.amw", 0x10000, 0x10000, CRC(b62cc7af) SHA1(048f80ad995a1516f97bb7544d3fb608a93893b1))
ROM_END

// Abit AH4/AH4T/AN4R2 ( the T model has a voltage regulator for DX4 CPUs) - CPU: Socket 3 - Chipset: SIS 85C471 / SIS 85C407
// RAM: 4xSIMM72, Cache: 9x28pin (32pin sockets except TAG) - BIOS: AMI - Keyboard-BIOS: AMIKEY - OSC: 14.31818 - ISA8: 1, ISA16: 4, ISA16/VL: 3
// BIOS-String: 08/30/95-SIS-85C471-2C4I9A12-02
ROM_START( abah4 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "ah4t_an4r2_02.bin", 0x10000, 0x10000, CRC(b45dc3b7) SHA1(94206ac9ed50fc37d954cc3cd1fb062fd75ea984))
ROM_END

// Aopen VI15G - Chipset: SiS 85C471, 85C407 - CPU: Socket 3 - RAM: 4xSocket72, Cache: 4x32pin, 4x28pin, TAG - BIOS: 28pin
// Keyboard-BIOS: AMIKEY-2 - ISA16: 4, ISA16/VL: 3
ROM_START( aovi15g )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0:
	ROM_SYSTEM_BIOS(0, "072594", "07/25/94")
	ROMX_LOAD( "amiwinch.bin", 0x10000, 0x10000, CRC(9ee72bef) SHA1(25f08714a384d68777d1570cf28a4db8b390257e), ROM_BIOS(0))
	// 1:
	ROM_SYSTEM_BIOS(1, "r23", "R23")
	ROMX_LOAD( "vi15gr23.rom", 0x10000, 0x10000, CRC(b704f571) SHA1(485795b8756069723dac865919dc3915a3162c12), ROM_BIOS(1))
ROM_END

// ASUS VL/I-486SV2G (GX4) (4xSIMM72, Cache: 128/256/512/1024KB, 7 ISA, 2 VLB)
// SiS 85C471 + 85C407; AMIKEY-2
ROM_START( a486sv2g )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 11/17/94-SIS-85C471-I486SV2G-00 / #401A0-0304
	ROM_SYSTEM_BIOS(0, "v304", "ASUS VL/I-486SV2G (GX4) V3.04")
	ROMX_LOAD( "sv2g0304.bin", 0x10000, 0x10000, CRC(cceabe6f) SHA1(45d0e25603045255d1ccaf5cbddd1a9146f61529), ROM_BIOS(0))
	// 1: BIOS-String: 01/11/95-SIS-85C471-I486SV2G-00 / #401A0-0305-1
	ROM_SYSTEM_BIOS(1, "v305", "ASUS VL/I-486SV2G (GX4) V3.05")
	ROMX_LOAD( "0305.001", 0x10000, 0x10000, CRC(9f2f9b75) SHA1(789807d82e39d69f948f7897f99b2fe362330dd1), ROM_BIOS(1))
	// 2: BIOS-String: 03/28/95-SIS-85C471-I486SV2G-00 / #401A0-0306 - complains about BIOS ROM checksum error
	ROM_SYSTEM_BIOS(2, "v306", "ASUS VL/I-486SV2G (GX4) V3.06")
	ROMX_LOAD( "asus_0306.bio", 0x10000, 0x10000, BAD_DUMP CRC(c87b7b55) SHA1(651938bcfdf6813a1e66c0e1b4812efe91740c91), ROM_BIOS(2))
	// 3: BIOS-String: 08/22/95-SIS-85C471-I486SV2G-00 / #401A0-0401
	ROM_SYSTEM_BIOS(3, "v401", "ASUS VL/I-486SV2G (GX4) V4.01")
	ROMX_LOAD( "sv2g0401.bin", 0x10000, 0x10000, CRC(f544f65a) SHA1(9a5e39cfbd545a0026f959b42dbc742246205b3c), ROM_BIOS(3))
	// 4: BIOS-String: 11/03/95-SIS-85C471-I486SV2G-00 / #401A0-0402-1
	ROM_SYSTEM_BIOS(4, "v402", "ASUS VL/I-486SV2G (GX4) V4.02")
	ROMX_LOAD( "sv2g0402.bin", 0x10000, 0x10000, CRC(db8fe666) SHA1(e499da86261bc6b312a6bc3d94b9465e17c5a449), ROM_BIOS(4))
	// 5: BIOS-String: 11/19/97-SIS-85C471-I486SV2GC-00 / #401A0-0402-1
	ROM_SYSTEM_BIOS(5, "v402b", "ASUS VL/I-486SV2G (GX4) V4.02 beta")
	ROMX_LOAD( "0402.001.bin", 0x10000, 0x10000, CRC(4705a480) SHA1(334c3d57cb6cb157798cd189207288c731a4dd7b), ROM_BIOS(5))
ROM_END

// Chaintech 486SLE M106 4SLE-Z1 - Chipset: SiS 85C407 85C471 - CPU: i486DX2-66 - BIOS: Award v4.50G - Keyboard-BIOS: Lance LT48C41
// BIOS-String: 11/09/94-SIS-85C471E-2C4I9C31-00 / 11/24/94 - ISA8: 1, ISA16: 3, ISA16/VL: 3 - OSC: 14.31818
ROM_START( ch4slez1 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "486-chaintech_486_sle.bin", 0x10000, 0x10000, CRC(8292bdb7) SHA1(461d582ea9fee4113d3a8ac050f76c7057ead7c7))
ROM_END

// Gemlight GMB-486SG rev 2.2 - Chipset: SiS 85C471 85C407 - BIOS/Version: Award - Keyboard BIOS: JETkey V5.0G
// CPU: 80486DX2-66 - RAM: 4xSIMM72, Cache: 5x 28pin DIP (TI256 SA 20TP fittet), 4x32pin DIP (W24257AK-15 fitted) - ISA8: 1, ISA16: 2, ISA16/VLB: 3
ROM_START( gmb486sg ) // BIOS-String: 01/10/95-SIS-85C471B/E/G-2C4I9G30-00
	ROM_REGION32_LE(0x20000, "bios", 0) // screen remains blank
	ROM_LOAD( "gmb486sg.bin", 0x10000, 0x10000, CRC(1f199b35) SHA1(0c4b19762426a30f7121c5c17f1b25a54a5df1f0))
ROM_END

// Gigabyte GA-486VF REV.6 - Chipset: SiS 85C407 85C471 - CPU: Cyrix Cx486 DX 40 - BIOS: Award L4162439, 28pin - Keyboard-BIOS: Lance LT38C41
// BIOS-String: 04/27/94-SIS-85C471-2C4I8G01-00 - ISA8: 1, ISA16: 3, ISA16/VL: 3 - OSC: 14.318MHz
ROM_START( ga486vf )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "ga-486svf.bin", 0x10000, 0x10000, CRC(e9fb3153) SHA1(b8e307658f95c3e910728ac9316ad83e7afdb551))
ROM_END

// Gigabyte GA-486VS - CPU: 486 - Chipset: SiS 85C471, 85C407 - Keyboard-BIOS: Lance LT38C41 - ISA16: 3, ISA16/VL: 3
// BIOS-String: 11/21/94-SIS-85C471B/E/G/2C4I9G01-00 / Nov 21, 1994 Rev.A
ROM_START( ga486vs )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "vs1121.rom", 0x10000, 0x10000, CRC(0afadecf) SHA1(66c0655b5c4905438603097998a98407bfa376e6))
ROM_END

// MSI MS-4132 G VER:1 - Chipset: SiS 85C471, 85C407, BIOS: AMI 486DX ISA BIOS 08/08/93 AB4827039 - CPU: SOCKET 3, solder pads for 8486
// RAM: 4xSIMM30, 2xSIMM72, Cache: 5xW24257AK-15, 4xIS61C256AH-15N - ISA16: 4, ISA16/VLB: 3 (2 master, 1 slave) - OSC: 14.31818MHz
ROM_START( ms4132 ) // BIOS String: 40-0100-001169-00101111-080893-SIS471B / A75A 033194
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "ms4132g.bin", 0x10000, 0x10000, CRC(23385e9d) SHA1(3637febf6e037aec9328d99877550ee9dee4c78c))
ROM_END

// MSI MS:4138 VER:1.3 - Chipset: SiS 85C471, 85C407 - CPU: Socket 3 - BIOS: EPROM/AMI 486DX ISA BIOS AC0250679
// Keyboard-BIOS: Winbond W83C42 - BIOS-String: - ISA16: 4, ISA16/VL: 3
ROM_START( ms4138 )
	ROM_REGION32_LE( 0x20000, "bios", 0)
	// 0: no display
	ROM_SYSTEM_BIOS( 0, "a75n", "A75N")
	ROMX_LOAD( "a75n.rom", 0x10000, 0x10000, CRC(f9b2130c) SHA1(7798b68275e547e858ba162abc5cf94dd6a85f4c), ROM_BIOS(0))
	// 1: no display
	ROM_SYSTEM_BIOS( 1, "msi4138", "MSI MS-4138")
	ROMX_LOAD( "ms-4138.bin", 0x10000, 0x10000, CRC(5461c523) SHA1(adb9fe0afa860897d575403a810ff44c85b9f93c), ROM_BIOS(1))
	// 2: BIOS-String: 08/14/95-SIS-85C471B/E/G-2C3I9W40-00 / W753BETA 26JAN96
	ROM_SYSTEM_BIOS( 2, "w753beta", "W753BETA")
	ROMX_LOAD( "w753beta.bin", 0x10000, 0x10000, CRC(4aeeba0b) SHA1(9d088c940599110ce3acea84bb881a61d42b6dcf), ROM_BIOS(2))
ROM_END

// DTK PKM-0038S E-2A  aka Gemlight GMB-486SG - Chipset: SIS 85C471, 85C407 - BIOS/Version: 01/10/95 Award (DTK PKM0038S.P02.03.02), 28pin - Keyboard-BIOS: JETkey V5.0
// CPU: Socket 3 - ISA8: 1, ISA16: 3, ISA16/VL: 3 - OSC: 14.318
ROM_START( pkm0038s )
	ROM_REGION32_LE( 0x20000, "bios", 0)
	// 0: BIOS-String: 01/10/95-SIS-85C471B/E/G-2C4I9G30-00 / (2C4I9G30) DTKPKM0038S.P2.03.02
	ROM_SYSTEM_BIOS(0, "p20302", "P2.03.02")
	ROMX_LOAD( "pkm0038s.bin", 0x10000, 0x10000, BAD_DUMP CRC(f6e7dd88) SHA1(5a2986ff0e6352ade8d5b0abaa86e436dddcf226), ROM_BIOS(0)) // BIOS ROM checksum error
	// 1: BIOS-String: 07/11/94-SIS-85C471E-2C4I9G30-00 / (2C4I9G30) DTKPKM0038S.P02.02.0
	ROM_SYSTEM_BIOS(1, "p2020", "P2.02.0")
	ROMX_LOAD( "4siw005.bin", 0x10000, 0x10000, CRC(0b8794d3) SHA1(153b38f6815a8a3d6723eb17df7ffc37054b3194), ROM_BIOS(1))
ROM_END

// EFA 4DMS HL3G-L4-VI - Chipset: SIS-85C471B/E/G / SIS471E/G - CPU: Socket 3 - RAM: 2xSIMM72, Cache: 8x32pin+TAG - ISA16: 5, ISA16/VL: 3
// BIOS: Award - BIOS-String: 12/07/95-SIS-85C471B/E/G-2C4I9E30-00 / 4DMS-HL3GC Award BIOS REV 1.05 01/12/'96
ROM_START( 4dmshl3g )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "3g105.bin", 0x00000, 0x20000, CRC(60e4841f) SHA1(60ad11e4e4a60eef858d837470a9014706e7576a))
ROM_END

// Soyo 025K2 - Chipset: SiS 85C471, 85C407 - BIOS: Award BIOS ISA 486 S/N 240383 - Rev G2 - 09/26/94 - Keyboard BIOS: JETkey V5.0
// CPU: Socket 3 - RAM: 4xSIMM30, 2xSIMM72, Cache: 8xtm11256-20, 1xAster AE88128AK-15 - ISA16: 4, ISA16/VLB: 3 (one marked MASTER)
ROM_START( so025k2 ) // BIOS-String: 09/26/94-SIS-85C471B/E/G-2C4I9S23-00 / REV .G2.
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "sy025k2.bin", 0x10000, 0x10000, CRC(a75fdf9a) SHA1(ce7a595ec3bb33acac76a72f704a58e08d54847a))
ROM_END

// Zida 4DVS - Chipset:  SiS 85C471, 85C407 - CPU: Socket 3, RAM: 4xSIMM72, Cache: 4x28pin, 4x32pin, TAG
// ISA8: 1, ISA16: 3, ISA16/VL: 3 - Keyboard-BIOS: AMIKEY-2
// BIOS-String:
ROM_START( zi4dvs )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: 02/09/95-SIS-85C471B/E/G-2C4I9000-00 / 12/10/95 SIS 85C471G
	ROM_SYSTEM_BIOS(0, "4dvs20", "4DVS20")
	ROMX_LOAD( "4dvs20.awa", 0x10000, 0x10000, CRC(831d33cb) SHA1(e5c3f01a9c93a7cf9dbcdc750e87952a5b6a5cf4), ROM_BIOS(0))
	// 1: blank screen
	ROM_SYSTEM_BIOS(1, "072594", "AMI 07/25/94")
	ROMX_LOAD( "4dvs-471.amw", 0x10000, 0x10000, CRC(da749314) SHA1(686321ffa59cd2259f4fe65a28b86c88cf739393), ROM_BIOS(1))
	// 2: blank screen
	ROM_SYSTEM_BIOS(2, "121593", "AMI 12/15/93")
	ROMX_LOAD( "486-ab8068594.bin", 0x10000, 0x10000, CRC(92eee700) SHA1(bd34360cf9a9849e0805cdb575cd7a0e007dd2f5), ROM_BIOS(2))
ROM_END

ROM_START( sis85c471 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: Chipset: SiS 85C407, another chip with the Energy Star/Green PC label (85C441) - CPU: 486 - BIOS: Award BIOS ISA 486 036875 - Keyboard-BIOS: Lance LT38C41
	// BIOS-String: 04/28/94-SIS-85C471-2C4I8S21-00 / REV. B - ISA16: 4, ISA16/VL: 3 (2 master)
	ROM_SYSTEM_BIOS(0, "revb", "REV. B")
	ROMX_LOAD("486-sis_green.bin", 0x10000, 0x10000, CRC(9d3b5022) SHA1(f11b27bb24deb2466226486cf8ba66ddbeff87d6), ROM_BIOS(0))
	// 1: Chipset: SiS 85C407 85C471 - CPU: Cyrix Cx486DX2-66 - BIOS: Award E0042537 - Keyboard-BIOS: Lance LT38C41 - ISA8: 1, ISA16: 3, ISA16/VL: 3
	// BIOS-String: 02/07/94-SIS-85C371-2C4I8C30-00 / 02/17/94
	ROM_SYSTEM_BIOS(1, "486sl", "486SL")
	ROMX_LOAD("486-sis_486sl.bin", 0x10000, 0x10000, CRC(7261263e) SHA1(d5c4ee484941bbb8ca756c5f6e53382748bbcfd6), ROM_BIOS(1))
	// 2: REV:1.2 - Chipset: SiS 85C471 P85C407 - CPU: Socket 3 - BIOS: AMI 486DX ISA BIOS AC03601316 (28pin) - Keyboard-BIOS: JETkey V5.0G - RAM: SIMM72x4, Cache: 8 sockets+1
	// ISA8: 1, ISA16: 4, ISA16/VL: 3
	ROM_SYSTEM_BIOS(2, "rev12", "REV.1.2") // no display
	ROMX_LOAD("486-sis_ac0360136.bin", 0x10000, 0x10000, CRC(940e3643) SHA1(f931f5c2b39ebb6c509033984ab050ffa1ff4334), ROM_BIOS(2))
ROM_END


// ***** 486 motherboards using the UMC UM8498F, UM8496F chipset

// Aquarius MD-4DUVC VER:2.1 / Aquarius MD-4DUV VER:2.1
ROM_START( md4duvc ) // "Memory test fail"
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: CPU: Socket 3/Overdrive - Chipset: UMC UM8496F, UM8498F - RAM: 2xSIMM72, 4xSIMM30, Cache: 9xW2457AX-15, sockets are 32pin
	// ISA16: 4, ISA16/VL: 3 - BIOS: 28/32pin
	ROM_SYSTEM_BIOS(0, "md4duvc", "MD-4DUVC") // BIOS-String: 01/12/94--2C4X6H01-00 / Release 01/04/95
	ROMX_LOAD( "md-4duvc.dmg", 0x10000, 0x10000, CRC(40d208bb) SHA1(c879599d2635c093fce420d1e7081631d27c621a), ROM_BIOS(0))
	// 1: Chipset: UMC UM8498F, UM8496F - BIOS/Version: 486SX/DX Award, R3.1,  I194220, 1994-1995 - CPU: Socket 3
	// RAM: 4xSIMM72, Cache: 9xW24257AK-15 - ISA16: 4, ISA16/VLB: 3
	ROM_SYSTEM_BIOS(1, "md4duv", "MD-4DUV") // BIOS-String: 11/1794-UMC-498GP-2C4X6A31-00 / MB-4DUV/UVC VER 3.1
	ROMX_LOAD( "atrom.bin", 0x10000, 0x10000, CRC(ecb764f5) SHA1(f34a7671e9efc6a6cd7ff1516c0c8ecbbfcd55e0), ROM_BIOS(1))
ROM_END

// BIOSTAR MB-1433UIV - Chipset: BIOTEQ 83C3498, 83C3496 - CPU: Socket 3 - RAM: 3xSIMM30, 4xSIMM72, Cache: 4x32pin, 4x28pin + TAG
// ISA16: 4, ISA16/VL: 3 - BIOS: 28pin
ROM_START( mb1433uiv )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0:
	ROM_SYSTEM_BIOS(0, "um8496fw", "UM8496FW")
	ROMX_LOAD( "um8496fw.ami", 0x10000, 0x10000, CRC(4a6dcc36) SHA1(f159f67eb662272244cd1781814ebcb5204a2625), ROM_BIOS(0))
	// 1:
	ROM_SYSTEM_BIOS(1, "w4", "W4")
	ROMX_LOAD( "um498-w4.amw", 0x10000, 0x10000, CRC(12fe6697) SHA1(2506dea874728916dc37f7dad8e8caf214a28525), ROM_BIOS(1))
ROM_END

// PC-Chips M912 - Chipset: UM8498F, UM8496F - CPU: 486 - BIOS: AMI - ISA16: 4, ISA16/VL: 3
ROM_START( pccm912 ) // no display
	ROM_REGION32_LE( 0x20000, "bios", 0)
	// 0:
	ROM_SYSTEM_BIOS( 0, "072594", "07/25/94")
	ROMX_LOAD( "m912.bin", 0x10000, 0x10000, CRC(7784aaf5) SHA1(f54935c5da12160251104d0273040fea22ccbc70), ROM_BIOS(0))
	// 1:
	ROM_SYSTEM_BIOS( 1, "120295", "12/02/95")
	ROMX_LOAD( "m912_12-02-1995x.bin", 0x10000, 0x10000, CRC(28a4a140) SHA1(a58989ab5ad5d040ad4f25888c5b7d77f31a4d82), ROM_BIOS(1))
ROM_END

// Pine Technology PT-430 - Chipset: UMC UM8498F UM8496F - BIOS: AMI 486DX ISA BIOS AB8906726 28pin - Keyboard-BIOS: silkscreen 8742, but socket empty
// BIOS-String: - ISA8: 1, ISA16: 3, ISA16/VL: 3 - OSC: 14.31818MHz
ROM_START( pt430 ) // no display
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "pt430.bin", 0x10000, 0x10000, CRC(d455c949) SHA1(c57c82ed015528f3d223f59c94ed6b8a9c323c39))
ROM_END

// PowerTech MB457 aka Pine PT-2068.1 - Chipset: UMC UM8498F & UMC UM8496F - CPU: Intel 80486DX4-100, solder pads for UMC U5 Green CPU
// RAM: 4xSIMM30, 1xSIMM72 - ISA8: 1, ISA16: 4 - BIOS/Version: Award 486DX J314549
ROM_START( ptmb457 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 10/19/94-UMC-498GP-2C4X6S21-00 / REV A
	ROM_SYSTEM_BIOS(0, "101994", "10/19/94")
	ROMX_LOAD( "umc-mb457-1.bin", 0x10000, 0x10000, CRC(71f66fec) SHA1(c798d68eb851fc93cc8a3dd67009b47388488e51), ROM_BIOS(0))
	// 1: BIOS-String: 02/07/95-UMC-498GP-2C4X6000-00 / PT-2068.1
	ROM_SYSTEM_BIOS( 1, "020795", "02/07/95")
	ROMX_LOAD( "umc-mb457-2.bin", 0x10000, 0x10000, CRC(2654aefd) SHA1(b888f4f891108a5ef268688840ff20be3a8e6aa5), ROM_BIOS(1))
ROM_END

// Soyo 025R2 - Chipset: UM8498F, UM8486F - CPU: Socket 3 - RAM: 4xSIMM30, 2xSIMM72, Cache: 4x28pin, 4x32pin, TAG, used: 4xUM61512AK-15, AE88128AK-15
// BIOS: 28pin - ISA16: 4, ISA16/VL: 3
// BIOS-String: 08/28/UMC-498GP-2C4X6S21-00 / REV B2
ROM_START( so025r2 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD("25r2-b2.bin", 0x10000, 0x10000, CRC(3b73360c) SHA1(eaaf47236154a9cc81ffda4c11086960aed0dadf))
ROM_END

// ID: ADI F4DXL-UC4 - Chipset: UM8498F, one unreadable - BIOS: AMI 486DX BIOS AB345213 - CPU: Socket 3 - RAM: 4xSIMM30, 2xSIMM72
// Cache: 4x32pin DIP, 5x28pin DIP (used: 9xCL63C256N-20) - OSC: FM14318 - ISA16: 4, ISA16/VLB: 3
ROM_START( f4dxluc4 ) // BIOS-String: 40-0E0-008060-00101111-12159-UM498-0 / ADI/UC4/V 1.0
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "486_ab345213.bin", 0x10000, 0x10000, CRC(e5b85a92) SHA1(aade1fb1463b07a616c2594293bf0215c9652511))
ROM_END

ROM_START( um8498f ) // no display
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_DEFAULT_BIOS("498gp")
	// 0: BIOS-String: 40-P101-001437-00101111-072594-GREEN-H - CPU: Socket 3 - RAM: 4xSIMM30, 2xSIMM72, Cache: 9xUM61256AK-15
	// BIOS: AMI AB9300757
	ROM_SYSTEM_BIOS(0, "v14", "V1.4") // no display
	ROMX_LOAD( "4umm001.bin", 0x10000, 0x10000, CRC(a5b768b4) SHA1(904ce2814d6542b65acec0c84532946172f2296d), ROM_BIOS(0))
	// 1:
	ROM_SYSTEM_BIOS(1, "um849801", "UM8498 #1") // no display
	ROMX_LOAD( "um8498.ami", 0x10000, 0x10000, CRC(51f71bc7) SHA1(0986d60081d2c578a66789c0c53fe1d5919c553f),ROM_BIOS(1))
	// 2: Chipset: UM8498F + UM8496F - BIOS label: Award BIOS ISA 486 485427 - BIOS version: Award Modular BIOS v4.50G - CPU: UMC U55X 486-33F, solder pads for 80486socket
	// RAM: 4xSIMM30, 2xSIMM72 - ISA8: 1, ISA16: 5
	// BIOS-String: 12/08/94-UMC-498GP-2C4X6S21-00 / REV A1
	ROM_SYSTEM_BIOS(2, "498gp", "498GP")
	ROMX_LOAD( "award_bios_isa_486.bin", 0x10000, 0x10000, CRC(ce3ccaa4) SHA1(3fdc9282d9934e18e45b46b50644022fc0387f33), ROM_BIOS(2))
ROM_END


// ***** 486 motherboards using the UM82C482A UM82C481A chipset

// Elitegroup UM486/UM486sx Rev.1.4. - Chipset: UMC UM82C482A UM82C481A UM82C206F - ISA8: 2, ISA16: 6 - CPU: i486DX-50, FPU socket provided
// RAM: 8xSIMM30 in 2 banks, Cache: 8xW24256AK-25+4xCY7C164-20PC - OSC: 33.000MHz, 14.31818MHz - BIOS: AMI 486 BIOS ZZ364969 - Keyboard-BIOS: AMI KB-BIOS-VER-F
// BIOS-String: 40-0500-D01131-00101111-070791-UMCWB-0 / UM486/486SX V1.3 09-24-91
ROM_START( um486 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "486-um486-um486sx.bin", 0x10000, 0x10000, CRC(0f20d193) SHA1(e9c7365b0343a815e5abc9726d128353becc18d3))
ROM_END

// Elitegroup UM486V-AIO - Chipset: UMC UM82C482AF, UM82C481BF, UM82C863F, UM82C865F, UM82C206F - ISA16: 4, ISA16/VL: 2
// BIOS: AMI - CPU: 486 - On board: Floppy, 1xIDE, parallel, 2x serial
// BIOS-String: 40-0100-001131-00101111-111192-UMC480-0 / UM100 V2.1 04-26-93
ROM_START( um486v )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "um486v.bin", 0x10000, 0x10000, CRC(eb52d3fd) SHA1(84f63904cfceca9171b5c469545068e19ae280a8))
ROM_END


// ***** 486 motherboards using the UM8886BF, UM8881F chipset

// A-Trend ATC-1415 - Chipset: UMC UM8881F/UM8886BF, OEC12C885, VT8235N - ISA8: 1, ISA16: 3, PCI: 3
// CPU: Socket 3 - BIOS: Award C 0247007 UMC8881/6 BIS VER : 3.20 95-12-08 / M271001 (128k)
// BIOS-String: 11/24/95-UMC-881/886A-2A4X5A2HC-00
ROM_START( atc1415 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "4umw002.bin", 0x00000, 0x20000, CRC(eacaa3de) SHA1(88c828def72aeae0798ba5a4a5c31cd465545c0b))
ROM_END

// Biostar MB8433UUD-A (4 SIMM, 2 IDE, 3 PCI, 4 ISA)
// UMC UM8881F, UM8886BF, UM8663AF; DS12887 RTC
ROM_START( mb8433uud ) // BootBlock BIOS
	ROM_REGION32_LE(0x20000, "bios", 0) // Intel Flash P28F010
	// 0: BIOS-String: 05/20/96-UMC-881E/886B-2A4X5B08C-00 / UUD960520S EVALUATION ROM - NOT FOR SALE
	ROM_SYSTEM_BIOS(0, "520s", "520S")
	ROMX_LOAD( "uud0520s.bin", 0x00000, 0x20000, CRC(0e347559) SHA1(060d3040b103dee051c5c2cfe8c53382acdfedad), ROM_BIOS(0))
	// 1: BIOS-String: 05/20/96-UMC-881E/886B-2A4X5B08C-00 / BIOSTAR MB-8433UUD v2014
	ROM_SYSTEM_BIOS(1, "2014", "2014")
	ROMX_LOAD( "uud2014.bin", 0x00000, 0x20000, CRC(315f7519) SHA1(e0174e4982d1861c64d871a7806b793a914f2366), ROM_BIOS(1))
	// 2: BIOS-String: 12/05/95-UMC-881E/886B-2A4X5B08C-00 / UUD951222S
	ROM_SYSTEM_BIOS(2, "8881d", "8881D")
	ROMX_LOAD( "um8881_d.awa", 0x00000, 0x20000, CRC(a47327b9) SHA1(6b57155cf34e2c597b7621e49ae8354c05cebfde), ROM_BIOS(2))
	// 3: BIOS-String: 11/07/95-UMC-881E/886B-2A4X5B08C-0 / UUD951108A
	ROM_SYSTEM_BIOS(3, "08a", "08A")
	ROMX_LOAD( "8433uud.awd", 0x00000, 0x20000, CRC(17ca5c2a) SHA1(7cda78f87f35fe8574a1f2d70d548cc2bc2af05b), ROM_BIOS(3))
ROM_END

// Gigabyte GA-486AM/S - CPU: Socket 3 - Chipset: UMC UM8886AF, UM8881F, UM88663AF, UM8667 - RAM: 4xSIMM72, Cache: 9x32pin, occupied: 4xUM6152AK-15, 1xUM61M256K-15
// BIOS: 32pin - OSC: 14.31818 - On board: 2xIDE, Floppy, par, 2xser - ISA16: 4, PCI: 3
ROM_START( ga486am ) // BootBlock
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "4am1107.bin", 0x00000, 0x20000, CRC(e836798d) SHA1(afd72654ed210bb19e37f9df96b9ecb75c84712a))
ROM_END

// PC-Chips M915i - CPU: 486 - Chipset: UM8881F, UM8886F - ISA16: 2, ISA16/VL: 2, PCI: 4 - On board: 2xIDE
ROM_START( pccm915i )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: no display
	ROM_SYSTEM_BIOS(0, "9151108s", "9151108S")
	ROMX_LOAD( "9151108s.rom", 0x00000, 0x20000, CRC(cba5525c) SHA1(9bdb586090f613a7172f6b46ed78e36331bf2135), ROM_BIOS(0))
	// 1: Amptron DX9200 aka PcChips M915i with Fake cache - Chipset: UMC UM8881F + UM8886BF, SECKS82C6818A - EPROM: AMD AM27C010
	// BIOS label: AMI 486DX ISA BIOS 1993 AC9051796 - BIOS Version: AMI 10/10/1995: 11-08-1995 - on board: 2xIDE
	// CPU: Socket 3 - RAM: 4xSIMM72, Cache: 9xblack blocks of plastic - OSC: 14.31818 - ISA16: 2, ISA16/VLB: 2, PCI: 4
	ROM_SYSTEM_BIOS(1, "dx9200", "Amptron DX9200") // BIOS String: 41-p400-001437-00101111-101094-486AVIP-H
	ROMX_LOAD( "m915_fake_cache.bin", 0x00000, 0x20000, CRC(82a4e810) SHA1(b20a6e128d6298adf8487d190dd182a751dfccf9), ROM_BIOS(1))
ROM_END

// PC-Chips M919 - this motherboard showcased the issues that gave PC-Chips its bad name, it was available with fake cache, a proprietary cache socket or with fully operational cache
// Chipset: UMC UM8881F/9714-EYS and UM8886BF/9652-FXS (V3.4B/F), UMC UM8886BF/9618-FXS and UM8881F/9622-EYS (Rev. 1.5)
// http://th2chips.freeservers.com/m919/ this mentions that the BIOS requires a flashable chip
ROM_START( pccm919 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "m919v1", "PC-Chips M919 v1")
	ROMX_LOAD( "9190914s.rom", 0x00000, 0x20000, CRC(bb18ff2d) SHA1(530d13df21f2d483ec0dddda44fb4fe7e29ec040), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "m919v2", "PC-Chips M919 v2")
	ROMX_LOAD( "9191016s.rom", 0x00000, 0x20000, CRC(2a2125a6) SHA1(753061ae6f80c0ca42d1af91aada657910feae18), ROM_BIOS(1))
ROM_END

// Shuttle HOT-433 - Chipset: UM8886BF, UM8881F, UM8669F, ??667
// CPU: Cyrix 5x86-120GP - ISA16: 4, PCI: 4 - On board: PS2-Mouse, 2xser, Floppy, par, 2xIDE
// Versions 1-3 can use Flash or EPROM, Version 4 only EPROM
ROM_START( hot433 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 07/25/95-UMC-881/886A-2A4X5H21-00 / (433WIE10) UMC880A For486PCI Green_PC
	ROM_SYSTEM_BIOS(0, "wie10", "WIE10")
	ROMX_LOAD( "433wie10.bin", 0x00000, 0x20000, CRC(90604ef4) SHA1(61e160678d48cb5752c84090ca990e09382ae01d), ROM_BIOS(0))
	// 1: BIOS: 10/10/94 AMI (or 02/02/95 depending on where you look), 486PCI/ISA 057890 in a Winbond W29EE011-15 - no display
	ROM_SYSTEM_BIOS(1, "v401", "V4.0 #1")
	ROMX_LOAD( "hot433.bin", 0x00000, 0x20000, CRC(1c279c6f) SHA1(4a0e99fafc5719959fb5800a61629c3f36778240), ROM_BIOS(1))
	// 2: Original AMI BIOS for rev 1-3 w/mouse support
	ROM_SYSTEM_BIOS(2, "aip16", "AIP16")
	ROMX_LOAD( "433aip16.rom", 0x00000, 0x20000, CRC(a9503fc6) SHA1(0ebd936f5478477e37528e6e487c567b064248f7), ROM_BIOS(2))
	// 3: AMI BIOS for the EPROM Programmer, not flashable
	ROM_SYSTEM_BIOS(3, "aue2a", "AUE2A")
	ROMX_LOAD( "433aue2a.rom", 0x00000, 0x20000, CRC(35f5633f) SHA1(01148eba919985165ab9cd12b5e6f509d6d1385f), ROM_BIOS(3))
	// 4: AMI BIOS for the EPROM Programmer, not flashable
	ROM_SYSTEM_BIOS(4, "aue33", "AUE33")
	ROMX_LOAD( "433aue33.rom", 0x00000, 0x20000, CRC(803c4b1e) SHA1(483c799c08eed0d446384d67e9d23341499806b1), ROM_BIOS(4))
	// 5: AMI BIOS for rev 1-3.  Some reports say for rev4
	ROM_SYSTEM_BIOS(5, "aus2a", "AUS2A")
	ROMX_LOAD( "433aus2a.rom", 0x00000, 0x20000, CRC(766d1f3f) SHA1(1e59140bc91ab98fcadcf7bb77e222932696419f), ROM_BIOS(5))
	// 6: Latest AMI BIOS for rev 1-3
	ROM_SYSTEM_BIOS(6, "aus2c", "AUS2C")
	ROMX_LOAD( "433aus2c.rom", 0x00000, 0x20000, CRC(bdc65766) SHA1(e87cc4aed14ae7fcdf6423063b0ababe65b41044), ROM_BIOS(6))
	// 7: AMI BIOS for rev 1-3 w/mouse support
	ROM_SYSTEM_BIOS(7, "aus26", "AUS26")
	ROMX_LOAD( "433aus36.rom", 0x00000, 0x20000, CRC(8f864716) SHA1(0bf4b8114cbb406646d89eed7833556611e1fbe6), ROM_BIOS(7))
	// 8: Latest AMI BIOS for rev4 of the Shuttle HOT-433 motherboard.
	ROM_SYSTEM_BIOS(8, "aus33", "AUS33")
	ROMX_LOAD( "433aus33.rom", 0x00000, 0x20000, CRC(278c9cc2) SHA1(ecd348106d5118eb1e1a8c6bd25c1a4bf322f3e6), ROM_BIOS(8))
	// 9: boots to BootBlock BIOS
	ROM_SYSTEM_BIOS(9, "2a4x5h21", "2A4X5H21")
	ROMX_LOAD( "2a4x5h21.bin", 0x00000, 0x20000, CRC(27c47b90) SHA1(09d17bc5edcd02a0ff4a3a7e9f1072202880251a), ROM_BIOS(9))
	// 10: no display
	ROM_SYSTEM_BIOS(10, "shuttle", "Shuttle HOT-433")
	ROMX_LOAD("bios.bin", 0x00000, 0x20000, CRC(a5ffafac) SHA1(0ec71a82c80a96a5724ba375934c709db468dabc), ROM_BIOS(10))
ROM_END

// PROTECH PM486PU-S7 - Chipset: UMC 881/886A (UM8881F/UM8886AF), SMC FDC, Winbond
// BIOS Chip: TI/TMS 27C010A (128K) - CPU: i486DX-33 - On board: 2xIDE, FDC, 2xser, par - RAM: 2xSIMM72, Cache: 4xGLT721208-15 +1 TAG - ISA16: 4, PCI: 3
// BIOS-String: 10/11/95-UMC-881/886A-2A4X5P62-00 / (PM486PU-S7) 486 WITH I/O PCI LOCAL BUS SYSTEM ...
ROM_START( pm486pu )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "pm486pu-s7-2a4x5p62-00.bin", 0x00000, 0x20000, CRC(143bdc07) SHA1(e2cf2ac61fd3e4797e5d737dfec4a2b214469190))
ROM_END

// Pine PT-432b - Chipset: UMC UM8886BF, UM8881F, UM8663F, UM8287, UM8667 - CPU: Am486DX4-100 - RAM: 4xSIMM72, Cache: 8+1 sockets
// ISA16: 4, PCI: 3 - BIOS: AMI 486PCI ISA BIOS AA0841149 (32pin) - On board: 2xser, par, 2xIDE, Floppy
ROM_START( pt432b ) // no display
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "sr_m401-a.bin", 0x00000, 0x20000, CRC(ff8cd351) SHA1(a9c6a54f38b1b548fba4d7d42643f117441b09a6))
ROM_END

// TD-4IP-UMC-AIO - CPU: Socket 3 - Chipset: UMC UM8881F, UM8886BF, UM8663BF, UM8667, bq3285P - RAM: 2xSIMM72, Cache: W24257AK-15, W24512AK-20 (4)
// BIOS: Award 020175447 / 29EE010 (128k) - BIOS-String: 12/19/95-UMC-881E/886B-2A4X5CF9C-00 - ISA16: 4, PCI: 3
ROM_START( td4ipaio )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "4ummw001.bin", 0x00000, 0x20000, CRC(4e716d77) SHA1(1b7af88c4da5ca388acbc0cb66bd26a0ae8f4931))
ROM_END

ROM_START( um8886 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: no display - UMC PCI 95C-0123 - Chipset: UMC UM8886AF, UM8881F, 4xUM8002, UM8663AF, UM8667 - CPU: Socket 3 - On board: 2xser, par, Floppy, 2xIDE - 4xISA16, 4xPCI
	// BIOS: AMI 486 PCI ISA in M27C1001 EPROM
	ROM_SYSTEM_BIOS( 0, "pci95c", "PCI 95C-0123")
	ROMX_LOAD( "486-umc_pci_95c-0123.bin", 0x00000, 0x20000, CRC(9db58de4) SHA1(5441f3181fb26911d796c4bf019136aa8e4c060b), ROM_BIOS(0))
	// 1: no display - V1.1A - Chipset: UMC UM8886AF UM8881F, UM8667, UM8663AF - CPU: i486DX2-66 - On board: 2xser, 2xIDE, Floppy, par - BIOS: AMI 486DX ISA BIOS AC6288199 - ISA16: 4, PCI: 3
	ROM_SYSTEM_BIOS( 1, "pcimini", "PCI mini")
	ROMX_LOAD( "486-umc-pci mini.bin", 0x00000, 0x20000, CRC(4ee12b46) SHA1(9397f67b21f11cfda57abd5ab28f93055909ee97), ROM_BIOS(1))
ROM_END

// Elitegroup UM8810 PAIO - Chipset: UMC UM8881F, UM8886F, UM82C865FSMC FDC37C665GT, PCI0640B - CPU: Socket 3 - RAM: 4xSIMM72, Cache: 32pin sockets, 9xUM61256FK-15 occupied
// BIOS: 32pin - Keyboard-BIOS: AMIKEY-2 - ISA16: 4, PCI: 3 - On board: 2xIDE, Floppy, par, 2xser
ROM_START( um8810paio )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 09/27/94-UMC-881/886-2A4X5E11-00 / UM8810P-AIO V1.0 09-27-94'
	ROM_SYSTEM_BIOS(0, "awd", "AWD")
	ROMX_LOAD( "awd_09-27-94.bin", 0x00000, 0x20000, CRC(25c671ba) SHA1(6b7641fb0f619e233d956e7cdb4e246f3c525673), ROM_BIOS(0))
	// 1: BootBlock BIOS
	ROM_SYSTEM_BIOS(1, "aw123", "AW_1-23")
	ROMX_LOAD( "aw_1-23.bin", 0x00000, 0x20000, CRC(420c3a43) SHA1(e0744b0bf171d279650d1f3a731e231562fe60ae), ROM_BIOS(1))
	// 2: blank screen, possibly for update only
	ROM_SYSTEM_BIOS(2, "v21", "V21")
	ROMX_LOAD( "881_v21_01-27-95.rom", 0x00000, 0x20000, CRC(2443759b) SHA1(02a9e51b51c2d2d21d5ae821b6d8aa97f62c3327), ROM_BIOS(2))
	// 3: blank screen
	ROM_SYSTEM_BIOS(3, "21e", "21E")
	ROMX_LOAD( "881avio_01-08-96.21e", 0x00000, 0x20000, CRC(446cd0c3) SHA1(32a61c0578326d17840db37855644543b6406535), ROM_BIOS(3))
	// 4: blank screen
	ROM_SYSTEM_BIOS(4, "31e", "31E")
	ROMX_LOAD( "881avio_01-08-96.31e", 0x00000, 0x20000, CRC(b5ec7dca) SHA1(390f79f50de986c3874464202251398baa87f4b5), ROM_BIOS(4))
	// 5: blank screen
	ROM_SYSTEM_BIOS(5, "32h", "32H")
	ROMX_LOAD( "8810aio_06-27-96.32h", 0x00000, 0x20000, CRC(b7e9d590) SHA1(bfdfa64bc87f2eb0d362e87755dce70584675427), ROM_BIOS(5))
	// 6: blank screen, possibly for update only
	ROM_SYSTEM_BIOS(6, "32j", "32J")
	ROMX_LOAD( "8810aio_12-11-96.32j", 0x00000, 0x20000, CRC(c6baec8b) SHA1(79bccaefb75e2df071d6edd191eea74aa82dc620), ROM_BIOS(6))
	// 7: blank screen
	ROM_SYSTEM_BIOS(7, "32f", "32F")
	ROMX_LOAD( "881032f.rom", 0x00000, 0x20000, CRC(5c337ae6) SHA1(29bc48a3d5165afa79612baf6deb9c04c7e55b32), ROM_BIOS(7))
	// 8: blank screen
	ROM_SYSTEM_BIOS(8, "v145", "V145")
	ROMX_LOAD( "8810aio_v145_09-21-94.bin", 0x00000, 0x20000, CRC(a253c017) SHA1(8273defe95a13ea0a260d4a410d601f82a947ad9), ROM_BIOS(8))
	// 9: 06/23/95-UMC-881/886-2A4X5E11-00 / Version VBS1.10H UM8810 ECS (Highscreen boot logo)
	ROM_SYSTEM_BIOS(9, "v110h", "V1.10H")
	ROMX_LOAD( "vbs1.10h.bin", 0x00000, 0x20000, CRC(1bf29727) SHA1(77ccd34110ec0387cdcfa260332b403d0c197d17), ROM_BIOS(9))
ROM_END


// ***** 486 motherboards using the UMC UM82C491F UM82C493F or clones (BIOTEQ) chipset

// Chicony CH-491E Rev. 1.4 - Chipset: UMC UM82C491F UM82C493F - BIOS: 04/04/93 AMI AB1987679 28pin - Keyboard-BIOS: AMIKEY
// BIOS-String: 40-0102-001116-00101111-040493-UMC491F-0 / UMC 491 for 80486 AUTO - ISA16: 4, ISA16/VL: 3
ROM_START( ch491e )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "ch491e.bin", 0x10000, 0x10000, CRC(2d24ff24) SHA1(72f35c19e907c6d0a03a49bd362c4f57cc89da1c))
ROM_END

// Aquarius System (ASI) MB-4D33/50NR VER:01 - Chipset: UMC UM82C491F UM82C493F - CPU: AM486DX2-66 - BIOS: Award 1060176, 28pin - Keyboard-BIOS: JETkey V5.0
// BIOS-String: 03/09/94-UMC-491-2C4X2A30-00 / MB-4D33/50NR-02 - ISA8: 1, ISA16: 3, ISA16/VL: 3
ROM_START( mb4d33 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "486-mb-4d33.bin", 0x10000, 0x10000, CRC(f1299131) SHA1(d8e2749e180135e23483e36a0a05479e64f23d8c))
ROM_END

	// BIOS-String: 40-0100-001281-00101111-080893-UMC491F-0 / HL3SC/SM AMI BIOS VER 4.9 01/09/'95
ROM_START( 4dmuhl3s )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "3s-49-1.rom", 0x10000, 0x10000, CRC(78a8ea39) SHA1(35e2d0103da28b93a8c1addb1f083bd6e4239d2a))
ROM_END

// Elitegroup ECS UC4915 A AIO - Chipset: UMC UM82C491F UM82C493F UM82C865F SMC FDC37C662QF P,  PROCHIP PR 4030 - CPU: Socket 3
// BIOS: AMI 486DX ISA BIOS AB2683223 28pin in 32pin socket - Keyboard-BIOS: Intel/AMI MEGA-KB-H-WP
// BIOS-String: 40-0401-001131-00101111-040493-UMC491C-0 / VOBIS UC4915-A V1.1 11-05-93' - ISA16: 4, ISA16/VL: 2 - OSC: 14.31818 - On board: IDE, Floppy, 2xser, par, Game
ROM_START( ec4915aio )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "486-ecs-uc4915-a-aio.bin", 0x10000, 0x10000, CRC(5b3429a3) SHA1(a1b3ddb6a0939d20ae66e034914ea94648ca7149))
ROM_END

//  Elitegroup UC4913 REV:1.1 (Peacock sticker)- Chipset: UMC UM 82491F 82C493F - CPU: 486 - BIOS: AMI 486DX ISA BIOS AA8960448 (28pin) - Keyboard-BIOS: AMI/Intel
// OSC: 14.31818 - RAM: SIMM30x8, Cache: 9 sockets, 4 (UM61256CK-20)+1 (MS6264A-20NC) filled - ISA8: 2, ISA16: 3, ISA16/VL: 3
// BIOS-String: 40-0401-001131-00101111-040493-UMC491C-0 / Peacock AG UC4913 BIOS Ver. 1.0 01.09.93
ROM_START( ec4913 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "486-peacock-uc4913-aa8960338.bin", 0x10000, 0x10000, CRC(58e6753c) SHA1(077c11532572ca0399f76a7ba2d31b8c1ca75d48))
ROM_END

// Biostar MB-1433UCV - Chipset: BIOTEQ 82C3491, 82C3493 (check mb133340 for a 386 motherboard using the same chipset)
// CPU: 486DX2-66 - RAM: 8xSIMM30, Cache: 8+1x28pin(AS57C256-20PC) - ISA8: 1, ISA16: 3, ISA16+VL: 3 - BIOS: AMI AB0975913 - Keyboard-BIOS: JETkey V5.0 - RTC: TH6887A 9410
ROM_START( mb1433ucv )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS-String: 40-0100-001223-00101111-040493-UMC491F-0 / MB-1333/40UCG-A, MB-1333/40UCQ-B / MB-1433-40UDV-A, MB-1433/50UCV-C, MB-6433/50UPC-A for EXT. RTC
	ROM_SYSTEM_BIOS(0, "ucvd", "UCV-D")
	ROMX_LOAD( "biostar_bios_mb-1433-50ucv-d_pcb_ver_2.bin", 0x10000, 0x10000, CRC(e5ff2d76) SHA1(d2abe00eb2051ec7cb9423cdb8b52e91f7e2d416), ROM_BIOS(0))
	// 1: BIOS-String: 40-0100-001223-00101111-04093-UMC491f-0 / UCV-G
	ROM_SYSTEM_BIOS(1, "ucvg", "UCV-G")
	ROMX_LOAD( "bioteq.rom", 0x10000, 0x10000, CRC(93321e89) SHA1(450e35787607a4b6aecd3159d6c0599a03cd42b1), ROM_BIOS(1))
ROM_END

// Mitac PWA-IH4077C - Chipset: UMC UM82C491F + UM82c493F - CPU: Socket 3, solder pads for PQFP CPU - RAM: 4xSIMM30, Cache: 9xUM61256AK-15 - OSC: 14.31818
ROM_START( pwaih4077c )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS String: 04/13/94-UMC-491-2C4X2m31-00 - UMC491F 80486 BIOS...1.01.00 (3371-3372) - BIOS label: IH4077CN BIOS R1.01.00
	// BIOS Version: Award 4.50G 04/13/94 - Keyboard BIOS: VIA VT82C42N
	ROM_SYSTEM_BIOS(0, "012694", "01/26/94")
	ROMX_LOAD( "ih4077c-1.00.00.bin", 0x10000, 0x10000, CRC(e75cca73) SHA1(4f27d16f4f8fce9d3410821ec62780f7df669776), ROM_BIOS(0))
	// 1: BIOS-String: 04/13/94-UMC-491-2C4X2m31-00 - UMC491F 80486 BIOS...1.01.00 (3371-3372)
	ROM_SYSTEM_BIOS(1, "041394", "04/13/94")
	ROMX_LOAD( "4077c101.bio", 0x10000, 0x10000, CRC(b6a27c48) SHA1(18ee3b2fc4897cbaafc0e0298938ba58a3a7f84c), ROM_BIOS(1))
ROM_END


// ***** motherboards using the Unichip U4800 chipset

// Gemlight GMB-486UNP v2.1 - Chipset: Unichip U4800-VLX, SIS85C206 - CPU: solder pads for 486FQFP, i486DX2-66 - RAM: 8xSIMM30, Cache: 8xUM61256AK-15
// BIOS: AMI 486DX ISA BIOS - ISA16: 4, ISA16/VLB: 3
ROM_START( gmb486unp ) // BIOS String: 40-0405-428036-00101111-080893-U4800-VLX-H / screen remains blank
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "gmb-486unp.bin", 0x10000, 0x10000, CRC(17d770c7) SHA1(8655610ceaf7bd9c17d7c0a550805ae55f128660))
ROM_END

// UNICHIP 486 WB 4407 REV 1.0 - Chipset: KS83C206Q UNICHIP U4800-VLX - BIOS: AMI 486 ISA BIOS AA6562949, 28pin - Keyboard-BIOS: AMI 2050778
// BIOS-String: 40-0200-001107-0010111-111192-U4800VLX-0 / 4407 UNICHIP BIOS VER 1.0 - OSC: 14.31818 - ISA16: 4, ISA16/VL: 3
ROM_START( uniwb4407 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "unichip_486_wb_4407.bin", 0x10000, 0x10000, CRC(91237686) SHA1(7db14451cc3e00a2273a453152a817bccbdfb10e))
ROM_END

ROM_START( uni4800 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// BIOS-String: 40-05TB-006257-00101111-060692-UNI4800-0
	ROM_LOAD( "uni4800.ami", 0x10000, 0x10000, CRC(555c4c34) SHA1(0a907d7a0ec16e9369d77a1ac1afb0e69e3d6c1a))
ROM_END


// ***** 486 motherboards using the VIA VT82C495 VT82C481 chipset

// FIC 4386-VC-V - CPU: 486 - Chipset: VIA VT82C495 VT82C481 - ISA8: 2, ISA16: 3, ISA16/VL: 2 - OSC: 33.333MHz - BIOS: AMI 486DX ISA BIOS AA6387315 (28pin) -
// BIOS-String: X0-0100-001121-00101111-021993-VIA-0 / Version 1.02 - Keyboard-BIOS: Lance LT38C41
ROM_START( fic4386vcv )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "486-4386-vc.bin", 0x10000, 0x10000, CRC(659210c2) SHA1(a730a547f3af215459632160fa670fde7e9c4f9a))
ROM_END

// HIGHSCREEN 486 Universal Board C82C33-A VIA4386-VIO - Chipset: VIA VT82C495 VT82C481, Winbond WB3757F - CPU: AM486DX2-66
// BIOS: Award F0599630 - Keyboard BIOS: AMI 1131 KEYBOARD BIOS PLUS - BIOS-String: Award Modular BIOS v4.20 / Version 1.143K
// On board: IDE, Floppy, 2xser, par, game - OSC: 32.0000MHz - ISA16: 6
ROM_START( via4386vio ) // probably a FIC board - KEYBOARD ERROR OR NO KEYBOARD PRESENT
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "486-highscreen.bin", 0x10000, 0x10000, CRC(059b6e51) SHA1(f8ede823e41cfa6f72bd9717ec75419079f9c40b))
ROM_END

// FIC 4386-VC-HD - Chipset: VIA VT82C481, VT82C495 - this board can take either 386 or 486 CPUs
// Keyboard-BIOS: Lance LT38C41 - CPU: AMD AMD386DX-40, FPU: IIT 3C87-40 - ISA16: 6
ROM_START( fic4386vchd )
	ROM_REGION32_LE(0x20000, "bios", 0)
	// 0: BIOS: AMI; Version 1.04; 06/06/92 - BIOS-String: X0-0100-001121-00101111-021993-VIA-0 / Version 1.04
	ROM_SYSTEM_BIOS(0, "ami104", "AMI V1.04")
	ROMX_LOAD( "3vim001.bin", 0x10000, 0x10000, CRC(668d8cab) SHA1(409b81e33ca07b0a9724dbb6ca395a3a0887aa02), ROM_BIOS(0))
	// 1: BIOS: Award F0111730 v1.15K 03/12/93-VENUS-VIA - BIOS-String: Award Modular BIOS v4.20 / Version 1.15K
	ROM_SYSTEM_BIOS(1, "awav115k", "Award V1.15k") // KEYBOARD ERROR OR NO KEYBOARD PRESENT
	ROMX_LOAD( "4386-vc-hd v1.15k.bin", 0x10000, 0x10000, CRC(acc5db45) SHA1(cb93322735e96614d3c54fbfcd4291ff1b3ca57c), ROM_BIOS(1))
	// 2: AWARD v4.20 F0166061 (28pin) - Keyboard-BIOS: Lance LT38C41 - CPU: 486 - BIOS-String
	ROM_SYSTEM_BIOS(2, "awav110k", "Award V1.10K") // KEYBOARD ERROR OR NO KEYBOARD PRESENT
	ROMX_LOAD("486-4386-vc-hd.bin", 0x10000, 0x10000, CRC(a32d30fc) SHA1(815a63e624b3145d9955aa3ce8c4c1e34fb438bb), ROM_BIOS(2))
	// 3: ID: Peacock 4386-VCHD - Chipset: VIA VT82C481, VT82C495 - CPU: AMD Am386DX-40, socket for 486 provided
	// RAM: 8xSIMM30, Cache: 10x28pin sockets - BIOS: Award 386DX F0121091 PEA 2_0 - ISA16: 6 - OSC: 80.0000MHz
	ROM_SYSTEM_BIOS(3, "pea20", "Pea 2_0") // BIOS-String: Award Modular BIOS V4.20 / 4386 BIOS Ver. 2.0 01.04.93 - KEYBOARD ERROR OR NO KEYBOARD PRESENT
	ROMX_LOAD("386-peacock-4386-vchd.bin", 0x10000, 0x10000, CRC(1cd08629) SHA1(9a2b359ade2e93ab1d164e3e4f2cb9e8604cd43d), ROM_BIOS(3))
ROM_END

// FIC 486-VC-HD - BIOS Version: AMI 05/05/1991 - Chipset: VIA VT82C495 VT82C481, DS1287/1187 - EPROM Label: 486EB
// Keyboard BIOS: Lance LT38C41 - CPU: Intel 80486DX-33, solder pads for 80486 - RAM: 8xSIMM30, Cache: 10x28pin DIP (4xKM68257BP-25, 1xMCM6206CP fitted)
// OSC: 33.333MHz - ISA16: 6
ROM_START( fic486vchd ) // BIOS ID String: 40-04C1-ZZ1124-00101111-050591-ET/486H-0
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "486-vc-hd_doc11670.bin", 0x10000, 0x10000, CRC(607ebe18) SHA1(870080bb49bad42fb4433f9208c17ad1c7ee437d))
ROM_END


// ***** 486 motherboards using the VIA VT82C505 + VT82C496G + VT82C406MV chipset

// FIC 486-PIO-2 (4 ISA, 4 PCI)
// VIA VT82C505 (ISA/VL to PCI bridge) + VT82C496G (system chipset) + VT82C406MV (keyboard controller, RTC, CMOS), NS311/312 or NS332 I/O
ROM_START( ficpio2 )
	ROM_REGION32_LE(0x40000, "isa", ROMREGION_ERASEFF)
	ROM_DEFAULT_BIOS("ficpio2c1")
	// 0
	ROM_SYSTEM_BIOS(0, "ficpio2c7", "FIC 486-PIO-2 1.15C701") /* pnp, i/o core: NS 332, doesn't boot, requires cache emulation? */
	ROMX_LOAD( "115c701.awd",  0x020000, 0x20000, CRC(b0dd7975) SHA1(bfde13b0fbd141bc945d37d92faca9f4f59b716d), ROM_BIOS(0))
	// 1
	ROM_SYSTEM_BIOS(1, "ficpio2b7", "FIC 486-PIO-2 1.15B701") /* pnp, i/o core: NS 311/312, doesn't boot, requires cache emulation? */
	ROMX_LOAD( "115b701.awd",  0x020000, 0x20000, CRC(ac24abad) SHA1(01174d84ed32fb1d95cd632d09f773acb8666c83), ROM_BIOS(1))
	// 2: BIOS-String: 04/18/96-VT496G-2A4LF0IC-00 / Version 1.15C101
	ROM_SYSTEM_BIOS(2, "ficpio2c1", "FIC 486-PIO-2 1.15C101") /* non-pnp, i/o core: NS 332, working  */
	ROMX_LOAD( "115c101.awd",  0x020000, 0x20000, CRC(5fadde88) SHA1(eff79692c1ecf34b6ea3f02409d14ce1f5c51bf9), ROM_BIOS(2))
	// 3: BIOS-String: 04/18/96-VT496G-2A4LF0IC-00 / Version 1.15B101
	ROM_SYSTEM_BIOS(3, "ficpio2b1", "FIC 486-PIO-2 1.15B101") /* non-pnp, i/o core: NS 311/312, working  */
	ROMX_LOAD( "115b101.awd",  0x020000, 0x20000, CRC(ff69617d) SHA1(ecbfc7315dcf6bd3e5b59e3ae9258759f64fe7a0), ROM_BIOS(3))
	// 4: no display - CPU: Socket3 - On board: 2xser, par, 2xIDE, Floppy, par - BIOS: Award F4215801, 32pin - ISA16: 4, PCI: 4
	ROM_SYSTEM_BIOS(4, "ficpio2", "FIC 486-PIO-2 DOC 14580")
	ROMX_LOAD( "486-pio2.bin", 0x20000, 0x20000, CRC(4609945d) SHA1(7ad446bc3b27f3f636fb5884e58b055681f081eb), ROM_BIOS(4))
ROM_END

// FIC 486-VIP-IO2 (3 ISA, 4 PCI)
// VIA VT82C505 + VT82C496G + VT82C406MV
// Boot block - BIOS-String: 05/28/96-VT496G-2A4L6F0IC-00 / 1.164G701
ROM_START( ficvipio2 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "1164g701.awd", 0x00000, 0x20000, CRC(7b762683) SHA1(84debce7239c8b1978246688ae538f7c4f519d13))
ROM_END


//**************************************************************************
//  80486 Desktop
//**************************************************************************

// NEC APC IV aka - available in three different packages: Wide desktop: Year: 1986 / Chipset: CHIPS P82C201, P82A204 and P82A205
// Portable: white/blue LCD
ROM_START( necapciv )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// 0: Phoenix 80286 ROM BIOS Version 1.57 / APC IV / NEC Corporation
	// on board: 2xser, par, Floppy - see https://stason.org/TULARC/pc/motherboards/N/NEC-TECHNOLOGIES-INC-286-APC-IV-SERIES-G9YAN.html for settings
	ROM_SYSTEM_BIOS(0, "wide157", "Wide desktop V1.57")
	ROMX_LOAD( "yan7m06.bin", 0x10000, 0x8000, CRC(21deafcb) SHA1(477fb36d64a9a60f6dc572fef1095391f6da73b3), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "yan7k06.bin", 0x10001, 0x8000, CRC(97563bae) SHA1(81ea93e1cd55e284609fdff6574aa49b06cd8a7f), ROM_SKIP(1) | ROM_BIOS(0))
	// 1: Phoenix 80286 ROM BIOS Version 3.07 03 / NEC Corporation
	// Narrow desktop (APC IV Powermate 1): Available with EGA (LIAI852/EVC215-001 chip) or VGA and 8Mhz/640k version and a 10Mhz/1MB version. [Ctrl]+[Alt]+[-]
	// is the speed switch on the 10MHz version.
	// on board: ser, par, Floppy - see http://www.uncreativelabs.de/th99/m/M-O/31487.htm for settings
	ROM_SYSTEM_BIOS(1, "narrow", "Narrow desktop V3.07 03")
	ROMX_LOAD( "bbx10j02.bin", 0x10000, 0x8000, CRC(5bb8c773) SHA1(21df040a92b2ee17e83955776af4ab14350d5ffd), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "bbx10g02.bin", 0x10001, 0x8000, CRC(050159ef) SHA1(b1e627f5d5ef749c51597b7be75f56bb8ff4d8af), ROM_SKIP(1) | ROM_BIOS(1))
ROM_END

// NCR Class 3433 - CPU: 486SX/DX 25/33, coprocessor socket provided - Chipset: NCR WPD CLEMSON 006-2001325 CQO1842 9209N
// LSI LOGIC L1A5840 006-2000654B NAR 9212Delta WG35494 GERMANY, NCR 006-2001895 WPD FALCON E CQO 2291 9218N,
// WD58C65JM, VLSI 9210AV 211411 VGA8203C4570 NCR PB 006-2001329, Dallas DS1387
// OSC: 16.000000MHz, 1.8432MHz,  65.00000MHz, 25.175/26.322, 36.000000MHz, 50.000000MHz, 14.31818MHz, 66.66600MHz
// on board VGA: NCR 77C22 VGA-2 609-3400639 CQO1570 9210R, 8xMCM514245AJ70
// Slots: 4xMCA, 3x32-bit external memory card, 1xSCSI host adapter - ports: PS/2 mouse, keyboard, 2xserial, parallel, floppy
ROM_START( ncr3433 )
	ROM_REGION32_LE( 0x20000, "bios", 0 )
	// 0: NCR ROM BIOS Version 82.01.00 (3433, 3434, 3432, 3437)
	ROM_SYSTEM_BIOS(0, "82.01.00", "82.01.00")
	ROMX_LOAD( "rbios206.bin", 0x00000, 0x20000, CRC(06e77547) SHA1(41c517b284ed6335024c2b8398504cf3eb8a09e1), ROM_BIOS(0) )
	// 1: NCR ROM BIOS Version 83.01.00 (3433, 3434, 3432, 3437)
	ROM_SYSTEM_BIOS(1, "83.01.00", "83.01.00")
	ROMX_LOAD( "rbios1.bin", 0x00000, 0x20000, CRC(cf793ce9) SHA1(69d531d14a86bdada8940ba2466515ebcd870d0d), ROM_BIOS(1) )

	ROM_REGION( 0x0800, "keyboard", 0 )
	ROM_LOAD( "i8742_150-0008390_vers_3.1.bin", 0x000, 0x800, CRC(1bf17f29) SHA1(031ea1bb40756e3e5a1f01b01a53cc3a7c074338) )
ROM_END

// Amstrad PC9486 - Board Type - UX486VIO-A Rev. 1.0 - Chipset: UMC - UM82C481BF/UM82C482AF/UM82C206F - BIOS/Version: AMI 486DX ISA BIOS 1993 - AA9222968
// BIOS String: 40-0100-001131-00111111-111192-UMC480-0 / Amstrad PC9486 - CPU: 80486sx-25 in Socket 3, solder pads for 486 CPU - RAM: 4xSIMM30, 1xSIMM72, Cache: 64K/128K256K
// on board VGA: Cirrus Logic CL-GD5426-80QC-A, 2xKM416C256AJ-7 - Jumpers: Parallel out/bidirectional, VGA enable/disable, SIMM type, PQFP or socket,
// 20/25/33/40/50 MHz, Parity enable/disable, -  CPU type - on board: VGA, Floppy, IDE - slots: 1, for riser card
ROM_START( pc9486 )
	ROM_REGION32_LE( 0x20000, "bios", 0 )
	ROM_LOAD ( "9486_saverom.bin", 0x00000, 0x20000, CRC(cbc35a4e) SHA1(dfa614c8255a1407c9850fa4ff99a6b2a52e1a4f) )
ROM_END

//**************************************************************************
//  80486 Laptop/Notebook
//**************************************************************************

// Siemens-Nixdorf PCD-4NL 486 subnotebook
// PhoenixBIOS(TM) A486 Version 1.03
// complains about "Pointer device failure" and "Memory failure at 00100000, read AA55 expecting 002C
ROM_START( pcd4nl )
	ROM_REGION32_LE( 0x20000, "bios", 0 )
	ROM_LOAD( "pcd4nl.bin", 0x00000, 0x20000, CRC(8adb4900) SHA1(a01c665fed769ff815bc2e5ae30901f7e12d721b) )
ROM_END

// Siemens-Nixdorf PCD-4ND 486 notebook - display remains blank
// Graphics chip: WDC WD90C24A-ZZ on VESA Local Bus, 4MB on mainboard, 4MB/8MB/16MB on CF card like RAM modules
// CPU: Intel 486 SX, 486DX2, 486DX4-75 or -100,  128KB Flash-Eprom for system and video BIOS, ESS688 soundchip
ROM_START( pcd4nd )
	ROM_REGION32_LE( 0x20000, "bios", 0 )
	ROM_SYSTEM_BIOS(0, "pcd4ndno1", "pcd4ndno1")
	ROMX_LOAD( "bf3m51.bin", 0x00000, 0x20000, CRC(6a2f90dd) SHA1(75704a83976e4bb02a028e761d01bd053cc0d4e7), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "pcd4ndno2", "pcd4ndno2")
	ROMX_LOAD( "bf3q42.bin", 0x00000, 0x20000, CRC(fa81cf6e) SHA1(91313a6856ca22f40710a6c9c8a65f8e340784ab), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS(2, "pcd4ndno3", "pcd4ndno3")
	ROMX_LOAD( "pcd-4nd_flash_28010.bin", 0x00000, 0x20000, CRC(53c0beea) SHA1(bfa17947529c51a8c9315884e156c01ddd23c0d8), ROM_BIOS(2) )
ROM_END

// LION 3500C notebook
ROM_START( lion3500 )
	ROM_REGION32_LE( 0x20000, "bios", 0 )
	ROM_LOAD( "lion3500.bin", 0x00000, 0x20000, CRC(fc409ac3) SHA1(9a7aa08b981dedffff31fda5d3496469ae2ec3a5) )
ROM_END

// Highscreen 486-25 aka Midwest Micro Elite TS34T-25 notebook
// integrated trackball - CPU: i486sx-25 - Chipset: Chips F82C721, Intel ?80C51SLBG, MCCS1468188F, AvaSem AV9129-08CW28, ACC Micro 2046, LT1137CS
// Video: CL-GD6410-320C-A - HD: Maxtor 2585AT
ROM_START( ts34t25 )// blank display
	ROM_REGION32_LE( 0x20000, "bios", 0 )
	ROM_LOAD( "p101a002_sys_bios_62fc.u24", 0x00000, 0x20000, CRC(2ce568bc) SHA1(84dc595abf0bf1948a6479afdea4a169f40e3b1b))

	ROM_REGION( 0x8000, "pmu", 0 ) // rom contains "PMU" string
	ROM_LOAD( "s34t0003_51slbios_019f.u31", 0x00000, 0x8000, CRC(40467716) SHA1(f976f2ce13eb22e0ed164d31d6382eda489545c1))
ROM_END

// Highscreen Colani Blue Note
// Chipset: Cirrus Logic CL-GD6235-65QC-A, CL-PD6720-QC-A ETEQ ET6000 A, Appian ADI2, SMC FDC37C651 QF P, DIA UA0300-QA, Chips F82C206J
// Video: Dual Scan Color LCD (monochrome available) - RAM: 4MB on board, RAM expansion board - on board: trackball, ser, par, PS2, VGA
// mass storage: Floppy, IDE - expansion: 2xPCMCIA, expansion bus
ROM_START( bluenote )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "7500d_rev26_121593.bin", 0x00000, 0x20000, CRC(d564f855) SHA1(181e4097c3b4ca2e8e79f1732d4aef9edd5b4586))
ROM_END

// Siemens PG-750 486 EISA
// blank screen, beeps
ROM_START( pg750eisa )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD( "pg_pg750_486_eisa.bin", 0x10000, 0x10000, CRC(2e6149a9) SHA1(9fcf29a6169efa1359c7c3eff09326dd3e4001dc))
ROM_END

} // anonymous namespace


/***************************************************************************
  Game driver(s)
***************************************************************************/

//    YEAR  NAME       PARENT   COMPAT   MACHINE    INPUT  CLASS         INIT            COMPANY        FULLNAME                FLAGS
COMP( 198?, asi100b0,  ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "<unknown>",    "ASI 100B0, identified as HAM 12 TI 286 Motherboard ZERO WAIT", MACHINE_NOT_WORKING )
COMP( 1987, at,        ibm5170, 0,       ibm5162,   0,     at_state,     init_at,        "<generic>",   "PC/AT (6 MHz, MF2 Keyboard)", MACHINE_NOT_WORKING )
COMP( 1987, ataripc4,  ibm5170, 0,       neat,      0,     at_state,     init_at,        "Atari", "PC4", MACHINE_NOT_WORKING )
COMP( 1989, atariabc286,ibm5170,0,       neat,      0,     at_state,     init_at,        "Atari", "ABC-286/30", MACHINE_NOT_WORKING )
COMP( 1987, atturbo,   ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "<generic>",   "PC/AT Turbo (12 MHz, MF2 Keyboard)" , MACHINE_NOT_WORKING )
COMP( 198?, aubam12s2, ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "AUVA COMPUTER, INC.", "BAM/12-S2", MACHINE_NOT_WORKING )
COMP( 198?, bam16a0,   ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "AUVA", "VIP-M21502A BAM16-A0", MACHINE_NOT_WORKING )
COMP( 198?, bay1000c,  ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Mintek", "BAY-1000C V1.01", MACHINE_NOT_WORKING )
COMP( 199?, bi025c,    ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "<unknown>",   "BI-025C HT-12 286 (HT12/A chipset)", MACHINE_NOT_WORKING )
COMP( 1990, c286lt,    ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Commodore Business Machines",  "Laptop C286LT", MACHINE_NOT_WORKING )
COMP( 199?, cdtekg2,   ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "CDTEK", "286 mainboard with Headland G2 chipset", MACHINE_NOT_WORKING )
COMP( 1990, cl28612s,  ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Wearnes", "CL286-12/16S (CL286-12S and CL286-16S)", MACHINE_NOT_WORKING )
COMP( 198?, cmpa286,   ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "CMP enterprise CO.LTD.", "286 motherboard", MACHINE_NOT_WORKING )
COMP( 1987, comportii ,ibm5170, 0,       comportii, 0,     at_state,     init_at,        "Compaq",      "Portable II", MACHINE_NOT_WORKING )
COMP( 1987, comportiii,ibm5170, 0,       comportiii,0,     at_state,     init_at,        "Compaq",      "Portable III", MACHINE_NOT_WORKING )
COMP( 1988, comslt286, ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Compaq",      "SLT/286", MACHINE_NOT_WORKING )
COMP( 199?, csl286,    ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Commodore Business Machines",  "SL 286-16", MACHINE_NOT_WORKING )
COMP( 1988, dsys200,   ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Dell Computer Corporation",    "System 200", MACHINE_NOT_WORKING )
COMP( 1989, ec1842,    ibm5150, 0,       ec1842,    0,     at_state,     init_at,        "<unknown>",   "EC-1842", MACHINE_NOT_WORKING )
COMP( 1993, ec1849,    ibm5170, 0,       ec1842,    0,     at_state,     init_at,        "<unknown>",   "EC-1849", MACHINE_NOT_WORKING )
COMP( 198?, elanht286, ibm5170, 0,       ibm5162,   0,     at_state,     init_at,        "Leanord SA", "Elan High Tech 286", MACHINE_NOT_WORKING )
COMP( 198?, elt286b,   ibm5170, 0,       neat,      0,     at_state,     init_at,        "Chaintech", "ELT-286B-160B(E)", MACHINE_NOT_WORKING )
COMP( 198?, epsax,     ibm5170, 0,       ibm5162,   0,     at_state,     init_at,        "Epson",       "PC AX", MACHINE_NOT_WORKING )
COMP( 198?, epsax2e,   ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Epson",       "PC AX2e", MACHINE_NOT_WORKING )
COMP( 1989, euroat,    ibm5170, 0,       euroat,    0,     at_state,     init_at,        "Schneider Rundfunkwerke AG", "Euro AT", MACHINE_NOT_WORKING )
COMP( 198?, ev1806,    ibm5170, 0,       ibm5162,   0,     at_state,     init_at,        "Everex Systems", "EV-1806", MACHINE_NOT_WORKING ) // continuous beeps (RAM not detected?)
COMP( 198?, ev1815,    ibm5170, 0,       ibm5162,   0,     at_state,     init_at,        "Everex Systems", "EV-1815", MACHINE_NOT_WORKING ) // continuous beeps (RAM not detected?)
COMP( 1986, ews286,    ibm5170, 0,       ews286,    0,     at_state,     init_at,        "Ericsson",    "Ericsson WS286", MACHINE_NOT_WORKING )
COMP( 199?, headg2,    ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "<unknown>", "286 motherboards with Headland G2 chipset", MACHINE_NOT_WORKING )
COMP( 19??, ht12a,     ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "<unknown>", "unknown 286 AT clones (HT12/A chipset)", MACHINE_NOT_WORKING )
COMP( 1985, ibm5162,   ibm5170, 0,       ibm5162,   0,     at_state,     init_at,        "International Business Machines",  "PC/XT-286 5162", MACHINE_NOT_WORKING )
COMP( 1984, ibm5170,   0,       ibm5150, ibm5170,   0,     at_state,     init_at,        "International Business Machines",  "PC/AT 5170", MACHINE_NOT_WORKING )
COMP( 1985, ibm5170a,  ibm5170, 0,       ibm5170a,  0,     at_state,     init_at,        "International Business Machines",  "PC/AT 5170 8MHz", MACHINE_NOT_WORKING )
COMP( 1989, ibm2011,   ibm5170, 0,       ibmps1,    0,     at_vrom_fix_state, init_at,   "International Business Machines",  "PS/1 2011", MACHINE_NOT_WORKING )
COMP( 1989, ibm2011rd, ibm5170, 0,       ibmps1,    0,     at_vrom_fix_state, init_at,   "International Business Machines",  "PS/1 2011 (international models with ROM DOS)", MACHINE_NOT_WORKING )
COMP( 198?, icldrsm40, ibm5170, 0,       neat,      0,     at_state,     init_at,        "ICL", "DRS M40", MACHINE_NOT_WORKING )
COMP( 1985, k286i,     ibm5170, 0,       k286i,     0,     at_state,     init_at,        "Kaypro",      "286i", MACHINE_NOT_WORKING )
COMP( 199?, kma202f,   ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "<unknown>",   "KMA-202F-12R (Winbond chipset)", MACHINE_NOT_WORKING )
COMP( 19??, kt216wb5,  ibm5170, 0,       ibm5162,   0,     at_state,     init_at,        "KT Technology", "KT216WB5-HI Rev.2", MACHINE_NOT_WORKING )
COMP( 198?, lm103s,    ibm5170, 0,       ibm5162,   0,     at_state,     init_at,        "<unknown>",   "LM-103S", MACHINE_NOT_WORKING )
COMP( 198?, m250e,     ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Olivetti",    "M250E", MACHINE_NOT_WORKING )
COMP( 1987, m290,      ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Olivetti",    "M290", MACHINE_NOT_WORKING )
COMP( 198?, magb233,   ibm5170, 0,       ibm5162,   0,     at_state,     init_at,        "Magitronic Technology", "Magitronic B233", MACHINE_NOT_WORKING )
COMP( 198?, magb236,   ibm5170, 0,       ibm5162,   0,     at_state,     init_at,        "Magitronic Technology", "Magitronic B236", MACHINE_NOT_WORKING )
COMP( 19??, mat286,    ibm5170, 0,       ibm5162,   0,     at_state,     init_at,        "<unknown>",   "MAT286 Rev.D", MACHINE_NOT_WORKING )
COMP( 199?, mb1212c,   ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Biostar",     "MB-1212C", MACHINE_NOT_WORKING )
COMP( 199?, mba009,    ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "<unknown>", "HLB-286 MBA-009", MACHINE_NOT_WORKING )
COMP( 199?, micral45,  ibm5170, 0,       micral45,  0,     at_state,     init_at,        "Bull", "Micral 45", MACHINE_NOT_WORKING )
COMP( 199?, minisys2k, ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "SIIG", "MiniSys 2000", MACHINE_NOT_WORKING )
COMP( 198?, mkp286,    ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Morse", "KP-286", MACHINE_NOT_WORKING )
COMP( 1987, n8810m15,  ibm5170, 0,       n8810m15,  0,     at_state,     init_at,        "Nixdorf Computer AG", "8810 M15", MACHINE_NOT_WORKING )
COMP( 1990, n8810m16c, ibm5170, 0,       n8810m15,  0,     at_state,     init_at,        "Nixdorf Computer AG", "8810 M16 CGA version", MACHINE_NOT_WORKING )
COMP( 1990, n8810m30,  ibm5170, 0,       neat,      0,     at_state,     init_at,        "Nixdorf Computer AG", "8810 M30", MACHINE_NOT_WORKING )
COMP( 1986, n8810m55,  ibm5170, 0,       n8810m55,  0,     at_state,     init_at,        "Nixdorf Computer AG", "8810 M55", MACHINE_NOT_WORKING )
COMP( 1990, n8810m16v, ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Nixdorf Computer AG", "8810 M16 VGA version", MACHINE_NOT_WORKING )
COMP( 199?, ncr3302,   ibm5170, 0,       neat,      0,     at_state,     init_at,        "NCR", "Class 3302 Model 0110", MACHINE_NOT_WORKING )
COMP( 1986, ncrpc8,    ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "NCR",         "PC-8", MACHINE_NOT_WORKING )
COMP( 1989, neat,      ibm5170, 0,       neat,      0,     at_state,     init_at,        "<generic>",   "NEAT (12 MHz, MF2 Keyboard)", MACHINE_NOT_WORKING )
COMP( 1986, necapciv,  ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "NEC", "APC IV", MACHINE_NOT_WORKING )
//COMP( 1988, nws286,    ibm5170,  0,      ews286,    0,     at_state,     at,        "Nokia Data",  "Nokia Data WS286", MACHINE_NOT_WORKING )
COMP( 198?, o286foxii, ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Octek",       "Fox II", MACHINE_NOT_WORKING )
COMP( 1990, ocfoxm,    ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Octek", "Fox M 286", MACHINE_NOT_WORKING )
COMP( 199?, octekg2,   ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Octek", "286 motherboard with Headland G2 chipset", MACHINE_NOT_WORKING )
COMP( 198?, ocxt286,   ibm5170, 0,       ibm5170,   0,     at_state,     init_at,        "Octek", "XT-286 motherboard", MACHINE_NOT_WORKING )
COMP( 199?, olim203,   ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Olivetti", "M203 motherboard", MACHINE_NOT_WORKING )
COMP( 1988, pc30iii,   ibm5170, 0,       pc30iii,   0,     at_state,     init_at,        "Commodore Business Machines",  "PC 30-III", MACHINE_NOT_WORKING )
COMP( 1988, pc40iii,   ibm5170, 0,       pc40iii,   0,     at_state,     init_at,        "Commodore Business Machines",  "PC 40-III", MACHINE_NOT_WORKING )
COMP( 198?, pc45iii,   ibm5170, 0,       pc40iii,   0,     at_state,     init_at,        "Commodore Business Machines",  "PC 45-III", MACHINE_NOT_WORKING )
COMP( 198?, pccm205,   ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "PC-Chips", "M205", MACHINE_NOT_WORKING )
COMP( 198?, pccm209,   ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "PC-Chips", "M209", MACHINE_NOT_WORKING )
COMP( 198?, pccm216,   ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "PC-Chips", "M216", MACHINE_NOT_WORKING )
COMP( 1986, pcd2,      ibm5170, 0,       ibm5170,   0,     at_state,     init_at,        "Siemens",     "PCD-2", MACHINE_NOT_WORKING )
COMP( 1991, pcd204,    ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Philips",     "PCD204 (PCD200 series)", MACHINE_NOT_WORKING )
COMP( 198?, pcd2m,     ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Siemens",     "PCD-2M", MACHINE_NOT_WORKING )
COMP( 198?, peas286,   ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Peacock Computer", "S-286 Rev A", MACHINE_NOT_WORKING )
COMP( 1990, profpc33,  ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Profex", "PC 33", MACHINE_NOT_WORKING )
COMP( 198?, prolite286,ibm5170, 0,       neat,      0,     at_state,     init_at,        "CAF", "Prolite 286/16", MACHINE_NOT_WORKING )
COMP( 198?, pwb7270e,  ibm5170, 0,       neat,      0,     at_state,     init_at,        "Advanced Logic Research", "PWB 7270 REV E", MACHINE_NOT_WORKING )
COMP( 199?, samdm286,  ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Samsung", "Deskmaster 286-12", MACHINE_NOT_WORKING )
COMP( 199?, sarcpc,    ibm5170, 0,       ibm5162,   0,     at_state,     init_at,        "<unknown>",   "80286 Standard System (SARC RC2015 chipset)", MACHINE_NOT_WORKING )
COMP( 198?, snomi286,  ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Snobol", "Mini 286", MACHINE_NOT_WORKING )
COMP( 198?, suntac303, ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "<unknown>", "286 motherboards with Suntac ST62C303-A chipset", MACHINE_NOT_WORKING )
COMP( 199?, suntac6,   ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "<unknown>", "286 motherboards with 6-chip SUNTAC chipset", MACHINE_NOT_WORKING )
COMP( 198?, td60c,     ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "<unknown>",   "TD60C", MACHINE_NOT_WORKING )
COMP( 19??, toptek286, ibm5170, 0,       ibm5162,   0,     at_state,     init_at,        "Toptek Micro Computer", "286 Turbo", MACHINE_NOT_WORKING )
COMP( 198?, towerat2xx,ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Schneider Rundfunkwerke AG", "Tower AT 201, 202, 220, 240 and 260 (286,EGA)", MACHINE_NOT_WORKING )
COMP( 198?, olyport40, ibm5170, 0,       olyport40, 0,     at_state,     init_at,        "AEG Olympia", "Olyport 40-21", MACHINE_NOT_WORKING )
COMP( 199?, twinnet,   ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Twinhead", "Netstation PC", MACHINE_NOT_WORKING )
COMP( 198?, u3911v3,   ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Uniron", "U3911-V3", MACHINE_NOT_WORKING )
COMP( 198?, v286c,     ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Victor", "V286C", MACHINE_NOT_WORKING )
COMP( 198?, vlsi5,     ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "<unknown>", "286 motherboards with 5-chip VLSI chipset", MACHINE_NOT_WORKING )
COMP( 1981, wpc250,    ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Wang Laboratories, Inc.", "PC-250/16", MACHINE_NOT_WORKING )
COMP( 198?, wy220001,  ibm5170, 0,       ibm5162,   0,     at_state,     init_at,        "Wyse", "WYSEpc 286", MACHINE_NOT_WORKING )
COMP( 1989, xb42639,   ibm5170, 0,       xb42639,   0,     at_state,     init_at,        "Apricot",     "Apricot XEN-S (Venus I Motherboard 286)" , MACHINE_NOT_WORKING )
COMP( 1990, xb42639a,  ibm5170, 0,       xb42639,   0,     at_state,     init_at,        "Apricot",     "Apricot XEN-S (Venus II Motherboard 286)" , MACHINE_NOT_WORKING )
COMP( 198?, zdz248,    ibm5170, 0,       atturbo,   0,     at_state,     init_at,        "Zenith Data Systems", "Z-248", MACHINE_NOT_WORKING )
COMP( 199?, 386sxvhcom,ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "<unknown>",   "80386SX-VH-COM", MACHINE_NOT_WORKING )
COMP( 199?, 3siud,     ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "<unknown>",   "3SIUD-1.1", MACHINE_NOT_WORKING )
COMP( 199?, alaleolx,  ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Alaris RYC", "LEOPARD LX", MACHINE_NOT_WORKING )
COMP( 199?, alim1217,  ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "<unknown>",   "386sx motherboards using the ALi M1217 chipset", MACHINE_NOT_WORKING )
COMP( 199?, anch386s,  ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "ANIX",        "CH-386S-16/20/25G", MACHINE_NOT_WORKING )
COMP( 199?, asc486slc, ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Computechnik", "ASC486SLC", MACHINE_NOT_WORKING )
COMP( 1988, at386sx,   ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "<generic>",   "PC/AT 386SX (16 MHz, MF2 Keyboard)", MACHINE_NOT_WORKING )
COMP( 1990, c386sx16,  ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Commodore Business Machines", "386SX-16", MACHINE_NOT_WORKING )
COMP( 1990, c386sx25,  ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Commodore Business Machines", "386SX-25", MACHINE_NOT_WORKING )
COMP( 1991, c386sxlt,  ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Commodore Business Machines",  "Laptop C386SX-LT", MACHINE_NOT_WORKING )
COMP( 1988, ct386sx,   ibm5170, 0,       ct386sx,   0,     at_state,     init_at,        "<generic>",   "NEAT 386SX (16 MHz, MF2 Keyboard)", MACHINE_NOT_WORKING )
COMP( 1993, cxsxd,     ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "CX Technology", "CX SXD", MACHINE_NOT_WORKING )
COMP( 199?, dfi386sx,  ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Diamond Flower International", "386SX-16/20CN Rev 1.0", MACHINE_NOT_WORKING )
COMP( 199?, dvbslan,   ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Datavan", "Book-Size LAN station", MACHINE_NOT_WORKING )
COMP( 199?, ecs8517,   ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Elitegroup", "ECS 8517 v3.3", MACHINE_NOT_WORKING )
COMP( 199?, elt386sx,  ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Elitegroup",  "ELT-386SX-160BE", MACHINE_NOT_WORKING )
COMP( 198?, eltp9,     ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Chaintech", "ELT-P9 / most likely ELT-386SX-160D", MACHINE_NOT_WORKING )
COMP( 198?, epsax3,    ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Epson",       "PC AX3", MACHINE_NOT_WORKING )
COMP( 19??, ht18c,     ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "<unknown>",   "unknown 386sx AT clones (HT18/C chipset)", MACHINE_NOT_WORKING )
COMP( 199?, ibm2121,   ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "International Business Machines",  "PS/1 2121", MACHINE_NOT_WORKING )
COMP( 199?, ibm2121rd, ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "International Business Machines",  "PS/1 2121 (international models with ROM DOS)", MACHINE_NOT_WORKING )
COMP( 199?, ibm2123,   ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "International Business Machines",  "PS/1 2123", MACHINE_NOT_WORKING )
COMP( 199?, ilm396b,   ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "ILON USA, INC.", "M-396B", MACHINE_NOT_WORKING )
COMP( 198?, ktx20t02,  ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Quadtel",     "QTC-SXM KT X20T02/HI Rev.3", MACHINE_NOT_WORKING )
COMP( 199?, mbc18nb,   ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Sanyo",       "MBC-18NB", MACHINE_NOT_WORKING )
COMP( 1992, mbc28,     ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Sanyo",       "MBC-28", MACHINE_NOT_WORKING ) // Complains about missing mouse hardware
COMP( 199?, mmbo4088,  ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "<unknown>",   "486MMBO4088 (TI TX486SLC/E)", MACHINE_NOT_WORKING )
COMP( 199?, mokp386sx, ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Morse",       "KP 386SX V2.21", MACHINE_NOT_WORKING )
COMP( 199?, ocpanii,   ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Octek",       "Panther II", MACHINE_NOT_WORKING )
COMP( 199?, op82c283,  ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "<unknown>",   "386sx motherboards using the OPTi 82C283 chipset", MACHINE_NOT_WORKING )
COMP( 199?, op82c291,  ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "<unknown>",   "386sx motherboards using the OPTi 82C291 chipset", MACHINE_NOT_WORKING )
COMP( 199?, p386sx25pw,ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Prolink",  "P386SX-25PW VER:2.00", MACHINE_NOT_WORKING )
COMP( 198?, pc50ii,    ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Commodore Business Machines",  "PC 50-II", MACHINE_NOT_WORKING )
COMP( 198?, pcb303,    ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Packard Bell", "PCB-303 Rev.01", MACHINE_NOT_WORKING )
COMP( 199?, pccm396f,  ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "PC-Chips",    "M396F", MACHINE_NOT_WORKING )
COMP( 199?, pcd3nsl,   ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Siemens-Nixdorf", "PCD-3Nsl Notebook Computer", MACHINE_NOT_WORKING )
COMP( 199?, pcd3nsx,   ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Siemens-Nixdorf", "PCD-3Nsx Notebook Computer", MACHINE_NOT_WORKING )
COMP( 199?, php3239,   ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Philips",     "P3239", MACHINE_NOT_WORKING )
COMP( 199?, ppm3333p,  ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "DTK Computer", "PPM-3333P", MACHINE_NOT_WORKING )
COMP( 199?, pt319a,    ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Pine",        "PT-319A", MACHINE_NOT_WORKING )
COMP( 199?, scamp386sx,ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "<unknown>",   "386sx motherboards using the SCAMPSX chipset", MACHINE_NOT_WORKING )
COMP( 199?, scsxaio,   ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Peacock",     "386sx Ver. 2.0 motherboard SCsxAIO", MACHINE_NOT_WORKING )
COMP( 199?, sh386sx20, ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Shuttle", "386SX REV 2.0A", MACHINE_NOT_WORKING )
COMP( 1991, t2000sx,   ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Toshiba",     "T2000SX", MACHINE_NOT_WORKING )
COMP( 199?, td70a,     ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "<unknown>",   "TD70A and TD70AN", MACHINE_NOT_WORKING )
COMP( 199?, td70n,     ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "<unknown>",   "TD70N", MACHINE_NOT_WORKING )
COMP( 199?, tot3200sxc,ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Toshiba",     "T3200SXC", MACHINE_NOT_WORKING )
COMP( 198?, tower386sx,ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Schneider Rundfunkwerke AG", "386SX System 40 (VGA)", MACHINE_NOT_WORKING )
COMP( 1992, walk386sx, ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "Triumph-Adler", "Walkstation 386 SX", MACHINE_NOT_WORKING ) // screen remains blank
COMP( 198?, zeos386sx, ibm5170, 0,       at386sx,   0,     at_state,     init_at,        "ZEOS", "386 SX-16", MACHINE_NOT_WORKING )
COMP( 199?, 386sc,     ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>",   "386 SC Rev A2", MACHINE_NOT_WORKING )
COMP( 199?, 386sc2c,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>",   "386 motherboard using the Symphony chipset", MACHINE_NOT_WORKING )
COMP( 199?, 386schg,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>",   "386-SC-HG", MACHINE_NOT_WORKING )
COMP( 199?, 4nd04a,    ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>",   "386-4N-D04A (UMC chipset)", MACHINE_NOT_WORKING )
COMP( 199?, al486vd,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "Daewoo",      "AL486V-D Rev:1.1", MACHINE_NOT_WORKING )
COMP( 199?, alacou,    ibm5170, 0,       at386,     0,     at_state,     init_at,        "Alaris",      "Cougar", MACHINE_NOT_WORKING )
COMP( 199?, alim1419,  ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>",   "386 motherboards using the ALi M1419 chipset", MACHINE_NOT_WORKING )
COMP( 199?, alim1429,  ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>",   "386 motherboards using the ALi M1429 A1 and M1431 A2 chipset", MACHINE_NOT_WORKING )
COMP( 199?, amibaby,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "AMI",         "Mark V Baby Screamer", MACHINE_NOT_WORKING )
COMP( 1990, aplanst,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "Apricot",     "Apricot LANstation (Krypton Motherboard)", MACHINE_NOT_WORKING )
COMP( 1990, aplannb,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "Apricot",     "Apricot LANstation (Novell Remote Boot)", MACHINE_NOT_WORKING )
COMP( 1992, aplscar,   ibm5170, 0,       at486l,    0,     at_state,     init_at,        "Apricot",     "Apricot LS Pro (Caracal Motherboard)", MACHINE_NOT_WORKING )
COMP( 1987, apxeni,    ibm5170, 0,       at386,     0,     at_state,     init_at,        "Apricot",     "Apricot XEN-i 386 (Leopard Motherboard)" , MACHINE_NOT_WORKING )
COMP( 1988, at386,     ibm5170, 0,       at386,     0,     at_state,     init_at,        "<generic>",   "PC/AT 386 (12 MHz, MF2 Keyboard)", MACHINE_NOT_WORKING )
COMP( 1988, ataripc5,  ibm5170, 0,       at386,     0,     at_state,     init_at,        "Atari", "PC5", MACHINE_NOT_WORKING )
COMP( 199?, chn333sc,  ibm5170, 0,       at386,     0,     at_state,     init_at,        "Chaintech",   "333SC", MACHINE_NOT_WORKING )
COMP( 199?, comt386,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "Commodore Business Machines",  "Tower 386", MACHINE_NOT_WORKING )
COMP( 198?, cs8230,    ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>",   "386 motherboards using the CS8230 chipset", MACHINE_NOT_WORKING )
COMP( 199?, ctcc386mx, ibm5170, 0,       at386,     0,     at_state,     init_at,        "Caching Tech Corporation", "C386MX", MACHINE_NOT_WORKING )
COMP( 198?, dfi386,    ibm5170, 0,       at386,     0,     at_state,     init_at,        "DFI", "386-20.REV0", MACHINE_NOT_WORKING )
COMP( 199?, dt386,     ibm5170, 0,       at386,     0,     at_state,     init_at,        "Commodore Business Machines", "DT386", MACHINE_NOT_WORKING )
COMP( 1988, ecs38632,  ibm5170, 0,       at386,     0,     at_state,     init_at,        "Elitegroup Computer Systems",      "ECS-386/32", MACHINE_NOT_WORKING )
COMP( 1988, ecs386a,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "Elitegroup Computer Systems",      "ECS-386A", MACHINE_NOT_WORKING )
COMP( 199?, ecsfx3000, ibm5170, 0,       at386,     0,     at_state,     init_at,        "Elitegroup Computer Systems", "FX-3000 REV1.0", MACHINE_NOT_WORKING )
COMP( 1992, ecsum386,  ibm5170, 0,       at386,     0,     at_state,     init_at,        "Elitegroup Computer Systems",      "UM386 (Rev 1.1)", MACHINE_NOT_WORKING )
COMP( 199?, fic4386vchd,ibm5170,0,       at486,     0,     at_state,     init_at,        "First International Computer", "4386-VC-HD", MACHINE_NOT_WORKING )
COMP( 199?, frx386c,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "Micro-Express Inc.", "Forex 386 Cache", MACHINE_NOT_WORKING )
COMP( 199?, frxc402,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>", "386 motherboards with a FOREX FRX46C402/FRX36C300/SIS85C206 chipset", MACHINE_NOT_WORKING )
COMP( 1991, fu340,     ibm5170, 0,       at386,     0,     at_state,     init_at,        "Abit",        "FU340", MACHINE_NOT_WORKING )
COMP( 199?, ges9051n,  ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>",   "GES 9051N-386C VER -0.01", MACHINE_NOT_WORKING )
COMP( 198?, gs611606a, ibm5170, 0,       at386,     0,     at_state,     init_at,        "Goldstar",    "GOLDSTAR P/N 611-606A Rev 1.0A", MACHINE_NOT_WORKING )
COMP( 198?, hot304,    ibm5170, 0,       at386,     0,     at_state,     init_at,        "Shuttle Computer International", "HOT-304", MACHINE_NOT_WORKING )
COMP( 198?, hot307h,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "Shuttle Computer International", "HOT-307H", MACHINE_NOT_WORKING )
COMP( 199?, isa386u30, ibm5170, 0,       at386,     0,     at_state,     init_at,        "Asus",        "ISA-386U30 REV.2.2", MACHINE_NOT_WORKING )
COMP( 1989, isa386c,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "Asus",        "ISA-386C", MACHINE_NOT_WORKING )
COMP( 199?, kma300g,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "Arche Technologies Inc.", "KMA-300G-25", MACHINE_NOT_WORKING )
COMP( 199?, mb133340,  ibm5170, 0,       at386,     0,     at_state,     init_at,        "Biostar",     "MB-1340UCQ-B", MACHINE_NOT_WORKING )
COMP( 199?, mba032q,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "DTK", "MBA-032Q TK83305-4N-D-03", MACHINE_NOT_WORKING )
COMP( 199?, megapcpla, megapc,  0,       megapcpla, 0,     at_vrom_fix_state, init_megapcpla, "Amstrad plc", "MegaPC Plus (WINBUS chipset)", MACHINE_NOT_WORKING ) // TODO: move to pc/megapc.cpp
COMP( 199?, mokp386,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "Morse", "KP920121523 V2.20", MACHINE_NOT_WORKING )
COMP( 199?, mom3v3,    ibm5170, 0,       at386,     0,     at_state,     init_at,        "Morse", "M3 V3.0", MACHINE_NOT_WORKING )
COMP( 199?, mx83c305,  ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>", "386 motherboards using the MX83C305(A)(FC)/MX83C05(A)(FC) chipset", MACHINE_NOT_WORKING )
COMP( 1989, n8810m20,  ibm5170, 0,       at386,     0,     at_state,     init_at,        "Nixdorf", "8810/20", MACHINE_NOT_WORKING )
COMP( 1992, ocjagii,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "Octek",       "Jaguar II", MACHINE_NOT_WORKING )
COMP( 1992, ocjagv,    ibm5170, 0,       at386,     0,     at_state,     init_at,        "Octek",       "Jaguar V v1.4", MACHINE_NOT_WORKING )
COMP( 199?, op82c381,  ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>",   "386 motherboards using the OPTi 82C381 chipset", MACHINE_NOT_WORKING )
COMP( 199?, op82c391,  ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>",   "386 motherboards using the OPTi 82C391 chipset", MACHINE_NOT_WORKING )
COMP( 199?, opti495slc,ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>", "386 motherboards using a OPTi 82C495SLC chipset", MACHINE_NOT_WORKING )
COMP( 199?, opti495xlc,ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>", "386 motherboards using a OPTi 82C495XLC chipset", MACHINE_NOT_WORKING )
COMP( 1987, t4000,     ibm5170, 0,       at386,     0,     at_state,     init_at,        "Tandy Radio Shack", "Tandy 4000", MACHINE_NOT_WORKING )
COMP( 199?, op386wb,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "OPTi", "OPTi 386WB VER.1.0", MACHINE_NOT_WORKING )
COMP( 199?, p386dx40,  ibm5170, 0,       at386,     0,     at_state,     init_at,        "Peacock", "P386DX-40", MACHINE_NOT_WORKING )
COMP( 1989, pc2386,    ibm5170, 0,       at386l,    0,     at_state,     init_at,        "Amstrad plc", "PC2386", MACHINE_NOT_WORKING )
COMP( 198?, pc60iii,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "Commodore Business Machines",  "PC 60-III", MACHINE_NOT_WORKING )
COMP( 199?, pccm317,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "PC-Chips", "M317", MACHINE_NOT_WORKING )
COMP( 199?, pccm321,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "PC-Chips", "M321", MACHINE_NOT_WORKING )
COMP( 199?, pccm326,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "PC-Chips", "M326", MACHINE_NOT_WORKING )
COMP( 198?, pem2530,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "DTK", "PEM 2539", MACHINE_NOT_WORKING )
COMP( 199?, pg750,     ibm5170, 0,       pg750,     0,     at_state,     init_at,        "Siemens", "PG 750", MACHINE_NOT_WORKING )
COMP( 199?, pt581392,  ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>",   "386 motherboard using the Forex FRX46C402 + FRX46C411 + SiS 85C206 chipset", MACHINE_NOT_WORKING )
COMP( 199?, qdu386dx,  ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>", "QD-U386DX VER 1.0", MACHINE_NOT_WORKING )
COMP( 1988, qi600,     ibm5170, 0,       at386,     0,     at_state,     init_at,        "Apricot",     "Apricot Qi 600 (Neptune Motherboard)", MACHINE_NOT_WORKING )
COMP( 199?, sisrabb,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>",   "386 motherboards using the SiS Rabbit chipset", MACHINE_NOT_WORKING )
COMP( 199?, sm38640f,  ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>",   "SM 386-40F", MACHINE_NOT_WORKING )
COMP( 19??, smih0107,  ibm5170, 0,       at386,     0,     at_state,     init_at,        "Forex Computer Company", "unknown 386 AT clone with Forex chipset", MACHINE_NOT_WORKING )
COMP( 199?, sy012,     ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>",   "SY-012 16/25 386MB VER: 5.2", MACHINE_NOT_WORKING )
COMP( 199?, sy019hi,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "Soyo", "SY-019H and SY-019I", MACHINE_NOT_WORKING )
COMP( 199?, sybaby386, ibm5170, 0,       at386,     0,     at_state,     init_at,        "Soyo", "Baby AT 386", MACHINE_NOT_WORKING )
COMP( 199?, tam25p2,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "AUVA", "TAM/25-P2 M31720P", MACHINE_NOT_WORKING )
COMP( 199?, tam3340ma0,ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>",   "TAM/33/40-MA0", MACHINE_NOT_WORKING )
COMP( 199?, um82c481af,ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>",   "386 motherboards using the UMC UM82C481AF chipset", MACHINE_NOT_WORKING )
COMP( 199?, um82c491f, ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>",   "386 motherboard using the UMC UM82C491F chipset", MACHINE_NOT_WORKING )
COMP( 199?, um82c493f, ibm5170, 0,       at386,     0,     at_state,     init_at,        "<unknown>",   "386 motherboards using the UMC UM82C491F + UM82C493F chipset or BIOTEQ equivalents", MACHINE_NOT_WORKING )
COMP( 199?, uni386w,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "UNICHIP", "386W 367C REV 1.0", MACHINE_NOT_WORKING )
COMP( 1992, walk386dx, ibm5170, 0,       at386,     0,     at_state,     init_at,        "Triumph-Adler", "Walkstation 386DX", MACHINE_NOT_WORKING ) // screen remains blank
COMP( 1988, xb42663,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "Apricot",     "Apricot Qi 300 (Rev D,E & F Motherboard)", MACHINE_NOT_WORKING )
COMP( 1989, xb42664,   ibm5170, 0,       at386,     0,     at_state,     init_at,        "Apricot",     "Apricot XEN-S (Venus I Motherboard 386)" , MACHINE_NOT_WORKING )
COMP( 1990, xb42664a,  ibm5170, 0,       at386,     0,     at_state,     init_at,        "Apricot",     "Apricot XEN-S (Venus II Motherboard 386)" , MACHINE_NOT_WORKING )
COMP( 199?, 486apio,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "EFA",   "486 APIO", MACHINE_NOT_WORKING )
COMP( 199?, 486ccv,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "Diamond Flower, Inc. (DFI)", "486-CCV", MACHINE_NOT_WORKING )
COMP( 199?, 486igb21,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "<unknown>", "486IG-B-2-1", MACHINE_NOT_WORKING )
COMP( 199?, 486wb6a3,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "Silicon Valley Computer, Inc.", "486WB6A3.B1", MACHINE_NOT_WORKING )
COMP( 199?, 4dmshl3g,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "EFA",   "4DMS HL3G-L4-VI", MACHINE_NOT_WORKING )
COMP( 199?, 4dmuhl3s,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "EFA",   "4DMU HL3S", MACHINE_NOT_WORKING )
COMP( 1992, a433cc,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "J-Bond",      "A433C-C/A450C-C", MACHINE_NOT_WORKING )
COMP( 1994, a486ap4,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Asus",        "PVI-486AP4", MACHINE_NOT_WORKING )
COMP( 199?, a486isa,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Asus",        "ISA-486", MACHINE_NOT_WORKING )
COMP( 199?, a486sio,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Asus",        "ISA-486SIO rev. 1.2", MACHINE_NOT_WORKING )
COMP( 1994, a486sp3g,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "Asus",        "PCI/I-486SP3G", MACHINE_NOT_WORKING )
COMP( 199?, a486sv2,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Asus",        "ISA-486SV2", MACHINE_NOT_WORKING )
COMP( 1994, a486sv2g,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "Asus",        "VL/I-486SV2G", MACHINE_NOT_WORKING )
COMP( 1994, a486sv1,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Asus",        "VL/EISA-486SV1", MACHINE_NOT_WORKING )
COMP( 199?, abae4,     ibm5170, 0,       at486,     0,     at_state,     init_at,        "Abit", "486 EISA-AE4", MACHINE_NOT_WORKING )
COMP( 199?, abah4,     ibm5170, 0,       at486,     0,     at_state,     init_at,        "Abit", "AB-AH4", MACHINE_NOT_WORKING )
COMP( 199?, abav4,     ibm5170, 0,       at486,     0,     at_state,     init_at,        "Abit", "AB-AV4", MACHINE_NOT_WORKING )
COMP( 199?, abax4,     ibm5170, 0,       at486,     0,     at_state,     init_at,        "Abit", "AB-AX4", MACHINE_NOT_WORKING )
COMP( 199?, abpb4,     ibm5170, 0,       at486,     0,     at_state,     init_at,        "Abit", "AB-PB4", MACHINE_NOT_WORKING )
COMP( 199?, abpw4,     ibm5170, 0,       at486,     0,     at_state,     init_at,        "Abit", "AB-PW4", MACHINE_NOT_WORKING )
COMP( 199?, alator2,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Alaris",      "Tornado 2", MACHINE_NOT_WORKING )
COMP( 199?, alim1489,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "<unknown>", "486 motherboards using the ALi 1487/1489 chipset", MACHINE_NOT_WORKING )
COMP( 199?, amient2,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "AMI",         "EISA Enterprise-II", MACHINE_NOT_WORKING )
COMP( 199?, amient3,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "AMI",         "EISA Enterprise-III", MACHINE_NOT_WORKING )
COMP( 199?, amient4,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "AMI",         "EISA Enterprise-IV", MACHINE_NOT_WORKING )
COMP( 199?, amisvpci2, ibm5170, 0,       at486,     0,     at_state,     init_at,        "AMI",         "Super Voyager PCI-II", MACHINE_NOT_WORKING )
COMP( 199?, amisvvlb,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "AMI",         "Super Voyager VLB", MACHINE_NOT_WORKING )
COMP( 199?, amisvvlb2, ibm5170, 0,       at486,     0,     at_state,     init_at,        "AMI",         "Super Voyager VLB-II", MACHINE_NOT_WORKING )
COMP( 199?, amisvvlb3, ibm5170, 0,       at486,     0,     at_state,     init_at,        "AMI",         "Super Voyager VLB-III", MACHINE_NOT_WORKING )
COMP( 199?, aovi15g,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Aopen", "VI15G", MACHINE_NOT_WORKING )
COMP( 1992, aplsbon,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Apricot",     "Apricot LS Pro (Bonsai Motherboard)", MACHINE_NOT_WORKING )
COMP( 1991, aprfte,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "Apricot",     "Apricot FT//ex 486 (J3 Motherboard)", MACHINE_NOT_WORKING )
COMP( 1992, aprpand,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Apricot",     "Apricot FTs (Panther Rev F 1.02.26)", MACHINE_NOT_WORKING )
COMP( 1989, apvxft,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "Apricot",     "Apricot VX FT server", MACHINE_NOT_WORKING )
COMP( 1993, apxena1,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Apricot",     "Apricot XEN PC (A1 Motherboard)", MACHINE_NOT_WORKING )
COMP( 1991, apxenls3,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "Apricot",     "Apricot XEN-LS (Venus IV Motherboard)", MACHINE_NOT_WORKING )
COMP( 1993, apxenp2,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Apricot",     "Apricot XEN PC (P2 Motherboard)", MACHINE_NOT_WORKING )
COMP( 1993, apxlsam,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Apricot",     "Apricot XEN-LS II (Samurai Motherboard)", MACHINE_NOT_WORKING )
COMP( 199?, ar4glx3,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Addtech Research", "4GLX3 Green-B 4GPV3.1", MACHINE_NOT_WORKING )
COMP( 199?, as496,     ibm5170, 0,       at486,     0,     at_state,     init_at,        "Arstoria",    "AS496", MACHINE_NOT_WORKING )
COMP( 1990, at486,     ibm5170, 0,       at486,     0,     at_state,     init_at,        "<generic>",   "PC/AT 486 (25 MHz, MF2 Keyboard)", MACHINE_NOT_WORKING )
COMP( 199?, atc1415,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "A-Trend", "ATC-1415", MACHINE_NOT_WORKING )
COMP( 199?, bluenote,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "Highscreen",  "Colani Blue Note", MACHINE_NOT_WORKING )
COMP( 199?, ch48633c,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "Chicony",   "CH-486-33C", MACHINE_NOT_WORKING )
COMP( 199?, ch491e,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "Chicony",   "CH-491E", MACHINE_NOT_WORKING )
COMP( 199?, ch4slez1,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "Chaintech", "486SLE M106 4SLE-Z1", MACHINE_NOT_WORKING )
COMP( 199?, comt486,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Commodore Business Machines",  "Tower 486", MACHINE_NOT_WORKING )
COMP( 199?, dt486,     ibm5170, 0,       at486,     0,     at_state,     init_at,        "Commodore Business Machines", "DT486", MACHINE_NOT_WORKING )
COMP( 199?, ec4913,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "Elitegroup", "UC4913 REV:1.1", MACHINE_NOT_WORKING )
COMP( 199?, ec4915aio, ibm5170, 0,       at486,     0,     at_state,     init_at,        "Elitegroup", "UC4915 A AIO", MACHINE_NOT_WORKING )
COMP( 199?, ed486vl3h, ibm5170, 0,       at486,     0,     at_state,     init_at,        "Edom", "486VL3H", MACHINE_NOT_WORKING )
COMP( 199?, edmv035f,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "Edom", "MV035F", MACHINE_NOT_WORKING )
COMP( 199?, exp4044,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "ExpertChip", "EXP4044", MACHINE_NOT_WORKING )
COMP( 199?, f4dxluc4,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "ADI", "F4DXL-UC4", MACHINE_NOT_WORKING )
COMP( 199?, fic486gvt, ibm5170, 0,       at486,     0,     at_state,     init_at,        "First International Computer", "486-GVT", MACHINE_NOT_WORKING )
COMP( 199?, fic4386vcv,ibm5170, 0,       at486,     0,     at_state,     init_at,        "First International Computer", "4386-VC-V", MACHINE_NOT_WORKING )
COMP( 199?, fic486kvd, ibm5170, 0,       at486,     0,     at_state,     init_at,        "First International Computer", "486 KVD", MACHINE_NOT_WORKING )
COMP( 199?, fic486vchd,ibm5170, 0,       at486,     0,     at_state,     init_at,        "First International Computer", "486-VC-HD", MACHINE_NOT_WORKING )
COMP( 199?, ficeli6ii, ibm5170, 0,       at486,     0,     at_state,     init_at,        "First International Computer", "FIC ELI6-II", MACHINE_NOT_WORKING )
COMP( 1994, ficgiovt2, ibm5170, 0,       at486,     0,     at_state,     init_at,        "First International Computer", "486-GIO-VT2", MACHINE_NOT_WORKING )
COMP( 1995, ficpio2,   ibm5170, 0,       ficpio2,   0,     at_state,     init_atpci,     "First International Computer", "486-PIO-2", MACHINE_NOT_WORKING )
COMP( 1994, ficvipio,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "First International Computer", "486-VIP-IO", MACHINE_NOT_WORKING )
COMP( 199?, ficvipio2, ibm5170, 0,       at486,     0,     at_state,     init_at,        "First International Computer", "486-VIP-IO2", MACHINE_NOT_WORKING )
COMP( 1991, ftsserv,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Apricot",     "Apricot FTs (Scorpion)", MACHINE_NOT_WORKING )
COMP( 199?, ga486am,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Gigabyte",    "GA-486AM/S", MACHINE_NOT_WORKING )
COMP( 199?, ga486vf,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Gigabyte",    "GA-486VF", MACHINE_NOT_WORKING )
COMP( 199?, ga486vs,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Gigabyte",    "GA-486VS", MACHINE_NOT_WORKING )
COMP( 199?, gc10a,     ibm5170, 0,       at486,     0,     at_state,     init_at,        "Eagle", "EAGLEN486 GC10A", MACHINE_NOT_WORKING )
COMP( 199?, gete486vl, ibm5170, 0,       at486,     0,     at_state,     init_at,        "GENOA",       "TurboExpress 486 VL", MACHINE_NOT_WORKING )
COMP( 199?, gmb486sg,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "Gemlight", "GMB-486SG v2.2", MACHINE_NOT_WORKING )
COMP( 199?, gmb486unp, ibm5170, 0,       at486,     0,     at_state,     init_at,        "Gemlight", "GMB-486UNP v2.1", MACHINE_NOT_WORKING )
COMP( 199?, hot409,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "Shuttle Computer International", "HOT-409", MACHINE_NOT_WORKING )
COMP( 199?, hot419,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "Shuttle Computer International", "HOT-419", MACHINE_NOT_WORKING )
COMP( 199?, hot433,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "Shuttle Computer International", "HOT-433", MACHINE_NOT_WORKING )
COMP( 199?, ibm2133,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "International Business Machines",  "PS/1 2133", MACHINE_NOT_WORKING )
COMP( 199?, jwj403tg,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "Jetway", "J-403TG", MACHINE_NOT_WORKING )
COMP( 1993, lion3500,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "Lion",        "3500", MACHINE_NOT_WORKING )
COMP( 199?, lsucm486v30,ibm5170,0,       at486,     0,     at_state,     init_at,        "Lucky Star", "UCM-486V30", MACHINE_NOT_WORKING )
COMP( 199?, mb1433aeap,ibm5170, 0,       at486,     0,     at_state,     init_at,        "Biostar",     "MB-1433/50 AEA-P - V:1", MACHINE_NOT_WORKING )
COMP( 199?, mb1433ucv, ibm5170, 0,       at486,     0,     at_state,     init_at,        "Biostar",     "MB-1433UCV", MACHINE_NOT_WORKING )
COMP( 199?, mb1433uiv, ibm5170, 0,       at486,     0,     at_state,     init_at,        "Biostar",     "MB-1433UIV", MACHINE_NOT_WORKING )
COMP( 199?, mb4d33,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "Aquarius System (ASI)", "MB-4D33/50NR", MACHINE_NOT_WORKING )
COMP( 199?, mb8433uud, ibm5170, 0,       at486,     0,     at_state,     init_at,        "Biostar",     "MB8433-UUD-A", MACHINE_NOT_WORKING ) // boots to Award BootBlock BIOS
COMP( 199?, mba029,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "Mitac", "MBA-029", MACHINE_NOT_WORKING )
COMP( 199?, md4duvc,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Aquarius System (ASI)", "MD-4DUV VER:2.1", MACHINE_NOT_WORKING )
COMP( 199?, mijx30gp,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "Micronics",   "JX30GP, Motherboard P/N: 09-00189-10 REV B1", MACHINE_NOT_WORKING )
COMP( 199?, ms4125,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "MSI",         "MS-4125", MACHINE_NOT_WORKING )
COMP( 199?, ms4132,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "MSI",         "MS-4132 G VER:1", MACHINE_NOT_WORKING )
COMP( 199?, ms4134,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "MSI",         "MS-4134", MACHINE_NOT_WORKING )
COMP( 199?, ms4138,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "MSI",         "MS-4138", MACHINE_NOT_WORKING )
COMP( 199?, ms4145,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "MSI",         "MS-4145", MACHINE_NOT_WORKING )
COMP( 199?, nat48pv,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "<unknown>", "NAT48PV-1.00 VL", MACHINE_NOT_WORKING )
COMP( 199?, ncr3433,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "NCR", "Class 3433", MACHINE_NOT_WORKING )
COMP( 199?, ochawk,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "Octek",       "Hawk", MACHINE_NOT_WORKING )
COMP( 199?, ochipcom,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "Octek",       "Hippo COM", MACHINE_NOT_WORKING )
COMP( 1994, ochipdca2, ibm5170, 0,       at486,     0,     at_state,     init_at,        "Octek",       "Hippo DCA2", MACHINE_NOT_WORKING )
COMP( 199?, ochipvlp,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "Octek",       "Hippo VL+", MACHINE_NOT_WORKING )
COMP( 199?, op82c392,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "<unknown>", "486 motherboards using the OPTi OPTi 82C392, 82C493 chipset", MACHINE_NOT_WORKING )
COMP( 199?, pc70iii,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Commodore Business Machines",  "PC 70-III", MACHINE_NOT_WORKING )
COMP( 199?, pc9486,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "Amstrad",     "PC9486", MACHINE_NOT_WORKING )
COMP( 199?, pccm912,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "PC-Chips", "M912", MACHINE_NOT_WORKING )
COMP( 199?, pccm915i,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "PC-Chips", "M915i", MACHINE_NOT_WORKING )
COMP( 199?, pccm919,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "PC-Chips", "M919", MACHINE_NOT_WORKING )
COMP( 1993, pcd4nd,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "Siemens-Nixdorf", "PCD-4ND", MACHINE_NOT_WORKING )
COMP( 1995, pcd4nl,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "Siemens-Nixdorf", "PCD-4NL", MACHINE_NOT_WORKING )
COMP( 199?, pcd4x,     ibm5170, 0,       at486,     0,     at_state,     init_at,        "Siemens-Nixdorf", "PCD-4H, PCD-4M", MACHINE_NOT_WORKING )
COMP( 199?, pci48af,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "TMC Research Corporation", "PCI48AF", MACHINE_NOT_WORKING )
COMP( 199?, pck486dx,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "Peacock",  "PCK 486 DX", MACHINE_NOT_WORKING )
COMP( 199?, pg750eisa, ibm5170, 0,       at486,     0,     at_state,     init_at,        "Siemens", "PG-750 486 EISA", MACHINE_NOT_WORKING )
COMP( 199?, pkm0038s,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "DTK", "PKM-0038S aka Gemlight GMB-486SG", MACHINE_NOT_WORKING )
COMP( 199?, pm486pu,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "PROTECH",  "PM486PU-S7", MACHINE_NOT_WORKING )
COMP( 199?, pt430,     ibm5170, 0,       at486,     0,     at_state,     init_at,        "Pine Technology", "PT-430", MACHINE_NOT_WORKING )
COMP( 199?, pt432b,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "Pine Technology", "PT-432b aka SR-M401-A", MACHINE_NOT_WORKING )
COMP( 199?, ptmb457,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "PowerTech", "MB457", MACHINE_NOT_WORKING )
COMP( 199?, pwaih4077c,ibm5170, 0,       at486,     0,     at_state,     init_at,        "Mitac", "PWA-IH4077C", MACHINE_NOT_WORKING )
COMP( 199?, pwaih4077d,ibm5170, 0,       at486,     0,     at_state,     init_at,        "Mitac", "PWA-IH4077D", MACHINE_NOT_WORKING )
COMP( 199?, px486p3,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "QDI", "PX486P3", MACHINE_NOT_WORKING )
COMP( 1990, qi900,     ibm5170, 0,       at486,     0,     at_state,     init_at,        "Apricot",     "Apricot Qi 900 (Scorpion Motherboard)", MACHINE_NOT_WORKING )
COMP( 199?, sis85c471, ibm5170, 0,       at486,     0,     at_state,     init_at,        "<unknown>", "486 motherboards using the SiS 85C471/85C407 chipset", MACHINE_NOT_WORKING )
COMP( 199?, sm48650usc,ibm5170, 0,       at486,     0,     at_state,     init_at,        "Vintage Sprite", "SM 486-50USC", MACHINE_NOT_WORKING )
COMP( 199?, so025d2,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "SOYO", "025D2", MACHINE_NOT_WORKING )
COMP( 199?, so025k2,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "SOYO", "025K2", MACHINE_NOT_WORKING )
COMP( 199?, so025r2,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "SOYO", "025R2", MACHINE_NOT_WORKING )
COMP( 199?, sto486wb,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "See-Thru", "Sto486Wb aka AUVA Cam-33-P2", MACHINE_NOT_WORKING )
COMP( 199?, td4ipaio,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "<unknown>", "TD-4IP-UMC-AIO", MACHINE_NOT_WORKING )
COMP( 199?, tmpat48pg4,ibm5170, 0,       at486,     0,     at_state,     init_at,        "TMC", "PAT48PG4", MACHINE_NOT_WORKING )
COMP( 199?, tmpat48av, ibm5170, 0,       at486,     0,     at_state,     init_at,        "TMC", "PAT48AV", MACHINE_NOT_WORKING )
COMP( 199?, ts34t25,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "Highscreen",  "486-25", MACHINE_NOT_WORKING )
COMP( 199?, um486,     ibm5170, 0,       at486,     0,     at_state,     init_at,        "Elitegroup", "UM486/UM486sx", MACHINE_NOT_WORKING )
COMP( 199?, um486v,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "Elitegroup", "UM486V-AIO", MACHINE_NOT_WORKING )
COMP( 199?, um8810paio,ibm5170, 0,       at486,     0,     at_state,     init_at,        "Elitegroup", "UM8810 PAIO", MACHINE_NOT_WORKING )
COMP( 199?, um8886,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "<unknown>", "486 motherboards using the UMC UM8886/UM8881 chipset", MACHINE_NOT_WORKING )
COMP( 199?, um8498f,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "<unknown>", "486 motherboards using the UMC UM8498F, UM8496F chipset", MACHINE_NOT_WORKING )
COMP( 199?, uni4800,   ibm5170, 0,       at486,     0,     at_state,     init_at,        "<unknown>", "486 motherboards using the UNI4800 chipset", MACHINE_NOT_WORKING )
COMP( 199?, uniwb4407, ibm5170, 0,       at486,     0,     at_state,     init_at,        "UNICHIP", "486 WB 4407 REV 1.0", MACHINE_NOT_WORKING )
COMP( 199?, v4p895p3,  ibm5170, 0,       at486,     0,     at_state,     init_at,        "QDI", "V4P895P3/SMT V5.0", MACHINE_NOT_WORKING )
COMP( 199?, via4386vio,ibm5170, 0,       at486,     0,     at_state,     init_at,        "<unknown>", "Via 4386 VIO / Highscreen universal board", MACHINE_NOT_WORKING )
COMP( 199?, zi4dvs,    ibm5170, 0,       at486,     0,     at_state,     init_at,        "ZIDA", "4DVS", MACHINE_NOT_WORKING )




atari400.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Juergen Buchmueller, Angelo Salese
/******************************************************************************

    Atari 400/800

    Juergen Buchmueller, June 1998

    TODO (generic):
    - add cassette support;
    - add floppy .atx support;
    - Investigate supported RAM sizes and OS versions in different models;
    - Fix various keyboard differences;
    - Freddy emulation for 800XLF?
    - Add support for proto boards and expansions (a1400xl, C/PM board, etc.)
    - a130xe: support extended bank readback for Antic;
    - a1200xl: requires reading TRIG3 high for detecting a cart inserted,
      depends on above;
    - a600xl, a1200xl: crashes on MMU test in Acid800;
    - slot support for PBI/ECI bus;
    - slot support for overlay DIY HW mods:
      \- PokeyMAX
         (with stereo support via second Pokey alias accessed to $d280-$d2ff,
         cfr. yoomp);
      \- Ultimate1MB;
      \- Covox;
      \- VBXE "VideoBoard XE";
      \- Incognito;
      \- Rapidus;
      \- AKI PS/2 keyboard inteface;
      \- RAMBO XL and COMPY RAM expansion;
      \- APE Warp+ OS 32-in-1;
      \- MyBIOS for MyIDE-II;
      \- Bit-3 Full-View 80
         (technically maps in cart CCTL space, but installs in RAM card slot 3
         and overrides ANTIC+GTIA layer when enabled);

    2009-05 FP changes:
     Factored out MESS specific code from MAME
     Added skeleton support for other XL/XE machines (VERY preliminary):
     - a600xl based on maxaflex emulation in MAME
     - a1200xl sharing a800xl code without BASIC
     - a65xe, a65xea, a130xe, a800xe, xegs sharing a800xl code (and this is wrong
      at least for xegs)
     Added proper dumps and labels, thanks to Freddy Offenga researches (a few
     are still marked BAD_DUMP while waiting for crc confirmation, since they
     have been obtained by splitting whole dumps)

    2013-11-06 Robert Tuccitto:
    Updated Palette per 'CGIA D020577' and 'GTIA C014805', including
    normalized grayscale with proper color gradient.  Added Phase Shift
    values 24.7 thru 27.7 degrees in 0.5 degree increments.  Enabled
    Phase Shift 26.2 degrees as default.

    2013-11-23 Robert Tuccitto:
    Added palette notes

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

#include "emu.h"
#include "atari400.h"

#include "cpu/m6502/m6502.h"
#include "machine/6821pia.h"
#include "machine/bankdev.h"
#include "machine/input_merger.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "sound/dac.h"
#include "sound/pokey.h"

#include "bus/a800/a800_slot.h"
#include "bus/a800/a800_carts.h"
#include "bus/a800/a8sio.h"
#include "bus/vcs_ctrl/ctrl.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

/******************************************************************************
    Atari 800 memory map (preliminary)

    ***************** read access *******************
    range     short   description
    0000-9FFF RAM     main memory
    A000-BFFF RAM/ROM RAM or (banked) ROM cartridges
    C000-CFFF ROM     unused or monitor ROM

    ********* GTIA    ********************************

    D000      m0pf    missile 0 playfield collisions
    D001      m1pf    missile 1 playfield collisions
    D002      m2pf    missile 2 playfield collisions
    D003      m3pf    missile 3 playfield collisions
    D004      p0pf    player 0 playfield collisions
    D005      p1pf    player 1 playfield collisions
    D006      p2pf    player 2 playfield collisions
    D007      p3pf    player 3 playfield collisions
    D008      m0pl    missile 0 player collisions
    D009      m1pl    missile 1 player collisions
    D00A      m2pl    missile 2 player collisions
    D00B      m3pl    missile 3 player collisions
    D00C      p0pl    player 0 player collisions
    D00D      p1pl    player 1 player collisions
    D00E      p2pl    player 2 player collisions
    D00F      p3pl    player 3 player collisions
    D010      but0    button stick 0
    D011      but1    button stick 1
    D012      but2    button stick 2
    D013      but3    button stick 3
    D014      xff     unused
    D015      xff     unused
    D016      xff     unused
    D017      xff     unused
    D018      xff     unused
    D019      xff     unused
    D01A      xff     unused
    D01B      xff     unused
    D01C      xff     unused
    D01D      xff     unused
    D01E      xff     unused
    D01F      cons    console keys
    D020-D0FF repeated 7 times

    D100-D1FF xff

    ********* POKEY   ********************************
    D200      pot0    paddle 0
    D201      pot1    paddle 1
    D202      pot2    paddle 2
    D203      pot3    paddle 3
    D204      pot4    paddle 4
    D205      pot5    paddle 5
    D206      pot6    paddle 6
    D207      pot7    paddle 7
    D208      potb    all paddles
    D209      kbcode  keyboard scan code
    D20A      random  random number generator
    D20B      xff     unused
    D20C      xff     unused
    D20D      serin   serial input
    D20E      irqst   IRQ status
    D20F      skstat  sk status
    D210-D2FF repeated 15 times

    ********* PIO     ********************************
    D300      porta   read pio port A
    D301      portb   read pio port B
    D302      pactl   read pio port A control
    D303      pbctl   read pio port B control
    D304-D3FF repeated 63 times

    ********* ANTIC   ********************************
    D400      xff     unused
    D401      xff     unused
    D402      xff     unused
    D403      xff     unused
    D404      xff     unused
    D405      xff     unused
    D406      xff     unused
    D407      xff     unused
    D408      xff     unused
    D409      xff     unused
    D40A      xff     unused
    D40B      vcount  vertical (scanline) counter
    D40C      penh    light pen horizontal pos
    D40D      penv    light pen vertical pos
    D40E      xff     unused
    D40F      nmist   NMI status

    D500-D7FF xff     unused memory

    D800-DFFF ROM     floating point ROM
    E000-FFFF ROM     bios ROM

    ***************** write access *******************
    range     short   description
    0000-9FFF RAM     main memory
    A000-BFFF RAM/ROM RAM or (banked) ROM
    C000-CFFF ROM     unused or monitor ROM

    ********* GTIA    ********************************
    D000      hposp0  player 0 horz position
    D001      hposp1  player 1 horz position
    D002      hposp2  player 2 horz position
    D003      hposp3  player 3 horz position
    D004      hposm0  missile 0 horz position
    D005      hposm1  missile 0 horz position
    D006      hposm2  missile 0 horz position
    D007      hposm3  missile 0 horz position
    D008      sizep0  size player 0
    D009      sizep1  size player 0
    D00A      sizep2  size player 0
    D00B      sizep3  size player 0
    D00C      sizem   size missiles
    D00D      grafp0  graphics data for player 0
    D00E      grafp1  graphics data for player 1
    D00F      grafp2  graphics data for player 2
    D010      grafp3  graphics data for player 3
    D011      grafm   graphics data for missiles
    D012      colpm0  color for player/missile 0
    D013      colpm1  color for player/missile 1
    D014      colpm2  color for player/missile 2
    D015      colpm3  color for player/missile 3
    D016      colpf0  color 0 playfield
    D017      colpf1  color 1 playfield
    D018      colpf2  color 2 playfield
    D019      colpf3  color 3 playfield
    D01A      colbk   background playfield
    D01B      prior   priority select
    D01C      vdelay  delay until vertical retrace
    D01D      gractl  graphics control
    D01E      hitclr  clear collisions
    D01F      wcons   write console (speaker)
    D020-D0FF repeated 7 times

    D100-D1FF xff     unused

    ********* POKEY   ********************************
    D200      audf1   frequency audio chan #1
    D201      audc1   control audio chan #1
    D202      audf2   frequency audio chan #2
    D203      audc2   control audio chan #2
    D204      audf3   frequency audio chan #3
    D205      audc3   control audio chan #3
    D206      audf4   frequency audio chan #4
    D207      audc4   control audio chan #4
    D208      audctl  audio control
    D209      stimer  start timer
    D20A      skres   sk reset
    D20B      potgo   start pot AD conversion
    D20C      xff     unused
    D20D      serout  serial output
    D20E      irqen   IRQ enable
    D20F      skctl   sk control
    D210-D2FF repeated 15 times

    ********* PIO     ********************************
    D300      porta   write pio port A (output or mask)
    D301      portb   write pio port B (output or mask)
    D302      pactl   write pio port A control
    D303      pbctl   write pio port B control
    D304-D3FF         repeated

    ********* ANTIC   ********************************
    D400      dmactl  write DMA control
    D401      chactl  write character control
    D402      dlistl  write display list lo
    D403      dlisth  write display list hi
    D404      hscrol  write horz scroll
    D405      vscrol  write vert scroll
    D406      xff     unused
    D407      pmbash  player/missile base addr hi
    D408      xff     unused
    D409      chbash  character generator base addr hi
    D40A      wsync   wait for hsync
    D40B      xff     unused
    D40C      xff     unused
    D40D      xff     unused
    D40E      nmien   NMI enable
    D40F      nmires  NMI reset

    D500-D7FF xff     unused memory

    D800-DFFF ROM     floating point ROM
    E000-FFFF ROM     BIOS ROM
******************************************************************************/

class a400_state : public atari_common_state
{
public:
	a400_state(const machine_config &mconfig, device_type type, const char *tag)
		: atari_common_state(mconfig, type, tag)
		, m_ram(*this, RAM_TAG)
		, m_pia(*this, "pia")
		, m_dac(*this, "dac")
		, m_sio(*this, "sio")
		, m_region_maincpu(*this, "maincpu")
		, m_cartleft(*this, "cartleft")
		, m_cart_rd4_view(*this, "cart_rd4_view")
		, m_cart_rd5_view(*this, "cart_rd5_view")
		, m_ctrl(*this, "ctrl%u", 1U)
	{ }

	void a400(machine_config &config);
	void a400pal(machine_config &config);

protected:
	void atari_common_nodac(machine_config &config);
	void atari_common(machine_config &config);

	void a400_mem(address_map &map) ATTR_COLD;
	TIMER_DEVICE_CALLBACK_MEMBER(a400_interrupt);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// TODO: these two should really be inside ram_device instead
	template <unsigned StartBase> uint8_t ram_r(address_space &space, offs_t offset)
	{
		const offs_t memory_offset = StartBase + offset;

		if (memory_offset < m_ram->size())
			return m_ram->pointer()[memory_offset];

		// TODO: floating bus
		return space.unmap();
	}

	template <unsigned StartBase> void ram_w(offs_t offset, uint8_t data)
	{
		const offs_t memory_offset = StartBase + offset;

		if (memory_offset < m_ram->size())
			m_ram->pointer()[memory_offset] = data;
	}

	virtual uint8_t djoy_b_r();

private:
	void a400_palette(palette_device &palette) const;

	void gtia_cb(uint8_t data);

	uint8_t djoy_0_1_r();
	void djoy_0_1_w(uint8_t data);
	uint8_t djoy_2_3_r();
	void djoy_2_3_w(uint8_t data);

protected:
	optional_device<ram_device> m_ram;
	optional_device<pia6821_device> m_pia;
	optional_device<dac_bit_interface> m_dac;
	optional_device<a8sio_device> m_sio;
	required_memory_region m_region_maincpu;
	optional_device<a800_cart_slot_device> m_cartleft;
	memory_view m_cart_rd4_view, m_cart_rd5_view;
	optional_device_array<vcs_control_port_device, 4> m_ctrl;

	void hw_iomap(address_map &map) ATTR_COLD;

	int m_cart_rd4_enabled = 0, m_cart_rd5_enabled = 0;
	void cart_rd4_w( int state );
	void cart_rd5_w( int state );

	virtual void area_8000_map(address_map &map) ATTR_COLD;
	virtual void area_a000_map(address_map &map) ATTR_COLD;
};

class a800_state : public a400_state
{
public:
	a800_state(const machine_config &mconfig, device_type type, const char *tag)
		: a400_state(mconfig, type, tag)
		, m_cartright(*this, "cartright")
	{ }

	void a800(machine_config &config);
	void a800pal(machine_config &config);

protected:
//  virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<a800_cart_slot_device> m_cartright;

	void a800_mem(address_map &map) ATTR_COLD;
};

class a1200xl_state : public a400_state
{
public:
	a1200xl_state(const machine_config &mconfig, device_type type, const char *tag)
		: a400_state(mconfig, type, tag)
		, m_kernel_view(*this, "kernel_view")
		, m_selftest_view(*this, "selftest_view")
	{ }

	void a1200xl(machine_config &config);

protected:
	void atari_xl_common(machine_config &config);

	void a1200xl_mem(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	TIMER_DEVICE_CALLBACK_MEMBER(xl_interrupt);

	memory_view m_kernel_view;
	memory_view m_selftest_view;

	virtual void portb_cb(uint8_t data);
	virtual uint8_t djoy_b_r() override;

	void selftest_map(memory_view::memory_view_entry &block, bool is_rom_mapping);
private:
	void pia_portb_w(uint8_t data);

	uint8_t m_mmu;
};

class a800xl_state : public a1200xl_state
{
public:
	a800xl_state(const machine_config &mconfig, device_type type, const char *tag)
		: a1200xl_state(mconfig, type, tag)
		, m_basic_view(*this, "basic_view")
	{ }

	void a800xl(machine_config &config);
	void a800xlpal(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;

	virtual void portb_cb(uint8_t data) override;

	void a800xl_mem(address_map &map) ATTR_COLD;

	virtual void area_a000_map(address_map &map) override ATTR_COLD;

	memory_view m_basic_view;
};

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

	void a600xl(machine_config &config);
};

class a130xe_state : public a800xl_state
{
public:
	a130xe_state(const machine_config &mconfig, device_type type, const char *tag)
		: a800xl_state(mconfig, type, tag)
		, m_ext_view(*this, "ext_view")
		, m_ext_bank(*this, "ext_bank")
	{ }

	void a130xe(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;

	void a130xe_mem(address_map &map) ATTR_COLD;

	virtual void portb_cb(uint8_t data) override;

	memory_view m_ext_view;
	required_device<address_map_bank_device> m_ext_bank;

	void extram_map(address_map &map) ATTR_COLD;

};

class xegs_state : public a800xl_state
{
public:
	xegs_state(const machine_config &mconfig, device_type type, const char *tag)
		: a800xl_state(mconfig, type, tag)
		, m_bank(*this, "bank")
	{ }

	void xegs(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual void portb_cb(uint8_t data) override;

	required_memory_bank m_bank;

	void xegs_mem(address_map &map) ATTR_COLD;
};

class a5200_state : public a400_state
{
public:
	a5200_state(const machine_config &mconfig, device_type type, const char *tag)
		: a400_state(mconfig, type, tag)
		, m_cart(*this, "cartslot")
	{ }

	void a5200(machine_config &config);
	void a5200a(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<a5200_cart_slot_device> m_cart;

	TIMER_DEVICE_CALLBACK_MEMBER(a5200_interrupt);

	void a5200_mem(address_map &map) ATTR_COLD;
};

/**************************************************************
 *
 * Memory maps
 *
 **************************************************************/

// TODO: transparent support for PBI (XL) / ECI (XE series), at 0xd1xx, 0xd6xx, 0xd7xx + ROM at 0xd800-0xdfff

void a400_state::hw_iomap(address_map &map)
{
	map(0x0000, 0x00ff).rw(m_gtia, FUNC(gtia_device::read), FUNC(gtia_device::write));
//  map(0x0100, 0x01ff).noprw();
	map(0x0200, 0x02ff).rw(m_pokey, FUNC(pokey_device::read), FUNC(pokey_device::write));
	map(0x0300, 0x03ff).rw(m_pia, FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
	map(0x0400, 0x04ff).rw(m_antic, FUNC(antic_device::read), FUNC(antic_device::write));
	map(0x0500, 0x05ff).rw(m_cartleft, FUNC(a800_cart_slot_device::read_cctl), FUNC(a800_cart_slot_device::write_cctl));
//  map(0x0600, 0x07ff).noprw();
}

void a400_state::area_8000_map(address_map &map)
{
	map(0x8000, 0x9fff).view(m_cart_rd4_view);
	m_cart_rd4_view[0](0x8000, 0x9fff).rw(FUNC(a400_state::ram_r<0x8000>), FUNC(a400_state::ram_w<0x8000>));
	m_cart_rd4_view[1](0x8000, 0x9fff).rw(m_cartleft, FUNC(a800_cart_slot_device::read_cart<0>), FUNC(a800_cart_slot_device::write_cart<0>));
}

void a400_state::area_a000_map(address_map &map)
{
	map(0xa000, 0xbfff).view(m_cart_rd5_view);
	m_cart_rd5_view[0](0xa000, 0xbfff).rw(FUNC(a400_state::ram_r<0xa000>), FUNC(a400_state::ram_w<0xa000>));
	m_cart_rd5_view[1](0xa000, 0xbfff).rw(m_cartleft, FUNC(a800_cart_slot_device::read_cart<1>), FUNC(a800_cart_slot_device::write_cart<1>));
}


// a400/a800 explicitly expects floating bus for unmapped ranges, will punt with value_low()
void a400_state::a400_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).rw(FUNC(a400_state::ram_r<0x0000>), FUNC(a400_state::ram_w<0x0000>));
	area_8000_map(map);
	area_a000_map(map);
	map(0xc000, 0xcfff).rom();
	map(0xd000, 0xd7ff).m(*this, FUNC(a400_state::hw_iomap));
	map(0xd800, 0xffff).rom();
}

void a800_state::a800_mem(address_map &map)
{
	a400_mem(map);
	// TODO: stub for right cart handling, to be eventually honored internally
	// ...
}

// XL specifics

void a1200xl_state::selftest_map(memory_view::memory_view_entry &block, bool is_rom_mapping)
{
	block(0x4000, 0x4fff).rw(FUNC(a1200xl_state::ram_r<0x4000>), FUNC(a1200xl_state::ram_w<0x4000>));
	if (is_rom_mapping)
		block(0x5000, 0x57ff).rom().region("maincpu", 0xd000);
	else
		block(0x5000, 0x57ff).rw(FUNC(a1200xl_state::ram_r<0x5000>), FUNC(a1200xl_state::ram_w<0x5000>));
	block(0x5800, 0x7fff).rw(FUNC(a1200xl_state::ram_r<0x5800>), FUNC(a1200xl_state::ram_w<0x5800>));
}

// from a800xl onward HW I/O map punches thru kernel view,
// for simplicity we just delay mapping former.

// same as a800xl except no built-in basic (just maps RAM there)
void a1200xl_state::a1200xl_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).rw(FUNC(a1200xl_state::ram_r<0x0000>), FUNC(a1200xl_state::ram_w<0x0000>));
	map(0x4000, 0x7fff).view(m_selftest_view);
	selftest_map(m_selftest_view[0], false);
	selftest_map(m_selftest_view[1], true);
	area_8000_map(map);
	area_a000_map(map);
	map(0xc000, 0xffff).view(m_kernel_view);
	m_kernel_view[0](0xc000, 0xffff).rw(FUNC(a1200xl_state::ram_r<0xc000>), FUNC(a1200xl_state::ram_w<0xc000>));
	m_kernel_view[1](0xc000, 0xffff).rom().region("maincpu", 0xc000);
	map(0xd000, 0xd7ff).m(*this, FUNC(a1200xl_state::hw_iomap));
}

void a800xl_state::area_a000_map(address_map &map)
{
	map(0xa000, 0xbfff).view(m_cart_rd5_view);
	m_cart_rd5_view[0](0xa000, 0xbfff).view(m_basic_view);
	m_basic_view[0](0xa000, 0xbfff).rw(FUNC(a800xl_state::ram_r<0xa000>), FUNC(a800xl_state::ram_w<0xa000>));
	m_basic_view[1](0xa000, 0xbfff).rom().region("maincpu", 0xa000);
	m_cart_rd5_view[1](0xa000, 0xbfff).rw(m_cartleft, FUNC(a800_cart_slot_device::read_cart<1>), FUNC(a800_cart_slot_device::write_cart<1>));
}

void a800xl_state::a800xl_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).rw(FUNC(a800xl_state::ram_r<0x0000>), FUNC(a800xl_state::ram_w<0x0000>));
	map(0x4000, 0x7fff).view(m_selftest_view);
	selftest_map(m_selftest_view[0], false);
	selftest_map(m_selftest_view[1], true);
	area_8000_map(map);
	area_a000_map(map);
	map(0xc000, 0xffff).view(m_kernel_view);
	m_kernel_view[0](0xc000, 0xffff).rw(FUNC(a800xl_state::ram_r<0xc000>), FUNC(a800xl_state::ram_w<0xc000>));
	m_kernel_view[1](0xc000, 0xffff).rom().region("maincpu", 0xc000);
	map(0xd000, 0xd7ff).m(*this, FUNC(a800xl_state::hw_iomap));
}

// selftest ROM has still priority over regular a130xe extended RAM
// cfr. a130xe MMU test in Acid800
void a130xe_state::extram_map(address_map &map)
{
	map(0x00000, 0x0ffff).mirror(0x10000).rw(FUNC(a130xe_state::ram_r<0x10000>), FUNC(a130xe_state::ram_w<0x10000>));
	map(0x11000, 0x117ff).mirror(0x0c000).rom().region("maincpu", 0xd000).unmapw();
}

void a130xe_state::a130xe_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).rw(FUNC(a130xe_state::ram_r<0x0000>), FUNC(a130xe_state::ram_w<0x0000>));
	map(0x4000, 0x7fff).view(m_ext_view);
	m_ext_view[0](0x4000, 0x7fff).view(m_selftest_view);
	selftest_map(m_selftest_view[0], false);
	selftest_map(m_selftest_view[1], true);
	m_ext_view[1](0x4000, 0x7fff).m(m_ext_bank, FUNC(address_map_bank_device::amap8));
	area_8000_map(map);
	area_a000_map(map);
	map(0xc000, 0xffff).view(m_kernel_view);
	m_kernel_view[0](0xc000, 0xffff).rw(FUNC(a130xe_state::ram_r<0xc000>), FUNC(a130xe_state::ram_w<0xc000>));
	m_kernel_view[1](0xc000, 0xffff).rom().region("maincpu", 0xc000);
	map(0xd000, 0xd7ff).m(*this, FUNC(a130xe_state::hw_iomap));
}


void xegs_state::xegs_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x4fff).ram();
	map(0x5000, 0x57ff).view(m_selftest_view);
	m_selftest_view[0](0x5000, 0x57ff).ram();
	m_selftest_view[1](0x5000, 0x57ff).rom().region("maincpu", 0xd000);
	map(0x5800, 0x7fff).ram();

	map(0x8000, 0x9fff).view(m_cart_rd4_view);
	m_cart_rd4_view[0](0x8000, 0x9fff).ram();
	m_cart_rd4_view[1](0x8000, 0x9fff).rw(m_cartleft, FUNC(a800_cart_slot_device::read_cart<0>), FUNC(a800_cart_slot_device::write_cart<0>));

	map(0xa000, 0xbfff).view(m_cart_rd5_view);
	m_cart_rd5_view[0](0xa000, 0xbfff).view(m_basic_view);
	m_basic_view[0](0xa000, 0xbfff).ram();
	m_basic_view[1](0xa000, 0xbfff).bankr(m_bank);
	m_cart_rd5_view[1](0xa000, 0xbfff).rw(m_cartleft, FUNC(a800_cart_slot_device::read_cart<1>), FUNC(a800_cart_slot_device::write_cart<1>));

	map(0xc000, 0xffff).view(m_kernel_view);
	m_kernel_view[0](0xc000, 0xffff).ram();
	m_kernel_view[1](0xc000, 0xffff).rom().region("maincpu", 0xc000);
	map(0xd000, 0xd7ff).m(*this, FUNC(xegs_state::hw_iomap));
}


void a5200_state::a5200_mem(address_map &map)
{
	map(0x0000, 0x3fff).ram();
	map(0x4000, 0xbfff).rw(m_cart, FUNC(a5200_cart_slot_device::read_cart), FUNC(a5200_cart_slot_device::write_cart));
	map(0xc000, 0xcfff).rw(m_gtia, FUNC(gtia_device::read), FUNC(gtia_device::write));
	map(0xd400, 0xd4ff).rw(m_antic, FUNC(antic_device::read), FUNC(antic_device::write));
	// 0xe000-0xe7ff - Expansion?
	map(0xe800, 0xefff).rw(m_pokey, FUNC(pokey_device::read), FUNC(pokey_device::write));
	map(0xf000, 0xffff).rom();
}



/**************************************************************
 *
 * Input ports
 *
 **************************************************************/


#define JOYSTICK_DELTA          10
#define JOYSTICK_SENSITIVITY    200

static INPUT_PORTS_START( atari_artifacting )
	PORT_START("artifacts")
	PORT_CONFNAME(0x40, 0x00, "Television Artifacts" )
	PORT_CONFSETTING(0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(0x40, DEF_STR( On ) )
INPUT_PORTS_END



static INPUT_PORTS_START( atari_console )
	PORT_START("console")
	PORT_BIT(0x04, 0x04, IPT_KEYBOARD) PORT_NAME("CONS.2: Option") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x02, 0x02, IPT_KEYBOARD) PORT_NAME("CONS.1: Select") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x01, 0x01, IPT_KEYBOARD) PORT_NAME("CONS.0: Start") PORT_CODE(KEYCODE_F1)
INPUT_PORTS_END



/* 2009-04 FP:
Small note about natural keyboard support: currently,
- "Break" is mapped to 'F1'
- "Clear" is mapped to 'F2'
- "Atari" is mapped to 'F3'                         */

static INPUT_PORTS_START( atari_keyboard )
	PORT_START("keyboard.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('+') PORT_CHAR('\\')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('*') PORT_CHAR('^')

	PORT_START("keyboard.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('=') PORT_CHAR('|')

	PORT_START("keyboard.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')

	PORT_START("keyboard.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Escape") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')

	PORT_START("keyboard.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('[')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(']')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Atari") PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(F3))

	PORT_START("keyboard.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR(UCHAR_MAMEKEY(TAB))
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')

	PORT_START("keyboard.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BackS  Delete") PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('@')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("<  Clear") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('<') PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(">  Insert") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('>') PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START("keyboard.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Lowr  Caps") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')

	PORT_START("fake")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
INPUT_PORTS_END



static INPUT_PORTS_START( a800 )
	PORT_INCLUDE( atari_artifacting )
	PORT_INCLUDE( atari_console )
	PORT_INCLUDE( atari_keyboard )
INPUT_PORTS_END

static INPUT_PORTS_START( a1200xl )
	PORT_INCLUDE( a800 )

	// option jumpers, available on a1200xl only
	// J1 causes a self-test if installed
	// J2 to J4 are unused by BIOS but may eventually be read by SW ...
	PORT_START("J1")
	PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_CONFNAME( 0x80, 0x80, "Enable self-test check (J1)" )
	PORT_CONFSETTING(    0x00, DEF_STR( Yes ) )
	PORT_CONFSETTING(    0x80, DEF_STR( No ) )

	PORT_START("J2")
	PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_CONFNAME( 0x80, 0x80, "Unused (J2)" )
	PORT_CONFSETTING(    0x80, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("J3")
	PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_CONFNAME( 0x80, 0x80, "Unused (J3)" )
	PORT_CONFSETTING(    0x80, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("J4")
	PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_CONFNAME( 0x80, 0x80, "Unused (J4)" )
	PORT_CONFSETTING(    0x80, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( a5200 )
	PORT_INCLUDE( atari_artifacting )

	PORT_START("djoy_b")    /* lower/upper buttons */
	PORT_BIT(0x01, 0x01, IPT_BUTTON1) PORT_PLAYER(1)
	PORT_BIT(0x02, 0x02, IPT_BUTTON1) PORT_PLAYER(2)
	PORT_BIT(0x04, 0x04, IPT_BUTTON1) PORT_PLAYER(3)
	PORT_BIT(0x08, 0x08, IPT_BUTTON1) PORT_PLAYER(4)
	PORT_BIT(0x10, 0x10, IPT_BUTTON2) PORT_PLAYER(1)
	PORT_BIT(0x20, 0x20, IPT_BUTTON2) PORT_PLAYER(2)
	PORT_BIT(0x40, 0x40, IPT_BUTTON2) PORT_PLAYER(3)
	PORT_BIT(0x80, 0x80, IPT_BUTTON2) PORT_PLAYER(4)

	PORT_START("keypad.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("(Break)") PORT_CODE(KEYCODE_PAUSE)    // is this correct?
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("[#]") PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("[0]") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("[*]") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0xf0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("keypad.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Reset") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("[9]") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("[8]") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("[7]") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0xf0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("keypad.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME(DEF_STR(Pause)) PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("[6]") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("[5]") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("[4]") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0xf0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("keypad.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_START)    PORT_NAME("Start")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("[3]") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("[2]") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("[1]") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0xf0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("analog_0")
	PORT_BIT(0xff, 0x72, IPT_AD_STICK_X) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0x00,0xe4) PORT_PLAYER(1)

	PORT_START("analog_1")
	PORT_BIT(0xff, 0x72, IPT_AD_STICK_Y) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0x00,0xe4) PORT_PLAYER(1)

	PORT_START("analog_2")
	PORT_BIT(0xff, 0x72, IPT_AD_STICK_X) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0x00,0xe4) PORT_PLAYER(2)

	PORT_START("analog_3")
	PORT_BIT(0xff, 0x72, IPT_AD_STICK_Y) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0x00,0xe4) PORT_PLAYER(2)

	PORT_START("analog_4")
	PORT_BIT(0xff, 0x72, IPT_AD_STICK_X) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0x00,0xe4) PORT_PLAYER(3)

	PORT_START("analog_5")
	PORT_BIT(0xff, 0x72, IPT_AD_STICK_Y) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0x00,0xe4) PORT_PLAYER(3)

	PORT_START("analog_6")
	PORT_BIT(0xff, 0x72, IPT_AD_STICK_X) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0x00,0xe4) PORT_PLAYER(4)

	PORT_START("analog_7")
	PORT_BIT(0xff, 0x72, IPT_AD_STICK_Y) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0x00,0xe4) PORT_PLAYER(4)

INPUT_PORTS_END



static INPUT_PORTS_START( a5200a )
	PORT_INCLUDE( a5200 )

	PORT_MODIFY("djoy_b")    /* lower/upper buttons */
	PORT_BIT(0x04, 0x04, IPT_UNUSED)
	PORT_BIT(0x08, 0x08, IPT_UNUSED)
	PORT_BIT(0x40, 0x40, IPT_UNUSED)
	PORT_BIT(0x80, 0x80, IPT_UNUSED)

	PORT_MODIFY("analog_4")
	PORT_BIT(0xff, 0x72, IPT_UNUSED)

	PORT_MODIFY("analog_5")
	PORT_BIT(0xff, 0x72, IPT_UNUSED)

	PORT_MODIFY("analog_6")
	PORT_BIT(0xff, 0x72, IPT_UNUSED)

	PORT_MODIFY("analog_7")
	PORT_BIT(0xff, 0x72, IPT_UNUSED)
INPUT_PORTS_END

/***************************************************************
Atari 5200 Palette Notes:

Palette on a modern flat panel display (LCD, LED, Plasma, etc.)
appears different from a traditional CRT. The most outstanding
difference is Hue 1x, the hue begin point. Hue 1x looks very
'green' (~-60 to -45 degrees - depending on how poor or well it
handles the signal conversion and its calibration) on a modern
flat panel display, as opposed to 'gold' (~-33 degrees) on a
CRT.  The official technical document, "GTIA C014805 NTSC"
stipulates Hue 1x as gold.

The "Atari 5200 Field Service Manual" provides two different
sets of instructions in harmony with utilizing the "PAM
Diagnostic SALT Cartridge v1.1".  In one account it states the
color just below and above the reference bar to be within one
shade of each other.

Under the same reference document, directions are given for it
to be the same color.  Phase Shift 25.7 degrees matches Hue 1x,
15x and the color below the reference bar.

However, if the system is adjusted within the first several
minutes of running, the warm up, consistent system run time,
causes Hue 15x (F$) to become stronger/darker gold (More brown
then ultimately red-brown); as well as leans Hue 14x (E$) more
brown than green.  Once achieving a phase shift of 27.7,
Hue 14x (E$) and Hue 15x (F$) near-exact match Hue 1x and 2x
respectively.

Accounting for system 'warm-up', phase shifting, as well as the
instructions for it to be within one shade of each other, would
make Phase Shift 26.2 degrees or 26.7 degrees a realistic
logical choice.

It also collaborates with the official "GTIA C014805 NTSC"
document for color order: Hue 1x = Gold, Hue 2x = Orange,
Hue 15x (F$) = Light-Orange; Phase Shift 26.2 places
Hue 15x (F$) between Hue 1x, Gold and Hue 2x, Orange;
a Light Orange in color.  Color descriptions are best measured
in the middle of the brightness scale.

It should be mentioned that Green-Yellow is referenced at
Hue 13x (D$), nowhere near Hue 1x.  A Green-Yellow Hue 1x is
how the palette is manipulated and modified (in part) under
a modern flat panel display.

Note though, even a properly calibrated console, at power on,
the phase shift appears as low as ~23 degrees and after a
considerable consistent runtime, can be as high as ~28 degrees.
In general, the low end of ~23 degrees lasts for maybe several
seconds, whereas higher values such as ~25-27 degrees is the
most dominant during system run time.

Additionally, the blue to red (And consequently blue to green)
ratio proportions may appear different on a modern flat panel
display than a CRT in some instances for the Atari 5200 system.
Furthermore, you may have some variation of proportions even
within the same display type.

One side effect of this on the console's palette is that some
values of red may appear too pinkish - Too much blue to red.
This is not the same as a traditional tint-hue control
adjustment; rather, can be demonstrated by changing the blue
ratio values via MAME HLSL settings.

Lastly, the Atari 2600 & 7800 NTSC color palettes hold the same
hue structure order and have similar appearance differences
dependent upon display type.
***************************************************************/
/**************************************************************
 *
 * Palette - Phase Shift 26.2
 *
 **************************************************************/

static const uint8_t atari_colors[256*3] =
{
	/* Grey */
	0x00,0x00,0x00, 0x11,0x11,0x11, 0x22,0x22,0x22, 0x33,0x33,0x33,
	0x44,0x44,0x44, 0x55,0x55,0x55, 0x66,0x66,0x66, 0x77,0x77,0x77,
	0x88,0x88,0x88, 0x99,0x99,0x99, 0xAA,0xAA,0xAA, 0xBB,0xBB,0xBB,
	0xCC,0xCC,0xCC, 0xDD,0xDD,0xDD, 0xEE,0xEE,0xEE, 0xFF,0xFF,0xFF,
	/* Gold */
	0x1A,0x07,0x00, 0x2B,0x18,0x00, 0x3C,0x29,0x00, 0x4D,0x3A,0x00,
	0x5E,0x4B,0x00, 0x6F,0x5C,0x00, 0x80,0x6D,0x00, 0x91,0x7E,0x09,
	0xA2,0x8F,0x1A, 0xB3,0xA0,0x2B, 0xC4,0xB1,0x3C, 0xD5,0xC2,0x4D,
	0xE6,0xD3,0x5E, 0xF7,0xE4,0x6F, 0xFF,0xF5,0x83, 0xFF,0xF7,0x97,
	/* Orange */
	0x31,0x00,0x00, 0x42,0x06,0x00, 0x53,0x17,0x00, 0x64,0x28,0x00,
	0x75,0x39,0x00, 0x86,0x4A,0x00, 0x97,0x5B,0x0B, 0xA8,0x6C,0x1C,
	0xB9,0x7D,0x2D, 0xCA,0x8E,0x3E, 0xDB,0x9F,0x4F, 0xEC,0xB0,0x60,
	0xFD,0xC1,0x71, 0xFF,0xD2,0x86, 0xFF,0xE3,0x9D, 0xFF,0xF4,0xB3,
	/* Red-Orange */
	0x3E,0x00,0x00, 0x4F,0x00,0x00, 0x60,0x08,0x00, 0x71,0x19,0x00,
	0x82,0x2A,0x0F, 0x93,0x3B,0x20, 0xA4,0x4C,0x31, 0xB5,0x5D,0x42,
	0xC6,0x6E,0x53, 0xD7,0x7F,0x64, 0xE8,0x90,0x75, 0xF9,0xA1,0x86,
	0xFF,0xB2,0x9A, 0xFF,0xC3,0xB0, 0xFF,0xD4,0xC6, 0xFF,0xE5,0xDC,
	/* Pink */
	0x3E,0x00,0x06, 0x4F,0x00,0x12, 0x60,0x00,0x1E, 0x71,0x0E,0x2E,
	0x82,0x1F,0x3F, 0x93,0x30,0x50, 0xA4,0x41,0x61, 0xB5,0x52,0x72,
	0xC6,0x63,0x83, 0xD7,0x74,0x94, 0xE8,0x85,0xA5, 0xF9,0x96,0xB6,
	0xFF,0xA7,0xCB, 0xFF,0xB8,0xE1, 0xFF,0xC9,0xEF, 0xFF,0xDA,0xF4,
	/* Purple */
	0x32,0x00,0x38, 0x43,0x00,0x44, 0x54,0x00,0x50, 0x65,0x0C,0x5F,
	0x76,0x1D,0x70, 0x87,0x2E,0x81, 0x98,0x3F,0x92, 0xA9,0x50,0xA3,
	0xBA,0x61,0xB4, 0xCB,0x72,0xC5, 0xDC,0x83,0xD6, 0xED,0x94,0xE4,
	0xFE,0xA5,0xE4, 0xFF,0xB6,0xE9, 0xFF,0xC7,0xEE, 0xFF,0xD8,0xF3,
	/* Purple-Blue */
	0x1B,0x00,0x5F, 0x2C,0x00,0x6B, 0x3D,0x00,0x77, 0x4E,0x11,0x88,
	0x5F,0x22,0x99, 0x70,0x33,0xAA, 0x81,0x44,0xBB, 0x92,0x55,0xCC,
	0xA3,0x66,0xDD, 0xB4,0x77,0xED, 0xC5,0x88,0xED, 0xD6,0x99,0xED,
	0xE7,0xAA,0xED, 0xF8,0xBB,0xED, 0xFF,0xCC,0xF0, 0xFF,0xDD,0xF5,
	/* Blue 1 */
	0x00,0x00,0x72, 0x10,0x00,0x7E, 0x21,0x0D,0x8E, 0x32,0x1E,0x9F,
	0x43,0x2F,0xB0, 0x54,0x40,0xC1, 0x65,0x51,0xD2, 0x76,0x62,0xE3,
	0x87,0x73,0xF4, 0x98,0x84,0xF9, 0xA9,0x95,0xF9, 0xBA,0xA6,0xF9,
	0xCB,0xB7,0xF9, 0xDC,0xC8,0xF9, 0xED,0xD9,0xF9, 0xFE,0xEA,0xF9,
	/* Blue 2 */
	0x00,0x00,0x65, 0x00,0x0C,0x7A, 0x05,0x1D,0x8E, 0x16,0x2E,0x9F,
	0x27,0x3F,0xB0, 0x38,0x50,0xC1, 0x49,0x61,0xD2, 0x5A,0x72,0xE3,
	0x6B,0x83,0xF4, 0x7C,0x94,0xFF, 0x8D,0xA5,0xFF, 0x9E,0xB6,0xFF,
	0xAF,0xC7,0xFF, 0xC0,0xD8,0xFF, 0xD1,0xE9,0xFF, 0xE2,0xFA,0xFF,
	/* Light-Blue */
	0x00,0x0D,0x48, 0x00,0x1E,0x5E, 0x00,0x2F,0x74, 0x00,0x40,0x8A,
	0x11,0x51,0x9B, 0x22,0x62,0xAC, 0x33,0x73,0xBD, 0x44,0x84,0xCE,
	0x55,0x95,0xDF, 0x66,0xA6,0xF0, 0x77,0xB7,0xFF, 0x88,0xC8,0xFF,
	0x99,0xD9,0xFF, 0xAA,0xEA,0xFF, 0xBB,0xFB,0xFF, 0xCC,0xFF,0xFF,
	/* Turquoise */
	0x00,0x1C,0x1C, 0x00,0x2D,0x32, 0x00,0x3E,0x49, 0x00,0x4F,0x5F,
	0x05,0x60,0x73, 0x16,0x71,0x84, 0x27,0x82,0x95, 0x38,0x93,0xA6,
	0x49,0xA4,0xB7, 0x5A,0xB5,0xC8, 0x6B,0xC6,0xD9, 0x7C,0xD7,0xEA,
	0x8D,0xE8,0xFB, 0x9E,0xF9,0xFF, 0xAF,0xFF,0xFF, 0xC0,0xFF,0xFF,
	/* Green-Blue */
	0x00,0x25,0x0B, 0x00,0x36,0x10, 0x00,0x47,0x18, 0x00,0x58,0x2E,
	0x07,0x69,0x42, 0x18,0x7A,0x53, 0x29,0x8B,0x64, 0x3A,0x9C,0x75,
	0x4B,0xAD,0x86, 0x5C,0xBE,0x97, 0x6D,0xCF,0xA8, 0x7E,0xE0,0xB9,
	0x8F,0xF1,0xCA, 0xA0,0xFF,0xDA, 0xB1,0xFF,0xE6, 0xC2,0xFF,0xF2,
	/* Green */
	0x00,0x27,0x0C, 0x00,0x38,0x11, 0x00,0x49,0x16, 0x04,0x5A,0x1A,
	0x15,0x6B,0x1A, 0x26,0x7C,0x22, 0x37,0x8D,0x33, 0x48,0x9E,0x44,
	0x59,0xAF,0x55, 0x6A,0xC0,0x66, 0x7B,0xD1,0x77, 0x8C,0xE2,0x88,
	0x9D,0xF3,0x99, 0xAE,0xFF,0xA8, 0xBF,0xFF,0xB4, 0xD0,0xFF,0xC0,
	/* Yellow-Green */
	0x00,0x21,0x0A, 0x00,0x32,0x0F, 0x0A,0x43,0x11, 0x1B,0x54,0x11,
	0x2C,0x65,0x11, 0x3D,0x76,0x11, 0x4E,0x87,0x11, 0x5F,0x98,0x1E,
	0x70,0xA9,0x2F, 0x81,0xBA,0x40, 0x92,0xCB,0x51, 0xA3,0xDC,0x62,
	0xB4,0xED,0x73, 0xC5,0xFE,0x84, 0xD6,0xFF,0x90, 0xE7,0xFF,0x9C,
	/* Orange-Green */
	0x05,0x13,0x04, 0x16,0x24,0x04, 0x27,0x35,0x04, 0x38,0x46,0x04,
	0x49,0x57,0x04, 0x5A,0x68,0x04, 0x6B,0x79,0x04, 0x7C,0x8A,0x09,
	0x8D,0x9B,0x1A, 0x9E,0xAC,0x2B, 0xAF,0xBD,0x3C, 0xC0,0xCE,0x4D,
	0xD1,0xDF,0x5E, 0xE2,0xF0,0x6F, 0xF3,0xFF,0x80, 0xFF,0xFF,0x8D,
	/* Light-Orange */
	0x21,0x02,0x00, 0x32,0x13,0x00, 0x43,0x24,0x00, 0x54,0x35,0x00,
	0x65,0x46,0x00, 0x76,0x57,0x00, 0x87,0x68,0x00, 0x98,0x79,0x0C,
	0xA9,0x8A,0x1D, 0xBA,0x9B,0x2E, 0xCB,0xAC,0x3F, 0xDC,0xBD,0x50,
	0xED,0xCE,0x61, 0xFE,0xDF,0x72, 0xFF,0xF0,0x87, 0xFF,0xFF,0x9D
};


/* Initialise the palette */
void a400_state::a400_palette(palette_device &palette) const
{
	for (unsigned i = 0; i < std::size(atari_colors) / 3; i++)
		palette.set_pen_color(i, atari_colors[i * 3], atari_colors[i * 3 + 1], atari_colors[i * 3 + 2]);
}
/******************************************************************
    PALETTE - PHASE 24.7 SHIFT

GREY
    0x00,0x00,0x00, 0x11,0x11,0x11, 0x22,0x22,0x22, 0x33,0x33,0x33,
    0x44,0x44,0x44, 0x55,0x55,0x55, 0x66,0x66,0x66, 0x77,0x77,0x77,
    0x88,0x88,0x88, 0x99,0x99,0x99, 0xAA,0xAA,0xAA, 0xBB,0xBB,0xBB,
    0xCC,0xCC,0xCC, 0xDD,0xDD,0xDD, 0xEE,0xEE,0xEE, 0xFF,0xFF,0xFF,

GOLD
    0x1A,0x07,0x00, 0x2B,0x18,0x00, 0x3C,0x29,0x00, 0x4D,0x3A,0x00,
    0x5E,0x4B,0x00, 0x6F,0x5C,0x00, 0x80,0x6D,0x00, 0x91,0x7E,0x09,
    0xA2,0x8F,0x1A, 0xB3,0xA0,0x2B, 0xC4,0xB1,0x3C, 0xD5,0xC2,0x4D,
    0xE6,0xD3,0x5E, 0xF7,0xE4,0x6F, 0xFF,0xF5,0x83, 0xFF,0xF7,0x97,

ORANGE
    0x30,0x00,0x00, 0x41,0x07,0x00, 0x52,0x18,0x00, 0x63,0x29,0x00,
    0x74,0x3A,0x00, 0x85,0x4B,0x00, 0x96,0x5C,0x0A, 0xA7,0x6D,0x1B,
    0xB8,0x7E,0x2C, 0xC9,0x8F,0x3D, 0xDA,0xA0,0x4E, 0xEB,0xB1,0x5F,
    0xFC,0xC2,0x70, 0xFF,0xD3,0x85, 0xFF,0xE4,0x9B, 0xFF,0xF5,0xB1,

RED-ORANGE
    0x3D,0x00,0x00, 0x4E,0x00,0x00, 0x5F,0x09,0x00, 0x70,0x1A,0x00,
    0x81,0x2B,0x09, 0x92,0x3C,0x1A, 0xA3,0x4D,0x2B, 0xB4,0x5E,0x3C,
    0xC5,0x6F,0x4D, 0xD6,0x80,0x5E, 0xE7,0x91,0x6F, 0xF8,0xA2,0x80,
    0xFF,0xB3,0x94, 0xFF,0xC4,0xAA, 0xFF,0xD5,0xC0, 0xFF,0xE6,0xD6,

PINK
    0x3F,0x00,0x00, 0x50,0x00,0x09, 0x61,0x00,0x15, 0x72,0x10,0x26,
    0x83,0x21,0x37, 0x94,0x32,0x48, 0xA5,0x43,0x59, 0xB6,0x54,0x6A,
    0xC7,0x65,0x7B, 0xD8,0x76,0x8C, 0xE9,0x87,0x9D, 0xFA,0x98,0xAE,
    0xFF,0xA9,0xC2, 0xFF,0xBA,0xD8, 0xFF,0xCB,0xEE, 0xFF,0xDC,0xF4,

PURPLE
    0x36,0x00,0x2E, 0x47,0x00,0x3A, 0x58,0x00,0x46, 0x69,0x0C,0x55,
    0x7A,0x1D,0x66, 0x8B,0x2E,0x77, 0x9C,0x3F,0x88, 0xAD,0x50,0x99,
    0xBE,0x61,0xAA, 0xCF,0x72,0xBB, 0xE0,0x83,0xCC, 0xF1,0x94,0xDD,
    0xFF,0xA5,0xE4, 0xFF,0xB6,0xE9, 0xFF,0xC7,0xEE, 0xFF,0xD8,0xF3,

PURPLE-BLUE
    0x23,0x00,0x55, 0x34,0x00,0x61, 0x45,0x00,0x6D, 0x56,0x0F,0x7E,
    0x67,0x20,0x8F, 0x78,0x31,0xA0, 0x89,0x42,0xB1, 0x9A,0x53,0xC2,
    0xAB,0x64,0xD3, 0xBC,0x75,0xE4, 0xCD,0x86,0xEA, 0xDE,0x97,0xEA,
    0xEF,0xA8,0xEA, 0xFF,0xB9,0xEA, 0xFF,0xCA,0xEF, 0xFF,0xDB,0xF4,

BLUE1
    0x09,0x00,0x6E, 0x1A,0x00,0x7A, 0x2B,0x08,0x88, 0x3C,0x19,0x99,
    0x4D,0x2A,0xAA, 0x5E,0x3B,0xBB, 0x6F,0x4C,0xCC, 0x80,0x5D,0xDD,
    0x91,0x6E,0xEE, 0xA2,0x7F,0xF4, 0xB3,0x90,0xF4, 0xC4,0xA1,0xF4,
    0xD5,0xB2,0xF4, 0xE6,0xC3,0xF4, 0xF7,0xD4,0xF4, 0xFF,0xE5,0xF7,

BLUE2
    0x00,0x00,0x6D, 0x00,0x05,0x80, 0x10,0x16,0x91, 0x21,0x27,0xA2,
    0x32,0x38,0xB3, 0x43,0x49,0xC4, 0x54,0x5A,0xD5, 0x65,0x6B,0xE6,
    0x76,0x7C,0xF7, 0x87,0x8D,0xFF, 0x98,0x9E,0xFF, 0xA9,0xAF,0xFF,
    0xBA,0xC0,0xFF, 0xCB,0xD1,0xFF, 0xDC,0xE2,0xFF, 0xED,0xF3,0xFF

LIGHT-BLUE
    0x00,0x05,0x57, 0x00,0x16,0x6E, 0x00,0x27,0x84, 0x09,0x38,0x97,
    0x1A,0x49,0xA8, 0x2B,0x5A,0xB9, 0x3C,0x6B,0xCA, 0x4D,0x7C,0xDB,
    0x5E,0x8D,0xEC, 0x6F,0x9E,0xFD, 0x80,0xAF,0xFF, 0x91,0xC0,0xFF,
    0xA2,0xD1,0xFF, 0xB3,0xE2,0xFF, 0xC4,0xF3,0xFF, 0xD5,0xFF,0xFF,

TURQUOISE
    0x00,0x15,0x34, 0x00,0x26,0x4A, 0x00,0x37,0x60, 0x00,0x48,0x77,
    0x0A,0x59,0x8A, 0x1B,0x6A,0x9B, 0x2C,0x7B,0xAC, 0x3D,0x8C,0xBD,
    0x4E,0x9D,0xCE, 0x5F,0xAE,0xDF, 0x70,0xBF,0xF0, 0x81,0xD0,0xFF,
    0x92,0xE1,0xFF, 0xA3,0xF2,0xFF, 0xB4,0xFF,0xFF, 0xC5,0xFF,0xFF

GREEN-BLUE
    0x00,0x21,0x0A, 0x00,0x32,0x1F, 0x00,0x43,0x35, 0x00,0x54,0x4B,
    0x04,0x65,0x60, 0x15,0x76,0x71, 0x26,0x87,0x82, 0x37,0x98,0x93,
    0x48,0xA9,0xA4, 0x59,0xBA,0xB5, 0x6A,0xCB,0xC6, 0x7B,0xDC,0xD7,
    0x8C,0xED,0xE8, 0x9D,0xFE,0xF9, 0xAE,0xFF,0xFF, 0xBF,0xFF,0xFF,

GREEN
    0x00,0x27,0x0C, 0x00,0x38,0x11, 0x00,0x49,0x16, 0x00,0x5A,0x1D,
    0x0A,0x6B,0x30, 0x1B,0x7C,0x41, 0x2C,0x8D,0x52, 0x3D,0x9E,0x63,
    0x4E,0xAF,0x74, 0x5F,0xC0,0x85, 0x70,0xD1,0x96, 0x81,0xE2,0xA7,
    0x92,0xF3,0xB8, 0xA3,0xFF,0xC8, 0xB4,0xFF,0xD3, 0xC5,0xFF,0xDF,

YELLOW-GREEN
    0x00,0x26,0x0B, 0x00,0x37,0x10, 0x00,0x48,0x16, 0x0A,0x59,0x18,
    0x1B,0x6A,0x18, 0x2C,0x7B,0x18, 0x3D,0x8C,0x27, 0x4E,0x9D,0x38,
    0x5F,0xAE,0x49, 0x70,0xBF,0x5A, 0x81,0xD0,0x6B, 0x92,0xE1,0x7C,
    0xA3,0xF2,0x8D, 0xB4,0xFF,0x9C, 0xC5,0xFF,0xA8, 0xD6,0xFF,0xB4,

ORANGE-GREEN
    0x00,0x1E,0x09, 0x00,0x2F,0x0E, 0x11,0x40,0x0E, 0x22,0x51,0x0E,
    0x33,0x62,0x0E, 0x44,0x73,0x0E, 0x55,0x84,0x0E, 0x66,0x95,0x17,
    0x77,0xA6,0x28, 0x88,0xB7,0x39, 0x99,0xC8,0x4A, 0xAA,0xD9,0x5B,
    0xBB,0xEA,0x6C, 0xCC,0xFB,0x7D, 0xDD,0xFF,0x8A, 0xEE,0xFF,0x96,

LIGHT-ORANGE
    0x0A,0x11,0x02, 0x1B,0x22,0x02, 0x2C,0x33,0x02, 0x3D,0x44,0x02,
    0x4E,0x55,0x02, 0x5F,0x66,0x02, 0x70,0x77,0x02, 0x81,0x88,0x09,
    0x92,0x99,0x1A, 0xA3,0xAA,0x2B, 0xB4,0xBB,0x3C, 0xC5,0xCC,0x4D,
    0xD6,0xDD,0x5E, 0xE7,0xEE,0x6F, 0xF8,0xFF,0x80, 0xFF,0xFF,0x8F,
*******************************************************************

*******************************************************************
    PALETTE - PHASE 25.2 SHIFT

GREY
    0x00,0x00,0x00, 0x11,0x11,0x11, 0x22,0x22,0x22, 0x33,0x33,0x33,
    0x44,0x44,0x44, 0x55,0x55,0x55, 0x66,0x66,0x66, 0x77,0x77,0x77,
    0x88,0x88,0x88, 0x99,0x99,0x99, 0xAA,0xAA,0xAA, 0xBB,0xBB,0xBB,
    0xCC,0xCC,0xCC, 0xDD,0xDD,0xDD, 0xEE,0xEE,0xEE, 0xFF,0xFF,0xFF,

GOLD
    0x1A,0x07,0x00, 0x2B,0x18,0x00, 0x3C,0x29,0x00, 0x4D,0x3A,0x00,
    0x5E,0x4B,0x00, 0x6F,0x5C,0x00, 0x80,0x6D,0x00, 0x91,0x7E,0x09,
    0xA2,0x8F,0x1A, 0xB3,0xA0,0x2B, 0xC4,0xB1,0x3C, 0xD5,0xC2,0x4D,
    0xE6,0xD3,0x5E, 0xF7,0xE4,0x6F, 0xFF,0xF5,0x83, 0xFF,0xF7,0x97,

ORANGE
    0x30,0x00,0x00, 0x41,0x07,0x00, 0x52,0x18,0x00, 0x63,0x29,0x00,
    0x74,0x3A,0x00, 0x85,0x4B,0x00, 0x96,0x5C,0x0A, 0xA7,0x6D,0x1B,
    0xB8,0x7E,0x2C, 0xC9,0x8F,0x3D, 0xDA,0xA0,0x4E, 0xEB,0xB1,0x5F,
    0xFC,0xC2,0x70, 0xFF,0xD3,0x85, 0xFF,0xE4,0x9B, 0xFF,0xF5,0xB1,

RED-ORANGE
    0x3E,0x00,0x00, 0x4F,0x00,0x00, 0x60,0x09,0x00, 0x71,0x1A,0x00,
    0x82,0x2B,0x0B, 0x93,0x3C,0x1C, 0xA4,0x4D,0x2D, 0xB5,0x5E,0x3E,
    0xC6,0x6F,0x4F, 0xD7,0x80,0x60, 0xE8,0x91,0x71, 0xF9,0xA2,0x82,
    0xFF,0xB3,0x96, 0xFF,0xC4,0xAC, 0xFF,0xD5,0xC2, 0xFF,0xE6,0xD8,

PINK
    0x3F,0x00,0x00, 0x50,0x00,0x0C, 0x61,0x00,0x18, 0x72,0x0F,0x28,
    0x83,0x20,0x39, 0x94,0x31,0x4A, 0xA5,0x42,0x5B, 0xB6,0x53,0x6C,
    0xC7,0x64,0x7D, 0xD8,0x75,0x8E, 0xE9,0x86,0x9F, 0xFA,0x97,0xB0,
    0xFF,0xA8,0xC5, 0xFF,0xB9,0xDB, 0xFF,0xCA,0xEF, 0xFF,0xDB,0xF4,

PURPLE
    0x35,0x00,0x31, 0x46,0x00,0x3D, 0x57,0x00,0x49, 0x68,0x0C,0x58,
    0x79,0x1D,0x69, 0x8A,0x2E,0x7A, 0x9B,0x3F,0x8B, 0xAC,0x50,0x9C,
    0xBD,0x61,0xAD, 0xCE,0x72,0xBE, 0xDF,0x83,0xCF, 0xF0,0x94,0xE0,
    0xFF,0xA5,0xE4, 0xFF,0xB6,0xE9, 0xFF,0xC7,0xEE, 0xFF,0xD8,0xF3,

PURPLE-BLUE
    0x20,0x00,0x59, 0x31,0x00,0x65, 0x42,0x00,0x71, 0x53,0x10,0x82,
    0x64,0x21,0x93, 0x75,0x32,0xA4, 0x86,0x43,0xB5, 0x97,0x54,0xC6,
    0xA8,0x65,0xD7, 0xB9,0x76,0xE8, 0xCA,0x87,0xEB, 0xDB,0x98,0xEB,
    0xEC,0xA9,0xEB, 0xFD,0xBA,0xEB, 0xFF,0xCB,0xEF, 0xFF,0xDC,0xF4,

BLUE1
    0x05,0x00,0x70, 0x16,0x00,0x7C, 0x27,0x09,0x8B, 0x38,0x1A,0x9C,
    0x49,0x2B,0xAD, 0x5A,0x3C,0xBE, 0x6B,0x4D,0xCF, 0x7C,0x5E,0xE0,
    0X8D,0x6F,0xF1, 0x9E,0x80,0xF6, 0xAF,0x91,0xF6, 0xC0,0xA2,0xF6,
    0xD1,0xB3,0xF6, 0xE2,0xC4,0xF6, 0xF3,0xD5,0xF6, 0xFF,0xE6,0xF7,

BLUE2
    0x00,0x00,0x6B, 0x00,0x08,0x7E, 0x0C,0x19,0x91, 0x1D,0x2A,0xA2,
    0x2E,0x3B,0xB3, 0x3F,0x4C,0xC4, 0x50,0x5D,0xD5, 0x61,0x6E,0xE6,
    0x72,0x7F,0xF7, 0x83,0x90,0xFF, 0x94,0xA1,0xFF, 0xA5,0xB2,0xFF,
    0xB6,0xC3,0xFF, 0xC7,0xD4,0xFF, 0xD8,0xE5,0xFF, 0xE9,0xF6,0xFF,

LIGHT-BLUE
    0x00,0x08,0x52, 0x00,0x19,0x68, 0x00,0x2A,0x7F, 0x05,0x3B,0x93,
    0x16,0x4C,0xA4, 0x27,0x5D,0xB5, 0x38,0x6E,0xC6, 0x49,0x7F,0xD7,
    0x5A,0x90,0xE8, 0x6B,0xA1,0xF9, 0x7C,0xB2,0xFF, 0x8D,0xC3,0xFF,
    0x9E,0xD4,0xFF, 0xAF,0xE5,0xFF, 0xC0,0xF6,0xFF, 0xD1,0xFF,0xFF,

TURQUOISE
    0x00,0x17,0x2D, 0x00,0x28,0x43, 0x00,0x39,0x59, 0x00,0x4A,0x6F,
    0x08,0x5B,0x83, 0x19,0x6C,0x94, 0x2A,0x7D,0xA5, 0x3B,0x8E,0xB6,
    0x4C,0x9F,0xC7, 0x5D,0xB0,0xD8, 0x6E,0xC1,0xE9, 0x7F,0xD2,0xFA,
    0x90,0xE3,0xFF, 0xA1,0xF4,0xFF, 0xB2,0xFF,0xFF, 0xC3,0xFF,0xFF,

GREEN-BLUE
    0x00,0x23,0x0A, 0x00,0x34,0x15, 0x00,0x45,0x2B, 0x00,0x56,0x41,
    0x04,0x67,0x56, 0x15,0x78,0x67, 0x26,0x89,0x78, 0x37,0x9A,0x89,
    0x48,0xAB,0x9A, 0x59,0xBC,0xAB, 0x6A,0xCD,0xBC, 0x7B,0xDE,0xCD,
    0x8C,0xEF,0xDE, 0x9D,0xFF,0xEE, 0xAE,0xFF,0xFA, 0xBF,0xFF,0xFF,

GREEN
    0x00,0x27,0x0C, 0x00,0x38,0x11, 0x00,0x49,0x16, 0x00,0x5A,0x1B,
    0x0D,0x6B,0x25, 0x1E,0x7C,0x36, 0x2F,0x8D,0x47, 0x40,0x9E,0x58,
    0x51,0xAF,0x69, 0x62,0xC0,0x7A, 0x73,0xD1,0x8B, 0x84,0xE2,0x9C,
    0x95,0xF3,0xAD, 0xA6,0xFF,0xBD, 0xB7,0xFF,0xC9, 0xC8,0xFF,0xD4,

YELLOW-GREEN
    0x00,0x24,0x0B, 0x00,0x35,0x10, 0x00,0x46,0x15, 0x10,0x57,0x15,
    0x21,0x68,0x15, 0x32,0x79,0x15, 0x43,0x8A,0x1C, 0x54,0x9B,0x2D,
    0x65,0xAC,0x3E, 0x76,0xBD,0x4F, 0x87,0xCE,0x60, 0x98,0xDF,0x71,
    0xA9,0xF0,0x82, 0xBA,0xFF,0x93, 0xCB,0xFF,0x9F, 0xDC,0xFF,0xAA,

ORANGE-GREEN
    0x00,0x1B,0x08, 0x08,0x2C,0x0B, 0x19,0x3D,0x0B, 0x2A,0x4E,0x0B,
    0x3B,0x5F,0x0B, 0x4C,0x70,0x0B, 0x5D,0x81,0x0B, 0x6E,0x92,0x11,
    0x7F,0xA3,0x22, 0x90,0xB4,0x33, 0xA1,0xC5,0x44, 0xB2,0xD6,0x55,
    0xC3,0xE7,0x66, 0xD4,0xF8,0x77, 0xE5,0xFF,0x85, 0xF6,0xFF,0x91,

LIGHT-ORANGE
    0x12,0x0C,0x00, 0x23,0x1D,0x00, 0x34,0x2E,0x00, 0x45,0x3F,0x00,
    0x56,0x50,0x00, 0x67,0x61,0x00, 0x78,0x72,0x00, 0x89,0x83,0x08,
    0x9A,0x94,0x19, 0xAB,0xA5,0x2A, 0xBC,0xB6,0x3B, 0xCD,0xC7,0x4C,
    0xDE,0xD8,0x5D, 0xEF,0xE9,0x6E, 0xFF,0xFA,0x80, 0xFF,0xFF,0x92,
*******************************************************************

*******************************************************************
    PALETTE - PHASE 25.7 SHIFT
GREY
    0x00,0x00,0x00, 0x11,0x11,0x11, 0x22,0x22,0x22, 0x33,0x33,0x33,
    0x44,0x44,0x44, 0x55,0x55,0x55, 0x66,0x66,0x66, 0x77,0x77,0x77,
    0x88,0x88,0x88, 0x99,0x99,0x99, 0xAA,0xAA,0xAA, 0xBB,0xBB,0xBB,
    0xCC,0xCC,0xCC, 0xDD,0xDD,0xDD, 0xEE,0xEE,0xEE, 0xFF,0xFF,0xFF,

GOLD
    0x1A,0x07,0x00, 0x2B,0x18,0x00, 0x3C,0x29,0x00, 0x4D,0x3A,0x00,
    0x5E,0x4B,0x00, 0x6F,0x5C,0x00, 0x80,0x6D,0x00, 0x91,0x7E,0x09,
    0xA2,0x8F,0x1A, 0xB3,0xA0,0x2B, 0xC4,0xB1,0x3C, 0xD5,0xC2,0x4D,
    0xE6,0xD3,0x5E, 0xF7,0xE4,0x6F, 0xFF,0xF5,0x83, 0xFF,0xF7,0x97,

ORANGE
    0x31,0x00,0x00, 0x42,0x06,0x00, 0x53,0x17,0x00, 0x64,0x28,0x00,
    0x75,0x39,0x00, 0x86,0X4A,0x00, 0x97,0x5B,0x0A, 0xA8,0x6C,0x1B,
    0xB9,0x7D,0x2C, 0xCA,0x8E,0x3D, 0xDB,0x9F,0x4E, 0xEC,0xB0,0x5F,
    0xFD,0xC1,0x70, 0xFF,0xD2,0x85, 0xFF,0xE3,0x9C, 0xFF,0xF4,0xB2,

RED-ORANGE
    0x3E,0x00,0x00, 0x4F,0x00,0x00, 0x60,0x08,0x00, 0x71,0x19,0x00,
    0x82,0x2A,0x0D, 0x93,0x3B,0x1E, 0xA4,0x4C,0x2F, 0xB5,0x5D,0x40,
    0xC6,0x6E,0x51, 0xD7,0x7F,0x62, 0xE8,0x90,0x73, 0xF9,0xA1,0x83,
    0xFF,0xB2,0x98, 0xFF,0xC3,0xAE, 0xFF,0xD4,0xC4, 0xFF,0xE5,0xDA,

PINK
    0x3F,0x00,0x03, 0x50,0x00,0x0F, 0x61,0x00,0x1B, 0x72,0x0F,0x2B,
    0x83,0x20,0x3C, 0x94,0x31,0x4D, 0xA5,0x42,0x5E, 0xB6,0x53,0x6F,
    0xC7,0x64,0x80, 0xD8,0x75,0x91, 0xE9,0x86,0xA2, 0xFA,0x97,0xB3,
    0xFF,0xA8,0xC8, 0xFF,0xB9,0xDE, 0xFF,0xCA,0xEF, 0xFF,0xDB,0xF4,

PURPLE
    0x33,0x00,0x35, 0x44,0x00,0x41, 0x55,0x00,0x4C, 0x66,0x0C,0x5C,
    0x77,0x1D,0x6D, 0x88,0x2E,0x7E, 0x99,0x3F,0x8F, 0xAA,0x50,0xA0,
    0xBB,0x61,0xB1, 0xCC,0x72,0xC2, 0xDD,0x83,0xD3, 0xEE,0x94,0xE4,
    0xFF,0xA5,0xE4, 0xFF,0xB6,0xE9, 0xFF,0xC7,0xEE, 0xFF,0xD8,0xF3,

PURPLE-BLUE
    0x1D,0x00,0x5C, 0x2E,0x00,0x68, 0x40,0x00,0x74, 0x51,0x10,0x84,
    0x62,0x21,0x95, 0x73,0x32,0xA6, 0x84,0x43,0xB7, 0x95,0x54,0xC8,
    0xA6,0x65,0xD9, 0xB7,0x76,0xEA, 0xC8,0x87,0xEB, 0xD9,0x98,0xEB,
    0xE9,0xA9,0xEC, 0xFB,0xBA,0xEB, 0xFF,0xCB,0xEF, 0xFF,0xDC,0xF4,

BLUE1
    0x02,0x00,0x71, 0x13,0x00,0x7D, 0x24,0x0B,0x8C, 0x35,0x1C,0x9D,
    0x46,0x2D,0xAE, 0x57,0x3E,0xBF, 0x68,0x4F,0xD0, 0x79,0x60,0xE1,
    0x8A,0x71,0xF2, 0x9B,0x82,0xF7, 0xAC,0x93,0xF7, 0xBD,0xA4,0xF7,
    0xCE,0xB5,0xF7, 0xDF,0xC6,0xF7, 0xF0,0xD7,0xF7, 0xFF,0xE8,0xF8,

BLUE2
    0x00,0x00,0x68, 0x00,0x0A,0x7C, 0x08,0x1B,0x90, 0x19,0x2C,0xA1,
    0x2A,0x3D,0xB2, 0x3B,0x4E,0xC3, 0x4C,0x5F,0xD4, 0x5D,0x70,0xE5,
    0x6E,0x81,0xF6, 0x7F,0x92,0xFF, 0x90,0xA3,0xFF, 0xA1,0xB4,0xFF,
    0xB2,0xC5,0xFF, 0xC3,0xD6,0xFF, 0xD4,0xE7,0xFF, 0xE5,0xF8,0xFF,

LIGHT-BLUE
    0x00,0x0A,0x4D, 0x00,0x1B,0x63, 0x00,0x2C,0x79, 0x02,0x3D,0x8F,
    0x13,0x4E,0xA0, 0x24,0x5F,0xB1, 0x35,0x70,0xC2, 0x46,0x81,0xD3,
    0x57,0x92,0xE4, 0x68,0xA3,0xF5, 0x79,0xB4,0xFF, 0x8A,0xC5,0xFF,
    0x9B,0xD6,0xFF, 0xAC,0xE7,0xFF, 0xBD,0xF8,0xFF, 0xCE,0xFF,0xFF,

TURQUOISE
    0x00,0x1A,0x26, 0x00,0x2B,0x3C, 0x00,0x3C,0x52, 0x00,0x4D,0x68,
    0x06,0x5E,0x7C, 0x17,0x6F,0x8D, 0x28,0x80,0x9E, 0x39,0x91,0xAF,
    0x4A,0xA2,0xC0, 0x5B,0xB3,0xD1, 0x6C,0xC4,0xE2, 0x7D,0xD5,0xF3,
    0x8E,0xE6,0xFF, 0x9F,0xF7,0xFF, 0xB0,0xFF,0xFF, 0xC1,0xFF,0xFF,

GREEN-BLUE
    0x00,0x24,0x0B, 0x00,0x35,0x10, 0x00,0x46,0x22, 0x00,0x57,0x38,
    0x05,0x68,0x4D, 0x16,0x79,0x5E, 0x27,0x8A,0x6F, 0x38,0x9B,0x80,
    0x49,0xAC,0x91, 0x5A,0xBD,0xA2, 0x6B,0xCE,0xB3, 0x7C,0xDF,0xC4,
    0x8D,0xF0,0xD5, 0x9E,0xFF,0xE5, 0xAF,0xFF,0xF1, 0xC0,0xFF,0xFD,

GREEN
    0x00,0x27,0x0C, 0x00,0x38,0x11, 0x00,0x49,0x16, 0x00,0x5A,0x1B,
    0x10,0x6B,0x1B, 0x21,0x7C,0x2C, 0x32,0x8D,0x3D, 0x43,0x9E,0x4E,
    0x54,0xAF,0x5F, 0x65,0xC0,0x70, 0x76,0xD1,0x81, 0x87,0xE2,0x92,
    0x98,0xF3,0xA3, 0xA9,0xFF,0xB3, 0xBA,0xFF,0xBF, 0xCB,0xFF,0xCB,

YELLOW-GREEN
    0x00,0x23,0x0A, 0x00,0x34,0x10, 0x04,0x45,0x13, 0x15,0x56,0x13,
    0x26,0x67,0x13, 0x37,0x78,0x13, 0x48,0x89,0x14, 0x59,0x9A,0x25,
    0x6A,0xAB,0x36, 0x7B,0xBC,0x47, 0x8C,0xCD,0x58, 0x9D,0xDE,0x69,
    0xAE,0xEF,0x7A, 0xBF,0xFF,0x8B, 0xD0,0xFF,0x97, 0xE1,0xFF,0xA3,

ORANGE-GREEN
    0x00,0x17,0x07, 0x0E,0x28,0x08, 0x1F,0x39,0x08, 0x30,0x4A,0x08,
    0x41,0x5B,0x08, 0x52,0x6C,0x08, 0x63,0x7D,0x08, 0x74,0x8E,0x0D,
    0x85,0x9F,0x1E, 0x96,0xB0,0x2F, 0xA7,0xC1,0x40, 0xB8,0xD2,0x51,
    0xC9,0xE3,0x62, 0xDA,0xF4,0x73, 0xEB,0xFF,0x82, 0xFC,0xFF,0x8E,

LIGHT-ORANGE
    0x19,0x07,0x00, 0x2A,0x18,0x00, 0x3B,0x29,0x00, 0x4C,0x3A,0x00,
    0x5D,0x4B,0x00, 0x6E,0x5C,0x00, 0x7F,0x6D,0x00, 0x90,0x7E,0x09,
    0xA1,0x8F,0x1A, 0xB2,0xA0,0x2B, 0xC3,0xB1,0x3C, 0xD4,0xC2,0x4D,
    0xE5,0xD3,0x5E, 0xF6,0xE4,0x6F, 0xFF,0xF5,0x82, 0xFF,0xFF,0x96,
*******************************************************************

*******************************************************************
    PALETTE - PHASE 26.7 SHIFT

GREY
    0x00,0x00,0x00, 0x11,0x11,0x11, 0x22,0x22,0x22, 0x33,0x33,0x33,
    0x44,0x44,0x44, 0x55,0x55,0x55, 0x66,0x66,0x66, 0x77,0x77,0x77,
    0x88,0x88,0x88, 0x99,0x99,0x99, 0xAA,0xAA,0xAA, 0xBB,0xBB,0xBB,
    0xCC,0xCC,0xCC, 0xDD,0xDD,0xDD, 0xEE,0xEE,0xEE, 0xFF,0xFF,0xFF,

GOLD
    0x1A,0x07,0x00, 0x2B,0x18,0x00, 0x3C,0x29,0x00, 0x4D,0x3A,0x00,
    0x5E,0x4B,0x00, 0x6F,0x5C,0x00, 0x80,0x6D,0x00, 0x91,0x7E,0x09,
    0xA2,0x8F,0x1A, 0xB3,0xA0,0x2B, 0xC4,0xB1,0x3C, 0xD5,0xC2,0x4D,
    0xE6,0xD3,0x5E, 0xF7,0xE4,0x6F, 0xFF,0xF5,0x83, 0xFF,0xFF,0x97,

ORANGE
    0x32,0x00,0x00, 0x43,0x06,0x00, 0x54,0x17,0x00, 0x65,0x28,0x00,
    0x79,0x39,0x00, 0x87,0x4A,0x00, 0x98,0x5B,0x0C, 0xA9,0x6C,0x1D,
    0xBA,0x7D,0x2E, 0xCB,0x8E,0x3F, 0xDC,0x9F,0x50, 0xED,0xB0,0x61,
    0xFE,0xC1,0x72, 0xFF,0xD2,0x87, 0xFF,0xE3,0x9E, 0xFF,0xF4,0xB4,

RED-ORANGE
    0x3E,0x00,0x00, 0x4F,0x00,0x00, 0x60,0x07,0x00, 0x71,0x18,0x00,
    0x82,0x29,0x10, 0x93,0x3A,0x21, 0xA4,0x4B,0x32, 0xB5,0x5C,0x43,
    0xC6,0x6D,0x54, 0xD7,0x7E,0x65, 0xE8,0x8F,0x76, 0xF9,0xA0,0x87,
    0xFF,0xB1,0x9C, 0xFF,0xC2,0xB2, 0xFF,0xD3,0xC8, 0xFF,0xE4,0xDE,

PINK
    0x3E,0x00,0x09, 0x4F,0x00,0x15, 0x60,0x00,0x21, 0x71,0x0E,0x31,
    0x82,0x1F,0x42, 0x93,0x30,0x53, 0xA4,0x41,0x64, 0xB5,0x52,0x75,
    0xC6,0x63,0x86, 0xD7,0x74,0x97, 0xE8,0x85,0xA8, 0xF9,0x96,0xB9,
    0xFF,0xA7,0xCE, 0xFF,0xB8,0xE4, 0xFF,0xC9,0xEF, 0xFF,0xDA,0xF4,

PURPLE
    0x30,0x00,0x3D, 0x41,0x00,0x48, 0x52,0x00,0x54, 0x63,0x0C,0x64,
    0x74,0x1D,0x75, 0x85,0x2E,0x86, 0x96,0x3F,0x97, 0xA7,0x50,0xA8,
    0xB8,0x61,0xB9, 0xC9,0x72,0xCA, 0xDA,0x83,0xDB, 0xEB,0x94,0xE5,
    0xFC,0xA5,0xE5, 0xFF,0xB6,0xE9, 0xFF,0xC7,0xEE, 0xFF,0xD8,0xF3,

PURPLE-BLUE
    0x18,0x00,0x62, 0x29,0x00,0x6E, 0x3A,0x01,0x7A, 0x4B,0x12,0x8B,
    0x5C,0x23,0x9C, 0x6D,0x34,0xAD, 0x7E,0x45,0xBE, 0x8F,0x56,0xCF,
    0xA0,0x67,0xE0, 0xB1,0x78,0xEE, 0xC2,0x89,0xEE, 0xD3,0x9A,0xEE,
    0xE4,0xAB,0xEE, 0xF5,0xBC,0xEE, 0xFF,0xCD,0xE0, 0xFF,0xDE,0xF5,

BLUE1
    0x00,0x00,0x72, 0x0C,0x00,0x7F, 0x1D,0x0E,0x8F, 0x2E,0x1F,0xA0,
    0x3F,0x30,0xB1, 0x50,0x41,0xC2, 0x61,0x52,0xD3, 0x72,0x63,0xE4,
    0x83,0x74,0xF5, 0x94,0x85,0xFA, 0xA5,0x96,0xFA, 0xB6,0xA7,0xFA,
    0xC7,0xB8,0xFA, 0xD8,0xC9,0xFA, 0xE9,0xDA,0xFA, 0xFA,0xE8,0xFA,

BLUE2
    0x00,0x00,0x62, 0x00,0x0F,0x77, 0x01,0x20,0x8D, 0x12,0x31,0x9E,
    0x23,0x42,0xAF, 0x34,0x53,0xC0, 0x45,0x64,0xD1, 0x56,0x75,0xE2,
    0x67,0x86,0xF3, 0x78,0x97,0xFF, 0x89,0xA8,0xFF, 0x9A,0xB9,0xFF,
    0xAB,0xCA,0xFF, 0xBC,0xDB,0xFF, 0xCD,0xEC,0xFF, 0xDE,0xFD,0xFF,

LIGHT-BLUE
    0x00,0x10,0x42, 0x00,0x21,0x58, 0x00,0x32,0x6E, 0x00,0x43,0x84,
    0x0E,0x54,0x96, 0x1F,0x65,0xA7, 0x30,0x76,0xB8, 0x41,0x87,0xC9,
    0x52,0x98,0xDA, 0x63,0xA9,0xEB, 0x74,0xBA,0xFC, 0x85,0xCB,0xFF,
    0x96,0xDC,0xFF, 0xA7,0xED,0xFF, 0xB8,0xFE,0xFF, 0xC9,0xFF,0xFF,

TURQUOISE
    0x00,0x1E,0x14, 0x00,0x2F,0x2A, 0x00,0x40,0x40, 0x00,0x51,0x56,
    0x04,0x62,0x6B, 0x15,0x73,0x7C, 0x26,0x84,0x8D, 0x37,0x95,0x9E,
    0x48,0xA6,0xAF, 0x59,0xB7,0xC0, 0x6A,0xC8,0xD1, 0x7B,0xD9,0xE2,
    0x8C,0xEA,0xF3, 0x9D,0xFB,0xFF, 0xAE,0xFF,0xFF, 0xBF,0xFF,0xFF,

GREEN-BLUE
    0x00,0x26,0x0B, 0x00,0x37,0x10, 0x00,0x48,0x16, 0x00,0x59,0x25,
    0x08,0x6A,0x38, 0x19,0x7B,0x49, 0x2A,0x8C,0x5A, 0x3B,0x9D,0x6B,
    0x4C,0xAE,0x7C, 0x5D,0xBF,0x8D, 0x6E,0xD0,0x9E, 0x7F,0xE1,0xAF,
    0x90,0xF2,0xC0, 0xA1,0xFF,0xD0, 0xB2,0xFF,0xDC, 0xC3,0xFF,0xE8,

GREEN
    0x00,0x26,0x0B, 0x00,0x37,0x10, 0x00,0x48,0x16, 0x08,0x59,0x18,
    0x19,0x6A,0x18, 0x2A,0x7B,0x18, 0x3B,0x8C,0x29, 0x4C,0x9D,0x3A,
    0x5D,0xAE,0x4B, 0x6E,0xBF,0x5C, 0x7F,0xD0,0x6D, 0x90,0xE1,0x7E,
    0xA1,0xF2,0x8F, 0xB2,0xFF,0x9F, 0xC3,0xFF,0xAB, 0xD4,0xFF,0xB7,

YELLOW-GREEN
    0x00,0x1E,0x09, 0x00,0x2F,0x0E, 0x11,0x40,0x0E, 0x22,0x51,0x0E,
    0x33,0x62,0x0E, 0x44,0x73,0x0E, 0x55,0x84,0x0E, 0x66,0x95,0x17,
    0x77,0xA6,0x28, 0x88,0xB7,0x39, 0x99,0xC8,0x4A, 0xAA,0xD9,0x5B,
    0xBB,0xEA,0x6C, 0xCC,0xFB,0x7D, 0xDD,0xFF,0x8A, 0xEE,0xFF,0x96,

ORANGE-GREEN
    0x0D,0x0F,0x01, 0x1E,0x20,0x01, 0x2F,0x31,0x01, 0x40,0x42,0x01,
    0x51,0x53,0x01, 0x62,0x64,0x01, 0x73,0x75,0x01, 0x84,0x86,0x08,
    0x95,0x97,0x19, 0xA6,0xA8,0x2A, 0xB7,0xB9,0x3B, 0xC8,0xCA,0x4C,
    0xD9,0xDB,0x5D, 0xEA,0xEC,0x6E, 0xFB,0xFD,0x7F, 0xFF,0xFF,0x8F,

LIGHT-ORANGE
    0x28,0x00,0x00, 0x39,0x0E,0x00, 0x4A,0x1F,0x00, 0x5B,0x30,0x00,
    0x6C,0x41,0x00, 0x7D,0x52,0x00, 0x8E,0x63,0x00, 0x9F,0x74,0x10,
    0xB0,0x85,0x21, 0xC1,0x96,0x32, 0xD2,0xA7,0x43, 0xE3,0xB8,0x54,
    0xF4,0xC9,0x65, 0xFF,0xDA,0x78, 0xFF,0xEB,0x8E, 0xFF,0xFC,0xA4,
*******************************************************************

*******************************************************************
    PALETTE - PHASE 27.2 SHIFT


GREY
    0x00,0x00,0x00, 0x11,0x11,0x11, 0x22,0x22,0x22, 0x33,0x33,0x33,
    0x44,0x44,0x44, 0x55,0x55,0x55, 0x66,0x66,0x66, 0x77,0x77,0x77,
    0x88,0x88,0x88, 0x99,0x99,0x99, 0xAA,0xAA,0xAA, 0xBB,0xBB,0xBB,
    0xCC,0xCC,0xCC, 0xDD,0xDD,0xDD, 0xEE,0xEE,0xEE, 0xFF,0xFF,0xFF,

GOLD
    0x1A,0x07,0x00, 0x2B,0x18,0x00, 0x3C,0x29,0x00, 0x4D,0x3A,0x00,
    0x5E,0x4B,0x00, 0x6F,0x5C,0x00, 0x80,0x6D,0x00, 0x91,0x7E,0x09,
    0xA2,0x8F,0x1A, 0xB3,0xA0,0x2B, 0xC4,0xB1,0x3C, 0xD5,0xC2,0x4D,
    0xE6,0xD3,0x5E, 0xF7,0xE4,0x6F, 0xFF,0xF5,0x83, 0xFF,0xF7,0x97,

ORANGE
    0x32,0x00,0x00, 0x43,0x05,0x00, 0x54,0x16,0x00, 0x65,0x27,0x00,
    0x76,0x38,0x00, 0x87,0X49,0x00, 0x98,0x5A,0x0C, 0xA9,0x6B,0x1D,
    0xBA,0x7C,0x2E, 0xCB,0x8D,0x3F, 0xDC,0x9E,0x50, 0xED,0xAF,0x61,
    0xFE,0xC0,0x72, 0xFF,0xD1,0x88, 0xFF,0xE2,0x9E, 0xFF,0xF3,0xB4,

RED-ORANGE
    0x3F,0x00,0x00, 0x50,0x00,0x00, 0x61,0x07,0x00, 0x72,0x18,0x01,
    0x83,0x29,0x12, 0x94,0x3A,0x23, 0xA5,0x4B,0x34, 0xB6,0x5C,0x45,
    0xC7,0x6D,0x56, 0xD8,0x7E,0x67, 0xE9,0x8F,0x78, 0xFA,0xA0,0x89,
    0xFF,0xB1,0x9E, 0xFF,0xC2,0xB4, 0xFF,0xD3,0xCA, 0xFF,0xE4,0xE0,

PINK
    0x3E,0x00,0x0C, 0x4F,0x00,0x18, 0x60,0x00,0x24, 0x71,0x0E,0x34,
    0x82,0x1F,0x45, 0x93,0x30,0x56, 0xA4,0x41,0x67, 0xB5,0x52,0x78,
    0xC6,0x63,0x89, 0xD7,0x74,0x9A, 0xE8,0x85,0xAB, 0xF9,0x96,0xB6,
    0xFF,0xA7,0xD1, 0xFF,0xB8,0xE7, 0xFF,0xC9,0xEF, 0xFF,0xDA,0xF4,

PURPLE
    0x2F,0x00,0x3F, 0x40,0x00,0x4B, 0x51,0x00,0x57, 0x62,0x0C,0x66,
    0x73,0x1D,0x77, 0x84,0x2E,0x88, 0x95,0x3F,0x99, 0xA6,0x50,0xAA,
    0xB7,0x61,0xBB, 0xC8,0x72,0xCC, 0xD9,0x83,0xDD, 0xEA,0x94,0xE5,
    0xFB,0xA5,0xE5, 0xFF,0xB6,0xE9, 0xFF,0xC7,0xEE, 0xFF,0xD8,0xF3,

PURPLE-BLUE
    0x16,0x00,0x64, 0x27,0x00,0x70, 0x38,0x02,0x7D, 0x49,0x13,0x8E,
    0x5A,0x24,0x9F, 0x6B,0x35,0xB0, 0x7C,0x46,0xC1, 0x8D,0x57,0xD2,
    0x9E,0x68,0xE3, 0xAF,0x79,0xEF, 0xC0,0x8A,0xEF, 0xD1,0x9D,0xEF,
    0xE2,0xAC,0xEF, 0xF3,0xBD,0xEF, 0xFF,0xCE,0xF0, 0xFF,0xDF,0xF5,

BLUE1
    0x00,0x00,0x71, 0x09,0x00,0x7F, 0x1A,0x10,0x90, 0x2B,0x21,0xA1,
    0x3C,0x32,0xB2, 0x4D,0x43,0xC3, 0x5E,0x54,0xD4, 0x6F,0x65,0xE5,
    0x80,0x76,0xF6, 0x91,0x87,0xFC, 0xA2,0x98,0xFC, 0xB3,0xA9,0xFC,
    0xC4,0xBA,0xFC, 0xD5,0xCB,0xFC, 0xE6,0xDC,0xFC, 0xF7,0xED,0xFC,

BLUE2
    0x00,0x00,0x5E, 0x00,0x11,0x74, 0x00,0x22,0x8A, 0x0F,0x33,0x9C,
    0x20,0x44,0xAD, 0x31,0x55,0xBE, 0x42,0x66,0xCF, 0x53,0x77,0xE0,
    0x64,0x88,0xF1, 0x75,0x99,0xFF, 0x86,0xAA,0xFF, 0x97,0xBB,0xFF,
    0xA8,0xCC,0xFF, 0xB9,0xDD,0xFF, 0xCA,0xEE,0xFF, 0xDB,0xFF,0xFF,

LIGHT-BLUE
    0x00,0x12,0x3B, 0x00,0x23,0x51, 0x00,0x34,0x68, 0x00,0x45,0x7E,
    0x0C,0x56,0x90, 0x1D,0x67,0xA1, 0x2E,0x78,0xB2, 0x3F,0x89,0xC3,
    0x50,0x9A,0xD4, 0x61,0xAB,0xE5, 0x72,0xBC,0xF6, 0x83,0xCD,0xFF,
    0x94,0xDE,0xFF, 0xA5,0xEF,0xFF, 0xB6,0xFF,0xFF, 0xC7,0xFF,0xFF,

TURQUOISE
    0x00,0x20,0x0C, 0x00,0x31,0x22, 0x00,0x42,0x38, 0x00,0x53,0x4E,
    0x04,0x64,0x63, 0x15,0x75,0x74, 0x26,0x86,0x85, 0x37,0x97,0x96,
    0x48,0xA8,0xA7, 0x59,0xB9,0xB8, 0x6A,0xCA,0xC9, 0x7B,0xDB,0xDA,
    0x8C,0xEC,0xEB, 0x9D,0xFD,0xFC, 0xAE,0xFF,0xFF, 0xBF,0xFF,0xFF,

GREEN-BLUE
    0x00,0x27,0x0C, 0x00,0x38,0x11, 0x00,0x49,0x16, 0x00,0x5A,0x1C,
    0x0B,0x6B,0x2F, 0x1C,0x7C,0x40, 0x2D,0x8D,0x51, 0x3E,0x9E,0x62,
    0x4F,0xAF,0x73, 0x60,0xC0,0x84, 0x71,0xD1,0x95, 0x82,0xE2,0xA6,
    0x93,0xF3,0xB7, 0xA4,0xFF,0xC6, 0xB5,0xFF,0xD2, 0xC6,0xFF,0xDE,

GREEN
    0x00,0x25,0x0B, 0x00,0x36,0x10, 0x00,0x47,0x15, 0x0D,0x58,0x16,
    0x1E,0x69,0x16, 0x2F,0x7A,0x16, 0x40,0x8B,0x21, 0x51,0x9C,0x32,
    0x62,0xAD,0x43, 0x73,0xBE,0x54, 0x84,0xCF,0x65, 0x95,0xE0,0x76,
    0xA6,0xF1,0x87, 0xB7,0xFF,0x98, 0xC8,0xFF,0xA3, 0xD9,0xFF,0xAF,

YELLOW-GREEN
    0x00,0x1B,0x08, 0x06,0x2C,0x0B, 0x17,0x3D,0x0B, 0x28,0x4E,0x0B,
    0x39,0x5F,0x0B, 0x4A,0x70,0x0B, 0x5B,0x81,0x0B, 0x6C,0x92,0x12,
    0x7D,0xA3,0x23, 0x8E,0xB4,0x34, 0x9F,0xC5,0x45, 0xB0,0xD6,0x56,
    0xC1,0xE7,0x67, 0xD2,0xF8,0x78, 0xE3,0xFF,0x86, 0xF4,0xFF,0x92,

ORANGE-GREEN
    0x13,0x0B,0x00, 0x24,0x1C,0x00, 0x35,0x2D,0x00, 0x46,0x3E,0x00,
    0x57,0x4F,0x00, 0x68,0x60,0x00, 0x79,0x71,0x00, 0x8A,0x82,0x08,
    0x9B,0x93,0x19, 0xAC,0xA4,0x2A, 0xBD,0xB5,0x3B, 0xCE,0xC6,0x4C,
    0xDF,0xD7,0x5D, 0xF0,0xE8,0x6E, 0xFF,0xF9,0x7F, 0xFF,0xFF,0x92,

LIGHT-ORANGE
    0x2D,0x00,0x00, 0x3E,0x0A,0x00, 0x4F,0x1B,0x00, 0x60,0x2C,0x00,
    0x71,0x3D,0x00, 0x82,0x4E,0x00, 0x93,0x5F,0x05, 0xA4,0x70,0x16,
    0xB5,0x81,0x27, 0xC4,0x90,0x37, 0xD7,0xA3,0x49, 0xE8,0xB4,0x5A,
    0xF9,0xC5,0x6B, 0xFF,0xD6,0x80, 0xFF,0xE7,0x96, 0xFF,0xF8,0xAC,
*******************************************************************

*******************************************************************
    PALETTE - 27.7 PHASE SHIFT

GREY
    0x00,0x00,0x00, 0x11,0x11,0x11, 0x22,0x22,0x22, 0x33,0x33,0x33,
    0x44,0x44,0x44, 0x55,0x55,0x55, 0x66,0x66,0x66, 0x77,0x77,0x77,
    0x88,0x88,0x88, 0x99,0x99,0x99, 0xAA,0xAA,0xAA, 0xBB,0xBB,0xBB,
    0xCC,0xCC,0xCC, 0xDD,0xDD,0xDD, 0xEE,0xEE,0xEE, 0xFF,0xFF,0xFF,

GOLD
    0x1A,0x07,0x00, 0x2B,0x18,0x00, 0x3C,0x29,0x00, 0x4D,0x3A,0x00,
    0x5E,0x4B,0x00, 0x6F,0x5C,0x00, 0x80,0x6D,0x00, 0x91,0x7E,0x09,
    0xA2,0x8F,0x1A, 0xB3,0xA0,0x2B, 0xC4,0xB1,0x3C, 0xD5,0xC2,0x4D,
    0xE6,0xD3,0x5E, 0xF7,0xE4,0x6F, 0xFF,0xF5,0x83, 0xFF,0xFF,0x97,

ORANGE
    0x32,0x00,0x00, 0x43,0x05,0x00, 0x54,0x16,0x00, 0x65,0x27,0x00,
    0x76,0x38,0x00, 0x87,0x49,0x00, 0x98,0x5A,0x0C, 0xA9,0x6B,0x1D,
    0xBA,0x7C,0x2E, 0xCB,0x8D,0x3F, 0xDC,0x9E,0x50, 0xED,0xAF,0x61,
    0xFE,0xC0,0x72, 0xFF,0xD1,0x88, 0xFF,0xE2,0x9E, 0xFF,0xF3,0xB4,

RED-ORANGE
    0x3F,0x00,0x00, 0x50,0x00,0x00, 0x61,0x06,0x00, 0x72,0x17,0x03,
    0x83,0x28,0x14, 0x94,0x39,0x25, 0xA5,0x4A,0x36, 0xB6,0x5B,0x47,
    0xC7,0x6C,0x58, 0xD8,0x7D,0x69, 0xE9,0x8E,0x7A, 0xFA,0x9F,0x8B,
    0xFF,0xB0,0x9F, 0xFF,0xC1,0xB5, 0xFF,0xD2,0xCB, 0xFF,0xE3,0xE1,

PINK
    0x3D,0x00,0x10, 0x4E,0x00,0x1C, 0x5F,0x00,0x27, 0x70,0x0D,0x37,
    0x81,0x1E,0x48, 0x92,0x2F,0x59, 0xA3,0x40,0x6A, 0xB4,0x51,0x7B,
    0xC5,0x62,0x8C, 0xD6,0x73,0x9D, 0xE7,0x84,0xAE, 0xF8,0x95,0xBF,
    0xFF,0xA6,0xD3, 0xFF,0xB7,0xE9, 0xFF,0xC8,0xEE, 0xFF,0xD9,0xF4,

PURPLE
    0x2D,0x00,0x42, 0x3E,0x00,0x4E, 0x4F,0x00,0x5A, 0x60,0x0C,0x6A,
    0x71,0x1D,0x7B, 0x82,0x2E,0x8C, 0x93,0x3F,0x9D, 0xA4,0x50,0xAE,
    0xB5,0x61,0xBF, 0xC6,0x72,0xD0, 0xD7,0x83,0xE1, 0xE8,0x94,0xE6,
    0xF9,0xA5,0xE6, 0xFF,0xB6,0xE9, 0xFF,0xC7,0xEE, 0xFF,0xD8,0xF3,

PURPLE-BLUE
    0x13,0x00,0x67, 0x24,0x00,0x73, 0x35,0x03,0x80, 0x46,0x14,0x91,
    0x57,0x25,0xA2, 0x68,0x36,0xB3, 0x79,0x47,0xC4, 0x8A,0x58,0xD5,
    0x9B,0x69,0xE6, 0xAC,0x7A,0xF0, 0xBD,0x8B,0xF0, 0xCE,0x9C,0xF0,
    0xDF,0xAD,0xF0, 0xF0,0xBE,0xF0, 0xFF,0xCF,0xF1, 0xFF,0xE0,0xF6,

BLUE1
    0x00,0x00,0x70, 0x05,0x01,0x80, 0x16,0x12,0x91, 0x27,0x23,0xA2,
    0x38,0x34,0xB3, 0x49,0x45,0xC4, 0x5A,0x56,0xD5, 0x6B,0x67,0xE6,
    0x7C,0x78,0xF7, 0x8D,0x89,0xFE, 0x9E,0x9A,0xFE, 0xAF,0xAB,0xFE,
    0xC0,0xBC,0xFE, 0xD1,0xCD,0xFE, 0xE2,0xDE,0xFE, 0xF3,0xEF,0xFE,

BLUE2
    0x00,0x03,0x5B, 0x00,0x14,0x71, 0x00,0x25,0x87, 0x0C,0x36,0x9A,
    0x1D,0x47,0xAB, 0x2E,0x58,0xBC, 0x3F,0x69,0xCD, 0x50,0x7A,0xDE,
    0x61,0x8B,0xEF, 0x72,0x9C,0xFF, 0x83,0xAD,0xFF, 0x94,0xBE,0xFF,
    0xA5,0xCF,0xFF, 0xB6,0xE0,0xFF, 0xC7,0xF1,0xFF, 0xD8,0xFF,0xFF,

LIGHT-BLUE
    0x00,0x15,0x35, 0x00,0x26,0x4B, 0x00,0x37,0x61, 0x00,0x48,0x78,
    0x0A,0x59,0x8B, 0x1B,0x6A,0x9C, 0x2C,0x7B,0xAD, 0x3D,0x8C,0xBE,
    0x4E,0x9D,0xCF, 0x5F,0xAE,0xE0, 0x70,0xBF,0xF1, 0x81,0xD0,0xFF,
    0x92,0xE1,0xFF, 0xA3,0xF2,0xFF, 0xB4,0xFF,0xFF, 0xC5,0xFF,0xFF,

TURQUOISE
    0x00,0x22,0x0A, 0x00,0x33,0x19, 0x00,0x44,0x2F, 0x00,0x55,0x45,
    0x04,0x66,0x5A, 0x15,0x77,0x6B, 0x26,0x88,0x7C, 0x37,0x99,0x8D,
    0x48,0xAA,0x9E, 0x59,0xBB,0xAF, 0x6A,0xCC,0xC0, 0x7B,0xDD,0xD1,
    0x8C,0xEE,0xE2, 0x9D,0xFF,0xF3, 0xAE,0xFF,0xFF, 0xBF,0xFF,0xFF,

GREEN-BLUE
    0x00,0x27,0x0C, 0x00,0x38,0x11, 0x00,0x49,0x16, 0x00,0x5A,0x1B,
    0x0D,0x6B,0x25, 0x1E,0x7C,0x36, 0x2F,0x8D,0x47, 0x40,0x9E,0x58,
    0x51,0xAF,0x69, 0x62,0xC0,0x7A, 0x73,0xD1,0x8B, 0x84,0xE2,0x9C,
    0x95,0xF3,0xAD, 0xA6,0xFF,0xBD, 0xB7,0xFF,0xC9, 0xC8,0xFF,0xD4,

GREEN
    0x00,0x24,0x0B, 0x00,0x35,0x10, 0x01,0x46,0x15, 0x12,0x57,0x15,
    0x23,0x68,0x15, 0x34,0x79,0x15, 0x45,0x8A,0x19, 0x56,0x9B,0x2A,
    0x67,0xAC,0x3B, 0x78,0xBD,0x4C, 0x89,0xCE,0x5D, 0x9A,0xDF,0x6E,
    0xAB,0xF0,0x7F, 0xBC,0xFF,0x8F, 0xCD,0xFF,0x9B, 0xDE,0xFF,0xA7,

YELLOW-GREEN
    0x00,0x18,0x07, 0x00,0x29,0x0C, 0x1E,0x3A,0x08, 0x2F,0x4B,0x08,
    0x40,0x5C,0x08, 0x51,0x6D,0x08, 0x62,0x7E,0x08, 0x73,0x8F,0x0D,
    0x84,0xA0,0x1E, 0x95,0xB1,0x2F, 0xA6,0xC2,0x40, 0xB7,0xD3,0x51,
    0xC8,0xE4,0x62, 0xD9,0xF5,0x73, 0xEA,0xFF,0x82, 0xFB,0xFF,0x8E,

ORANGE-GREEN
    0x1B,0x07,0x00, 0x2C,0x18,0x00, 0x3D,0x29,0x00, 0x4E,0x3A,0x00,
    0x5F,0x4B,0x00, 0x70,0x5C,0x00, 0x81,0x6D,0x00, 0x92,0x7E,0x09,
    0xA3,0x8F,0x1A, 0xB4,0xA0,0x2B, 0xC5,0xB1,0x3C, 0xD6,0xC2,0x4D,
    0xE7,0xD3,0x5E, 0xF8,0xE4,0x6F, 0xFF,0xF5,0x83, 0xFF,0xFF,0x97,

LIGHT-ORANGE
    0x33,0x00,0x00, 0x44,0x05,0x00, 0x55,0x16,0x00, 0x66,0x27,0x00,
    0x77,0x38,0x00, 0x88,0x49,0x00, 0x99,0x5A,0x0D, 0xAA,0x6B,0x1E,
    0xBB,0x7C,0x2F, 0xCC,0x8D,0x40, 0xDD,0x9E,0x51, 0xEE,0xAF,0x62,
    0xFF,0xC0,0x73, 0xFF,0xD1,0x89, 0xFF,0xE2,0x9F, 0xFF,0xF3,0xB5
*******************************************************************/

/**************************************************************
 *
 *    Memory setup
 *
 **************************************************************/

void a400_state::cart_rd4_w(int state)
{
	m_cart_rd4_enabled = state;
	m_cart_rd4_view.select(m_cart_rd4_enabled);
}

void a400_state::cart_rd5_w(int state)
{
	m_cart_rd5_enabled = state;
	m_cart_rd5_view.select(m_cart_rd5_enabled);
}

TIMER_DEVICE_CALLBACK_MEMBER( a400_state::a400_interrupt )
{
	m_antic->generic_interrupt(4);
}

TIMER_DEVICE_CALLBACK_MEMBER( a1200xl_state::xl_interrupt )
{
	m_antic->generic_interrupt(2);
}

TIMER_DEVICE_CALLBACK_MEMBER( a5200_state::a5200_interrupt )
{
	m_antic->generic_interrupt(4);
}

void a400_state::machine_start()
{
	save_item(NAME(m_cart_rd4_enabled));
	save_item(NAME(m_cart_rd5_enabled));

	if (m_sio.found())
		m_sio->ready_w(1);
}

void a1200xl_state::machine_start()
{
	a400_state::machine_start();
	save_item(NAME(m_mmu));
}

void xegs_state::machine_start()
{
	a1200xl_state::machine_start();
	m_bank->configure_entries(0, 2, memregion("maincpu")->base() + 0x8000, 0x2000);
}

void a400_state::machine_reset()
{
	m_pokey->write(15, 0);

	// TODO: stub reset state
	if (!m_cartleft->exists())
	{
		m_cart_rd4_enabled = 0;
		m_cart_rd5_enabled = 0;
	}
	else
		std::tie(m_cart_rd4_enabled, m_cart_rd5_enabled) = m_cartleft->get_initial_rd_state();

	m_cart_rd4_view.select(m_cart_rd4_enabled);
	m_cart_rd5_view.select(m_cart_rd5_enabled);
}

void a800_state::machine_reset()
{
	a400_state::machine_reset();

	// TODO: stub reset state, verify if any can be run stand-alone
/*
    if (!m_cartright->exists())
    {
        m_cart_rd4_enabled = false;
        m_cart_rd4_view.select(0);
    }*/
}

void a1200xl_state::machine_reset()
{
	a400_state::machine_reset();
	m_mmu = 0x83;
	m_kernel_view.select(1);
	m_selftest_view.select(0);
}

void a800xl_state::machine_reset()
{
	a1200xl_state::machine_reset();
	m_basic_view.select(0);
}

void xegs_state::machine_reset()
{
	a800xl_state::machine_reset();
	m_bank->set_entry(0);
}

void a130xe_state::machine_reset()
{
	a800xl_state::machine_reset();
	m_ext_view.select(0);
	m_ext_bank->set_bank(3);
}

void a5200_state::machine_start()
{
}

void a5200_state::machine_reset()
{
	m_pokey->write(15, 0);
}

/**************************************************************
 *
 * GTIA interface
 *
 **************************************************************/

void a400_state::gtia_cb(uint8_t data)
{
	m_dac->write(BIT(data, 3));
}

/**************************************************************
 *
 * PIA interface
 *
 **************************************************************/

uint8_t a400_state::djoy_0_1_r()
{
	return (m_ctrl[0]->read_joy() & 0x0f) | (m_ctrl[1]->read_joy() & 0x0f) << 4;
}

void a400_state::djoy_0_1_w(uint8_t data)
{
	m_ctrl[0]->joy_w(data & 0x0f);
	m_ctrl[1]->joy_w(data >> 4);
}

uint8_t a400_state::djoy_2_3_r()
{
	return (m_ctrl[2]->read_joy() & 0x0f) | (m_ctrl[3]->read_joy() & 0x0f) << 4;
}

void a400_state::djoy_2_3_w(uint8_t data)
{
	m_ctrl[2]->joy_w(data & 0x0f);
	m_ctrl[3]->joy_w(data >> 4);
}

uint8_t a400_state::djoy_b_r()
{
	uint8_t b = 0;
	for (int i = 0; i < 4; i++)
		if (!m_ctrl[i].found() || BIT(m_ctrl[i]->read_joy(), 5))
			b |= 1 << i;
	return b;
}

uint8_t a1200xl_state::djoy_b_r()
{
	uint8_t b = 0;
	for (int i = 0; i < 2; i++)
		if (!m_ctrl[i].found() || BIT(m_ctrl[i]->read_joy(), 5))
			b |= 1 << i;

	// TODO: TRIG2 (0) for XEGS keyboard disconnected, always 1 otherwise
	b |= 1 << 2;
	// TODO: TRIG3 should really read from RD5 instead
	// - non-64KB SDX boot (will hang in kernel during POST by boot conflict with BASIC if not handled properly)
	// - a1200xl cart boot (expects this to be reversed)
	// - returning m_cartleft->exists() makes BASIC disable thru OPTION to not work and makes maxflash to fail loading most .xex
//  b |= m_cart_rd5_enabled << 3;
	b |= !m_cartleft->exists() << 3;

	return b;
}

void a1200xl_state::pia_portb_w(uint8_t data)
{
	const u8 dir = m_pia->port_b_z_mask();

	// On a1200xl BIOS jumps from PC=0xc550, expecting ROM to be still
	// enabled at PC=0xe40c, needing direction to work properly.
	const u8 new_mmu = (data & ~dir) | (m_mmu & dir);

	if (m_mmu != new_mmu)
	{
		m_mmu = new_mmu;
		portb_cb(m_mmu);
	}
}

/*
 * x--- ---- /DRE (1) 0x5000-0x57ff self-test ROM enabled if also kernel ROM enabled
 *                (0) RAM or NOP
 * ---- --x- /BAE (0) 0xa000-0xbfff BASIC ROM enabled (1) disabled
 * ---- ---x /OSE (1) 0xd800-0xffff kernel ROM enable (0) RAM
 */
void a800xl_state::portb_cb(uint8_t data)
{
	m_kernel_view.select(BIT(data, 0));
	m_basic_view.select(!BIT(data, 1));
	m_selftest_view.select((data & 0x81) == 0x01);
}

/*
 * same as a800xl plus:
 * ---- xx-- LED states (specific for this variant only)
 * Note: basic ROM not present so bit 1 doesn't do anything
 */
void a1200xl_state::portb_cb(uint8_t data)
{
	m_kernel_view.select(BIT(data, 0));
	m_selftest_view.select((data & 0x81) == 0x01);
}

/*
 * same as a800xl plus:
 * -x-- ---- (0) enables Missile Command ROM at 0xa000-0xbfff, ignored if BASIC ROM bit 1 also 0
 *           (BASIC has higher priority)
 *
 * To access Missile Command with a keyboard connected
 * hold console SELECT switch while booting the machine
 */
void xegs_state::portb_cb(uint8_t data)
{
	m_kernel_view.select(BIT(data, 0));
	const bool game_rom = !BIT(data, 6);
	const bool basic_rom = !BIT(data, 1);
	if (game_rom || basic_rom)
	{
		m_basic_view.select(1);
		m_bank->set_entry(basic_rom);
	}
	else
		m_basic_view.select(0);
	m_selftest_view.select((data & 0x81) == 0x01);
}

/*
 * same as a800xl plus:
 * --x- ---- ANTIC extended memory access enable
 * ---x ---- CPU extended memory access enable
 * ---- xx-- 0x4000-0x7fff extended RAM bank select
 */
void a130xe_state::portb_cb(uint8_t data)
{
	a800xl_state::portb_cb(data);
	if (!BIT(data, 4))
	{
		m_ext_view.select(1);
		const bool selftest_enabled = (data & 0x81) == 0x01;
		m_ext_bank->set_bank((data & 0xc) >> 2 | (selftest_enabled << 2));
	}
	else
		m_ext_view.select(0);

	// TODO: ANTIC extended memory access enable
	// Prince of Persia homebrew has it always on.
	// May be used for the intro screen (where text don't draw at the time of this writing)
}

// TODO: propagate portb to RAMBO XL and COMPY sub-devices for a800xl and onward
/*
 * For RAMBO, same as a130xe plus:
 * -xx- ---- A2-A3 select bits for RAM bank
 * ---x ---- /RAME (1) 0x4000-0x7fff selects main system RAM (0) extended RAM access
 * NB: if A0-A3 selects banks $0-$3 then main system RAM is used,
 *     only $4-$f and onward really accesses the sub-board RAM installed.
 */

/**************************************************************
 *
 * Machine drivers
 *
 **************************************************************/

// note: both screen setups are actually non-interlaced, and always 240 lines
void atari_common_state::config_ntsc_screen(machine_config &config)
{
	// 15.69975KHz x 59.9271 Hz
	m_screen->set_raw(XTAL(14'318'181), 912, antic_device::MIN_X, antic_device::MAX_X, 262, antic_device::MIN_Y, antic_device::MAX_Y);
	m_gtia->set_region(GTIA_NTSC);
}

void atari_common_state::config_pal_screen(machine_config &config)
{
	// 15.55655KHz x 49.86074 Hz, master clock rated at 14.18757 MHz
	// TODO: confirm hsync
	m_screen->set_raw(XTAL(3'546'800) * 4, 912, antic_device::MIN_X, antic_device::MAX_X, 312, antic_device::MIN_Y, antic_device::MAX_Y);
	m_gtia->set_region(GTIA_PAL);
}

void a400_state::atari_common_nodac(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, pokey_device::FREQ_17_EXACT);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_screen_update("antic", FUNC(antic_device::screen_update));
	m_screen->set_palette("palette");
//  m_screen->set_video_attributes(VIDEO_UPDATE_SCANLINE);

	PALETTE(config, "palette", FUNC(a400_state::a400_palette), std::size(atari_colors) / 3);

	/* sound hardware */
	SPEAKER(config, "speaker").front_center();
	POKEY(config, m_pokey, pokey_device::FREQ_17_EXACT);
	m_pokey->set_keyboard_callback(FUNC(a400_state::a800_keyboard));
	m_pokey->irq_w().set_inputline(m_maincpu, m6502_device::IRQ_LINE);
	m_pokey->add_route(ALL_OUTPUTS, "speaker", 1.0);
}

void a400_state::atari_common(machine_config &config)
{
	atari_common_nodac(config);

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, m6502_device::IRQ_LINE);
	m_pokey->irq_w().set("mainirq", FUNC(input_merger_device::in_w<0>));
	m_pokey->pot_r<0>().set(m_ctrl[0], FUNC(vcs_control_port_device::read_pot_y));
	m_pokey->pot_r<1>().set(m_ctrl[0], FUNC(vcs_control_port_device::read_pot_x));
	m_pokey->pot_r<2>().set(m_ctrl[1], FUNC(vcs_control_port_device::read_pot_y));
	m_pokey->pot_r<3>().set(m_ctrl[1], FUNC(vcs_control_port_device::read_pot_x));
	m_pokey->pot_r<4>().set(m_ctrl[2], FUNC(vcs_control_port_device::read_pot_y));
	m_pokey->pot_r<5>().set(m_ctrl[2], FUNC(vcs_control_port_device::read_pot_x));
	m_pokey->pot_r<6>().set(m_ctrl[3], FUNC(vcs_control_port_device::read_pot_y));
	m_pokey->pot_r<7>().set(m_ctrl[3], FUNC(vcs_control_port_device::read_pot_x));
	m_pokey->oclk_w().set(m_sio, FUNC(a8sio_device::clock_out_w));
	m_pokey->sod_w().set(m_sio, FUNC(a8sio_device::data_out_w));

	DAC_1BIT(config, "dac", 0).add_route(ALL_OUTPUTS, "speaker", 0.03);

	/* internal ram */
	RAM(config, m_ram).set_default_size("48K");

	ATARI_GTIA(config, m_gtia, 0);
	m_gtia->read_callback().set_ioport("console");
	m_gtia->write_callback().set(FUNC(a400_state::gtia_cb));
	m_gtia->trigger_callback().set(FUNC(a400_state::djoy_b_r));

	ATARI_ANTIC(config, m_antic, 0);
	m_antic->set_gtia_tag(m_gtia);

	/* devices */
	PIA6821(config, m_pia);
	m_pia->readpa_handler().set(FUNC(a400_state::djoy_0_1_r));
	m_pia->writepa_handler().set(FUNC(a400_state::djoy_0_1_w));
	m_pia->readpb_handler().set(FUNC(a400_state::djoy_2_3_r));
	m_pia->writepb_handler().set(FUNC(a400_state::djoy_2_3_w));
	m_pia->ca2_handler().set(m_sio, FUNC(a8sio_device::motor_w));
	m_pia->cb2_handler().set(m_sio, FUNC(a8sio_device::command_w));
	m_pia->irqa_handler().set("mainirq", FUNC(input_merger_device::in_w<1>));
	m_pia->irqb_handler().set("mainirq", FUNC(input_merger_device::in_w<2>));

	a8sio_device &sio(A8SIO(config, m_sio, "fdc"));
	//sio.clock_in().set(m_pokey, FUNC(pokey_device::bclk_w));
	sio.data_in().set(m_pokey, FUNC(pokey_device::sid_w));
	sio.proceed().set(m_pia, FUNC(pia6821_device::ca1_w));
	sio.interrupt().set(m_pia, FUNC(pia6821_device::cb1_w));

	A800_CART_SLOT(config, m_cartleft, a800_left, nullptr);
	m_cartleft->rd4_callback().set(FUNC(a400_state::cart_rd4_w));
	m_cartleft->rd5_callback().set(FUNC(a400_state::cart_rd5_w));

	/* software lists */
	SOFTWARE_LIST(config, "flop_list").set_original("a800_flop");
	SOFTWARE_LIST(config, "cass_list").set_original("a800_cass");
	SOFTWARE_LIST(config, "cart_list").set_original("a800");
	SOFTWARE_LIST(config, "xegs_list").set_compatible("xegs");

	VCS_CONTROL_PORT(config, m_ctrl[0], a800_control_port_devices, "joy");
	VCS_CONTROL_PORT(config, m_ctrl[1], a800_control_port_devices, "joy");
	VCS_CONTROL_PORT(config, m_ctrl[2], a800_control_port_devices, "joy");
	VCS_CONTROL_PORT(config, m_ctrl[3], a800_control_port_devices, "joy");
}

// memory map A400 + NTSC screen
void a400_state::a400(machine_config &config)
{
	atari_common(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &a400_state::a400_mem);
	TIMER(config, "scantimer").configure_scanline(FUNC(a400_state::a400_interrupt), "screen", 0, 1);

	config_ntsc_screen(config);

	// TODO: confirm all of the official config
	// 4KB was the release model,
	// shouldn't be possible to reach 48K but added as convenience for now.
	m_ram->set_default_size("16K");
	m_ram->set_extra_options("4K,48K");
}

// memory map A400 + PAL screen
void a400_state::a400pal(machine_config &config)
{
	a400(config);

	config_pal_screen(config);
}

// memory map A800 + NTSC screen + Right cartslot
void a800_state::a800(machine_config &config)
{
	atari_common(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &a800_state::a800_mem);
	TIMER(config, "scantimer").configure_scanline(FUNC(a800_state::a400_interrupt), "screen", 0, 1);

	config_ntsc_screen(config);

	A800_CART_SLOT(config, m_cartright, a800_right, nullptr);

	// TODO: confirm all of the official config
	// 8K release model, 48K possible here?
	m_ram->set_default_size("16K");
	m_ram->set_extra_options("8K,48K");
}


// memory map A800 + PAL screen + Right cartslot
void a800_state::a800pal(machine_config &config)
{
	a800(config);

	config_pal_screen(config);
}

// memory map A1200XL+ MMU via PIA portB
void a1200xl_state::atari_xl_common(machine_config &config)
{
	atari_common(config);

	TIMER(config, "scantimer").configure_scanline(FUNC(a1200xl_state::xl_interrupt), "screen", 0, 1);

	m_pokey->pot_r<4>().set_constant(0xff);
	m_pokey->pot_r<5>().set_constant(0xff);
	m_pokey->pot_r<6>().set_constant(0xff);
	m_pokey->pot_r<7>().set_constant(0xff);

	m_pia->readpb_handler().set_constant(0x83);
	m_pia->writepb_handler().set(FUNC(a1200xl_state::pia_portb_w));

	config_ntsc_screen(config);

	m_ram->set_default_size("16K");

	config.device_remove("ctrl3");
	config.device_remove("ctrl4");
}

void a1200xl_state::a1200xl(machine_config &config)
{
	atari_xl_common(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &a1200xl_state::a1200xl_mem);

	m_pokey->pot_r<4>().set_ioport("J1");
	m_pokey->pot_r<5>().set_ioport("J2");
	m_pokey->pot_r<6>().set_ioport("J3");
	m_pokey->pot_r<7>().set_ioport("J4");
	// TODO: console LEDs for this model only

	// retail has 64KB minimum
	m_ram->set_default_size("64K");
//  m_ram->set_extra_options("16K,32K,48K");
}

// memory map A800XL + NTSC screen + MMU via PIA portB
void a800xl_state::a800xl(machine_config &config)
{
	atari_xl_common(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &a800xl_state::a800xl_mem);

	m_ram->set_default_size("64K");
}

// memory map A600XL (same as 800XL but less RAM) + NTSC screen + MMU via PIA portB
void a600xl_state::a600xl(machine_config &config)
{
	a800xl(config);

	m_ram->set_default_size("16K");
	m_ram->set_extra_options("32K,48K,64K");
}

// memory map A800XL + PAL screen + MMU via PIA portB
void a800xl_state::a800xlpal(machine_config &config)
{
	a800xl(config);

	m_maincpu->set_clock(1773000);

	config_pal_screen(config);

	m_pokey->set_clock(1773000);
}

// memory map A130XE
void a130xe_state::a130xe(machine_config &config)
{
	a800xl(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &a130xe_state::a130xe_mem);

	// minimum RAM 128KB, further sizes with optional modules
	m_ram->set_default_size("128K");

	ADDRESS_MAP_BANK(config, m_ext_bank).set_map(&a130xe_state::extram_map).set_options(ENDIANNESS_LITTLE, 8, 16 + 1, 0x4000);
}

// memory map XEGS
void xegs_state::xegs(machine_config &config)
{
	a800xl(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &xegs_state::xegs_mem);

	// not installable unless with DIY mods
	config.device_remove("ram");

//  m_cartleft->set_default_option("xegs");
	m_cartleft->set_is_xegs(true);

	// uses exact same slot connector
	SOFTWARE_LIST(config.replace(), "cart_list").set_compatible("a800");
	SOFTWARE_LIST(config.replace(), "xegs_list").set_original("xegs");
}

// memory map A5200, different ports, less RAM
void a5200_state::a5200(machine_config &config)
{
	atari_common_nodac(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &a5200_state::a5200_mem);
	TIMER(config, "scantimer").configure_scanline(FUNC(a5200_state::a5200_interrupt), "screen", 0, 1);

	// Not used but exposed via expansion port
	//m_pokey->serin_r().set_constant(0);
	//m_pokey->serout_w().set_nop();
	m_pokey->pot_r<0>().set_ioport("analog_0");
	m_pokey->pot_r<1>().set_ioport("analog_1");
	m_pokey->pot_r<2>().set_ioport("analog_2");
	m_pokey->pot_r<3>().set_ioport("analog_3");
	m_pokey->pot_r<4>().set_ioport("analog_4");
	m_pokey->pot_r<5>().set_ioport("analog_5");
	m_pokey->pot_r<6>().set_ioport("analog_6");
	m_pokey->pot_r<7>().set_ioport("analog_7");
	m_pokey->set_keyboard_callback(FUNC(a5200_state::a5200_keypads));
	m_pokey->add_route(ALL_OUTPUTS, "speaker", 1.0);

	ATARI_GTIA(config, m_gtia, 0);
	m_gtia->trigger_callback().set_ioport("djoy_b");

	ATARI_ANTIC(config, m_antic, 0);
	m_antic->set_gtia_tag(m_gtia);

	config_ntsc_screen(config);

	A5200_CART_SLOT(config, m_cart, a5200_carts, nullptr);

	/* Software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("a5200");

	// not installable like at all?
	//config.device_remove("ram");
}

/*
From Analog Computing Magazine, issue 16 (1984-02):
  Newer releases of the 5200 incorporate some minor hardware changes.
  Controller ports 3 and 4 have been eliminated, making POT4 through
  POT7, TRIG2, TRIG3, and bit 1 of CONSOL useless. A few of the
  connector pins have been redefined. Pin 2 of the I/O expansion
  connector now carries POKEY's Audio Out signal. Three pins on the
  cartridge connector have changed to accommodate the new 2600 adapter.
  The system clock, 02, is output on pin 14, isolated through a diode.
  An alternate video input is taken from pin 24 and is also isolated
  through a diode. Pin 30 provides an alternate audio input.

  There is space on the newer boards for circuitry for a PAL (European
  TV standard) version of the 5200. Also, on power-up, the monitor
  program checks for the PAL version by examining the GTIA register PAL
  after step 2 of the initialization routine. It also checks the
  cartridge program for PAL compatibility. The byte at $BFE7 should
  read $02 if compatible, or $00 if not. This is the only important
  change to the monitor program. There are some additional hardware
  changes, but none affects the machine's operation from the
  programmer's view.
*/
void a5200_state::a5200a(machine_config &config)
{
	a5200(config);

	m_pokey->pot_r<4>().set_constant(0);
	m_pokey->pot_r<5>().set_constant(0);
	m_pokey->pot_r<6>().set_constant(0);
	m_pokey->pot_r<7>().set_constant(0);
}


/**************************************************************
 *
 * ROM loading
 *
 **************************************************************/

ROM_START( a400 )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD( "co12399b.rom", 0xd800, 0x0800, CRC(6a5d766e) SHA1(01a6044f7a81d409c938e7dfde0a1af5832229d2) )
	ROM_SYSTEM_BIOS(0, "default", "OS Rev. B")
	ROMX_LOAD( "co12499b.rom",  0xe000, 0x1000, BAD_DUMP CRC(d818f3e8) SHA1(bcdec2188f6a6a5bfc1df4e383bd828d34b5c4ac), ROM_BIOS(0) )    // CRC and label waiting for confirmation
	ROMX_LOAD( "co14599b.rom",  0xf000, 0x1000, BAD_DUMP CRC(c1690a9b) SHA1(c5248e8565574fd39ae1c3f4f356aa4cac07df95), ROM_BIOS(0) )    // CRC and label waiting for confirmation
	ROM_SYSTEM_BIOS(1, "reva", "OS Rev. A")
	ROMX_LOAD( "co12499a.rom",  0xe000, 0x1000, BAD_DUMP CRC(29f64e17) SHA1(abf7ec488c6b600f1b7f30bdc7f8a2bf6a727675), ROM_BIOS(1) )    // CRC and label waiting for confirmation
	ROMX_LOAD( "co14599a.rom",  0xf000, 0x1000, BAD_DUMP CRC(bc533f0c) SHA1(e217148495fa747fe5488132d8d22533e68c7e58), ROM_BIOS(1) )    // CRC and label waiting for confirmation
ROM_END

ROM_START( a400pal )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD( "co12399b.rom", 0xd800, 0x0800, CRC(6a5d766e) SHA1(01a6044f7a81d409c938e7dfde0a1af5832229d2) )
	ROM_LOAD( "co15199.rom", 0xe000, 0x1000, BAD_DUMP CRC(8e547f56) SHA1(1bd746ea798b723bfb18495a7facca113183d713) )    // Rev. A - CRC and label waiting for confirmation
	ROM_LOAD( "co15299.rom", 0xf000, 0x1000, BAD_DUMP CRC(be55b413) SHA1(d88afae49b08e75943d0258cb580e5d34756414a) )    // Rev. A - CRC and label waiting for confirmation
ROM_END

ROM_START( a800 )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD( "co12399b.rom", 0xd800, 0x0800, CRC(6a5d766e) SHA1(01a6044f7a81d409c938e7dfde0a1af5832229d2) )
	ROM_SYSTEM_BIOS(0, "default", "OS Rev. B")
	ROMX_LOAD( "co12499b.rom",  0xe000, 0x1000, BAD_DUMP CRC(d818f3e8) SHA1(bcdec2188f6a6a5bfc1df4e383bd828d34b5c4ac), ROM_BIOS(0) )    // CRC and label waiting for confirmation
	ROMX_LOAD( "co14599b.rom",  0xf000, 0x1000, BAD_DUMP CRC(c1690a9b) SHA1(c5248e8565574fd39ae1c3f4f356aa4cac07df95), ROM_BIOS(0) )    // CRC and label waiting for confirmation
	ROM_SYSTEM_BIOS(1, "reva", "OS Rev. A")
	ROMX_LOAD( "co12499a.rom",  0xe000, 0x1000, BAD_DUMP CRC(29f64e17) SHA1(abf7ec488c6b600f1b7f30bdc7f8a2bf6a727675), ROM_BIOS(1) )    // CRC and label waiting for confirmation
	ROMX_LOAD( "co14599a.rom",  0xf000, 0x1000, BAD_DUMP CRC(bc533f0c) SHA1(e217148495fa747fe5488132d8d22533e68c7e58), ROM_BIOS(1) )    // CRC and label waiting for confirmation
ROM_END

ROM_START( a800pal )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD( "co12399b.rom", 0xd800, 0x0800, CRC(6a5d766e) SHA1(01a6044f7a81d409c938e7dfde0a1af5832229d2) )
	ROM_LOAD( "co15199.rom", 0xe000, 0x1000, BAD_DUMP CRC(8e547f56) SHA1(1bd746ea798b723bfb18495a7facca113183d713) )    // Rev. A - CRC and label waiting for confirmation
	ROM_LOAD( "co15299.rom", 0xf000, 0x1000, BAD_DUMP CRC(be55b413) SHA1(d88afae49b08e75943d0258cb580e5d34756414a) )    // Rev. A - CRC and label waiting for confirmation
ROM_END

ROM_START( a1200xl )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_SYSTEM_BIOS(0, "default", "OS Rev. 11")
	ROMX_LOAD( "co60616b.rom", 0xc000, 0x2000, BAD_DUMP CRC(6e29ec8d) SHA1(3f9c06d6b4d261f3d5bf4354e3cff0c17b9347b9), ROM_BIOS(0) )    // CRC and label waiting for confirmation
	ROMX_LOAD( "co60617b.rom", 0xe000, 0x2000, BAD_DUMP CRC(d73ce29a) SHA1(64790242d902643fe0c40dd842749f1fe461831b), ROM_BIOS(0) )    // CRC and label waiting for confirmation
	ROM_SYSTEM_BIOS(1, "rev10", "OS Rev. 10")
	ROMX_LOAD( "co60616a.rom", 0xc000, 0x2000, BAD_DUMP CRC(0391386b) SHA1(7c176657c88b89b8a69bf021fa8e0939efc0dff2), ROM_BIOS(1) )    // CRC and label waiting for confirmation
	ROMX_LOAD( "co60617a.rom", 0xe000, 0x2000, BAD_DUMP CRC(b502f1e7) SHA1(6688db57d97fa570aef5c15cef3e5fb2688879c2), ROM_BIOS(1) )    // CRC and label waiting for confirmation
ROM_END

ROM_START( a600xl )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD( "co60302a.rom", 0xa000, 0x2000, CRC(f0202fb3) SHA1(7ad88dd99ff4a6ee66f6d162074db6f8bef7a9b6) )    // Rev. B
	ROM_LOAD( "co62024.rom",  0xc000, 0x4000, CRC(643bcc98) SHA1(881d030656b40bbe48f15a696b28f22c0b752ab0) )    // Rev. 1
ROM_END

ROM_START( a800xl )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD( "co60302a.rom", 0xa000, 0x2000, CRC(f0202fb3) SHA1(7ad88dd99ff4a6ee66f6d162074db6f8bef7a9b6) )   // Rev. B
	ROM_LOAD( "co61598b.rom", 0xc000, 0x4000, CRC(1f9cd270) SHA1(ae4f523ba08b6fd59f3cae515a2b2410bbd98f55) )   // Rev. 2
ROM_END

#define rom_a800xlp rom_a800xl

ROM_START( a65xe )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD( "co24947a.rom", 0xa000, 0x2000, CRC(7d684184) SHA1(3693c9cb9bf3b41bae1150f7a8264992468fc8c0) )   // Rev. C
	ROM_LOAD( "co61598b.rom", 0xc000, 0x4000, CRC(1f9cd270) SHA1(ae4f523ba08b6fd59f3cae515a2b2410bbd98f55) )   // Rev. 2
ROM_END

ROM_START( a65xea )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD( "basic_ar.rom", 0xa000, 0x2000, CRC(c899f4d6) SHA1(043df191d1fe402e792266a108e147ffcda35130) )   // is this correct? or shall we use Rev. C?
//  ROM_LOAD( "c101700.rom",  0xc000, 0x4000, CRC(7f9a76c8) SHA1(57eb6d87850a763f11767f53d4eaede186f831a2) )   // this was from Savetz and has wrong bits!
	ROM_LOAD( "c101700.rom",  0xc000, 0x4000, CRC(45f47988) SHA1(a36b8b20f657580f172749bb0625c08706ed824c) )   // Rev. 3B ?
ROM_END

ROM_START( a130xe )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD( "co24947a.rom", 0xa000, 0x2000, CRC(7d684184) SHA1(3693c9cb9bf3b41bae1150f7a8264992468fc8c0) )   // Rev. C
	ROM_LOAD( "co61598b.rom", 0xc000, 0x4000, CRC(1f9cd270) SHA1(ae4f523ba08b6fd59f3cae515a2b2410bbd98f55) )   // Rev. 2
ROM_END

ROM_START( a800xe )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD( "co24947a.rom", 0xa000, 0x2000, CRC(7d684184) SHA1(3693c9cb9bf3b41bae1150f7a8264992468fc8c0) )   // Rev. C
	ROM_LOAD( "c300717.rom",  0xc000, 0x4000, CRC(29f133f7) SHA1(f03b9b93000ee84abb9cf8d6367241006f172182) )   // Rev. 3
ROM_END

ROM_START( xegs )
	ROM_REGION(0x10000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD( "c101687.rom", 0x8000, 0x8000, CRC(d50260d1) SHA1(0e0625ab2473f8431640df3ac8af61925760b9b9) )    // Rev. C + Rev. 4 + Missile Command
ROM_END


ROM_START( a5200 )
	ROM_REGION(0x10000, "maincpu", ROMREGION_ERASEFF)
	ROM_LOAD( "co19156.u8", 0xf800, 0x0800, CRC(4248d3e3) SHA1(6ad7a1e8c9fad486fbec9498cb48bf5bc3adc530) )
ROM_END

ROM_START( a5200a )
	ROM_REGION(0x10000, "maincpu", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "default", "2-Port")
	ROMX_LOAD( "co19156a.a8", 0xf800, 0x0800, CRC(c2ba2613) SHA1(1d2a3f00109d75d2d79fecb565775eb95b7d04d5), ROM_BIOS(0) ) // This breaks "K-Razy Shoot-Out", "Mountain King", and "Pitfall!"
	ROM_SYSTEM_BIOS(1, "4port", "4-Port")
	ROMX_LOAD( "co19156.u8", 0xf800, 0x0800, CRC(4248d3e3) SHA1(6ad7a1e8c9fad486fbec9498cb48bf5bc3adc530), ROM_BIOS(1) )
ROM_END


/**************************************************************
 *
 * Game driver(s)
 *
 **************************************************************/

/*     YEAR  NAME    PARENT  COMPAT  MACHINE    INPUT   CLASS       INIT        COMPANY  FULLNAME */
COMP( 1979, a400,    0,      0,      a400,      a800,   a400_state,   empty_init, "Atari", "Atari 400 (NTSC)",     0)
COMP( 1979, a400pal, a400,   0,      a400pal,   a800,   a400_state,   empty_init, "Atari", "Atari 400 (PAL)",      0)
COMP( 1979, a800,    0,      0,      a800,      a800,   a800_state,   empty_init, "Atari", "Atari 800 (NTSC)",     0)
COMP( 1979, a800pal, a800,   0,      a800pal,   a800,   a800_state,   empty_init, "Atari", "Atari 800 (PAL)",      0)
COMP( 1982, a1200xl, a800,   0,      a1200xl,   a1200xl,a1200xl_state, empty_init, "Atari", "Atari 1200XL",         MACHINE_NOT_WORKING )      // 64k RAM
COMP( 1983, a600xl,  a800xl, 0,      a600xl,    a800,   a600xl_state, empty_init, "Atari", "Atari 600XL",          MACHINE_IMPERFECT_GRAPHICS )      // 16k RAM
COMP( 1983, a800xl,  0,      0,      a800xl,    a800,   a800xl_state, empty_init, "Atari", "Atari 800XL (NTSC)",   MACHINE_IMPERFECT_GRAPHICS )      // 64k RAM
COMP( 1983, a800xlp, a800xl, 0,      a800xlpal, a800,   a800xl_state, empty_init, "Atari", "Atari 800XL (PAL)",    MACHINE_IMPERFECT_GRAPHICS )      // 64k RAM
COMP( 1986, a65xe,   a800xl, 0,      a800xl,    a800,   a800xl_state, empty_init, "Atari", "Atari 65XE",           MACHINE_IMPERFECT_GRAPHICS )      // 64k RAM
COMP( 1986, a65xea,  a800xl, 0,      a800xl,    a800,   a800xl_state, empty_init, "Atari", "Atari 65XE (Arabic)",  MACHINE_NOT_WORKING )
COMP( 1986, a130xe,  a800xl, 0,      a130xe,    a800,   a130xe_state, empty_init, "Atari", "Atari 130XE",          MACHINE_NOT_WORKING )      // 128k RAM
COMP( 1986, a800xe,  a800xl, 0,      a800xl,    a800,   a800xl_state, empty_init, "Atari", "Atari 800XE",          MACHINE_IMPERFECT_GRAPHICS )      // 64k RAM
COMP( 1987, xegs,    0,      0,      xegs,      a800,   xegs_state,   empty_init, "Atari", "Atari XE Game System", MACHINE_IMPERFECT_GRAPHICS )  // 64k RAM

CONS( 1982, a5200,   0,      0,      a5200,     a5200,  a5200_state,  empty_init, "Atari", "Atari 5200",           0)
CONS( 1983, a5200a,  a5200,  0,      a5200a,    a5200a, a5200_state,  empty_init, "Atari", "Atari 5200 (2-port)",  0)



atarist.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder, Olivier Galibert
#include "emu.h"

#include "ataristb.h"
#include "atarist_v.h"
#include "stkbd.h"
#include "stmmu.h"
#include "stvideo.h"

#include "bus/centronics/ctronics.h"
#include "bus/midi/midi.h"
#include "bus/rs232/rs232.h"
#include "bus/st/stcart.h"
#include "cpu/m68000/m68000.h"
#include "imagedev/floppy.h"
#include "machine/6850acia.h"
#include "machine/z80scc.h"
#include "machine/clock.h"
#include "machine/input_merger.h"
#include "machine/mc68901.h"
#include "machine/ram.h"
#include "machine/rescap.h"
#include "machine/rp5c15.h"
#include "machine/wd_fdc.h"
#include "sound/ay8910.h"
#include "sound/lmc1992.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

/*

    TODO:

    - floppy write
    - floppy DMA transfer timer
    - mouse moves too fast?
    - UK keyboard layout for the special keys
    - accurate screen timing
    - STe DMA sound and LMC1992 Microwire mixer
    - Mega ST/STe MC68881 FPU
    - Mega STe 16KB cache
    - Mega STe LAN

    http://dev-docs.atariforge.org/
    http://info-coach.fr/atari/software/protection.php

*/

#include "formats/st_dsk.h"
#include "formats/pasti_dsk.h"
#include "formats/mfi_dsk.h"
#include "formats/dfi_dsk.h"
#include "formats/ipf_dsk.h"

#include "utf8.h"


//**************************************************************************
//  CONSTANTS / MACROS
//**************************************************************************

#define LOG 0

namespace {

#define M68000_TAG      "m68000"
#define YM2149_TAG      "ym2149"
#define MC6850_0_TAG    "mc6850_0"
#define MC6850_1_TAG    "mc6850_1"
#define Z8530_TAG       "z8530"
#define COP888_TAG      "u703"
#define RP5C15_TAG      "rp5c15"
#define YM3439_TAG      "ym3439"
#define MC68901_TAG     "mc68901"
#define LMC1992_TAG     "lmc1992"
#define WD1772_TAG      "wd1772"
#define SCREEN_TAG      "screen"
#define CENTRONICS_TAG  "centronics"
#define RS232_TAG       "rs232"

// Atari ST

#define Y1      XTAL(2'457'600)

// 32028400 also exists
#define Y2      32084988.0
#define Y2_NTSC 32042400.0

// STBook

#define U517    XTAL(16'000'000)
#define Y200    XTAL(2'457'600)
#define Y700    XTAL(10'000'000)

static const double DMASOUND_RATE[] = { Y2/640.0/8.0, Y2/640.0/4.0, Y2/640.0/2.0, Y2/640.0 };

class st_state : public driver_device
{
public:
	st_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
			m_maincpu(*this, M68000_TAG),
			m_mainram(*this, "mainram", 0x400000, ENDIANNESS_BIG),
			m_ikbd(*this, "ikbd"),
			m_mmu(*this, "mmu"),
			m_stb(*this, "stb"),
			m_fdc(*this, WD1772_TAG),
			m_floppy(*this, WD1772_TAG ":%u", 0U),
			m_mfp(*this, MC68901_TAG),
			m_acia(*this, {MC6850_0_TAG, MC6850_1_TAG}),
			m_centronics(*this, CENTRONICS_TAG),
			m_cart(*this, "cartslot"),
			m_ramcfg(*this, RAM_TAG),
			m_rs232(*this, RS232_TAG),
			m_ymsnd(*this, YM2149_TAG),
			m_config(*this, "config"),
			m_monochrome(1),
			m_video(*this, "video"),
			m_videox(*this, "videox"),
			m_screen(*this, "screen")
	{ }

	void write_monochrome(int state);

	void st(machine_config &config);

protected:
	required_device<m68000_device> m_maincpu;
	memory_share_creator<u16> m_mainram;
	required_device<st_kbd_device> m_ikbd;
	required_device<st_mmu_device> m_mmu;
	optional_device<st_blitter_device> m_stb;
	required_device<wd1772_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<mc68901_device> m_mfp;
	required_device_array<acia6850_device, 2> m_acia;
	required_device<centronics_device> m_centronics;
	required_device<stcart_connector> m_cart;
	required_device<ram_device> m_ramcfg;
	required_device<rs232_port_device> m_rs232;
	required_device<ym2149_device> m_ymsnd;
	optional_ioport m_config;

	TIMER_CALLBACK_MEMBER(mouse_tick);

	void psg_pa_w(uint8_t data);

	void reset_w(int state);

	static void floppy_formats(format_registration &fr);

	int m_monochrome;
	optional_device<st_video_device> m_video;
	optional_device<stx_video_device> m_videox;
	required_device<screen_device> m_screen;

	void common(machine_config &config);
	void cpu_space_map(address_map &map) ATTR_COLD;
	void st_super_map(address_map &map) ATTR_COLD;
	void st_user_map(address_map &map) ATTR_COLD;
	void megast_super_map(address_map &map) ATTR_COLD;

	uint16_t fpu_r();
	void fpu_w(uint16_t data);

	virtual void machine_start() override ATTR_COLD;
};

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

	void megast(machine_config &config);
};

class ste_state : public st_state
{
public:
	ste_state(const machine_config &mconfig, device_type type, const char *tag)
		: st_state(mconfig, type, tag),
			m_lmc1992(*this, LMC1992_TAG)
	{ }

	optional_device<lmc1992_device> m_lmc1992;

	uint8_t sound_dma_control_r();
	uint8_t sound_dma_base_r(offs_t offset);
	uint8_t sound_dma_counter_r(offs_t offset);
	uint8_t sound_dma_end_r(offs_t offset);
	uint8_t sound_mode_r();
	void sound_dma_control_w(uint8_t data);
	void sound_dma_base_w(offs_t offset, uint8_t data);
	void sound_dma_end_w(offs_t offset, uint8_t data);
	void sound_mode_w(uint8_t data);
	uint16_t microwire_data_r();
	void microwire_data_w(uint16_t data);
	uint16_t microwire_mask_r();
	void microwire_mask_w(uint16_t data);

	void write_monochrome(int state);

	void dmasound_set_state(int level);
	TIMER_CALLBACK_MEMBER(dmasound_tick);
	void microwire_shift();
	TIMER_CALLBACK_MEMBER(microwire_tick);
	void state_save();

	/* microwire state */
	uint16_t m_mw_data = 0U;
	uint16_t m_mw_mask = 0U;
	int m_mw_shift = 0;

	/* DMA sound state */
	uint32_t m_dmasnd_base = 0U;
	uint32_t m_dmasnd_end = 0U;
	uint32_t m_dmasnd_cntr = 0U;
	uint32_t m_dmasnd_baselatch = 0U;
	uint32_t m_dmasnd_endlatch = 0U;
	uint8_t m_dmasnd_ctrl = 0U;
	uint8_t m_dmasnd_mode = 0U;
	uint8_t m_dmasnd_fifo[8]{};
	uint8_t m_dmasnd_samples = 0U;
	int m_dmasnd_active = 0;

	// timers
	emu_timer *m_microwire_timer = 0;
	emu_timer *m_dmasound_timer = 0;

	void falcon40(machine_config &config);
	void tt030(machine_config &config);
	void falcon(machine_config &config);
	void ste(machine_config &config);
	void ste_super_map(address_map &map) ATTR_COLD;
protected:
	virtual void machine_start() override ATTR_COLD;
};

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

	[[maybe_unused]] uint16_t cache_r();
	[[maybe_unused]] void cache_w(uint16_t data);

	uint16_t m_cache = 0;
	void megaste(machine_config &config);
	void megaste_super_map(address_map &map) ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
};

class stbook_state : public ste_state
{
public:
	stbook_state(const machine_config &mconfig, device_type type, const char *tag)
		: ste_state(mconfig, type, tag),
			m_sw400(*this, "SW400")
	{ }

	required_ioport m_sw400;

	[[maybe_unused]] uint16_t config_r();
	[[maybe_unused]] void lcd_control_w(uint16_t data);

	[[maybe_unused]] void psg_pa_w(uint8_t data);
	uint8_t mfp_gpio_r();
	void stbook_map(address_map &map) ATTR_COLD;
protected:
	virtual void machine_start() override ATTR_COLD;
};


//**************************************************************************
//  FPU
//**************************************************************************

//-------------------------------------------------
//  fpu_r -
//-------------------------------------------------

uint16_t st_state::fpu_r()
{
	// HACK diagnostic cartridge wants to see this value
	return 0x0802;
}


void st_state::fpu_w(uint16_t data)
{
}

void st_state::write_monochrome(int state)
{
	m_monochrome = state;
	m_mfp->i7_w(m_monochrome);
}

void st_state::reset_w(int state)
{
	if (m_video.found())
		m_video->reset();
	if (m_videox.found())
		m_videox->reset();
	if (m_stb.found())
		m_stb->reset();
	m_mfp->reset();
	m_ikbd->reset();
	m_ymsnd->reset();
	m_fdc->soft_reset();
	//m_acsi->reset();
}



//**************************************************************************
//  DMA SOUND
//**************************************************************************

//-------------------------------------------------
//  dmasound_set_state -
//-------------------------------------------------

void ste_state::dmasound_set_state(int level)
{
	m_dmasnd_active = level;
	m_mfp->tai_w(m_dmasnd_active);
	m_mfp->i7_w(m_monochrome ^ m_dmasnd_active);

	if (level == 0)
	{
		m_dmasnd_baselatch = m_dmasnd_base;
		m_dmasnd_endlatch = m_dmasnd_end;
	}
	else
	{
		m_dmasnd_cntr = m_dmasnd_baselatch;
	}
}


void ste_state::write_monochrome(int state)
{
	m_monochrome = state;
	m_mfp->i7_w(m_monochrome ^ m_dmasnd_active);
}

//-------------------------------------------------
//  dmasound_tick -
//-------------------------------------------------

TIMER_CALLBACK_MEMBER(ste_state::dmasound_tick)
{
	if (m_dmasnd_samples == 0)
	{
		for (auto & elem : m_dmasnd_fifo)
		{
			elem = m_mainram[m_dmasnd_cntr];
			m_dmasnd_cntr++;
			m_dmasnd_samples++;

			if (m_dmasnd_cntr == m_dmasnd_endlatch)
			{
				dmasound_set_state(0);
				break;
			}
		}
	}

	if (m_dmasnd_ctrl & 0x80)
	{
		if (LOG) logerror("DMA sound left  %i\n", m_dmasnd_fifo[7 - m_dmasnd_samples]);
		m_dmasnd_samples--;

		if (LOG) logerror("DMA sound right %i\n", m_dmasnd_fifo[7 - m_dmasnd_samples]);
		m_dmasnd_samples--;
	}
	else
	{
		if (LOG) logerror("DMA sound mono %i\n", m_dmasnd_fifo[7 - m_dmasnd_samples]);
		m_dmasnd_samples--;
	}

	if ((m_dmasnd_samples == 0) && (m_dmasnd_active == 0))
	{
		if ((m_dmasnd_ctrl & 0x03) == 0x03)
		{
			dmasound_set_state(1);
		}
		else
		{
			m_dmasound_timer->enable(0);
		}
	}
}


//-------------------------------------------------
//  sound_dma_control_r -
//-------------------------------------------------

uint8_t ste_state::sound_dma_control_r()
{
	return m_dmasnd_ctrl;
}


//-------------------------------------------------
//  sound_dma_base_r -
//-------------------------------------------------

uint8_t ste_state::sound_dma_base_r(offs_t offset)
{
	uint8_t data = 0;

	switch (offset)
	{
	case 0x00:
		data = (m_dmasnd_base >> 16) & 0x3f;
		break;

	case 0x01:
		data = (m_dmasnd_base >> 8) & 0xff;
		break;

	case 0x02:
		data = m_dmasnd_base & 0xff;
		break;
	}

	return data;
}


//-------------------------------------------------
//  sound_dma_counter_r -
//-------------------------------------------------

uint8_t ste_state::sound_dma_counter_r(offs_t offset)
{
	uint8_t data = 0;

	switch (offset)
	{
	case 0x00:
		data = (m_dmasnd_cntr >> 16) & 0x3f;
		break;

	case 0x01:
		data = (m_dmasnd_cntr >> 8) & 0xff;
		break;

	case 0x02:
		data = m_dmasnd_cntr & 0xff;
		break;
	}

	return data;
}


//-------------------------------------------------
//  sound_dma_end_r -
//-------------------------------------------------

uint8_t ste_state::sound_dma_end_r(offs_t offset)
{
	uint8_t data = 0;

	switch (offset)
	{
	case 0x00:
		data = (m_dmasnd_end >> 16) & 0x3f;
		break;

	case 0x01:
		data = (m_dmasnd_end >> 8) & 0xff;
		break;

	case 0x02:
		data = m_dmasnd_end & 0xff;
		break;
	}

	return data;
}


//-------------------------------------------------
//  sound_mode_r -
//-------------------------------------------------

uint8_t ste_state::sound_mode_r()
{
	return m_dmasnd_mode;
}


//-------------------------------------------------
//  sound_dma_control_w -
//-------------------------------------------------

void ste_state::sound_dma_control_w(uint8_t data)
{
	m_dmasnd_ctrl = data & 0x03;

	if (m_dmasnd_ctrl & 0x01)
	{
		if (!m_dmasnd_active)
		{
			dmasound_set_state(1);
			m_dmasound_timer->adjust(attotime::zero, 0, attotime::from_hz(DMASOUND_RATE[m_dmasnd_mode & 0x03]));
		}
	}
	else
	{
		dmasound_set_state(0);
		m_dmasound_timer->enable(0);
	}
}


//-------------------------------------------------
//  sound_dma_base_w -
//-------------------------------------------------

void ste_state::sound_dma_base_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 0x00:
		m_dmasnd_base = (data << 16) & 0x3f0000;
		break;
	case 0x01:
		m_dmasnd_base = (m_dmasnd_base & 0x3f00fe) | (data << 8);
		break;
	case 0x02:
		m_dmasnd_base = (m_dmasnd_base & 0x3fff00) | (data & 0xfe);
		break;
	}

	if (!m_dmasnd_active)
	{
		m_dmasnd_baselatch = m_dmasnd_base;
	}
}


//-------------------------------------------------
//  sound_dma_end_w -
//-------------------------------------------------

void ste_state::sound_dma_end_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 0x00:
		m_dmasnd_end = (data << 16) & 0x3f0000;
		break;
	case 0x01:
		m_dmasnd_end = (m_dmasnd_end & 0x3f00fe) | (data & 0xff) << 8;
		break;
	case 0x02:
		m_dmasnd_end = (m_dmasnd_end & 0x3fff00) | (data & 0xfe);
		break;
	}

	if (!m_dmasnd_active)
	{
		m_dmasnd_endlatch = m_dmasnd_end;
	}
}


//-------------------------------------------------
//  sound_mode_w -
//-------------------------------------------------

void ste_state::sound_mode_w(uint8_t data)
{
	m_dmasnd_mode = data & 0x83;
}



//**************************************************************************
//  MICROWIRE
//**************************************************************************

//-------------------------------------------------
//  microwire_shift -
//-------------------------------------------------

void ste_state::microwire_shift()
{
	if (BIT(m_mw_mask, 15))
	{
		m_lmc1992->data_w(BIT(m_mw_data, 15));
		m_lmc1992->clock_w(1);
		m_lmc1992->clock_w(0);
	}

	// rotate mask and data left
	m_mw_mask = (m_mw_mask << 1) | BIT(m_mw_mask, 15);
	m_mw_data = (m_mw_data << 1) | BIT(m_mw_data, 15);
	m_mw_shift++;
}


//-------------------------------------------------
//  microwire_tick -
//-------------------------------------------------

TIMER_CALLBACK_MEMBER(ste_state::microwire_tick)
{
	switch (m_mw_shift)
	{
	case 0:
		m_lmc1992->enable_w(0);
		microwire_shift();
		break;

	default:
		microwire_shift();
		break;

	case 15:
		microwire_shift();
		m_lmc1992->enable_w(1);
		m_mw_shift = 0;
		m_microwire_timer->enable(0);
		break;
	}
}


//-------------------------------------------------
//  microwire_data_r -
//-------------------------------------------------

uint16_t ste_state::microwire_data_r()
{
	return m_mw_data;
}


//-------------------------------------------------
//  microwire_data_w -
//-------------------------------------------------

void ste_state::microwire_data_w(uint16_t data)
{
	if (!m_microwire_timer->enabled())
	{
		m_mw_data = data;
		m_microwire_timer->adjust(attotime::zero, 0, attotime::from_usec(2));
	}
}


//-------------------------------------------------
//  microwire_mask_r -
//-------------------------------------------------

uint16_t ste_state::microwire_mask_r()
{
	return m_mw_mask;
}


//-------------------------------------------------
//  microwire_mask_w -
//-------------------------------------------------

void ste_state::microwire_mask_w(uint16_t data)
{
	if (!m_microwire_timer->enabled())
	{
		m_mw_mask = data;
	}
}



//**************************************************************************
//  CACHE
//**************************************************************************

//-------------------------------------------------
//  cache_r -
//-------------------------------------------------

uint16_t megaste_state::cache_r()
{
	return m_cache;
}


//-------------------------------------------------
//  cache_w -
//-------------------------------------------------

void megaste_state::cache_w(uint16_t data)
{
	m_cache = data;

	m_maincpu->set_unscaled_clock(BIT(data, 0) ? Y2/2 : Y2/4);
}



//**************************************************************************
//  STBOOK
//**************************************************************************

//-------------------------------------------------
//  config_r -
//-------------------------------------------------

uint16_t stbook_state::config_r()
{
	/*

	    bit     description

	    0       _POWER_SWITCH
	    1       _TOP_CLOSED
	    2       _RTC_ALARM
	    3       _SOURCE_DEAD
	    4       _SOURCE_LOW
	    5       _MODEM_WAKE
	    6       (reserved)
	    7       _EXPANSION_WAKE
	    8       (reserved)
	    9       (reserved)
	    10      (reserved)
	    11      (reserved)
	    12      (reserved)
	    13      SELF TEST
	    14      LOW SPEED FLOPPY
	    15      DMA AVAILABLE

	*/

	return (m_sw400->read() << 8) | 0xff;
}


//-------------------------------------------------
//  lcd_control_w -
//-------------------------------------------------

void stbook_state::lcd_control_w(uint16_t data)
{
	/*

	    bit     description

	    0       Shadow Chip OFF
	    1       _SHIFTER OFF
	    2       POWEROFF
	    3       _22ON
	    4       RS-232_OFF
	    5       (reserved)
	    6       (reserved)
	    7       MTR_PWR_ON

	*/
}


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

//-------------------------------------------------
//  ADDRESS_MAP( cpu_space_map )
//-------------------------------------------------

void st_state::cpu_space_map(address_map &map)
{
	map(0xfffff3, 0xfffff3).before_time(m_maincpu, FUNC(m68000_device::vpa_sync)).after_delay(m_maincpu, FUNC(m68000_device::vpa_after)).lr8(NAME([] () -> u8 { return 25; }));
	map(0xfffff5, 0xfffff5).before_time(m_maincpu, FUNC(m68000_device::vpa_sync)).after_delay(m_maincpu, FUNC(m68000_device::vpa_after)).lr8(NAME([this] () -> u8 { m_maincpu->set_input_line(2, CLEAR_LINE); return 26; }));
	map(0xfffff7, 0xfffff7).before_time(m_maincpu, FUNC(m68000_device::vpa_sync)).after_delay(m_maincpu, FUNC(m68000_device::vpa_after)).lr8(NAME([] () -> u8 { return 27; }));
	map(0xfffff9, 0xfffff9).before_time(m_maincpu, FUNC(m68000_device::vpa_sync)).after_delay(m_maincpu, FUNC(m68000_device::vpa_after)).lr8(NAME([this] () -> u8 { m_maincpu->set_input_line(4, CLEAR_LINE); return 28; }));
	map(0xfffffb, 0xfffffb).before_time(m_maincpu, FUNC(m68000_device::vpa_sync)).after_delay(m_maincpu, FUNC(m68000_device::vpa_after)).lr8(NAME([] () -> u8 { return 29; }));
	map(0xfffffd, 0xfffffd).r(m_mfp, FUNC(mc68901_device::get_vector));
	map(0xffffff, 0xffffff).before_time(m_maincpu, FUNC(m68000_device::vpa_sync)).after_delay(m_maincpu, FUNC(m68000_device::vpa_after)).lr8(NAME([] () -> u8 { return 31; }));
}

//-------------------------------------------------
//  ADDRESS_MAP( st_map )
//-------------------------------------------------

void st_state::st_super_map(address_map &map)
{
	// Ram mapped by the mmu
	map.unmap_value_high();
	map(0x000000, 0x000007).rom().region(M68000_TAG, 0);
	map(0x000000, 0x000007).before_delay(NAME([](offs_t) { return 64; })).w(m_maincpu, FUNC(m68000_device::berr_w));
	map(0x400000, 0xf9ffff).before_delay(NAME([](offs_t) { return 64; })).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0xfc0000, 0xfeffff).rom().region(M68000_TAG, 0);
	map(0xfc0000, 0xfeffff).before_delay(NAME([](offs_t) { return 64; })).w(m_maincpu, FUNC(m68000_device::berr_w));


	map(0xff8000, 0xff8fff).m(m_mmu, FUNC(st_mmu_device::map));
	map(0xff8000, 0xff8fff).m(m_video, FUNC(st_video_device::map));
	map(0xff8800, 0xff8800).after_delay(NAME([](offs_t) { return 1; })).rw(YM2149_TAG, FUNC(ay8910_device::data_r), FUNC(ay8910_device::address_w)).mirror(0xfc);
	map(0xff8802, 0xff8802).after_delay(NAME([](offs_t) { return 1; })).rw(YM2149_TAG, FUNC(ay8910_device::data_r), FUNC(ay8910_device::data_w)).mirror(0xfc);

	// no blitter on original ST

	map(0xfffa00, 0xfffa3f).after_delay(NAME([](offs_t) { return 4; })).rw(m_mfp, FUNC(mc68901_device::read), FUNC(mc68901_device::write)).umask16(0x00ff);
	map(0xfffc00, 0xfffc03).rw(m_acia[0], FUNC(acia6850_device::read), FUNC(acia6850_device::write)).umask16(0xff00);
	map(0xfffc04, 0xfffc07).rw(m_acia[1], FUNC(acia6850_device::read), FUNC(acia6850_device::write)).umask16(0xff00);

	// Do a bus error where nothing answers in 64 cycles
	map(0xff0000, 0xff7fff).before_delay(NAME([](offs_t) { return 64; })).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0xff8100, 0xff81ff).before_delay(NAME([](offs_t) { return 64; })).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0xff8300, 0xff85ff).before_delay(NAME([](offs_t) { return 64; })).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0xff8700, 0xff87ff).before_delay(NAME([](offs_t) { return 64; })).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0xff8900, 0xfff9ff).before_delay(NAME([](offs_t) { return 64; })).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0xfffb00, 0xfffbff).before_delay(NAME([](offs_t) { return 64; })).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0xfffd00, 0xffffff).before_delay(NAME([](offs_t) { return 64; })).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
}

void st_state::st_user_map(address_map &map)
{
	// Ram mapped by the mmu
	map.unmap_value_high();
	map(0x000000, 0x0007ff).before_delay(NAME([](offs_t) { return 64; })).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0x400000, 0xf9ffff).before_delay(NAME([](offs_t) { return 64; })).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0xfc0000, 0xfeffff).rom().region(M68000_TAG, 0).w(m_maincpu, FUNC(m68000_device::berr_w));
	map(0xfc0000, 0xfeffff).before_delay(NAME([](offs_t) { return 64; })).w(m_maincpu, FUNC(m68000_device::berr_w));
	map(0xff0000, 0xffffff).before_delay(NAME([](offs_t) { return 64; })).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
}


//-------------------------------------------------
//  ADDRESS_MAP( megast_map )
//-------------------------------------------------

void st_state::megast_super_map(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x3fffff).ram().share(m_mainram);
	map(0x000000, 0x000007).rom().region(M68000_TAG, 0);
	//map(0xfa0000, 0xfbffff)      // mapped by the cartslot
	map(0xfc0000, 0xfeffff).rom().region(M68000_TAG, 0);
//  map(0xff7f30, 0xff7f31).rw(m_stb, FUNC(st_blitter_device::dst_inc_y_r), FUNC(st_blitter_device::dst_inc_y_w) // for TOS 1.02
	map(0xff8000, 0xff8fff).m(m_mmu, FUNC(st_mmu_device::map));
	map(0xff8000, 0xff8fff).m(m_video, FUNC(st_video_device::map));
#if 0
	map(0xff8200, 0xff8203).rw(m_videox, FUNC(stx_video_device::shifter_base_r), FUNC(stx_video_device::shifter_base_w)).umask16(0x00ff);
	map(0xff8204, 0xff8209).r(m_videox, FUNC(stx_video_device::shifter_counter_r)).umask16(0x00ff);
	map(0xff820a, 0xff820a).rw(m_videox, FUNC(stx_video_device::shifter_sync_r), FUNC(stx_video_device::shifter_sync_w));
	map(0xff8240, 0xff825f).rw(m_videox, FUNC(stx_video_device::shifter_palette_r), FUNC(stx_video_device::shifter_palette_w));
	map(0xff8260, 0xff8260).rw(m_videox, FUNC(stx_video_device::shifter_mode_r), FUNC(stx_video_device::shifter_mode_w));
#endif
	map(0xff8800, 0xff8800).rw(YM2149_TAG, FUNC(ay8910_device::data_r), FUNC(ay8910_device::address_w));
	map(0xff8802, 0xff8802).w(YM2149_TAG, FUNC(ay8910_device::data_w));
	map(0xff8a00, 0xff8a1f).rw(m_stb, FUNC(st_blitter_device::halftone_r), FUNC(st_blitter_device::halftone_w));
	map(0xff8a20, 0xff8a21).rw(m_stb, FUNC(st_blitter_device::src_inc_x_r), FUNC(st_blitter_device::src_inc_x_w));
	map(0xff8a22, 0xff8a23).rw(m_stb, FUNC(st_blitter_device::src_inc_y_r), FUNC(st_blitter_device::src_inc_y_w));
	map(0xff8a24, 0xff8a27).rw(m_stb, FUNC(st_blitter_device::src_r), FUNC(st_blitter_device::src_w));
	map(0xff8a28, 0xff8a2d).rw(m_stb, FUNC(st_blitter_device::end_mask_r), FUNC(st_blitter_device::end_mask_w));
	map(0xff8a2e, 0xff8a2f).rw(m_stb, FUNC(st_blitter_device::dst_inc_x_r), FUNC(st_blitter_device::dst_inc_x_w));
	map(0xff8a30, 0xff8a31).rw(m_stb, FUNC(st_blitter_device::dst_inc_y_r), FUNC(st_blitter_device::dst_inc_y_w));
	map(0xff8a32, 0xff8a35).rw(m_stb, FUNC(st_blitter_device::dst_r), FUNC(st_blitter_device::dst_w));
	map(0xff8a36, 0xff8a37).rw(m_stb, FUNC(st_blitter_device::count_x_r), FUNC(st_blitter_device::count_x_w));
	map(0xff8a38, 0xff8a39).rw(m_stb, FUNC(st_blitter_device::count_y_r), FUNC(st_blitter_device::count_y_w));
	map(0xff8a3a, 0xff8a3b).rw(m_stb, FUNC(st_blitter_device::op_r), FUNC(st_blitter_device::op_w));
	map(0xff8a3c, 0xff8a3d).rw(m_stb, FUNC(st_blitter_device::ctrl_r), FUNC(st_blitter_device::ctrl_w));
	map(0xfffa00, 0xfffa3f).rw(m_mfp, FUNC(mc68901_device::read), FUNC(mc68901_device::write)).umask16(0x00ff);
	map(0xfffa40, 0xfffa57).rw(FUNC(st_state::fpu_r), FUNC(st_state::fpu_w));
	map(0xfffc00, 0xfffc03).rw(m_acia[0], FUNC(acia6850_device::read), FUNC(acia6850_device::write)).umask16(0xff00);
	map(0xfffc04, 0xfffc07).rw(m_acia[1], FUNC(acia6850_device::read), FUNC(acia6850_device::write)).umask16(0xff00);
	map(0xfffc20, 0xfffc3f).rw(RP5C15_TAG, FUNC(rp5c15_device::read), FUNC(rp5c15_device::write)).umask16(0x00ff);
}


//-------------------------------------------------
//  ADDRESS_MAP( ste_map )
//-------------------------------------------------

void ste_state::ste_super_map(address_map &map)
{
	// Ram mapped by the mmu
	map.unmap_value_high();
	map(0x000000, 0x000007).rom().region(M68000_TAG, 0);
	map(0x000000, 0x000007).before_delay(NAME([](offs_t) { return 64; })).w(m_maincpu, FUNC(m68000_device::berr_w));
	map(0x400000, 0xf9ffff).before_delay(NAME([](offs_t) { return 64; })).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	//map(0xfa0000, 0xfbffff)      // mapped by the cartslot
	map(0xe00000, 0xe3ffff).rom().region(M68000_TAG, 0);

	map(0xff8000, 0xff8fff).m(m_mmu, FUNC(st_mmu_device::map));
	map(0xff8800, 0xff8800).rw(YM2149_TAG, FUNC(ay8910_device::data_r), FUNC(ay8910_device::address_w)).mirror(0xfc);
	map(0xff8802, 0xff8802).rw(YM2149_TAG, FUNC(ay8910_device::data_r), FUNC(ay8910_device::data_w)).mirror(0xfc);
	map(0xff8901, 0xff8901).rw(FUNC(ste_state::sound_dma_control_r), FUNC(ste_state::sound_dma_control_w));
	map(0xff8902, 0xff8907).rw(FUNC(ste_state::sound_dma_base_r), FUNC(ste_state::sound_dma_base_w)).umask16(0x00ff);
	map(0xff8908, 0xff890d).r(FUNC(ste_state::sound_dma_counter_r)).umask16(0x00ff);
	map(0xff890e, 0xff8913).rw(FUNC(ste_state::sound_dma_end_r), FUNC(ste_state::sound_dma_end_w)).umask16(0x00ff);
	map(0xff8921, 0xff8921).rw(FUNC(ste_state::sound_mode_r), FUNC(ste_state::sound_mode_w));
	map(0xff8922, 0xff8923).rw(FUNC(ste_state::microwire_data_r), FUNC(ste_state::microwire_data_w));
	map(0xff8924, 0xff8925).rw(FUNC(ste_state::microwire_mask_r), FUNC(ste_state::microwire_mask_w));
	map(0xff8a00, 0xff8a1f).rw(m_stb, FUNC(st_blitter_device::halftone_r), FUNC(st_blitter_device::halftone_w));
	map(0xff8a20, 0xff8a21).rw(m_stb, FUNC(st_blitter_device::src_inc_x_r), FUNC(st_blitter_device::src_inc_x_w));
	map(0xff8a22, 0xff8a23).rw(m_stb, FUNC(st_blitter_device::src_inc_y_r), FUNC(st_blitter_device::src_inc_y_w));
	map(0xff8a24, 0xff8a27).rw(m_stb, FUNC(st_blitter_device::src_r), FUNC(st_blitter_device::src_w));
	map(0xff8a28, 0xff8a2d).rw(m_stb, FUNC(st_blitter_device::end_mask_r), FUNC(st_blitter_device::end_mask_w));
	map(0xff8a2e, 0xff8a2f).rw(m_stb, FUNC(st_blitter_device::dst_inc_x_r), FUNC(st_blitter_device::dst_inc_x_w));
	map(0xff8a30, 0xff8a31).rw(m_stb, FUNC(st_blitter_device::dst_inc_y_r), FUNC(st_blitter_device::dst_inc_y_w));
	map(0xff8a32, 0xff8a35).rw(m_stb, FUNC(st_blitter_device::dst_r), FUNC(st_blitter_device::dst_w));
	map(0xff8a36, 0xff8a37).rw(m_stb, FUNC(st_blitter_device::count_x_r), FUNC(st_blitter_device::count_x_w));
	map(0xff8a38, 0xff8a39).rw(m_stb, FUNC(st_blitter_device::count_y_r), FUNC(st_blitter_device::count_y_w));
	map(0xff8a3a, 0xff8a3b).rw(m_stb, FUNC(st_blitter_device::op_r), FUNC(st_blitter_device::op_w));
	map(0xff8a3c, 0xff8a3d).rw(m_stb, FUNC(st_blitter_device::ctrl_r), FUNC(st_blitter_device::ctrl_w));
	map(0xff9200, 0xff9201).portr("JOY0");
	map(0xff9202, 0xff9203).portr("JOY1");
	map(0xff9210, 0xff9211).portr("PADDLE0X");
	map(0xff9212, 0xff9213).portr("PADDLE0Y");
	map(0xff9214, 0xff9215).portr("PADDLE1X");
	map(0xff9216, 0xff9217).portr("PADDLE1Y");
	map(0xff9220, 0xff9221).portr("GUNX");
	map(0xff9222, 0xff9223).portr("GUNY");
	map(0xfffa00, 0xfffa3f).rw(m_mfp, FUNC(mc68901_device::read), FUNC(mc68901_device::write)).umask16(0x00ff);
	map(0xfffc00, 0xfffc03).rw(m_acia[0], FUNC(acia6850_device::read), FUNC(acia6850_device::write)).umask16(0xff00);
	map(0xfffc04, 0xfffc07).rw(m_acia[1], FUNC(acia6850_device::read), FUNC(acia6850_device::write)).umask16(0xff00);
}


//-------------------------------------------------
//  ADDRESS_MAP( megaste_map )
//-------------------------------------------------

void megaste_state::megaste_super_map(address_map &map)
{
}


//-------------------------------------------------
//  ADDRESS_MAP( stbook_map )
//-------------------------------------------------
#if 0
void stbook_state::stbook_map(address_map &map)
{
	map(0x000000, 0x3fffff).ram().share(m_mainram);
//  map(0xd40000, 0xd7ffff).rom();
	map(0xe00000, 0xe3ffff).rom().region(M68000_TAG, 0);
//  map(0xe80000, 0xebffff).rom();
//  map(0xfa0000, 0xfbffff).rom(); // cartridge
	map(0xfc0000, 0xfeffff).rom().region(M68000_TAG, 0);
/*  map(0xf00000, 0xf1ffff).rw(FUNC(stbook_state::stbook_ide_r), FUNC(stbook_state::stbook_ide_w));
    map(0xff8000, 0xff8001).rw(FUNC(stbook_state::stbook_mmu_r), FUNC(stbook_state::stbook_mmu_w));
    map(0xff8200, 0xff8203).rw(m_videox, FUNC(stbook_video_device::stbook_shifter_base_r), FUNC(stbook_video_device::stbook_shifter_base_w));
    map(0xff8204, 0xff8209).rw(m_videox, FUNC(stbook_video_device::stbook_shifter_counter_r), FUNC(stbook_video_device::stbook_shifter_counter_w));
    map(0xff820a, 0xff820a).rw(m_videox, FUNC(stbook_video_device::stbook_shifter_sync_r), FUNC(stbook_video_device::stbook_shifter_sync_w));
    map(0xff820c, 0xff820d).rw(m_videox, FUNC(stbook_video_device::stbook_shifter_base_low_r), FUNC(stbook_video_device::stbook_shifter_base_low_w));
    map(0xff820e, 0xff820f).rw(m_videox, FUNC(stbook_video_device::stbook_shifter_lineofs_r), FUNC(stbook_video_device::stbook_shifter_lineofs_w));
    map(0xff8240, 0xff8241).rw(m_videox, FUNC(stbook_video_device::stbook_shifter_palette_r), FUNC(stbook_video_device::stbook_shifter_palette_w));
    map(0xff8260, 0xff8260).rw(m_videox, FUNC(stbook_video_device::stbook_shifter_mode_r), FUNC(stbook_video_device::stbook_shifter_mode_w));
    map(0xff8264, 0xff8265).rw(m_videox, FUNC(stbook_video_device::stbook_shifter_pixelofs_r), FUNC(stbook_video_device::stbook_shifter_pixelofs_w));
    map(0xff827e, 0xff827f).w(m_videox, FUNC(stbook_video_device::lcd_control_w));*/
	map(0xff8800, 0xff8800).rw(YM3439_TAG, FUNC(ay8910_device::data_r), FUNC(ay8910_device::address_w));
	map(0xff8802, 0xff8802).w(YM3439_TAG, FUNC(ay8910_device::data_w));
/*  map(0xff8901, 0xff8901).rw(FUNC(stbook_state::sound_dma_control_r), FUNC(stbook_state::sound_dma_control_w));
    map(0xff8902, 0xff8907).rw(FUNC(stbook_state::sound_dma_base_r), FUNC(stbook_state::sound_dma_base_w)).umask16(0x00ff);
    map(0xff8908, 0xff890d).r(FUNC(stbook_state::sound_dma_counter_r)).umask16(0x00ff);
    map(0xff890e, 0xff8913).rw(FUNC(stbook_state::sound_dma_end_r), FUNC(stbook_state::sound_dma_end_w)).umask16(0x00ff);
    map(0xff8921, 0xff8921).rw(FUNC(stbook_state::sound_mode_r), FUNC(stbook_state::sound_mode_w));
    map(0xff8922, 0xff8923).rw(FUNC(stbook_state::microwire_data_r), FUNC(stbook_state::microwire_data_w));
    map(0xff8924, 0xff8925).rw(FUNC(stbook_state::microwire_mask_r), FUNC(stbook_state::microwire_mask_w));
    map(0xff8a00, 0xff8a1f).rw(m_stb, FUNC(st_blitter_device::halftone_r), FUNC(st_blitter_device::halftone_w));
    map(0xff8a20, 0xff8a21).rw(m_stb, FUNC(st_blitter_device::src_inc_x_r), FUNC(st_blitter_device::src_inc_x_w));
    map(0xff8a22, 0xff8a23).rw(m_stb, FUNC(st_blitter_device::src_inc_y_r), FUNC(st_blitter_device::src_inc_y_w));
    map(0xff8a24, 0xff8a27).rw(m_stb, FUNC(st_blitter_device::src_r), FUNC(st_blitter_device::src_w));
    map(0xff8a28, 0xff8a2d).rw(m_stb, FUNC(st_blitter_device::end_mask_r), FUNC(st_blitter_device::end_mask_w));
    map(0xff8a2e, 0xff8a2f).rw(m_stb, FUNC(st_blitter_device::dst_inc_x_r), FUNC(st_blitter_device::dst_inc_x_w));
    map(0xff8a30, 0xff8a31).rw(m_stb, FUNC(st_blitter_device::dst_inc_y_r), FUNC(st_blitter_device::dst_inc_y_w));
    map(0xff8a32, 0xff8a35).rw(m_stb, FUNC(st_blitter_device::dst_r), FUNC(st_blitter_device::dst_w));
    map(0xff8a36, 0xff8a37).rw(m_stb, FUNC(st_blitter_device::count_x_r), FUNC(st_blitter_device::count_x_w));
    map(0xff8a38, 0xff8a39).rw(m_stb, FUNC(st_blitter_device::count_y_r), FUNC(st_blitter_device::count_y_w));
    map(0xff8a3a, 0xff8a3b).rw(m_stb, FUNC(st_blitter_device::op_r), FUNC(st_blitter_device::op_w));
    map(0xff8a3c, 0xff8a3d).rw(m_stb, FUNC(st_blitter_device::ctrl_r), FUNC(st_blitter_device::ctrl_w));
    map(0xff9200, 0xff9201).r(FUNC(stbook_state::config_r));
    map(0xff9202, 0xff9203).rw(FUNC(stbook_state::lcd_contrast_r), FUNC(stbook_state::lcd_contrast_w));
    map(0xff9210, 0xff9211).rw(FUNC(stbook_state::power_r), FUNC(stbook_state::power_w));
    map(0xff9214, 0xff9215).rw(FUNC(stbook_state::reference_r), FUNC(stbook_state::reference_w));*/
}
#endif


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


//-------------------------------------------------
//  INPUT_PORTS( st )
//-------------------------------------------------

static INPUT_PORTS_START( st )
	PORT_START("config")
	PORT_CONFNAME( 0x80, 0x80, "Monitor") PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(st_state::write_monochrome))
	PORT_CONFSETTING( 0x00, "Monochrome (Atari SM124)" )
	PORT_CONFSETTING( 0x80, "Color (Atari SC1224)" )
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( ste )
//-------------------------------------------------

static INPUT_PORTS_START( ste )
	PORT_START("config")
	PORT_CONFNAME( 0x80, 0x80, "Monitor") PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(ste_state::write_monochrome))
	PORT_CONFSETTING( 0x00, "Monochrome (Atari SM124)" )
	PORT_CONFSETTING( 0x80, "Color (Atari SC1435)" )

	PORT_START("JOY0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(3)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(4)
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("JOY1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )    PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )  PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )  PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )    PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(3) PORT_8WAY
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )  PORT_PLAYER(3) PORT_8WAY
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )  PORT_PLAYER(3) PORT_8WAY
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )    PORT_PLAYER(3) PORT_8WAY
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(4) PORT_8WAY
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )  PORT_PLAYER(4) PORT_8WAY
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )  PORT_PLAYER(4) PORT_8WAY
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )    PORT_PLAYER(4) PORT_8WAY

	PORT_START("PADDLE0X")
	PORT_BIT( 0xff, 0x00, IPT_PADDLE ) PORT_SENSITIVITY(30) PORT_KEYDELTA(15) PORT_PLAYER(1)

	PORT_START("PADDLE0Y")
	PORT_BIT( 0xff, 0x00, IPT_PADDLE_V ) PORT_SENSITIVITY(30) PORT_KEYDELTA(15) PORT_PLAYER(1)

	PORT_START("PADDLE1X")
	PORT_BIT( 0xff, 0x00, IPT_PADDLE ) PORT_SENSITIVITY(30) PORT_KEYDELTA(15) PORT_PLAYER(2)

	PORT_START("PADDLE1Y")
	PORT_BIT( 0xff, 0x00, IPT_PADDLE_V ) PORT_SENSITIVITY(30) PORT_KEYDELTA(15) PORT_PLAYER(2)

	PORT_START("GUNX") // should be 10-bit
	PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(50) PORT_KEYDELTA(10) PORT_PLAYER(1)

	PORT_START("GUNY") // should be 10-bit
	PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_SENSITIVITY(70) PORT_KEYDELTA(10) PORT_PLAYER(1)
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( stbook )
//-------------------------------------------------

#if 0
static INPUT_PORTS_START( stbook )
	PORT_START("SW400")
	PORT_DIPNAME( 0x80, 0x80, "DMA sound hardware")
	PORT_DIPSETTING( 0x00, DEF_STR( No ) )
	PORT_DIPSETTING( 0x80, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x40, 0x00, "WD1772 FDC")
	PORT_DIPSETTING( 0x40, "Low Speed (8 MHz)" )
	PORT_DIPSETTING( 0x00, "High Speed (16 MHz)" )
	PORT_DIPNAME( 0x20, 0x00, "Bypass Self Test")
	PORT_DIPSETTING( 0x00, DEF_STR( No ) )
	PORT_DIPSETTING( 0x20, DEF_STR( Yes ) )
	PORT_BIT( 0x1f, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END
#endif


//-------------------------------------------------
//  INPUT_PORTS( tt030 )
//-------------------------------------------------

static INPUT_PORTS_START( tt030 )
	PORT_INCLUDE(ste)
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( falcon )
//-------------------------------------------------

static INPUT_PORTS_START( falcon )
	PORT_INCLUDE(ste)
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  ay8910_interface psg_intf
//-------------------------------------------------

void st_state::psg_pa_w(uint8_t data)
{
	/*

	    bit     description

	    0       SIDE 0
	    1       DRIVE 0
	    2       DRIVE 1
	    3       RTS
	    4       DTR
	    5       STROBE
	    6       GPO
	    7

	*/

	// drive select
	floppy_image_device *floppy = nullptr;
	if (!BIT(data, 1))
		floppy = m_floppy[0]->get_device();
	else if (!BIT(data, 2))
		floppy = m_floppy[1]->get_device();

	// side select
	if (floppy)
		floppy->ss_w(BIT(data, 0) ? 0 : 1);

	m_fdc->set_floppy(floppy);

	// request to send
	m_rs232->write_rts(BIT(data, 3));

	// data terminal ready
	m_rs232->write_dtr(BIT(data, 4));

	// centronics strobe
	m_centronics->write_strobe(BIT(data, 5));
}

//-------------------------------------------------
//  ay8910_interface stbook_psg_intf
//-------------------------------------------------

void stbook_state::psg_pa_w(uint8_t data)
{
	/*

	    bit     description

	    0       SIDE 0
	    1       DRIVE 0
	    2       DRIVE 1
	    3       RTS
	    4       DTR
	    5       STROBE
	    6       IDE RESET
	    7       DDEN

	*/

	// drive select
	floppy_image_device *floppy = nullptr;
	if (!BIT(data, 1))
		floppy = m_floppy[0]->get_device();
	else if (!BIT(data, 2))
		floppy = m_floppy[1]->get_device();

	// side select
	if (floppy)
		floppy->ss_w(BIT(data, 0) ? 0 : 1);

	m_fdc->set_floppy(floppy);

	// request to send
	m_rs232->write_rts(BIT(data, 3));

	// data terminal ready
	m_rs232->write_dtr(BIT(data, 4));

	// centronics strobe
	m_centronics->write_strobe(BIT(data, 5));

	// density select
	m_fdc->dden_w(BIT(data, 7));
}

//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( st )
//-------------------------------------------------

void st_state::machine_start()
{
	m_mmu->set_ram_size(m_ramcfg->size());

	m_cart->map(m_maincpu->space(AS_PROGRAM));
	m_cart->map(m_maincpu->space(m68000_device::AS_USER_PROGRAM));

	/// TODO: get callbacks to trigger these.
	m_mfp->i0_w(1);
	m_mfp->i4_w(1);
	m_mfp->i5_w(1);
	m_mfp->i7_w(1);
}


//-------------------------------------------------
//  state_save -
//-------------------------------------------------

void ste_state::state_save()
{
	save_item(NAME(m_dmasnd_base));
	save_item(NAME(m_dmasnd_end));
	save_item(NAME(m_dmasnd_cntr));
	save_item(NAME(m_dmasnd_baselatch));
	save_item(NAME(m_dmasnd_endlatch));
	save_item(NAME(m_dmasnd_ctrl));
	save_item(NAME(m_dmasnd_mode));
	save_item(NAME(m_dmasnd_fifo));
	save_item(NAME(m_dmasnd_samples));
	save_item(NAME(m_dmasnd_active));
	save_item(NAME(m_mw_data));
	save_item(NAME(m_mw_mask));
	save_item(NAME(m_mw_shift));
}


//-------------------------------------------------
//  MACHINE_START( ste )
//-------------------------------------------------

void ste_state::machine_start()
{
	m_mmu->set_ram_size(m_ramcfg->size());

	m_cart->map(m_maincpu->space(AS_PROGRAM));
	m_cart->map(m_maincpu->space(m68000_device::AS_USER_PROGRAM));

	/* allocate timers */
	m_dmasound_timer = timer_alloc(FUNC(ste_state::dmasound_tick), this);
	m_microwire_timer = timer_alloc(FUNC(ste_state::microwire_tick), this);

	/* register for state saving */
	state_save();

	/// TODO: get callbacks to trigger these.
	m_mfp->i0_w(1);
	m_mfp->i4_w(1);
	m_mfp->i5_w(1);
	m_mfp->i7_w(1);
}


//-------------------------------------------------
//  MACHINE_START( megaste )
//-------------------------------------------------

void megaste_state::machine_start()
{
	ste_state::machine_start();

	save_item(NAME(m_cache));
}


//-------------------------------------------------
//  MACHINE_START( stbook )
//-------------------------------------------------

void stbook_state::machine_start()
{
	/* configure RAM size */
	address_space &program = m_maincpu->space(AS_PROGRAM);

	switch (m_ramcfg->size())
	{
	case 1024 * 1024:
		program.unmap_readwrite(0x100000, 0x3fffff);
		break;
	}

	m_cart->map(m_maincpu->space(AS_PROGRAM));
	m_cart->map(m_maincpu->space(m68000_device::AS_USER_PROGRAM));

	/* register for state saving */
	ste_state::state_save();

	/// TODO: get callbacks to trigger these.
	m_mfp->i0_w(1);
	m_mfp->i4_w(1);
	m_mfp->i5_w(1);
}

void st_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_ST_FORMAT);
	fr.add(FLOPPY_MSA_FORMAT);
	fr.add(FLOPPY_PASTI_FORMAT);
	fr.add(FLOPPY_IPF_FORMAT);
}

static void atari_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
}



//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

void st_state::common(machine_config &config)
{
	// basic machine hardware
	M68000(config, m_maincpu, Y2/4);
	m_maincpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &st_state::cpu_space_map);
	m_maincpu->set_addrmap(m68000_base_device::AS_USER_PROGRAM, &st_state::st_user_map);
	m_maincpu->reset_cb().set(FUNC(st_state::reset_w));

	ST_MMU(config, m_mmu);
	m_mmu->set_ram(m_mainram);
	m_mmu->set_cpu(m_maincpu);
	m_mmu->set_fdc(m_fdc);

	// sound
	YM2149(config, m_ymsnd, Y2/16);
	m_ymsnd->set_flags(AY8910_SINGLE_OUTPUT);
	m_ymsnd->set_resistors_load(RES_K(1), 0, 0);
	m_ymsnd->port_a_write_callback().set(FUNC(st_state::psg_pa_w));
	m_ymsnd->port_b_write_callback().set("cent_data_out", FUNC(output_latch_device::write));

	// devices
	WD1772(config, m_fdc, Y2/4);
	m_fdc->intrq_wr_callback().set(m_mfp, FUNC(mc68901_device::i5_w)).invert();
	m_fdc->drq_wr_callback().set(m_mmu, FUNC(st_mmu_device::fdc_drq_w));
	FLOPPY_CONNECTOR(config, WD1772_TAG ":0", atari_floppies, "35dd",  st_state::floppy_formats);
	FLOPPY_CONNECTOR(config, WD1772_TAG ":1", atari_floppies, nullptr, st_state::floppy_formats);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(m_mfp, FUNC(mc68901_device::i0_w));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	MC68901(config, m_mfp, Y2/8);
	m_mfp->set_timer_clock(Y1);
	m_mfp->out_irq_cb().set_inputline(m_maincpu, M68K_IRQ_6);
	m_mfp->out_tdo_cb().set(m_mfp, FUNC(mc68901_device::tc_w));
	m_mfp->out_tdo_cb().append(m_mfp, FUNC(mc68901_device::rc_w));
	m_mfp->out_so_cb().set(m_rs232, FUNC(rs232_port_device::write_txd));

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set(m_mfp, FUNC(mc68901_device::si_w));
	m_rs232->dcd_handler().set(m_mfp, FUNC(mc68901_device::i1_w));
	m_rs232->cts_handler().set(m_mfp, FUNC(mc68901_device::i2_w));
	m_rs232->ri_handler().set(m_mfp, FUNC(mc68901_device::i6_w));

	ST_KBD(config, m_ikbd);

	ACIA6850(config, m_acia[0]);
	m_acia[0]->txd_handler().set(m_ikbd, FUNC(st_kbd_device::tx_w));
	m_ikbd->rx_cb().set(m_acia[0], FUNC(acia6850_device::write_rxd));
	m_acia[0]->irq_handler().set("aciairq", FUNC(input_merger_device::in_w<0>));
	m_acia[0]->write_cts(0);
	m_acia[0]->write_dcd(0);

	ACIA6850(config, m_acia[1]);
	m_acia[1]->txd_handler().set("mdout", FUNC(midi_port_device::write_txd));
	m_acia[1]->irq_handler().set("aciairq", FUNC(input_merger_device::in_w<1>));
	m_acia[1]->write_cts(0);
	m_acia[1]->write_dcd(0);

	input_merger_device &aciairq(INPUT_MERGER_ANY_HIGH(config, "aciairq"));
	aciairq.output_handler().set(m_mfp, FUNC(mc68901_device::i4_w)).invert();

	MIDI_PORT(config, "mdin", midiin_slot, "midiin").rxd_handler().set(m_acia[1], FUNC(acia6850_device::write_rxd));
	MIDI_PORT(config, "mdout", midiout_slot, "midiout");

	clock_device &acia_clock(CLOCK(config, "acia_clock", Y2/64)); // 500kHz
	acia_clock.signal_handler().set(m_acia[0], FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append(m_acia[0], FUNC(acia6850_device::write_rxc));
	acia_clock.signal_handler().append(m_acia[1], FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append(m_acia[1], FUNC(acia6850_device::write_rxc));

	// cartridge

	STCART_CONNECTOR(config, m_cart, stcart_intf, "rom");

	// software lists
	SOFTWARE_LIST(config, "flop_list").set_original("st_flop");
	SOFTWARE_LIST(config, "cart_list").set_original("st_cart");
}

//-------------------------------------------------
//  machine_config( st )
//-------------------------------------------------

void st_state::st(machine_config &config)
{
	common(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &st_state::st_super_map);

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_screen_update(m_video, FUNC(st_video_device::screen_update));

	ST_VIDEO(config, m_video, Y2);
	m_video->set_screen(m_screen);
	m_video->set_ram(m_mainram);
	m_video->set_mmu(m_mmu);
	m_video->de_cb().set(m_mfp, FUNC(mc68901_device::tbi_w));
	m_video->hsync_cb().set([this](int state) { if(!state) m_maincpu->set_input_line(2, ASSERT_LINE); });
	m_video->vsync_cb().set([this](int state) { if(!state) m_maincpu->set_input_line(4, ASSERT_LINE); });

	// sound hardware
	SPEAKER(config, "mono").front_center();
	m_ymsnd->add_route(ALL_OUTPUTS, "mono", 1.00);

	// internal ram
	RAM(config, m_ramcfg);
	m_ramcfg->set_default_size("1M"); // 1040ST
	m_ramcfg->set_extra_options("512K,256K"); // 520ST, 260ST
}


//-------------------------------------------------
//  machine_config( megast )
//-------------------------------------------------

void megast_state::megast(machine_config &config)
{
	common(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &megast_state::megast_super_map);

	ST_BLITTER(config, m_stb, Y2/4);
	m_stb->set_space(m_maincpu, AS_PROGRAM);
	m_stb->int_callback().set(m_mfp, FUNC(mc68901_device::i3_w));

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_screen_update(m_video, FUNC(st_video_device::screen_update));

	ST_VIDEO(config, m_video, Y2);
	m_video->set_screen(m_screen);
	m_video->set_ram(m_mainram);
	m_video->set_mmu(m_mmu);
	m_video->de_cb().set(m_mfp, FUNC(mc68901_device::tbi_w));
	m_video->hsync_cb().set_inputline(m_maincpu, 2);
	m_video->vsync_cb().set_inputline(m_maincpu, 4);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	m_ymsnd->add_route(ALL_OUTPUTS, "mono", 1.00);

	// devices
	RP5C15(config, RP5C15_TAG, XTAL(32'768));

	// internal ram
	RAM(config, m_ramcfg);
	m_ramcfg->set_default_size("4M"); // Mega ST 4
	m_ramcfg->set_extra_options("2M,1M"); // Mega ST 2, Mega ST 1
}


//-------------------------------------------------
//  machine_config( ste )
//-------------------------------------------------

void ste_state::ste(machine_config &config)
{
	common(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &ste_state::ste_super_map);

	ST_BLITTER(config, m_stb, Y2/4);
	m_stb->set_space(m_maincpu, AS_PROGRAM);
	m_stb->int_callback().set(m_mfp, FUNC(mc68901_device::i3_w));

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_screen_update(m_videox, FUNC(ste_video_device::screen_update));
	m_screen->set_raw(Y2/4, ATARIST_HTOT_PAL, ATARIST_HBEND_PAL, ATARIST_HBSTART_PAL, ATARIST_VTOT_PAL, ATARIST_VBEND_PAL, ATARIST_VBSTART_PAL);

	STE_VIDEO(config, m_videox, Y2);
	m_videox->set_screen(m_screen);
	m_videox->set_ram_space(m_maincpu, AS_PROGRAM);
	m_videox->de_callback().set(m_mfp, FUNC(mc68901_device::tbi_w));

	// sound hardware
	SPEAKER(config, "speaker", 2).front();
	m_ymsnd->add_route(0, "speaker", 0.50, 0);
	m_ymsnd->add_route(0, "speaker", 0.50, 1);
/*
    custom_device &custom_dac(CUSTOM(config, "custom", 0)); // DAC
    custom_dac.add_route(0, "speaker", 0.50);
    custom_dac.add_route(1, "speaker", 0.50);
*/
	LMC1992(config, LMC1992_TAG);

	// internal ram
	RAM(config, m_ramcfg);
	m_ramcfg->set_default_size("1M"); // 1040STe
	m_ramcfg->set_extra_options("512K"); // 520STe
}


//-------------------------------------------------
//  machine_config( megaste )
//-------------------------------------------------

void megaste_state::megaste(machine_config &config)
{
	ste(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &megaste_state::megaste_super_map);
	RP5C15(config, RP5C15_TAG, XTAL(32'768));
	SCC8530(config, Z8530_TAG, Y2/4);

	/* internal ram */
	m_ramcfg->set_default_size("4M"); // Mega STe 4
	m_ramcfg->set_extra_options("2M,1M"); // Mega STe 2, Mega STe 1
}


//-------------------------------------------------
//  machine_config( stbook )
//-------------------------------------------------
#if 0
void stbook_state::stbook(machine_config &config)
{
	// basic machine hardware
	M68000(config, m_maincpu, U517/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &stbook_state::stbook_map);
	m_maincpu->reset_cb().set(FUNC(st_state::reset_w));

	//COP888(config, COP888_TAG, Y700);

	ST_BLITTER(config, m_stb, U517/2);
	m_stb->set_space(m_maincpu, AS_PROGRAM);
	m_stb->int_callback().set(m_mfp, FUNC(mc68901_device::i3_w));

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_screen_update(m_videox, FUNC(stbook_video_device::screen_update));
	m_screen->set_refresh_hz(60);
	m_screen->set_size(640, 400);
	m_screen->set_visarea(0, 639, 0, 399);

	STBOOK_VIDEO(config, m_videox, Y2);
	m_videox->set_screen(m_screen);
	m_videox->set_ram_space(m_maincpu, AS_PROGRAM);
	m_videox->de_callback().set(m_mfp, FUNC(mc68901_device::tbi_w));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	ym3439_device &ym3439(YM3439(config, YM3439_TAG, U517/8));
	ym3439.set_flags(AY8910_SINGLE_OUTPUT);
	ym3439.set_resistors_load(RES_K(1), 0, 0);
	ym3439.port_a_write_callback().set(FUNC(stbook_state::psg_pa_w));
	ym3439.port_b_write_callback().set("cent_data_out", FUNC(output_latch_device::write));
	ym3439.add_route(ALL_OUTPUTS, "mono", 1.00);

	MC68901(config, m_mfp, U517/8);
	m_mfp->set_timer_clock(Y1);
	m_mfp->out_irq_cb().set_inputline(M68000_TAG, M68K_IRQ_6);
	m_mfp->out_tdo_cb().set(m_mfp, FUNC(mc68901_device::tc_w));
	m_mfp->out_tdo_cb().append(m_mfp, FUNC(mc68901_device::rc_w));
	m_mfp->out_so_cb().set(RS232_TAG, FUNC(rs232_port_device::write_txd));

	WD1772(config, m_fdc, U517/2);
	m_fdc->intrq_wr_callback().set(m_mfp, FUNC(mc68901_device::i5_w)).invert();
	m_fdc->drq_wr_callback().set(FUNC(st_state::fdc_drq_w));
	FLOPPY_CONNECTOR(config, WD1772_TAG ":0", atari_floppies, "35dd", 0, st_state::floppy_formats);
	FLOPPY_CONNECTOR(config, WD1772_TAG ":1", atari_floppies, 0,      0, st_state::floppy_formats);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(m_mfp, FUNC(mc68901_device::i0_w));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out");
	m_centronics->set_output_latch(cent_data_out);

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set(m_mfp, FUNC(mc68901_device::si_w));
	m_rs232->dcd_handler().set(m_mfp, FUNC(mc68901_device::i1_w));
	m_rs232->cts_handler().set(m_mfp, FUNC(mc68901_device::i2_w));
	m_rs232->ri_handler().set(m_mfp, FUNC(mc68901_device::i6_w));

	ST_KBD(config, m_ikbd);

	ACIA6850(config, m_acia[0]);
	m_acia[0]->txd_handler().set(m_ikbd, FUNC(st_kbd_device::tx_w));
	m_ikbd->rx_cb().set(m_acia[0], FUNC(acia6850_device::write_rxd));
	m_acia[0]->irq_handler().set("aciairq", FUNC(input_merger_device::in_w<0>));
	m_acia[0]->write_cts(0);
	m_acia[0]->write_dcd(0);

	ACIA6850(config, m_acia[1]);
	m_acia[1]->txd_handler().set("mdout", FUNC(midi_port_device::write_txd));
	m_acia[1]->irq_handler().set("aciairq", FUNC(input_merger_device::in_w<1>));
	m_acia[1]->write_cts(0);
	m_acia[1]->write_dcd(0);

	input_merger_device &aciairq(INPUT_MERGER_ANY_HIGH(config, "aciairq"));
	aciairq.output_handler().set(m_mfp, FUNC(mc68901_device::i4_w)).invert();

	MIDI_PORT(config, "mdin", midiin_slot, "midiin").rxd_handler().set(m_acia[1], FUNC(acia6850_device::write_rxd));
	MIDI_PORT(config, "mdout", midiout_slot, "midiout");

	clock_device &acia_clock(CLOCK(config, "acia_clock", Y2/64)); // 500kHz
	acia_clock.signal_handler().set(m_acia[0], FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append(m_acia[0], FUNC(acia6850_device::write_rxc));
	acia_clock.signal_handler().append(m_acia[1], FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append(m_acia[1], FUNC(acia6850_device::write_rxc));

	// cartridge
	GENERIC_CARTSLOT(config, m_cart, generic_linear_slot, "st_cart", "bin,rom");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_endian(ENDIANNESS_BIG);
	SOFTWARE_LIST(config, "cart_list").set_original("st_cart");

	/* internal ram */
	RAM(config, m_ramcfg).set_default_size("4M").set_extra_options("1M");
}
#endif

//-------------------------------------------------
//  machine_config( tt030 )
//-------------------------------------------------

void ste_state::tt030(machine_config &config)
{
	ste(config);
}


//-------------------------------------------------
//  machine_config( falcon )
//-------------------------------------------------

void ste_state::falcon(machine_config &config)
{
	ste(config);
}


//-------------------------------------------------
//  machine_config( falcon40 )
//-------------------------------------------------

void ste_state::falcon40(machine_config &config)
{
	ste(config);
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( st )
//-------------------------------------------------

ROM_START( st )
	ROM_REGION16_BE( 0x30000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos100")
	ROM_SYSTEM_BIOS( 0, "tos099", "TOS 0.99 (Disk TOS)" )
	ROMX_LOAD( "tos099.bin", 0x00000, 0x04000, CRC(cee3c664) SHA1(80c10b31b63b906395151204ec0a4984c8cb98d6), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos100", "TOS 1.0 (ROM TOS)" )
	ROMX_LOAD( "tos100.bin", 0x00000, 0x30000, CRC(d331af30) SHA1(7bcc2311d122f451bd03c9763ade5a119b2f90da), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "tos102", "TOS 1.02 (MEGA TOS)" )
	ROMX_LOAD( "tos102.bin", 0x00000, 0x30000, CRC(d3c32283) SHA1(735793fdba07fe8d5295caa03484f6ef3de931f5), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "tos104", "TOS 1.04 (Rainbow TOS)" )
	ROMX_LOAD( "tos104.bin", 0x00000, 0x30000, CRC(90f4fbff) SHA1(2487f330b0895e5d88d580d4ecb24061125e88ad), ROM_BIOS(3) )
ROM_END


//-------------------------------------------------
//  ROM( st_uk )
//-------------------------------------------------

ROM_START( st_uk )
	ROM_REGION16_BE( 0x30000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos100")
	ROM_SYSTEM_BIOS( 0, "tos100", "TOS 1.0 (ROM TOS)" )
	ROMX_LOAD( "tos100uk.bin", 0x00000, 0x30000, CRC(1a586c64) SHA1(9a6e4c88533a9eaa4d55cdc040e47443e0226eb2), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos102", "TOS 1.02 (MEGA TOS)" )
	ROMX_LOAD( "tos102uk.bin", 0x00000, 0x30000, CRC(3b5cd0c5) SHA1(87900a40a890fdf03bd08be6c60cc645855cbce5), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "tos104", "TOS 1.04 (Rainbow TOS)" )
	ROMX_LOAD( "tos104uk.bin", 0x00000, 0x30000, CRC(a50d1d43) SHA1(9526ef63b9cb1d2a7109e278547ae78a5c1db6c6), ROM_BIOS(2) )
ROM_END


//-------------------------------------------------
//  ROM( st_de )
//-------------------------------------------------

ROM_START( st_de )
	ROM_REGION16_BE( 0x30000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos100")
	ROM_SYSTEM_BIOS( 0, "tos100", "TOS 1.0 (ROM TOS)" )
	ROMX_LOAD( "tos100de.bin", 0x00000, 0x30000, CRC(16e3e979) SHA1(663d9c87cfb44ae8ada855fe9ed3cccafaa7a4ce), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos102", "TOS 1.02 (MEGA TOS)" )
	ROMX_LOAD( "tos102de.bin", 0x00000, 0x30000, CRC(36a0058e) SHA1(cad5d2902e875d8bf0a14dc5b5b8080b30254148), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "tos104", "TOS 1.04 (Rainbow TOS)" )
	ROMX_LOAD( "tos104de.bin", 0x00000, 0x30000, CRC(62b82b42) SHA1(5313733f91b083c6265d93674cb9d0b7efd02da8), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "tos10x", "TOS 1.0?" )
	ROMX_LOAD( "st 7c1 a4.u4", 0x00000, 0x08000, CRC(867fdd7e) SHA1(320d12acf510301e6e9ab2e3cf3ee60b0334baa0), ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "st 7c1 a9.u7", 0x00001, 0x08000, CRC(30e8f982) SHA1(253f26ff64b202b2681ab68ffc9954125120baea), ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "st 7c1 b0.u3", 0x10000, 0x08000, CRC(b91337ed) SHA1(21a338f9bbd87bce4a12d38048e03a361f58d33e), ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "st 7a4 a6.u6", 0x10001, 0x08000, CRC(969d7bbe) SHA1(72b998c1f25211c2a96c81a038d71b6a390585c2), ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "st 7c1 a2.u2", 0x20000, 0x08000, CRC(d0513329) SHA1(49855a3585e2f75b2af932dd4414ed64e6d9501f), ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "st 7c1 b1.u5", 0x20001, 0x08000, CRC(c115cbc8) SHA1(2b52b81a1a4e0818d63f98ee4b25c30e2eba61cb), ROM_SKIP(1) | ROM_BIOS(3) )
ROM_END


//-------------------------------------------------
//  ROM( st_fr )
//-------------------------------------------------

ROM_START( st_fr )
	ROM_REGION16_BE( 0x30000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos100")
	ROM_SYSTEM_BIOS( 0, "tos100", "TOS 1.0 (ROM TOS)" )
	ROMX_LOAD( "tos100fr.bin", 0x00000, 0x30000, CRC(2b7f2117) SHA1(ecb00a2e351a6205089a281b4ce6e08959953704), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos102", "TOS 1.02 (MEGA TOS)" )
	ROMX_LOAD( "tos102fr.bin", 0x00000, 0x30000, CRC(8688fce6) SHA1(f5a79aac0a4e812ca77b6ac51d58d98726f331fe), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "tos104", "TOS 1.04 (Rainbow TOS)" )
	ROMX_LOAD( "tos104fr.bin", 0x00000, 0x30000, CRC(a305a404) SHA1(20dba880344b810cf63cec5066797c5a971db870), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "tos10x", "TOS 1.0?" )
	ROMX_LOAD( "c101658-001.u63", 0x00000, 0x08000, CRC(9c937f6f) SHA1(d4a3ea47568ef6233f3f2056e384b09eedd84961), ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "c101661-001.u67", 0x00001, 0x08000, CRC(997298f3) SHA1(9e06d42df88557252a36791b514afe455600f679), ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "c101657-001.u59", 0x10000, 0x08000, CRC(b63be6a1) SHA1(434f443472fc649568e4f8be6880f39c2def7819), ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "c101660-001.u62", 0x10001, 0x08000, CRC(a813892c) SHA1(d041c113050dfb00166c4a7a52766e1b7eac9cab), ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "c101656-001.u48", 0x20000, 0x08000, CRC(dbd93fb8) SHA1(cf9ec11e4bc2465490e7e6c981d9f61eae6cb359), ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "c101659-001.u53", 0x20001, 0x08000, CRC(67c9785a) SHA1(917a17e9f83bee015c25b327780eebb11cb2c5a5), ROM_SKIP(1) | ROM_BIOS(3) )
ROM_END


//-------------------------------------------------
//  ROM( st_es )
//-------------------------------------------------

ROM_START( st_es )
	ROM_REGION16_BE( 0x30000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos104")
	ROM_SYSTEM_BIOS( 0, "tos104", "TOS 1.04 (Rainbow TOS)" )
	ROMX_LOAD( "tos104es.bin", 0x00000, 0x30000, CRC(f4e8ecd2) SHA1(df63f8ac09125d0877b55d5ba1282779b7f99c16), ROM_BIOS(0) )
ROM_END


//-------------------------------------------------
//  ROM( st_nl )
//-------------------------------------------------

ROM_START( st_nl )
	ROM_REGION16_BE( 0x30000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos104")
	ROM_SYSTEM_BIOS( 0, "tos104", "TOS 1.04 (Rainbow TOS)" )
	ROMX_LOAD( "tos104nl.bin", 0x00000, 0x30000, CRC(bb4370d4) SHA1(6de7c96b2d2e5c68778f4bce3eaf85a4e121f166), ROM_BIOS(0) )
ROM_END


//-------------------------------------------------
//  ROM( st_se )
//-------------------------------------------------

ROM_START( st_se )
	ROM_REGION16_BE( 0x30000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos102")
	ROM_SYSTEM_BIOS( 0, "tos102", "TOS 1.02 (MEGA TOS)" )
	ROMX_LOAD( "tos102se.bin", 0x00000, 0x30000, CRC(673fd0c2) SHA1(433de547e09576743ae9ffc43d43f2279782e127), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos104", "TOS 1.04 (Rainbow TOS)" )
	ROMX_LOAD( "tos104se.bin", 0x00000, 0x30000, CRC(80ecfdce) SHA1(b7ad34d5cdfbe86ea74ae79eca11dce421a7bbfd), ROM_BIOS(1) )
ROM_END


//-------------------------------------------------
//  ROM( st_sg )
//-------------------------------------------------

ROM_START( st_sg )
	ROM_REGION16_BE( 0x30000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos102")
	ROM_SYSTEM_BIOS( 0, "tos102", "TOS 1.02 (MEGA TOS)" )
	ROMX_LOAD( "tos102sg.bin", 0x00000, 0x30000, CRC(5fe16c66) SHA1(45acb2fc4b1b13bd806c751aebd66c8304fc79bc), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos104", "TOS 1.04 (Rainbow TOS)" )
	ROMX_LOAD( "tos104sg.bin", 0x00000, 0x30000, CRC(e58f0bdf) SHA1(aa40bf7203f02b2251b9e4850a1a73ff1c7da106), ROM_BIOS(1) )
ROM_END


//-------------------------------------------------
//  ROM( megast )
//-------------------------------------------------

ROM_START( megast )
	ROM_REGION16_BE( 0x30000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos104")
	ROM_SYSTEM_BIOS( 0, "tos102", "TOS 1.02 (MEGA TOS)" ) // came in both 6 rom and 2 rom formats; 6 roms are 27512, and 2 roms are non-jedec RP231024 (TC531000 equivalent) 28-pin roms with A16 instead of /OE on pin 22
	ROMX_LOAD( "tos102.bin", 0x00000, 0x30000, CRC(d3c32283) SHA1(735793fdba07fe8d5295caa03484f6ef3de931f5), ROM_BIOS(0) )
	//For a C100167-001 revision B Mega ST motherboard, jumpered for 2 roms:
	//ROMX_LOAD( "c101629-001__(c)atari_1987__38__rp231024e__0564__8807_z07.rp231024.u9", 0x00000, 0x20000, NO_DUMP, ROM_BIOS(0)|ROM_SKIP(1) ) // in u9 HI-0 socket
	//ROMX_LOAD( "c101630-002__(c)atari_1987__38__rp231024e__0563__8809_z10.rp231024.u10", 0x00001, 0x20000, NO_DUMP, ROM_BIOS(0)|ROM_SKIP(1) ) // in u10 LO-0 socket
	//For a C100167-001 revision B Mega ST motherboard, jumpered for 6 roms:
	//ROMX_LOAD( "unknownmarkings_hi-0.27512.u9", 0x00000, 0x10000, NO_DUMP, ROM_BIOS(0)|ROM_SKIP(1) )
	//ROMX_LOAD( "unknownmarkings_lo-0.27512.u10", 0x00001, 0x10000, NO_DUMP, ROM_BIOS(0)|ROM_SKIP(1) )
	//ROMX_LOAD( "unknownmarkings_hi-1.27512.u6", 0x10000, 0x10000, NO_DUMP, ROM_BIOS(0)|ROM_SKIP(1) )
	//ROMX_LOAD( "unknownmarkings_lo-1.27512.u7", 0x10001, 0x10000, NO_DUMP, ROM_BIOS(0)|ROM_SKIP(1) )
	//ROMX_LOAD( "unknownmarkings_hi-2.27512.u3", 0x20000, 0x10000, NO_DUMP, ROM_BIOS(0)|ROM_SKIP(1) )
	//ROMX_LOAD( "unknownmarkings_lo-2.27512.u4", 0x20001, 0x10000, NO_DUMP, ROM_BIOS(0)|ROM_SKIP(1) )
	ROM_SYSTEM_BIOS( 1, "tos104", "TOS 1.04 (Rainbow TOS)" )  // came in both 6 rom and 2 rom formats; 6 roms are 27512, and 2 roms are non-jedec RP231024 (TC531000 equivalent) 28-pin roms with A16 instead of /OE on pin 22
	ROMX_LOAD( "tos104.bin", 0x00000, 0x30000, CRC(90f4fbff) SHA1(2487f330b0895e5d88d580d4ecb24061125e88ad), ROM_BIOS(1) )
	/*For a C100167-001 revision B Mega ST motherboard, jumpered for 2 roms:
	These came in an upgrade kit pouch with label:
	RAINBOW (TOS 1.4)
	CA400407 2CHIPSET
	(C) ATARI CORP.
	and an end-user pamphlet explaining the changes in 1.04 and a sheet giving installation instructions (jumper strapping to convert between 2/6 chip)
	*/
	//ROMX_LOAD( "rainbow_(tos_1.4)__c300683-002_h0__(c)atari_corp.rp231024e.u9", 0x00000, 0x20000, NO_DUMP, ROM_BIOS(1)|ROM_SKIP(1) )
	//ROMX_LOAD( "rainbow_(tos_1.4)__c300684-002_l0__(c)atari_corp.rp231024e.u10", 0x00001, 0x20000, NO_DUMP, ROM_BIOS(1)|ROM_SKIP(1) )
	//For a C100167-001 revision B Mega ST motherboard, jumpered for 6 roms:
	//ROMX_LOAD( "rainbow_(tos_1.4)__c300789-002_h0__(c)atari_corp.27512.u9", 0x00000, 0x10000, NO_DUMP, ROM_BIOS(1)|ROM_SKIP(1) )
	//ROMX_LOAD( "rainbow_(tos_1.4)__c300792-002_l0__(c)atari_corp.27512.u10", 0x00001, 0x10000, NO_DUMP, ROM_BIOS(1)|ROM_SKIP(1) )
	//ROMX_LOAD( "rainbow_(tos_1.4)__c300788-002_h1__(c)atari_corp.27512.u6", 0x10000, 0x10000, NO_DUMP, ROM_BIOS(1)|ROM_SKIP(1) )
	//ROMX_LOAD( "rainbow_(tos_1.4)__c300791-002_l1__(c)atari_corp.27512.u7", 0x10001, 0x10000, NO_DUMP, ROM_BIOS(1)|ROM_SKIP(1) )
	//ROMX_LOAD( "rainbow_(tos_1.4)__c300787-002_h2__(c)atari_corp.27512.u3", 0x20000, 0x10000, NO_DUMP, ROM_BIOS(1)|ROM_SKIP(1) )
	//ROMX_LOAD( "rainbow_(tos_1.4)__c300790-002_l2__(c)atari_corp.27512.u4", 0x20001, 0x10000, NO_DUMP, ROM_BIOS(1)|ROM_SKIP(1) )
ROM_END


//-------------------------------------------------
//  ROM( megast_uk )
//-------------------------------------------------

ROM_START( megast_uk )
	ROM_REGION16_BE( 0x30000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos104")
	ROM_SYSTEM_BIOS( 0, "tos102", "TOS 1.02 (MEGA TOS)" )
	ROMX_LOAD( "tos102uk.bin", 0x00000, 0x30000, CRC(3b5cd0c5) SHA1(87900a40a890fdf03bd08be6c60cc645855cbce5), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos104", "TOS 1.04 (Rainbow TOS)" )
	ROMX_LOAD( "tos104uk.bin", 0x00000, 0x30000, CRC(a50d1d43) SHA1(9526ef63b9cb1d2a7109e278547ae78a5c1db6c6), ROM_BIOS(1) )
ROM_END


//-------------------------------------------------
//  ROM( megast_de )
//-------------------------------------------------

ROM_START( megast_de )
	ROM_REGION16_BE( 0x30000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos104")
	ROM_SYSTEM_BIOS( 0, "tos102", "TOS 1.02 (MEGA TOS)" )
	ROMX_LOAD( "tos102de.bin", 0x00000, 0x30000, CRC(36a0058e) SHA1(cad5d2902e875d8bf0a14dc5b5b8080b30254148), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos104", "TOS 1.04 (Rainbow TOS)" )
	ROMX_LOAD( "tos104de.bin", 0x00000, 0x30000, CRC(62b82b42) SHA1(5313733f91b083c6265d93674cb9d0b7efd02da8), ROM_BIOS(1) )
ROM_END


//-------------------------------------------------
//  ROM( megast_fr )
//-------------------------------------------------

ROM_START( megast_fr )
	ROM_REGION16_BE( 0x30000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos104")
	ROM_SYSTEM_BIOS( 0, "tos102", "TOS 1.02 (MEGA TOS)" )
	ROMX_LOAD( "tos102fr.bin", 0x00000, 0x30000, CRC(8688fce6) SHA1(f5a79aac0a4e812ca77b6ac51d58d98726f331fe), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos104", "TOS 1.04 (Rainbow TOS)" )
	ROMX_LOAD( "tos104fr.bin", 0x00000, 0x30000, CRC(a305a404) SHA1(20dba880344b810cf63cec5066797c5a971db870), ROM_BIOS(1) )
ROM_END


//-------------------------------------------------
//  ROM( megast_se )
//-------------------------------------------------

ROM_START( megast_se )
	ROM_REGION16_BE( 0x30000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos104")
	ROM_SYSTEM_BIOS( 0, "tos102", "TOS 1.02 (MEGA TOS)" )
	ROMX_LOAD( "tos102se.bin", 0x00000, 0x30000, CRC(673fd0c2) SHA1(433de547e09576743ae9ffc43d43f2279782e127), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos104", "TOS 1.04 (Rainbow TOS)" )
	ROMX_LOAD( "tos104se.bin", 0x00000, 0x30000, CRC(80ecfdce) SHA1(b7ad34d5cdfbe86ea74ae79eca11dce421a7bbfd), ROM_BIOS(1) )
ROM_END


//-------------------------------------------------
//  ROM( megast_sg )
//-------------------------------------------------

ROM_START( megast_sg )
	ROM_REGION16_BE( 0x30000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos104")
	ROM_SYSTEM_BIOS( 0, "tos102", "TOS 1.02 (MEGA TOS)" )
	ROMX_LOAD( "tos102sg.bin", 0x00000, 0x30000, CRC(5fe16c66) SHA1(45acb2fc4b1b13bd806c751aebd66c8304fc79bc), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos104", "TOS 1.04 (Rainbow TOS)" )
	ROMX_LOAD( "tos104sg.bin", 0x00000, 0x30000, CRC(e58f0bdf) SHA1(aa40bf7203f02b2251b9e4850a1a73ff1c7da106), ROM_BIOS(1) )
ROM_END


//-------------------------------------------------
//  ROM( stacy )
//-------------------------------------------------

#if 0
ROM_START( stacy )
	ROM_REGION16_BE( 0x30000, M68000_TAG, 0 )
	ROM_SYSTEM_BIOS( 0, "tos104", "TOS 1.04 (Rainbow TOS)" )
	ROMX_LOAD( "tos104.bin", 0x00000, 0x30000, CRC(a50d1d43) SHA1(9526ef63b9cb1d2a7109e278547ae78a5c1db6c6), ROM_BIOS(0) )
ROM_END
#endif


//-------------------------------------------------
//  ROM( ste )
//-------------------------------------------------

ROM_START( ste )
	ROM_REGION16_BE( 0x40000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos206")
	ROM_SYSTEM_BIOS( 0, "tos106", "TOS 1.06 (STE TOS, Revision 1)" )
	ROMX_LOAD( "tos106.bin", 0x00000, 0x40000, CRC(a2e25337) SHA1(6a850810a92fdb1e64d005a06ea4079f51c97145), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos162", "TOS 1.62 (STE TOS, Revision 2)" )
	ROMX_LOAD( "tos162.bin", 0x00000, 0x40000, CRC(1c1a4eba) SHA1(42b875f542e5b728905d819c83c31a095a6a1904), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "tos206", "TOS 2.06 (ST/STE TOS)" )
	ROMX_LOAD( "tos206.bin", 0x00000, 0x40000, CRC(3f2f840f) SHA1(ee58768bdfc602c9b14942ce5481e97dd24e7c83), ROM_BIOS(2) )
ROM_END


//-------------------------------------------------
//  ROM( ste_uk )
//-------------------------------------------------

ROM_START( ste_uk )
	ROM_REGION16_BE( 0x40000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos206")
	ROM_SYSTEM_BIOS( 0, "tos106", "TOS 1.06 (STE TOS, Revision 1)" )
	ROMX_LOAD( "tos106uk.bin", 0x00000, 0x40000, CRC(d72fea29) SHA1(06f9ea322e74b682df0396acfaee8cb4d9c90cad), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos162", "TOS 1.62 (STE TOS, Revision 2)" )
	ROMX_LOAD( "tos162uk.bin", 0x00000, 0x40000, CRC(d1c6f2fa) SHA1(70db24a7c252392755849f78940a41bfaebace71), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "tos206", "TOS 2.06 (ST/STE TOS)" )
	ROMX_LOAD( "tos206uk.bin", 0x00000, 0x40000, CRC(08538e39) SHA1(2400ea95f547d6ea754a99d05d8530c03f8b28e3), ROM_BIOS(2) )
ROM_END


//-------------------------------------------------
//  ROM( ste_de )
//-------------------------------------------------

ROM_START( ste_de )
	ROM_REGION16_BE( 0x40000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos206")
	ROM_SYSTEM_BIOS( 0, "tos106", "TOS 1.06 (STE TOS, Revision 1)" )
	ROMX_LOAD( "tos106de.bin", 0x00000, 0x40000, CRC(7c67c5c9) SHA1(3b8cf5ffa41b252eb67f8824f94608fa4005d6dd), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos162", "TOS 1.62 (STE TOS, Revision 2)" )
	ROMX_LOAD( "tos162de.bin", 0x00000, 0x40000, CRC(2cdeb5e5) SHA1(10d9f61705048ee3dcbec67df741bed49b922149), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "tos206", "TOS 2.06 (ST/STE TOS)" )
	ROMX_LOAD( "tos206de.bin", 0x00000, 0x40000, CRC(143cd2ab) SHA1(d1da866560734289c4305f1028c36291d331d417), ROM_BIOS(2) )
ROM_END


//-------------------------------------------------
//  ROM( ste_es )
//-------------------------------------------------

ROM_START( ste_es )
	ROM_REGION16_BE( 0x40000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos106")
	ROM_SYSTEM_BIOS( 0, "tos106", "TOS 1.06 (STE TOS, Revision 1)" )
	ROMX_LOAD( "tos106es.bin", 0x00000, 0x40000, CRC(5cd2a540) SHA1(3a18f342c8288c0bc1879b7a209c73d5d57f7e81), ROM_BIOS(0) )
ROM_END


//-------------------------------------------------
//  ROM( ste_fr )
//-------------------------------------------------

ROM_START( ste_fr )
	ROM_REGION16_BE( 0x40000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos206")
	ROM_SYSTEM_BIOS( 0, "tos106", "TOS 1.06 (STE TOS, Revision 1)" )
	ROMX_LOAD( "tos106fr.bin", 0x00000, 0x40000, CRC(b6e58a46) SHA1(7d7e3cef435caa2fd7733a3fbc6930cb9ea7bcbc), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos162", "TOS 1.62 (STE TOS, Revision 2)" )
	ROMX_LOAD( "tos162fr.bin", 0x00000, 0x40000, CRC(0ab003be) SHA1(041e134da613f718fca8bd47cd7733076e8d7588), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "tos206", "TOS 2.06 (ST/STE TOS)" )
	ROMX_LOAD( "tos206fr.bin", 0x00000, 0x40000, CRC(e3a99ca7) SHA1(387da431e6e3dd2e0c4643207e67d06cf33618c3), ROM_BIOS(2) )
ROM_END


//-------------------------------------------------
//  ROM( ste_it )
//-------------------------------------------------

ROM_START( ste_it )
	ROM_REGION16_BE( 0x40000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos106")
	ROM_SYSTEM_BIOS( 0, "tos106", "TOS 1.06 (STE TOS, Revision 1)" )
	ROMX_LOAD( "tos106it.bin", 0x00000, 0x40000, CRC(d3a55216) SHA1(28dc74e5e0fa56b685bbe15f9837f52684fee9fd), ROM_BIOS(0) )
ROM_END


//-------------------------------------------------
//  ROM( ste_se )
//-------------------------------------------------

ROM_START( ste_se )
	ROM_REGION16_BE( 0x40000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos206")
	ROM_SYSTEM_BIOS( 0, "tos162", "TOS 1.62 (STE TOS, Revision 2)" )
	ROMX_LOAD( "tos162se.bin", 0x00000, 0x40000, CRC(90f124b1) SHA1(6e5454e861dbf4c46ce5020fc566c31202087b88), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos206", "TOS 2.06 (ST/STE TOS)" )
	ROMX_LOAD( "tos206se.bin", 0x00000, 0x40000, CRC(be61906d) SHA1(ebdf5a4cf08471cd315a91683fcb24e0f029d451), ROM_BIOS(1) )
ROM_END


//-------------------------------------------------
//  ROM( ste_sg )
//-------------------------------------------------

ROM_START( ste_sg )
	ROM_REGION16_BE( 0x40000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos206")
	ROM_SYSTEM_BIOS( 0, "tos206", "TOS 2.06 (ST/STE TOS)" )
	ROMX_LOAD( "tos206sg.bin", 0x00000, 0x40000, CRC(8c4fe57d) SHA1(c7a9ae3162f020dcac0c2a46cf0c033f91b98644), ROM_BIOS(0) )
ROM_END


//-------------------------------------------------
//  ROM( megaste )
//-------------------------------------------------

ROM_START( megaste )
	ROM_REGION16_BE( 0x40000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos206")
	ROM_SYSTEM_BIOS( 0, "tos205", "TOS 2.05 (Mega STE TOS)" )
	ROMX_LOAD( "atari mega ste 205 018 tms27c010.bin", 0x00000, 0x20000, CRC(befac3ab) SHA1(5b49f101f15a4d1c89cfd1d7ce3fec84a5ca36d0), ROM_BIOS(0) | ROM_SKIP(1) )
	ROMX_LOAD( "atari mega ste 205 019 tms27c010.bin", 0x00001, 0x20000, CRC(ea2a136d) SHA1(c3c259293de562d2a0fac4d41f95cf3d42ad6df4), ROM_BIOS(0) | ROM_SKIP(1) )
	ROM_SYSTEM_BIOS( 1, "tos206", "TOS 2.06 (ST/STE TOS)" )
	ROMX_LOAD( "tos206.bin", 0x00000, 0x40000, CRC(3f2f840f) SHA1(ee58768bdfc602c9b14942ce5481e97dd24e7c83), ROM_BIOS(1) )
	ROMX_LOAD( "tos206.bin", 0x00000, 0x40000, CRC(3f2f840f) SHA1(ee58768bdfc602c9b14942ce5481e97dd24e7c83), ROM_BIOS(1) )
ROM_END


//-------------------------------------------------
//  ROM( megaste_uk )
//-------------------------------------------------

ROM_START( megaste_uk )
	ROM_REGION16_BE( 0x40000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos206")
#if 0
	ROM_SYSTEM_BIOS( 0, "tos202", "TOS 2.02 (Mega STE TOS)" )
	ROMX_LOAD( "tos202uk.bin", 0x00000, 0x40000, NO_DUMP, ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos205", "TOS 2.05 (Mega STE TOS)" )
	ROMX_LOAD( "tos205uk.bin", 0x00000, 0x40000, NO_DUMP, ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "tos206", "TOS 2.06 (ST/STE TOS)" )
	ROMX_LOAD( "tos206uk.bin", 0x00000, 0x40000, CRC(08538e39) SHA1(2400ea95f547d6ea754a99d05d8530c03f8b28e3), ROM_BIOS(2) )
#else
	ROM_SYSTEM_BIOS( 0, "tos206", "TOS 2.06 (ST/STE TOS)" )
	ROMX_LOAD( "tos206uk.bin", 0x00000, 0x40000, CRC(08538e39) SHA1(2400ea95f547d6ea754a99d05d8530c03f8b28e3), ROM_BIOS(0) )
#endif
ROM_END


//-------------------------------------------------
//  ROM( megaste_fr )
//-------------------------------------------------

ROM_START( megaste_fr )
	ROM_REGION16_BE( 0x40000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos206")
	ROM_SYSTEM_BIOS( 0, "tos205", "TOS 2.05 (Mega STE TOS)" )
	ROMX_LOAD( "tos205fr.bin", 0x00000, 0x40000, CRC(27b83d2f) SHA1(83963b0feb0d119b2ca6f51e483e8c20e6ab79e1), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos206", "TOS 2.06 (ST/STE TOS)" )
	ROMX_LOAD( "tos206fr.bin", 0x00000, 0x40000, CRC(e3a99ca7) SHA1(387da431e6e3dd2e0c4643207e67d06cf33618c3), ROM_BIOS(1) )
ROM_END


//-------------------------------------------------
//  ROM( megaste_de )
//-------------------------------------------------

ROM_START( megaste_de )
	ROM_REGION16_BE( 0x40000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos206")
	ROM_SYSTEM_BIOS( 0, "tos205", "TOS 2.05 (Mega STE TOS)" )
	ROMX_LOAD( "tos205de.bin", 0x00000, 0x40000, CRC(518b24e6) SHA1(084e083422f8fd9ac7a2490f19b81809c52b91b4), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos206", "TOS 2.06 (ST/STE TOS)" )
	ROMX_LOAD( "tos206de.bin", 0x00000, 0x40000, CRC(143cd2ab) SHA1(d1da866560734289c4305f1028c36291d331d417), ROM_BIOS(1) )
ROM_END


//-------------------------------------------------
//  ROM( megaste_es )
//-------------------------------------------------

ROM_START( megaste_es )
	ROM_REGION16_BE( 0x40000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos205")
	ROM_SYSTEM_BIOS( 0, "tos205", "TOS 2.05 (Mega STE TOS)" )
	ROMX_LOAD( "tos205es.bin", 0x00000, 0x40000, CRC(2a426206) SHA1(317715ad8de718b5acc7e27ecf1eb833c2017c91), ROM_BIOS(0) )
ROM_END


//-------------------------------------------------
//  ROM( megaste_it )
//-------------------------------------------------

ROM_START( megaste_it )
	ROM_REGION16_BE( 0x40000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos205")
	ROM_SYSTEM_BIOS( 0, "tos205", "TOS 2.05 (Mega STE TOS)" )
	ROMX_LOAD( "tos205it.bin", 0x00000, 0x40000, CRC(b28bf5a1) SHA1(8e0581b442384af69345738849cf440d72f6e6ab), ROM_BIOS(0) )
ROM_END


//-------------------------------------------------
//  ROM( megaste_se )
//-------------------------------------------------

ROM_START( megaste_se )
	ROM_REGION16_BE( 0x40000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos206")
	ROM_SYSTEM_BIOS( 0, "tos205", "TOS 2.05 (Mega STE TOS)" )
	ROMX_LOAD( "tos205se.bin", 0x00000, 0x40000, CRC(6d49ccbe) SHA1(c065b1a9a2e42e5e373333e99be829028902acaa), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos206", "TOS 2.06 (ST/STE TOS)" )
	ROMX_LOAD( "tos206se.bin", 0x00000, 0x40000, CRC(be61906d) SHA1(ebdf5a4cf08471cd315a91683fcb24e0f029d451), ROM_BIOS(1) )
ROM_END


//-------------------------------------------------
//  ROM( stbook )
//-------------------------------------------------

#if 0
ROM_START( stbook )
	ROM_REGION16_BE( 0x40000, M68000_TAG, 0 )
	ROM_SYSTEM_BIOS( 0, "tos208", "TOS 2.08" )
	ROMX_LOAD( "tos208.bin", 0x00000, 0x40000, NO_DUMP, ROM_BIOS(0) )

	ROM_REGION( 0x1000, COP888_TAG, 0 )
	ROM_LOAD( "cop888c0.u703", 0x0000, 0x1000, NO_DUMP )
ROM_END
#endif


//-------------------------------------------------
//  ROM( stpad )
//-------------------------------------------------

#if 0
ROM_START( stpad )
	ROM_REGION16_BE( 0x40000, M68000_TAG, 0 )
	ROM_SYSTEM_BIOS( 0, "tos205", "TOS 2.05" )
	ROMX_LOAD( "tos205.bin", 0x00000, 0x40000, NO_DUMP, ROM_BIOS(0) )
ROM_END
#endif


//-------------------------------------------------
//  ROM( tt030 )
//-------------------------------------------------

ROM_START( tt030 )
	ROM_REGION32_BE( 0x80000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos306")
	ROM_SYSTEM_BIOS( 0, "tos306", "TOS 3.06 (TT TOS)" )
	ROMX_LOAD( "tos306.bin", 0x00000, 0x80000, CRC(e65adbd7) SHA1(b15948786278e1f2abc4effbb6d40786620acbe8), ROM_BIOS(0) )
ROM_END


//-------------------------------------------------
//  ROM( tt030_uk )
//-------------------------------------------------

ROM_START( tt030_uk )
	ROM_REGION32_BE( 0x80000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos306")
	ROM_SYSTEM_BIOS( 0, "tos306", "TOS 3.06 (TT TOS)" )
	ROMX_LOAD( "tos306uk.bin", 0x00000, 0x80000, CRC(75dda215) SHA1(6325bdfd83f1b4d3afddb2b470a19428ca79478b), ROM_BIOS(0) )
ROM_END


//-------------------------------------------------
//  ROM( tt030_de )
//-------------------------------------------------

ROM_START( tt030_de )
	ROM_REGION32_BE( 0x80000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos306")
	ROM_SYSTEM_BIOS( 0, "tos306", "TOS 3.06 (TT TOS)" )
	ROMX_LOAD( "tos306de.bin", 0x00000, 0x80000, CRC(4fcbb59d) SHA1(80af04499d1c3b8551fc4d72142ff02c2182e64a), ROM_BIOS(0) )
ROM_END


//-------------------------------------------------
//  ROM( tt030_fr )
//-------------------------------------------------

ROM_START( tt030_fr )
	ROM_REGION32_BE( 0x80000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos306")
	ROM_SYSTEM_BIOS( 0, "tos306", "TOS 3.06 (TT TOS)" )
	ROMX_LOAD( "tos306fr.bin", 0x00000, 0x80000, CRC(1945511c) SHA1(6bb19874e1e97dba17215d4f84b992c224a81b95), ROM_BIOS(0) )
ROM_END


//-------------------------------------------------
//  ROM( tt030_pl )
//-------------------------------------------------

ROM_START( tt030_pl )
	ROM_REGION32_BE( 0x80000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos306")
	ROM_SYSTEM_BIOS( 0, "tos306", "TOS 3.06 (TT TOS)" )
	ROMX_LOAD( "tos306pl.bin", 0x00000, 0x80000, CRC(4f2404bc) SHA1(d122b8ceb202b52754ff0d442b1c81f8b4de3436), ROM_BIOS(0) )
ROM_END


//-------------------------------------------------
//  ROM( fx1 )
//-------------------------------------------------

#if 0
ROM_START( fx1 )
	ROM_REGION16_BE( 0x40000, M68000_TAG, 0 )
	ROM_SYSTEM_BIOS( 0, "tos207", "TOS 2.07" )
	ROMX_LOAD( "tos207.bin", 0x00000, 0x40000, NO_DUMP, ROM_BIOS(0) )
ROM_END
#endif


//-------------------------------------------------
//  ROM( falcon30 )
//-------------------------------------------------

ROM_START( falcon30 )
	ROM_REGION32_BE( 0x80000, M68000_TAG, 0 )
	ROM_DEFAULT_BIOS("tos404")
	ROM_SYSTEM_BIOS( 0, "tos400", "TOS 4.00" )
	ROMX_LOAD( "tos400.bin", 0x00000, 0x80000, CRC(4fcf2471) SHA1(aed6e21226544a5b5ea4eaa0fad4d4c31767cbe9), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tos401", "TOS 4.01" )
	ROMX_LOAD( "tos401.bin", 0x00000, 0x80000, CRC(4a1f42af) SHA1(7157f6a8aff275cfbb5ea6aa8e788dda8a977e56), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "tos402", "TOS 4.02" )
	ROMX_LOAD( "tos402.bin", 0x00000, 0x80000, CRC(63f82f23) SHA1(75de588f6bbc630fa9c814f738195da23b972cc6), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "tos404", "TOS 4.04" )
	ROMX_LOAD( "tos404.bin", 0x00000, 0x80000, CRC(028b561d) SHA1(27dcdb31b0951af99023b2fb8c370d8447ba6ebc), ROM_BIOS(3) )
ROM_END


//-------------------------------------------------
//  ROM( falcon40 )
//-------------------------------------------------

ROM_START( falcon40 )
	ROM_REGION32_BE( 0x80000, M68000_TAG, 0 )
	ROM_SYSTEM_BIOS( 0, "tos492", "TOS 4.92" )
	ROMX_LOAD( "tos492.bin", 0x00000, 0x7d314, CRC(bc8e497f) SHA1(747a38042844a6b632dcd9a76d8525fccb5eb892), ROM_BIOS(0) )
ROM_END

} // anonymous namespace



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME        PARENT    COMPAT  MACHINE   INPUT   CLASS          INIT        COMPANY  FULLNAME                 FLAGS
COMP( 1985, st,         0,        0,      st,       st,     st_state,      empty_init, "Atari", "ST (USA)",              MACHINE_NOT_WORKING )
COMP( 1985, st_uk,      st,       0,      st,       st,     st_state,      empty_init, "Atari", "ST (UK)",               MACHINE_NOT_WORKING )
COMP( 1985, st_de,      st,       0,      st,       st,     st_state,      empty_init, "Atari", "ST (Germany)",          MACHINE_NOT_WORKING )
COMP( 1985, st_es,      st,       0,      st,       st,     st_state,      empty_init, "Atari", "ST (Spain)",            MACHINE_NOT_WORKING )
COMP( 1985, st_fr,      st,       0,      st,       st,     st_state,      empty_init, "Atari", "ST (France)",           MACHINE_NOT_WORKING )
COMP( 1985, st_nl,      st,       0,      st,       st,     st_state,      empty_init, "Atari", "ST (Netherlands)",      MACHINE_NOT_WORKING )
COMP( 1985, st_se,      st,       0,      st,       st,     st_state,      empty_init, "Atari", "ST (Sweden)",           MACHINE_NOT_WORKING )
COMP( 1985, st_sg,      st,       0,      st,       st,     st_state,      empty_init, "Atari", "ST (Switzerland)",      MACHINE_NOT_WORKING )
COMP( 1987, megast,     st,       0,      megast,   st,     megast_state,  empty_init, "Atari", "MEGA ST (USA)",         MACHINE_NOT_WORKING )
COMP( 1987, megast_uk,  st,       0,      megast,   st,     megast_state,  empty_init, "Atari", "MEGA ST (UK)",          MACHINE_NOT_WORKING )
COMP( 1987, megast_de,  st,       0,      megast,   st,     megast_state,  empty_init, "Atari", "MEGA ST (Germany)",     MACHINE_NOT_WORKING )
COMP( 1987, megast_fr,  st,       0,      megast,   st,     megast_state,  empty_init, "Atari", "MEGA ST (France)",      MACHINE_NOT_WORKING )
COMP( 1987, megast_se,  st,       0,      megast,   st,     megast_state,  empty_init, "Atari", "MEGA ST (Sweden)",      MACHINE_NOT_WORKING )
COMP( 1987, megast_sg,  st,       0,      megast,   st,     megast_state,  empty_init, "Atari", "MEGA ST (Switzerland)", MACHINE_NOT_WORKING )
COMP( 1989, ste,        0,        0,      ste,      ste,    ste_state,     empty_init, "Atari", "STe (USA)",             MACHINE_NOT_WORKING )
COMP( 1989, ste_uk,     ste,      0,      ste,      ste,    ste_state,     empty_init, "Atari", "STe (UK)",              MACHINE_NOT_WORKING )
COMP( 1989, ste_de,     ste,      0,      ste,      ste,    ste_state,     empty_init, "Atari", "STe (Germany)",         MACHINE_NOT_WORKING )
COMP( 1989, ste_es,     ste,      0,      ste,      ste,    ste_state,     empty_init, "Atari", "STe (Spain)",           MACHINE_NOT_WORKING )
COMP( 1989, ste_fr,     ste,      0,      ste,      ste,    ste_state,     empty_init, "Atari", "STe (France)",          MACHINE_NOT_WORKING )
COMP( 1989, ste_it,     ste,      0,      ste,      ste,    ste_state,     empty_init, "Atari", "STe (Italy)",           MACHINE_NOT_WORKING )
COMP( 1989, ste_se,     ste,      0,      ste,      ste,    ste_state,     empty_init, "Atari", "STe (Sweden)",          MACHINE_NOT_WORKING )
COMP( 1989, ste_sg,     ste,      0,      ste,      ste,    ste_state,     empty_init, "Atari", "STe (Switzerland)",     MACHINE_NOT_WORKING )
//COMP( 1990, stbook,     ste,      0,      stbook,   stbook, stbook_state,  empty_init, "Atari", "STBook",                MACHINE_NOT_WORKING )
COMP( 1990, tt030,      0,        0,      tt030,    tt030,  ste_state,     empty_init, "Atari", "TT030 (USA)",           MACHINE_NOT_WORKING )
COMP( 1990, tt030_uk,   tt030,    0,      tt030,    tt030,  ste_state,     empty_init, "Atari", "TT030 (UK)",            MACHINE_NOT_WORKING )
COMP( 1990, tt030_de,   tt030,    0,      tt030,    tt030,  ste_state,     empty_init, "Atari", "TT030 (Germany)",       MACHINE_NOT_WORKING )
COMP( 1990, tt030_fr,   tt030,    0,      tt030,    tt030,  ste_state,     empty_init, "Atari", "TT030 (France)",        MACHINE_NOT_WORKING )
COMP( 1990, tt030_pl,   tt030,    0,      tt030,    tt030,  ste_state,     empty_init, "Atari", "TT030 (Poland)",        MACHINE_NOT_WORKING )
COMP( 1991, megaste,    ste,      0,      megaste,  st,     megaste_state, empty_init, "Atari", "MEGA STe (USA)",        MACHINE_NOT_WORKING )
COMP( 1991, megaste_uk, ste,      0,      megaste,  st,     megaste_state, empty_init, "Atari", "MEGA STe (UK)",         MACHINE_NOT_WORKING )
COMP( 1991, megaste_de, ste,      0,      megaste,  st,     megaste_state, empty_init, "Atari", "MEGA STe (Germany)",    MACHINE_NOT_WORKING )
COMP( 1991, megaste_es, ste,      0,      megaste,  st,     megaste_state, empty_init, "Atari", "MEGA STe (Spain)",      MACHINE_NOT_WORKING )
COMP( 1991, megaste_fr, ste,      0,      megaste,  st,     megaste_state, empty_init, "Atari", "MEGA STe (France)",     MACHINE_NOT_WORKING )
COMP( 1991, megaste_it, ste,      0,      megaste,  st,     megaste_state, empty_init, "Atari", "MEGA STe (Italy)",      MACHINE_NOT_WORKING )
COMP( 1991, megaste_se, ste,      0,      megaste,  st,     megaste_state, empty_init, "Atari", "MEGA STe (Sweden)",     MACHINE_NOT_WORKING )
COMP( 1992, falcon30,   0,        0,      falcon,   falcon, ste_state,     empty_init, "Atari", "Falcon030",             MACHINE_NOT_WORKING )
COMP( 1992, falcon40,   falcon30, 0,      falcon40, falcon, ste_state,     empty_init, "Atari", "Falcon040 (prototype)", MACHINE_NOT_WORKING )
//COMP( 1989, stacy,      st,       0,      stacy,    stacy,  st_state,      empty_init, "Atari", "Stacy",                 MACHINE_NOT_WORKING )
//COMP( 1991, stpad,      ste,      0,      stpad,    stpad,  st_state,      empty_init, "Atari", "STPad (prototype)",     MACHINE_NOT_WORKING )
//COMP( 1992, fx1,        0,        0,      falcon,   falcon, ste_state,     empty_init, "Atari", "FX-1 (prototype)",      MACHINE_NOT_WORKING )



atm.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/*******************************************************************************************

MicroART ATM (clone of Spectrum)

NOTES:
    Current implementation based on ATM Turbo 2+. If anybody wants to validate ATM1, existing
    code must be moved to atmtb2_state not modified.

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

#include "emu.h"
#include "atm.h"

#include "bus/spectrum/ay/slot.h"
#include "bus/ata/atapicdr.h"
#include "bus/ata/hdd.h"

#define LOG_MEM   (1U << 1)
#define LOG_VIDEO (1U << 2)
#define LOG_WARN  (1U << 3)

#define VERBOSE ( /*LOG_GENERAL | LOG_MEM | LOG_VIDEO |*/ LOG_WARN )
#include "logmacro.h"

#define LOGMEM(...)   LOGMASKED(LOG_MEM,   __VA_ARGS__)
#define LOGVIDEO(...) LOGMASKED(LOG_VIDEO, __VA_ARGS__)
#define LOGWARN(...)  LOGMASKED(LOG_WARN,  __VA_ARGS__)

void atm_state::atm_update_cpu()
{
	m_maincpu->set_clock(X1_128_SINCLAIR / 10 * (1 << BIT(m_port_77_data, 3))); // 0 - 3.5MHz, 1 - 7MHz
}

void atm_state::atm_update_io()
{
	if (is_dos_active())
		m_io_view.select(0);
	else
		m_io_view.disable();
}

void atm_state::atm_update_memory()
{
	using views_link = std::reference_wrapper<memory_view>;
	views_link views[] = { m_bank_view0, m_bank_view1, m_bank_view2, m_bank_view3 };
	LOGMEM("PEN%d.%X ", BIT(m_port_7ffd_data, 4), (m_port_7ffd_data & 0x07));
	for (auto bank = 0; bank < 4 ; bank++)
	{
		u16 page = atm_update_memory_get_page(bank);
		const char* is_dos7ffd = page & PEN_DOS7FFD_MASK ? "+" : " ";
		if (page & PEN_RAMNROM_MASK)
		{
			if (page & PEN_DOS7FFD_MASK)
				page = merge_ram_with_7ffd(page);
			LOGMEM("RA%s%X ", is_dos7ffd, page & ram_pages_mask);
			m_bank_ram[bank]->set_entry(page & ram_pages_mask);
			if (page & PEN_WRDISBL_MASK)
				views[bank].get().select(1);
			else
				views[bank].get().disable();
		}
		else
		{
			if (page & PEN_DOS7FFD_MASK)
				page = (page & ~1) | is_dos_active();
			LOGMEM("RO%s%X ", is_dos7ffd, page & rom_pages_mask);
			m_bank_rom[bank]->set_entry(page & rom_pages_mask);
			views[bank].get().select(0);
		}
	}
	LOGMEM("\n");
}

u16 atm_state::atm_update_memory_get_page(u8 bank)
{
	return m_pen ? pen_page(bank) : (u16) (~PEN_RAMNROM_MASK & ~PEN_DOS7FFD_MASK);
}

void atm_state::atm_ula_w(offs_t offset, u8 data)
{
	m_br3 = ~offset & 0x08;
	spectrum_128_state::spectrum_ula_w(offset, data);
}

void atm_state::atm_port_ff_w(offs_t offset, u8 data)
{
	if (m_pen2)
	{
		m_beta_drive_selected = data;
		m_beta->param_w(data);
	}
	else
	{
		// Must read current ULA value (which is doesn't work now) from the BUS.
		// Good enough as non-border case is too complicated and possibly no software uses it.
		u8 pen = 0x0f & get_border_color(m_screen->hpos(), m_screen->vpos());
		m_palette_data[pen] = data;
		m_palette->set_pen_color(pen,
			(BIT(~data, 1) * 0xaa) | (BIT(~data, 6) * 0x55),
			(BIT(~data, 4) * 0xaa) | (BIT(~data, 7) * 0x55),
			(BIT(~data, 0) * 0xaa) | (BIT(~data, 5) * 0x55));
	}
}

void atm_state::atm_port_7ffd_w(offs_t offset, u8 data)
{
	if (is_port_7ffd_locked())
		return;

	m_port_7ffd_data = data;
	atm_update_memory();

	m_screen->update_now();
	m_screen_location = m_ram->pointer() + ((BIT(m_port_7ffd_data, 3) ? 7 : 5) << 14);
}

void atm_state::atm_port_77_w(offs_t offset, u8 data)
{
	m_port_77_data = data;

	m_pen = BIT(offset, 8);
	m_cpm_n = BIT(offset, 9);
	atm_update_io();

	m_pen2 = BIT(offset, 14);
	LOGMASKED(LOG_VIDEO | LOG_MEM, "PEN %s, CPM %s, PEN2 %s\n", m_pen ? "on" : "off", m_cpm_n ? "off" : "on", m_pen2 ? "off" : "on");
	atm_update_memory();

	atm_update_cpu();

	int rg = data & 0x07;
	if ( m_rg ^ rg )
	{
		m_rg = rg;
		atm_update_video_mode();
	}
}

void atm_state::atm_port_f7_w(offs_t offset, u8 data)
{
	u8 bank = offset >> 14;
	u16 page = (u16(data & 0xc0) << 8) | u8(~data & 0x3f);

	LOGMEM("ATM%s=%X %s%d%s%02X\n", BIT(m_port_7ffd_data, 4), data, (page & PEN_RAMNROM_MASK) ? "RAM" : "ROM", bank, (page & PEN_DOS7FFD_MASK) ? "+" : " ", page & 0x3f);
	pen_page(bank) = page;
	atm_update_memory();
	atm_update_io();
}

INTERRUPT_GEN_MEMBER(atm_state::atm_interrupt)
{
	// 14395=64*224+59 z80(3.5Hz) clocks between INT and screen paper begins. Screen clock is 7Hz.
	m_irq_on_timer->adjust(m_screen->time_until_pos(80 - 64, 80) - m_screen->clocks_to_attotime(118));
}

rectangle atm_state::get_screen_area()
{
	switch (m_rg)
	{
		case 0b111:
		case 0b110: // 80x25txt
		case 0b010: // 640x200
			return rectangle { 80, 80 + 639, 80, 80 + 199 };
			break;
		case 0b000: // 320x200
			return rectangle { 80, 80 + 319, 80, 80 + 199 };
			break;
		case 0b011: // 256x192zx
		default:
			return rectangle { 80, 80 + 255, 80, 80 + 191 };
			break;
	}
}

u8 atm_state::get_border_color(u16 hpos, u16 vpos)
{
	return m_br3 | (m_port_fe_data & 0x07);
}

void atm_state::atm_update_video_mode()
{
	bool zx_scale = m_rg & 1;
	bool double_width = BIT(m_rg, 1) && !zx_scale;
	u8 border_x = (40 - (32 * !zx_scale)) << (double_width ? 1 : 0);
	u8 border_y = (40 - (4 * !zx_scale));
	rectangle scr = get_screen_area();
	m_screen->configure(448 << (double_width ? 1 : 0), m_screen->height(), {scr.left() - border_x, scr.right() + border_x, scr.top() - border_y, scr.bottom() + border_y}, m_screen->frame_period().as_attoseconds());
	LOGVIDEO("Video mode: %d\n", m_rg);

	//spectrum_palette(m_palette);
}

void atm_state::spectrum_update_screen(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	switch (m_rg)
	{
		case 0b110: // txt
			atm_update_screen_tx(screen, bitmap, cliprect);
			break;
		case 0b010: // 640x200
			atm_update_screen_hi(screen, bitmap, cliprect);
			break;
		case 0b000: // 320x200
			atm_update_screen_lo(screen, bitmap, cliprect);
			break;
		case 0b011: // 256x192
		default:    // + unsupported
			spectrum_128_state::spectrum_update_screen(screen, bitmap, cliprect);
			break;
	}
}

void atm_state::atm_update_screen_lo(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (u16 vpos = cliprect.top(); vpos <= cliprect.bottom(); vpos++)
	{
		u16 y = vpos - get_screen_area().top();
		for (u16 hpos = cliprect.left(); hpos <= cliprect.right(); hpos++)
		{
			u16 x = hpos - get_screen_area().left();
			u8 *scr = m_screen_location;
			if (!BIT(x, 1)) scr -= 4 << 14;
			if (BIT(x, 2)) scr += 0x2000;
			scr += (x >> 3) + y * 40;
			u8 pix_pair = *scr;
			pix_pair = x & 1
				? (((pix_pair & 0x80) >> 1) | (pix_pair & 0x38)) >> 3
				: ((pix_pair & 0x40) >> 3) | (pix_pair & 0x07);
			bitmap.pix(vpos, hpos) = pix_pair;
		}
	}
}

void atm_state::atm_update_screen_hi(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (u16 vpos = cliprect.top(); vpos <= cliprect.bottom(); vpos++)
	{
		u16 y = vpos - get_screen_area().top();
		for (u16 hpos = cliprect.left() & 0xfff8; hpos <= cliprect.right();)
		{
			u16 x = hpos - get_screen_area().left();
			u8 *scr = m_screen_location + (x >> 4) + y * 40;
			if (BIT(x, 3)) scr += 0x2000;

			u8 attr = *(scr - (4 << 14));
			u8 fg = ((attr & 0x40) >> 3) | (attr & 0x07);
			u8 bg = (((attr & 0x80) >> 1) | (attr & 0x38)) >> 3;

			u8 chunk = *scr;
			for (u8 i = 0x80; i; i >>= 1)
			{
				bitmap.pix(vpos, hpos++) = (chunk & i) ? fg : bg;
			}
		}
	}
}

void atm_state::atm_update_screen_tx(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (u16 vpos = cliprect.top(); vpos <= cliprect.bottom(); vpos++)
	{
		u16 y = vpos - get_screen_area().top();
		for (u16 hpos = cliprect.left() & 0xfff8; hpos <= cliprect.right();)
		{
			u16 x = hpos - get_screen_area().left();
			u8 *symb_location = m_screen_location + 0x1c0 + (x >> 4) + ((y >> 3) * 64);
			u8 *attr_location = symb_location - (4 << 14) + BIT(x, 3);
			if (BIT(x, 3))
				symb_location += 0x2000;
			else
				attr_location += 0x2000;

			u8 attr = *attr_location;
			u8 fg = ((attr & 0x40) >> 3) | (attr & 0x07);
			u8 bg = (((attr & 0x80) >> 1) | (attr & 0x38)) >> 3;

			u8 chunk = *(m_char_location + (*symb_location << 3) + (y & 0x07));
			for (u8 i = 0x80; i; i >>= 1)
			{
				bitmap.pix(vpos, hpos++) = (chunk & i) ? fg : bg;
			}
		}
	}
}

u8 atm_state::beta_neutral_r(offs_t offset)
{
	return m_program.read_byte(offset);
}

u8 atm_state::beta_enable_r(offs_t offset)
{
	if (!machine().side_effects_disabled() && !m_beta->is_active())
	{
		bool is_rom0 = !(atm_update_memory_get_page(0) & PEN_RAMNROM_MASK);
		if (is_rom0 || !m_cpm_n)
		{
			m_beta->enable();
			atm_update_memory();
			atm_update_io();
		}
	}
	return beta_neutral_r(offset + 0x3d00);
}

u8 atm_state::beta_disable_r(offs_t offset)
{
	if (!machine().side_effects_disabled() && m_beta->is_active())
	{
		if (m_cpm_n)
		{
			m_beta->disable();
			atm_update_memory();
			atm_update_io();
		}
	}
	return beta_neutral_r(offset + 0x4000);
}

u8 atm_state::ata_r(offs_t offset)
{
	u8 ata_offset = BIT(offset, 5, 3);
	u16 data = m_ata->cs0_r(ata_offset);

	if (!machine().side_effects_disabled() && !ata_offset)
		m_ata_data_latch = data >> 8;

	return data & 0xff;
}

void atm_state::ata_w(offs_t offset, u8 data)
{
	u8 ata_offset = BIT(offset, 5, 3);
	u16 ata_data = data;
	if (!ata_offset)
		ata_data |= m_ata_data_latch << 8;

	m_ata->cs0_w(ata_offset, ata_data);
}

template <u8 Bank> void atm_state::atm_ram_w(offs_t offset, u8 data)
{
	if (m_rg == 0b011 && (m_bank_ram[Bank]->entry() == (BIT(m_port_7ffd_data, 3) ? 7 : 5)) && offset < 0x1b00)
		m_screen->update_now();

	((u8*)m_bank_ram[Bank]->base())[offset] = data;
}

void atm_state::atm_mem(address_map &map)
{
	map(0x0000, 0x3fff).bankr(m_bank_ram[0]).w(FUNC(atm_state::atm_ram_w<0>));
	map(0x0000, 0x3fff).view(m_bank_view0);
	m_bank_view0[0](0x0000, 0x3fff).bankr(m_bank_rom[0]).nopw();
	m_bank_view0[1](0x0000, 0x3fff).nopw(); // RO RAM

	map(0x4000, 0x7fff).bankr(m_bank_ram[1]).w(FUNC(atm_state::atm_ram_w<1>));
	map(0x4000, 0x7fff).view(m_bank_view1);
	m_bank_view1[0](0x4000, 0x7fff).bankr(m_bank_rom[1]).nopw();
	m_bank_view1[1](0x4000, 0x7fff).nopw();

	map(0x8000, 0xbfff).bankr(m_bank_ram[2]).w(FUNC(atm_state::atm_ram_w<2>));
	map(0x8000, 0xbfff).view(m_bank_view2);
	m_bank_view2[0](0x8000, 0xbfff).bankr(m_bank_rom[2]).nopw();
	m_bank_view2[1](0x8000, 0xbfff).nopw();

	map(0xc000, 0xffff).bankr(m_bank_ram[3]).w(FUNC(atm_state::atm_ram_w<3>));
	map(0xc000, 0xffff).view(m_bank_view3);
	m_bank_view3[0](0xc000, 0xffff).bankr(m_bank_rom[3]).nopw();
	m_bank_view3[1](0xc000, 0xffff).nopw();
}

void atm_state::atm_io(address_map &map)
{
	map.unmap_value_high();

	// PORTS: Always
	map(0x00f6, 0x00f6).select(0xff08).rw(FUNC(atm_state::spectrum_ula_r), FUNC(atm_state::atm_ula_w));
	map(0x00fb, 0x00fb).mirror(0xff00).w("cent_data_out", FUNC(output_latch_device::write));
	map(0x00fd, 0x00fd).mirror(0xff00).w(FUNC(atm_state::atm_port_7ffd_w));

	map(0xfadf, 0xfadf).mirror(0x0500).nopr(); // TODO 0xfadf, 0xfbdf, 0xffdf Kempston Mouse
	map(0x8000, 0x8000).mirror(0x3ffd).w("ay_slot", FUNC(ay_slot_device::data_w));
	map(0xc000, 0xc000).mirror(0x3ffd).rw("ay_slot", FUNC(ay_slot_device::data_r), FUNC(ay_slot_device::address_w));

	// PORTS: Shadow
	map(0x0000, 0xffff).view(m_io_view);
	m_io_view[0](0x001f, 0x001f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::status_r), FUNC(beta_disk_device::command_w));
	m_io_view[0](0x003f, 0x003f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::track_r), FUNC(beta_disk_device::track_w));
	m_io_view[0](0x005f, 0x005f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::sector_r), FUNC(beta_disk_device::sector_w));
	m_io_view[0](0x007f, 0x007f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::data_r), FUNC(beta_disk_device::data_w));
	m_io_view[0](0x00ff, 0x00ff).mirror(0xff00).r(m_beta, FUNC(beta_disk_device::state_r)).w(FUNC(atm_state::atm_port_ff_w));

	m_io_view[0](0x0077, 0x0077).select(0xff00).w(FUNC(atm_state::atm_port_77_w));
	m_io_view[0](0x00f7, 0x00f7).select(0xff00).w(FUNC(atm_state::atm_port_f7_w));

	// A: .... .... nnn0 1111
	m_io_view[0](0x000f, 0x000f).select(0xffe0).rw(FUNC(atm_state::ata_r), FUNC(atm_state::ata_w));
	// A: .... ...1 0000 1111
	m_io_view[0](0x010f, 0x010f).mirror(0xfe00).lrw8(NAME([this](offs_t offset) { return m_ata_data_latch; })
		, NAME([this](offs_t offset, u8 data) { m_ata_data_latch = data; }));
}

void atm_state::atm_switch(address_map &map)
{
	map(0x0000, 0x3fff).r(FUNC(atm_state::beta_neutral_r)); // Overlap with previous because we want real addresses on the 3e00-3fff range
	map(0x3d00, 0x3dff).r(FUNC(atm_state::beta_enable_r));
	map(0x4000, 0xffff).r(FUNC(atm_state::beta_disable_r));
}


void atm_state::machine_start()
{
	spectrum_128_state::machine_start();

	save_item(NAME(m_port_77_data));
	save_item(NAME(m_pen));
	save_item(NAME(m_cpm_n));
	save_item(NAME(m_pages_map));
	save_item(NAME(m_pen2));
	save_item(NAME(m_rg));
	save_item(NAME(m_br3));
	save_item(NAME(m_beta_drive_selected));

	// reconfigure ROMs
	memory_region *rom = memregion("maincpu");
	rom_pages_mask = (rom->bytes() - 0x10001) / 0x4000;
	for (auto i = 0; i < 4; i++)
		m_bank_rom[i]->configure_entries(0, rom_pages_mask + 1, rom->base() + 0x10000, 0x4000); // todo dyn

	ram_pages_mask = (m_ram->size() - 1) / 0x4000;
	m_bank_ram[0]->configure_entries(0, ram_pages_mask + 1, m_ram->pointer(), 0x4000);
}

void atm_state::machine_reset()
{
	m_beta->enable();
	m_beta_drive_selected = 0;

	m_port_fe_data = -1;
	m_port_7ffd_data = 0;
	m_port_1ffd_data = -1;
	m_port_77_data = 0;

	m_br3 = 0;
	atm_port_77_w(0x4000, 3); // m_port_77_data: CPM=0(on), PEN=0(off), PEN2=1(off); vmode: zx
}

void atm_state::video_start()
{
	spectrum_state::video_start();
	m_screen_location = m_ram->pointer() + (5 << 14);
	m_char_location = m_char_rom;
	subdevice<gfxdecode_device>("gfxdecode")->gfx(0)->set_source(m_char_location);
}

/* F4 Character Displayer */
static const gfx_layout spectrum_charlayout =
{
	8, 8,            // 8 x 8 characters
	96,              // 96 characters
	1,               // 1 bits per pixel
	{ 0 },           // no bitplanes
	{ STEP8(0, 1) }, // x offsets
	{ STEP8(0, 8) }, // y offsets
	8 * 8            // every char takes 8 bytes
};

static const gfx_layout atm_charlayout =
{
	8, 8,            // 8 x 8 characters
	256,             // 256 characters
	1,               // 1 bits per pixel
	{ 0 },           // no bitplanes
	{ STEP8(0, 1) }, // x offsets
	{ STEP8(0, 8) }, // y offsets
	8 * 8            // every char takes 8 bytes
};

static GFXDECODE_START( gfx_atm )
	GFXDECODE_ENTRY( "maincpu", 0, atm_charlayout, 7, 1 ) // charrom
	GFXDECODE_ENTRY( "maincpu", 0x1fd00, spectrum_charlayout, 7, 1 )
GFXDECODE_END

static GFXDECODE_START( gfx_atmtb2 )
	GFXDECODE_ENTRY( "maincpu", 0, atm_charlayout, 7, 1 ) // charrom
	GFXDECODE_ENTRY( "maincpu", 0x13d00, spectrum_charlayout, 7, 1 )
GFXDECODE_END

static void atm_ata_devices(device_slot_interface &device)
{
	device.option_add("hdd", IDE_HARDDISK);
	device.option_add("cdrom", ATAPI_CDROM);
}

void atm_state::atm(machine_config &config)
{
	spectrum_128(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &atm_state::atm_mem);
	m_maincpu->set_addrmap(AS_IO, &atm_state::atm_io);
	m_maincpu->set_addrmap(AS_OPCODES, &atm_state::atm_switch);
	m_maincpu->set_vblank_int("screen", FUNC(atm_state::atm_interrupt));
	m_maincpu->refresh_cb().remove();
	m_maincpu->nomreq_cb().remove();

	m_screen->set_raw(X1_128_SINCLAIR / 5, 448, 312, {get_screen_area().left() - 40, get_screen_area().right() + 40, get_screen_area().top() - 40, get_screen_area().bottom() + 40});
	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_atm);
	SPECTRUM_ULA_UNCONTENDED(config.replace(), m_ula);

	BETA_DISK(config, m_beta, 0);
	ATA_INTERFACE(config, m_ata).options(atm_ata_devices, nullptr, nullptr, false);

	CENTRONICS(config, m_centronics, centronics_devices, "covox");
	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	config.device_remove("exp");
}


/******************************************************************************
 * ATM Turbo 2
 * ***************************************************************************/
void atm_state::atmtb2(machine_config &config)
{
	atm(config);

	m_ram->set_default_size("512K").set_extra_options("128K,256K");

	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_atmtb2);
}


/******************************************************************************
 * ATM Turbo 2+
 * ***************************************************************************/
void atm_state::atmtb2plus(machine_config &config)
{
	atmtb2(config);

	m_ram->set_default_size("1M").set_extra_options("128K,256K,512K");
}


/***************************************************************************
 * Game driver(s)
 * ************************************************************************/
ROM_START( atm )
	ROM_REGION(0x020000, "maincpu", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "v1", "v.1.03")
	ROMX_LOAD( "atm103.rom", 0x010000, 0x10000, CRC(4912e249) SHA1(a4adff05bb215dd126c47201b36956115b8fed76), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v2", "v.1.03rs")
	ROMX_LOAD( "atm103rs.rom", 0x010000, 0x10000, CRC(cdec1dfb) SHA1(08190807c6b110cb2e657d8e7d0ad18668915375), ROM_BIOS(1))
ROM_END

ROM_START( atmtb2 )
	ROM_REGION(0x020000, "maincpu", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "v1.06", "BIOS v1.06") // joined dump
	ROMX_LOAD( "atm106.rom", 0x010000, 0x10000, CRC(75350b37) SHA1(2afc9994f026645c74b6c4b35bcee2e0bc0d6edc), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v1.06a", "BIOS v1.06 (split)")
	ROMX_LOAD( "atm106-1.rom", 0x010000, 0x4000, CRC(658c98f1) SHA1(1ec694795aa6cac10147e58f38a9db0bdf7ed89b), ROM_BIOS(1))
	ROMX_LOAD( "atm106-2.rom", 0x014000, 0x4000, CRC(8fe367f9) SHA1(56de8fd39061663b9c315b74fd3c31acddae279c), ROM_BIOS(1))
	ROMX_LOAD( "atm106-3.rom", 0x018000, 0x4000, CRC(124ad9e0) SHA1(d07fcdeca892ee80494d286ea9ea5bf3928a1aca), ROM_BIOS(1))
	ROMX_LOAD( "atm106-4.rom", 0x01c000, 0x4000, CRC(f352f2ab) SHA1(6045500ab01be708cef62327e9821b4a358a4673), ROM_BIOS(1))

	ROM_REGION(0x01000, "keyboard", ROMREGION_ERASEFF)
	ROM_LOAD( "rf2ve3.rom",  0x0000, 0x0580, CRC(35e0f9ec) SHA1(adcf14758fab8472cfa0167af7e8326c66416416)) // XT Keyboard
	ROM_LOAD( "rfat710.rom", 0x0600, 0x0680, CRC(03734365) SHA1(6cb6311727fad9bc4ccb18919c3c39b37529b8e6)) // AT Keyboard

	ROM_REGION(0x08000, "charrom", ROMREGION_ERASEFF) // Char gen rom
	ROM_LOAD( "sgen.rom", 0x0000, 0x0800, CRC(1f4387d6) SHA1(93b3774dc8a486643a1bdd48c606b0c84fa0e22b))
ROM_END

ROM_START( atmtb2plus )
	ROM_REGION(0x030000, "maincpu", ROMREGION_ERASEFF)
	ROM_DEFAULT_BIOS("v1.37")
	ROM_SYSTEM_BIOS(0, "v1.07.12", "BIOS v1.07.12, CP/M v2.2, TR-DOS v5.03") // joined dump
	ROMX_LOAD( "atmtb2.rom",   0x020000, 0x10000,CRC(05218c26) SHA1(71ed9864e7aa85131de97cf1e53dc152e7c79488), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v1.07.12a", "BIOS v1.07.12, CP/M v2.2, TR-DOS v5.03 (split)")
	ROMX_LOAD( "atmtb2-1.rom", 0x020000, 0x4000, CRC(658c98f1) SHA1(1ec694795aa6cac10147e58f38a9db0bdf7ed89b), ROM_BIOS(1))
	ROMX_LOAD( "atmtb2-2.rom", 0x024000, 0x4000, CRC(bc3f6b2b) SHA1(afa9df63857141fef270e2c97e12d2edc60cf919), ROM_BIOS(1))
	ROMX_LOAD( "atmtb2-3.rom", 0x028000, 0x4000, CRC(124ad9e0) SHA1(d07fcdeca892ee80494d286ea9ea5bf3928a1aca), ROM_BIOS(1))
	ROMX_LOAD( "atmtb2-4.rom", 0x02c000, 0x4000, CRC(5869d8c4) SHA1(c3e198138f528ac4a8dff3c76cd289fd4713abff), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v1.07.13", "BIOS v1.07.13, CP/M v2.2, TR-DOS v5.03")
	ROMX_LOAD( "atmtb213.rom", 0x020000, 0x10000, CRC(34a91d53) SHA1(8f0af0f3c0ff1644535f20545c73d01576d6e52f), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v1.37", "Dual eXtra v1.37XT: BIOS v1.07.15, CP/M v2.2, TR-DOS v5.04R")
	ROMX_LOAD( "atmtb2x37xt.rom", 0x010000, 0x20000, CRC(e5ef44d9) SHA1(3fbb9ace7cb031e7365c19e4f8b67ed366e24064), ROM_BIOS(3))

	ROM_REGION(0x01000, "keyboard", ROMREGION_ERASEFF)
	ROM_LOAD( "rf2ve3.rom",  0x0000, 0x0580, CRC(35e0f9ec) SHA1(adcf14758fab8472cfa0167af7e8326c66416416)) // XT Keyboard
	ROM_LOAD( "rfat710.rom", 0x0600, 0x0680, CRC(03734365) SHA1(6cb6311727fad9bc4ccb18919c3c39b37529b8e6)) // AT Keyboard

	ROM_REGION(0x08000, "charrom", ROMREGION_ERASEFF) // Char gen rom
	ROM_LOAD( "sgen.rom", 0x0000, 0x0800, CRC(1f4387d6) SHA1(93b3774dc8a486643a1bdd48c606b0c84fa0e22b))
ROM_END

/*    YEAR  NAME        PARENT   COMPAT MACHINE     INPUT        CLASS          INIT        COMPANY     FULLNAME                  FLAGS */
COMP( 1991, atm,        spec128, 0,     atm,        spec_plus2a, atm_state,     empty_init, "MicroART", "ATM-Turbo (ATM-CP)",     MACHINE_NOT_WORKING)
COMP( 1992, atmtb2,     spec128, 0,     atmtb2,     spec_plus2a, atm_state,     empty_init, "MicroART", "ATM-Turbo 2",            MACHINE_SUPPORTS_SAVE)
COMP( 1993, atmtb2plus, spec128, 0,     atmtb2plus, spec_plus2a, atm_state,     empty_init, "MicroART", "ATM-Turbo 2+",           MACHINE_SUPPORTS_SAVE)



atom.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nathan Woods,Nigel Barnes
/***************************************************************************

Acorn Atom:
  http://chrisacorns.computinghistory.org.uk/Computers/Atom.html
  http://chrisacorns.computinghistory.org.uk/Computers/Busicomputers_Prophet2.html
  http://chrisacorns.computinghistory.org.uk/Computers/Busicomputers_Prophet3.html

Memory map.

CPU: 6502
        0000-00ff Zero page
        0100-01ff Stack
        0200-1fff RAM (expansion)
        0a00-0a04 FDC 8271
        2000-21ff RAM (dos catalogue buffer)
        2200-27ff RAM (dos seq file buffer)
        2800-28ff RAM (float buffer)
        2900-7fff RAM (text RAM)
        8000-97ff VDG 6847
        9800-9fff RAM (expansion)
        a000-afff ROM (extension)
        b000-b003 PPIA 8255
        b003-b7ff NOP
        b800-bbff VIA 6522
        bc00-bfdf NOP
        bfe0-bfe2 MOUSE - extension??
        bfe3-bfff NOP
        c000-cfff ROM (basic)
        d000-dfff ROM (float)
        e000-efff ROM (dos)
        f000-ffff ROM (kernel)

Video:      MC6847

Sound:      Buzzer
Floppy:     FDC8271

Hardware:   PPIA 8255

    output  b000    0 - 3 keyboard row, 4 - 7 graphics mode
            b002    0 cas output, 1 enable 2.4kHz, 2 buzzer, 3 colour set

    input   b001    0 - 5 keyboard column, 6 CTRL key, 7 SHIFT key
            b002    4 2.4kHz input, 5 cas input, 6 REPT key, 7 60 Hz input

            VIA 6522


    DOS:

    The original location of the 8271 memory mapped registers is 0xa00-0x0a04.
    (This is the memory range assigned by Acorn in their design.)

    This is in the middle of the area for expansion RAM. Many Atom owners
    thought this was a bad design and have modified their Atom's and dos rom
    to use a different memory area.

    The atom driver in MAME uses the original memory area.

    ---

    The Econet card for the ATOM is decoded on the ATOM PCB at memory address B400 (hex). The Econet Eurocard has decoding circuits on it which select memory address 1940 (hex).
    There are then five significant addresses above these bases which contain the following registers: -

                ATOM card   Eurocard
    6854    register 1  B400        1940
    6854    register 2  B401        1941
    6854    register 3  B402        1942
    6854    Tx/Rx Data reg. B403        1943
    Station identification  B404        1944

    Station identification

    The identity number of each station is set up in hardware by links to IC 8. IC 8 is an octal buffer which when enabled feeds the cards station ID to the computer bus.
    Each link codes a bit in an eight bit binary number allowing any station ID in the range 0 to 255 to be set up. if a link is left open then the bit is a one, when a
    link is made the bit is a zero. Hence all links open corresponds to station ID 255, and all links made to station ID 0. Each station must have a unique identity and
    some identities are associated with specific functions on the network. Station ID zero is reserved for broadcast signals and should not be used. Station ID 255 is
    reserved at present for the file server, and 235 for the printer server. Wire links must be soldered to each network station card during installation, a suggested
    scheme for number allocation is to number normal user stations from one upwards and to number special stations and servers from 255 downwards.


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

/*

    TODO:

    - display should be monochrome -- Should be optional, Acorn produced a Colour Card, and there is
        at least one after market Colour card.
    - tap files
    - mouse
    - color card
    - CP/M card
    - econet
    - Busicomputers Prophet 2
        * The Shift and Return keys are orange and the Return key is large,
        * There is a MODE switch to the top right of the keyboard,
        * There is a VIDEO port in addition to the TV output,
        * An Acorn AtomCalc ROM PCB is installed (is this standard on the Prophet2 or an upgrade?),
        * An Acorn 32K dynamic RAM card is installed,
        * A 5v DC input is added in addition to the standard power in (but this may be a later upgrade),
        * The Utility ROM is labelled P2/FP is installed

    - Cassette UEF not working - all data blocks overwrite each other at 0000 in ram
    - move internal expansion cards (BBC Basic, RAMROM, etc.) to slot devices.

*/

#include "emu.h"

#include "bus/acorn/bus.h"
#include "bus/centronics/ctronics.h"
#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/m6502/m6502.h"
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "imagedev/snapquik.h"
#include "machine/6522via.h"
#include "machine/clock.h"
#include "machine/i8255.h"
#include "machine/timer.h"
#include "sound/spkrdev.h"
//#include "video/es5700.h"
#include "video/mc6847.h"

#include "formats/atom_dsk.h"
#include "formats/atom_tap.h"
#include "formats/imageutl.h"
#include "formats/uef_cas.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "multibyte.h"

#define VERBOSE 0
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"


namespace {

class atom_state : public driver_device
{
public:
	atom_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_text_ram(*this, "textram", 0x1400, ENDIANNESS_LITTLE)
		, m_video_ram(*this, "videoram", 0x1800, ENDIANNESS_LITTLE)
		, m_vdg(*this, "vdg")
		, m_cassette(*this, "cassette")
		, m_speaker(*this, "speaker")
		, m_bus(*this, "bus")
		, m_io_keyboard(*this, "Y%u", 0U)
		, m_cfg_ram(*this, "CFG_RAM")
	{ }

	void atom_base(machine_config &config);
	void atom(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(trigger_reset);

protected:
	required_device<cpu_device> m_maincpu;
	memory_share_creator<uint8_t> m_text_ram;
	memory_share_creator<uint8_t> m_video_ram;
	required_device<mc6847_base_device> m_vdg;
	required_device<cassette_image_device> m_cassette;
	required_device<speaker_sound_device> m_speaker;
	optional_device<acorn_bus_device> m_bus;
	required_ioport_array<12> m_io_keyboard;
	required_ioport m_cfg_ram;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint8_t textram_r(offs_t offset);
	void textram_w(offs_t offset, uint8_t data);
	uint8_t videoram_r(offs_t offset);
	void videoram_w(offs_t offset, uint8_t data);

	void ppi_pa_w(uint8_t data);
	uint8_t ppi_pb_r();
	uint8_t ppi_pc_r();
	void ppi_pc_w(uint8_t data);
	uint8_t vdg_videoram_r(offs_t offset);

	/* keyboard state */
	u8 m_keylatch = 0U;

	/* cassette state */
	bool m_hz2400 = 0;
	bool m_pc0 = 0;
	bool m_pc1 = 0;

	static void floppy_formats(format_registration &fr);
	void cassette_output_tick(int state);

	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);

	void atom_mem(address_map &map) ATTR_COLD;
};


class atombbc_state : public atom_state
{
public:
	atombbc_state(const machine_config &mconfig, device_type type, const char *tag)
		: atom_state(mconfig, type, tag)
		, m_mode(*this, "mode")
	{
	}

	void atombbc(machine_config &config);

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

private:
	memory_view m_mode;

	void atombbc_mem(address_map &map) ATTR_COLD;
};


class prophet_state : public atom_state
{
public:
	prophet_state(const machine_config &mconfig, device_type type, const char *tag)
		: atom_state(mconfig, type, tag)
		, m_cfg_mode(*this, "CFG_MODE")
	{
	}

	void prophet2(machine_config &config);
	void atomes(machine_config &config);

protected:
	void machine_reset() override;

private:
	required_ioport m_cfg_mode;

	void prophet_mem(address_map &map) ATTR_COLD;
	void atomes_mem(address_map &map) ATTR_COLD;
};


class atomrr_state : public atom_state
{
public:
	atomrr_state(const machine_config &mconfig, device_type type, const char *tag)
		: atom_state(mconfig, type, tag)
		, m_ram(*this, "ram", 0x8000, ENDIANNESS_LITTLE)
		, m_flash(*this, "flash")
		, m_mode(*this, "mode")
		, m_jumper(*this, "JUMPER")
		, m_rom_latch(0)
		, m_switch_latch(0)
	{
	}

	void atomrr(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER( clock_boost );

protected:
	void machine_start() override ATTR_COLD;

private:
	memory_share_creator<uint8_t> m_ram;
	required_region_ptr<uint8_t> m_flash;
	memory_view m_mode;
	required_ioport m_jumper;

	void atomrr_mem(address_map &map) ATTR_COLD;

	uint8_t dskram_r(offs_t offset);
	void dskram_w(offs_t offset, uint8_t data);
	uint8_t topram_r(offs_t offset);
	void topram_w(offs_t offset, uint8_t data);
	uint8_t dskrom_r(offs_t offset);
	uint8_t extram_r(offs_t offset);
	void extram_w(offs_t offset, uint8_t data);
	uint8_t extram1_r(offs_t offset);
	void extram1_w(offs_t offset, uint8_t data);
	uint8_t extram2_r(offs_t offset);
	void extram2_w(offs_t offset, uint8_t data);

	uint8_t switch_r();
	void switch_w(uint8_t data);

	uint8_t m_rom_latch = 0;
	uint8_t m_switch_latch = 0;
};


/*-------------------------------------------------
    QUICKLOAD_LOAD_MEMBER(atom_state::quickload_cb)
-------------------------------------------------*/

QUICKLOAD_LOAD_MEMBER(atom_state::quickload_cb)
{
	/*

	    The format for the .ATM files is as follows:

	    Offset Size     Description
	    ------ -------- -----------------------------------------------------------
	    0000h  16 BYTEs ATOM filename (if less than 16 BYTEs, rest is 00h bytes)
	    0010h  WORD     Start address for load
	    0012h  WORD     Execution address
	    0014h  WORD     Size of data in BYTEs
	    0016h  Size     Data

	*/

	uint8_t header[0x16] = { 0 };

	image.fread(header, 0x16);

	uint16_t start_address = get_u16le(&header[0x10]);
	uint16_t run_address = get_u16le(&header[0x12]);
	uint16_t size = get_u16le(&header[0x14]);

	char pgmname[17];
	for (int i = 0; i < 16; i++)
		pgmname[i] = header[i];
	pgmname[16] = 0;
	LOG("ATM filename: %s\n", pgmname);
	LOG("ATM start address: %04X\n", start_address);
	LOG("ATM run address: %04X\n", run_address);
	LOG("ATM size: %04X\n", size);

	address_space &space = m_maincpu->space(AS_PROGRAM);
	for (int i = 0; i < size; i++)
	{
		uint8_t data;
		image.fread(&data, 1);
		space.write_byte(start_address + i, data);
	}

	if (run_address == 0xc2b2)
		space.write_word(0x0c, start_address + size);
	else
		m_maincpu->set_state_int(M6502_PC, run_address);   // if not basic, autostart program (set_pc doesn't work)

	return std::make_pair(std::error_condition(), std::string());
}

/***************************************************************************
    READ/WRITE HANDLERS
***************************************************************************/

uint8_t atom_state::textram_r(offs_t offset)
{
	uint16_t textram_size = BIT(m_cfg_ram->read(), 0, 4) * 1024;

	if (offset < textram_size)
		return m_text_ram[offset];
	else
		return 0x00;
}

void atom_state::textram_w(offs_t offset, uint8_t data)
{
	uint16_t textram_size = BIT(m_cfg_ram->read(), 0, 4) * 1024;

	if (offset < textram_size)
		m_text_ram[offset] = data;
}


uint8_t atom_state::videoram_r(offs_t offset)
{
	uint16_t videoram_size = BIT(m_cfg_ram->read(), 4, 4) * 1024;

	if (offset < videoram_size)
		return m_video_ram[offset];
	else
		return 0x00;
}

void atom_state::videoram_w(offs_t offset, uint8_t data)
{
	uint16_t videoram_size = BIT(m_cfg_ram->read(), 4, 4) * 1024;

	if (offset < videoram_size)
		m_video_ram[offset] = data;
}


uint8_t atomrr_state::dskram_r(offs_t offset)
{
	if (BIT(m_switch_latch, 1))
		return m_ram[offset | 0x0a00];
	else
		return m_bus->read(offset | 0x0a00);
}

void atomrr_state::dskram_w(offs_t offset, uint8_t data)
{
	if (BIT(m_switch_latch, 1))
		m_ram[offset | 0x0a00] = data;
	else
		m_bus->write(offset | 0x0a00, data);
}


uint8_t atomrr_state::dskrom_r(offs_t offset)
{
	uint8_t data = 0xff;

	if (BIT(m_switch_latch, 2))
	{
		data = m_flash[offset | 0x10000];
	}
	else
	{
		data = m_flash[offset | 0x14000];

		// external pl6/7 buffers enabled
		if ((offset & 0x3000) == 0x2000)
			data = m_bus->read(offset | 0xe000);
	}

	return data;
}


uint8_t atomrr_state::topram_r(offs_t offset)
{
	if (!BIT(m_switch_latch, 0))
		return m_ram[offset | 0x7000];
	else
		return 0xff;
}

void atomrr_state::topram_w(offs_t offset, uint8_t data)
{
	if (!BIT(m_switch_latch, 0))
		m_ram[offset | 0x7000] = data;
}


uint8_t atomrr_state::extram_r(offs_t offset)
{
	if (BIT(m_switch_latch, 0))
		return m_ram[offset | 0x7000];
	else
		return m_flash[offset | (m_rom_latch << 12)];
}

void atomrr_state::extram_w(offs_t offset, uint8_t data)
{
	if (BIT(m_switch_latch, 0))
		m_ram[offset | 0x7000] = data;
}


uint8_t atomrr_state::extram1_r(offs_t offset)
{
	if (BIT(m_switch_latch, 0))
		return m_ram [offset | 0x6000];
	else
		return m_flash[offset | (m_rom_latch << 12)];
}

void atomrr_state::extram1_w(offs_t offset, uint8_t data)
{
	if (BIT(m_switch_latch, 0))
		m_ram[offset | 0x6000] = data;
}


uint8_t atomrr_state::extram2_r(offs_t offset)
{
	if (BIT(m_switch_latch, 1))
		return m_ram[offset | 0x7000];
	else
		return m_flash[offset | 0x19000];
}

void atomrr_state::extram2_w(offs_t offset, uint8_t data)
{
	if (BIT(m_switch_latch, 1))
		m_ram[offset | 0x7000] = data;
}

uint8_t atomrr_state::switch_r()
{
	return 0xb0 | m_switch_latch;
}

void atomrr_state::switch_w(uint8_t data)
{
	/*

	    bit     description
	            In Atom Mode:
	    0       ExtRamEn controls whether #A000 bank0 is ROM (0) or RAM (1)
	    1       DskRamEn controls whether #0A00 is RAM
	    2       DskRomEn controls which OS set is selected
	    3       Mode = 0 (AtomMode)
	            In Beeb Mode:
	    0       ExtRamEn1 controls whether #6000 bank 0 is ROM (0) or RAM (1)
	    1       ExtRamEn2 controls whether #7000 is ROM (0) or RAM (1)
	    2       unused
	    3       Mode = 1 (BeebMode)

	*/

	m_switch_latch = data & 0x0f;

	m_mode.select(BIT(data, 3));
}


/***************************************************************************
    MEMORY MAPS
***************************************************************************/

/*-------------------------------------------------
    ADDRESS_MAP( atom_mem )
-------------------------------------------------*/

void atom_state::atom_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(m_bus, FUNC(acorn_bus_device::read), FUNC(acorn_bus_device::write));
	map(0x0000, 0x03ff).ram();
	map(0x2800, 0x3bff).rw(FUNC(atom_state::textram_r), FUNC(atom_state::textram_w));
	map(0x8000, 0x97ff).rw(FUNC(atom_state::videoram_r), FUNC(atom_state::videoram_w));
	map(0xa000, 0xafff).r("romslot", FUNC(generic_slot_device::read_rom));
	map(0xb000, 0xb003).mirror(0x3fc).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xb800, 0xb80f).mirror(0x3f0).m("via", FUNC(via6522_device::map));
	map(0xc000, 0xdfff).rom().region("maincpu", 0x0000);
	map(0xf000, 0xffff).rom().region("maincpu", 0x3000);
}

/*-------------------------------------------------
    ADDRESS_MAP( atombbc_mem )
-------------------------------------------------*/

void atombbc_state::atombbc_mem(address_map &map)
{
	map(0x0000, 0xffff).view(m_mode);
	/* Atom */
	m_mode[0](0x0000, 0xffff).rw(m_bus, FUNC(acorn_bus_device::read), FUNC(acorn_bus_device::write));
	m_mode[0](0x0000, 0x03ff).ram().share("ram00");
	m_mode[0](0x2000, 0x33ff).ram().share("textram");
	m_mode[0](0x3400, 0x37ff).ram().share("ram34");
	m_mode[0](0x3800, 0x3bff).ram().share("ram38");
	m_mode[0](0x8000, 0x97ff).rw(FUNC(atombbc_state::videoram_r), FUNC(atombbc_state::videoram_w));
	m_mode[0](0xa000, 0xafff).r("romslot", FUNC(generic_slot_device::read_rom));
	m_mode[0](0xb000, 0xb003).mirror(0x3fc).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	m_mode[0](0xb800, 0xb80f).mirror(0x3f0).m("via", FUNC(via6522_device::map));
	m_mode[0](0xc000, 0xdfff).rom().region("maincpu", 0x0000);
	m_mode[0](0xf000, 0xffff).rom().region("maincpu", 0x3000);
	/* BBC BASIC */
	m_mode[1](0x0000, 0xffff).rw(m_bus, FUNC(acorn_bus_device::read), FUNC(acorn_bus_device::write));
	m_mode[1](0x0000, 0x13ff).ram().share("textram");
	m_mode[1](0x1400, 0x17ff).ram().share("ram34");
	m_mode[1](0x1800, 0x1bff).ram().share("ram38");
	m_mode[1](0x1c00, 0x1fff).ram().share("ram00");
	m_mode[1](0x4000, 0x57ff).rw(FUNC(atombbc_state::videoram_r), FUNC(atombbc_state::videoram_w));
	m_mode[1](0x6000, 0x6fff).r("romslot", FUNC(generic_slot_device::read_rom));
	m_mode[1](0x7000, 0x7003).mirror(0x3fc).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	m_mode[1](0x7800, 0x780f).mirror(0x3f0).m("via", FUNC(via6522_device::map));
	m_mode[1](0x8000, 0xbfff).rom().region("basic", 0);
	m_mode[1](0xc000, 0xdfff).rom().region("maincpu", 0x4000);
	m_mode[1](0xf000, 0xffff).rom().region("maincpu", 0x7000);
}

/*-------------------------------------------------
    ADDRESS_MAP( prophet_mem )
-------------------------------------------------*/

void prophet_state::prophet_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(m_bus, FUNC(acorn_bus_device::read), FUNC(acorn_bus_device::write));
	map(0x0000, 0x03ff).ram();
	//map(0x2800, 0x3bff) // RAM not installed, external bus buffered.
	map(0x8000, 0x97ff).rw(FUNC(prophet_state::videoram_r), FUNC(prophet_state::videoram_w));
	map(0xa000, 0xafff).rom().region("atomcalc", 0x0000);
	map(0xb000, 0xb003).mirror(0x3fc).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xb800, 0xb80f).mirror(0x3f0).m("via", FUNC(via6522_device::map));
	map(0xc000, 0xdfff).rom().region("maincpu", 0x0000);
	map(0xe000, 0xefff).rom().region("atomcalc", 0x1000);
	map(0xf000, 0xffff).rom().region("maincpu", 0x3000);
}

/*-------------------------------------------------
    ADDRESS_MAP( atomes_mem )
-------------------------------------------------*/

void prophet_state::atomes_mem(address_map &map)
{
	map(0x0000, 0x03ff).ram();
	//map(0x2800, 0x3bff) // RAM not installed, external bus buffered.
	map(0x8000, 0x97ff).rw(FUNC(prophet_state::videoram_r), FUNC(prophet_state::videoram_w));
	map(0xa000, 0xafff).rom().region("ext", 0x0000);
	map(0xb000, 0xb003).mirror(0x3fc).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xb800, 0xb80f).mirror(0x3f0).m("via", FUNC(via6522_device::map));
	map(0xc000, 0xdfff).rom().region("maincpu", 0x0000);
	map(0xf000, 0xffff).rom().region("maincpu", 0x3000);
}

/*-------------------------------------------------
    ADDRESS_MAP( atomrr_mem )
-------------------------------------------------*/

void atomrr_state::atomrr_mem(address_map &map)
{
	map(0x0000, 0xffff).view(m_mode);
	/* Atom */
	m_mode[0](0x0000, 0x6fff).lrw8(
		NAME([this](offs_t offset) { return m_ram[offset]; }),
		NAME([this](offs_t offset, uint8_t data) { m_ram[offset] = data; })
	);
	m_mode[0](0x0a00, 0x0aff).rw(FUNC(atomrr_state::dskram_r), FUNC(atomrr_state::dskram_w));
	m_mode[0](0x7000, 0x7fff).rw(FUNC(atomrr_state::topram_r), FUNC(atomrr_state::topram_w));
	m_mode[0](0x8000, 0x97ff).rw(FUNC(atomrr_state::videoram_r), FUNC(atomrr_state::videoram_w));
	m_mode[0](0x9800, 0x9fff).ram().share("vram_ext");
	m_mode[0](0xa000, 0xafff).rw(FUNC(atomrr_state::extram_r), FUNC(atomrr_state::extram_w));
	m_mode[0](0xc000, 0xffff).r(FUNC(atomrr_state::dskrom_r));
	/* BBC BASIC */
	m_mode[1](0x0000, 0x5fff).lrw8(
		NAME([this](offs_t offset) { return m_ram[offset]; }),
		NAME([this](offs_t offset, uint8_t data) { m_ram[offset] = data; })
	);
	m_mode[1](0x6000, 0x6fff).rw(FUNC(atomrr_state::extram1_r), FUNC(atomrr_state::extram1_w));
	m_mode[1](0x7000, 0x7fff).rw(FUNC(atomrr_state::extram2_r), FUNC(atomrr_state::extram2_w));
	m_mode[1](0x8000, 0x97ff).rw(FUNC(atomrr_state::videoram_r), FUNC(atomrr_state::videoram_w));
	m_mode[0](0x9800, 0x9fff).ram().share("vram_ext");
	m_mode[1](0xa000, 0xffff).rom().region("flash", 0x1a000);
	/* I/O */
	map(0xb000, 0xb003).mirror(0x3fc).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xb400, 0xbff0).lrw8(
		NAME([this](offs_t offset) { return m_bus->read(offset | 0xb400); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0xb400, data); })
	);
	map(0xb800, 0xb80f).mirror(0x3f0).m("via", FUNC(via6522_device::map));
	map(0xbffd, 0xbffd).portr("JUMPER");
	map(0xbffe, 0xbffe).rw(FUNC(atomrr_state::switch_r), FUNC(atomrr_state::switch_w));
	map(0xbfff, 0xbfff).lrw8(NAME([this]() { return 0xb0 | m_rom_latch; }), NAME([this](uint8_t data) { m_rom_latch = data & 0x0f; }));
}


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

/*-------------------------------------------------
    INPUT_CHANGED_MEMBER( trigger_reset )
-------------------------------------------------*/

INPUT_CHANGED_MEMBER( atom_state::trigger_reset )
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? CLEAR_LINE : ASSERT_LINE);

	if (newval)
	{
		machine().schedule_soft_reset();
	}
}

/*-------------------------------------------------
    INPUT_PORTS( atom )
-------------------------------------------------*/

static INPUT_PORTS_START( atom )
	PORT_START("Y0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_G)          PORT_CHAR('G')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_Q)          PORT_CHAR('Q')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ESC")          PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC),27)

	PORT_START("Y1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_F)          PORT_CHAR('F')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_P)          PORT_CHAR('P')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_Z)          PORT_CHAR('Z')

	PORT_START("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2195")     PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_E)          PORT_CHAR('E')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_O)          PORT_CHAR('O')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_Y)          PORT_CHAR('Y')

	PORT_START("Y3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2194")     PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_0)          PORT_CHAR('0')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_D)          PORT_CHAR('D')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_N)          PORT_CHAR('N')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_X)          PORT_CHAR('X')

	PORT_START("Y4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LOCK")         PORT_CODE(KEYCODE_CAPSLOCK)   PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DELETE")       PORT_CODE(KEYCODE_DEL)        PORT_CHAR(8)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_C)          PORT_CHAR('C')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_M)          PORT_CHAR('M')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_W)          PORT_CHAR('W')

	PORT_START("Y5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2191")     PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR('^') // U+2191 = ↑
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("COPY")         PORT_CODE(KEYCODE_TAB)        PORT_CHAR(UCHAR_MAMEKEY(TAB))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_B)          PORT_CHAR('B')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_L)          PORT_CHAR('L')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_V)          PORT_CHAR('V')

	PORT_START("Y6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR(']')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RETURN")       PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_A)          PORT_CHAR('A')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_K)          PORT_CHAR('K')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_U)          PORT_CHAR('U')

	PORT_START("Y7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('\\')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('@')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_J)          PORT_CHAR('J')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_T)          PORT_CHAR('T')

	PORT_START("Y8")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR('[')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_5)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_I)          PORT_CHAR('I')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_S)          PORT_CHAR('S')

	PORT_START("Y9")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPACE")        PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(32)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_H)          PORT_CHAR('H')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_R)          PORT_CHAR('R')

	PORT_START("Y10")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL")         PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT")        PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("Y11")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("REPT")         PORT_CODE(KEYCODE_RCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))

	PORT_START("BRK")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BREAK")        PORT_CODE(KEYCODE_F12)   PORT_CHAR(UCHAR_MAMEKEY(F12)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(atom_state::trigger_reset), 0)
INPUT_PORTS_END

static INPUT_PORTS_START( atom_ram )
	PORT_INCLUDE(atom)

	PORT_START("CFG_RAM")
	PORT_CONFNAME(0x0f, 0x05, "Lower Text Space RAM")
	PORT_CONFSETTING(0x00, "None")
	PORT_CONFSETTING(0x01, "1K #2800-#2BFF")
	PORT_CONFSETTING(0x02, "2K #2800-#2FFF")
	PORT_CONFSETTING(0x03, "3K #2800-#33FF")
	PORT_CONFSETTING(0x04, "4K #2800-#37FF")
	PORT_CONFSETTING(0x05, "5K #2800-#3BFF")
	PORT_CONFNAME(0xf0, 0x60, "Video Graphics RAM")
	PORT_CONFSETTING(0x10, "1K #8000-#83FF")
	PORT_CONFSETTING(0x20, "2K #8000-#87FF")
	PORT_CONFSETTING(0x30, "3K #8000-#8BFF")
	PORT_CONFSETTING(0x40, "4K #8000-#8FFF")
	PORT_CONFSETTING(0x50, "5K #8000-#93FF")
	PORT_CONFSETTING(0x60, "6K #8000-#97FF")
INPUT_PORTS_END


/*-------------------------------------------------
    INPUT_PORTS( atombbc )
-------------------------------------------------*/

static INPUT_PORTS_START( atombbc )
	PORT_INCLUDE(atom)

	PORT_START("CFG_RAM")
	PORT_CONFNAME(0x0f, 0x05, "Lower Text Space RAM")
	PORT_CONFSETTING(0x05, "5K #2800-#3BFF")
	PORT_CONFNAME(0xf0, 0x60, "Video Graphics RAM")
	PORT_CONFSETTING(0x60, "6K #8000-#97FF")
INPUT_PORTS_END


/*-------------------------------------------------
    INPUT_PORTS( prophet )
-------------------------------------------------*/

static INPUT_PORTS_START( prophet )
	PORT_INCLUDE(atom)

	PORT_MODIFY("Y0")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2191")     PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP)) // U+2191 = ↑

	PORT_MODIFY("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2193")     PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN)) // U+2193 = ↓
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_COLON)      PORT_CHAR('+') PORT_CHAR(';')

	PORT_MODIFY("Y3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2190")     PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT)) // U+2190 = ←
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                           PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('*') PORT_CHAR(':')

	PORT_MODIFY("Y4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SCREEN")       PORT_CODE(KEYCODE_TAB)        PORT_CHAR(UCHAR_MAMEKEY(TAB))

	PORT_MODIFY("Y5")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2192")     PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) // U+2192 = →

	PORT_MODIFY("Y6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("HERE")         PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR(']')

	PORT_MODIFY("Y7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ESC")          PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC),27)

	PORT_MODIFY("Y8")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("COPY")         PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR('[')

	PORT_START("CFG_RAM")
	PORT_CONFNAME(0x0f, 0x00, "Lower Text Space RAM")
	PORT_CONFSETTING(0x00, "None")
	PORT_CONFNAME(0xf0, 0x10, "Video Graphics RAM")
	PORT_CONFSETTING(0x10, "1K #8000-#83FF")

	PORT_START("CFG_MODE")
	PORT_CONFNAME(0x01, 0x01, "Mode")
	PORT_CONFSETTING(0x00, "Normal")
	PORT_CONFSETTING(0x01, "Autoboot")
INPUT_PORTS_END


/*-------------------------------------------------
    INPUT_PORTS( atomes )
-------------------------------------------------*/

static INPUT_PORTS_START( atomes )
	PORT_INCLUDE(atom)

	PORT_START("CFG_RAM")
	PORT_CONFNAME(0x0f, 0x00, "Lower Text Space RAM")
	PORT_CONFSETTING(0x00, "None")
	PORT_CONFNAME(0xf0, 0x10, "Video Graphics RAM")
	PORT_CONFSETTING(0x10, "1K #8000-#83FF")

	PORT_START("CFG_MODE")
	PORT_CONFNAME(0x01, 0x01, "Mode")
	PORT_CONFSETTING(0x00, "Normal")
	PORT_CONFSETTING(0x01, "Sign")
INPUT_PORTS_END


/*-------------------------------------------------
    INPUT_PORTS( atomrr )
-------------------------------------------------*/

static INPUT_PORTS_START( atomrr )
	PORT_INCLUDE(atom)

	PORT_START("CFG_RAM")
	PORT_CONFNAME(0x0f, 0x00, "Lower Text Space RAM")
	PORT_CONFSETTING(0x00, "None")
	PORT_CONFNAME(0xf0, 0x60, "Video Graphics RAM")
	PORT_CONFSETTING(0x60, "6K #8000-#97FF")

	PORT_START("JUMPER")
	PORT_CONFNAME(0x04, 0x04, "DSKROMEN")
	PORT_CONFSETTING(0x00, "External")
	PORT_CONFSETTING(0x04, "Flash (AtoMMC)")
	PORT_CONFNAME(0x08, 0x00, "Clock Boost") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(atomrr_state::clock_boost), 0)
	PORT_CONFSETTING(0x00, DEF_STR( No ))
	PORT_CONFSETTING(0x08, DEF_STR( Yes ))
INPUT_PORTS_END


/*-------------------------------------------------
    INPUT_CHANGED_MEMBER( clock_boost )
-------------------------------------------------*/

INPUT_CHANGED_MEMBER( atomrr_state::clock_boost )
{
	m_maincpu->set_unscaled_clock(newval ? 3.579545_MHz_XTAL/2 : 4_MHz_XTAL/4);
}


/***************************************************************************
    DEVICE CONFIGURATION
***************************************************************************/

/*-------------------------------------------------
    I8255 interface
-------------------------------------------------*/

void atom_state::ppi_pa_w(uint8_t data)
{
	/*

	    bit     description

	    0       keyboard column 0
	    1       keyboard column 1
	    2       keyboard column 2
	    3       keyboard column 3
	    4       MC6847 A/G
	    5       MC6847 GM0
	    6       MC6847 GM1
	    7       MC6847 GM2

	*/

	/* keyboard column */
	m_keylatch = data & 0x0f;

	/* MC6847 */
	m_vdg->ag_w(BIT(data, 4));
	m_vdg->gm0_w(BIT(data, 5));
	m_vdg->gm1_w(BIT(data, 6));
	m_vdg->gm2_w(BIT(data, 7));
}

uint8_t atom_state::ppi_pb_r()
{
	/*

	    bit     description

	    0       keyboard row 0
	    1       keyboard row 1
	    2       keyboard row 2
	    3       keyboard row 3
	    4       keyboard row 4
	    5       keyboard row 5
	    6       keyboard CTRL
	    7       keyboard SHFT

	*/

	uint8_t data = 0x3f;

	if (m_keylatch < 10)
		data = m_io_keyboard[m_keylatch]->read() & 0x3f;

	data |= m_io_keyboard[10]->read() & 0xc0;

	return data;
}

uint8_t atom_state::ppi_pc_r()
{
	/*

	    bit     description

	    0       O/P 1, cassette output 0
	    1       O/P 2, cassette output 1
	    2       O/P 3, speaker output
	    3       O/P 4, MC6847 CSS
	    4       2400 Hz input
	    5       cassette input
	    6       keyboard RPT
	    7       MC6847 FS

	*/

	/* 2400 Hz input */
	u8 data = m_hz2400 ? 0x10 : 0;

	/* cassette input */
	data |= (m_cassette->input() > 0.02) << 5;

	/* keyboard RPT */
	data |= m_io_keyboard[11]->read() & 0x40;

	/* MC6847 FS */
	data |= m_vdg->fs_r() ? 0x80 : 0;

	return data;
}

void atom_state::ppi_pc_w(uint8_t data)
{
	/*

	    bit     description

	    0       O/P 1, cassette output 0
	    1       O/P 2, cassette output 1
	    2       O/P 3, speaker output
	    3       O/P 4, MC6847 CSS
	    4       2400 Hz input
	    5       cassette input
	    6       keyboard RPT
	    7       MC6847 FS

	*/

	/* cassette output */
	m_pc0 = BIT(data, 0);
	m_pc1 = BIT(data, 1);

	/* speaker output */
	m_speaker->level_w(BIT(data, 2));

	/* MC6847 CSS */
	m_vdg->css_w(BIT(data, 3));
}

void atom_state::cassette_output_tick(int state)
{
	m_hz2400 = state;

	bool level = m_pc0 && !(m_pc1 && !m_hz2400);

	m_cassette->output(level ? -1.0 : +1.0);
}

/*-------------------------------------------------
    mc6847 interface
-------------------------------------------------*/

uint8_t atom_state::vdg_videoram_r(offs_t offset)
{
	if (offset == ~0) return 0xff;

	m_vdg->as_w(BIT(m_video_ram[offset], 6));
	m_vdg->intext_w(BIT(m_video_ram[offset], 6));
	m_vdg->inv_w(BIT(m_video_ram[offset], 7));

	return m_video_ram[offset];
}

/***************************************************************************
    MACHINE INITIALIZATION
***************************************************************************/

void atom_state::machine_start()
{
	/* Kees van Oss mentions that address 8-b are used for the random number
	generator. I don't know if this is hardware, or random data because the
	ram chips are not cleared at start-up. So at this time, these numbers
	are poked into the memory to simulate it. */
	address_space &space = m_maincpu->space(AS_PROGRAM);
	space.write_byte(0x08, machine().rand() & 0xff);
	space.write_byte(0x09, machine().rand() & 0xff);
	space.write_byte(0x0a, machine().rand() & 0xff);
	space.write_byte(0x0b, machine().rand() & 0xff);

	save_item(NAME(m_keylatch));
	save_item(NAME(m_hz2400));
	save_item(NAME(m_pc0));
	save_item(NAME(m_pc1));
}

void atom_state::machine_reset()
{
	m_keylatch = 0;
	m_hz2400 = 0;
	m_pc0 = 0;
	m_pc1 = 0;
}


void atombbc_state::machine_start()
{
	atom_state::machine_start();

	/* select BBC BASIC by default */
	m_mode.select(1);
}

void atombbc_state::machine_reset()
{
	atom_state::machine_reset();

	/* mode is selected with SHIFT+BREAK (Atom) or CTRL+BREAK (BBC BASIC) */
	switch (m_io_keyboard[10]->read())
	{
	case 0x40: m_mode.select(0); break; /* Atom */
	case 0x80: m_mode.select(1); break; /* BBC BASIC */
	}
}


void prophet_state::machine_reset()
{
	atom_state::machine_reset();

	if (m_cfg_mode->read())
	{
		/* Autoboot into ROM */
		m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE);
	}
}


void atomrr_state::machine_start()
{
	atom_state::machine_start();

	switch_w(6);

	save_item(NAME(m_rom_latch));
	save_item(NAME(m_switch_latch));
}


/***************************************************************************
    DEFAULT CARD SETTINGS
***************************************************************************/

static DEVICE_INPUT_DEFAULTS_START(32k_ram32)
	DEVICE_INPUT_DEFAULTS("LINKS", 0x07, 0x02)
DEVICE_INPUT_DEFAULTS_END

static DEVICE_INPUT_DEFAULTS_START(32k_ram32dos)
	DEVICE_INPUT_DEFAULTS("LINKS", 0x07, 0x03)
DEVICE_INPUT_DEFAULTS_END


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

void atom_state::atom_base(machine_config &config)
{
	M6502(config, m_maincpu, 4_MHz_XTAL/4);
	m_maincpu->set_addrmap(AS_PROGRAM, &atom_state::atom_mem);
	config.set_perfect_quantum(m_maincpu); // required for Tube interface

	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	MC6847(config, m_vdg, 3.579545_MHz_XTAL);
	m_vdg->input_callback().set(FUNC(atom_state::vdg_videoram_r));
	m_vdg->set_screen("screen");

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);

	clock_device &cass_clock(CLOCK(config, "cass_clock", 4_MHz_XTAL/16/13/8));
	cass_clock.signal_handler().set(FUNC(atom_state::cassette_output_tick));  // 2403.846Hz

	i8255_device &ppi(I8255(config, "ppi"));
	ppi.out_pa_callback().set(FUNC(atom_state::ppi_pa_w));
	ppi.in_pb_callback().set(FUNC(atom_state::ppi_pb_r));
	ppi.in_pc_callback().set(FUNC(atom_state::ppi_pc_r));
	ppi.out_pc_callback().set(FUNC(atom_state::ppi_pc_w));

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_formats(atom_cassette_formats);
	m_cassette->set_interface("atom_cass");
}

void atom_state::atom(machine_config &config)
{
	atom_base(config);

	via6522_device &via(MOS6522(config, "via", 4_MHz_XTAL/4));
	via.writepa_handler().set("cent_data_out", FUNC(output_latch_device::write));
	via.readpb_handler().set(m_bus, FUNC(acorn_bus_device::pb_r));
	via.writepb_handler().set(m_bus, FUNC(acorn_bus_device::pb_w));
	via.ca2_handler().set("centronics", FUNC(centronics_device::write_strobe));
	via.cb1_handler().set(m_bus, FUNC(acorn_bus_device::write_cb1));
	via.cb2_handler().set(m_bus, FUNC(acorn_bus_device::write_cb2));

	centronics_device &centronics(CENTRONICS(config, "centronics", centronics_devices, "printer"));
	centronics.ack_handler().set("via", FUNC(via6522_device::write_ca1));
	centronics.busy_handler().set("via", FUNC(via6522_device::write_pa7));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	centronics.set_output_latch(cent_data_out);

	QUICKLOAD(config, "quickload", "atm", attotime::from_seconds(2)).set_load_callback(FUNC(atom_state::quickload_cb));

	/* extension bus */
	ACORN_BUS(config, m_bus);
	m_bus->out_irq_callback().set_inputline("maincpu", M6502_IRQ_LINE);
	m_bus->out_nmi_callback().set_inputline("maincpu", INPUT_LINE_NMI);
	m_bus->cb1_handler().set("via", FUNC(via6522_device::write_cb1));
	m_bus->cb2_handler().set("via", FUNC(via6522_device::write_cb2));
	ACORN_BUS_SLOT(config, "pl6", m_bus, atom_bus_devices, "discpack");
	ACORN_BUS_SLOT(config, "pl7", m_bus, atom_bus_devices, "32k").set_option_device_input_defaults("32k", DEVICE_INPUT_DEFAULTS_NAME(32k_ram32dos));
	ACORN_BUS_SLOT(config, "pl8", m_bus, atom_pl8_devices, nullptr);

	/* utility rom slot */
	GENERIC_SOCKET(config, "romslot", generic_linear_slot, "atom_cart", "bin,rom");

	/* software lists */
	SOFTWARE_LIST(config, "rom_list").set_original("atom_rom");
	SOFTWARE_LIST(config, "cass_list").set_original("atom_cass");
	SOFTWARE_LIST(config, "flop_list").set_original("atom_flop");
}


void atombbc_state::atombbc(machine_config &config)
{
	atom(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &atombbc_state::atombbc_mem);
}


void prophet_state::prophet2(machine_config &config)
{
	atom(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &prophet_state::prophet_mem);

	/* extension bus */
	subdevice<acorn_bus_slot_device>("pl6")->set_default_option(nullptr);
	subdevice<acorn_bus_slot_device>("pl7")->set_default_option("32k").set_option_device_input_defaults("32k", DEVICE_INPUT_DEFAULTS_NAME(32k_ram32));
	subdevice<acorn_bus_slot_device>("pl7")->set_fixed(true);
}


//void atom_state::prophet3(machine_config &config)
//{
//  atom(config);
//  m_maincpu->set_addrmap(AS_PROGRAM, &atom_state::prophet_mem);
//
//  /* extension bus */
//  subdevice<acorn_bus_slot_device>("pl6")->set_default_option("discpack"); // Polebrook Computer Services Ltd FDC1 (missing ROM P3DOS + 4K RAM)
//  subdevice<acorn_bus_slot_device>("pl7")->set_default_option("32k");
//  subdevice<acorn_bus_slot_device>("pl7")->set_fixed(true);
//}


void prophet_state::atomes(machine_config &config)
{
	atom_base(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &prophet_state::atomes_mem);

	via6522_device &via(MOS6522(config, "via", 4_MHz_XTAL/4));
	via.writepa_handler().set_nop(); // ("es5700", FUNC(es5700_device::write));

	// TODO: add LED message device
	//ES5700(config, "es5700");
}


void atomrr_state::atomrr(machine_config &config)
{
	atom(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &atomrr_state::atomrr_mem);

	/* extension bus */
	subdevice<acorn_bus_slot_device>("pl6")->set_default_option("atomsid");
	subdevice<acorn_bus_slot_device>("pl7")->set_default_option("atomtube");
	subdevice<acorn_bus_slot_device>("pl8")->set_default_option(nullptr); // atommc (add default when AtoMMC is emulated)
}


/***************************************************************************
    ROMS
***************************************************************************/

ROM_START( atom )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "abasic.ic20", 0x0000, 0x1000, CRC(289b7791) SHA1(0072c83458a9690a3ea1f6094f0f38cf8e96a445) )
	ROM_CONTINUE(            0x3000, 0x1000 )
	ROM_LOAD( "afloat.ic21", 0x1000, 0x1000, CRC(81d86af7) SHA1(ebcde5b36cb3a3344567cbba4c7b9fde015f4802) )
ROM_END

ROM_START( atombbc )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASE00 )
	/* Atom mode */
	ROM_LOAD( "abasic.ic20", 0x0000, 0x1000, CRC(289b7791) SHA1(0072c83458a9690a3ea1f6094f0f38cf8e96a445) )
	ROM_CONTINUE(            0x3000, 0x1000 )
	ROM_LOAD( "afloat.ic21", 0x1000, 0x1000, CRC(81d86af7) SHA1(ebcde5b36cb3a3344567cbba4c7b9fde015f4802) )
	/* BBC mode */
	ROM_LOAD( "mos3.rom",    0x7000, 0x1000, CRC(20158bd8) SHA1(5ee4c0d2b65be72646e17d69b76fb00a0e5298df) )

	ROM_REGION( 0x4000, "basic", 0)
	ROM_LOAD( "bbcbasic.rom", 0x0000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281) )
ROM_END

ROM_START( prophet2 )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "abasic.ic20", 0x0000, 0x1000, CRC(289b7791) SHA1(0072c83458a9690a3ea1f6094f0f38cf8e96a445) )
	ROM_CONTINUE(            0x3000, 0x1000)
	ROM_LOAD( "p2fp.ic21",   0x1000, 0x1000, CRC(8be45181) SHA1(b033e2b0b0690e3e4c1f1ec3036f8d83da619f68) )

	ROM_REGION( 0x2000, "atomcalc", 0 )
	ROM_LOAD( "a_69ed.rom", 0x0000, 0x1000, CRC(006010b7) SHA1(1e25c451baad92b74e946cb8dd48abfcaed59d91) )
	ROM_LOAD( "e_61e5.rom", 0x1000, 0x1000, CRC(ecd2d08b) SHA1(d3fe606db1c3372d1e2c62e1143682d33b9fff38) )
ROM_END

//#define rom_prophet3 rom_atom

ROM_START( atomes )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "abasic.ic20", 0x0000, 0x1000, CRC(289b7791) SHA1(0072c83458a9690a3ea1f6094f0f38cf8e96a445) )
	ROM_CONTINUE(            0x3000, 0x1000)

	ROM_REGION( 0x1000, "ext", 0 )
	ROM_LOAD( "pr2b.ic24",   0x0000, 0x1000, CRC(98ddae59) SHA1(92b0df53ab8275e4d7e120d495186f6fdefb0822) )
ROM_END

ROM_START( atomrr )
	/*
	  0x00000 - Atom #A000 Bank 0 - SDDOS v3.25
	  0x01000 - Atom #A000 Bank 1 - PCharme v1.73
	  0x02000 - Atom #A000 Bank 2 - Gags v2.3
	  0x03000 - Atom #A000 Bank 3 - AXR1
	  0x04000 - Atom #A000 Bank 4 - AtomFpga Utils v0.21 (was SALFAA v2.6)
	  0x05000 - Atom #A000 Bank 5 - Atomic Windows v1.1
	  0x06000 - Atom #A000 Bank 6 - WE ROM
	  0x07000 - Atom #A000 Bank 7 - Program Power Programmers Toolkit
	  0x08000 - BBC #6000 Bank 0 (ExtROM1)
	  0x09000 - BBC #6000 Bank 1 (ExtROM1)
	  0x0A000 - BBC #6000 Bank 2 (ExtROM1)
	  0x0B000 - BBC #6000 Bank 3 (ExtROM1)
	  0x0C000 - BBC #6000 Bank 4 (ExtROM1)
	  0x0D000 - BBC #6000 Bank 5 (ExtROM1)
	  0x0E000 - BBC #6000 Bank 6 (ExtROM1)
	  0x0F000 - BBC #6000 Bank 7 (ExtROM1)
	  0x10000 - Atom Basic (DskRomEn=1)
	  0x11000 - Atom FP (DskRomEn=1)
	  0x12000 - Atom MMC (DskRomEn=1)
	  0x13000 - Atom Kernel (DskRomEn=1)
	  0x14000 - Atom Basic (DskRomEn=0)
	  0x15000 - Atom FP (DskRomEn=0)
	  0x16000 - unused
	  0x17000 - Atom Kernel (DskRomEn=0)
	  0x18000 - unused
	  0x19000 - BBC #7000 (ExtROM2)
	  0x1A000 - BBC Basic 1/4
	  0x1B000 - unused
	  0x1C000 - BBC Basic 2/4
	  0x1D000 - BBC Basic 3/4
	  0x1E000 - BBC Basic 4/4
	  0x1F000 - BBC MOS 3.0
	*/
	ROM_REGION( 0x20000, "flash", 0 )
	ROM_LOAD( "ramrom.rom", 0x0000, 0x20000, CRC(250c5b3e) SHA1(ffc4a965ad002c082cd68cea9d2c73045bc9d073) )
ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS          INIT         COMPANY             FULLNAME                                   FLAGS
COMP( 1979, atom,     0,      0,      atom,     atom_ram, atom_state,    empty_init,  "Acorn Computers",  "Atom",                                    MACHINE_SUPPORTS_SAVE )
COMP( 1982, atombbc,  atom,   0,      atombbc,  atombbc,  atombbc_state, empty_init,  "Acorn Computers",  "Atom with BBC Basic",                     MACHINE_SUPPORTS_SAVE )
COMP( 1983, prophet2, atom,   0,      prophet2, prophet,  prophet_state, empty_init,  "Busicomputers",    "Prophet 2",                               MACHINE_SUPPORTS_SAVE )
//COMP( 1983, prophet3, atom,   0,      prophet3, prophet,  prophet_state, empty_init,  "Busicomputers",    "Prophet 3",                               MACHINE_SUPPORTS_SAVE )
COMP( 198?, atomes,   atom,   0,      atomes,   atomes,   prophet_state, empty_init,  "Pearce Signs",     "ES5700 (LED Electronic Message System)",  MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING )
COMP( 2014, atomrr,   atom,   0,      atomrr,   atomrr,   atomrr_state,  empty_init,  "Acorn Computers",  "Atom with RAMROM",                        MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING )



atpci.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol, Miodrag Milanovic, Carl

#include "emu.h"
#include "cpu/i386/i386.h"
#include "bus/isa/isa_cards.h"
#include "bus/lpci/pci.h"
#include "bus/lpci/i82371ab.h"
#include "bus/lpci/i82371sb.h"
#include "bus/lpci/i82439tx.h"
#include "machine/at.h"
#include "machine/fdc37c93x.h"
#include "machine/pckeybrd.h"


namespace {

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

	void at586x3(machine_config &config);
	void at586(machine_config &config);
	void at586m55(machine_config &config);

protected:
	void at_softlists(machine_config &config);

	void boot_state_w(uint8_t data);

	void tx_config(device_t *device);
	void sb_config(device_t *device);
	void superio_config(device_t *device);

	void at586_io(address_map &map) ATTR_COLD;
	void at586_map(address_map &map) ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
};

void at586_state::boot_state_w(uint8_t data)
{
	logerror("Boot state %02x\n", data);
	printf("[%02X]",data);
}

void at586_state::tx_config(device_t *device)
{
	downcast<i82439tx_device *>(device)->set_cpu(m_maincpu);
	downcast<i82439tx_device *>(device)->set_region("isa");
}

void at586_state::sb_config(device_t *device)
{
	i82371sb_device &sb = *downcast<i82371sb_device *>(device);
	sb.set_cpu(m_maincpu);
	sb.boot_state_hook().set(":", FUNC(at586_state::boot_state_w));
	sb.smi().set_inputline(m_maincpu, INPUT_LINE_SMI);
}


void at586_state::superio_config(device_t *device)
{
	fdc37c93x_device &fdc = *downcast<fdc37c93x_device *>(device);
	fdc.set_sysopt_pin(1);
	fdc.gp20_reset().set_inputline(m_maincpu, INPUT_LINE_RESET);
	fdc.gp25_gatea20().set_inputline(m_maincpu, INPUT_LINE_A20);
	fdc.irq1().set(":pcibus:7:i82371sb:pic8259_master", FUNC(pic8259_device::ir1_w));
}


static void isa_internal_devices(device_slot_interface &device)
{
	device.option_add("fdc37c93x", FDC37C93X);
}

static void pci_devices(device_slot_interface &device)
{
	device.option_add_internal("i82439tx", I82439TX_LEGACY);
	device.option_add_internal("i82371ab", I82371AB);
	device.option_add_internal("i82371sb", I82371SB);
}

void at586_state::at586_map(address_map &map)
{
	map(0x00000000, 0x0009ffff).bankrw("bank10");
	map(0x000a0000, 0x000bffff).noprw();
	map(0x00800000, 0x00800bff).ram().share("nvram");
	map(0xfffe0000, 0xffffffff).rom().region("isa", 0x20000);
}

void at586_state::at586_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0cf8, 0x0cff).rw("pcibus", FUNC(pci_bus_device::read), FUNC(pci_bus_device::write));
}

void at586_state::at_softlists(machine_config &config)
{
	/* software lists */
	SOFTWARE_LIST(config, "pc_disk_list").set_original("ibm5150");
	SOFTWARE_LIST(config, "at_disk_list").set_original("ibm5170");
	SOFTWARE_LIST(config, "at_cdrom_list").set_original("ibm5170_cdrom");
	SOFTWARE_LIST(config, "at_hdd_list").set_original("ibm5170_hdd");
	SOFTWARE_LIST(config, "midi_disk_list").set_compatible("midi_flop");
	SOFTWARE_LIST(config, "photocd_list").set_compatible("photo_cd");
}

void at586_state::at586(machine_config &config)
{
	pentium_device &maincpu(PENTIUM(config, m_maincpu, 60000000));
	maincpu.set_addrmap(AS_PROGRAM, &at586_state::at586_map);
	maincpu.set_addrmap(AS_IO, &at586_state::at586_io);
	maincpu.set_irq_acknowledge_callback("pcibus:1:i82371ab:pic8259_master", FUNC(pic8259_device::inta_cb));

	RAM(config, RAM_TAG).set_default_size("4M").set_extra_options("1M,2M,8M,16M,32M,64M,128M,256M");

	PCI_BUS(config, "pcibus", 0).set_busnum(0);
	PCI_CONNECTOR(config, "pcibus:0", pci_devices, "i82439tx", true).set_option_machine_config("i82439tx", [this](device_t *device) { tx_config(device); });
	PCI_CONNECTOR(config, "pcibus:1", pci_devices, "i82371ab", true);

	// FIXME: determine ISA bus clock
	ISA16_SLOT(config, "isa1", 0, "pcibus:1:i82371ab:isabus", pc_isa16_cards, "svga_et4k", false);
	ISA16_SLOT(config, "isa2", 0, "pcibus:1:i82371ab:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa3", 0, "pcibus:1:i82371ab:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa4", 0, "pcibus:1:i82371ab:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa5", 0, "pcibus:1:i82371ab:isabus", pc_isa16_cards, nullptr, false);

	at_softlists(config);
}

void at586_state::at586x3(machine_config &config)
{
	pentium_device &maincpu(PENTIUM(config, m_maincpu, 60000000));
	maincpu.set_addrmap(AS_PROGRAM, &at586_state::at586_map);
	maincpu.set_addrmap(AS_IO, &at586_state::at586_io);
	maincpu.smiact().set("pcibus:0:i82439tx", FUNC(i82439tx_device::smi_act_w));
	maincpu.set_irq_acknowledge_callback("pcibus:1:i82371sb:pic8259_master", FUNC(pic8259_device::inta_cb));

	RAM(config, RAM_TAG).set_default_size("4M").set_extra_options("1M,2M,8M,16M,32M,64M,128M,256M");

	PCI_BUS(config, "pcibus", 0).set_busnum(0);
	PCI_CONNECTOR(config, "pcibus:0", pci_devices, "i82439tx", true).set_option_machine_config("i82439tx", [this](device_t *device) { tx_config(device); });
	PCI_CONNECTOR(config, "pcibus:1", pci_devices, "i82371sb", true).set_option_machine_config("i82371sb", [this](device_t *device) { sb_config(device); });

	// FIXME: determine ISA bus clock
	ISA16_SLOT(config, "isa1", 0, "pcibus:1:i82371sb:isabus", pc_isa16_cards, "svga_et4k", false);
	ISA16_SLOT(config, "isa2", 0, "pcibus:1:i82371sb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa3", 0, "pcibus:1:i82371sb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa4", 0, "pcibus:1:i82371sb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa5", 0, "pcibus:1:i82371sb:isabus", pc_isa16_cards, nullptr, false);

	at_softlists(config);
}

static INPUT_PORTS_START(at586m55)
INPUT_PORTS_END

void at586_state::at586m55(machine_config &config)
{
	pentium_device &pentium(PENTIUM(config, m_maincpu, 60000000));
	pentium.set_addrmap(AS_PROGRAM, &at586_state::at586_map);
	pentium.set_addrmap(AS_IO, &at586_state::at586_io);
	pentium.smiact().set("pcibus:0:i82439tx", FUNC(i82439tx_device::smi_act_w));
	pentium.set_irq_acknowledge_callback("pcibus:7:i82371sb:pic8259_master", FUNC(pic8259_device::inta_cb));

	RAM(config, RAM_TAG).set_default_size("4M").set_extra_options("1M,2M,8M,16M,32M,64M,128M,256M");

	PCI_BUS(config, "pcibus", 0).set_busnum(0);
	PCI_CONNECTOR(config, "pcibus:0", pci_devices, "i82439tx", true).set_option_machine_config("i82439tx", [this](device_t *device) { tx_config(device); });
	PCI_CONNECTOR(config, "pcibus:7", pci_devices, "i82371sb", true).set_option_machine_config("i82371sb", [this](device_t *device) { sb_config(device); });

	ISA16_SLOT(config, "board4", 0, "pcibus:7:i82371sb:isabus",  isa_internal_devices, "fdc37c93x", true).set_option_machine_config("fdc37c93x", [this](device_t *device) { superio_config(device); });
	ISA16_SLOT(config, "isa1", 0, "pcibus:7:i82371sb:isabus",  pc_isa16_cards, "svga_et4k", false);
	ISA16_SLOT(config, "isa2", 0, "pcibus:7:i82371sb:isabus",  pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa3", 0, "pcibus:7:i82371sb:isabus",  pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa4", 0, "pcibus:7:i82371sb:isabus",  pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa5", 0, "pcibus:7:i82371sb:isabus",  pc_isa16_cards, nullptr, false);

	at_softlists(config);
}

ROM_START( at586 )
	ROM_REGION32_LE(0x40000, "isa", 0)
	ROM_SYSTEM_BIOS(0, "sptx", "SP-586TX")
	ROMX_LOAD("sp586tx.bin",   0x20000, 0x20000, CRC(1003d72c) SHA1(ec9224ff9b0fdfd6e462cb7bbf419875414739d6), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "unisys", "Unisys 586") // probably bad dump due to need of hack in i82439tx to work
	ROMX_LOAD("at586.bin",     0x20000, 0x20000, CRC(717037f5) SHA1(1d49d1b7a4a40d07d1a897b7f8c827754d76f824), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "ga586t2", "Gigabyte GA-586T2") // ITE 8679 I/O
	ROMX_LOAD("gb_ga586t2.bin",  0x20000, 0x20000, CRC(3a50a6e1) SHA1(dea859b4f1492d0d08aacd260ed1e83e00ebac08), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "5tx52", "Acorp 5TX52") // W83877TF I/O
	ROMX_LOAD("acorp_5tx52.bin", 0x20000, 0x20000, CRC(04d69419) SHA1(983377674fef05e710c8665c14cc348c99166fb6), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "txp4", "ASUS TXP4") // W83977TF-A I/O
	ROMX_LOAD("asus_txp4.bin",   0x20000, 0x20000, CRC(a1321bb1) SHA1(92e5f14d8505119f85b148a63510617ac12bcdf3), ROM_BIOS(4))
ROM_END

// Elitegroup SI5PI AIO Rev. 1.1 - Chipset: SiS 85C501, 85C502, 85C502, FDC37C666GT, UM82C865F, PCI0640B - CPU: Socket 4 - RAM: 4xSIMM72, Cache: 18x32pin
// BIOS: Award 32pin - Keyboard-BIOS: AMIKEY-2 - ISA16: 4, PCI: 4 - On board: 2xISA, 2xser, Floppy, par
ROM_START( ecssi5pi )
	ROM_REGION32_LE(0x40000, "isa", 0)
	// BIOS-String: 11/17/94-SiS-501-503-2A5IAE11-00 / SI5PI AIO 11/18/94
	ROM_SYSTEM_BIOS(0, "111894", "11/18/94")
	ROMX_LOAD("si5piaio11_isa_pci_bios_013908435.bin", 0x20000, 0x20000, CRC(c39aa47c) SHA1(6b31bdc5441c5f7a29d0ceb8989ccfed92c49900), ROM_BIOS(0))
	// BIOS-String: 12/02/94-SiS-501-503-2A5IAE11-00 / SI5PI AIO 12/02/94
	ROM_SYSTEM_BIOS(1, "120294", "12/02/94")
	ROMX_LOAD("si5piaio.bin", 0x20000, 0x20000, CRC(9dfb8510) SHA1(b86cc1930dc78db3c4c9d1ed13ec60b2333db7d1), ROM_BIOS(1))
ROM_END


ROM_START(at586x3)
	ROM_REGION32_LE(0x40000, "isa", 0)
	ROM_SYSTEM_BIOS(0, "5hx29", "5HX29")
	ROMX_LOAD("5hx29.bin", 0x20000, 0x20000, CRC(07719a55) SHA1(b63993fd5186cdb4f28c117428a507cd069e1f68), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "n7ns04", "Version 21/01/98, without integrated sound") // Micronics M7S-HI with SMSC FDC37C93X I/O
	ROMX_LOAD("m7ns04.rom", 0x00000, 0x40000, CRC(9c1f656b) SHA1(f4a0a522d8c47b6ddb6c01fe9a34ddf5b1977f8d), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "n7s04", "Version 21/01/98, with integrated sound")
	ROMX_LOAD("m7s04.rom", 0x00000, 0x40000, CRC(3689f5a9) SHA1(8daacdb0dc6783d2161680564ffe83ac2515f7ef), ROM_BIOS(2))

	ROM_SYSTEM_BIOS(3, "m7shi03", "m7shi03") // Micronics M7S-Hi
	ROMX_LOAD("m7shi03.snd", 0x00000, 0x40000, CRC(3a35a939) SHA1(74af69eb5ca546b0960540e7c3ea62a532157f2a), ROM_BIOS(3))
ROM_END

/* FIC VT-503 (Intel TX chipset, ITE 8679 Super I/O) */
ROM_START( ficvt503 )
	ROM_REGION32_LE(0x40000, "isa", 0)
	ROM_SYSTEM_BIOS(0, "109gi13", "1.09GI13") /* 1997-10-02 */
	ROMX_LOAD("109gi13.bin", 0x20000, 0x20000, CRC(0c32af48) SHA1(2cce40a98598f1ed1f398975f7a90c8be4200667), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "109gi14", "1.09GI14") /* 1997-11-07 */
	ROMX_LOAD("109gi14.awd", 0x20000, 0x20000, CRC(588c5cc8) SHA1(710e5405850fd975b362a422bfe9bc6d6c9a36cd), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "109gi15", "1.09GI15") /* 1997-11-07 */
	ROMX_LOAD("109gi15.awd", 0x20000, 0x20000, CRC(649a3481) SHA1(e681c6ab55a67cec5978dfffa75fcddc2aa0de4d), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "109gi16", "1.09GI16") /* 2000-03-23 */
	ROMX_LOAD("109gi16.bin", 0x20000, 0x20000, CRC(a928f271) SHA1(127a83a60752cc33b3ca49774488e511ec7bac55), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "115gk140", "1.15GK140") /* 1999-03-03 */
	ROMX_LOAD("115gk140.awd", 0x20000, 0x20000, CRC(65e88956) SHA1(f94bb0732e00b5b0f18f4e349db24a289f8379c5), ROM_BIOS(4))
ROM_END

/* Micronics M55Hi-Plus (Intel 430HX chipset, SMSC FDC37C93X Super I/O) */
ROM_START(m55hipl)
	ROM_REGION32_LE(0x40000, "isa", 0)
	ROM_SYSTEM_BIOS(0, "m55ns04", "m55ns04") // Micronics M55HI-Plus with no sound
	ROMX_LOAD("m55-04ns.rom", 0x20000, 0x20000, CRC(0116b2b0) SHA1(19b0203decfd4396695334517488d488aec3ccde), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "m55s04", "m55s04") // with sound
	ROMX_LOAD("m55-04s.rom", 0x20000, 0x20000, CRC(34a7422e) SHA1(68753fe373c97844beff83ea75c634c77cfedb8f), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "m55ns03", "m55ns03") // Micronics M55HI-Plus with no sound
	ROMX_LOAD("m55ns03.rom", 0x20000, 0x20000, CRC(6a3deb49) SHA1(78bfc20e0f8699f4d153d241a757153afcde3efb), ROM_BIOS(2))

	ROM_SYSTEM_BIOS(3, "m55hi03", "m55hi03") // with sound
	ROMX_LOAD("m55hi03.rom", 0x20000, 0x20000, CRC(bd476200) SHA1(7633ba27819ad45c6253abb728b1ef0c49229743), ROM_BIOS(3))
ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT   COMPAT   MACHINE   INPUT     CLASS        INIT        COMPANY      FULLNAME             FLAGS
COMP( 1990, at586,    ibm5170, 0,       at586,    0,        at586_state, empty_init, "<generic>", "PC/AT 586 (PIIX4)", MACHINE_NOT_WORKING )
COMP( 1990, at586x3,  ibm5170, 0,       at586x3,  0,        at586_state, empty_init, "<generic>", "PC/AT 586 (PIIX3)", MACHINE_NOT_WORKING )
COMP( 1997, ficvt503, ibm5170, 0,       at586,    0,        at586_state, empty_init, "FIC",       "VT-503",            MACHINE_NOT_WORKING )
COMP( 1990, m55hipl,  ibm5170, 0,       at586m55, at586m55, at586_state, empty_init, "Micronics", "M55Hi-Plus",        MACHINE_NOT_WORKING )
COMP( 199?, ecssi5pi, ibm5170, 0,       at586,    0,        at586_state, empty_init, "Elitegroup","SI5PI AIO",         MACHINE_NOT_WORKING )



att3b2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for AT&T Model 3B2 computer.

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

#include "emu.h"
#include "cpu/we32000/we32100.h"
#include "machine/am9517a.h"
#include "machine/mc68681.h"
#include "machine/pit8253.h"
//#include "machine/upd7261.h"
#include "machine/wd_fdc.h"


namespace {

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

	void att3b2v2(machine_config &config);
	void att3b2v3(machine_config &config);

private:
	void mem_map_300(address_map &map) ATTR_COLD;
	void mem_map_600(address_map &map) ATTR_COLD;

	required_device<we32100_device> m_maincpu;
};


void att3b2_state::mem_map_300(address_map &map)
{
	map.global_mask(0x07ffffff); // 27-bit physical addresses
	map(0x00000000, 0x00007fff).rom().region("bootstrap", 0);
	map(0x00042000, 0x0004200f).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask32(0x000000ff);
	//map(0x00042013, 0x00042013).r(FUNC(att3b2_state::clear_csr6_r));
	//map(0x00043000, 0x00043fff).rw(FUNC(att3b2_state::nvram_nibble_r), FUNC(att3b2_state::nvram_nibble_w)).umask32(0x00ff00ff);
	//map(0x00044000, 0x0004403f).w(FUNC(att3b2_state::csr_w)).umask32(0x000000ff);
	//map(0x00044002, 0x00044003).r(FUNC(att3b2_state::csr_r));
	//map(0x00045003, 0x00045003).w(FUNC(att3b2_state::hard_disk_page_w));
	map(0x00048000, 0x0004800f).rw("dmac", FUNC(am9517a_device::read), FUNC(am9517a_device::write));
	map(0x00049000, 0x0004900f).rw("duart", FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	//map(0x0004a000, 0x0004a001).rw("hdc", FUNC(upd7261_device::read), FUNC(upd7261_device::write));
	//map(0x0004c003, 0x0004c003).r(FUNC(att3b2_state::dpdram_size_r));
	map(0x0004d000, 0x0004d003).rw("fdc", FUNC(wd2797_device::read), FUNC(wd2797_device::write));
	map(0x02000000, 0x0203ffff).ram();
}

void att3b2_state::mem_map_600(address_map &map)
{
	map(0x00000000, 0x0001ffff).rom().region("bootstrap", 0);
	//map(0x00040003, 0x00040003).w(FUNC(att3b2_state::floppy_control_w));
	map(0x00041000, 0x0004100f).rw("pit", FUNC(pit8254_device::read), FUNC(pit8254_device::write)).umask32(0x000000ff);
	//map(0x00042000, 0x00043fff).rw(FUNC(att3b2_state::nvram_byte_r), FUNC(att3b2_state::nvram_byte_w)).umask32(0x00ff00ff);
	//map(0x00045002, 0x00045003).w(FUNC(att3b2_state::floppy_page_12bit_w));
	map(0x00048000, 0x0004800f).rw("dmac", FUNC(am9517a_device::read), FUNC(am9517a_device::write));
	map(0x00049000, 0x0004900f).rw("duart", FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0x0004a000, 0x0004a003).rw("fdc", FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	map(0x02000000, 0x0203ffff).mirror(0x1000000).ram();
}


static INPUT_PORTS_START(att3b2)
INPUT_PORTS_END

void att3b2_state::att3b2v2(machine_config &config)
{
	WE32100(config, m_maincpu, 10_MHz_XTAL); // special WE32102 XTAL runs at 1x or 2x speed
	m_maincpu->set_addrmap(AS_PROGRAM, &att3b2_state::mem_map_300);

	PIT8253(config, "pit"); // D8253C-5; unknown clocks

	AM9517A(config, "dmac", 5'000'000); // AM9517A-5DC; unknown clock

	SCN2681(config, "duart", 3'686'400); // MC2681P

	// TODO: hard disk controller (NEC D7261AD)

	WD2797(config, "fdc", 1'000'000); // TMS2797NL

	// TODO: RTC (MM58174AN)
}

void att3b2_state::att3b2v3(machine_config &config)
{
	att3b2v2(config);
	m_maincpu->set_clock(18'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &att3b2_state::mem_map_600);

	PIT8254(config.replace(), "pit"); // Intel 82C54

	FD1793(config.replace(), "fdc", 1'000'000); // FD 1793-02
}

ROM_START(3b2_300)
	ROM_REGION32_BE(0x8000, "bootstrap", 0)
	ROM_LOAD32_BYTE("3b2300-c.bin", 0x0000, 0x2000, CRC(b7f955c5) SHA1(54886c4fce5a5681af84538b65de1cc68d0f7af4))
	ROM_LOAD32_BYTE("3b2300-d.bin", 0x0001, 0x2000, CRC(5812e262) SHA1(5a69714c0c8f21d7655e43443dee0e76cf219403))
	ROM_LOAD32_BYTE("3b2300-e.bin", 0x0002, 0x2000, CRC(e28ca685) SHA1(a337a0480218db8c2d984442f7bc560834853152))
	ROM_LOAD32_BYTE("3b2300-f.bin", 0x0003, 0x2000, CRC(b8e138c4) SHA1(d2da4a7150150d0f9294814edb7ed357f9341858))
ROM_END

ROM_START(3b2_310)
	ROM_REGION32_BE(0x8000, "bootstrap", 0)
	ROM_LOAD32_BYTE("aayyc.bin", 0x0000, 0x2000, CRC(b7f955c5) SHA1(54886c4fce5a5681af84538b65de1cc68d0f7af4))
	ROM_LOAD32_BYTE("aayyd.bin", 0x0001, 0x2000, CRC(5812e262) SHA1(5a69714c0c8f21d7655e43443dee0e76cf219403))
	ROM_LOAD32_BYTE("aayye.bin", 0x0002, 0x2000, CRC(e28ca685) SHA1(a337a0480218db8c2d984442f7bc560834853152))
	ROM_LOAD32_BYTE("aayyf.bin", 0x0003, 0x2000, CRC(39dcfd8c) SHA1(2e662b3811f78ab689bc2687b73e3158f33f7f89))
ROM_END

ROM_START(3b2_400)
	ROM_REGION32_BE(0x8000, "bootstrap", 0)
	ROM_LOAD32_BYTE("3b2-aayyc.bin", 0x0000, 0x2000, CRC(b7f955c5) SHA1(54886c4fce5a5681af84538b65de1cc68d0f7af4))
	ROM_LOAD32_BYTE("3b2-aayyd.bin", 0x0001, 0x2000, CRC(5812e262) SHA1(5a69714c0c8f21d7655e43443dee0e76cf219403))
	ROM_LOAD32_BYTE("3b2-aayye.bin", 0x0002, 0x2000, CRC(e28ca685) SHA1(a337a0480218db8c2d984442f7bc560834853152))
	ROM_LOAD32_BYTE("3b2-aayyf-4.bin", 0x0003, 0x2000, CRC(85b8c5d3) SHA1(85bdf3f889f6c14cbf33ce81421f1f1d02328223))
ROM_END

ROM_START(3b2_600)
	ROM_REGION32_BE(0x20000, "bootstrap", 0)
	ROM_LOAD32_BYTE("abtry.bin", 0x0000, 0x8000, CRC(fa8d488b) SHA1(2e169f4171bd30aba1a9cd550a418c943eb78ceb))
	ROM_LOAD32_BYTE("abtrw.bin", 0x0001, 0x8000, CRC(8705cb68) SHA1(f41365cd0b4f90d8ad0335655b0ac04a7d14d6c6))
	ROM_LOAD32_BYTE("abtru.bin", 0x0002, 0x8000, CRC(ea9e127b) SHA1(6618998ead5a5e07ead8c572619a6bcf71d84497))
	ROM_LOAD32_BYTE("abtrt.bin", 0x0003, 0x8000, CRC(0f075161) SHA1(b67c9c4549dc789df33b5a38e4b35fe26fdfbea6))
ROM_END

} // anonymous namespace


COMP(1984, 3b2_300, 0,       0, att3b2v2, att3b2, att3b2_state, empty_init, "AT&T", "3B2/300", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP(1985, 3b2_310, 3b2_300, 0, att3b2v2, att3b2, att3b2_state, empty_init, "AT&T", "3B2/310", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP(1985, 3b2_400, 3b2_300, 0, att3b2v2, att3b2, att3b2_state, empty_init, "AT&T", "3B2/400", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP(1987, 3b2_600, 0,       0, att3b2v3, att3b2, att3b2_state, empty_init, "AT&T", "3B2/600", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



att4425.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************

    AT&T model 4425 text terminal with mosaic graphics character set
    Also known as Teletype model 56D.

    Skeleton driver.  This terminal is supported by netpbm -- pbmto4425(1)

    To do:
    - keyboard
    - everything else

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


#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/keyboard.h"
#include "machine/ram.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"

#include "emupal.h"
#include "screen.h"


namespace {

#define SCREEN_TAG          "screen"
#define Z80_TAG             "maincpu"
#define Z80CTC_TAG          "z80ctc"
#define Z80SIO_TAG          "z80sio"
#define RS232_A_TAG         "sioa"
#define RS232_B_TAG         "siob"
#define I8251_TAG           "i8251"


class att4425_state : public driver_device
{
public:
	att4425_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, Z80_TAG)
		, m_i8251(*this, I8251_TAG)
		, m_sio(*this, Z80SIO_TAG)
		, m_p_videoram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
		, m_screen(*this, SCREEN_TAG)
	{ }

	void att4425(machine_config &config);

private:
	void port10_w(uint8_t data);
	void port14_w(uint8_t data);
	uint8_t port14_r();
	uint8_t port15_r();

	void write_line_clock(int state);
	void write_keyboard_clock(int state);

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	virtual void machine_start() override ATTR_COLD;
	void att4425_io(address_map &map) ATTR_COLD;
	void att4425_mem(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_device<i8251_device> m_i8251;
	required_device<z80sio_device> m_sio;
	required_shared_ptr<uint8_t> m_p_videoram;
	required_region_ptr<u8> m_p_chargen;
	required_device<screen_device> m_screen;
};

/* Memory Maps */

void att4425_state::port10_w(uint8_t data)
{
	logerror("Writing %02X to port 10\n", data);
}

void att4425_state::port14_w(uint8_t data)
{
	logerror("Writing %02X to port 14\n", data);
}

uint8_t att4425_state::port14_r()
{
	// only complement of bit 0 used?
	return 0;
}

uint8_t att4425_state::port15_r()
{
	// status of something (at least bits 2 and 3 used)
	return 0;
}

void att4425_state::att4425_mem(address_map &map)
{
	map(0x0000, 0x7fff).rom().region(Z80_TAG, 0);
	map(0x8000, 0xffff).ram().share("videoram"); // c000..f7af?
}

void att4425_state::att4425_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x01).rw(m_i8251, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x10, 0x10).w(FUNC(att4425_state::port10_w));
	map(0x14, 0x14).rw(FUNC(att4425_state::port14_r), FUNC(att4425_state::port14_w));
	map(0x15, 0x15).r(FUNC(att4425_state::port15_r));
	map(0x18, 0x1b).rw(Z80CTC_TAG, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x1c, 0x1f).rw(m_sio, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
}

/* Input Ports */

static INPUT_PORTS_START( att4425 )
INPUT_PORTS_END

/* Video */

uint32_t att4425_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint16_t sy = 0;

	uint8_t fg = 2;
	uint8_t bg = 0;

	for (uint8_t y = 0; y < 27; y++)
	{
		uint16_t ma = 0x7e9c + 4 * (81 - 27 + y);
		ma = (m_p_videoram[ma] << 8) + m_p_videoram[ma + 1] - 0x8000;

		for (uint8_t ra = 0; ra < 13; ra++)
		{
			uint16_t *p = &bitmap.pix(sy++);

			for (uint16_t x = ma; x < ma + 160; x += 2)
			{
				uint8_t const chr = m_p_videoram[x + 1];
				uint8_t const attr = m_p_videoram[x];
				uint16_t ca = (chr << 4) & 0x7f0;
				uint8_t gfx;

				// font 2
				if (attr & 0x01)
					ca += 0x0800;

				// underline
				if (attr & 0x10 && ra == 12)
					gfx = 0xff;
				else
					gfx = m_p_chargen[ca | ra] ^ 255;

				// dim
				if (attr & 0x02)
					fg = 1;
				else
					fg = 2;

				// conceal
				if (attr & 0x04)
					fg = bg;

				// reverse video
				if (attr & 0x20)
					gfx ^= 255;

				/* Display a scanline of a character */
				for (int i = 7; i >= 0; i--)
					*p++ = BIT(gfx, i) ? fg : bg;

				*p++ = bg;
			}
		}
	}
	return 0;
}

static const gfx_layout att4425_charlayout =
{
	8, 13,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP8(0,1) },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, },
	16*8
};

static GFXDECODE_START( gfx_att4425 )
	GFXDECODE_ENTRY( "chargen", 0x0000, att4425_charlayout, 0, 1 )
GFXDECODE_END

/* Machine Initialization */

void att4425_state::machine_start()
{
}

/* Machine Driver */

void att4425_state::write_line_clock(int state)
{
	m_sio->rxca_w(state);
	m_sio->txca_w(state);
	m_sio->rxtxcb_w(state);
}

void att4425_state::write_keyboard_clock(int state)
{
	m_i8251->write_txc(state);
	m_i8251->write_rxc(state);
}

static const z80_daisy_config att4425_daisy_chain[] =
{
	{ Z80SIO_TAG },
	{ Z80CTC_TAG },
	{ nullptr }
};

void att4425_state::att4425(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(32'000'000) / 8);
	m_maincpu->set_addrmap(AS_PROGRAM, &att4425_state::att4425_mem);
	m_maincpu->set_addrmap(AS_IO, &att4425_state::att4425_io);
	m_maincpu->set_daisy_config(att4425_daisy_chain);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER, rgb_t::green());
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_screen_update(FUNC(att4425_state::screen_update));
	m_screen->set_palette("palette");
	m_screen->set_size(720, 351);
	m_screen->set_visarea(0, 720 - 1, 0, 351 - 1);
	GFXDECODE(config, "gfxdecode", "palette", gfx_att4425);
	PALETTE(config, "palette", palette_device::MONOCHROME_HIGHLIGHT);

	// ch.3 -- timer?
	z80ctc_device &ctc(Z80CTC(config, Z80CTC_TAG, XTAL(32'000'000)));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
#ifdef notdef
	ctc.zc_callback<0>().set(m_sio, FUNC(z80sio_device::rxca_w));
	ctc.zc_callback<0>().append(m_sio, FUNC(z80sio_device::txca_w));
	ctc.zc_callback<2>().set(m_sio, FUNC(z80sio_device::rxtxcb_w));
#endif

	Z80SIO(config, m_sio, 4800);
	m_sio->out_int_callback().set_inputline(Z80_TAG, INPUT_LINE_IRQ0);
	m_sio->out_txda_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_txd));
	m_sio->out_dtra_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_dtr));
	m_sio->out_rtsa_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_rts));
	m_sio->out_txdb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_txd));

	// host
	rs232_port_device &rs232a(RS232_PORT(config, RS232_A_TAG, default_rs232_devices, "null_modem"));
	rs232a.rxd_handler().set(m_sio, FUNC(z80sio_device::rxa_w));
	rs232a.dcd_handler().set(m_sio, FUNC(z80sio_device::dcda_w));
	rs232a.cts_handler().set(m_sio, FUNC(z80sio_device::ctsa_w));

	// aux printer?
	rs232_port_device &rs232b(RS232_PORT(config, RS232_B_TAG, default_rs232_devices, "printer"));
	rs232b.rxd_handler().set(m_sio, FUNC(z80sio_device::rxb_w));

	clock_device &line_clock(CLOCK(config, "line_clock", 9600 * 64));
	line_clock.signal_handler().set(FUNC(att4425_state::write_line_clock));

	I8251(config, m_i8251, 0);
	m_i8251->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_i8251->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	m_i8251->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "keyboard"));
	rs232.rxd_handler().set(m_i8251, FUNC(i8251_device::write_rxd));
	rs232.cts_handler().set(m_i8251, FUNC(i8251_device::write_cts));
	rs232.dsr_handler().set(m_i8251, FUNC(i8251_device::write_dsr));

	clock_device &keyboard_clock(CLOCK(config, "keyboard_clock", 4800 * 64));
	keyboard_clock.signal_handler().set(FUNC(att4425_state::write_keyboard_clock));

	RAM(config, RAM_TAG).set_default_size("32K").set_default_value(0);
}

/* ROMs */

ROM_START( att4425 )
	ROM_REGION( 0x8000, Z80_TAG, 0 )
	ROM_LOAD( "455773-1.bin", 0x0000, 0x2000, CRC(d216515b) SHA1(6e098c35f8fe6be4f28a577a43145c92972041b1) )
	ROM_LOAD( "455774-1.bin", 0x2000, 0x2000, CRC(636c069a) SHA1(c00648eae44c574b983de6a0ba6cf74a7f07b098) )
	ROM_LOAD( "456305-1.bin", 0x4000, 0x2000, CRC(43cbf638) SHA1(939569e65957370ab8e60d4f90179373b72b9573) )
	ROM_LOAD( "456306-1.bin", 0x6000, 0x2000, CRC(e4f2b0f1) SHA1(c80c2b7219b313b4924834b0a9d1d42536d1ae63) )

	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "char.bin", 0x0000, 0x2000, CRC(cca962cc) SHA1(201d97b954f782ceae8d17a08fb9a1c4d5ae7a58) )
ROM_END

} // anonymous namespace


/* System Drivers */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY  FULLNAME              FLAGS
COMP( 1983, att4425, 0,      0,      att4425, att4425, att4425_state, empty_init, "AT&T",  "AT&T Teletype 4425", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



att610.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for AT&T 610 Business Communication Terminal (BCT).

    The video ASIC is a 68-pin PLCC marked:

        (M) AT&T
        1006B2*
        456555
        18289S 71

    Another custom IC (28-pin DIP) is next to the character generator:

           WE
         492A*
        18186 71

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

#include "emu.h"

//#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/mc68681.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "emupal.h"
#include "screen.h"


namespace {

class att610_state : public driver_device
{
public:
	att610_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_sio(*this, "sio")
		, m_screen(*this, "screen")
		, m_rom(*this, "rom")
	{
	}

	void att610(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void cart_select_w(u8 data);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_device<z80sio_device> m_sio;
	required_device<screen_device> m_screen;
	required_memory_bank m_rom;
};

void att610_state::machine_start()
{
	m_rom->configure_entry(0, memregion("firmware")->base());
	m_rom->configure_entry(1, memregion("cartridge")->base());

	m_sio->ctsb_w(0);
}

void att610_state::machine_reset()
{
	m_rom->set_entry(0);
}

u32 att610_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

static const gfx_layout char_layout =
{
	8, 13,
	RGN_FRAC(1, 1),
	1,
	{ 0 },
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8 },
	8 * 16
};

static GFXDECODE_START(chars)
	GFXDECODE_ENTRY("chargen", 0, char_layout, 0, 1)
GFXDECODE_END

void att610_state::cart_select_w(u8 data)
{
	m_rom->set_entry(BIT(data, 0));
}

void att610_state::mem_map(address_map &map)
{
	map(0x0000, 0x5fff).bankr("rom");
	map(0xc000, 0xffff).ram();
}

void att610_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).rw("sio", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
	map(0x10, 0x10).w(FUNC(att610_state::cart_select_w));
	map(0x60, 0x63).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x70, 0x7f).rw("duart", FUNC(scn2681_device::read), FUNC(scn2681_device::write));
}


static INPUT_PORTS_START(att610)
	// TODO: supports 103-key (56K430/ACZ) and 98-key (56K420/ADA) keyboards
INPUT_PORTS_END

static const z80_daisy_config daisy_chain[] =
{
	{ "sio" },
	{ "ctc" },
	{ nullptr }
};

void att610_state::att610(machine_config &config)
{
	Z80(config, m_maincpu, 27.72_MHz_XTAL / 7); // MK3880N-4; CPU clock guessed
	m_maincpu->set_addrmap(AS_PROGRAM, &att610_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &att610_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	z80ctc_device &ctc(Z80CTC(config, "ctc", 27.72_MHz_XTAL / 7)); // Z8430APS
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80SIO(config, m_sio, 27.72_MHz_XTAL / 7); // Z8441APS (SIO/1)
	m_sio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	scn2681_device &duart(SCN2681(config, "duart", 3'686'400)); // MC2681P (adjacent XTAL not legible)
	duart.irq_cb().set("sio", FUNC(z80sio_device::syncb_w)).invert();
	duart.outport_cb().set("sio", FUNC(z80sio_device::rxcb_w)).bit(3);
	duart.outport_cb().append("sio", FUNC(z80sio_device::txcb_w)).bit(3);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER, rgb_t::green());
	m_screen->set_raw(21.6675_MHz_XTAL, 963, 0, 720, 375, 0, 351);
	//m_screen->set_raw(27.72_MHz_XTAL, 1232, 0, 924, 375, 0, 351);
	m_screen->set_screen_update(FUNC(att610_state::screen_update));
	m_screen->screen_vblank().set("ctc", FUNC(z80ctc_device::trg0));

	PALETTE(config, "palette", palette_device::MONOCHROME);

	GFXDECODE(config, "gfxdecode", "palette", chars);
}

ROM_START(att610)
	ROM_REGION(0x6000, "firmware", 0)
	ROM_LOAD("455798-1.d4", 0x0000, 0x4000, CRC(91bd636f) SHA1(53bc886ba580dd64446ebe9b8a042414ff8834d6)) // MBM27128-25
	ROM_LOAD("455799-1.b4", 0x4000, 0x2000, CRC(7fd75ee0) SHA1(597b23c43b3f283b49b51b9dee60109ff683b041)) // MBM2764-25

	ROM_REGION(0x6000, "cartridge", ROMREGION_ERASE00) // optional

	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD("att-tc85_456309-1.h7", 0x0000, 0x2000, CRC(d313e022) SHA1(a24df1d8d8c55413e4cdb0734783c0fa244bdf00)) // HN27C64G-15
ROM_END

ROM_START( att615 )
	ROM_REGION(0x10000, "firmware", 0)
	ROM_LOAD("523481059.bin", 0x00000, 0x10000, CRC(9649bf59) SHA1(bed7900f8848a98b04ba69492c3bc3727d30d67d))

	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD("457756-2.bin", 0x0000, 0x2000, CRC(85871007) SHA1(018ea318ea9668e65043bc7c52c5b6dd3ed68687))

	ROM_REGION(0x6000, "cartridge", 0)
	ROM_LOAD("523458727.bin", 0x0000, 0x4000, CRC(beea70fd) SHA1(7b7a50832e44e5ece85a0ef83d40599f9fff5692))
	ROM_LOAD("523458735.bin", 0x4000, 0x2000, CRC(7f1b9d63) SHA1(0e07c435e7f6bef153e4b1f0d1d9d391a7562602))

	ROM_REGION(0x1000, "keyboard", 0)
	ROM_LOAD("20417-21_8031.bin", 0x0000, 0x1000, CRC(c48b4a2a) SHA1(f01dadc0239f81a58d50ae077f5cb3a029bad110))
ROM_END

} // anonymous namespace


COMP(1986, att610, 0, 0, att610, att610, att610_state, empty_init, "AT&T", "610 Business Communication Terminal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP(1987, att615, 0, 0, att610, att610, att610_state, empty_init, "AT&T", "615 MT", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



att630.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************************

Skeleton driver for AT&T 630 MTG terminal.

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

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "machine/mc68681.h"
#include "machine/nvram.h"
#include "screen.h"


namespace {

class att630_state : public driver_device
{
public:
	att630_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_duart2(*this, "duart2")
		, m_vram(*this, "vram")
		, m_buttons(*this, "BUTTONS")
		, m_bram_data(*this, "bram", 0x2000, ENDIANNESS_BIG)
	{ }

	void att630(machine_config &config);
	void att730x(machine_config &config);

	INPUT_CHANGED_MEMBER(mouse_moved);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	u8 buttons_r();
	u8 bram_r(offs_t offset);
	void bram_w(offs_t offset, u8 data);

	void att630_map(address_map &map) ATTR_COLD;
	void att730_map(address_map &map) ATTR_COLD;

	required_device<m68000_device> m_maincpu;
	required_device<scn2681_device> m_duart2;
	required_shared_ptr<u16> m_vram;
	required_ioport m_buttons;
	memory_share_creator<u8> m_bram_data;
};

void att630_state::machine_start()
{
}

u32 att630_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	for (int y = 0; y < 1024; y++)
		for (int x = 0; x < 1024; x++)
			bitmap.pix(y, x) = BIT(m_vram[y * (1024 / 16) + x / 16], 15 - (x % 16)) ? rgb_t::white() : rgb_t::black();

	return 0;
}

INPUT_CHANGED_MEMBER(att630_state::mouse_moved)
{
	m_duart2->ip2_w(0);
	m_duart2->ip2_w(1);
}

u8 att630_state::buttons_r()
{
	if (!machine().side_effects_disabled())
		m_maincpu->set_input_line(M68K_IRQ_1, CLEAR_LINE);
	return m_buttons->read();
}

u8 att630_state::bram_r(offs_t offset)
{
	return m_bram_data[offset];
}

void att630_state::bram_w(offs_t offset, u8 data)
{
	m_bram_data[offset] = data;
}

void att630_state::att630_map(address_map &map)
{
	map(0x000000, 0x03ffff).rom().region("maincpu", 0);
	map(0x040000, 0x0fffff).noprw(); // additional space for rom
	map(0x100000, 0x1fffff).noprw(); // cartridge space
	map(0x200000, 0x20001f).rw("duart1", FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask16(0x00ff);
	map(0x200020, 0x20003f).rw("duart2", FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask16(0x00ff);
	map(0x200040, 0x200041).portr("MOUSEX");
	map(0x200042, 0x200043).portr("MOUSEY");
	map(0x200045, 0x200045).r(FUNC(att630_state::buttons_r));
	map(0x400000, 0x6fffff).noprw(); // expansion i/o card
//  map(0x700000, 0x75ffff).noprw(); // video controller
	map(0x760000, 0x77ffff).ram().share("vram");
	map(0x780000, 0x7fffff).ram(); // program ram
	map(0xe00000, 0xe03fff).mirror(0x1fc000).rw(FUNC(att630_state::bram_r), FUNC(att630_state::bram_w)).umask16(0x00ff);
}

void att630_state::att730_map(address_map &map)
{
	att630_map(map);
	map(0x040000, 0x05ffff).rom().region("maincpu", 0x40000);
	map(0x100000, 0x15ffff).rom().region("cart", 0);
	map(0x800000, 0x87ffff).ram(); // expansion RAM
	map(0xde0000, 0xdfffff).rom().region("starlan", 0);
}

static INPUT_PORTS_START( att630 )
	PORT_START("MOUSEX")
	PORT_BIT(0x0fff, 0x0000, IPT_MOUSE_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(att630_state::mouse_moved), 0)
	PORT_BIT(0xf000, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("MOUSEY")
	PORT_BIT(0x0fff, 0x0000, IPT_MOUSE_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(att630_state::mouse_moved), 0)
	PORT_BIT(0xf000, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("BUTTONS")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON3)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON1)
	PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

void att630_state::att630(machine_config &config)
{
	M68000(config, m_maincpu, 40_MHz_XTAL / 4); // clock not confirmed
	m_maincpu->set_addrmap(AS_PROGRAM, &att630_state::att630_map);
	m_maincpu->set_cpu_space(AS_PROGRAM); // vectors are in BRAM

	NVRAM(config, "bram", nvram_device::DEFAULT_ALL_0); // 5264 + battery

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(87.18336_MHz_XTAL, 1376, 0, 1024, 1056, 0, 1024);
	screen.set_color(rgb_t::amber());
	screen.set_screen_update(FUNC(att630_state::screen_update));
	screen.screen_vblank().set_inputline(m_maincpu, M68K_IRQ_1, ASSERT_LINE);

	scn2681_device &duart1(SCN2681(config, "duart1", 3.6864_MHz_XTAL));
	duart1.irq_cb().set_inputline(m_maincpu, M68K_IRQ_3);

	scn2681_device &duart2(SCN2681(config, "duart2", 3.6864_MHz_XTAL));
	duart2.irq_cb().set_inputline(m_maincpu, M68K_IRQ_2);
}

void att630_state::att730x(machine_config &config)
{
	att630(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &att630_state::att730_map);
}


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

AT&T 630 MTG.
Chips: 2x SCN2681A, AT&T 492F proprietory, blank chip, MC68000P10, MB113F316 (square), MB113F316 (DIL), PAL16R4ACN
Crystals: 40MHz, 87.18336, 3.6864? (hard to read)

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

ROM_START( att630 )
	ROM_REGION(0x40000, "maincpu", 0)
	ROM_LOAD16_BYTE( "460621-1.bin", 0x00000, 0x10000, CRC(136749cd) SHA1(15378c292ddc7384cc69a35de55b69257a9f2a1c) )
	ROM_LOAD16_BYTE( "460620-1.bin", 0x00001, 0x10000, CRC(27ab77f0) SHA1(5ff1d9ee5a69dee308d62c447ee67e1888afab0e) )
	ROM_LOAD16_BYTE( "460623-1.bin", 0x20000, 0x10000, CRC(aeae12fb) SHA1(fa3ce26e4622875aa1dea7cf1bd1df237010ff2b) )
	ROM_LOAD16_BYTE( "460622-1.bin", 0x20001, 0x10000, CRC(c108c1e0) SHA1(ef01349e890b8a4117c01e78d1c23fbd113ba58f) )
ROM_END

ROM_START( att730x )
	ROM_REGION(0x60000, "maincpu", 0) // D27512-200V10 EPROMs "© AT&T 1990"
	ROM_LOAD16_BYTE( "106219637_mln3.bin", 0x00000, 0x10000, CRC(8bb2f5f9) SHA1(e7a50b8da4dde5479c8184f89f59daf511989377) )
	ROM_LOAD16_BYTE( "106219645_mln2.bin", 0x00001, 0x10000, CRC(5663da5b) SHA1(a572381a4ca73c3ddac2eff461603c8b46949687) )
	ROM_LOAD16_BYTE( "106219652_mlq3.bin", 0x20000, 0x10000, CRC(e0f88160) SHA1(2def2bb1ad11eb976bfbab25ffa536fd14eb75ca) )
	ROM_LOAD16_BYTE( "106219660_mlq2.bin", 0x20001, 0x10000, CRC(66cdd13c) SHA1(902f653ef88c4f4d93701f0b578a45618f6caa63) )
	ROM_LOAD16_BYTE( "106219678_mls3.bin", 0x40000, 0x10000, CRC(675b8d6f) SHA1(748f42021ff9655dbc4670ac412444171e7c0e95) )
	ROM_LOAD16_BYTE( "106219686_mls2.bin", 0x40001, 0x10000, CRC(87155cfd) SHA1(2e7a2a654193979330a4cadbf4a8f00617a73c05) )

	// 730X CARTRIDGE PC Code 33503 © 1989 AT&T ISSUE 1 MADE IN USA
	ROM_REGION16_BE(0x60000, "cart", 0) // D27512-200V05 EPROMs "© AT&T-1989"
	ROM_LOAD16_BYTE( "846481877.b1", 0x00000, 0x10000, CRC(5797e737) SHA1(0406add8373ab1b8c00511fd038b6548fa431d0a) )
	ROM_LOAD16_BYTE( "846481828.a1", 0x00001, 0x10000, CRC(d58e9ad6) SHA1(787a5011778be470b01036d2053945bb330da948) )
	ROM_LOAD16_BYTE( "846481844.b2", 0x20000, 0x10000, CRC(4a426790) SHA1(33209daa61255fe9b7f010e337f8de1e9446ee2b) )
	ROM_LOAD16_BYTE( "846481851.a2", 0x20001, 0x10000, CRC(57e0d7bd) SHA1(7b664c26ed9e9496d229a9907c9340e298934aba) )
	ROM_LOAD16_BYTE( "846481885.b3", 0x40000, 0x10000, CRC(eb4b0c4c) SHA1(0a520485be72992a45d5113ac053db7234adf609) )
	ROM_LOAD16_BYTE( "846481836.a3", 0x40001, 0x10000, CRC(8becf605) SHA1(8b5820d8b931a235e0c0a375973c461e14d55fed) )

	ROM_REGION16_BE(0x20000, "starlan", 0) // D27512-200V05 EPROMs "© AT&T 1990"
	ROM_LOAD16_BYTE( "846541910_abf1_001.d10", 0x00000, 0x10000, CRC(3fce193e) SHA1(e5f9704e9d06f97700ccf6f88fc935efbcd0cd4e) )
	ROM_LOAD16_BYTE( "846541902_6d82_000.b10", 0x00001, 0x10000, CRC(dcf9165c) SHA1(3f264193228bbf935a2699e64bbff08ceb1c2164) )
ROM_END

} // anonymous namespace


COMP( 1986, att630, 0, 0, att630, att630, att630_state, empty_init, "AT&T", "630 MTG", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1990, att730x, 0, 0, att730x, att630, att630_state, empty_init, "AT&T", "730X", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



attache.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Barry Rodewald
/*
 * attache.c
 *
 *  Created on: 17/05/2013
 *
 *  Driver by Barry Rodewald
 *
 *
 *  Otrona Attache
 *
 *  CPU: Zilog Z80-A, 4MHz
 *  RAM: 64kB
 *  DMA: AMD 9517A (or compatible)
 *  RTC: Oki MSM5832, Z80-PIO
 *  Sound: GI AY-3-8912
 *  FDC: NEC D765A, 5.25" floppies
 *  Video: CRT5027, 320x240
 *  Serial: Z80-SIO, two RS-232C or RS-422/423 ports
 *
 *  Note:
 *  In terminal mode (when disk booting fails or no disk is inserted), press Ctrl+Linefeed (ctrl+pgdn by default)
 *  to enter monitor mode.  From here you can run a bunch of diagnostic tests.
 *
 *  G - Display Test Pattern
 *  H - Display RAM Test
 *  nnI - Input Test  (nn = port number)
 *  J - Jump
 *  K - Keyboard Test
 *  L - Loop Tests
 *  M - Map Test
 *  nnmmO - Output Test (nn = port number, mm = data to send)
 *  P - Format Diskette (P to format disk in Drive A, 1P for Drive B)
 *  Q - CMOS RAM Test
 *  nR - Main RAM Test (n = 16kB bank to test [0-3])
 *  bbpcS - Select Output Ports (first b = printer baud rate, second b = comm baud rate, p = printer port, c = comm port)
 *  T - Real Time Clock Test
 *  U - United Tests
 *  cchsV - Read a sector from disk (cc = cylinder, h = head [bit 0=drive, bit 2=side], s = sector)
 *  cchsW - Write a sector from disk
 *  nnnnmmmmX - I/O port transmit (nnnn = number of bytes to transmit, mmmm = start of data to transmit)
 *  nnnnY - I/O port receive (nnnn = address of data loaded)
 *  Z - Auto Disk Test (1Z for drive B)
 *
 *  For the 8086 board (will display an 'x' if the 8086 board is not installed):
 *  [ - 8086 RAM Test
 *  ] - SCC Test
 *  ( - GPIB Listener/Talker Test
 *  ) - GPIB Controller Test
 *
 *  The Attache 8:16 is an upgraded Attache adding an 8086 (+ optional 8087) board with its own 256kB of RAM,
 *  and optionally a GPIB controller (TMS9914A) and serial synchronous port (Z8530 SCC).  It also has modifications
 *  to the main Z80 board, specifically the display circuitry, adding a high-resolution display, and replacing
 *  the character ROM with a larger ROM containing an IBM character set.
 *  It effectively allows the Attache to run MS-DOS and use a 10MB hard disk.
 *
 *  TODO:
 *    - Keyboard repeat
 *    - Get at least some of the system tests to pass
 *    - and probably lots more I've forgotten, too.
 *    - improve Z80-8086 comms on the 8:16, saving a file to the RAM disk under CP/M often ends in deadlock.
 *    - add Z8530 SCC and TMS9914A GPIB to the 8:16.  These are optional devices, so aren't strictly required at this stage.
 *    - connect dma and sio (channel 3)
 */

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/z80daisy.h"
#include "bus/rs232/rs232.h"
#include "imagedev/floppy.h"
#include "machine/am9517a.h"
#include "machine/msm5832.h"
#include "machine/nvram.h"
#include "machine/ram.h"
#include "machine/upd765.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "machine/z80pio.h"
#include "machine/i8255.h"
#include "cpu/i86/i86.h"
#include "sound/ay8910.h"
#include "video/tms9927.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class attache_state : public driver_device
{
public:
	attache_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "boot")
		, m_ram(*this, RAM_TAG)
		, m_char_rom(*this, "video")
		, m_rtc(*this, "rtc")
		, m_psg(*this, "psg")
		, m_fdc(*this, "fdc")
		, m_sio(*this, "sio")
		, m_pio(*this, "pio")
		, m_ctc(*this, "ctc")
		, m_crtc(*this, "crtc")
		, m_dma(*this, "dma")
		, m_palette(*this, "palette")
		, m_floppy0(*this, "fdc:0:525dd")
		, m_floppy1(*this, "fdc:1:525dd")
		, m_kb_rows(*this, "row%u", 0U)
		, m_kb_mod(*this, "modifiers")
		, m_membank1(*this, "bank1")
		, m_membank2(*this, "bank2")
		, m_membank3(*this, "bank3")
		, m_membank4(*this, "bank4")
		, m_membank5(*this, "bank5")
		, m_membank6(*this, "bank6")
		, m_membank7(*this, "bank7")
		, m_membank8(*this, "bank8")
		, m_nvram(*this, "nvram")
		, m_rom_active(true)
		, m_gfx_enabled(false)
		, m_kb_clock(true)
		, m_kb_empty(true)
	{ }

	void attache(machine_config &config);

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	uint8_t pio_portA_r();
	uint8_t pio_portB_r();
	void pio_portA_w(uint8_t data);
	void pio_portB_w(uint8_t data);

	uint8_t dma_mem_r(offs_t offset);
	void dma_mem_w(offs_t offset, uint8_t data);

	void hreq_w(int state);
	void eop_w(int state);
	[[maybe_unused]] void fdc_dack_w(int state);

protected:
	// PIO port B operation select
	enum
	{
		PIO_SEL_8910_ADDR = 0,
		PIO_SEL_8910_DATA,
		PIO_SEL_5832_READ,
		PIO_SEL_5832_WRITE,
		PIO_SEL_5101_WRITE,
		PIO_SEL_5101_READ,
		PIO_SEL_LATCH,
		PIO_SEL_NOP
	};

	// Display controller operation select
	enum
	{
		DISP_GFX_0 = 0,
		DISP_GFX_1,
		DISP_GFX_2,
		DISP_GFX_3,
		DISP_GFX_4,
		DISP_CRTC,
		DISP_ATTR,
		DISP_CHAR
	};

	// overrides
	virtual void driver_start() override;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint8_t rom_r(offs_t offset);
	void rom_w(offs_t offset, uint8_t data);

	void display_command_w(uint8_t data);
	uint8_t display_data_r(offs_t offset);
	void display_data_w(offs_t offset, uint8_t data);
	uint8_t dma_mask_r();
	void dma_mask_w(uint8_t data);

	uint8_t memmap_r();
	void memmap_w(uint8_t data);

	void set_latch(uint8_t data);
	void operation_strobe(uint8_t data);
	void keyboard_clock_w(bool state);
	uint8_t keyboard_data_r();
	uint16_t get_key();

	void attache_io(address_map &map) ATTR_COLD;
	void attache_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_memory_region m_rom;
	required_device<ram_device> m_ram;
	required_memory_region m_char_rom;
	required_device<msm5832_device> m_rtc;
	required_device<ay8912_device> m_psg;
	required_device<upd765a_device> m_fdc;
	required_device<z80sio_device> m_sio;
	required_device<z80pio_device> m_pio;
	required_device<z80ctc_device> m_ctc;
	required_device<tms9927_device> m_crtc;
	required_device<am9517a_device> m_dma;
	required_device<palette_device> m_palette;
	required_device<floppy_image_device> m_floppy0;
	required_device<floppy_image_device> m_floppy1;
	required_ioport_array<8> m_kb_rows;
	required_ioport m_kb_mod;
	required_memory_bank m_membank1;
	required_memory_bank m_membank2;
	required_memory_bank m_membank3;
	required_memory_bank m_membank4;
	required_memory_bank m_membank5;
	required_memory_bank m_membank6;
	required_memory_bank m_membank7;
	required_memory_bank m_membank8;
	required_device<nvram_device> m_nvram;

	bool m_rom_active;
	bool m_gfx_enabled;
	uint8_t m_pio_porta;
	uint8_t m_pio_portb;
	uint8_t m_pio_select;
	uint8_t m_pio_latch;
	uint8_t m_crtc_reg_select;
	uint8_t m_current_cmd;
	uint8_t m_char_ram[128*32];
	uint8_t m_attr_ram[128*32];
	uint8_t m_gfx_ram[128*32*5];
	uint8_t m_char_line;
	uint8_t m_attr_line;
	uint8_t m_gfx_line;
	uint8_t m_cmos_ram[64];
	uint8_t m_cmos_select;
	uint16_t m_kb_current_key;
	bool m_kb_clock;
	bool m_kb_empty;
	uint8_t m_kb_bitpos;
	uint8_t m_memmap;
};

class attache816_state : public attache_state
{
public:
	attache816_state(const machine_config &mconfig, device_type type, const char *tag)
		: attache_state(mconfig, type, tag)
		, m_extcpu(*this,"extcpu")
		, m_ppi(*this,"ppi")
		, m_comms_val(0)
		, m_x86_irq_enable(0)
		, m_z80_rx_ready(false)
		, m_z80_tx_ready(false)
	{ }

	void attache816(machine_config &config);

private:
	void x86_comms_w(uint8_t data);
	uint8_t x86_comms_r();
	void x86_irq_enable(uint8_t data);
	void x86_iobf_enable_w(offs_t offset, uint8_t data);
	uint8_t z80_comms_r();
	void z80_comms_w(uint8_t data);
	uint8_t z80_comms_status_r();
	void z80_comms_ctrl_w(uint8_t data);
	void ppi_irq(int state);
	void x86_dsr(int state);

	virtual void machine_reset() override ATTR_COLD;

	[[maybe_unused]] void attache816_io(address_map &map) ATTR_COLD;
	void attache_x86_io(address_map &map) ATTR_COLD;
	void attache_x86_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_extcpu;
	required_device<i8255_device> m_ppi;

	uint8_t m_comms_val;
	uint8_t m_x86_irq_enable;
	bool m_z80_rx_ready;
	bool m_z80_tx_ready;
};

// Attributes (based on schematics):
// bit 0 = ALT
// bit 1 = RW
// bit 2 = BKG (reverse?)
// bit 3 = brightness
// bit 4 = double-size (width)
// bit 5 = underline
// bit 6 = superscript
// bit 7 = subscript (superscript and subscript combined produces strikethrough)
uint32_t attache_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	// Graphics output (if enabled)
	if(m_gfx_enabled)
	{
		pen_t const *const pen = m_palette->pens();

		for(uint8_t y=0;y<(bitmap.height()-1)/10;y++)
		{
			for(uint8_t x=0;x<(bitmap.width()-1)/8;x++)
			{
				// graphics pixels use half the clock of text, so 4 graphics pixels per character
				for(uint8_t scan=0;scan<10;scan+=2)
				{
					uint8_t const data = m_gfx_ram[(128*32*(scan/2))+(y*128+x)];
					bitmap.pix(y*10+scan,x*8)   = pen[BIT(data,7)];
					bitmap.pix(y*10+scan,x*8+1) = pen[BIT(data,7)];
					bitmap.pix(y*10+scan,x*8+2) = pen[BIT(data,6)];
					bitmap.pix(y*10+scan,x*8+3) = pen[BIT(data,6)];
					bitmap.pix(y*10+scan,x*8+4) = pen[BIT(data,5)];
					bitmap.pix(y*10+scan,x*8+5) = pen[BIT(data,5)];
					bitmap.pix(y*10+scan,x*8+6) = pen[BIT(data,4)];
					bitmap.pix(y*10+scan,x*8+7) = pen[BIT(data,4)];
					bitmap.pix(y*10+scan+1,x*8)   = pen[BIT(data,3)];
					bitmap.pix(y*10+scan+1,x*8+1) = pen[BIT(data,3)];
					bitmap.pix(y*10+scan+1,x*8+2) = pen[BIT(data,2)];
					bitmap.pix(y*10+scan+1,x*8+3) = pen[BIT(data,2)];
					bitmap.pix(y*10+scan+1,x*8+4) = pen[BIT(data,1)];
					bitmap.pix(y*10+scan+1,x*8+5) = pen[BIT(data,1)];
					bitmap.pix(y*10+scan+1,x*8+6) = pen[BIT(data,0)];
					bitmap.pix(y*10+scan+1,x*8+7) = pen[BIT(data,0)];
				}
			}
		}
	}
	else
		bitmap.fill(0);

	// Text output
	uint8_t dbl_mode = 0;  // detemines which half of character to display when using double size attribute,
							// as it can start on either odd or even character cells.
	for(uint8_t y=0;y<(bitmap.height()-1)/10;y++)  // lines
	{
		uint8_t const start = m_crtc->upscroll_offset();
		uint8_t const vy = (start + y) % 24;

		for(uint8_t x=0;x<(bitmap.width()-1)/8;x++)  // columns
		{
			assert(((y*128)+x) >= 0 && ((y*128)+x) < std::size(m_char_ram));
			assert(((vy*128)+x) >= 0 && ((vy*128)+x) < std::size(m_char_ram));
			uint8_t ch = m_char_ram[(vy*128)+x];
			pen_t fg = m_palette->pen(m_attr_ram[(vy*128)+x] & 0x08 ? 2 : 1); // brightness
			if(m_attr_ram[(vy*128)+x] & 0x10) // double-size
				dbl_mode++;
			else
				dbl_mode = 0;

			for(uint8_t scan=0;scan<10;scan++)  // 10 scanlines per line
			{
				uint8_t data = m_char_rom->base()[(ch*16+scan)];
				if((m_attr_ram[(vy*128)+x] & 0xc0) != 0xc0)  // if not strikethrough
				{
					if(m_attr_ram[(vy*128)+x] & 0x40)  // superscript
					{
						if(scan >= 5)
							data = 0;
						else
							data = m_char_rom->base()[ch*16+(scan*2)+1];
					}
					if(m_attr_ram[(vy*128)+x] & 0x80)  // subscript
					{
						if(scan < 5)
							data = 0;
						else
							data = m_char_rom->base()[ch*16+((scan-5)*2)+1];
					}
				}
				if((m_attr_ram[(vy*128)+x] & 0x20) && scan == 9)  // underline
					data = 0xff;
				if((m_attr_ram[(vy*128)+x] & 0xc0) == 0xc0 && scan == 3)  // strikethrough
					data = 0xff;
				if(m_attr_ram[(vy*128)+x] & 0x04)  // reverse
					data = ~data;
				if(m_attr_ram[(vy*128)+x] & 0x10) // double-size
				{
					uint8_t newdata = 0;
					if(dbl_mode & 1)
					{
						newdata = (data & 0x80) | ((data & 0x80) >> 1)
								| ((data & 0x40) >> 1) | ((data & 0x40) >> 2)
								| ((data & 0x20) >> 2) | ((data & 0x20) >> 3)
								| ((data & 0x10) >> 3) | ((data & 0x10) >> 4);
					}
					else
					{
						newdata = ((data & 0x08) << 4) | ((data & 0x08) << 3)
								| ((data & 0x04) << 3) | ((data & 0x04) << 2)
								| ((data & 0x02) << 2) | ((data & 0x02) << 1)
								| ((data & 0x01) << 1) | (data & 0x01);
					}
					data = newdata;
				}

				for(uint8_t bit=0;bit<8;bit++)  // 8 pixels per character
				{
					uint16_t const xpos = x*8+bit;
					uint16_t const ypos = y*10+scan;

					if(BIT(data,7-bit))
						bitmap.pix(ypos,xpos) = fg;
				}
			}
		}
	}
	return 0;
}

uint8_t attache_state::rom_r(offs_t offset)
{
	if(m_rom_active)
		return m_rom->base()[offset];
	else
		return m_ram->pointer()[m_membank1->entry()*0x2000 + offset];
}

void attache_state::rom_w(offs_t offset, uint8_t data)
{
	m_ram->pointer()[m_membank1->entry()*0x2000 + offset] = data;
}

uint16_t attache_state::get_key()
{
	uint8_t row,bits,data;
	uint8_t res = 0;

	// scan input ports
	for(row=0;row<8;row++)
	{
		data = m_kb_rows[row]->read();
		for(bits=0;bits<8;bits++)
		{
			if(BIT(data,bits))
			{
				res = bits & 0x07;
				res |= ((row & 0x07) << 3);
				m_kb_empty = false;
				data = m_kb_mod->read();
				if(~data & 0x01)
					res |= 0x80;  // shift
				if(data & 0x02)
					res |= 0x40;  // ctrl
				//logerror("KB: hit row %i, bit %i\n",row,bits);
				return res;
			}
		}
	}
	// no key pressed
	m_kb_empty = true;
	return res;
}

uint8_t attache_state::keyboard_data_r()
{
	uint16_t key;
	if(m_kb_bitpos == 1)  // start bit, if data is available
	{
		key = get_key();
		if(m_kb_current_key != key)
			m_kb_current_key = key;
		else
			return 0x00;
		//logerror("KB: bit position %i, key %02x, empty %i\n",m_kb_bitpos,m_kb_current_key,m_kb_empty);
		if(m_kb_empty)
			return 0x00;
		else
			return 0x40;
	}
	else
	{
		//logerror("KB: bit position %i, key %02x, empty %i\n",m_kb_bitpos,m_kb_current_key,m_kb_empty);
		if(m_kb_current_key & (1<<(m_kb_bitpos-2)))
			return 0x00;
		else
			return 0x40;
	}
}

void attache_state::keyboard_clock_w(bool state)
{
	if(!state && m_kb_clock) // high to low transition - advance bit position
	{
		m_kb_bitpos++;
		if(m_kb_bitpos > 9)
			m_kb_bitpos = 1;
	}
	m_kb_clock = state;
}

// TODO: Figure out exactly how the HLD, RD, WR and CS lines on the RTC are hooked up
uint8_t attache_state::pio_portA_r()
{
	uint8_t ret = 0xff;
	uint8_t porta = m_pio_porta;

	switch(m_pio_select)
	{
	case PIO_SEL_8910_DATA:
		ret = m_psg->data_r();
		logerror("PSG: data read %02x\n",ret);
		break;
	case PIO_SEL_5832_WRITE:
		m_rtc->cs_w(1);
		m_rtc->write_w(0);
		m_rtc->read_w(1);
		m_rtc->address_w((porta & 0xf0) >> 4);
		ret = m_rtc->data_r();
		logerror("RTC: read %02x from %02x (write)\n",ret,(porta & 0xf0) >> 4);
		break;
	case PIO_SEL_5832_READ:
		m_rtc->cs_w(1);
		m_rtc->write_w(0);
		m_rtc->read_w(1);
		m_rtc->address_w((porta & 0xf0) >> 4);
		ret = m_rtc->data_r();
		logerror("RTC: read %02x from %02x\n",ret,(porta & 0xf0) >> 4);
		break;
	case PIO_SEL_5101_WRITE:
		m_cmos_select = (m_cmos_select & 0xf0) | ((porta & 0xf0) >> 4);
		ret = m_cmos_ram[m_cmos_select] & 0x0f;
		logerror("CMOS: read %02x from byte %02x (write)\n",ret, m_cmos_select);
		break;
	case PIO_SEL_5101_READ:
		m_cmos_select = (m_cmos_select & 0xf0) | ((porta & 0xf0) >> 4);
		ret = m_cmos_ram[m_cmos_select] & 0x0f;
		logerror("CMOS: read %02x from byte %02x\n",ret, m_cmos_select);
		break;
	case PIO_SEL_LATCH:
		ret = 0x00;  // Write-only?
		break;
	case PIO_SEL_NOP:
		logerror("PIO: NOP read\n");
		break;
	}
	//logerror("PIO: Port A read operation %i returning %02x\n",m_pio_select,ret);

	return ret;
}

uint8_t attache_state::pio_portB_r()
{
	uint8_t ret = m_pio_portb & 0xbf;
	ret |= keyboard_data_r();
	return ret;
}

void attache_state::set_latch(uint8_t data)
{
	m_pio_latch = data;
	m_rom_active = ~data & 0x04;
	m_floppy0->mon_w((data & 0x01) ? 0 : 1);
	m_floppy1->mon_w((data & 0x01) ? 0 : 1);
	m_gfx_enabled = data & 0x02;
	// TODO: display brightness
}

void attache_state::operation_strobe(uint8_t data)
{
	//logerror("PIO: Port A write operation %i, data %02x\n",m_pio_select,data);
	switch(m_pio_select)
	{
	case PIO_SEL_8910_ADDR:
		m_psg->address_w(data);
		break;
	case PIO_SEL_8910_DATA:
		m_psg->data_w(data);
		break;
	case PIO_SEL_5832_WRITE:
		m_rtc->cs_w(1);
		m_rtc->read_w(0);
		m_rtc->address_w((data & 0xf0) >> 4);
		m_rtc->data_w(data & 0x0f);
		m_rtc->write_w(1);
		logerror("RTC: write %01x to %01x\n",data & 0x0f,(data & 0xf0) >> 4);
		break;
	case PIO_SEL_5832_READ:
		m_rtc->cs_w(1);
		m_rtc->write_w(0);
		m_rtc->read_w(0);
		m_rtc->address_w((data & 0xf0) >> 4);
		logerror("RTC: write %01x to %01x (read)\n",data & 0x0f,(data & 0xf0) >> 4);
		break;
	case PIO_SEL_5101_WRITE:
		m_cmos_select = (m_cmos_select & 0xf0) | ((data & 0xf0) >> 4);
		m_cmos_ram[m_cmos_select] = data & 0x0f;
		logerror("CMOS: write %01x to byte %02x\n",data & 0x0f, m_cmos_select);
		break;
	case PIO_SEL_5101_READ:
		m_cmos_select = (m_cmos_select & 0xf0) | ((data & 0xf0) >> 4);
		logerror("CMOS: write %01x to byte %02x (read)\n",data & 0x0f, m_cmos_select);
		break;
	case PIO_SEL_LATCH:
		set_latch(data);
		break;
	case PIO_SEL_NOP:
		logerror("PIO: NOP write\n");
		break;
	default:
		logerror("PIO: Invalid write operation %i, data %02x\n",m_pio_select,data);
	}
}

void attache_state::pio_portA_w(uint8_t data)
{
	//  AO-7 = LATCH DATA OUT:
	//  LO = MOTOR ON
	//  L1 = GRAPHICS ENABLE
	//  L2 = /EPROM ENABLE
	//  L3-7 = DISPLAY BRIGHTNESS
	//  AO-7 = 8910 DATA I/O:
	//  AO-3 = 5832 DO-3 I/O
	//  A4-7 = 5832 AO-3 OUT
	//  AO-3 = 5101 DO-3 I/O
	//  A4-7 = 5101 AO-3 OUT
	m_pio_porta = data;
}

void attache_state::pio_portB_w(uint8_t data)
{
	//  BO-1 = 5101 A4-5
	//  B2-4 = OPERATION SELECT
	//  0 = 8910 ADDR LOAD
	//  1 = 8910 DATA LOAD
	//  2 = 5832 WRITE  -- the CP/M BIOS dumped from an actual disc seems to switch the RTC operations around
	//  3 = 5832 READ      this differs from the BIOS source listings available for both CP/M 2.2.3 and 2.2.5
	//  4 = 5101 WRITE
	//  5 = 5101 READ
	//  6 = LATCH LOAD
	//  7 = NO-OP
	//B5 = /'138 OPERATION STROBE
	//B6 = /KEYBOARD DATA IN
	//B7 = /KEYBOARD CLOCK OUT
	m_cmos_select = ((data & 0x03) << 4) | (m_cmos_select & 0x0f);
	if(!(data & 0x20) && (m_pio_portb & 0x20))
	{
		m_pio_select = (data & 0x1c) >> 2;
		operation_strobe(m_pio_porta);
	}
	m_pio_portb = data;
	keyboard_clock_w(data & 0x80);
}

// Display uses A8-A15 placed on the bus by the OUT instruction as an extra parameter
uint8_t attache_state::display_data_r(offs_t offset)
{
	uint8_t ret = 0xff;
	uint8_t param = (offset & 0xff00) >> 8;

	switch(m_current_cmd)
	{
	case DISP_GFX_0:
		ret = m_gfx_ram[(m_gfx_line*128)+(param & 0x7f)];
		break;
	case DISP_GFX_1:
		ret = m_gfx_ram[(m_gfx_line*128)+(param & 0x7f)+(128*32)];
		break;
	case DISP_GFX_2:
		ret = m_gfx_ram[(m_gfx_line*128)+(param & 0x7f)+(128*32*2)];
		break;
	case DISP_GFX_3:
		ret = m_gfx_ram[(m_gfx_line*128)+(param & 0x7f)+(128*32*3)];
		break;
	case DISP_GFX_4:
		ret = m_gfx_ram[(m_gfx_line*128)+(param & 0x7f)+(128*32*4)];
		break;
	case DISP_CRTC:
		ret = m_crtc->read(m_crtc_reg_select);
		break;
	case DISP_ATTR:
		ret = m_attr_ram[(m_attr_line*128)+(param & 0x7f)];
		break;
	case DISP_CHAR:
		ret = m_char_ram[(m_char_line*128)+(param & 0x7f)];
		break;
	default:
		logerror("Unimplemented display operation %02x\n",m_current_cmd);
	}

	return ret;
}

void attache_state::display_data_w(offs_t offset, uint8_t data)
{
	uint8_t param = (offset & 0xff00) >> 8;
	switch(m_current_cmd)
	{
	case DISP_GFX_0:
		m_gfx_ram[(m_gfx_line*128)+(param & 0x7f)] = data;
		break;
	case DISP_GFX_1:
		m_gfx_ram[(m_gfx_line*128)+(param & 0x7f)+(128*32)] = data;
		break;
	case DISP_GFX_2:
		m_gfx_ram[(m_gfx_line*128)+(param & 0x7f)+(128*32*2)] = data;
		break;
	case DISP_GFX_3:
		m_gfx_ram[(m_gfx_line*128)+(param & 0x7f)+(128*32*3)] = data;
		break;
	case DISP_GFX_4:
		m_gfx_ram[(m_gfx_line*128)+(param & 0x7f)+(128*32*4)] = data;
		break;
	case DISP_CRTC:
		m_crtc->write(m_crtc_reg_select, data);
		//logerror("CRTC: write reg %02x, data %02x\n",m_crtc_reg_select,data);
		break;
	case DISP_ATTR:
		m_attr_ram[(m_attr_line*128)+(param & 0x7f)] = data;
		break;
	case DISP_CHAR:
		m_char_ram[(m_char_line*128)+(param & 0x7f)] = data;
		break;
//  default:
//      logerror("Unimplemented display operation %02x data %02x param %02x\n",m_current_cmd,data,param);
	}
}

void attache_state::display_command_w(uint8_t data)
{
	uint8_t cmd = (data & 0xe0) >> 5;

	m_current_cmd = cmd;

	switch(cmd)
	{
	case DISP_GFX_0:
	case DISP_GFX_1:
	case DISP_GFX_2:
	case DISP_GFX_3:
	case DISP_GFX_4:
		m_gfx_line = data & 0x1f;
		break;
	case DISP_CRTC:
		// CRT5027/TMS9927 registers
		m_crtc_reg_select = data & 0x0f;
		break;
	case DISP_ATTR:
		// Attribute RAM
		m_attr_line = data & 0x1f;
		break;
	case DISP_CHAR:
		// Character RAM
		m_char_line = data & 0x1f;
		break;
	}
}

uint8_t attache_state::memmap_r()
{
	return m_memmap;
}

void attache_state::memmap_w(uint8_t data)
{
	// TODO: figure this out properly
	// Tech manual says that RAM is split into 8kB chunks.
	// Would seem that bit 4 is always 0 and bit 3 is always 1?
	uint8_t bank = (data & 0xe0) >> 5;
	uint8_t loc = data & 0x07;
	memory_bank* banknum[8] = { m_membank1, m_membank2, m_membank3, m_membank4, m_membank5, m_membank6, m_membank7, m_membank8 };
	m_memmap = data;

	banknum[bank]->set_entry(loc);

	logerror("MEM: write %02x - bank %i, location %i\n",data, bank, loc);
}

uint8_t attache_state::dma_mask_r()
{
	return m_dma->read(0x0f);
}

void attache_state::dma_mask_w(uint8_t data)
{
	m_dma->write(0x0f,data);
}

uint8_t attache_state::dma_mem_r(offs_t offset)
{
	return m_maincpu->space(AS_PROGRAM).read_byte(offset);
}

void attache_state::dma_mem_w(offs_t offset, uint8_t data)
{
	m_maincpu->space(AS_PROGRAM).write_byte(offset,data);
}

void attache_state::hreq_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);

	m_dma->hack_w(state);
}

void attache_state::eop_w(int state)
{
	m_fdc->tc_w(state);
}

void attache_state::fdc_dack_w(int state)
{
}

/*
 * Z80 <-> 8086 communication
 */

void attache816_state::x86_comms_w(uint8_t data)
{
	m_comms_val = data;
	m_ppi->pc6_w(1);
	m_z80_rx_ready = false;
}

uint8_t attache816_state::x86_comms_r()
{
	m_z80_tx_ready = false;
	m_ppi->pc4_w(1);
	return m_comms_val;
}

// PPI Port B - IRQ enable
// bit 0: i8255A PPI
// bit 1: TMS9914A GPIB
// bit 2: Z8530 SCC
// bit 3: 8087 FPU
// bit 4: enable WAIT logic
// bit 5: enable high-resolution graphics
void attache816_state::x86_irq_enable(uint8_t data)
{
	m_x86_irq_enable = data;
}

void attache816_state::x86_iobf_enable_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
		case 0x00:
			m_ppi->pc6_w(0);
			break;
		case 0x01:
			m_ppi->pc6_w(1);
			break;
		case 0x04:
			m_ppi->pc4_w(0);
			break;
		case 0x05:
			m_ppi->pc4_w(1);
			break;
		default:
			logerror("Invalid x86 IRQ enable write offset %02x data %02x\n",offset,data);
	}
}

uint8_t attache816_state::z80_comms_r()
{
	m_z80_rx_ready = true;
	m_ppi->pc6_w(0);
	return m_comms_val;
}

void attache816_state::z80_comms_w(uint8_t data)
{
	m_comms_val = data;
	m_z80_tx_ready = true;
	m_ppi->pc4_w(0);
}

// Z80 comms status
// bit 0: set if no data is ready
// bit 1: set if ready to accept data
uint8_t attache816_state::z80_comms_status_r()
{
	uint8_t ret = 0xf0;  // low nibble always high?

	if(m_z80_rx_ready)
		ret |= 0x01;
	if(m_z80_tx_ready)
		ret |= 0x02;

	return ret;
}

// Z80 comms controller
// bit 0: Reset 8086
void attache816_state::z80_comms_ctrl_w(uint8_t data)
{
	m_extcpu->set_input_line(INPUT_LINE_RESET,(data & 0x01) ? ASSERT_LINE : CLEAR_LINE);
}

void attache816_state::ppi_irq(int state)
{
	if(m_x86_irq_enable & 0x01)
		m_extcpu->set_input_line_and_vector(0,state,0x03); // I8086
}

void attache816_state::x86_dsr(int state)
{
	// TODO: /DSR to Z8530 SCC
}

void attache_state::attache_map(address_map &map)
{
	map(0x0000, 0x1fff).bankrw("bank1");
	map(0x2000, 0x3fff).bankrw("bank2");
	map(0x4000, 0x5fff).bankrw("bank3");
	map(0x6000, 0x7fff).bankrw("bank4");
	map(0x8000, 0x9fff).bankrw("bank5");
	map(0xa000, 0xbfff).bankrw("bank6");
	map(0xc000, 0xdfff).bankrw("bank7");
	map(0xe000, 0xffff).bankrw("bank8");
}

void attache_state::attache_io(address_map &map)
{
	map(0xe0, 0xed).rw(m_dma, FUNC(am9517a_device::read), FUNC(am9517a_device::write)).mirror(0xff00);
	map(0xee, 0xee).w(FUNC(attache_state::display_command_w)).mirror(0xff00);
	map(0xef, 0xef).rw(FUNC(attache_state::dma_mask_r), FUNC(attache_state::dma_mask_w)).mirror(0xff00);
	map(0xf0, 0xf1).rw(m_sio, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w)).mirror(0xff00);
	map(0xf4, 0xf7).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write)).mirror(0xff00);
	map(0xf8, 0xfb).rw(m_pio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt)).mirror(0xff00);
	map(0xfc, 0xfd).m(m_fdc, FUNC(upd765a_device::map)).mirror(0xff00);
	map(0xfe, 0xfe).rw(FUNC(attache_state::display_data_r), FUNC(attache_state::display_data_w)).select(0xff00);
	map(0xff, 0xff).rw(FUNC(attache_state::memmap_r), FUNC(attache_state::memmap_w)).mirror(0xff00);
}

void attache816_state::attache816_io(address_map &map)
{
	map(0xb8, 0xb8).rw(FUNC(attache816_state::z80_comms_status_r), FUNC(attache816_state::z80_comms_ctrl_w)).mirror(0xff00);
	map(0xb9, 0xb9).rw(FUNC(attache816_state::z80_comms_r), FUNC(attache816_state::z80_comms_w)).mirror(0xff00);
	map(0xe0, 0xed).rw(m_dma, FUNC(am9517a_device::read), FUNC(am9517a_device::write)).mirror(0xff00);
	map(0xee, 0xee).w(FUNC(attache816_state::display_command_w)).mirror(0xff00);
	map(0xef, 0xef).rw(FUNC(attache816_state::dma_mask_r), FUNC(attache816_state::dma_mask_w)).mirror(0xff00);
	map(0xf0, 0xf1).rw(m_sio, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w)).mirror(0xff00);
	map(0xf4, 0xf7).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write)).mirror(0xff00);
	map(0xf8, 0xfb).rw(m_pio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt)).mirror(0xff00);
	map(0xfc, 0xfd).m(m_fdc, FUNC(upd765a_device::map)).mirror(0xff00);
	map(0xfe, 0xfe).rw(FUNC(attache816_state::display_data_r), FUNC(attache816_state::display_data_w)).select(0xff00);
	map(0xff, 0xff).rw(FUNC(attache816_state::memmap_r), FUNC(attache816_state::memmap_w)).mirror(0xff00);
}

void attache816_state::attache_x86_map(address_map &map)
{
	map(0x00000, 0x3ffff).ram();
	map(0xb0000, 0xbffff).noprw();  // triggers IRQ?
	map(0xfe000, 0xfffff).rom().region("x86bios", 0x0000);
}

void attache816_state::attache_x86_io(address_map &map)
{
	map(0x100, 0x107).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
	map(0x108, 0x10d).w(FUNC(attache816_state::x86_iobf_enable_w));
// 0x140/2/4/6 - Z8530 SCC serial
// 0x180/2/4/6/8/a/c/e - GPIB (TMS9914A)
}

static INPUT_PORTS_START(attache)
	PORT_START("row0")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("BS") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("TAB") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("LF") PORT_CODE(KEYCODE_PGDN) PORT_CHAR(10)
	PORT_BIT(0x18,IP_ACTIVE_HIGH,IPT_UNUSED)
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_UNUSED)
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("LOCK") PORT_CODE(KEYCODE_PGUP)

	PORT_START("row1")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x06,IP_ACTIVE_HIGH,IPT_UNUSED)
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))

	PORT_START("row2")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0 ^") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('^')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1 !") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2 @") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3 #") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("4 $") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("5 %") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("6 &") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("7 *") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('*')

	PORT_START("row3")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("8 (") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("9 )") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("' \"") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("; :") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("= +") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("row4")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("` ~") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('`') PORT_CHAR('~')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')

	PORT_START("row5")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("row6")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')

	PORT_START("row7")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("[ {") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\\ |") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("] }") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("- _") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(DEL))

	PORT_START("modifiers")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
INPUT_PORTS_END

// IRQ daisy chain = CTC -> SIO -> Expansion
static const z80_daisy_config attache_daisy_chain[] =
{
	{ "ctc" },
	{ "sio" },
	// expansion
	{ nullptr }
};

static void attache_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

void attache_state::driver_start()
{
	uint8_t *RAM = m_ram->pointer();

	m_membank1->configure_entries(0, 8, &RAM[0x0000], 0x2000);
	m_membank2->configure_entries(0, 8, &RAM[0x0000], 0x2000);
	m_membank3->configure_entries(0, 8, &RAM[0x0000], 0x2000);
	m_membank4->configure_entries(0, 8, &RAM[0x0000], 0x2000);
	m_membank5->configure_entries(0, 8, &RAM[0x0000], 0x2000);
	m_membank6->configure_entries(0, 8, &RAM[0x0000], 0x2000);
	m_membank7->configure_entries(0, 8, &RAM[0x0000], 0x2000);
	m_membank8->configure_entries(0, 8, &RAM[0x0000], 0x2000);

	m_membank1->set_entry(0);
	m_membank2->set_entry(1);
	m_membank3->set_entry(2);
	m_membank4->set_entry(3);
	m_membank5->set_entry(4);
	m_membank6->set_entry(5);
	m_membank7->set_entry(6);
	m_membank8->set_entry(7);

	memset(RAM,0,65536);

	m_nvram->set_base(m_cmos_ram,64);

	m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x0000,0x0fff, read8sm_delegate(*this, FUNC(attache_state::rom_r)), write8sm_delegate(*this, FUNC(attache_state::rom_w)));

	save_pointer(m_char_ram,"Character RAM",128*32);
	save_pointer(m_attr_ram,"Attribute RAM",128*32);
	save_pointer(m_gfx_ram,"Graphics RAM",128*32*5);
	save_pointer(m_cmos_ram,"CMOS RAM",64);
}

void attache_state::machine_start()
{
	// initialise RAM
	memset(m_cmos_ram,0,64);
	memset(m_attr_ram,0,128*32);
	memset(m_char_ram,0,128*32);
	memset(m_gfx_ram,0,128*32*5);
}

void attache_state::machine_reset()
{
	m_kb_bitpos = 0;
	set_latch(0);
}

void attache816_state::machine_reset()
{
	attache_state::machine_reset();
}

void attache_state::attache(machine_config &config)
{
	Z80(config, m_maincpu, 8_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &attache_state::attache_map);
	m_maincpu->set_addrmap(AS_IO, &attache_state::attache_io);
	m_maincpu->set_daisy_config(attache_daisy_chain);

	config.set_maximum_quantum(attotime::from_hz(60));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_raw(12.324_MHz_XTAL, 784, 0, 640, 262, 0, 240);
	screen.set_screen_update(FUNC(attache_state::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	SPEAKER(config, "mono").front_center();
	AY8912(config, m_psg, 8_MHz_XTAL / 4);
	m_psg->add_route(ALL_OUTPUTS, "mono", 0.25);

	MSM5832(config, m_rtc, 32.768_kHz_XTAL);

	Z80PIO(config, m_pio, 8_MHz_XTAL / 2);
	m_pio->in_pa_callback().set(FUNC(attache_state::pio_portA_r));
	m_pio->out_pa_callback().set(FUNC(attache_state::pio_portA_w));
	m_pio->in_pb_callback().set(FUNC(attache_state::pio_portB_r));
	m_pio->out_pb_callback().set(FUNC(attache_state::pio_portB_w));

	Z80SIO(config, m_sio, 8_MHz_XTAL / 2);
	m_sio->out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
	m_sio->out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts));
	m_sio->out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
	m_sio->out_rtsb_callback().set("rs232b", FUNC(rs232_port_device::write_rts));
	m_sio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_sio, FUNC(z80sio_device::rxa_w));
	rs232a.cts_handler().set(m_sio, FUNC(z80sio_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_sio, FUNC(z80sio_device::rxb_w));
	rs232b.cts_handler().set(m_sio, FUNC(z80sio_device::ctsb_w));

	Z80CTC(config, m_ctc, 8_MHz_XTAL / 2);
	m_ctc->set_clk<0>(8_MHz_XTAL / 26); // 307.692 KHz
	m_ctc->set_clk<1>(8_MHz_XTAL / 26); // 307.692 KHz
	m_ctc->zc_callback<0>().set(m_sio, FUNC(z80sio_device::rxca_w));
	m_ctc->zc_callback<0>().append(m_sio, FUNC(z80sio_device::txca_w));
	m_ctc->zc_callback<1>().set(m_sio, FUNC(z80sio_device::rxtxcb_w));
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	AM9517A(config, m_dma, 8_MHz_XTAL / 4);
	m_dma->out_hreq_callback().set(FUNC(attache_state::hreq_w));
	m_dma->out_eop_callback().set(FUNC(attache_state::eop_w));
	m_dma->in_memr_callback().set(FUNC(attache_state::dma_mem_r));
	m_dma->out_memw_callback().set(FUNC(attache_state::dma_mem_w));
	m_dma->in_ior_callback<0>().set(m_fdc, FUNC(upd765a_device::dma_r));
	m_dma->out_iow_callback<0>().set(m_fdc, FUNC(upd765a_device::dma_w));
	// m_dma->out_dack_callback<0>().set(FUNC(attache_state::fdc_dack_w));

	UPD765A(config, m_fdc, 8_MHz_XTAL, true, true);
	m_fdc->intrq_wr_callback().set(m_ctc, FUNC(z80ctc_device::trg3));
	m_fdc->drq_wr_callback().set(m_dma, FUNC(am9517a_device::dreq0_w)).invert();
	FLOPPY_CONNECTOR(config, "fdc:0", attache_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", attache_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);

	TMS9927(config, m_crtc, 12.324_MHz_XTAL / 8);
	m_crtc->set_char_width(8);
	m_crtc->vsyn_callback().set(m_ctc, FUNC(z80ctc_device::trg2));
	m_crtc->set_screen("screen");

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	RAM(config, RAM_TAG).set_default_size("64K");

	SOFTWARE_LIST(config, "disk_list").set_original("attache");
}

void attache816_state::attache816(machine_config &config)
{
	Z80(config, m_maincpu, 8_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &attache816_state::attache_map);
	m_maincpu->set_addrmap(AS_IO, &attache816_state::attache_io);
	m_maincpu->set_daisy_config(attache_daisy_chain);

	config.set_maximum_quantum(attotime::from_hz(60));

	I8086(config, m_extcpu, 24_MHz_XTAL / 3);
	m_extcpu->set_addrmap(AS_PROGRAM, &attache816_state::attache_x86_map);
	m_extcpu->set_addrmap(AS_IO, &attache816_state::attache_x86_io);
	config.set_perfect_quantum(m_extcpu);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_raw(12.324_MHz_XTAL, 784, 0, 640, 262, 0, 240);
	screen.set_screen_update(FUNC(attache_state::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	SPEAKER(config, "mono").front_center();
	AY8912(config, m_psg, 8_MHz_XTAL / 4);
	m_psg->add_route(ALL_OUTPUTS, "mono", 0.25);

	MSM5832(config, m_rtc, 32.768_kHz_XTAL);

	Z80PIO(config, m_pio, 8_MHz_XTAL / 2);
	m_pio->in_pa_callback().set(FUNC(attache_state::pio_portA_r));
	m_pio->out_pa_callback().set(FUNC(attache_state::pio_portA_w));
	m_pio->in_pb_callback().set(FUNC(attache_state::pio_portB_r));
	m_pio->out_pb_callback().set(FUNC(attache_state::pio_portB_w));

	Z80SIO(config, m_sio, 8_MHz_XTAL / 2);
	m_sio->out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
	m_sio->out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts));
	m_sio->out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
	m_sio->out_rtsb_callback().set("rs232b", FUNC(rs232_port_device::write_rts));
	m_sio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_sio, FUNC(z80sio_device::rxa_w));
	rs232a.cts_handler().set(m_sio, FUNC(z80sio_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_sio, FUNC(z80sio_device::rxb_w));
	rs232b.cts_handler().set(m_sio, FUNC(z80sio_device::ctsb_w));

	Z80CTC(config, m_ctc, 8_MHz_XTAL / 2);
	m_ctc->set_clk<0>(8_MHz_XTAL / 26); // 307.692 KHz
	m_ctc->set_clk<1>(8_MHz_XTAL / 26); // 307.692 KHz
	m_ctc->zc_callback<0>().set(m_sio, FUNC(z80sio_device::rxca_w));
	m_ctc->zc_callback<0>().append(m_sio, FUNC(z80sio_device::txca_w));
	m_ctc->zc_callback<1>().set(m_sio, FUNC(z80sio_device::rxtxcb_w));
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	I8255A(config, m_ppi, 0);
	m_ppi->out_pa_callback().set(FUNC(attache816_state::x86_comms_w));
	m_ppi->in_pa_callback().set(FUNC(attache816_state::x86_comms_r));
	m_ppi->out_pb_callback().set(FUNC(attache816_state::x86_irq_enable));
	m_ppi->out_pc_callback().set(FUNC(attache816_state::x86_dsr)).bit(0);
	m_ppi->out_pc_callback().append(FUNC(attache816_state::ppi_irq)).bit(7).invert();

	AM9517A(config, m_dma, 8_MHz_XTAL / 4);
	m_dma->out_hreq_callback().set(FUNC(attache_state::hreq_w));
	m_dma->out_eop_callback().set(FUNC(attache_state::eop_w));
	m_dma->in_memr_callback().set(FUNC(attache_state::dma_mem_r));
	m_dma->out_memw_callback().set(FUNC(attache_state::dma_mem_w));
	m_dma->in_ior_callback<0>().set(m_fdc, FUNC(upd765a_device::dma_r));
	m_dma->out_iow_callback<0>().set(m_fdc, FUNC(upd765a_device::dma_w));
	// m_dma->out_dack_callback<0>().set(FUNC(attache_state::fdc_dack_w));

	UPD765A(config, m_fdc, 8_MHz_XTAL, true, true);
	m_fdc->intrq_wr_callback().set(m_ctc, FUNC(z80ctc_device::trg3));
	m_fdc->drq_wr_callback().set(m_dma, FUNC(am9517a_device::dreq0_w)).invert();
	FLOPPY_CONNECTOR(config, "fdc:0", attache_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", attache_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);

	TMS9927(config, m_crtc, 12.324_MHz_XTAL / 8);
	m_crtc->set_char_width(8);
	m_crtc->vsyn_callback().set(m_ctc, FUNC(z80ctc_device::trg2));
	m_crtc->set_screen("screen");

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	RAM(config, RAM_TAG).set_default_size("64K");

	SOFTWARE_LIST(config, "disk_list").set_original("attache");
}

ROM_START( attache )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_FILL(0x0000,0x10000,0x00)

	ROM_REGION(0x1000, "boot", 0)
	ROM_SYSTEM_BIOS(0, "u252revh", "Boot Rev.H")
	ROMX_LOAD("u252revh.bin", 0x0000, 0x1000, CRC(a06f0bdf) SHA1(d526cf23bfe0f8f9bcde812cd864a2a4cbc8b673), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "u252revg", "Boot Rev.G")
	ROMX_LOAD("u252revg.bin", 0x0000, 0x1000, CRC(113136b7) SHA1(845afd9ed2fd2b28c39921d8f2ba99e5295e0330), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "u252revf", "Boot Rev.F")
	ROMX_LOAD("u252revf.bin", 0x0000, 0x1000, CRC(b49eb3b2) SHA1(5b1b348301b2f76b1f250ba68bb8733fc15d18c2), ROM_BIOS(2))

	ROM_REGION(0x1000, "video", 0)
	ROM_LOAD("u416vid.bin",  0x0000, 0x1000, CRC(e376ec59) SHA1(7b9e9db575e77ce2f479eb9ae913528e4f0d125d) )

	ROM_REGION(0x100, "attr", 0)
	ROM_LOAD("u413.bin",  0x0000, 0x0100, CRC(5b60e622) SHA1(43450c747db1394466eabe5c26a61bf75a4f3b52) )

	ROM_REGION(0x200, "iosel", 0)
	ROM_LOAD("u110.bin",  0x0000, 0x0200, CRC(70dd255a) SHA1(36dcce07a2c14eefc069433459c422341bd47efb) )

	ROM_REGION(0x100, "floppy", 0)
	ROM_LOAD("u630.bin",  0x0000, 0x0100, CRC(f7a5c821) SHA1(fea07d9ac7e4e5f4f72aa7b2159deaedbd662ead) )

ROM_END

ROM_START( attache816 )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_FILL(0x0000,0x10000,0x00)

	ROM_REGION(0x1000, "boot", 0)
	ROM_SYSTEM_BIOS(0, "u252revh", "Boot Rev.H")
	ROMX_LOAD("u252revh.bin", 0x0000, 0x1000, CRC(a06f0bdf) SHA1(d526cf23bfe0f8f9bcde812cd864a2a4cbc8b673), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "u252revg", "Boot Rev.G")
	ROMX_LOAD("u252revg.bin", 0x0000, 0x1000, CRC(113136b7) SHA1(845afd9ed2fd2b28c39921d8f2ba99e5295e0330), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "u252revf", "Boot Rev.F")
	ROMX_LOAD("u252revf.bin", 0x0000, 0x1000, CRC(b49eb3b2) SHA1(5b1b348301b2f76b1f250ba68bb8733fc15d18c2), ROM_BIOS(2))

	ROM_REGION(0x2000, "video", 0)
	ROM_LOAD("u416vid2.bin",  0x0000, 0x2000, CRC(0bdaed8d) SHA1(eee1e8505906e7c3587ecdf9dd9227a2a3b3cdd4) )

	ROM_REGION(0x100, "attr", 0)
	ROM_LOAD("u413.bin",  0x0000, 0x0100, CRC(5b60e622) SHA1(43450c747db1394466eabe5c26a61bf75a4f3b52) )

	ROM_REGION(0x200, "iosel", 0)
	ROM_LOAD("u110.bin",  0x0000, 0x0200, CRC(70dd255a) SHA1(36dcce07a2c14eefc069433459c422341bd47efb) )

	ROM_REGION(0x100, "floppy", 0)
	ROM_LOAD("u630.bin",  0x0000, 0x0100, CRC(f7a5c821) SHA1(fea07d9ac7e4e5f4f72aa7b2159deaedbd662ead) )

	// chip locations based on schematics
	ROM_REGION16_LE(0x2000, "x86bios", 0)
	ROM_LOAD16_BYTE("u4.bin",  0x0000, 0x1000, CRC(658c8f93) SHA1(ce4b388af5b73884194f548afa706964305462f7) )
	ROM_LOAD16_BYTE("u9.bin",  0x0001, 0x1000, CRC(cc4cd938) SHA1(6a1d316628641f9b4de5c8c46f9430ef5bd6120f) )

ROM_END

} // anonymous namespace


//    YEAR  NAME        PARENT   COMPAT  MACHINE     INPUT    CLASS             INIT        COMPANY   FULLNAME          FLAGS
COMP( 1982, attache,    0,       0,      attache,    attache, attache_state,    empty_init, "Otrona", u8"Attaché",      MACHINE_IMPERFECT_GRAPHICS )
COMP( 1983, attache816, attache, 0,      attache816, attache, attache816_state, empty_init, "Otrona", u8"Attaché 8:16", MACHINE_IMPERFECT_GRAPHICS )



aussiebyte.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/*************************************************************************************************

    The Aussie Byte II Single-Board Computer, created by SME Systems, Melbourne, Australia.
    Also known as the Knight 2000 Microcomputer.

    Status:
    Boots up from floppy.
    Output to serial terminal and to 6545 are working. Serial keyboard works.

    Developed in conjunction with members of the MSPP. Written in July, 2015.

    ToDo:
    - CRT8002 attributes controller
    - Graphics
    - Hard drive controllers and drives
    - Test Centronics printer
    - PIO connections

    Note of MAME restrictions:
    - Votrax doesn't sound anything like the real thing
    - WD1001/WD1002 device is not emulated
    - CRT8002 device is not emulated

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

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

    Includes

************************************************************/
#include "emu.h"
#include "aussiebyte.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


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

    Address Maps

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

void aussiebyte_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).bankr("bankr0").bankw("bankw0");
	map(0x4000, 0x7fff).bankrw("bank1");
	map(0x8000, 0xbfff).bankrw("bank2");
	map(0xc000, 0xffff).ram();
}

void aussiebyte_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x03).rw("sio1", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x04, 0x07).rw(m_pio1, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x08, 0x0b).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x0c, 0x0f).noprw(); // winchester interface
	map(0x10, 0x13).rw(m_fdc, FUNC(wd2797_device::read), FUNC(wd2797_device::write));
	map(0x14, 0x14).rw(m_dma, FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	map(0x15, 0x15).w(FUNC(aussiebyte_state::port15_w)); // boot rom disable
	map(0x16, 0x16).w(FUNC(aussiebyte_state::port16_w)); // fdd select
	map(0x17, 0x17).w(FUNC(aussiebyte_state::port17_w)); // DMA mux
	map(0x18, 0x18).w(FUNC(aussiebyte_state::port18_w)); // fdc select
	map(0x19, 0x19).r(FUNC(aussiebyte_state::port19_r)); // info port
	map(0x1a, 0x1a).w(FUNC(aussiebyte_state::port1a_w)); // membank
	map(0x1b, 0x1b).w(FUNC(aussiebyte_state::port1b_w)); // winchester control
	map(0x1c, 0x1f).w(FUNC(aussiebyte_state::port1c_w)); // gpebh select
	map(0x20, 0x23).rw(m_pio2, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x24, 0x27).rw("sio2", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x28, 0x28).r(FUNC(aussiebyte_state::port28_r)).w(m_votrax, FUNC(votrax_sc01_device::write));
	map(0x2c, 0x2c).w(m_votrax, FUNC(votrax_sc01_device::inflection_w));
	map(0x30, 0x30).w(FUNC(aussiebyte_state::address_w));
	map(0x31, 0x31).r(m_crtc, FUNC(mc6845_device::status_r));
	map(0x32, 0x32).w(FUNC(aussiebyte_state::register_w));
	map(0x33, 0x33).r(FUNC(aussiebyte_state::port33_r));
	map(0x34, 0x34).w(FUNC(aussiebyte_state::port34_w)); // video control
	map(0x35, 0x35).w(FUNC(aussiebyte_state::port35_w)); // data to vram and aram
	map(0x36, 0x36).r(FUNC(aussiebyte_state::port36_r)); // data from vram and aram
	map(0x37, 0x37).r(FUNC(aussiebyte_state::port37_r)); // read dispen flag
	map(0x40, 0x4f).rw(FUNC(aussiebyte_state::rtc_r), FUNC(aussiebyte_state::rtc_w));
}

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

    Keyboard

************************************************************/
static INPUT_PORTS_START( aussiebyte )
INPUT_PORTS_END

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

    I/O Ports

************************************************************/
void aussiebyte_state::port15_w(u8 data)
{
	m_bankr0->set_entry(m_port15); // point at ram
	m_port15 = true;
}

/* FDD select
0 Drive Select bit O
1 Drive Select bit 1
2 Drive Select bit 2
3 Drive Select bit 3
  - These bits connect to a 74LS145 binary to BCD converter.
  - Drives 0 to 3 are 5.25 inch, 4 to 7 are 8 inch, 9 and 0 are not used.
  - Currently we only support drive 0.
4 Side Select to Disk Drives.
5 Disable 5.25 inch floppy spindle motors.
6 Unused.
7 Enable write precompensation on WD2797 controller. */
void aussiebyte_state::port16_w(u8 data)
{
	floppy_image_device *m_floppy = nullptr;
	if ((data & 15) == 0)
		m_floppy = m_floppy0->get_device();
	else
	if ((data & 15) == 1)
		m_floppy = m_floppy1->get_device();

	m_fdc->set_floppy(m_floppy);

	if (m_floppy)
	{
		m_floppy->mon_w(BIT(data, 5));
		m_floppy->ss_w(BIT(data, 4));
	}
}

/* DMA select
0 - FDC
1 - SIO Ch A
2 - SIO Ch B
3 - Winchester bus
4 - SIO Ch C
5 - SIO Ch D
6 - Ext ready 1
7 - Ext ready 2 */
void aussiebyte_state::port17_w(u8 data)
{
	m_port17 = data & 7;
	m_dma->rdy_w(BIT(m_port17_rdy, data));
}

/* FDC params
2 EXC: WD2797 clock frequency. H = 5.25"; L = 8"
3 WIEN: WD2797 Double density select. */
void aussiebyte_state::port18_w(u8 data)
{
	m_fdc->set_unscaled_clock(BIT(data, 2) ? 1e6 : 2e6);
	m_fdc->dden_w(BIT(data, 3));
}

u8 aussiebyte_state::port19_r()
{
	return m_port19;
}

// Memory banking
void aussiebyte_state::port1a_w(u8 data)
{
	data &= 7;
	switch (data)
	{
		case 0:
		case 1:
		case 2:
		case 3:
		case 4:
			m_port1a = data*3+1;
			if (m_port15)
				m_bankr0->set_entry(data*3+1);
			m_bankw0->set_entry(data*3+1);
			m_bank1->set_entry(data*3+2);
			m_bank2->set_entry(data*3+3);
			break;
		case 5:
			m_port1a = 1;
			if (m_port15)
				m_bankr0->set_entry(1);
			m_bankw0->set_entry(1);
			m_bank1->set_entry(2);
			m_bank2->set_entry(13);
			break;
		case 6:
			m_port1a = 14;
			if (m_port15)
				m_bankr0->set_entry(14);
			m_bankw0->set_entry(14);
			m_bank1->set_entry(15);
			//m_bank2->set_entry(0); // open bus
			break;
		case 7:
			m_port1a = 1;
			if (m_port15)
				m_bankr0->set_entry(1);
			m_bankw0->set_entry(1);
			m_bank1->set_entry(4);
			m_bank2->set_entry(13);
			break;
	}
}

// Winchester control
void aussiebyte_state::port1b_w(u8 data)
{
}

// GPEHB control
void aussiebyte_state::port1c_w(u8 data)
{
}

void aussiebyte_state::port20_w(u8 data)
{
	m_speaker->level_w(BIT(data, 7));
	m_rtc->cs_w(BIT(data, 0));
	m_rtc->hold_w(BIT(data, 0));
}

u8 aussiebyte_state::port28_r()
{
	return m_port28;
}

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

    RTC

************************************************************/
u8 aussiebyte_state::rtc_r(offs_t offset)
{
	m_rtc->read_w(1);
	m_rtc->address_w(offset);
	u8 data = m_rtc->data_r();
	m_rtc->read_w(0);
	return data;
}

void aussiebyte_state::rtc_w(offs_t offset, u8 data)
{
	m_rtc->address_w(offset);
	m_rtc->data_w(data);
	m_rtc->write_w(1);
	m_rtc->write_w(0);
}

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

    DMA

************************************************************/
u8 aussiebyte_state::memory_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	return prog_space.read_byte(offset);
}

void aussiebyte_state::memory_write_byte(offs_t offset, u8 data)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	prog_space.write_byte(offset, data);
}

u8 aussiebyte_state::io_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_IO);
	return prog_space.read_byte(offset);
}

void aussiebyte_state::io_write_byte(offs_t offset, u8 data)
{
	address_space& prog_space = m_maincpu->space(AS_IO);
	prog_space.write_byte(offset, data);
}

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

    DMA selector

************************************************************/
void aussiebyte_state::sio1_rdya_w(int state)
{
	m_port17_rdy = (m_port17_rdy & 0xfd) | (u8)(state << 1);
	if (m_port17 == 1)
		m_dma->rdy_w(state);
}

void aussiebyte_state::sio1_rdyb_w(int state)
{
	m_port17_rdy = (m_port17_rdy & 0xfb) | (u8)(state << 2);
	if (m_port17 == 2)
		m_dma->rdy_w(state);
}

void aussiebyte_state::sio2_rdya_w(int state)
{
	m_port17_rdy = (m_port17_rdy & 0xef) | (u8)(state << 4);
	if (m_port17 == 4)
		m_dma->rdy_w(state);
}

void aussiebyte_state::sio2_rdyb_w(int state)
{
	m_port17_rdy = (m_port17_rdy & 0xdf) | (u8)(state << 5);
	if (m_port17 == 5)
		m_dma->rdy_w(state);
}


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

    Video

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

/* F4 Character Displayer */
static const gfx_layout crt8002_charlayout =
{
	8, 12,                   /* 7 x 11 characters */
	128,                  /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_crt8002 )
	GFXDECODE_ENTRY( "chargen", 0x0000, crt8002_charlayout, 0, 1 )
GFXDECODE_END

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

    Daisy Chain

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

static const z80_daisy_config daisy_chain_intf[] =
{
	{ "dma" },
	{ "pio2" },
	{ "sio1" },
	{ "sio2" },
	{ "pio1" },
	{ "ctc" },
	{ nullptr }
};


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

    Floppy Disk

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

void aussiebyte_state::fdc_intrq_w(int state)
{
	u8 data = (m_port19 & 0xbf) | (state ? 0x40 : 0);
	m_port19 = data;
}

void aussiebyte_state::fdc_drq_w(int state)
{
	u8 data = (m_port19 & 0x7f) | (state ? 0x80 : 0);
	m_port19 = data;
	state ^= 1; // inverter on pin38 of fdc
	m_port17_rdy = (m_port17_rdy & 0xfe) | (u8)state;
	if (m_port17 == 0)
		m_dma->rdy_w(state);
}

static void aussiebyte_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}


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

    Quickload

    This loads a .COM file to address 0x100 then jumps
    there. Sometimes .COM has been renamed to .CPM to
    prevent windows going ballistic. These can be loaded
    as well.

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

QUICKLOAD_LOAD_MEMBER(aussiebyte_state::quickload_cb)
{
	// RAM must be banked in
	m_port15 = true; // disable boot rom
	m_port1a = 4;
	m_bankr0->set_entry(m_port1a); // enable correct program bank
	m_bankw0->set_entry(m_port1a);

	address_space& prog_space = m_maincpu->space(AS_PROGRAM);

	// Avoid loading a program if CP/M-80 is not in memory
	if ((prog_space.read_byte(0) != 0xc3) || (prog_space.read_byte(5) != 0xc3))
	{
		machine_reset();
		return std::make_pair(image_error::UNSUPPORTED, "CP/M must already be running");
	}

	const int mem_avail = 256 * prog_space.read_byte(7) + prog_space.read_byte(6) - 512;
	if (mem_avail < image.length())
		return std::make_pair(image_error::UNSPECIFIED, "Insufficient memory available");

	// Load image to the TPA (Transient Program Area)
	u16 quickload_size = image.length();
	for (u16 i = 0; i < quickload_size; i++)
	{
		u8 data;
		if (image.fread(&data, 1) != 1)
			return std::make_pair(image_error::UNSPECIFIED, "Problem reading the image at offset " + std::to_string(i));
		prog_space.write_byte(i + 0x100, data);
	}

	// clear out command tail
	prog_space.write_byte(0x80, 0);
	prog_space.write_byte(0x81, 0);

	// Roughly set SP basing on the BDOS position
	m_maincpu->set_state_int(Z80_SP, mem_avail + 384);
	m_maincpu->set_pc(0x100); // start program

	return std::make_pair(std::error_condition(), std::string());
}

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

    Machine Driver

************************************************************/
void aussiebyte_state::machine_reset()
{
	m_port15 = false;
	m_port17 = 0;
	m_port17_rdy = 0;
	m_port1a = 1;
	m_alpha_address = 0;
	m_graph_address = 0;
	m_bankr0->set_entry(16); // point at rom
	m_bankw0->set_entry(1); // always write to ram
	m_bank1->set_entry(2);
	m_bank2->set_entry(3);
	m_maincpu->reset();
}

void aussiebyte_state::machine_start()
{
	m_vram = std::make_unique<u8[]>(0x10000);
	m_aram = std::make_unique<u8[]>(0x800);
	m_ram = make_unique_clear<u8[]>(0x40000);
	save_pointer(NAME(m_vram), 0x10000);
	save_pointer(NAME(m_aram), 0x800);
	save_pointer(NAME(m_ram),  0x40000);
	save_item(NAME(m_port15));
	save_item(NAME(m_port17));
	save_item(NAME(m_port17_rdy));
	save_item(NAME(m_port19));
	save_item(NAME(m_port1a));
	save_item(NAME(m_port28));
	save_item(NAME(m_port34));
	save_item(NAME(m_port35));
	save_item(NAME(m_video_index));
	save_item(NAME(m_cnt));
	save_item(NAME(m_alpha_address));
	save_item(NAME(m_graph_address));
	save_item(NAME(m_centronics_busy));

	// Main ram is divided into 16k blocks (0-15). The boot rom is block number 16.
	// For convenience, bank 0 is permanently assigned to C000-FFFF

	m_bankr0->configure_entries(0, 16, m_p_mram, 0x4000);
	m_bankw0->configure_entries(0, 16, m_p_mram, 0x4000);
	m_bank1->configure_entries(0, 16, m_p_mram, 0x4000);
	m_bank2->configure_entries(0, 16, m_p_mram, 0x4000);
	m_bankr0->configure_entry(16, memregion("roms")->base());

	m_cnt = 0;
}


void aussiebyte_state::aussiebyte(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &aussiebyte_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &aussiebyte_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain_intf);
	m_maincpu->busack_cb().set(m_dma, FUNC(z80dma_device::bai_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(16_MHz_XTAL, 952, 0, 640, 336, 0, 288);
	screen.set_screen_update("crtc", FUNC(sy6545_1_device::screen_update));

	GFXDECODE(config, "gfxdecode", "palette", gfx_crt8002);
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);
	VOTRAX_SC01A(config, m_votrax, 720000); // 720kHz? needs verify
	m_votrax->ar_callback().set([this] (bool state) { m_port28 = state ? 0 : 1; });
	m_votrax->add_route(ALL_OUTPUTS, "mono", 1.00);

	/* devices */
	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->set_data_input_buffer("cent_data_in");
	m_centronics->busy_handler().set([this] (bool state) { m_centronics_busy = state; });
	INPUT_BUFFER(config, "cent_data_in");
	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	Z80CTC(config, m_ctc, 16_MHz_XTAL / 4);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->set_clk<0>(4.9152_MHz_XTAL / 4);
	m_ctc->set_clk<1>(4.9152_MHz_XTAL / 4);
	m_ctc->set_clk<2>(4.9152_MHz_XTAL / 4);
	m_ctc->zc_callback<0>().set("sio1", FUNC(z80sio_device::rxca_w));
	m_ctc->zc_callback<0>().append("sio1", FUNC(z80sio_device::txca_w));
	m_ctc->zc_callback<1>().set("sio1", FUNC(z80sio_device::rxtxcb_w));
	m_ctc->zc_callback<1>().append("sio2", FUNC(z80sio_device::rxca_w));
	m_ctc->zc_callback<1>().append("sio2", FUNC(z80sio_device::txca_w));
	m_ctc->zc_callback<2>().set("ctc", FUNC(z80ctc_device::trg3));
	m_ctc->zc_callback<2>().append("sio2", FUNC(z80sio_device::rxtxcb_w));

	Z80DMA(config, m_dma, 16_MHz_XTAL / 4);
	m_dma->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_dma->out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	// BAO, not used
	m_dma->in_mreq_callback().set(FUNC(aussiebyte_state::memory_read_byte));
	m_dma->out_mreq_callback().set(FUNC(aussiebyte_state::memory_write_byte));
	m_dma->in_iorq_callback().set(FUNC(aussiebyte_state::io_read_byte));
	m_dma->out_iorq_callback().set(FUNC(aussiebyte_state::io_write_byte));

	Z80PIO(config, m_pio1, 16_MHz_XTAL / 4);
	m_pio1->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio1->out_pa_callback().set("cent_data_out", FUNC(output_latch_device::write));
	m_pio1->in_pb_callback().set("cent_data_in", FUNC(input_buffer_device::read));
	m_pio1->out_ardy_callback().set(m_centronics, FUNC(centronics_device::write_strobe)).invert();

	Z80PIO(config, m_pio2, 16_MHz_XTAL / 4);
	m_pio2->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio2->out_pa_callback().set(FUNC(aussiebyte_state::port20_w));

	z80sio_device& sio1(Z80SIO(config, "sio1", 16_MHz_XTAL / 4));
	sio1.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	sio1.out_wrdya_callback().set(FUNC(aussiebyte_state::sio1_rdya_w));
	sio1.out_wrdyb_callback().set(FUNC(aussiebyte_state::sio1_rdyb_w));

	z80sio_device& sio2(Z80SIO(config, "sio2", 16_MHz_XTAL / 4));
	sio2.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	sio2.out_wrdya_callback().set(FUNC(aussiebyte_state::sio2_rdya_w));
	sio2.out_wrdyb_callback().set(FUNC(aussiebyte_state::sio2_rdyb_w));
	sio2.out_txda_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	sio2.out_dtra_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	sio2.out_rtsa_callback().set("rs232", FUNC(rs232_port_device::write_rts));

	RS232_PORT(config, m_rs232, default_rs232_devices, "keyboard");
	m_rs232->rxd_handler().set("sio2", FUNC(z80sio_device::rxa_w));

	WD2797(config, m_fdc, 16_MHz_XTAL / 16);
	m_fdc->intrq_wr_callback().set(FUNC(aussiebyte_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(aussiebyte_state::fdc_drq_w));
	FLOPPY_CONNECTOR(config, "fdc:0", aussiebyte_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", aussiebyte_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	/* devices */
	SY6545_1(config, m_crtc, 16_MHz_XTAL / 8);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(aussiebyte_state::crtc_update_row));
	m_crtc->set_on_update_addr_change_callback(FUNC(aussiebyte_state::crtc_update_addr));

	MSM5832(config, m_rtc, 32.768_kHz_XTAL);

	/* quickload */
	QUICKLOAD(config, "quickload", "com,cpm", attotime::from_seconds(3)).set_load_callback(FUNC(aussiebyte_state::quickload_cb));

	SOFTWARE_LIST(config, "flop_list").set_original("aussiebyte");
}


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

    Game driver

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


ROM_START(aussieby)
	ROM_REGION(0x4000, "roms", 0) // Size of bank 16
	ROM_LOAD( "knight_boot_0000.u27", 0x0000, 0x1000, CRC(1f200437) SHA1(80d1d208088b325c16a6824e2da605fb2b00c2ce) )

	ROM_REGION(0x800, "chargen", 0)
	ROM_LOAD( "8002.bin", 0x0000, 0x0800, CRC(fdd6eb13) SHA1(a094d416e66bdab916e72238112a6265a75ca690) )
ROM_END

//    YEAR  NAME      PARENT  COMPAT  MACHINE     INPUT       CLASS             INIT        COMPANY        FULLNAME          FLAGS
COMP( 1984, aussieby, 0,      0,      aussiebyte, aussiebyte, aussiebyte_state, empty_init, "SME Systems", "Aussie Byte II", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )



ave_arb.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

AVE Micro Systems ARB chess computer driver, in some regions redistributed
by Chafitz, and in Germany by Sandy Electronic.

Auto Response Board (ARB) overview:
- R6502P CPU @ 2MHz(4MHz XTAL), R6522P VIA
- 2KB RAM(4*2114), cartridge port
- magnetic chessboard, 8*8+12 leds
- PCB label AV001C01 REV A

The electronic magnetic chessboard is the first of its kind. AVE later licensed
it to Fidelity (see fidelity/elite.cpp).

ARB is a romless system, the program ROM is on a cartridge.

Known chess modules:
- Grand Master Series 3
- Grand Master Series 4.0
- Sargon 2.5
- Sargon 3.5 (unofficial)

Other games:
- Avelan (checkers)

Sandy Electronic renamed GMS 3 and GMS 4.0 to "3000 GMS" and "4,0 - 50 S".
Sargon 3.5 was an unofficial module published by them. It was also a free EPROM
upgrade for their customers who were unhappy with GMS 3.

GMS 4.0 included button label stickers for OPTIONS, Verify, Take Back, Clear.

Around 2012, Steve Braid(aka Trilobyte/Steve UK) started manufacturing ARB V2
boards without a module slot. CPU and VIA were replaced with new WDC 14MHz-rated
chips, running at 16MHz.

TODO:
- avelan, gms3, gms4, sargon35 rom labels

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

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/m6502/m6502.h"
#include "cpu/m6502/w65c02s.h"
#include "video/pwm.h"
#include "machine/6522via.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"

#include "speaker.h"
#include "softlist_dev.h"

// internal artwork
#include "ave_arb.lh"


namespace {

class arb_state : public driver_device
{
public:
	arb_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_board(*this, "board"),
		m_via(*this, "via"),
		m_extram(*this, "extram", 0x800, ENDIANNESS_LITTLE),
		m_dac(*this, "dac"),
		m_cart(*this, "cartslot"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// halt button is tied to NMI, reset button to RESET(but only if halt button is held)
	DECLARE_INPUT_CHANGED_MEMBER(reset_button) { update_reset(); }
	DECLARE_INPUT_CHANGED_MEMBER(halt_button) { m_maincpu->set_input_line(M6502_NMI_LINE, newval ? ASSERT_LINE : CLEAR_LINE); update_reset(); }
	void update_reset();

	void arb(machine_config &config);
	void v2(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<sensorboard_device> m_board;
	required_device<via6522_device> m_via;
	memory_share_creator<u8> m_extram;
	required_device<dac_1bit_device> m_dac;
	optional_device<generic_slot_device> m_cart;
	required_ioport_array<2> m_inputs;

	u16 m_inp_mux = 0;
	u16 m_led_select = 0;
	u8 m_led_group = 0;
	u8 m_led_latch = 0;
	u16 m_led_data = 0;

	bool m_altboard = false;

	void main_map(address_map &map) ATTR_COLD;
	void v2_map(address_map &map) ATTR_COLD;

	void init_board(u8 data);
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	void update_display();
	void leds_w(u8 data);
	void control_w(u8 data);
	u8 input_r();
};

void arb_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_led_select));
	save_item(NAME(m_led_group));
	save_item(NAME(m_led_latch));
	save_item(NAME(m_led_data));
}

void arb_state::update_reset()
{
	bool state = m_inputs[1]->read() == 3;

	// RESET goes to 6502+6522
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? ASSERT_LINE : CLEAR_LINE);
	if (state)
		m_via->reset();
}



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

// sensorboard

void arb_state::init_board(u8 data)
{
	// different board setup for checkers
	if (m_altboard)
	{
		for (int i = 0; i < 12; i++)
		{
			m_board->write_piece((i % 4) * 2 + ((i / 4) & 1), i / 4, 13); // white
			m_board->write_piece((i % 4) * 2 + (~(i / 4) & 1), i / 4 + 5, 14); // black
		}
	}
	else
		m_board->preset_chess(data);
}


// cartridge

DEVICE_IMAGE_LOAD_MEMBER(arb_state::cart_load)
{
	u32 size = m_cart->common_get_size("rom");
	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	// extra ram (optional)
	if (image.get_feature("ram"))
		m_maincpu->space(AS_PROGRAM).install_ram(0x0800, 0x0fff, 0x1000, m_extram);

	m_altboard = bool(image.get_feature("altboard"));

	return std::make_pair(std::error_condition(), std::string());
}


// R6522 ports

void arb_state::update_display()
{
	// 12 led column data lines via 3 7475
	u16 mask = 0;
	mask |= (m_led_group & 1) ? 0xf00 : 0;
	mask |= (m_led_group & 2) ? 0x0ff : 0;

	m_led_data = (m_led_data & ~mask) | ((m_led_latch << 8 | m_led_latch) & mask);
	m_display->matrix(m_led_select | 0x200, m_led_data);
}

void arb_state::leds_w(u8 data)
{
	// PA0-PA7: led latch input
	m_led_latch = ~data & 0xff;
	update_display();
}

void arb_state::control_w(u8 data)
{
	// PB0-PB3: 74145 A-D
	// 74145 0-8: input mux, led row select
	m_inp_mux = data & 0xf;
	m_led_select = 1 << (data & 0xf) & 0x1ff;

	// PB4,PB5: led group select
	m_led_group = data >> 4 & 3;
	update_display();

	// PB7: speaker out
	m_dac->write(BIT(data, 7));
}

u8 arb_state::input_r()
{
	u8 data = 0;

	// PA0-PA7: multiplexed inputs
	if (m_inp_mux < 8)
		data = m_board->read_file(m_inp_mux);
	else if (m_inp_mux < 9)
		data = m_inputs[m_inp_mux - 8]->read();

	return ~data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void arb_state::main_map(address_map &map)
{
	// external slot is A0-A14, potential bus conflict with RAM/VIA
	map(0x0000, 0x7fff).mirror(0x8000).r(m_cart, FUNC(generic_slot_device::read_rom));
	map(0x0000, 0x07ff).mirror(0x1000).ram().share("nvram");
	map(0x8000, 0x800f).mirror(0x1ff0).m(m_via, FUNC(via6522_device::map));
}

void arb_state::v2_map(address_map &map)
{
	map(0x0000, 0x7fff).ram().share("nvram"); // BS62LV256
	map(0x8000, 0x800f).mirror(0x1ff0).m(m_via, FUNC(via6522_device::map));
	map(0xa000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( arb )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_7) PORT_NAME("Hint / Black")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_CODE(KEYCODE_6) PORT_NAME("Variable / Clear / White / 6")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_CODE(KEYCODE_5) PORT_NAME("Monitor / Take Back / King / 5")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_4) PORT_NAME("Self Play / Verify / Queen / 4")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_3) PORT_NAME("Change Board / Rook / 3")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_2) PORT_NAME("Change Color / Bishop / 2")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_CODE(KEYCODE_1) PORT_NAME("Change Level / Knight / 1")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_CODE(KEYCODE_0) PORT_NAME("New Game / Options / Pawn / 0")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_F1) PORT_NAME("Reset") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(arb_state::reset_button), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_CODE(KEYCODE_F1) PORT_NAME("Halt") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(arb_state::halt_button), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void arb_state::v2(machine_config &config)
{
	// basic machine hardware
	W65C02S(config, m_maincpu, 16_MHz_XTAL); // W65C02S6TPG-14
	m_maincpu->set_addrmap(AS_PROGRAM, &arb_state::v2_map);

	W65C22S(config, m_via, 16_MHz_XTAL); // W65C22S6TPG-14
	m_via->writepa_handler().set(FUNC(arb_state::leds_w));
	m_via->writepb_handler().set(FUNC(arb_state::control_w));
	m_via->readpa_handler().set(FUNC(arb_state::input_r));
	m_via->irq_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(FUNC(arb_state::init_board));
	m_board->set_spawnpoints(12+2); // +2 checkers pieces
	m_board->set_delay(attotime::from_msec(100));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9+1, 12);
	config.set_default_layout(layout_ave_arb);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void arb_state::arb(machine_config &config)
{
	v2(config);

	// basic machine hardware
	M6502(config.replace(), m_maincpu, 4_MHz_XTAL/2); // R6502P
	m_maincpu->set_addrmap(AS_PROGRAM, &arb_state::main_map);

	MOS6522(config.replace(), m_via, 4_MHz_XTAL/4); // R6522P
	m_via->writepa_handler().set(FUNC(arb_state::leds_w));
	m_via->writepb_handler().set(FUNC(arb_state::control_w));
	m_via->readpa_handler().set(FUNC(arb_state::input_r));
	m_via->irq_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	// cartridge
	GENERIC_CARTSLOT(config, m_cart, generic_linear_slot, "arb");
	m_cart->set_device_load(FUNC(arb_state::cart_load));
	m_cart->set_must_be_loaded(true);

	SOFTWARE_LIST(config, "cart_list").set_original("arb");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( arb )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
	// none here, it's in the module slot
ROM_END

ROM_START( arbv2 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "sargon_4.0", 0x0000, 0x10000, CRC(c519c9e8) SHA1(d7597d50c0f4f9aa6d990c8d3b485e39cb44ff06) ) // AT27C512R
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY, FULLNAME, FLAGS
SYST( 1980, arb,   0,      0,      arb,     arb,   arb_state, empty_init, "AVE Micro Systems", "Auto Response Board", MACHINE_SUPPORTS_SAVE )
SYST( 2012, arbv2, arb,    0,      v2,      arb,   arb_state, empty_init, "hack (Steve Braid)", "ARB V2 Sargon 4.0", MACHINE_SUPPORTS_SAVE )



avigo.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Kevin Thacker,Sandro Ronco
/******************************************************************************

        TI "Avigo" PDA


        system driver

        Documentation:
                Hans B Pufal
                Avigo simulator


        MEMORY MAP:
            0x0000-0x03fff: flash 0 block 0
            0x4000-0x07fff: bank 1
            0x8000-0x0bfff: bank 2
            0xc000-0x0ffff: ram block 0

        Bankswitch:
            Bank 1 is controlled by ports 5 and 6, port 5 is the bank select
            and port 6 is the chip select, the known chip select are:
            - 0: flash 0
            - 1: RAM
            - 3: flash 1
            - 5: flash 2 (optional)
            - 6: VideoRAM
            - 7: flash 0

            Bank 2 have the same behavior but is controlled by ports 7 and 8

        Hardware:
            - Z80 CPU
            - 16c500c UART
            - amd29f080 flash-file memory x 3 (3mb)
            - 128k ram
            - stylus pen
            - touch-pad screen

        Flash:
            The following flash ID are checked by Avigo OS, if the returned
            ID is different the PDA doesn't boot.

            Maker ID    Device ID   Description
            0xb0        0x21        Sharp LH28F004
            0x89        0xa6        Sharp LH28F008
            0x89        0xaa        Sharp LH28F016
            0xb0        0x88        Sharp LH28F032
            0x01        0xa4        AMD AM29F040
            0x01        0xd5        AMD AM29F080
            0x01        0xad        AMD AM29F016

        TODO:
            - Found a better way to emulate the touchscreen panel out of the screen
              area (the six buttons at the bottom)
            - Alarm doesn't work
            - Serial communications and IR port.

            I don't have any documentation on the hardware, so a lot of this
            driver has been written using educated guesswork and a lot of help
            from an existing emulation written by Hans Pufal. Hans's emulator
            is also written from educated guesswork.

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


#include "emu.h"
#include "avigo.h"

#include "screen.h"
#include "speaker.h"

#include "avigo.lh"


#define AVIGO_LOG 0
#define LOG(x) do { if (AVIGO_LOG) logerror x; } while (0)


/*
    IRQ bits (port 3) ordered by priority:

    bit 7: power down request       high priority. When it occurs, clear this bit.
    bit 5: real time clock
    bit 3: uart int
    bit 6: pen int                  An interrupt when pen is pressed against screen.
    bit 4: 1 sec int                used for auto power off
    bit 2: synchronisation link interrupt???keyboard int            ;; check bit 5 of port 1,
    bit 1: ???      (cleared in nmi, and then set again)
    bit 0: not checked, probably unused
*/

void avigo_state::refresh_ints()
{
	if (m_irq!=0)
		m_maincpu->set_input_line(0, HOLD_LINE);
	else
		m_maincpu->set_input_line(0, CLEAR_LINE);
}

/* does not do anything yet */
void avigo_state::tc8521_alarm_int(int state)
{
//#if 0
	m_irq &=~(1<<5);

	if (state)
	{
		m_irq |= (1<<5);
	}

	refresh_ints();
//#endif
}

void avigo_state::com_interrupt(int state)
{
	LOG(("com int\r\n"));

	m_irq &= ~(1<<3);

	if (state)
	{
		m_irq |= (1<<3);
	}

	refresh_ints();
}

void avigo_state::machine_reset()
{
	m_irq = 0;
	m_bank1_l = 0;
	m_bank1_h = 0;
	m_bank2_l = 0;
	m_bank2_h = 0;

	m_bankdev1->set_bank(0);
	m_bankdev2->set_bank(0);
}

void avigo_state::machine_start()
{
	// bank3 always first ram bank
	membank("bank2")->set_base(m_nvram);

	m_warm_start = 1;

	// register for state saving
	save_item(NAME(m_key_line));
	save_item(NAME(m_irq));
	save_item(NAME(m_port2));
	save_item(NAME(m_bank2_l));
	save_item(NAME(m_bank2_h));
	save_item(NAME(m_bank1_l));
	save_item(NAME(m_bank1_h));
	save_item(NAME(m_ad_control_status));
	save_item(NAME(m_ad_value));
	save_item(NAME(m_screen_column));
	save_item(NAME(m_warm_start));
}

void avigo_state::avigo_banked_map(address_map &map)
{
	map(0x0000000, 0x00fffff).mirror(0x0300000).rw("flash0", FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0x0400000, 0x041ffff).mirror(0x03e0000).ram().share("nvram");

	map(0x0c00000, 0x0cfffff).mirror(0x0300000).rw(m_flash1, FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0x1400000, 0x14fffff).mirror(0x0300000).rw("flash2", FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0x1c00000, 0x1cfffff).mirror(0x0300000).rw("flash0", FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));

	map(0x1800000, 0x1803fff).mirror(0x03fc000).rw(FUNC(avigo_state::vid_memory_r), FUNC(avigo_state::vid_memory_w));
}

void avigo_state::avigo_mem(address_map &map)
{
	map(0x0000, 0x3fff).rw("flash0", FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0x4000, 0x7fff).rw(m_bankdev1, FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0x8000, 0xbfff).rw(m_bankdev2, FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0xc000, 0xffff).bankrw("bank2");
}


uint8_t avigo_state::key_data_read_r()
{
	uint8_t data = 0x0f;

	if (!(m_key_line & 0x01))
	{
		data &= ioport("LINE0")->read();
	}

	if (!(m_key_line & 0x02))
	{
		data &= ioport("LINE1")->read();
	}

	if (!(m_key_line & 0x04))
	{
		data &= ioport("LINE2")->read();
	}

	/* bit 3 is cold/warm start */
	data &= ((m_warm_start<<3) ^ 0xff);

	/* if bit 5 is clear shows synchronisation logo! */
	data |= (1<<5);

	return data;
}


/* set key line(s) to read */
/* bit 0 set for line 0, bit 1 set for line 1, bit 2 set for line 2 */
void avigo_state::set_key_line_w(uint8_t data)
{
	/* 5, 101, read back 3 */
	m_key_line = data;

	m_warm_start = BIT(data, 3);
}

uint8_t avigo_state::irq_r()
{
	return m_irq;
}

void avigo_state::irq_w(uint8_t data)
{
	m_irq &= data;

	refresh_ints();
}

void avigo_state::port2_w(uint8_t data)
{
	/*
	    bit 4     LCD backlight on/off
	    bit 5-6   source select for a/d converter
	*/

	if ((m_port2 ^ data) & 0x10)
		popmessage("Backlight %s", data & 0x10 ? "on" : "off");

	m_port2 = data;
}

uint8_t avigo_state::bank1_r(offs_t offset)
{
	return offset ? m_bank1_h: m_bank1_l;
}

uint8_t avigo_state::bank2_r(offs_t offset)
{
	return offset ? m_bank2_h: m_bank2_l;
}

void avigo_state::bank1_w(offs_t offset, uint8_t data)
{
	if (offset)
	{
		LOG(("bank1 h w: %04x\n", data));
		m_bank1_h = data;
	}
	else
	{
		LOG(("bank1 l w: %04x\n", data));
		m_bank1_l = data & 0x3f;
	}

	m_bankdev1->set_bank(((m_bank1_h & 0x07) << 8) | m_bank1_l);
}

void avigo_state::bank2_w(offs_t offset, uint8_t data)
{
	if (offset)
	{
		LOG(("bank2 h w: %04x\n", data));
		m_bank2_h = data;
	}
	else
	{
		LOG(("bank2 l w: %04x\n", data));
		m_bank2_l = data & 0x3f;
	}

	m_bankdev2->set_bank(((m_bank2_h & 0x07) << 8) | m_bank2_l);
}

uint8_t avigo_state::ad_control_status_r()
{
	LOG(("avigo ad control read %02x\n", (int) m_ad_control_status));
	return m_ad_control_status;
}


void avigo_state::ad_control_status_w(uint8_t data)
{
	LOG(("avigo ad control w %02x\n",data));

	switch (m_port2 & 0x60)
	{
		case 0x20:
			// read main battery status valid range 0x000-0x3ff
			LOG(("a/d main battery status\n"));

			m_ad_value = 0x3ff;
			break;
		case 0x40:
			// read backup battery status valid range 0x000-0x3ff
			LOG(("a/d backup battery status\n"));

			m_ad_value = 0x3ff;
			break;
		case 0x60:
			if ((data & 0x070)==0x070)
			{
				/* bit 3 appears to select between 1 = x coord, 0 = y coord */
				/* when 6,5,4 = 1 */
				if ((data & 0x08)!=0)
				{
					LOG(("a/d select x coordinate\n"));
					LOG(("x coord: %d\n", ioport("POSX")->read()));

					/* on screen range 0x060->0x03a0 */
					if (ioport("LINE3")->read() & 0x01)
					{
						/* this might not be totally accurate because hitable screen
						area may include the border around the screen! */
						m_ad_value = ioport("POSX")->read();
					}
					else
					{
						m_ad_value = 0;
					}

					LOG(("ad value: %d\n",m_ad_value));

				}
				else
				{
					/* in the avigo rom, the y coordinate is inverted! */
					/* therefore a low value would be near the bottom of the display,
					and a high value at the top */

					/* total valid range 0x044->0x03a6 */
					/* 0x0350 is also checked */

					/* assumption 0x044->0x0350 is screen area and
					0x0350->0x03a6 is panel at bottom */

					LOG(("a/d select y coordinate\n"));
					LOG(("y coord: %d\n", ioport("POSY")->read()));

					if (ioport("LINE3")->read() & 0x01)
					{
						m_ad_value = ioport("POSY")->read();
					}
					else
					{
						m_ad_value = 0;
					}

					LOG(("ad value: %d\n",m_ad_value));
				}
			}
	}

	/* bit 0: 1 if a/d complete, 0 if a/d not complete */
	m_ad_control_status = data | 1;
}

uint8_t avigo_state::ad_data_r()
{
	uint8_t data = 0;

	/* original */

	/* status AND   11110111 */
	/* status OR    01110000 -> C20F */

	switch (m_ad_control_status & 0x078)
	{
		/* x1110xxx */
		/* read upper 4 bits of 10 bit A/D number */
		case 0x060:
		case 0x070:
		case 0x078:
		{
			/* upper 4 bits of 10 bit A/D number in bits 7-4 of data */
			/* bit 0 must be 0, bit 1 must be 0 */
			/* bit 3 must be 1. bit 2 can have any value */

			LOG(("a/d read upper 4 bits\n"));
			data = ((m_ad_value>>6) & 0x0f)<<4;
			data |= 8;
		}
		break;

		/* x0111xxx */
		case 0x020:
		case 0x038:
		{
			/* lower 6 bits of 10-bit A/D number in bits 7-2 of data */
			/* bit 0 must be 1, bit 1 must be 0 */

			LOG(("a/d lower 6-bits\n"));
			data = ((m_ad_value & 0x03f)<<2);
			data |= 1;
		}
		break;

		default:
			break;
	}

	/* x coord? */
	/* wait for bit 0 of status to become 1 */
	/* read data -> d */


	/* C20f AND 10111111 */
	/* C20f OR  00001000 */
	/* x0111xxx */

	/* bit 1 must be 0, bit 0 must be 1 */
	/* read data -> e */

	/* upper 4 bits of d contain data */
	/* bits 0 and 1 do not contain data of e, but all other bits do */

	/* get bit 5 and 6 of d */
	/* and put into bit 0 and 1 of e */

	/* C20f OR  01000000 */
	/* x1111xxx */

	/* y coord? */
	/* bit 0 must be 0, bit 1 must be 0 */
	/* bit 3 must be 1. bit 2 can have any value */
	/* read data -> d */

	/* C20f AND  10111111 */
	/* x0111xxx */

	/* bit 1 must be 0, bit 0 must be 1 */
	/* read data -> e */


	/* original and 1111100 */
	/* original or  1111000 */
	/* 1111x00 */



	/* if fails! */
	/* original */
	/* AND 1001100 */
	/* OR  1001000 */
	/* 1001x00 */


	/* AND 1101100 */
	/* OR  1101000 */
	/* 1101x00 */

	/* 1111x00 */

	LOG(("avigo ad read %02x\n",data));

	return data;
}


void avigo_state::speaker_w(uint8_t data)
{
	/* Speaker output state */
	m_speaker->level_w(BIT(data, 3));
}


/* port 0x04:

  bit 7: ??? if set, does a write 0x00 to 0x02e */

	/* port 0x029:
	port 0x02e */
uint8_t avigo_state::port_04_r()
{
	/* must be both 0 for it to boot! */
	return 0x0ff^((1<<7) | (1<<5));
}



void avigo_state::avigo_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x001, 0x001).rw(FUNC(avigo_state::key_data_read_r), FUNC(avigo_state::set_key_line_w));
	map(0x002, 0x002).w(FUNC(avigo_state::port2_w));
	map(0x003, 0x003).rw(FUNC(avigo_state::irq_r), FUNC(avigo_state::irq_w));
	map(0x004, 0x004).r(FUNC(avigo_state::port_04_r));
	map(0x005, 0x006).rw(FUNC(avigo_state::bank1_r), FUNC(avigo_state::bank1_w));
	map(0x007, 0x008).rw(FUNC(avigo_state::bank2_r), FUNC(avigo_state::bank2_w));
	map(0x009, 0x009).rw(FUNC(avigo_state::ad_control_status_r), FUNC(avigo_state::ad_control_status_w));
	map(0x010, 0x01f).rw("rtc", FUNC(tc8521_device::read), FUNC(tc8521_device::write));
	map(0x028, 0x028).w(FUNC(avigo_state::speaker_w));
	map(0x02d, 0x02d).r(FUNC(avigo_state::ad_data_r));
	map(0x030, 0x037).rw(m_uart, FUNC(ns16550_device::ins8250_r), FUNC(ns16550_device::ins8250_w));
}


INPUT_CHANGED_MEMBER( avigo_state::pen_irq )
{
	LOG(("pen pressed interrupt\n"));

	// an irq is generated when the pen is pressed down on the screen
	// or lifted up from the screen
	m_irq |= (1<<6);

	refresh_ints();
}

INPUT_CHANGED_MEMBER( avigo_state::pen_move_irq )
{
	// an irq is generated when the pen is down on the screen and is being moved
	if (ioport("LINE3")->read() & 0x01)
	{
		LOG(("pen move interrupt\n"));
		m_irq |= (1<<6);

		refresh_ints();
	}
}

INPUT_CHANGED_MEMBER( avigo_state::kb_irq )
{
	LOG(("key pressed interrupt\n"));

	if (!newval)
	{
		m_irq |= (1<<2);

		refresh_ints();
	}
}

INPUT_CHANGED_MEMBER( avigo_state::power_down_irq )
{
	if(newval)
	{
		m_irq |= (1<<7);

		refresh_ints();
	}
}

static INPUT_PORTS_START(avigo)
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("PAGE UP")      PORT_CODE(KEYCODE_PGUP) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(avigo_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("PAGE DOWN")    PORT_CODE(KEYCODE_PGDN) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(avigo_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("LIGHT")        PORT_CODE(KEYCODE_L)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(avigo_state::kb_irq), 0)
	PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("TO DO")        PORT_CODE(KEYCODE_T)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(avigo_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("ADDRESS")      PORT_CODE(KEYCODE_A)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(avigo_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("SCHEDULE")     PORT_CODE(KEYCODE_S)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(avigo_state::kb_irq), 0)
	PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("MEMO")         PORT_CODE(KEYCODE_M)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(avigo_state::kb_irq), 0)
	PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Pen/Stylus pressed") PORT_CODE(KEYCODE_ENTER) PORT_CODE(MOUSECODE_BUTTON1)  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(avigo_state::pen_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("?? Causes a NMI") PORT_CODE(KEYCODE_W) PORT_CODE(JOYCODE_BUTTON2)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Power Down")       PORT_CODE(KEYCODE_Q) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(avigo_state::power_down_irq), 0)

	/* these two ports are used to emulate the position of the pen/stylus on the screen */
	PORT_START("POSX") /* Mouse - X AXIS */
	PORT_BIT(0x3ff, 0x060, IPT_LIGHTGUN_X) PORT_SENSITIVITY(100) PORT_CROSSHAIR(X, 1, 0, 0) PORT_MINMAX(0x060, 0x3a0) PORT_KEYDELTA(10) PORT_PLAYER(1)              PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(avigo_state::pen_move_irq), 0)

	PORT_START("POSY") /* Mouse - Y AXIS */
	PORT_BIT(0x3ff, 0x044, IPT_LIGHTGUN_Y) PORT_SENSITIVITY(100) PORT_CROSSHAIR(Y, 1, 0, 0) PORT_MINMAX(0x044, 0x3a6) PORT_INVERT PORT_KEYDELTA(10) PORT_PLAYER(1)  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(avigo_state::pen_move_irq), 0)
INPUT_PORTS_END

/* F4 Character Displayer */
static const gfx_layout avigo_charlayout =
{
	8, 16,                  /* 8 x 16 characters */
	96,                 /* 96 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16                    /* every char takes 16 bytes */
};

static const gfx_layout avigo_8_by_14 =
{
	8, 14,                  /* 8 x 16 characters */
	1024,                   /* 1024 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*32                    /* every char takes 32 bytes */
};

static const gfx_layout avigo_16_by_15 =
{
	16, 15,                 /* 8 x 16 characters */
	1024,                   /* 1024 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
	/* y offsets */
	{ 1*16, 2*16, 3*16, 4*16, 5*16, 6*16, 7*16, 8*16, 9*16, 10*16, 11*16, 12*16, 13*16, 14*16, 15*16 },
	16*16                   /* every char takes 16 bytes */
};

static const gfx_layout avigo_15_by_16 =
{
	15, 16,                 /* 8 x 16 characters */
	1024,                   /* 1024 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 1*16, 2*16, 3*16, 4*16, 5*16, 6*16, 7*16, 8*16, 9*16, 10*16, 11*16, 12*16, 13*16, 14*16, 15*16 },
	/* y offsets */
	{ 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 },
	16*16                   /* every char takes 16 bytes */
};

static const gfx_layout avigo_8_by_8 =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8 },
	16*16                   /* every char takes 16 bytes */
};

static const gfx_layout avigo_6_by_8 =
{
	6, 8,                   /* 6 x 8 characters */
	255,                    /* 255 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	/* y offsets */
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	16*16                   /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_avigo )
	GFXDECODE_ENTRY( "flash0", 0x08992, avigo_charlayout, 0, 1 )
	GFXDECODE_ENTRY( "flash0", 0x0c020, avigo_8_by_14, 0, 1 )
	GFXDECODE_ENTRY( "flash0", 0x0c020, avigo_16_by_15, 0, 1 )
	GFXDECODE_ENTRY( "flash0", 0x14020, avigo_15_by_16, 0, 1 )
	GFXDECODE_ENTRY( "flash0", 0x1c020, avigo_8_by_8, 0, 1 )
	GFXDECODE_ENTRY( "flash0", 0x1e020, avigo_6_by_8, 0, 1 )
GFXDECODE_END


TIMER_DEVICE_CALLBACK_MEMBER(avigo_state::avigo_scan_timer)
{
	m_irq |= (1<<1);

	refresh_ints();
}

TIMER_DEVICE_CALLBACK_MEMBER(avigo_state::avigo_1hz_timer)
{
	m_irq |= (1<<4);

	refresh_ints();
}

QUICKLOAD_LOAD_MEMBER(avigo_state::quickload_cb)
{
	const char *systemname = machine().system().name;
	uint32_t first_app_page = (0x50000>>14);
	int app_page;

	// german and spanish language are 4 pages bigger than other
	if (strcmp( systemname, "avigo_de" ) == 0 || strcmp( systemname, "avigo_es" ) == 0)
		first_app_page += 4;

	// search the first empty page
	for (app_page = first_app_page + 1; app_page<0x40; app_page++)
	{
		bool empty_page = true;

		for (int offset=0; offset<0x4000; offset++)
		{
			if (m_flash1->read_raw((app_page<<14) + offset) != 0xff)
			{
				empty_page = false;
				break;
			}
		}

		if (empty_page)
			break;
	}

	// if there is the required free space installs the application
	if ((app_page + (image.length()>>14)) < 0x40)
	{
		logerror("Application loaded at 0x%05x-0x%05x\n", app_page<<14, (app_page<<14) + (uint32_t)image.length());

		// copy app file into flash memory
		image.fread(m_flash1->base() + (app_page<<14), image.length());

		// update the application ID
		m_flash1->write_raw((app_page<<14) + 0x1a5, 0x80 + (app_page - (first_app_page>>14)));

		// reset the CPU for allow at the Avigo OS to recognize the installed app
		m_warm_start = 1;
		m_maincpu->reset();
		return std::make_pair(std::error_condition(), std::string());
	}

	return std::make_pair(image_error::INVALIDLENGTH, std::string());
}

void avigo_state::nvram_init(nvram_device &nvram, void *base, size_t size)
{
	m_warm_start = 0;
	memset(base, 0x00, size);
}

void avigo_state::avigo(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 4000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &avigo_state::avigo_mem);
	m_maincpu->set_addrmap(AS_IO, &avigo_state::avigo_io);
	config.set_maximum_quantum(attotime::from_hz(60));

	NS16550(config, m_uart, XTAL(1'843'200));
	m_uart->out_tx_callback().set(m_serport, FUNC(rs232_port_device::write_txd));
	m_uart->out_dtr_callback().set(m_serport, FUNC(rs232_port_device::write_dtr));
	m_uart->out_rts_callback().set(m_serport, FUNC(rs232_port_device::write_rts));
	m_uart->out_int_callback().set(FUNC(avigo_state::com_interrupt));

	RS232_PORT(config, m_serport, default_rs232_devices, nullptr);
	m_serport->rxd_handler().set(m_uart, FUNC(ins8250_uart_device::rx_w));
	m_serport->dcd_handler().set(m_uart, FUNC(ins8250_uart_device::dcd_w));
	m_serport->dsr_handler().set(m_uart, FUNC(ins8250_uart_device::dsr_w));
	m_serport->ri_handler().set(m_uart, FUNC(ins8250_uart_device::ri_w));
	m_serport->cts_handler().set(m_uart, FUNC(ins8250_uart_device::cts_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(avigo_state::screen_update));
	screen.set_size(AVIGO_SCREEN_WIDTH, AVIGO_SCREEN_HEIGHT + AVIGO_PANEL_HEIGHT);
	screen.set_visarea_full();
	screen.set_palette(m_palette);

	config.set_default_layout(layout_avigo);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_avigo);
	PALETTE(config, m_palette, palette_device::MONOCHROME_INVERTED);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* real time clock */
	tc8521_device &rtc(TC8521(config, "rtc", XTAL(32'768)));
	rtc.out_alarm_callback().set(FUNC(avigo_state::tc8521_alarm_int));

	/* flash ROMs */
	AMD_29F080(config, "flash0");
	AMD_29F080(config, "flash1");
	AMD_29F080(config, "flash2");

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("128K");

	ADDRESS_MAP_BANK(config, "bank0").set_map(&avigo_state::avigo_banked_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);
	ADDRESS_MAP_BANK(config, "bank1").set_map(&avigo_state::avigo_banked_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);

	NVRAM(config, "nvram").set_custom_handler(FUNC(avigo_state::nvram_init));

	// IRQ 1 is used for scan the pen and for cursor blinking
	TIMER(config, "scan_timer").configure_periodic(FUNC(avigo_state::avigo_scan_timer), attotime::from_hz(50));

	// IRQ 4 is generated every second, used for auto power off
	TIMER(config, "1hz_timer").configure_periodic(FUNC(avigo_state::avigo_1hz_timer), attotime::from_hz(1));

	/* quickload */
	QUICKLOAD(config, "quickload", "app").set_load_callback(FUNC(avigo_state::quickload_cb));
}


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

  Game driver(s)

***************************************************************************/
ROM_START(avigo)
	ROM_REGION(0x100000, "flash0", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS( 0, "v1004", "v1.004" )
	ROM_SYSTEM_BIOS( 1, "v1002", "v1.002" )
	ROM_SYSTEM_BIOS( 2, "v100", "v1.00" )

	ROMX_LOAD("os_1004.rom", 0x000000, 0x0100000, CRC(62acd55c) SHA1(b2be12f5cc1053b6026bff2a265146ba831a7ffa), ROM_BIOS(0))
	ROMX_LOAD("os_1002.rom", 0x000000, 0x0100000, CRC(484bb95c) SHA1(ddc28f22f8cbc99f60f91c58ee0e2d15170024fb), ROM_BIOS(1))
	ROMX_LOAD("os_100.rom", 0x000000, 0x0100000, CRC(13ea7b38) SHA1(85566ff142d86d504ac72613f169d8758e2daa09), ROM_BIOS(2))

	ROM_REGION(0x100000, "flash1", ROMREGION_ERASEFF)
	ROMX_LOAD("english_1004.rom", 0x000000, 0x050000, CRC(c9c3a225) SHA1(7939993a5615ca59ff2047e69b6d85122d437dca), ROM_BIOS(0))
	ROMX_LOAD("english_1002.rom", 0x000000, 0x050000, CRC(31cab0ac) SHA1(87d337830506a12514a4beb9a8502a0de94816f2), ROM_BIOS(1))
	ROMX_LOAD("english_100.rom",  0x000000, 0x050000, CRC(e2824b44) SHA1(3252454b05c3d3a4d7df1cb48dc3441ae82f2b1c), ROM_BIOS(2))
ROM_END

ROM_START(avigo_de)
	ROM_REGION(0x100000, "flash0", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS( 0, "v1004", "v1.004" )
	ROM_SYSTEM_BIOS( 1, "v1002", "v1.002" )
	ROM_SYSTEM_BIOS( 2, "v100", "v1.00" )

	ROMX_LOAD("os_1004.rom", 0x000000, 0x0100000, CRC(62acd55c) SHA1(b2be12f5cc1053b6026bff2a265146ba831a7ffa), ROM_BIOS(0))
	ROMX_LOAD("os_1002.rom", 0x000000, 0x0100000, CRC(484bb95c) SHA1(ddc28f22f8cbc99f60f91c58ee0e2d15170024fb), ROM_BIOS(1))
	ROMX_LOAD("os_100.rom", 0x000000, 0x0100000, CRC(13ea7b38) SHA1(85566ff142d86d504ac72613f169d8758e2daa09), ROM_BIOS(2))

	ROM_REGION(0x100000, "flash1", ROMREGION_ERASEFF)
	ROMX_LOAD("german_1004.rom", 0x000000, 0x060000, CRC(0fa437b3) SHA1(e9352aa8fee6d93b898412bd129452b82baa9a21), ROM_BIOS(0))
	ROMX_LOAD("german_1002.rom", 0x000000, 0x060000, CRC(c6bf07ba) SHA1(d3185687aa510f6c3b3ab3baaabe7e8ce1a79e3b), ROM_BIOS(1))
	ROMX_LOAD("german_100.rom",  0x000000, 0x060000, CRC(117d9189) SHA1(7e959ab1381ba831821fcf87973b25d87f12d34e), ROM_BIOS(2))
ROM_END

ROM_START(avigo_fr)
	ROM_REGION(0x100000, "flash0", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS( 0, "v1004", "v1.004" )
	ROM_SYSTEM_BIOS( 1, "v1002", "v1.002" )
	ROM_SYSTEM_BIOS( 2, "v100", "v1.00" )

	ROMX_LOAD("os_1004.rom", 0x000000, 0x0100000, CRC(62acd55c) SHA1(b2be12f5cc1053b6026bff2a265146ba831a7ffa), ROM_BIOS(0))
	ROMX_LOAD("os_1002.rom", 0x000000, 0x0100000, CRC(484bb95c) SHA1(ddc28f22f8cbc99f60f91c58ee0e2d15170024fb), ROM_BIOS(1))
	ROMX_LOAD("os_100.rom", 0x000000, 0x0100000, CRC(13ea7b38) SHA1(85566ff142d86d504ac72613f169d8758e2daa09), ROM_BIOS(2))

	ROM_REGION(0x100000, "flash1", ROMREGION_ERASEFF)
	ROMX_LOAD("french_1004.rom", 0x000000, 0x050000, CRC(5e4d90f7) SHA1(07df3af8a431ba65e079d6c987fb5d544f6541d8), ROM_BIOS(0))
	ROMX_LOAD("french_1002.rom", 0x000000, 0x050000,CRC(caa3eb91) SHA1(ab199986de301d933f069a5e1f5150967e1d7f59), ROM_BIOS(1))
	ROMX_LOAD("french_100.rom",  0x000000, 0x050000, CRC(fffa2345) SHA1(399447cede3cdd0be768952cb24f7e4431147e3d), ROM_BIOS(2))
ROM_END

ROM_START(avigo_es)
	ROM_REGION(0x100000, "flash0", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS( 0, "v1004", "v1.004" )
	ROM_SYSTEM_BIOS( 1, "v1002", "v1.002" )
	ROM_SYSTEM_BIOS( 2, "v100", "v1.00" )

	ROMX_LOAD("os_1004.rom", 0x000000, 0x0100000, CRC(62acd55c) SHA1(b2be12f5cc1053b6026bff2a265146ba831a7ffa), ROM_BIOS(0))
	ROMX_LOAD("os_1002.rom", 0x000000, 0x0100000, CRC(484bb95c) SHA1(ddc28f22f8cbc99f60f91c58ee0e2d15170024fb), ROM_BIOS(1))
	ROMX_LOAD("os_100.rom", 0x000000, 0x0100000, CRC(13ea7b38) SHA1(85566ff142d86d504ac72613f169d8758e2daa09), ROM_BIOS(2))

	ROM_REGION(0x100000, "flash1", ROMREGION_ERASEFF)
	ROMX_LOAD("spanish_1004.rom", 0x000000, 0x060000, CRC(235a7f8d) SHA1(94da4ecafb54dcd5d80bc5063cb4024e66e6a21f), ROM_BIOS(0))
	ROMX_LOAD("spanish_1002.rom", 0x000000, 0x060000, CRC(a6e80cc4) SHA1(e741657558c11f7bce646ba3d7b5f845bfa275b7), ROM_BIOS(1))
	ROMX_LOAD("spanish_100.rom",  0x000000, 0x060000, CRC(953a5276) SHA1(b9ba1dbdc2127b1ef419c911ef66313024a7351a), ROM_BIOS(2))
ROM_END

ROM_START(avigo_it)
	ROM_REGION(0x100000, "flash0", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS( 0, "v1004", "v1.004" )
	ROM_SYSTEM_BIOS( 1, "v1002", "v1.002" )
	ROM_SYSTEM_BIOS( 2, "v100", "v1.00" )

	ROMX_LOAD("os_1004.rom", 0x000000, 0x0100000, CRC(62acd55c) SHA1(b2be12f5cc1053b6026bff2a265146ba831a7ffa), ROM_BIOS(0))
	ROMX_LOAD("os_1002.rom", 0x000000, 0x0100000, CRC(484bb95c) SHA1(ddc28f22f8cbc99f60f91c58ee0e2d15170024fb), ROM_BIOS(1))
	ROMX_LOAD("os_100.rom", 0x000000, 0x0100000, CRC(13ea7b38) SHA1(85566ff142d86d504ac72613f169d8758e2daa09), ROM_BIOS(2))

	ROM_REGION(0x100000, "flash1", ROMREGION_ERASEFF)
	ROMX_LOAD("italian_1004.rom", 0x000000, 0x050000, CRC(fb7941ec) SHA1(230e8346a3b0da1ee24568ec090ce6860ebfe995), ROM_BIOS(0))
	ROMX_LOAD("italian_1002.rom", 0x000000, 0x050000, CRC(093bc032) SHA1(2c75d950d356a7fd1d058808e5f0be8e15b8ea2a), ROM_BIOS(1))
	ROMX_LOAD("italian_100.rom",  0x000000, 0x050000, CRC(de359218) SHA1(6185727aba8ffc98723f2df74dda388fd0d70cc9), ROM_BIOS(2))
ROM_END

//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY              FULLNAME                     FLAGS
COMP( 1997, avigo,    0,      0,      avigo,   avigo, avigo_state, empty_init, "Texas Instruments", "TI Avigo 10 PDA",           MACHINE_SUPPORTS_SAVE)
COMP( 1997, avigo_de, avigo,  0,      avigo,   avigo, avigo_state, empty_init, "Texas Instruments", "TI Avigo 10 PDA (German)",  MACHINE_SUPPORTS_SAVE)
COMP( 1997, avigo_fr, avigo,  0,      avigo,   avigo, avigo_state, empty_init, "Texas Instruments", "TI Avigo 10 PDA (French)",  MACHINE_SUPPORTS_SAVE)
COMP( 1997, avigo_es, avigo,  0,      avigo,   avigo, avigo_state, empty_init, "Texas Instruments", "TI Avigo 10 PDA (Spanish)", MACHINE_SUPPORTS_SAVE)
COMP( 1997, avigo_it, avigo,  0,      avigo,   avigo, avigo_state, empty_init, "Texas Instruments", "TI Avigo 10 PDA (Italian)", MACHINE_SUPPORTS_SAVE)



aviion88k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * Data General AViiON M88k systems.
 *
 * Sources:
 *  - https://archive.org/details/Aviion530Docs/40020761
 *
 * TODO:
 *  - everything
 */

#include "emu.h"

// processors and memory
#include "cpu/m88000/m88000.h"
#include "machine/ram.h"
#include "machine/28fxxx.h"

// i/o devices
#include "machine/timekpr.h"
#include "machine/mc68681.h"
#include "machine/53c7xx.h"
#include "machine/scn_pci.h"
#include "machine/scnxx562.h"

// busses and connectors
#include "machine/nscsi_bus.h"
#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"
#include "bus/rs232/rs232.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "bus/pc_kbd/keyboards.h"

#include "sound/spkrdev.h"
#include "speaker.h"

#include "debugger.h"

//#define VERBOSE (LOG_GENERAL)
#include "logmacro.h"

namespace {

class aviion88k_state : public driver_device
{
public:
	aviion88k_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_prom(*this, "prom%u", 0U)
		, m_ram(*this, "ram")
		, m_novram(*this, "novram")
		, m_uart(*this, "uart")
		, m_kbdc(*this, "kbdc")
		, m_duart(*this, "duart%u", 0U)
		, m_async(*this, { "console_port", "seriala", "mouse_port", "serialb" })
		, m_duscc(*this, "duscc")
		, m_scsibus(*this, "scsi")
		, m_scsi(*this, "scsi:7:ncr53c700")
		, m_speaker(*this, "speaker")
		, m_leds(*this, "CR%u", 1U)
		, m_mbus(*this, "mbus")
	{
	}

	// machine config
	void aviion_4600(machine_config &config);

	void init();

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// address maps
	void cpu_map(address_map &map) ATTR_COLD;

	void pit_timer(s32 param) { LOG("pit_timer<%d> expired\n", param); }

	template <unsigned N> u32 pit_cnt_r() { return m_pit[N]->enabled() ? m_pit[N]->elapsed().as_ticks(m_cpu->clock()) : 0; }
	template <unsigned N> u32 pit_sts_r() { return m_pit_cmd[N]; }
	template <unsigned N> void pit_cnt_w(u32 data) { LOG("pit_cnt_w<%d> 0x%08x\n", N, data); m_pit_cnt[N] = data & 0xffffff00U; }
	template <unsigned N> void pit_cmd_w(u32 data)
	{
		LOG("pit_cmd_w<%d> 0x%x\n", N, data & 15); m_pit_cmd[N] = data & 15;

		// reset
		if (BIT(data, 0))
			m_pit[N]->adjust(attotime::from_ticks(-m_pit_cnt[N], m_cpu->clock()), N);

		// count enable
		if (BIT(data, 3) && !m_pit[N]->enabled())
			m_pit[N]->enable(true);
		else if (!BIT(data, 3) && m_pit[N]->enabled())
			m_pit[N]->enable(false);
	}

private:
	// processors and memory
	required_device<mc88100_device> m_cpu;
	required_device_array<intel_28f010_device, 4> m_prom;
	required_device<ram_device> m_ram;

	// i/o devices
	required_device<timekeeper_device> m_novram;
	required_device<scn_pci_device> m_uart;
	required_device<pc_kbdc_device> m_kbdc;
	required_device_array<scn2681_device, 2> m_duart;
	required_device_array<rs232_port_device, 4> m_async;
	required_device<duscc68562_device> m_duscc;
	required_device<nscsi_bus_device> m_scsibus;
	required_device<ncr53c7xx_device> m_scsi;
	required_device<speaker_sound_device> m_speaker;

	output_finder<3> m_leds;

	memory_view m_mbus;

	u32 m_ucs = 0;

	emu_timer *m_pit[4]{};

	u32 m_pit_cmd[4]{};
	u32 m_pit_cnt[4] = {};
};

void aviion88k_state::machine_start()
{
	m_leds.resolve();

	for (emu_timer *&pit : m_pit)
		pit = timer_alloc(FUNC(aviion88k_state::pit_timer), this);
}

void aviion88k_state::machine_reset()
{
	// disable mbus address decode
	m_mbus.select(0);

	m_ucs = 0b000'0110'0001'1011;
}

void aviion88k_state::init()
{
	// map the configured ram
	m_mbus[1].install_ram(0x00000000, m_ram->mask(), m_ram->pointer());
}

void aviion88k_state::cpu_map(address_map &map)
{
	map(0x00000000, 0xffc7ffff).view(m_mbus);

	/*
	 * utility space (4M) 0xffc0'0000-ffff'ffff
	 *
	 * madv=0: only utility space available, mapped to 0x0000'0000-ffc7'ffff
	 * madv=1: utility space 0xffc7'0000-ffc7'ffff
	 */
	// mbus address decode disabled
	m_mbus[0](0x00000000, 0x0007ffff).rw(m_prom[0], FUNC(intel_28f010_device::read), FUNC(intel_28f010_device::write)).mirror(0xffc00000).umask32(0xff000000);
	m_mbus[0](0x00000000, 0x0007ffff).rw(m_prom[1], FUNC(intel_28f010_device::read), FUNC(intel_28f010_device::write)).mirror(0xffc00000).umask32(0x00ff0000);
	m_mbus[0](0x00000000, 0x0007ffff).rw(m_prom[2], FUNC(intel_28f010_device::read), FUNC(intel_28f010_device::write)).mirror(0xffc00000).umask32(0x0000ff00);
	m_mbus[0](0x00000000, 0x0007ffff).rw(m_prom[3], FUNC(intel_28f010_device::read), FUNC(intel_28f010_device::write)).mirror(0xffc00000).umask32(0x000000ff);

	// mbus address decode enabled
	m_mbus[1](0xffc00000, 0xffc7ffff).rw(m_prom[0], FUNC(intel_28f010_device::read), FUNC(intel_28f010_device::write)).umask32(0xff000000);
	m_mbus[1](0xffc00000, 0xffc7ffff).rw(m_prom[1], FUNC(intel_28f010_device::read), FUNC(intel_28f010_device::write)).umask32(0x00ff0000);
	m_mbus[1](0xffc00000, 0xffc7ffff).rw(m_prom[2], FUNC(intel_28f010_device::read), FUNC(intel_28f010_device::write)).umask32(0x0000ff00);
	m_mbus[1](0xffc00000, 0xffc7ffff).rw(m_prom[3], FUNC(intel_28f010_device::read), FUNC(intel_28f010_device::write)).umask32(0x000000ff);

	map(0xfff8'0000, 0xfff8'1fff).rw(m_novram, FUNC(mk48t12_device::read), FUNC(mk48t12_device::write)).umask32(0x000000ff);
	map(0xfff8'2000, 0xfff8'203f).rw(m_duart[0], FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask32(0x000000ff);
	map(0xfff8'2040, 0xfff8'207f).rw(m_duart[1], FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask32(0x000000ff);
	map(0xfff8'2800, 0xfff8'280f).rw(m_uart, FUNC(scn2661a_device::read), FUNC(scn2661a_device::write)).umask32(0x000000ff);

	map(0xfff8'3100, 0xfff8'3103).lw32(
		[this](u32 data)
		{
			if (!BIT(data, 0))
				m_duart[0]->reset();
			if (!BIT(data, 1))
				m_duart[1]->reset();

			// reset the keyboard or the uart?
			if (!BIT(data, 3))
			{
				LOG("uart reset\n");
				m_uart->reset();
			}
		}, "srst_w");

	map(0xfff8'7000, 0xfff8'7003).lr32([this]() { return m_ucs; }, "ucs_r");
	map(0xfff8'8000, 0xfff8'8003).lw32([this](u32 data) { LOG("madv %d\n", BIT(data, 1)); if (BIT(data, 1)) m_mbus.select(BIT(data, 1)); }, "ccs_w");
	map(0xfff8'8018, 0xfff8'801b).lr32([]() { return 0xa1; }, "whoami_r");

	map(0xfff8'f004, 0xfff8'f007).rw(FUNC(aviion88k_state::pit_cnt_r<0>), FUNC(aviion88k_state::pit_cnt_w<0>));
	map(0xfff8'f008, 0xfff8'f00b).rw(FUNC(aviion88k_state::pit_cnt_r<1>), FUNC(aviion88k_state::pit_cnt_w<1>));
	map(0xfff8'f010, 0xfff8'f013).rw(FUNC(aviion88k_state::pit_cnt_r<2>), FUNC(aviion88k_state::pit_cnt_w<2>));
	map(0xfff8'f020, 0xfff8'f023).rw(FUNC(aviion88k_state::pit_cnt_r<3>), FUNC(aviion88k_state::pit_cnt_w<3>));
	map(0xfff8'f044, 0xfff8'f047).rw(FUNC(aviion88k_state::pit_sts_r<0>), FUNC(aviion88k_state::pit_cmd_w<0>));
	map(0xfff8'f048, 0xfff8'f04b).rw(FUNC(aviion88k_state::pit_sts_r<1>), FUNC(aviion88k_state::pit_cmd_w<1>));
	map(0xfff8'f050, 0xfff8'f053).rw(FUNC(aviion88k_state::pit_sts_r<2>), FUNC(aviion88k_state::pit_cmd_w<2>));
	map(0xfff8'f060, 0xfff8'f063).rw(FUNC(aviion88k_state::pit_sts_r<3>), FUNC(aviion88k_state::pit_cmd_w<3>));

	map(0xfff8'ff00, 0xfff8'ff03).lrw32(
		[]() { return 0b1110'0000'0001'1000; }, "mds_r",
		[](u32 data) {}, "mcs_w");
}

static void aviion88k_scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
	device.option_add("cdrom", NSCSI_CDROM);
}

void aviion88k_state::aviion_4600(machine_config &config)
{
	MC88100(config, m_cpu, 33'333'333);
	m_cpu->set_addrmap(AS_PROGRAM, &aviion88k_state::cpu_map);

	INTEL_28F010(config, m_prom[0]);
	INTEL_28F010(config, m_prom[1]);
	INTEL_28F010(config, m_prom[2]);
	INTEL_28F010(config, m_prom[3]);

	// 8 SIMM slots (populated with pairs of 4M or 16M modules)
	RAM(config, m_ram);
	m_ram->set_default_size("8M");
	m_ram->set_extra_options("16M,24M,32M,40M,48M,56M,64M,72M,80M,96M,104M,128M");
	m_ram->set_default_value(0);

	MK48T12(config, m_novram);

	// uart - keyboard interface
	SCN2661A(config, m_uart, 0);

	// keyboard connector
	PC_KBDC(config, m_kbdc, pc_at_keyboards, nullptr);
	m_kbdc->out_clock_cb().set(m_uart, FUNC(scn2661a_device::rxc_w));
	m_kbdc->out_data_cb().set(m_uart, FUNC(scn2661a_device::rxd_w));

	// duart 1
	SCN2681(config, m_duart[0], 14.7456_MHz_XTAL / 4); // SCC2692
	RS232_PORT(config, m_async[0], default_rs232_devices, "terminal"); // console: DCD,RXD,TXD,DTR,RTS,CTS
	RS232_PORT(config, m_async[1], default_rs232_devices, nullptr); // async A: DCD,RXD,TXD,DTR,DSR,RTS,CTS,RI

	m_duart[0]->a_tx_cb().set(m_async[0], FUNC(rs232_port_device::write_txd));
	m_duart[0]->b_tx_cb().set(m_async[1], FUNC(rs232_port_device::write_txd));
	m_async[0]->rxd_handler().set(m_duart[0], FUNC(scn2681_device::rx_a_w));
	m_async[1]->rxd_handler().set(m_duart[0], FUNC(scn2681_device::rx_b_w));

	// duart 2
	SCN2681(config, m_duart[1], 14.7456_MHz_XTAL / 4); // SCC2692
	RS232_PORT(config, m_async[2], default_rs232_devices, nullptr); // mouse: RTS,DTR,TXD,RXD
	RS232_PORT(config, m_async[3], default_rs232_devices, nullptr); // async B: DCD,RXD,TXD,DTR,DSR,RTS,CTS,RI

	m_duart[1]->a_tx_cb().set(m_async[2], FUNC(rs232_port_device::write_txd));
	m_duart[1]->b_tx_cb().set(m_async[3], FUNC(rs232_port_device::write_txd));
	m_async[2]->rxd_handler().set(m_duart[1], FUNC(scn2681_device::rx_a_w));
	m_async[3]->rxd_handler().set(m_duart[1], FUNC(scn2681_device::rx_b_w));

	// duscc
	DUSCC68562(config, m_duscc, 14.7456_MHz_XTAL);

	// scsi bus and devices
	NSCSI_BUS(config, m_scsibus);
	NSCSI_CONNECTOR(config, "scsi:0", aviion88k_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:1", aviion88k_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", aviion88k_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", aviion88k_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", aviion88k_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", aviion88k_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", aviion88k_scsi_devices, nullptr);

	// scsi host adapter (NCR53C700)
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr53c700", NCR53C7XX).clock(66'000'000);

	// TODO: ethernet (AM79C900)

	// speaker
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.50);
}

ROM_START(aviion_4600)
	ROM_REGION(0x20000, "prom0", 0)
	ROM_LOAD("11513__x02__92-05.bin", 0, 0x20000, CRC(7031d7d4) SHA1(c1ca7567b764b7f48e53b9bc8df40407464f9f67))
	ROM_REGION(0x20000, "prom1", 0)
	ROM_LOAD("11514__x02__92-06.bin", 0, 0x20000, CRC(4fcf85e6) SHA1(9afeec63cf8098d4518dc0712ba92614d44cd859))
	ROM_REGION(0x20000, "prom2", 0)
	ROM_LOAD("11515__x02__92-05.bin", 0, 0x20000, CRC(c9ce39d7) SHA1(fbdd3287b9f9eb6a621d7c10d900ccaff02660c5))
	ROM_REGION(0x20000, "prom3", 0)
	ROM_LOAD("11516__x02__92-05.bin", 0, 0x20000, CRC(71b6d338) SHA1(eb85bd16a25b6cd790272f007b8117fcf13b6b40))
ROM_END

} // anonymous namespace

/*   YEAR   NAME         PARENT  COMPAT  MACHINE      INPUT  CLASS            INIT  COMPANY         FULLNAME       FLAGS */
COMP(1991,  aviion_4600, 0,      0,      aviion_4600, 0,     aviion88k_state, init, "Data General", "AViiON 4600", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



avrmax.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
/*******************************************************************************

AVR-Max Chess Computer, aka "SHAH"
In Germany it was named AVR-Max-Schachzwerg

The chess engine is H.G. Muller's micro-Max 4.8, ATMega88 port and prototype
hardware design by Andre Adrian. PCB finalization by Elektor, published in
Elektor magazine in 2009.

The LCD version uses Elektor's ATM18 (aka CC2) system, and a 2-wire LCD module.
It was not manufactured by Elektor, only described as a homebrew project.

FN 1 = new game, FN 2 = set level, FN 3 = principle variation.
Moves are confirmed by pressing GO twice.

The chess program is the same for all versions, only the display differs.

Hardware notes:

LED version:
- PCB label 081101-1 (c)Elektor v1.4
- Atmel ATMega88P-20PU @ ~8MHz, 8KB internal ROM
- 4-digit 7seg display, button panel, no sound

LCD version:
- Elektor ATM18 Testboard (has ATmega88 TQFP32 @ 16MHz)
- Elektor 2-wire LCD board (CD4094, HD44780U, 4*20 display)
- custom keypad board

Elektor magazine references (English):
- 04/2008: ATM18 AVR Board
- 05/2008: Two-wire LCD
- 09/2009: ATM18 Mini Chess Computer
- 11/2009: AVR-Max Chess Computer

For the German magazine, AVR-Max-Schachzwerg and CC2-Schachzwerg are in 06/2009.

TODO:
- AVR8 SLEEP opcode is not working, it's used for power-saving here and was
  harmless to hack out, but needs to be put back when it's emulated

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

#include "emu.h"

#include "cpu/avr8/avr8.h"
#include "video/hd44780.h"
#include "video/pwm.h"

#include "screen.h"

// internal artwork
#include "avrmax.lh"
#include "atm18mcc.lh"


namespace {

class avrmax_state : public driver_device
{
public:
	avrmax_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_digit_pwm(*this, "digit_pwm"),
		m_lcd(*this, "lcd"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void base(machine_config &config);
	void avrmax(machine_config &config);
	void atm18mcc(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<atmega88_device> m_maincpu;
	optional_device<pwm_display_device> m_digit_pwm;
	optional_device<hd44780_device> m_lcd;
	required_ioport_array<4> m_inputs;

	u8 m_inp_mux = 0;
	u8 m_shift_reg = 0;
	int m_shift_clk = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;
	void data_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void input_w(u8 data);
	u8 input_r();
	void digit_w(u8 data);
	void segment_w(u8 data);
	void lcd_w(u8 data);

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};

void avrmax_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_shift_reg));
	save_item(NAME(m_shift_clk));
}



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

// common

void avrmax_state::input_w(u8 data)
{
	// PC0-PC3: input mux
	m_inp_mux = ~data & 0xf;
}

u8 avrmax_state::input_r()
{
	u8 data = 0;

	// PB0-PB2: multiplexed inputs
	for (int i = 0; i < 4; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read();

	return ~data & 7;
}


// LED handlers

void avrmax_state::digit_w(u8 data)
{
	// PC0-PC3: also digit select
	input_w(data);
	m_digit_pwm->write_my(m_inp_mux);
}

void avrmax_state::segment_w(u8 data)
{
	// PD0-PD7: digit 7seg data
	m_digit_pwm->write_mx(~data);
}


// LCD handlers

u32 avrmax_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	bitmap.fill(0xffffff, cliprect);
	const u8 *render = m_lcd->render();

	// draw lcd characters
	for (int i = 0; i < 80; i++)
	{
		const u8 *src = render + 16 * i;
		for (int y = 0; y < 8; y++)
		{
			for (int x = 0; x < 5; x++)
			{
				int row = bitswap<2>(i / 20, 0, 1);
				int col = i % 20;
				u32 color = (BIT(src[y], 4 - x)) ? 0 : 0xe8e8e8;
				bitmap.pix(row * 9 + y + 1, col * 6 + x + 1) = color;
			}
		}
	}

	return 0;
}

void avrmax_state::lcd_w(u8 data)
{
	// PD2: CD4094 data
	int shift_data = BIT(data, 2);

	// PD3: CD4094 clock
	int shift_clk = BIT(data, 3);
	if (shift_clk && !m_shift_clk)
		m_shift_reg = (m_shift_reg << 1) | shift_data;
	m_shift_clk = shift_clk;

	// CD4094 Q2-Q5: LCD data
	// CD4094 Q6: LCD RS
	// CD4094 Q7: LCD E (ANDed with CD4094 data)
	m_lcd->db_w(m_shift_reg << 3 & 0xf0);
	m_lcd->rs_w(BIT(m_shift_reg, 5));
	m_lcd->e_w(BIT(m_shift_reg, 6) & shift_data);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void avrmax_state::main_map(address_map &map)
{
	map(0x0000, 0x1fff).rom();
}

void avrmax_state::data_map(address_map &map)
{
	map(0x0100, 0x04ff).ram();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( avrmax )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("A1") PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("E5") PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("FN") PORT_CODE(KEYCODE_N)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("B2") PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("F6") PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("CL") PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE)

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("C3") PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("G7") PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("GO") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("D4") PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("H8") PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void avrmax_state::base(machine_config &config)
{
	// basic machine hardware
	ATMEGA88(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &avrmax_state::main_map);
	m_maincpu->set_addrmap(AS_DATA, &avrmax_state::data_map);
	m_maincpu->set_eeprom_tag("eeprom");
	m_maincpu->gpio_in<atmega88_device::GPIOB>().set(FUNC(avrmax_state::input_r));
	m_maincpu->gpio_out<atmega88_device::GPIOC>().set(FUNC(avrmax_state::input_w));
}

void avrmax_state::avrmax(machine_config &config)
{
	base(config);

	// basic machine hardware
	m_maincpu->set_clock(8'000'000); // internal R/C clock
	m_maincpu->gpio_out<atmega88_device::GPIOC>().set(FUNC(avrmax_state::digit_w));
	m_maincpu->gpio_out<atmega88_device::GPIOD>().set(FUNC(avrmax_state::segment_w));

	// video hardware
	PWM_DISPLAY(config, m_digit_pwm).set_size(4, 8);
	m_digit_pwm->set_segmask(0xf, 0xff);
	config.set_default_layout(layout_avrmax);
}

void avrmax_state::atm18mcc(machine_config &config)
{
	base(config);

	// basic machine hardware
	m_maincpu->gpio_out<atmega88_device::GPIOD>().set(FUNC(avrmax_state::lcd_w));

	// video hardware
	auto &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_size(20 * 6 + 1, 4 * 9 + 1);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(avrmax_state::screen_update));

	HD44780U(config, m_lcd, 270'000); // TODO: clock not measured, datasheet typical clock used
	// HD44780UA02 is required for certain international characters in cc2schach,
	// the English version can optionally use a more standard HD44780[U]A00 display
	m_lcd->set_default_bios_tag("a02");

	config.set_default_layout(layout_atm18mcc);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( avrmax )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "elektor_081101-41.ic1", 0x0000, 0x2000, CRC(86d2a654) SHA1(3c235b8f6f735eaf408f54cbf44872166e7161d5) ) // avrmax_en-v1.0.hex

	// HACK: changed SLEEP to NOP
	ROM_FILL( 0x025c, 2, 0x00 )
	ROM_FILL( 0x0f8e, 2, 0x00 )
	ROM_FILL( 0x0fde, 2, 0x00 )
	ROM_FILL( 0x1020, 2, 0x00 )
	ROM_FILL( 0x1060, 2, 0x00 )
	ROM_FILL( 0x129a, 2, 0x00 )

	ROM_REGION( 0x200, "eeprom", ROMREGION_ERASE00 )
ROM_END

ROM_START( avrmaxg )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "elektor_081101-41_d.ic1", 0x0000, 0x2000, CRC(18ec7a56) SHA1(a018421aa0ad8cce3d852f7519dec3691f3c55a0) ) // avrmax_de-v1.0.hex

	// HACK: changed SLEEP to NOP
	ROM_FILL( 0x025c, 2, 0x00 )
	ROM_FILL( 0x0f8e, 2, 0x00 )
	ROM_FILL( 0x0fde, 2, 0x00 )
	ROM_FILL( 0x1020, 2, 0x00 )
	ROM_FILL( 0x1060, 2, 0x00 )
	ROM_FILL( 0x129a, 2, 0x00 )

	ROM_REGION( 0x200, "eeprom", ROMREGION_ERASE00 )
ROM_END

ROM_START( atm18mcc )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "avrmax_cc2_en-v1.0.bin", 0x0000, 0x2000, CRC(715f4642) SHA1(d20739c6caa49a01e002b3dfbf0f39abf7992540) )

	// HACK: changed SLEEP to NOP
	ROM_FILL( 0x05da, 2, 0x00 )
	ROM_FILL( 0x1466, 2, 0x00 )
	ROM_FILL( 0x14a2, 2, 0x00 )
	ROM_FILL( 0x14de, 2, 0x00 )
	ROM_FILL( 0x1528, 2, 0x00 )
	ROM_FILL( 0x16fe, 2, 0x00 )

	ROM_REGION( 0x200, "eeprom", ROMREGION_ERASE00 )
ROM_END

ROM_START( cc2schach )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "avrmax_cc2_de-v1.0.bin", 0x0000, 0x2000, CRC(64cfc646) SHA1(4c371ea9f48c8745cf5f5bcf10973838e239e564) )

	// HACK: changed SLEEP to NOP
	ROM_FILL( 0x05da, 2, 0x00 )
	ROM_FILL( 0x1466, 2, 0x00 )
	ROM_FILL( 0x14a2, 2, 0x00 )
	ROM_FILL( 0x14de, 2, 0x00 )
	ROM_FILL( 0x1528, 2, 0x00 )
	ROM_FILL( 0x16fe, 2, 0x00 )

	ROM_REGION( 0x200, "eeprom", ROMREGION_ERASE00 )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT    COMPAT  MACHINE   INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 2009, avrmax,    0,        0,      avrmax,   avrmax, avrmax_state, empty_init, "Elektor", "AVR-Max Chess Computer (English)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 2009, avrmaxg,   avrmax,   0,      avrmax,   avrmax, avrmax_state, empty_init, "Elektor", "AVR-Max-Schachzwerg (German)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW ) // German 'text'

SYST( 2009, atm18mcc,  0,        0,      atm18mcc, avrmax, avrmax_state, empty_init, "Elektor", "ATM18 Mini Chess Computer (English)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 2009, cc2schach, atm18mcc, 0,      atm18mcc, avrmax, avrmax_state, empty_init, "Elektor", "CC2-Schachzwerg (German)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )



ax145.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Bartman/Abyss

#include "emu.h"

#include "cpu/z180/z180.h"
#include "machine/timer.h"
#include "video/hd44780.h"

#include "debug/debugcpu.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


// 240x18x4 = 960x72
// -log -debug -window -intscalex 4 -intscaley 4 -resolution 960x72 ax145

//////////////////////////////////////////////////////////////////////////
// AX-145
//////////////////////////////////////////////////////////////////////////

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

Brother AX-145
198?

Hardware:

Main PCB B482360-1
========

#7 Hitachi
HD64180RP6
6 MHz, 80xx Peripherals

#6 Texas Instruments
LM393P
Dual differential comparator, commercial grade

#5 not populated
µPD23C2001

#2 NEC
D43256AC-10L
NEC
Static CMOS RAM 32,768 x 8-Bit

#3 NEC
D23C4001EC-172
UA2849-A
4MBit Mask ROM for Dictionary

#4 NEC
D23C1001EC-522
UA4474-C
1MBit Mask ROM

#1 Texas Instruments
SN74HC00N
Quad 2-Input Positive-NAND Gates

#8 NEC
D65013-A73
UA3001-A
CMOS Gate Array

LCD PCB connected via 12-pin ribbon cable
========
Hitachi
HD44780S-B02
U17978-A
Dot Matrix Liquid Crystal Display Controller/Driver
Custom Font
(driven in 4-bit mode, R/W connected)

2x Hitachi
HD66100F
LCD Driver with 80-Channel Outputs

LCD: 40 characters x 2 lines

see https://github.com/BartmanAbyss/brother-hardware/tree/master/0G%20-%20Brother%20AX-145 for datasheets, photos

// Status:
// doesn't go further than "SCHREIBWERK ÜBERPRÜFEN" (check printer)
// TODO: needs european font for HD44780

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

// hw_config:
// - & 0x1f:
//    6            => deutsch
//    12           => francais/nederlands
//    13           => nederlands
//    16, 17       => francais/deutsch
//    8, 9, 14, 21 => espanol

namespace {

class ax145_state : public driver_device
{
public:
	ax145_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_lcdc(*this, "hd44780"),
		m_ram(*this, "ram", 0x8000, ENDIANNESS_LITTLE),
		m_rom(*this, "maincpu"),
		dictionary_bank(*this, "dictionary")
	{ }

	void ax145(machine_config &config) ATTR_COLD;

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

private:
	// valid values (bei IO3000=0x0b,0x07) (read_config @ 0x14c87): 0 = german => 0, 1 = german => 1, 2 = espanol => 2, 4 (gehäusedeckel offen) => 3, 8 = francais => 4, 16 = german => 5
	static constexpr uint8_t ID = 1;

	// devices
	required_device<hd64180rp_device> m_maincpu;
	required_device<hd44780_device> m_lcdc;
	memory_share_creator<uint8_t> m_ram;
	required_region_ptr<uint8_t> m_rom;
	required_memory_bank dictionary_bank;

	// config switch
	uint8_t io_3000{};

	// bit 2: R/~W (Read / Not Write)
	// bit 1: RS (Register Select)
	// bit 0: E (Enable)
	uint8_t lcd_signal{};

	void map_program(address_map &map) ATTR_COLD
	{
		map(0x00000, 0x01fff).rom();
		map(0x04000, 0x1ffff).rom();
		map(0x40000, 0x5ffff).bankr(dictionary_bank);
		// RAM is installed in machine_start()
	}

	void map_io(address_map &map) ATTR_COLD
	{
		map.global_mask(0xffff);
		map(0x0000, 0x003f).noprw(); // Z180 internal registers
		//map(0x0040, 0x00ff).rw(FUNC(ax145_state::illegal_io_r), FUNC(ax145_state::illegal_io_w));
		//map(0x2000, 0x2000).w(TODO);
		//map(0x2800, 0x2800).w(TODO);
		map(0x3000, 0x3000).w(FUNC(ax145_state::io_3000_w));
		map(0x3800, 0x3800).r(FUNC(ax145_state::io_3800_r)); // config lower 4 bits
		map(0x4000, 0x4000).r(FUNC(ax145_state::io_4000_r)); // config upper 4 bits
		map(0x5000, 0x5000).w(FUNC(ax145_state::dictionary_bank_w));
		map(0x5800, 0x5800).w(FUNC(ax145_state::lcd_signal_w));
		map(0x6000, 0x6000).rw(FUNC(ax145_state::lcd_data_r), FUNC(ax145_state::lcd_data_w));
		//map(0x6800, 0x6800).w(TODO);
		//map(0x7000, 0x7000).w(TODO);
		//map(0x7800, 0x7800).w(TODO);
		//map(0x8000, 0x8000).w(TODO);
		//map(0x8800, 0x8800).w(TODO);
		//map(0x9000, 0x9000).r(TODO);
		map(0xb000, 0xb000).r(FUNC(ax145_state::irq_ack_r));
		//map(0xb800, 0xb800).rw(TODO, TODO);
	}

	void palette(palette_device &palette) const ATTR_COLD
	{
		palette.set_pen_color(0, rgb_t(138, 146, 148));
		palette.set_pen_color(1, rgb_t(92, 83, 88));
	}

	// IO
	void io_3000_w(uint8_t data) { io_3000 = data; }

	// should probably return something different depending on io_3000
	// 0x0014a9 also reads but io_3000=0xfe,0xff...
	uint8_t io_3800_r()
	{
		// config lower 4 bits
		return (~ID) & 0x0f;
	}

	uint8_t io_4000_r()
	{
		// config upper 4 bits
		return (~ID) >> 4;
	}

	void lcd_signal_w(uint8_t data)
	{
		lcd_signal = data;
	}

	uint8_t lcd_data_r()
	{
		if(BIT(lcd_signal, 1)) // RS
			return m_lcdc->data_r();
		else
			return m_lcdc->control_r();
	}
	void lcd_data_w(uint8_t data)
	{
		if(BIT(lcd_signal, 0)) { // E
			if(BIT(lcd_signal, 1)) // RS
				m_lcdc->data_w(data << 4);
			else
				m_lcdc->control_w(data << 4);
		}
	}

	void dictionary_bank_w(uint8_t data)
	{
		dictionary_bank->set_entry(data & 0x03);
	}

	// int2
	TIMER_DEVICE_CALLBACK_MEMBER(int2_timer_callback)
	{
		m_maincpu->set_input_line(INPUT_LINE_IRQ2, ASSERT_LINE);
	}

	uint8_t irq_ack_r()
	{
		if(!machine().side_effects_disabled())
			m_maincpu->set_input_line(INPUT_LINE_IRQ2, CLEAR_LINE);
		return 0;
	}
};

void ax145_state::video_start()
{
}

void ax145_state::machine_start()
{
	m_maincpu->space(AS_PROGRAM).install_ram(0x02000, 0x03fff, m_ram); // first 0x2000 bytes of RAM
	m_maincpu->space(AS_PROGRAM).install_ram(0x62000, 0x69fff, m_ram); // complete 0x8000 bytes of RAM
	dictionary_bank->configure_entries(0, 4, memregion("dictionary")->base(), 0x20000);

	// TODO: ROM patch
}

void ax145_state::machine_reset()
{
}

void ax145_state::ax145(machine_config &config) {
	// basic machine hardware
	HD64180RP(config, m_maincpu, 12'000'000 / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &ax145_state::map_program);
	m_maincpu->set_addrmap(AS_IO, &ax145_state::map_io);

	TIMER(config, "1khz").configure_periodic(FUNC(ax145_state::int2_timer_callback), attotime::from_hz(1000)); // just guessing frequency, based on LW-30, 350, 450

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_color(rgb_t(6, 245, 206));
	screen.set_physical_aspect(480, 128);
	screen.set_refresh_hz(78.1);
	screen.set_size(6*40, 9*2);
	screen.set_visarea_full();
	screen.set_screen_update("hd44780", FUNC(hd44780_device::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(ax145_state::palette), 2);

	HD44780(config, m_lcdc, 270'000); // TODO: Wrong device type, should be HD44780-B02 custom character set mask; clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 40);
}

static INPUT_PORTS_START(ax145)
	// TODO
INPUT_PORTS_END

/***************************************************************************
  Machine driver(s)
***************************************************************************/

ROM_START( ax145 )
	ROM_REGION(0x20000, "maincpu", 0)
	ROM_LOAD("ua4774-c", 0x00000, 0x20000, CRC(82e0f117) SHA1(2bb2883feb73c7c20e2e3004b3588ba354e52b3a)) // german/french/dutch/spanish
	ROM_REGION(0x80000, "dictionary", 0)
	ROM_LOAD("ua2849-a", 0x00000, 0x80000, CRC(fa8712eb) SHA1(2d3454138c79e75604b30229c05ed8fb8e7d15fe)) // german dictionary
ROM_END

} // anonymous namespace

//    YEAR  NAME  PARENT COMPAT   MACHINE INPUT   CLASS            INIT              COMPANY         FULLNAME  FLAGS
COMP( 198?, ax145,   0,   0,      ax145,  ax145,  ax145_state,     empty_init,       "Brother",      "AX-145", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



ax20.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

    Axel AX-20

    27/12/2011 Skeleton driver.

    Hardware description:
    - CPU: I8088
    - FDC: I8272
    - PIT: I8253
    - PIC: I8259

    Also marketed under the Matra brand as MAX-20 ("M" for Matra ?)

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

#include "emu.h"
#include "bus/isa/fdc.h"
#include "cpu/i86/i86.h"
#include "emupal.h"
#include "screen.h"


namespace {

class ax20_state : public driver_device
{
public:
	ax20_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_p_vram(*this, "p_vram"),
		m_gfxdecode(*this, "gfxdecode"),
		m_palette(*this, "palette"),
		m_fdc(*this, "fdc")
	{ }

	void ax20(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	required_shared_ptr<uint8_t> m_p_vram;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<palette_device> m_palette;
	required_device<i8272a_device> m_fdc;

	virtual void machine_start() override ATTR_COLD;
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	uint8_t unk_r();
	void tc_w(uint8_t data);
	void ctl_w(uint8_t data);

	void ax20_io(address_map &map) ATTR_COLD;
	void ax20_map(address_map &map) ATTR_COLD;
};

uint8_t ax20_state::unk_r()
{
	return 0;
}

void ax20_state::tc_w(uint8_t data)
{
	m_fdc->tc_w((data & 0xf0) == 0xf0);
}

void ax20_state::ctl_w(uint8_t data)
{
	m_fdc->subdevice<floppy_connector>("0")->get_device()->mon_w(!(data & 1));
}

uint32_t ax20_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for ( int y = 0; y < 24; y++ )
	{
		for ( int x = 0; x < 80; x++ )
		{
			uint16_t tile = m_p_vram[24 +  y * 128 + x ] & 0x7f;

			m_gfxdecode->gfx(0)->opaque(bitmap,cliprect, tile, 0, 0, 0, x*8, y*12);
		}
	}

	return 0;
}


void ax20_state::ax20_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x1ffff).ram();
	map(0x20000, 0x3ffff).ram(); //optional RAM
	map(0xf0400, 0xf0fff).ram().share("p_vram");
	map(0xff800, 0xfffff).rom().region("ipl", 0);
}

void ax20_state::ax20_io(address_map &map)
{
	map.unmap_value_high();
	map(0xffc0, 0xffc0).w(FUNC(ax20_state::tc_w));
	map(0xffd0, 0xffd0).w(FUNC(ax20_state::ctl_w));
	map(0xffe0, 0xffe0).r(FUNC(ax20_state::unk_r));
	map(0xff80, 0xff81).m(m_fdc, FUNC(i8272a_device::map));
}

/* Input ports */
static INPUT_PORTS_START( ax20 )
INPUT_PORTS_END


void ax20_state::machine_start()
{
}

static const gfx_layout ax20_charlayout =
{
	8, 12,
	128,
	1,
	{ 0 },
	{ 4*8+7, 4*8+6, 4*8+5, 4*8+4, 4*8+3, 4*8+2, 4*8+1, 4*8+0 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 8*9, 8*10, 8*11 },
	8*16
};

static GFXDECODE_START( gfx_ax20 )
	GFXDECODE_ENTRY( "chargen", 0x0000, ax20_charlayout, 0, 1 )
GFXDECODE_END

static void ax20_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

void ax20_state::ax20(machine_config &config)
{
	/* basic machine hardware */
	I8088(config, m_maincpu, XTAL(14'318'181)/3);
	m_maincpu->set_addrmap(AS_PROGRAM, &ax20_state::ax20_map);
	m_maincpu->set_addrmap(AS_IO, &ax20_state::ax20_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(ax20_state::screen_update));
	screen.set_size(80*8, 24*12);
	screen.set_visarea(0, 80*8-1, 0, 24*12-1);
	screen.set_palette(m_palette);
	GFXDECODE(config, m_gfxdecode, m_palette, gfx_ax20);
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	I8272A(config, m_fdc, 8'000'000, true);

	/* Devices */
	FLOPPY_CONNECTOR(config, "fdc:0", ax20_floppies, "525dd", isa8_fdc_device::floppy_formats);
}

/* ROM definition */
ROM_START( ax20 )
	ROM_REGION( 0x0800, "ipl", 0 )
	ROM_LOAD( "ax20-s.rom", 0x0000, 0x0800, CRC(f11f95b9) SHA1(59949332dd431fcf8211c2d556e1f49351e90750))

	ROM_REGION( 0x4000, "chargen", 0 )
	ROM_LOAD( "ax20-g.rom", 0x0000, 0x0800, CRC(90bcef80) SHA1(922067fd7316de9e69b9600c793ada5c87197eeb))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT CLASS       INIT        COMPANY  FULLNAME  FLAGS
COMP( 1982, ax20, 0,      0,      ax20,    ax20, ax20_state, empty_init, "Axel",  "AX-20",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



b16.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Angelo Salese
// thanks-to: Mike Stedman
/**************************************************************************************************

Hitachi B(asic Master?) 16000 series?

TODO:
- Barely anything is known about the HW;
- Requires a compatible system disk to make it go further;
- hookup proper keyboard;
- FDC throws disk error, mon_w hookup is unconfirmed, doesn't seem to select a drive ...
- b16: hangs for checking bit 7 in vblank fashion at $80, either the DMA is at the
  wrong spot or the earlier variant effectively don't have it.
- b16ex2: "error message 1101", bypassed between ports $80 and $48
- b16ex2: confirm kanji hookup;

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

B16 EX-II PCB contents:

TIM uPD8253C-5 @ 16B
DMAC uPD8257C-5 @ 18B
INTM / INTS 2x uPD8259AC @ 13D / 15D
Labelless CRTC, Fujitsu 6845 package
FDC is unpopulated on board, should be a 765 coupled with a SED9420C/AC
USART is unpopulated on board, assume i8251
PPI is unpopulated on board
BDC HN65013G025 @ 2A
CAL HN6223S @ 12K  - Near bus slots
CA HN6022B @ 12L   /
MPX HG61H06R29F @ 9J
RAC HN60236 - 81005V @ 9H
A HN60230 - 81007V @ 9F
PAC HG61H20B12F @ 9D
DECO HG61H15B19F @ 11D
KAM HN671105AU @ 16A
DECI HG61H15B19F @ 2F
VAP NEC uPD65030G035 @ 4K
GN NEC uPD65021G030 @ 2K
4x 32x3 slots, labeled SLOT0 to 3
11 Jumpers "SH1"
Several connectors scattered across the board
Centronics port CN9, Serial port CN8
2 empty sockets for CN10 / CN11 (DE-9 options?)

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

irq vectors

intm
ir0 pit timer (does work RAM logic only)
ir1 FDC irq
ir2 ?
ir3 vblank?
ir4 ?
ir5 ?
ir6 slave ack
ir7 ?

ints
ir0 keyboard
ir1 ?
ir2 ?
ir3 device at I/O $38 / $3a
ir4 device at I/O $4c / $4e
ir5 ?
ir6 ?
ir7 ?

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

Error codes (TODO: RE them all)
0301 PIT failure
1101 b16ex2 bus config error?

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

#include "emu.h"
#include "cpu/i86/i86.h"
#include "cpu/i86/i286.h"
#include "imagedev/floppy.h"
#include "machine/i8257.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/upd765.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
//#include "softlist_dev.h"

namespace {

class b16_state : public driver_device
{
public:
	b16_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pit(*this, "pit")
		, m_intm(*this, "intm")
		, m_ints(*this, "ints")
		, m_dma(*this, "dma")
		, m_crtc(*this, "crtc")
		, m_vram(*this, "vram")
		, m_gfxdecode(*this, "gfxdecode")
		, m_palette(*this, "palette")
		, m_kanji_rom(*this, "kanji")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0U)
	{ }

	void b16(machine_config &config);
	void b16ex2(machine_config &config);
	DECLARE_INPUT_CHANGED_MEMBER(key_pressed);

protected:
	virtual void video_start() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void b16_map(address_map &map) ATTR_COLD;
	void b16_io(address_map &map) ATTR_COLD;
	void b16ex2_map(address_map &map) ATTR_COLD;

private:
	uint8_t m_crtc_vreg[0x100]{}, m_crtc_index = 0;
	uint8_t m_port78 = 0;
	uint8_t m_keyb_scancode = 0;

	required_device<cpu_device> m_maincpu;
	required_device<pit8253_device> m_pit;
	required_device<pic8259_device> m_intm;
	required_device<pic8259_device> m_ints;
	required_device<i8257_device> m_dma;
	required_device<mc6845_device> m_crtc;
	required_shared_ptr<uint16_t> m_vram;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<palette_device> m_palette;
	required_region_ptr<uint8_t> m_kanji_rom;
	required_device<upd765a_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;

	std::unique_ptr<u8[]> m_ig_ram;

	u8 ig_ram_r(offs_t offset);
	void ig_ram_w(offs_t offset, uint8_t data);
	void crtc_address_w(uint8_t data);
	void crtc_data_w(uint8_t data);
	uint8_t memory_read_byte(offs_t offset);
	void memory_write_byte(offs_t offset, uint8_t data);

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	TIMER_CALLBACK_MEMBER( tc_tick_cb );
	emu_timer *m_timer_tc;

	static void floppy_formats(format_registration &fr);
};

#define mc6845_h_char_total     (m_crtc_vreg[0])
#define mc6845_h_display        (m_crtc_vreg[1])
#define mc6845_h_sync_pos       (m_crtc_vreg[2])
#define mc6845_sync_width       (m_crtc_vreg[3])
#define mc6845_v_char_total     (m_crtc_vreg[4])
#define mc6845_v_total_adj      (m_crtc_vreg[5])
#define mc6845_v_display        (m_crtc_vreg[6])
#define mc6845_v_sync_pos       (m_crtc_vreg[7])
#define mc6845_mode_ctrl        (m_crtc_vreg[8])
#define mc6845_tile_height      (m_crtc_vreg[9]+1)
#define mc6845_cursor_y_start   (m_crtc_vreg[0x0a])
#define mc6845_cursor_y_end     (m_crtc_vreg[0x0b])
#define mc6845_start_addr       (((m_crtc_vreg[0x0c]<<8) & 0x3f00) | (m_crtc_vreg[0x0d] & 0xff))
#define mc6845_cursor_addr      (((m_crtc_vreg[0x0e]<<8) & 0x3f00) | (m_crtc_vreg[0x0f] & 0xff))
#define mc6845_light_pen_addr   (((m_crtc_vreg[0x10]<<8) & 0x3f00) | (m_crtc_vreg[0x11] & 0xff))
#define mc6845_update_addr      (((m_crtc_vreg[0x12]<<8) & 0x3f00) | (m_crtc_vreg[0x13] & 0xff))


void b16_state::video_start()
{
	m_ig_ram = make_unique_clear<u8[]>(0x4000);
	m_gfxdecode->gfx(1)->set_source_and_total(m_ig_ram.get(), 0x200 * 2);

	save_item(NAME(m_crtc_vreg));
	save_item(NAME(m_crtc_index));
}


uint32_t b16_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	// TODO: convert to scanline renderer
	for(int y = 0; y < mc6845_v_display; y++)
	{
		for(int x = 0; x < mc6845_h_display; x++)
		{
			const u32 tile_offset = x + y * mc6845_h_display;
			const u16 lo_vram = m_vram[tile_offset];
			const u16 hi_vram = m_vram[tile_offset + 0x4000 / 2];
			const u16 tile = lo_vram & 0x00ff;
			const u8 color = (lo_vram & 0x0700) >> 8;

			for(int yi = 0; yi < mc6845_tile_height; yi++)
			{
				u8 gfx_data = 0;
				// TODO: incorrect select (will print gibberish after the system bootup message)
				// system doesn't bother to clear kanji upper addresses, may opt-out thru a global register.
				if (BIT(hi_vram, 5))
				{
					// TODO: apply bitswap at device_init time, move this calculation out of this inner loop.
					const u16 lr = BIT(hi_vram, 7) ^ 1;
					const u16 kanji_bank = (hi_vram & 0x1f);
					const u32 kanji_offset = bitswap<13>(tile + (kanji_bank << 8), 12, 7, 6, 5, 11, 10, 9, 8, 4, 3, 2, 1, 0) << 4;
					gfx_data = m_kanji_rom[((kanji_offset + yi) << 1) + lr];
				}
				else
				{
					gfx_data = m_ig_ram[(tile << 4) + yi];
				}

				for(int xi = 0; xi < 8; xi++)
				{
					const int res_x = x * 8 + xi;
					const int res_y = y * mc6845_tile_height + yi;
					if (!cliprect.contains(res_x, res_y))
						continue;

					int const pen = BIT(gfx_data, 7 - xi) ? color : 0;
					bitmap.pix(res_y, res_x) = m_palette->pen(pen);
				}
			}
		}
	}

	return 0;
}

u8 b16_state::ig_ram_r(offs_t offset)
{
	// swap bit 0 for now, so we can see a setup thru debugger later on.
	// SW does mov ah,0 -> stosw when uploading.
	const u16 ig_offset = bitswap<13>(offset, 0, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
	return m_ig_ram[ig_offset];
}

void b16_state::ig_ram_w(offs_t offset, uint8_t data)
{
	const u16 ig_offset = bitswap<13>(offset, 0, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
	m_ig_ram[ig_offset] = data;

	m_gfxdecode->gfx(1)->mark_dirty(ig_offset >> 4);
}

void b16_state::b16_map(address_map &map)
{
	map.unmap_value_high();
	// TODO: amount of work RAM depends on model type
	map(0x00000, 0x9ffff).ram();
	map(0xa0000, 0xaffff).ram(); // bitmap?
	map(0xb0000, 0xb7fff).ram().share("vram");
	map(0xb8000, 0xbbfff).rw(FUNC(b16_state::ig_ram_r), FUNC(b16_state::ig_ram_w)).umask16(0xffff);
	map(0xfc000, 0xfffff).rom().region("ipl", 0);
}

void b16_state::b16ex2_map(address_map &map)
{
	b16_state::b16_map(map);
//  map(0x0e****) bus slot ROM?
	map(0x0f8000, 0x0fffff).rom().region("ipl", 0);
	map(0xff8000, 0xffffff).rom().region("ipl", 0);
}

void b16_state::crtc_address_w(uint8_t data)
{
	m_crtc_index = data;
	m_crtc->address_w(data);
}

void b16_state::crtc_data_w(uint8_t data)
{
	m_crtc_vreg[m_crtc_index] = data;
	m_crtc->register_w(data);
}

void b16_state::b16_io(address_map &map)
{
	map.unmap_value_high();
//  map(0x00, 0x07).umask16(0x00ff); // PIT mirror?
	map(0x08, 0x0f).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0x10, 0x13).rw(m_intm, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x18, 0x1b).rw(m_ints, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x20, 0x20).w(FUNC(b16_state::crtc_address_w));
	map(0x22, 0x22).w(FUNC(b16_state::crtc_data_w));
//  map(0x28, 0x2b) keyboard, likely thru USART
	map(0x28, 0x28).lr8(NAME([this]() {return m_keyb_scancode; }));
	map(0x2a, 0x2a).lr8(NAME([]() { return 0x02; }));
	// Jumper block?
//  map(0x40, 0x40)
//  map(0x42, 0x42)
//  map(0x44, 0x44)
	// b16ex2: jumps to $e0000 if bit 7 high, noisy on bit 4
	map(0x48, 0x48).lr8(NAME([] () { return 0; }));
	map(0x70, 0x73).m(m_fdc, FUNC(upd765a_device::map)).umask16(0x00ff);
	map(0x74, 0x74).lw8(
		NAME([this] (u8 data) {
			floppy_image_device *floppy = m_floppy[0]->get_device();

			// motor on strobe?
			if (floppy != nullptr)
			{
				floppy->mon_w(1);
				floppy->mon_w(0);
			}
		})
	);
	map(0x78, 0x78).lrw8(
		NAME([this] () {
			return m_port78;
		}),
		NAME([this] (u8 data) {
			logerror("Port $78 %02x\n", data);
			// bit 0: TC strobe?
			// bit 2: FDC reset?
			m_port78 = data;
			if (BIT(data, 0))
			{
				m_fdc->tc_w(true);
				m_timer_tc->adjust(attotime::zero);
			}
		})
	);
	map(0x79, 0x79).lrw8(
		NAME([] () {
			// bit 0 read at POST
			return 0;
		}),
		NAME([this] (u8 data) {
			logerror("Port $79 write %02x\n", data);
		})
	);
	map(0x80, 0x89).rw(m_dma, FUNC(i8257_device::read), FUNC(i8257_device::write)).umask16(0x00ff);
//  map(0x00a0, 0x00bf) DMA upper segments or video clut
//  map(0x8020, 0x8023) external FDC? Can branch at FDC irq
}

INPUT_CHANGED_MEMBER(b16_state::key_pressed)
{
	m_ints->ir0_w(!newval);
	m_keyb_scancode = (u8)param | (newval << 7);
}


static INPUT_PORTS_START( b16 )
	// question marks denotes unknown keys, asterisks for empty chars
	// KEY0
	// ?1234567
	// ? = left arrow (backspace?)

	// KEY1
	// 890-<backslash>***

	PORT_START("KEY2")
	// ?qwertyu
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(b16_state::key_pressed), 0x10 | 6)

	// KEY3
	// iop@[***

	PORT_START("KEY4")
	// asdfghjk
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(b16_state::key_pressed), 0x20)

	// KEY5
	// ?;:]****

	PORT_START("KEY6")
	// zxcvbnm,
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(b16_state::key_pressed), 0x30)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(b16_state::key_pressed), 0x31)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(b16_state::key_pressed), 0x32)
//    PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(b16_state::key_pressed), 0x33)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(b16_state::key_pressed), 0x34)
INPUT_PORTS_END




static const gfx_layout charlayout =
{
	8, 16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP8(0,1) },
	{ STEP16(0,8) },
	8*16
};

static const gfx_layout kanjilayout =
{
	16, 16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP16(0,1) },
	{ STEP16(0,8*2) },
	16*16
};


static GFXDECODE_START( gfx_b16 )
	GFXDECODE_ENTRY( "kanji", 0x0000, kanjilayout, 0, 1 )
	GFXDECODE_RAM( nullptr, 0x0000, charlayout, 0, 1 )
GFXDECODE_END

uint8_t b16_state::memory_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	return prog_space.read_byte(offset);
}

void b16_state::memory_write_byte(offs_t offset, uint8_t data)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	return prog_space.write_byte(offset, data);
}

static void b16_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
	// TODO: at least PC-98 3.5" x 1.2MB format
}

void b16_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
//  fr.add(FLOPPY_PC98_FORMAT);
//  fr.add(FLOPPY_PC98FDI_FORMAT);
//  fr.add(FLOPPY_FDD_FORMAT);
//  fr.add(FLOPPY_DCP_FORMAT);
//  fr.add(FLOPPY_DIP_FORMAT);
//  fr.add(FLOPPY_NFD_FORMAT);
}

TIMER_CALLBACK_MEMBER( b16_state::tc_tick_cb )
{
//  logerror("tc off\n");
	m_fdc->tc_w(false);
}

void b16_state::machine_start()
{
	m_timer_tc = timer_alloc(FUNC(b16_state::tc_tick_cb), this);
}

void b16_state::machine_reset()
{
	floppy_image_device *floppy = m_floppy[0]->get_device();

	if (floppy != nullptr)
		floppy->set_rpm(300);
	m_fdc->set_rate(250000);

}

void b16_state::b16(machine_config &config)
{
	// TODO: attached ROM may be B16 EX instead, with 8086-2 (8 MHz)
	// Original B16 should be I8088
	I8086(config, m_maincpu, XTAL(16'000'000)/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &b16_state::b16_map);
	m_maincpu->set_addrmap(AS_IO, &b16_state::b16_io);
	m_maincpu->set_irq_acknowledge_callback(m_intm, FUNC(pic8259_device::inta_cb));

	PIT8253(config, m_pit);
	// TODO: unconfirmed, just enough to make it surpass POST checks
	m_pit->set_clk<0>(XTAL(16'000'000) / 8);
	m_pit->out_handler<0>().set(m_intm, FUNC(pic8259_device::ir0_w));
	m_pit->set_clk<1>(XTAL(16'000'000) / 4);
//  m_pit->out_handler<1>()
	m_pit->set_clk<2>(XTAL(16'000'000) / 4);
//  m_pit->out_handler<2>()

	I8257(config, m_dma, XTAL(16'000'000));
	m_dma->in_memr_cb().set(FUNC(b16_state::memory_read_byte));
	m_dma->out_memw_cb().set(FUNC(b16_state::memory_write_byte));

	PIC8259(config, m_intm);
	m_intm->out_int_callback().set_inputline(m_maincpu, 0);
	m_intm->in_sp_callback().set_constant(1);
	m_intm->read_slave_ack_callback().set(m_ints, FUNC(pic8259_device::acknowledge));

	PIC8259(config, m_ints, 0);
	m_ints->out_int_callback().set(m_intm, FUNC(pic8259_device::ir6_w));
	m_ints->in_sp_callback().set_constant(0);

	// clock unconfirmed, definitely want the ready line on
	// would stop at `SEARCH_ADDRESS_MARK_HEADER` otherwise
	UPD765A(config, m_fdc, XTAL(16'000'000) / 2, true, false);
	m_fdc->intrq_wr_callback().set(m_intm, FUNC(pic8259_device::ir1_w));
	m_fdc->drq_wr_callback().set([this] (int state) { logerror("drq %d\n", state);});
	FLOPPY_CONNECTOR(config, "fdc:0", b16_floppies, "525dd", b16_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", b16_floppies, "525dd", b16_state::floppy_formats).enable_sound(true);

	/* unknown variant, unknown clock, hand tuned to get ~60 fps */
	MC6845(config, m_crtc, XTAL(16'000'000)/6);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(b16_state::screen_update));
	screen.set_size(640, 400);
	screen.set_visarea_full();
	screen.set_palette(m_palette);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_b16);
	// TODO: palette format is a guess
	PALETTE(config, m_palette, palette_device::BRG_3BIT).set_entries(8);
}

void b16_state::b16ex2(machine_config &config)
{
	b16_state::b16(config);
	I80286(config.replace(), m_maincpu, XTAL(16'000'000) / 2); // A80286-8 / S
	m_maincpu->set_addrmap(AS_PROGRAM, &b16_state::b16ex2_map);
	m_maincpu->set_addrmap(AS_IO, &b16_state::b16_io);
	m_maincpu->set_irq_acknowledge_callback(m_intm, FUNC(pic8259_device::inta_cb));

	// TODO: as above
	m_pit->set_clk<0>(XTAL(16'000'000) / 4);
//  m_pit->out_handler<0>()
	m_pit->set_clk<1>(XTAL(16'000'000) / 4);
//  m_pit->out_handler<1>()
	m_pit->set_clk<2>(XTAL(16'000'000) / 2);
//  m_pit->out_handler<2>()

	m_palette->set_entries(16);

	// 512~1.5M RAM
	// 5.25" FDD x 2
}


ROM_START( b16 )
	ROM_REGION16_LE( 0x4000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "ipl.rom", 0x0000, 0x4000, CRC(7c1c93d5) SHA1(2a1e63a689c316ff836f21646166b38714a18e03) )

	ROM_REGION( 0x40000, "kanji", ROMREGION_ERASEFF )
ROM_END

ROM_START( b16ex2 )
	ROM_REGION16_LE( 0x8000, "ipl", ROMREGION_ERASEFF )
	// M27128-L
	ROM_LOAD16_BYTE( "j4c-0072.4b", 0x0001, 0x4000, CRC(7f6e4143) SHA1(afe126639b7161562d93e955c1fc720a93e1596b))
	// M27128-H
	ROM_LOAD16_BYTE( "j5c-0072.6b", 0x0000, 0x4000, CRC(5f3c85ca) SHA1(4988d8e1e763268a62f2a86104db4d106babd242))

	ROM_REGION( 0x40000, "kanji", ROMREGION_ERASE00 )
	// Both HN62301AP, sockets labeled "KANJI1" and "KANJI2"
	ROM_LOAD16_BYTE( "7l1.11f", 0x00001, 0x20000, CRC(a31b3efd) SHA1(818b9fbb5109779bb3d66f76a93cc087c3b21698) )
	ROM_LOAD16_BYTE( "7m1.12f", 0x00000, 0x20000, CRC(9eff324e) SHA1(2dfe14cc4b884eb6bb3c953d0ba9677d663b256e) )
ROM_END

} // anonymous namespace


// TODO: pinpoint MB SKU for all
// Original would be MB-16001, flyer shows a "Basic Master" subtitle
COMP( 1982?, b16,     0,      0,      b16,     b16,   b16_state, empty_init, "Hitachi", "B16",    MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
// B16 EX 8086-2
// B16 SX superimpose option, 3.5" x 1.2MB floppies

COMP( 1983?, b16ex2,  0,      0,      b16ex2,  b16,   b16_state, empty_init, "Hitachi", "B16 EX-II",    MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
// B16 MX-II, 1~2MB RAM + 640x494 extra video mode + support for M[ultiuser?]DOS

// B16 EX-III, 386 based? <fill me>

// B16 LX, LCD variants
// Ricoh Mr. My Tool (Mr.マイツール), sister variants with 3.5 2HD floppies



b2m.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

Bashkiria-2M driver by Miodrag Milanovic

2008-03-28 Preliminary driver.

To get numbers, you have to hold down Shift.

B2M:
- Hit enter while the square block is showing - it will attempt to boot
  a disk.
- Or, just wait and a menu appears with choices S,L,W,R,G. It's all in
  Russian, and choosing any of them produces an error. (Doesn't work
  currently?)

B2MROM:
- At start you are in an empty ramdisk called A:
- You can use SAVE to create an empty file in the CP/M fashion, and you can
  TYPE it.
- You can go to Basic by typing BAS. This is corrupted and only a few
  commands are accepted: REM, LIST. The commands RUN and NEW cause a disk
  error and the machine freezes. SYSTEM quits back to A:
- There's nothing else on the romdisk.

So, in the end, nothing useful works.

Need a schematic so that the fdc could be repaired.

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


#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8251.h"
#include "b2m.h"
#include "formats/smx_dsk.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

/* Address maps */
void b2m_state::b2m_mem(address_map &map)
{
	// Dynamic, set with b2m_set_bank
}

void b2m_state::b2m_io(address_map &map)
{
	map.global_mask(0x1f);
	map(0x00, 0x03).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x04, 0x07).rw("ppi2", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x08, 0x0b).rw("ppi1", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x0c, 0x0c).rw(FUNC(b2m_state::localmachine_r), FUNC(b2m_state::localmachine_w));
	map(0x10, 0x13).rw(FUNC(b2m_state::palette_r), FUNC(b2m_state::palette_w));
	map(0x14, 0x15).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x18, 0x19).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x1c, 0x1f).rw(m_fdc, FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	map(0x1c, 0x1c).r(FUNC(b2m_state::fdc_status_hack_r));
}

void b2m_state::b2m_rom_io(address_map &map)
{
	map.global_mask(0x1f);
	map(0x00, 0x03).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x04, 0x07).rw("ppi3", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x08, 0x0b).rw("ppi1", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x0c, 0x0c).rw(FUNC(b2m_state::localmachine_r), FUNC(b2m_state::localmachine_w));
	map(0x10, 0x13).rw(FUNC(b2m_state::palette_r), FUNC(b2m_state::palette_w));
	map(0x14, 0x15).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x18, 0x19).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));
}


/* Input ports */
static INPUT_PORTS_START( b2m )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('{')

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('}')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')

	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('*') PORT_CHAR('8')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('&') PORT_CHAR('7')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('^') PORT_CHAR('6')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('%') PORT_CHAR('5')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('$') PORT_CHAR('4')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('#') PORT_CHAR('3')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('@') PORT_CHAR('2')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('_') PORT_CHAR('-')

	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR('<') PORT_CHAR(',')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR('\'') PORT_CHAR(';')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('+') PORT_CHAR('=')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("`") PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('!') PORT_CHAR('1')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('|') PORT_CHAR('\\')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR(')') PORT_CHAR('0')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('(') PORT_CHAR('9')

	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("*") PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F12) PORT_CHAR(UCHAR_MAMEKEY(F12))
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)

	PORT_START("LINE7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Rshift") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LAlt") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LCtrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("???") PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11))
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LShift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("LINE8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("??") PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("PgdN") PORT_CODE(KEYCODE_PGDN)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("end") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ins") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START("LINE9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('?') PORT_CHAR('/')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Del") PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\"') PORT_CHAR(':')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)    PORT_CHAR('>') PORT_CHAR('.')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("PgUp") PORT_CODE(KEYCODE_PGUP)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))

	PORT_START("LINE10")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_START("MONITOR")
	PORT_CONFNAME(0x01, 0x01, "Monitor")
	PORT_CONFSETTING(   0x01, "Color")
	PORT_CONFSETTING(   0x00, "B/W")
INPUT_PORTS_END

void b2m_state::b2m_floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_SMX_FORMAT);
}

static void b2m_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}


/* Machine driver */
void b2m_state::b2m(machine_config &config)
{
	/* basic machine hardware */
	i8080_cpu_device &maincpu(I8080(config, m_maincpu, 2000000));
	maincpu.set_addrmap(AS_PROGRAM, &b2m_state::b2m_mem);
	maincpu.set_addrmap(AS_IO, &b2m_state::b2m_io);
	maincpu.set_vblank_int("screen", FUNC(b2m_state::vblank_interrupt));
	maincpu.in_inta_func().set("pic", FUNC(pic8259_device::acknowledge));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(384, 256);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(b2m_state::screen_update));
	screen.set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(b2m_state::b2m_palette), 4);

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(0);
	m_pit->out_handler<0>().set(m_pic, FUNC(pic8259_device::ir1_w));
	m_pit->set_clk<1>(2000000);
	m_pit->out_handler<1>().set(FUNC(b2m_state::pit_out1));
	m_pit->set_clk<2>(2000000);
	m_pit->out_handler<2>().set(m_pit, FUNC(pit8253_device::write_clk0));

	i8255_device &ppi1(I8255(config, "ppi1"));
	ppi1.out_pa_callback().set(FUNC(b2m_state::ppi1_porta_w));
	ppi1.in_pb_callback().set(FUNC(b2m_state::ppi1_portb_r));
	ppi1.out_pb_callback().set(FUNC(b2m_state::ppi1_portb_w));
	ppi1.out_pc_callback().set(FUNC(b2m_state::ppi1_portc_w));

	i8255_device &ppi2(I8255(config, "ppi2"));
	ppi2.out_pc_callback().set(FUNC(b2m_state::ppi2_portc_w));

	i8255_device &ppi3(I8255(config, "ppi3"));
	ppi3.in_pa_callback().set(FUNC(b2m_state::romdisk_porta_r));
	ppi3.out_pb_callback().set(FUNC(b2m_state::romdisk_portb_w));
	ppi3.out_pc_callback().set(FUNC(b2m_state::romdisk_portc_w));

	PIC8259(config, m_pic, 0);
	m_pic->out_int_callback().set_inputline(m_maincpu, 0);

	/* sound */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	/* uart */
	I8251(config, "uart", 0);

	FD1793(config, m_fdc, 8_MHz_XTAL / 8);
	m_fdc->drq_wr_callback().set(FUNC(b2m_state::fdc_drq));

	FLOPPY_CONNECTOR(config, m_fd[0], b2m_floppies, "525qd", b2m_state::b2m_floppy_formats);
	FLOPPY_CONNECTOR(config, m_fd[1], b2m_floppies, "525qd", b2m_state::b2m_floppy_formats);
	SOFTWARE_LIST(config, "flop_list").set_original("b2m");

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("128K").set_default_value(0x00);
}

void b2m_state::b2mrom(machine_config &config)
{
	b2m(config);
	m_maincpu->set_addrmap(AS_IO, &b2m_state::b2m_rom_io);
}

/* ROM definition */

ROM_START( b2m )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "b2m.rom", 0x0000, 0x2000, CRC(3f3214d6) SHA1(dd93e7fbabf14d1aed6777fe1ccfe0a3ca8fcaf2) )
ROM_END

ROM_START( b2mrom )
	ROM_REGION( 0x80c0, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "bios2.rom",  0x0000, 0x2000, CRC(c22a98b7) SHA1(7de91e653bf4b191ded62cf21532578268e4a2c1) )
	ROM_LOAD( "ramdos.sys", 0x2000, 0x60c0, CRC(91ed6df0) SHA1(4fd040f2647a6b7930c330c75560a035027d0606) )
ROM_END


/* Driver */

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY  FULLNAME                 FLAGS */
COMP( 1989, b2m,    0,      0,      b2m,     b2m,   b2m_state, empty_init, "BNPO",  "Bashkiria-2M",          MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)
COMP( 1989, b2mrom, b2m,    0,      b2mrom,  b2m,   b2m_state, empty_init, "BNPO",  "Bashkiria-2M ROM-disk", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)



babbage.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

    Babbage-2nd skeleton driver (19/OCT/2011)

    http://takeda-toshiya.my.coocan.jp/babbage/index.html

    Pasting:
        0-F : as is
        INC : ^
        AD : -
        DA : =
        GO : X

    Here is a test program to turn on the LEDs.
    Copy the text and Paste into the emulator.
    =3E^=0F^=D3^=13^=3E^=07^=D3^=13^=3E^=00^=D3^=12^=76^-1000X


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

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/timer.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "machine/z80daisy.h"
#include "video/pwm.h"
#include "babbage.lh"


namespace {

#define MAIN_CLOCK 25e5

class babbage_state : public driver_device
{
public:
	babbage_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pio(*this, "z80pio_%u", 1U)
		, m_ctc(*this, "z80ctc")
		, m_display(*this, "display")
		, m_io_keyboard(*this, "X%u", 0U)
		, m_leds(*this, "led%u", 0U)
	{ }

	void babbage(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	uint8_t pio2_a_r();
	void pio1_b_w(uint8_t data);
	void pio2_b_w(uint8_t data);
	void ctc_z0_w(int state);
	void ctc_z1_w(int state);
	void ctc_z2_w(int state);
	TIMER_DEVICE_CALLBACK_MEMBER(keyboard_callback);

	void babbage_io(address_map &map) ATTR_COLD;
	void babbage_map(address_map &map) ATTR_COLD;

	uint8_t m_seg = 0U;
	uint8_t m_key = 0U;
	uint8_t m_prev_key = 0U;
	bool m_step = 0;

	required_device<z80_device> m_maincpu;
	required_device_array<z80pio_device, 2> m_pio;
	required_device<z80ctc_device> m_ctc;
	required_device<pwm_display_device> m_display;
	required_ioport_array<4> m_io_keyboard;
	output_finder<8> m_leds;
};



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

    Address Map

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

void babbage_state::babbage_map(address_map &map)
{
	map.global_mask(0x3fff);
	map(0x0000, 0x07ff).rom();
	map(0x1000, 0x17ff).ram();
}

void babbage_state::babbage_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x10, 0x13).rw(m_pio[0], FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x20, 0x23).rw(m_pio[1], FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
}



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

    Keyboard Layout

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

// no idea of the actual key matrix
static INPUT_PORTS_START( babbage )
	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("GO") PORT_CODE(KEYCODE_X) PORT_CHAR('X')

	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("AD") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DA") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=')

	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("INC") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
INPUT_PORTS_END

/* Z80-CTC Interface */

void babbage_state::ctc_z0_w(int state)
{
}

void babbage_state::ctc_z1_w(int state)
{
}

void babbage_state::ctc_z2_w(int state)
{
}

/* Z80-PIO Interface */

// The 8 LEDs
// bios never writes here - you need to set PIO for output yourself - see test program above
void babbage_state::pio1_b_w(uint8_t data)
{
	for (int i = 0; i < 8; i++)
		m_leds[i] = BIT(data, i);
}

uint8_t babbage_state::pio2_a_r()
{
	if (!machine().side_effects_disabled())
		m_maincpu->set_input_line(0, CLEAR_LINE); // release interrupt
	return m_key;
}

void babbage_state::pio2_b_w(uint8_t data)
{
	if (BIT(data, 7))
		m_step = false;
	else
	if (!m_step)
	{
		m_seg = data;
		m_step = true;
	}
	else
		m_display->matrix(data, m_seg);
}

static const z80_daisy_config babbage_daisy_chain[] =
{// need to check the order
	{ "z80pio_1" },
	{ "z80pio_2" },
	{ "z80ctc" },
	{ nullptr }
};

TIMER_DEVICE_CALLBACK_MEMBER(babbage_state::keyboard_callback)
{
	u8 inp, data = 0xff;

	for (u8 i = 0; i < 4; i++)
	{
		inp = m_io_keyboard[i]->read();

		for (u8 j = 0; j < 5; j++)
			if (BIT(inp, j))
				data = (j << 2) | i;
	}

	/* make sure only one keystroke */
	if (data != m_prev_key)
		m_prev_key = data;
	else
		data = 0xff;

	/* while key is down, activate strobe. When key released, deactivate strobe which causes an interrupt */
	if (data < 0xff)
	{
		m_key = data;
		m_pio[1]->strobe(0, 0);
	}
	else
		m_pio[1]->strobe(0, 1);
}

void babbage_state::machine_start()
{
	m_leds.resolve();

	save_item(NAME(m_seg));
	save_item(NAME(m_key));
	save_item(NAME(m_prev_key));
	save_item(NAME(m_step));
}

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

    Machine driver

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

void babbage_state::babbage(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, MAIN_CLOCK); //2.5MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &babbage_state::babbage_map);
	m_maincpu->set_addrmap(AS_IO, &babbage_state::babbage_io);
	m_maincpu->set_daisy_config(babbage_daisy_chain);

	/* video hardware */
	config.set_default_layout(layout_babbage);
	PWM_DISPLAY(config, m_display).set_size(6, 8);
	m_display->set_segmask(0x3f, 0xff);

	/* Devices */
	Z80CTC(config, m_ctc, MAIN_CLOCK);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->zc_callback<0>().set(FUNC(babbage_state::ctc_z0_w));
	m_ctc->zc_callback<1>().set(FUNC(babbage_state::ctc_z1_w));
	m_ctc->zc_callback<2>().set(FUNC(babbage_state::ctc_z2_w));

	Z80PIO(config, m_pio[0], MAIN_CLOCK);
	m_pio[0]->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio[0]->out_pb_callback().set(FUNC(babbage_state::pio1_b_w));

	Z80PIO(config, m_pio[1], MAIN_CLOCK);
	m_pio[1]->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio[1]->in_pa_callback().set(FUNC(babbage_state::pio2_a_r));
	m_pio[1]->out_pb_callback().set(FUNC(babbage_state::pio2_b_w));

	TIMER(config, "keyboard_timer", 0).configure_periodic(FUNC(babbage_state::keyboard_callback), attotime::from_hz(30));
}


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

    Game driver

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

ROM_START(babbage)
	ROM_REGION(0x0800, "maincpu", ROMREGION_ERASEFF)
	ROM_LOAD("mon.rom",    0x0000, 0x0200, CRC(469bd607) SHA1(8f3489a0f96de6a03b05c1ee01b89d9848f4b152) )
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY               FULLNAME       FLAGS
COMP( 1986, babbage, 0,      0,      babbage, babbage, babbage_state, empty_init, "Mr Takafumi Aihara", "Babbage-2nd", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



banctec.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Felipe Sanches
/***************************************************************************
  This driver covers only the Operator Panel of the BancTec 91690 Document Processor equipment

  Author: Felipe Sanches <juca@members.fsf.org>

  Maintainence Manual: <https://garoa.net.br/w/images/PAINEL_BANCTEC_91690.PDF>

  The display is considered a replaceable part, not repairable. Therefore the manual
  has no circuit description or schematic. And so, much of the below is guesswork.

  Machine starts up and clears video ram, then nothing more. Location of videoram
  in the memory map is unknown, left at 8000 for now.

*/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "cpu/m6800/m6801.h"
#include "video/mc6845.h"
#include "emupal.h"
#include "screen.h"


namespace {

class banctec_state : public driver_device
{
public:
	banctec_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_palette(*this, "palette")
		, m_video_address(0)
		, m_maincpu(*this, "maincpu")
		, m_videoram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
	{ }

	void banctec(machine_config &config);

protected:
	MC6845_UPDATE_ROW(crtc_update_row);
	MC6845_ON_UPDATE_ADDR_CHANGED(crtc_addr);
	void videoram_w(u8 data);

	virtual void machine_reset() override ATTR_COLD;
	void banctec_mcu_mem(address_map &map) ATTR_COLD;
	void banctec_mem(address_map &map) ATTR_COLD;

private:
	required_device<palette_device> m_palette;
	u8 m_video_address;
	required_device<cpu_device> m_maincpu;
	required_shared_ptr<u8> m_videoram;
	required_region_ptr<u8> m_p_chargen;
};

void banctec_state::banctec_mem(address_map &map)
{
	map(0x0000, 0x07ff).rom();
	map(0x0800, 0xffff).ram(); /* Probably wrong. Must be verified on pcb! */
}

void banctec_state::banctec_mcu_mem(address_map &map)
{
	map(0x2000, 0x2000).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x2001, 0x2001).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x2003, 0x2003).w(FUNC(banctec_state::videoram_w));
	map(0x8000, 0x80ff).ram().share("videoram"); /* Probably wrong. Must be verified on pcb! */
	map(0xe000, 0xffff).rom().region("mcu", 0x0000);
}

void banctec_state::machine_reset()
{
}


/****************************
* Video/Character functions *
****************************/

void banctec_state::videoram_w(u8 data)
{
	m_videoram[m_video_address] = data;
	m_video_address++;
}

/* ROCKWELL 6545 - Transparent Memory Addressing */

MC6845_UPDATE_ROW( banctec_state::crtc_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u32 *p = &bitmap.pix(y);

	for (u16 x = 0; x < x_count; x++)
	{
		u16 const mem = (ma + x) & 0xff;
		u8 const chr = m_videoram[mem];
		u8 const gfx = m_p_chargen[chr | (ra << 8)] ^ ((x == cursor_x) ? 0xff : 0);

		/* Display a scanline of a character (8 pixels) */
		*p++ = palette[BIT(gfx, 7)];
		*p++ = palette[BIT(gfx, 6)];
		*p++ = palette[BIT(gfx, 5)];
		*p++ = palette[BIT(gfx, 4)];
		*p++ = palette[BIT(gfx, 3)];
		*p++ = palette[BIT(gfx, 2)];
		*p++ = palette[BIT(gfx, 1)];
		*p++ = palette[BIT(gfx, 0)];
	}
}

MC6845_ON_UPDATE_ADDR_CHANGED(banctec_state::crtc_addr)
{
	/* What is this function meant to do ? */
	m_video_address = address;
}

/******************************
* Graphics Decode Information *
******************************/

const gfx_layout banctec_gfx_layout =
{
	8, 8,               /* 8x8 characters */
	256,                /* 256 characters */
	1,                  /* 1 bits per pixel */
	{0},                /* no bitplanes; 1 bit per pixel */
	{0, 1, 2, 3, 4, 5, 6, 7},
	{0 * 256*8, 1 * 256*8, 2 * 256*8, 3 * 256*8, 4 * 256*8, 5 * 256*8, 6 * 256*8, 7 * 256*8},
	8                 /* size of one char */
};

static GFXDECODE_START( gfx_banctec )
	GFXDECODE_ENTRY( "chargen", 0x00000, banctec_gfx_layout, 0, 1 )
GFXDECODE_END

void banctec_state::banctec(machine_config &config)
{
	/* basic machine hardware */

	I80C31(config, m_maincpu, XTAL(11'059'200));
	m_maincpu->set_addrmap(AS_PROGRAM, &banctec_state::banctec_mem);

	m6803_cpu_device &mcu(M6803(config, "mcu", 4000000));     /* Actual MCU is a Motorola 6803 and the clock frequency is still unknown */
	mcu.set_addrmap(AS_PROGRAM, &banctec_state::banctec_mcu_mem);

// The video signal is generated by a R6545EAP character generator chip
// The U20 EPROM holds the image data for the character set.

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_size((52+1)*8, (31+1)*8);
	screen.set_visarea(0*8, 40*8-1, 0*8, 25*8-1);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	PALETTE(config, m_palette, palette_device::MONOCHROME);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_banctec);

	r6545_1_device &crtc(R6545_1(config, "crtc", XTAL(2'000'000))); /* (?) */
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(8);
	crtc.set_update_row_callback(FUNC(banctec_state::crtc_update_row));
	crtc.set_on_update_addr_change_callback(FUNC(banctec_state::crtc_addr));
}

ROM_START(banctec)
	ROM_REGION(0x800, "maincpu", 0)
	ROM_LOAD("banctec_eseries_panel_opnl.u20", 0x000, 0x800, CRC(c2ab9c06) SHA1(a296589034f656790ad5ffbce028dd846a40cf03))

	ROM_REGION(0x2000, "mcu", 0)
	ROM_LOAD("banctec_eseries_panel.u8", 0x0000, 0x2000, CRC(f3335e0a) SHA1(5ca45fdcb7ef45a65c28c79abfa9ebb7a8a06619))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("banctec_eseries_panel.u20", 0x0000, 0x1000, CRC(5b6ecec9) SHA1(35aff8f965bce77205e3a43d71e39097585091a7))
ROM_END

} // anonymous namespace


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

  Game driver(s)

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

/*    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT  STATE          INIT        COMPANY              FULLNAME                 FLAGS */
CONS( 1989, banctec, 0,      0,      banctec, 0,     banctec_state, empty_init, "DALE Electronics",  "BancTec ESeries Panel", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



basf7100.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    BASF 7100

    This system is based on (or even identical to) the DigiLog Microterm II

    Models:
    - 7120: 24k disk controller memory, 3x 5.25" single sided
    - 7125: 32k disk controller memory, 3x 5.25" double sided
    - 7130: 32k disk controller memory, 1x 5.25" double sided, 1x Winchester

    Hardware:
    - Z-80A
    - I8259A
    - I8251
    - COM8116
    - 3x 8255A
    - 64k memory
    - 5.0688 MHz XTAL
    - 4x DSW8
    - External ports: Printer, Dialer, Data Comm I/O, Aux I/O

    Video board:
    - Motorola 160A002-B (?)
    - 2x 8255A
    - 18.720 MHz XTAL

    Floppy controller:
    - Z-80A
    - 24k/32k memory
    - FD1791B-02

    Aux I/O board:
    - I8251
    - COM8116
    - 5.0688 MHz XTAL

    TODO:
    - Dump real character ROM
    - Improve video emulation
    - Find documentation for switches S2, S3, S4 (might be app. specific)
    - Serial interrupts, flags, control

    Notes:
    - Runs the BOS operating system, possibly also CP/M?

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

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/com8116.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/pic8259.h"
#include "machine/wd_fdc.h"
#include "bus/centronics/ctronics.h"
#include "bus/rs232/rs232.h"
#include "imagedev/floppy.h"
#include "basf7100_kbd.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"


namespace {


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

class basf7100_state : public driver_device
{
public:
	basf7100_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_fdccpu(*this, "fdccpu"),
		m_pic(*this, "pic"),
		m_ppi(*this, "ppi%u", 0U),
		m_usart(*this, "usart%u", 0U),
		m_centronics(*this, "centronics"),
		m_fdc(*this, "fdc"),
		m_floppy(*this, "fdc:%u", 0),
		m_screen(*this, "screen"),
		m_palette(*this, "palette"),
		m_shared_ram(*this, "shared_ram"),
		m_chargen(*this, "chargen"),
		m_bootview(*this, "bootview"),
		m_cursor_col(0x00), m_cursor_row(0x00),
		m_highlight(0x00),
		m_roll_offset(0x00),
		m_sod(0x0000),
		m_fdc_intrq_vector(0),
		m_fdc_drq(false),
		m_int_flags(0x00)
	{ }

	void basf7100(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;
	required_device<z80_device> m_fdccpu;
	required_device<pic8259_device> m_pic;
	required_device_array<i8255_device, 5> m_ppi;
	required_device_array<i8251_device, 2> m_usart;
	required_device<centronics_device> m_centronics;
	required_device<fd1791_device> m_fdc;
	required_device_array<floppy_connector, 3> m_floppy;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_shared_ptr<uint8_t> m_shared_ram;
	required_region_ptr<uint8_t> m_chargen;
	memory_view m_bootview;

	enum : uint8_t
	{
		INT_KEYBOARD   = 0x08,
		INT_VBLANK     = 0x10,
		INT_CENTRONICS = 0x40
	};

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	void fdc_mem_map(address_map &map) ATTR_COLD;
	void fdc_io_map(address_map &map) ATTR_COLD;

	uint8_t mmio_r(offs_t offset);
	void mmio_w(offs_t offset, uint8_t data);

	void keyboard_w(uint8_t data);

	void cursor_col_w(uint8_t data);
	void cursor_row_w(uint8_t data);
	void highlight_w(uint8_t data);
	void roll_offset_w(uint8_t data);
	void sod_high_w(uint8_t data);
	void sod_low_w(uint8_t data);
	void vblank_w(int state);
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void centronics_ctrl_w(uint8_t data);
	void centronics_busy_w(int state);

	void fdc_drq_w(int state);
	uint8_t fdc_ctrl_r();
	void fdc_ctrl_w(uint8_t data);
	void fdc_intrq_vector_w(uint8_t data);
	void unk_cc_w(uint8_t data);

	IRQ_CALLBACK_MEMBER(maincpu_irq_callback);
	IRQ_CALLBACK_MEMBER(fdccpu_irq_callback);

	uint8_t m_cursor_col;
	uint8_t m_cursor_row;
	uint8_t m_highlight;
	uint8_t m_roll_offset;
	uint16_t m_sod;
	uint8_t m_fdc_intrq_vector;
	bool m_fdc_drq;
	uint8_t m_int_flags;
};


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

void basf7100_state::mem_map(address_map &map)
{
	map(0x0000, 0x9fff).ram().share("shared_ram");
	map(0xa000, 0xffff).ram();
	map(0xff00, 0xffff).rw(FUNC(basf7100_state::mmio_r), FUNC(basf7100_state::mmio_w));
	map(0x0000, 0xffff).view(m_bootview);
	m_bootview[0](0x0000, 0x001f).mirror(0xffe0).rom().region("maincpu", 0);
}

void basf7100_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).rw(m_ppi[0], FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x04, 0x07).rw(m_ppi[1], FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x08, 0x09).mirror(0x02).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x0c, 0x0f).rw(m_ppi[2], FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x10, 0x11).rw(m_usart[0], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x12, 0x12).w("brg0", FUNC(com8116_device::stt_str_w)); // or str_stt_w
	map(0x14, 0x15).rw(m_usart[1], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x16, 0x16).w("brg1", FUNC(com8116_device::stt_str_w)); // or str_stt_w
//  map(0x17, 0x17) rs232 flags/control
	map(0x18, 0x18).lr8(NAME([this] () -> uint8_t { return m_int_flags; }));
	map(0x1c, 0x1c).portr("S1");
	map(0x1d, 0x1d).portr("S2");
	map(0x1e, 0x1e).portr("S3");
	map(0x1f, 0x1f).portr("S4");
//  map(0xb0, 0xb3) display hardware clear
	map(0xb8, 0xbb).rw(m_ppi[3], FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xbc, 0xbf).lr8(NAME([this] (offs_t offset) -> uint8_t { return m_ppi[4]->read(offset ^ 3); }));
	map(0xbc, 0xbf).lw8(NAME([this] (offs_t offset, uint8_t data) { m_ppi[4]->write(offset ^ 3, data); }));
	map(0xc0, 0xc3).rw(m_fdc, FUNC(fd1791_device::read), FUNC(fd1791_device::write));
	map(0xc4, 0xc4).rw(FUNC(basf7100_state::fdc_ctrl_r), FUNC(basf7100_state::fdc_ctrl_w));
	map(0xca, 0xca).w(FUNC(basf7100_state::fdc_intrq_vector_w));
	map(0xcc, 0xcc).w(FUNC(basf7100_state::unk_cc_w));
}

void basf7100_state::fdc_mem_map(address_map &map)
{
	map(0x0000, 0x9fff).ram().share("shared_ram");
	map(0xa000, 0xfbff).ram();
	map(0xfc00, 0xffff).rom().region("fdccpu", 0);
	map(0xff00, 0xffff).rw(FUNC(basf7100_state::mmio_r), FUNC(basf7100_state::mmio_w));
}

void basf7100_state::fdc_io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0xff).rw(FUNC(basf7100_state::mmio_r), FUNC(basf7100_state::mmio_w));
}


//**************************************************************************
//  INPUT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( basf7100 )
	PORT_START("S1")
	PORT_DIPNAME(0x0f, 0x0e, "Baud Rate")      PORT_DIPLOCATION("S1:1,2,3,4")
	PORT_DIPSETTING(   0x00, "50")
	PORT_DIPSETTING(   0x01, "75")
	PORT_DIPSETTING(   0x02, "110")
	PORT_DIPSETTING(   0x03, "135")
	PORT_DIPSETTING(   0x04, "150")
	PORT_DIPSETTING(   0x05, "300")
	PORT_DIPSETTING(   0x06, "600")
	PORT_DIPSETTING(   0x07, "1200")
	PORT_DIPSETTING(   0x08, "1800")
	PORT_DIPSETTING(   0x09, "2005")
	PORT_DIPSETTING(   0x0a, "2400")
	PORT_DIPSETTING(   0x0b, "3600")
	PORT_DIPSETTING(   0x0c, "4800")
	PORT_DIPSETTING(   0x0d, "7200")
	PORT_DIPSETTING(   0x0e, "9600")
	PORT_DIPSETTING(   0x0f, "19800")
	PORT_DIPNAME(0x30, 0x30, "Data Bits")      PORT_DIPLOCATION("S1:5,6")
	PORT_DIPSETTING(   0x00, "5")
	PORT_DIPSETTING(   0x10, "6")
	PORT_DIPSETTING(   0x20, "7")
	PORT_DIPSETTING(   0x30, "8")
	PORT_DIPNAME(0x40, 0x00, "Parity Enabled") PORT_DIPLOCATION("S1:7")
	PORT_DIPSETTING(   0x00, DEF_STR(No))
	PORT_DIPSETTING(   0x40, DEF_STR(Yes))
	PORT_DIPNAME(0x80, 0x80, "Parity")         PORT_DIPLOCATION("S1:8")
	PORT_DIPSETTING(   0x00, "Odd")
	PORT_DIPSETTING(   0x80, "Even")

	PORT_START("S2")
	PORT_DIPUNKNOWN_DIPLOC(0x01, 0x01, "S2:1")
	PORT_DIPUNKNOWN_DIPLOC(0x02, 0x02, "S2:2")
	PORT_DIPUNKNOWN_DIPLOC(0x04, 0x04, "S2:3")
	PORT_DIPUNKNOWN_DIPLOC(0x08, 0x08, "S2:4")
	PORT_DIPUNKNOWN_DIPLOC(0x10, 0x10, "S2:5")
	PORT_DIPUNKNOWN_DIPLOC(0x20, 0x20, "S2:6")
	PORT_DIPUNKNOWN_DIPLOC(0x40, 0x40, "S2:7")
	PORT_DIPUNKNOWN_DIPLOC(0x80, 0x80, "S2:8")

	PORT_START("S3")
	PORT_DIPUNKNOWN_DIPLOC(0x01, 0x01, "S3:1")
	PORT_DIPUNKNOWN_DIPLOC(0x02, 0x02, "S3:2")
	PORT_DIPUNKNOWN_DIPLOC(0x04, 0x04, "S3:3")
	PORT_DIPUNKNOWN_DIPLOC(0x08, 0x08, "S3:4")
	PORT_DIPUNKNOWN_DIPLOC(0x10, 0x10, "S3:5")
	PORT_DIPUNKNOWN_DIPLOC(0x20, 0x20, "S3:6")
	PORT_DIPUNKNOWN_DIPLOC(0x40, 0x40, "S3:7")
	PORT_DIPUNKNOWN_DIPLOC(0x80, 0x80, "S3:8")

	PORT_START("S4")
	PORT_DIPUNKNOWN_DIPLOC(0x01, 0x01, "S4:1")
	PORT_DIPUNKNOWN_DIPLOC(0x02, 0x02, "S4:2")
	PORT_DIPUNKNOWN_DIPLOC(0x04, 0x04, "S4:3")
	PORT_DIPUNKNOWN_DIPLOC(0x08, 0x08, "S4:4")
	PORT_DIPUNKNOWN_DIPLOC(0x10, 0x10, "S4:5")
	PORT_DIPUNKNOWN_DIPLOC(0x20, 0x20, "S4:6")
	PORT_DIPUNKNOWN_DIPLOC(0x40, 0x40, "S4:7")
	PORT_DIPUNKNOWN_DIPLOC(0x80, 0x80, "S4:8")
INPUT_PORTS_END

void basf7100_state::keyboard_w(uint8_t data)
{
	// PPI OBF
	m_int_flags &= ~INT_KEYBOARD;
	m_int_flags |= BIT(data, 1) ? INT_KEYBOARD : 0x00;

	m_pic->ir3_w(BIT(data, 1));

	// TODO: keyboard bell
}


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

static const gfx_layout crt8002_charlayout =
{
	8, 12,
	128,
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8*16
};

static GFXDECODE_START( gfx_crt8002 )
	GFXDECODE_ENTRY( "chargen", 0, crt8002_charlayout, 0, 1 )
GFXDECODE_END

void basf7100_state::cursor_col_w(uint8_t data)
{
	m_cursor_col = data;
}

void basf7100_state::cursor_row_w(uint8_t data)
{
	// 7-------  graphics enable?
	// -65-----  unknown
	// ---43210  cursor row

	m_cursor_row = data & 0x1f;
}

void basf7100_state::highlight_w(uint8_t data)
{
	logerror("highlight = %02x\n", data);
	m_highlight = data;
}

void basf7100_state::roll_offset_w(uint8_t data)
{
	m_roll_offset = data;
}

void basf7100_state::sod_high_w(uint8_t data)
{
	m_sod = (data << 8) | (m_sod & 0x00ff);
}

void basf7100_state::sod_low_w(uint8_t data)
{
	// 7654----  sod low byte
	// ----3210  set to input (clear in progress flag?)

	m_sod = (m_sod & 0xff00) | ((data & 0xf0) << 0);
}

void basf7100_state::vblank_w(int state)
{
	m_int_flags &= ~INT_VBLANK;
	m_int_flags |= state ? INT_VBLANK : 0x00;

	m_pic->ir4_w(state);
}

uint32_t basf7100_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const rgb_t *palette = m_palette->palette()->entry_list_raw();
	uint16_t addr = m_sod + (m_roll_offset * 16);

	for (int y = 0; y < 24; y++)
	{
		uint8_t line_attr = 0x00;

		for (int x = 0; x < 80; x++)
		{
			if (addr >= (m_sod + 0x780))
				addr = m_sod;

			uint8_t code = m_shared_ram[addr];

			// draw 12 lines
			for (int i = 0; i < 12; i++)
			{
				uint8_t data = m_chargen[(((code & 0x7f) << 4) + i)];

				// character attributes
				bool inverse = false;
				bool half = false;
				bool underline = false;
				bool blink = false;

				// global attributes
				if (BIT(m_highlight, 3) && BIT(code, 7))
				{
					inverse = bool(BIT(m_highlight, 4));
					half = bool(BIT(m_highlight, 5));
					underline = bool(BIT(m_highlight, 6));
					blink = bool(BIT(m_highlight, 7));
				}

				// line attribute
				if (BIT(m_highlight, 1))
				{
					// control characters
					if ((code & 0xf0) == 0x90) // only 0x90-0x9f, not 0x10-0x1f?
						line_attr = code & 0x0f;

					inverse = bool(BIT(line_attr, 0));
					half = bool(BIT(line_attr, 1));
					underline = bool(BIT(line_attr, 2));
					blink = bool(BIT(line_attr, 3));
				}

				// correct some issues because of the wrong charrom
				if ((code & 0x7f) == 0 || ((code & 0x7f) >= 0x10 && (code & 0x7f) <= 0x14))
					data = 0x00;

				// display of control characters
				if (BIT(m_highlight, 0) == 0 && (code & 0x60) == 0)
					data = 0x00;

				if (underline && i == 10)
					data = 0xff;

				if (blink && m_screen->frame_number() & 0x08) // timing?
					data = 0x00;

				if (inverse)
					data ^= 0xff;

				if (y == m_cursor_row && x == m_cursor_col && m_screen->frame_number() & 0x08)
					data = 0xff;

				// 8 pixels of the character
				bitmap.pix(y * 12 + i, x * 8 + 0) = palette[BIT(data, 7) ? 2 - (half ? 1 : 0) : 0];
				bitmap.pix(y * 12 + i, x * 8 + 1) = palette[BIT(data, 6) ? 2 - (half ? 1 : 0) : 0];
				bitmap.pix(y * 12 + i, x * 8 + 2) = palette[BIT(data, 5) ? 2 - (half ? 1 : 0) : 0];
				bitmap.pix(y * 12 + i, x * 8 + 3) = palette[BIT(data, 4) ? 2 - (half ? 1 : 0) : 0];
				bitmap.pix(y * 12 + i, x * 8 + 4) = palette[BIT(data, 3) ? 2 - (half ? 1 : 0) : 0];
				bitmap.pix(y * 12 + i, x * 8 + 5) = palette[BIT(data, 2) ? 2 - (half ? 1 : 0) : 0];
				bitmap.pix(y * 12 + i, x * 8 + 6) = palette[BIT(data, 1) ? 2 - (half ? 1 : 0) : 0];
				bitmap.pix(y * 12 + i, x * 8 + 7) = palette[BIT(data, 0) ? 2 - (half ? 1 : 0) : 0];
			}

			// next char
			addr++;
		}
	}

	return 0;
}


//**************************************************************************
//  CENTRONICS
//**************************************************************************

void basf7100_state::centronics_ctrl_w(uint8_t data)
{
	// ppi configured in mode1 (strobed output)
	m_centronics->write_strobe(BIT(data, 7));
}

void basf7100_state::centronics_busy_w(int state)
{
	m_int_flags &= ~INT_CENTRONICS;
	m_int_flags |= state ? 0x00 : INT_CENTRONICS;

	m_pic->ir6_w(state);
}


//**************************************************************************
//  FLOPPY
//**************************************************************************

static void basf7100_floppies(device_slot_interface &device)
{
	device.option_add("basf6106", FLOPPY_525_SSSD);
}

void basf7100_state::fdc_drq_w(int state)
{
	m_fdc_drq = bool(state);
}

uint8_t basf7100_state::fdc_ctrl_r()
{
	// 7-------  unknown, checked after seek cmd
	// -654321-  unknown
	// -------0  fdc drq

	uint8_t data = 0x00;

	data |= 1 << 7;
	data |= (m_fdc_drq ? 0x01 : 0x00);

	return data;
}

void basf7100_state::fdc_ctrl_w(uint8_t data)
{
	// 7654----  unknown
	// ----3---  motor on?
	// -----2--  unknown
	// ------10  drive select

	if (data != 0x08)
		logerror("fdc_ctrl_w: %04x\n", data);

	floppy_image_device *floppy = nullptr;

	if ((data & 0x03) < 3)
		floppy = m_floppy[data & 0x03]->get_device();

	m_fdc->set_floppy(floppy);

	// motor runs all the time for now
	if (floppy)
		floppy->mon_w(0);

	// set to mfm
	m_fdc->dden_w(0);
}

void basf7100_state::fdc_intrq_vector_w(uint8_t data)
{
	m_fdc_intrq_vector = data;
}

void basf7100_state::unk_cc_w(uint8_t data)
{
	logerror("unk_cc_w: %02x\n", data);

	m_bootview.disable();
	m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
	m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
}


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

uint8_t basf7100_state::mmio_r(offs_t offset)
{
	return m_maincpu->space(AS_IO).read_byte(offset);
}

void basf7100_state::mmio_w(offs_t offset, uint8_t data)
{
	m_maincpu->space(AS_IO).write_byte(offset, data);
}

IRQ_CALLBACK_MEMBER( basf7100_state::maincpu_irq_callback )
{
	uint32_t vector = 0;

	vector |= m_pic->acknowledge() << 16;
	vector |= m_pic->acknowledge();
	vector |= m_pic->acknowledge() << 8;

	return vector;
}

IRQ_CALLBACK_MEMBER( basf7100_state::fdccpu_irq_callback )
{
	return m_fdc_intrq_vector;
}

void basf7100_state::machine_start()
{
	// register for save states
	save_item(NAME(m_cursor_col));
	save_item(NAME(m_cursor_row));
	save_item(NAME(m_highlight));
	save_item(NAME(m_roll_offset));
	save_item(NAME(m_sod));
	save_item(NAME(m_fdc_intrq_vector));
	save_item(NAME(m_fdc_drq));
	save_item(NAME(m_int_flags));
}

void basf7100_state::machine_reset()
{
	m_bootview.select(0);
	m_fdccpu->set_pc(0xfc00);
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void basf7100_state::basf7100(machine_config &config)
{
	Z80(config, m_maincpu, 4000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &basf7100_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &basf7100_state::io_map);
	m_maincpu->set_irq_acknowledge_callback(FUNC(basf7100_state::maincpu_irq_callback));

	Z80(config, m_fdccpu, 4000000);
	m_fdccpu->set_addrmap(AS_PROGRAM, &basf7100_state::fdc_mem_map);
	m_fdccpu->set_addrmap(AS_IO, &basf7100_state::fdc_io_map);
	m_fdccpu->set_irq_acknowledge_callback(FUNC(basf7100_state::fdccpu_irq_callback));

	config.set_maximum_quantum(attotime::from_usec(600));

	PIC8259(config, m_pic, 0);
	m_pic->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	I8251(config, m_usart[0], 0); // unknown clock
	m_usart[0]->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_usart[0]->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_usart[0]->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));

	com8116_device &brg0(COM8116(config, "brg0", 5.0688_MHz_XTAL));
	brg0.fr_handler().set(m_usart[0], FUNC(i8251_device::write_rxc));
	brg0.ft_handler().set(m_usart[0], FUNC(i8251_device::write_txc));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_usart[0], FUNC(i8251_device::write_rxd));
	rs232.cts_handler().set(m_usart[0], FUNC(i8251_device::write_cts));
	rs232.dsr_handler().set(m_usart[0], FUNC(i8251_device::write_dsr));

	I8251(config, m_usart[1], 0); // unknown clock
	m_usart[1]->txd_handler().set("auxrs232", FUNC(rs232_port_device::write_txd));
	m_usart[1]->rts_handler().set("auxrs232", FUNC(rs232_port_device::write_rts));
	m_usart[1]->dtr_handler().set("auxrs232", FUNC(rs232_port_device::write_dtr));

	com8116_device &brg1(COM8116(config, "brg1", 5.0688_MHz_XTAL));
	brg1.fr_handler().set(m_usart[1], FUNC(i8251_device::write_rxc));
	brg1.ft_handler().set(m_usart[1], FUNC(i8251_device::write_txc));

	rs232_port_device &auxrs232(RS232_PORT(config, "auxrs232", default_rs232_devices, nullptr));
	auxrs232.rxd_handler().set(m_usart[1], FUNC(i8251_device::write_rxd));
	auxrs232.cts_handler().set(m_usart[1], FUNC(i8251_device::write_cts));
	auxrs232.dsr_handler().set(m_usart[1], FUNC(i8251_device::write_dsr));

	I8255(config, m_ppi[0]);
	// port a: input (switches?)
	m_ppi[0]->in_pb_callback().set("keyboard", FUNC(basf7100_kbd_device::read));
	m_ppi[0]->out_pc_callback().set(FUNC(basf7100_state::keyboard_w));

	I8255(config, m_ppi[1]);
	m_ppi[1]->out_pa_callback().set("centronics_latch", FUNC(output_latch_device::write));
	// port b: output (leds?)
	m_ppi[1]->out_pc_callback().set(FUNC(basf7100_state::centronics_ctrl_w));

	I8255(config, m_ppi[2]);
	// port a: input (rs232 flags, auto dial)
	// port b: output (auto dialer digits)
	// port c: input (auto dialer status), output (rs232 and auto dialer control)

	I8255(config, m_ppi[3]);
	m_ppi[3]->out_pa_callback().set(FUNC(basf7100_state::cursor_col_w));
	m_ppi[3]->out_pb_callback().set(FUNC(basf7100_state::cursor_row_w));
	m_ppi[3]->out_pc_callback().set(FUNC(basf7100_state::highlight_w));

	I8255(config, m_ppi[4]);
	m_ppi[4]->out_pa_callback().set(FUNC(basf7100_state::roll_offset_w));
	m_ppi[4]->out_pb_callback().set(FUNC(basf7100_state::sod_high_w));
	m_ppi[4]->out_pc_callback().set(FUNC(basf7100_state::sod_low_w));

	// video
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_color(rgb_t::green());
	m_screen->set_size(640, 288); // wrong because of wrong charrom
	m_screen->set_visarea_full();
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // not accurate
	m_screen->set_screen_update(FUNC(basf7100_state::screen_update));
	m_screen->screen_vblank().set(FUNC(basf7100_state::vblank_w));

	GFXDECODE(config, "gfxdecode", "palette", gfx_crt8002);

	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	// centronics
	output_latch_device &centronics_latch(OUTPUT_LATCH(config, "centronics_latch"));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->set_output_latch(centronics_latch);
	m_centronics->ack_handler().set(m_ppi[1], FUNC(i8255_device::pc6_w));
	m_centronics->busy_handler().set(FUNC(basf7100_state::centronics_busy_w));

	// floppy
	FD1791(config, m_fdc, 1000000);
	m_fdc->intrq_wr_callback().set_inputline(m_fdccpu, INPUT_LINE_IRQ0);
	m_fdc->drq_wr_callback().set(FUNC(basf7100_state::fdc_drq_w));
	FLOPPY_CONNECTOR(config, "fdc:0", basf7100_floppies, "basf6106", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", basf7100_floppies, "basf6106", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:2", basf7100_floppies, "basf6106", floppy_image_device::default_mfm_floppy_formats);

	SOFTWARE_LIST(config, "floppy_list").set_original("basf7100");

	// keyboard
	basf7100_kbd_device &keyboard(BASF7100_KBD(config, "keyboard"));
	keyboard.int_handler().set(m_ppi[0], FUNC(i8255_device::pc2_w));
}


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

ROM_START( basf7120 )
	ROM_REGION(0x20, "maincpu", 0)
	ROM_LOAD("19-2113-2.u21", 0x00, 0x20, CRC(4405e26f) SHA1(0f93c47e9f546b42a85e5eced58337e0add443c4)) // IM5610CJE

	ROM_REGION(0x400, "fdccpu", 0)
	ROM_LOAD("19-2130-2h.u45", 0x000, 0x400, CRC(cb077c69) SHA1(dfa16082b88275442c48082aeb5f62fe1238ae3e)) // 2708

	ROM_REGION(0x50, "floppy_pal", 0)
	ROM_LOAD("19-2131-1.u23", 0x00, 0x28, CRC(f37ed4bc) SHA1(824b4405f396c262cf8116f85eb0b548eabb4c04)) // PAL10L8MJ
	ROM_LOAD("19-2132-1.u24", 0x28, 0x28, CRC(b918ff18) SHA1(c6d7cd9642ed32e56b5c1df1ddf3afe09d744ebc)) // PAL10L8MJ

	ROM_REGION(0x100, "prom", 0)
	ROM_LOAD("video.30", 0x000, 0x100, CRC(89175ac9) SHA1(69b2055bee87e11cc74c70cef2f2bebcbd0004c9)) // N82S129N (label missing)

	ROM_REGION(0x800, "chargen", 0)
	ROM_LOAD("160a002-b.bin", 0x000, 0x800, BAD_DUMP CRC(8787145c) SHA1(91d8c79f901a41117e99325a53b677f06baf1074)) // handcrafted, wrong
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY   FULLNAME  FLAGS
COMP( 1982, basf7120, 0,      0,      basf7100, basf7100, basf7100_state, empty_init, "BASF",   "7120",   MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



basic52.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

        MCS BASIC 52 and MCS BASIC 31 board

        03/12/2009 Skeleton driver.

        2012-08-08 Made to work [Robbbert]

BASIC-52 is an official Intel release.

BASIC-31 (and variants) as found on the below url, are homebrews.

https://web.archive.org/web/20110908004037/http://dsaprojects.110mb.com/electronics/8031-ah/8031-bas.html

Once the system starts, all input must be in uppercase. Read the manual
to discover the special features of this Basic.

Sound: BASIC-31 has sound, and BASIC-52 doesn't. The sound command is PWM.
       Example: PWM 800,200,900

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

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/i8255.h"
#include "bus/rs232/terminal.h"
#include "bus/rs232/rs232.h"
#include "sound/spkrdev.h"
#include "speaker.h"


namespace {

class basic52_state : public driver_device
{
public:
	basic52_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_serial(*this, "serial")
		, m_speaker(*this, "speaker")
	{ }

	void basic52(machine_config &config);
	void basic31(machine_config &config);

private:
	void machine_start() override ATTR_COLD;
	void port1_w(u8 data);
	uint8_t port3_r();
	void port3_w(u8 data);
	void rx_w(int state);
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	uint8_t m_port3 = 0U;
	required_device<mcs51_cpu_device> m_maincpu;
	required_device<rs232_port_device> m_serial;
	required_device<speaker_sound_device> m_speaker;
};


void basic52_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom();
}

void basic52_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).ram();
	// 8000-9FFF is reserved for a plug-in EPROM containing BASIC programs.
	map(0xa000, 0xa003).rw("ppi8255", FUNC(i8255_device::read), FUNC(i8255_device::write));  // PPI-8255
}

/* Input ports */
static INPUT_PORTS_START( basic52 )
INPUT_PORTS_END


uint8_t basic52_state::port3_r()
{
	return m_port3;
}

void basic52_state::port1_w(u8 data)
{
	m_speaker->level_w(BIT(data, 2));
}

void basic52_state::port3_w(u8 data)
{
	// preserve RXD
	m_port3 = (m_port3 & 1) | (data & ~1);

	m_serial->write_txd(BIT(data, 1));
}

void basic52_state::rx_w(int state)
{
	if (state)
		m_port3 |= 1;
	else
		m_port3 &= ~1;
}

void basic52_state::machine_start()
{
	save_item(NAME(m_port3));
}

static void serial_devices(device_slot_interface &device)
{
	device.option_add("terminal", SERIAL_TERMINAL);
}

void basic52_state::basic31(machine_config &config)
{
	/* basic machine hardware */
	I8031(config, m_maincpu, XTAL(11'059'200));
	m_maincpu->set_addrmap(AS_PROGRAM, &basic52_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &basic52_state::io_map);
	m_maincpu->port_out_cb<1>().set(FUNC(basic52_state::port1_w));
	m_maincpu->port_out_cb<3>().set(FUNC(basic52_state::port3_w));
	m_maincpu->port_in_cb<3>().set(FUNC(basic52_state::port3_r));

	RS232_PORT(config, m_serial, serial_devices, "terminal");
	m_serial->rxd_handler().set(FUNC(basic52_state::rx_w));

	I8255(config, "ppi8255", 0);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);
}

void basic52_state::basic52(machine_config &config)
{
	basic31(config);
	/* basic machine hardware */
	I8052(config.replace(), m_maincpu, XTAL(11'059'200));
	m_maincpu->set_addrmap(AS_PROGRAM, &basic52_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &basic52_state::io_map);
	m_maincpu->port_out_cb<3>().set(FUNC(basic52_state::port3_w));
	m_maincpu->port_in_cb<3>().set(FUNC(basic52_state::port3_r));
}

/* ROM definition */
ROM_START( basic52 )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_SYSTEM_BIOS(0, "v11", "v 1.1")
	ROMX_LOAD( "mcs-51-11.bin",  0x0000, 0x2000, CRC(4157b22b) SHA1(bd9e6869b400cc1c9b163243be7bdcf16ce72789), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v11b", "v 1.1b")
	ROMX_LOAD( "mcs-51-11b.bin", 0x0000, 0x2000, CRC(a60383cc) SHA1(9515cc435e2ca3d3adb19631c03a62dfbeab0826), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v131", "v 1.3.1")
	ROMX_LOAD( "mcs-51-131.bin", 0x0000, 0x2000, CRC(6a493162) SHA1(ed1079a6b4d4dbf448e15238c5a9e4dd004e401c), ROM_BIOS(2))
ROM_END

ROM_START( basic31 )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_SYSTEM_BIOS(0, "v12", "v 1.2")
	ROMX_LOAD( "mcs-51-12.bin",  0x0000, 0x2000, CRC(ee667c7c) SHA1(e69b32e69ecda2012c7113649634a3a64e984bed), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v12a", "v 1.2a")
	ROMX_LOAD( "mcs-51-12a.bin", 0x0000, 0x2000, CRC(225bb2f0) SHA1(46e97643a7a5cb4c278f9e3c73d18cd93209f8bf), ROM_BIOS(1))
ROM_END

} // anonymous namespace


/* Driver */
/*    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY  FULLNAME        FLAGS */
COMP( 1985, basic52, 0,       0,      basic52, basic52, basic52_state, empty_init, "Intel", "MCS BASIC 52", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1985, basic31, basic52, 0,      basic31, basic52, basic52_state, empty_init, "Intel", "MCS BASIC 31", MACHINE_SUPPORTS_SAVE )



basssta.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Novation BassStation synthesizers.

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

#include "emu.h"
#include "cpu/mn1880/mn1880.h"
#include "machine/eeprompar.h"


namespace {

class basssta_state : public driver_device
{
public:
	basssta_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_inputs(*this, "IN%u", 0U)
		, m_input_select(0xff)
	{
	}

	void bassstr(machine_config &config);
	void sbasssta(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void input_select_w(u8 data);
	u8 input_r();

	void bassstr_prog(address_map &map) ATTR_COLD;
	void sbasssta_prog(address_map &map) ATTR_COLD;
	void bassstr_data(address_map &map) ATTR_COLD;
	void sbasssta_data(address_map &map) ATTR_COLD;

	required_device<mn1880_device> m_maincpu;
	required_ioport_array<4> m_inputs;

	u8 m_input_select;
};


void basssta_state::machine_start()
{
	save_item(NAME(m_input_select));
}

void basssta_state::input_select_w(u8 data)
{
	m_input_select = data;
}

u8 basssta_state::input_r()
{
	u8 ret = 0xff;
	for (int n = 0; n < 4; n++)
		if (!BIT(m_input_select, n))
			ret &= m_inputs[n]->read();
	return ret;
}

void basssta_state::bassstr_prog(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("program", 0);
}

void basssta_state::sbasssta_prog(address_map &map)
{
	map(0x0000, 0xffff).rom().region("program", 0);
}

void basssta_state::bassstr_data(address_map &map)
{
	map(0x0001, 0x0001).noprw();
	map(0x0003, 0x0003).noprw();
	map(0x000f, 0x000f).noprw();
	map(0x001f, 0x001f).noprw();
	map(0x0060, 0x03cf).ram(); // TODO: this plus everything above is probably internal to CPU
	map(0x8000, 0x87ff).rw("eeprom", FUNC(eeprom_parallel_28xx_device::read), FUNC(eeprom_parallel_28xx_device::write));
	map(0x8800, 0x8800).r(FUNC(basssta_state::input_r));
	map(0xa800, 0xa800).w(FUNC(basssta_state::input_select_w));
}

void basssta_state::sbasssta_data(address_map &map)
{
	map(0x0000, 0x0003).nopr();
	map(0x0001, 0x0001).nopw();
	map(0x0003, 0x0003).nopw();
	map(0x000f, 0x000f).noprw();
	map(0x0014, 0x0014).noprw();
	map(0x001c, 0x001c).nopr();
	map(0x001e, 0x001e).nopw();
	map(0x001f, 0x001f).noprw();
	map(0x0030, 0x0030).nopw();
	map(0x0032, 0x0032).nopw();
	map(0x0034, 0x0034).nopw();
	map(0x0036, 0x0036).nopw();
	map(0x005d, 0x005d).noprw();
	map(0x0060, 0x07ff).ram(); // TODO: this plus everything above is probably internal to CPU
	map(0x6000, 0x7fff).rw("eeprom", FUNC(eeprom_parallel_28xx_device::read), FUNC(eeprom_parallel_28xx_device::write));
	map(0x8001, 0x8001).w(FUNC(basssta_state::input_select_w));
	map(0x8002, 0x8004).nopw();
	map(0xc000, 0xc000).r(FUNC(basssta_state::input_r));
}


static INPUT_PORTS_START(basssta)
	PORT_START("IN0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN)
	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("IN1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN)
	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_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN)
	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("IN3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN)
	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)
INPUT_PORTS_END

void basssta_state::bassstr(machine_config &config)
{
	MN1880(config, m_maincpu, 8000000); // type and clock unknown (custom silkscreen; might be MN18P83220)
	m_maincpu->set_addrmap(AS_PROGRAM, &basssta_state::bassstr_prog);
	m_maincpu->set_addrmap(AS_DATA, &basssta_state::bassstr_data);

	EEPROM_2816(config, "eeprom");
}

void basssta_state::sbasssta(machine_config &config)
{
	MN1880(config, m_maincpu, 8000000); // type and clock unknown (custom silkscreen)
	m_maincpu->set_addrmap(AS_PROGRAM, &basssta_state::sbasssta_prog);
	m_maincpu->set_addrmap(AS_DATA, &basssta_state::sbasssta_data);

	EEPROM_2864(config, "eeprom"); // Microchip 28C64AF-15I/L
}

ROM_START(bassstr)
	ROM_REGION(0x8000, "program", 0)
	ROM_LOAD("v1.5.bin", 0x0000, 0x8000, CRC(a8bc9721) SHA1(2c2dc3bee0fabf1e77d02cdf3e53885f2c5b913e)) // ST M27C256B (DIP28)
ROM_END

ROM_START(sbasssta)
	ROM_REGION(0x10000, "program", 0)
	ROM_LOAD("v1.9.bin", 0x00000, 0x10000, CRC(045bf9e3) SHA1(69155026c2497a4731162cadb6b441e00902d3f6))
ROM_END

} // anonymous namespace


SYST(1995, bassstr, 0, 0,  bassstr,  basssta, basssta_state, empty_init, "Novation", "BassStation Rack Analogue Synthesizer Module", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1997, sbasssta, 0, 0, sbasssta, basssta, basssta_state, empty_init, "Novation", "Super Bass Station", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



bbcb.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Gordon Jefferyes, Nigel Barnes
/******************************************************************************

    BBC Model A,B

    ANA01 - Model A
    ANA02 - Model A with Econet interface

    ANB01 - Model B
    ANB02 - Model B with Econet interface
    ANB03 - Model B with Disc interface
    ANB04 - Model B with Disc and Econet interfaces

    GNB14 - Model B with Disc, Econet & Speech (German export)
    UNB09 - Model B with Disc, Econet & Speech (US export)
            Model B with Disc (Norway dealer import)

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

#include "emu.h"
#include "bbc.h"
#include "acorn_serproc.h"
#include "bbc_kbd.h"

#include "bus/bbc/vsp/slot.h"
#include "sound/tms5220.h"

#include "formats/uef_cas.h"
#include "formats/csw_cas.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "bbc.lh"


namespace {

class bbcb_state : public bbc_state
{
public:
	bbcb_state(const machine_config &mconfig, device_type type, const char *tag)
		: bbc_state(mconfig, type, tag)
		, m_kbd(*this, "kbd")
		, m_sn(*this, "sn")
		, m_vsp(*this, "vsp")
		, m_adlc(*this, "mc6854")
		, m_statid(*this, "STATID")
	{ }

	void bbca(machine_config &config);
	void bbcb(machine_config &config);
	void bbcb_de(machine_config &config);
	void bbcb_no(machine_config &config);
	void bbcb_us(machine_config &config);
	void dolphinm(machine_config &config);
	void sist1(machine_config &config);
	void torchf(machine_config &config);
	void torchh(machine_config &config);
	void torch301(machine_config &config);
	void torch725(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<bbc_kbd_device> m_kbd;
	required_device<sn76489a_device> m_sn;
	optional_device<bbc_vsp_slot_device> m_vsp;
	optional_device<mc6854_device> m_adlc;
	optional_ioport m_statid;

	void bbca_mem(address_map &map) ATTR_COLD;
	void bbcb_mem(address_map &map) ATTR_COLD;
	void dolphin_mem(address_map &map) ATTR_COLD;

	void trigger_reset(int state);

	uint8_t ram_r(offs_t offset);
	void ram_w(offs_t offset, uint8_t data);
	uint8_t romsel_r(offs_t offset);
	void romsel_w(offs_t offset, uint8_t data);
	uint8_t paged_r(offs_t offset);
	void paged_w(offs_t offset, uint8_t data);

	uint8_t sysvia_pa_r();
	void sysvia_pa_w(uint8_t data);
	void update_sdb();
	uint8_t sysvia_pb_r();
	void sysvia_pb_w(uint8_t data);

	uint8_t m_sdb = 0x00;
};


void bbcb_state::machine_start()
{
	bbc_state::machine_start();

	save_item(NAME(m_sdb));
}

void bbcb_state::machine_reset()
{
	// install econet hardware
	if (m_bbcconfig.read_safe(0) & 0x04)
		m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0xfea0, 0xfea3, 0, 0x1c, 0, emu::rw_delegate(*m_adlc, FUNC(mc6854_device::read)), emu::rw_delegate(*m_adlc, FUNC(mc6854_device::write)));
	else
		m_maincpu->space(AS_PROGRAM).install_read_handler(0xfea0, 0xfea3, 0, 0x1c, 0, read8smo_delegate(*this, []() { return 0xfe; }, "fe_r"));
}


void bbcb_state::bbca_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).rw(m_ram, FUNC(ram_device::read), FUNC(ram_device::write));                                    //    0000-7FFF                 Regular RAM
	map(0x8000, 0xbfff).rw(FUNC(bbcb_state::paged_r), FUNC(bbcb_state::paged_w));                                      //    8000-BFFF                 Paged ROM/RAM
	map(0xc000, 0xffff).rom().region("mos", 0);                                                                        //    C000-FBFF                 OS ROM
	map(0xfe00, 0xfeff).lr8(NAME([]() { return 0xfe; })).nopw();                                                       //    FE00-FEFF                 SHEILA Address Page
	map(0xfe00, 0xfe00).mirror(0x06).rw(m_crtc, FUNC(hd6845s_device::status_r), FUNC(hd6845s_device::address_w));      //    FE00-FE07  6845 CRTC      Video controller
	map(0xfe01, 0xfe01).mirror(0x06).rw(m_crtc, FUNC(hd6845s_device::register_r), FUNC(hd6845s_device::register_w));
	map(0xfe08, 0xfe09).mirror(0x06).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));            //    FE08-FE0F  6850 ACIA      Serial controller
	map(0xfe10, 0xfe17).rw("serproc", FUNC(acorn_serproc_device::read), FUNC(acorn_serproc_device::write));            //    FE10-FE17  Serial ULA     Serial system chip
	map(0xfe20, 0xfe2f).w(FUNC(bbcb_state::video_ula_w));                                                              // W: FE20-FE2F  Video ULA      Video system chip
	map(0xfe30, 0xfe3f).w(FUNC(bbcb_state::romsel_w));                                                                 // W: FE30-FE3F  74LS161        Paged ROM selector
	map(0xfe40, 0xfe4f).mirror(0x10).m(m_sysvia, FUNC(via6522_device::map));                                           //    FE40-FE5F  6522 VIA       SYSTEM VIA
}


void bbcb_state::bbcb_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).rw(FUNC(bbcb_state::ram_r), FUNC(bbcb_state::ram_w));                                          //    0000-7FFF                 Regular RAM
	map(0x8000, 0xbfff).rw(FUNC(bbcb_state::paged_r), FUNC(bbcb_state::paged_w));                                      //    8000-BFFF                 Paged ROM/RAM
	map(0xc000, 0xffff).rw(FUNC(bbcb_state::mos_r), FUNC(bbcb_state::mos_w));                                          //    C000-FBFF                 OS ROM
	map(0xfc00, 0xfcff).rw(m_1mhzbus, FUNC(bbc_1mhzbus_slot_device::fred_r), FUNC(bbc_1mhzbus_slot_device::fred_w));   //    FC00-FCFF                 FRED Address Page
	map(0xfd00, 0xfdff).rw(m_1mhzbus, FUNC(bbc_1mhzbus_slot_device::jim_r), FUNC(bbc_1mhzbus_slot_device::jim_w));     //    FD00-FDFF                 JIM Address Page
	map(0xfe00, 0xfeff).lr8(NAME([]() { return 0xfe; })).nopw();                                                       //    FE00-FEFF                 SHEILA Address Page
	map(0xfe00, 0xfe00).mirror(0x06).rw(m_crtc, FUNC(hd6845s_device::status_r), FUNC(hd6845s_device::address_w));      //    FE00-FE07  6845 CRTC      Video controller
	map(0xfe01, 0xfe01).mirror(0x06).rw(m_crtc, FUNC(hd6845s_device::register_r), FUNC(hd6845s_device::register_w));
	map(0xfe08, 0xfe09).mirror(0x06).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));            //    FE08-FE0F  6850 ACIA      Serial controller
	map(0xfe10, 0xfe17).rw("serproc", FUNC(acorn_serproc_device::read), FUNC(acorn_serproc_device::write));            //    FE10-FE17  Serial ULA     Serial system chip
	map(0xfe18, 0xfe1f).lr8(NAME([this]() { econet_int_enable(0); return m_statid->read(); }));                        // R: FE18-FE1F  INTOFF/STATID  ECONET Interrupt Off / ID No.
	map(0xfe18, 0xfe1f).lw8(NAME([this](uint8_t data) { econet_int_enable(0); }));                                     // W: FE18-FE1F  INTOFF         ECONET Interrupt Off
	map(0xfe20, 0xfe2f).lr8(NAME([this]() { econet_int_enable(1); return 0xfe; }));                                    // R: FE20-FE2F  INTON          ECONET Interrupt On
	map(0xfe20, 0xfe2f).w(FUNC(bbcb_state::video_ula_w));                                                              // W: FE20-FE2F  Video ULA      Video system chip
	map(0xfe30, 0xfe3f).rw(FUNC(bbcb_state::romsel_r), FUNC(bbcb_state::romsel_w));                                    // W: FE30-FE3F  84LS161        Paged ROM selector
	map(0xfe40, 0xfe4f).mirror(0x10).m(m_sysvia, FUNC(via6522_device::map));                                           //    FE40-FE5F  6522 VIA       SYSTEM VIA
	map(0xfe60, 0xfe6f).mirror(0x10).m(m_uservia, FUNC(via6522_device::map));                                          //    FE60-FE7F  6522 VIA       USER VIA
	map(0xfe80, 0xfe9f).rw(m_fdc, FUNC(bbc_fdc_slot_device::read), FUNC(bbc_fdc_slot_device::write));                  //    FE80-FE9F  8271 FDC       Floppy disc controller
	map(0xfea0, 0xfea3).mirror(0x1c).rw(m_adlc, FUNC(mc6854_device::read), FUNC(mc6854_device::write));                //    FEA0-FEBF  68B54 ADLC     ECONET controller
	map(0xfec0, 0xfec3).mirror(0x1c).rw("upd7002", FUNC(upd7002_device::read), FUNC(upd7002_device::write));           //    FEC0-FEDF  uPD7002        Analogue to digital converter
	map(0xfee0, 0xfeff).rw(m_tube, FUNC(bbc_tube_slot_device::host_r), FUNC(bbc_tube_slot_device::host_w));            //    FEE0-FEFF  Tube ULA       Tube system interface
}


void bbcb_state::dolphin_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).rw(m_ram, FUNC(ram_device::read), FUNC(ram_device::write));                                    //    0000-7FFF                 Regular RAM
	map(0x8000, 0xbfff).rw(FUNC(bbcb_state::paged_r), FUNC(bbcb_state::paged_w));                                      //    8000-BFFF                 Paged ROM/RAM
	map(0xc000, 0xffff).rom().region("mos", 0);                                                                        //    C000-FBFF                 OS ROM
	//map(0xfc00, 0xfcff).rw(m_1mhzbus, FUNC(bbc_1mhzbus_slot_device::fred_r), FUNC(bbc_1mhzbus_slot_device::fred_w));   //    FC00-FCFF                 FRED Address Page
	//map(0xfd00, 0xfdff).rw(m_1mhzbus, FUNC(bbc_1mhzbus_slot_device::jim_r), FUNC(bbc_1mhzbus_slot_device::jim_w));     //    FD00-FDFF                 JIM Address Page
	map(0xfe00, 0xfeff).lr8(NAME([]() { return 0xfe; })).nopw();                                                       //    FE00-FEFF                 SHEILA Address Page
	map(0xfe00, 0xfe00).mirror(0x06).rw(m_crtc, FUNC(hd6845s_device::status_r), FUNC(hd6845s_device::address_w));      //    FE00-FE07  6845 CRTC      Video controller
	map(0xfe01, 0xfe01).mirror(0x06).rw(m_crtc, FUNC(hd6845s_device::register_r), FUNC(hd6845s_device::register_w));
	map(0xfe08, 0xfe09).mirror(0x06).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));            //    FE08-FE0F  6850 ACIA      Serial controller
	map(0xfe10, 0xfe17).rw("serproc", FUNC(acorn_serproc_device::read), FUNC(acorn_serproc_device::write));            //    FE10-FE17  Serial ULA     Serial system chip
	map(0xfe20, 0xfe2f).w(FUNC(bbcb_state::video_ula_w));                                                              // W: FE20-FE2F  Video ULA      Video system chip
	map(0xfe30, 0xfe3f).lw8(NAME([this](uint8_t data) { m_romsel = data & 0x0f; }));                                   // W: FE30-FE3F  84LS161        Paged ROM selector
	map(0xfe40, 0xfe4f).mirror(0x10).m(m_sysvia, FUNC(via6522_device::map));                                           //    FE40-FE5F  6522 VIA       SYSTEM VIA
	map(0xfe60, 0xfe6f).mirror(0x10).m(m_uservia, FUNC(via6522_device::map));                                          //    FE60-FE7F  6522 VIA       USER VIA
	map(0xfe80, 0xfe9f).rw(m_fdc, FUNC(bbc_fdc_slot_device::read), FUNC(bbc_fdc_slot_device::write));                  //    FE80-FE9F  8271 FDC       Floppy disc controller
	//map(0xfec0, 0xfec3).mirror(0x1c).rw("upd7002", FUNC(upd7002_device::read), FUNC(upd7002_device::write));           //    FEC0-FEDF  uPD7002        Analogue to digital converter
}


uint8_t bbcb_state::ram_r(offs_t offset)
{
	if (m_internal && m_internal->overrides_ram())
		return m_internal->ram_r(offset);
	else
		return m_ram->pointer()[offset & m_ram->mask()];
}

void bbcb_state::ram_w(offs_t offset, uint8_t data)
{
	if (m_internal && m_internal->overrides_ram())
		m_internal->ram_w(offset, data);
	else
		m_ram->pointer()[offset & m_ram->mask()] = data;
}

uint8_t bbcb_state::romsel_r(offs_t offset)
{
	if (m_internal && m_internal->overrides_rom())
		return m_internal->romsel_r(offset);
	else
		return 0xfe;
}

void bbcb_state::romsel_w(offs_t offset, uint8_t data)
{
	// no sideways expansion board fitted so address only the 4 on board ROM sockets
	m_romsel = data & 0x03;

	// pass ROMSEL to internal expansion board
	if (m_internal && m_internal->overrides_rom())
		m_internal->romsel_w(offset, data);
}

uint8_t bbcb_state::paged_r(offs_t offset)
{
	uint8_t data;

	if (m_internal && m_internal->overrides_rom())
	{
		data = m_internal->paged_r(offset);
	}
	else
	{
		if (m_rom[m_romsel] && m_rom[m_romsel]->present())
		{
			data = m_rom[m_romsel]->read(offset);
		}
		else
		{
			data = m_region_rom->base()[offset + (m_romsel << 14)];
		}
	}

	return data;
}

void bbcb_state::paged_w(offs_t offset, uint8_t data)
{
	if (m_internal && m_internal->overrides_rom())
	{
		m_internal->paged_w(offset, data);
	}
	else
	{
		if (m_rom[m_romsel])
		{
			m_rom[m_romsel]->write(offset, data);
		}
	}
}


uint8_t bbcb_state::sysvia_pa_r()
{
	update_sdb();

	return m_sdb;
}

void bbcb_state::sysvia_pa_w(uint8_t data)
{
	m_sdb = data;

	update_sdb();
}


void bbcb_state::update_sdb()
{
	uint8_t const latch = m_latch->output_state();

	// sound
	if (!BIT(latch, 0))
		m_sn->write(m_sdb);

	// speech
	if (m_vsp)
	{
		m_vsp->combined_rsq_wsq_w(bitswap<2>(latch, 1, 2));
		switch (bitswap<2>(~latch, 1, 2))
		{
		case tms5200_device::RS:
			m_sdb = m_vsp->read();
			break;
		case tms5200_device::WS:
			m_vsp->write(m_sdb);
			break;
		}
	}

	// keyboard
	m_sdb = m_kbd->read(m_sdb);
}


uint8_t bbcb_state::sysvia_pb_r()
{
	uint8_t data = 0xff;

	if (m_analog)
	{
		data &= ~0x30;
		data |= m_analog->pb_r();
	}

	if (m_vsp)
	{
		data &= ~0xc0;
		data |= m_vsp->intq_r() << 6;
		data |= m_vsp->readyq_r() << 7;
	}

	return data;
}

void bbcb_state::sysvia_pb_w(uint8_t data)
{
	m_latch->write_nibble_d3(data);

	if (m_analog)
	{
		m_analog->pb_w(data & 0x30);
	}

	update_sdb();
}


void bbcb_state::trigger_reset(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? CLEAR_LINE : ASSERT_LINE);

	if (!state)
	{
		if (m_uservia) m_uservia->reset();
		if (m_adlc) m_adlc->reset();
		if (m_fdc) m_fdc->reset();
		if (m_1mhzbus) m_1mhzbus->reset();
		if (m_tube) m_tube->reset();
		if (m_internal) m_internal->reset();
	}
}


static INPUT_PORTS_START(bbc_statid)
	PORT_START("STATID")
	PORT_DIPNAME(0xff, 0xfe, "Econet ID") PORT_DIPLOCATION("S11:1,2,3,4,5,6,7,8")
	PORT_DIPSETTING(   0x00,   "0" )    PORT_DIPSETTING(   0x01,   "1" )    PORT_DIPSETTING(   0x02,   "2" )    PORT_DIPSETTING(   0x03,   "3" )    PORT_DIPSETTING(   0x04,   "4" )
	PORT_DIPSETTING(   0x05,   "5" )    PORT_DIPSETTING(   0x06,   "6" )    PORT_DIPSETTING(   0x07,   "7" )    PORT_DIPSETTING(   0x08,   "8" )    PORT_DIPSETTING(   0x09,   "9" )
	PORT_DIPSETTING(   0x0a,  "10" )    PORT_DIPSETTING(   0x0b,  "11" )    PORT_DIPSETTING(   0x0c,  "12" )    PORT_DIPSETTING(   0x0d,  "13" )    PORT_DIPSETTING(   0x0e,  "14" )
	PORT_DIPSETTING(   0x0f,  "15" )    PORT_DIPSETTING(   0x10,  "16" )    PORT_DIPSETTING(   0x11,  "17" )    PORT_DIPSETTING(   0x12,  "18" )    PORT_DIPSETTING(   0x13,  "19" )
	PORT_DIPSETTING(   0x14,  "20" )    PORT_DIPSETTING(   0x15,  "21" )    PORT_DIPSETTING(   0x16,  "22" )    PORT_DIPSETTING(   0x17,  "23" )    PORT_DIPSETTING(   0x18,  "24" )
	PORT_DIPSETTING(   0x19,  "25" )    PORT_DIPSETTING(   0x1a,  "26" )    PORT_DIPSETTING(   0x1b,  "27" )    PORT_DIPSETTING(   0x1c,  "28" )    PORT_DIPSETTING(   0x1d,  "29" )
	PORT_DIPSETTING(   0x1e,  "30" )    PORT_DIPSETTING(   0x1f,  "31" )    PORT_DIPSETTING(   0x20,  "32" )    PORT_DIPSETTING(   0x21,  "33" )    PORT_DIPSETTING(   0x22,  "34" )
	PORT_DIPSETTING(   0x23,  "35" )    PORT_DIPSETTING(   0x24,  "36" )    PORT_DIPSETTING(   0x25,  "37" )    PORT_DIPSETTING(   0x26,  "38" )    PORT_DIPSETTING(   0x27,  "39" )
	PORT_DIPSETTING(   0x28,  "40" )    PORT_DIPSETTING(   0x29,  "41" )    PORT_DIPSETTING(   0x2a,  "42" )    PORT_DIPSETTING(   0x2b,  "43" )    PORT_DIPSETTING(   0x2c,  "44" )
	PORT_DIPSETTING(   0x2d,  "45" )    PORT_DIPSETTING(   0x2e,  "46" )    PORT_DIPSETTING(   0x2f,  "47" )    PORT_DIPSETTING(   0x30,  "48" )    PORT_DIPSETTING(   0x31,  "49" )
	PORT_DIPSETTING(   0x32,  "50" )    PORT_DIPSETTING(   0x33,  "51" )    PORT_DIPSETTING(   0x34,  "52" )    PORT_DIPSETTING(   0x35,  "53" )    PORT_DIPSETTING(   0x36,  "54" )
	PORT_DIPSETTING(   0x37,  "55" )    PORT_DIPSETTING(   0x38,  "56" )    PORT_DIPSETTING(   0x39,  "57" )    PORT_DIPSETTING(   0x3a,  "58" )    PORT_DIPSETTING(   0x3b,  "59" )
	PORT_DIPSETTING(   0x3c,  "60" )    PORT_DIPSETTING(   0x3d,  "61" )    PORT_DIPSETTING(   0x3e,  "62" )    PORT_DIPSETTING(   0x3f,  "63" )    PORT_DIPSETTING(   0x40,  "64" )
	PORT_DIPSETTING(   0x41,  "65" )    PORT_DIPSETTING(   0x42,  "66" )    PORT_DIPSETTING(   0x43,  "67" )    PORT_DIPSETTING(   0x44,  "68" )    PORT_DIPSETTING(   0x45,  "69" )
	PORT_DIPSETTING(   0x46,  "70" )    PORT_DIPSETTING(   0x47,  "71" )    PORT_DIPSETTING(   0x48,  "72" )    PORT_DIPSETTING(   0x49,  "73" )    PORT_DIPSETTING(   0x4a,  "74" )
	PORT_DIPSETTING(   0x4b,  "75" )    PORT_DIPSETTING(   0x4c,  "76" )    PORT_DIPSETTING(   0x4d,  "77" )    PORT_DIPSETTING(   0x4e,  "78" )    PORT_DIPSETTING(   0x4f,  "79" )
	PORT_DIPSETTING(   0x50,  "80" )    PORT_DIPSETTING(   0x51,  "81" )    PORT_DIPSETTING(   0x52,  "82" )    PORT_DIPSETTING(   0x53,  "83" )    PORT_DIPSETTING(   0x54,  "84" )
	PORT_DIPSETTING(   0x55,  "85" )    PORT_DIPSETTING(   0x56,  "86" )    PORT_DIPSETTING(   0x57,  "87" )    PORT_DIPSETTING(   0x58,  "88" )    PORT_DIPSETTING(   0x59,  "89" )
	PORT_DIPSETTING(   0x5a,  "90" )    PORT_DIPSETTING(   0x5b,  "91" )    PORT_DIPSETTING(   0x5c,  "92" )    PORT_DIPSETTING(   0x5d,  "93" )    PORT_DIPSETTING(   0x5e,  "94" )
	PORT_DIPSETTING(   0x5f,  "95" )    PORT_DIPSETTING(   0x60,  "96" )    PORT_DIPSETTING(   0x61,  "97" )    PORT_DIPSETTING(   0x62,  "98" )    PORT_DIPSETTING(   0x63,  "99" )
	PORT_DIPSETTING(   0x64, "100" )    PORT_DIPSETTING(   0x65, "101" )    PORT_DIPSETTING(   0x66, "102" )    PORT_DIPSETTING(   0x67, "103" )    PORT_DIPSETTING(   0x68, "104" )
	PORT_DIPSETTING(   0x69, "105" )    PORT_DIPSETTING(   0x6a, "106" )    PORT_DIPSETTING(   0x6b, "107" )    PORT_DIPSETTING(   0x6c, "108" )    PORT_DIPSETTING(   0x6d, "109" )
	PORT_DIPSETTING(   0x6e, "110" )    PORT_DIPSETTING(   0x6f, "111" )    PORT_DIPSETTING(   0x70, "112" )    PORT_DIPSETTING(   0x71, "113" )    PORT_DIPSETTING(   0x72, "114" )
	PORT_DIPSETTING(   0x73, "115" )    PORT_DIPSETTING(   0x74, "116" )    PORT_DIPSETTING(   0x75, "117" )    PORT_DIPSETTING(   0x76, "118" )    PORT_DIPSETTING(   0x77, "119" )
	PORT_DIPSETTING(   0x78, "120" )    PORT_DIPSETTING(   0x79, "121" )    PORT_DIPSETTING(   0x7a, "122" )    PORT_DIPSETTING(   0x7b, "123" )    PORT_DIPSETTING(   0x7c, "124" )
	PORT_DIPSETTING(   0x7d, "125" )    PORT_DIPSETTING(   0x7e, "126" )    PORT_DIPSETTING(   0x7f, "127" )    PORT_DIPSETTING(   0x80, "128" )    PORT_DIPSETTING(   0x81, "129" )
	PORT_DIPSETTING(   0x82, "130" )    PORT_DIPSETTING(   0x83, "131" )    PORT_DIPSETTING(   0x84, "132" )    PORT_DIPSETTING(   0x85, "133" )    PORT_DIPSETTING(   0x86, "134" )
	PORT_DIPSETTING(   0x87, "135" )    PORT_DIPSETTING(   0x88, "136" )    PORT_DIPSETTING(   0x89, "137" )    PORT_DIPSETTING(   0x8a, "138" )    PORT_DIPSETTING(   0x8b, "139" )
	PORT_DIPSETTING(   0x8c, "140" )    PORT_DIPSETTING(   0x8d, "141" )    PORT_DIPSETTING(   0x8e, "142" )    PORT_DIPSETTING(   0x8f, "143" )    PORT_DIPSETTING(   0x90, "144" )
	PORT_DIPSETTING(   0x91, "145" )    PORT_DIPSETTING(   0x92, "146" )    PORT_DIPSETTING(   0x93, "147" )    PORT_DIPSETTING(   0x94, "148" )    PORT_DIPSETTING(   0x95, "149" )
	PORT_DIPSETTING(   0x96, "150" )    PORT_DIPSETTING(   0x97, "151" )    PORT_DIPSETTING(   0x98, "152" )    PORT_DIPSETTING(   0x99, "153" )    PORT_DIPSETTING(   0x9a, "154" )
	PORT_DIPSETTING(   0x9b, "155" )    PORT_DIPSETTING(   0x9c, "156" )    PORT_DIPSETTING(   0x9d, "157" )    PORT_DIPSETTING(   0x9e, "158" )    PORT_DIPSETTING(   0x9f, "159" )
	PORT_DIPSETTING(   0xa0, "160" )    PORT_DIPSETTING(   0xa1, "161" )    PORT_DIPSETTING(   0xa2, "162" )    PORT_DIPSETTING(   0xa3, "163" )    PORT_DIPSETTING(   0xa4, "164" )
	PORT_DIPSETTING(   0xa5, "165" )    PORT_DIPSETTING(   0xa6, "166" )    PORT_DIPSETTING(   0xa7, "167" )    PORT_DIPSETTING(   0xa8, "168" )    PORT_DIPSETTING(   0xa9, "169" )
	PORT_DIPSETTING(   0xaa, "170" )    PORT_DIPSETTING(   0xab, "171" )    PORT_DIPSETTING(   0xac, "172" )    PORT_DIPSETTING(   0xad, "173" )    PORT_DIPSETTING(   0xae, "174" )
	PORT_DIPSETTING(   0xaf, "175" )    PORT_DIPSETTING(   0xb0, "176" )    PORT_DIPSETTING(   0xb1, "177" )    PORT_DIPSETTING(   0xb2, "178" )    PORT_DIPSETTING(   0xb3, "179" )
	PORT_DIPSETTING(   0xb4, "180" )    PORT_DIPSETTING(   0xb5, "181" )    PORT_DIPSETTING(   0xb6, "182" )    PORT_DIPSETTING(   0xb7, "183" )    PORT_DIPSETTING(   0xb8, "184" )
	PORT_DIPSETTING(   0xb9, "185" )    PORT_DIPSETTING(   0xba, "186" )    PORT_DIPSETTING(   0xbb, "187" )    PORT_DIPSETTING(   0xbc, "188" )    PORT_DIPSETTING(   0xbd, "189" )
	PORT_DIPSETTING(   0xbe, "190" )    PORT_DIPSETTING(   0xbf, "191" )    PORT_DIPSETTING(   0xc0, "192" )    PORT_DIPSETTING(   0xc1, "193" )    PORT_DIPSETTING(   0xc2, "194" )
	PORT_DIPSETTING(   0xc3, "195" )    PORT_DIPSETTING(   0xc4, "196" )    PORT_DIPSETTING(   0xc5, "197" )    PORT_DIPSETTING(   0xc6, "198" )    PORT_DIPSETTING(   0xc7, "199" )
	PORT_DIPSETTING(   0xc8, "200" )    PORT_DIPSETTING(   0xc9, "201" )    PORT_DIPSETTING(   0xca, "202" )    PORT_DIPSETTING(   0xcb, "203" )    PORT_DIPSETTING(   0xcc, "204" )
	PORT_DIPSETTING(   0xcd, "205" )    PORT_DIPSETTING(   0xce, "206" )    PORT_DIPSETTING(   0xcf, "207" )    PORT_DIPSETTING(   0xd0, "208" )    PORT_DIPSETTING(   0xd1, "209" )
	PORT_DIPSETTING(   0xd2, "210" )    PORT_DIPSETTING(   0xd3, "211" )    PORT_DIPSETTING(   0xd4, "212" )    PORT_DIPSETTING(   0xd5, "213" )    PORT_DIPSETTING(   0xd6, "214" )
	PORT_DIPSETTING(   0xd7, "215" )    PORT_DIPSETTING(   0xd8, "216" )    PORT_DIPSETTING(   0xd9, "217" )    PORT_DIPSETTING(   0xda, "218" )    PORT_DIPSETTING(   0xdb, "219" )
	PORT_DIPSETTING(   0xdc, "220" )    PORT_DIPSETTING(   0xdd, "221" )    PORT_DIPSETTING(   0xde, "222" )    PORT_DIPSETTING(   0xdf, "223" )    PORT_DIPSETTING(   0xe0, "224" )
	PORT_DIPSETTING(   0xe1, "225" )    PORT_DIPSETTING(   0xe2, "226" )    PORT_DIPSETTING(   0xe3, "227" )    PORT_DIPSETTING(   0xe4, "228" )    PORT_DIPSETTING(   0xe5, "229" )
	PORT_DIPSETTING(   0xe6, "230" )    PORT_DIPSETTING(   0xe7, "231" )    PORT_DIPSETTING(   0xe8, "232" )    PORT_DIPSETTING(   0xe9, "233" )    PORT_DIPSETTING(   0xea, "234" )
	PORT_DIPSETTING(   0xeb, "235" )    PORT_DIPSETTING(   0xec, "236" )    PORT_DIPSETTING(   0xed, "237" )    PORT_DIPSETTING(   0xee, "238" )    PORT_DIPSETTING(   0xef, "239" )
	PORT_DIPSETTING(   0xf0, "240" )    PORT_DIPSETTING(   0xf1, "241" )    PORT_DIPSETTING(   0xf2, "242" )    PORT_DIPSETTING(   0xf3, "243" )    PORT_DIPSETTING(   0xf4, "244" )
	PORT_DIPSETTING(   0xf5, "245" )    PORT_DIPSETTING(   0xf6, "246" )    PORT_DIPSETTING(   0xf7, "247" )    PORT_DIPSETTING(   0xf8, "248" )    PORT_DIPSETTING(   0xf9, "249" )
	PORT_DIPSETTING(   0xfa, "250" )    PORT_DIPSETTING(   0xfb, "251" )    PORT_DIPSETTING(   0xfc, "252" )    PORT_DIPSETTING(   0xfd, "253" )    PORT_DIPSETTING(   0xfe, "254" )
	PORT_DIPSETTING(   0xff, "255" )
INPUT_PORTS_END


static INPUT_PORTS_START(bbca)
	PORT_INCLUDE(bbc_config)
INPUT_PORTS_END

static INPUT_PORTS_START(bbcb)
	PORT_INCLUDE(bbc_config)
	PORT_INCLUDE(bbc_statid)
INPUT_PORTS_END

static INPUT_PORTS_START(torch)
	PORT_INCLUDE(bbc_statid)
INPUT_PORTS_END


static const char *const bbc_sample_names[] =
{
	"*bbc",
	"motoroff",
	"motoron",
	nullptr
};


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

    BBC Micro

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

void bbcb_state::bbca(machine_config &config)
{
	M6502(config, m_maincpu, 16_MHz_XTAL / 8);
	m_maincpu->set_addrmap(AS_PROGRAM, &bbcb_state::bbca_mem);
	config.set_perfect_quantum(m_maincpu);

	INPUT_MERGER_ANY_HIGH(config, m_irqs).output_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	RAM(config, m_ram).set_default_size("16K").set_extra_options("32K");

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(16_MHz_XTAL, 1024, 0, 640, 312, 0, 256);
	m_screen->set_screen_update("crtc", FUNC(hd6845s_device::screen_update));

	PALETTE(config, m_palette).set_entries(16);

	SAA5050(config, m_trom, 12_MHz_XTAL / 2);

	HD6845S(config, m_crtc, 16_MHz_XTAL / 8);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_reconfigure_callback(FUNC(bbcb_state::crtc_reconfigure));
	m_crtc->set_update_row_callback(FUNC(bbcb_state::crtc_update_row));
	m_crtc->out_de_callback().set(FUNC(bbcb_state::de_changed));
	m_crtc->out_hsync_callback().set(FUNC(bbcb_state::hsync_changed));
	m_crtc->out_vsync_callback().set(FUNC(bbcb_state::vsync_changed));

	config.set_default_layout(layout_bbc);

	LS259(config, m_latch);
	m_latch->q_out_cb<3>().set(m_kbd, FUNC(bbc_kbd_device::write_kb_en));
	m_latch->q_out_cb<6>().set_output("capslock_led");
	m_latch->q_out_cb<7>().set_output("shiftlock_led");

	MOS6522(config, m_sysvia, 16_MHz_XTAL / 16);
	m_sysvia->readpa_handler().set(FUNC(bbcb_state::sysvia_pa_r));
	m_sysvia->writepa_handler().set(FUNC(bbcb_state::sysvia_pa_w));
	m_sysvia->readpb_handler().set(FUNC(bbcb_state::sysvia_pb_r));
	m_sysvia->writepb_handler().set(FUNC(bbcb_state::sysvia_pb_w));
	m_sysvia->cb2_handler().set([this](int state) { if (state) m_crtc->assert_light_pen_input(); });
	m_sysvia->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<0>));

	BBC_KBD(config, m_kbd, 16_MHz_XTAL / 16);
	m_kbd->ca2_handler().set(m_sysvia, FUNC(via6522_device::write_ca2));
	m_kbd->rst_handler().set(FUNC(bbcb_state::trigger_reset));

	SPEAKER(config, "mono").front_center();

	SN76489A(config, m_sn, 16_MHz_XTAL / 4);
	m_sn->add_route(ALL_OUTPUTS, "mono", 1.0);

	SAMPLES(config, m_samples);
	m_samples->set_channels(1);
	m_samples->set_samples_names(bbc_sample_names);
	m_samples->add_route(ALL_OUTPUTS, "mono", 1.0);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(bbc_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED);
	m_cassette->set_interface("bbc_cass");

	acia6850_device &acia(ACIA6850(config, "acia"));
	acia.txd_handler().set("serproc", FUNC(acorn_serproc_device::write_txd));
	acia.rts_handler().set("serproc", FUNC(acorn_serproc_device::write_rtsi));
	acia.irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<2>));

	acorn_serproc_device &serproc(ACORN_SERPROC(config, "serproc", 16_MHz_XTAL / 13));
	serproc.rxc_handler().set("acia", FUNC(acia6850_device::write_rxc));
	serproc.txc_handler().set("acia", FUNC(acia6850_device::write_txc));
	serproc.rxd_handler().set("acia", FUNC(acia6850_device::write_rxd));
	serproc.dcd_handler().set("acia", FUNC(acia6850_device::write_dcd));
	serproc.ctso_handler().set("acia", FUNC(acia6850_device::write_cts));
	serproc.dout_handler().set("rs423", FUNC(rs232_port_device::write_txd));
	serproc.rtso_handler().set("rs423", FUNC(rs232_port_device::write_rts));
	serproc.casmo_handler().set(FUNC(bbcb_state::cassette_motor));
	serproc.casin_handler("cassette", FUNC(cassette_image_device::input));
	serproc.casout_handler("cassette", FUNC(cassette_image_device::output));

	rs232_port_device &rs423(RS232_PORT(config, "rs423", default_rs232_devices, nullptr));
	rs423.rxd_handler().set("serproc", FUNC(acorn_serproc_device::write_din));
	rs423.cts_handler().set("serproc", FUNC(acorn_serproc_device::write_ctsi));

	BBC_ROMSLOT16(config, m_rom[0], bbc_rom_devices, nullptr); // IC101
	BBC_ROMSLOT16(config, m_rom[1], bbc_rom_devices, nullptr); // IC100
	BBC_ROMSLOT16(config, m_rom[2], bbc_rom_devices, nullptr); // IC88
	BBC_ROMSLOT16(config, m_rom[3], bbc_rom_devices, nullptr); // IC52

	SOFTWARE_LIST(config, "cass_ls").set_original("bbc_cass").set_filter("A");
	SOFTWARE_LIST(config, "rom_ls").set_original("bbc_rom").set_filter("B");
}


void bbcb_state::bbcb(machine_config &config)
{
	bbca(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &bbcb_state::bbcb_mem);

	m_ram->set_default_size("32K");

	BBC_VSP_SLOT(config, m_vsp, bbc_vsp_devices, "speech");
	m_vsp->add_route(ALL_OUTPUTS, "mono", 1.0);
	m_vsp->set_vsm_region("vsm");

	MOS6522(config, m_uservia, 16_MHz_XTAL / 16);
	m_uservia->writepa_handler().set("cent_data_out", FUNC(output_latch_device::write));
	m_uservia->readpb_handler().set(m_userport, FUNC(bbc_userport_slot_device::pb_r));
	m_uservia->writepb_handler().set(m_userport, FUNC(bbc_userport_slot_device::pb_w));
	m_uservia->writepb_handler().append(m_internal, FUNC(bbc_internal_slot_device::latch_fe60_w));
	m_uservia->ca2_handler().set("printer", FUNC(centronics_device::write_strobe));
	m_uservia->cb1_handler().set(m_userport, FUNC(bbc_userport_slot_device::write_cb1));
	m_uservia->cb2_handler().set(m_userport, FUNC(bbc_userport_slot_device::write_cb2));
	m_uservia->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<1>));

	centronics_device &centronics(CENTRONICS(config, "printer", centronics_devices, "printer"));
	centronics.ack_handler().set(m_uservia, FUNC(via6522_device::write_ca1));
	output_latch_device &latch(OUTPUT_LATCH(config, "cent_data_out"));
	centronics.set_output_latch(latch);

	upd7002_device &upd7002(UPD7002(config, "upd7002", 16_MHz_XTAL / 16));
	upd7002.get_analogue_callback().set(m_analog, FUNC(bbc_analogue_slot_device::ch_r));
	upd7002.eoc_callback().set(m_sysvia, FUNC(via6522_device::write_cb1));

	BBC_ANALOGUE_SLOT(config, m_analog, bbc_analogue_devices, nullptr);
	m_analog->lpstb_handler().set(m_sysvia, FUNC(via6522_device::write_cb2));
	m_analog->lpstb_handler().append([this](int state) { if (state) m_crtc->assert_light_pen_input(); });

	BBC_FDC_SLOT(config, m_fdc, 16_MHz_XTAL / 2, bbc_fdc_devices, "acorn8271");
	m_fdc->intrq_wr_callback().set(FUNC(bbcb_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(bbcb_state::fdc_drq_w));

	MC6854(config, m_adlc);
	m_adlc->out_txd_cb().set("network", FUNC(econet_device::host_data_w));
	m_adlc->out_irq_cb().set(FUNC(bbcb_state::adlc_irq_w));
	//m_adlc->out_rts_cb().

	econet_device &econet(ECONET(config, "network", 0));
	econet.clk_wr_callback().set(m_adlc, FUNC(mc6854_device::txc_w));
	econet.clk_wr_callback().append(m_adlc, FUNC(mc6854_device::rxc_w));
	econet.data_wr_callback().set(m_adlc, FUNC(mc6854_device::set_rx));
	ECONET_SLOT(config, "econet", "network", econet_devices);

	BBC_1MHZBUS_SLOT(config, m_1mhzbus, 16_MHz_XTAL / 16, bbc_1mhzbus_devices, nullptr);
	m_1mhzbus->add_route(ALL_OUTPUTS, "mono", 1.0);
	m_1mhzbus->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<3>));
	m_1mhzbus->nmi_handler().set(FUNC(bbcb_state::bus_nmi_w));

	BBC_TUBE_SLOT(config, m_tube, bbc_tube_devices, nullptr);
	m_tube->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<4>));

	BBC_USERPORT_SLOT(config, m_userport, bbc_userport_devices, nullptr);
	m_userport->cb1_handler().set(m_uservia, FUNC(via6522_device::write_cb1));
	m_userport->cb2_handler().set(m_uservia, FUNC(via6522_device::write_cb2));

	BBC_INTERNAL_SLOT(config, m_internal, 16_MHz_XTAL, bbcb_internal_devices, nullptr);
	m_internal->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<5>));
	m_internal->nmi_handler().set(FUNC(bbcb_state::bus_nmi_w));

	m_irqs->output_handler().append(m_internal, FUNC(bbc_internal_slot_device::irq6502_w));

	subdevice<software_list_device>("cass_ls")->set_filter("A,B");
	SOFTWARE_LIST(config, "flop_ls_b").set_original("bbcb_flop");
	SOFTWARE_LIST(config, "flop_ls_b_orig").set_original("bbcb_flop_orig");
	SOFTWARE_LIST(config, "hdd_ls").set_original("bbc_hdd").set_filter("B");
	SOFTWARE_LIST(config, "vsm_ls").set_original("bbc_vsm");
}


void bbcb_state::bbcb_de(machine_config &config)
{
	bbcb(config);

	m_fdc->set_fixed(true);
	m_fdc->set_insert_rom(false);
}


void bbcb_state::bbcb_no(machine_config &config)
{
	bbcb(config);

	BBC_KBD_NO(config.replace(), m_kbd, 16_MHz_XTAL / 16);
	m_kbd->ca2_handler().set(m_sysvia, FUNC(via6522_device::write_ca2));
	m_kbd->rst_handler().set(FUNC(bbcb_state::trigger_reset));

	m_fdc->set_fixed(true);
	m_fdc->set_insert_rom(false);
}


void bbcb_state::bbcb_us(machine_config &config)
{
	bbcb(config);

	m_screen->set_raw(16_MHz_XTAL, 1024, 0, 640, 262, 0, 200);

	m_fdc->set_fixed(true);
	m_fdc->set_insert_rom(false);

	SOFTWARE_LIST(config, "flop_ls_b_us").set_original("bbcb_flop_us");
}


void bbcb_state::dolphinm(machine_config &config)
{
	bbca(config);

	// The following ports are not fitted (maybe optional upgrades):
	//   Speech sockets (TMS5220/TMS6100)
	//   1MHz Expansion Bus Interface
	//   Analogue Interface, and uPD7002
	//   Econet, and MC6854 (no provision on board)

	m_maincpu->set_addrmap(AS_PROGRAM, &bbcb_state::dolphin_mem);

	m_ram->set_default_size("32K");

	MOS6522(config, m_uservia, 16_MHz_XTAL / 16);
	m_uservia->writepa_handler().set("cent_data_out", FUNC(output_latch_device::write));
	m_uservia->readpb_handler().set(m_userport, FUNC(bbc_userport_slot_device::pb_r));
	m_uservia->writepb_handler().set(m_userport, FUNC(bbc_userport_slot_device::pb_w));
	m_uservia->ca2_handler().set("printer", FUNC(centronics_device::write_strobe));
	m_uservia->cb1_handler().set(m_userport, FUNC(bbc_userport_slot_device::write_cb1));
	m_uservia->cb2_handler().set(m_userport, FUNC(bbc_userport_slot_device::write_cb2));
	m_uservia->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<1>));

	centronics_device &centronics(CENTRONICS(config, "printer", centronics_devices, "printer"));
	centronics.ack_handler().set(m_uservia, FUNC(via6522_device::write_ca1));
	output_latch_device &latch(OUTPUT_LATCH(config, "cent_data_out"));
	centronics.set_output_latch(latch);

	BBC_FDC_SLOT(config, m_fdc, 16_MHz_XTAL / 2, bbc_fdc_devices, "acorn1770").set_fixed(true); // WD1772
	m_fdc->set_insert_rom(false);
	m_fdc->intrq_wr_callback().set(FUNC(bbcb_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(bbcb_state::fdc_drq_w));

	BBC_USERPORT_SLOT(config, m_userport, bbc_userport_devices, nullptr);
	m_userport->cb1_handler().set(m_uservia, FUNC(via6522_device::write_cb1));
	m_userport->cb2_handler().set(m_uservia, FUNC(via6522_device::write_cb2));

	config.device_remove("romslot1");
	config.device_remove("romslot3");
	BBC_ROMSLOT32(config.replace(), m_rom[0], bbc_rom_devices, nullptr); // IC52 32K socket
	BBC_ROMSLOT32(config.replace(), m_rom[2], bbc_rom_devices, nullptr); // IC53 32K socket

	subdevice<software_list_device>("cass_ls")->set_filter("A,B");
	SOFTWARE_LIST(config, "flop_ls_b").set_original("bbcb_flop");
	SOFTWARE_LIST(config, "flop_ls_b_orig").set_original("bbcb_flop_orig");

	// TODO: Cartridge Bus Interface (in place of Tube port, unknown purpose/pinout)
}


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

    Torch Computers

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

void bbcb_state::torchf(machine_config &config)
{
	bbcb(config);

	TORCHB_KBD(config.replace(), m_kbd, 16_MHz_XTAL / 16);
	m_kbd->ca2_handler().set(m_sysvia, FUNC(via6522_device::write_ca2));
	m_kbd->rst_handler().set(FUNC(bbcb_state::trigger_reset));

	m_fdc->set_fixed(true);
	m_fdc->set_insert_rom(false);

	// Torch Z80 Communicator co-processor
	m_tube->set_default_option("zep100").set_fixed(true);
}


void bbcb_state::torchh(machine_config &config)
{
	torchf(config);

	//subdevice<floppy_connector>("acorn8271:i8271:1")->set_default_option(nullptr);

	// 10MB or 21MB HDD
	m_1mhzbus->set_default_option("torchhd").set_fixed(true);
}


void bbcb_state::torch301(machine_config &config)
{
	torchf(config);

	TORCHI_KBD(config.replace(), m_kbd, 16_MHz_XTAL / 16);
	m_kbd->ca2_handler().set(m_sysvia, FUNC(via6522_device::write_ca2));
	m_kbd->rst_handler().set(FUNC(bbcb_state::trigger_reset));

	//subdevice<floppy_connector>("acorn8271:i8271:1")->set_default_option(nullptr);

	// Torch Z80 Communicator co-processor
	m_tube->set_default_option("zep100").set_fixed(true);
	m_tube->set_insert_rom(false);

	// 20MB HDD
	m_1mhzbus->set_default_option("torchhd").set_fixed(true);
}


void bbcb_state::torch725(machine_config &config)
{
	torch301(config);

	// Torch 68000 Atlas co-processor
	//m_tube->set_default_option("atlas").set_fixed(true);
	m_tube->set_insert_rom(false);
}


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

    Cisco Systems

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

void bbcb_state::sist1(machine_config &config)
{
	bbcb(config);

	m_1mhzbus->set_default_option("cisco").set_fixed(true);
}


ROM_START(bbca)
	ROM_REGION(0x4000, "mos", 0)
	ROM_SYSTEM_BIOS(0, "120", "OS 1.20")
	ROMX_LOAD("os12.rom",  0x0000, 0x4000, CRC(3c14fc70) SHA1(0d9bcaf6a393c9ce2359ed700ddb53c232c2c45d), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "100", "OS 1.00")
	ROMX_LOAD("os10.rom",  0x0000, 0x4000, CRC(9679b8f8) SHA1(d35f6723132aabe3c4d00fc16fd9ecc6768df753), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "092", "OS 0.92")
	ROMX_LOAD("os092.rom", 0x0000, 0x4000, CRC(59ef7eb8) SHA1(dca33995c0d008a527efe923d03333394b01022c), ROM_BIOS(2))

	ROM_SYSTEM_BIOS(3, "010", "OS 0.10") // OS0.1 does not support ROM paging, load BASIC into all pages
	ROMX_LOAD("os01.rom",  0x0000, 0x4000, CRC(45ee0980) SHA1(4b0ece6dc139d5d3f4fabd023716fb6f25149b80), ROM_BIOS(3))

	// page 0 00000 IC52
	// page 1 04000 IC88
	// page 2 08000 IC100
	// page 3 0c000 IC101 BASIC
	ROM_REGION(0x40000, "rom", ROMREGION_ERASEFF)
	ROMX_LOAD("basic2.rom", 0x0c000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281), ROM_BIOS(0))
	ROMX_LOAD("basic2.rom", 0x0c000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281), ROM_BIOS(1))
	ROMX_LOAD("basic1.rom", 0x0c000, 0x4000, CRC(b3364108) SHA1(890f6e3e7fab3340f75b85e93ff29332bc9ecb2e), ROM_BIOS(2))
	ROMX_LOAD("basic1.rom", 0x00000, 0x4000, CRC(b3364108) SHA1(890f6e3e7fab3340f75b85e93ff29332bc9ecb2e), ROM_BIOS(3))
	ROM_RELOAD(             0x04000, 0x4000 )
	ROM_RELOAD(             0x08000, 0x4000 )
	ROM_RELOAD(             0x0c000, 0x4000 )
ROM_END


ROM_START(bbcb)
	ROM_REGION(0x4000, "mos", 0)
	ROM_SYSTEM_BIOS(0, "120", "OS 1.20")
	ROMX_LOAD("os12.rom",  0x0000, 0x4000, CRC(3c14fc70) SHA1(0d9bcaf6a393c9ce2359ed700ddb53c232c2c45d), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "100", "OS 1.00")
	ROMX_LOAD("os10.rom",  0x0000, 0x4000, CRC(9679b8f8) SHA1(d35f6723132aabe3c4d00fc16fd9ecc6768df753), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "092", "OS 0.92")
	ROMX_LOAD("os092.rom", 0x0000, 0x4000, CRC(59ef7eb8) SHA1(dca33995c0d008a527efe923d03333394b01022c), ROM_BIOS(2))

	ROM_SYSTEM_BIOS(3, "010", "OS 0.10") // OS0.1 does not support ROM paging, load BASIC into all pages
	ROMX_LOAD("os01.rom",  0x0000, 0x4000, CRC(45ee0980) SHA1(4b0ece6dc139d5d3f4fabd023716fb6f25149b80), ROM_BIOS(3))

	// page 0 00000 IC52  DFS
	// page 1 04000 IC88
	// page 2 08000 IC100
	// page 3 0c000 IC101 BASIC
	ROM_REGION(0x40000, "rom", ROMREGION_ERASEFF)
	ROMX_LOAD("basic2.rom", 0x0c000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281), ROM_BIOS(0))
	ROMX_LOAD("basic2.rom", 0x0c000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281), ROM_BIOS(1))
	ROMX_LOAD("basic1.rom", 0x0c000, 0x4000, CRC(b3364108) SHA1(890f6e3e7fab3340f75b85e93ff29332bc9ecb2e), ROM_BIOS(2))
	ROMX_LOAD("basic1.rom", 0x00000, 0x4000, CRC(b3364108) SHA1(890f6e3e7fab3340f75b85e93ff29332bc9ecb2e), ROM_BIOS(3))
	ROM_RELOAD(             0x04000, 0x4000 )
	ROM_RELOAD(             0x08000, 0x4000 )
	ROM_RELOAD(             0x0c000, 0x4000 )

	ROM_REGION(0x40000, "vsm", ROMREGION_ERASE00)
	ROM_LOAD("cm62024.bin", 0x3c000, 0x4000, CRC(98e1bf9e) SHA1(b369809275cb67dfd8a749265e91adb2d2558ae6))
ROM_END


ROM_START(bbcb_de)
	ROM_REGION(0x4000, "mos", 0)
	ROM_LOAD("os_de.rom",   0x0000, 0x4000, CRC(b7262caf) SHA1(aadf90338ee9d1c85dfa73beba50e930c2a38f10))

	// page 0 00000 IC72 DFS
	// page 1 04000 IC73
	// page 2 08000 IC74
	// page 3 0c000 IC75 BASIC
	ROM_REGION(0x40000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("basic2.rom",  0xc000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281))
	ROM_LOAD("dfs10.rom",   0x0000, 0x4000, CRC(7e367e8c) SHA1(161f585dc45665ea77433c84afd2f95049f7f5a0))

	ROM_REGION(0x40000, "vsm", ROMREGION_ERASE00)
	ROM_LOAD("cm62024.bin", 0x3c000, 0x4000, CRC(98e1bf9e) SHA1(b369809275cb67dfd8a749265e91adb2d2558ae6))
ROM_END


ROM_START(bbcb_no)
	ROM_REGION(0x4000, "mos", 0)
	ROM_LOAD("nos12.rom", 0x0000, 0x4000, CRC(49859294) SHA1(2b6aecd33a43f296c20832524e47cc7e3a9c3b17))

	// page 0 00000 IC72 DFS
	// page 1 04000 IC73 VIEW2.1
	// page 2 08000 IC74
	// page 3 0c000 IC75 BASIC
	ROM_REGION(0x40000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("dfs0.9h.rom",  0x0000, 0x2000, CRC(af2fa873) SHA1(dbbec4d2540a854c120be3194c7566a2b79d153b))
	ROM_LOAD("viewa210.rom", 0x4000, 0x4000, CRC(4345359f) SHA1(88c93df1854f5fbe6cd6e5f0e29a8bf4ea3b5614))
	ROM_LOAD("basic2.rom",   0xc000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281))

	ROM_REGION(0x40000, "vsm", ROMREGION_ERASE00)
	ROM_LOAD("vm61002.bin", 0x0000, 0x4000, CRC(bf4b3b64) SHA1(66876702d1d95eecc034d20f25047f893a27cde5))
ROM_END


ROM_START(bbcb_us)
	ROM_REGION(0x4000, "mos", 0)
	ROM_LOAD("usmos10.rom", 0x0000, 0x4000, CRC(c8e946a9) SHA1(83d91d089dca092d2c8b7c3650ff8143c9069b89))

	// page 0 00000 IC72 VIEW2.1
	// page 1 04000 IC73 US DNFS
	// page 2 08000 IC74 US BASIC
	// page 3 0c000 IC75
	ROM_REGION(0x40000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("usbasic3.rom", 0x8000, 0x4000, CRC(161b9539) SHA1(b39014610a968789afd7695aa04d1277d874405c))
	ROM_LOAD("viewa210.rom", 0x0000, 0x4000, CRC(4345359f) SHA1(88c93df1854f5fbe6cd6e5f0e29a8bf4ea3b5614))
	ROM_LOAD("usdnfs10.rom", 0x4000, 0x4000, CRC(7e367e8c) SHA1(161f585dc45665ea77433c84afd2f95049f7f5a0))

	ROM_REGION(0x40000, "vsm", ROMREGION_ERASE00)
	ROM_LOAD("vm61002.bin", 0x0000, 0x4000, CRC(bf4b3b64) SHA1(66876702d1d95eecc034d20f25047f893a27cde5))
ROM_END


ROM_START(dolphinm)
	// page ? 00000 IC51 OS/BASIC
	// page ? 04000 IC52
	// page ? 08000 IC53 DFS
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("hope_os_basic.rom", 0x3c000, 0x8000, CRC(8b30f9e5) SHA1(0a07161fa2fbc102fb306233bd81790c7b04f793))
	ROM_LOAD("hope_dfs.rom",      0x0c000, 0x4000, CRC(5d404973) SHA1(4c9f7cf141ad028fe5e517c45e6e3a43b1a76d24))

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)

	ROM_REGION(0x40000, "vsm", ROMREGION_ERASE00)
ROM_END


ROM_START(torchf)
	ROM_REGION(0x4000, "mos", 0)
	ROM_LOAD("os12.rom", 0x0000, 0x4000, CRC(3c14fc70) SHA1(0d9bcaf6a393c9ce2359ed700ddb53c232c2c45d))

	// page 0 00000 IC52  BASIC
	// page 1 04000 IC88  DNFS
	// page 2 08000 IC100 CPN (inserted by bbc_tube_device)
	// page 3 0c000 IC101
	ROM_REGION(0x40000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("basic2.rom",         0x0000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281))
	ROM_LOAD("dnfs120-201666.rom", 0x4000, 0x4000, CRC(8ccd2157) SHA1(7e3c536baeae84d6498a14e8405319e01ee78232))

	ROM_REGION(0x40000, "vsm", ROMREGION_ERASE00)
	ROM_LOAD("vm61002.bin", 0x0000, 0x4000, CRC(bf4b3b64) SHA1(66876702d1d95eecc034d20f25047f893a27cde5))
ROM_END


ROM_START(torchh)
	ROM_REGION(0x4000, "mos", 0)
	ROM_LOAD("os12.rom", 0x0000, 0x4000, CRC(3c14fc70) SHA1(0d9bcaf6a393c9ce2359ed700ddb53c232c2c45d))

	// page 0 00000 IC52  BASIC
	// page 1 04000 IC88  DNFS
	// page 2 08000 IC100 CPN (inserted by bbc_tube_device)
	// page 3 0c000 IC101
	ROM_REGION(0x40000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("basic2.rom",         0x0000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281))
	ROM_LOAD("dnfs120-201666.rom", 0x4000, 0x4000, CRC(8ccd2157) SHA1(7e3c536baeae84d6498a14e8405319e01ee78232))

	ROM_REGION(0x40000, "vsm", ROMREGION_ERASE00)
	ROM_LOAD("vm61002.bin", 0x0000, 0x4000, CRC(bf4b3b64) SHA1(66876702d1d95eecc034d20f25047f893a27cde5))

	DISK_REGION("1mhzbus:torchhd:sasi:0:s1410")
	DISK_IMAGE("torch_utilities", 0, BAD_DUMP SHA1(33a5f169bd91b9c6049e8bd0b237429c091fddd0)) // NEC D5126 contains Standard and Hard Disc Utilities, not known what was factory installed
ROM_END


ROM_START(torch301)
	ROM_REGION(0x4000, "mos", 0)
	ROM_LOAD("os12.rom", 0x0000, 0x4000, CRC(3c14fc70) SHA1(0d9bcaf6a393c9ce2359ed700ddb53c232c2c45d))

	// page 0 00000 IC52  BASIC
	// page 1 04000 IC88  ECO 3.35K
	// page 2 08000 IC100
	// page 3 0c000 IC101 MCP 1.01
	ROM_REGION(0x40000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("basic2.rom",      0x0000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281))
	ROM_LOAD("eco_3.35k.rom",   0x4000, 0x4000, CRC(3ca2faea) SHA1(7462ced7b83d74b822815bc00ed40a89f84e0276))
	ROM_LOAD("mcp_1.01_ci.rom", 0xc000, 0x4000, CRC(436e7fe9) SHA1(be10872aeb88714bd56462a2e86929953dee1c01))

	ROM_REGION(0x40000, "vsm", ROMREGION_ERASE00)
	ROM_LOAD("vm61002.bin", 0x0000, 0x4000, CRC(bf4b3b64) SHA1(66876702d1d95eecc034d20f25047f893a27cde5))
ROM_END


ROM_START(torch725)
	ROM_REGION(0x4000, "mos", 0)
	ROM_LOAD("os12.rom", 0x0000, 0x4000, CRC(3c14fc70) SHA1(0d9bcaf6a393c9ce2359ed700ddb53c232c2c45d))

	// page 0 00000 IC52  BASIC
	// page 1 04000 IC88  ECO 3.35K
	// page 2 08000 IC100 Unix Host
	// page 3 0c000 IC101 MCP 1.22
	ROM_REGION(0x40000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("basic2.rom",      0x0000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281))
	ROM_LOAD("eco_3.35k.rom",   0x4000, 0x4000, CRC(3ca2faea) SHA1(7462ced7b83d74b822815bc00ed40a89f84e0276))
	ROM_LOAD("unix_1.00.rom",   0x8000, 0x4000, CRC(90f85ce9) SHA1(e37d043c8df30c49ba8717e1aa0b92105cb0c937))
	ROM_LOAD("mcp_1.22_ci.rom", 0xc000, 0x4000, CRC(764f4948) SHA1(409762deafb76b1f86be39bfbf2f812d5de3ff92))

	ROM_REGION(0x40000, "vsm", ROMREGION_ERASE00)
	ROM_LOAD("vm61002.bin", 0x0000, 0x4000, CRC(bf4b3b64) SHA1(66876702d1d95eecc034d20f25047f893a27cde5))
ROM_END


ROM_START(sist1)
	ROM_REGION(0x4000, "mos", 0)
	ROM_LOAD("os12.rom", 0x0000, 0x4000, CRC(3c14fc70) SHA1(0d9bcaf6a393c9ce2359ed700ddb53c232c2c45d))

	// page 0 00000 IC52  DFS
	// page 1 04000 IC88  PROGS
	// page 2 08000 IC100 BASIC
	// page 3 0c000 IC101 STARTUP
	ROM_REGION(0x40000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("dnfs120-201666.rom", 0x0000, 0x4000, CRC(8ccd2157) SHA1(7e3c536baeae84d6498a14e8405319e01ee78232))
	ROM_LOAD("sist1_progs.bin",    0x4000, 0x4000, CRC(aea21243) SHA1(4398ba29c871fa397654aa182c63ccdcad597625))
	ROM_LOAD("basic2.rom",         0x8000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281))
	ROM_LOAD("sist1_startup.bin",  0xc000, 0x4000, CRC(9cd1602c) SHA1(5ea266f47ff83821ccdbec006b8506b2e892b115))

	ROM_REGION(0x40000, "vsm", ROMREGION_ERASE00)
	ROM_LOAD("cm62024.bin", 0x3c000, 0x4000, CRC(98e1bf9e) SHA1(b369809275cb67dfd8a749265e91adb2d2558ae6))
ROM_END

} // anonymous namespace


//    YEAR  NAME        PARENT  COMPAT MACHINE     INPUT     CLASS         INIT       COMPANY                FULLNAME                          FLAGS
COMP( 1981, bbcb,       0,      bbca,  bbcb,       bbcb,     bbcb_state,   init_bbc,  "Acorn Computers",     "BBC Micro Model B",              MACHINE_IMPERFECT_GRAPHICS )
COMP( 1981, bbca,       bbcb,   0,     bbca,       bbca,     bbcb_state,   init_bbc,  "Acorn Computers",     "BBC Micro Model A",              MACHINE_IMPERFECT_GRAPHICS )
COMP( 1982, bbcb_de,    bbcb,   0,     bbcb_de,    bbcb,     bbcb_state,   init_bbc,  "Acorn Computers",     "BBC Micro Model B (German)",     MACHINE_IMPERFECT_GRAPHICS )
COMP( 1984, bbcb_no,    bbcb,   0,     bbcb_no,    bbcb,     bbcb_state,   init_bbc,  "Acorn Computers",     "BBC Micro Model B (Norway)",     MACHINE_IMPERFECT_GRAPHICS )
COMP( 1983, bbcb_us,    bbcb,   0,     bbcb_us,    bbcb,     bbcb_state,   init_bbc,  "Acorn Computers",     "BBC Micro Model B (US)",         MACHINE_IMPERFECT_GRAPHICS )
COMP( 1989, dolphinm,   bbcb,   0,     dolphinm,   bbca,     bbcb_state,   init_bbc,  "Hope Computers",      "Dolphin Microcomputer",          MACHINE_IMPERFECT_GRAPHICS )

// Torch Computers
COMP( 1982, torchf,     bbcb,   0,     torchf,     torch,    bbcb_state,   init_bbc,  "Torch Computers",     "Torch CF240",                    MACHINE_IMPERFECT_GRAPHICS )
COMP( 1983, torchh,     bbcb,   0,     torchh,     torch,    bbcb_state,   init_bbc,  "Torch Computers",     "Torch CH240",                    MACHINE_IMPERFECT_GRAPHICS )
COMP( 1983, torch301,   bbcb,   0,     torch301,   torch,    bbcb_state,   init_bbc,  "Torch Computers",     "Torch Model 301",                MACHINE_NOT_WORKING )
COMP( 1983, torch725,   bbcb,   0,     torch725,   torch,    bbcb_state,   init_bbc,  "Torch Computers",     "Torch Model 725",                MACHINE_NOT_WORKING )

// Industrial
COMP( 198?, sist1,      bbcb,   0,     sist1,      bbcb,     bbcb_state,   init_bbc,  "Cisco Systems",       "Cisco SIST1 Terminal",           MACHINE_NOT_WORKING )



bbcbc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Incog, Robbbert
/***************************************************************************

  bbcbc.c

  BBC Bridge Companion

  Inputs hooked up - 2009-03-14 - Robbbert
  Clock Freq added - 2009-05-18 - incog

  From the Operations Manual, Page 1:

    The BBC BRIDGE COMPANION and its associated family of
    Cartridges are distributed in the U.K. by:

    CONTEMPORARY CHESS COMPUTERS
    2/3 Noble Corner
    Great West Road
    HOUNSLOW
    Middlesex
    TW5 0PA

    (C) 1985 Unicard Ltd.

    The title BBC Bridge is under licence from BBC Enterprises Ltd.

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

#include "emu.h"
#include "cpu/z80/z80.h"
#include "video/tms9928a.h"
#include "machine/z80pio.h"
#include "machine/z80daisy.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "softlist_dev.h"


namespace {

class bbcbc_state : public driver_device
{
public:
	bbcbc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_z80pio(*this, "z80pio")
		, m_buttons(*this, "BUTTONS.%u", 0U)
	{ }

	void bbcbc(machine_config &config);

private:
	uint8_t input_r();
	void input_select_w(uint8_t data);
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	uint8_t m_input_select = 0U;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	required_device<z80_device> m_maincpu;
	required_device<z80pio_device> m_z80pio;
	required_ioport_array<3> m_buttons;
};


#define MAIN_CLOCK XTAL(4'433'619)


void bbcbc_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	map(0x4000, 0xbfff).r("cartslot", FUNC(generic_slot_device::read_rom));
	map(0xe000, 0xe7ff).ram();
}

void bbcbc_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x7f).lrw8(
						 NAME([this](offs_t offset) { return m_z80pio->read(offset >> 5); }),
						 NAME([this](offs_t offset, u8 data) { m_z80pio->write(offset >> 5, data); }));
	map(0x80, 0x81).rw("tms9129", FUNC(tms9129_device::read), FUNC(tms9129_device::write));
}

// Input bits are read through the PIO four at a time, then stored individually in RAM at E030-E03B
static INPUT_PORTS_START( bbcbc )
	PORT_START("BUTTONS.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Pass") PORT_CODE(KEYCODE_A) // Grey button
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Spades") PORT_CODE(KEYCODE_Z) // Grey button
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Clubs") PORT_CODE(KEYCODE_V) // Grey button
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Rdbl") PORT_CODE(KEYCODE_F) // Grey button
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("BUTTONS.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("NT") PORT_CODE(KEYCODE_S) // Grey button
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Hearts, Up") PORT_CODE(KEYCODE_X) // Grey button
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("Play, Yes") // Yellow button
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_SELECT) PORT_NAME("Back") // Red button
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("BUTTONS.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Dbl") PORT_CODE(KEYCODE_D) // Grey button
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Diamonds, Down") PORT_CODE(KEYCODE_C) // Grey button
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_START) PORT_NAME("Start") // Red button
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("Play, No") // Yellow button
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END


static const z80_daisy_config bbcbc_daisy_chain[] =
{
	{ "z80pio" },
	{ nullptr }
};


void bbcbc_state::bbcbc(machine_config &config)
{
	Z80(config, m_maincpu, 10.6875_MHz_XTAL / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &bbcbc_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &bbcbc_state::io_map);
	m_maincpu->set_daisy_config(bbcbc_daisy_chain);

	Z80PIO(config, m_z80pio, 10.6875_MHz_XTAL / 3);
	//m_z80pio->out_pa_callback().set(???);
	m_z80pio->in_pb_callback().set(FUNC(bbcbc_state::input_r));
	m_z80pio->out_pb_callback().set(FUNC(bbcbc_state::input_select_w));

	tms9129_device &vdp(TMS9129(config, "tms9129", 10.6875_MHz_XTAL));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	vdp.int_callback().set_inputline("maincpu", 0);
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	// Software on ROM cartridges
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "bbcbc_cart");
	SOFTWARE_LIST(config, "cart_list").set_original("bbcbc");
}


void bbcbc_state::machine_start()
{
	save_item(NAME(m_input_select));
}

void bbcbc_state::machine_reset()
{
	m_input_select = 0xff;
}

uint8_t bbcbc_state::input_r()
{
	switch (m_input_select)
	{
		case 0xef:
			return m_buttons[0]->read();
		case 0xdf:
			return m_buttons[1]->read();
		case 0xbf:
			return m_buttons[2]->read();
	}
	logerror("Unknown input select: %02x\n", m_input_select);
	return 0xff;
}

void bbcbc_state::input_select_w(uint8_t data)
{
	m_input_select = data;
}


ROM_START( bbcbc )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD("br_4_1.ic3", 0x0000, 0x2000, CRC(7c880d75) SHA1(954db096bd9e8edfef72946637a12f1083841fb0))
	ROM_LOAD("br_4_2.ic4", 0x2000, 0x2000, CRC(16a33aef) SHA1(9529f9f792718a3715af2063b91a5fb18f741226))
ROM_END

} // anonymous namespace


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

  Game driver(s)

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

//   YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY    FULLNAME                FLAGS
CONS(1985, bbcbc, 0,      0,      bbcbc,   bbcbc, bbcbc_state, empty_init, "Unicard", "BBC Bridge Companion", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



bbcbp.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Gordon Jefferyes, Nigel Barnes
/******************************************************************************

    BBC Model B+

    ANB51 - BBC Model B+ 64K
    ANB52 - BBC Model B+ 64K with Econet
    ANB53 - BBC Model B+ 64K with Disc interface
    ANB54 - BBC Model B+ 64K with Disc and Econet interfaces
    ANB55 - BBC Model B+ 128K with Disc interface

    Econet

    AEH25 - Econet X25 Gateway

    Acorn Business Computer

    ABC110        - 64K,   10MB HDD, Z80, CP/M 2.2
    ABC210/ACW443 - 4096K, 20MB HDD, 32016, PanOS
    ABC310        - 1024K, 10MB HDD, 80286, DOS 3.1/GEM

    TODO:
    - Cambridge Workstation has a Tube switch to disable internal co-processor.

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

#include "emu.h"
#include "bbc.h"
#include "acorn_serproc.h"
#include "bbc_kbd.h"

#include "machine/tms6100.h"
#include "machine/wd_fdc.h"
#include "sound/tms5220.h"

#include "formats/uef_cas.h"
#include "formats/csw_cas.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "bbc.lh"


namespace {

class bbcbp_state : public bbc_state
{
public:
	bbcbp_state(const machine_config &mconfig, device_type type, const char *tag)
		: bbc_state(mconfig, type, tag)
		, m_view_shadow(*this, "view_shadow")
		, m_view_paged(*this, "view_paged")
		, m_kbd(*this, "kbd")
		, m_sn(*this, "sn")
		, m_vsp(*this, "vsp")
		, m_wdfdc(*this, "wdfdc")
		, m_adlc(*this, "mc6854")
		, m_statid(*this, "STATID")
	{ }

	void bbcbp(machine_config &config);
	void bbcbp128(machine_config &config);
	void abc110(machine_config &config);
	void acw443(machine_config &config);
	void abc310(machine_config &config);
	void cfa3000bp(machine_config &config);
	void econx25(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	memory_view m_view_shadow;
	memory_view m_view_paged;
	required_device<bbc_kbd_device> m_kbd;
	required_device<sn76489a_device> m_sn;
	optional_device<tms5220_device> m_vsp;
	optional_device<wd1770_device> m_wdfdc;
	required_device<mc6854_device> m_adlc;
	required_ioport m_statid;

	void bbcbp_fetch(address_map &map) ATTR_COLD;
	void bbcbp_mem(address_map &map) ATTR_COLD;
	void econx25_mem(address_map &map) ATTR_COLD;

	void trigger_reset(int state);

	uint8_t fetch_r(offs_t offset);
	void romsel_w(offs_t offset, uint8_t data);
	uint8_t paged_r(offs_t offset);
	void paged_w(offs_t offset, uint8_t data);
	void drive_control_w(uint8_t data);

	uint8_t sysvia_pa_r();
	void sysvia_pa_w(uint8_t data);
	void update_sdb();
	uint8_t sysvia_pb_r();
	void sysvia_pb_w(uint8_t data);

	uint8_t m_sdb = 0x00;
};


void bbcbp_state::machine_start()
{
	bbc_state::machine_start();

	save_item(NAME(m_sdb));
}


void bbcbp_state::bbcbp_fetch(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(bbcbp_state::fetch_r));
}


void bbcbp_state::bbcbp_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).rw(m_ram, FUNC(ram_device::read), FUNC(ram_device::write));                                    //    0000-7FFF                 Regular RAM
	map(0x3000, 0x7fff).view(m_view_shadow);                                                                           //    3000-7FFF                 20K Shadow RAM
	m_view_shadow[0](0x3000, 0x7fff).lr8(NAME([this] (offs_t offset) { return m_ram->pointer()[offset + 0xb000]; }));
	m_view_shadow[0](0x3000, 0x7fff).lw8(NAME([this] (offs_t offset, uint8_t data) { m_ram->pointer()[offset + 0xb000] = data; }));
	map(0x8000, 0xbfff).rw(FUNC(bbcbp_state::paged_r), FUNC(bbcbp_state::paged_w));                                    //    8000-BFFF                 Paged ROM/RAM
	map(0x8000, 0xafff).view(m_view_paged);                                                                            //    8000-AFFF                 12K Paged RAM
	m_view_paged[0](0x8000, 0xafff).lr8(NAME([this] (offs_t offset) { return m_ram->pointer()[offset + 0x8000]; }));
	m_view_paged[0](0x8000, 0xafff).lw8(NAME([this] (offs_t offset, uint8_t data) { m_ram->pointer()[offset + 0x8000] = data; }));
	map(0xc000, 0xffff).rw(FUNC(bbcbp_state::mos_r), FUNC(bbcbp_state::mos_w));                                        //    C000-FBFF                 OS ROM
	map(0xfc00, 0xfcff).rw(m_1mhzbus, FUNC(bbc_1mhzbus_slot_device::fred_r), FUNC(bbc_1mhzbus_slot_device::fred_w));   //    FC00-FCFF                 FRED Address Page
	map(0xfd00, 0xfdff).rw(m_1mhzbus, FUNC(bbc_1mhzbus_slot_device::jim_r), FUNC(bbc_1mhzbus_slot_device::jim_w));     //    FD00-FDFF                 JIM Address Page
	map(0xfe00, 0xfeff).lr8(NAME([]() { return 0xfe; })).nopw();                                                       //    FE00-FEFF                 SHEILA Address Page
	map(0xfe00, 0xfe00).mirror(0x06).rw(m_crtc, FUNC(hd6845s_device::status_r), FUNC(hd6845s_device::address_w));      //    FE00-FE07  6845 CRTC      Video controller
	map(0xfe01, 0xfe01).mirror(0x06).rw(m_crtc, FUNC(hd6845s_device::register_r), FUNC(hd6845s_device::register_w));
	map(0xfe08, 0xfe09).mirror(0x06).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));            //    FE08-FE0F  6850 ACIA      Serial controller
	map(0xfe10, 0xfe17).rw("serproc", FUNC(acorn_serproc_device::read), FUNC(acorn_serproc_device::write));            //    FE10-FE17  Serial ULA     Serial system chip
	map(0xfe18, 0xfe1f).lr8(NAME([this]() { econet_int_enable(0); return m_statid->read(); }));                        // R: FE18-FE1F  INTOFF/STATID  ECONET Interrupt Off / ID No.
	map(0xfe18, 0xfe1f).lw8(NAME([this](uint8_t data) { econet_int_enable(0); }));                                     // W: FE18-FE1F  INTOFF         ECONET Interrupt Off
	map(0xfe20, 0xfe2f).lr8(NAME([this]() { econet_int_enable(1); return 0xfe; }));                                    // R: FE20-FE2F  INTON          ECONET Interrupt On
	map(0xfe20, 0xfe2f).w(FUNC(bbcbp_state::video_ula_w));                                                             // W: FE20-FE2F  Video ULA      Video system chip
	map(0xfe30, 0xfe3f).w(FUNC(bbcbp_state::romsel_w));                                                                // W: FE30-FE3F  84LS161        Paged ROM selector
	map(0xfe40, 0xfe4f).mirror(0x10).m(m_sysvia, FUNC(via6522_device::map));                                           //    FE40-FE5F  6522 VIA       SYSTEM VIA
	map(0xfe60, 0xfe6f).mirror(0x10).m(m_uservia, FUNC(via6522_device::map));                                          //    FE60-FE7F  6522 VIA       USER VIA
	map(0xfe80, 0xfe83).w(FUNC(bbcbp_state::drive_control_w));                                                         //    FE80-FE83  1770 FDC       Drive control register
	map(0xfe84, 0xfe9f).rw(m_wdfdc, FUNC(wd1770_device::read), FUNC(wd1770_device::write));                            //    FE84-FE9F  1770 FDC       Floppy disc controller
	map(0xfea0, 0xfea3).mirror(0x1c).rw(m_adlc, FUNC(mc6854_device::read), FUNC(mc6854_device::write));                //    FEA0-FEBF  68B54 ADLC     ECONET controller
	map(0xfec0, 0xfec3).mirror(0x1c).rw("upd7002", FUNC(upd7002_device::read), FUNC(upd7002_device::write));           //    FEC0-FEDF  uPD7002        Analogue to digital converter
	map(0xfee0, 0xfeff).rw(m_tube, FUNC(bbc_tube_slot_device::host_r), FUNC(bbc_tube_slot_device::host_w));            //    FEE0-FEFF  Tube ULA       Tube system interface
}


void bbcbp_state::econx25_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).rw(m_ram, FUNC(ram_device::read), FUNC(ram_device::write));                                    //    0000-7FFF                 Regular RAM
	map(0x3000, 0x7fff).view(m_view_shadow);                                                                           //    3000-7FFF                 20K Shadow RAM
	m_view_shadow[0](0x3000, 0x7fff).lr8(NAME([this] (offs_t offset) { return m_ram->pointer()[offset + 0xb000]; }));
	m_view_shadow[0](0x3000, 0x7fff).lw8(NAME([this] (offs_t offset, uint8_t data) { m_ram->pointer()[offset + 0xb000] = data; }));
	map(0x8000, 0xbfff).rw(FUNC(bbcbp_state::paged_r), FUNC(bbcbp_state::paged_w));                                    //    8000-BFFF                 Paged ROM/RAM
	map(0x8000, 0xafff).view(m_view_paged);                                                                            //    8000-AFFF                 12K Paged RAM
	m_view_paged[0](0x8000, 0xafff).lr8(NAME([this] (offs_t offset) { return m_ram->pointer()[offset + 0x8000]; }));
	m_view_paged[0](0x8000, 0xafff).lw8(NAME([this] (offs_t offset, uint8_t data) { m_ram->pointer()[offset + 0x8000] = data; }));
	map(0xc000, 0xffff).rw(FUNC(bbcbp_state::mos_r), FUNC(bbcbp_state::mos_w));                                        //    C000-FBFF                 OS ROM
	map(0xfc00, 0xfcff).rw(m_1mhzbus, FUNC(bbc_1mhzbus_slot_device::fred_r), FUNC(bbc_1mhzbus_slot_device::fred_w));   //    FC00-FCFF                 FRED Address Page
	map(0xfd00, 0xfdff).rw(m_1mhzbus, FUNC(bbc_1mhzbus_slot_device::jim_r), FUNC(bbc_1mhzbus_slot_device::jim_w));     //    FD00-FDFF                 JIM Address Page
	map(0xfe00, 0xfeff).lr8(NAME([]() { return 0xfe; })).nopw();                                                       //    FE00-FEFF                 SHEILA Address Page
	map(0xfe00, 0xfe00).mirror(0x06).rw(m_crtc, FUNC(hd6845s_device::status_r), FUNC(hd6845s_device::address_w));      //    FE00-FE07  6845 CRTC      Video controller
	map(0xfe01, 0xfe01).mirror(0x06).rw(m_crtc, FUNC(hd6845s_device::register_r), FUNC(hd6845s_device::register_w));
	map(0xfe08, 0xfe09).mirror(0x06).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));            //    FE08-FE0F  6850 ACIA      Serial controller
	map(0xfe10, 0xfe17).rw("serproc", FUNC(acorn_serproc_device::read), FUNC(acorn_serproc_device::write));            //    FE10-FE17  Serial ULA     Serial system chip
	map(0xfe18, 0xfe1f).lr8(NAME([this]() { econet_int_enable(0); return m_statid->read(); }));                        // R: FE18-FE1F  INTOFF/STATID  ECONET Interrupt Off / ID No.
	map(0xfe18, 0xfe1f).lw8(NAME([this](uint8_t data) { econet_int_enable(0); }));                                     // W: FE18-FE1F  INTOFF         ECONET Interrupt Off
	map(0xfe20, 0xfe2f).lr8(NAME([this]() { econet_int_enable(1); return 0xfe; }));                                    // R: FE20-FE2F  INTON          ECONET Interrupt On
	map(0xfe20, 0xfe2f).w(FUNC(bbcbp_state::video_ula_w));                                                             // W: FE20-FE2F  Video ULA      Video system chip
	map(0xfe30, 0xfe3f).w(FUNC(bbcbp_state::romsel_w));                                                                // W: FE30-FE3F  84LS161        Paged ROM selector
	map(0xfe40, 0xfe4f).mirror(0x10).m(m_sysvia, FUNC(via6522_device::map));                                           //    FE40-FE5F  6522 VIA       SYSTEM VIA
	map(0xfe60, 0xfe6f).mirror(0x10).m(m_uservia, FUNC(via6522_device::map));                                          //    FE60-FE7F  6522 VIA       USER VIA
	map(0xfea0, 0xfea3).mirror(0x1c).rw(m_adlc, FUNC(mc6854_device::read), FUNC(mc6854_device::write));                //    FEA0-FEBF  68B54 ADLC     ECONET controller
	map(0xfec0, 0xfec3).mirror(0x1c).rw("upd7002", FUNC(upd7002_device::read), FUNC(upd7002_device::write));           //    FEC0-FEDF  uPD7002        Analogue to digital converter
	map(0xfee0, 0xfeff).rw(m_tube, FUNC(bbc_tube_slot_device::host_r), FUNC(bbc_tube_slot_device::host_w));            //    FEE0-FEFF  Tube ULA       Tube system interface
}


uint8_t bbcbp_state::fetch_r(offs_t offset)
{
	switch (offset & 0xf000)
	{
	case 0xa000:
		// Code executing from sideways RAM between 0xa000-0xafff will access the shadow RAM (if selected)
		if (m_vdusel && m_paged_ram)
			m_view_shadow.select(0);
		else
			m_view_shadow.disable();
		break;

	case 0xc000:
	case 0xd000:
		// Access shadow RAM if VDU drivers and shadow RAM selected
		if (m_vdusel)
			m_view_shadow.select(0);
		else
			m_view_shadow.disable();
		break;

	default:
		m_view_shadow.disable();
		break;
	}
	return m_maincpu->space(AS_PROGRAM).read_byte(offset);
}

void bbcbp_state::romsel_w(offs_t offset, uint8_t data)
{
	// the BBC Model B+ addresses all 16 ROM sockets and extra 12K of RAM at 0x8000 and 20K of shadow RAM at 0x3000
	switch (offset & 0x07)
	{
	case 0x00:
		m_paged_ram = BIT(data, 7);
		if (m_paged_ram)
			m_view_paged.select(0);
		else
			m_view_paged.disable();

		m_romsel = data & 0x0f;
		break;

	case 0x04:
		// the video display should now use this flag to display the shadow RAM memory
		m_vdusel = BIT(data, 7);
		setvideoshadow(m_vdusel);
		break;
	}

	// pass ROMSEL to internal expansion board
	if (m_internal && m_internal->overrides_rom())
		m_internal->romsel_w(offset, data);
}

uint8_t bbcbp_state::paged_r(offs_t offset)
{
	uint8_t data;

	if (m_internal && m_internal->overrides_rom())
	{
		data = m_internal->paged_r(offset);
	}
	else
	{
		// 32K sockets
		if (m_rom[m_romsel & 0x0e] && m_rom[m_romsel & 0x0e]->present())
			data = m_rom[m_romsel & 0x0e]->read(offset | (m_romsel & 0x01) << 14);
		else
			data = m_region_rom->base()[offset + (m_romsel << 14)];
	}

	return data;
}

void bbcbp_state::paged_w(offs_t offset, uint8_t data)
{
	if (m_internal && m_internal->overrides_rom())
	{
		m_internal->paged_w(offset, data);
	}
	else
	{
		// 32K sockets
		if (m_rom[m_romsel & 0x0e])
			m_rom[m_romsel & 0x0e]->write(offset | (m_romsel & 0x01) << 14, data);
	}
}


uint8_t bbcbp_state::sysvia_pa_r()
{
	update_sdb();

	return m_sdb;
}

void bbcbp_state::sysvia_pa_w(uint8_t data)
{
	m_sdb = data;

	update_sdb();
}


void bbcbp_state::update_sdb()
{
	uint8_t const latch = m_latch->output_state();

	// sound
	if (!BIT(latch, 0))
		m_sn->write(m_sdb);

	// speech
	if (m_vsp)
	{
		m_vsp->combined_rsq_wsq_w(bitswap<2>(latch, 1, 2));
		switch (bitswap<2>(~latch, 1, 2))
		{
		case tms5200_device::RS:
			m_sdb = m_vsp->status_r();
			break;
		case tms5200_device::WS:
			m_vsp->data_w(m_sdb);
			break;
		}
	}

	// keyboard
	m_sdb = m_kbd->read(m_sdb);
}


uint8_t bbcbp_state::sysvia_pb_r()
{
	uint8_t data = 0xff;

	if (m_analog)
	{
		data &= ~0x30;
		data |= m_analog->pb_r();
	}

	if (m_vsp)
	{
		data &= ~0xc0;
		data |= m_vsp->intq_r() << 6;
		data |= m_vsp->readyq_r() << 7;
	}

	return data;
}

void bbcbp_state::sysvia_pb_w(uint8_t data)
{
	m_latch->write_nibble_d3(data);

	if (m_analog)
	{
		m_analog->pb_w(data & 0x30);
	}

	update_sdb();
}


void bbcbp_state::drive_control_w(uint8_t data)
{
	// Bit       Meaning
	// -----------------
	// 7,6       Not used
	//  5        Reset drive controller chip. (0 = reset controller, 1 = no reset)
	//  4        Interrupt Enable (0 = enable int, 1 = disable int)
	//  3        Double density select (0 = double, 1 = single)
	//  2        Side select (0 = side 0, 1 = side 1)
	//  1        Drive select 1
	//  0        Drive select 0

	floppy_image_device *floppy = nullptr;

	// bit 0, 1: drive select
	if (BIT(data, 0)) floppy = m_wdfdc->subdevice<floppy_connector>("0")->get_device();
	if (BIT(data, 1)) floppy = m_wdfdc->subdevice<floppy_connector>("1")->get_device();
	m_wdfdc->set_floppy(floppy);

	// bit 2: side select
	if (floppy)
		floppy->ss_w(BIT(data, 2));

	// bit 3: density
	m_wdfdc->dden_w(BIT(data, 3));

	// bit 4: disable NMI (S5 wire link not fitted)

	// bit 5: reset
	m_wdfdc->mr_w(BIT(data, 5));
}


void bbcbp_state::trigger_reset(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? CLEAR_LINE : ASSERT_LINE);

	if (!state)
	{
		if (m_uservia) m_uservia->reset();
		if (m_adlc) m_adlc->reset();
		if (m_wdfdc) m_wdfdc->reset();
		if (m_1mhzbus) m_1mhzbus->reset();
		if (m_tube) m_tube->reset();
		if (m_internal) m_internal->reset();
	}
}


static INPUT_PORTS_START(bbc_statid)
	PORT_START("STATID")
	PORT_DIPNAME(0xff, 0xfe, "Econet ID") PORT_DIPLOCATION("S23:1,2,3,4,5,6,7,8")
	PORT_DIPSETTING(   0x00,   "0" )    PORT_DIPSETTING(   0x01,   "1" )    PORT_DIPSETTING(   0x02,   "2" )    PORT_DIPSETTING(   0x03,   "3" )    PORT_DIPSETTING(   0x04,   "4" )
	PORT_DIPSETTING(   0x05,   "5" )    PORT_DIPSETTING(   0x06,   "6" )    PORT_DIPSETTING(   0x07,   "7" )    PORT_DIPSETTING(   0x08,   "8" )    PORT_DIPSETTING(   0x09,   "9" )
	PORT_DIPSETTING(   0x0a,  "10" )    PORT_DIPSETTING(   0x0b,  "11" )    PORT_DIPSETTING(   0x0c,  "12" )    PORT_DIPSETTING(   0x0d,  "13" )    PORT_DIPSETTING(   0x0e,  "14" )
	PORT_DIPSETTING(   0x0f,  "15" )    PORT_DIPSETTING(   0x10,  "16" )    PORT_DIPSETTING(   0x11,  "17" )    PORT_DIPSETTING(   0x12,  "18" )    PORT_DIPSETTING(   0x13,  "19" )
	PORT_DIPSETTING(   0x14,  "20" )    PORT_DIPSETTING(   0x15,  "21" )    PORT_DIPSETTING(   0x16,  "22" )    PORT_DIPSETTING(   0x17,  "23" )    PORT_DIPSETTING(   0x18,  "24" )
	PORT_DIPSETTING(   0x19,  "25" )    PORT_DIPSETTING(   0x1a,  "26" )    PORT_DIPSETTING(   0x1b,  "27" )    PORT_DIPSETTING(   0x1c,  "28" )    PORT_DIPSETTING(   0x1d,  "29" )
	PORT_DIPSETTING(   0x1e,  "30" )    PORT_DIPSETTING(   0x1f,  "31" )    PORT_DIPSETTING(   0x20,  "32" )    PORT_DIPSETTING(   0x21,  "33" )    PORT_DIPSETTING(   0x22,  "34" )
	PORT_DIPSETTING(   0x23,  "35" )    PORT_DIPSETTING(   0x24,  "36" )    PORT_DIPSETTING(   0x25,  "37" )    PORT_DIPSETTING(   0x26,  "38" )    PORT_DIPSETTING(   0x27,  "39" )
	PORT_DIPSETTING(   0x28,  "40" )    PORT_DIPSETTING(   0x29,  "41" )    PORT_DIPSETTING(   0x2a,  "42" )    PORT_DIPSETTING(   0x2b,  "43" )    PORT_DIPSETTING(   0x2c,  "44" )
	PORT_DIPSETTING(   0x2d,  "45" )    PORT_DIPSETTING(   0x2e,  "46" )    PORT_DIPSETTING(   0x2f,  "47" )    PORT_DIPSETTING(   0x30,  "48" )    PORT_DIPSETTING(   0x31,  "49" )
	PORT_DIPSETTING(   0x32,  "50" )    PORT_DIPSETTING(   0x33,  "51" )    PORT_DIPSETTING(   0x34,  "52" )    PORT_DIPSETTING(   0x35,  "53" )    PORT_DIPSETTING(   0x36,  "54" )
	PORT_DIPSETTING(   0x37,  "55" )    PORT_DIPSETTING(   0x38,  "56" )    PORT_DIPSETTING(   0x39,  "57" )    PORT_DIPSETTING(   0x3a,  "58" )    PORT_DIPSETTING(   0x3b,  "59" )
	PORT_DIPSETTING(   0x3c,  "60" )    PORT_DIPSETTING(   0x3d,  "61" )    PORT_DIPSETTING(   0x3e,  "62" )    PORT_DIPSETTING(   0x3f,  "63" )    PORT_DIPSETTING(   0x40,  "64" )
	PORT_DIPSETTING(   0x41,  "65" )    PORT_DIPSETTING(   0x42,  "66" )    PORT_DIPSETTING(   0x43,  "67" )    PORT_DIPSETTING(   0x44,  "68" )    PORT_DIPSETTING(   0x45,  "69" )
	PORT_DIPSETTING(   0x46,  "70" )    PORT_DIPSETTING(   0x47,  "71" )    PORT_DIPSETTING(   0x48,  "72" )    PORT_DIPSETTING(   0x49,  "73" )    PORT_DIPSETTING(   0x4a,  "74" )
	PORT_DIPSETTING(   0x4b,  "75" )    PORT_DIPSETTING(   0x4c,  "76" )    PORT_DIPSETTING(   0x4d,  "77" )    PORT_DIPSETTING(   0x4e,  "78" )    PORT_DIPSETTING(   0x4f,  "79" )
	PORT_DIPSETTING(   0x50,  "80" )    PORT_DIPSETTING(   0x51,  "81" )    PORT_DIPSETTING(   0x52,  "82" )    PORT_DIPSETTING(   0x53,  "83" )    PORT_DIPSETTING(   0x54,  "84" )
	PORT_DIPSETTING(   0x55,  "85" )    PORT_DIPSETTING(   0x56,  "86" )    PORT_DIPSETTING(   0x57,  "87" )    PORT_DIPSETTING(   0x58,  "88" )    PORT_DIPSETTING(   0x59,  "89" )
	PORT_DIPSETTING(   0x5a,  "90" )    PORT_DIPSETTING(   0x5b,  "91" )    PORT_DIPSETTING(   0x5c,  "92" )    PORT_DIPSETTING(   0x5d,  "93" )    PORT_DIPSETTING(   0x5e,  "94" )
	PORT_DIPSETTING(   0x5f,  "95" )    PORT_DIPSETTING(   0x60,  "96" )    PORT_DIPSETTING(   0x61,  "97" )    PORT_DIPSETTING(   0x62,  "98" )    PORT_DIPSETTING(   0x63,  "99" )
	PORT_DIPSETTING(   0x64, "100" )    PORT_DIPSETTING(   0x65, "101" )    PORT_DIPSETTING(   0x66, "102" )    PORT_DIPSETTING(   0x67, "103" )    PORT_DIPSETTING(   0x68, "104" )
	PORT_DIPSETTING(   0x69, "105" )    PORT_DIPSETTING(   0x6a, "106" )    PORT_DIPSETTING(   0x6b, "107" )    PORT_DIPSETTING(   0x6c, "108" )    PORT_DIPSETTING(   0x6d, "109" )
	PORT_DIPSETTING(   0x6e, "110" )    PORT_DIPSETTING(   0x6f, "111" )    PORT_DIPSETTING(   0x70, "112" )    PORT_DIPSETTING(   0x71, "113" )    PORT_DIPSETTING(   0x72, "114" )
	PORT_DIPSETTING(   0x73, "115" )    PORT_DIPSETTING(   0x74, "116" )    PORT_DIPSETTING(   0x75, "117" )    PORT_DIPSETTING(   0x76, "118" )    PORT_DIPSETTING(   0x77, "119" )
	PORT_DIPSETTING(   0x78, "120" )    PORT_DIPSETTING(   0x79, "121" )    PORT_DIPSETTING(   0x7a, "122" )    PORT_DIPSETTING(   0x7b, "123" )    PORT_DIPSETTING(   0x7c, "124" )
	PORT_DIPSETTING(   0x7d, "125" )    PORT_DIPSETTING(   0x7e, "126" )    PORT_DIPSETTING(   0x7f, "127" )    PORT_DIPSETTING(   0x80, "128" )    PORT_DIPSETTING(   0x81, "129" )
	PORT_DIPSETTING(   0x82, "130" )    PORT_DIPSETTING(   0x83, "131" )    PORT_DIPSETTING(   0x84, "132" )    PORT_DIPSETTING(   0x85, "133" )    PORT_DIPSETTING(   0x86, "134" )
	PORT_DIPSETTING(   0x87, "135" )    PORT_DIPSETTING(   0x88, "136" )    PORT_DIPSETTING(   0x89, "137" )    PORT_DIPSETTING(   0x8a, "138" )    PORT_DIPSETTING(   0x8b, "139" )
	PORT_DIPSETTING(   0x8c, "140" )    PORT_DIPSETTING(   0x8d, "141" )    PORT_DIPSETTING(   0x8e, "142" )    PORT_DIPSETTING(   0x8f, "143" )    PORT_DIPSETTING(   0x90, "144" )
	PORT_DIPSETTING(   0x91, "145" )    PORT_DIPSETTING(   0x92, "146" )    PORT_DIPSETTING(   0x93, "147" )    PORT_DIPSETTING(   0x94, "148" )    PORT_DIPSETTING(   0x95, "149" )
	PORT_DIPSETTING(   0x96, "150" )    PORT_DIPSETTING(   0x97, "151" )    PORT_DIPSETTING(   0x98, "152" )    PORT_DIPSETTING(   0x99, "153" )    PORT_DIPSETTING(   0x9a, "154" )
	PORT_DIPSETTING(   0x9b, "155" )    PORT_DIPSETTING(   0x9c, "156" )    PORT_DIPSETTING(   0x9d, "157" )    PORT_DIPSETTING(   0x9e, "158" )    PORT_DIPSETTING(   0x9f, "159" )
	PORT_DIPSETTING(   0xa0, "160" )    PORT_DIPSETTING(   0xa1, "161" )    PORT_DIPSETTING(   0xa2, "162" )    PORT_DIPSETTING(   0xa3, "163" )    PORT_DIPSETTING(   0xa4, "164" )
	PORT_DIPSETTING(   0xa5, "165" )    PORT_DIPSETTING(   0xa6, "166" )    PORT_DIPSETTING(   0xa7, "167" )    PORT_DIPSETTING(   0xa8, "168" )    PORT_DIPSETTING(   0xa9, "169" )
	PORT_DIPSETTING(   0xaa, "170" )    PORT_DIPSETTING(   0xab, "171" )    PORT_DIPSETTING(   0xac, "172" )    PORT_DIPSETTING(   0xad, "173" )    PORT_DIPSETTING(   0xae, "174" )
	PORT_DIPSETTING(   0xaf, "175" )    PORT_DIPSETTING(   0xb0, "176" )    PORT_DIPSETTING(   0xb1, "177" )    PORT_DIPSETTING(   0xb2, "178" )    PORT_DIPSETTING(   0xb3, "179" )
	PORT_DIPSETTING(   0xb4, "180" )    PORT_DIPSETTING(   0xb5, "181" )    PORT_DIPSETTING(   0xb6, "182" )    PORT_DIPSETTING(   0xb7, "183" )    PORT_DIPSETTING(   0xb8, "184" )
	PORT_DIPSETTING(   0xb9, "185" )    PORT_DIPSETTING(   0xba, "186" )    PORT_DIPSETTING(   0xbb, "187" )    PORT_DIPSETTING(   0xbc, "188" )    PORT_DIPSETTING(   0xbd, "189" )
	PORT_DIPSETTING(   0xbe, "190" )    PORT_DIPSETTING(   0xbf, "191" )    PORT_DIPSETTING(   0xc0, "192" )    PORT_DIPSETTING(   0xc1, "193" )    PORT_DIPSETTING(   0xc2, "194" )
	PORT_DIPSETTING(   0xc3, "195" )    PORT_DIPSETTING(   0xc4, "196" )    PORT_DIPSETTING(   0xc5, "197" )    PORT_DIPSETTING(   0xc6, "198" )    PORT_DIPSETTING(   0xc7, "199" )
	PORT_DIPSETTING(   0xc8, "200" )    PORT_DIPSETTING(   0xc9, "201" )    PORT_DIPSETTING(   0xca, "202" )    PORT_DIPSETTING(   0xcb, "203" )    PORT_DIPSETTING(   0xcc, "204" )
	PORT_DIPSETTING(   0xcd, "205" )    PORT_DIPSETTING(   0xce, "206" )    PORT_DIPSETTING(   0xcf, "207" )    PORT_DIPSETTING(   0xd0, "208" )    PORT_DIPSETTING(   0xd1, "209" )
	PORT_DIPSETTING(   0xd2, "210" )    PORT_DIPSETTING(   0xd3, "211" )    PORT_DIPSETTING(   0xd4, "212" )    PORT_DIPSETTING(   0xd5, "213" )    PORT_DIPSETTING(   0xd6, "214" )
	PORT_DIPSETTING(   0xd7, "215" )    PORT_DIPSETTING(   0xd8, "216" )    PORT_DIPSETTING(   0xd9, "217" )    PORT_DIPSETTING(   0xda, "218" )    PORT_DIPSETTING(   0xdb, "219" )
	PORT_DIPSETTING(   0xdc, "220" )    PORT_DIPSETTING(   0xdd, "221" )    PORT_DIPSETTING(   0xde, "222" )    PORT_DIPSETTING(   0xdf, "223" )    PORT_DIPSETTING(   0xe0, "224" )
	PORT_DIPSETTING(   0xe1, "225" )    PORT_DIPSETTING(   0xe2, "226" )    PORT_DIPSETTING(   0xe3, "227" )    PORT_DIPSETTING(   0xe4, "228" )    PORT_DIPSETTING(   0xe5, "229" )
	PORT_DIPSETTING(   0xe6, "230" )    PORT_DIPSETTING(   0xe7, "231" )    PORT_DIPSETTING(   0xe8, "232" )    PORT_DIPSETTING(   0xe9, "233" )    PORT_DIPSETTING(   0xea, "234" )
	PORT_DIPSETTING(   0xeb, "235" )    PORT_DIPSETTING(   0xec, "236" )    PORT_DIPSETTING(   0xed, "237" )    PORT_DIPSETTING(   0xee, "238" )    PORT_DIPSETTING(   0xef, "239" )
	PORT_DIPSETTING(   0xf0, "240" )    PORT_DIPSETTING(   0xf1, "241" )    PORT_DIPSETTING(   0xf2, "242" )    PORT_DIPSETTING(   0xf3, "243" )    PORT_DIPSETTING(   0xf4, "244" )
	PORT_DIPSETTING(   0xf5, "245" )    PORT_DIPSETTING(   0xf6, "246" )    PORT_DIPSETTING(   0xf7, "247" )    PORT_DIPSETTING(   0xf8, "248" )    PORT_DIPSETTING(   0xf9, "249" )
	PORT_DIPSETTING(   0xfa, "250" )    PORT_DIPSETTING(   0xfb, "251" )    PORT_DIPSETTING(   0xfc, "252" )    PORT_DIPSETTING(   0xfd, "253" )    PORT_DIPSETTING(   0xfe, "254" )
	PORT_DIPSETTING(   0xff, "255" )
INPUT_PORTS_END


static INPUT_PORTS_START(bbcbp)
	PORT_INCLUDE(bbc_config)
	PORT_INCLUDE(bbc_statid)
INPUT_PORTS_END

static INPUT_PORTS_START(abc)
	PORT_INCLUDE(bbc_statid)
INPUT_PORTS_END


static void bbc_floppies(device_slot_interface &device)
{
	device.option_add("525sssd", FLOPPY_525_SSSD);
	device.option_add("525sd",   FLOPPY_525_SD);
	device.option_add("525qd",   FLOPPY_525_QD);
	device.option_add("35dd",    FLOPPY_35_DD);
}


static const char *const bbc_sample_names[] =
{
	"*bbc",
	"motoroff",
	"motoron",
	nullptr
};


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

    BBC Model B+

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

void bbcbp_state::bbcbp(machine_config &config)
{
	M6512(config, m_maincpu, 16_MHz_XTAL / 8);
	m_maincpu->set_addrmap(AS_PROGRAM, &bbcbp_state::bbcbp_mem);
	m_maincpu->set_addrmap(AS_OPCODES, &bbcbp_state::bbcbp_fetch);
	config.set_perfect_quantum(m_maincpu);

	INPUT_MERGER_ANY_HIGH(config, m_irqs).output_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);
	m_irqs->output_handler().append(m_internal, FUNC(bbc_internal_slot_device::irq6502_w));

	RAM(config, m_ram).set_default_size("64K");

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(16_MHz_XTAL, 1024, 0, 640, 312, 0, 256);
	m_screen->set_screen_update("crtc", FUNC(hd6845s_device::screen_update));

	PALETTE(config, m_palette).set_entries(16);

	SAA5050(config, m_trom, 12_MHz_XTAL / 2);

	HD6845S(config, m_crtc, 16_MHz_XTAL / 8);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_reconfigure_callback(FUNC(bbcbp_state::crtc_reconfigure));
	m_crtc->set_update_row_callback(FUNC(bbcbp_state::crtc_update_row));
	m_crtc->out_de_callback().set(FUNC(bbcbp_state::de_changed));
	m_crtc->out_hsync_callback().set(FUNC(bbcbp_state::hsync_changed));
	m_crtc->out_vsync_callback().set(FUNC(bbcbp_state::vsync_changed));

	config.set_default_layout(layout_bbc);

	LS259(config, m_latch);
	m_latch->q_out_cb<3>().set(m_kbd, FUNC(bbc_kbd_device::write_kb_en));
	m_latch->q_out_cb<6>().set_output("capslock_led");
	m_latch->q_out_cb<7>().set_output("shiftlock_led");

	MOS6522(config, m_sysvia, 16_MHz_XTAL / 16);
	m_sysvia->readpa_handler().set(FUNC(bbcbp_state::sysvia_pa_r));
	m_sysvia->writepa_handler().set(FUNC(bbcbp_state::sysvia_pa_w));
	m_sysvia->readpb_handler().set(FUNC(bbcbp_state::sysvia_pb_r));
	m_sysvia->writepb_handler().set(FUNC(bbcbp_state::sysvia_pb_w));
	m_sysvia->cb2_handler().set([this](int state) { if (state) m_crtc->assert_light_pen_input(); });
	m_sysvia->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<0>));

	BBC_KBD(config, m_kbd, 16_MHz_XTAL / 16);
	m_kbd->ca2_handler().set(m_sysvia, FUNC(via6522_device::write_ca2));
	m_kbd->rst_handler().set(FUNC(bbcbp_state::trigger_reset));

	SPEAKER(config, "mono").front_center();

	SN76489A(config, m_sn, 16_MHz_XTAL / 4);
	m_sn->add_route(ALL_OUTPUTS, "mono", 1.0);

	TMS5220(config, m_vsp, 640000);
	m_vsp->add_route(ALL_OUTPUTS, "mono", 0.5);

	TMS6100(config, "vsm", 0);
	m_vsp->m0_cb().set("vsm", FUNC(tms6100_device::m0_w));
	m_vsp->m1_cb().set("vsm", FUNC(tms6100_device::m1_w));
	m_vsp->addr_cb().set("vsm", FUNC(tms6100_device::add_w));
	m_vsp->data_cb().set("vsm", FUNC(tms6100_device::data_line_r));
	m_vsp->romclk_cb().set("vsm", FUNC(tms6100_device::clk_w));

	SAMPLES(config, m_samples);
	m_samples->set_channels(1);
	m_samples->set_samples_names(bbc_sample_names);
	m_samples->add_route(ALL_OUTPUTS, "mono", 1.0);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(bbc_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED);
	m_cassette->set_interface("bbc_cass");

	acia6850_device &acia(ACIA6850(config, "acia"));
	acia.txd_handler().set("serproc", FUNC(acorn_serproc_device::write_txd));
	acia.rts_handler().set("serproc", FUNC(acorn_serproc_device::write_rtsi));
	acia.irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<2>));

	acorn_serproc_device &serproc(ACORN_SERPROC(config, "serproc", 16_MHz_XTAL / 13));
	serproc.rxc_handler().set("acia", FUNC(acia6850_device::write_rxc));
	serproc.txc_handler().set("acia", FUNC(acia6850_device::write_txc));
	serproc.rxd_handler().set("acia", FUNC(acia6850_device::write_rxd));
	serproc.dcd_handler().set("acia", FUNC(acia6850_device::write_dcd));
	serproc.ctso_handler().set("acia", FUNC(acia6850_device::write_cts));
	serproc.dout_handler().set("rs423", FUNC(rs232_port_device::write_txd));
	serproc.rtso_handler().set("rs423", FUNC(rs232_port_device::write_rts));
	serproc.casmo_handler().set(FUNC(bbcbp_state::cassette_motor));
	serproc.casin_handler("cassette", FUNC(cassette_image_device::input));
	serproc.casout_handler("cassette", FUNC(cassette_image_device::output));

	rs232_port_device &rs423(RS232_PORT(config, "rs423", default_rs232_devices, nullptr));
	rs423.rxd_handler().set("serproc", FUNC(acorn_serproc_device::write_din));
	rs423.cts_handler().set("serproc", FUNC(acorn_serproc_device::write_ctsi));

	MOS6522(config, m_uservia, 16_MHz_XTAL / 16);
	m_uservia->writepa_handler().set("cent_data_out", FUNC(output_latch_device::write));
	m_uservia->readpb_handler().set(m_userport, FUNC(bbc_userport_slot_device::pb_r));
	m_uservia->writepb_handler().set(m_userport, FUNC(bbc_userport_slot_device::pb_w));
	m_uservia->writepb_handler().append(m_internal, FUNC(bbc_internal_slot_device::latch_fe60_w));
	m_uservia->ca2_handler().set("printer", FUNC(centronics_device::write_strobe));
	m_uservia->cb1_handler().set(m_userport, FUNC(bbc_userport_slot_device::write_cb1));
	m_uservia->cb2_handler().set(m_userport, FUNC(bbc_userport_slot_device::write_cb2));
	m_uservia->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<1>));

	centronics_device &centronics(CENTRONICS(config, "printer", centronics_devices, "printer"));
	centronics.ack_handler().set(m_uservia, FUNC(via6522_device::write_ca1));
	output_latch_device &latch(OUTPUT_LATCH(config, "cent_data_out"));
	centronics.set_output_latch(latch);

	upd7002_device &upd7002(UPD7002(config, "upd7002", 16_MHz_XTAL / 16));
	upd7002.get_analogue_callback().set(m_analog, FUNC(bbc_analogue_slot_device::ch_r));
	upd7002.eoc_callback().set(m_sysvia, FUNC(via6522_device::write_cb1));

	BBC_ANALOGUE_SLOT(config, m_analog, bbc_analogue_devices, nullptr);
	m_analog->lpstb_handler().set(m_sysvia, FUNC(via6522_device::write_cb2));
	m_analog->lpstb_handler().append([this](int state) { if (state) m_crtc->assert_light_pen_input(); });

	WD1770(config, m_wdfdc, 16_MHz_XTAL / 2);
	m_wdfdc->intrq_wr_callback().set(FUNC(bbcbp_state::fdc_intrq_w));
	m_wdfdc->drq_wr_callback().set(FUNC(bbcbp_state::fdc_drq_w));

	FLOPPY_CONNECTOR(config, "wdfdc:0", bbc_floppies, "525qd", bbc_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "wdfdc:1", bbc_floppies, "525qd", bbc_state::floppy_formats).enable_sound(true);

	MC6854(config, m_adlc);
	m_adlc->out_txd_cb().set("network", FUNC(econet_device::host_data_w));
	m_adlc->out_irq_cb().set(FUNC(bbcbp_state::adlc_irq_w));
	//m_adlc->out_rts_cb().

	econet_device &econet(ECONET(config, "network", 0));
	econet.clk_wr_callback().set(m_adlc, FUNC(mc6854_device::txc_w));
	econet.clk_wr_callback().append(m_adlc, FUNC(mc6854_device::rxc_w));
	econet.data_wr_callback().set(m_adlc, FUNC(mc6854_device::set_rx));
	ECONET_SLOT(config, "econet", "network", econet_devices);

	BBC_1MHZBUS_SLOT(config, m_1mhzbus, 16_MHz_XTAL / 16, bbc_1mhzbus_devices, nullptr);
	m_1mhzbus->add_route(ALL_OUTPUTS, "mono", 1.0);
	m_1mhzbus->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<3>));
	m_1mhzbus->nmi_handler().set(FUNC(bbcbp_state::bus_nmi_w));

	BBC_TUBE_SLOT(config, m_tube, bbc_tube_devices, nullptr);
	m_tube->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<4>));

	BBC_USERPORT_SLOT(config, m_userport, bbc_userport_devices, nullptr);
	m_userport->cb1_handler().set(m_uservia, FUNC(via6522_device::write_cb1));
	m_userport->cb2_handler().set(m_uservia, FUNC(via6522_device::write_cb2));

	BBC_ROMSLOT32(config, m_rom[0x02], bbc_rom_devices, nullptr); // IC35 32K socket
	BBC_ROMSLOT32(config, m_rom[0x04], bbc_rom_devices, nullptr); // IC44 32K socket
	BBC_ROMSLOT32(config, m_rom[0x06], bbc_rom_devices, nullptr); // IC57 32K socket
	BBC_ROMSLOT32(config, m_rom[0x08], bbc_rom_devices, nullptr); // IC62 32K socket
	BBC_ROMSLOT32(config, m_rom[0x0a], bbc_rom_devices, nullptr); // IC68 32K socket

	BBC_INTERNAL_SLOT(config, m_internal, 16_MHz_XTAL, bbcbp_internal_devices, nullptr);
	m_internal->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<5>));
	m_internal->nmi_handler().set(FUNC(bbcbp_state::bus_nmi_w));

	SOFTWARE_LIST(config, "cass_ls").set_original("bbc_cass").set_filter("A,B");
	SOFTWARE_LIST(config, "rom_ls").set_original("bbc_rom").set_filter("B+");
	SOFTWARE_LIST(config, "flop_ls_b").set_original("bbcb_flop");
	SOFTWARE_LIST(config, "flop_ls_b_orig").set_original("bbcb_flop_orig");
	SOFTWARE_LIST(config, "hdd_ls").set_original("bbc_hdd").set_filter("B");
}


void bbcbp_state::bbcbp128(machine_config &config)
{
	bbcbp(config);

	m_ram->set_default_size("128K");

	BBC_ROMSLOT32(config, m_rom[0x00], bbc_rom_devices, "ram").set_fixed_ram(true);
	BBC_ROMSLOT32(config, m_rom[0x0c], bbc_rom_devices, "ram").set_fixed_ram(true);
}


void bbcbp_state::cfa3000bp(machine_config &config)
{
	bbcbp(config);

	// no floppy drives
	m_wdfdc->subdevice<floppy_connector>("0")->set_default_option(nullptr);
	m_wdfdc->subdevice<floppy_connector>("1")->set_default_option(nullptr);

	// keyboard
	m_userport->set_default_option("cfa3000kbd").set_fixed(true);

	// option board
	m_1mhzbus->set_default_option("cfa3000opt").set_fixed(true);

	// analogue dials/sensors
	m_analog->set_default_option("cfa3000a").set_fixed(true);

	config.device_remove("cass_ls");
	config.device_remove("flop_ls_b");
	config.device_remove("flop_ls_b_orig");
}


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

    Acorn Business Computers

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

void bbcbp_state::abc110(machine_config &config)
{
	bbcbp(config);

	ABC_KBD(config.replace(), m_kbd, 16_MHz_XTAL / 16);
	m_kbd->ca2_handler().set(m_sysvia, FUNC(via6522_device::write_ca2));
	m_kbd->rst_handler().set(FUNC(bbcbp_state::trigger_reset));

	// single floppy drive
	m_wdfdc->subdevice<floppy_connector>("1")->set_default_option(nullptr);

	// Acorn Z80 co-processor
	m_tube->set_default_option("z80w").set_fixed(true);

	// Acorn Winchester Disc 10MB
	m_1mhzbus->set_default_option("awhd").set_fixed(true);
}


void bbcbp_state::acw443(machine_config &config)
{
	abc110(config);

	// 32016 co-processor
	m_tube->set_default_option("32016l").set_fixed(true);

	// Acorn Winchester Disc 20MB
	m_1mhzbus->set_default_option("awhd").set_fixed(true);

	SOFTWARE_LIST(config, "flop_ls_32016").set_original("bbc_flop_32016");
}


void bbcbp_state::abc310(machine_config &config)
{
	abc110(config);

	// Acorn 80286 co-processor
	m_tube->set_default_option("80286").set_fixed(true);

	// Acorn Winchester Disc 10MB
	m_1mhzbus->set_default_option("awhd").set_fixed(true);

	// Acorn Mouse
	m_userport->set_default_option("m512mouse");
}


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

    Econet X25 Gateway

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

void bbcbp_state::econx25(machine_config &config)
{
	bbcbp(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &bbcbp_state::econx25_mem);

	// Econet X25 Gateway co-processor
	m_tube->set_default_option("x25").set_fixed(true);

	// fdc and speech not fitted
	config.device_remove("wdfdc");
	config.device_remove("vsm");
	config.device_remove("vsp");

	subdevice<centronics_device>("printer")->set_default_option(nullptr);

	config.device_remove("cass_ls");
	config.device_remove("flop_ls_b");
	config.device_remove("flop_ls_b_orig");
}


ROM_START(bbcbp)
	// page 0  00000  SWRAM (B+ 128K only)           // page 8  20000  IC62 32K IN PAGE 9
	// page 1  04000  SWRAM (B+ 128K only)           // page 9  24000  IC62
	// page 2  08000  IC35 32K IN PAGE 3             // page 10 28000  IC68 32K IN PAGE 11
	// page 3  0c000  IC35                           // page 11 2c000  IC68
	// page 4  10000  IC44 32K IN PAGE 5             // page 12 30000  SWRAM (B+ 128K only)
	// page 5  14000  IC44 ADFS                      // page 13 34000  SWRAM (B+ 128K only)
	// page 6  18000  IC57 32K IN PAGE 7             // page 14 38000  IC71 32K IN PAGE 15
	// page 7  1c000  IC57 DDFS                      // page 15 3C000  IC71 BASIC
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("bpos2.ic71",  0x3c000, 0x8000, CRC(9f356396) SHA1(ea7d3a7e3ee1ecfaa1483af994048057362b01f2))
	//ROM_LOAD("adfs130.rom", 0x14000, 0x4000, CRC(d3855588) SHA1(301fd05c475a629c4bec70510d4507256a5b00d8)) // not fitted as standard
	ROM_LOAD("ddfs223.rom", 0x1c000, 0x4000, CRC(7891f9b7) SHA1(0d7ed0b0b3852cb61970ada1993244f2896896aa))

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)

	ROM_REGION(0x40000, "vsm", ROMREGION_ERASE00)
	ROM_LOAD("cm62024.bin", 0x3c000, 0x4000, CRC(98e1bf9e) SHA1(b369809275cb67dfd8a749265e91adb2d2558ae6))
ROM_END


#define rom_bbcbp128 rom_bbcbp


ROM_START(abc110)
	// page 0  00000                                 // page 8  20000  IC62 32K IN PAGE 9
	// page 1  04000  IC71 selectable with link S13  // page 9  24000  IC62
	// page 2  08000  IC35 32K IN PAGE 3             // page 10 28000  IC68 32K IN PAGE 11
	// page 3  0c000  IC35                           // page 11 2c000  IC68
	// page 4  10000  IC44 32K IN PAGE 5             // page 12 30000
	// page 5  14000  IC44 DDFS                      // page 13 34000
	// page 6  18000  IC57 32K IN PAGE 7             // page 14 38000
	// page 7  1c000  IC57 ADFS                      // page 15 3C000  IC71 BASIC
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "200", "MOS2.00")
	ROMX_LOAD("mos200.rom",     0x40000, 0x4000, CRC(5e88f994) SHA1(76235ff15d736f5def338f73ac7497c41b916505), ROM_BIOS(0))
	ROMX_LOAD("basic200.rom",   0x3c000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "123stor", "MOS1.23 + ViewStore")
	ROMX_LOAD("mos123stor.rom", 0x3c000, 0x8000, CRC(4e84f452) SHA1(145ee54f04b3eb4d0e5afaabe21915be48db3c54), ROM_BIOS(1)) // rom 15 3C000 ViewStore
	ROM_SYSTEM_BIOS(2, "123", "MOS1.23")
	ROMX_LOAD("mos123.rom",     0x40000, 0x4000, CRC(90d31d08) SHA1(42a01892cf8bd2ada4db1c8b36aff80c85eb5dcb), ROM_BIOS(2))
	ROMX_LOAD("basic200.rom",   0x3c000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281), ROM_BIOS(2))

	ROM_SYSTEM_BIOS(3, "120", "MOS1.20")
	ROMX_LOAD("mos120.rom",     0x40000, 0x4000, CRC(0a1e83a0) SHA1(21dc3a94eef7c003b194686730fb461779f44925), ROM_BIOS(3))
	ROMX_LOAD("basic200.rom",   0x3c000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281), ROM_BIOS(3))

	//ROM_LOAD("ddfs223.rom",   0x14000, 0x4000, CRC(7891f9b7) SHA1(0d7ed0b0b3852cb61970ada1993244f2896896aa))
	ROM_LOAD("acwddfs225.rom",  0x14000, 0x4000, CRC(7d0f9016) SHA1(bdfe44c79e18142d747436627e71a362a04cf746))
	ROM_LOAD("adfs130.rom",     0x1c000, 0x4000, CRC(d3855588) SHA1(301fd05c475a629c4bec70510d4507256a5b00d8))

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)

	ROM_REGION(0x40000, "vsm", ROMREGION_ERASE00)
	ROM_LOAD("cm62024.bin", 0x3c000, 0x4000, CRC(98e1bf9e) SHA1(b369809275cb67dfd8a749265e91adb2d2558ae6))
ROM_END


#define rom_abc310 rom_abc110


ROM_START(acw443)
	// page 0  00000                                 // page 8  20000  IC62 32K IN PAGE 9
	// page 1  04000  IC71 selectable with link S13  // page 9  24000  IC62 ADFS
	// page 2  08000  IC35 32K IN PAGE 3             // page 10 28000  IC68 BASIC
	// page 3  0c000  IC35 DNFS                      // page 11 2c000  IC68 unused OS?
	// page 4  10000  IC44 32K IN PAGE 5             // page 12 30000
	// page 5  14000  IC44 ACW DFS                   // page 13 34000
	// page 6  18000  IC57 32K IN PAGE 7             // page 14 38000
	// page 7  1c000  IC57 TERMINAL                  // page 15 3C000  IC71 selectable with link S13
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "210", "MOS2.10")
	ROMX_LOAD("acwmos210.rom",     0x40000, 0x4000, CRC(168d6753) SHA1(dcd01d8f5f6e0cd92ae626ca52a3db71abf5d282), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "200", "MOS2.00")
	ROMX_LOAD("mos200.rom",        0x40000, 0x4000, CRC(5e88f994) SHA1(76235ff15d736f5def338f73ac7497c41b916505), ROM_BIOS(1))

	ROM_LOAD("dnfs120-201666.rom", 0x0c000, 0x4000, CRC(8ccd2157) SHA1(7e3c536baeae84d6498a14e8405319e01ee78232))
	ROM_LOAD("acwddfs225.rom",     0x14000, 0x4000, CRC(7d0f9016) SHA1(bdfe44c79e18142d747436627e71a362a04cf746))
	ROM_LOAD("acwterminal.rom",    0x1c000, 0x4000, CRC(81afaeb9) SHA1(6618ed9158776b4b8aa030957bd19ba77e4a993c))
	ROM_LOAD("adfs130.rom",        0x24000, 0x4000, CRC(d3855588) SHA1(301fd05c475a629c4bec70510d4507256a5b00d8))
	ROM_LOAD("basic200.rom",       0x28000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281))

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)

	ROM_REGION(0x40000, "vsm", ROMREGION_ERASE00)
	ROM_LOAD("cm62024.bin", 0x3c000, 0x4000, CRC(98e1bf9e) SHA1(b369809275cb67dfd8a749265e91adb2d2558ae6))
ROM_END


ROM_START(econx25)
	// page 0  00000                                 // page 8  20000  IC62 BASIC
	// page 1  04000  IC71 selectable with link S13  // page 9  24000  IC62 unused OS
	// page 2  08000  IC35 32K IN PAGE 3             // page 10 28000  IC68 32K IN PAGE 11
	// page 3  0c000  IC35                           // page 11 2c000  IC68
	// page 4  10000  IC44 32K IN PAGE 5             // page 12 30000
	// page 5  14000  IC44                           // page 13 34000
	// page 6  18000  IC57 32K IN PAGE 7             // page 14 38000
	// page 7  1c000  IC57 ANFS                      // page 15 3C000  IC71 selectable with link S13
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("0246,201_01_x25os.rom", 0x40000, 0x4000, CRC(8b652337) SHA1(6a5c7ace255c8ac96c983d5ba67084fbd71ff61e))
	ROM_LOAD("2201,248_03_anfs.rom",  0x1c000, 0x4000, CRC(744a60a7) SHA1(c733b108d74cf3b1c5de395335236800a7c9c0d8))
	ROM_LOAD("0201,241_01_bpos2.rom", 0x20000, 0x8000, CRC(9f356396) SHA1(ea7d3a7e3ee1ecfaa1483af994048057362b01f2))
	// X25 TSI is in IC37 which is supposed to take a speech PHROM, so not sure where this is mapped
	ROM_LOAD("0246,215_02_x25tsi_v0.51.rom", 0x0c000, 0x4000, CRC(71dd84e4) SHA1(bbfa892fdcc6f753dda5134ecb97cc7c42b959c2))

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)
ROM_END


ROM_START(cfa3000bp)
	// page 0  00000  SWRAM (B+ 128K only)           // page 8  20000  IC62 32K IN PAGE 9
	// page 1  04000  SWRAM (B+ 128K only)           // page 9  24000  IC62
	// page 2  08000  IC35 32K IN PAGE 3             // page 10 28000  IC68 32K IN PAGE 11
	// page 3  0c000  IC35                           // page 11 2c000  IC68
	// page 4  10000  IC44 32K IN PAGE 5             // page 12 30000  SWRAM (B+ 128K only)
	// page 5  14000  IC44                           // page 13 34000  SWRAM (B+ 128K only)
	// page 6  18000  IC57 32K IN PAGE 7             // page 14 38000  IC71 32K IN PAGE 15
	// page 7  1c000  IC57                           // page 15 3C000  IC71 BASIC
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("bpos2.ic71",     0x3c000, 0x8000, CRC(9f356396) SHA1(ea7d3a7e3ee1ecfaa1483af994048057362b01f2))
	ROM_SYSTEM_BIOS(0, "40", "Issue 4")
	ROMX_LOAD("cfa3000_3.rom", 0x14000, 0x4000, CRC(4f246cd5) SHA1(6ba9625248c585deed5c651a889eecc86384a60d), ROM_BIOS(0))
	ROMX_LOAD("cfa3000_4.rom", 0x1c000, 0x4000, CRC(ca0e30fd) SHA1(abddc7ba6d16855ebda2ef55fe8662bc545ae755), ROM_BIOS(0))
	ROMX_LOAD("cfa3000_s.rom", 0x24000, 0x4000, CRC(71fd4c8a) SHA1(5bad70ee55403bc0191f6b189c9b6e5effdbca4c), ROM_BIOS(0))

	// link S13 set for BASIC to take low priority ROM numbers 0/1
	ROM_COPY("rom", 0x3c000, 0x4000, 0x4000)
	ROM_FILL(0x3c000, 0x4000, 0xff)

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)

	ROM_REGION(0x40000, "vsm", ROMREGION_ERASE00)
	ROM_LOAD("cm62024.bin", 0x3c000, 0x4000, CRC(98e1bf9e) SHA1(b369809275cb67dfd8a749265e91adb2d2558ae6))
ROM_END


#define rom_ltmpbp rom_bbcbp

} // anonymous namespace


//    YEAR  NAME        PARENT  COMPAT MACHINE     INPUT    CLASS         INIT       COMPANY                        FULLNAME                              FLAGS
COMP( 1985, bbcbp,      0,      bbcb,  bbcbp,      bbcbp,   bbcbp_state,  init_bbc,  "Acorn Computers",             "BBC Micro Model B+ 64K",             MACHINE_IMPERFECT_GRAPHICS )
COMP( 1985, bbcbp128,   bbcbp,  0,     bbcbp128,   bbcbp,   bbcbp_state,  init_bbc,  "Acorn Computers",             "BBC Micro Model B+ 128K",            MACHINE_IMPERFECT_GRAPHICS )
COMP( 1985, abc110,     bbcbp,  0,     abc110,     abc,     bbcbp_state,  init_bbc,  "Acorn Computers",             "ABC 110",                            MACHINE_NOT_WORKING )
COMP( 1985, acw443,     bbcbp,  0,     acw443,     abc,     bbcbp_state,  init_bbc,  "Acorn Computers",             "ABC 210/Cambridge Workstation",      MACHINE_NOT_WORKING )
COMP( 1985, abc310,     bbcbp,  0,     abc310,     abc,     bbcbp_state,  init_bbc,  "Acorn Computers",             "ABC 310",                            MACHINE_NOT_WORKING )
COMP( 1986, econx25,    bbcbp,  0,     econx25,    bbcbp,   bbcbp_state,  init_bbc,  "Acorn Computers",             "Econet X25 Gateway",                 MACHINE_NOT_WORKING )

// Industrial
COMP( 1985, ltmpbp,     bbcbp,  0,     bbcbp,      abc,     bbcbp_state,  init_ltmp, "Lawrie T&M Ltd.",             "LTM Portable (B+)",                  MACHINE_IMPERFECT_GRAPHICS )
COMP( 198?, cfa3000bp,  bbcbp,  0,     cfa3000bp,  bbcbp,   bbcbp_state,  init_cfa,  "Tinsley Medical Instruments", "Henson CFA 3000 (B+)",               MACHINE_NOT_WORKING )



bbcm.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Gordon Jefferyes, Nigel Barnes
/******************************************************************************

    BBC Master Series

    AMB15 - Master 128
    ADB12 - Master Econet Terminal
    AVC12 - Master AIV (Domesday)
    ARM1  - ARM Evaluation System

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

#include "emu.h"
#include "bbc.h"
#include "acorn_serproc.h"
#include "bbc_kbd.h"

#include "bus/bbc/modem/modem.h"
#include "cpu/m6502/g65sc02.h"
#include "machine/mc146818.h"
#include "machine/wd_fdc.h"

#include "formats/uef_cas.h"
#include "formats/csw_cas.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "bbcm.lh"


namespace {

class bbcm_state : public bbc_state
{
public:
	bbcm_state(const machine_config &mconfig, device_type type, const char *tag)
		: bbc_state(mconfig, type, tag)
		, m_view_lynne(*this, "view_lynne")
		, m_view_hazel(*this, "view_hazel")
		, m_view_andy(*this, "view_andy")
		, m_view_1mhz(*this, "view_1mhz")
		, m_view_tst(*this, "view_tst")
		, m_kbd(*this, "kbd")
		, m_sn(*this, "sn")
		, m_rtc(*this, "rtc")
		, m_wdfdc(*this, "wdfdc")
		, m_adlc(*this, "mc6854")
		, m_modem(*this, "modem")
		, m_power_led(*this, "power_led")
	{ }

	void bbcm(machine_config &config);
	void bbcmt(machine_config &config);
	void bbcmet(machine_config &config);
	void bbcmaiv(machine_config &config);
	void bbcm512(machine_config &config);
	void bbcmarm(machine_config &config);
	void cfa3000(machine_config &config);
	void daisy(machine_config &config);
	void ht280(machine_config &config);
	void discmon(machine_config &config);
	void discmate(machine_config &config);
	void mpc800(machine_config &config);
	void mpc900(machine_config &config);
	void mpc900gx(machine_config &config);
	void se3010(machine_config &config);

	void init_se();

	//static void mpc_prisma_default(device_t *device);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	memory_view m_view_lynne;
	memory_view m_view_hazel;
	memory_view m_view_andy;
	memory_view m_view_1mhz;
	memory_view m_view_tst;
	required_device<bbc_kbd_device> m_kbd;
	optional_device<sn76489a_device> m_sn;
	required_device<mc146818_device> m_rtc;
	optional_device<wd1770_device> m_wdfdc;
	required_device<mc6854_device> m_adlc;
	optional_device<bbc_modem_slot_device> m_modem;
	output_finder<> m_power_led;

	void bbcm_fetch(address_map &map) ATTR_COLD;
	void bbcm_base(address_map &map) ATTR_COLD;
	void bbcm_mem(address_map &map) ATTR_COLD;
	void bbcm_io(address_map &map) ATTR_COLD;
	void bbcmet_mem(address_map &map) ATTR_COLD;
	void bbcmet_io(address_map &map) ATTR_COLD;

	void trigger_reset(int state);

	uint8_t fetch_r(offs_t offset);
	uint8_t acccon_r();
	void acccon_w(uint8_t data);
	uint8_t romsel_r();
	void romsel_w(offs_t offset, uint8_t data);
	uint8_t paged_r(offs_t offset);
	void paged_w(offs_t offset, uint8_t data);
	uint8_t fred_r(offs_t offset);
	void fred_w(offs_t offset, uint8_t data);
	uint8_t jim_r(offs_t offset);
	void jim_w(offs_t offset, uint8_t data);
	uint8_t tube_r(offs_t offset);
	void tube_w(offs_t offset, uint8_t data);
	void drive_control_w(uint8_t data);

	uint8_t sysvia_pa_r();
	void sysvia_pa_w(uint8_t data);
	void update_sdb();
	uint8_t sysvia_pb_r();
	void sysvia_pb_w(uint8_t data);

	int m_mc146818_as = 0;
	int m_mc146818_ce = 0;

	uint8_t m_sdb = 0x00;
	uint8_t m_acccon = 0x00;

	enum
	{
		ACCCON_D = 0,
		ACCCON_E,
		ACCCON_X,
		ACCCON_Y,
		ACCCON_ITU,
		ACCCON_IFJ,
		ACCCON_TST,
		ACCCON_IRR
	};
};


void bbcm_state::machine_start()
{
	bbc_state::machine_start();

	m_power_led.resolve();

	save_item(NAME(m_acccon));
	save_item(NAME(m_sdb));
}


void bbcm_state::machine_reset()
{
	m_power_led = 0;
}


void bbcm_state::bbcm_fetch(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(bbcm_state::fetch_r));
}


void bbcm_state::bbcm_base(address_map &map)
{
	map(0x0000, 0x7fff).rw(m_ram, FUNC(ram_device::read), FUNC(ram_device::write));                                      //    0000-7FFF                 Regular RAM
	map(0x3000, 0x7fff).view(m_view_lynne);                                                                              //    3000-7FFF                 20K Shadow RAM LYNNE
	m_view_lynne[0](0x3000, 0x7fff).lr8(NAME([this] (offs_t offset) { return m_ram->pointer()[offset + 0xb000]; }));
	m_view_lynne[0](0x3000, 0x7fff).lw8(NAME([this] (offs_t offset, uint8_t data) { m_ram->pointer()[offset + 0xb000] = data; }));
	map(0x8000, 0xbfff).rw(FUNC(bbcm_state::paged_r), FUNC(bbcm_state::paged_w));                                        //    8000-8FFF                 Paged ROM/RAM
	map(0x8000, 0x8fff).view(m_view_andy);                                                                               //    8000-8FFF                 4K RAM ANDY
	m_view_andy[0](0x8000, 0x8fff).lr8(NAME([this] (offs_t offset) { return m_ram->pointer()[offset + 0x8000]; }));
	m_view_andy[0](0x8000, 0x8fff).lw8(NAME([this] (offs_t offset, uint8_t data) { m_ram->pointer()[offset + 0x8000] = data; }));
	map(0xc000, 0xffff).rw(FUNC(bbcm_state::mos_r), FUNC(bbcm_state::mos_w));                                            //    C000-FFFF                 OS ROM
	map(0xc000, 0xdfff).view(m_view_hazel);                                                                              //    C000-DFFF                 8K RAM HAZEL
	m_view_hazel[0](0xc000, 0xdfff).lr8(NAME([this] (offs_t offset) { return m_ram->pointer()[offset + 0x9000]; }));
	m_view_hazel[0](0xc000, 0xdfff).lw8(NAME([this] (offs_t offset, uint8_t data) { m_ram->pointer()[offset + 0x9000] = data; }));
}

void bbcm_state::bbcm_mem(address_map &map)
{
	bbcm_base(map);
	map(0xfc00, 0xfeff).m(FUNC(bbcm_state::bbcm_io));                                                                    //    FC00-FFFF                 OS ROM or hardware IO
	map(0xfc00, 0xfeff).view(m_view_tst);
	m_view_tst[0](0xfc00, 0xfeff).m(FUNC(bbcm_state::bbcm_io));
	m_view_tst[0](0xfc00, 0xfeff).lr8(NAME([this](offs_t offset) { return mos_r(0x3c00 + offset); }));

}

void bbcm_state::bbcm_io(address_map &map)
{
	map(0x0000, 0x00ff).rw(FUNC(bbcm_state::fred_r), FUNC(bbcm_state::fred_w));                                          //    FC00-FCFF  Master         FRED Address Page
	map(0x0100, 0x01ff).rw(FUNC(bbcm_state::jim_r), FUNC(bbcm_state::jim_w));                                            //    FD00-FDFF  Master         JIM Address Page
	map(0x0200, 0x02ff).lr8(NAME([]() { return 0xfe; })).nopw();                                                         //    FE00-FEFF                 SHEILA Address Page
	map(0x0200, 0x0200).mirror(0x06).rw(m_crtc, FUNC(hd6845s_device::status_r), FUNC(hd6845s_device::address_w));        //    FE00-FE07  6845 CRTC      Video controller
	map(0x0201, 0x0201).mirror(0x06).rw(m_crtc, FUNC(hd6845s_device::register_r), FUNC(hd6845s_device::register_w));
	map(0x0208, 0x0209).mirror(0x06).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));              //    FE08-FE0F  6850 ACIA      Serial controller
	map(0x0210, 0x0217).rw("serproc", FUNC(acorn_serproc_device::read), FUNC(acorn_serproc_device::write));              //    FE10-FE17  Serial ULA     Serial system chip
	map(0x0218, 0x021b).mirror(0x04).rw("upd7002", FUNC(upd7002_device::read), FUNC(upd7002_device::write));             //    FE18-FE1F  uPD7002        Analogue to digital converter
	map(0x0220, 0x0223).w(FUNC(bbcm_state::video_ula_w));                                                                // W: FE20-FE23  Video ULA      Video system chip
	map(0x0224, 0x0227).w(FUNC(bbcm_state::drive_control_w));                                                            // W: FE24-FE27  FDC Latch      1770 Control latch
	map(0x0228, 0x022f).rw(m_wdfdc, FUNC(wd1770_device::read), FUNC(wd1770_device::write));                              //    FE28-FE2F  1770 FDC       Floppy disc controller
	map(0x0230, 0x0233).rw(FUNC(bbcm_state::romsel_r), FUNC(bbcm_state::romsel_w));                                      //    FE30-FE33  ROMSEL         ROM Select
	map(0x0234, 0x0237).rw(FUNC(bbcm_state::acccon_r), FUNC(bbcm_state::acccon_w));                                      //    FE34-FE37  ACCCON         ACCCON select register
	map(0x0238, 0x023b).lr8(NAME([this]() { econet_int_enable(0); return 0xfe; }));                                      // R: FE38-FE3B  INTOFF         ECONET Interrupt Off
	map(0x0238, 0x023b).lw8(NAME([this](uint8_t data) { econet_int_enable(0); }));                                       // W: FE38-FE3B  INTOFF         ECONET Interrupt Off
	map(0x023c, 0x023f).lr8(NAME([this]() { econet_int_enable(1); return 0xfe; }));                                      // R: FE3C-FE3F  INTON          ECONET Interrupt On
	map(0x023c, 0x023f).lw8(NAME([this](uint8_t data) { econet_int_enable(1); }));                                       // W: FE3C-FE3F  INTON          ECONET Interrupt On
	map(0x0240, 0x024f).mirror(0x10).m(m_sysvia, FUNC(via6522_device::map));                                             //    FE40-FE5F  6522 VIA       SYSTEM VIA
	map(0x0260, 0x026f).mirror(0x10).m(m_uservia, FUNC(via6522_device::map));                                            //    FE60-FE7F  6522 VIA       USER VIA
	map(0x0280, 0x028f).mirror(0x10).rw(m_modem, FUNC(bbc_modem_slot_device::read), FUNC(bbc_modem_slot_device::write)); //    FE80-FE9F  Int. Modem     Int. Modem
	map(0x02a0, 0x02a3).mirror(0x1c).rw(m_adlc, FUNC(mc6854_device::read), FUNC(mc6854_device::write));                  //    FEA0-FEBF  68B54 ADLC     ECONET controller
	map(0x02e0, 0x02ff).rw(FUNC(bbcm_state::tube_r), FUNC(bbcm_state::tube_w));                                          //    FEE0-FEFF  Tube ULA       Tube system interface
}


void bbcm_state::bbcmet_mem(address_map &map)
{
	bbcm_base(map);
	map(0xfc00, 0xfeff).m(FUNC(bbcm_state::bbcmet_io));                                                                  //    FC00-FFFF                 OS ROM or hardware IO
	map(0xfc00, 0xfeff).view(m_view_tst);
	m_view_tst[0](0xfc00, 0xfeff).m(FUNC(bbcm_state::bbcmet_io));
	m_view_tst[0](0xfc00, 0xfeff).lr8(NAME([this](offs_t offset) { return mos_r(0x3c00 + offset); }));
}

void bbcm_state::bbcmet_io(address_map &map)
{
	map(0x0000, 0x00ff).noprw();                                                                                         //    FC00-FCFF                 FRED Address Page
	map(0x0100, 0x01ff).noprw();                                                                                         //    FD00-FDFF                 JIM Address Page
	map(0x0200, 0x02ff).lr8(NAME([]() { return 0xfe; })).nopw();                                                         //    FE00-FEFF                 SHEILA Address Page
	map(0x0200, 0x0200).mirror(0x06).rw(m_crtc, FUNC(hd6845s_device::status_r), FUNC(hd6845s_device::address_w));        //    FE00-FE07  6845 CRTC      Video controller
	map(0x0201, 0x0201).mirror(0x06).rw(m_crtc, FUNC(hd6845s_device::register_r), FUNC(hd6845s_device::register_w));
	map(0x0220, 0x0223).w(FUNC(bbcm_state::video_ula_w));                                                                // W: FE20-FE23  Video ULA      Video system chip
	map(0x0230, 0x0233).rw(FUNC(bbcm_state::romsel_r), FUNC(bbcm_state::romsel_w));                                      // W: FE30-FE33  ROMSEL         ROM Select
	map(0x0234, 0x0237).rw(FUNC(bbcm_state::acccon_r), FUNC(bbcm_state::acccon_w));                                      //    FE34-FE37  ACCCON         ACCCON select register
	map(0x0238, 0x023b).lr8(NAME([this]() { econet_int_enable(0); return 0xfe; }));                                      // R: FE38-FE3B  INTOFF         ECONET Interrupt Off
	map(0x0238, 0x023b).lw8(NAME([this](uint8_t data) { econet_int_enable(0); }));                                       // W: FE38-FE3B  INTOFF         ECONET Interrupt Off
	map(0x023c, 0x023f).lr8(NAME([this]() { econet_int_enable(1); return 0xfe; }));                                      // R: FE3C-FE3F  INTON          ECONET Interrupt On
	map(0x023c, 0x023f).lw8(NAME([this](uint8_t data) { econet_int_enable(1); }));                                       // W: FE3C-FE3F  INTON          ECONET Interrupt On
	map(0x0240, 0x024f).mirror(0x10).m(m_sysvia, FUNC(via6522_device::map));                                             //    FE40-FE5F  6522 VIA       SYSTEM VIA
	map(0x02a0, 0x02a3).mirror(0x1c).rw(m_adlc, FUNC(mc6854_device::read), FUNC(mc6854_device::write));                  //    FEA0-FEBF  68B54 ADLC     ECONET controller
}


uint8_t bbcm_state::fetch_r(offs_t offset)
{
	if (BIT(m_acccon, ACCCON_X) || (BIT(m_acccon, ACCCON_E) && offset >= 0xc000 && offset <= 0xdfff))
		m_view_lynne.select(0);
	else
		m_view_lynne.disable();

	return m_maincpu->space(AS_PROGRAM).read_byte(offset);
}


uint8_t bbcm_state::romsel_r()
{
	return m_romsel;
}

void bbcm_state::romsel_w(offs_t offset, uint8_t data)
{
	// ROMSEL - FE30 read/write register
	//  b7 RAM 1 = Page in ANDY 8000-8FFF
	//         0 = Page in ROM  8000-8FFF
	//  b6     Not Used
	//  b5     Not Used
	//  b4     Not Used
	//  b3-b0  ROM/RAM Bank Select
	if (BIT(data, 7))
		m_view_andy.select(0);
	else
		m_view_andy.disable();

	m_romsel = data & 0x0f;

	// pass ROMSEL to internal expansion board
	if (m_internal)
		m_internal->romsel_w(offset, data);
}


uint8_t bbcm_state::acccon_r()
{
	return m_acccon;
}

void bbcm_state::acccon_w(uint8_t data)
{
	// ACCCON - FE34 read/write register
	//  b7 IRR 1 = Causes an IRQ to the processor
	//  b6 TST 1 = Selects FC00-FEFF read from OS-ROM
	//  b5 IFJ 1 = Internal 1MHz bus
	//         0 = External 1MHz bus
	//  b4 ITU 1 = Internal Tube
	//         0 = External Tube
	//  b3 Y   1 = Read/Write HAZEL C000-DFFF RAM
	//         0 = Read/Write ROM C000-DFFF OS-ROM
	//  b2 X   1 = Read/Write LYNNE
	//         0 = Read/Write main memory 3000-8000
	//  b1 E   1 = Causes shadow if VDU code
	//         0 = Main all the time
	//  b0 D   1 = Display LYNNE as screen
	//         0 = Display main RAM screen
	m_acccon = data;

	// Bit IRR causes Interrupt Request.
	m_irqs->in_w<6>(BIT(m_acccon, ACCCON_IRR));

	// Bit D causes the CRT controller to display the contents of LYNNE.
	setvideoshadow(BIT(m_acccon, ACCCON_D));

	// Bit Y causes 8 Kbyte of RAM referred to as HAZEL to be overlayed on the MOS VDU drivers.
	if (BIT(m_acccon, ACCCON_Y))
		m_view_hazel.select(0);
	else
		m_view_hazel.disable();

	// Bit X causes all accesses to 3000-7fff to be re-directed to LYNNE.
	if (BIT(m_acccon, ACCCON_X))
		m_view_lynne.select(0);
	else
		m_view_lynne.disable();

	// Bit TST controls paging of ROM reads in the 0xfc00-0xfeff region
	// if 0 the I/O is paged for both reads and writes
	// if 1 the ROM is paged in for reads but writes still go to I/O
	if (BIT(m_acccon, ACCCON_TST))
		m_view_tst.select(0);
	else
		m_view_tst.disable();
}


uint8_t bbcm_state::paged_r(offs_t offset)
{
	uint8_t data = 0xff;

	switch (m_romsel)
	{
	case 0: case 1: case 2: case 3:
		if (m_cart[BIT(m_romsel, 1)])
		{
			if (m_cart[BIT(m_romsel, 1)]->present())
				data = m_cart[BIT(m_romsel, 1)]->read(offset, 0, 0, m_romsel & 0x01, 1, 0);
			else
				data = bus_video_data();
		}
		else
		{
			data = m_region_rom->base()[offset + (m_romsel << 14)];
		}
		break;

	default:
		if (m_internal && m_internal->overrides_rom())
		{
			data = m_internal->paged_r(offset);
		}
		else
		{
			switch (m_romsel)
			{
			case 4: case 5: case 6: case 7:
				// 32K sockets
				if (m_rom[m_romsel & 0x0e] && m_rom[m_romsel & 0x0e]->present())
					data = m_rom[m_romsel & 0x0e]->read(offset | (m_romsel & 0x01) << 14);
				else
					data = m_region_rom->base()[offset + (m_romsel << 14)];
				break;
			default:
				// 16K sockets
				if (m_rom[m_romsel] && m_rom[m_romsel]->present())
					data = m_rom[m_romsel]->read(offset);
				else
					data = m_region_rom->base()[offset + (m_romsel << 14)];
				break;
			}
		}
		break;
	}

	return data;
}

void bbcm_state::paged_w(offs_t offset, uint8_t data)
{
	switch (m_romsel)
	{
	case 0: case 1: case 2: case 3:
		if (m_cart[BIT(m_romsel, 1)])
			m_cart[BIT(m_romsel, 1)]->write(offset, data, 0, 0, m_romsel & 0x01, 1, 0);
		else
			m_region_rom->base()[offset + (m_romsel << 14)] = data;
		break;

	default:
		if (m_internal && m_internal->overrides_rom())
		{
			m_internal->paged_w(offset, data);
		}
		else
		{
			switch (m_romsel)
			{
			case 4: case 5: case 6: case 7:
				// 32K sockets
				if (m_rom[m_romsel & 0x0e])
					m_rom[m_romsel & 0x0e]->write(offset | (m_romsel & 0x01) << 14, data);
				break;
			default:
				// 16K sockets
				if (m_rom[m_romsel])
					m_rom[m_romsel]->write(offset, data);
				break;
			}
		}
		break;
	}
}


uint8_t bbcm_state::fred_r(offs_t offset)
{
	uint8_t data = 0xff;

	if (BIT(m_acccon, ACCCON_IFJ))
		data = m_cart[m_romsel & 1]->read(offset, 1, 0, m_romsel & 1, 0, 0);
	else
		data = m_1mhzbus->fred_r(offset);

	return data;
}

void bbcm_state::fred_w(offs_t offset, uint8_t data)
{
	if (BIT(m_acccon, ACCCON_IFJ))
		m_cart[m_romsel & 1]->write(offset, data, 1, 0, m_romsel & 1, 0, 0);
	else
		m_1mhzbus->fred_w(offset, data);
}


uint8_t bbcm_state::jim_r(offs_t offset)
{
	uint8_t data = 0xff;

	if (BIT(m_acccon, ACCCON_IFJ))
		data = m_cart[m_romsel & 1]->read(offset, 0, 1, m_romsel & 1, 0, 0);
	else
		data = m_1mhzbus->jim_r(offset);

	return data;
}

void bbcm_state::jim_w(offs_t offset, uint8_t data)
{
	if (BIT(m_acccon, ACCCON_IFJ))
		m_cart[m_romsel & 1]->write(offset, data, 0, 1, m_romsel & 1, 0, 0);
	else
		m_1mhzbus->jim_w(offset, data);
}


uint8_t bbcm_state::tube_r(offs_t offset)
{
	uint8_t data = 0xfe;

	if (BIT(m_acccon, ACCCON_ITU))
		data = m_intube->host_r(offset);
	else
		data = m_extube->host_r(offset);

	return data;
}

void bbcm_state::tube_w(offs_t offset, uint8_t data)
{
	if (BIT(m_acccon, ACCCON_ITU))
		m_intube->host_w(offset, data);
	else
		m_extube->host_w(offset, data);
}


uint8_t bbcm_state::sysvia_pa_r()
{
	update_sdb();

	return m_sdb;
}

void bbcm_state::sysvia_pa_w(uint8_t data)
{
	m_sdb = data;

	update_sdb();
}


void bbcm_state::update_sdb()
{
	uint8_t const latch = m_latch->output_state();

	// sound
	if (!BIT(latch,0))
		m_sn->write(m_sdb);

	// rtc
	if (m_mc146818_ce)
	{
		// if data select is set then access the data in the MC146818
		if (BIT(latch, 2)) // DS
		{
			if (BIT(latch, 1)) // RD
			{
				m_sdb = m_rtc->data_r();
			}
			else
			{
				m_rtc->data_w(m_sdb);
			}
		}
		// if address select is set then set the address in the MC146818
		if (m_mc146818_as)
		{
			m_rtc->address_w(m_sdb);
		}
	}

	// keyboard
	m_sdb = m_kbd->read(m_sdb);
}


uint8_t bbcm_state::sysvia_pb_r()
{
	uint8_t data = 0xff;

	if (m_analog)
	{
		data &= ~0x30;
		data |= m_analog->pb_r();
	}

	return data;
}

void bbcm_state::sysvia_pb_w(uint8_t data)
{
	m_latch->write_nibble_d3(data);

	if (m_analog)
	{
		m_analog->pb_w(data & 0x30);
	}

	// set MC146818 CE and AS lines
	m_mc146818_ce = BIT(data, 6);
	m_mc146818_as = BIT(data, 7);

	update_sdb();
}


void bbcm_state::drive_control_w(uint8_t data)
{
	// Bit       Meaning
	// -----------------
	// 7,6       Not used
	//  5        Double density select (0 = double, 1 = single)
	//  4        Side select (0 = side 0, 1 = side 1)
	//  3        Drive select 2
	//  2        Reset drive controller chip. (0 = reset controller, 1 = no reset)
	//  1        Drive select 1
	//  0        Drive select 0

	floppy_image_device *floppy = nullptr;

	// bit 0, 1, 3: drive select
	if (BIT(data, 0)) floppy = m_wdfdc->subdevice<floppy_connector>("0")->get_device();
	if (BIT(data, 1)) floppy = m_wdfdc->subdevice<floppy_connector>("1")->get_device();
	m_wdfdc->set_floppy(floppy);

	// bit 4: side select
	if (floppy)
		floppy->ss_w(BIT(data, 4));

	// bit 5: density
	m_wdfdc->dden_w(BIT(data, 5));

	// bit 2: reset
	m_wdfdc->mr_w(BIT(data, 2));
}


void bbcm_state::trigger_reset(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? CLEAR_LINE : ASSERT_LINE);

	if (!state)
	{
		if (m_uservia) m_uservia->reset();
		if (m_adlc) m_adlc->reset();
		if (m_rtc) m_rtc->reset();
		if (m_wdfdc) m_wdfdc->reset();
		if (m_1mhzbus) m_1mhzbus->reset();
		if (m_intube) m_intube->reset();
		if (m_extube) m_extube->reset();
		if (m_internal) m_internal->reset();
		if (m_modem) m_modem->reset();
		if (m_cart[0]) m_cart[0]->reset();
		if (m_cart[1]) m_cart[1]->reset();
	}
}


static INPUT_PORTS_START(bbcm)
	PORT_INCLUDE(bbc_config)
INPUT_PORTS_END


void bbcm_state::init_se()
{
	bbc_state::init_bbc();

	uint8_t *cmos = memregion("rtc")->base();

	cmos[0x13] |= 0x0f; // *Configure File 15
	cmos[0x1e] |= 0x10; // *Configure Boot
}


static void bbc_floppies(device_slot_interface &device)
{
	device.option_add("525sssd", FLOPPY_525_SSSD);
	device.option_add("525sd",   FLOPPY_525_SD);
	device.option_add("525qd",   FLOPPY_525_QD);
	device.option_add("35dd",    FLOPPY_35_DD);
}


static const char *const bbc_sample_names[] =
{
	"*bbc",
	"motoroff",
	"motoron",
	nullptr
};


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

    BBC Master Series

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

void bbcm_state::bbcmet(machine_config &config)
{
	G65SC12(config, m_maincpu, 16_MHz_XTAL / 8);
	m_maincpu->set_addrmap(AS_PROGRAM, &bbcm_state::bbcmet_mem);
	m_maincpu->set_addrmap(AS_OPCODES, &bbcm_state::bbcm_fetch);
	config.set_perfect_quantum(m_maincpu);

	INPUT_MERGER_ANY_HIGH(config, m_irqs).output_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	RAM(config, m_ram).set_default_size("128K");

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(16_MHz_XTAL, 1024, 0, 640, 312, 0, 256);
	m_screen->set_screen_update("crtc", FUNC(hd6845s_device::screen_update));

	PALETTE(config, m_palette).set_entries(16);

	SAA5050(config, m_trom, 12_MHz_XTAL / 2);

	HD6845S(config, m_crtc, 16_MHz_XTAL / 8);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_reconfigure_callback(FUNC(bbcm_state::crtc_reconfigure));
	m_crtc->set_update_row_callback(FUNC(bbcm_state::crtc_update_row));
	m_crtc->out_de_callback().set(FUNC(bbcm_state::de_changed));
	m_crtc->out_hsync_callback().set(FUNC(bbcm_state::hsync_changed));
	m_crtc->out_vsync_callback().set(FUNC(bbcm_state::vsync_changed));

	config.set_default_layout(layout_bbcm);

	LS259(config, m_latch);
	m_latch->q_out_cb<3>().set(m_kbd, FUNC(bbc_kbd_device::write_kb_en));
	m_latch->q_out_cb<6>().set_output("capslock_led");
	m_latch->q_out_cb<7>().set_output("shiftlock_led");

	MOS6522(config, m_sysvia, 16_MHz_XTAL / 16);
	m_sysvia->readpa_handler().set(FUNC(bbcm_state::sysvia_pa_r));
	m_sysvia->writepa_handler().set(FUNC(bbcm_state::sysvia_pa_w));
	m_sysvia->readpb_handler().set(FUNC(bbcm_state::sysvia_pb_r));
	m_sysvia->writepb_handler().set(FUNC(bbcm_state::sysvia_pb_w));
	m_sysvia->cb2_handler().set([this](int state) { if (state) m_crtc->assert_light_pen_input(); });
	m_sysvia->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<0>));

	BBCM_KBD(config, m_kbd, 16_MHz_XTAL / 16);
	m_kbd->ca2_handler().set(m_sysvia, FUNC(via6522_device::write_ca2));
	m_kbd->rst_handler().set(FUNC(bbcm_state::trigger_reset));

	SPEAKER(config, "mono").front_center();

	SN76489A(config, m_sn, 16_MHz_XTAL / 4);
	m_sn->add_route(ALL_OUTPUTS, "mono", 1.0);

	MC146818(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->irq().set(m_irqs, FUNC(input_merger_device::in_w<7>));

	MC6854(config, m_adlc);
	m_adlc->out_txd_cb().set("network", FUNC(econet_device::host_data_w));
	m_adlc->out_irq_cb().set(FUNC(bbcm_state::adlc_irq_w));

	econet_device &econet(ECONET(config, "network", 0));
	econet.clk_wr_callback().set(m_adlc, FUNC(mc6854_device::txc_w));
	econet.clk_wr_callback().append(m_adlc, FUNC(mc6854_device::rxc_w));
	econet.clk_wr_callback().append(m_adlc, FUNC(mc6854_device::set_cts)).invert();
	econet.data_wr_callback().set(m_adlc, FUNC(mc6854_device::set_rx));
	ECONET_SLOT(config, "econet", "network", econet_devices);

	BBCM_CARTSLOT(config, m_cart[0], 16_MHz_XTAL, bbcm_cart, nullptr);
	m_cart[0]->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<10>));
	m_cart[0]->nmi_handler().set(FUNC(bbcm_state::bus_nmi_w));
	BBCM_CARTSLOT(config, m_cart[1], 16_MHz_XTAL, bbcm_cart, nullptr);
	m_cart[1]->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<11>));
	m_cart[1]->nmi_handler().set(FUNC(bbcm_state::bus_nmi_w));

	BBC_ROMSLOT16(config, m_rom[0x08], bbc_rom_devices, nullptr); // IC27
}


void bbcm_state::bbcm(machine_config &config)
{
	bbcmet(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &bbcm_state::bbcm_mem);

	SAMPLES(config, m_samples);
	m_samples->set_channels(1);
	m_samples->set_samples_names(bbc_sample_names);
	m_samples->add_route(ALL_OUTPUTS, "mono", 1.0);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(bbc_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED);
	m_cassette->set_interface("bbc_cass");

	acia6850_device &acia(ACIA6850(config, "acia"));
	acia.txd_handler().set("serproc", FUNC(acorn_serproc_device::write_txd));
	acia.rts_handler().set("serproc", FUNC(acorn_serproc_device::write_rtsi));
	acia.irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<2>));

	acorn_serproc_device &serproc(ACORN_SERPROC(config, "serproc", 16_MHz_XTAL / 13));
	serproc.rxc_handler().set("acia", FUNC(acia6850_device::write_rxc));
	serproc.txc_handler().set("acia", FUNC(acia6850_device::write_txc));
	serproc.rxd_handler().set("acia", FUNC(acia6850_device::write_rxd));
	serproc.dcd_handler().set("acia", FUNC(acia6850_device::write_dcd));
	serproc.ctso_handler().set("acia", FUNC(acia6850_device::write_cts));
	serproc.dout_handler().set("rs423", FUNC(rs232_port_device::write_txd));
	serproc.rtso_handler().set("rs423", FUNC(rs232_port_device::write_rts));
	serproc.casmo_handler().set(FUNC(bbcm_state::cassette_motor));
	serproc.casin_handler("cassette", FUNC(cassette_image_device::input));
	serproc.casout_handler("cassette", FUNC(cassette_image_device::output));

	rs232_port_device &rs423(RS232_PORT(config, "rs423", default_rs232_devices, nullptr));
	rs423.rxd_handler().set("serproc", FUNC(acorn_serproc_device::write_din));
	rs423.cts_handler().set("serproc", FUNC(acorn_serproc_device::write_ctsi));

	MOS6522(config, m_uservia, 16_MHz_XTAL / 16);
	m_uservia->writepa_handler().set("cent_data_out", FUNC(output_latch_device::write));
	m_uservia->readpb_handler().set(m_userport, FUNC(bbc_userport_slot_device::pb_r));
	m_uservia->writepb_handler().set(m_userport, FUNC(bbc_userport_slot_device::pb_w));
	m_uservia->ca2_handler().set("printer", FUNC(centronics_device::write_strobe));
	m_uservia->cb1_handler().set(m_userport, FUNC(bbc_userport_slot_device::write_cb1));
	m_uservia->cb2_handler().set(m_userport, FUNC(bbc_userport_slot_device::write_cb2));
	m_uservia->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<1>));

	centronics_device &centronics(CENTRONICS(config, "printer", centronics_devices, "printer"));
	centronics.ack_handler().set(m_uservia, FUNC(via6522_device::write_ca1));
	output_latch_device &latch(OUTPUT_LATCH(config, "cent_data_out"));
	centronics.set_output_latch(latch);

	upd7002_device &upd7002(UPD7002(config, "upd7002", 16_MHz_XTAL / 16));
	upd7002.get_analogue_callback().set(m_analog, FUNC(bbc_analogue_slot_device::ch_r));
	upd7002.eoc_callback().set(m_sysvia, FUNC(via6522_device::write_cb1));

	BBC_ANALOGUE_SLOT(config, m_analog, bbc_analogue_devices, nullptr);
	m_analog->lpstb_handler().set(m_sysvia, FUNC(via6522_device::write_cb2));
	m_analog->lpstb_handler().append([this](int state) { if (state) m_crtc->assert_light_pen_input(); });

	WD1770(config, m_wdfdc, 16_MHz_XTAL / 2);
	m_wdfdc->intrq_wr_callback().set(FUNC(bbcm_state::fdc_intrq_w));
	m_wdfdc->drq_wr_callback().set(FUNC(bbcm_state::fdc_drq_w));

	FLOPPY_CONNECTOR(config, "wdfdc:0", bbc_floppies, "525qd", bbc_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "wdfdc:1", bbc_floppies, "525qd", bbc_state::floppy_formats).enable_sound(true);

	BBC_1MHZBUS_SLOT(config, m_1mhzbus, 16_MHz_XTAL / 16, bbcm_1mhzbus_devices, nullptr);
	m_1mhzbus->add_route(ALL_OUTPUTS, "mono", 1.0);
	m_1mhzbus->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<3>));
	m_1mhzbus->nmi_handler().set(FUNC(bbcm_state::bus_nmi_w));

	BBC_TUBE_SLOT(config, m_intube, bbc_intube_devices, nullptr);
	m_intube->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<4>));

	BBC_TUBE_SLOT(config, m_extube, bbc_extube_devices, nullptr);
	m_extube->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<5>));

	BBC_USERPORT_SLOT(config, m_userport, bbc_userport_devices, nullptr);
	m_userport->cb1_handler().set(m_uservia, FUNC(via6522_device::write_cb1));
	m_userport->cb2_handler().set(m_uservia, FUNC(via6522_device::write_cb2));

	BBC_ROMSLOT32(config, m_rom[0x04], bbc_rom_devices, "ram").set_fixed_ram(true); // IC41 32K socket
	BBC_ROMSLOT32(config, m_rom[0x06], bbc_rom_devices, "ram").set_fixed_ram(true); // IC37 32K socket

	BBC_INTERNAL_SLOT(config, m_internal, 16_MHz_XTAL, bbcm_internal_devices, nullptr);
	m_internal->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<8>));
	m_internal->nmi_handler().set(FUNC(bbcm_state::bus_nmi_w));

	BBC_MODEM_SLOT(config, m_modem, 16_MHz_XTAL / 16, bbcm_modem_devices, nullptr);
	m_modem->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<9>));

	SOFTWARE_LIST(config, "cass_ls").set_original("bbc_cass").set_filter("A,B,M");
	SOFTWARE_LIST(config, "flop_ls_m").set_original("bbcm_flop");
	SOFTWARE_LIST(config, "cart_ls_m").set_original("bbcm_cart");
	SOFTWARE_LIST(config, "flop_ls_b").set_compatible("bbcb_flop");
	SOFTWARE_LIST(config, "flop_ls_b_orig").set_compatible("bbcb_flop_orig");
	SOFTWARE_LIST(config, "rom_ls").set_original("bbc_rom").set_filter("M");
	SOFTWARE_LIST(config, "hdd_ls").set_original("bbc_hdd").set_filter("M");
}


void bbcm_state::bbcmt(machine_config &config)
{
	bbcm(config);

	// Acorn 65C102 co-processor
	m_intube->set_default_option("65c102").set_fixed(true);
}


void bbcm_state::bbcmaiv(machine_config &config)
{
	bbcm(config);

	// Acorn 65C102 co-processor
	m_intube->set_default_option("65c102").set_fixed(true);

	// Philips VP415 Laserdisc player
	m_modem->set_default_option("scsiaiv").set_fixed(true);

	// Acorn Tracker Ball
	m_userport->set_default_option("tracker");
}


void bbcm_state::bbcm512(machine_config &config)
{
	bbcm(config);

	// Acorn Intel 80186 co-processor
	m_intube->set_default_option("80186").set_fixed(true);

	// Acorn Mouse
	m_userport->set_default_option("m512mouse");
}


void bbcm_state::bbcmarm(machine_config &config)
{
	bbcm(config);

	// Acorn ARM co-processor
	m_extube->set_default_option("arm").set_fixed(true);

	// Acorn Winchester Disc
	m_1mhzbus->set_default_option("awhd");
}


void bbcm_state::cfa3000(machine_config &config)
{
	bbcm(config);

	m_wdfdc->subdevice<floppy_connector>("0")->set_default_option(nullptr);
	m_wdfdc->subdevice<floppy_connector>("1")->set_default_option(nullptr);

	// LK18 and LK19 are set to enable rom, disabling ram
	m_rom[0x04]->set_default_option("rom").set_fixed(true);
	m_rom[0x06]->set_default_option("rom").set_fixed(true);

	// keyboard
	m_userport->set_default_option("cfa3000kbd").set_fixed(true);

	// option board
	m_1mhzbus->set_default_option("cfa3000opt").set_fixed(true);

	// analogue dials/sensors
	m_analog->set_default_option("cfa3000a").set_fixed(true);

	// software lists
	config.device_remove("cass_ls");
	config.device_remove("flop_ls_m");
	config.device_remove("flop_ls_b");
	config.device_remove("flop_ls_b_orig");
}


void bbcm_state::daisy(machine_config &config)
{
	bbcm(config);

	// Acorn 65C102 co-processor
	m_intube->set_default_option("65c102").set_fixed(true);

	// Start Interface / 64K SRAM / Analog Board
	//m_1mhzbus->set_default_option("daisy").set_fixed(true);

	// LK18 and LK19 are set to enable ROM, disabling RAM
	m_rom[0x04]->set_default_option("rom").set_fixed(true);
	m_rom[0x06]->set_default_option("rom").set_fixed(true);
}


void bbcm_state::discmon(machine_config &config)
{
	bbcm(config);

	// TODO: Add coin slot

	config.device_remove("cass_ls");
	config.device_remove("flop_ls_m");
	config.device_remove("flop_ls_b");
	config.device_remove("flop_ls_b_orig");
}


void bbcm_state::discmate(machine_config &config)
{
	bbcm(config);

	// TODO: Add Sony CDK-3000PII Auto Disc Loader

	// TODO: Add interface boards connected to cassette and RS423

	config.device_remove("cass_ls");
	config.device_remove("flop_ls_m");
	config.device_remove("flop_ls_b");
	config.device_remove("flop_ls_b_orig");
}


void bbcm_state::ht280(machine_config &config)
{
	bbcm(config);

	HT280_KBD(config.replace(), m_kbd, 16_MHz_XTAL / 16);
	m_kbd->ca2_handler().set(m_sysvia, FUNC(via6522_device::write_ca2));
	m_kbd->rst_handler().set(FUNC(bbcm_state::trigger_reset));

	// Z80 Controller Board
	//m_1mhzbus->set_default_option("ht280").set_fixed(true);

	// LK18 and LK19 are set to enable rom, disabling ram
	m_rom[0x04]->set_default_option("rom").set_fixed(true);
	m_rom[0x06]->set_default_option("rom").set_fixed(true);

	// cartridge sockets
	config.device_remove("cartslot1");
	config.device_remove("cartslot2");
}


//void bbcm_state::mpc_prisma_default(device_t* device)
//{
//  device->subdevice<bbc_1mhzbus_slot_device>("1mhzbus")->set_default_option("awhd");
//  device->subdevice<bbc_1mhzbus_slot_device>("1mhzbus")->set_fixed(true);
//}


void bbcm_state::mpc800(machine_config &config)
{
	bbcm(config);

	// Acorn 65C102 co-processor
	m_intube->set_default_option("65c102").set_fixed(true);

	// Prisma-2
	//m_1mhzbus->set_default_option("prisma2").set_fixed(true);
	//m_1mhzbus->set_option_machine_config("prisma2", mpc_prisma_default);

	// Mouse (AMX compatible)
	m_userport->set_default_option("amxmouse").set_fixed(true);

	// cartridge sockets
	config.device_remove("cartslot1");
	config.device_remove("cartslot2");
}


void bbcm_state::mpc900(machine_config &config)
{
	mpc800(config);

	// Prisma-3
	//m_1mhzbus->set_default_option("prisma3").set_fixed(true);
	//m_1mhzbus->set_option_machine_config("prisma3", mpc_prisma_default);
}


void bbcm_state::mpc900gx(machine_config &config)
{
	mpc800(config);

	// Prisma-3 Plus
	//m_1mhzbus->set_default_option("prisma3p").set_fixed(true);
	//m_1mhzbus->set_option_machine_config("prisma3p", mpc_prisma_default);
}


void bbcm_state::se3010(machine_config &config)
{
	bbcm(config);

	SE3010_KBD(config.replace(), m_kbd, 16_MHz_XTAL / 16);
	m_kbd->ca2_handler().set(m_sysvia, FUNC(via6522_device::write_ca2));
	m_kbd->rst_handler().set(FUNC(bbcm_state::trigger_reset));

	// cartridge sockets
	config.device_remove("cartslot1");
	config.device_remove("cartslot2");
}


ROM_START(bbcm)
	// page 0  00000  SK3 Rear Cartridge bottom 16K      // page 8  20000  IC27 ANFS
	// page 1  04000  SK3 Rear Cartridge top 16K         // page 9  24000  IC24 DFS + SRAM
	// page 2  08000  SK4 Front Cartridge bottom 16K     // page 10 28000  IC24 Viewsheet
	// page 3  0C000  SK4 Front Cartridge top 16K        // page 11 2C000  IC24 Edit
	// page 4  10000  IC41 SWRAM or bottom 16K           // page 12 30000  IC24 BASIC
	// page 5  14000  IC41 SWRAM or top 16K              // page 13 34000  IC24 ADFS
	// page 6  18000  IC37 SWRAM or bottom 16K           // page 14 38000  IC24 View + MOS code
	// page 7  1C000  IC37 SWRAM or top 16K              // page 15 3C000  IC24 Terminal + Tube host + CFS
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "320", "MOS 3.20")
	ROMX_LOAD("mos320.ic24", 0x20000, 0x20000, CRC(0f747ebe) SHA1(eacacbec3892dc4809ad5800e6c8299ff9eb528f), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "350", "MOS 3.50")
	ROMX_LOAD("mos350.ic24", 0x20000, 0x20000, CRC(141027b9) SHA1(85211b5bc7c7a269952d2b063b7ec0e1f0196803), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "329", "MOS 3.29")
	ROMX_LOAD("mos329.ic24", 0x20000, 0x20000, CRC(8dd7338b) SHA1(4604203c70c04a9fd003103deec438fc5bd44839), ROM_BIOS(2))

	ROM_COPY("rom", 0x20000, 0x40000, 0x4000) // Move loaded roms into place
	ROM_FILL(0x20000, 0x4000, 0xff)

	//ROM_LOAD("anfs425-2201351.rom", 0x20000, 0x4000, CRC(c2a6655e) SHA1(14f75d36ffe9af14aaac42df55b4fe3729ba75cf))

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)

	ROM_REGION(0x40, "rtc", 0)
	ROMX_LOAD("mos320.cmos", 0x00, 0x40, CRC(c7f9e85a) SHA1(f24cc9db0525910689219f7204bf8b864033ee94), ROM_BIOS(0))
	ROMX_LOAD("mos350.cmos", 0x00, 0x40, CRC(e84c1854) SHA1(f3cb7f12b7432caba28d067f01af575779220aac), ROM_BIOS(1))
	ROMX_LOAD("mos350.cmos", 0x00, 0x40, CRC(e84c1854) SHA1(f3cb7f12b7432caba28d067f01af575779220aac), ROM_BIOS(2))
ROM_END


#define rom_bbcmt rom_bbcm
#define rom_bbcm512 rom_bbcm


ROM_START(bbcmaiv)
	// page 0  00000  SK3 Rear Cartridge bottom 16K      // page 8  20000  IC27 VFS
	// page 1  04000  SK3 Rear Cartridge top 16K         // page 9  24000  IC24 DFS + SRAM
	// page 2  08000  SK4 Front Cartridge bottom 16K     // page 10 28000  IC24 Viewsheet
	// page 3  0C000  SK4 Front Cartridge top 16K        // page 11 2C000  IC24 Edit
	// page 4  10000  IC41 SWRAM or bottom 16K           // page 12 30000  IC24 BASIC
	// page 5  14000  IC41 SWRAM or top 16K              // page 13 34000  IC24 ADFS
	// page 6  18000  IC37 SWRAM or bottom 16K           // page 14 38000  IC24 View + MOS code
	// page 7  1C000  IC37 SWRAM or top 16K              // page 15 3C000  IC24 Terminal + Tube host + CFS
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("mos320.ic24", 0x20000, 0x20000, CRC(0f747ebe) SHA1(eacacbec3892dc4809ad5800e6c8299ff9eb528f))

	ROM_COPY("rom", 0x20000, 0x40000, 0x4000) // Move loaded roms into place
	ROM_FILL(0x20000, 0x4000, 0xff)

	ROM_LOAD("vfs170.rom", 0x20000, 0x4000, CRC(b124a0bb) SHA1(ba31c757815cf470402d7829a70a0e1d3fb1355b) )

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)

	ROM_REGION(0x40, "rtc", 0)
	ROM_LOAD("mos320aiv.cmos", 0x0e, 0x32, BAD_DUMP CRC(b9ae42a1) SHA1(abf3e94b013f24027ca36c96720963c3411e93f8))
ROM_END


ROM_START(bbcmet)
	// page 0  00000  SK3 Rear Cartridge bottom 16K      // page 8  20000
	// page 1  04000  SK3 Rear Cartridge top 16K         // page 9  24000  IC24 BASIC
	// page 2  08000  SK4 Front Cartridge bottom 16K     // page 10 28000  IC24 ANFS
	// page 3  0C000  SK4 Front Cartridge top 16K        // page 11 2C000  IC24 MOS code
	// page 4  10000  IC41 SWRAM or bottom 16K           // page 12 30000  IC24 unused
	// page 5  14000  IC41 SWRAM or top 16K              // page 13 34000  IC24 BASIC
	// page 6  18000  IC37 SWRAM or bottom 16K           // page 14 38000  IC24 ANFS
	// page 7  1C000  IC37 SWRAM or top 16K              // page 15 3C000  IC24 MOS code
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("mos400.ic24", 0x20000, 0x10000, CRC(81729034) SHA1(d4bc2c7f5e66b5298786138f395908e70c772971))
	ROM_RELOAD(             0x30000, 0x10000) // mirror

	ROM_COPY("rom", 0x20000, 0x40000, 0x4000) // Move loaded roms into place
	ROM_FILL(0x20000, 0x4000, 0xff)

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)

	ROM_REGION(0x40, "rtc", 0)
	ROM_LOAD("mos400.cmos", 0x0e, 0x32, BAD_DUMP CRC(fff41cc5) SHA1(3607568758f90b3bd6c7dc9533e2aa24f9806ff3))
ROM_END


ROM_START(bbcmarm)
	// page 0  00000  SK3 Rear Cartridge bottom 16K      // page 8  20000  IC27 ANFS
	// page 1  04000  SK3 Rear Cartridge top 16K         // page 9  24000  IC24 DFS + SRAM
	// page 2  08000  SK4 Front Cartridge bottom 16K     // page 10 28000  IC24 Viewsheet
	// page 3  0C000  SK4 Front Cartridge top 16K        // page 11 2C000  IC24 Edit
	// page 4  10000  IC41 SWRAM or bottom 16K           // page 12 30000  IC24 BASIC
	// page 5  14000  IC41 SWRAM or top 16K              // page 13 34000  IC24 ADFS
	// page 6  18000  IC37 SWRAM or bottom 16K           // page 14 38000  IC24 View + MOS code
	// page 7  1C000  IC37 SWRAM or top 16K              // page 15 3C000  IC24 Terminal + Tube host + CFS
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("mos320.ic24", 0x20000, 0x20000, CRC(0f747ebe) SHA1(eacacbec3892dc4809ad5800e6c8299ff9eb528f))

	ROM_COPY("rom", 0x20000, 0x40000, 0x4000) // Move loaded roms into place
	ROM_FILL(0x20000, 0x4000, 0xff)

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)

	ROM_REGION(0x40, "rtc", 0)
	ROM_LOAD("mos320arm.cmos", 0x00, 0x40, CRC(56117257) SHA1(ed98563bef18f9d2a0b2d941cd20823d760fb127))
ROM_END


ROM_START(cfa3000)
	// page 0  00000  SK3 Rear Cartridge bottom 16K      // page 8  20000  IC27
	// page 1  04000  SK3 Rear Cartridge top 16K         // page 9  24000  IC24
	// page 2  08000  SK4 Front Cartridge bottom 16K     // page 10 28000  IC24
	// page 3  0C000  SK4 Front Cartridge top 16K        // page 11 2C000  IC24
	// page 4  10000  IC41 SWRAM or bottom 16K           // page 12 30000  IC24
	// page 5  14000  IC41 SWRAM or top 16K              // page 13 34000  IC24 DFS
	// page 6  18000  IC37 SWRAM or bottom 16K           // page 14 38000  IC24 BASIC
	// page 7  1C000  IC37 SWRAM or top 16K              // page 15 3C000  IC24 Terminal + Tube host + CFS
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "103", "Issue 10.3")
	ROMX_LOAD("cfa3000_3m4_iss10.3.ic41",           0x10000, 0x08000, CRC(ecb385ab) SHA1(eafa9b34cb1cf63790f74332bb7d85ee356b6973), ROM_BIOS(0))
	ROMX_LOAD("cfa3000_sm_iss10.3.ic37",            0x18000, 0x08000, CRC(c07aee5f) SHA1(1994e3755dc15d1ea7e105bc19cd57893b719779), ROM_BIOS(0))
	ROMX_LOAD("acorn_mos,tinsley_64k,iss10.3.ic24", 0x20000, 0x10000, CRC(4413c3ee) SHA1(76d0462b4dabe2461010fce2341570ff3d606d54), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "102", "Issue 10.2")
	ROMX_LOAD("cfa3000_3m4_iss10.2.ic41",           0x10000, 0x08000, CRC(ecb385ab) SHA1(eafa9b34cb1cf63790f74332bb7d85ee356b6973), ROM_BIOS(1))
	ROMX_LOAD("cfa3000_sm_iss10.2.ic37",            0x18000, 0x08000, CRC(e733d5b3) SHA1(07e89943c6ac0953b75686ee06e947f33119dbed), ROM_BIOS(1))
	ROMX_LOAD("acorn_mos,tinsley_64k,iss10.2.ic24", 0x20000, 0x10000, CRC(4413c3ee) SHA1(76d0462b4dabe2461010fce2341570ff3d606d54), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "90", "Issue 9")
	ROMX_LOAD("cfa3000_3m4_iss9.ic41",              0x10000, 0x08000, CRC(a4bd5d53) SHA1(90747ff7bd81ac1e124bae964c206d8df163e1d6), ROM_BIOS(2))
	ROMX_LOAD("cfa3000_sm_iss9.ic37",               0x18000, 0x08000, CRC(559d1fae) SHA1(271e1ab9b53e82028e92e7cdb8c517df06e76477), ROM_BIOS(2))
	ROMX_LOAD("acorn_mos,tinsley_64k,iss9.ic24",    0x20000, 0x10000, CRC(4413c3ee) SHA1(76d0462b4dabe2461010fce2341570ff3d606d54), ROM_BIOS(2))

	ROM_SYSTEM_BIOS(3, "70", "Issue 7")
	ROMX_LOAD("cfa3000_3m4_iss7.ic41",              0x10000, 0x08000, CRC(a0b32288) SHA1(83b047e9eb35f0644bd8f0acb1a56e1428bacc0b), ROM_BIOS(3))
	ROMX_LOAD("cfa3000_sm_iss7.ic37",               0x18000, 0x08000, CRC(3cd42bbd) SHA1(17f6c66039d20a364cc9e1377c7ced14d5302603), ROM_BIOS(3))
	ROMX_LOAD("acorn_mos,tinsley_64k,iss7.ic24",    0x20000, 0x10000, CRC(4413c3ee) SHA1(76d0462b4dabe2461010fce2341570ff3d606d54), ROM_BIOS(3))

	ROM_COPY("rom", 0x20000, 0x30000, 0x10000) // mirror

	ROM_COPY("rom", 0x20000, 0x40000, 0x04000) // Move loaded roms into place
	ROM_FILL(0x20000, 0x4000, 0xff)

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)

	ROM_REGION(0x40, "rtc", 0)
	ROM_LOAD("mos350.cmos", 0x00, 0x40, CRC(e84c1854) SHA1(f3cb7f12b7432caba28d067f01af575779220aac))
ROM_END


ROM_START(daisy)
	// page 0  00000  SK3 Rear Cartridge bottom 16K      // page 8  20000  IC27 HiBASIC3
	// page 1  04000  SK3 Rear Cartridge top 16K         // page 9  24000  IC24 DFS + SRAM
	// page 2  08000  SK4 Front Cartridge bottom 16K     // page 10 28000  IC24 Viewsheet
	// page 3  0C000  SK4 Front Cartridge top 16K        // page 11 2C000  IC24 Edit
	// page 4  10000  IC41 SIMDIST                       // page 12 30000  IC24 BASIC
	// page 5  14000  IC41 DHRFDSY                       // page 13 34000  IC24 ADFS
	// page 6  18000  IC37 IRFDSY                        // page 14 38000  IC24 View + MOS code
	// page 7  1C000  IC37 DAISY                         // page 15 3C000  IC24 Terminal + Tube host + CFS
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("mos320.ic24", 0x20000, 0x20000, CRC(0f747ebe) SHA1(eacacbec3892dc4809ad5800e6c8299ff9eb528f))

	ROM_COPY("rom", 0x20000, 0x40000, 0x4000) // Move loaded roms into place
	ROM_FILL(0x20000, 0x4000, 0xff)

	ROM_LOAD("simdist_2_22-2-89_dhrfdsy.rom", 0x10000, 0x8000, CRC(8a9d9c4a) SHA1(278c5c63e06359601cdf972f55e98f5a7442a713))
	ROM_LOAD("daisy_irfdsy_vr4_5-1-89.rom",   0x18000, 0x8000, CRC(9662d779) SHA1(0841c1fd34c152f3f02ace8633f1fa3f5139069e))
	ROM_LOAD("hibas03_a063.rom",              0x20000, 0x4000, CRC(6ea7affc) SHA1(99234b55fde57680e4217b72ef4ccb8fc56edeff))

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)

	ROM_REGION(0x40, "rtc", 0)
	ROM_LOAD("mos320.cmos", 0x00, 0x40, CRC(c7f9e85a) SHA1(f24cc9db0525910689219f7204bf8b864033ee94))
ROM_END


ROM_START(discmon)
	// page 0  00000  SK3 Rear Cartridge bottom 16K      // page 8  20000  IC27 DiscMonitor
	// page 1  04000  SK3 Rear Cartridge top 16K         // page 9  24000  IC24 DFS + SRAM
	// page 2  08000  SK4 Front Cartridge bottom 16K     // page 10 28000  IC24 Viewsheet
	// page 3  0C000  SK4 Front Cartridge top 16K        // page 11 2C000  IC24 Edit
	// page 4  10000  IC41 SWRAM or bottom 16K           // page 12 30000  IC24 BASIC
	// page 5  14000  IC41 SWRAM or top 16K              // page 13 34000  IC24 ADFS
	// page 6  18000  IC37 SWRAM or bottom 16K           // page 14 38000  IC24 View + MOS code
	// page 7  1C000  IC37 SWRAM or top 16K              // page 15 3C000  IC24 Terminal + Tube host + CFS
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("mos320.ic24", 0x20000, 0x20000, CRC(0f747ebe) SHA1(eacacbec3892dc4809ad5800e6c8299ff9eb528f))

	ROM_COPY("rom", 0x20000, 0x40000, 0x4000) // Move loaded roms into place
	ROM_FILL(0x20000, 0x4000, 0xff)

	ROM_LOAD("discmonitor406.rom", 0x20000, 0x4000, CRC(12e30e9b) SHA1(0e5356531978e08e75913e793cb0afc0e75e61ad))

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)

	ROM_REGION(0x40, "rtc", 0)
	ROM_LOAD("mos320.cmos", 0x00, 0x40, CRC(c7f9e85a) SHA1(f24cc9db0525910689219f7204bf8b864033ee94))
ROM_END


ROM_START(discmate)
	// page 0  00000  SK3 Rear Cartridge bottom 16K      // page 8  20000  IC27 Discmaster
	// page 1  04000  SK3 Rear Cartridge top 16K         // page 9  24000  IC24 DFS + SRAM
	// page 2  08000  SK4 Front Cartridge bottom 16K     // page 10 28000  IC24 Viewsheet
	// page 3  0C000  SK4 Front Cartridge top 16K        // page 11 2C000  IC24 Edit
	// page 4  10000  IC41 SWRAM or bottom 16K           // page 12 30000  IC24 BASIC
	// page 5  14000  IC41 SWRAM or top 16K              // page 13 34000  IC24 ADFS
	// page 6  18000  IC37 SWRAM or bottom 16K           // page 14 38000  IC24 View + MOS code
	// page 7  1C000  IC37 SWRAM or top 16K              // page 15 3C000  IC24 Terminal + Tube host + CFS
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("mos320.ic24", 0x20000, 0x20000, CRC(0f747ebe) SHA1(eacacbec3892dc4809ad5800e6c8299ff9eb528f))

	ROM_COPY("rom", 0x20000, 0x40000, 0x4000) // Move loaded roms into place
	ROM_FILL(0x20000, 0x4000, 0xff)

	ROM_LOAD("discmaster303.rom", 0x20000, 0x4000, CRC(73974057) SHA1(79f99eae62ab46818386ab8a67fe50319ae30226))

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)

	ROM_REGION(0x40, "rtc", 0)
	ROM_LOAD("mos320.cmos", 0x00, 0x40, CRC(c7f9e85a) SHA1(f24cc9db0525910689219f7204bf8b864033ee94))
ROM_END


ROM_START(ht280)
	// page 0  00000  SK3 32K SRAM Cartridge bottom 16K  // page 8  20000  IC27 280T D 5 3.04
	// page 1  04000  SK3 32K SRAM Cartridge top 16K     // page 9  24000  IC24 DFS + SRAM
	// page 2  08000  SK4 Care 2 ROM Cartridge           // page 10 28000  IC24 Viewsheet
	// page 3  0C000  SK4 Care 2 ROM Cartridge           // page 11 2C000  IC24 Edit
	// page 4  10000  IC41 280T D 6M+S 3.59              // page 12 30000  IC24 BASIC
	// page 5  14000  IC41 280T D 6M+S 3.59              // page 13 34000  IC24 ADFS
	// page 6  18000  IC37 280T D 1+2 3.57               // page 14 38000  IC24 View + MOS code
	// page 7  1C000  IC37 280T D 1+2 3.57               // page 15 3C000  IC24 Terminal + Tube host + CFS
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("mos320.ic24", 0x20000, 0x20000, CRC(0f747ebe) SHA1(eacacbec3892dc4809ad5800e6c8299ff9eb528f))

	ROM_COPY("rom", 0x20000, 0x40000, 0x4000) // Move loaded roms into place
	ROM_FILL(0x20000, 0x4000, 0xff)

	ROM_LOAD("sram_reset.bin",      0x00000, 0x8000, CRC(7311ad92) SHA1(ba52f52012b005285724c9507e684800ef316ba9)) // 32K SRAM Cartridge (saved after *RESET)
	ROM_LOAD("280td_3_3.06.bin",    0x08000, 0x4000, CRC(ff1da25a) SHA1(3b75f1bdee295767a0767ee9a3d9af58b62e6d6d)) // Care 2 ROM Cartridge
	ROM_LOAD("280td_4_3.60.bin",    0x0c000, 0x4000, CRC(30ebb04e) SHA1(40e81b91441eefa199a362f09d623dc834192eb2)) // Care 2 ROM Cartridge
	ROM_LOAD("280td_6m+s_3.54.rom", 0x10000, 0x8000, CRC(7a36b159) SHA1(fc2648418f60fe050152b2eac90de786acd8891a))
	ROM_LOAD("280td_1+2_3.59.rom",  0x18000, 0x8000, CRC(cacec014) SHA1(f445ccf86d7c8415e924e8c3181351e3ca612b80))
	ROM_LOAD("280td_5_3.04.rom",    0x20000, 0x4000, CRC(75cb57b1) SHA1(708b4d8a71dd5050d57c3a8533fd6ca8737084df))

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)

	ROM_REGION(0x40, "rtc", 0)
	ROM_LOAD("mos320.cmos", 0x00, 0x40, CRC(c7f9e85a) SHA1(f24cc9db0525910689219f7204bf8b864033ee94))
ROM_END


#define rom_ltmpm rom_bbcm


ROM_START(mpc800)
	// page 0  00000  SK3 Rear Cartridge bottom 16K      // page 8  20000  IC27 800 Manager
	// page 1  04000  SK3 Rear Cartridge top 16K         // page 9  24000  IC24 DFS + SRAM
	// page 2  08000  SK4 Front Cartridge bottom 16K     // page 10 28000  IC24 Viewsheet
	// page 3  0C000  SK4 Front Cartridge top 16K        // page 11 2C000  IC24 Edit
	// page 4  10000  IC41 SWRAM or bottom 16K           // page 12 30000  IC24 BASIC
	// page 5  14000  IC41 SWRAM or top 16K              // page 13 34000  IC24 ADFS
	// page 6  18000  IC37 SWRAM or bottom 16K           // page 14 38000  IC24 View + MOS code
	// page 7  1C000  IC37 SWRAM or top 16K              // page 15 3C000  IC24 Terminal + Tube host + CFS
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("mos320.ic24", 0x20000, 0x20000, CRC(0f747ebe) SHA1(eacacbec3892dc4809ad5800e6c8299ff9eb528f))

	ROM_COPY("rom", 0x20000, 0x40000, 0x4000) // Move loaded roms into place
	ROM_FILL(0x20000, 0x4000, 0xff)

	ROM_LOAD("prisma2_v1-10.bin",      0x00000, 0x4000, CRC(14b85cdd) SHA1(c975041933dd0e223470292d169dd70568c25c59))
	ROM_LOAD("mpc800manager-2.40.rom", 0x20000, 0x4000, CRC(d5a27b00) SHA1(533e846f47803d61508fe270fd7021c010a21a84))

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)

	ROM_REGION(0x40, "rtc", 0)
	ROM_LOAD("mos320.cmos", 0x00, 0x40, CRC(c7f9e85a) SHA1(f24cc9db0525910689219f7204bf8b864033ee94))
ROM_END


ROM_START(mpc900)
	// page 0  00000  SK3 Rear Cartridge bottom 16K      // page 8  20000  IC27 900 Manager
	// page 1  04000  SK3 Rear Cartridge top 16K         // page 9  24000  IC24 DFS + SRAM
	// page 2  08000  SK4 Front Cartridge bottom 16K     // page 10 28000  IC24 Viewsheet
	// page 3  0C000  SK4 Front Cartridge top 16K        // page 11 2C000  IC24 Edit
	// page 4  10000  IC41 SWRAM or bottom 16K           // page 12 30000  IC24 BASIC
	// page 5  14000  IC41 SWRAM or top 16K              // page 13 34000  IC24 ADFS
	// page 6  18000  IC37 SWRAM or bottom 16K           // page 14 38000  IC24 View + MOS code
	// page 7  1C000  IC37 SWRAM or top 16K              // page 15 3C000  IC24 Terminal + Tube host + CFS
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("mos320.ic24", 0x20000, 0x20000, CRC(0f747ebe) SHA1(eacacbec3892dc4809ad5800e6c8299ff9eb528f))

	ROM_COPY("rom", 0x20000, 0x40000, 0x4000) // Move loaded roms into place
	ROM_FILL(0x20000, 0x4000, 0xff)

	ROM_LOAD("prisma3_v1-30main.bin",   0x00000, 0x4000, CRC(6ed1e8aa) SHA1(ceda5ca869f960a4bb5368adbf4acd4861efd3a0))
	ROM_LOAD("prisma3_v1-30utils.bin",  0x04000, 0x4000, CRC(76c42acb) SHA1(36aab45a3565325dd31ada64934597bafe765ed8))
	ROM_LOAD("mpc900_manager-1.20.rom", 0x20000, 0x4000, BAD_DUMP CRC(3470af89) SHA1(5d54ace2fbfdb9a7ec88aeaebcfe978688ef1893))

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)

	ROM_REGION(0x40, "rtc", 0)
	ROM_LOAD("mos320.cmos", 0x00, 0x40, CRC(c7f9e85a) SHA1(f24cc9db0525910689219f7204bf8b864033ee94))
ROM_END


ROM_START(mpc900gx)
	// page 0  00000  SK3 Rear Cartridge bottom 16K      // page 8  20000  IC27 900GX Manager
	// page 1  04000  SK3 Rear Cartridge top 16K         // page 9  24000  IC24 DFS + SRAM
	// page 2  08000  SK4 Front Cartridge bottom 16K     // page 10 28000  IC24 Viewsheet
	// page 3  0C000  SK4 Front Cartridge top 16K        // page 11 2C000  IC24 Edit
	// page 4  10000  IC41 SWRAM or bottom 16K           // page 12 30000  IC24 BASIC
	// page 5  14000  IC41 SWRAM or top 16K              // page 13 34000  IC24 ADFS
	// page 6  18000  IC37 SWRAM or bottom 16K           // page 14 38000  IC24 View + MOS code
	// page 7  1C000  IC37 SWRAM or top 16K              // page 15 3C000  IC24 Terminal + Tube host + CFS
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("mos320.ic24", 0x20000, 0x20000, CRC(0f747ebe) SHA1(eacacbec3892dc4809ad5800e6c8299ff9eb528f))

	ROM_COPY("rom", 0x20000, 0x40000, 0x4000) // Move loaded roms into place
	ROM_FILL(0x20000, 0x4000, 0xff)

	ROM_LOAD("prisma3_v2-30main.bin",     0x00000, 0x4000, CRC(b9489c7c) SHA1(59a7606e3b92e1c7fbd0075d54e4fffd7c28417e))
	ROM_LOAD("prisma3_v2-30utils.bin",    0x04000, 0x4000, CRC(fa395bca) SHA1(be7b1f590818e362a8acc5b203f0a08fce967f42))
	ROM_LOAD("mpc900gx_manager-1.20.rom", 0x20000, 0x4000, CRC(3470af89) SHA1(5d54ace2fbfdb9a7ec88aeaebcfe978688ef1893))

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)

	ROM_REGION(0x40, "rtc", 0)
	ROM_LOAD("mos320.cmos", 0x00, 0x40, CRC(c7f9e85a) SHA1(f24cc9db0525910689219f7204bf8b864033ee94))
ROM_END


ROM_START(se3010)
	// page 0  00000  SK3 RFS Data                       // page 8  20000  IC27
	// page 1  04000  SK3 RFS Data                       // page 9  24000  IC24 DFS + SRAM
	// page 2  08000  SK4                                // page 10 28000  IC24 Viewsheet
	// page 3  0C000  SK4                                // page 11 2C000  IC24 Edit
	// page 4  10000  IC41 SWRAM or bottom 16K           // page 12 30000  IC24 BASIC
	// page 5  14000  IC41 SWRAM or top 16K              // page 13 34000  IC24 ADFS
	// page 6  18000  IC37 SWRAM or bottom 16K           // page 14 38000  IC24 View + MOS code
	// page 7  1C000  IC37 SWRAM or top 16K              // page 15 3C000  IC24 Terminal + Tube host + CFS
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_LOAD("mos320.ic24", 0x20000, 0x20000, CRC(0f747ebe) SHA1(eacacbec3892dc4809ad5800e6c8299ff9eb528f))

	ROM_SYSTEM_BIOS(0, "44", "V4.4")
	ROMX_LOAD("sprite_control_v4.4.bin", 0x00000, 0x4000, CRC(9c5a507d) SHA1(7c097ea17ad863fac575c91c0b626198059b5aea), ROM_BIOS(0))
	ROMX_LOAD("sprite_master_v4.4.bin",  0x04000, 0x4000, CRC(96fd46cf) SHA1(ac8f55658de0b11e3234454a833abc27c242db23), ROM_BIOS(0))

	//ROM_SYSTEM_BIOS(1, "551", "V5.51")
	//ROMX_LOAD("sprite.bin",       0x00000, 0x4000, NO_DUMP, ROM_BIOS(1))
	//ROMX_LOAD("communicator.bin", 0x04000, 0x4000, NO_DUMP, ROM_BIOS(1))
	//ROMX_LOAD("mdm2.bin",         0x08000, 0x4000, NO_DUMP, ROM_BIOS(1))
	//ROMX_LOAD("mdm3.bin",         0x0c000, 0x4000, NO_DUMP, ROM_BIOS(1))

	ROM_COPY("rom", 0x20000, 0x40000, 0x4000) // Move loaded roms into place
	ROM_FILL(0x20000, 0x4000, 0xff)

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)

	ROM_REGION(0x40, "rtc", 0)
	ROM_LOAD("mos320.cmos", 0x00, 0x40, CRC(c7f9e85a) SHA1(f24cc9db0525910689219f7204bf8b864033ee94))
ROM_END

} // anonymous namespace


//    YEAR  NAME        PARENT  COMPAT MACHINE     INPUT   CLASS         INIT       COMPANY                        FULLNAME                              FLAGS
COMP( 1986, bbcm,       0,      bbcb,  bbcm,       bbcm,   bbcm_state,   init_bbc,  "Acorn Computers",             "BBC Master 128",                     MACHINE_IMPERFECT_GRAPHICS )
COMP( 1986, bbcmt,      bbcm,   0,     bbcmt,      bbcm,   bbcm_state,   init_bbc,  "Acorn Computers",             "BBC Master Turbo",                   MACHINE_IMPERFECT_GRAPHICS )
COMP( 1986, bbcmaiv,    bbcm,   0,     bbcmaiv,    bbcm,   bbcm_state,   init_bbc,  "Acorn Computers",             "BBC Master AIV",                     MACHINE_NOT_WORKING )
COMP( 1986, bbcmet,     bbcm,   0,     bbcmet,     bbcm,   bbcm_state,   init_bbc,  "Acorn Computers",             "BBC Master ET",                      MACHINE_IMPERFECT_GRAPHICS )
COMP( 1986, bbcm512,    bbcm,   0,     bbcm512,    bbcm,   bbcm_state,   init_bbc,  "Acorn Computers",             "BBC Master 512",                     MACHINE_IMPERFECT_GRAPHICS )
COMP( 1986, bbcmarm,    bbcm,   0,     bbcmarm,    bbcm,   bbcm_state,   init_bbc,  "Acorn Computers",             "BBC Master (ARM Evaluation)",        MACHINE_NOT_WORKING )

// TV Production
COMP( 1987, mpc800,     bbcm,   0,     mpc800,     bbcm,   bbcm_state,   init_bbc,  "G2 Systems",                  "MasterPieCe 800 Series",             MACHINE_NOT_WORKING )
COMP( 1988, mpc900,     bbcm,   0,     mpc900,     bbcm,   bbcm_state,   init_bbc,  "G2 Systems",                  "MasterPieCe 900 Series",             MACHINE_NOT_WORKING )
COMP( 1990, mpc900gx,   bbcm,   0,     mpc900gx,   bbcm,   bbcm_state,   init_bbc,  "G2 Systems",                  "MasterPieCe 900GX Series",           MACHINE_NOT_WORKING )
COMP( 1987, se3010,     bbcm,   0,     se3010,     bbcm,   bbcm_state,   init_se,   "Softel Electronics",          "SE3010 Teletext Editing Terminal",   MACHINE_NOT_WORKING )

// Jukeboxes
//COMP( 1988, discmast,   bbcm,   0,     discmast,   bbcm,   bbcm_state,   init_bbc,  "Arbiter Leisure",             "Arbiter Discmaster A-00",            MACHINE_NOT_WORKING )
COMP( 1988, discmon,    bbcm,   0,     discmon,    bbcm,   bbcm_state,   init_bbc,  "Arbiter Leisure",             "Arbiter Discmonitor A-01",           MACHINE_NOT_WORKING )
COMP( 1988, discmate,   bbcm,   0,     discmate,   bbcm,   bbcm_state,   init_bbc,  "Arbiter Leisure",             "Arbiter Discmate A-02",              MACHINE_NOT_WORKING )

// Industrial
COMP( 1986, ltmpm,      bbcm,   0,     bbcm,       0,      bbcm_state,   init_ltmp, "Lawrie T&M Ltd.",             "LTM Portable (Master)",              MACHINE_IMPERFECT_GRAPHICS )
COMP( 1987, daisy,      bbcm,   0,     daisy,      bbcm,   bbcm_state,   init_bbc,  "Comus Instruments Ltd.",      "Comus Daisy",                        MACHINE_NOT_WORKING )
COMP( 1989, cfa3000,    bbcm,   0,     cfa3000,    bbcm,   bbcm_state,   init_cfa,  "Tinsley Medical Instruments", "Henson CFA 3000 (Master)",           MACHINE_NOT_WORKING )
COMP( 1992, ht280,      bbcm,   0,     ht280,      0,      bbcm_state,   init_bbc,  "T.S.Harrison",                "Harrison Trainer 280 CNC/Manual",    MACHINE_NOT_WORKING )



bbcmc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/******************************************************************************

    BBC Master Compact

    ADB20 - Master Compact

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

#include "emu.h"
#include "bbc.h"
#include "acorn_serproc.h"
#include "bbc_kbd.h"

#include "bus/bbc/exp/exp.h"
#include "bus/bbc/joyport/joyport.h"
#include "cpu/m6502/g65sc02.h"
#include "machine/i2cmem.h"
#include "machine/wd_fdc.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "bbcm.lh"


namespace {

class bbcmc_state : public bbc_state
{
public:
	bbcmc_state(const machine_config &mconfig, device_type type, const char *tag)
		: bbc_state(mconfig, type, tag)
		, m_view_lynne(*this, "view_lynne")
		, m_view_hazel(*this, "view_hazel")
		, m_view_andy(*this, "view_andy")
		, m_view_tst(*this, "view_tst")
		, m_kbd(*this, "kbd")
		, m_sn(*this, "sn")
		, m_i2cmem(*this, "i2cmem")
		, m_wdfdc(*this, "wdfdc")
		, m_adlc(*this, "mc6854")
		, m_joyport(*this, "joyport")
		, m_power_led(*this, "power_led")
	{ }

	void bbcmc(machine_config &config);
	void bbcmc_ar(machine_config &config);
	void autoc15(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	memory_view m_view_lynne;
	memory_view m_view_hazel;
	memory_view m_view_andy;
	memory_view m_view_tst;
	required_device<bbc_kbd_device> m_kbd;
	required_device<sn76489a_device> m_sn;
	required_device<i2cmem_device> m_i2cmem;
	required_device<wd1772_device> m_wdfdc;
	required_device<mc6854_device> m_adlc;
	required_device<bbc_joyport_slot_device> m_joyport;
	output_finder<> m_power_led;

	void bbcmc_fetch(address_map &map) ATTR_COLD;
	void bbcmc_mem(address_map &map) ATTR_COLD;
	void bbcmc_io(address_map &map) ATTR_COLD;
	void autoc15_mem(address_map &map) ATTR_COLD;
	void autoc15_io(address_map &map) ATTR_COLD;

	void trigger_reset(int state);

	uint8_t fetch_r(offs_t offset);
	uint8_t acccon_r();
	void acccon_w(uint8_t data);
	uint8_t romsel_r();
	void romsel_w(offs_t offset, uint8_t data);
	uint8_t paged_r(offs_t offset);
	void paged_w(offs_t offset, uint8_t data);
	void drive_control_w(uint8_t data);

	uint8_t sysvia_pa_r();
	void sysvia_pa_w(uint8_t data);
	void update_sdb();
	uint8_t sysvia_pb_r();
	void sysvia_pb_w(uint8_t data);
	uint8_t joyport_r();
	void joyport_w(uint8_t data);

	uint8_t m_sdb = 0x00;
	uint8_t m_acccon = 0x00;

	enum
	{
		ACCCON_D = 0,
		ACCCON_E,
		ACCCON_X,
		ACCCON_Y,
		ACCCON_ITU,
		ACCCON_IFJ,
		ACCCON_TST,
		ACCCON_IRR
	};
};


void bbcmc_state::machine_start()
{
	bbc_state::machine_start();

	m_power_led.resolve();

	save_item(NAME(m_acccon));
	save_item(NAME(m_sdb));
}


void bbcmc_state::machine_reset()
{
	m_power_led = 0;
}


void bbcmc_state::bbcmc_fetch(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(bbcmc_state::fetch_r));
}


void bbcmc_state::bbcmc_mem(address_map &map)
{
	map(0x0000, 0x7fff).rw(m_ram, FUNC(ram_device::read), FUNC(ram_device::write));                                    //    0000-7FFF                 Regular RAM
	map(0x3000, 0x7fff).view(m_view_lynne);                                                                            //    3000-7FFF                 20K Shadow RAM LYNNE
	m_view_lynne[0](0x3000, 0x7fff).lr8(NAME([this] (offs_t offset) { return m_ram->pointer()[offset + 0xb000]; }));
	m_view_lynne[0](0x3000, 0x7fff).lw8(NAME([this] (offs_t offset, uint8_t data) { m_ram->pointer()[offset + 0xb000] = data; }));
	map(0x8000, 0xbfff).rw(FUNC(bbcmc_state::paged_r), FUNC(bbcmc_state::paged_w));                                    //    8000-8FFF                 Paged ROM/RAM
	map(0x8000, 0x8fff).view(m_view_andy);                                                                             //    8000-8FFF                 4K RAM ANDY
	m_view_andy[0](0x8000, 0x8fff).lr8(NAME([this] (offs_t offset) { return m_ram->pointer()[offset + 0x8000]; }));
	m_view_andy[0](0x8000, 0x8fff).lw8(NAME([this] (offs_t offset, uint8_t data) { m_ram->pointer()[offset + 0x8000] = data; }));
	map(0xc000, 0xffff).rw(FUNC(bbcmc_state::mos_r), FUNC(bbcmc_state::mos_w));                                        //    C000-FFFF                 OS ROM
	map(0xc000, 0xdfff).view(m_view_hazel);                                                                            //    C000-DFFF                 8K RAM HAZEL
	m_view_hazel[0](0xc000, 0xdfff).lr8(NAME([this](offs_t offset) { return m_ram->pointer()[offset + 0x9000]; }));
	m_view_hazel[0](0xc000, 0xdfff).lw8(NAME([this](offs_t offset, uint8_t data) { m_ram->pointer()[offset + 0x9000] = data; }));
	map(0xfc00, 0xfeff).m(FUNC(bbcmc_state::bbcmc_io));                                                                //    FC00-FFFF                 OS ROM or hardware IO
	map(0xfc00, 0xfeff).view(m_view_tst);
	m_view_tst[0](0xfc00, 0xfeff).m(FUNC(bbcmc_state::bbcmc_io));
	m_view_tst[0](0xfc00, 0xfeff).lr8(NAME([this](offs_t offset) { return mos_r(0x3c00 + offset); }));
}

void bbcmc_state::bbcmc_io(address_map &map)
{
	map(0x0000, 0x00ff).rw(m_exp, FUNC(bbc_exp_slot_device::fred_r), FUNC(bbc_exp_slot_device::fred_w));               //    FC00-FCFF  Compact        FRED Address Page
	map(0x0100, 0x01ff).rw(m_exp, FUNC(bbc_exp_slot_device::jim_r), FUNC(bbc_exp_slot_device::jim_w));                 //    FD00-FDFF  Compact        JIM Address Page
	map(0x0200, 0x02ff).lr8(NAME([]() { return 0xfe; })).nopw();                                                       //    FE00-FEFF                 SHEILA Address Page
	map(0x0200, 0x0200).mirror(0x06).rw(m_crtc, FUNC(hd6845s_device::status_r), FUNC(hd6845s_device::address_w));      //    FE00-FE07  6845 CRTC      Video controller
	map(0x0201, 0x0201).mirror(0x06).rw(m_crtc, FUNC(hd6845s_device::register_r), FUNC(hd6845s_device::register_w));
	map(0x0208, 0x0209).mirror(0x06).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));            //    FE08-FE0F  6850 ACIA      Serial controller
	map(0x0210, 0x0217).rw("serproc", FUNC(acorn_serproc_device::read), FUNC(acorn_serproc_device::write));            //    FE10-FE17  Serial ULA     Serial system chip
	map(0x0220, 0x0223).w(FUNC(bbcmc_state::video_ula_w));                                                             // W: FE20-FE23  Video ULA      Video system chip
	map(0x0224, 0x0227).w(FUNC(bbcmc_state::drive_control_w));                                                         // W: FE24-FE27  FDC Latch      1772 Control latch
	map(0x0228, 0x022f).rw(m_wdfdc, FUNC(wd1772_device::read), FUNC(wd1772_device::write));                            //    FE28-FE2F  1772 FDC       Floppy disc controller
	map(0x0230, 0x0233).rw(FUNC(bbcmc_state::romsel_r), FUNC(bbcmc_state::romsel_w));                                  //    FE30-FE33  ROMSEL         ROM Select
	map(0x0234, 0x0237).rw(FUNC(bbcmc_state::acccon_r), FUNC(bbcmc_state::acccon_w));                                  //    FE34-FE37  ACCCON         ACCCON select register
	map(0x0238, 0x023b).lr8(NAME([this]() { econet_int_enable(0); return 0xfe; }));                                    // R: FE38-FE3B  INTOFF         ECONET Interrupt Off
	map(0x0238, 0x023b).lw8(NAME([this](uint8_t data) { econet_int_enable(0); }));                                     // W: FE38-FE3B  INTOFF         ECONET Interrupt Off
	map(0x023c, 0x023f).lr8(NAME([this]() { econet_int_enable(1); return 0xfe; }));                                    // R: FE3C-FE3F  INTON          ECONET Interrupt On
	map(0x023c, 0x023f).lw8(NAME([this](uint8_t data) { econet_int_enable(1); }));                                     // W: FE3C-FE3F  INTON          ECONET Interrupt On
	map(0x0240, 0x024f).mirror(0x10).m(m_sysvia, FUNC(via6522_device::map));                                           //    FE40-FE5F  6522 VIA       SYSTEM VIA
	map(0x0260, 0x026f).mirror(0x10).m(m_uservia, FUNC(via6522_device::map));                                          //    FE60-FE7F  6522 VIA       USER VIA
	map(0x0280, 0x029f).lr8(NAME([]() { return 0xfe; })).nopw();                                                       //    FE80-FE9F  Int. Modem     Int. Modem
	map(0x02a0, 0x02a3).mirror(0x1c).rw(m_adlc, FUNC(mc6854_device::read), FUNC(mc6854_device::write));                //    FEA0-FEBF  68B54 ADLC     ECONET controller
	map(0x02e0, 0x02ff).lr8(NAME([]() { return 0xfe; })).nopw();                                                       //    FEE0-FEFF  Tube ULA       Tube system interface
}


void bbcmc_state::autoc15_mem(address_map &map)
{
	bbcmc_mem(map);
	map(0xfc00, 0xfeff).m(FUNC(bbcmc_state::autoc15_io));                                                              //    FC00-FFFF                 OS ROM or hardware IO
	m_view_tst[0](0xfc00, 0xfeff).m(FUNC(bbcmc_state::autoc15_io));
	m_view_tst[0](0xfc00, 0xfeff).lr8(NAME([this](offs_t offset) { return mos_r(0x3c00 + offset); }));
}

void bbcmc_state::autoc15_io(address_map &map)
{
	bbcmc_io(map);
	map(0x0200, 0x0200).mirror(0x06).rw(m_crtc, FUNC(hd6345_device::status_r), FUNC(hd6345_device::address_w));        //    FE00-FE07  6345 CRTC      Video controller
	map(0x0201, 0x0201).mirror(0x06).rw(m_crtc, FUNC(hd6345_device::register_r), FUNC(hd6345_device::register_w));
}


uint8_t bbcmc_state::fetch_r(offs_t offset)
{
	if (BIT(m_acccon, ACCCON_X) || (BIT(m_acccon, ACCCON_E) && offset >= 0xc000 && offset <= 0xdfff))
		m_view_lynne.select(0);
	else
		m_view_lynne.disable();

	return m_maincpu->space(AS_PROGRAM).read_byte(offset);
}


uint8_t bbcmc_state::romsel_r()
{
	return m_romsel;
}

void bbcmc_state::romsel_w(offs_t offset, uint8_t data)
{
	// ROMSEL - FE30 read/write register
	//  b7 RAM 1 = Page in ANDY 8000-8FFF
	//         0 = Page in ROM  8000-8FFF
	//  b6     Not Used
	//  b5     Not Used
	//  b4     Not Used
	//  b3-b0  ROM/RAM Bank Select
	if (BIT(data, 7))
		m_view_andy.select(0);
	else
		m_view_andy.disable();

	m_romsel = data & 0x0f;
}


uint8_t bbcmc_state::acccon_r()
{
	return m_acccon;
}

void bbcmc_state::acccon_w(uint8_t data)
{
	// ACCCON - FE34 read/write register
	//  b7 IRR 1 = Causes an IRQ to the processor
	//  b6 TST 1 = Selects FC00-FEFF read from OS-ROM
	//  b5 IFJ 1 = Internal 1MHz bus
	//         0 = External 1MHz bus
	//  b4 ITU 1 = Internal Tube
	//         0 = External Tube
	//  b3 Y   1 = Read/Write HAZEL C000-DFFF RAM
	//         0 = Read/Write ROM C000-DFFF OS-ROM
	//  b2 X   1 = Read/Write LYNNE
	//         0 = Read/Write main memory 3000-8000
	//  b1 E   1 = Causes shadow if VDU code
	//         0 = Main all the time
	//  b0 D   1 = Display LYNNE as screen
	//         0 = Display main RAM screen
	m_acccon = data;

	// Bit IRR causes Interrupt Request.
	m_irqs->in_w<4>(BIT(m_acccon, ACCCON_IRR));

	// Bit D causes the CRT controller to display the contents of LYNNE.
	setvideoshadow(BIT(m_acccon, ACCCON_D));

	// Bit Y causes 8 Kbyte of RAM referred to as HAZEL to be overlayed on the MOS VDU drivers.
	if (BIT(m_acccon, ACCCON_Y))
		m_view_hazel.select(0);
	else
		m_view_hazel.disable();

	// Bit X causes all accesses to 3000-7fff to be re-directed to LYNNE.
	if (BIT(m_acccon, ACCCON_X))
		m_view_lynne.select(0);
	else
		m_view_lynne.disable();

	// Bit TST controls paging of ROM reads in the 0xfc00-0xfeff region
	// if 0 the I/O is paged for both reads and writes
	// if 1 the ROM is paged in for reads but writes still go to I/O
	if (BIT(m_acccon, ACCCON_TST))
		m_view_tst.select(0);
	else
		m_view_tst.disable();
}


uint8_t bbcmc_state::paged_r(offs_t offset)
{
	uint8_t data = 0xff;

	switch (m_romsel)
	{
	case 0: case 1:
		// 32K socket or External (selected by link PL11)
		if (m_rom[m_romsel & 0x0e] && m_rom[m_romsel & 0x0e]->present())
		{
			data = m_rom[m_romsel & 0x0e]->read(offset | (m_romsel & 0x01) << 14);
		}
		else
		{
			data  = m_exp->rom_r(offset | (m_romsel & 0x01) << 14);
			data &= m_region_rom->base()[offset + (m_romsel << 14)];
		}
		break;

	case 4: case 5: case 6: case 7:
		// 32K sockets
		if (m_rom[m_romsel & 0x0e] && m_rom[m_romsel & 0x0e]->present())
		{
			data = m_rom[m_romsel & 0x0e]->read(offset | (m_romsel & 0x01) << 14);
		}
		else
		{
			data = m_region_rom->base()[offset + (m_romsel << 14)];
		}
		break;

	default:
		// 16K sockets
		if (m_rom[m_romsel] && m_rom[m_romsel]->present())
		{
			data = m_rom[m_romsel]->read(offset);
		}
		else
		{
			data = m_region_rom->base()[offset + (m_romsel << 14)];
		}
		break;
	}

	return data;
}

void bbcmc_state::paged_w(offs_t offset, uint8_t data)
{
	switch (m_romsel)
	{
	case 0: case 1:
		// 32K socket or External (selected by link PL11)
		if (m_rom[m_romsel & 0x0e] && m_rom[m_romsel & 0x0e]->present())
		{
			m_rom[m_romsel & 0x0e]->write(offset | (m_romsel & 0x01) << 14, data);
		}
		else
		{
			m_exp->rom_w(offset | (m_romsel & 0x01) << 14, data);
		}
		break;

	case 4: case 5: case 6: case 7:
		// 32K sockets
		if (m_rom[m_romsel & 0x0e])
		{
			m_rom[m_romsel & 0x0e]->write(offset | (m_romsel & 0x01) << 14, data);
		}
		break;

	default:
		// 16K sockets
		if (m_rom[m_romsel])
		{
			m_rom[m_romsel]->write(offset, data);
		}
		break;
	}
}


uint8_t bbcmc_state::sysvia_pa_r()
{
	update_sdb();

	return m_sdb;
}

void bbcmc_state::sysvia_pa_w(uint8_t data)
{
	m_sdb = data;

	update_sdb();
}


void bbcmc_state::update_sdb()
{
	uint8_t const latch = m_latch->output_state();

	// sound
	if (!BIT(latch, 0))
		m_sn->write(m_sdb);

	// keyboard
	m_sdb = m_kbd->read(m_sdb);
}


uint8_t bbcmc_state::sysvia_pb_r()
{
	uint8_t data = 0xff;

	data &= ~0x30;
	data |= m_i2cmem->read_sda() << 4;

	return data;
}

void bbcmc_state::sysvia_pb_w(uint8_t data)
{
	m_latch->write_nibble_d3(data);

	m_i2cmem->write_sda(BIT(data, 4));
	m_i2cmem->write_scl(BIT(data, 5));

	update_sdb();
}


uint8_t bbcmc_state::joyport_r()
{
	uint8_t data = 0xff;

	if (m_joyport->get_card_device())
	{
		data = m_joyport->pb_r();
	}
	else if (m_exp->get_card_device())
	{
		// Mertec Companion also connects to joystick port
		data = m_exp->pb_r();
	}

	return data;
}

void bbcmc_state::joyport_w(uint8_t data)
{
	if (m_joyport->get_card_device())
	{
		m_joyport->pb_w(data);
	}
	else if (m_exp->get_card_device())
	{
		// Mertec Companion also connects to joystick port
		m_exp->pb_w(data);
	}
}


void bbcmc_state::drive_control_w(uint8_t data)
{
	// Bit       Meaning
	// -----------------
	// 7,6       Not used
	//  5        Double density select (0 = double, 1 = single)
	//  4        Side select (0 = side 0, 1 = side 1)
	//  3        Drive select 2
	//  2        Reset drive controller chip. (0 = reset controller, 1 = no reset)
	//  1        Drive select 1
	//  0        Drive select 0

	floppy_image_device *floppy = nullptr;

	// bit 0, 1, 3: drive select
	if (BIT(data, 0)) floppy = m_wdfdc->subdevice<floppy_connector>("0")->get_device();
	if (BIT(data, 1)) floppy = m_wdfdc->subdevice<floppy_connector>("1")->get_device();
	m_wdfdc->set_floppy(floppy);

	// bit 4: side select
	if (floppy)
		floppy->ss_w(BIT(data, 4));

	// bit 5: density
	m_wdfdc->dden_w(BIT(data, 5));

	// bit 2: reset
	m_wdfdc->mr_w(BIT(data, 2));
}


void bbcmc_state::trigger_reset(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? CLEAR_LINE : ASSERT_LINE);

	if (!state)
	{
		if (m_uservia) m_uservia->reset();
		if (m_adlc) m_adlc->reset();
		if (m_wdfdc) m_wdfdc->reset();
		if (m_exp) m_exp->reset();
	}
}


static INPUT_PORTS_START(bbcm)
	PORT_INCLUDE(bbc_config)
INPUT_PORTS_END


static void bbc_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
}


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

    BBC Master Compact

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

void bbcmc_state::bbcmc(machine_config &config)
{
	G65SC12(config, m_maincpu, 16_MHz_XTAL / 8);
	m_maincpu->set_addrmap(AS_PROGRAM, &bbcmc_state::bbcmc_mem);
	m_maincpu->set_addrmap(AS_OPCODES, &bbcmc_state::bbcmc_fetch);

	INPUT_MERGER_ANY_HIGH(config, m_irqs).output_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	RAM(config, m_ram).set_default_size("128K");

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(16_MHz_XTAL, 1024, 0, 640, 312, 0, 256);
	m_screen->set_screen_update("crtc", FUNC(hd6845s_device::screen_update));

	PALETTE(config, m_palette).set_entries(16);

	SAA5050(config, m_trom, 12_MHz_XTAL / 2);

	HD6845S(config, m_crtc, 16_MHz_XTAL / 8);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_reconfigure_callback(FUNC(bbcmc_state::crtc_reconfigure));
	m_crtc->set_update_row_callback(FUNC(bbcmc_state::crtc_update_row));
	m_crtc->out_de_callback().set(FUNC(bbcmc_state::de_changed));
	m_crtc->out_hsync_callback().set(FUNC(bbcmc_state::hsync_changed));
	m_crtc->out_vsync_callback().set(FUNC(bbcmc_state::vsync_changed));

	config.set_default_layout(layout_bbcm);

	LS259(config, m_latch);
	m_latch->q_out_cb<3>().set(m_kbd, FUNC(bbc_kbd_device::write_kb_en));
	m_latch->q_out_cb<6>().set_output("capslock_led");
	m_latch->q_out_cb<7>().set_output("shiftlock_led");

	MOS6522(config, m_sysvia, 16_MHz_XTAL / 16);
	m_sysvia->readpa_handler().set(FUNC(bbcmc_state::sysvia_pa_r));
	m_sysvia->writepa_handler().set(FUNC(bbcmc_state::sysvia_pa_w));
	m_sysvia->readpb_handler().set(FUNC(bbcmc_state::sysvia_pb_r));
	m_sysvia->writepb_handler().set(FUNC(bbcmc_state::sysvia_pb_w));
	m_sysvia->cb2_handler().set([this](int state) { if (state) m_crtc->assert_light_pen_input(); });
	m_sysvia->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<0>));

	BBCMC_KBD(config, m_kbd, 16_MHz_XTAL / 16);
	m_kbd->ca2_handler().set(m_sysvia, FUNC(via6522_device::write_ca2));
	m_kbd->rst_handler().set(FUNC(bbcmc_state::trigger_reset));

	SPEAKER(config, "mono").front_center();

	SN76489A(config, m_sn, 16_MHz_XTAL / 4);
	m_sn->add_route(ALL_OUTPUTS, "mono", 1.0);

	acia6850_device &acia(ACIA6850(config, "acia"));
	acia.txd_handler().set("serproc", FUNC(acorn_serproc_device::write_txd));
	acia.txd_handler().append("rs423", FUNC(rs232_port_device::write_txd));
	acia.rts_handler().set("serproc", FUNC(acorn_serproc_device::write_rtsi));
	acia.rts_handler().append("rs423", FUNC(rs232_port_device::write_rts));
	acia.irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<2>));

	acorn_serproc_device &serproc(ACORN_SERPROC(config, "serproc", 16_MHz_XTAL / 13));
	serproc.rxc_handler().set("acia", FUNC(acia6850_device::write_rxc));
	serproc.txc_handler().set("acia", FUNC(acia6850_device::write_txc));
	serproc.dcd_handler().set("acia", FUNC(acia6850_device::write_dcd));

	rs232_port_device &rs423(RS232_PORT(config, "rs423", default_rs232_devices, nullptr));
	rs423.rxd_handler().set("acia", FUNC(acia6850_device::write_rxd));
	rs423.cts_handler().set("acia", FUNC(acia6850_device::write_cts));

	I2C_PCD8572(config, "i2cmem", 0);

	centronics_device &centronics(CENTRONICS(config, "printer", centronics_devices, "printer"));
	centronics.ack_handler().set(m_uservia, FUNC(via6522_device::write_ca1));
	output_latch_device &latch(OUTPUT_LATCH(config, "cent_data_out"));
	centronics.set_output_latch(latch);

	MOS6522(config, m_uservia, 16_MHz_XTAL / 16);
	m_uservia->writepa_handler().set("cent_data_out", FUNC(output_latch_device::write));
	m_uservia->readpb_handler().set(FUNC(bbcmc_state::joyport_r)).mask(0x1f);
	m_uservia->readpb_handler().append(m_exp, FUNC(bbc_exp_slot_device::pb_r)).mask(0xe0);
	m_uservia->writepb_handler().set(FUNC(bbcmc_state::joyport_w)).mask(0x1f);
	m_uservia->writepb_handler().append(m_exp, FUNC(bbc_exp_slot_device::pb_w)).mask(0xe0);
	m_uservia->ca2_handler().set("printer", FUNC(centronics_device::write_strobe));
	m_uservia->cb1_handler().set(m_joyport, FUNC(bbc_joyport_slot_device::write_cb1));
	m_uservia->cb2_handler().set(m_joyport, FUNC(bbc_joyport_slot_device::write_cb2));
	m_uservia->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<1>));
	// appended for Mertec Companion that also connects to joystick port
	m_uservia->cb1_handler().append(m_exp, FUNC(bbc_exp_slot_device::write_cb1));
	m_uservia->cb2_handler().append(m_exp, FUNC(bbc_exp_slot_device::write_cb2));

	WD1772(config, m_wdfdc, 16_MHz_XTAL / 2);
	m_wdfdc->intrq_wr_callback().set(FUNC(bbcmc_state::fdc_intrq_w));
	m_wdfdc->drq_wr_callback().set(FUNC(bbcmc_state::fdc_drq_w));

	FLOPPY_CONNECTOR(config, "wdfdc:0", bbc_floppies, "35dd", bbc_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "wdfdc:1", bbc_floppies, nullptr, bbc_state::floppy_formats).enable_sound(true);

	MC6854(config, m_adlc);
	m_adlc->out_txd_cb().set("network", FUNC(econet_device::host_data_w));
	m_adlc->out_irq_cb().set(FUNC(bbcmc_state::adlc_irq_w));

	econet_device &econet(ECONET(config, "network", 0));
	econet.clk_wr_callback().set(m_adlc, FUNC(mc6854_device::txc_w));
	econet.clk_wr_callback().append(m_adlc, FUNC(mc6854_device::rxc_w));
	econet.clk_wr_callback().append(m_adlc, FUNC(mc6854_device::set_cts)).invert();
	econet.data_wr_callback().set(m_adlc, FUNC(mc6854_device::set_rx));
	ECONET_SLOT(config, "econet", "network", econet_devices);

	BBC_EXP_SLOT(config, m_exp, 16_MHz_XTAL / 2, bbc_exp_devices, nullptr);
	m_exp->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<3>));
	m_exp->nmi_handler().set(FUNC(bbcmc_state::bus_nmi_w));
	m_exp->lpstb_handler().set(m_sysvia, FUNC(via6522_device::write_cb2));
	m_exp->lpstb_handler().append([this](int state) { if (state) m_crtc->assert_light_pen_input(); });
	// CB handlers for Mertec device that also plugs into joystick port.
	m_exp->cb1_handler().set(m_uservia, FUNC(via6522_device::write_cb1));
	m_exp->cb2_handler().set(m_uservia, FUNC(via6522_device::write_cb2));

	BBC_JOYPORT_SLOT(config, m_joyport, bbc_joyport_devices, nullptr);
	m_joyport->cb1_handler().set(m_uservia, FUNC(via6522_device::write_cb1));
	m_joyport->cb2_handler().set(m_uservia, FUNC(via6522_device::write_cb2));

	BBC_ROMSLOT16(config, m_rom[0x03], bbc_rom_devices, nullptr); // IC17
	BBC_ROMSLOT16(config, m_rom[0x02], bbc_rom_devices, nullptr); // IC23
	BBC_ROMSLOT32(config, m_rom[0x00], bbc_rom_devices, nullptr); // IC38
	BBC_ROMSLOT16(config, m_rom[0x08], bbc_rom_devices, nullptr); // IC29
	BBC_ROMSLOT32(config, m_rom[0x04], bbc_rom_devices, "ram").set_fixed_ram(true); // IC41 32K socket
	BBC_ROMSLOT32(config, m_rom[0x06], bbc_rom_devices, "ram").set_fixed_ram(true); // IC37 32K socket

	SOFTWARE_LIST(config, "flop_ls_mc").set_original("bbcmc_flop");
	SOFTWARE_LIST(config, "flop_ls_128s").set_original("pro128s_flop");
	SOFTWARE_LIST(config, "flop_ls_m").set_compatible("bbcm_flop");
	SOFTWARE_LIST(config, "flop_ls_b").set_compatible("bbcb_flop");
	SOFTWARE_LIST(config, "flop_ls_b_orig").set_compatible("bbcb_flop_orig");
	SOFTWARE_LIST(config, "cart_ls_m").set_original("bbcm_cart");
	SOFTWARE_LIST(config, "rom_ls").set_original("bbc_rom").set_filter("M");
}


void bbcmc_state::bbcmc_ar(machine_config &config)
{
	bbcmc(config);

	BBCMC_KBD_AR(config.replace(), m_kbd, 16_MHz_XTAL / 16);
	m_kbd->ca2_handler().set(m_sysvia, FUNC(via6522_device::write_ca2));
	m_kbd->rst_handler().set(FUNC(bbcmc_state::trigger_reset));
}


void bbcmc_state::autoc15(machine_config &config)
{
	bbcmc(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &bbcmc_state::autoc15_mem);

	AUTOC15_KBD(config.replace(), m_kbd, 16_MHz_XTAL / 16);
	m_kbd->ca2_handler().set(m_sysvia, FUNC(via6522_device::write_ca2));
	m_kbd->rst_handler().set(FUNC(bbcmc_state::trigger_reset));

	// replaces HD6845 to support smooth scrolling
	HD6345(config.replace(), m_crtc, 16_MHz_XTAL / 8);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_reconfigure_callback(FUNC(bbcmc_state::crtc_reconfigure));
	m_crtc->set_update_row_callback(FUNC(bbcmc_state::crtc_update_row));
	m_crtc->out_de_callback().set(FUNC(bbcmc_state::de_changed));
	m_crtc->out_hsync_callback().set(FUNC(bbcmc_state::hsync_changed));
	m_crtc->out_vsync_callback().set(FUNC(bbcmc_state::vsync_changed));

	// Autocue RAM disc
	m_exp->set_default_option("autocue").set_fixed(true);
}


ROM_START(bbcmc)
	// page 0  00000  IC38 or EXTERNAL               // page 8  20000  IC29
	// page 1  04000  IC38 or EXTERNAL               // page 9  24000  unused
	// page 2  08000  IC23                           // page 10 28000  unused
	// page 3  0c000  IC17                           // page 11 2c000  unused
	// page 4  10000  SWRAM                          // page 12 30000  unused
	// page 5  14000  SWRAM                          // page 13 34000  IC49 ADFS
	// page 6  18000  SWRAM                          // page 14 38000  IC49 BASIC
	// page 7  1c000  SWRAM                          // page 15 3c000  IC49 Utils
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "510", "MOS 5.10")
	ROMX_LOAD("mos510.ic49", 0x30000, 0x10000, CRC(9a2a6086) SHA1(094ab37b0b6437c4f1653eaa0602ef102737adb6), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "500", "MOS 5.00")
	ROMX_LOAD("mos500.ic49", 0x30000, 0x10000, CRC(f6170023) SHA1(140d002d2d9cd34b47197a2ba823505af2a84633), ROM_BIOS(1))

	ROM_COPY("rom", 0x30000, 0x40000, 0x4000) // Move loaded roms into place
	ROM_FILL(0x30000, 0x4000, 0xff)

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)
ROM_END


ROM_START(bbcmc_ar)
	// page 0  00000  IC38 or EXTERNAL               // page 8  20000  IC29 Arabian
	// page 1  04000  IC38 or EXTERNAL               // page 9  24000  unused
	// page 2  08000  IC23 International             // page 10 28000  unused
	// page 3  0c000  IC17                           // page 11 2c000  unused
	// page 4  10000  SWRAM                          // page 12 30000  unused
	// page 5  14000  SWRAM                          // page 13 34000  IC49 ADFS
	// page 6  18000  SWRAM                          // page 14 38000  IC49 BASIC
	// page 7  1c000  SWRAM                          // page 15 3c000  IC49 Utils
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "511i", "MOS 5.11i")
	ROMX_LOAD("mos511.ic49", 0x30000, 0x10000, CRC(8708803c) SHA1(d2170c8b9b536f3ad84a4a603a7fe712500cc751), ROM_BIOS(0))

	ROM_COPY("rom", 0x30000, 0x40000, 0x4000) // Move loaded roms into place
	ROM_FILL(0x30000, 0x4000, 0xff)

	ROM_LOAD("international16.rom", 0x08000, 0x4000, CRC(0ef527b1) SHA1(dc5149ccf588cd591a6ad47727474ef3313272ce) )
	ROM_LOAD("arabian-c22.rom"    , 0x20000, 0x4000, CRC(4f3aadff) SHA1(2bbf61ba68264ce5845aab9c54e750b0efe219c8) )

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)
ROM_END


ROM_START(pro128s)
	// page 0  00000  IC38 or EXTERNAL               // page 8  20000  IC29
	// page 1  04000  IC38 or EXTERNAL               // page 9  24000  unused
	// page 2  08000  IC23                           // page 10 28000  unused
	// page 3  0c000  IC17                           // page 11 2c000  unused
	// page 4  10000  SWRAM                          // page 12 30000  unused
	// page 5  14000  SWRAM                          // page 13 34000  IC49 ADFS
	// page 6  18000  SWRAM                          // page 14 38000  IC49 BASIC
	// page 7  1c000  SWRAM                          // page 15 3c000  IC49 Utils
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "i510c", "MOS I5.10C")
	ROMX_LOAD("mos510o.ic49", 0x30000, 0x10000, CRC(c16858d3) SHA1(ad231ed21a55e493b553703285530d1cacd3de7a), ROM_BIOS(0)) // System ROM 0258,211-01

	ROM_COPY("rom", 0x30000, 0x40000, 0x4000) // Move loaded roms into place
	ROM_FILL(0x30000, 0x4000, 0xff)

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)
ROM_END


ROM_START(autoc15)
	// page 0  00000  IC38 SBII                      // page 8  20000  IC29 MODROM 0.47
	// page 1  04000  IC38 SBII                      // page 9  24000  IC49 ADFS
	// page 2  08000  IC23 APROMPT                   // page 10 28000  IC49 BASIC
	// page 3  0c000  IC17 Swedish 16K ISO           // page 11 2c000  IC49 Autocue Giant
	// page 4  10000  SWRAM                          // page 12 30000  IC49 Autocue Large
	// page 5  14000  SWRAM                          // page 13 34000  IC49 Autocue Medium
	// page 6  18000  SWRAM                          // page 14 38000  IC49 Autocue Small
	// page 7  1c000  SWRAM                          // page 15 3c000  IC49 Utils
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "510i", "MOS 5.10i")
	ROMX_LOAD("swedish_mega_29-1.ic49", 0x20000, 0x20000, CRC(67512992) SHA1(5d04b6e53a3a75af22ab10c652cceb9a63b23a6d), ROM_BIOS(0))

	ROM_COPY("rom", 0x20000, 0x40000, 0x4000) // Move loaded roms into place
	ROM_FILL(0x20000, 0x4000, 0xff)

	ROM_LOAD("sbii-25-1-88.ic38",    0x00000, 0x8000, CRC(36af3215) SHA1(16d39f15b10b4e23e76bad23a53b4111ce877bc1))
	ROM_LOAD("aprompt.ic23",         0x08000, 0x4000, NO_DUMP)
	ROM_LOAD("swedish_16k_iso.ic17", 0x0c000, 0x4000, CRC(bd7716c0) SHA1(8a70f941f4de64d87e956e2086eb50287b8205b9))
	ROM_LOAD("modrom0_47.ic29",      0x20000, 0x4000, CRC(0d7874cb) SHA1(3f467f0b1618fb6546a2b94ca22b9f58d58bbdce))

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)
ROM_END

} // anonymous namespace


//    YEAR  NAME        PARENT  COMPAT MACHINE     INPUT   CLASS         INIT        COMPANY                        FULLNAME                              FLAGS
COMP( 1986, bbcmc,      0,      bbcm,  bbcmc,      bbcm,   bbcmc_state,   init_bbc,  "Acorn Computers",             "BBC Master Compact",                 MACHINE_IMPERFECT_GRAPHICS )
COMP( 1986, bbcmc_ar,   bbcmc,  0,     bbcmc_ar,   bbcm,   bbcmc_state,   init_bbc,  "Acorn Computers",             "BBC Master Compact (Arabic)",        MACHINE_IMPERFECT_GRAPHICS )
COMP( 1987, pro128s,    bbcmc,  0,     bbcmc,      bbcm,   bbcmc_state,   init_bbc,  "Olivetti",                    "Prodest PC 128S",                    MACHINE_IMPERFECT_GRAPHICS )

// TV Production
COMP( 1988, autoc15,    bbcmc,  0,     autoc15,    bbcm,   bbcmc_state,   init_bbc,  "Autocue Ltd.",                "Autocue 1500 Teleprompter",          MACHINE_NOT_WORKING )



bcs3.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

BCS 3

2009-05-12 Skeleton driver.
2015-09-25 Improvements

http://hc-ddr.hucki.net/wiki/doku.php/homecomputer:bcs3

East German home built computer. No sound facilities.
All documents are in German.

Main CPU is a U880 (Z80 equivalent). Other ICs also have unusual names.

The CTC sends an interrupt every so often. This uses a lookup table to
jump to an address in the range 38xx-39xx. This seems to work much the
same as reading video memory in the ZX80. This, I think, is to stop snow
appearing on the screen. It also slows everything down noticeably.

It appears that a read of 1400 activates the Z80's /WAIT pin. This will
be released by a VS pulse. (Not emulated)

System Clock
    - is not a crystal, but instead an LC oscillator at 5 MHz
    - frequency divided by 2 to form the CPU clock.
    - bcs3b increased oscillator frequency to 7 MHz to handle 40 characters per line

CTC channels
    - 0, 15625Hz, TV line sync
    - 1, 244Hz, scanline counter for character generator (976Hz on bcs3)
    - 2, 49Hz, TV frame sync
    - 3, 1Hz, not used
    During cassette save, ch 1 is disabled, which causes ch 2 and 3 to stop.
    Ch 0 will output pulses at a rate of 1220Hz while each bit is different from
    the last, or 2441Hz if bits are the same (1708 and 3417Hz for bcs3b).
    These pulses are fed to a flipflop to create a train of wide and narrow
    cycles, to be recorded onto the tape.

Cassette
    - is hooked up according to the documentation, but doesn't work.
    - on the real machine it overrides the video output, so the video will
      be lost during cassette operations.
    - does not exist on "bcs3". The commands SAVE and LOAD will attempt to
      use a non-existing rom at 0800, and crash the system.
    - When you start any cassette-based system it asks for the number of lines
      you'd like on the screen, or hit enter for the default. Depending on the
      number, the BASIC program starts in a different place. So, to LOAD a tape,
      you must have the same number of lines that the program was SAVED at.
    - Recordings are not compatible between the versions of BASIC.

Bugs:
    - Tell it to PRINT ! (or any character it doesn't understand) and the screen
      fills up with zeroes. String are enclosed in single quotes. Double quotes
      will cause this bug.
    - Undefined strings print as 0 instead of nothing.

Known Memory Map:
    0000 - 0FFF: Main ROM
    1000 - 13FF: Keyboard
    1400 - 17FF: /WAIT circuit
    1800 - 1BFF: Output one character's scanline to the monitor. ZX-video process.
    1C00 - 1FFF: Video RAM
    2000 - 3FFF: Mirror of 0000 - 1FFF.
    4000 - up  : Extra RAM (required for hack versions)

Hack versions:
    - They are fitted with Basic 3.x, require more RAM, and use hardware scrolling.
    - No schematic has been found, so the code is educated guesswork.
    - The ZX process is still to be worked out. For now, we return 0xF7 to finish.
    - There is a machine-language monitor fitted to some models. To access:
    -- Y = USR(0F000H)
    -- Commands are S (substitute), M (move), T (test), G (go), Q (quit)

To Do:
    - Need software
    - Fix cassette
    - Hack versions: fix the ZX process

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

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/z80daisy.h"
#include "machine/z80ctc.h"
#include "imagedev/cassette.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class bcs3_state : public driver_device
{
public:
	bcs3_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_ctc(*this, "ctc")
		, m_p_chargen(*this, "chargen")
		, m_p_videoram(*this, "videoram")
		, m_cass(*this, "cassette")
		, m_io_keyboard(*this, "KEY.%u", 0U)
	{ }

	void bcs3(machine_config &config);
	void bcs3a(machine_config &config);
	void bcs3b(machine_config &config);
	void init_bcs3a();
	void init_bcs3b();
	void init_bcs3c();
	void init_bcs3d();

private:
	u8 keyboard_r(offs_t offset);
	u8 video_r(offs_t offset);
	u8 zx_r();
	void ctc_z0_w(int state);
	void ctc_z1_w(int state);
	u32 screen_update_bcs3(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	u32 screen_update_bcs3a(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void bcs3_io(address_map &map) ATTR_COLD;
	void bcs3_mem(address_map &map) ATTR_COLD;
	void bcs3a_mem(address_map &map) ATTR_COLD;
	void machine_start() override ATTR_COLD;
	bool m_cassbit = 0;
	u8 s_curs = 0U;
	u8 s_init = 0U;
	u8 s_rows = 0U;
	u8 s_cols = 0U;

	required_device<z80_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<z80ctc_device> m_ctc;
	required_region_ptr<u8> m_p_chargen;
	required_shared_ptr<u8> m_p_videoram;
	optional_device<cassette_image_device> m_cass;
	required_ioport_array<10> m_io_keyboard;
};

u8 bcs3_state::keyboard_r(offs_t offset)
{
	u8 data = 0;

	if (offset == 0 && m_cass)
		data = (m_cass->input() > +0.01) ? 0x80 : 0;

	offset ^= 0x3ff;

	for (u8 i = 0; i < 10; i++)
		if (BIT(offset, i))
			data |= m_io_keyboard[i]->read();

	return data;
}

// 00-7F = NUL, 0xE0 = end of line.
u8 bcs3_state::video_r(offs_t offset)
{
	u8 data = m_p_videoram[offset];
	return BIT(data, 7) ? data : 0;
}

// Unsure of how this works.
// 00-7F = NUL, 0xFF = end of line, 0xF7 = finish.
u8 bcs3_state::zx_r()
{
	return 0xf7;
}

void bcs3_state::bcs3_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).mirror(0x2000).rom().region("roms", 0);
	map(0x1000, 0x13ff).mirror(0x2000).r(FUNC(bcs3_state::keyboard_r));
	map(0x1400, 0x17ff).mirror(0x2000).noprw(); //  /WAIT circuit
	map(0x1800, 0x1bff).mirror(0x2000).r(FUNC(bcs3_state::video_r));
	map(0x1c00, 0x1fff).mirror(0x2000).ram().share("videoram");
}

void bcs3_state::bcs3a_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).mirror(0x2000).rom().region("roms", 0);
	map(0x1000, 0x13ff).mirror(0x2000).r(FUNC(bcs3_state::keyboard_r));
	map(0x1400, 0x17ff).mirror(0x2000).noprw(); //  /WAIT circuit
	map(0x1800, 0x1bff).mirror(0x2000).r(FUNC(bcs3_state::zx_r));
	map(0x3c00, 0x7fff).ram().share("videoram");
	map(0xf000, 0xf3ff).rom().region("roms", 0x1000);
}

void bcs3_state::bcs3_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(3);
	// coded in the rom as F8 to FB
	map(0x00, 0x03).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
}

/* Input ports */
static INPUT_PORTS_START( bcs3 )
	PORT_START("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K :") PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR(':')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A &") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')

	PORT_START("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L ;") PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR(';')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B '") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1 !") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')

	PORT_START("KEY.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M <") PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('<')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C (") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2 \"") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR(34)

	PORT_START("KEY.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N =") PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('=')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D )") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3 #") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')

	PORT_START("KEY.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O >") PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('>')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E *") PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4 $") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')

	PORT_START("KEY.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F +") PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('+')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5 %") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')

	PORT_START("KEY.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(32)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G ,") PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR(',')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6 &") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')

	PORT_START("KEY.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H -") PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('-')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7 '") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR(39)

	PORT_START("KEY.8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I .") PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('.')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8 (") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')

	PORT_START("KEY.9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J /") PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('/')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9 )") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
INPUT_PORTS_END

// Official version
u32 bcs3_state::screen_update_bcs3(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 sy=0,ma=0x50;

	for (u8 y = 0; y < 12; y++)
	{
		for (u8 ra = 0; ra < 10; ra++)
		{
			uint16_t *p = &bitmap.pix(sy++);
			u8 const rat = (ra + 1) & 7;

			for (u16 x = ma; x < ma + 28; x++)
			{
				u8 gfx;
				if (ra < 8)
				{
					u8 const chr = m_p_videoram[x] & 0x7f;

					/* get pattern of pixels for that character scanline */
					gfx = m_p_chargen[(chr<<3) | rat] ^ 0xff;
				}
				else
					gfx = 0xff;

				/* Display a scanline of a character (8 pixels) */
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma+=28;
	}
	return 0;
}

/* Hacks: When it starts, it has 4 lines of data. Pressing enter causes it to allocate 100 lines.
   I'm assuming that it only shows a portion of this, with the cursor always in sight. */
u32 bcs3_state::screen_update_bcs3a(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 sy = 0, ma = s_init;
	u16 const cursor = (m_p_videoram[s_curs] | (m_p_videoram[s_curs+1] << 8)) - 0x3c00 - ma;  // get cursor relative position
	u8 const cw = cursor / (s_cols+1);
	if (cw > (s_rows-1)) ma += (cw-(s_rows-1)) * (s_cols+1);

	for (u8 y = 0; y < s_rows; y++)
	{
		for (u8 ra = 0; ra < 10; ra++)
		{
			uint16_t *p = &bitmap.pix(sy++);
			u8 const rat = (ra + 1) & 7;

			for (u16 x = ma; x < ma + s_cols; x++)
			{
				u8 gfx;
				if (ra < 8)
				{
					u8 const chr = m_p_videoram[x] & 0x7f;

					/* get pattern of pixels for that character scanline */
					gfx = m_p_chargen[(chr<<3) | rat] ^ 0xff;
				}
				else
					gfx = 0xff;

				/* Display a scanline of a character (8 pixels) */
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma+=(s_cols+1);
	}
	return 0;
}


/* F4 Character Displayer */
static const gfx_layout bcs3_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	128,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 0*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_bcs3 )
	GFXDECODE_ENTRY( "chargen", 0x0000, bcs3_charlayout, 0, 1 )
GFXDECODE_END

void bcs3_state::ctc_z0_w(int state)
{
	m_ctc->trg1(state);
	if (state && m_cass)
	{
		m_cassbit ^= 1;
		m_cass->output(m_cassbit ? -1.0 : +1.0);
	}
}

// The manual says the cassette pulses come from here, but
// it's total silence during cassette saving.
void bcs3_state::ctc_z1_w(int state)
{
	m_ctc->trg2(state);
}

void bcs3_state::machine_start()
{
	save_item(NAME(m_cassbit));
	save_item(NAME(s_curs));
	save_item(NAME(s_init));
	save_item(NAME(s_rows));
	save_item(NAME(s_cols));
}

static const z80_daisy_config daisy_chain_intf[] =
{
	{ "ctc" },
	{ nullptr }
};

void bcs3_state::init_bcs3a()
{
	s_curs = 0x7a;
	s_init = 0x80;
	s_rows = 12;
	s_cols = 29;
}

void bcs3_state::init_bcs3b()
{
	s_curs = 0x7a;
	s_init = 0x80;
	s_rows = 24;
	s_cols = 40;
}

void bcs3_state::init_bcs3c()
{
	s_curs = 0x08;
	s_init = 0xa0;
	s_rows = 12;
	s_cols = 29;
}

void bcs3_state::init_bcs3d()
{
	s_curs = 0x08;
	s_init = 0xb4;
	s_rows = 12;
	s_cols = 29;
}

void bcs3_state::bcs3(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(5'000'000) /2);
	m_maincpu->set_addrmap(AS_PROGRAM, &bcs3_state::bcs3_mem);
	m_maincpu->set_addrmap(AS_IO, &bcs3_state::bcs3_io);
	m_maincpu->set_daisy_config(daisy_chain_intf);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_size(28*8, 12*10);
	m_screen->set_visarea_full();
	m_screen->set_screen_update(FUNC(bcs3_state::screen_update_bcs3));
	m_screen->set_palette("palette");
	GFXDECODE(config, "gfxdecode", "palette", gfx_bcs3);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	Z80CTC(config, m_ctc, XTAL(5'000'000) / 2);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->zc_callback<0>().set(FUNC(bcs3_state::ctc_z0_w));
	m_ctc->zc_callback<1>().set(FUNC(bcs3_state::ctc_z1_w));
}

void bcs3_state::bcs3a(machine_config &config)
{
	bcs3(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &bcs3_state::bcs3a_mem);
	m_screen->set_size(29*8, 12*10);
	m_screen->set_visarea(0, 29*8-1, 0, 12*10-1);
	m_screen->set_screen_update(FUNC(bcs3_state::screen_update_bcs3a));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
}

void bcs3_state::bcs3b(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(7'000'000) /2);
	m_maincpu->set_addrmap(AS_PROGRAM, &bcs3_state::bcs3a_mem);
	m_maincpu->set_addrmap(AS_IO, &bcs3_state::bcs3_io);
	m_maincpu->set_daisy_config(daisy_chain_intf);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_size(40*8, 24*10);
	m_screen->set_visarea_full();
	m_screen->set_screen_update(FUNC(bcs3_state::screen_update_bcs3a));
	m_screen->set_palette("palette");
	GFXDECODE(config, "gfxdecode", "palette", gfx_bcs3);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	Z80CTC(config, m_ctc, XTAL(7'000'000) / 2);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->zc_callback<0>().set(FUNC(bcs3_state::ctc_z0_w));
	m_ctc->zc_callback<1>().set(FUNC(bcs3_state::ctc_z1_w));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
}


/* ROM definition */
ROM_START( bcs3 )
	ROM_REGION( 0x2000, "roms", ROMREGION_ERASEFF )
	//ROM_LOAD( "se24.bin", 0x0000, 0x0800, CRC(268de5ee) SHA1(78784945956c1b0282a4e82ad55e7c3a77389e50))
	ROM_LOAD( "se24_000.d6", 0x0000, 0x0400, CRC(157a0d28) SHA1(0a6666c289b95d98128fd282478dff6319031b6e) )
	ROM_LOAD( "se24_400.d7", 0x0400, 0x0400, CRC(2159de0f) SHA1(09b567e750931019de914f25d5ab1e4910465de6) )
	// Cassette rom goes here starting at 0x0800 ... but did this rom ever exist??

	ROM_REGION( 0x0400, "chargen", 0 )
	ROM_LOAD( "bcs_zg_24.d21", 0x0000, 0x0400, CRC(eaed9d84) SHA1(7023a6187cd6bd0c6489d76ff662453f14e5b636))
ROM_END

ROM_START( bcs3a )
	ROM_REGION( 0x2000, "roms", ROMREGION_ERASEFF )
	//ROM_LOAD( "se31_29.bin", 0x0000, 0x1000, CRC(e9b55544) SHA1(82bae68c4bcaecf66632f5b43913b50a1acba316))
	ROM_LOAD( "se31_000.d6", 0x0000, 0x0400, CRC(0765bd83) SHA1(137ceffd50eeaf21caab286d3e01161ba3267ea4) )
	ROM_LOAD( "se31_400.d7", 0x0400, 0x0400, CRC(1a87a3ed) SHA1(c8121ff198f8cf0c7bc7bc7e258ecfa51d3bb02c) )
	ROM_LOAD( "se31_800.d8", 0x0800, 0x0400, CRC(05654a8f) SHA1(b42fa6cf5710dab23f062dbeea81e85b4c18e1b0) )
	ROM_LOAD( "se31_c00.d9", 0x0c00, 0x0400, CRC(858ca28b) SHA1(90f943b0c1d102dd058a859ba139057a0bd278a6) )
	ROM_LOAD( "se31mceditor.bin", 0x1000, 0x0400, CRC(8eac92ec) SHA1(8950a3ef05d02abf34269bfce002c46d273ce113))

	ROM_REGION( 0x0400, "chargen", 0 )
	ROM_LOAD( "bcs_zg_31.d21", 0x0000, 0x0400, CRC(a20c93c9) SHA1(b2be1c0d98b7ac05713349b099b392975968be1d))
ROM_END

ROM_START( bcs3b )
	ROM_REGION( 0x2000, "roms", ROMREGION_ERASEFF )
	ROM_LOAD( "se31_40.bin", 0x0000, 0x1000, CRC(4e993152) SHA1(6bb01ff5779627fa2eb2df432fffcfccc1e33231))
	ROM_LOAD( "se31mceditor.bin", 0x1000, 0x0400, CRC(8eac92ec) SHA1(8950a3ef05d02abf34269bfce002c46d273ce113))

	ROM_REGION( 0x0400, "chargen", 0 )
	ROM_LOAD( "bcs_zg_31.d21", 0x0000, 0x0400, CRC(a20c93c9) SHA1(b2be1c0d98b7ac05713349b099b392975968be1d))
ROM_END

ROM_START( bcs3c )
	ROM_REGION( 0x2000, "roms", ROMREGION_ERASEFF )
	ROM_LOAD( "bcs32.bin", 0x0000, 0x1000, CRC(1523b846) SHA1(ca5e3213707a604e02d9e7a7ebfc362ef294ddb8) )

	ROM_REGION( 0x0400, "chargen", 0 )
	ROM_LOAD( "bcs_zg_32.d21", 0x0000, 0x0400, CRC(abe9e820) SHA1(03a8792d08774cd67b98efd3f83a78e897b4e001) )
ROM_END

ROM_START( bcs3d )
	ROM_REGION( 0x2000, "roms", ROMREGION_ERASEFF )
	ROM_LOAD( "sp33_29.bin", 0x0000, 0x1000, CRC(1c851eb2) SHA1(4f8bb5274ea1861a35a840e8f3482bdc693047c4))

	ROM_REGION( 0x0400, "chargen", 0 )
	ROM_LOAD( "bcs_zg_33.d21", 0x0000, 0x0400, CRC(b27f1c07) SHA1(61c80c585f198370ba5e856839c12b15acdc58ee))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY             FULLNAME                   FLAGS */
COMP( 1984, bcs3,  0,      0,      bcs3,    bcs3,  bcs3_state, empty_init, "Eckhard Schiller", "BCS 3 rev 2.4",           MACHINE_NO_SOUND_HW )
COMP( 1986, bcs3a, bcs3,   0,      bcs3a,   bcs3,  bcs3_state, init_bcs3a, "Eckhard Schiller", "BCS 3 rev 3.1 29-column", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1986, bcs3b, bcs3,   0,      bcs3b,   bcs3,  bcs3_state, init_bcs3b, "Eckhard Schiller", "BCS 3 rev 3.1 40-column", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1986, bcs3c, bcs3,   0,      bcs3a,   bcs3,  bcs3_state, init_bcs3c, "Eckhard Schiller", "BCS 3 rev 3.2",           MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1986, bcs3d, bcs3,   0,      bcs3a,   bcs3,  bcs3_state, init_bcs3d, "Eckhard Schiller", "BCS 3 rev 3.3",           MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



bebox.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nathan Woods, R. Belmont
/***************************************************************************

    drivers/bebox.c

    BeBox

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

/* Core includes */
#include "emu.h"
#include "bebox.h"

/* Components */
#include "bus/lpci/cirrus.h"
#include "bus/lpci/mpc105.h"
#include "bus/scsi/scsi.h"
#include "sound/ymopl.h"
#include "machine/mc146818.h"
#include "machine/pckeybrd.h"
#include "video/pc_vga_cirrus.h"

#include "speaker.h"

/* Devices */
#include "bus/scsi/scsicd.h"
#include "bus/scsi/scsihd.h"
#include "machine/8042kbdc.h"

uint8_t bebox_state::at_dma8237_1_r(offs_t offset) { return m_dma8237[1]->read(offset / 2); }
void bebox_state::at_dma8237_1_w(offs_t offset, uint8_t data) { m_dma8237[1]->write(offset / 2, data); }

void bebox_state::main_mem(address_map &map)
{
	map(0x7FFFF0F0, 0x7FFFF0F7).rw(FUNC(bebox_state::bebox_cpu0_imask_r), FUNC(bebox_state::bebox_cpu0_imask_w));
	map(0x7FFFF1F0, 0x7FFFF1F7).rw(FUNC(bebox_state::bebox_cpu1_imask_r), FUNC(bebox_state::bebox_cpu1_imask_w));
	map(0x7FFFF2F0, 0x7FFFF2F7).r(FUNC(bebox_state::bebox_interrupt_sources_r));
	map(0x7FFFF3F0, 0x7FFFF3F7).rw(FUNC(bebox_state::bebox_crossproc_interrupts_r), FUNC(bebox_state::bebox_crossproc_interrupts_w));
	map(0x7FFFF4F0, 0x7FFFF4F7).w(FUNC(bebox_state::bebox_processor_resets_w));

	map(0x80000000, 0x8000001F).rw(m_dma8237[0], FUNC(am9517a_device::read), FUNC(am9517a_device::write));
	map(0x80000020, 0x8000003F).rw(m_pic8259[0], FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x80000040, 0x8000005f).rw(m_pit8254, FUNC(pit8254_device::read), FUNC(pit8254_device::write));
	map(0x80000060, 0x8000006F).rw("kbdc", FUNC(kbdc8042_device::data_r), FUNC(kbdc8042_device::data_w));
	map(0x80000070, 0x8000007F).w("rtc", FUNC(mc146818_device::address_w)).umask64(0xff00ff00ff00ff00);
	map(0x80000070, 0x8000007F).rw("rtc", FUNC(mc146818_device::data_r), FUNC(mc146818_device::data_w)).umask64(0x00ff00ff00ff00ff);
	map(0x80000080, 0x8000009F).rw(FUNC(bebox_state::bebox_page_r), FUNC(bebox_state::bebox_page_w));
	map(0x800000A0, 0x800000BF).rw(m_pic8259[1], FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x800000C0, 0x800000DF).rw(FUNC(bebox_state::at_dma8237_1_r), FUNC(bebox_state::at_dma8237_1_w));
	map(0x800001F0, 0x800001F7).rw("ide", FUNC(ide_controller_device::cs0_r), FUNC(ide_controller_device::cs0_w));
	map(0x800002F8, 0x800002FF).rw("ns16550_1", FUNC(ns16550_device::ins8250_r), FUNC(ns16550_device::ins8250_w));
	map(0x80000380, 0x80000387).rw("ns16550_2", FUNC(ns16550_device::ins8250_r), FUNC(ns16550_device::ins8250_w));
	map(0x80000388, 0x8000038F).rw("ns16550_3", FUNC(ns16550_device::ins8250_r), FUNC(ns16550_device::ins8250_w));
	map(0x800003B0, 0x800003DF).m(m_vga, FUNC(cirrus_gd5446_vga_device::io_map));
	map(0x800003F0, 0x800003F7).rw("ide", FUNC(ide_controller_device::cs1_r), FUNC(ide_controller_device::cs1_w));
	map(0x800003F0, 0x800003F7).m(m_smc37c78, FUNC(smc37c78_device::map));
	map(0x800003F8, 0x800003FF).rw("ns16550_0", FUNC(ns16550_device::ins8250_r), FUNC(ns16550_device::ins8250_w));
	map(0x80000480, 0x8000048F).rw(FUNC(bebox_state::bebox_80000480_r), FUNC(bebox_state::bebox_80000480_w));
	map(0x80000CF8, 0x80000CFF).rw(m_pcibus, FUNC(pci_bus_device::read_64be), FUNC(pci_bus_device::write_64be));
	//map(0x800042E8, 0x800042EF).w("cirrus", FUNC(cirrus_device::cirrus_42E8_w));

	map(0xBFFFFFF0, 0xBFFFFFFF).r(FUNC(bebox_state::bebox_interrupt_ack_r));
	map(0xC00A0000, 0xC00BFFFF).rw(m_vga, FUNC(cirrus_gd5446_vga_device::mem_r), FUNC(cirrus_gd5446_vga_device::mem_w));
	map(0xC1000000, 0xC11FFFFF).rw(m_vga, FUNC(cirrus_gd5446_vga_device::mem_linear_r), FUNC(cirrus_gd5446_vga_device::mem_linear_w));
	map(0xFFF00000, 0xFFF03FFF).bankr("bank2");
	map(0xFFF04000, 0xFFFFFFFF).rw(FUNC(bebox_state::bebox_flash_r), FUNC(bebox_state::bebox_flash_w));
}

// The following is a gross hack to let the BeBox boot ROM identify the processors correctly.
// This needs to be done in a better way if someone comes up with one.

uint64_t bebox_state::bb_slave_64be_r(offs_t offset, uint64_t mem_mask)
{
	// 2e94 is the real address, 2e84 is where the PC appears to be under full DRC
	if ((m_ppc[1]->pc() == 0xfff02e94) || (m_ppc[1]->pc() == 0xfff02e84))
	{
		return 0x108000ff;  // indicate slave CPU
	}

	return m_pcibus->read_64be(offset, mem_mask);
}

void bebox_state::slave_mem(address_map &map)
{
	main_mem(map);
	map(0x80000cf8, 0x80000cff).r(FUNC(bebox_state::bb_slave_64be_r));
	map(0x80000cf8, 0x80000cff).w(m_pcibus, FUNC(pci_bus_device::write_64be));
}

#define BYTE_REVERSE32(x)       (((x >> 24) & 0xff) | \
								((x >> 8) & 0xff00) | \
								((x << 8) & 0xff0000) | \
								((x << 24) & 0xff000000))

uint32_t bebox_state::scsi_fetch(uint32_t dsp)
{
	const uint32_t result = m_ppc[0]->space(AS_PROGRAM).read_dword(dsp & 0x7FFFFFFF);
	return BYTE_REVERSE32(result);
}


void bebox_state::scsi_irq_callback(int state)
{
	bebox_set_irq_bit(21, state);
}


void bebox_state::scsi_dma_callback(uint32_t src, uint32_t dst, int length, int byteswap)
{
}

void bebox_state::mpc105_config(device_t *device)
{
	mpc105_device &mpc105 = *downcast<mpc105_device *>(device);
	mpc105.set_cpu(":ppc1");
	mpc105.set_bank_base_default(0);
}

void bebox_state::cirrus_config(device_t *device)
{
	pci_cirrus_svga_device &cirruspci = *downcast<pci_cirrus_svga_device *>(device);
	cirruspci.set_vga(m_vga);
}

/*************************************
 *
 *  Keyboard
 *
 *************************************/

void bebox_state::bebox_keyboard_interrupt(int state)
{
	bebox_set_irq_bit(16, state);
	m_pic8259[0]->ir1_w(state);
}

pci_connector_device &bebox_state::add_pci_slot(machine_config &config, const char *tag, size_t index, const char *default_tag)
{
	pci_connector_device &pcislot(PCI_CONNECTOR(config, tag, index));
	pcislot.option_add("mpc105", MPC105);
	pcislot.option_add("cirrus", PCI_CIRRUS_SVGA);
	pcislot.set_default_option(default_tag);
	pcislot.set_fixed(true);
	return pcislot;
}

void bebox_state::bebox_peripherals(machine_config &config)
{
	config.set_maximum_quantum(attotime::from_hz(60));

	PIT8254(config, m_pit8254, 0);
	m_pit8254->set_clk<0>(4772720/4); /* heartbeat IRQ */
	m_pit8254->set_clk<1>(4772720/4); /* dram refresh */
	m_pit8254->set_clk<2>(4772720/4); /* pio port c pin 4, and speaker polling */
	m_pit8254->out_handler<0>().set(FUNC(bebox_state::bebox_timer0_w));
	m_pit8254->out_handler<2>().set("kbdc", FUNC(kbdc8042_device::write_out2));

	AM9517A(config, m_dma8237[0], XTAL(14'318'181)/3);
	m_dma8237[0]->out_hreq_callback().set(FUNC(bebox_state::bebox_dma_hrq_changed));
	m_dma8237[0]->out_eop_callback().set(FUNC(bebox_state::bebox_dma8237_out_eop));
	m_dma8237[0]->in_memr_callback().set(FUNC(bebox_state::bebox_dma_read_byte));
	m_dma8237[0]->out_memw_callback().set(FUNC(bebox_state::bebox_dma_write_byte));
	m_dma8237[0]->in_ior_callback<2>().set(m_smc37c78, FUNC(smc37c78_device::dma_r));
	m_dma8237[0]->out_iow_callback<2>().set(m_smc37c78, FUNC(smc37c78_device::dma_w));
	m_dma8237[0]->out_dack_callback<0>().set(FUNC(bebox_state::pc_dack0_w));
	m_dma8237[0]->out_dack_callback<1>().set(FUNC(bebox_state::pc_dack1_w));
	m_dma8237[0]->out_dack_callback<2>().set(FUNC(bebox_state::pc_dack2_w));
	m_dma8237[0]->out_dack_callback<3>().set(FUNC(bebox_state::pc_dack3_w));

	AM9517A(config, m_dma8237[1], XTAL(14'318'181)/3);

	PIC8259(config, m_pic8259[0], 0);
	m_pic8259[0]->out_int_callback().set(FUNC(bebox_state::bebox_pic8259_master_set_int_line));
	m_pic8259[0]->in_sp_callback().set_constant(1);
	m_pic8259[0]->read_slave_ack_callback().set(m_pic8259[1], FUNC(pic8259_device::acknowledge));

	PIC8259(config, m_pic8259[1], 0);
	m_pic8259[1]->out_int_callback().set(FUNC(bebox_state::bebox_pic8259_slave_set_int_line));
	m_pic8259[1]->in_sp_callback().set_constant(0);

	NS16550(config, "ns16550_0", 0);   /* TODO: Verify model */
	NS16550(config, "ns16550_1", 0);   /* TODO: Verify model */
	NS16550(config, "ns16550_2", 0);   /* TODO: Verify model */
	NS16550(config, "ns16550_3", 0);   /* TODO: Verify model */

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(XTAL(25'174'800), 900, 0, 640, 526, 0, 480);
	screen.set_screen_update(m_vga, FUNC(cirrus_gd5446_vga_device::screen_update));

	// was GD5428, assume mistake (GD5446 is PCI)
	CIRRUS_GD5446_VGA(config, m_vga, 0);
	m_vga->set_screen("screen");
	m_vga->set_vram_size(0x200000);

	speaker_device &speaker(SPEAKER(config, "mono"));
	speaker.front_center();

	ym3812_device &ym3812(YM3812(config, "ym3812", 3579545));
	ym3812.add_route(ALL_OUTPUTS, speaker, 1.0);

	FUJITSU_29F016A(config, "flash");

	scsi_port_device &scsibus(SCSI_PORT(config, "scsi"));
	scsibus.set_slot_device(1, "harddisk", SCSIHD, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_0));
	scsibus.set_slot_device(2, "cdrom", SCSICD, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_3));

	lsi53c810_device &scsictrl(LSI53C810(config, "lsi53c810"));
	scsictrl.set_irq_callback(FUNC(bebox_state::scsi_irq_callback));
	scsictrl.set_dma_callback(FUNC(bebox_state::scsi_dma_callback));
	scsictrl.set_fetch_callback(FUNC(bebox_state::scsi_fetch));
	scsictrl.set_scsi_port("scsi");

	ide_controller_device &idectrl(IDE_CONTROLLER(config, "ide"));
	idectrl.options(ata_devices, "hdd", nullptr, false);
	idectrl.irq_handler().set(FUNC(bebox_state::bebox_ide_interrupt));

	/* pci */
	PCI_BUS(config, m_pcibus, 0);
	m_pcibus->set_busnum(0);

	pci_connector_device &pcislot0 = add_pci_slot(config, "pcibus:0", 0, "mpc105");
	pcislot0.set_option_machine_config("mpc105", [this](device_t *device) { mpc105_config(device); });
	pci_connector_device &pcislot1 = add_pci_slot(config, "pcibus:1", 1, "cirrus");
	pcislot1.set_option_machine_config("cirrus", [this](device_t *device) { cirrus_config(device); });

	/*PCI_BUS_LEGACY_DEVICE(12, nullptr, scsi53c810_pci_read, scsi53c810_pci_write)*/

	SMC37C78(config, m_smc37c78, 24'000'000);
	m_smc37c78->intrq_wr_callback().set(FUNC(bebox_state::fdc_interrupt));
	m_smc37c78->drq_wr_callback().set(m_dma8237[0], FUNC(am9517a_device::dreq2_w));

	floppy_connector &fdc(FLOPPY_CONNECTOR(config, "smc37c78:0"));
	fdc.option_add("35hd", FLOPPY_35_HD);
	fdc.set_default_option("35hd");
	fdc.set_formats(floppy_image_device::default_pc_floppy_formats);

	MC146818(config, "rtc", 32.768_kHz_XTAL);

	kbdc8042_device &kbdc(KBDC8042(config, "kbdc"));
	kbdc.set_keyboard_type(kbdc8042_device::KBDC8042_STANDARD);
	kbdc.system_reset_callback().set_inputline(m_ppc[0], INPUT_LINE_RESET);
	kbdc.input_buffer_full_callback().set(FUNC(bebox_state::bebox_keyboard_interrupt));
	kbdc.set_keyboard_tag("at_keyboard");

	at_keyboard_device &at_keyb(AT_KEYB(config, "at_keyboard", pc_keyboard_device::KEYBOARD_TYPE::AT, 1));
	at_keyb.keypress().set("kbdc", FUNC(kbdc8042_device::keyboard_w));

	/* internal ram */
	RAM(config, m_ram);
	m_ram->set_default_size("32M");
	m_ram->set_extra_options("8M,16M");
}

void bebox_state::bebox(machine_config &config)
{
	PPC603(config, m_ppc[0], 66000000);  /* 66 MHz */
	m_ppc[0]->set_addrmap(AS_PROGRAM, &bebox_state::main_mem);

	PPC603(config, m_ppc[1], 66000000);  /* 66 MHz */
	m_ppc[1]->set_addrmap(AS_PROGRAM, &bebox_state::slave_mem);

	bebox_peripherals(config);
}

void bebox_state::bebox2(machine_config &config)
{
	PPC603E(config, m_ppc[0], 133000000);  /* 133 MHz */
	m_ppc[0]->set_addrmap(AS_PROGRAM, &bebox_state::main_mem);

	PPC603E(config, m_ppc[1], 133000000);  /* 133 MHz */
	m_ppc[1]->set_addrmap(AS_PROGRAM, &bebox_state::slave_mem);

	bebox_peripherals(config);
}

static INPUT_PORTS_START( bebox )
INPUT_PORTS_END

ROM_START(bebox)
	ROM_REGION(0x00200000, "user1", ROMREGION_64BIT | ROMREGION_BE)
	ROM_LOAD( "bootmain.rom", 0x000000, 0x20000, CRC(df2d19e0) SHA1(da86a7d23998dc953dd96a2ac5684faaa315c701) )
	ROM_REGION(0x4000, "user2", ROMREGION_64BIT | ROMREGION_BE)
	ROM_LOAD( "bootnub.rom", 0x000000, 0x4000, CRC(5348d09a) SHA1(1b637a3d7a2b072aa128dd5c037bbb440d525c1a) )
ROM_END

ROM_START(bebox2)
	ROM_REGION(0x00200000, "user1", ROMREGION_64BIT | ROMREGION_BE)
	ROM_LOAD( "bootmain.rom", 0x000000, 0x20000, CRC(df2d19e0) SHA1(da86a7d23998dc953dd96a2ac5684faaa315c701) )
	ROM_REGION(0x4000, "user2", ROMREGION_64BIT | ROMREGION_BE)
	ROM_LOAD( "bootnub.rom", 0x000000, 0x4000, CRC(5348d09a) SHA1(1b637a3d7a2b072aa128dd5c037bbb440d525c1a) )
ROM_END

/*    YEAR   NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY   FULLNAME */
COMP( 1995,  bebox,  0,      0,      bebox,   bebox, bebox_state, init_bebox, "Be Inc", "BeBox Dual603-66",  MACHINE_NOT_WORKING )
COMP( 1996,  bebox2, bebox,  0,      bebox2,  bebox, bebox_state, init_bebox, "Be Inc", "BeBox Dual603-133", MACHINE_NOT_WORKING )



berlin.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco, hap
/*******************************************************************************

Mephisto Berlin 68000 / Berlin Professional 68020
Berlin Professional has the same engine as Mephisto Genius.

Hardware notes:

Berlin 68000:
- MC68HC000FN12 @ 12.288MHz
- 128KB ROM (2*M27C512-15XF1)
- 512KB DRAM (4*TC514256AP-80)
- 8KB SRAM (HM6264ALP-12 or equivalent), CR2032 battery
- HD44780, 2-line LCD display (small daughterboard)
- 8*8 chessboard buttons, 64 leds, piezo

Berlin Professional 68020:
- MC68EC020RP25 @ 24.576MHz
- 256KB ROM (D27C020-200V10)
- 1MB DRAM (8*MCM514256AP70)
- rest is similar to Berlin 68000

Undocumented buttons:
- holding ENTER and LEFT cursor on boot runs diagnostics
- holding CLEAR on boot clears battery backed RAM

TODO:
- verify IRQ level and acknowledge
- does it have ROM waitstates like modular.cpp?

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

#include "emu.h"

#include "mmboard.h"
#include "mmdisplay2.h"

#include "cpu/m68000/m68000.h"
#include "cpu/m68000/m68020.h"
#include "machine/nvram.h"

// internal artwork
#include "mephisto_berlin.lh"


namespace {

class berlin_state : public driver_device
{
public:
	berlin_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_nvram(*this, "nvram", 0x2000, ENDIANNESS_BIG),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_keys(*this, "KEY")
	{ }

	void berlin(machine_config &config);
	void berlinp(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	memory_share_creator<u8> m_nvram;
	required_device<mephisto_board_device> m_board;
	required_device<mephisto_display2_device> m_display;
	required_ioport m_keys;

	void berlin_mem(address_map &map) ATTR_COLD;
	void berlinp_mem(address_map &map) ATTR_COLD;

	u8 nvram_r(offs_t offset) { return m_nvram[offset]; }
	void nvram_w(offs_t offset, u8 data) { m_nvram[offset] = data; }

	u8 input_r();
};



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

u8 berlin_state::input_r()
{
	// display i/o d7 selects keypad
	u8 data = (m_display->io_r() & 0x80) ? 0 : m_keys->read();
	return ~m_board->input_r() | data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void berlin_state::berlin_mem(address_map &map)
{
	map(0x000000, 0x01ffff).rom();
	map(0x800000, 0x87ffff).ram();
	map(0x900000, 0x903fff).rw(FUNC(berlin_state::nvram_r), FUNC(berlin_state::nvram_w)).umask16(0xff00);
	map(0xa00000, 0xa00000).r(FUNC(berlin_state::input_r));
	map(0xb00000, 0xb00000).w(m_board, FUNC(mephisto_board_device::mux_w));
	map(0xc00000, 0xc00000).w(m_display, FUNC(mephisto_display2_device::latch_w));
	map(0xd00008, 0xd00008).w(m_display, FUNC(mephisto_display2_device::io_w));
	map(0xe00000, 0xe00000).w(m_board, FUNC(mephisto_board_device::led_w));
	map(0xe00000, 0xe00001).nopr(); // clr.b
}

void berlin_state::berlinp_mem(address_map &map)
{
	map(0x000000, 0x03ffff).rom();
	map(0x400000, 0x4fffff).ram();
	map(0x800000, 0x800000).r(FUNC(berlin_state::input_r));
	map(0x900000, 0x900000).w(m_board, FUNC(mephisto_board_device::mux_w));
	map(0xa00000, 0xa00000).w(m_board, FUNC(mephisto_board_device::led_w));
	map(0xb00000, 0xb00000).w(m_display, FUNC(mephisto_display2_device::io_w));
	map(0xc00000, 0xc00000).w(m_display, FUNC(mephisto_display2_device::latch_w));
	map(0xd00000, 0xd07fff).rw(FUNC(berlin_state::nvram_r), FUNC(berlin_state::nvram_w)).umask32(0xff000000);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( berlin )
	PORT_START("KEY")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Enter")           PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Clear")           PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Up")              PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Down")            PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Left")            PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Right")           PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("New Game (1/2)")  PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_F1) // combine for NEW GAME
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("New Game (2/2)")  PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_F1) // "
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void berlin_state::berlin(machine_config &config)
{
	// basic machine hardware
	M68000(config, m_maincpu, 12.288_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &berlin_state::berlin_mem);

	const attotime irq_period = attotime::from_hz(12.288_MHz_XTAL / 0x4000); // 750Hz
	m_maincpu->set_periodic_int(FUNC(berlin_state::irq2_line_hold), irq_period);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	MEPHISTO_BUTTONS_BOARD(config, m_board); // internal
	subdevice<sensorboard_device>("board:board")->set_nvram_enable(true);

	MEPHISTO_DISPLAY_MODULE2(config, m_display); // internal
	config.set_default_layout(layout_mephisto_berlin);
}

void berlin_state::berlinp(machine_config &config)
{
	berlin(config);

	// basic machine hardware
	M68EC020(config.replace(), m_maincpu, 24.576_MHz_XTAL); // M68EC020RP25
	m_maincpu->set_addrmap(AS_PROGRAM, &berlin_state::berlinp_mem);

	const attotime irq_period = attotime::from_hz(24.576_MHz_XTAL / 0x8000); // 750Hz
	m_maincpu->set_periodic_int(FUNC(berlin_state::irq2_line_hold), irq_period);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( berl16 ) // B003 8C60 CA47
	ROM_REGION16_BE( 0x20000, "maincpu", 0 )
	ROM_LOAD16_BYTE("berlin_68000_even.bin",     0x00000, 0x10000, CRC(31337f15) SHA1(0dcacb153a6f8376e6f1c2f3e57e60aad4370740) )
	ROM_LOAD16_BYTE("berlin_68000_odd_b003.bin", 0x00001, 0x10000, CRC(cc146819) SHA1(e4b2c6e496eff4a657a0718be292f563fb4e5688) )
ROM_END

ROM_START( berl16a ) // B002 8C59 CA47
	ROM_REGION16_BE( 0x20000, "maincpu", 0 )
	ROM_LOAD16_BYTE("berlin_68000_even.bin",     0x00000, 0x10000, CRC(31337f15) SHA1(0dcacb153a6f8376e6f1c2f3e57e60aad4370740) )
	ROM_LOAD16_BYTE("berlin_68000_odd_b002.bin", 0x00001, 0x10000, CRC(513a95f2) SHA1(cbaef0078a119163577e76a78b2110939b17be6b) )
ROM_END

ROM_START( berl16l ) // B500 ABD5 CA47
	ROM_REGION16_BE( 0x20000, "maincpu", 0 )
	ROM_LOAD16_BYTE("berlin_68000_london_even.bin", 0x00000, 0x10000, CRC(0ccddbc6) SHA1(90effdc9f2811a24d450b74ccfb24995ce896b86) )
	ROM_LOAD16_BYTE("berlin_68000_london_odd.bin",  0x00001, 0x10000, CRC(5edac658) SHA1(18ebebc5ceffd9a01798d8a3709875120bd096f7) )
ROM_END

ROM_START( berlinp ) // B400 8AA1 E785
	ROM_REGION32_BE( 0x40000, "maincpu", 0 )
	ROM_LOAD("berlin_020_v400.u2", 0x00000, 0x40000, CRC(82fbaf6e) SHA1(729b7cef3dfaecc4594a6178fc4ba6015afa6202) )
ROM_END

ROM_START( berlinpl ) // B500 53CA 3DCE
	ROM_REGION32_BE( 0x40000, "maincpu", 0 )
	ROM_LOAD("berlin_020_london.u2", 0x00000, 0x40000, CRC(d75e170f) SHA1(ac0ebdaa114abd4fef87361a03df56928768b1ae) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT   COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1992, berl16,   0,       0,      berlin,  berlin, berlin_state, empty_init, "Hegener + Glaser", "Mephisto Berlin 68000 (v0.03)", MACHINE_SUPPORTS_SAVE )
SYST( 1992, berl16a,  berl16,  0,      berlin,  berlin, berlin_state, empty_init, "Hegener + Glaser", "Mephisto Berlin 68000 (v0.02)", MACHINE_SUPPORTS_SAVE )
SYST( 1996, berl16l,  berl16,  0,      berlin,  berlin, berlin_state, empty_init, "Richard Lang", "Mephisto Berlin 68000 (London upgrade)", MACHINE_SUPPORTS_SAVE )

SYST( 1994, berlinp,  0,       0,      berlinp, berlin, berlin_state, empty_init, "Hegener + Glaser", "Mephisto Berlin Professional 68020", MACHINE_SUPPORTS_SAVE )
SYST( 1996, berlinpl, berlinp, 0,      berlinp, berlin, berlin_state, empty_init, "Richard Lang", "Mephisto Berlin Professional 68020 (London upgrade)", MACHINE_SUPPORTS_SAVE )



bert.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    BERT apparently stands for "Basic Einplatinen Rechner für TV-Serie." It is
    a small educational single-board computer manufactured by the West German
    firm Thomsen-Elektronik, based on the Z8671 microcontroller with a built-in
    BASIC/DEBUG ROM.

    Besides a 5-pin DIN connector for the terminal or PC, the board makes four
    generic input and/or output ports available to the user through pin headers.
    The first two ports, including the fully bidirectional port A, are directly
    wired to the processor. Two other ports are read or written through memory-
    mapped latches, and a fifth port represents the onboard DIP switches.

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

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/z8/z8.h"
#include "machine/ram.h"


namespace {

class bert_state : public driver_device
{
public:
	bert_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_mpu(*this, "mpu")
		, m_ram(*this, RAM_TAG)
		, m_serial(*this, "serial")
	{
	}

	void bert(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void portb_w(u8 data);
	void portd_w(u8 data);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<z8_device> m_mpu;
	required_device<ram_device> m_ram;
	required_device<rs232_port_device> m_serial;
};

void bert_state::machine_start()
{
	m_mpu->space(AS_PROGRAM).install_ram(0x2000, 0x2000 + m_ram->size() - 1, 0x8000, m_ram->pointer());
}

void bert_state::portb_w(u8 data)
{
	m_serial->write_txd(BIT(data, 7));
}

void bert_state::portd_w(u8 data)
{
}

void bert_state::mem_map(address_map &map)
{
	// A15 (P07) is only decoded internally
	map(0x0800, 0x0fff).mirror(0x8000).noprw();
	map(0x1000, 0x1fff).mirror(0x8000).rom().region("eprom", 0).nopw(); // 2732
	map(0x5000, 0x5000).mirror(0x8fff).portr("PORTC"); // 74LS373
	map(0x6000, 0x6000).mirror(0x8fff).w(FUNC(bert_state::portd_w)); // 74LS373
	map(0x7000, 0x7000).mirror(0x8fff).portr("PORTE"); // 74LS368
}

static INPUT_PORTS_START(bert)
	PORT_START("PORTA")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("PORTC")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("PORTE")
	PORT_DIPNAME(0x07, 0x02, "Baud Rate") PORT_DIPLOCATION("E:!1,!2,!3")
	PORT_DIPSETTING(0x06, "110")
	PORT_DIPSETTING(0x00, "150")
	PORT_DIPSETTING(0x07, "300")
	PORT_DIPSETTING(0x05, "1200")
	PORT_DIPSETTING(0x04, "2400")
	PORT_DIPSETTING(0x03, "4800")
	PORT_DIPSETTING(0x02, "9600")
	PORT_DIPSETTING(0x01, "19200")
	PORT_DIPNAME(0x08, 0x00, DEF_STR(Unused)) PORT_DIPLOCATION("E:!4")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x08, DEF_STR(On))
	PORT_DIPNAME(0x10, 0x00, DEF_STR(Unused)) PORT_DIPLOCATION("E:!5")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x10, DEF_STR(On))
	PORT_DIPNAME(0x20, 0x00, DEF_STR(Unused)) PORT_DIPLOCATION("E:!6")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x20, DEF_STR(On))
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

void bert_state::bert(machine_config &config)
{
	Z8671(config, m_mpu, 7.3728_MHz_XTAL);
	m_mpu->set_addrmap(AS_PROGRAM, &bert_state::mem_map);
	m_mpu->p2_in_cb().set_ioport("PORTA");
	m_mpu->p3_out_cb().set(FUNC(bert_state::portb_w));

	RAM(config, m_ram).set_default_size("2K").set_extra_options("4K"); // two 6116 sockets

	RS232_PORT(config, m_serial, default_rs232_devices, "terminal"); // TTL-level, not EIA/CCITT ±12V
	m_serial->rxd_handler().set_inputline(m_mpu, INPUT_LINE_IRQ3).invert(); // no actual inverter
}

ROM_START(bert)
	ROM_REGION(0x1000, "eprom", 0)
	ROM_LOAD("bert.bin", 0x0000, 0x1000, CRC(52ece0e7) SHA1(d39cc9b6248547cfa8fd49d43532abf9399348a5))
ROM_END

} // anonymous namespace


COMP(1987, bert, 0, 0, bert, bert, bert_state, empty_init, "VGS Verlagsgesellschaft", "BERT", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)



beta.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/***************************************************************************

    Beta Computer

    http://retro.hansotten.nl/index.php?page=beta-computer

    When first started, press G to begin.

    Pasting:
        0-F : as is
        M (inc) : ^
        AD : -
        DA : =
        GO : X

    Test Paste:
        X-0011=11^22^33^44^55^66^77^88^99^-0011
        Now press M to confirm the data has been entered.

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

/*

    TODO:

    - verify whether feeding a 0xff-filled 2K binary file as cart allows
      to write back correctly EPROM or not

*/

#include "emu.h"

#include "cpu/m6502/m6502.h"
#include "imagedev/floppy.h"
#include "machine/mos6530.h"
#include "machine/ram.h"
#include "sound/spkrdev.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "speaker.h"

#include "beta.lh"


namespace {

#define SCREEN_TAG      "screen"
#define M6502_TAG       "m6502"
#define M6532_TAG       "m6532"
#define EPROM_TAG       "eprom"
//#define SPEAKER_TAG       "b237"

class beta_state : public driver_device
{
public:
	beta_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, M6502_TAG),
		m_speaker(*this, "speaker"),
		m_eprom(*this, EPROM_TAG),
		m_q(*this, "Q%u", 6U),
		m_digits(*this, "digit%u", 0U),
		m_leds(*this, "led%u", 0U)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER( trigger_reset );
	void beta(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

	uint8_t riot_pa_r();
	void riot_pa_w(uint8_t data);
	uint8_t riot_pb_r();
	void riot_pb_w(uint8_t data);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(load_beta_eprom);
	DECLARE_DEVICE_IMAGE_UNLOAD_MEMBER(unload_beta_eprom);

	TIMER_CALLBACK_MEMBER(led_refresh);

	void beta_mem(address_map &map) ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<speaker_sound_device> m_speaker;
	required_device<generic_slot_device> m_eprom;
	required_ioport_array<4> m_q;
	output_finder<6> m_digits;
	output_finder<2> m_leds;

	/* EPROM state */
	int m_eprom_oe = 0;
	int m_eprom_ce = 0;
	uint16_t m_eprom_addr = 0;
	uint8_t m_eprom_data = 0;
	uint8_t m_old_data = 0;
	std::vector<uint8_t> m_eprom_rom{};

	/* display state */
	uint8_t m_ls145_p = 0;
	uint8_t m_segment = 0;

	emu_timer *m_led_refresh_timer = nullptr;
};


/* Memory Maps */

void beta_state::beta_mem(address_map &map)
{
	map(0x0000, 0x007f).mirror(0x7f00).m(M6532_TAG, FUNC(mos6532_device::ram_map));
	map(0x0080, 0x00ff).mirror(0x7f00).m(M6532_TAG, FUNC(mos6532_device::io_map));
	map(0x8000, 0x87ff).mirror(0x7800).rom();
}

/* Input Ports */

INPUT_CHANGED_MEMBER( beta_state::trigger_reset )
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? CLEAR_LINE : ASSERT_LINE);
}

static INPUT_PORTS_START( beta )
	PORT_START("Q6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GO") PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Q7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Q8")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DA") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Q9")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("AD") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("SPECIAL")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RESET") PORT_CODE(KEYCODE_R) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(beta_state::trigger_reset), 0)
INPUT_PORTS_END

/* M6532 Interface */

TIMER_CALLBACK_MEMBER(beta_state::led_refresh)
{
	if (m_ls145_p < 6)
		m_digits[m_ls145_p] = m_segment;
}

uint8_t beta_state::riot_pa_r()
{
	/*

	    bit     description

	    PA0     2716 D0, segment D, key bit 0
	    PA1     2716 D1, segment E, key bit 1
	    PA2     2716 D2, segment C, key bit 2
	    PA3     2716 D3, segment G, key bit 3
	    PA4     2716 D4, segment F, key bit 4
	    PA5     2716 D5, segment B
	    PA6     2716 D6, segment A
	    PA7     2716 D7

	*/

	uint8_t data = 0xff;

	switch (m_ls145_p)
	{
	case 6:
	case 7:
	case 8:
	case 9:
		data &= m_q[m_ls145_p - 6]->read();
		break;
	default:
		if (!m_eprom_oe && !m_eprom_ce)
		{
			data = m_eprom_rom[m_eprom_addr & 0x7ff];
			popmessage("EPROM read %04x = %02x\n", m_eprom_addr & 0x7ff, data);
		}
	}

	return data;
}

void beta_state::riot_pa_w(uint8_t data)
{
	/*

	    bit     description

	    PA0     2716 D0, segment D, key bit 0
	    PA1     2716 D1, segment E, key bit 1
	    PA2     2716 D2, segment C, key bit 2
	    PA3     2716 D3, segment G, key bit 3
	    PA4     2716 D4, segment F, key bit 4
	    PA5     2716 D5, segment B
	    PA6     2716 D6, segment A
	    PA7     2716 D7

	*/

//  logerror("PA %02x\n", data);

	/* display */
	m_segment = bitswap<8>(data, 7, 3, 4, 1, 0, 2, 5, 6) & 0x7f;
	m_led_refresh_timer->adjust(attotime::from_usec(70));

	/* EPROM data */
	m_eprom_data = data;
}

uint8_t beta_state::riot_pb_r()
{
	return 0;
}

void beta_state::riot_pb_w(uint8_t data)
{
	/*

	    bit     description

	    PB0     74LS145 P0
	    PB1     74LS145 P1
	    PB2     74LS145 P2
	    PB3     74LS145 P3, 74LS164 D
	    PB4     loudspeaker, data led
	    PB5     address led, 74LS164 CP
	    PB6     2716 _OE
	    PB7     2716 _CE

	*/

	//logerror("PB %02x %02x\n", data, olddata);

	/* display */
	m_ls145_p = data & 0x0f;

	/* speaker */
	m_speaker->level_w(!BIT(data, 4));

	/* address led */
	m_leds[0] = BIT(data, 5);

	/* data led */
	m_leds[1] = !BIT(data, 5);

	/* EPROM address shift */
	if (!BIT(m_old_data, 5) && BIT(data, 5))
	{
		m_eprom_addr <<= 1;
		m_eprom_addr |= BIT(m_old_data, 3);
	}

	/* EPROM output enable */
	m_eprom_oe = BIT(data, 6);

	/* EPROM chip enable */
	m_eprom_ce = BIT(data, 7);

	if (BIT(data, 6) && (!BIT(m_old_data, 7) && BIT(data, 7)))
	{
		popmessage("EPROM write %04x = %02x\n", m_eprom_addr & 0x7ff, m_eprom_data);
		m_eprom_rom[m_eprom_addr & 0x7ff] &= m_eprom_data;
	}

	m_old_data = data;
}

/* EPROM socket */

DEVICE_IMAGE_LOAD_MEMBER(beta_state::load_beta_eprom)
{
	uint32_t const size = m_eprom->common_get_size("rom");

	if (size != 0x800)
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported cartridge size (only 2K cartridges are supported)");

	m_eprom->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_eprom->common_load_rom(m_eprom->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

DEVICE_IMAGE_UNLOAD_MEMBER(beta_state::unload_beta_eprom)
{
	if (!image.loaded_through_softlist())
		image.fwrite(&m_eprom_rom[0], 0x800);
}

/* Machine Initialization */

void beta_state::machine_start()
{
	m_digits.resolve();
	m_leds.resolve();

	m_led_refresh_timer = timer_alloc(FUNC(beta_state::led_refresh), this);

	m_eprom_rom.resize(0x800);

	if (!m_eprom->exists())
		memset(&m_eprom_rom[0], 0xff, 0x800);
	else
	{
		std::string region_tag;
		memcpy(&m_eprom_rom[0], memregion(region_tag.assign(m_eprom->tag()).append(GENERIC_ROM_REGION_TAG).c_str())->base(), 0x800);
	}

	// state saving
	save_item(NAME(m_eprom_oe));
	save_item(NAME(m_eprom_ce));
	save_item(NAME(m_eprom_addr));
	save_item(NAME(m_eprom_data));
	save_item(NAME(m_old_data));
	save_item(NAME(m_eprom_rom));
	save_item(NAME(m_ls145_p));
	save_item(NAME(m_segment));
}

/* Machine Driver */

void beta_state::beta(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, XTAL(4'000'000)/4);
	m_maincpu->set_addrmap(AS_PROGRAM, &beta_state::beta_mem);

	/* video hardware */
	config.set_default_layout(layout_beta);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	/* devices */
	mos6532_device &m6532(MOS6532(config, M6532_TAG, XTAL(4'000'000)/4));
	m6532.pa_rd_callback().set(FUNC(beta_state::riot_pa_r));
	m6532.pa_wr_callback().set(FUNC(beta_state::riot_pa_w));
	m6532.pb_rd_callback().set(FUNC(beta_state::riot_pb_r));
	m6532.pb_wr_callback().set(FUNC(beta_state::riot_pb_w));
	m6532.irq_wr_callback().set_inputline(m_maincpu, M6502_IRQ_LINE);

	/* EPROM socket */
	generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config, EPROM_TAG, generic_plain_slot, nullptr, "bin,rom"));
	cartslot.set_device_load(FUNC(beta_state::load_beta_eprom));
	cartslot.set_device_unload(FUNC(beta_state::unload_beta_eprom));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("256");
}

/* ROMs */

ROM_START( beta )
	ROM_REGION( 0x10000, M6502_TAG, 0 )
	ROM_LOAD( "beta.rom", 0x8000, 0x0800, CRC(d42fdb17) SHA1(595225a0cd43dd76c46b2aff6c0f27d5991cc4f0))
ROM_END

} // anonymous namespace


/* System Drivers */

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY      FULLNAME  FLAGS
COMP( 1984, beta, 0,      0,      beta,    beta,  beta_state, empty_init, "Pitronics", "Beta",   MACHINE_SUPPORTS_SAVE )



betacam.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Felipe Sanches
/****************************************************************************

    Skeleton driver for Sony BETACAM-SP Videocassette Players and Recorders

    List of major ICs:
     - IC202 - H8/534 (Hitachi Single-Chip Microcomputer)
     - IC227 - MB88201 (MB88200 Series CMOS Low end Single-Chip 4-Bit Microprocessor)
     - IC211 - CXD1095BQ (C-MOS I/O PORT EXPANDER)
     - IC226 - CXD1095BQ (C-MOS I/O PORT EXPANDER)
     - IC100 - CXD8384Q (C-MOS LTC READER/GENERATOR)
     - IC219 - CXD2202Q (SERVO IC)
     - IC213 - LC3564BM-10 (Sanyo 64Kbit SRAM (8192-word x8-bit))
     - IC1 - D70320GJ-8 (CPU NEC V25)
     - IC3 - LC3564BM-10 (Sanyo 64Kbit SRAM (8192-word x8-bit))
     - IC5 - CXD8176AQ (C-MOS DUAL PORT RAM CONTROLLER)
     - IC15 - D6453GT (MOS INTEGRATED CIRCUIT CMOS LSI FOR 12 lines X 24 columns CHARACTER DISPLAY ON SCREEN)

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

#include "emu.h"
#include "cpu/nec/v25.h"
#include "cpu/h8500/h8534.h"
#include "machine/cxd1095.h"


namespace {

class betacam_state : public driver_device
{
public:
	betacam_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_systemcpu(*this, "systemcpu")
		, m_servocpu(*this, "servocpu")
	{
	}

	void betacam(machine_config &config);

private:
	void system_mem_map(address_map &map) ATTR_COLD;
	void servo_mem_map(address_map &map) ATTR_COLD;

	required_device<v25_device> m_systemcpu;
	required_device<h8534_device> m_servocpu;
};


void betacam_state::system_mem_map(address_map &map)
{
	map(0x17f00, 0x17fff).ram();                        //   256b ?
	map(0x18000, 0x189fe).ram();                        //   8kb SRAM at IC3
	map(0x189ff, 0x189ff).noprw();                      //   ZERO
	map(0x18a00, 0x19fff).ram();                        //   8kb SRAM at IC3 (init SS:SP)
	map(0x20800, 0x20fff).ram().share("svram");         //   2kb servo dual-port SRAM
	map(0x21800, 0x2183f).ram().share("ltcram");        //   64b LTC SRAM
	map(0xe0000, 0xfffff).rom().region("systemcpu", 0); // 128kb EPROM at IC4
}


void betacam_state::servo_mem_map(address_map &map)
{
	map(0x00000, 0x3ffff).rom().region("servocpu", 0); //guessed
	map(0x40000, 0x41fff).ram(); //guessed
	//map(0x?????, 0x?????).rw("cxdio0", FUNC(cxd1095_device::read), FUNC(cxd1095_device::write));
	//map(0x?????, 0x?????).rw("cxdio1", FUNC(cxd1095_device::read), FUNC(cxd1095_device::write));
}


static INPUT_PORTS_START(betacam)
	PORT_START("DSW1")
	PORT_DIPUNUSED_DIPLOC( 0x01, 0x00, "SW1:1" )
	PORT_DIPNAME( 0x02, 0x00, "RGB Output Sel" ) PORT_DIPLOCATION("SW1:2")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x04, 0x00, "RGB Input Sel" ) PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(    0x04, DEF_STR( Yes ) )
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPNAME( 0x08, 0x00, "Wide" ) PORT_DIPLOCATION("SW1:4")
	PORT_DIPSETTING(    0x08, DEF_STR( Yes ) )
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPNAME( 0x10, 0x00, "R/P E/F" ) PORT_DIPLOCATION("SW1:5")
	PORT_DIPSETTING(    0x10, DEF_STR( Yes ) )
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPNAME( 0x20, 0x20, "Rec/Player" ) PORT_DIPLOCATION("SW1:6")
	PORT_DIPSETTING(    0x20, DEF_STR( Yes ) )
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPNAME( 0x40, 0x00, "J U/C" ) PORT_DIPLOCATION("SW1:7")
	PORT_DIPSETTING(    0x40, DEF_STR( Yes ) )
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPNAME( 0x80, 0x00, "NTSC / PAL" ) PORT_DIPLOCATION("SW1:8")
	PORT_DIPSETTING(    0x80, DEF_STR( Yes ) )
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
INPUT_PORTS_END

void betacam_state::betacam(machine_config &config)
{
	V25(config, m_systemcpu, 16_MHz_XTAL); // NEC upD70320GJ-8
	m_systemcpu->set_addrmap(AS_PROGRAM, &betacam_state::system_mem_map);
	m_systemcpu->p2_in_cb().set_ioport("DSW1");

	HD6435348(config, m_servocpu, 20_MHz_XTAL); //Actual chip is marked "H8/534 6435348F 10"
	m_servocpu->set_mode(3);
	m_servocpu->set_addrmap(AS_PROGRAM, &betacam_state::servo_mem_map);

	//CXD1095(config, "cxdio0");
	//CXD1095(config, "cxdio1");
}

ROM_START(uvw1200)
	ROM_REGION(0x20000, "systemcpu", 0)
	ROM_LOAD("75932697_uvw-1000_sy_v2.00_f8b4.ic4", 0x00000, 0x20000, CRC(08e9b891) SHA1(3e01f0e037e83825dcb4a745ddc9148cf3cc7674))

	ROM_REGION(0x40000, "servocpu", 0)
	ROM_LOAD("75953491_uvw-1000_sv_v2.04_f024.ic212", 0x00000, 0x40000, CRC(dc2b8d4b) SHA1(f10a3dc0c317582e3dcb6f3dcc741c0d55c6fd22))
ROM_END

ROM_START(uvw1600)
	ROM_REGION(0x20000, "systemcpu", 0)
	ROM_LOAD("75925907_uvw-1000_sy_v1.03_e3b4.ic4", 0x00000, 0x20000, CRC(f9e575ce) SHA1(2f802c5685f7ce00586079ad5bc456083c595d66))

	ROM_REGION(0x40000, "servocpu", 0)
	ROM_LOAD("75927098_uvw-1000_sv_v1.04_150c.ic212", 0x00000, 0x40000, CRC(b4cb9c02) SHA1(92ae5ce303b9f67977b960047bac7f6bb337b8c0))
ROM_END

ROM_START(uvw1800)
	ROM_REGION(0x20000, "systemcpu", 0)
	ROM_LOAD("75925907_uvw-1000_sy_v1.03_e3b4.ic4", 0x00000, 0x20000, CRC(f9e575ce) SHA1(2f802c5685f7ce00586079ad5bc456083c595d66))

	ROM_REGION(0x40000, "servocpu", 0)
	ROM_LOAD("75927098_uvw-1000_sv_v1.04_150c.ic212", 0x00000, 0x40000, CRC(b4cb9c02) SHA1(92ae5ce303b9f67977b960047bac7f6bb337b8c0))
ROM_END

} // anonymous namespace


//   YEAR  NAME   PARENT/COMPAT MACHINE  INPUT    CLASS          INIT       COMPANY  FULLNAME                                                FLAGS
SYST(199?, uvw1200,   0, 0,     betacam, betacam, betacam_state, empty_init, "Sony", "BETACAM-SP Videocassette Player UVW-1200 RGB",          MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(199?, uvw1600,   0, 0,     betacam, betacam, betacam_state, empty_init, "Sony", "BETACAM-SP Videocassette Player/Recorder UVW-1600 RGB", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(199?, uvw1800,   0, 0,     betacam, betacam, betacam_state, empty_init, "Sony", "BETACAM-SP Videocassette Player/Recorder UVW-1800 RGB", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



bigbord2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Ferguson / Digital Research Computers Big Board II

2009-05-12 Skeleton driver.

This is very much under construction.

Despite the name, this is not like the xerox or bigboard at all.

It is compatible only if the software uses the same published
calls to the bios. Everything else is different.

80 = sio ce
84 = ctca ce
88 = ctcb ce
8c = dma ce
c0 = prog
c4 = status 7,6,5,4 = sw1-4; 3 = kbdstb; 2 = motor; 1 = rxdb; 0 = rxda
c8 = sys1
cc = sys2
d0 = kbd
d4 = 1793 ce
d8 = port7
dc = 6845 ce


Difficulties encountered:

The FDC has a INTRQ pin, the diagram says it goes to page 6, but
it just vanishes instead.

What works:

Turn it on, wait for cursor to appear in the top corner. Press Enter.
Now you can enter commands.

Memory banking:

0000-7FFF are controlled by bit 0 of port C8, and select ROM&video, or RAM
8000-FFFF control if RAM is onboard, or on S100 bus (do not know what controls this)
We do not emulate the S100, so therefore banks 1&2 are the same as 3&4.
The switching from port C8 is emulated.

ToDo:
- Finish floppy disk support (i have no boot disk)
- (optional) Connect SIO to RS232.
- (optional) Connect up the SASI, Centronics and other interfaces on ports D8-DB.
- (optional) Connect up the programming port C0-C3.
- (optional) Connect up the numerous board jumpers.
- Need software

Monitor commands:
B - boot from disk
C - copy memory
D - dump memory
F - fill memory
G - go
I - in port
M - modify memory
O - out port
R - read a sector
T - test memory
V - compare blocks of memory
X - change banks

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



#include "emu.h"

#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/z80daisy.h"
#include "machine/74259.h"
#include "machine/clock.h"
#include "machine/keyboard.h"
#include "machine/wd_fdc.h"
#include "machine/z80ctc.h"
#include "machine/z80dma.h"
#include "machine/z80sio.h"
#include "sound/beep.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class bigbord2_state : public driver_device
{
public:
	bigbord2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_palette(*this, "palette")
		, m_maincpu(*this, "maincpu")
		, m_p_chargen(*this, "chargen")
		, m_ctc1(*this, "ctc1")
		, m_ctc2(*this, "ctc2")
		, m_sio(*this, "sio")
		, m_dma(*this, "dma")
		, m_fdc(*this, "fdc")
		, m_floppy0(*this, "fdc:0")
		, m_floppy1(*this, "fdc:1")
		, m_syslatch1(*this, "syslatch1")
		, m_dsw(*this, "DSW")
		, m_bankr(*this, "bankr")
		, m_bankw(*this, "bankw")
		, m_bankv(*this, "bankv")
		, m_banka(*this, "banka")
		, m_bankv1(*this, "bankv1")
		, m_banka1(*this, "banka1")
	{ }

	void bigbord2(machine_config &config);
	void init_bigbord2();

private:
	void side_select_w(int state);
	void smc1_w(int state);
	void smc2_w(int state);
	void head_load_w(int state);
	void disk_motor_w(int state);
	void syslatch2_w(u8 data);
	u8 status_port_r();
	u8 kbd_r();
	void kbd_put(u8 data);
	void clock_w(int state);
	void ctc_z1_w(int state);
	void sio_wrdya_w(int state);
	void sio_wrdyb_w(int state);
	void fdc_drq_w(int state);
	u8 memory_read_byte(offs_t offset);
	void memory_write_byte(offs_t offset, u8 data);
	u8 io_read_byte(offs_t offset);
	void io_write_byte(offs_t offset, u8 data);
	MC6845_UPDATE_ROW(crtc_update_row);
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	u8 crt8002(u8 ac_ra, u8 ac_chr, u8 ac_attr, uint16_t ac_cnt, bool ac_curs);
	u8 m_term_data = 0U;
	u8 m_term_status = 0U;
	uint16_t m_cnt = 0U;
	bool m_cc[8]{};
	floppy_image_device *m_floppy;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	address_space *m_mem;
	address_space *m_io;
	std::unique_ptr<u8[]> m_vram; // video ram 2k
	std::unique_ptr<u8[]> m_aram; // attribute ram 2k
	std::unique_ptr<u8[]> m_ram;  // main ram 64k
	std::unique_ptr<u8[]> m_dummy;  // black hole for write to rom
	required_device<palette_device> m_palette;
	required_device<z80_device> m_maincpu;
	required_region_ptr<u8> m_p_chargen;
	required_device<z80ctc_device> m_ctc1;
	required_device<z80ctc_device> m_ctc2;
	required_device<z80sio_device> m_sio;
	required_device<z80dma_device> m_dma;
	required_device<mb8877_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	required_device<floppy_connector> m_floppy1;
	required_device<ls259_device> m_syslatch1;
	required_ioport m_dsw;
	required_memory_bank m_bankr;
	required_memory_bank m_bankw;
	required_memory_bank m_bankv;
	required_memory_bank m_banka;
	required_memory_bank m_bankv1;
	required_memory_bank m_banka1;
};

/* Status port
    0 = RXDA
    1 = RXDB
    2 = MOTOR
    3 = KBDSTB
    4 = DIPSW 1
    5 = DIPSW 2
    6 = DIPSW 3
    7 = DIPSW 4 */

u8 bigbord2_state::status_port_r()
{
	u8 ret = m_term_status | 3 | (m_syslatch1->q6_r() << 2) | m_dsw->read();
	m_term_status = 0;
	return ret;
}

// KBD port - read ascii value of key pressed

u8 bigbord2_state::kbd_r()
{
	u8 ret = m_term_data;
	m_term_data = 0;
	return ret;
}

void bigbord2_state::kbd_put(u8 data)
{
	if (data)
	{
		m_term_data = data;
		m_term_status = 8;
		m_ctc1->trg0(0);
		m_ctc1->trg0(1);
	}
}

void bigbord2_state::sio_wrdya_w(int state)
{
	m_cc[0] = state;
}

void bigbord2_state::sio_wrdyb_w(int state)
{
	m_cc[1] = state;
}

void bigbord2_state::fdc_drq_w(int state)
{
	m_cc[2] = state;
}


/* Z80 DMA */


u8 bigbord2_state::memory_read_byte(offs_t offset)
{
	return m_mem->read_byte(offset);
}

void bigbord2_state::memory_write_byte(offs_t offset, u8 data)
{
	m_mem->write_byte(offset, data);
}

u8 bigbord2_state::io_read_byte(offs_t offset)
{
	return m_io->read_byte(offset);
}

void bigbord2_state::io_write_byte(offs_t offset, u8 data)
{
	m_io->write_byte(offset, data);
}


/* Read/Write Handlers */

void bigbord2_state::side_select_w(int state)
{
	if (m_floppy)
		m_floppy->ss_w(state);
}

void bigbord2_state::smc1_w(int state)
{
	// connects to "U6 (FDC9216B)" which drives the fdc "rawread" and "rclk" pins
}

void bigbord2_state::smc2_w(int state)
{
	// connects to "U6 (FDC9216B)" which drives the fdc "rawread" and "rclk" pins
}

void bigbord2_state::head_load_w(int state)
{
	// connects to HLD pin on floppy drive
}

void bigbord2_state::disk_motor_w(int state)
{
	// motor on
	if (m_floppy)
		m_floppy->mon_w(state ? 0 : 1);
}

void bigbord2_state::syslatch2_w(u8 data)
{
	/*

	    bit     signal      description

	    0,1,2   operates a 74LS151 for 8 individual inputs to DMA RDY
	      0     W/RDYA      channel A of SIO
	      1     W/RDYB      channel B of SIO
	      2     DRQ         DRQ on fdc
	      3     JB7 pin 1
	      4     JB7 pin 2
	      5     JB7 pin 3
	      6     JB7 pin 4
	      7     JB7 pin 5
	    3       /TEST       test pin on FDC
	    4       DS3         drive 3 select
	    5       DS2         drive 2 select
	    6       DS1         drive 1 select
	    7       DS0         drive 0 select

	*/

	/* drive select */
	m_floppy = nullptr;
	if (BIT(data, 7)) m_floppy = m_floppy0->get_device();
	if (BIT(data, 6)) m_floppy = m_floppy1->get_device();
	//if (BIT(data, 5)) m_floppy = m_floppy2->get_device();
	//if (BIT(data, 4)) m_floppy = m_floppy3->get_device();

	m_fdc->set_floppy(m_floppy);
	if (m_floppy)
	{
		m_floppy->ss_w(m_syslatch1->q1_r());
		m_floppy->mon_w(m_syslatch1->q6_r() ? 0 : 1);
	}

	m_dma->rdy_w(m_cc[data & 7]);
}



/* Memory Maps */

void bigbord2_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x5fff).bankr(m_bankr).bankw(m_bankw);
	map(0x6000, 0x67ff).bankrw(m_bankv);
	map(0x6800, 0x6fff).bankrw(m_bankv1);
	map(0x7000, 0x77ff).bankrw(m_banka);
	map(0x7800, 0x7fff).bankrw(m_banka1);
	map(0x8000, 0xffff).ram();
}

void bigbord2_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x80, 0x83).rw(m_sio, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w)); // u16
	map(0x84, 0x87).rw(m_ctc1, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write)); // u37 has issues
	map(0x88, 0x8b).rw(m_ctc2, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write)); // u21
	map(0x8c, 0x8f).rw(m_dma, FUNC(z80dma_device::read), FUNC(z80dma_device::write)); // u62
	map(0xc0, 0xc3).w("proglatch", FUNC(ls259_device::write_nibble_d3)); // u41 - eprom programming port
	map(0xc4, 0xc7).r(FUNC(bigbord2_state::status_port_r)); // u11
	map(0xc8, 0xcb).w(m_syslatch1, FUNC(ls259_device::write_nibble_d3)); // u14
	map(0xcc, 0xcf).w(FUNC(bigbord2_state::syslatch2_w));
	map(0xd0, 0xd3).r(FUNC(bigbord2_state::kbd_r)); // u1
	map(0xd4, 0xd7).rw(m_fdc, FUNC(mb8877_device::read), FUNC(mb8877_device::write)); // u10
	//map(0xd8, 0xdb).rw(FUNC(bigbord2_state::portd8_r), FUNC(bigbord2_state::portd8_w)); // various external data ports; DB = centronics printer
	map(0xd9, 0xd9).w("outlatch1", FUNC(ls259_device::write_nibble_d3)); // u96
	map(0xdc, 0xdc).mirror(2).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w)); // u30
	map(0xdd, 0xdd).mirror(2).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
}


/* Input Ports */

static INPUT_PORTS_START( bigbord2 )
	PORT_START("DSW")
	PORT_BIT( 0xf, 0, IPT_UNUSED )
	PORT_DIPNAME( 0x10, 0x10, "Switch 4") PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(    0x10, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x20, 0x00, "Switch 3") PORT_DIPLOCATION("SW1:2")
	PORT_DIPSETTING(    0x20, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x40, 0x00, "Switch 2") PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(    0x40, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x80, 0x00, "Switch 1") PORT_DIPLOCATION("SW1:4")
	PORT_DIPSETTING(    0x80, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
INPUT_PORTS_END


/* Z80 CTC */

void bigbord2_state::clock_w(int state)
{
	m_ctc2->trg0(state);
	m_ctc2->trg1(state);
	if (m_floppy)
		m_ctc1->trg1(m_floppy->idx_r());
}

// there's a multitude of optional jumpers in this area, but this will do
void bigbord2_state::ctc_z1_w(int state)
{
	m_sio->rxca_w(state);
	m_sio->txca_w(state);
}

/* Z80 Daisy Chain */

static const z80_daisy_config daisy_chain[] =
{
	{ "dma" },
	{ "ctc1" },
	{ "ctc2" },
	{ "sio" },
	{ nullptr }
};

/* WD1793 Interface */

static void bigbord2_floppies(device_slot_interface &device)
{
	device.option_add("8dsdd", FLOPPY_8_DSDD);
}


/* Machine Initialization */

void bigbord2_state::machine_start()
{
	save_pointer(NAME(m_vram), 0x2000);
	save_pointer(NAME(m_aram), 0x2000);
	save_pointer(NAME(m_ram),  0x8000);

	/* register for state saving */
	save_item(NAME(m_term_data));
	save_item(NAME(m_term_status));
	save_item(NAME(m_cnt));
	save_item(NAME(m_cc));

	m_floppy = nullptr;
}

void bigbord2_state::machine_reset()
{
	u8 i;
	for (i = 0; i < 8; i++)
		m_cc[i] = 1;
	m_cc[2] = 0;
	m_bankr->set_entry(0);
	m_bankw->set_entry(0);
	m_bankv->set_entry(0);
	m_banka->set_entry(0);
	m_bankv1->set_entry(0);
	m_banka1->set_entry(0);
}

void bigbord2_state::init_bigbord2()
{
	m_mem = &m_maincpu->space(AS_PROGRAM);
	m_io = &m_maincpu->space(AS_IO);
	m_vram = std::make_unique<u8[]>(0x2000);
	m_aram = std::make_unique<u8[]>(0x2000);
	m_ram = make_unique_clear<u8[]>(0x8000);
	m_dummy = std::make_unique<u8[]>(0x6000);

	u8 *v = m_vram.get();
	u8 *a = m_aram.get();
	u8 *r = m_ram.get();
	u8 *d = m_dummy.get();
	u8 *m = memregion("maincpu")->base();
	m_bankr->configure_entry( 0, &m[0]);
	m_bankr->configure_entry( 1, r);
	m_bankw->configure_entry( 0, d);
	m_bankw->configure_entry( 1, r);
	m_bankv->configure_entry( 0, v);
	m_bankv->configure_entry( 1, r+0x6000);
	m_bankv1->configure_entry(0, v);
	m_bankv1->configure_entry(1, r+0x6800);
	m_banka->configure_entry( 0, a);
	m_banka->configure_entry( 1, r+0x7000);
	m_banka1->configure_entry(0, a);
	m_banka1->configure_entry(1, r+0x7800);
}


/* Screen */

/* F4 Character Displayer */
static const gfx_layout crt8002_charlayout =
{
	8, 12,                   /* 7 x 11 characters */
	128,                  /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_crt8002 )
	GFXDECODE_ENTRY( "chargen", 0x0000, crt8002_charlayout, 0, 1 )
GFXDECODE_END

u8 bigbord2_state::crt8002(u8 ac_ra, u8 ac_chr, u8 ac_attr, uint16_t ac_cnt, bool ac_curs)
{
	u8 gfx = 0;
	switch (ac_attr & 3)
	{
		case 0: // lores gfx
			switch (ac_ra)
			{
				case 0:
				case 1:
				case 2:
					gfx = (BIT(ac_chr, 7) ? 0xf8 : 0) | (BIT(ac_chr, 3) ? 7 : 0);
					break;
				case 3:
				case 4:
				case 5:
					gfx = (BIT(ac_chr, 6) ? 0xf8 : 0) | (BIT(ac_chr, 2) ? 7 : 0);
					break;
				case 6:
				case 7:
				case 8:
					gfx = (BIT(ac_chr, 5) ? 0xf8 : 0) | (BIT(ac_chr, 1) ? 7 : 0);
					break;
				default:
					gfx = (BIT(ac_chr, 4) ? 0xf8 : 0) | (BIT(ac_chr, 0) ? 7 : 0);
					break;
			}
			break;
		case 1: // external mode
			gfx = bitswap<8>(ac_chr, 0,1,2,3,4,5,6,7);
			break;
		case 2: // thin gfx
			break;
		case 3: // alpha
			gfx = m_p_chargen[((ac_chr & 0x7f)<<4) | ac_ra];
			break;
	}

	if (BIT(ac_attr, 3) & (ac_ra == 11)) // underline
		gfx = 0xff;
	if (BIT(ac_attr, 2) & ((ac_ra == 5) | (ac_ra == 6))) // strike-through
		gfx = 0xff;
	if (BIT(ac_attr, 6) & BIT(ac_cnt, 13)) // flash
		gfx = 0;
	if (BIT(ac_attr, 5)) // blank
		gfx = 0;
	if (ac_curs && BIT(ac_cnt, 14)) // cursor
		gfx ^= 0xff;
	if (BIT(ac_attr, 4)) // reverse video
		gfx ^= 0xff;
	return gfx;
}

MC6845_UPDATE_ROW( bigbord2_state::crtc_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	uint32_t *p = &bitmap.pix(y);
	ra &= 15;
	m_cnt++;

	for (u16 x = 0; x < x_count; x++)
	{
		u16 mem = (ma + x) & 0x7ff;
		u8 attr = m_aram[mem];
		u8 chr = m_vram[mem];

		/* process attributes */
		u8 gfx = crt8002(ra, chr, attr, m_cnt, (x==cursor_x));

		/* Display a scanline of a character */
		*p++ = palette[BIT( gfx, 7 )];
		*p++ = palette[BIT( gfx, 6 )];
		*p++ = palette[BIT( gfx, 5 )];
		*p++ = palette[BIT( gfx, 4 )];
		*p++ = palette[BIT( gfx, 3 )];
		*p++ = palette[BIT( gfx, 2 )];
		*p++ = palette[BIT( gfx, 1 )];
		*p++ = palette[BIT( gfx, 0 )];
	}
}

/* Machine Drivers */

#define MAIN_CLOCK 8_MHz_XTAL / 2

void bigbord2_state::bigbord2(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, MAIN_CLOCK);  // U39
	m_maincpu->set_addrmap(AS_PROGRAM, &bigbord2_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &bigbord2_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);
	m_maincpu->busack_cb().set(m_dma, FUNC(z80dma_device::bai_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(10.69425_MHz_XTAL, 700, 0, 560, 260, 0, 240);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	GFXDECODE(config, "gfxdecode", m_palette, gfx_crt8002);
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	CLOCK(config, "ctc_clock", MAIN_CLOCK).signal_handler().set(FUNC(bigbord2_state::clock_w));

	/* devices */
	Z80DMA(config, m_dma, MAIN_CLOCK);  // U62
	m_dma->out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	m_dma->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_dma->in_mreq_callback().set(FUNC(bigbord2_state::memory_read_byte));
	m_dma->out_mreq_callback().set(FUNC(bigbord2_state::memory_write_byte));
	m_dma->in_iorq_callback().set(FUNC(bigbord2_state::io_read_byte));
	m_dma->out_iorq_callback().set(FUNC(bigbord2_state::io_write_byte));

	Z80SIO(config, m_sio, MAIN_CLOCK); // U16
	m_sio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_sio->out_synca_callback().set(m_ctc1, FUNC(z80ctc_device::trg2));
	m_sio->out_wrdya_callback().set(FUNC(bigbord2_state::sio_wrdya_w));
	m_sio->out_wrdyb_callback().set(FUNC(bigbord2_state::sio_wrdyb_w));

	Z80CTC(config, m_ctc1, MAIN_CLOCK); // U37
	m_ctc1->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80CTC(config, m_ctc2, MAIN_CLOCK); // U21
	m_ctc2->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc2->zc_callback<0>().set(m_sio, FUNC(z80sio_device::rxtxcb_w));    // to SIO Ch B
	m_ctc2->zc_callback<1>().set(FUNC(bigbord2_state::ctc_z1_w));  // to SIO Ch A
	m_ctc2->zc_callback<2>().set(m_ctc2, FUNC(z80ctc_device::trg3));

	MB8877(config, m_fdc, 16_MHz_XTAL / 8); // U10 : 2MHz for 8 inch, or 1MHz otherwise (jumper-selectable)
	//m_fdc->intrq_wr_callback().set_inputline(m_maincpu, ??); // info missing from schematic
	m_fdc->drq_wr_callback().set(FUNC(bigbord2_state::fdc_drq_w));
	FLOPPY_CONNECTOR(config, "fdc:0", bigbord2_floppies, "8dsdd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", bigbord2_floppies, "8dsdd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	mc6845_device &crtc(MC6845(config, "crtc", 16_MHz_XTAL / 8));  // U30
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(8);
	crtc.set_update_row_callback(FUNC(bigbord2_state::crtc_update_row));
	crtc.out_vsync_callback().set(m_ctc1, FUNC(z80ctc_device::trg3));

	ls259_device &proglatch(LS259(config, "proglatch")); // U41
	// d0=to U42; d1=DECODE; d3=PGM; d4=VPPENB; d5=STD-B8
	proglatch.q_out_cb<6>().set("outlatch1", FUNC(ls259_device::clear_w)); // FCRST - also resets the 8877

	LS259(config, m_syslatch1, 0); // U14
	m_syslatch1->q_out_cb<0>().set_membank(m_bankr); // D_S
	m_syslatch1->q_out_cb<0>().append_membank(m_bankv);
	m_syslatch1->q_out_cb<0>().append_membank(m_banka);
	m_syslatch1->q_out_cb<0>().append_membank(m_bankw);
	m_syslatch1->q_out_cb<0>().append_membank(m_bankv1);
	m_syslatch1->q_out_cb<0>().append_membank(m_banka1);
	m_syslatch1->q_out_cb<1>().set(FUNC(bigbord2_state::side_select_w)); // SIDSEL
	m_syslatch1->q_out_cb<2>().set(FUNC(bigbord2_state::smc1_w)); // SMC1
	m_syslatch1->q_out_cb<3>().set(FUNC(bigbord2_state::smc2_w)); // SMC2
	m_syslatch1->q_out_cb<4>().set(m_fdc, FUNC(mb8877_device::dden_w)); // DDEN
	m_syslatch1->q_out_cb<5>().set(FUNC(bigbord2_state::head_load_w)); // HLD
	m_syslatch1->q_out_cb<6>().set(FUNC(bigbord2_state::disk_motor_w)); // MOTOR
	m_syslatch1->q_out_cb<7>().set("beeper", FUNC(beep_device::set_state)); // BELL

	LS259(config, "outlatch1", 0); // U96

	/* keyboard */
	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(bigbord2_state::kbd_put));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, "beeper", 950).add_route(ALL_OUTPUTS, "mono", 0.50); // actual frequency is unknown
}


/* ROMs */

ROM_START( bigbord2 )
	// for optional roms and eproms
	ROM_REGION( 0x6000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "bigbrdii.u85", 0x0000, 0x1000, CRC(c588189e) SHA1(4133903171ee8b9fcf12cc72de843af782b4a645) )

	ROM_REGION( 0x800, "chargen", 0 )
	ROM_LOAD( "8002.u52", 0x0000, 0x0800, CRC(fdd6eb13) SHA1(a094d416e66bdab916e72238112a6265a75ca690) )

	ROM_REGION( 0x1800, "proms", 0)
	ROM_LOAD( "pal16l8.u23", 0x0000, 0x0400, NO_DUMP )
	ROM_LOAD( "pal10l8.u34", 0x0400, 0x0400, NO_DUMP )
ROM_END

} // anonymous namespace


/* System Drivers */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT           COMPANY                       FULLNAME        FLAGS
COMP( 1982, bigbord2, 0,      0,      bigbord2, bigbord2, bigbord2_state, init_bigbord2, "Digital Research Computers", "Big Board II", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



binbug.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

        BINBUG

        2013-01-14 Driver created

        All input must be in uppercase.

        Commands:
        A - See and alter memory
        B - Set breakpoint (2 permitted)
        C - Clear breakpoint
        D - cassette save
        G - Go to address, run
        L - cassette load
        S - See and alter registers

        BINBUG is an alternate bios to PIPBUG, however it uses its own
        video output. Method of output is through a DG640 board.

        Keyboard input, like PIPBUG, is via a serial device.
        The baud rate is 300, 8N1.

        The SENSE and FLAG lines are used for 300 baud cassette, in
        conjunction with unknown hardware.

        There are 3 versions of BINBUG:

        - 3.6 300 baud tape routines, 300 baud keyboard, memory-mapped VDU

        - 4.4 300 baud keyboard, ACOS tape system, advanced video routines

        - 5.2 ACOS tape system, 1200 baud terminal


        ToDo:
        - Need dumps of 4.4 and 5.2.

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

#include "emu.h"
#include "bus/rs232/keyboard.h"
#include "cpu/s2650/s2650.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "machine/clock.h"
#include "machine/timer.h"
#include "bus/s100/s100.h"
#include "bus/s100/dg640.h"
#include "speaker.h"


namespace {

class binbug_state : public driver_device
{
public:
	binbug_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cass(*this, "cassette")
		, m_rs232(*this, "keyboard")
		, m_clock(*this, "cass_clock")
		, m_s100(*this, "s100")
	{ }

	void binbug(machine_config &config);

private:
	u8 mem_r(offs_t offset);
	void mem_w(offs_t offset, u8 data);
	void kansas_w(int state);
	int serial_r();
	void serial_w(int state);
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);

	u8 m_cass_data[4]{};
	bool m_cassold = 0, m_cassinbit = 0, m_cassoutbit = 0;

	void mem_map(address_map &map) ATTR_COLD;
	void machine_start() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<cassette_image_device> m_cass;
	required_device<rs232_port_device> m_rs232;
	required_device<clock_device> m_clock;
	required_device<s100_bus_device> m_s100;
};

void binbug_state::kansas_w(int state)
{
	if ((m_cass->get_state() & CASSETTE_MASK_UISTATE) != CASSETTE_RECORD)
		return;

	u8 twobit = m_cass_data[3] & 15;

	if (state)
	{
		if (twobit == 0)
			m_cassold = m_cassoutbit;

		if (m_cassold)
			m_cass->output(BIT(m_cass_data[3], 0) ? -1.0 : +1.0); // 2400Hz
		else
			m_cass->output(BIT(m_cass_data[3], 1) ? -1.0 : +1.0); // 1200Hz

		m_cass_data[3]++;
	}
}

TIMER_DEVICE_CALLBACK_MEMBER( binbug_state::kansas_r )
{
	// no tape - set to idle
	m_cass_data[1]++;
	if (m_cass_data[1] > 32)
	{
		m_cass_data[1] = 32;
		m_cassinbit = 1;
	}

	if ((m_cass->get_state() & CASSETTE_MASK_UISTATE) != CASSETTE_PLAY)
		return;

	/* cassette - turn 1200/2400Hz to a bit */
	u8 cass_ws = (m_cass->input() > +0.04) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		m_cassinbit = (m_cass_data[1] < 12) ? 1 : 0;
		m_cass_data[1] = 0;
	}
}

int binbug_state::serial_r()
{
	return m_rs232->rxd_r() & m_cassinbit;
}

void binbug_state::serial_w(int state)
{
	m_cassoutbit = state;
}

u8 binbug_state::mem_r(offs_t offset)
{
	return m_s100->smemr_r(offset + 0x7800);
}

void binbug_state::mem_w(offs_t offset, u8 data)
{
	m_s100->mwrt_w(offset + 0x7800, data);
}

void binbug_state::machine_start()
{
	save_item(NAME(m_cass_data));
	save_item(NAME(m_cassold));
	save_item(NAME(m_cassinbit));
	save_item(NAME(m_cassoutbit));
}

void binbug_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x03ff).rom();
	map(0x0400, 0x77ff).ram();
	map(0x7800, 0x7fff).rw(FUNC(binbug_state::mem_r),FUNC(binbug_state::mem_w));
}


/* Input ports */
static INPUT_PORTS_START( binbug )
INPUT_PORTS_END


QUICKLOAD_LOAD_MEMBER(binbug_state::quickload_cb)
{
	int const quick_length = image.length();
	if (quick_length < 0x0444)
	{
		return std::make_pair(image_error::INVALIDLENGTH, "File too short");
	}
	else if (quick_length > 0x8000)
	{
		return std::make_pair(image_error::INVALIDLENGTH, "File too long");
	}

	std::vector<u8> quick_data;
	quick_data.resize(quick_length);
	int const read_ = image.fread( &quick_data[0], quick_length);
	if (read_ != quick_length)
	{
		return std::make_pair(image_error::UNSPECIFIED, "Cannot read the file");
	}
	else if (quick_data[0] != 0xc4)
	{
		return std::make_pair(image_error::INVALIDIMAGE, "Invalid header");
	}

	int const exec_addr = quick_data[1] * 256 + quick_data[2];
	if (exec_addr >= quick_length)
	{
		return std::make_pair(
				image_error::INVALIDIMAGE,
				util::string_format("Exec address %04X beyond end of file %04X", exec_addr, quick_length));
	}

	address_space &space = m_maincpu->space(AS_PROGRAM);
	constexpr int QUICK_ADDR = 0x440;
	for (int i = QUICK_ADDR; i < read_; i++)
		space.write_byte(i, quick_data[i]);

	// display a message about the loaded quickload
	image.message(" Quickload: size=%04X : exec=%04X",quick_length,exec_addr);

	// Start the quickload
	m_maincpu->set_state_int(S2650_PC, exec_addr);

	return std::make_pair(std::error_condition(), std::string());
}

static DEVICE_INPUT_DEFAULTS_START( keyboard )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_300 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

static void binbug_s100_devices(device_slot_interface &device)
{
	device.option_add("dg640", S100_DG640);
}

void binbug_state::binbug(machine_config &config)
{
	SPEAKER(config, "mono").front_center();

	/* Cassette */
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	TIMER(config, "kansas_r").configure_periodic(FUNC(binbug_state::kansas_r), attotime::from_hz(40000));

	CLOCK(config, m_clock, 4'800); // 300 baud x 16(divider) = 4800
	m_clock->signal_handler().set(FUNC(binbug_state::kansas_w));

	/* basic machine hardware */
	s2650_device &maincpu(S2650(config, m_maincpu, XTAL(1'000'000)));
	maincpu.set_addrmap(AS_PROGRAM, &binbug_state::mem_map);
	maincpu.sense_handler().set(FUNC(binbug_state::serial_r));
	maincpu.flag_handler().set(FUNC(binbug_state::serial_w));

	/* Keyboard */
	RS232_PORT(config, m_rs232, default_rs232_devices, "keyboard").set_option_device_input_defaults("keyboard", DEVICE_INPUT_DEFAULTS_NAME(keyboard));

	/* quickload */
	QUICKLOAD(config, "quickload", "pgm", attotime::from_seconds(1)).set_load_callback(FUNC(binbug_state::quickload_cb));

	S100_BUS(config, m_s100, 0);
	S100_SLOT(config, "s100:1", binbug_s100_devices, "dg640");
}


/* ROM definition */
ROM_START( binbug )
	ROM_REGION( 0x0400, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "binbug.rom", 0x0000, 0x0400, CRC(2cb1ac6e) SHA1(a969883fc767484d6b0fa103cfa4b4129b90441b) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT   MACHINE    INPUT   CLASS         INIT        COMPANY      FULLNAME      FLAGS
COMP( 1980, binbug, pipbug,   0,     binbug,    binbug, binbug_state, empty_init, "MicroByte", "BINBUG 3.6", MACHINE_SUPPORTS_SAVE )




bingobear.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle, David Viens
/*******************************************************************************

Bingo Bear (model 70501) / Monkgomery Monkey (model 70502), aka the Yakity Yaks,
they're talking hand puppets.

Initially released by Hasbro, although the 1986 Bingo Bear trademark was
registered by Tiger, and eventually in 1989 Tiger published it themselves.
Programming was apparently done by Micom Tech.

Hardware notes:
- PCB label: 7-130M-9R
- TMS1100 E7CL04N2L (die label: 1100G E7CL04)
- TMS5110ANL, 2*16KB VSM
- module slot

Sensor locations (just electronic switches):
- mouth: close/open it to make him talk
- body (left and right, connected to eachother): hug
- neck: scratch here when he asks for it

Monkgomery Monkey (trademarked by Hasbro) has the same MCU. The voice box label
is 7~150 instead of 7~130.

The external modules also came with a new outfit for the plushie to wear.
It looks like the Bingo Bear modules mostly work fine with Monkgomery Monkey,
though obviously his voice will change.

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

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/tms1000/tms1100.h"
#include "machine/tms6100.h"
#include "sound/tms5110.h"

#include "softlist_dev.h"
#include "speaker.h"


namespace {

class bingobear_state : public driver_device
{
public:
	bingobear_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_tms5100(*this, "tms5100"),
		m_tms6100(*this, "tms6100"),
		m_cart(*this, "cartslot"),
		m_inputs(*this, "IN.0")
	{ }

	void bingobear(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(power_on);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<tms1100_cpu_device> m_maincpu;
	required_device<tms5110_device> m_tms5100;
	required_device<tms6100_device> m_tms6100;
	optional_device<generic_slot_device> m_cart;
	required_ioport m_inputs;

	bool m_power_on = false;
	u16 m_inp_mux = 0;
	u32 m_r = 0;

	void power_off();
	u8 read_k();
	void write_o(u16 data);
	void write_r(u32 data);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
};

void bingobear_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_power_on));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_r));
}



/*******************************************************************************
    Power
*******************************************************************************/

void bingobear_state::machine_reset()
{
	m_power_on = true;
	m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
}

INPUT_CHANGED_MEMBER(bingobear_state::power_on)
{
	if (newval && !m_power_on)
		machine_reset();
}

void bingobear_state::power_off()
{
	m_power_on = false;
	m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
}



/*******************************************************************************
    Cartridge
*******************************************************************************/

DEVICE_IMAGE_LOAD_MEMBER(bingobear_state::cart_load)
{
	u32 const size = m_cart->common_get_size("rom");

	if (size > 0x4000)
		return std::make_pair(image_error::INVALIDLENGTH, "Invalid image file size (must be no more than 16K)");

	u8 *const base = memregion("tms6100")->base() + 0x8000;
	m_cart->common_load_rom(base, size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}



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

void bingobear_state::write_r(u32 data)
{
	// R1234: TMS5100 CTL8421
	u32 r = bitswap<5>(data,0,1,2,3,4) | (data & ~0x1f);
	m_tms5100->ctl_w(r & 0xf);

	// R0: TMS5100 PDC pin
	m_tms5100->pdc_w(data & 1);

	// R6-R8: input mux high
	m_inp_mux = (m_inp_mux & 1) | (data >> 5 & 0xe);

	// R9: power-off request, on falling edge
	if (~data & m_r & 0x200)
		power_off();

	m_r = r;
}

void bingobear_state::write_o(u16 data)
{
	// O0: input mux low
	m_inp_mux = (m_inp_mux & ~1) | (data & 1);
}

u8 bingobear_state::read_k()
{
	// K: sensors
	u8 data = m_inp_mux & m_inputs->read();

	// and TMS5100 CTL (also tied to R1234)
	return data | m_tms5100->ctl_r() | (m_r & 0xf);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( bingobear )
	PORT_START("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_3) PORT_NAME("Neck Sensor")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) // test pad
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_CODE(KEYCODE_1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bingobear_state::power_on), 0) PORT_NAME("Mouth Sensor")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_CODE(KEYCODE_2) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bingobear_state::power_on), 0) PORT_NAME("Body Sensor")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void bingobear_state::bingobear(machine_config &config)
{
	constexpr u32 MASTER_CLOCK = 640'000; // approximation

	// basic machine hardware
	TMS1100(config, m_maincpu, MASTER_CLOCK/2);
	m_maincpu->read_k().set(FUNC(bingobear_state::read_k));
	m_maincpu->write_o().set(FUNC(bingobear_state::write_o));
	m_maincpu->write_r().set(FUNC(bingobear_state::write_r));

	// sound hardware
	TMS6100(config, m_tms6100, MASTER_CLOCK/4);

	SPEAKER(config, "mono").front_center();
	TMS5110A(config, m_tms5100, MASTER_CLOCK);
	m_tms5100->m0().set(m_tms6100, FUNC(tms6100_device::m0_w));
	m_tms5100->m1().set(m_tms6100, FUNC(tms6100_device::m1_w));
	m_tms5100->addr().set(m_tms6100, FUNC(tms6100_device::add_w));
	m_tms5100->data().set(m_tms6100, FUNC(tms6100_device::data_line_r));
	m_tms5100->romclk().set(m_tms6100, FUNC(tms6100_device::clk_w));
	m_tms5100->add_route(ALL_OUTPUTS, "mono", 0.5);

	// cartridge
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "bingobear", "vsm,bin");
	m_cart->set_device_load(FUNC(bingobear_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("bingobear");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( bbear )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "e7cl04n2l", 0x0000, 0x0800, CRC(e2e4ff07) SHA1(8a944ade69b71667b18ce024c1105c522128ea75) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_bbear_output.pla", 0, 365, CRC(5a0b8e88) SHA1(e96cffb2ac3bf81f335d6f95125637446ff0f1b7) )

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // 8000-bfff = space reserved for cartridge
	ROM_LOAD( "cm62056", 0x0000, 0x4000, CRC(550b40d1) SHA1(ead5ed19a875309ec0be07adcb2986fead3405e0) ) // CM62091 label also seen, same ROM contents
	ROM_LOAD( "cm62057", 0x4000, 0x4000, CRC(383a8a9a) SHA1(3a4b8837febbec9fe9603970cf520f69304c5a68) ) // CM62092 "
ROM_END

ROM_START( monkmonk )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "e7cl04n2l", 0x0000, 0x0800, CRC(e2e4ff07) SHA1(8a944ade69b71667b18ce024c1105c522128ea75) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_bbear_output.pla", 0, 365, CRC(5a0b8e88) SHA1(e96cffb2ac3bf81f335d6f95125637446ff0f1b7) )

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // 8000-bfff = space reserved for cartridge
	ROM_LOAD( "cm62059", 0x0000, 0x4000, CRC(fa00a337) SHA1(b8625cee6605a9083de5da9e6ef3ea1c5d36e15c) )
	ROM_LOAD( "cm62060", 0x4000, 0x4000, CRC(494e06c2) SHA1(679517f1304617f7db1cd89ea2260e969170c6ac) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY, FULLNAME, FLAGS
SYST( 1986, bbear,    0,      0,      bingobear, bingobear, bingobear_state, empty_init, "Hasbro / Tiger Electronics", "Bingo Bear", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_CONTROLS | MACHINE_IMPERFECT_SOUND )
SYST( 1986, monkmonk, 0,      0,      bingobear, bingobear, bingobear_state, empty_init, "Hasbro / Tiger Electronics", "Monkgomery Monkey", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_CONTROLS | MACHINE_IMPERFECT_SOUND )



bitel.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Skeleton driver for Siemens Bitel and similar BtX terminals.

    "Bitel" apparently stands for "Bildschirmtext + Telefon."
    "Fe Ap" is short for "Fernsprechapparat."

    Both of these terminals have an integrated membrane keyboard and a
    telephone receiver mounted on top of the black-and-white monitor.

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

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "cpu/mcs48/mcs48.h"
//#include "video/saa5350.h"


namespace {

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

	void t3210(machine_config &config);
	void feap90(machine_config &config);

private:
	void prog_map(address_map &map) ATTR_COLD;
	void ext_map(address_map &map) ATTR_COLD;
	void sub_prog_map(address_map &map) ATTR_COLD;

	required_device<mcs51_cpu_device> m_maincpu;
};

void bitel_state::prog_map(address_map &map)
{
	map(0x0000, 0xffff).rom().region("program", 0);
}

void bitel_state::sub_prog_map(address_map &map)
{
	map(0x000, 0x7ff).rom().region("sub", 0);
}

static INPUT_PORTS_START(bitel)
INPUT_PORTS_END

void bitel_state::t3210(machine_config &config)
{
	I8031(config, m_maincpu, 12000000); // main board clocks unknown
	m_maincpu->set_addrmap(AS_PROGRAM, &bitel_state::prog_map);

	I8742(config, "upi", 6000000).set_disable();

	i8039_device &submcu(I8039(config, "submcu", 4.194304_MHz_XTAL));
	submcu.set_addrmap(AS_PROGRAM, &bitel_state::sub_prog_map);
}

void bitel_state::feap90(machine_config &config)
{
	I8031(config, m_maincpu, 12000000); // XTAL illegible
	m_maincpu->set_addrmap(AS_PROGRAM, &bitel_state::prog_map);

	I8042AH(config, "upi", 6000000).set_disable(); // XTAL illegible
}

ROM_START(t3210) // i8031, 8742, D80C39C // 4+2k ram onboard; 24kb in battery-backed expansion
	ROM_REGION(0x10000, "program", 0)
	ROM_LOAD("s22723_r115-c1-6_ct.d6", 0x0000, 0x8000, CRC(d09fea94) SHA1(52168060093dfe964c0316d9ff335cd59da01d48))
	ROM_LOAD("s22723_r115-c2-6_ct.d7", 0x8000, 0x8000, CRC(6e1eaacd) SHA1(cfda25dbbeddc7c75379c4b0dc97addb602d79ef))

	ROM_REGION(0x800, "upi", 0)
	ROM_LOAD("d8742_s22723_r118-c1.d16", 0x000, 0x800, CRC(f334a2a3) SHA1(c1cd4d775c2984252e6869a4c8f99d56646b89e9) BAD_DUMP) // BADADDR xx-xxxxxxxx

	ROM_REGION(0x800, "sub", 0) // lock card
	ROM_LOAD("s22723_r121-c2-2.d11", 0x000, 0x800, CRC(f0eda00e) SHA1(6b0d9f5e9d99644c3be16cbf0c0d3b1ea05aabee))

	ROM_REGION(0x100, "prom", 0)
	ROM_LOAD("prom_s22723_r120-c1.bin", 0x000, 0x100, CRC(4460cd50) SHA1(fe36d758d64493cb5f8217fe51bbbe8203424fbe))
ROM_END


ROM_START(feap90) // i8031, 80C42C121 (+SAA5351) // 4+2k ram onboard; 24kb in battery-backed expansion
	ROM_REGION(0x18000, "program", 0)
	ROM_LOAD("s22723-r116-c25-6 ex.d6", 0x00000, 0x10000, CRC(8362778d) SHA1(30fbe45eaedc1ed2e7b189f12e2ba7c23ab75de7))
	ROM_LOAD("s22723-r116-c26-6 ex.d2", 0x10000, 0x08000, CRC(121622ba) SHA1(c447da13f88772ec7d26e55ca8822e2c2dc3ecef))

	ROM_REGION(0x800, "upi", 0)
	ROM_LOAD("8838p8-80c42c121-a85.d16", 0x000, 0x800, NO_DUMP)
ROM_END

} // anonymous namespace


COMP(1986, t3210,  0, 0, t3210,  bitel, bitel_state, empty_init, "Siemens", "Bitel T3210", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP(1989, feap90, 0, 0, feap90, bitel, bitel_state, empty_init, "Siemens", "Multitel Fe Ap 90-1.1", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



bitgraph.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************

    BBN BitGraph -- monochrome, raster graphics (768x1024), serial terminal.

    Apparently had at least four hardware revisions, A-D, but which ROM
    revisions support which hardware is unclear.  A Versabus slot, and
    various hardware and software options are mentioned in the docs.  Best
    guesses follow.

    Onboard hardware (common to all revisions) is
    - 32K ROM
    - 128K RAM (includes frame buffer)
    - 3 serial ports, each driven by 6850 ACIA
    - unknown baud rate generator (possibly COM8016)
    - sync serial port, driven by 6854 but apparently never supported by ROM
    - 682x PIA
    - AY-3-891x PSG
    - ER2055 EAROM
    - DEC VT100 keyboard interface

    Rev A has additional 4th serial port for mouse (not supported by ROM 1.25).
    Rev A has 40 hz realtime clock, the rest use 1040 hz.
    Rev A-C use AY-3-8912 (with one external PIO port, to connect the EAROM).
    Rev D uses AY-3-8913 (no external ports; EAROM is wired to TBD).
    Rev B-D have onboard 8035 to talk to parallel printer and mouse.
    Rev B-D have more memory (at least up to 512K).

    ROM 1.25 doesn't support mouse, setup mode, pixel data upload and autowrap.

    Missing/incorrect emulation:
        8035.
        EAROM.
        1.25 only -- clksync() is dummied out -- causes watchdog resets.
        Selectable memory size.
        Video enable/reverse video switch.

    FIXME: The CPU and PSG XTAL values are probably fake. More likely, all
    clocks except the BRG clock are divisions of a ~41.42 MHz dot clock.
    (What is its actual value?)

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

#include "emu.h"

#include "bus/centronics/ctronics.h"
#include "bus/keytronic/keytronic.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/6821pia.h"
#include "machine/6850acia.h"
#include "machine/clock.h"
#include "machine/com8116.h"
#include "machine/er2055.h"
#include "machine/i8243.h"
#include "machine/mc6854.h"
#include "machine/ram.h"
#include "sound/ay8910.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


#define M68K_TAG "maincpu"
#define PPU_TAG "ppu"

#define ACIA0_TAG "acia0"
#define ACIA1_TAG "acia1"
#define ACIA2_TAG "acia2"
#define ACIA3_TAG "acia3"
#define RS232_H_TAG "rs232host"
#define RS232_D_TAG "rs232debug"
#define RS232_M_TAG "rs232mouse"
#define ADLC_TAG "adlc"
#define PIA_TAG "pia"
#define PSG_TAG "psg"
#define EAROM_TAG "earom"


#define LOG_PIA       (1U << 1)
#define LOG_DEBUG     (1U << 2)

//#define VERBOSE (LOG_DEBUG)
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"

#define LOGPIA(...) LOGMASKED(LOG_PIA, __VA_ARGS__)
#define LOGDBG(...) LOGMASKED(LOG_DEBUG, __VA_ARGS__)


namespace {

class bitgraph_state : public driver_device
{
public:
	bitgraph_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, M68K_TAG)
		, m_ram(*this, RAM_TAG)
		, m_acia0(*this, ACIA0_TAG)
		, m_acia1(*this, ACIA1_TAG)
		, m_acia2(*this, ACIA2_TAG)
		, m_acia3(*this, ACIA3_TAG)
		, m_adlc(*this, ADLC_TAG)
		, m_dbrga(*this, "com8116_a")
		, m_dbrgb(*this, "com8116_b")
		, m_pia(*this, PIA_TAG)
		, m_psg(*this, PSG_TAG)
		, m_earom(*this, EAROM_TAG)
		, m_centronics(*this, "centronics")
		, m_screen(*this, "screen")
		, m_videoram(*this, "videoram")
	{ }

	void bitgrpha(machine_config &config);
	void bitgrphb(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint8_t pia_r(offs_t offset);
	void pia_w(offs_t offset, uint8_t data);
	uint8_t pia_pa_r();
	uint8_t pia_pb_r();
	void pia_pa_w(uint8_t data);
	void pia_pb_w(uint8_t data);
	int pia_ca1_r();
	void pia_cb2_w(int state);

	void baud_write(uint16_t data);
	void com8116_a_fr_w(int state);
	void com8116_a_ft_w(int state);
	void com8116_b_fr_w(int state);
	void com8116_b_ft_w(int state);

	uint8_t adlc_r(offs_t offset);
	void adlc_w(offs_t offset, uint8_t data);

	void earom_write(uint8_t data);
	void misccr_write(uint8_t data);
	void system_clock_write(int state);

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	[[maybe_unused]] uint8_t ppu_read(offs_t offset);
	void ppu_write(offs_t offset, uint8_t data);
	template <unsigned Offset> void ppu_i8243_w(uint8_t data);

	void bg_motherboard(machine_config &config);
	[[maybe_unused]] void bg_ppu(machine_config &config);

	void bitgrapha_mem(address_map &map) ATTR_COLD;
	void bitgraphb_mem(address_map &map) ATTR_COLD;
	void ppu_io(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<acia6850_device> m_acia0;
	required_device<acia6850_device> m_acia1;
	required_device<acia6850_device> m_acia2;
	optional_device<acia6850_device> m_acia3;
	optional_device<mc6854_device> m_adlc;
	required_device<com8116_device> m_dbrga;
	required_device<com8116_device> m_dbrgb;
	required_device<pia6821_device> m_pia;
	required_device<ay8912_device> m_psg;
	required_device<er2055_device> m_earom;
	optional_device<centronics_device> m_centronics;
	required_device<screen_device> m_screen;
	required_shared_ptr<uint16_t> m_videoram;

	uint8_t m_misccr = 0;
	uint8_t m_pia_a = 0;
	uint8_t m_pia_b = 0;
	uint8_t m_ppu[4]{};
};

void bitgraph_state::bitgrapha_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x007fff).rom();
	map(0x010000, 0x010000).rw(m_acia0, FUNC(acia6850_device::data_r), FUNC(acia6850_device::data_w));   // HOST
	map(0x010002, 0x010002).rw(m_acia0, FUNC(acia6850_device::status_r), FUNC(acia6850_device::control_w));
	map(0x010009, 0x010009).rw(m_acia1, FUNC(acia6850_device::data_r), FUNC(acia6850_device::data_w));   // KEYBOARD
	map(0x01000b, 0x01000b).rw(m_acia1, FUNC(acia6850_device::status_r), FUNC(acia6850_device::control_w));
	map(0x010011, 0x010011).rw(m_acia2, FUNC(acia6850_device::data_r), FUNC(acia6850_device::data_w));   // DEBUGGER
	map(0x010013, 0x010013).rw(m_acia2, FUNC(acia6850_device::status_r), FUNC(acia6850_device::control_w));
	map(0x010019, 0x010019).rw(m_acia3, FUNC(acia6850_device::data_r), FUNC(acia6850_device::data_w));   // POINTER
	map(0x01001b, 0x01001b).rw(m_acia3, FUNC(acia6850_device::status_r), FUNC(acia6850_device::control_w));
	map(0x010020, 0x010027).rw(FUNC(bitgraph_state::adlc_r), FUNC(bitgraph_state::adlc_w)).umask16(0xff00);
	map(0x010028, 0x01002f).rw(FUNC(bitgraph_state::pia_r), FUNC(bitgraph_state::pia_w)).umask16(0xff00);    // EAROM, PSG
	map(0x010030, 0x010031).w(FUNC(bitgraph_state::baud_write));
	map(0x3e0000, 0x3fffff).ram().share("videoram");
}

void bitgraph_state::bitgraphb_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x007fff).rom();
	map(0x010000, 0x010000).rw(m_acia0, FUNC(acia6850_device::data_r), FUNC(acia6850_device::data_w));   // HOST
	map(0x010002, 0x010002).rw(m_acia0, FUNC(acia6850_device::status_r), FUNC(acia6850_device::control_w));
	map(0x010009, 0x010009).rw(m_acia1, FUNC(acia6850_device::data_r), FUNC(acia6850_device::data_w));   // KEYBOARD
	map(0x01000b, 0x01000b).rw(m_acia1, FUNC(acia6850_device::status_r), FUNC(acia6850_device::control_w));
	map(0x010011, 0x010011).rw(m_acia2, FUNC(acia6850_device::data_r), FUNC(acia6850_device::data_w));   // DEBUGGER
	map(0x010013, 0x010013).rw(m_acia2, FUNC(acia6850_device::status_r), FUNC(acia6850_device::control_w));
	map(0x01001b, 0x01001b).w(FUNC(bitgraph_state::misccr_write));
	map(0x010020, 0x010027).rw(FUNC(bitgraph_state::adlc_r), FUNC(bitgraph_state::adlc_w)).umask16(0xff00);
	map(0x010028, 0x01002f).rw(FUNC(bitgraph_state::pia_r), FUNC(bitgraph_state::pia_w)).umask16(0xff00);    // EAROM, PSG
	map(0x010030, 0x010031).w(FUNC(bitgraph_state::baud_write));
//  map(0x010030, 0x010037).r(FUNC(bitgraph_state::ppu_read)).umask16(0x00ff);
	map(0x010038, 0x01003f).w(FUNC(bitgraph_state::ppu_write)).umask16(0x00ff);
	map(0x380000, 0x3dffff).ram();
	map(0x3e0000, 0x3fffff).ram().share("videoram");
}

static INPUT_PORTS_START(bitgraph)
INPUT_PORTS_END


uint8_t bitgraph_state::pia_r(offs_t offset)
{
	LOGPIA("PIA R %d\n", offset);
	return m_pia->read(3 - offset);
}

void bitgraph_state::pia_w(offs_t offset, uint8_t data)
{
	LOGPIA("PIA W %d < %02X\n", offset, data);
	return m_pia->write(3 - offset, data);
}

int bitgraph_state::pia_ca1_r()
{
	return m_screen->frame_number() & 1;
}

void bitgraph_state::pia_cb2_w(int state)
{
	// no-op to shut up verbose log
}

uint8_t bitgraph_state::pia_pa_r()
{
	uint8_t data = BIT(m_pia_b, 3) ? m_earom->data() : m_pia_a;
	LOGDBG("PIA A == %02X (%s)\n", data, BIT(m_pia_b, 3) ? "earom" : "pia");
	return data;
}

void bitgraph_state::pia_pa_w(uint8_t data)
{
	LOGDBG("PIA A <- %02X\n", data);
	m_pia_a = data;
}

/*
    B0  O: BC1  to noisemaker.
    B1  O: BDIR to noisemaker.
    B2  O: Clock for EAROM.
    B3  O: CS1   for EAROM.
    B4  O: Enable HDLC Xmt interrupt.
    B5  O: Enable HDLC Rcv interrupt.
    B6  O: Clear Clock interrupt.  Must write a 0 [clear interrupt], then a 1.
    B7  I: EVEN field ??
*/
uint8_t bitgraph_state::pia_pb_r()
{
	LOGDBG("PIA B == %02X\n", m_pia_b);
	return m_pia_b;
}

void bitgraph_state::pia_pb_w(uint8_t data)
{
	LOGDBG("PIA B <- %02X\n", data);
	m_pia_b = data;

	switch (m_pia_b & 0x03)
	{
	case 2:
		m_psg->data_w(m_pia_a);
		break;
	case 3:
		m_psg->address_w(m_pia_a);
		break;
	}

	if (BIT(m_pia_b, 3))
	{
		LOGDBG("EAROM data <- %02X\n", m_pia_a);
		m_earom->set_data(m_pia_a);
	}
	// CS1, ~CS2, C1, C2
	m_earom->set_control(BIT(m_pia_b, 3), BIT(m_pia_b, 3), BIT(m_pia_a, 6), BIT(m_pia_a, 7));
	m_earom->set_clk(BIT(m_pia_b, 2));

	if (!BIT(m_pia_b, 6))
	{
		m_maincpu->set_input_line(M68K_IRQ_6, CLEAR_LINE);
	}
}

void bitgraph_state::earom_write(uint8_t data)
{
	LOGDBG("EAROM addr <- %02X (%02X)\n", data & 0x3f, data);
	m_earom->set_address(data & 0x3f);
}

// written once and never changed
void bitgraph_state::misccr_write(uint8_t data)
{
	LOG("MISCCR <- %02X (DTR %d MAP %d)\n", data, BIT(data, 3), (data & 3));
	m_misccr = data;
}

void bitgraph_state::system_clock_write(int state)
{
	if (!BIT(m_pia_b, 6))
	{
		m_maincpu->set_input_line(M68K_IRQ_6, CLEAR_LINE);
		return;
	}
	if (state)
	{
		m_maincpu->set_input_line(M68K_IRQ_6, ASSERT_LINE);
	}
	else
	{
		m_maincpu->set_input_line(M68K_IRQ_6, CLEAR_LINE);
	}
}

// rev A writes EA5E -- 9600 HOST, 2400 PNT, 300 KBD, 9600 DBG
// rev B writes EE5E -- 9600 HOST, 9600 PNT, 300 KBD, 9600 DBG
void bitgraph_state::baud_write(uint16_t data)
{
	LOG("Baud %04X\n", data);
	m_dbrgb->str_w(data & 15);      // 2 DBG
	m_dbrga->stt_w((data >> 4) & 15);   // 1 KBD
	m_dbrgb->stt_w((data >> 8) & 15);   // 3 PNT
	m_dbrga->str_w((data >> 12) & 15);  // 0 HOST
}

void bitgraph_state::com8116_a_fr_w(int state)
{
	m_acia0->write_txc(state);
	m_acia0->write_rxc(state);
}

void bitgraph_state::com8116_a_ft_w(int state)
{
	m_acia1->write_txc(state);
	m_acia1->write_rxc(state);
}

void bitgraph_state::com8116_b_fr_w(int state)
{
	m_acia2->write_txc(state);
	m_acia2->write_rxc(state);
}

void bitgraph_state::com8116_b_ft_w(int state)
{
	if (m_acia3.found())
	{
		m_acia3->write_txc(state);
		m_acia3->write_rxc(state);
	}
}

uint8_t bitgraph_state::adlc_r(offs_t offset)
{
	LOG("ADLC R %d\n", offset);
	return m_adlc.found() ? m_adlc->read(3 - offset) : 0xff;
}

void bitgraph_state::adlc_w(offs_t offset, uint8_t data)
{
	LOG("ADLC W %d < %02X\n", offset, data);
	if (m_adlc.found()) return m_adlc->write(3 - offset, data);
}

uint32_t bitgraph_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (int y = 0; y < 768; y++)
	{
		uint16_t *p = &bitmap.pix(y);

		for (int x = 0; x < 1024 / 16; x++)
		{
			uint16_t gfx = m_videoram[x | (y << 6)];
			for (int i = 15; i >= 0; i--)
			{
				*p++ = BIT(gfx, i);
			}
		}
	}
	return 0;
}

uint8_t bitgraph_state::ppu_read(offs_t offset)
{
	uint8_t data = m_ppu[offset];
	LOGDBG("PPU %d == %02X\n", offset, data);
	return data;
}

void bitgraph_state::ppu_write(offs_t offset, uint8_t data)
{
	LOGDBG("PPU %d <- %02X\n", offset, data);
	m_ppu[offset] = data;
}

void bitgraph_state::ppu_io(address_map &map)
{
//  map(0x00, 0x00).r(FUNC(bitgraph_state::ppu_irq));
}

/*
    p4  O: Centronics data 3..0
    p5  O: Centronics data 7..4
    p6  O: Centronics control
    p7  I: Centronics status
*/
template <unsigned Offset> void bitgraph_state::ppu_i8243_w(uint8_t data)
{
	LOG("PPU 8243 %d <- %02X\n", Offset, data);
	switch (Offset)
	{
	case 4:
		m_centronics->write_data0(BIT(data, 0));
		m_centronics->write_data1(BIT(data, 1));
		m_centronics->write_data2(BIT(data, 2));
		m_centronics->write_data3(BIT(data, 3));
		break;
	case 5:
		m_centronics->write_data4(BIT(data, 0));
		m_centronics->write_data5(BIT(data, 1));
		m_centronics->write_data6(BIT(data, 2));
		m_centronics->write_data7(BIT(data, 3));
		break;
	case 6:
		m_centronics->write_strobe(BIT(data, 0));
		// 1: Paper instruction
		m_centronics->write_init(BIT(data, 2));
		break;
	case 7:
		m_centronics->write_ack(BIT(data, 0));
		m_centronics->write_busy(BIT(data, 1));
		m_centronics->write_perror(BIT(data, 2));
		m_centronics->write_select(BIT(data, 3));
		break;
	}
}


void bitgraph_state::machine_start()
{
}

void bitgraph_state::machine_reset()
{
	m_maincpu->reset();
	m_misccr = 0;
	m_pia_a = 0;
	m_pia_b = 0;
	memset(m_ppu, 0, sizeof(m_ppu));
}


void bitgraph_state::bg_motherboard(machine_config &config)
{
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(40);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_size(1024, 768);
	m_screen->set_visarea_full();
	m_screen->set_screen_update(FUNC(bitgraph_state::screen_update));
	m_screen->set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	ACIA6850(config, m_acia0, 0);
	m_acia0->txd_handler().set(RS232_H_TAG, FUNC(rs232_port_device::write_txd));
	m_acia0->rts_handler().set(RS232_H_TAG, FUNC(rs232_port_device::write_rts));
	m_acia0->irq_handler().set_inputline(m_maincpu, M68K_IRQ_1);

	rs232_port_device &rs232h(RS232_PORT(config, RS232_H_TAG, default_rs232_devices, "null_modem"));
	rs232h.rxd_handler().set(m_acia0, FUNC(acia6850_device::write_rxd));
	rs232h.dcd_handler().set(m_acia0, FUNC(acia6850_device::write_dcd));
	rs232h.cts_handler().set(m_acia0, FUNC(acia6850_device::write_cts));

	ACIA6850(config, m_acia1, 0);
	m_acia1->txd_handler().set("keyboard", FUNC(keytronic_connector_device::ser_in_w));
	m_acia1->irq_handler().set_inputline(m_maincpu, M68K_IRQ_1);

	keytronic_connector_device &keyboard(KEYTRONIC_CONNECTOR(config, "keyboard", ascii_terminal_keyboards, "l2207"));
	keyboard.ser_out_callback().set(m_acia1, FUNC(acia6850_device::write_rxd));

	ACIA6850(config, m_acia2, 0);
	m_acia2->txd_handler().set(RS232_D_TAG, FUNC(rs232_port_device::write_txd));
	m_acia2->rts_handler().set(RS232_D_TAG, FUNC(rs232_port_device::write_rts));
	m_acia2->irq_handler().set_inputline(m_maincpu, M68K_IRQ_1);

	rs232_port_device &rs232d(RS232_PORT(config, RS232_D_TAG, default_rs232_devices, nullptr));
	rs232d.rxd_handler().set(m_acia2, FUNC(acia6850_device::write_rxd));
	rs232d.dcd_handler().set(m_acia2, FUNC(acia6850_device::write_dcd));
	rs232d.cts_handler().set(m_acia2, FUNC(acia6850_device::write_cts));

	COM8116(config, m_dbrga, 5.0688_MHz_XTAL);
	m_dbrga->fr_handler().set(FUNC(bitgraph_state::com8116_a_fr_w));
	m_dbrga->ft_handler().set(FUNC(bitgraph_state::com8116_a_ft_w));

	COM8116(config, m_dbrgb, 5.0688_MHz_XTAL);
	m_dbrgb->fr_handler().set(FUNC(bitgraph_state::com8116_b_fr_w));
	m_dbrgb->ft_handler().set(FUNC(bitgraph_state::com8116_b_ft_w));

	PIA6821(config, m_pia);
	m_pia->readca1_handler().set(FUNC(bitgraph_state::pia_ca1_r));
	m_pia->cb2_handler().set(FUNC(bitgraph_state::pia_cb2_w));
	m_pia->readpa_handler().set(FUNC(bitgraph_state::pia_pa_r));
	m_pia->writepa_handler().set(FUNC(bitgraph_state::pia_pa_w));
	m_pia->readpb_handler().set(FUNC(bitgraph_state::pia_pb_r));
	m_pia->writepb_handler().set(FUNC(bitgraph_state::pia_pb_w));

	ER2055(config, m_earom, 0);

	SPEAKER(config, "mono").front_center();
	AY8912(config, m_psg, XTAL(1'294'400));
	m_psg->port_a_write_callback().set(FUNC(bitgraph_state::earom_write));
	m_psg->add_route(ALL_OUTPUTS, "mono", 1.00);
}

void bitgraph_state::bg_ppu(machine_config &config)
{
	i8035_device &ppu(I8035(config, PPU_TAG, XTAL(6'900'000)));
	ppu.set_addrmap(AS_IO, &bitgraph_state::ppu_io);
//  ppu.t0_in_cb().set(FUNC(bitgraph_state::ppu_t0_r));
	ppu.prog_out_cb().set("i8243", FUNC(i8243_device::prog_w));

	i8243_device &i8243(I8243(config, "i8243"));
	i8243.p4_in_cb().set_constant(0);
	i8243.p5_in_cb().set_constant(0);
	i8243.p6_in_cb().set_constant(0);
	i8243.p7_in_cb().set_constant(0);
	i8243.p4_out_cb().set(FUNC(bitgraph_state::ppu_i8243_w<4>));
	i8243.p5_out_cb().set(FUNC(bitgraph_state::ppu_i8243_w<5>));
	i8243.p6_out_cb().set(FUNC(bitgraph_state::ppu_i8243_w<6>));
	i8243.p7_out_cb().set(FUNC(bitgraph_state::ppu_i8243_w<7>));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set("cent_status_in", FUNC(input_buffer_device::write_bit6));
	m_centronics->busy_handler().set("cent_status_in", FUNC(input_buffer_device::write_bit7));
	m_centronics->fault_handler().set("cent_status_in", FUNC(input_buffer_device::write_bit4));
	m_centronics->perror_handler().set("cent_status_in", FUNC(input_buffer_device::write_bit5));

	INPUT_BUFFER(config, "cent_status_in");

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);
}

void bitgraph_state::bitgrpha(machine_config &config)
{
	M68000(config, m_maincpu, XTAL(6'900'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &bitgraph_state::bitgrapha_mem);

	bg_motherboard(config);

	CLOCK(config, "system_clock", 40).signal_handler().set(FUNC(bitgraph_state::system_clock_write));

	ACIA6850(config, m_acia3, 0);
	m_acia3->txd_handler().set(RS232_M_TAG, FUNC(rs232_port_device::write_txd));
	m_acia3->rts_handler().set(RS232_M_TAG, FUNC(rs232_port_device::write_rts));
	m_acia3->irq_handler().set_inputline(M68K_TAG, M68K_IRQ_1);

	rs232_port_device &rs232m(RS232_PORT(config, RS232_M_TAG, default_rs232_devices, nullptr));
	rs232m.rxd_handler().set(m_acia3, FUNC(acia6850_device::write_rxd));
	rs232m.dcd_handler().set(m_acia3, FUNC(acia6850_device::write_dcd));
	rs232m.cts_handler().set(m_acia3, FUNC(acia6850_device::write_cts));

	RAM(config, RAM_TAG).set_default_size("128K");
}

void bitgraph_state::bitgrphb(machine_config &config)
{
	M68000(config, m_maincpu, XTAL(6'900'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &bitgraph_state::bitgraphb_mem);

	bg_motherboard(config);
//  bg_ppu(config);

	CLOCK(config, "system_clock", 1040).signal_handler().set(FUNC(bitgraph_state::system_clock_write));

	RAM(config, RAM_TAG).set_default_size("512K");
}

/* ROM definition */
ROM_START( bitgrpha )
	ROM_REGION16_BE( 0x8000, M68K_TAG, 0 )
	ROM_LOAD( "bg125.rom", 0x000000, 0x008000, CRC(b86c974e) SHA1(5367db80a856444c2a55de22b69a13f97a62f602))
	ROM_FILL( 0x38e4, 1, 0x4e ) // disable clksync()
	ROM_FILL( 0x38e5, 1, 0x75 )
ROM_END

ROM_START( bitgrphb )
	ROM_REGION16_BE( 0x8000, M68K_TAG, 0 )
	ROM_DEFAULT_BIOS("2.33a")

	ROM_SYSTEM_BIOS(0, "2.33a", "rev 2.33 Alpha' ROM")
	ROMX_LOAD( "bg2.32lo_u10.bin", 0x004001, 0x002000, CRC(6a702a96) SHA1(acdf1ba34038b4ccafb5b8069e70ae57a3b8a7e0), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD( "bg2.32hi_u12.bin", 0x004000, 0x002000, CRC(a282a2c8) SHA1(ea7e4d4e197201c8944acef54479d5c2b26d409f), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD( "bg2.32lo_u11.bin", 0x000001, 0x002000, CRC(46912afd) SHA1(c1f771adc1ef62b1fb1b904ed1d2a61009e24f55), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD( "bg2.32hi_u13.bin", 0x000000, 0x002000, CRC(731df44f) SHA1(8c238b5943b8864e539f92891a0ffa6ddd4fc779), ROM_BIOS(0) | ROM_SKIP(1))

	ROM_SYSTEM_BIOS(1, "3.0p", "rev 3.0P ROM")
	ROMX_LOAD( "bg5173_u10.bin", 0x004001, 0x002000, CRC(40014850) SHA1(ef0b7da58a5183391a3a03947882197f25694518), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD( "bg5175_u12.bin", 0x004000, 0x002000, CRC(c2c4cc6c) SHA1(dbbce7cb58b4cef1557a834cbb07b3ace298cb8b), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD( "bg5174_u11.bin", 0x000001, 0x002000, CRC(639768b9) SHA1(68f623bcf3bb75390ba2b17efc067cf25f915ec0), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD( "bg5176_u13.bin", 0x000000, 0x002000, CRC(984e7e8c) SHA1(dd13cbaff96a8b9936ae8cb07205c6abe8b27b6e), ROM_BIOS(1) | ROM_SKIP(1))

	ROM_SYSTEM_BIOS(2, "ramtest", "RAM test")
	ROMX_LOAD( "ramtest.rom", 0x000000, 0x004000, CRC(fabe3b34) SHA1(4d892a2ed2b7ea12d83843609981be9069611d43), ROM_BIOS(2))

	ROM_REGION( 0x800, PPU_TAG, 0 )
	ROM_LOAD( "bg_mouse_u9.bin", 0x0000, 0x0800, CRC(fd827ff5) SHA1(6d4a8e9b18c7610c5cfde40464826d144d387601))
ROM_END

} // anonymous namespace

/* Driver */
//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY  FULLNAME          FLAGS
COMP( 1981, bitgrpha, 0,      0,      bitgrpha, bitgraph, bitgraph_state, empty_init, "BBN",   "BitGraph rev A", ROT90 )
COMP( 1982, bitgrphb, 0,      0,      bitgrphb, bitgraph, bitgraph_state, empty_init, "BBN",   "BitGraph rev B", ROT270 | MACHINE_NOT_WORKING )



bk.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Sergey Svishchev
/***************************************************************************

BK driver by Miodrag Milanovic

2008-03-10 Preliminary driver.


- BK0010 - error message at start - thrown into monitor. H for help.
  Can use M to load a tape and S to run it.

- BK001001 - can load its own recordings, but cannot proceed past the header
  of software-list items.
  However...if you enter the Monitor with MON, enter M, enter the filename
  (case sensitive), start tape - it loads. Use S to run it.

- BK0010FD - can boot ANDOS.  Use S160000 in Monitor to run it.

- BK0011M - black screen. No emulation of this variant has been done.
- BK0011M - 128KB RAM, clock speed 4MHz by default, floppy drive facility.

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


#include "emu.h"
#include "bk.h"

#include "formats/rk_cas.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


/* Address maps */
void bk_state::bk0010_mem(address_map &map)
{
	map(0000000, 0177777).rw(FUNC(bk_state::trap_r), FUNC(bk_state::trap_w));

	map(0000000, 0037777).ram();
	map(0040000, 0077777).ram().share("videoram");
	map(0100000, 0177577).rom().region("maincpu",0);
	map(0177660, 0177663).r(m_kbd, FUNC(k1801vp014_device::read));
	map(0177660, 0177661).w(m_kbd, FUNC(k1801vp014_device::write));
	map(0177664, 0177665).rw(FUNC(bk_state::vid_scroll_r), FUNC(bk_state::vid_scroll_w));
	map(0177714, 0177715).noprw();
	map(0177716, 0177717).rw(FUNC(bk_state::sel1_r), FUNC(bk_state::sel1_w));
}

void bk_state::bk0010fd_mem(address_map &map)
{
	bk0010_mem(map);
	map(0120000, 0157777).ram();
}

/* Input ports */
static INPUT_PORTS_START( bk0010 )
INPUT_PORTS_END

static const z80_daisy_config daisy_chain[] =
{
	{ "keyboard" },
	{ "qbus" },
	{ nullptr }
};

void bk_state::bk0010(machine_config &config)
{
	/* basic machine hardware */
	K1801VM1(config, m_maincpu, 3000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &bk_state::bk0010_mem);
	m_maincpu->set_daisy_config(daisy_chain);
	m_maincpu->out_reset().set(FUNC(bk_state::reset_w));

	QBUS(config, m_qbus, 0);
	m_qbus->set_space(m_maincpu, AS_PROGRAM);
	m_qbus->birq4().set_inputline(m_maincpu, t11_device::VEC_LINE);
	QBUS_SLOT(config, "qbus" ":1", qbus_cards, nullptr);

	K1801VP014(config, m_kbd, 0);
	m_kbd->virq_wr_callback().set_inputline(m_maincpu, t11_device::VEC_LINE);
	m_kbd->keydown_wr_callback().set(
			[this] (int state)
			{
				if (state)
					m_sel1 &= ~SEL1_KEYDOWN;
				else
					m_sel1 |= SEL1_KEYDOWN;
			});
	m_kbd->halt_wr_callback().set_inputline(m_maincpu, t11_device::HLT_LINE);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(4000000.0/81920.0);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(512, 256);
	screen.set_visarea(0, 512-1, 0, 256-1);
	screen.set_screen_update(FUNC(bk_state::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	SPEAKER(config, "mono").front_center();
	DAC_1BIT(config, m_dac, 0).add_route(ALL_OUTPUTS, "mono", 0.25);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_DISABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("bk0010_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("bk0010");
}

void bk_state::bk0010fd(machine_config &config)
{
	bk0010(config);
	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &bk_state::bk0010fd_mem);

	subdevice<qbus_slot_device>("qbus:1")->set_default_option("by");
}


/* ROM definition */

ROM_START( bk0010 )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "monit10.rom", 0x0000, 0x2000, CRC(26c6e8a0) SHA1(4e83a94ae5155bbea14d7331a5a8db82457bd5ae) )  // to 8000, mask 017
	ROM_LOAD( "focal.rom",   0x2000, 0x2000, CRC(717149b7) SHA1(75df26f81ebd281bcb5c55ba81a7d97f31e388b2) )  // to A000, mask 018 (socketed)
	ROM_LOAD( "tests.rom",   0x6000, 0x1f80, CRC(91aecb4d) SHA1(6b14d552045194a3004bb6b795a2538110921519) )  // to E000, mask 019 (socketed)
ROM_END

ROM_START( bk001001 )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "monit10.rom",   0x0000, 0x2000, CRC(26c6e8a0) SHA1(4e83a94ae5155bbea14d7331a5a8db82457bd5ae) ) // to 8000, mask 017
	ROM_LOAD( "basic10-1.rom", 0x2000, 0x2000, CRC(5e3ff5da) SHA1(5ea4db1eaac87bd0ac96e52a608bc783709f5042) ) // to A000, mask 106
	ROM_LOAD( "basic10-2.rom", 0x4000, 0x2000, CRC(ea63863c) SHA1(acf068925e4052989b05dd5cf736a1dab5438011) ) // to C000, mask 107
	ROM_LOAD( "basic10-3.rom", 0x6000, 0x1f80, CRC(63f3df2e) SHA1(b5463f08e7c5f9f5aa31a2e7b6c1ed94fe029d65) ) // to E000, mask 108
ROM_END

ROM_START( bk0010fd )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "monit10.rom",  0x0000, 0x2000, CRC(26c6e8a0) SHA1(4e83a94ae5155bbea14d7331a5a8db82457bd5ae) ) // to 8000
	ROM_LOAD( "disk_327.rom", 0x6000, 0x1000, CRC(ed8a43ae) SHA1(28eefbb63047b26e4aec104aeeca74e2f9d0276c) ) // to E000
ROM_END

ROM_START( bk0011m )
	ROM_REGION( 0x1b000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "bas11m_0.rom", 0x10000, 0x4000, CRC(e71f9339) SHA1(9d76f3eefd64e032c763fa1ebf9cd3d9bd22317a) )
	ROM_LOAD( "bas11m_1.rom", 0x14000, 0x2000, CRC(4c68ff59) SHA1(34fa37599f2f9eb607390ef2458a3c22d87f09a9) )
	ROM_LOAD( "b11m_ext.rom", 0x16000, 0x2000, CRC(b2e4c33c) SHA1(f087af69044432a1ef2431a72ac06946e32f2dd3) )
	ROM_LOAD( "b11m_bos.rom", 0x18000, 0x2000, CRC(8ffd5efa) SHA1(7e9a30e38d7b78981999821640a68a201bb6df01) )
	ROM_LOAD( "disk_327.rom", 0x1A000, 0x1000, CRC(ed8a43ae) SHA1(28eefbb63047b26e4aec104aeeca74e2f9d0276c) )
ROM_END

/* Driver */

/*    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT   CLASS     INIT        COMPANY        FULLNAME       FLAGS */
COMP( 1985, bk0010,   0,      0,      bk0010,   bk0010, bk_state, empty_init, "Elektronika", "BK 0010",     MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1986, bk001001, bk0010, 0,      bk0010,   bk0010, bk_state, empty_init, "Elektronika", "BK 0010-01",  MACHINE_SUPPORTS_SAVE )
COMP( 1986, bk0010fd, bk0010, 0,      bk0010fd, bk0010, bk_state, empty_init, "Elektronika", "BK 0010 FDD", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1986, bk0011m,  bk0010, 0,      bk0010fd, bk0010, bk_state, empty_init, "Elektronika", "BK 0011M",    MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



blaucds32.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:

/*
   Skeleton driver for Blaupunkt CDS 32-ID terminal with colour screen.
   More hardware inforation and photos:  https://www.oldcomputers.es/terminal-blaupunkt-cds-32-id/

   Hardware setup:

 Printer PCB (silkscreened as "BLAUPUNKT  BTX-DRUCKER-INTERFACE  TYP 8668 305 585")
    ____________________________________________________________________________
   |                    ___________   ___________   ___________   ___________  |
 __|__                 |CONN_MAIN_|  |SN74ALS174N  |SN74LS164N|  |SN74ALS161BN |
|     | ___________   ___________    ___________    ___________                |
|DB15 ||SN74LS367AN  |SN74LS02N_|   |SN74LS244N|   |SN74LS374N|                |
| FEM |               __________________________    ________________           |
|     | ___________  | Motorola MC6821P        |   | NEC D4016C-3  |           |
|     ||SN74LS367AN  |                         |   |               |           |
|_____|              |_________________________|   |_______________|           |
   |        Xtal      __________________________    ___________   ___________  |
   |_     400 kHz    | Motorola MC6802P        |   |SN74LS161AN  |SN74AS04N_|  |
   - |               |                         |    ___________   ___________  |
   -_|<- Dips x 4    |_________________________|   |SN74LS161AN  |SN74AS02N_|  |
   |    ___________   ________________              ___________   ___________  |
   |   |_MC14069U_|  | EPROM         |             |SN74LS161AN  |SN74LS00N_|  |
   |                 |_______________|                                         |
   |___________________________________________________________________________|

Main PCB
   _____________________________________________________________________________
  |  _______________   _______________                            ___________  |
  | | NEC D4016C-3 |  | NEC D4016C-3 |                           |PC74HCT367P  |
  | |______________|  |______________|                             __________  |
  |  ___________       ___________                                CONN PRINTER |
  | |CD74HCT365E      |CD74HCT373E         Xtal 6 MHz                          |
  |  ___________       ___________   ____________________                      |
  | |CD74HCT365E      |CD74HCT373E  | M4613D/A          |                      |
  |  ____________      ___________  | 4782 DUG8629      |                      |
  | |CD74HCT373E|     |CD74HCT365E  |___________________|       _____________  |
  |  ___________       ___________   ___   ______________      | ASTEC       | |
  | |CD74HCT86E|      |_N82S153N_|  |  |  | NMC9816AN-25|      | AD1D12A10   | |
  |  _____________________   Xtal   |  |  |_____________|      |_____________| |
  | | MAB 8031AH 12P     | 11.0592  |_<-CD74HCT245E______                      |
  | |                    |   MHz          | D4016C-3    |                      |
  | |____________________|                |_____________|                      |
  |                                        ______________                      |
  |                                       | EPROM       |                      |
  |                                       |_____________|                      |
  |                                                                            |
  |____                                                                        |
      |                   -----------------------------------------            |
      |                   -------------- V24 INTERFACE ------------            |
      |_______________________      ___      ___      ___      ________________|
                              |____|   |____|   |____|   |____|
                             Keyboard   Tape      CVS     Modem

V24 Interface riser PCB
   ____________________________________________________________
  |  ___________   ___________    ___________    ___________  |
  | |_SN75189N_|  |_SN75188N_|   |PC74HCT00P|   |PC74HCT157P  |
  |_____________________                                      |
                        |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|

Keyboard PCB
  ___________________________________________________________________________________________
 |  _____________   ___________   ___________________   ____________                        |
 | |CD74HCT4514E|  |_74LS244N_|  | M5L8039P-11      |  | EPROM     |   ___________          |
 | |____________|    Xtal        |__________________|  |___________|  |PC74HCT373P          |
 |________________ 9.216 MHz _______________________________________________________________|

*/

#include "emu.h"

#include "cpu/m6800/m6800.h"
#include "cpu/mcs48/mcs48.h"
#include "cpu/mcs51/mcs51.h"

#include "machine/6821pia.h"

namespace {

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

	void blaucds32(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};

void blaucds32_state::machine_start()
{
}

void blaucds32_state::machine_reset()
{
}

static INPUT_PORTS_START( blaucds32 )
	// Printer interface config
	PORT_START("DSW1")
	PORT_DIPUNKNOWN_DIPLOC(0x01, 0x01, "SW1:1")
	PORT_DIPUNKNOWN_DIPLOC(0x02, 0x02, "SW1:2")
	PORT_DIPUNKNOWN_DIPLOC(0x04, 0x04, "SW1:3")
	PORT_DIPUNKNOWN_DIPLOC(0x08, 0x08, "SW1:4")
INPUT_PORTS_END

void blaucds32_state::blaucds32(machine_config &config)
{
	I8031(config, m_maincpu, 11.0592_MHz_XTAL);

	// Printer interface
	M6802(config, "printercpu", 400_kHz_XTAL); // Motorola MC6802P
	PIA6821(config, "pia"); // Motorola MC6821P

	// Cherry 601-1415 keyboard (TODO: Convert to device)
	I8039(config, "kbdc", 9.216_MHz_XTAL); // Mitsubishi M5L8039P-11
}

ROM_START( blaucds32 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "v4292_b0147-00.v4292", 0x00000, 0x10000, CRC(a25d8966) SHA1(72298dd4f3d8bb333c545fec2683bcae4bb18e26) )

	ROM_REGION( 0x2000, "printercpu", 0 )
	ROM_LOAD( "v5270_b0141-01.v270",  0x00000, 0x02000, CRC(95943b74) SHA1(4554b796b75c6790d07dc4bf5278df00f00b6804) )

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD( "426.bin",              0x00000, 0x00800, CRC(0465f6a7) SHA1(14d9d9ae58baad2f7ccbf1f35ef8599e32ec1ed1) )

	ROM_REGION( 0x0eb, "prom", 0 )
	ROM_LOAD( "n82s153n.v4245",       0x00000, 0x000eb, NO_DUMP ) // On main PCB
ROM_END

} // anonymous namespace


//    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY      FULLNAME     FLAGS
COMP( 19??, blaucds32, 0,      0,      blaucds32, blaucds32, blaucds32_state, empty_init, "Blaupunkt", "CDS 32-ID", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



blit.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************

    AT&T Blit -- monochrome, raster graphics (800x1024), serial terminal.

    https://code.9front.org/hg/plan9front/file/f4fa0b9d0397/sys/src/games/blit/mmap
        memory map

    https://pbs.twimg.com/media/C_uxVGgUwAAImk6.jpg:orig
        board photo

    Onboard hardware (common to all revisions) is
    - 24K ROM
    - 256K RAM (includes frame buffer)
    - 2 serial ports, each driven by 6850 ACIA

    To do:
    - keyboard
    - mouse
    - make 'mux' window system work -- "68ld: load protocol failed"
    - sound?
    - what do dip switches do?

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

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
#include "machine/6850acia.h"
#include "machine/clock.h"
#include "machine/ram.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


#define M68K_TAG "maincpu"
#define ACIA0_TAG "acia0"
#define ACIA1_TAG "acia1"
#define RS232_H_TAG "host"
#define RS232_K_TAG "keyboard"


#define LOG_PIA       (1U << 1)
#define LOG_DEBUG     (1U << 2)

//#define VERBOSE (LOG_DEBUG)
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"

#define LOGPIA(...) LOGMASKED(LOG_PIA, __VA_ARGS__)
#define LOGDBG(...) LOGMASKED(LOG_DEBUG, __VA_ARGS__)


namespace {

class blit_state : public driver_device
{
public:
	blit_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, M68K_TAG)
		, m_ram(*this, RAM_TAG)
		, m_acia0(*this, ACIA0_TAG)
		, m_acia1(*this, ACIA1_TAG)
		, m_screen(*this, "screen")
		, m_misccr(*this, "misccr")
		, m_p_ram(*this, "p_ram")
		, m_sysrom(*this, M68K_TAG)
	{ }

	void system_clock_write(int state);

	void start_write(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	void vblank_ack(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void blit(machine_config &config);
	void blit_mem(address_map &map) ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<acia6850_device> m_acia0;
	required_device<acia6850_device> m_acia1;
	required_device<screen_device> m_screen;
	required_shared_ptr<uint16_t> m_misccr;
	required_shared_ptr<uint16_t> m_p_ram;
	required_region_ptr<uint16_t> m_sysrom;

	memory_passthrough_handler m_rom_shadow_tap;
	int m_videostart = 0;
};

void blit_state::blit_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x03ffff).ram().share("p_ram");
	map(0x040000, 0x045fff).rom().region(M68K_TAG, 0);
	// octal 0000, 0002 - 16-bit
//  map(0x060000, 0x060003).r(FUNC(blit_state::mouse_xy));
	// octal 0011, 0013 - 8-bit - host
	map(0x060008, 0x060009).rw(m_acia0, FUNC(acia6850_device::status_r), FUNC(acia6850_device::control_w));
	map(0x06000a, 0x06000b).rw(m_acia0, FUNC(acia6850_device::data_r), FUNC(acia6850_device::data_w));
	// octal 0021 - 8-bit
//  map(0x060010, 0x060011).r(FUNC(blit_state::mouse_buttons)).mask(0x00ff);
	// octal 0025 - mirror
//  map(0x060014, 0x060015).r(FUNC(blit_state::mouse_buttons)).mask(0x00ff);
	// octal 0027 - ???
	// octal 0030 - 16-bit
	map(0x060018, 0x060019).w(FUNC(blit_state::start_write));
	// octal 0040 - 16-bit
	map(0x060020, 0x060029).ram().share("misccr");
	// octal 0050 - 16-bit
//  map(0x060028, 0x060029).w(FUNC(blit_state::init_write));
	// octal 0060, 0062 - 8-bit - keyboard
	map(0x060030, 0x060031).rw(m_acia1, FUNC(acia6850_device::status_r), FUNC(acia6850_device::control_w));
	map(0x060032, 0x060033).rw(m_acia1, FUNC(acia6850_device::data_r), FUNC(acia6850_device::data_w));
	// octal 0070 - 16-bit
	map(0x060038, 0x060039).w(FUNC(blit_state::vblank_ack));
}

static INPUT_PORTS_START(blit)
INPUT_PORTS_END

static DEVICE_INPUT_DEFAULTS_START( host_rs232_defaults )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_19200 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_19200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
	DEVICE_INPUT_DEFAULTS( "FLOW_CONTROL", 0x07, 0x01 )
DEVICE_INPUT_DEFAULTS_END

static DEVICE_INPUT_DEFAULTS_START( kbd_rs232_defaults )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_19200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END


void blit_state::start_write(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	LOG("START <- %04X (addr %06X)\n", data, data << 2);
	m_videostart = data << 1;
}

void blit_state::system_clock_write(int state)
{
	m_acia0->write_txc(state);
	m_acia0->write_rxc(state);
	//
	m_acia1->write_txc(state);
	m_acia1->write_rxc(state);
}

void blit_state::vblank_ack(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	m_maincpu->set_input_line(M68K_IRQ_1, CLEAR_LINE);
}


uint32_t blit_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	int fg, bg;

	if (BIT(*m_misccr, 0))
	{
		fg = 0; bg = 1;
	}
	else
	{
		fg = 1; bg = 0;
	}

	for (int y = 0; y < 1024; y++)
	{
		uint16_t *p = &bitmap.pix(y);

		for (int x = 0; x < 800 / 16; x += 1)
		{
			uint16_t gfx;

			gfx = m_p_ram[m_videostart + x + y * 50];

			for (int z = 15; z >= 0; z--)
			{
				*p++ = BIT(gfx, z) ? fg : bg;
			}
		}
	}
	return 0;
}


void blit_state::machine_start()
{
}

void blit_state::machine_reset()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x000000, 0x000007, m_sysrom);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0x040000, 0x045fff,
			"rom_shadow_r",
			[this] (offs_t offset, u16 &data, u16 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// reinstall ram over the rom shadow
					m_maincpu->space(AS_PROGRAM).install_ram(0x000000, 0x000007, m_p_ram);
				}
			},
			&m_rom_shadow_tap);

	*m_misccr = 0;
}

void blit_state::blit(machine_config &config)
{
	M68000(config, m_maincpu, XTAL(33'000'000) / 4); // maybe XTAL(32'760'000)
	m_maincpu->set_addrmap(AS_PROGRAM, &blit_state::blit_mem);

	SCREEN(config, m_screen, SCREEN_TYPE_LCD, rgb_t::white());
	m_screen->set_refresh_hz(60);
	m_screen->set_size(800, 1024);
	m_screen->set_visarea(0, 800-1, 0, 1024-1);
	m_screen->set_screen_update(FUNC(blit_state::screen_update));
	m_screen->screen_vblank().set_inputline(M68K_TAG, M68K_IRQ_1);
	m_screen->set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	CLOCK(config, "system_clock", 19200 * 16).signal_handler().set(FUNC(blit_state::system_clock_write));

	ACIA6850(config, m_acia0, 0);
	m_acia0->txd_handler().set(RS232_H_TAG, FUNC(rs232_port_device::write_txd));
	m_acia0->rts_handler().set(RS232_H_TAG, FUNC(rs232_port_device::write_rts));
	m_acia0->irq_handler().set_inputline(M68K_TAG, M68K_IRQ_5);

	rs232_port_device &rs232h(RS232_PORT(config, RS232_H_TAG, default_rs232_devices, "null_modem"));
	rs232h.rxd_handler().set(m_acia0, FUNC(acia6850_device::write_rxd));
	rs232h.dcd_handler().set(m_acia0, FUNC(acia6850_device::write_dcd));
	rs232h.cts_handler().set(m_acia0, FUNC(acia6850_device::write_cts));
	rs232h.set_option_device_input_defaults("null_modem", DEVICE_INPUT_DEFAULTS_NAME(host_rs232_defaults));

	ACIA6850(config, m_acia1, 0);
	m_acia1->txd_handler().set(RS232_K_TAG, FUNC(rs232_port_device::write_txd));
	m_acia1->rts_handler().set(RS232_K_TAG, FUNC(rs232_port_device::write_rts));
	m_acia1->irq_handler().set_inputline(M68K_TAG, M68K_IRQ_2);

	rs232_port_device &rs232k(RS232_PORT(config, RS232_K_TAG, default_rs232_devices, "keyboard"));
	rs232k.rxd_handler().set(m_acia1, FUNC(acia6850_device::write_rxd));
	rs232k.dcd_handler().set(m_acia1, FUNC(acia6850_device::write_dcd));
	rs232k.cts_handler().set(m_acia1, FUNC(acia6850_device::write_cts));
	rs232k.set_option_device_input_defaults("keyboard", DEVICE_INPUT_DEFAULTS_NAME(kbd_rs232_defaults));

	RAM(config, RAM_TAG).set_default_size("256K");
}

ROM_START( blit )
	ROM_REGION16_BE(0x6000, M68K_TAG, 0)
	ROM_DEFAULT_BIOS("blit")

	ROM_SYSTEM_BIOS(0, "blit", "blit ROM")
	ROMX_LOAD("rom0.bin", 0x0001, 0x1000, CRC(e1cea320) SHA1(ba71ccd794b3bb87af58e2ff3056fc6b6e03a167), ROM_BIOS(0)|ROM_SKIP(1))
	ROMX_LOAD("rom1.bin", 0x0000, 0x1000, CRC(b717fa64) SHA1(45fbc6029031cfa6f16c258c131916f56f7d8d38), ROM_BIOS(0)|ROM_SKIP(1))
	ROMX_LOAD("rom2.bin", 0x2001, 0x1000, CRC(8a80bf57) SHA1(0b21ef8657a03418f5333772312b163d9a8e4ba0), ROM_BIOS(0)|ROM_SKIP(1))
	ROMX_LOAD("rom3.bin", 0x2000, 0x1000, CRC(846b742c) SHA1(7d27970df6e0304f3b026fd350acffa5fd921a96), ROM_BIOS(0)|ROM_SKIP(1))
	ROMX_LOAD("rom4.bin", 0x4001, 0x08bf, CRC(2af93f8b) SHA1(1168cb341bfe2a5bc283281f6afe42837adb60f1), ROM_BIOS(0)|ROM_SKIP(1))
	ROMX_LOAD("rom5.bin", 0x4000, 0x08bf, CRC(d87f121f) SHA1(6e776ac29554b8a8bb332168c155bcc502c927b5), ROM_BIOS(0)|ROM_SKIP(1))
ROM_END

} // anonymous namespace

/* Driver */
//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS        INIT           COMPANY  FULLNAME     FLAGS
COMP( 1981, blit,     0,      0,      blit,     blit,     blit_state,  empty_init,    "AT&T",  "Blit",      MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



blitz.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Saitek Kasparov Blitz (model 291)

NOTE: Turn the power switch off before exiting MAME, otherwise NVRAM won't save
properly.

This is the last Saitek chess computer with Julio Kaplan's involvement (Heuristic
Software), although Craig Barnes continued working for Saitek. Julio Kaplan kept
programming his chess engine for a few more years, he stopped altogether after
the release of Kasparov's Gambit (Socrates II) for MS-DOS in 1993.

At first power-on, the chessboard gets calibrated, this is done automatically
in MAME. Not counting the chessboard, the whole user interface is with 2 vertical
dials at the right edge. There are no pushbuttons.

If chessboard calibration goes wrong somehow (eg. it wrongly tells you to place
a piece on a square, thinking the square is empty): clear the sensorboard, go to
options and select calibration, wait until the "board clr?" message goes away,
and then reset the sensorboard.

Hardware notes:
- PCB label: SA9-PE-020 REV 3/4
- Hitachi H8/325 MCU (either Mask ROM or PROM), 20MHz XTAL
- LCD with 10 7segs and custom segments
- piezo, 64 LEDs, metal sensors chessboard*, 2 dials

*: The chessboard technology is not with magnets (reed switches or hall effect
sensors). There are lots of copper wires beneath the chessboard, each square
resembles a metal detector. Chess pieces have aluminium washers at the bottom.

TODO:
- if/when MAME supports an exit callback, hook up power-off switch to that

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

#include "emu.h"

#include "cpu/h8/h8325.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "saitek_blitz.lh"


namespace {

class blitz_state : public driver_device
{
public:
	blitz_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_led_pwm(*this, "led_pwm"),
		m_lcd_pwm(*this, "lcd_pwm"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.0"),
		m_out_lcd(*this, "s%u.%u", 0U, 0U)
	{ }

	void blitz(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(power_off) { m_power = false; }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<h8325_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_led_pwm;
	required_device<pwm_display_device> m_lcd_pwm;
	required_device<dac_1bit_device> m_dac;
	required_ioport m_inputs;
	output_finder<4, 22> m_out_lcd;

	u8 m_inp_mux = 0;
	u8 m_sensor_strength = 0;
	bool m_sensor_state = false;
	u32 m_lcd_segs = 0;
	u8 m_lcd_com = 0;
	bool m_power = false;

	u8 m_port1 = 0xff;
	u8 m_port3 = 0xff;
	u8 m_port6 = 0xff;

	attotime m_board_init_time;

	bool board_active() { return machine().time() > m_board_init_time; }

	// I/O handlers
	void standby(int state);
	u8 power_r();

	void lcd_pwm_w(offs_t offset, u8 data);
	void update_lcd();
	template <int N> void lcd_segs_w(u8 data);

	void p1_w(u8 data);
	void p2_w(u8 data);
	u8 p3_r();
	void p3_w(u8 data);
	u8 p6_r();
	void p6_w(u8 data);
};



/*******************************************************************************
    Initialization
*******************************************************************************/

void blitz_state::machine_start()
{
	m_out_lcd.resolve();

	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_sensor_strength));
	save_item(NAME(m_sensor_state));
	save_item(NAME(m_lcd_segs));
	save_item(NAME(m_lcd_com));
	save_item(NAME(m_power));
	save_item(NAME(m_port1));
	save_item(NAME(m_port3));
	save_item(NAME(m_port6));
	save_item(NAME(m_board_init_time));
}

void blitz_state::machine_reset()
{
	m_power = true;

	// briefly deactivate board after a cold boot to give it time to calibrate
	if (m_port1 & 0x40)
		m_board_init_time = machine().time() + attotime::from_msec(1750);
}



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

// power

void blitz_state::standby(int state)
{
	// clear display
	if (state)
	{
		m_lcd_pwm->clear();
		m_led_pwm->clear();
	}
}

u8 blitz_state::power_r()
{
	// P46: power switch state
	return m_power ? 0xbf : 0xff;
}


// LCD

void blitz_state::lcd_pwm_w(offs_t offset, u8 data)
{
	m_out_lcd[offset & 0x3f][offset >> 6] = data;
}

void blitz_state::update_lcd()
{
	u32 lcd_segs = bitswap<22>(m_lcd_segs,26,27,28,29,30,31,0,1,2,3,4,5,19,7,8,9,10,11,12,13,24,25);

	for (int i = 0; i < 4; i++)
	{
		// LCD common is analog (voltage level)
		const u8 com = population_count_32(m_lcd_com >> (i * 2) & 3);
		const u32 data = (com == 0) ? lcd_segs : (com == 2) ? ~lcd_segs : 0;
		m_lcd_pwm->write_row(i, data);
	}
}

template <int N>
void blitz_state::lcd_segs_w(u8 data)
{
	// P4x, P5x, P63, P7x: LCD segments
	const u8 shift = 8 * N;
	m_lcd_segs = (m_lcd_segs & ~(0xff << shift)) | (data << shift);
	update_lcd();
}


// misc

void blitz_state::p1_w(u8 data)
{
	// P10-P15: board sensor strength (higher is more sensitive)
	m_sensor_strength = bitswap<6>(data,0,1,2,3,4,5);

	// P16: ext power
	m_port1 = data;
}

void blitz_state::p2_w(u8 data)
{
	// P20-P27: LED select
	m_led_pwm->write_my(~data);
}

u8 blitz_state::p3_r()
{
	// P37: board sensor state
	return m_sensor_state ? 0x7f : 0xff;
}

void blitz_state::p3_w(u8 data)
{
	// P36: reset board sensor on falling edge
	if (m_port3 & ~data & 0x40)
	{
		// P30-P32: board x
		// P33-P35: board y
		const u8 x = data & 7;
		const u8 y = data >> 3 & 7;
		const u8 s = m_sensor_strength;

		m_sensor_state = (s > 0x38) || (s > 0x30 && board_active() && m_board->read_sensor(x, y));
	}

	m_port3 = data;
}

u8 blitz_state::p6_r()
{
	// P66: multiplexed inputs
	return (m_inp_mux & m_inputs->read()) ? 0xbf : 0xff;
}

void blitz_state::p6_w(u8 data)
{
	// P62: speaker out
	m_dac->write(BIT(data, 2));

	// P60: 74164(1) CP
	// P61: 74164(1) DSB, outputs to input mux / LED data
	if (~m_port6 & data & 1)
		m_inp_mux = m_inp_mux << 1 | BIT(~data, 1);
	m_led_pwm->write_mx(m_inp_mux);

	// P64: 74164(2) CP
	// P65: 74164(2) DSB, output to LCD common
	if (~m_port6 & data & 0x10)
		m_lcd_com = m_lcd_com << 1 | BIT(data, 5);

	m_port6 = data;
	update_lcd();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

// mode dial, rotary switch with 12 stops (duplicate halves)
// ready, options, info, game, set up, level
static const ioport_value mode_dial[6] = { 5, 1, 2, 4, 6, 3 };

// shuttle dial, free running dial with 6 magnets and 2 reed switches
static const ioport_value shuttle_dial[4] = { 0, 1, 3, 2 };

static INPUT_PORTS_START( blitz )
	PORT_START("IN.0")
	PORT_BIT(0x07, 0x00, IPT_POSITIONAL_V) PORT_POSITIONS(6) PORT_WRAPS PORT_REMAP_TABLE(mode_dial) PORT_SENSITIVITY(6) PORT_KEYDELTA(1) PORT_CENTERDELTA(0) PORT_NAME("Mode Dial")
	PORT_BIT(0x18, 0x00, IPT_POSITIONAL_H) PORT_POSITIONS(4) PORT_WRAPS PORT_REMAP_TABLE(shuttle_dial) PORT_SENSITIVITY(12) PORT_KEYDELTA(1) PORT_CENTERDELTA(0) PORT_NAME("Shuttle Dial")
	PORT_BIT(0x60, IP_ACTIVE_HIGH, IPT_CUSTOM) // freq sel
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNKNOWN)

	PORT_START("POWER")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(blitz_state::power_off), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void blitz_state::blitz(machine_config &config)
{
	// basic machine hardware
	H8325(config, m_maincpu, 20_MHz_XTAL);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->nvram_set_default_value(~0);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(h8325_device::nvram_set_battery));
	m_maincpu->standby_cb().append(FUNC(blitz_state::standby));
	m_maincpu->write_port1().set(FUNC(blitz_state::p1_w));
	m_maincpu->write_port2().set(FUNC(blitz_state::p2_w));
	m_maincpu->read_port3().set(FUNC(blitz_state::p3_r));
	m_maincpu->write_port3().set(FUNC(blitz_state::p3_w));
	m_maincpu->read_port4().set(FUNC(blitz_state::power_r));
	m_maincpu->write_port4().set(FUNC(blitz_state::lcd_segs_w<0>));
	m_maincpu->write_port5().set(FUNC(blitz_state::lcd_segs_w<1>));
	m_maincpu->read_port6().set(FUNC(blitz_state::p6_r));
	m_maincpu->write_port6().set(FUNC(blitz_state::p6_w));
	m_maincpu->write_port6().append(FUNC(blitz_state::lcd_segs_w<2>));
	m_maincpu->write_port7().set(FUNC(blitz_state::lcd_segs_w<3>));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_lcd_pwm).set_size(4, 22);
	m_lcd_pwm->output_x().set(FUNC(blitz_state::lcd_pwm_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/5, 406/5);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_led_pwm).set_size(8, 8);
	m_led_pwm->set_bri_levels(0.25);

	config.set_default_layout(layout_saitek_blitz);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( kblitz )
	ROM_REGION16_BE( 0x8000, "maincpu", 0 ) // serial S004184xx
	ROM_LOAD("90_saitek_86058151sa9_3258a03p.u1", 0x0000, 0x8000, CRC(636ccde5) SHA1(c333ea3260892458a9d0c37db849d7d290674f86) )

	ROM_REGION( 94712, "screen", 0 )
	ROM_LOAD("kblitz.svg", 0, 94712, CRC(b3bda86b) SHA1(9b64d3d6f6a275acd05a0f78899a5aee48d6b86b) )
ROM_END

ROM_START( kblitza )
	ROM_REGION16_BE( 0x8000, "maincpu", 0 ) // serial S003078xx
	ROM_LOAD("h8_325_hd6473258p10_sa9_702g.u1", 0x0000, 0x8000, CRC(a5d17819) SHA1(e46c0b70aff31809ae02a5c5973935cf118f2324) )

	ROM_REGION( 94712, "screen", 0 )
	ROM_LOAD("kblitz.svg", 0, 94712, CRC(b3bda86b) SHA1(9b64d3d6f6a275acd05a0f78899a5aee48d6b86b) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY, FULLNAME, FLAGS
SYST( 1990, kblitz,  0,      0,      blitz,   blitz, blitz_state, empty_init, "Saitek / Heuristic Software", "Kasparov Blitz (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1990, kblitza, kblitz, 0,      blitz,   blitz, blitz_state, empty_init, "Saitek / Heuristic Software", "Kasparov Blitz (set 2)", MACHINE_SUPPORTS_SAVE )



bmjr.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
/**************************************************************************************************

Basic Master Jr. (MB-6885) (c) 1982? Hitachi

Notes:
- MON G command ends with space key, not enter (will otherwise just ignore the command).

TODO:
- Identify and improve Sound DAC details;
- Keyboard eats inputs if typed relatively fast (verify);
- Add kana mappings to keyboard;
- Timer control (needs SW that bothers with it);
- Downgrade for earlier variants (needs dump first), convert MP-1710 color adapter to expansion
  bus device (bmjr specific);
- Floppy adapter MP-1803, thru expansion bus;
- Border color for MP-1710;
- Printer, MP-1041/MP-1045;

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

#include "emu.h"
#include "cpu/m6800/m6800.h"
#include "imagedev/cassette.h"
#include "machine/timer.h"
#include "sound/dac.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class bmjr_state : public driver_device
{
public:
	bmjr_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cassette(*this, "cassette")
		, m_dac(*this, "dac")
		, m_work_ram(*this, "work_ram")
		, m_basic_view(*this, "basic_view")
		, m_printer_view(*this, "printer_view")
		, m_monitor_view(*this, "monitor_view")
		, m_p_chargen(*this, "chargen")
		, m_io_keyboard(*this, "KEY%d", 0U)
	{ }

	void bmjr(machine_config &config);
	DECLARE_INPUT_CHANGED_MEMBER(break_key_pressed);
	static constexpr feature_type unemulated_features() { return feature::PRINTER; }

protected:
	virtual void video_start() override ATTR_COLD;
	virtual void video_reset() override ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
private:
	void main_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<cassette_image_device> m_cassette;
	required_device<dac_5bit_binary_weighted_device> m_dac;
	required_shared_ptr<u8> m_work_ram;
	memory_view m_basic_view;
	memory_view m_printer_view;
	memory_view m_monitor_view;
	required_region_ptr<u8> m_p_chargen;
	required_ioport_array<16> m_io_keyboard;

	bool m_tape_switch = 0;
	u8 m_screen_reverse = 0U;
	u8 m_key_select = 0U;
	u8 m_nmi_enable = 0U;
	u16 m_casscnt = 0U;
	bool m_cassold = 0, m_cassbit = 0;
	u8 m_bank_mode = 0U;

	u8 bank_mode_r();
	void bank_mode_w(u8 data);
	u8 key_r();
	void key_w(u8 data);
	u8 timer_r();
	u8 tape_r();
	void tape_w(u8 data);
	u8 tape_stop_r();
	u8 tape_start_r();
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);

	std::unique_ptr<u8[]> m_color_ram;
	u8 m_color_mode = 0U;
	u8 m_tile_latch = 0U;
	u8 m_screen_mode = 0U;
	void mp1710_map(address_map &map) ATTR_COLD;
	void screen_mode_w(u8 data);
	void screen_reverse_w(u8 data);
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
};

void bmjr_state::video_start()
{
	m_color_ram = std::make_unique<u8[]>(0x300);
	save_item(NAME(m_screen_mode));
	save_item(NAME(m_screen_reverse));
	save_item(NAME(m_color_mode));
	save_item(NAME(m_tile_latch));
	save_pointer(NAME(m_color_ram), 0x300);
}

void bmjr_state::video_reset()
{
	m_tile_latch = 0x07;
	m_screen_reverse = 0;
	m_screen_mode = 0;
}

u32 bmjr_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	const u8 fg_shift = m_screen_reverse ? 4 : 0;
	const u8 bg_shift = m_screen_reverse ? 0 : 4;
	const u16 screen_bank_offset = 0x900 + ((m_screen_mode & 0xf) << 9);

	// TODO: convert to scanline based renderer
	for(int y = cliprect.min_y; y <= cliprect.max_y; y++ )
	{
		for (int x = cliprect.min_x; x <= cliprect.max_x; x+= 8)
		{
			const u16 tile_offset = (x >> 3) + ((y >> 3) * 32);
			const u16 tile = m_work_ram[0x100 + tile_offset] << 3;
			const u8 gfx_data = BIT(m_screen_mode, 7) ? m_work_ram[screen_bank_offset + (x >> 3) + (y * 32)] : m_p_chargen[tile | (y & 7)];
			const u8 attr = BIT(m_color_mode, 0) ? m_color_ram[tile_offset] : 0x07;
			const u8 fg_color = (attr >> fg_shift) & 7;
			const u8 bg_color = (attr >> bg_shift) & 7;

			for (int xi = 0; xi < 8; xi++)
			{
				const u8 pen = BIT(gfx_data, 7 - xi) ? fg_color : bg_color;
				bitmap.pix(y, x + xi) = pen;
			}
		}
	}

	return 0;
}

u8 bmjr_state::key_r()
{
	return (m_io_keyboard[m_key_select]->read() & 0xf) | ioport("KEYMOD")->read();
}

void bmjr_state::key_w(u8 data)
{
	m_key_select = data & 0xf;

	m_nmi_enable = BIT(data, 7);
	if (!m_nmi_enable)
		m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
}

/*
 * x--- ---- checked in irq routine
 * 1--- ---- routes to 1 second timer at $000c (TIME 3)
 * 0--- ---- routes to $000a-$000b (TIME 1/TIME 2), likely enabled thru $efd0 bit 4 high
 */
u8 bmjr_state::timer_r()
{
	return 0xff;
}

TIMER_DEVICE_CALLBACK_MEMBER( bmjr_state::kansas_r )
{
	/* cassette - turn pulses into a bit */
	bool cass_ws = (m_cassette->input() > +0.04) ? 1 : 0;
	m_casscnt++;

	if (cass_ws != m_cassold)
	{
		m_cassold = cass_ws;
		m_cassbit = (m_casscnt < 12) ? 1 : 0;
		m_casscnt = 0;
	}
	else if (m_casscnt > 32)
	{
		m_casscnt = 32;
		m_cassbit = 0;
	}
}

u8 bmjr_state::tape_r()
{
	//m_cassette->change_state(CASSETTE_PLAY,CASSETTE_MASK_UISTATE);

	return m_cassbit ? 0xff : 0x00;
}

void bmjr_state::tape_w(u8 data)
{
	if(!m_tape_switch)
	{
		m_dac->write((data >> 1) & 0x1f);
	}
	else
	{
		//m_cassette->change_state(CASSETTE_RECORD,CASSETTE_MASK_UISTATE);
		m_cassette->output(BIT(data, 0) ? -1.0 : +1.0);
	}
}

u8 bmjr_state::tape_stop_r()
{
	m_tape_switch = 0;
	//m_cassette->change_state(CASSETTE_STOPPED,CASSETTE_MASK_UISTATE);
	m_cassette->change_state(CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR);
	return 0x01;
}

u8 bmjr_state::tape_start_r()
{
	m_tape_switch = 1;
	m_cassette->change_state(CASSETTE_MOTOR_ENABLED,CASSETTE_MASK_MOTOR);
	return 0x01;
}

u8 bmjr_state::bank_mode_r()
{
	return m_bank_mode;
}

/*
 * ---x ---- timer enable?
 * ---- -x-- maps $f000-$ffff to work RAM
 * ---- --x- maps $e000-$edff to work RAM
 * ---- ---x maps $b000-$dfff to work RAM
 */
void bmjr_state::bank_mode_w(u8 data)
{
	logerror("bank_mode_w %02x\n", data);
	m_bank_mode = data;
	if (BIT(data, 0))
		m_basic_view.disable();
	else
		m_basic_view.select(0);

	if (BIT(data, 1))
		m_printer_view.disable();
	else
		m_printer_view.select(0);

	if (BIT(data, 2))
		m_monitor_view.disable();
	else
		m_monitor_view.select(0);
}

void bmjr_state::screen_reverse_w(u8 data)
{
	m_screen_reverse = BIT(data, 7);
}

/*
 * x--- ---- enable bitmap mode
 * -?-- ---- <unknown, looks set in tandem with bit 7>
 * ---- xxxx bank base for bitmap mode
 */
void bmjr_state::screen_mode_w(u8 data)
{
	m_screen_mode = data;
}

void bmjr_state::mp1710_map(address_map &map)
{
	map(0x00, 0x00).lrw8(
		NAME([this] (offs_t offset) { return m_tile_latch; }),
		NAME([this] (offs_t offset, u8 data) { m_tile_latch = data; })
	);
	//map(0x01, 0x01) border color?
	map(0x02, 0x02).lrw8(
		NAME([this] (offs_t offset) { return m_color_mode; }),
		NAME([this] (offs_t offset, u8 data) { m_color_mode = data; })
	);
}

void bmjr_state::main_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).ram().share("work_ram");
	// overlay for MP-1710 tile latches
	map(0x0100, 0x03ff).lrw8(
		NAME([this] (offs_t offset) { return m_work_ram[offset + 0x100]; }),
		NAME([this] (offs_t offset, u8 data) {
			m_work_ram[offset + 0x100] = data;
			m_color_ram[offset] = m_tile_latch;
		})
	);
	map(0xb000, 0xdfff).view(m_basic_view);
	m_basic_view[0](0xb000, 0xdfff).rom().region("basic", 0);
	map(0xe000, 0xefff).view(m_printer_view);
	m_printer_view[0](0xe000, 0xe7ff).rom().region("printer", 0);
	// 0xe800-0xedff expansion I/O
//  map(0xe800, 0xe803) 6820 or 6821 PIA
	m_printer_view[0](0xe890, 0xe89f).m(*this, FUNC(bmjr_state::mp1710_map));
	// 0xee00-0xefff system I/O (ignored by printer view enabled)
	map(0xee00, 0xee00).r(FUNC(bmjr_state::tape_stop_r)); //R stop tape
	map(0xee20, 0xee20).r(FUNC(bmjr_state::tape_start_r)); //R start tape
	map(0xee40, 0xee40).w(FUNC(bmjr_state::screen_reverse_w)); //W Picture reverse
	map(0xee80, 0xee80).rw(FUNC(bmjr_state::tape_r), FUNC(bmjr_state::tape_w));//RW tape input / output
	map(0xeec0, 0xeec0).rw(FUNC(bmjr_state::key_r), FUNC(bmjr_state::key_w));//RW keyboard
	map(0xef00, 0xef00).r(FUNC(bmjr_state::timer_r));
	map(0xef80, 0xef80).portr("BREAK");
	map(0xefd0, 0xefd0).rw(FUNC(bmjr_state::bank_mode_r), FUNC(bmjr_state::bank_mode_w));
	map(0xefe0, 0xefe0).w(FUNC(bmjr_state::screen_mode_w));
	map(0xf000, 0xffff).view(m_monitor_view);
	m_monitor_view[0](0xf000, 0xffff).rom().region("monitor", 0);
}

INPUT_CHANGED_MEMBER(bmjr_state::break_key_pressed)
{
	if (newval && m_nmi_enable)
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
}

static INPUT_PORTS_START( bmjr )
	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("1 !") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("2 \"") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("3 #") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("4 $") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')

	PORT_START("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("5 %") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')

	PORT_START("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("6 &") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')

	PORT_START("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("7 \'") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("KEY7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("8 (") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')

	PORT_START("KEY8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("9 )") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')

	PORT_START("KEY9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("; +") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')

	PORT_START("KEY10")
	// NOTE: works on kana / shift only
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("_") PORT_CHAR('_')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(": *") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"@ \u2191") PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('@')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("- =") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')

	PORT_START("KEY11")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("]") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"[ \u2193") PORT_CODE(KEYCODE_OPENBRACE) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('[')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"^ \u2192") PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('^')

	PORT_START("KEY12")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"¥ \u2190") PORT_CODE(KEYCODE_4_PAD)

	PORT_START("KEY13")
	PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY14")
	PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY15")
	PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEYMOD") /* Note: you should press Normal to return from a Kana state and vice-versa */
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(DEF_STR( Normal )) PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Kana Shift") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Kana") PORT_CODE(KEYCODE_RCONTROL)

	PORT_START("BREAK")
	PORT_BIT(0x7f, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // TODO: read by timer irq service
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Break") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bmjr_state::break_key_pressed), 0)
INPUT_PORTS_END

static const gfx_layout bmjr_charlayout =
{
	8, 8,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP8(0, 1) },
	{ STEP8(0, 8) },
	8*8
};

static GFXDECODE_START( gfx_bmjr )
	GFXDECODE_ENTRY( "chargen", 0x0000, bmjr_charlayout, 0, 4 )
GFXDECODE_END

void bmjr_state::machine_start()
{
	save_item(NAME(m_tape_switch));
	save_item(NAME(m_key_select));
	save_item(NAME(m_casscnt));
	save_item(NAME(m_cassold));
	save_item(NAME(m_cassbit));
	save_item(NAME(m_nmi_enable));
	save_item(NAME(m_bank_mode));
}

void bmjr_state::machine_reset()
{
	//m_beep->set_state(0);
	m_tape_switch = 0;
	m_key_select = 0;
	m_cassette->change_state(CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR);
	m_bank_mode = 0;
	m_basic_view.select(0);
	m_printer_view.select(0);
	m_monitor_view.select(0);
	m_maincpu->reset();
}

void bmjr_state::bmjr(machine_config &config)
{
	// 750khz gets the cassette sound close to a normal kansas city 300 baud
	M6800(config, m_maincpu, 754'560); // TODO: HD46800, derive from actual clock / divider
	m_maincpu->set_addrmap(AS_PROGRAM, &bmjr_state::main_map);
	// NOTE: checked by using TIME commands, which implies a separate thread than the actual timer control
	m_maincpu->set_vblank_int("screen", FUNC(bmjr_state::irq0_line_hold));

	// TRQ237/TRQ359
	CASSETTE(config, m_cassette);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("bmjr_cass");

	TIMER(config, "kansas_r").configure_periodic(FUNC(bmjr_state::kansas_r), attotime::from_hz(40000));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(320, 262);
	screen.set_visarea(0, 256 - 1, 0, 192 - 1);
	screen.set_screen_update(FUNC(bmjr_state::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::BRG_3BIT);
	GFXDECODE(config, "gfxdecode", "palette", gfx_bmjr);

	SPEAKER(config, "mono").front_center();
	// TODO: unknown DAC type, likely connected to discrete circuitry.
	DAC_5BIT_BINARY_WEIGHTED(config, m_dac).add_route(ALL_OUTPUTS, "mono", 0.25);

	SOFTWARE_LIST(config, "cass_list").set_original("bmjr_cass");
}

/* ROM definition */
ROM_START( bmjr )
	ROM_REGION( 0x3000, "basic", ROMREGION_ERASEFF )
	ROM_LOAD( "bas.rom", 0x0000, 0x3000, BAD_DUMP CRC(2318e04e) SHA1(cdb3535663090f5bcaba20b1dbf1f34724ef6a5f)) // needs splitting in three halves

	ROM_REGION( 0x1000, "monitor", ROMREGION_ERASEFF )
	ROM_LOAD( "mon.rom", 0x0000, 0x1000, CRC(776cfa3a) SHA1(be747bc40fdca66b040e0f792b05fcd43a1565ce))

	ROM_REGION( 0x800, "printer", ROMREGION_ERASEFF )
	ROM_LOAD( "prt.rom", 0x000, 0x800, CRC(b9aea867) SHA1(b8dd5348790d76961b6bdef41cfea371fdbcd93d))

	ROM_REGION( 0x800, "chargen", 0 )
	ROM_LOAD( "font.rom", 0x0000, 0x0800, BAD_DUMP CRC(258c6fd7) SHA1(d7c7dd57d6fc3b3d44f14c32182717a48e24587f)) // taken from a JP emulator
ROM_END

} // anonymous namespace


// 1979 Basic Master MB-6880 (retroactively Level 1)
// 1979 Basic Master Level 2 MB-6880L2
// 1980 Basic Master Level 2 II MB-6881
COMP( 1981, bmjr, 0,      0,      bmjr,    bmjr,  bmjr_state, empty_init, "Hitachi", "Basic Master Jr. (MB-6885)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )



bml3.cpp
<---------------------------------------------------------------------->
// license: GPL-2.0+
// copyright-holders: Angelo Salese, Jonathan Edwards, Christopher Edwards, Robbbert
/**************************************************************************************************

Basic Master Level 3 (MB-689x) "Peach" (c) 1980 Hitachi
ベーシックマスターレベル3

References:
- http://s-sasaji.ddo.jp/bml3mk5/tech.htm
- https://www.leadedsolder.com/2023/05/09/hitachi-basic-master-level-iii-mark-ii-cleaning-pickup.html
- https://github.com/bml3mk5/EmuB-6892/blob/master/src/docs/spec.txt

TODO:
- keyboard break NMI (as per bmjr);
- Move keyboard timer logic as 6845 hsync callback, fix logic (would key repeat too fast);
- implement sound as a bus slot device;
- implement RAM expansion as bus slots (RAM3 at 0x8000-0xbfff, RAM4 at 0xc000-0xefff);
- bml3mk5: BANK REG $ffe8 (applies EMS for the RAM expansion?);
- Cassettes requires manual press of play button when issuing a LOAD (verify);

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

#include "emu.h"
#include "bml3.h"

#include "bus/bml3/kanji.h"
#include "bus/bml3/mp1802.h"
#include "bus/bml3/mp1805.h"
#include "bus/bml3/rtc.h"
#include "cpu/m6809/m6809.h"
#include "machine/6821pia.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


MC6845_UPDATE_ROW( bml3_state::crtc_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	// The MB-6890 has a 5-bit colour RAM region.  The meaning of the bits are:
	// 0: blue
	// 1: red
	// 2: green
	// 3: reverse/inverse video
	// 4: graphic (not character)

	u8 const interlace = (m_crtc_vreg[8] & 3) ? 1 : 0;
	bool const lowres = BIT(m_hres_reg, 6);
	u8 const bgcolor = m_hres_reg & 7;

	if (interlace)
	{
		ra >>= 1;
		if (y > 0x191) return;
	}

	for(u8 x=0; x<x_count; x++)
	{
		u16 mem;
		if (lowres)
			mem = (ma + x - 0x400) & 0x3fff;
		else
			mem = (ma + x + ra * x_count/40 * 0x400 -0x400) & 0x3fff;

		u8 const attr = m_aram[mem];
		u8 const rawbits = m_vram[mem];
		// HACK: tracer bullet, should be composed in an entirely different way
		// This will never be hit by regular l3 being a specific feature of mk5,
		// still expect further headaches for S1 support ...
		if (get_ig_mode(attr))
		{
			for(u8 xi = 0; xi < 8; xi++)
			{
				u8 pen = get_ig_dots(rawbits, ra, 7 - xi);
				bitmap.pix(y, x*8+xi) = palette[pen];
			}

			continue;
		}

		u8 const color = attr & 7;
		bool const reverse = BIT(attr, 3) ^ (x == cursor_x);
		bool const graphic = BIT(attr, 4);


		u8 dots[2] = { 0, 0 };
		if (graphic)
		{
			if (lowres)
			{
				// low-res graphics, each tile has 8 bits arranged as follows:
				// 4 0
				// 5 1
				// 6 2
				// 7 3
				dots[0] = dots[1] = (rawbits >> ra/2 & 0x11) * 0xf;
			}
			else
			{
				dots[0] = dots[1] = rawbits;
			}
		}
		else
		{
			// character mode
			int const tile = rawbits & 0x7f;
			int const tile_bank = BIT(rawbits, 7);
			if (interlace)
			{
				dots[0] = m_p_chargen[(tile_bank<<11)|(tile<<4)|(ra<<1)];
				dots[1] = m_p_chargen[(tile_bank<<11)|(tile<<4)|(ra<<1)|tile_bank];
			}
			else
			{
				dots[0] = dots[1] = m_p_chargen[(tile<<4)|(ra<<1)|tile_bank];
			}
		}

		for(u8 hf=0;hf<=interlace;hf++)
		{
			for(u8 xi=0;xi<8;xi++)
			{
				u8 pen;
				if(reverse)
					pen = (dots[hf] >> (7-xi) & 1) ? bgcolor : color;
				else
					pen = (dots[hf] >> (7-xi) & 1) ? color : bgcolor;

				bitmap.pix(y, x*8+xi) = palette[pen];
				// when the mc6845 device gains full interlace&video support, replace the line above with the line below
				// bitmap.pix(y*(interlace+1)+hf, x*8+xi) = palette[pen];
			}
		}
	}
}


u8 bml3_state::mc6845_r(offs_t offset)
{
	if (offset)
		return m_crtc->register_r();
	else
		return m_crtc->status_r();
}

void bml3_state::mc6845_w(offs_t offset, u8 data)
{
	if(offset == 0)
	{
		m_crtc_index = data;
		m_crtc->address_w(data);
	}
	else
	{
		m_crtc_vreg[m_crtc_index] = data;
		m_crtc->register_w(data);
	}
}

u8 bml3_state::kb_sel_r()
{
	m_maincpu->set_input_line(M6809_IRQ_LINE, CLEAR_LINE);
	u8 ret = m_keyb_scancode;
	if (!machine().side_effects_disabled())
		m_keyb_scancode &= 0x7f;
	return ret;
}

// KB SEL - Keyboard mode register, interrupt control, keyboard LEDs
void bml3_state::kb_sel_w(u8 data)
{
	m_keyb_katakana_led_on = BIT(data, 0);
	m_keyb_hiragana_led_on = BIT(data, 1);
	m_keyb_capslock_led_on = BIT(data, 2);
	m_keyb_counter_operation_disabled = BIT(data, 3);
	m_keyb_interrupt_enabled = BIT(data, 6);
	m_keyb_nmi_disabled = !BIT(data, 7);
}

void bml3_state::crtc_change_clock()
{
	const u8 width80 = BIT(m_hres_reg, 7);
	const u8 interlace = BIT(m_vres_reg, 3);
	// CRTC and MPU are synchronous by default
	int clock = (width80 ? C80_CLOCK : C40_CLOCK).value() << interlace;
	m_crtc->set_unscaled_clock(clock);
}

/*
 * MODE_SEL - Graphics mode select
 * cfr. see service manual p.43
 * x--- ---- "W" bit: 0 = 40 columns, 1 = 80 columns
 * -x-- ---- "HR" bit: 0 = high resolution, 1 = normal
 * --x- ---- "C" bit - ACIA mode: 0 = cassette, 1 = RS-232C
 * ---- -RGB Background colour
 */
void bml3_state::mode_sel_w(u8 data)
{
	m_hres_reg = data;

	crtc_change_clock();
}

// INTERLACE_SEL - Interlaced video mode
void bml3_state::interlace_sel_w(u8 data)
{
	m_vres_reg = data;

	crtc_change_clock();
}

u8 bml3_state::vram_r(offs_t offset)
{
	// Bit 7 masks reading back to the latch
	if (!BIT(m_attr_latch, 7) && !machine().side_effects_disabled())
		m_attr_latch = m_aram[offset];

	return m_vram[offset];
}

void bml3_state::vram_w(offs_t offset, u8 data)
{
	m_vram[offset] = data;
	// color ram is 5-bit
	// NOTE: will break hiwriter with this, "write enable" only on reads!?
	//if (!BIT(m_attr_latch, 7))
	m_aram[offset] = m_attr_latch & get_attr_mask();
}

u8 bml3_state::psg_latch_r()
{
	return 0x7f;
}

void bml3_state::psg_latch_w(u8 data)
{
	m_psg_latch = data;
}

u8 bml3_state::ym2203_r()
{
	u8 dev_offs = ((m_psg_latch & 3) != 3);

	return m_ym2203->read(dev_offs);
}

void bml3_state::ym2203_w(u8 data)
{
	u8 dev_offs = ((m_psg_latch & 3) != 3);

	m_ym2203->write(dev_offs, data);
}

/*
 * C-REG-SELECT register
 * Reads from a VRAM address copy the corresponding 'colour RAM' address to the
 * low-order 5 bits of this register as a side-effect
 */
u8 bml3_state::c_reg_sel_r()
{
	return m_attr_latch;
}

/*
 * C_REG_SEL - Attribute register (character/video mode and colours)
 * Writes to a VRAM address copy the low-order 5 bits of this register to the corresponding 'colour RAM' address as a side-effect
 * x--- ---- "MK" bit: 0 = enable write, 1 = prohibit write
 * ---x ---- "GC" bit: 0 = character, 1 = graphic
 * ---- x--- "RV" bit: 0 = normal, 1 - reverse
 * ---- -RGB Foreground colour
 *
 */
void bml3_state::c_reg_sel_w(u8 data)
{
	m_attr_latch = data;
}

u8 bml3_state::music_sel_r()
{
	return -1; // BEEP status read?
}

// MUSIC SEL - Music select: toggle audio output level when rising
void bml3_state::music_sel_w(u8 data)
{
	m_speaker->level_w(BIT(data, 7));
}

// REMOTE - Remote relay control for cassette - bit 7
void bml3_state::remote_w(u8 data)
{
	m_cassette->change_state(
		BIT(data, 7) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
}

// BAUD SEL - ACIA clock select 600/1200 baud
// (Misspelled as BANK SEL in the English documentation)
void bml3_state::baud_sel_w(u8 data)
{
	m_baud_sel = BIT(data, 0);
	m_acia_clock->set_unscaled_clock(m_baud_sel ? 19'200 : 9'600);
}

u8 bml3_state::baud_sel_r()
{
	return m_baud_sel;
}

// KBNMI - Keyboard "Break" key non-maskable interrupt
u8 bml3_state::kbnmi_r()
{
	return m_nmi; // bit 7 used to signal a BREAK key pressure
}

// TIME_MASK - Prohibit timer IRQ
void bml3_state::time_mask_w(u8 data)
{
	m_firq_mask = data & 0x80;
	if(m_firq_mask)
	{
		m_firq_status = 0; // clear pending firq
		m_maincpu->set_input_line(M6809_FIRQ_LINE, CLEAR_LINE);
	}
}

// TIMER - System timer enable
u8 bml3_state::timer_r()
{
	u8 res = m_firq_status << 7;
	if (!machine().side_effects_disabled())
	{
		m_firq_status = 0;
		m_maincpu->set_input_line(M6809_FIRQ_LINE, CLEAR_LINE);
	}
	return res;
}


void bml3_state::main_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x03ff).ram();
	map(0x0400, 0x43ff).rw(FUNC(bml3_state::vram_r), FUNC(bml3_state::vram_w));
	map(0x4400, 0x7fff).ram();
	map(0x8000, 0x9fff).ram();
	map(0xa000, 0xbfff).view(m_banka);
	m_banka[0](0xa000, 0xbfff).readonly().share("rama");
	map(0xa000, 0xbfff).writeonly().share("rama");
	map(0xc000, 0xdfff).view(m_bankc);
	m_bankc[0](0xc000, 0xdfff).readonly().share("ramc");
	map(0xc000, 0xdfff).writeonly().share("ramc");
	map(0xe000, 0xefff).view(m_banke);
	m_banke[0](0xe000, 0xefff).readonly().share("rame");
	map(0xe000, 0xefff).writeonly().share("rame");
	map(0xf000, 0xfeff).view(m_bankf);
	m_bankf[0](0xf000, 0xfeff).readonly().share("ramf");
	map(0xf000, 0xfeff).writeonly().share("ramf");
	map(0xfff0, 0xffff).view(m_bankg);
	m_bankg[0](0xfff0, 0xffff).readonly().share("ramg");
	map(0xfff0, 0xffff).writeonly().share("ramg");
	map(0x8000, 0xffff).view(m_rom_view);
	m_rom_view[0](0xa000, 0xfeff).rom().region("maincpu", 0xa000);
	m_rom_view[0](0xfff0, 0xffff).rom().region("maincpu", 0xfff0);
	map(0xff00, 0xffef).m(*this, FUNC(bml3_state::system_io));
}

// Relative to $ffxx block
void bml3_state::system_io(address_map &map)
{
	map(0x0040, 0x0046).noprw(); // RS-232C
	map(0x00c0, 0x00c3).rw("pia", FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x00c4, 0x00c5).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0x00c6, 0x00c7).rw(FUNC(bml3_state::mc6845_r), FUNC(bml3_state::mc6845_w));
	map(0x00c8, 0x00c8).r(FUNC(bml3_state::kbnmi_r));
	map(0x00c9, 0x00c9).portr("DIPSW");
	map(0x00ca, 0x00ca).r(FUNC(bml3_state::timer_r));
//  map(0x00cb, 0x00cb) LPFLG - Light pen interrupt
	map(0x00d0, 0x00d0).w(FUNC(bml3_state::mode_sel_w));
//  map(0x00d1, 0x00d1) TRACE - Trace counter
	map(0x00d2, 0x00d2).w(FUNC(bml3_state::remote_w));
	map(0x00d3, 0x00d3).rw(FUNC(bml3_state::music_sel_r), FUNC(bml3_state::music_sel_w));
	map(0x00d4, 0x00d4).w(FUNC(bml3_state::time_mask_w));
	map(0x00d5, 0x00d5).noprw(); // L/P ENBL - Light pen operation enable
	map(0x00d6, 0x00d6).w(FUNC(bml3_state::interlace_sel_w));
	map(0x00d7, 0x00d7).rw(FUNC(bml3_state::baud_sel_r), FUNC(bml3_state::baud_sel_w));
	map(0x00d8, 0x00d8).rw(FUNC(bml3_state::c_reg_sel_r), FUNC(bml3_state::c_reg_sel_w));
	map(0x00e0, 0x00e0).rw(FUNC(bml3_state::kb_sel_r), FUNC(bml3_state::kb_sel_w));
//  map(0x00e8, 0x00e8) bank register
//  map(0x00e9, 0x00e9) IG mode register (Mark 5 only, below)
//  map(0x00ea, 0x00ea) IG enable register

#if 0
	map(0xff00, 0xff00).rw(FUNC(bml3_state::ym2203_r), FUNC(bml3_state::ym2203_w));
	map(0xff02, 0xff02).rw(FUNC(bml3_state::psg_latch_r), FUNC(bml3_state::psg_latch_w)); // PSG address/data select
#endif
}

void bml3mk5_state::ig_ram_w(offs_t offset, u8 data)
{
	for (int i = 0; i < 3; i++)
	{
		if (BIT(m_igen, i))
			m_ig_ram[offset + 0x800 * i] = data;
	}
	m_gfxdecode->gfx(0)->mark_dirty(offset >> 3);
}

void bml3mk5_state::main_map(address_map &map)
{
	bml3_state::main_map(map);
	map(0xa000, 0xa7ff).view(m_ig_view);
	m_ig_view[0](0xa000, 0xa7ff).writeonly().w(FUNC(bml3mk5_state::ig_ram_w));
}

void bml3mk5_state::system_io(address_map &map)
{
	bml3_state::system_io(map);
	// IGMODREG
	map(0x00e9, 0x00e9).lw8(
		NAME([this] (u8 data) {
			if (BIT(data, 0))
				m_ig_view.select(0);
			else
				m_ig_view.disable();
		})
	);
	// IGENREG
	map(0x00ea, 0x00ea).lw8(
		NAME([this] (u8 data) {
			m_igen = data & 7;
		})
	);
}

INPUT_CHANGED_MEMBER(bml3_state::nmi_button)
{
	// TODO: supposed to actually raise an NMI, just like earlier Basic Master LV1/2
	m_nmi = newval ? 0x80 : 0;
}

static INPUT_PORTS_START( bml3 )

	// DIP switches (service manual p.88)
	// Note the NEWON command reboots with a soft override for the DIP switch
	PORT_START("DIPSW")
	PORT_DIPNAME( 0x01, 0x01, "BASIC/terminal mode") PORT_DIPLOCATION("SW:!1")
	PORT_DIPSETTING(0x00, "Terminal mode")
	PORT_DIPSETTING(0x01, "BASIC mode")
	PORT_DIPNAME( 0x02, 0x02, "Interlaced video") PORT_DIPLOCATION("SW:!2")
	PORT_DIPSETTING(0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(0x02, DEF_STR( On ))
	// This is overridden by the 'Mode' toggle button
	PORT_DIPNAME( 0x04, 0x04, "40-/80-column") PORT_DIPLOCATION("SW:!3")
	PORT_DIPSETTING(0x00, "40 chars/line")
	PORT_DIPSETTING(0x04, "80 chars/line")
	PORT_DIPNAME( 0x08, 0x00, "Video resolution") PORT_DIPLOCATION("SW:!4")
	PORT_DIPSETTING(0x00, "High")
	PORT_DIPSETTING(0x08, "Low")
	PORT_DIPNAME( 0x10, 0x00, "Show PF key content") PORT_DIPLOCATION("SW:!5")
	PORT_DIPSETTING(0x00, DEF_STR ( Off ) )
	PORT_DIPSETTING(0x10, DEF_STR ( On ))
	PORT_DIPNAME( 0x20, 0x00, "Terminal duplex") PORT_DIPLOCATION("SW:!6")
	PORT_DIPSETTING(0x00, "Full duplex")
	PORT_DIPSETTING(0x20, "Half duplex")
	PORT_DIPNAME( 0x40, 0x00, "Terminal bits") PORT_DIPLOCATION("SW:!7")
	PORT_DIPSETTING(0x00, "8 bits/char")
	PORT_DIPSETTING(0x40, "7 bits/char")
	PORT_DIPNAME( 0x80, 0x00, "Hiragana->Katakana") PORT_DIPLOCATION("SW:!8")
	PORT_DIPSETTING( 0x00, DEF_STR( Off ) )
	PORT_DIPSETTING( 0x80, DEF_STR( On ) )

// TODO: also model the CS jumper block?

	// RAM expansion configurations (see service manual p.76)
/*
    PORT_START("RAM")
    PORT_DIPNAME( 0x0003, 0x0002, "RAM size" )
    PORT_DIPSETTING(   0x0000, "32 kiB (standard)" )
    PORT_DIPSETTING(   0x0001, "40 kiB (32 kiB + 8 kiB)" )
    PORT_DIPSETTING(   0x0002, "60 kiB (32 kiB + 28 kiB)" )
*/

	PORT_START("X0") //0x00-0x1f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("? PAD")
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Control") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("X2")  // ???
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Caps Lock") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Kana Lock") PORT_CODE(KEYCODE_NUMLOCK)
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Kana Shift")  PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("8 PAD") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("9 PAD") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("*") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR(39)
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('^')
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Delete") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("7 PAD") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Delete PAD") //backspace
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(u8"¥") PORT_CODE(KEYCODE_TAB)

	PORT_START("X1") //0x20-0x3f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('@')
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0 PAD") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(". PAD")  PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("HOME") PORT_CODE(KEYCODE_HOME) //or cls?
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("4 PAD") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("5 PAD") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("6 PAD") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("- PAD") PORT_CODE(KEYCODE_MINUS_PAD)

	PORT_START("X2") //0x40-0x5f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("/ PAD") PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("_") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('_')
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1 PAD") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2 PAD") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3 PAD") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("+") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF5") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT(0xffe00000,IP_ACTIVE_HIGH,IPT_UNKNOWN)

	PORT_START("X3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bml3_state::nmi_button), 0)
INPUT_PORTS_END

static INPUT_PORTS_START( bml3mk5 )
	PORT_INCLUDE( bml3 )
	// No dipswitches on Mark 5
	PORT_MODIFY("DIPSW")
	PORT_BIT( 0x03, IP_ACTIVE_LOW, IPT_UNUSED )
	// TODO: add MODE front panel button here, in place of dipswitch
	// On regular bml3 there's an extra button that xor content, that is directly routed here
	PORT_BIT( 0xf8, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

TIMER_DEVICE_CALLBACK_MEMBER(bml3_state::keyboard_callback)
{
	bool trigger = false;

	if(!BIT(m_keyb_scancode, 7))
	{
		m_keyb_scancode = (m_keyb_scancode + 1) & 0x7F;
		if (m_keyb_counter_operation_disabled)
		{
			m_keyb_scancode &= 0x7;
		}

		if (m_keyb_scancode == 0x7F)
		{
			if (m_keyb_empty_scan == 1)
			{
				// full scan completed with no keypress
				trigger = true;
			}
			if (m_keyb_empty_scan > 0)
				m_keyb_empty_scan--;
		}
		else if (m_keyb_scancode < 32*3)
		{
			u8 port_i = m_keyb_scancode / 32;
			u8 i = m_keyb_scancode % 32;
			if(BIT(m_io_keyboard[port_i]->read(),i))
			{
				m_keyb_empty_scan = 2;
				trigger = true;
			}
		}
		if (trigger)
		{
			m_kbt = 0xfff;
			m_keyb_scancode |= 0x80;
			if (m_keyb_interrupt_enabled)
				m_maincpu->set_input_line(M6809_IRQ_LINE, HOLD_LINE);
		}
	}
	else
	{
		if (m_kbt > 0)
			m_kbt--;
		if (m_kbt == 1)
			m_keyb_scancode &= 0x7f;
	}
}

TIMER_DEVICE_CALLBACK_MEMBER( bml3_state::kansas_r )
{
	/* cassette - turn 1200/2400Hz to a bit */
	m_cass_data[1]++;
	u8 cass_ws = (m_cassette->input() > +0.03) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		m_acia->write_rxd((m_cass_data[1] < 12) ? 1 : 0);
		m_cass_data[1] = 0;
	}
}


INTERRUPT_GEN_MEMBER(bml3_state::timer_firq)
{
	if(!m_firq_mask)
	{
		m_maincpu->set_input_line(M6809_FIRQ_LINE, ASSERT_LINE);
		m_firq_status = 1;
	}
}

void bml3_state::machine_start()
{
	m_vram = make_unique_clear<u8[]>(0x4000);
	m_aram = make_unique_clear<u8[]>(0x4000);

	// TODO: this setup is goofy, and bound to fail if things changes downstream.
	// Should really overlay the ROM region, not the view itself.
	// Additionally each slot has separate ROM KIL and EXROM KIL signal controls ...
	m_bml3bus->map_exrom(m_rom_view[0]);
	m_bml3bus->map_io(m_maincpu->space(AS_PROGRAM));

	save_pointer(NAME(m_vram), 0x4000);
	save_pointer(NAME(m_aram), 0x4000);

	save_item(NAME(m_hres_reg));
	save_item(NAME(m_crtc_vreg));
	save_item(NAME(m_psg_latch));
	save_item(NAME(m_attr_latch));
	save_item(NAME(m_vres_reg));
	save_item(NAME(m_keyb_interrupt_enabled));
	save_item(NAME(m_keyb_nmi_disabled));
	save_item(NAME(m_keyb_counter_operation_disabled));
	save_item(NAME(m_keyb_empty_scan));
	save_item(NAME(m_keyb_scancode));
	save_item(NAME(m_keyb_capslock_led_on));
	save_item(NAME(m_keyb_hiragana_led_on));
	save_item(NAME(m_keyb_katakana_led_on));
	save_item(NAME(m_kbt));
	save_item(NAME(m_cassbit));
	save_item(NAME(m_cassold));
	save_item(NAME(m_cass_data));
	save_item(NAME(m_crtc_index));
	save_item(NAME(m_firq_mask));
	save_item(NAME(m_firq_status));
	save_item(NAME(m_nmi));
}

void bml3_state::machine_reset()
{
	/* defaults */
	m_rom_view.select(0);
	m_banka.disable();
	m_bankc.disable();
	m_banke.disable();
	m_bankf.disable();
	m_bankg.disable();

	m_firq_mask = -1; // disable firq
	m_psg_latch = 0;
	m_attr_latch = 0;
	m_vres_reg = 0;
	m_keyb_interrupt_enabled = 0;
	m_keyb_nmi_disabled = 0;
	m_keyb_counter_operation_disabled = 0;
	m_keyb_empty_scan = 0;
	m_keyb_scancode = 0;
	m_keyb_capslock_led_on = 0;
	m_keyb_hiragana_led_on = 0;
	m_keyb_katakana_led_on = 0;
	m_crtc_index = 0;
	m_firq_status = 0;
	m_cassbit = 0;
	m_cassold = 0;
	m_nmi = 0;
	m_kbt = 0;

	// NOTE: bml3 do not bother with CRTC on soft resets (which is physically tied to front panel).
	//m_hres_reg = 0;
	//m_vres_reg = 0;
	//crtc_change_clock();
}

void bml3mk5_state::machine_start()
{
	bml3_state::machine_start();
	m_ig_ram = make_unique_clear<u8[]>(0x800 * 3);
	m_gfxdecode->gfx(0)->set_source_and_total(m_ig_ram.get(), 0x100);

	save_pointer(NAME(m_ig_ram), 0x800 * 3);
}

void bml3mk5_state::machine_reset()
{
	bml3_state::machine_reset();
	m_ig_view.disable();
	m_igen = 0;
}

void bml3_state::piaA_w(uint8_t data)
{
	/* ROM banking:
	-0-- --0- 0xa000 - 0xbfff ROM R RAM W
	-1-- --0- 0xa000 - 0xbfff RAM R/W
	-x-- --1- 0xa000 - 0xbfff no change
	0--- -0-- 0xc000 - 0xdfff ROM R RAM W
	1--- -0-- 0xc000 - 0xdfff RAM R/W
	x--- -1-- 0xc000 - 0xdfff no change
	0--- 0--- 0xe000 - 0xefff ROM R RAM W
	1--- 0--- 0xe000 - 0xefff RAM R/W
	x--- 1--- 0xe000 - 0xefff no change
	---- ---x 0xf000 - 0xfeff (0) ROM R RAM W (1) RAM R/W
	---- --x- 0xfff0 - 0xffff (0) ROM R RAM W (1) RAM R/W
	*/
	logerror("Check banking PIA A -> %02x\n",data);

	if (!BIT(data, 1))
	{
		if (BIT(data, 6))
			m_banka.select(0);
		else
			m_banka.disable();
	}

	if (!BIT(data, 2))
	{
		if (BIT(data, 7))
			m_bankc.select(0);
		else
			m_bankc.disable();
	}

	if (!BIT(data, 3))
	{
		if (BIT(data, 7))
			m_banke.select(0);
		else
			m_banke.disable();
	}

	if (BIT(data, 0))
		m_bankf.select(0);
	else
		m_bankf.disable();

	if (BIT(data, 1))
		m_bankg.select(0);
	else
		m_bankg.disable();
}

void bml3_state::acia_rts_w(int state)
{
	logerror("%02x TAPE RTS\n",state);
}

void bml3_state::acia_irq_w(int state)
{
	logerror("%02x TAPE IRQ\n",state);
}

TIMER_DEVICE_CALLBACK_MEMBER( bml3_state::kansas_w )
{
	m_cass_data[3]++;

	if (m_cassbit != m_cassold)
	{
		m_cass_data[3] = 0;
		m_cassold = m_cassbit;
	}

	if (m_cassbit)
		m_cassette->output(BIT(m_cass_data[3], 0) ? -1.0 : +1.0); // 2400Hz
	else
		m_cassette->output(BIT(m_cass_data[3], 1) ? -1.0 : +1.0); // 1200Hz
}

// Debugging only
static const gfx_layout ig_charlayout =
{
	8, 8,
	0x100,
	3,
	{ 0x1000*8, 0x800*8, 0 },
	{ STEP8(0, 1) },
	{ STEP8(0, 8) },
	8*8
};

static GFXDECODE_START( gfx_bml3mk5 )
	GFXDECODE_RAM( nullptr, 0, ig_charlayout, 0, 1 )
GFXDECODE_END


static void bml3_cards(device_slot_interface &device)
{
	device.option_add("mp1802", BML3BUS_MP1802);
	device.option_add("mp1805", BML3BUS_MP1805);
	device.option_add("kanji",  BML3BUS_KANJI);
	device.option_add("rtc",    BML3BUS_RTC);
}

void bml3_state::bml3(machine_config &config)
{
	/* basic machine hardware */
	MC6809(config, m_maincpu, CPU_EXT_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &bml3_state::main_map);
	m_maincpu->set_vblank_int("screen", FUNC(bml3_state::timer_firq));
//  m_maincpu->set_periodic_int(FUNC(bml3_state::firq), attotime::fromhz(45));

	// fire once per scan of an individual key
	// According to the service manual (p.65), the keyboard timer is driven by the horizontal video sync clock.
	TIMER(config, "keyboard_timer").configure_periodic(FUNC(bml3_state::keyboard_callback), attotime::from_hz(H_CLOCK/2));
	TIMER(config, "kansas_w").configure_periodic(FUNC(bml3_state::kansas_w), attotime::from_hz(4800));
	TIMER(config, "kansas_r").configure_periodic(FUNC(bml3_state::kansas_r), attotime::from_hz(40000));

	pia6821_device &pia(PIA6821(config, "pia"));
	pia.writepa_handler().set(FUNC(bml3_state::piaA_w));

	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set([this] (bool state) { m_cassbit = state; });
	m_acia->rts_handler().set(FUNC(bml3_state::acia_rts_w));
	m_acia->irq_handler().set(FUNC(bml3_state::acia_irq_w));

	CLOCK(config, m_acia_clock, 9'600); // 600 baud x 16(divider) = 9600
	m_acia_clock->signal_handler().set(m_acia, FUNC(acia6850_device::write_txc));
	m_acia_clock->signal_handler().append(m_acia, FUNC(acia6850_device::write_rxc));

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("bml3_cass");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	// Service manual specifies "Raster return period" as 2.4 ms (p.64),
	// although the total vertical non-displaying time seems to be 4 ms.
	// NOTE: D80_CLOCK x 2 as per MAME interlace limitation
	screen.set_raw(D80_CLOCK * 2, 1024, 0, 640 - 1, 518, 0, 400 - 1);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	PALETTE(config, m_palette, palette_device::BRG_3BIT);

	/* Devices */
	// CRTC clock should be synchronous with the CPU clock.
	HD6845S(config, m_crtc, CPU_CLOCK); // HD46505SP
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(bml3_state::crtc_update_row));

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	BML3BUS(config, m_bml3bus, 0);
	m_bml3bus->nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_bml3bus->irq_callback().set_inputline(m_maincpu, M6809_IRQ_LINE);
	m_bml3bus->firq_callback().set_inputline(m_maincpu, M6809_FIRQ_LINE);
	/* Default to nothing, to stop machine hanging at start.
	   Can use MP-1805 disk (3" or 5.25" SS/SD), as our MB-6892 ROM dump includes the MP-1805 ROM.
	   Or use MP-1802 (5.25" DS/DD).
	   Note it isn't feasible to use both, as they each place boot ROM at F800.
	 */
	// TODO: find actual defaults for each variant (should be no option for at least base model)
	BML3BUS_SLOT(config, "sl1", m_bml3bus, bml3_cards, nullptr);
	BML3BUS_SLOT(config, "sl2", m_bml3bus, bml3_cards, "rtc");
	BML3BUS_SLOT(config, "sl3", m_bml3bus, bml3_cards, nullptr);
	BML3BUS_SLOT(config, "sl4", m_bml3bus, bml3_cards, nullptr);
	BML3BUS_SLOT(config, "sl5", m_bml3bus, bml3_cards, nullptr);
	BML3BUS_SLOT(config, "sl6", m_bml3bus, bml3_cards, "kanji");

	SOFTWARE_LIST(config, "cass_list").set_original("bml3_cass");

#if 0
	// TODO: slot device for sound card
	// audio
	YM2203(config, m_ym2203, 2000000); //unknown clock / divider
	m_ym2203->set_flags(AY8910_LEGACY_OUTPUT);
	m_ym2203->add_route(0, "mono", 0.25);
	m_ym2203->add_route(1, "mono", 0.25);
	m_ym2203->add_route(2, "mono", 0.50);
	m_ym2203->add_route(3, "mono", 0.50);
#endif
}

void bml3mk2_state::bml3mk2(machine_config &config)
{
	bml3_state::bml3(config);
	// TODO: override bus defaults
}

void bml3mk5_state::bml3mk5(machine_config &config)
{
	bml3mk2_state::bml3mk2(config);
	// TODO: override bus defaults
	GFXDECODE(config, "gfxdecode", "palette", gfx_bml3mk5);
}

/* ROM definition */
// floppy-drive slot devices expect "maincpu" is sized 0x10000.
ROM_START( bml3 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
//  ROM_LOAD( "l3bas.rom", 0xa000, 0x6000, BAD_DUMP CRC(d81baa07) SHA1(a8fd6b29d8c505b756dbf5354341c48f9ac1d24d)) //original, 24k isn't a proper rom size!
	// TODO: replace with MB-6890 ROMs (these are from an MB-6892)
	ROM_LOAD( "598 p16611.ic3", 0xa000, 0x2000, BAD_DUMP CRC(954b9bad) SHA1(047948fac6808717c60a1d0ac9205a5725362430))
	ROM_LOAD( "599 p16561.ic4", 0xc000, 0x2000, BAD_DUMP CRC(b27a48f5) SHA1(94cb616df4caa6415c5076f9acdf675acb7453e2))
	// TODO: Replace checksums with a ROM dump without a disk controller patch (checksums here are inclusive of the MP1805 patch)
	ROM_LOAD( "600 p16681.ic5", 0xe000, 0x2000, BAD_DUMP CRC(fe3988a5) SHA1(edc732f1cd421e0cf45ffcfc71c5589958ceaae7))

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD("font.rom", 0x0000, 0x1000, BAD_DUMP CRC(0b6f2f10) SHA1(dc411b447ca414e94843636d8b5f910c954581fb) ) // handcrafted
ROM_END

ROM_START( bml3mk2 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
//  ROM_LOAD( "l3bas.rom", 0xa000, 0x6000, BAD_DUMP CRC(d81baa07) SHA1(a8fd6b29d8c505b756dbf5354341c48f9ac1d24d)) //original, 24k isn't a proper rom size!
	// TODO: replace with MB-6891 ROMs (these are from an MB-6892)
	ROM_LOAD( "598 p16611.ic3", 0xa000, 0x2000, BAD_DUMP CRC(954b9bad) SHA1(047948fac6808717c60a1d0ac9205a5725362430))
	ROM_LOAD( "599 p16561.ic4", 0xc000, 0x2000, BAD_DUMP CRC(b27a48f5) SHA1(94cb616df4caa6415c5076f9acdf675acb7453e2))
	ROM_LOAD( "600 p16681.ic5", 0xe000, 0x2000, BAD_DUMP CRC(fe3988a5) SHA1(edc732f1cd421e0cf45ffcfc71c5589958ceaae7))

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD("font.rom", 0x0000, 0x1000, BAD_DUMP CRC(0b6f2f10) SHA1(dc411b447ca414e94843636d8b5f910c954581fb) ) // handcrafted
ROM_END

ROM_START( bml3mk5 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
//  ROM_LOAD( "l3bas.rom", 0xa000, 0x6000, BAD_DUMP CRC(d81baa07) SHA1(a8fd6b29d8c505b756dbf5354341c48f9ac1d24d)) //original, 24k isn't a proper rom size!
	/* Handcrafted ROMs, rom labels and contents might not match */
	ROM_LOAD( "598 p16611.ic3", 0xa000, 0x2000, BAD_DUMP CRC(954b9bad) SHA1(047948fac6808717c60a1d0ac9205a5725362430))
	ROM_LOAD( "599 p16561.ic4", 0xc000, 0x2000, BAD_DUMP CRC(b27a48f5) SHA1(94cb616df4caa6415c5076f9acdf675acb7453e2))
	// TODO: Replace checksums with a ROM dump without a disk controller patch (checksums here are inclusive of the MP1805 patch)
	ROM_LOAD( "600 p16681.ic5", 0xe000, 0x2000, BAD_DUMP CRC(fe3988a5) SHA1(edc732f1cd421e0cf45ffcfc71c5589958ceaae7))

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD("font.rom", 0x0000, 0x1000, BAD_DUMP CRC(0b6f2f10) SHA1(dc411b447ca414e94843636d8b5f910c954581fb) ) // handcrafted
ROM_END

COMP( 1980, bml3,    0,     0,      bml3,    bml3,     bml3_state,    empty_init, "Hitachi", "Basic Master Level 3 (MB-6890)",        MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1982, bml3mk2, bml3,  0,      bml3mk2, bml3,     bml3mk2_state, empty_init, "Hitachi", "Basic Master Level 3 Mark II (MB-6891)", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1983, bml3mk5, bml3,  0,      bml3mk5, bml3mk5,  bml3mk5_state, empty_init, "Hitachi", "Basic Master Level 3 Mark 5 (MB-6892)", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



bob85.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

BOB85 driver by Miodrag Milanovic

2008-05-24 Preliminary driver.
2009-05-12 Skeleton driver.
2013-06-02 Working driver.
2019-07-14 Fixed cassette load - it loads correctly then says EEEE

Pasting:
        0-F : as is
        NEXT : ^
        SMEM : -
        GO : X

Test Paste:
        -0600^11^22^33^44^55^66^77^88^99^--0600^
        Now press up-arrow to confirm the data has been entered.


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

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/timer.h"
#include "imagedev/cassette.h"
#include "speaker.h"
#include "bob85.lh"


namespace {

class bob85_state : public driver_device
{
public:
	bob85_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cass(*this, "cassette")
		, m_line0(*this, "LINE0")
		, m_line1(*this, "LINE1")
		, m_line2(*this, "LINE2")
		, m_digits(*this, "digit%u", 0U)
		{ }

	void bob85(machine_config &config);

private:
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	uint8_t bob85_keyboard_r();
	void bob85_7seg_w(offs_t offset, uint8_t data);
	void sod_w(int state);
	int sid_r();
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);
	uint8_t m_prev_key = 0;
	uint8_t m_count_key = 0;
	u16 m_casscnt = 0;
	bool m_cassold = false, m_cassbit = false;
	void machine_start() override ATTR_COLD;
	required_device<i8085a_cpu_device> m_maincpu;
	required_device<cassette_image_device> m_cass;
	required_ioport m_line0;
	required_ioport m_line1;
	required_ioport m_line2;
	output_finder<6> m_digits;
};



uint8_t bob85_state::bob85_keyboard_r()
{
	uint8_t retVal = 0;
	uint8_t line0 = m_line0->read();
	uint8_t line1 = m_line1->read();
	uint8_t line2 = m_line2->read();

	if (line0)
	{
		switch(line0)
		{
			case 0x01 : retVal = 0x80; break;
			case 0x02 : retVal = 0x81; break;
			case 0x04 : retVal = 0x82; break;
			case 0x08 : retVal = 0x83; break;
			case 0x10 : retVal = 0x84; break;
			case 0x20 : retVal = 0x85; break;
			case 0x40 : retVal = 0x86; break;
			case 0x80 : retVal = 0x87; break;
			default : break;
		}
	}

	if (line1)
	{
		switch(line1)
		{
			case 0x01 : retVal = 0x88; break;
			case 0x02 : retVal = 0x89; break;
			case 0x04 : retVal = 0x8A; break;
			case 0x08 : retVal = 0x8B; break;
			case 0x10 : retVal = 0x8C; break;
			case 0x20 : retVal = 0x8D; break;
			case 0x40 : retVal = 0x8E; break;
			case 0x80 : retVal = 0x8F; break;
			default : break;
		}
	}

	if (line2)
	{
		switch(line2)
		{
			case 0x01 : retVal |= 0x90; break;
			case 0x02 : retVal |= 0xA0; break;
			case 0x04 : retVal |= 0xB0; break;
			case 0x08 : retVal |= 0xC0; break;
			case 0x10 : retVal |= 0xD0; break;
			case 0x20 : retVal |= 0xE0; break;
			default : break;
		}
	}

	if (retVal != m_prev_key)
	{
		m_prev_key = retVal;
		m_count_key = 0;
		return retVal;
	}
	else
	{
		if (m_count_key <1)
		{
			m_count_key++;
			return retVal;
		}
		else
			return 0;
	}
}

void bob85_state::bob85_7seg_w(offs_t offset, uint8_t data)
{
	m_digits[offset] = bitswap<8>( data,3,2,1,0,7,6,5,4 );
}

void bob85_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x02ff).rom();
	map(0x0600, 0x09ff).ram();
}

void bob85_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0a, 0x0a).r(FUNC(bob85_state::bob85_keyboard_r));
	map(0x0a, 0x0f).w(FUNC(bob85_state::bob85_7seg_w));
}

/* Input ports */
static INPUT_PORTS_START( bob85 )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7')

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("GO") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SMEM") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("REC") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("VEK1") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("VEK2") PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("NEXT") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT(0xC0, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

void bob85_state::machine_start()
{
	m_digits.resolve();
	save_item(NAME(m_prev_key));
	save_item(NAME(m_count_key));
	save_item(NAME(m_casscnt));
	save_item(NAME(m_cassold));
	save_item(NAME(m_cassbit));
}

TIMER_DEVICE_CALLBACK_MEMBER( bob85_state::kansas_r )
{
	/* cassette - turn pulses into a bit */
	bool cass_ws = (m_cass->input() > +0.04) ? 1 : 0;
	m_casscnt++;

	if (cass_ws != m_cassold)
	{
		m_cassold = cass_ws;
		m_cassbit = (m_casscnt < 12) ? 1 : 0;
		m_casscnt = 0;
	}
	else
	if (m_casscnt > 32)
	{
		m_casscnt = 32;
		m_cassbit = 0;
	}
}

void bob85_state::sod_w(int state)
{
	m_cass->output(state ? +1.0 : -1.0);
}

int bob85_state::sid_r()
{
	return m_cassbit;
}

void bob85_state::bob85(machine_config &config)
{
	/* basic machine hardware */
	I8085A(config, m_maincpu, XTAL(5'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &bob85_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &bob85_state::io_map);
	m_maincpu->in_sid_func().set(FUNC(bob85_state::sid_r));
	m_maincpu->out_sod_func().set(FUNC(bob85_state::sod_w));

	/* video hardware */
	config.set_default_layout(layout_bob85);

	SPEAKER(config, "mono").front_center();

	// devices
	CASSETTE(config, m_cass).set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	TIMER(config, "kansas_r").configure_periodic(FUNC(bob85_state::kansas_r), attotime::from_hz(40000));
}

/* ROM definition */
ROM_START( bob85 )
	ROM_REGION( 0x0300, "maincpu", 0 )
	ROM_LOAD( "bob85.rom", 0x0000, 0x0300, BAD_DUMP CRC(adde33a8) SHA1(00f26dd0c52005e7705e6cc9cb11a20e572682c6) ) // should be 6 separate 74S287's (256x4)
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY             FULLNAME  FLAGS
COMP( 1984, bob85, 0,      0,      bob85,   bob85, bob85_state, empty_init, "Josef Kratochvil", "BOB-85", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



boris.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Applied Concepts Boris (electronic chess computer)

Hardware notes:
- MK3850N-3 CPU @ 2 MHz from XTAL, MK3853N memory interface
- 256 bytes RAM(2*2112), 2*AMI 2KB ROM (2nd ROM only half used)
- 8-digit 16seg led panel

When it was first released, it was in kit form. An extensive assembly manual with
schematics was included. It was later distributed by Chafitz in pre-assembled form.
There's also an updated revision, identifiable by the startup message "Boris awaits
your move"(same as Boris Master) instead of "Boris plays black".

Boris Master included a battery, RESET was renamed to MEMORY. 2 known versions:
one with C10617/C10618 ROMs(same as Boris rev. 01), and one with a single 4KB
ROM labeled 007-7027-00.

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

#include "emu.h"

#include "cpu/f8/f8.h"
#include "machine/f3853.h"
#include "video/pwm.h"

// internal artwork
#include "aci_boris.lh"


namespace {

class boris_state : public driver_device
{
public:
	boris_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void boris(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(reset_switch) { update_reset(newval); }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_ioport_array<4> m_inputs;

	u8 m_io[2] = { };
	u8 m_4042 = 0;

	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	void update_reset(ioport_value state);

	void update_display();
	void mux_w(u8 data);
	void digit_w(u8 data);
	u8 input_r();
};

void boris_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_io));
	save_item(NAME(m_4042));
}

void boris_state::machine_reset()
{
	update_reset(ioport("RESET")->read());
}

void boris_state::update_reset(ioport_value state)
{
	// reset switch is tied to MK3850 RESET pin
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? ASSERT_LINE : CLEAR_LINE);

	// clear display
	if (state)
		m_display->clear();
}



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

// MK3850 ports/TTL

void boris_state::update_display()
{
	// port 1 is latched as long as 4042 clock is low
	// (yes low, this is actually (~~m_io[0] & 8) since output ports are inverted)
	if (m_io[0] & 8)
		m_4042 = bitswap<8>(m_io[1],4,2,0,6,5,1,3,7);

	// 16 segments via port 1 and 4042 output (latched port 1)
	u16 seg_data = ~(m_4042 << 8 | m_io[1]);
	m_display->matrix(1 << (~m_io[0] & 7), seg_data);
}

void boris_state::mux_w(u8 data)
{
	// IO00-IO02: 4028 A-C to digit/input mux (4028 D to GND)
	// IO03: clock 4042
	m_io[0] = data;
	update_display();
}

u8 boris_state::input_r()
{
	// IO04-IO07: multiplexed inputs from 4028 4-7
	u8 data = m_io[0];
	u8 sel = ~data & 7;
	if (sel >= 4)
		data |= m_inputs[sel-4]->read() << 4;

	return data;
}

void boris_state::digit_w(u8 data)
{
	// IO10-IO17: digit segments
	m_io[1] = data;
	update_display();
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void boris_state::main_map(address_map &map)
{
	map.global_mask(0x0fff);
	map(0x0000, 0x0bff).rom();
	map(0x0c00, 0x0cff).mirror(0x300).ram();
}

void boris_state::main_io(address_map &map)
{
	map(0x00, 0x00).rw(FUNC(boris_state::input_r), FUNC(boris_state::mux_w));
	map(0x01, 0x01).w(FUNC(boris_state::digit_w));
	map(0x0c, 0x0f).rw("smi", FUNC(f3853_device::read), FUNC(f3853_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( boris )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("G.7")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("H.8")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("Set / 9") // labeled just "SET" on 1st version
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE") // clear entry

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("D.4 / Rook")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("E.5 / Queen")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("F.6 / King")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Time")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("A.1 / Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("B.2 / Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("C.3 / Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_K) PORT_NAME("Rank")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_SPACE) PORT_CODE(KEYCODE_MINUS) PORT_NAME("-")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("B/W") // black/white
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_F1) PORT_TOGGLE PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(boris_state::reset_switch), 0) PORT_NAME("Reset Switch")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void boris_state::boris(machine_config &config)
{
	// basic machine hardware
	F8(config, m_maincpu, 2_MHz_XTAL); // MK3850
	m_maincpu->set_addrmap(AS_PROGRAM, &boris_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &boris_state::main_io);
	m_maincpu->set_irq_acknowledge_callback("smi", FUNC(f3853_device::int_acknowledge));

	f3853_device &smi(F3853(config, "smi", 2_MHz_XTAL));
	smi.int_req_callback().set_inputline("maincpu", F8_INPUT_LINE_INT_REQ);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 16);
	m_display->set_segmask(0xff, 0xffff);
	m_display->set_bri_levels(0.05);
	config.set_default_layout(layout_aci_boris);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( boris )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("007-7020-01_c10617", 0x0000, 0x0800, CRC(dadf1693) SHA1(ffaef7a78f07dfcec9cc6e4034d665d188748225) )
	ROM_LOAD("007-7021-01_c10618", 0x0800, 0x0800, CRC(89b10faa) SHA1(b86cf42f93051b29f398691270e9a860b2978043) ) // identical halves
ROM_END

ROM_START( borisa )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("007-7020-00_c10502", 0x0000, 0x0800, CRC(18182870) SHA1(cb717a4b5269b04b0d7ae61aaf4a8f6a019626a5) )
	ROM_LOAD("007-7021-00_c10503", 0x0800, 0x0800, CRC(4185d183) SHA1(43155493593d6f52a0f6906d4414f4eff3098c5f) ) // identical halves, less than 512 bytes used
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY, FULLNAME, FLAGS
SYST( 1978, boris,  0,      0,      boris,   boris, boris_state, empty_init, "Applied Concepts", "Boris (rev. 01)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW ) // "Boris awaits your move"
SYST( 1978, borisa, boris,  0,      boris,   boris, boris_state, empty_init, "Applied Concepts", "Boris (rev. 00)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW ) // "Boris plays black"



borisdpl.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco, hap
// thanks-to:Sean Riddle
/*******************************************************************************

Applied Concepts Boris Diplomat

Hardware notes:
- F3870 MCU (Motorola SC80265P or Fairchild SL90259)
- 256 bytes RAM(2*2112-1)
- 8-digit 7seg led panel

Two versions exist, a blue one(seen with SC80265P) and a brown one(seen with
either SC80265P or SL90259). The one emulated here is from a brown version with
the SC80265P. Motorola SC80265P is a 3870 clone, it's assumed that the program
is the same as SL90259.

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

#include "emu.h"

#include "cpu/f8/f8.h"
#include "machine/f3853.h"
#include "video/pwm.h"

// internal artwork
#include "aci_borisdpl.lh"


namespace {

class borisdpl_state : public driver_device
{
public:
	borisdpl_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void borisdpl(machine_config &config);

	// reset button is tied to MCU RESET pin
	DECLARE_INPUT_CHANGED_MEMBER(reset_button) { m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE); }

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_ioport_array<4> m_inputs;

	std::unique_ptr<u8[]> m_ram;
	u8 m_ram_address = 0;
	u8 m_matrix = 0;
	u8 m_digit_data = 0;

	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	void update_display();
	void digit_w(u8 data);
	u8 input_r();
	void matrix_w(u8 data);

	// 256 bytes data RAM accessed via I/O ports
	u8 ram_address_r() { return m_ram_address; }
	void ram_address_w(u8 data) { m_ram_address = data; }
	u8 ram_data_r() { return m_ram[m_ram_address]; }
	void ram_data_w(u8 data) { m_ram[m_ram_address] = data; }
};

void borisdpl_state::machine_start()
{
	m_ram = make_unique_clear<u8[]>(0x100);

	// register for savestates
	save_pointer(NAME(m_ram), 0x100);
	save_item(NAME(m_ram_address));
	save_item(NAME(m_matrix));
	save_item(NAME(m_digit_data));
}



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

// F3870 ports

void borisdpl_state::update_display()
{
	m_display->matrix(1 << (m_matrix & 7), m_digit_data);
}

void borisdpl_state::digit_w(u8 data)
{
	// digit segments
	m_digit_data = ~data & 0x7f;
	update_display();
}

void borisdpl_state::matrix_w(u8 data)
{
	// d0-d2: MC14028B to input/digit select
	m_matrix = data;
	update_display();
}

u8 borisdpl_state::input_r()
{
	// d4-d7: multiplexed inputs (only one lane can be selected at the same time)
	u8 data = m_matrix;
	if ((m_matrix & 7) < 4)
		data |= m_inputs[m_matrix & 3]->read() << 4;

	return data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void borisdpl_state::main_map(address_map &map)
{
	map.global_mask(0x7ff);
	map(0x0000, 0x07ff).rom();
}

void borisdpl_state::main_io(address_map &map)
{
	map(0x00, 0x00).rw(FUNC(borisdpl_state::input_r), FUNC(borisdpl_state::matrix_w));
	map(0x01, 0x01).w(FUNC(borisdpl_state::digit_w));
	map(0x04, 0x07).rw("psu", FUNC(f38t56_device::read), FUNC(f38t56_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( borisdpl )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_SPACE) PORT_CODE(KEYCODE_MINUS) PORT_NAME("-")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("B/W") // black/white
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("A.1 / Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("B.2 / Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("C.3 / Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_K) PORT_NAME("Rank")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("D.4 / Rook")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("E.5 / Queen")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("F.6 / King")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Time")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("G.7")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("H.8")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9 / Set")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE") // clear entry

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(borisdpl_state::reset_button), 0) PORT_NAME("Reset")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void borisdpl_state::borisdpl(machine_config &config)
{
	// basic machine hardware
	F8(config, m_maincpu, 3'000'000/2); // frequency approximated from video reference
	m_maincpu->set_addrmap(AS_PROGRAM, &borisdpl_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &borisdpl_state::main_io);
	m_maincpu->set_irq_acknowledge_callback("psu", FUNC(f38t56_device::int_acknowledge));

	f38t56_device &psu(F38T56(config, "psu", 3'000'000/2));
	psu.set_int_vector(0x5020);
	psu.int_req_callback().set_inputline("maincpu", F8_INPUT_LINE_INT_REQ);
	psu.read_a().set(FUNC(borisdpl_state::ram_data_r));
	psu.write_a().set(FUNC(borisdpl_state::ram_data_w));
	psu.read_b().set(FUNC(borisdpl_state::ram_address_r));
	psu.write_b().set(FUNC(borisdpl_state::ram_address_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 7);
	m_display->set_segmask(0xff, 0x7f);
	config.set_default_layout(layout_aci_borisdpl);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( borisdpl )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD("007-7024-00_7847.u8", 0x0000, 0x0800, CRC(e20bac03) SHA1(9e17b9d90522371fbf7018926356150f70b9a3b6) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1979, borisdpl, 0,      0,      borisdpl, borisdpl, borisdpl_state, empty_init, "Applied Concepts", "Boris Diplomat", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



boss_se70.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Boss SE-70 signal processor.

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

#include "emu.h"
#include "cpu/h8500/h8510.h"


namespace {

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

	void se70(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<h8510_device> m_maincpu;
};


void boss_se70_state::mem_map(address_map &map)
{
	map(0x000000, 0x07ffff).mirror(0xf00000).rom().region("program", 0);
}


static INPUT_PORTS_START(se70)
INPUT_PORTS_END

void boss_se70_state::se70(machine_config &config)
{
	HD6415108(config, m_maincpu, 16_MHz_XTAL);
	// TODO: operates in mode 3 with 8-bit data bus
	m_maincpu->set_addrmap(AS_PROGRAM, &boss_se70_state::mem_map);

	//TC6088AF(config, "csp", 65.152_MHz_XTAL);
	//UPD65622GF040(config, "interface", 24.576_MHz_XTAL);
}

ROM_START(se70)
	ROM_REGION16_BE(0x80000, "program", 0)
	ROM_LOAD("boss_se-70_v1.01.ic29", 0x00000, 0x80000, CRC(f19151f3) SHA1(6c0de1e0debe72374802d54f8d37517b3ad0b131)) // 27C4001
ROM_END

} // anonymous namespace


SYST(1993, se70, 0, 0, se70, se70, boss_se70_state, empty_init, "Roland", "Boss SE-70 Super Effects Processor", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



boss_sx700.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Boss SX-700 signal processor.

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

#include "emu.h"
#include "cpu/h8/h83002.h"


namespace {

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

	void sx700(machine_config &config);
	void gx700(machine_config &config);

private:
	void sx700_map(address_map &map) ATTR_COLD;
	void gx700_map(address_map &map) ATTR_COLD;

	required_device<h83002_device> m_maincpu;
};


void boss_sx700_state::sx700_map(address_map &map)
{
	map(0x000000, 0x01ffff).rom().region("program", 0);
	map(0x020000, 0x02ffff).ram();

}

void boss_sx700_state::gx700_map(address_map &map)
{
	map(0x000000, 0x01ffff).rom().region("program", 0);
	map(0x020000, 0x027fff).ram();
	map(0x028000, 0x03ffff).rom().region("program", 0x28000);
}


static INPUT_PORTS_START(sx700)
INPUT_PORTS_END

void boss_sx700_state::sx700(machine_config &config)
{
	H83002(config, m_maincpu, 16_MHz_XTAL); // HD6413002F
	// TODO: operates in mode 3 with 8-bit data bus
	m_maincpu->set_addrmap(AS_PROGRAM, &boss_sx700_state::sx700_map);

	//TC170C140AF_003(config, "dsp", 67.7376_MHz_XTAL);
}

void boss_sx700_state::gx700(machine_config &config)
{
	sx700(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &boss_sx700_state::gx700_map);
}

ROM_START(sx700)
	ROM_REGION16_BE(0x80000, "program", 0)
	ROM_LOAD("sx-700_1_0_2.ic17", 0x00000, 0x80000, CRC(6739f525) SHA1(3370f43fd586baa0bcc71891a766b45e1d42253d)) // M27C4001-10F1
ROM_END

ROM_START(gx700)
	ROM_REGION16_BE(0x40000, "program", 0)
	ROM_SYSTEM_BIOS(0, "v110", "v1.10 WATERDRAGON")
	ROMX_LOAD("gx-700_1_1_0.ic20", 0x00000, 0x40000, CRC(feb8a186) SHA1(5f039ff1aa45ed4a33ecd8fd9ad39880646999cb), ROM_BIOS(0)) // M27C2001-10F1
	ROM_SYSTEM_BIOS(1, "v109", "v1.09 WATERDRAGON")
	ROMX_LOAD("gx-700_1_0_9.ic20", 0x00000, 0x40000, CRC(e38b3eeb) SHA1(8ce0563b70d37103acafe1578706ec1c32419e34), ROM_BIOS(1)) // M27C2001-10F1
ROM_END

} // anonymous namespace


SYST(1996, sx700, 0, 0, sx700, sx700, boss_sx700_state, empty_init, "Roland", "Boss SX-700 Studio Effects Processor", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1996, gx700, 0, 0, gx700, sx700, boss_sx700_state, empty_init, "Roland", "Boss GX-700 Guitar Effects Processor", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



bpmmicro.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Jonathan Gevaryahu
/******************************************************************************
*       BPM Microsystems (formerly BP Microsystems, before 20060828)
          universal device programmers
*       Models supported in this driver so far: BP-1148/BP-1200

*       1000-series (non-ganged, manual feed) models:
*       EP-series:
*           EP-1 - 28 pin, eproms only? has adapter for 32 pin?
*           EP-1132 - 32 pin, eproms only?
*           EP-1140 - 40 pin, eproms only?
*       PLD-series:
*           PLD-1100 - ndip plds/pals only?
*           PLD-1128 - ndip plds/pals only?
*       286 based w/512k ram, fixed: (1992ish)
*           BP-1148 - fixed 512k ram, uses a special 'low cost' BP-1148 socket
              instead of a tech adapter+socket module
*           BP-1200 - fixed 512k ram, uses a TA-84 or TA-240 tech adapter plus
              a socket module, otherwise identical to above, same firmware
*       286 based w/240 pin tech adapter integrated as a "mezzanine board", expandable ram:
*           BP-1400/240 - uses a 30 OR 72 pin SIMM (some programmers may have
              the 30 pin SIMM socket populated) for up to 8MB? of ram
*           Silicon Sculptor - custom firmware locked to Actel fpga/pld [1400?]
              devices, may have a custom MB; drives 84 pins (no third connector)
              so probably "BP1400/84" based, i.e. a neutered BP-1400/240.
*           Silicon Sculptor 6X - as above but 6 programmers ganged together
*       486 based:
*           BP-1600 - 486DX4 100Mhz based, uses a 72 pin SIMM for up to 16MB of
              ram (does NOT support 32MB SIMMs!), supports 1.5vdd devices
*           Silicon Sculptor II - same as BP-1600 except it has the extra
              button and different firmware and a different mezzanine
              board/tech adapter; comes with a 72-pin SIMM installed
*       probably 'universal platform':
*           BP-2510
*       486+USB "6th Gen":
*           BP-1410 - 486DX4 based, uses a laptop SODIMM for up to 512MB? of ram?, has USB
*           BP-1610 - unclear what the difference to 1410 is
*           BP-1710 - same as BP1610, but two programmers ganged together in a single case
*           Silicon Sculptor III - 486DX4 100Mhz
*       There exist "7th" "8th" and "9th" gen programmers as well.

*       2000-series (ganged, manual programmers)
*       Unclear whether 286 or 486, all have extra button per programmer:
*           BP-2100/84x4 - four BP-1?00s ganged together [1400/84 based?]
*           BP-2200/240x2 - two BP-1?00s ganged together [1400/240 based?]
*           BP-2200/240x4 - four BP-1?00s ganged together
*           BP-2200/240x6 - six BP-1?00s ganged together
*           BP-2500/240x4 - four BP-1?00s ganged together [1600 based?]
*           BP-2000, BP-2600M - ganged programmers?

*
******************************************************************************
*       TODO:
*       Everything!
*       status LEDs
*       8-pin pin driver boards
*       tech adapter relays
*       Parallel port interface
*       Serial EEPROM (93c46) at U38 on BP1200 mainboard
*       Serial EEPROM (93c46) at (U1? U7? U12?) on TA-84 tech adapter
*       Serial EEPROM (93c46a) at ? on SM48D socket adapter
*       other socket adapters other than sm48d
*       DONE:
*       hardware pictures, cpu documentation, crystal, ram size
******************************************************************************
*       Links:
*       http://www3.bpmmicro.com/web/helpandsupport.nsf/69f301ee4e15195486256fcf0062c2eb/8194a48179484c9f862573220065d38e!OpenDocument
*       ftp://ftp.bpmmicro.com/Dnload/
******************************************************************************
*       Analog driver cards:
        The BP-1200, 1400 and 1600 have up to 6 of these cards in them.
        Each card can drive exactly 8 pins with analog (pwm-controlled?)
        voltages. The BP-1200 is probably usable with as few as one of these
        cards installed, but can only be used with 8-pin devices in that case!
******************************************************************************
*       Tech adapters for BP-1148 and BP-1200:
*       Note: Regardless of tech adapter, only up to 48 pins are drivable with
         analog (pseudo-dac-per-pin) voltages, the remainder are pulled high or
         low by the tech adapter.
*       TA-84: 84 pin tech adapter
         Rev C: Small board which doesn't cover the whole front of the BP-1200.
          no screen printing on the case, only identifiable by the pcb marking
          this PCB can be populated with either 48 or 84 relays; if the former,
          it is known as an STD48 pcb; the latter is presumably STD84 and may
          have an otherwise unpopulated PGA FPGA or ASIC on it as well.
         Rev E: Marked "CPCBTA84V", a larger board which covers the entire
          front of the BP-1200 including the LEDs, but has its own 3 LEDs on
          it (why not plastic light pipes?) controlled probably through the
          93c46 bus. This board again has either 48 relays on it, or 84 relays
          and an FPGA or ASIC on it.
*        TA-240: 240 pin tech adapter, this is a full sized shield which
          like the CPCBTA84V covers the entire front of the BP1200.
          It likely has even more relays in it, and it provides the same
          "three" connectors that the bp1400 and 1600 do natively, to allow
          for 240 pins to be driven. It almost certainly has an FPGA or ASIC
          on it as well, possibly several.
******************************************************************************
*       SM48D socket module:
*       The SM48D socket module has two DIN 41612/IEC 60603-2 sockets on the
          bottom, each of which has two rows of pins, with the middle "B" row
          unpopulated. It contains six 74HCT164 Serial-in Parallel-out shift
          registers, for 48 bits of serially drivable state, which is used to
          drive the gates of transistors which act as a pull-down to GND for
          each of the 48 pins.
          There is also a relay, of unknown purpose.
          There are several versions of the SM48D pcb which existed:
          Rev B uses all through-hole components, and extra passive resistor arrays for every pin (1992)
          Rev C uses all through-hole components (1992)
          Rev E "CPCBS48D" uses all surface mount/soic components (1998)
          For revision E, assuming IC1 is the upper leftmost IC, and IC7 is
          the lower rightmost ic, from left to right then top to bottom
          and ic4 being the 93c46;
          The 74hct164s are chained in this order and drive the following pins:
          IC3: TODO: ADD ME
          IC2: "
          IC7: "
          IC5: "
          IC6: "
          IC1: "

*       Looking "through" the pcb from the top, the connectors are arranged
          as such:
(note: J3 may be the wrong label, it could be J5)

           ___J3___                                             ___J4___
   GND -- |A32  C32|                                           |A32  C32| -- GND
   VCC -- |A31  C31|                                           |A31  C31| <> J3 A03
J4 C30 <> |A30  C30|                           ?Relay control? |A30  C30| <> J3 A30
   GND -- |A29  C29|                                           |A29  C29|
Pin 01 <> |A28  C28|                                           |A28  C28| <> Pin 48
Pin 02 <> |A27  C27|                                           |A27  C27| <> Pin 47
Pin 03 <> |A26  C26|                                           |A26  C26| <> Pin 46
Pin 04 <> |A25  C25|                                           |A25  C25| <> Pin 45
Pin 05 <> |A24  C24|                                           |A24  C24| <> Pin 44
Pin 06 <> |A23  C23| -- GND                             GND -- |A23  C23| <> Pin 43
Pin 07 <> |A22  C22|                                           |A22  C22| <> Pin 42
Pin 08 <> |A21  C21|                                           |A21  C21| <> Pin 41
Pin 09 <> |A20  C20|                                           |A20  C20| <> Pin 40
Pin 10 <> |A19  C19|                                           |A19  C19| <> Pin 39
Pin 11 <> |A18  C18|                                           |A18  C18| <> Pin 38
Pin 12 <> |A17  C17| -- GND                             GND -- |A17  C17| <> Pin 37
Pin 13 <> |A16  C16|                                           |A16  C16| <> Pin 36
Pin 14 <> |A15  C15|                                           |A15  C15| <> Pin 35
Pin 15 <> |A14  C14|                                           |A14  C14| <> Pin 34
Pin 16 <> |A13  C13|                                           |A13  C13| <> Pin 33
Pin 17 <> |A12  C12|                                           |A12  C12| <> Pin 32
Pin 18 <> |A11  C11| -- GND                             GND -- |A11  C11| <> Pin 31
Pin 19 <> |A10  C10|                                           |A10  C10| <> Pin 30
Pin 20 <> |A09  C09|                                           |A09  C09| <> Pin 29
Pin 21 <> |A08  C08|                                           |A08  C08| <> Pin 28
Pin 22 <> |A07  C07|                                     ?? <> |A07  C07| <> Pin 27
Pin 23 <> |A06  C06|                                           |A06  C06| <> Pin 26
Pin 24 <> |A05  C05|                                           |A05  C05| <> Pin 25
          |A04  C04|                               93c46 CS -> |A04  C04| -- VCC
J4 C31 <> |A03  C03|                              93c46 CLK -> |A03  C03|
J4 C02 <> |A02  C02| <- '164 CP   93c46 DI AND '164 IC3 DSB -> |A02  C02| <> J3 A02
   GND -- |A01  C01| <- '164 /MR                   93c46 DO <- |A01  C01| -- GND
          ----------                                           ----------

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

/* Core includes */
#include "emu.h"
#include "cpu/i86/i286.h"
#include "machine/eepromser.h"


namespace {

class bpmmicro_state : public driver_device
{
public:
	bpmmicro_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_eeprom_u38(*this, "eeprom_u38")
	{
	}

	void init_bp1200();
	void unknown_82200_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t latch_84000_r(offs_t offset, uint16_t mem_mask = ~0);
	void latch_84002_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	void unknown_8400e_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	void unknown_84018_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	void unknown_8401a_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	void eeprom_8401c_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	virtual void machine_start() override ATTR_COLD;
	void bpmmicro(machine_config &config);
	void i286_io(address_map &map) ATTR_COLD;
	void i286_mem(address_map &map) ATTR_COLD;
private:
	required_device<cpu_device> m_maincpu;
	required_device<eeprom_serial_93cxx_device> m_eeprom_u38;
	uint16_t m_shifter = 0;
	uint16_t m_latch = 0;
};

/******************************************************************************
 Driver Init
******************************************************************************/

void bpmmicro_state::init_bp1200()
{
	m_shifter = 0;
	m_latch = 0;
}

/******************************************************************************
 Machine Start/Reset
******************************************************************************/

void bpmmicro_state::machine_start()
{
	save_item(NAME(m_shifter));
	save_item(NAME(m_latch));
}

/******************************************************************************
 Read/Write handlers
******************************************************************************/

void bpmmicro_state::unknown_82200_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: unknown write to 0x82200 offset %08x mask (%08x) data %08x\n",machine().describe_context(),offset<<1,mem_mask,data);
}

uint16_t bpmmicro_state::latch_84000_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t returnval = m_latch; // not sure this is correct, it could be that the 93c48 DO pin is connected directly to bit 7 here...
	logerror("%08x:Read 0x84000 octal latch %08x (%08x), got %08x\n", machine().describe_context(), offset << 1, mem_mask, returnval);
	return returnval;
}

void bpmmicro_state::latch_84002_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: write to 0x84002 octal latch clock? %08x mask (%08x) data %08x\n",machine().describe_context(),offset<<1,mem_mask,data);
	if (data) m_latch = m_shifter;
}

void bpmmicro_state::unknown_8400e_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: unknown write to 0x8400e offset %08x mask (%08x) data %08x\n",machine().describe_context(),offset<<1,mem_mask,data);
}

void bpmmicro_state::unknown_84018_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: unknown write to 0x84018 offset %08x mask (%08x) data %08x\n",machine().describe_context(),offset<<1,mem_mask,data);
	if (data&1) // HACK
	{
		m_eeprom_u38->cs_write(CLEAR_LINE);
		m_eeprom_u38->cs_write(ASSERT_LINE);
	}
}

void bpmmicro_state::unknown_8401a_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: unknown write to 0x8401a offset %08x mask (%08x) data %08x\n",machine().describe_context(),offset<<1,mem_mask,data);
}

void bpmmicro_state::eeprom_8401c_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s:write eeprom %08x (%08x) %08x\n",machine().describe_context(),offset<<1,mem_mask,data);
	if (ACCESSING_BITS_0_7)
	{
		m_eeprom_u38->di_write(BIT(data, 0));
		m_eeprom_u38->clk_write(BIT(data, 1) ? ASSERT_LINE : CLEAR_LINE);
		if (BIT(data,1)) // is this correct at all?
		{
			m_shifter <<= 1;
			m_shifter |= m_eeprom_u38->do_read();
		}
		/* bits 2 thru 7 here also do something;
		There is a 74HCT14 hex inverter schmitt trigger at u26
		and it is possible these 6 bits feed the inputs of that chip.
		These MIGHT be the CS lines for the 6 pin driver cards!
		 */
	}
}


/* todo (v1.24 rom)
from boot (2 digit hex offsets have 0x84000 added to them):
0x0a00 -> 0x82200
0x0001 -> 18
0x0003 -> 1a
0x0001 -> 0e
{lots of data banged to 1c}
0x0001 -> 02
0x0000 -> 02
read <- 00

0x0001 -> 18
0x0002 -> 1a
{lots of data banged to 1c}
0x0001 -> 02
0x0000 -> 02

0x0000 -> 18
0x0003 -> 1a
{lots of data banged to 1c}
0x0001 -> 02
0x0000 -> 02
read <- 00
read <- 00

0x0000 -> 18
0x0001 -> 1a
{lots of data banged to 1c}
0x0001 -> 02
0x0000 -> 02

0x0000 -> 0e
0x0001 -> 0e
0x0000 -> 0e
0x0001 -> 0e
0x0000 -> 0e
0x0001 -> 0e
read <- 00

0x0a00 -> 0x82200
0x0112 -> 0x82200

0x0000 -> 06
0x0000 -> 04
0x0000 -> 00

*/


/******************************************************************************
 Address Maps
******************************************************************************/

void bpmmicro_state::i286_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x07ffff).ram(); // 512k ram
	map(0x082200, 0x82201).w(FUNC(bpmmicro_state::unknown_82200_w));
	map(0x084000, 0x84001).r(FUNC(bpmmicro_state::latch_84000_r)); // GUESS: this is reading the octal latch
	map(0x084002, 0x84003).w(FUNC(bpmmicro_state::latch_84002_w)); // GUESS: this is clocking the CK pin on the octal latch from bit 0, dumping the contents of a serial in parallel out shifter into said latch
	map(0x08400e, 0x8400f).w(FUNC(bpmmicro_state::unknown_8400e_w));
	map(0x084018, 0x84019).w(FUNC(bpmmicro_state::unknown_84018_w));
	map(0x08401a, 0x8401b).w(FUNC(bpmmicro_state::unknown_8401a_w));
	map(0x08401c, 0x8401d).w(FUNC(bpmmicro_state::eeprom_8401c_w));
	map(0x0f0000, 0x0fffff).rom().region("bios", 0x10000);
	//map(0xfe0000, 0xffffff).rom().region("bios", 0); //?
	map(0xfffff0, 0xffffff).rom().region("bios", 0x1fff0); //?
}

void bpmmicro_state::i286_io(address_map &map)
{
	map.unmap_value_high();
}


/******************************************************************************
 Input Ports
******************************************************************************/
static INPUT_PORTS_START( bpmmicro )
INPUT_PORTS_END

/******************************************************************************
 Machine Drivers
******************************************************************************/

void bpmmicro_state::bpmmicro(machine_config &config)
{
	/* basic machine hardware */
	I80286(config, m_maincpu, XTAL(32'000'000)/4); /* divider is guessed, cpu is an AMD N80L286-16/S part */
	m_maincpu->set_addrmap(AS_PROGRAM, &bpmmicro_state::i286_mem);
	m_maincpu->set_addrmap(AS_IO, &bpmmicro_state::i286_io);

	EEPROM_93C46_16BIT(config, "eeprom_u38");
}



/******************************************************************************
 ROM Definitions
******************************************************************************/

ROM_START(bp1200)
	ROM_REGION16_LE(0x20000, "bios", 0)
	// note about roms: the BP-1200 has two jumpers controlling what type of rom is installed;
	// it needs 120ns or faster roms
	// the "W1" and "W2" labels are next to pins A on the pcb
	// for 2764 roms:
	// W1 [A B]C
	// W2 [A B]C
	// for 27256 roms:
	// W1 [A B]C
	// W2  A[B C]
	ROM_DEFAULT_BIOS("v124")
	ROM_SYSTEM_BIOS( 0, "v124", "BP-1200 V1.24")
	ROMX_LOAD("version_1.24_u8.st_m27c256b-12f1l.u8", 0x10001, 0x8000, CRC(86d46d76) SHA1(4733b03a28689a3d2c58278495fbf31d0c74ac01), ROM_SKIP(1) | ROM_BIOS(0)) // "bios1200.v124a.u8" on bpmmicro site
	ROMX_LOAD("version_1.24_u9.st_m27c256b-12f1l.u9", 0x10000, 0x8000, CRC(3bcc5c72) SHA1(3b281f2b464d8a4e366f8e2f0a8fa6dfd0a8f28c), ROM_SKIP(1) | ROM_BIOS(0)) // "bios1200.v124a.u9" on bpmmicro site
	ROM_SYSTEM_BIOS( 1, "v118", "BP-1200 V1.18")
	ROMX_LOAD("version_1.18_u8_2000.am27c256.u8", 0x10001, 0x8000, CRC(f8afa614) SHA1(a372bc35aea30595ab8f05c5e641021b45043ed3), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("version_1.18_u9_2000.am27c256.u9", 0x10000, 0x8000, CRC(049b2ad1) SHA1(c9405ff805f3814493ad007bae7a8cb6a12aeb32), ROM_SKIP(1) | ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "v113", "BP-1200 V1.13")
	ROMX_LOAD("bp-1200_v1.13_u8_1992_1997.at27c256r-12pc.u8", 0x10001, 0x8000, CRC(ec61dcad) SHA1(dbfee285456d24b93c1fa6e8557b13ab80c3c877), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD("bp-1200_v1.13_u9_1992_1995.at27c256r-12pc.u9", 0x10000, 0x8000, CRC(91ca5e70) SHA1(4a8c1894a67dfd5e0db088519a3ee4edaafdef58), ROM_SKIP(1) | ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "v111", "BP-1200 V1.11")
	ROMX_LOAD("bp-1200_version_1.11_u8_1992_1995.at27c256r-12pc.u8", 0x10001, 0x8000, CRC(d1c051e4) SHA1(b27007a931b0662b3dc7e2d41c6ec5ed0cd49308), ROM_SKIP(1) | ROM_BIOS(3))
	ROMX_LOAD("bp-1200_version_1.11_u9_1992_1995.at27c256r-12pc.u9", 0x10000, 0x8000, CRC(99d46ba1) SHA1(144dbe6ed989ea88cfc1f6d1142508bb92519f87), ROM_SKIP(1) | ROM_BIOS(3))
	ROM_SYSTEM_BIOS( 4, "v105", "BP-1200 V1.05")
	ROMX_LOAD("bp-1200_1.05_u8__=c=_1993_rev_c__bp_microsystems.27c64-12pc.u8", 0x1c001, 0x2000, CRC(2c13a43c) SHA1(5dd67a09f72f693c085160b9beedd2454ba4ec37), ROM_SKIP(1) | ROM_BIOS(4)) // "BP-1200 1.05 U8 // (C) 1993 REV C // BP Microsystems" 27C64-12PC @U8
	ROMX_LOAD("bp-1200_1.05_u9__=c=_1993_rev_c__bp_microsystems.27c64-12pc.u9", 0x1c000, 0x2000, CRC(b88a311c) SHA1(fb5e0543c811cbbf8f24d1de204b4c0c1bd2f485), ROM_SKIP(1) | ROM_BIOS(4)) // "BP-1200 1.05 U9 // (C) 1993 REV C // BP Microsystems" 27C64-12PC @U9
	ROM_SYSTEM_BIOS( 5, "v104", "BP-1200 V1.04")
	ROMX_LOAD("bp-1200_1.04_u8__=c=_1992_rev_c__bp_microsystems.27c64.u8", 0x1c001, 0x2000, CRC(2ab47324) SHA1(052e578dae5db023f94b35d686a5352ffceec414), ROM_SKIP(1) | ROM_BIOS(5)) // "BP-1200 1.04 U8 // (C) 1992 REV C // BP Microsystems" OTP 27C64 @ U8
	ROMX_LOAD("bp-1200_1.04_u9__=c=_1993_rev_c__bp_microsystems.27c64.u9", 0x1c000, 0x2000, CRC(17b94d7a) SHA1(7ceed660dbdc638ac86ca8ba7fa456c297d88766), ROM_SKIP(1) | ROM_BIOS(5)) // "BP-1200 1.04 U9 // (C) 1993 REV C // BP Microsystems" OTP 27C64 @ U9
	ROM_SYSTEM_BIOS( 6, "v103", "BP-1200 V1.03")
	ROMX_LOAD("bp-1200_1.03_u8__=c=_1992_rev_c__bp_microsystems.27c64a-12.u8", 0x1c001, 0x2000, CRC(b01968b6) SHA1(d0c6aa0f0fe47b0915658e8c27286ab6ea90972e), ROM_SKIP(1) | ROM_BIOS(6)) // "BP-1200 1.03 U8 // (C) 1992 REV C // BP Microsystems" 27C64A-12 @ U8
	ROMX_LOAD("bp-1200_1.03_u9__=c=_1992_rev_c__bp_microsystems.27c64a-12.u9", 0x1c000, 0x2000, CRC(f58ffebb) SHA1(700d3ffed269fff6dc1cf2190bde8b989715c22a), ROM_SKIP(1) | ROM_BIOS(6)) // "BP-1200 1.03 U9 // (C) 1992 REV C // BP Microsystems" 27C64A-12 @ U9
ROM_END

} // anonymous namespace


/******************************************************************************
 Drivers
******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT         COMPANY            FULLNAME   FLAGS
COMP( 1992, bp1200, 0,      0,      bpmmicro, bpmmicro, bpmmicro_state, init_bp1200, "BP Microsystems", "BP-1200", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



br8641.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************

        Brandt 8641

        Currency Counter

        2012-12-24 Skeleton driver. [Micko]
        2015-09-27 Added devices & inputs based entirely on guesswork [Robbbert]

        There seems to be 15 buttons (according to images, I just have board)
        also there are 8 dips currently set at 00011100 (1 is on)

        Looks like a LCD display? I think ports 8 & 9 write to it. Also looks
        like the display should say 8641 when machine is turned on.

ToDo:
- Need manuals, schematics, etc.
- Artwork
- Everything

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

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/z80daisy.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"
#include "sound/beep.h"
#include "speaker.h"


namespace {

class brandt8641_state : public driver_device
{
public:
	brandt8641_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pio1(*this, "pio1")
		, m_pio2(*this, "pio2")
		, m_pio3(*this, "pio3")
		, m_ctc(*this, "ctc")
		, m_sio(*this, "sio")
		, m_io_keyboard(*this, "KEY.%u", 0U)
		, m_beep(*this, "beeper")
	{ }

	void brandt8641(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	u8 port08_r();
	void port08_w(u8 data);
	void port09_w(u8 data);
	u8 m_port08 = 0U;
	u8 m_port09 = 0U;
	required_device<z80_device> m_maincpu;
	required_device<z80pio_device> m_pio1;
	required_device<z80pio_device> m_pio2;
	required_device<z80pio_device> m_pio3;
	required_device<z80ctc_device> m_ctc;
	required_device<z80sio_device> m_sio;
	required_ioport_array<8> m_io_keyboard;
	required_device<beep_device> m_beep;
};

void brandt8641_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).rom(); // 27256 at U12
	map(0x8000, 0x9fff).ram(); // 8KB static ram 6264 at U12
}

void brandt8641_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x04, 0x07).rw(m_pio1, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x08, 0x0b).rw(m_pio2, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x0c, 0x0f).rw(m_pio3, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x10, 0x13).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x1c, 0x1f).rw(m_sio, FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
}

/* Input ports */
// No idea what each key does
static INPUT_PORTS_START( brandt8641 )
	PORT_START("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)

	PORT_START("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)

	PORT_START("KEY.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)

	PORT_START("KEY.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)

	PORT_START("KEY.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)

	PORT_START("KEY.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)

	PORT_START("KEY.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)

	PORT_START("KEY.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)
INPUT_PORTS_END

u8 brandt8641_state::port08_r()
{
	u8 i, data = 7;

	for (i = 0; i < 8; i++)
		if (BIT(m_port09, i))
			data &= m_io_keyboard[i]->read();

	return data | m_port08;
}

void brandt8641_state::port08_w(u8 data)
{
	m_port08 = data & 0xf8;
	m_beep->set_state(BIT(data, 4));
}

void brandt8641_state::port09_w(u8 data)
{
	m_port09 = data ^ 0xff;
}

void brandt8641_state::machine_start()
{
	save_item(NAME(m_port08));
	save_item(NAME(m_port09));
}

static const z80_daisy_config daisy_chain_intf[] =
{
	{ "pio1" },
	{ "pio2" },
	{ "pio3" },
	{ "ctc" },
	{ "sio" },
	{ nullptr }
};



void brandt8641_state::brandt8641(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'000'000)); // U4 ,4MHz crystal on board
	m_maincpu->set_addrmap(AS_PROGRAM, &brandt8641_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &brandt8641_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain_intf);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 2000).add_route(ALL_OUTPUTS, "mono", 0.50);

	// Z80APIO U9
	// Z80APIO U14
	// Z80PIO U7 - unknown which is which
	Z80PIO(config, m_pio1, XTAL(4'000'000));
	m_pio1->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80PIO(config, m_pio2, XTAL(4'000'000));
	m_pio2->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio2->in_pa_callback().set(FUNC(brandt8641_state::port08_r));
	m_pio2->out_pa_callback().set(FUNC(brandt8641_state::port08_w));
	m_pio2->out_pb_callback().set(FUNC(brandt8641_state::port09_w));

	Z80PIO(config, m_pio3, XTAL(4'000'000));
	m_pio3->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80CTC(config, m_ctc, XTAL(4'000'000)); // Z80CTC U8
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80SIO(config, m_sio, XTAL(4'000'000)); // unknown type
	m_sio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
}

/* ROM definition */
ROM_START( br8641 )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "v0911he.u11", 0x0000, 0x8000, CRC(59a16951) SHA1(893dba60ec8bfa391fb2d2a30db5d42d601f5eb9))
ROM_END

} // anonymous namespace


/* Driver */
COMP( 1986, br8641, 0, 0, brandt8641, brandt8641, brandt8641_state, empty_init, "Brandt", "Brandt 8641", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



braiplus.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for BraiLab Plus talking computer.

    (from a document translated from Hungarian)
In addition to the Z80 microprocessor on the two-sided printed circuit board,
there are 256 Kbytes of dynamic RAM, 16 Kbytes of EPROM, 2 Z80PIOs, 4 Kbytes
of static RAM, a character generator EPROM, a WD2793 floppy disk controller,
a I8251 PUSART and a MEA-8000 speech synthesizer.

180 Kbytes of 256 Kbytes of RAM is handled as a fixed RAM disk under the
CP/M operating system. Memory management supports the use of a dynamic RAM
fixed disk, provides the appropriate address space for the operating system,
and flips between them and between EPROMs. The two Z80PIOs perform centronics
interface, system clock interrupt handling, and serial programming of the
baud rate with serial interface I8251 and HD-4702.

The increased size of 4 Kbytes memory enabled 25x80 character screen.
Floppy disk holds 792kb (formatted).

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

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/clock.h"
#include "machine/f4702.h"
#include "machine/i8251.h"
#include "machine/wd_fdc.h"
#include "machine/z80pio.h"


namespace {

class braiplus_state : public driver_device
{
public:
	braiplus_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pio(*this, "pio%u", 1U)
		, m_keyboard(*this, "K%u", 0U)
		, m_baud_rate(0)
	{
	}

	void braiplus(machine_config &config);

private:
	void baud_rate_w(u8 data);
	u8 baud_rate_r();
	void bank_w(u8 data);
	void unknown_w(u8 data);
	u8 keyboard_r(offs_t offset);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_device_array<z80pio_device, 2> m_pio;
	required_ioport_array<10> m_keyboard;

	u8 m_baud_rate;
};


void braiplus_state::baud_rate_w(u8 data)
{
	m_baud_rate = data >> 4;
}

u8 braiplus_state::baud_rate_r()
{
	return m_baud_rate;
}

void braiplus_state::bank_w(u8 data)
{
	// TODO: bankswitching
}

void braiplus_state::unknown_w(u8 data)
{
	// TODO: unknown (only high nibble used)
}

u8 braiplus_state::keyboard_r(offs_t offset)
{
	u8 ret = offset < 0x14 ? m_keyboard[offset >> 1]->read() : 0xff;
	if (BIT(offset, 0))
		ret >>= 4;
	else
		ret &= 0x0f;

	// TODO: high nibble appears to be used by an optional PC/XT keyboard adapter
	return ret | 0xf0;
}

void braiplus_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).rom().region("maincpu", 0);
	map(0x8000, 0x801f).r(FUNC(braiplus_state::keyboard_r));
	map(0xa000, 0xbfff).ram();
	map(0xf000, 0xffff).ram();
}

void braiplus_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x04, 0x07).rw(m_pio[0], FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x08, 0x0b).rw(m_pio[1], FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x0c, 0x0d).rw("usart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x10, 0x13).rw("fdc", FUNC(wd2793_device::read), FUNC(wd2793_device::write));
	map(0x14, 0x14).w(FUNC(braiplus_state::bank_w));
	map(0x18, 0x18).w(FUNC(braiplus_state::unknown_w));
}


static INPUT_PORTS_START(braiplus)
	PORT_START("K0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_SHIFT_1) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left Ctrl") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right Ctrl") PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key A0")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key A1")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key A2")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key A3")

	PORT_START("K1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR('"') PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR('$') PORT_CODE(KEYCODE_4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR('&') PORT_CODE(KEYCODE_6)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR('\'') PORT_CODE(KEYCODE_7)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('8') PORT_CHAR('(') PORT_CODE(KEYCODE_8)

	PORT_START("K2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('q') PORT_CHAR('Q') PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('w') PORT_CHAR('W') PORT_CODE(KEYCODE_W)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('e') PORT_CHAR('E') PORT_CODE(KEYCODE_E)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('r') PORT_CHAR('R') PORT_CODE(KEYCODE_R)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('t') PORT_CHAR('T') PORT_CODE(KEYCODE_T)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('y') PORT_CHAR('Y') PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('u') PORT_CHAR('U') PORT_CODE(KEYCODE_U)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('i') PORT_CHAR('I') PORT_CODE(KEYCODE_I)

	PORT_START("K3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('a') PORT_CHAR('A') PORT_CODE(KEYCODE_A)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('s') PORT_CHAR('S') PORT_CODE(KEYCODE_S)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('d') PORT_CHAR('D') PORT_CODE(KEYCODE_D)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('f') PORT_CHAR('F') PORT_CODE(KEYCODE_F)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('g') PORT_CHAR('G') PORT_CODE(KEYCODE_G)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('h') PORT_CHAR('H') PORT_CODE(KEYCODE_H)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('j') PORT_CHAR('J') PORT_CODE(KEYCODE_J)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('k') PORT_CHAR('K') PORT_CODE(KEYCODE_K)

	PORT_START("K4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('n') PORT_CHAR('N') PORT_CODE(KEYCODE_N)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('x') PORT_CHAR('X') PORT_CODE(KEYCODE_X)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('c') PORT_CHAR('C') PORT_CODE(KEYCODE_C)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('v') PORT_CHAR('V') PORT_CODE(KEYCODE_V)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('b') PORT_CHAR('B') PORT_CODE(KEYCODE_B)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('z') PORT_CHAR('Z') PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_M)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',') PORT_CHAR('<') PORT_CODE(KEYCODE_COMMA)

	PORT_START("K5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('p') PORT_CHAR('P') PORT_CODE(KEYCODE_P)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x00e9, 0x7b) PORT_CHAR(0x00c9, 0x5b) PORT_NAME(u8"É") PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x1b) PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x00e1, 0x60) PORT_CHAR(0x00c1, 0x40) PORT_NAME(u8"Á") PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0') PORT_CHAR('_') PORT_CODE(KEYCODE_0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('9') PORT_CHAR(')') PORT_CODE(KEYCODE_9)

	PORT_START("K6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(' ') PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('o') PORT_CHAR('O') PORT_CODE(KEYCODE_O)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('l') PORT_CHAR('L') PORT_CODE(KEYCODE_L)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('.') PORT_CHAR('>') PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('?') PORT_CHAR('/') PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('=') PORT_CHAR('-')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x0d) PORT_NAME("CR") PORT_CODE(KEYCODE_ENTER)

	PORT_START("K7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x00f3, 0x7e) PORT_CHAR(0x00d3, 0x5e) PORT_NAME(u8"Ó") PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x00f6, 0x7c) PORT_CHAR(0x00d6, 0x5c) PORT_NAME(u8"Ö") PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 5F")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x00fc, 0x7d) PORT_CHAR(0x00dc, 0x5d) PORT_NAME(u8"Ü") PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 05")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 08")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 18")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 04")

	PORT_START("K8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key E0")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key E1")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key E2")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key E3")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key E4")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key E5")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key E6")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key E7")

	PORT_START("K9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key E8")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key E9")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key EA")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key EB")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key EC")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key ED")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key EE")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key EF")
INPUT_PORTS_END

static const z80_daisy_config daisy_chain[] =
{
	{ "pio1" },
	{ "pio2" },
	{ nullptr }
};

void braiplus_state::braiplus(machine_config &config)
{
	Z80(config, m_maincpu, 4'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &braiplus_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &braiplus_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	Z80PIO(config, m_pio[0], 4'000'000);
	m_pio[0]->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80PIO(config, m_pio[1], 4'000'000);
	m_pio[1]->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio[1]->out_pa_callback().set(FUNC(braiplus_state::baud_rate_w));

	f4702_device &brg(F4702(config, "brg", 2.4576_MHz_XTAL));
	brg.s_callback().set(FUNC(braiplus_state::baud_rate_r));
	brg.z_callback().set("usart", FUNC(i8251_device::write_txc));
	brg.z_callback().append("usart", FUNC(i8251_device::write_rxc));

	i8251_device &usart(I8251(config, "usart", 2'000'000));
	usart.rxrdy_handler().set(m_pio[1], FUNC(z80pio_device::pa3_w));

	WD2793(config, "fdc", 2'000'000);

	// HACK: what should be driving this?
	CLOCK(config, "clock_hack", 100).signal_handler().set(m_pio[0], FUNC(z80pio_device::pa7_w));
}

ROM_START( braiplus )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "brailabplus.bin", 0x0000, 0x4000, CRC(521d6952) SHA1(f7405520d86fc7abd2dec51d1d016658472f6fe8) )

	ROM_REGION( 0x0800, "chargen", 0 ) // no idea what chargen it uses, using the one from homelab4 for now
	ROM_LOAD( "hl4.chr",   0x0000, 0x0800, BAD_DUMP CRC(f58ee39b) SHA1(49399c42d60a11b218a225856da86a9f3975a78a))
ROM_END

} // anonymous namespace


COMP( 1988, braiplus, 0, 0, braiplus, braiplus, braiplus_state, empty_init, "Jozsef and Endre Lukacs", "BraiLab Plus", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



bridgeb.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Fidelity Bridge Bidder (BB / 7004)

It's related to the Bridge Challenger series, but has no card reader, nor does
it play Bridge. It's more like a (cheat) tool for helping with bidding.

The 101-64109 ROM is identical to the one in Advanced Bridge Challenger.

Hardware notes:
- PCB label: main PCB: 510-1012, display PCB: 510-1013
- Zilog Z80-CPU-PS @ ~1MHz (R/C clock, no XTAL)
- Zilog Z80 PIO (custom label)
- 16KB ROM (2*NEC 2364C), 1KB RAM (2*TC5514P)
- 3*Litronix DL1414, 4 more leds, piezo

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

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/z80pio.h"
#include "sound/dac.h"
#include "video/dl1416.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "fidel_bridgeb.lh"


namespace {

class bridgeb_state : public driver_device
{
public:
	bridgeb_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_z80pio(*this, "z80pio"),
		m_dl1414(*this, "dl1414_%u", 0),
		m_led_pwm(*this, "led_pwm"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_digits(*this, "digit%u", 0U)
	{ }

	void bridgeb(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(reset_button);
	DECLARE_INPUT_CHANGED_MEMBER(input_changed) { update_pa(); }

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;
	required_device<z80pio_device> m_z80pio;
	required_device_array<dl1414_device, 3> m_dl1414;
	required_device<pwm_display_device> m_led_pwm;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<8> m_inputs;
	output_finder<12> m_digits;

	u8 m_inp_mux = 0;

	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	template <int N> void update_digits(offs_t offset, u16 data);

	void digit_w(offs_t offset, u8 data);
	void control_w(u8 data);
	u8 input_r();
	void input_w(u8 data);
	void update_pa();
};

void bridgeb_state::machine_start()
{
	m_digits.resolve();

	// register for savestates
	save_item(NAME(m_inp_mux));
}

INPUT_CHANGED_MEMBER(bridgeb_state::reset_button)
{
	// reset button is directly wired to Z80 RESET pin
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}



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

// DL1414

template <int N>
void bridgeb_state::update_digits(offs_t offset, u16 data)
{
	m_digits[N * 4 + offset] = data;
}

void bridgeb_state::digit_w(offs_t offset, u8 data)
{
	// write to one DL1414 digit
	m_dl1414[(offset >> 13) % 3]->bus_w(offset >> 11 & 3, data);
}


// Z80 PIO

void bridgeb_state::control_w(u8 data)
{
	// A4,A5: led data
	// A6: N/C
	m_led_pwm->matrix(1, 1 << (data >> 4 & 3));

	// A7: speaker out
	m_dac->write(BIT(data, 7));
}

u8 bridgeb_state::input_r()
{
	u8 data = 0;

	// A0-A3: multiplexed inputs
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read() & 0xf;

	return ~data;
}

void bridgeb_state::update_pa()
{
	// push inputs to port A (can trigger IRQ)
	m_z80pio->port_a_write(input_r());
}

void bridgeb_state::input_w(u8 data)
{
	// B0-B7: input mux
	m_inp_mux = ~data;
	update_pa();
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void bridgeb_state::main_map(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	map(0x6000, 0x63ff).mirror(0x1c00).ram();
	map(0xa000, 0xffff).w(FUNC(bridgeb_state::digit_w));
}

void bridgeb_state::main_io(address_map &map)
{
	map.global_mask(0x03);
	map(0x00, 0x03).rw(m_z80pio, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

#define PORT_CHANGED_CB(x) \
	PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bridgeb_state::x), 0)

static INPUT_PORTS_START( bridgeb )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_I) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("A")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("10")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_U) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("K")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_Y) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME("Q")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_SLASH) PORT_NAME("P")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_T) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME("J")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_STOP) PORT_NAME("NT")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("EN")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_F) PORT_NAME("HD")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_V) PORT_NAME("IN")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_COMMA) PORT_NAME("Spades")

	PORT_START("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CL")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_D) PORT_NAME("DB")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_C) PORT_NAME("VL")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_M) PORT_NAME("Hearts")

	PORT_START("IN.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_W) PORT_CODE(KEYCODE_SPACE) PORT_NAME("Speaker")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_S) PORT_NAME("PR")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_X) PORT_NAME("CV")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_N) PORT_NAME("Diamonds")

	PORT_START("IN.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_A) PORT_NAME("BR")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_Z) PORT_NAME("DL")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(input_changed) PORT_CODE(KEYCODE_B) PORT_NAME("Clubs")

	PORT_START("RESET") // is not on matrix IN.7 d0
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_CB(reset_button) PORT_CODE(KEYCODE_Q) PORT_NAME("RE")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

static const z80_daisy_config daisy_chain[] =
{
	{ "z80pio" },
	{ nullptr }
};

void bridgeb_state::bridgeb(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 1'000'000); // R/C clock, appromixation
	m_maincpu->set_addrmap(AS_PROGRAM, &bridgeb_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &bridgeb_state::main_io);
	m_maincpu->set_daisy_config(daisy_chain);

	Z80PIO(config, m_z80pio, 1'000'000);
	m_z80pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_z80pio->out_pa_callback().set(FUNC(bridgeb_state::control_w));
	m_z80pio->in_pa_callback().set(FUNC(bridgeb_state::input_r));
	m_z80pio->out_pb_callback().set(FUNC(bridgeb_state::input_w));

	// video hardware
	DL1414T(config, m_dl1414[0], 0U).update().set(FUNC(bridgeb_state::update_digits<0>));
	DL1414T(config, m_dl1414[1], 0U).update().set(FUNC(bridgeb_state::update_digits<1>));
	DL1414T(config, m_dl1414[2], 0U).update().set(FUNC(bridgeb_state::update_digits<2>));

	PWM_DISPLAY(config, m_led_pwm).set_size(1, 4);
	config.set_default_layout(layout_fidel_bridgeb);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( bridgeb )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101-64111", 0x0000, 0x2000, CRC(903d0c3c) SHA1(8360bc21acfd0115730242fa1aa62356f289a88d) ) // NEC 2364C 218
	ROM_LOAD("101-64109", 0x2000, 0x2000, CRC(320afa0f) SHA1(90edfe0ac19b108d232cda376b03a3a24befad4c) ) // NEC 2364C 211
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1980, bridgeb, 0,      0,      bridgeb, bridgeb, bridgeb_state, empty_init, "Fidelity Electronics", "Bridge Bidder", MACHINE_SUPPORTS_SAVE )



brikett.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Hegener + Glaser Mephisto (I)/1X/II/ESB II/III/Junior
The base device is nicknamed the "Brikett"

Mephisto is the 1st chess computer by H+G, chess engine by Thomas Nitsche & Elmar Henne.
Initially, it didn't support an electronic chessboard. H+G released a dedicated ESB II
in 1981, and in 1982 they released ESB 3000 and ESB 6000 for use with ESB II/III programs.

Hardware notes:

1st model (Mephisto, early Mephisto II):
- 2 PCBs: DH 4001-C(computer), DH 4002-C(LCD/keypad)
- CDP1802CE @ 3.579MHz
- CDP1852CE (I/O port chip), external port
- 8*MWS5101L3 (256x4 RAM)
- 4-digit 7seg LCD
- piezo, TTL, 16-button keypad
- module slot

2nd model (later Mephisto II): (listed differences)
- PCB label: DH 4005-101 00
- CDP1802ACE @ 3.579MHz or 4.194MHz (chess clock runs faster on 4.194MHz)
- 2*MWS5114E or 2*TC5514P (1KBx4 RAM)
- optional bridge at _EF4 to GND for English piece notation (this is enabled
  in the export version of Mephisto ESB II, as shown in the English manual)

3rd model (later Mephisto II, Mephisto III): (listed differences)
- PCB label: DH 4005-101 01
- CDP1802ACE @ 6.144MHz, later serials also seen with 1806 CPU
- 2 XTALs (3.579MHz and 6.144MHz), lower speed when running on battery
- 2*TC5514P may be unpopulated on some Mephisto III

Mephisto Junior: (listed differences)
- 2 PCBs: RAWE 003010 B(computer), RAWE 003010 A(LCD/keypad)
  (RAWE = Rawe Datentechnik GmbH)
- CDP1802ACE @ 4.194MHz
- 1KB RAM (2*MWS5114E1), 8KB ROM (SMM2365C)
- no module slot, I/O chip, or external port

Mephisto program module:
- PCB label: DF 4003-B or DH 4005 20100
- 6*CDP1833CE (1KB ROM)

Mephisto II/ESB II program module:
- PCB label: HG 4005 02 301 00
- 3*TC5334P (4KB ROM), 4*M2532A on ESB II
- 2*TC5514P (1KBx4 RAM)
- 2*CDP1859CE (4bit latch)

Mephisto 1X program module:
- PCB label: DH 4005 02 301 00
- rest is same as Mephisto II, but ROM chips are CM3200-2

Mephisto III program module:
- PCB label: HG 4005 02 401 00
- 2*HN4827128G (16KB EPROM), also seen with HN613256P G81 (32KB ROM)
- 2*CDM6116E1 (2KB RAM)
- DM74LS373N (latch)
- HCF4556BE (chip select?)

ESB II board interface (via module slot):
- PCB label: DH 5000 00 111 01
- CD4001, 3*74373, MDP1603-331G (resistor array)

ESB 6000 board interface (via external port):
- PCB label: DH 5000 00 111 12
- TC4081, TC4082, TC4017, 74373, 74374

ESB II/6000 chessboard:
- 128 reed switches (magnet sensors, 2 per square)
- 64 leds + power led

ESB 3000 hardware is probably same as ESB 6000.
There are no other known external port peripherals.

The Brikett (sans main PCB) was also used in the 1983 Mephisto Excalibur, but
the hardware is completely different, based on a 68000 (see excalibur.cpp).

BTANB:
- bad bug in mephistoj opening library: e4 e6 / d4 d5 / Nd2 c5 / exd5 Qd1xd5,
  in other words: computer makes an illegal move with the WHITE queen

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

#include "emu.h"

#include "mmdisplay1.h"

#include "cpu/cosmac/cosmac.h"
#include "machine/cdp1852.h"
#include "machine/clock.h"
#include "machine/sensorboard.h"
#include "sound/spkrdev.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "mephisto_1.lh"
#include "mephisto_esb2.lh"
#include "mephisto_3.lh"
#include "mephisto_junior.lh"


namespace {

class brikett_state : public driver_device
{
public:
	brikett_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_irq_clock(*this, "irq_clock"),
		m_extport(*this, "extport"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_led_pwm(*this, "led_pwm"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(reset_button) { if (newval) machine_reset(); }
	DECLARE_INPUT_CHANGED_MEMBER(change_cpu_freq);

	// machine configs
	void mephisto(machine_config &config);
	void mephistoj(machine_config &config);
	void mephisto2(machine_config &config);
	void mephistoe2(machine_config &config);
	void mephistoe2a(machine_config &config);
	void mephisto3(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD { m_reset = true; }

private:
	// devices/pointers
	required_device<cdp1802_device> m_maincpu;
	required_device<clock_device> m_irq_clock;
	optional_device<cdp1852_device> m_extport;
	optional_device<sensorboard_device> m_board;
	required_device<mephisto_display1_device> m_display;
	optional_device<pwm_display_device> m_led_pwm;
	required_device<speaker_sound_device> m_dac;
	optional_ioport_array<4+2> m_inputs;

	emu_timer *m_speaker_off;
	bool m_reset = false;
	u8 m_esb_led = 0;
	u8 m_esb_row = 0;
	u8 m_esb_select = 0;

	// address maps
	void mephisto_map(address_map &map) ATTR_COLD;
	void mephistoj_map(address_map &map) ATTR_COLD;
	void mephisto2_map(address_map &map) ATTR_COLD;
	void mephistoe2_map(address_map &map) ATTR_COLD;
	void mephistoe2a_map(address_map &map) ATTR_COLD;
	void mephisto3_map(address_map &map) ATTR_COLD;
	void mephisto_io(address_map &map) ATTR_COLD;
	void mephistoj_io(address_map &map) ATTR_COLD;

	// I/O handlers
	int clear_r();
	u8 input_r(offs_t offset);
	u8 sound_r();

	void esb2_w(offs_t offset, u8 data);
	u8 esb2_r(offs_t offset);
	void esb6_w(u8 data);
	int esb6_r();

	TIMER_CALLBACK_MEMBER(speaker_off) { m_dac->level_w(0); }
};



/*******************************************************************************
    Initialization
*******************************************************************************/

void brikett_state::machine_start()
{
	m_speaker_off = timer_alloc(FUNC(brikett_state::speaker_off), this);

	// register for savestates
	save_item(NAME(m_reset));
	save_item(NAME(m_esb_led));
	save_item(NAME(m_esb_row));
	save_item(NAME(m_esb_select));
}

INPUT_CHANGED_MEMBER(brikett_state::change_cpu_freq)
{
	newval = m_inputs[4]->read();

	if (newval & 8)
	{
		/*
		    3rd hardware model has 2 XTALs, it will increase CPU voltage (and speed)
		    when running on mains power, the 3.579545MHz XTAL is still used for IRQ.

		    Mephisto III could be fitted with a 12MHz XTAL instead of 6.144MHz and
		    a newer CDP1805CE CPU by Hobby Computer Centrale on request. (It is
		    unexpected that the 1805 accepts such a high overclock, but tests show
		    that it is indeed twice faster)
		*/
		static const XTAL freq[3] = { 3.579545_MHz_XTAL, 6.144_MHz_XTAL, 12_MHz_XTAL };
		m_maincpu->set_unscaled_clock(freq[(newval & 3) % 3]);
	}
	else
	{
		m_maincpu->set_unscaled_clock((newval & 4) ? 4.194304_MHz_XTAL : 3.579545_MHz_XTAL);
	}

	// also change interrupt frequency
	m_irq_clock->set_period(attotime::from_ticks(0x10000, (newval & 4) ? 4.194304_MHz_XTAL : 3.579545_MHz_XTAL));
}



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

// base hardware

int brikett_state::clear_r()
{
	// CLEAR low + WAIT high resets cpu
	int ret = (m_reset) ? 0 : 1;
	m_reset = false;
	return ret;
}

u8 brikett_state::sound_r()
{
	// port 1 read enables the speaker
	if (!machine().side_effects_disabled())
	{
		m_dac->level_w(1);
		m_speaker_off->adjust(attotime::from_ticks(8, m_maincpu->clock()));
	}

	return 0xff;
}

u8 brikett_state::input_r(offs_t offset)
{
	u8 data = 0;

	// a0-a3,d0-d3: read keypad
	for (int i = 0; i < 4; i++)
		if (BIT(~offset, i))
			data |= m_inputs[i]->read();

	return data;
}


// ESB II board

void brikett_state::esb2_w(offs_t offset, u8 data)
{
	// a0-a7,d0-d7: chessboard leds
	m_led_pwm->matrix(~offset, m_inputs[5].read_safe(0) ? data : 0);
}

u8 brikett_state::esb2_r(offs_t offset)
{
	u8 data = 0;

	// a0-a7,d0-d7: read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(~offset, i))
			data |= m_board->read_rank(i);

	return m_inputs[5].read_safe(0) ? ~data : 0;
}


// ESB 6000 board

void brikett_state::esb6_w(u8 data)
{
	// CDP1852 SR + DO0-DO7 goes to external port, to chessboard
	if (!m_inputs[5].read_safe(0))
	{
		// chessboard disabled
		m_led_pwm->clear();
		return;
	}

	// SR clocks TC4017
	// 4017 Q0: N/C
	// 4017 Q1 + d0-d7: 74374 to led data
	// 4017 Q2 + d0-d7: 74373 to row select
	// 4017 Q2-Q9: column select
	m_esb_select = (m_esb_select + 1) % 10;

	// DO0-DO7 ANDed together: 4017 reset
	if (data == 0xff)
		m_esb_select = 0;

	if (m_esb_select == 1)
		m_esb_led = data;

	if (m_esb_select == 2)
		m_esb_row = data;

	// update chessboard leds
	m_led_pwm->matrix(~m_esb_row, m_esb_led);
}

int brikett_state::esb6_r()
{
	// EF1: read chessboard square
	if (m_inputs[5].read_safe(0))
		return (m_board->read_file(m_esb_select - 2) & ~m_esb_row) ? 0 : 1;
	else
		return 0;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void brikett_state::mephisto_map(address_map &map)
{
	map(0x0000, 0x17ff).rom();
	map(0xf400, 0xf7ff).ram();
	map(0xfb00, 0xfb00).mirror(0x00ff).w(m_display, FUNC(mephisto_display1_device::data_w));
	map(0xfff0, 0xffff).r(FUNC(brikett_state::input_r));
}

void brikett_state::mephistoj_map(address_map &map)
{
	mephisto_map(map);
	map(0x0000, 0x1fff).rom();
}

void brikett_state::mephisto2_map(address_map &map)
{
	mephisto_map(map);
	map(0x0000, 0x2fff).rom();
	map(0xf000, 0xf3ff).ram();
}

void brikett_state::mephistoe2_map(address_map &map)
{
	mephisto2_map(map);
	map(0x3000, 0x3fff).rom();
}

void brikett_state::mephistoe2a_map(address_map &map)
{
	mephistoe2_map(map);
	map(0xbf00, 0xbfff).rw(FUNC(brikett_state::esb2_r), FUNC(brikett_state::esb2_w));
}

void brikett_state::mephisto3_map(address_map &map)
{
	mephisto_map(map);
	map(0x0000, 0x7fff).rom().nopw(); // dummy write with sound_r
	map(0x8000, 0x8fff).ram();
}

void brikett_state::mephisto_io(address_map &map)
{
	map(0x01, 0x01).r(FUNC(brikett_state::sound_r));
	map(0x02, 0x02).w(m_extport, FUNC(cdp1852_device::write));
}

void brikett_state::mephistoj_io(address_map &map)
{
	map(0x01, 0x01).r(FUNC(brikett_state::sound_r));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( mephisto )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_NAME("CL")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("A / 1")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("ENT")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("B / 2 / Pawn")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("STA") // enter move
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("C / 3 / Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("LEV")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("D / 4 / Bishop")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("LIST")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("E / 5 / Rook")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("SW / 9")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("F / 6 / Queen")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("WS / 0")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("G / 7 / King")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("REV")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("H / 8")

	PORT_START("IN.4") // 3rd model main PCB has 2 XTALs on PCB
	PORT_CONFNAME( 0x03, 0x01, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(brikett_state::change_cpu_freq), 0) PORT_CONDITION("IN.4", 0x0c, EQUALS, 0x08)
	PORT_CONFSETTING(    0x00, "3.579MHz (Battery)" )
	PORT_CONFSETTING(    0x01, "6.144MHz (Mains)" )
	PORT_CONFNAME( 0x0c, 0x00, "Base Hardware" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(brikett_state::change_cpu_freq), 0)
	PORT_CONFSETTING(    0x00, "1st Model (3.579MHz)" ) // or 2nd model with this XTAL
	PORT_CONFSETTING(    0x04, "2nd Model (4.194MHz)" )
	PORT_CONFSETTING(    0x08, "3rd Model (2 XTALs)" )

	PORT_START("PIECE")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("RES") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(brikett_state::reset_button), 0)
INPUT_PORTS_END

static INPUT_PORTS_START( mephisto2 )
	PORT_INCLUDE( mephisto )

	PORT_MODIFY("PIECE")
	PORT_CONFNAME( 0x01, 0x00, "Piece Notation" )
	PORT_CONFSETTING(    0x01, DEF_STR( English ) ) // KQRBNP
	PORT_CONFSETTING(    0x00, DEF_STR( German ) ) // KDTLSB
INPUT_PORTS_END

static INPUT_PORTS_START( mephistoj )
	PORT_INCLUDE( mephisto2 )

	PORT_MODIFY("IN.4") // 1 XTAL
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_MODIFY("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( mephistoe2 )
	PORT_INCLUDE( mephisto2 )

	PORT_START("IN.5") // optional
	PORT_CONFNAME( 0x01, 0x01, "ESB 6000 Board" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( mephistoe2a )
	PORT_INCLUDE( mephistoe2 )

	PORT_MODIFY("IN.5") // optional
	PORT_CONFNAME( 0x01, 0x01, "ESB II Board" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( mephisto3 )
	PORT_INCLUDE( mephistoe2 )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("ENT")

	PORT_MODIFY("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("INFO")

	PORT_MODIFY("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("POS")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Left / Black / 9")

	PORT_MODIFY("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Right / White / 0")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("MEM")

	PORT_MODIFY("IN.4")
	PORT_CONFNAME( 0x03, 0x01, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(brikett_state::change_cpu_freq), 0)
	PORT_CONFSETTING(    0x00, "3.579MHz (Battery)" )
	PORT_CONFSETTING(    0x01, "6.144MHz (Mains)" )
	PORT_CONFSETTING(    0x02, "12MHz (Special)" )
	PORT_BIT(0x0c, 0x08, IPT_CUSTOM) // 3rd model
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void brikett_state::mephistoj(machine_config &config)
{
	// basic machine hardware
	CDP1802(config, m_maincpu, 4.194304_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &brikett_state::mephistoj_map);
	m_maincpu->set_addrmap(AS_IO, &brikett_state::mephistoj_io);
	m_maincpu->clear_cb().set(FUNC(brikett_state::clear_r));
	m_maincpu->q_cb().set(m_display, FUNC(mephisto_display1_device::common_w));
	m_maincpu->ef4_cb().set_ioport("PIECE"); // hardwired

	const attotime irq_period = attotime::from_ticks(0x10000, 4.194304_MHz_XTAL); // through SAJ300T
	CLOCK(config, m_irq_clock).set_period(irq_period);
	m_irq_clock->signal_handler().set_inputline(m_maincpu, COSMAC_INPUT_LINE_INT, HOLD_LINE);

	// video hardware
	MEPHISTO_DISPLAY_MODULE1(config, m_display); // internal
	config.set_default_layout(layout_mephisto_junior);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	SPEAKER_SOUND(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.5);
}

void brikett_state::mephisto(machine_config &config)
{
	mephistoj(config);

	// basic machine hardware
	m_maincpu->set_clock(3.579545_MHz_XTAL); // see change_cpu_freq
	m_maincpu->set_addrmap(AS_PROGRAM, &brikett_state::mephisto_map);
	m_maincpu->set_addrmap(AS_IO, &brikett_state::mephisto_io);
	m_maincpu->tpb_cb().set(m_extport, FUNC(cdp1852_device::clock_w));
	m_maincpu->ef1_cb().set_constant(0); // external port
	m_maincpu->ef3_cb().set_constant(0); // external port, but unused

	m_irq_clock->set_period(attotime::from_ticks(0x10000, 3.579545_MHz_XTAL));

	CDP1852(config, m_extport);
	m_extport->mode_cb().set_constant(1);
	m_extport->do_cb().set_nop();

	config.set_default_layout(layout_mephisto_1);
}

void brikett_state::mephisto2(machine_config &config)
{
	mephisto(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &brikett_state::mephisto2_map);
}

void brikett_state::mephistoe2a(machine_config &config)
{
	mephisto2(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &brikett_state::mephistoe2a_map);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(250));

	PWM_DISPLAY(config, m_led_pwm).set_size(8, 8);
	config.set_default_layout(layout_mephisto_esb2);
}

void brikett_state::mephistoe2(machine_config &config)
{
	mephistoe2a(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &brikett_state::mephistoe2_map);
	m_maincpu->ef1_cb().set(FUNC(brikett_state::esb6_r));
	m_extport->do_cb().set(FUNC(brikett_state::esb6_w));
}

void brikett_state::mephisto3(machine_config &config)
{
	mephistoe2(config);

	// basic machine hardware
	m_maincpu->set_clock(6.144_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &brikett_state::mephisto3_map);

	config.set_default_layout(layout_mephisto_3);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( mephisto ) // module s/n 00226xx (898xx Mask ROMs), 01011xx (911xx Mask ROMs)
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("89810", 0x0000, 0x0400, CRC(6816be9e) SHA1(f5f1d5084925fe239f5b2ecf4724751e0dc4fc51) ) // CDP1833CE, also seen with label 91143
	ROM_LOAD("89811", 0x0400, 0x0400, CRC(15febc73) SHA1(10353a7f021993f2cf7d509a928425617e1786fb) ) // " or 91144
	ROM_LOAD("89812", 0x0800, 0x0400, CRC(5e45eb65) SHA1(9d46e5f405bd48705d1e29826917522595fc9768) ) // " or 91145
	ROM_LOAD("89813", 0x0c00, 0x0400, CRC(62da3d89) SHA1(a7f9ada7037e0bd61420358c147b2f57ee47ebcb) ) // " or 91146
	ROM_LOAD("89814", 0x1000, 0x0400, CRC(8e212d9c) SHA1(5df221ce8ca4fbb74f34f31738db4c2efee7fb01) ) // " or 91163
	ROM_LOAD("89815", 0x1400, 0x0400, CRC(072e0b01) SHA1(5b1074932b3f21ab01392250061c093de4af3624) ) // " or 91147
	// 911xx Mask ROMs have the same contents as 898xx Mask ROMs, some modules have both 898xx and 911xx
ROM_END

ROM_START( mephisto1x )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("3-2911_adi.1", 0x0000, 0x1000, CRC(0d62fa67) SHA1(b4bd934fec595f37f99b74eb341d220c511c07a5) ) // CM3200-2
	ROM_LOAD("3-2501_adj.2", 0x1000, 0x1000, CRC(4e1b67ae) SHA1(4fded3ed1a1e168dedc07eea4086fa31805252d9) ) // "
	ROM_LOAD("3-2841_adk.3", 0x2000, 0x1000, CRC(5dd05a5d) SHA1(372ed83a936fb0720b68590ca6ff4a02c80f4bab) ) // "
ROM_END

ROM_START( mephistoj )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("smm2365c.7", 0x0000, 0x2000, CRC(f2f46583) SHA1(960b6f781e4c3d98db85a1bb4f90df4a133f06ba) ) // SMM2365C, no label
ROM_END


ROM_START( mephisto2 ) // module s/n 01142xx (HN462532G EPROMs), 00476xx (TC5334P Mask ROMs)
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("4005_02_351_02.1", 0x0000, 0x1000, CRC(5b13d7bf) SHA1(e1b7dee278a03f75e8a1554715fca4c7fbbc1cb8) ) // HN462532G
	ROM_LOAD("4005_02_351_02.2", 0x1000, 0x1000, CRC(e93bf521) SHA1(42f9adce0d5e25b1b9d10217f8e3e0994d7b70d5) ) // "
	ROM_LOAD("4005_02_351_02.3", 0x2000, 0x1000, CRC(430dac62) SHA1(a0e23fcb4cfa27778a9398bd4994a7792e4541d0) ) // "
	// TC5334P Mask ROM contents is the same (labels 5619 03 351, 5620 03 351, 5621 03 351)
ROM_END

ROM_START( mephisto2a ) // module s/n 01085xx
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("4005_02_351_01.1", 0x0000, 0x1000, CRC(5b13d7bf) SHA1(e1b7dee278a03f75e8a1554715fca4c7fbbc1cb8) ) // HN462532G
	ROM_LOAD("4005_02_352_01.2", 0x1000, 0x1000, CRC(da88b62f) SHA1(f5e71521ba8ab0b481e4725ffa706b1c157424b5) ) // "
	ROM_LOAD("4005_02_353_01.3", 0x2000, 0x1000, CRC(1f933d33) SHA1(5d5bfd40158354830c434f4c8b4ff1cac8ab4f5c) ) // "
ROM_END

ROM_START( mephisto2b ) // module s/n 01070xx
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("4005_02_351_00.1", 0x0000, 0x1000, CRC(5b13d7bf) SHA1(e1b7dee278a03f75e8a1554715fca4c7fbbc1cb8) ) // HN462532G
	ROM_LOAD("4005_02_352_00.2", 0x1000, 0x1000, CRC(da88b62f) SHA1(f5e71521ba8ab0b481e4725ffa706b1c157424b5) ) // "
	ROM_LOAD("4005_02_353_00.3", 0x2000, 0x1000, CRC(23f9eee4) SHA1(c86d63d3c720ba4ae2a12d3b68356cffacae9592) ) // "
ROM_END


ROM_START( mephistoe2 ) // module s/n (0)0111xx
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("251-11.1", 0x0000, 0x1000, CRC(3c8e2631) SHA1(5960e47f0659b1e5f164107069738e730e3ff255) ) // TMS2532JL-35
	ROM_LOAD("252-10.2", 0x1000, 0x1000, CRC(832b053e) SHA1(b0dfe857c38f13a4b04ac67a8a46f37c962a8629) ) // "
	ROM_LOAD("253-09.3", 0x2000, 0x1000, CRC(00788b63) SHA1(cf94dc19ef85b359989410e7824280c59433fca9) ) // "
	ROM_LOAD("254-09.4", 0x3000, 0x1000, CRC(d6be47a6) SHA1(3d577036111c026292b6c445efcb126cf7a6a472) ) // "
ROM_END

ROM_START( mephistoe2a ) // module s/n 00065xx
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("5000_00_251_01.1", 0x0000, 0x1000, CRC(cebf66be) SHA1(7793ba80159a8ae857a1b35d270bb6972fef0563) ) // HN462532G
	ROM_LOAD("5000_00_252_01.2", 0x1000, 0x1000, CRC(66f38992) SHA1(2f9007985ce350a8ee82797a66f37bb9a4de36a4) ) // "
	ROM_LOAD("5000_00_253_01.3", 0x2000, 0x1000, CRC(1b685892) SHA1(af197e62e8a47a6244f7c2261929847f483775e5) ) // "
	ROM_LOAD("5000_00_254_01.4", 0x3000, 0x1000, CRC(e455845d) SHA1(871e2b2b7e75b6ee09d3e027e1b4e5a24ce4c550) ) // "
ROM_END


ROM_START( mephisto3 ) // module s/n 05072xx
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("g81", 0x0000, 0x8000, CRC(7b49475d) SHA1(30193153f0c259294b47e95d3e33834e40a94821) ) // HN613256P
ROM_END

ROM_START( mephisto3a ) // module s/n 00744xx
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("109", 0x0000, 0x4000, CRC(02f9e37d) SHA1(1911d45c0c8db030d129c4d2b25572678835112a) ) // D27128-4
	ROM_LOAD("209", 0x4000, 0x4000, CRC(0f217caf) SHA1(5aa77157af51a73e0654c344636ea2887bc45d42) ) // "
ROM_END

ROM_START( mephisto3b ) // module s/n 00737xx
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("107", 0x0000, 0x4000, CRC(52c072b8) SHA1(938dfaa18d751f06f42be16dedb7d32d010023b2) ) // HN4827128G-25
	ROM_LOAD("207", 0x4000, 0x4000, CRC(9b45c350) SHA1(96a11f740c657a915a9ce3fa417a59f4e064a10b) ) // "
ROM_END

ROM_START( mephisto3c ) // module s/n 04001xx
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("103", 0x0000, 0x4000, CRC(c9fc143a) SHA1(5f3268d9c8c0c0b8fac3e687c30a4871663fbd4f) ) // DQ5143-250 27128-25
	ROM_LOAD("203", 0x4000, 0x4000, CRC(d565d4b2) SHA1(561584e5ef1db9e9aee67d3c1c1267d210fb7859) ) // "
ROM_END

ROM_START( mephisto3d ) // module s/n 00711xx
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101", 0x0000, 0x4000, CRC(923de04f) SHA1(ca7cb3e29aeb3432a815c9d58bb0ed45e7302581) ) // HN4827128G-45 or D27128-4
	ROM_LOAD("201", 0x4000, 0x4000, CRC(0c3cb8fa) SHA1(31449422142c19fc71474a057fc5d6af8a86be7d) ) // "
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME         PARENT      COMPAT  MACHINE      INPUT        STATE          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1980, mephisto,    0,          0,      mephisto,    mephisto,    brikett_state, empty_init, "Hegener + Glaser", "Mephisto", MACHINE_SUPPORTS_SAVE )

SYST( 1981, mephisto1x,  0,          0,      mephisto2,   mephisto2,   brikett_state, empty_init, "Hegener + Glaser", "Mephisto 1X", MACHINE_SUPPORTS_SAVE ) // France
SYST( 1982, mephistoj,   0,          0,      mephistoj,   mephistoj,   brikett_state, empty_init, "Hegener + Glaser", "Mephisto Junior (1982 version)", MACHINE_SUPPORTS_SAVE ) // there's also a "Mephisto Junior" from 1990

SYST( 1981, mephisto2,   0,          0,      mephisto2,   mephisto2,   brikett_state, empty_init, "Hegener + Glaser", "Mephisto II (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1981, mephisto2a,  mephisto2,  0,      mephisto2,   mephisto2,   brikett_state, empty_init, "Hegener + Glaser", "Mephisto II (set 2)", MACHINE_SUPPORTS_SAVE )
SYST( 1981, mephisto2b,  mephisto2,  0,      mephisto2,   mephisto2,   brikett_state, empty_init, "Hegener + Glaser", "Mephisto II (set 3)", MACHINE_SUPPORTS_SAVE )

SYST( 1981, mephistoe2,  0,          0,      mephistoe2,  mephistoe2,  brikett_state, empty_init, "Hegener + Glaser", "Mephisto ESB II (ESB 6000 board)", MACHINE_SUPPORTS_SAVE )
SYST( 1981, mephistoe2a, mephistoe2, 0,      mephistoe2a, mephistoe2a, brikett_state, empty_init, "Hegener + Glaser", "Mephisto ESB II (ESB II board)", MACHINE_SUPPORTS_SAVE )

SYST( 1983, mephisto3,   0,          0,      mephisto3,   mephisto3,   brikett_state, empty_init, "Hegener + Glaser", "Mephisto III (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1983, mephisto3a,  mephisto3,  0,      mephisto3,   mephisto3,   brikett_state, empty_init, "Hegener + Glaser", "Mephisto III (set 2)", MACHINE_SUPPORTS_SAVE )
SYST( 1983, mephisto3b,  mephisto3,  0,      mephisto3,   mephisto3,   brikett_state, empty_init, "Hegener + Glaser", "Mephisto III (set 3)", MACHINE_SUPPORTS_SAVE )
SYST( 1983, mephisto3c,  mephisto3,  0,      mephisto3,   mephisto3,   brikett_state, empty_init, "Hegener + Glaser", "Mephisto III (set 4)", MACHINE_SUPPORTS_SAVE )
SYST( 1983, mephisto3d,  mephisto3,  0,      mephisto3,   mephisto3,   brikett_state, empty_init, "Hegener + Glaser", "Mephisto III (set 5)", MACHINE_SUPPORTS_SAVE )



bullet.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

Wave Mate Bullet

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

|-------------------------------------------|
|                           CN7     CN6     |
|                                   PIO     |
|                           FDC             |
|       4164 4164                   CN5     |
|       4164 4164       SW1              CN2|
|       4164 4164                           |
|       4164 4164                   PROM    |
|       4164 4164   4.9152MHz               |
|       4164 4164   16MHz   DMA     CPU     |
|       4164 4164                           |
|       4164 4164           CN4 CN3      CN1|
|                                           |
|                           DART    CTC     |
|                                   CN8     |
|-------------------------------------------|

Notes:
    Relevant IC's shown.

    CPU     - SGS Z80ACPUB1 Z80A CPU
    DMA     - Zilog Z8410APS Z80A DMA
    PIO     - SGS Z80APIOB1 Z80A PIO
    DART    - Zilog Z8470APS Z80A DART
    CTC     - Zilog Z8430APS Z80A CTC
    FDC     - Synertek SY1793-02 FDC
    PROM    - AMD AM27S190C 32x8 TTL PROM
    4164    - Fujitsu MB8264-15 64Kx1 RAM
    CN1     - 2x25 PCB header, external DMA bus
    CN2     - 2x17 PCB header, Winchester / 2x25 header, SCSI (in board revision E)
    CN3     - 2x5 PCB header, RS-232 A (system console)
    CN4     - 2x5 PCB header, RS-232 B
    CN5     - 2x17 PCB header, Centronics
    CN6     - 2x25 PCB header, 8" floppy drives
    CN7     - 2x17 PCB header, 5.25" floppy drives
    CN8     - 4-pin Molex

*/

/*

    TODO:

    - memory banking is broken
    - z80dart wait/ready
    - IMI 7710 Winchester controller
        chdman createhd -o imi7710.chd -chs 350,3,10 -ss 1024
    - revision E model

*/

#include "emu.h"
#include "bullet.h"

#include "bus/rs232/rs232.h"
#include "bus/scsi/scsihd.h"
#include "softlist.h"


//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

// DMA ready sources
enum
{
	FDRDY = 0,
	DARTARDY,
	DARTBRDY,
	WINRDY,
	EXRDY1,
	EXRDY2
};

#define SEG0 \
	BIT(m_mbank, 0)

#define SEG5 \
	BIT(m_mbank, 5)

#define DMB4 \
	BIT(m_xdma0, 4)

#define DMB6 \
	BIT(m_xdma0, 6)



//**************************************************************************
//  READ/WRITE HANDLERS
//**************************************************************************

//-------------------------------------------------
//  mreq_r -
//-------------------------------------------------

uint8_t bullet_state::mreq_r(offs_t offset)
{
	uint8_t data = 0;

	if (!m_brom && !BIT(offset, 5))
	{
		data = m_rom->base()[offset & 0x1f];
	}
	else
	{
		if (offset < 0xc000)
		{
			data = m_ram->pointer()[(m_segst << 16) | offset];
		}
		else
		{
			data = m_ram->pointer()[offset];
		}
	}

	return data;
}


//-------------------------------------------------
//  mreq_w -
//-------------------------------------------------

void bullet_state::mreq_w(offs_t offset, uint8_t data)
{
	if (offset < 0xc000)
	{
		m_ram->pointer()[(m_segst << 16) | offset] = data;
	}
	else
	{
		m_ram->pointer()[offset] = data;
	}
}


//-------------------------------------------------
//  win_r -
//-------------------------------------------------

uint8_t bullet_state::win_r()
{
	return 0;
}


//-------------------------------------------------
//  wstrobe_w -
//-------------------------------------------------

void bullet_state::wstrobe_w(uint8_t data)
{
}


//-------------------------------------------------
//  brom_r -
//-------------------------------------------------

uint8_t bullet_state::brom_r()
{
	m_brom = 1;

	return 0;
}


//-------------------------------------------------
//  brom_w -
//-------------------------------------------------

void bullet_state::brom_w(uint8_t data)
{
	m_brom = 1;
}


//-------------------------------------------------
//  exdsk_w -
//-------------------------------------------------

void bullet_state::exdsk_w(uint8_t data)
{
	/*

	    bit     signal      description

	    0                   drive select 0
	    1                   drive select 1
	    2                   select 8" floppy
	    3       MSE         select software control of port
	    4       SIDE        select side 2
	    5       _MOTOR      disable 5" floppy spindle motors
	    6
	    7       WPRC        enable write precompensation

	*/

	if (BIT(data, 3))
	{
		m_exdsk_sw = true;
	}

	if (m_exdsk_sw)
	{
		// drive select
		m_floppy = nullptr;

		switch (data & 0x07)
		{
		// 5.25"
		case 0: m_floppy = m_floppy0->get_device(); break;
		case 1: m_floppy = m_floppy1->get_device(); break;
		case 2: m_floppy = m_floppy2->get_device(); break;
		case 3: m_floppy = m_floppy3->get_device(); break;
		// 8"
		case 4: m_floppy = m_floppy4->get_device(); break;
		case 5: m_floppy = m_floppy5->get_device(); break;
		case 6: m_floppy = m_floppy6->get_device(); break;
		case 7: m_floppy = m_floppy7->get_device(); break;
		}

		m_fdc->set_floppy(m_floppy);
	}

	if (m_floppy)
	{
		// side select
		m_floppy->ss_w(BIT(data, 4));

		// floppy motor
		m_floppy->mon_w(BIT(data, 5));
	}
}


//-------------------------------------------------
//  exdma_w -
//-------------------------------------------------

void bullet_state::exdma_w(uint8_t data)
{
	/*

	    bit     description

	    0       DMA ready source select 0
	    1       DMA ready source select 1
	    2       DMA ready source select 2
	    3       memory control 0
	    4       memory control 1
	    5
	    6
	    7

	*/

	m_exdma = data;

	m_buf = BIT(data, 3);

	update_dma_rdy();
}


//-------------------------------------------------
//  hdcon_w -
//-------------------------------------------------

void bullet_state::hdcon_w(uint8_t data)
{
	/*

	    bit     signal  description

	    0       PLO     phase lock oscillator
	    1       RCD     read clock frequency
	    2       EXC     MB8877 clock frequency
	    3       DEN     MB8877 density select
	    4               enable software control of mode
	    5
	    6
	    7

	*/

	if (BIT(data, 4))
	{
		m_hdcon_sw = true;
	}

	if (m_hdcon_sw)
	{
		// FDC clock
		m_fdc->set_unscaled_clock(16_MHz_XTAL / (BIT(data, 2) ? 16 : 8));

		// density select
		m_fdc->dden_w(BIT(data, 3));
	}
}


//-------------------------------------------------
//  info_r -
//-------------------------------------------------

uint8_t bullet_state::info_r()
{
	/*

	    bit     signal      description

	    0       SW1         DIP switch 1
	    1       SW2         DIP switch 2
	    2       SW3         DIP switch 3
	    3       SW4         DIP switch 4
	    4       HLDST       floppy disk head load status
	    5       *XDCG       floppy disk exchange (8" only)
	    6       FDIRQ       FDC interrupt request line
	    7       FDDRQ       FDC data request line

	*/

	uint8_t data = 0;

	// DIP switches
	data |= m_sw1->read() & 0x0f;

	// floppy
	data |= m_fdc->hld_r() << 4;
	data |= (m_floppy ? m_floppy->dskchg_r() : 1) << 5;
	data |= m_fdc->intrq_r() << 6;
	data |= m_fdc->drq_r() << 7;

	return data;
}


//-------------------------------------------------
//  segst_w -
//-------------------------------------------------

void bullet_state::segst_w(uint8_t data)
{
	m_segst = BIT(data, 0);
}


//-------------------------------------------------
//  mreq_r -
//-------------------------------------------------

uint8_t bulletf_state::mreq_r(offs_t offset)
{
	uint8_t data = 0;

	if (!m_rome && !BIT(offset, 5))
	{
		data = m_rom->base()[offset & 0x1f];
	}
	else
	{
		if (offset < 0xc000)
		{
			if (!SEG5)
			{
				data = m_ram->pointer()[(SEG0 << 16) | offset];
			}
			else if (offset >= 0x8000)
			{
				data = m_ram->pointer()[0x1c000 + (offset - 0x8000)];
			}
		}
		else
		{
			data = m_ram->pointer()[offset];
		}
	}

	return data;
}


//-------------------------------------------------
//  mreq_w -
//-------------------------------------------------

void bulletf_state::mreq_w(offs_t offset, uint8_t data)
{
	if (offset < 0xc000)
	{
		if (!SEG5)
		{
			m_ram->pointer()[(SEG0 << 16) | offset] = data;
		}
		else if (offset >= 0x8000)
		{
			m_ram->pointer()[0x1c000 + (offset - 0x8000)] = data;
		}
	}
	else
	{
		m_ram->pointer()[offset] = data;
	}
}


//-------------------------------------------------
//  xdma0_w -
//-------------------------------------------------

void bulletf_state::xdma0_w(uint8_t data)
{
	/*

	    bit     signal

	    0       device select (0=FDC, 1=SCSI)
	    1
	    2
	    3
	    4       DMB4        Source bank
	    5
	    6       DMB6        Destination bank
	    7

	*/

	m_rome = 1;

	m_xdma0 = data;
}


//-------------------------------------------------
//  xfdc_w -
//-------------------------------------------------

void bulletf_state::xfdc_w(uint8_t data)
{
	/*

	    bit     signal

	    0       Unit select number
	    1       Unit select number
	    2       Unit select number
	    3       Unit select number
	    4       Select side 2
	    5       Disable 3 & 5 inch spindle motors
	    6       Set for 1 MHz controller operation, reset for 2 MHz controller operation
	    7       Set to select single density

	*/

	// drive select
	m_floppy = nullptr;

	switch (data & 0x0f)
	{
	// 5.25"
	case 0: m_floppy = m_floppy0->get_device(); break;
	case 1: m_floppy = m_floppy1->get_device(); break;
	case 2: m_floppy = m_floppy2->get_device(); break;
	case 3: m_floppy = m_floppy3->get_device(); break;
	// 8"
	case 4: m_floppy = m_floppy4->get_device(); break;
	case 5: m_floppy = m_floppy5->get_device(); break;
	case 6: m_floppy = m_floppy6->get_device(); break;
	case 7: m_floppy = m_floppy7->get_device(); break;
	// 3.5"
	case 8: m_floppy = m_floppy8->get_device(); break;
	case 9: m_floppy = m_floppy9->get_device(); break;
	}

	m_fdc->set_floppy(m_floppy);

	if (m_floppy)
	{
		// side select
		m_floppy->ss_w(BIT(data, 4));

		// floppy motor
		m_floppy->mon_w(BIT(data, 5));
	}

	// FDC clock
	m_fdc->set_unscaled_clock(16_MHz_XTAL / (BIT(data, 6) ? 16 : 8));

	// density select
	m_fdc->dden_w(BIT(data, 7));
}


//-------------------------------------------------
//  mbank_w -
//-------------------------------------------------

void bulletf_state::mbank_w(uint8_t data)
{
	/*

	    bit     signal

	    0       Select active bank (0=bank 0, 1=bank 1)
	    1
	    2
	    3
	    4       Select system space overlay (0=no overlay, 1=overlay)
	    5
	    6
	    7

	*/

	m_mbank = data;
}


//-------------------------------------------------
//  scsi_r -
//-------------------------------------------------

uint8_t bulletf_state::scsi_r()
{
	uint8_t data = m_scsi_data_in->read();

	m_scsibus->write_ack(1);

	m_wack = 0;
	update_dma_rdy();

	return data;
}


//-------------------------------------------------
//  scsi_w -
//-------------------------------------------------

void bulletf_state::scsi_w(uint8_t data)
{
	m_scsi_data_out->write(data);

	m_scsibus->write_ack(1);

	m_wack = 0;
	update_dma_rdy();
}

//-------------------------------------------------
//  hwsts_r -
//-------------------------------------------------

uint8_t bulletf_state::hwsts_r()
{
	/*

	    bit     signal

	    0       CBUSY   Centronics busy
	    1       SW2     DIP switch 2
	    2       SW3     DIP switch 3
	    3       FD2S    *Floppy disk two sided
	    4       HLDST   Floppy disk head load status
	    5       XDCG    *Floppy disk exchange (8-inch only)
	    6       FDIRQ   FDC interrupt request line
	    7       FDDRQ   FDC data request line

	*/

	uint8_t data = 0;

	// centronics busy
	data |= m_centronics_busy;

	// DIP switches
	data |= m_sw1->read() & 0x06;

	// floppy
	data |= (m_floppy ? m_floppy->twosid_r() : 1) << 3;
	data |= m_fdc->hld_r() << 4;
	data |= (m_floppy ? m_floppy->dskchg_r() : 1) << 5;
	data |= m_fdc->intrq_r() << 6;
	data |= m_fdc->drq_r() << 7;

	return data;
}



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

//-------------------------------------------------
//  ADDRESS_MAP( bullet_mem )
//-------------------------------------------------

void bullet_state::bullet_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(bullet_state::mreq_r), FUNC(bullet_state::mreq_w));
}


//-------------------------------------------------
//  ADDRESS_MAP( bullet_io )
//-------------------------------------------------

void bullet_state::bullet_io(address_map &map)
{
	map.global_mask(0x1f);
	map(0x00, 0x03).rw(m_dart, FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
	map(0x04, 0x07).rw(Z80PIO_TAG, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x08, 0x0b).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x0c, 0x0c).mirror(0x03).rw(FUNC(bullet_state::win_r), FUNC(bullet_state::wstrobe_w));
	map(0x10, 0x13).rw(m_fdc, FUNC(mb8877_device::read), FUNC(mb8877_device::write));
	map(0x14, 0x14).rw(m_dmac, FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	map(0x15, 0x15).rw(FUNC(bullet_state::brom_r), FUNC(bullet_state::brom_w));
	map(0x16, 0x16).w(FUNC(bullet_state::exdsk_w));
	map(0x17, 0x17).w(FUNC(bullet_state::exdma_w));
	map(0x18, 0x18).w(FUNC(bullet_state::hdcon_w));
	map(0x19, 0x19).r(FUNC(bullet_state::info_r));
	map(0x1a, 0x1a).w(FUNC(bullet_state::segst_w));
}


//-------------------------------------------------
//  ADDRESS_MAP( bulletf_mem )
//-------------------------------------------------

void bulletf_state::bulletf_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(bulletf_state::mreq_r), FUNC(bulletf_state::mreq_w));
}


//-------------------------------------------------
//  ADDRESS_MAP( bulletf_io )
//-------------------------------------------------

void bulletf_state::bulletf_io(address_map &map)
{
	map.global_mask(0x3f);
	map(0x00, 0x03).rw(m_dart, FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
	map(0x04, 0x07).rw(Z80PIO_TAG, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x08, 0x0b).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x10, 0x13).rw(m_fdc, FUNC(mb8877_device::read), FUNC(mb8877_device::write));
	map(0x14, 0x14).w(FUNC(bulletf_state::xdma0_w));
	map(0x16, 0x16).w(FUNC(bulletf_state::xfdc_w));
	map(0x17, 0x17).w(FUNC(bulletf_state::mbank_w));
	map(0x19, 0x19).rw(FUNC(bulletf_state::scsi_r), FUNC(bulletf_state::scsi_w));
	map(0x1a, 0x1a).rw(m_dmac, FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	map(0x1b, 0x1b).r(FUNC(bulletf_state::hwsts_r));
}



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

//-------------------------------------------------
//  INPUT_PORTS( bullet )
//-------------------------------------------------

INPUT_PORTS_START( bullet )
	PORT_START("SW1")
	PORT_DIPUNUSED_DIPLOC( 0x01, IP_ACTIVE_LOW, "SW1:1" )
	PORT_DIPUNUSED_DIPLOC( 0x02, IP_ACTIVE_LOW, "SW1:2" )
	PORT_DIPUNUSED_DIPLOC( 0x04, IP_ACTIVE_LOW, "SW1:3" )
	PORT_DIPUNUSED_DIPLOC( 0x08, IP_ACTIVE_LOW, "SW1:4" )
	PORT_DIPNAME( 0xf0, 0x50, "Floppy Type" ) PORT_DIPLOCATION("SW1:5,6,7,8")
	PORT_DIPSETTING(    0xf0, "5.25\" SD" )
	PORT_DIPSETTING(    0x50, "5.25\" DD" )
	PORT_DIPSETTING(    0x90, "8\" SD" )
	PORT_DIPSETTING(    0x00, "8\" DD" )
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( bulletf )
//-------------------------------------------------

INPUT_PORTS_START( bulletf )
	PORT_START("SW1")
	PORT_DIPNAME( 0x01, 0x01, "SCSI Bus Termination" ) PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPUNUSED_DIPLOC( 0x02, IP_ACTIVE_LOW, "SW1:2" )
	PORT_DIPUNUSED_DIPLOC( 0x04, IP_ACTIVE_LOW, "SW1:3" )
	PORT_DIPNAME( 0x08, 0x08, "Boot ROM Device" ) PORT_DIPLOCATION("SW1:4")
	PORT_DIPSETTING(    0x00, "Onboard" )
	PORT_DIPSETTING(    0x08, "EPROM" )
	PORT_DIPNAME( 0xf0, 0xc0, "Floppy Type" ) PORT_DIPLOCATION("SW1:5,6,7,8")
	PORT_DIPSETTING(    0x10, "3\" DD" )
	PORT_DIPSETTING(    0xc0, "5.25\" SD" )
	PORT_DIPSETTING(    0x40, "5.25\" DD" )
	PORT_DIPSETTING(    0xa0, "8\" SD" )
	PORT_DIPSETTING(    0x20, "8\" DD" )
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  Z80CTC
//-------------------------------------------------

TIMER_DEVICE_CALLBACK_MEMBER(bullet_state::ctc_tick)
{
	m_ctc->trg0(1);
	m_ctc->trg0(0);

	m_ctc->trg1(1);
	m_ctc->trg1(0);

	m_ctc->trg2(1);
	m_ctc->trg2(0);
}

void bullet_state::dart_rxtxca_w(int state)
{
	m_dart->txca_w(state);
	m_dart->rxca_w(state);
}

//-------------------------------------------------
//  Z80DART
//-------------------------------------------------

void bullet_state::dartardy_w(int state)
{
	m_dartardy = state;
	update_dma_rdy();
}

void bullet_state::dartbrdy_w(int state)
{
	m_dartbrdy = state;
	update_dma_rdy();
}

//-------------------------------------------------
//  Z80DMA
//-------------------------------------------------

void bullet_state::update_dma_rdy()
{
	int rdy = 1;

	switch (m_exdma & 0x07)
	{
	case FDRDY:
		rdy = m_fdrdy;
		break;

	case DARTARDY:
		rdy = m_dartardy;
		break;

	case DARTBRDY:
		rdy = m_dartbrdy;
		break;

	case WINRDY:
		rdy = m_winrdy;
		break;

	case EXRDY1:
		rdy = m_exrdy1;
		break;

	case EXRDY2:
		rdy = m_exrdy2;
		break;
	}

	m_dmac->rdy_w(rdy);
}

uint8_t bullet_state::dma_mreq_r(offs_t offset)
{
	uint8_t data = m_ram->pointer()[(m_buf << 16) | offset];

	if (BIT(m_exdma, 4))
	{
		m_buf = !m_buf;
	}

	return data;
}

void bullet_state::dma_mreq_w(offs_t offset, uint8_t data)
{
	m_ram->pointer()[(m_buf << 16) | offset] = data;

	if (BIT(m_exdma, 4))
	{
		m_buf = !m_buf;
	}
}

uint8_t bullet_state::io_read_byte(offs_t offset)
{
	return m_maincpu->space(AS_IO).read_byte(offset);
}

void bullet_state::io_write_byte(offs_t offset, uint8_t data)
{
	m_maincpu->space(AS_IO).write_byte(offset, data);
}

//-------------------------------------------------
//  Z80DMA for bulletf (not used currently)
//-------------------------------------------------

void bulletf_state::update_dma_rdy()
{
	int rdy = 1;

	if (BIT(m_xdma0, 0))
	{
		rdy = m_wack || m_wrdy;
	}
	else
	{
		rdy = m_fdrdy;
	}

	m_dmac->rdy_w(rdy);
}

uint8_t bulletf_state::dma_mreq_r(offs_t offset)
{
	return m_ram->pointer()[(DMB4 << 16) | offset];
}

void bulletf_state::dma_mreq_w(offs_t offset, uint8_t data)
{
	m_ram->pointer()[(DMB6 << 16) | offset] = data;
}

//-------------------------------------------------
//  Z80PIO
//-------------------------------------------------

void bullet_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

void bullet_state::write_centronics_perror(int state)
{
	m_centronics_perror = state;
}

void bullet_state::write_centronics_select(int state)
{
	m_centronics_select = state;
}

void bullet_state::write_centronics_fault(int state)
{
	m_centronics_fault = state;
}

uint8_t bullet_state::pio_pb_r()
{
	/*

	    bit     signal      description

	    0                   centronics busy
	    1                   centronics paper end
	    2                   centronics selected
	    3       *FAULT      centronics fault
	    4                   external vector
	    5       WBUSDIR     winchester bus direction
	    6       WCOMPLETE   winchester command complete
	    7       *WINRDY     winchester ready

	*/

	uint8_t data = 0;

	// centronics
	data |= m_centronics_busy;
	data |= m_centronics_perror << 1;
	data |= m_centronics_select << 2;
	data |= m_centronics_fault << 3;

	return data;
}


void bulletf_state::pio_pa_w(uint8_t data)
{
	/*

	    bit     signal

	    0       ATN
	    1       RST
	    2       SEL
	    3       BUSY
	    4       MSG
	    5       C/D
	    6       REQ
	    7       I/O

	*/

	m_scsibus->write_atn(BIT(data, 0));
	m_scsibus->write_rst(BIT(data, 1));
	m_scsibus->write_sel(BIT(data, 2));
}

void bulletf_state::cstrb_w(int state)
{
	m_centronics->write_strobe(!state);
}

static void bullet_525_floppies(device_slot_interface &device)
{
	device.option_add("525sd", FLOPPY_525_SD);
	device.option_add("525dd", FLOPPY_525_DD);
	device.option_add("525qd", FLOPPY_525_QD);
}

static void bullet_8_floppies(device_slot_interface &device)
{
	device.option_add("8dssd", FLOPPY_8_DSSD);
	device.option_add("8dsdd", FLOPPY_8_DSDD);
}

static void bullet_35_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
}

void bullet_state::fdc_drq_w(int state)
{
	m_fdrdy = !state;
	update_dma_rdy();
}

void bulletf_state::req_w(int state)
{
	if (!state)
	{
		m_scsibus->write_ack(0);

		m_wack = 1;
	}

	m_wrdy = !state;
	update_dma_rdy();

	m_scsi_ctrl_in->write_bit6(state);
}


static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END


//-------------------------------------------------
//  z80_daisy_config daisy_chain
//-------------------------------------------------

static const z80_daisy_config daisy_chain[] =
{
	{ Z80DMA_TAG },
	{ Z80DART_TAG },
	{ Z80PIO_TAG },
	{ Z80CTC_TAG },
	{ nullptr }
};



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( bullet )
//-------------------------------------------------

void bullet_state::machine_start()
{
	// state saving
	save_item(NAME(m_segst));
	save_item(NAME(m_brom));
	save_item(NAME(m_exdma));
	save_item(NAME(m_buf));
	save_item(NAME(m_fdrdy));
	save_item(NAME(m_dartardy));
	save_item(NAME(m_dartbrdy));
	save_item(NAME(m_winrdy));
	save_item(NAME(m_exrdy1));
	save_item(NAME(m_exrdy2));
	save_item(NAME(m_centronics_busy));
	save_item(NAME(m_centronics_perror));
	save_item(NAME(m_centronics_select));
	save_item(NAME(m_centronics_fault));
}


//-------------------------------------------------
//  MACHINE_START( bulletf )
//-------------------------------------------------

void bulletf_state::machine_start()
{
	// state saving
	save_item(NAME(m_fdrdy));
	save_item(NAME(m_rome));
	save_item(NAME(m_xdma0));
	save_item(NAME(m_mbank));
	save_item(NAME(m_wack));
	save_item(NAME(m_wrdy));
	save_item(NAME(m_centronics_busy));
}


void bullet_state::machine_reset()
{
	// memory banking
	m_brom = 0;
	m_segst = 0;

	// DMA ready
	m_exdma = 0;
	m_buf = 0;
	update_dma_rdy();

	// disable software control
	m_exdsk_sw = false;
	m_hdcon_sw = false;

	uint8_t sw1 = m_sw1->read();
	int mini = BIT(sw1, 6);
	m_fdc->set_unscaled_clock(16_MHz_XTAL / (mini ? 16 : 8));
	m_fdc->dden_w(BIT(sw1, 7));

	if (mini)
	{
		m_floppy = m_floppy0->get_device();
	}
	else
	{
		m_floppy = m_floppy4->get_device();
	}

	m_fdc->set_floppy(m_floppy);

	if (m_floppy)
	{
		m_floppy->ss_w(0);
		m_floppy->mon_w(0);
	}
}


void bulletf_state::machine_reset()
{
	// memory banking
	m_rome = 0;
	m_mbank = 0;

	// DMA ready
	m_xdma0 = 0;
	m_wack = 0;
	m_wrdy = 0;
	update_dma_rdy();
}



//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  machine_config( bullet )
//-------------------------------------------------

void bullet_state::bullet(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &bullet_state::bullet_mem);
	m_maincpu->set_addrmap(AS_IO, &bullet_state::bullet_io);
	m_maincpu->set_daisy_config(daisy_chain);
	m_maincpu->busack_cb().set(m_dmac, FUNC(z80dma_device::bai_w));

	// devices
	Z80CTC(config, m_ctc, 16_MHz_XTAL / 4);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->zc_callback<0>().set(FUNC(bullet_state::dart_rxtxca_w));
	m_ctc->zc_callback<1>().set(m_dart, FUNC(z80dart_device::rxtxcb_w));
	m_ctc->zc_callback<2>().set(m_ctc, FUNC(z80ctc_device::trg3));

	TIMER(config, "ctc").configure_periodic(FUNC(bullet_state::ctc_tick), attotime::from_hz(4.9152_MHz_XTAL / 4));

	Z80DART(config, m_dart, 16_MHz_XTAL / 4);
	m_dart->out_txda_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_txd));
	m_dart->out_dtra_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_dtr));
	m_dart->out_rtsa_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_rts));
	m_dart->out_wrdya_callback().set(FUNC(bullet_state::dartardy_w));
	m_dart->out_txdb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_txd));
	m_dart->out_dtrb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_dtr));
	m_dart->out_rtsb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_rts));
	m_dart->out_wrdyb_callback().set(FUNC(bullet_state::dartbrdy_w));
	m_dart->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80DMA(config, m_dmac, 16_MHz_XTAL / 4);
	m_dmac->out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	m_dmac->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_dmac->in_mreq_callback().set(FUNC(bullet_state::dma_mreq_r));
	m_dmac->out_mreq_callback().set(FUNC(bullet_state::dma_mreq_w));
	m_dmac->in_iorq_callback().set(FUNC(bullet_state::io_read_byte));
	m_dmac->out_iorq_callback().set(FUNC(bullet_state::io_write_byte));

	z80pio_device& pio(Z80PIO(config, Z80PIO_TAG, 16_MHz_XTAL / 4));
	pio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	pio.out_pa_callback().set("cent_data_out", FUNC(output_latch_device::write));
	pio.in_pb_callback().set(FUNC(bullet_state::pio_pb_r));

	MB8877(config, m_fdc, 16_MHz_XTAL / 16);
	m_fdc->intrq_wr_callback().set(m_dart, FUNC(z80dart_device::dcda_w));
	m_fdc->drq_wr_callback().set(FUNC(bullet_state::fdc_drq_w));
	FLOPPY_CONNECTOR(config, MB8877_TAG":0", bullet_525_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, MB8877_TAG":1", bullet_525_floppies, nullptr,    floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, MB8877_TAG":2", bullet_525_floppies, nullptr,    floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, MB8877_TAG":3", bullet_525_floppies, nullptr,    floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, MB8877_TAG":4", bullet_8_floppies, nullptr,      floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, MB8877_TAG":5", bullet_8_floppies, nullptr,      floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, MB8877_TAG":6", bullet_8_floppies, nullptr,      floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, MB8877_TAG":7", bullet_8_floppies, nullptr,      floppy_image_device::default_mfm_floppy_formats);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(bullet_state::write_centronics_busy));
	m_centronics->perror_handler().set(FUNC(bullet_state::write_centronics_perror));
	m_centronics->select_handler().set(FUNC(bullet_state::write_centronics_select));
	m_centronics->fault_handler().set(FUNC(bullet_state::write_centronics_fault));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	rs232_port_device &rs232a(RS232_PORT(config, RS232_A_TAG, default_rs232_devices, "terminal"));
	rs232a.rxd_handler().set(m_dart, FUNC(z80dart_device::rxa_w));
	rs232a.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	rs232_port_device &rs232b(RS232_PORT(config, RS232_B_TAG, default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_dart, FUNC(z80dart_device::rxb_w));

	// software lists
	SOFTWARE_LIST(config, "flop_list").set_original("wmbullet");

	// internal ram
	RAM(config, RAM_TAG).set_default_size("128K");
}


//-------------------------------------------------
//  machine_config( bulletf )
//-------------------------------------------------

void bulletf_state::bulletf(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &bulletf_state::bulletf_mem);
	m_maincpu->set_addrmap(AS_IO, &bulletf_state::bulletf_io);
	m_maincpu->set_daisy_config(daisy_chain);
	m_maincpu->busack_cb().set(m_dmac, FUNC(z80dma_device::bai_w));

	// devices
	Z80CTC(config, m_ctc, 16_MHz_XTAL / 4);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->zc_callback<0>().set(FUNC(bullet_state::dart_rxtxca_w));
	m_ctc->zc_callback<1>().set(m_dart, FUNC(z80dart_device::rxtxcb_w));
	m_ctc->zc_callback<2>().set(m_ctc, FUNC(z80ctc_device::trg3));

	TIMER(config, "ctc").configure_periodic(FUNC(bullet_state::ctc_tick), attotime::from_hz(4.9152_MHz_XTAL / 4));

	Z80DART(config, m_dart, 16_MHz_XTAL / 4);
	m_dart->out_txda_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_txd));
	m_dart->out_dtra_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_dtr));
	m_dart->out_rtsa_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_rts));
	m_dart->out_wrdya_callback().set(FUNC(bullet_state::dartardy_w));
	m_dart->out_txdb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_txd));
	m_dart->out_dtrb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_dtr));
	m_dart->out_rtsb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_rts));
	m_dart->out_wrdyb_callback().set(FUNC(bullet_state::dartbrdy_w));
	m_dart->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80DMA(config, m_dmac, 16_MHz_XTAL / 4);
	m_dmac->out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	m_dmac->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_dmac->in_mreq_callback().set(FUNC(bullet_state::dma_mreq_r));
	m_dmac->out_mreq_callback().set(FUNC(bullet_state::dma_mreq_w));
	m_dmac->in_iorq_callback().set(FUNC(bullet_state::io_read_byte));
	m_dmac->out_iorq_callback().set(FUNC(bullet_state::io_write_byte));

	z80pio_device& pio(Z80PIO(config, Z80PIO_TAG, 16_MHz_XTAL / 4));
	pio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	pio.in_pa_callback().set("scsi_ctrl_in", FUNC(input_buffer_device::read));
	pio.out_pa_callback().set(FUNC(bulletf_state::pio_pa_w));
	pio.out_ardy_callback().set("cent_data_out", FUNC(output_latch_device::write));
	pio.out_brdy_callback().set(FUNC(bulletf_state::cstrb_w));

	MB8877(config, m_fdc, 16_MHz_XTAL / 16);
	m_fdc->intrq_wr_callback().set(m_dart, FUNC(z80dart_device::rib_w));
	m_fdc->drq_wr_callback().set(FUNC(bullet_state::fdc_drq_w));
	FLOPPY_CONNECTOR(config, MB8877_TAG":0", bullet_525_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, MB8877_TAG":1", bullet_525_floppies, nullptr,    floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, MB8877_TAG":2", bullet_525_floppies, nullptr,    floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, MB8877_TAG":3", bullet_525_floppies, nullptr,    floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, MB8877_TAG":4", bullet_8_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, MB8877_TAG":5", bullet_8_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, MB8877_TAG":6", bullet_8_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, MB8877_TAG":7", bullet_8_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, MB8877_TAG":8", bullet_35_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, MB8877_TAG":9", bullet_35_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(bullet_state::write_centronics_busy));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	rs232_port_device &rs232a(RS232_PORT(config, RS232_A_TAG, default_rs232_devices, "terminal"));
	rs232a.rxd_handler().set(m_dart, FUNC(z80dart_device::rxa_w));
	rs232a.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	rs232_port_device &rs232b(RS232_PORT(config, RS232_B_TAG, default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_dart, FUNC(z80dart_device::rxb_w));

	SCSI_PORT(config, m_scsibus, 0);
	m_scsibus->bsy_handler().set(m_scsi_ctrl_in, FUNC(input_buffer_device::write_bit3));
	m_scsibus->msg_handler().set(m_scsi_ctrl_in, FUNC(input_buffer_device::write_bit4));
	m_scsibus->cd_handler().set(m_scsi_ctrl_in, FUNC(input_buffer_device::write_bit5));
	m_scsibus->req_handler().set(FUNC(bulletf_state::req_w));
	m_scsibus->io_handler().set(m_scsi_ctrl_in, FUNC(input_buffer_device::write_bit7));
	m_scsibus->set_data_input_buffer(m_scsi_data_in);
	m_scsibus->set_slot_device(1, "harddisk", SCSIHD, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_0));

	OUTPUT_LATCH(config, m_scsi_data_out);
	m_scsibus->set_output_latch(*m_scsi_data_out);
	INPUT_BUFFER(config, m_scsi_data_in);
	INPUT_BUFFER(config, m_scsi_ctrl_in);

	// software lists
	SOFTWARE_LIST(config, "flop_list").set_original("wmbullet");

	// internal ram
	RAM(config, RAM_TAG).set_default_size("128K");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( bullet )
//-------------------------------------------------

ROM_START( wmbullet )
	ROM_REGION( 0x10000, Z80_TAG, 0 )
	ROM_LOAD( "sr70x.u8", 0x00, 0x20, CRC(d54b8a30) SHA1(65ff8753dd63c9dd1899bc9364a016225585d050) )
ROM_END


#define rom_wmbulletf rom_wmbullet



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME       PARENT    COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY      FULLNAME               FLAGS
// the setname 'bullet' is used by Sega's Bullet in MAME.
COMP( 1982, wmbullet,  0,        0,      bullet,  bullet,  bullet_state,  empty_init, "Wave Mate", "Bullet",              MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1984, wmbulletf, wmbullet, 0,      bulletf, bulletf, bulletf_state, empty_init, "Wave Mate", "Bullet (Revision F)", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )



bungo.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
/**************************************************************************************************

NEC 文豪 / "Bungo" Word Processors (laptop family)

TODO:
- needs list of components, no documentation available from the net except for bare specs here:
  https://museum.ipsj.or.jp/en/computer/word/0058.html
- Needs an actual dump of the kanji ROM;
- Garbled message tells user to reset machine and hold SHIFT+MENU while having the
  auxiliary disk in
  https://www.leadedsolder.com/2022/10/15/pwp50sx-nec-mini5-psu-repair-pickup.html
  NB: MENU key doesn't exist with current PC98 keyboard device, also note that the usual
  I/O at $41-$43 is not polled (either expects an irq or perhaps they relocated)
- Verify what exactly the handwritten “Function 2 + F2 = Floppy.” printed on aux disk
  means.
- Verify how much of PC-98 this really uses if anything at all.

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

#include "emu.h"
#include "bungo.h"

void bungo_mini5sx_state::bungo_palette(palette_device &palette) const
{
	palette.set_pen_color(0, 160, 168, 160);
	palette.set_pen_color(1, 48, 56, 16);
}

uint32_t bungo_mini5sx_state::screen_update( screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect )
{
	for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
		for (int x = cliprect.min_x; x <= cliprect.max_x; x += 16)
		{
			u16 pen = bitswap<16>(m_gvram[(y*640+x)/16], 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7);

			for (int xi = 0; xi < 16; xi++)
			{
				u8 dot = (pen >> xi) & 1;
				bitmap.pix(y, x+xi) = m_palette->pen(dot);
			}
		}

	return 0;
}

// TODO: PoC offsets
// Reads this area for a pointer to the various alphabets that lives at $c0000 and onward
// The real ROM must have compressed GFX tables to bother like this.
u16 bungo_mini5sx_state::fake_dict_r(offs_t offset)
{
	switch(offset * 2)
	{
		case 0x08:
			return 0;
		case 0x0a:
			return 0x3c20; // hiragana
		case 0x0c:
			return 0x2000;
		case 0x0e:
			return 0x0048; // katakana

		//default:
		//  logerror("%04x\n", offset * 2);
	}

	return 0;
}

void bungo_mini5sx_state::mini5sx_map(address_map &map)
{
	map(0x00000, 0x9ffff).ram(); // 640 KB

	// same as PC98HA: it doesn't have 7220s but just a b&w framebuffer
	map(0xa8000, 0xaffff).ram().share("gvram");

	map(0xc0000, 0xcdfff).rom().region("kanji", 0);
	map(0xce000, 0xcffff).r(FUNC(bungo_mini5sx_state::fake_dict_r));

	map(0xe0000, 0xfffff).rom().region("ipl", 0);
}

void bungo_mini5sx_state::mini5sx_io(address_map &map)
{
	// ...
}

static INPUT_PORTS_START( mini5sx )
INPUT_PORTS_END

// debug
static const gfx_layout gfx_16x16x1 =
{
	16,16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP16(0,1) },
	{ STEP16(0,16) },
	16*16
};

static GFXDECODE_START( gfx_bungo )
	GFXDECODE_ENTRY( "kanji",   0x00000, gfx_8x8x1,   0x000, 0x01 )
	GFXDECODE_ENTRY( "kanji",   0x00000, gfx_16x16x1, 0x000, 0x01 )
GFXDECODE_END

void bungo_mini5sx_state::machine_start()
{
}

static void pc9801_floppies(device_slot_interface &device)
{
//  device.option_add("525dd", FLOPPY_525_DD);
//  device.option_add("525hd", FLOPPY_525_HD);
	device.option_add("35hd", FLOPPY_35_HD);
}



void bungo_mini5sx_state::mini5sx_config(machine_config &config)
{
	const XTAL xtal = XTAL(16'000'000);
	V33(config, m_maincpu, xtal);
	m_maincpu->set_addrmap(AS_PROGRAM, &bungo_mini5sx_state::mini5sx_map);
	m_maincpu->set_addrmap(AS_IO, &bungo_mini5sx_state::mini5sx_io);
//  m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	I8251(config, m_sio_kbd, 0);
//  m_sio_kbd->txd_handler().set("keyb", FUNC(pc9801_kbd_device::input_txd));
//  m_sio_kbd->rxrdy_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ1);

//  clock_device &kbd_clock(CLOCK(config, "kbd_clock", 19'200));
//  kbd_clock.signal_handler().set(m_sio_kbd, FUNC(i8251_device::write_rxc));
//  kbd_clock.signal_handler().append(m_sio_kbd, FUNC(i8251_device::write_txc));

	// TODO: should be PC-98 based with no numpad and some extra keys.
	PC98_KBD(config, m_keyb, 0);
//  m_keyb->rxd_callback().set("sio_kbd", FUNC(i8251_device::write_rxd));

	I8255(config, m_ppi_sys, 0);
//  m_ppi_sys->in_pa_callback().set(m_ppi_sys, FUNC(i8255_device::pa_r));
//  m_ppi_sys->in_pb_callback().set_ioport("SYSB");
//  m_ppi_sys->in_pc_callback().set_constant(0xa0); // 0x80 cpu triple fault reset flag?
//  m_ppi_sys->out_pc_callback().set(FUNC(pc98lt_state::ppi_sys_beep_portc_w));

	// TODO: unverified, known to have a 8-pin "sheet feeder" port
	pc9801_serial(config);

	I8255(config, m_ppi_prn, 0);
//  m_ppi_prn->in_pb_callback().set_ioport("PRNB");

	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	// TODO: copied verbatim from base PC98, verify clock et al.
	m_screen->set_raw(21.0526_MHz_XTAL, 848, 0, 640, 440, 0, 400);
	m_screen->set_screen_update(FUNC(bungo_mini5sx_state::screen_update));
//  m_screen->screen_vblank().set(FUNC(pc9801_state::vrtc_irq));

	PALETTE(config, m_palette, FUNC(bungo_mini5sx_state::bungo_palette), 2);
	GFXDECODE(config, m_gfxdecode, m_palette, gfx_bungo);

	UPD765A(config, m_fdc, 8'000'000, false, true);
//  m_fdc->intrq_wr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ6);
//  m_fdc->drq_wr_callback().set(m_maincpu, FUNC(v50_device::dreq_w<2>)).invert();
//  m_fdc->drq_wr_callback().set(m_maincpu, FUNC(v50_device::dreq_w<3>)).invert(); // 2dd

	FLOPPY_CONNECTOR(config, "upd765:0", pc9801_floppies, "35hd", pc9801_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "upd765:1", pc9801_floppies, "35hd", pc9801_state::floppy_formats);

	SOFTWARE_LIST(config, "disk_list").set_original("bungo_flop");

	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 2400).add_route(ALL_OUTPUTS, "mono", 0.05);
}

ROM_START( mini5sx )
	ROM_REGION16_LE( 0x100000, "biosrom", ROMREGION_ERASEFF )
	ROM_LOAD16_BYTE( "upd23c4001eacz-048.ic16", 0x000000, 0x080000, CRC(c7ce7ad6) SHA1(1f8616c1c5f817030decda539b9561fab2eef327) )
	ROM_LOAD16_BYTE( "upd23c4001eacz-049.ic19", 0x000001, 0x080000, CRC(09740f3e) SHA1(18ece5fd79392fe86c85007192ca0702728b004d) )

	ROM_REGION16_LE( 0x20000, "ipl", ROMREGION_ERASEFF )
	ROM_COPY( "biosrom", 0x60000, 0x00000, 0x20000 )

	// TODO: none of these is actually provided in dump, backported from pc98ha for now
	ROM_REGION16_LE( 0x40000, "kanji", ROMREGION_ERASEFF )
	ROM_LOAD( "kanji.rom",    0x000000, 0x040000, BAD_DUMP CRC(4be5ff2f) SHA1(261d28419a2ddebe3177a282952806d7bb036b40) )

	ROM_REGION16_LE( 0x40000, "backup", ROMREGION_ERASEFF )
	ROM_LOAD( "backup.bin",   0x000000, 0x040000, BAD_DUMP CRC(3c5b2a99) SHA1(f8e2f5a4c7601d4e81d5e9c83621107ed3f5a29a) )

	ROM_REGION( 0x100000, "dict", ROMREGION_ERASEFF )
	ROM_LOAD( "dict.rom",     0x000000, 0x0c0000, BAD_DUMP CRC(6dc8493c) SHA1(3e04cdc3403a814969b6590cd78e239e72677fe5) )

	ROM_REGION( 0x100000, "romdrv", ROMREGION_ERASEFF )
	ROM_COPY( "biosrom", 0x00000, 0x00000, 0x100000 )

	// $00 filled with odd size
	ROM_REGION( 0x200000, "ramdrv", ROMREGION_ERASEFF )
	ROM_LOAD( "ramdrv.bin",   0x000000, 0x160000, BAD_DUMP CRC(f2cec994) SHA1(c986ad6d8f810ac0a9657c1af26b6fec712d56ed) )
ROM_END

COMP( 1991, mini5sx,     0,        0, mini5sx_config,    mini5sx,   bungo_mini5sx_state, empty_init,   "NEC",   "Bungo mini 5SX", MACHINE_NOT_WORKING )



busicom.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

Busicom 141-PF

2009-08-04 Initial driver by Miodrag Milanovic

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

#include "emu.h"
#include "busicom.h"

#include "screen.h"

#include <algorithm>


uint8_t busicom_state::get_bit_selected(uint32_t val, int num)
{
	for (int i = 0; i < num; i++)
		if (!BIT(val, i))
			return i;

	return 0;
}

uint8_t busicom_state::keyboard_r()
{
	return m_input_lines[get_bit_selected(m_keyboard_shifter & 0x3ff, 10)]->read();
}

uint8_t busicom_state::printer_r()
{
	uint8_t retVal = 0;
	if (m_drum_index == 0)
		retVal |= 1;
	retVal |= (ioport("PAPERADV")->read() & 1) ? 8 : 0;
	return retVal;
}


void busicom_state::shifter_w(uint8_t data)
{
	// FIXME: detect edges, maybe make 4003 shifter a device
	if (BIT(data, 0))
	{
		m_keyboard_shifter <<= 1;
		m_keyboard_shifter |= BIT(data, 1);
	}

	if (BIT(data, 2))
	{
		m_printer_shifter <<= 1;
		m_printer_shifter |= BIT(data, 1);
	}
}

void busicom_state::printer_w(uint8_t data)
{
	if (BIT(data, 0))
	{
		logerror("color : %02x %02x %d\n", BIT(data, 0), data, m_drum_index);
		m_printer_line_color[10] = 1;
	}

	if (BIT(data, 1))
	{
		for (u8 i = 3; i < 18; i++)
			if (BIT(m_printer_shifter, i))
				m_printer_line[10][i - 3] = m_drum_index + 1;

		if (BIT(m_printer_shifter, 0))
			m_printer_line[10][15] = m_drum_index + 13 + 1;

		if (BIT(m_printer_shifter, 1))
			m_printer_line[10][16] = m_drum_index + 26 + 1;
	}

	if (BIT(data, 3))
	{
		for (u8 j = 0; j < (std::size(m_printer_line) - 1); j++)
			std::copy(std::begin(m_printer_line[j + 1]), std::end(m_printer_line[j + 1]), std::begin(m_printer_line[j]));
		std::copy_n(&m_printer_line_color[1], std::size(m_printer_line_color) - 1, &m_printer_line_color[0]);

		std::fill(std::begin(m_printer_line[10]), std::end(m_printer_line[10]), 0);
		m_printer_line_color[10] = 0;
	}
}

void busicom_state::status_w(uint8_t data)
{
#if 0
	uint8_t mem_lamp = BIT(data, 0);
	uint8_t over_lamp = BIT(data, 1);
	uint8_t minus_lamp = BIT(data, 2);
#endif
	//logerror("status %c %c %c\n", mem_lamp ? 'M':'x', over_lamp ? 'O':'x', minus_lamp ? '-':'x');
}

void busicom_state::printer_ctrl_w(uint8_t data)
{
}

void busicom_state::busicom_rom(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x04FF).rom().region("maincpu", 0);
}

void busicom_state::busicom_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07F).ram();
}

void busicom_state::busicom_stat(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x01F).ram();
}

void busicom_state::busicom_rp(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x000f).mirror(0x0700).w(FUNC(busicom_state::shifter_w)); // ROM0 I/O
	map(0x0010, 0x001f).mirror(0x0700).rw(FUNC(busicom_state::keyboard_r), FUNC(busicom_state::printer_ctrl_w)); // ROM1 I/O
	map(0x0020, 0x002f).mirror(0x0700).r(FUNC(busicom_state::printer_r)); // ROM2 I/O
}

void busicom_state::busicom_mp(address_map &map)
{
	map(0x00, 0x00).w(FUNC(busicom_state::printer_w)); // RAM0 output
	map(0x01, 0x01).w(FUNC(busicom_state::status_w));  // RAM1 output
}

/* Input ports */
static INPUT_PORTS_START( busicom )
	PORT_START("LINE0")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CM") PORT_CODE(KEYCODE_1)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RM") PORT_CODE(KEYCODE_2)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M-") PORT_CODE(KEYCODE_3)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M+") PORT_CODE(KEYCODE_4)
	PORT_START("LINE1")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SQRT") PORT_CODE(KEYCODE_S)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("%") PORT_CODE(KEYCODE_5)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M=-") PORT_CODE(KEYCODE_6)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M=+") PORT_CODE(KEYCODE_7)
	PORT_START("LINE2")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("diamond") PORT_CODE(KEYCODE_D)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH_PAD)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("*") PORT_CODE(KEYCODE_ASTERISK)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("=") PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_START("LINE3")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS_PAD)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+") PORT_CODE(KEYCODE_PLUS_PAD)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("diamond 2") PORT_CODE(KEYCODE_TILDE)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("000") PORT_CODE(KEYCODE_8)
	PORT_START("LINE4")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9_PAD)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6_PAD)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3_PAD)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_DEL_PAD)
	PORT_START("LINE5")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8_PAD)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5_PAD)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2_PAD)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("00") PORT_CODE(KEYCODE_9)
	PORT_START("LINE6")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7_PAD)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4_PAD)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1_PAD)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0_PAD)
	PORT_START("LINE7")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Sign") PORT_CODE(KEYCODE_RALT) PORT_CODE(KEYCODE_LALT)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("EX") PORT_CODE(KEYCODE_X)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CE") PORT_CODE(KEYCODE_E)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
	PORT_START("LINE8")
		PORT_CONFNAME( 0x0f, 0x00, "Digital point")
			PORT_CONFSETTING( 0x00, "0" )
			PORT_CONFSETTING( 0x01, "1" )
			PORT_CONFSETTING( 0x02, "2" )
			PORT_CONFSETTING( 0x03, "3" )
			PORT_CONFSETTING( 0x04, "4" )
			PORT_CONFSETTING( 0x05, "5" )
			PORT_CONFSETTING( 0x06, "6" )
			PORT_CONFSETTING( 0x08, "8" )
	PORT_START("LINE9")
		PORT_CONFNAME( 0x0f, 0x00, "Rounding")
			PORT_CONFSETTING( 0x01, "/N" )
			PORT_CONFSETTING( 0x00, "FL" )
			PORT_CONFSETTING( 0x08, "5/4" )
	PORT_START("PAPERADV")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Paper adv.") PORT_CODE(KEYCODE_SPACE)

INPUT_PORTS_END


TIMER_DEVICE_CALLBACK_MEMBER(busicom_state::timer_callback)
{
	m_timer ^= 1;
	if (m_timer == 1)
		m_drum_index++;
	if (m_drum_index == 13)
		m_drum_index = 0;
	m_maincpu->set_input_line(I4004_TEST_LINE, m_timer);
}

void busicom_state::machine_start()
{
	save_item(NAME(m_drum_index));
	save_item(NAME(m_keyboard_shifter));
	save_item(NAME(m_printer_shifter));
	save_item(NAME(m_timer));
	save_item(NAME(m_printer_line));
	save_item(NAME(m_printer_line_color));
}

void busicom_state::machine_reset()
{
	m_timer = 0;
	m_drum_index = 0;
	m_keyboard_shifter = 0;
	m_printer_shifter = 0;

	for (int i = 0; i < std::size(m_printer_line); i++)
		std::fill(std::begin(m_printer_line[i]), std::end(m_printer_line[i]), 0);
	std::fill(std::begin(m_printer_line_color), std::end(m_printer_line_color), 0);
}

void busicom_state::busicom(machine_config &config)
{
	// basic machine hardware
	I4004(config, m_maincpu, 750000);
	m_maincpu->set_rom_map(&busicom_state::busicom_rom);
	m_maincpu->set_ram_memory_map(&busicom_state::busicom_mem);
	m_maincpu->set_rom_ports_map(&busicom_state::busicom_rp);
	m_maincpu->set_ram_status_map(&busicom_state::busicom_stat);
	m_maincpu->set_ram_ports_map(&busicom_state::busicom_mp);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // not accurate
	screen.set_size(40*17, 44*11);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(busicom_state::screen_update_busicom));
	screen.set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(busicom_state::busicom_palette), 16);

	TIMER(config, "busicom_timer").configure_periodic(FUNC(busicom_state::timer_callback), attotime::from_msec(28*2));
}

// ROM definition
ROM_START( busicom )
	ROM_REGION( 0x0500, "maincpu", 0 )
	ROM_LOAD( "busicom.l01", 0x0000, 0x0100, CRC(51ae2513) SHA1(5cb4097a3945db35af4ed64b629b20b08fc9824f))
	ROM_LOAD( "busicom.l02", 0x0100, 0x0100, CRC(a05411ad) SHA1(81503a99a0d34fa29bf1245de0a44af2f174abdd))
	ROM_LOAD( "busicom.l05", 0x0200, 0x0100, CRC(6120addf) SHA1(4b7ec183613630120b3c313c782122713d4327c5))
	ROM_LOAD( "busicom.l07", 0x0300, 0x0100, CRC(84a90daa) SHA1(e2931753b0fd35144cb5a9d73fcae8e104e5e3ed))
	ROM_LOAD( "busicom.l11", 0x0400, 0x0100, CRC(4d2b2942) SHA1(9a59db76eff084369797735ec19da8cbc70d0d39))
ROM_END

// Driver

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY                          FULLNAME          FLAGS
COMP( 1974, busicom, 0,      0,      busicom, busicom, busicom_state, empty_init, "Business Computer Corporation", "Busicom 141-PF", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



bvm.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Sony BVM-{14|20}{E|F}1{E|U} series monitors.

    List of major ICs:
    - Hitachi HD6435368CP10 H8/536 [IC1]
      or: Hitachi HD6475368CP10 C764
      or: Hitachi HD6435368CP10 W51
      near 20 MHz XTAL
    - CSI CAT28F020P-15 2 Megabit CMOS Flash Memory [IC3]
    - Sony CXK58257AP-10 [IC4]
      or: Sanyo LC35256D-10
      next to: Sony CR2025 3V Lithium Cell [BAT1]
    - Sony CXD1095Q [IC5, IC6]
    - NEC D6453GT-101 On-Screen Character Display [IC7]
    - Sony CXA1727Q [IC102]
    - Sony CXD2343S [IC105]
    - Hitachi HN27C256AG-10 [IC107, IC108]
    - Sony CXD1171M 8-Bit D/A Converter [IC109]
    - Sony CXD1030M Sync Signal Generator [IC120]
    - Zilog Z8622812PSC Line 21 Closed-Caption Controller [IC124]
    - Sony CXD1132Q Time Code Generator/Reader [IC126]

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

#include "emu.h"
#include "cpu/h8500/h8534.h"
#include "machine/cxd1095.h"
#include "machine/intelfsh.h"
#include "machine/nvram.h"


namespace {

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

	void bvm(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<h8536_device> m_maincpu;
};


void bvm_state::mem_map(address_map &map)
{
	map(0x00000, 0x0f67f).rw("flash", FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0x10000, 0x17fff).ram().share("nvram");
	map(0x18000, 0x18007).rw("cxdio0", FUNC(cxd1095_device::read), FUNC(cxd1095_device::write));
	map(0x18020, 0x18027).rw("cxdio1", FUNC(cxd1095_device::read), FUNC(cxd1095_device::write));
	map(0x40000, 0x7ffff).rw("flash", FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
}


static INPUT_PORTS_START(bvm)
INPUT_PORTS_END

void bvm_state::bvm(machine_config &config)
{
	HD6435368(config, m_maincpu, 20_MHz_XTAL);
	m_maincpu->set_mode(3); // internal ROM not used here?
	m_maincpu->set_addrmap(AS_PROGRAM, &bvm_state::mem_map);

	CAT28F020(config, "flash");

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	CXD1095(config, "cxdio0");
	CXD1095(config, "cxdio1");
}

ROM_START(bvm20f1e)
	ROM_REGION(0x40000, "flash", 0)
	ROM_LOAD("cat28f020p-15.ic3", 0x00000, 0x40000, CRC(43a1bdf8) SHA1(65cf61921ac86a8baa3a3da4ed0b3e61aa2f03b3))

	ROM_REGION(0x10000, "eproms", 0)
	ROM_LOAD("cd93-27c256.ic107", 0x0000, 0x8000, CRC(f8da1f2f) SHA1(b2f5a730d0f26f2a12fd62a111ee429da8426877))
	ROM_LOAD("541d-27c256.ic108", 0x8000, 0x8000, CRC(9da347f9) SHA1(413096830bdcae6404e9d686abb56e60d58bdc2f))
ROM_END

} // anonymous namespace


SYST(1998, bvm20f1e, 0, 0, bvm, bvm, bvm_state, empty_init, "Sony", "Trinitron Color Video Monitor BVM-20F1E", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



bw12.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/***************************************************************************

    Bondwell 12/14

    12/05/2009 Skeleton driver.

    - Z80A CPU 4MHz
    - 64KB RAM (BW 12), 128KB RAM (BW 14)
    - 4KB ROM System
    - UPD765A Floppy controller
    - 2 x 5.25" Floppy drives 48 tpi SSDD (BW 12), DSDD (BW 14)
    - MC6845 Video controller
    - 2KB RAM Video buffer
    - 4KB ROM Character set
    - Z80SIO Serial interface
    - MC6821 Parallel interface
    - I8253 Counter-timer
    - MC1408 8-bit DAC sound
    - KB3600 PRO (AY-5-3600 PRO) Keyboard controller

    http://www.eld.leidenuniv.nl/~moene/Home/sitemap/
    http://www.baltissen.org/newhtm/schemas.htm

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

#include "emu.h"
#include "bw12.h"
#include "bus/rs232/rs232.h"
#include "sound/dac.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "utf8.h"

/*

    TODO:

    - floppy motor off timer

*/

void bw12_state::bankswitch()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	switch (m_curbank)
	{
	case 0: /* ROM */
		program.install_read_bank(0x0000, 0x7fff, m_bank);
		program.unmap_write(0x0000, 0x7fff);
		break;

	case 1: /* BK0 */
		program.install_readwrite_bank(0x0000, 0x7fff, m_bank);
		break;

	case 2: /* BK1 */
	case 3: /* BK2 */
		if (m_ram->size() > 64*1024)
		{
			program.install_readwrite_bank(0x0000, 0x7fff, m_bank);
		}
		else
		{
			program.unmap_readwrite(0x0000, 0x7fff);
		}
		break;
	}

	m_bank->set_entry(m_curbank);
}

void bw12_state::floppy_motor_on_off()
{
	if (m_motor0 || m_motor1)
	{
		m_motor_on = 1;
		m_floppy[0]->mon_w(0);
		m_floppy[1]->mon_w(0);
		m_floppy_timer->adjust(attotime::never);
	}
	else
		m_floppy_timer->adjust(attotime::from_msec(170));
}

TIMER_DEVICE_CALLBACK_MEMBER(bw12_state::floppy_motor_off_tick)
{
	if (m_motor0 || m_motor1)
		return;
	m_floppy[0]->mon_w(1);
	m_floppy[1]->mon_w(1);

	m_motor_on = 0;
}

void bw12_state::ls138_a0_w(int state)
{
	m_curbank = (m_curbank & 0x02) | state;
	bankswitch();
}

void bw12_state::ls138_a1_w(int state)
{
	m_curbank = (state << 1) | (m_curbank & 0x01);
	bankswitch();
}

void bw12_state::init_w(int state)
{
}

void bw12_state::motor0_w(int state)
{
	m_motor0 = state;
	floppy_motor_on_off();
}

void bw12_state::motor1_w(int state)
{
	m_motor1 = state;
	floppy_motor_on_off();
}

uint8_t bw12_state::ls259_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		m_latch->write_bit(offset >> 1, BIT(offset, 0));

	return 0;
}

/* Memory Maps */

void bw12_state::bw12_mem(address_map &map)
{
	map(0x0000, 0x7fff).bankrw("bank");
	map(0x8000, 0xf7ff).ram();
	map(0xf800, 0xffff).ram().share("video_ram");
}

void bw12_state::bw12_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x0f).r(FUNC(bw12_state::ls259_r)).w(m_latch, FUNC(ls259_device::write_a0));
	map(0x10, 0x10).mirror(0x0e).w(m_crtc, FUNC(mc6845_device::address_w));
	map(0x11, 0x11).mirror(0x0e).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x20, 0x21).mirror(0x0e).m(m_fdc, FUNC(upd765a_device::map));
	map(0x30, 0x33).mirror(0x0c).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x40, 0x43).mirror(0x0c).rw(m_sio, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x50, 0x50).mirror(0x0f).w("dac", FUNC(dac_byte_interface::data_w));
	map(0x60, 0x63).mirror(0x0c).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
}

/* Input Ports */

static INPUT_PORTS_START( bw12 )
	/*

	  KB3600 PRO2 Keyboard matrix

	      | Y0  | Y1  | Y2  | Y3  | Y4  | Y5  | Y6  | Y7  | Y8  | Y9  |
	      |     |     |     |     |     |     |     |     |     |     |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X0  |  7  |  8  |  9  |  0  |  1  |  2  |  3  |  4  |  5  |  6  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X1  |  U  |  I  |  O  |  P  |  Q  |  W  |  E  |  R  |  T  |  Y  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X2  | F15 | F16 | RET | N.  | SP  | LOCK| F11 | F12 | F13 | F14 |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X3  | F7  | F8  | F9  | F10 | F1  | F2  | F3  | F4  | F5  | F6  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X4  | LEFT|RIGHT| N3  | BS  |  @  |     |  -  |  ]  | UP  | DOWN|
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X5  | N9  | CL  | N2  | LF  | DEL | HT  |ARROW|  [  | N7  | N8  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X6  |  M  |  ,  |  .  |  /  |  Z  |  X  |  C  |  V  |  B  |  N  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X7  |  J  |  K  |  L  |  ;  |  A  |  S  |  D  |  F  |  G  |  H  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X8  | N6  |  -  | N1  | N0  | ESC |     |  :  | NRET| N4  | N5  |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|

	*/

	PORT_START("X0")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('|')
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')

	PORT_START("X1")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')

	PORT_START("X2")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F15")
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F16")
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad .") PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CAPS LOCK") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F11") PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11))
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F12") PORT_CODE(KEYCODE_F12) PORT_CHAR(UCHAR_MAMEKEY(F12))
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F13")
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F14")

	PORT_START("X3")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F7") PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F8") PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F9") PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F10") PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F5") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F6") PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))

	PORT_START("X4")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("BS") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('@') PORT_CHAR('\\')
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))

	PORT_START("X5")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CL")
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("LF")
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL)
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("HT")
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 8") PORT_CODE(KEYCODE_8_PAD)

	PORT_START("X6")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')

	PORT_START("X7")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')

	PORT_START("X8")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC)
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad RET") PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 5") PORT_CODE(KEYCODE_5_PAD)

	PORT_START("MODIFIERS")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
INPUT_PORTS_END

/* Video */

MC6845_UPDATE_ROW( bw12_state::crtc_update_row )
{
	pen_t const *const pen = m_palette->pens();

	for (int column = 0; column < x_count; column++)
	{
		uint8_t const code = m_video_ram[((ma + column) & BW12_VIDEORAM_MASK)];
		uint16_t const addr = code << 4 | (ra & 0x0f);
		uint8_t data = m_char_rom->base()[addr & BW12_CHARROM_MASK];

		if (column == cursor_x)
		{
			data = 0xff;
		}

		for (int bit = 0; bit < 8; bit++)
		{
			int const x = (column * 8) + bit;
			int const color = BIT(data, 7) && de;

			bitmap.pix(vbp + y, hbp + x) = pen[color];

			data <<= 1;
		}
	}
}


/* PIA6821 Interface */

void bw12_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

void bw12_state::write_centronics_fault(int state)
{
	m_centronics_fault = state;
}

void bw12_state::write_centronics_perror(int state)
{
	m_centronics_perror = state;
}

uint8_t bw12_state::pia_pa_r()
{
	/*

	    bit     description

	    PA0     Input from Centronics BUSY status
	    PA1     Input from Centronics ERROR status
	    PA2     Input from Centronics PAPER OUT status
	    PA3     Input from FDC MOTOR
	    PA4     Input from PIT OUT2
	    PA5     Input from keyboard strobe
	    PA6     Input from keyboard serial data
	    PA7     Input from FDC interrupt

	*/

	uint8_t data = 0;

	data |= m_centronics_busy;
	data |= (m_centronics_fault << 1);
	data |= (m_centronics_perror << 2);
	data |= (m_motor_on << 3);
	data |= (m_pit_out2 << 4);
	data |= (m_key_stb << 5);
	data |= (m_key_sin << 6);
	data |= (m_fdc->get_irq() ? 1 : 0) << 7;

	return data;
}

void bw12_state::pia_cb2_w(int state)
{
	if (state)
	{
		/* keyboard shift clock */
		m_key_shift++;

		if (m_key_shift < 9)
		{
			m_key_sin = m_key_data[m_key_shift];
		}
	}
}

/* PIT8253 Interface */

void bw12_state::pit_out2_w(int state)
{
	m_pit_out2 = state;
}

/* AY-5-3600-PRO-002 Interface */

int bw12_state::ay3600_shift_r()
{
	return BIT(m_modifiers->read(), 0);
}

int bw12_state::ay3600_control_r()
{
	return BIT(m_modifiers->read(), 1);
}

void bw12_state::ay3600_data_ready_w(int state)
{
	m_key_stb = state;

	m_pia->cb1_w(state);

	if (state)
	{
		uint16_t data = m_kbc->b_r();

		m_key_data[0] = BIT(data, 6);
		m_key_data[1] = BIT(data, 3);
		m_key_data[2] = BIT(data, 1);
		m_key_data[3] = BIT(data, 0);
		m_key_data[4] = BIT(data, 2);
		m_key_data[5] = BIT(data, 4);
		m_key_data[6] = BIT(data, 5);
		m_key_data[7] = BIT(data, 7);
		m_key_data[8] = BIT(data, 8);

		m_key_shift = 0;
		m_key_sin = m_key_data[m_key_shift];
	}
}

/* Machine Initialization */

void bw12_state::machine_start()
{
	/* setup memory banking */
	m_bank->configure_entry(0, m_rom->base());
	m_bank->configure_entry(1, m_ram->pointer());
	m_bank->configure_entries(2, 2, m_ram->pointer() + 0x10000, 0x8000);

	/* register for state saving */
	save_item(NAME(m_curbank));
	save_item(NAME(m_pit_out2));
	save_item(NAME(m_key_data));
	save_item(NAME(m_key_sin));
	save_item(NAME(m_key_stb));
	save_item(NAME(m_key_shift));
	save_item(NAME(m_motor_on));
	save_item(NAME(m_motor0));
	save_item(NAME(m_motor1));
	save_item(NAME(m_centronics_busy));
	save_item(NAME(m_centronics_fault));
	save_item(NAME(m_centronics_perror));
	machine().save().register_postload(save_prepost_delegate(FUNC(bw12_state::bankswitch), this));
}

void bw12_state::machine_reset()
{
	m_key_stb = 0;
	m_key_shift = 0;
}

static void bw12_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_SSDD);
}

void bw12_state::bw12_floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_BW12_FORMAT);
}

static void bw14_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

void bw12_state::bw14_floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_BW12_FORMAT);
}


/* F4 Character Displayer */
static const gfx_layout bw12_charlayout =
{
	8, 9,                   /* 8 x 9 characters */
	256,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_bw12 )
	GFXDECODE_ENTRY( "chargen", 0x0000, bw12_charlayout, 0, 1 )
GFXDECODE_END


/* Machine Driver */
void bw12_state::common(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &bw12_state::bw12_mem);
	m_maincpu->set_addrmap(AS_IO, &bw12_state::bw12_io);

	LS259(config, m_latch); // IC18
	m_latch->q_out_cb<0>().set(FUNC(bw12_state::ls138_a0_w)); // LS138 A0
	m_latch->q_out_cb<1>().set(FUNC(bw12_state::ls138_a1_w)); // LS138 A1
	// Q2 not connected
	m_latch->q_out_cb<3>().set(FUNC(bw12_state::init_w)); // _INIT
	m_latch->q_out_cb<4>().set_output("led0"); // CAP LOCK
	m_latch->q_out_cb<5>().set(FUNC(bw12_state::motor0_w)); // MOTOR 0
	m_latch->q_out_cb<6>().set(FUNC(bw12_state::motor1_w)); // MOTOR 1
	m_latch->q_out_cb<7>().set(m_fdc, FUNC(upd765a_device::tc_line_w)); // FDC TC

	/* video hardware */
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER, rgb_t::amber()));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // not accurate
	screen.set_screen_update(MC6845_TAG, FUNC(mc6845_device::screen_update));
	screen.set_size(640, 200);
	screen.set_visarea(0, 640-1, 0, 200-1);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_bw12);
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	MC6845(config, m_crtc, 16_MHz_XTAL / 8);
	m_crtc->set_screen(SCREEN_TAG);
	m_crtc->set_show_border_area(true);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(bw12_state::crtc_update_row));

	/* sound hardware */
	SPEAKER(config, "speaker").front_center();
	MC1408(config, "dac", 0).add_route(ALL_OUTPUTS, "speaker", 0.125); // ls273.ic5 + mc1408.ic4

	/* devices */
	TIMER(config, FLOPPY_TIMER_TAG).configure_generic(FUNC(bw12_state::floppy_motor_off_tick));
	UPD765A(config, m_fdc, 16_MHz_XTAL / 4, false, true);

	PIA6821(config, m_pia);
	m_pia->readpa_handler().set(FUNC(bw12_state::pia_pa_r));
	m_pia->writepb_handler().set("cent_data_out", FUNC(output_latch_device::write));
	m_pia->ca2_handler().set(CENTRONICS_TAG, FUNC(centronics_device::write_strobe));
	m_pia->cb2_handler().set(FUNC(bw12_state::pia_cb2_w));
	m_pia->irqa_handler().set_inputline(Z80_TAG, INPUT_LINE_IRQ0);
	m_pia->irqb_handler().set_inputline(Z80_TAG, INPUT_LINE_IRQ0);

	Z80SIO(config, m_sio, 16_MHz_XTAL / 4); // SIO/0
	m_sio->out_txda_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_txd));
	m_sio->out_dtra_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_dtr));
	m_sio->out_rtsa_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_rts));
	m_sio->out_txdb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_txd));
	m_sio->out_dtrb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_dtr));
	m_sio->out_rtsb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_rts));
	m_sio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	PIT8253(config, m_pit);
	m_pit->set_clk<0>(1.8432_MHz_XTAL);
	m_pit->out_handler<0>().set(m_sio, FUNC(z80sio_device::rxca_w));
	m_pit->out_handler<0>().append(m_sio, FUNC(z80sio_device::txca_w));
	m_pit->out_handler<0>().append(RS232_A_TAG, FUNC(rs232_port_device::write_etc));
	m_pit->set_clk<1>(1.8432_MHz_XTAL);
	m_pit->out_handler<1>().set(m_sio, FUNC(z80sio_device::rxtxcb_w));
	m_pit->out_handler<1>().append(RS232_B_TAG, FUNC(rs232_port_device::write_etc));
	m_pit->set_clk<2>(1.8432_MHz_XTAL);
	m_pit->out_handler<2>().set(FUNC(bw12_state::pit_out2_w));

	AY3600(config, m_kbc, 0);
	m_kbc->x0().set_ioport("X0");
	m_kbc->x1().set_ioport("X1");
	m_kbc->x2().set_ioport("X2");
	m_kbc->x3().set_ioport("X3");
	m_kbc->x4().set_ioport("X4");
	m_kbc->x5().set_ioport("X5");
	m_kbc->x6().set_ioport("X6");
	m_kbc->x7().set_ioport("X7");
	m_kbc->x8().set_ioport("X8");
	m_kbc->shift().set(FUNC(bw12_state::ay3600_shift_r));
	m_kbc->control().set(FUNC(bw12_state::ay3600_control_r));
	m_kbc->data_ready().set(FUNC(bw12_state::ay3600_data_ready_w));

	rs232_port_device &rs232a(RS232_PORT(config, RS232_A_TAG, default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_sio, FUNC(z80sio_device::rxa_w));
	rs232a.dcd_handler().set(m_sio, FUNC(z80sio_device::dcda_w));
	rs232a.cts_handler().set(m_sio, FUNC(z80sio_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, RS232_B_TAG, default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_sio, FUNC(z80sio_device::rxb_w));
	rs232b.dcd_handler().set(m_sio, FUNC(z80sio_device::dcdb_w));
	rs232b.cts_handler().set(m_sio, FUNC(z80sio_device::ctsb_w));

	/* printer */
	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set(m_pia, FUNC(pia6821_device::ca1_w));
	m_centronics->busy_handler().set(FUNC(bw12_state::write_centronics_busy));
	m_centronics->fault_handler().set(FUNC(bw12_state::write_centronics_fault));
	m_centronics->perror_handler().set(FUNC(bw12_state::write_centronics_perror));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);
}

void bw12_state::bw12(machine_config &config)
{
	common(config);
	/* floppy drives */
	FLOPPY_CONNECTOR(config, UPD765_TAG ":1", bw12_floppies, "525dd", bw12_state::bw12_floppy_formats);
	FLOPPY_CONNECTOR(config, UPD765_TAG ":2", bw12_floppies, "525dd", bw12_state::bw12_floppy_formats);

	// software lists
	SOFTWARE_LIST(config, "flop_list").set_original("bw12");

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("64K");
}

void bw12_state::bw14(machine_config &config)
{
	common(config);
	/* floppy drives */
	FLOPPY_CONNECTOR(config, UPD765_TAG ":1", bw14_floppies, "525dd", bw12_state::bw14_floppy_formats);
	FLOPPY_CONNECTOR(config, UPD765_TAG ":2", bw14_floppies, "525dd", bw12_state::bw14_floppy_formats);

	// software lists
	SOFTWARE_LIST(config, "flop_list").set_original("bw14");

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("128K");
	}

/* ROMs */

ROM_START( bw12 )
	ROM_REGION( 0x10000, Z80_TAG, 0 )
	ROM_LOAD( "bw14boot.ic41", 0x0000, 0x1000, CRC(782fe341) SHA1(eefe5ad6b1ef77a1caf0af743b74de5fa1c4c19d) )

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "bw14char.ic1",  0x0000, 0x1000, CRC(f9dd68b5) SHA1(50132b759a6d84c22c387c39c0f57535cd380411) )
ROM_END

#define rom_bw14 rom_bw12

ROM_START( bw14d )
	ROM_REGION( 0x10000, Z80_TAG, 0 )
	ROM_LOAD( "bw14boot.ic41", 0x0000, 0x1000, CRC(782fe341) SHA1(eefe5ad6b1ef77a1caf0af743b74de5fa1c4c19d) )

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "gcrd.bin",  0x0000, 0x1000, CRC(638f3e1d) SHA1(5a0b2f47c66fe8db6f58d348ac29074a4db51258) )
ROM_END

/* System Drivers */

/*    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY             FULLNAME       FLAGS */
COMP( 1984, bw12,  0,      0,      bw12,    bw12,  bw12_state, empty_init, "Bondwell Holding", "Bondwell 12", MACHINE_SUPPORTS_SAVE )
COMP( 1984, bw14,  bw12,   0,      bw14,    bw12,  bw12_state, empty_init, "Bondwell Holding", "Bondwell 14", MACHINE_SUPPORTS_SAVE )
COMP( 1984, bw14d, bw12,   0,      bw14,    bw12,  bw12_state, empty_init, "Bondwell Holding", "Bondwell Portable Computer Model 14 (German keyboard)", MACHINE_SUPPORTS_SAVE )



bw2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/***************************************************************************

    drivers/bw2.c

    Bondwell BW 2

    - Z80L CPU 4MHz
    - 64KB RAM, expandable to 224KB RAM.
    - 4KB System ROM
    - MSM6255 LCD controller, 640x200 pixels
    - 16KB Video RAM
    - TMS2797 FDC controller
    - 8251 USART serial interface
    - 8253 PIT timer
    - 8255 PPI

  http://www.thebattles.net/bondwell/

  http://www.vintage-computer.com/bondwell2.shtml

  http://www2.okisemi.com/site/productscatalog/displaydrivers/availabledocuments/Intro-7090.html

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

#include "emu.h"
#include "bw2.h"
#include "bus/rs232/rs232.h"
#include "screen.h"
#include "softlist_dev.h"


//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

enum
{
	RAM1 = 0,
	VRAM,
	RAM2,
	RAM3,
	RAM4,
	RAM5,
	RAM6,
	ROM
};

#define HAS_KB_OF_RAM(_kb) \
	(m_ram->size() >= (_kb * 1024))


//**************************************************************************
//  ADDRESS DECODING
//**************************************************************************

//-------------------------------------------------
//  read -
//-------------------------------------------------

uint8_t bw2_state::read(offs_t offset)
{
	int rom = 1, vram = 1, ram1 = 1, ram2 = 1, ram3 = 1, ram4 = 1, ram5 = 1, ram6 = 1;

	uint8_t data = 0xff;

	switch (m_bank)
	{
	case RAM1: ram1 = 0; break;
	case VRAM: vram = 0; break;
	case RAM2: ram2 = 0; break;
	case RAM3: ram3 = 0; break;
	case RAM4: ram4 = 0; break;
	case RAM5: ram5 = 0; break;
	case RAM6: ram6 = 0; break;
	case ROM: rom = 0; break;
	}

	if (offset < 0x8000)
	{
		if (!rom)
			data = m_rom->base()[offset & 0x3fff];

		if (!vram)
			data = m_video_ram[offset & 0x3fff];

		if (!ram1)
			data = m_ram->pointer()[offset];

		if (!ram2 && HAS_KB_OF_RAM(96))
			data = m_ram->pointer()[0x10000 | offset];

		if (!ram3 && HAS_KB_OF_RAM(128))
			data = m_ram->pointer()[0x18000 | offset];

		if (!ram4 && HAS_KB_OF_RAM(160))
			data = m_ram->pointer()[0x20000 | offset];

		if (!ram5 && HAS_KB_OF_RAM(192))
			data = m_ram->pointer()[0x28000 | offset];

		if (!ram6 && HAS_KB_OF_RAM(224))
			data = m_ram->pointer()[0x30000 | offset];
	}
	else
	{
		data = m_ram->pointer()[offset];
	}

	return m_exp->cd_r(offset, data, ram2, ram3, ram4, ram5, ram6);
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

void bw2_state::write(offs_t offset, uint8_t data)
{
	int vram = 1, ram1 = 1, ram2 = 1, ram3 = 1, ram4 = 1, ram5 = 1, ram6 = 1;

	switch (m_bank)
	{
	case RAM1: ram1 = 0; break;
	case VRAM: vram = 0; break;
	case RAM2: ram2 = 0; break;
	case RAM3: ram3 = 0; break;
	case RAM4: ram4 = 0; break;
	case RAM5: ram5 = 0; break;
	case RAM6: ram6 = 0; break;
	}

	if (offset < 0x8000)
	{
		if (!vram)
			m_video_ram[offset & 0x3fff] = data;

		if (!ram1)
			m_ram->pointer()[offset] = data;

		if (!ram2 && HAS_KB_OF_RAM(96))
			m_ram->pointer()[0x10000 | offset] = data;

		if (!ram3 && HAS_KB_OF_RAM(128))
			m_ram->pointer()[0x18000 | offset] = data;

		if (!ram4 && HAS_KB_OF_RAM(160))
			m_ram->pointer()[0x20000 | offset] = data;

		if (!ram5 && HAS_KB_OF_RAM(192))
			m_ram->pointer()[0x28000 | offset] = data;

		if (!ram6 && HAS_KB_OF_RAM(224))
			m_ram->pointer()[0x30000 | offset] = data;
	}
	else
	{
		m_ram->pointer()[offset] = data;
	}

	m_exp->cd_w(offset, data, ram2, ram3, ram4, ram5, ram6);
}



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

//-------------------------------------------------
//  ADDRESS_MAP( bw2_mem )
//-------------------------------------------------

void bw2_state::bw2_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(bw2_state::read), FUNC(bw2_state::write));
}


//-------------------------------------------------
//  ADDRESS_MAP( bw2_io )
//-------------------------------------------------

void bw2_state::bw2_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x03).rw(I8255A_TAG, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x10, 0x13).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x20, 0x21).m(m_lcdc, FUNC(msm6255_device::map));
	map(0x30, 0x3f).rw(m_exp, FUNC(bw2_expansion_slot_device::slot_r), FUNC(bw2_expansion_slot_device::slot_w));
	map(0x40, 0x41).rw(m_uart, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x50, 0x50).w("cent_data_out", FUNC(output_latch_device::write));
	map(0x60, 0x63).rw(m_fdc, FUNC(wd2797_device::read), FUNC(wd2797_device::write));
	map(0x70, 0x7f).rw(m_exp, FUNC(bw2_expansion_slot_device::modsel_r), FUNC(bw2_expansion_slot_device::modsel_w));
}


//-------------------------------------------------
//  ADDRESS_MAP( lcdc_map )
//-------------------------------------------------

void bw2_state::lcdc_map(address_map &map)
{
	map.global_mask(0x3fff);
	map(0x0000, 0x3fff).ram().share("videoram");
}



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

//-------------------------------------------------
//  INPUT_PORTS( bw2 )
//-------------------------------------------------

/*
  Keyboard matrix
        X0    X1    X2    X3    X4    X5    X6    X7
     +-----+-----+-----+-----+-----+-----+-----+-----+
  Y9 |CAPS |     |     |     |     |     |     |     |
     +-----+-----+-----+-----+-----+-----+-----+-----+
  Y8 |SHIFT|     |SPACE|  Z  |  A  |  Q  | 2 " | F1  |
     +-----+-----+-----+-----+-----+-----+-----+-----+
  Y7 |CTRL | - = | \ | |  X  |  S  |  W  | 3 # | F2  |
     +-----+-----+-----+-----+-----+-----+-----+-----+
  Y6 | @ ` |  P  | UP  |  C  |  D  |  E  | 4 $ | F3  |
     +-----+-----+-----+-----+-----+-----+-----+-----+
  Y5 | 1 ! |     |     |  V  |  F  |  R  | 5 % | F4  |
     +-----+-----+-----+-----+-----+-----+-----+-----+
  Y4 | ESC |     |     |  B  |  G  |  T  | 6 & | F5  |
     +-----+-----+-----+-----+-----+-----+-----+-----+
  Y3 | TAB | : * |ENTER|  N  |  H  |  Y  | 7 ' | F6  |
     +-----+-----+-----+-----+-----+-----+-----+-----+
  Y2 |DOWN | ^ ~ | [ { |  M  |  J  |  U  | 8 ( | F7  |
     +-----+-----+-----+-----+-----+-----+-----+-----+
  Y1 |RIGHT| ; + | ] } | , < |  K  |  I  | 9 ) | F8  |
     +-----+-----+-----+-----+-----+-----+-----+-----+
  Y0 |LEFT | BS  | / ? | . > |  L  |  O  | 0 _ | DEL |
     +-----+-----+-----+-----+-----+-----+-----+-----+
*/

static INPUT_PORTS_START( bw2 )
	PORT_START("Y0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('_')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(DEL))

	PORT_START("Y1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))

	PORT_START("Y2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))

	PORT_START("Y3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR(UCHAR_MAMEKEY(TAB))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))

	PORT_START("Y4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))

	PORT_START("Y5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))

	PORT_START("Y6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR(3)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))

	PORT_START("Y7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))

	PORT_START("Y8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_START("Y9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("BAUD")
	PORT_CONFNAME( 0x05, 0x05, "Baud rate")
	PORT_CONFSETTING( 0x00, "9600 baud" )
	PORT_CONFSETTING( 0x01, "4800 baud" )
	PORT_CONFSETTING( 0x02, "2400 baud" )
	PORT_CONFSETTING( 0x03, "1200 baud" )
	PORT_CONFSETTING( 0x04, "600 baud" )
	PORT_CONFSETTING( 0x05, "300 baud" )
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

void bw2_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

//-------------------------------------------------
//  I8255A interface
//-------------------------------------------------

void bw2_state::ppi_pa_w(uint8_t data)
{
	/*

	    PA0     KB0 Keyboard line select 0
	    PA1     KB1 Keyboard line select 1
	    PA2     KB2 Keyboard line select 2
	    PA3     KB3 Keyboard line select 3
	    PA4     /DS0 Drive select 0
	    PA5     /DS1 Drive select 1
	    PA6     Select RS232 connector
	    PA7     /STROBE to centronics printer

	*/

	// keyboard
	m_kb = data & 0x0f;

	// drive select
	m_floppy = nullptr;
	if (BIT(data, 4)) m_floppy = m_floppy0->get_device();
	if (BIT(data, 5)) m_floppy = m_floppy1->get_device();

	m_fdc->set_floppy(m_floppy);
	if (m_floppy) m_floppy->mon_w(m_mtron);

	// centronics strobe
	m_centronics->write_strobe(BIT(data, 7));
}

uint8_t bw2_state::ppi_pb_r()
{
	/*

	    PB0     Keyboard column status of selected line
	    PB1     Keyboard column status of selected line
	    PB2     Keyboard column status of selected line
	    PB3     Keyboard column status of selected line
	    PB4     Keyboard column status of selected line
	    PB5     Keyboard column status of selected line
	    PB6     Keyboard column status of selected line
	    PB7     Keyboard column status of selected line

	*/

	uint8_t data = 0xff;

	if (m_kb < 10)
	{
		data = m_y[m_kb]->read();
	}

	return data;
}

void bw2_state::ppi_pc_w(uint8_t data)
{
	/*

	    PC0     Memory bank select
	    PC1     Memory bank select
	    PC2     Memory bank select
	    PC3     Not connected

	*/

	m_bank = data & 0x07;
}

uint8_t bw2_state::ppi_pc_r()
{
	/*

	    PC4     BUSY from centronics printer
	    PC5     M/FDBK motor feedback
	    PC6     RLSD Carrier detect from RS232
	    PC7     /PROT Write protected disk

	*/

	uint8_t data = m_bank;

	// centronics busy
	data |= m_centronics_busy << 4;

	// floppy motor
	data |= m_mfdbk << 5;

	// write protect
	if (m_floppy) data |= !m_floppy->wpt_r() << 7;

	return data;
}

//-------------------------------------------------
//  pit8253_config pit_intf
//-------------------------------------------------

void bw2_state::mtron_w(int state)
{
	m_mtron = state;
	m_mfdbk = !state;

	if (m_floppy) m_floppy->mon_w(m_mtron);
}

//-------------------------------------------------
//  floppy_formats
//-------------------------------------------------

void bw2_state::fdc_drq_w(int state)
{
	if (state)
	{
		if (m_maincpu->state_int(Z80_HALT))
		{
			m_maincpu->set_input_line(INPUT_LINE_NMI, HOLD_LINE);
		}
	}
	else
	{
		m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
	}
}

void bw2_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_BW2_FORMAT);
}

static void bw2_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD); // Teac FD-35
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************


void bw2_state::bw2_palette(palette_device &palette) const
{
	palette.set_pen_color(0, 0xa5, 0xad, 0xa5);
	palette.set_pen_color(1, 0x31, 0x39, 0x10);
}


//-------------------------------------------------
//  MACHINE_START( bw2 )
//-------------------------------------------------

void bw2_state::machine_start()
{
	// register for state saving
	save_item(NAME(m_kb));
	save_item(NAME(m_bank));
	save_item(NAME(m_mtron));
	save_item(NAME(m_mfdbk));
	save_item(NAME(m_centronics_busy));
}



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

//-------------------------------------------------
//  machine_config( bw2 )
//-------------------------------------------------

void bw2_state::bw2(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &bw2_state::bw2_mem);
	m_maincpu->set_addrmap(AS_IO, &bw2_state::bw2_io);

	// video hardware
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_screen_update(MSM6255_TAG, FUNC(msm6255_device::screen_update));
	screen.set_size(640, 200);
	screen.set_visarea(0, 640-1, 0, 200-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(bw2_state::bw2_palette), 2);

	// devices
	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(16_MHz_XTAL / 4); // 8251 USART TXC, RXC
	m_pit->out_handler<0>().set(m_uart, FUNC(i8251_device::write_txc));
	m_pit->out_handler<0>().append(m_uart, FUNC(i8251_device::write_rxc));
	m_pit->set_clk<1>(11000); // LCD controller
	m_pit->out_handler<1>().set(m_pit, FUNC(pit8253_device::write_clk2));
	m_pit->set_clk<2>(0); // Floppy /MTRON
	m_pit->out_handler<2>().set(FUNC(bw2_state::mtron_w));

	i8255_device &ppi(I8255A(config, I8255A_TAG));
	ppi.out_pa_callback().set(FUNC(bw2_state::ppi_pa_w));
	ppi.in_pb_callback().set(FUNC(bw2_state::ppi_pb_r));
	ppi.in_pc_callback().set(FUNC(bw2_state::ppi_pc_r));
	ppi.out_pc_callback().set(FUNC(bw2_state::ppi_pc_w));

	MSM6255(config, m_lcdc, 16_MHz_XTAL);
	m_lcdc->set_addrmap(0, &bw2_state::lcdc_map);
	m_lcdc->set_screen(SCREEN_TAG);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(bw2_state::write_centronics_busy));

	output_latch_device &latch(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(latch);

	I8251(config, m_uart, 0);
	m_uart->txd_handler().set(RS232_TAG, FUNC(rs232_port_device::write_txd));
	m_uart->dtr_handler().set(RS232_TAG, FUNC(rs232_port_device::write_dtr));
	m_uart->rts_handler().set(RS232_TAG, FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, RS232_TAG, default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_uart, FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set(m_uart, FUNC(i8251_device::write_dsr));

	WD2797(config, m_fdc, 16_MHz_XTAL / 16);
	m_fdc->intrq_wr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_fdc->drq_wr_callback().set(FUNC(bw2_state::fdc_drq_w));

	FLOPPY_CONNECTOR(config, WD2797_TAG":0", bw2_floppies, "35dd", bw2_state::floppy_formats);
	FLOPPY_CONNECTOR(config, WD2797_TAG":1", bw2_floppies, nullptr, bw2_state::floppy_formats);
	BW2_EXPANSION_SLOT(config, m_exp, 16_MHz_XTAL, bw2_expansion_cards, nullptr);

	// software list
	SOFTWARE_LIST(config, "flop_list").set_original("bw2");

	// internal ram
	RAM(config, RAM_TAG).set_default_size("64K").set_extra_options("96K,128K,160K,192K,224K");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( bw2 )
//-------------------------------------------------

ROM_START( bw2 )
	ROM_REGION( 0x1000, Z80_TAG, 0 )
	ROM_DEFAULT_BIOS( "v20" )
	ROM_SYSTEM_BIOS( 0, "v12", "BW 2 v1.2" )
	ROMX_LOAD( "bw2-12.ic8", 0x0000, 0x1000, CRC(0ab42d10) SHA1(430b232631eee9b715151b8d191b7eb9449ac513), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v20", "BW 2 v2.0" )
	ROMX_LOAD( "bw2-20.ic8", 0x0000, 0x1000, CRC(86f36471) SHA1(a3e2ba4edd50ff8424bb0675bdbb3b9f13c04c9d), ROM_BIOS(1) )
ROM_END



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT   CLASS      INIT        COMPANY             FULLNAME            FLAGS
COMP( 1985, bw2,  0,      0,      bw2,     bw2,    bw2_state, empty_init, "Bondwell Holding", "Bondwell Model 2", MACHINE_NO_SOUND_HW )



byte.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Andrei I. Holub
/**********************************************************************
    PEVM Byte

Refs:
    https://web.archive.org/web/20241003085933/https://zxbyte.ru/index_en.htm

TODO:
- Kempston joystick
- Byte-01?

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

#include "emu.h"

#include "spectrum.h"

#include "machine/pit8253.h"

namespace {

#define DD66_ROM_REGION  "dd66_rom"
#define DD71_ROM_REGION  "dd71_rom"

class byte_state : public spectrum_state
{
public:
	byte_state(const machine_config &mconfig, device_type type, const char *tag)
		: spectrum_state(mconfig, type, tag)
		, m_pit(*this, "pit")
		, m_dd66_rom(*this, DD66_ROM_REGION)
		, m_dd71_rom(*this, DD71_ROM_REGION)
		, m_io_comp(*this, "COMP")
		, m_io_line(*this, "IO_LINE%u", 0U)
	{ }

	void byte(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void map_io(address_map &map) ATTR_COLD;

	virtual u8 spectrum_rom_r(offs_t offset) override;
	u8 kbd_fe_r(offs_t offset);

private:
	required_device<pit8253_device> m_pit;
	required_region_ptr<u8> m_dd66_rom;
	required_region_ptr<u8> m_dd71_rom;

	required_ioport m_io_comp;
	required_ioport_array<8> m_io_line;

	bool m_1f_gate;
};

u8 byte_state::spectrum_rom_r(offs_t offset)
{
	if (m_1f_gate)
	{
		const u16 adr66 = ((offset >> 7) & 0xff) | ((m_io_comp->read() & 1) << 8);
		const u8 dat66 = m_dd66_rom[adr66];
		if (~dat66 & 0x10)
		{
			u16 adr71 = ((dat66 & 0x0f) << 7) | (offset & 0x7f);
			return m_dd71_rom[adr71];
		}
	}

	return spectrum_state::spectrum_rom_r(offset);
}

void byte_state::map_io(address_map &map)
{
	spectrum_state::spectrum_clone_io(map);
	map(0x0000, 0x0000).select(0xfffe).rw(FUNC(byte_state::kbd_fe_r), FUNC(byte_state::spectrum_ula_w));

	// #8e-ch0, #ae-ch1, #ce-ch2, #ee-ctrl
	map(0x0004, 0x0004).select(0xffea).lw8(NAME([this](offs_t offset, u8 data) { m_pit->write(BIT(offset, 5, 2), data); } ));
	map(0x0015, 0x0015).mirror(0xff8a).lr8(NAME([this]() { m_1f_gate = true; return 0xff; })); // #1f
}

u8 byte_state::kbd_fe_r(offs_t offset)
{
	m_ula->ula_r(offset);

	u8 lines = offset >> 8;
	u8 data = 0xff;
	bool cs_on = false;
	bool ss_on = false;

	for (auto i = 0; i < 8; i++)
	{
		u32 tmp = m_io_line[i]->read();
		if (~lines & 1)
			data &= tmp;

		tmp >>= 8;
		cs_on |= (tmp & 0xff) != 0xff;
		if (~lines & 1)
			data &= tmp;

		tmp >>= 8;
		ss_on |= (tmp & 0xff) != 0xff;
		if (~lines & 1)
			data &= tmp;

		lines >>= 1;
	}

	if (cs_on && (~offset & 0x0100))
		data &= ~0x01; // CS

	if (ss_on && (~offset & 0x8000))
		data &= ~0x02; // SS

	data = data | 0x40;
	if (m_cassette->input() > 0.0038 )
	{
		data &= ~0x40;
	}

	return data;
}

INPUT_PORTS_START(byte)

	PORT_START("IO_LINE0") /* 0xFEFE */
	PORT_BIT(0x000001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"ПРОПИСНЫЕ")       PORT_CODE(KEYCODE_LSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)
																				   PORT_CODE(KEYCODE_RSHIFT)   PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x000002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Я  Z  :")         PORT_CODE(KEYCODE_Z)        PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(':')
	PORT_BIT(0x020000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("(SS+Z) :")          PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x000004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ч  X  $")         PORT_CODE(KEYCODE_X)        PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR('$')
	PORT_BIT(0x000008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"С  C  ?")         PORT_CODE(KEYCODE_C)        PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR('?')
	PORT_BIT(0x000010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"М  V  /")         PORT_CODE(KEYCODE_V)        PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR('/')
	PORT_BIT(0x000080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ю  ПФ6")          PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0xfdff60, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE1") /* 0xFDFE */
	PORT_BIT(0x000001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ф  A  ~")         PORT_CODE(KEYCODE_A)        PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR('~')
	PORT_BIT(0x000002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ы  S  |")         PORT_CODE(KEYCODE_S)        PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR('|')
	PORT_BIT(0x000004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"В  D  \\")        PORT_CODE(KEYCODE_D)        PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR('\\')
	PORT_BIT(0x000008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"А  F  {")         PORT_CODE(KEYCODE_F)        PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR('{')
	PORT_BIT(0x000010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"П  G  }")         PORT_CODE(KEYCODE_G)        PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR('}')
	PORT_BIT(0x000020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"ЛАТ")             PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x000080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Э  ПФ4")          PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0xffff40, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE2") /* 0xFBFE */
	PORT_BIT(0x000001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Й  Q  <=")        PORT_CODE(KEYCODE_Q)        PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x000002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ц  W  <>")        PORT_CODE(KEYCODE_W)        PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x000004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"У  E  >=")        PORT_CODE(KEYCODE_E)        PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x000008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"К  R  <")         PORT_CODE(KEYCODE_R)        PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR('<')
	PORT_BIT(0x000010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Е  T  >")         PORT_CODE(KEYCODE_T)        PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR('>')
	PORT_BIT(0xffffe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE3") /* 0xF7FE */
	PORT_BIT(0x000001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1  !")              PORT_CODE(KEYCODE_1)        PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x000100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"(CS+1) РЕД")      PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x000002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2  @")              PORT_CODE(KEYCODE_2)        PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x000200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"(CS+2) ПР/СТ")    PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x000004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3  #")              PORT_CODE(KEYCODE_3)        PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x000400, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"(CS+3) ПОЗИТ")    PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x000008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4  $")              PORT_CODE(KEYCODE_4)        PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x000800, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"(CS+4) НЕГАТ")    PORT_CODE(KEYCODE_END)
	PORT_BIT(0x000010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5  %")              PORT_CODE(KEYCODE_5)        PORT_CHAR('5') PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CHAR('%')
	PORT_BIT(0x001000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"(CS+5) \u2190")   PORT_CODE(KEYCODE_LEFT)  // ←
	PORT_BIT(0xffe0e0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE4") /* 0xEFFE */
	PORT_BIT(0x000001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0  _")              PORT_CODE(KEYCODE_0)        PORT_CHAR('0') PORT_CHAR(8) PORT_CHAR('_')
	PORT_BIT(0x000100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"(CS+0) УДАЛЕНИЕ") PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x000002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9  )")              PORT_CODE(KEYCODE_9)        PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x000200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"(CS+9) ГРАФ")     PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x000004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8  (")              PORT_CODE(KEYCODE_8)        PORT_CHAR('8') PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR('(')
	PORT_BIT(0x000400, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"(CS+8) \u2192")   PORT_CODE(KEYCODE_RIGHT) // →
	PORT_BIT(0x000008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7  '")              PORT_CODE(KEYCODE_7)        PORT_CHAR('7') PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CHAR('\'')
	PORT_BIT(0x000800, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"(CS+7) \u2191")   PORT_CODE(KEYCODE_UP)    // ↑
	PORT_BIT(0x000010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6  &")              PORT_CODE(KEYCODE_6)        PORT_CHAR('6') PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CHAR('&')
	PORT_BIT(0x001000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"(CS+6) \u2193")   PORT_CODE(KEYCODE_DOWN)  // ↓
	PORT_BIT(0x000020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"РУС")             PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x000080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ъ  ПФ2")          PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0xffe040, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE5") /* 0xDFFE */
	PORT_BIT(0x000001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"З  P  \"  ©")     PORT_CODE(KEYCODE_P)        PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR('"')
	PORT_BIT(0x010000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("(SS+P) \"")         PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x000002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Щ  O  ;")         PORT_CODE(KEYCODE_O)        PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(';')
	PORT_BIT(0x000004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ш  I")            PORT_CODE(KEYCODE_I)        PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x000008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Г  U  ]")         PORT_CODE(KEYCODE_U)        PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(']')
	PORT_BIT(0x000010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Н  Y  [")         PORT_CODE(KEYCODE_Y)        PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR('[')
	PORT_BIT(0x000020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"ЛАТ/РУС")         PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x000080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Х  ПФ1")          PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0xfeff40, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE6") /* 0xBFFE */
	PORT_BIT(0x000001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"ВВОД")            PORT_CODE(KEYCODE_ENTER)    PORT_CHAR(13)
	PORT_BIT(0x000002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Д  L  =")         PORT_CODE(KEYCODE_L)        PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR('=')
	PORT_BIT(0x000004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Л  K  +")         PORT_CODE(KEYCODE_K)        PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR('+')
	PORT_BIT(0x000008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"О  J  -")         PORT_CODE(KEYCODE_J)        PORT_CHAR('j') PORT_CHAR('J') PORT_CHAR('-')
	PORT_BIT(0x000010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Р  H  ^")         PORT_CODE(KEYCODE_H)        PORT_CHAR('h') PORT_CHAR('H') PORT_CHAR('^')
	PORT_BIT(0x000080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ж  ПФ3")          PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0xffff60, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE7") /* 0x7FFE */
	PORT_BIT(0x000001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"ПРОБЕЛ")          PORT_CODE(KEYCODE_SPACE)    PORT_CHAR(' ')
	PORT_BIT(0x000100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"ОСТАНОВ")         PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x000002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"РЕГ1")            PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
																				   PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))
	PORT_BIT(0x000200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"РЕГ2")            PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x000004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ь  M  .")         PORT_CODE(KEYCODE_M)        PORT_CHAR('m') PORT_CHAR('M') PORT_CHAR('.')
	PORT_BIT(0x000008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Т  N  ,")         PORT_CODE(KEYCODE_N)        PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(',')
	PORT_BIT(0x000010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"И  B  *")         PORT_CODE(KEYCODE_B)        PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR('*')
	PORT_BIT(0x000080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Б  ПФ5")          PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0xfffc60, IP_ACTIVE_LOW, IPT_UNUSED)


	PORT_START("COMP")
	PORT_CONFNAME( 0x01, 0x01, "Compatibility")
	PORT_CONFSETTING(    0x00, "On (Sinclair)")
	PORT_CONFSETTING(    0x01, "Off (Byte)")
INPUT_PORTS_END

void byte_state::machine_start()
{
	spectrum_state::machine_start();

	// Save
	save_item(NAME(m_1f_gate));
}

void byte_state::machine_reset()
{
	spectrum_state::machine_reset();

	m_1f_gate = false;
}


void byte_state::byte(machine_config &config)
{
	spectrum_state::spectrum_clone(config);

	PIT8253(config, m_pit, 0); // КР580ВИ53
	m_pit->set_clk<0>(20_MHz_XTAL / 10);
	m_pit->out_handler<0>().set([this](int state) { m_speaker->level_w(state); });
	m_pit->set_clk<1>(20_MHz_XTAL / 10);
	m_pit->out_handler<1>().set([this](int state) { m_speaker->level_w(state); });
	m_pit->set_clk<2>((20_MHz_XTAL / 10));
	m_pit->out_handler<2>().set([this](int state) { m_speaker->level_w(state); });

	m_maincpu->set_io_map(&byte_state::map_io);
	m_exp->fb_r_handler().set([]() { return 0xff; });
}

ROM_START(byte)
	ROM_REGION(0x10000, "maincpu", ROMREGION_ERASEFF)
	ROM_DEFAULT_BIOS("prusak")

	ROM_SYSTEM_BIOS(0, "v1", "V1")
	ROMX_LOAD("byte.rom", 0x0000, 0x4000, CRC(c13ba473) SHA1(99f40727185abbb2413f218d69df021ae2e99e45), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "prusak", "Prusak's")
	ROMX_LOAD("dd72.bin", 0x0000, 0x2000, CRC(2464d537) SHA1(e8b4a468e6f254f090fc5b2c59ea573c2a4f0455), ROM_BIOS(1))
	ROMX_LOAD("dd73.bin", 0x2000, 0x2000, CRC(bd430288) SHA1(b4c67a6213b1ecfa37cc476bc483e8a8deef6149), ROM_BIOS(1))

	ROM_REGION(0x800, DD71_ROM_REGION, ROMREGION_ERASEFF)
	ROM_LOAD("dd71_rt7.bin", 0x000, 0x800, CRC(c91b07c2) SHA1(2365d45b028b1e91dffb5bcdc87bd26ca9a7c26f))

	ROM_REGION(0x200, DD66_ROM_REGION, ROMREGION_ERASEFF)
	ROM_LOAD("dd66_rt5.bin", 0x000, 0x200, CRC(f8f9766a) SHA1(3f5345763a30e5370199c454301de655e7f1a1da))

	ROM_REGION(0x600, "tbd", ROMREGION_ERASEFF)
	ROM_LOAD("dd10_rt5.reva.bin", 0x000, 0x200, CRC(aae13e3e) SHA1(46f0ca97ceee0c591277aaac8b0cecc445927690)) // SN 1..7599 - 1989..1990
	ROM_LOAD("dd10_rt5.revb.bin", 0x200, 0x200, CRC(b649b5d1) SHA1(2d067962b08aee8cdf1bc4f5ce337815dd9d6c66)) // SN 7600..  - 1991..1996
	ROM_LOAD("dd11_rt5.bin", 0x400, 0x200, CRC(0f32b304) SHA1(d7adf9861c332510ff3682a1b06e6d9898343b6d))
ROM_END

} // Anonymous namespace

//    YEAR  NAME      PARENT    COMPAT  MACHINE INPUT CLASS       INIT           COMPANY FULLNAME     FLAGS
COMP( 1990, byte,     spectrum, 0,      byte,   byte, byte_state, init_spectrum, "BEMZ", "PEVM Byte", 0 )
//COMP( 1993, byte01,   ...



c10.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/*****************************************************************************************************

Cromemco C-10 Personal Computer

2010-08-30 Skeleton driver

Photos show: Intersil 74954-1, Mostek MK3880N-4 (Z80A), CROMEMCO 011-0082-01, CROMEMCO 011-0095,
             Intel P8275-2, AM92128BPC (16K ROM), NEC D8257C-5, CROMEMCO 011-0083, WDC FD1793B-02,
             2x 8251. Crystals: 8MHz, 13.028MHz

Driver currently gets to a loop where it waits for an interrupt.
The interrupt routine presumably writes to FE69 which the loop is
constantly looking at.

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

#include "emu.h"

#include "cpu/z80/z80.h"

#include "emupal.h"
#include "screen.h"


namespace {

class c10_state : public driver_device
{
public:
	c10_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_vram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
	{ }

	void c10(machine_config &config);

private:
	void machine_reset() override ATTR_COLD;
	void machine_start() override ATTR_COLD;

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	memory_passthrough_handler m_rom_shadow_tap;
	required_device<z80_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_shared_ptr<u8> m_vram;
	required_region_ptr<u8> m_p_chargen;
};



void c10_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).ram().share("mainram");
	map(0x8000, 0xbfff).rom().region("maincpu", 0);
	map(0xc000, 0xf0a1).ram();
	map(0xf0a2, 0xffff).ram().share("videoram");
}

void c10_state::io_map(address_map &map)
{
	map.global_mask(0xff);
}

/* Input ports */
static INPUT_PORTS_START( c10 )
INPUT_PORTS_END

void c10_state::machine_start()
{
}

void c10_state::machine_reset()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x0000, 0x0fff, m_rom);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0x8000, 0x8fff,
			"rom_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// reinstall RAM over the ROM shadow
					m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x0fff, m_ram);
				}
			},
			&m_rom_shadow_tap);
}

/* This system appears to have inline attribute bytes of unknown meaning.
    Currently they are ignored. The word at FAB5 looks like it might be cursor location. */
uint32_t c10_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	//static uint8_t framecnt=0;
	uint16_t sy=0,ma=0;

	//framecnt++;

	for (uint8_t y = 0; y < 25; y++)
	{
		for (uint8_t ra = 0; ra < 10; ra++)
		{
			uint16_t *p = &bitmap.pix(sy++);

			uint16_t xx = ma;
			for (uint16_t x = ma; x < ma + 80; x++)
			{
				uint8_t gfx = 0;
				if (ra < 9)
				{
					uint8_t chr = m_vram[xx++];

				//  /* Take care of flashing characters */
				//  if ((chr < 0x80) && (framecnt & 0x08))
				//      chr |= 0x80;

					if (BIT(chr, 7)) // ignore attribute bytes
						x--;
					else
						gfx = m_p_chargen[(chr<<4) | ra ];
				}
				/* Display a scanline of a character */
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma+=96;
	}
	return 0;
}

/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 9,                   /* 8 x 9 characters */
	512,                    /* 512 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_c10 )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END


void c10_state::c10(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(8'000'000) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &c10_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &c10_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(c10_state::screen_update));
	screen.set_size(640, 250);
	screen.set_visarea_full();
	screen.set_palette("palette");
	GFXDECODE(config, "gfxdecode", "palette", gfx_c10);
	PALETTE(config, "palette", palette_device::MONOCHROME);
}


/* ROM definition */
ROM_START( c10 )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "502-0055.ic16", 0x0000, 0x4000, CRC(2ccf5983) SHA1(52f7c497f5284bf5df9eb0d6e9142bb1869d8c24))

	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "c10_char.ic9", 0x0000, 0x2000, CRC(cb530b6f) SHA1(95590bbb433db9c4317f535723b29516b9b9fcbf))
ROM_END

} // anonymous namespace

/* Driver */

/*   YEAR   NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY     FULLNAME  FLAGS */
COMP( 1982, c10,  0,      0,      c10,     c10,   c10_state, empty_init, "Cromemco", "C-10",   MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )




c128.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

    TODO:

    - connect CAPS LOCK to charom A12 on international variants

*/

#include "emu.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "bus/c64/exp.h"
#include "bus/cbmiec/cbmiec.h"
#include "bus/cbmiec/c1571.h"
#include "bus/cbmiec/c1581.h"
#include "bus/vic20/user.h"
#include "bus/pet/cass.h"
#include "bus/vcs_ctrl/ctrl.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "imagedev/snapquik.h"
#include "cpu/m6502/m8502.h"
#include "cpu/z80/z80.h"
#include "cbm_snqk.h"
#include "machine/input_merger.h"
#include "machine/mos6526.h"
#include "machine/mos8722.h"
#include "machine/pla.h"
#include "machine/ram.h"
#include "sound/mos6581.h"
#include "video/mos6566.h"
#include "video/mos8563.h"

#define M8502_TAG       "u6"
#define MOS8563_TAG     "u22"
#define MOS8564_TAG     "u21"
#define MOS8566_TAG     "u21"
#define MOS8721_TAG     "u11"
#define SCREEN_VIC_TAG  "screen"
#define SCREEN_VDC_TAG  "screen80"
#define CONTROL1_TAG    "joy1"
#define CONTROL2_TAG    "joy2"


namespace {

class c128_state : public driver_device
{
public:
	c128_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "u10"),
		m_subcpu(*this, M8502_TAG),
		m_nmi(*this, "nmi"),
		m_mmu(*this, "u7"),
		m_pla(*this, MOS8721_TAG),
		m_vdc(*this, MOS8563_TAG),
		m_vic(*this, MOS8564_TAG),
		m_sid(*this, "u5"),
		m_cia1(*this, "u1"),
		m_cia2(*this, "u4"),
		m_iec(*this, CBM_IEC_TAG),
		m_joy1(*this, CONTROL1_TAG),
		m_joy2(*this, CONTROL2_TAG),
		m_exp(*this, "exp"),
		m_user(*this, "user"),
		m_ram(*this, RAM_TAG),
		m_cassette(*this, PET_DATASSETTE_PORT_TAG),
		m_from(*this, "from"),
		m_rom(*this, M8502_TAG),
		m_charom(*this, "charom"),
		m_color_ram(*this, "color_ram", 0x800, ENDIANNESS_LITTLE),
		m_row(*this, "ROW%u", 0),
		m_k(*this, "K%u", 0),
		m_lock(*this, "LOCK"),
		m_caps(*this, "CAPS"),
		m_40_80(*this, "40_80"),
		m_z80en(0),
		m_loram(1),
		m_hiram(1),
		m_charen(1),
		m_game(1),
		m_exrom(1),
		m_va14(1),
		m_va15(1),
		m_clrbank(0),
		m_cnt1(1),
		m_sp1(1),
		m_iec_data_out(1),
		m_cass_rd(1),
		m_iec_srq(1),
		m_vic_k(0x07),
		m_caps_lock(1)
	{ }

	required_device<cpu_device> m_maincpu;
	required_device<m8502_device> m_subcpu;
	required_device<input_merger_device> m_nmi;
	required_device<mos8722_device> m_mmu;
	required_device<pla_device> m_pla;
	required_device<mos8563_device> m_vdc;
	required_device<mos6566_device> m_vic;
	required_device<mos6581_device> m_sid;
	required_device<mos6526_device> m_cia1;
	required_device<mos6526_device> m_cia2;
	required_device<cbm_iec_device> m_iec;
	required_device<vcs_control_port_device> m_joy1;
	required_device<vcs_control_port_device> m_joy2;
	required_device<c64_expansion_slot_device> m_exp;
	required_device<pet_user_port_device> m_user;
	required_device<ram_device> m_ram;
	required_device<pet_datassette_port_device> m_cassette;
	required_device<generic_slot_device> m_from;
	required_memory_region m_rom;
	required_memory_region m_charom;
	memory_share_creator<uint8_t> m_color_ram;
	required_ioport_array<8> m_row;
	required_ioport_array<3> m_k;
	required_ioport m_lock;
	required_ioport m_caps;
	required_ioport m_40_80;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	inline void check_interrupts();
	int read_pla(offs_t offset, offs_t ca, offs_t vma, int ba, int rw, int aec, int z80io, int ms3, int ms2, int ms1, int ms0);
	uint8_t read_memory(offs_t offset, offs_t vma, int ba, int aec, int z80io);
	void write_memory(offs_t offset, offs_t vma, uint8_t data, int ba, int aec, int z80io);
	inline void update_iec();

	uint8_t z80_r(offs_t offset);
	void z80_w(offs_t offset, uint8_t data);
	uint8_t z80_io_r(offs_t offset);
	void z80_io_w(offs_t offset, uint8_t data);
	uint8_t read(offs_t offset);
	void write(offs_t offset, uint8_t data);
	uint8_t vic_videoram_r(offs_t offset);
	uint8_t vic_colorram_r(offs_t offset);

	void mmu_z80en_w(int state);
	void mmu_fsdir_w(int state);
	int mmu_game_r();
	int mmu_exrom_r();
	int mmu_sense40_r();

	void vic_k_w(uint8_t data);

	uint8_t sid_potx_r();
	uint8_t sid_poty_r();

	void cia1_cnt_w(int state);
	void cia1_sp_w(int state);
	uint8_t cia1_pa_r();
	void cia1_pa_w(uint8_t data);
	uint8_t cia1_pb_r();
	void cia1_pb_w(uint8_t data);

	uint8_t cia2_pa_r();
	void cia2_pa_w(uint8_t data);

	uint8_t cpu_r();
	void cpu_w(uint8_t data);

	void iec_srq_w(int state);
	void iec_data_w(int state);

	uint8_t exp_dma_cd_r(offs_t offset);
	void exp_dma_cd_w(offs_t offset, uint8_t data);
	void exp_dma_w(int state);
	void exp_reset_w(int state);

	void write_restore(int state);
	DECLARE_INPUT_CHANGED_MEMBER( caps_lock );

	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_c128);

	uint8_t cia2_pb_r();
	void cia2_pb_w(uint8_t data);

	void write_user_pa2(int state) { m_user_pa2 = state; }
	void write_user_pb0(int state) { if (state) m_user_pb |= 1; else m_user_pb &= ~1; }
	void write_user_pb1(int state) { if (state) m_user_pb |= 2; else m_user_pb &= ~2; }
	void write_user_pb2(int state) { if (state) m_user_pb |= 4; else m_user_pb &= ~4; }
	void write_user_pb3(int state) { if (state) m_user_pb |= 8; else m_user_pb &= ~8; }
	void write_user_pb4(int state) { if (state) m_user_pb |= 16; else m_user_pb &= ~16; }
	void write_user_pb5(int state) { if (state) m_user_pb |= 32; else m_user_pb &= ~32; }
	void write_user_pb6(int state) { if (state) m_user_pb |= 64; else m_user_pb &= ~64; }
	void write_user_pb7(int state) { if (state) m_user_pb |= 128; else m_user_pb &= ~128; }

	void update_cia1_flag();
	void cass_rd_w(int state) { m_cass_rd = state; update_cia1_flag(); }

	// memory state
	int m_z80en;
	int m_loram;
	int m_hiram;
	int m_charen;
	int m_game;
	int m_exrom;
	int m_reset;

	// video state
	int m_va14;
	int m_va15;
	int m_clrbank;

	// fast serial state
	int m_cnt1;
	int m_sp1;
	int m_iec_data_out;

	// interrupt state
	int m_exp_dma;
	int m_cass_rd;
	int m_iec_srq;

	// keyboard state
	uint8_t m_vic_k;
	int m_caps_lock;

	int m_user_pa2;
	int m_user_pb;

	void softlists(machine_config &config, const char *filter);
	void pal(machine_config &config);
	void ntsc(machine_config &config);
	void c128pal(machine_config &config);
	void c128(machine_config &config);
	void c128dcr(machine_config &config);
	void c128dcrp(machine_config &config);
	void c128d81(machine_config &config);
	void m8502_mem(address_map &map) ATTR_COLD;
	void vdc_videoram_map(address_map &map) ATTR_COLD;
	void vic_colorram_map(address_map &map) ATTR_COLD;
	void vic_videoram_map(address_map &map) ATTR_COLD;
	void z80_io(address_map &map) ATTR_COLD;
	void z80_mem(address_map &map) ATTR_COLD;
};



//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

#define LOG 0

#define A15 BIT(offset, 15)
#define A14 BIT(offset, 14)
#define A13 BIT(offset, 13)
#define A12 BIT(offset, 12)
#define A11 BIT(offset, 11)
#define A10 BIT(offset, 10)
#define VMA5 BIT(vma, 13)
#define VMA4 BIT(vma, 12)

enum
{
	PLA_OUT_SDEN = 0,
	PLA_OUT_ROM4 = 1,
	PLA_OUT_ROM2 = 2,
	PLA_OUT_DIR = 3,
	PLA_OUT_ROML = 4,
	PLA_OUT_ROMH = 5,
	PLA_OUT_CLRBANK = 6,
	PLA_OUT_FROM1 = 7,
	PLA_OUT_ROM3 = 8,
	PLA_OUT_ROM1 = 9,
	PLA_OUT_IOCS = 10,
	PLA_OUT_DWE = 11,
	PLA_OUT_CASENB = 12,
	PLA_OUT_VIC = 13,
	PLA_OUT_IOACC = 14,
	PLA_OUT_GWE = 15,
	PLA_OUT_COLORRAM = 16,
	PLA_OUT_CHAROM = 17
};


QUICKLOAD_LOAD_MEMBER(c128_state::quickload_c128)
{
	return general_cbm_loadsnap(image, m_maincpu->space(AS_PROGRAM), 0, cbm_quick_sethiaddress);
}


//**************************************************************************
//  INTERRUPTS
//**************************************************************************

//-------------------------------------------------
//  check_interrupts -
//-------------------------------------------------

inline void c128_state::check_interrupts()
{
	//int irq = m_cia1_irq || m_vic_irq || m_exp_irq;
	//int nmi = m_cia2_irq || !m_restore || m_exp_nmi;
	//int aec = m_exp_dma && m_z80_busack;
	//int rdy = m_vic_aec && m_z80en && m_vic_ba;
	//int busreq = !m_z80en || !(m_z80_busack && !aec)
}



//**************************************************************************
//  ADDRESS DECODING
//**************************************************************************

//-------------------------------------------------
//  read_pla -
//-------------------------------------------------

int c128_state::read_pla(offs_t offset, offs_t ca, offs_t vma, int ba, int rw, int aec, int z80io, int ms3, int ms2, int ms1, int ms0)
{
	int _128_256 = 1;
	int dmaack = 1;
	int vicfix = 1;
	int sphi2 = m_vic->phi0_r();

	m_game = m_exp->game_r(ca, sphi2, ba, rw, m_loram, m_hiram);
	m_exrom = m_exp->exrom_r(ca, sphi2, ba, rw, m_loram, m_hiram);

	uint32_t input = sphi2 << 26 | m_va14 << 25 | m_charen << 24 |
		m_hiram << 23 | m_loram << 22 | ba << 21 | VMA5 << 20 | VMA4 << 19 | ms0 << 18 | ms1 << 17 | ms2 << 16 |
		m_exrom << 15 | m_game << 14 | rw << 13 | aec << 12 | A10 << 11 | A11 << 10 | A12 << 9 | A13 << 8 |
		A14 << 7 | A15 << 6 | z80io << 5 | m_z80en << 4 | ms3 << 3 | vicfix << 2 | dmaack << 1 | _128_256;

	return m_pla->read(input);
}


//-------------------------------------------------
//  read_memory -
//-------------------------------------------------

uint8_t c128_state::read_memory(offs_t offset, offs_t vma, int ba, int aec, int z80io)
{
	int rw = 1, ms0 = 1, ms1 = 1, ms2 = 1, ms3 = 1, cas0 = 1, cas1 = 1;
	int io1 = 1, io2 = 1;
	int sphi2 = m_vic->phi0_r();

	uint8_t data = 0xff;

	offs_t ta = m_mmu->ta_r(offset, aec, &ms0, &ms1, &ms2, &ms3, &cas0, &cas1);
	offs_t ma = 0;
	offs_t sa = 0;

	if (aec)
	{
		data = m_vic->bus_r();
		ma = ta | (offset & 0xff);
		sa = offset & 0xff;
	}
	else
	{
		ta &= ~0xf00;
		ta |= (vma & 0xf00);
		ma = (!m_va15 << 15) | (!m_va14 << 14) | vma;
		sa = vma & 0xff;
	}

	offs_t ca = ta | sa;

	int plaout = read_pla(offset, ca, vma, ba, rw, aec, z80io, ms3, ms2, ms1, ms0);

	m_clrbank = BIT(plaout, PLA_OUT_CLRBANK);

	if (!BIT(plaout, PLA_OUT_CASENB))
	{
		if (!cas0)
		{
			data = m_ram->pointer()[ma];
		}
		if (!cas1)
		{
			data = m_ram->pointer()[0x10000 | ma];
		}
	}
	if (!BIT(plaout, PLA_OUT_ROM1))
	{
		// CR: data = m_rom1[(ms3 << 14) | ((BIT(ta, 14) && BIT(offset, 13)) << 13) | (ta & 0x1000) | (offset & 0xfff)];
		data = m_rom->base()[((BIT(ta, 14) && BIT(offset, 13)) << 13) | (ta & 0x1000) | (offset & 0xfff)];
	}
	if (!BIT(plaout, PLA_OUT_ROM2))
	{
		data = m_rom->base()[0x4000 | (offset & 0x3fff)];
	}
	if (!BIT(plaout, PLA_OUT_ROM3))
	{
		// CR: data = m_rom3[(BIT(offset, 15) << 14) | (offset & 0x3fff)];
		data = m_rom->base()[0x8000 | (offset & 0x3fff)];
	}
	if (!BIT(plaout, PLA_OUT_ROM4))
	{
		data = m_rom->base()[0xc000 | (ta & 0x1000) | (offset & 0x2fff)];
	}
	if (!BIT(plaout, PLA_OUT_CHAROM))
	{
		data = m_charom->base()[(ms3 << 12) | (ta & 0xf00) | sa];
	}
	if (!BIT(plaout, PLA_OUT_COLORRAM) && aec)
	{
		data = m_color_ram[(m_clrbank << 10) | (ta & 0x300) | sa] & 0x0f;
	}
	if (!BIT(plaout, PLA_OUT_VIC))
	{
		data = m_vic->read(offset & 0x3f);
	}
	if (!BIT(plaout, PLA_OUT_FROM1) && m_from->exists())
	{
		data = m_from->read_rom(offset & 0x7fff);
	}
	if (!BIT(plaout, PLA_OUT_IOCS) && BIT(offset, 10))
	{
		switch ((BIT(offset, 11) << 2) | ((offset >> 8) & 0x03))
		{
		case 0: // SID
			data = m_sid->read(offset & 0x1f);
			break;

		case 2: // CS8563
			if (BIT(offset, 0))
			{
				data = m_vdc->register_r();
			}
			else
			{
				data = m_vdc->status_r();
			}
			break;

		case 4: // CIA1
			data = m_cia1->read(offset & 0x0f);
			break;

		case 5: // CIA2
			data = m_cia2->read(offset & 0x0f);
			break;

		case 6: // I/O1
			io1 = 0;
			break;

		case 7: // I/O2
			io2 = 0;
			break;
		}
	}

	int roml = BIT(plaout, PLA_OUT_ROML);
	int romh = BIT(plaout, PLA_OUT_ROMH);

	data = m_exp->cd_r(ca, data, sphi2, ba, roml, romh, io1, io2);

	return m_mmu->read(offset, data);
}


//-------------------------------------------------
//  write_memory -
//-------------------------------------------------

void c128_state::write_memory(offs_t offset, offs_t vma, uint8_t data, int ba, int aec, int z80io)
{
	int rw = 0, ms0 = 1, ms1 = 1, ms2 = 1, ms3 = 1, cas0 = 1, cas1 = 1;
	int io1 = 1, io2 = 1;
	int sphi2 = m_vic->phi0_r();

	offs_t ta = m_mmu->ta_r(offset, aec, &ms0, &ms1, &ms2, &ms3, &cas0, &cas1);
	offs_t ca = ta | (offset & 0xff);
	offs_t ma = ta | (offset & 0xff);
	offs_t sa = offset & 0xff;

	int plaout = read_pla(offset, ca, vma, ba, rw, aec, z80io, ms3, ms2, ms1, ms0);

	m_clrbank = BIT(plaout, PLA_OUT_CLRBANK);

	if (!BIT(plaout, PLA_OUT_CASENB) && !BIT(plaout, PLA_OUT_DWE))
	{
		if (!cas0)
		{
			m_ram->pointer()[ma] = data;
		}
		if (!cas1)
		{
			m_ram->pointer()[0x10000 | ma] = data;
		}
	}
	if (!BIT(plaout, PLA_OUT_COLORRAM) && !BIT(plaout, PLA_OUT_GWE))
	{
		m_color_ram[(m_clrbank << 10) | (ta & 0x300) | sa] = data & 0x0f;
	}
	if (!BIT(plaout, PLA_OUT_VIC))
	{
		m_vic->write(offset & 0x3f, data);
	}
	if (!BIT(plaout, PLA_OUT_IOCS) && BIT(offset, 10))
	{
		switch ((BIT(offset, 11) << 2) | ((offset >> 8) & 0x03))
		{
		case 0: // SID
			m_sid->write(offset & 0x1f, data);
			break;

		case 2: // CS8563
			if (BIT(offset, 0))
			{
				m_vdc->register_w(data);
			}
			else
			{
				m_vdc->address_w(data);
			}
			break;

		case 4: // CIA1
			m_cia1->write(offset & 0x0f, data);
			break;

		case 5: // CIA2
			m_cia2->write(offset & 0x0f, data);
			break;

		case 6: // I/O1
			io1 = 0;
			break;

		case 7: // I/O2
			io2 = 0;
			break;
		}
	}

	int roml = BIT(plaout, PLA_OUT_ROML);
	int romh = BIT(plaout, PLA_OUT_ROMH);

	m_exp->cd_w(ca, data, sphi2, ba, roml, romh, io1, io2);

	m_mmu->write(offset, data);
}


//-------------------------------------------------
//  z80_r -
//-------------------------------------------------

uint8_t c128_state::z80_r(offs_t offset)
{
	int ba = 1, aec = 1, z80io = 1;
	offs_t vma = 0;

	return read_memory(offset, vma, ba, aec, z80io);
}


//-------------------------------------------------
//  z80_w -
//-------------------------------------------------

void c128_state::z80_w(offs_t offset, uint8_t data)
{
	int ba = 1, aec = 1, z80io = 1;
	offs_t vma = 0;

	write_memory(offset, vma, data, ba, aec, z80io);
}


//-------------------------------------------------
//  z80_io_r -
//-------------------------------------------------

uint8_t c128_state::z80_io_r(offs_t offset)
{
	int ba = 1, aec = 1, z80io = 0;
	offs_t vma = 0;

	return read_memory(offset, vma, ba, aec, z80io);
}


//-------------------------------------------------
//  z80_io_w -
//-------------------------------------------------

void c128_state::z80_io_w(offs_t offset, uint8_t data)
{
	int ba = 1, aec = 1, z80io = 0;
	offs_t vma = 0;

	write_memory(offset, vma, data, ba, aec, z80io);
}


//-------------------------------------------------
//  read -
//-------------------------------------------------

uint8_t c128_state::read(offs_t offset)
{
	int ba = 1, aec = 1, z80io = 1;
	offs_t vma = 0;

	return read_memory(offset, vma, ba, aec, z80io);
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

void c128_state::write(offs_t offset, uint8_t data)
{
	int ba = 1, aec = 1, z80io = 1;
	offs_t vma = 0;

	write_memory(offset, vma, data, ba, aec, z80io);
}


//-------------------------------------------------
//  vic_videoram_r -
//-------------------------------------------------

uint8_t c128_state::vic_videoram_r(offs_t offset)
{
	int ba = 0, aec = 0, z80io = 1;

	return read_memory(0, offset, ba, aec, z80io);
}


//-------------------------------------------------
//  vic_colorram_r -
//-------------------------------------------------

uint8_t c128_state::vic_colorram_r(offs_t offset)
{
	return m_color_ram[(m_clrbank << 10) | offset];
}



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

//-------------------------------------------------
//  ADDRESS_MAP( z80_mem )
//-------------------------------------------------

void c128_state::z80_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(c128_state::z80_r), FUNC(c128_state::z80_w));
}


//-------------------------------------------------
//  ADDRESS_MAP( z80_io )
//-------------------------------------------------

void c128_state::z80_io(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(c128_state::z80_io_r), FUNC(c128_state::z80_io_w));
}


//-------------------------------------------------
//  ADDRESS_MAP( m8502_mem )
//-------------------------------------------------

void c128_state::m8502_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(c128_state::read), FUNC(c128_state::write));
}


//-------------------------------------------------
//  ADDRESS_MAP( vic_videoram_map )
//-------------------------------------------------

void c128_state::vic_videoram_map(address_map &map)
{
	map(0x0000, 0x3fff).r(FUNC(c128_state::vic_videoram_r));
}


//-------------------------------------------------
//  ADDRESS_MAP( vic_colorram_map )
//-------------------------------------------------

void c128_state::vic_colorram_map(address_map &map)
{
	map(0x000, 0x3ff).r(FUNC(c128_state::vic_colorram_r));
}


//-------------------------------------------------
//  ADDRESS_MAP( vdc_videoram_map )
//-------------------------------------------------

void c128_state::vdc_videoram_map(address_map &map)
{
	map(0x0000, 0xffff).ram();
}



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

//-------------------------------------------------
//  INPUT_PORTS( c128 )
//-------------------------------------------------

void c128_state::write_restore(int state)
{
	m_nmi->in_w<1>(!state);
}

INPUT_CHANGED_MEMBER( c128_state::caps_lock )
{
	m_caps_lock = newval;
}

static INPUT_PORTS_START( c128 )
	PORT_START( "ROW0" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Crsr Down Up") PORT_CODE(KEYCODE_RALT)        PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3)                                    PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2)                                    PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1)                                    PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F4)                                    PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Crsr Right Left") PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER)             PORT_CHAR(13)
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INST DEL") PORT_CODE(KEYCODE_BACKSPACE)       PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START( "ROW1" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Shift (Left)") PORT_CODE(KEYCODE_LSHIFT)      PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)         PORT_CHAR('E')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)         PORT_CHAR('S')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)         PORT_CHAR('Z')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)         PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)         PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)         PORT_CHAR('W')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)         PORT_CHAR('3') PORT_CHAR('#')

	PORT_START( "ROW2" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)         PORT_CHAR('X')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)         PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)         PORT_CHAR('F')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)         PORT_CHAR('C')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6)         PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)         PORT_CHAR('D')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)         PORT_CHAR('R')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5)         PORT_CHAR('5') PORT_CHAR('%')

	PORT_START( "ROW3" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)         PORT_CHAR('V')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)         PORT_CHAR('U')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)         PORT_CHAR('H')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)         PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8)         PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)         PORT_CHAR('G')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)         PORT_CHAR('Y')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7)         PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START( "ROW4" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)         PORT_CHAR('N')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)         PORT_CHAR('O')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)         PORT_CHAR('K')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)         PORT_CHAR('M')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0)         PORT_CHAR('0')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)         PORT_CHAR('J')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)         PORT_CHAR('I')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9)         PORT_CHAR('9') PORT_CHAR(')')

	PORT_START( "ROW5" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA)     PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)     PORT_CHAR(':') PORT_CHAR('[')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)      PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS)    PORT_CHAR('-')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)         PORT_CHAR('L')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)         PORT_CHAR('P')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)     PORT_CHAR('+')

	PORT_START( "ROW6" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH)                             PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL)                               PORT_CHAR(0x2191,'^') PORT_CHAR(U'π') // U+2191 = ↑
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH)                         PORT_CHAR('=')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Shift (Right)") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CLR HOME") PORT_CODE(KEYCODE_INSERT)      PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE)                             PORT_CHAR(';') PORT_CHAR(']')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE)                        PORT_CHAR('*')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH2)                        PORT_CHAR(U'£')

	PORT_START( "ROW7" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RUN STOP") PORT_CODE(KEYCODE_HOME)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)                                 PORT_CHAR('Q')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CBM") PORT_CODE(KEYCODE_LALT)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE)                             PORT_CHAR(' ')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2)                                 PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TAB)                               PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE)                             PORT_CHAR(0x2190) // U+2190 = ←
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1)                                 PORT_CHAR('1') PORT_CHAR('!')

	PORT_START( "K0" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1_PAD)             PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7_PAD)             PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4_PAD)             PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2_PAD)             PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("TAB") PORT_CODE(KEYCODE_F6)               PORT_CHAR('\t')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5_PAD)             PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8_PAD)             PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("HELP") PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(PGUP))

	PORT_START( "K1" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3_PAD)             PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9_PAD)             PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6_PAD)             PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER_PAD)         PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LINE FEED") PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(PGDN))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD)          PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS_PAD)         PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ESC") PORT_CODE(KEYCODE_F5)               PORT_CHAR(UCHAR_MAMEKEY(ESC))

	PORT_START( "K2" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("NO SCROLL") PORT_CODE(KEYCODE_F12) PORT_TOGGLE
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RIGHT)             PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LEFT)              PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DOWN)              PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_UP)                PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL_PAD)           PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0_PAD)             PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ALT") PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(LALT))

	PORT_START( "RESTORE" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RESTORE") PORT_CODE(KEYCODE_PRTSCR) PORT_WRITE_LINE_MEMBER(FUNC(c128_state::write_restore))

	PORT_START( "LOCK" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START( "CAPS" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CAPS LOCK") PORT_CODE(KEYCODE_F8) PORT_TOGGLE PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(c128_state::caps_lock), 0)

	PORT_START( "40_80" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("40/80 DISPLAY") PORT_CODE(KEYCODE_F11) PORT_TOGGLE
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( c128_de )
//-------------------------------------------------

static INPUT_PORTS_START( c128_de )
	PORT_INCLUDE( c128 )

	PORT_MODIFY( "ROW1" )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Z  { Y }") PORT_CODE(KEYCODE_Z)                     PORT_CHAR('Z')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"3  #  { 3  § }") PORT_CODE(KEYCODE_3)             PORT_CHAR('3') PORT_CHAR('#')

	PORT_MODIFY( "ROW3" )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Y  { Z }") PORT_CODE(KEYCODE_Y)                     PORT_CHAR('Y')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("7  '  { 7  / }") PORT_CODE(KEYCODE_7)               PORT_CHAR('7') PORT_CHAR('\'')

	PORT_MODIFY( "ROW4" )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("0  { = }") PORT_CODE(KEYCODE_0)                     PORT_CHAR('0')

	PORT_MODIFY( "ROW5" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(",  <  { ; }") PORT_CODE(KEYCODE_COMMA)              PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"§  \u2191  { ü }") PORT_CODE(KEYCODE_OPENBRACE)   PORT_CHAR(U'§') PORT_CHAR(0x2191) // U+2191 = ↑
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8":  [  { ä }") PORT_CODE(KEYCODE_COLON)            PORT_CHAR(':') PORT_CHAR('[')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(".  >  { : }") PORT_CODE(KEYCODE_STOP)               PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("-  { '  ` }") PORT_CODE(KEYCODE_EQUALS)             PORT_CHAR('-')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"+  { ß ? }") PORT_CODE(KEYCODE_MINUS)             PORT_CHAR('+')

	PORT_MODIFY( "ROW6" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("/  ?  { -  _ }") PORT_CODE(KEYCODE_SLASH)           PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"Σ  π  { ] \\ }") PORT_CODE(KEYCODE_DEL)           PORT_CHAR(U'Σ') PORT_CHAR(U'π')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("=  { # ' }") PORT_CODE(KEYCODE_BACKSLASH)           PORT_CHAR('=')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8";  ]  { ö }") PORT_CODE(KEYCODE_QUOTE)            PORT_CHAR(';') PORT_CHAR(']')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("*  `  { +  * }") PORT_CODE(KEYCODE_CLOSEBRACE)      PORT_CHAR('*') PORT_CHAR('`')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\\  { [  \u2191 }") PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('\\') // U+2191 = ↑

	PORT_MODIFY( "ROW7" )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("_  { <  > }") PORT_CODE(KEYCODE_TILDE)              PORT_CHAR('_')

	PORT_MODIFY( "CAPS" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ASCII/DIN") PORT_CODE(KEYCODE_F8) PORT_TOGGLE PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(c128_state::caps_lock), 0)
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( c128_fr )
//-------------------------------------------------
#ifdef UNUSED_CODE
static INPUT_PORTS_START( c128_fr )
	PORT_INCLUDE( c128 )

	PORT_MODIFY( "ROW1" )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Z  { W }") PORT_CODE(KEYCODE_Z)                    PORT_CHAR('Z')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("4  $  { '  4 }") PORT_CODE(KEYCODE_4)              PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A  { Q }") PORT_CODE(KEYCODE_A)                    PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("W  { Z }") PORT_CODE(KEYCODE_W)                    PORT_CHAR('W')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("3  #  { \"  3 }") PORT_CODE(KEYCODE_3)             PORT_CHAR('3') PORT_CHAR('#')

	PORT_MODIFY( "ROW2" )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"6  &  { §  6 }") PORT_CODE(KEYCODE_6)            PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("5  %  { (  5 }") PORT_CODE(KEYCODE_5)              PORT_CHAR('5') PORT_CHAR('%')

	PORT_MODIFY( "ROW3" )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("8  (  { !  8 }") PORT_CODE(KEYCODE_8)              PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"7  '  { è  7 }") PORT_CODE(KEYCODE_7)            PORT_CHAR('7') PORT_CHAR('\'')

	PORT_MODIFY( "ROW4" )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("K  Large-  { \\ }") PORT_CODE(KEYCODE_K)           PORT_CHAR('K')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("M  Large-/  { ,  ? }") PORT_CODE(KEYCODE_M)        PORT_CHAR('M')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"0  { à  0 }") PORT_CODE(KEYCODE_0)               PORT_CHAR('0')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"9  )  { ç  9 }") PORT_CODE(KEYCODE_9)            PORT_CHAR('9') PORT_CHAR(')')

	PORT_MODIFY( "ROW5" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(",  <  { ;  . }") PORT_CODE(KEYCODE_COMMA)          PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"@  û  { ^  ¨ }") PORT_CODE(KEYCODE_OPENBRACE)    PORT_CHAR('@') PORT_CHAR(U'¨')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8":  [  { ù  % }") PORT_CODE(KEYCODE_COLON)        PORT_CHAR(':') PORT_CHAR('[')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(".  >  { :  / }") PORT_CODE(KEYCODE_STOP)           PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"-  °  { -  _ }") PORT_CODE(KEYCODE_EQUALS)       PORT_CHAR('-') PORT_CHAR(U'°')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"+  ë  { )  ° }") PORT_CODE(KEYCODE_MINUS)        PORT_CHAR('+') PORT_CHAR(U'ë')

	PORT_MODIFY( "ROW6" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("/  ?  { =  + }") PORT_CODE(KEYCODE_SLASH)          PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2191  π  { *  ] }") PORT_CODE(KEYCODE_DEL)     PORT_CHAR(0x2191) PORT_CHAR(U'π') // U+2191 = ↑
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"=  { \u2191  \\ }") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('=') // U+2191 = ↑
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(";  ]  { M  Large-/ }") PORT_CODE(KEYCODE_QUOTE)    PORT_CHAR(';') PORT_CHAR(']')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("*  `  { $  [ }") PORT_CODE(KEYCODE_CLOSEBRACE)     PORT_CHAR('*') PORT_CHAR('`')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\\  { @  # }") PORT_CODE(KEYCODE_BACKSLASH)        PORT_CHAR('\\')

	PORT_MODIFY( "ROW7" )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Q  { A }") PORT_CODE(KEYCODE_Q)                    PORT_CHAR('Q')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"2  \"  { é  2 }") PORT_CODE(KEYCODE_2)           PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("_   { <  > }") PORT_CODE(KEYCODE_TILDE)            PORT_CHAR('_')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("1  !  { &  1 }") PORT_CODE(KEYCODE_1)              PORT_CHAR('1') PORT_CHAR('!')

	PORT_MODIFY( "CAPS" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CAPS LOCK ASCII/CC") PORT_CODE(KEYCODE_F8) PORT_TOGGLE PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(c128_state::caps_lock), 0)
INPUT_PORTS_END
#endif

//-------------------------------------------------
//  INPUT_PORTS( c128_it )
//-------------------------------------------------
#ifdef UNUSED_CODE
static INPUT_PORTS_START( c128_it )
	PORT_INCLUDE( c128 )

	PORT_MODIFY( "ROW1" )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Z  { W }") PORT_CODE(KEYCODE_Z)                    PORT_CHAR('Z')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("4  $  { '  4 }") PORT_CODE(KEYCODE_4)              PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("W  { Z }") PORT_CODE(KEYCODE_W)                    PORT_CHAR('W')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("3  #  { \"  3 }") PORT_CODE(KEYCODE_3)             PORT_CHAR('3') PORT_CHAR('#')

	PORT_MODIFY( "ROW2" )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("6  &  { _  6 }") PORT_CODE(KEYCODE_6)              PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("5  %  { (  5 }") PORT_CODE(KEYCODE_5)              PORT_CHAR('5') PORT_CHAR('%')

	PORT_MODIFY( "ROW3" )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("8  (  { &  8 }") PORT_CODE(KEYCODE_8)              PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"7  '  { è  7 }") PORT_CODE(KEYCODE_7)            PORT_CHAR('7') PORT_CHAR('\'')

	PORT_MODIFY( "ROW4" )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("M  Large-/  { ,  ? }") PORT_CODE(KEYCODE_M)        PORT_CHAR('M')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"0  { à  0 }") PORT_CODE(KEYCODE_0)               PORT_CHAR('0')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"9  )  { ç  9 }") PORT_CODE(KEYCODE_9)            PORT_CHAR('9') PORT_CHAR(')')

	PORT_MODIFY( "ROW5" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(",  <   { ;  . }") PORT_CODE(KEYCODE_COMMA)         PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"@  û  { ì  = }") PORT_CODE(KEYCODE_OPENBRACE)    PORT_CHAR('@') PORT_CHAR(U'û')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8":  [  { ù  % }") PORT_CODE(KEYCODE_COLON)        PORT_CHAR(':') PORT_CHAR('[')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(".  >  { :  / }") PORT_CODE(KEYCODE_STOP)           PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"-  °  { -  + }") PORT_CODE(KEYCODE_EQUALS)       PORT_CHAR('-') PORT_CHAR(U'°')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"+  ë  { )  ° }") PORT_CODE(KEYCODE_MINUS)        PORT_CHAR('+') PORT_CHAR(U'ë')

	PORT_MODIFY( "ROW6" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"/  ?  { ò  ! }") PORT_CODE(KEYCODE_SLASH)        PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2191  π  { *  ] }") PORT_CODE(KEYCODE_DEL)     PORT_CHAR(0x2191) PORT_CHAR(U'π')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"=  { \u2191  \\ }") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('=') // U+2191 = ↑
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(";  ]  { M }") PORT_CODE(KEYCODE_QUOTE)             PORT_CHAR(';') PORT_CHAR(']')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("*  `  { $  [ }") PORT_CODE(KEYCODE_CLOSEBRACE)     PORT_CHAR('*') PORT_CHAR('`')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\\  { @  # }") PORT_CODE(KEYCODE_BACKSLASH2)       PORT_CHAR('\\')

	PORT_MODIFY( "ROW7" )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"2  \"  { é  2 }") PORT_CODE(KEYCODE_2)           PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("_  { <  > }") PORT_CODE(KEYCODE_TILDE)             PORT_CHAR('_')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"1  !  { £  1 }") PORT_CODE(KEYCODE_1)            PORT_CHAR('1') PORT_CHAR('!')
INPUT_PORTS_END
#endif

//-------------------------------------------------
//  INPUT_PORTS( c128_se )
//-------------------------------------------------

static INPUT_PORTS_START( c128_se )
	PORT_INCLUDE( c128 )

	PORT_MODIFY( "ROW1" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"3  #  { 3  § }") PORT_CODE(KEYCODE_3)   PORT_CHAR('3') PORT_CHAR('#')

	PORT_MODIFY( "ROW3" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("7  '  { 7  / }") PORT_CODE(KEYCODE_7)     PORT_CHAR('7') PORT_CHAR('\'')

	PORT_MODIFY( "ROW5" )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"]  { â }") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(']')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"[  { ä }") PORT_CODE(KEYCODE_COLON)     PORT_CHAR('[')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS)                            PORT_CHAR('=')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)                             PORT_CHAR('-')

	PORT_MODIFY( "ROW6" )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(";  +") PORT_CODE(KEYCODE_BACKSLASH)       PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"£  { ö }") PORT_CODE(KEYCODE_QUOTE)     PORT_CHAR(U'£')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("@") PORT_CODE(KEYCODE_CLOSEBRACE)         PORT_CHAR('@')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(":  *") PORT_CODE(KEYCODE_BACKSLASH2)      PORT_CHAR(':') PORT_CHAR('*')

	PORT_MODIFY( "ROW7" )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("_  { <  > }") PORT_CODE(KEYCODE_TILDE)    PORT_CHAR('_')

	PORT_MODIFY( "CAPS" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CAPS LOCK ASCII/CC") PORT_CODE(KEYCODE_F8) PORT_TOGGLE PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(c128_state::caps_lock), 0)
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  MOS8722_INTERFACE( mmu_intf )
//-------------------------------------------------

void c128_state::mmu_z80en_w(int state)
{
	if (state)
	{
		m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
		m_subcpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);

		if (m_reset)
		{
			m_subcpu->reset();

			m_reset = 0;
		}
	}
	else
	{
		m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
		m_subcpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
	}

	m_z80en = state;
}

void c128_state::mmu_fsdir_w(int state)
{
	update_iec();
}

int c128_state::mmu_game_r()
{
	return m_game;
}

int c128_state::mmu_exrom_r()
{
	return m_exrom;
}

int c128_state::mmu_sense40_r()
{
	return BIT(m_40_80->read(), 0);
}


//-------------------------------------------------
//  mc6845
//-------------------------------------------------

static GFXDECODE_START( gfx_c128 )
	GFXDECODE_ENTRY( "charom", 0x0000, gfx_8x8x1, 0, 1 )
GFXDECODE_END


//-------------------------------------------------
//  MOS8564_INTERFACE( vic_intf )
//-------------------------------------------------

void c128_state::vic_k_w(uint8_t data)
{
	m_vic_k = data;
}


//-------------------------------------------------
//  MOS6581_INTERFACE( sid_intf )
//-------------------------------------------------

uint8_t c128_state::sid_potx_r()
{
	uint8_t data = 0xff;

	switch (m_cia1->pa_r() >> 6)
	{
	case 1: data = m_joy1->read_pot_x(); break;
	case 2: data = m_joy2->read_pot_x(); break;
	case 3:
		if (m_joy1->has_pot_x() && m_joy2->has_pot_x())
		{
			data = 1 / (1 / m_joy1->read_pot_x() + 1 / m_joy2->read_pot_x());
		}
		else if (m_joy1->has_pot_x())
		{
			data = m_joy1->read_pot_x();
		}
		else if (m_joy2->has_pot_x())
		{
			data = m_joy2->read_pot_x();
		}
		break;
	}

	return data;
}

uint8_t c128_state::sid_poty_r()
{
	uint8_t data = 0xff;

	switch (m_cia1->pa_r() >> 6)
	{
	case 1: data = m_joy1->read_pot_y(); break;
	case 2: data = m_joy2->read_pot_y(); break;
	case 3:
		if (m_joy1->has_pot_y() && m_joy2->has_pot_y())
		{
			data = 1 / (1 / m_joy1->read_pot_y() + 1 / m_joy2->read_pot_y());
		}
		else if (m_joy1->has_pot_y())
		{
			data = m_joy1->read_pot_y();
		}
		else if (m_joy2->has_pot_y())
		{
			data = m_joy2->read_pot_y();
		}
		break;
	}

	return data;
}


//-------------------------------------------------
//  MOS6526_INTERFACE( cia1_intf )
//-------------------------------------------------

uint8_t c128_state::cia1_pa_r()
{
	/*

	    bit     description

	    PA0     COL0, JOYB0
	    PA1     COL1, JOYB1
	    PA2     COL2, JOYB2
	    PA3     COL3, JOYB3
	    PA4     COL4, FBTNB
	    PA5     COL5
	    PA6     COL6
	    PA7     COL7

	*/

	uint8_t data = 0xff;

	// joystick
	uint8_t joy_b = m_joy2->read_joy();

	data &= (0xf0 | (joy_b & 0x0f));
	data &= ~(!BIT(joy_b, 5) << 4);

	// keyboard
	uint8_t cia1_pb = m_cia1->pb_r();
	uint32_t row[8] = { m_row[0]->read(), m_row[1]->read() & m_lock->read(), m_row[2]->read(), m_row[3]->read(),
						m_row[4]->read(), m_row[5]->read(), m_row[6]->read(), m_row[7]->read() };

	for (int i = 0; i < 8; i++)
	{
		if (!BIT(cia1_pb, i))
		{
			if (!BIT(row[7], i)) data &= ~0x80;
			if (!BIT(row[6], i)) data &= ~0x40;
			if (!BIT(row[5], i)) data &= ~0x20;
			if (!BIT(row[4], i)) data &= ~0x10;
			if (!BIT(row[3], i)) data &= ~0x08;
			if (!BIT(row[2], i)) data &= ~0x04;
			if (!BIT(row[1], i)) data &= ~0x02;
			if (!BIT(row[0], i)) data &= ~0x01;
		}
	}

	return data;
}

void c128_state::cia1_pa_w(uint8_t data)
{
	/*

	    bit     description

	    PA0     COL0, JOYB0
	    PA1     COL1, JOYB1
	    PA2     COL2, JOYB2
	    PA3     COL3, JOYB3
	    PA4     COL4, FBTNB
	    PA5     COL5
	    PA6     COL6
	    PA7     COL7

	*/

	m_joy2->joy_w(data & 0x1f);
}

uint8_t c128_state::cia1_pb_r()
{
	/*

	    bit     description

	    PB0     ROW0, JOYA0
	    PB1     ROW1, JOYA1
	    PB2     ROW2, JOYA2
	    PB3     ROW3, JOYA3
	    PB4     ROW4, FBTNA, _LP
	    PB5     ROW5
	    PB6     ROW6
	    PB7     ROW7

	*/

	uint8_t data = 0xff;

	// joystick
	uint8_t joy_a = m_joy1->read_joy();

	data &= (0xf0 | (joy_a & 0x0f));
	data &= ~(!BIT(joy_a, 5) << 4);

	// keyboard
	uint8_t cia1_pa = m_cia1->pa_r();

	if (!BIT(cia1_pa, 7)) data &= m_row[7]->read();
	if (!BIT(cia1_pa, 6)) data &= m_row[6]->read();
	if (!BIT(cia1_pa, 5)) data &= m_row[5]->read();
	if (!BIT(cia1_pa, 4)) data &= m_row[4]->read();
	if (!BIT(cia1_pa, 3)) data &= m_row[3]->read();
	if (!BIT(cia1_pa, 2)) data &= m_row[2]->read();
	if (!BIT(cia1_pa, 1)) data &= m_row[1]->read() & m_lock->read();
	if (!BIT(cia1_pa, 0)) data &= m_row[0]->read();

	if (!BIT(m_vic_k, 0)) data &= m_k[0]->read();
	if (!BIT(m_vic_k, 1)) data &= m_k[1]->read();
	if (!BIT(m_vic_k, 2)) data &= m_k[2]->read();

	return data;
}

void c128_state::cia1_pb_w(uint8_t data)
{
	/*

	    bit     description

	    PB0     ROW0, JOYA0
	    PB1     ROW1, JOYA1
	    PB2     ROW2, JOYA2
	    PB3     ROW3, JOYA3
	    PB4     ROW4, FBTNA, _LP
	    PB5     ROW5
	    PB6     ROW6
	    PB7     ROW7

	*/

	m_joy1->joy_w(data & 0x1f);

	m_vic->lp_w(BIT(data, 4));
}

void c128_state::cia1_cnt_w(int state)
{
	m_cnt1 = state;
	m_user->write_4(state);

	update_iec();
}

void c128_state::cia1_sp_w(int state)
{
	m_sp1 = state;
	m_user->write_5(state);

	update_iec();
}


//-------------------------------------------------
//  MOS6526_INTERFACE( cia2_intf )
//-------------------------------------------------

uint8_t c128_state::cia2_pa_r()
{
	/*

	    bit     description

	    PA0
	    PA1
	    PA2     USER PORT
	    PA3
	    PA4
	    PA5
	    PA6     CLK
	    PA7     DATA

	*/

	uint8_t data = 0;

	// user port
	data |= m_user_pa2 << 2;

	// IEC bus
	data |= m_iec->clk_r() << 6;
	data |= m_iec->data_r() << 7;

	return data;
}

void c128_state::cia2_pa_w(uint8_t data)
{
	/*

	    bit     description

	    PA0     _VA14
	    PA1     _VA15
	    PA2     USER PORT
	    PA3     ATN OUT
	    PA4     CLK OUT
	    PA5     DATA OUT
	    PA6
	    PA7

	*/

	// VIC banking
	m_va14 = BIT(data, 0);
	m_va15 = BIT(data, 1);

	// user port
	m_user->write_m(BIT(data, 2));

	// IEC bus
	m_iec->host_atn_w(!BIT(data, 3));
	m_iec->host_clk_w(!BIT(data, 4));
	m_iec_data_out = BIT(data, 5);

	update_iec();
}

uint8_t c128_state::cia2_pb_r()
{
	return m_user_pb;
}

void c128_state::cia2_pb_w(uint8_t data)
{
	m_user->write_c((data>>0)&1);
	m_user->write_d((data>>1)&1);
	m_user->write_e((data>>2)&1);
	m_user->write_f((data>>3)&1);
	m_user->write_h((data>>4)&1);
	m_user->write_j((data>>5)&1);
	m_user->write_k((data>>6)&1);
	m_user->write_l((data>>7)&1);
}

//-------------------------------------------------
//  M6510_INTERFACE( cpu_intf )
//-------------------------------------------------

uint8_t c128_state::cpu_r()
{
	/*

	    bit     description

	    P0      1
	    P1      1
	    P2      1
	    P3
	    P4      CASS SENSE
	    P5
	    P6      CAPS LOCK

	*/

	uint8_t data = 0x07;

	// cassette sense
	data |= m_cassette->sense_r() << 4;

	// CAPS LOCK
	data |= m_caps_lock << 6;

	return data;
}

void c128_state::cpu_w(uint8_t data)
{
	/*

	    bit     description

	    P0      LORAM
	    P1      HIRAM
	    P2      CHAREN
	    P3      CASS WRT
	    P4
	    P5      CASS MOTOR
	    P6

	*/

	// memory banking
	m_loram = BIT(data, 0);
	m_hiram = BIT(data, 1);
	m_charen = BIT(data, 2);

	// cassette write
	m_cassette->write(BIT(data, 3));

	// cassette motor
	m_cassette->motor_w(BIT(data, 5));
}


//-------------------------------------------------
//  CBM_IEC_INTERFACE( cbm_iec_intf )
//-------------------------------------------------

void c128_state::update_cia1_flag()
{
	m_cia1->flag_w(m_cass_rd & m_iec_srq);
}

inline void c128_state::update_iec()
{
	int fsdir = m_mmu->fsdir_r();

	// fast serial data in
	int data_in = m_iec->data_r();

	m_cia1->sp_w(fsdir || data_in);

	// fast serial data out
	int data_out = !m_iec_data_out;

	if (fsdir) data_out &= m_sp1;

	m_iec->host_data_w(data_out);

	// fast serial clock in
	m_cia1->cnt_w(fsdir || m_iec_srq);

	// fast serial clock out
	int srq_out = 1;

	if (fsdir) srq_out &= m_cnt1;

	m_iec->host_srq_w(srq_out);
}

void c128_state::iec_srq_w(int state)
{
	m_iec_srq = state;
	update_iec();
	update_cia1_flag();
}

void c128_state::iec_data_w(int state)
{
	update_iec();
}


//-------------------------------------------------
//  C64_EXPANSION_INTERFACE( expansion_intf )
//-------------------------------------------------

uint8_t c128_state::exp_dma_cd_r(offs_t offset)
{
	int ba = 0, aec = 1, z80io = 1;
	offs_t vma = 0;

	return read_memory(offset, vma, ba, aec, z80io);
}

void c128_state::exp_dma_cd_w(offs_t offset, uint8_t data)
{
	int ba = 0, aec = 1, z80io = 1;
	offs_t vma = 0;

	return write_memory(offset, data, vma, ba, aec, z80io);
}

void c128_state::exp_dma_w(int state)
{
	m_exp_dma = state;

	check_interrupts();
}

void c128_state::exp_reset_w(int state)
{
	if (!state)
	{
		machine_reset();
	}
}


//-------------------------------------------------
//  SLOT_INTERFACE( c128dcr_iec_devices )
//-------------------------------------------------

[[maybe_unused]] void c128dcr_iec_devices(device_slot_interface &device)
{
	device.option_add("c1571", C1571);
	device.option_add("c1571cr", C1571CR);
}


//-------------------------------------------------
//  SLOT_INTERFACE( c128d81_iec_devices )
//-------------------------------------------------

void c128d81_iec_devices(device_slot_interface &device)
{
	device.option_add("c1563", C1563);
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( c64 )
//-------------------------------------------------

void c128_state::machine_start()
{
	// initialize memory
	uint8_t data = 0xff;

	for (offs_t offset = 0; offset < m_ram->size(); offset++)
	{
		m_ram->pointer()[offset] = data;
		if (!(offset % 64)) data ^= 0xff;
	}

	// state saving
	save_item(NAME(m_z80en));
	save_item(NAME(m_loram));
	save_item(NAME(m_hiram));
	save_item(NAME(m_charen));
	save_item(NAME(m_game));
	save_item(NAME(m_exrom));
	save_item(NAME(m_reset));
	save_item(NAME(m_va14));
	save_item(NAME(m_va15));
	save_item(NAME(m_clrbank));
	save_item(NAME(m_cnt1));
	save_item(NAME(m_sp1));
	save_item(NAME(m_iec_data_out));
	save_item(NAME(m_exp_dma));
	save_item(NAME(m_cass_rd));
	save_item(NAME(m_iec_srq));
	save_item(NAME(m_vic_k));
	save_item(NAME(m_caps_lock));
	save_item(NAME(m_user_pa2));
	save_item(NAME(m_user_pb));
}


void c128_state::machine_reset()
{
	m_maincpu->reset();
	m_reset = 1;

	m_mmu->reset();
	m_vic->reset();
	m_vdc->reset();
	m_sid->reset();
	m_cia1->reset();
	m_cia2->reset();

	m_iec->reset();
	m_exp->reset();

	m_user->write_3(0);
	m_user->write_3(1);
}



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

//-------------------------------------------------
//  machine_config( softlists )
//-------------------------------------------------

void c128_state::softlists(machine_config &config, const char *filter)
{
	SOFTWARE_LIST(config, "cart_list").set_original("c128_cart").set_filter(filter);
	SOFTWARE_LIST(config, "flop_list").set_original("c128_flop").set_filter(filter);
	SOFTWARE_LIST(config, "from_list").set_original("c128_rom").set_filter(filter);
	SOFTWARE_LIST(config, "cart_list_c64").set_original("c64_cart").set_filter(filter);
	SOFTWARE_LIST(config, "cass_list_c64").set_original("c64_cass").set_filter(filter);
	SOFTWARE_LIST(config, "cart_list_vic10").set_original("vic10").set_filter(filter);
	SOFTWARE_LIST(config, "flop_list_c64_orig").set_compatible("c64_flop_orig").set_filter(filter);
	SOFTWARE_LIST(config, "flop_list_c64_misc").set_compatible("c64_flop_misc").set_filter(filter);
}


//-------------------------------------------------
//  machine_config( ntsc )
//-------------------------------------------------

void c128_state::ntsc(machine_config &config)
{
	// basic hardware
	Z80(config, m_maincpu, XTAL(14'318'181)*2/3.5/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &c128_state::z80_mem);
	m_maincpu->set_addrmap(AS_IO, &c128_state::z80_io);

	M8502(config, m_subcpu, XTAL(14'318'181)*2/3.5/8);
	m_subcpu->read_callback().set(FUNC(c128_state::cpu_r));
	m_subcpu->write_callback().set(FUNC(c128_state::cpu_w));
	m_subcpu->set_pulls(0x07, 0x20);
	m_subcpu->set_addrmap(AS_PROGRAM, &c128_state::m8502_mem);
	config.set_perfect_quantum(m_subcpu);

	input_merger_device &irq(INPUT_MERGER_ANY_HIGH(config, "irq"));
	irq.output_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	irq.output_handler().append_inputline(m_subcpu, m8502_device::IRQ_LINE);

	INPUT_MERGER_ANY_HIGH(config, m_nmi);
	m_nmi->output_handler().set_inputline(m_subcpu, m8502_device::NMI_LINE);

	// video hardware
	MOS8563(config, m_vdc, XTAL(16'000'000));
	m_vdc->set_screen(SCREEN_VDC_TAG);
	m_vdc->set_addrmap(0, &c128_state::vdc_videoram_map);
	m_vdc->set_show_border_area(true);
	m_vdc->set_char_width(8);

	screen_device &screen_vdc(SCREEN(config, SCREEN_VDC_TAG, SCREEN_TYPE_RASTER));
	screen_vdc.set_refresh_hz(60);
	screen_vdc.set_size(640, 200);
	screen_vdc.set_visarea(0, 640-1, 0, 200-1);
	screen_vdc.set_screen_update(MOS8563_TAG, FUNC(mos8563_device::screen_update));

	MOS8564(config, m_vic, XTAL(14'318'181)*2/3.5);
	m_vic->set_cpu(m_subcpu);
	m_vic->irq_callback().set("irq", FUNC(input_merger_device::in_w<1>));
	m_vic->k_callback().set(FUNC(c128_state::vic_k_w));
	m_vic->set_screen(SCREEN_VIC_TAG);
	m_vic->set_addrmap(0, &c128_state::vic_videoram_map);
	m_vic->set_addrmap(1, &c128_state::vic_colorram_map);

	screen_device &screen_vic(SCREEN(config, SCREEN_VIC_TAG, SCREEN_TYPE_RASTER));
	screen_vic.set_refresh_hz(VIC6567_VRETRACERATE);
	screen_vic.set_size(VIC6567_COLUMNS, VIC6567_LINES);
	screen_vic.set_visarea(0, VIC6567_VISIBLECOLUMNS - 1, 0, VIC6567_VISIBLELINES - 1);
	screen_vic.set_screen_update(MOS8564_TAG, FUNC(mos8564_device::screen_update));

	GFXDECODE(config, "gfxdecode", MOS8563_TAG, gfx_c128);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	MOS6581(config, m_sid, XTAL(14'318'181)*2/3.5/8);
	m_sid->potx().set(FUNC(c128_state::sid_potx_r));
	m_sid->poty().set(FUNC(c128_state::sid_poty_r));
	m_sid->add_route(ALL_OUTPUTS, "speaker", 0.5);

	// devices
	MOS8722(config, m_mmu, XTAL(14'318'181)*2/3.5/8);
	m_mmu->z80en().set(FUNC(c128_state::mmu_z80en_w));
	m_mmu->fsdir().set(FUNC(c128_state::mmu_fsdir_w));
	m_mmu->game().set(FUNC(c128_state::mmu_game_r));
	m_mmu->exrom().set(FUNC(c128_state::mmu_exrom_r));
	m_mmu->sense40().set(FUNC(c128_state::mmu_sense40_r));

	MOS8721(config, m_pla);

	MOS6526(config, m_cia1, XTAL(14'318'181)*2/3.5/8);
	m_cia1->set_tod_clock(60);
	m_cia1->irq_wr_callback().set("irq", FUNC(input_merger_device::in_w<0>));
	m_cia1->cnt_wr_callback().set(FUNC(c128_state::cia1_cnt_w));
	m_cia1->sp_wr_callback().set(FUNC(c128_state::cia1_sp_w));
	m_cia1->pa_rd_callback().set(FUNC(c128_state::cia1_pa_r));
	m_cia1->pa_wr_callback().set(FUNC(c128_state::cia1_pa_w));
	m_cia1->pb_rd_callback().set(FUNC(c128_state::cia1_pb_r));
	m_cia1->pb_wr_callback().set(FUNC(c128_state::cia1_pb_w));

	MOS6526(config, m_cia2, XTAL(14'318'181)*2/3.5/8);
	m_cia2->set_tod_clock(60);
	m_cia2->irq_wr_callback().set(m_nmi, FUNC(input_merger_device::in_w<0>));
	m_cia2->cnt_wr_callback().set(m_user, FUNC(pet_user_port_device::write_6));
	m_cia2->sp_wr_callback().set(m_user, FUNC(pet_user_port_device::write_7));
	m_cia2->pa_rd_callback().set(FUNC(c128_state::cia2_pa_r));
	m_cia2->pa_wr_callback().set(FUNC(c128_state::cia2_pa_w));
	m_cia2->pb_rd_callback().set(FUNC(c128_state::cia2_pb_r));
	m_cia2->pb_wr_callback().set(FUNC(c128_state::cia2_pb_w));
	m_cia2->pc_wr_callback().set(m_user, FUNC(pet_user_port_device::write_8));

	PET_DATASSETTE_PORT(config, m_cassette, cbm_datassette_devices, "c1530");
	m_cassette->read_handler().set(FUNC(c128_state::cass_rd_w));

	VCS_CONTROL_PORT(config, m_joy1, vcs_control_port_devices, nullptr);
	m_joy1->trigger_wr_callback().set(m_vic, FUNC(mos8564_device::lp_w));
	VCS_CONTROL_PORT(config, m_joy2, vcs_control_port_devices, "joy");

	C64_EXPANSION_SLOT(config, m_exp, XTAL(14'318'181)*2/3.5/8, c64_expansion_cards, nullptr);
	m_exp->irq_callback().set("irq", FUNC(input_merger_device::in_w<2>));
	m_exp->nmi_callback().set(m_nmi, FUNC(input_merger_device::in_w<2>));
	m_exp->reset_callback().set(FUNC(c128_state::exp_reset_w));
	m_exp->cd_input_callback().set(FUNC(c128_state::exp_dma_cd_r));
	m_exp->cd_output_callback().set(FUNC(c128_state::exp_dma_cd_w));
	m_exp->dma_callback().set(FUNC(c128_state::exp_dma_w));

	PET_USER_PORT(config, m_user, c64_user_port_cards, nullptr);
	m_user->p3_handler().set(FUNC(c128_state::exp_reset_w));
	m_user->p4_handler().set(m_cia1, FUNC(mos6526_device::cnt_w));
	m_user->p5_handler().set(m_cia1, FUNC(mos6526_device::sp_w));
	m_user->p6_handler().set(m_cia2, FUNC(mos6526_device::cnt_w));
	m_user->p7_handler().set(m_cia2, FUNC(mos6526_device::sp_w));
	m_user->p9_handler().set(m_iec, FUNC(cbm_iec_device::host_atn_w));
	m_user->pb_handler().set(m_cia2, FUNC(mos6526_device::flag_w));
	m_user->pc_handler().set(FUNC(c128_state::write_user_pb0));
	m_user->pd_handler().set(FUNC(c128_state::write_user_pb1));
	m_user->pe_handler().set(FUNC(c128_state::write_user_pb2));
	m_user->pf_handler().set(FUNC(c128_state::write_user_pb3));
	m_user->ph_handler().set(FUNC(c128_state::write_user_pb4));
	m_user->pj_handler().set(FUNC(c128_state::write_user_pb5));
	m_user->pk_handler().set(FUNC(c128_state::write_user_pb6));
	m_user->pl_handler().set(FUNC(c128_state::write_user_pb7));
	m_user->pm_handler().set(FUNC(c128_state::write_user_pa2));

	QUICKLOAD(config, "quickload", "p00,prg", CBM_QUICKLOAD_DELAY).set_load_callback(FUNC(c128_state::quickload_c128));

	// software lists
	softlists(config, "NTSC");

	// function ROM
	GENERIC_SOCKET(config, "from", generic_plain_slot, "c128_rom", "bin,rom");

	// internal ram
	RAM(config, RAM_TAG).set_default_size("128K");
}


//-------------------------------------------------
//  machine_config( c128 )
//-------------------------------------------------

void c128_state::c128(machine_config &config)
{
	ntsc(config);
	cbm_iec_slot_device::add(config, m_iec, "c1571");
	m_iec->srq_callback().set(FUNC(c128_state::iec_srq_w));
	m_iec->data_callback().set(FUNC(c128_state::iec_data_w));
}


//-------------------------------------------------
//  machine_config( c128dcr )
//-------------------------------------------------

void c128_state::c128dcr(machine_config &config)
{
	ntsc(config);
	cbm_iec_slot_device::add(config, m_iec, "c1571"); // TODO c1571cr
	m_iec->srq_callback().set(FUNC(c128_state::iec_srq_w));
	m_iec->data_callback().set(FUNC(c128_state::iec_data_w));
}


//-------------------------------------------------
//  machine_config( c128d81 )
//-------------------------------------------------

void c128_state::c128d81(machine_config &config)
{
	ntsc(config);
	cbm_iec_slot_device::add(config, m_iec, nullptr);
	m_iec->srq_callback().set(FUNC(c128_state::iec_srq_w));
	m_iec->data_callback().set(FUNC(c128_state::iec_data_w));

	CBM_IEC_SLOT(config.replace(), "iec8", 8, c128d81_iec_devices, "c1563");
}


//-------------------------------------------------
//  machine_config( pal )
//-------------------------------------------------

void c128_state::pal(machine_config &config)
{
	// basic hardware
	Z80(config, m_maincpu, XTAL(17'734'472)*2/4.5/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &c128_state::z80_mem);
	m_maincpu->set_addrmap(AS_IO, &c128_state::z80_io);

	M8502(config, m_subcpu, XTAL(17'734'472)*2/4.5/8);
	m_subcpu->read_callback().set(FUNC(c128_state::cpu_r));
	m_subcpu->write_callback().set(FUNC(c128_state::cpu_w));
	m_subcpu->set_pulls(0x07, 0x20);
	m_subcpu->set_addrmap(AS_PROGRAM, &c128_state::m8502_mem);
	config.set_perfect_quantum(m_subcpu);

	input_merger_device &irq(INPUT_MERGER_ANY_HIGH(config, "irq"));
	irq.output_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	irq.output_handler().append_inputline(m_subcpu, m8502_device::IRQ_LINE);

	INPUT_MERGER_ANY_HIGH(config, m_nmi);
	m_nmi->output_handler().set_inputline(m_subcpu, m8502_device::NMI_LINE);

	// video hardware
	MOS8563(config, m_vdc, XTAL(16'000'000));
	m_vdc->set_screen(SCREEN_VDC_TAG);
	m_vdc->set_addrmap(0, &c128_state::vdc_videoram_map);
	m_vdc->set_show_border_area(true);
	m_vdc->set_char_width(8);

	screen_device &screen_vdc(SCREEN(config, SCREEN_VDC_TAG, SCREEN_TYPE_RASTER));
	screen_vdc.set_refresh_hz(60);
	screen_vdc.set_size(640, 200);
	screen_vdc.set_visarea(0, 640-1, 0, 200-1);
	screen_vdc.set_screen_update(MOS8563_TAG, FUNC(mos8563_device::screen_update));

	mos8566_device &mos8566(MOS8566(config, MOS8566_TAG, XTAL(17'734'472)*2/4.5));
	mos8566.set_cpu(M8502_TAG);
	mos8566.irq_callback().set("irq", FUNC(input_merger_device::in_w<1>));
	mos8566.k_callback().set(FUNC(c128_state::vic_k_w));
	mos8566.set_screen(SCREEN_VIC_TAG);
	mos8566.set_addrmap(0, &c128_state::vic_videoram_map);
	mos8566.set_addrmap(1, &c128_state::vic_colorram_map);

	screen_device &screen_vic(SCREEN(config, SCREEN_VIC_TAG, SCREEN_TYPE_RASTER));
	screen_vic.set_refresh_hz(VIC6569_VRETRACERATE);
	screen_vic.set_size(VIC6569_COLUMNS, VIC6569_LINES);
	screen_vic.set_visarea(0, VIC6569_VISIBLECOLUMNS - 1, 0, VIC6569_VISIBLELINES - 1);
	screen_vic.set_screen_update(MOS8566_TAG, FUNC(mos8566_device::screen_update));

	GFXDECODE(config, "gfxdecode", MOS8563_TAG, gfx_c128);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	MOS6581(config, m_sid, XTAL(17'734'472)*2/4.5/8);
	m_sid->potx().set(FUNC(c128_state::sid_potx_r));
	m_sid->poty().set(FUNC(c128_state::sid_poty_r));
	m_sid->add_route(ALL_OUTPUTS, "speaker", 0.5);

	// devices
	MOS8722(config, m_mmu, XTAL(17'734'472)*2/4.5/8);
	m_mmu->z80en().set(FUNC(c128_state::mmu_z80en_w));
	m_mmu->fsdir().set(FUNC(c128_state::mmu_fsdir_w));
	m_mmu->game().set(FUNC(c128_state::mmu_game_r));
	m_mmu->exrom().set(FUNC(c128_state::mmu_exrom_r));
	m_mmu->sense40().set(FUNC(c128_state::mmu_sense40_r));

	MOS8721(config, m_pla);

	MOS6526(config, m_cia1, XTAL(17'734'472)*2/4.5/8);
	m_cia1->set_tod_clock(50);
	m_cia1->irq_wr_callback().set("irq", FUNC(input_merger_device::in_w<0>));
	m_cia1->cnt_wr_callback().set(FUNC(c128_state::cia1_cnt_w));
	m_cia1->sp_wr_callback().set(FUNC(c128_state::cia1_sp_w));
	m_cia1->pa_rd_callback().set(FUNC(c128_state::cia1_pa_r));
	m_cia1->pa_wr_callback().set(FUNC(c128_state::cia1_pa_w));
	m_cia1->pb_rd_callback().set(FUNC(c128_state::cia1_pb_r));
	m_cia1->pb_wr_callback().set(FUNC(c128_state::cia1_pb_w));

	MOS6526(config, m_cia2, XTAL(17'734'472)*2/4.5/8);
	m_cia2->set_tod_clock(50);
	m_cia2->irq_wr_callback().set(m_nmi, FUNC(input_merger_device::in_w<0>));
	m_cia2->cnt_wr_callback().set(m_user, FUNC(pet_user_port_device::write_6));
	m_cia2->sp_wr_callback().set(m_user, FUNC(pet_user_port_device::write_7));
	m_cia2->pa_rd_callback().set(FUNC(c128_state::cia2_pa_r));
	m_cia2->pa_wr_callback().set(FUNC(c128_state::cia2_pa_w));
	m_cia2->pb_rd_callback().set(FUNC(c128_state::cia2_pb_r));
	m_cia2->pb_wr_callback().set(FUNC(c128_state::cia2_pb_w));
	m_cia2->pc_wr_callback().set(m_user, FUNC(pet_user_port_device::write_8));

	PET_DATASSETTE_PORT(config, m_cassette, cbm_datassette_devices, "c1530");
	m_cassette->read_handler().set(FUNC(c128_state::cass_rd_w));

	VCS_CONTROL_PORT(config, m_joy1, vcs_control_port_devices, nullptr);
	m_joy1->trigger_wr_callback().set(m_vic, FUNC(mos8566_device::lp_w));
	VCS_CONTROL_PORT(config, m_joy2, vcs_control_port_devices, "joy");

	C64_EXPANSION_SLOT(config, m_exp, XTAL(17'734'472)*2/4.5/8, c64_expansion_cards, nullptr);
	m_exp->irq_callback().set("irq", FUNC(input_merger_device::in_w<2>));
	m_exp->nmi_callback().set(m_nmi, FUNC(input_merger_device::in_w<2>));
	m_exp->reset_callback().set(FUNC(c128_state::exp_reset_w));
	m_exp->cd_input_callback().set(FUNC(c128_state::exp_dma_cd_r));
	m_exp->cd_output_callback().set(FUNC(c128_state::exp_dma_cd_w));
	m_exp->dma_callback().set(FUNC(c128_state::exp_dma_w));

	PET_USER_PORT(config, m_user, c64_user_port_cards, nullptr);
	m_user->p3_handler().set(FUNC(c128_state::exp_reset_w));
	m_user->p4_handler().set(m_cia1, FUNC(mos6526_device::cnt_w));
	m_user->p5_handler().set(m_cia1, FUNC(mos6526_device::sp_w));
	m_user->p6_handler().set(m_cia2, FUNC(mos6526_device::cnt_w));
	m_user->p7_handler().set(m_cia2, FUNC(mos6526_device::sp_w));
	m_user->p9_handler().set(m_iec, FUNC(cbm_iec_device::host_atn_w));
	m_user->pb_handler().set(m_cia2, FUNC(mos6526_device::flag_w));
	m_user->pc_handler().set(FUNC(c128_state::write_user_pb0));
	m_user->pd_handler().set(FUNC(c128_state::write_user_pb1));
	m_user->pe_handler().set(FUNC(c128_state::write_user_pb2));
	m_user->pf_handler().set(FUNC(c128_state::write_user_pb3));
	m_user->ph_handler().set(FUNC(c128_state::write_user_pb4));
	m_user->pj_handler().set(FUNC(c128_state::write_user_pb5));
	m_user->pk_handler().set(FUNC(c128_state::write_user_pb6));
	m_user->pl_handler().set(FUNC(c128_state::write_user_pb7));
	m_user->pm_handler().set(FUNC(c128_state::write_user_pa2));

	QUICKLOAD(config, "quickload", "p00,prg", CBM_QUICKLOAD_DELAY).set_load_callback(FUNC(c128_state::quickload_c128));

	// software list
	softlists(config, "PAL");

	// function ROM
	GENERIC_SOCKET(config, "from", generic_plain_slot, "c128_rom", "bin,rom");

	// internal ram
	RAM(config, RAM_TAG).set_default_size("128K");
}


//-------------------------------------------------
//  machine_config( c128pal )
//-------------------------------------------------

void c128_state::c128pal(machine_config &config)
{
	pal(config);
	cbm_iec_slot_device::add(config, m_iec, "c1571");
	m_iec->srq_callback().set(FUNC(c128_state::iec_srq_w));
	m_iec->data_callback().set(FUNC(c128_state::iec_data_w));
}


//-------------------------------------------------
//  machine_config( c128dcrp )
//-------------------------------------------------

void c128_state::c128dcrp(machine_config &config)
{
	pal(config);
	cbm_iec_slot_device::add(config, m_iec, "c1571"); // TODO c1571cr
	m_iec->srq_callback().set(FUNC(c128_state::iec_srq_w));
	m_iec->data_callback().set(FUNC(c128_state::iec_data_w));
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( c128 )
//-------------------------------------------------

ROM_START( c128 )
	ROM_REGION( 0x10000, M8502_TAG, 0 )
	ROM_DEFAULT_BIOS("r4")
	ROM_LOAD( "251913-01.u32", 0x0000, 0x4000, CRC(0010ec31) SHA1(765372a0e16cbb0adf23a07b80f6b682b39fbf88) )
	ROM_SYSTEM_BIOS( 0, "r2", "Revision 2" )
	ROMX_LOAD( "318018-02.u33", 0x4000, 0x4000, CRC(2ee6e2fa) SHA1(60e1491e1d5782e3cf109f518eb73427609badc6), ROM_BIOS(0) )
	ROMX_LOAD( "318019-02.u34", 0x8000, 0x4000, CRC(d551fce0) SHA1(4d223883e866645328f86a904b221464682edc4f), ROM_BIOS(0) )
	ROMX_LOAD( "318020-03.u35", 0xc000, 0x4000, CRC(1e94bb02) SHA1(e80ffbafae068cc0e42698ec5c5c39af46ac612a), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "r4", "Revision 4" )
	ROMX_LOAD( "318018-04.u33", 0x4000, 0x4000, CRC(9f9c355b) SHA1(d53a7884404f7d18ebd60dd3080c8f8d71067441), ROM_BIOS(1) )
	ROMX_LOAD( "318019-04.u34", 0x8000, 0x4000, CRC(6e2c91a7) SHA1(c4fb4a714e48a7bf6c28659de0302183a0e0d6c0), ROM_BIOS(1) )
	ROMX_LOAD( "318020-05.u35", 0xc000, 0x4000, CRC(ba456b8e) SHA1(ceb6e1a1bf7e08eb9cbc651afa29e26adccf38ab), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "jiffydos", "JiffyDOS v6.01" )
	ROMX_LOAD( "318018-04.u33", 0x4000, 0x4000, CRC(9f9c355b) SHA1(d53a7884404f7d18ebd60dd3080c8f8d71067441), ROM_BIOS(2) )
	ROMX_LOAD( "318019-04.u34", 0x8000, 0x4000, CRC(6e2c91a7) SHA1(c4fb4a714e48a7bf6c28659de0302183a0e0d6c0), ROM_BIOS(2) )
	ROMX_LOAD( "jiffydos c128.u35", 0xc000, 0x4000, CRC(4b7964de) SHA1(7d1898f32beae4b2ae610d469ce578a588efaa7c), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "quikslvr", "QuickSilver 128" ) // requires add-on cartridge
	ROMX_LOAD( "318018-04.u33", 0x4000, 0x4000, CRC(9f9c355b) SHA1(d53a7884404f7d18ebd60dd3080c8f8d71067441), ROM_BIOS(3) )
	ROMX_LOAD( "318019-04.u34", 0x8000, 0x4000, CRC(6e2c91a7) SHA1(c4fb4a714e48a7bf6c28659de0302183a0e0d6c0), ROM_BIOS(3) )
	ROMX_LOAD( "quicksilver128.u35", 0xc000, 0x4000, CRC(c2e74338) SHA1(916cdcc62eb631073aa7f096815dcf33b3229ca8), ROM_BIOS(3) )

	ROM_REGION( 0x2000, "charom", 0 )
	ROM_LOAD( "390059-01.u18", 0x0000, 0x2000, CRC(6aaaafe6) SHA1(29ed066d513f2d5c09ff26d9166ba23c2afb2b3f) )

	ROM_REGION( 0xc88, MOS8721_TAG, 0 )
	// converted from http://www.zimmers.net/anonftp/pub/cbm/firmware/computers/c128/8721-reduced.zip/8721-reduced.txt
	ROM_LOAD( "8721r3.u11", 0x000, 0xc88, BAD_DUMP CRC(154db186) SHA1(ccadcdb1db3b62c51dc4ce60fe6f96831586d297) )
ROM_END

#define rom_c128p       rom_c128
#define rom_c128d       rom_c128
#define rom_c128dp      rom_c128
#define rom_c128dpr     rom_c128
#define rom_c128d81     rom_c128


//-------------------------------------------------
//  ROM( c128_de )
//-------------------------------------------------

ROM_START( c128_de )
	ROM_REGION( 0x10000, M8502_TAG, 0 )
	ROM_DEFAULT_BIOS("r4")
	ROM_LOAD( "251913-01.u32", 0x0000, 0x4000, CRC(0010ec31) SHA1(765372a0e16cbb0adf23a07b80f6b682b39fbf88) )
	ROM_SYSTEM_BIOS( 0, "r2", "Revision 2" )
	ROMX_LOAD( "318018-02.u33", 0x4000, 0x4000, CRC(2ee6e2fa) SHA1(60e1491e1d5782e3cf109f518eb73427609badc6), ROM_BIOS(0) )
	ROMX_LOAD( "318019-02.u34", 0x8000, 0x4000, CRC(d551fce0) SHA1(4d223883e866645328f86a904b221464682edc4f), ROM_BIOS(0) )
	ROMX_LOAD( "315078-01.u35", 0xc000, 0x4000, CRC(a51e2168) SHA1(bcf82a89a8fc5d086bec2ff3bcbdecc8af2be3af), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "r4", "Revision 4" )
	ROMX_LOAD( "318018-04.u33", 0x4000, 0x4000, CRC(9f9c355b) SHA1(d53a7884404f7d18ebd60dd3080c8f8d71067441), ROM_BIOS(1) )
	ROMX_LOAD( "318019-04.u34", 0x8000, 0x4000, CRC(6e2c91a7) SHA1(c4fb4a714e48a7bf6c28659de0302183a0e0d6c0), ROM_BIOS(1) )
	ROMX_LOAD( "315078-02.u35", 0xc000, 0x4000, CRC(b275bb2e) SHA1(78ac5dcdd840b092ba1ee6d19b33af079613291f), ROM_BIOS(1) )

	ROM_REGION( 0x2000, "charom", 0 )
	ROM_LOAD( "315079-01.u18", 0x00000, 0x2000, CRC(fe5a2db1) SHA1(638f8aff51c2ac4f99a55b12c4f8c985ef4bebd3) )

	ROM_REGION( 0xc88, MOS8721_TAG, 0 )
	// converted from http://www.zimmers.net/anonftp/pub/cbm/firmware/computers/c128/8721-reduced.zip/8721-reduced.txt
	ROM_LOAD( "8721r3.u11", 0x000, 0xc88, BAD_DUMP CRC(154db186) SHA1(ccadcdb1db3b62c51dc4ce60fe6f96831586d297) )
ROM_END


//-------------------------------------------------
//  ROM( c128_se )
//-------------------------------------------------

ROM_START( c128_se )
	ROM_REGION( 0x10000, M8502_TAG, 0 )
	ROM_LOAD( "325182-01.u32", 0x0000, 0x4000, CRC(2aff27d3) SHA1(267654823c4fdf2167050f41faa118218d2569ce) ) // "C128 64 Sw/Fi"
	ROM_LOAD( "318018-02.u33", 0x4000, 0x4000, CRC(2ee6e2fa) SHA1(60e1491e1d5782e3cf109f518eb73427609badc6) )
	ROM_LOAD( "318019-02.u34", 0x8000, 0x4000, CRC(d551fce0) SHA1(4d223883e866645328f86a904b221464682edc4f) )
	ROM_LOAD( "325189-01.u35", 0xc000, 0x4000, CRC(9526fac4) SHA1(a01dd871241c801db51e8ebc30fedfafd8cc506b) ) // "C128 Ker Sw/Fi"

	ROM_REGION( 0x2000, "charom", 0 )
	ROM_LOAD( "325181-01.bin", 0x0000, 0x2000, CRC(7a70d9b8) SHA1(aca3f7321ee7e6152f1f0afad646ae41964de4fb) ) // "C128 Char Sw/Fi"

	ROM_REGION( 0xc88, MOS8721_TAG, 0 )
	// converted from http://www.zimmers.net/anonftp/pub/cbm/firmware/computers/c128/8721-reduced.zip/8721-reduced.txt
	ROM_LOAD( "8721r3.u11", 0x000, 0xc88, BAD_DUMP CRC(154db186) SHA1(ccadcdb1db3b62c51dc4ce60fe6f96831586d297) )
ROM_END


//-------------------------------------------------
//  ROM( c128cr )
//-------------------------------------------------

ROM_START( c128cr )
	/* C128CR prototype, owned by Bo Zimmers
	PCB markings: "COMMODORE 128CR REV.3 // PCB NO.252270" and "PCB ASSY NO.250783"
	Sticker on rom cart shield: "C128CR  No.2 // ENG. SAMPLE // Jun/9/'86   KNT"
	3 ROMs (combined basic, combined c64/kernal, plain character rom)
	6526A-1 CIAs
	?prototype? 2568R1X VDC w/ 1186 datecode
	*/
	ROM_REGION( 0x10000, M8502_TAG, 0 )
	ROM_LOAD( "252343-03.u34", 0x4000, 0x8000, CRC(bc07ed87) SHA1(0eec437994a3f2212343a712847213a8a39f4a7b) ) // "252343-03 // U34"
	ROM_LOAD( "252343-04.u32", 0x0000, 0x4000, CRC(cc6bdb69) SHA1(36286b2e8bea79f7767639fd85e12c5447c7041b) ) // "252343-04 // US // U32"
	ROM_CONTINUE(              0xc000, 0x4000 )

	ROM_REGION( 0x2000, "charom", 0 )
	ROM_LOAD( "390059-01.u18", 0x0000, 0x2000, CRC(6aaaafe6) SHA1(29ed066d513f2d5c09ff26d9166ba23c2afb2b3f) ) // "MOS // (C)1985 CBM // 390059-01 // M468613 8547H"

	ROM_REGION( 0xc88, MOS8721_TAG, 0 )
	// converted from http://www.zimmers.net/anonftp/pub/cbm/firmware/computers/c128/8721-reduced.zip/8721-reduced.txt
	ROM_LOAD( "8721r3.u11", 0x000, 0xc88, BAD_DUMP CRC(154db186) SHA1(ccadcdb1db3b62c51dc4ce60fe6f96831586d297) )
ROM_END


//-------------------------------------------------
//  ROM( c128dcr )
//-------------------------------------------------

ROM_START( c128dcr )
	ROM_REGION( 0x10000, M8502_TAG, 0 )
	ROM_LOAD( "318022-02.u34", 0x4000, 0x8000, CRC(af1ae1e8) SHA1(953dcdf5784a6b39ef84dd6fd968c7a03d8d6816) )
	ROM_LOAD( "318023-02.u32", 0x0000, 0x4000, CRC(eedc120a) SHA1(f98c5a986b532c78bb68df9ec6dbcf876913b99f) )
	ROM_CONTINUE(              0xc000, 0x4000 )

	ROM_REGION( 0x2000, "charom", 0 )
	ROM_LOAD( "390059-01.u18", 0x0000, 0x2000, CRC(6aaaafe6) SHA1(29ed066d513f2d5c09ff26d9166ba23c2afb2b3f) )

	ROM_REGION( 0xc88, MOS8721_TAG, 0 )
	// converted from http://www.zimmers.net/anonftp/pub/cbm/firmware/computers/c128/8721-reduced.zip/8721-reduced.txt
	ROM_LOAD( "8721r3.u11", 0x000, 0xc88, BAD_DUMP CRC(154db186) SHA1(ccadcdb1db3b62c51dc4ce60fe6f96831586d297) )
ROM_END

#define rom_c128dcrp    rom_c128dcr


//-------------------------------------------------
//  ROM( c128dcr_de )
//-------------------------------------------------

ROM_START( c128dcr_de )
	ROM_REGION( 0x10000, M8502_TAG, 0 )
	ROM_LOAD( "318022-02.u34", 0x4000, 0x8000, CRC(af1ae1e8) SHA1(953dcdf5784a6b39ef84dd6fd968c7a03d8d6816) )
	ROM_LOAD( "318077-01.u32", 0x0000, 0x4000, CRC(eb6e2c8f) SHA1(6b3d891fedabb5335f388a5d2a71378472ea60f4) )
	ROM_CONTINUE(              0xc000, 0x4000 )

	ROM_REGION( 0x2000, "charom", 0 )
	ROM_LOAD( "315079-01.u18", 0x0000, 0x2000, CRC(fe5a2db1) SHA1(638f8aff51c2ac4f99a55b12c4f8c985ef4bebd3) )

	ROM_REGION( 0xc88, MOS8721_TAG, 0 )
	// converted from http://www.zimmers.net/anonftp/pub/cbm/firmware/computers/c128/8721-reduced.zip/8721-reduced.txt
	ROM_LOAD( "8721r3.u11", 0x000, 0xc88, BAD_DUMP CRC(154db186) SHA1(ccadcdb1db3b62c51dc4ce60fe6f96831586d297) )
ROM_END


//-------------------------------------------------
//  ROM( c128dcr_se )
//-------------------------------------------------

ROM_START( c128dcr_se )
	ROM_REGION( 0x10000, M8502_TAG, 0 )
	ROM_LOAD( "318022-02.u34", 0x4000, 0x8000, CRC(af1ae1e8) SHA1(953dcdf5784a6b39ef84dd6fd968c7a03d8d6816) )
	ROM_LOAD( "318034-01.u32", 0x0000, 0x4000, CRC(cb4e1719) SHA1(9b0a0cef56d00035c611e07170f051ee5e63aa3a) )
	ROM_CONTINUE(              0xc000, 0x4000 )

	ROM_REGION( 0x2000, "charom", 0 )
	ROM_LOAD( "325181-01.u18", 0x0000, 0x2000, CRC(7a70d9b8) SHA1(aca3f7321ee7e6152f1f0afad646ae41964de4fb) )

	ROM_REGION( 0xc88, MOS8721_TAG, 0 )
	// converted from http://www.zimmers.net/anonftp/pub/cbm/firmware/computers/c128/8721-reduced.zip/8721-reduced.txt
	ROM_LOAD( "8721r3.u11", 0x000, 0xc88, BAD_DUMP CRC(154db186) SHA1(ccadcdb1db3b62c51dc4ce60fe6f96831586d297) )
ROM_END

} // anonymous namespace



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME        PARENT  COMPAT  MACHINE   INPUT    CLASS       INIT        COMPANY                        FULLNAME                               FLAGS
COMP( 1985, c128,       0,      0,      c128,     c128,    c128_state, empty_init, "Commodore Business Machines", "Commodore 128 (NTSC)",                MACHINE_SUPPORTS_SAVE )
COMP( 1985, c128p,      0,      0,      c128pal,  c128,    c128_state, empty_init, "Commodore Business Machines", "Commodore 128 (PAL)",                 MACHINE_SUPPORTS_SAVE )
COMP( 1985, c128_de,    c128,   0,      c128pal,  c128_de, c128_state, empty_init, "Commodore Business Machines", "Commodore 128 (Germany)",             MACHINE_SUPPORTS_SAVE )
//COMP( 1985, c128_fr,    c128,   0,      c128pal,  c128_fr, c128_state, empty_init, "Commodore Business Machines", "Commodore 128 (France)", MACHINE_SUPPORTS_SAVE )
//COMP( 1985, c128_no,    c128,   0,      c128pal,  c128_it, c128_state, empty_init, "Commodore Business Machines", "Commodore 128 (Norway)", MACHINE_SUPPORTS_SAVE )
COMP( 1985, c128_se,    c128,   0,      c128pal,  c128_se, c128_state, empty_init, "Commodore Business Machines", "Commodore 128 (Sweden/Finland)",      MACHINE_SUPPORTS_SAVE )
COMP( 1986, c128d,      c128,   0,      c128,     c128,    c128_state, empty_init, "Commodore Business Machines", "Commodore 128D (NTSC, prototype)",    MACHINE_SUPPORTS_SAVE )
COMP( 1986, c128dp,     c128,   0,      c128pal,  c128,    c128_state, empty_init, "Commodore Business Machines", "Commodore 128D (PAL)",                MACHINE_SUPPORTS_SAVE )

COMP( 1986, c128cr,     c128,   0,      c128,     c128,    c128_state, empty_init, "Commodore Business Machines", "Commodore 128CR (NTSC, prototype)",   MACHINE_SUPPORTS_SAVE )

COMP( 1987, c128dcr,    c128,   0,      c128dcr,  c128,    c128_state, empty_init, "Commodore Business Machines", "Commodore 128DCR (NTSC)",             MACHINE_SUPPORTS_SAVE )
COMP( 1987, c128dcrp,   c128,   0,      c128dcrp, c128,    c128_state, empty_init, "Commodore Business Machines", "Commodore 128DCR (PAL)",              MACHINE_SUPPORTS_SAVE )
COMP( 1987, c128dcr_de, c128,   0,      c128dcrp, c128_de, c128_state, empty_init, "Commodore Business Machines", "Commodore 128DCR (Germany)",          MACHINE_SUPPORTS_SAVE )
//COMP( 1986, c128dcr_it, c128,   0,      c128dcrp, c128_it, c128_state, empty_init, "Commodore Business Machines", "Commodore 128DCR (Italy)", MACHINE_SUPPORTS_SAVE )
COMP( 1987, c128dcr_se, c128,   0,      c128dcrp, c128_se, c128_state, empty_init, "Commodore Business Machines", "Commodore 128DCR (Sweden/Finland)",   MACHINE_SUPPORTS_SAVE )

COMP( 1986, c128d81,    c128,   0,      c128d81,  c128,    c128_state, empty_init, "Commodore Business Machines", "Commodore 128D/81 (NTSC, prototype)", MACHINE_SUPPORTS_SAVE )



c2color.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/******************************************************************************

    basic information
    https://gbatemp.net/threads/the-c2-color-game-console-an-obscure-chinese-handheld.509320/

    "The C2 is a glorious console with a D-Pad, Local 2.4GHz WiFi, Cartridge slot, A, B, and C buttons,
     and has micro usb power! Don't be fooled though, there is no lithium battery, so you have to put in
     3 AA batteries if you don't want to play with it tethered to a charger.

     It comes with a built in game based on the roco kingdom characters.

     In addition, there is a slot on the side of the console allowing cards to be swiped through. Those
     cards can add characters to the game. The console scans the barcode and a new character or item appears in the game for you to use.

     The C2 comes with 9 holographic game cards that will melt your eyes."

    also includes a link to the following video
    https://www.youtube.com/watch?v=D3XO4aTZEko

    TODO:
    identify CPU type - It's an i8051 derived CPU, and seems to be "Mars Semiconductor Corp" related, there is a MARS-PCCAM string
                        amongst other things, this is a known USB identifier for the "Discovery Kids Digital Camera"
                        Possibly a MR97327B, which is listed as RISC-51 in places, but little information can be found in English


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

#include "emu.h"

#include "cpu/mcs51/mcs51.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "screen.h"
#include "emupal.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class c2_color_state : public driver_device
{
public:
	c2_color_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cart(*this, "cartslot")
		, m_cart_region(nullptr)
		, m_palette(*this, "palette")
		, m_screen(*this, "screen")
	{ }

	void c2_color(machine_config &config);

private:
	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);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	u8 cart_r(offs_t offset);

	void prog_map(address_map &map) ATTR_COLD;
	void ext_map(address_map &map) ATTR_COLD;

	required_device<mcs51_cpu_device> m_maincpu;
	required_device<generic_slot_device> m_cart;
	memory_region *m_cart_region;
	required_device<palette_device> m_palette;
	required_device<screen_device> m_screen;
};

uint32_t c2_color_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}




void c2_color_state::machine_start()
{
	// if there's a cart, override the standard mapping
	if (m_cart && m_cart->exists())
	{
		std::string region_tag;
		m_cart_region = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());
	}
}

void c2_color_state::machine_reset()
{
}

DEVICE_IMAGE_LOAD_MEMBER(c2_color_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

u8 c2_color_state::cart_r(offs_t offset)
{
	// skip past 32-byte header
	return m_cart->read_rom(offset + 32);
}

void c2_color_state::prog_map(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(c2_color_state::cart_r));
}

void c2_color_state::ext_map(address_map &map)
{
	map(0x2400, 0x2400).nopr();
}

static INPUT_PORTS_START( c2_color )
INPUT_PORTS_END

void c2_color_state::c2_color(machine_config &config)
{
	I8032(config, m_maincpu, 12'000'000); // exact type and clock unknown
	m_maincpu->set_addrmap(AS_PROGRAM, &c2_color_state::prog_map);
	m_maincpu->set_addrmap(AS_IO, &c2_color_state::ext_map);

	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, 0*8, 30*8-1);
	m_screen->set_screen_update(FUNC(c2_color_state::screen_update));
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette).set_format(palette_device::xBGR_555, 0x200);

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "c2color_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(c2_color_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("c2color_cart");
}

ROM_START( c2color )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "bootloader", 0x0000, 0x4000, NO_DUMP )

	// As with the cartridges, each of these has a 0x20 byte header before the i8051
	// code starts.  This suggests it is unlikely the game runs directly from the SPI
	// ROM and more likely a bootloader copies the code into RAM.
	// The Mainboard has a 2MByte DRAM on it

	// This, the larger of the 2 ROMs contains unique code, it appears to be the base
	// game, and system functions.  It also has some 16-bit signed PCM samples.
	ROM_REGION( 0x800000, "spi1", ROMREGION_ERASEFF )
	ROM_LOAD( "spi.u7", 0x000000, 0x800000, CRC(6a4d2cd2) SHA1(46e109bbd5db206911716919ad13efc080cbdf34) )

	// The smaller ROM is much more similar to the cartridges (actually identical up
	// until the first MRDB resource block at 0x26000 aside from a few bytes in the
	// 0x20 header, and the game number at the 0x20000 mark being 0)
	//
	// This ROM also has a 2nd MRDB resource block, whereas the cartridges only have
	// a single block
	//
	// The code still contains a lot of generic 'firmware' like functions, but it is
	// unclear if any of the code is used, or if these ROMs are used more like skins
	// for the base game, accessing the resource table only
	//
	// A large number of graphics, which are assumed to be JPEG compressed are indexed
	// by the MRDB tables, no sound effects or non-graphical resources have been
	// identified

	ROM_REGION( 0x400000, "spi2", ROMREGION_ERASEFF )
	ROM_LOAD( "spi.u16", 0x000000, 0x400000, CRC(9101b02a) SHA1(8c31e7641f4667bd8d5d7cc991cd5976828a0628) )
ROM_END

} // anonymous namespace


//    year, name,         parent,  compat, machine,      input,        class,              init,       company,  fullname,                             flags
CONS( 201?, c2color,      0,       0,      c2_color,   c2_color, c2_color_state, empty_init, "Baiyi Animation", "C2 Color (China)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



c64.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

    TODO:

    - floating bus writes to peripheral registers in m6502.c
    - sort out kernals between PAL/NTSC
    - tsuit215 test failures
        - IRQ (WRONG $DC0D)
        - NMI (WRONG $DD0D)
        - some CIA tests
    - PDC Clipper (C64 in a briefcase with 3" floppy, electroluminescent flat screen, thermal printer)

*/

#include "emu.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "bus/cbmiec/cbmiec.h"
#include "bus/cbmiec/c1541.h"
#include "bus/c64/exp.h"
#include "bus/vic20/user.h"
#include "bus/pet/cass.h"
#include "bus/vcs_ctrl/ctrl.h"
#include "cpu/m6502/m6510.h"
#include "imagedev/snapquik.h"
#include "cbm_snqk.h"
#include "machine/input_merger.h"
#include "machine/mos6526.h"
#include "machine/pla.h"
#include "machine/ram.h"
#include "sound/mos6581.h"
#include "video/mos6566.h"


namespace {

#define MOS6567_TAG     "u19"
#define MOS6569_TAG     "u19"
#define MOS6581_TAG     "u18"
#define MOS6526_1_TAG   "u1"
#define MOS6526_2_TAG   "u2"
#define PLA_TAG         "u17"
#define SCREEN_TAG      "screen"
#define CONTROL1_TAG    "joy1"
#define CONTROL2_TAG    "joy2"
#define PET_USER_PORT_TAG     "user"

class c64_state : public driver_device
{
public:
	c64_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "u7"),
		m_nmi(*this, "nmi"),
		m_pla(*this, PLA_TAG),
		m_vic(*this, MOS6569_TAG),
		m_sid(*this, MOS6581_TAG),
		m_cia1(*this, MOS6526_1_TAG),
		m_cia2(*this, MOS6526_2_TAG),
		m_iec(*this, CBM_IEC_TAG),
		m_joy1(*this, CONTROL1_TAG),
		m_joy2(*this, CONTROL2_TAG),
		m_exp(*this, "exp"),
		m_user(*this, PET_USER_PORT_TAG),
		m_ram(*this, RAM_TAG),
		m_cassette(*this, PET_DATASSETTE_PORT_TAG),
		m_color_ram(*this, "color_ram", 0x400, ENDIANNESS_LITTLE),
		m_row(*this, "ROW%u", 0),
		m_lock(*this, "LOCK"),
		m_loram(1),
		m_hiram(1),
		m_charen(1),
		m_va14(1),
		m_va15(1),
		m_cass_rd(1),
		m_iec_srq(1)
	{ }

	// ROM
	uint8_t *m_basic;
	uint8_t *m_kernal;
	uint8_t *m_charom;

	required_device<m6510_device> m_maincpu;
	required_device<input_merger_device> m_nmi;
	required_device<pla_device> m_pla;
	required_device<mos6566_device> m_vic;
	required_device<mos6581_device> m_sid;
	required_device<mos6526_device> m_cia1;
	required_device<mos6526_device> m_cia2;
	optional_device<cbm_iec_device> m_iec;
	required_device<vcs_control_port_device> m_joy1;
	required_device<vcs_control_port_device> m_joy2;
	required_device<c64_expansion_slot_device> m_exp;
	required_device<pet_user_port_device> m_user;
	required_device<ram_device> m_ram;
	optional_device<pet_datassette_port_device> m_cassette;
	memory_share_creator<uint8_t> m_color_ram;
	optional_ioport_array<8> m_row;
	optional_ioport m_lock;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	[[maybe_unused]] void check_interrupts();
	int read_pla(offs_t offset, offs_t va, int rw, int aec, int ba);
	uint8_t read_memory(offs_t offset, offs_t va, int aec, int ba);
	void write_memory(offs_t offset, uint8_t data, int aec, int ba);

	uint8_t read(offs_t offset);
	void write(offs_t offset, uint8_t data);

	uint8_t vic_videoram_r(offs_t offset);
	uint8_t vic_colorram_r(offs_t offset);

	uint8_t sid_potx_r();
	uint8_t sid_poty_r();

	uint8_t cia1_pa_r();
	void cia1_pa_w(uint8_t data);
	uint8_t cia1_pb_r();
	void cia1_pb_w(uint8_t data);

	uint8_t cia2_pa_r();
	void cia2_pa_w(uint8_t data);

	uint8_t cpu_r();
	void cpu_w(uint8_t data);

	void write_restore(int state);
	void exp_dma_w(int state);
	void exp_reset_w(int state);

	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_c64);

	uint8_t cia2_pb_r();
	void cia2_pb_w(uint8_t data);

	void write_user_pa2(int state) { m_user_pa2 = state; }
	void write_user_pb0(int state) { if (state) m_user_pb |= 1; else m_user_pb &= ~1; }
	void write_user_pb1(int state) { if (state) m_user_pb |= 2; else m_user_pb &= ~2; }
	void write_user_pb2(int state) { if (state) m_user_pb |= 4; else m_user_pb &= ~4; }
	void write_user_pb3(int state) { if (state) m_user_pb |= 8; else m_user_pb &= ~8; }
	void write_user_pb4(int state) { if (state) m_user_pb |= 16; else m_user_pb &= ~16; }
	void write_user_pb5(int state) { if (state) m_user_pb |= 32; else m_user_pb &= ~32; }
	void write_user_pb6(int state) { if (state) m_user_pb |= 64; else m_user_pb &= ~64; }
	void write_user_pb7(int state) { if (state) m_user_pb |= 128; else m_user_pb &= ~128; }

	void update_cia1_flag() { m_cia1->flag_w(m_cass_rd & m_iec_srq); }
	void cass_rd_w(int state) { m_cass_rd = state; update_cia1_flag(); }
	void iec_srq_w(int state) { m_iec_srq = state; update_cia1_flag(); }

	// memory state
	int m_loram;
	int m_hiram;
	int m_charen;

	// video state
	int m_va14;
	int m_va15;

	// interrupt state
	int m_exp_dma;
	int m_cass_rd;
	int m_iec_srq;

	int m_user_pa2;
	int m_user_pb;

	void pal(machine_config &config);
	void ntsc(machine_config &config);
	void pet64(machine_config &config);
	void c64_mem(address_map &map) ATTR_COLD;
	void vic_colorram_map(address_map &map) ATTR_COLD;
	void vic_videoram_map(address_map &map) ATTR_COLD;

	offs_t dasm_zeropage(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const char *opname);
	offs_t dasm_vector(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const char *opname);
	offs_t dasm_zeropage_vector(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const char *opname);
	offs_t dasm_override(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const util::disasm_interface::data_buffer &params);
};


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

	uint8_t cpu_r();
	void cpu_w(uint8_t data);
	void ntsc_sx(machine_config &config);
	void ntsc_dx(machine_config &config);
	void pal_sx(machine_config &config);
};


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

	void pal_c(machine_config &config);
	void ntsc_c(machine_config &config);
};


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

	uint8_t cpu_r();
	void cpu_w(uint8_t data);

	uint8_t cia1_pa_r();
	uint8_t cia1_pb_r();
	void pal_gs(machine_config &config);
};


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

	void clipper(machine_config &config);
};


struct dasm_zeropage_data
{
	u8 addr;
	const char *name;
};

struct dasm_vector_data
{
	u16 addr;
	const char *name;
};

static const struct dasm_zeropage_data c64_zeropage_locations[] =
{
	{ 0x00, "D6510" }, { 0x01, "R6510" }, { 0x03, "ADRAY1" }, { 0x05, "ADRAY2" }, { 0x07, "CHARAC" }, { 0x08, "ENDCHR" }, { 0x09, "TRMPOS" }, { 0x0a, "VERCK" },
	{ 0x0b, "COUNT" }, { 0x0c, "DIMFLG" }, { 0x0d, "VALTYP" }, { 0x0e, "INTFLG" }, { 0x0f, "GARBFL" }, { 0x10, "SUBFLG" }, { 0x11, "INPFLG" }, { 0x12, "TANSGN" },
	{ 0x14, "LINNUM" }, { 0x16, "TEMPPT" }, { 0x17, "LASTPT" }, { 0x19, "TEMPST" }, { 0x22, "INDEX" }, { 0x26, "RESHO" }, { 0x2b, "TXTTAB" }, { 0x2d, "VARTAB" },
	{ 0x2f, "ARYTAB" }, { 0x31, "STREND" }, { 0x33, "FRETOP" }, { 0x35, "FRESPC" }, { 0x37, "MEMSIZ" }, { 0x39, "CURLIN" }, { 0x3b, "OLDLIN" }, { 0x3d, "OLDTXT" },
	{ 0x3f, "DATLIN" }, { 0x41, "DATPTR" }, { 0x43, "INPPTR" }, { 0x45, "VARNAM" }, { 0x47, "VARPNT" }, { 0x49, "FORPNT" }, { 0x61, "FACEXP" }, { 0x62, "FACHO" },
	{ 0x66, "FACSGN" }, { 0x67, "SGNFLG" }, { 0x68, "BITS" }, { 0x69, "ARGEXP" }, { 0x6a, "ARGHO" }, { 0x6e, "ARGSGN" }, { 0x6f, "ARISGN" }, { 0x70, "FACOV" },
	{ 0x71, "FBUFPT" }, { 0x73, "CHRGET" }, { 0x79, "CHRGOT" }, { 0x7a, "TXTPTR" }, { 0x8b, "RNDX" }, { 0x90, "STATUS" }, { 0x91, "STKEY" }, { 0x92, "SVXT" },
	{ 0x93, "VERCK" }, { 0x94, "C3PO" }, { 0x95, "BSOUR" }, { 0x96, "SYNO" }, { 0x98, "LDTND" }, { 0x99, "DFLTN" }, { 0x9a, "DFLTO" }, { 0x9b, "PRTY" },
	{ 0x9c, "DPSW" }, { 0x9d, "MSGFLG" }, { 0x9e, "PTR1" }, { 0x9f, "PTR2" }, { 0xa0, "TIME" }, { 0xa5, "CNTDN" }, { 0xa6, "BUFPNT" }, { 0xa7, "INBIT" },
	{ 0xa8, "BITCI" }, { 0xa9, "RINONE" }, { 0xaa, "RIDATA" }, { 0xab, "RIPRTY" }, { 0xac, "SAL" }, { 0xae, "EAL" }, { 0xb0, "CMPO" }, { 0xb2, "TAPE1" },
	{ 0xb4, "BITTS" }, { 0xb5, "NXTBIT" }, { 0xb6, "RODATA" }, { 0xb7, "FNLEN" }, { 0xb8, "LA" }, { 0xb9, "SA" }, { 0xba, "FA" }, { 0xbb, "FNADR" },
	{ 0xbc, "ROPRTY" }, { 0xbe, "FSBLK" }, { 0xbf, "MYCH" }, { 0xc0, "CAS1" }, { 0xc1, "STAL" }, { 0xc3, "MEMUSS" }, { 0xc5, "LSTX" }, { 0xc6, "NDX" },
	{ 0xc7, "RVS" }, { 0xc8, "INDX" }, { 0xc9, "LXSP" }, { 0xcb, "SFDX" }, { 0xcc, "BLNSW" }, { 0xcd, "BLNCT" }, { 0xce, "GDBLN" }, { 0xcf, "BLNON" },
	{ 0xd0, "CRSW" }, { 0xd1, "PNT" }, { 0xd3, "PNTR" }, { 0xd4, "QTSW" }, { 0xd5, "LNMX" }, { 0xd6, "TBLX" }, { 0xd8, "INSRT" }, { 0xd9, "LDTB1" },
	{ 0xf3, "USER" }, { 0xf5, "KEYTAB" }, { 0xf7, "RIBUF" }, { 0xf9, "ROBUF" }, { 0xfb, "FREKZB" }, { 0xff, "BASZPT" }
};

static const struct dasm_vector_data cbm_kernal_vectors[] =
{
	{ 0xff81, "CINT" }, { 0xff84, "IOINT" }, { 0xff87, "RAMTAS" }, { 0xff8a, "RESTOR" }, { 0xff8d, "VECTOR" }, { 0xff90, "SETMSG" }, { 0xff93, "SECOND" }, { 0xff96, "TKSA" },
	{ 0xff99, "MEMTOP" }, { 0xff9c, "MEMBOT" }, { 0xff9f, "SCNKEY" }, { 0xffa2, "SETTMO" }, { 0xffa5, "ACPTR" }, { 0xffa8, "CIOUT" }, { 0xffab, "UNTALK" }, { 0xffae, "UNLSN" },
	{ 0xffb1, "LISTEN" }, { 0xffb4, "TALK" }, { 0xffb7, "READST" }, { 0xffba, "SETLFS" }, { 0xffbd, "SETNAM" }, { 0xffc3, "CLOSE" }, { 0xffc6, "CHKIN" }, { 0xffc9, "CHKOUT" },
	{ 0xffcc, "CLRCHN" }, { 0xffcf, "CHRIN" }, { 0xffd2, "CHROUT" }, { 0xffd5, "LOAD" }, { 0xffd8, "SAVE" }, { 0xffdb, "SETTIM" }, { 0xffde, "RDTIM" }, { 0xffe1, "STOP" },
	{ 0xffe4, "GETIN" }, { 0xffe7, "CLALL" }, { 0xffea, "UDTIM" }, { 0xffed, "SCREEN" }, { 0xfff0, "PLOT" }, { 0xfff3, "IOBASE" }
};

offs_t c64_state::dasm_override(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const util::disasm_interface::data_buffer &params)
{
	switch (opcodes.r8(pc))
	{
	case 0x20: return dasm_vector(stream, pc, opcodes, "jsr %s");
	case 0x4c: return dasm_vector(stream, pc, opcodes, "jmp %s");
	case 0x6c: return dasm_zeropage_vector(stream, pc, opcodes, "jmp (%s)");
	case 0x65: return dasm_zeropage(stream, pc, opcodes, "adc %s");
	case 0x75: return dasm_zeropage(stream, pc, opcodes, "adc %s, x");
	case 0x61: return dasm_zeropage(stream, pc, opcodes, "adc (%s, x)");
	case 0x71: return dasm_zeropage(stream, pc, opcodes, "adc (%s), y");
	case 0x25: return dasm_zeropage(stream, pc, opcodes, "and %s");
	case 0x35: return dasm_zeropage(stream, pc, opcodes, "and %s, x");
	case 0x21: return dasm_zeropage(stream, pc, opcodes, "and (%s, x)");
	case 0x31: return dasm_zeropage(stream, pc, opcodes, "and (%s), y");
	case 0x06: return dasm_zeropage(stream, pc, opcodes, "asl %s");
	case 0x16: return dasm_zeropage(stream, pc, opcodes, "asl %s, x");
	case 0x24: return dasm_zeropage(stream, pc, opcodes, "bit %s");
	case 0xc5: return dasm_zeropage(stream, pc, opcodes, "cmp %s");
	case 0xd5: return dasm_zeropage(stream, pc, opcodes, "cmp %s, x");
	case 0xc1: return dasm_zeropage(stream, pc, opcodes, "cmp (%s, x)");
	case 0xd1: return dasm_zeropage(stream, pc, opcodes, "cmp (%s), y");
	case 0xe4: return dasm_zeropage(stream, pc, opcodes, "cpx %s");
	case 0xc4: return dasm_zeropage(stream, pc, opcodes, "cpy %s");
	case 0xc6: return dasm_zeropage(stream, pc, opcodes, "dec %s");
	case 0xd6: return dasm_zeropage(stream, pc, opcodes, "dec %s, x");
	case 0x45: return dasm_zeropage(stream, pc, opcodes, "eor %s");
	case 0x55: return dasm_zeropage(stream, pc, opcodes, "eor %s, x");
	case 0x41: return dasm_zeropage(stream, pc, opcodes, "eor (%s, x)");
	case 0x51: return dasm_zeropage(stream, pc, opcodes, "eor (%s), y");
	case 0xe6: return dasm_zeropage(stream, pc, opcodes, "inc %s");
	case 0xf6: return dasm_zeropage(stream, pc, opcodes, "inc %s, x");
	case 0xa5: return dasm_zeropage(stream, pc, opcodes, "lda %s");
	case 0xb5: return dasm_zeropage(stream, pc, opcodes, "lda %s, x");
	case 0xa1: return dasm_zeropage(stream, pc, opcodes, "lda (%s, x)");
	case 0xb1: return dasm_zeropage(stream, pc, opcodes, "lda (%s), y");
	case 0xa6: return dasm_zeropage(stream, pc, opcodes, "ldx %s");
	case 0xb6: return dasm_zeropage(stream, pc, opcodes, "ldx %s, y");
	case 0xa4: return dasm_zeropage(stream, pc, opcodes, "ldy %s");
	case 0xb4: return dasm_zeropage(stream, pc, opcodes, "ldy %s, x");
	case 0x46: return dasm_zeropage(stream, pc, opcodes, "lsr %s");
	case 0x56: return dasm_zeropage(stream, pc, opcodes, "lsr %s, x");
	case 0x05: return dasm_zeropage(stream, pc, opcodes, "ora %s");
	case 0x15: return dasm_zeropage(stream, pc, opcodes, "ora %s, x");
	case 0x01: return dasm_zeropage(stream, pc, opcodes, "ora (%s, x)");
	case 0x11: return dasm_zeropage(stream, pc, opcodes, "ora (%s), y");
	case 0x26: return dasm_zeropage(stream, pc, opcodes, "rol %s");
	case 0x36: return dasm_zeropage(stream, pc, opcodes, "rol %s, x");
	case 0x66: return dasm_zeropage(stream, pc, opcodes, "ror %s");
	case 0x76: return dasm_zeropage(stream, pc, opcodes, "ror %s, x");
	case 0xe5: return dasm_zeropage(stream, pc, opcodes, "sbc %s");
	case 0xf5: return dasm_zeropage(stream, pc, opcodes, "sbc %s, x");
	case 0xe1: return dasm_zeropage(stream, pc, opcodes, "sbc (%s, x)");
	case 0xf1: return dasm_zeropage(stream, pc, opcodes, "sbc (%s), y");
	case 0x85: return dasm_zeropage(stream, pc, opcodes, "sta %s");
	case 0x95: return dasm_zeropage(stream, pc, opcodes, "sta %s, x");
	case 0x81: return dasm_zeropage(stream, pc, opcodes, "sta (%s, x)");
	case 0x91: return dasm_zeropage(stream, pc, opcodes, "sta (%s), y");
	case 0x86: return dasm_zeropage(stream, pc, opcodes, "stx %s");
	case 0x96: return dasm_zeropage(stream, pc, opcodes, "stx (%s), y");
	case 0x84: return dasm_zeropage(stream, pc, opcodes, "sty %s");
	case 0x94: return dasm_zeropage(stream, pc, opcodes, "sty %s, x");
	}

	return 0;
}

offs_t c64_state::dasm_zeropage(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const char *opname)
{
	int item = 0;
	u8 operand = opcodes.r8(pc+1);

	while (c64_zeropage_locations[item].addr != 0xff)
	{
		if (c64_zeropage_locations[item].addr == operand)
		{
			std::ostringstream buffer;
			util::stream_format(buffer, opname, c64_zeropage_locations[item].name);
			stream << buffer.str();
			return 2 | util::disasm_interface::SUPPORTED;
		}
		item++;
	}

	return 0;
}

offs_t c64_state::dasm_vector(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const char *opname)
{
	int item = 0;
	u16 operand = opcodes.r16(pc+1);

	while (cbm_kernal_vectors[item].addr != 0xfff3)
	{
		if (cbm_kernal_vectors[item].addr == operand)
		{
			std::ostringstream buffer;
			util::stream_format(buffer, opname, cbm_kernal_vectors[item].name);
			stream << buffer.str();
			return 3 | util::disasm_interface::SUPPORTED;
		}
		item++;
	}

	return 0;
}

offs_t c64_state::dasm_zeropage_vector(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const char *opname)
{
	int item = 0;
	u16 operand = opcodes.r16(pc+1);

	if (operand < 0x100)
	{
		while (c64_zeropage_locations[item].addr != 0xff)
		{
			if (c64_zeropage_locations[item].addr == operand)
			{
				std::ostringstream buffer;
				util::stream_format(buffer, opname, c64_zeropage_locations[item].name);
				stream << buffer.str();
				return 3 | util::disasm_interface::SUPPORTED;
			}
			item++;
		}
	}

	return 0;
}


//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

#define A15 BIT(offset, 15)
#define A14 BIT(offset, 14)
#define A13 BIT(offset, 13)
#define A12 BIT(offset, 12)
#define VA13 BIT(va, 13)
#define VA12 BIT(va, 12)

enum
{
	PLA_OUT_CASRAM = 0,
	PLA_OUT_BASIC  = 1,
	PLA_OUT_KERNAL = 2,
	PLA_OUT_CHAROM = 3,
	PLA_OUT_GRW    = 4,
	PLA_OUT_IO     = 5,
	PLA_OUT_ROML   = 6,
	PLA_OUT_ROMH   = 7
};


QUICKLOAD_LOAD_MEMBER(c64_state::quickload_c64)
{
	return general_cbm_loadsnap(image, m_maincpu->space(AS_PROGRAM), 0, cbm_quick_sethiaddress);
}


//**************************************************************************
//  INTERRUPTS
//**************************************************************************

//-------------------------------------------------
//  check_interrupts -
//-------------------------------------------------

void c64_state::check_interrupts()
{
	//int irq = m_cia1_irq || m_vic_irq || m_exp_irq;
	//int nmi = m_cia2_irq || !m_restore || m_exp_nmi;
	//int rdy = m_exp_dma && m_vic_ba;
}



//**************************************************************************
//  ADDRESS DECODING
//**************************************************************************

//-------------------------------------------------
//  read_pla -
//-------------------------------------------------

int c64_state::read_pla(offs_t offset, offs_t va, int rw, int aec, int ba)
{
	//int ba = m_vic->ba_r();
	//int aec = !m_vic->aec_r();
	int sphi2 = m_vic->phi0_r();
	int game = m_exp->game_r(offset, sphi2, ba, rw, m_loram, m_hiram);
	int exrom = m_exp->exrom_r(offset, sphi2, ba, rw, m_loram, m_hiram);
	int cas = 0;

	uint32_t input = VA12 << 15 | VA13 << 14 | game << 13 | exrom << 12 | rw << 11 | aec << 10 | ba << 9 | A12 << 8 |
		A13 << 7 | A14 << 6 | A15 << 5 | m_va14 << 4 | m_charen << 3 | m_hiram << 2 | m_loram << 1 | cas;

	return m_pla->read(input);
}


//-------------------------------------------------
//  read_memory -
//-------------------------------------------------

uint8_t c64_state::read_memory(offs_t offset, offs_t va, int aec, int ba)
{
	int rw = 1;
	int io1 = 1, io2 = 1;
	int sphi2 = m_vic->phi0_r();

	int plaout = read_pla(offset, va, rw, !aec, ba);

	uint8_t data = 0xff;

	if (!aec)
	{
		data = m_vic->bus_r();
	}

	if (!BIT(plaout, PLA_OUT_CASRAM))
	{
		if (aec)
		{
			data = m_ram->pointer()[offset];
		}
		else
		{
			data = m_ram->pointer()[(!m_va15 << 15) | (!m_va14 << 14) | va];
		}
	}
	if (!BIT(plaout, PLA_OUT_BASIC))
	{
		data = m_basic[offset & 0x1fff];
	}
	if (!BIT(plaout, PLA_OUT_KERNAL))
	{
		data = m_kernal[offset & 0x1fff];
	}
	if (!BIT(plaout, PLA_OUT_CHAROM))
	{
		data = m_charom[offset & 0xfff];
	}
	if (!BIT(plaout, PLA_OUT_IO))
	{
		switch ((offset >> 8) & 0x0f)
		{
		case 0:
		case 1:
		case 2:
		case 3: // VIC
			data = m_vic->read(offset & 0x3f);
			break;

		case 4:
		case 5:
		case 6:
		case 7: // SID
			data = m_sid->read(offset & 0x1f);
			break;

		case 0x8:
		case 0x9:
		case 0xa:
		case 0xb: // COLOR
			data = m_color_ram[offset & 0x3ff] & 0x0f;
			break;

		case 0xc: // CIA1
			data = m_cia1->read(offset & 0x0f);
			break;

		case 0xd: // CIA2
			data = m_cia2->read(offset & 0x0f);
			break;

		case 0xe: // I/O1
			io1 = 0;
			break;

		case 0xf: // I/O2
			io2 = 0;
			break;
		}
	}

	int roml = BIT(plaout, PLA_OUT_ROML);
	int romh = BIT(plaout, PLA_OUT_ROMH);
	return m_exp->cd_r(offset, data, sphi2, ba, roml, romh, io1, io2);
}


//-------------------------------------------------
//  write_memory -
//-------------------------------------------------

void c64_state::write_memory(offs_t offset, uint8_t data, int aec, int ba)
{
	int rw = 0;
	offs_t va = 0;
	int io1 = 1, io2 = 1;
	int sphi2 = m_vic->phi0_r();

	int plaout = read_pla(offset, va, rw, !aec, ba);

	if (offset < 0x0002)
	{
		// write to internal CPU register
		data = m_vic->bus_r();
	}

	if (!BIT(plaout, PLA_OUT_CASRAM))
	{
		m_ram->pointer()[offset] = data;
	}
	if (!BIT(plaout, PLA_OUT_IO))
	{
		switch ((offset >> 8) & 0x0f)
		{
		case 0:
		case 1:
		case 2:
		case 3: // VIC
			m_vic->write(offset & 0x3f, data);
			break;

		case 4:
		case 5:
		case 6:
		case 7: // SID
			m_sid->write(offset & 0x1f, data);
			break;

		case 0x8:
		case 0x9:
		case 0xa:
		case 0xb: // COLOR
			if (!BIT(plaout, PLA_OUT_GRW)) m_color_ram[offset & 0x3ff] = data & 0x0f;
			break;

		case 0xc: // CIA1
			m_cia1->write(offset & 0x0f, data);
			break;

		case 0xd: // CIA2
			m_cia2->write(offset & 0x0f, data);
			break;

		case 0xe: // I/O1
			io1 = 0;
			break;

		case 0xf: // I/O2
			io2 = 0;
			break;
		}
	}

	int roml = BIT(plaout, PLA_OUT_ROML);
	int romh = BIT(plaout, PLA_OUT_ROMH);
	m_exp->cd_w(offset, data, sphi2, ba, roml, romh, io1, io2);
}


//-------------------------------------------------
//  read -
//-------------------------------------------------

uint8_t c64_state::read(offs_t offset)
{
	int aec = 1, ba = 1;

	// VIC address bus is floating
	offs_t va = 0x3fff;

	return read_memory(offset, va, aec, ba);
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

void c64_state::write(offs_t offset, uint8_t data)
{
	int aec = 1, ba = 1;

	write_memory(offset, data, aec, ba);
}


//-------------------------------------------------
//  vic_videoram_r -
//-------------------------------------------------

uint8_t c64_state::vic_videoram_r(offs_t offset)
{
	int aec = m_vic->aec_r(), ba = m_vic->ba_r();
	offs_t va = offset;

	// A15/A14 are not connected to VIC so they are floating
	//offset |= 0xc000;

	return read_memory(offset, va, aec, ba);
}


//-------------------------------------------------
//  vic_colorram_r -
//-------------------------------------------------

uint8_t c64_state::vic_colorram_r(offs_t offset)
{
	uint8_t data;

	if (m_vic->aec_r())
	{
		// TODO low nibble of last opcode
		data = 0x0f;
	}
	else
	{
		data = m_color_ram[offset] & 0x0f;
	}

	return data;
}



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

//-------------------------------------------------
//  ADDRESS_MAP( c64_mem )
//-------------------------------------------------

void c64_state::c64_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(c64_state::read), FUNC(c64_state::write));
}


//-------------------------------------------------
//  ADDRESS_MAP( vic_videoram_map )
//-------------------------------------------------

void c64_state::vic_videoram_map(address_map &map)
{
	map(0x0000, 0x3fff).r(FUNC(c64_state::vic_videoram_r));
}


//-------------------------------------------------
//  ADDRESS_MAP( vic_colorram_map )
//-------------------------------------------------

void c64_state::vic_colorram_map(address_map &map)
{
	map(0x000, 0x3ff).r(FUNC(c64_state::vic_colorram_r));
}



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

//-------------------------------------------------
//  INPUT_PORTS( c64 )
//-------------------------------------------------

void c64_state::write_restore(int state)
{
	m_nmi->in_w<1>(!state);
}

static INPUT_PORTS_START( c64 )
	PORT_START( "ROW0" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Crsr Down Up") PORT_CODE(KEYCODE_RALT)        PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3)                                    PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2)                                    PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1)                                    PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F4)                                    PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Crsr Right Left") PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER)             PORT_CHAR(13)
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INST DEL") PORT_CODE(KEYCODE_BACKSPACE)       PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START( "ROW1" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Shift (Left)") PORT_CODE(KEYCODE_LSHIFT)      PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)         PORT_CHAR('E')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)         PORT_CHAR('S')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)         PORT_CHAR('Z')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)         PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)         PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)         PORT_CHAR('W')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)         PORT_CHAR('3') PORT_CHAR('#')

	PORT_START( "ROW2" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)         PORT_CHAR('X')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)         PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)         PORT_CHAR('F')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)         PORT_CHAR('C')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6)         PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)         PORT_CHAR('D')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)         PORT_CHAR('R')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5)         PORT_CHAR('5') PORT_CHAR('%')

	PORT_START( "ROW3" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)         PORT_CHAR('V')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)         PORT_CHAR('U')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)         PORT_CHAR('H')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)         PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8)         PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)         PORT_CHAR('G')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)         PORT_CHAR('Y')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7)         PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START( "ROW4" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)         PORT_CHAR('N')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)         PORT_CHAR('O')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)         PORT_CHAR('K')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)         PORT_CHAR('M')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0)         PORT_CHAR('0')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)         PORT_CHAR('J')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)         PORT_CHAR('I')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9)         PORT_CHAR('9') PORT_CHAR(')')

	PORT_START( "ROW5" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA)     PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)     PORT_CHAR(':') PORT_CHAR('[')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)      PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS)    PORT_CHAR('-')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)         PORT_CHAR('L')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)         PORT_CHAR('P')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)     PORT_CHAR('+')

	PORT_START( "ROW6" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH)                             PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\xE2\x86\x91  Pi") PORT_CODE(KEYCODE_DEL) PORT_CHAR(0x2191) PORT_CHAR(0x03C0)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH)                         PORT_CHAR('=')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Shift (Right)") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CLR HOME") PORT_CODE(KEYCODE_INSERT)      PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE)                             PORT_CHAR(';') PORT_CHAR(']')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE)                        PORT_CHAR('*')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH2)                        PORT_CHAR(0xA3)

	PORT_START( "ROW7" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RUN STOP") PORT_CODE(KEYCODE_HOME)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)                                 PORT_CHAR('Q')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CBM") PORT_CODE(KEYCODE_LALT)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE)                             PORT_CHAR(' ')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2)                                 PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TAB)                               PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\xE2\x86\x90") PORT_CODE(KEYCODE_TILDE)   PORT_CHAR(0x2190)
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1)                                 PORT_CHAR('1') PORT_CHAR('!')

	PORT_START( "RESTORE" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RESTORE") PORT_CODE(KEYCODE_PRTSCR) PORT_WRITE_LINE_MEMBER(FUNC(c64_state::write_restore))

	PORT_START( "LOCK" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( c64sw )
//-------------------------------------------------

static INPUT_PORTS_START( c64sw )
	PORT_INCLUDE( c64 )

	PORT_MODIFY( "ROW5" )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(0x00E5) PORT_CHAR(0x00C5)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)                             PORT_CHAR(';') PORT_CHAR(']')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS)                            PORT_CHAR('=')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)                             PORT_CHAR('-')

	PORT_MODIFY( "ROW6" )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(0x00E4) PORT_CHAR(0x00C4)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE)     PORT_CHAR(0x00F6) PORT_CHAR(0x00D6)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE)                        PORT_CHAR('@')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH2)                        PORT_CHAR(':') PORT_CHAR('*')
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( c64gs )
//-------------------------------------------------

static INPUT_PORTS_START( c64gs )
	// no keyboard
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( clipper )
//-------------------------------------------------

static INPUT_PORTS_START( clipper )
	PORT_INCLUDE( c64 )
	// TODO extra keys
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  MOS6581_INTERFACE( sid_intf )
//-------------------------------------------------

uint8_t c64_state::sid_potx_r()
{
	uint8_t data = 0xff;

	switch (m_cia1->pa_r() >> 6)
	{
	case 1: data = m_joy1->read_pot_x(); break;
	case 2: data = m_joy2->read_pot_x(); break;
	case 3:
		if (m_joy1->has_pot_x() && m_joy2->has_pot_x())
		{
			data = 1 / (1 / m_joy1->read_pot_x() + 1 / m_joy2->read_pot_x());
		}
		else if (m_joy1->has_pot_x())
		{
			data = m_joy1->read_pot_x();
		}
		else if (m_joy2->has_pot_x())
		{
			data = m_joy2->read_pot_x();
		}
		break;
	}

	return data;
}

uint8_t c64_state::sid_poty_r()
{
	uint8_t data = 0xff;

	switch (m_cia1->pa_r() >> 6)
	{
	case 1: data = m_joy1->read_pot_y(); break;
	case 2: data = m_joy2->read_pot_y(); break;
	case 3:
		if (m_joy1->has_pot_y() && m_joy2->has_pot_y())
		{
			data = 1 / (1 / m_joy1->read_pot_y() + 1 / m_joy2->read_pot_y());
		}
		else if (m_joy1->has_pot_y())
		{
			data = m_joy1->read_pot_y();
		}
		else if (m_joy2->has_pot_y())
		{
			data = m_joy2->read_pot_y();
		}
		break;
	}

	return data;
}


//-------------------------------------------------
//  MOS6526_INTERFACE( cia1_intf )
//-------------------------------------------------

uint8_t c64_state::cia1_pa_r()
{
	/*

	    bit     description

	    PA0     COL0, JOY B0
	    PA1     COL1, JOY B1
	    PA2     COL2, JOY B2
	    PA3     COL3, JOY B3
	    PA4     COL4, BTNB
	    PA5     COL5
	    PA6     COL6
	    PA7     COL7

	*/

	uint8_t data = 0xff;

	// joystick
	uint8_t joy_b = m_joy2->read_joy();

	data &= (0xf0 | (joy_b & 0x0f));
	data &= ~(!BIT(joy_b, 5) << 4);

	// keyboard
	uint8_t cia1_pb = m_cia1->pb_r();
	uint32_t row[8] = { m_row[0]->read(), m_row[1]->read() & m_lock->read(), m_row[2]->read(), m_row[3]->read(),
						m_row[4]->read(), m_row[5]->read(), m_row[6]->read(), m_row[7]->read() };

	for (int i = 0; i < 8; i++)
	{
		if (!BIT(cia1_pb, i))
		{
			if (!BIT(row[7], i)) data &= ~0x80;
			if (!BIT(row[6], i)) data &= ~0x40;
			if (!BIT(row[5], i)) data &= ~0x20;
			if (!BIT(row[4], i)) data &= ~0x10;
			if (!BIT(row[3], i)) data &= ~0x08;
			if (!BIT(row[2], i)) data &= ~0x04;
			if (!BIT(row[1], i)) data &= ~0x02;
			if (!BIT(row[0], i)) data &= ~0x01;
		}
	}

	return data;
}

void c64_state::cia1_pa_w(uint8_t data)
{
	/*

	    bit     description

	    PA0     COL0, JOY B0
	    PA1     COL1, JOY B1
	    PA2     COL2, JOY B2
	    PA3     COL3, JOY B3
	    PA4     COL4, BTNB
	    PA5     COL5
	    PA6     COL6
	    PA7     COL7

	*/

	m_joy2->joy_w(data & 0x1f);
}

uint8_t c64_state::cia1_pb_r()
{
	/*

	    bit     description

	    PB0     ROW0, JOY A0
	    PB1     ROW1, JOY A1
	    PB2     ROW2, JOY A2
	    PB3     ROW3, JOY A3
	    PB4     ROW4, BTNA, _LP
	    PB5     ROW5
	    PB6     ROW6
	    PB7     ROW7

	*/

	uint8_t data = 0xff;

	// joystick
	uint8_t joy_a = m_joy1->read_joy();

	data &= (0xf0 | (joy_a & 0x0f));
	data &= ~(!BIT(joy_a, 5) << 4);

	// keyboard
	uint8_t cia1_pa = m_cia1->pa_r();

	if (!BIT(cia1_pa, 7)) data &= m_row[7]->read();
	if (!BIT(cia1_pa, 6)) data &= m_row[6]->read();
	if (!BIT(cia1_pa, 5)) data &= m_row[5]->read();
	if (!BIT(cia1_pa, 4)) data &= m_row[4]->read();
	if (!BIT(cia1_pa, 3)) data &= m_row[3]->read();
	if (!BIT(cia1_pa, 2)) data &= m_row[2]->read();
	if (!BIT(cia1_pa, 1)) data &= m_row[1]->read() & m_lock->read();
	if (!BIT(cia1_pa, 0)) data &= m_row[0]->read();

	return data;
}

void c64_state::cia1_pb_w(uint8_t data)
{
	/*

	    bit     description

	    PB0     ROW0, JOY A0
	    PB1     ROW1, JOY A1
	    PB2     ROW2, JOY A2
	    PB3     ROW3, JOY A3
	    PB4     ROW4, BTNA, _LP
	    PB5     ROW5
	    PB6     ROW6
	    PB7     ROW7

	*/

	m_joy1->joy_w(data & 0x1f);

	m_vic->lp_w(BIT(data, 4));
}

uint8_t c64gs_state::cia1_pa_r()
{
	/*

	    bit     description

	    PA0     JOY B0
	    PA1     JOY B1
	    PA2     JOY B2
	    PA3     JOY B3
	    PA4     BTNB
	    PA5
	    PA6
	    PA7

	*/

	uint8_t data = 0xff;

	// joystick
	uint8_t joy_b = m_joy2->read_joy();

	data &= (0xf0 | (joy_b & 0x0f));
	data &= ~(!BIT(joy_b, 5) << 4);

	return data;
}

uint8_t c64gs_state::cia1_pb_r()
{
	/*

	    bit     description

	    PB0     JOY A0
	    PB1     JOY A1
	    PB2     JOY A2
	    PB3     JOY A3
	    PB4     BTNA/_LP
	    PB5
	    PB6
	    PB7

	*/

	uint8_t data = 0xff;

	// joystick
	uint8_t joy_a = m_joy1->read_joy();

	data &= (0xf0 | (joy_a & 0x0f));
	data &= ~(!BIT(joy_a, 5) << 4);

	return data;
}


//-------------------------------------------------
//  MOS6526_INTERFACE( cia2_intf )
//-------------------------------------------------

uint8_t c64_state::cia2_pa_r()
{
	/*

	    bit     description

	    PA0
	    PA1
	    PA2     USER PORT
	    PA3
	    PA4
	    PA5
	    PA6     CLK
	    PA7     DATA

	*/

	uint8_t data = 0;

	// user port
	data |= m_user_pa2 << 2;

	// IEC bus
	data |= m_iec->clk_r() << 6;
	data |= m_iec->data_r() << 7;

	return data;
}

void c64_state::cia2_pa_w(uint8_t data)
{
	/*

	    bit     description

	    PA0     _VA14
	    PA1     _VA15
	    PA2     USER PORT
	    PA3     ATN OUT
	    PA4     CLK OUT
	    PA5     DATA OUT
	    PA6
	    PA7

	*/

	// VIC banking
	m_va14 = BIT(data, 0);
	m_va15 = BIT(data, 1);

	// user port
	m_user->write_m(BIT(data, 2));

	// IEC bus
	m_iec->host_atn_w(!BIT(data, 3));
	m_iec->host_clk_w(!BIT(data, 4));
	m_iec->host_data_w(!BIT(data, 5));
}

uint8_t c64_state::cia2_pb_r()
{
	return m_user_pb;
}

void c64_state::cia2_pb_w(uint8_t data)
{
	m_user->write_c((data>>0)&1);
	m_user->write_d((data>>1)&1);
	m_user->write_e((data>>2)&1);
	m_user->write_f((data>>3)&1);
	m_user->write_h((data>>4)&1);
	m_user->write_j((data>>5)&1);
	m_user->write_k((data>>6)&1);
	m_user->write_l((data>>7)&1);
}

//-------------------------------------------------
//  M6510_INTERFACE( cpu_intf )
//-------------------------------------------------

uint8_t c64_state::cpu_r()
{
	/*

	    bit     description

	    P0      1
	    P1      1
	    P2      1
	    P3
	    P4      CASS SENS
	    P5      0

	*/

	uint8_t data = 0x07;

	data |= m_cassette->sense_r() << 4;

	return data;
}

void c64_state::cpu_w(uint8_t data)
{
	/*

	    bit     description

	    P0      LORAM
	    P1      HIRAM
	    P2      CHAREN
	    P3      CASS WRT
	    P4
	    P5      CASS MOTOR

	*/

	// memory banking
	m_loram = BIT(data, 0);
	m_hiram = BIT(data, 1);
	m_charen = BIT(data, 2);

	// cassette write
	m_cassette->write(BIT(data, 3));

	// cassette motor
	m_cassette->motor_w(BIT(data, 5));
}


//-------------------------------------------------
//  M6510_INTERFACE( sx64_cpu_intf )
//-------------------------------------------------

uint8_t sx64_state::cpu_r()
{
	/*

	    bit     description

	    P0      1
	    P1      1
	    P2      1
	    P3
	    P4
	    P5

	*/

	return 0x07;
}

void sx64_state::cpu_w(uint8_t data)
{
	/*

	    bit     description

	    P0      LORAM
	    P1      HIRAM
	    P2      CHAREN
	    P3
	    P4
	    P5

	*/

	// memory banking
	m_loram = BIT(data, 0);
	m_hiram = BIT(data, 1);
	m_charen = BIT(data, 2);
}


//-------------------------------------------------
//  M6510_INTERFACE( c64gs_cpu_intf )
//-------------------------------------------------

uint8_t c64gs_state::cpu_r()
{
	/*

	    bit     description

	    P0      1
	    P1      1
	    P2      1
	    P3
	    P4
	    P5

	*/

	return 0x07;
}

void c64gs_state::cpu_w(uint8_t data)
{
	/*

	    bit     description

	    P0      LORAM
	    P1      HIRAM
	    P2      CHAREN
	    P3
	    P4
	    P5

	*/

	// memory banking
	m_loram = BIT(data, 0);
	m_hiram = BIT(data, 1);
	m_charen = BIT(data, 2);
}


//-------------------------------------------------
//  C64_EXPANSION_INTERFACE( expansion_intf )
//-------------------------------------------------

void c64_state::exp_dma_w(int state)
{
	if (m_exp_dma != state)
	{
		m_exp_dma = state;

		m_maincpu->set_input_line(INPUT_LINE_HALT, m_exp_dma);
	}
}

void c64_state::exp_reset_w(int state)
{
	if (!state)
	{
		machine_reset();
	}
}


//-------------------------------------------------
//  SLOT_INTERFACE( sx1541_iec_devices )
//-------------------------------------------------

void sx1541_iec_devices(device_slot_interface &device)
{
	device.option_add("sx1541", SX1541);
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( c64 )
//-------------------------------------------------

void c64_state::machine_start()
{
	// get pointers to ROMs
	if (memregion("basic") != nullptr)
	{
		m_basic = memregion("basic")->base();
		m_kernal = memregion("kernal")->base();
	}
	else
	{
		m_basic = memregion("kernal")->base();
		m_kernal = &m_basic[0x2000];
	}
	m_charom = memregion("charom")->base();

	// initialize memory
	uint8_t data = 0xff;

	for (offs_t offset = 0; offset < m_ram->size(); offset++)
	{
		m_ram->pointer()[offset] = data;
		if (!(offset % 64)) data ^= 0xff;
	}

	// state saving
	save_item(NAME(m_loram));
	save_item(NAME(m_hiram));
	save_item(NAME(m_charen));
	save_item(NAME(m_va14));
	save_item(NAME(m_va15));
	save_item(NAME(m_exp_dma));
	save_item(NAME(m_cass_rd));
	save_item(NAME(m_iec_srq));
	save_item(NAME(m_user_pa2));
	save_item(NAME(m_user_pb));
}


void c64_state::machine_reset()
{
	m_maincpu->reset();

	m_vic->reset();
	m_sid->reset();
	m_cia1->reset();
	m_cia2->reset();

	m_iec->reset();
	m_exp->reset();

	m_user->write_3(0);
	m_user->write_3(1);
}



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

//-------------------------------------------------
//  machine_config( ntsc )
//-------------------------------------------------

void c64_state::ntsc(machine_config &config)
{
	// basic hardware
	M6510(config, m_maincpu, XTAL(14'318'181)/14);
	m_maincpu->set_addrmap(AS_PROGRAM, &c64_state::c64_mem);
	m_maincpu->read_callback().set(FUNC(c64_state::cpu_r));
	m_maincpu->write_callback().set(FUNC(c64_state::cpu_w));
	m_maincpu->set_pulls(0x17, 0xc8);
	m_maincpu->set_dasm_override(FUNC(c64_state::dasm_override));
	config.set_perfect_quantum(m_maincpu);

	input_merger_device &irq(INPUT_MERGER_ANY_HIGH(config, "irq"));
	irq.output_handler().set_inputline(m_maincpu, m6510_device::IRQ_LINE);

	INPUT_MERGER_ANY_HIGH(config, m_nmi);
	m_nmi->output_handler().set_inputline(m_maincpu, m6510_device::NMI_LINE);

	// video hardware
	mos6567_device &mos6567(MOS6567(config, MOS6567_TAG, XTAL(14'318'181)/14));
	mos6567.set_cpu(m_maincpu);
	mos6567.irq_callback().set("irq", FUNC(input_merger_device::in_w<1>));
	mos6567.set_screen(SCREEN_TAG);
	mos6567.set_addrmap(0, &c64_state::vic_videoram_map);
	mos6567.set_addrmap(1, &c64_state::vic_colorram_map);

	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(VIC6567_VRETRACERATE);
	screen.set_size(VIC6567_COLUMNS, VIC6567_LINES);
	screen.set_visarea(0, VIC6567_VISIBLECOLUMNS - 1, 0, VIC6567_VISIBLELINES - 1);
	screen.set_screen_update(MOS6567_TAG, FUNC(mos6567_device::screen_update));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	MOS6581(config, m_sid, XTAL(14'318'181)/14);
	m_sid->potx().set(FUNC(c64_state::sid_potx_r));
	m_sid->poty().set(FUNC(c64_state::sid_poty_r));
	m_sid->add_route(ALL_OUTPUTS, "mono", 1.00);

	// devices
	PLS100(config, m_pla);

	MOS6526(config, m_cia1, XTAL(14'318'181)/14);
	m_cia1->set_tod_clock(60);
	m_cia1->irq_wr_callback().set("irq", FUNC(input_merger_device::in_w<0>));
	m_cia1->cnt_wr_callback().set(m_user, FUNC(pet_user_port_device::write_4));
	m_cia1->sp_wr_callback().set(m_user, FUNC(pet_user_port_device::write_5));
	m_cia1->pa_rd_callback().set(FUNC(c64_state::cia1_pa_r));
	m_cia1->pb_rd_callback().set(FUNC(c64_state::cia1_pb_r));
	m_cia1->pb_wr_callback().set(FUNC(c64_state::cia1_pb_w));

	MOS6526(config, m_cia2, XTAL(14'318'181)/14);
	m_cia2->set_tod_clock(60);
	m_cia2->irq_wr_callback().set(m_nmi, FUNC(input_merger_device::in_w<0>));
	m_cia2->cnt_wr_callback().set(m_user, FUNC(pet_user_port_device::write_6));
	m_cia2->sp_wr_callback().set(m_user, FUNC(pet_user_port_device::write_7));
	m_cia2->pa_rd_callback().set(FUNC(c64_state::cia2_pa_r));
	m_cia2->pa_wr_callback().set(FUNC(c64_state::cia2_pa_w));
	m_cia2->pb_rd_callback().set(FUNC(c64_state::cia2_pb_r));
	m_cia2->pb_wr_callback().set(FUNC(c64_state::cia2_pb_w));
	m_cia2->pc_wr_callback().set(m_user, FUNC(pet_user_port_device::write_8));

	PET_DATASSETTE_PORT(config, m_cassette, cbm_datassette_devices, "c1530");
	m_cassette->read_handler().set(FUNC(c64_state::cass_rd_w));

	cbm_iec_slot_device::add(config, m_iec, "c1541");
	m_iec->srq_callback().set(FUNC(c64_state::iec_srq_w));
	m_iec->data_callback().set(m_user, FUNC(pet_user_port_device::write_9));

	VCS_CONTROL_PORT(config, m_joy1, vcs_control_port_devices, nullptr);
	m_joy1->trigger_wr_callback().set(MOS6567_TAG, FUNC(mos6567_device::lp_w));
	VCS_CONTROL_PORT(config, m_joy2, vcs_control_port_devices, "joy");

	C64_EXPANSION_SLOT(config, m_exp, XTAL(14'318'181)/14, c64_expansion_cards, nullptr);
	m_exp->irq_callback().set("irq", FUNC(input_merger_device::in_w<2>));
	m_exp->nmi_callback().set(m_nmi, FUNC(input_merger_device::in_w<2>));
	m_exp->reset_callback().set(FUNC(c64_state::exp_reset_w));
	m_exp->cd_input_callback().set(FUNC(c64_state::read));
	m_exp->cd_output_callback().set(FUNC(c64_state::write));
	m_exp->dma_callback().set(FUNC(c64_state::exp_dma_w));

	PET_USER_PORT(config, m_user, c64_user_port_cards, nullptr);
	m_user->p3_handler().set(FUNC(c64_state::exp_reset_w));
	m_user->p4_handler().set(m_cia1, FUNC(mos6526_device::cnt_w));
	m_user->p5_handler().set(m_cia1, FUNC(mos6526_device::sp_w));
	m_user->p6_handler().set(m_cia2, FUNC(mos6526_device::cnt_w));
	m_user->p7_handler().set(m_cia2, FUNC(mos6526_device::sp_w));
	m_user->p9_handler().set(m_iec, FUNC(cbm_iec_device::host_atn_w));
	m_user->pb_handler().set(m_cia2, FUNC(mos6526_device::flag_w));
	m_user->pc_handler().set(FUNC(c64_state::write_user_pb0));
	m_user->pd_handler().set(FUNC(c64_state::write_user_pb1));
	m_user->pe_handler().set(FUNC(c64_state::write_user_pb2));
	m_user->pf_handler().set(FUNC(c64_state::write_user_pb3));
	m_user->ph_handler().set(FUNC(c64_state::write_user_pb4));
	m_user->pj_handler().set(FUNC(c64_state::write_user_pb5));
	m_user->pk_handler().set(FUNC(c64_state::write_user_pb6));
	m_user->pl_handler().set(FUNC(c64_state::write_user_pb7));
	m_user->pm_handler().set(FUNC(c64_state::write_user_pa2));

	QUICKLOAD(config, "quickload", "p00,prg,t64", CBM_QUICKLOAD_DELAY).set_load_callback(FUNC(c64_state::quickload_c64));

	// software list
	SOFTWARE_LIST(config, "cart_list_vic10").set_original("vic10").set_filter("NTSC");
	SOFTWARE_LIST(config, "cart_list_c64").set_original("c64_cart").set_filter("NTSC");
	SOFTWARE_LIST(config, "cass_list").set_original("c64_cass").set_filter("NTSC");
	// disk softlist split into originals and misc (homebrew and cracks)
	SOFTWARE_LIST(config, "flop525_orig").set_original("c64_flop_orig").set_filter("NTSC");
	SOFTWARE_LIST(config, "flop525_misc").set_original("c64_flop_misc").set_filter("NTSC");

	// internal ram
	RAM(config, RAM_TAG).set_default_size("64K");
}


//-------------------------------------------------
//  machine_config( pet64 )
//-------------------------------------------------

void c64_state::pet64(machine_config &config)
{
	ntsc(config);
	// TODO monochrome green palette
}


//-------------------------------------------------
//  machine_config( ntsc_sx )
//-------------------------------------------------

void sx64_state::ntsc_sx(machine_config &config)
{
	ntsc(config);

	// basic hardware
	m_maincpu->read_callback().set(FUNC(sx64_state::cpu_r));
	m_maincpu->write_callback().set(FUNC(sx64_state::cpu_w));
	m_maincpu->set_pulls(0x07, 0xc0);

	// devices
	CBM_IEC_SLOT(config.replace(), "iec8", 8, sx1541_iec_devices, "sx1541");
}


//-------------------------------------------------
//  machine_config( ntsc_dx )
//-------------------------------------------------

void sx64_state::ntsc_dx(machine_config &config)
{
	ntsc_sx(config);

	// devices
	CBM_IEC_SLOT(config.replace(), "iec9", 9, sx1541_iec_devices, "sx1541");
}


//-------------------------------------------------
//  machine_config( ntsc_c )
//-------------------------------------------------

void c64c_state::ntsc_c(machine_config &config)
{
	ntsc(config);
	MOS8580(config.replace(), m_sid, XTAL(14'318'181)/14);
	m_sid->potx().set(FUNC(c64_state::sid_potx_r));
	m_sid->poty().set(FUNC(c64_state::sid_poty_r));
	m_sid->add_route(ALL_OUTPUTS, "mono", 1.00);
}


//-------------------------------------------------
//  machine_config( pal )
//-------------------------------------------------

void c64_state::pal(machine_config &config)
{
	// basic hardware
	M6510(config, m_maincpu, XTAL(17'734'472)/18);
	m_maincpu->set_addrmap(AS_PROGRAM, &c64_state::c64_mem);
	m_maincpu->read_callback().set(FUNC(c64_state::cpu_r));
	m_maincpu->write_callback().set(FUNC(c64_state::cpu_w));
	m_maincpu->set_pulls(0x17, 0xc8);
	m_maincpu->set_dasm_override(FUNC(c64_state::dasm_override));
	config.set_perfect_quantum(m_maincpu);

	input_merger_device &irq(INPUT_MERGER_ANY_HIGH(config, "irq"));
	irq.output_handler().set_inputline(m_maincpu, m6510_device::IRQ_LINE);

	INPUT_MERGER_ANY_HIGH(config, m_nmi);
	m_nmi->output_handler().set_inputline(m_maincpu, m6510_device::NMI_LINE);

	// video hardware
	mos6569_device &mos6569(MOS6569(config, MOS6569_TAG, XTAL(17'734'472)/18));
	mos6569.set_cpu(m_maincpu);
	mos6569.irq_callback().set("irq", FUNC(input_merger_device::in_w<1>));
	mos6569.set_screen(SCREEN_TAG);
	mos6569.set_addrmap(0, &c64_state::vic_videoram_map);
	mos6569.set_addrmap(1, &c64_state::vic_colorram_map);

	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(VIC6569_VRETRACERATE);
	screen.set_size(VIC6569_COLUMNS, VIC6569_LINES);
	screen.set_visarea(0, VIC6569_VISIBLECOLUMNS - 1, 0, VIC6569_VISIBLELINES - 1);
	screen.set_screen_update(MOS6569_TAG, FUNC(mos6569_device::screen_update));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	MOS6581(config, m_sid, XTAL(17'734'472)/18);
	m_sid->potx().set(FUNC(c64_state::sid_potx_r));
	m_sid->poty().set(FUNC(c64_state::sid_poty_r));
	m_sid->add_route(ALL_OUTPUTS, "mono", 1.00);

	// devices
	PLS100(config, m_pla);

	MOS6526(config, m_cia1, XTAL(17'734'472)/18);
	m_cia1->set_tod_clock(50);
	m_cia1->irq_wr_callback().set("irq", FUNC(input_merger_device::in_w<0>));
	m_cia1->cnt_wr_callback().set(m_user, FUNC(pet_user_port_device::write_4));
	m_cia1->sp_wr_callback().set(m_user, FUNC(pet_user_port_device::write_5));
	m_cia1->pa_rd_callback().set(FUNC(c64_state::cia1_pa_r));
	m_cia1->pb_rd_callback().set(FUNC(c64_state::cia1_pb_r));
	m_cia1->pb_wr_callback().set(FUNC(c64_state::cia1_pb_w));

	MOS6526(config, m_cia2, XTAL(17'734'472)/18);
	m_cia2->set_tod_clock(50);
	m_cia2->irq_wr_callback().set(m_nmi, FUNC(input_merger_device::in_w<0>));
	m_cia2->cnt_wr_callback().set(m_user, FUNC(pet_user_port_device::write_6));
	m_cia2->sp_wr_callback().set(m_user, FUNC(pet_user_port_device::write_7));
	m_cia2->pa_rd_callback().set(FUNC(c64_state::cia2_pa_r));
	m_cia2->pa_wr_callback().set(FUNC(c64_state::cia2_pa_w));
	m_cia2->pb_rd_callback().set(FUNC(c64_state::cia2_pb_r));
	m_cia2->pb_wr_callback().set(FUNC(c64_state::cia2_pb_w));
	m_cia2->pc_wr_callback().set(m_user, FUNC(pet_user_port_device::write_8));

	PET_DATASSETTE_PORT(config, m_cassette, cbm_datassette_devices, "c1530");
	m_cassette->read_handler().set(FUNC(c64_state::cass_rd_w));

	cbm_iec_slot_device::add(config, m_iec, "c1541");
	m_iec->srq_callback().set(FUNC(c64_state::iec_srq_w));
	m_iec->data_callback().set(m_user, FUNC(pet_user_port_device::write_9));

	VCS_CONTROL_PORT(config, m_joy1, vcs_control_port_devices, nullptr);
	m_joy1->trigger_wr_callback().set(MOS6569_TAG, FUNC(mos6569_device::lp_w));
	VCS_CONTROL_PORT(config, m_joy2, vcs_control_port_devices, "joy");

	C64_EXPANSION_SLOT(config, m_exp, XTAL(17'734'472)/18, c64_expansion_cards, nullptr);
	m_exp->irq_callback().set("irq", FUNC(input_merger_device::in_w<2>));
	m_exp->nmi_callback().set(m_nmi, FUNC(input_merger_device::in_w<2>));
	m_exp->reset_callback().set(FUNC(c64_state::exp_reset_w));
	m_exp->cd_input_callback().set(FUNC(c64_state::read));
	m_exp->cd_output_callback().set(FUNC(c64_state::write));
	m_exp->dma_callback().set(FUNC(c64_state::exp_dma_w));

	PET_USER_PORT(config, m_user, c64_user_port_cards, nullptr);
	m_user->p3_handler().set(FUNC(c64_state::exp_reset_w));
	m_user->p4_handler().set(m_cia1, FUNC(mos6526_device::cnt_w));
	m_user->p5_handler().set(m_cia1, FUNC(mos6526_device::sp_w));
	m_user->p6_handler().set(m_cia2, FUNC(mos6526_device::cnt_w));
	m_user->p7_handler().set(m_cia2, FUNC(mos6526_device::sp_w));
	m_user->p9_handler().set(m_iec, FUNC(cbm_iec_device::host_atn_w));
	m_user->pb_handler().set(m_cia2, FUNC(mos6526_device::flag_w));
	m_user->pc_handler().set(FUNC(c64_state::write_user_pb0));
	m_user->pd_handler().set(FUNC(c64_state::write_user_pb1));
	m_user->pe_handler().set(FUNC(c64_state::write_user_pb2));
	m_user->pf_handler().set(FUNC(c64_state::write_user_pb3));
	m_user->ph_handler().set(FUNC(c64_state::write_user_pb4));
	m_user->pj_handler().set(FUNC(c64_state::write_user_pb5));
	m_user->pk_handler().set(FUNC(c64_state::write_user_pb6));
	m_user->pl_handler().set(FUNC(c64_state::write_user_pb7));
	m_user->pm_handler().set(FUNC(c64_state::write_user_pa2));

	QUICKLOAD(config, "quickload", "p00,prg,t64", CBM_QUICKLOAD_DELAY).set_load_callback(FUNC(c64_state::quickload_c64));

	// software list
	SOFTWARE_LIST(config, "cart_list_vic10").set_original("vic10").set_filter("PAL");
	SOFTWARE_LIST(config, "cart_list_c64").set_original("c64_cart").set_filter("PAL");
	SOFTWARE_LIST(config, "cass_list").set_original("c64_cass").set_filter("PAL");
	// disk softlist split into originals and misc (homebrew and cracks)
	SOFTWARE_LIST(config, "flop525_orig").set_original("c64_flop_orig").set_filter("PAL");
	SOFTWARE_LIST(config, "flop525_misc").set_original("c64_flop_misc").set_filter("PAL");

	// internal ram
	RAM(config, RAM_TAG).set_default_size("64K");
}


//-------------------------------------------------
//  machine_config( pal_sx )
//-------------------------------------------------

void sx64_state::pal_sx(machine_config &config)
{
	pal(config);

	// basic hardware
	m_maincpu->read_callback().set(FUNC(sx64_state::cpu_r));
	m_maincpu->write_callback().set(FUNC(sx64_state::cpu_w));
	m_maincpu->set_pulls(0x07, 0xc0);

	// devices
	CBM_IEC_SLOT(config.replace(), "iec8", 8, sx1541_iec_devices, "sx1541");
}


//-------------------------------------------------
//  machine_config( pal_c )
//-------------------------------------------------

void c64c_state::pal_c(machine_config &config)
{
	pal(config);
	MOS8580(config.replace(), m_sid, XTAL(17'734'472)/18);
	m_sid->potx().set(FUNC(c64_state::sid_potx_r));
	m_sid->poty().set(FUNC(c64_state::sid_poty_r));
	m_sid->add_route(ALL_OUTPUTS, "mono", 1.00);
}


//-------------------------------------------------
//  machine_config( pal_gs )
//-------------------------------------------------

void c64gs_state::pal_gs(machine_config &config)
{
	// basic hardware
	M6510(config, m_maincpu, XTAL(17'734'472)/18);
	m_maincpu->set_addrmap(AS_PROGRAM, &c64gs_state::c64_mem);
	m_maincpu->read_callback().set(FUNC(c64gs_state::cpu_r));
	m_maincpu->write_callback().set(FUNC(c64gs_state::cpu_w));
	m_maincpu->set_pulls(0x07, 0xc0);
	m_maincpu->set_dasm_override(FUNC(c64_state::dasm_override));
	config.set_perfect_quantum(m_maincpu);

	input_merger_device &irq(INPUT_MERGER_ANY_HIGH(config, "irq"));
	irq.output_handler().set_inputline(m_maincpu, m6510_device::IRQ_LINE);

	INPUT_MERGER_ANY_HIGH(config, m_nmi);
	m_nmi->output_handler().set_inputline(m_maincpu, m6510_device::NMI_LINE);

	// video hardware
	mos8565_device &mos8565(MOS8565(config, MOS6569_TAG, XTAL(17'734'472)/18));
	mos8565.set_cpu(m_maincpu);
	mos8565.irq_callback().set("irq", FUNC(input_merger_device::in_w<1>));
	mos8565.set_screen(SCREEN_TAG);
	mos8565.set_addrmap(0, &c64_state::vic_videoram_map);
	mos8565.set_addrmap(1, &c64_state::vic_colorram_map);

	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(VIC6569_VRETRACERATE);
	screen.set_size(VIC6569_COLUMNS, VIC6569_LINES);
	screen.set_visarea(0, VIC6569_VISIBLECOLUMNS - 1, 0, VIC6569_VISIBLELINES - 1);
	screen.set_screen_update(MOS6569_TAG, FUNC(mos8565_device::screen_update));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	MOS8580(config, m_sid, XTAL(17'734'472)/18);
	m_sid->potx().set(FUNC(c64_state::sid_potx_r));
	m_sid->poty().set(FUNC(c64_state::sid_poty_r));
	m_sid->add_route(ALL_OUTPUTS, "mono", 1.00);

	// devices
	PLS100(config, m_pla);

	MOS6526(config, m_cia1, XTAL(17'734'472)/18);
	m_cia1->set_tod_clock(50);
	m_cia1->irq_wr_callback().set("irq", FUNC(input_merger_device::in_w<0>));
	m_cia1->cnt_wr_callback().set(m_user, FUNC(pet_user_port_device::write_4));
	m_cia1->sp_wr_callback().set(m_user, FUNC(pet_user_port_device::write_5));
	m_cia1->pa_rd_callback().set(FUNC(c64gs_state::cia1_pa_r));
	m_cia1->pa_wr_callback().set(FUNC(c64_state::cia1_pa_w));
	m_cia1->pb_rd_callback().set(FUNC(c64gs_state::cia1_pb_r));
	m_cia1->pb_wr_callback().set(FUNC(c64_state::cia1_pb_w));

	MOS6526(config, m_cia2, XTAL(17'734'472)/18);
	m_cia2->set_tod_clock(50);
	m_cia2->irq_wr_callback().set(m_nmi, FUNC(input_merger_device::in_w<0>));
	m_cia2->cnt_wr_callback().set(m_user, FUNC(pet_user_port_device::write_6));
	m_cia2->sp_wr_callback().set(m_user, FUNC(pet_user_port_device::write_7));
	m_cia2->pa_rd_callback().set(FUNC(c64_state::cia2_pa_r));
	m_cia2->pa_wr_callback().set(FUNC(c64_state::cia2_pa_w));
	m_cia2->pb_rd_callback().set(FUNC(c64_state::cia2_pb_r));
	m_cia2->pb_wr_callback().set(FUNC(c64_state::cia2_pb_w));
	m_cia2->pc_wr_callback().set(m_user, FUNC(pet_user_port_device::write_8));

	cbm_iec_slot_device::add(config, m_iec, nullptr);
	m_iec->srq_callback().set(m_cia1, FUNC(mos6526_device::flag_w));
	m_iec->data_callback().set(m_user, FUNC(pet_user_port_device::write_9));

	VCS_CONTROL_PORT(config, m_joy1, vcs_control_port_devices, nullptr);
	m_joy1->trigger_wr_callback().set(MOS6569_TAG, FUNC(mos6569_device::lp_w));
	VCS_CONTROL_PORT(config, m_joy2, vcs_control_port_devices, "joy");

	C64_EXPANSION_SLOT(config, m_exp, XTAL(17'734'472)/18, c64_expansion_cards, nullptr);
	m_exp->irq_callback().set("irq", FUNC(input_merger_device::in_w<2>));
	m_exp->nmi_callback().set(m_nmi, FUNC(input_merger_device::in_w<2>));
	m_exp->reset_callback().set(FUNC(c64_state::exp_reset_w));
	m_exp->cd_input_callback().set(FUNC(c64_state::read));
	m_exp->cd_output_callback().set(FUNC(c64_state::write));
	m_exp->dma_callback().set(FUNC(c64_state::exp_dma_w));

	PET_USER_PORT(config, m_user, c64_user_port_cards, nullptr);
	m_user->p3_handler().set(FUNC(c64_state::exp_reset_w));
	m_user->p4_handler().set(m_cia1, FUNC(mos6526_device::cnt_w));
	m_user->p5_handler().set(m_cia1, FUNC(mos6526_device::sp_w));
	m_user->p6_handler().set(m_cia2, FUNC(mos6526_device::cnt_w));
	m_user->p7_handler().set(m_cia2, FUNC(mos6526_device::sp_w));
	m_user->p9_handler().set(m_iec, FUNC(cbm_iec_device::host_atn_w));
	m_user->pb_handler().set(m_cia2, FUNC(mos6526_device::flag_w));
	m_user->pc_handler().set(FUNC(c64_state::write_user_pb0));
	m_user->pd_handler().set(FUNC(c64_state::write_user_pb1));
	m_user->pe_handler().set(FUNC(c64_state::write_user_pb2));
	m_user->pf_handler().set(FUNC(c64_state::write_user_pb3));
	m_user->ph_handler().set(FUNC(c64_state::write_user_pb4));
	m_user->pj_handler().set(FUNC(c64_state::write_user_pb5));
	m_user->pk_handler().set(FUNC(c64_state::write_user_pb6));
	m_user->pl_handler().set(FUNC(c64_state::write_user_pb7));
	m_user->pm_handler().set(FUNC(c64_state::write_user_pa2));

	QUICKLOAD(config, "quickload", "p00,prg,t64", CBM_QUICKLOAD_DELAY).set_load_callback(FUNC(c64_state::quickload_c64));

	// software list
	SOFTWARE_LIST(config, "cart_list_vic10").set_original("vic10").set_filter("PAL");
	SOFTWARE_LIST(config, "cart_list_c64").set_original("c64_cart").set_filter("PAL");

	// internal ram
	RAM(config, RAM_TAG).set_default_size("64K");
}


//-------------------------------------------------
//  machine_config( clipper )
//-------------------------------------------------

void clipper_state::clipper(machine_config &config)
{
	pal(config);

	// TODO extra hardware

	// software list
	SOFTWARE_LIST(config, "flop525").set_original("clipper_flop");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( c64 )
//-------------------------------------------------

ROM_START( c64 )
	ROM_REGION( 0x2000, "basic", 0 )
	ROM_LOAD( "901226-01.u3", 0x0000, 0x2000, CRC(f833d117) SHA1(79015323128650c742a3694c9429aa91f355905e) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROM_DEFAULT_BIOS("r3")
	ROM_SYSTEM_BIOS(0, "r1", "Kernal rev. 1" )
	ROMX_LOAD( "901227-01.u4", 0x0000, 0x2000, CRC(dce782fa) SHA1(87cc04d61fc748b82df09856847bb5c2754a2033), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "r2", "Kernal rev. 2" )
	ROMX_LOAD( "901227-02.u4", 0x0000, 0x2000, CRC(a5c687b3) SHA1(0e2e4ee3f2d41f00bed72f9ab588b83e306fdb13), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS(2, "r3", "Kernal rev. 3" )
	ROMX_LOAD( "901227-03.u4", 0x0000, 0x2000, CRC(dbe3e7c7) SHA1(1d503e56df85a62fee696e7618dc5b4e781df1bb), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS(3, "jiffydos", "JiffyDOS v6.01" )
	ROMX_LOAD( "jiffydos c64.u4", 0x0000, 0x2000, CRC(2f79984c) SHA1(31e73e66eccb28732daea8ec3ad1addd9b39a017), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS(4, "speeddos", "SpeedDOS" )
	ROMX_LOAD( "speed-dos.u4", 0x0000, 0x2000, CRC(5beb9ac8) SHA1(8896c8de9e26ef1396eb46020b2de346a3eeab7e), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS(5, "speeddos20", "SpeedDOS-Plus+ v2.0" )
	ROMX_LOAD( "speed-dosplus.u4", 0x0000, 0x2000, CRC(10aee0ae) SHA1(6cebd4dc0c5e8c0b073586a3f1c43cc3349b9736), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS(6, "speeddos27", "SpeedDOS-Plus+ v2.7" )
	ROMX_LOAD( "speed-dosplus27.u4", 0x0000, 0x2000, CRC(ff59995e) SHA1(c8d864e5fc7089af8afce97dc0a0224df11df1c3), ROM_BIOS(6) )
	ROM_SYSTEM_BIOS(7, "prodos", "Professional-DOS v1" )
	ROMX_LOAD( "prodos.u4", 0x0000, 0x2000, CRC(37ed83a2) SHA1(35f4f0fe03c0b7b3762b526ba855de41b496fb60), ROM_BIOS(7) )
	ROM_SYSTEM_BIOS(8, "prodos2", "Professional-DOS Release 2/4L2" )
	ROMX_LOAD( "prodos24l2.u4", 0x0000, 0x2000, CRC(41dad9fe) SHA1(fbf3dcc2ed40e58b07595740ea6fbff7ab19ebad), ROM_BIOS(8) )
	ROM_SYSTEM_BIOS(9, "prodos3", "Professional-DOS Release 3/5L2" )
	ROMX_LOAD( "prodos35l2.u4", 0x0000, 0x2000, CRC(2822eee7) SHA1(77356b84c1648018863d1c8dd5bc3a37485bc00e), ROM_BIOS(9) )
	ROM_SYSTEM_BIOS(10, "turborom", "Cockroach Turbo-ROM" )
	ROMX_LOAD( "turborom.u4", 0x0000, 0x2000, CRC(e6c763a2) SHA1(eff5a4b6bc65daa9421bd3856dd99a3195068e1c), ROM_BIOS(10) )
	ROM_SYSTEM_BIOS(11, "dosrom", "DOS-ROM v1.2" )
	ROMX_LOAD( "dosrom12.u4", 0x0000, 0x2000, CRC(ac030fc0) SHA1(0e4b38e81b49f55d52162154a44f0fffd2b0d04f), ROM_BIOS(11) )
	ROM_SYSTEM_BIOS(12, "turborom2", "Datel Turbo ROM II (PAL)" )
	ROMX_LOAD( "turborom2.u4", 0x0000, 0x2000, CRC(ea3ba683) SHA1(4bb23f764a3d255119fbae37202ca820caa04e1f), ROM_BIOS(12) )
	ROM_SYSTEM_BIOS(13, "mercury", "Mercury-ROM v3 (NTSC)" )
	ROMX_LOAD( "mercury3.u4", 0x0000, 0x2000, CRC(6eac46a2) SHA1(4e351aa5fcb97c4c21e565aa2c830cc09bd47533), ROM_BIOS(13) )
	ROM_SYSTEM_BIOS(14, "dolphin", "Dolphin-DOS 1.0" )
	ROMX_LOAD( "kernal-10-mager.u4", 0x0000, 0x2000, CRC(c9bb21bc) SHA1(e305216e50ff8a7acf102be6c6343e3d44a16233), ROM_BIOS(14) )
	ROM_SYSTEM_BIOS(15, "dolphin201au", "Dolphin-DOS 2.0 1 au" )
	ROMX_LOAD( "kernal-20-1_au.u4", 0x0000, 0x2000, CRC(7068bbcc) SHA1(325ce7e32609a8fc704aaa76f5eb4cd7d8099a92), ROM_BIOS(15) )
	ROM_SYSTEM_BIOS(16, "dolphin201", "Dolphin-DOS 2.0 1" )
	ROMX_LOAD( "kernal-20-1.u4", 0x0000, 0x2000, CRC(c9c4c44e) SHA1(7f5d8f08c5ed2182ffb415a3d777fdd922496d02), ROM_BIOS(16) )
	ROM_SYSTEM_BIOS(17, "dolphin202", "Dolphin-DOS 2.0 2" )
	ROMX_LOAD( "kernal-20-2.u4", 0x0000, 0x2000, CRC(ffaeb9bc) SHA1(5f6c1bad379da16f77bccb58e80910f307dfd5f8), ROM_BIOS(17) )
	ROM_SYSTEM_BIOS(18, "dolphin203", "Dolphin-DOS 2.0 3" )
	ROMX_LOAD( "kernal-20-3.u4", 0x0000, 0x2000, CRC(4fd511f2) SHA1(316fba280dcb29496d593c0c4e3ee9a19844054e), ROM_BIOS(18) )
	ROM_SYSTEM_BIOS(19, "dolphin30", "Dolphin-DOS 3.0" )
	ROMX_LOAD( "kernal-30.u4", 0x0000, 0x2000, CRC(5402d643) SHA1(733acb96fead2fb4df77840c5bb618f08439fc7e), ROM_BIOS(19) )
	ROM_SYSTEM_BIOS(20, "taccess", "TurboAccess v2.6" )
	ROMX_LOAD( "turboaccess26.u4", 0x0000, 0x2000, CRC(93de6cd9) SHA1(a74478f3b9153c13176eac80ebfacc512ae7cbf0), ROM_BIOS(20) )
	ROM_SYSTEM_BIOS(21, "ttrans301", "TurboTrans v3.0 1" )
	ROMX_LOAD( "turboaccess301.u4", 0x0000, 0x2000, CRC(b3304dcf) SHA1(4d47a265ef65e4823f862cfc3d514c2a71473580), ROM_BIOS(21) )
	ROM_SYSTEM_BIOS(22, "ttrans302", "TurboTrans v3.0 2" )
	ROMX_LOAD( "turboaccess302.u4", 0x0000, 0x2000, CRC(9e696a7b) SHA1(5afae75d66d539f4bb4af763f029f0ef6523a4eb), ROM_BIOS(22) )
	ROM_SYSTEM_BIOS(23, "tprocess", "Turbo-Process (PAL)" )
	ROMX_LOAD( "turboprocess.u4", 0x0000, 0x2000, CRC(e5610d76) SHA1(e3f35777cfd16cce4717858f77ff354763395ba9), ROM_BIOS(23) )
	ROM_SYSTEM_BIOS(24, "tprocessn", "Turbo-Process (NTSC)" )
	ROMX_LOAD( "turboprocessus.u4", 0x0000, 0x2000, CRC(7480b76a) SHA1(ef1664b5057ae3cc6d104fc2f5c1fb29ee5a1b2b), ROM_BIOS(24) )
	ROM_SYSTEM_BIOS(25, "exos3", "EXOS v3" )
	ROMX_LOAD( "exos3.u4", 0x0000, 0x2000, CRC(4e54d020) SHA1(f8931b7c0b26807f4de0cc241f0b1e2c8f5271e9), ROM_BIOS(25) )
	ROM_SYSTEM_BIOS(26, "exos4", "EXOS v4" )
	ROMX_LOAD( "exos4.u4", 0x0000, 0x2000, CRC(d5cf83a9) SHA1(d5f03a5c0e9d00032d4751ecc6bcd6385879c9c7), ROM_BIOS(26) )
	ROM_SYSTEM_BIOS(27, "digidos", "DigiDOS" )
	ROMX_LOAD( "digidos.u4", 0x0000, 0x2000, CRC(2b0c8e89) SHA1(542d6f61c318bced0642e7c2d4d3b34a0f13e634), ROM_BIOS(27) )
	ROM_SYSTEM_BIOS(28, "magnum", "Magnum Load" )
	ROMX_LOAD( "magnum.u4", 0x0000, 0x2000, CRC(b2cffcc6) SHA1(827c782c1723b5d0992c05c00738ae4b2133b641), ROM_BIOS(28) )
	ROM_SYSTEM_BIOS(29, "mercury31s", "Mercury-ROM v3.1s" )
	ROMX_LOAD( "mercury31s.u4", 0x0000, 0x2000, CRC(97aa5d2f) SHA1(9fc653e61c34225245036f266db14e05feeadb21), ROM_BIOS(29) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901225-01.u5", 0x0000, 0x1000, CRC(ec4272ee) SHA1(adc7c31e18c7c7413d54802ef2f4193da14711aa) )

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "906114-01.u17", 0x00, 0xf5, CRC(54c89351) SHA1(efb315f560b6f72444b8f0b2ca4b0ccbcd144a1b) )
ROM_END


//-------------------------------------------------
//  ROM( c64_jp )
//-------------------------------------------------

ROM_START( c64_jp )
	ROM_REGION( 0x2000, "basic", 0 )
	ROM_LOAD( "901226-01.u3", 0x0000, 0x2000, CRC(f833d117) SHA1(79015323128650c742a3694c9429aa91f355905e) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROM_LOAD( "906145-02.u4", 0x0000, 0x2000, CRC(3a9ef6f1) SHA1(4ff0f11e80f4b57430d8f0c3799ed0f0e0f4565d) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "906143-02.u5", 0x0000, 0x1000, CRC(1604f6c1) SHA1(0fad19dbcdb12461c99657b2979dbb5c2e47b527) )

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "906114-01.u17", 0x00, 0xf5, CRC(54c89351) SHA1(efb315f560b6f72444b8f0b2ca4b0ccbcd144a1b) )
ROM_END


//-------------------------------------------------
//  ROM( c64p )
//-------------------------------------------------

#define rom_c64p rom_c64


//-------------------------------------------------
//  ROM( c64_se )
//-------------------------------------------------

ROM_START( c64_se )
	ROM_REGION( 0x2000, "basic", 0 )
	ROM_LOAD( "901226-01.u3", 0x0000, 0x2000, CRC(f833d117) SHA1(79015323128650c742a3694c9429aa91f355905e) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROM_LOAD( "kernel.u4",  0x0000, 0x2000, CRC(f10c2c25) SHA1(e4f52d9b36c030eb94524eb49f6f0774c1d02e5e) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_SYSTEM_BIOS( 0, "default", "Swedish Characters" )
	ROMX_LOAD( "charswe.u5", 0x0000, 0x1000, CRC(bee9b3fd) SHA1(446ae58f7110d74d434301491209299f66798d8a), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "alt", "Swedish Characters (Alt)" )
	ROMX_LOAD( "charswe2.u5", 0x0000, 0x1000, CRC(377a382b) SHA1(20df25e0ba1c88f31689c1521397c96968967fac), ROM_BIOS(1) )

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "906114-01.u17", 0x00, 0xf5, CRC(54c89351) SHA1(efb315f560b6f72444b8f0b2ca4b0ccbcd144a1b) )
ROM_END


//-------------------------------------------------
//  ROM( pet64 )
//-------------------------------------------------

ROM_START( pet64 )
	ROM_REGION( 0x2000, "basic", 0 )
	ROM_LOAD( "901226-01.u3", 0x0000, 0x2000, CRC(f833d117) SHA1(79015323128650c742a3694c9429aa91f355905e) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROM_LOAD( "901246-01.u4", 0x0000, 0x2000, CRC(789c8cc5) SHA1(6c4fa9465f6091b174df27dfe679499df447503c) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901225-01.u5", 0x0000, 0x1000, CRC(ec4272ee) SHA1(adc7c31e18c7c7413d54802ef2f4193da14711aa) )

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "906114-01.u17", 0x00, 0xf5, CRC(54c89351) SHA1(efb315f560b6f72444b8f0b2ca4b0ccbcd144a1b) )
ROM_END


//-------------------------------------------------
//  ROM( edu64 )
//-------------------------------------------------

#define rom_edu64   rom_c64


//-------------------------------------------------
//  ROM( sx64 )
//-------------------------------------------------

ROM_START( sx64 )
	ROM_REGION( 0x2000, "basic", 0 )
	ROM_LOAD( "901226-01.ud4", 0x0000, 0x2000, CRC(f833d117) SHA1(79015323128650c742a3694c9429aa91f355905e) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROM_SYSTEM_BIOS(0, "cbm", "Original" )
	ROMX_LOAD( "251104-04.ud3", 0x0000, 0x2000, CRC(2c5965d4) SHA1(aa136e91ecf3c5ac64f696b3dbcbfc5ba0871c98), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "jiffydos", "JiffyDOS v6.01" )
	ROMX_LOAD( "jiffydos sx64.ud3", 0x0000, 0x2000, CRC(2b5a88f5) SHA1(942c2150123dc30f40b3df6086132ef0a3c43948), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS(2, "1541flash", "1541 FLASH!" )
	ROMX_LOAD( "1541 flash.ud3", 0x0000, 0x2000, CRC(0a1c9b85) SHA1(0bfcaab0ae453b663a6e01cd59a9764805419e00), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS(3, "turborom", "Cockroach Turbo-ROM" )
	ROMX_LOAD( "turboromsx.u4", 0x0000, 0x2000, CRC(48579c30) SHA1(6c907fdd07c14e162eb8c8fb750b1bbaf69dccb4), ROM_BIOS(3) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901225-01.ud1", 0x0000, 0x1000, CRC(ec4272ee) SHA1(adc7c31e18c7c7413d54802ef2f4193da14711aa) )

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "906114-01.ue4", 0x00, 0xf5, CRC(54c89351) SHA1(efb315f560b6f72444b8f0b2ca4b0ccbcd144a1b) )
ROM_END


//-------------------------------------------------
//  ROM( rom_sx64p )
//-------------------------------------------------

#define rom_sx64p   rom_sx64


//-------------------------------------------------
//  ROM( vip64 )
//-------------------------------------------------

ROM_START( vip64 )
	ROM_REGION( 0x2000, "basic", 0 )
	ROM_LOAD( "901226-01.ud4", 0x0000, 0x2000, CRC(f833d117) SHA1(79015323128650c742a3694c9429aa91f355905e) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROM_LOAD( "kernelsx.ud3", 0x0000, 0x2000, CRC(7858d3d7) SHA1(097cda60469492a8916c2677b7cce4e12a944bc0) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "charswe.ud1", 0x0000, 0x1000, CRC(bee9b3fd) SHA1(446ae58f7110d74d434301491209299f66798d8a) )

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "906114-01.ue4", 0x00, 0xf5, CRC(54c89351) SHA1(efb315f560b6f72444b8f0b2ca4b0ccbcd144a1b) )
ROM_END


//-------------------------------------------------
//  ROM( dx64 )
//-------------------------------------------------

// ROM_LOAD( "dx64kern.bin", 0x0000, 0x2000, CRC(58065128) ) TODO where is this illusive ROM?
#define rom_dx64    rom_sx64


//-------------------------------------------------
//  ROM( tesa6240 )
//-------------------------------------------------

ROM_START( tesa6240 )
	ROM_REGION( 0x2000, "basic", 0 )
	ROM_LOAD( "tesa-basic.ud4", 0x0000, 0x2000, CRC(f319d661) SHA1(0033afa7d2fbff314d80427324633c5444fbf1cd) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROM_LOAD( "tesa-kernal.ud3", 0x0000, 0x2000, CRC(af638f9c) SHA1(a2c9c83f598623c9940949979ac643f12397e907) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "tesa-char.ud1", 0x0000, 0x1000, CRC(10765a90) SHA1(1b824df5a295d0479e830e272758640b9fe99344) )

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "906114-01.ue4", 0x00, 0xf5, CRC(54c89351) SHA1(efb315f560b6f72444b8f0b2ca4b0ccbcd144a1b) )
ROM_END


//-------------------------------------------------
//  ROM( c64c )
//-------------------------------------------------

ROM_START( c64c )
	ROM_REGION( 0x4000, "kernal", 0 )
	ROM_DEFAULT_BIOS("cbm")
	ROM_SYSTEM_BIOS(0, "cbm", "Original" )
	ROMX_LOAD( "251913-01.u4", 0x0000, 0x4000, CRC(0010ec31) SHA1(765372a0e16cbb0adf23a07b80f6b682b39fbf88), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "pdc", "ProLogic-DOS Classic" )
	ROMX_LOAD( "pdc.u4", 0x0000, 0x4000, CRC(6b653b9c) SHA1(0f44a9c62619424a0cd48a90e1b377b987b494e0), ROM_BIOS(1) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901225-01.u5", 0x0000, 0x1000, CRC(ec4272ee) SHA1(adc7c31e18c7c7413d54802ef2f4193da14711aa) )

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "252715-01.u8", 0x00, 0xf5, BAD_DUMP CRC(54c89351) SHA1(efb315f560b6f72444b8f0b2ca4b0ccbcd144a1b) )
ROM_END


//-------------------------------------------------
//  ROM( c64cp )
//-------------------------------------------------

#define rom_c64cp       rom_c64c


//-------------------------------------------------
//  ROM( c64g )
//-------------------------------------------------

#define rom_c64g        rom_c64c


//-------------------------------------------------
//  ROM( c64c_es )
//-------------------------------------------------

ROM_START( c64c_es )
	ROM_REGION( 0x4000, "kernal", 0 )
	ROM_LOAD( "251913-01.u4", 0x0000, 0x4000, CRC(0010ec31) SHA1(765372a0e16cbb0adf23a07b80f6b682b39fbf88) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "325056-03.u5", 0x0000, 0x1000, CRC(c890c175) SHA1(4f57259fff9ef1963a4e87165a6f35ca23864c76) ) // aka 325245-01

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "252715-01.u8", 0x00, 0xf5, BAD_DUMP CRC(54c89351) SHA1(efb315f560b6f72444b8f0b2ca4b0ccbcd144a1b) )
ROM_END


//-------------------------------------------------
//  ROM( c64c_se )
//-------------------------------------------------

ROM_START( c64c_se )
	ROM_REGION( 0x4000, "kernal", 0 )
	ROM_LOAD( "325182-01.u4", 0x0000, 0x4000, CRC(2aff27d3) SHA1(267654823c4fdf2167050f41faa118218d2569ce) ) // 128/64 FI

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "cbm 64 skand.gen.u5", 0x0000, 0x1000, CRC(377a382b) SHA1(20df25e0ba1c88f31689c1521397c96968967fac) )

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "252715-01.u8", 0x00, 0xf5, BAD_DUMP CRC(54c89351) SHA1(efb315f560b6f72444b8f0b2ca4b0ccbcd144a1b) )
ROM_END


//-------------------------------------------------
//  ROM( c64gs )
//-------------------------------------------------

ROM_START( c64gs )
	ROM_REGION( 0x4000, "kernal", 0 )
	ROM_LOAD( "390852-01.u4", 0x0000, 0x4000, CRC(b0a9c2da) SHA1(21940ef5f1bfe67d7537164f7ca130a1095b067a) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901225-01.u5", 0x0000, 0x1000, CRC(ec4272ee) SHA1(adc7c31e18c7c7413d54802ef2f4193da14711aa) )

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "252535-01.u8", 0x00, 0xf5, BAD_DUMP CRC(54c89351) SHA1(efb315f560b6f72444b8f0b2ca4b0ccbcd144a1b) )
ROM_END


//-------------------------------------------------
//  ROM( clipper )
//-------------------------------------------------

ROM_START( clipper )
	ROM_REGION( 0x2000, "kernal", 0 )
	ROM_LOAD( "kernal.bin", 0x0000, 0x2000, CRC(13ca39ca) SHA1(d668e7980887a5b90fad693eba35fac49c7ad941) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "chr_gen.bin", 0x0000, 0x1000, CRC(a675a239) SHA1(9ad11a5de5bd7e43c43e985b31bed7ca96101fc5) )

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "906114-01.u17", 0x00, 0xf5, CRC(54c89351) SHA1(efb315f560b6f72444b8f0b2ca4b0ccbcd144a1b) )

	ROM_REGION( 0x4000, "fdc", 0 )
	ROM_LOAD( "fdc.bin", 0x0000, 0x2000, CRC(44b0b1fc) SHA1(effcf165cb4ea32540a8a8c12781303dc36fa4b2) )
	ROM_LOAD( "fdc_12.bin", 0x2000, 0x2000, CRC(397a2219) SHA1(7eefcc871a805f45be4ba016fe9fc7d25318c431) )

	ROM_REGION( 0x6000, "sb", 0 )
	ROM_LOAD( "sb1.bin", 0x0000, 0x2000, CRC(400040be) SHA1(b290216f49b24355a1a2b25adfa96709c5d9c049) )
	ROM_LOAD( "sb2.bin", 0x2000, 0x2000, CRC(a3d7177a) SHA1(0f50381aecf3c5ea03cce358a3325b3e06939c37) )
	ROM_LOAD( "sb3.bin", 0x4000, 0x2000, CRC(7b1fc6c6) SHA1(900fe4be8d6348bf68dbda0c7ecefc84bda51202) )

	ROM_REGION( 0x1000, "thdr", 0 )
	ROM_LOAD( "thdr5.bin", 0x0000, 0x1000, CRC(b4296e62) SHA1(4b6edadbb810c409ece77d5834568fcc2e0bbd61) )
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT    CLASS         INIT        COMPANY                        FULLNAME                                   FLAGS
COMP( 1982, c64,      0,      0,      ntsc,    c64,     c64_state,     empty_init, "Commodore Business Machines", "Commodore 64 (NTSC)",                     MACHINE_SUPPORTS_SAVE )
COMP( 1982, c64_jp,   c64,    0,      ntsc,    c64,     c64_state,     empty_init, "Commodore Business Machines", "Commodore 64 (Japan)",                    MACHINE_SUPPORTS_SAVE )
COMP( 1982, c64p,     c64,    0,      pal,     c64,     c64_state,     empty_init, "Commodore Business Machines", "Commodore 64 (PAL)",                      MACHINE_SUPPORTS_SAVE )
COMP( 1982, c64_se,   c64,    0,      pal,     c64sw,   c64_state,     empty_init, "Commodore Business Machines", "Commodore 64 / VIC-64S (Sweden/Finland)", MACHINE_SUPPORTS_SAVE )
COMP( 1983, pet64,    c64,    0,      pet64,   c64,     c64_state,     empty_init, "Commodore Business Machines", "PET 64 / CBM 4064 (NTSC)",                MACHINE_SUPPORTS_SAVE | MACHINE_WRONG_COLORS )
COMP( 1983, edu64,    c64,    0,      pet64,   c64,     c64_state,     empty_init, "Commodore Business Machines", "Educator 64 (NTSC)",                      MACHINE_SUPPORTS_SAVE | MACHINE_WRONG_COLORS )
COMP( 1984, sx64,     c64,    0,      ntsc_sx, c64,     sx64_state,    empty_init, "Commodore Business Machines", "SX-64 / Executive 64 (NTSC)",             MACHINE_SUPPORTS_SAVE )
COMP( 1984, sx64p,    c64,    0,      pal_sx,  c64,     sx64_state,    empty_init, "Commodore Business Machines", "SX-64 / Executive 64 (PAL)",              MACHINE_SUPPORTS_SAVE )
COMP( 1984, vip64,    c64,    0,      pal_sx,  c64sw,   sx64_state,    empty_init, "Commodore Business Machines", "VIP-64 (Sweden/Finland)",                 MACHINE_SUPPORTS_SAVE )
COMP( 1984, dx64,     c64,    0,      ntsc_dx, c64,     sx64_state,    empty_init, "Commodore Business Machines", "DX-64 (NTSC)",                            MACHINE_SUPPORTS_SAVE )
COMP( 1984, tesa6240, c64,    0,      pal_sx,  c64,     sx64_state,    empty_init, "Tesa Etikett",                "Etikettendrucker 6240",                   MACHINE_SUPPORTS_SAVE )
COMP( 1984, clipper,  c64,    0,      clipper, clipper, clipper_state, empty_init, "Professional Data Computer",  "Clipper",                                 MACHINE_NOT_WORKING )
COMP( 1986, c64c,     c64,    0,      ntsc_c,  c64,     c64c_state,    empty_init, "Commodore Business Machines", "Commodore 64C (NTSC)",                    MACHINE_SUPPORTS_SAVE )
COMP( 1986, c64cp,    c64,    0,      pal_c,   c64,     c64c_state,    empty_init, "Commodore Business Machines", "Commodore 64C (PAL)",                     MACHINE_SUPPORTS_SAVE )
COMP( 1988, c64c_es,  c64,    0,      pal_c,   c64sw,   c64c_state,    empty_init, "Commodore Business Machines", "Commodore 64C (Spain)",                   MACHINE_SUPPORTS_SAVE )
COMP( 1986, c64c_se,  c64,    0,      pal_c,   c64sw,   c64c_state,    empty_init, "Commodore Business Machines", "Commodore 64C (Sweden/Finland)",          MACHINE_SUPPORTS_SAVE )
COMP( 1986, c64g,     c64,    0,      pal_c,   c64,     c64c_state,    empty_init, "Commodore Business Machines", "Commodore 64G (PAL)",                     MACHINE_SUPPORTS_SAVE )
CONS( 1990, c64gs,    c64,    0,      pal_gs,  c64gs,   c64gs_state,   empty_init, "Commodore Business Machines", "Commodore 64 Games System (PAL)",         MACHINE_SUPPORTS_SAVE )




c64dtv.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*************************************************************************************************************

Commodore 64 Direct-to-TV
It looks like a fat joystick.

Chips: All unmarked. According to Wikipedia an ASIC contains an entire
       Commodore 64 (6510 CPU, VIC-II, SID, CIA, PLA) running at 32MHz. The
       6510 CPU portion runs at 1MHz.
Crystals: 32.720 (X1), unmarked (X2).

*************************************************************************************************************/
#include "emu.h"
#include "screen.h"


namespace {

//**************************************************************************
//  DRIVER STATE
//**************************************************************************

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

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
	{
		bitmap.fill(rgb_t::black(), cliprect);
		return 0;
	}
	void c64dtv(machine_config &config);
};



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

static INPUT_PORTS_START( c64dtv )
INPUT_PORTS_END



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

void c64dtv_state::c64dtv(machine_config &config)
{
	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_screen_update(FUNC(c64dtv_state::screen_update));
	screen.set_size(640,480);
	screen.set_visarea_full();
	screen.set_refresh_hz(30);
}



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

// BASIC sits at 0xa000-0xc000, chargen-like chunks sit at 0x1000-0x2000, 0x9000-0xa000 and 0xd000-0xe000
// kernel sits at 0xe000
// from 0x10000 on there are the games
ROM_START( c64dtv )
	ROM_REGION( 0x200000, "asic", 0 )
	ROM_LOAD( "flash.u2", 0x000000, 0x200000, CRC(b820375a) SHA1(b9f88919e2bed825eb2b2cb605977d55971b423b) )
ROM_END

} // anonymous namespace


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

CONS( 2005, c64dtv, 0, 0, c64dtv, c64dtv, c64dtv_state, empty_init, "The Toy:Lobster Company", "Commodore 64 Direct-to-TV (Version 2 050711) (PAL)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



c65.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Angelo Salese
/**************************************************************************************************

C=65 / C=64DX (c) 1991 Commodore

TODO:
- DDR/port support from M4510;
- Complete memory model;
\- rom8 / roma / rome all causes bootstrap issues if hooked up (needs the CPU DDR port?)
\- Work RAM should really use a memory_share_creator, VIC-III can potentially access the full range.
\- CRAM is really just RAM that bitplane mode can access as-is;
- Complete interrupts;
- F011 FDC (1581 equivalent, does it implies IEC?);
- bios 0 detects an expansion RAM without checking REC first and no matter if there's
  effectively a RAM bank available or not, supposed to bus error or just buggy/hardwired code?

Notes:
- C=64 mode can be entered in two ways: with "GO64" at BASIC prompt, or by holding C= key during
boot;
- C=65 mode from C=64 BASIC is possible, as long as you do some pre-setup such as setting KEY in
VIC-III mode and disable CIA irqs;

===================================================================================================
References:

Hardware infos can be found at:
http://www.zimmers.net/cbmpics/cbm/c65/c65manual.txt
http://www.zimmers.net/cbmpics/cbm/c65/c65faq20.txt

Hardware pics:
http://www.zimmers.net/cbmpics/cbm/c65/c65-2b-lhs.JPG
http://www.zimmers.net/cbmpics/cbm/c65/c65-2b-rhs.JPG

Schematics:
http://www.zimmers.net/anonftp/pub/cbm/schematics/computers/C65%20Rev%202A%20Schematic.pdf
http://www.zimmers.net/anonftp/pub/cbm/schematics/computers/C64DX_aka_C65_System_Specifications_Preliminary_(1991_Mar).pdf

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

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "bus/vcs_ctrl/ctrl.h"
#include "cpu/m6502/m4510.h"
#include "machine/input_merger.h"
#include "machine/mos6526.h"
#include "sound/mos6581.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#define MAIN_C65_CLOCK XTAL(28'375'160) / 8
#define MAIN_C64_CLOCK XTAL(14'318'181) / 14 // TODO: unconfirmed

// TODO: move to own file, subclass with f018a and f018b
// using device_execute_interface will tank performance, avoid it for now.
class dmagic_f018_device : public device_t
{
public:
	// construction/destruction
	dmagic_f018_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	template <class T> void set_space(T &&tag, int spacenum) { m_space.set_tag(std::forward<T>(tag), spacenum); }

	void map(address_map &map) ATTR_COLD;

protected:
	virtual void device_start() override ATTR_COLD;
	virtual void device_reset() override ATTR_COLD;

private:
	enum dma_state_t : u8 {
		COPY,
		MIX,
		SWAP,
		FILL,
		IDLE,
		FETCH_PARAMS
	};

	required_address_space m_space;

	u32 m_dmalist_address = 0;

	dma_state_t m_state;
	u32 m_src, m_dst, m_length, m_command, m_modulo;
	u8 m_src_mode, m_dst_mode;
	bool m_chained_transfer;

	emu_timer *m_dma_timer;

	TIMER_CALLBACK_MEMBER(execute_cb);

	void check_state(int next_cycles);
	void increment_src();
	void increment_dst();
};

ALLOW_SAVE_TYPE(dmagic_f018_device::dma_state_t)

// CSG 390957-01
DEFINE_DEVICE_TYPE(DMAGIC_F018, dmagic_f018_device, "dmagic_f018", "DMAgic F018 Gate Array")

dmagic_f018_device::dmagic_f018_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, DMAGIC_F018, tag, owner, clock)
	, m_space(*this, finder_base::DUMMY_TAG, -1)
{
}

void dmagic_f018_device::device_start()
{
	save_item(NAME(m_dmalist_address));
	save_item(NAME(m_state));

	m_dma_timer = timer_alloc(FUNC(dmagic_f018_device::execute_cb), this);
}

void dmagic_f018_device::device_reset()
{
	m_dma_timer->adjust(attotime::never);
	m_state = dma_state_t::IDLE;
	m_chained_transfer = false;
	m_dmalist_address = 0;
}

void dmagic_f018_device::check_state(int next_cycles)
{
	if (m_length != 0)
		m_dma_timer->adjust(attotime::from_ticks((next_cycles), clock()));
	else
	{
		m_chained_transfer = bool(BIT(m_command, 2));
		if (m_chained_transfer)
			popmessage("DMAgic: untested Chain Mode");
		m_state = m_chained_transfer ? FETCH_PARAMS : IDLE;
		m_dma_timer->adjust(m_chained_transfer ? attotime::from_ticks(12, clock()) : attotime::never);
	}
}

void dmagic_f018_device::increment_src()
{
	const int src_dir = BIT(m_src_mode, 2) ? -1 : +1;
	m_src = ((m_src + src_dir) & 0xffff) | (m_src & 0xf'0000);
}

void dmagic_f018_device::increment_dst()
{
	const int dst_dir = BIT(m_dst_mode, 2) ? -1 : +1;
	m_dst = ((m_dst + dst_dir) & 0xffff) | (m_dst & 0xf'0000);
}


TIMER_CALLBACK_MEMBER(dmagic_f018_device::execute_cb)
{
	switch(m_state)
	{
		case dma_state_t::IDLE:
			return;

		case dma_state_t::FETCH_PARAMS: {
			static const char *const dma_cmd_string[] =
			{
				"COPY",
				"MIX",
				"SWAP",
				"FILL"
			};

			u32 address = m_dmalist_address;
			logerror("%05x ", address);
			m_command = m_space->read_byte(address++);

			m_length = m_space->read_byte(address++);
			m_length |=(m_space->read_byte(address++)<<8);

			if (m_length == 0)
				m_length = 0x10000;

			m_src = m_space->read_byte(address++);
			m_src |=(m_space->read_byte(address++)<<8);
			m_src |=(m_space->read_byte(address++)<<16);

			m_dst = m_space->read_byte(address++);
			m_dst |=(m_space->read_byte(address++)<<8);
			m_dst |=(m_space->read_byte(address++)<<16);

			m_modulo = m_space->read_byte(address++);
			m_modulo |=(m_space->read_byte(address++)<<8);

			m_dmalist_address = address;
			m_state = (dma_state_t)(m_command & 3);

			m_src_mode = m_src >> 20;
			m_dst_mode = m_dst >> 20;

			logerror("DMAgic %s [%06x] -> [%06x] length = %04x modulo = %02x (CHAIN=%s)\n"
				, dma_cmd_string[m_command & 3]
				, m_src
				, m_dst
				, m_length
				, m_modulo
				, m_command & 4 ? "yes" : "no"
			);

			if (m_command & 0xf8)
				popmessage("DMAgic: unhandled command %02x", m_command);

			// x--- I/O select
			// -x-- DIR
			//      \- decrements instead of increment, used by "LIST"
			// --x- MOD
			// ---x HOLD
			if ((m_src_mode & 0xb || m_dst_mode & 0xb) && m_length != 1)
			{
				popmessage("DMAgic: unhandled source %06x dst %06x length %05x", m_src, m_dst, m_length);
			}

			m_dma_timer->adjust(attotime::from_ticks((1), clock()));
			break;
		}

		// TODO: very limited use by the BIOS, not extensively tested
		// generally uses length == 1 (for copying BASIC strings from work RAM)
		// or length == 50 (for scrolling vertical text, cfr. c64dx)
		case dma_state_t::COPY: {
			m_space->write_byte(m_dst & 0xf'ffff, m_space->read_byte(m_src & 0xf'ffff));
			increment_src();
			increment_dst();
			m_length --;
			check_state(2);
			break;
		}

		case dma_state_t::MIX:
			popmessage("DMAgic: unimplemented MIX");
			break;

		case dma_state_t::SWAP: {
			popmessage("DMAgic: untested SWAP");
			const u8 tmp_src = m_space->read_byte(m_src & 0xf'ffff);
			const u8 tmp_dst = m_space->read_byte(m_dst & 0xf'ffff);
			m_space->write_byte(m_src & 0xf'ffff, tmp_dst);
			m_space->write_byte(m_dst & 0xf'ffff, tmp_src);
			increment_src();
			increment_dst();
			m_length --;
			check_state(4);
			break;
		}

		case dma_state_t::FILL:
			m_space->write_byte(m_dst & 0xf'ffff, m_src & 0xff);
			increment_dst();
			m_length --;
			check_state(1);
			break;
	}
}


void dmagic_f018_device::map(address_map &map)
{
	map(0, 0).lw8(
		NAME([this] (offs_t offset, u8 data) {
			m_dmalist_address &= 0xffff00;
			m_dmalist_address |= data;
			m_state = FETCH_PARAMS;
			m_dma_timer->adjust(attotime::from_ticks((12), clock()));
		})
	);
	map(1, 1).lw8(
		NAME([this] (offs_t offset, u8 data) {
			m_dmalist_address &= 0xff00ff;
			m_dmalist_address |= data << 8;
		})
	);
	map(2, 2).lw8(
		NAME([this] (offs_t offset, u8 data) {
			m_dmalist_address &= 0x00ffff;
			m_dmalist_address |= data << 16;
		})
	);
	map(3, 3).lr8(
		NAME([this] (offs_t offset) {
			return (m_state != IDLE) << 7 | (m_chained_transfer << 0);
		})
	);
}

namespace {

class c65_state : public driver_device
{
public:
	c65_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cia(*this, "cia_%u", 0U)
		, m_sid(*this, "sid_%u", 0U)
		, m_irqs(*this, "irqs")
		, m_nmis(*this, "nmis")
		, m_dma(*this, "dma")
		, m_joy(*this, "joy%u", 1U)
		, m_cram_view(*this, "cram_view")
		//, m_rom8_view(*this, "rom8_view")
		//, m_roma_view(*this, "roma_view")
		, m_romc_view(*this, "romc_view")
		//, m_rome_view(*this, "rome_view")
		//, m_loram_view(*this, "loram_view")
		//, m_hiram_view(*this, "hiram_view")
		, m_charen_view(*this, "charen_view")
		, m_screen(*this, "screen")
		, m_palette(*this, "palette")
		, m_workram(*this, "work_ram", 0x20000, ENDIANNESS_LITTLE)
		, m_palred(*this, "redpal")
		, m_palgreen(*this, "greenpal")
		, m_palblue(*this, "bluepal")
		, m_cram(*this, "cram")
		, m_gfxdecode(*this, "gfxdecode")
		, m_ipl_rom(*this, "ipl")
		, m_cart_exp(*this, "cart_exp")
		, m_exrom_view(*this, "exrom_view")
	{ }

	void init_c65();
	void init_c65pal();

	void c65(machine_config &config);

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual void video_start() override ATTR_COLD;
	virtual void video_reset() override ATTR_COLD;
private:
	required_device<m4510_device> m_maincpu;
	required_device_array<mos6526_device, 2> m_cia;
	required_device_array<mos6581_device, 2> m_sid;
	required_device<input_merger_device> m_irqs;
	required_device<input_merger_device> m_nmis;
	required_device<dmagic_f018_device> m_dma;
	required_device_array<vcs_control_port_device, 2> m_joy;

	memory_view m_cram_view;
	//memory_view m_rom8_view;
	//memory_view m_roma_view;
	memory_view m_romc_view;
	//memory_view m_rome_view;
	//memory_view m_loram_view;
	//memory_view m_hiram_view;
	memory_view m_charen_view;

	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	memory_share_creator<uint8_t> m_workram;
	required_shared_ptr<uint8_t> m_palred;
	required_shared_ptr<uint8_t> m_palgreen;
	required_shared_ptr<uint8_t> m_palblue;
	required_shared_ptr<uint8_t> m_cram;
	required_device<gfxdecode_device> m_gfxdecode;
	required_memory_region m_ipl_rom;
	required_device<generic_slot_device> m_cart_exp;
	memory_view m_exrom_view;

	uint8_t m_keyb_input[10]{};
	uint8_t m_keyb_c0_c7 = 0U;
	uint8_t m_keyb_c8_c9 = 0U;

	void vic4567_map(address_map &map) ATTR_COLD;
	void palette_red_w(offs_t offset, uint8_t data);
	void palette_green_w(offs_t offset, uint8_t data);
	void palette_blue_w(offs_t offset, uint8_t data);
	uint8_t uart_r(offs_t offset);
	void uart_w(offs_t offset, uint8_t data);
	uint8_t cia0_porta_r();
	void cia0_porta_w(uint8_t data);
	uint8_t cia0_portb_r();
	void cia0_portb_w(uint8_t data);
	void cia1_porta_w(uint8_t data);
	uint8_t cpu_r();
	void cpu_w(uint8_t data);

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void palette_init(palette_device &palette);

	void c65_map(address_map &map) ATTR_COLD;

	void irq_check(uint8_t irq_cause);

	template <unsigned StartBase> uint8_t ram_r(offs_t offset)
	{
		return m_workram[offset + StartBase];
	}

	template <unsigned StartBase> void ram_w(offs_t offset, uint8_t data)
	{
		m_workram[offset + StartBase] = data;
	}

	u16 m_vic_bank_base = 0U;

	// TODO: move to own device
	uint8_t m_irq_pending = 0U, m_irq_mask = 0U;
	uint8_t m_border_color = 0U;
	uint8_t m_vs_cb_base = 0U;
	uint8_t m_bk_color_clut[4]{};
	uint8_t m_sprite_enable = 0U;
	uint8_t m_control_a = 0U;
	uint8_t m_control_b = 0U;
	u16 m_rcr = 0xffU;
	bool m_ecm = false;
	bool m_bmm = false;
	bool m_blnk = false;
	u8 m_yscl = 0U;
	u8 m_xscl = 0U;
	bool m_csel = false;
	bool m_mcm = false;
	u8 m_ssc = 0U;
	u8 m_sbc = 0U;
	// handler helpers
	u16 m_vs_base_offset = 0U;
	u16 m_cb_base_offset = 0U;
	u8 m_gfxmode = 0U;
	u8* m_video_ptr = nullptr;
	u8* m_char_ptr = nullptr;

	// TODO: tracer bullet
	// this pointer fetch should happen as a bridge between VIC device and memory controller.
	void flush_cb_base()
	{
		m_video_ptr = &m_workram[m_vs_base_offset | m_vic_bank_base];
		// In bitmap modes only bit 3 has weight for CB base, cfr. isoccer
		if (m_gfxmode & 2)
		{
			m_char_ptr = &m_workram[(m_cb_base_offset & 0x2000) | m_vic_bank_base];
		}
		else
		{
			if ((m_cb_base_offset & 0x3000) == 0x1000 && !(BIT(m_vic_bank_base, 14)))
			{
				m_char_ptr = &m_ipl_rom->base()[((BIT(m_control_a, 6)) ? 0x9000 : 0xd000) + (m_cb_base_offset & 0x800)];
			}
			else
				m_char_ptr = &m_workram[m_cb_base_offset | m_vic_bank_base];
		}
	};
	struct {
		u16 x;
		u8 y;
		u8 clut;
	}m_sprite[16];
	u8 m_sprite_hi_xoffs;
	u8 m_sexx, m_sexy;
	u8 m_scm;
	u8 m_sprite_multicolor_clut[2];
	u8 m_bsp;

	bool m_blink_enable = false;
	bitmap_ind16 m_bitmap;
	emu_timer *m_scanline_timer;
	TIMER_CALLBACK_MEMBER(scanline_cb);
	std::tuple<u8, bool> get_tile_pixel(int y, int x);
	std::tuple<u8, u8, u8, bool> get_sprite_pixel(int y, int x);
	typedef std::tuple<u8, bool> (c65_state::*draw_tile_func)(u8 tile, u8 attr, int yi, int xi, int pixel_width);
	static const draw_tile_func draw_tile_table[8];
	std::tuple<u8, bool> draw_standard_char(u8 tile, u8 attr, int yi, int xi, int pixel_width);
	std::tuple<u8, bool> draw_multicolor_char(u8 tile, u8 attr, int yi, int xi, int pixel_width);
	std::tuple<u8, bool> draw_standard_bitmap(u8 tile, u8 attr, int yi, int xi, int pixel_width);
	std::tuple<u8, bool> draw_multicolor_bitmap(u8 tile, u8 attr, int yi, int xi, int pixel_width);
	std::tuple<u8, bool> draw_extended_background(u8 tile, u8 attr, int yi, int xi, int pixel_width);
	std::tuple<u8, bool> draw_invalid(u8 tile, u8 attr, int yi, int xi, int pixel_width);

	void palette_entry_flush(uint8_t offset);
};

// TODO: c65 bitplane mode
const c65_state::draw_tile_func c65_state::draw_tile_table[8] =
{
	&c65_state::draw_standard_char,
	&c65_state::draw_multicolor_char,
	&c65_state::draw_standard_bitmap,
	&c65_state::draw_multicolor_bitmap,
	&c65_state::draw_extended_background,
	&c65_state::draw_invalid,
	&c65_state::draw_invalid,
	&c65_state::draw_invalid
};


void c65_state::video_start()
{
	m_screen->register_screen_bitmap(m_bitmap);
	m_scanline_timer = timer_alloc(FUNC(c65_state::scanline_cb), this);
}

void c65_state::video_reset()
{
	m_scanline_timer->adjust(m_screen->time_until_pos(m_screen->vpos() + 1, 0), m_screen->vpos() + 1);
	m_blnk = false;
	// vestigial, so to have a pointer in any case
	flush_cb_base();
}

void c65_state::vic4567_map(address_map &map)
{
//  53248/$d000 - 53263/$d00f S#X - S#Y Sprite X/Y
	map(0x00, 0x00).select(0xe).lrw8(
		NAME([this] (offs_t offset){
			return m_sprite[offset >> 1].x & 0xff;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_sprite[offset >> 1].x &= 0x100;
			m_sprite[offset >> 1].x |= data;
		})
	);
	map(0x01, 0x01).select(0xe).lrw8(
		NAME([this] (offs_t offset){
			return m_sprite[offset >> 1].y;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_sprite[offset >> 1].y = data;
		})
	);
//  53264/$d010 S#X8 bit 8 for Sprites X pos
	map(0x10, 0x10).lrw8(
		NAME([this] (offs_t offset) {
			return m_sprite_hi_xoffs;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_sprite_hi_xoffs = data;
			for (int i = 0; i < 8; i++)
			{
				m_sprite[i].x &= 0xff;
				m_sprite[i].x |= BIT(data, i) << 8;
			}
		})
	);
/*
 * 53265/$d011
 * x--- ---- RC8 bit 8 of beam V
 * -x-- ---- ECM Extended Color Mode
 * --x- ---- BMM Bitmap Mode
 * ---x ---- BLNK Enable video output
 * ---- x--- RSEL 25/24 visible rows
 * ---- -xxx YSCL2-0 Screen Soft V Scroll
 */
	map(0x11, 0x11).lrw8(
		NAME([this] (offs_t offset) {
			return ((m_screen->vpos() & 0x100) >> 1) | (m_ecm << 6) | (m_bmm << 5) | (m_blnk << 4) | (m_yscl & 7);
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_rcr = ((data & 0x80) << 1) | (m_rcr & 0xff);
			m_ecm = bool(BIT(data, 6));
			m_bmm = bool(BIT(data, 5));
			m_blnk = bool(BIT(data, 4));
			m_yscl = data & 7;
			logerror("VIC2: 53265 mode %02x\n", data);
			m_gfxmode = (m_ecm << 2) | (m_bmm << 1) | (m_mcm << 0);
			flush_cb_base();
		})
	);
//  53266/$d012 RC Raster CouNT
	map(0x12, 0x12).lrw8(
		NAME([this] (offs_t offset) {
			return (m_screen->vpos() & 0xff);
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_rcr = data | (m_rcr & 0x100);
		})
	);
//  map(0x13, 0x13) 53267/$d013 LPX lightpen X
//  map(0x14, 0x14) 53268/$d014 LPY lightpen Y
	// 53269/$d015 SE# Sprite Enable
	map(0x15, 0x15).lrw8(
		NAME([this] (offs_t offset) {
			return m_sprite_enable;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_sprite_enable = data;
		})
	);
/*
 *  53270/$d016
 * --x- ---- RST <unknown>, in c65 specs only?
 * ---x ---- MCM [Background] Multicolor Mode
 * ---- x--- CSEL 40/38 visible columns
 * ---- -xxx XSCL2-0 Screen Soft Scroll H
 */
	map(0x16, 0x16).lrw8(
		NAME([this] (offs_t offset) {
			return (m_mcm << 4) | (m_csel << 3) | (m_xscl & 7);
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_mcm = bool(BIT(data, 4));
			m_csel = bool(BIT(data, 3));
			m_xscl = data & 7;
			m_gfxmode = (m_ecm << 2) | (m_bmm << 1) | (m_mcm << 0);
			flush_cb_base();
		})
	);
//  53271/$d017 SEXY# Sprite magnify V
	map(0x17, 0x17).lrw8(
		NAME([this] (offs_t offset) {
			return m_sexy;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_sexy = data;
		})
	);
/*
 * 53272/$d018
 * xxxx ---- Screen RAM base (note bit 4 ignored in C=65 width 80)
 * ---- xxx- Character Set base
 */
	map(0x18, 0x18).lrw8(
		NAME([this] (offs_t offset) {
			return m_vs_cb_base;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_vs_cb_base = data;
			m_vs_base_offset = (m_vs_cb_base & 0xf0) << 6;
			m_cb_base_offset = (m_vs_cb_base & 0x0e) << 10;
			flush_cb_base();
		})
	);
/*
 * x--- ---- Latches high if any IRQ taken below (valid for pending reg only)
 * ---- x--- Lightpen input IRQ
 * ---- -x-- Sprite-Sprite collision IRQ
 * ---- --x- Sprite-Background collision IRQ
 * ---- ---x rasterline IRQ
 */
	map(0x19, 0x19).lrw8(
		NAME([this] (offs_t offset) {
			return m_irq_pending;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_irq_pending &= ~data;
			irq_check(0);
		})
	);
	map(0x1a, 0x1a).lrw8(
		NAME([this] (offs_t offset) {
			return m_irq_mask;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_irq_mask = data & 0xf;
			irq_check(0);
		})
	);
//  53275/$d01b BSP# Sprite-Background priority
	map(0x1b, 0x1b).lrw8(
		NAME([this] (offs_t offset) {
			return m_bsp;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_bsp = data;
		})
	);
//  53276/$d01c SCM# Sprite multicolor enable
	map(0x1c, 0x1c).lrw8(
		NAME([this] (offs_t offset) {
			return m_scm;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_scm = data;
		})
	);
//  53277/$d01d Sprite magnify X
	map(0x1d, 0x1d).lrw8(
		NAME([this] (offs_t offset) {
			return m_sexx;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_sexx = data;
		})
	);
//  53278/$d01e Sprite-Sprite collision
	map(0x1e, 0x1e).lr8(
		NAME([this] (offs_t offset) {
			u8 res = m_ssc;
			if (!machine().side_effects_disabled())
				m_ssc = 0;
			return res;
		})
	);
//  53279/$d01f Sprite-background collision
	map(0x1f, 0x1f).lr8(
		NAME([this] (offs_t offset) {
			u8 res = m_sbc;
			if (!machine().side_effects_disabled())
				m_sbc = 0;
			return res;
		})
	);
//  53280/$d020 BORD border color
	map(0x20, 0x20).lrw8(
		NAME([this] (offs_t offset) {
			return m_border_color;
		}),
		NAME([this] (offs_t offset, u8 data) {
			// TODO: all 8-bits in C=65 mode
			m_border_color = data & 0xf;
			//m_screen->update_partial(m_screen->vpos());
		})
	);
//  53281/$d021 BK#C background clut BK0-BK3
	map(0x21, 0x24).lrw8(
		NAME([this] (offs_t offset) {
			return m_bk_color_clut[offset];
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_bk_color_clut[offset] = data & 0xf;
		})
	);
//  53285/$d025 - 53286/$d026 sprite multicolor clut
	map(0x25, 0x26).lrw8(
		NAME([this] (offs_t offset) {
			return m_sprite_multicolor_clut[offset];
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_sprite_multicolor_clut[offset] = data & 0xf;
		})
	);
//  53287/$d027 - 53294/$d02e sprite color clut
	map(0x27, 0x2e).lrw8(
		NAME([this] (offs_t offset) {
			return m_sprite[offset].clut;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_sprite[offset].clut = data & 0xf;
		})
	);
/*
 * KEY register, handles vic-iii and vic-ii modes via two consecutive writes
 * 0xa5 -> 0x96 vic-iii mode
 * any other write vic-ii mode (changes base memory map)
 * vic-iv (MEGA65/C65GS) also has another KEY init sequence (0x47 -> 0x53)
 */
//  map(0x2f, 0x2f)
/*
 * x--- ---- overlay ROM at $e000
 * -x-- ---- Move CROM at $9000 (C=65), $d000 otherwise (C=64)
 * --x- ---- overlay ROM at $c000
 * ---x ---- overlay ROM at $a000
 * ---- x--- overlay ROM at $8000
 * ---- -x-- read PALette from [P]ROM
 * ---- --x- EXT SYNC
 * ---- ---x overlay CRAM at $dc00
 */
	map(0x30, 0x30).lrw8(
		NAME([this] (offs_t offset) {
			return m_control_a;
		}),
		NAME([this] (offs_t offset, u8 data) {
			if((data & 0xfe) != 0x64)
				logerror("CONTROL A %02x\n",data);
			m_control_a = data;
			// TODO: all the other bits
			m_cram_view.select(BIT(data, 0));
			//m_rom8_view.select(BIT(data, 3));
			//m_roma_view.select(BIT(data, 4));
			m_romc_view.select(BIT(data, 5));
			flush_cb_base();
			//m_rome_view.select(BIT(data, 7));
			logerror("\tROM @ 8000 %d\n", BIT(data, 3));
			logerror("\tROM @ a000 %d\n", BIT(data, 4));
			logerror("\tROM @ c000 %d\n", BIT(data, 5));
			logerror("\tROM @ e000 %d\n", BIT(data, 7));
		})
	);
/*
 * x--- ---- H640
 * -x-- ---- FAST
 * --x- ---- ATTR
 * ---x ---- BPM Bitplane Mode
 * ---- x--- V400
 * ---- -x-- H1280
 * ---- --x- MONO
 * ---- ---x INT
 */
	map(0x31, 0x31).lrw8(
		NAME([this] (offs_t offset) {
			return m_control_b;
		}),
		NAME([this] (offs_t offset, u8 data) {
			logerror("CONTROL B %02x\n", data);
			m_control_b = data;
			// FAST mode
			const XTAL clock = BIT(data, 6) ? MAIN_C65_CLOCK : MAIN_C64_CLOCK;
			m_maincpu->set_unscaled_clock(clock);
			m_cia[0]->set_unscaled_clock(clock);
			m_cia[1]->set_unscaled_clock(clock);
		})
	);
//  map(0x32, 0x32) Bitplane enable
//  map(0x33, 0x3a) Bitplane addresses
//  map(0x3b, 0x3b) BP COMP
//  map(0x3c, 0x3c) Bitplane X
//  map(0x3d, 0x3d) Bitplane Y
//  map(0x3e, 0x3e) Horizontal [screen?] position
//  map(0x3f, 0x3f) Vertical [screen?] position
//  map(0x40, 0x47) DAT Bitplane ports
}

std::tuple<u8, bool> c65_state::draw_standard_char(u8 tile, u8 attr, int yi, int xi, int pixel_width)
{
	const int xm = 7 - ((xi / pixel_width) & 7);
	const int ym = (yi & 7);
	const int foreground_color = attr & 0xf;
	const int background_color = m_bk_color_clut[0] & 0xf;
	u8 highlight_color = 0;

	u8 enable_dot = ((m_char_ptr[((tile << 3) + ym) & 0x3fff] >> xm) & 1);
	if ((attr & 0x80) && ym == 7) enable_dot = 1;
	if (attr & 0x40) highlight_color = 16;
	if (attr & 0x20) enable_dot = !enable_dot;
	if (attr & 0x10 && !m_blink_enable) enable_dot = 0;

	return std::make_tuple(highlight_color + ((enable_dot) ? foreground_color : background_color), enable_dot != 0);
}

std::tuple<u8, bool> c65_state::draw_multicolor_char(u8 tile, u8 attr, int yi, int xi, int pixel_width)
{
	if (!(attr & 8))
		return draw_standard_char(tile, attr, yi, xi, pixel_width);
	const int xm = 6 - ((xi / pixel_width) & 6);
	const int ym = (yi & 7);
	const u8 color11 = attr & 0xf;
	const std::array<u8, 4> color_map = { m_bk_color_clut[0], m_bk_color_clut[1], m_bk_color_clut[2], color11 };

	u8 highlight_color = 0;

	u8 enable_dot = ((m_char_ptr[((tile << 3) + ym) & 0x3fff] >> xm) & 3);
	if ((attr & 0x80) && ym == 7) enable_dot = 1;
	if (attr & 0x40) highlight_color = 16;
	if (attr & 0x20) enable_dot = !enable_dot;
	if (attr & 0x10 && !m_blink_enable) enable_dot = 0;

	return std::make_tuple(highlight_color + color_map[enable_dot], enable_dot & 2);
}

std::tuple<u8, bool> c65_state::draw_standard_bitmap(u8 tile, u8 attr, int yi, int xi, int pixel_width)
{
	const int xm = 7 - ((xi / pixel_width) & 7);
	const int ym = (yi & 7);
	const int foreground_color = tile >> 4;
	const int background_color = tile & 0xf;
	u8 highlight_color = 0;

	u8 enable_dot = ((m_char_ptr[((xi >> (3 + (pixel_width - 1))) * 8  + (yi >> 3) * 320 + ym) & 0x3fff] >> xm) & 1);
//  if ((attr & 0x80) && ym == 7) enable_dot = 1;
//  if (attr & 0x40) highlight_color = 16;
//  if (attr & 0x20) enable_dot = !enable_dot;
//  if (attr & 0x10 && !m_blink_enable) enable_dot = 0;

	return std::make_tuple(highlight_color + ((enable_dot) ? foreground_color : background_color), enable_dot != 0);
}

std::tuple<u8, bool> c65_state::draw_multicolor_bitmap(u8 tile, u8 attr, int yi, int xi, int pixel_width)
{
	const int xm = 6 - ((xi / pixel_width) & 6);
	const int ym = (yi & 7);
	const u8 color01 = tile >> 4;
	const u8 color10 = tile & 0xf;
	const u8 color11 = attr & 0xf;
	const std::array<u8, 4> color_map = { m_bk_color_clut[0], color01, color10, color11 };

	u8 highlight_color = 0;
	u8 enable_dot = ((m_char_ptr[((xi >> (3 + (pixel_width - 1))) * 8  + (yi >> 3) * 320 + ym) & 0x3fff] >> xm) & 3);
//  if ((attr & 0x80) && ym == 7) enable_dot = 1;
//  if (attr & 0x40) highlight_color = 16;
//  if (attr & 0x20) enable_dot = !enable_dot;
//  if (attr & 0x10 && !m_blink_enable) enable_dot = 0;

	return std::make_tuple(highlight_color + color_map[enable_dot], enable_dot & 2);
}

std::tuple<u8, bool> c65_state::draw_extended_background(u8 tile, u8 attr, int yi, int xi, int pixel_width)
{
	const int xm = 7 - ((xi / pixel_width) & 7);
	const int ym = (yi & 7);
	const u8 foreground_color = attr & 0xf;
	const u8 background_color = m_bk_color_clut[tile >> 6] & 0xf;

	u8 highlight_color = 0;
	u8 enable_dot = ((m_char_ptr[(((tile & 0x3f) << 3) + ym) & 0x3fff] >> xm) & 1);
	if ((attr & 0x80) && ym == 7) enable_dot = 1;
	if (attr & 0x40) highlight_color = 16;
	if (attr & 0x20) enable_dot = !enable_dot;
	if (attr & 0x10 && !m_blink_enable) enable_dot = 0;

	return std::make_tuple(highlight_color + ((enable_dot) ? foreground_color : background_color), enable_dot != 0);
}

// TODO: invalid modes essentially draws black but still weights for collision info
std::tuple<u8, bool> c65_state::draw_invalid(u8 tile, u8 attr, int yi, int xi, int pixel_width)
{
	return std::make_tuple(0, false);
}

std::tuple<u8, bool> c65_state::get_tile_pixel(int y, int x)
{
	// TODO: move width as a screen setup
	int pixel_width = (m_control_b & 0x80) ? 1 : 2;
	int columns = 80 / pixel_width;

	int xi = (x >> 3) / pixel_width;
	int yi = (y >> 3);
	uint8_t tile = m_video_ptr[(xi + yi * columns) & 0x3fff];
	uint8_t attr = m_cram[xi + yi * columns];

	return (this->*draw_tile_table[m_gfxmode])(tile, attr, y, x, pixel_width);
}

std::tuple<u8, u8, u8, bool> c65_state::get_sprite_pixel(int y, int x)
{
	if (!m_sprite_enable)
		return std::make_tuple(0, 0, 0, false);

	u8 enable_dot = 0;
	u8 sprite_mask = 0;
	u8 idx = 0;
	// TODO: move masking outside this function, bitplane mode should also affect this
	const u8 *sprite_ptr = &m_video_ptr[BIT(m_control_b, 7) ? 0x7f8 : 0x3f8];

	// sprite #7 < #6 < ... < #0
	for (int i = 7; i >= 0; i--)
	{
		if (!BIT(m_sprite_enable, i))
			continue;

		// NOTE: "0 +" intentional for 400i mode, eventually.
		const int y_width = 0 + BIT(m_sexy, i);
		const int ysize = 21 << y_width;
		const u16 yi = m_sprite[i].y;

		if (!(y >= yi && y < yi + ysize))
			continue;

		const int x_width = 1 + BIT(m_sexx, i);
		const int xsize = 24 << x_width;
		const u16 xi = m_sprite[i].x << 1;

		if (!(x >= xi && x < xi + xsize))
			continue;

		u32 sprite_offset = sprite_ptr[i] << 6;
		const int xm = (x - xi) >> x_width;
		const int ym = (y - yi) >> y_width;

		// TODO: not using m_workram here breaks isoccer boot in gfxmode=0, investigate
		u8 sprite_data = m_video_ptr[(ym * 3) + (xm >> 3) + sprite_offset];
		const bool is_multicolor = bool(BIT(m_scm, i));
		const u8 dot_mask = is_multicolor << 1 | 1;
		const u8 shift_mask = 7 - is_multicolor;
		const u8 color_shift = !is_multicolor;
		u8 sprite_dot = (sprite_data >> (shift_mask - (xm & shift_mask))) & dot_mask;
		if (sprite_dot)
		{
			const std::array<u8, 4> color_map = { 0, m_sprite_multicolor_clut[0], m_sprite[i].clut, m_sprite_multicolor_clut[1] };
			sprite_mask |= 1 << i;
			idx = i;
			enable_dot = color_map[sprite_dot << color_shift];
		}
	}
	return std::make_tuple(enable_dot, sprite_mask, idx, enable_dot != 0);
}

TIMER_CALLBACK_MEMBER(c65_state::scanline_cb)
{
	int y = param;
	uint16_t *p = &m_bitmap.pix(y);
	//popmessage("%02x %02x %02x", m_sprite_enable, m_sprite[0].x, m_sprite[0].y);

	const int border_left = 0;
	const int active_left = 24;
	const int active_right = 640 + active_left;
	const int border_right = active_right + active_left;
	const int active_top = 30;
	const int active_bottom = 200 + active_top;

	int x = border_left;

	if (!m_blnk)
	{
		// TODO: blank color
		for (x = border_left; x < border_right; x++)
			p[x] = 0;
	}
	else
	{
		if (y < active_top || y >= active_bottom)
		{
			// TODO: $3fff "opening the border" stuff
			for (x = border_left; x < border_right; x++)
				p[x] = m_border_color;
		}
		else
		{
			const int width80 = BIT(m_control_b, 7) ^ 1;
			for (x = border_left; x < active_left; x++)
				p[x] = m_border_color;
			for (;x < active_right; x++)
			{
				u8 tile_dot, sprite_dot, sprite_mask, sprite_active;
				bool is_foreground, is_sprite;
				// NOTE: VIC-II and VIC-III can switch mid-frame, but latches occur in 8 scanline steps
				// at least from/to a base C=64 tilemap mode, also cfr. "bad lines".
				std::tie(tile_dot, is_foreground) = get_tile_pixel(y - active_top, x - active_left - (m_xscl << width80));
				// HACK: are sprite positions in native coordinates from the border?
				std::tie(sprite_dot, sprite_mask, sprite_active, is_sprite) = get_sprite_pixel(y + 20, x + active_left);

				// TODO: collisions
				//m_ssc |= sprite_mask;
				// TODO: reduce this
				if (is_foreground && !(BIT(sprite_mask, sprite_active) & (BIT(m_bsp ^ 0xff, sprite_active))))
					p[x] = m_palette->pen(tile_dot);
				else
					p[x] = m_palette->pen(is_sprite ? sprite_dot : tile_dot);
			}
			for (;x < border_right; x++)
				p[x] = m_border_color;
		}
	}

	// HACK: need to compensate, cfr. princess
	if (y == m_rcr - 21)
		irq_check(1);

	y += 1;
	y %= 262;
	if (y == 0)
		m_blink_enable = bool((m_screen->frame_number() & 4) == 4);
	m_scanline_timer->adjust(m_screen->time_until_pos(y, 0), y);
}

uint32_t c65_state::screen_update( screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect )
{
	copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
	return 0;
}

void c65_state::palette_entry_flush(uint8_t offset)
{
	m_palette->set_pen_color(offset, pal4bit(m_palred[offset]), pal4bit(m_palgreen[offset]), pal4bit(m_palblue[offset]));
}

void c65_state::palette_red_w(offs_t offset, uint8_t data)
{
	// TODO: bit 4 for FG/BG, superimposing?
	m_palred[offset] = data;
	palette_entry_flush(offset);
}

void c65_state::palette_green_w(offs_t offset, uint8_t data)
{
	m_palgreen[offset] = data;
	palette_entry_flush(offset);
}

void c65_state::palette_blue_w(offs_t offset, uint8_t data)
{
	m_palblue[offset] = data;
	palette_entry_flush(offset);
}

uint8_t c65_state::uart_r(offs_t offset)
{
	switch (offset)
	{
		case 7:
			return ioport("CAPS")->read();
	}
	return 0xff;
}

void c65_state::uart_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
		case 7:
			m_keyb_c8_c9 = (~data >> 1) & 3;
			break;
		case 8:
			// ddr?
			break;
	}
}

uint8_t c65_state::cia0_porta_r()
{
	uint8_t res = 0xff;

	// joystick
	uint8_t joy_b = m_joy[1]->read_joy();

	res &= (0xf0 | (joy_b & 0x0f));
	res &= ~(!BIT(joy_b, 5) << 4);

	return res;

}

uint8_t c65_state::cia0_portb_r()
{
	static const char *const c64ports[] = { "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7" };
	static const char *const c65ports[] = { "C8", "C9" };
	uint8_t res;

	res = 0xff;
	uint8_t joy_a = m_joy[0]->read_joy();

	res &= (0xf0 | (joy_a & 0x0f));
	res &= ~(!BIT(joy_a, 5) << 4);

	for(int i = 0; i < 8; i++)
	{
		m_keyb_input[i] = ioport(c64ports[i])->read();

		if(m_keyb_c0_c7 & 1 << (i))
			res &= m_keyb_input[i];
	}

	for(int i = 0; i < 2; i++)
	{
		m_keyb_input[i + 8] = ioport(c65ports[i])->read();

		if(m_keyb_c8_c9 & 1 << (i))
			res &= m_keyb_input[i + 8];
	}

	return res;
}

void c65_state::cia0_porta_w(uint8_t data)
{
	m_keyb_c0_c7 = ~data;
	m_joy[1]->joy_w(data & 0x1f);
//  logerror("%02x\n",m_keyb_c0_c7);
}

void c65_state::cia0_portb_w(uint8_t data)
{
	m_joy[0]->joy_w(data & 0x1f);

}

/*
 * xx-- ---- serial bus input
 * --xx x--- serial bus output
 * ---- -x-- RS-232 TXD output
 * ---- --xx VIC bank base
 */
void c65_state::cia1_porta_w(uint8_t data)
{
	m_vic_bank_base = ~(data & 0x3) << 14;
	flush_cb_base();
}

void c65_state::c65_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x07fff).rw(FUNC(c65_state::ram_r<0x00000>), FUNC(c65_state::ram_w<0x00000>));

	map(0x08000, 0x0bfff).view(m_exrom_view);
	m_exrom_view[0](0x08000, 0x0bfff).r(m_cart_exp, FUNC(generic_slot_device::read_rom));
	m_exrom_view[1](0x08000, 0x09fff).rw(FUNC(c65_state::ram_r<0x08000>), FUNC(c65_state::ram_w<0x08000>));
	m_exrom_view[1](0x0a000, 0x0bfff).rom().region("ipl", 0x0a000);

	map(0x0c000, 0x0cfff).view(m_romc_view);
	m_romc_view[0](0x0c000, 0x0cfff).rw(FUNC(c65_state::ram_r<0x0c000>), FUNC(c65_state::ram_w<0x0c000>));
	m_romc_view[1](0x0c000, 0x0cfff).rom().region("ipl", 0x0c000);

	map(0x0d000, 0x0dfff).view(m_charen_view);
	m_charen_view[0](0x0d000, 0x0dfff).rom().region("ipl", 0x0d000);
	m_charen_view[1](0x0d000, 0x0d07f).m(*this, FUNC(c65_state::vic4567_map));
	// 0x0d080, 0x0d09f FDC
	m_charen_view[1](0x0d080, 0x0d09f).lr8(NAME([] (offs_t offset) { return 0; }));
	// 0x0d0a0, 0x0d0ff Ram Expansion Control (REC)
	m_charen_view[1](0x0d100, 0x0d1ff).ram().w(FUNC(c65_state::palette_red_w)).share("redpal");
	m_charen_view[1](0x0d200, 0x0d2ff).ram().w(FUNC(c65_state::palette_green_w)).share("greenpal");
	m_charen_view[1](0x0d300, 0x0d3ff).ram().w(FUNC(c65_state::palette_blue_w)).share("bluepal");
	// 0x0d400, 0x0d4*f Right SID
	// keyboard hold left shift will read to $d484 (?)
	m_charen_view[1](0x0d400, 0x0d41f).mirror(0x80).rw(m_sid[1], FUNC(mos6581_device::read), FUNC(mos6581_device::write));
	// 0x0d440, 0x0d4*f Left  SID
	m_charen_view[1](0x0d440, 0x0d45f).mirror(0x80).rw(m_sid[0], FUNC(mos6581_device::read), FUNC(mos6581_device::write));
	m_charen_view[1](0x0d600, 0x0d6ff).rw(FUNC(c65_state::uart_r), FUNC(c65_state::uart_w));
	// 0x0d700, 0x0d7** DMAgic
	m_charen_view[1](0x0d700, 0x0d703).m(m_dma, FUNC(dmagic_f018_device::map));
	// 0x0d800, 0x0d8** Color matrix
	m_charen_view[1](0x0d800, 0x0dfff).view(m_cram_view);
	// maps lower 1024 bytes regardless of the setting (essentially touches $dc00 as overlay)
	m_cram_view[0](0x0d800, 0x0dbff).lrw8(
		NAME([this] (offs_t offset) { return m_cram[offset]; }),
		NAME([this] (offs_t offset, u8 data) { m_cram[offset] = data; })
	);
	m_cram_view[0](0x0dc00, 0x0dc0f).rw(m_cia[0], FUNC(mos6526_device::read), FUNC(mos6526_device::write));
	m_cram_view[0](0x0dd00, 0x0dd0f).rw(m_cia[1], FUNC(mos6526_device::read), FUNC(mos6526_device::write));
	// 0x0de00, 0x0de** Ext I/O Select 1 (IO1)
	// 0x0df00, 0x0df** Ext I/O Select 2 (IO2)
	m_cram_view[1](0x0d800, 0x0dfff).ram().share("cram");

	map(0x0e000, 0x0ffff).rom().region("ipl", 0x0e000);

	map(0x10000, 0x1f7ff).rw(FUNC(c65_state::ram_r<0x010000>), FUNC(c65_state::ram_w<0x10000>));
	map(0x1f800, 0x1ffff).ram().share("cram");
	map(0x20000, 0x3ffff).rom().region("ipl", 0);
//  0x40000, 0x7ffff cart expansion
//  0x80000, 0xfffff RAM expansion
//  map(0x80000, 0xfffff).ram();
}



static INPUT_PORTS_START( c65 )
	PORT_START("C0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INST DEL") PORT_CODE(KEYCODE_BACKSPACE)       PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER)             PORT_CHAR(13)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Crsr Right Left") PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F4)                                    PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1)                                    PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2)                                    PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3)                                    PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Crsr Down Up") PORT_CODE(KEYCODE_RALT)        PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CHAR(UCHAR_MAMEKEY(UP))

	PORT_START("C1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)         PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)         PORT_CHAR('W')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)         PORT_CHAR('A')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)         PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)         PORT_CHAR('Z')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)         PORT_CHAR('S')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)         PORT_CHAR('E')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Shift (Left)") PORT_CODE(KEYCODE_LSHIFT)      PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("C2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5)         PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)         PORT_CHAR('R')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)         PORT_CHAR('D')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6)         PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)         PORT_CHAR('C')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)         PORT_CHAR('F')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)         PORT_CHAR('T')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)         PORT_CHAR('X')

	PORT_START("C3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7)         PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)         PORT_CHAR('Y')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)         PORT_CHAR('G')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8)         PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)         PORT_CHAR('B')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)         PORT_CHAR('H')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)         PORT_CHAR('U')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)         PORT_CHAR('V')

	PORT_START("C4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9)         PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)         PORT_CHAR('I')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)         PORT_CHAR('J')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0)         PORT_CHAR('0')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)         PORT_CHAR('M')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)         PORT_CHAR('K')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)         PORT_CHAR('O')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)         PORT_CHAR('N')

	PORT_START("C5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)     PORT_CHAR('+')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)         PORT_CHAR('P')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)         PORT_CHAR('L')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS)    PORT_CHAR('-')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)      PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)     PORT_CHAR(':') PORT_CHAR('[')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA)     PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("C6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH2)                        PORT_CHAR(0xA3)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE)                        PORT_CHAR('*')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE)                             PORT_CHAR(';') PORT_CHAR(']')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CLR HOME") PORT_CODE(KEYCODE_INSERT)      PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Shift (Right)") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH)                         PORT_CHAR('=')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\xE2\x86\x91  Pi") PORT_CODE(KEYCODE_DEL) PORT_CHAR(0x2191) PORT_CHAR(0x03C0)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH)                             PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("C7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1)                                 PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\xE2\x86\x90") PORT_CODE(KEYCODE_TILDE)   PORT_CHAR(0x2190)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LCONTROL)                          PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2)                                 PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE)                             PORT_CHAR(' ')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CBM") PORT_CODE(KEYCODE_LALT)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)                                 PORT_CHAR('Q')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RUN STOP") PORT_CODE(KEYCODE_HOME)

	PORT_START("C8")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("NO SCROLL") PORT_CODE(KEYCODE_SCRLOCK)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TAB)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RALT)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("HELP") PORT_CODE(KEYCODE_F9)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F5)                                    PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F6)                                    PORT_CHAR(UCHAR_MAMEKEY(F11))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F7)                                    PORT_CHAR(UCHAR_MAMEKEY(F13))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ESC)

	PORT_START("C9")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("UNKNOWN1")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("UNKNOWN2")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("linefeed?")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("cursor left?")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("UNKNOWN5")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("UNKNOWN6")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("UNKNOWN7")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("UNKNOWN8")

	PORT_START("CAPS")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CAPS LOCK") PORT_CODE(KEYCODE_F8) PORT_TOGGLE
INPUT_PORTS_END


void c65_state::machine_start()
{
	save_pointer(NAME(m_cram.target()), 0x800);
}

void c65_state::machine_reset()
{
	m_control_a = 0;
	m_ssc = m_sbc = 0;
	m_cram_view.select(0);
	m_exrom_view.select(m_cart_exp->exists() ^ 1);
}


void c65_state::palette_init(palette_device &palette)
{
	// HACK: should read from an (undumped) PROM, and switch when bit 2 of control A is high
	static const u8 r_default[] = {
		0x00, 0x0f, 0x0f, 0x00,
		0x0f, 0x00, 0x00, 0x0f,
		0x0f, 0x0a, 0x0f, 0x05,
		0x08, 0x09, 0x09, 0x0b
	};
	static const u8 g_default[] = {
		0x00, 0x0f, 0x00, 0x0f,
		0x00, 0x0f, 0x00, 0x0f,
		0x06, 0x04, 0x07, 0x05,
		0x08, 0x0f, 0x09, 0x0b
	};
	static const u8 b_default[] = {
		0x00, 0x0f, 0x00, 0x0f,
		0x0f, 0x00, 0x0f, 0x00,
		0x00, 0x00, 0x07, 0x05,
		0x08, 0x09, 0x0f, 0x0b
	};
	int i;

	for (i = 0; i < 0x10; i++)
	{
		m_palred[i] = r_default[i];
		m_palgreen[i] = g_default[i];
		m_palblue[i] = b_default[i];
	}

	for (i = 0; i < 0x100; i++)
		palette_entry_flush(i);
}

// debug
static const gfx_layout charlayout =
{
	8,8,
	0x1000/8,
	1,
	{ RGN_FRAC(0,1) },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};

static GFXDECODE_START( gfx_c65 )
	GFXDECODE_ENTRY( "ipl", 0xd000, charlayout,     0, 16 )
	// almost identical to above
	GFXDECODE_ENTRY( "ipl", 0x9000, charlayout,     0, 16 )
GFXDECODE_END

void c65_state::irq_check(uint8_t irq_cause)
{
	m_irq_pending |= (irq_cause != 0) ? 0x80 : 0x00;
	m_irq_pending |= irq_cause;

	m_irqs->in_w<1>(m_irq_mask & m_irq_pending);
}

uint8_t c65_state::cpu_r()
{
	return 0x07;
}

void c65_state::cpu_w(uint8_t data)
{
//  m_loram = BIT(data, 0);
//  m_hiram = BIT(data, 1);
	m_charen_view.select(BIT(data, 2));
}


void c65_state::c65(machine_config &config)
{
	M4510(config, m_maincpu, MAIN_C65_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &c65_state::c65_map);
	m_maincpu->read_callback().set(FUNC(c65_state::cpu_r));
	m_maincpu->write_callback().set(FUNC(c65_state::cpu_w));
	m_maincpu->set_pulls(0x07, 0xc0);

	// TODO: multiply by x8 because with a more canonical x1 no transfer will complete in time.
	// is this thing a mixed burst/cycle steal really?
	DMAGIC_F018(config, m_dma, MAIN_C65_CLOCK * 8);
	m_dma->set_space(m_maincpu, AS_PROGRAM);

	input_merger_device &irq(INPUT_MERGER_ANY_HIGH(config, "irqs"));
	irq.output_handler().set_inputline(m_maincpu, M4510_IRQ_LINE);

	INPUT_MERGER_ANY_HIGH(config, m_nmis);
	m_nmis->output_handler().set_inputline(m_maincpu, M4510_NMI_LINE);

	MOS6526(config, m_cia[0], MAIN_C65_CLOCK);
	m_cia[0]->set_tod_clock(60);
	m_cia[0]->irq_wr_callback().set("irqs", FUNC(input_merger_device::in_w<0>));
	m_cia[0]->pa_rd_callback().set(FUNC(c65_state::cia0_porta_r));
	m_cia[0]->pa_wr_callback().set(FUNC(c65_state::cia0_porta_w));
	m_cia[0]->pb_rd_callback().set(FUNC(c65_state::cia0_portb_r));
	m_cia[0]->pb_wr_callback().set(FUNC(c65_state::cia0_portb_w));

	MOS6526(config, m_cia[1], MAIN_C65_CLOCK);
	m_cia[1]->set_tod_clock(60);
	m_cia[1]->irq_wr_callback().set(m_nmis, FUNC(input_merger_device::in_w<0>));
//  m_cia[1]->pa_rd_callback().set(FUNC(c65_state::c65_cia1_port_a_r));
	m_cia[1]->pa_wr_callback().set(FUNC(c65_state::cia1_porta_w));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_screen_update(FUNC(c65_state::screen_update));
	// TODO: stub parameters
	// C=64 / width 40 modes should actually be running in 320x200
	m_screen->set_raw(MAIN_C65_CLOCK*4, 910, 0, 640+24 * 2, 262, 0, 200+30*2);
	m_screen->set_palette(m_palette);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_c65);

	PALETTE(config, m_palette, FUNC(c65_state::palette_init), 0x100);

	SPEAKER(config, "speaker", 2).front();
	// 8580 SID
	MOS6581(config, m_sid[0], MAIN_C64_CLOCK);
	//m_sid->potx().set(FUNC(c64_state::sid_potx_r));
	//m_sid->poty().set(FUNC(c64_state::sid_poty_r));
	m_sid[0]->add_route(ALL_OUTPUTS, "speaker", 0.50, 0);

	MOS6581(config, m_sid[1], MAIN_C64_CLOCK);
	//m_sid->potx().set(FUNC(c64_state::sid_potx_r));
	//m_sid->poty().set(FUNC(c64_state::sid_poty_r));
	m_sid[1]->add_route(ALL_OUTPUTS, "speaker", 0.50, 1);

	VCS_CONTROL_PORT(config, m_joy[0], vcs_control_port_devices, "joy");
	//m_joy1->trigger_wr_callback().set(MOS6567_TAG, FUNC(mos6567_device::lp_w));
	VCS_CONTROL_PORT(config, m_joy[1], vcs_control_port_devices, "joy");

	GENERIC_CARTSLOT(config, m_cart_exp, generic_plain_slot, "c64_cart");

	SOFTWARE_LIST(config, "flop_list").set_original("c65_flop");
//    SOFTWARE_LIST(config, "cart_list").set_compatible("c64_cart");
}


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

  Game driver(s)

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

ROM_START( c65 )
	ROM_REGION( 0x20000, "ipl", 0 )
	ROM_SYSTEM_BIOS( 0, "910111", "V0.9.910111" ) // sum16 CAFF, this shows up on the picture from a spare, unused rom on the 20171102 c64dx auction as "390488-02 CAFF" with the 02 scratched off on the chip and 03 written in pen, unclear what the "correct" label is.
	ROMX_LOAD( "910111.bin", 0x0000, 0x20000, CRC(c5d8d32e) SHA1(71c05f098eff29d306b0170e2c1cdeadb1a5f206), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "910523", "V0.9.910523" ) // sum16 B96B
	ROMX_LOAD( "910523.bin", 0x0000, 0x20000, CRC(e8235dd4) SHA1(e453a8e7e5b95de65a70952e9d48012191e1b3e7), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "910626", "V0.9.910626" ) // sum16 888C
	ROMX_LOAD( "910626.bin", 0x0000, 0x20000, CRC(12527742) SHA1(07c185b3bc58410183422f7ac13a37ddd330881b), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "910828", "V0.9.910828" ) // sum16 C9CD
	ROMX_LOAD( "910828.bin", 0x0000, 0x20000, CRC(3ee40b06) SHA1(b63d970727a2b8da72a0a8e234f3c30a20cbcb26), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "911001", "V0.9.911001" ) // sum16 4BCF
	ROMX_LOAD( "911001.bin", 0x0000, 0x20000, CRC(0888b50f) SHA1(129b9a2611edaebaa028ac3e3f444927c8b1fc5d), ROM_BIOS(4) )
ROM_END

ROM_START( c64dx )
	ROM_REGION( 0x20000, "ipl", 0 ) // "v0.90.910429", sum16 E96A
	ROM_LOAD( "910429.bin", 0x0000, 0x20000, CRC(b025805c) SHA1(c3b05665684f74adbe33052a2d10170a1063ee7d) )
ROM_END

void c65_state::init_c65()
{
//  m_dma.version = 2;
//  c65_common_driver_init();
}

void c65_state::init_c65pal()
{
//  m_dma.version = 1;
//  c65_common_driver_init();
//  m_pal = 1;
}

} // anonymous namespace

COMP( 1991, c65,   0,   0, c65, c65, c65_state, init_c65,    "Commodore Business Machines", "Commodore 65 Development System (Prototype, NTSC)",          MACHINE_NOT_WORKING )
COMP( 1991, c64dx, c65, 0, c65, c65, c65_state, init_c65pal, "Commodore Business Machines", "Commodore 64DX Development System (Prototype, PAL, German)", MACHINE_NOT_WORKING )



c80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/***************************************************************************

C-80 Trainer (East Germany)

Pasting:
    0-F : as is
    + (inc) : ^
    - (dec) : V
    M : -
    GO : X

Test Paste:
    -800^11^22^33^44^55^66^77^88^99^-800
    Now press up-arrow to confirm the data has been entered.

Commands:
    R : REGister
    M : MEMory manipulation
    G : GO
  F10 : RESet
  ESC : BRK

Functions (press F1 then the indicated number):
    0 : FILL
    1 : SAVE
    2 : LOAD
    3 : LOADP
    4 : MOVE
    5 : IN
    6 : OUT

When REG is chosen, use UP to scroll through the list of regs,
or press 0 thru C to choose one directly:
    0 : SP
    1 : PC
    2 : AF
    3 : BC
    4 : DE
    5 : HL
    6 : AF'
    7 : BC'
    8 : DE'
    9 : HL'
    A : IFF
    B : IX
    C : IY

When MEM is chosen, enter the address, press UP, enter data, press UP, enter
data of next byte, and so on.

Cassette SAVE: Press F1 1 aaaa DOWN bbbb UP
Cassette LOAD: Press F1 2 aaaa DOWN bbbb UP
(where aaaa = 4-digit beginning address, bbbb = 4-digit ending address)

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

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/z80daisy.h"
#include "machine/z80pio.h"
#include "imagedev/cassette.h"
#include "speaker.h"
#include "c80.lh"

namespace {

#define Z80_TAG         "d2"
#define Z80PIO1_TAG     "d11"
#define Z80PIO2_TAG     "d12"
// You could use a piezo at 455 kHz, or a crystal 500 to 2500 kHz.
// Cassette successfully tested at 455 kHz
#define MASTER_CLOCK    455000

class c80_state : public driver_device
{
public:
	c80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, Z80_TAG)
		, m_pio1(*this, Z80PIO1_TAG)
		, m_pio2(*this, Z80PIO2_TAG)
		, m_cassette(*this, "cassette")
		, m_row0(*this, "ROW0")
		, m_row1(*this, "ROW1")
		, m_row2(*this, "ROW2")
		, m_digits(*this, "digit%d", 0U)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER( trigger_reset );
	DECLARE_INPUT_CHANGED_MEMBER( trigger_nmi );
	void c80(machine_config &config);

private:
	required_device<z80_device> m_maincpu;
	required_device<z80pio_device> m_pio1;
	required_device<z80pio_device> m_pio2;
	required_device<cassette_image_device> m_cassette;
	required_ioport m_row0;
	required_ioport m_row1;
	required_ioport m_row2;
	output_finder<8> m_digits;

	virtual void machine_start() override ATTR_COLD;

	uint8_t pio1_pa_r();
	void pio1_pa_w(uint8_t data);
	void pio1_pb_w(uint8_t data);
	void pio1_brdy_w(int state);

	/* keyboard state */
	u8 m_keylatch = 0;

	/* display state */
	u8 m_digit = 0;
	bool m_pio1_a5 = false;
	u8 m_pio1_brdy = 0;
	void c80_io(address_map &map) ATTR_COLD;
	void c80_mem(address_map &map) ATTR_COLD;
};


/* Memory Maps */

void c80_state::c80_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).rom();
	map(0x0800, 0x0bff).mirror(0x400).ram();
}

void c80_state::c80_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x7c, 0x7f).rw(m_pio2, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0xbc, 0xbf).rw(m_pio1, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
}

/* Input Ports */

INPUT_CHANGED_MEMBER( c80_state::trigger_reset )
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? CLEAR_LINE : ASSERT_LINE);
}

INPUT_CHANGED_MEMBER( c80_state::trigger_nmi )
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, newval ? CLEAR_LINE : ASSERT_LINE);
}

static INPUT_PORTS_START( c80 )
	PORT_START("ROW0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("REG") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GO") PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("FCN") PORT_CODE(KEYCODE_F1)

	PORT_START("ROW1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("+") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("-") PORT_CODE(KEYCODE_DOWN) PORT_CHAR('V')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')

	PORT_START("ROW2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("MEM") PORT_CODE(KEYCODE_M) PORT_CHAR('-')

	PORT_START("SPECIAL")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RES") PORT_CODE(KEYCODE_F10) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(c80_state::trigger_reset), 0)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BRK") PORT_CODE(KEYCODE_ESC) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(c80_state::trigger_nmi), 0)
INPUT_PORTS_END

/* Z80-PIO Interface */

uint8_t c80_state::pio1_pa_r()
{
	/*

	    bit     description

	    PA0     keyboard row 0 input
	    PA1     keyboard row 1 input
	    PA2     keyboard row 2 input
	    PA3
	    PA4     _BSTB input
	    PA5     display enable output (0=enabled, 1=disabled)
	    PA6     tape output
	    PA7     tape input

	*/

	uint8_t data = m_pio1_brdy | 0x07;

	for (u8 i = 0; i < 8; i++)
	{
		if (!BIT(m_keylatch, i))
		{
			if (!BIT(m_row0->read(), i)) data &= ~0x01;
			if (!BIT(m_row1->read(), i)) data &= ~0x02;
			if (!BIT(m_row2->read(), i)) data &= ~0x04;
		}
	}

	data |= (m_cassette->input() < +0.0) << 7;

	return data;
}

void c80_state::pio1_pa_w(uint8_t data)
{
	/*

	    bit     description

	    PA0     keyboard row 0 input
	    PA1     keyboard row 1 input
	    PA2     keyboard row 2 input
	    PA3
	    PA4     _BSTB input
	    PA5     display enable output (0=enabled, 1=disabled)
	    PA6     tape output
	    PA7     tape input

	*/

	m_pio1_a5 = BIT(data, 5);

	if (!BIT(data, 5))
	{
		m_digit = 0;
	}

	m_cassette->output(BIT(data, 6) ? +1.0 : -1.0);
}

void c80_state::pio1_pb_w(uint8_t data)
{
	/*

	    bit     description

	    PB0     VQD30 segment A
	    PB1     VQD30 segment B
	    PB2     VQD30 segment C
	    PB3     VQD30 segment D
	    PB4     VQD30 segment E
	    PB5     VQD30 segment F
	    PB6     VQD30 segment G
	    PB7     VQD30 segment P

	*/

	if (!m_pio1_a5 && (m_digit < 8))
	{
		m_digits[m_digit] = data;
	}

	m_keylatch = data;
}

void c80_state::pio1_brdy_w(int state)
{
	m_pio1_brdy = state ? 0 : 0x10;

	if (state)
	{
		if (!m_pio1_a5)
		{
			m_digit++;
		}

		m_pio1->strobe_b(1);
		m_pio1->strobe_b(0);
	}
}

/* Z80 Daisy Chain */

static const z80_daisy_config c80_daisy_chain[] =
{
	{ Z80PIO1_TAG },
	{ Z80PIO2_TAG },
	{ nullptr }
};

/* Machine Initialization */

void c80_state::machine_start()
{
	m_digits.resolve();
	/* register for state saving */
	save_item(NAME(m_keylatch));
	save_item(NAME(m_digit));
	save_item(NAME(m_pio1_a5));
	save_item(NAME(m_pio1_brdy));
}

/* Machine Driver */

void c80_state::c80(machine_config &config)
{
	// CPU
	Z80(config, m_maincpu, MASTER_CLOCK); // U880D
	m_maincpu->set_addrmap(AS_PROGRAM, &c80_state::c80_mem);
	m_maincpu->set_addrmap(AS_IO, &c80_state::c80_io);
	m_maincpu->set_daisy_config(c80_daisy_chain);

	// video
	config.set_default_layout(layout_c80);

	// devices
	Z80PIO(config, m_pio1, MASTER_CLOCK); // U855D
	m_pio1->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio1->in_pa_callback().set(FUNC(c80_state::pio1_pa_r));
	m_pio1->out_pa_callback().set(FUNC(c80_state::pio1_pa_w));
	m_pio1->out_pb_callback().set(FUNC(c80_state::pio1_pb_w));
	m_pio1->out_brdy_callback().set(FUNC(c80_state::pio1_brdy_w));

	Z80PIO(config, m_pio2, MASTER_CLOCK); // U855D
	m_pio2->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	// cassette
	SPEAKER(config, "mono").front_center();
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
}

/* ROMs */

ROM_START( c80 )
	ROM_REGION( 0x0800, Z80_TAG, ROMREGION_ERASEFF )
	ROM_LOAD( "c80.d3", 0x0000, 0x0400, CRC(ad2b3296) SHA1(14f72cb73a4068b7a5d763cc0e254639c251ce2e) )
ROM_END

} // Anonymous namespace

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY          FULLNAME  FLAGS */
COMP( 1986, c80,  0,      0,      c80,     c80,   c80_state, empty_init, "Joachim Czepa", "C-80",   MACHINE_SUPPORTS_SAVE )



c900.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder, Robbbert
/******************************************************************************************

Commodore C900
UNIX prototype

http://www.zimmers.net/cbmpics/c900.html
http://www.zimmers.net/cbmpics/cbm/900/c900-chips.txt

Chips: Z8001 CPU, Z8010 MMU, Z8030 SCC, Z8036 CIO. Crystal: 12MHz

The Z8030 runs 2 serial ports. The Z8036 runs the IEEE interface and the speaker.

The FDC is an intelligent device that communicates with the main board via the MMU.
It has a 6508 CPU.

Disk drive is a Matsushita JA-560-012

Increasing the amount of RAM stops the error message, however it still keeps running
into the weeds (jumps to 00000). Due to lack of banking, the stack is pointing at rom.

To Do:
- Banking
- Pretty much everything
- Need schematics, technical manuals and so on.
- Eventually, will need software.
- Disassembler needs fixing

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


#include "emu.h"
#include "cpu/z8000/z8000.h"
#include "cpu/m6502/m6510.h"
#include "machine/z80scc.h"
#include "bus/rs232/rs232.h"
#include "machine/z8536.h"
#include "sound/spkrdev.h"
#include "emupal.h"
#include "speaker.h"


namespace {

class c900_state : public driver_device
{
public:
	c900_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_fdcpu(*this, "fdcpu")
		, m_spkrdev(*this, "speaker")
	{ }

	void c900(machine_config &config);

private:
	void sound_pb_w(u8 data);

	void data_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void special_io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	void fdc_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<cpu_device> m_fdcpu;
	required_device<speaker_sound_device> m_spkrdev;
};

void c900_state::sound_pb_w(u8 data)
{
	m_spkrdev->level_w(BIT(data, 0));
}

void c900_state::mem_map(address_map &map)
{
	map(0x000000, 0x007fff).rom().region("roms", 0);
}

void c900_state::data_map(address_map &map)
{
	map(0x000000, 0x007fff).rom().region("roms", 0);
	map(0x008000, 0x06ffff).ram();
	map(0x3f0000, 0x3f1fff).ram();
}

void c900_state::io_map(address_map &map)
{
	map(0x0000, 0x007f).rw("cio", FUNC(z8036_device::read), FUNC(z8036_device::write)).umask16(0x00ff);
	map(0x0100, 0x013f).rw("scc", FUNC(scc8030_device::zbus_r), FUNC(scc8030_device::zbus_w)).umask16(0x00ff);
}

void c900_state::special_io_map(address_map &map)
{
	// TODO: Z8010 MMU
}

void c900_state::fdc_map(address_map &map)
{
	map(0x0000, 0x01ff).noprw(); // internal
	map(0xe000, 0xffff).rom().region("fdc", 0);
}

static INPUT_PORTS_START( c900 )
INPUT_PORTS_END

/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 16,                   /* 8 x 16 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_c900 )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END

void c900_state::c900(machine_config &config)
{
	/* basic machine hardware */
	Z8001(config, m_maincpu, 12_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &c900_state::mem_map);
	m_maincpu->set_addrmap(AS_DATA, &c900_state::data_map);
	m_maincpu->set_addrmap(AS_IO, &c900_state::io_map);
	m_maincpu->set_addrmap(z8001_device::AS_SIO, &c900_state::special_io_map);

	M6508(config, m_fdcpu, 12_MHz_XTAL / 8); // PH1/PH2 = 1.5 MHz
	m_fdcpu->set_addrmap(AS_PROGRAM, &c900_state::fdc_map);

	GFXDECODE(config, "gfxdecode", "palette", gfx_c900);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	z8036_device &cio(Z8036(config, "cio", 12_MHz_XTAL / 16)); // SNDCLK = 750kHz
	cio.pb_wr_cb().set(FUNC(c900_state::sound_pb_w));

	scc8030_device &scc(SCC8030(config, "scc", 12_MHz_XTAL / 2)); // 5'850'000 is the ideal figure
	/* Port B */
	scc.out_txdb_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	scc.out_dtrb_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	scc.out_rtsb_callback().set("rs232", FUNC(rs232_port_device::write_rts));
	//scc.out_int_callback().set("rs232", FUNC(c900_state::scc_int));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("scc", FUNC(scc8030_device::rxb_w));
	rs232.cts_handler().set("scc", FUNC(scc8030_device::ctsb_w));

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_spkrdev).add_route(ALL_OUTPUTS, "mono", 0.05);
}

ROM_START( c900 )
	ROM_REGION16_BE( 0x8000, "roms", 0 )
	ROM_LOAD16_BYTE( "c 900 boot-h v 1.0.bin.u17", 0x0000, 0x4000, CRC(c3aa7fc1) SHA1(ff12dd100fa7b1e7e931e9a8ef4c4f5cc056e099) )
	ROM_LOAD16_BYTE( "c 900 boot-l v 1.0.bin.u18", 0x0001, 0x4000, CRC(0aa39272) SHA1(b2c5da4586d38fc66bb33aafeae4dbda36080f1e) )

	ROM_REGION( 0x2000, "fdc", 0 )
	ROM_LOAD( "s41_6-20-85.bin", 0x0000, 0x2000, CRC(ec245721) SHA1(4cc19014b4887833a56b1236dc5fe39cc5d7b5c3) )

	ROM_REGION( 0x1000, "chargen", 0 ) // this must be for the c900 terminal as the mainframe has no video output
	ROM_LOAD( "380217-01.u2", 0x0000, 0x1000, CRC(64cb4171) SHA1(e60d796170addfd27e2c33090f9c512c7e3f99f5) )
ROM_END

} // anonymous namespace


/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY      FULLNAME         FLAGS */
COMP( 1985, c900, 0,      0,      c900,    c900,  c900_state, empty_init, "Commodore", "Commodore 900", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



camirf1.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders:

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

Skeleton driver for Honeywell CAMIR-F1 wireless passive
infrared motion sensor with built-in colour camera.

Hardware based on a PIC18LF26K22 as main CPU with a Conexant
CX93610 (DIFT JPEG Encoder with a BT.656 Camera Interface and
Optional Microphone Input) and a IS25LQ040 Serial Flash.

PCB:
  _________    _    _________
 /         |__| |__|         \
|                             |
|            _____        _   |
|           |CAMERA------| |  |
|           |SENSOR------| |  |
|           |_____|------|_|  |
|            _____     _____  |
|           |CX93610  |PIC18LF26K22
|           |     |   |    |  |
|           |_____|   |    |  |
|                     |____|  |
|                  _____      |
|                 |IS25LQ040  |
|        Xtal                 |
|       27 MHz                |
|              ____           |
|             /IR  |          |
|            |SENSOR   __     |
|   ___      |____/   |__|    |
|  |___|                      |
|           _________         |
|__________|         |________|

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

#include "emu.h"
#include "cpu/pic16x8x/pic16x8x.h"

namespace {


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

	void camirf1(machine_config &config) ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
};

INPUT_PORTS_START( camirf1 )
INPUT_PORTS_END

void camirf1_state::camirf1(machine_config &config)
{
	PIC16F84(config, m_maincpu, 16_MHz_XTAL); // Actually a PIC18LF26K22
}

ROM_START( camirf1 )
	// ID = 00000000ffffffffh, CONFIG = 28130e003d81000fc00fe00fh
	ROM_REGION( 0x04280, "maincpu", 0 )
	ROM_LOAD( "pic18lf26k22_user.bin", 0x00000, 0x01000, CRC(47e4e6c6) SHA1(e0b1d0690cf991803673e9bcf2d244aa15c42feb) )
	ROM_LOAD( "pic18lf26k22_data.bin", 0x00000, 0x00400, CRC(b15001ef) SHA1(6d722abfb433fbf76f2a53b015febc42f4d638c2) )

	ROM_REGION( 0x80000, "sflash", 0 )
	ROM_LOAD( "is25lq040.bin",         0x00000, 0x80000, CRC(4458e03f) SHA1(52cf7001c90ae946ed00f4452a43523c12c81e9d) )
ROM_END

} // anonymous namespace


SYST( 2016, camirf1, 0, 0, camirf1, camirf1, camirf1_state, empty_init, "Honeywell", "CAMIR-F1", MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )



camplynx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

      Camputers Lynx

      2009-05 Skeleton driver.
      2015-10-23 Working

      The Lynx was an 8-bit British home computer that was first released
      in early 1983 as a 48 kB model.
      The designer of the Lynx was John Shireff and several models were
      available with 48 kB, 96 kB (from Sep 1983) or 128 kB RAM (from Dec
      1983). It was possible reach 192 kB with RAM expansions on-board.

      The machine was based around a Z80A CPU clocked at 4 MHz, and featured
      a Motorola 6845 as video controller. It was possible to run CP/M with
      the optional 5.25" floppy disk-drive on the 96 kB and 128 kB models.
      Approximately 30,000 Lynx units were sold world-wide.

      Camputers ceased trading in June 1984. Anston Technology took over in
      November the same year and a re-launch was planned but never happened.

      In June 1986, Anston sold everything - hardware, design rights and
      thousands of cassettes - to the National Lynx User Group. The group
      planned to produce a Super-Lynx but was too busy supplying spares and
      technical information to owners of existing models, and the project never
      came into being.

      Hardware info:
       - CPU: Zilog Z80A 4 MHz
       - CO-PROCESSOR: Motorola 6845 (CRT controller)
       - RAM: 48 kb, 96 kb or 128 kb depending on models (max. 192 kb)
       - ROM: 16 kb (48K version), 24 KB (96K and 128K versions)
       - TEXT MODES: 40 x 24, 80 x 24
       - GRAPHIC MODES: 256 x 248, 512 x 480
       - SOUND: one voice beeper

      Lynx 128 Memory Map

              | 0000    2000    4000    6000    8000    a000    c000     e000
              | 1fff    3fff    5fff    7fff    9fff    bfff    dfff     ffff
     -------------------------------------------------------------------------
              |                      |                        |       |
      Bank 0  |      BASIC ROM       |    Not Available       |  Ext  |  Ext
              |                      |                        |  ROM1 |  ROM2
     -------------------------------------------------------------------------
              |                      |
      Bank 1  |        STORE         |           Workspace RAM
              |                      |
     -------------------------------------------------------------------------
              |               |               |               |
      Bank 2  |       RED     |     BLUE      |     GREEN     |     Alt
              |               |               |               |    Green
     -------------------------------------------------------------------------
              |
      Bank 3  |              Available for Video Expansion
              |
     -------------------------------------------------------------------------
              |
      Bank 4  |              Available for User RAM Expansion
              |



    48k and 96k are basically the same machine. 128k is different. Most software for
    one won't work properly on the other, due to major hardware differences.

    This computer is weird, because it allows reads and writes from multiple banks
    at the same time. We can write to multiple banks, but we must limit ourselves
    to reading from the lowest bank selected.

    Notes:
    - The screen doesn't scroll. This is normal.
    - Variable names are case-sensitive.
    - Break key is the Escape key.
    - Typing a line number doesn't delete the line, you are in a calculator mode
      (for example, typing 67-5 will print 62)
    - DEL line-number deletes the line.
    - To edit a line, press Ctrl-E, it asks for line number, type it in,
      then arrows to move left-right, backspace to delete, press keys to insert them
    - If you entered a line and got a syntax error, press Ctrl-Q to edit it.
    - Cassette tapes made on 128k are a different speed to 48k tapes. To load a 128k
      tape on a 48k system, enter TAPE 3 before loading. (2,3,4,5 all seem to work).
    - When loading, there's no wildcard; you must specify the name.
    - INT should be activated by the MC6845 CURS pin, but this doesn't happen. Using
      VSYNC instead. Required for CP/M to boot.
    - Info about disks:
      - You must firstly do XROM to initialise the DOS ROM.
      - Then do EXT DIR with a Lynx-formatted disk, or EXT BOOT with a CP/M disk.
      - Then, on a Lynx-formatted disk, do EXT LOAD "name" or EXT MLOAD "name".

    TODO:
    - printer
    - joysticks
    - UART type COM8017 (48k,96k only) (not used by any programs)
    - There's a few games that are not perfectly perfect, but it runs at least
      as well as any other Lynx emulator.
    - Racer - Sideways scrolling incorrect.
    - Nuclear Invaders - User defined characters fail to be defined.
    - Card Index - Not enough RAM detected.

    Game bugs (reproducible in Jynx):
    - 3D Monster Craze: When attacked, garbage on screen
    - 3D Monster Craze: When you find the key, the game freezes
    - Ynxvaders: Colours of top 2 rows of invaders should be white and yellow
      but they show as magenta and red. After game ends, title screen has wrong
      colours.

    Game Hints:
      Most games have instructions or are quite obvious.
    - Power Blaster: Using debug.exe, change byte 1865 from FE to F2 to fix
      the loading. Then, arrows to turn, Shift to clean out a whole row of dots.
      You can then move into the vacated areas. Even though it says you have
      3 lives, you actually only have 1.
    - Treasure Island: Doesn't auto-run after loading main program, type RUN.
    - Backgammon: This is just the instructions. The game is missing.
    - LogiChess: The page at http://www.nascomhomepage.com/games/logichess.html
      should provide enough clues to enable you to work out how to play.

    Alternate ROMs for Lynx 96k:
    - Scorpion EXTensions
        OR
        XOR       - create new patterns and colours
        SON/SOFF  - scroll the screen
        SCROLL
        MCOPY
        VAR       - print values of variables used
        DIM       - print size of dimensioned arrays used
        LSTR$     - print length of all strings used
        OLD       - bring back NEWed programs
        ZERODIM   - zero all arrays
        ALTGREEN  - easily access the ALTernate GREEN BANK
        GREEN
        CLEAR     - clear all variables
        UMEM      - display amount of memory used
        VERSION   - display version
        FAST      - faster screen printing
        FTEXT     - fast 8*8 text
        FPRINT
        VAL
        BLOCK
        INSTR
        WSWAP
    - Danish EXTensions
        PAINT
        CAT
        FAST
        MULTI
        VARS
        RECOVER
        MSAVE
        ALARM
        TIMER
        WRUL
        RULON/RULOFF

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

#include "emu.h"

#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "machine/wd_fdc.h"
#include "sound/dac.h"
#include "video/mc6845.h"
#include "machine/ram.h"
#include "emupal.h"

#include "formats/camplynx_cas.h"
#include "formats/camplynx_dsk.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class camplynx_state : public driver_device
{
public:
	camplynx_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_palette(*this, "palette")
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_bankr(*this, "bankr%d", 0U)
		, m_cass(*this, "cassette")
		//, m_printer(*this, "centronics")
		, m_crtc(*this, "crtc")
		, m_dac(*this, "dac")
		, m_fdc(*this, "fdc")
		, m_floppy0(*this, "fdc:0")
		, m_floppy1(*this, "fdc:1")
	{ }

	void lynx_common(machine_config &config);
	void lynx_disk(machine_config &config);
	void lynx128k(machine_config &config);
	void lynx48k(machine_config &config);
	void lynx96k(machine_config &config);
	void init_lynx48k();
	void init_lynx128k();
	DECLARE_INPUT_CHANGED_MEMBER(brk_key);

private:
	void bank1_w(offs_t offset, u8 data);
	void bank6_w(offs_t offset, u8 data);
	void port58_w(u8 data); // drive select etc
	void port7f_w(u8 data); // banking 48k
	u8 port80_r(); // cassin for 48k
	void port80_w(u8 data); // control port 48k
	u8 port82_r(); // cassin for 128k
	void port82_w(u8 data); // banking 128k
	void port84_w(u8 data); // dac port 48k
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;
	static void camplynx_floppy_formats(format_registration &fr);
	MC6845_UPDATE_ROW(lynx48k_update_row);
	MC6845_UPDATE_ROW(lynx128k_update_row);
	void lynx128k_io(address_map &map) ATTR_COLD;
	void lynx128k_mem(address_map &map) ATTR_COLD;
	void lynx48k_io(address_map &map) ATTR_COLD;
	void lynx48k_mem(address_map &map) ATTR_COLD;
	void lynx96k_io(address_map &map) ATTR_COLD;
	u8 m_port58 = 0U;
	u8 m_port80 = 0U;
	u8 m_bankdata = 0U;
	u8 m_wbyte = 0U;
	u8 *m_p_ram = nullptr;
	bool m_is_128k = 0;
	required_device<palette_device> m_palette;
	required_device<z80_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_memory_bank_array<8> m_bankr;
	required_device<cassette_image_device> m_cass;
	//required_device<> m_printer;
	required_device<mc6845_device> m_crtc;
	required_device<dac_byte_interface> m_dac;
	optional_device<fd1793_device> m_fdc;
	optional_device<floppy_connector> m_floppy0;
	optional_device<floppy_connector> m_floppy1;
};

void camplynx_state::port7f_w(u8 data)
{
/*
d0 = write to bank 1
d1 = write to bank 2
d2 = write to bank 3
d3 = write to bank 4
d4 = read from bank 0 - roms
d5 = read from bank 1 - user ram
d6 = read from banks 2 and 3 - videoram
d7 = read from bank 4 */

	m_bankdata = data;
	data ^= 0x31; // make all lines active high
//printf("%s:%X\n", machine().describe_context().c_str(), data);
	// do writes
	m_wbyte = (data & 0x0f) | ((m_port80 & 0x0c) << 3);
	// do reads
	u8 rbyte = (data & 0x70) | (m_port80 & 0x0c);
	switch (rbyte)
	{
		case 0x00:
		case 0x04:
		case 0x08:
		case 0x0c:
		case 0x10:
		case 0x14:
		case 0x18:
		case 0x1c:
			m_bankr[0]->set_entry(0);
			m_bankr[1]->set_entry(1);
			m_bankr[2]->set_entry(2);
			m_bankr[3]->set_entry(3);
			m_bankr[4]->set_entry(0);
			m_bankr[5]->set_entry(1);
			m_bankr[6]->set_entry(2);
			m_bankr[7]->set_entry(7);
			break;
		case 0x20:
		case 0x24:
		case 0x28:
		case 0x2c:
			m_bankr[0]->set_entry(8);
			m_bankr[1]->set_entry(9);
			m_bankr[2]->set_entry(10);
			m_bankr[3]->set_entry(11);
			m_bankr[4]->set_entry(12);
			m_bankr[5]->set_entry(13);
			m_bankr[6]->set_entry(14);
			m_bankr[7]->set_entry(BIT(m_port58, 4) ? 15 : 7);
			break;
		case 0x30:
		case 0x34:
		case 0x38:
		case 0x3c:
			m_bankr[0]->set_entry(0);
			m_bankr[1]->set_entry(1);
			m_bankr[2]->set_entry(2);
			m_bankr[3]->set_entry(11);
			m_bankr[4]->set_entry(12);
			m_bankr[5]->set_entry(13);
			m_bankr[6]->set_entry(14);
			m_bankr[7]->set_entry(BIT(m_port58, 4) ? 15 : 7);
			break;
		case 0x44:
		case 0x64:
			m_bankr[0]->set_entry(24);
			m_bankr[1]->set_entry(25);
			m_bankr[2]->set_entry(26);
			m_bankr[3]->set_entry(27);
			m_bankr[4]->set_entry(28);
			m_bankr[5]->set_entry(29);
			m_bankr[6]->set_entry(30);
			m_bankr[7]->set_entry(31);
			break;
		case 0x40:
		case 0x60:
		case 0x48:
		case 0x68:
		case 0x4c:
		case 0x6c:
			m_bankr[0]->set_entry(16);
			m_bankr[1]->set_entry(17);
			m_bankr[2]->set_entry(18);
			m_bankr[3]->set_entry(19);
			m_bankr[4]->set_entry(20);
			m_bankr[5]->set_entry(21);
			m_bankr[6]->set_entry(22);
			m_bankr[7]->set_entry(23);
			break;
		case 0x54:
		case 0x74:
			m_bankr[0]->set_entry(0);
			m_bankr[1]->set_entry(1);
			m_bankr[2]->set_entry(2);
			m_bankr[3]->set_entry(27);
			m_bankr[4]->set_entry(28);
			m_bankr[5]->set_entry(29);
			m_bankr[6]->set_entry(30);
			m_bankr[7]->set_entry(31);
			break;
		case 0x50:
		case 0x70:
		case 0x58:
		case 0x78:
		case 0x5c:
		case 0x7c:
			m_bankr[0]->set_entry(0);
			m_bankr[1]->set_entry(1);
			m_bankr[2]->set_entry(2);
			m_bankr[3]->set_entry(19);
			m_bankr[4]->set_entry(20);
			m_bankr[5]->set_entry(21);
			m_bankr[6]->set_entry(22);
			m_bankr[7]->set_entry(23);
			break;
		default:
			printf("Banking code %X not handled\n", m_bankdata);
	}
}

void camplynx_state::port82_w(u8 data)
{
/* Almost the same as the 48k, except the bit order is reversed.
d7 = write to bank 1
d6 = write to bank 2
d5 = colsel - allow r/w to bank 3??
d4 = write to bank 4
d3 = read from bank 0 - roms
d2 = read from bank 1 - user ram
d1 = read from bank 2 - videoram
d0 = read from bank 4 */
	m_bankdata = data;
	data ^= 0x8c; // make all lines active high
	// do writes
	m_wbyte = bitswap<8>(data, 0, 0, 0, 0, 4, 5, 6, 7) & 0x0f; // rearrange to 1,2,3,4
	// do reads
	u8 rbyte = bitswap<8>(data, 0, 0, 0, 0, 0, 1, 2, 3) & 0x0f; // rearrange to 0,1,2,4
	if (BIT(rbyte, 1))
		rbyte &= 0x07; // remove 4 if 1 selected (AND gate in IC82)
//printf("%s:%X:%X:%X\n", machine().describe_context().c_str(), data, rbyte, m_wbyte);
	switch (rbyte)
	{
		case 0x00:
		case 0x01:
			m_bankr[0]->set_entry(0);
			m_bankr[1]->set_entry(1);
			m_bankr[2]->set_entry(2);
			m_bankr[3]->set_entry(3);
			m_bankr[4]->set_entry(4);
			m_bankr[5]->set_entry(5);
			m_bankr[6]->set_entry(6);
			m_bankr[7]->set_entry(7);
			break;
		case 0x02:
		case 0x0a:
			m_bankr[0]->set_entry(8);
			m_bankr[1]->set_entry(9);
			m_bankr[2]->set_entry(10);
			m_bankr[3]->set_entry(11);
			m_bankr[4]->set_entry(12);
			m_bankr[5]->set_entry(13);
			m_bankr[6]->set_entry(14);
			m_bankr[7]->set_entry(15);
			break;
		case 0x03:
		case 0x07:
		case 0x0b:
		case 0x0f:
			m_bankr[0]->set_entry(0);
			m_bankr[1]->set_entry(1);
			m_bankr[2]->set_entry(2);
			m_bankr[3]->set_entry(11);
			m_bankr[4]->set_entry(12);
			m_bankr[5]->set_entry(13);
			m_bankr[6]->set_entry(14);
			m_bankr[7]->set_entry(BIT(m_port58, 4) ? 15 : 7);
			break;
		case 0x04:
		case 0x06:
		case 0x0c:
		case 0x0e:
			m_bankr[0]->set_entry(16);
			m_bankr[1]->set_entry(17);
			m_bankr[2]->set_entry(18);
			m_bankr[3]->set_entry(19);
			m_bankr[4]->set_entry(20);
			m_bankr[5]->set_entry(21);
			m_bankr[6]->set_entry(22);
			m_bankr[7]->set_entry(23);
			break;
		case 0x05:
		case 0x0d:
			m_bankr[0]->set_entry(0);
			m_bankr[1]->set_entry(1);
			m_bankr[2]->set_entry(2);
			m_bankr[3]->set_entry(19);
			m_bankr[4]->set_entry(20);
			m_bankr[5]->set_entry(21);
			m_bankr[6]->set_entry(22);
			m_bankr[7]->set_entry(23);
			break;
		case 0x08:
			m_bankr[0]->set_entry(32);
			m_bankr[1]->set_entry(33);
			m_bankr[2]->set_entry(34);
			m_bankr[3]->set_entry(35);
			m_bankr[4]->set_entry(36);
			m_bankr[5]->set_entry(37);
			m_bankr[6]->set_entry(38);
			m_bankr[7]->set_entry(39);
			break;
		case 0x09:
			m_bankr[0]->set_entry(0);
			m_bankr[1]->set_entry(1);
			m_bankr[2]->set_entry(2);
			m_bankr[3]->set_entry(35);
			m_bankr[4]->set_entry(36);
			m_bankr[5]->set_entry(37);
			m_bankr[6]->set_entry(38);
			m_bankr[7]->set_entry(39);
			break;
		default:
			printf("Banking code %X not handled\n", m_bankdata);
	}
}

void camplynx_state::lynx48k_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).bankr("bankr0");
	map(0x2000, 0x3fff).bankr("bankr1");
	map(0x4000, 0x5fff).bankr("bankr2");
	map(0x6000, 0x7fff).bankr("bankr3");
	map(0x8000, 0x9fff).bankr("bankr4");
	map(0xa000, 0xbfff).bankr("bankr5");
	map(0xc000, 0xdfff).bankr("bankr6");
	map(0xe000, 0xffff).bankr("bankr7");
	map(0x0000, 0xffff).w(FUNC(camplynx_state::bank6_w));
}

void camplynx_state::lynx128k_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).bankr("bankr0");
	map(0x2000, 0x3fff).bankr("bankr1");
	map(0x4000, 0x5fff).bankr("bankr2");
	map(0x6000, 0x7fff).bankr("bankr3");
	map(0x8000, 0x9fff).bankr("bankr4");
	map(0xa000, 0xbfff).bankr("bankr5");
	map(0xc000, 0xdfff).bankr("bankr6");
	map(0xe000, 0xffff).bankr("bankr7");
	map(0x0000, 0xffff).w(FUNC(camplynx_state::bank1_w));
}

void camplynx_state::lynx48k_io(address_map &map)
{
	map.unmap_value_high();
	map(0x007f, 0x007f).mirror(0xff80).w(FUNC(camplynx_state::port7f_w));
	map(0x0080, 0x0080).mirror(0xff00).w(FUNC(camplynx_state::port80_w));
	map(0x0080, 0x0080).mirror(0xf000).r(FUNC(camplynx_state::port80_r));
	map(0x0180, 0x0180).mirror(0xf000).portr("LINE1");
	map(0x0280, 0x0280).mirror(0xf000).portr("LINE2");
	map(0x0380, 0x0380).mirror(0xf000).portr("LINE3");
	map(0x0480, 0x0480).mirror(0xf000).portr("LINE4");
	map(0x0580, 0x0580).mirror(0xf000).portr("LINE5");
	map(0x0680, 0x0680).mirror(0xf000).portr("LINE6");
	map(0x0780, 0x0780).mirror(0xf000).portr("LINE7");
	map(0x0880, 0x0880).mirror(0xf000).portr("LINE8");
	map(0x0980, 0x0980).mirror(0xf000).portr("LINE9");
	map(0x0084, 0x0084).mirror(0xff00).w(FUNC(camplynx_state::port84_w));
	map(0x0086, 0x0086).mirror(0xff00).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x0087, 0x0087).mirror(0xff00).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
}

void camplynx_state::lynx96k_io(address_map &map)
{
	lynx48k_io(map);
	map(0x0050, 0x0053).mirror(0xff80).r("fdc", FUNC(fd1793_device::read));
	map(0x0054, 0x0057).mirror(0xff80).w("fdc", FUNC(fd1793_device::write));
	map(0x0058, 0x0058).mirror(0xff80).w(FUNC(camplynx_state::port58_w));
}

void camplynx_state::lynx128k_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0050, 0x0053).mirror(0xff80).r("fdc", FUNC(fd1793_device::read));
	map(0x0054, 0x0057).mirror(0xff80).w("fdc", FUNC(fd1793_device::write));
	map(0x0058, 0x0058).mirror(0xff80).w(FUNC(camplynx_state::port58_w));
//  map(0x007a, 0x007b).mirror(0xff80).r(FUNC(camplynx_state::lynx128k_joysticks_r));
//  map(0x007c, 0x007c).mirror(0xff80).r(FUNC(camplynx_state::lynx128k_printer_r));
//  map(0x007d, 0x007d).mirror(0xff80).w(FUNC(camplynx_state::lynx128k_printer_init_w)); // this is rw
//  map(0x007e, 0x007e).mirror(0xff80).w(FUNC(camplynx_state::lynx128k_printer_w));
	map(0x0080, 0x0080).mirror(0xff00).w(FUNC(camplynx_state::port80_w));
	map(0x0080, 0x0080).mirror(0xf000).portr("LINE0");
	map(0x0180, 0x0180).mirror(0xf000).portr("LINE1");
	map(0x0280, 0x0280).mirror(0xf000).portr("LINE2");
	map(0x0380, 0x0380).mirror(0xf000).portr("LINE3");
	map(0x0480, 0x0480).mirror(0xf000).portr("LINE4");
	map(0x0580, 0x0580).mirror(0xf000).portr("LINE5");
	map(0x0680, 0x0680).mirror(0xf000).portr("LINE6");
	map(0x0780, 0x0780).mirror(0xf000).portr("LINE7");
	map(0x0880, 0x0880).mirror(0xf000).portr("LINE8");
	map(0x0980, 0x0980).mirror(0xf000).portr("LINE9");
	map(0x0082, 0x0082).mirror(0xff00).rw(FUNC(camplynx_state::port82_r), FUNC(camplynx_state::port82_w)); // read=serial buffer
	map(0x0084, 0x0084).mirror(0xff00).w(FUNC(camplynx_state::port84_w));
	map(0x0086, 0x0086).mirror(0xff00).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x0087, 0x0087).mirror(0xff00).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
}

/* Input ports */
static INPUT_PORTS_START( lynx48k )
	PORT_START("LINE0")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(camplynx_state::brk_key), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x0e, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 !") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_START("LINE1")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C CONT") PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D DEL") PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X WEND") PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E DATA") PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 $") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 #") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_START("LINE2")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Control") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A AUTO") PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S STOP") PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z RESTORE") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W WHILE") PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q REM") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 \"") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR(34)
	PORT_START("LINE3")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F DEFPROC") PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G GOTO") PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V VERIFY") PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T TRACE") PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R REPEAT") PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 %") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_START("LINE4")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B BEEP") PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N NEXT") PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(32)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H GOSUB") PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y RUN") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 &") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_START("LINE5")
	PORT_BIT(0xd0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J LABEL") PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M RETURN") PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U UNTIL") PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 (") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 \'") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR(39)
	PORT_START("LINE6")
	PORT_BIT(0xd0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K MON") PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O ENDPROC") PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I INPUT") PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 )") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_START("LINE7")
	PORT_BIT(0xd0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("; +") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L LIST") PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P PROC") PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0 _") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_START("LINE8")
	PORT_BIT(0xd0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(": *") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR(0x5e)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@ \\") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('@') PORT_CHAR('\\')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("- =") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_START("LINE9")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("End") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR(0x7f)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Delete") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
INPUT_PORTS_END

INPUT_CHANGED_MEMBER( camplynx_state::brk_key )
{
	m_maincpu->set_input_line(0, newval ? CLEAR_LINE : ASSERT_LINE);
}

void camplynx_state::bank1_w(offs_t offset, u8 data)
{
	if (BIT(m_wbyte, 0))
		m_p_ram[offset+0x10000] = data;
	if ((m_wbyte & 0x22) == 0x02)
		m_p_ram[offset+0x20000] = data;
	if ((m_wbyte & 0x44) == 0x04)
		m_p_ram[offset+0x30000] = data;
	if (m_is_128k && BIT(m_wbyte, 3))
		m_p_ram[offset+0x40000] = data;
}

void camplynx_state::bank6_w(offs_t offset, u8 data)
{
	if (BIT(m_wbyte, 0))
		m_p_ram[offset+0x10000] = data;

	offset &= 0x5fff;

	if ((m_wbyte & 0x22) == 0x02)
	{
		m_p_ram[offset | 0x20000] = data;
		m_p_ram[offset | 0x22000] = data;
		m_p_ram[offset | 0x28000] = data;
		m_p_ram[offset | 0x2a000] = data;
	}

	if ((m_wbyte & 0x44) == 0x04)
	{
		m_p_ram[offset | 0x30000] = data;
		m_p_ram[offset | 0x32000] = data;
		m_p_ram[offset | 0x38000] = data;
		m_p_ram[offset | 0x3a000] = data;
	}
}

u8 camplynx_state::port80_r()
{
	u8 data = ioport("LINE0")->read();
	// when reading tape, bit 0 becomes cass-in signal
	if (BIT(m_port80, 1))
	{
		data &= 0xfe;
		data |= (m_cass->input() > +0.02) ? 0 : 1;
	}
	return data;
}

/* 128k:
d7 = serial data out
d6 = interline control
d5 = cpu access
d4 = alt green or normal green (afaik this only affects the display not the banking)
d3 = cass motor on
d2 = cass enable
d1 = serial h/s out
d0 = speaker */
void camplynx_state::port80_w(u8 data)
{
	m_port80 = data;
	m_cass->change_state( BIT(data, (m_is_128k) ? 3 : 1) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
	if (!m_is_128k)
		port7f_w(m_bankdata);
}

/* DAC port (6-bit). If writing cassette, output goes to tape as a sine wave, otherwise it goes to speaker.
   There is code below to write as a sine wave or a square wave, both work and can be loaded successfully.
   However the PALE emulator cannot load either of them, although it loads its own output.
   MAME can load PALE's wav files though.
   Currently square wave output is selected. */

void camplynx_state::port84_w(u8 data)
{
	if (BIT(m_port80, (m_is_128k) ? 3 : 1)) // for 128k, bit 2 might be ok too
	{
		// Sine wave output
		//float t = (float)(unsigned)data - 32.0f;
		//m_cass->output(t/31);

		// Square wave output
		m_cass->output(BIT(data, 5) ? -1.0 : +1.0);
	}
	else    // speaker output
		m_dac->write(data);
}

/*
d7 = clock
d2 = cass-in
d1 = serial data in
d0 = serial h/s in */
u8 camplynx_state::port82_r()
{
	u8 data = 0xfb; // guess
	data |= (m_cass->input() > +0.02) ? 4 : 0;
	return data;
}

void camplynx_state::machine_reset()
{
	m_port58 = 0;
	m_port80 = 0;
	if (m_is_128k)
		port82_w( 0 );
	else
		port7f_w( 0 );
	m_maincpu->reset();
}

MC6845_UPDATE_ROW( camplynx_state::lynx48k_update_row )
{
	uint32_t green_bank, *p = &bitmap.pix(y);
	uint16_t const mem = ((ma << 2) + (ra << 5)) & 0x1fff;

	// determine green bank
	if (BIT(m_port80, 4))
		green_bank = 0x38000 + mem; // alt green
	else
		green_bank = 0x3c000 + mem; // normal green

	for (u8 x = 0; x < x_count; x++)
	{
		u8 const r = m_p_ram[0x2c000 + mem + x];
		u8 const b = m_p_ram[0x28000 + mem + x];
		u8 const g = m_p_ram[green_bank + x];

		*p++ = m_palette->pen_color((BIT(b, 7) << 2) | (BIT(g, 7) << 1) | (BIT(r, 7)));
		*p++ = m_palette->pen_color((BIT(b, 6) << 2) | (BIT(g, 6) << 1) | (BIT(r, 6)));
		*p++ = m_palette->pen_color((BIT(b, 5) << 2) | (BIT(g, 5) << 1) | (BIT(r, 5)));
		*p++ = m_palette->pen_color((BIT(b, 4) << 2) | (BIT(g, 4) << 1) | (BIT(r, 4)));
		*p++ = m_palette->pen_color((BIT(b, 3) << 2) | (BIT(g, 3) << 1) | (BIT(r, 3)));
		*p++ = m_palette->pen_color((BIT(b, 2) << 2) | (BIT(g, 2) << 1) | (BIT(r, 2)));
		*p++ = m_palette->pen_color((BIT(b, 1) << 2) | (BIT(g, 1) << 1) | (BIT(r, 1)));
		*p++ = m_palette->pen_color((BIT(b, 0) << 2) | (BIT(g, 0) << 1) | (BIT(r, 0)));
	}
}

MC6845_UPDATE_ROW( camplynx_state::lynx128k_update_row )
{
	uint32_t green_bank, *p = &bitmap.pix(y);
	uint16_t const mem = ((ma << 2) + (ra << 6)) & 0x3fff;

	// determine green bank
	if (BIT(m_port80, 4))
		green_bank = 0x2c000 + mem; // alt green
	else
		green_bank = 0x28000 + mem; // normal green

	for (u8 x = 0; x < x_count; x++)
	{
		u8 const r = m_p_ram[0x20000 + mem + x];
		u8 const b = m_p_ram[0x24000 + mem + x];
		u8 const g = m_p_ram[green_bank + x];

		*p++ = m_palette->pen_color((BIT(b, 7) << 2) | (BIT(g, 7) << 1) | (BIT(r, 7)));
		*p++ = m_palette->pen_color((BIT(b, 6) << 2) | (BIT(g, 6) << 1) | (BIT(r, 6)));
		*p++ = m_palette->pen_color((BIT(b, 5) << 2) | (BIT(g, 5) << 1) | (BIT(r, 5)));
		*p++ = m_palette->pen_color((BIT(b, 4) << 2) | (BIT(g, 4) << 1) | (BIT(r, 4)));
		*p++ = m_palette->pen_color((BIT(b, 3) << 2) | (BIT(g, 3) << 1) | (BIT(r, 3)));
		*p++ = m_palette->pen_color((BIT(b, 2) << 2) | (BIT(g, 2) << 1) | (BIT(r, 2)));
		*p++ = m_palette->pen_color((BIT(b, 1) << 2) | (BIT(g, 1) << 1) | (BIT(r, 1)));
		*p++ = m_palette->pen_color((BIT(b, 0) << 2) | (BIT(g, 0) << 1) | (BIT(r, 0)));
	}
}

void camplynx_state::port58_w(u8 data)
{
/*
d0,d1 = drive select
d2 = side
d3 = motor
d4 = eprom
d5 = not used
d6 = no precomp
d7 = 125ns or 250ns */

	if (BIT(m_port58, 4) ^ BIT(data, 4))
	{
		m_port58 = data;
		if (m_is_128k)
			port82_w(m_bankdata);
		else
			port7f_w(m_bankdata);
	}

	floppy_image_device *floppy = nullptr;
	if ((data & 3) == 0)
		floppy = m_floppy0->get_device();
	else if ((data & 3) == 1)
		floppy = m_floppy1->get_device();

	m_fdc->set_floppy(floppy);

	if (floppy)
		floppy->ss_w(BIT(data, 2));

	m_floppy0->get_device()->mon_w(BIT(data, 3));
	m_floppy1->get_device()->mon_w(BIT(data, 3));
}

void camplynx_state::machine_start()
{
	save_item(NAME(m_port58));
	save_item(NAME(m_port80));
	save_item(NAME(m_bankdata));
	save_item(NAME(m_wbyte));
	save_item(NAME(m_is_128k));
}

void camplynx_state::camplynx_floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_CAMPLYNX_FORMAT);
}

static void camplynx_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

void camplynx_state::lynx_common(machine_config &config)
{
	PALETTE(config, m_palette, palette_device::RGB_3BIT);

	/* sound hardware */
	SPEAKER(config, "speaker").front_center();
	DAC_6BIT_R2R(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.375); // unknown DAC
}

void camplynx_state::lynx_disk(machine_config &config)
{
	FD1793(config, m_fdc, 24_MHz_XTAL / 24);
	FLOPPY_CONNECTOR(config, m_floppy0, camplynx_floppies, "525qd", camplynx_state::camplynx_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy1, camplynx_floppies, "525qd", camplynx_state::camplynx_floppy_formats).enable_sound(true);
}

void camplynx_state::lynx48k(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 24_MHz_XTAL / 6);
	m_maincpu->set_addrmap(AS_PROGRAM, &camplynx_state::lynx48k_mem);
	m_maincpu->set_addrmap(AS_IO, &camplynx_state::lynx48k_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(512, 480);
	screen.set_visarea_full();
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	lynx_common(config);

	CASSETTE(config, m_cass);
	m_cass->set_formats(lynx48k_cassette_formats);
	m_cass->set_default_state(CASSETTE_PLAY | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_DISABLED);
	m_cass->add_route(ALL_OUTPUTS, "speaker", 0.05);
	m_cass->set_interface("camplynx_cass");

	/* devices */
	MC6845(config, m_crtc, 12_MHz_XTAL / 8);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(camplynx_state::lynx48k_update_row));
	m_crtc->out_vsync_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("256K");

	/* software lists */
	SOFTWARE_LIST(config, "cass_list").set_original("camplynx_cass");
}

void camplynx_state::lynx96k(machine_config &config)
{
	lynx48k(config);
	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_IO, &camplynx_state::lynx96k_io);

	lynx_disk(config);

	/* software lists */
	SOFTWARE_LIST(config, "flop_list").set_original("camplynx_flop").set_filter("96K");
}

void camplynx_state::lynx128k(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 24_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &camplynx_state::lynx128k_mem);
	m_maincpu->set_addrmap(AS_IO, &camplynx_state::lynx128k_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(512, 480);
	screen.set_visarea_full();
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	lynx_common(config);

	CASSETTE(config, m_cass);
	m_cass->set_formats(lynx128k_cassette_formats);
	m_cass->set_default_state(CASSETTE_PLAY | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_DISABLED);
	m_cass->add_route(ALL_OUTPUTS, "speaker", 0.05);
	m_cass->set_interface("camplynx_cass");

	/* devices */
	MC6845(config, m_crtc, 12_MHz_XTAL / 8);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(camplynx_state::lynx128k_update_row));
	m_crtc->out_vsync_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("320K");

	lynx_disk(config);

	/* software lists */
	SOFTWARE_LIST(config, "cass_list").set_original("camplynx_cass");
	SOFTWARE_LIST(config, "flop_list").set_original("camplynx_flop").set_filter("128K");
}

void camplynx_state::init_lynx48k()
{
	m_is_128k = false;
	u8 *m = memregion("maincpu")->base();
	m_p_ram = m_ram->pointer();
	for (auto &bank : m_bankr)
		bank->configure_entries(0, 32, m_p_ram, 0x2000);
	for (u8 i = 0; i < 8; i++)
		for (auto &bank : m_bankr)
			bank->configure_entry(i, m+i*0x2000);
}

void camplynx_state::init_lynx128k()
{
	m_is_128k = true;
	u8 *m = memregion("maincpu")->base();
	m_p_ram = m_ram->pointer();
	for (auto &bank : m_bankr)
		bank->configure_entries(0, 40, m_p_ram, 0x2000);
	for (u8 i = 0; i < 8; i++)
		for (auto &bank : m_bankr)
			bank->configure_entry(i, m+i*0x2000);
}


/* ROM definition */
ROM_START( lynx48k )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS("1")
	ROM_SYSTEM_BIOS(0, "1", "Set1")
	ROMX_LOAD( "lynx48-1.ic46", 0x0000, 0x2000, CRC(56feec44) SHA1(7ded5184561168e159a30fa8e9d3fde5e52aa91a), ROM_BIOS(0) )
	ROMX_LOAD( "lynx48-2.ic45", 0x2000, 0x2000, CRC(d894562e) SHA1(c08a78ecb4eb05baa4c52488fce3648cd2688744), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "2", "Set2")
	ROMX_LOAD( "lynx4811.ic46", 0x0000, 0x2000, CRC(a933e577) SHA1(c7b30a28d99b38dbe63a1314c78e3e614287143b), ROM_BIOS(1) )
	ROMX_LOAD( "lynx4812.ic45", 0x2000, 0x2000, CRC(3d3fdd0e) SHA1(259d124f05367a96f891790f9418cc9c7798e2f8), ROM_BIOS(1) )
ROM_END

ROM_START( lynx96k )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "lynx9646.ic46",  0x0000, 0x2000, CRC(f86c5514) SHA1(77a4af7557382003d697d08f364839e2dc28f063) )
	ROM_LOAD( "lynx9645.ic45",  0x2000, 0x2000, CRC(f596b9a3) SHA1(3fca46bd68422d34c6cd801dd904507e52bd8846) )
	ROM_DEFAULT_BIOS("orig")
	ROM_SYSTEM_BIOS(0, "orig", "Original")
	ROMX_LOAD( "lynx9644.ic44", 0x4000, 0x1000, CRC(4b96b0de) SHA1(c372a8d26399b9b45e615b674d61ccda76491b8b), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "scorp", "Scorpion") /* Scorpion ROM v2.1 03/86 (Reading Lynx User Group) */
	ROMX_LOAD( "skorprom.ic44", 0x4000, 0x2000, CRC(698d3de9) SHA1(c707bdcecef79774c2a8a23d1f3e9ba382cb9304), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS(2, "danish", "Danish")
	ROMX_LOAD( "danish96k3.ic44", 0x4000, 0x2000, CRC(795c22ea) SHA1(0a57394cd986c5b338b38d514e894bace7f6e47b), ROM_BIOS(2) )
	ROM_LOAD( "dosrom.rom",    0xe000, 0x2000, CRC(011e106a) SHA1(e77f0ca99790551a7122945f3194516b2390fb69) )
ROM_END

ROM_START( lynx128k )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "lynx128-1.ic1", 0x0000, 0x2000, CRC(65d292ce) SHA1(36567c2fbd9cf72f758e8cb80c21cb4d82040752) )
	ROM_LOAD( "lynx128-2.ic2", 0x2000, 0x2000, CRC(23288773) SHA1(e12a7ebea3fae5eb375c03e848dbb81070d9d189) )
	ROM_LOAD( "lynx128-3.ic3", 0x4000, 0x2000, CRC(9827b9e9) SHA1(1092367b2af51c72ce9be367179240d692aeb131) )
	ROM_LOAD( "dosrom.rom",    0xe000, 0x2000, CRC(011e106a) SHA1(e77f0ca99790551a7122945f3194516b2390fb69) )
ROM_END

} // anonymous namespace


/* Driver */
/*    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT    CLASS           INIT           COMPANY      FULLNAME     FLAGS */
COMP( 1983, lynx48k,  0,       0,      lynx48k,  lynx48k, camplynx_state, init_lynx48k,  "Camputers", "Lynx 48k",  MACHINE_SUPPORTS_SAVE )
COMP( 1983, lynx96k,  lynx48k, 0,      lynx96k,  lynx48k, camplynx_state, init_lynx48k,  "Camputers", "Lynx 96k",  MACHINE_SUPPORTS_SAVE )
COMP( 1983, lynx128k, lynx48k, 0,      lynx128k, lynx48k, camplynx_state, init_lynx128k, "Camputers", "Lynx 128k", MACHINE_SUPPORTS_SAVE )



candela.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Joakim Larsson Edstrom
/*
 *
 * History of Candela Data AB
 *---------------------------
 * The Candela computer was designed to be the big breakthough and developed by Candela Data AB, "a Didact Company".
 * The Candela system was based around a main unit that could run OS-9 or Flex and a terminal unit that had a
 * proprietary software including CDBASIC. The Candela system lost the battle of the Swedish schools to
 * the Compis computer by TeleNova which was based on CP/M initially.  Later both lost to IBM PC as we know.
 * Candela Data continued to sell their system to the swedish industry without major successes despite great
 * innovation and spririt.
 *
 * Misc links about the boards supported by this driver.
 *-----------------------------------------------------
 * http://frakaday.blogspot.com/p/candela.html
 * http://elektronikforumet.com/forum/viewtopic.php?f=11&t=51424
 *
 *  TODO:
 *  Candela designs:   can09t, can09
 * -------------------------------------
 *  - Add PCB layouts   OK
 *  - Dump ROM:s,       OK      OK
 *  - Keyboard
 *  - Display/CRT
 *  - Clickable Artwork
 *  - Sound
 *  - Cassette i/f
 *  - Expansion bus
 *  - Expansion overlay
 *  - Interrupts
 *  - Serial            OK
 ****************************************************************************/

#include "emu.h"
#include "cpu/m6809/m6809.h"
#include "machine/6821pia.h"
#include "machine/6840ptm.h"
#include "machine/6850acia.h"
#include "machine/clock.h"
#include "machine/ram.h"
#include "machine/wd_fdc.h"
#include "video/mc6845.h"

// Features
#include "imagedev/cassette.h"
#include "bus/rs232/rs232.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

#define LOG_SETUP   (1U << 1)
#define LOG_SCAN    (1U << 2)
#define LOG_BANK    (1U << 3)
#define LOG_SCREEN  (1U << 4)
#define LOG_READ    (1U << 5)
#define LOG_CS      (1U << 6)
#define LOG_PLA     (1U << 7)
#define LOG_PROM    (1U << 8)

//#define VERBOSE (LOG_READ | LOG_GENERAL | LOG_SETUP | LOG_PLA | LOG_BANK)
//#define LOG_OUTPUT_FUNC printf
#include "logmacro.h"

#define LOGSETUP(...)   LOGMASKED(LOG_SETUP,   __VA_ARGS__)
#define LOGSCAN(...)    LOGMASKED(LOG_SCAN,    __VA_ARGS__)
#define LOGBANK(...)    LOGMASKED(LOG_BANK,    __VA_ARGS__)
#define LOGSCREEN(...)  LOGMASKED(LOG_SCREEN,  __VA_ARGS__)
#define LOGR(...)       LOGMASKED(LOG_READ,    __VA_ARGS__)
#define LOGCS(...)      LOGMASKED(LOG_CS,      __VA_ARGS__)
#define LOGPLA(...)     LOGMASKED(LOG_PLA,     __VA_ARGS__)
#define LOGPROM(...)    LOGMASKED(LOG_PROM,    __VA_ARGS__)

#ifdef _MSC_VER
#define FUNCNAME __func__
#else
#define FUNCNAME __PRETTY_FUNCTION__
#endif


namespace {

#define PIA1_TAG "pia1"
#define PIA2_TAG "pia2"
#define PIA3_TAG "pia3"
#define PIA4_TAG "pia4"

/*
 *         The CAN09 v1.4 CPU board                                                  The CAN09 ROM and AD/DA board
 *       +-------------------------------------------------------------+  ___     +----------------------------------------------------------+
 *      +-+  +---+   +------+ +-----++-----+     +-----++-----+        |_|   |   +-+   +-----++-----++-----++-----++-----++-----+            |
 *      | |  |RST|   |      | | ROM ||     |+---+| RAM ||     | +--+   | |   |   | |J2 |PROM7||PROM6||PROM5||PROM4||PROM3||PROM2|  +---+     |
 *      | |  +---+   |      | |27256||62256||74 ||58256||62256| |CN|   | |   |   | |   |empty||empty||empty||empty||empty||empty|  |74 |     |
 *      | |      O   | 6809 | | MON ||empty||245||     ||empty| |J7|   | |   |   | |  o|     ||     ||     ||     ||     ||     |  |393|     |
 *      | |    J2O   |      | | 58B ||     ||   ||     ||     | |  |   | |   |   | |  o|     ||     ||     ||     ||     ||     |  |   |     |
 *      | |      O   |      | |     ||     ||   ||     ||     | |  |   | |   |   | |  o+-----++-----++-----++-----++-----++-----+  +---+     |
 *      | |      O   |      | +-----++-----++---++-----++-----+ +--+   | |EUR|   | |  o        ______                              +---+     |
 *      | |J1    O   |      |      +-------+         +-----+ +-----+   | |   |   | |  oJ1     | 74138|                             |74 |     |
 *      +-+      O   |      |      | 74138 |         |     | |     |   | |CN |   +-+  o       +------+                             |393|     |
 *      +-+      O +-+------+      +-------+  +-----+|     | |     |   | |J4 |  J4|8  o   +-----------------+  +-----------------+ |   |     |
 *      | |J6    O |74165 |        +--++-----+|     || PIA | | PIA |   | |   |    |8  o   | PIA 6821        |  | PIA 6821        | +---+     |
 *      | |      O +------+   +---+|CN||     || PTM || 6821| | 6821|   | |   |   +-+  o   |                 |  |                 |           |
 *      | |      O  VR1 VR2   |82S||J8||     || 6840||     | |     |   | |   |   | |  o   +-----------------+  +-----------------+           |
 *      | |      O +------+   |123||  ||ACIA ||     ||     | |     |   | |   |   | |  o   +-------++-------+   +--------+   +--------+       |
 *      +-+      O |7405  |   |   ||  || 6850||     ||     | |     |   | |   |   | |  o   |MPD1603|| 14051 |   | TL501  |   | 74LS02 |       |
 *      _===       +------+   ++--++--+|     ||     ||     | |     |   | |   |   | |      +-------++-------+   +--------+   +--------+       |
 * comp _|=        |74132 |    | 7400 |+----+++-----++-----+ +-----+   |_|   |   | |J3       +-------+       +---------+  +----------+       |
 * video ===       +------+    +------+     |MAX232 |                  | |___|   +-+         |MC34004|       |AD7528   |  |ADC0820   |       |
 *       +----------------------------------+-------+------------------+          +----------+-------+-------+---------+--+----------+-------+
 *
 */
/*
 * Candela CAN09 SBC
 * TODO:
 * - Map additional PIA:s on the ROM board
 * - Vram and screen
 * - Keyboard
 * - LCD
 * - AD/DA support for related BASIC functions
 * - Promett rom cartridge support
 */

#define SYSPIA_TAG PIA1_TAG
#define USRPIA_TAG PIA2_TAG

// Start offset for each device segment in bank array
#define RAM0 0x00
#define RAM1 0x08
#define PROM 0x10
#define RAM2 0x18
#define IO   0x1a

/* Candela CAN09 driver class */
  class can09t_state :  public driver_device
{
public:
	can09t_state(const machine_config &mconfig, device_type type, const char * tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_syspia(*this, SYSPIA_TAG)
		, m_usrpia(*this, USRPIA_TAG)
		, m_pia3(*this, PIA3_TAG)
		, m_pia4(*this, PIA4_TAG)
		, m_ptm(*this, "ptm")
		, m_acia(*this, "acia")
		, m_cass(*this, "cassette")
		, m_banksel(1)
	{ }
	required_device<cpu_device> m_maincpu;
	virtual void machine_start() override ATTR_COLD;
	uint8_t read(offs_t offset);
	void write(offs_t offset, uint8_t data);
	uint8_t syspia_A_r();
	uint8_t syspia_B_r();
	void syspia_B_w(uint8_t data);
	void syspia_cb2_w(int state);
	void usrpia_cb2_w(int state);
	void write_acia_clock(int state);
	void can09t(machine_config &config);
	void can09t_map(address_map &map) ATTR_COLD;
protected:
	required_device<pia6821_device> m_syspia;
	required_device<pia6821_device> m_usrpia;
	required_device<pia6821_device> m_pia3;
	required_device<pia6821_device> m_pia4;
	required_device<ptm6840_device> m_ptm;
	required_device<acia6850_device> m_acia;
	required_device<cassette_image_device> m_cass;

	uint8_t m_banksel = 0;
	uint8_t *m_plap = nullptr;
	uint8_t *m_epromp = nullptr;

	uint8_t m_ram0[1024 *  8]; // IC3
	uint8_t m_ram1[1024 * 32]; // IC4
	uint8_t m_ram2[1024 *  8]; // IC5
private:
	enum {
		PLA_EPROM = 0x80,
		PLA_IO1   = 0x40,
		PLA_RAM2  = 0x20,
		PLA_RAM1  = 0x10,
		PLA_RAM0  = 0x08,
		PLA_RAM13 = 0x04,
		PLA_RAM14 = 0x02,
		PLA_MPX   = 0x01
	};

	enum {
		CPU_A4 = 0x010,
		CPU_A5 = 0x020,
		CPU_A6 = 0x040,
		CPU_A7 = 0x080,
		CPU_A8 = 0x100,
		CPU_A9 = 0x200,
	};

	enum { // 74138 outputs, naming after schematics
		X0XX = 0x0000,
		X1XX = 0x0010,
		X2XX = 0x0020,
		X3XX = 0x0030,
		X4XX = 0x0080,
		X5XX = 0x0090,
		X6XX = 0x00a0,
		X7XX = 0x00b0,
	};
};

void can09t_state::machine_start()
{
	LOG("%s()\n", FUNCNAME);

	/* TBP18S030 32 bytes fuse PROM controls the entire memory map in two parts/map-banks
	   BANK 0: 6f 6f 69 69 7b 7b ee ee e8 e8 ec bc ea ea 7f 7f
	   BANK 1: f1 f1 f5 f5 f3 f3 f7 f7 dc dc ec bc ea ea 7f 7f

	   Connected as follows:
	                  A12 -- A0  Y0 -- MPX* - latch for screen RAM
	                  A13 -- A1  Y1 -- RAM14
	                  A14 -- A2  Y2 -- RAM13
	                  A15 -- A3  Y3 -- RAM0
	   SYSPIA PB5 -- BANK -- A4  Y4 -- RAM1
	                 E+Q  -- S*  Y5 -- RAM2
	                             Y6 -- IO1
	                             Y7 -- EPROM

	   The PAL outputs are used as follows:
	   --------------------------------
	   Y0 IC12 MUX 74245: D0-D7 + MPX + RW => BD0-BD7
	   Y1 RAM14 is used together with the other outputs
	   Y2 RAM13 is used together with the other outputs
	   Y3 IC3 SRAM  6264: A0-A12 + RAM13:CS2 + RAM14:NC + RAM0:OE:CE + jumper B2 RW/pulled high:RW + VCC:BATTERY
	   Y4 IC4 SRAM 62256: A0-A12 + RAM13 + RAM14 + RW + RAM1:OE:CE + BD0-BD7
	   Y5 IC5 SRAM  6264: A0-A12 + RAM13:CS2 + RAM14:NC + RW + RAM2:OE:CE + BD0-BD7
	   Y6 IC11 DMX 74138: A4:A A5:B A7:C A8:G1 IO1:G2A A9:G2B => x0xx-x7xx see bellow:
	   Y7 IC2 PROM 27256: A0-A14 + EPROM

	   The 74138 demuxer outputs are used as follows:
	    A B C (A4 A5 and A7 respectivelly, not A6!)
	   ----------------------------------------------
	    0 0 0 Y0 x0xx ACIA   0xB100-B101
	    0 0 1 Y1 x1xx SYSPIA 0xB110-B113
	    0 1 0 Y2 x2xx USRPIA 0xB120-B123
	    0 1 1 Y3 x3xx PTM    0xB130-B137
	    1 0 0 Y4 x4xx        0xB180-B18F
	    1 0 1 Y5 x5xx J1, J2 0xB190-B19F
	    1 1 0 Y6 x6xx
	    1 1 1 Y7 x7xx J1, J2 0xB1B0-B1BF
	 */
	m_plap = (uint8_t*)(memregion ("plas")->base ());
	m_epromp = (uint8_t*)(memregion ("roms")->base ());
	memset(m_ram0, 0, sizeof(m_ram0));
	memset(m_ram1, 0, sizeof(m_ram1));
	memset(m_ram2, 0, sizeof(m_ram2));
};

uint8_t can09t_state::read(offs_t offset)
{
	LOG("%s %02x\n", FUNCNAME, offset);
	uint8_t pla_offset = 0;
	uint8_t pla_data = 0;
	uint8_t byte = 0;

	// Form the offset into the PLA (see above for explanation on PLA hookup)
	pla_offset = ((offset >> 12) & 0x0f) | (m_banksel ? 0x10 : 0x00);

	// Get the PLA data byte
	pla_data = m_plap[pla_offset & 0x1f];
	LOGPLA("PLA[%02x] read: %02x Bank%d\n", pla_offset, pla_data, m_banksel != 0 ? 0 : 1);

	// Find the device
	// IC2 EPROM - PAL Y7 PROM 27256: A0-A14 + EPROM
	if (~pla_data & PLA_EPROM)
	{
		byte = m_epromp[(offset & 0x7fff) % memregion("roms")->bytes()];
		LOGPROM("- EPROM %04x[%04x]->%02x\n", offset, (offset & 0x7fff) % memregion("roms")->bytes(), byte);
	}

	// IC3 RAM0 - PAL Y3 SRAM 6264: A0-A12 + RAM13:1 (RAM14 connected to NC)
	if ( (~pla_data & PLA_RAM0) && (pla_data & PLA_RAM13))
	{
		byte = m_ram0[offset % sizeof(m_ram0)];
		LOGPLA("- RAM0 %04x->%02x\n", offset, byte);
	}

	// IC4 RAM1 PAL Y4 SRAM 62256: A0-A12 + banked with RAM13/RAM14 data buffer 74LS245 enabled through PAL Y0
	if ( (~pla_data & PLA_RAM1) && (~pla_data & PLA_MPX) )
	{
		uint16_t ofs = (offset & 0x1fff) | ((pla_data & PLA_RAM13) ? 0x2000 : 0) | ((pla_data & PLA_RAM14) ? 0x4000 : 0);
		byte = m_ram1[ofs % sizeof(m_ram1)];
		LOGPLA("- RAM1 %04x->%02x\n", ofs, byte);
	}

	// IC5 RAM2 - PAL Y5 SRAM 6264: A0-A12 + RAM13:1 (RAM14 connected to NC) data buffer 74LS245 enabled through PAL Y0
	if ( (~pla_data & PLA_RAM0) && (pla_data & PLA_RAM13) && (~pla_data & PLA_MPX) )
	{
		byte = m_ram2[offset % sizeof(m_ram2)];
		LOGPLA("- RAM2 %04x->%02x\n", offset, byte);
	}

	// IC11 74138 Y6 demultiplexer : A5, A5, A7 + A8:1 + A9:0 maps in the i/o area at some
	if ( (~pla_data & PLA_IO1) && (~pla_data & PLA_MPX) && (offset & CPU_A8) && (~offset & CPU_A9) )
	{
		switch (offset & 0x00f0)
		{
		case X0XX: // ACIA
			LOGPLA("-- ACIA\n");
			byte = m_acia->read(offset & 1);
			break;
		case X1XX: // SYSPIA
			LOGPLA("-- SYSPIA\n");
			byte = m_syspia->read_alt(offset & 3);
			break;
		case X2XX: // USRPIA
			LOGPLA("-- USRPIA\n");
			byte = m_usrpia->read_alt(offset & 3);
			break;
		case X3XX: // PTM
			LOGPLA("-- PTM\n");
			byte = m_ptm->read(offset & 7);
			break;
		case X4XX: //
			LOGPLA("-- XX4X\n");
			break;
		case X5XX: // J1, J2
			LOGPLA("-- XX5X\n");
			break;
		case X6XX: //
			LOGPLA("-- XX6X\n");
			break;
		case X7XX: // J1, J2
			LOGPLA("-- XX7X\n");
			break;
		}
	}
	LOGR("- %04x <- %02x\n\n", offset, byte);
	return byte;
}

void can09t_state::write(offs_t offset, uint8_t data)
{
	LOG("%s() %04x : %02x\n", FUNCNAME, offset, data);
	uint8_t pla_offset = 0;
	uint8_t pla_data = 0;

	// Form the offset into the PLA (see above for explanation on PLA hookup)
	pla_offset = ((offset >> 12) & 0x0f) | (m_banksel ? 0x10 : 0x00);

	// Get the PLA data byte
	pla_data = m_plap[pla_offset & 0x1f];
	LOGPLA("PLA[%02x] write: %02x Bank%d\n", pla_offset, pla_data, m_banksel != 0 ? 0 : 1);

	// Find the device
	// IC2 EPROM - PAL Y7 PROM 27256: A0-A14 + EPROM*
	if (~pla_data & PLA_EPROM)
	{
		LOGPLA("- EPROM (ignored) %04x<-%02x\n", offset, data);
	}

	// IC3 RAM0 - PAL Y3 SRAM 6264: A0-A12 + RAM13:1 (RAM14 connected to NC)
	if ( (~pla_data & PLA_RAM0) && (pla_data & PLA_RAM13))
	{
		m_ram0[offset % sizeof(m_ram0)] = data;
		LOGPLA("- RAM0 %04x<-%02x\n", offset, data);
	}

	// IC4 RAM1 Y4 SRAM 62256: A0-A12 + banked with RAM13/RAM14 data buffer 74LS245 enabled through PAL Y0
	if ( (~pla_data & PLA_RAM1) && (~pla_data & PLA_MPX) )
	{
		uint16_t ofs = (offset & 0x1fff) | ((pla_data & PLA_RAM13) ? 0x2000 : 0) | ((pla_data & PLA_RAM14) ? 0x4000 : 0);
		m_ram1[ofs % sizeof(m_ram1)] = data;
		LOGPLA("- RAM1 %04x<-%02x\n", ofs, data);
	}

	// IC5 RAM2 - PAL Y5 SRAM 6264: A0-A12 + RAM13:1 (RAM14 connected to NC) data buffer 74LS245 enabled through PAL Y0
	if ( (~pla_data & PLA_RAM0) && (pla_data & PLA_RAM13) && (~pla_data & PLA_MPX) )
	{
		m_ram2[offset % sizeof(m_ram2)] = data;
		LOGPLA("- RAM2 %04x->%02x\n", offset, data);
	}

	// IC11 74138 Y6 demultiplexer : A5, A5, A7 + A8:1 + A9:0 maps in the i/o area at some
	if ( (~pla_data & PLA_IO1) && (~pla_data & PLA_MPX) && (offset & CPU_A8) && (~offset & CPU_A9) )
	{
		LOGPLA("- IO1 %04x<-%02x\n", offset, data);
		switch (offset & 0x00f0)
		{
		case X0XX: // ACIA
			LOGPLA("-- ACIA\n");
			m_acia->write(offset & 1, data);
			break;
		case X1XX: // SYSPIA
			LOGPLA("-- SYSPIA\n");
			m_syspia->write_alt(offset & 3, data);
			break;
		case X2XX: // USRPIA
			LOGPLA("-- USRPIA\n");
			m_usrpia->write_alt(offset & 3, data);
			break;
		case X3XX: // PTM
			LOGPLA("-- PTM\n");
			m_ptm->write(offset & 7, data);
			break;
		case X4XX: //
			LOGPLA("-- XX4X\n");
			break;
		case X5XX: // J1, J2
			LOGPLA("-- XX5X\n");
			break;
		case X6XX: //
			LOGPLA("-- XX6X\n");
			break;
		case X7XX: // J1, J2
			LOGPLA("-- XX7X\n");
			break;
		}
	}
}

uint8_t can09t_state::syspia_A_r()
{
	LOG("%s()\n", FUNCNAME);
	return 0;
}

uint8_t can09t_state::syspia_B_r()
{
	LOG("%s()\n", FUNCNAME);
	u8 data = (m_cass->input() > 0.04) ? 0x80 : 0;
	return data;
}

void can09t_state::syspia_B_w(uint8_t data)
{
	LOG("%s(%02x)\n", FUNCNAME, data);

	m_banksel = (data & 0x20) ? 0x10 : 0;
	LOGBANK("Bank select: %d", (m_banksel >> 4) & 1);
	m_cass->output(BIT(data, 6) ? 1.0 : -1.0);
}

void can09t_state::syspia_cb2_w(int state)
{
	LOG("%s(%02x)\n", FUNCNAME, state);
}

void can09t_state::usrpia_cb2_w(int state)
{
	LOG("%s(%02x)\n", FUNCNAME, state);
}

void can09t_state::write_acia_clock(int state)
{
		m_acia->write_txc (state);
		m_acia->write_rxc (state);
}

/*
 * Address map from documentation based on specific PAL
 *
 *   BANK 0                            BANK 1
 *   0x0000-0x34ff PROM VDU-program    0x0000-0x34ff RAM0 user applications (0)
 *   0x3500-0x5fff PROM simulator      0x3500-0x5fff RAM0 user applications (0)
 *   0x6000-0x7fff RAM screen memory   0x6000-0x7fff RAM0 user applications (0)
 *   0x8000-0x9fff RAM screen memory   0x8000-0x9fff PROM user applications (2)
 *  *0xa000-0xafff RAM Stack, system   0xa000-0xafff RAM Stack, system
 *  *0xb000-0xb0ff Free                0xb000-0xb0ff Free
 *  *0xb100-0xb101 ACIA                0xb100-0xb101 ACIA
 *  *0xb110-0xb113 PIA System          0xb110-0xb113 PIA System
 *  *0xb120-0xb123 PIA User apps       0xb120-0xb123 PIA User apps
 *  *0xb130-0xb137 PTM                 0xb130-0xb137 PTM
 *  *0xb180-0xb1bf Free                0xb180-0xb1bf Free
 *  *0xb200-0xb3ff Free                0xb200-0xb3ff Free
 *  *0xc000-0xdfff RAM User apps (1)   0xc000-0xdfff RAM User apps (1)
 *  *0xe000-0xffff PROM monitor        0xe000-0xffff PROM monitor
 */

void can09t_state::can09t_map(address_map &map)
{
// Everything is dynamically and asymetrically mapped through the PAL decoded by read/write
	map(0x0000, 0xffff).rw(FUNC(can09t_state::read), FUNC(can09t_state::write));
}

static INPUT_PORTS_START( can09t )
INPUT_PORTS_END

/*
 * Candela Main Unit
 * TODO:
 * - Map PIA:S
 * - ROM/RAM paging by using the PIAs and the myriad of 74138s on the board
 * - Vram and screen for the 6845 CRTC
 * - Check actual clock source for CRTC. An 8MHz UKI crystal is also nearby
 * - Keyboard
 * - Serial port
 * - Floppy controller
 * - Split out commonalities in a candela_state base class for can09 and can09t
 */
/* Candela main unit driver class */
  class can09_state :  public driver_device
{
public:
	can09_state(const machine_config &mconfig, device_type type, const char * tag)
		: driver_device(mconfig, type, tag)
		,m_maincpu(*this, "maincpu")
		,m_pia1(*this, PIA1_TAG)
		,m_ram(*this, RAM_TAG)
		,m_bank1(*this, "bank1")
		,m_crtc(*this, "crtc")
	{ }

	void can09(machine_config &config);

protected:
	required_device<cpu_device> m_maincpu;
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	uint8_t pia1_A_r();
	void pia1_A_w(uint8_t data);
	uint8_t pia1_B_r();
	void pia1_B_w(uint8_t data);
	void pia1_cb2_w(int state);
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void can09_map(address_map &map) ATTR_COLD;
	required_device<pia6821_device> m_pia1;
	required_device<ram_device> m_ram;
	required_memory_bank m_bank1;
	required_device<hd6845s_device> m_crtc;
};

void can09_state::machine_reset()
{
	LOG("%s()\n", FUNCNAME);
	m_bank1->set_entry(0);
}

void can09_state::machine_start()
{
	LOG("%s()\n", FUNCNAME);
	m_bank1->configure_entries(0, 8, m_ram->pointer(), 0x8000);
}

uint32_t can09_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	int x, y;
#if 0
	int vramad;
	UINT8 *chardata;
	UINT8 charcode;
#endif

	LOGSCREEN("%s()\n", FUNCNAME);
	//  vramad = 0;
	for (int row = 0; row < 72 * 8; row += 8)
	{
		for (int col = 0; col < 64 * 8; col += 8)
		{
#if 0
			/* look up the character data */
			charcode = m_vram[vramad];
			if (VERBOSE && charcode != 0x20 && charcode != 0) LOGSCREEN("\n %c at X=%d Y=%d: ", charcode, col, row);
			chardata = &m_char_ptr[(charcode * 8)];
#endif
			/* plot the character */
			for (y = 0; y < 8; y++)
			{
				//              if (VERBOSE && charcode != 0x20 && charcode != 0) LOGSCREEN("\n  %02x: ", *chardata);
				for (x = 0; x < 8; x++)
				{
					//                  if (VERBOSE && charcode != 0x20 && charcode != 0) LOGSCREEN(" %02x: ", *chardata);
					bitmap.pix(row + y, col + x) = x & 1; //(*chardata & (1 << x)) ? 1 : 0;
				}
				//              chardata++;
			}
			//          vramad++;
		}
		//      if (VERBOSE && charcode != 0x20 && charcode != 0) LOGSCREEN("\n");
	}

	return 0;
}

uint8_t can09_state::pia1_A_r()
{
	LOG("%s()\n", FUNCNAME);
	return 0x40;
}

void can09_state::pia1_A_w(uint8_t data)
{
	LOG("%s(%02x)\n", FUNCNAME, data);
}

uint8_t can09_state::pia1_B_r()
{
	LOG("%s()\n", FUNCNAME);
	return 0;
}

void can09_state::pia1_B_w(uint8_t data)
{
	//  UINT8 *RAM = memregion("maincpu")->base();
	LOG("%s(%02x)\n", FUNCNAME, data);
	//  membank("bank1")->set_entry((data & 0x70) >> 4);
	m_bank1->set_entry((data & 0x70) >> 4);
#if 0
	switch (data & 0x70){
	case 0x00: membank("bank1")->set_base(&RAM[0x10000]); break;
	case 0x10: membank("bank1")->set_base(&RAM[0x18000]); break;
	case 0x20: membank("bank1")->set_base(&RAM[0x20000]); break;
	case 0x30: membank("bank1")->set_base(&RAM[0x28000]); break;
	case 0x40: membank("bank1")->set_base(&RAM[0x30000]); break;
	case 0x50: membank("bank1")->set_base(&RAM[0x38000]); break;
	case 0x60: membank("bank1")->set_base(&RAM[0x40000]); logerror("strange memory bank"); break;
	case 0x70: membank("bank1")->set_base(&RAM[0x48000]); logerror("strange memory bank"); break;
	default: logerror("%s Programming error, please report!\n", FUNCNAME);
	}
#endif
}

void can09_state::pia1_cb2_w(int state)
{
	LOG("%s(%02x)\n", FUNCNAME, state);
}

static INPUT_PORTS_START( can09 )
	PORT_START("LINE0")
	PORT_START("LINE1")
	PORT_START("LINE2")
	PORT_START("LINE3")
	PORT_START("LINE4")
INPUT_PORTS_END

// traced and guessed from pcb images and debugger
// It is very likely that this is a PIA based dynamic address map, needs more analysis
void can09_state::can09_map(address_map &map)
{
/*
 * Port A=0x18 B=0x20 erase 0-7fff
 * Port A=0x18 B=0x30 erase 0-7fff
 * Port A=0x18 B=0x00
 * Port A=0x10 B=
*/
//  map(0x0000, 0x7fff).ram();
	map(0x0000, 0x7fff).ram().bankrw("bank1");
	map(0xe000, 0xffff).rom().region("roms", 0);
	map(0xe020, 0xe020).w(m_crtc, FUNC(hd6845s_device::address_w));
	map(0xe021, 0xe021).w(m_crtc, FUNC(hd6845s_device::register_w));
	map(0xe034, 0xe037).rw(m_pia1, FUNC(pia6821_device::read), FUNC(pia6821_device::write));

#if 0
	map(0xb100, 0xb101).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xb110, 0xb113).rw(m_pia1, FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
	map(0xb120, 0xb123).rw(PIA2_TAG, FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
	map(0xb130, 0xb137).rw("ptm", FUNC(ptm6840_device::read), FUNC(ptm6840_device::write));
	map(0xb200, 0xc1ff).rom().region("roms", 0x3200);
	map(0xc200, 0xdfff).ram(); /* Needed for BASIC etc */
#endif
}

#ifdef UNUSED_VARIABLE
static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_300 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_300 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END
#endif

/* Fake clock values until we TODO: figure out how the PTM generates the clocks */
#define CAN09T_BAUDGEN_CLOCK 1.8432_MHz_XTAL
#define CAN09T_ACIA_CLOCK (CAN09T_BAUDGEN_CLOCK / 12)

void can09t_state::can09t(machine_config &config)
{
	MC6809(config, m_maincpu, 4.9152_MHz_XTAL); // IPL crystal
	m_maincpu->set_addrmap(AS_PROGRAM, &can09t_state::can09t_map);

	/* --PIA inits----------------------- */
	PIA6821(config, m_syspia); // CPU board
	m_syspia->readpa_handler().set(FUNC(can09t_state::syspia_A_r));
	m_syspia->readpb_handler().set(FUNC(can09t_state::syspia_B_r));
	m_syspia->writepb_handler().set(FUNC(can09t_state::syspia_B_w));
	m_syspia->cb2_handler().set(FUNC(can09t_state::syspia_cb2_w));
	/* 0xE1FB 0xB112 (SYSPIA Control A) = 0x00 - Channel A IRQ disabled */
	/* 0xE1FB 0xB113 (SYSPIA Control B) = 0x00 - Channel B IRQ disabled */
	/* 0xE203 0xB110 (SYSPIA DDR A)     = 0x00 - Port A all inputs */
	/* 0xE203 0xB111 (SYSPIA DDR B)     = 0x7F - Port B mixed mode */
	/* 0xE20A 0xB112 (SYSPIA Control A) = 0x05 - IRQ A enabled on falling transition on CA2 */
	/* 0xE20A 0xB113 (SYSPIA Control B) = 0x34 - CB2 is low and lock DDRB */
	/* 0xE20E 0xB111 (SYSPIA port B)    = 0x10 - Data to port B */

	PIA6821(config, m_usrpia); // CPU board
	m_usrpia->cb2_handler().set(FUNC(can09t_state::usrpia_cb2_w));
	/* 0xE212 0xB122 (USRPIA Control A) = 0x00 - Channel A IRQ disabled */
	/* 0xE212 0xB123 (USRPIA Control B) = 0x00 - Channel B IRQ disabled */
	/* 0xE215 0xB120 (USRPIA DDR A)     = 0x00 - Port A all inputs */
	/* 0xE215 0xB121 (USRPIA DDR B)     = 0xFF - Port B all outputs */
	/* 0xE21A 0xB122 (USRPIA Control A) = 0x34 - CA2 is low and lock DDRB */
	/* 0xE21A 0xB123 (USRPIA Control B) = 0x34 - CB2 is low and lock DDRB */
	PIA6821(config, m_pia3); // ROM board
	PIA6821(config, m_pia4); // ROM board

	PTM6840(config, "ptm", 0);

	/* RS232 usage: mame can09t -window -debug -rs232 terminal */
	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_acia->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_acia, FUNC(acia6850_device::write_rxd));
	rs232.cts_handler().set(m_acia, FUNC(acia6850_device::write_cts));

	CLOCK(config, "acia_clock", CAN09T_ACIA_CLOCK).signal_handler().set(FUNC(can09t_state::write_acia_clock));

	SPEAKER(config, "mono").front_center();
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
}

#define CAN09_X1_CLOCK 22.1184_MHz_XTAL        /* UKI 22118.40 Khz */
#define CAN09_CPU_CLOCK (CAN09_X1_CLOCK / 16) /* ~1.38MHz Divider needs to be check but is the most likelly */
void can09_state::can09(machine_config &config)
{
	MC6809E(config, m_maincpu, CAN09_CPU_CLOCK); // MC68A09EP
	m_maincpu->set_addrmap(AS_PROGRAM, &can09_state::can09_map);

	/* RAM banks */
	RAM(config, RAM_TAG).set_default_size("768K");

	// CRTC init
	hd6845s_device &crtc(HD6845S(config, "crtc", CAN09_CPU_CLOCK)); // HD46505SP-1 (HD68A45SP)
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(8);
	//crtc.set_update_row_callback(FUNC(can09_state::crtc_update_row), this); // not written yet

	/* Setup loop from data table in ROM: 0xFFCB 0xE020 (CRTC register number), 0xFFD0 0xE021 (CRTC register value)
	    Reg  Value Comment
	    0x00 0x55  Horizontal Total number of characters,
	    0x01 0x40  Horizontal Displayed number of characters
	    0x02 0x43  Horizontal Sync Position, character number
	    0x03 0x03  Horizontal Sync width, number of charcters
	    0x04 0x50  Vertical Total number of characters
	    0x05 0x09  Vertical Total Adjust number of characters
	    0x06 0x48  Vertical Displayed number of characters
	    0x07 0x4B  Vertical Sync Position, character number
	    0x08 0x00  Interlace Mode/Scew, Non-Interlaced
	    0x09 0x03  Max Scan Line Address Register
	    0x0A 0x00  Cursor Start
	    0x0B 0x0A  Cursor End
	    0x0C 0x00  Start Address hi
	    0x0D 0x00  Start Address lo
	    0x0E 0x00  Cursor hi
	    0x0F 0x00  Cursor lo
	    Note - no init of Light Pen registers
	*/



	/* screen - totally faked value for now */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_raw(4_MHz_XTAL / 2, 512, 0, 512, 576, 0, 576);
	screen.set_screen_update(FUNC(can09_state::screen_update));
	screen.set_palette("palette");
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* Floppy */
	WD1770(config, "wd1770", 8_MHz_XTAL); // TODO: Verify 8MHz UKI crystal assumed to be used
#if 0
	FLOPPY_CONNECTOR(config, "wd1770:0", candela_floppies, "3dd", floppy_image_device::default_mfm_floppy_formats);
	SOFTWARE_LIST(config, "flop3_list").set_original("candela");
#endif

	/* --PIA inits----------------------- */
	PIA6821(config, m_pia1); // CPU board
	m_pia1->readpa_handler().set(FUNC(can09_state::pia1_A_r));
	m_pia1->writepa_handler().set(FUNC(can09_state::pia1_A_w));
	m_pia1->readpb_handler().set(FUNC(can09_state::pia1_B_r));
	m_pia1->writepb_handler().set(FUNC(can09_state::pia1_B_w));
	m_pia1->cb2_handler().set(FUNC(can09_state::pia1_cb2_w));
	/* 0xFF7D 0xE035 (PIA1 Control A) = 0x00 - Channel A IRQ disabled */
	/* 0xFF81 0xE037 (PIA1 Control B) = 0x00 - Channel A IRQ disabled */
	/* 0xFF85 0xE034 (PIA1 DDR A)     = 0x1F - Port A mixed mode */
	/* 0xFF89 0xE036 (PIA1 DDR B)     = 0x79 - Port B mixed mode */
	/* 0xFF8D 0xE035 (PIA1 Control A) = 0x04 - Channel A lock DDR */
	/* 0xFF8F 0xE037 (PIA1 Control B) = 0x04 - Channel B lock DDR */
	/* 0xFF93 0xE034 (PIA1 Port B)    = 0x18 - Write Data on Port B */

#if 1
	PIA6821(config, PIA2_TAG); // CPU board
	ACIA6850(config, "acia1", 0); // CPU board
	ACIA6850(config, "acia2", 0); // CPU board
#endif
}

ROM_START( can09t ) /* The smaller grey computer */
	ROM_REGION(0x10000, "roms", 0)
	/* CAN09 v7 and CDBASIC 3.8 */
	ROM_LOAD( "ic2-mon58b-c8d7.bin", 0x0000, 0x8000, CRC(7eabfec6) SHA1(e08e2349035389b441227df903aa54f4c1e4a337) )

	ROM_REGION(0x1000, "plas", 0)
	/* Programmable logic for the CAN09 1.4 PCB (CAN21.1) */
	ROM_LOAD( "ic10-21.1.bin",       0x0000, 0x20,   CRC(b75ac72d) SHA1(689363200035b11a823d17a8d717f313eeefc3bf) )
ROM_END

ROM_START( can09 ) /* The bigger black computer CAN v1 */
	ROM_REGION(0x10000, "roms", 0)
	ROM_LOAD( "ic14-vdu42.bin", 0x0000, 0x2000, CRC(67fc3c8c) SHA1(1474d6259646798377ef4ce7e43d3c8d73858344) )
ROM_END

} // anonymous namespace


//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY            FULLNAME            FLAGS
COMP( 1984, can09,  0,      0,      can09,   can09,  can09_state,  empty_init, "Candela Data AB", "Candela CAN09 v1", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_IMPERFECT_GRAPHICS)
COMP( 1984, can09t, 0,      0,      can09t,  can09t, can09t_state, empty_init, "Candela Data AB", "Candela CAN09",    MACHINE_NO_SOUND_HW )



canon_s80.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:FelipeSanches
/*
 * canon_s80.cpp
 *
 *    CANON S-80 (electronic typewriter, not the S80 digital camera)
 *    CANON Typestar 3
 *
 * skeleton driver by:
 *    Felipe Correa da Silva Sanches <juca@members.fsf.org>
 *
 * known issues:
 *  - memory-map is uncertain
 *  - maincpu clock was only verified for canonts3, but it's probably the same for canons80
 *  - still lacks description of the keyboard inputs
 *  - as well as a "paper" device to plot the output of the dot matrix print head

*************************************************************************************
Canon Typestar 3 Electronic Typewriter, Canon 1989/1990.
Hardware info by Guru
Last updated: 21st May 2021
---------------------------

This is an electronic typewriter made by Canon.
It uses a thermal printing process with ribbon cartridges containing a coil
of plastic tape coated with black 'ink' that is fused onto the paper via
heat from the thermal print-head.
The LCD has an alpha-numeric 15 character display with full editing capability and
some other areas that show fixed functions and operation modes.
It appears to be functionally identical to the Canon S80 Electronic Typewriter.

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

CANON NH1-0275-04
|------------------------------------------|
|CN1                 CN2              CN6  |
|            PIEZO       VR2  FUSE(3A)   DC|
|  T1719A                                  |
| CN3     CN4    M5233  M54523P         SW1|
|         CN5                              |
|                         LC3518           |
| MB64H192   HD63A01X0P   NH4-0268.IC6  VR1|
|           6MHz                           |
|------------------------------------------|
Notes:
         CN1 - Head left-travel end-of-stroke switch connector (2 wires)
               On power-up the head moves left to touch this switch then moves right about 1 inch then stops.
         CN2 - Multi-pin flat cable for carriage power and data
         CN3 - Multi-pin flat cable for LCD
     CN4/CN5 - Multi-pin flat cables for keyboard
         CN6 - Connector for battery power input (6V via 4x 1.5V D-Cells). There are 3 wires so
               the CPU can detect when the battery is low and give the user a warning about it.
         VR1 - Print darkness adjustment pot
         VR2 - Piezo volume adjustment pot
       PIEZO - Piezo speaker/beeper
      LC3518 - Sanyo LC3518BL-15 2kb x8-bit SRAM, equivalent to 6116
NH4-0268.IC6 - Toshiba TC53257P 32kb x8-bit mask ROM marked 'NH4-0268' at location IC6
               The ROM supports all characters in English, German, French, Spanish and Italian languages.
  HD63A01X0P - Hitachi HD63A01X0P micro-controller with 4kb x8-bit internal mask ROM. Clock input 6MHz.
               HD63A01 has an on-chip divide-by-4 clock divider so the MCU runs at 1.5MHz internally.
               MCU is marked with a Canon part number 'NH4-0021' at location IC5.
               The internal ROM was trojan-dumped using the undocumented test mode.
      T1719A - Toshiba T1719A. Datasheet not available. This is most likely the keyboard and LCD
               controller and probably made exclusively for Canon.
    MB64H192 - Fujitsu MB64H192. Datasheet not available. This is most likely the carriage
               motor controller and probably made exclusively for Canon.
       M5233 - Mitsubishi M5233 Dual Comparitor, equivalent to LM4558
     M54523P - Mitsubishi M54523P 7-Unit 500mA Darlington Transistor Array with clamp diode
          DC - 6V DC power input 5.5mm barrel jack
         SW1 - Power on/off switch

Keyboard Layout
---------------

Keyboard is made by Matsushita
Part#: NS5-0594
|--------------------------------------------------------------------------------|
|MARGIN    KB                 |---------------------|           L/MARGIN R/MARGIN|
|RELEASE                      |         LCD         |    VR                      |
|          MODE               |                     |           TABSET    TABCLR |
|                             |---------------------|                            |
|     !     @     #     $     %     Yen   &     *     (     )     _ +   <-    -> |
|+-   1     2     3     4     5     6     7     8     9     0     - =            |
|                                                                    1/4         |
| TAB    Q     W     E     R     T     Y     U     I     O     P     1/2     BS  |
|                                                                                |
|                                                                :     "[        |
| LOCK     A     S     D     F     G     H     J     K     L     ;     ']        |
|                                                                          RETURN|
|                                                                  ?3            |
| SHIFT      Z     X     C     V     B     N     M     ,     .     /2   SHIFT    |
|                                                                                |
|        REPEAT                   SPACE                         RELOC        CODE|
|--------------------------------------------------------------------------------|
Notes:
      In the diagram above not all characters are shown due to being special or
      international characters that are not available on US keyboards, however every
      character used in the languages included in the ROM are available from this keyboard.

      There are several keys with up to 4 different characters on them. They are
      accessed by holding the key modifier SHIFT and KB keys.
      KB is used to select the characters on the left side of a key or the right side
      of a key. On the LCD fixed lower display, it will show I or II denoting either
      the left (I) or right (II) side of the key is active. When typing a key the lower
      character is normally active and the upper character is accessed by holding SHIFT.

      VR2 - LCD brightness adjustment pot

      MODE KEY FUNCTIONS (* = power-on default)
      ---------|---------------------------------------------------
    * MODE +   |    +-     Sets line spacing mode to 1
      MODE +   |    1      Sets line spacing mode to 1 1/2
      MODE +   |    2      Sets line spacing mode to 2
      MODE +   |    3      Sets typing mode to C (character by character printing like a mechanical typewriter, characters do not appear on the LCD)
    * MODE +   |    4      Sets typing mode to L (line printing, characters show on LCD and can be edited, then printed as one line by pressing RETURN)
    * MODE +   |    5      Sets printing mode to normal print
      MODE +   |    6      Sets printing mode to double-width print
      MODE +   |    7      Sets printing mode to underlined print
      MODE +   |    9      Sets manual carriage return mode
    * MODE +   |    0      Sets automatic carriage return mode
      MODE +   |    =      Sets justified text mode
      MODE +   |    Q      Moves the carriage while using the display for manual carriage positioning (for example typing in columns etc)
    * MODE +   |    <-     Sets typestyle mode to A (COURIER 10)
      MODE +   |    ->     Sets typestyle mode to B (CUBIC PS)
      MODE +   |    Z      Sets/cancels shading print mode. A 2nd menu will appear showing 4 shading modes that are selected by pressing numbers 1 - 4
      ---------|---------------------------------------------------


      CODE KEY FUNCTIONS
      ---------|---------------------------------------------------
      CODE +   |    1      Sets centering between margins
      CODE +   |    2      Sets centering between tabs
      CODE +   |    4      Executes right margin alignment
      CODE +   |    5      Sets/clears indent position
      CODE +   |    6      Executes decimal tab
      CODE +   |    0      Executes automatic paper feed (i.e. loads paper to a fixed start position automatically)
      CODE +   | TABSET    Sets decimal tab
      CODE +   |   TAB     Confirms decimal tab position
      CODE +   |TAB+TABCLR Clears specific decimal tab
      CODE +   | TABCLR    Clears all tabs and decimal tabs
      CODE +   |    =      Executes permanent hyphen
      CODE +   |    B      Executes permanent space
      CODE +   |  SPACE    Executes controlled carriage movement
      CODE +   | RETURN    Returns carriage without line feed
      CODE +   |    D      Prints a demo on the paper
      ---------|---------------------------------------------------

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

#include "emu.h"
#include "cpu/m6800/m6801.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"


namespace {

class canons80_state : public driver_device
{
public:
	canons80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_keyboard(*this, "KEY%X", 0U)
		, m_keyscan(0)
	{ }

	void canons80(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	u8 keyboard_r();
	void keyscan_w(u8 data);

	HD44780_PIXEL_UPDATE(pixel_update);

	void canons80_map(address_map &map) ATTR_COLD;

	optional_ioport_array<16> m_keyboard;
	u8 m_keyscan;
};


void canons80_state::machine_start()
{
	save_item(NAME(m_keyscan));
}

HD44780_PIXEL_UPDATE(canons80_state::pixel_update)
{
	if (pos < 8)
		bitmap.pix((line & 2) * 4 + y, (line & 1) * 48 + pos * 6 + x) = state;
}

u8 canons80_state::keyboard_r()
{
	return m_keyboard[m_keyscan & 0x0f].read_safe(0xff);
}

void canons80_state::keyscan_w(u8 data)
{
	m_keyscan = data;
}

void canons80_state::canons80_map(address_map &map)
{
	map(0x0100, 0x07ff).ram();
	map(0x1000, 0x1000).w(FUNC(canons80_state::keyscan_w));
	map(0x2000, 0x2001).rw("lcdc", FUNC(hd44780_device::read), FUNC(hd44780_device::write));
	map(0x4000, 0x7fff).rom().region("external", 0x4000);
	map(0x8000, 0xbfff).rom().region("external", 0);
}

static INPUT_PORTS_START(canons80)
	PORT_START("KEY8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('e') PORT_CHAR('E') PORT_CODE(KEYCODE_E)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('w') PORT_CHAR('W') PORT_CODE(KEYCODE_W)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR('@') PORT_CODE(KEYCODE_2)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('d') PORT_CHAR('D') PORT_CODE(KEYCODE_D)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('f') PORT_CHAR('F') PORT_CODE(KEYCODE_F)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('c') PORT_CHAR('C') PORT_CODE(KEYCODE_C)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('v') PORT_CHAR('V') PORT_CODE(KEYCODE_V)

	PORT_START("KEY9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Mode") PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('a') PORT_CHAR('A') PORT_CODE(KEYCODE_A)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('q') PORT_CHAR('Q') PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CHAR(0x09) PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('s') PORT_CHAR('S') PORT_CODE(KEYCODE_S)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('z') PORT_CHAR('Z') PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('x') PORT_CHAR('X') PORT_CODE(KEYCODE_X)

	PORT_START("KEYA")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('t') PORT_CHAR('T') PORT_CODE(KEYCODE_T)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('r') PORT_CHAR('R') PORT_CODE(KEYCODE_R)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR('$') PORT_CODE(KEYCODE_4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('g') PORT_CHAR('G') PORT_CODE(KEYCODE_G)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('h') PORT_CHAR('H') PORT_CODE(KEYCODE_H)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('b') PORT_CHAR('B') PORT_CODE(KEYCODE_B)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('n') PORT_CHAR('N') PORT_CODE(KEYCODE_N)

	PORT_START("KEYB")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('_') PORT_CHAR(0x2192) PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 33") PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 38") PORT_CODE(KEYCODE_F9)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Repeat") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(' ') PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 35") PORT_CODE(KEYCODE_F6)

	PORT_START("KEYC")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('o') PORT_CHAR('O') PORT_CODE(KEYCODE_O)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('i') PORT_CHAR('I') PORT_CODE(KEYCODE_I)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('9') PORT_CHAR('(') PORT_CODE(KEYCODE_9)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('8') PORT_CHAR('*') PORT_CODE(KEYCODE_8)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('l') PORT_CHAR('L') PORT_CODE(KEYCODE_L)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHAR(':') PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('.') PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('/') PORT_CHAR('?') PORT_CODE(KEYCODE_SLASH)

	PORT_START("KEYD")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('u') PORT_CHAR('U') PORT_CODE(KEYCODE_U)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('y') PORT_CHAR('Y') PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR('&') PORT_CODE(KEYCODE_7)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR('^') PORT_CODE(KEYCODE_6)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('j') PORT_CHAR('J') PORT_CODE(KEYCODE_J)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('k') PORT_CHAR('K') PORT_CODE(KEYCODE_K)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_M)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',') PORT_CODE(KEYCODE_COMMA)

	PORT_START("KEYE")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\'') PORT_CHAR('"') PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x23a1) PORT_CHAR(0x23a6) PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('p') PORT_CHAR('P') PORT_CODE(KEYCODE_P)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0') PORT_CHAR(')') PORT_CODE(KEYCODE_0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('-') PORT_CHAR(0x2014) PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2190") PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CODE(KEYCODE_LEFT) // ←
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 36") PORT_CODE(KEYCODE_F7)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 31") PORT_CODE(KEYCODE_F3)

	PORT_START("KEYF")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2192") PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CODE(KEYCODE_RIGHT) // →
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CHAR(0x08) PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 2D") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 3B") PORT_CODE(KEYCODE_F10)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 37") PORT_CODE(KEYCODE_F8)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 32") PORT_CODE(KEYCODE_F4)

	PORT_START("P2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CHAR(UCHAR_SHIFT_1) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Lock") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x34, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

void canons80_state::canons80(machine_config &config)
{
	// basic machine hardware
	hd6301x0_cpu_device &maincpu(HD6301X0(config, "maincpu", 6_MHz_XTAL)); // hd63a01x0p
	maincpu.set_addrmap(AS_PROGRAM, &canons80_state::canons80_map);
	maincpu.in_p2_cb().set_ioport("P2");
	maincpu.in_p5_cb().set(FUNC(canons80_state::keyboard_r));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(16*6, 16);
	screen.set_visarea(0, 16*6-1, 0, 16-1);
	screen.set_palette("palette");

	hd44780_device &hd44780(HD44780(config, "lcdc", 270'000)); // TODO: Wrong device type, should be T1719A; clock not measured, datasheet typical clock used
	hd44780.set_lcd_size(2, 16);
	hd44780.set_pixel_update_cb(FUNC(canons80_state::pixel_update));

	PALETTE(config, "palette").set_entries(2);
}


ROM_START( canons80 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "hd63a1x0p.bin", 0x0000, 0x1000, NO_DUMP )
	ROM_FILL( 0xfec, 1, 0xbf )
	ROM_FILL( 0xfed, 1, 0xf2 )
	ROM_FILL( 0xff4, 1, 0xbf )
	ROM_FILL( 0xff5, 1, 0xf5 )
	ROM_FILL( 0xffa, 1, 0xbf )
	ROM_FILL( 0xffb, 1, 0xf8 )
	ROM_FILL( 0xffe, 1, 0xbf )
	ROM_FILL( 0xfff, 1, 0xfb )

	ROM_REGION( 0x8000, "external", 0 )
	ROM_LOAD( "canon_8735kx_nh4-0029_064.ic6", 0x0000, 0x8000, CRC(b6cd2ff7) SHA1(e47a136300c826e480fac1be7fc090523078a2a6) )
ROM_END

ROM_START( canonts3 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	// every 0x100 range was read via trojan 7 times, but reads weren't totally consistent. This was reconstructed by taking the most consistent reads for each byte.
	ROM_LOAD( "nh4-0021.ic5", 0x0000, 0x1000, BAD_DUMP CRC(b859b7d4) SHA1(3a5a80b1b8040fe0b13c0fb52b93f738a06eff16) )

	ROM_REGION( 0x8000, "external", 0 )
	ROM_LOAD( "nh4-0268.ic6", 0x0000, 0x8000, CRC(bbdd9f74) SHA1(347fa0d37f4df0c175ff1d7feb634f681739804f) )
ROM_END

} // Anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY  FULLNAME                                  FLAGS
COMP( 1988, canons80, 0,      0,      canons80, canons80, canons80_state, empty_init, "Canon", "S-80 (Canon) (electronic typewriter)",   MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
COMP( 1988, canonts3, 0,      0,      canons80, canons80, canons80_state, empty_init, "Canon", "Typestar 3",                             MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



card.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Kevin Horton, Jonathan Gevaryahu, Sandro Ronco, hap
/*******************************************************************************

Fidelity electronic card games
- Bridge Challenger (BRC)
- Advanced Bridge Challenger (UBC/UB2)
- Voice Bridge Challenger (VBRC/BV2)
- Bridge Challenger III (BV3)
- Gin & Cribbage Challenger (GIN)
- *Skat Challenger (SKT)

*: not dumped yet

Card Challenger (model CDC) was announced but not released, it was supposed to be
a card games console, with games on separate cartridges. Maybe some of the games
were already finished, and used later for GIN and SKT.

NOTE: The card scanner is simulated, but the player is kind of forced to cheat
and has to peek at the card before it is scanned.

BTANB:
- on BRC, the 2 jokers are identified as Spade 1 and Spade 2

TODO:
- verify if BV2 is a newer program version than VBRC
- the VFD scrolls around 30% too slow compared to the real one, probably depends
  on how many T1 clock edges the 8041 can detect (see mcu_t1_r)

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

Voice Bridge Challenger (Model VBRC, later reissued as Model 7002/BV2)
and Bridge Challenger 3 (Model 7014)
(which both share the same* hardware)
--------------------------------
* The Bridge Challenger 3 does not actually have the 8 LEDs nor the
latches which operate them populated and the plastic indicator cap locations
are instead are covered by a piece of plastic, but they do work if manually
added.

RE notes by Kevin Horton

This unit is similar in construction kinda to the chess challengers, however it
has an 8041 which does ALL of the system I/O. The Z80 has NO IO AT ALL other than
what is performed through the 8041!

The main CPU is a Z80 running at 2.5MHz

INT connects to VCC (not used)
NMI connects to VCC (not used)
RST connects to power on reset, and reset button

The 8041 runs at 5MHz.

Memory Map:
-----------
0000-1FFF: 8K 101-64108 ROM
2000-3FFF: 8K 101-64109 ROM
4000-5FFF: 8K 101-64110 ROM
6000-7FFF: 1K of RAM (2114 * 2)
8000-DFFF: unused
E000-FFFF: write to TSI chip

NOTE: when the TSI chip is written to, the CPU IS STOPPED. The CPU will run again
when the word is done being spoken. This is because D0-D5 run to the TSI chip directly.

The TSI chip's ROM is 4K, and is marked 101-32118. The clock is the same as the Chess
Challengers- 470K/100pf which gives a frequency around 25KHz or so.

Port Map:
---------
00-FF: 8041 I/O ports (A0 selects between the two)

8041 pinout:
------------
(note: columns are pulled up with 10K resistors)

P10 - column H, RD LED, VFD grid 0
P11 - column G, DB LED, VFD grid 1
P12 - column F, <>V LED, VFD grid 2
P13 - column E, ^V LED, VFD grid 3
P14 - column D, W LED, VFD grid 4
P15 - column C, S LED, VFD grid 5
P16 - column B, E LED, VFD grid 6
P17 - column A, N LED, VFD grid 7

P20 - I/O expander
P21 - I/O expander
P22 - I/O expander
P23 - I/O expander
P24 - row 0 through inverter
P25 - row 1 through inverter
P26 - row 2 through inverter
P27 - row 3 through inverter

PROG - I/O expander

T0 - optical card sensor (high = bright/reflective, low = dark/non reflective)
T1 - connects to inverter, then 5MHz/4

D8243C I/O expander:
--------------------
P4.0 - segment M
P4.1 - segment L
P4.2 - segment N
P4.3 - segment E

P5.0 - segment D
P5.1 - segment I
P5.2 - segment K
P5.3 - segment J

P6.0 - segment A
P6.1 - segment B
P6.2 - segment F
P6.3 - segment G

P7.0 - LED enable (high = LEDs can be lit. low = LEDs will not light)
P7.1 - goes through inverter, to pads that are not used
P7.2 - segment C
P7.3 - segment H

Button matrix:
--------------
the matrix is composed of 8 columns by 4 rows.

     A  B  C  D     E  F  G  H
     -------------------------
0-   RE xx CL EN    J  Q  K  A
1-   BR PB DB SC    7  8  9 10
2-   DL CV VL PL    3  4  5  6
3-   cl di he sp   NT  P  1  2

xx - speaker symbol
cl - clubs symbol
di - diamonds symbol
he - hearts symbol
sp - spades symbol

NOTE: RE is not wired into the matrix, and is run separately out.

There are 8 LEDs, and an 8 digit 14 segment VFD with commas and periods.
This display is the same one as can be found on the speak and spell.

       A       * comma
  ***********  *
 * *I  *J K* *
F*  *  *  *  *B
 *   * * *   *
  G**** *****H
 *   * * *   *
E*  *  *  *  *C
 * *N  *M L* *
  ***********  *decimal point
       D

The digits of the display are numbered left to right, 0 through 7 and are controlled
by the grids. hi = grid on, hi = segment on.

A detailed description of the hardware can be found also in the patent 4,373,719.

cards:
------
Playing cards have a 9-bit barcode on the face side near the edge. Swipe them downward
against the card scanner and the game will detect the card.
Barcode sync bits(msb and lsb) are the same for each card so that leaves 7 bits of data:
2 for suit, 4 for value, and 1 for parity so the card can be scanned backwards.

Two card decks exist (red and blue), each has the same set of barcodes.

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

#include "emu.h"

#include "cpu/z80/z80.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/i8243.h"
#include "machine/timer.h"
#include "sound/dac.h"
#include "sound/s14001a.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "fidel_brc.lh"
#include "fidel_bv3.lh"
#include "fidel_gin.lh"


namespace {

class card_state : public driver_device
{
public:
	card_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_mcu(*this, "mcu"),
		m_i8243(*this, "i8243"),
		m_display(*this, "display"),
		m_speech(*this, "speech"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// machine configs
	void brc(machine_config &config);
	void vbrc(machine_config &config);
	void bv3(machine_config &config);
	void gin(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(reset_button);
	DECLARE_INPUT_CHANGED_MEMBER(start_scan);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void brc_base(machine_config &config);

	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<i8041a_device> m_mcu;
	required_device<i8243_device> m_i8243;
	required_device<pwm_display_device> m_display;
	optional_device<s14001a_device> m_speech;
	optional_device<dac_1bit_device> m_dac;
	required_ioport_array<8> m_inputs;

	u32 m_barcode = 0;
	u16 m_vfd_data = 0;
	u8 m_inp_mux = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	TIMER_DEVICE_CALLBACK_MEMBER(barcode_shift) { m_barcode >>= 1; }

	// I/O handlers
	void update_display();
	void speech_w(u8 data);
	void mcu_p1_w(u8 data);
	u8 mcu_p2_r();
	int mcu_t0_r();
	int mcu_t1_r();
	template<int P> void ioexp_port_w(uint8_t data);
};

void card_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_barcode));
	save_item(NAME(m_vfd_data));
	save_item(NAME(m_inp_mux));
}

INPUT_CHANGED_MEMBER(card_state::reset_button)
{
	// reset button is directly wired to maincpu/mcu RESET pins
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
	m_mcu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}



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

// misc handlers

void card_state::update_display()
{
	// 14seg VFD segments, d15(12) is extra LED
	u16 outdata = bitswap<16>(m_vfd_data,12,13,1,6,5,2,0,7,15,11,10,14,4,3,9,8);
	m_display->matrix(m_inp_mux, outdata);

	// I8243 P71 + I8041 P17 is tone (not on speech model)
	if (m_dac != nullptr)
		m_dac->write(BIT(~m_inp_mux, 7) & BIT(m_vfd_data, 13));
}

void card_state::speech_w(u8 data)
{
	if (m_speech == nullptr)
		return;

	m_speech->data_w(data & 0x3f);
	m_speech->start_w(1);
	m_speech->start_w(0);
}


// card scanner

INPUT_CHANGED_MEMBER(card_state::start_scan)
{
	if (!newval)
		return;

	u32 code = (u32)param;
	m_barcode = 0;

	// convert bits to rising/falling edges
	for (int i = 0; i < 9; i++)
	{
		m_barcode <<= 2;
		m_barcode |= 1 << (code & 1);
		code >>= 1;
	}

	// 6*white at msb for card edge
	m_barcode <<= 8;
	m_barcode = ~m_barcode;
}


// I8243 I/O expander

template<int P>
void card_state::ioexp_port_w(uint8_t data)
{
	// P4x-P7x: digit segment data
	m_vfd_data = (m_vfd_data & ~(0xf << (4*P))) | ((data & 0xf) << (4*P));
	update_display();
}


// I8041 MCU

void card_state::mcu_p1_w(u8 data)
{
	// P10-P17: input mux, digit select
	m_inp_mux = data;
	update_display();
}

u8 card_state::mcu_p2_r()
{
	// P20-P23: I8243 P2
	u8 data = m_i8243->p2_r() & 0x0f;

	// P24-P27: multiplexed inputs (active low)
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read() << 4;

	return data ^ 0xf0;
}

int card_state::mcu_t0_r()
{
	// T0: card scanner light sensor (1=white, 0=black/none)
	return m_barcode & 1;
}

int card_state::mcu_t1_r()
{
	// T1: xtal / 4 (do *2 for high-low transitions)
	return (machine().time().as_ticks(5_MHz_XTAL / 4 * 2)) & 1;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void card_state::main_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x5fff).rom();
	map(0x6000, 0x63ff).mirror(0x1c00).ram();
	map(0xe000, 0xe000).mirror(0x1fff).w(FUNC(card_state::speech_w));
}

void card_state::main_io(address_map &map)
{
	map.global_mask(0x01);
	map(0x00, 0x01).rw(m_mcu, FUNC(i8041a_device::upi41_master_r), FUNC(i8041a_device::upi41_master_w));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

#define SCAN_CHANGED(x) \
	PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(card_state::start_scan), x)

static INPUT_PORTS_START( scanner )
	PORT_START("CARDS.0") // spades + jokers
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x6f) PORT_NAME("Scan: Spades A")
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x47) PORT_NAME("Scan: Spades 2")
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xd7) PORT_NAME("Scan: Spades 3")
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x27) PORT_NAME("Scan: Spades 4")
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xb7) PORT_NAME("Scan: Spades 5")
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x77) PORT_NAME("Scan: Spades 6")
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xe7) PORT_NAME("Scan: Spades 7")
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x0f) PORT_NAME("Scan: Spades 8")
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x9f) PORT_NAME("Scan: Spades 9")
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x5f) PORT_NAME("Scan: Spades 10")
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xcf) PORT_NAME("Scan: Spades J")
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x3f) PORT_NAME("Scan: Spades Q")
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xaf) PORT_NAME("Scan: Spades K")
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xf9) PORT_NAME("Scan: Joker 1")
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xed) PORT_NAME("Scan: Joker 2")

	PORT_START("CARDS.1") // hearts
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x7b) PORT_NAME("Scan: Hearts A")
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x53) PORT_NAME("Scan: Hearts 2")
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xc3) PORT_NAME("Scan: Hearts 3")
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x33) PORT_NAME("Scan: Hearts 4")
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xa3) PORT_NAME("Scan: Hearts 5")
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x63) PORT_NAME("Scan: Hearts 6")
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xf3) PORT_NAME("Scan: Hearts 7")
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x1b) PORT_NAME("Scan: Hearts 8")
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x8b) PORT_NAME("Scan: Hearts 9")
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x4b) PORT_NAME("Scan: Hearts 10")
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xdb) PORT_NAME("Scan: Hearts J")
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x2b) PORT_NAME("Scan: Hearts Q")
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xbb) PORT_NAME("Scan: Hearts K")

	PORT_START("CARDS.2") // clubs
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x69) PORT_NAME("Scan: Clubs A")
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x95) PORT_NAME("Scan: Clubs 2")
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xd1) PORT_NAME("Scan: Clubs 3")
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x93) PORT_NAME("Scan: Clubs 4")
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xb1) PORT_NAME("Scan: Clubs 5")
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x71) PORT_NAME("Scan: Clubs 6")
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xe1) PORT_NAME("Scan: Clubs 7")
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x87) PORT_NAME("Scan: Clubs 8")
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x99) PORT_NAME("Scan: Clubs 9")
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x59) PORT_NAME("Scan: Clubs 10")
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xc9) PORT_NAME("Scan: Clubs J")
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x39) PORT_NAME("Scan: Clubs Q")
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xa9) PORT_NAME("Scan: Clubs K")

	PORT_START("CARDS.3") // diamonds
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x7d) PORT_NAME("Scan: Diamonds A")
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x55) PORT_NAME("Scan: Diamonds 2")
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xc5) PORT_NAME("Scan: Diamonds 3")
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x35) PORT_NAME("Scan: Diamonds 4")
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xa5) PORT_NAME("Scan: Diamonds 5")
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x65) PORT_NAME("Scan: Diamonds 6")
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xf5) PORT_NAME("Scan: Diamonds 7")
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x1d) PORT_NAME("Scan: Diamonds 8")
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x8d) PORT_NAME("Scan: Diamonds 9")
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x4d) PORT_NAME("Scan: Diamonds 10")
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xdd) PORT_NAME("Scan: Diamonds J")
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0x2d) PORT_NAME("Scan: Diamonds Q")
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_OTHER) SCAN_CHANGED(0xbd) PORT_NAME("Scan: Diamonds K")
INPUT_PORTS_END

static INPUT_PORTS_START( brc )
	PORT_INCLUDE( scanner )

	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("A")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("10")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("K")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME("Q")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_SLASH) PORT_NAME("P")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME("J")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_STOP) PORT_NAME("NT")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("EN")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("SC")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("PL")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_COMMA) PORT_NAME("Spades")

	PORT_START("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CL")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("DB")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("VL")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Hearts")

	PORT_START("IN.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_CODE(KEYCODE_SPACE) PORT_NAME("Speaker")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("PB")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("CV")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("Diamonds")

	PORT_START("IN.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("BR")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("DL")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("Clubs")

	PORT_START("RESET") // is not on matrix IN.7 d0
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(card_state::reset_button), 0) PORT_NAME("RE")
INPUT_PORTS_END

static INPUT_PORTS_START( bv3 )
	PORT_INCLUDE( brc )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("Ace")

	PORT_MODIFY("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("King")

	PORT_MODIFY("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME("Queen")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_SLASH) PORT_NAME("Quit")

	PORT_MODIFY("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME("Jack")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_STOP) PORT_NAME("No Trump")

	PORT_MODIFY("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Yes/Enter")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("No/Pass")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("Player")

	PORT_MODIFY("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Clear")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Double")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Score")

	PORT_MODIFY("IN.6")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Auto")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Conv")

	PORT_MODIFY("IN.7")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Review")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("Dealer")

	PORT_MODIFY("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(card_state::reset_button), 0) PORT_NAME("Reset")
INPUT_PORTS_END

static INPUT_PORTS_START( gin )
	PORT_INCLUDE( bv3 )

	PORT_MODIFY("IN.2")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_SLASH) PORT_NAME("Human")

	PORT_MODIFY("IN.3")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_STOP) PORT_NAME("Computer")

	PORT_MODIFY("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Yes/Go")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("No")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("Hand")

	PORT_MODIFY("IN.5")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Score")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Conv")

	PORT_MODIFY("IN.6")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Quit")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Language")

	PORT_MODIFY("IN.7")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Knock")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("Dealer")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void card_state::brc_base(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 5_MHz_XTAL/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &card_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &card_state::main_io);
	config.set_perfect_quantum(m_maincpu);

	I8041A(config, m_mcu, 5_MHz_XTAL);
	m_mcu->p1_out_cb().set(FUNC(card_state::mcu_p1_w));
	m_mcu->p2_in_cb().set(FUNC(card_state::mcu_p2_r));
	m_mcu->p2_out_cb().set(m_i8243, FUNC(i8243_device::p2_w));
	m_mcu->prog_out_cb().set(m_i8243, FUNC(i8243_device::prog_w));
	m_mcu->t0_in_cb().set(FUNC(card_state::mcu_t0_r));
	m_mcu->t1_in_cb().set(FUNC(card_state::mcu_t1_r));

	I8243(config, m_i8243);
	m_i8243->p4_out_cb().set(FUNC(card_state::ioexp_port_w<0>));
	m_i8243->p5_out_cb().set(FUNC(card_state::ioexp_port_w<1>));
	m_i8243->p6_out_cb().set(FUNC(card_state::ioexp_port_w<2>));
	m_i8243->p7_out_cb().set(FUNC(card_state::ioexp_port_w<3>));

	TIMER(config, "barcode_shift").configure_periodic(FUNC(card_state::barcode_shift), attotime::from_msec(2));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 16);
	m_display->set_segmask(0xff, 0x3fff);
	config.set_default_layout(layout_fidel_brc);
}

void card_state::brc(machine_config &config)
{
	brc_base(config);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void card_state::vbrc(machine_config &config)
{
	brc_base(config);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	S14001A(config, m_speech, 25000); // R/C circuit, around 25khz
	m_speech->bsy().set_inputline("maincpu", Z80_INPUT_LINE_WAIT);
	m_speech->add_route(ALL_OUTPUTS, "speaker", 0.75);
}

void card_state::bv3(machine_config &config)
{
	brc(config);
	config.set_default_layout(layout_fidel_bv3);
}

void card_state::gin(machine_config &config)
{
	brc(config);
	config.set_default_layout(layout_fidel_gin);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( bridgec ) // model BRC, PCB label 510-4020-1C
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD("bridge-1", 0x0000, 0x2000, CRC(83319c59) SHA1(db2dffb99320cbd60d33dcf9bb56a51266a041b2) ) // NEC 2364C 069
	ROM_LOAD("bridge-2", 0x2000, 0x2000, CRC(7c54f9bc) SHA1(e57221ea3e22238192bb260ad5e385f5179bb34e) ) // NEC 2364C 070
	ROM_LOAD("bridge-3", 0x4000, 0x1000, CRC(d3cda2e3) SHA1(69b62fa22b388a922abad4e89c78bdb01a5fb322) ) // NEC 2332C 188

	ROM_REGION( 0x0400, "mcu", 0 )
	ROM_LOAD("d8041c_531", 0x0000, 0x0400, CRC(b2baaaec) SHA1(bb0764d91dc1dcb143213faba204c2f2ff80aa33) ) // no custom label
ROM_END


ROM_START( bridgeca ) // model UBC, PCB label 510-4020-1C
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101-64108", 0x0000, 0x2000, CRC(08472223) SHA1(859865b13c908dbb474333263dc60f6a32461141) ) // NEC 2364C 210
	ROM_LOAD("101-64109", 0x2000, 0x2000, CRC(320afa0f) SHA1(90edfe0ac19b108d232cda376b03a3a24befad4c) ) // NEC 2364C 211
	ROM_LOAD("101-64110", 0x4000, 0x2000, CRC(3040d0bd) SHA1(caa55fc8d9196e408fb41e7171a68e5099519813) ) // NEC 2364C 212

	ROM_REGION( 0x0400, "mcu", 0 )
	ROM_LOAD("100-1009", 0x0000, 0x0400, CRC(60eb343f) SHA1(8a63e95ebd62e123bdecc330c0484a47c354bd1a) ) // NEC D8041C 563
ROM_END

ROM_START( bridgecv ) // model VBRC aka 7002/BV2
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101-64108", 0x0000, 0x2000, CRC(08472223) SHA1(859865b13c908dbb474333263dc60f6a32461141) ) // NEC 2364C 210
	ROM_LOAD("101-64109", 0x2000, 0x2000, CRC(320afa0f) SHA1(90edfe0ac19b108d232cda376b03a3a24befad4c) ) // NEC 2364C 211
	ROM_LOAD("101-64110", 0x4000, 0x2000, CRC(3040d0bd) SHA1(caa55fc8d9196e408fb41e7171a68e5099519813) ) // NEC 2364C 212

	ROM_REGION( 0x0400, "mcu", 0 )
	ROM_LOAD("100-1009", 0x0000, 0x0400, CRC(60eb343f) SHA1(8a63e95ebd62e123bdecc330c0484a47c354bd1a) ) // NEC D8041C 563

	ROM_REGION( 0x1000, "speech", 0 )
	ROM_LOAD("101-32118", 0x0000, 0x1000, CRC(a0b8bb8f) SHA1(f56852108928d5c6caccfc8166fa347d6760a740) )
ROM_END


ROM_START( bridgec3 ) // model BV3 aka 7014, PCB label 510-1016 Rev.1
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("7014_white", 0x0000, 0x2000, CRC(eb1620ef) SHA1(987a9abc8c685f1a68678ea4ee65ec4a99419179) ) // TMM2764AD-20, white sticker
	ROM_LOAD("7014_red",   0x2000, 0x2000, CRC(74af0019) SHA1(8dc05950c254ca050b95b93e5d0cf48f913a6d49) ) // TMM2764AD-20, red sticker
	ROM_LOAD("7014_blue",  0x4000, 0x2000, CRC(341d9ca6) SHA1(370876573bb9408e75f4fc797304b6c64af0590a) ) // TMM2764AD-20, blue sticker

	ROM_REGION( 0x0400, "mcu", 0 )
	ROM_LOAD("100-1009", 0x0000, 0x0400, CRC(60eb343f) SHA1(8a63e95ebd62e123bdecc330c0484a47c354bd1a) ) // NEC P07021-027 || D8041C 563 100-1009

	ROM_REGION( 0x1000, "speech", 0 )
	ROM_LOAD("101-32118", 0x0000, 0x1000, CRC(a0b8bb8f) SHA1(f56852108928d5c6caccfc8166fa347d6760a740) ) // ea 101-32118 || (C) 1980 || EA 8332A247-4 || 8034
ROM_END


ROM_START( gincribc ) // model GIN, PCB label 510-4020-1C
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD("101-1036a01", 0x0000, 0x2000, CRC(30d8d900) SHA1(b31a4acc52143baad28a35ec515ab30d7b39683a) ) // MOSTEK MK36974N-5
	ROM_LOAD("101-1037a02", 0x2000, 0x2000, CRC(8802a71b) SHA1(416350acc1cbf38ff74194d49916b848bf6c2330) ) // MOSTEK MK36976N-5
	ROM_LOAD("bridge-3",    0x4000, 0x1000, CRC(d3cda2e3) SHA1(69b62fa22b388a922abad4e89c78bdb01a5fb322) ) // NEC 2332C 188

	ROM_REGION( 0x0400, "mcu", 0 )
	ROM_LOAD("100-1009", 0x0000, 0x0400, CRC(60eb343f) SHA1(8a63e95ebd62e123bdecc330c0484a47c354bd1a) ) // NEC D8041C 563
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT    COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY, FULLNAME, FLAGS
SYST( 1979, bridgec,  0,        0,      brc,     brc,   card_state, empty_init, "Fidelity Electronics", "Bridge Challenger", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_CONTROLS )

SYST( 1979, bridgeca, 0,        0,      brc,     brc,   card_state, empty_init, "Fidelity Electronics", "Advanced Bridge Challenger", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_CONTROLS )
SYST( 1979, bridgecv, bridgeca, 0,      vbrc,    brc,   card_state, empty_init, "Fidelity Electronics", "Voice Bridge Challenger", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_CONTROLS )

SYST( 1982, bridgec3, 0,        0,      bv3,     bv3,   card_state, empty_init, "Fidelity Electronics", "Bridge Challenger III", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_CONTROLS )

SYST( 1982, gincribc, 0,        0,      gin,     gin,   card_state, empty_init, "Fidelity Electronics", "Gin & Cribbage Challenger", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_CONTROLS )



cardinal.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for Standard Microsystems Cardinal.

    This is a generic video display terminal controller on a Eurocard-sized
    PCB. It provides space for two RS-232-C connectors and pin headers for
    a serial keyboard and monitor.

    All video timing and display signal generation, including the character
    set, is integrated within Standard Microsystems' CRT9028 Video Terminal
    Logic Controller.

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

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "bus/rs232/rs232.h"
#include "machine/eepromser.h"
#include "sound/spkrdev.h"
#include "video/crt9028.h"
#include "screen.h"
#include "speaker.h"


namespace {

class cardinal_state : public driver_device
{
public:
	cardinal_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_eeprom(*this, "eeprom")
		, m_vtlc(*this, "vtlc")
		, m_speaker(*this, "speaker")
		, m_rs232(*this, "rs232")
		, m_address_select(false)
	{
	}

	void cardinal(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	u8 p1_r();
	void p1_w(u8 data);

	u8 vtlc_r();
	void vtlc_w(u8 data);

	void prog_map(address_map &map) ATTR_COLD;
	void ext_map(address_map &map) ATTR_COLD;
	void ram_map(address_map &map) ATTR_COLD;

	required_device<eeprom_serial_93cxx_device> m_eeprom;
	required_device<crt9028_device> m_vtlc;
	required_device<speaker_sound_device> m_speaker;
	required_device<rs232_port_device> m_rs232;

	bool m_address_select;
};


void cardinal_state::machine_start()
{
	save_item(NAME(m_address_select));
}

u8 cardinal_state::p1_r()
{
	return 0x9f | (m_eeprom->do_read() << 5) | (0 /*m_rs232->cts_r()*/ << 6);
}

void cardinal_state::p1_w(u8 data)
{
	m_eeprom->cs_write(BIT(data, 0));
	m_eeprom->di_write(BIT(data, 4));
	m_eeprom->clk_write(BIT(data, 3));

	m_address_select = BIT(data, 1);

	m_speaker->level_w(!BIT(data, 2));
	m_rs232->write_rts(BIT(data, 7));
}

u8 cardinal_state::vtlc_r()
{
	return m_vtlc->read(m_address_select);
}

void cardinal_state::vtlc_w(u8 data)
{
	m_vtlc->write(m_address_select, data);
}

void cardinal_state::prog_map(address_map &map)
{
	map(0x0000, 0x0fff).rom().region("program", 0);
}

void cardinal_state::ext_map(address_map &map)
{
	map(0, 0).mirror(0xffff).rw(FUNC(cardinal_state::vtlc_r), FUNC(cardinal_state::vtlc_w));
}

void cardinal_state::ram_map(address_map &map)
{
	map(0x000, 0x7ff).ram();
}


static INPUT_PORTS_START(cardinal)
	PORT_START("P3")
	PORT_DIPNAME(0x10, 0x00, "Keyboard Baud Rate")
	PORT_DIPSETTING(0x10, "300")
	PORT_DIPSETTING(0x00, "600")
	PORT_BIT(0xef, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

static DEVICE_INPUT_DEFAULTS_START(keyboard)
	DEVICE_INPUT_DEFAULTS("RS232_TXBAUD", 0xff, RS232_BAUD_600)
DEVICE_INPUT_DEFAULTS_END


void cardinal_state::cardinal(machine_config &config)
{
	i8031_device &maincpu(I8031(config, "maincpu", 7.3728_MHz_XTAL));
	maincpu.set_addrmap(AS_PROGRAM, &cardinal_state::prog_map);
	maincpu.set_addrmap(AS_IO, &cardinal_state::ext_map);
	maincpu.port_in_cb<1>().set(FUNC(cardinal_state::p1_r));
	maincpu.port_out_cb<1>().set(FUNC(cardinal_state::p1_w));
	maincpu.port_in_cb<3>().set_ioport("P3");

	EEPROM_93C06_16BIT(config, m_eeprom);

	CRT9028_000(config, m_vtlc, 10.92_MHz_XTAL);
	m_vtlc->set_screen("screen");
	m_vtlc->set_addrmap(0, &cardinal_state::ram_map);
	m_vtlc->vsync_callback().set_inputline("maincpu", MCS51_INT0_LINE).invert();

	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.05);

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);

	rs232_port_device &kb(RS232_PORT(config, "kb", default_rs232_devices, "keyboard"));
	kb.set_option_device_input_defaults("keyboard", DEVICE_INPUT_DEFAULTS_NAME(keyboard));
	kb.rxd_handler().set_inputline("maincpu", MCS51_INT1_LINE).invert();
}


// STANDARD MICROSYSTEMS CORP. CARDINAL © 1984
// ASSY NO.710.015 REV
// Complete IC list:
// * TI SN74LS240N (A1)
// * NS NMC9306N (A2)
// * Intel P8031AH (A3)
// * SMC CRT9028-000 (A4)
// * Hitachi HM6116LP-4 (A5)
// * TI SN74LS373N (A6)
// * TI MC1489A/75189AN (A7)
// * 2732 EPROM in 28-pin socket (A8)
// * TI MC1488/75188N (A9)
// Oscillators: 7.3728 (Y1), 10.920MHz (Y2)
ROM_START(cardinal)
	ROM_REGION(0x1000, "program", 0)
	ROM_LOAD("smc_8031_crt9028.bin", 0x0000, 0x1000, CRC(486705d0) SHA1(39f3fd80a72756b3267d771202cf917060eb04e1))
ROM_END

} // anonymous namespace


COMP(1984, cardinal, 0, 0, cardinal, cardinal, cardinal_state, empty_init, "Standard Microsystems", "Cardinal Video Terminal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



casio_rompack.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:

/*
    This is just a holder for the Casio ROM Pack Software List to ensure they aren't orphaned
    These ROM Packs are used by various devices, but none of the devices have been dumped

    Once a supported system is dumped this can be removed and the list can be hooked up to that

    Possible systems:

    TODO
*/

#include "emu.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "softlist_dev.h"


namespace {

class casiorom_state : public driver_device
{
public:
	casiorom_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_cart(*this, "cartslot")
	{ }

	void casiorom(machine_config &config);
protected:
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
	optional_device<generic_slot_device> m_cart;
};


static INPUT_PORTS_START( casiorom )
INPUT_PORTS_END

DEVICE_IMAGE_LOAD_MEMBER(casiorom_state::cart_load)
{
	uint32_t size = m_cart->common_get_size("rom");
	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");
	return std::make_pair(std::error_condition(), std::string());
}

void casiorom_state::casiorom(machine_config &config)
{
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "casio_rompack");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(casiorom_state::cart_load));
	m_cart->set_must_be_loaded(true);

	SOFTWARE_LIST(config, "cart_list").set_original("casio_rompack");
}

ROM_START( casiorom )
ROM_END

} // anonymous namespace


CONS( 198?, casiorom, 0, 0, casiorom,  casiorom, casiorom_state, empty_init, "Casio", "Casio ROM Pack Software List holder", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



casloopy.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Phil Bennett
/*****************************************************************************

    Casio Loopy (c) 1995 Casio

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

Casio Loopy PCB Layout
----------------------

JCM631-MA1M C
|---------------------------------------------------------|
|    CB    CC              CD         CE       CF      CG |
|--|                                                      |
   |                                        BA10339F      |
|--| 15218  |--|     CXA1645M                           CH|
|           |  |                A1603C                    |
|    15218  |  |                                          |
|           |  |                                          |
|BIOS.LSI352|  |                                          |
|           |  |                      21MHz               |
| |--------||  |   |------|                 SW1           |
| |NEC     ||  |   |SH7021|      |----------|             |
| |CDT109  ||CA|   |      |      |          |             |
| |        ||  |   |------|      |CASIO     |             |
| |--------||  |                 |RH-7500   |             |
|           |  |                 |5C315     |          |--|
| |-------| |  |                 |          |          |
| |CASIO  | |  |                 |----------|          |--|
| |RH-7501| |  |  HM514260                                |
| |5C350  | |  |                               HM62256    |
| |-------| |  |                                          |
| 6379      |--|    SW301                      HM62256    /
|--------|                        HM538123               /
         |                                              /
         |                                             /
         |--------------------------------------------/

Notes:
      Connectors
      ----------
      CA - Cartridge connector
      CB - Power Input connector
      CC - Composite Video and Audio Out connector
      CD - Printer Cassette Motor connector
      CE - Printer Data connector
      CF - Printer Head connector
      CG - Paper Sensor connector
      CH - Joystick connector
      Connectors on the back of the main unit include RCA audio (left/right), RCA composite video,
      24V DC power input and contrast knob.
      On top of the main unit, there is a reset button, on/off slide switch, a big eject button, a
      button to cut off stickers after they're printed, a button to open the hatch where the sticker
      cassette is located and a red LED for power.

      IC's
      ----
      LSI352      - Hitachi HN62434 512k x8 (4MBit) mask ROM (SOP40)
      CDT-109     - NEC CDT109 (QFP120). This is the sound chip.
      RH-7500     - Casio RH-7500 5C315 (QFP208). This is the graphics generator chip.
      RH-7501     - Casio RH-7501 5C350 (QFP64). This is a support chip for the sound chip.
      SH7021      - Hitachi HD6437021TE20 SuperH RISC Engine SH-1 CPU with 32k internal mask ROM (TQFP100)
      CXA1645M    - Sony CXA1645M RGB Encoder (RGB -> Composite Video) (SOIC24)
      A1603C      - NEC uPA1603C Compound Field Effect Power Transistor Array (DIP16)
      HM514260    - Hitachi HM514260 256k x 16 DRAM (SOJ40)
      HM538123    - Hitachi HM538123 128k x8 multi-port Video DRAM with 256-word x8 serial access memory (SOJ40)
      HM62256     - Hitachi HM62256 32k x8 SRAM (SOP28)
      BA10339F    - Rohm BA10339F Quad Comparitor (SOIC14)
      6379        - NEC uPD6379 2-channel 16-bit D/A convertor for digital audio signal demodulation (SOIC8)
      15218       - Rohm BA15218 Dual High Slew Rate, Low Noise Operational Amplifier (SOIC8)

      Other
      -----
      SW1        - Reset Switch
      SW301      - ON/OFF Slide Switch


Inside the carts
----------------

Carts 401 - 404:
PCB 'JCM632-AN1M C'
1x 16M mask ROM (SOP44)
1x 8k x8 SRAM (SOP28)
1x 3V coin battery (CR2032)

Cart 501:
PCB 'Z544-1 A240427-1'
1x 16M mask ROM (SOP44)
1x 8k x8 SRAM (SOP28)
1x OKI MSM6653A Voice Synthesis IC with 544Kbits internal maskROM (SOP24)
1x Rohm BA15218 High Slew Rate, Low Noise, Dual Operational Amplifier (SOIC8)
1x 74HC273 logic chip
1x 3V coin battery (CR2032)

Cart 502:
PCB 'Z545-1 A240570-1'
1x 16M mask ROM (SOP44)
1x 32k x8 SRAM (SOP28)
1x 74HC00 logic chip
1x 3V coin battery (CR2032)



 TODO:
- Fix 8-bit readback mode for color blended pixels (this seems broken in HW)
- Clean up and condense video layer priority logic
- Factor out joypad and mouse into slot devices
- Sound
- ADPCM sound for wanwanam
- Printer

 Issues:
- vswordp: Green pixels show up in place of the demo images

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

#include "emu.h"

#include "cpu/sh/sh7021.h"
#include "bus/casloopy/rom.h"
#include "bus/casloopy/slot.h"
#include "machine/timer.h"

#include "emuopts.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

/*************************************
 *
 *  Definitions
 *
 *************************************/

class casloopy_state : public driver_device
{
public:
	casloopy_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cart(*this, "cartslot")
		, m_palette(*this, "palette")
		, m_screen(*this, "screen")
		, m_io0(*this, "CONTROLLER_0")
		, m_io1(*this, "CONTROLLER_1")
		, m_io2(*this, "CONTROLLER_2")
		, m_mouse(*this, "MOUSE")
		, m_mouse_x(*this, "MOUSE_X")
		, m_mouse_y(*this, "MOUSE_Y")
		, m_config(*this, "CONFIG")
	{ }

	void casloopy(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

private:
	static constexpr XTAL SH1_CLOCK = XTAL(16'000'000);
	static constexpr XTAL VIDEO_CLOCK = XTAL(21'477'272);
	static constexpr int H_TOTAL = 341;
	static constexpr int V_TOTAL = 263;
	static constexpr int H_ACTIVE = 256;
	static constexpr int V_ACTIVE_0 = 224;
	static constexpr int V_ACTIVE_1 = 240;

	required_device<sh7021_device> m_maincpu;
	required_device<casloopy_cart_slot_device> m_cart;
	required_device<palette_device> m_palette;
	required_device<screen_device> m_screen;
	required_ioport m_io0;
	required_ioport m_io1;
	required_ioport m_io2;
	required_ioport m_mouse;
	required_ioport m_mouse_x;
	required_ioport m_mouse_y;
	required_ioport m_config;

	std::unique_ptr<u16[]> m_sprite_ram;
	std::unique_ptr<u16[]> m_palette_ram;
	std::unique_ptr<u16[]> m_vram;
	std::unique_ptr<u8[]> m_bitmap_vram;

	std::unique_ptr<u16[]> m_bmp_line[4];
	std::unique_ptr<u16[]> m_tile_line[2];
	std::unique_ptr<u16[]> m_spr_line[2];

	emu_timer * m_hblank_timer = nullptr;

	u8 m_bmp_latch[4];
	u16 m_readback_buffer[256];
	u16 m_readback_latch;
	s16 m_d_mouse_x;
	s16 m_d_mouse_y;

	u16 m_system_control;
	u16 m_bmp_vram_x[4];
	u16 m_bmp_vram_y[4];
	u16 m_bmp_screen_x[4];
	u16 m_bmp_screen_y[4];
	u16 m_bmp_size_x[4];
	u16 m_bmp_size_y[4];
	u16 m_bmp_color;
	u16 m_bmp_control;
	u16 m_bmp_span[4];
	u16 m_screen_width;
	u16 m_screen_height;
	u16 m_int_control;
	u16 m_tilemap_color[2];
	u16 m_tilemap_control;
	u16 m_tilemap_page;
	u16 m_tilemap_scroll_x[2];
	u16 m_tilemap_scroll_y[2];
	u16 m_layer_control_0;
	u16 m_layer_enable;
	u16 m_layer_control_2;
	u16 m_sprite_control;
	u16 m_sprite_color[2];
	u16 m_color[2];
	u16 m_readback_control;
	u16 m_clear_mask;
	u16 m_clear_color;
	u16 m_5d020;
	u16 m_5d030;
	u16 m_5d040;
	u16 m_5d042;
	u16 m_5d044;
	u16 m_seal_sw;

	// Byte offsets
	static constexpr offs_t s_gfxdata_offsets[] =
	{
		0x4000,
		0x2000,
		0x2000,
		0x1000,
		0x2000,
		0x1000,
		0x1000,
		0x800
	};

	enum Layer : u8
	{
		B0,
		B1,
		B2,
		B3,
		X0,
		X1,
		Y0,
		Y1,
		S0,
		S1,
		NG,
	};

	static const Layer s_m4_pri_2[256][16][10];
	static const Layer s_m4_pri_4[256][16][10];
	static const Layer s_m4_pri_6[256][16][10];
	static const Layer s_m5_pri_6[256][16][10];
	static const Layer s_m0_pri_6[256][16][10];

	int bitmap_bpp() const;
	offs_t tilemap_offset(int which) const;
	offs_t tiledata_offset() const;
	offs_t spritedata_offset() const;
	u8 readback_mode() const;
	Layer priority(int mode1, int mode2, int mode3, int mode4, int i) const;
	u16 background_color() const;
	u16 border_color() const;

	TIMER_CALLBACK_MEMBER(hblank_start);
	void update_scanline_buffers(int y);
	void draw_bitmap_line(int which, int y);
	void draw_tilemap_line(int which, int y);
	void draw_sprite_line(int y);
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void casloopy_map(address_map &map) ATTR_COLD;

	u8 bitmap_r(offs_t offset);
	void bitmap_w(offs_t offset, u8 data);
	u16 vram_r(offs_t offset);
	void vram_w(offs_t offset, u16 data);
	u16 sprite_ram_r(offs_t offset);
	void sprite_ram_w(offs_t offset, u16 data);
	u16 palette_r(offs_t offset);
	u16 readback_r(offs_t offset);
	void palette_w(offs_t offset, u16 data);
	u16 system_control_r();
	void system_control_w(u16 data);
	u16 hpos_r();
	void hpos_w(u16 data);
	u16 vpos_r();
	void vpos_w(u16 data);
	u16 reg_58006_r();
	void reg_58006_w(u16 data);
	void reg_58008_w(u16 data);
	u16 reg_5d000_r();
	void reg_5d000_w(u16 data);
	u16 controller_0_r();
	u16 controller_1_r();
	u16 controller_2_r();
	u16 reg_5d020_r();
	void reg_5d020_w(u16 data);
	u16 reg_5d030_r();
	void reg_5d030_w(u16 data);
	u16 reg_5d040_r();
	void reg_5d040_w(u16 data);
	u16 reg_5d042_r();
	void reg_5d042_w(u16 data);
	u16 reg_5d044_r();
	void reg_5d044_w(u16 data);
	u16 reg_5d054_r();
	void reg_5d054_w(u16 data);
	u16 reg_5e000_r();
	void reg_5e000_w(u16 data);
	void reg_60000_w(u16 data);
	void reg_a0000_w(u16 data);
	u16 default_mouse_state();
	u16 mouse1_r();
	u16 mouse2_r();
	void fast_clear_w(offs_t offset, u16 data);
	void sound_control_w(u16 data);
};


/*************************************
 *
 *  Video emulation
 *
 *************************************/

void casloopy_state::video_start()
{
	m_palette_ram = make_unique_clear<u16[]>(0x1000);
	m_vram = make_unique_clear<u16[]>(0x8000);
	m_bitmap_vram = make_unique_clear<u8[]>(0x20000);
	m_sprite_ram = make_unique_clear<u16[]>(0x100);

	m_bmp_line[0] = std::make_unique<u16[]>(256);
	m_bmp_line[1] = std::make_unique<u16[]>(256);
	m_bmp_line[2] = std::make_unique<u16[]>(256);
	m_bmp_line[3] = std::make_unique<u16[]>(256);
	m_spr_line[0] = std::make_unique<u16[]>(256);
	m_spr_line[1] = std::make_unique<u16[]>(256);
	m_tile_line[0] = std::make_unique<u16[]>(256);
	m_tile_line[1] = std::make_unique<u16[]>(256);

	m_hblank_timer = timer_alloc(FUNC(casloopy_state::hblank_start), this);
	m_hblank_timer->adjust(m_screen->time_until_pos(0, 256));

	save_pointer(NAME(m_palette_ram), 0x1000);
	save_pointer(NAME(m_vram), 0x8000);
	save_pointer(NAME(m_bitmap_vram), 0x20000);
	save_pointer(NAME(m_sprite_ram), 0x100);
}

int casloopy_state::bitmap_bpp() const
{
	static constexpr int bpp_table[] = { 8, 8, 4, 4, 4, 8, 8, 8 };
	return bpp_table[m_bmp_control];
}

offs_t casloopy_state::tilemap_offset(int which) const
{
	const u32 size = BIT(m_tilemap_control, 0, 3);

	if ((which == 0) || (size & 1))
		return 0;

	return s_gfxdata_offsets[size] / 2;
}

offs_t casloopy_state::tiledata_offset() const
{
	if (!BIT(m_tilemap_control, 3))
	{
		// 4bpp
		u32 v = m_tilemap_page & 0x7f;
		const offs_t page = v * 0x200;
		return (s_gfxdata_offsets[BIT(m_tilemap_control, 0, 3)] + page) / 2;
	}
	else
	{
		// 8bpp
		return s_gfxdata_offsets[BIT(m_tilemap_control, 0, 3)] / 2;
	}
}

offs_t casloopy_state::spritedata_offset() const
{
	return s_gfxdata_offsets[BIT(m_tilemap_control, 0, 3)] / 2;
}

u8 casloopy_state::readback_mode() const
{
	switch (BIT(m_readback_control, 8, 2))
	{
		case 0:
		case 1:
			return 1;
		case 2:
		case 3:
		default:
			return 0;
	}
}

#include "casloopy_tbl.ipp"

casloopy_state::Layer casloopy_state::priority(int mode1, int mode2, int mode3, int mode4, int i) const
{
	static constexpr u8 s_layers_enabled[8][16] =
	{
		{ 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1 },
		{ 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1 },
		{ 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1 },
		{ 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1 },
		{ 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0 },
		{ 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1 },
		{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
	};

	Layer result = NG;

	switch (mode2)
	{
		case 0:
		case 1:
		case 8:
		case 9:
		case 0xb:
			result = NG;
			break;

		case 2:
		case 0xa:
			result = s_m4_pri_2[mode3][mode4][i];
			break;

		case 4:
		case 5:
		case 7:
		case 0xc:
		case 0xd:
		case 0xf:
			result = s_m4_pri_4[mode3][mode4][i];
			break;

		case 6:
		case 0xe:
			switch (mode1)
			{
				case 0:
				case 1:
				case 3:
					result = s_m0_pri_6[mode3][mode4][i];
					break;

				case 2:
					result = s_m4_pri_4[mode3][mode4][i];
					break;

				case 4:
					result = s_m4_pri_6[mode3][mode4][i];
					break;

				case 5:
					result = s_m5_pri_6[mode3][mode4][i];
					break;

				default:
					result = NG;
					break;

			}
			break;
	}

	return s_layers_enabled[mode1][mode2] ? result : NG;
}

u16 casloopy_state::background_color() const
{
	const int bmode = BIT(m_layer_control_0, 0, 3);

	if (bmode == 6 || bmode == 7)
		return 0;

	const int mode = BIT(m_layer_control_2, 4, 4);

	switch (bmode)
	{
		case 0:
		case 1:
		{
			switch (mode)
			{
				case 2:
				case 3:
					return m_color[0];

				case 4:
				case 5:
				case 6:
					return m_color[1];

				case 7:
					return m_color[0];

				case 12:
				case 13:
				case 14:
				case 15:
					return m_color[1];

				default:
					return 0;
			}
		}
		case 2:
		{
			switch (mode)
			{
				case 4:
				case 5:
				case 6:
				case 7:
					return m_color[1];

				case 12:
				case 13:
				case 14:
				case 15:
					return m_color[1];

				default:
					return 0;
			}
		}
		case 3:
		{
			switch (mode)
			{
				case 2:
				case 3:
					return m_color[0];

				case 4:
				case 5:
				case 6:
				case 7:
					return m_color[1];

				case 10:
				case 11:
					return m_color[0];

				case 12:
				case 13:
				case 14:
				case 15:
					return m_color[1];

				default:
					return 0;
			}
		}
		case 4:
		{
			switch (mode)
			{
				case 3:
					return m_color[0];

				case 4:
				case 5:
				case 6:
					return m_color[1];

				case 7:
					return m_color[0];

				case 11:
					return m_color[0];

				case 12:
				case 13:
				case 14:
					return m_color[1];

				case 15:
					return m_color[0];

				default:
					return 0;
			}
		}
		case 5:
		{
			switch (mode)
			{
				case 2:
				case 3:
					return m_color[0];

				case 6:
				case 7:
					return m_color[0];

				case 10:
				case 11:
					return m_color[0];

				case 14:
				case 15:
					return m_color[0];

				default:
					return 0;
			}
		}
	}
	return 0;
}

#ifdef UNUSED_FUNCTION
u16 casloopy_state::border_color() const
{
	const int bmode = BIT(m_layer_control_0, 0, 3);

	if (bmode == 6 || bmode == 7)
		return 0;

	const int mode = BIT(m_layer_control_2, 4, 4);

	switch (mode)
	{
		case 4:
		case 5:
		case 6:
		case 7:
			return m_color[1];

		case 12:
		case 13:
		case 14:
		case 15:
			return m_color[1];

		default:
			return 0;
	}
}
#endif

void add_colors(u16 ca, u16 cb, int &r, int &g, int &b)
{
	int ar = (ca >> 10) & 0x1f;
	int ag = (ca >> 5) & 0x1f;
	int ab = (ca >> 0) & 0x1f;

	int br = (cb >> 10) & 0x1f;
	int bg = (cb >> 5) & 0x1f;
	int bb = (cb >> 0) & 0x1f;

	r = ar + br;
	g = ag + bg;
	b = ab + bb;

	// Clamping is done later
}

void sub_colors(u16 ca, u16 cb, int &r, int &g, int &b)
{
	int ar = (ca >> 10) & 0x1f;
	int ag = (ca >> 5) & 0x1f;
	int ab = (ca >> 0) & 0x1f;

	int br = (cb >> 10) & 0x1f;
	int bg = (cb >> 5) & 0x1f;
	int bb = (cb >> 0) & 0x1f;

	r = ar - br;
	g = ag - bg;
	b = ab - bb;

	if (r < 0) r = 0;
	if (g < 0) g = 0;
	if (b < 0) b = 0;
}

void casloopy_state::draw_bitmap_line(int which, int y)
{
	const int sy_start = m_bmp_screen_y[which];
	const int y_size = m_bmp_size_y[which];
	int y_offs;

	if (sy_start & 0x100)
	{
		y_offs = (0x200 - sy_start) + y;
	}
	else
	{
		y_offs = y - sy_start;

		if (y < sy_start)
			return;
	}

	if (y_offs > y_size)
		return;

	const bool is_8bpp = bitmap_bpp() == 8;
	const int sx_start = m_bmp_screen_x[which];
	const int x_size = BIT(m_bmp_size_x[which], 0, 8);
	const int x_min = BIT(m_bmp_size_x[which], 8, 8);
	const int bx_start = m_bmp_vram_x[which] & (is_8bpp ? 0xff : 0x1ff);
	const int by_start = m_bmp_vram_y[which];
	const int pal_offs = !is_8bpp ? ((m_bmp_color >> (4 * (3 - which))) & 0xf) * 0x10 : 0;
	const bool x_wrap = m_bmp_control == 3;
	const bool y_wrap = (m_bmp_control == 0 || m_bmp_control == 2 || m_bmp_control == 5);
	const int x_mask = x_wrap ? 0xff : 0x1ff;
	const int y_mask = y_wrap ? 0xff : 0x1ff;
	const int x_offs = x_wrap ? bx_start & 0x100 : 0;
	const u8 * bmp_ram = y_wrap ? &m_bitmap_vram[(by_start & 0x100) * 256] : &m_bitmap_vram[0];
	const int by = by_start + y_offs;
	const int x_pixels = x_size + 1;

	int x = sx_start;
	int bx = bx_start;

	for (int xp = 0; xp < x_pixels; ++xp)
	{
		if (xp >= x_min && x < 256)
		{
			u8 pix;

			if (is_8bpp)
			{
				pix = bmp_ram[(by & y_mask) * 256 + (bx & 0xff)];
			}
			else
			{
				pix = bmp_ram[(by & y_mask) * 256 + (x_offs / 2) + (bx & x_mask) / 2];
				pix = bx & 1 ? pix & 0xf : (pix >> 4) & 0xf;
			}

			if (pix != 0)
				m_bmp_line[which][x] = pal_offs | pix;
		}
		bx++;
		x = (x + 1) & 0x1ff;
	}
}

void casloopy_state::draw_tilemap_line(int which, int y)
{
	const bool is_8bpp = BIT(m_tilemap_control, 3);
	const int size = BIT(m_tilemap_control, 1, 2);
	const int ts_shift = (m_tilemap_control >> (4 + 2 * (1 ^ which))) & 3;
	const int rows = size & 1 ? 32 : 64;
	const int cols = size & 2 ? 32 : 64;
	const int col_mask = cols - 1;
	const int row_mask = rows - 1;
	const int color_base = m_tilemap_color[which];
	const int vram_offs = tilemap_offset(which) / 2;
	const int num_tiles = 1 << ts_shift;
	const int tile_size = 8 << ts_shift;
	const int scroll_x = m_tilemap_scroll_x[which];
	const int scroll_y = m_tilemap_scroll_y[which];
	const offs_t td_offs = tiledata_offset();

	int cs = scroll_x / tile_size;
	int rs = (y + scroll_y) / tile_size;
	int x_offs2 = (scroll_x % tile_size) / 8;
	int x_offs = scroll_x & 7;
	int y_offs = (y + scroll_y) % tile_size;
	int sy = y_offs & 7;
	int ys = 8 * ((y_offs / 8) % tile_size);
	int c = cs;
	int x = 0;

	while (x < 256)
	{
		const u32 tile_index = (rs & row_mask) * cols + (c & col_mask);
		const u16 data = m_vram[vram_offs + tile_index];
		const u16 code = data & 0x7ff;
		const int pri = BIT(data, 11);
		const int fx = BIT(data, 14);
		const int fy = BIT(data, 15);
		const int fx_mask = (fx * (num_tiles - 1));
		const int fy_mask = (fy * ((num_tiles - 1) << 3));

		u32 color = 0;

		if (!is_8bpp)
		{
			u32 color_idx = BIT(data, 12, 2);
			color = (color_base >> (4 * color_idx)) & 0xf;
		}

		int ctmp = ((code & ~7) + (ys ^ fy_mask));

		for (int tx = x_offs2; tx < num_tiles; ++tx)
		{
			const int idx = ctmp | ((code + (tx ^ fx_mask)) & 7);

			if (is_8bpp)
			{
				const offs_t fy_offs = fy ? 28 : 0;
				const offs_t base_offs = td_offs + 32 * idx + fy_offs;
				const u16 * pix_data = &m_vram[(base_offs ^ (sy * 4)) & 0x7fff];
				for (int sx = x_offs; sx < 8; ++sx, ++x)
				{
					int v = (fx ? 8 : 0) ^ ((sx & 1) ? 0 : 8);
					u16 word = pix_data[(fx ? 3 : 0) ^ (sx >> 1)];
					u8 pix = (word >> v) & 0xff;

					if (x < 256 && pix)
						m_tile_line[which][x] = (pri ? 0x100 : 0) | pix;
				}
			}
			else
			{
				const offs_t fy_offs = fy ? 14 : 0;
				const offs_t base_offs = td_offs + 16 * idx + fy_offs;
				const u16 * pix_data = &m_vram[(base_offs ^ (sy * 2)) & 0x7fff];
				for (int sx = x_offs; sx < 8; ++sx, ++x)
				{
					int v = (fx ? 0 : 12) ^ ((sx & 3) << 2);
					u16 word = pix_data[(fx ? 1 : 0) ^ (sx >> 2)];
					u8 pix = (word >> v) & 0xf;

					if (x < 256 && pix)
						m_tile_line[which][x] = (pri ? 0x100 : 0) | (color << 4) | pix;
				}
			}
			x_offs = 0;
		}
		x_offs2 = 0;
		c++;
	}
}

void casloopy_state::draw_sprite_line(int y)
{
	const int split = m_sprite_control & 0x7f;
	const int invert_group = BIT(m_sprite_control, 7);
	const bool is_8bpp = BIT(m_sprite_control, 14);
	const int code0 = (m_sprite_control & 0x3800) >> 3;
	const int code1 = m_sprite_control & 0x0700;
	const u16 * vram = m_vram.get() + spritedata_offset();

	int tiles_left = 32;

	for (int i = 0; i < 128; ++i)
	{
		const u16 word0 = m_sprite_ram[i * 2 + 0];
		const u16 word1 = m_sprite_ram[i * 2 + 1];
		const int size = (word1 >> 10) & 0x3;
		int y_start = word0 & 0xff;

		if (BIT(word1, 9))
			y_start -= 256;

		static constexpr int x_sizes[] = { 1, 2, 2, 4 };
		static constexpr int y_sizes[] = { 1, 2, 4, 4 };

		const int y_offs = y - y_start;

		if (y < y_start || y_offs >= (8 * y_sizes[size]))
			continue;

		const int idx = invert_group ^ (i < split);
		const int code = (word0 >> 8) | (idx == 0 ? code0 : code1);
		const u32 color_idx = (word1 >> 12) & 0x3;
		const u32 color = (m_sprite_color[idx] >> (4 * color_idx)) & 0xf;
		const u32 color_offs = color << 4;
		const int fx = BIT(word1, 14);
		const int fy = BIT(word1, 15);
		const int x_tiles = x_sizes[size];
		const int y_tiles = y_sizes[size];
		const int sy = y_offs & 7;
		const int ys = 8 * ((y_offs / 8) % (y_tiles * 8));
		const int fx_mask = (fx * (x_tiles - 1));
		const int fy_mask = (fy * ((y_tiles - 1) << 3));

		u16 * line_buffer = &m_spr_line[idx][0];
		int x = word1 & 0x1ff;
		int ctmp = ((code & ~7) + (ys ^ fy_mask));

		for (int tx = 0; tx < x_tiles; ++tx)
		{
			const int idx = ctmp | ((code + (tx ^ fx_mask)) & 7);

			if (is_8bpp)
			{
				const offs_t fy_offs = fy ? 28 : 0;
				const offs_t base_offs = 32 * idx + fy_offs;
				const u16 * pix_data = &vram[(base_offs ^ (sy * 4)) & 0x7fff];

				for (int sx = 0; sx < 8; ++sx)
				{
					if (x < 256 && line_buffer[x] == 0)
					{
						int v = (fx ? 8 : 0) ^ ((sx & 1) ? 0 : 8);
						u16 word = pix_data[(fx ? 3 : 0) ^ (sx >> 1)];
						u8 pix = (word >> v) & 0xff;

						if (pix)
							line_buffer[x] = pix;
					}
					x = (x + 1) & 0x1ff;
				}
			}
			else
			{
				const offs_t fy_offs = fy ? 14 : 0;
				const offs_t base_offs = 16 * idx + fy_offs;
				const u16 * pix_data = &vram[(base_offs ^ (sy * 2)) & 0x7fff];

				for (int sx = 0; sx < 8; ++sx)
				{
					if (x < 256 && line_buffer[x] == 0)
					{
						int v = (fx ? 0 : 12) ^ ((sx & 3) << 2);
						u16 word = pix_data[(fx ? 1 : 0) ^ (sx >> 2)];
						u8 pix = (word >> v) & 0xf;

						if (pix)
							line_buffer[x] = color_offs | pix;
					}
					x = (x + 1) & 0x1ff;
				}
			}
			if (--tiles_left == 0)
				return;
		}
	}
}

void casloopy_state::update_scanline_buffers(int y)
{
	std::fill_n(m_bmp_line[0].get(), 256, 0);
	std::fill_n(m_bmp_line[1].get(), 256, 0);
	std::fill_n(m_bmp_line[2].get(), 256, 0);
	std::fill_n(m_bmp_line[3].get(), 256, 0);
	std::fill_n(m_tile_line[0].get(), 256, 0);
	std::fill_n(m_tile_line[1].get(), 256, 0);
	std::fill_n(m_spr_line[0].get(), 256, 0);
	std::fill_n(m_spr_line[1].get(), 256, 0);

	if (BIT(m_layer_enable, 0))
		draw_tilemap_line(0, y);

	if (BIT(m_layer_enable, 1))
		draw_tilemap_line(1, y);

	if (BIT(m_layer_enable, 2))
		draw_bitmap_line(0, y);

	if (BIT(m_layer_enable, 3))
		draw_bitmap_line(1, y);

	if (BIT(m_layer_enable, 4))
		draw_bitmap_line(2, y);

	if (BIT(m_layer_enable, 5))
		draw_bitmap_line(3, y);

	if (BIT(m_layer_enable, 6) || BIT(m_layer_enable, 7))
		draw_sprite_line(y);
}

u32 casloopy_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const rgb_t * const palette = m_palette->palette()->entry_list_raw();
	const int wy = BIT(m_readback_control, 0, 8);
	const int bmode = BIT(m_layer_control_0, 0, 3);
	const int mode = BIT(m_layer_enable, 8, 8);
	const int mode2 = BIT(m_layer_control_2, 4, 4);
	const int pri_select = BIT(m_layer_control_2, 0, 4);
	const int rback_mode = readback_mode();
	const u16 bg = background_color();
	const bool bmp_8bpp = bitmap_bpp() == 8;
	const int bits = m_layer_enable;
	const int b10mode = BIT(bits, 8, 2);
	const int b32mode = BIT(bits, 10, 2);
	const int s0mode = BIT(bits, 12, 2);
	const int s1mode = BIT(bits, 14, 2);

	Layer pri_table[10];

	for (int i = 0; i < 10; ++i)
		pri_table[i] = priority(bmode, mode2, mode, pri_select, i);

	// Combine the layers
	for (int y = cliprect.min_y; y <= cliprect.max_y; ++y)
	{
		const bool rback_en = (y == wy) && m_readback_latch;

		if (rback_en)
			m_readback_latch = 0;

		update_scanline_buffers(y);

		for (int x = cliprect.min_x; x <= cliprect.max_x; ++x)
		{
			u16 pixels[11] = {};

			const u16 pix_x = m_tile_line[0][x];
			const u16 pix_y = m_tile_line[1][x];

			pixels[pix_x & 0x100 ? X1 : X0] = pix_x & 0xff;
			pixels[pix_y & 0x100 ? Y1 : Y0] = pix_y & 0xff;
			pixels[B0] = m_bmp_line[0][x];
			pixels[B1] = m_bmp_line[1][x];
			pixels[B2] = m_bmp_line[2][x];
			pixels[B3] = m_bmp_line[3][x];
			pixels[S0] = m_spr_line[0][x];
			pixels[S1] = m_spr_line[1][x];

			u8 l1 = NG;
			u8 l2 = NG;

			// Handle bitmap layer latching
			if (bmp_8bpp)
			{
				for (int i = 0; i < 4; ++i)
				{
					if (m_bmp_span[i] & 0x100)
					{
						if (pixels[B0 + i] < (m_bmp_span[i] & 0xff))
						{
							m_bmp_latch[i] = pixels[B0 + i];
						}
						else if (pixels[B0 + i] == 0xff)
						{
							pixels[B0 + i] = m_bmp_latch[i];
						}
					}
				}
			}
			else
			{
				for (int i = 0; i < 4; ++i)
				{
					if (m_bmp_span[i] & 0x100)
					{
						if ((pixels[B0 + i] & 0xf) < (m_bmp_span[i] & 0xf))
						{
							m_bmp_latch[i] = pixels[B0 + i] & 0xf;
						}
						else if ((pixels[B0 + i] & 0xf) == 0xf)
						{
							if (m_bmp_latch[i] == 0)
							{
								pixels[B0 + i] = 0;
							}
							else
							{
								pixels[B0 + i] &= 0xf0;
								pixels[B0 + i] |= m_bmp_latch[i];
							}
						}
					}
				}
			}

			// Get two visible pixels
			for (int i = 0; i < 10; ++i)
			{
				u8 layer = pri_table[i];

				if (pixels[layer] != 0)
				{
					if (l1 == NG)
					{
						l1 = layer;
					}
					else if (l2 == NG)
					{
						if (l1 == B0 && layer == B1)
							continue;
						if (l1 == B2 && layer == B3)
							continue;

						l2 = layer;
						break;
					}
				}
			}

			// Replacement doesn't apply?
			u8 r8 = l1 != NG ? pixels[l1] : 0;

			int r, g, b;

			const int lmode = BIT(m_layer_control_2, 4, 4);

			bool half_bright = bmode == 1;

			int mode1 = 0;
			int mode2 = 0;

			switch (l1)
			{
				case B0:
				case B1:
					mode1 = b10mode;

					if (mode1 == 3 && (bmode == 1 || bmode == 3))
						half_bright = false;

					break;

				case B2:
				case B3:
					mode1 = b32mode;

					if (mode1 == 3 && (bmode == 1 || bmode == 3))
						half_bright = false;

					break;

				case S0:
					mode1 = s0mode;
					break;

				case S1:
					mode1 = s1mode;
					break;

				case X0:
				case Y0:
					mode1 = 2;
					break;

				case X1:
				case Y1:
					mode1 = 1;
					break;
			}

			if (bmode == 0 || bmode == 1 || bmode == 3)
			{
				switch (l2)
				{
					case B0:
					case B1:
						mode2 = b10mode;
						break;

					case B2:
					case B3:
						mode2 = b32mode;
						break;

					case S0:
						mode2 = s0mode;
						break;

					case S1:
						mode2 = s1mode;
						break;

					case X0:
					case Y0:
						mode2 = 2;
						break;

					case X1:
					case Y1:
						mode2 = 1;
						break;
				}

				switch (lmode)
				{
					case 6:
					{
						// Blend
						if (mode1 != 3
							&& mode1 != mode2
							&& l2 != NG)
						{
							u16 color1 = pixels[l1];
							u16 color2 = pixels[l2];

							color1 = palette[color1].as_rgb15();
							color2 = palette[color2].as_rgb15();

							add_colors(color1, color2, r, g, b);
						}
						// Add a constant
						else
						{
							u16 color1 = palette[pixels[l1]].as_rgb15();
							u16 color2 = 0;

							switch (l1)
							{
								case B0:
								case B1:
								case B2:
								case B3:
								{
									if (mode1 == 0)
									{
										throw false;
									}
									else if (mode1 == 1)
									{
										color2 = m_color[1];
									}
									else if (mode1 == 2)
									{
										color2 = m_color[0];
									}
									else if (mode1 == 3)
									{
										color2 = 0;
									}
									break;
								}
								case S0:
								case S1:
								{
									if (mode1 == 0)
									{
										throw false;
									}
									else if (mode1 == 1)
									{
										color2 = m_color[1];
									}
									else if (mode1 == 2)
									{
										color2 = m_color[0];
									}
									else if (mode1 == 3)
									{
										color2 = color1;
									}
									break;
								}
								case X0:
								case Y0:
									color2 = m_color[0];
									break;
								case X1:
								case Y1:
									color2 = m_color[1];
									break;
								default:
									color1 = m_color[0];
									color2 = m_color[1];
									break;
							}

							add_colors(color1, color2, r, g, b);
						}
						break;
					}

					case 7:
					{
						// Add a constant
						u16 color1 = palette[pixels[l1]].as_rgb15();
						u16 color2 = 0;

						switch (l1)
						{
							case B0:
							case B1:
							case B2:
							case B3:
							case S0:
							case S1:
							{
								if (mode1 == 0 || mode1 == 1)
								{
									throw false;
								}
								else if (mode1 == 2 || mode == 3)
								{
									color2 = m_color[0];
								}
								break;
							}
							case X0:
							case Y0:
								color2 = m_color[0];
								break;
							default:
								color1 = m_color[0];
								color2 = m_color[1];
								break;
						}

						add_colors(color1, color2, r, g, b);
						break;
					}

					case 0xe:
					{
						bool skip = mode1 == 3;

						// Blend
						if (!skip
							&& mode1 != mode2
							&& l2 != NG)
						{
							u16 color1 = pixels[l1];
							u16 color2 = pixels[l2];

							color1 = palette[color1].as_rgb15();
							color2 = palette[color2].as_rgb15();

							if (mode1 == 1)
								sub_colors(color2, color1, r, g, b);
							else
								sub_colors(color1, color2, r, g, b);
						}
						// Add a constant
						else
						{
							u16 color1 = palette[pixels[l1]].as_rgb15();
							u16 color2 = 0;

							switch (l1)
							{
								case B0:
								case B1:
								case B2:
								case B3:
								case S0:
								case S1:
								{
									if (mode1 == 0)
									{
										throw false;
									}
									else if (mode1 == 1)
									{
										color2 = color1;
										color1 = m_color[1];
									}
									else if (mode1 == 2)
									{
										color2 = m_color[0];
									}
									else if (mode1 == 3)
									{
										color1 = 0;
										color2 = 0;
									}
									break;
								}
								case X0:
								case Y0:
									color2 = m_color[0];
									break;
								case X1:
								case Y1:
									color2 = color1;
									color1 = m_color[1];
									break;
								default:
									color1 = m_color[1];
									color2 = m_color[0];
									break;
							}

							sub_colors(color1, color2, r, g, b);
						}
						break;
					}
					case 0xf:
					{
						u16 color1 = palette[pixels[l1]].as_rgb15();
						u16 color2 = 0;

						switch (l1)
						{
							case B0:
							case B1:
							case B2:
							case B3:
							case S0:
							case S1:
							{
								if (mode1 == 0 || mode1 == 1)
								{
									throw false;
								}
								else if (mode1 == 2 || mode1 == 3)
								{
									color2 = m_color[0];
								}
								break;
							}
							case X0:
							case Y0:
								color2 = m_color[0];
								break;
							case X1:
							case Y1:
								throw false;
							default:
								color1 = m_color[1];
								color2 = m_color[0];
								break;
						}

						sub_colors(color1, color2, r, g, b);
						break;
					}
					default:
					{
						if (l1 != NG)
						{
							u8 idx = pixels[l1];
							u16 color = palette[idx].as_rgb15();
							r = (color >> 10) & 0x1f;
							g = (color >> 5) & 0x1f;
							b = (color >> 0) & 0x1f;
						}
						else
						{
							r = (bg >> 10) & 0x1f;
							g = (bg >> 5) & 0x1f;
							b = (bg >> 0) & 0x1f;
						}
					}
				}
			}
			else
			{
				if (l1 != NG)
				{
					u8 idx = pixels[l1];
					u16 color = palette[idx].as_rgb15();
					r = (color >> 10) & 0x1f;
					g = (color >> 5) & 0x1f;
					b = (color >> 0) & 0x1f;
				}
				else
				{
					r = (bg >> 10) & 0x1f;
					g = (bg >> 5) & 0x1f;
					b = (bg >> 0) & 0x1f;
				}
			}

			if (half_bright)
			{
				r >>= 1;
				g >>= 1;
				b >>= 1;
			}

			if (r > 0x1f) r = 0x1f;
			if (g > 0x1f) g = 0x1f;
			if (b > 0x1f) b = 0x1f;

			bitmap.pix(y, x) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));

			if (rback_en)
			{
				if (rback_mode == 0)
				{
					m_readback_buffer[x] = r8;
				}
				else if (rback_mode == 1)
				{
					m_readback_buffer[x] = (r << 10) | (g << 5) | b;
				}
			}
		}
	}

	return 0;
}

TIMER_CALLBACK_MEMBER(casloopy_state::hblank_start)
{
	int v = m_screen->vpos();

	m_screen->update_partial(v);

	if (BIT(m_int_control, 2))
	{
		const int nmi_line = BIT(m_system_control, 1) ? 239 : 223;

		if (v == nmi_line)
		{
			m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
			m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
		}
	}

	// Adjust for next scanline
	if (++v >= 262)
		v = 0;

	m_hblank_timer->adjust(m_screen->time_until_pos(v, 256));
}


/*************************************
 *
 *  Handlers
 *
 *************************************/

u8 casloopy_state::bitmap_r(offs_t offset)
{
	// Only bitmap RAM supports 8-bit accesses
	return m_bitmap_vram[offset];
}

void casloopy_state::bitmap_w(offs_t offset, u8 data)
{
	m_bitmap_vram[offset] = data;
}

u16 casloopy_state::vram_r(offs_t offset)
{
	return m_vram[offset];
}

void casloopy_state::vram_w(offs_t offset, u16 data)
{
	m_vram[offset] = data;
}

u16 casloopy_state::sprite_ram_r(offs_t offset)
{
	return m_sprite_ram[offset];
}

void casloopy_state::sprite_ram_w(offs_t offset, u16 data)
{
	m_sprite_ram[offset] = data;
}

u16 casloopy_state::palette_r(offs_t offset)
{
	return m_palette_ram[offset];
}

void casloopy_state::palette_w(offs_t offset, u16 data)
{
	m_palette_ram[offset] = data;

	int r = (m_palette_ram[offset] & 0x7c00) >> 10;
	int g = (m_palette_ram[offset] & 0x03e0) >> 5;
	int b = m_palette_ram[offset] & 0x001f;

	m_palette->set_pen_color(offset, pal5bit(r), pal5bit(g), pal5bit(b));
}

u16 casloopy_state::readback_r(offs_t offset)
{
	if (readback_mode() == 0)
	{
		int x = offset * 2;
		return (m_readback_buffer[x] << 8) | (m_readback_buffer[x + 1]);
	}
	else
	{
		return m_readback_buffer[offset];
	}
}

u16 casloopy_state::system_control_r()
{
	return m_system_control;
}

void casloopy_state::system_control_w(u16 data)
{
	// 224/240 line mode
	if (BIT(m_system_control, 1) != BIT(data, 1))
	{
		const attoseconds_t refresh = HZ_TO_ATTOSECONDS(VIDEO_CLOCK / 4) * H_TOTAL * V_TOTAL;
		const int v_active = BIT(data, 1) ? V_ACTIVE_1 : V_ACTIVE_0;

		rectangle visarea(0, H_ACTIVE - 1, 0, v_active - 1);
		m_screen->configure(H_TOTAL, V_TOTAL, visarea, refresh);
	}

	m_system_control = data;
}

u16 casloopy_state::hpos_r()
{
	int hpos = m_screen->hpos();

	if (hpos >= 258)
		hpos += 170;

	return hpos;
}

u16 casloopy_state::vpos_r()
{
	u32 vpos = m_screen->vpos();

	if (m_screen->vblank())
		return 0x100 | ((vpos - 7) & 0xff);
	else
		return vpos & 0xff;
}

u16 casloopy_state::reg_58006_r()
{
	return 0;
}

void casloopy_state::reg_58006_w(u16 data)
{
	if (BIT(data, 0))
		m_readback_latch = 1;

	if (BIT(data, 2))
		m_seal_sw = BIT(m_config->read(), 0, 3);
}

void casloopy_state::reg_58008_w(u16 data)
{
}

u16 casloopy_state::default_mouse_state()
{
	u16 buttons = m_mouse->read();
	return 0x8080 | buttons | (buttons >> 8);
}

u16 casloopy_state::reg_5d000_r()
{
	return 4 << 6;
}

void casloopy_state::reg_5d000_w(u16 data)
{

}

u16 casloopy_state::controller_0_r()
{
	if (!BIT(m_config->read(), 5))
		return BIT(m_config->read(), 4) ? m_io0->read() | 1 : 0;
	else
		return default_mouse_state();
}

u16 casloopy_state::controller_1_r()
{
	if (!BIT(m_config->read(), 5))
		return BIT(m_config->read(), 4) ? m_io1->read() : 0;
	else
		return default_mouse_state();
}

u16 casloopy_state::controller_2_r()
{
	if (!BIT(m_config->read(), 5))
		return m_io2->read();
	else
		return default_mouse_state();
}

u16 casloopy_state::reg_5d020_r()
{
	return m_5d020;
}

void casloopy_state::reg_5d020_w(u16 data)
{
	m_5d020 = data;
}

u16 casloopy_state::reg_5d030_r()
{
	return (m_seal_sw << 4) | 7;
}

void casloopy_state::reg_5d030_w(u16 data)
{

}

u16 casloopy_state::reg_5d040_r()
{
	return m_5d040;
}
void casloopy_state::reg_5d040_w(u16 data)
{
	m_5d040 = data;
}

u16 casloopy_state::reg_5d042_r()
{
	return m_5d042;
}
void casloopy_state::reg_5d042_w(u16 data)
{
	m_5d042 = data;
}

u16 casloopy_state::reg_5d044_r()
{
	return m_5d044;
}
void casloopy_state::reg_5d044_w(u16 data)
{
	m_5d044 = data;
}

u16 casloopy_state::reg_5d054_r()
{
	return 0;
}

void casloopy_state::reg_5d054_w(u16 data)
{

}

u16 casloopy_state::reg_5e000_r()
{
	return 0;
}

void casloopy_state::reg_5e000_w(u16 data)
{

}

void casloopy_state::reg_60000_w(u16 data)
{

}
void casloopy_state::reg_a0000_w(u16 data)
{

}

u16 casloopy_state::mouse1_r()
{
	u16 val = 0;

	// Mouse present?
	if (BIT(m_config->read(), 5))
	{
		val = m_mouse->read();

		// Mouse X/Y reading enabled?
		if (BIT(m_system_control, 3))
		{
			int x = m_mouse_x->read();
			val |= (x - m_d_mouse_x) & 0xfff;

			if (!machine().side_effects_disabled())
				m_d_mouse_x = x;
		}
	}
	return val;
}

u16 casloopy_state::mouse2_r()
{
	u16 val = 0;

	// Mouse present and X/Y reading enabled?
	if (BIT(m_config->read(), 5) && BIT(m_system_control, 3))
	{
		int y = m_mouse_y->read();
		val = (y - m_d_mouse_y) & 0xfff;

		if (!machine().side_effects_disabled())
			m_d_mouse_y = y;
	}
	return val;
}

void casloopy_state::fast_clear_w(offs_t offset, u16 data)
{
	const int mask = m_clear_mask & 0xff;
	const int value = m_clear_color & mask;

	for (int x = 0; x < 256; ++x)
	{
		m_bitmap_vram[offset * 256 + x] &= ~mask;
		m_bitmap_vram[offset * 256 + x] |= value;
	}
}

void casloopy_state::sound_control_w(u16 data)
{

}


/*************************************
 *
 *  Memory Map
 *
 *************************************/

void casloopy_state::casloopy_map(address_map &map)
{
	// /CS0: Internal ROM

	// /CS1: Work DRAM
	map(0x01000000, 0x0107ffff).ram().mirror(0x08f80000);

	// /CS2: Cartridge SRAM
	map(0x02000000, 0x023fffff).rw(m_cart, FUNC(casloopy_cart_slot_device::ram_r), FUNC(casloopy_cart_slot_device::ram_w));

	// /CS4: RH-7500 VDP
	map(0x04000000, 0x0401ffff).rw(FUNC(casloopy_state::bitmap_r), FUNC(casloopy_state::bitmap_w)).mirror(0x00020000);
	map(0x04040000, 0x0404ffff).rw(FUNC(casloopy_state::vram_r), FUNC(casloopy_state::vram_w));
	map(0x04050000, 0x040501ff).rw(FUNC(casloopy_state::sprite_ram_r), FUNC(casloopy_state::sprite_ram_w));
	map(0x04050200, 0x040503ff).ram();
	map(0x04051000, 0x040511ff).rw(FUNC(casloopy_state::palette_r), FUNC(casloopy_state::palette_w));
	map(0x04052000, 0x040521ff).r(FUNC(casloopy_state::readback_r));
	map(0x04058000, 0x04058001).rw(FUNC(casloopy_state::system_control_r), FUNC(casloopy_state::system_control_w));
	map(0x04058002, 0x04058003).r(FUNC(casloopy_state::hpos_r));
	map(0x04058004, 0x04058005).r(FUNC(casloopy_state::vpos_r));
	map(0x04058006, 0x04058007).rw(FUNC(casloopy_state::reg_58006_r), FUNC(casloopy_state::reg_58006_w));
	map(0x04058008, 0x04058009).w(FUNC(casloopy_state::reg_58008_w));
	map(0x04059000, 0x0405a001).lrw16(NAME([this]() { return m_bmp_vram_x[0]; }), NAME([this](u16 data) { m_bmp_vram_x[0] = data & 0x1ff; }));
	map(0x04059002, 0x0405a003).lrw16(NAME([this]() { return m_bmp_vram_x[1]; }), NAME([this](u16 data) { m_bmp_vram_x[1] = data & 0x1ff; }));
	map(0x04059004, 0x0405a005).lrw16(NAME([this]() { return m_bmp_vram_x[2]; }), NAME([this](u16 data) { m_bmp_vram_x[2] = data & 0x1ff; }));
	map(0x04059006, 0x0405a007).lrw16(NAME([this]() { return m_bmp_vram_x[3]; }), NAME([this](u16 data) { m_bmp_vram_x[3] = data & 0x1ff; }));
	map(0x04059008, 0x0405a009).lrw16(NAME([this]() { return m_bmp_vram_y[0]; }), NAME([this](u16 data) { m_bmp_vram_y[0] = data & 0x1ff; }));
	map(0x0405900a, 0x0405a00b).lrw16(NAME([this]() { return m_bmp_vram_y[1]; }), NAME([this](u16 data) { m_bmp_vram_y[1] = data & 0x1ff; }));
	map(0x0405900c, 0x0405a00d).lrw16(NAME([this]() { return m_bmp_vram_y[2]; }), NAME([this](u16 data) { m_bmp_vram_y[2] = data & 0x1ff; }));
	map(0x0405900e, 0x0405a00f).lrw16(NAME([this]() { return m_bmp_vram_y[3]; }), NAME([this](u16 data) { m_bmp_vram_y[3] = data & 0x1ff; }));
	map(0x04059010, 0x0405a011).lrw16(NAME([this]() { return m_bmp_screen_x[0]; }), NAME([this](u16 data) { m_bmp_screen_x[0] = data & 0x1ff; }));
	map(0x04059012, 0x0405a013).lrw16(NAME([this]() { return m_bmp_screen_x[1]; }), NAME([this](u16 data) { m_bmp_screen_x[1] = data & 0x1ff; }));
	map(0x04059014, 0x0405a015).lrw16(NAME([this]() { return m_bmp_screen_x[2]; }), NAME([this](u16 data) { m_bmp_screen_x[2] = data & 0x1ff; }));
	map(0x04059016, 0x0405a017).lrw16(NAME([this]() { return m_bmp_screen_x[3]; }), NAME([this](u16 data) { m_bmp_screen_x[3] = data & 0x1ff; }));
	map(0x04059018, 0x0405a019).lrw16(NAME([this]() { return m_bmp_screen_y[0]; }), NAME([this](u16 data) { m_bmp_screen_y[0] = data & 0x1ff; }));
	map(0x0405901a, 0x0405a01b).lrw16(NAME([this]() { return m_bmp_screen_y[1]; }), NAME([this](u16 data) { m_bmp_screen_y[1] = data & 0x1ff; }));
	map(0x0405901c, 0x0405a01d).lrw16(NAME([this]() { return m_bmp_screen_y[2]; }), NAME([this](u16 data) { m_bmp_screen_y[2] = data & 0x1ff; }));
	map(0x0405901e, 0x0405a01f).lrw16(NAME([this]() { return m_bmp_screen_y[3]; }), NAME([this](u16 data) { m_bmp_screen_y[3] = data & 0x1ff; }));
	map(0x04059020, 0x0405a021).lrw16(NAME([this]() { return m_bmp_size_x[0]; }), NAME([this](u16 data) { m_bmp_size_x[0] = data; }));
	map(0x04059022, 0x0405a023).lrw16(NAME([this]() { return m_bmp_size_x[1]; }), NAME([this](u16 data) { m_bmp_size_x[1] = data; }));
	map(0x04059024, 0x0405a025).lrw16(NAME([this]() { return m_bmp_size_x[2]; }), NAME([this](u16 data) { m_bmp_size_x[2] = data; }));
	map(0x04059026, 0x0405a027).lrw16(NAME([this]() { return m_bmp_size_x[3]; }), NAME([this](u16 data) { m_bmp_size_x[3] = data; }));
	map(0x04059028, 0x0405a029).lrw16(NAME([this]() { return m_bmp_size_y[0]; }), NAME([this](u16 data) { m_bmp_size_y[0] = data & 0xff; }));
	map(0x0405902a, 0x0405a02b).lrw16(NAME([this]() { return m_bmp_size_y[1]; }), NAME([this](u16 data) { m_bmp_size_y[1] = data & 0xff; }));
	map(0x0405902c, 0x0405a02d).lrw16(NAME([this]() { return m_bmp_size_y[2]; }), NAME([this](u16 data) { m_bmp_size_y[2] = data & 0xff; }));
	map(0x0405902e, 0x0405a02f).lrw16(NAME([this]() { return m_bmp_size_y[3]; }), NAME([this](u16 data) { m_bmp_size_y[3] = data & 0xff; }));
	map(0x04059030, 0x04059031).lrw16(NAME([this]() { return m_bmp_control; }), NAME([this](u16 data) { m_bmp_control = data & 7; }));
	map(0x04059040, 0x04059041).lrw16(NAME([this]() { return m_bmp_color; }), NAME([this](u16 data) { m_bmp_color = data; }));
	map(0x04059050, 0x0405a051).lrw16(NAME([this]() { return m_bmp_span[0]; }), NAME([this](u16 data) { m_bmp_span[0] = data & 0x1ff; }));
	map(0x04059052, 0x0405a053).lrw16(NAME([this]() { return m_bmp_span[1]; }), NAME([this](u16 data) { m_bmp_span[1] = data & 0x1ff; }));
	map(0x04059054, 0x0405a055).lrw16(NAME([this]() { return m_bmp_span[2]; }), NAME([this](u16 data) { m_bmp_span[2] = data & 0x1ff; }));
	map(0x04059056, 0x0405a057).lrw16(NAME([this]() { return m_bmp_span[3]; }), NAME([this](u16 data) { m_bmp_span[3] = data & 0x1ff; }));
	map(0x0405a000, 0x0405a001).lrw16(NAME([this]() { return m_tilemap_control; }), NAME([this](u16 data) { m_tilemap_control = data & 0xff; }));
	map(0x0405a002, 0x0405a003).lrw16(NAME([this]() { return m_tilemap_scroll_x[0]; }), NAME([this](u16 data) { m_tilemap_scroll_x[0] = data & 0xfff; }));
	map(0x0405a004, 0x0405a005).lrw16(NAME([this]() { return m_tilemap_scroll_y[0]; }), NAME([this](u16 data) { m_tilemap_scroll_y[0] = data & 0xfff; }));
	map(0x0405a006, 0x0405a007).lrw16(NAME([this]() { return m_tilemap_scroll_x[1]; }), NAME([this](u16 data) { m_tilemap_scroll_x[1] = data & 0xfff; }));
	map(0x0405a008, 0x0405a009).lrw16(NAME([this]() { return m_tilemap_scroll_y[1]; }), NAME([this](u16 data) { m_tilemap_scroll_y[1] = data & 0xfff; }));
	map(0x0405a00a, 0x0405a00b).lrw16(NAME([this]() { return m_tilemap_color[0]; }), NAME([this](u16 data) { m_tilemap_color[0] = data; }));
	map(0x0405a00c, 0x0405a00d).lrw16(NAME([this]() { return m_tilemap_color[1]; }), NAME([this](u16 data) { m_tilemap_color[1] = data; }));
	map(0x0405a010, 0x0405a011).lrw16(NAME([this]() { return m_sprite_control; }), NAME([this](u16 data) { m_sprite_control = data & 0x7fff; }));
	map(0x0405a012, 0x0405a013).lrw16(NAME([this]() { return m_sprite_color[0]; }), NAME([this](u16 data) { m_sprite_color[0] = data; }));
	map(0x0405a014, 0x0405a015).lrw16(NAME([this]() { return m_sprite_color[1]; }), NAME([this](u16 data) { m_sprite_color[1] = data; }));
	map(0x0405a020, 0x0405a021).lrw16(NAME([this]() { return m_tilemap_page; }), NAME([this](u16 data) { m_tilemap_page = data & 0x7f; }));
	map(0x0405b000, 0x0405b001).lrw16(NAME([this]() { return m_layer_control_0; }), NAME([this](u16 data) { m_layer_control_0 = data & 7; }));
	map(0x0405b002, 0x0405b003).lrw16(NAME([this]() { return m_layer_enable; }), NAME([this](u16 data) { m_layer_enable = data; }));
	map(0x0405b004, 0x0405b005).lrw16(NAME([this]() { return m_layer_control_2; }), NAME([this](u16 data) { m_layer_control_2 = data & 0xff; }));
	map(0x0405b006, 0x0405b007).lrw16(NAME([this]() { return m_color[0]; }), NAME([this](u16 data) { m_color[0] = data & 0x7fff; }));
	map(0x0405b008, 0x0405b009).lrw16(NAME([this]() { return m_color[1]; }), NAME([this](u16 data) { m_color[1] = data & 0x7fff; }));
	map(0x0405b00a, 0x0405b00b).lrw16(NAME([this]() { return m_readback_control; }), NAME([this](u16 data) { m_readback_control = data & 0xfff; }));
	map(0x0405c000, 0x0405c001).lrw16(NAME([this]() { return m_int_control; }), NAME([this](u16 data) { m_int_control = data & 0x1ff; }));
	map(0x0405c002, 0x0405c003).lrw16(NAME([this]() { return m_screen_width; }), NAME([this](u16 data) { m_screen_width = data & 0x1ff; }));
	map(0x0405c004, 0x0405c005).lrw16(NAME([this]() { return m_screen_height; }), NAME([this](u16 data) { m_screen_height = data & 0x1ff; }));
	map(0x0405d000, 0x0405d001).rw(FUNC(casloopy_state::reg_5d000_r), FUNC(casloopy_state::reg_5d000_w));
	map(0x0405d010, 0x0405d011).r(FUNC(casloopy_state::controller_0_r));
	map(0x0405d012, 0x0405d013).r(FUNC(casloopy_state::controller_1_r));
	map(0x0405d014, 0x0405d015).r(FUNC(casloopy_state::controller_2_r));
	map(0x0405d020, 0x0405d021).rw(FUNC(casloopy_state::reg_5d020_r), FUNC(casloopy_state::reg_5d020_w));
	map(0x0405d030, 0x0405d031).rw(FUNC(casloopy_state::reg_5d030_r), FUNC(casloopy_state::reg_5d030_w));
	map(0x0405d040, 0x0405d041).rw(FUNC(casloopy_state::reg_5d040_r), FUNC(casloopy_state::reg_5d040_w));
	map(0x0405d042, 0x0405d043).rw(FUNC(casloopy_state::reg_5d042_r), FUNC(casloopy_state::reg_5d042_w));
	map(0x0405d044, 0x0405d045).rw(FUNC(casloopy_state::reg_5d044_r), FUNC(casloopy_state::reg_5d044_w));
	map(0x0405d050, 0x0405d051).r(FUNC(casloopy_state::mouse1_r));
	map(0x0405d052, 0x0405d053).r(FUNC(casloopy_state::mouse2_r));
	map(0x0405d054, 0x0405d055).rw(FUNC(casloopy_state::reg_5d054_r), FUNC(casloopy_state::reg_5d054_w));
	map(0x0405e000, 0x0405e001).rw(FUNC(casloopy_state::reg_5e000_r), FUNC(casloopy_state::reg_5e000_w));
	map(0x0405e002, 0x0405e003).lrw16(NAME([this]() { return m_clear_mask; }), NAME([this](u16 data) { m_clear_mask = data & 0x1ff; }));
	map(0x0405e004, 0x0405e005).lrw16(NAME([this]() { return m_clear_color; }), NAME([this](u16 data) { m_clear_color = data & 0xff; }));
	map(0x0405f000, 0x0405f3ff).w(FUNC(casloopy_state::fast_clear_w));
	map(0x04060000, 0x04060001).w(FUNC(casloopy_state::reg_60000_w));
	map(0x04080000, 0x04080001).w(FUNC(casloopy_state::sound_control_w));
	map(0x040a0000, 0x040a0001).w(FUNC(casloopy_state::reg_a0000_w));

	// /CS6: Cartridge ROM
	map(0x06000000, 0x063fffff).r(m_cart, FUNC(casloopy_cart_slot_device::rom_r)).mirror(0x08000000); // FIXME: This mirror should not be required
}


/*************************************
 *
 *  Input definitions
 *
 *************************************/

static INPUT_PORTS_START( casloopy )
	PORT_START("CONTROLLER_0")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_CUSTOM ) // Joypad presence
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_START )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("L")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("R")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_CUSTOM ) // Mouse presence
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("A")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("D")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("C")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("B")

	PORT_START("CONTROLLER_1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_8WAY
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_8WAY
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_8WAY
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_8WAY

	PORT_START("CONTROLLER_2")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("CONFIG")
	PORT_CONFNAME( 0x07, 0x00, "Seal Cartridge" )
	PORT_CONFSETTING(    0x00, DEF_STR( None ) )
	PORT_CONFSETTING(    0x01, "XS-11/XS-14" )
	PORT_CONFSETTING(    0x03, "XS-31" )
	PORT_CONFNAME( 0x70, 0x10, "Controller" )
	PORT_CONFSETTING(    0x00, DEF_STR( None ) )
	PORT_CONFSETTING(    0x10, "Joypad" )
	PORT_CONFSETTING(    0x20, "Mouse" )

	PORT_START("MOUSE")
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("Left Mouse Button") PORT_CODE(MOUSECODE_BUTTON1)
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("Right Mouse Button") PORT_CODE(MOUSECODE_BUTTON2)

	PORT_START("MOUSE_X")
	PORT_BIT( 0xfff, 0x00, IPT_MOUSE_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(0)

	PORT_START("MOUSE_Y")
	PORT_BIT( 0xfff, 0x00, IPT_MOUSE_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_REVERSE
INPUT_PORTS_END


/*************************************
 *
 *  Machine driver
 *
 *************************************/

void casloopy_state::machine_start()
{
	save_item(NAME(m_d_mouse_x));
	save_item(NAME(m_d_mouse_y));
	save_item(NAME(m_bmp_latch));
	save_item(NAME(m_readback_buffer));
	save_item(NAME(m_system_control));
	save_item(NAME(m_bmp_vram_x));
	save_item(NAME(m_bmp_vram_y));
	save_item(NAME(m_bmp_screen_x));
	save_item(NAME(m_bmp_screen_y));
	save_item(NAME(m_bmp_size_x));
	save_item(NAME(m_bmp_size_y));
	save_item(NAME(m_bmp_color));
	save_item(NAME(m_bmp_control));
	save_item(NAME(m_bmp_span));
	save_item(NAME(m_screen_width));
	save_item(NAME(m_screen_height));
	save_item(NAME(m_int_control));
	save_item(NAME(m_tilemap_color));
	save_item(NAME(m_tilemap_control));
	save_item(NAME(m_tilemap_page));
	save_item(NAME(m_tilemap_scroll_x));
	save_item(NAME(m_tilemap_scroll_y));
	save_item(NAME(m_layer_control_0));
	save_item(NAME(m_layer_enable));
	save_item(NAME(m_layer_control_2));
	save_item(NAME(m_sprite_control));
	save_item(NAME(m_sprite_color));
	save_item(NAME(m_color));
	save_item(NAME(m_readback_control));
	save_item(NAME(m_clear_mask));
	save_item(NAME(m_clear_color));
	save_item(NAME(m_5d020));
	save_item(NAME(m_5d030));
	save_item(NAME(m_5d040));
	save_item(NAME(m_5d044));
	save_item(NAME(m_seal_sw));
	save_item(NAME(m_readback_latch));
}

void casloopy_state::machine_reset()
{
	m_d_mouse_x = 0;
	m_d_mouse_y = 0;
	std::fill_n(m_bmp_latch, std::size(m_bmp_latch), 0);
	std::fill_n(m_readback_buffer, std::size(m_readback_buffer), 0);

	m_system_control = 0;
	std::fill_n(m_bmp_vram_x, std::size(m_bmp_vram_x), 0);
	std::fill_n(m_bmp_vram_y, std::size(m_bmp_vram_y), 0);
	std::fill_n(m_bmp_screen_x, std::size(m_bmp_screen_x), 0);
	std::fill_n(m_bmp_screen_y, std::size(m_bmp_screen_y), 0);
	std::fill_n(m_bmp_size_x, std::size(m_bmp_size_x), 0);
	std::fill_n(m_bmp_size_y, std::size(m_bmp_size_y), 0);
	std::fill_n(m_bmp_span, std::size(m_bmp_span), 0x00ff);
	m_bmp_color = 0;
	m_bmp_control = 0;
	m_screen_width = 0;
	m_screen_height = 0;
	m_int_control = 0;
	std::fill_n(m_tilemap_color, std::size(m_tilemap_color), 0);
	m_tilemap_control = 0;
	m_tilemap_page = 0;
	std::fill_n(m_tilemap_scroll_x, std::size(m_tilemap_scroll_x), 0);
	std::fill_n(m_tilemap_scroll_y, std::size(m_tilemap_scroll_y), 0);
	m_layer_control_0 = 0;
	m_layer_enable = 0;
	m_layer_control_2 = 0;
	m_sprite_control = 0;
	std::fill_n(m_sprite_color, std::size(m_sprite_color), 0);
	std::fill_n(m_color, std::size(m_color), 0);
	m_readback_control = 0;
	m_clear_mask = 0;
	m_clear_color = 0;
	m_5d020 = 0;
	m_5d030 = 0;
	m_5d040 = 0;
	m_5d044 = 0;
	m_seal_sw = 0;
	m_readback_latch = 0;

	if (m_cart->exists())
	{
		// Cartridge presence bit
		m_maincpu->write_padr_bit<8>(1);
	}
}

static void casloopy_cart(device_slot_interface &device)
{
	device.option_add_internal("std",      CASLOOPY_ROM_STD);
	device.option_add_internal("adpcm",    CASLOOPY_ROM_ADPCM);
}

void casloopy_state::casloopy(machine_config &config)
{
	SH7021(config, m_maincpu, SH1_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &casloopy_state::casloopy_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(VIDEO_CLOCK / 4, H_TOTAL, 0, H_ACTIVE, V_TOTAL, 0, V_ACTIVE_0);
	m_screen->set_screen_update(FUNC(casloopy_state::screen_update));

	PALETTE(config, m_palette).set_entries(256);

	CASLOOPY_CART_SLOT(config, m_cart, SH1_CLOCK, casloopy_cart, nullptr).set_must_be_loaded(true);

	SOFTWARE_LIST(config, "cart_list").set_original("casloopy");
}

} // anonymous namespace


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

  Driver

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

ROM_START( casloopy )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD( "hd6437021.lsi302", 0x0000, 0x8000, CRC(8c57ff9f) SHA1(17542ef275ea8ebeaaddd00d304232c3c355ea25) )

	ROM_REGION( 0x80000, "wave", 0)
	ROM_LOAD( "hn62434fa.lsi352", 0x0000, 0x80000, CRC(8f51fa17) SHA1(99f50be06b083fdb07e08f30b0b26d9037afc869) )
ROM_END

CONS( 1995, casloopy, 0, 0, casloopy, casloopy, casloopy_state, empty_init, "Casio", "Loopy", MACHINE_NO_SOUND | MACHINE_NODEVICE_PRINTER )



cassvisn.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Vas Crabb

/*

for architecture details see
https://www.oguchi-rd.com/LSI%20products.php
the linked 'design note' contains a large amount of useful information
https://www.oguchi-rd.com/777/777%20Design%20Note.pdf

*/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/upd777/upd777.h"

#include "softlist_dev.h"


namespace {

class cassvisn_state : public driver_device
{
public:
	cassvisn_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_cart(*this, "cartslot"),
		m_in(*this, "IN%u", 0)
	{ }

	void cassvisn(machine_config &config);

protected:
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
private:
	u8 input_r(offs_t offset);

	required_device<upd777_cpu_device> m_maincpu;
	required_device<generic_slot_device> m_cart;
	required_ioport_array<2> m_in;
};

u8 cassvisn_state::input_r(offs_t offset)
{
	return m_in[offset & 1]->read();
}

static INPUT_PORTS_START( cassvisn )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START1 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START2 )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON4 )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON3 )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON1 ) // Jump on elvpanic

	PORT_START("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON10 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON9 )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON8 )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON7 )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON6 )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON5 )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON2 ) // Jump on monstrmn
INPUT_PORTS_END

DEVICE_IMAGE_LOAD_MEMBER(cassvisn_state::cart_load)
{
	if (!image.loaded_through_softlist())
		return std::make_pair(image_error::UNSUPPORTED, "Cartridges must be loaded from the software list");

	auto const prgsize = m_cart->common_get_size("prg");
	if (prgsize != 0xf00)
		return std::make_pair(image_error::BADSOFTWARE, "prg region must be 0xf00 bytes in size");

	m_cart->rom_alloc(prgsize, GENERIC_ROM16_WIDTH, ENDIANNESS_BIG);
	m_cart->common_load_rom(m_cart->get_rom_base(), prgsize, "prg");
	uint16_t *prgbase = m_maincpu->get_prgregion();
	memcpy(prgbase, m_cart->get_rom_base(), prgsize);

	auto const patsize = m_cart->common_get_size("pat");
	if (patsize != 0x4d0)
		return std::make_pair(image_error::BADSOFTWARE, "pat region patsize must be 0x4d0 bytes in size");

	m_cart->common_load_rom(m_maincpu->get_patregion(), patsize, "pat");

	return std::make_pair(std::error_condition(), std::string());
}

void cassvisn_state::cassvisn(machine_config &config)
{
	UPD777(config, m_maincpu, 2'000'000); // frequency? UPD774 / UPD778 in some carts?
	m_maincpu->in_cb().set(FUNC(cassvisn_state::input_r));

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "cassvisn_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(cassvisn_state::cart_load));
	m_cart->set_must_be_loaded(true);

	SOFTWARE_LIST(config, "cart_list").set_original("cassvisn_cart");
}

ROM_START( cassvisn )
ROM_END

} // anonymous namespace

CONS( 1981, cassvisn, 0, 0, cassvisn,  cassvisn, cassvisn_state, empty_init, "Epoch", "Cassette Vision", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



cat.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Jonathan Gevaryahu
/***************************************************************************

    Canon Cat, Model V777
    Copyright (C) 2009-2013 Miodrag Milanovic and Jonathan Gevaryahu AKA Lord Nightmare
    With information and help from John "Sandy" Bumgarner, Dwight Elvey,
    Charles Springer, Terry Holmes, Jonathan Sand, Aza Raskin and others.


    This driver is dedicated in memory of Jef Raskin and Dave Boulton

    12/06/2009 Skeleton driver.
    15/06/2009 Working driver

Pictures: http://www.regnirps.com/Apple6502stuff/apple_iie_cat.htm

Canon cat blue "Use Front Labels":
` (really degree, +-, paragraph-mark and section-mark): LEFT MARGIN
-_: INDENT
+=: RIGHT MARGIN
UNDO: SPELL CHECK LEAP (this is actually a red LEAP label)
PERM SPACE/TAB: SET/CLEAR TAB
Q: UNDER LINE
W: BOLD
E: CAPS
T: Paragraph STYLE
U: LINE SPACE
O: ADD SPELLING
[ (really 1/4, 1/2, double-underline and |): SETUP
Backspace (really ERASE): ANSWER
LOCK: DOCUMENT LOCK
A: COPY
D: SEND CONTROL
G: CALC
J: PRINT
L: DISK
Colon: FORMAT/WIPE DISK (only when wheel! mode is enabled, see below)
"': PHONE
Enter (really RETURN): SEND
X: LOCAL LEAP
V: LEARN
N: EXPLAIN
,: SORT
?/: KBI/II
Pgup (really DOCUMENT/PAGE): TITLES
Leap Left: LEAP AGAIN (left)
Leap Right: LEAP AGAIN (right)


How to enable the FORTH interpreter (http://canoncat.org/canoncat/enableforth.html)

The definitive instructions of going Forth on a Cat, tested on a living Cat.
The Canon Cat is programmed with FORTH, a remarkably different programming language in itself.
On the Canon Cat, you can access the FORTH interpreter with a special sequence of key presses.
Here are exact working instructions thanks to Sandy Bumgarner:
- Type Enable Forth Language exactly like that, capitals and all.
- Highlight from the En to the ge, exactly [i.e. Leap back to Enable by keeping the left Leap
  key down while typing E n a, release the leap key, then click both leap keys simultaneously],
  and press USE FRONT with ERASE (the ANSWER command).
- The Cat should beep and flash the ruler.
- Then press USE FRONT and the SHIFT keys together and tap the space bar. Note that the cursor
  stops blinking. Now a Pressing the RETURN key gets the Forth OK and you are 'in' as they say.

In MESS, to activate it as above:
* when the Cat boots, type (without quotes) "Enable Forth Language"
* hold left-alt(leap left) and type E n a, release left-alt (the left cursor is now at the first character)
* simultaneously press both alt keys for a moment and release both (the whole "Enable Forth Language" line will be selected)
* press control(use front) and press backspace(ERASE) (The cat will beep here)
* press control(use front), shift, and space (the cursor should stop blinking)
* press enter and the forth "ok" prompt should appear. you can type 'page' and enter to clear the screen
Optional further steps:
* type without quotes "-1 wheel! savesetup re" at the forth prompt to permanently
  enable shift + use front + space to dump to forth mode easily
* change the keyboard setting in the setup menu (use front + [ ) to ASCII so you can type < and >
* after doing the -1 wheel! thing, you can compile a selected forth program in the editor
  by selecting it and hitting ANSWER (use front + ERASE)

If ever in forth mode you can return to the editor with the forth word (without quotes) "re"

Canon cat gate array ASIC markings:
GA1 (prototype): D65013CW276 [same as final]
GA1 (final): NH4-5001 276 [schematic: upD65013CW-276]
GA2 (prototype): D65013CW208 [DIFFERENT FROM FINAL! larger asic used here, shrank for final?]
GA2 (final): NH4-5002 191 [schematic: upD65012CW-191]
GA3 (prototype): D65013CW141 [same as final? typo 65013 vs 65012?]
GA3 (final): NH4-5003 141 [schematic: upD65012CW-141]

Canon cat credits easter egg:
* hold either leap key, then simultaneously hold shift, then type Q W E R A S D F Z X C V and release all the keys
* hit EXPLAIN (use front + N) and the credits screen will be displayed

Canon Cat credits details: (WIP)
Jef Raskin
John "Sandy" Bumgarner
Charles Springer
Jonathan Sand
Terry Holmes - wrote tForth, the language in which the cat is programmed
Scott Kim - responsible for fonts on swyft and cat
Ralph Voorhees - Model construction and mockups (swyft 'flat cat')

Cat HLSL stuff:
*scanlines:
the cat has somewhat visible and fairly close scanlines with very little fuzziness
try hlsl options:
hlsl_prescale_x           4
hlsl_prescale_y           4
scanline_alpha            0.3
scanline_size             1.0
scanline_height           0.7
scanline_bright_scale     1.0
scanline_bright_offset    0.6
*phosphor persistence of the original cat CRT is VERY LONG and fades to a greenish-yellow color, though the main color itself is white
try hlsl option:
phosphor_life             0.93,0.95,0.87
which is fairly close but may actually be too SHORT compared to the real thing.


Canon Cat versions:
There is really only one version of the cat which saw wide release, the US version.
* It is possible a very small number of UK/European units were released as a test.
  If so, these will have slightly different keyboard key caps and different
  system and spellcheck ROMs.

As for prototypes/dev cat machines, a few minor variants exist:
* Prototype cat motherboards used 16k*4bit drams instead of 64k*4bit as the
  final system did and hence max out at 128k of dram instead of 512k.
  One of the gate arrays is also different, and the motherboard is arranged
  differently. The IC9 "buserr" PAL is not used, even on the prototype.
  The final system included 256k of dram and can be upgraded to 512k.
* At least some developer units were modified to have an external BNC
  connector, ostensibly to display the internal screen's video externally.
  http://www.digibarn.com/collections/systems/canon-cat/Image55.jpg


Canon Cat:
<insert guru-diagram here once drawn>
Crystals:
X1: 19.968Mhz, used by GA2 (plus a PLL to multiply by 2?), and divide by 4 for
    cpuclk, divide by 8 for 2.5mhz and divide by 5.5 for 3.63mhz (is this
    supposed to be divide by 6? there may not be a pll if it is...)
X2: 3.579545Mhz, used by the DTMF generator chip AMI S2579 at IC40
X3: 2.4576Mhz, used by the modem chip AMI S35213 at IC37


ToDo:
* Canon Cat
- Find the mirrors for the write-only video control register and figure out
  what the writes actually do; hook these up properly to screen timing etc
- Floppy drive (3.5", Single Sided Double Density MFM, ~400kb)
  * Cat has very low level control of data being read or written, much like
    the Amiga or AppleII does
  * first sector is id ZERO which is unusual since most MFM formats are base-1
    for sector numbering
  * sectors are 512 bytes, standard MFM with usual address and data marks and
    ccitt crc16
  * track 0 contains sector zero repeated identically 10 times; the cat SEEMS
    to use this as a disk/document ID
  * tracks 1-79 each contain ten sectors, id 0x0 thru 0x9
    ('normal' mfm parlance: sectors -1, 0, 1, ... 7, 8)
  * this means the whole disk effectively contains 512*10*80 = 409600 bytes of
    data, though track 0 is just a disk "unique" identifier for the cat
    meaning 404480 usable bytes
  * (Once the floppy is working I'd declare the system working)
- Centronics port finishing touches: verify where the paper out, slct/err, and
  IPP pins map in memory. The firmware doesn't actually use them, but they must
  map somewhere as they connect to the ASIC.
- RS232C port and Modem "port" connected to the DUART's two ports
  These are currently optionally debug-logged but don't connect anywhere
- DTMF generator chip (connected to DUART 'user output' pins OP4,5,6,7)
- WIP: Watchdog timer/powerfail at 0x85xxxx (watchdog NMI needs to actually
  fire if wdt goes above a certain number, possibly 3, 7 or F?)
- Canon Cat released versions known: 1.74 US (dumped), 2.40 US (dumped both
  original compile, and Dwight's recompile from the released source code),
  2.42 (NEED DUMP)
  It is possible a few prototype UK 1.74 or 2.40 units were produced; the code
  ROMs of these will differ (they contain different spellcheck "core" code) as
  well as the spellcheck ROMs, the keyboard id and the keycaps.
- Known Spellcheck ROMs: NH7-0684 (US, dumped); NH7-0724 (UK, NEED DUMP);
  NH7-0813/0814 (Quebec/France, NEED DUMP); NH7-1019/1020/1021 (Germany, NEED DUMP)
  It is possible the non-US ROMs were never officially released.
  Wordlist sources: American Heritage (US and UK), Librarie Larousse (FR),
  Langenscheidt (DE)
- (would-be-really-nice-but-totally-unnecessary feature): due to open bus, the
  svrom1 and svrom2 checksums in diagnostics read as 01A80000 and 01020000
  respectively on a real machine (and hence appear inverted/'fail'-state).
  This requires sub-cycle accurate 68k open bus emulation to pull off, as well
  as emulating the fact that UDS/LDS are ?not connected? (unclear because this
  happens inside an asic) for the SVROMS (or the svram or the code ROMs, for
  that matter!)
- Hook Battery Low input to a dipswitch.
- Hook pfail to a dipswitch.
- Hook the floppy control register readback up properly, things seem to get
  confused.

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

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "machine/clock.h"
#include "machine/mc68681.h"
#include "machine/nvram.h"
#include "sound/spkrdev.h"
#include "bus/centronics/ctronics.h"
#include "screen.h"
#include "speaker.h"


namespace {

// Defines

#undef DEBUG_GA2OPR_W
#undef DEBUG_VIDEO_CONTROL_W

#undef DEBUG_FLOPPY_CONTROL_W
#undef DEBUG_FLOPPY_CONTROL_R
#undef DEBUG_FLOPPY_DATA_W
#undef DEBUG_FLOPPY_DATA_R
#undef DEBUG_FLOPPY_STATUS_R

#undef DEBUG_PRINTER_DATA_W
#undef DEBUG_PRINTER_CONTROL_W

#undef DEBUG_MODEM_R
#undef DEBUG_MODEM_W

#undef DEBUG_DUART_OUTPUT_LINES
// data sent to rs232 port
#undef DEBUG_DUART_TXA
// data sent to modem chip
#undef DEBUG_DUART_TXB
#undef DEBUG_DUART_IRQ_HANDLER
#undef DEBUG_PRN_FF

#undef DEBUG_TEST_W

#define DEBUG_SWYFT_VIA0 1
#define DEBUG_SWYFT_VIA1 1


class cat_state : public driver_device
{
public:
	cat_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		//m_nvram(*this, "nvram"), // merge with svram?
		m_duart(*this, "duartn68681"),
		m_ctx(*this, "ctx"),
		m_ctx_data_out(*this, "ctx_data_out"),
		m_speaker(*this, "speaker"),
		m_svram(*this, "svram"), // nvram
		m_p_cat_videoram(*this, "p_cat_vram"),
		m_y0(*this, "Y0"),
		m_y1(*this, "Y1"),
		m_y2(*this, "Y2"),
		m_y3(*this, "Y3"),
		m_y4(*this, "Y4"),
		m_y5(*this, "Y5"),
		m_y6(*this, "Y6"),
		m_y7(*this, "Y7"),
		m_dipsw(*this, "DIPSW1")
	{ }

	void cat(machine_config &config);

	void init_cat();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	//optional_device<nvram_device> m_nvram;
	required_device<mc68681_device> m_duart;
	required_device<centronics_device> m_ctx;
	required_device<output_latch_device> m_ctx_data_out;
	required_device<speaker_sound_device> m_speaker;
	required_shared_ptr<uint16_t> m_svram;
	required_shared_ptr<uint16_t> m_p_cat_videoram;
	required_ioport m_y0;
	required_ioport m_y1;
	required_ioport m_y2;
	required_ioport m_y3;
	required_ioport m_y4;
	required_ioport m_y5;
	required_ioport m_y6;
	required_ioport m_y7;
	required_ioport m_dipsw;
	emu_timer *m_keyboard_timer;
	emu_timer *m_6ms_timer;

	uint32_t screen_update_cat(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void cat_duart_irq_handler(int state);
	void cat_duart_txa(int state);
	void cat_duart_txb(int state);
	void cat_duart_output(uint8_t data);
	void prn_ack_ff(int state);

	uint16_t cat_floppy_control_r(offs_t offset);
	void cat_floppy_control_w(offs_t offset, uint16_t data);
	void cat_printer_data_w(offs_t offset, uint16_t data);
	uint16_t cat_floppy_data_r(offs_t offset);
	void cat_floppy_data_w(offs_t offset, uint16_t data);
	uint16_t cat_keyboard_r(offs_t offset);
	void cat_keyboard_w(uint16_t data);
	void cat_video_control_w(offs_t offset, uint16_t data);
	uint16_t cat_floppy_status_r(offs_t offset);
	uint16_t cat_battery_r();
	void cat_printer_control_w(offs_t offset, uint16_t data);
	uint16_t cat_modem_r(offs_t offset);
	void cat_modem_w(offs_t offset, uint16_t data);
	uint16_t cat_6ms_counter_r();
	void cat_opr_w(offs_t offset, uint16_t data);
	uint16_t cat_wdt_r();
	void cat_tcb_w(offs_t offset, uint16_t data);
	uint16_t cat_2e80_r();
	uint16_t cat_0080_r();
	uint16_t cat_0000_r();


	/* gate array 2 has a 16-bit counter inside which counts at 10mhz and
	   rolls over at FFFF->0000; on roll-over (or likely at FFFF terminal count)
	   it triggers the KTOBF output. It does this every 6.5535ms, which causes
	   a 74LS74 d-latch at IC100 to invert the state of the DUART IP2 line;
	   this causes the DUART to fire an interrupt, which makes the 68000 read
	   the keyboard.
	   The watchdog counter and the 6ms counter are both incremented
	   every time the KTOBF pulses.
	 */
	uint16_t m_6ms_counter;
	uint8_t m_wdt_counter;
	uint8_t m_duart_ktobf_ff;
	/* the /ACK line from the centronics printer port goes through a similar
	   flipflop to the ktobf line as well, so duart IP4 inverts on /ACK rising edge
	 */
	uint8_t m_duart_prn_ack_prev_state;
	uint8_t m_duart_prn_ack_ff;
	/* Gate array 2 is in charge of serializing the video for display to the screen;
	   Gate array 1 is in charge of vblank/hblank timing, and in charge of refreshing
	   dram and indicating to GA2, using the /LDPS signal, what times the address it is
	   writing to ram it is intending to be used by GA2 to read 16 bits of data to be
	   shifted out to the screen. (it is obviously not active during hblank and vblank)
	   GA2 then takes: ((output_bit XNOR video_invert) AND video enable), and serially
	   bangs the result to the analog display circuitry.
	 */
	uint8_t m_video_enable;
	uint8_t m_video_invert;
	uint16_t m_pr_cont;
	uint8_t m_keyboard_line;
	uint8_t m_floppy_control;

	//TIMER_CALLBACK_MEMBER(keyboard_callback);
	TIMER_CALLBACK_MEMBER(counter_6ms_callback);

	void cat_mem(address_map &map) ATTR_COLD;
	void cpu_space_map(address_map &map) ATTR_COLD;
};

// TODO: this init doesn't actually work yet! please fix me!
/*
void cat_state::init_cat()
{
    uint8_t *svrom = memregion("svrom")->base();
    // fill svrom with the correct 2e80 pattern except where svrom1 sits
    // first half
    for (int i = 0; i < 0x20000; i+=2)
        svrom[i] = 0x2E;
    // second half
    for (int i = 0x20000; i < 0x40000; i+=2)
    {
        svrom[i] = 0x2E;
        svrom[i+1] = 0x80;
    }
}*/

/* 0x600000-0x65ffff Write: Video Generator (AKA NH4-5001 AKA Gate Array #1 @ IC30)
 writing to the video generator is done by putting the register number in the high 3 bits
 and the data to write in the lower 12 (14?) bits.
 The actual data-bus data written here is completely ignored,
 in fact it isn't even connected to the gate array; The firmware writes 0x0000.
 hence:
 0 = must be 0
 s = register select
 d = data to write
 ? = unknown
 x = ignored
 ? 1 1 ?  ? s s s  s ? ? ?  ? ? ? d  d d d d  d d d .
 600xxx - VSE (End of Frame)
 608xxx - VST (End of VSync)
 610xxx - VSS (VSync Start)
 618xxx - VDE (Active Lines) = value written * 4
 620xxx - unknown
 628xxx - unknown
 630xxx - unknown
 638xxx - unknown
 640xxx - HSE (End H Line)
 648xxx - HST (End HSync)
 650xxx - HSS (HSync Start)
 658xxx - VOC (Video Control)
 */
void cat_state::cat_video_control_w(offs_t offset, uint16_t data)
{
	/*
	 * 006500AE ,          ( HSS HSync Start    89 )
	 * 006480C2 ,          ( HST End HSync   96 )
	 * 006400CE ,          ( HSE End H Line    104 )
	 * 006180B0 ,          ( VDE Active Lines    344 )
	 * 006100D4 ,          ( VSS VSync Start   362 )
	 * 006080F4 ,          ( VST End of VSync    378 )
	 * 00600120 ,          ( VSE End of Frame    400 )
	 * 006581C0 ,          ( VOC Video Control Normal Syncs )
	 * based on diagrams from https://archive.org/details/DTCJefRaskinDoc062
	 * we can determine what at least some of these values do:
	 * HORIZONTAL:
	 *   0 is the first pixel output to the screen at the end of the frontporch
	 *   stuff is shifted to the screen here, and then zeroed for the backporch; manual claims backporch is 2 cycles, it is actually 7
	 *   HSS (89) is the horizontal count at which the HSYNC pin goes high
	 *   sync is active here for 7 cycles; manual claims 10 but is wrong
	 *   HST (96) is the horizontal count at which the HSYNC pin goes low
	 *   sync is inactive here for 7 cycles, manual claims 8 but is wrong?, this is the frontporch
	 *   HSE (104) is the horizontal count at which the horizontal counter is reset to 0 (so counts 0-103 then back to 0)
	 *
	 * VERTICAL:
	 *   0 is the first vertical line displayed to screen
	 *   VDE (344) affects the number of lines that /LDPS is active for display of
	 *   VSS (362) is the vertical line at which the VSYNC pin goes high
	 *   VST (378) is the vertical line at which the VSYNC pin goes low
	 *   VSE (400) is the vertical line at which the vertical line count is reset to 0
	 *   VOC (0x1c0) controls the polarity and enabling of the sync signals in some unknown way
	 *     Suffice to say, whatever bit combination 0b00011100000x does, it enables both horiz and vert sync and both are positive
	 */
#ifdef DEBUG_VIDEO_CONTROL_W
	static const char *const regDest[16] = { "VSE (End of frame)", "VST (End of VSync)", "VSS (Start of VSync)", "VDE (Active Lines)",
	"unknown 620xxx", "unknown 628xxx", "unknown 630xxx", "unknown 638xxx",
	"HSE (end of horizontal line)", "HST (end of HSync)", "HSS (HSync Start)", "VOC (Video Control)",
	"unknown 660xxx", "unknown 668xxx", "unknown 670xxx", "unknown 678xxx" };
	fprintf(stderr,"Write to video chip address %06X; %02X -> register %s with data %04X\n", 0x600000+(offset<<1), offset&0xFF, regDest[(offset&0x3C000)>>14], data);
#endif
}

// Floppy control register (called fd.cont in the cat source code)
	/* FEDCBA98 (76543210 is open bus)
	 * |||||||\-- unknown[1] (may be some sort of 'reset' or debug bit? the cat code explicitly clears this bit but never sets it)
	 * ||||||\--- WRITE GATE: 0 = write head disabled, 1 = write head enabled (verified from cat source code)
	 * |||||\---- unknown[2] (leftover debug bit? unused by cat code)
	 * ||||\----- /DIRECTION: 1 = in, 0 = out (verified from forth cmd)
	 * |||\------ /SIDESEL: 1 = side1, 0 = side0 (verified from forth cmd)
	 * ||\------- STEP: 1 = STEP active, 0 = STEP inactive (verified from cat source code)
	 * |\-------- MOTOR ON: 1 = on, 0 = off (verified)
	 * \--------- /DRIVESELECT: 1 = drive 0, 0 = drive 1 (verified from forth cmd)
	 * all 8 bits 'stick' on write and are readable at this register as well
	 * [1] writing this bit as high seems to 'freeze' floppy acquisition so
	 *   the value at the floppy_data_r register is held rather than updated
	 *   with new data from the shifter/mfm clock/data separator
	 * [2] this bit's function is unknown. it could possibly be an FM vs MFM selector bit, where high = MFM, low = FM ? or MFM vs GCR?
	 */
// 0x800000-0x800001 read
uint16_t cat_state::cat_floppy_control_r(offs_t offset)
{
#ifdef DEBUG_FLOPPY_CONTROL_R
	fprintf(stderr,"Read from Floppy Status address %06X\n", 0x800000+(offset<<1));
#endif
	return (m_floppy_control << 8)|0x80; // LOW 8 BITS ARE OPEN BUS
}
// 0x800000-0x800001 write
void cat_state::cat_floppy_control_w(offs_t offset, uint16_t data)
{
#ifdef DEBUG_FLOPPY_CONTROL_W
	fprintf(stderr,"Write to Floppy Control address %06X, data %04X\n", 0x800000+(offset<<1), data);
#endif
	m_floppy_control = (data >> 8)&0xFF;
}

// 0x800002-0x800003 read = 0x0080, see open bus
// 0x800002-0x800003 write
void cat_state::cat_keyboard_w(uint16_t data)
{
	m_keyboard_line = data >> 8;
}

// 0x800004-0x800005 'pr.data' write
// /DSTB (centronics pin 1) is implied by the cat source code to be pulsed
// low (for some unknown period of time) upon any write to this port.
void cat_state::cat_printer_data_w(offs_t offset, uint16_t data)
{
#ifdef DEBUG_PRINTER_DATA_W
	fprintf(stderr,"Write to Printer Data address %06X, data %04X\n", 0x800004+(offset<<1), data);
#endif
	m_ctx_data_out->write(data>>8);
	m_ctx->write_strobe(1);
	m_ctx->write_strobe(0);
	m_ctx->write_strobe(1);
}
// 0x800006-0x800007: Floppy data register (called fd.dwr in the cat source code)
uint16_t cat_state::cat_floppy_data_r(offs_t offset)
{
#ifdef DEBUG_FLOPPY_DATA_R
	fprintf(stderr,"Read from Floppy Data address %06X\n", 0x800006+(offset<<1));
#endif
	return 0x0080;
}
void cat_state::cat_floppy_data_w(offs_t offset, uint16_t data)
{
#ifdef DEBUG_FLOPPY_DATA_W
	fprintf(stderr,"Write to Floppy Data address %06X, data %04X\n", 0x800006+(offset<<1), data);
#endif
}

// 0x800008-0x800009: Floppy status register (called fd.status in the cat source code)
	/* FEDCBA98 (76543210 is open bus)
	 * |||||||\-- ? always low
	 * ||||||\--- ? always low
	 * |||||\---- READY: 1 = ready, 0 = not ready (verified from cat source code)
	 * ||||\----- /WRITE PROTECT: 1 = writable, 0 = protected (verified)
	 * |||\------ /TRACK0: 0 = on track 0, 1 = not on track 0 (verified)
	 * ||\------- /INDEX: 0 = index sensor active, 1 = index sensor inactive (verified)
	 * |\-------- ? this bit may indicate which drive is selected, i.e. same as floppy control bit 7; low on drive 1, high on drive 0?
	 * \--------- ? this bit may indicate 'data separator overflow'; it is usually low but becomes high if you manually select the floppy drive
	 ALL of these bits except bit F seem to be reset when the selected drive in floppy control is switched
	 */
uint16_t cat_state::cat_floppy_status_r(offs_t offset)
{
#ifdef DEBUG_FLOPPY_STATUS_R
	fprintf(stderr,"Read from Floppy Status address %06X\n", 0x800008+(offset<<1));
#endif
	return 0x2480;
}

// 0x80000a-0x80000b
uint16_t cat_state::cat_keyboard_r(offs_t offset)
{
	uint16_t retVal = 0;
	// Read country code
	if ((m_pr_cont&0xFF00) == 0x0900)
		retVal = m_dipsw->read();

	// Regular keyboard read
	if ((m_pr_cont&0xFF00) == 0x0800 || (m_pr_cont&0xFF00) == 0x0a00)
	{
		retVal=0xff00;
		switch(m_keyboard_line)
		{
			case 0x01: retVal = m_y0->read() << 8; break;
			case 0x02: retVal = m_y1->read() << 8; break;
			case 0x04: retVal = m_y2->read() << 8; break;
			case 0x08: retVal = m_y3->read() << 8; break;
			case 0x10: retVal = m_y4->read() << 8; break;
			case 0x20: retVal = m_y5->read() << 8; break;
			case 0x40: retVal = m_y6->read() << 8; break;
			case 0x80: retVal = m_y7->read() << 8; break;
		}
	}
#if 0
	if (((m_pr_cont&0xFF00) != 0x0800) && ((m_pr_cont&0xFF00) != 0x0900) && ((m_pr_cont&0xFF00) != 0x0a00))
	{
		fprintf(stderr,"Read from keyboard in %06X with unexpected pr_cont %04X\n", 0x80000a+(offset<<1), m_pr_cont);
	}
#endif
	return retVal;
}

// 0x80000c-0x80000d (unused in cat source code; may have originally been a separate read only port where 800006 would have been write-only)

// 0x80000e-0x80000f 'pr.cont' read
uint16_t cat_state::cat_battery_r()
{
	/*
	 * FEDCBA98 (76543210 is open bus)
	 * |||||||\-- ? possibly PE (pin 1) read ("PAPER OUT" pin 12 of centronics port)
	 * ||||||\--- ? possibly SLCT/ERR (pin 3) read ("not selected or error" NAND of pins 32 and 13 of centronics port)
	 * |||||\---- (always 0?)
	 * ||||\----- (always 0?)
	 * |||\------ (always 0?)
	 * ||\------- (always 0?)
	 * |\-------- (always 0?)
	 * \--------- Battery status (0 = good, 1 = bad)
	 */
	/* just return that battery is full, i.e. bit 15 is 0 */
	/* to make the cat think the battery is bad, return 0x8080 instead of 0x0080 */
	// TODO: hook this to a dipswitch
	return 0x0080;
}
// 0x80000e-0x80000f 'pr.cont' write
void cat_state::cat_printer_control_w(offs_t offset, uint16_t data)
{
	/*
	 * FEDCBA98 (76543210 is ignored)
	 * |||||||\-- CC line enable (pin 34) (verified from cat source code)
	 * ||||||\--- LEDE line enable (pin 33) (verified from cat source code)
	 * |||||\---- ?
	 * ||||\----- ? may be IPP (pin 2) write (non-standard pin 34 of centronics port) or another watchdog reset bit; may also be /DSTB-enable-on-pr.data-write
	 * |||\------ ?
	 * ||\------- ?
	 * |\-------- ?
	 * \--------- ?
	 */
	// writes of 0x0A00 turn on the keyboard LED on the LOCK key
	// writes of 0x0800 turn off the keyboard LED on the LOCK key
#ifdef DEBUG_PRINTER_CONTROL_W
	fprintf(stderr,"Write to Printer Control address %06X, data %04X\n", (offset<<1)+0x80000e, data);
#endif
	m_pr_cont = data;
}

// 0x820000: AMI S35213 300/1200 Single Chip Modem (datasheet found at http://bitsavers.trailing-edge.com/pdf/ami/_dataBooks/1985_AMI_MOS_Products_Catalog.pdf on pdf page 243)
uint16_t cat_state::cat_modem_r(offs_t offset)
{
#ifdef DEBUG_MODEM_R
	fprintf(stderr,"Read from s35213 modem address %06X\n", 0x820000+(offset<<1));
#endif
// HACK: return default 'sane' modem state
	return 0x00;
}

void cat_state::cat_modem_w(offs_t offset, uint16_t data)
{
#ifdef DEBUG_MODEM_W
	fprintf(stderr,"Write to s35213 modem address %06X, data %04X\n", 0x820000+(offset<<1), data);
#endif
}

// 0x830000: 6ms counter (counts KTOBF pulses and does not reset; 16 bits wide)
uint16_t cat_state::cat_6ms_counter_r()
{
	return m_6ms_counter;
}

/* 0x840001: 'opr' or 'ga2opr' Output Port Register (within GA2)
 * writing anything with bit 3 set here resets the watchdog
 * if the watchdog expires /NMI (and maybe /RESET) are asserted to the cpu
 * watchdog counter (counts KTOBF pulses and is reset on any ga2opr write with bit 3 set; <9 bits wide)
 */
void cat_state::cat_opr_w(offs_t offset, uint16_t data)
{
	/*
	 * 76543210 (FEDCBA98 are ignored)
	 * |||||||\-- OFFHOOK pin (pin 3) output control (1 = puts phone off hook by energizing relay K2)
	 * ||||||\--- PHONE pin (pin 4) output control (1 = connects phone and line together by energizing relay K1)
	 * |||||\---- Video enable (1 = video on, 0 = video off/screen black)
	 * ||||\----- Watchdog reset (the watchdog is reset whenever this bit is written with a 1)
	 * |||\------ Video invert (1 = black-on-white video; 0 = white-on-black)
	 * ||\------- (unused?)
	 * |\-------- (unused?)
	 * \--------- (unused?)
	 */
#ifdef DEBUG_GA2OPR_W
	if (data != 0x001C)
		fprintf(stderr, "GA2 OPR (video ena/inv, watchdog, and phone relay) reg write: offset %06X, data %04X\n", 0x840000+(offset<<1), data);
#endif
	if (data&0x08) m_wdt_counter = 0;
	m_video_enable = BIT( data, 2 );
	m_video_invert = 1-BIT( data, 4 );
}

// 0x850000: 'wdt' "watchdog timer and power fail", read only?
	/* NOTE: BELOW IS A GUESS based on seeing what the register reads as,
	 * the cat code barely touches this stuff at all, despite the fact
	 * that the service manual states that PFAIL is supposed to be checked
	 * before each write to SVRAM, the forth code does NOT actually do that!
	 *
	 * 76543210
	 * ??????\\-- Watchdog count? (counts upward? if this reaches <some unknown number greater than 3> the watchdog fires? writing bit 3 set to opr above resets this)
	 *
	 * FEDCBA98
	 * |||||||\-- PFAIL state (MB3771 comparator: 1: vcc = 5v; 0: vcc != 5v, hence do not write to svram!)
	 * ||||||\--- (always 0?)
	 * |||||\---- (always 0?)
	 * ||||\----- (always 0?)
	 * |||\------ (always 0?)
	 * ||\------- (always 0?)
	 * |\-------- (always 0?)
	 * \--------- (always 0?)
	 */
uint16_t cat_state::cat_wdt_r()
{
	uint16_t Retval = 0x0100; // set pfail to 1; should this be a dipswitch?
	return Retval | m_wdt_counter;
}

// 0x860000: 'tcb' "test control bits" test mode register; what the bits do is
// unknown. 0x0000 is written here to disable test mode, and that is the extent
// of the cat touching this register.
void cat_state::cat_tcb_w(offs_t offset, uint16_t data)
{
#ifdef DEBUG_TEST_W
	fprintf(stderr, "Test reg write: offset %06X, data %04X\n", 0x860000+(offset<<1), data);
#endif
}

// open bus etc handlers
uint16_t cat_state::cat_2e80_r()
{
	return 0x2e80;
}

uint16_t cat_state::cat_0000_r()
{
	return 0x0000;
}

uint16_t cat_state::cat_0080_r()
{
	return 0x0080;
}


/* Canon cat memory map, based on testing and a 16MB dump of the entire address space of a running unit using forth "1000000 0 do i c@ semit loop"
68k address map:
a23 a22 a21 a20 a19 a18 a17 a16 a15 a14 a13 a12 a11 a10 a9  a8  a7  a6  a5  a4  a3  a2  a1  (a0 via UDS/LDS)
*i  *i  *   x   x   *   *   *   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x       *GATE ARRAY 2 DECODES THESE LINES TO ENABLE THIS AREA* (a23 and a22 are indirectly decoded via the /RAMROMCS and /IOCS lines from gate array 1)
0   0   0   x   x   0   a   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   b       R   ROM (ab: 00=ic4 01=ic2 10=ic5 11=ic3) (EPROM 27C512 x 4) [controlled via GA2 /ROMCS]
0   0   0   x   x   1   0   0   x   x   *   *   *   *   *   *   *   *   *   *   *   *   *   0       RW  SVRAM ic11 d4364 (battery backed) [controlled via GA2 /RAMCS]
0   0   0   x   x   1   x   1   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   0       O   OPEN BUS (reads as 0x2e) [may be controlled via GA2 /RAMCS?]
0   0   0   x   x   1   1   0   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   0       O   OPEN BUS (reads as 0x2e) [may be controlled via GA2 /RAMCS?]
0   0   0   x   x   1   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   1       O   OPEN BUS (reads as 0x80) [may be controlled via GA2 /RAMCS?]
0   0   1   x   x   0   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   0       R   SVROM 2 ic7 (not present on cat as sold, open bus reads as 0x2e) [controlled via GA2 /SVCS0]
0   0   1   x   x   0   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   1       R   SVROM 0 ic6 (mask ROM tc531000) [controlled via GA2 /SVCS0]
0   0   1   x   x   1   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   0       O   OPEN BUS (reads as 0x2e) [controlled via GA2 /SVCS1] *SEE BELOW*
0   0   1   x   x   1   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   1       R   SVROM 1 ic8 (not present on cat as sold, open bus reads as 0x80) [controlled via GA2 /SVCS1] *SEE BELOW*
                                                                                                    *NOTE: on Dwight E's user-made developer unit, two 128K SRAMS are mapped in place of the
                                                                                                    two entries immediately above!* (this involves some creative wiring+sockets); the official
                                                                                                    IAI 'shadow ram board' maps the ram to the A00000-A3FFFF area instead)
0   1   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *       *BOTH GATE ARRAYS 1 and 2 DECODE THIS AREA; 2 DEALS WITH ADDR AND 1 WITH DATA/CAS/RAS*
0   1   0   x   x   a   b   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *       RW  VIDEO/SYSTEM DRAM (ab: 00=row 0, ic26-29; 01=row 1, ic22-25; 10=row 2; ic18-21; 11=row 3; ic14-17)
                                                                                                    *NOTE: DRAM rows 2 and 3 above are only usually populated in cat developer units!*
0   1   1   ?   ?   *   *   *   ?   ?   ?   ?   ?   ?   ?   *   *   *   *   *   *   *   *   x       W   VIDEO CONTRL REGISTERS (reads as 0x2e80)
1   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   *   *   *   *   *       *GATE ARRAY 3 DECODES THIS AREA, GA3 IS ENABLED BY /IOCS1 FROM GA2*
1   0   0   Y   Y   0   0   0   x   x   x   x   x   x   x   x   x   x   x   *   *   *   *   0       {'ga3'} *IO AREA* Note byte reads in this area behave erratically if both Y bits are set while word reads work fine always
                                                                            x   x   x   x   1       O   OPEN BUS (reads as 0x80)
                                                                            0   0   0   0   0       RW  {'fd.cont'} Floppy control lines (drive select, motor on, direction, step, side select, ?write gate?)
                                                                            0   0   0   1   0       W   {'kb.wr'} Keyboard Row Select (reads as 0x00)
                                                                            0   0   1   0   0       W   {'pr.data'} Centronics Printer Data W (reads as 0x00)
                                                                            0   0   1   1   0       RW  {'fd.dwr' and 'fd.drd'} Floppy data read/write register; maybe the write gate value in fd.cont chooses which?
                                                                            0   1   0   0   0       R   {'fd.status'} Floppy status lines (write protect, ready, index, track0)
                                                                            0   1   0   1   0       R   Keyboard Column Read
                                                                            0   1   1   0   0       W?  Unknown (reads as 0x00)
                                                                            0   1   1   1   0       RW  {'pr.cont'} Read: Battery status (MSB bit, 0 = ok, 1 = dead, other bits read as 0)/Write: Centronics Printer and Keyboard LED/Country Code Related
                                                                            1   x   x   x   0       W?  Unknown (reads as 0x00)
1   0   0   x   x   0   0   1   x   x   x   x   x   x   x   x   x   x   x   *   *   *   *   1       RW  {'duart'} 68681 DUART at ic34 [controlled via GA2 /DUARTCS]
1   0   0   x   x   0   1   0   x   x   x   x   x   x   x   x   x   x   *   *   *   *   *   0       RW  {'modem'} Modem Chip AMI S35213 @ IC37 DATA BIT 7 ONLY [controlled via GA2 /SMCS]
1   0   0   x   x   0   1   1   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   *       R   {'timer'} Read: Fixed 16-bit counter from ga2. increments every 6.5535ms when another 16-bit counter clocked at 10mhz overflows
1   0   0   x   x   1   0   0   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   *       W   {'opr'} Output Port (Video/Sync enable and watchdog reset?) register (screen enable on bit 3?) (reads as 0x2e80)
1   0   0   x   x   1   0   1   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   *       R   {'wdt'} Watchdog timer reads as 0x0100 0x0101 or 0x0102, some sort of test register or video status register?
1   0   0   x   x   1   1   0   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   *       R?W {'tcb'} test control bits (reads as 0x0000)
1   0   0   x   x   1   1   1   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   *       ?   Unknown (reads as 0x2e80)

1   0   1   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x       O   OPEN BUS (reads as 0x2e80) [68k DTACK is asserted by gate array 1 when accessing this area, for testing?] On real IAI shadow ROM board, at least 0x40000 of ram lives here.
1   1   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x       O   OPEN BUS (reads as 0x2e80) [68k VPA is asserted by gate array 1 when accessing this area, for testing?]
*/


void cat_state::cat_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x03ffff).rom().mirror(0x180000); // 256 KB ROM
	map(0x040000, 0x043fff).ram().share("svram").mirror(0x18C000);// SRAM powered by battery
	map(0x200000, 0x27ffff).rom().region("svrom", 0x0000).mirror(0x180000); // SV ROM
	map(0x400000, 0x47ffff).ram().share("p_cat_vram").mirror(0x180000); // 512 KB RAM
	map(0x600000, 0x67ffff).rw(FUNC(cat_state::cat_2e80_r), FUNC(cat_state::cat_video_control_w)).mirror(0x180000); // Gate Array #1: Video Addressing and Timing, dram refresh timing, dram /cs and /wr (ga2 does the actual video invert/display and access to the dram data bus)
	map(0x800000, 0x800001).rw(FUNC(cat_state::cat_floppy_control_r), FUNC(cat_state::cat_floppy_control_w)).mirror(0x18FFE0); // floppy control lines and readback
	map(0x800002, 0x800003).rw(FUNC(cat_state::cat_0080_r), FUNC(cat_state::cat_keyboard_w)).mirror(0x18FFE0); // keyboard col write
	map(0x800004, 0x800005).rw(FUNC(cat_state::cat_0080_r), FUNC(cat_state::cat_printer_data_w)).mirror(0x18FFE0); // Centronics Printer Data
	map(0x800006, 0x800007).rw(FUNC(cat_state::cat_floppy_data_r), FUNC(cat_state::cat_floppy_data_w)).mirror(0x18FFE0); // floppy data read/write
	map(0x800008, 0x800009).r(FUNC(cat_state::cat_floppy_status_r)).mirror(0x18FFE0); // floppy status lines
	map(0x80000a, 0x80000b).r(FUNC(cat_state::cat_keyboard_r)).mirror(0x18FFE0); // keyboard row read
	map(0x80000c, 0x80000d).r(FUNC(cat_state::cat_0080_r)).mirror(0x18FFE0); // Open bus?
	map(0x80000e, 0x80000f).rw(FUNC(cat_state::cat_battery_r), FUNC(cat_state::cat_printer_control_w)).mirror(0x18FFE0); // Centronics Printer Control, keyboard led and country code enable
	map(0x800010, 0x80001f).r(FUNC(cat_state::cat_0080_r)).mirror(0x18FFE0); // Open bus?
	map(0x810000, 0x81001f).rw(m_duart, FUNC(mc68681_device::read), FUNC(mc68681_device::write)).umask16(0x00ff).mirror(0x18FFE0);
	map(0x820000, 0x82003f).rw(FUNC(cat_state::cat_modem_r), FUNC(cat_state::cat_modem_w)).mirror(0x18FFC0); // AMI S35213 Modem Chip, all access is on bit 7
	map(0x830000, 0x830001).r(FUNC(cat_state::cat_6ms_counter_r)).mirror(0x18FFFE); // 16bit 6ms counter clocked by output of another 16bit counter clocked at 10mhz
	map(0x840000, 0x840001).rw(FUNC(cat_state::cat_2e80_r), FUNC(cat_state::cat_opr_w)).mirror(0x18FFFE); // GA2 Output port register (video enable, invert, watchdog reset, phone relays)
	map(0x850000, 0x850001).r(FUNC(cat_state::cat_wdt_r)).mirror(0x18FFFE); // watchdog and power fail state read
	map(0x860000, 0x860001).rw(FUNC(cat_state::cat_0000_r), FUNC(cat_state::cat_tcb_w)).mirror(0x18FFFE); // Test mode
	map(0x870000, 0x870001).r(FUNC(cat_state::cat_2e80_r)).mirror(0x18FFFE); // Open bus?
	map(0xA00000, 0xA00001).r(FUNC(cat_state::cat_2e80_r)).mirror(0x1FFFFE); // Open bus/dtack? The 0xA00000-0xA3ffff area is ram used for shadow ROM storage on cat developer machines, which is either banked over top of, or jumped to instead of the normal ROM
	map(0xC00000, 0xC00001).r(FUNC(cat_state::cat_2e80_r)).mirror(0x3FFFFE); // Open bus/vme?
}

/* Input ports */

/* 2009-07 FP
   FIXME: Natural keyboard does not catch all the Shifted chars. No idea of the reason!  */
static INPUT_PORTS_START( cat )
	PORT_START("DIPSW1")
	PORT_DIPNAME( 0x8000, 0x8000, "Mode" )
	PORT_DIPSETTING(    0x8000, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x0000, "Diagnostic" )
	PORT_DIPNAME( 0x7f00,0x7f00, "Country code" )
	PORT_DIPSETTING(    0x7f00, "United States" )
	PORT_DIPSETTING(    0x7e00, "Canada" )
	PORT_DIPSETTING(    0x7d00, "United Kingdom" )
	PORT_DIPSETTING(    0x7c00, "Norway" )
	PORT_DIPSETTING(    0x7b00, "France" )
	PORT_DIPSETTING(    0x7a00, "Denmark" )
	PORT_DIPSETTING(    0x7900, "Sweden" )
	PORT_DIPSETTING(    0x7800, DEF_STR(Japan) )
	PORT_DIPSETTING(    0x7700, "West Germany" )
	PORT_DIPSETTING(    0x7600, "Netherlands" )
	PORT_DIPSETTING(    0x7500, "Spain" )
	PORT_DIPSETTING(    0x7400, "Italy" )
	PORT_DIPSETTING(    0x7300, "Latin America" )
	PORT_DIPSETTING(    0x7200, "South Africa" )
	PORT_DIPSETTING(    0x7100, "Switzerland" )
	PORT_DIPSETTING(    0x7000, "ASCII" )

	PORT_START("Y0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR(0xA2)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')

	PORT_START("Y1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('n') PORT_CHAR('B')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')

	PORT_START("Y2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED) // totally unused
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')

	PORT_START("Y3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left USE FRONT") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED) // totally unused

	PORT_START("Y4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right USE FRONT") PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_F2) // intl only: latin diaresis and latin !; norway, danish and finnish * and '; others
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')

	PORT_START("Y5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(0xBD) PORT_CHAR(0xBC) //PORT_CHAR('}') PORT_CHAR('{')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')

	PORT_START("Y6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_F1) // intl only: latin inv ? and inv !; norway and danish ! and |; finnish <>; others
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left LEAP") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('[')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED) // totally unused

	PORT_START("Y7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_RSHIFT) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right Leap") PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Page") PORT_CODE(KEYCODE_PGUP) PORT_CODE(KEYCODE_PGDN)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift Lock") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Erase") PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED) // totally unused
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("UNDO") PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR(0xB1) PORT_CHAR(0xB0) // PORT_CHAR('\\') PORT_CHAR('~')
INPUT_PORTS_END


TIMER_CALLBACK_MEMBER(cat_state::counter_6ms_callback)
{
	// This is effectively also the KTOBF (guessed: acronym for "Keyboard Timer Out Bit Flip")
	// line connected in such a way to invert the d-flipflop connected to the duart IP2
	m_duart_ktobf_ff ^= 1;
	m_duart->ip2_w(m_duart_ktobf_ff);
	m_wdt_counter++;
	m_6ms_counter++;
}

void cat_state::cpu_space_map(address_map &map)
{
	map(0xfffff0, 0xffffff).m(m_maincpu, FUNC(m68000_base_device::autovectors_map));
	map(0xfffff3, 0xfffff3).lr8(NAME([this]() { m_maincpu->set_input_line(1, CLEAR_LINE); return m68000_device::autovector(1); }));
}

void cat_state::machine_start()
{
	m_duart_ktobf_ff = 0; // reset doesn't touch this
	m_duart_prn_ack_prev_state = 1; // technically uninitialized
	m_duart_prn_ack_ff = 0; // reset doesn't touch this
	m_6ms_counter = 0;
	m_wdt_counter = 0;
	m_video_enable = 1;
	m_video_invert = 0;
	m_6ms_timer = timer_alloc(FUNC(cat_state::counter_6ms_callback), this);
	subdevice<nvram_device>("nvram")->set_base(m_svram, 0x4000);
}

void cat_state::machine_reset()
{
	m_6ms_counter = 0;
	m_wdt_counter = 0;
	m_floppy_control = 0;
	m_6ms_timer->adjust(attotime::zero, 0, attotime::from_hz((XTAL(19'968'000)/2)/65536));
}

void cat_state::video_start()
{
}

uint32_t cat_state::screen_update_cat(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const rgb_t on_color = m_video_invert ? rgb_t::black() : rgb_t::white();
	const rgb_t off_color = m_video_invert ? rgb_t::white() : rgb_t::black();

	int addr = 0;
	if (m_video_enable == 1)
	{
		for (int y = 0; y < 344; y++)
		{
			int horpos = 0;
			for (int x = 0; x < 42; x++)
			{
				const uint16_t code = m_p_cat_videoram[addr++];
				for (int b = 15; b >= 0; b--)
				{
					bitmap.pix(y, horpos++) = BIT(code, b) ? on_color : off_color;
				}
			}
		}
	} else {
		const rectangle black_area(0, 672 - 1, 0, 344 - 1);
		bitmap.fill(rgb_t::black(), black_area);
	}
	return 0;
}

/* The duart is the only thing actually connected to the cpu IRQ pin
 * The KTOBF output of the gate array 2 (itself the terminal count output
 * of a 16-bit counter clocked at ~10mhz, hence 6.5536ms period) goes to a
 * d-latch and inputs on ip2 of the duart, causing the duart to fire an irq;
 * this is used by the cat to read the keyboard.
 * The duart also will, if configured to do so, fire an int when the state
 * changes of the centronics /ACK pin; this is used while printing.
 */
void cat_state::cat_duart_irq_handler(int state)
{
#ifdef DEBUG_DUART_IRQ_HANDLER
	fprintf(stderr, "Duart IRQ handler called: state: %02X, vector: %06X\n", state, irqvector);
#endif
	m_maincpu->set_input_line(M68K_IRQ_1, state);
}

void cat_state::cat_duart_txa(int state) // semit sends stuff here; connects to the serial port on the back
{
#ifdef DEBUG_DUART_TXA
	fprintf(stderr, "Duart TXA: data %02X\n", state);
#endif
}

void cat_state::cat_duart_txb(int state) // memit sends stuff here; connects to the modem chip
{
#ifdef DEBUG_DUART_TXB
	fprintf(stderr, "Duart TXB: data %02X\n", state);
#endif
}

/* mc68681 DUART Input pins:
 * IP0: CTS [using the DUART builtin hardware-CTS feature?]
 * IP1: Centronics /ACK (pin 10) positive edge detect (IP1 changes state 0->1
        or 1->0 on the rising edge of /ACK using a 74ls74a d-flipflop)
 * IP2: KTOBF (IP2 changes state 0->1 or 1->0 on the rising edge of KTOBF
        using a 74ls74a d-flipflop; KTOBF is a 6.5536ms-period squarewave
        generated by one of the gate arrays, I need to check with a scope to
        see whether it is a single spike/pulse every 6.5536ms or if from the
        gate array it inverts every 6.5536ms, documentation isn't 100% clear
        but I suspect the former) [uses the Delta IP2 state change detection
        feature to generate an interrupt; I'm not sure if IP2 is used as a
        counter clock source but given the beep frequency of the real unit I
        very much doubt it, 6.5536ms is too slow]
 * IP3: RG ("ring" input)
 * IP4: Centronics BUSY (pin 11), inverted
 * IP5: DSR
 */

/* mc68681 DUART Output pins:
 * OP0: RTS [using the DUART builtin hardware-RTS feature?]
 * OP1: DTR
 * OP2: /TDCS (select/enable the S2579 DTMF tone generator chip)
 * OP3: speaker out [using the 'channel b 1X tx or rx clock output' or more likely the 'timer output' feature to generate a squarewave]
 * OP4: TD03 (data bus for the S2579 DTMF tone generator chip)
 * OP5: TD02 "
 * OP6: TD01 "
 * OP7: TD00 "
 */
void cat_state::cat_duart_output(uint8_t data)
{
#ifdef DEBUG_DUART_OUTPUT_LINES
	fprintf(stderr,"Duart output io lines changed to: %02X\n", data);
#endif
	m_speaker->level_w((data >> 3) & 1);
}

void cat_state::prn_ack_ff(int state) // switch the flipflop state on the rising edge of /ACK
{
	if ((m_duart_prn_ack_prev_state == 0) && (state == 1))
	{
		m_duart_prn_ack_ff ^= 1;
	}
	m_duart->ip1_w(m_duart_prn_ack_ff);
	m_duart_prn_ack_prev_state = state;
#ifdef DEBUG_PRN_FF
	fprintf(stderr, "Printer ACK: state %02X, flipflop is now %02x\n", state, m_duart_prn_ack_ff);
#endif
}

void cat_state::cat(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, XTAL(19'968'000)/4);
	m_maincpu->set_addrmap(AS_PROGRAM, &cat_state::cat_mem);
	m_maincpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &cat_state::cpu_space_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(672, 344);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(cat_state::screen_update_cat));

	MC68681(config, m_duart, (XTAL(19'968'000)*2)/11); // duart is normally clocked by 3.6864mhz xtal, but cat seemingly uses a divider from the main xtal instead which probably yields 3.63054545Mhz. There is a trace to cut and a mounting area to allow using an actual 3.6864mhz xtal if you so desire
	m_duart->irq_cb().set(FUNC(cat_state::cat_duart_irq_handler));
	m_duart->a_tx_cb().set(FUNC(cat_state::cat_duart_txa));
	m_duart->b_tx_cb().set(FUNC(cat_state::cat_duart_txb));
	m_duart->outport_cb().set(FUNC(cat_state::cat_duart_output));

	CENTRONICS(config, m_ctx, centronics_devices, "printer");
	m_ctx->ack_handler().set(FUNC(cat_state::prn_ack_ff));
	m_ctx->busy_handler().set(m_duart, FUNC(mc68681_device::ip4_w)).invert();

	OUTPUT_LATCH(config, m_ctx_data_out);
	m_ctx->set_output_latch(*m_ctx_data_out);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 1.00);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);
}

ROM_START( cat )
	ROM_REGION( 0x40000, "maincpu", ROMREGION_ERASEFF )
	// SYS ROM
	/* This 2.40 code came from two development cat machines owned or formerly
	 * owned by former IAI employees Sandy Bumgarner and Dave Boulton.
	 * Dave Boulton's machine is interesting in that it has a prototype cat
	 * motherboard in it, which it has less space for dram than a 'released'
	 * cat does: it uses 16k*4 dram chips instead of 64k*4 as in the final
	 * cat, and hence can only support 128k of ram with all 4 rows of drams
	 * populated, as opposed to 256k-standard (2 rows) and 512k-max with all
	 * 4 rows populated on a "released" cat.
	 */
	ROM_SYSTEM_BIOS( 0, "r240", "Canon Cat V2.40 US Firmware")
	ROMX_LOAD( "boultl0.ic2", 0x00001, 0x10000, CRC(77b66208) SHA1(9d718c0a521fefe4f86ef328805b7921bade9d89), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "boulth0.ic4", 0x00000, 0x10000, CRC(f1e1361a) SHA1(0a85385527e2cc55790de9f9919eb44ac32d7f62), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "boultl1.ic3", 0x20001, 0x10000, CRC(c61dafb0) SHA1(93216c26c2d5fc71412acc548c96046a996ea668), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "boulth1.ic5", 0x20000, 0x10000, CRC(bed1f761) SHA1(d177e1d3a39b005dd94a6bda186221d597129af4), ROM_SKIP(1) | ROM_BIOS(0))
	/* This 2.40 code was compiled by Dwight Elvey based on the v2.40 source
	 * code disks recovered around 2004. It does NOT exactly match the above
	 * set exactly but has a few small differences. One of the printer drivers
	 * may have been replaced by Dwight with an HP PCL4 driver.
	 * It is as of yet unknown whether it is earlier or later code than the
	 * set above.
	 */
	ROM_SYSTEM_BIOS( 1, "r240r", "Canon Cat V2.40 US Firmware compiled from recovered source code")
	ROMX_LOAD( "r240l0.ic2", 0x00001, 0x10000, CRC(1b89bdc4) SHA1(39c639587dc30f9d6636b46d0465f06272838432), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "r240h0.ic4", 0x00000, 0x10000, CRC(94f89b8c) SHA1(6c336bc30636a02c625d31f3057ec86bf4d155fc), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "r240l1.ic3", 0x20001, 0x10000, CRC(1a73be4f) SHA1(e2de2cb485f78963368fb8ceba8fb66ca56dba34), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "r240h1.ic5", 0x20000, 0x10000, CRC(898dd9f6) SHA1(93e791dd4ed7e4afa47a04df6fdde359e41c2075), ROM_SKIP(1) | ROM_BIOS(1))
	/* This v1.74 code comes from (probably) the 'main us release' of first-run
	 * Canon cats, and was dumped from machine serial number R12014979
	 * Canon cat v1.74 ROMs are labeled as r74; they only added the major number
	 * to the ROM label after v2.0?
	 */
	ROM_SYSTEM_BIOS( 2, "r174", "Canon Cat V1.74 US Firmware")
	ROMX_LOAD( "r74__0l__c18c.blue.ic2", 0x00001, 0x10000, CRC(b19aa0c8) SHA1(85b3e549cfb91bd3dd32335e02eaaf9350e80900), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD( "r74__0h__75a6.yellow.ic4", 0x00000, 0x10000, CRC(75281f77) SHA1(ed8b5e37713892ee83413d23c839d09e2fd2c1a9), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD( "r74__1l__c8a3.green.ic3", 0x20001, 0x10000, CRC(93275558) SHA1(f690077a87076fd51ae385ac5a455804cbc43c8f), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD( "r74__1h__3c37.white.ic5", 0x20000, 0x10000, CRC(5d7c3962) SHA1(8335993583fdd30b894c01c1a7a6aca61cd81bb4), ROM_SKIP(1) | ROM_BIOS(2))
	// According to Sandy Bumgarner, there should be a 2.42 version which fixes some bugs in the calc command vs 2.40
	// According to the Cat Repair Manual page 4-20, there should be a version called B91U0x (maybe 1.91 or 0.91?) with sum16s of 9F1F, FF0A, 79BF and 03FF

	ROM_REGION16_BE( 0x80000, "svrom", ROMREGION_ERASE00 )
	// SPELLING VERIFICATION ROM (SVROM)
	/* Romspace here is a little strange: there are 3 ROM sockets on the board:
	 * svrom-0 maps to 200000-21ffff every ODD byte (d8-d0)
	 * svrom-1 maps to 200000-21ffff every EVEN byte (d15-d7)
	 *  (since no ROM is in the socket; it reads as open bus, sometimes 0x2E)
	 * svrom-2 maps to 240000-25ffff every ODD byte (d8-d0)
	 *  (since no ROM is in the socket; it reads as open bus, sometimes 0x80)
	 * there is no svrom-3 socket; 240000-25ffff EVEN always reads as 0x2E
	 * since ROM_FILL16BE(0x0, 0x80000, 0x2e80) doesn't exist, the
	 * even bytes and latter chunk of the svrom space need to be filled in
	 * DRIVER_INIT or some other means needs to be found to declare them as
	 * 'open bus' once the mame/mess core supports that.
	 * NOTE: there are at least 6 more SVROMS which existed (possibly in
	 * limited form), and are not dumped:
	 * UK (1 ROM, NH7-0724)
	 * French/Quebec (2 ROMs, NH7-0813/0814)
	 * German (3 ROMs, NH7-1019/1020/1021)
	 * Each of these will also have its own code ROMset as well.
	 */
	// NH7-0684 (US, dumped):
	ROMX_LOAD( "uv1__nh7-0684__hn62301apc11__7h1.ic6", 0x00001, 0x20000, CRC(229ca210) SHA1(564b57647a34acdd82159993a3990a412233da14), ROM_SKIP(1)) // this is a 28pin tc531000 mask ROM, 128KB long; "US" SVROM

	/* There is an unpopulated PAL16L8 at IC9 whose original purpose (based
	 * on the schematics) was probably to cause a 68k bus error when
	 * memory in certain ranges when accessed (likely so 'forth gone insane'
	 * won't destroy the contents of ram and svram).
	 * Its connections are (where Ix = inp on pin x, Ox = out on pin x):
	 * I1 = A23, I2 = A22, I3 = A2, I4 = R/W, I5 = A5, I6 = FC2, I7 = gnd,
	 * I8 = A1, I9 = gnd, I11 = gnd, O16 = /BERR,
	 * I14 = REMAP (connects to emulator 'shadow ROM' board or to gnd when unused)
	 * Based on the inputs and outputs of this pal, almost if not the entire
	 * open bus and mirrored areas of the cat address space could be made
	 * to cause bus errors. REMAP was probably used to 'open up' the A00000-A7ffff
	 * shadow ROM/RAM area and make it writeable without erroring.
	 */
ROM_END

} // Anonymous namespace
/* Driver */

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY  FULLNAME  FLAGS */
COMP( 1987, cat,  0,      0,      cat,     cat,   cat_state, empty_init, "Canon", "Cat",    MACHINE_NOT_WORKING)



cbm2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

    TODO:

    - 8088 board
    - CIA timers fail in burn-in test
    - cbm620hu charom banking?

*/

#include "emu.h"

#include "cbm_snqk.h"

#include "bus/cbm2/exp.h"
#include "bus/cbm2/user.h"
#include "bus/ieee488/ieee488.h"
#include "bus/pet/cass.h"
#include "bus/rs232/rs232.h"
#include "bus/vcs_ctrl/ctrl.h"
#include "cpu/m6502/m6509.h"
#include "cpu/i86/i86.h"
#include "imagedev/snapquik.h"
#include "machine/6525tpi.h"
#include "machine/ds75160a.h"
#include "machine/ds75161a.h"
#include "machine/input_merger.h"
#include "machine/mos6526.h"
#include "machine/mos6551.h"
#include "machine/pic8259.h"
#include "machine/pla.h"
#include "machine/ram.h"
#include "sound/mos6581.h"
#include "video/mc6845.h"
#include "video/mos6566.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "utf8.h"


namespace {

#define PLA1_TAG        "u78"
#define PLA2_TAG        "u88"
#define MOS6567_TAG     "u23"
#define MOS6569_TAG     "u23"
#define MC68B45_TAG     "u10"
#define MOS6581_TAG     "u4"
#define MOS6525_1_TAG   "u20"
#define MOS6525_2_TAG   "u102"
#define MOS6551A_TAG    "u19"
#define MOS6526_TAG     "u2"
#define DS75160A_TAG    "u3"
#define DS75161A_TAG    "u7"
#define SCREEN_TAG      "screen"
#define CONTROL1_TAG    "joy1"
#define CONTROL2_TAG    "joy2"
#define RS232_TAG       "rs232"
#define USER_PORT_TAG   "user"

#define EXT_I8088_TAG   "ext_u1"
#define EXT_I8087_TAG   "ext_u4"
#define EXT_I8259A_TAG  "ext_u3"
#define EXT_MOS6526_TAG "ext_u15"
#define EXT_MOS6525_TAG "ext_u16"

class cbm2_state : public driver_device
{
public:
	cbm2_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "u13"),
		m_pla1(*this, PLA1_TAG),
		m_crtc(*this, MC68B45_TAG),
		m_palette(*this, "palette"),
		m_sid(*this, MOS6581_TAG),
		m_tpi1(*this, MOS6525_1_TAG),
		m_tpi2(*this, MOS6525_2_TAG),
		m_acia(*this, MOS6551A_TAG),
		m_cia(*this, MOS6526_TAG),
		m_ieee1(*this, DS75160A_TAG),
		m_ieee2(*this, DS75161A_TAG),
		m_joy1(*this, CONTROL1_TAG),
		m_joy2(*this, CONTROL2_TAG),
		m_exp(*this, "exp"),
		m_user(*this, USER_PORT_TAG),
		m_ram(*this, RAM_TAG),
		m_cassette(*this, PET_DATASSETTE_PORT_TAG),
		m_ieee(*this, IEEE488_TAG),
		m_ext_cpu(*this, EXT_I8088_TAG),
		m_ext_pic(*this, EXT_I8259A_TAG),
		m_ext_cia(*this, EXT_MOS6526_TAG),
		m_ext_tpi(*this, EXT_MOS6525_TAG),
		m_basic(*this, "basic"),
		m_kernal(*this, "kernal"),
		m_charom(*this, "charom"),
		m_buffer_ram(*this, "buffer_ram", 0x800, ENDIANNESS_LITTLE),
		m_extbuf_ram(*this, "extbuf_ram", 0x800, ENDIANNESS_LITTLE),
		m_video_ram(*this, "video_ram", 0x800, ENDIANNESS_LITTLE),
		m_pa(*this, "PA%u", 0),
		m_pb(*this, "PB%u", 0),
		m_lock(*this, "LOCK"),
		m_dramon(1),
		m_video_ram_size(0x800),
		m_graphics(1),
		m_todclk(0),
		m_tpi2_pa(0),
		m_tpi2_pb(0)
	{ }

	required_device<m6509_device> m_maincpu;
	required_device<pla_device> m_pla1;
	optional_device<mc6845_device> m_crtc;
	optional_device<palette_device> m_palette;
	required_device<mos6581_device> m_sid;
	required_device<tpi6525_device> m_tpi1;
	required_device<tpi6525_device> m_tpi2;
	required_device<mos6551_device> m_acia;
	required_device<mos6526_device> m_cia;
	required_device<ds75160a_device> m_ieee1;
	required_device<ds75161a_device> m_ieee2;
	required_device<vcs_control_port_device> m_joy1;
	required_device<vcs_control_port_device> m_joy2;
	required_device<cbm2_expansion_slot_device> m_exp;
	required_device<cbm2_user_port_device> m_user;
	required_device<ram_device> m_ram;
	required_device<pet_datassette_port_device> m_cassette;
	required_device<ieee488_device> m_ieee;
	optional_device<cpu_device> m_ext_cpu;
	optional_device<pic8259_device> m_ext_pic;
	optional_device<mos6526_device> m_ext_cia;
	optional_device<tpi6525_device> m_ext_tpi;
	required_memory_region m_basic;
	required_memory_region m_kernal;
	required_memory_region m_charom;
	memory_share_creator<uint8_t> m_buffer_ram;
	memory_share_creator<uint8_t> m_extbuf_ram;
	memory_share_creator<uint8_t> m_video_ram;
	required_ioport_array<8> m_pa;
	required_ioport_array<8> m_pb;
	required_ioport m_lock;

	TIMER_CALLBACK_MEMBER( tod_tick );

	DECLARE_MACHINE_START( cbm2 );
	DECLARE_MACHINE_START( cbm2_ntsc );
	DECLARE_MACHINE_START( cbm2_pal );
	DECLARE_MACHINE_START( cbm2x_ntsc );
	DECLARE_MACHINE_START( cbm2x_pal );
	DECLARE_MACHINE_RESET( cbm2 );

	virtual void read_pla(offs_t offset, int ras, int cas, int refen, int eras, int ecas,
		int *casseg1, int *casseg2, int *casseg3, int *casseg4, int *rasseg1, int *rasseg2, int *rasseg3, int *rasseg4);

	void bankswitch(offs_t offset, int eras, int ecas, int refen, int cas, int ras, int *sysioen, int *dramen,
		int *casseg1, int *casseg2, int *casseg3, int *casseg4, int *buframcs, int *extbufcs, int *vidramcs,
		int *diskromcs, int *csbank1, int *csbank2, int *csbank3, int *basiccs, int *knbcs, int *kernalcs,
		int *crtccs, int *cs1, int *sidcs, int *extprtcs, int *ciacs, int *aciacs, int *tript1cs, int *tript2cs);

	uint8_t read_keyboard();
	void set_busy2(int state);

	uint8_t read(offs_t offset);
	void write(offs_t offset, uint8_t data);
	uint8_t ext_read(offs_t offset);
	void ext_write(offs_t offset, uint8_t data);

	uint8_t sid_potx_r();
	uint8_t sid_poty_r();

	uint8_t tpi1_pa_r();
	void tpi1_pa_w(uint8_t data);
	uint8_t tpi1_pb_r();
	void tpi1_pb_w(uint8_t data);
	void tpi1_ca_w(int state);
	void tpi1_cb_w(int state);

	void tpi2_pa_w(uint8_t data);
	void tpi2_pb_w(uint8_t data);
	uint8_t tpi2_pc_r();

	uint8_t cia_pa_r();
	void cia_pa_w(uint8_t data);
	uint8_t cia_pb_r();

	uint8_t ext_tpi_pb_r();
	void ext_tpi_pb_w(uint8_t data);
	void ext_tpi_pc_w(uint8_t data);

	void ext_cia_irq_w(int state);
	uint8_t ext_cia_pb_r();
	void ext_cia_pb_w(uint8_t data);

	MC6845_UPDATE_ROW( crtc_update_row );

	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cbmb);
	// memory state
	int m_dramon;
	int m_busen1;
	int m_busy2;

	// video state
	size_t m_video_ram_size;
	int m_graphics;
	int m_ntsc;

	// interrupt state
	int m_todclk;

	// keyboard state;
	uint8_t m_tpi2_pa;
	uint8_t m_tpi2_pb;
	uint8_t m_cia_pa;

	uint8_t m_ext_cia_pb;
	uint8_t m_ext_tpi_pb;

	// timers
	emu_timer *m_todclk_timer;
	void _128k(machine_config &config);
	void _256k(machine_config &config);
	void cbm2lp_ntsc(machine_config &config);
	void cbm2lp_pal(machine_config &config);
	void cbm2hp_ntsc(machine_config &config);
	void cbm2hp_pal(machine_config &config);
	void cbm620(machine_config &config);
	void b128(machine_config &config);
	void b256(machine_config &config);
	void cbm610(machine_config &config);
	void cbm2_mem(address_map &map) ATTR_COLD;
	void ext_io(address_map &map) ATTR_COLD;
	void ext_mem(address_map &map) ATTR_COLD;
};


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

	virtual void read_pla(offs_t offset, int ras, int cas, int refen, int eras, int ecas,
		int *casseg1, int *casseg2, int *casseg3, int *casseg4, int *rasseg1, int *rasseg2, int *rasseg3, int *rasseg4) override;

	uint8_t tpi2_pc_r();
	void b256hp(machine_config &config);
	void b128hp(machine_config &config);
	void cbm710(machine_config &config);
	void cbm730(machine_config &config);
	void cbm720(machine_config &config);
	void bx256hp(machine_config &config);
};


class p500_state : public cbm2_state
{
public:
	p500_state(const machine_config &mconfig, device_type type, const char *tag)
		: cbm2_state(mconfig, type, tag),
			m_pla2(*this, PLA2_TAG),
			m_vic(*this, MOS6569_TAG),
			m_color_ram(*this, "color_ram", 0x400, ENDIANNESS_LITTLE),
			m_statvid(1),
			m_vicdotsel(1),
			m_vicbnksel(0x03)
	{ }

	required_device<pla_device> m_pla2;
	required_device<mos6566_device> m_vic;
	memory_share_creator<uint8_t> m_color_ram;

	DECLARE_MACHINE_START( p500 );
	DECLARE_MACHINE_START( p500_ntsc );
	DECLARE_MACHINE_START( p500_pal );
	DECLARE_MACHINE_RESET( p500 );

	void read_pla1(offs_t offset, int busy2, int clrnibcsb, int procvid, int refen, int ba, int aec, int srw,
		int *datxen, int *dramxen, int *clrniben, int *segf, int *_64kcasen, int *casenb, int *viddaten, int *viddat_tr);

	void read_pla2(offs_t offset, offs_t va, int ba, int vicen, int ae, int segf, int bank0,
		int *clrnibcsb, int *extbufcs, int *discromcs, int *buframcs, int *charomcs, int *procvid, int *viccs, int *vidmatcs);

	void bankswitch(offs_t offset, offs_t va, int srw, int ba, int ae, int busy2, int refen,
		int *datxen, int *dramxen, int *clrniben, int *_64kcasen, int *casenb, int *viddaten, int *viddat_tr,
		int *clrnibcs, int *extbufcs, int *discromcs, int *buframcs, int *charomcs, int *viccs, int *vidmatcs,
		int *csbank1, int *csbank2, int *csbank3, int *basiclocs, int *basichics, int *kernalcs,
		int *cs1, int *sidcs, int *extprtcs, int *ciacs, int *aciacs, int *tript1cs, int *tript2cs, int *aec, int *vsysaden);

	uint8_t read_memory(offs_t offset, offs_t va, int ba, int ae);
	void write_memory(offs_t offset, uint8_t data, int ba, int ae);

	uint8_t read(offs_t offset);
	void write(offs_t offset, uint8_t data);

	uint8_t vic_videoram_r(offs_t offset);
	uint8_t vic_colorram_r(offs_t offset);

	void tpi1_ca_w(int state);
	void tpi1_cb_w(int state);

	uint8_t tpi2_pc_r();
	void tpi2_pc_w(uint8_t data);

	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_p500);
	// video state
	int m_statvid;
	int m_vicdotsel;
	int m_vicbnksel;

	// interrupt state
	void p500_pal(machine_config &config);
	void p500_ntsc(machine_config &config);
	void p500_mem(address_map &map) ATTR_COLD;
	void vic_colorram_map(address_map &map) ATTR_COLD;
	void vic_videoram_map(address_map &map) ATTR_COLD;
};



//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

#define P3 BIT(offset, 19)
#define P2 BIT(offset, 18)
#define P1 BIT(offset, 17)
#define P0 BIT(offset, 16)
#define A15 BIT(offset, 15)
#define A14 BIT(offset, 14)
#define A13 BIT(offset, 13)
#define A12 BIT(offset, 12)
#define A11 BIT(offset, 11)
#define A10 BIT(offset, 10)
#define A0 BIT(offset, 0)
#define VA12 BIT(va, 12)

static void cbmb_quick_sethiaddress(address_space &space, uint16_t hiaddress)
{
	space.write_byte(0xf0046, hiaddress & 0xff);
	space.write_byte(0xf0047, hiaddress >> 8);
}

QUICKLOAD_LOAD_MEMBER(cbm2_state::quickload_cbmb)
{
	return general_cbm_loadsnap(image, m_maincpu->space(AS_PROGRAM), 0x10000, cbmb_quick_sethiaddress);
}

QUICKLOAD_LOAD_MEMBER(p500_state::quickload_p500)
{
	return general_cbm_loadsnap(image, m_maincpu->space(AS_PROGRAM), 0, cbmb_quick_sethiaddress);
}

//**************************************************************************
//  ADDRESS DECODING
//**************************************************************************

//-------------------------------------------------
//  read_pla - low profile PLA read
//-------------------------------------------------

void cbm2_state::read_pla(offs_t offset, int ras, int cas, int refen, int eras, int ecas,
	int *casseg1, int *casseg2, int *casseg3, int *casseg4, int *rasseg1, int *rasseg2, int *rasseg3, int *rasseg4)
{
	uint32_t input = P0 << 15 | P1 << 14 | P2 << 13 | P3 << 12 | m_busy2 << 11 | eras << 10 | ecas << 9 | refen << 8 | cas << 7 | ras << 6;
	uint32_t data = m_pla1->read(input);

	*casseg1 = BIT(data, 0);
	*rasseg1 = BIT(data, 1);
	*rasseg2 = BIT(data, 2);
	*casseg2 = BIT(data, 3);
	*rasseg4 = BIT(data, 4);
	*casseg4 = BIT(data, 5);
	*casseg3 = BIT(data, 6);
	*rasseg3 = BIT(data, 7);
}


//-------------------------------------------------
//  read_pla - high profile PLA read
//-------------------------------------------------

void cbm2hp_state::read_pla(offs_t offset, int ras, int cas, int refen, int eras, int ecas,
	int *casseg1, int *casseg2, int *casseg3, int *casseg4, int *rasseg1, int *rasseg2, int *rasseg3, int *rasseg4)
{
	uint32_t input = ras << 13 | cas << 12 | refen << 11 | eras << 10 | ecas << 9 | m_busy2 << 8 | P3 << 3 | P2 << 2 | P1 << 1 | P0;
	uint32_t data = m_pla1->read(input);

	*casseg1 = BIT(data, 0);
	*casseg2 = BIT(data, 1);
	*casseg3 = BIT(data, 2);
	*casseg4 = BIT(data, 3);
	*rasseg1 = BIT(data, 4);
	*rasseg2 = BIT(data, 5);
	*rasseg3 = BIT(data, 6);
	*rasseg4 = BIT(data, 7);
}


//-------------------------------------------------
//  bankswitch -
//-------------------------------------------------

void cbm2_state::bankswitch(offs_t offset, int eras, int ecas, int refen, int cas, int ras, int *sysioen, int *dramen,
	int *casseg1, int *casseg2, int *casseg3, int *casseg4, int *buframcs, int *extbufcs, int *vidramcs,
	int *diskromcs, int *csbank1, int *csbank2, int *csbank3, int *basiccs, int *knbcs, int *kernalcs,
	int *crtccs, int *cs1, int *sidcs, int *extprtcs, int *ciacs, int *aciacs, int *tript1cs, int *tript2cs)
{
	int rasseg1 = 1, rasseg2 = 1, rasseg3 = 1, rasseg4 = 1;

	this->read_pla(offset, ras, cas, refen, eras, ecas, casseg1, casseg2, casseg3, casseg4, &rasseg1, &rasseg2, &rasseg3, &rasseg4);

	int decoden = 0;
	*sysioen = !(P0 && P1 && P2 && P3) && m_busen1;
	*dramen = !((!(P0 && P1 && P2 && P3)) && m_busen1);

	if (!decoden && !*sysioen)
	{
		switch ((offset >> 13) & 0x07)
		{
		case 0:
			switch ((offset >> 11) & 0x03)
			{
			case 0: *buframcs = 0; break;
			case 1: *extbufcs = 0; break;
			case 2: // fallthru
			case 3: *diskromcs = 0; break;
			}
			break;

		case 1: *csbank1 = 0; break;
		case 2: *csbank2 = 0; break;
		case 3: *csbank3 = 0; break;
		case 4: *basiccs = 0; break;
		case 5: *knbcs = 0; break;
		case 6:
			switch ((offset >> 11) & 0x03)
			{
			case 2: *vidramcs = 0; break;
			case 3:
				switch ((offset >> 8) & 0x07)
				{
				case 0: *crtccs = 0; break;
				case 1: *cs1 = 0; break;
				case 2: *sidcs = 0; break;
				case 3: *extprtcs = 0; break;
				case 4: *ciacs = 0; break;
				case 5: *aciacs = 0; break;
				case 6: *tript1cs = 0; break;
				case 7: *tript2cs = 0; break;
				}
				break;
			}
			break;

		case 7: *kernalcs = 0; break;
		}
	}
}


//-------------------------------------------------
//  read -
//-------------------------------------------------

uint8_t cbm2_state::read(offs_t offset)
{
	int eras = 1, ecas = 1, refen = 0, cas = 0, ras = 1, sysioen = 1, dramen = 1;
	int casseg1 = 1, casseg2 = 1, casseg3 = 1, casseg4 = 1, buframcs = 1, extbufcs = 1, vidramcs = 1;
	int diskromcs = 1, csbank1 = 1, csbank2 = 1, csbank3 = 1, basiccs = 1, knbcs = 1, kernalcs = 1;
	int crtccs = 1, cs1 = 1, sidcs = 1, extprtcs = 1, ciacs = 1, aciacs = 1, tript1cs = 1, tript2cs = 1;

	bankswitch(offset, eras, ecas, refen, cas, ras, &sysioen, &dramen,
		&casseg1, &casseg2, &casseg3, &casseg4, &buframcs, &extbufcs, &vidramcs,
		&diskromcs, &csbank1, &csbank2, &csbank3, &basiccs, &knbcs, &kernalcs,
		&crtccs, &cs1, &sidcs, &extprtcs, &ciacs, &aciacs, &tript1cs, &tript2cs);

	uint8_t data = 0xff;

	if (!dramen)
	{
		if (!casseg1)
		{
			data = m_ram->pointer()[offset & 0xffff];
		}
		if (!casseg2)
		{
			data = m_ram->pointer()[0x10000 | (offset & 0xffff)];
		}
		if (!casseg3 && (m_ram->size() > 0x20000))
		{
			data = m_ram->pointer()[0x20000 | (offset & 0xffff)];
		}
		if (!casseg4 && (m_ram->size() > 0x30000))
		{
			data = m_ram->pointer()[0x30000 | (offset & 0xffff)];
		}
	}

	if (!sysioen)
	{
		if (!buframcs)
		{
			data = m_buffer_ram[offset & 0x7ff];
		}
		if (!extbufcs && m_extbuf_ram)
		{
			data = m_extbuf_ram[offset & 0x7ff];
		}
		if (!vidramcs)
		{
			data = m_video_ram[offset & 0x7ff];
		}
		if (!basiccs || !knbcs)
		{
			data = m_basic->base()[offset & 0x3fff];
		}
		if (!kernalcs)
		{
			data = m_kernal->base()[offset & 0x1fff];
		}
		if (!crtccs)
		{
			if (A0)
			{
				data = m_crtc->register_r();
			}
			else
			{
				data = m_crtc->status_r();
			}
		}
		if (!sidcs)
		{
			data = m_sid->read(offset & 0x1f);
		}
		if (!extprtcs && m_ext_cia)
		{
			data = m_ext_cia->read(offset & 0x0f);
		}
		if (!ciacs)
		{
			data = m_cia->read(offset & 0x0f);
		}
		if (!aciacs)
		{
			data = m_acia->read(offset & 0x03);
		}
		if (!tript1cs)
		{
			data = m_tpi1->read(offset & 0x07);
		}
		if (!tript2cs)
		{
			data = m_tpi2->read(offset & 0x07);
		}

		data = m_exp->read(offset & 0x1fff, data, csbank1, csbank2, csbank3);
	}

	return data;
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

void cbm2_state::write(offs_t offset, uint8_t data)
{
	int eras = 1, ecas = 1, refen = 0, cas = 0, ras = 1, sysioen = 1, dramen = 1;
	int casseg1 = 1, casseg2 = 1, casseg3 = 1, casseg4 = 1, buframcs = 1, extbufcs = 1, vidramcs = 1;
	int diskromcs = 1, csbank1 = 1, csbank2 = 1, csbank3 = 1, basiccs = 1, knbcs = 1, kernalcs = 1;
	int crtccs = 1, cs1 = 1, sidcs = 1, extprtcs = 1, ciacs = 1, aciacs = 1, tript1cs = 1, tript2cs = 1;

	bankswitch(offset, eras, ecas, refen, cas, ras, &sysioen, &dramen,
		&casseg1, &casseg2, &casseg3, &casseg4, &buframcs, &extbufcs, &vidramcs,
		&diskromcs, &csbank1, &csbank2, &csbank3, &basiccs, &knbcs, &kernalcs,
		&crtccs, &cs1, &sidcs, &extprtcs, &ciacs, &aciacs, &tript1cs, &tript2cs);

	if (!dramen)
	{
		if (!casseg1)
		{
			m_ram->pointer()[offset & 0xffff] = data;
		}
		if (!casseg2)
		{
			m_ram->pointer()[0x10000 | (offset & 0xffff)] = data;
		}
		if (!casseg3 && (m_ram->size() > 0x20000))
		{
			m_ram->pointer()[0x20000 | (offset & 0xffff)] = data;
		}
		if (!casseg4 && (m_ram->size() > 0x30000))
		{
			m_ram->pointer()[0x30000 | (offset & 0xffff)] = data;
		}
	}

	if (!sysioen)
	{
		if (!buframcs)
		{
			m_buffer_ram[offset & 0x7ff] = data;
		}
		if (!extbufcs && m_extbuf_ram)
		{
			m_extbuf_ram[offset & 0x7ff] = data;
		}
		if (!vidramcs)
		{
			m_video_ram[offset & 0x7ff] = data;
		}
		if (!crtccs)
		{
			if (A0)
			{
				m_crtc->register_w(data);
			}
			else
			{
				m_crtc->address_w(data);
			}
		}
		if (!sidcs)
		{
			m_sid->write(offset & 0x1f, data);
		}
		if (!extprtcs && m_ext_cia)
		{
			m_ext_cia->write(offset & 0x0f, data);
		}
		if (!ciacs)
		{
			m_cia->write(offset & 0x0f, data);
		}
		if (!aciacs)
		{
			m_acia->write(offset & 0x03, data);
		}
		if (!tript1cs)
		{
			m_tpi1->write(offset & 0x07, data);
		}
		if (!tript2cs)
		{
			m_tpi2->write(offset & 0x07, data);
		}

		m_exp->write(offset & 0x1fff, data, csbank1, csbank2, csbank3);
	}
}


//-------------------------------------------------
//  ext_read -
//-------------------------------------------------

uint8_t cbm2_state::ext_read(offs_t offset)
{
#ifdef USE_PLA_DECODE
	int ras = 1, cas = 1, refen = 0, eras = 1, ecas = 0;
	int casseg1 = 1, casseg2 = 1, casseg3 = 1, casseg4 = 1, rasseg1 = 1, rasseg2 = 1, rasseg3 = 1, rasseg4 = 1;

	this->read_pla(offset, ras, cas, refen, eras, ecas, &casseg1, &casseg2, &casseg3, &casseg4, &rasseg1, &rasseg2, &rasseg3, &rasseg4);
	uint8_t data = 0xff;

	if (!casseg1)
	{
		data = m_ram->pointer()[offset & 0xffff];
	}
	if (!casseg2)
	{
		data = m_ram->pointer()[0x10000 | (offset & 0xffff)];
	}
	if (!casseg3 && (m_ram->size() > 0x20000))
	{
		data = m_ram->pointer()[0x20000 | (offset & 0xffff)];
	}
	if (!casseg4 && (m_ram->size() > 0x30000))
	{
		data = m_ram->pointer()[0x30000 | (offset & 0xffff)];
	}

	return data;
#endif

	uint8_t data = 0;
	if (offset < 0x40000) data = m_ram->pointer()[offset];
	return data;
}


//-------------------------------------------------
//  ext_write -
//-------------------------------------------------

void cbm2_state::ext_write(offs_t offset, uint8_t data)
{
#ifdef USE_PLA_DECODE
	int ras = 1, cas = 1, refen = 0, eras = 1, ecas = 0;
	int casseg1 = 1, casseg2 = 1, casseg3 = 1, casseg4 = 1, rasseg1 = 1, rasseg2 = 1, rasseg3 = 1, rasseg4 = 1;

	this->read_pla(offset, ras, cas, refen, eras, ecas, &casseg1, &casseg2, &casseg3, &casseg4, &rasseg1, &rasseg2, &rasseg3, &rasseg4);

	if (!casseg1)
	{
		m_ram->pointer()[offset & 0xffff] = data;
	}
	if (!casseg2)
	{
		m_ram->pointer()[0x10000 | (offset & 0xffff)] = data;
	}
	if (!casseg3 && (m_ram->size() > 0x20000))
	{
		m_ram->pointer()[0x20000 | (offset & 0xffff)] = data;
	}
	if (!casseg4 && (m_ram->size() > 0x30000))
	{
		m_ram->pointer()[0x30000 | (offset & 0xffff)] = data;
	}
#endif

	if (offset < 0x40000) m_ram->pointer()[offset] = data;
}


//-------------------------------------------------
//  read_pla1 - P500 PLA #1 read
//-------------------------------------------------

void p500_state::read_pla1(offs_t offset, int busy2, int clrnibcsb, int procvid, int refen, int ba, int aec, int srw,
	int *datxen, int *dramxen, int *clrniben, int *segf, int *_64kcasen, int *casenb, int *viddaten, int *viddat_tr)
{
	int sphi2 = m_vic->phi0_r();
	int bras = 1;

	uint32_t input = P0 << 15 | P2 << 14 | bras << 13 | P1 << 12 | P3 << 11 | busy2 << 10 | m_statvid << 9 | sphi2 << 8 |
			clrnibcsb << 7 | m_dramon << 6 | procvid << 5 | refen << 4 | m_vicdotsel << 3 | ba << 2 | aec << 1 | srw;

	uint32_t data = m_pla1->read(input);

	*datxen = BIT(data, 0);
	*dramxen = BIT(data, 1);
	*clrniben = BIT(data, 2);
	*segf = BIT(data, 3);
	*_64kcasen = BIT(data, 4);
	*casenb = BIT(data, 5);
	*viddaten = BIT(data, 6);
	*viddat_tr = BIT(data, 7);
}


//-------------------------------------------------
//  read_pla2 - P500 PLA #2 read
//-------------------------------------------------

void p500_state::read_pla2(offs_t offset, offs_t va, int ba, int vicen, int ae, int segf, int bank0,
	int *clrnibcsb, int *extbufcs, int *discromcs, int *buframcs, int *charomcs, int *procvid, int *viccs, int *vidmatcs)
{
	int sphi2 = m_vic->phi0_r();
	int bcas = 1;

	uint32_t input = VA12 << 15 | ba << 14 | A13 << 13 | A15 << 12 | A14 << 11 | A11 << 10 | A10 << 9 | A12 << 8 |
			sphi2 << 7 | vicen << 6 | m_statvid << 5 | m_vicdotsel << 4 | ae << 3 | segf << 2 | bcas << 1 | bank0;

	uint32_t data = m_pla2->read(input);

	*clrnibcsb = BIT(data, 0);
	*extbufcs = BIT(data, 1);
	*discromcs = BIT(data, 2);
	*buframcs = BIT(data, 3);
	*charomcs = BIT(data, 4);
	*procvid = BIT(data, 5);
	*viccs = BIT(data, 6);
	*vidmatcs = BIT(data, 7);
}


//-------------------------------------------------
//  bankswitch -
//-------------------------------------------------

void p500_state::bankswitch(offs_t offset, offs_t va, int srw, int ba, int ae, int busy2, int refen,
	int *datxen, int *dramxen, int *clrniben, int *_64kcasen, int *casenb, int *viddaten, int *viddat_tr,
	int *clrnibcs, int *extbufcs, int *discromcs, int *buframcs, int *charomcs, int *viccs, int *vidmatcs,
	int *csbank1, int *csbank2, int *csbank3, int *basiclocs, int *basichics, int *kernalcs,
	int *cs1, int *sidcs, int *extprtcs, int *ciacs, int *aciacs, int *tript1cs, int *tript2cs, int *aec, int *vsysaden)
{
	int sphi2 = m_vic->phi0_r();
	int sphi1 = !sphi2;
	//int ba = !m_vic->ba_r();
	//int ae = m_vic->aec_r();
	int bcas = 0;

	*aec = !((m_statvid || ae) && sphi2);
	*vsysaden = sphi1 || ba;

	int clrnibcsb = 1, procvid = 1, segf = 1;

	read_pla1(offset, busy2, clrnibcsb, procvid, refen, ba, *aec, srw,
		datxen, dramxen, clrniben, &segf, _64kcasen, casenb, viddaten, viddat_tr);

	int bank0 = 1, vicen = 1;

	if (!*aec && !segf)
	{
		switch ((offset >> 13) & 0x07)
		{
		case 0: bank0 = 0; break;
		case 1: *csbank1 = 0; break;
		case 2: *csbank2 = 0; break;
		case 3: *csbank3 = 0; break;
		case 4: *basiclocs = 0; break;
		case 5: *basichics = 0; break;
		case 6:
			if (A12 && A11)
			{
				switch ((offset >> 8) & 0x07)
				{
				case 0: vicen = 0; break;
				case 1: *cs1 = 0; break;
				case 2: *sidcs = 0; break;
				case 3: *extprtcs = 0; break;
				case 4: *ciacs = 0; break;
				case 5: *aciacs = 0; break;
				case 6: *tript1cs = 0; break;
				case 7: *tript2cs = 0; break;
				}
			}
			break;

		case 7: *kernalcs = 0; break;
		}
	}

	int vidmatcsb = 1;

	read_pla2(offset, va, ba, vicen, ae, segf, bank0,
		&clrnibcsb, extbufcs, discromcs, buframcs, charomcs, &procvid, viccs, &vidmatcsb);

	*clrnibcs = clrnibcsb || bcas;
	*vidmatcs = vidmatcsb || bcas;

	read_pla1(offset, busy2, clrnibcsb, procvid, refen, ba, *aec, srw,
		datxen, dramxen, clrniben, &segf, _64kcasen, casenb, viddaten, viddat_tr);
}


//-------------------------------------------------
//  read_memory -
//-------------------------------------------------

uint8_t p500_state::read_memory(offs_t offset, offs_t va, int ba, int ae)
{
	int srw = 1, busy2 = 1, refen = 0;

	int datxen = 1, dramxen = 1, clrniben = 1, _64kcasen = 1, casenb = 1, viddaten = 1, viddat_tr = 1;
	int clrnibcs = 1, extbufcs = 1, discromcs = 1, buframcs = 1, charomcs = 1, viccs = 1, vidmatcs = 1;
	int csbank1 = 1, csbank2 = 1, csbank3 = 1, basiclocs = 1, basichics = 1, kernalcs = 1;
	int cs1 = 1, sidcs = 1, extprtcs = 1, ciacs = 1, aciacs = 1, tript1cs = 1, tript2cs = 1;
	int aec = 1, vsysaden = 1;

	bankswitch(offset, va, srw, ba, ae, busy2, refen,
		&datxen, &dramxen, &clrniben, &_64kcasen, &casenb, &viddaten, &viddat_tr,
		&clrnibcs, &extbufcs, &discromcs, &buframcs, &charomcs, &viccs, &vidmatcs,
		&csbank1, &csbank2, &csbank3, &basiclocs, &basichics, &kernalcs,
		&cs1, &sidcs, &extprtcs, &ciacs, &aciacs, &tript1cs, &tript2cs, &aec, &vsysaden);

	uint8_t data = 0xff;

	if (clrniben)
	{
		if (!clrnibcs && !vsysaden)
		{
			data = m_color_ram[offset & 0x3ff];
		}
	}

	if (!dramxen)
	{
		if (casenb)
		{
			switch (offset >> 16)
			{
			case 1: data = m_ram->pointer()[0x10000 + (offset & 0xffff)]; break;
			case 2: if (m_ram->size() > 0x20000) data = m_ram->pointer()[0x20000 + (offset & 0xffff)]; break;
			case 3: if (m_ram->size() > 0x30000) data = m_ram->pointer()[0x30000 + (offset & 0xffff)]; break;
			}
		}
	}

	if (!datxen)
	{
		if (!_64kcasen && !aec)
		{
			data = m_ram->pointer()[offset & 0xffff];
		}
		if (!buframcs)
		{
			data = m_buffer_ram[offset & 0x7ff];
		}
		if (!vidmatcs && !vsysaden && !viddaten && viddat_tr)
		{
			data = m_video_ram[offset & 0x3ff];
		}
		if (!basiclocs || !basichics)
		{
			data = m_basic->base()[offset & 0x3fff];
		}
		if (!kernalcs)
		{
			data = m_kernal->base()[offset & 0x1fff];
		}
		if (!charomcs && !vsysaden && !viddaten && viddat_tr)
		{
			data = m_charom->base()[offset & 0xfff];
		}
		if (!viccs && !viddaten && viddat_tr)
		{
			data = m_vic->read(offset & 0x3f);
		}
		if (!sidcs)
		{
			data = m_sid->read(offset & 0x1f);
		}
		if (!ciacs)
		{
			data = m_cia->read(offset & 0x0f);
		}
		if (!aciacs)
		{
			data = m_acia->read(offset & 0x03);
		}
		if (!tript1cs)
		{
			data = m_tpi1->read(offset & 0x07);
		}
		if (!tript2cs)
		{
			data = m_tpi2->read(offset & 0x07);
		}

		data = m_exp->read(offset & 0x1fff, data, csbank1, csbank2, csbank3);
	}

	return data;
}


//-------------------------------------------------
//  write_memory -
//-------------------------------------------------

void p500_state::write_memory(offs_t offset, uint8_t data, int ba, int ae)
{
	int srw = 0, busy2 = 1, refen = 0;
	offs_t va = 0xffff;

	int datxen = 1, dramxen = 1, clrniben = 1, _64kcasen = 1, casenb = 1, viddaten = 1, viddat_tr = 1;
	int clrnibcs = 1, extbufcs = 1, discromcs = 1, buframcs = 1, charomcs = 1, viccs = 1, vidmatcs = 1;
	int csbank1 = 1, csbank2 = 1, csbank3 = 1, basiclocs = 1, basichics = 1, kernalcs = 1;
	int cs1 = 1, sidcs = 1, extprtcs = 1, ciacs = 1, aciacs = 1, tript1cs = 1, tript2cs = 1;
	int aec = 1, vsysaden = 1;

	bankswitch(offset, va, srw, ba, ae, busy2, refen,
		&datxen, &dramxen, &clrniben, &_64kcasen, &casenb, &viddaten, &viddat_tr,
		&clrnibcs, &extbufcs, &discromcs, &buframcs, &charomcs, &viccs, &vidmatcs,
		&csbank1, &csbank2, &csbank3, &basiclocs, &basichics, &kernalcs,
		&cs1, &sidcs, &extprtcs, &ciacs, &aciacs, &tript1cs, &tript2cs, &aec, &vsysaden);

	if (clrniben)
	{
		if (!clrnibcs && !vsysaden)
		{
			m_color_ram[offset & 0x3ff] = data & 0x0f;
		}
	}

	if (!dramxen)
	{
		if (casenb)
		{
			switch (offset >> 16)
			{
			case 1: m_ram->pointer()[0x10000 + (offset & 0xffff)] = data; break;
			case 2: if (m_ram->size() > 0x20000) m_ram->pointer()[0x20000 + (offset & 0xffff)] = data; break;
			case 3: if (m_ram->size() > 0x30000) m_ram->pointer()[0x30000 + (offset & 0xffff)] = data; break;
			}
		}
	}

	if (!datxen)
	{
		if (!_64kcasen && !aec)
		{
			m_ram->pointer()[offset & 0xffff] = data;
		}
		if (!buframcs)
		{
			m_buffer_ram[offset & 0x7ff] = data;
		}
		if (!vidmatcs && !vsysaden && !viddaten && !viddat_tr)
		{
			m_video_ram[offset & 0x3ff] = data;
		}
		if (!viccs && !viddaten && !viddat_tr)
		{
			m_vic->write(offset & 0x3f, data);
		}
		if (!sidcs)
		{
			m_sid->write(offset & 0x1f, data);
		}
		if (!ciacs)
		{
			m_cia->write(offset & 0x0f, data);
		}
		if (!aciacs)
		{
			m_acia->write(offset & 0x03, data);
		}
		if (!tript1cs)
		{
			m_tpi1->write(offset & 0x07, data);
		}
		if (!tript2cs)
		{
			m_tpi2->write(offset & 0x07, data);
		}

		m_exp->write(offset & 0x1fff, data, csbank1, csbank2, csbank3);
	}
}


//-------------------------------------------------
//  read -
//-------------------------------------------------

uint8_t p500_state::read(offs_t offset)
{
	int ba = 0, ae = 1;
	offs_t va = 0xffff;

	return read_memory(offset, va, ba, ae);
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

void p500_state::write(offs_t offset, uint8_t data)
{
	int ba = 0, ae = 1;

	write_memory(offset, data, ba, ae);
}


//-------------------------------------------------
//  vic_videoram_r -
//-------------------------------------------------

uint8_t p500_state::vic_videoram_r(offs_t offset)
{
	int srw = 1, busy2 = 1, refen = 0;
	int ba = !m_vic->ba_r(), ae = m_vic->aec_r();
	int datxen = 1, dramxen = 1, clrniben = 1, _64kcasen = 1, casenb = 1, viddaten = 1, viddat_tr = 1;
	int clrnibcs = 1, extbufcs = 1, discromcs = 1, buframcs = 1, charomcs = 1, viccs = 1, vidmatcs = 1;
	int csbank1 = 1, csbank2 = 1, csbank3 = 1, basiclocs = 1, basichics = 1, kernalcs = 1;
	int cs1 = 1, sidcs = 1, extprtcs = 1, ciacs = 1, aciacs = 1, tript1cs = 1, tript2cs = 1;
	int aec = 1, vsysaden = 1;

	bankswitch(0, offset, srw, ba, ae, busy2, refen,
		&datxen, &dramxen, &clrniben, &_64kcasen, &casenb, &viddaten, &viddat_tr,
		&clrnibcs, &extbufcs, &discromcs, &buframcs, &charomcs, &viccs, &vidmatcs,
		&csbank1, &csbank2, &csbank3, &basiclocs, &basichics, &kernalcs,
		&cs1, &sidcs, &extprtcs, &ciacs, &aciacs, &tript1cs, &tript2cs, &aec, &vsysaden);

	uint8_t data = 0xff;
//  uint8_t clrnib = 0xf;

	if (vsysaden)
	{
		if (!_64kcasen && !aec && !viddaten && !viddat_tr)
		{
			data = m_ram->pointer()[(m_vicbnksel << 14) | offset];
		}
/*      if (!clrnibcs)
        {
            clrnib = m_color_ram[offset & 0x3ff];
        }*/
		if (!vidmatcs)
		{
			data = m_video_ram[offset & 0x3ff];
		}
		if (!charomcs)
		{
			data = m_charom->base()[offset & 0xfff];
		}
	}

	return data;
}


//-------------------------------------------------
//  vic_videoram_r -
//-------------------------------------------------

uint8_t p500_state::vic_colorram_r(offs_t offset)
{
	int srw = 1, busy2 = 1, refen = 0;
	int ba = !m_vic->ba_r(), ae = m_vic->aec_r();
	int datxen = 1, dramxen = 1, clrniben = 1, _64kcasen = 1, casenb = 1, viddaten = 1, viddat_tr = 1;
	int clrnibcs = 1, extbufcs = 1, discromcs = 1, buframcs = 1, charomcs = 1, viccs = 1, vidmatcs = 1;
	int csbank1 = 1, csbank2 = 1, csbank3 = 1, basiclocs = 1, basichics = 1, kernalcs = 1;
	int cs1 = 1, sidcs = 1, extprtcs = 1, ciacs = 1, aciacs = 1, tript1cs = 1, tript2cs = 1;
	int aec = 1, vsysaden = 1;

	bankswitch(0, offset, srw, ba, ae, busy2, refen,
		&datxen, &dramxen, &clrniben, &_64kcasen, &casenb, &viddaten, &viddat_tr,
		&clrnibcs, &extbufcs, &discromcs, &buframcs, &charomcs, &viccs, &vidmatcs,
		&csbank1, &csbank2, &csbank3, &basiclocs, &basichics, &kernalcs,
		&cs1, &sidcs, &extprtcs, &ciacs, &aciacs, &tript1cs, &tript2cs, &aec, &vsysaden);

	uint8_t data = 0x0f;

	if (!clrnibcs)
	{
		data = m_color_ram[offset & 0x3ff];
	}

	return data;
}



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

//-------------------------------------------------
//  ADDRESS_MAP( cbm2_mem )
//-------------------------------------------------

void cbm2_state::cbm2_mem(address_map &map)
{
	map(0x00000, 0xfffff).rw(FUNC(cbm2_state::read), FUNC(cbm2_state::write));
}


//-------------------------------------------------
//  ADDRESS_MAP( ext_mem )
//-------------------------------------------------

void cbm2_state::ext_mem(address_map &map)
{
	map(0x00000, 0xeffff).r(FUNC(cbm2_state::ext_read)).w(FUNC(cbm2_state::ext_write));
	map(0xf0000, 0xf0fff).mirror(0xf000).rom().region(EXT_I8088_TAG, 0);
}


//-------------------------------------------------
//  ADDRESS_MAP( ext_io )
//-------------------------------------------------

void cbm2_state::ext_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x0000, 0x0001).mirror(0x1e).rw(m_ext_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x0020, 0x0027).mirror(0x18).rw(EXT_MOS6525_TAG, FUNC(tpi6525_device::read), FUNC(tpi6525_device::write));
}


//-------------------------------------------------
//  ADDRESS_MAP( p500_mem )
//-------------------------------------------------

void p500_state::p500_mem(address_map &map)
{
	map(0x00000, 0xfffff).rw(FUNC(p500_state::read), FUNC(p500_state::write));
}


//-------------------------------------------------
//  ADDRESS_MAP( vic_videoram_map )
//-------------------------------------------------

void p500_state::vic_videoram_map(address_map &map)
{
	map(0x0000, 0x3fff).r(FUNC(p500_state::vic_videoram_r));
}


//-------------------------------------------------
//  ADDRESS_MAP( vic_colorram_map )
//-------------------------------------------------

void p500_state::vic_colorram_map(address_map &map)
{
	map(0x000, 0x3ff).r(FUNC(p500_state::vic_colorram_r));
}



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

//-------------------------------------------------
//  INPUT_PORTS( cbm2 )
//-------------------------------------------------

static INPUT_PORTS_START( cbm2 )
	PORT_START("PB0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("TAB") PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PB1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PB2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PB3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PB4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F5") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PB5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F6") PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PB6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F7") PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PB7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F8") PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F9") PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F10") PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT" \xC2\xA3") PORT_CODE(KEYCODE_TILDE) PORT_CHAR(UCHAR_MAMEKEY(TILDE)) PORT_CHAR(U'£')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR('\r')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR(U'π')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INS/DEL") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C=") PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CLR/HOME") PORT_CODE(KEYCODE_HOME)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad ?")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 7") PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 4") PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 1") PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 0") PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("OFF/RVS") PORT_CODE(KEYCODE_END)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad CE")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 8") PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 5") PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 2") PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad .") PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("NORM/GRAPH") PORT_CODE(KEYCODE_PGUP)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad *") PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(ASTERISK))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 9") PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 6") PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 3") PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 00") PORT_CODE(KEYCODE_00_PAD) PORT_CHAR(UCHAR_MAMEKEY(00_PAD))
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RUN/STOP") PORT_CODE(KEYCODE_PGDN)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad /") PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad -") PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad +") PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad ENTER") PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("LOCK")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_TOGGLE
	PORT_BIT( 0xef, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( cbm2_de )
//-------------------------------------------------

static INPUT_PORTS_START( cbm2_de )
	PORT_INCLUDE(cbm2)
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( cbm2_hu )
//-------------------------------------------------

static INPUT_PORTS_START( cbm2_hu )
	PORT_INCLUDE(cbm2)
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( cbm2_se )
//------------------------------------------------

static INPUT_PORTS_START( cbm2_se )
	PORT_INCLUDE(cbm2)

	PORT_MODIFY("PA0")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(U'ö') PORT_CHAR(U'Ö')

	PORT_MODIFY("PA1")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(U'å') PORT_CHAR(U'Å')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(U'ä') PORT_CHAR(U'Ä')

	PORT_MODIFY("PA2")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT" \xCF\x80") PORT_CODE(KEYCODE_TILDE) PORT_CHAR(UCHAR_MAMEKEY(TILDE)) PORT_CHAR(U'π')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR(';') PORT_CHAR(':')
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  mc6845
//-------------------------------------------------

MC6845_UPDATE_ROW( cbm2_state::crtc_update_row )
{
	pen_t const *const pen = m_palette->pens();

	int x = 0;

	for (int column = 0; column < x_count; column++)
	{
		uint8_t code = m_video_ram[(ma + column) & 0x7ff];
		offs_t char_rom_addr = (ma & 0x1000) | (m_graphics << 11) | ((code & 0x7f) << 4) | (ra & 0x0f);
		uint8_t data = m_charom->base()[char_rom_addr & 0xfff];

		for (int bit = 0; bit < 9; bit++)
		{
			int color = BIT(data, 7) ^ BIT(code, 7) ^ BIT(ma, 13);
			if (cursor_x == column) color ^= 1;
			color &= de;

			bitmap.pix(vbp + y, hbp + x++) = pen[color];

			if (bit < 8 || !m_graphics) data <<= 1;
		}
	}
}


//-------------------------------------------------
//  MOS6581_INTERFACE( sid_intf )
//-------------------------------------------------

uint8_t cbm2_state::sid_potx_r()
{
	uint8_t data = 0xff;

	switch (m_cia_pa >> 6)
	{
	case 1: data = m_joy1->read_pot_x(); break;
	case 2: data = m_joy2->read_pot_x(); break;
	case 3:
		if (m_joy1->has_pot_x() && m_joy2->has_pot_x())
		{
			data = 1 / (1 / m_joy1->read_pot_x() + 1 / m_joy2->read_pot_x());
		}
		else if (m_joy1->has_pot_x())
		{
			data = m_joy1->read_pot_x();
		}
		else if (m_joy2->has_pot_x())
		{
			data = m_joy2->read_pot_x();
		}
		break;
	}

	return data;
}

uint8_t cbm2_state::sid_poty_r()
{
	uint8_t data = 0xff;

	switch (m_cia_pa >> 6)
	{
	case 1: data = m_joy1->read_pot_y(); break;
	case 2: data = m_joy2->read_pot_y(); break;
	case 3:
		if (m_joy1->has_pot_y() && m_joy2->has_pot_y())
		{
			data = 1 / (1 / m_joy1->read_pot_y() + 1 / m_joy2->read_pot_y());
		}
		else if (m_joy1->has_pot_y())
		{
			data = m_joy1->read_pot_y();
		}
		else if (m_joy2->has_pot_y())
		{
			data = m_joy2->read_pot_y();
		}
		break;
	}

	return data;
}


//-------------------------------------------------
//  tpi6525_interface tpi1_intf
//-------------------------------------------------

uint8_t cbm2_state::tpi1_pa_r()
{
	/*

	    bit     description

	    0       0
	    1       0
	    2       REN
	    3       ATN
	    4       DAV
	    5       EOI
	    6       NDAC
	    7       NRFD

	*/

	uint8_t data = 0;

	// IEEE-488
	data |= m_ieee2->ren_r() << 2;
	data |= m_ieee2->atn_r() << 3;
	data |= m_ieee2->dav_r() << 4;
	data |= m_ieee2->eoi_r() << 5;
	data |= m_ieee2->ndac_r() << 6;
	data |= m_ieee2->nrfd_r() << 7;

	return data;
}

void cbm2_state::tpi1_pa_w(uint8_t data)
{
	/*

	    bit     description

	    0       75161A DC
	    1       75161A TE
	    2       REN
	    3       ATN
	    4       DAV
	    5       EOI
	    6       NDAC
	    7       NRFD

	*/

	// IEEE-488
	m_ieee2->dc_w(BIT(data, 0));

	m_ieee1->te_w(BIT(data, 1));
	m_ieee2->te_w(BIT(data, 1));

	m_ieee2->ren_w(BIT(data, 2));
	m_ieee2->atn_w(BIT(data, 3));
	m_ieee2->dav_w(BIT(data, 4));
	m_ieee2->eoi_w(BIT(data, 5));
	m_ieee2->ndac_w(BIT(data, 6));
	m_ieee2->nrfd_w(BIT(data, 7));
}

uint8_t cbm2_state::tpi1_pb_r()
{
	/*

	    bit     description

	    0       IFC
	    1       SRQ
	    2       user port PB2
	    3       user port PB3
	    4
	    5
	    6
	    7       CASS SW

	*/

	uint8_t data = 0;

	// IEEE-488
	data |= m_ieee2->ifc_r();
	data |= m_ieee2->srq_r() << 1;

	// user port
	data |= m_user->pb2_r() << 2;
	data |= m_user->pb3_r() << 3;

	// cassette
	data |= m_cassette->sense_r() << 7;

	return data;
}

void cbm2_state::tpi1_pb_w(uint8_t data)
{
	/*

	    bit     description

	    0       IFC
	    1       SRQ
	    2       user port PB2
	    3       user port PB3
	    4       DRAMON
	    5       CASS WRT
	    6       CASS MTR
	    7

	*/

	// IEEE-488
	m_ieee2->ifc_w(BIT(data, 0));
	m_ieee2->srq_w(BIT(data, 1));

	// user port
	m_user->pb2_w(BIT(data, 2));
	m_user->pb3_w(BIT(data, 3));

	// memory
	m_dramon = BIT(data, 4);
	if (m_busy2) m_busen1 = m_dramon;

	// cassette
	m_cassette->write(BIT(data, 5));
	m_cassette->motor_w(BIT(data, 6));
}

void cbm2_state::tpi1_ca_w(int state)
{
	m_graphics = state;
}

void p500_state::tpi1_ca_w(int state)
{
	m_statvid = state;
}

void p500_state::tpi1_cb_w(int state)
{
	m_vicdotsel = state;
}

//-------------------------------------------------
//  tpi6525_interface tpi2_intf
//-------------------------------------------------

uint8_t cbm2_state::read_keyboard()
{
	uint8_t data = 0xff;

	if (!BIT(m_tpi2_pa, 0)) data &= m_pa[0]->read();
	if (!BIT(m_tpi2_pa, 1)) data &= m_pa[1]->read();
	if (!BIT(m_tpi2_pa, 2)) data &= m_pa[2]->read();
	if (!BIT(m_tpi2_pa, 3)) data &= m_pa[3]->read();
	if (!BIT(m_tpi2_pa, 4)) data &= m_pa[4]->read();
	if (!BIT(m_tpi2_pa, 5)) data &= m_pa[5]->read();
	if (!BIT(m_tpi2_pa, 6)) data &= m_pa[6]->read();
	if (!BIT(m_tpi2_pa, 7)) data &= m_pa[7]->read();
	if (!BIT(m_tpi2_pb, 0)) data &= m_pb[0]->read() & m_lock->read();
	if (!BIT(m_tpi2_pb, 1)) data &= m_pb[1]->read();
	if (!BIT(m_tpi2_pb, 2)) data &= m_pb[2]->read();
	if (!BIT(m_tpi2_pb, 3)) data &= m_pb[3]->read();
	if (!BIT(m_tpi2_pb, 4)) data &= m_pb[4]->read();
	if (!BIT(m_tpi2_pb, 5)) data &= m_pb[5]->read();
	if (!BIT(m_tpi2_pb, 6)) data &= m_pb[6]->read();
	if (!BIT(m_tpi2_pb, 7)) data &= m_pb[7]->read();

	return data;
}

void cbm2_state::tpi2_pa_w(uint8_t data)
{
	m_tpi2_pa = data;
}

void cbm2_state::tpi2_pb_w(uint8_t data)
{
	m_tpi2_pb = data;
}

uint8_t cbm2_state::tpi2_pc_r()
{
	/*

	    bit     description

	    0       COLUMN 0
	    1       COLUMN 1
	    2       COLUMN 2
	    3       COLUMN 3
	    4       COLUMN 4
	    5       COLUMN 5
	    6       0=PAL, 1=NTSC
	    7       0

	*/

	return (m_ntsc << 6) | (read_keyboard() & 0x3f);
}

uint8_t cbm2hp_state::tpi2_pc_r()
{
	/*

	    bit     description

	    0       COLUMN 0
	    1       COLUMN 1
	    2       COLUMN 2
	    3       COLUMN 3
	    4       COLUMN 4
	    5       COLUMN 5
	    6       1
	    7       1

	*/

	return read_keyboard();
}

uint8_t p500_state::tpi2_pc_r()
{
	/*

	    bit     description

	    0       COLUMN 0
	    1       COLUMN 1
	    2       COLUMN 2
	    3       COLUMN 3
	    4       COLUMN 4
	    5       COLUMN 5
	    6       0
	    7       0

	*/

	return read_keyboard();
}

void p500_state::tpi2_pc_w(uint8_t data)
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4
	    5
	    6       VICBNKSEL0
	    7       VICBNKSEL1

	*/

	m_vicbnksel = data >> 6;
}

//-------------------------------------------------
//  MOS6526_INTERFACE( cia_intf )
//-------------------------------------------------

uint8_t cbm2_state::cia_pa_r()
{
	/*

	    bit     description

	    0       IEEE-488 D0, user port 1D0
	    1       IEEE-488 D1, user port 1D1
	    2       IEEE-488 D2, user port 1D2
	    3       IEEE-488 D3, user port 1D3
	    4       IEEE-488 D4, user port 1D4
	    5       IEEE-488 D5, user port 1D5
	    6       IEEE-488 D6, user port 1D6, LTPN
	    7       IEEE-488 D7, user port 1D7, GAME TRIGGER 24

	*/

	uint8_t data = 0;

	// IEEE-488
	data |= m_ieee1->read();

	// user port
	data &= m_user->d1_r();

	// joystick
	data &= ~(!BIT(m_joy1->read_joy(), 5) << 6);
	data &= ~(!BIT(m_joy2->read_joy(), 5) << 7);

	return data;
}

void cbm2_state::cia_pa_w(uint8_t data)
{
	/*

	    bit     description

	    0       IEEE-488 D0, user port 1D0
	    1       IEEE-488 D1, user port 1D1
	    2       IEEE-488 D2, user port 1D2
	    3       IEEE-488 D3, user port 1D3
	    4       IEEE-488 D4, user port 1D4
	    5       IEEE-488 D5, user port 1D5
	    6       IEEE-488 D6, user port 1D6
	    7       IEEE-488 D7, user port 1D7

	*/

	// IEEE-488
	m_ieee1->write(data);

	// user port
	m_user->d1_w(data);

	// joystick
	m_cia_pa = data;
}

uint8_t cbm2_state::cia_pb_r()
{
	/*

	    bit     description

	    0       user port 2D0, GAME10
	    1       user port 2D1, GAME11
	    2       user port 2D2, GAME12
	    3       user port 2D3, GAME13
	    4       user port 2D4, GAME20
	    5       user port 2D5, GAME21
	    6       user port 2D6, GAME22
	    7       user port 2D7, GAME23

	*/

	uint8_t data = 0;

	// joystick
	data |= m_joy1->read_joy() & 0x0f;
	data |= (m_joy2->read_joy() & 0x0f) << 4;

	// user port
	data &= m_user->d2_r();

	return data;
}


//-------------------------------------------------
//  tpi6525_interface ext_tpi_intf
//-------------------------------------------------

void cbm2_state::set_busy2(int state)
{
	m_busy2 = state;

	if (m_busy2)
	{
		//m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);

		m_busen1 = m_dramon;
	}
	else
	{
		//m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);

		m_busen1 = 0;
	}
}

uint8_t cbm2_state::ext_tpi_pb_r()
{
	/*

	    bit     description

	    0       _BUSY1
	    1       _BUSY2
	    2       _REQ
	    3       _ACK
	    4       DATA/_CMD
	    5       DIR
	    6       1
	    7       1

	*/

	uint8_t data = 0xc0;

	// _BUSY1
	data |= !m_busen1;

	// _BUSY2
	data |= m_busy2 << 1;

	// CIA
	data |= m_ext_tpi_pb & m_ext_cia_pb & 0x3c;

	return data;
}

void cbm2_state::ext_tpi_pb_w(uint8_t data)
{
	/*

	    bit     description

	    0
	    1       _BUSY2
	    2
	    3
	    4
	    5
	    6       CIA FLAG
	    7

	*/

	m_ext_tpi_pb = data;

	// _BUSY2
	if (!BIT(data, 1))
	{
		set_busy2(0);
	}

	// FLAG
	m_ext_cia->flag_w(BIT(data, 6));
}

void cbm2_state::ext_tpi_pc_w(uint8_t data)
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4
	    5       BSYCLK
	    6
	    7

	*/

	// _BUSY2
	if (BIT(data, 5))
	{
		set_busy2(1);
	}
}

//-------------------------------------------------
//  MOS6526_INTERFACE( ext_cia_intf )
//-------------------------------------------------

void cbm2_state::ext_cia_irq_w(int state)
{
	m_tpi1->i3_w(!state);
}

uint8_t cbm2_state::ext_cia_pb_r()
{
	/*

	    bit     description

	    0       _BUSY1
	    1       _BUSY2
	    2       _REQ
	    3       _ACK
	    4       DATA/_CMD
	    5       DIR
	    6       1
	    7       1

	*/

	uint8_t data = 0xc0;

	// _BUSY1
	data |= !m_busen1;

	// _BUSY2
	data |= m_busy2 << 1;

	// TPI
	data |= m_ext_tpi_pb & m_ext_cia_pb & 0x3c;

	return data;
}

void cbm2_state::ext_cia_pb_w(uint8_t data)
{
	/*

	    bit     description

	    0
	    1       _BUSY2
	    2
	    3
	    4
	    5
	    6       _INT1
	    7       _INT2

	*/

	m_ext_cia_pb = data;

	// _BUSY2
	if (!BIT(data, 1))
	{
		set_busy2(0);
	}

	if (!BIT(data, 6))
	{
		set_busy2(0);
	}

	m_ext_pic->ir0_w(!BIT(data, 6));
	m_ext_pic->ir7_w(BIT(data, 7));
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  tod_tick - advance the TOD clock
//-------------------------------------------------

TIMER_CALLBACK_MEMBER(cbm2_state::tod_tick)
{
	m_tpi1->i0_w(m_todclk);

	if (m_ext_pic) m_ext_pic->ir2_w(m_todclk);

	m_todclk = !m_todclk;
}


//-------------------------------------------------
//  MACHINE_START( cbm2 )
//-------------------------------------------------

MACHINE_START_MEMBER( cbm2_state, cbm2 )
{
	// allocate timer
	int todclk = (m_ntsc ? 60 : 50) * 2;

	m_todclk_timer = timer_alloc(FUNC(cbm2_state::tod_tick), this);
	m_todclk_timer->adjust(attotime::from_hz(todclk), 0, attotime::from_hz(todclk));

	// state saving
	save_item(NAME(m_dramon));
	save_item(NAME(m_busen1));
	save_item(NAME(m_busy2));
	save_item(NAME(m_graphics));
	save_item(NAME(m_ntsc));
	save_item(NAME(m_todclk));
	save_item(NAME(m_tpi2_pa));
	save_item(NAME(m_tpi2_pb));
	save_item(NAME(m_cia_pa));
}


//-------------------------------------------------
//  MACHINE_START( cbm2_ntsc )
//-------------------------------------------------

MACHINE_START_MEMBER( cbm2_state, cbm2_ntsc )
{
	m_ntsc = 1;

	MACHINE_START_CALL_MEMBER(cbm2);
}


//-------------------------------------------------
//  MACHINE_START( cbm2_pal )
//-------------------------------------------------

MACHINE_START_MEMBER( cbm2_state, cbm2_pal )
{
	m_ntsc = 0;

	MACHINE_START_CALL_MEMBER(cbm2);
}


//-------------------------------------------------
//  MACHINE_START( cbm2x_ntsc )
//-------------------------------------------------

MACHINE_START_MEMBER( cbm2_state, cbm2x_ntsc )
{
	MACHINE_START_CALL_MEMBER(cbm2_ntsc);
}


//-------------------------------------------------
//  MACHINE_START( cbm2x_pal )
//-------------------------------------------------

MACHINE_START_MEMBER( cbm2_state, cbm2x_pal )
{
	MACHINE_START_CALL_MEMBER(cbm2_pal);
}


//-------------------------------------------------
//  MACHINE_START( p500 )
//-------------------------------------------------

MACHINE_START_MEMBER( p500_state, p500 )
{
	m_video_ram_size = 0x400;

	MACHINE_START_CALL_MEMBER(cbm2);

	// state saving
	save_item(NAME(m_statvid));
	save_item(NAME(m_vicdotsel));
	save_item(NAME(m_vicbnksel));
}


//-------------------------------------------------
//  MACHINE_START( p500_ntsc )
//-------------------------------------------------

MACHINE_START_MEMBER( p500_state, p500_ntsc )
{
	m_ntsc = 1;

	MACHINE_START_CALL_MEMBER(p500);
}


//-------------------------------------------------
//  MACHINE_START( p500_pal )
//-------------------------------------------------

MACHINE_START_MEMBER( p500_state, p500_pal )
{
	m_ntsc = 0;

	MACHINE_START_CALL_MEMBER(p500);
}


MACHINE_RESET_MEMBER( cbm2_state, cbm2 )
{
	m_dramon = 1;
	m_busen1 = 1;
	m_busy2 = 1;
	m_graphics = 1;

m_ext_tpi_pb = 0xff;
m_ext_cia_pb = 0xff;

	m_maincpu->reset();

	if (m_crtc) m_crtc->reset();
	m_sid->reset();
	m_tpi1->reset();
	m_tpi2->reset();
	m_acia->reset();
	m_cia->reset();

	m_ieee->reset();
}


MACHINE_RESET_MEMBER( p500_state, p500 )
{
	MACHINE_RESET_CALL_MEMBER(cbm2);

	m_vic->reset();

	m_statvid = 1;
	m_vicdotsel = 1;
	m_vicbnksel = 0x03;
}



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

//-------------------------------------------------
//  machine_config( 128k )
//-------------------------------------------------

void cbm2_state::_128k(machine_config &config)
{
	RAM(config, RAM_TAG).set_default_size("128K").set_extra_options("256K");
}


//-------------------------------------------------
//  machine_config( 256k )
//-------------------------------------------------

void cbm2_state::_256k(machine_config &config)
{
	RAM(config, RAM_TAG).set_default_size("256K");
}


//-------------------------------------------------
//  machine_config( p500_ntsc )
//-------------------------------------------------

void p500_state::p500_ntsc(machine_config &config)
{
	MCFG_MACHINE_START_OVERRIDE(p500_state, p500_ntsc)
	MCFG_MACHINE_RESET_OVERRIDE(p500_state, p500)

	// basic hardware
	M6509(config, m_maincpu, XTAL(14'318'181)/14);
	m_maincpu->set_addrmap(AS_PROGRAM, &p500_state::p500_mem);
	config.set_perfect_quantum(m_maincpu);

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, m6509_device::IRQ_LINE);

	// video hardware
	mos6567_device &mos6567(MOS6567(config, MOS6567_TAG, XTAL(14'318'181)/14));
	mos6567.set_cpu(m_maincpu);
	mos6567.irq_callback().set("mainirq", FUNC(input_merger_device::in_w<0>));
	mos6567.set_screen(SCREEN_TAG);
	mos6567.set_addrmap(0, &p500_state::vic_videoram_map);
	mos6567.set_addrmap(1, &p500_state::vic_colorram_map);

	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(VIC6567_VRETRACERATE);
	screen.set_size(VIC6567_COLUMNS, VIC6567_LINES);
	screen.set_visarea(0, VIC6567_VISIBLECOLUMNS - 1, 0, VIC6567_VISIBLELINES - 1);
	screen.set_screen_update(MOS6567_TAG, FUNC(mos6567_device::screen_update));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	MOS6581(config, m_sid, XTAL(14'318'181)/14);
	m_sid->potx().set(FUNC(p500_state::sid_potx_r));
	m_sid->poty().set(FUNC(p500_state::sid_poty_r));
	m_sid->add_route(ALL_OUTPUTS, "mono", 1.00);

	// devices
	PLS100(config, m_pla1);
	PLS100(config, m_pla2);

	TPI6525(config, m_tpi1, 0);
	m_tpi1->out_irq_cb().set("mainirq", FUNC(input_merger_device::in_w<0>));
	m_tpi1->in_pa_cb().set(FUNC(cbm2_state::tpi1_pa_r));
	m_tpi1->out_pa_cb().set(FUNC(cbm2_state::tpi1_pa_w));
	m_tpi1->in_pb_cb().set(FUNC(cbm2_state::tpi1_pb_r));
	m_tpi1->out_pa_cb().set(FUNC(cbm2_state::tpi1_pb_w));
	m_tpi1->out_ca_cb().set(FUNC(p500_state::tpi1_ca_w));
	m_tpi1->out_cb_cb().set(FUNC(p500_state::tpi1_cb_w));

	TPI6525(config, m_tpi2, 0);
	m_tpi2->out_pa_cb().set(FUNC(cbm2_state::tpi2_pa_w));
	m_tpi2->out_pb_cb().set(FUNC(cbm2_state::tpi2_pb_w));
	m_tpi2->in_pc_cb().set(FUNC(p500_state::tpi2_pc_r));
	m_tpi2->out_pc_cb().set(FUNC(p500_state::tpi2_pc_w));

	MOS6551(config, m_acia, VIC6567_CLOCK);
	m_acia->set_xtal(XTAL(1'843'200));
	m_acia->irq_handler().set(m_tpi1, FUNC(tpi6525_device::i4_w));
	m_acia->txd_handler().set(RS232_TAG, FUNC(rs232_port_device::write_txd));
	m_acia->dtr_handler().set(RS232_TAG, FUNC(rs232_port_device::write_dtr));
	m_acia->rts_handler().set(RS232_TAG, FUNC(rs232_port_device::write_rts));
	m_acia->rxc_handler().set(RS232_TAG, FUNC(rs232_port_device::write_etc));

	MOS6526A(config, m_cia, XTAL(14'318'181)/14);
	m_cia->set_tod_clock(60);
	m_cia->irq_wr_callback().set(m_tpi1, FUNC(tpi6525_device::i2_w));
	m_cia->cnt_wr_callback().set(m_user, FUNC(cbm2_user_port_device::cnt_w));
	m_cia->sp_wr_callback().set(m_user, FUNC(cbm2_user_port_device::sp_w));
	m_cia->pa_rd_callback().set(FUNC(cbm2_state::cia_pa_r));
	m_cia->pa_wr_callback().set(FUNC(cbm2_state::cia_pa_w));
	m_cia->pb_rd_callback().set(FUNC(cbm2_state::cia_pb_r));
	m_cia->pb_wr_callback().set(m_user, FUNC(cbm2_user_port_device::d2_w));
	m_cia->pc_wr_callback().set(m_user, FUNC(cbm2_user_port_device::pc_w));

	DS75160A(config, m_ieee1, 0);
	m_ieee1->read_callback().set(IEEE488_TAG, FUNC(ieee488_device::dio_r));
	m_ieee1->write_callback().set(IEEE488_TAG, FUNC(ieee488_device::host_dio_w));

	DS75161A(config, m_ieee2, 0);
	m_ieee2->in_ren().set(IEEE488_TAG, FUNC(ieee488_device::ren_r));
	m_ieee2->in_ifc().set(IEEE488_TAG, FUNC(ieee488_device::ifc_r));
	m_ieee2->in_ndac().set(IEEE488_TAG, FUNC(ieee488_device::ndac_r));
	m_ieee2->in_nrfd().set(IEEE488_TAG, FUNC(ieee488_device::nrfd_r));
	m_ieee2->in_dav().set(IEEE488_TAG, FUNC(ieee488_device::dav_r));
	m_ieee2->in_eoi().set(IEEE488_TAG, FUNC(ieee488_device::eoi_r));
	m_ieee2->in_atn().set(IEEE488_TAG, FUNC(ieee488_device::atn_r));
	m_ieee2->in_srq().set(IEEE488_TAG, FUNC(ieee488_device::srq_r));
	m_ieee2->out_ren().set(IEEE488_TAG, FUNC(ieee488_device::host_ren_w));
	m_ieee2->out_ifc().set(IEEE488_TAG, FUNC(ieee488_device::host_ifc_w));
	m_ieee2->out_ndac().set(IEEE488_TAG, FUNC(ieee488_device::host_ndac_w));
	m_ieee2->out_nrfd().set(IEEE488_TAG, FUNC(ieee488_device::host_nrfd_w));
	m_ieee2->out_dav().set(IEEE488_TAG, FUNC(ieee488_device::host_dav_w));
	m_ieee2->out_eoi().set(IEEE488_TAG, FUNC(ieee488_device::host_eoi_w));
	m_ieee2->out_atn().set(IEEE488_TAG, FUNC(ieee488_device::host_atn_w));
	m_ieee2->out_srq().set(IEEE488_TAG, FUNC(ieee488_device::host_srq_w));

	IEEE488(config, m_ieee, 0);
	ieee488_slot_device::add_cbm_defaults(config, "c8050");
	m_ieee->srq_callback().set(m_tpi1, FUNC(tpi6525_device::i1_w));

	PET_DATASSETTE_PORT(config, m_cassette, cbm_datassette_devices, nullptr);
	m_cassette->read_handler().set(m_cia, FUNC(mos6526_device::flag_w));

	VCS_CONTROL_PORT(config, m_joy1, vcs_control_port_devices, nullptr);
	m_joy1->trigger_wr_callback().set(MOS6567_TAG, FUNC(mos6567_device::lp_w));
	VCS_CONTROL_PORT(config, m_joy2, vcs_control_port_devices, nullptr);

	CBM2_EXPANSION_SLOT(config, m_exp, XTAL(14'318'181)/14, cbm2_expansion_cards, nullptr);

	CBM2_USER_PORT(config, m_user, cbm2_user_port_cards, nullptr);
	m_user->irq_callback().set("mainirq", FUNC(input_merger_device::in_w<1>));
	m_user->sp_callback().set(MOS6526_TAG, FUNC(mos6526_device::sp_w));
	m_user->cnt_callback().set(MOS6526_TAG, FUNC(mos6526_device::cnt_w));
	m_user->flag_callback().set(MOS6526_TAG, FUNC(mos6526_device::flag_w));

	rs232_port_device &rs232(RS232_PORT(config, RS232_TAG, default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_acia, FUNC(mos6551_device::write_rxd));
	rs232.dcd_handler().set(m_acia, FUNC(mos6551_device::write_dcd));
	rs232.dsr_handler().set(m_acia, FUNC(mos6551_device::write_dsr));
	rs232.cts_handler().set(m_acia, FUNC(mos6551_device::write_cts));

	QUICKLOAD(config, "quickload", "p00,prg", CBM_QUICKLOAD_DELAY).set_load_callback(FUNC(p500_state::quickload_p500));

	// internal ram
	_128k(config);

	// software list
	SOFTWARE_LIST(config, "cart_list").set_original("cbm2_cart").set_filter("NTSC");
	SOFTWARE_LIST(config, "flop_list").set_original("p500_flop").set_filter("NTSC");
}


//-------------------------------------------------
//  machine_config( p500_pal )
//-------------------------------------------------

void p500_state::p500_pal(machine_config &config)
{
	MCFG_MACHINE_START_OVERRIDE(p500_state, p500_pal)
	MCFG_MACHINE_RESET_OVERRIDE(p500_state, p500)

	// basic hardware
	M6509(config, m_maincpu, XTAL(17'734'472)/18);
	m_maincpu->set_addrmap(AS_PROGRAM, &p500_state::p500_mem);
	config.set_perfect_quantum(m_maincpu);

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, m6509_device::IRQ_LINE);

	// video hardware
	mos6569_device &mos6569(MOS6569(config, MOS6569_TAG, XTAL(17'734'472)/18));
	mos6569.set_cpu(m_maincpu);
	mos6569.irq_callback().set("mainirq", FUNC(input_merger_device::in_w<0>));
	mos6569.set_screen(SCREEN_TAG);
	mos6569.set_addrmap(0, &p500_state::vic_videoram_map);
	mos6569.set_addrmap(1, &p500_state::vic_colorram_map);

	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(VIC6569_VRETRACERATE);
	screen.set_size(VIC6569_COLUMNS, VIC6569_LINES);
	screen.set_visarea(0, VIC6569_VISIBLECOLUMNS - 1, 0, VIC6569_VISIBLELINES - 1);
	screen.set_screen_update(MOS6569_TAG, FUNC(mos6569_device::screen_update));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	MOS6581(config, m_sid, XTAL(17'734'472)/18);
	m_sid->potx().set(FUNC(p500_state::sid_potx_r));
	m_sid->poty().set(FUNC(p500_state::sid_poty_r));
	m_sid->add_route(ALL_OUTPUTS, "mono", 1.00);

	// devices
	PLS100(config, m_pla1);
	PLS100(config, m_pla2);

	TPI6525(config, m_tpi1, 0);
	m_tpi1->out_irq_cb().set("mainirq", FUNC(input_merger_device::in_w<1>));
	m_tpi1->in_pa_cb().set(FUNC(cbm2_state::tpi1_pa_r));
	m_tpi1->out_pa_cb().set(FUNC(cbm2_state::tpi1_pa_w));
	m_tpi1->in_pb_cb().set(FUNC(cbm2_state::tpi1_pb_r));
	m_tpi1->out_pa_cb().set(FUNC(cbm2_state::tpi1_pb_w));
	m_tpi1->out_ca_cb().set(FUNC(p500_state::tpi1_ca_w));
	m_tpi1->out_cb_cb().set(FUNC(p500_state::tpi1_cb_w));

	TPI6525(config, m_tpi2, 0);
	m_tpi2->out_pa_cb().set(FUNC(cbm2_state::tpi2_pa_w));
	m_tpi2->out_pb_cb().set(FUNC(cbm2_state::tpi2_pb_w));
	m_tpi2->in_pc_cb().set(FUNC(p500_state::tpi2_pc_r));
	m_tpi2->out_pc_cb().set(FUNC(p500_state::tpi2_pc_w));

	MOS6551(config, m_acia, 0);
	m_acia->set_xtal(XTAL(1'843'200));
	m_acia->irq_handler().set(m_tpi1, FUNC(tpi6525_device::i4_w));
	m_acia->txd_handler().set(RS232_TAG, FUNC(rs232_port_device::write_txd));

	MOS6526A(config, m_cia, XTAL(17'734'472)/18);
	m_cia->set_tod_clock(50);
	m_cia->irq_wr_callback().set(m_tpi1, FUNC(tpi6525_device::i2_w));
	m_cia->cnt_wr_callback().set(m_user, FUNC(cbm2_user_port_device::cnt_w));
	m_cia->sp_wr_callback().set(m_user, FUNC(cbm2_user_port_device::sp_w));
	m_cia->pa_rd_callback().set(FUNC(cbm2_state::cia_pa_r));
	m_cia->pa_wr_callback().set(FUNC(cbm2_state::cia_pa_w));
	m_cia->pb_rd_callback().set(FUNC(cbm2_state::cia_pb_r));
	m_cia->pb_wr_callback().set(m_user, FUNC(cbm2_user_port_device::d2_w));
	m_cia->pc_wr_callback().set(m_user, FUNC(cbm2_user_port_device::pc_w));

	DS75160A(config, m_ieee1, 0);
	m_ieee1->read_callback().set(IEEE488_TAG, FUNC(ieee488_device::dio_r));
	m_ieee1->write_callback().set(IEEE488_TAG, FUNC(ieee488_device::host_dio_w));

	DS75161A(config, m_ieee2, 0);
	m_ieee2->in_ren().set(IEEE488_TAG, FUNC(ieee488_device::ren_r));
	m_ieee2->in_ifc().set(IEEE488_TAG, FUNC(ieee488_device::ifc_r));
	m_ieee2->in_ndac().set(IEEE488_TAG, FUNC(ieee488_device::ndac_r));
	m_ieee2->in_nrfd().set(IEEE488_TAG, FUNC(ieee488_device::nrfd_r));
	m_ieee2->in_dav().set(IEEE488_TAG, FUNC(ieee488_device::dav_r));
	m_ieee2->in_eoi().set(IEEE488_TAG, FUNC(ieee488_device::eoi_r));
	m_ieee2->in_atn().set(IEEE488_TAG, FUNC(ieee488_device::atn_r));
	m_ieee2->in_srq().set(IEEE488_TAG, FUNC(ieee488_device::srq_r));
	m_ieee2->out_ren().set(IEEE488_TAG, FUNC(ieee488_device::host_ren_w));
	m_ieee2->out_ifc().set(IEEE488_TAG, FUNC(ieee488_device::host_ifc_w));
	m_ieee2->out_ndac().set(IEEE488_TAG, FUNC(ieee488_device::host_ndac_w));
	m_ieee2->out_nrfd().set(IEEE488_TAG, FUNC(ieee488_device::host_nrfd_w));
	m_ieee2->out_dav().set(IEEE488_TAG, FUNC(ieee488_device::host_dav_w));
	m_ieee2->out_eoi().set(IEEE488_TAG, FUNC(ieee488_device::host_eoi_w));
	m_ieee2->out_atn().set(IEEE488_TAG, FUNC(ieee488_device::host_atn_w));
	m_ieee2->out_srq().set(IEEE488_TAG, FUNC(ieee488_device::host_srq_w));

	IEEE488(config, m_ieee, 0);
	ieee488_slot_device::add_cbm_defaults(config, "c8050");
	m_ieee->srq_callback().set(m_tpi1, FUNC(tpi6525_device::i1_w));

	PET_DATASSETTE_PORT(config, m_cassette, cbm_datassette_devices, nullptr);
	m_cassette->read_handler().set(m_cia, FUNC(mos6526_device::flag_w));

	VCS_CONTROL_PORT(config, m_joy1, vcs_control_port_devices, nullptr);
	m_joy1->trigger_wr_callback().set(MOS6567_TAG, FUNC(mos6567_device::lp_w));
	VCS_CONTROL_PORT(config, m_joy2, vcs_control_port_devices, nullptr);

	CBM2_EXPANSION_SLOT(config, m_exp, XTAL(17'734'472)/18, cbm2_expansion_cards, nullptr);

	CBM2_USER_PORT(config, m_user, cbm2_user_port_cards, nullptr);
	m_user->irq_callback().set("mainirq", FUNC(input_merger_device::in_w<2>));
	m_user->sp_callback().set(MOS6526_TAG, FUNC(mos6526_device::sp_w));
	m_user->cnt_callback().set(MOS6526_TAG, FUNC(mos6526_device::cnt_w));
	m_user->flag_callback().set(MOS6526_TAG, FUNC(mos6526_device::flag_w));

	rs232_port_device &rs232(RS232_PORT(config, RS232_TAG, default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_acia, FUNC(mos6551_device::write_rxd));
	rs232.dcd_handler().set(m_acia, FUNC(mos6551_device::write_dcd));
	rs232.dsr_handler().set(m_acia, FUNC(mos6551_device::write_dsr));
	rs232.cts_handler().set(m_acia, FUNC(mos6551_device::write_cts));

	QUICKLOAD(config, "quickload", "p00,prg", CBM_QUICKLOAD_DELAY).set_load_callback(FUNC(p500_state::quickload_p500));

	// internal ram
	_128k(config);

	// software list
	SOFTWARE_LIST(config, "cart_list").set_original("cbm2_cart").set_filter("PAL");
	SOFTWARE_LIST(config, "flop_list").set_original("p500_flop").set_filter("PAL");
}


//-------------------------------------------------
//  machine_config( cbm2lp_ntsc )
//-------------------------------------------------

void cbm2_state::cbm2lp_ntsc(machine_config &config)
{
	MCFG_MACHINE_START_OVERRIDE(cbm2_state, cbm2_ntsc)
	MCFG_MACHINE_RESET_OVERRIDE(cbm2_state, cbm2)

	// basic hardware
	M6509(config, m_maincpu, XTAL(18'000'000)/9);
	m_maincpu->set_addrmap(AS_PROGRAM, &cbm2_state::cbm2_mem);
	config.set_perfect_quantum(m_maincpu);

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, m6509_device::IRQ_LINE);

	// video hardware
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_screen_update(MC68B45_TAG, FUNC(mc6845_device::screen_update));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.set_size(768, 312);
	screen.set_visarea(0, 768-1, 0, 312-1);

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	MC6845(config, m_crtc, XTAL(18'000'000)/9);
	m_crtc->set_screen(SCREEN_TAG);
	m_crtc->set_show_border_area(true);
	m_crtc->set_char_width(9);
	m_crtc->set_update_row_callback(FUNC(cbm2_state::crtc_update_row));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	MOS6581(config, m_sid, XTAL(18'000'000)/9);
	m_sid->potx().set(FUNC(p500_state::sid_potx_r));
	m_sid->poty().set(FUNC(p500_state::sid_poty_r));
	m_sid->add_route(ALL_OUTPUTS, "mono", 1.00);

	// devices
	PLS100(config, m_pla1);

	TPI6525(config, m_tpi1, 0);
	m_tpi1->out_irq_cb().set("mainirq", FUNC(input_merger_device::in_w<1>));
	m_tpi1->in_pa_cb().set(FUNC(cbm2_state::tpi1_pa_r));
	m_tpi1->out_pa_cb().set(FUNC(cbm2_state::tpi1_pa_w));
	m_tpi1->in_pb_cb().set(FUNC(cbm2_state::tpi1_pb_r));
	m_tpi1->out_pb_cb().set(FUNC(cbm2_state::tpi1_pb_w));
	m_tpi1->out_ca_cb().set(FUNC(cbm2_state::tpi1_ca_w));

	TPI6525(config, m_tpi2, 0);
	m_tpi2->out_pa_cb().set(FUNC(cbm2_state::tpi2_pa_w));
	m_tpi2->out_pb_cb().set(FUNC(cbm2_state::tpi2_pb_w));
	m_tpi2->in_pc_cb().set(FUNC(cbm2_state::tpi2_pc_r));

	MOS6551(config, m_acia, 0);
	m_acia->set_xtal(XTAL(1'843'200));
	m_acia->irq_handler().set(m_tpi1, FUNC(tpi6525_device::i4_w));
	m_acia->txd_handler().set(RS232_TAG, FUNC(rs232_port_device::write_txd));

	MOS6526A(config, m_cia, XTAL(18'000'000)/9);
	m_cia->set_tod_clock(60);
	m_cia->irq_wr_callback().set(m_tpi1, FUNC(tpi6525_device::i2_w));
	m_cia->cnt_wr_callback().set(m_user, FUNC(cbm2_user_port_device::cnt_w));
	m_cia->sp_wr_callback().set(m_user, FUNC(cbm2_user_port_device::sp_w));
	m_cia->pa_rd_callback().set(FUNC(cbm2_state::cia_pa_r));
	m_cia->pa_wr_callback().set(FUNC(cbm2_state::cia_pa_w));
	m_cia->pb_rd_callback().set(FUNC(cbm2_state::cia_pb_r));
	m_cia->pb_wr_callback().set(m_user, FUNC(cbm2_user_port_device::d2_w));
	m_cia->pc_wr_callback().set(m_user, FUNC(cbm2_user_port_device::pc_w));

	DS75160A(config, m_ieee1, 0);
	m_ieee1->read_callback().set(IEEE488_TAG, FUNC(ieee488_device::dio_r));
	m_ieee1->write_callback().set(IEEE488_TAG, FUNC(ieee488_device::host_dio_w));

	DS75161A(config, m_ieee2, 0);
	m_ieee2->in_ren().set(IEEE488_TAG, FUNC(ieee488_device::ren_r));
	m_ieee2->in_ifc().set(IEEE488_TAG, FUNC(ieee488_device::ifc_r));
	m_ieee2->in_ndac().set(IEEE488_TAG, FUNC(ieee488_device::ndac_r));
	m_ieee2->in_nrfd().set(IEEE488_TAG, FUNC(ieee488_device::nrfd_r));
	m_ieee2->in_dav().set(IEEE488_TAG, FUNC(ieee488_device::dav_r));
	m_ieee2->in_eoi().set(IEEE488_TAG, FUNC(ieee488_device::eoi_r));
	m_ieee2->in_atn().set(IEEE488_TAG, FUNC(ieee488_device::atn_r));
	m_ieee2->in_srq().set(IEEE488_TAG, FUNC(ieee488_device::srq_r));
	m_ieee2->out_ren().set(IEEE488_TAG, FUNC(ieee488_device::host_ren_w));
	m_ieee2->out_ifc().set(IEEE488_TAG, FUNC(ieee488_device::host_ifc_w));
	m_ieee2->out_ndac().set(IEEE488_TAG, FUNC(ieee488_device::host_ndac_w));
	m_ieee2->out_nrfd().set(IEEE488_TAG, FUNC(ieee488_device::host_nrfd_w));
	m_ieee2->out_dav().set(IEEE488_TAG, FUNC(ieee488_device::host_dav_w));
	m_ieee2->out_eoi().set(IEEE488_TAG, FUNC(ieee488_device::host_eoi_w));
	m_ieee2->out_atn().set(IEEE488_TAG, FUNC(ieee488_device::host_atn_w));
	m_ieee2->out_srq().set(IEEE488_TAG, FUNC(ieee488_device::host_srq_w));

	IEEE488(config, m_ieee, 0);
	ieee488_slot_device::add_cbm_defaults(config, "c8050");
	m_ieee->srq_callback().set(m_tpi1, FUNC(tpi6525_device::i1_w));

	PET_DATASSETTE_PORT(config, m_cassette, cbm_datassette_devices, nullptr);
	m_cassette->read_handler().set(m_cia, FUNC(mos6526_device::flag_w));

	VCS_CONTROL_PORT(config, m_joy1, vcs_control_port_devices, nullptr);
	VCS_CONTROL_PORT(config, m_joy2, vcs_control_port_devices, nullptr);

	CBM2_EXPANSION_SLOT(config, m_exp, XTAL(18'000'000)/9, cbm2_expansion_cards, nullptr);

	CBM2_USER_PORT(config, m_user, cbm2_user_port_cards, nullptr);
	m_user->irq_callback().set("mainirq", FUNC(input_merger_device::in_w<2>));
	m_user->sp_callback().set(MOS6526_TAG, FUNC(mos6526_device::sp_w));
	m_user->cnt_callback().set(MOS6526_TAG, FUNC(mos6526_device::cnt_w));
	m_user->flag_callback().set(MOS6526_TAG, FUNC(mos6526_device::flag_w));

	rs232_port_device &rs232(RS232_PORT(config, RS232_TAG, default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_acia, FUNC(mos6551_device::write_rxd));
	rs232.dcd_handler().set(m_acia, FUNC(mos6551_device::write_dcd));
	rs232.dsr_handler().set(m_acia, FUNC(mos6551_device::write_dsr));
	rs232.cts_handler().set(m_acia, FUNC(mos6551_device::write_cts));

	QUICKLOAD(config, "quickload", "p00,prg,t64", CBM_QUICKLOAD_DELAY).set_load_callback(FUNC(cbm2_state::quickload_cbmb));

	// software list
	SOFTWARE_LIST(config, "cart_list").set_original("cbm2_cart").set_filter("NTSC");
	SOFTWARE_LIST(config, "flop_list").set_original("cbm2_flop").set_filter("NTSC");
}


//-------------------------------------------------
//  machine_config( b128 )
//-------------------------------------------------

void cbm2_state::b128(machine_config &config)
{
	cbm2lp_ntsc(config);
	_128k(config);
}


//-------------------------------------------------
//  machine_config( b256 )
//-------------------------------------------------

void cbm2_state::b256(machine_config &config)
{
	cbm2lp_ntsc(config);
	_256k(config);
}


//-------------------------------------------------
//  machine_config( cbm2lp_pal )
//-------------------------------------------------

void cbm2_state::cbm2lp_pal(machine_config &config)
{
	cbm2lp_ntsc(config);
	MCFG_MACHINE_START_OVERRIDE(cbm2_state, cbm2_pal)
	m_cia->set_tod_clock(50);
}


//-------------------------------------------------
//  machine_config( cbm610 )
//-------------------------------------------------

void cbm2_state::cbm610(machine_config &config)
{
	cbm2lp_pal(config);
	_128k(config);
}


//-------------------------------------------------
//  machine_config( cbm620 )
//-------------------------------------------------

void cbm2_state::cbm620(machine_config &config)
{
	cbm2lp_pal(config);
	_256k(config);
}


//-------------------------------------------------
//  machine_config( cbm2hp_ntsc )
//-------------------------------------------------

void cbm2_state::cbm2hp_ntsc(machine_config &config)
{
	cbm2lp_ntsc(config);
	m_tpi2->in_pc_cb().set(FUNC(cbm2hp_state::tpi2_pc_r));
}


//-------------------------------------------------
//  machine_config( b128hp )
//-------------------------------------------------

void cbm2hp_state::b128hp(machine_config &config)
{
	cbm2hp_ntsc(config);
	_128k(config);
}


//-------------------------------------------------
//  machine_config( b256hp )
//-------------------------------------------------

void cbm2hp_state::b256hp(machine_config &config)
{
	cbm2hp_ntsc(config);
	_256k(config);
}


//-------------------------------------------------
//  machine_config( bx256hp )
//-------------------------------------------------

void cbm2hp_state::bx256hp(machine_config &config)
{
	b256hp(config);
	MCFG_MACHINE_START_OVERRIDE(cbm2_state, cbm2x_ntsc)

	I8088(config, m_ext_cpu, XTAL(12'000'000));
	m_ext_cpu->set_addrmap(AS_PROGRAM, &cbm2hp_state::ext_mem);
	m_ext_cpu->set_addrmap(AS_IO, &cbm2hp_state::ext_io);
	m_ext_cpu->set_irq_acknowledge_callback(EXT_I8259A_TAG, FUNC(pic8259_device::inta_cb));

	PIC8259(config, m_ext_pic, 0);
	m_ext_pic->out_int_callback().set_inputline(m_ext_cpu, INPUT_LINE_IRQ0);

	TPI6525(config, m_ext_tpi, 0);
	m_ext_tpi->in_pa_cb().set(m_ext_cia, FUNC(mos6526_device::pa_r));
	m_ext_tpi->in_pb_cb().set(FUNC(cbm2_state::ext_tpi_pb_r));
	m_ext_tpi->out_pb_cb().set(FUNC(cbm2_state::ext_tpi_pb_w));
	m_ext_tpi->out_pc_cb().set(FUNC(cbm2_state::ext_tpi_pc_w));

	MOS6526(config, m_ext_cia, XTAL(18'000'000)/9);
	m_ext_cia->set_tod_clock(60);
	m_ext_cia->irq_wr_callback().set(FUNC(cbm2_state::ext_cia_irq_w));
	m_ext_cia->pa_rd_callback().set(m_ext_tpi, FUNC(tpi6525_device::pa_r));
	m_ext_cia->pb_rd_callback().set(FUNC(cbm2_state::ext_cia_pb_r));
	m_ext_cia->pb_wr_callback().set(FUNC(cbm2_state::ext_cia_pb_w));

	SOFTWARE_LIST(config, "flop_list2").set_original("bx256hp_flop");
}


//-------------------------------------------------
//  machine_config( cbm2hp_pal )
//-------------------------------------------------

void cbm2_state::cbm2hp_pal(machine_config &config)
{
	cbm2hp_ntsc(config);
	MCFG_MACHINE_START_OVERRIDE(cbm2_state, cbm2_pal)

	// devices
	m_tpi2->in_pc_cb().set(FUNC(cbm2hp_state::tpi2_pc_r));
	m_cia->set_tod_clock(50);
}


//-------------------------------------------------
//  machine_config( cbm710 )
//-------------------------------------------------

void cbm2hp_state::cbm710(machine_config &config)
{
	cbm2hp_pal(config);
	_128k(config);
}


//-------------------------------------------------
//  machine_config( cbm720 )
//-------------------------------------------------

void cbm2hp_state::cbm720(machine_config &config)
{
	cbm2hp_pal(config);
	_256k(config);
}


//-------------------------------------------------
//  machine_config( cbm730 )
//-------------------------------------------------

void cbm2hp_state::cbm730(machine_config &config)
{
	cbm720(config);
	MCFG_MACHINE_START_OVERRIDE(cbm2_state, cbm2x_pal)

	I8088(config, m_ext_cpu, XTAL(12'000'000));
	m_ext_cpu->set_addrmap(AS_PROGRAM, &cbm2hp_state::ext_mem);
	m_ext_cpu->set_addrmap(AS_IO, &cbm2hp_state::ext_io);
	m_ext_cpu->set_irq_acknowledge_callback(EXT_I8259A_TAG, FUNC(pic8259_device::inta_cb));

	PIC8259(config, m_ext_pic, 0);
	m_ext_pic->out_int_callback().set_inputline(m_ext_cpu, INPUT_LINE_IRQ0);

	TPI6525(config, m_ext_tpi, 0);
	m_ext_tpi->in_pa_cb().set(EXT_MOS6526_TAG, FUNC(mos6526_device::pa_r));
	m_ext_tpi->in_pb_cb().set(FUNC(cbm2_state::ext_tpi_pb_r));
	m_ext_tpi->out_pb_cb().set(FUNC(cbm2_state::ext_tpi_pb_w));
	m_ext_tpi->out_pc_cb().set(FUNC(cbm2_state::ext_tpi_pc_w));

	MOS6526(config, m_ext_cia, XTAL(18'000'000)/9);
	m_ext_cia->set_tod_clock(50);
	m_ext_cia->irq_wr_callback().set(FUNC(cbm2_state::ext_cia_irq_w));
	m_ext_cia->pa_rd_callback().set(m_ext_tpi, FUNC(tpi6525_device::pa_r));
	m_ext_cia->pb_rd_callback().set(FUNC(cbm2_state::ext_cia_pb_r));
	m_ext_cia->pb_wr_callback().set(FUNC(cbm2_state::ext_cia_pb_w));

	SOFTWARE_LIST(config, "flop_list2").set_original("bx256hp_flop");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( p500 )
//-------------------------------------------------

ROM_START( p500 )
	ROM_DEFAULT_BIOS("r2")
	ROM_SYSTEM_BIOS( 0, "r1", "Revision 1" )
	ROM_SYSTEM_BIOS( 1, "r2", "Revision 2" )

	ROM_REGION( 0x4000, "basic", 0 )
	ROMX_LOAD( "901236-01.u84", 0x0000, 0x2000, CRC(33eb6aa2) SHA1(7e3497ae2edbb38c753bd31ed1bf3ae798c9a976), ROM_BIOS(0) )
	ROMX_LOAD( "901235-01.u83", 0x2000, 0x2000, CRC(18a27feb) SHA1(951b5370dd7db762b8504a141f9f26de345069bb), ROM_BIOS(0) )
	ROMX_LOAD( "901236-02.u84", 0x0000, 0x2000, CRC(c62ab16f) SHA1(f50240407bade901144f7e9f489fa9c607834eca), ROM_BIOS(1) )
	ROMX_LOAD( "901235-02.u83", 0x2000, 0x2000, CRC(20b7df33) SHA1(1b9a55f12f8cf025754d8029cc5324b474c35841), ROM_BIOS(1) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROMX_LOAD( "901234-01.u82", 0x0000, 0x2000, CRC(67962025) SHA1(24b41b65c85bf30ab4e2911f677ce9843845b3b1), ROM_BIOS(0) )
	ROMX_LOAD( "901234-02.u82", 0x0000, 0x2000, CRC(f46bbd2b) SHA1(097197d4d08e0b82e0466a5f1fbd49a24f3d2523), ROM_BIOS(1) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901225-01.u76", 0x0000, 0x1000, CRC(ec4272ee) SHA1(adc7c31e18c7c7413d54802ef2f4193da14711aa) )

	ROM_REGION( 0xf5, PLA1_TAG, 0 )
	ROM_LOAD( "906114-02.u78", 0x00, 0xf5, CRC(6436b20b) SHA1(57ebebe771791288051afd1abe9b7500bd2df847) )

	ROM_REGION( 0xf5, PLA2_TAG, 0 )
	ROM_LOAD( "906114-03.u88", 0x00, 0xf5, CRC(668c073e) SHA1(1115858bb2dc91ea9e2016ba2e23ec94239358b4) )
ROM_END

#define rom_p500p   rom_p500


//-------------------------------------------------
//  ROM( b500 )
//-------------------------------------------------

ROM_START( b500 )
	ROM_REGION( 0x4000, "basic", 0 )
	ROM_LOAD( "901243-01.u59",  0x0000, 0x2000, CRC(22822706) SHA1(901bbf59d8b8682b481be8b2de99b406fffa4bab) )
	ROM_LOAD( "901242-01a.u60", 0x2000, 0x2000, CRC(ef13d595) SHA1(2fb72985d7d4ab69c5780179178828c931a9f5b0) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROM_LOAD( "901244-01.u61",  0x0000, 0x2000, CRC(93414213) SHA1(a54a593dbb420ae1ac39b0acde9348160f7840ff) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901237-01.u25", 0x0000, 0x1000, CRC(1acf5098) SHA1(e63bf18da48e5a53c99ef127c1ae721333d1d102) )

	ROM_REGION( 0xf5, PLA1_TAG, 0 )
	ROM_LOAD( "906114-04.u18", 0x00, 0xf5, CRC(ae3ec265) SHA1(334e0bc4b2c957ecb240c051d84372f7b47efba3) )
ROM_END


//-------------------------------------------------
//  ROM( b128 )
//-------------------------------------------------

ROM_START( b128 )
	ROM_DEFAULT_BIOS("r2")
	ROM_SYSTEM_BIOS( 0, "r1", "Revision 1" )
	ROM_SYSTEM_BIOS( 1, "r2", "Revision 2" )

	ROM_REGION( 0x4000, "basic", 0 )
	ROMX_LOAD( "901243-02b.u59", 0x0000, 0x2000, CRC(9d0366f9) SHA1(625f7337ea972a8bce2bdf2daababc0ed0b3b69b), ROM_BIOS(0) )
	ROMX_LOAD( "901242-02b.u60", 0x2000, 0x2000, CRC(837978b5) SHA1(56e8d2f86bf73ba36b3d3cb84dd75806b66c530a), ROM_BIOS(0) )
	ROMX_LOAD( "901243-04a.u59", 0x0000, 0x2000, CRC(b0dcb56d) SHA1(08d333208060ee2ce84d4532028d94f71c016b96), ROM_BIOS(1) )
	ROMX_LOAD( "901242-04a.u60", 0x2000, 0x2000, CRC(de04ea4f) SHA1(7c6de17d46a3343dc597d9b9519cf63037b31908), ROM_BIOS(1) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROMX_LOAD( "901244-03b.u61", 0x0000, 0x2000, CRC(4276dbba) SHA1(a624899c236bc4458570144d25aaf0b3be08b2cd), ROM_BIOS(0) )
	ROMX_LOAD( "901244-04a.u61", 0x0000, 0x2000, CRC(09a5667e) SHA1(abb26418b9e1614a8f52bdeee0822d4a96071439), ROM_BIOS(1) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901237-01.u25", 0x0000, 0x1000, CRC(1acf5098) SHA1(e63bf18da48e5a53c99ef127c1ae721333d1d102) )

	ROM_REGION( 0xf5, PLA1_TAG, 0 )
	ROM_LOAD( "906114-04.u18", 0x00, 0xf5, CRC(ae3ec265) SHA1(334e0bc4b2c957ecb240c051d84372f7b47efba3) )
ROM_END

#define rom_cbm610  rom_b128
#define rom_cbm620  rom_b256


//-------------------------------------------------
//  ROM( b256 )
//-------------------------------------------------

ROM_START( b256 )
	ROM_REGION( 0x4000, "basic", 0 )
	ROM_LOAD( "901241-03.u59", 0x0000, 0x2000, CRC(5c1f3347) SHA1(2d46be2cd89594b718cdd0a86d51b6f628343f42) )
	ROM_LOAD( "901240-03.u60", 0x2000, 0x2000, CRC(72aa44e1) SHA1(0d7f77746290afba8d0abeb87c9caab9a3ad89ce) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROM_DEFAULT_BIOS("r2")
	ROM_SYSTEM_BIOS( 0, "r1", "Revision 1" )
	ROMX_LOAD( "901244-03b.u61", 0x0000, 0x2000, CRC(4276dbba) SHA1(a624899c236bc4458570144d25aaf0b3be08b2cd), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "r2", "Revision 2" )
	ROMX_LOAD( "901244-04a.u61", 0x0000, 0x2000, CRC(09a5667e) SHA1(abb26418b9e1614a8f52bdeee0822d4a96071439), ROM_BIOS(1) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901237-01.u25", 0x0000, 0x1000, CRC(1acf5098) SHA1(e63bf18da48e5a53c99ef127c1ae721333d1d102) )

	ROM_REGION( 0xf5, PLA1_TAG, 0 )
	ROM_LOAD( "906114-04.u18", 0x00, 0xf5, CRC(ae3ec265) SHA1(334e0bc4b2c957ecb240c051d84372f7b47efba3) )
ROM_END


//-------------------------------------------------
//  ROM( cbm620_hu )
//-------------------------------------------------

ROM_START( cbm620_hu )
	ROM_REGION( 0x4000, "basic", 0 )
	ROM_LOAD( "610.u60", 0x0000, 0x4000, CRC(8eed0d7e) SHA1(9d06c5c3c012204eaaef8b24b1801759b62bf57e) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROM_LOAD( "kernhun.u61", 0x0000, 0x2000, CRC(0ea8ca4d) SHA1(9977c9f1136ee9c04963e0b50ae0c056efa5663f) )

	ROM_REGION( 0x2000, "charom", 0 )
	ROM_LOAD( "charhun.u25", 0x0000, 0x2000, CRC(1fb5e596) SHA1(3254e069f8691b30679b19a9505b6afdfedce6ac) )

	ROM_REGION( 0xf5, PLA1_TAG, 0 )
	ROM_LOAD( "906114-04.u18", 0x00, 0xf5, CRC(ae3ec265) SHA1(334e0bc4b2c957ecb240c051d84372f7b47efba3) )
ROM_END


//-------------------------------------------------
//  ROM( b128hp )
//-------------------------------------------------

ROM_START( b128hp )
	ROM_DEFAULT_BIOS("r2")
	ROM_SYSTEM_BIOS( 0, "r1", "Revision 1" )
	ROM_SYSTEM_BIOS( 1, "r2", "Revision 2" )

	ROM_REGION( 0x4000, "basic", 0 )
	ROMX_LOAD( "901243-02b.u59", 0x0000, 0x2000, CRC(9d0366f9) SHA1(625f7337ea972a8bce2bdf2daababc0ed0b3b69b), ROM_BIOS(0) )
	ROMX_LOAD( "901242-02b.u60", 0x2000, 0x2000, CRC(837978b5) SHA1(56e8d2f86bf73ba36b3d3cb84dd75806b66c530a), ROM_BIOS(0) )
	ROMX_LOAD( "901243-04a.u59", 0x0000, 0x2000, CRC(b0dcb56d) SHA1(08d333208060ee2ce84d4532028d94f71c016b96), ROM_BIOS(1) )
	ROMX_LOAD( "901242-04a.u60", 0x2000, 0x2000, CRC(de04ea4f) SHA1(7c6de17d46a3343dc597d9b9519cf63037b31908), ROM_BIOS(1) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROMX_LOAD( "901244-03b.u61", 0x0000, 0x2000, CRC(4276dbba) SHA1(a624899c236bc4458570144d25aaf0b3be08b2cd), ROM_BIOS(0) )
	ROMX_LOAD( "901244-04a.u61", 0x0000, 0x2000, CRC(09a5667e) SHA1(abb26418b9e1614a8f52bdeee0822d4a96071439), ROM_BIOS(1) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901232-01.u25", 0x0000, 0x1000, CRC(3a350bc3) SHA1(e7f3cbc8e282f79a00c3e95d75c8d725ee3c6287) )

	ROM_REGION( 0xf5, PLA1_TAG, 0 )
	ROM_LOAD( "906114-05.u75", 0x00, 0xf5, CRC(ff6ba6b6) SHA1(45808c570eb2eda7091c51591b3dbd2db1ac646a) )
ROM_END

#define rom_cbm710  rom_b128hp


//-------------------------------------------------
//  ROM( b256hp )
//-------------------------------------------------

ROM_START( b256hp )
	ROM_REGION( 0x4000, "basic", 0 )
	ROM_LOAD( "901241-03.u59", 0x0000, 0x2000, CRC(5c1f3347) SHA1(2d46be2cd89594b718cdd0a86d51b6f628343f42) )
	ROM_LOAD( "901240-03.u60", 0x2000, 0x2000, CRC(72aa44e1) SHA1(0d7f77746290afba8d0abeb87c9caab9a3ad89ce) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROM_DEFAULT_BIOS("r2")
	ROM_SYSTEM_BIOS( 0, "r1", "Revision 1" )
	ROMX_LOAD( "901244-03b.u61", 0x0000, 0x2000, CRC(4276dbba) SHA1(a624899c236bc4458570144d25aaf0b3be08b2cd), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "r2", "Revision 2" )
	ROMX_LOAD( "901244-04a.u61", 0x0000, 0x2000, CRC(09a5667e) SHA1(abb26418b9e1614a8f52bdeee0822d4a96071439), ROM_BIOS(1) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901232-01.u25", 0x0000, 0x1000, CRC(3a350bc3) SHA1(e7f3cbc8e282f79a00c3e95d75c8d725ee3c6287) )

	ROM_REGION( 0xf5, PLA1_TAG, 0 )
	ROM_LOAD( "906114-05.u75", 0x00, 0xf5, CRC(ff6ba6b6) SHA1(45808c570eb2eda7091c51591b3dbd2db1ac646a) )
ROM_END

#define rom_cbm720  rom_b256hp


//-------------------------------------------------
//  ROM( bx256hp )
//-------------------------------------------------

ROM_START( bx256hp )
	ROM_REGION( 0x4000, "basic", 0 )
	ROM_LOAD( "901241-03.u59", 0x0000, 0x2000, CRC(5c1f3347) SHA1(2d46be2cd89594b718cdd0a86d51b6f628343f42) )
	ROM_LOAD( "901240-03.u60", 0x2000, 0x2000, CRC(72aa44e1) SHA1(0d7f77746290afba8d0abeb87c9caab9a3ad89ce) )

	ROM_REGION( 0x1000, EXT_I8088_TAG, 0)
	ROM_LOAD( "8088.u14", 0x0000, 0x1000, CRC(195e0281) SHA1(ce8acd2a5fb6cbd70d837811d856d656544a1f97) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROM_DEFAULT_BIOS("r2")
	ROM_SYSTEM_BIOS( 0, "r1", "Revision 1" )
	ROMX_LOAD( "901244-03b.u61", 0x0000, 0x2000, CRC(4276dbba) SHA1(a624899c236bc4458570144d25aaf0b3be08b2cd), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "r2", "Revision 2" )
	ROMX_LOAD( "901244-04a.u61", 0x0000, 0x2000, CRC(09a5667e) SHA1(abb26418b9e1614a8f52bdeee0822d4a96071439), ROM_BIOS(1) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901232-01.u25", 0x0000, 0x1000, CRC(3a350bc3) SHA1(e7f3cbc8e282f79a00c3e95d75c8d725ee3c6287) )

	ROM_REGION( 0xf5, PLA1_TAG, 0 )
	ROM_LOAD( "906114-05.u75", 0x00, 0xf5, CRC(ff6ba6b6) SHA1(45808c570eb2eda7091c51591b3dbd2db1ac646a) )
ROM_END

#define rom_cbm730  rom_bx256hp


//-------------------------------------------------
//  ROM( cbm720_de )
//-------------------------------------------------

ROM_START( cbm720_de )
	ROM_REGION( 0x4000, "basic", 0 )
	ROM_LOAD( "901241-03.u59", 0x0000, 0x2000, CRC(5c1f3347) SHA1(2d46be2cd89594b718cdd0a86d51b6f628343f42) )
	ROM_LOAD( "901240-03.u60", 0x2000, 0x2000, CRC(72aa44e1) SHA1(0d7f77746290afba8d0abeb87c9caab9a3ad89ce) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROM_LOAD( "324866-03a.u61", 0x0000, 0x2000, CRC(554b008d) SHA1(1483a46924308d86f4c7f9cb71c34851c510fcf4) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "324867-02.u25", 0x0000, 0x1000, NO_DUMP )

	ROM_REGION( 0xf5, PLA1_TAG, 0 )
	ROM_LOAD( "906114-05.u75", 0x00, 0xf5, CRC(ff6ba6b6) SHA1(45808c570eb2eda7091c51591b3dbd2db1ac646a) )
ROM_END


//-------------------------------------------------
//  ROM( cbm720_se )
//-------------------------------------------------

ROM_START( cbm720_se )
	ROM_REGION( 0x4000, "basic", 0 )
	ROM_LOAD( "901241-03.u59", 0x0000, 0x2000, CRC(5c1f3347) SHA1(2d46be2cd89594b718cdd0a86d51b6f628343f42) )
	ROM_LOAD( "901240-03.u60", 0x2000, 0x2000, CRC(72aa44e1) SHA1(0d7f77746290afba8d0abeb87c9caab9a3ad89ce) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROM_LOAD( "swe-901244-03.u61", 0x0000, 0x2000, CRC(87bc142b) SHA1(fa711f6082741b05a9c80744f5aee68dc8c1dcf4) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901233-03.u25", 0x0000, 0x1000, CRC(09518b19) SHA1(2e28491e31e2c0a3b6db388055216140a637cd09) )

	ROM_REGION( 0xf5, PLA1_TAG, 0 )
	ROM_LOAD( "906114-05.u75", 0x00, 0xf5, CRC(ff6ba6b6) SHA1(45808c570eb2eda7091c51591b3dbd2db1ac646a) )
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT    CLASS         INIT    COMPANY                         FULLNAME                    FLAGS
COMP( 1983, p500,      0,      0,      p500_ntsc, cbm2,    p500_state,   empty_init, "Commodore Business Machines",  "P500 (NTSC)",              MACHINE_SUPPORTS_SAVE )
COMP( 1983, p500p,     p500,   0,      p500_pal,  cbm2,    p500_state,   empty_init, "Commodore Business Machines",  "P500 (PAL)",               MACHINE_SUPPORTS_SAVE )
COMP( 1983, b500,      0,      0,      b128,      cbm2,    cbm2_state,   empty_init, "Commodore Business Machines",  "B500",                     MACHINE_SUPPORTS_SAVE )
COMP( 1983, b128,      b500,   0,      b128,      cbm2,    cbm2_state,   empty_init, "Commodore Business Machines",  "B128",                     MACHINE_SUPPORTS_SAVE )
COMP( 1983, b256,      b500,   0,      b256,      cbm2,    cbm2_state,   empty_init, "Commodore Business Machines",  "B256",                     MACHINE_SUPPORTS_SAVE )
COMP( 1983, cbm610,    b500,   0,      cbm610,    cbm2,    cbm2_state,   empty_init, "Commodore Business Machines",  "CBM 610",                  MACHINE_SUPPORTS_SAVE )
COMP( 1983, cbm620,    b500,   0,      cbm620,    cbm2,    cbm2_state,   empty_init, "Commodore Business Machines",  "CBM 620",                  MACHINE_SUPPORTS_SAVE )
COMP( 1983, cbm620_hu, b500,   0,      cbm620,    cbm2_hu, cbm2_state,   empty_init, "Commodore Business Machines",  "CBM 620 (Hungary)",        MACHINE_SUPPORTS_SAVE )
COMP( 1983, b128hp,    0,      0,      b128hp,    cbm2,    cbm2hp_state, empty_init, "Commodore Business Machines",  "B128-80HP",                MACHINE_SUPPORTS_SAVE )
COMP( 1983, b256hp,    b128hp, 0,      b256hp,    cbm2,    cbm2hp_state, empty_init, "Commodore Business Machines",  "B256-80HP",                MACHINE_SUPPORTS_SAVE )
COMP( 1983, bx256hp,   b128hp, 0,      bx256hp,   cbm2,    cbm2hp_state, empty_init, "Commodore Business Machines",  "BX256-80HP",               MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) // 8088 co-processor is missing
COMP( 1983, cbm710,    b128hp, 0,      cbm710,    cbm2,    cbm2hp_state, empty_init, "Commodore Business Machines",  "CBM 710",                  MACHINE_SUPPORTS_SAVE )
COMP( 1983, cbm720,    b128hp, 0,      cbm720,    cbm2,    cbm2hp_state, empty_init, "Commodore Business Machines",  "CBM 720",                  MACHINE_SUPPORTS_SAVE )
COMP( 1983, cbm720_de, b128hp, 0,      cbm720,    cbm2_de, cbm2hp_state, empty_init, "Commodore Business Machines",  "CBM 720 (Germany)",        MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1983, cbm720_se, b128hp, 0,      cbm720,    cbm2_se, cbm2hp_state, empty_init, "Commodore Business Machines",  "CBM 720 (Sweden/Finland)", MACHINE_SUPPORTS_SAVE )
COMP( 1983, cbm730,    b128hp, 0,      cbm730,    cbm2,    cbm2hp_state, empty_init, "Commodore Business Machines",  "CBM 730",                  MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) // 8088 co-processor is missing



cbnt2039.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/************************************************************************************************************

    Skeleton driver for Cablenet, Inc. 2039 coax/twinax Lexmark printer controller card.

    Main CPU is a Chips & Technologies F8680 PC/CHIP (x86 with custom opcodes).

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

#include "emu.h"
//#include "cpu/i86/f8680.h"
#include "cpu/bcp/dp8344.h"
//#include "machine/eepromser.h"


namespace {

class cbnt2039_state : public driver_device
{
public:
	cbnt2039_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_bcp(*this, "bcp")
	{
	}

	void cbnt2039(machine_config &config);

private:
	void bcp_prog_map(address_map &map) ATTR_COLD;
	void bcp_data_map(address_map &map) ATTR_COLD;

	required_device<dp8344_device> m_bcp;
};

void cbnt2039_state::bcp_prog_map(address_map &map)
{
	map(0x0000, 0x1fff).rom().region("sbprom", 0x10906); // FIXME: should be RAM (2x MCM6264CP25), with code uploaded by F8680
}

void cbnt2039_state::bcp_data_map(address_map &map)
{
}


static INPUT_PORTS_START(cbnt2039)
INPUT_PORTS_END


void cbnt2039_state::cbnt2039(machine_config &config)
{
	//F8680(config, m_maincpu, OSC1);

	DP8344B(config, m_bcp, 18.8696_MHz_XTAL); // DP8344BV; Y1 = "RXD8.000"; Y2 = "RXD18.86"
	m_bcp->set_addrmap(AS_PROGRAM, &cbnt2039_state::bcp_prog_map);
	m_bcp->set_addrmap(AS_DATA, &cbnt2039_state::bcp_data_map);

	//EEPROM_93C56_16BIT(config, "eeprom");
}


ROM_START(cbnt2039)
	ROM_REGION16_LE(0x20000, "sbprom", 0)
	ROM_LOAD("sbprom_27c_2ea2-1001.u12", 0x00000, 0x20000, CRC(4ee02833) SHA1(17c8b02bbef7b855a91dfb8bd9758ffb5cc9b9e7)) // handwritten label
ROM_END

} // anonymous namespace


COMP(1993, cbnt2039, 0, 0, cbnt2039, cbnt2039, cbnt2039_state, empty_init, "Cablenet", "2039 Controller", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



cc1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger, Sean Riddle
/*******************************************************************************

Fidelity's 1st generation chess computers:
- Chess Challenger
- Chess Challenger (upgraded version) - more commonly known as CC3
- Chess Challenger (model UCC10) - more commonly known as CC10 ver. C

The first generation of chesscomputers didn't have an electronic chessboard.
Some of them required a separate chessboard, others had a small chessboard
attached to it (the latter applies to Fidelity).

For those familiar with MAME's sensorboard interface and really want to use it
for the old keypad-input machines, there is an awkward workaround: Start a 2nd
instance of MAME with -sound none and load mephisto3 or mephisto2e, turn off
the ESB 6000 board in the machine configuration, and set the video options to
"Internal Layout (Board)". The same thing can be done with ccmk6.

That being said, it's probably a better idea to use a real chessboard.

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

Chess Challenger (1)
--------------------
This is the world's 1st released dedicated chess computer. Oddly, the rows/columns
are reversed: left to right is 1-8, bottom to top is A-H, eg. pawn from D2 to D4
is 4B to 4D here.

The CC1 patent(US4235442) refers to a Hewlett Packard chess program. It was eventually
found out that it was written for a HP-9810A by Alan A. Wray in 1974, and CC1 is very
similar to it. Ron C. Nelson must have ported the algorithms to 8080 when he wrote
his Altair 8800 chess program, and this is what made it into CC1.

CC1 hardware overview:
- PCB label: PC-P-86, P179 C-2 7.77
- NEC 8080AF @ 2MHz(18MHz XTAL through a 8224)
- Everything goes via a NEC B8228, its special features are unused.
- NEC 2316A ROM(2KB), 4*2101AL RAM(0.5KB total)
- 8255C for I/O, 4*7seg display + 2 extra leds, 12-key keypad, no sound

Chess Challenger (upgraded version) released a few months later is on the same
hardware, but with double the ROM size, and they corrected the reversed chess
notation. It was also offered as an upgrade to CC1. PCB label P179 C-3 9.77.

Chess Challenger (model UCC10) is on nearly the same PCB too, same label as CC3,
with an 8KB ROM on a small daughterboard(P-410A 4 12 79). Again, it was also
offered as an upgrade to CC1, or CC3.

Note that although these 2 newer versions are known as "Chess Challenger 3" and
"Chess Challeger 10 C" nowadays, those are not the official titles. CC3 simply
says "upgraded version" on the 1st page of the manual (even the newly sold ones,
not just the literal CC1 upgrades). UCC10 mentions "10 levels of play". Consumenta
Computer(reseller of Fidelity chesscomputers) did name it Chess-Challenger 10 C.
Officially, Fidelity started adding level numbers to their chesscomputer titles
with CCX and CC7.

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

#include "emu.h"

#include "cpu/i8085/i8085.h"
#include "machine/i8255.h"
#include "video/pwm.h"

// internal artwork
#include "fidel_cc1.lh"
#include "fidel_cc3.lh"
#include "fidel_cc10c.lh"


namespace {

class cc1_state : public driver_device
{
public:
	cc1_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ppi8255(*this, "ppi8255"),
		m_display(*this, "display"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// RE button is tied to 8224 RESIN pin
	DECLARE_INPUT_CHANGED_MEMBER(reset_button) { m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE); }

	// machine configs
	void cc1(machine_config &config);
	void cc3(machine_config &config);
	void cc10c(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<i8255_device> m_ppi8255;
	required_device<pwm_display_device> m_display;
	required_ioport_array<2> m_inputs;

	attotime m_555_delay;
	u8 m_led_select = 0;
	u8 m_7seg_data = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;
	void cc10c_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_display();
	u8 ppi_porta_r();
	void ppi_portb_w(u8 data);
	void ppi_portc_w(u8 data);
};

void cc1_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_555_delay));
	save_item(NAME(m_led_select));
	save_item(NAME(m_7seg_data));
}



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

// I8255 PPI

void cc1_state::update_display()
{
	// 4 7segs + 2 leds
	m_display->matrix(m_led_select, m_7seg_data);
}

u8 cc1_state::ppi_porta_r()
{
	// 74148(priority encoder) I0-I7: inputs
	// d0-d2: 74148 S0-S2, d3: 74148 GS
	u8 data = count_leading_zeros_32(m_inputs[0]->read()) - 24;
	if (data == 8) data = 0xf;

	// d5-d7: more inputs (direct)
	data |= ~m_inputs[1]->read() << 5 & 0xe0;

	// d4: 555 Q
	return data | ((m_555_delay > machine().time()) ? 0x10 : 0);
}

void cc1_state::ppi_portb_w(u8 data)
{
	// d0-d6: digit segment data
	m_7seg_data = bitswap<7>(data,0,1,2,3,4,5,6);
	update_display();
}

void cc1_state::ppi_portc_w(u8 data)
{
	// d6: trigger monostable 555 (R=15K, C=1uF)
	if (~data & m_led_select & 0x40 && m_555_delay < machine().time())
		m_555_delay = machine().time() + attotime::from_msec(17);

	// d0-d3: digit select
	// d4: check led, d5: lose led
	m_led_select = data;
	update_display();
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void cc1_state::main_map(address_map &map)
{
	map.global_mask(0x1fff);
	map(0x0000, 0x0fff).rom();
	map(0x1000, 0x11ff).mirror(0x0e00).ram();
}

void cc1_state::main_io(address_map &map)
{
	map.global_mask(0x0f);
	map(0x00, 0x03).mirror(0x04).rw(m_ppi8255, FUNC(i8255_device::read), FUNC(i8255_device::write));
}

void cc1_state::cc10c_map(address_map &map)
{
	map.global_mask(0x3fff);
	map(0x0000, 0x0fff).rom();
	map(0x1000, 0x11ff).mirror(0x2e00).ram();
	map(0x2000, 0x2fff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( cc1 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("8H") PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_H)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("7G") PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_G)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("6F") PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_F)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("5E") PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_E)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("4D") PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_D)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("3C") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_C)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("2B") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_B)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("1A") PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_A)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("EN") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("DM") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("CL") PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE)

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("RE") PORT_CODE(KEYCODE_R) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cc1_state::reset_button), 0)
INPUT_PORTS_END

static INPUT_PORTS_START( cc3 )
	PORT_INCLUDE( cc1 )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("H8") PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_H)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("G7") PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_G)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("F6") PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_F)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("E5") PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_E)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("D4") PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_D)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("C3") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_C)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("B2") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_B)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("A1") PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_A)
INPUT_PORTS_END

static INPUT_PORTS_START( cc10c )
	PORT_INCLUDE( cc3 )

	PORT_MODIFY("IN.1")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("DM / PB") PORT_CODE(KEYCODE_M) PORT_CODE(KEYCODE_P)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void cc1_state::cc1(machine_config &config)
{
	// basic machine hardware
	I8080A(config, m_maincpu, 18_MHz_XTAL/9);
	m_maincpu->set_addrmap(AS_PROGRAM, &cc1_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &cc1_state::main_io);

	I8255(config, m_ppi8255);
	m_ppi8255->in_pa_callback().set(FUNC(cc1_state::ppi_porta_r));
	m_ppi8255->out_pb_callback().set(FUNC(cc1_state::ppi_portb_w));
	m_ppi8255->tri_pb_callback().set_constant(0);
	m_ppi8255->out_pc_callback().set(FUNC(cc1_state::ppi_portc_w));
	m_ppi8255->tri_pc_callback().set_constant(0);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(6, 7);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_fidel_cc1);
}

void cc1_state::cc3(machine_config &config)
{
	cc1(config);
	config.set_default_layout(layout_fidel_cc3);
}

void cc1_state::cc10c(machine_config &config)
{
	cc1(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &cc1_state::cc10c_map);

	config.set_default_layout(layout_fidel_cc10c);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( cc1 )
	ROM_REGION(0x10000, "maincpu", ROMREGION_ERASEFF)
	ROM_LOAD("d2316ac_011", 0x0000, 0x0800, CRC(4af38ed6) SHA1(3f07691266196f3aa5c440a40bfe4802303e7c07) )
ROM_END

ROM_START( cc3 )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD("d2332c_011", 0x0000, 0x1000, CRC(51cf4682) SHA1(197374c633a0bf1a9b7ea51a72dc2b89a6c9c508) )
ROM_END

ROM_START( cc10c )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD("ucc_10", 0x0000, 0x1000, CRC(2232c1c4) SHA1(fd282ba7ce7ac28834c860cec2cca398aec1b3f3) )
	ROM_CONTINUE(      0x2000, 0x1000 )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY, FULLNAME, FLAGS
SYST( 1977, cc1,   0,      0,      cc1,     cc1,   cc1_state, empty_init, "Fidelity Electronics", "Chess Challenger", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1977, cc3,   0,      0,      cc3,     cc3,   cc1_state, empty_init, "Fidelity Electronics", "Chess Challenger (upgraded version, 3 levels)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW ) // aka Chess Challenger 3
SYST( 1979, cc10c, 0,      0,      cc10c,   cc10c, cc1_state, empty_init, "Fidelity Electronics", "Chess Challenger (model UCC10, 10 levels)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW ) // aka Chess Challenger 10 C



cc10.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Jonathan Gevaryahu, Sandro Ronco, hap
// thanks-to:Berger, Sean Riddle
/*******************************************************************************

Fidelity CC10 / Fidelity ACR

TODO:
- What is cc10 8255 PB.7 for? When set, maximum levels is 3, like in CC3. But
  there is no CC3 with 16 buttons, and things get glitchy in this mode.

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

Fidelity Chess Challenger 10 (CCX)
----------------------------------
3 versions are known to exist: A,B,C. Strangely, version C(UCC10) has an 8080
instead of Z80 and no beeper, it's on CC1-based hardware (see cc1.cpp).

Z80A CPU @ 4MHz, NEC D8255C
4KB ROM(NEC 2332A), 2*256 bytes RAM(4*NEC 2111AL-4)
The beeper is via a 556 timer, fixed-frequency at around 1300-1400Hz.

Checker Challenger (ACR) is on the same PCB, twice less RAM and the beeper gone.
In the 1980s, Fidelity started naming it Checker Challenger "4" in some of their
advertisements, but box and manual still simply name it Checker Challenger.

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

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/bankdev.h"
#include "machine/i8255.h"
#include "machine/timer.h"
#include "sound/beep.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "fidel_acr.lh"
#include "fidel_cc10.lh"


namespace {

class ccx_state : public driver_device
{
public:
	ccx_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_mainmap(*this, "mainmap"),
		m_ppi8255(*this, "ppi8255"),
		m_display(*this, "display"),
		m_beeper_off(*this, "beeper_off"),
		m_beeper(*this, "beeper"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// RE button is tied to Z80 RESET pin
	DECLARE_INPUT_CHANGED_MEMBER(reset_button) { m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE); }

	// machine configs
	void acr(machine_config &config);
	void ccx(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<address_map_bank_device> m_mainmap;
	required_device<i8255_device> m_ppi8255;
	required_device<pwm_display_device> m_display;
	optional_device<timer_device> m_beeper_off;
	optional_device<beep_device> m_beeper;
	required_ioport_array<4> m_inputs;

	u8 m_inp_mux = 0;
	u8 m_led_select = 0;
	u8 m_7seg_data = 0;

	// address maps
	void acr_map(address_map &map) ATTR_COLD;
	void ccx_map(address_map &map) ATTR_COLD;
	void main_trampoline(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	u8 main_trampoline_r(offs_t offset);
	void main_trampoline_w(offs_t offset, u8 data);

	TIMER_DEVICE_CALLBACK_MEMBER(beeper_off) { m_beeper->set_state(0); }

	// I/O handlers
	void update_display();
	void ppi_porta_w(u8 data);
	void ppi_portb_w(u8 data);
	u8 ppi_portc_r();
	void ppi_portc_w(u8 data);
};

void ccx_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_led_select));
	save_item(NAME(m_7seg_data));
}



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

void ccx_state::update_display()
{
	// 4 7segs + 2 leds
	m_display->matrix(m_led_select, m_7seg_data);
}

void ccx_state::ppi_porta_w(u8 data)
{
	// d7: enable beeper on falling edge (556 monostable) (unpopulated on ACR)
	if (m_beeper != nullptr && ~data & m_7seg_data & 0x80 && !m_beeper_off->enabled())
	{
		m_beeper->set_state(1);
		m_beeper_off->adjust(attotime::from_msec(80)); // duration is approximate
	}

	// d0-d6: digit segment data
	m_7seg_data = bitswap<8>(data,7,0,1,2,3,4,5,6);
	update_display();
}

void ccx_state::ppi_portb_w(u8 data)
{
	// d0: lose led, d1: check(win) led
	// d2-d5: digit select
	m_led_select = bitswap<6>(data,0,1,5,4,3,2);
	update_display();
}

u8 ccx_state::ppi_portc_r()
{
	u8 data = 0;

	// d0-d3: multiplexed inputs (active low)
	for (int i = 0; i < 4; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read();

	return ~data & 0xf;
}

void ccx_state::ppi_portc_w(u8 data)
{
	// d4-d7: input mux (inverted)
	m_inp_mux = ~data >> 4 & 0xf;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void ccx_state::acr_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).rom().region("maincpu", 0); // _A12
	map(0x2000, 0x20ff).mirror(0x0f00).ram(); // A13
}

void ccx_state::ccx_map(address_map &map)
{
	acr_map(map);
	map(0x4000, 0x40ff).mirror(0x0f00).ram(); // A14
}

// PCB design is prone to bus conflicts, but assuming software behaves fine,
// it will access ROM at $0xxx, RAM at $3xxx and $5xxx
void ccx_state::main_trampoline_w(offs_t offset, u8 data)
{
	if (offset & 0x2000)
		m_mainmap->write8(offset & 0x2fff, data);
	if (offset & 0x4000)
		m_mainmap->write8(offset & 0x4fff, data);
}

u8 ccx_state::main_trampoline_r(offs_t offset)
{
	u8 data = 0xff;
	if (~offset & 0x1000)
		data &= m_mainmap->read8(offset & 0x0fff);
	if (offset & 0x2000)
		data &= m_mainmap->read8(offset & 0x2fff);
	if (offset & 0x4000)
		data &= m_mainmap->read8(offset & 0x4fff);

	return data;
}

void ccx_state::main_trampoline(address_map &map)
{
	map.global_mask(0x7fff);
	map(0x0000, 0x7fff).rw(FUNC(ccx_state::main_trampoline_r), FUNC(ccx_state::main_trampoline_w));
}

void ccx_state::main_io(address_map &map)
{
	map.global_mask(0x03);
	map(0x00, 0x03).rw(m_ppi8255, FUNC(i8255_device::read), FUNC(i8255_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( ccx )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("LV") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("A1") PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_A)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("E5") PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_E)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Speaker") PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("DM") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("B2") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_B)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("F6") PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_F)

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("CL") PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("PB") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("C3") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_C)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("G7") PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_G)

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("EN") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("PV") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("D4") PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_D)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("H8") PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_H)

	PORT_START("RESET") // is not on matrix IN.0 d0
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("RE") PORT_CODE(KEYCODE_R) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ccx_state::reset_button), 0)
INPUT_PORTS_END

static INPUT_PORTS_START( acr )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("LV") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("PV") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("CL") PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("TO") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("EN") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)

	PORT_START("RESET") // is not on matrix IN.0 d0
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("RE") PORT_CODE(KEYCODE_R) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ccx_state::reset_button), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void ccx_state::acr(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &ccx_state::main_trampoline);
	ADDRESS_MAP_BANK(config, m_mainmap).set_map(&ccx_state::acr_map).set_options(ENDIANNESS_LITTLE, 8, 16);
	m_maincpu->set_addrmap(AS_IO, &ccx_state::main_io);

	I8255(config, m_ppi8255);
	m_ppi8255->out_pa_callback().set(FUNC(ccx_state::ppi_porta_w));
	m_ppi8255->tri_pa_callback().set_constant(0);
	m_ppi8255->in_pb_callback().set_constant(0);
	m_ppi8255->out_pb_callback().set(FUNC(ccx_state::ppi_portb_w));
	m_ppi8255->tri_pb_callback().set_constant(0);
	m_ppi8255->in_pc_callback().set(FUNC(ccx_state::ppi_portc_r));
	m_ppi8255->out_pc_callback().set(FUNC(ccx_state::ppi_portc_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(6, 8);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_fidel_acr);
}

void ccx_state::ccx(machine_config &config)
{
	acr(config);

	// basic machine hardware
	m_mainmap->set_addrmap(AS_PROGRAM, &ccx_state::ccx_map);

	config.set_default_layout(layout_fidel_cc10);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	BEEP(config, m_beeper, 1360); // approximation, from 556 timer ic
	m_beeper->add_route(ALL_OUTPUTS, "speaker", 0.25);
	TIMER(config, "beeper_off").configure_generic(FUNC(ccx_state::beeper_off));
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( cc10 ) // model CCX, PCB label P241C-1
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "cn19053n_cc10b", 0x0000, 0x1000, CRC(afd3ca99) SHA1(870d09b2b52ccb8572d69642c59b5215d5fb26ab) ) // 2332
ROM_END

ROM_START( cc10a ) // model CCX
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "cc-10", 0x0000, 0x1000, CRC(853db345) SHA1(36799e204eb0aef915eb483a5f43372fca5a0fc0) ) // MCM68A332P
ROM_END


ROM_START( checkc4 ) // model ACR, PCB label P241C
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "d2332c_043", 0x0000, 0x1000, CRC(4c251d90) SHA1(474d54b05971f2a3208bab56dc6e27f03781c541) ) // no custom label
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY, FULLNAME, FLAGS
SYST( 1978, cc10,    0,      0,      ccx,     ccx,   ccx_state, empty_init, "Fidelity Electronics", "Chess Challenger \"10\" (model CCX, rev. B)", MACHINE_SUPPORTS_SAVE )
SYST( 1978, cc10a,   cc10,   0,      ccx,     ccx,   ccx_state, empty_init, "Fidelity Electronics", "Chess Challenger \"10\" (model CCX)", MACHINE_SUPPORTS_SAVE ) // aka version A

SYST( 1978, checkc4, 0,      0,      acr,     acr,   ccx_state, empty_init, "Fidelity Electronics", "Checker Challenger (model ACR, 4 levels)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )



cc40.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Jon Guidry
/*******************************************************************************

Texas Instruments Compact Computer 40 (aka CC-40)
hardware family: CC-40 -> CC-40+(unreleased) -> TI-74 BASICALC -> TI-95 PROCALC

---------------------------------------------
| ---------------------------------------   |
| |                                     |   |
| |             LCD screen              |   |
| |                                     |   ---------------
| ---------------------------------------                 |
|                                                         |
|                                                         |
|                                                         |
|                           *HD44100H                     |
|       *HD44780A00                                       |
|                                                         |
|                                                         |
|                                                         |
----|||||||||||-----------------------------|||||||||||----
    |||||||||||                             |||||||||||
----|||||||||||-----------------------------|||||||||||----
|                                                         |
|            HM6116LP-4    HM6116LP-4                     |
|                                                         |
|                                                         |
|             HM6116LP-4                    TMX70C20N2L   |
|                                      5MHz               |
|                             AMI 1041036-1               |
|             HN61256PC09                                 |
|                                             *Cartridge  |
|                                           ---------------
|                                           |
|       -------------------------------------
|*HEXBUS|
---------

HM6116LP-4    - Hitachi 2KB SRAM (newer 18KB version has two HM6264 8KB chips)
HN61256PC09   - Hitachi DIP-28 32KB CMOS Mask PROM (also seen with HN61256PB02, earlier version?)
TMX70C20N2L   - Texas Instruments TMS70C20 CPU (128 bytes RAM, 2KB ROM) @ 2.5MHz, 40 pins - "X" implies prototype
AMI 1041036-1 - 68-pin QFP AMI Gate Array
HD44100H      - 60-pin QFP Hitachi HD44100 LCD Driver
HD44780A00    - 80-pin TFP Hitachi HD44780 LCD Controller

*             - indicates that it's on the other side of the PCB


CC-40 is powered by 4 AA batteries. These will also save internal RAM,
provided that the machine is turned off properly. If a program is running,
you may have to press [BREAK] before turning the CC-40 off. If RAM contents
ends up dodgy somehow, just delete the nvram files.

Officially, minimum total RAM size is 6KB. The system will still boot with less,
but don't expect all software to work properly.

To run a cartridge, usually the command RUN "DIR" shows which program(s)
can be loaded. Load a program by pressing the [RUN] key while viewing the list,
or manually with the command RUN "<shortname of program in list>"

As for the CC-40+, the product was finalized, but in the end it wasn't released.
The hardware is very similar to CC-40. The main differences are a TMS70C40 CPU
(twice larger internal ROM), and a cassette port separate from Hexbus. The
controller chip is a TI TP0373 this time, it appears that the basic functionality
is the same as the one by AMI. Like the CC-40, it had either 6KB or 18KB RAM.

The CC-40+ cassette device number is 1, eg. SAVE"1.FILENAME" to save, and
OLD"1.FILENAME" to load.


TODO:
- external RAM cartridge (bus_control_w cartridge memory addressing)
- auto clock divider on slow memory access
- Hexbus interface and peripherals
  * HX-1000: color plotter
  * HX-1010: thermal printer
  * HX-3000: RS-232 interface
  * HX-3100: modem
  * HX-3200: Centronics printer interface

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

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/tms7000/tms7000.h"
#include "imagedev/cassette.h"
#include "machine/nvram.h"
#include "sound/dac.h"
#include "video/hd44780.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "cc40.lh"


namespace {

class cc40_state : public driver_device
{
public:
	cc40_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_nvram(*this, "sysram.%u", 1U),
		m_sysbank(*this, "sysbank"),
		m_cartbank(*this, "cartbank"),
		m_cart(*this, "cartslot"),
		m_cass(*this, "cassette"),
		m_key_matrix(*this, "IN.%u", 0),
		m_segs(*this, "seg%u", 0U)
	{
		m_sysram[0] = nullptr;
		m_sysram[1] = nullptr;
	}

	DECLARE_INPUT_CHANGED_MEMBER(sysram_size_changed);

	void cc40(machine_config &config);
	void cc40p(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	virtual void device_post_load() override;

private:
	required_device<tms7000_device> m_maincpu;
	required_device_array<nvram_device, 2> m_nvram;
	required_memory_bank m_sysbank;
	required_memory_bank m_cartbank;
	required_device<generic_slot_device> m_cart;
	optional_device<cassette_image_device> m_cass;
	required_ioport_array<8> m_key_matrix;
	output_finder<80> m_segs;

	memory_region *m_cart_rom;

	u8 m_bus_control = 0;
	u8 m_power = 0;
	u8 m_banks = 0;
	u8 m_clock_control = 0;
	u8 m_clock_divider = 0;
	u8 m_key_select = 0;

	std::unique_ptr<u8[]> m_sysram[2];
	u16 m_sysram_size[2];
	u16 m_sysram_end[2];
	u16 m_sysram_mask[2];

	void init_sysram(int chip, u16 size);
	void update_lcd_indicator(u8 y, u8 x, int state);
	void update_clock_divider();

	u8 sysram_r(offs_t offset);
	void sysram_w(offs_t offset, u8 data);
	u8 bus_control_r();
	void bus_control_w(u8 data);
	u8 power_r();
	void power_w(u8 data);
	u8 bankswitch_r();
	void bankswitch_w(u8 data);
	u8 clock_control_r();
	void clock_control_w(u8 data);
	u8 keyboard_r();
	void keyboard_w(u8 data);
	u8 cass_r();
	void cass_w(u8 data);

	void cc40_palette(palette_device &palette) const;
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
	HD44780_PIXEL_UPDATE(cc40_pixel_update);

	void cc40_map(address_map &map) ATTR_COLD;
	void cc40p_map(address_map &map) ATTR_COLD;
};



/*******************************************************************************
    Initialisation
*******************************************************************************/

void cc40_state::init_sysram(int chip, u16 size)
{
	if (m_sysram[chip] == nullptr)
	{
		// init to largest possible
		m_sysram[chip] = std::make_unique<u8[]>(0x2000);
		save_pointer(NAME(m_sysram[chip]), 0x2000, chip);

		save_item(NAME(m_sysram_size[chip]), chip);
		save_item(NAME(m_sysram_end[chip]), chip);
		save_item(NAME(m_sysram_mask[chip]), chip);
	}

	m_nvram[chip]->set_base(m_sysram[chip].get(), size);
	m_sysram_size[chip] = size;
}

void cc40_state::device_post_load()
{
	init_sysram(0, m_sysram_size[0]);
	init_sysram(1, m_sysram_size[1]);

	update_clock_divider();
}

void cc40_state::machine_start()
{
	// init
	m_segs.resolve();
	std::string region_tag;
	m_cart_rom = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());

	m_sysbank->configure_entries(0, 4, memregion("system")->base(), 0x2000);
	if (m_cart_rom)
		m_cartbank->configure_entries(0, 4, m_cart_rom->base(), 0x8000);
	else
		m_cartbank->set_base(memregion("maincpu")->base() + 0x5000);

	init_sysram(0, 0x800); // default to 6KB
	init_sysram(1, 0x800); // "

	bus_control_w(0);
	bankswitch_w(0);

	// register for savestates
	save_item(NAME(m_bus_control));
	save_item(NAME(m_power));
	save_item(NAME(m_banks));
	save_item(NAME(m_clock_control));
	save_item(NAME(m_clock_divider));
	save_item(NAME(m_key_select));
}

void cc40_state::machine_reset()
{
	m_power = 1;

	update_clock_divider();
	bankswitch_w(0);
}



/*******************************************************************************
    Cartridge
*******************************************************************************/

DEVICE_IMAGE_LOAD_MEMBER(cc40_state::cart_load)
{
	u32 const size = m_cart->common_get_size("rom");

	// max size is 4*32KB
	if (size > 0x2'0000)
		return std::make_pair(image_error::INVALIDLENGTH, "Invalid file size (must be no more than 128K)");

	m_cart->rom_alloc(0x2'0000, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE); // allocate a larger ROM region to have 4x32K banks
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}



/*******************************************************************************
    Video
*******************************************************************************/

void cc40_state::cc40_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148)); // background
	palette.set_pen_color(1, rgb_t(50, 45, 60)); // lcd pixel on
	palette.set_pen_color(2, rgb_t(131, 136, 139)); // lcd pixel off
}

void cc40_state::update_lcd_indicator(u8 y, u8 x, int state)
{
	// reference _________________...
	// output#  |10  11     12     13     14      0      1      2      3   4
	// above    | <  SHIFT  CTL    FN     DEG    RAD    GRAD   I/O    UCL  >
	// ---- raw lcd screen here ----
	// under    |    ERROR   v      v      v      v      v      v    _LOW
	// output#  |    60     61     62     63     50     51     52     53
	m_segs[y * 10 + x] = state ? 1 : 0;
}

HD44780_PIXEL_UPDATE(cc40_state::cc40_pixel_update)
{
	// char size is 5x7 + cursor
	if (x > 4 || y > 7)
		return;

	if (line == 1 && pos == 15)
	{
		// the last char is used to control the 18 lcd indicators
		update_lcd_indicator(y, x, state);
	}
	else if (line < 2 && pos < 16)
	{
		// internal: 2*16, external: 1*31
		if (y == 7) y++; // the cursor is slightly below the character
		bitmap.pix(1 + y, 1 + line*16*6 + pos*6 + x) = state ? 1 : 2;
	}
}



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

u8 cc40_state::sysram_r(offs_t offset)
{
	// read system ram, based on addressing configured in bus_control_w
	if (offset < m_sysram_end[0] && m_sysram_size[0] != 0)
		return m_sysram[0][offset & (m_sysram_size[0] - 1)];
	else if (offset < m_sysram_end[1] && m_sysram_size[1] != 0)
		return m_sysram[1][(offset - m_sysram_end[0]) & (m_sysram_size[1] - 1)];
	else
		return 0xff;
}

void cc40_state::sysram_w(offs_t offset, u8 data)
{
	// write system ram, based on addressing configured in bus_control_w
	if (offset < m_sysram_end[0] && m_sysram_size[0] != 0)
		m_sysram[0][offset & (m_sysram_size[0] - 1)] = data;
	else if (offset < m_sysram_end[1] && m_sysram_size[1] != 0)
		m_sysram[1][(offset - m_sysram_end[0]) & (m_sysram_size[1] - 1)] = data;
}

u8 cc40_state::bus_control_r()
{
	return m_bus_control;
}

void cc40_state::bus_control_w(u8 data)
{
	// d0,d1: auto enable clock divider on cartridge memory access (d0: area 1, d1: area 2)

	// d2,d3: system ram addressing
	// 00: 8K, 8K @ $1000-$2fff, $3000-$4fff
	// 01: 8K, 2K @ $1000-$2fff, $3000-$37ff
	// 10: 2K, 8K @ $1000-$17ff, $1800-$37ff
	// 11: 2K, 2K @ $1000-$17ff, $1800-$1fff
	int d2 = (data & 4) ? 0x0800 : 0x2000;
	int d3 = (data & 8) ? 0x0800 : 0x2000;
	m_sysram_end[0] = d3;
	m_sysram_mask[0] = d3 - 1;
	m_sysram_end[1] = d3 + d2;
	m_sysram_mask[1] = d2 - 1;

	// d4,d5: cartridge memory addressing
	// 00: 2K @ $5000-$57ff & $5800-$5fff
	// 01: 8K @ $5000-$6fff & $7000-$8fff
	// 10:16K @ $5000-$8fff & $9000-$cfff
	// 11: 8K @ $1000-$2fff & $3000-$4fff - system ram is disabled

	// d6: auto enable clock divider on system rom access

	// d7: unused?
	m_bus_control = data;
}

u8 cc40_state::power_r()
{
	return m_power;
}

void cc40_state::power_w(u8 data)
{
	// d0: power-on hold latch
	m_power = data & 1;

	// stop running
	if (!m_power)
		m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
}

u8 cc40_state::bankswitch_r()
{
	return m_banks;
}

void cc40_state::bankswitch_w(u8 data)
{
	data &= 0x0f;

	// d0-d1: system rom bankswitch
	m_sysbank->set_entry(data & 3);

	// d2-d3: cartridge 32KB page bankswitch
	if (m_cart_rom)
		m_cartbank->set_entry(data >> 2 & 3);

	m_banks = data;
}

u8 cc40_state::clock_control_r()
{
	return m_clock_control;
}

void cc40_state::update_clock_divider()
{
	// 2.5MHz /3 to /17 in steps of 2
	m_clock_divider = (~m_clock_control & 7) * 2 + 1;
	m_maincpu->set_clock_scale((m_clock_control & 8) ? (1.0 / (double)m_clock_divider) : 1);
}

void cc40_state::clock_control_w(u8 data)
{
	data &= 0x0f;

	// d0-d2: clock divider
	// d3: enable clock divider always
	// other bits: unused?
	if (m_clock_control != data)
	{
		m_clock_control = data;
		update_clock_divider();
	}
}

u8 cc40_state::keyboard_r()
{
	u8 data = 0;

	// read selected keyboard rows
	for (int i = 0; i < 8; i++)
	{
		if (m_key_select >> i & 1)
			data |= m_key_matrix[i]->read();
	}

	return data;
}

void cc40_state::keyboard_w(u8 data)
{
	// d0-d7: select keyboard column
	m_key_select = data;
}

u8 cc40_state::cass_r()
{
	// d3: cass data in
	return (m_cass->input() > 0.04) ? 8 : 0;
}

void cc40_state::cass_w(u8 data)
{
	// d4: cass motor
	m_cass->set_motor((data & 0x10) ? 1 : 0);

	// d3: cass data out
	m_cass->output((data & 8) ? +1.0 : -1.0);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void cc40_state::cc40_map(address_map &map)
{
	map.unmap_value_high();

	map(0x0110, 0x0110).rw(FUNC(cc40_state::bus_control_r), FUNC(cc40_state::bus_control_w));
	map(0x0111, 0x0111).rw(FUNC(cc40_state::power_r), FUNC(cc40_state::power_w));
	map(0x0112, 0x0112).noprw(); // d0-d3: Hexbus data
	map(0x0113, 0x0113).noprw(); // d0: Hexbus available
	map(0x0114, 0x0114).noprw(); // d1: Hexbus handshake
	map(0x0115, 0x0115).w("dac", FUNC(dac_bit_interface::data_w));
	map(0x0116, 0x0116).portr("BATTERY");
	map(0x0119, 0x0119).rw(FUNC(cc40_state::bankswitch_r), FUNC(cc40_state::bankswitch_w));
	map(0x011a, 0x011a).rw(FUNC(cc40_state::clock_control_r), FUNC(cc40_state::clock_control_w));
	map(0x011e, 0x011f).rw("hd44780", FUNC(hd44780_device::read), FUNC(hd44780_device::write));

	map(0x0800, 0x0fff).ram().share("sysram.0");
	map(0x1000, 0x4fff).rw(FUNC(cc40_state::sysram_r), FUNC(cc40_state::sysram_w));
	map(0x5000, 0xcfff).bankr("cartbank");
	map(0xd000, 0xefff).bankr("sysbank");
}

void cc40_state::cc40p_map(address_map &map)
{
	cc40_map(map);
	map(0x0121, 0x0121).rw(FUNC(cc40_state::cass_r), FUNC(cc40_state::cass_w));
}



/*******************************************************************************
    Inputs
*******************************************************************************/

INPUT_CHANGED_MEMBER(cc40_state::sysram_size_changed)
{
	init_sysram((int)param, newval << 11);
}

static INPUT_PORTS_START( cc40 )
	PORT_START("RAMSIZE")
	PORT_CONFNAME( 0x07, 0x01, "RAM Chip 1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cc40_state::sysram_size_changed), 0)
	PORT_CONFSETTING(    0x00, "None" )
	PORT_CONFSETTING(    0x01, "2KB" )
	PORT_CONFSETTING(    0x04, "8KB" )
	PORT_CONFNAME( 0x70, 0x10, "RAM Chip 2") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cc40_state::sysram_size_changed), 1)
	PORT_CONFSETTING(    0x00, "None" ) // note: invalid configuration, unless Chip 1 is also 0x00
	PORT_CONFSETTING(    0x10, "2KB" )
	PORT_CONFSETTING(    0x40, "8KB" )

	PORT_START("BATTERY")
	PORT_CONFNAME( 0x01, 0x01, "Battery Status" )
	PORT_CONFSETTING(    0x00, "Low" )
	PORT_CONFSETTING(    0x01, DEF_STR( Normal ) )

	// 8x8 keyboard matrix, RESET and ON buttons are not on it. Unused entries are not connected, but some might have a purpose for factory testing(?)
	// The numpad number keys are shared with the ones on the main keyboard, also on the real machine.
	// PORT_NAME lists functions under [SHIFT] as secondaries.
	PORT_START("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')

	PORT_START("IN.1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')

	PORT_START("IN.2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')

	PORT_START("IN.3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("IN.4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') PORT_NAME("SPACE")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('?')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("IN.5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0') PORT_CHAR('\'')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(END)) PORT_NAME("CLR  UCL")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CHAR(UCHAR_MAMEKEY(DEL)) PORT_NAME("LEFT  DEL")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR(UCHAR_MAMEKEY(INSERT)) PORT_NAME("RIGHT  INS")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_NAME("UP  PB")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_NAME("/")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_NAME("DOWN")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9') PORT_CHAR(')')

	PORT_START("IN.6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR('+') PORT_NAME("+")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13) PORT_NAME("ENTER")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR('*') PORT_NAME("*")

	PORT_START("IN.7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2) PORT_NAME("CTL")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PORT_NAME("SHIFT")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(PAUSE)) PORT_NAME("BREAK")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_NAME("RUN")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_NAME("FN")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PGDN) PORT_CHAR(UCHAR_MAMEKEY(PGDN)) PORT_NAME("OFF")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void cc40_state::cc40(machine_config &config)
{
	// basic machine hardware
	TMS70C20(config, m_maincpu, 5_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &cc40_state::cc40_map);
	m_maincpu->in_porta().set(FUNC(cc40_state::keyboard_r));
	m_maincpu->out_portb().set(FUNC(cc40_state::keyboard_w));

	NVRAM(config, "sysram.0", nvram_device::DEFAULT_ALL_0);
	NVRAM(config, "sysram.1", nvram_device::DEFAULT_ALL_0);
	NVRAM(config, "sysram.2", nvram_device::DEFAULT_ALL_0);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60); // arbitrary
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.set_size(6*31+1, 9*1+1+1);
	screen.set_visarea_full();
	config.set_default_layout(layout_cc40);
	screen.set_screen_update("hd44780", FUNC(hd44780_device::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(cc40_state::cc40_palette), 3);

	hd44780_device &hd44780(HD44780(config, "hd44780", 270'000)); // OSC = 91K resistor
	hd44780.set_lcd_size(2, 16); // 2*16 internal
	hd44780.set_pixel_update_cb(FUNC(cc40_state::cc40_pixel_update));

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, "dac").add_route(ALL_OUTPUTS, "speaker", 0.25);

	// cartridge
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "cc40_cart", "bin,rom,256").set_device_load(FUNC(cc40_state::cart_load));
	SOFTWARE_LIST(config, "cart_list").set_original("cc40_cart");
}

void cc40_state::cc40p(machine_config &config)
{
	cc40(config);

	// basic machine hardware
	TMS70C40(config.replace(), m_maincpu, 5_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &cc40_state::cc40p_map);
	m_maincpu->in_porta().set(FUNC(cc40_state::keyboard_r));
	m_maincpu->out_portb().set(FUNC(cc40_state::keyboard_w));

	// cassette
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_MUTED | CASSETTE_MOTOR_DISABLED);
	SPEAKER(config, "cass_output").front_center(); // on data recorder
	m_cass->add_route(ALL_OUTPUTS, "cass_output", 0.05);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( cc40 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "c11002", 0x0000, 0x0800, CRC(a21bf6ab) SHA1(3da8435ecbee143e7fa149ee8e1c92949bade1d8) ) // internal cpu rom

	ROM_REGION( 0x8000, "system", 0 )
	ROM_LOAD( "hn61256pc09", 0x0000, 0x8000, CRC(f5322fab) SHA1(1b5c4052a53654363c458f75eac7a27f0752def6) ) // system rom, banked
ROM_END

ROM_START( cc40p )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "75305.u200", 0x0000, 0x1000, CRC(42bb6af2) SHA1(642dede16cb4ef2c5b9eaae79e28054f1111eef8) ) // internal cpu rom

	ROM_REGION( 0x8000, "system", 0 )
	ROM_LOAD( "hn61256pc09.u205", 0x0000, 0x8000, CRC(f5322fab) SHA1(1b5c4052a53654363c458f75eac7a27f0752def6) ) // system rom, banked
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY              FULLNAME               FLAGS
SYST( 1983, cc40,  0,     0,      cc40,    cc40,  cc40_state, empty_init, "Texas Instruments", "Compact Computer 40", MACHINE_SUPPORTS_SAVE )
SYST( 1984, cc40p, cc40,  0,      cc40p,   cc40,  cc40_state, empty_init, "Texas Instruments", "Compact Computer 40 Plus (prototype)", MACHINE_SUPPORTS_SAVE )



cc7.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Fidelity Chess Challenger 7 (BCC)
---------------------------------
It was Fidelity's most sold chess computer. The first version was released in
1979, and a newer PCB revision was produced in 1980.

Zilog Z80A, 3.579MHz from XTAL
Z80 IRQ/NMI unused, no timer IC.
This is a cost-reduced design from CC10, no special I/O chips.

Known ROM labels (long production run, so there are several revisions):
CN19064N BCC, CN19103N BCC-REVB, 101-32016, 101-1005C01

Backgammon Challenger (BKC) is the same PCB, with the speaker connection going
to the display panel instead.

CC7 (BCC) was also bootlegged around 1981 by Splice Industria Brasileira,
as "Byte XD-300". Mostek MK3880N-4 @ 4MHz, ROM contents is same as BCC REVB.

RE information from netlist by Berger (1st version PCB)

Memory map:
-----------
0000-0FFF: 4K 2332 ROM
2000-2FFF: ROM/RAM bus conflict!
3000-3FFF: 256 bytes RAM (2111 SRAM x2)
4000-FFFF: Z80 A14/A15 not connected

Port map (Write):
-----------------
D0-D3: digit select and keypad mux
D4: CHECK led
D5: LOSE led
A0-A2: NE591 A0-A2
D7: NE591 D (_C not used)
NE591 Q0-Q6: digit segments A-G
NE591 Q7: buzzer

Port map (Read):
----------------
D0-D3: keypad row

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

#include "emu.h"

#include "cpu/z80/z80.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "fidel_bkc.lh"
#include "fidel_cc7.lh"


namespace {

class cc7_state : public driver_device
{
public:
	cc7_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// machine configs
	void cc7(machine_config &config);
	void bkc(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	optional_device<dac_1bit_device> m_dac;
	required_ioport_array<4> m_inputs;

	u8 m_inp_mux = 0;
	u8 m_7seg_data = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	// I/O handlers
	u8 input_r();
	void control_w(offs_t offset, u8 data);
};

void cc7_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_7seg_data));
}



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

void cc7_state::control_w(offs_t offset, u8 data)
{
	// a0-a2,d7: digit segment data via NE591
	u8 mask = 1 << (offset & 7);
	m_7seg_data = (m_7seg_data & ~mask) | ((data & 0x80) ? mask : 0);

	// BCC: NE591 Q7 is speaker out
	if (m_dac != nullptr)
		m_dac->write(BIT(m_7seg_data, 7));

	// d0-d3: led select, input mux
	// d4,d5: upper leds(direct)
	m_display->matrix(data & 0x3f, m_7seg_data);
	m_inp_mux = data & 0xf;
}

u8 cc7_state::input_r()
{
	u8 data = 0;

	// d0-d3: multiplexed inputs
	for (int i = 0; i < 4; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read();

	return data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void cc7_state::main_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x3fff);
	map(0x0000, 0x0fff).rom();
	map(0x3000, 0x30ff).mirror(0x0f00).ram();
}

void cc7_state::main_io(address_map &map)
{
	map.global_mask(0x07);
	map(0x00, 0x07).rw(FUNC(cc7_state::input_r), FUNC(cc7_state::control_w));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( cc7 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("EN") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("PV") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("D4") PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_D)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("H8") PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_H)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("CL") PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("PB") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("C3") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_C)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("G7") PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_G)

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("CB") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("DM") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("B2") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_B)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("F6") PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_F)

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("RE") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("LV") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("A1") PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_A)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("E5") PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_E)
INPUT_PORTS_END

static INPUT_PORTS_START( bkc )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("EN") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("CL") PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("GM") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("RE") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("PB") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("PV") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void cc7_state::bkc(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 3.579545_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &cc7_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &cc7_state::main_io);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(6, 8);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_fidel_bkc);
}

void cc7_state::cc7(machine_config &config)
{
	bkc(config);
	config.set_default_layout(layout_fidel_cc7);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( cc7 ) // PCB label 510-380
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "101-32016", 0x0000, 0x1000, CRC(b9076c52) SHA1(09b17ac6cd6a1c5c62aea3649f3367bcf4405598) ) // 2332
ROM_END

ROM_START( cc7a )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "cn19103n_bcc-revb", 0x0000, 0x1000, CRC(a397d471) SHA1(9b12bc442fccee40f4d8500c792bc9d886c5e1a5) ) // 2332
ROM_END


ROM_START( backgamc ) // model BKC, PCB label P-380A-5
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "cn19255n_101-32012", 0x0000, 0x1000, CRC(0a8a19b7) SHA1(d6f0dd44b33c9b79570cf0ceac02a036ec91ba57) ) // 2332
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY, FULLNAME, FLAGS
SYST( 1980, cc7,      0,      0,      cc7,     cc7,   cc7_state, empty_init, "Fidelity Electronics", "Chess Challenger \"7\" (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1979, cc7a,     cc7,    0,      cc7,     cc7,   cc7_state, empty_init, "Fidelity Electronics", "Chess Challenger \"7\" (set 2)", MACHINE_SUPPORTS_SAVE )

SYST( 1979, backgamc, 0,      0,      bkc,     bkc,   cc7_state, empty_init, "Fidelity Electronics", "Backgammon Challenger", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )



ccs2810.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert,AJR
/***************************************************************************

CCS Model 2810

2009-12-11 Skeleton driver.
2011-06-02 Connected to a terminal

Chips: INS8250N-B, Z80A, uPD2716D, 82S129. Crystals: 16 MHz, 1.8432MHz

SYSTEM OPERATION:
Press Enter up to three times to start the system.
Supported baud rates include 50, 75, 110, 134.5, 150, 200, 300, 600, 1200,
1800, 2000, 2400, 3600, 7200, 9600, 14400, 38400, 57600 and 115200.
All commands are in uppercase.

A    Assign logical device
Dn,n Dump memory
E    Punch End-of-File to paper tape
F    Fill
G    Go
H    Hex arithmetic
I    In
L    Punch Leader to paper tape
M    Move
O    Out
Q    Query logical devices
R    Read a file from paper tape
S    Edit memory
T    Test memory
V    Verify (compare 2 blocks of memory)
W    Write a file to paper tape
X    Examine Registers
Y    Set Baud rate of i8250
Z    Zleep (lock terminal). Press control+G twice to unlock.

*****************************************************************************

CCS Model 2422B

Chips: UB1793/MB8877, 3 proms and one eprom. Crystal = 16MHz

SYSTEM OPERATION:
Same as above, plus some extra commands

B    Boot from floppy
L    removed
P    Set disk parameters e.g. P0 10 0 = drive A, 10 sectors per track, 1 sided
Q    Set disk position for raw read/write e.g. Q6 0 9 = track 6, side 0, sector 9
Rs f Read absolute disk data (set by P and Q) to memory range s to f
Ws f Write absolute disk data (set by P and Q) from memory range s to f

ToDo:
- fdc hld_r() has no code behind it, to be written
- not sure of the polarities of some of the floppy/fdc signals (not documented)
- no obvious option to change the fdc clock
- currently hard coded for a 20cm drive, not sure how to option select drive sizes
- a few jumpers to be added
- the one disk that exists fails the test:
  Incorrect layout on track 0 head 0, expected_size=41666, current_size=68144

*****************************************************************************

CCS Model 300 / 400

2009-12-11 Skeleton driver.

It requires a floppy disk to boot from.

Early on, it does a read from port F2. If bit 3 is low, the system becomes
a Model 400.

The CPU board appears to be similar to the 2820 System Processor, which has
Z80A CTC, Z80A PIO, Z80A SIO/0 and Z80A DMA peripherals on board. Several
features, including IEI/IEO daisy chain priority, are jumper-configurable.

However, the 2820 has the i/o ports rearranged slightly (even though the
manual says it should work!), and no fdc support.

ToDo:
- Using the 2422's FDC, since the ports are the same
- As before, the only disks that exist cause an unexpected exit.

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

#include "emu.h"
#include "cpu/z80/z80.h"
#include "bus/rs232/rs232.h"
//#include "bus/s100/s100.h"
#include "imagedev/floppy.h"
#include "machine/ins8250.h"
#include "machine/ram.h"
#include "machine/wd_fdc.h"
#include "machine/z80daisy.h"
#include "machine/z80ctc.h"
#include "machine/z80dma.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"


namespace {

class ccs_state : public driver_device
{
public:
	ccs_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_rom(*this, "maincpu")
		, m_ins8250(*this, "ins8250")
		, m_fdc(*this, "fdc")
		, m_floppy0(*this, "fdc:0")
		, m_jump_addr_sel(*this, {"ADDRLO", "ADDRHI"})
		, m_ser_addr_sel(*this, "SERADDR")
		, m_jump_en(*this, "JMPEN")
		, m_rom_en(*this, "ROMEN")
		, m_ser_en(*this, "SEREN")
	{ }

	void ccs2810(machine_config &config);
	void ccs2422(machine_config &config);

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

	u8 port04_r();
	u8 port34_r();
	void port04_w(u8 data);
	void port34_w(u8 data);

	bool m_ss = 0;
	bool m_dden = 0;
	bool m_dsize = 0;
	u8 m_ds = 0U;
	floppy_image_device *m_floppy;

	required_device<z80_device> m_maincpu;
	optional_device<ram_device> m_ram;
	required_region_ptr<u8> m_rom;
	optional_device<ins8250_device> m_ins8250;
	optional_device<mb8877_device> m_fdc;
	optional_device<floppy_connector> m_floppy0;
	optional_ioport_array<2> m_jump_addr_sel;
	optional_ioport m_ser_addr_sel;
	optional_ioport m_jump_en;
	optional_ioport m_rom_en;
	optional_ioport m_ser_en;

private:
	u8 memory_read(offs_t offset);
	void memory_write(offs_t offset, u8 data);
	u8 io_read(offs_t offset);
	void io_write(offs_t offset, u8 data);

	void port40_w(u8 data);

	void ccs2422_io(address_map &map) ATTR_COLD;
	void ccs2810_io(address_map &map) ATTR_COLD;
	void ccs2810_mem(address_map &map) ATTR_COLD;

	u8 m_power_on_status = 0U;
};

class ccs300_state : public ccs_state
{
public:
	ccs300_state(const machine_config &mconfig, device_type type, const char *tag)
		: ccs_state(mconfig, type, tag)
		, m_ram1(*this, "mainram")
		, m_bank1(*this, "bank1")
	{ }

	void ccs300(machine_config &config);

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

private:
	void ccs300_io(address_map &map) ATTR_COLD;
	void ccs300_mem(address_map &map) ATTR_COLD;
	void port40_w(u8 data);
	required_shared_ptr<u8> m_ram1;
	required_memory_bank    m_bank1;
};

u8 ccs_state::memory_read(offs_t offset)
{
	u8 result = m_ram->read(offset);

	if (!BIT(m_power_on_status, 0))
	{
		if (BIT(m_power_on_status, 1))
			result = m_jump_addr_sel[1]->read();
		else
			result = !BIT(m_power_on_status, 2) ? 0xc3 : m_jump_addr_sel[0]->read();
	}
	else if ((offset & 0xf800) == 0xf000 && m_rom_en->read() == 0)
	{
		result = m_rom[offset & 0x7ff];

		// wait state forced for 4 MHz operation
		if (!machine().side_effects_disabled())
			m_maincpu->adjust_icount(-1);
	}

	if (!machine().side_effects_disabled())
		m_power_on_status |= m_power_on_status >> 1;

	return result;
}

void ccs_state::memory_write(offs_t offset, u8 data)
{
	m_ram->write(offset, data);
}

u8 ccs_state::io_read(offs_t offset)
{
	// A7-A3 are compared against jumper settings
	if (m_ser_en->read() && (offset & 0x00f8) == m_ser_addr_sel->read())
		return m_ins8250->ins8250_r(offset & 7);

	return 0xff;
}

void ccs_state::io_write(offs_t offset, u8 data)
{
	// A7-A3 are compared against jumper settings
	if (m_ser_en->read() && (offset & 0x00f8) == m_ser_addr_sel->read())
		m_ins8250->ins8250_w(offset & 7, data);
}

void ccs_state::ccs2810_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(ccs_state::memory_read), FUNC(ccs_state::memory_write));
}

void ccs_state::ccs2810_io(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(ccs_state::io_read), FUNC(ccs_state::io_write));
}

void ccs_state::ccs2422_io(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(ccs_state::io_read), FUNC(ccs_state::io_write));
	map(0x04, 0x04).mirror(0xff00).rw(FUNC(ccs_state::port04_r), FUNC(ccs_state::port04_w));
	map(0x30, 0x33).mirror(0xff00).rw(m_fdc, FUNC(mb8877_device::read), FUNC(mb8877_device::write));
	map(0x34, 0x34).mirror(0xff00).rw(FUNC(ccs_state::port34_r), FUNC(ccs_state::port34_w));
	map(0x40, 0x40).mirror(0xff00).w(FUNC(ccs_state::port40_w));
}

void ccs300_state::ccs300_mem(address_map &map)
{
	map(0x0000, 0xffff).ram().share("mainram");
	map(0x0000, 0x07ff).bankr("bank1");
}

void ccs300_state::ccs300_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x04, 0x04).rw(FUNC(ccs300_state::port04_r), FUNC(ccs300_state::port04_w));
	map(0x10, 0x13).rw("sio", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x14, 0x17).rw("pio", FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x18, 0x1b).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x30, 0x33).rw(m_fdc, FUNC(mb8877_device::read), FUNC(mb8877_device::write));
	map(0x34, 0x34).rw(FUNC(ccs300_state::port34_r), FUNC(ccs300_state::port34_w));
	map(0x40, 0x40).w(FUNC(ccs300_state::port40_w));
	map(0xf0, 0xf0).rw("dma", FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	map(0xf2, 0xf2).portr("MODEL"); // dip or jumper? only used by CCS-400
}


/* Input ports */
static INPUT_PORTS_START( ccs2810 )
	PORT_START("ROMEN")
	PORT_DIPNAME(1, 0, "Enable On-board ROM") PORT_DIPLOCATION("ROM EN:1")
	PORT_DIPSETTING(1, DEF_STR(Off))
	PORT_DIPSETTING(0, DEF_STR(On))

	PORT_START("JMPEN")
	PORT_DIPNAME(1, 0, "Enable Power-on Jump") PORT_DIPLOCATION("JMP EN:1")
	PORT_DIPSETTING(1, DEF_STR(Off))
	PORT_DIPSETTING(0, DEF_STR(On))

	PORT_START("ADDRLO")
	PORT_DIPNAME(0xff, 0x00, "Power-on Jump Address (Low)") PORT_DIPLOCATION("JA:!16,!15,!14,!13,!12,!11,!10,!9")
	PORT_DIPSETTING(0x00, "XX00h")
	PORT_DIPSETTING(0x01, "XX01h")
	PORT_DIPSETTING(0x02, "XX02h")
	PORT_DIPSETTING(0x03, "XX03h")
	PORT_DIPSETTING(0x04, "XX04h")
	PORT_DIPSETTING(0x05, "XX05h")
	PORT_DIPSETTING(0x06, "XX06h")
	PORT_DIPSETTING(0x07, "XX07h")
	PORT_DIPSETTING(0x08, "XX08h")
	PORT_DIPSETTING(0x09, "XX09h")
	PORT_DIPSETTING(0x0a, "XX0Ah")
	PORT_DIPSETTING(0x0b, "XX0Bh")
	PORT_DIPSETTING(0x0c, "XX0Ch")
	PORT_DIPSETTING(0x0d, "XX0Dh")
	PORT_DIPSETTING(0x0e, "XX0Eh")
	PORT_DIPSETTING(0x0f, "XX0Fh")
	PORT_DIPSETTING(0x10, "XX10h")
	PORT_DIPSETTING(0x11, "XX11h")
	PORT_DIPSETTING(0x12, "XX12h")
	PORT_DIPSETTING(0x13, "XX13h")
	PORT_DIPSETTING(0x14, "XX14h")
	PORT_DIPSETTING(0x15, "XX15h")
	PORT_DIPSETTING(0x16, "XX16h")
	PORT_DIPSETTING(0x17, "XX17h")
	PORT_DIPSETTING(0x18, "XX18h")
	PORT_DIPSETTING(0x19, "XX19h")
	PORT_DIPSETTING(0x1a, "XX1Ah")
	PORT_DIPSETTING(0x1b, "XX1Bh")
	PORT_DIPSETTING(0x1c, "XX1Ch")
	PORT_DIPSETTING(0x1d, "XX1Dh")
	PORT_DIPSETTING(0x1e, "XX1Eh")
	PORT_DIPSETTING(0x1f, "XX1Fh")
	PORT_DIPSETTING(0x20, "XX20h")
	PORT_DIPSETTING(0x21, "XX21h")
	PORT_DIPSETTING(0x22, "XX22h")
	PORT_DIPSETTING(0x23, "XX23h")
	PORT_DIPSETTING(0x24, "XX24h")
	PORT_DIPSETTING(0x25, "XX25h")
	PORT_DIPSETTING(0x26, "XX26h")
	PORT_DIPSETTING(0x27, "XX27h")
	PORT_DIPSETTING(0x28, "XX28h")
	PORT_DIPSETTING(0x29, "XX29h")
	PORT_DIPSETTING(0x2a, "XX2Ah")
	PORT_DIPSETTING(0x2b, "XX2Bh")
	PORT_DIPSETTING(0x2c, "XX2Ch")
	PORT_DIPSETTING(0x2d, "XX2Dh")
	PORT_DIPSETTING(0x2e, "XX2Eh")
	PORT_DIPSETTING(0x2f, "XX2Fh")
	PORT_DIPSETTING(0x30, "XX30h")
	PORT_DIPSETTING(0x31, "XX31h")
	PORT_DIPSETTING(0x32, "XX32h")
	PORT_DIPSETTING(0x33, "XX33h")
	PORT_DIPSETTING(0x34, "XX34h")
	PORT_DIPSETTING(0x35, "XX35h")
	PORT_DIPSETTING(0x36, "XX36h")
	PORT_DIPSETTING(0x37, "XX37h")
	PORT_DIPSETTING(0x38, "XX38h")
	PORT_DIPSETTING(0x39, "XX39h")
	PORT_DIPSETTING(0x3a, "XX3Ah")
	PORT_DIPSETTING(0x3b, "XX3Bh")
	PORT_DIPSETTING(0x3c, "XX3Ch")
	PORT_DIPSETTING(0x3d, "XX3Dh")
	PORT_DIPSETTING(0x3e, "XX3Eh")
	PORT_DIPSETTING(0x3f, "XX3Fh")
	PORT_DIPSETTING(0x40, "XX40h")
	PORT_DIPSETTING(0x41, "XX41h")
	PORT_DIPSETTING(0x42, "XX42h")
	PORT_DIPSETTING(0x43, "XX43h")
	PORT_DIPSETTING(0x44, "XX44h")
	PORT_DIPSETTING(0x45, "XX45h")
	PORT_DIPSETTING(0x46, "XX46h")
	PORT_DIPSETTING(0x47, "XX47h")
	PORT_DIPSETTING(0x48, "XX48h")
	PORT_DIPSETTING(0x49, "XX49h")
	PORT_DIPSETTING(0x4a, "XX4Ah")
	PORT_DIPSETTING(0x4b, "XX4Bh")
	PORT_DIPSETTING(0x4c, "XX4Ch")
	PORT_DIPSETTING(0x4d, "XX4Dh")
	PORT_DIPSETTING(0x4e, "XX4Eh")
	PORT_DIPSETTING(0x4f, "XX4Fh")
	PORT_DIPSETTING(0x50, "XX50h")
	PORT_DIPSETTING(0x51, "XX51h")
	PORT_DIPSETTING(0x52, "XX52h")
	PORT_DIPSETTING(0x53, "XX53h")
	PORT_DIPSETTING(0x54, "XX54h")
	PORT_DIPSETTING(0x55, "XX55h")
	PORT_DIPSETTING(0x56, "XX56h")
	PORT_DIPSETTING(0x57, "XX57h")
	PORT_DIPSETTING(0x58, "XX58h")
	PORT_DIPSETTING(0x59, "XX59h")
	PORT_DIPSETTING(0x5a, "XX5Ah")
	PORT_DIPSETTING(0x5b, "XX5Bh")
	PORT_DIPSETTING(0x5c, "XX5Ch")
	PORT_DIPSETTING(0x5d, "XX5Dh")
	PORT_DIPSETTING(0x5e, "XX5Eh")
	PORT_DIPSETTING(0x5f, "XX5Fh")
	PORT_DIPSETTING(0x60, "XX60h")
	PORT_DIPSETTING(0x61, "XX61h")
	PORT_DIPSETTING(0x62, "XX62h")
	PORT_DIPSETTING(0x63, "XX63h")
	PORT_DIPSETTING(0x64, "XX64h")
	PORT_DIPSETTING(0x65, "XX65h")
	PORT_DIPSETTING(0x66, "XX66h")
	PORT_DIPSETTING(0x67, "XX67h")
	PORT_DIPSETTING(0x68, "XX68h")
	PORT_DIPSETTING(0x69, "XX69h")
	PORT_DIPSETTING(0x6a, "XX6Ah")
	PORT_DIPSETTING(0x6b, "XX6Bh")
	PORT_DIPSETTING(0x6c, "XX6Ch")
	PORT_DIPSETTING(0x6d, "XX6Dh")
	PORT_DIPSETTING(0x6e, "XX6Eh")
	PORT_DIPSETTING(0x6f, "XX6Fh")
	PORT_DIPSETTING(0x70, "XX70h")
	PORT_DIPSETTING(0x71, "XX71h")
	PORT_DIPSETTING(0x72, "XX72h")
	PORT_DIPSETTING(0x73, "XX73h")
	PORT_DIPSETTING(0x74, "XX74h")
	PORT_DIPSETTING(0x75, "XX75h")
	PORT_DIPSETTING(0x76, "XX76h")
	PORT_DIPSETTING(0x77, "XX77h")
	PORT_DIPSETTING(0x78, "XX78h")
	PORT_DIPSETTING(0x79, "XX79h")
	PORT_DIPSETTING(0x7a, "XX7Ah")
	PORT_DIPSETTING(0x7b, "XX7Bh")
	PORT_DIPSETTING(0x7c, "XX7Ch")
	PORT_DIPSETTING(0x7d, "XX7Dh")
	PORT_DIPSETTING(0x7e, "XX7Eh")
	PORT_DIPSETTING(0x7f, "XX7Fh")
	PORT_DIPSETTING(0x80, "XX80h")
	PORT_DIPSETTING(0x81, "XX81h")
	PORT_DIPSETTING(0x82, "XX82h")
	PORT_DIPSETTING(0x83, "XX83h")
	PORT_DIPSETTING(0x84, "XX84h")
	PORT_DIPSETTING(0x85, "XX85h")
	PORT_DIPSETTING(0x86, "XX86h")
	PORT_DIPSETTING(0x87, "XX87h")
	PORT_DIPSETTING(0x88, "XX88h")
	PORT_DIPSETTING(0x89, "XX89h")
	PORT_DIPSETTING(0x8a, "XX8Ah")
	PORT_DIPSETTING(0x8b, "XX8Bh")
	PORT_DIPSETTING(0x8c, "XX8Ch")
	PORT_DIPSETTING(0x8d, "XX8Dh")
	PORT_DIPSETTING(0x8e, "XX8Eh")
	PORT_DIPSETTING(0x8f, "XX8Fh")
	PORT_DIPSETTING(0x90, "XX90h")
	PORT_DIPSETTING(0x91, "XX91h")
	PORT_DIPSETTING(0x92, "XX92h")
	PORT_DIPSETTING(0x93, "XX93h")
	PORT_DIPSETTING(0x94, "XX94h")
	PORT_DIPSETTING(0x95, "XX95h")
	PORT_DIPSETTING(0x96, "XX96h")
	PORT_DIPSETTING(0x97, "XX97h")
	PORT_DIPSETTING(0x98, "XX98h")
	PORT_DIPSETTING(0x99, "XX99h")
	PORT_DIPSETTING(0x9a, "XX9Ah")
	PORT_DIPSETTING(0x9b, "XX9Bh")
	PORT_DIPSETTING(0x9c, "XX9Ch")
	PORT_DIPSETTING(0x9d, "XX9Dh")
	PORT_DIPSETTING(0x9e, "XX9Eh")
	PORT_DIPSETTING(0x9f, "XX9Fh")
	PORT_DIPSETTING(0xa0, "XXA0h")
	PORT_DIPSETTING(0xa1, "XXA1h")
	PORT_DIPSETTING(0xa2, "XXA2h")
	PORT_DIPSETTING(0xa3, "XXA3h")
	PORT_DIPSETTING(0xa4, "XXA4h")
	PORT_DIPSETTING(0xa5, "XXA5h")
	PORT_DIPSETTING(0xa6, "XXA6h")
	PORT_DIPSETTING(0xa7, "XXA7h")
	PORT_DIPSETTING(0xa8, "XXA8h")
	PORT_DIPSETTING(0xa9, "XXA9h")
	PORT_DIPSETTING(0xaa, "XXAAh")
	PORT_DIPSETTING(0xab, "XXABh")
	PORT_DIPSETTING(0xac, "XXACh")
	PORT_DIPSETTING(0xad, "XXADh")
	PORT_DIPSETTING(0xae, "XXAEh")
	PORT_DIPSETTING(0xaf, "XXAFh")
	PORT_DIPSETTING(0xb0, "XXB0h")
	PORT_DIPSETTING(0xb1, "XXB1h")
	PORT_DIPSETTING(0xb2, "XXB2h")
	PORT_DIPSETTING(0xb3, "XXB3h")
	PORT_DIPSETTING(0xb4, "XXB4h")
	PORT_DIPSETTING(0xb5, "XXB5h")
	PORT_DIPSETTING(0xb6, "XXB6h")
	PORT_DIPSETTING(0xb7, "XXB7h")
	PORT_DIPSETTING(0xb8, "XXB8h")
	PORT_DIPSETTING(0xb9, "XXB9h")
	PORT_DIPSETTING(0xba, "XXBAh")
	PORT_DIPSETTING(0xbb, "XXBBh")
	PORT_DIPSETTING(0xbc, "XXBCh")
	PORT_DIPSETTING(0xbd, "XXBDh")
	PORT_DIPSETTING(0xbe, "XXBEh")
	PORT_DIPSETTING(0xbf, "XXBFh")
	PORT_DIPSETTING(0xc0, "XXC0h")
	PORT_DIPSETTING(0xc1, "XXC1h")
	PORT_DIPSETTING(0xc2, "XXC2h")
	PORT_DIPSETTING(0xc3, "XXC3h")
	PORT_DIPSETTING(0xc4, "XXC4h")
	PORT_DIPSETTING(0xc5, "XXC5h")
	PORT_DIPSETTING(0xc6, "XXC6h")
	PORT_DIPSETTING(0xc7, "XXC7h")
	PORT_DIPSETTING(0xc8, "XXC8h")
	PORT_DIPSETTING(0xc9, "XXC9h")
	PORT_DIPSETTING(0xca, "XXCAh")
	PORT_DIPSETTING(0xcb, "XXCBh")
	PORT_DIPSETTING(0xcc, "XXCCh")
	PORT_DIPSETTING(0xcd, "XXCDh")
	PORT_DIPSETTING(0xce, "XXCEh")
	PORT_DIPSETTING(0xcf, "XXCFh")
	PORT_DIPSETTING(0xd0, "XXD0h")
	PORT_DIPSETTING(0xd1, "XXD1h")
	PORT_DIPSETTING(0xd2, "XXD2h")
	PORT_DIPSETTING(0xd3, "XXD3h")
	PORT_DIPSETTING(0xd4, "XXD4h")
	PORT_DIPSETTING(0xd5, "XXD5h")
	PORT_DIPSETTING(0xd6, "XXD6h")
	PORT_DIPSETTING(0xd7, "XXD7h")
	PORT_DIPSETTING(0xd8, "XXD8h")
	PORT_DIPSETTING(0xd9, "XXD9h")
	PORT_DIPSETTING(0xda, "XXDAh")
	PORT_DIPSETTING(0xdb, "XXDBh")
	PORT_DIPSETTING(0xdc, "XXDCh")
	PORT_DIPSETTING(0xdd, "XXDDh")
	PORT_DIPSETTING(0xde, "XXDEh")
	PORT_DIPSETTING(0xdf, "XXDFh")
	PORT_DIPSETTING(0xe0, "XXE0h")
	PORT_DIPSETTING(0xe1, "XXE1h")
	PORT_DIPSETTING(0xe2, "XXE2h")
	PORT_DIPSETTING(0xe3, "XXE3h")
	PORT_DIPSETTING(0xe4, "XXE4h")
	PORT_DIPSETTING(0xe5, "XXE5h")
	PORT_DIPSETTING(0xe6, "XXE6h")
	PORT_DIPSETTING(0xe7, "XXE7h")
	PORT_DIPSETTING(0xe8, "XXE8h")
	PORT_DIPSETTING(0xe9, "XXE9h")
	PORT_DIPSETTING(0xea, "XXEAh")
	PORT_DIPSETTING(0xeb, "XXEBh")
	PORT_DIPSETTING(0xec, "XXECh")
	PORT_DIPSETTING(0xed, "XXEDh")
	PORT_DIPSETTING(0xee, "XXEEh")
	PORT_DIPSETTING(0xef, "XXEFh")
	PORT_DIPSETTING(0xf0, "XXF0h")
	PORT_DIPSETTING(0xf1, "XXF1h")
	PORT_DIPSETTING(0xf2, "XXF2h")
	PORT_DIPSETTING(0xf3, "XXF3h")
	PORT_DIPSETTING(0xf4, "XXF4h")
	PORT_DIPSETTING(0xf5, "XXF5h")
	PORT_DIPSETTING(0xf6, "XXF6h")
	PORT_DIPSETTING(0xf7, "XXF7h")
	PORT_DIPSETTING(0xf8, "XXF8h")
	PORT_DIPSETTING(0xf9, "XXF9h")
	PORT_DIPSETTING(0xfa, "XXFAh")
	PORT_DIPSETTING(0xfb, "XXFBh")
	PORT_DIPSETTING(0xfc, "XXFCh")
	PORT_DIPSETTING(0xfd, "XXFDh")
	PORT_DIPSETTING(0xfe, "XXFEh")
	PORT_DIPSETTING(0xff, "XXFFh")

	PORT_START("ADDRHI")
	PORT_DIPNAME(0xff, 0xf0, "Power-on Jump Address (High)") PORT_DIPLOCATION("JA:!8,!7,!6,!5,!4,!3,!2,!1")
	PORT_DIPSETTING(0x00, "00XXh")
	PORT_DIPSETTING(0x01, "01XXh")
	PORT_DIPSETTING(0x02, "02XXh")
	PORT_DIPSETTING(0x03, "03XXh")
	PORT_DIPSETTING(0x04, "04XXh")
	PORT_DIPSETTING(0x05, "05XXh")
	PORT_DIPSETTING(0x06, "06XXh")
	PORT_DIPSETTING(0x07, "07XXh")
	PORT_DIPSETTING(0x08, "08XXh")
	PORT_DIPSETTING(0x09, "09XXh")
	PORT_DIPSETTING(0x0a, "0AXXh")
	PORT_DIPSETTING(0x0b, "0BXXh")
	PORT_DIPSETTING(0x0c, "0CXXh")
	PORT_DIPSETTING(0x0d, "0DXXh")
	PORT_DIPSETTING(0x0e, "0EXXh")
	PORT_DIPSETTING(0x0f, "0FXXh")
	PORT_DIPSETTING(0x10, "10XXh")
	PORT_DIPSETTING(0x11, "11XXh")
	PORT_DIPSETTING(0x12, "12XXh")
	PORT_DIPSETTING(0x13, "13XXh")
	PORT_DIPSETTING(0x14, "14XXh")
	PORT_DIPSETTING(0x15, "15XXh")
	PORT_DIPSETTING(0x16, "16XXh")
	PORT_DIPSETTING(0x17, "17XXh")
	PORT_DIPSETTING(0x18, "18XXh")
	PORT_DIPSETTING(0x19, "19XXh")
	PORT_DIPSETTING(0x1a, "1AXXh")
	PORT_DIPSETTING(0x1b, "1BXXh")
	PORT_DIPSETTING(0x1c, "1CXXh")
	PORT_DIPSETTING(0x1d, "1DXXh")
	PORT_DIPSETTING(0x1e, "1EXXh")
	PORT_DIPSETTING(0x1f, "1FXXh")
	PORT_DIPSETTING(0x20, "20XXh")
	PORT_DIPSETTING(0x21, "21XXh")
	PORT_DIPSETTING(0x22, "22XXh")
	PORT_DIPSETTING(0x23, "23XXh")
	PORT_DIPSETTING(0x24, "24XXh")
	PORT_DIPSETTING(0x25, "25XXh")
	PORT_DIPSETTING(0x26, "26XXh")
	PORT_DIPSETTING(0x27, "27XXh")
	PORT_DIPSETTING(0x28, "28XXh")
	PORT_DIPSETTING(0x29, "29XXh")
	PORT_DIPSETTING(0x2a, "2AXXh")
	PORT_DIPSETTING(0x2b, "2BXXh")
	PORT_DIPSETTING(0x2c, "2CXXh")
	PORT_DIPSETTING(0x2d, "2DXXh")
	PORT_DIPSETTING(0x2e, "2EXXh")
	PORT_DIPSETTING(0x2f, "2FXXh")
	PORT_DIPSETTING(0x30, "30XXh")
	PORT_DIPSETTING(0x31, "31XXh")
	PORT_DIPSETTING(0x32, "32XXh")
	PORT_DIPSETTING(0x33, "33XXh")
	PORT_DIPSETTING(0x34, "34XXh")
	PORT_DIPSETTING(0x35, "35XXh")
	PORT_DIPSETTING(0x36, "36XXh")
	PORT_DIPSETTING(0x37, "37XXh")
	PORT_DIPSETTING(0x38, "38XXh")
	PORT_DIPSETTING(0x39, "39XXh")
	PORT_DIPSETTING(0x3a, "3AXXh")
	PORT_DIPSETTING(0x3b, "3BXXh")
	PORT_DIPSETTING(0x3c, "3CXXh")
	PORT_DIPSETTING(0x3d, "3DXXh")
	PORT_DIPSETTING(0x3e, "3EXXh")
	PORT_DIPSETTING(0x3f, "3FXXh")
	PORT_DIPSETTING(0x40, "40XXh")
	PORT_DIPSETTING(0x41, "41XXh")
	PORT_DIPSETTING(0x42, "42XXh")
	PORT_DIPSETTING(0x43, "43XXh")
	PORT_DIPSETTING(0x44, "44XXh")
	PORT_DIPSETTING(0x45, "45XXh")
	PORT_DIPSETTING(0x46, "46XXh")
	PORT_DIPSETTING(0x47, "47XXh")
	PORT_DIPSETTING(0x48, "48XXh")
	PORT_DIPSETTING(0x49, "49XXh")
	PORT_DIPSETTING(0x4a, "4AXXh")
	PORT_DIPSETTING(0x4b, "4BXXh")
	PORT_DIPSETTING(0x4c, "4CXXh")
	PORT_DIPSETTING(0x4d, "4DXXh")
	PORT_DIPSETTING(0x4e, "4EXXh")
	PORT_DIPSETTING(0x4f, "4FXXh")
	PORT_DIPSETTING(0x50, "50XXh")
	PORT_DIPSETTING(0x51, "51XXh")
	PORT_DIPSETTING(0x52, "52XXh")
	PORT_DIPSETTING(0x53, "53XXh")
	PORT_DIPSETTING(0x54, "54XXh")
	PORT_DIPSETTING(0x55, "55XXh")
	PORT_DIPSETTING(0x56, "56XXh")
	PORT_DIPSETTING(0x57, "57XXh")
	PORT_DIPSETTING(0x58, "58XXh")
	PORT_DIPSETTING(0x59, "59XXh")
	PORT_DIPSETTING(0x5a, "5AXXh")
	PORT_DIPSETTING(0x5b, "5BXXh")
	PORT_DIPSETTING(0x5c, "5CXXh")
	PORT_DIPSETTING(0x5d, "5DXXh")
	PORT_DIPSETTING(0x5e, "5EXXh")
	PORT_DIPSETTING(0x5f, "5FXXh")
	PORT_DIPSETTING(0x60, "60XXh")
	PORT_DIPSETTING(0x61, "61XXh")
	PORT_DIPSETTING(0x62, "62XXh")
	PORT_DIPSETTING(0x63, "63XXh")
	PORT_DIPSETTING(0x64, "64XXh")
	PORT_DIPSETTING(0x65, "65XXh")
	PORT_DIPSETTING(0x66, "66XXh")
	PORT_DIPSETTING(0x67, "67XXh")
	PORT_DIPSETTING(0x68, "68XXh")
	PORT_DIPSETTING(0x69, "69XXh")
	PORT_DIPSETTING(0x6a, "6AXXh")
	PORT_DIPSETTING(0x6b, "6BXXh")
	PORT_DIPSETTING(0x6c, "6CXXh")
	PORT_DIPSETTING(0x6d, "6DXXh")
	PORT_DIPSETTING(0x6e, "6EXXh")
	PORT_DIPSETTING(0x6f, "6FXXh")
	PORT_DIPSETTING(0x70, "70XXh")
	PORT_DIPSETTING(0x71, "71XXh")
	PORT_DIPSETTING(0x72, "72XXh")
	PORT_DIPSETTING(0x73, "73XXh")
	PORT_DIPSETTING(0x74, "74XXh")
	PORT_DIPSETTING(0x75, "75XXh")
	PORT_DIPSETTING(0x76, "76XXh")
	PORT_DIPSETTING(0x77, "77XXh")
	PORT_DIPSETTING(0x78, "78XXh")
	PORT_DIPSETTING(0x79, "79XXh")
	PORT_DIPSETTING(0x7a, "7AXXh")
	PORT_DIPSETTING(0x7b, "7BXXh")
	PORT_DIPSETTING(0x7c, "7CXXh")
	PORT_DIPSETTING(0x7d, "7DXXh")
	PORT_DIPSETTING(0x7e, "7EXXh")
	PORT_DIPSETTING(0x7f, "7FXXh")
	PORT_DIPSETTING(0x80, "80XXh")
	PORT_DIPSETTING(0x81, "81XXh")
	PORT_DIPSETTING(0x82, "82XXh")
	PORT_DIPSETTING(0x83, "83XXh")
	PORT_DIPSETTING(0x84, "84XXh")
	PORT_DIPSETTING(0x85, "85XXh")
	PORT_DIPSETTING(0x86, "86XXh")
	PORT_DIPSETTING(0x87, "87XXh")
	PORT_DIPSETTING(0x88, "88XXh")
	PORT_DIPSETTING(0x89, "89XXh")
	PORT_DIPSETTING(0x8a, "8AXXh")
	PORT_DIPSETTING(0x8b, "8BXXh")
	PORT_DIPSETTING(0x8c, "8CXXh")
	PORT_DIPSETTING(0x8d, "8DXXh")
	PORT_DIPSETTING(0x8e, "8EXXh")
	PORT_DIPSETTING(0x8f, "8FXXh")
	PORT_DIPSETTING(0x90, "90XXh")
	PORT_DIPSETTING(0x91, "91XXh")
	PORT_DIPSETTING(0x92, "92XXh")
	PORT_DIPSETTING(0x93, "93XXh")
	PORT_DIPSETTING(0x94, "94XXh")
	PORT_DIPSETTING(0x95, "95XXh")
	PORT_DIPSETTING(0x96, "96XXh")
	PORT_DIPSETTING(0x97, "97XXh")
	PORT_DIPSETTING(0x98, "98XXh")
	PORT_DIPSETTING(0x99, "99XXh")
	PORT_DIPSETTING(0x9a, "9AXXh")
	PORT_DIPSETTING(0x9b, "9BXXh")
	PORT_DIPSETTING(0x9c, "9CXXh")
	PORT_DIPSETTING(0x9d, "9DXXh")
	PORT_DIPSETTING(0x9e, "9EXXh")
	PORT_DIPSETTING(0x9f, "9FXXh")
	PORT_DIPSETTING(0xa0, "A0XXh")
	PORT_DIPSETTING(0xa1, "A1XXh")
	PORT_DIPSETTING(0xa2, "A2XXh")
	PORT_DIPSETTING(0xa3, "A3XXh")
	PORT_DIPSETTING(0xa4, "A4XXh")
	PORT_DIPSETTING(0xa5, "A5XXh")
	PORT_DIPSETTING(0xa6, "A6XXh")
	PORT_DIPSETTING(0xa7, "A7XXh")
	PORT_DIPSETTING(0xa8, "A8XXh")
	PORT_DIPSETTING(0xa9, "A9XXh")
	PORT_DIPSETTING(0xaa, "AAXXh")
	PORT_DIPSETTING(0xab, "ABXXh")
	PORT_DIPSETTING(0xac, "ACXXh")
	PORT_DIPSETTING(0xad, "ADXXh")
	PORT_DIPSETTING(0xae, "AEXXh")
	PORT_DIPSETTING(0xaf, "AFXXh")
	PORT_DIPSETTING(0xb0, "B0XXh")
	PORT_DIPSETTING(0xb1, "B1XXh")
	PORT_DIPSETTING(0xb2, "B2XXh")
	PORT_DIPSETTING(0xb3, "B3XXh")
	PORT_DIPSETTING(0xb4, "B4XXh")
	PORT_DIPSETTING(0xb5, "B5XXh")
	PORT_DIPSETTING(0xb6, "B6XXh")
	PORT_DIPSETTING(0xb7, "B7XXh")
	PORT_DIPSETTING(0xb8, "B8XXh")
	PORT_DIPSETTING(0xb9, "B9XXh")
	PORT_DIPSETTING(0xba, "BAXXh")
	PORT_DIPSETTING(0xbb, "BBXXh")
	PORT_DIPSETTING(0xbc, "BCXXh")
	PORT_DIPSETTING(0xbd, "BDXXh")
	PORT_DIPSETTING(0xbe, "BEXXh")
	PORT_DIPSETTING(0xbf, "BFXXh")
	PORT_DIPSETTING(0xc0, "C0XXh")
	PORT_DIPSETTING(0xc1, "C1XXh")
	PORT_DIPSETTING(0xc2, "C2XXh")
	PORT_DIPSETTING(0xc3, "C3XXh")
	PORT_DIPSETTING(0xc4, "C4XXh")
	PORT_DIPSETTING(0xc5, "C5XXh")
	PORT_DIPSETTING(0xc6, "C6XXh")
	PORT_DIPSETTING(0xc7, "C7XXh")
	PORT_DIPSETTING(0xc8, "C8XXh")
	PORT_DIPSETTING(0xc9, "C9XXh")
	PORT_DIPSETTING(0xca, "CAXXh")
	PORT_DIPSETTING(0xcb, "CBXXh")
	PORT_DIPSETTING(0xcc, "CCXXh")
	PORT_DIPSETTING(0xcd, "CDXXh")
	PORT_DIPSETTING(0xce, "CEXXh")
	PORT_DIPSETTING(0xcf, "CFXXh")
	PORT_DIPSETTING(0xd0, "D0XXh")
	PORT_DIPSETTING(0xd1, "D1XXh")
	PORT_DIPSETTING(0xd2, "D2XXh")
	PORT_DIPSETTING(0xd3, "D3XXh")
	PORT_DIPSETTING(0xd4, "D4XXh")
	PORT_DIPSETTING(0xd5, "D5XXh")
	PORT_DIPSETTING(0xd6, "D6XXh")
	PORT_DIPSETTING(0xd7, "D7XXh")
	PORT_DIPSETTING(0xd8, "D8XXh")
	PORT_DIPSETTING(0xd9, "D9XXh")
	PORT_DIPSETTING(0xda, "DAXXh")
	PORT_DIPSETTING(0xdb, "DBXXh")
	PORT_DIPSETTING(0xdc, "DCXXh")
	PORT_DIPSETTING(0xdd, "DDXXh")
	PORT_DIPSETTING(0xde, "DEXXh")
	PORT_DIPSETTING(0xdf, "DFXXh")
	PORT_DIPSETTING(0xe0, "E0XXh")
	PORT_DIPSETTING(0xe1, "E1XXh")
	PORT_DIPSETTING(0xe2, "E2XXh")
	PORT_DIPSETTING(0xe3, "E3XXh")
	PORT_DIPSETTING(0xe4, "E4XXh")
	PORT_DIPSETTING(0xe5, "E5XXh")
	PORT_DIPSETTING(0xe6, "E6XXh")
	PORT_DIPSETTING(0xe7, "E7XXh")
	PORT_DIPSETTING(0xe8, "E8XXh")
	PORT_DIPSETTING(0xe9, "E9XXh")
	PORT_DIPSETTING(0xea, "EAXXh")
	PORT_DIPSETTING(0xeb, "EBXXh")
	PORT_DIPSETTING(0xec, "ECXXh")
	PORT_DIPSETTING(0xed, "EDXXh")
	PORT_DIPSETTING(0xee, "EEXXh")
	PORT_DIPSETTING(0xef, "EFXXh")
	PORT_DIPSETTING(0xf0, "F0XXh")
	PORT_DIPSETTING(0xf1, "F1XXh")
	PORT_DIPSETTING(0xf2, "F2XXh")
	PORT_DIPSETTING(0xf3, "F3XXh")
	PORT_DIPSETTING(0xf4, "F4XXh")
	PORT_DIPSETTING(0xf5, "F5XXh")
	PORT_DIPSETTING(0xf6, "F6XXh")
	PORT_DIPSETTING(0xf7, "F7XXh")
	PORT_DIPSETTING(0xf8, "F8XXh")
	PORT_DIPSETTING(0xf9, "F9XXh")
	PORT_DIPSETTING(0xfa, "FAXXh")
	PORT_DIPSETTING(0xfb, "FBXXh")
	PORT_DIPSETTING(0xfc, "FCXXh")
	PORT_DIPSETTING(0xfd, "FDXXh")
	PORT_DIPSETTING(0xfe, "FEXXh")
	PORT_DIPSETTING(0xff, "FFXXh")

	PORT_START("SEREN")
	PORT_DIPNAME(1, 1, "Enable Serial Port") PORT_DIPLOCATION("SER EN:!1")
	PORT_DIPSETTING(0, DEF_STR(Off))
	PORT_DIPSETTING(1, DEF_STR(On))

	PORT_START("SERADDR")
	PORT_DIPNAME(0xf8, 0x20, "Serial Address Select") PORT_DIPLOCATION("A7-A3:!5,!4,!3,!2,!1")
	PORT_DIPSETTING(0x00, "00h-07h")
	PORT_DIPSETTING(0x08, "08h-0Fh")
	PORT_DIPSETTING(0x10, "10h-17h")
	PORT_DIPSETTING(0x18, "18h-1Fh")
	PORT_DIPSETTING(0x20, "20h-27h")
	PORT_DIPSETTING(0x28, "28h-2Fh")
	PORT_DIPSETTING(0x30, "30h-37h")
	PORT_DIPSETTING(0x38, "38h-3Fh")
	PORT_DIPSETTING(0x40, "40h-47h")
	PORT_DIPSETTING(0x48, "48h-4Fh")
	PORT_DIPSETTING(0x50, "50h-57h")
	PORT_DIPSETTING(0x58, "58h-5Fh")
	PORT_DIPSETTING(0x60, "60h-67h")
	PORT_DIPSETTING(0x68, "68h-6Fh")
	PORT_DIPSETTING(0x70, "70h-77h")
	PORT_DIPSETTING(0x78, "78h-7Fh")
	PORT_DIPSETTING(0x80, "80h-87h")
	PORT_DIPSETTING(0x88, "88h-8Fh")
	PORT_DIPSETTING(0x90, "90h-97h")
	PORT_DIPSETTING(0x98, "98h-9Fh")
	PORT_DIPSETTING(0xa0, "A0h-A7h")
	PORT_DIPSETTING(0xa8, "A8h-AFh")
	PORT_DIPSETTING(0xb0, "B0h-B7h")
	PORT_DIPSETTING(0xb8, "B8h-BFh")
	PORT_DIPSETTING(0xc0, "C0h-C7h")
	PORT_DIPSETTING(0xc8, "C8h-CFh")
	PORT_DIPSETTING(0xd0, "D0h-D7h")
	PORT_DIPSETTING(0xd8, "D8h-DFh")
	PORT_DIPSETTING(0xe0, "E0h-E7h")
	PORT_DIPSETTING(0xe8, "E8h-EFh")
	PORT_DIPSETTING(0xf0, "F0h-F7h")
	PORT_DIPSETTING(0xf8, "F8h-FFh")
INPUT_PORTS_END

static INPUT_PORTS_START( ccs300 )
	// No information available on this system, but it may be assumed that
	//  the 300 is floppy-only, while the 400 boots off a hard drive.
	//  Plugging in the HDC cable would ground this pin to inform the bios
	//  it should be a 400. This "dip" is so you can see (and trace) what
	//  happens.
	PORT_START("MODEL")
	PORT_DIPNAME(0x08, 0x08, "Model")
	PORT_DIPSETTING(0x00, "CCS-400")
	PORT_DIPSETTING(0x08, "CCS-300")
	PORT_BIT(0xf7, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END


//*************************************
//
//  Status / Control ports
//
//*************************************

/* Status 1
d0 : intrq
d1 : ds0
d2 : ds1
d3 : ds2
d4 : ds3
d5 : hld (1=head loaded)
d6 : autoboot (1=go to monitor)
d7 : drq
*/

u8 ccs_state::port34_r()
{
	//return (u8)m_drq | (m_ds << 1) | ((u8)fdc->hld_r() << 5) | 0x40 | ((u8)m_intrq << 7);
	return (u8)m_fdc->drq_r() | (m_ds << 1) | 0x20 | 0x40 | ((u8)m_fdc->intrq_r() << 7); // hld_r doesn't do anything
}

/* Status 2
d0 : trk00 (1=trk00 on 20cm drive; 0=trk00 on 13cm drive)
d1 : drive size (0=20cm, 1=13cm)
d2 : wprt from drive (0=write protected)
d3 : /ss (1=side 0 is selected)
d4 : index hole = 0
d5 : dden (1 = double density)
d6 : double (0 = a double-sided 20cm disk is in the drive)
d7 : drq
*/

u8 ccs_state::port04_r()
{
	bool trk00=1,wprt=0,dside=1;
	int idx=1;
	if (m_floppy)
	{
		trk00 = !m_floppy->trk00_r();
		wprt = !m_floppy->wpt_r();
		idx = m_floppy->idx_r()^1;
		dside = m_floppy->twosid_r();
	}
	return (u8)trk00 | 0 | ((u8)wprt << 2) | ((u8)m_ss << 3) |
		idx << 4 | ((u8)m_dden << 5) | ((u8)dside << 6) | ((u8)m_fdc->drq_r() << 7);
}

/* Control 1
d0 : ds0
d1 : ds1
d2 : ds2
d3 : ds3
d4 : drive size (adjusts fdc clock)
d5 : mon (1=motor on)
d6 : dden
d7 : autowait (0=ignore drq)
*/

void ccs_state::port34_w(u8 data)
{
	m_ds = data & 15;
	m_dsize = BIT(data, 4);
	m_dden = BIT(data, 6);

	m_floppy = nullptr;
	if (BIT(data, 0)) m_floppy = m_floppy0->get_device();
	m_fdc->set_floppy(m_floppy);
	m_fdc->dden_w(!m_dden);

	if (m_floppy)
	{
		m_floppy->mon_w(!BIT(data, 5));
	}
}

/* Control 2
d2 : remote eject for persci drive (1=eject)
d4 : fast seek mode (1=on)
d6 : /ss (0=side 1 selected)
d7 : rom enable (1=firmware enabled)
other bits not used
*/

void ccs_state::port04_w(u8 data)
{
	m_ss = BIT(data, 6);
	if (m_floppy)
		m_floppy->ss_w(!m_ss);
}


//*************************************
//
//  Machine
//
//*************************************
void ccs_state::port40_w(u8 data)
{
	//possibly a banking control, like ccs300 ?
}

void ccs_state::machine_start()
{
	save_item(NAME(m_power_on_status));
	save_item(NAME(m_ss));
	save_item(NAME(m_dden));
	save_item(NAME(m_dsize));
	save_item(NAME(m_ds));
}

void ccs_state::machine_reset()
{
	// pRESET clears 74LS175 wired as shift register
	m_power_on_status = m_jump_en->read() | 8;
}

void ccs300_state::port40_w(u8 data)
{
	m_bank1->set_entry(BIT(~data, 0));
}

void ccs300_state::machine_reset()
{
	m_bank1->set_entry(1);
}

void ccs300_state::machine_start()
{
	m_bank1->configure_entry(0, m_ram1);
	m_bank1->configure_entry(1, m_rom);

	save_item(NAME(m_ss));
	save_item(NAME(m_dden));
	save_item(NAME(m_dsize));
	save_item(NAME(m_ds));
}

//*************************************
//
//  Config
//
//*************************************

static const z80_daisy_config daisy_chain[] =
{
	{ "ctc" },
	{ "sio" },
	{ "pio" },
	{ "dma" },
	{ nullptr }
};

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END

static void ccs_floppies(device_slot_interface &device)
{
	device.option_add("8sssd", FLOPPY_8_SSSD);
	//device.option_add("8sssd", FLOPPY_525_DD);
}


void ccs_state::ccs2810(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &ccs_state::ccs2810_mem);
	m_maincpu->set_addrmap(AS_IO, &ccs_state::ccs2810_io);

	RAM(config, RAM_TAG).set_default_size("64K");

	/* Devices */
	INS8250(config, m_ins8250, 1.8432_MHz_XTAL);
	m_ins8250->out_tx_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	m_ins8250->out_dtr_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	m_ins8250->out_rts_callback().set("rs232", FUNC(rs232_port_device::write_rts));
	m_ins8250->out_out1_callback().set("rs232", FUNC(rs232_port_device::write_spds)); // RLSD

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_ins8250, FUNC(ins8250_device::rx_w));
	rs232.rxd_handler().append(m_ins8250, FUNC(ins8250_device::ri_w));
	rs232.dcd_handler().set(m_ins8250, FUNC(ins8250_device::dcd_w));
	rs232.dsr_handler().set(m_ins8250, FUNC(ins8250_device::dsr_w));
	rs232.cts_handler().set(m_ins8250, FUNC(ins8250_device::cts_w));
}

void ccs_state::ccs2422(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &ccs_state::ccs2810_mem);
	m_maincpu->set_addrmap(AS_IO, &ccs_state::ccs2422_io);

	RAM(config, RAM_TAG).set_default_size("64K");

	/* Devices */
	INS8250(config, m_ins8250, 1.8432_MHz_XTAL);
	m_ins8250->out_tx_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	m_ins8250->out_dtr_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	m_ins8250->out_rts_callback().set("rs232", FUNC(rs232_port_device::write_rts));
	m_ins8250->out_out1_callback().set("rs232", FUNC(rs232_port_device::write_etc)); // RLSD

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_ins8250, FUNC(ins8250_device::rx_w));
	rs232.rxd_handler().append(m_ins8250, FUNC(ins8250_device::ri_w));
	rs232.dcd_handler().set(m_ins8250, FUNC(ins8250_device::dcd_w));
	rs232.dsr_handler().set(m_ins8250, FUNC(ins8250_device::dsr_w));
	rs232.cts_handler().set(m_ins8250, FUNC(ins8250_device::cts_w));

	MB8877(config, m_fdc, 16_MHz_XTAL / 8); // UB1793 or MB8877
	FLOPPY_CONNECTOR(config, "fdc:0", ccs_floppies, "8sssd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
}

void ccs300_state::ccs300(machine_config & config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &ccs300_state::ccs300_mem);
	m_maincpu->set_addrmap(AS_IO, &ccs300_state::ccs300_io);
	m_maincpu->set_daisy_config(daisy_chain);

	/* Devices */
	z80sio_device &sio(Z80SIO(config, "sio", 16_MHz_XTAL / 4));
	sio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	sio.out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
	sio.out_dtra_callback().set("rs232a", FUNC(rs232_port_device::write_dtr));
	sio.out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts));
	sio.out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
	sio.out_dtrb_callback().set("rs232b", FUNC(rs232_port_device::write_dtr));
	sio.out_rtsb_callback().set("rs232b", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, "terminal"));
	rs232a.rxd_handler().set("sio", FUNC(z80sio_device::rxa_w));
	rs232a.cts_handler().set("sio", FUNC(z80sio_device::ctsa_w));
	rs232a.dcd_handler().set("sio", FUNC(z80sio_device::dcda_w));
	rs232a.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal)); // must be exactly here

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set("sio", FUNC(z80sio_device::rxb_w));
	rs232b.cts_handler().set("sio", FUNC(z80sio_device::ctsb_w));
	rs232b.dcd_handler().set("sio", FUNC(z80sio_device::dcdb_w));

	z80ctc_device &ctc(Z80CTC(config, "ctc", 16_MHz_XTAL / 4));
	ctc.set_clk<0>(16_MHz_XTAL / 8);     // 153'846
	//ctc.set_clk<1>(16_MHz_XTAL / 8);    // not used
	ctc.set_clk<2>(16_MHz_XTAL / 8);      // 9'615
	//ctc.set_clk<3>(16_MHz_XTAL / 8);   // 2'000'000 - this causes an IRQ storm, hanging the machine
	ctc.zc_callback<0>().set("sio", FUNC(z80sio_device::txca_w));
	ctc.zc_callback<0>().append("sio", FUNC(z80sio_device::rxca_w));
	ctc.zc_callback<2>().append("sio", FUNC(z80sio_device::rxtxcb_w));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80pio_device &pio(Z80PIO(config, "pio", 16_MHz_XTAL / 4));
	pio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80dma_device &dma(Z80DMA(config, "dma", 16_MHz_XTAL / 4));
	dma.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	MB8877(config, m_fdc, 16_MHz_XTAL / 8); // UB1793 or MB8877
	FLOPPY_CONNECTOR(config, "fdc:0", ccs_floppies, "8sssd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
}


/* ROM definition */
ROM_START( ccs2810 )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "ccs2810.u8",   0x0000, 0x0800, CRC(0c3054ea) SHA1(c554b7c44a61af13decb2785f3c9b33c6fc2bfce))

	ROM_REGION( 0x100, "proms", 0 )
	ROM_LOAD_OPTIONAL( "5623.u9", 0x0000, 0x0100, NO_DUMP ) // actual PROM type may differ
ROM_END

ROM_START( ccs2422 )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "2422.u24",  0x0000, 0x0800, CRC(6b47586b) SHA1(73ba779a659da4a1f0e22a3fa351a2b36d8456a0))

	ROM_REGION( 0x300, "proms", 0 )
	ROM_LOAD_OPTIONAL( "2422.u23",  0x0000, 0x0100, CRC(b279cada) SHA1(6cc6e00ec49ba2245c8836d6f09266b09d6e7648))
	ROM_LOAD_OPTIONAL( "2422.u22",  0x0100, 0x0100, CRC(e41858bb) SHA1(0be53725032ebea16e32cb720f099551a357e761))
	ROM_LOAD_OPTIONAL( "2422.u21",  0x0200, 0x0100, NO_DUMP )
ROM_END

ROM_START( ccs300 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "ccs300.rom", 0x0000, 0x0800, CRC(6cf22e31) SHA1(9aa3327cd8c23d0eab82cb6519891aff13ebe1d0))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME     PARENT   COMPAT  MACHINE   INPUT    CLASS         INIT        COMPANY                        FULLNAME                    FLAGS
COMP( 1980, ccs2810, 0,       0,      ccs2810,  ccs2810, ccs_state,    empty_init, "California Computer Systems", "CCS Model 2810 CPU card",  MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1980, ccs2422, ccs2810, 0,      ccs2422,  ccs2810, ccs_state,    empty_init, "California Computer Systems", "CCS Model 2422B FDC card", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1981, ccs300,  ccs2810, 0,      ccs300,   ccs300,  ccs300_state, empty_init, "California Computer Systems", "CCS Model 300",            MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



cd100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Rowe CD-100 jukebox series.

    Communication between the Central Control Computer and the Mech
    Controller is handled through an unemulated serial link (Z180 emulation
    improvements are needed to handle this).

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

#include "emu.h"
#include "cpu/m6800/m6801.h"
#include "cpu/z180/z180.h"
#include "machine/msm6242.h"
#include "machine/nvram.h"


namespace {

class cd100_state : public driver_device
{
public:
	cd100_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mechcpu(*this, "mechcpu")
		, m_switches(*this, "IN%d", 0U)
		, m_misc(*this, "MISC")
		, m_link(*this, "LINK")
		, m_strobe(0x3f)
		, m_mech_p1(0)
	{
	}

	void cd100b(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	u8 ccc_in_r();
	void ccc_out0_w(u8 data);
	void ccc_out1_w(u8 data);
	u8 mech_p1_r();
	void mech_p1_w(u8 data);
	u8 mech_p2_r();
	void mech_p2_w(u8 data);
	void mech_latch_w(u8 data);

	void ccc_mem_map(address_map &map) ATTR_COLD;
	void ccc_io_map(address_map &map) ATTR_COLD;
	void mc6803_map(address_map &map) ATTR_COLD;

	required_device<z180_device> m_maincpu;
	required_device<m6803_cpu_device> m_mechcpu;
	required_ioport_array<6> m_switches;
	required_ioport m_misc;
	required_ioport m_link;

	u8 m_strobe;
	u8 m_mech_p1;
};


void cd100_state::machine_start()
{
	save_item(NAME(m_strobe));
}

u8 cd100_state::ccc_in_r()
{
	u8 ret = 0x0f;

	for (int i = 0; i < 6; i++)
		if (BIT(m_strobe, i))
			ret &= m_switches[i]->read();

	return ret | m_misc->read() << 4;
}

void cd100_state::ccc_out0_w(u8 data)
{
	m_strobe = data & 0x3f;

	// D6 (STROBE6): Not used
	// D7 (STROBE7): Background music active
}

void cd100_state::ccc_out1_w(u8 data)
{
	// D0 (STROBE8): Display reset
	// D1 (STROBE9): Speed info to motor chip
	// D2 (STROBE10): Direction info to motor chip
	// D3: Mute
	// D4: RoweLink Tx/Rx select
	// D5: System error LED
	// D6: Board error LED
	// D7: Watchdog strobe
}

u8 cd100_state::mech_p1_r()
{
	u8 ret = 0;

	// P10: IIC latched data Rxed
	// P11: IIC bit trapped
	// P12: 74HCT151 Y return (Z12)
	// P13: 74HCT151 Y return (Z17)
	if (BIT(m_link->read(), (m_mech_p1 & 0xe0) >> 5))
		ret |= 0x08;

	return ret;
}

void cd100_state::mech_p1_w(u8 data)
{
	// P14: Board error LED
	// P15: 74HCT151 A select (Z12, Z17)
	// P16: 74HCT151 B select (Z12, Z17)
	// P17: 74HCT151 C select (Z12, Z17)
	m_mech_p1 = data;
}

u8 cd100_state::mech_p2_r()
{
	// P21: +5VDC
	// P22: GND
	// P23: System Rx
	return 0x02;
}

void cd100_state::mech_p2_w(u8 data)
{
	// P20: IIC enable
	// P24: System Tx
}

void cd100_state::mech_latch_w(u8 data)
{
	// Q0: Money counter
	// Q1: Play counter
	// Q2: Magazine motor
	// Q3: Transfer motor
	// Q4: Detent solenoid
	// Q5: System Tx/Rx
	// Q6: Audio mute
	// Q7: IIC data Tx
}

void cd100_state::ccc_mem_map(address_map &map)
{
	map(0x00000, 0x1ffff).rom().region("ccc_program", 0);
	map(0x20000, 0x21fff).ram().share("nvram");
}

void cd100_state::ccc_io_map(address_map &map)
{
	map(0x0000, 0x003f).noprw(); // internal registers
	map(0x2000, 0x200f).rw("rtc", FUNC(rtc72421_device::read), FUNC(rtc72421_device::write));
	map(0x6000, 0x6000).w(FUNC(cd100_state::ccc_out1_w));
	map(0x8000, 0x8000).w(FUNC(cd100_state::ccc_out0_w));
	map(0xa000, 0xa000).r(FUNC(cd100_state::ccc_in_r));
}

void cd100_state::mc6803_map(address_map &map)
{
	map(0x2000, 0x27ff).ram();
	map(0x4000, 0x4000).w(FUNC(cd100_state::mech_latch_w));
	// TODO: CD-100B has more peripheral stuff in the 60XX range, not on CD-100A schematics?
	map(0x8000, 0xffff).rom().region("mech_program", 0);
}


static INPUT_PORTS_START(cd100b)
	PORT_START("IN0")
	PORT_BIT(0x1, IP_ACTIVE_LOW, IPT_COIN1) // 5¢
	PORT_BIT(0x2, IP_ACTIVE_LOW, IPT_COIN2) // 10¢
	PORT_BIT(0x4, IP_ACTIVE_LOW, IPT_COIN3) // 25¢
	PORT_BIT(0x8, IP_ACTIVE_LOW, IPT_COIN4) // 50¢

	PORT_START("IN1")
	PORT_BIT(0x1, IP_ACTIVE_LOW, IPT_UNKNOWN) // Tile Disp Limit
	PORT_BIT(0x2, IP_ACTIVE_LOW, IPT_UNKNOWN) // Tile Disp Index
	PORT_BIT(0x4, IP_ACTIVE_LOW, IPT_UNKNOWN) // Reserved
	PORT_BIT(0x8, IP_ACTIVE_LOW, IPT_UNKNOWN) // UK Defaults

	PORT_START("IN2")
	PORT_BIT(0x1, IP_ACTIVE_LOW, IPT_UNKNOWN) // Key 0
	PORT_BIT(0x2, IP_ACTIVE_LOW, IPT_UNKNOWN) // Key 1
	PORT_BIT(0x4, IP_ACTIVE_LOW, IPT_UNKNOWN) // Key 2
	PORT_BIT(0x8, IP_ACTIVE_LOW, IPT_UNKNOWN) // Key 3

	PORT_START("IN3")
	PORT_BIT(0x1, IP_ACTIVE_LOW, IPT_UNKNOWN) // Key 4
	PORT_BIT(0x2, IP_ACTIVE_LOW, IPT_UNKNOWN) // Key 5
	PORT_BIT(0x4, IP_ACTIVE_LOW, IPT_UNKNOWN) // Key 6
	PORT_BIT(0x8, IP_ACTIVE_LOW, IPT_UNKNOWN) // Key 7

	PORT_START("IN4")
	PORT_BIT(0x1, IP_ACTIVE_LOW, IPT_UNKNOWN) // Key 8
	PORT_BIT(0x2, IP_ACTIVE_LOW, IPT_UNKNOWN) // Key 9
	PORT_BIT(0x4, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x8, IP_ACTIVE_LOW, IPT_UNKNOWN) // Audit report start

	PORT_START("IN5")
	PORT_BIT(0x1, IP_ACTIVE_LOW, IPT_UNKNOWN) // "POPULAR"
	PORT_BIT(0x2, IP_ACTIVE_LOW, IPT_UNKNOWN) // "RESET"
	PORT_BIT(0x4, IP_ACTIVE_LOW, IPT_UNKNOWN) // "OUT"
	PORT_BIT(0x8, IP_ACTIVE_LOW, IPT_UNKNOWN) // "IN"

	PORT_START("MISC")
	PORT_BIT(0x1, IP_ACTIVE_LOW, IPT_UNKNOWN) // "Cancel Switch"
	PORT_BIT(0x2, IP_ACTIVE_LOW, IPT_UNKNOWN) // "Display Attention"
	PORT_BIT(0x4, IP_ACTIVE_LOW, IPT_SERVICE)
	PORT_BIT(0x8, IP_ACTIVE_LOW, IPT_UNKNOWN) // "Bad Battery"

	PORT_START("LINK")
	PORT_DIPNAME(0xe0, 0xe0, "RoweLink Address") PORT_DIPLOCATION("SW1:1,2,3")
	PORT_DIPSETTING(0x00, "0")
	PORT_DIPSETTING(0x20, "1")
	PORT_DIPSETTING(0x40, "2")
	PORT_DIPSETTING(0x60, "3")
	PORT_DIPSETTING(0x80, "4")
	PORT_DIPSETTING(0xa0, "5")
	PORT_DIPSETTING(0xc0, "6")
	PORT_DIPSETTING(0xe0, "7")
INPUT_PORTS_END


void cd100_state::cd100b(machine_config &config)
{
	HD64180RP(config, m_maincpu, 12.288_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &cd100_state::ccc_mem_map);
	m_maincpu->set_addrmap(AS_IO, &cd100_state::ccc_io_map);
	// TODO: ASCI channel 0 for peripheral interface (RS232C, through MAX232)
	// TODO: ASCI channel 1 for RoweLink (RS485, through SN75176)
	// TODO: CSIO for display (RS422, through DS8923)

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // 6264/62256 + same battery as RTC

	RTC72421(config, "rtc", 32768).out_int_handler().set_inputline(m_maincpu, Z180_INPUT_LINE_IRQ2);

	M6803(config, m_mechcpu, 4.9152_MHz_XTAL);
	m_mechcpu->set_addrmap(AS_PROGRAM, &cd100_state::mc6803_map);
	m_mechcpu->in_p1_cb().set(FUNC(cd100_state::mech_p1_r));
	m_mechcpu->out_p1_cb().set(FUNC(cd100_state::mech_p1_w));
	m_mechcpu->in_p2_cb().set(FUNC(cd100_state::mech_p2_r));
	m_mechcpu->out_p2_cb().set(FUNC(cd100_state::mech_p2_w));
}


ROM_START(cd100b)
	ROM_REGION(0x20000, "ccc_program", ROMREGION_ERASE00)
	ROM_SYSTEM_BIOS(0, "v27", "Version 2.7")
	ROM_SYSTEM_BIOS(1, "v30", "Version 3.0")
	ROM_SYSTEM_BIOS(2, "v40", "Version 4.0")
	ROM_SYSTEM_BIOS(3, "v41", "Version 4.1")
	ROMX_LOAD("70039903.v27", 0x00000, 0x10000, CRC(2aa0c0dc) SHA1(f0be4f7c26b9e798790c169370c8dc2d2c2edbbb), ROM_BIOS(0))
	ROMX_LOAD("70042704.v30", 0x00000, 0x20000, CRC(3e1af9ac) SHA1(2ee4ff59963270fa34ff61f0428bd3d1a01a743a), ROM_BIOS(1))
	ROMX_LOAD("70042704.v40", 0x00000, 0x20000, CRC(66ba3ede) SHA1(22d9ee14fadef81b1ed7d95c1432d235bea5eba7), ROM_BIOS(2))
	ROMX_LOAD("jukeccc",      0x00000, 0x20000, CRC(416a9dd5) SHA1(83fcd73092792b1f0d391e80d81ab8791c4c8e39), ROM_BIOS(3))

	ROM_REGION(0x8000, "mech_program", 0)
	ROM_LOAD("70038325.31", 0x0000, 0x8000, CRC(6c46039a) SHA1(e4410180d94e9a60ddc8f42296ee60be20b28b5b)) // "CDM4 Mech Controller version 3.1"
ROM_END

} // anonymous namespace


SYST(1992, cd100b, 0, 0, cd100b, cd100b, cd100_state, empty_init, "Rowe International", "CD-100B LaserStar", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_MECHANICAL | MACHINE_REQUIRES_ARTWORK)



cd2650.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Central Data 2650 Computer System

2010-04-08 Skeleton driver.

No info available on this computer apart from a few newsletters and
magazine articles. The computer was described in a series of articles
published between April and June 1977 in Radio-Electronics, which include
supposedly complete schematics and fairly detailed subsystem descriptions.
There is supposed to be a “Computer System Manual” with definitive revised
schematics, but this has not been found yet.

All signals to and from the 2650 board (including the built-in 300 baud
Kansas City standard cassette tape interface) are passed through six ribbon
cables. Central Data later produced an “extender board” that adapted the
bus signals to a S-100 backplane. This interface was missing a considerable
number of standard S-100 timing signals, though it was compatible at least
with some dynamic RAM boards released by the company.

The unusual XTAL frequency seems deliberately chosen to produce a vertical
sync rate of exactly 60 Hz.

The system only uses 1000-14FF for videoram and 17F0-17FF for
scratch ram. All other ram is optional.

Commands (must be in uppercase):
A    Examine memory; press C to alter memory
B    Set breakpoint
C    Clear unused breakpoint
D    Dump to cassette (must save each block separately, then an empty block)
E    Execute
I    Inspect Registers after breakpoint
L    Load from cassette
R    Turn on cassette motor
V    Verify cassette vs memory
Press Esc to exit most commands.

TODO
- Cassette doesn't work. Saving is ok because the data can be loaded onto
  the Super-80, but this system has great difficulty loading its own programs.

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

#define CHARACTER_WIDTH 8
#define CHARACTER_HEIGHT 8
#define CHARACTER_LINES 12

#include "emu.h"
#include "cpu/s2650/s2650.h"
//#include "bus/s100/s100.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "machine/74259.h"
#include "machine/keyboard.h"
#include "machine/timer.h"
#include "sound/beep.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class cd2650_state : public driver_device
{
public:
	cd2650_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_p_videoram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
		, m_cass(*this, "cassette")
	{ }

	void cd2650(machine_config &config);

private:
	void data_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	u8 keyin_r();
	void kbd_put(u8 data);
	void tape_deck_on_w(int state);
	int cass_r();
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_w);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	u8 m_term_data = 0U;
	bool m_cassbit = 0;
	bool m_cassold = 0;
	u8 m_cass_data[4]{};
	void machine_reset() override ATTR_COLD;
	void machine_start() override ATTR_COLD;
	required_device<s2650_device> m_maincpu;
	required_shared_ptr<u8> m_p_videoram;
	required_region_ptr<u8> m_p_chargen;
	required_device<cassette_image_device> m_cass;
};


TIMER_DEVICE_CALLBACK_MEMBER(cd2650_state::kansas_w)
{
	m_cass_data[3]++;

	if (m_cassbit != m_cassold)
	{
		m_cass_data[3] = 0;
		m_cassold = m_cassbit;
	}

	if (m_cassbit)
		m_cass->output(BIT(m_cass_data[3], 0) ? -1.0 : +1.0); // 2400Hz
	else
		m_cass->output(BIT(m_cass_data[3], 1) ? -1.0 : +1.0); // 1200Hz
}

TIMER_DEVICE_CALLBACK_MEMBER(cd2650_state::kansas_r)
{
	/* cassette - turn 1200/2400Hz to a bit */
	m_cass_data[1]++;
	u8 cass_ws = (m_cass->input() > +0.03) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		m_cass_data[2] = ((m_cass_data[1] < 12) ? 1 : 0);
		m_cass_data[1] = 0;
	}
}

void cd2650_state::tape_deck_on_w(int state)
{
	m_cass->change_state(state ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
}

int cd2650_state::cass_r()
{
	return m_cass_data[2];
}

u8 cd2650_state::keyin_r()
{
	u8 ret = m_term_data;
	m_term_data = ret | 0x80;
	return ret;
}

void cd2650_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).rom().region("roms", 0);
	map(0x1000, 0x7fff).ram().share("videoram");
}

void cd2650_state::io_map(address_map &map)
{
	map.unmap_value_high();
	//map(0x80, 0x84) disk i/o
}

void cd2650_state::data_map(address_map &map)
{
	map(S2650_DATA_PORT, S2650_DATA_PORT).r(FUNC(cd2650_state::keyin_r)).w("outlatch", FUNC(f9334_device::write_nibble_d3));
}

/* Input ports */
static INPUT_PORTS_START( cd2650 )
INPUT_PORTS_END


void cd2650_state::machine_reset()
{
	m_term_data = 0x80;
}

void cd2650_state::machine_start()
{
	save_item(NAME(m_term_data));
	save_item(NAME(m_cassbit));
	save_item(NAME(m_cassold));
	save_item(NAME(m_cass_data));
}

u32 cd2650_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
/* The video is unusual in that the characters in each line are spaced at 16 bytes in memory,
    thus line 1 starts at 1000, line 2 at 1001, etc. There are 16 lines of 80 characters.
    Further, the letters have bit 6 set low, thus the range is 01 to 1A.
    When the bottom of the screen is reached, it does not scroll, it just wraps around. */

	u16 offset=0,sy=0;

	for (u8 y = 0; y < 16; y++)
	{
		for (u8 ra = 0; ra < CHARACTER_LINES; ra++)
		{
			u16 *p = &bitmap.pix(sy++);

			for (u16 x = 0; x < 80; x++)
			{
				u8 gfx = 0;
				if (ra < CHARACTER_HEIGHT)
				{
					u16 mem = offset + y + (x<<4);

					if (mem > 0x4ff)
						mem -= 0x500;

					u8 chr = m_p_videoram[mem] & 0x3f;

					gfx = m_p_chargen[(bitswap<8>(chr,7,6,2,1,0,3,4,5)<<3) | ra];
				}

				/* Display a scanline of a character */
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
	}
	return 0;
}

/* F4 Character Displayer */
static const gfx_layout cd2650_charlayout =
{
	8, 8,                  /* 8 x 8 characters */
	192,                    /* 64 characters in char.rom + 128 characters in char2.rom */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                    /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_cd2650 )
	GFXDECODE_ENTRY( "chargen", 0x0000, cd2650_charlayout, 0, 1 )
GFXDECODE_END

void cd2650_state::kbd_put(u8 data)
{
	if (data)
		m_term_data = data;
}

QUICKLOAD_LOAD_MEMBER(cd2650_state::quickload_cb)
{
	int const quick_length = image.length();
	if (quick_length < 0x1500)
		return std::make_pair(image_error::INVALIDLENGTH, "Image file too short (must be at least 5376 bytes)");
	else if (quick_length > 0x8000)
		return std::make_pair(image_error::INVALIDLENGTH, "Image file too long (must be no more than 32K)");

	std::vector<u8> quick_data(quick_length);
	int read_ = image.fread( &quick_data[0], quick_length);
	if (read_ != quick_length)
		return std::make_pair(image_error::UNSPECIFIED, "Cannot read the file");
	else if (quick_data[0] != 0x40)
		return std::make_pair(image_error::INVALIDIMAGE, "Invalid file header");

	int const exec_addr = quick_data[1] * 256 + quick_data[2];
	if (exec_addr >= quick_length)
	{
		return std::make_pair(
				image_error::INVALIDIMAGE,
				util::string_format("Exec address %04X beyond end of file %04X", exec_addr, quick_length));
	}

	// do not overwite system area (17E0-17FF) otherwise chess3 has problems
	read_ = std::min<int>(0x17e0, quick_length);

	for (int i = 0x1500; i < read_; i++)
		m_p_videoram[i-0x1000] = quick_data[i];

	if (quick_length > 0x17ff)
		for (int i = 0x1800; i < quick_length; i++)
			m_p_videoram[i-0x1000] = quick_data[i];

	// display a message about the loaded quickload
	image.message(" Quickload: size=%04X : exec=%04X", quick_length, exec_addr);

	// Start the quickload
	m_maincpu->set_state_int(S2650_PC, exec_addr);

	return std::make_pair(std::error_condition(), std::string());
}

void cd2650_state::cd2650(machine_config &config)
{
	/* basic machine hardware */
	S2650(config, m_maincpu, XTAL(14'192'640) / 12); // 1.182720MHz according to RE schematic
	m_maincpu->set_addrmap(AS_PROGRAM, &cd2650_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &cd2650_state::io_map);
	m_maincpu->set_addrmap(AS_DATA, &cd2650_state::data_map);
	m_maincpu->sense_handler().set(FUNC(cd2650_state::cass_r));
	m_maincpu->flag_handler().set([this] (bool state) { m_cassbit = state; });

	f9334_device &outlatch(F9334(config, "outlatch")); // IC26
	outlatch.q_out_cb<0>().set(FUNC(cd2650_state::tape_deck_on_w)); // TD ON
	outlatch.q_out_cb<7>().set("beeper", FUNC(beep_device::set_state)); // OUT6
	// Q1-Q7 = OUT 0-6, not defined in RE
	// The connection of OUT6 to a 700-1200 Hz noise generator is suggested
	// in Central Data 2650 Newsletter, Volume 1, Issue 3 for use with the
	// "Morse Code" program by Mike Durham.

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(XTAL(14'192'640), 112 * CHARACTER_WIDTH, 0, 80 * CHARACTER_WIDTH, 22 * CHARACTER_LINES, 0, 16 * CHARACTER_LINES);
	screen.set_screen_update(FUNC(cd2650_state::screen_update));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_cd2650);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* quickload */
	QUICKLOAD(config, "quickload", "pgm", attotime::from_seconds(1)).set_load_callback(FUNC(cd2650_state::quickload_cb));

	/* Sound */
	SPEAKER(config, "mono").front_center();
	BEEP(config, "beeper", 950).add_route(ALL_OUTPUTS, "mono", 0.50); // guess

	/* Devices */
	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(cd2650_state::kbd_put));
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.15);
	TIMER(config, "kansas_w").configure_periodic(FUNC(cd2650_state::kansas_w), attotime::from_hz(4800));
	TIMER(config, "kansas_r").configure_periodic(FUNC(cd2650_state::kansas_r), attotime::from_hz(40000));
}

/* ROM definition */
ROM_START( cd2650 )
	ROM_REGION( 0x1000, "roms", 0 )
	ROM_LOAD( "cd2650.rom", 0x0000, 0x0400, CRC(5397328e) SHA1(7106fdb60e1ad2bc5e8e45527f348c23296e8d6a))

	ROM_REGION( 0x0600, "chargen", 0 )
	ROM_LOAD( "char.rom",   0x0000, 0x0200, CRC(9b75db2a) SHA1(4367c01afa503d7cba0c38078fde0b95392c6c2c))
	ROM_LOAD_OPTIONAL( "char2.rom",  0x0200, 0x0400, CRC(b450eea8) SHA1(c1bdba52c2dc5698cad03b6b884b942a083465ed)) // not used

	// various unused roms found on Amigan site
	ROM_REGION( 0xc900, "user1", 0 )
	ROM_LOAD_OPTIONAL( "01a_cd_boots.bin", 0x0000, 0x0200, CRC(5336c62f) SHA1(e94cf7be01ea806ff7c7b90aee1a4e88f4f1ba9f))
	ROM_LOAD_OPTIONAL( "01a_cd_dos.bin",   0x0200, 0x2000, CRC(3f177cdd) SHA1(01afd77ad2f065158cbe032aa26682cb20afe7d8))
	ROM_LOAD_OPTIONAL( "01a_cd_pop.bin",   0x2200, 0x1000, CRC(d8f44f11) SHA1(605ab5a045290fa5b99ff4fc8fbfa2a3f202578f))
	ROM_LOAD_OPTIONAL( "01b_cd_alp.bin",   0x3200, 0x2a00, CRC(b05568bb) SHA1(29e74633c0cd731c0be25313288cfffdae374236))
	ROM_LOAD_OPTIONAL( "01b_cd_basic.bin", 0x5c00, 0x3b00, CRC(0cf1e3d8) SHA1(3421e679c238aeea49cd170b34a6f344da4770a6))
	ROM_LOAD_OPTIONAL( "01b_cd_mon_m.bin", 0x9700, 0x0400, CRC(f6f19c08) SHA1(1984d85d57fc2a6c5a3bd51fbc58540d7129a0ae))
	ROM_LOAD_OPTIONAL( "01b_cd_mon_o.bin", 0x9b00, 0x0400, CRC(9d40b4dc) SHA1(35cffcbd983b7b37c878a15af44100568d0659d1))
	ROM_LOAD_OPTIONAL( "02b_cd_alp.bin",   0x9f00, 0x2a00, CRC(a66b7f32) SHA1(2588f9244b0ec6b861dcebe666d37d3fa88dd043))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY         FULLNAME                FLAGS
COMP( 1977, cd2650, 0,      0,      cd2650,  cd2650, cd2650_state, empty_init, "Central Data", "2650 Computer System", MACHINE_SUPPORTS_SAVE )



cdc721.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/************************************************************************************************************

Control Data Corporation CDC 721 Terminal (Viking)

2013-08-13 Skeleton


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

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/i8255.h"
#include "machine/input_merger.h"
#include "machine/ins8250.h"
#include "machine/nvram.h"
#include "machine/output_latch.h"
#include "machine/z80ctc.h"
#include "video/tms9927.h"
#include "emupal.h"
#include "screen.h"


namespace {

class cdc721_state : public driver_device
{
public:
	cdc721_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_block0(*this, "block0")
		, m_block4(*this, "block4")
		, m_block8(*this, "block8")
		, m_blockc(*this, "blockc")
		, m_crtc(*this, "crtc")
		, m_rom_chargen(*this, "chargen")
		, m_ram_chargen(*this, "chargen")
		, m_videoram(*this, "videoram")
		, m_nvram(*this, "nvram")
	{ }

	void cdc721(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void cdc721_palette(palette_device &palette) const;
	void interrupt_mask_w(u8 data);
	void misc_w(u8 data);
	void block_select_w(u8 data);
	void nvram_w(offs_t offset, u8 data);

	template<int Line> void int_w(int state);
	TIMER_CALLBACK_MEMBER(update_interrupts);
	IRQ_CALLBACK_MEMBER(restart_cb);

	template<int Bit> void foreign_char_bank_w(int state);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	u8 m_flashcnt = 0;
	u8 m_foreign_char_bank = 0;

	u8 m_pending_interrupts = 0;
	u8 m_active_interrupts = 0;
	u8 m_interrupt_mask = 0;

	required_device<cpu_device> m_maincpu;
	memory_view m_block0, m_block4, m_block8, m_blockc;
	required_device<crt5037_device> m_crtc;
	required_region_ptr<u8> m_rom_chargen;
	required_shared_ptr<u8> m_ram_chargen;
	required_shared_ptr<u8> m_videoram;
	required_shared_ptr<u8> m_nvram;
};

void cdc721_state::interrupt_mask_w(u8 data)
{
	m_interrupt_mask = data ^ 0xff;
	machine().scheduler().synchronize(timer_expired_delegate(FUNC(cdc721_state::update_interrupts), this));
}

template <int Line>
void cdc721_state::int_w(int state)
{
	if (BIT(m_pending_interrupts, Line) == state)
		return;

	if (state)
		m_pending_interrupts |= 0x01 << Line;
	else
		m_pending_interrupts &= ~(0x01 << Line);

	machine().scheduler().synchronize(timer_expired_delegate(FUNC(cdc721_state::update_interrupts), this));
}

TIMER_CALLBACK_MEMBER(cdc721_state::update_interrupts)
{
	m_active_interrupts = m_pending_interrupts & m_interrupt_mask;
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, m_active_interrupts != 0 ? ASSERT_LINE : CLEAR_LINE);
}

IRQ_CALLBACK_MEMBER(cdc721_state::restart_cb)
{
	// IM 2 vector is produced through a SN74LS148N priority encoder plus some buffers and a latch
	// The CTC vector is not even fetched
	u8 vector = 0x00;
	u8 active = m_active_interrupts;
	while (vector < 0x0e && !BIT(active, 0))
	{
		active >>= 1;
		vector += 0x02;
	}
	return vector;
}

void cdc721_state::misc_w(u8 data)
{
	// 7: Stop Refresh Operation
	// 6: Enable RAM Char Gen
	// 5: Enable Block Cursor
	// 4: Reverse Video
	// 3: 132/80
	// 2: Enable Blink
	// 1: Enable Blinking Cursor
	// 0: Alarm

	logerror("%s: %d-column display selected\n", machine().describe_context(), BIT(data, 3) ? 132 : 80);
}

void cdc721_state::block_select_w(u8 data)
{
	logerror("%s: Bank select = %02X\n", machine().describe_context(), data);
	m_block0.select(BIT(data, 0, 2));
	m_block4.select(BIT(data, 2, 2));
	m_block8.select(BIT(data, 4, 2));
	m_blockc.select(BIT(data, 6, 2));
}

void cdc721_state::nvram_w(offs_t offset, u8 data)
{
	m_nvram[offset] = data & 0x0f;
}

template<int Bit>
void cdc721_state::foreign_char_bank_w(int state)
{
	if (state)
		m_foreign_char_bank |= 1 << Bit;
	else
		m_foreign_char_bank &= ~(1 << Bit);
}

void cdc721_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).view(m_block0);
	m_block0[0](0x0000, 0x3fff).rom().region("resident", 0);
	m_block0[1](0x0000, 0x3fff).unmaprw();
	m_block0[2](0x0000, 0x3fff).ram();
	m_block0[3](0x0000, 0x3fff).unmaprw();
	map(0x4000, 0x7fff).view(m_block4);
	m_block4[0](0x4000, 0x40ff).mirror(0x2700).ram().share("nvram").w(FUNC(cdc721_state::nvram_w));
	m_block4[0](0x4800, 0x4bff).mirror(0x2400).ram().share("chargen"); // 2x P2114AL-2
	m_block4[1](0x4000, 0x7fff).unmaprw();
	m_block4[2](0x4000, 0x7fff).rom().region("16krom", 0);
	m_block4[3](0x4000, 0x7fff).ram();
	map(0x8000, 0xbfff).view(m_block8);
	m_block8[0](0x8000, 0xbfff).rom().region("rompack", 0);
	m_block8[1](0x8000, 0xbfff).ram();
	m_block8[2](0x8000, 0xbfff).unmaprw();
	m_block8[3](0x8000, 0xbfff).unmaprw();
	map(0xc000, 0xffff).view(m_blockc);
	m_blockc[0](0xc000, 0xdfff).ram();
	m_blockc[0](0xe000, 0xffff).ram().share("videoram");
	m_blockc[1](0xc000, 0xc0ff).mirror(0x2700).ram().share("nvram").w(FUNC(cdc721_state::nvram_w));
	m_blockc[1](0xc800, 0xcbff).mirror(0x2400).ram().share("chargen");
	m_blockc[2](0xc000, 0xffff).unmaprw();
	m_blockc[3](0xc000, 0xffff).unmaprw();
}

void cdc721_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x10, 0x1f).rw("crtc", FUNC(crt5037_device::read), FUNC(crt5037_device::write));
	map(0x20, 0x27).rw("kbduart", FUNC(ins8250_device::ins8250_r), FUNC(ins8250_device::ins8250_w));
	map(0x30, 0x33).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x40, 0x47).rw("comuart", FUNC(ins8250_device::ins8250_r), FUNC(ins8250_device::ins8250_w));
	map(0x50, 0x50).w("ledlatch", FUNC(output_latch_device::write));
	map(0x70, 0x70).w(FUNC(cdc721_state::block_select_w));
	map(0x80, 0x87).rw("pauart", FUNC(ins8250_device::ins8250_r), FUNC(ins8250_device::ins8250_w));
	map(0x90, 0x97).rw("pbuart", FUNC(ins8250_device::ins8250_r), FUNC(ins8250_device::ins8250_w));
}

static INPUT_PORTS_START( cdc721 )
INPUT_PORTS_END

void cdc721_state::machine_reset()
{
	m_block0.select(0);
	m_block4.select(0);
	m_block8.select(0);
	m_blockc.select(0);
}

void cdc721_state::machine_start()
{
	m_pending_interrupts = 0;
	m_active_interrupts = 0;
	m_interrupt_mask = 0;
	m_foreign_char_bank = 0;

	save_item(NAME(m_pending_interrupts));
	save_item(NAME(m_active_interrupts));
	save_item(NAME(m_interrupt_mask));
	save_item(NAME(m_foreign_char_bank));
}

/* F4 Character Displayer */
static const gfx_layout cdc721_charlayout =
{
	8, 16,                  /* 8 x 16 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	/* y offsets */
	{ 0*8, 0x100*8, 0x200*8, 0x300*8, 0x400*8, 0x500*8, 0x600*8, 0x700*8,
	0x800*8, 0x900*8, 0xa00*8, 0xb00*8, 0xc00*8, 0xd00*8, 0xe00*8, 0xf00*8 },
	8                   /* every char takes 16 x 1 bytes */
};

static GFXDECODE_START( gfx_cdc721 )
	GFXDECODE_ENTRY( "chargen", 0x0000, cdc721_charlayout, 0, 1 )
GFXDECODE_END

void cdc721_state::cdc721_palette(palette_device &palette) const
{
	palette.set_pen_color(0, 0, 0, 0 );     // Black
	palette.set_pen_color(1, 0, 255, 0 );   // Full
	palette.set_pen_color(2, 0, 128, 0 );   // Dimmed
}

uint32_t cdc721_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint16_t sy=0;
	m_flashcnt++;

	for (uint8_t y = 0; y < 30; y++)
	{
		uint16_t ma = m_videoram[y * 2] | m_videoram[y * 2 + 1] << 8;

		for (uint8_t ra = 0; ra < 15; ra++)
		{
			uint16_t *p = &bitmap.pix(sy++);

			for (uint16_t x = 0; x < 160; x+=2)
			{
				uint8_t pen = 1;
				uint8_t chr = m_videoram[(x + ma) & 0x1fff];
				uint8_t attr = m_videoram[(x + ma + 1) & 0x1fff];
				uint8_t gfx = m_rom_chargen[chr | (ra << 8) ];
				if (BIT(attr, 0))  // blank
					pen = 0;
				if (BIT(attr, 1) && (ra == 14)) // underline
					gfx = 0xff;
				if (BIT(attr, 4)) // dim
					pen = 2;
				if (BIT(attr, 2)) // rv
					gfx ^= 0xff;
				if (BIT(attr, 3) && BIT(m_flashcnt, 6)) // blink
					gfx = 0;

				/* Display a scanline of a character */
				*p++ = BIT(gfx, 0) ? pen : 0;
				*p++ = BIT(gfx, 1) ? pen : 0;
				*p++ = BIT(gfx, 2) ? pen : 0;
				*p++ = BIT(gfx, 3) ? pen : 0;
				*p++ = BIT(gfx, 4) ? pen : 0;
				*p++ = BIT(gfx, 5) ? pen : 0;
				*p++ = BIT(gfx, 6) ? pen : 0;
				*p++ = BIT(gfx, 7) ? pen : 0;
			}
		}
	}
	return 0;
}

void cdc721_state::cdc721(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 6_MHz_XTAL); // Zilog Z8400B (Z80B)
	m_maincpu->set_addrmap(AS_PROGRAM, &cdc721_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &cdc721_state::io_map);
	m_maincpu->set_irq_acknowledge_callback(FUNC(cdc721_state::restart_cb));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // MCM51L01C45 (256x4) + battery

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(12.936_MHz_XTAL, 800, 0, 640, 539, 0, 450);
	screen.set_screen_update(FUNC(cdc721_state::screen_update));
	screen.set_palette("palette");
	PALETTE(config, "palette", FUNC(cdc721_state::cdc721_palette), 3);
	GFXDECODE(config, "gfxdecode", "palette", gfx_cdc721);

	CRT5037(config, m_crtc, 12.936_MHz_XTAL / 8).set_char_width(8);
	m_crtc->set_screen("screen");

	z80ctc_device& ctc(Z80CTC(config, "ctc", 6_MHz_XTAL)); // Zilog Z8430B (M1 pulled up)
	ctc.intr_callback().set(FUNC(cdc721_state::int_w<6>));
	ctc.zc_callback<1>().set("ctc", FUNC(z80ctc_device::trg2));
	//ctc.zc_callback<2>().set("comuart", FUNC(ins8250_device::rclk_w));

	i8255_device &ppi(I8255A(config, "ppi"));
	ppi.out_pb_callback().set(FUNC(cdc721_state::interrupt_mask_w));
	ppi.out_pc_callback().set(FUNC(cdc721_state::misc_w));

	output_latch_device &ledlatch(OUTPUT_LATCH(config, "ledlatch"));
	ledlatch.bit_handler<0>().set_output("error").invert();
	ledlatch.bit_handler<1>().set_output("alert").invert();
	ledlatch.bit_handler<2>().set_output("lock").invert();
	ledlatch.bit_handler<3>().set_output("message").invert();
	ledlatch.bit_handler<4>().set_output("prog1").invert();
	ledlatch.bit_handler<5>().set_output("prog2").invert();
	ledlatch.bit_handler<6>().set_output("prog3").invert();
	ledlatch.bit_handler<7>().set_output("dsr").invert();

	ins8250_device &comuart(INS8250(config, "comuart", 1.8432_MHz_XTAL));
	comuart.out_int_callback().set(FUNC(cdc721_state::int_w<0>));
	comuart.out_tx_callback().set("comm", FUNC(rs232_port_device::write_txd));
	comuart.out_dtr_callback().set("comm", FUNC(rs232_port_device::write_dtr));
	comuart.out_rts_callback().set("comm", FUNC(rs232_port_device::write_rts));

	rs232_port_device &comm(RS232_PORT(config, "comm", default_rs232_devices, nullptr));
	comm.rxd_handler().set("comuart", FUNC(ins8250_device::rx_w));
	comm.dsr_handler().set("comuart", FUNC(ins8250_device::dsr_w));
	comm.dcd_handler().set("comuart", FUNC(ins8250_device::dcd_w));
	comm.cts_handler().set("comuart", FUNC(ins8250_device::cts_w));
	comm.ri_handler().set("comuart", FUNC(ins8250_device::ri_w));

	ins8250_device &kbduart(INS8250(config, "kbduart", 1.8432_MHz_XTAL));
	kbduart.out_int_callback().set(FUNC(cdc721_state::int_w<5>));
	kbduart.out_dtr_callback().set(FUNC(cdc721_state::foreign_char_bank_w<2>));
	//kbduart.out_rts_callback().set(FUNC(cdc721_state::alarm_high_low_w));
	kbduart.out_out1_callback().set(FUNC(cdc721_state::foreign_char_bank_w<1>));
	kbduart.out_out2_callback().set(FUNC(cdc721_state::foreign_char_bank_w<0>));

	ins8250_device &pauart(INS8250(config, "pauart", 1.8432_MHz_XTAL));
	pauart.out_int_callback().set("int2", FUNC(input_merger_device::in_w<1>));
	pauart.out_tx_callback().set("cha", FUNC(rs232_port_device::write_txd));
	pauart.out_dtr_callback().set("cha", FUNC(rs232_port_device::write_dtr));
	pauart.out_rts_callback().set("cha", FUNC(rs232_port_device::write_rts));

	rs232_port_device &cha(RS232_PORT(config, "cha", default_rs232_devices, nullptr));
	cha.rxd_handler().set("pauart", FUNC(ins8250_device::rx_w));
	cha.dsr_handler().set("pauart", FUNC(ins8250_device::dsr_w));
	cha.dcd_handler().set("pauart", FUNC(ins8250_device::dcd_w));
	cha.cts_handler().set("pauart", FUNC(ins8250_device::cts_w));
	cha.ri_handler().set("pauart", FUNC(ins8250_device::ri_w));

	ins8250_device &pbuart(INS8250(config, "pbuart", 1.8432_MHz_XTAL));
	pbuart.out_int_callback().set("int2", FUNC(input_merger_device::in_w<0>));
	pbuart.out_tx_callback().set("chb", FUNC(rs232_port_device::write_txd));
	pbuart.out_dtr_callback().set("chb", FUNC(rs232_port_device::write_dtr));
	pbuart.out_rts_callback().set("chb", FUNC(rs232_port_device::write_rts));

	rs232_port_device &chb(RS232_PORT(config, "chb", default_rs232_devices, nullptr));
	chb.rxd_handler().set("pbuart", FUNC(ins8250_device::rx_w));
	chb.dsr_handler().set("pbuart", FUNC(ins8250_device::dsr_w));
	chb.dcd_handler().set("pbuart", FUNC(ins8250_device::dcd_w));
	chb.cts_handler().set("pbuart", FUNC(ins8250_device::cts_w));
	chb.ri_handler().set("pbuart", FUNC(ins8250_device::ri_w));

	INPUT_MERGER_ANY_HIGH(config, "int2").output_handler().set(FUNC(cdc721_state::int_w<2>)); // 74S05 (open collector)
}

ROM_START( cdc721 )
	ROM_REGION( 0x4000, "resident", 0 )
	ROM_LOAD( "66315359", 0x0000, 0x2000, CRC(20ff3eb4) SHA1(5f15cb14893d75a46dc66d3042356bb054d632c2) )
	ROM_LOAD( "66315361", 0x2000, 0x2000, CRC(21d59d09) SHA1(9c087537d68c600ddf1eb9b009cf458231c279f4) )

	ROM_REGION( 0x4000, "16krom", 0 )
	ROM_LOAD( "66315360", 0x0000, 0x1000, CRC(feaa0fc5) SHA1(f06196553a1f10c07b2f7e495823daf7ea26edee) )
	//ROM_FILL(0x0157,1,0xe0)

	ROM_REGION( 0x4000, "rompack", ROMREGION_ERASE00 )

	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "66315039", 0x0000, 0x1000, CRC(5c9aa968) SHA1(3ec7c5f25562579e6ed3fda7562428ff5e6b9550) )
	ROM_LOAD( "66307828", 0x1000, 0x1000, CRC(ac97136f) SHA1(0d280e1aa4b9502bd390d260f83af19bf24905cd) ) // foreign character ROM

	// Graphics Firmware pack
	ROM_REGION( 0x4000, "gfxfw", 0 ) // load at 0x8000
	ROM_LOAD( "66315369.bin", 0x0000, 0x2000, CRC(224d3368) SHA1(e335ef6cd56d77194235f5a2a7cf2af9ebf42342) )
	ROM_LOAD( "66315370.bin", 0x2000, 0x2000, CRC(2543bf32) SHA1(1ac73a0e475d9fd86fba054e1a7a443d5bad1987) )
ROM_END

} // anonymous namespace


COMP( 1981, cdc721, 0, 0, cdc721, cdc721, cdc721_state, empty_init, "Control Data Corporation", "721 Display Terminal", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



cdi.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/******************************************************************************


    Philips CD-i consoles and games
    -------------------------------

    Preliminary MAME driver by Ryan Holtz
    Help provided by CD-i Fan


*******************************************************************************

STATUS:

  CD-i:
- The SLAVE MCU cannot be low-level emulated until there is proper /DTACK
  support in the 68k core. A2/A1 and D7..D0 are hooked up to Port C bits 1/0
  and Port A respectively, the Read/Write signal is sent to Port D bit 7, and
  /DTACK is received from Port B bit 6. The MCU therefore has the capability to
  pull /DTACK high on a data read in order to tell the 68k to hold off until
  data is ready.

- There is currently a lack of documentation on any of the chips used for
  audio in any of the CD-i models. The CDIC, which was used on Mono-I boards,
  is partially emulated thanks to information provided by CD-i Fan, the author
  of CD-i Emu. Desired documentation includes:
  * GSX38KG307CE46, "ATTEX"
  * Philips IMS66490, "CDIC" ADPCM decoder
  * PC85010 DSP

TODO:

- Screen clocks are a hack right now; they should be exactly CLOCK_A/2. However, the
  MCD-212 documentation states in both tables and timing diagrams that vertical retrace
  has an additional half-line even in non-interlaced mode, which cannot be represented
  in the current screen-timing framework. The input clock has been adjusted downward
  to factor out this half-line, resulting in the expected 50Hz exactly in PAL mode.

- Proper abstraction of the 68070's internal devices (UART, DMA, Timers, etc.)

- Mono-I: Full emulation of the CDIC, as well as the SERVO and SLAVE MCUs

- Mono-II: SERVO and SLAVE I/O device hookup
- Mono-II: DSP56k hookup

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

#include "emu.h"
#include "cdi.h"

#include "cpu/m6805/m6805.h"
#include "imagedev/cdromimg.h"
#include "machine/timekpr.h"
#include "sound/cdda.h"

#include "emupal.h"
#include "screen.h"
#include "softlist.h"
#include "speaker.h"

#include "cdrom.h"

#include "cdi.lh"

// TODO: NTSC system clock is 30.2098 MHz; additional 4.9152 MHz XTAL provided for UART
#define CLOCK_A 30_MHz_XTAL

#define LOG_DVC             (1U << 1)
#define LOG_QUIZARD_READS   (1U << 2)
#define LOG_QUIZARD_WRITES  (1U << 3)
#define LOG_QUIZARD_OTHER   (1U << 4)
#define LOG_UART            (1U << 5)

#define VERBOSE         (0)
#include "logmacro.h"

#define ENABLE_UART_PRINTING (0)

/*************************
*      Memory maps       *
*************************/

void cdi_state::cdimono1_mem(address_map &map)
{
	map(0x000000, 0xffffff).rw(FUNC(cdi_state::bus_error_r), FUNC(cdi_state::bus_error_w));
	map(0x000000, 0x07ffff).rw(FUNC(cdi_state::plane_r<0>), FUNC(cdi_state::plane_w<0>)).share("plane0");
	map(0x200000, 0x27ffff).rw(FUNC(cdi_state::plane_r<1>), FUNC(cdi_state::plane_w<1>)).share("plane1");
	map(0x300000, 0x303bff).rw(m_cdic, FUNC(cdicdic_device::ram_r), FUNC(cdicdic_device::ram_w));
#if ENABLE_UART_PRINTING
	map(0x301400, 0x301403).r(m_maincpu, FUNC(scc68070_device::uart_loopback_enable));
#endif
	map(0x303c00, 0x303fff).rw(m_cdic, FUNC(cdicdic_device::regs_r), FUNC(cdicdic_device::regs_w));
	map(0x310000, 0x317fff).rw(m_slave_hle, FUNC(cdislave_hle_device::slave_r), FUNC(cdislave_hle_device::slave_w));
	map(0x318000, 0x31ffff).noprw();
	map(0x320000, 0x323fff).rw("mk48t08", FUNC(timekeeper_device::read), FUNC(timekeeper_device::write)).umask16(0xff00);    /* nvram (only low bytes used) */
	map(0x400000, 0x47ffff).r(FUNC(cdi_state::main_rom_r));
	map(0x4fffe0, 0x4fffff).m(m_mcd212, FUNC(mcd212_device::map));
	map(0x500000, 0x57ffff).ram();
	map(0xd00000, 0xdfffff).ram(); // DVC RAM block 1
	map(0xe00000, 0xe7ffff).rw(FUNC(cdi_state::dvc_r), FUNC(cdi_state::dvc_w));
	map(0xe80000, 0xefffff).ram(); // DVC RAM block 2
}

void cdi_state::cdimono2_mem(address_map &map)
{
	map(0x000000, 0x07ffff).ram().share("plane0");
	map(0x200000, 0x27ffff).ram().share("plane1");
#if ENABLE_UART_PRINTING
	map(0x301400, 0x301403).r(m_maincpu, FUNC(scc68070_device::uart_loopback_enable));
#endif
	map(0x320000, 0x323fff).rw("mk48t08", FUNC(timekeeper_device::read), FUNC(timekeeper_device::write)).umask16(0xff00);    /* nvram (only low bytes used) */
	map(0x400000, 0x47ffff).r(FUNC(cdi_state::main_rom_r));
	map(0x4fffe0, 0x4fffff).m(m_mcd212, FUNC(mcd212_device::map));
}

void cdi_state::cdi910_mem(address_map &map)
{
	map(0x000000, 0x07ffff).ram().share("plane0");
	map(0x180000, 0x1fffff).rom().region("maincpu", 0); // boot vectors point here
	map(0x200000, 0x27ffff).ram().share("plane1");
#if ENABLE_UART_PRINTING
	map(0x301400, 0x301403).r(m_maincpu, FUNC(scc68070_device::uart_loopback_enable));
#endif
	map(0x320000, 0x323fff).rw("mk48t08", FUNC(timekeeper_device::read), FUNC(timekeeper_device::write)).umask16(0xff00);    /* nvram (only low bytes used) */
	map(0x4fffe0, 0x4fffff).m(m_mcd212, FUNC(mcd212_device::map));
	map(0x500000, 0xffffff).noprw();
}


/*************************
*      Input ports       *
*************************/

static INPUT_PORTS_START( cdi )
	PORT_START("MOUSEX")
	PORT_BIT(0xffff, 0x000, IPT_MOUSE_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(2)

	PORT_START("MOUSEY")
	PORT_BIT(0xffff, 0x000, IPT_MOUSE_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(2)

	PORT_START("MOUSEBTN")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_CODE(MOUSECODE_BUTTON1) PORT_NAME("Button 1")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_CODE(MOUSECODE_BUTTON2) PORT_NAME("Button 2")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_CODE(MOUSECODE_BUTTON3) PORT_NAME("Button 3")
	PORT_BIT(0xf8, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( cdimono2 )
INPUT_PORTS_END

static INPUT_PORTS_START( quizard )
	PORT_START("P0")
	PORT_DIPNAME( 0x07, 0x05, "Settings" )
	PORT_DIPSETTING(    0x00, "1 Coin, 0 Bonus Limit, 0 Bonus Number" )
	PORT_DIPSETTING(    0x01, "2 Coins, 0 Bonus Limit, 0 Bonus Number" )
	PORT_DIPSETTING(    0x02, "1 Coin, 2 Bonus Limit, 1 Bonus Number" )
	PORT_DIPSETTING(    0x03, "1 Coin, 3 Bonus Limit, 1 Bonus Number" )
	PORT_DIPSETTING(    0x04, "1 Coin, 5 Bonus Limit, 1 Bonus Number" )
	PORT_DIPSETTING(    0x05, "1 Coin, 5 Bonus Limit, 2 Bonus Number" )
	PORT_DIPSETTING(    0x06, "1 Coin, 10 Bonus Limit, 2 Bonus Number" )
	PORT_DIPSETTING(    0x07, "2 Coins, 4 Bonus Limit, 1 Bonus Number" )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_COIN1 )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_COIN2 )
	PORT_BIT( 0xc8, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P1")
	PORT_BIT( 0x1f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_START2 )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_SERVICE1 )

	PORT_START("P2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Player 1 A")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Player 1 B")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Player 1 C")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Player 2 A")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("Player 2 B")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("Player 2 C")
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END


/***************************
*  Machine Initialization  *
***************************/

void cdi_state::machine_reset()
{
	uint16_t *src = &m_main_rom[0];
	uint16_t *dst = &m_plane_ram[0][0];
	memcpy(dst, src, 0x8);
}

void quizard_state::machine_start()
{
	save_item(NAME(m_boot_press));

	m_boot_timer = timer_alloc(FUNC(quizard_state::boot_press_tick), this);

	set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1);
	set_rate(9600);
}

void quizard_state::machine_reset()
{
	cdi_state::machine_reset();

	m_boot_press = false;
	m_boot_timer->adjust(attotime::from_seconds(13), 1);
	m_mcu_p3 = 0x05; // RTS|RXD
}


/***************************
*  Wait-State Handling     *
***************************/

template<int Channel>
uint16_t cdi_state::plane_r(offs_t offset, uint16_t mem_mask)
{
	m_maincpu->eat_cycles(m_mcd212->ram_dtack_cycle_count<Channel>());
	return m_plane_ram[Channel][offset];
}

template<int Channel>
void cdi_state::plane_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	m_maincpu->eat_cycles(m_mcd212->ram_dtack_cycle_count<Channel>());
	COMBINE_DATA(&m_plane_ram[Channel][offset]);
}

uint16_t cdi_state::main_rom_r(offs_t offset)
{
	m_maincpu->eat_cycles(m_mcd212->rom_dtack_cycle_count());
	return m_main_rom[offset];
}


/**********************
*  BERR Handling      *
**********************/

uint16_t cdi_state::bus_error_r(offs_t offset)
{
	if(!machine().side_effects_disabled())
	{
		m_maincpu->set_buserror_details(offset*2, true, m_maincpu->get_fc());
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
	}
	return 0xff;
}

void cdi_state::bus_error_w(offs_t offset, uint16_t data)
{
	if(!machine().side_effects_disabled())
	{
		m_maincpu->set_buserror_details(offset*2, false, m_maincpu->get_fc());
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
	}
}


/**********************
*  Quizard Protection *
**********************/

TIMER_CALLBACK_MEMBER(quizard_state::boot_press_tick)
{
	m_boot_press = (bool)param;
	if (m_boot_press)
		m_boot_timer->adjust(attotime::from_msec(250), 0);
}

uint8_t quizard_state::mcu_button_press()
{
	return (uint8_t)m_boot_press;
}

void quizard_state::mcu_rtsn_from_cpu(int state)
{
	LOGMASKED(LOG_UART, "MCU receiving RTSN from CPU: %d\n", state);
}

void quizard_state::mcu_rx_from_cpu(uint8_t data)
{
	LOGMASKED(LOG_UART, "MCU receiving %02x from CPU\n", data);

	transmit_register_setup(data);
}

uint8_t quizard_state::mcu_p0_r()
{
	const uint8_t data = m_inputs[0]->read();
	LOGMASKED(LOG_QUIZARD_READS, "%s: MCU Port 0 Read (%02x)\n", machine().describe_context(), data);
	return data;
}

uint8_t quizard_state::mcu_p1_r()
{
	uint8_t data = m_inputs[1]->read();
	if (BIT(~m_inputs[0]->read(), 4))
		data &= ~(1 << 4);
	LOGMASKED(LOG_QUIZARD_READS, "%s: MCU Port 1 Read (%02x)\n", machine().describe_context(), data);
	return data;
}

uint8_t quizard_state::mcu_p2_r()
{
	const uint8_t data = m_inputs[2]->read();
	LOGMASKED(LOG_QUIZARD_READS, "%s: MCU Port 2 Read (%02x)\n", machine().describe_context(), data);
	return data;
}

uint8_t quizard_state::mcu_p3_r()
{
	LOGMASKED(LOG_QUIZARD_READS, "%s: MCU Port 3 Read (%02x)\n", machine().describe_context(), m_mcu_p3);
	return m_mcu_p3;
}

void quizard_state::mcu_p0_w(uint8_t data)
{
	LOGMASKED(LOG_QUIZARD_WRITES, "%s: MCU Port 0 Write (%02x)\n", machine().describe_context(), data);
}

void quizard_state::mcu_p1_w(uint8_t data)
{
	LOGMASKED(LOG_QUIZARD_WRITES, "%s: MCU Port 1 Write (%02x)\n", machine().describe_context(), data);
}

void quizard_state::mcu_p2_w(uint8_t data)
{
	LOGMASKED(LOG_QUIZARD_WRITES, "%s: MCU Port 2 Write (%02x)\n", machine().describe_context(), data);
}

void quizard_state::mcu_p3_w(uint8_t data)
{
	LOGMASKED(LOG_QUIZARD_WRITES, "%s: MCU Port 3 Write (%02x)\n", machine().describe_context(), data);
	rx_w(BIT(data, 1));
	m_maincpu->uart_ctsn(BIT(data, 6));
}

/*************************
*     DVC cartridge      *
*************************/

uint16_t cdi_state::dvc_r(offs_t offset, uint16_t mem_mask)
{
	LOGMASKED(LOG_DVC, "%s: dvc_r: %08x = 0000 & %04x\n", machine().describe_context(), 0xe80000 + (offset << 1), mem_mask);
	return 0;
}

void cdi_state::dvc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	LOGMASKED(LOG_DVC, "%s: dvc_w: %08x = %04x & %04x\n", machine().describe_context(), 0xe80000 + (offset << 1), data, mem_mask);
}

/*************************
*       LCD screen       *
*************************/

static const uint16_t cdi220_lcd_char[20*22] =
{
	0x2000, 0x2000, 0x2000, 0x2000, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0200, 0x0200, 0x0200, 0x0200,
	0x2000, 0x2000, 0x2000, 0x2000, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0200, 0x0200, 0x0200, 0x0200,
	0x2000, 0x2000, 0x2000, 0x2000, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0200, 0x0200, 0x0200, 0x0200,
	0x2000, 0x2000, 0x2000, 0x2000, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0200, 0x0200, 0x0200, 0x0200,
	0x2000, 0x2000, 0x2000, 0x2000, 0x8000, 0x8000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0002, 0x0200, 0x0200, 0x0200, 0x0200,
	0x2000, 0x2000, 0x2000, 0x2000, 0x8000, 0x8000, 0x8000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0200, 0x0200, 0x0200, 0x0200,
	0x2000, 0x2000, 0x2000, 0x2000, 0x8000, 0x8000, 0x8000, 0x8000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0200, 0x0200, 0x0200, 0x0200,
	0x2000, 0x2000, 0x2000, 0x2000, 0x0000, 0x8000, 0x8000, 0x8000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002, 0x0002, 0x0000, 0x0200, 0x0200, 0x0200, 0x0200,
	0x2000, 0x2000, 0x2000, 0x2000, 0x0000, 0x0000, 0x8000, 0x8000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002, 0x0000, 0x0000, 0x0200, 0x0200, 0x0200, 0x0200,
	0x2000, 0x2000, 0x2000, 0x2000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x0200, 0x0200, 0x0200, 0x0200,
	0x2000, 0x2000, 0x2000, 0x2000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x0200, 0x0200, 0x0200, 0x0200,
	0x1000, 0x1000, 0x1000, 0x1000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x0400, 0x0400, 0x0400, 0x0400,
	0x1000, 0x1000, 0x1000, 0x1000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x0400, 0x0400, 0x0400, 0x0400,
	0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, 0x0010, 0x0010, 0x0001, 0x0001, 0x0001, 0x0001, 0x0008, 0x0008, 0x0000, 0x0000, 0x0400, 0x0400, 0x0400, 0x0400,
	0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0010, 0x0010, 0x0010, 0x0001, 0x0001, 0x0001, 0x0001, 0x0008, 0x0008, 0x0008, 0x0000, 0x0400, 0x0400, 0x0400, 0x0400,
	0x1000, 0x1000, 0x1000, 0x1000, 0x0010, 0x0010, 0x0010, 0x0010, 0x0001, 0x0001, 0x0001, 0x0001, 0x0008, 0x0008, 0x0008, 0x0008, 0x0400, 0x0400, 0x0400, 0x0400,
	0x1000, 0x1000, 0x1000, 0x1000, 0x0010, 0x0010, 0x0010, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0008, 0x0008, 0x0008, 0x0400, 0x0400, 0x0400, 0x0400,
	0x1000, 0x1000, 0x1000, 0x1000, 0x0010, 0x0010, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0008, 0x0008, 0x0400, 0x0400, 0x0400, 0x0400,
	0x1000, 0x1000, 0x1000, 0x1000, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0400, 0x0400, 0x0400, 0x0400,
	0x1000, 0x1000, 0x1000, 0x1000, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0400, 0x0400, 0x0400, 0x0400,
	0x1000, 0x1000, 0x1000, 0x1000, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0400, 0x0400, 0x0400, 0x0400,
	0x1000, 0x1000, 0x1000, 0x1000, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0400, 0x0400, 0x0400, 0x0400
};

uint32_t cdi_state::screen_update_cdimono1_lcd(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if (!m_slave_hle.found())
		return 0;

	for (int y = 0; y < 22; y++)
	{
		uint32_t *scanline = &bitmap.pix(y);

		for (int lcd = 0; lcd < 8; lcd++)
		{
			uint16_t data = (m_slave_hle->get_lcd_state()[lcd*2] << 8) |
							m_slave_hle->get_lcd_state()[lcd*2 + 1];
			for (int x = 0; x < 20; x++)
			{
				if (data & cdi220_lcd_char[y*20 + x])
				{
					scanline[(7 - lcd)*24 + x] = rgb_t::white();
				}
				else
				{
					scanline[(7 - lcd)*24 + x] = rgb_t::black();
				}
			}
		}
	}

	return 0;
}

/*************************
*    Machine Drivers     *
*************************/

// CD-i Mono-I system base
void cdi_state::cdimono1_base(machine_config &config)
{
	SCC68070(config, m_maincpu, CLOCK_A);
	m_maincpu->set_addrmap(AS_PROGRAM, &cdi_state::cdimono1_mem);
	m_maincpu->iack4_callback().set(m_cdic, FUNC(cdicdic_device::intack_r));

	MCD212(config, m_mcd212, CLOCK_A, m_plane_ram[0], m_plane_ram[1]);
	m_mcd212->set_screen("screen");
	m_mcd212->int_callback().set(m_maincpu, FUNC(scc68070_device::int1_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(960*(312*2-32)*50, 960, 0, 768, 312*2-32, 32, 312*2-32);
	screen.set_video_attributes(VIDEO_UPDATE_SCANLINE);
	screen.set_screen_update(m_mcd212, FUNC(mcd212_device::screen_update));

	SCREEN(config, m_lcd, SCREEN_TYPE_RASTER);
	m_lcd->set_refresh_hz(50);
	m_lcd->set_vblank_time(ATTOSECONDS_IN_USEC(0));
	m_lcd->set_size(192, 22);
	m_lcd->set_visarea(0, 192-1, 0, 22-1);
	m_lcd->set_screen_update(FUNC(cdi_state::screen_update_cdimono1_lcd));

	PALETTE(config, "palette").set_entries(0x100);

	config.set_default_layout(layout_cdi);

	// IMS66490 CDIC input clocks are 22.5792 MHz and 19.3536 MHz
	// DSP input clock is 7.5264 MHz
	CDI_CDIC(config, m_cdic, 45.1584_MHz_XTAL / 2);
	m_cdic->set_clock2(45.1584_MHz_XTAL * 3 / 7); // generated by PLL circuit incorporating 19.3575 MHz XTAL
	m_cdic->intreq_callback().set(m_maincpu, FUNC(scc68070_device::in4_w));

	CDI_SLAVE_HLE(config, m_slave_hle, 0);
	m_slave_hle->int_callback().set(m_maincpu, FUNC(scc68070_device::in2_w));

	CDROM(config, m_cdrom);
	m_cdrom->set_interface("cdrom");

	/* sound hardware */
	SPEAKER(config, "speaker", 2).front();

	DMADAC(config, m_dmadac[0]);
	m_dmadac[0]->add_route(ALL_OUTPUTS, "speaker", 1.0, 0);

	DMADAC(config, m_dmadac[1]);
	m_dmadac[1]->add_route(ALL_OUTPUTS, "speaker", 1.0, 1);

	MK48T08(config, "mk48t08");
}

// CD-i model 220 (Mono-II, NTSC)
void cdi_state::cdimono2(machine_config &config)
{
	SCC68070(config, m_maincpu, CLOCK_A);
	m_maincpu->set_addrmap(AS_PROGRAM, &cdi_state::cdimono2_mem);

	MCD212(config, m_mcd212, CLOCK_A, m_plane_ram[0], m_plane_ram[1]);
	m_mcd212->set_screen("screen");
	m_mcd212->int_callback().set(m_maincpu, FUNC(scc68070_device::int1_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(14976000, 960, 0, 768, 312, 32, 312);
	screen.set_video_attributes(VIDEO_UPDATE_SCANLINE);
	screen.set_screen_update(m_mcd212, FUNC(mcd212_device::screen_update));

	SCREEN(config, m_lcd, SCREEN_TYPE_RASTER);
	m_lcd->set_refresh_hz(60);
	m_lcd->set_vblank_time(ATTOSECONDS_IN_USEC(0));
	m_lcd->set_size(192, 22);
	m_lcd->set_visarea(0, 192-1, 0, 22-1);
	m_lcd->set_screen_update(FUNC(cdi_state::screen_update_cdimono1_lcd));

	PALETTE(config, "palette").set_entries(0x100);

	config.set_default_layout(layout_cdi);

	M68HC05C8(config, m_servo, 4_MHz_XTAL);
	M68HC05C8(config, m_slave, 4_MHz_XTAL);

	CDROM(config, m_cdrom).set_interface("cdrom");
	SOFTWARE_LIST(config, "cd_list").set_original("cdi").set_filter("!DVC");
	SOFTWARE_LIST(config, "photocd_list").set_compatible("photo_cd");

	/* sound hardware */
	SPEAKER(config, "speaker", 2).front();

	DMADAC(config, m_dmadac[0]);
	m_dmadac[0]->add_route(ALL_OUTPUTS, "speaker", 1.0, 0);

	DMADAC(config, m_dmadac[1]);
	m_dmadac[1]->add_route(ALL_OUTPUTS, "speaker", 1.0, 1);

	MK48T08(config, "mk48t08");
}

void cdi_state::cdi910(machine_config &config)
{
	SCC68070(config, m_maincpu, CLOCK_A);
	m_maincpu->set_addrmap(AS_PROGRAM, &cdi_state::cdi910_mem);

	MCD212(config, m_mcd212, CLOCK_A, m_plane_ram[0], m_plane_ram[1]);
	m_mcd212->set_screen("screen");
	m_mcd212->int_callback().set(m_maincpu, FUNC(scc68070_device::int1_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(14976000, 960, 0, 768, 312, 32, 312);
	screen.set_video_attributes(VIDEO_UPDATE_SCANLINE);
	screen.set_screen_update(m_mcd212, FUNC(mcd212_device::screen_update));

	SCREEN(config, m_lcd, SCREEN_TYPE_RASTER);
	m_lcd->set_refresh_hz(60);
	m_lcd->set_vblank_time(ATTOSECONDS_IN_USEC(0));
	m_lcd->set_size(192, 22);
	m_lcd->set_visarea(0, 192-1, 0, 22-1);
	m_lcd->set_screen_update(FUNC(cdi_state::screen_update_cdimono1_lcd));

	PALETTE(config, "palette").set_entries(0x100);

	config.set_default_layout(layout_cdi);

	M68HC05C8(config, m_servo, 4_MHz_XTAL);
	M68HC05C8(config, m_slave, 4_MHz_XTAL);

	CDROM(config, "cdrom").set_interface("cdrom");
	SOFTWARE_LIST(config, "cd_list").set_original("cdi").set_filter("!DVC");
	SOFTWARE_LIST(config, "photocd_list").set_compatible("photo_cd");

	/* sound hardware */
	SPEAKER(config, "speaker", 2).front();

	DMADAC(config, m_dmadac[0]);
	m_dmadac[0]->add_route(ALL_OUTPUTS, "speaker", 1.0, 0);

	DMADAC(config, m_dmadac[1]);
	m_dmadac[1]->add_route(ALL_OUTPUTS, "speaker", 1.0, 1);

	MK48T08(config, "mk48t08");
}

// CD-i Mono-I, with CD-ROM image device (MESS) and Software List (MESS)
void cdi_state::cdimono1(machine_config &config)
{
	cdimono1_base(config);

	m_slave_hle->read_mousex().set_ioport("MOUSEX");
	m_slave_hle->read_mousey().set_ioport("MOUSEY");
	m_slave_hle->read_mousebtn().set_ioport("MOUSEBTN");

	SOFTWARE_LIST(config, "cd_list").set_original("cdi").set_filter("!DVC");
	SOFTWARE_LIST(config, "photocd_list").set_compatible("photo_cd");
}

void quizard_state::quizard(machine_config &config)
{
	cdimono1_base(config);
	m_cdrom->add_region("cdrom");

	m_maincpu->set_addrmap(AS_PROGRAM, &quizard_state::cdimono1_mem);
	m_maincpu->uart_rtsn_callback().set(FUNC(quizard_state::mcu_rtsn_from_cpu));
	m_maincpu->uart_tx_callback().set(FUNC(quizard_state::mcu_rx_from_cpu));

	I8751(config, m_mcu, 11.0592_MHz_XTAL);
	m_mcu->port_in_cb<0>().set(FUNC(quizard_state::mcu_p0_r));
	m_mcu->port_in_cb<1>().set(FUNC(quizard_state::mcu_p1_r));
	m_mcu->port_in_cb<2>().set(FUNC(quizard_state::mcu_p2_r));
	m_mcu->port_in_cb<3>().set(FUNC(quizard_state::mcu_p3_r));
	m_mcu->port_out_cb<0>().set(FUNC(quizard_state::mcu_p0_w));
	m_mcu->port_out_cb<1>().set(FUNC(quizard_state::mcu_p1_w));
	m_mcu->port_out_cb<2>().set(FUNC(quizard_state::mcu_p2_w));
	m_mcu->port_out_cb<3>().set(FUNC(quizard_state::mcu_p3_w));

	m_slave_hle->read_mousebtn().set(FUNC(quizard_state::mcu_button_press));
}

void quizard_state::tra_callback()
{
	if (transmit_register_get_data_bit())
		m_mcu_p3 |= 1;
	else
		m_mcu_p3 &= ~1;
}

void quizard_state::rcv_complete()
{
	receive_register_extract();

	const uint8_t data = get_received_char();
	LOGMASKED(LOG_QUIZARD_OTHER, "%s: MCU transmitting %02x\n", machine().describe_context(), data);
	m_maincpu->uart_rx(data);
}

/*************************
*        Rom Load        *
*************************/

ROM_START( cdimono1 )
	ROM_REGION(0x80000, "maincpu", 0) // these roms need byteswapping
	ROM_SYSTEM_BIOS( 0, "mcdi200", "Magnavox CD-i 200" )
	ROMX_LOAD( "cdi200.rom", 0x000000, 0x80000, CRC(40c4e6b9) SHA1(d961de803c89b3d1902d656ceb9ce7c02dccb40a), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "pcdi220", "Philips CD-i 220 F2" )
	ROMX_LOAD( "cdi220b.rom", 0x000000, 0x80000, CRC(279683ca) SHA1(53360a1f21ddac952e95306ced64186a3fc0b93e), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "pcdi220_alt", "Philips CD-i 220?" ) // doesn't boot
	ROMX_LOAD( "cdi220.rom", 0x000000, 0x80000, CRC(584c0af8) SHA1(5d757ab46b8c8fc36361555d978d7af768342d47), ROM_BIOS(2) )

	// The two MCU dumps below are taken from the cdi910. We still need dumps from a Mono-I board in case the revisions are different.
	ROM_REGION(0x2000, "servo", 0)
	ROM_LOAD( "zx405037p__cdi_servo_2.1__b43t__llek9215.mc68hc705c8a_withtestrom.7201", 0x0000, 0x2000, CRC(7a3af407) SHA1(fdf8d78d6a0df4a56b5b963d72eabd39fcec163f) BAD_DUMP )

	ROM_REGION(0x2000, "slave", 0)
	ROM_LOAD( "zx405042p__cdi_slave_2.0__b43t__zzmk9213.mc68hc705c8a_withtestrom.7206", 0x0000, 0x2000, CRC(688cda63) SHA1(56d0acd7caad51c7de703247cd6d842b36173079) BAD_DUMP )
ROM_END

ROM_START( cdi910 )
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_SYSTEM_BIOS( 0, "cdi910", "CD-I 910-17P Mini-MMC" )
	ROMX_LOAD( "philips__cd-i_2.1__mb834200b-15__26b_aa__9224_z01.tc574200.7211", 0x000000, 0x80000, CRC(4ae3bee3) SHA1(9729b4ee3ce0c17172d062339c47b1ab822b222b), ROM_BIOS(0) | ROM_GROUPWORD | ROM_REVERSE )
	ROM_SYSTEM_BIOS( 1, "cdi910_alt", "alt" )
	ROMX_LOAD( "cdi910.rom", 0x000000, 0x80000, CRC(2f3048d2) SHA1(11c4c3e602060518b52e77156345fa01f619e793), ROM_BIOS(1) | ROM_GROUPWORD | ROM_REVERSE )

	ROM_REGION(0x2000, "servo", 0)
	ROM_LOAD( "zx405037p__cdi_servo_2.1__b43t__llek9215.mc68hc705c8a_withtestrom.7201", 0x0000, 0x2000, CRC(7a3af407) SHA1(fdf8d78d6a0df4a56b5b963d72eabd39fcec163f) )

	ROM_REGION(0x2000, "slave", 0)
	ROM_LOAD( "zx405042p__cdi_slave_2.0__b43t__zzmk9213.mc68hc705c8a_withtestrom.7206", 0x0000, 0x2000, CRC(688cda63) SHA1(56d0acd7caad51c7de703247cd6d842b36173079) )

	ROM_REGION(0x2000, "pals", 0)
	ROM_LOAD( "ti_portugal_206xf__tibpal20l8-15cnt__m7205n.7205.bin",      0x0000, 0x144, CRC(dd167e0d) SHA1(2ba82a4619d7a0f19e62e02a2841afd4d45d56ba) )
	ROM_LOAD( "ti_portugal_774_206xf__tibpal16l8-10cn_m7204n.7204.bin",    0x0000, 0x104, CRC(04e6bd37) SHA1(153d1a977291bedb7420484a9f889325dbd3628e) )
ROM_END

ROM_START( cdimono2 )
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_LOAD16_WORD_SWAP( "philips__cdi-220_ph3_r1.2__mb834200b-15__02f_aa__9402_z04.tc574200-le._1.7211", 0x000000, 0x80000, CRC(17d723e7) SHA1(6c317a82e35d60ca5e7a74fc99f665055693169d) )

	ROM_REGION(0x2000, "servo", 0)
	ROM_LOAD( "zc405351p__servo_cdi_4.1__0d67p__lluk9404.mc68hc705c8a.7490", 0x0000, 0x2000, CRC(2bc8e4e9) SHA1(8cd052b532fc052d6b0077261c12f800e8655bb1) )

	ROM_REGION(0x2000, "slave", 0)
	ROM_LOAD( "zc405352p__slave_cdi_4.1__0d67p__lltr9403.mc68hc705c8a.7206", 0x0000, 0x2000, CRC(5b19da07) SHA1(cf02d84977050c71e87a38f1249e83c43a93949b) )
ROM_END

ROM_START( cdi490a )
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_SYSTEM_BIOS( 0, "cdi490", "CD-i 490" )
	ROMX_LOAD( "cdi490a.rom", 0x000000, 0x80000, CRC(e2f200f6) SHA1(c9bf3c4c7e4fe5cbec3fe3fc993c77a4522ca547), ROM_BIOS(0) | ROM_GROUPWORD | ROM_REVERSE  )

	ROM_REGION(0x60000, "mpegs", 0) // keep these somewhere
	ROM_LOAD( "impega.rom", 0x00000, 0x40000, CRC(84d6f6aa) SHA1(02526482a0851ea2a7b582d8afaa8ef14a8bd914) ) // 1ST AND 2ND HALF IDENTICAL
	// Philips CD-i - DVC card 22ER9141
	ROM_LOAD16_BYTE( "fmv ffd9 p7308 r4.1 vmpeg.bin", 0x40000, 0x10000, CRC(30ba9273) SHA1(d8adca0627b356ced6131b9458ac1175e43e6548) )
	ROM_LOAD16_BYTE( "fmv 4ba9 p7307 r4.1 vmpeg.bin", 0x40001, 0x10000, CRC(623edb1f) SHA1(4c6b11e28ad4c2f5c2e439f7910a783e0a79d1a9) )
ROM_END

ROM_START( gpi1200 )
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_LOAD16_WORD_SWAP( "gpi-1200k-1313.bin", 0x000000, 0x80000, CRC(dbd41615) SHA1(83929617a5c01551ee961aeb685295fcc0810f54) )
ROM_END

ROM_START( cdibios ) // for the quizard sets
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_SYSTEM_BIOS( 0, "mcdi200", "Magnavox CD-i 200" )
	ROMX_LOAD( "cdi200.rom", 0x000000, 0x80000, CRC(40c4e6b9) SHA1(d961de803c89b3d1902d656ceb9ce7c02dccb40a), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "pcdi220", "Philips CD-i 220 F2" )
	ROMX_LOAD( "cdi220b.rom", 0x000000, 0x80000, CRC(279683ca) SHA1(53360a1f21ddac952e95306ced64186a3fc0b93e), ROM_BIOS(1) )

	// The MCU dump below is taken from the cdi910. We still need a dump from a Mono-I board SLAVE MCU in case the revisions are different.
	ROM_REGION(0x2000, "slave", 0)
	ROM_LOAD( "zx405042p__cdi_slave_2.0__b43t__zzmk9213.mc68hc705c8a_withtestrom.7206", 0x0000, 0x2000, CRC(688cda63) SHA1(56d0acd7caad51c7de703247cd6d842b36173079) BAD_DUMP )
ROM_END

/*  Quizard notes

    The MCU controls the protection sequence, which in turn controls the game display language.
    Each Quizard game (1,2,3,4) requires its own MCU, you can upgrade between revisions by changing
    just the CD, but not between games as a new MCU is required.

    MCU Notes:
    i8751 MCU dumps confirmed good on original hardware
    Italian language MCU for Quizard 1 is dumped
    German language MCUs for Quizard 1 through 4 are dumped
    Czech language MCU for Quizard 4 is dumped
    Alt. German language MCU for Quizard 2 is known to exist (DE 122 D3, not dumped)

*/


#define QUIZARD_BIOS_ROM \
	ROM_REGION(0x80000, "maincpu", 0) \
	ROM_LOAD( "cdi220b.rom", 0x000000, 0x80000, CRC(279683ca) SHA1(53360a1f21ddac952e95306ced64186a3fc0b93e) )

//********************************************************
//                     Quizard (1)
//********************************************************

#define QUIZARD1_CHD_10 \
	DISK_REGION( "cdrom" ) \
	DISK_IMAGE_READONLY( "quizard10", 0, SHA1(5715db50f0d5ffe06f47c0943f4bf0481ab6048e) ) // Dumped via BurnAtOnce 0.99.5, CHDMAN 0.163, TS-L633R drive

// CD-ROM printed 01/95
#define QUIZARD1_CHD_12 \
	DISK_REGION( "cdrom" ) \
	DISK_IMAGE_READONLY( "quizard12", 0, BAD_DUMP SHA1(6e41683b96b74e903040842aeb18437ad7813c82) )

#define QUIZARD1_CHD_17 \
	DISK_REGION( "cdrom" ) \
	DISK_IMAGE_READONLY( "quizard17", 0, BAD_DUMP SHA1(4bd698f076505b4e17be978481bce027eb47123b) )

#define QUIZARD1_CHD_18 \
	DISK_REGION( "cdrom" ) \
	DISK_IMAGE_READONLY( "quizard18", 0, BAD_DUMP SHA1(ede873b22957f2a707bbd3039e962ef2ca5aedbd) )

// MCU Type: Intel D8751H MCU
#define QUIZARD1_MCU_DE \
	ROM_REGION(0x1000, "mcu", 0) \
	ROM_LOAD( "de_11_d3.bin", 0x0000, 0x1000, CRC(95f45b6b) SHA1(51b34956539b1e2cf0306f243a970750f1e18d01) ) // German

#define QUIZARD1_MCU_IT \
	ROM_REGION(0x1000, "mcu", 0) \
	ROM_LOAD( "it_11_i2.bin", 0x0000, 0x1000, CRC(e00dc02c) SHA1(e4ef1ea47c242879a99c9d54cfc008ae99a651cb) ) // Italian

ROM_START( quizard )
	QUIZARD_BIOS_ROM
	QUIZARD1_CHD_18
	QUIZARD1_MCU_DE
ROM_END

ROM_START( quizard_17 )
	QUIZARD_BIOS_ROM
	QUIZARD1_CHD_17
	QUIZARD1_MCU_DE
ROM_END

ROM_START( quizard_12 )
	QUIZARD_BIOS_ROM
	QUIZARD1_CHD_12
	QUIZARD1_MCU_DE
ROM_END

ROM_START( quizard_10 )
	QUIZARD_BIOS_ROM
	QUIZARD1_CHD_10
	QUIZARD1_MCU_DE
ROM_END

ROM_START( quizardi )
	QUIZARD_BIOS_ROM
	QUIZARD1_CHD_18
	QUIZARD1_MCU_IT
ROM_END

ROM_START( quizardi_17 )
	QUIZARD_BIOS_ROM
	QUIZARD1_CHD_17
	QUIZARD1_MCU_IT
ROM_END

ROM_START( quizardi_12 )
	QUIZARD_BIOS_ROM
	QUIZARD1_CHD_12
	QUIZARD1_MCU_IT
ROM_END

//********************************************************
//                     Quizard 2
//********************************************************

ROM_START( quizard2 ) /* CD-ROM printed ??/?? */
	QUIZARD_BIOS_ROM

	DISK_REGION( "cdrom" )
	DISK_IMAGE_READONLY( "quizard23", 0, BAD_DUMP SHA1(cd909d9a54275d6f2d36e03e83eea996e781b4d3) )

	ROM_REGION(0x1000, "mcu", 0) // Intel D8751H MCU
	ROM_LOAD( "dn_122_d3.bin", 0x0000, 0x1000, CRC(d48063ea) SHA1(b512fa5e53f296a180340e09b53613dd1c0d38bc) ) // German language - DE 122 D3 known to exist
ROM_END

ROM_START( quizard2_22 )
	QUIZARD_BIOS_ROM

	DISK_REGION( "cdrom" )
	DISK_IMAGE_READONLY( "quizard22", 0, BAD_DUMP SHA1(03c8fdcf27ead6e221691111e8c679b551099543) )

	ROM_REGION(0x1000, "mcu", 0) // Intel D8751H MCU
	ROM_LOAD( "dn_122_d3.bin", 0x0000, 0x1000, CRC(d48063ea) SHA1(b512fa5e53f296a180340e09b53613dd1c0d38bc) ) // German language - DE 122 D3 known to exist
ROM_END


//********************************************************
//                     Quizard 3
//********************************************************

ROM_START( quizard3 ) /* CD-ROM printed ??/?? */
	QUIZARD_BIOS_ROM

	DISK_REGION( "cdrom" )
	DISK_IMAGE_READONLY( "quizard34", 0, BAD_DUMP SHA1(37ad49b72b5175afbb87141d57bc8604347fe032) )

	ROM_REGION(0x1000, "mcu", 0) // Intel D8751H MCU
	ROM_LOAD( "de_132_d3.bin", 0x0000, 0x1000, CRC(8858251e) SHA1(2c1005a74bb6f0c2918dff4ab6326528eea48e1f) ) // German language
ROM_END

ROM_START( quizard3a ) /* CD-ROM printed ??/?? */
	QUIZARD_BIOS_ROM

	DISK_REGION( "cdrom" )
	DISK_IMAGE_READONLY( "quizard34", 0, BAD_DUMP SHA1(37ad49b72b5175afbb87141d57bc8604347fe032) )

	ROM_REGION(0x1000, "mcu", 0) // Intel D8751H MCU
	ROM_LOAD( "de_132_a1.bin", 0x0000, 0x1000, CRC(313ac673) SHA1(cb0ee7e9a6eaa5f4d000f5ea99b7ee4c440b31d1) ) // German language - earlier version of MCU code
ROM_END

ROM_START( quizard3_32 )
	QUIZARD_BIOS_ROM

	DISK_REGION( "cdrom" )
	DISK_IMAGE_READONLY( "quizard32", 0, BAD_DUMP SHA1(31e9fa2169aa44d799c37170b238134ab738e1a1) )

	ROM_REGION(0x1000, "mcu", 0) // Intel D8751H MCU
	ROM_LOAD( "de_132_d3.bin", 0x0000, 0x1000, CRC(8858251e) SHA1(2c1005a74bb6f0c2918dff4ab6326528eea48e1f) ) // German language
ROM_END


//********************************************************
//                     Quizard 4
//********************************************************

ROM_START( quizard4 ) /* CD-ROM printed 09/98 */
	QUIZARD_BIOS_ROM

	DISK_REGION( "cdrom" )
	DISK_IMAGE_READONLY( "quizard4r42", 0, BAD_DUMP SHA1(a5d5c8950b4650b8753f9119dc7f1ccaa2aa5442) )

	ROM_REGION(0x1000, "mcu", 0) // Intel D8751H MCU
	ROM_LOAD( "de_142_d3.bin", 0x0000, 0x1000, CRC(77be0b40) SHA1(113b5c239480a2259f55e411ba8fb3972e6d4301) ) // German language
ROM_END

ROM_START( quizard4cz ) /* CD-ROM printed 09/98 */
	QUIZARD_BIOS_ROM

	DISK_REGION( "cdrom" )
	DISK_IMAGE_READONLY( "quizard4r42", 0, BAD_DUMP SHA1(a5d5c8950b4650b8753f9119dc7f1ccaa2aa5442) )

	ROM_REGION(0x1000, "mcu", 0) // Intel D8751H MCU
	ROM_LOAD( "ts142_cz1.bin", 0x0000, 0x1000, CRC(fdc1f457) SHA1(5169c4d2ea4073a854c3f619205161386c9af8af) ) // Czech language - works with all Quizard 4 versions
ROM_END

ROM_START( quizard4_41 )
	QUIZARD_BIOS_ROM

	DISK_REGION( "cdrom" )
	DISK_IMAGE_READONLY( "quizard4r41", 0, BAD_DUMP SHA1(2c0484c6545aac8e00b318328c6edce6f5dde43d) )

	ROM_REGION(0x1000, "mcu", 0) // Intel D8751H MCU
	ROM_LOAD( "de_142_d3.bin", 0x0000, 0x1000, CRC(77be0b40) SHA1(113b5c239480a2259f55e411ba8fb3972e6d4301) ) // German language
ROM_END

ROM_START( quizard4_40 ) /* CD-ROM printed 07/97 */
	QUIZARD_BIOS_ROM

	DISK_REGION( "cdrom" )
	DISK_IMAGE_READONLY( "quizard4r40", 0, BAD_DUMP SHA1(288cc37a994e4f1cbd47aa8c92342879c6fc0b87) )

	ROM_REGION(0x1000, "mcu", 0) // Intel D8751H MCU
	ROM_LOAD( "de_142_d3.bin", 0x0000, 0x1000, CRC(77be0b40) SHA1(113b5c239480a2259f55e411ba8fb3972e6d4301) ) // German language
ROM_END

// only the CD was dumped, MCU not available
ROM_START( quizardff ) /* CD-ROM printed 01/96 */
	QUIZARD_BIOS_ROM

	DISK_REGION( "cdrom" )
	DISK_IMAGE_READONLY( "quizardff", 0, SHA1(ac533040379c1350066e778e3a86d1beb11c6f71) )

	ROM_REGION(0x1000, "mcu", 0) // Intel D8751H MCU
	ROM_LOAD( "8751.bin", 0x0000, 0x1000, NO_DUMP )
ROM_END


/*************************
*      Game driver(s)    *
*************************/

/*    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS      INIT        COMPANY       FULLNAME */
// BIOS / System
CONS( 1991, cdimono1, 0,      0,      cdimono1, cdi,      cdi_state, empty_init, "Philips",    "CD-i (Mono-I) (PAL)",   MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
CONS( 1991, cdimono2, 0,      0,      cdimono2, cdimono2, cdi_state, empty_init, "Philips",    "CD-i (Mono-II) (NTSC)",   MACHINE_NOT_WORKING )
CONS( 1991, cdi910,   0,      0,      cdi910,   cdimono2, cdi_state, empty_init, "Philips",    "CD-i 910-17P Mini-MMC (PAL)",   MACHINE_NOT_WORKING )
CONS( 1991, cdi490a,  0,      0,      cdimono1, cdi,      cdi_state, empty_init, "Philips",    "CD-i 490",   MACHINE_NOT_WORKING )
CONS( 1995, gpi1200,  0,      0,      cdimono1, cdi,      cdi_state, empty_init, "Goldstar",   "GPi 1200",   MACHINE_NOT_WORKING )

// The Quizard games are retail CD-i units in a cabinet, with an additional JAMMA adapter and dongle for protection, hence being clones of the system.
/*    YEAR  NAME         PARENT    MACHINE        INPUT     DEVICE          INIT         MONITOR     COMPANY         FULLNAME */
GAME( 1995, cdibios,     0,        cdimono1,      quizard,  cdi_state,     empty_init,  ROT0,     "Philips",  "CD-i (Mono-I) (PAL) BIOS", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IS_BIOS_ROOT )

GAME( 1995, quizard,     cdibios,  quizard,       quizard,  quizard_state, empty_init,  ROT0, "TAB Austria",  "Quizard (v1.8, German, i8751 DE 11 D3)", MACHINE_IMPERFECT_SOUND )
GAME( 1995, quizard_17,  quizard,  quizard,       quizard,  quizard_state, empty_init,  ROT0, "TAB Austria",  "Quizard (v1.7, German, i8751 DE 11 D3)", MACHINE_IMPERFECT_SOUND )
GAME( 1995, quizard_12,  quizard,  quizard,       quizard,  quizard_state, empty_init,  ROT0, "TAB Austria",  "Quizard (v1.2, German, i8751 DE 11 D3)", MACHINE_IMPERFECT_SOUND )
GAME( 1995, quizard_10,  quizard,  quizard,       quizard,  quizard_state, empty_init,  ROT0, "TAB Austria",  "Quizard (v1.0, German, i8751 DE 11 D3)", MACHINE_IMPERFECT_SOUND )
GAME( 1995, quizardi,    quizard,  quizard,       quizard,  quizard_state, empty_init,  ROT0, "TAB Austria",  "Quizard (v1.8, Italian, i8751 IT 11 I2)", MACHINE_IMPERFECT_SOUND )
GAME( 1995, quizardi_17, quizard,  quizard,       quizard,  quizard_state, empty_init,  ROT0, "TAB Austria",  "Quizard (v1.7, Italian, i8751 IT 11 I2)", MACHINE_IMPERFECT_SOUND )
GAME( 1995, quizardi_12, quizard,  quizard,       quizard,  quizard_state, empty_init,  ROT0, "TAB Austria",  "Quizard (v1.2, Italian, i8751 IT 11 I2)", MACHINE_IMPERFECT_SOUND )

GAME( 1995, quizard2,    cdibios,  quizard,       quizard,  quizard_state, empty_init,  ROT0, "TAB Austria",  "Quizard 2 (v2.3, German, i8751 DN 122 D3)", MACHINE_IMPERFECT_SOUND )
GAME( 1995, quizard2_22, quizard2, quizard,       quizard,  quizard_state, empty_init,  ROT0, "TAB Austria",  "Quizard 2 (v2.2, German, i8751 DN 122 D3)", MACHINE_IMPERFECT_SOUND )

// Quizard 3 and 4 will hang after starting a game (CDIC issues?)
GAME( 1995, quizard3,    cdibios,  quizard,       quizard,  quizard_state, empty_init,  ROT0, "TAB Austria",  "Quizard 3 (v3.4, German, i8751 DE 132 D3)", MACHINE_IMPERFECT_SOUND )
GAME( 1995, quizard3a,   quizard3, quizard,       quizard,  quizard_state, empty_init,  ROT0, "TAB Austria",  "Quizard 3 (v3.4, German, i8751 DE 132 A1)", MACHINE_IMPERFECT_SOUND )
GAME( 1996, quizard3_32, quizard3, quizard,       quizard,  quizard_state, empty_init,  ROT0, "TAB Austria",  "Quizard 3 (v3.2, German, i8751 DE 132 D3)", MACHINE_IMPERFECT_SOUND )

GAME( 1998, quizard4,    cdibios,  quizard,       quizard,  quizard_state, empty_init,  ROT0, "TAB Austria",  "Quizard 4 Rainbow (v4.2, German, i8751 DE 142 D3)", MACHINE_IMPERFECT_SOUND )
GAME( 1998, quizard4cz,  quizard4, quizard,       quizard,  quizard_state, empty_init,  ROT0, "TAB Austria",  "Quizard 4 Rainbow (v4.2, Czech, i8751 TS142 CZ1)", MACHINE_IMPERFECT_SOUND )
GAME( 1998, quizard4_41, quizard4, quizard,       quizard,  quizard_state, empty_init,  ROT0, "TAB Austria",  "Quizard 4 Rainbow (v4.1, German, i8751 DE 142 D3)", MACHINE_IMPERFECT_SOUND )
GAME( 1997, quizard4_40, quizard4, quizard,       quizard,  quizard_state, empty_init,  ROT0, "TAB Austria",  "Quizard 4 Rainbow (v4.0, German, i8751 DE 142 D3)", MACHINE_IMPERFECT_SOUND )

GAME( 1996, quizardff,   cdibios,  quizard,       quizard,  quizard_state, empty_init,  ROT0, "TAB Austria",  "Quizard Fun and Fascination (French Edition V1 - 01/96)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )



cdsys5.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for "CD System 5" jukebox by Sound Leisure Ltd.

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

#include "emu.h"
#include "cpu/mc68hc11/mc68hc11.h"
#include "machine/74259.h"
#include "machine/mc146818.h"
#include "machine/nvram.h"


namespace {

class cdsys5_state : public driver_device
{
public:
	cdsys5_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_shift_register(0)
	{
	}

	void minijook(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void shift_data_w(u8 data);
	void shift_latch_w(u8 data);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<mc68hc11_cpu_device> m_maincpu;

	u16 m_shift_register;
};


void cdsys5_state::machine_start()
{
	save_item(NAME(m_shift_register));
}

void cdsys5_state::shift_data_w(u8 data)
{
	m_shift_register = (m_shift_register << 1) | (data & 1);
}

void cdsys5_state::shift_latch_w(u8 data)
{
	//logerror("Latching $%04X from shift register\n", m_shift_register);
}

void cdsys5_state::mem_map(address_map &map)
{
	map(0x0401, 0x0401).nopw(); // watchdog?
	//map(0x0420, 0x0421).w("lcdc", FUNC(hd44780_device::write));
	map(0x0440, 0x0441).nopr();
	map(0x0802, 0x0803).nopr();
	map(0x0807, 0x0807).nopr();
	map(0x0810, 0x081f).w("outlatch1", FUNC(cd4099_device::write_a0));
	map(0x0820, 0x082f).w("outlatch2", FUNC(cd4099_device::write_a0));
	map(0x0833, 0x0833).nopr();
	map(0x0850, 0x085f).w("outlatch5", FUNC(cd4099_device::write_a0));
	map(0x0860, 0x086f).w("outlatch6", FUNC(cd4099_device::write_a0));
	map(0x0880, 0x088f).w("outlatch8", FUNC(cd4099_device::write_a0));
	map(0x08a0, 0x08a0).w(FUNC(cdsys5_state::shift_latch_w));
	map(0x08b0, 0x08b0).w(FUNC(cdsys5_state::shift_data_w));
	map(0x0c00, 0x0c3f).rw("rtc", FUNC(mc146818_device::read_direct), FUNC(mc146818_device::write_direct));
	map(0x4000, 0x47ff).ram().share("novram");
	map(0x8000, 0xffff).rom().region("program", 0);
}


static INPUT_PORTS_START(minijook)
	PORT_START("DIP1") // next to a TC4512BP 8-to-1 multiplexer
	PORT_DIPNAME(0x01, 0x01, DEF_STR(Unknown)) PORT_DIPLOCATION("DIL1:1")
	PORT_DIPSETTING(0x01, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPNAME(0x02, 0x02, DEF_STR(Unknown)) PORT_DIPLOCATION("DIL1:2")
	PORT_DIPSETTING(0x02, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPNAME(0x04, 0x04, DEF_STR(Unknown)) PORT_DIPLOCATION("DIL1:3")
	PORT_DIPSETTING(0x04, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPNAME(0x08, 0x08, DEF_STR(Unknown)) PORT_DIPLOCATION("DIL1:4")
	PORT_DIPSETTING(0x08, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPNAME(0x10, 0x10, DEF_STR(Unknown)) PORT_DIPLOCATION("DIL1:5")
	PORT_DIPSETTING(0x10, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPNAME(0x20, 0x20, DEF_STR(Unknown)) PORT_DIPLOCATION("DIL1:6")
	PORT_DIPSETTING(0x20, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPNAME(0x40, 0x40, DEF_STR(Unknown)) PORT_DIPLOCATION("DIL1:7")
	PORT_DIPSETTING(0x40, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPNAME(0x80, 0x80, DEF_STR(Unknown)) PORT_DIPLOCATION("DIL1:8")
	PORT_DIPSETTING(0x80, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))

	// DIL2 next to DIL1 with its own TC4512BP, both unpopulated
	// 3 more TC4512BP on board, likely for additional inputs
INPUT_PORTS_END


void cdsys5_state::minijook(machine_config &config)
{
	MC68HC11D0(config, m_maincpu, 8_MHz_XTAL); // 44-pin PLCC next to unpopulated 40-pin DIP "68HC11"
	m_maincpu->set_addrmap(AS_PROGRAM, &cdsys5_state::mem_map);
	// RS232 I/O through MAX232CPE

	NVRAM(config, "novram", nvram_device::DEFAULT_ALL_0); // Xicor X20C17P-55 Autostore NOVRAM

	MC146818(config, "rtc", 32.768_kHz_XTAL);

	CD4099(config, "outlatch1");
	CD4099(config, "outlatch2");
	CD4099(config, "outlatch5");
	CD4099(config, "outlatch6");
	CD4099(config, "outlatch8"); // 5 on board
}


ROM_START(minijook)
	ROM_REGION(0x8000, "program", 0)
	ROM_LOAD("mj-sl-s-v3.4.u10", 0x0000, 0x8000, CRC(7b8f03ce) SHA1(9d7fb9a9f5051ecadad5a33860117bc4dfd8ab30)) // 0xxxxxxxxxxxxxx = 0xFF
ROM_END

} // anonymous namespace


SYST(199?, minijook, 0, 0, minijook, minijook, cdsys5_state, empty_init, "Sound Leisure", "MiniJook", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_MECHANICAL | MACHINE_REQUIRES_ARTWORK)



celint2k.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders:Nigel Barnes

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

Skeleton driver for Olivetti Celint 2000 phone with Videotext terminal.
In Spain, Banco Santander distributed it as the Superfono Santander (with a custom ROM) as
part of a "bank at home" pilot program.

Main PCB:
    ____________________________________________________________________________________
   |                          |           _________________                            |
   |                          |          |  GoldStar      |  ___________  ___________  |
   |   POWER SUPPLY           |   BATT   |  GM76C256L-85  | |SN74HC245N| |_SN74HC74_|  |
   |                          |          |________________|               ___________  |
   |                          |           Xtal                           |M74HC00B1_|  |
   |__________________________|        4.4334 MHz         ____________                 |
   |                                                      |           |                |
   |                                     Xtal 32.768 kHz  | OKI M6255 |                |
   |                 ___________          ___________     |___________|                |
   |                |SN74HC08N_|         |_PCF8573P_|   _________________________      |
   |                 ___________                       | GoldStar               |      |
   |                |M74HC374B1|                       | GM76C8128ALL-85        |      |
   |                                                   |________________________|      |
   |                 _________________________          _________________________      |
   |                | GolsStar               |         |                        |      |
   | SMARTCARD      | GM76C88L-15            |         | EPROM                  |      |
   |  READER        |________________________|         |________________________|      |
   |                 ______________________________                                    |
   |                | Zilog                       |    Xtal           ___________      |
   |                | Z84C0006PEC Z80 CPU         |   11.0573 MHz    |SN74LS145N|      |
   |                |_____________________________|         ________________           |
   |                                     ___________       |               |           |
   |                                    |GAL16V8-20|       | Zilog         |           |
   |                                                       | Z84C9008VSC   |           |
   |                                                       | Z80 KIO       |           |
   |                     _____________________             |_______________|           |
   |                    |                    |                                         |
   |                    | 73K322L-IP         |  _____        ___________      :        |
   |                    |____________________| |TL7705ACP   |HCF4094BE_|      ·        |
   |                                            ___________                            |
   |      _______                              |HCF4066BE_|                            |
   |     |7805CT|             ___________       _____                                  |
 __|_                        |_TL084CN__|      MC34119P                                |
|    |                                                         _____________________   |
|DB25|                                                        |                    |   |
|RS232                                                        | MC34118P           |   |
|____|    ___________                                         |____________________|   |
   |     |MC145406P_|                                                                  |
 __|_                                  ___________                                     |
|    |                                |_GD4053B__|                                     |
|P/T |                                     _______                         ___________ |
|____|                                    |H11D1_|                        |__M3541B__| |
   |                                                                                   |
 __|_                                                  ___________                     |
|    |                                                |_KA8501A__|                     |
|LINE|                                                                                 |
|____|                                                                                 |
  _|_                                                                    SPEAKER       |
 | CD/MF switch                                                                        |
 |___|                                                           _______               |
   |                                           _____            |LS1240A               |
   |__________________________________________|    |___________________________________|
                                              |____|

Video screen is driven by 9 Hitachi HD61105A chips (separate PCB).

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

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/pcf8573.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"
#include "video/msm6255.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class celint2k_state : public driver_device
{
public:
	celint2k_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "msm6255")
		, m_pio(*this, "pio")
		, m_ctc(*this, "ctc")
		, m_sio(*this, "sio")
		, m_rtc(*this, "rtc")
		, m_ram(*this, "ram", 0x20000, ENDIANNESS_LITTLE)
		, m_bank_view(*this, "bank")
		, m_bank_ram(*this, "bank_ram")
		, m_bank_rom(*this, "bank_rom")
	{ }

	void celint2k(machine_config &config) ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;
	required_device<msm6255_device> m_lcdc;
	required_device<z80pio_device> m_pio;
	required_device<z80ctc_device> m_ctc;
	required_device<z80sio_device> m_sio;
	required_device<pcf8573_device> m_rtc;
	memory_share_creator<uint8_t> m_ram;
	memory_view m_bank_view;
	memory_bank_creator m_bank_ram;
	memory_bank_creator m_bank_rom;

	void lcdc_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	void celint2k_palette(palette_device &palette) const;

	[[maybe_unused]] void port_e0_w(uint8_t data);
	uint8_t pa_data_r();
	void pa_data_w(uint8_t data);
	uint8_t pb_data_r();
	void pb_data_w(uint8_t data);
	uint8_t pc_data_r();
	void pc_data_w(uint8_t data);
	void pc_ctrl_w(uint8_t data);
	void kio_cmd_w(uint8_t data);

	uint8_t m_pc_data_in;
	uint8_t m_pc_data_out;
	uint8_t m_pc_ctrl;
};

void celint2k_state::celint2k_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(131, 136, 139));
	palette.set_pen_color(1, rgb_t( 92,  83,  88));
}

void celint2k_state::lcdc_map(address_map &map)
{
	map.global_mask(0x7fff);
	map(0x0000, 0x7fff).ram().share("videoram");
}

void celint2k_state::mem_map(address_map &map)
{
	map(0x0000, 0x5fff).rom().region("maincpu", 0);
	map(0x6000, 0x7fff).ram();
	map(0x8000, 0xffff).view(m_bank_view);
	m_bank_view[0](0x8000, 0xffff).ram().share("videoram");
	m_bank_view[1](0x8000, 0xffff).bankrw(m_bank_ram);
	m_bank_view[2](0x8000, 0xffff).bankr(m_bank_rom);
}

void celint2k_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).rw(m_pio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x04, 0x07).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x08, 0x0b).rw(m_sio, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x0c, 0x0c).rw(FUNC(celint2k_state::pc_data_r), FUNC(celint2k_state::pc_data_w));
	map(0x0d, 0x0d).w(FUNC(celint2k_state::pc_ctrl_w));
	map(0x0e, 0x0e).w(FUNC(celint2k_state::kio_cmd_w));
	map(0x20, 0x21).m(m_lcdc, FUNC(msm6255_device::map));
	//map(0x40, 0x40).w()
	//map(0x80, 0x80).w()
	//map(0xa0, 0xa0).r()
	//map(0xe0, 0xe0).w(FUNC(celint2k_state::port_e0_w));
}

void celint2k_state::machine_start()
{
	m_bank_ram->configure_entries(0, 4, m_ram, 0x8000);
	m_bank_rom->configure_entries(0, 7, memregion("maincpu")->base() + 0x8000, 0x8000);
}

void celint2k_state::machine_reset()
{
	m_bank_ram->set_entry(0);
	m_bank_rom->set_entry(0);

	m_pc_data_in = 0x00;
	m_pc_data_out = 0x00;
	m_pc_ctrl = 0xff;

	m_bank_view.select(2);
	m_bank_rom->set_entry(0);
}

void celint2k_state::port_e0_w(uint8_t data)
{
	logerror("%s port_e0_w: %02x\n", machine().describe_context(), data);
}

uint8_t celint2k_state::pa_data_r()
{
	// input 0xff
	uint8_t data = 0x00;
	//logerror("%s pa_data_r: %02x\n", machine().describe_context(), data);
	return data;
}

void celint2k_state::pa_data_w(uint8_t data)
{
	// output 0x00
	//logerror("%s pa_data_w: %02x\n", machine().describe_context(), data);
}

uint8_t celint2k_state::pb_data_r()
{
	uint8_t data = 0x00;
	//logerror("%s pb_data_r: %02x\n", machine().describe_context(), data);
	data = m_rtc->sda_r() << 1;

	return data;
}

void celint2k_state::pb_data_w(uint8_t data)
{
	//logerror("%s pb_data_w: %02x\n", machine().describe_context(), data);

	m_rtc->sda_w(BIT(data, 3));
	m_rtc->scl_w(BIT(data, 2));
}

uint8_t celint2k_state::pc_data_r()
{
	uint8_t m_pc_data_in = 0x00;
	uint8_t data = (m_pc_data_in & m_pc_ctrl) | (m_pc_data_out & (m_pc_ctrl ^ 0xff));
	logerror("%s pc_data_r: %02x & %02x\n", machine().describe_context(), data, m_pc_ctrl);
	return data;
}

void celint2k_state::pc_data_w(uint8_t data)
{
	// output 0xff
	m_pc_data_out = data;
	logerror("%s pc_data_w: %02x | %02x\n", machine().describe_context(), m_pc_data_out, m_pc_ctrl);

	switch (data & 0x0f)
	{
	case 0x00:
		m_bank_view.select(0);
		break;
	case 0x02:
		m_bank_view.select(1);
		break;
	case 0x06:
		m_bank_view.select(2);
		//switch (data & 0x70)
		//{
		//case 0x70:
		//  m_bank_rom->set_entry(0);
		//  break;
		//default:
		//  m_bank_rom->set_entry((data >> 4) & 7);
		//  break;
		//}
		//if ((data >> 4) < 7)
		//  m_bank_rom->set_entry(data >> 4);
		break;
	default:
		logerror("%s pc_data_w: %02x | %02x unknown bank\n", machine().describe_context(), m_pc_data_out, m_pc_ctrl);
		break;
	}
}

void celint2k_state::pc_ctrl_w(uint8_t data)
{
	//logerror("%s pc_ctrl_w: %02x\n", machine().describe_context(), data);
	m_pc_ctrl = data;
}

void celint2k_state::kio_cmd_w(uint8_t data)
{
	logerror("%s kio_cmd_w: %02x\n", machine().describe_context(), data);

	if (BIT(data, 4))
		m_pio->reset();

	if (BIT(data, 5))
		m_ctc->reset();

	if (BIT(data, 6))
		m_sio->reset();
}

INPUT_PORTS_START( celint2k )
INPUT_PORTS_END

static const z80_daisy_config daisy_chain[] =
{
	{ "sio" },
	{ "ctc" },
	{ "pio" },
	{ nullptr }
};

void celint2k_state::celint2k(machine_config &config)
{
	Z80(config, m_maincpu, 11'057'300 / 2); // Z84C0006PEC, verified divisor
	m_maincpu->set_addrmap(AS_PROGRAM, &celint2k_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &celint2k_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	Z80PIO(config, m_pio, 11'057'300 / 2); // TODO: part of Zilog Z84C9008VSC Z80 KIO
	m_pio->out_pa_callback().set(FUNC(celint2k_state::pa_data_w));
	m_pio->in_pa_callback().set(FUNC(celint2k_state::pa_data_r));
	m_pio->out_pb_callback().set(FUNC(celint2k_state::pb_data_w));
	m_pio->in_pb_callback().set(FUNC(celint2k_state::pb_data_r));
	m_pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80CTC(config, m_ctc, 11'057'300 / 2); // TODO: part of Zilog Z84C9008VSC Z80 KIO
	m_ctc->set_clk<0>(2.4576_MHz_XTAL / 2);
	m_ctc->set_clk<1>(2.4576_MHz_XTAL / 2);
	m_ctc->zc_callback<0>().set(m_sio, FUNC(z80sio_device::rxca_w));
	m_ctc->zc_callback<0>().append(m_sio, FUNC(z80sio_device::txca_w));
	m_ctc->zc_callback<1>().set(m_sio, FUNC(z80sio_device::rxcb_w));
	m_ctc->zc_callback<1>().append(m_sio, FUNC(z80sio_device::txcb_w));
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80SIO(config, m_sio, 11'057'300 / 2); // TODO: part of Zilog Z84C9008VSC Z80 KIO
	m_sio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_sio->out_txda_callback().set("serial", FUNC(rs232_port_device::write_txd));
	m_sio->out_rtsa_callback().set("serial", FUNC(rs232_port_device::write_rts));
	m_sio->out_dtra_callback().set("serial", FUNC(rs232_port_device::write_dtr));
	m_sio->out_txdb_callback().set("host", FUNC(rs232_port_device::write_txd));

	rs232_port_device &serial(RS232_PORT(config, "serial", default_rs232_devices, nullptr));
	serial.rxd_handler().set(m_sio, FUNC(z80sio_device::rxa_w));
	serial.cts_handler().set(m_sio, FUNC(z80sio_device::ctsa_w));
	serial.dcd_handler().set(m_sio, FUNC(z80sio_device::dcda_w));

	rs232_port_device &host(RS232_PORT(config, "host", default_rs232_devices, nullptr));
	host.rxd_handler().set(m_sio, FUNC(z80sio_device::rxb_w));

	PCF8573(config, m_rtc, 32.768_kHz_XTAL);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD)); // TECDIS C425901 backlight 320x240 gLCD
	screen.set_refresh_hz(60); // Guess
	screen.set_screen_update(m_lcdc, FUNC(msm6255_device::screen_update));
	screen.set_size(480, 240);
	screen.set_visarea(0, 480-1, 0, 240-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(celint2k_state::celint2k_palette), 2);

	MSM6255(config, m_lcdc, 4'433'400 );
	m_lcdc->set_addrmap(0, &celint2k_state::lcdc_map);
	m_lcdc->set_screen("screen");

	SPEAKER(config, "mono").front_center();
}

ROM_START( celint2kss )
	ROM_REGION( 0x40000, "maincpu", 0 )
	ROM_LOAD( "sa21-b_eae5_27c020.ic19", 0x00000, 0x40000, CRC(0f5fd110) SHA1(9d1abc90db5eb5efbcde1da4b8a7ef6438723664) )

	ROM_REGION( 0x0117, "pld", 0 )
	ROM_LOAD( "gal16v8-20lnc.ic15", 0x0000, 0x0117, CRC(45724282) SHA1(09c1029af68ef6f8bd1d17d19dbce7a691f80171) )
ROM_END

} // anonymous namespace

COMP( 1995, celint2kss, 0, 0, celint2k, celint2k, celint2k_state, empty_init, "Olivetti", "Celint 2000 (Superfono Santander edition)", MACHINE_NOT_WORKING ) // Labeled as model "MULTIMEDIA - T"



ceres.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * Eidgenössische Technische Hochschule (ETH) Zürich Ceres-1
 *
 * Sources:
 *   - http://bitsavers.org/pdf/eth/ceres/Eberle_Hardware_Description_of_the_Workstation_Ceres_198701.pdf
 *
 * TODO:
 *   - startup/reset memory map
 *   - WD1002-05 Winchester/Floppy Disk Controller (WFC)
 *   - keyboard
 */

#include "emu.h"

// cpus and memory
#include "cpu/ns32000/ns32000.h"
#include "machine/ram.h"

// various hardware
#include "machine/ns32081.h"
#include "machine/ns32082.h"
#include "machine/mc68681.h"
#include "machine/z80scc.h"
#include "machine/m3002.h"
#include "machine/am9519.h"
#include "machine/wd_fdc.h"

// busses and connectors
#include "bus/rs232/rs232.h"
#include "imagedev/harddriv.h"
#include "imagedev/floppy.h"

// video
#include "screen.h"

#define VERBOSE 0
#include "logmacro.h"

namespace {

class ceres1_state : public driver_device
{
public:
	ceres1_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_fpu(*this, "fpu")
		, m_mmu(*this, "mmu")
		, m_ram(*this, "ram")
		, m_uart(*this, "uart")
		, m_serial(*this, "v24")
		, m_scc(*this, "scc")
		, m_rtc(*this, "rtc")
		, m_icu(*this, "icu")
		, m_fdc(*this, "fdc")
		, m_fdd(*this, "fdc:%u:fdd", 0U)
		, m_hdd(*this, "hdd%u", 0U)
		, m_screen(*this, "screen")
		, m_vram(*this, "vram")
	{
	}

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// address maps
	template <unsigned ST> void cpu_map(address_map &map) ATTR_COLD;

public:
	// machine config
	void ceres1(machine_config &config);

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect);

	void wfc_w(offs_t offset, u8 data);
	u8 wfc_r(offs_t offset);
	void wfc_command(u8 command);
	int get_lbasector(harddisk_image_device *hdf);

	DECLARE_INPUT_CHANGED_MEMBER(mouse_x);
	DECLARE_INPUT_CHANGED_MEMBER(mouse_y);

protected:
	required_device<ns32032_device> m_cpu;
	required_device<ns32081_device> m_fpu;
	required_device<ns32082_device> m_mmu;
	required_device<ram_device> m_ram;

	required_device<scn2681_device> m_uart;
	required_device<rs232_port_device> m_serial;
	required_device<z80scc_device> m_scc;
	required_device<m3002_device> m_rtc;
	required_device<am9519_device> m_icu;

	required_device<wd2797_device> m_fdc;
	optional_device_array<floppy_image_device, 4> m_fdd;
	required_device_array<harddisk_image_device, 3> m_hdd;

	required_device<screen_device> m_screen;
	required_shared_ptr<u32> m_vram;

	u8 m_dcr = 0;
	u16 m_mouse_x = 0;
	u16 m_mouse_y = 0;

	enum wfc_status : u8
	{
		WFC_S_ERR = 0x01, // error
		WFC_S_CD  = 0x04, // corrected data
		WFC_S_DRQ = 0x08, // data request
		WFC_S_SC  = 0x10, // seek complete
		WFC_S_WF  = 0x20, // write fault
		WFC_S_RDY = 0x40, // drive ready
		WFC_S_BSY = 0x80, // busy
	};
	u8 m_wfc_sram[2048]{};
	unsigned m_wfc_offset = 0;
	u8 m_wfc_error = 0;
	u8 m_wfc_precomp = 0;
	u8 m_wfc_count = 0;
	u8 m_wfc_sector = 0;
	u16 m_wfc_cylinder = 0;
	u8 m_wfc_sdh = 0;
	u8 m_wfc_status = 0;
};

void ceres1_state::machine_start()
{
	save_item(NAME(m_dcr));
}

void ceres1_state::machine_reset()
{
	m_dcr = 0;
	m_mouse_x = 0;
	m_mouse_y = 0;

	m_wfc_offset = 0;
	m_wfc_status = WFC_S_RDY;
}

void ceres1_state::wfc_w(offs_t offset, u8 data)
{
	switch (offset)
	{
	case 0:
		m_wfc_sram[m_wfc_offset++] = data;
		m_wfc_offset &= 0x3ff;
		break;
	case 1: m_wfc_precomp = data; break;
	case 2: m_wfc_count = data; break;
	case 3: m_wfc_sector = data; break;
	case 4: m_wfc_cylinder = (m_wfc_cylinder & 0xff00) | data; break;
	case 5: m_wfc_cylinder = u16(data) << 8 | (m_wfc_cylinder & 0xff); break;
	case 6: m_wfc_sdh = data; break;
	case 7: wfc_command(data); break;
	}
}

u8 ceres1_state::wfc_r(offs_t offset)
{
	u8 data = 0;

	switch (offset)
	{
	case 0: // data
		data = m_wfc_sram[m_wfc_offset++];
		m_wfc_offset &= 0x3ff;
		break;
	case 1: data = m_wfc_error; break;
	case 2: data = m_wfc_count; break;
	case 3: data = m_wfc_sector; break;
	case 4: data = u8(m_wfc_cylinder); break;
	case 5: data = m_wfc_cylinder >> 8; break;
	case 6: data = m_wfc_sdh; break;
	case 7: data = m_wfc_status; m_icu->ireq3_w(0); break;
	}

	return data;
}

void ceres1_state::wfc_command(u8 command)
{
	m_wfc_status |= WFC_S_BSY;
	m_wfc_status &= ~WFC_S_ERR;
	m_wfc_error = 0;

	if (((m_wfc_sdh >> 3) & 3) == 3)
		return;

	harddisk_image_device *hdf = m_hdd[(m_wfc_sdh >> 3) & 3];

	switch (command >> 4)
	{
	case 1:
		// restore
		break;
	case 2:
		LOG("read sector drive %d chs %d,%d,%d count %d\n",
			(m_wfc_sdh >> 3) & 3, m_wfc_cylinder & 0x3ff, (m_wfc_sdh >> 0) & 7, m_wfc_sector, m_wfc_count);
		if (hdf->exists())
			hdf->read(get_lbasector(hdf), m_wfc_sram);
		m_wfc_offset = 0;
		break;
	case 3:
		LOG("write sector drive %d chs %d,%d,%d count %d\n",
			(m_wfc_sdh >> 3) & 3, m_wfc_cylinder & 0x3ff, (m_wfc_sdh >> 0) & 7, m_wfc_sector, m_wfc_count);
		if (hdf->exists())
			hdf->write(get_lbasector(hdf), m_wfc_sram);
		m_wfc_offset = 0;
		break;
	case 4:
		// scan id
		break;
	case 5:
		// write format id
		break;
	case 7:
		// seek
		break;
	}

	m_wfc_status &= ~WFC_S_BSY;
	m_icu->ireq3_w(1);
}

int ceres1_state::get_lbasector(harddisk_image_device *hdf)
{
	const auto &info = hdf->get_info();

	int lbasector = m_wfc_cylinder & 0x3ff;
	lbasector *= info.heads;
	lbasector += (m_wfc_sdh >> 0) & 7;
	lbasector *= info.sectors;
	lbasector += m_wfc_sector;

	return lbasector;
}

template <unsigned ST> void ceres1_state::cpu_map(address_map &map)
{
	// FIXME: address lines 19-23 driven high until boot flipflop reset
	map(0x000000, 0x007fff).rom().region("eprom", 0);

	map(0xe00000, 0xe3ffff).ram().share("vram");
	map(0xf80000, 0xf87fff).rom().region("eprom", 0);

	map(0xfffa00, 0xfffa00).lw8(
		[this](u8 data)
		{
			m_dcr = data & 7;
		}, "dcr_w");
	map(0xfffc00, 0xfffc1f).rw(FUNC(ceres1_state::wfc_r), FUNC(ceres1_state::wfc_w)).umask32(0xff);
	map(0xfffc40, 0xfffc7f).lrw32(
		[this]() { m_cpu->set_input_line(INPUT_LINE_NMI, 0); return 0; }, "parity_clear",
		[this](u32 data) { m_cpu->set_input_line(INPUT_LINE_NMI, 0); }, "parity_clear");
	map(0xfffc80, 0xfffc80).rw(m_rtc, FUNC(m3002_device::read), FUNC(m3002_device::write));
	map(0xfffd00, 0xfffd01).lr16([this]() { return m_mouse_x; }, "mouse_x");
	map(0xfffd04, 0xfffd05).lr16([this]() { return m_mouse_y; }, "mouse_y");
	map(0xfffd08, 0xfffd0b).portr("mouse_buttons");
	map(0xfffd40, 0xfffd7f).rw(m_uart, FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask32(0xff);
	map(0xfffd80, 0xfffd8f).rw(m_scc, FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w)).umask32(0xff);
	map(0xfffdc0, 0xfffdc3).portr("SW1");
	map(0xfffdc0, 0xfffdc0).lw8(
		[this](u8 data)
		{
			m_cpu->space(0).install_ram(0, m_ram->mask(), m_ram->pointer());
		}, "bt_ff_w");
	if (ST == 4)
		map(0xfffe00, 0xfffe00).lr8([this]() { return m_icu->acknowledge(); }, "iack");
	map(0xfffe08, 0xfffe08).rw(m_icu, FUNC(am9519_device::data_r), FUNC(am9519_device::data_w));
	map(0xfffe0c, 0xfffe0c).rw(m_icu, FUNC(am9519_device::stat_r), FUNC(am9519_device::cmd_w));
}

INPUT_CHANGED_MEMBER(ceres1_state::mouse_x)
{
	// compute x delta
	int delta = newval - oldval;
	if (delta > 0x80)
		delta -= 0x100;
	else if (delta < -0x80)
		delta += 0x100;

	m_mouse_x = std::clamp(m_mouse_x + delta, 0, 1023);
}

INPUT_CHANGED_MEMBER(ceres1_state::mouse_y)
{
	// compute y delta
	int delta = newval - oldval;
	if (delta > 0x80)
		delta -= 0x100;
	else if (delta < -0x80)
		delta += 0x100;

	m_mouse_y = std::clamp(m_mouse_y - delta, 0, 799);
}

static INPUT_PORTS_START(ceres1)
	PORT_START("mouse_x")
	PORT_BIT(0xff, 0x00, IPT_MOUSE_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ceres1_state::mouse_x), 0)

	PORT_START("mouse_y")
	PORT_BIT(0xff, 0x00, IPT_MOUSE_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ceres1_state::mouse_y), 0)

	PORT_START("mouse_buttons")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("Mouse Left Button")   PORT_CODE(MOUSECODE_BUTTON1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("Mouse Middle Button") PORT_CODE(MOUSECODE_BUTTON3)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON3) PORT_NAME("Mouse Right Button")  PORT_CODE(MOUSECODE_BUTTON2)

	PORT_START("SW1")
	PORT_DIPNAME(0x80, 0x00, "Diagnostic") PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPSETTING(0x80, DEF_STR(Off))

	PORT_DIPNAME(0x40, 0x40, "FPU") PORT_DIPLOCATION("SW1:2")
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPSETTING(0x40, DEF_STR(Off))

	PORT_DIPNAME(0x20, 0x00, "MMU") PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPSETTING(0x20, DEF_STR(Off))

	PORT_DIPUNUSED_DIPLOC(0x10, 0x00, "SW1:4")

	// TODO: configuration is guesswork
	PORT_DIPNAME(0x0f, 0x08, "Memory Size") PORT_DIPLOCATION("SW1:5,6,7,8")
	PORT_DIPSETTING(0x01, "2M")
	PORT_DIPSETTING(0x03, "4M")
	PORT_DIPSETTING(0x07, "6M")
	PORT_DIPSETTING(0x0f, "8M")
INPUT_PORTS_END

void ceres1_state::ceres1(machine_config &config)
{
	NS32032(config, m_cpu, 10_MHz_XTAL);
	m_cpu->set_addrmap(0, &ceres1_state::cpu_map<0>);
	m_cpu->set_addrmap(4, &ceres1_state::cpu_map<4>);

	NS32081(config, m_fpu, 10_MHz_XTAL);
	m_cpu->set_fpu(m_fpu);

	NS32082(config, m_mmu, 10_MHz_XTAL);
	m_cpu->set_mmu(m_mmu);

	RAM(config, m_ram);
	m_ram->set_default_size("4MiB");
	m_ram->set_default_value(0);
	m_ram->set_extra_options("2MiB,6MiB,8MiB");

	// TODO: port A txd/rxd connect to keyboard
	SCN2681(config, m_uart, 3.6864_MHz_XTAL);
	m_uart->irq_cb().set(m_icu, FUNC(am9519_device::ireq2_w)).invert();
	m_uart->b_tx_cb().set(m_serial, FUNC(rs232_port_device::write_txd)).invert();
	m_uart->outport_cb().set(m_serial, FUNC(rs232_port_device::write_dtr)).bit(0).invert();
	m_uart->outport_cb().set(m_serial, FUNC(rs232_port_device::write_rts)).bit(1).invert();
	m_uart->outport_cb().set(m_icu, FUNC(am9519_device::ireq0_w)).bit(3);
	m_uart->outport_cb().set(m_icu, FUNC(am9519_device::ireq4_w)).bit(4);

	RS232_PORT(config, m_serial, default_rs232_devices, nullptr);
	m_serial->rxd_handler().set(m_uart, FUNC(scn2681_device::rx_b_w));
	m_serial->cts_handler().set(m_uart, FUNC(scn2681_device::ip1_w));
	m_serial->dcd_handler().set(m_uart, FUNC(scn2681_device::ip0_w));
	m_serial->dsr_handler().set(m_uart, FUNC(scn2681_device::ip2_w));

	// TODO: RS-485 ports "na" and "nb"
	SCC8530(config, m_scc, 6_MHz_XTAL);
	m_scc->configure_channels(m_uart->clock(), 0, m_uart->clock(), 0);
	m_scc->out_int_callback().set(m_icu, FUNC(am9519_device::ireq1_w)).invert();

	M3002(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->irq_out().set(m_icu, FUNC(am9519_device::ireq5_w));

	AM9519(config, m_icu);
	m_icu->out_int_callback().set_inputline(m_cpu, INPUT_LINE_IRQ0);

	// TODO: WD1002-05 Winchester/Floppy Disk Controller (WFC)
	HARDDISK(config, m_hdd[0], 0);
	HARDDISK(config, m_hdd[1], 0);
	HARDDISK(config, m_hdd[2], 0);

	WD2797(config, m_fdc, 20_MHz_XTAL / 20);
	FLOPPY_CONNECTOR(config, "fdc:0", "fdd", FLOPPY_35_DD, true, floppy_image_device::default_mfm_floppy_formats);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(70'000'000, 1344, 0, 1024, 838, 0, 800);
	m_screen->set_screen_update(FUNC(ceres1_state::screen_update));
}

u32 ceres1_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect)
{
	if (!BIT(m_dcr, 0))
	{
		offs_t offset = BIT(m_dcr, 1) ? 0x20000 : 0;
		u32 const invert = BIT(m_dcr, 2) ? 0xffffffffU : 0;

		for (int y = screen.visible_area().min_y; y <= screen.visible_area().max_y; y++)
		{
			for (int x = screen.visible_area().min_x; x <= screen.visible_area().max_x; x += 32)
			{
				u32 const data = m_vram[offset++] ^ invert;

				for (unsigned i = 0; i < 32; i++)
					bitmap.pix(y, x + i) = BIT(data, i) ? rgb_t::white() : rgb_t::black();
			}
		}
	}

	return 0;
}

ROM_START(ceres1)
	ROM_REGION32_LE(0x8000, "eprom", 0)
	ROM_SYSTEM_BIOS(0, "300689", "30.6.89")
	ROMX_LOAD("by0_u44__30_6_89.u44", 0x0000, 0x2000, CRC(8e1559fe) SHA1(2e11c144fd698b9576816f5cfb003c46ab9ce43b), ROM_BIOS(0) | ROM_SKIP(3))
	ROMX_LOAD("by1_u43__30_6_89.u43", 0x0001, 0x2000, CRC(18625b0c) SHA1(3319043507b7eeb488ecd839708032f968e6e53e), ROM_BIOS(0) | ROM_SKIP(3))
	ROMX_LOAD("by2_u42__30_6_89.u42", 0x0002, 0x2000, CRC(a56497c5) SHA1(85858bea9fd27031ee0191b1d508c7469a384f10), ROM_BIOS(0) | ROM_SKIP(3))
	ROMX_LOAD("by3_u41__30_6_89.u41", 0x0003, 0x2000, CRC(6b628912) SHA1(f76ea089e8bf82f572f27def344b5174e577ebbb), ROM_BIOS(0) | ROM_SKIP(3))

	ROM_SYSTEM_BIOS(1, "231087", "CBUG-Ceres32 V231087 Release 3.2")
	ROMX_LOAD("cbug32__by0__23_10_87.u44", 0x0000, 0x2000, CRC(177bab38) SHA1(ce8c9edbcd142b638a4e906b1d88b6770ac41702), ROM_BIOS(1) | ROM_SKIP(3))
	ROMX_LOAD("cbug32__by1__23_10_87.u43", 0x0001, 0x2000, CRC(4ed51850) SHA1(66b2ee07c88bfa3adac1d75eab463e7f280220a3), ROM_BIOS(1) | ROM_SKIP(3))
	ROMX_LOAD("cbug32__by2__23_10_87.u42", 0x0002, 0x2000, CRC(a05cdf91) SHA1(509b97bfb9e4631a3c6e9af27458d1099903a0ff), ROM_BIOS(1) | ROM_SKIP(3))
	ROMX_LOAD("cbug32__by3__23_10_87.u41", 0x0003, 0x2000, CRC(7e3fd512) SHA1(77a9590490f8fd2212b79cd6c4f81b8f46d6b486), ROM_BIOS(1) | ROM_SKIP(3))
ROM_END

}

/*   YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY                                        FULLNAME   FLAGS */
COMP(1986, ceres1, 0,      0,      ceres1,  ceres1, ceres1_state, empty_init, "Eidgenössische Technische Hochschule Zürich", "Ceres-1", MACHINE_NOT_WORKING)



cexpert.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Novag Constellation Expert (model 853)

The u3 ROM contains the following message (it's David Kittinger's company):
Copyright (c) 1985, Intelligent Heuristic Programming, Inc

Hardware notes:
- PCB label: 100045
- R65C02P4 @ 5MHz (10MHz XTAL)
- 2*2KB RAM(NEC D449C-3), 2*32KB ROM
- 64+8 leds, magnet sensors chessboard
- ports for optional printer and chess clock

I/O is again similar to supercon

The 1st version had a R65C02P3 @ 4MHz (8MHz XTAL). IRQ and beeper via the XTAL
instead of 555 timer, but nearly the same frequency. The 4MHz version was also
seen on a modified Super Constellation PCB, with 4 TMM27128 and identical ROM
contents as the newer version.

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

#include "emu.h"

#include "cpu/m6502/r65c02.h"
#include "machine/clock.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/beep.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "novag_cexpert.lh"


namespace {

class cexpert_state : public driver_device
{
public:
	cexpert_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_board(*this, "board"),
		m_beeper(*this, "beeper"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void cexpert(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(change_cpu_freq);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<sensorboard_device> m_board;
	required_device<beep_device> m_beeper;
	required_ioport_array<8> m_inputs;

	u8 m_inp_mux = 0;

	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void mux_w(u8 data);
	void control_w(u8 data);
	u8 input1_r();
	u8 input2_r();
};

void cexpert_state::machine_start()
{
	save_item(NAME(m_inp_mux));
}

INPUT_CHANGED_MEMBER(cexpert_state::change_cpu_freq)
{
	// old version had a 4MHz CPU
	m_maincpu->set_unscaled_clock((newval & 1) ? (10_MHz_XTAL/2) : (8_MHz_XTAL/2));
}



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

void cexpert_state::mux_w(u8 data)
{
	// d0-d7: input mux, led data
	m_inp_mux = data;
	m_display->write_mx(data);
}

void cexpert_state::control_w(u8 data)
{
	// d0-d2: clock?

	// d3: enable beeper
	m_beeper->set_state(BIT(data, 3));

	// d4-d7: 74145 to led select
	u8 sel = data >> 4 & 0xf;
	m_display->write_my(1 << sel);
}

u8 cexpert_state::input1_r()
{
	u8 data = 0;

	// d0-d7: multiplexed inputs (chessboard squares)
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_rank(i ^ 7, true);

	return ~data;
}

u8 cexpert_state::input2_r()
{
	u8 data = 0;

	// d6,d7: multiplexed inputs (side panel)
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read() << 6;

	// d0-d2: printer
	// other: ?
	return ~data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void cexpert_state::main_map(address_map &map)
{
	map(0x0000, 0x0fff).ram().share("nvram");
	map(0x1000, 0x1000).nopw(); // printer
	map(0x1100, 0x1100).nopw(); // printer
	map(0x1200, 0x1200).rw(FUNC(cexpert_state::input2_r), FUNC(cexpert_state::mux_w));
	map(0x1300, 0x1300).rw(FUNC(cexpert_state::input1_r), FUNC(cexpert_state::control_w));
	map(0x1800, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( cexpert )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("New Game")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Player/Player / Gambit Book / King")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Verify / Set Up")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Best Move/Random / Training Level / Queen")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Change Color")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Sound / Depth Search / Bishop")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Clear Board / Tournament Book")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Solve Mate / Infinite / Knight")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Print Moves / Print Evaluations")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Print Board / Interface / Rook")

	PORT_START("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Trace Forward / Auto Play")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Print List / Acc. Time / Pawn")

	PORT_START("IN.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Hint / Next Best")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Set Level")

	PORT_START("IN.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Go")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Take Back / Restore")

	PORT_START("CPU")
	PORT_CONFNAME( 0x01, 0x01, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cexpert_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "4MHz" )
	PORT_CONFSETTING(    0x01, "5MHz" )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void cexpert_state::cexpert(machine_config &config)
{
	// basic machine hardware
	R65C02(config, m_maincpu, 10_MHz_XTAL/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &cexpert_state::main_map);

	auto &irq_clock(CLOCK(config, "irq_clock", 15440 / 32)); // 555 timer (measured), to 4020
	irq_clock.set_pulse_width(attotime::from_nsec(15200)); // active for 15.2us
	irq_clock.signal_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(200));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	config.set_default_layout(layout_novag_cexpert);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 15440 / 16); // 965Hz
	m_beeper->add_route(ALL_OUTPUTS, "mono", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( cexpert )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("novag_8533-503.u3", 0x0000, 0x8000, CRC(e384de2f) SHA1(13b56e2870e3755073e7a7cc63bae995cd468562) )
	ROM_LOAD("novag_8532-502.u2", 0x8000, 0x8000, CRC(c2c367b5) SHA1(e000871c93a5e0fc2f8b29f3d2fec0607a91559b) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1985, cexpert, 0,      0,      cexpert, cexpert, cexpert_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Constellation Expert", MACHINE_SUPPORTS_SAVE )



cforte.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Novag Constellation Forte

Hardware notes:
- R65C02P4 @ 5MHz (10MHz XTAL)
- 2*2KB RAM(NEC D449C-3), 2*32KB ROM(27C256)
- HLCD0538P, 10-digit 7seg LCD display
- TTL, 18 LEDs, 8*8 chessboard buttons
- ports for optional printer and chess clock

I/O is similar to supercon

TODO:
- add power-off NMI? does nothing, it will just go into an infinite loop

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

#include "emu.h"

#include "cpu/m6502/r65c02.h"
#include "machine/clock.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/beep.h"
#include "video/hlcd0538.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "novag_cforte.lh"


namespace {

class cforte_state : public driver_device
{
public:
	cforte_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_board(*this, "board"),
		m_lcd(*this, "hlcd0538"),
		m_beeper(*this, "beeper"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// machine configs
	void cforte(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<sensorboard_device> m_board;
	required_device<hlcd0538_device> m_lcd;
	required_device<beep_device> m_beeper;
	required_ioport_array<8> m_inputs;

	u8 m_inp_mux = 0;
	u8 m_led_select = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_display();
	void lcd_output_w(u64 data);
	void mux_w(u8 data);
	void control_w(u8 data);
	u8 input1_r();
	u8 input2_r();
};

void cforte_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_led_select));
}



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

// LCD

void cforte_state::lcd_output_w(u64 data)
{
	// 4 rows used
	u32 rowdata[4];
	for (int i = 0; i < 4; i++)
		rowdata[i] = (data >> i & 1) ? u32(data >> 8) : 0;

	// 2 segments per row
	for (int dig = 0; dig < 13; dig++)
	{
		data = 0;
		for (int i = 0; i < 4; i++)
			data |= ((rowdata[i] >> (2*dig) & 3) << (2*i));

		data = bitswap<8>(data,7,2,0,4,6,5,3,1);
		m_display->write_row(dig+3, data);
	}
}


// misc

void cforte_state::update_display()
{
	// 3 led rows
	m_display->matrix_partial(0, 3, m_led_select, m_inp_mux);
}

void cforte_state::mux_w(u8 data)
{
	// d0-d7: input mux, led data
	m_inp_mux = data;
	update_display();
}

void cforte_state::control_w(u8 data)
{
	// d0: HLCD0538 data in
	// d1: HLCD0538 clk
	// d2: HLCD0538 lcd
	m_lcd->data_w(BIT(data, 0));
	m_lcd->clk_w(BIT(data, 1));
	m_lcd->lcd_w(BIT(data, 2));

	// d3: ? (goes high at power-off NMI)

	// d4-d6: select led row
	m_led_select = data >> 4 & 7;
	update_display();

	// d7: enable beeper
	m_beeper->set_state(BIT(data, 7));
}

u8 cforte_state::input1_r()
{
	u8 data = 0;

	// d0-d7: multiplexed inputs (chessboard squares)
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_rank(i ^ 7, true);

	return ~data;
}

u8 cforte_state::input2_r()
{
	u8 data = 0;

	// d6,d7: multiplexed inputs (side panel)
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read() << 6;

	// d0-d2: printer
	// other: ?
	return ~data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void cforte_state::main_map(address_map &map)
{
	map(0x0000, 0x0fff).ram().share("nvram");
	map(0x1c00, 0x1c00).nopw(); // printer
	map(0x1d00, 0x1d00).nopw(); // printer
	map(0x1e00, 0x1e00).rw(FUNC(cforte_state::input2_r), FUNC(cforte_state::mux_w));
	map(0x1f00, 0x1f00).rw(FUNC(cforte_state::input1_r), FUNC(cforte_state::control_w));
	map(0x2000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( cforte )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("New Game")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Player/Player / Gambit/Large / King")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Verify/Set Up / Pro-Op")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Random/Tour/Normal / Training Level / Queen")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Change Color / Time Control / Priority")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Sound / Depth Search / Bishop")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Flip Display / Clear Board / Clear Book")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Solve Mate / Infinite / Knight")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Print Moves / Print Evaluations / Print Book")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Print Board / Interface / Rook")

	PORT_START("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Trace Forward / Auto Play / No/End")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Print List / Acc. Time / Pawn")

	PORT_START("IN.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Hint / Next Best / Yes/Start")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Set Level")

	PORT_START("IN.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Go / Right")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Take Back / Restore / Left")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void cforte_state::cforte(machine_config &config)
{
	// basic machine hardware
	R65C02(config, m_maincpu, 10_MHz_XTAL/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &cforte_state::main_map);

	auto &irq_clock(CLOCK(config, "irq_clock", 32.768_kHz_XTAL/128)); // 256Hz
	irq_clock.set_pulse_width(attotime::from_usec(11)); // active for 11us
	irq_clock.signal_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(200));
	m_board->set_nvram_enable(true);

	// video hardware
	HLCD0538(config, m_lcd).write_cols().set(FUNC(cforte_state::lcd_output_w));
	PWM_DISPLAY(config, m_display).set_size(3+13, 8);
	m_display->set_segmask(0x3ff0, 0xff);
	config.set_default_layout(layout_novag_cforte);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 32.768_kHz_XTAL/32); // 1024Hz
	m_beeper->add_route(ALL_OUTPUTS, "mono", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( cfortea )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("a_903_l", 0x0000, 0x8000, CRC(01e7e306) SHA1(6a2b982bb0f412a63f5d3603958dd863e38669d9) ) // NEC D27C256AD-12
	ROM_LOAD("a_903_h", 0x8000, 0x8000, CRC(c5a5573f) SHA1(7e11eb2f3d96bc41386a14a19635427a386ec1ec) ) // "
ROM_END

ROM_START( cforteb )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("forte_b_l.bin", 0x0000, 0x8000, CRC(e3d194a1) SHA1(80457580d7c57e07895fd14bfdaf14b30952afca) )
	ROM_LOAD("forte_b_h.bin", 0x8000, 0x8000, CRC(dd824be8) SHA1(cd8666b6b525887f9fc48a730b71ceabcf07f3b9) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1986, cfortea, 0,       0,      cforte,  cforte, cforte_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Constellation Forte (version A)", MACHINE_SUPPORTS_SAVE )
SYST( 1986, cforteb, cfortea, 0,      cforte,  cforte, cforte_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Constellation Forte (version B)", MACHINE_SUPPORTS_SAVE )



cfx9850.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/***************************************************************************

    Driver for Casio CFX-9850

To operate:
The unit is switched off by default, you have to switch it on by pressing 'Q'.

Currently (year2011) it is on by default, the only key that works is '\'
which turns it off. After that nothing happens.

Normal operation:
- AC/ON - turns the unit on
- SHIFT + OFF - turns the unit off

The unit automatically powers off after no operation for about 6 minutes (intro/quick start, page viii).

Debugging information:
1. g 10b3 (Initialise system)
2. cs=23
3. ip= one of these: ip=d1a4,d1af,d1ba,d1c5,d1d0,d1db,d1ea,d1f9,d208,d217,
   d226,d235,d244
4. g 23108c to get a test pattern on the screen.

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

#include "emu.h"
#include "cpu/hcd62121/hcd62121.h"
#include "emupal.h"
#include "screen.h"


namespace {

class cfx9850_state : public driver_device
{
public:
	cfx9850_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_video_ram(*this, "video_ram")
		, m_display_ram(*this, "display_ram")
		, m_ko_port(*this, "KO%u", 1)
		, m_ko(0)
		, m_port(0)
		, m_opt(0)
	{ }

	void cfx9850(machine_config &config);

protected:
	required_device<hcd62121_cpu_device> m_maincpu;

private:
	required_shared_ptr<u8> m_video_ram;
	required_shared_ptr<u8> m_display_ram;
	required_ioport_array<12> m_ko_port;

	u16 m_ko;   // KO lines KO1 - KO14
	u8 m_port;  // PORT lines PORT0 - PORT7 (serial I/O)
	u8 m_opt;   // OPT lines OPT0 - OPT7 (contrast)

	void kol_w(u8 data);
	void koh_w(u8 data);
	void port_w(u8 data);
	void opt_w(u8 data);
	u8 ki_r();
	u8 in0_r();
	void cfx9850_palette(palette_device &palette) const;
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void cfx9850_mem(address_map &map) ATTR_COLD;
};


void cfx9850_state::cfx9850_mem(address_map &map)
{
	map(0x000000, 0x007fff).mirror(0x008000).rom();
	map(0x080000, 0x0807ff).ram().share("video_ram");
//  map(0x100000, 0x10ffff) // some memory mapped i/o???
//  map(0x110000, 0x11ffff) // some memory mapped i/o???
	map(0x200000, 0x27ffff).rom().region("bios", 0);
	map(0x400000, 0x407fff).mirror(0x008000).ram();
	map(0x600000, 0x6007ff).mirror(0xf800).ram().share("display_ram");
//  map(0xe10000, 0xe1ffff) // some memory mapped i/o???
}


void cfx9850_state::kol_w(u8 data)
{
	m_ko = (m_ko & 0xff00) | data;
	logerror("KO is now %04x\n", m_ko);
}


void cfx9850_state::koh_w(u8 data)
{
	m_ko = (m_ko & 0x00ff) | (data << 8);
	logerror("KO is now %04x\n", m_ko);
}


// 7------- PORT7 - RX enable?
// -6------ PORT6 - NC / CP45
// --5----- PORT5 - TX enable?
// ---4---- PORT4 - data out (see code at 2225A0)
// ----3--- PORT3 - NC / CP28
// -----2-- PORT2 - NC / CP29
// ------1- PORT1 - NC / CP30
// -------0 PORT0 - display (enable?) related? + CP31
void cfx9850_state::port_w(u8 data)
{
	m_port = data;
	logerror("PORT is now %02x\n", m_port);
}


// 7------- OPT7 - NC / CP46
// -6------ OPT6 - NC / CP25
// --5----- OPT5 - NC / CP26
// ---4---- OPT4 - NC / CP27
// ----3--- OPT3 - contrast (TC74HC4066AFS pin 12)
// -----2-- OPT2 - contrast (TC74HC4066AFS pin 6)
// ------1- OPT1 - contrast (TC74HC4066AFS pin 5)
// -------0 OPT0 - contrast (TC74HC4066AFS pin 13)
void cfx9850_state::opt_w(u8 data)
{
	m_opt = data;
	logerror("OPT is now %02x\n", m_opt);
}


u8 cfx9850_state::ki_r()
{
	u8 data = 0;

	for (int i = 0; i < 12; i++)
	{
		if (BIT(m_ko, i))
		{
			data |= m_ko_port[i]->read();
		}
	}

	return data;
}


u8 cfx9850_state::in0_r()
{
	// battery level?
	// bit4 -> if reset CPU keeps restarting (several unknown instructions before jumping to 0)
	//         perhaps a battery present check?
	// bit 5 -> 0 = low battery

	// --XX ---- VDET
	// ---- -X-- data-in
	return 0x30 & ~ 0x00;
}


static INPUT_PORTS_START(cfx9850)
	PORT_START("KO1")
	/* KI1 */ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("AC On/Off") PORT_CODE(KEYCODE_BACKSLASH)
			  PORT_BIT(0xfe, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("KO2")
	/* KI3 */ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("EXE Enter") PORT_CODE(KEYCODE_ENTER)
	/* KI4 */ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(-) Ans") PORT_CODE(KEYCODE_MINUS)
	/* KI5 */ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("EXP Pi") PORT_CODE(KEYCODE_EQUALS)
	/* KI6 */ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(". SPACE") PORT_CODE(KEYCODE_SPACE)
	/* KI7 */ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0   Z") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x83, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("KO3")
	/* KI3 */ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("- ] Y") PORT_CODE(KEYCODE_Y)
	/* KI4 */ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+ [ X") PORT_CODE(KEYCODE_X)
	/* KI5 */ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3   W") PORT_CODE(KEYCODE_W)
	/* KI6 */ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2   V") PORT_CODE(KEYCODE_V)
	/* KI7 */ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1   U") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x83, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("KO4")
	/* KI3 */ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/ } T") PORT_CODE(KEYCODE_T)
	/* KI4 */ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("* { S") PORT_CODE(KEYCODE_S)
	/* KI5 */ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6   R") PORT_CODE(KEYCODE_R)
	/* KI6 */ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5   Q") PORT_CODE(KEYCODE_Q)
	/* KI7 */ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4   P") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x83, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("KO5")
	/* KI4 */ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DEL INS") PORT_CODE(KEYCODE_BACKSPACE)
	/* KI5 */ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9   O") PORT_CODE(KEYCODE_O)
	/* KI6 */ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8   N") PORT_CODE(KEYCODE_N)
	/* KI7 */ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7   M") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x87, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("KO6")
	/* KI2 */ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("-> L") PORT_CODE(KEYCODE_L)
	/* KI3 */ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(",   K") PORT_CODE(KEYCODE_K)
	/* KI4 */ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(") x^-1 J") PORT_CODE(KEYCODE_J)
	/* KI5 */ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("( x^(1/3) I") PORT_CODE(KEYCODE_I)
	/* KI6 */ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F<=>D H") PORT_CODE(KEYCODE_H)
	/* KI7 */ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("a b/c G") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x81, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("KO7")
	/* KI2 */ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("tan tan^-1 F") PORT_CODE(KEYCODE_F)
	/* KI3 */ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("cos cos^-1 E") PORT_CODE(KEYCODE_E)
	/* KI4 */ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("sin sin^-1 D") PORT_CODE(KEYCODE_D)
	/* KI5 */ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ln e^x C") PORT_CODE(KEYCODE_C)
	/* KI6 */ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("log 10^x B") PORT_CODE(KEYCODE_B)
	/* KI7 */ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("x,theta,t A") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x81, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("KO8")
	/* KI2 */ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
	/* KI3 */ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)
	/* KI4 */ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("EXIT QUIT") PORT_CODE(KEYCODE_STOP)
	/* KI5 */ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("^ root theta") PORT_CODE(KEYCODE_COMMA)
	/* KI6 */ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("x^2 sqrt ro") PORT_CODE(KEYCODE_SLASH)
	/* KI7 */ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ALPHA ALPHA-LOCK") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x81, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("KO9")
	/* KI2 */ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
	/* KI3 */ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
	/* KI4 */ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MENU SET UP") PORT_CODE(KEYCODE_OPENBRACE)
	/* KI5 */ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("VARS PRGM") PORT_CODE(KEYCODE_CLOSEBRACE)
	/* KI6 */ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("OPTN") PORT_CODE(KEYCODE_LCONTROL)
	/* KI7 */ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x81, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("KO10")
	/* KI2 */ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F6 G<=>T") PORT_CODE(KEYCODE_F6)
	/* KI3 */ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F5 G-Solv") PORT_CODE(KEYCODE_F5)
	/* KI4 */ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F4 Sketch") PORT_CODE(KEYCODE_F4)
	/* KI5 */ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F3 V-Window") PORT_CODE(KEYCODE_F3)
	/* KI6 */ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F2 Zoom") PORT_CODE(KEYCODE_F2)
	/* KI7 */ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F1 Trace") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x81, IP_ACTIVE_HIGH, IPT_UNUSED)

	// KO11 is not connected
	PORT_START("KO11")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("KO12")
	/* KI4 */ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TEST") PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0xf7, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END


void cfx9850_state::cfx9850_palette(palette_device &palette) const
{
	// Referenced from screenshots in "fx-9750G PLUS User's Guide"
	palette.set_pen_color(0, 0xbb, 0xdd, 0xaa);
	palette.set_pen_color(1, 0x22, 0x55, 0xaa);
	palette.set_pen_color(2, 0x22, 0xaa, 0x55);
	palette.set_pen_color(3, 0xff, 0x55, 0x22);
}


u32 cfx9850_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 offset = 0;

	for (int i = 0; i < 16; i++)
	{
		int x = 120 - i * 8;

		for (int j = 0; j < 64; j++)
		{
			u8 data1 = m_display_ram[offset];
			u8 data2 = m_display_ram[offset + 0x400];

			for (int b = 0; b < 8; b++)
			{
				bitmap.pix(63-j, x+b) = ( data1 & 0x80 ) ? ( data2 & 0x80 ? 3 : 2 ) : ( data2 & 0x80 ? 1 : 0 );
				data1 <<= 1;
				data2 <<= 1;
			}

			offset++;
		}
	}

	return 0;
}


void cfx9850_state::cfx9850(machine_config &config)
{
	HCD62121(config, m_maincpu, 4300000); /* X1 - 4.3 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &cfx9850_state::cfx9850_mem);
	m_maincpu->kol_cb().set(FUNC(cfx9850_state::kol_w));
	m_maincpu->koh_cb().set(FUNC(cfx9850_state::koh_w));
	m_maincpu->port_cb().set(FUNC(cfx9850_state::port_w));
	m_maincpu->opt_cb().set(FUNC(cfx9850_state::opt_w));
	m_maincpu->ki_cb().set(FUNC(cfx9850_state::ki_r));
	m_maincpu->in0_cb().set(FUNC(cfx9850_state::in0_r));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_size(128, 64);
	screen.set_visarea(0, 127, 0, 63);
	screen.set_screen_update(FUNC(cfx9850_state::screen_update));
	screen.set_palette("palette");

	// TODO: Verify amount of colors and palette. Colors can be changed by changing the contrast.
	PALETTE(config, "palette", FUNC(cfx9850_state::cfx9850_palette), 4);
}


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

	void cfx9850gb(machine_config &config);
	void cfx9850gb_mem(address_map &map) ATTR_COLD;
};

void cfx9850gb_state::cfx9850gb_mem(address_map &map)
{
	map(0x000000, 0x007fff).mirror(0x008000).rom();
	map(0x080000, 0x0807ff).ram().share("video_ram");
//  map(0x100000, 0x10ffff) // some memory mapped i/o???
//  map(0x110000, 0x11ffff) // some memory mapped i/o???
	map(0x200000, 0x2fffff).rom().region("bios", 0);
	map(0x400000, 0x40f7ff).ram();
	map(0x40f800, 0x40ffff).ram().share("display_ram");
	map(0x410000, 0x41ffff).ram();
//  map(0xe10000, 0xe1ffff) // some memory mapped i/o???
}

void cfx9850gb_state::cfx9850gb(machine_config &config)
{
	cfx9850(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &cfx9850gb_state::cfx9850gb_mem);
}


#define ROM_MAINCPU \
	ROM_REGION(0x8000, "maincpu", 0) \
	ROM_LOAD("hcd62121.bin", 0x0000, 0x8000, CRC(e72075f8) SHA1(f50d176e1c225dab69abfc67702c9dfb296b6a78))

ROM_START(cfx9850)
	ROM_MAINCPU

	ROM_REGION(0x80000, "bios", 0)
	// Unknown yet which rom is which version.
	ROM_SYSTEM_BIOS(0, "rom1", "rom1, version unknown")
	ROMX_LOAD("cfx9850.bin", 0x00000, 0x80000, CRC(6c9bd903) SHA1(d5b6677ab4e0d3f84e5769e89e8f3d101f98f848), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "rom2", "rom2, version unknown")
	ROMX_LOAD("cfx9850b.bin", 0x00000, 0x80000, CRC(cd3c497f) SHA1(1d1aa38205eec7aba3ed6bef7389767e38afe075), ROM_BIOS(1))
ROM_END

ROM_START(cfx9850gb)
	ROM_MAINCPU

	ROM_REGION(0x100000, "bios", 0)
	// White model
	// Back case revision: G359-21
	// PCB revision: PWB-GY355-E4 RJA509401-1 / PWB-GY357-1 RJA509402-1
	ROM_LOAD("r27v802d-34.lsi2", 0x00000, 0x100000, CRC(7ad44c51) SHA1(7cde6074758b5ae474b4eb3ee7396dbfb481ddcf))
ROM_END

} // anonymous namespace


COMP(1996, cfx9850,   0,       0, cfx9850,   cfx9850, cfx9850_state,   empty_init, "Casio", "CFX-9850G",       MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP(1996, cfx9850gb, cfx9850, 0, cfx9850gb, cfx9850, cfx9850gb_state, empty_init, "Casio", "CFX-9850GB Plus", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



cgc7900.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/***************************************************************************

        Chromatics CGC 7900

        05/04/2010 Skeleton driver.

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

/*

    TODO:

    . does not boot
    . interrupts
    . vsync interrupt
    . map ROM to 000000-000007 at boot
    - NVRAM (= battery backed SRAM) at 0xE40xxx
    - floppy
    - bitmap display
    - Z mode read/write
    - color status write
    - bitmap roll
    - overlay roll
    . keyboard
    - joystick
    - light pen
    - memory parity
    - sync
    - disk DMA
    - PIO DMA
    - serial port controller
    - HVG
    - OMTI Series 10 SCSI controller (8" Winchester HD)

    docs:

    bitsavers://pdf/chromatics/
    http://z80cpu.eu/mirrors/oldcomputers.dyndns.org/public/pub/manuals/omti10a.pdf

    how to:

    C-u @ -- enter Terminal
    C-u F -- use serial port from Terminal (null_modem has to be set up 9600, 7E1)
    C-u M -- enter Monitor
    C-u T -- enter Thaw (setup utility)
    C-u d -- boot DOS
    C-u Q 1 -- display clock

    See User's Manual appendix A for more control sequences.
*/

#include "emu.h"
#include "cgc7900.h"
#include "speaker.h"

#define VERBOSE 1
#include "logmacro.h"



/***************************************************************************
    PARAMETERS
***************************************************************************/

#define INT_RTC             0x0001
#define INT_RS449_TXRDY     0x0002
#define INT_BINT2           0x0004
#define INT_RS232_TXRDY     0x0008
#define INT_DISK            0x0010
#define INT_BINT3           0x0020
#define INT_BEZEL           0x0040
#define INT_KEYBOARD        0x0080
#define INT_RS449_RXRDY     0x0100
#define INT_LIGHT_PEN       0x0200
#define INT_BINT4           0x0400
#define INT_JOYSTICK        0x0800
#define INT_VSYNC           0x1000
#define INT_BINT5           0x2000
#define INT_BINT1           0x4000
#define INT_RS232_RXRDY     0x8000

/***************************************************************************
    READ/WRITE HANDLERS
***************************************************************************/

void cgc7900_state::kbd_put(u8 data)
{
	kbd_ready = true;
	kbd_data = data;
	irq_encoder(7, ASSERT_LINE);
}

/*-------------------------------------------------
    keyboard_r - keyboard data read
-------------------------------------------------*/

u16 cgc7900_state::keyboard_r()
{
	u16 data;

	/*

	    bit     description

	     0      key data bit 0
	     1      key data bit 1
	     2      key data bit 2
	     3      key data bit 3
	     4      key data bit 4
	     5      key data bit 5
	     6      key data bit 6
	     7      key data bit 7
	     8      SHIFT key
	     9      CTRL key
	    10      M1 key
	    11      M2 key
	    12
	    13
	    14
	    15

	*/

	if (kbd_ready)
	{
		data = kbd_data | kbd_mods;
		kbd_ready = false;
		irq_encoder(7, CLEAR_LINE);
	}
	else
	{
		data = kbd_mods;
	}

	LOG("kbd == %04x\n", data);

	if (kbd_mods)
		kbd_mods = 0;

	return data;
}

/*-------------------------------------------------
    keyboard_w - keyboard data write
-------------------------------------------------*/

void cgc7900_state::keyboard_w(u16 data)
{
	/*

	    bit     description

	     0      LED select bit 0
	     1      LED select bit 1
	     2      LED select bit 2
	     3      LED select bit 3
	     4      LED select bit 4
	     5
	     6
	     7      LED switch (1=on, 0=off)
	     8
	     9
	    10
	    11
	    12
	    13
	    14
	    15

	*/

	LOG("kbd <- %04x\n", data);
}

/*-------------------------------------------------
    interrupt_mask_w - interrupt mask write
-------------------------------------------------*/

static const int int_levels[16] = { 5, 4, 5, 4, 4, 5, 4, 5, 5, 4, 5, 4, 4, 5, 4, 5 };
static const int int_vectors[16] = {
	0x4b, 0x44, 0x4c, 0x43, 0x42, 0x4d, 0x45, 0x4a, 0x49, 0x46, 0x4e, 0x41, 0x40, 0x4f, 0x47, 0x48
};

void cgc7900_state::interrupt_mask_w(u16 data)
{
	/*

	    bit     description     vec level

	     0      real time clock 4b  5
	     1      RS-449 Tx ready 44  4
	     2      BINT 2          4c  5
	     3      RS-232 Tx ready 43  4
	     4      disk            42  4
	     5      BINT 3          4d  5
	     6      bezel keys      45  4
	     7      keyboard        4a  5
	     8      RS-449 Rx ready 49  5
	     9      light pen       46  4
	    10      BINT 4          4e  5
	    11      joystick        41  4
	    12      vert. retrace   40  4
	    13      BINT 5          4f  5
	    14      BINT 1          47  4
	    15      RS-232 Rx ready 48  5

	    default mask is 0x7e3f -- RS232 Rx, RS449 Rx, keyboard, bezel.
	*/

	if (m_int_mask != data)
	{
		u16 changed = m_int_mask ^ data;

		LOG("i_mask: changed %04X -> %04X\n", m_int_mask, data);

		for (int i = 0; i < 16; i++)
		{
			if (BIT(changed, i) && BIT(data, i))
			{
				LOG("i_mask: pin %d disabled, clearing irq\n", i);
				irq_encoder(i, CLEAR_LINE);
			}
			if (BIT(changed, i) && !BIT(data, i))
			{
				LOG("i_mask: pin %d enabled, %s irq\n", i, BIT(m_int_active, i) ? "setting" : "clearing");
				irq_encoder(i, BIT(m_int_active, i));
			}
		}
	}

	m_int_mask = data;
}

void cgc7900_state::cpu_space_map(address_map &map)
{
	map(0xfffff2, 0xffffff).lr16(NAME([] (offs_t offset) -> u16 { return int_vectors[offset + 1]; }));
}

void cgc7900_state::irq_encoder(int pin, int state)
{
	if (state == ASSERT_LINE)
		m_int_active |= (1 << pin);
	else
		m_int_active &= ~(1 << pin);

	if (!BIT(m_int_mask, pin))
	{
		m_maincpu->set_input_line(int_levels[pin], state);
	}
}

/*-------------------------------------------------
    disk_data_r - disk data read
-------------------------------------------------*/

u16 cgc7900_state::disk_data_r()
{
	return 0;
}

/*-------------------------------------------------
    disk_data_w - disk data write
-------------------------------------------------*/

void cgc7900_state::disk_data_w(u16 data)
{
}

/*-------------------------------------------------
    disk_status_r - disk status read
-------------------------------------------------*/

u16 cgc7900_state::disk_status_r()
{
	/*

	    bit     signal      description

	     0      _I/O        input/output
	     1      _REQ        request
	     2      _BSY        busy
	     3      C/_D        control/data
	     4      _MSG        message
	     5      _RDY        ready
	     6
	     7
	     8
	     9
	    10
	    11
	    12
	    13
	    14
	    15

	*/

	return 0xffff - 0x04;
}

/*-------------------------------------------------
    disk_command_w - disk command write
-------------------------------------------------*/

void cgc7900_state::disk_command_w(u16 data)
{
}

u16 cgc7900_state::unmapped_r()
{
	return machine().rand();
}

/***************************************************************************
    MEMORY MAPS
***************************************************************************/

/*-------------------------------------------------
    ADDRESS_MAP( cgc7900_mem )
-------------------------------------------------*/

void cgc7900_state::cgc7900_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x1fffff).ram().share("chrom_ram");
	map(0x800000, 0x80ffff).rom().region(M68000_TAG, 0);
	map(0x810000, 0x9fffff).r(FUNC(cgc7900_state::unmapped_r));
	map(0xa00000, 0xbfffff).rw(FUNC(cgc7900_state::z_mode_r), FUNC(cgc7900_state::z_mode_w));
	map(0xc00000, 0xdfffff).ram().share("plane_ram");
	map(0xe00000, 0xe1ffff).w(FUNC(cgc7900_state::color_status_w));
//  map(0xe20000, 0xe23fff) Raster Processor
	map(0xe30000, 0xe303ff).ram().share("clut_ram");
	map(0xe38000, 0xe3bfff).ram().share("overlay_ram");
	map(0xe40000, 0xe40001).ram().share("roll_bitmap");
	map(0xe40002, 0xe40003).ram().share("pan_x");
	map(0xe40004, 0xe40005).ram().share("pan_y");
	map(0xe40006, 0xe40007).ram().share("zoom");
	map(0xe40008, 0xe40009).ram();
	map(0xe4000a, 0xe4000f).ram(); // Raster Processor
	map(0xe40010, 0xe40011).ram().share("blink_select");
	map(0xe40012, 0xe40013).ram().share("plane_select");
	map(0xe40014, 0xe40015).ram().share("plane_switch");
	map(0xe40016, 0xe40017).ram().share("color_status_fg");
	map(0xe40018, 0xe40019).ram().share("color_status_bg");
	map(0xe4001a, 0xe4001b).ram().share("roll_overlay");
	map(0xe4001c, 0xe40fff).ram();
//  map(0xefc440, 0xefc441) HVG Load X
//  map(0xefc442, 0xefc443) HVG Load Y
//  map(0xefc444, 0xefc445) HVG Load dX
//  map(0xefc446, 0xefc447) HVG Load dY
//  map(0xefc448, 0xefc449) HVG Load Pixel Color
//  map(0xefc44a, 0xefc44b) HVG Load Trip
	map(0xff8000, 0xff8003).rw(m_i8251_0, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0xff8040, 0xff8043).rw(m_i8251_1, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0xff8080, 0xff8081).rw(FUNC(cgc7900_state::keyboard_r), FUNC(cgc7900_state::keyboard_w));
//  map(0xff80c6, 0xff80c7) Joystick X axis
//  map(0xff80ca, 0xff80cb) Joystick Y axis
//  map(0xff80cc, 0xff80cd) Joystick Z axis
	map(0xff8100, 0xff8101).rw(FUNC(cgc7900_state::disk_data_r), FUNC(cgc7900_state::disk_data_w));
	map(0xff8120, 0xff8121).rw(FUNC(cgc7900_state::disk_status_r), FUNC(cgc7900_state::disk_command_w));
	map(0xff8140, 0xff8141).portr("BEZEL");
	map(0xff8180, 0xff8180).w(K1135A_TAG, FUNC(com8116_device::stt_str_w));
	map(0xff81c0, 0xff81ff).rw(MM58167_TAG, FUNC(mm58167_device::read), FUNC(mm58167_device::write)).umask16(0x00ff);
	map(0xff8200, 0xff8201).w(FUNC(cgc7900_state::interrupt_mask_w));
//  map(0xff8240, 0xff8241) Light Pen enable
//  map(0xff8242, 0xff8243) Light Pen X value
//  map(0xff8244, 0xff8245) Light Pen Y value
//  map(0xff8246, 0xff8247) Buffer memory parity check
//  map(0xff8248, 0xff8249) Buffer memory parity set/reset
	map(0xff824a, 0xff824b).r(FUNC(cgc7900_state::sync_r));
	map(0xff83c0, 0xff83c0).w(AY8910_TAG, FUNC(ay8910_device::address_w));
	map(0xff83c2, 0xff83c2).r(AY8910_TAG, FUNC(ay8910_device::data_r));
	map(0xff83c4, 0xff83c4).w(AY8910_TAG, FUNC(ay8910_device::data_w));
	// DDMA option board
//  map(0xff8500, 0xff8501) Disk DMA Command Register
//  map(0xff8502, 0xff8503) Disk DMA Address Register
//  map(0xff8507, 0xff8507) Disk DMA Control/Status Register
}

/*-------------------------------------------------
    ADDRESS_MAP( keyboard_mem )
-------------------------------------------------*/

void cgc7900_state::keyboard_mem(address_map &map)
{
	map(0x000, 0x7ff).rom();
}

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

/*-------------------------------------------------
    INPUT_PORTS( cgc7900 )
-------------------------------------------------*/

static INPUT_PORTS_START( cgc7900 )
	PORT_START("BEZEL")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Bezel 7")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Bezel 8")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Bezel 5")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Bezel 6")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Bezel 3")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Bezel 4")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Bezel 1")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Bezel 2")

	PORT_START("JOYSTICK_X")
	PORT_BIT( 0x3ff, 0x200, IPT_AD_STICK_X ) PORT_MINMAX(0x000, 0x3ff) PORT_SENSITIVITY(100) PORT_KEYDELTA(50)

	PORT_START("JOYSTICK_Y")
	PORT_BIT( 0x3ff, 0x200, IPT_AD_STICK_Y ) PORT_MINMAX(0x000, 0x3ff) PORT_SENSITIVITY(100) PORT_KEYDELTA(50)

	PORT_START("JOYSTICK_Z")
	PORT_BIT( 0x3ff, 0x200, IPT_AD_STICK_Z ) PORT_MINMAX(0x000, 0x3ff) PORT_SENSITIVITY(100) PORT_KEYDELTA(50)

	PORT_START("LIGHT_PEN_X")
	PORT_BIT( 0x1ff, 0x100, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_MINMAX(0, 511) PORT_SENSITIVITY(20) PORT_KEYDELTA(25)

	PORT_START("LIGHT_PEN_Y")
	PORT_BIT( 0x1ff, 0x0c0, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_MINMAX(0, 383) PORT_SENSITIVITY(20) PORT_KEYDELTA(25)
INPUT_PORTS_END


/***************************************************************************
    MACHINE INITIALIZATION
***************************************************************************/

/*-------------------------------------------------
    MACHINE_START( cgc7900 )
-------------------------------------------------*/

void cgc7900_state::machine_start()
{
	/* register for state saving */
	save_pointer(NAME(m_overlay_ram.target()), 0x4000);
}

void cgc7900_state::machine_reset()
{
	u8 *user1 = memregion(M68000_TAG)->base();

	memcpy((u8 *)m_chrom_ram.target(), user1, 8); // not really what happens but...

	kbd_mods = 0x300; // forces cold boot -- initializes SRAM contents
	kbd_data = 0;
	kbd_ready = false;

	m_i8251_0->write_cts(0);

	m_int_active = 0;
}

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

/*-------------------------------------------------
    MACHINE_DRIVER( cgc7900 )
-------------------------------------------------*/

void cgc7900_state::cgc7900(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, XTAL(28'480'000)/4);
	m_maincpu->set_addrmap(AS_PROGRAM, &cgc7900_state::cgc7900_mem);
	m_maincpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &cgc7900_state::cpu_space_map);

	i8035_device &kbmcu(I8035(config, I8035_TAG, 1000000));
	kbmcu.set_addrmap(AS_PROGRAM, &cgc7900_state::keyboard_mem);
	kbmcu.set_disable();

//  am2910_device &am2910(AM2910(config, AM2910_TAG, XTAL(17'360'000)));
//  am2910.set_addrmap(AS_PROGRAM, &cgc7900_state::omti10_mem);


	/* video hardware */
	cgc7900_video(config);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	AY8910(config, AY8910_TAG, XTAL(28'480'000)/16).add_route(ALL_OUTPUTS, "mono", 0.25);

	/* devices */
	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(cgc7900_state::kbd_put));

	mm58167_device &rtc(MM58167(config, MM58167_TAG, XTAL(32'768)));
	rtc.irq().set(FUNC(cgc7900_state::irq<0x0>));

	com8116_device &k1135a(COM8116(config, K1135A_TAG, XTAL(5'068'800)));
	k1135a.fr_handler().set(m_i8251_0, FUNC(i8251_device::write_txc));
	k1135a.fr_handler().append(m_i8251_0, FUNC(i8251_device::write_rxc));
	k1135a.ft_handler().set(m_i8251_1, FUNC(i8251_device::write_txc));
	k1135a.ft_handler().append(m_i8251_1, FUNC(i8251_device::write_rxc));

	I8251(config, m_i8251_0, 0);
	m_i8251_0->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_i8251_0->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	m_i8251_0->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_i8251_0->rxrdy_handler().set(FUNC(cgc7900_state::irq<0xf>));
	m_i8251_0->txrdy_handler().set(FUNC(cgc7900_state::irq<0x3>));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "null_modem"));
	rs232.rxd_handler().set(m_i8251_0, FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set(m_i8251_0, FUNC(i8251_device::write_dsr));

	I8251(config, m_i8251_1, 0);
	m_i8251_1->txd_handler().set("rs449", FUNC(rs232_port_device::write_txd));
	m_i8251_1->dtr_handler().set("rs449", FUNC(rs232_port_device::write_dtr));
	m_i8251_1->rts_handler().set("rs449", FUNC(rs232_port_device::write_rts));
	m_i8251_1->rxrdy_handler().set(FUNC(cgc7900_state::irq<0x8>));
	m_i8251_1->txrdy_handler().set(FUNC(cgc7900_state::irq<0x1>));

	rs232_port_device &rs449(RS232_PORT(config, "rs449", default_rs232_devices, nullptr));
	rs449.rxd_handler().set(m_i8251_1, FUNC(i8251_device::write_rxd));
	rs449.dsr_handler().set(m_i8251_1, FUNC(i8251_device::write_dsr));
}

/***************************************************************************
    ROMS
***************************************************************************/

ROM_START( cgc7900 )
	ROM_REGION16_BE( 0x10000, M68000_TAG, 0 )
	ROM_LOAD16_BYTE( "210274 800k even a3ee term 1.4.ue24",                     0x0000, 0x1000, CRC(5fa8f368) SHA1(120dbcfedce0badd38bf5b23e1fbc99667eb286c) )
	ROM_LOAD16_BYTE( "210275 800k odd bbb3 term 1.4.uf24",                      0x0001, 0x1000, CRC(4d479457) SHA1(5fa96a1eadfd9ba493d28691286e2e001a489a19) )
	ROM_LOAD16_BYTE( "210276 802k even 0c22 term 1.4.ue22",                     0x2000, 0x1000, CRC(c88c44ec) SHA1(f39d8a3cf7aaefd815b4426348965b076c1f2265) )
	ROM_LOAD16_BYTE( "210277 802k odd b58c term 1.4.uf22",                      0x2001, 0x1000, CRC(52ffe01f) SHA1(683aa71c2eb17b7ad639b888487db73d7684901a) )
	ROM_LOAD16_BYTE( "210278 804k even eaf4 term 1.4.ue21",                     0x4000, 0x1000, CRC(7fe326db) SHA1(d39d05e008160fb8afa62e0d4cfb1d813f2296d4) )
	ROM_LOAD16_BYTE( "210279 804k odd 3f6d term 1.4.uf21",                      0x4001, 0x1000, CRC(6c12d81c) SHA1(efe7c20e567c02b9c5b66a2d18e035d5f5f56b28) )
	ROM_LOAD16_BYTE( "210280 806k even 679d term 1.4.ue20",                     0x6000, 0x1000, CRC(70930d74) SHA1(a5ab1c0bd8bd829408961107e01598cd71a97fec) )
	ROM_LOAD16_BYTE( "210281 1.4 806k odd sum 611e.uf20",                       0x6001, 0x1000, CRC(8080aa2a) SHA1(c018a23e6f2158e2d63723cade0a3ad737090921) )
	ROM_LOAD16_BYTE( "su7700 210282 808k even opmod term 1.4 sum 2550.ue18",    0x8000, 0x1000, CRC(8f5834cd) SHA1(3cd03c82aa85c30cbc8e954f5a9fc4e9034f913b) )
	ROM_LOAD16_BYTE( "su7700 210283 808k odd opmod term 1.4 sum faca.uf18",     0x8001, 0x1000, CRC(e593b133) SHA1(6c641df844706e0d990b5fd544e98594171080a1) )
	ROM_LOAD16_BYTE( "210258 80ak even b2f6 mon 1.3.ue15",                      0xa000, 0x1000, CRC(ec5a1250) SHA1(ffef73d35f172ac610b35bdf729d51eb6f9346ba) )
	ROM_LOAD16_BYTE( "210259 80ak odd cd66 mon 1.3.uf15",                       0xa001, 0x1000, CRC(61eb43c6) SHA1(baaae0b787798147da453aac1f815589ea4ed921) )
	ROM_LOAD16_BYTE( "210244 80c even ce40 dos 1.6b.ue13",                      0xc000, 0x1000, CRC(3b64e4cb) SHA1(71b28d160b101ea6165e602ff1c54272b7b30ece) )
	ROM_LOAD16_BYTE( "210245 80c odd 1ac3 dos 1.6b.uf13",                       0xc001, 0x1000, CRC(0b6539ca) SHA1(d49e6d3307e5d634a412fd80b59492f31e29f7e0) )
	ROM_LOAD16_BYTE( "210290 idris even rel3 sum 0cce.ue12",                    0xe000, 0x1000, CRC(07065772) SHA1(620ea5d55021e5c38efc010722ddbd852cd49e39) )
	ROM_LOAD16_BYTE( "210291 idris odd rel3 sum 5d11.uf12",                     0xe001, 0x1000, CRC(d81b30da) SHA1(228f9b4e39d430ce4aaa81ea63f4516a51e6d001) )

	ROM_REGION( 0x800, "i8035", 0 )
	ROM_LOAD( "keyboard controller i8035", 0x0000, 0x0800, NO_DUMP )

	ROM_REGION( 0x800, "gfx1", 0 )
	ROM_LOAD( "norm chrset 4b40.ua13",  0x0000, 0x0400, CRC(55eb7b87) SHA1(768cea80597e7396d9e26f8cd09e6b480a526fce) )
	ROM_LOAD( "alt 46a7.ua14",          0x0400, 0x0400, CRC(be22b7e4) SHA1(83ef252c7fab33b4d3821a3049b89d044df35de8) )

	ROM_REGION( 0x20, "proms", 0 )
	ROM_LOAD( "rp0a.ub8",  0x0000, 0x0020, NO_DUMP )
	ROM_LOAD( "rp1a.ub7",  0x0000, 0x0020, NO_DUMP )
	ROM_LOAD( "rp2a.ub9",  0x0000, 0x0020, NO_DUMP )
	ROM_LOAD( "ha-5.ub1",  0x0000, 0x0020, NO_DUMP )
	ROM_LOAD( "03c0.ua16", 0x0000, 0x0020, NO_DUMP )
	ROM_LOAD( "03c0.ua11", 0x0000, 0x0020, NO_DUMP )

	/* OMTI Series 10 SCSI controller */
	ROM_REGION( 0x1400, AM2910_TAG, 0 )
	ROM_LOAD( "35 1.7b", 0x0000, 0x0400, NO_DUMP ) /* 82S137N */
	ROM_LOAD( "35 2.7b", 0x0400, 0x0400, NO_DUMP ) /* 82S137N */
	ROM_LOAD( "35 3.7b", 0x0800, 0x0400, NO_DUMP ) /* 82S137N */
	ROM_LOAD( "35 4.7b", 0x0c00, 0x0400, NO_DUMP ) /* 82S137N */
	ROM_LOAD( "35 5.7b", 0x1000, 0x0400, NO_DUMP ) /* 82S137N */
ROM_END

/***************************************************************************
    SYSTEM DRIVERS
***************************************************************************/

/*    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY       FULLNAME    FLAGS */
COMP( 1980, cgc7900, 0,      0,      cgc7900, cgc7900, cgc7900_state, empty_init, "Chromatics", "CGC 7900", MACHINE_NOT_WORKING)



cgenie.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Dirk Best
/***************************************************************************

    EACA Colour Genie EG2000

    TODO:
    - System is too fast, there should be one wait cycle every five cycles?
    - Adjust visible area so that the borders aren't that large (needs
      MC6845 changes)
    - Verify BASIC and character set versions

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

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/ram.h"
#include "video/mc6845.h"
#include "sound/ay8910.h"
#include "imagedev/cassette.h"
#include "formats/cgen_cas.h"
#include "bus/rs232/rs232.h"
#include "bus/cgenie/expansion/expansion.h"
#include "bus/cgenie/parallel/parallel.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

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

class cgenie_state : public driver_device
{
public:
	cgenie_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_cassette(*this, "cassette"),
		m_ram(*this, RAM_TAG),
		m_crtc(*this, "crtc"),
		m_rs232(*this, "rs232"),
		m_exp(*this, "exp"),
		m_char_rom(*this, "gfx1"),
		m_color_ram(*this, "colorram"),
		m_font_ram(*this, "fontram"),
		m_keyboard(*this, "KEY.%u", 0),
		m_palette(nullptr),
		m_control(0xff),
		m_rs232_rx(1),
		m_rs232_dcd(1)
	{ }

	void init_cgenie_eu();
	void init_cgenie_nz();

	MC6845_BEGIN_UPDATE(crtc_begin_update);
	MC6845_UPDATE_ROW(crtc_update_row);

	// 4-bit color ram
	uint8_t colorram_r(offs_t offset);
	void colorram_w(offs_t offset, uint8_t data);

	// control port
	void control_w(uint8_t data);
	uint8_t control_r();

	uint8_t keyboard_r(offs_t offset);
	DECLARE_INPUT_CHANGED_MEMBER(rst_callback);

	void rs232_rx_w(int state);
	void rs232_dcd_w(int state);

	void cgenie(machine_config &config);
	void cgenie_io(address_map &map) ATTR_COLD;
	void cgenie_mem(address_map &map) ATTR_COLD;
protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<cassette_image_device> m_cassette;
	required_device<ram_device> m_ram;
	required_device<hd6845s_device> m_crtc;
	required_device<rs232_port_device> m_rs232;
	required_device<cg_exp_slot_device> m_exp;
	required_memory_region m_char_rom;
	required_shared_ptr<uint8_t> m_color_ram;
	required_shared_ptr<uint8_t> m_font_ram;
	required_ioport_array<8> m_keyboard;

	static const rgb_t m_palette_bg[];
	static const rgb_t m_palette_eu[];
	static const rgb_t m_palette_nz[];

	const rgb_t *m_palette;
	rgb_t m_background_color;

	uint8_t m_control;

	int m_rs232_rx;
	int m_rs232_dcd;
};


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

void cgenie_state::cgenie_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).rom();
//  map(0x4000, 0xbfff).ram(); // set up in machine_start()
	map(0xc000, 0xefff).noprw(); // cartridge space
	map(0xf000, 0xf3ff).rw(FUNC(cgenie_state::colorram_r), FUNC(cgenie_state::colorram_w)).share("colorram");
	map(0xf400, 0xf7ff).ram().share("fontram");
	map(0xf800, 0xf8ff).mirror(0x300).r(FUNC(cgenie_state::keyboard_r));
	map(0xfc00, 0xffff).noprw(); // cartridge space
}

void cgenie_state::cgenie_io(address_map &map)
{
	map.global_mask(0xff);
	map(0xf8, 0xf8).w("ay8910", FUNC(ay8910_device::address_w));
	map(0xf9, 0xf9).rw("ay8910", FUNC(ay8910_device::data_r), FUNC(ay8910_device::data_w));
	map(0xfa, 0xfa).w(m_crtc, FUNC(hd6845s_device::address_w));
	map(0xfb, 0xfb).rw(m_crtc, FUNC(hd6845s_device::register_r), FUNC(hd6845s_device::register_w));
	map(0xff, 0xff).rw(FUNC(cgenie_state::control_r), FUNC(cgenie_state::control_w));
}


//**************************************************************************
//  INPUTS
//**************************************************************************

static INPUT_PORTS_START( cgenie )
	PORT_START("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)     PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)             PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)             PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)             PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)             PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)             PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)             PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)             PORT_CHAR('G') PORT_CHAR('g')

	PORT_START("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)             PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)             PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)             PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)             PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)             PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)             PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)             PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)             PORT_CHAR('O') PORT_CHAR('o')

	PORT_START("KEY.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)             PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)             PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)             PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)             PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)             PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)             PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)             PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)             PORT_CHAR('W') PORT_CHAR('w')

	PORT_START("KEY.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)             PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)             PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)             PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)   // produces [ and { when pressed, not on keyboard
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)            PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)            PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)            PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)            PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_CHAR(UCHAR_MAMEKEY(F8))

	PORT_START("KEY.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)             PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)             PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)             PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)             PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)             PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)             PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)             PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)             PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("KEY.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)             PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)             PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)         PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)         PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)         PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)         PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)          PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)         PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("KEY.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)         PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Clear") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)            PORT_CHAR(UCHAR_MAMEKEY(UP)) // prints [ which is interpreted by basic as ^
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)          PORT_CHAR(UCHAR_MAMEKEY(DOWN)) // newline
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)          PORT_CHAR(UCHAR_MAMEKEY(LEFT)) // backspace
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)         PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) // tab
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)         PORT_CHAR(' ')

	PORT_START("KEY.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Mod Sel") PORT_CODE(KEYCODE_LALT)  PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Rpt") PORT_CODE(KEYCODE_PGDN) PORT_CHAR(UCHAR_MAMEKEY(F11))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("L.P.") // marked as "L.P." in the manual, lightpen?

	PORT_START("RST")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_F5) PORT_NAME("Rst") PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cgenie_state::rst_callback), 0)
INPUT_PORTS_END


//**************************************************************************
//  KEYBOARD
//**************************************************************************

uint8_t cgenie_state::keyboard_r(offs_t offset)
{
	uint8_t data = 0;

	for (int i = 0; i < 8; i++)
		if (BIT(offset, i))
			data |= m_keyboard[i]->read();

	return data;
}

INPUT_CHANGED_MEMBER( cgenie_state::rst_callback )
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, newval);
}


//**************************************************************************
//  CONTROL PORT & RS232
//**************************************************************************

void cgenie_state::control_w(uint8_t data)
{
	// cassette output
	m_cassette->output(BIT(data, 0) ? -1.0 : 1.0);

	// serial output
	m_rs232->write_txd(BIT(data, 1));

	// background color selection
	if (BIT(data, 2))
		m_background_color = m_palette_bg[4];
	else
		m_background_color = m_palette_bg[data >> 6 & 0x03];

	// graphics/text mode switch
	m_crtc->set_hpixels_per_column(BIT(data, 5) ? 4 : 8);

	m_control = data;
}

uint8_t cgenie_state::control_r()
{
	uint8_t data = 0;

	data |= m_cassette->input() > 0 ? 1 : 0;
	data |= m_rs232_rx << 1;
	data |= m_rs232_dcd << 2;

	return data;
}

void cgenie_state::rs232_rx_w(int state)
{
	m_rs232_rx = state;
}

void cgenie_state::rs232_dcd_w(int state)
{
	m_rs232_dcd = state;
}


//**************************************************************************
//  DRIVER INIT
//**************************************************************************

void cgenie_state::init_cgenie_eu()
{
	m_palette = &m_palette_eu[0];
}

void cgenie_state::init_cgenie_nz()
{
	m_palette = &m_palette_nz[0];
}

void cgenie_state::machine_start()
{
	// setup ram
	m_maincpu->space(AS_PROGRAM).install_ram(0x4000, 0x4000 + m_ram->size() - 1, m_ram->pointer());
}


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

uint8_t cgenie_state::colorram_r(offs_t offset)
{
	return m_color_ram[offset] | 0xf0;
}

void cgenie_state::colorram_w(offs_t offset, uint8_t data)
{
	m_color_ram[offset] = data & 0x0f;
}

MC6845_BEGIN_UPDATE( cgenie_state::crtc_begin_update )
{
	bitmap.fill(m_background_color, cliprect);
}

MC6845_UPDATE_ROW( cgenie_state::crtc_update_row )
{
	// don't need to do anything in vblank
	if (!de)
		return;

	for (int column = 0; column < x_count; column++)
	{
		uint8_t code = m_ram->pointer()[ma + column];
		uint8_t color = m_color_ram[(ma + column) & 0x3ff];

		// gfx mode?
		if (BIT(m_control, 5))
		{
			const rgb_t map[] = { m_background_color, m_palette[8], m_palette[6], m_palette[5] };

			bitmap.pix(y + vbp, column * 4 + hbp + 0) = map[code >> 6 & 0x03];
			bitmap.pix(y + vbp, column * 4 + hbp + 1) = map[code >> 4 & 0x03];
			bitmap.pix(y + vbp, column * 4 + hbp + 2) = map[code >> 2 & 0x03];
			bitmap.pix(y + vbp, column * 4 + hbp + 3) = map[code >> 0 & 0x03];
		}
		else
		{
			uint8_t gfx = 0;

			// cursor visible?
			if (cursor_x == column)
				gfx = 0xff;

			// or use character rom?
			else if ((code < 128) || (code < 192 && BIT(m_control, 4)) || (code >= 192 && BIT(m_control, 3)))
				gfx = m_char_rom->base()[(code << 3) | ra];

			// or the programmable characters?
			else
				gfx = m_font_ram[((code << 3) | ra) & 0x3ff];

			// 8 pixel chars
			for (int p = 0; p < 8; p++)
				bitmap.pix(y + vbp, column * 8 + hbp + p) = BIT(gfx, 7 - p) ? m_palette[color] : m_background_color;
		}
	}
}


//**************************************************************************
//  PALETTE
//**************************************************************************

// how accurate are these colors?
const rgb_t cgenie_state::m_palette_bg[] =
{
	rgb_t::black(),
	rgb_t(0x70, 0x28, 0x20), // dark orange
	rgb_t(0x28, 0x70, 0x20), // dark green
	rgb_t(0x48, 0x48, 0x48), // dark gray
	rgb_t(0x70, 0x00, 0x70)  // dark purple
};

// european palette
const rgb_t cgenie_state::m_palette_eu[] =
{
	rgb_t(0x5e, 0x5e, 0x5e), // gray
	rgb_t(0x11, 0xff, 0xea), // cyan
	rgb_t(0xff, 0x00, 0x5e), // red
	rgb_t(0xea, 0xea, 0xea), // white
	rgb_t(0xff, 0xf9, 0x00), // yellow
	rgb_t(0x6e, 0xff, 0x00), // green
	rgb_t(0xff, 0x52, 0x00), // orange
	rgb_t(0xea, 0xff, 0x00), // light yellow
	rgb_t(0x02, 0x48, 0xff), // blue
	rgb_t(0x8e, 0xd4, 0xff), // light blue
	rgb_t(0xff, 0x12, 0xff), // pink
	rgb_t(0x88, 0x43, 0xff), // purple
	rgb_t(0x8c, 0x8c, 0x8c), // light gray
	rgb_t(0x00, 0xfb, 0x8c), // turquoise
	rgb_t(0xd2, 0x00, 0xff), // magenta
	rgb_t::white()           // bright white
};

// new zealand palette
const rgb_t cgenie_state::m_palette_nz[] =
{
	rgb_t::white(),
	rgb_t(0x12, 0xff, 0xff),
	rgb_t(0xff, 0x6f, 0xff),
	rgb_t(0x31, 0x77, 0xff),
	rgb_t(0xff, 0xcb, 0x00),
	rgb_t(0x6e, 0xff, 0x00),
	rgb_t(0xff, 0x52, 0x00),
	rgb_t(0x5e, 0x5e, 0x5e),
	rgb_t(0xea, 0xea, 0xea),
	rgb_t(0x00, 0xff, 0xdd),
	rgb_t(0xd2, 0x00, 0xff),
	rgb_t(0x02, 0x48, 0xff),
	rgb_t(0xff, 0xf9, 0x00),
	rgb_t(0x00, 0xda, 0x00),
	rgb_t(0xff, 0x22, 0x00),
	rgb_t::black()
};


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void cgenie_state::cgenie(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, XTAL(17'734'470) / 8); // 2.2168 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &cgenie_state::cgenie_mem);
	m_maincpu->set_addrmap(AS_IO, &cgenie_state::cgenie_io);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(XTAL(17'734'470) / 2, 568, 32, 416, 312, 28, 284);
	screen.set_screen_update("crtc", FUNC(hd6845s_device::screen_update));

	HD6845S(config, m_crtc, XTAL(17'734'470) / 16);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(true);
	m_crtc->set_char_width(8);
	m_crtc->set_begin_update_callback(FUNC(cgenie_state::crtc_begin_update));
	m_crtc->set_update_row_callback(FUNC(cgenie_state::crtc_update_row));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	ay8910_device &ay8910(AY8910(config, "ay8910", XTAL(17'734'470) / 8));
	ay8910.port_a_read_callback().set("par", FUNC(cg_parallel_slot_device::pa_r));
	ay8910.port_a_write_callback().set("par", FUNC(cg_parallel_slot_device::pa_w));
	ay8910.port_b_read_callback().set("par", FUNC(cg_parallel_slot_device::pb_r));
	ay8910.port_b_write_callback().set("par", FUNC(cg_parallel_slot_device::pb_w));
	ay8910.add_route(ALL_OUTPUTS, "mono", 0.75);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(cgenie_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("cgenie_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("cgenie_cass");

	// serial port
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(FUNC(cgenie_state::rs232_rx_w));
	rs232.dcd_handler().set(FUNC(cgenie_state::rs232_dcd_w));

	// cartridge expansion slot
	CG_EXP_SLOT(config, m_exp);
	m_exp->set_program_space(m_maincpu, AS_PROGRAM);
	m_exp->set_io_space(m_maincpu, AS_IO);
	m_exp->int_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	// parallel slot
	CG_PARALLEL_SLOT(config, "par");

	// internal ram
	RAM(config, RAM_TAG).set_default_size("16K").set_extra_options("32K");
}


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

ROM_START( cgenie )
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_LOAD("cg_rom1.z1", 0x0000, 0x1000, CRC(d3369420) SHA1(fe7f06f8e2b648d695d4787d00a374d3e4ac5d39))
	ROM_LOAD("cg_rom2.z2", 0x1000, 0x1000, CRC(73d2c9ea) SHA1(343d595b4eeaea627f9c36d5cef3827c79593425))
	ROM_LOAD("cg_rom3.z3", 0x2000, 0x1000, CRC(3f358811) SHA1(6ba151759fd8fd367806cf2dd5f1dfc33ee9521f))
	ROM_LOAD("cg_rom4.z4", 0x3000, 0x1000, CRC(be235782) SHA1(d7d61208a9855ffd09ecb051618f5ed6a8816f3f))

	ROM_REGION(0x0800, "gfx1", 0)
	// this is a "german" character set with umlauts, is this official?
	ROM_LOAD("cgenieg.fnt", 0x0000, 0x0800, CRC(c3e60d57) SHA1(fb96f608fdb47391145fdcd614a9c7a79756e6a4))
	// default character set
	ROM_LOAD("cgenie1.fnt", 0x0000, 0x0800, CRC(4fed774a) SHA1(d53df8212b521892cc56be690db0bb474627d2ff))
ROM_END

ROM_START( cgenienz )
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_SYSTEM_BIOS(0, "old", "Old ROM")
	ROMX_LOAD("cg-basic-rom-v1-pal-en.rom", 0x0000, 0x4000, CRC(844aaedd) SHA1(b7f984bc5cd979c7ad11ff909e8134f694aea7aa), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "new", "New ROM")
	ROMX_LOAD("cgromv2.rom", 0x0000, 0x4000, CRC(cfb84e09) SHA1(e199e4429bab6f9fca2bb05e71324538928a693a), ROM_BIOS(1))

	ROM_REGION(0x0800, "gfx1", 0)
	ROM_LOAD("cgenie1.fnt", 0x0000, 0x0800, CRC(4fed774a) SHA1(d53df8212b521892cc56be690db0bb474627d2ff))
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME      PARENT  COMPAT  MACHINE INPUT   CLASS         INIT            COMPANY FULLNAME                             FLAGS
COMP( 1982, cgenie,   0,      0,      cgenie, cgenie, cgenie_state, init_cgenie_eu, "EACA", "Colour Genie EG2000",               0)
COMP( 1982, cgenienz, cgenie, 0,      cgenie, cgenie, cgenie_state, init_cgenie_nz, "EACA", "Colour Genie EG2000 (New Zealand)", 0)



challenge_gear.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

#include "emu.h"

#include "cpu/olms66k/msm665xx.h"
#include "video/sed1520.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"

/*

TSOP32 mask ROM:
The pinout nearly matches the 29LV040 (TSOP32) Flash ROM. It has no WE# and pin0 7 and 9 are swapped.

       --------
A11 --|01    32|-- OE#
A09 --|02    31|-- A10
A08 --|03    30|-- CE#
A13 --|04    29|-- Q07
A14 --|05    28|-- Q06
A17 --|06    27|-- Q05
A18 --|07    26|-- Q04
VCC --|08    25|-- Q03
NC  --|09    24|-- GND
A16 --|10    23|-- Q02
A15 --|11    22|-- Q01
A12 --|12    21|-- Q00
A07 --|13    20|-- A00
A06 --|14    19|-- A01
A05 --|15    18|-- A02
A04 --|16    17|-- A03
       --------

*/

namespace {

class challenge_gear_state : public driver_device
{
public:
	challenge_gear_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		//, m_maincpu(*this, "maincpu")
		, m_cart(*this, "cartslot")
	{ }

	void challenge_gear(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
	NT7502_UPDATE_CB(lcd_update);

	void program_map(address_map &map) ATTR_COLD;
	void data_map(address_map &map) ATTR_COLD;

	//required_device<cpu_device> m_maincpu;
	required_device<generic_slot_device> m_cart;
	uint32_t m_rom_size = 0;
};


DEVICE_IMAGE_LOAD_MEMBER( challenge_gear_state::cart_load )
{
	m_rom_size = m_cart->common_get_size("rom");
	m_cart->rom_alloc(m_rom_size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), m_rom_size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

NT7502_UPDATE_CB( challenge_gear_state::lcd_update )
{
	// TODO
	return 0;
}

void challenge_gear_state::program_map(address_map &map)
{
	map(0x00000, 0x7ffff).rom().region("maincpu", 0);
}

void challenge_gear_state::data_map(address_map &map)
{
	map.global_mask(0xffff);
	map(0x8000, 0x8001).rw("lcdc", FUNC(nt7502_device::read), FUNC(nt7502_device::write));
}

static INPUT_PORTS_START( challenge_gear )
INPUT_PORTS_END

void challenge_gear_state::machine_start()
{
}

void challenge_gear_state::machine_reset()
{
}


void challenge_gear_state::challenge_gear(machine_config &config)
{
	/* basic machine hardware */
	msm665xx_device &maincpu(MSM66573(config, "maincpu", 4_MHz_XTAL)); // 100-lead square glob (type guessed); XL2 = 4.00H
	//maincpu.set_xtclk(32.768_kHz_XTAL);
	maincpu.set_addrmap(AS_PROGRAM, &challenge_gear_state::program_map);
	maincpu.set_addrmap(AS_DATA, &challenge_gear_state::data_map);
	// P6.5 <-> I2C SDA
	// P6.6 -> I2C SCL
	// P8 -> key matrix -> P10

	//I2C_24C16(config, "eeprom"); // "BR24C16FV" silkscreened on PCB

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(0));
	screen.set_size(100, 64);
	screen.set_visarea_full();
	screen.set_palette("palette");
	screen.set_screen_update("lcdc", FUNC(nt7502_device::screen_update));

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	NT7502(config, "lcdc").set_screen_update_cb(FUNC(challenge_gear_state::lcd_update));

	generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "challenge_gear_cart", "bin"));
	cartslot.set_device_load(FUNC(challenge_gear_state::cart_load));

	SOFTWARE_LIST(config, "cg_list").set_compatible("challenge_gear_cart");
}


ROM_START( chalgear )
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_SYSTEM_BIOS(0, "rom501", "ms040501") // yellow function buttons (blue text), orange number buttons (blue text), patterned purple screen surround
	ROMX_LOAD("ms040501.ic2", 0x0000, 0x80000, CRC(fe8918ca) SHA1(e1e560d0bf811f3f8a6c122fbff065da4a30a9b6), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "rom402", "ms040402") // blue function buttons (yellow text), yellow numbers buttons (blue text), plain black screen surrounded
	ROMX_LOAD("ms040402.ic2", 0x0000, 0x80000, CRC(25aa24b2) SHA1(72c3f304289da531165d83c63f2c3632985428ba), ROM_BIOS(1))
ROM_END

} // Anonymous namespace

CONS( 2002, chalgear, 0,      0,      challenge_gear, challenge_gear, challenge_gear_state, empty_init, "Benesse Corporation", "Challenge Gear (Japan)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



channelf.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Juergen Buchmueller, Frank Palazzolo, Sean Riddle
/******************************************************************
 *
 *  Fairchild Channel F driver
 *
 *  Juergen Buchmueller
 *  Frank Palazzolo
 *  Sean Riddle
 *
 *  Fredric "e5frog" Blaoholtz, added support large cartridges
 *    also spanning from $3000 to $FFFF. Added clones
 *  Fabio "etabeta" Priuli, moved carts to be slot devices
 *
 *  TODO:
 *  - hook up F3851 and F3853 devices (note: from a black box pov there's
 *    currently no problem, nothing uses the timer or irq)
 *
 ******************************************************************/

#include "emu.h"
#include "channelf.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


/* The F8 has latches on its port pins
 * These mimic's their behavior
 * [0]=port0, [1]=port1, [2]=port4, [3]=port5
 *
 * Note: this should really be moved into f8.c,
 * but it's complicated by possible peripheral usage.
 *
 * If the read/write operation is going external to the F3850
 * (or F3853, etc.), then the latching applies.  However, relaying the
 * port read/writes from the F3850 to a peripheral like the F3853
 * should not be latched in this way. (See mk1 driver)
 *
 * The f8 cannot determine how its ports are mapped at runtime,
 * so it can't easily decide to latch or not.
 *
 * ...so it stays here for now.
 */

uint8_t channelf_state::port_read_with_latch(uint8_t ext, uint8_t latch_state)
{
	return (~ext | latch_state);
}

uint8_t channelf_state::port_0_r()
{
	return port_read_with_latch(ioport("PANEL")->read(), m_latch[0]);
}

uint8_t channelf_state::port_1_r()
{
	uint8_t ext_value;

	if ((m_latch[0] & 0x40) == 0)
		ext_value = ioport("RIGHT_C")->read();
	else
		ext_value = 0xc0 | ioport("RIGHT_C")->read();

	return port_read_with_latch(ext_value,m_latch[1]);
}

uint8_t channelf_state::port_4_r()
{
	uint8_t ext_value;

	if ((m_latch[0] & 0x40) == 0)
		ext_value = ioport("LEFT_C")->read();
	else
		ext_value = 0xff;

	return port_read_with_latch(ext_value,m_latch[2]);
}

uint8_t channelf_state::port_5_r()
{
	return port_read_with_latch(0xff, m_latch[3]);
}

void channelf_state::port_0_w(uint8_t data)
{
	int offs;

	m_latch[0] = data;

	if (data & 0x20)
	{
		offs = m_row_reg*128+m_col_reg;
		m_p_videoram[offs] = m_val_reg;
	}
}

void channelf_state::port_1_w(uint8_t data)
{
	m_latch[1] = data;
	m_val_reg = ((data ^ 0xff) >> 6) & 0x03;
}

void channelf_state::port_4_w(uint8_t data)
{
	m_latch[2] = data;
	m_col_reg = (data | 0x80) ^ 0xff;
}

void channelf_state::port_5_w(uint8_t data)
{
	m_latch[3] = data;
	m_custom->sound_w((data>>6)&3);
	m_row_reg = (data | 0xc0) ^ 0xff;
}

void channelf_state::channelf_map(address_map &map)
{
	map(0x0000, 0x07ff).rom();
	map(0x0800, 0xffff).r(m_cart, FUNC(channelf_cart_slot_device::read_rom));
}

void channelf_state::channelf_io(address_map &map)
{
	map(0x00, 0x00).rw(FUNC(channelf_state::port_0_r), FUNC(channelf_state::port_0_w)); /* Front panel switches */
	map(0x01, 0x01).rw(FUNC(channelf_state::port_1_r), FUNC(channelf_state::port_1_w)); /* Right controller     */
	map(0x04, 0x04).rw(FUNC(channelf_state::port_4_r), FUNC(channelf_state::port_4_w)); /* Left controller      */
	map(0x05, 0x05).rw(FUNC(channelf_state::port_5_r), FUNC(channelf_state::port_5_w));
}



static INPUT_PORTS_START( channelf )
	PORT_START("PANEL")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TIME (Button 1)") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("HOLD (Button 2)") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("MODE (Button 3)") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("START (Button 4)") PORT_CODE(KEYCODE_4)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("RIGHT_C")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_PLAYER(1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT) PORT_PLAYER(1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN) PORT_PLAYER(1)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP) PORT_PLAYER(1)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON4) PORT_NAME("P1 Twist Counterclockwise") PORT_PLAYER(1)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_NAME("P1 Twist Clockwise")PORT_PLAYER(1)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("P1 Pull Up") PORT_PLAYER(1)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("P1 Push Down") PORT_PLAYER(1)

	PORT_START("LEFT_C")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_PLAYER(2)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT) PORT_PLAYER(2)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN) PORT_PLAYER(2)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP) PORT_PLAYER(2)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON4) PORT_NAME("P2 Twist Counterclockwise") PORT_PLAYER(2)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_NAME("P2 Twist Clockwise")PORT_PLAYER(2)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("P2 Pull Up") PORT_PLAYER(2)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("P2 Push Down") PORT_PLAYER(2)
INPUT_PORTS_END


void channelf_state::machine_start()
{
	if (m_cart->exists())
	{
		switch (m_cart->get_type())
		{
			case CF_MAZE:
				m_maincpu->space(AS_IO).install_readwrite_handler(0x24, 0x25, read8sm_delegate(*m_cart, FUNC(channelf_cart_slot_device::read_ram)), write8sm_delegate(*m_cart, FUNC(channelf_cart_slot_device::write_ram)));
				break;
			case CF_HANGMAN:
				m_maincpu->space(AS_IO).install_readwrite_handler(0x20, 0x21, read8sm_delegate(*m_cart, FUNC(channelf_cart_slot_device::read_ram)), write8sm_delegate(*m_cart, FUNC(channelf_cart_slot_device::write_ram)));
				break;
			case CF_CHESS:
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x2800, 0x2fff, read8sm_delegate(*m_cart, FUNC(channelf_cart_slot_device::read_ram)), write8sm_delegate(*m_cart, FUNC(channelf_cart_slot_device::write_ram)));
				break;
			case CF_MULTI:
			case CF_MULTI_OLD:
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x2800, 0x2fff, read8sm_delegate(*m_cart, FUNC(channelf_cart_slot_device::read_ram)), write8sm_delegate(*m_cart, FUNC(channelf_cart_slot_device::write_ram)));
				m_maincpu->space(AS_PROGRAM).install_write_handler(0x3000, 0x3fff, write8smo_delegate(*m_cart, FUNC(channelf_cart_slot_device::write_bank)));
				break;
		}

		m_cart->save_ram();
	}
}

static void cf_cart(device_slot_interface &device)
{
	device.option_add_internal("std",      CHANF_ROM_STD);
	device.option_add_internal("maze",     CHANF_ROM_MAZE);
	device.option_add_internal("hangman",  CHANF_ROM_HANGMAN);
	device.option_add_internal("chess",    CHANF_ROM_CHESS);
	device.option_add_internal("multi_old",CHANF_ROM_MULTI_OLD);
	device.option_add_internal("multi",    CHANF_ROM_MULTI_FINAL);
}


void channelf_state::channelf_cart(machine_config &config)
{
	/* cartridge */
	CHANF_CART_SLOT(config, m_cart, cf_cart, nullptr);

	/* Software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("channelf");
}

void channelf_state::channelf(machine_config &config)
{
	/* basic machine hardware */
	F8(config, m_maincpu, 3.579545_MHz_XTAL/2); /* Colorburst/2 */
	m_maincpu->set_addrmap(AS_PROGRAM, &channelf_state::channelf_map);
	m_maincpu->set_addrmap(AS_IO, &channelf_state::channelf_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(3.579545_MHz_XTAL * 8 / 7, 256, 8, 212, 264, 16, 248);
	screen.set_screen_update(FUNC(channelf_state::screen_update_ntsc));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(channelf_state::channelf_palette), 8);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	CHANNELF_SOUND(config, m_custom).add_route(ALL_OUTPUTS, "mono", 1.00);

	channelf_cart(config);
}

void channelf_state::sabavdpl(machine_config &config)
{
	/* basic machine hardware */
	F8(config, m_maincpu, 4_MHz_XTAL/2); /* PAL speed */
	m_maincpu->set_addrmap(AS_PROGRAM, &channelf_state::channelf_map);
	m_maincpu->set_addrmap(AS_IO, &channelf_state::channelf_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(4_MHz_XTAL, 256, 8, 212, 312, 20, 310);
	screen.set_screen_update(FUNC(channelf_state::screen_update_pal));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(channelf_state::channelf_palette), 8);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	CHANNELF_SOUND(config, m_custom).add_route(ALL_OUTPUTS, "mono", 1.00);

	channelf_cart(config);
}


void channelf_state::channlf2(machine_config &config)
{
	/* basic machine hardware */
	F8(config, m_maincpu, 3.579545_MHz_XTAL/2); /* Colorburst / 2 */
	m_maincpu->set_addrmap(AS_PROGRAM, &channelf_state::channelf_map);
	m_maincpu->set_addrmap(AS_IO, &channelf_state::channelf_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(3.579545_MHz_XTAL * 8 / 7, 256, 8, 212, 264, 16, 248);
	screen.set_screen_update(FUNC(channelf_state::screen_update_ntsc));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(channelf_state::channelf_palette), 8);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	CHANNELF_SOUND(config, m_custom).add_route(ALL_OUTPUTS, "mono", 1.00);

	channelf_cart(config);
}


void channelf_state::sabavpl2(machine_config &config)
{
	/* basic machine hardware */
	F8(config, m_maincpu, 4_MHz_XTAL/2); /* PAL speed */
	m_maincpu->set_addrmap(AS_PROGRAM, &channelf_state::channelf_map);
	m_maincpu->set_addrmap(AS_IO, &channelf_state::channelf_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(4_MHz_XTAL, 256, 8, 212, 312, 20, 310);
	screen.set_screen_update(FUNC(channelf_state::screen_update_pal));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(channelf_state::channelf_palette), 8);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	CHANNELF_SOUND(config, m_custom).add_route(ALL_OUTPUTS, "mono", 0.50);

	channelf_cart(config);
}

ROM_START( channelf )
	ROM_REGION(0x10000,"maincpu",0)
	ROM_SYSTEM_BIOS( 0, "sl90025", "Luxor Video Entertainment System" )
	ROMX_LOAD("sl90025.rom",  0x0000, 0x0400, CRC(015c1e38) SHA1(759e2ed31fbde4a2d8daf8b9f3e0dffebc90dae2), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "sl31253", "Channel F" )
	ROMX_LOAD("sl31253.rom",  0x0000, 0x0400, CRC(04694ed9) SHA1(81193965a374d77b99b4743d317824b53c3e3c78), ROM_BIOS(1))
	ROM_LOAD("sl31254.rom",   0x0400, 0x0400, CRC(9c047ba3) SHA1(8f70d1b74483ba3a37e86cf16c849d601a8c3d2c))
	ROM_REGION(0x2000, "vram", ROMREGION_ERASE00)
ROM_END

#define rom_sabavdpl rom_channelf
#define rom_luxorves rom_channelf
#define rom_channlf2 rom_channelf
#define rom_sabavdpl rom_channelf
#define rom_sabavpl2 rom_channelf
#define rom_luxorvec rom_channelf
#define rom_itttelma rom_channelf
#define rom_ingtelma rom_channelf

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

  Game driver(s)

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

//    YEAR  NAME      PARENT    COMPAT    MACHINE   INPUT     CLASS           INIT        COMPANY         FULLNAME                                FLAGS
CONS( 1976, channelf, 0,        0,        channelf, channelf, channelf_state, empty_init, "Fairchild",    "Channel F",                            0 )
CONS( 1977, sabavdpl, channelf, 0,        sabavdpl, channelf, channelf_state, empty_init, "SABA",         "SABA Videoplay",                       0 )
CONS( 197?, luxorves, channelf, 0,        sabavdpl, channelf, channelf_state, empty_init, "Luxor",        "Luxor Video Entertainment System",     0 )
CONS( 1978, channlf2, 0,        channelf, channlf2, channelf, channelf_state, empty_init, "Fairchild",    "Channel F II",                         0 )
CONS( 1978, sabavpl2, channlf2, 0,        sabavpl2, channelf, channelf_state, empty_init, "SABA",         "SABA Videoplay 2",                     0 )
CONS( 197?, luxorvec, channlf2, 0,        sabavpl2, channelf, channelf_state, empty_init, "Luxor",        "Luxor Video Entertainment Computer",   0 )
CONS( 197?, itttelma, channlf2, 0,        sabavpl2, channelf, channelf_state, empty_init, "ITT",          "ITT Tele-Match Processor",             0 )
CONS( 1978, ingtelma, channlf2, 0,        sabavpl2, channelf, channelf_state, empty_init, "Ingelen",      "Ingelen Tele-Match Processor",         0 )



chaos.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************

    Chaos2

    2010-04-08 Skeleton driver.
    2012-05-19 Connected to a terminal, system is usable [Robbbert]

    This is a homebrew system: http://koo.corpus.cam.ac.uk/chaos/

    There are no schematics or manuals, so the results might not be
    totally accurate.

    With the DOS config switch turned off, the only accepted input
    is a line starting with '&'. The actual commands are unknown.

    With DOS enabled, a large number of commands become available.
    These are:
    access, ask, ascdis, bpclr, bpset, close, control, copy, devfive, dir,
    end, exec, execute, fill, find, goto, if, input, let, list, load, lowercase,
    memdis, memset, open, port, read, reboot, runhex, run, save, type, typesl,
    verify.
    An example is: memdis 0 8 (memory dump starting at 0, show 8 lines)
    Don't try 'fill' - it fills all memory with zeroes, crashing the system.

    ToDo:
    - Connect up floppy disk (WD1771 fdc, 5.25", single density,
      no other info available)

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

#include "emu.h"
#include "cpu/s2650/s2650.h"
#include "machine/terminal.h"


namespace {

class chaos_state : public driver_device
{
public:
	chaos_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_terminal(*this, "terminal")
		, m_p_ram(*this, "ram")
		, m_maincpu(*this, "maincpu")
	{ }

	void chaos(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	u8 port1e_r();
	void port1f_w(u8 data);
	u8 port90_r();
	u8 port91_r();
	void kbd_put(u8 data);
	void data_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	u8 m_term_data = 0U;
	required_device<generic_terminal_device> m_terminal;
	required_shared_ptr<u8> m_p_ram;
	required_device<cpu_device> m_maincpu;
};


void chaos_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).ram().share("ram");
}

void chaos_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x1e, 0x1e).r(FUNC(chaos_state::port1e_r));
	map(0x1f, 0x1f).rw(FUNC(chaos_state::port90_r), FUNC(chaos_state::port1f_w));
	map(0x90, 0x90).r(FUNC(chaos_state::port90_r));
	map(0x91, 0x91).r(FUNC(chaos_state::port91_r));
	map(0x92, 0x92).w(m_terminal, FUNC(generic_terminal_device::write));
}

void chaos_state::data_map(address_map &map)
{
	map(S2650_DATA_PORT, S2650_DATA_PORT).noprw(); // stops error log filling up while using debug
}

/* Input ports */
static INPUT_PORTS_START( chaos )
	PORT_START("CONFIG")
	PORT_CONFNAME( 0x01, 0x00, "Enable DOS")
	PORT_CONFSETTING(    0x01, DEF_STR(No))
	PORT_CONFSETTING(    0x00, DEF_STR(Yes))
INPUT_PORTS_END


// Port 1E - Bit 0 indicates key pressed, Bit 1 indicates ok to output

u8 chaos_state::port1e_r()
{
	return (m_term_data) ? 1 : 0;
}

void chaos_state::port1f_w(u8 data)
{
	// make the output readable on our terminal
	if (data == 0x09)
		return;
	else
	if (!data)
		data = 0x24;

	m_terminal->write(data);

	if (data == 0x0d)
		m_terminal->write(0x0a);
}

u8 chaos_state::port90_r()
{
	u8 ret = m_term_data;
	m_term_data = 0;
	return ret;
}

// Status port
// Bit 0 = L use ports 1E & 1F; H use ports 90 & 92
// Bit 3 = key pressed
// Bit 7 = ok to output

u8 chaos_state::port91_r()
{
	u8 ret = 0x80 | ioport("CONFIG")->read();
	ret |= (m_term_data) ? 8 : 0;
	return ret;
}

void chaos_state::kbd_put(u8 data)
{
	m_term_data = data;
}

void chaos_state::machine_start()
{
	save_item(NAME(m_term_data));

	m_term_data = 0;
}

void chaos_state::machine_reset()
{
	// copy the roms into ram
	u8* ROM = memregion("roms")->base();
	memcpy(m_p_ram, ROM, 0x3000);
	memcpy(m_p_ram+0x7000, ROM+0x3000, 0x1000);
}

void chaos_state::chaos(machine_config &config)
{
	/* basic machine hardware */
	S2650(config, m_maincpu, XTAL(1'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &chaos_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &chaos_state::io_map);
	m_maincpu->set_addrmap(AS_DATA, &chaos_state::data_map);

	/* video hardware */
	GENERIC_TERMINAL(config, m_terminal, 0);
	m_terminal->set_keyboard_callback(FUNC(chaos_state::kbd_put));
}

/* ROM definition */
ROM_START( chaos )
	ROM_REGION( 0x4000, "roms", 0 )
	ROM_LOAD( "chaos.001", 0x0000, 0x1000, CRC(3b433e72) SHA1(5b487337d71253d0e64e123f405da9eaf20e87ac))
	ROM_LOAD( "chaos.002", 0x1000, 0x1000, CRC(8b0b487f) SHA1(0d167cf3004a81c87446f2f1464e3debfa7284fe))
	ROM_LOAD( "chaos.003", 0x2000, 0x1000, CRC(5880db81) SHA1(29b8f1b03c83953f66464ad1fbbfe2e019637ce1))
	ROM_LOAD( "chaos.004", 0x3000, 0x1000, CRC(5d6839d6) SHA1(237f52f0780ac2e29d57bf06d0f7a982eb523084))
ROM_END

} // Anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY          FULLNAME   FLAGS
COMP( 1983, chaos, 0,      0,      chaos,   chaos, chaos_state, empty_init, "David Greaves", "Chaos 2", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



checkc2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Fidelity Checker Challenger (CR)

Even though it has fewer levels and presumedly a weaker program, this one
is a couple of months newer than model ACR (see cc10.cpp).

Hardware notes:
- PCB label: P261A
- NEC uCOM-43 MCU, label D546C 055 (die label same)
- 256x4 RAM (NEC D2101AL-4)
- 4-digit 7seg led display + 2 other leds, no sound

TODO:
- according to the manual, the right digits should blink when the CPU
  opponent wants to make a double jump, but it doesn't blink on MAME

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

#include "emu.h"

#include "cpu/ucom4/ucom4.h"
#include "video/pwm.h"

// internal artwork
#include "fidel_cr.lh"


namespace {

class cr_state : public driver_device
{
public:
	cr_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void cr(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<ucom4_cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_ioport_array<4> m_inputs;

	u8 m_ram[0x100] = { };
	u8 m_ram_address = 0;
	u8 m_ram_data = 0;
	u8 m_ram_control = 0;

	u8 m_inp_mux = 0;
	u8 m_led_select = 0;
	u8 m_7seg_data = 0;

	// I/O handlers
	void update_display();
	void segsel_w(u8 data);
	void seg0_w(u8 data);
	void seg1_w(u8 data);
	void control_w(u8 data);
	void ram_w(u8 data);
	u8 ram_r();
	void rama0_w(u8 data);
	void rama1_w(u8 data);
	u8 input_r();
};

void cr_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_ram));
	save_item(NAME(m_ram_address));
	save_item(NAME(m_ram_data));
	save_item(NAME(m_ram_control));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_led_select));
	save_item(NAME(m_7seg_data));
}



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

void cr_state::update_display()
{
	m_display->matrix(m_led_select, m_7seg_data);
}

void cr_state::segsel_w(u8 data)
{
	// G: 7seg select
	m_led_select = (m_led_select & 0x30) | data;
	update_display();
}

void cr_state::seg0_w(u8 data)
{
	// I: 7seg data(low)
	m_7seg_data = (m_7seg_data & 0x78) | (data & 7);
	update_display();
}

void cr_state::seg1_w(u8 data)
{
	// H: 7seg data(high)
	m_7seg_data = (m_7seg_data & 0x07) | data << 3;
	update_display();
}

void cr_state::control_w(u8 data)
{
	// F0,F1: direct leds
	m_led_select = (m_led_select & 0x0f) | (data << 4 & 0x30);
	update_display();

	// F2: RAM R/W, F3: RAM CE
	if ((data & 0xc) == 8 && ~m_ram_control & 2)
		m_ram[m_ram_address] = m_ram_data;
	m_ram_control = data >> 2;
}

void cr_state::ram_w(u8 data)
{
	// C: RAM DI
	m_ram_data = data;
}

u8 cr_state::ram_r()
{
	// B: RAM DO
	return m_ram[m_ram_address];
}

void cr_state::rama0_w(u8 data)
{
	// E: RAM address(low), input mux
	m_ram_address = (m_ram_address & 0xf0) | data;
	m_inp_mux = data;
}

void cr_state::rama1_w(u8 data)
{
	// D: RAM address(high)
	m_ram_address = (m_ram_address & 0x0f) | data << 4;
}

u8 cr_state::input_r()
{
	u8 data = 0;

	// A: multiplexed inputs
	for (int i = 0; i < 4; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read();

	return data;
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( cr )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("PV") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("LV") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("RE") PORT_CODE(KEYCODE_R)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("CL") PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE)

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("TO") PORT_CODE(KEYCODE_T)

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("EN") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void cr_state::cr(machine_config &config)
{
	// basic machine hardware
	NEC_D546(config, m_maincpu, 400'000); // approximation
	m_maincpu->read_a().set(FUNC(cr_state::input_r));
	m_maincpu->read_b().set(FUNC(cr_state::ram_r));
	m_maincpu->write_c().set(FUNC(cr_state::ram_w));
	m_maincpu->write_d().set(FUNC(cr_state::rama1_w));
	m_maincpu->write_e().set(FUNC(cr_state::rama0_w));
	m_maincpu->write_f().set(FUNC(cr_state::control_w));
	m_maincpu->write_g().set(FUNC(cr_state::segsel_w));
	m_maincpu->write_h().set(FUNC(cr_state::seg1_w));
	m_maincpu->write_i().set(FUNC(cr_state::seg0_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(6, 7);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_fidel_cr);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( checkc2 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d546c-055", 0x0000, 0x0800, CRC(ee2de21e) SHA1(ca727093dc36dc15453bc5cca4e559fdc8242355) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT CLASS     INIT        COMPANY, FULLNAME, FLAGS
SYST( 1978, checkc2, 0,      0,      cr,      cr,   cr_state, empty_init, "Fidelity Electronics", "Checker Challenger (model CR, 2 levels)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )



chess.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Tryom Electronic Chess (model CC-700)

Hardware notes:
- PCB label: 170-313-01, JAB, TRYOM
- Fairchild 3870 MCU, label SL90453
- 256x4 RAM (SCM5101E-8)
- 9-digit 7seg led panel (3 unused), piezo

The hardware has similarities with Omar I.

The user interface resembles CompuChess. At the "L" prompt, enter level (1-8).
At "bP", press A for new game, B for empty board, C to continue. At "0", press
1-4 for an opening book, or 5 to select one at random.

BTANB:
- it locks up after pressing CE while it's thinking

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

#include "emu.h"

#include "cpu/f8/f8.h"
#include "machine/f3853.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

#include "tchess.lh"


namespace {

class chess_state : public driver_device
{
public:
	chess_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_psu(*this, "psu"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void tchess(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<f38t56_device> m_psu;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<5> m_inputs;

	std::unique_ptr<u8[]> m_ram;
	u8 m_ram_address = 0;
	u8 m_ram_data = 0;
	u8 m_inp_mux = 0;
	u8 m_digit_data = 0;

	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	u8 read_inputs();
	void p0_w(u8 data);
	u8 p0_r();
	void p1_w(u8 data);
	u8 p1_r();
	void p4_w(u8 data);
	u8 p4_r();
	void p5_w(u8 data);
	u8 p5_r();
};

void chess_state::machine_start()
{
	m_ram = make_unique_clear<u8[]>(0x100);

	// register for savestates
	save_pointer(NAME(m_ram), 0x100);
	save_item(NAME(m_ram_address));
	save_item(NAME(m_ram_data));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_digit_data));
}



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

u8 chess_state::read_inputs()
{
	u8 data = 0;

	for (int i = 0; i < 5; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read();

	return data;
}

void chess_state::p0_w(u8 data)
{
	// P00-P05: digit select, input mux
	// P06: RAM CE1
	m_display->write_my(~data & 0x3f);
	m_inp_mux = data;
}

u8 chess_state::p0_r()
{
	// P07: multiplexed inputs (high)
	return (read_inputs() << 3 & 0x80) | m_inp_mux;
}

void chess_state::p1_w(u8 data)
{
	// P10-P17: RAM address
	m_ram_address = data;
}

u8 chess_state::p1_r()
{
	return m_ram_address;
}

void chess_state::p4_w(u8 data)
{
	// P40-P43: RAM DI
	if (m_inp_mux & 0x40)
		m_ram[m_ram_address] = data & 0xf;

	m_ram_data = data;
}

u8 chess_state::p4_r()
{
	// P44-P47: multiplexed inputs (low), RAM DO
	u8 data = read_inputs();
	if (m_inp_mux & 0x40)
		data |= m_ram[m_ram_address];

	return data << 4 | m_ram_data;
}

void chess_state::p5_w(u8 data)
{
	// P50-P56: digit data
	m_display->write_mx(bitswap<7>(~data,0,1,5,4,6,2,3));
	m_digit_data = data;

	// P57: speaker out
	m_dac->write(BIT(data, 7));
}

u8 chess_state::p5_r()
{
	return m_digit_data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void chess_state::main_map(address_map &map)
{
	map.global_mask(0x7ff);
	map(0x0000, 0x07ff).rom();
}

void chess_state::main_io(address_map &map)
{
	map(0x00, 0x00).rw(FUNC(chess_state::p0_r), FUNC(chess_state::p0_w));
	map(0x01, 0x01).rw(FUNC(chess_state::p1_r), FUNC(chess_state::p1_w));
	map(0x04, 0x07).rw(m_psu, FUNC(f38t56_device::read), FUNC(f38t56_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( tchess )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("XMV")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("FP")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("EP")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("PLY")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("RST")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("A / WK")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("B / WQ")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("C / WB")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("D / WN")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("WSD")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("E / WR")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("F / WP")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("G")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("H")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("BSD")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1 / BK")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2 / BQ")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3 / BB")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4 / BN")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5 / BR")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6 / BP")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void chess_state::tchess(machine_config &config)
{
	// basic machine hardware
	F8(config, m_maincpu, 4500000/2); // approximation
	m_maincpu->set_addrmap(AS_PROGRAM, &chess_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &chess_state::main_io);
	m_maincpu->set_irq_acknowledge_callback(m_psu, FUNC(f38t56_device::int_acknowledge));

	F38T56(config, m_psu, 4500000/2);
	m_psu->set_int_vector(0x20);
	m_psu->int_req_callback().set_inputline(m_maincpu, F8_INPUT_LINE_INT_REQ);
	m_psu->read_a().set(FUNC(chess_state::p4_r));
	m_psu->write_a().set(FUNC(chess_state::p4_w));
	m_psu->read_b().set(FUNC(chess_state::p5_r));
	m_psu->write_b().set(FUNC(chess_state::p5_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(6, 7);
	m_display->set_segmask(0x3f, 0x7f);
	config.set_default_layout(layout_tchess);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( tchess )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD("sl90453", 0x0000, 0x0800, CRC(f7fbe9b0) SHA1(d79ae43acfdf733908bc57b1fcca2563a2fdf48e) ) // 3870X-0453
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS        INIT        COMPANY, FULLNAME, FLAGS
SYST( 1981, tchess, 0,      0,      tchess,  tchess, chess_state, empty_init, "Tryom", "Electronic Chess (Tryom)", MACHINE_SUPPORTS_SAVE )



chess2001.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

CXG Chess 2001, also sold by Hanimex as Computachess (model HCG 1900),
and by CGL as Computachess Champion.
CXG Chess 3000 is assumed to be on similar hardware as this.

The chess engine is by Richard Lang, based on Cyrus.

CXG Systems S.A. and Newcrest Technology Ltd. are related companies, with
Eric White at the steering wheel. Newcrest(1984-1991) is probably a rename of
"White and Allcock"(1981-1984).

Hardware notes:
- Zilog Z8400APS @ 4 MHz (8MHz XTAL)
- 2KB RAM HM6116, 16KB ROM D27128D
- TTL, piezo, 8*8+9 LEDs, magnetic sensors

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

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/clock.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "cxg_chess2001.lh"


namespace {

class chess2001_state : public driver_device
{
public:
	chess2001_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_board(*this, "board"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void chess2001(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<sensorboard_device> m_board;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<2> m_inputs;

	u16 m_inp_mux = 0;
	int m_dac_data = 0;

	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void speaker_w(u8 data);
	void leds_w(u8 data);
	u8 input_r();
};

void chess2001_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_dac_data));
}



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

void chess2001_state::speaker_w(u8 data)
{
	// 74ls109 toggle to speaker
	m_dac_data ^= 1;
	m_dac->write(m_dac_data);
}

void chess2001_state::leds_w(u8 data)
{
	// d0-d7: 74ls273 (WR to CLK)
	// 74ls273 Q1-Q4: 74ls145 A-D
	// 74ls145 0-9: input mux/led select
	m_inp_mux = data & 0xf;
	u16 sel = 1 << m_inp_mux & 0x3ff;

	// 74ls273 Q5-Q8: MC14028 A-D
	// MC14028 Q0-Q7: led data, Q8,Q9: N/C
	u8 led_data = 1 << (data >> 4 & 0xf) & 0xff;
	m_display->matrix(sel, led_data);
}

u8 chess2001_state::input_r()
{
	u8 data = 0;

	// d0-d7: multiplexed inputs
	// read chessboard sensors
	if (m_inp_mux < 8)
		data = m_board->read_file(m_inp_mux, true);

	// read other buttons
	else if (m_inp_mux < 10)
		data = m_inputs[m_inp_mux - 8]->read();

	return ~data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void chess2001_state::main_map(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	map(0x4000, 0x47ff).mirror(0x3800).ram();
	map(0x8000, 0x8000).mirror(0x3fff).rw(FUNC(chess2001_state::input_r), FUNC(chess2001_state::leds_w));
	map(0xc000, 0xc000).mirror(0x3fff).w(FUNC(chess2001_state::speaker_w));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( chess2001 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Black")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("King")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Queen")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Bishop")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Knight")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Pawn")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("White")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Set up")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("New Game")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Take Back")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Forward")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Hint")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Move")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Level")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Sound")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void chess2001_state::chess2001(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 8_MHz_XTAL/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &chess2001_state::main_map);

	auto &irq_clock(CLOCK(config, "irq_clock", 568)); // 555 timer (20nF, 100K+33K, 1K2), measured 568Hz
	irq_clock.set_pulse_width(attotime::from_nsec(16600)); // active for 16.6us
	irq_clock.signal_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 8);
	config.set_default_layout(layout_cxg_chess2001);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( ch2001 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("chess2001.bin", 0x0000, 0x4000, CRC(b3485c73) SHA1(f405c6f67fe70edf45dcc383a4049ee6bad387a9) ) // D27128D, no label
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY, FULLNAME, FLAGS
SYST( 1984, ch2001, 0,      0,      chess2001, chess2001, chess2001_state, empty_init, "CXG Systems / Newcrest Technology / Intelligent Software", "Chess 2001", MACHINE_SUPPORTS_SAVE )



chessac.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle, Berger
/*******************************************************************************

Saitek Kasparov Chess Academy / Mephisto Schachakademie (both were later rebranded
to Mephisto Talking Chess Academy)

The chess engine is by Frans Morsch, similar to the one in GK 2000. Other features,
such as the speech and tutorial lessons, were supposedly added by Craig Barnes.

Hardware notes:
- PCB label: SCH RT33-PE-041 Rev 3.0
- Hitachi H8/3214 MCU, 16MHz XTAL
- same LCD as GK 2000
- OKI MSM6588 ADPCM Recorder @ 4MHz, small daughterboard with 4MB ROM under epoxy
- 8*8 LEDs, button sensors chessboard

The German version has 2 epoxy blobs (4MB and 2MB) on the daughterboard.

TODO:
- it does a cold boot at every reset, so nvram won't work properly unless MAME
  adds some kind of auxillary autosave state feature at power-off
- does a French speech version exist?

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

#include "emu.h"

#include "cpu/h8/h83217.h"
#include "machine/sensorboard.h"
#include "sound/okim6588.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "mephisto_schachak.lh"
#include "saitek_chessac.lh"


namespace {

class chessac_state : public driver_device
{
public:
	chessac_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_adpcm_rom(*this, "adpcm"),
		m_led_pwm(*this, "led_pwm"),
		m_lcd_pwm(*this, "lcd_pwm"),
		m_okim6588(*this, "okim6588"),
		m_inputs(*this, "IN.%u", 0),
		m_out_lcd(*this, "s%u.%u", 0U, 0U)
	{ }

	void chessac(machine_config &config);
	void schachak(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(go_button);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<h83214_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_region_ptr<u8> m_adpcm_rom;
	required_device<pwm_display_device> m_led_pwm;
	required_device<pwm_display_device> m_lcd_pwm;
	required_device<okim6588_device> m_okim6588;
	required_ioport_array<3> m_inputs;
	output_finder<2, 24> m_out_lcd;

	u16 m_inp_mux = 0;
	u32 m_lcd_segs = 0;
	u8 m_lcd_com = 0;
	u8 m_led_select = 0;
	u8 m_led_data = 0;
	u32 m_adpcm_address = 0;

	u8 m_port3 = 0xff;
	u8 m_port5 = 0xff;
	u8 m_port7 = 0xff;

	// I/O handlers
	void lcd_pwm_w(offs_t offset, u8 data);
	void update_lcd();
	template <int N> void lcd_segs_w(u8 data);

	void standby(int state);

	void update_leds();
	void update_adpcm_address();

	void p3_w(u8 data);
	u8 p4_r();
	void p5_w(u8 data);
	void p6_w(u8 data);
	u8 p7_r();
	void p7_w(u8 data);
};

void chessac_state::machine_start()
{
	m_out_lcd.resolve();

	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_lcd_segs));
	save_item(NAME(m_lcd_com));
	save_item(NAME(m_led_select));
	save_item(NAME(m_led_data));
	save_item(NAME(m_adpcm_address));
	save_item(NAME(m_port3));
	save_item(NAME(m_port5));
	save_item(NAME(m_port7));
}



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

// power

void chessac_state::standby(int state)
{
	// clear display
	if (state)
	{
		m_lcd_pwm->clear();
		m_led_pwm->clear();
	}
}

INPUT_CHANGED_MEMBER(chessac_state::go_button)
{
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, newval ? ASSERT_LINE : CLEAR_LINE);
}


// LCD

void chessac_state::lcd_pwm_w(offs_t offset, u8 data)
{
	m_out_lcd[offset & 0x3f][offset >> 6] = data;
}

void chessac_state::update_lcd()
{
	// LCD latch from P1x
	if (m_port5 & 1)
		m_lcd_segs = (m_lcd_segs << 8 & 0xff0000) | (m_lcd_segs & 0xffff);

	u32 lcd_segs = bitswap<24>(m_lcd_segs,11,18,19,20,21,12,13,14,15,22,23,0,1,2,3,4,5,6,7,16,17,8,9,10);

	for (int i = 0; i < 2; i++)
	{
		// LCD common is analog (voltage level)
		const u8 com = population_count_32(m_lcd_com >> (i * 2) & 3);
		const u32 data = (com == 0) ? lcd_segs : (com == 2) ? ~lcd_segs : 0;
		m_lcd_pwm->write_row(i, data);
	}
}

template <int N>
void chessac_state::lcd_segs_w(u8 data)
{
	// P1x, P2x: LCD segments
	const u8 shift = 8 * N;
	m_lcd_segs = (m_lcd_segs & ~(0xff << shift)) | (data << shift);
	update_lcd();
}


// misc

void chessac_state::update_leds()
{
	if (m_port5 & 0x10)
		m_led_select = ~m_port7;
	if (m_port5 & 0x20)
		m_led_data = m_port7;

	m_led_pwm->matrix(m_led_select, m_led_data);
}

void chessac_state::update_adpcm_address()
{
	for (int i = 0; i < 3; i++)
		if (BIT(m_port3, i))
		{
			const u8 shift = 8 * i;
			m_adpcm_address = (m_adpcm_address & ~(0xff << shift)) | (m_port7 << shift);
		}
}

void chessac_state::p3_w(u8 data)
{
	// P37: MSM6588 RESET
	if (m_port3 & 0x80 && ~data & 0x80)
		m_okim6588->reset();

	// P34: MSM6588 RD
	// P35: MSM6588 WR
	// P36: MSM6588 CE
	if ((data & 0xc0) == 0x80 && m_port3 & 0x20 && ~data & 0x20)
		m_okim6588->data_w(m_port7);

	// P33: ADPCM ROM CE
	// P30-P32: enable ADPCM ROM address latches
	m_port3 = data;
	update_adpcm_address();
}

u8 chessac_state::p4_r()
{
	// P40-P47: multiplexed inputs
	u8 data = 0;

	// read buttons
	for (int i = 0; i < 2; i++)
		if (BIT(m_inp_mux, i + 8))
			data |= m_inputs[i]->read();

	// read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_file(i, true);

	return ~data;
}

void chessac_state::p5_w(u8 data)
{
	// P50: enable LCD latch
	m_port5 = data;
	update_lcd();

	// P51: ext power (no need to emulate it)
	// P52,P53: N/C

	// P54,P55: enable LED latches
	update_leds();
}

void chessac_state::p6_w(u8 data)
{
	// P60-P63: LCD common
	m_lcd_com = data & 0xf;
	update_lcd();

	// P65,P66: input mux (buttons)
	m_inp_mux = (m_inp_mux & 0xff) | (~data << 3 & 0x300);
}

u8 chessac_state::p7_r()
{
	u8 data = 0xff;

	// P70-P77: read ADPCM ROM
	if (~m_port3 & 8)
		data &= m_adpcm_rom[m_adpcm_address & (m_adpcm_rom.bytes() - 1)];

	// P70-P73: read MSM6588 status
	if ((m_port3 & 0xf0) == 0xa0)
		data &= (m_okim6588->data_r() | 0xf0);

	return data;
}

void chessac_state::p7_w(u8 data)
{
	// P70-P77: input mux (chessboard)
	m_inp_mux = (m_inp_mux & 0x300) | (data ^ 0xff);

	// also data for LED latches, ADPCM address latches, and MSM6588
	m_port7 = data;
	update_leds();
	update_adpcm_address();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( chessac )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_CODE(KEYCODE_F1) PORT_NAME("Yes") // combine for NEW GAME
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_CODE(KEYCODE_F1) PORT_NAME("No")  // "
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("Position")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Hint / Info")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_RIGHT) PORT_CODE(KEYCODE_B) PORT_NAME("Fwd / Black")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Option")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Tutorial")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Say Again")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_W) PORT_NAME("Back / White")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")

	PORT_START("IN.2")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(chessac_state::go_button), 0) PORT_NAME("Go / Stop")
	PORT_BIT(0xef, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void chessac_state::chessac(machine_config &config)
{
	// basic machine hardware
	H83214(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(h83214_device::nvram_set_battery));
	m_maincpu->standby_cb().append(FUNC(chessac_state::standby));
	m_maincpu->write_port1().set(FUNC(chessac_state::lcd_segs_w<1>));
	m_maincpu->write_port2().set(FUNC(chessac_state::lcd_segs_w<0>));
	m_maincpu->write_port3().set(FUNC(chessac_state::p3_w));
	m_maincpu->read_port4().set(FUNC(chessac_state::p4_r));
	m_maincpu->write_port5().set(FUNC(chessac_state::p5_w));
	m_maincpu->read_port6().set_ioport("IN.2").invert();
	m_maincpu->write_port6().set(FUNC(chessac_state::p6_w));
	m_maincpu->read_port7().set(FUNC(chessac_state::p7_r));
	m_maincpu->write_port7().set(FUNC(chessac_state::p7_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	//m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_lcd_pwm).set_size(2, 24);
	m_lcd_pwm->output_x().set(FUNC(chessac_state::lcd_pwm_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/5, 804/5);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_led_pwm).set_size(8, 8);
	config.set_default_layout(layout_saitek_chessac);

	// sound hardware
	SPEAKER(config, "speaker").front_center();

	OKIM6588(config, m_okim6588, 4_MHz_XTAL).add_route(ALL_OUTPUTS, "speaker", 0.5);
	m_okim6588->write_mon().set_inputline(m_maincpu, INPUT_LINE_NMI).invert();
	m_okim6588->set_mcum_pin(1);
}

void chessac_state::schachak(machine_config &config)
{
	chessac(config);
	config.set_default_layout(layout_mephisto_schachak);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( chessac )
	ROM_REGION16_BE( 0x8000, "maincpu", 0 )
	ROM_LOAD("97_saitek_86165400831_hd6433214a08f.u1", 0x0000, 0x8000, CRC(29d06d6a) SHA1(08b6f4093b240b0a34d9da67c9acffc576ba1d2d) )

	ROM_REGION( 0x400000, "adpcm", 0 )
	ROM_LOAD("adpcm.u10", 0x000000, 0x400000, CRC(73d9650c) SHA1(ecf3bd72fc954528fa72f64eac91e225d11150c6) ) // no label

	ROM_REGION( 68501, "screen", 0 )
	ROM_LOAD("gk2000.svg", 0, 68501, CRC(80554c49) SHA1(88f06ec8f403eaaf7cbce4cc84807b5742ce7108) )
ROM_END

ROM_START( schachak )
	ROM_REGION16_BE( 0x8000, "maincpu", 0 )
	ROM_LOAD("97_saitek_86165400831_hd6433214a08f.u1", 0x0000, 0x8000, CRC(29d06d6a) SHA1(08b6f4093b240b0a34d9da67c9acffc576ba1d2d) )

	ROM_REGION( 0x800000, "adpcm", 0 )
	ROM_LOAD("adpcm.u2", 0x000000, 0x400000, CRC(21608a97) SHA1(42f16cab961f1c53a649a7d630bb96304208e850) ) // no label
	ROM_LOAD("adpcm.u1", 0x400000, 0x200000, CRC(03ed8eaf) SHA1(44c6de8414b943044dcb8c20e43b40701d1ebc85) ) // "
	ROM_RELOAD(          0x600000, 0x200000 )

	ROM_REGION( 68501, "screen", 0 )
	ROM_LOAD("gk2000.svg", 0, 68501, CRC(80554c49) SHA1(88f06ec8f403eaaf7cbce4cc84807b5742ce7108) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT    CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1997, chessac,  0,       0,      chessac,  chessac, chessac_state, empty_init, "Saitek", "Kasparov Chess Academy", MACHINE_SUPPORTS_SAVE )
SYST( 1997, schachak, chessac, 0,      schachak, chessac, chessac_state, empty_init, "Saitek", "Mephisto Schachakademie", MACHINE_SUPPORTS_SAVE )



chessking.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood, hap
/*

Chess King (棋王之王), LCD handheld console presumably from Taiwan.
Hold down U+D+L+R buttons at boot to enter factory reset mode.
Hold down START at boot to enter test mode.

TODO:
- lots of unknown writes
- dump/add more cartridges? considering how unknown the handheld is, maybe only a handful were released
- LCD chip(s) is not emulated, maybe the I/O chip does a DMA from RAM to the LCD?
- chess game is buggy, assume that's just the way it is, aka BTANB
  eg. sometimes it makes an illegal move, or castling doesn't erase the king from its original spot

Hardware notes:

Main:
- PCB label: TD-24
- NEC D70108HG-10 V20, 9.600MHz XTAL
- 64KB RAM (2*SRM20256), 256KB ROM (custom label)
- unknown 80 pin QFP, label CCH01 ET-MATE F3X0 713, assume custom I/O chip
- 1-bit sound
- cartridge slot

LCD module:
- PCB label: P102-2, DATA VISION
- 2*Hitachi HD66204F, Hitachi HD66205F
- unknown 80 pin QFP, label 16160 S2RB 94.10
- 2bpp 160*160 LCD screen

*/

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/nec/nec.h"
#include "machine/nvram.h"
#include "sound/beep.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class chessking_state : public driver_device
{
public:
	chessking_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen"),
		m_mainrom(*this, "maincpu"),
		m_mainram(*this, "mainram"),
		m_videoram(*this, "videoram"),
		m_beeper(*this, "beeper"),
		m_cart(*this, "cartslot")
	{ }

	void chesskng(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_region_ptr<uint8_t> m_mainrom;
	required_shared_ptr<uint8_t> m_mainram;
	required_shared_ptr<uint8_t> m_videoram;
	required_device<beep_device> m_beeper;
	required_device<generic_slot_device> m_cart;

	uint8_t m_3f_data = 0;
	uint8_t m_cart_bank = 0;
	uint16_t m_beeper_freq = 0;

	void chesskng_map(address_map &map) ATTR_COLD;
	void chesskng_io(address_map &map) ATTR_COLD;

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
	uint8_t cartridge_r(offs_t offset);
	void cart_bank_w(uint8_t data);

	void update_beeper();
	void beeper_freq_low_w(uint8_t data);
	void beeper_freq_high_w(uint8_t data);
	void beeper_enable_w(uint8_t data);

	INTERRUPT_GEN_MEMBER(interrupt);
	void irq_clear_w(uint8_t data);
	void unk_1f_w(uint8_t data);
	void unk_2f_w(uint8_t data);
	uint8_t unk_3f_r();
	void unk_3f_w(uint8_t data);
	void unk_5f_w(uint8_t data);
	void unk_6f_w(uint8_t data);

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};

void chessking_state::machine_start()
{
	save_item(NAME(m_3f_data));
	save_item(NAME(m_cart_bank));
	save_item(NAME(m_beeper_freq));
}



/*******************************************************************************
    Video
*******************************************************************************/

uint32_t chessking_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	// quickly draw from memory (should be handled by LCDC?)
	for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
	{
		int offset = y * 256 / 8;

		uint32_t *dst = &bitmap.pix(y);
		for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
		{
			// 2 256x256 images (160x160 area used) one at c000, one at e000 to form 2bpp graphics
			uint8_t data = m_videoram[0x4000 + offset + x/8];
			uint8_t data2 = m_videoram[0x6000 + offset + x/8];
			uint8_t pix = BIT(data, ~x & 7) | BIT(data2, ~x & 7) << 1;

			static const rgb_t pens[4] = { rgb_t::white(), rgb_t(0x55,0x55,0x55), rgb_t(0xaa,0xaa,0xaa), rgb_t::black() };
			dst[x] = pens[pix];
		}
	}

	return 0;
}



/*******************************************************************************
    Sound
*******************************************************************************/

void chessking_state::beeper_freq_low_w(uint8_t data)
{
	//logerror("%s: 8f write %02x\n", machine().describe_context(), data);
	m_beeper_freq = (m_beeper_freq & 0xff00) | data;
	update_beeper();
}

void chessking_state::beeper_freq_high_w(uint8_t data)
{
	//logerror("%s: 9f write %02x\n", machine().describe_context(), data);
	m_beeper_freq = (m_beeper_freq & 0x00ff) | data << 8;
	update_beeper();
}

void chessking_state::beeper_enable_w(uint8_t data)
{
	// writes same value here after writing to 0x3f, assume sound related since
	// the register is in proximity of sound freq registers
	m_beeper->set_state(BIT(data, 3));
}

void chessking_state::update_beeper()
{
	uint16_t freq = (~m_beeper_freq & 0x1ff) + 1;
	double base = (9.6_MHz_XTAL).dvalue() / 0x80;
	m_beeper->set_clock(base / freq);
}



/*******************************************************************************
    Cartridge
*******************************************************************************/

DEVICE_IMAGE_LOAD_MEMBER(chessking_state::cart_load)
{
	uint32_t size = m_cart->common_get_size("rom");
	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

uint8_t chessking_state::cartridge_r(offs_t offset)
{
	// bank 1 selects main rom
	if (m_cart_bank == 1)
		return m_mainrom[offset & 0x3ffff];

	// banks 4-7 go to cartridge
	else if (m_cart_bank >= 4)
		return m_cart->read_rom(offset | (m_cart_bank & 3) << 19);

	// other banks: maybe cartridge too?
	return 0;
}

void chessking_state::cart_bank_w(uint8_t data)
{
	m_cart_bank = data & 0x7;
}



/*******************************************************************************
    Misc I/O
*******************************************************************************/

INTERRUPT_GEN_MEMBER(chessking_state::interrupt)
{
	device.execute().set_input_line_and_vector(0, ASSERT_LINE, 0x20/4);
}

void chessking_state::irq_clear_w(uint8_t data)
{
	// writes here at the start of irq routine
	m_maincpu->set_input_line(0, CLEAR_LINE);
}

void chessking_state::unk_1f_w(uint8_t data)
{
	// writes at start-up only
	logerror("%s: 1f write %02x\n", machine().describe_context(), data);
}

void chessking_state::unk_2f_w(uint8_t data)
{
	// writes at start-up only
	logerror("%s: 2f write %02x\n", machine().describe_context(), data);
}

uint8_t chessking_state::unk_3f_r()
{
	return m_3f_data;
}

void chessking_state::unk_3f_w(uint8_t data)
{
	// writes frequently, at the same times as 7f/8f/9f/af writes, note address is also read
	//logerror("%s: 3f write %02x\n", machine().describe_context(), data);
	m_3f_data = data;
}

void chessking_state::unk_5f_w(uint8_t data)
{
	// writes at start-up only
	logerror("%s: 5f write %02x\n", machine().describe_context(), data);
}

void chessking_state::unk_6f_w(uint8_t data)
{
	logerror("%s: 6f write %02x\n", machine().describe_context(), data);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void chessking_state::chesskng_map(address_map &map)
{
	map(0x00000, 0x7ffff).mirror(0x80000).r(FUNC(chessking_state::cartridge_r));
	map(0x00000, 0x1ffff).unmapr();

	map(0x00000, 0x07fff).ram().share(m_mainram); // SRM20256, battery-backed
	map(0x08000, 0x0ffff).ram().share(m_videoram); // SRM20256

	map(0xe0000, 0xfffff).rom().region("maincpu", 0x20000);
}

void chessking_state::chesskng_io(address_map &map)
{
	map(0x0f, 0x0f).portr("BUTTONS");
	map(0x1f, 0x1f).w(FUNC(chessking_state::unk_1f_w));
	map(0x2f, 0x2f).w(FUNC(chessking_state::unk_2f_w));
	map(0x3f, 0x3f).rw(FUNC(chessking_state::unk_3f_r), FUNC(chessking_state::unk_3f_w));
	map(0x4f, 0x4f).w(FUNC(chessking_state::irq_clear_w));
	map(0x5f, 0x5f).w(FUNC(chessking_state::unk_5f_w));
	map(0x6f, 0x6f).w(FUNC(chessking_state::unk_6f_w));
	map(0x7f, 0x7f).w(FUNC(chessking_state::cart_bank_w));
	map(0x8f, 0x8f).w(FUNC(chessking_state::beeper_freq_low_w));
	map(0x9f, 0x9f).w(FUNC(chessking_state::beeper_freq_high_w));
	map(0xaf, 0xaf).w(FUNC(chessking_state::beeper_enable_w));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( chesskng )
	PORT_START("BUTTONS")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_SELECT ) // SELECT / 選擇
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_START ) // START / 啓動
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON2 ) // A / 確認 (confirm)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1 ) // B / 回位 (return)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_16WAY
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void chessking_state::chesskng(machine_config &config)
{
	// Basic machine hardware
	V20(config, m_maincpu, 9.6_MHz_XTAL); // D70108HG-10 V20
	m_maincpu->set_addrmap(AS_PROGRAM, &chessking_state::chesskng_map);
	m_maincpu->set_addrmap(AS_IO, &chessking_state::chesskng_io);

	attotime irq_freq = attotime::from_hz(9.6_MHz_XTAL / 0x10000); // gives approximate seconds on timer
	m_maincpu->set_periodic_int(FUNC(chessking_state::interrupt), irq_freq);

	NVRAM(config, "mainram", nvram_device::DEFAULT_ALL_0);

	// Video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(160, 160);
	m_screen->set_visarea_full();
	m_screen->set_screen_update(FUNC(chessking_state::screen_update));

	// Sound hardware
	SPEAKER(config, "mono").front_center();

	BEEP(config, m_beeper, 0);
	m_beeper->add_route(ALL_OUTPUTS, "mono", 0.25);

	// Cartridge
	GENERIC_CARTSLOT(config, m_cart, generic_linear_slot, "chessking_cart");
	m_cart->set_device_load(FUNC(chessking_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("chessking_cart");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( chesskng )
	ROM_REGION( 0x40000, "maincpu", 0 )
	ROM_LOAD( "etmate-cch.u6", 0x00000, 0x40000, CRC(a4d1764b) SHA1(ccfae1e985f6ad316ff192206fbc0f8bcd4e44d5) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS            INIT        COMPANY             FULLNAME                   FLAGS
SYST( 1994, chesskng, 0,      0,      chesskng, chesskng, chessking_state, empty_init, "I-Star Co., Ltd.", "Chess King (model ET-6)", MACHINE_SUPPORTS_SAVE )



chessmate.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Peter Trauner, hap
/*******************************************************************************

Commodore Chessmate (stylized as CHESSmate) / Novag Chess Champion MK II
Initial driver version by PeT mess@utanet.at September 2000.
Driver mostly rewritten later.

The hardware is pretty similar to KIM-1. In fact, the chess engine is an improved
version of Peter R. Jennings's Microchess, originally made for the KIM-1. Jennings
went on to co-found Personal Software (later named VisiCorp, known for VisiCalc).

It was planned to be called "Bobby", pending Bobby Fischer's approval. Although
the program was stronger than most other chess computers at the time, Fischer
declined the idea after trying out the prototype.

Commodore also licensed the Chessmate program to Novag, and they released it as
Chess Champion: MK II. The hardware is almost identical and the software is the
same (identical ROM labels). Two designs were made, one jukebox shape, and one
brick shape. The one in MAME came from the jukebox, but both have the same ROMs.

Note that like MK I, although it is a Winkler/Auge production, it doesn't involve
SciSys company. SciSys was founded by Winkler after MK II.

TEC Schachcomputer from 1981 is also assumed to have the same ROMs.

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

Hardware notes:

MOS MPS 6504 2179
MOS MPS 6530 024 1879
 layout of 6530 dumped with my adapter
 0x1300-0x133f I/O
 0x1380-0x13bf RAM
 0x1400-0x17ff ROM

2*MPS6111 RAM (256x4)
MOS MPS 6332 005 2179
Older version has 2 2KB ROMs (identical ROM contents)

74145 bcd to decimal encoder
4*7-segment LED display
4 single LEDs
19 buttons (11 on brick model)

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

#include "emu.h"
#include "cpu/m6502/m6504.h"
#include "machine/mos6530.h"
#include "sound/dac.h"
#include "video/pwm.h"
#include "speaker.h"

// internal artwork
#include "chessmate.lh"
#include "novag_mk2.lh"
#include "novag_mk2a.lh"


namespace {

class chmate_state : public driver_device
{
public:
	chmate_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_miot(*this, "miot"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// machine configs
	void chmate(machine_config &config);
	void mk2(machine_config &config);
	void mk2a(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(reset_button);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<mos6530_device> m_miot;
	required_device<pwm_display_device> m_display;
	required_device<dac_2bit_ones_complement_device> m_dac;
	optional_ioport_array<5> m_inputs;

	u8 m_inp_mux = 0;
	u8 m_7seg_data = 0;
	u8 m_led_data = 0;

	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_display();
	u8 input_r();
	void digit_w(u8 data);
	void control_w(u8 data);
};

void chmate_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_7seg_data));
	save_item(NAME(m_led_data));
}

INPUT_CHANGED_MEMBER(chmate_state::reset_button)
{
	// NEW GAME button is tied to reset pin(s)
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
	if (newval)
		m_miot->reset();
}



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

// 6530 ports

void chmate_state::update_display()
{
	m_display->matrix_partial(0, 4, 1 << m_inp_mux, m_7seg_data);
	m_display->write_row(4, m_led_data | (m_7seg_data >> 5 & 4));
}

u8 chmate_state::input_r()
{
	u8 data = 0;

	// PA0-PA6: multiplexed inputs (74145 Q4,Q5)
	if (m_inp_mux == 4 || m_inp_mux == 5)
	{
		// note that number/letter buttons are electronically the same
		u8 i = m_inp_mux - 4;
		data = m_inputs[i]->read() | m_inputs[i | 2].read_safe(0);
	}

	return ~data;
}

void chmate_state::digit_w(u8 data)
{
	// PA0-PA6: digit segment data
	// PA7: lose led
	m_7seg_data = data;
	update_display();
}

void chmate_state::control_w(u8 data)
{
	// PB0-PB2: 74145 to input mux/digit select
	m_inp_mux = data & 7;

	// 74145 Q6,Q7: speaker out
	bool sound_on = !m_inputs[4].read_safe(0);
	m_dac->write(sound_on ? ((1 << m_inp_mux) >> 6) : 0);

	// PB3,PB4: leds (direct)
	m_led_data = data >> 3 & 3;
	update_display();
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void chmate_state::main_map(address_map &map)
{
	map.global_mask(0x1fff);
	map(0x0000, 0x00ff).mirror(0x0100).ram();
	map(0x0b00, 0x0b0f).mirror(0x0030).m(m_miot, FUNC(mos6530_device::io_map));
	map(0x0b80, 0x0bbf).m(m_miot, FUNC(mos6530_device::ram_map));
	map(0x0c00, 0x0fff).m(m_miot, FUNC(mos6530_device::rom_map));
	map(0x1000, 0x1fff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( chmate )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("F / Skill Level")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("E / Stop Clock")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("D / Display Time")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("C / Chess Clock")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("B / Board Verify")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("A / White")

	PORT_START("IN.1")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Clear")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("H / Black")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("G / Game Moves")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")

	PORT_START("IN.3")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(chmate_state::reset_button), 0) PORT_NAME("New Game")
INPUT_PORTS_END

static INPUT_PORTS_START( mk2a ) // meaning of black/white reversed
	PORT_INCLUDE( chmate )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("F / Level")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("A / Black")

	PORT_MODIFY("IN.1")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("H / White")
INPUT_PORTS_END

static INPUT_PORTS_START( mk2 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_F) PORT_NAME("6 / F / Level")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_E) PORT_NAME("5 / E / Stop Clock / Rook")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_D) PORT_NAME("4 / D / Display Time")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_C) PORT_NAME("3 / C / Chess Clock / Bishop")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_B) PORT_NAME("2 / B / Board Verify / Knight")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_A) PORT_NAME("1 / A / White / Pawn")

	PORT_START("IN.1")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Clear")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_H) PORT_NAME("8 / H / Black / Queen")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_G) PORT_NAME("7 / G / Game Moves")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_S) PORT_TOGGLE PORT_NAME("Sound Switch")

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(chmate_state::reset_button), 0) PORT_NAME("New Game")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void chmate_state::chmate(machine_config &config)
{
	// basic machine hardware
	M6504(config, m_maincpu, 8_MHz_XTAL/8);
	m_maincpu->set_addrmap(AS_PROGRAM, &chmate_state::main_map);

	MOS6530(config, m_miot, 8_MHz_XTAL/8);
	m_miot->pa_rd_callback().set(FUNC(chmate_state::input_r));
	m_miot->pa_wr_callback().set(FUNC(chmate_state::digit_w));
	m_miot->pb_wr_callback().set(FUNC(chmate_state::control_w));
	m_miot->irq_wr_callback().set_inputline(m_maincpu, 0);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4+1, 7);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_chessmate);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_2BIT_ONES_COMPLEMENT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.125);
}

void chmate_state::mk2(machine_config &config)
{
	chmate(config);
	config.set_default_layout(layout_novag_mk2);
}

void chmate_state::mk2a(machine_config &config)
{
	chmate(config);
	config.set_default_layout(layout_novag_mk2a);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( chmate )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD("6332_005", 0x1000, 0x1000, CRC(6f10991b) SHA1(90cdc5a15d9ad813ad20410f21081c6e3e481812) )

	ROM_REGION( 0x400, "miot", 0 )
	ROM_LOAD("6530_024", 0x0000, 0x0400, CRC(4f28c443) SHA1(e33f8b7f38e54d7a6e0f0763f2328cc12cb0eade) )
ROM_END

#define rom_ccmk2 rom_chmate
#define rom_ccmk2a rom_chmate

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1978, chmate, 0,      0,      chmate,  chmate, chmate_state, empty_init, "Commodore", "Chessmate", MACHINE_SUPPORTS_SAVE )

SYST( 1979, ccmk2,  chmate, 0,      mk2,     mk2,    chmate_state, empty_init, "Novag Industries / Commodore", "Chess Champion: MK II (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1979, ccmk2a, chmate, 0,      mk2a,    mk2a,   chmate_state, empty_init, "Novag Industries / Commodore", "Chess Champion: MK II (set 2)", MACHINE_SUPPORTS_SAVE ) // 1st version (jukebox model), aka version B



chessmst.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco, hap
/*******************************************************************************

VEB Mikroelektronik "Karl Marx" Erfurt Chess-Master (aka Schachcomputer CM)
- Chess-Master (G-5003-500) (10*U505 roms)
- Chess-Master (G-5003-501) (2 roms set)

Unlike SC2, the chess engine was not copied from an existing one. It is an
original creation by Rüdiger Worbs and Dieter Schultze. It competed in
Budapest WMCCC 1983 and ended at a low 16th place.

Hardware notes:
- PCB label: 15003-500-2902 B 01 02 03
- UB880 Z80 @ ~2.5MHz
- 2*Z80 PIO
- 10KB ROM (10*U505D), 2KB RAM (4*U214D)
- chessboard with 64 hall sensors, 64+15 leds, piezo

Chess-Master Schachtisch embedded in a wooden table is on the same hardware
and has the same ROMs, they manually changed the PCB label from 15003 to 15005.
A newer version of Chess-Master had a 4MHz UA880 and 2 ROM chips (8KB + 2KB).

BTANB:
- corner leds flicker sometimes

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

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/z80pio.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "chessmst.lh"


namespace {

class chessmst_state : public driver_device
{
public:
	chessmst_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_pio(*this, "z80pio%u", 0),
		m_board(*this, "board"),
		m_led_pwm(*this, "led_pwm"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(halt_button);
	DECLARE_INPUT_CHANGED_MEMBER(reset_button);

	void chessmst(machine_config &config);
	void chessmsta(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;
	required_device_array<z80pio_device, 2> m_pio;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_led_pwm;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<2> m_inputs;

	u16 m_matrix = 0;
	u8 m_led_data[2] = { 0, 0 };

	void chessmst_io(address_map &map) ATTR_COLD;
	void chessmst_mem(address_map &map) ATTR_COLD;

	void pio1_port_a_w(u8 data);
	void pio1_port_b_w(u8 data);
	u8 pio2_port_a_r();
	void pio2_port_b_w(u8 data);

	void update_leds();
};

void chessmst_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_matrix));
	save_item(NAME(m_led_data));
}



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

void chessmst_state::update_leds()
{
	m_led_pwm->matrix(m_matrix, m_led_data[0] | m_led_data[1]);
}

void chessmst_state::pio1_port_a_w(u8 data)
{
	// d0-d7: led data
	m_led_data[0] = ~data;
	update_leds();
}

void chessmst_state::pio1_port_b_w(u8 data)
{
	// d0,d1: input mux/led select high
	m_matrix = (m_matrix & 0xff) | ((data & 0x03) << 8);

	// d2,d3: led data 2nd/3rd rows (duplicate)
	m_led_data[1] = ~data >> 1 & 6;
	update_leds();

	// d6: speaker out
	m_dac->write(BIT(data, 6));
}

u8 chessmst_state::pio2_port_a_r()
{
	u8 data = 0xff;

	// read chessboard sensors
	for (int i = 0; i < 8; i++)
		if (BIT(m_matrix, i))
			data &= ~m_board->read_file(i);

	// read other buttons
	if (m_matrix & 0x100)
		data &= m_inputs[0]->read();

	return data;
}

void chessmst_state::pio2_port_b_w(u8 data)
{
	// d0-d7: input mux/led select
	m_matrix = (data & 0xff) | (m_matrix & ~0xff);
	update_leds();
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void chessmst_state::chessmst_mem(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x7fff); // A15 not connected
	map(0x0000, 0x27ff).rom();
	map(0x3400, 0x3bff).ram();
}

void chessmst_state::chessmst_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x0f);
	//map(0x00, 0x03) read/write to both PIOs, but not used by software
	map(0x04, 0x07).rw(m_pio[0], FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x08, 0x0b).rw(m_pio[1], FUNC(z80pio_device::read), FUNC(z80pio_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

INPUT_CHANGED_MEMBER(chessmst_state::halt_button)
{
	// halt button goes to PIO(0) ASTB pin
	m_pio[0]->strobe_a(newval);
	reset_button(field, param, oldval, newval);
}

INPUT_CHANGED_MEMBER(chessmst_state::reset_button)
{
	// pressing both halt+reset buttons causes a reset
	const bool reset = (m_inputs[1]->read() & 0x03) == 0x03;
	m_maincpu->set_input_line(INPUT_LINE_RESET, reset ? ASSERT_LINE : CLEAR_LINE);
}

static INPUT_PORTS_START( chessmst )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Hint / 7")              PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_H)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Random / 6")            PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_R)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Referee / 5 / King")    PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_F)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Self Play / 4 / Queen") PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_S)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Board / 3 / Rook")      PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_B)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Color / 2 / Bishop")    PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_C)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Level / 1 / Knight")    PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_L)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("New Game / 0 / Pawn")   PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_N)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Halt")  PORT_CODE(KEYCODE_F2) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(chessmst_state::halt_button), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Reset") PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(chessmst_state::reset_button), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

static const z80_daisy_config chessmst_daisy_chain[] =
{
	{ "z80pio0" },
	{ nullptr }
};

void chessmst_state::chessmst(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 8_MHz_XTAL / 2); // UA880
	m_maincpu->set_addrmap(AS_PROGRAM, &chessmst_state::chessmst_mem);
	m_maincpu->set_addrmap(AS_IO, &chessmst_state::chessmst_io);
	m_maincpu->set_daisy_config(chessmst_daisy_chain);

	Z80PIO(config, m_pio[0], 8_MHz_XTAL / 2);
	m_pio[0]->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio[0]->out_pa_callback().set(FUNC(chessmst_state::pio1_port_a_w));
	m_pio[0]->out_pb_callback().set(FUNC(chessmst_state::pio1_port_b_w));

	Z80PIO(config, m_pio[1], 8_MHz_XTAL / 2);
	m_pio[1]->in_pa_callback().set(FUNC(chessmst_state::pio2_port_a_r));
	m_pio[1]->out_pb_callback().set(FUNC(chessmst_state::pio2_port_b_w));

	SENSORBOARD(config, m_board);
	m_board->set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_led_pwm).set_size(10, 8);
	config.set_default_layout(layout_chessmst);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void chessmst_state::chessmsta(machine_config &config)
{
	chessmst(config);

	// slower UB880 CPU
	const XTAL clk = 9.8304_MHz_XTAL / 4;
	m_maincpu->set_clock(clk);
	m_pio[0]->set_clock(clk);
	m_pio[1]->set_clock(clk);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( chessmst )
	ROM_REGION( 0x2800, "maincpu", 0 )
	ROM_LOAD("bm001.d204", 0x0000, 0x2000, CRC(6be28876) SHA1(fd7d77b471e7792aef3b2b3f7ff1de4cdafc94c9) ) // U2364D45
	ROM_LOAD("bm108.d205", 0x2000, 0x0800, CRC(2599794f) SHA1(8d612d6159b29f9fc6a58d8f1e5a86d0e7c2ce75) ) // U2616D45
ROM_END

ROM_START( chessmsta )
	ROM_REGION( 0x2800, "maincpu", 0 )
	ROM_LOAD("bm056.d208", 0x0000, 0x0400, CRC(2b90e5d3) SHA1(c47445964b2e6cb11bd1f27e395cf980c97af196) ) // U505
	ROM_LOAD("bm057.d209", 0x0400, 0x0400, CRC(e666fc56) SHA1(3fa75b82cead81973bea94191a5c35f0acaaa0e6) ) // "
	ROM_LOAD("bm058.d210", 0x0800, 0x0400, CRC(6a17fbec) SHA1(019051e93a5114477c50eaa87e1ff01b02eb404d) ) // "
	ROM_LOAD("bm059.d211", 0x0c00, 0x0400, CRC(e96e3d07) SHA1(20fab75f206f842231f0414ebc473ce2a7371e7f) ) // "
	ROM_LOAD("bm060.d212", 0x1000, 0x0400, CRC(0e31f000) SHA1(daac924b79957a71a4b276bf2cef44badcbe37d3) ) // "
	ROM_LOAD("bm061.d213", 0x1400, 0x0400, CRC(69ad896d) SHA1(25d999b59d4cc74bd339032c26889af00e64df60) ) // "
	ROM_LOAD("bm062.d214", 0x1800, 0x0400, CRC(c42925fe) SHA1(c42d8d7c30a9b6d91ac994cec0cc2723f41324e9) ) // "
	ROM_LOAD("bm063.d215", 0x1c00, 0x0400, CRC(86be4cdb) SHA1(741f984c15c6841e227a8722ba30cf9e6b86d878) ) // "
	ROM_LOAD("bm064.d216", 0x2000, 0x0400, CRC(e82f5480) SHA1(38a939158052f5e6484ee3725b86e522541fe4aa) ) // "
	ROM_LOAD("bm065.d217", 0x2400, 0x0400, CRC(4ec0e92c) SHA1(0b748231a50777391b04c1778750fbb46c21bee8) ) // "
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT    COMPAT  MACHINE    INPUT     CLASS           INIT        COMPANY                                     FULLNAME                           FLAGS
SYST( 1984, chessmst,  0,        0,      chessmst,  chessmst, chessmst_state, empty_init, "VEB Mikroelektronik \"Karl Marx\" Erfurt", "Chess-Master (model G-5003-501)", MACHINE_SUPPORTS_SAVE )
SYST( 1984, chessmsta, chessmst, 0,      chessmsta, chessmst, chessmst_state, empty_init, "VEB Mikroelektronik \"Karl Marx\" Erfurt", "Chess-Master (model G-5003-500)", MACHINE_SUPPORTS_SAVE )



chessmstdm.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco, hap
/*******************************************************************************

Chess-Master Diamond (G-5004-500), by VEB Mikroelektronik "Karl Marx" Erfurt

The chess engine is a continuation of the older Chess-Master model. So it
plays quite weak when compared with other chess computers from 1987.

PM10 and PM11 modules were included (not separate purchases). No other modules
were released. The player is supposed to hotswap them with the [CH M] option.

Hardware notes:
- UA880 Z80 @ 4MHz
- 2*Z80 PIO
- 16KB ROM (2*U2364D), 3KB RAM (6*U224D)
- 4-digit 16seg display
- module slot for opening book/endgame
- chessboard with 64 hall sensors, 64+2 leds, beeper

TODO:
- hotswapping module doesn't work since MAME forces a hard reset
- the 555 only connects to BSTB pin, why is the data_b_write workaround needed?

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

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/z80/z80.h"
#include "machine/clock.h"
#include "machine/z80pio.h"
#include "machine/sensorboard.h"
#include "sound/beep.h"
#include "video/pwm.h"

#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "chessmstdm.lh"


namespace {

class chessmstdm_state : public driver_device
{
public:
	chessmstdm_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_pio(*this, "z80pio%u", 0),
		m_board(*this, "board"),
		m_led_pwm(*this, "led_pwm"),
		m_digit_pwm(*this, "digit_pwm"),
		m_beeper(*this, "beeper"),
		m_inputs(*this, "IN.%u", 0),
		m_digits(*this, "digit%u", 0U)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(monitor_button);
	DECLARE_INPUT_CHANGED_MEMBER(view_button);

	void chessmstdm(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;
	required_device_array<z80pio_device, 2> m_pio;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_led_pwm;
	required_device<pwm_display_device> m_digit_pwm;
	required_device<beep_device> m_beeper;
	required_ioport_array<2> m_inputs;
	output_finder<4> m_digits;

	u16 m_matrix = 0;
	u8 m_led_data = 0;
	u8 m_direct_leds = 0;
	u8 m_digit_matrix = 0;
	int m_digit_dot = 0;
	u16 m_digit_data = 0;

	void chessmstdm_mem(address_map &map) ATTR_COLD;
	void chessmstdm_io(address_map &map) ATTR_COLD;

	void reset_w(u8 data = 0);
	u8 reset_r();
	void digits_w(u8 data);
	void pio1_port_a_w(u8 data);
	void pio1_port_b_w(u8 data);
	u8 pio2_port_a_r();
	void pio2_port_b_w(u8 data);

	void update_leds();
	void update_digits();
};

void chessmstdm_state::machine_start()
{
	m_digits.resolve();

	// register for savestates
	save_item(NAME(m_matrix));
	save_item(NAME(m_direct_leds));
	save_item(NAME(m_led_data));
	save_item(NAME(m_digit_matrix));
	save_item(NAME(m_digit_dot));
	save_item(NAME(m_digit_data));
}

void chessmstdm_state::machine_reset()
{
	// reset digit shift registers
	m_digit_data = 0;
	update_digits();
}



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

void chessmstdm_state::reset_w(u8 data)
{
	// accessing 9cxx asserts a reset
	m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
	machine_reset();
}

u8 chessmstdm_state::reset_r()
{
	if (!machine().side_effects_disabled())
		reset_w();

	return 0xff;
}

void chessmstdm_state::update_digits()
{
	u16 digit_data = bitswap<16>(m_digit_data, 3,5,12,10,14,1,2,13,8,6,11,15,7,9,4,0);
	m_digit_pwm->matrix(m_digit_matrix, digit_data | (m_digit_dot << 16));
}

void chessmstdm_state::digits_w(u8 data)
{
	// d0-d3: shift digit segment data into 4015 shift registers
	m_digit_data = (m_digit_data << 4) | (data & 0x0f);

	// d4-d7: digit select
	m_digit_matrix = (data >> 4) & 0x0f;
	update_digits();
}

void chessmstdm_state::update_leds()
{
	m_led_pwm->matrix((m_matrix & 0xff) | (m_direct_leds << 8), m_led_data);
}

void chessmstdm_state::pio1_port_a_w(u8 data)
{
	// d0-d7: chessboard led data
	m_led_data = ~data;
	update_leds();
}

void chessmstdm_state::pio1_port_b_w(u8 data)
{
	// d2: input mux highest bit
	m_matrix = (m_matrix & 0xff) | ((data & 0x04) << 6);

	// d3: enable beeper
	m_beeper->set_state(BIT(data, 3));

	// d4: digits DP
	m_digit_dot = BIT(data, 4);
	update_digits();

	// d5: monitor led, d6: playmode led
	m_direct_leds = data >> 5 & 3;
	update_leds();

	// d4 and d7 also go to cartslot, but unused
}

u8 chessmstdm_state::pio2_port_a_r()
{
	u8 data = 0;

	// read chessboard sensors
	for (int i = 0; i < 8; i++)
		if (BIT(m_matrix, i))
			data |= m_board->read_file(i);

	// read other buttons
	if (m_matrix & 0x100)
		data |= m_inputs[0]->read();

	return ~data;
}

void chessmstdm_state::pio2_port_b_w(u8 data)
{
	// d0-d7: input mux/led select
	m_matrix = (data & 0xff) | (m_matrix & ~0xff);
	update_leds();
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void chessmstdm_state::chessmstdm_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).rom();
	map(0x4000, 0x7fff).r("cartslot", FUNC(generic_slot_device::read_rom));
	map(0x8000, 0x8bff).ram();
	map(0x9c00, 0x9c00).mirror(0x0300).rw(FUNC(chessmstdm_state::reset_r), FUNC(chessmstdm_state::reset_w));
}

void chessmstdm_state::chessmstdm_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x7f);
	//map(0x00, 0x03).mirror(0x70) read/write to both PIOs, but not used by software
	map(0x04, 0x07).mirror(0x70).rw(m_pio[0], FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x08, 0x0b).mirror(0x70).rw(m_pio[1], FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x4c, 0x4c).mirror(0x33).w(FUNC(chessmstdm_state::digits_w));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

INPUT_CHANGED_MEMBER(chessmstdm_state::monitor_button)
{
	view_button(field, param, oldval, newval);

	// releasing monitor button clears reset
	if (!newval && oldval)
		m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
}

INPUT_CHANGED_MEMBER(chessmstdm_state::view_button)
{
	// pressing both monitor+view buttons buttons causes a reset
	if ((m_inputs[1]->read() & 0x03) == 0x03)
		reset_w();
}

static INPUT_PORTS_START( chessmstdm )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Move Fore")               PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Move Back")               PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Board")                   PORT_CODE(KEYCODE_B)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Match / Time")            PORT_CODE(KEYCODE_M)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Parameter / Information") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Selection / Dialogue")    PORT_CODE(KEYCODE_S)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Function / Notation")     PORT_CODE(KEYCODE_F)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Enter")                   PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Monitor") PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(chessmstdm_state::monitor_button), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("View")    PORT_CODE(KEYCODE_F2) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(chessmstdm_state::view_button), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

static const z80_daisy_config daisy_chain[] =
{
	{ "z80pio1" },
	{ nullptr }
};

void chessmstdm_state::chessmstdm(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 8_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &chessmstdm_state::chessmstdm_mem);
	m_maincpu->set_addrmap(AS_IO, &chessmstdm_state::chessmstdm_io);
	m_maincpu->set_daisy_config(daisy_chain);

	auto &strobe(CLOCK(config, "strobe", 500)); // from 555 timer, 50% duty
	strobe.signal_handler().set(m_pio[1], FUNC(z80pio_device::strobe_b));
	strobe.signal_handler().append([this](int) { m_pio[1]->data_b_write(m_matrix); });

	Z80PIO(config, m_pio[0], 8_MHz_XTAL / 2);
	m_pio[0]->out_pa_callback().set(FUNC(chessmstdm_state::pio1_port_a_w));
	m_pio[0]->out_pb_callback().set(FUNC(chessmstdm_state::pio1_port_b_w));
	m_pio[0]->in_pb_callback().set_ioport("IN.1");

	Z80PIO(config, m_pio[1], 8_MHz_XTAL / 2);
	m_pio[1]->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio[1]->in_pa_callback().set(FUNC(chessmstdm_state::pio2_port_a_r));
	m_pio[1]->out_pb_callback().set(FUNC(chessmstdm_state::pio2_port_b_w));

	SENSORBOARD(config, m_board);
	m_board->set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_digit_pwm).set_size(4, 17);
	m_digit_pwm->set_segmask(0xf, 0x1ffff);
	m_digit_pwm->output_digit().set([this](offs_t offset, u64 data) { m_digits[offset] = data; });

	PWM_DISPLAY(config, m_led_pwm).set_size(8+2, 8);
	config.set_default_layout(layout_chessmstdm);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	BEEP(config, m_beeper, 1000).add_route(ALL_OUTPUTS, "speaker", 0.25);

	// cartridge
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "chessmstdm_cart");
	SOFTWARE_LIST(config, "cart_list").set_original("chessmstdm");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( chessmstdm )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("002.d224", 0x0000, 0x2000, CRC(bed56fef) SHA1(dad0f8ddbd9b10013a5bdcc09ee6db39cfb26b78) ) // U2364D45
	ROM_LOAD("201.d225", 0x2000, 0x2000, CRC(c9dc7f29) SHA1(a3e1b66d0e15ffe83a9165d15c4a83013852c2fe) ) // "
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME        PARENT  COMPAT  MACHINE     INPUT       CLASS             INIT        COMPANY                                     FULLNAME                FLAGS
SYST( 1987, chessmstdm, 0,      0,      chessmstdm, chessmstdm, chessmstdm_state, empty_init, "VEB Mikroelektronik \"Karl Marx\" Erfurt", "Chess-Master Diamond", MACHINE_SUPPORTS_SAVE )



chesster.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:yoyo_chessboard, Berger
/*******************************************************************************

Fidelity Chesster Challenger

These were made after Hegener + Glaser became the parent company of Fidelity
(design phase started before that). Kishon Chesster was released under both
Fidelity and Mephisto brands. Fidelity changed from Fidelity International,
Inc. to Fidelity Electronics International, Inc. after becoming a subsidiary.

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

Fidelity Chesster (model 6120)
There is also a German version titled Kishon Chesster (model 6120G, or 6127)
----------------
PCB label: 510.1141C01
Ricoh RP65C02G CPU, 5MHz XTAL
8KB RAM(UM6264-12), 32KB ROM(M27C256B)
8-bit DAC (8L513 02 resistor array) timed via IRQ, 128KB ROM(AMI custom label)
8*(8+1) buttons, 8+8+1 LEDs

I/O is via TTL, memory map is similar to Designer Display

The speech technology was invented by Forrest S. Mozer(same person that invented
the S14001A in the 70s), this time a 65C02 software solution.

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

#include "emu.h"

#include "cpu/m6502/r65c02.h"
#include "machine/clock.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "fidel_chesster.lh"


namespace {

class chesster_state : public driver_device
{
public:
	chesster_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_rombank(*this, "rombank"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.0")
	{ }

	void chesster(machine_config &config);
	void kishon(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD { m_dac->write(0x80); }

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_memory_bank m_rombank;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_8bit_r2r_device> m_dac;
	required_ioport m_inputs;

	int m_numbanks = 0;
	u8 m_speech_bank = 0;
	u8 m_select = 0;

	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void control_w(offs_t offset, u8 data);
	u8 input_r(offs_t offset);
};

void chesster_state::machine_start()
{
	// set up ROM banks (kishon's is 4 times larger)
	m_numbanks = memregion("rombank")->bytes() / 0x4000;
	m_rombank->configure_entries(0, m_numbanks, memregion("rombank")->base(), 0x4000);

	// register for savestates
	save_item(NAME(m_speech_bank));
	save_item(NAME(m_select));
}



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

void chesster_state::control_w(offs_t offset, u8 data)
{
	// a0-a2,d7: 74259(1)
	u8 mask = 1 << offset;
	m_select = (m_select & ~mask) | ((data & 0x80) ? mask : 0);

	// 74259 Q4-Q7: 7442 a0-a3
	// 7442 0-8: led data, input mux
	u16 led_data = 1 << (m_select >> 4 & 0xf) & 0x1ff;

	// 74259 Q0,Q1: led select (active low)
	m_display->matrix(~m_select & 3, led_data);

	// 74259 Q2,Q3: speechrom A14,A15
	// a0-a2,d0: 74259(2) Q3,Q2,Q0 to A16,A17,A18
	m_speech_bank = (m_speech_bank & ~mask) | ((data & 1) ? mask : 0);
	u8 bank = (m_select >> 2 & 3) | bitswap<3>(m_speech_bank, 0,2,3) << 2;
	m_rombank->set_entry(bank & (m_numbanks - 1));
}

u8 chesster_state::input_r(offs_t offset)
{
	u8 sel = m_select >> 4 & 0xf;
	u8 data = 0;

	// a0-a2,d7: multiplexed inputs (active low)
	// read chessboard sensors
	if (sel < 8)
		data = m_board->read_rank(sel ^ 7, true);

	// read button panel
	else if (sel == 8)
		data = m_inputs->read();

	return (data >> offset & 1) ? 0 : 0x80;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void chesster_state::main_map(address_map &map)
{
	map(0x0000, 0x1fff).ram();
	map(0x2000, 0x2007).mirror(0x1ff8).rw(FUNC(chesster_state::input_r), FUNC(chesster_state::control_w));
	map(0x4000, 0x7fff).bankr(m_rombank);
	map(0x6000, 0x6000).mirror(0x1fff).w(m_dac, FUNC(dac_8bit_r2r_device::data_w));
	map(0x8000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( chesster )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_NAME("Clear")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Move / No")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("Hint / Yes")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Take Back / Repeat")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Level / New")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("Option / Replay")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Verify / Problem")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Shift")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void chesster_state::chesster(machine_config &config)
{
	// basic machine hardware
	R65C02(config, m_maincpu, 5_MHz_XTAL); // RP65C02G
	m_maincpu->set_addrmap(AS_PROGRAM, &chesster_state::main_map);

	auto &irq_clock(CLOCK(config, "irq_clock", 9600)); // from 555 timer, measured (9.6kHz on a Chesster, 9.3kHz on a Kishon)
	irq_clock.set_pulse_width(attotime::from_nsec(2600)); // active for 2.6us
	irq_clock.signal_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(100));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(2, 9);
	config.set_default_layout(layout_fidel_chesster);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_8BIT_R2R(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.5);
}

void chesster_state::kishon(machine_config &config)
{
	chesster(config);

	// basic machine hardware
	m_maincpu->set_clock(3.579545_MHz_XTAL); // same CPU
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( chesster ) // model 6120, PCB label 510.1141C01
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("ch_1.3.ic9", 0x8000, 0x8000, CRC(8b42d1ad) SHA1(2161fc5ab2476fe7ca4ffc226e3cb329b8a57a01) ) // 27256, CH 1.3 on sticker

	ROM_REGION( 0x20000, "rombank", 0 )
	ROM_LOAD("101-1091b02.ic10", 0x00000, 0x20000, CRC(fa370e88) SHA1(a937c8f1ec295cf9539d12466993974e40771493) ) // AMI, 27C010 or equivalent
ROM_END

ROM_START( chesstera ) // model 6120, PCB label 510.1141C01
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("chesster.ic9", 0x8000, 0x8000, CRC(29f9a698) SHA1(4c83ca46fd5fc9c40302e9c7f16b4ae2c18b06e6) ) // M27C256B, sticker but no label

	ROM_REGION( 0x20000, "rombank", 0 )
	ROM_LOAD("101-1091a02.ic10", 0x00000, 0x20000, CRC(2b4d243c) SHA1(921e51978facb502b207b4f64a73b1e74127e826) ) // AMI, 27C010 or equivalent
ROM_END

ROM_START( kishon ) // model 6120G or 6127(same), PCB label 510.1141C01
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("gc_2.3.ic9", 0x8000, 0x8000, CRC(121c007f) SHA1(652e9ea47b6bb1632d10eb0fcd7f98cdba22fce7) ) // 27C256, GC 2.3 on sticker, also seen without label

	ROM_REGION( 0x80000, "rombank", 0 )
	ROM_LOAD("kishon_chesster_v2.6.ic10", 0x00000, 0x80000, CRC(50598869) SHA1(2087e0c2f40a2408fe217a6502c8c3a247bdd063) ) // Toshiba TC544000P-12, 1-14-91, aka 101-1094A01 on 6127
ROM_END

ROM_START( kishona ) // possibly Mephisto brand?, PCB label 510.1141C01
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("german_chesster_v2.2.ic9", 0x8000, 0x8000, CRC(43e0cfcd) SHA1(961c7335f562b19fa96324c429ab70e8ab4d7647) ) // 27C256, 15.1.91

	ROM_REGION( 0x80000, "rombank", 0 )
	ROM_LOAD("kishon_chesster_v2.6.ic10", 0x00000, 0x80000, CRC(50598869) SHA1(2087e0c2f40a2408fe217a6502c8c3a247bdd063) ) // Toshiba TC544000P-12
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT    COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1990, chesster,  0,        0,      chesster, chesster, chesster_state, empty_init, "Fidelity Electronics International", "Chesster Challenger (v1.3)", MACHINE_SUPPORTS_SAVE )
SYST( 1990, chesstera, chesster, 0,      chesster, chesster, chesster_state, empty_init, "Fidelity Electronics International", "Chesster Challenger", MACHINE_SUPPORTS_SAVE )
SYST( 1991, kishon,    chesster, 0,      kishon,   chesster, chesster_state, empty_init, "Fidelity Electronics International", "Kishon Chesster (v2.3)", MACHINE_SUPPORTS_SAVE )
SYST( 1991, kishona,   chesster, 0,      kishon,   chesster, chesster_state, empty_init, "Fidelity Electronics International", "Kishon Chesster (v2.2)", MACHINE_SUPPORTS_SAVE )



chesstrv.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco, hap
// thanks-to:Sean Riddle, Berger
/*******************************************************************************

SciSys Chess Traveler (aka Novag Pocket Chess)
SciSys Chess Intercontinental Traveler

Chess Traveler hardware notes:
- Fairchild 3870 MCU, label SL90387 (does not use the timer or irq at all)
- 256 bytes RAM(3539)
- 4-digit 7seg led panel

It was also redistributed by Acetronic as "Chess Traveller"(British spelling there),
and by Prinztronic as well, another British brand

SciSys/Novag's "Chess Champion: Pocket Chess" is assumed to be the same game,
it has the same MCU serial (SL90387). They added battery low voltage detection
to it (rightmost digit DP lights up).

Chess Intercontinental Traveler, released after the SciSys/Novag partnership splitup,
is nearly the same program. Only a few bytes different, not counting changed jump
addresses due to inserted code.

Chess Intercontinental Traveler hardware notes:
- Fairchild 3870 MCU, label SL90594, backside label 3870T-0594
- 256 bytes RAM(2*TC5501P)
- 4-digit LCD screen, the exact same one as in Mini Chess, but of the extra segments,
  only the 'low battery' and 'computing' segments are used

Regarding MCU frequency:

Chess Traveler should be around 4.5MHz. An Acetronic version was measured ~3MHz,
but that is far too slow. On Novag Pocket Chess, opening move F2F3 at level 6 is
answered after around 60 seconds.

Likewise, Intercontinental Traveler was measured ~5MHz, but in reality is much
closer to 6MHz. This was confirmed by hooking up a 6MHz XTAL, taking around
44 seconds after the same F2F3 move.

Both chesscomputers have a cheap RC circuit for the MCU clock, it seems that
electronically measuring them affected the frequency.

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

#include "emu.h"

#include "cpu/f8/f8.h"
#include "machine/f3853.h"
#include "machine/timer.h"
#include "video/pwm.h"

#include "screen.h"

// internal artwork
#include "saitek_chesstrv.lh"
#include "saitek_chesstrvi.lh"


namespace {

class chesstrv_state : public driver_device
{
public:
	chesstrv_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_comp_timer(*this, "comp_timer"),
		m_computing(*this, "computing"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void chesstrv(machine_config &config);
	void chesstrvi(machine_config &config);

	// battery status indicator is not software controlled
	DECLARE_INPUT_CHANGED_MEMBER(battery) { update_display(); }

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	optional_device<timer_device> m_comp_timer;
	output_finder<> m_computing;
	required_ioport_array<5> m_inputs;

	std::unique_ptr<u8[]> m_ram;
	u8 m_ram_address = 0;
	u8 m_inp_mux = 0;
	u8 m_7seg_data = 0;

	void chesstrv_mem(address_map &map) ATTR_COLD;
	void chesstrv_io(address_map &map) ATTR_COLD;

	TIMER_DEVICE_CALLBACK_MEMBER(computing) { m_computing = 1; }

	void update_display();
	void matrix_w(u8 data);
	void digit_w(u8 data);
	u8 input_r();

	// 256 bytes data RAM accessed via I/O ports
	u8 ram_address_r() { return m_ram_address; }
	void ram_address_w(u8 data) { m_ram_address = data; }
	u8 ram_data_r() { return m_ram[m_ram_address]; }
	void ram_data_w(u8 data) { m_ram[m_ram_address] = data; }
};

void chesstrv_state::machine_start()
{
	m_ram = make_unique_clear<u8[]>(0x100);
	m_computing.resolve();

	// register for savestates
	save_pointer(NAME(m_ram), 0x100);
	save_item(NAME(m_ram_address));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_7seg_data));
}



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

void chesstrv_state::update_display()
{
	u8 battery = m_inputs[4]->read() << 7 & 0x80;
	m_display->matrix(~m_inp_mux, m_7seg_data | battery);
}

void chesstrv_state::digit_w(u8 data)
{
	// digit segments
	m_7seg_data = bitswap<8>(data,0,1,2,3,4,5,6,7) & 0x7f;
	update_display();
}

void chesstrv_state::matrix_w(u8 data)
{
	// chesstrvi: "computing" segment goes on when LCD isn't driven
	if (m_comp_timer != nullptr && ~data & m_inp_mux & 1)
	{
		m_computing = 0;
		m_comp_timer->adjust(attotime::from_msec(100));
	}

	// d0-d3: input/digit select (active low)
	m_inp_mux = data;
	update_display();
}

u8 chesstrv_state::input_r()
{
	u8 data = m_inp_mux;

	// d0-d3: multiplexed inputs from d4-d7
	for (int i = 0; i < 4; i++)
		if (BIT(m_inp_mux, i+4))
			data |= m_inputs[i]->read();

	// d4-d7: multiplexed inputs from d0-d3
	for (int i = 0; i < 4; i++)
		if (m_inp_mux & m_inputs[i]->read())
			data |= 1 << (i+4);

	return data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void chesstrv_state::chesstrv_mem(address_map &map)
{
	map.global_mask(0x7ff);
	map(0x0000, 0x07ff).rom();
}

void chesstrv_state::chesstrv_io(address_map &map)
{
	map(0x00, 0x00).rw(FUNC(chesstrv_state::ram_address_r), FUNC(chesstrv_state::ram_address_w));
	map(0x01, 0x01).w(FUNC(chesstrv_state::digit_w));
	map(0x04, 0x07).rw("psu", FUNC(f38t56_device::read), FUNC(f38t56_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( chesstrv )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("A 1 / Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("B 2 / Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("C 3 / Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("D 4 / Rook")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("E 5 / Queen")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("F 6 / King")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("G 7 / White")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("H 8 / Black")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("LV / CS") // level/clear square
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("FP") // find position
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("EP") // enter position
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("CB") // clear board

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE") // clear entry
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("MM") // multi move
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.4")
	PORT_CONFNAME( 0x01, 0x00, "Battery Status" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(chesstrv_state::battery), 0)
	PORT_CONFSETTING(    0x01, "Low" )
	PORT_CONFSETTING(    0x00, DEF_STR( Normal ) )
INPUT_PORTS_END

static INPUT_PORTS_START( chesstrvi )
	PORT_INCLUDE( chesstrv )

	PORT_MODIFY("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level / CS")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Find Position")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("Enter Position")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")

	PORT_MODIFY("IN.3")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Multi Move")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void chesstrv_state::chesstrv(machine_config &config)
{
	// basic machine hardware
	F8(config, m_maincpu, 4'500'000/2); // approximation
	m_maincpu->set_addrmap(AS_PROGRAM, &chesstrv_state::chesstrv_mem);
	m_maincpu->set_addrmap(AS_IO, &chesstrv_state::chesstrv_io);

	f38t56_device &psu(F38T56(config, "psu", 4'500'000/2));
	psu.read_a().set(FUNC(chesstrv_state::ram_data_r));
	psu.write_a().set(FUNC(chesstrv_state::ram_data_w));
	psu.read_b().set(FUNC(chesstrv_state::input_r));
	psu.write_b().set(FUNC(chesstrv_state::matrix_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 8);
	m_display->set_segmask(0xf, 0x7f);
	m_display->set_segmask(0x1, 0xff); // DP for low battery
	config.set_default_layout(layout_saitek_chesstrv);
}

void chesstrv_state::chesstrvi(machine_config &config)
{
	chesstrv(config);

	// basic machine hardware
	m_maincpu->set_clock(6'000'000/2); // approximation
	subdevice<f38t56_device>("psu")->set_clock(6'000'000/2);

	TIMER(config, m_comp_timer).configure_generic(FUNC(chesstrv_state::computing));
	config.set_default_layout(layout_saitek_chesstrvi);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/2, 567/2);
	screen.set_visarea_full();
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( chesstrv )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD("sl90387", 0x0000, 0x0800, CRC(b76214d8) SHA1(7760903a64d9c513eb54c4787f535dabec62eb64) )
ROM_END

ROM_START( chesstrvi )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD("sl90594.u3", 0x0000, 0x0800, CRC(9162e89a) SHA1(c3f71365b73b0112aae09f11722bd78186c78408) )

	ROM_REGION( 48655, "screen", 0 )
	ROM_LOAD("chesstrvi.svg", 0, 48655, CRC(13aa3a99) SHA1(6ea2c55dc8c617532455c4754da9bcc5cad170e2) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT      CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1980, chesstrv,  0,      0,      chesstrv,  chesstrv,  chesstrv_state, empty_init, "SciSys / Novag Industries / Philidor Software", "Chess Traveler", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
SYST( 1982, chesstrvi, 0,      0,      chesstrvi, chesstrvi, chesstrv_state, empty_init, "SciSys / Philidor Software", "Chess Intercontinental Traveler", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



chloe.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Andrei I. Holub
/**********************************************************************
    Chloe 280SE
**********************************************************************/

#include "emu.h"

#include "screen_ula.h"
#include "spec128.h"

#include "bus/spectrum/ay/slot.h"
#include "machine/8042kbdc.h"
#include "machine/spi_sdcard.h"
#include "sound/dac.h"

#include "screen.h"
#include "speaker.h"


#define LOG_IO    (1U << 1)
#define LOG_MEM   (1U << 2)
#define LOG_WARN  (1U << 3)

#define VERBOSE ( /*LOG_GENERAL | LOG_IO | LOG_MEM |*/ LOG_WARN )
#include "logmacro.h"

#define LOGIO(...)    LOGMASKED(LOG_IO,    __VA_ARGS__)
#define LOGMEM(...)   LOGMASKED(LOG_MEM,   __VA_ARGS__)
#define LOGWARN(...)  LOGMASKED(LOG_WARN,  __VA_ARGS__)


namespace {

#define TIMINGS_PERFECT     0

// Must be 800x525 to match VGA. With below puts odd scanlines to the right in order to avoid the same line redrawing.
static constexpr u16 CYCLES_HORIZ = 800 << 1;
static constexpr u16 CYCLES_VERT = 525 >> 1;
static constexpr rectangle SCR_FULL = { 0, 640 - 1, 0, 240 - 1 };
static constexpr rectangle SCR_256x192 = {  64, 64 + (256 << 1) - 1, 24, 24 + 192 - 1 };

class chloe_state : public spectrum_128_state
{
public:
	chloe_state(const machine_config &mconfig, device_type type, const char *tag)
		: spectrum_128_state(mconfig, type, tag)
		, m_bank_ram(*this, "bank_ram%u", 0U)
		, m_bank0_view(*this, "bank0_view")
		, m_bank1_view(*this, "bank1_view")
		, m_regs_map(*this, "regs_map")
		, m_palette(*this, "palette")
		, m_ula_scr(*this, "ula_scr")
		, m_sdcard(*this, "sdcard")
		, m_io_line(*this, "IO_LINE%u", 0U)
		, m_io_mouse(*this, "mouse_input%u", 1U)
		, m_covox(*this, "covox")
		, m_kbdc(*this, "pc_kbdc")
	{}

	void chloe(machine_config &config);

	INPUT_CHANGED_MEMBER(on_divmmc_nmi);

protected:
	static inline constexpr u8 BASIC48_ROM = 0x01;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

	void map_regs(address_map &map) ATTR_COLD;
	void map_fetch(address_map &map) ATTR_COLD;
	void map_mem(address_map &map) ATTR_COLD;
	void map_io(address_map &map) ATTR_COLD;

	void irq_keyboard_w(int state);
	u8 kbd_fe_r(offs_t offset);
	u8 divmmc_neutral_r(offs_t offset);
	u8 divmmc_enable_r(offs_t offset);
	u8 divmmc_disable_r(offs_t offset);
	void dma_reg_w(offs_t offset, u8 data);
	void port_7ffd_w(u8 data);
	void port_f4_w(u8 data);
	void port_ff_w(u8 data);
	void port_e3_w(u8 data);
	u8 spi_data_r();
	void spi_data_w(u8 data);
	void spi_miso_w(u8 data);

	void update_memory();
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void raster_irq_adjust();

private:
	TIMER_CALLBACK_MEMBER(spi_clock);
	TIMER_CALLBACK_MEMBER(raster_irq_on);
	TIMER_CALLBACK_MEMBER(raster_irq_off);
	INTERRUPT_GEN_MEMBER(chloe_interrupt);

	memory_access<8, 0, 0, ENDIANNESS_LITTLE>::specific m_uno_regs;
	memory_bank_array_creator<8> m_bank_ram;
	memory_view m_bank0_view, m_bank1_view;
	required_device<address_map_bank_device> m_regs_map;
	required_device<device_palette_interface> m_palette;
	required_device<screen_ula_plus_device> m_ula_scr;
	required_device<spi_sdcard_device> m_sdcard;
	required_ioport_array<8> m_io_line;
	required_ioport_array<3> m_io_mouse;
	required_device<dac_byte_interface> m_covox;
	required_device<kbdc8042_device> m_kbdc;

	u8 m_timex_mmu;
	u8 m_port_ff_data;
	bool m_core_boot;
	u8 m_reg_selected;
	bool m_divmmc_paged;
	u8 m_divmmc_ctrl;
	u8 m_uno_regs_data[256];
	u8 m_palpen_selected;
	bool m_dma_hilo;
	u8 m_dma_src_latch;
	u8 m_dma_dst_latch;
	u8 m_dma_pre_latch;
	u8 m_dma_len_latch;
	u8 m_dma_prob_latch;

	emu_timer *m_irq_raster_on_timer;
	emu_timer *m_irq_raster_off_timer;

	emu_timer *m_spi_clock;
	int m_spi_clock_cycles;
	bool m_spi_clock_state;
	u8 m_spi_mosi_dat;
	u8 m_spi_miso_dat;
};


void chloe_state::update_memory()
{
	m_screen->update_now();
	m_ula_scr->ula_shadow_en_w(BIT(m_port_7ffd_data, 3));

	const bool ext = BIT(m_port_ff_data, 7); // 0 - DOC 7xxxx=28+; 1 - EXT 6xxxx=24+
	m_bank0_view.disable();
	m_bank1_view.disable();
	const u8 divmmc_sram_page = BIT(m_divmmc_ctrl, 0, 6);
	const bool divmmc_sram_page_is_valid = BIT(divmmc_sram_page, 4, 2) == 0;
	const bool mapram_mode = BIT(m_divmmc_ctrl, 6);
	const bool conmem = BIT(m_divmmc_ctrl, 7);
	const bool divmmc_rom_active = m_divmmc_paged || conmem;
	for (int i = 0; i < 8; ++i)
	{
		const bool paged = BIT(m_timex_mmu, i);

		u8 pg;
		if (i == 0 && (divmmc_rom_active || !paged))
		{
			if (divmmc_rom_active)
			{
				pg = (!mapram_mode || conmem) ? 24 : 35;
			}
			else
			{
				pg = (8 + BIT(m_port_7ffd_data, 4)) << 1;
			}
			m_bank0_view.select(0);
			m_bank_ram[0]->set_entry(pg);
		}
		else if (i == 1 && (divmmc_rom_active || !paged))
		{
			if (divmmc_rom_active)
			{
				pg = 32 + (divmmc_sram_page & 0x0f);
				if (!mapram_mode || conmem)
				{
					if (!divmmc_sram_page_is_valid)
					{
						m_bank1_view.select(0);
					}
				}
				else
				{
					if ((mapram_mode && (divmmc_sram_page == 3)) || !divmmc_sram_page_is_valid)
					{
						m_bank1_view.select(0);
					}
				}
			}
			else
			{
				pg = ((8 + BIT(m_port_7ffd_data, 4)) << 1) + 1;
				m_bank1_view.select(0);
			}
			m_bank_ram[1]->set_entry(pg);
		}
		else if (paged)
		{
			pg = ((28 - 4 * ext) << 1) + i;
			m_bank_ram[i]->set_entry(pg);
		}
		else
		{
			if (i < 4)
			{
				pg = 5;
			}
			else if (i < 6)
			{
				pg = 2;
			}
			else
			{
				pg = m_port_7ffd_data & 0x07;
			}

			m_bank_ram[i]->set_entry((pg << 1) + (i & 1));
		}
	}
}

u32 chloe_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	rectangle clip256x192 = SCR_256x192;
	clip256x192 &= cliprect;

	screen.priority().fill(0, cliprect);
	m_ula_scr->draw_border(bitmap, cliprect, m_port_fe_data & 0x07);

	const bool flash = u64(screen.frame_number() / m_frame_invert_count) & 1;
	m_ula_scr->draw(screen, bitmap, clip256x192, flash, 0);

	return 0;
}

void chloe_state::port_7ffd_w(u8 data)
{
	if (m_port_7ffd_data & 0x20)
	{
		return;
	}

	m_port_7ffd_data = data;
	update_memory();
}

void chloe_state::port_ff_w(u8 data)
{
	m_ula_scr->port_ff_reg_w(data);

	m_port_ff_data = data;
	update_memory();
}

void chloe_state::port_f4_w(u8 data)
{
	m_timex_mmu = data;
	update_memory();
}

void chloe_state::port_e3_w(u8 data)
{
	if (m_divmmc_ctrl & 0x40)
	{
		m_divmmc_ctrl = data | 0x40;
	}
	else
	{
		m_divmmc_ctrl = data;
	}
	update_memory();
}

u8 chloe_state::spi_data_r()
{
	u8 din = m_spi_miso_dat;
	if (!machine().side_effects_disabled())
	{
		spi_data_w(0xff);
	}

	return din;
}

void chloe_state::spi_data_w(u8 data)
{
	m_spi_mosi_dat = data;
#if TIMINGS_PERFECT
	m_spi_clock_cycles = 8;
	m_spi_clock->adjust(m_maincpu->clocks_to_attotime(1) / 4, 0, m_maincpu->clocks_to_attotime(1) / 4);
#else
	for (u8 m = 0x80; m; m >>= 1)
	{
		m_sdcard->spi_mosi_w(m_spi_mosi_dat & m ? 1 : 0);
		m_sdcard->spi_clock_w(CLEAR_LINE);
		m_sdcard->spi_clock_w(ASSERT_LINE);
	}
#endif
}

void chloe_state::spi_miso_w(u8 data)
{
	m_spi_miso_dat <<= 1;
	m_spi_miso_dat |= data;
}

TIMER_CALLBACK_MEMBER(chloe_state::spi_clock)
{
	if (m_spi_clock_cycles > 0)
	{
		if (m_spi_clock_state)
		{
			m_sdcard->spi_clock_w(ASSERT_LINE);
			m_spi_clock_cycles--;
		}
		else
		{
			m_sdcard->spi_mosi_w(BIT(m_spi_mosi_dat, m_spi_clock_cycles - 1));
			m_sdcard->spi_clock_w(CLEAR_LINE);
		}

		m_spi_clock_state = !m_spi_clock_state;
	}
	else
	{
		m_spi_clock_state = false;
		m_spi_clock->reset();
	}
}


u8 chloe_state::divmmc_neutral_r(offs_t offset)
{
	return m_program.read_byte(offset);
}

u8 chloe_state::divmmc_enable_r(offs_t offset)
{
	// M1
	if (!machine().side_effects_disabled() && !m_divmmc_paged && (offset >= 0x3d00))
	{
		m_divmmc_paged = 1;
		update_memory();
	}
	const u8 op = m_program.read_byte(offset);
	// after M1
	if (!machine().side_effects_disabled() && !m_divmmc_paged)
	{
		m_divmmc_paged = 1;
		update_memory();
	}
	return op;
}

u8 chloe_state::divmmc_disable_r(offs_t offset)
{
	if (!machine().side_effects_disabled() && m_divmmc_paged && (offset >= 0x4000))
	{
		m_divmmc_paged = 0;
		update_memory();
	}
	const u8 op = m_program.read_byte(offset);
	if (!machine().side_effects_disabled() && m_divmmc_paged)
	{
		m_divmmc_paged = 0;
		update_memory();
	}
	return op;
}

void chloe_state::dma_reg_w(offs_t offset, u8 data)
{
	// TODO dma stub
	m_uno_regs_data[0xa0 + offset] = data;
	m_dma_hilo = !m_dma_hilo;

	switch (offset + 10 * m_dma_hilo)
	{
	case 0: case 10: // DMACTRL
		// 07;
		break;
	case  1: // DMASRC
		//m_dma->((m_dma_src_latch << 8) | data);
		break;
	case 11:
		m_dma_src_latch = data;
		break;
	case  2: // DMADST
		//m_dma->((m_dma_dst_latch << 8) | data);
		break;
	case 12:
		m_dma_dst_latch = data;
		break;
	case  3: // DMAPRE
		//m_dma->((m_dma_pre_latch << 8) | data);
		break;
	case 13:
		m_dma_pre_latch = data;
		break;
	case  4: // DMALEN
		//m_dma->((m_dma_len_latch << 8) | data);
		break;
	case 14:
		m_dma_len_latch = data;
		break;
	case  5: // DMAPROB
		//m_dma->((m_dma_prob_latch << 8) | data);
		break;
	case 15:
		m_dma_prob_latch = data;
		break;
	case 6: case 16: // DMASTAT
		break;
	}
}

void chloe_state::raster_irq_adjust()
{
	if (BIT(m_uno_regs_data[0x0d], 1))
	{
		u16 line = (BIT(m_uno_regs_data[0x0d], 0) << 8) | m_uno_regs_data[0x0c];
		m_irq_raster_on_timer->adjust(m_screen->time_until_pos((SCR_256x192.top() + line) % m_screen->height()));
	}
	else
		m_irq_raster_on_timer->reset();
}

INPUT_CHANGED_MEMBER(chloe_state::on_divmmc_nmi)
{
	if ((newval & 1) && (~m_io_line[0]->read() & 0x8000))
	{
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
	}
}

TIMER_CALLBACK_MEMBER(chloe_state::raster_irq_on)
{
	m_screen->update_now();
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
	m_irq_raster_off_timer->adjust(m_maincpu->clocks_to_attotime(32));
}

TIMER_CALLBACK_MEMBER(chloe_state::raster_irq_off)
{
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
	raster_irq_adjust();
}

INTERRUPT_GEN_MEMBER(chloe_state::chloe_interrupt)
{
	if (BIT(~m_uno_regs_data[0x0d], 2))
	{
		m_irq_on_timer->adjust(m_screen->time_until_pos(SCR_256x192.top(), SCR_256x192.left())
			- attotime::from_ticks(14365, m_maincpu->unscaled_clock())); // TODO confirm
	}
}

void chloe_state::map_fetch(address_map &map)
{
	map(0x0000, 0x3fff).r(FUNC(chloe_state::divmmc_neutral_r));
	map(0x0000, 0x0000).lr8(NAME([this]() { return divmmc_enable_r(0x0000); }));
	map(0x0008, 0x0008).lr8(NAME([this]() { return divmmc_enable_r(0x0008); }));
	map(0x0038, 0x0038).lr8(NAME([this]() { return divmmc_enable_r(0x0038); }));
	map(0x0066, 0x0066).lr8(NAME([this]() { return divmmc_enable_r(0x0066); }));
	map(0x04c6, 0x04c6).lr8(NAME([this]() { return divmmc_enable_r(0x04c6); }));
	map(0x0562, 0x0562).lr8(NAME([this]() { return divmmc_enable_r(0x0562); }));
	map(0x1ff8, 0x1fff).lr8(NAME([this](offs_t offset) { return divmmc_disable_r(0x1ff8 + offset); }));
	map(0x3d00, 0x3dff).lr8(NAME([this](offs_t offset) { return divmmc_enable_r(0x3d00 + offset); }));
	map(0x4000, 0xffff).lr8(NAME([this](offs_t offset) { return divmmc_disable_r(0x4000 + offset); }));
}

void chloe_state::map_mem(address_map &map)
{
	for (int i = 0; i < 8; i++)
		map(0x0000 + i * 0x2000, 0x1fff + i * 0x2000).bankrw(m_bank_ram[i]);

	map(0x0000, 0x1fff).view(m_bank0_view);
	m_bank0_view[0](0x0000, 0x1fff).nopw();
	map(0x2000, 0x3fff).view(m_bank1_view);
	m_bank1_view[0](0x2000, 0x3fff).nopw();
}

void chloe_state::map_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0000).select(0xfffe).rw(FUNC(chloe_state::kbd_fe_r), FUNC(chloe_state::spectrum_ula_w));
	map(0x00ff, 0x00ff).mirror(0xff00).lr8(NAME([this]() { return m_port_ff_data; })).w(FUNC(chloe_state::port_ff_w));
	map(0x7ffd, 0x7ffd).w(FUNC(chloe_state::port_7ffd_w));
	map(0x1ffd, 0x1ffd).w(FUNC(chloe_state::port_7ffd_w));
	map(0x00f4, 0x00f4).mirror(0xff00).w(FUNC(chloe_state::port_f4_w));

	map(0x00e3, 0x00e3).mirror(0xff00).w(FUNC(chloe_state::port_e3_w));
	map(0x00e7, 0x00e7).mirror(0xff00).lw8(NAME([this](u8 data) { m_sdcard->spi_ss_w(data & 1); }));
	map(0x00eb, 0x00eb).mirror(0xff00).rw(FUNC(chloe_state::spi_data_r), FUNC(chloe_state::spi_data_w));

	map(0x00fd, 0x00fd).lw8(NAME([this](u8 data)
	{
		if (data & 1)
		{
			LOGMEM("Core Boot Off\n");
			m_core_boot = 0;
			reset();
		}
	}));

	map(0xbffd, 0xbffd).w("ay_slot", FUNC(ay_slot_device::data_w));
	map(0xfffd, 0xfffd).rw("ay_slot", FUNC(ay_slot_device::data_r), FUNC(ay_slot_device::address_w));
	map(0xbf3b, 0xbf3b).lw8(NAME([this](u8 data)
	{
		m_palpen_selected = data;
		if ((data & 0xc0) == 0x40)
		{
			port_ff_w(data);
		}
	}));
	map(0xff3b, 0xff3b).lrw8(NAME([this]()
	{
		rgb_t rgb = m_palette->pen_color(0xc0 | m_palpen_selected);
		return ((rgb.g() >> 5) << 5) | ((rgb.r() >> 5) << 2) | ((rgb.b() >> 6) << 0);
	}), NAME([this](u8 data)
		{
			if (m_palpen_selected < 64)
			{
				m_palette->set_pen_color(0xc0 | m_palpen_selected, rgbexpand<3,3,3>((data << 1) | (((data >> 1) | data) & 1), 3, 6, 0));
			}
			else if ((m_palpen_selected & 0xc0) == 0x40)
			{
				m_ula_scr->ulap_en_w(data & 1);
			}
		}));
	map(0xfc3b, 0xfc3b).lrw8(NAME([this]() { return m_reg_selected; })
		, NAME([this](u8 data) { m_dma_hilo = 0; m_reg_selected = data; }));
	map(0xfd3b, 0xfd3b).lrw8(NAME([this]() { return m_uno_regs.read_byte(m_reg_selected); })
		, NAME([this](u8 data) { m_uno_regs.write_byte(m_reg_selected, data); }));

	//map(0x007f, 0x007f).mirror(0xff00).lr8(NAME([]() -> u8 { return 0x00; })); // Fuller
	map(0x001f, 0x001f).mirror(0xff00).lr8(NAME([this]() -> u8 { return m_io_joy1->read() & 0x1f; })); // Kempston 1
	map(0x00df, 0x00df).mirror(0xff00).lrw8(NAME([this]() -> u8 { return m_io_joy2->read() & 0x1f; })  // Kempston 2
		, NAME([this](u8 data) { m_covox->data_w(data); }));
	map(0x00b3, 0x00b3).mirror(0xff00).lw8(NAME([this](u8 data) { m_covox->data_w(data); }));
	map(0xfadf, 0xfadf).lr8(NAME([this]() -> u8 { return 0x80 | (m_io_mouse[2]->read() & 0x07); }));
	map(0xfbdf, 0xfbdf).lr8(NAME([this]() -> u8 { return  m_io_mouse[0]->read(); }));
	map(0xffdf, 0xffdf).lr8(NAME([this]() -> u8 { return ~m_io_mouse[1]->read(); }));

	map(0x00f7, 0x00f7).mirror(0xff00).nopw(); // Audio Mixer. No support for now, using default ACB
	map(0x8e3b, 0x8e3b).nopw(); // PRISMSPEEDCTRL used by software compatible with Prism
}

void chloe_state::map_regs(address_map &map)
{
	map(0x00, 0xff).lrw8(NAME([this](offs_t offset)
	{
		if (!machine().side_effects_disabled())
		{
			LOGIO("rREG %02x\n", offset);
		}
		return m_uno_regs_data[offset];
	}), NAME([this](offs_t offset, u8 data)
	{
		LOGIO("wREG %02x = %02x\n", offset, data);
		m_uno_regs_data[offset] = data;
	}));

	map(0x01, 0x01).lw8(NAME([this](u8 data)
	{
		m_uno_regs_data[0x01] = data;
		LOGMEM("UnoMapper %d\n", data);
	}));
	map(0x04, 0x04).lr8(NAME([this]() // SCANCODE
	{
		u8 dat = m_kbdc->data_r(0);
		if (dat == 0xf0)
		{
			dat = 0;
			m_kbdc->data_r(0);
		}
		return dat;
	}));
	map(0x05, 0x05).lr8(NAME([this]() // KBSTATUS
	{
		return m_uno_regs_data[0x05];
	}));
	// 0x07 KEYMAP
	map(0x0b, 0x0b).lw8(NAME([this](u8 data)
	{
		m_uno_regs_data[0x0b] = data;
		m_maincpu->set_clock_scale(1 << BIT(data, 6, 2));
	}));
	map(0x0c, 0x0d).lw8(NAME([this](offs_t offset, u8 data)
	{
		m_uno_regs_data[0x0c + offset] = data;
		raster_irq_adjust();
	}));
	map(0xa0, 0xa6).w(FUNC(chloe_state::dma_reg_w));
}

void chloe_state::irq_keyboard_w(int state)
{
	if (state)
		m_uno_regs_data[0x05] |= 5;
	else
		m_uno_regs_data[0x05] &= ~5;
}

u8 chloe_state::kbd_fe_r(offs_t offset)
{
	u16 data = 0xffff;
	u16 shifts = 0xffff;

	u8 oi = offset >> 8;
	for (u8 i = 0; i < 8; i++, oi >>= 1)
	{
		u16 line_data = m_io_line[i]->read();
		shifts &= line_data;
		if ((oi & 1) == 0)
		{
			data &= line_data;
		}
	}

	bool shift_hold = ~m_io_line[0]->read() & 0x8000;
	if (shift_hold && ((shifts & 0x1f00) != 0x1f00))
	{
		data >>= 8;
		shifts >>= 8;
	}

	if (((offset & 0x0100) == 0) && BIT(~shifts, 6))
	{
		data &= ~0x01; // CS
	}

	if (((offset & 0x8000) == 0) && BIT(~shifts, 7))
	{
		data &= ~0x02; // SS
	}

	data |= 0xe0;

	/* cassette input from wav */
	if (m_cassette->input() > 0.0038)
	{
		data &= ~0x40;
	}

	return data;
}

INPUT_PORTS_START(chloe)
	/* PORT_NAME =  KEY Mode    CAPS Mode    SYMBOL Mode  */
	PORT_START("IO_LINE0") /* 0xFEFE */
	PORT_BIT(0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Caps Shift")   PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("z   Z   :")    PORT_CODE(KEYCODE_Z)      PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(':')
	PORT_BIT(0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("x   X   `")    PORT_CODE(KEYCODE_X)      PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR('`')
																			PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x0008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("c   C   ?")    PORT_CODE(KEYCODE_C)      PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR('?')
	PORT_BIT(0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("v   V   /")    PORT_CODE(KEYCODE_V)      PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR('/')
																			PORT_CODE(KEYCODE_SLASH) PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT(0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line0")
	PORT_BIT(0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line0")     PORT_CODE(KEYCODE_TILDE) PORT_CODE(KEYCODE_SLASH) PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT(0x0200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(": (SS+KEY)")   PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x0800, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("? (SS+KEY)")   PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x8000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS")           PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x7520, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE1") /* 0xFDFE */
	PORT_BIT(0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("a   A   ~")    PORT_CODE(KEYCODE_A)       PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR('~')
	PORT_BIT(0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("s   S   |")    PORT_CODE(KEYCODE_S)       PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR('|')
	PORT_BIT(0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("d   D   \\")   PORT_CODE(KEYCODE_D)       PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR('\\')
																			PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x0008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f   F   {")    PORT_CODE(KEYCODE_F)       PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR('{')
	PORT_BIT(0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("g   G   }")    PORT_CODE(KEYCODE_G)       PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR('}')
	PORT_BIT(0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line1")
	PORT_BIT(0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line1")     PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("~ (SS+KEY)")   PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x0200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("| (SS+KEY)")   PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x0800, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("{ (SS+KEY)")   PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x1000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("} (SS+KEY)")   PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0xe420, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE2") /* 0xFBFE */
	PORT_BIT(0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("q   Q   Hom")  PORT_CODE(KEYCODE_Q) PORT_CODE(KEYCODE_HOME) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("w   W   Del")  PORT_CODE(KEYCODE_W) PORT_CODE(KEYCODE_DEL)  PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("e   E   End")  PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_END)  PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x0008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("r   R   <")    PORT_CODE(KEYCODE_R)                         PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR('<')
	PORT_BIT(0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("t   T   >")    PORT_CODE(KEYCODE_T)                         PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR('>')
	PORT_BIT(0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line2")
	PORT_BIT(0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line2")     PORT_CODE(KEYCODE_HOME) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_END)
	PORT_BIT(0x0800, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("< (SS+KEY)")   PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x1000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("> (SS+KEY)")   PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0xe720, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE3") /* 0xF7FE */
	PORT_BIT(0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1   Tab   !")  PORT_CODE(KEYCODE_1)       PORT_CHAR('1') PORT_CHAR('!')
																			PORT_CODE(KEYCODE_TAB) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2   CLk   @")  PORT_CODE(KEYCODE_2)       PORT_CHAR('2') PORT_CHAR('@')
																			PORT_CODE(KEYCODE_CAPSLOCK) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3   PgU   #")  PORT_CODE(KEYCODE_3)       PORT_CHAR('3') PORT_CHAR('#')
																			PORT_CODE(KEYCODE_PGUP) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x0008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4   PgD   $")  PORT_CODE(KEYCODE_4)       PORT_CHAR('4') PORT_CHAR('$')
																			PORT_CODE(KEYCODE_PGDN) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5   Lft   %")  PORT_CODE(KEYCODE_5)       PORT_CHAR('5') PORT_CHAR('%')
																			PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line3")     PORT_CODE(KEYCODE_TAB) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CODE(KEYCODE_PGUP) PORT_CODE(KEYCODE_PGDN) PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line3")
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("! (SS+KEY)")   PORT_CODE(KEYCODE_1)
	PORT_BIT(0x0200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@ (SS+KEY)")   PORT_CODE(KEYCODE_2)
	PORT_BIT(0x0400, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("# (SS+KEY)")   PORT_CODE(KEYCODE_3)
	PORT_BIT(0x0800, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("$ (SS+KEY)")   PORT_CODE(KEYCODE_4)
	PORT_BIT(0x1000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("% (SS+KEY)")   PORT_CODE(KEYCODE_5)
	PORT_BIT(0xe020, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE4") /* 0xEFFE */
	PORT_BIT(0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0   BSp   _")  PORT_CODE(KEYCODE_0)       PORT_CHAR('0') PORT_CHAR('_')
																			PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9   Ctr   )")  PORT_CODE(KEYCODE_9)       PORT_CHAR('9') PORT_CHAR(')')
																			PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8   Rgt   (")  PORT_CODE(KEYCODE_8)       PORT_CHAR('8') PORT_CHAR('(')
																			PORT_CODE(KEYCODE_RIGHT) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x0008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7   Up    '")  PORT_CODE(KEYCODE_7)       PORT_CHAR('7') PORT_CHAR('\'')
																			PORT_CODE(KEYCODE_UP) PORT_CODE(KEYCODE_QUOTE) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6   Dwn   &")  PORT_CODE(KEYCODE_6)       PORT_CHAR('6') PORT_CHAR('&')
																			PORT_CODE(KEYCODE_DOWN) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line4")     PORT_CODE(KEYCODE_BACKSPACE)  PORT_CODE(KEYCODE_RIGHT) PORT_CODE(KEYCODE_UP) PORT_CODE(KEYCODE_DOWN) PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line4")     PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("_ (SS+KEY)")   PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x0200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(") (SS+KEY)")   PORT_CODE(KEYCODE_0)
	PORT_BIT(0x0400, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("( (SS+KEY)")   PORT_CODE(KEYCODE_9)
	PORT_BIT(0x1000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("& (SS+KEY)")   PORT_CODE(KEYCODE_7)
	PORT_BIT(0xe820, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE5") /* 0xDFFE */
	PORT_BIT(0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("p   P   \"")   PORT_CODE(KEYCODE_P)       PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR('"')
	PORT_BIT(0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("o   O   ;")    PORT_CODE(KEYCODE_O)       PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(';')
																			PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("i   I   Ins")  PORT_CODE(KEYCODE_I)       PORT_CHAR('i') PORT_CHAR('I')
																			PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x0008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("u   U   ]")    PORT_CODE(KEYCODE_U)       PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(']')
																			PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("y   Y   [")    PORT_CODE(KEYCODE_Y)       PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR('[')
																			PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line5")
	PORT_BIT(0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line5")     PORT_CODE(KEYCODE_COLON) PORT_CODE(KEYCODE_INSERT) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\" (SS+KEY)")  PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0xfe20, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE6") /* 0xBFFE */
	PORT_BIT(0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ret    Cmp   Clr") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
																				PORT_CODE(KEYCODE_ENTER_PAD)  PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("l    L    =")      PORT_CODE(KEYCODE_L)     PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR('=')
																				PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("k    K    +")      PORT_CODE(KEYCODE_K)     PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR('+')
																				PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x0008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("j    J    -")      PORT_CODE(KEYCODE_J)     PORT_CHAR('j') PORT_CHAR('J') PORT_CHAR('-')
																				PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("h    H    ^")      PORT_CODE(KEYCODE_H)     PORT_CHAR('h') PORT_CHAR('H') PORT_CHAR('^')
	PORT_BIT(0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line6")         PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line6")         PORT_CODE(KEYCODE_EQUALS) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x0400, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("+ (SS+KEY)")       PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x1000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^ (SS+KEY)")       PORT_CODE(KEYCODE_6)
	PORT_BIT(0xeb20, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE7") /* 0x7FFE */
	PORT_BIT(0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Spc   Esc   Hlp")  PORT_CODE(KEYCODE_SPACE)    PORT_CHAR(' ') PORT_CODE(KEYCODE_ESC)
																				PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Koru")             PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("m   M   .")        PORT_CODE(KEYCODE_M)        PORT_CHAR('m') PORT_CHAR('M') PORT_CHAR('.')
																				PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x0008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("n   N   ,")        PORT_CODE(KEYCODE_N)        PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(',')
																				PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("b   B   *")        PORT_CODE(KEYCODE_B)        PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR('*')
																				PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT(0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line7")         PORT_CODE(KEYCODE_ESC) PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line7")         PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_COMMA) PORT_CODE(KEYCODE_ASTERISK) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x1000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("* (SS+KEY)")       PORT_CODE(KEYCODE_8)
	PORT_BIT(0xef20, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("NMI")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("NMI (SS+KEY)") PORT_CODE(KEYCODE_ESC) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(chloe_state::on_divmmc_nmi), 0)


	PORT_START("JOY1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON1)        PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)    PORT_8WAY PORT_PLAYER(1) PORT_CODE(JOYCODE_Y_UP_SWITCH)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)  PORT_8WAY PORT_PLAYER(1) PORT_CODE(JOYCODE_Y_DOWN_SWITCH)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_8WAY PORT_PLAYER(1) PORT_CODE(JOYCODE_X_RIGHT_SWITCH)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)  PORT_8WAY PORT_PLAYER(1) PORT_CODE(JOYCODE_X_LEFT_SWITCH)

	PORT_START("JOY2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)  PORT_8WAY PORT_PLAYER(2) PORT_CODE(JOYCODE_X_LEFT_SWITCH)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_8WAY PORT_PLAYER(2) PORT_CODE(JOYCODE_X_RIGHT_SWITCH)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)  PORT_8WAY PORT_PLAYER(2) PORT_CODE(JOYCODE_Y_DOWN_SWITCH)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)    PORT_8WAY PORT_PLAYER(2) PORT_CODE(JOYCODE_Y_UP_SWITCH)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_BUTTON1)        PORT_PLAYER(2) PORT_CODE(JOYCODE_BUTTON1)

	PORT_START("mouse_input1")
	PORT_BIT(0xff, 0, IPT_MOUSE_X) PORT_SENSITIVITY(30)

	PORT_START("mouse_input2")
	PORT_BIT(0xff, 0, IPT_MOUSE_Y) PORT_SENSITIVITY(30)

	PORT_START("mouse_input3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON4) PORT_NAME("Left mouse button") PORT_CODE(MOUSECODE_BUTTON1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON5) PORT_NAME("Right mouse button") PORT_CODE(MOUSECODE_BUTTON2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON6) PORT_NAME("Middle mouse button") PORT_CODE(MOUSECODE_BUTTON3)

INPUT_PORTS_END


void chloe_state::machine_start()
{
	spectrum_128_state::machine_start();

	m_spi_clock = timer_alloc(FUNC(chloe_state::spi_clock), this);
	m_irq_raster_on_timer = timer_alloc(FUNC(chloe_state::raster_irq_on), this);
	m_irq_raster_off_timer = timer_alloc(FUNC(chloe_state::raster_irq_off), this);

	m_regs_map->space(AS_PROGRAM).specific(m_uno_regs);

	for (int i = 0; i < 8; i++)
	{
		m_bank_ram[i]->configure_entries(0, m_ram->size() / 0x2000, m_ram->pointer(), 0x2000);
		m_bank_ram[i]->configure_entries( 8 << 1, 4, memregion("maincpu")->base(), 0x2000);
		m_bank_ram[i]->configure_entries(12 << 1, 2, memregion("maincpu")->base() + 0x8000, 0x2000);
	}

	memset(m_uno_regs_data, 0 , 256);
	m_core_boot = 1;
	m_divmmc_ctrl = 0;

	// Save
	save_item(NAME(m_timex_mmu));
	save_item(NAME(m_port_ff_data));
	save_item(NAME(m_core_boot));
	save_item(NAME(m_reg_selected));
	save_item(NAME(m_divmmc_paged));
	save_item(NAME(m_divmmc_ctrl));
	save_pointer(NAME(m_uno_regs_data), 256);
	save_item(NAME(m_palpen_selected));
	save_item(NAME(m_dma_hilo));
	save_item(NAME(m_dma_src_latch));
	save_item(NAME(m_dma_dst_latch));
	save_item(NAME(m_dma_pre_latch));
	save_item(NAME(m_dma_len_latch));
	save_item(NAME(m_dma_prob_latch));

	save_item(NAME(m_spi_clock_cycles));
	save_item(NAME(m_spi_clock_state));
	save_item(NAME(m_spi_mosi_dat));
	save_item(NAME(m_spi_miso_dat));
}

void chloe_state::machine_reset()
{
	spectrum_128_state::machine_reset();

	m_irq_raster_on_timer->reset();
	m_irq_raster_off_timer->reset();

	m_spi_clock->reset();
	m_spi_clock_cycles = 0;
	m_spi_clock_state = false;
	m_dma_hilo = 0;

	m_timex_mmu = 0;
	m_port_ff_data = 0;
	m_port_7ffd_data = 0;
	m_divmmc_paged = 1;
	m_divmmc_ctrl &= 0x40;

	update_memory();
}

static const gfx_layout chloe_charlayout =
{
	8, 8,            // 8 x 8 characters
	256,             // 128 characters
	1,               // 1 bits per pixel
	{ 0 },           // no bitplanes
	{ STEP8(0, 1) }, // x offsets
	{ STEP8(0, 8) }, // y offsets
	8 * 8            // every char takes 8 bytes
};

static GFXDECODE_START(gfx_chloe)
	GFXDECODE_ENTRY("maincpu", 0x2a00, chloe_charlayout, 7, 1)
GFXDECODE_END

void chloe_state::video_start()
{
	spectrum_128_state::video_start();

	const u8 *ram = m_ram->pointer();
	m_ula_scr->set_host_ram_ptr(ram);
}


void chloe_state::chloe(machine_config &config)
{
	spectrum_128(config);
	config.device_remove("exp");
	config.device_remove("palette");
	m_ram->set_default_size("512K").set_default_value(0xff);

	Z80(config.replace(), m_maincpu, 28_MHz_XTAL / 8);
	m_maincpu->set_m1_map(&chloe_state::map_fetch);
	m_maincpu->set_memory_map(&chloe_state::map_mem);
	m_maincpu->set_io_map(&chloe_state::map_io);
	m_maincpu->set_vblank_int("screen", FUNC(chloe_state::chloe_interrupt));
	//m_maincpu->busack_cb().set("dma", FUNC(dma_slot_device::bai_w));

	ADDRESS_MAP_BANK(config, m_regs_map).set_map(&chloe_state::map_regs).set_options(ENDIANNESS_LITTLE, 8, 8, 0);

	/*
	???dma_slot_device &dma(DMA_SLOT(config.replace(), "dma", 28_MHz_XTAL / 8, default_dma_slot_devices, nullptr));
	dma.set_io_space(m_maincpu, AS_IO);
	dma.out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	dma.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	dma.in_mreq_callback().set([this](offs_t offset) { return m_program.read_byte(offset); });
	dma.out_mreq_callback().set([this](offs_t offset, u8 data) { m_program.write_byte(offset, data); });
	dma.in_iorq_callback().set([this](offs_t offset) { return m_io.read_byte(offset); });
	dma.out_iorq_callback().set([this](offs_t offset, u8 data) { m_io.write_byte(offset, data); });
	*/

	SPI_SDCARD(config, m_sdcard, 0);
	m_sdcard->set_prefer_sdhc();
	m_sdcard->spi_miso_callback().set(FUNC(chloe_state::spi_miso_w));

	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_chloe);
	m_screen->set_raw(25.175_MHz_XTAL, CYCLES_HORIZ, CYCLES_VERT, SCR_FULL); // VGA
	m_screen->set_screen_update(FUNC(chloe_state::screen_update));
	m_screen->set_no_palette();
	PALETTE(config, m_palette, FUNC(chloe_state::spectrum_palette), 256);
	SPECTRUM_ULA_UNCONTENDED(config.replace(), m_ula);

	SCREEN_ULA_PLUS(config, m_ula_scr, 0).set_raster_offset(SCR_256x192.left(), SCR_256x192.top()).set_palette(m_palette->device().tag(), 0x000, 0x000);

	SPEAKER(config.replace(), "speakers", 2).front();

	AY_SLOT(config.replace(), "ay_slot", 28_MHz_XTAL / 16, default_ay_slot_devices, "ay_turbosound")
		.add_route(0, "speakers", 0.50, 0)
		.add_route(2, "speakers", 0.25, 0)
		.add_route(2, "speakers", 0.25, 1)
		.add_route(1, "speakers", 0.50, 1);

	DAC_8BIT_R2R(config, m_covox, 0)
		.add_route(ALL_OUTPUTS, "speakers", 0.75, 0)
		.add_route(ALL_OUTPUTS, "speakers", 0.75, 1);

	KBDC8042(config, m_kbdc);
	m_kbdc->set_keyboard_type(kbdc8042_device::KBDC8042_STANDARD);
	m_kbdc->set_interrupt_type(kbdc8042_device::KBDC8042_SINGLE);
	m_kbdc->input_buffer_full_callback().set(FUNC(chloe_state::irq_keyboard_w));
	m_kbdc->set_keyboard_tag("at_keyboard");

	at_keyboard_device &at_keyb(AT_KEYB(config, "at_keyboard", pc_keyboard_device::KEYBOARD_TYPE::AT, 2));
	at_keyb.keypress().set(m_kbdc, FUNC(kbdc8042_device::keyboard_w));

	SOFTWARE_LIST(config, "cass_list_t").set_original("timex_cass");
}

ROM_START(chloe)
	ROM_REGION(0xc000, "maincpu", ROMREGION_ERASEFF)

	// SE/OS 1.0
	ROM_LOAD( "10_boot.rom", 0x0000, 0x4000, CRC(efbfe46e) SHA1(f5a86b56955661f72fa416e7e644de0b3afe6509))
	ROM_LOAD( "10_basic_42.rom", 0x4000, 0x4000, CRC(c6273eaa) SHA1(f09a26c50f5cfe454e4d56c920cdcc62bc4f90cb))
	ROM_LOAD( "10_dos_31.rom", 0x8000, 0x2000, CRC(67dfef09) SHA1(ba9616494071dfe65834d7db657e0d3bcce0b732))
ROM_END

} // Anonymous namespace

/*    YEAR   NAME     PARENT   COMPAT  MACHINE  INPUT   CLASS         INIT         COMPANY               FULLNAME       FLAGS */
COMP( 1999,  chloe,   spec128, 0,      chloe,   chloe,  chloe_state,  empty_init,  "Chloe Corporation",  "Chloe 280SE", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_CONTROLS )



cit101.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

Preliminary driver for first-generation C. Itoh/CIE video terminals.

CIT-101 (released December 1980)
    C. Itoh's first terminal, based on DEC VT100. ANSI X3.64 and V52 compatible.
    12-inch monochrome screen displaying 24 lines of 80 or 132 characters.
    8 x 10 character cell, 10 x 10 (80 columns)/9 x 10 (132 columns) display cell.
    15,600 Hz horizontal frequency; 50 Hz/60 Hz vertical frequency selectable.
    Cursor may be selected as blinking or solid block/underline, or invisible.
    7 or 8 bit ASCII characters.
    RS232-C or 20 mA current loop communications and auxiliary (printer) ports.
    85-key detachable keyboard with 7 LEDs and settable key click.
CIT-80 (released September 1981)
    "Entry-level version" of CIT-101.
    12-inch monochrome screen displaying 24 lines of 80 characters.
    7-bit characters only.
CIT-161 (released 1982)
    Colorized version of the CIT-101.
    12-inch color screen displaying 24 lines of 80 or 132 characters.
    64 combinations of 8 colors are programmable.
CIT-500 (released 1982)
    Word processing terminal with full page display.
    15-inch vertically oriented monochrome screen with tilt/swivel.
    64 lines of 80 characters (interlaced).
    105-key keyboard.
CIT-101e (released 1983)
    Ergonomic redesign of CIT-101.
    Competitive with DEC VT220 (which was released several months later).
    14-inch monochrome screen with tilt/swivel, 24 lines of 80 or 132 characters.
    10 x 12 (80 columns)/9 x 12 (132 columns) display cell.
    19,610 Hz (80 columns)/18,870 Hz (132 columns) horizontal frequency.
    85-key low-profile keyboard.
CIG-201
    Plug-in graphics card for CIT-101 and CIT-101e.
    Compatible with Tektronix 4010/4014.
CIG-261
    Plug-in color graphics card for CIT-161.
    Compatible with Tektronix 4010/4014.
CIG-267
    Plug-in color graphics card for CIT-161.
    Compatible with Tektronix 4027A.

Special SET-UP control codes:
* CTRL+S: Save settings to NVR
* CTRL+R: Recall settings from NVR
* CTRL+D: Restore default NVR settings
* CTRL+A: Set answerback message
* CTRL+X: Enable/disable Bidirectional Auxiliary I/O Channel and SET-UP D Mode
          (SET-UP B Mode only; documented only for CIT-101e but also valid on original)

The NVR checksum error reported when the terminal is first used is nonfatal. Default settings will be used and can be saved.

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

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/i8085/i8085.h"
#include "machine/er2055.h"
#include "machine/i8251.h"
#include "machine/input_merger.h"
#include "machine/pit8253.h"
#include "machine/i8255.h"
#include "screen.h"

#include "cit101_kbd.h"


namespace {

class cit101_state : public driver_device
{
public:
	cit101_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_nvr(*this, "nvr")
		, m_comuart(*this, "comuart")
		, m_kbduart(*this, "kbduart")
		, m_rombank(*this, "rombank")
		, m_chargen(*this, "chargen")
		, m_mainram(*this, "mainram")
		, m_extraram(*this, "extraram")
	{
	}

	void cit101(machine_config &config);
	void cit101e(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void draw_line(uint32_t *pixptr, int minx, int maxx, int line, bool last_line, u16 rowaddr, u16 rowattr, u8 scrattr);
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void screen_reconfigure(const XTAL &xtal, int visible_width, int total_width, int visible_height, int total_height);

	u8 bank_switch_r(offs_t offset);

	u8 c000_ram_r(offs_t offset);
	void c000_ram_w(offs_t offset, u8 data);
	u8 e0_latch_r();
	void e0_latch_w(u8 data);

	void blink_w(int state);
	void screen_control_w(u8 data);
	void screen_control_101e_w(u8 data);
	void brightness_w(u8 data);
	void brightness_101e_w(u8 data);

	void nvr_address_w(u8 data);
	void nvr_control_w(u8 data);

	void mem_map(address_map &map) ATTR_COLD;
	void mem_map_101e(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void io_map_101e(address_map &map) ATTR_COLD;

	u8 m_e0_latch;

	bool m_blink;
	u8 m_brightness;

	required_device<i8085a_cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<er2055_device> m_nvr;
	required_device<i8251_device> m_comuart;
	required_device<i8251_device> m_kbduart;
	optional_memory_bank m_rombank;
	required_region_ptr<u8> m_chargen;
	required_shared_ptr<u8> m_mainram;
	required_shared_ptr<u8> m_extraram;
};


void cit101_state::machine_start()
{
	if (m_rombank.found())
	{
		m_rombank->configure_entries(0, 8, memregion("banked")->base(), 0x1000);
		m_rombank->set_entry(0);
	}

	m_comuart->write_cts(0);
	m_kbduart->write_cts(0);

	m_brightness = 0xff;

	save_item(NAME(m_e0_latch));
	save_item(NAME(m_blink));
	save_item(NAME(m_brightness));
}


void cit101_state::draw_line(uint32_t *pixptr, int minx, int maxx, int line, bool last_line, u16 rowaddr, u16 rowattr, u8 scrattr)
{
	// Character attribute bit 0: underline (also used to render cursor)
	// Character attribute bit 1: reverse video (also used to render cursor)
	// Character attribute bit 2: boldface
	// Character attribute bit 3: blinking (half intensity)

	const int char_width = BIT(scrattr, 1) ? 10 : 9;
	int c = 0;
	u8 attr = m_extraram[rowaddr];
	u8 char_data = m_chargen[(m_mainram[rowaddr] << 4) | line];
	if (last_line && BIT(attr, 0))
		char_data ^= 0xff;
	rgb_t on_color = (BIT(attr, 1) != BIT(scrattr, 0)) ? rgb_t::black() : rgb_t(m_brightness, m_brightness, m_brightness);
	rgb_t off_color = (BIT(attr, 1) != BIT(scrattr, 0)) ? rgb_t(m_brightness, m_brightness, m_brightness) : rgb_t::black();
	if (BIT(attr, 3) && m_blink)
		on_color = rgb_t(m_brightness * 0.75, m_brightness * 0.75, m_brightness * 0.75);
	bool last_bit = false;
	for (int x = 0; x <= maxx; x++)
	{
		const bool cur_bit = BIT(char_data, 7);
		if (x >= minx)
			pixptr[x] = (cur_bit || (BIT(attr, 2) && last_bit)) ? on_color : off_color;
		last_bit = cur_bit;

		c++;
		if (!BIT(rowattr, 9) || !BIT(c, 0))
		{
			if (c < (BIT(rowattr, 9) ? char_width << 1 : char_width))
				char_data = (char_data << 1) | (char_data & 1);
			else
			{
				c = 0;
				rowaddr = (rowaddr + 1) & 0x3fff;
				attr = m_extraram[rowaddr];
				char_data = m_chargen[(m_mainram[rowaddr] << 4) | line];
				if (last_line && BIT(attr, 0))
					char_data ^= 0xff;
				on_color = (BIT(attr, 1) != BIT(scrattr, 0)) ? rgb_t::black() : rgb_t(m_brightness, m_brightness, m_brightness);
				off_color = (BIT(attr, 1) != BIT(scrattr, 0)) ? rgb_t(m_brightness, m_brightness, m_brightness) : rgb_t::black();
				if (BIT(attr, 3) && m_blink)
					on_color = rgb_t(m_brightness * 0.75, m_brightness * 0.75, m_brightness * 0.75);
				last_bit = false;
			}
		}
	}
}

u32 cit101_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	// While screen height is fixed at 240 pixels, the number of character rows and the height of each row are not.
	// The "Set-Up" screens configure 3 ordinary 10-pixel rows on the top (using two for double-height characters)
	// and 2 more on the bottom, separated by 11 blank rows of 16 pixels and 1 blank row of 14 pixels. This is
	// also used to implement the "smooth scroll" option.

	const u8 scrattr = m_extraram[0];

	int y = 0;
	for (u16 rowptr = m_mainram[0]; y <= cliprect.bottom(); rowptr += 2)
	{
		const u16 rowattr = m_mainram[rowptr] | m_extraram[rowptr] << 8;
		const int rowlines = 16 - ((rowattr & 0x0f0) >> 4);

		int z = 0;
		if (y < cliprect.top())
		{
			z = cliprect.top() - y;
			if (z >= rowlines)
			{
				y += rowlines;
				continue;
			}
			y = cliprect.top();
		}

		const u16 rowaddr = m_mainram[rowptr + 1] | (m_extraram[rowptr + 1] & 0x3f) << 8;
		while (y <= cliprect.bottom())
		{
			const int line = ((z / (BIT(rowattr, 8) ? 2 : 1)) + (rowattr & 0x00f)) & 15;
			const bool last_line = z++ == rowlines - 1;
			draw_line(&bitmap.pix(y++), cliprect.left(), cliprect.right(), line, last_line, rowaddr, rowattr, scrattr);
			if (last_line)
				break;
		}
	}
	return 0;
}


void cit101_state::screen_reconfigure(const XTAL &xtal, int visible_width, int total_width, int visible_height, int total_height)
{
	const rectangle visarea(0, visible_width - 1, 0, visible_height - 1);
	const attoseconds_t frame_period = attotime::from_ticks(total_width * total_height, xtal).as_attoseconds();

	m_screen->set_unscaled_clock(xtal);
	m_screen->configure(total_width, total_height, visarea, frame_period);
}


u8 cit101_state::bank_switch_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		m_rombank->set_entry(offset);

	return 0xc9; // RET
}

u8 cit101_state::c000_ram_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		m_e0_latch = m_extraram[offset];
	return m_mainram[offset];
}

void cit101_state::c000_ram_w(offs_t offset, u8 data)
{
	m_extraram[offset] = m_e0_latch;
	m_mainram[offset] = data;
}

u8 cit101_state::e0_latch_r()
{
	return m_e0_latch;
}

void cit101_state::e0_latch_w(u8 data)
{
	m_e0_latch = data;
}

void cit101_state::blink_w(int state)
{
	m_blink = state;
}

void cit101_state::screen_control_w(u8 data)
{
	if ((m_extraram[0] & 0x06) != (data & 0x06))
	{
		const int height = BIT(data, 2) ? 312 : 260;
		if (BIT(data, 1))
			screen_reconfigure(14.976_MHz_XTAL, 800, 960, 240, height);
		else
			screen_reconfigure(22.464_MHz_XTAL, 1188, 1440, 240, height);
	}

	m_extraram[0] = data;
}

void cit101_state::screen_control_101e_w(u8 data)
{
	if ((m_extraram[0] & 0x06) != (data & 0x06))
	{
		// TODO: interlace mode
		const int height = 300;
		if (BIT(data, 1))
			screen_reconfigure(19.6608_MHz_XTAL, 800, 1000, 288, height);
		else
			screen_reconfigure(27.956_MHz_XTAL, 1188, 1476, 288, height);
	}

	m_extraram[0] = data;
}

void cit101_state::brightness_w(u8 data)
{
	// Function of upper 3 bits is unknown
	m_brightness = pal5bit(~data & 0x1f);
}

void cit101_state::brightness_101e_w(u8 data)
{
	// Function of most significant bit is unknown
	m_brightness = pal7bit(~data & 0x7f);
}

void cit101_state::nvr_address_w(u8 data)
{
	m_nvr->set_address(data & 0x3f);
	m_nvr->set_clk(BIT(data, 6));
}

void cit101_state::nvr_control_w(u8 data)
{
	m_nvr->set_control(BIT(data, 5), !BIT(data, 4), BIT(data, 7), BIT(data, 6));
}

void cit101_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).rom().region("maincpu", 0);
	map(0x4000, 0x7fff).ram().share("mainram");
	map(0x8000, 0xbfff).ram().share("extraram"); // only 4 bits wide?
	map(0x8000, 0x8000).w(FUNC(cit101_state::screen_control_w));
	map(0xc000, 0xdfff).rw(FUNC(cit101_state::c000_ram_r), FUNC(cit101_state::c000_ram_w));
	map(0xfc00, 0xfc01).rw("auxuart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xfc20, 0xfc21).rw("comuart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xfc40, 0xfc41).rw("kbduart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xfc60, 0xfc63).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xfc80, 0xfc83).w("pit0", FUNC(pit8253_device::write));
	map(0xfcc0, 0xfcc3).w("pit1", FUNC(pit8253_device::write));
}

void cit101_state::mem_map_101e(address_map &map)
{
	map(0x0000, 0x2fff).rom().region("maincpu", 0);
	map(0x3000, 0x3ff7).bankr("rombank");
	map(0x3ff8, 0x3fff).r(FUNC(cit101_state::bank_switch_r));
	map(0x4000, 0x7fff).ram().share("mainram");
	map(0x8000, 0xbfff).ram().share("extraram"); // 6 bits wide here?
	map(0x8000, 0x8000).w(FUNC(cit101_state::screen_control_101e_w));
	map(0xc000, 0xfbff).rw(FUNC(cit101_state::c000_ram_r), FUNC(cit101_state::c000_ram_w));
	map(0xfc00, 0xfc01).rw("auxuart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xfc20, 0xfc21).rw("comuart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xfc40, 0xfc41).rw("kbduart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xfc60, 0xfc63).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xfc80, 0xfc83).w("pit0", FUNC(pit8253_device::write));
	map(0xfcc0, 0xfcc3).w("pit1", FUNC(pit8253_device::write));
}

void cit101_state::io_map(address_map &map)
{
	map(0x00, 0x01).rw("auxuart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x20, 0x21).rw("comuart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x40, 0x41).rw("kbduart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x60, 0x63).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xa0, 0xa0).w(FUNC(cit101_state::brightness_w));
	map(0xe0, 0xe0).rw(FUNC(cit101_state::e0_latch_r), FUNC(cit101_state::e0_latch_w));
}

void cit101_state::io_map_101e(address_map &map)
{
	io_map(map);
	map(0xa0, 0xa0).w(FUNC(cit101_state::brightness_101e_w));
}


static INPUT_PORTS_START(cit101)
INPUT_PORTS_END


void cit101_state::cit101(machine_config &config)
{
	I8085A(config, m_maincpu, 6.144_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &cit101_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &cit101_state::io_map);
	m_maincpu->in_sid_func().set_constant(0); // used to time NVR reads
	m_maincpu->out_sod_func().set(FUNC(cit101_state::blink_w));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	//m_screen->set_raw(14.976_MHz_XTAL, 960, 0, 800, 260, 0, 240);
	m_screen->set_raw(22.464_MHz_XTAL, 1440, 0, 1188, 260, 0, 240);
	m_screen->set_screen_update(FUNC(cit101_state::screen_update));
	m_screen->screen_vblank().set_inputline("maincpu", I8085_RST75_LINE);

	I8251(config, m_comuart, 6.144_MHz_XTAL / 2);
	m_comuart->txd_handler().set("comm", FUNC(rs232_port_device::write_txd));
	m_comuart->dtr_handler().set("comm", FUNC(rs232_port_device::write_dtr));
	m_comuart->rts_handler().set("comm", FUNC(rs232_port_device::write_rts));
	m_comuart->rxrdy_handler().set("uartint", FUNC(input_merger_device::in_w<0>));
	m_comuart->txrdy_handler().set("uartint", FUNC(input_merger_device::in_w<2>));

	rs232_port_device &comm(RS232_PORT(config, "comm", default_rs232_devices, nullptr));
	comm.rxd_handler().set("comuart", FUNC(i8251_device::write_rxd));
	comm.dsr_handler().set("comuart", FUNC(i8251_device::write_dsr));
	// CTS can be disabled in SET-UP Mode C
	// DSR, CD, SI, RI are examined only during the modem test, not "always ignored" as the User's Manual claims

	i8251_device &auxuart(I8251(config, "auxuart", 6.144_MHz_XTAL / 2));
	auxuart.txd_handler().set("printer", FUNC(rs232_port_device::write_txd));
	auxuart.rxrdy_handler().set("uartint", FUNC(input_merger_device::in_w<1>));
	auxuart.txrdy_handler().set("uartint", FUNC(input_merger_device::in_w<3>));

	rs232_port_device &printer(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	printer.rxd_handler().set("auxuart", FUNC(i8251_device::write_rxd));
	printer.cts_handler().set("auxuart", FUNC(i8251_device::write_cts));

	INPUT_MERGER_ANY_HIGH(config, "uartint").output_handler().set_inputline("maincpu", I8085_RST55_LINE);

	I8251(config, m_kbduart, 6.144_MHz_XTAL / 2);
	m_kbduart->txd_handler().set("keyboard", FUNC(cit101_keyboard_device::write_rxd));
	m_kbduart->rxrdy_handler().set_inputline("maincpu", I8085_RST65_LINE);

	CIT101_KEYBOARD(config, "keyboard").txd_callback().set("kbduart", FUNC(i8251_device::write_rxd));

	pit8253_device &pit0(PIT8253(config, "pit0", 0));
	pit0.set_clk<0>(6.144_MHz_XTAL / 4);
	pit0.set_clk<1>(6.144_MHz_XTAL / 4);
	//pit0.set_clk<2>(6.144_MHz_XTAL / 4);
	pit0.out_handler<0>().set("auxuart", FUNC(i8251_device::write_txc));
	pit0.out_handler<1>().set("auxuart", FUNC(i8251_device::write_rxc));
	// OUT2 might be used for an internal expansion similar to the VT100 STP.
	// The output appears to be fixed to a 307.2 kHz rate; turning this off boosts driver performance.

	pit8253_device &pit1(PIT8253(config, "pit1", 0));
	pit1.set_clk<0>(6.144_MHz_XTAL / 4);
	pit1.set_clk<1>(6.144_MHz_XTAL / 4);
	pit1.set_clk<2>(6.144_MHz_XTAL / 4);
	pit1.out_handler<0>().set("comuart", FUNC(i8251_device::write_txc));
	pit1.out_handler<1>().set("comuart", FUNC(i8251_device::write_rxc));
	pit1.out_handler<2>().set("kbduart", FUNC(i8251_device::write_txc));
	pit1.out_handler<2>().append("kbduart", FUNC(i8251_device::write_rxc));

	i8255_device &ppi(I8255A(config, "ppi", 0));
	ppi.out_pa_callback().set(FUNC(cit101_state::nvr_address_w));
	ppi.in_pb_callback().set(m_nvr, FUNC(er2055_device::data));
	ppi.out_pb_callback().set(m_nvr, FUNC(er2055_device::set_data));
	ppi.in_pc_callback().set("comm", FUNC(rs232_port_device::cts_r)).lshift(0);
	ppi.in_pc_callback().append("comm", FUNC(rs232_port_device::dcd_r)).lshift(1); // tied to DSR for loopback test
	ppi.in_pc_callback().append("comm", FUNC(rs232_port_device::ri_r)).lshift(2); // tied to CTS for loopback test
	ppi.in_pc_callback().append("comm", FUNC(rs232_port_device::si_r)).lshift(3); // tied to CTS for loopback test
	ppi.out_pc_callback().set(FUNC(cit101_state::nvr_control_w));

	ER2055(config, m_nvr);
}

void cit101_state::cit101e(machine_config &config)
{
	cit101(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &cit101_state::mem_map_101e);
	m_maincpu->set_addrmap(AS_IO, &cit101_state::io_map_101e);

	//m_screen->set_raw(19.6608_MHz_XTAL, 1000, 0, 800, 300, 0, 288); // 65.3 Hz nominal vertical frequency
	m_screen->set_raw(27.956_MHz_XTAL, 1476, 0, 1188, 300, 0, 288); // 63.2 Hz nominal vertical frequency

	CIT101E_KEYBOARD(config.replace(), "keyboard").txd_callback().set("kbduart", FUNC(i8251_device::write_rxd));
}


// PCB ID: HAV-2P005B / CIT-101 / C. ITOH
// CPU: NEC D8085AC
// RAM: 12x NEC D416C-2 (16 positions labeled 8116E, including 4 unpopulated ones)
// Peripherals: 3x M5L8251AP-5 (2M, 7J, 7K); 2x NEC D8253C-2 (7I, 7L); NEC D8255AC-2 (7N); GI ER-2055 (8P)
// Oscillators: 6.144 (XTAL1), 14.976 (XTAL2), 22.464 (XTAL3)
ROM_START(cit101)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_LOAD("ic1_=3g04=_v10a.bin", 0x0000, 0x1000, CRC(5601fcac) SHA1(cad0d0335d133dd43993bc718ff72d12b8445cd1)) // TMM2732D-45
	ROM_LOAD("ic2_=3h04=_v10a.bin", 0x1000, 0x1000, CRC(23d263e0) SHA1(586e8185f9804987e0a4081724c060e74769d41d)) // TMM2732D-45
	ROM_LOAD("ic3_=3i04=_v10a.bin", 0x2000, 0x1000, CRC(15994b1d) SHA1(6d125db4ef5e1dd4d5a4d2f4d6f6bdf574e5bad8)) // TMM2732D-45
	ROM_LOAD("ic4_=3j04=_v10a.bin", 0x3000, 0x0800, CRC(d786995f) SHA1(943b521dcc7abc0662d6e136169b7db480ae1e5c)) // MB8516
	ROM_RELOAD(0x3800, 0x0800)

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("1h =5h 1 02= char rom.bin", 0x0000, 0x1000, CRC(ee0ff889) SHA1(a74ada19d19041b29e1b49aaf57ba7d9d54575e1)) // TMM2332P

	ROM_REGION(0x180, "proms", 0)
	ROM_LOAD("2i_=3a00=.bin", 0x000, 0x100, NO_DUMP) // position labeled (MB)7052
	ROM_LOAD("2f_=6g00=.bin", 0x100, 0x020, NO_DUMP) // position labeled TBP18S030
	ROM_LOAD("2e_=7i00=.bin", 0x120, 0x020, NO_DUMP) // position labeled TBP18S030
	ROM_LOAD("5d_=4l00=.bin", 0x140, 0x020, NO_DUMP) // position labeled TBP18S030
	ROM_LOAD("5g_=7f00=.bin", 0x160, 0x020, NO_DUMP) // position labeled TBP18S030
ROM_END

// PCB ID: C.ITOH CIT-101e HBG-2P002
// CPU: NEC D8085AHC
// RAM: 14x Fujitsu MB8118-12 (16 positions labeled 8118, including 2 unpopulated ones)
// Peripherals: 3x NEC D8251AFC (7M, 7N, 7R); 2x NEC D8253C-2 (7J, 7K); NEC D8255AC-2 (6N); GI ER-2055 (5R)
// Oscillators: 19.6608 (XTAL1), 27.956 (XTAL2), 6.144 (XTAL3)
ROM_START(cit101e)
	ROM_REGION(0x3000, "maincpu", 0)
	ROM_LOAD("101e_v12c__12.7a", 0x0000, 0x2000, CRC(bc71ad27) SHA1(e61481752e20b115531b76688242691d265853e7))
	ROM_LOAD("101e_v12c__3.7c", 0x2000, 0x1000, CRC(b4c63dd1) SHA1(aff9bd8e79e83c176c882fa3251a1419a283e753))

	ROM_REGION(0x8000, "banked", ROMREGION_ERASEFF)
	ROM_LOAD("101e_v12c__ab.7f", 0x0000, 0x2000, CRC(6d6bc1ee) SHA1(f42596b379bfda0468045d9e3810a1f0990f76f6))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("cit-101e_char_gen.3g", 0x0000, 0x1000, CRC(ccf259b4) SHA1(d918f16ce148c813a865280a43a766983673464a)) // position labeled 2732/2332

	ROM_REGION(0x1c0, "proms", 0)
	ROM_LOAD("2c=atr=__.bin", 0x000, 0x100, NO_DUMP) // position labeled MB7052
	ROM_LOAD("6j=adr=00.bin", 0x100, 0x020, NO_DUMP) // position labeled (TBP)18S030
	ROM_LOAD("1n=hs=00.bin", 0x120, 0x020, NO_DUMP) // position labeled (TBP)18S030
	ROM_LOAD("2n=hl=__.bin", 0x140, 0x020, NO_DUMP) // position labeled (TBP)18S030
	ROM_LOAD("1s=vs=__.bin", 0x160, 0x020, NO_DUMP) // position labeled (TBP)18S030
	ROM_LOAD("4s=it1=__.bin", 0x180, 0x020, NO_DUMP) // position labeled (TBP)18S030
	ROM_LOAD("4t=it2=06.bin", 0x1a0, 0x020, NO_DUMP) // position labeled (TBP)18S030
ROM_END

} // anonymous namespace


COMP(1980, cit101,  0, 0, cit101,  cit101, cit101_state, empty_init, "C. Itoh Electronics", "CIT-101 Video Terminal", MACHINE_SUPPORTS_SAVE)
COMP(1983, cit101e, 0, 0, cit101e, cit101, cit101_state, empty_init, "C. Itoh Electronics", "CIT-101e Video Terminal", MACHINE_SUPPORTS_SAVE)



cit101xl.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

Skeleton driver for CIE Terminals (C. Itoh) CIT-50+ and CIT-101XL video terminals.

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

#include "emu.h"
//#include "bus/rs232/rs232.h"
#include "cpu/z180/z180.h"
#include "machine/nvram.h"
#include "video/scn2674.h"
#include "screen.h"


namespace {

class cit101xl_state : public driver_device
{
public:
	cit101xl_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_avdc(*this, "avdc")
	{
	}

	void cit101xl(machine_config &config);

private:
	SCN2674_DRAW_CHARACTER_MEMBER(draw_character);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void char_map(address_map &map) ATTR_COLD;
	void attr_map(address_map &map) ATTR_COLD;

	required_device<z180_device> m_maincpu;
	required_device<scn2674_device> m_avdc;
};


SCN2674_DRAW_CHARACTER_MEMBER(cit101xl_state::draw_character)
{
}


void cit101xl_state::mem_map(address_map &map)
{
	map(0x00000, 0x07fff).rom().region("program", 0);
	map(0x1c000, 0x1ffff).rom().region("program", 0x8000);
	map(0x2e000, 0x2ffff).ram().share("nvram");
}

void cit101xl_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x3f).noprw(); // HD64180 internal registers
	map(0x40, 0x47).rw(m_avdc, FUNC(scn2674_device::read), FUNC(scn2674_device::write));
	map(0x48, 0x48).nopr(); // watchdog?
	map(0x60, 0x60).w(m_avdc, FUNC(scn2674_device::buffer_w));
	map(0x68, 0x68).r(m_avdc, FUNC(scn2674_device::buffer_r));
	map(0x70, 0x70).w(m_avdc, FUNC(scn2674_device::attr_buffer_w));
	map(0x78, 0x78).r(m_avdc, FUNC(scn2674_device::attr_buffer_r));
}

void cit101xl_state::char_map(address_map &map)
{
	map(0x0000, 0x3fff).ram(); // TMM2063P-12 x2
}

void cit101xl_state::attr_map(address_map &map)
{
	map(0x0000, 0x3fff).ram(); // TMM2063P-12 x2
}


static INPUT_PORTS_START(cit101xl)
INPUT_PORTS_END


void cit101xl_state::cit101xl(machine_config &config)
{
	HD64180RP(config, m_maincpu, 12.288_MHz_XTAL); // HD64B180R0P
	m_maincpu->set_addrmap(AS_PROGRAM, &cit101xl_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &cit101xl_state::io_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // TC5564APL-15 + battery?

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(24.27_MHz_XTAL, 980, 0, 820, 413, 0, 364);
	screen.set_screen_update(m_avdc, FUNC(scn2674_device::screen_update));

	SCN2674(config, m_avdc, 24.27_MHz_XTAL / 10); // SCN2674BC4N40
	//m_avdc->intr_callback().set_inputline(m_maincpu, ???);
	m_avdc->set_character_width(10); // 10x13 character cell
	m_avdc->set_display_callback(FUNC(cit101xl_state::draw_character));
	m_avdc->set_addrmap(0, &cit101xl_state::char_map);
	m_avdc->set_addrmap(1, &cit101xl_state::attr_map);
	m_avdc->set_screen("screen");
}

// XTAL OSCs: 12.288 MHz (CPU), 24.270 MHz, 36.000 MHz
// Gate arrays: HG61H09R84F (QFP80), L7A0084 4155P 00416A (LCC68)
ROM_START(cit101xl)
	ROM_REGION(0xc000, "program", 0)
	ROM_LOAD("cit50p-101xl_v.1.1_0800.u2", 0x0000, 0x8000, CRC(2dbbd7f6) SHA1(f87b32e803bda5a8dd0e39e2e339357cfa4082ad)) // HN27256G-25 with handwritten label
	ROM_LOAD("cit50p-101xl_v.1.1_8456.u4", 0x8000, 0x4000, CRC(4df0b677) SHA1(1916c65935c47cb0e11a8c7f293b608e648c542b)) // M5L27128K with handwritten label

	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD("tmm2464ap_1104_cit50p_v1.2.u3", 0x0000, 0x2000, CRC(e07723f7) SHA1(466d69382cc75ac0abcda08e1a227da73fc77980)) // Toshiba OTP ROM with silkscreened label
ROM_END

} // anonymous namespace


COMP(1987, cit101xl, 0, 0, cit101xl, cit101xl, cit101xl_state, empty_init, "CIE Terminals", "CIT-101XL Video Display Terminal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



cit1500.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for public pay phones by Alcatel/Telefónica/Citesa.

Components (trmavia):

  Parts side:
CIT1500 (80-pin 78K/0 MCU): NEC CITESA ICS1 7CE52872AA 0143H8L01
CIT1200: ST HCF4067 Analog Multiplexer/demultiplexer
CIT500: Philips TEA1067AT Telephone Transmission Circuit With Dialler Interface
CIT1400 (display?) NEC D7225GB 0119EY002
  Subboard 49:
(visible side) FX604D4 V23 modem
(back side) CMX673D4 Call progress tone detector
  Reverse side:
LCD display
DS1302S trickle-charge timekeeping
Philips 3312CT DTMF/modem/musical-tone generator
Philips 74HC573D (behind the LCD)

Components (teletup):

  Parts side:
CIT1500 (80-pin 78K/0 MCU): NEC CITESA ICS1 7CE52872AA 0143H8L01
CIT1200: ST HCF4067 Analog Multiplexer/demultiplexer
CIT500: Philips TEA1067AT Telephone Transmission Circuit With Dialler Interface
CIT1400: (display?) NEC D7225GB 0119EY002
CIT900: CMX673D4 Call progress tone detector
  Reverse side:
LCD display
Philips 3312CT DTMF/modem/musical-tone generator
DS1302S trickle-charge timekeeping

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

#include "emu.h"
#include "cpu/upd78k/upd78k0.h"
//#include "machine/ds1302.h"
//#include "video/upd7225.h"
//#include "screen.h"


namespace {

class cit1500_state : public driver_device
{
public:
	cit1500_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_cit1500(*this, "cit1500")
	{
	}

	void cit1500(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<upd78053_device> m_cit1500;
};


void cit1500_state::mem_map(address_map &map)
{
	map(0x6000, 0xfa7f).rom().region("cit1600", 0x6000); // TODO: banked
}


static INPUT_PORTS_START(cit1500)
INPUT_PORTS_END


void cit1500_state::cit1500(machine_config &config)
{
	UPD78053(config, m_cit1500, 5_MHz_XTAL, 32.768_kHz_XTAL);
	m_cit1500->set_addrmap(AS_PROGRAM, &cit1500_state::mem_map);

	//UPD7225(config, "cit1400");

	//SCREEN(config, "screen", SCREEN_TYPE_LCD);

	//DS1302(config, "ds1302", 32.768_kHz_XTAL);
}


ROM_START(trmavia)
	ROM_REGION(0x20000, "cit1600", 0) // external program ROM
	ROM_LOAD("3cq-1039-e-183cf58-3a-28-06-01_m27w101.cit1600", 0x00000, 0x20000, CRC(db6bff63) SHA1(1b33ad98112d2946edc9444c8e6df4b4da778377))

	ROM_REGION(0x6000, "cit1500", 0) // 24K internal mask ROM
	ROM_LOAD("citesa_ics1_7ce52872aa_0143h8l01", 0x0000, 0x6000, NO_DUMP)
	ROM_COPY("cit1600", 0x06000, 0x0000, 2) // hack to provide at least a reset vector

	ROM_REGION(0x1000, "cit1300", 0)
	ROM_LOAD("tpe-2c-eaaa_24lc32a.cit1300", 0x0000, 0x1000, CRC(141d94e0) SHA1(f76a920a4eb6476999b5b59a1bf9eaa7b616cd0b))
ROM_END

ROM_START(teletup)
	ROM_REGION(0x20000, "cit1600", 0) // external program ROM
	ROM_LOAD("3cq-1043-ae-1616d87-3d-16-11-00_27v101.cit1600", 0x00000, 0x20000, CRC(656c3ac5) SHA1(abcda259826d91a8f8c66a64066bd3da017a61af))

	ROM_REGION(0x6000, "cit1500", 0) // 24K internal mask ROM
	ROM_LOAD("citesa_ics1_7ce52872aa_0143h8l01", 0x0000, 0x6000, NO_DUMP)
	ROM_COPY("cit1600", 0x06000, 0x0000, 2) // hack to provide at least a reset vector

	ROM_REGION(0x1000, "cit1300", 0)
	ROM_LOAD("tue2a-xbaa_br24c32.cit1300", 0x0000, 0x1000, CRC(dd447884) SHA1(8ae677f46cd39015355a38a7f1147a7abc412674))
ROM_END

} // anonymous namespace


SYST(2001, trmavia, 0, 0, cit1500, cit1500, cit1500_state, empty_init, "Alcatel / Telefonica", "TRMA VIA", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(2000, teletup, 0, 0, cit1500, cit1500, cit1500_state, empty_init, "Alcatel / Telefonica", "TeleTUP", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



cit220.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

Skeleton driver for VT220-compatible terminals by C. Itoh/CIE Terminals and similar terminals by ADDS.

The CIT-220+ Video Terminal was introduced as a direct competitor to DEC's VT220. It copied the design of the VT220 closely
enough to provoke a lawsuit, which led to its eventual withdrawal in favor of its successor, the CIT224.

"COPYRIGHT MICROWEST 1984" can be found in the program ROMs of both the CIT-220+ and Viewpoint 122. The code is clearly similar,
and the SCN2674 video timing parameters appear to be identical.

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

#include "emu.h"
//#include "bus/rs232/rs232.h"
#include "cpu/i8085/i8085.h"
#include "cit220_kbd.h"
//#include "machine/eeprompar.h"
#include "machine/i8251.h"
#include "machine/mc68681.h"
#include "machine/nvram.h"
#include "machine/pit8253.h"
#include "video/scn2674.h"
#include "screen.h"


namespace {

class cit220_state : public driver_device
{
public:
	cit220_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_avdc(*this, "avdc")
		, m_chargen(*this, "chargen")
	{ }

	void cit220p(machine_config &config);
	void vp122(machine_config &config);
	void tabe22(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;

	void sod_w(int state);
	void cols_w(int state);
	SCN2674_DRAW_CHARACTER_MEMBER(draw_character);

	void cit220p_mem_map(address_map &map) ATTR_COLD;
	void cit220p_io_map(address_map &map) ATTR_COLD;
	void vp122_mem_map(address_map &map) ATTR_COLD;
	void vp122_io_map(address_map &map) ATTR_COLD;

	void char_map(address_map &map) ATTR_COLD;
	void attr_map(address_map &map) ATTR_COLD;

	required_device<i8085a_cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<scn2674_device> m_avdc;
	required_region_ptr<u8> m_chargen;

	bool m_132_cols = false;
};


void cit220_state::machine_start()
{
	subdevice<i8251_device>("usart")->write_cts(0);

	m_132_cols = false;
	save_item(NAME(m_132_cols));
}


void cit220_state::sod_w(int state)
{
	// probably asserts PBREQ on SCN2674 to access memory at Exxx
}

void cit220_state::cols_w(int state)
{
	if (state == m_132_cols)
	{
		m_132_cols = !state;
		m_avdc->set_character_width(m_132_cols ? 9 : 10);
		m_avdc->set_unscaled_clock(m_132_cols ? 22'096'000 / 9 : 14'916'000 / 10);
	}
}

void cit220_state::cit220p_mem_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("maincpu", 0);
	map(0x8000, 0x87ff).ram();
	map(0xa000, 0xa1ff).rom().region("eeprom", 0x800);
	map(0xe000, 0xe7ff).ram(); // ???
}

void cit220_state::cit220p_io_map(address_map &map)
{
	map(0x00, 0x0f).rw("duart", FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0x10, 0x11).rw("usart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x20, 0x27).rw(m_avdc, FUNC(scn2674_device::read), FUNC(scn2674_device::write));
	map(0xa0, 0xa0).rw(m_avdc, FUNC(scn2674_device::attr_buffer_r), FUNC(scn2674_device::attr_buffer_w));
	map(0xc0, 0xc0).rw(m_avdc, FUNC(scn2674_device::buffer_r), FUNC(scn2674_device::buffer_w));
}

void cit220_state::vp122_mem_map(address_map &map)
{
	map(0x0000, 0x9fff).rom().region("maincpu", 0);
	map(0xa000, 0xa7ff).ram().share("nvram");
	map(0xe000, 0xe7ff).noprw();
}

void cit220_state::vp122_io_map(address_map &map)
{
	map(0x00, 0x07).rw(m_avdc, FUNC(scn2674_device::read), FUNC(scn2674_device::write));
	map(0x10, 0x1f).rw("duart", FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0x20, 0x21).rw("usart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x50, 0x50).rw(m_avdc, FUNC(scn2674_device::buffer_r), FUNC(scn2674_device::buffer_w));
	map(0x60, 0x60).rw(m_avdc, FUNC(scn2674_device::attr_buffer_r), FUNC(scn2674_device::attr_buffer_w));
	map(0x70, 0x73).w("pit", FUNC(pit8253_device::write));
}


SCN2674_DRAW_CHARACTER_MEMBER(cit220_state::draw_character)
{
	u16 dots = m_chargen[charcode << 4 | linecount] << 2;
	const int width = m_132_cols ? 9 : 10;

	if (BIT(dots, 2))
		dots |= 3;
	if (BIT(attrcode, 2))
		dots = ~dots;
	if (cursor)
		dots = ~dots;

	for (int i = 0; i < width; i++)
	{
		bitmap.pix(y, x++) = BIT(dots, 9) ? rgb_t::white() : rgb_t::black();
		dots <<= 1;
	}
}

void cit220_state::char_map(address_map &map)
{
	map(0x0000, 0x2fff).ram();
}

void cit220_state::attr_map(address_map &map)
{
	map(0x0000, 0x2fff).ram();
}


static INPUT_PORTS_START( cit220p )
INPUT_PORTS_END


void cit220_state::cit220p(machine_config &config)
{
	I8085A(config, m_maincpu, 8'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &cit220_state::cit220p_mem_map);
	m_maincpu->set_addrmap(AS_IO, &cit220_state::cit220p_io_map);
	m_maincpu->out_sod_func().set(FUNC(cit220_state::sod_w));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	//m_screen->set_raw(14'916'000, 960, 0, 800, 259, 0, 240);
	m_screen->set_raw(22'096'000, 1422, 0, 1188, 259, 0, 240);
	m_screen->set_screen_update(m_avdc, FUNC(scn2674_device::screen_update));

	SCN2674(config, m_avdc, 22'096'000 / 9);
	m_avdc->intr_callback().set_inputline(m_maincpu, I8085_RST65_LINE);
	m_avdc->set_character_width(9); // 10 in 80-column modes
	m_avdc->set_display_callback(FUNC(cit220_state::draw_character));
	m_avdc->set_addrmap(0, &cit220_state::char_map);
	m_avdc->set_addrmap(1, &cit220_state::attr_map);
	m_avdc->set_screen(m_screen);

	scn2681_device &duart(SCN2681(config, "duart", 3'686'400));
	duart.irq_cb().set_inputline(m_maincpu, I8085_RST55_LINE);
	duart.outport_cb().set("usart", FUNC(i8251_device::write_txc)).bit(3);
	duart.outport_cb().append("usart", FUNC(i8251_device::write_rxc)).bit(3);
	duart.outport_cb().append(FUNC(cit220_state::cols_w)).bit(7);

	i8251_device &usart(I8251(config, "usart", 4'000'000));
	usart.txd_handler().set("keyboard", FUNC(cit220p_keyboard_device::write_rxd));

	cit220p_keyboard_device &keyboard(CIT220P_KEYBOARD(config, "keyboard"));
	keyboard.txd_callback().set("usart", FUNC(i8251_device::write_rxd));
}

void cit220_state::vp122(machine_config &config)
{
	I8085A(config, m_maincpu, 8_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &cit220_state::vp122_mem_map);
	m_maincpu->set_addrmap(AS_IO, &cit220_state::vp122_io_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // MK48Z02

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(14.916_MHz_XTAL, 960, 0, 800, 259, 0, 240);
	//m_screen->set_raw(22.096_MHz_XTAL, 1422, 0, 1188, 259, 0, 240);
	m_screen->set_screen_update(m_avdc, FUNC(scn2674_device::screen_update));

	SCN2674(config, m_avdc, 14.916_MHz_XTAL / 10);
	m_avdc->intr_callback().set_inputline(m_maincpu, I8085_RST65_LINE);
	m_avdc->set_character_width(10); // 9 in 132-column modes
	m_avdc->set_display_callback(FUNC(cit220_state::draw_character));
	m_avdc->set_addrmap(0, &cit220_state::char_map);
	m_avdc->set_addrmap(1, &cit220_state::attr_map);
	m_avdc->set_screen("screen");

	scn2681_device &duart(SCN2681(config, "duart", 3.6864_MHz_XTAL));
	duart.irq_cb().set_inputline(m_maincpu, I8085_RST55_LINE);
	duart.outport_cb().set("usart", FUNC(i8251_device::write_txc)).bit(3);
	duart.outport_cb().append("usart", FUNC(i8251_device::write_rxc)).bit(3);
	duart.outport_cb().append(FUNC(cit220_state::cols_w)).bit(7);

	I8251(config, "usart", 8_MHz_XTAL / 2);

	PIT8253(config, "pit");
	// Input clocks are video-related and should differ for 80-column and 132-column modes
}


ROM_START(cit220p)
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD("v17_001a.ic23", 0x0000, 0x4000, CRC(2cc43026) SHA1(366f57292c6e44571368c29e3258203779847356))
	ROM_LOAD("v17_001b.ic24", 0x4000, 0x4000, CRC(a56b16f1) SHA1(c68f26b35453153f7defcf1cf2b7ad7fe36cc9e7))

	ROM_REGION(0x1000, "eeprom", 0)
	ROM_LOAD("eeprom.bin",    0x0000, 0x1000, CRC(7b24878a) SHA1(20086fb792a24339b65abe627aefbcf48e2abcf4))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("v20_cg.ic17",   0x0000, 0x1000, CRC(76ef7ca9) SHA1(6e7799ca0a41350fbc369bbbd4ab581150f37b10))
ROM_END

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

ADDS Viewpoint 122 (VPT-122).
Chips: D8085AC-2, SCN2674B, SCB2675T, D8251AFC, SCN2681A, D8253C-2, 5x MB8128-15, MK48Z02B-20
Crystals: 22.096, 14.916, 3.6864, 8.000

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

ROM_START(vp122)
	ROM_REGION(0xc000, "maincpu", 0)
	ROM_LOAD("223-48600.uj1", 0x0000, 0x4000, CRC(4d140c69) SHA1(04aa5a4f0c0e0d07b9dc983a6d626ee88ef8b8ba))
	ROM_LOAD("223-48500.ug1", 0x4000, 0x4000, CRC(4e98554d) SHA1(0cbb9cb7efd02a3209caed410ccc8495a5ec1772))
	ROM_LOAD("223-49400.uj2", 0x8000, 0x4000, CRC(447d90d3) SHA1(f8c0db824198b5a571eef80cc3eaf1e829aa2c2a))

	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD("223-48700.uk4", 0x0000, 0x2000, CRC(4dbab4bd) SHA1(18e9a23ba22e2096fa529541fa329f5a56740e62))
ROM_END

} // anonymous namespace


COMP(1984, cit220p, 0, 0, cit220p, cit220p, cit220_state, empty_init, "C. Itoh Electronics", "CIT-220+ Video Terminal", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND)
COMP(1985, vp122, 0, 0, vp122, cit220p, cit220_state, empty_init, "ADDS", "Viewpoint 122", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND)



ckz80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

CKZ-80

2010-08-30 Skeleton driver
2010-11-27 Connected to a terminal
2014-01-08 Added devices

Only known info: http://forumcpm.gaby.de/oldboard/showtopic0adb.html?threadid=280

On main board there are Z80A CPU, Z80A PIO, Z80A DART and Z80A CTC
   there is 8K ROM and XTAL 16MHz
   Two undumped proms, AM27S20DC and D3631-1.
FDC board contains Z80A DMA and NEC 765A (XTAL on it is 8MHZ)
Mega board contains 74LS612 and memory chips (32x 41256)

Status:
  It prints 2 lines of text, then waits for a floppy.

ToDo:
- Everything... no diagram or manuals, so EVERYTHING below is guesswork.
- Need software

I/O ports: These ranges are what is guessed
  40 : rom switching
  4c-4F : PIO
  50-53 : DART
  54-57 : CTC
  80-81 : Parallel port (no programming bytes are sent, so it isn't a device)
  C0-C1 : FDC
  It is not known what address is used by:
  - the DMA
  - the Motor-on signal(s)
  as there are no unknown writes.


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

#include "emu.h"

#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/terminal.h"
#include "machine/upd765.h"
#include "machine/z80ctc.h"
#include "machine/z80daisy.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"


namespace {

class ckz80_state : public driver_device
{
public:
	ckz80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_bank1(*this, "bank1")
		, m_terminal(*this, "terminal")
		, m_fdc(*this, "fdc")
	{ }

	void ckz80(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	u8 port80_r();
	u8 port81_r();
	void port40_w(u8 data);
	void kbd_put(u8 data);
	void ctc_z0_w(int state);
	void ctc_z1_w(int state);
	void ctc_z2_w(int state);
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	u8 m_term_data = 0U;
	memory_passthrough_handler m_rom_shadow_tap;
	required_device<z80_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_memory_bank    m_bank1;
	required_device<generic_terminal_device> m_terminal;
	required_device<upd765a_device> m_fdc;
};


void ckz80_state::port40_w(u8 data)
{
	m_bank1->set_entry(BIT(~data, 1));
}

u8 ckz80_state::port80_r()
{
	u8 ret = m_term_data;
	m_term_data = 0;
	return ret;
}

u8 ckz80_state::port81_r()
{
	return (m_term_data) ? 3 : 1;
}

void ckz80_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).ram().share("mainram");
	map(0xe000, 0xffff).bankr("bank1");
}

void ckz80_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x40, 0x40).w(FUNC(ckz80_state::port40_w));
	map(0x4c, 0x4f).rw("pio", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x50, 0x53).rw("dart", FUNC(z80dart_device::cd_ba_r), FUNC(z80dart_device::cd_ba_w));
	map(0x54, 0x57).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	// 80, 81 - no setup bytes
	map(0x80, 0x80).r(FUNC(ckz80_state::port80_r)).w(m_terminal, FUNC(generic_terminal_device::write));
	map(0x81, 0x81).r(FUNC(ckz80_state::port81_r));
	map(0xc0, 0xc1).m(m_fdc, FUNC(upd765a_device::map));
}

/* Input ports */
static INPUT_PORTS_START( ckz80 )
INPUT_PORTS_END

/* Z80 Daisy Chain */

static const z80_daisy_config daisy_chain[] =
{
	{ "pio" },
	{ "dart" },
	{ "ctc" },
	{ nullptr }
};

/* Z80-CTC Interface */

void ckz80_state::ctc_z0_w(int state)
{
// guess this generates clock for z80dart
}

void ckz80_state::ctc_z1_w(int state)
{
}

void ckz80_state::ctc_z2_w(int state)
{
}

void ckz80_state::machine_start()
{
	m_bank1->configure_entry(0, m_ram+0xe000);
	m_bank1->configure_entry(1, m_rom);
	save_item(NAME(m_term_data));
}

void ckz80_state::machine_reset()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x0000, 0x1fff, m_rom);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0xe000, 0xffff,
			"rom_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// reinstall RAM over the ROM shadow
					m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x1fff, m_ram);
				}
			},
			&m_rom_shadow_tap);

	m_bank1->set_entry(1);
}

static void ckz80_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

void ckz80_state::kbd_put(u8 data)
{
	m_term_data = data;
}

void ckz80_state::ckz80(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &ckz80_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &ckz80_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	GENERIC_TERMINAL(config, m_terminal, 0);
	m_terminal->set_keyboard_callback(FUNC(ckz80_state::kbd_put));
	UPD765A(config, m_fdc, 8_MHz_XTAL, true, true);
	FLOPPY_CONNECTOR(config, "fdc:0", ckz80_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);

	z80ctc_device& ctc(Z80CTC(config, "ctc", 16_MHz_XTAL / 4));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	ctc.zc_callback<0>().set(FUNC(ckz80_state::ctc_z0_w));
	ctc.zc_callback<1>().set(FUNC(ckz80_state::ctc_z1_w));
	ctc.zc_callback<2>().set(FUNC(ckz80_state::ctc_z2_w));

	z80dart_device& dart(Z80DART(config, "dart", 16_MHz_XTAL / 4));
	//dart.out_txda_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	//dart.out_dtra_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	//dart.out_rtsa_callback().set("rs232", FUNC(rs232_port_device::write_rts));
	dart.out_int_callback().set_inputline("maincpu", INPUT_LINE_IRQ0);

	z80pio_device& pio(Z80PIO(config, "pio", 16_MHz_XTAL / 4));
	pio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
}


/* ROM definition */
ROM_START( ckz80 )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "ckz80.rom", 0x0000, 0x2000, CRC(7081b7c6) SHA1(13f75b14ea73b252bdfa2384e6eead6e720e49e3))
ROM_END

} // anonymous namespace

/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY      FULLNAME  FLAGS
COMP( 198?, ckz80, 0,      0,      ckz80,   ckz80, ckz80_state, empty_init, "<unknown>", "CKZ-80", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



clcd.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder, smf, Mike Naberezny
/***************************************************************************

Commodore LCD prototype

OSC: 4MHz, 1.8432MHz, 3.579545MHz
GTE G65SC102PI-2
GTE G65SC51P-1
Rockwell R65C22P2 x 2
AMI S3530X Bell 103/V.21 Single chip modem
OKI M5260 x 2

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


#include "emu.h"

#include "bus/cbmiec/cbmiec.h"
#include "bus/centronics/ctronics.h"
#include "bus/rs232/rs232.h"
#include "cpu/m6502/g65sc02.h"
#include "machine/6522via.h"
#include "machine/bankdev.h"
#include "machine/input_merger.h"
#include "machine/mos6551.h"
#include "machine/msm58321.h"
#include "machine/ram.h"
#include "machine/nvram.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class clcd_state : public driver_device
{
public:
	clcd_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_acia(*this, "acia"),
		m_via0(*this, "via0"),
		m_rtc(*this, "rtc"),
		m_centronics(*this, "centronics"),
		m_iec(*this, CBM_IEC_TAG),
		m_ram(*this, "ram"),
		m_nvram(*this, "nvram"),
		m_bankdev(*this, "bank%u", 1U),
		m_lcd_char_rom(*this, "lcd_char_rom"),
		m_lcd_scrollx(0),
		m_lcd_scrolly(0),
		m_lcd_mode(0),
		m_lcd_size(0),
		m_mmu_mode(MMU_MODE_KERN),
		m_mmu_saved_mode(MMU_MODE_KERN),
		m_mmu_offset1(0),
		m_mmu_offset2(0),
		m_mmu_offset3(0),
		m_mmu_offset4(0),
		m_mmu_offset5(0),
		m_key_clk(0),
		m_key_poll(0),
		m_key_row(0),
		m_key_shift(0xffff),
		m_key_force_format(0),
		m_row(*this, "ROW%u", 0U),
		m_special(*this, "SPECIAL"),
		m_power(1),
		m_power_on(0)
	{
	}

	virtual void driver_start() override
	{
		m_mmu_mode = MMU_MODE_TEST;
		update_mmu_mode(MMU_MODE_KERN);

		save_item(NAME(m_lcd_scrollx));
		save_item(NAME(m_lcd_scrolly));
		save_item(NAME(m_lcd_mode));
		save_item(NAME(m_lcd_size));
		save_item(NAME(m_mmu_mode));
		save_item(NAME(m_mmu_saved_mode));
		save_item(NAME(m_mmu_offset1));
		save_item(NAME(m_mmu_offset2));
		save_item(NAME(m_mmu_offset3));
		save_item(NAME(m_mmu_offset4));
		save_item(NAME(m_mmu_offset5));
		save_item(NAME(m_key_clk));
		save_item(NAME(m_key_poll));
		save_item(NAME(m_key_row));
		save_item(NAME(m_key_shift));
		save_item(NAME(m_power));
		save_item(NAME(m_power_on));

		m_rtc->cs1_w(1);
		m_acia->write_cts(0);
		m_nvram->set_base(ram()->pointer(), ram()->size());
	}

	void clcd_palette(palette_device &palette) const
	{
		palette.set_pen_color(0, rgb_t(124, 149, 143));
		palette.set_pen_color(1, rgb_t(54, 64, 65));
	}

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
	{
		if (m_lcd_mode & LCD_MODE_GRAPH)
		{
			for (int y = 0; y < 128; y++)
			{
				int offset = (m_lcd_scrolly * 128) + (y * 64);

				for (int x = 0; x < 60; x++)
				{
					uint8_t const bit = m_ram->pointer()[offset++];

					bitmap.pix(y, (x * 8) + 0) = BIT(bit, 7);
					bitmap.pix(y, (x * 8) + 1) = BIT(bit, 6);
					bitmap.pix(y, (x * 8) + 2) = BIT(bit, 5);
					bitmap.pix(y, (x * 8) + 3) = BIT(bit, 4);
					bitmap.pix(y, (x * 8) + 4) = BIT(bit, 3);
					bitmap.pix(y, (x * 8) + 5) = BIT(bit, 2);
					bitmap.pix(y, (x * 8) + 6) = BIT(bit, 1);
					bitmap.pix(y, (x * 8) + 7) = BIT(bit, 0);
				}
			}
		}
		else
		{
			uint8_t const *font = m_lcd_char_rom->base();
			if (m_lcd_mode & LCD_MODE_ALT)
			{
				font += 1024;
			}

			int const chrw = (m_lcd_size & LCD_SIZE_CHRW) ? 8 : 6;

			for (int y = 0; y < 128; y++)
			{
				int const offset = (m_lcd_scrolly * 128) + (m_lcd_scrollx & 0x7f) + ((y / 8) * 128);

				for (int x = 0; x < 480; x++)
				{
					uint8_t const ch = m_ram->pointer()[offset + (x / chrw)];
					uint8_t bit = font[((ch & 0x7f) * 8) + (y % 8)];
					if (ch & 0x80)
					{
						bit = ~bit;
					}

					bitmap.pix(y, x) = (bit >> (7 - (x % chrw))) & 1;
				}
			}
		}

		return 0;
	}

	uint8_t ram_r(offs_t offset)
	{
		if (offset < m_ram->size())
		{
			return m_ram->pointer()[offset];
		}

		return 0xff;
	}

	void ram_w(offs_t offset, uint8_t data)
	{
		if (offset < m_ram->size())
		{
			m_ram->pointer()[offset] = data;
		}
	}

	enum
	{
		MMU_MODE_KERN,
		MMU_MODE_APPL,
		MMU_MODE_RAM,
		MMU_MODE_TEST
	};

	enum
	{
		LCD_MODE_ALT = 1,
		LCD_MODE_GRAPH = 2
	};

	enum
	{
		LCD_SIZE_CHRW = 4
	};

	void update_mmu_mode(int new_mode)
	{
		if (m_mmu_mode != new_mode)
		{
			m_mmu_mode = new_mode;

			switch (m_mmu_mode)
			{
			case MMU_MODE_KERN:
				m_bankdev[0]->set_bank(0x1000 / 0x400);
				update_mmu_offset5();
				m_bankdev[2]->set_bank(0x38000 / 0x400);
				m_bankdev[3]->set_bank(0x3c000 / 0x400);
				break;

			case MMU_MODE_APPL:
				update_mmu_offset1();
				update_mmu_offset2();
				update_mmu_offset3();
				update_mmu_offset4();
				break;

			case MMU_MODE_RAM:
				m_bankdev[0]->set_bank(0x1000 / 0x400);
				m_bankdev[1]->set_bank(0x4000 / 0x400);
				m_bankdev[2]->set_bank(0x8000 / 0x400);
				m_bankdev[3]->set_bank(0xc000 / 0x400);
				break;

			case MMU_MODE_TEST:
				m_bankdev[0]->set_bank(0x81000 / 0x400);
				m_bankdev[1]->set_bank(0x84000 / 0x400);
				m_bankdev[2]->set_bank(0x88000 / 0x400);
				m_bankdev[3]->set_bank(0x8c000 / 0x400);
				break;
			}
		}
	}

	void update_mmu_offset1()
	{
		if (m_mmu_mode == MMU_MODE_APPL)
		{
			m_bankdev[0]->set_bank(((0x1000 / 0x400) + m_mmu_offset1) & 0xff);
		}
	}

	void update_mmu_offset2()
	{
		if (m_mmu_mode == MMU_MODE_APPL)
		{
			m_bankdev[1]->set_bank(((0x4000 / 0x400) + m_mmu_offset2) & 0xff);
		}
	}

	void update_mmu_offset3()
	{
		if (m_mmu_mode == MMU_MODE_APPL)
		{
			m_bankdev[2]->set_bank(((0x8000 / 0x400) + m_mmu_offset3) & 0xff);
		}
	}

	void update_mmu_offset4()
	{
		if (m_mmu_mode == MMU_MODE_APPL)
		{
			m_bankdev[3]->set_bank(((0xc000 / 0x400) + m_mmu_offset4) & 0xff);
		}
	}

	void update_mmu_offset5()
	{
		if (m_mmu_mode == MMU_MODE_KERN)
		{
			m_bankdev[1]->set_bank(((0x4000 / 0x400) + m_mmu_offset5) & 0xff);
		}
	}

	void mmu_mode_kern_w(uint8_t data)
	{
		update_mmu_mode(MMU_MODE_KERN);
	}

	void mmu_mode_appl_w(uint8_t data)
	{
		update_mmu_mode(MMU_MODE_APPL);
	}

	void mmu_mode_ram_w(uint8_t data)
	{
		update_mmu_mode(MMU_MODE_RAM);
	}

	void mmu_mode_recall_w(uint8_t data)
	{
		update_mmu_mode(m_mmu_saved_mode);
	}

	void mmu_mode_save_w(uint8_t data)
	{
		m_mmu_saved_mode = m_mmu_mode;
	}

	void mmu_mode_test_w(uint8_t data)
	{
		update_mmu_mode(MMU_MODE_TEST);
	}

	void mmu_offset1_w(uint8_t data)
	{
		if (m_mmu_offset1 != data)
		{
			m_mmu_offset1 = data;
			update_mmu_offset1();
		}
	}

	void mmu_offset2_w(uint8_t data)
	{
		if (m_mmu_offset2 != data)
		{
			m_mmu_offset2 = data;
			update_mmu_offset2();
		}
	}

	void mmu_offset3_w(uint8_t data)
	{
		if (m_mmu_offset3 != data)
		{
			m_mmu_offset3 = data;
			update_mmu_offset3();
		}
	}

	void mmu_offset4_w(uint8_t data)
	{
		if (m_mmu_offset4 != data)
		{
			m_mmu_offset4 = data;
			update_mmu_offset4();
		}
	}

	void mmu_offset5_w(uint8_t data)
	{
		if (m_mmu_offset5 != data)
		{
			m_mmu_offset5 = data;
			update_mmu_offset5();
		}
	}

	uint8_t mmu_offset1_r()
	{
		return m_mmu_offset1;
	}

	uint8_t mmu_offset2_r()
	{
		return m_mmu_offset2;
	}

	uint8_t mmu_offset3_r()
	{
		return m_mmu_offset3;
	}

	uint8_t mmu_offset4_r()
	{
		return m_mmu_offset4;
	}

	uint8_t mmu_offset5_r()
	{
		return m_mmu_offset5;
	}

	void lcd_scrollx_w(uint8_t data)
	{
		m_lcd_scrollx = data;
	}

	void lcd_scrolly_w(uint8_t data)
	{
		m_lcd_scrolly = data;
	}

	void lcd_mode_w(uint8_t data)
	{
		m_lcd_mode = data;
	}

	void lcd_size_w(uint8_t data)
	{
		m_lcd_size = data;
	}

	void via0_pa_w(uint8_t data)
	{
		m_key_row = data;
	}

	uint8_t via0_pb_r()
	{
		uint8_t data = 0;

		if (!m_iec->clk_r())
		{
			data |= 1 << 6;
		}

		if (!m_iec->data_r())
		{
			data |= 1 << 7;
		}

		return data;
	}

	void via0_pb_w(uint8_t data)
	{
		write_sl(BIT(data, 0));
		m_rtc->cs2_w(BIT(data, 1));
		write_power_on(BIT(data, 2));
		m_iec->host_atn_w(!BIT(data, 3));
		m_iec->host_clk_w(!BIT(data, 4));
		m_iec->host_data_w(!BIT(data, 5));

		machine().scheduler().perfect_quantum(attotime::from_usec(2000));
	}

	void write_sl(int state)
	{
		if (m_key_poll != state)
		{
			m_key_poll = state;

			if (m_key_poll != 0)
			{
				m_key_shift = 0xff00 | m_special->read();

				for (int i = 0; i < 8; i++)
					if (!BIT(m_key_row, i))
						m_key_shift &= (m_row[i]->read() << 8) | 0xff;

				if (m_key_force_format)
				{
					m_key_shift = ~0x15; // Simulate holding CBM+SHIFT+STOP if you have no NVRAM

					m_key_force_format--;
				}
			}
		}
	}

	void via0_cb1_w(int state)
	{
		int newm_key_clk = state & 1;
		if (m_key_clk != newm_key_clk)
		{
			m_key_clk = newm_key_clk;

			if (!m_key_clk)
			{
				m_via0->write_cb2(!BIT(m_key_shift, 15));
				m_key_shift <<= 1;
			}
		}
	}

	void via1_pa_w(uint8_t data)
	{
		m_rtc->d0_w(BIT(data, 0));
		m_centronics->write_data0(BIT(data, 0));

		m_rtc->d1_w(BIT(data, 1));
		m_centronics->write_data1(BIT(data, 1));

		m_rtc->d2_w(BIT(data, 2));
		m_centronics->write_data2(BIT(data, 2));

		m_rtc->d3_w(BIT(data, 3));
		m_centronics->write_data3(BIT(data, 3));

		m_rtc->read_w(BIT(data, 4));
		m_centronics->write_data4(BIT(data, 4));

		m_rtc->write_w(BIT(data, 5));
		m_centronics->write_data5(BIT(data, 5));

		m_rtc->address_write_w(BIT(data, 6));
		m_centronics->write_data6(BIT(data, 6));

		m_centronics->write_data7(BIT(data, 7));
	}

	void via1_pb_w(uint8_t data)
	{
		//int centronics_unknown = !BIT(data,5);
	}

	void write_power_on(int state)
	{
		if (m_power_on != state)
		{
			m_power_on = state;
			power_on_reset();
		}
	}

	void write_power(int state)
	{
		if (m_power != state)
		{
			m_power = state;
			power_on_reset();
		}
	}

	void power_on_reset()
	{
		if (!m_power && m_power_on)
			machine().schedule_soft_reset();
	}

	ram_device *ram()
	{
		return m_ram;
	}

	void force_format()
	{
		m_key_force_format = 10;
	}

	void nvram_init(nvram_device &nvram, void *data, size_t size);

	void clcd(machine_config &config);
	void clcd_banked_mem(address_map &map) ATTR_COLD;
	void clcd_mem(address_map &map) ATTR_COLD;

private:
	required_device<g65sc102_device> m_maincpu;
	required_device<mos6551_device> m_acia;
	required_device<via6522_device> m_via0;
	required_device<msm58321_device> m_rtc;
	required_device<centronics_device> m_centronics;
	required_device<cbm_iec_device> m_iec;
	required_device<ram_device> m_ram;
	required_device<nvram_device> m_nvram;
	required_device_array<address_map_bank_device, 4> m_bankdev;
	required_memory_region m_lcd_char_rom;
	int m_lcd_scrollx;
	int m_lcd_scrolly;
	int m_lcd_mode;
	int m_lcd_size;
	int m_mmu_mode;
	int m_mmu_saved_mode;
	uint8_t m_mmu_offset1;
	uint8_t m_mmu_offset2;
	uint8_t m_mmu_offset3;
	uint8_t m_mmu_offset4;
	uint8_t m_mmu_offset5;
	int m_key_clk;
	int m_key_poll;
	int m_key_row;
	uint16_t m_key_shift;
	int m_key_force_format;
	required_ioport_array<8> m_row;
	required_ioport m_special;
	bool m_power;
	bool m_power_on;
};

void clcd_state::nvram_init(nvram_device &nvram, void *data, size_t size)
{
	memset(data, 0x00, size);
	force_format();
}


void clcd_state::clcd_banked_mem(address_map &map)
{
	/* KERN/APPL/RAM */
	map(0x00000, 0x1ffff).mirror(0x40000).rw(FUNC(clcd_state::ram_r), FUNC(clcd_state::ram_w));
	map(0x20000, 0x3ffff).mirror(0x40000).rom().region("maincpu", 0);

	/* TEST */
	map(0x81000, 0x83fff).r(FUNC(clcd_state::mmu_offset1_r));
	map(0x84000, 0x87fff).r(FUNC(clcd_state::mmu_offset2_r));
	map(0x88000, 0x8bfff).r(FUNC(clcd_state::mmu_offset3_r));
	map(0x8c000, 0x8dfff).r(FUNC(clcd_state::mmu_offset4_r));
	map(0x8e000, 0x8f7ff).r(FUNC(clcd_state::mmu_offset5_r));
}

void clcd_state::clcd_mem(address_map &map)
{
	map(0x0000, 0x0fff).rw(FUNC(clcd_state::ram_r), FUNC(clcd_state::ram_w));
	map(0x1000, 0x3fff).rw(m_bankdev[0], FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0x4000, 0x7fff).rw(m_bankdev[1], FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0x8000, 0xbfff).rw(m_bankdev[2], FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0xc000, 0xf7ff).rw(m_bankdev[3], FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0xf800, 0xf80f).mirror(0x70).m(m_via0, FUNC(via6522_device::map));
	map(0xf880, 0xf88f).mirror(0x70).m("via1", FUNC(via6522_device::map));
	map(0xf980, 0xf983).mirror(0x7c).rw(m_acia, FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0xfa00, 0xffff).rom().region("maincpu", 0x1fa00);
	map(0xfa00, 0xfa00).mirror(0x7f).w(FUNC(clcd_state::mmu_mode_kern_w));
	map(0xfa80, 0xfa80).mirror(0x7f).w(FUNC(clcd_state::mmu_mode_appl_w));
	map(0xfb00, 0xfb00).mirror(0x7f).w(FUNC(clcd_state::mmu_mode_ram_w));
	map(0xfb80, 0xfb80).mirror(0x7f).w(FUNC(clcd_state::mmu_mode_recall_w));
	map(0xfc00, 0xfc00).mirror(0x7f).w(FUNC(clcd_state::mmu_mode_save_w));
	map(0xfc80, 0xfc80).mirror(0x7f).w(FUNC(clcd_state::mmu_mode_test_w));
	map(0xfd00, 0xfd00).mirror(0x7f).w(FUNC(clcd_state::mmu_offset1_w));
	map(0xfd80, 0xfd80).mirror(0x7f).w(FUNC(clcd_state::mmu_offset2_w));
	map(0xfe00, 0xfe00).mirror(0x7f).w(FUNC(clcd_state::mmu_offset3_w));
	map(0xfe80, 0xfe80).mirror(0x7f).w(FUNC(clcd_state::mmu_offset4_w));
	map(0xff00, 0xff00).mirror(0x7f).w(FUNC(clcd_state::mmu_offset5_w));
	map(0xff80, 0xff80).mirror(0x0c).w(FUNC(clcd_state::lcd_scrollx_w));
	map(0xff81, 0xff81).mirror(0x0c).w(FUNC(clcd_state::lcd_scrolly_w));
	map(0xff82, 0xff82).mirror(0x0c).w(FUNC(clcd_state::lcd_mode_w));
	map(0xff83, 0xff83).mirror(0x0c).w(FUNC(clcd_state::lcd_size_w));
}

/* Input ports */
// The keyboard has { } ~ _ marked, but there doesn't seem to be a way to type them in.
// Natural keyboard is set up for the various apps, but since BASIC is the usual Commodore Basic, uppercase turns into graphics and is unusable.
static INPUT_PORTS_START( clcd )
	PORT_START( "ROW0" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("1 !")             PORT_CODE(KEYCODE_1)            PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LEFT")            PORT_CODE(KEYCODE_LEFT)         PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DOWN")            PORT_CODE(KEYCODE_DOWN)         PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("9 )")             PORT_CODE(KEYCODE_9)            PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("7 \'")            PORT_CODE(KEYCODE_7)            PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("5 %")             PORT_CODE(KEYCODE_5)            PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("3 #")             PORT_CODE(KEYCODE_3)            PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DEL INST")        PORT_CODE(KEYCODE_BACKSPACE)    PORT_CHAR('\b') PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START( "ROW1" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("HOME CLR")        PORT_CODE(KEYCODE_HOME)         PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("*")               PORT_CODE(KEYCODE_BACKSLASH)    PORT_CHAR('*')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("P")               PORT_CODE(KEYCODE_P)            PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("I")               PORT_CODE(KEYCODE_I)            PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Y")               PORT_CODE(KEYCODE_Y)            PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("R")               PORT_CODE(KEYCODE_R)            PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("W")               PORT_CODE(KEYCODE_W)            PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RETURN")          PORT_CODE(KEYCODE_ENTER)        PORT_CHAR('\r')

	PORT_START( "ROW2" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F2")              PORT_CODE(KEYCODE_F2)           PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("; ]")             PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_CHAR(';') PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("L")               PORT_CODE(KEYCODE_L)            PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("J")               PORT_CODE(KEYCODE_J)            PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("G")               PORT_CODE(KEYCODE_G)            PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D")               PORT_CODE(KEYCODE_D)            PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A")               PORT_CODE(KEYCODE_A)            PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("TAB")             PORT_CODE(KEYCODE_TAB)          PORT_CHAR('\t')

	PORT_START( "ROW3" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("2 \"")            PORT_CODE(KEYCODE_2)            PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RIGHT")           PORT_CODE(KEYCODE_RIGHT)        PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("UP")              PORT_CODE(KEYCODE_UP)           PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("0 ^")             PORT_CODE(KEYCODE_0)            PORT_CHAR('0') PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("8 (")             PORT_CODE(KEYCODE_8)            PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("6 &")             PORT_CODE(KEYCODE_6)            PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("4 $")             PORT_CODE(KEYCODE_4)            PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F7")              PORT_CODE(KEYCODE_F7)           PORT_CHAR(UCHAR_MAMEKEY(F7))

	PORT_START( "ROW4" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPACE")           PORT_CODE(KEYCODE_SPACE)        PORT_CHAR(' ')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ESC")             PORT_CODE(KEYCODE_ESC)          PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(". >")             PORT_CODE(KEYCODE_STOP)         PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("M")               PORT_CODE(KEYCODE_M)            PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("B")               PORT_CODE(KEYCODE_B)            PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C")               PORT_CODE(KEYCODE_C)            PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Z")               PORT_CODE(KEYCODE_Z)            PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F1")              PORT_CODE(KEYCODE_F1)           PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_START( "ROW5" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F8")              PORT_CODE(KEYCODE_F8)           PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("=")               PORT_CODE(KEYCODE_EQUALS)       PORT_CHAR('=')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(": [")             PORT_CODE(KEYCODE_OPENBRACE)    PORT_CHAR(':') PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("K")               PORT_CODE(KEYCODE_K)            PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("H")               PORT_CODE(KEYCODE_H)            PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F")               PORT_CODE(KEYCODE_F)            PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("S")               PORT_CODE(KEYCODE_S)            PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F3")              PORT_CODE(KEYCODE_F3)           PORT_CHAR(UCHAR_MAMEKEY(F3))

	PORT_START( "ROW6" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Q")               PORT_CODE(KEYCODE_Q)            PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("+")               PORT_CODE(KEYCODE_PLUS_PAD)     PORT_CHAR('+')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("-")               PORT_CODE(KEYCODE_MINUS)        PORT_CHAR('-')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("O")               PORT_CODE(KEYCODE_O)            PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("U")               PORT_CODE(KEYCODE_U)            PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("T")               PORT_CODE(KEYCODE_T)            PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("E")               PORT_CODE(KEYCODE_E)            PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F5")              PORT_CODE(KEYCODE_F5)           PORT_CHAR(UCHAR_MAMEKEY(F5))

	PORT_START( "ROW7" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F6")              PORT_CODE(KEYCODE_F6)           PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("/ ?")             PORT_CODE(KEYCODE_SLASH)        PORT_CHAR('/') PORT_CHAR('?') PORT_CHAR('_')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(", <")             PORT_CODE(KEYCODE_COMMA)        PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("N")               PORT_CODE(KEYCODE_N)            PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("V")               PORT_CODE(KEYCODE_V)            PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("X")               PORT_CODE(KEYCODE_X)            PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F4")              PORT_CODE(KEYCODE_F4)           PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("@")               PORT_CODE(KEYCODE_QUOTE)        PORT_CHAR('@')

	PORT_START( "SPECIAL" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("STOP")            PORT_CODE(KEYCODE_END)          PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CAPSLOCK")        PORT_CODE(KEYCODE_CAPSLOCK)     PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LEFT SHIFT")      PORT_CODE(KEYCODE_LSHIFT)       PORT_CHAR(UCHAR_MAMEKEY(LSHIFT),UCHAR_SHIFT_1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CONTROL")         PORT_CODE(KEYCODE_LCONTROL)     PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CBM")             PORT_CODE(KEYCODE_LALT)         PORT_CHAR(UCHAR_MAMEKEY(LALT))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("POWER")           PORT_CODE(KEYCODE_F9)           PORT_WRITE_LINE_MEMBER(FUNC(clcd_state::write_power))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN ) // lo batt
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN ) // no batt
INPUT_PORTS_END

void clcd_state::clcd(machine_config &config)
{
	// basic machine hardware
	G65SC102(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &clcd_state::clcd_mem);

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline("maincpu", g65sc102_device::IRQ_LINE);

	via6522_device &via0(R65C22(config, "via0", 4_MHz_XTAL / 4));
	via0.writepa_handler().set(FUNC(clcd_state::via0_pa_w));
	via0.writepb_handler().set(FUNC(clcd_state::via0_pb_w));
	via0.readpb_handler().set(FUNC(clcd_state::via0_pb_r));
	via0.cb1_handler().set(FUNC(clcd_state::via0_cb1_w));
	via0.irq_handler().set("mainirq", FUNC(input_merger_device::in_w<0>));

	via6522_device &via1(R65C22(config, "via1", 4_MHz_XTAL / 4));
	via1.writepa_handler().set(FUNC(clcd_state::via1_pa_w));
	via1.writepb_handler().set(FUNC(clcd_state::via1_pb_w));
	via1.irq_handler().set("mainirq", FUNC(input_merger_device::in_w<1>));
	via1.ca2_handler().set(m_centronics, FUNC(centronics_device::write_strobe)).invert();
	via1.cb2_handler().set("speaker", FUNC(speaker_sound_device::level_w));

	MOS6551(config, m_acia, 4_MHz_XTAL / 4);
	m_acia->set_xtal(1.8432_MHz_XTAL);
	m_acia->irq_handler().set("mainirq", FUNC(input_merger_device::in_w<2>));
	m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_acia->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_acia->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_acia, FUNC(mos6551_device::write_rxd));
	rs232.dcd_handler().set(m_acia, FUNC(mos6551_device::write_dcd));
	rs232.dsr_handler().set(m_acia, FUNC(mos6551_device::write_dsr));
	rs232.cts_handler().set("via1", FUNC(via6522_device::write_pb4));

	CENTRONICS(config, m_centronics, centronics_devices, nullptr);
	m_centronics->busy_handler().set("via1", FUNC(via6522_device::write_pb6)).invert();

	for (auto &bankdev : m_bankdev)
	{
		ADDRESS_MAP_BANK(config, bankdev);
		bankdev->set_addrmap(AS_PROGRAM, &clcd_state::clcd_banked_mem);
		bankdev->set_endianness(ENDIANNESS_LITTLE);
		bankdev->set_data_width(8);
		bankdev->set_stride(0x400);
	}

	MSM58321(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->d0_handler().set("via1", FUNC(via6522_device::write_pa0));
	m_rtc->d1_handler().set("via1", FUNC(via6522_device::write_pa1));
	m_rtc->d2_handler().set("via1", FUNC(via6522_device::write_pa2));
	m_rtc->d3_handler().set("via1", FUNC(via6522_device::write_pa3));
	m_rtc->busy_handler().set("via1", FUNC(via6522_device::write_pa7));
	m_rtc->set_year0(1984);
	m_rtc->set_default_24h(true);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(80);
	screen.set_screen_update(FUNC(clcd_state::screen_update));
	screen.set_size(480, 128);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(clcd_state::clcd_palette), 2);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.25);

	RAM(config, "ram").set_default_size("128K").set_extra_options("32K,64K").set_default_value(0);

	NVRAM(config, "nvram").set_custom_handler(FUNC(clcd_state::nvram_init));

	cbm_iec_slot_device::add(config, m_iec, nullptr);
}


ROM_START( clcd )
	ROM_REGION( 0x20000, "maincpu", 0 )

	ROM_SYSTEM_BIOS( 0, "apr85", "Bil Herd Prototype" )
	ROMX_LOAD( "ss,calc 13apr.u105",    0x000000, 0x008000, CRC(88a587a7) SHA1(b08f3169b7cd696bb6a9b6e6e87a077345377ac4), ROM_BIOS(0) )
	ROMX_LOAD( "wp,t,m 13apr.u104",     0x008000, 0x008000, CRC(41028c3c) SHA1(fcab6f0bbeef178eb8e5ecf82d9c348d8f318a8f), ROM_BIOS(0) )
	ROMX_LOAD( "s12apr.u103",           0x010000, 0x008000, CRC(0aa91d9f) SHA1(f0842f370607f95d0a0ec6afafb81bc063c32745), ROM_BIOS(0) )
	ROMX_LOAD( "k12apr.u102",           0x018000, 0x008000, CRC(59103d52) SHA1(e49c20b237a78b54c2cb26b133d5903bb60bd8ef), ROM_BIOS(0) )
	// Patch RTC register table by swapping day & month values
	ROMX_FILL( 0x1c216, 1, 0x09, ROM_BIOS(0) )
	ROMX_FILL( 0x1c217, 1, 0x07, ROM_BIOS(0) )

	ROM_SYSTEM_BIOS( 1, "may85", "Jeff Porter prototype" )
	ROMX_LOAD( "s 3-24-85.u108",        0x000000, 0x008000, CRC(52db0ee9) SHA1(bea1e04fb88d205ebac7a1dbe2f5e98f84e7a3a7), ROM_BIOS(1) )
	ROMX_LOAD( "calc.u107",             0x008000, 0x008000, CRC(c1a09460) SHA1(5fe08cd7a075e33164edef60e18f090163bbf35c), ROM_BIOS(1) )
	ROMX_LOAD( "term,wp 5-30.u106",     0x010000, 0x008000, CRC(8dfabe3c) SHA1(da7f65edb0613ae10f702ed479b68ce17cf4ef97), ROM_BIOS(1) )
	ROMX_LOAD( "k5-28 newi_o.u105",     0x018000, 0x008000, CRC(5a8728ad) SHA1(64fd82ab51fe51758d6df9abe826a10dafdc63a5), ROM_BIOS(1) )

	ROM_REGION( 0x800, "lcd_char_rom", 0 )
	ROMX_LOAD( "lcd-char-rom.u16",      0x000000, 0x000800, CRC(7b6d3867) SHA1(cb594801438849f933ddc3e64b03b56f42f59f09), ROM_BIOS(0) )
	ROM_IGNORE( 0x000800 )

	ROMX_LOAD( "char rom.u16",          0x000000, 0x000800, CRC(e010c384) SHA1(0b74a1fe7083614860d3f325d920e3c0281e23d6), ROM_BIOS(1) )
	ROM_IGNORE( 0x001800 )

	ROM_DEFAULT_BIOS("may85")
ROM_END

} // anonymous namespace


/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY                        FULLNAME           FLAGS */
COMP( 1985, clcd, 0,      0,      clcd,    clcd,  clcd_state, empty_init, "Commodore Business Machines", "LCD (prototype)", 0 )



clickstart.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood
/******************************************************************************

    Leapfrog Clickstart Emulation

         Die markings show "SunPlus QL8041C" (known as Sunplus SPG2??).
         The keyboard has a "SunPlus PU6583" MCU under a glob, and the
         mouse optical sensor is a N2163, probably from Agilent.

    ClickStart cartridges pinout:

         1 N/C          2 GND
         3 GND          4 VCC
         5 GND          6 VCC
         7 N/C          8 N/C
         9 N/C         10 N/C
        11 N/C         12 to 53
        13 A2          14 A1
        15 A4          16 A3
        17 A6          18 A5
        19 A17         20 A7
                 Key
        21 N/C         22 A18
        23 A19         24 A20
        25 A9          26 A8
        27 A11         28 A10
        29 A13         30 A12
        31 A15         32 A14
        33 D15         34 A16
        35 D14         36 D7
        37 D13         38 D6
        39 D12         40 D5
        41 D11         42 D4
        43 D10         44 D3
        45 D9          46 D2
        47 D8          48 D1
        49 N/C         50 D0
        51 /OE         52 A0
        53 to 12       54 N/C
        55 GND         56 N/C
        57 GND         58 N/C
        59 GND         60 GND

    Status:

         Some games have Checksums listed in the header area that appear to be
         like the byte checksums on the Radica games in vii.cpp, however the
         calculation doesn't add up correctly.  There is also a checksum in
         a footer area at the end of every ROM that does add up correctly in
         all cases.

         The ROM carts are marked for 4MByte ROMs at least so the sizes
         should be correct.

        What type of SPG is this?

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

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "cpu/unsp/unsp.h"

#include "machine/spg2xx.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class clickstart_state : public driver_device
{
public:
	clickstart_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_cart(*this, "cartslot")
		, m_system_region(*this, "maincpu")
		, m_io_mouse_x(*this, "MOUSEX")
		, m_io_mouse_y(*this, "MOUSEY")
		, m_keys(*this, "KEYS%u", 0U)
		, m_cart_region(nullptr)
		, m_mouse_x(0)
		, m_mouse_y(0)
		, m_mouse_button(0)
		, m_mouse_dx(0)
		, m_mouse_dy(0)
		, m_uart_tx_fifo_start(0)
		, m_uart_tx_fifo_end(0)
		, m_uart_tx_fifo_count(0)
		, m_uart_tx_timer(nullptr)
		, m_unk_portc_toggle(0)
	{ }

	void clickstart(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(mouse_update);
	DECLARE_INPUT_CHANGED_MEMBER(key_update);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void mem_map(address_map &map) ATTR_COLD;

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	uint16_t rom_r(offs_t offset);

	void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	void portb_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	void portc_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t porta_r(offs_t offset, uint16_t mem_mask = ~0);
	uint16_t portb_r(offs_t offset, uint16_t mem_mask = ~0);
	uint16_t portc_r(offs_t offset, uint16_t mem_mask = ~0);

	void chip_sel_w(uint8_t data);

	TIMER_CALLBACK_MEMBER(handle_uart_tx);
	void uart_tx_fifo_push(uint8_t value);

	void update_mouse_buffer();

	required_device<spg2xx_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<generic_slot_device> m_cart;
	required_memory_region m_system_region;
	required_ioport m_io_mouse_x;
	required_ioport m_io_mouse_y;
	required_ioport_array<3> m_keys;
	memory_region *m_cart_region;

	uint16_t m_mouse_x;
	uint16_t m_mouse_y;
	uint8_t m_mouse_button;
	int16_t m_mouse_dx;
	int16_t m_mouse_dy;

	uint8_t m_uart_tx_fifo[1024]; // arbitrary size
	uint8_t m_uart_tx_fifo_start;
	uint8_t m_uart_tx_fifo_end;
	uint8_t m_uart_tx_fifo_count;
	emu_timer *m_uart_tx_timer;

	uint16_t m_unk_portc_toggle;
};

void clickstart_state::machine_start()
{
	// if there's a cart, override the standard mapping
	if (m_cart && m_cart->exists())
	{
		std::string region_tag;
		m_cart_region = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());
	}

	save_item(NAME(m_mouse_x));
	save_item(NAME(m_mouse_y));
	save_item(NAME(m_mouse_button));
	save_item(NAME(m_mouse_dx));
	save_item(NAME(m_mouse_dy));

	save_item(NAME(m_uart_tx_fifo));
	save_item(NAME(m_uart_tx_fifo_start));
	save_item(NAME(m_uart_tx_fifo_end));
	save_item(NAME(m_uart_tx_fifo_count));

	save_item(NAME(m_unk_portc_toggle));

	m_uart_tx_timer = timer_alloc(FUNC(clickstart_state::handle_uart_tx), this);
	m_uart_tx_timer->adjust(attotime::never);
}

void clickstart_state::machine_reset()
{
	m_mouse_x = m_io_mouse_x->read();
	m_mouse_y = m_io_mouse_y->read();
	m_mouse_button = 0;
	m_mouse_dx = 0;
	m_mouse_dy = 0;

	std::fill(std::begin(m_uart_tx_fifo), std::end(m_uart_tx_fifo), 0);
	m_uart_tx_fifo_start = 0;
	m_uart_tx_fifo_end = 0;
	m_uart_tx_fifo_count = 0;
	m_uart_tx_timer->adjust(attotime::from_hz(3200/10), 0, attotime::from_hz(3200/10));

	m_unk_portc_toggle = 0;
}

DEVICE_IMAGE_LOAD_MEMBER(clickstart_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

TIMER_CALLBACK_MEMBER(clickstart_state::handle_uart_tx)
{
	if (m_uart_tx_fifo_count == 0)
		return;

	m_maincpu->uart_rx(m_uart_tx_fifo[m_uart_tx_fifo_start]);
	m_uart_tx_fifo_start = (m_uart_tx_fifo_start + 1) % std::size(m_uart_tx_fifo);
	m_uart_tx_fifo_count--;
}

void clickstart_state::uart_tx_fifo_push(uint8_t value)
{
	if (m_uart_tx_fifo_count >= std::size(m_uart_tx_fifo))
	{
		logerror("Warning: Trying to push too much data onto the mouse Tx FIFO, data will be lost.\n");
	}

	m_uart_tx_fifo[m_uart_tx_fifo_end] = value;
	m_uart_tx_fifo_end = (m_uart_tx_fifo_end + 1) % std::size(m_uart_tx_fifo);
	m_uart_tx_fifo_count++;
}

INPUT_CHANGED_MEMBER(clickstart_state::key_update)
{
	const size_t keycode = static_cast<size_t>(param);

	printf("keycode:%02x, oldval:%02x, newval:%02x\n", (uint8_t)keycode, oldval, newval);

	uint8_t buffer[6] = {};
	buffer[0] = 0x00;
	buffer[1] = 0x01;
	buffer[2] = newval ? keycode : 0x3f;
	buffer[3] = 0x2f;
	buffer[4] = 0x01;
	buffer[5] = 0x01;

	printf("Keyboard queueing: ");
	uint16_t sum = 0;
	for (int i = 0; i < 6; i++)
	{
		uart_tx_fifo_push(buffer[i] ^ 0xff);
		sum += buffer[i];
		printf("%02x/%02x ", buffer[i], buffer[i] ^ 0xff);
	}
	sum = (sum & 0xff) ^ 0xff;
	uart_tx_fifo_push((uint8_t)sum);
	printf("%02x\n", (uint8_t)sum);
}

INPUT_CHANGED_MEMBER(clickstart_state::mouse_update)
{
	m_mouse_button = newval ? (uint8_t)static_cast<size_t>(param) : 0;

	const uint16_t x_val(m_io_mouse_x->read());
	const uint16_t y_val(m_io_mouse_y->read());
	int16_t x_delta(x_val - m_mouse_x);
	int16_t y_delta(y_val - m_mouse_y);

	// deal with wraparound
	if (0x0200 <= x_delta)
		x_delta -= 0x400;
	else if (-0x0200 >= x_delta)
		x_delta += 0x400;
	if (0x100 <= y_delta)
		y_delta -= 0x200;
	else if (-0x100 >= y_delta)
		x_delta += 0x200;

	m_mouse_x = x_val;
	m_mouse_y = y_val;

	m_mouse_dx = x_delta;
	m_mouse_dy = -y_delta;

	if (m_mouse_dx < -255)
		m_mouse_dx = -255;
	else if (m_mouse_dx > 255)
		m_mouse_dx = 255;

	if (m_mouse_dy < -255)
		m_mouse_dy = -255;
	else if (m_mouse_dy > 255)
		m_mouse_dy = 255;

	update_mouse_buffer();

	m_mouse_dx = 0;
	m_mouse_dy = 0;
}

void clickstart_state::update_mouse_buffer()
{
	uint8_t buffer[6] = {};
	buffer[0] = 0x00;
	buffer[1] = 0x01 | m_mouse_button;
	buffer[2] = 0x3e | ((m_mouse_dx >> 1) & 0x80);
	buffer[3] = 0x3f | ((m_mouse_dy >> 1) & 0x80);
	buffer[4] = (m_mouse_dx & 0xfe) + 1;
	buffer[5] = (m_mouse_dy & 0xfe) + 1;

	uint16_t sum = 0;
	for (int i = 0; i < 6; i++)
	{
		uart_tx_fifo_push(buffer[i] ^ 0xff);
		sum += buffer[i];
	}
	sum = (sum & 0xff) ^ 0xff;
	uart_tx_fifo_push((uint8_t)sum);
}

uint16_t clickstart_state::rom_r(offs_t offset)
{
	if (offset < 0x400000 / 2)
	{
		if (m_cart->exists())
			return ((uint16_t*)m_cart_region->base())[offset];
		else
			return ((uint16_t*)m_system_region->base())[offset];
	}
	else
	{
		return ((uint16_t*)m_system_region->base())[offset];
	}
}

void clickstart_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	//logerror("%s: porta_w: %04x & %04x\n", machine().describe_context(), data, mem_mask);
}

void clickstart_state::portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	//logerror("%s: portb_w: %04x & %04x\n", machine().describe_context(), data, mem_mask);
}

void clickstart_state::portc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	// Bit 12: SCK from SPG SIO
	// Bit 11: SDA from SPG SIO
	//logerror("%s: portc_w: %04x & %04x\n", machine().describe_context(), data, mem_mask);
}

uint16_t clickstart_state::porta_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t data = 0x4000;
	logerror("%s: porta_r: %04x & %04x\n", machine().describe_context(), data, mem_mask);
	return data;
}

uint16_t clickstart_state::portb_r(offs_t offset, uint16_t mem_mask)
{
	//logerror("%s: portb_r: %04x\n", machine().describe_context(), mem_mask);
	return 0;
}

uint16_t clickstart_state::portc_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t data = m_unk_portc_toggle;
	m_unk_portc_toggle ^= 0x0400;
	//logerror("%s: portc_r: %04x & %04x\n", machine().describe_context(), data, mem_mask);
	return data;
}

void clickstart_state::chip_sel_w(uint8_t data)
{
	// Seems unused, currently
}

void clickstart_state::mem_map(address_map &map)
{
	map(0x000000, 0x3fffff).r(FUNC(clickstart_state::rom_r));
}

static INPUT_PORTS_START( clickstart )
	PORT_START("MOUSEX")
	PORT_BIT(0x3ff, 0x00, IPT_MOUSE_X) PORT_MINMAX(0x0000,0x03ff) PORT_SENSITIVITY(10) PORT_KEYDELTA(10) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::mouse_update), 0)

	PORT_START("MOUSEY")
	PORT_BIT(0x1ff, 0x00, IPT_MOUSE_Y) PORT_MINMAX(0x0000,0x01ff) PORT_SENSITIVITY(10) PORT_KEYDELTA(10) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::mouse_update), 0)

	PORT_START("MOUSEBTN")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::mouse_update), 0x10)
	PORT_BIT(0xfffe, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("KEYS0")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x01) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x02) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x03) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x04) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x05) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x06) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x07) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x08) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x09) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x0a) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x0b) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x0c) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x0d) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x0e) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x0f) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x10) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')

	PORT_START("KEYS1")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x11) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('q')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x12) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('r')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x13) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('s')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x14) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('t')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x15) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('u')
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x16) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('v')
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x17) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('w')
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x18) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('x')
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x19) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('y')
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x1a) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('z')
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x25) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x26) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x28) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x29) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x2a) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x2b) PORT_CODE(KEYCODE_5) PORT_CHAR('5')

	PORT_START("KEYS2")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x2d) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x2e) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x2f) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x24) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x27) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x2c) PORT_CODE(KEYCODE_LALT) PORT_NAME("Music")
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0xa9) PORT_CODE(KEYCODE_LSHIFT) PORT_NAME("Shift") PORT_TOGGLE
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(clickstart_state::key_update), 0x2f) PORT_CODE(KEYCODE_TILDE) PORT_NAME("Pause") PORT_TOGGLE
	PORT_BIT(0xff00, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

// There is a 24C08AN SEEPROM on the motherboard

void clickstart_state::clickstart(machine_config &config)
{
	SPG28X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &clickstart_state::mem_map);
	m_maincpu->porta_out().set(FUNC(clickstart_state::porta_w));
	m_maincpu->portb_out().set(FUNC(clickstart_state::portb_w));
	m_maincpu->portc_out().set(FUNC(clickstart_state::portc_w));
	m_maincpu->porta_in().set(FUNC(clickstart_state::porta_r));
	m_maincpu->portb_in().set(FUNC(clickstart_state::portb_r));
	m_maincpu->portc_in().set(FUNC(clickstart_state::portc_r));
	m_maincpu->adc_in<0>().set_constant(0x0fff);
	m_maincpu->chip_select().set(FUNC(clickstart_state::chip_sel_w));
	m_maincpu->add_route(ALL_OUTPUTS, "speaker", 0.5, 0);
	m_maincpu->add_route(ALL_OUTPUTS, "speaker", 0.5, 1);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320, 262);
	m_screen->set_visarea(0, 320-1, 0, 240-1);
	m_screen->set_screen_update("maincpu", FUNC(spg2xx_device::screen_update));
	m_screen->screen_vblank().set(m_maincpu, FUNC(spg2xx_device::vblank));

	SPEAKER(config, "speaker", 2).front();

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "clickstart_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(clickstart_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("clickstart_cart");
}

ROM_START( clikstrt )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "clickstartbios.bin", 0x000000, 0x800000, CRC(7c833bd0) SHA1(2e9ef38e1a7582705920339e6b9944f6404fcf9b) )
ROM_END

} // anonymous namespace


// year, name, parent, compat, machine, input, class, init, company, fullname, flags
CONS( 2007, clikstrt,  0,      0, clickstart,  clickstart, clickstart_state, empty_init, "LeapFrog Enterprises", "ClickStart",      MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING ) // 'My First Computer' tagline



clie_db.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:

/*
Skeleton driver for Sony Clie PDAs running on Motorola Dragonball CPU.
Later series ran on ARM based SoCs and should go in a separate driver.

Sony Clie PEG T650C main components (PCB YSX-1230 MP-43):
- Motorola Super VZ DragonBall MC68SZ328AVH66
- MediaQ MQ1100-CBC
- Sony CXD3523AGG
- Mosel-Vitelic V54C3128164VAT7
- Sony CXD1859GA
*/

#include "emu.h"

#include "machine/mc68328.h"

#include "screen.h"
#include "speaker.h"


namespace {

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

	void t650c(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;

	void program_map(address_map &map) ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};


uint32_t clie_db_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}


void clie_db_state::program_map(address_map &map)
{
	map(0x00000000, 0x007fffff).rom();
}


static INPUT_PORTS_START( t650c )
INPUT_PORTS_END

void clie_db_state::t650c(machine_config &config)
{
	MC68EZ328(config, m_maincpu, 66'000'000); // unknown clock, 66 MHz according to flyer
	m_maincpu->set_addrmap(AS_PROGRAM, &clie_db_state::program_map);

	screen_device&screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_size(320, 320); // 320 x 320 according to flyer
	screen.set_visarea(0, 320 - 1, 0, 320 - 1);
	screen.set_screen_update(FUNC(clie_db_state::screen_update));

	// TODO: MediaQ MQ1100-CB LCD controller

	SPEAKER(config, "speaker").front_center();
	// TODO: CXD1859GA audio chip
}


ROM_START( t650c )
	ROM_REGION16_BE( 0x800000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "sony_clie_peg_t650c_flashrom.bin", 0x000000, 0x800000, CRC(60855a64) SHA1(e08350e64438c62401041aaa335def08aa0decb7) ) // verified factory defaulted image
ROM_END

} // anonymous namespace


SYST( 2002, t650c, 0, 0, t650c, t650c, clie_db_state, empty_init, "Sony", "Clie PEG-T650C", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



clxvme186.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for 80186-based VMEbus controller by Colex, Inc.

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

#include "emu.h"
#include "cpu/i86/i186.h"
//#include "bus/vme/vme.h"
#include "machine/74259.h"
#include "machine/m3002.h"
#include "machine/z80scc.h"


namespace {

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

	void clxvme186(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void unknown_w(u16 data);
	u8 sasi_status_r();

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<i80186_cpu_device> m_maincpu;
};


void clxvme186_state::machine_start()
{
}


void clxvme186_state::unknown_w(u16 data)
{
}

u8 clxvme186_state::sasi_status_r()
{
	return 0;
}

void clxvme186_state::mem_map(address_map &map)
{
	map(0x00000, 0x3ffff).ram(); // On-board RAM
	map(0x40000, 0xbffff).unmaprw(); // Off-board memory
	map(0xc0000, 0xc7fff).unmapr(); // Vector interrupt input
	map(0xc8000, 0xcffff).unmaprw(); // VMEbus short I/O
	map(0xf0000, 0xf7fff).mirror(0x8000).rom().region("eprom", 0); // On-board EPROM
}

void clxvme186_state::io_map(address_map &map)
{
	map(0xe000, 0xe001).unmaprw(); // SASI data port (PCS0)
	map(0xe080, 0xe087).rw("scc", FUNC(scc8530_device::ab_dc_r), FUNC(scc8530_device::ab_dc_w)).umask16(0x00ff); // Serial I/O ports A & B (PCS1)
	map(0xe100, 0xe100).rw("rtc", FUNC(m3000_device::read), FUNC(m3000_device::write)); // Real time clock (PCS2)
	map(0xe180, 0xe181).portr("TTL"); // TTL input port (PCS3)
	map(0xe180, 0xe181).w(FUNC(clxvme186_state::unknown_w));
	map(0xe190, 0xe190).r(FUNC(clxvme186_state::sasi_status_r)); // SASI status port (PCS3)
	map(0xe190, 0xe19f).w("ctrllatch", FUNC(ls259_device::write_d0)).umask16(0x00ff); // Control ports (PCS3)
	map(0xe200, 0xe207).unmapw(); // Register files (PCS4)
	map(0xe280, 0xe281).unmapw(); // Printer data port (PCS5)
}


static INPUT_PORTS_START(clxvme186)
	PORT_START("TTL")
	PORT_BIT(0x0001, IP_ACTIVE_LOW, IPT_UNKNOWN) // P6 X data
	PORT_BIT(0x0002, IP_ACTIVE_LOW, IPT_UNKNOWN) // P6 Y data
	PORT_BIT(0x0004, IP_ACTIVE_LOW, IPT_UNKNOWN) // P6 M data
	PORT_BIT(0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN) // P6 D data
	PORT_BIT(0x0010, IP_ACTIVE_LOW, IPT_UNKNOWN) // P6 G data
	PORT_BIT(0x0020, IP_ACTIVE_LOW, IPT_UNKNOWN) // Printer busy
	PORT_BIT(0x0040, IP_ACTIVE_LOW, IPT_UNKNOWN) // Printer paper empty
	PORT_BIT(0x0080, IP_ACTIVE_LOW, IPT_UNKNOWN) // /SYSFAIL
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("rtc", FUNC(m3000_device::busy_r))
	PORT_BIT(0x0200, IP_ACTIVE_LOW, IPT_UNKNOWN) // Colex use only
	PORT_BIT(0x0400, IP_ACTIVE_LOW, IPT_UNKNOWN) // Colex use only
	PORT_BIT(0x0800, IP_ACTIVE_LOW, IPT_UNKNOWN) // /ACFAIL
	PORT_BIT(0x1000, IP_ACTIVE_LOW, IPT_UNKNOWN) // TTL input bit P2 pin 7c
	PORT_BIT(0xe000, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END


void clxvme186_state::clxvme186(machine_config &config)
{
	I80186(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &clxvme186_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &clxvme186_state::io_map);

	SCC8530(config, "scc", 3.6864_MHz_XTAL);

	M3000(config, "rtc", 32.768_kHz_XTAL);

	LS259(config, "ctrllatch");
}


ROM_START(clxvme186)
	ROM_REGION16_LE(0x8000, "eprom", 0)
	ROM_LOAD16_BYTE("clxu37.bin", 0x0000, 0x4000, CRC(9308130c) SHA1(a0230f8e9943a0c15cac365d4c58614e7befde59))
	ROM_LOAD16_BYTE("clxu53.bin", 0x0001, 0x4000, CRC(66bf7cc5) SHA1(cfe8df9260b565840249256a30631a34e931c886))
	ROM_IGNORE(0x4000)
ROM_END

} // anonymous namespace


COMP(1983, clxvme186, 0, 0, clxvme186, clxvme186, clxvme186_state, empty_init, "Colex", "Colex VME-80186", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



cm1800.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

CM-1800
(note name is in cyrilic letters)

more info at http://ru.wikipedia.org/wiki/%D0%A1%D0%9C_%D0%AD%D0%92%D0%9C
         and http://www.computer-museum.ru/histussr/sm1800.htm

2011-04-26 Skeleton driver.

Commands to be in uppercase:
C Compare
D Dump
F Fill
G
I
L
M Move
S Edit
T
X Registers

For most commands, enter all 4 digits of each hex address, the system will
add the appropriate spacing as you type. No need to press Enter.

The L command looks like it might be for loading a file, for example
L ABC will read/write to port 70,71,73 and eventually time out if you wait
a while. No idea if it wants to read a disk or a tape. There doesn't seem
to be a save command.

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


#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/ay31015.h"
#include "machine/clock.h"
#include "bus/rs232/rs232.h"


namespace {

class cm1800_state : public driver_device
{
public:
	cm1800_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_uart(*this, "uart")
	{ }

	void cm1800(machine_config &config);

private:
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	u8 uart_status_r();
	required_device<cpu_device> m_maincpu;
	required_device<ay31015_device> m_uart;
};

u8 cm1800_state::uart_status_r()
{
	return (m_uart->dav_r()) | (m_uart->tbmt_r() << 2);
}

void cm1800_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).rom().region("roms", 0);
	map(0x0800, 0xffff).ram();
}

void cm1800_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x00).rw(m_uart, FUNC(ay31015_device::receive), FUNC(ay31015_device::transmit));
	map(0x01, 0x01).r(FUNC(cm1800_state::uart_status_r));
}

/* Input ports */
static INPUT_PORTS_START( cm1800 )
INPUT_PORTS_END


void cm1800_state::machine_reset()
{
	m_uart->write_xr(0);
	m_uart->write_xr(1);
	m_uart->write_swe(0);
	m_uart->write_np(1);
	m_uart->write_tsb(0);
	m_uart->write_nb1(1);
	m_uart->write_nb2(1);
	m_uart->write_eps(1);
	m_uart->write_cs(1);
	m_uart->write_cs(0);
}

void cm1800_state::cm1800(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, XTAL(2'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &cm1800_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &cm1800_state::io_map);

	/* video hardware */
	AY51013(config, m_uart); // exact uart type is unknown
	m_uart->read_si_callback().set("rs232", FUNC(rs232_port_device::rxd_r));
	m_uart->write_so_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	m_uart->set_auto_rdav(true);

	clock_device &uart_clock(CLOCK(config, "uart_clock", 153600));
	uart_clock.signal_handler().set(m_uart, FUNC(ay31015_device::write_tcp));
	uart_clock.signal_handler().append(m_uart, FUNC(ay31015_device::write_rcp));

	RS232_PORT(config, "rs232", default_rs232_devices, "terminal");
}

/* ROM definition */
ROM_START( cm1800 )
	ROM_REGION( 0x800, "roms", ROMREGION_ERASEFF )
	ROM_LOAD( "cm1800.rom", 0x0000, 0x0800, CRC(85d71d25) SHA1(42dc87d2eddc2906fa26d35db88a2e29d50fb481) )
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY      FULLNAME   FLAGS */
COMP( 1981, cm1800, 0,      0,      cm1800,  cm1800, cm1800_state, empty_init, "<unknown>", "CM-1800", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



cmi.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Phil Bennett
/***************************************************************************

    Fairlight CMI Series

    driver by Phil Bennett

    Systems supported:

        * CMI IIx

    To do:
    * V12 system software reports that it can't load MIDI support and then hangs.

    Information from:

             CMI SYSTEM SERVICE MANUAL
        FAIRLIGHT INSTRUMENTS, FEBRUARY 1985
                  Revision 2.1

    This document is available on archive.org at the following URL:

    https://archive.org/details/fairlight_CMI-IIx_SERVICE_MANUAL

    Summary:

        The Fairlight CMI system conists typically of:
            - One velocity-sensitive unweighted keyboard, with a numeric
              keypad and several control surfaces
            - (Optionally) one additional keyboard, not velocity-sensitive
            - One alphanumeric keyboard for manual control
            - A 15-inch green-screen monitor and light pen for more direct
              control
            - A box consisting of:
              * An audio board including balanced line drivers for eight
                channels and mixed output
              * A 500-watt power supply
              * A 21-slot backplane
              * Two 8-inch double-density floppy disk drives. The format
                used is soft-sectored, 128 bytes per sector (single density),
                or 256 bytes per sector (double density), using FM
                recording.
              * And the following cards:
                Slot  1: Master Card CMI-02
                Slot  2: General Interface Card CMI-08/28 (Optional)
                Slots 3-11: 8 Channel Controller Cards & 1 Voice Master Module Card, order unknown
                Slot 12: 64K System RAM Q-096
                Slot 13: 256K System RAM Q-256
                Slot 14: 256K System RAM Q-256
                Slot 15: 4-Port ACIA Module Q-014 (Optional)
                Slot 16: Processor Control Module Q-133
                Slot 17: Central Processor Module Q-209
                Slot 18: Lightpen/Graphics Interface Q-219
                Slot 19: Floppy Disk Controller QFC-9
                Slot 20: Hard Disk Controller Q-077 (Optional)

        Q209 Dual 6809 Central Processor Card
        -------------------------------------

        The CPU card has two 6809 processors, with robust inter-CPU
        communications capabilities including:
            - Uninterruptible instructions
            - CPU-specific ID register and memory map registers
            - Interprocessor interrupts
            - Automatic memory map-switching register

        The CPUs are multiplexed onto the address and data buses
        in an interleaved manner such that there is no contention
        on simultaneous memory accesses.

        All system timing is derived from a 40MHz clock crystal, which
        is divided into two opposite-phase 20MHz squre waves.

        Other data entry from service manual to be completed later - RH 12 Aug 2016

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

#include "emu.h"

#include "cmi01a.h"
#include "cmi_ankbd.h"
#include "cmi_mkbd.h"

#include "bus/midi/midi.h"

#include "cpu/m6809/m6809.h"
#include "cpu/m68000/m68000.h"

#include "imagedev/floppy.h"
#include "machine/6821pia.h"
#include "machine/6850acia.h"
#include "machine/6840ptm.h"
#include "machine/74259.h"
#include "machine/7474.h"
#include "machine/bankdev.h"
#include "machine/clock.h"
#include "machine/i8214.h"
#include "machine/input_merger.h"
#include "machine/mos6551.h"
#include "machine/msm5832.h"
#include "machine/wd_fdc.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#define LOG_CHANNELS (1U << 1)

#define VERBOSE     (0)
#include "logmacro.h"


namespace {

#define Q209_CPU_CLOCK          (40.21_MHz_XTAL / 40) // verified by manual
#define SYSTEM_CAS_CLOCK        (40.21_MHz_XTAL / 20) // likewise

#define M6809_CLOCK             8000000 // wrong
#define MASTER_OSCILLATOR       34.291712_MHz_XTAL

#define CPU_1                   0
#define CPU_2                   1

#define MAPPING_A               1
#define MAPPING_B               0

#define NUM_Q256_CARDS          1   // Max of 2
#define NUM_CHANNEL_CARDS       8

#define PAGE_SIZE               2048
#define PAGE_COUNT              (65536 / PAGE_SIZE)
#define PAGE_MASK               (PAGE_SIZE - 1)
#define PAGE_SHIFT              5

#define PIXEL_CLOCK             10.38_MHz_XTAL
#define HTOTAL                  672
#define HBLANK_END              0
#define HBLANK_START            512
#define VTOTAL                  304
#define VBLANK_END              0
#define VBLANK_START            256

#define HBLANK_FREQ             (PIXEL_CLOCK / HTOTAL)
#define VBLANK_FREQ             (HBLANK_FREQ / VTOTAL)

#define MAPSEL_P2_B             0x00
#define MAPSEL_P2_A             0x03
#define MAPSEL_P2_A_DMA1        0x04
#define MAPSEL_P2_A_DMA2        0x05
#define MAPSEL_P2_A_DMA3        0x06
#define MAPSEL_P2_A_DMA4        0x07
#define MAPSEL_P1_B             0x08
#define MAPSEL_P1_A             0x0b
#define MAPSEL_P1_A_DMA1        0x0c
#define MAPSEL_P1_A_DMA2        0x0d
#define MAPSEL_P1_A_DMA3        0x0e
#define MAPSEL_P1_A_DMA4        0x0f

#define IRQ_ACINT_LEVEL         (0 ^ 7)
#define IRQ_MIDINT_LEVEL        (0 ^ 7)
#define IRQ_TIMINT_LEVEL        (1 ^ 7)
#define IRQ_INTP1_LEVEL         (2 ^ 7)
#define IRQ_IPI1_LEVEL          (3 ^ 7)
#define IRQ_SMIDINT_LEVEL       (3 ^ 7)
#define IRQ_AIC_LEVEL           (4 ^ 7)

#define IRQ_PERRINT_LEVEL       (0 ^ 7)
#define IRQ_RTCINT_LEVEL        (0 ^ 7)
#define IRQ_RINT_LEVEL          (1 ^ 7)
#define IRQ_INTP2_LEVEL         (2 ^ 7)
#define IRQ_IPI2_LEVEL          (3 ^ 7)
#define IRQ_TOUCHINT_LEVEL      (4 ^ 7)
#define IRQ_PENINT_LEVEL        (5 ^ 7)
#define IRQ_ADINT_LEVEL         (6 ^ 7)
#define IRQ_DISKINT_LEVEL       (7 ^ 7)

#define FDC_CONTROL_INTEN       (1 << 2)

#define FDC_STATUS_READY        (1 << 3)
#define FDC_STATUS_TWO_SIDED    (1 << 4)
#define FDC_STATUS_DISK_CHANGE  (1 << 5)
#define FDC_STATUS_INTERRUPT    (1 << 6)
#define FDC_STATUS_DRIVER_LOAD  (1 << 7)

class cmi_state : public driver_device
{
public:
	cmi_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu1(*this, "maincpu1")
		, m_maincpu2(*this, "maincpu2")
		, m_midicpu(*this, "smptemidi")
		, m_cmi07cpu(*this, "cmi07cpu")
		, m_maincpu1_irq_merger(*this, "maincpu1_irq_merger")
		, m_maincpu2_irq0_merger(*this, "maincpu2_irq0_merger")
		, m_msm5832(*this, "msm5832")
		, m_i8214(*this, "i8214_%u", 1U)
		, m_q133_pia(*this, "q133_pia_%u", 1U)
		, m_q133_ptm(*this, "q133_ptm")
		, m_q133_acia(*this, "q133_acia_%u", 0U)
		, m_q133_region(*this, "q133")
		, m_q219_pia(*this, "q219_pia")
		, m_q219_ptm(*this, "q219_ptm")
		, m_cmi02_pia(*this, "cmi02_pia_%u", 1U)
		, m_cmi02_ptm(*this, "cmi02_ptm")
		, m_cmi07_ptm(*this, "cmi07_ptm")
		, m_midi_ptm(*this, "midi_ptm_%u", 1U)
		, m_midi_acia(*this, "midi_acia_%u", 1U)
		, m_midi_out(*this, "midi_out_%u", 1U)
		, m_midi_in(*this, "midi_in_%u", 1U)
		, m_qfc9_region(*this, "qfc9")
		, m_floppy(*this, "wd1791:%u", 0U)
		, m_wd1791(*this, "wd1791")
		, m_channels(*this, "cmi01a_%u", 0)
		, m_screen(*this, "screen")
		, m_palette(*this, "palette")
		, m_lp_x_port(*this, "LP_X")
		, m_lp_y_port(*this, "LP_Y")
		, m_lp_touch_port(*this, "LP_TOUCH")
		, m_cmi07_ram(*this, "cmi07_ram")
		, m_cpu_periphs(*this, "cpu%u_periphs", 1U)
	{
	}

	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

	void set_interrupt(int cpunum, int level, int state);

	void init_cmi2x();

	// CPU card
	void q133_acia_irq(int state);
	void i8214_cpu1_w(u8 data);
	void i8214_cpu2_w(u8 data);
	void maincpu2_irq0_w(int state);
	void i8214_1_int_w(int state);
	void i8214_2_int_w(int state);
	void i8214_3_int_w(int state);
	void i8214_3_enlg(int state);
	u8 shared_ram_r(offs_t offset);
	void shared_ram_w(offs_t offset, u8 data);
	template<int CpuNum> u8 perr_r(offs_t offset);
	template<int CpuNum> void perr_w(offs_t offset, u8 data);

	u16 m_aic_ad565_in[16]{};
	u8 m_aic_mux_latch = 0;

	u8 aic_ad574_r();
	template<int Dac> void aic_dac_w(u8 data);
	void aic_mux_latch_w(u8 data);
	void aic_ad565_msb_w(u8 data);
	void aic_ad565_lsb_w(u8 data);

	u8 q133_1_porta_r();
	void q133_1_porta_w(u8 data);
	void q133_1_portb_w(u8 data);

	void cmi_iix_vblank(int state);
	u8 vfetch1_r(offs_t offset);
	u8 vfetch2_r(offs_t offset);

	// Video-related
	u8 video_r(offs_t offset);
	u8 lightpen_r(offs_t offset);
	u8 pia_q219_b_r();
	void video_w(offs_t offset, u8 data);
	void vscroll_w(u8 data);
	void video_attr_w(u8 data);

	u8 vram_r(offs_t offset);
	void vram_w(offs_t offset, u8 data);

	template <int CpuNum, u16 base> u8 ram_range_r(offs_t offset);
	template <int CpuNum, u16 base> void ram_range_w(offs_t offset, u8 data);
	template <int CpuNum> u8 vram_range_r(offs_t offset);
	template <int CpuNum> void vram_range_w(offs_t offset, u8 data);
	template <int CpuNum> u8 cards_range_r(offs_t offset);
	template <int CpuNum> void cards_range_w(offs_t offset, u8 data);
	template <int CpuNum> u8 periphs_range_r(offs_t offset);
	template <int CpuNum> void periphs_range_w(offs_t offset, u8 data);

	[[maybe_unused]] u8 tvt_r();
	[[maybe_unused]] void tvt_w(u8 data);
	void pia_q219_irqa(int state);
	void pia_q219_irqb(int state);
	void ptm_q219_irq(int state);
	u32 screen_update_cmi2x(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	// Memory mapping
	template<int CpuNum> u8 rom_r(offs_t offset);
	void map_ram_w(offs_t offset, u8 data);
	template<int CpuNum> u8 map_r();
	template<int CpuNum> void map_w(u8 data);
	u8 atomic_r();
	template<int CpuNum> void ipi_w(int state);
	template<int CpuNum> void nmi_reset_w(int state);
	template<int CpuNum> void ms_w(int state);
	u8 parity_r(offs_t offset);
	void mapsel_w(offs_t offset, u8 data);
	template<int CpuNum> u8 scratch_ram_r(offs_t offset);
	template<int CpuNum> void scratch_ram_w(offs_t offset, u8 data);

	// MIDI/SMPTE
	void midi_dma_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	u16 midi_dma_r(offs_t offset);
	void midi_ptm0_c3_w(int state);
	void midi_latch_w(u8 data);

	// Floppy
	void fdc_w(offs_t offset, u8 data);
	u8 fdc_r(offs_t offset);
	void wd1791_irq(int state);
	void wd1791_drq(int state);

	// Master card
	u8 cmi02_r(offs_t offset);
	void cmi02_w(offs_t offset, u8 data);
	void cmi02_chsel_w(u8 data);
	u8 cmi02_chsel_r();
	void master_tune_w(u8 data);
	u8 master_tune_r();
	void cmi02_ptm_irq(int state);
	void cmi02_ptm_o2(int state);
	void cmi02_pia2_irqa_w(int state);
	void cmi02_pia2_cb2_w(int state);

	u8 cmi07_r();
	void cmi07_w(u8 data);

	void msm5832_irq_w(int state);
	void cmi07_irq(int state);
	void q133_acia_clock(int state);

	template<int Channel> void channel_irq(int state);

	void cmi2x(machine_config &config);
	void cmi07cpu_map(address_map &map) ATTR_COLD;
	void maincpu1_map(address_map &map) ATTR_COLD;
	void maincpu2_map(address_map &map) ATTR_COLD;
	void midicpu_map(address_map &map) ATTR_COLD;

	template <int CpuNum> void cpu_periphs_map(address_map &map) ATTR_COLD;

protected:
	required_device<mc6809e_device> m_maincpu1;
	required_device<mc6809e_device> m_maincpu2;
	required_device<m68000_device> m_midicpu;
	required_device<mc6809e_device> m_cmi07cpu;

	required_device<input_merger_any_high_device> m_maincpu1_irq_merger;
	required_device<input_merger_any_high_device> m_maincpu2_irq0_merger;
	required_device<msm5832_device> m_msm5832;
	required_device_array<i8214_device, 3> m_i8214;
	required_device_array<pia6821_device, 2> m_q133_pia;
	required_device<ptm6840_device> m_q133_ptm;
	required_device_array<mos6551_device, 4> m_q133_acia;
	required_memory_region m_q133_region;

	required_device<pia6821_device> m_q219_pia;
	required_device<ptm6840_device> m_q219_ptm;

	required_device_array<pia6821_device, 2> m_cmi02_pia;
	required_device<ptm6840_device> m_cmi02_ptm;

	required_device<ptm6840_device> m_cmi07_ptm;

	required_device_array<ptm6840_device, 2> m_midi_ptm;
	required_device_array<acia6850_device, 4> m_midi_acia;
	required_device_array<midi_port_device, 4> m_midi_out;
	required_device_array<midi_port_device, 3> m_midi_in;

	required_memory_region m_qfc9_region;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<fd1791_device> m_wd1791;

	required_device_array<cmi01a_device, 8> m_channels;

	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;

	required_ioport m_lp_x_port;
	required_ioport m_lp_y_port;
	required_ioport m_lp_touch_port;

	required_shared_ptr<u8> m_cmi07_ram;

	required_device_array<address_map_bank_device, 2> m_cpu_periphs;

	address_space *m_cpu1space = nullptr;
	address_space *m_cpu2space = nullptr;

private:
	emu_timer *m_map_switch_timer = nullptr;
	emu_timer *m_hblank_timer = nullptr;
	emu_timer *m_jam_timeout_timer = nullptr;

	u8 m_video_data = 0;

	// Memory
	TIMER_CALLBACK_MEMBER(switch_map);
	TIMER_CALLBACK_MEMBER(jam_timeout);
	bool map_is_active(int cpunum, int map, u8 *map_info);
	void update_address_space(int cpunum, u8 mapinfo);

	// Video
	TIMER_CALLBACK_MEMBER(hblank);
	template <int Y, int X, bool ByteSize> void update_video_pos();

	// Floppy
	void dma_fdc_rom();
	void write_fdc_ctrl(u8 data);
	void fdc_dma_transfer();

	// Q133 CPU Card
	u8 *m_q133_rom = nullptr;

	u16  m_int_state[2]{};
	u8   m_lp_int = 0;
	u8   m_hp_int = 0;
	std::unique_ptr<u8[]>    m_shared_ram;
	std::unique_ptr<u8[]>    m_scratch_ram[2];

	/* Memory management */
	u8   m_map_sel[16]{};
	std::unique_ptr<u8[]>    m_map_ram[2];
	std::unique_ptr<u8[]>    m_q256_ram[2];
	u8   m_map_ram_latch = 0;
	int     m_cpu_active_space[2]{};
	int     m_cpu_map_switch[2]{};
	u8 m_curr_mapinfo[2]{};

	/* Q219 lightpen/graphics card */
	std::unique_ptr<u8[]>    m_video_ram;
	u16  m_x_pos = 0;
	u8   m_y_pos = 0;
	u16  m_lp_x = 0;
	u8   m_lp_y = 0;
	u8   m_q219_b_touch = 0;

	/* QFC9 floppy disk controller card */
	u8 * m_qfc9_region_ptr = 0;
	int       m_fdc_drq = 0;
	u8   m_fdc_addr = 0;
	u8   m_fdc_ctrl = 0;
	u8   m_fdc_status = 0;
	PAIR      m_fdc_dma_addr{};
	PAIR      m_fdc_dma_cnt{};

	/* CMI-07 */
	u8   m_cmi07_ctrl = 0;
	bool      m_cmi07_base_enable[2]{};
	u16  m_cmi07_base_addr = 0;

	u8   m_msm5832_addr = 0;

	// Master card (CMI-02)
	int       m_cmi02_ptm_irq = 0;
	u8   m_cmi02_pia_chsel = 0;
	u8   m_master_tune = 0;
};

/**************************************
 *
 *  Video hardware
 *
 *************************************/

u32 cmi_state::screen_update_cmi2x(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const pen_t *pen = m_palette->pens();
	u8 y_scroll = m_q219_pia->a_output();
	u8 invert = BIT(~m_q219_pia->b_output(), 3);

	for (int y = cliprect.min_y; y <= cliprect.max_y; ++y)
	{
		u8 *src = &m_video_ram[(512/8) * ((y + y_scroll) & 0xff)];
		u32 *dest = &bitmap.pix(y, cliprect.min_x);

		for (int x = cliprect.min_x; x <= cliprect.max_x; x += 8)
		{
			u8 data = *src++;

			/* Store 8 pixels */
			for (int i = 0; i < 8; ++i)
				*dest++ = pen[BIT(data, 7 - i) ^ invert];
		}
	}

	/* Get lightpen position */
	//if (LPCEN && NOT_TOUCHING)
	if (m_lp_touch_port->read() && BIT(m_q219_pia->b_output(), 1))
	{
		/* Invert target pixel */
		bitmap.pix(m_lp_y_port->read(), m_lp_x_port->read()) ^= 0x00ffffff;
	}



	return 0;
}

TIMER_CALLBACK_MEMBER(cmi_state::hblank)
{
	int v = m_screen->vpos();

	if (!m_screen->vblank())
	{
		int _touch = m_lp_touch_port->read();
		int _tfh = !BIT(m_q219_pia->b_output(), 2);

		if (v == m_lp_y_port->read())
		{
			m_q219_b_touch = _touch ? 0 : (1 << 5);
			m_q219_pia->ca1_w(_touch ? 0 : 1);

			if (!_touch || !_tfh)
			{
				/* Latch the counters */
				m_lp_x = m_lp_x_port->read();
				m_lp_y = m_lp_y_port->read();

				/* LPSTB */
				m_q219_pia->cb1_w(1);
			}
		}
	}
	/* Adjust for next scanline */
	if (++v >= VTOTAL)
		v = 0;

	m_hblank_timer->adjust(m_screen->time_until_pos(v, HBLANK_START));

}

template void cmi_state::update_video_pos< 0,  0, false>();
template void cmi_state::update_video_pos< 0,  1, false>();
template void cmi_state::update_video_pos< 0, -1, false>();
template void cmi_state::update_video_pos< 0,  0, true>();
template void cmi_state::update_video_pos< 1,  0, false>();
template void cmi_state::update_video_pos< 1,  1, false>();
template void cmi_state::update_video_pos< 1, -1, false>();
template void cmi_state::update_video_pos< 1,  0, true>();
template void cmi_state::update_video_pos<-1,  0, false>();
template void cmi_state::update_video_pos<-1,  1, false>();
template void cmi_state::update_video_pos<-1, -1, false>();
template void cmi_state::update_video_pos<-1,  0, true>();

template <int Y, int X, bool ByteSize> void cmi_state::update_video_pos()
{
	u8 *video_addr = &m_video_ram[m_y_pos * (512 / 8) + (m_x_pos / 8)];

	if (ByteSize)
	{
		*video_addr = m_video_data;
	}
	else
	{
		int bit_mask = 1 << ((7 ^ m_x_pos) & 7);

		*video_addr &= ~bit_mask;
		*video_addr |= m_video_data & bit_mask;
	}

	m_y_pos = (m_y_pos + Y) & 0xff;
	m_x_pos = (m_x_pos + X) & 0x1ff;
}

u8 cmi_state::video_r(offs_t offset)
{
	if (machine().side_effects_disabled())
		return m_video_data;

	m_video_data = m_video_ram[m_y_pos * (512 / 8) + (m_x_pos / 8)];

	switch (offset & 0x0f)
	{
		case 0x0: update_video_pos< 0,  0, false>(); break;
		case 0x1: update_video_pos< 0,  1, false>(); break;
		case 0x2: update_video_pos< 0, -1, false>(); break;
		case 0x3: update_video_pos< 0,  0, true>(); break;
		case 0x4: update_video_pos< 1,  0, false>(); break;
		case 0x5: update_video_pos< 1,  1, false>(); break;
		case 0x6: update_video_pos< 1, -1, false>(); break;
		case 0x7: update_video_pos< 1,  0, true>(); break;
		case 0x8: update_video_pos<-1,  0, false>(); break;
		case 0x9: update_video_pos<-1,  1, false>(); break;
		case 0xa: update_video_pos<-1, -1, false>(); break;
		case 0xb: update_video_pos<-1,  0, true>(); break;
		default: break;
	}

	return m_video_data;
}

u8 cmi_state::lightpen_r(offs_t offset)
{
	if (offset & 2)
		return m_lp_y;
	else
		return m_lp_x >> 1;
}

u8 cmi_state::pia_q219_b_r()
{
	return ((m_lp_x << 7) & 0x80) | m_q219_b_touch;
}


void cmi_state::video_w(offs_t offset, u8 data)
{
	m_video_data = data;

	switch (offset & 0x0f)
	{
		case 0x0: update_video_pos< 0,  0, false>(); break;
		case 0x1: update_video_pos< 0,  1, false>(); break;
		case 0x2: update_video_pos< 0, -1, false>(); break;
		case 0x3: update_video_pos< 0,  0, true>(); break;
		case 0x4: update_video_pos< 1,  0, false>(); break;
		case 0x5: update_video_pos< 1,  1, false>(); break;
		case 0x6: update_video_pos< 1, -1, false>(); break;
		case 0x7: update_video_pos< 1,  0, true>(); break;
		case 0x8: update_video_pos<-1,  0, false>(); break;
		case 0x9: update_video_pos<-1,  1, false>(); break;
		case 0xa: update_video_pos<-1, -1, false>(); break;
		case 0xb: update_video_pos<-1,  0, true>(); break;
		default: break;
	}
}

void cmi_state::vscroll_w(u8 data)
{
	// TODO: Partial updates. Also, this should be done through a PIA
}

void cmi_state::video_attr_w(u8 data)
{
	// TODO
}

void cmi_state::tvt_w(u8 data)
{
	if ((data >= 0x20 && data <= 0x7e) || data == 0x0a || data == 0x0d)
	{
		osd_printf_debug("%c", data);
	}
}

u8 cmi_state::tvt_r()
{
	return 0;
}

void cmi_state::vram_w(offs_t offset, u8 data)
{
	m_video_ram[offset] = data;
}

u8 cmi_state::vram_r(offs_t offset)
{
	if (machine().side_effects_disabled())
		return m_video_ram[offset];

	/* Latch the current video position */
	m_y_pos = (offset >> 6) & 0xff;
	m_x_pos = (offset & 0x3f) << 3;

	return m_video_ram[offset];
}



/* Memory handling */

template<int CpuNum> u8 cmi_state::rom_r(offs_t offset)
{
	u16 base = (CpuNum ? 0x1000 : 0x2000);
	return *(((u8 *)m_q133_region->base()) + base + offset);
}

template<int CpuNum> u8 cmi_state::perr_r(offs_t offset)
{
	m_maincpu2_irq0_merger->in_w<1>(1);

	const u8 page = offset >> 11;
	const u8 mapinfo = m_curr_mapinfo[CpuNum];
	const u8 page_info = m_map_ram[0][((mapinfo & 0x1f) << PAGE_SHIFT) + page];
	const u8 data = m_q256_ram[0][(page_info & 0x7f) * PAGE_SIZE + (offset & 0x7ff)];
	return data;
}

template<int CpuNum> void cmi_state::perr_w(offs_t offset, u8 data)
{
	const u8 page = offset >> 11;
	const u8 mapinfo = m_curr_mapinfo[CpuNum];
	const u8 page_info = m_map_ram[0][((mapinfo & 0x1f) << PAGE_SHIFT) + page];
	m_q256_ram[0][(page_info & 0x7f) * PAGE_SIZE + (offset & 0x7ff)] = data;
}

template <int CpuNum, u16 base> u8 cmi_state::ram_range_r(offs_t offset)
{
	const u16 addr = base + offset;
	const u8 mapinfo = m_curr_mapinfo[CpuNum];
	const bool perr_en = BIT(mapinfo, 6);
	const u8 page = addr >> 11;
	const u8 page_info = m_map_ram[0][((mapinfo & 0x1f) << PAGE_SHIFT) + page];

	if (m_cmi07_base_enable[CpuNum] && (addr & 0xc000) == m_cmi07_base_addr)
	{
		return m_cmi07_ram[(page * PAGE_SIZE) & 0x3fff];
	}

	if (perr_en)
	{
		return perr_r<CpuNum>(addr);
	}
	else if (BIT(page_info, 7))
	{
		const u32 ram_base = (page_info & 0x7f) << 11;
		return m_q256_ram[0][ram_base | (addr & 0x7ff)];
	}

	return 0x00;
}

template <int CpuNum, u16 base> void cmi_state::ram_range_w(offs_t offset, u8 data)
{
	const u16 addr = base + offset;
	const u8 mapinfo = m_curr_mapinfo[CpuNum];
	const bool perr_en = BIT(mapinfo, 6);
	const u8 page = addr >> 11;
	const u8 page_info = m_map_ram[0][((mapinfo & 0x1f) << PAGE_SHIFT) + page];

	if (m_cmi07_base_enable[CpuNum] && (addr & 0xc000) == m_cmi07_base_addr)
	{
		m_cmi07_ram[(page * PAGE_SIZE) & 0x3fff] = data;
		return;
	}

	if (perr_en)
	{
		perr_w<CpuNum>(addr, data);
	}
	else if (BIT(page_info, 7))
	{
		const u32 ram_base = (page_info & 0x7f) << 11;
		m_q256_ram[0][ram_base | (addr & 0x7ff)] = data;
	}
}

template <int CpuNum> u8 cmi_state::vram_range_r(offs_t offset)
{
	const u8 mapinfo = m_curr_mapinfo[CpuNum];
	if (!BIT(mapinfo, 5))
	{
		return vram_r(offset);
	}
	else
	{
		const u16 address = 0x8000 + offset;
		const u8 page = (offset >> 11) + 16;
		const u8 page_info = m_map_ram[0][((mapinfo & 0x1f) << PAGE_SHIFT) + page];

		if (m_cmi07_base_enable[CpuNum] && (address & 0xc000) == m_cmi07_base_addr)
		{
			return m_cmi07_ram[(page * PAGE_SIZE) & 0x3fff];
		}

		if (BIT(page_info, 7))
		{
			const u32 ram_base = (page_info & 0x7f) << 11;
			return m_q256_ram[0][ram_base | (offset & 0x7ff)];
		}
	}

	return 0x00;
}

template <int CpuNum> void cmi_state::vram_range_w(offs_t offset, u8 data)
{
	const u8 mapinfo = m_curr_mapinfo[CpuNum];
	if (!BIT(mapinfo, 5))
	{
		vram_w(offset, data);
	}
	else
	{
		const u16 address = 0x8000 + offset;
		const u8 page = (offset >> 11) + 16;
		const u8 page_info = m_map_ram[0][((mapinfo & 0x1f) << PAGE_SHIFT) + page];

		if (m_cmi07_base_enable[CpuNum] && (address & 0xc000) == m_cmi07_base_addr)
		{
			m_cmi07_ram[(page * PAGE_SIZE) & 0x3fff] = data;
			return;
		}

		if (BIT(page_info, 7))
		{
			const u32 ram_base = (page_info & 0x7f) << 11;
			m_q256_ram[0][ram_base | (offset & 0x7ff)] = data;
		}
	}
}

template <int CpuNum> u8 cmi_state::cards_range_r(offs_t offset)
{
	const u8 mapinfo = m_curr_mapinfo[CpuNum];
	if (!BIT(mapinfo, 7) && offset < 0x40)
	{
		return cmi02_r(offset);
	}
	else
	{
		const u8 page = (offset >> 11) + 28;
		const u8 page_info = m_map_ram[0][((mapinfo & 0x1f) << PAGE_SHIFT) + page];

		if (BIT(page_info, 7))
		{
			const u32 ram_base = (page_info & 0x7f) << 11;
			return m_q256_ram[0][ram_base | (offset & 0x7ff)];
		}
	}
	return 0x00;
}

template <int CpuNum> void cmi_state::cards_range_w(offs_t offset, u8 data)
{
	const u8 mapinfo = m_curr_mapinfo[CpuNum];
	if (!BIT(mapinfo, 7) && offset < 0x40)
	{
		cmi02_w(offset, data);
	}
	else
	{
		const u8 page = (offset >> 11) + 28;
		const u8 page_info = m_map_ram[0][((mapinfo & 0x1f) << PAGE_SHIFT) + page];

		if (BIT(page_info, 7))
		{
			const u32 ram_base = (page_info & 0x7f) << 11;
			m_q256_ram[0][ram_base | (offset & 0x7ff)] = data;
		}
	}
}

template <int CpuNum> u8 cmi_state::periphs_range_r(offs_t offset)
{
	const u8 mapinfo = m_curr_mapinfo[CpuNum];
	if (!BIT(mapinfo, 7))
	{
		return m_cpu_periphs[CpuNum]->read8(offset);
	}
	else
	{
		const u8 page = (offset >> 11) + 30;
		const u8 page_info = m_map_ram[0][((mapinfo & 0x1f) << PAGE_SHIFT) + page];

		if (BIT(page_info, 7))
		{
			const u32 ram_base = (page_info & 0x7f) << 11;
			return m_q256_ram[0][ram_base | (offset & 0x7ff)];
		}
	}

	return 0x00;
}

template <int CpuNum> void cmi_state::periphs_range_w(offs_t offset, u8 data)
{
	const u8 mapinfo = m_curr_mapinfo[CpuNum];
	if (!BIT(mapinfo, 7))
	{
		m_cpu_periphs[CpuNum]->write8(offset, data);
	}
	else
	{
		const u8 page = (offset >> 11) + 30;
		const u8 page_info = m_map_ram[0][((mapinfo & 0x1f) << PAGE_SHIFT) + page];

		if (BIT(page_info, 7))
		{
			const u32 ram_base = (page_info & 0x7f) << 11;
			m_q256_ram[0][ram_base | (offset & 0x7ff)] = data;
		}
	}
}

void cmi_state::map_ram_w(offs_t offset, u8 data)
{
	if ((offset & 1) == 0)
	{
		m_map_ram_latch = data;
	}
	else
	{
		u8 map_info;
		int map = (offset >> 6);
		int page_enable = ((m_map_ram_latch & 0x80) && (0 == (m_map_ram_latch & 7))) ? 0x80 : 0;

		m_map_ram[0][offset >> 1] = page_enable | (data & 0x7f);

		/* Determine if this map is in use by either CPU */
		if (map_is_active(CPU_1, map, &map_info))
			update_address_space(0, map_info);

		if (map_is_active(CPU_2, map, &map_info))
			update_address_space(1, map_info);
	}
}

template<int CpuNum> u8 cmi_state::map_r()
{
	return (m_cpu_active_space[1] << 2) | (m_cpu_active_space[0] << 1) | CpuNum;
}

template<int CpuNum> void cmi_state::map_w(u8 data)
{
	m_map_switch_timer->adjust(attotime::from_ticks(data & 0xf, M6809_CLOCK), CpuNum);
}

TIMER_CALLBACK_MEMBER(cmi_state::switch_map)
{
	m_cpu_active_space[param] = m_cpu_map_switch[param];
	u8 map_info = (m_cpu_map_switch[param] == MAPPING_A) ?
					 m_map_sel[param ? MAPSEL_P2_A : MAPSEL_P1_A] :
					 m_map_sel[param ? MAPSEL_P2_B : MAPSEL_P1_B];
	update_address_space(param, map_info);
	m_map_switch_timer->adjust(attotime::never);
}

TIMER_CALLBACK_MEMBER(cmi_state::jam_timeout)
{
	m_maincpu2->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
	m_jam_timeout_timer->adjust(attotime::never);
}

u8 cmi_state::atomic_r()
{
	// TODO
	//osd_printf_debug("atomic access\n");
	return 0;
}

template<int CpuNum> void cmi_state::ipi_w(int state)
{
	set_interrupt(CpuNum, IRQ_IPI2_LEVEL, state ? ASSERT_LINE : CLEAR_LINE);
}

template<int CpuNum> void cmi_state::nmi_reset_w(int state)
{
	// TODO: Hardware trace
}

template<int CpuNum> void cmi_state::ms_w(int state)
{
	m_cpu_map_switch[CpuNum] = state;
}

u8 cmi_state::parity_r(offs_t offset)
{
	m_maincpu2_irq0_merger->in_w<1>(0);
	LOG("%s: parity_r %04x\n", machine().describe_context(), offset);
	return 0xff;
}

void cmi_state::mapsel_w(offs_t offset, u8 data)
{
	LOG("%s: mapsel_w: %02x = %02x\n", machine().describe_context(), offset, data);

	data ^= 0x1f;
	m_map_sel[offset] = data;

	if ((offset == MAPSEL_P1_A) && (m_cpu_active_space[0] == MAPPING_A))
		update_address_space(0, data);
	else if ((offset == MAPSEL_P1_B) && (m_cpu_active_space[0] == MAPPING_B))
		update_address_space(0, data);

	if ((offset == MAPSEL_P2_A) && (m_cpu_active_space[1] == MAPPING_A))
		update_address_space(1, data);
	else if ((offset == MAPSEL_P2_B) && (m_cpu_active_space[1] == MAPPING_B))
		update_address_space(1, data);
}



void cmi_state::midi_dma_w(offs_t offset, u16 data, u16 mem_mask)
{
	address_space *cmi_space = ((offset & 0x8000) ? m_cpu2space : m_cpu1space);
	offset &= 0x7fff;

	if (ACCESSING_BITS_0_7)
		cmi_space->write_byte(offset * 2 + 1, data);
	if (ACCESSING_BITS_8_15)
		cmi_space->write_byte(offset * 2, data >> 8);
}

u16 cmi_state::midi_dma_r(offs_t offset)
{
	address_space *cmi_space = ((offset & 0x8000) ? m_cpu2space : m_cpu1space);
	offset &= 0x7fff;
	const u16 data = cmi_space->read_word(offset * 2);
	return data;
}

void cmi_state::midi_ptm0_c3_w(int state)
{
	m_midi_ptm[1]->set_clock(0, state);
	m_midi_ptm[1]->set_clock(1, state);
	m_midi_ptm[1]->set_clock(2, state);
}

void cmi_state::midi_latch_w(u8 data)
{
	const u8 bit_offset = data & 0x7;
	const u8 bit_value = BIT(data, 3);
	switch (bit_offset)
	{
	case 0x00: // /INT1
		LOG("%s: %sing INT1 line on SMIDI card\n", machine().describe_context(), bit_value ? "Clear" : "Sett");
		m_midicpu->set_input_line(M68K_IRQ_1, bit_value ? CLEAR_LINE : ASSERT_LINE);
		break;
	case 0x02: // SMIDINT L0
		LOG("%s: %sing SMIDI to CMI interrupt P1 level 0\n", machine().describe_context(), bit_value ? "Sett" : "Clear");
		set_interrupt(CPU_1, IRQ_MIDINT_LEVEL, bit_value ? ASSERT_LINE : CLEAR_LINE);
		break;
	case 0x03: // /INT7
		LOG("%s: %sing INT7 line on SMIDI card\n", machine().describe_context(), bit_value ? "Clear" : "Sett");
		m_midicpu->set_input_line(M68K_IRQ_7, bit_value ? CLEAR_LINE : ASSERT_LINE);
		break;
	case 0x04: // SMIDINT L3
		LOG("%s: %sing SMIDI to CMI interrupt P1 level 3\n", machine().describe_context(), bit_value ? "Sett" : "Clear");
		set_interrupt(CPU_1, IRQ_SMIDINT_LEVEL, bit_value ? ASSERT_LINE : CLEAR_LINE);
		break;
	case 0x05: // /HALT
		LOG("%s: %sing HALT line on SMIDI card\n", machine().describe_context(), bit_value ? "Clear" : "Sett");
		m_midicpu->set_input_line(INPUT_LINE_HALT, bit_value ? CLEAR_LINE : ASSERT_LINE);
		break;
	case 0x06: // SYNCSW
		LOG("%s: %sing SYNCSW line on SMIDI card\n", machine().describe_context(), bit_value ? "Sett" : "Clear");
		break;
	case 0x07: // /RESET
		LOG("%s: %sing RESET line on SMIDI card\n", machine().describe_context(), bit_value ? "Clear" : "Sett");
		m_midicpu->set_input_line(INPUT_LINE_RESET, bit_value ? CLEAR_LINE : ASSERT_LINE);
		break;
	}
}

void cmi_state::maincpu1_map(address_map &map)
{
	map(0x0000, 0x7fff).rw(&cmi_state::ram_range_r<0, 0x0000>, "cmi_state::ram_range_r<0, 0x0000>",
		&cmi_state::ram_range_w<0, 0x0000>, "cmi_state::ram_range_w<0, 0x0000>");
	map(0x8000, 0xbfff).rw(FUNC(cmi_state::vram_range_r<0>), FUNC(cmi_state::vram_range_w<0>));
	map(0xc000, 0xdfff).rw(&cmi_state::ram_range_r<0, 0xc000>, "cmi_state::ram_range_r<0, 0xc000>",
		&cmi_state::ram_range_w<0, 0xc000>, "cmi_state::ram_range_w<0, 0xc000>");
	map(0xe000, 0xefff).rw(FUNC(cmi_state::cards_range_r<0>), FUNC(cmi_state::cards_range_w<0>));
	map(0xf000, 0xffff).rw(FUNC(cmi_state::periphs_range_r<0>), FUNC(cmi_state::periphs_range_w<0>));
}

void cmi_state::maincpu2_map(address_map &map)
{
	map(0x0000, 0x7fff).rw(&cmi_state::ram_range_r<1, 0x0000>, "cmi_state::ram_range_r<1, 0x0000>",
		&cmi_state::ram_range_w<1, 0x0000>, "cmi_state::ram_range_w<1, 0x0000>");
	map(0x8000, 0xbfff).rw(FUNC(cmi_state::vram_range_r<1>), FUNC(cmi_state::vram_range_w<1>));
	map(0xc000, 0xdfff).rw(&cmi_state::ram_range_r<1, 0xc000>, "cmi_state::ram_range_r<1, 0xc000>",
		&cmi_state::ram_range_w<1, 0xc000>, "cmi_state::ram_range_w<1, 0xc000>");
	map(0xe000, 0xefff).rw(FUNC(cmi_state::cards_range_r<1>), FUNC(cmi_state::cards_range_w<1>));
	map(0xf000, 0xffff).rw(FUNC(cmi_state::periphs_range_r<1>), FUNC(cmi_state::periphs_range_w<1>));
}

void cmi_state::midicpu_map(address_map &map)
{
	map(0x000000, 0x003fff).rom();
	map(0x040000, 0x05ffff).rw(FUNC(cmi_state::midi_dma_r), FUNC(cmi_state::midi_dma_w));
	map(0x060000, 0x06000f).rw(m_midi_ptm[0], FUNC(ptm6840_device::read), FUNC(ptm6840_device::write)).umask16(0xff00);
	map(0x060010, 0x06001f).rw(m_midi_ptm[1], FUNC(ptm6840_device::read), FUNC(ptm6840_device::write)).umask16(0xff00);
	map(0x060020, 0x06005f).rw(m_midi_acia[0], FUNC(acia6850_device::read), FUNC(acia6850_device::write)).umask16(0x00ff);
	map(0x060030, 0x06003f).rw(m_midi_acia[1], FUNC(acia6850_device::read), FUNC(acia6850_device::write)).umask16(0x00ff);
	map(0x060040, 0x06004f).rw(m_midi_acia[2], FUNC(acia6850_device::read), FUNC(acia6850_device::write)).umask16(0x00ff);
	map(0x060050, 0x06005f).rw(m_midi_acia[3], FUNC(acia6850_device::read), FUNC(acia6850_device::write)).umask16(0x00ff);
	//map(0x060060, 0x06007f) SMPTE
	map(0x080000, 0x083fff).ram();
}

void cmi_state::cmi07cpu_map(address_map &map)
{
	map(0x0000, 0x0000).r(FUNC(cmi_state::aic_ad574_r)).mirror(0x3fff);
	map(0x4000, 0x4000).w(FUNC(cmi_state::aic_dac_w<0>)).mirror(0x3ff8);
	map(0x4001, 0x4001).w(FUNC(cmi_state::aic_dac_w<1>)).mirror(0x3ff8);
	map(0x4002, 0x4002).w(FUNC(cmi_state::aic_dac_w<2>)).mirror(0x3ff8);
	map(0x4003, 0x4003).w(FUNC(cmi_state::aic_dac_w<3>)).mirror(0x3ff8);
	map(0x4004, 0x4004).w(FUNC(cmi_state::aic_mux_latch_w)).mirror(0x3ff8);
	map(0x4006, 0x4006).w(FUNC(cmi_state::aic_ad565_msb_w)).mirror(0x3ff8);
	map(0x4007, 0x4007).w(FUNC(cmi_state::aic_ad565_lsb_w)).mirror(0x3ff8);
	map(0x8000, 0x8fff).rw(m_cmi07_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write));
	map(0xc000, 0xffff).ram().share("cmi07_ram");
}

template <int CpuNum> void cmi_state::cpu_periphs_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).rw(FUNC(cmi_state::rom_r<CpuNum>), FUNC(cmi_state::map_ram_w));
	map(0x0800, 0x0bff).rom().region("q133", 0x2800 - CpuNum * 0x1000);
	map(0x0c40, 0x0c4f).rw(FUNC(cmi_state::parity_r), FUNC(cmi_state::mapsel_w));
	map(0x0c5a, 0x0c5b).noprw(); // Q077 HDD controller - not installed
	map(0x0c5e, 0x0c5e).r(FUNC(cmi_state::atomic_r)).w("cpulatch", FUNC(ls259_device::write_nibble_d3));
	map(0x0c5f, 0x0c5f).rw(FUNC(cmi_state::map_r<CpuNum>), FUNC(cmi_state::map_w<CpuNum>));
	map(0x0c80, 0x0c83).rw(m_q133_acia[0], FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x0c84, 0x0c87).rw(m_q133_acia[1], FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x0c88, 0x0c8b).rw(m_q133_acia[2], FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x0c8c, 0x0c8f).rw(m_q133_acia[3], FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x0c90, 0x0c97).rw(m_q133_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write));
	map(0x0ca0, 0x0ca0).w(FUNC(cmi_state::midi_latch_w));
	map(0x0cbc, 0x0cbc).rw(FUNC(cmi_state::cmi07_r), FUNC(cmi_state::cmi07_w));
	map(0x0cc0, 0x0cc3).r(FUNC(cmi_state::lightpen_r));
	map(0x0cc4, 0x0cc7).rw(m_q219_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x0cc8, 0x0ccf).rw(m_q219_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write));
	map(0x0cd0, 0x0cdc).rw(FUNC(cmi_state::video_r), FUNC(cmi_state::video_w));
	map(0x0ce0, 0x0ce1).rw(FUNC(cmi_state::fdc_r), FUNC(cmi_state::fdc_w));
	map(0x0ce2, 0x0cef).noprw(); // Monitor ROM will attempt to detect floppy disk controller cards in this entire range
	map(0x0cf0, 0x0cf7).rw(m_q133_pia[0], FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x0cf8, 0x0cff).rw(m_q133_pia[1], FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x0cfc, 0x0cfc).w(FUNC(cmi_state::i8214_cpu1_w));
	map(0x0cfd, 0x0cfd).w(FUNC(cmi_state::i8214_cpu2_w));
	map(0x0d00, 0x0eff).rw(FUNC(cmi_state::shared_ram_r), FUNC(cmi_state::shared_ram_w));
	map(0x0f00, 0x0fff).rw(FUNC(cmi_state::scratch_ram_r<CpuNum>), FUNC(cmi_state::scratch_ram_w<CpuNum>));
}

/* Input ports */
static INPUT_PORTS_START( cmi2x )
	PORT_START("LP_X")
	PORT_BIT( 0xffff, HBLANK_START/2, IPT_LIGHTGUN_X) PORT_NAME ("Lightpen X") PORT_MINMAX(0, HBLANK_START - 1) PORT_SENSITIVITY(50) PORT_CROSSHAIR(X, 1.0, 0.0, 0)

	PORT_START("LP_Y")
	PORT_BIT( 0xffff, VBLANK_START/2, IPT_LIGHTGUN_Y) PORT_NAME ("Lightpen Y") PORT_MINMAX(0, VBLANK_START - 1) PORT_SENSITIVITY(50) PORT_CROSSHAIR(Y, 1.0, 0.0, 0)

	PORT_START("LP_TOUCH")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME ( "Lightpen Touch" ) PORT_CODE( MOUSECODE_BUTTON1 )
INPUT_PORTS_END

template <int CpuNum> u8 cmi_state::scratch_ram_r(offs_t offset)
{
	return m_scratch_ram[CpuNum][offset];
}

template <int CpuNum> void cmi_state::scratch_ram_w(offs_t offset, u8 data)
{
	m_scratch_ram[CpuNum][offset] = data;
}

bool cmi_state::map_is_active(int cpunum, int map, u8 *map_info)
{
	if (m_cpu_active_space[cpunum] == MAPPING_A)
	{
		*map_info = m_map_sel[cpunum ? MAPSEL_P2_A : MAPSEL_P1_A];

		if ((*map_info & 0x1f) == map)
			return true;
	}
	else
	{
		*map_info = m_map_sel[cpunum ? MAPSEL_P2_B : MAPSEL_P1_B];

		if ((*map_info & 0x1f) == map)
			return true;
	}

	return false;
}

void cmi_state::update_address_space(int cpunum, u8 mapinfo)
{
	m_curr_mapinfo[cpunum] = mapinfo;
}

void cmi_state::cmi07_w(u8 data)
{
	LOG("%s: cmi07_w: %02x\n", machine().describe_context(), data);

	if (true)
	{
		return;
	}

	const u8 prev = m_cmi07_ctrl;
	m_cmi07_ctrl = data;

	m_cmi07_base_enable[0] = BIT(data, 6) && (data & 0x30);
	m_cmi07_base_enable[1] = m_cmi07_base_enable[0] && !BIT(data, 7);
	m_cmi07_base_addr = (data & 0x30) << 10;

	m_cmi07cpu->set_input_line(INPUT_LINE_RESET, BIT(data, 0) ? CLEAR_LINE : ASSERT_LINE);
	m_cmi07cpu->set_input_line(M6809_FIRQ_LINE,  BIT(data, 1) ? CLEAR_LINE : ASSERT_LINE);
	m_cmi07cpu->set_input_line(INPUT_LINE_NMI,   BIT(data, 2) ? CLEAR_LINE : ASSERT_LINE);
	m_cmi07cpu->set_input_line(INPUT_LINE_HALT,  BIT(data, 3) ? CLEAR_LINE : ASSERT_LINE);

	/* We need to update the address spaces */
	u8 map_info = (m_cpu_active_space[0] == MAPPING_A) ? m_map_sel[MAPSEL_P1_A] : m_map_sel[MAPSEL_P1_B];
	update_address_space(0, map_info);

	/* CPU 2 space is untouched by this update */
	if (BIT(prev & data, 7))
		return;

	map_info = (m_cpu_active_space[1] == MAPPING_A) ? m_map_sel[MAPSEL_P2_A] : m_map_sel[MAPSEL_P2_B];
	update_address_space(1, map_info);
}

u8 cmi_state::cmi07_r()
{
	LOG("%s: cmi07_r: %02x\n", machine().describe_context(), 0xff);
	return 0xff;
}

void cmi_state::q133_acia_irq(int state)
{
	set_interrupt(CPU_1, IRQ_ACINT_LEVEL, state ? ASSERT_LINE : CLEAR_LINE);
}


/**************************************
 *
 *  Floppy disk interface
 *
 *************************************/

void cmi_state::dma_fdc_rom()
{
	/* DMA channel 1 is used*/
	u8 map_info = m_map_sel[MAPSEL_P2_A_DMA1];
	int map = map_info & 0x1f;
	int addr = m_fdc_dma_addr.w.l & ~PAGE_MASK;
	int page = addr / PAGE_SIZE;
	u8 p_info = 0;

	/* Active low */
	m_fdc_status &= ~FDC_STATUS_DRIVER_LOAD;

	int i = 0;
	for (i = 0; i < NUM_Q256_CARDS; ++i)
	{
		p_info = m_map_ram[i][(map << PAGE_SHIFT) | page];

		if (p_info & 0x80)
			break;
	}

	if ((p_info & 0x80) == 0)
	{
		osd_printf_debug("Trying to DMA FDC driver to a non-enabled page!\n");
		return;
	}

	/* TODO: This should be stuck in a deferred write */
	int cnt = std::min(m_fdc_dma_cnt.w.l ^ 0xffff, 2048);
	memcpy(&m_q256_ram[i][(p_info & 0x7f) * PAGE_SIZE], m_qfc9_region_ptr, cnt);
	m_fdc_status |= FDC_STATUS_DRIVER_LOAD;

	/* TODO: Is this correct? */
	m_fdc_dma_addr.w.l += 0x800;
	m_fdc_dma_cnt.w.l = 0;
}

void cmi_state::write_fdc_ctrl(u8 data)
{
	int drive = data & 1;
	int side = BIT(data, 5) ? 1 : 0;

	m_wd1791->set_floppy(m_floppy[drive]->get_device());

	if (m_floppy[0]->get_device()) m_floppy[0]->get_device()->ss_w(side);
	if (m_floppy[1]->get_device()) m_floppy[1]->get_device()->ss_w(side);

	m_wd1791->dden_w(BIT(data, 7) ? true : false);

	m_fdc_ctrl = data;
}

void cmi_state::fdc_w(offs_t offset, u8 data)
{
	if (offset == 0)
	{
		switch (m_fdc_addr)
		{
			case 0x0: write_fdc_ctrl(data);         break;
			case 0x2: m_fdc_dma_addr.b.l = data;    break;
			case 0x4: m_fdc_dma_addr.b.h  = data;   break;
			case 0x6: m_fdc_dma_cnt.b.l = data;     break;
			case 0x8: m_fdc_dma_cnt.b.h = data;     break;
			case 0xa: dma_fdc_rom();                break;
			case 0xc: m_wd1791->cmd_w(data ^ 0xff);        break;
			case 0xd: m_wd1791->track_w(data ^ 0xff);      break;
			case 0xe: m_wd1791->sector_w(data ^ 0xff);     break;
			case 0xf: m_wd1791->data_w(data ^ 0xff);       break;
			default: osd_printf_debug("fdc_w: Invalid access (%x with %x)", m_fdc_addr, data);
		}
	}
	else
		m_fdc_addr = data;
}

u8 cmi_state::fdc_r(offs_t offset)
{
	if (machine().side_effects_disabled())
		return 0;

	if (offset == 0)
	{
		switch (m_fdc_addr)
		{
			case 0xc: return m_wd1791->status_r() ^ 0xff;
			case 0xd: return m_wd1791->track_r() ^ 0xff;
			case 0xe: return m_wd1791->sector_r() ^ 0xff;
			case 0xf: return m_wd1791->data_r() ^ 0xff;
			default:  return 0;
		}
	}
	else
		return m_fdc_status;
}

void cmi_state::fdc_dma_transfer()
{
	/* DMA channel 1 is used*/
	u8 map_info = m_map_sel[MAPSEL_P2_A_DMA1];
	int map = map_info & 0x1f;

	int cpu_page = (m_fdc_dma_addr.w.l & ~PAGE_MASK) / PAGE_SIZE;
	int phys_page = 0;

	int i = 0;
	for (i = 0; i < NUM_Q256_CARDS; ++i)
	{
		phys_page = m_map_ram[i][(map << PAGE_SHIFT) | cpu_page];

		if (phys_page & 0x80)
			break;
	}

	//phys_page &= 0x7f;

	/* Transfer from disk to RAM */
	if (!BIT(m_fdc_ctrl, 4))
	{
		/* Read a byte at a time */
		u8 data = m_wd1791->data_r() ^ 0xff;

		if (m_fdc_dma_cnt.w.l == 0xffff)
			return;

		if (m_cmi07_ctrl & 0x30)
			if (BIT(m_cmi07_ctrl, 6) && !BIT(m_cmi07_ctrl, 7))
			{
				if ((m_fdc_dma_addr.w.l & 0xc000) == ((m_cmi07_ctrl & 0x30) << 10))
					m_cmi07_ram[m_fdc_dma_addr.w.l & 0x3fff] = data;
			}

		if (phys_page & 0x80)
			m_q256_ram[i][((phys_page & 0x7f) * PAGE_SIZE) + (m_fdc_dma_addr.w.l & PAGE_MASK)] = data;

		if (!BIT(m_fdc_ctrl, 3))
			m_fdc_dma_addr.w.l++;
	}

	// Transfer from RAM to disk
	else
	{
		if (m_fdc_dma_cnt.w.l == 0xffff)
			return;

		/* Write a byte at a time */
		u8 data = 0;

		/* TODO: This should be stuck in a deferred write */
		if (phys_page & 0x80)
			data = m_q256_ram[i][((phys_page & 0x7f) * PAGE_SIZE) + (m_fdc_dma_addr.w.l & PAGE_MASK)];

		m_wd1791->data_w(data ^ 0xff);

		if (!BIT(m_fdc_ctrl, 3))
			m_fdc_dma_addr.w.l++;
	}

	m_fdc_dma_cnt.w.l++;
}

void cmi_state::wd1791_irq(int state)
{
	if (state)
	{
		m_fdc_status |= FDC_STATUS_INTERRUPT;

		if (m_fdc_ctrl & FDC_CONTROL_INTEN)
			set_interrupt(CPU_2, IRQ_DISKINT_LEVEL, ASSERT_LINE);

	}
	else
	{
		m_fdc_status &= ~FDC_STATUS_INTERRUPT;
		set_interrupt(CPU_2, IRQ_DISKINT_LEVEL, CLEAR_LINE);
	}
}

void cmi_state::wd1791_drq(int state)
{
	m_fdc_drq = state;
	if (state)
		fdc_dma_transfer();
}



/**************************************
 *
 *  Master card and channel cards
 *
 *************************************/

/*
0 - 1f
20 = PIA
21 = PIA
22 = PIA
23 = PIA

24 = ADC
25 = ADC
26 = HALT CPU 2
27 = UNHALT CPU 2
28 - 2B = PIA
*/

void cmi_state::master_tune_w(u8 data)
{
	m_master_tune = data;
	double mfreq = ((0xf00 | data) * (MASTER_OSCILLATOR.dvalue() / 2.0)) / 4096.0;
	for (int i = 0; i < 8; i++)
	{
		m_channels[i]->set_master_osc(mfreq);
	}
}

u8 cmi_state::master_tune_r()
{
	return m_master_tune;
}

void cmi_state::cmi02_chsel_w(u8 data)
{
	m_cmi02_pia_chsel = data;
}

u8 cmi_state::cmi02_chsel_r()
{
	return m_cmi02_pia_chsel;
}

void cmi_state::cmi02_ptm_irq(int state)
{
	m_cmi02_ptm_irq = state;
	set_interrupt(CPU_1, IRQ_TIMINT_LEVEL, m_cmi02_ptm_irq ? ASSERT_LINE : CLEAR_LINE);
}

void cmi_state::cmi02_ptm_o2(int state)
{
	m_cmi02_ptm->set_c1(state);
	m_cmi02_ptm->set_c3(state);
}

void cmi_state::cmi02_pia2_irqa_w(int state)
{
	LOG("%s: cmi02_pia2_irqa_w: %d\n", machine().describe_context(), state);
	set_interrupt(CPU_2, IRQ_ADINT_LEVEL, state ? ASSERT_LINE : CLEAR_LINE);
}

void cmi_state::cmi02_pia2_cb2_w(int state)
{
	LOG("%s: cmi02_pia2_cb2_w: %d\n", machine().describe_context(), state);
	m_cmi02_pia[1]->ca1_w(1);
	m_cmi02_pia[1]->ca1_w(0);
}

u8 cmi_state::cmi02_r(offs_t offset)
{
	if (machine().side_effects_disabled())
		return 0;

	if (offset <= 0x1f)
	{
		for (int i = 0; i < 8; i++)
		{
			if (BIT(m_cmi02_pia_chsel, i))
			{
				return m_channels[i]->read(offset);
			}
		}

		return 0xff;
	}
	else
	{
		switch (offset)
		{
			case 0x20: case 0x21: case 0x22: case 0x23:
				return m_cmi02_pia[0]->read(offset & 3);

			case 0x26:
				m_maincpu2->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
				/* LS123 one-shot with 10n and 150k */
				m_jam_timeout_timer->adjust(attotime::from_usec(675));
				return 0xff;

			case 0x27:
				m_maincpu2->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
				return 0xff;

			case 0x28: case 0x29: case 0x2a: case 0x2b:
				return m_cmi02_pia[1]->read(offset & 3);

			case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
				return m_cmi02_ptm->read(offset & 7);

			default:
				return 0;
		}
	}
}

void cmi_state::cmi02_w(offs_t offset, u8 data)
{
	if (offset <= 0x1f)
	{
		for (int i = 0; i < 8; i++)
		{
			if (BIT(m_cmi02_pia_chsel, i))
			{
				m_channels[i]->write(offset & 0x1f, data);
			}
		}
	}
	else
	{
		switch (offset)
		{
			case 0x20: case 0x21: case 0x22: case 0x23:
				m_cmi02_pia[0]->write(offset & 3, data);
				break;

			case 0x28: case 0x29: case 0x2a: case 0x2b:
				m_cmi02_pia[1]->write(offset & 3, data);
				break;

			case 0x30:
				m_hp_int = 0;
				m_maincpu1_irq_merger->in_w<1>(0);
				m_i8214[2]->b_sgs_w(~(data & 0xf));
				break;

			case 0x31: case 0x32:
				set_interrupt(0, IRQ_INTP1_LEVEL, (offset & 2) ? CLEAR_LINE : ASSERT_LINE);
				break;

			case 0x33: case 0x34:
				set_interrupt(1, IRQ_INTP2_LEVEL, (offset & 4) ? CLEAR_LINE : ASSERT_LINE);
				break;

			case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
				m_cmi02_ptm->write(offset & 7, data);
				break;

			default:
				break;
		}
	}
}

template<int Channel>
void cmi_state::channel_irq(int state)
{
	if (Channel == 0)
	{
		LOGMASKED(LOG_CHANNELS, "Channel IRQ: %d\n", state);
	}
	static const int ch_int_levels[8] = { 12, 8, 13, 9, 14, 10, 15, 11 };
	set_interrupt(CPU_1, ch_int_levels[Channel] ^ 7, state);
}

void cmi_state::i8214_cpu1_w(u8 data)
{
	m_maincpu1_irq_merger->in_w<0>(0);
	m_lp_int = 0;
	m_i8214[0]->b_sgs_w(~(data & 0xf));
}


void cmi_state::i8214_cpu2_w(u8 data)
{
	LOG("%s: i8214_cpu2_w, clearing CPU2 IRQ line: %02x\n", machine().describe_context(), data);
	m_maincpu2->set_input_line(M6809_IRQ_LINE, CLEAR_LINE);
	m_i8214[1]->b_sgs_w(~(data & 0xf));
}

u8 cmi_state::shared_ram_r(offs_t offset)
{
	return m_shared_ram[offset];
}

void cmi_state::shared_ram_w(offs_t offset, u8 data)
{
	m_shared_ram[offset] = data;
}

u8 cmi_state::aic_ad574_r()
{
	const bool adca = BIT(m_aic_mux_latch, 5); // false - MSB, true - LSB
	const u16 val = m_aic_ad565_in[m_aic_mux_latch & 0x07];
	const u8 data = adca ? ((u8)val) : (val >> 8);
	LOG("%s: AIC AD574 read: %02x\n", machine().describe_context(), data);
	return data;
}

template<int Dac> void cmi_state::aic_dac_w(u8 data)
{
	LOG("%s: AIC DAC%d write: %02x\n", machine().describe_context(), Dac + 1, data);
	// To Do
}

void cmi_state::aic_mux_latch_w(u8 data)
{
	LOG("%s: AIC mux latch write: %02x\n", machine().describe_context(), data);
	set_interrupt(CPU_1, IRQ_AIC_LEVEL, BIT(data, 7) ? ASSERT_LINE : CLEAR_LINE);
	if (data & 0x07)
	{
		m_aic_mux_latch = data;
	}
	else
	{
		m_aic_mux_latch &= 0x07;
		m_aic_mux_latch |= data & 0xf8;
	}
	if (!BIT(data, 6))
	{
		LOG("%s: ADCR is 0, initiating ADC conversion request\n", machine().describe_context());
		m_aic_ad565_in[m_aic_mux_latch & 0x07] = 0;
	}
}

void cmi_state::aic_ad565_msb_w(u8 data)
{
	m_aic_ad565_in[m_aic_mux_latch & 0x07] &= 0x00ff;
	m_aic_ad565_in[m_aic_mux_latch & 0x07] |= (u16)data << 8;
	LOG("%s: AIC AD565 MSB write: %02x, input %04x\n", machine().describe_context(), data, m_aic_ad565_in[m_aic_mux_latch & 0x07]);
}

void cmi_state::aic_ad565_lsb_w(u8 data)
{
	LOG("%s: AIC AD565 LSB write: %02x\n", machine().describe_context(), data);
	m_aic_ad565_in[m_aic_mux_latch & 0x07] &= 0xff00;
	m_aic_ad565_in[m_aic_mux_latch & 0x07] |= data;
}

/*************************************
 *
 *  Interrupt Handling
 *
 *************************************/

void cmi_state::ptm_q219_irq(int state)
{
	set_interrupt(CPU_2, IRQ_RINT_LEVEL, state);
}

u8 cmi_state::vfetch1_r(offs_t offset)
{
	// Switch to mapping A
	m_cpu_active_space[CPU_1] = MAPPING_A;
	update_address_space(CPU_1, m_map_sel[MAPSEL_P1_A]);

	if ((offset & 0x000e) == 0x0008)
	{
		// IRQ vector
		int vector = (m_hp_int ? 0xffe0 : 0xffd0);
		int level = (m_hp_int ? m_i8214[2]->a_r() : m_i8214[0]->a_r()) ^ 7;
		u8 irq_address = m_cpu1space->read_byte(vector + level * 2 + BIT(offset, 0));
		LOG("%s: CPU1 IRQ vector %s byte: %02x\n", machine().describe_context(), BIT(offset, 0) ? "low" : "high", irq_address);
		return irq_address;
	}
	else if ((offset & 0x000e) == 0x000e)
	{
		// RESET vector
		return m_q133_rom[offset & 0xfff];
	}
	else
	{
		LOG("%s: Some other CPU1 interrupt, fetching vector from $%04x\n", machine().describe_context(), offset);
		return m_scratch_ram[CPU_1][offset & 0xff];
	}
}

u8 cmi_state::vfetch2_r(offs_t offset)
{
	// Switch to mapping A
	m_cpu_active_space[CPU_2] = MAPPING_A;
	update_address_space(CPU_2, m_map_sel[MAPSEL_P2_A]);

	if ((offset & 0x000e) == 0x0008)
	{
		// IRQ vector
		int level = m_i8214[1]->a_r() ^ 7;
		u8 irq_address = m_cpu2space->read_byte(0xffe0 + level*2 + BIT(offset, 0));
		LOG("%s: CPU2 IRQ vector %s byte: %02x\n", machine().describe_context(), BIT(offset, 0) ? "low" : "high", irq_address);
		return irq_address;
	}
	else if ((offset & 0x000e) == 0x000e)
	{
		// RESET vector
		return m_q133_rom[offset & 0xbff];
	}
	else
	{
		LOG("%s: Some other CPU2 interrupt, fetching vector from $%04x\n", machine().describe_context(), offset);
		return m_scratch_ram[CPU_2][offset & 0xff];
	}
}

void cmi_state::set_interrupt(int cpunum, int level, int state)
{
	LOG("%s: CPU%d Int: %x State: %x\n", machine().describe_context(), cpunum + 1, level, state);

	if (state == ASSERT_LINE)
		m_int_state[cpunum] |= (1 << level);
	else
		m_int_state[cpunum] &= ~(1 << level);

	if (cpunum == 0)
	{
		if (level < 8)
			m_i8214[2]->r_all_w(~(m_int_state[cpunum]));
		else
			m_i8214[0]->r_all_w(~(m_int_state[cpunum] >> 8));
	}
	else
	{
		m_i8214[1]->r_all_w(~m_int_state[cpunum]);
	}
}

void cmi_state::maincpu2_irq0_w(int state)
{
	LOG("%s: maincpu2_irq0_w: %d\n", machine().describe_context(), state);
	set_interrupt(CPU_2, 0 ^ 7, state ? ASSERT_LINE : CLEAR_LINE);
}

void cmi_state::i8214_1_int_w(int state)
{
	LOG("%s: i8214_1_int_w %d%s\n", machine().describe_context(), state, state ? ", setting IRQ merger bit 0" : "");
	if (state)
	{
		m_lp_int = 1;
		//m_maincpu1->set_input_line(M6809_IRQ_LINE, ASSERT_LINE);
		m_maincpu1_irq_merger->in_w<0>(state);
	}
}

void cmi_state::i8214_2_int_w(int state)
{
	LOG("%s: i8214_2_int_w: %d\n", machine().describe_context(), state);
	if (state)
		m_maincpu2->set_input_line(M6809_IRQ_LINE, ASSERT_LINE);
}

void cmi_state::i8214_3_int_w(int state)
{
	LOG("%s: i8214_3_int_w %d%s\n", machine().describe_context(), state, state ? ", setting IRQ merger bit 1" : "");
	if (state)
	{
		m_hp_int = 1;
		m_maincpu1_irq_merger->in_w<1>(state);
	}
	//m_hp_int = 1;
	//m_maincpu1->set_input_line(M6809_IRQ_LINE, ASSERT_LINE);
	//m_maincpu1->set_input_line(M6809_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
}


void cmi_state::i8214_3_enlg(int state)
{
	// Not needed?
//  m_hp_int = state;
}

void cmi_state::pia_q219_irqa(int state)
{
	set_interrupt(CPU_2, IRQ_TOUCHINT_LEVEL, state);
}

void cmi_state::pia_q219_irqb(int state)
{
	set_interrupt(CPU_2, IRQ_PENINT_LEVEL, state);
}


/* E9 - E11 - MSM5832RS */
/*
    A0-A3  = MSM5832 A0-A3
    A4+CA1 = MSM5832 D0
    A5+CB1 = MSM5832 D1
    A6     = MSM5832 D2
    A7     = MSM5832 D3
    B0     = HOLD
    B1     = READ
    B2     = WRITE
    CB2    = OPTO
    B3     = ----
    B4-B7  = PC0-3

    IRQA/B = /RTINT?
*/

u8 cmi_state::q133_1_porta_r()
{
	if (BIT(m_q133_pia[0]->b_output(), 1))
	{
		return m_msm5832->data_r() << 4;
	}
	return 0xff;
}

void cmi_state::q133_1_porta_w(u8 data)
{
	m_msm5832_addr = data & 0xf;
	m_msm5832->address_w(data & 0x0f);
}

void cmi_state::q133_1_portb_w(u8 data)
{
	m_msm5832->hold_w(BIT(data, 0));
	m_msm5832->read_w(BIT(data, 1));
	m_msm5832->write_w(BIT(data, 2));
}

 /*************************************
 *
 *  6551 ACIAs
 *
 *************************************/

//static int kbd_to_cmi;
//static int cmi_to_kbd;

void cmi_state::q133_acia_clock(int state)
{
	for (auto &acia : m_q133_acia)
		acia->write_rxc(state);
}

void cmi_state::msm5832_irq_w(int state)
{
	set_interrupt(CPU_2, IRQ_RTCINT_LEVEL, state ? ASSERT_LINE : CLEAR_LINE);
}

void cmi_state::cmi07_irq(int state)
{
	m_cmi07cpu->set_input_line(INPUT_LINE_IRQ0, state ? ASSERT_LINE : CLEAR_LINE);
}

void cmi_state::machine_reset()
{
	m_cpu1space = &m_maincpu1->space(AS_PROGRAM);
	m_cpu2space = &m_maincpu2->space(AS_PROGRAM);

	m_qfc9_region_ptr = (u8 *)m_qfc9_region->base();

	m_int_state[0] = 0;
	m_int_state[1] = 0;

	/* Set 8214 interrupt lines */
	m_i8214[0]->etlg_w(1);
	m_i8214[0]->inte_w(1);
	m_i8214[1]->etlg_w(1);
	m_i8214[1]->inte_w(1);
	m_i8214[2]->etlg_w(1);
	m_i8214[2]->inte_w(1);

	m_hblank_timer->adjust(m_screen->time_until_pos(0, HBLANK_START));

	for (int cpunum = 0; cpunum < 2; ++cpunum)
	{
		/* Select A (system) spaces */
		m_cpu_active_space[cpunum] = MAPPING_A;
	}

	// TODO - we need to detect empty disk drive!!
	m_fdc_status |= FDC_STATUS_READY;

	/* CMI-07 */
	m_cmi07_ctrl = 0;
	m_cmi07_base_enable[0] = false;
	m_cmi07_base_enable[1] = false;
	m_cmi07_base_addr = 0;
	m_cmi07cpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);

	// SMIDI
	m_midicpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);

	m_cmi02_ptm_irq = 0;
	m_cmi02_ptm->set_c2(1);
	m_cmi02_ptm->set_g1(0);
	m_cmi02_ptm->set_g2(0);
	m_cmi02_ptm->set_g3(0);

	//m_midi_ptm[0]->set_g1(1); // /G1 has unknown source, "TIMER 1A /GATE" per schematic
	m_midi_ptm[0]->set_g2(0); // /G2 and /G3 wired to ground per schematic
	m_midi_ptm[0]->set_g3(0);

	m_midi_ptm[1]->set_g1(0); // /G1, /G2, and /G3 wired to ground per schematic
	m_midi_ptm[1]->set_g2(0);
	m_midi_ptm[1]->set_g3(0);

	memset(m_map_sel, 0, 16);

	for (int i = 0; i < 4; i++)
	{
		m_q133_acia[i]->write_dsr(0);
		m_q133_acia[i]->write_dcd(0);
	}

	m_curr_mapinfo[0] = 0x00;
	m_curr_mapinfo[1] = 0x00;
}

void cmi_state::machine_start()
{
	m_q133_rom = (u8 *)m_q133_region->base();

	// allocate timers for the built-in two channel timer
	m_map_switch_timer = timer_alloc(FUNC(cmi_state::switch_map), this);
	m_hblank_timer = timer_alloc(FUNC(cmi_state::hblank), this);
	m_jam_timeout_timer = timer_alloc(FUNC(cmi_state::jam_timeout), this);

	m_map_switch_timer->adjust(attotime::never);
	m_hblank_timer->adjust(attotime::never);
	m_jam_timeout_timer->adjust(attotime::never);

	/* Allocate 1kB memory mapping RAM */
	m_map_ram[0] = std::make_unique<u8[]>(0x400);
	m_map_ram[1] = std::make_unique<u8[]>(0x400);

	/* Allocate 256kB for each Q256 RAM card */
	m_q256_ram[0] = std::make_unique<u8[]>(0x40000);
	m_q256_ram[1] = std::make_unique<u8[]>(0x40000);

	/* Allocate 16kB video RAM */
	m_video_ram = std::make_unique<u8[]>(0x4000);

	/* Allocate 512B shared RAM */
	m_shared_ram = std::make_unique<u8[]>(0x200);

	/* Allocate 256B scratch RAM per CPU */
	m_scratch_ram[0] = std::make_unique<u8[]>(0x100);
	m_scratch_ram[1] = std::make_unique<u8[]>(0x100);

	m_msm5832->cs_w(1);
}

void cmi_state::cmi_iix_vblank(int state)
{
	if (state)
	{
		/* VSYNC */
		m_q219_pia->cb2_w(1);
		m_q219_pia->cb2_w(0);

		/* LPSTB */
		m_q219_pia->cb1_w(0);
	}
}

static void cmi2x_floppies(device_slot_interface &device)
{
	device.option_add("8dsdd", FLOPPY_8_DSDD);
	device.option_add("8dssd", FLOPPY_8_DSSD);
}

void cmi_state::cmi2x(machine_config &config)
{
	MC6809E(config, m_maincpu1, Q209_CPU_CLOCK);
	m_maincpu1->set_addrmap(AS_PROGRAM, &cmi_state::maincpu1_map);
	m_maincpu1->interrupt_vector_read().set(FUNC(cmi_state::vfetch1_r));

	MC6809E(config, m_maincpu2, Q209_CPU_CLOCK);
	m_maincpu2->set_addrmap(AS_PROGRAM, &cmi_state::maincpu2_map);
	m_maincpu2->interrupt_vector_read().set(FUNC(cmi_state::vfetch2_r));

	ADDRESS_MAP_BANK(config, m_cpu_periphs[0]).set_options(ENDIANNESS_BIG, 8, 16, 0x1000);
	m_cpu_periphs[0]->set_addrmap(AS_PROGRAM, &cmi_state::cpu_periphs_map<0>);

	ADDRESS_MAP_BANK(config, m_cpu_periphs[1]).set_options(ENDIANNESS_BIG, 8, 16, 0x1000);
	m_cpu_periphs[1]->set_addrmap(AS_PROGRAM, &cmi_state::cpu_periphs_map<1>);

	ls259_device &cpulatch(LS259(config, "cpulatch")); // 6D (Q209)
	cpulatch.q_out_cb<0>().set(FUNC(cmi_state::ipi_w<0>));
	cpulatch.q_out_cb<1>().set(FUNC(cmi_state::ipi_w<1>));
	cpulatch.q_out_cb<2>().set(FUNC(cmi_state::nmi_reset_w<0>));
	cpulatch.q_out_cb<3>().set(FUNC(cmi_state::nmi_reset_w<1>));
	cpulatch.q_out_cb<4>().set(FUNC(cmi_state::ms_w<0>));
	cpulatch.q_out_cb<5>().set(FUNC(cmi_state::ms_w<1>));
	cpulatch.q_out_cb<6>().set_inputline(m_maincpu1, M6809_FIRQ_LINE);
	cpulatch.q_out_cb<7>().set_inputline(m_maincpu2, M6809_FIRQ_LINE);

	M68000(config, m_midicpu, 20_MHz_XTAL / 2);
	m_midicpu->set_addrmap(AS_PROGRAM, &cmi_state::midicpu_map);

	MC6809E(config, m_cmi07cpu, Q209_CPU_CLOCK);
	m_cmi07cpu->set_addrmap(AS_PROGRAM, &cmi_state::cmi07cpu_map);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER, rgb_t::green());
	m_screen->set_raw(PIXEL_CLOCK, HTOTAL, HBLANK_END, HBLANK_START, VTOTAL, VBLANK_END, VBLANK_START);
	m_screen->set_screen_update(FUNC(cmi_state::screen_update_cmi2x));
	m_screen->screen_vblank().set(FUNC(cmi_state::cmi_iix_vblank));

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	MSM5832(config, m_msm5832, 32.768_kHz_XTAL);

	I8214(config, m_i8214[0], 1000000);
	m_i8214[0]->int_wr_callback().set(FUNC(cmi_state::i8214_1_int_w));
	I8214(config, m_i8214[1], 1000000);
	m_i8214[1]->int_wr_callback().set(FUNC(cmi_state::i8214_2_int_w));
	I8214(config, m_i8214[2], 1000000);
	m_i8214[2]->int_wr_callback().set(FUNC(cmi_state::i8214_3_int_w));
	m_i8214[2]->enlg_wr_callback().set(FUNC(cmi_state::i8214_3_enlg));

	INPUT_MERGER_ANY_HIGH(config, m_maincpu1_irq_merger).output_handler().set_inputline(m_maincpu1, M6809_IRQ_LINE);
	INPUT_MERGER_ANY_HIGH(config, m_maincpu2_irq0_merger).output_handler().set(FUNC(cmi_state::maincpu2_irq0_w));

	PIA6821(config, m_q133_pia[0]);
	m_q133_pia[0]->readpa_handler().set(FUNC(cmi_state::q133_1_porta_r));
	m_q133_pia[0]->writepa_handler().set(FUNC(cmi_state::q133_1_porta_w));
	m_q133_pia[0]->writepb_handler().set(FUNC(cmi_state::q133_1_portb_w));
	m_q133_pia[0]->irqa_handler().set("rtc_irq_merger", FUNC(input_merger_device::in_w<0>));
	m_q133_pia[0]->irqb_handler().set("rtc_irq_merger", FUNC(input_merger_device::in_w<1>));

	INPUT_MERGER_ANY_HIGH(config, "rtc_irq_merger").output_handler().set(FUNC(cmi_state::msm5832_irq_w));

	PIA6821(config, m_q133_pia[1]);

	PTM6840(config, m_q133_ptm, SYSTEM_CAS_CLOCK);
	m_q133_ptm->set_external_clocks(1024, 1, 111); // Third is todo
	m_q133_ptm->irq_callback().set(m_maincpu2_irq0_merger, FUNC(input_merger_any_high_device::in_w<0>));

	PIA6821(config, m_q219_pia);
	m_q219_pia->readpb_handler().set(FUNC(cmi_state::pia_q219_b_r));
	m_q219_pia->writepa_handler().set(FUNC(cmi_state::vscroll_w));
	m_q219_pia->writepb_handler().set(FUNC(cmi_state::video_attr_w));
	m_q219_pia->irqa_handler().set(FUNC(cmi_state::pia_q219_irqa));
	m_q219_pia->irqb_handler().set(FUNC(cmi_state::pia_q219_irqb));

	PTM6840(config, m_q219_ptm, SYSTEM_CAS_CLOCK);
	m_q219_ptm->set_external_clocks(HBLANK_FREQ.dvalue(), VBLANK_FREQ.dvalue(), SYSTEM_CAS_CLOCK.dvalue() / 2.0);
	m_q219_ptm->irq_callback().set(FUNC(cmi_state::ptm_q219_irq));

	PIA6821(config, m_cmi02_pia[0]);
	m_cmi02_pia[0]->readpa_handler().set(FUNC(cmi_state::cmi02_chsel_r));
	m_cmi02_pia[0]->writepa_handler().set(FUNC(cmi_state::cmi02_chsel_w));
	m_cmi02_pia[0]->readpb_handler().set(FUNC(cmi_state::master_tune_r));
	m_cmi02_pia[0]->writepb_handler().set(FUNC(cmi_state::master_tune_w));

	PIA6821(config, m_cmi02_pia[1]);
	m_cmi02_pia[1]->irqa_handler().set(FUNC(cmi_state::cmi02_pia2_irqa_w));
	m_cmi02_pia[1]->ca1_w(0);
	m_cmi02_pia[1]->cb2_handler().set(FUNC(cmi_state::cmi02_pia2_cb2_w));

	PTM6840(config, m_cmi02_ptm, SYSTEM_CAS_CLOCK);
	m_cmi02_ptm->set_external_clocks(0, 0, 0);
	m_cmi02_ptm->o2_callback().set(FUNC(cmi_state::cmi02_ptm_o2));
	m_cmi02_ptm->irq_callback().set(FUNC(cmi_state::cmi02_ptm_irq));

	clock_device &q133_acia_clock(CLOCK(config, "q133_acia_clock", 1.8432_MHz_XTAL / 12));
	q133_acia_clock.signal_handler().set(FUNC(cmi_state::q133_acia_clock));

	for (auto &acia : m_q133_acia)
		MOS6551(config, acia, 1.8432_MHz_XTAL).set_xtal(1.8432_MHz_XTAL);

	m_q133_acia[0]->irq_handler().set("q133_acia_irq", FUNC(input_merger_device::in_w<0>));
	m_q133_acia[1]->irq_handler().set("q133_acia_irq", FUNC(input_merger_device::in_w<1>));
	m_q133_acia[2]->irq_handler().set("q133_acia_irq", FUNC(input_merger_device::in_w<2>));
	m_q133_acia[3]->irq_handler().set("q133_acia_irq", FUNC(input_merger_device::in_w<3>));

	INPUT_MERGER_ANY_HIGH(config, "q133_acia_irq").output_handler().set(FUNC(cmi_state::q133_acia_irq));

	m_q133_acia[0]->txd_handler().set("mkbd", FUNC(cmi_music_keyboard_device::cmi_rxd_w));
	m_q133_acia[0]->rts_handler().set("mkbd", FUNC(cmi_music_keyboard_device::cmi_cts_w));

	// Musical keyboard
	cmi_music_keyboard_device &mkbd(CMI_MUSIC_KEYBOARD(config, "mkbd"));
	mkbd.cmi_txd_handler().set(m_q133_acia[0], FUNC(mos6551_device::write_rxd));
	mkbd.cmi_rts_handler().set(m_q133_acia[0], FUNC(mos6551_device::write_cts));
	mkbd.kbd_txd_handler().set("alphakeys", FUNC(cmi_alphanumeric_keyboard_device::rxd_w));
	mkbd.kbd_rts_handler().set("alphakeys", FUNC(cmi_alphanumeric_keyboard_device::cts_w));

	// Alphanumeric keyboard
	cmi_alphanumeric_keyboard_device &alphakeys(CMI_ALPHANUMERIC_KEYBOARD(config, "alphakeys"));
	alphakeys.txd_handler().set("mkbd", FUNC(cmi_music_keyboard_device::kbd_rxd_w));
	alphakeys.rts_handler().set("mkbd", FUNC(cmi_music_keyboard_device::kbd_cts_w));

	PTM6840(config, m_cmi07_ptm, 2000000); // ptm_cmi07_config
	m_cmi07_ptm->irq_callback().set(FUNC(cmi_state::cmi07_irq));

	PTM6840(config, m_midi_ptm[0], 0);
	m_midi_ptm[0]->set_external_clocks(0, 384000, 0); // C1 is 0, C2 is 384kHz per schematic block diagram, C3 is CLICK SYNC IN
	//m_midi_ptm[0]->o1_callback().set(FUNC(cmi_state::midi_ptm0_c1_w)); // TIMER 1A O/P per schematic
	//m_midi_ptm[0]->o2_callback().set(FUNC(cmi_state::midi_ptm0_c2_w)); // CLK 2 per schematic
	m_midi_ptm[0]->o3_callback().set(FUNC(cmi_state::midi_ptm0_c3_w));

	PTM6840(config, m_midi_ptm[1], 0); // entirely clocked by PTM 0
	//m_midi_ptm[1]->o1_callback().set(FUNC(cmi_state::midi_sync_out_1_w)); // SYNC OUT 1 per schematic
	//m_midi_ptm[1]->o2_callback().set(FUNC(cmi_state::midi_sync_out_2_w)); // SYNC OUT 2 per schematic
	//m_midi_ptm[1]->o3_callback().set(FUNC(cmi_state::midi_sync_out_3_w)); // SYNC OUT 3 per schematic

	clock_device &midi_clock(CLOCK(config, "midi_clock", 20_MHz_XTAL / 10));
	midi_clock.signal_handler().set(m_midi_acia[0], FUNC(acia6850_device::write_rxc));
	midi_clock.signal_handler().append(m_midi_acia[0], FUNC(acia6850_device::write_txc));
	//midi_clock.signal_handler().append(m_midi_acia[1], FUNC(acia6850_device::write_rxc));
	//midi_clock.signal_handler().append(m_midi_acia[1], FUNC(acia6850_device::write_txc));
	//midi_clock.signal_handler().append(m_midi_acia[2], FUNC(acia6850_device::write_rxc));
	//midi_clock.signal_handler().append(m_midi_acia[2], FUNC(acia6850_device::write_txc));
	//midi_clock.signal_handler().append(m_midi_acia[3], FUNC(acia6850_device::write_txc));

	for (int i = 0; i < 4; i++)
	{
		ACIA6850(config, m_midi_acia[i]);
		m_midi_acia[i]->txd_handler().set(m_midi_out[i], FUNC(midi_port_device::write_txd));

		MIDI_PORT(config, m_midi_out[i]);
		midiout_slot(*m_midi_out[i]);
	}

	for (int i = 0; i < 3; i++)
	{
		MIDI_PORT(config, m_midi_in[i]);
		midiin_slot(*m_midi_in[i]);
		m_midi_in[i]->rxd_handler().set(m_midi_acia[i], FUNC(acia6850_device::write_rxd));
	}

	INPUT_MERGER_ANY_HIGH(config, "midi_ptm_irq").output_handler().set_inputline(m_midicpu, M68K_IRQ_2);
	m_midi_ptm[0]->irq_callback().set("midi_ptm_irq", FUNC(input_merger_device::in_w<0>));
	m_midi_ptm[1]->irq_callback().set("midi_ptm_irq", FUNC(input_merger_device::in_w<1>));

	INPUT_MERGER_ANY_HIGH(config, "midi_acia_irq").output_handler().set_inputline(m_midicpu, M68K_IRQ_3);
	m_midi_acia[0]->irq_handler().set("midi_acia_irq", FUNC(input_merger_device::in_w<0>));
	m_midi_acia[1]->irq_handler().set("midi_acia_irq", FUNC(input_merger_device::in_w<1>));
	m_midi_acia[2]->irq_handler().set("midi_acia_irq", FUNC(input_merger_device::in_w<2>));
	m_midi_acia[3]->irq_handler().set("midi_acia_irq", FUNC(input_merger_device::in_w<3>));

	FD1791(config, m_wd1791, 16_MHz_XTAL / 8); // wd1791_interface
	m_wd1791->intrq_wr_callback().set(FUNC(cmi_state::wd1791_irq));
	m_wd1791->drq_wr_callback().set(FUNC(cmi_state::wd1791_drq));
	FLOPPY_CONNECTOR(config, m_floppy[0], cmi2x_floppies, "8dsdd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[1], cmi2x_floppies, "8dsdd", floppy_image_device::default_mfm_floppy_formats);

	SPEAKER(config, "mono").front_center();

	// Channel cards
	CMI01A_CHANNEL_CARD(config, m_channels[0], SYSTEM_CAS_CLOCK, 0);
	m_channels[0]->add_route(ALL_OUTPUTS, "mono", 0.125);
	m_channels[0]->irq_callback().set(FUNC(cmi_state::channel_irq<0>));
	CMI01A_CHANNEL_CARD(config, m_channels[1], SYSTEM_CAS_CLOCK, 1);
	m_channels[1]->add_route(ALL_OUTPUTS, "mono", 0.125);
	m_channels[1]->irq_callback().set(FUNC(cmi_state::channel_irq<1>));
	CMI01A_CHANNEL_CARD(config, m_channels[2], SYSTEM_CAS_CLOCK, 2);
	m_channels[2]->add_route(ALL_OUTPUTS, "mono", 0.125);
	m_channels[2]->irq_callback().set(FUNC(cmi_state::channel_irq<2>));
	CMI01A_CHANNEL_CARD(config, m_channels[3], SYSTEM_CAS_CLOCK, 3);
	m_channels[3]->add_route(ALL_OUTPUTS, "mono", 0.125);
	m_channels[3]->irq_callback().set(FUNC(cmi_state::channel_irq<3>));
	CMI01A_CHANNEL_CARD(config, m_channels[4], SYSTEM_CAS_CLOCK, 4);
	m_channels[4]->add_route(ALL_OUTPUTS, "mono", 0.125);
	m_channels[4]->irq_callback().set(FUNC(cmi_state::channel_irq<4>));
	CMI01A_CHANNEL_CARD(config, m_channels[5], SYSTEM_CAS_CLOCK, 5);
	m_channels[5]->add_route(ALL_OUTPUTS, "mono", 0.125);
	m_channels[5]->irq_callback().set(FUNC(cmi_state::channel_irq<5>));
	CMI01A_CHANNEL_CARD(config, m_channels[6], SYSTEM_CAS_CLOCK, 6);
	m_channels[6]->add_route(ALL_OUTPUTS, "mono", 0.125);
	m_channels[6]->irq_callback().set(FUNC(cmi_state::channel_irq<6>));
	CMI01A_CHANNEL_CARD(config, m_channels[7], SYSTEM_CAS_CLOCK, 7);
	m_channels[7]->add_route(ALL_OUTPUTS, "mono", 0.125);
	m_channels[7]->irq_callback().set(FUNC(cmi_state::channel_irq<7>));
}

ROM_START( cmi2x )
	/* Q133 Processor control card */
	ROM_REGION( 0x3000, "q133", 0 )
	ROM_LOAD( "q9f0mrk1.bin", 0x000, 0x800, CRC(16f195cc) SHA1(fcc4be370ba60ae5a4145c36cdbdc97a7be91f8f) )
	ROM_LOAD( "f8lmrk5.bin",  0x800, 0x800, CRC(cfc7967f) SHA1(0695cc757cf6fab35414dc068dd2a3e50084685c) )

	/* For CPU1 */
	ROM_COPY( "q133", 0x000, 0x1000, 0x800 )
	ROM_COPY( "q133", 0x800, 0x1800, 0x400 )

	/* For CPU2 */
	ROM_COPY( "q133", 0x000, 0x2000, 0x800 )
	ROM_COPY( "q133", 0xc00, 0x2800, 0x400 )

	/* General Interface (SMPTE/MIDI) CPU */
	ROM_REGION( 0x4000, "smptemidi", 0 )
	ROM_LOAD16_BYTE( "mon1110e.bin", 0x0000, 0x2000, CRC(476f7d5f) SHA1(9af21e0072eaa58cae42947c20dca05d35dfadd0) )
	ROM_LOAD16_BYTE( "mon1110o.bin", 0x0001, 0x2000, CRC(150c8ebe) SHA1(bbd371bebac29628f60537832d0587e83323ad01) )

	/* QFC9 Floppy disk controller driver */
	ROM_REGION( 0x800, "qfc9", 0 )
	ROM_LOAD( "dqfc911.bin", 0x00, 0x800, CRC(5bc38db2) SHA1(bd840e19e51a336e669c40b9e18cdaf6b3c62a8a) )

	// All of these PROM dumps have been trimmed to size from within a roughly 2x-bigger file.
	// The actual sizes are known from the schematics and the starting address of the actual PROM data was obvious
	// based on repeated data in some of the 256x4 PROMs, but it would be nice to get redumps, in the extremely
	// unlikely event that someone finds a CMI IIx for sale.
	ROM_REGION( 0x420, "proms", 0 )
	ROM_LOAD( "brom.bin",   0x000, 0x100, CRC(3f730d15) SHA1(095df6eee95b9ad6418b910fb5d2ae46913750f9) ) // Unknown use, lightgun/graphics card
	ROM_LOAD( "srom.bin",   0x100, 0x100, CRC(a1b4b71b) SHA1(6ea96480af2f1e43967f209218a74fc17972ce0e) ) // Used to generate signal timing for lightpen
	ROM_LOAD( "mrom.bin",   0x200, 0x100, CRC(dc26642c) SHA1(49b207ff80d1b055c3b855dc954129846c49bfe3) ) // Unknown use, master card
	ROM_LOAD( "timrom.bin", 0x300, 0x100, CRC(a426e4a2) SHA1(6b7ea128c730f5afd1042820ccd55bbda683afd8) ) // Unknown use, master card
	ROM_LOAD( "wrom.bin",   0x400, 0x020, CRC(68a9e17f) SHA1(c3364a37a8d19a1882d7910add1c1df9b63ee32c) ) // Unknown use, lightgun/graphics card
ROM_END

/* TODO: Machine start? */
void cmi_state::init_cmi2x()
{
}

} // anonymous namespace


CONS( 1983, cmi2x, 0, 0, cmi2x, cmi2x, cmi_state, init_cmi2x, "Fairlight", "CMI IIx", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



cms.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
// thanks-to:Phill Harvey-Smith
/***************************************************************************

    CMS 6502 Development System

    This is an industrial 6502 system in a rack chassis consisting of a
    back-plane and plug-in cards, with two 3.5" floppy disk drives, a
    "SIMON" System Monitor, DFS, and an extended version of BBC BASIC
    called MULTI-BASIC. It has an IBM-XT style keyboard.

    The "Eurocard" standard format cards are as follows:
    - 6502 2nd Processor For The BBC Micro (CMS0026 6502)
    - 40/80 Terminal Card (also handles the keyboard) (CMS 0010-3)
    - High Performance Colour Graphics Card (CMS Video-4)
    - Versatile Interface Board (CMS0006 Issue 4)
    - Analogue Digital Interface Board (CMS00092 A/D I/O)
    - Floppy Disk Controller

    System I/O Allocation
      &FA00-&FBFF Advanced graphics card
      &FC00-&FC0F CPU card VIA
      &FC10-&FC1F GDP card control register
      &FC20-&FC2F GDP card colour register
      &FC30-&FC3F Real-Time-Clock registers (EM M3002)
      &FC40-&FC4F Disc controller registers
      &FC50-&FC5F Disc controller drive select register
      &FC60-&FC6F IEEE controller card
      &FC70-&FC7F Memory select register (on CPU card)
      &FC80-&FC8F VIA1 on VIB card
      &FC90-&FC9F VIA2 on VIB card
      &FCA0-&FCAF VIA3 on VIB card
      &FCB0-&FCBF VIA4 on VIB card (Printer interface)
      &FCC0-&FCCF Analogue card ADC
      &FCD0-&FCDF Analogue card DAC
      &FCE0-&FCEF Analogue card VIA
      &FCF0-&FCFF Serial Interface on VIB card
      &FD00-&FD1F Reserved
      &FD20-&FD2F 40/80 column card video processor
      &FD30-&FD3F 40/80 column card VIA (printer interface)
      &FD40-&FD4F 40/80 column card serial interface
      &FD50-&FD5F Reserved for future I/O cards

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

#include "emu.h"

#include "bus/acorn/bus.h"
#include "cpu/m6502/m6502.h"
#include "machine/6522via.h"
#include "machine/input_merger.h"
#include "machine/m3002.h"


namespace {

class cms_state : public driver_device
{
public:
	cms_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "rom")
		, m_bank1(*this, "bank1")
		, m_via(*this, "via")
		, m_irqs(*this, "irqs")
		, m_bus(*this, "bus")
		, m_map_select(0)
	{ }

	void cms6502(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void map_select_w(uint8_t data);
	void page_select_w(uint8_t data);

	required_device<cpu_device> m_maincpu;
	required_memory_region m_rom;
	required_memory_bank m_bank1;
	required_device<via6522_device> m_via;
	required_device<input_merger_device> m_irqs;
	required_device<acorn_bus_device> m_bus;

	void cms6502_mem(address_map &map) ATTR_COLD;

	uint8_t m_map_select;
	uint8_t m_page_select[4];
};


void cms_state::cms6502_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).ram();  /* socket M1 43256C-12 32K RAM */
	map(0x8000, 0xbfff).bankr("bank1").w(FUNC(cms_state::page_select_w));
	map(0xc000, 0xffff).rom().region("mos", 0);
	map(0xfc00, 0xfdff).lrw8(NAME([this](offs_t offset) { return m_bus->read(offset | 0xfc00); }), NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0xfc00, data); }));
	map(0xfc00, 0xfc0f).m(m_via, FUNC(via6522_device::map));
	map(0xfc30, 0xfc30).mirror(0xf).rw("rtc", FUNC(m3002_device::read), FUNC(m3002_device::write));
	map(0xfc70, 0xfc7f).w(FUNC(cms_state::map_select_w));
}


void cms_state::map_select_w( uint8_t data)
{
	m_map_select = data & 0x03;
	m_bank1->set_entry((m_map_select << 2) | m_page_select[m_map_select]);
}

void cms_state::page_select_w(uint8_t data)
{
	/* 27513 in socket M4 (map 0) */
	if (m_map_select == 0x00)
	{
		m_page_select[m_map_select] = data & 0x03;
	}
	m_bank1->set_entry((m_map_select << 2) | m_page_select[m_map_select]);
}


void cms_state::machine_start()
{
	m_bank1->configure_entries(0, 16, m_rom->base(), 0x4000);

	/* register for save states */
	save_item(NAME(m_map_select));
	save_item(NAME(m_page_select));
}


void cms_state::machine_reset()
{
	m_map_select = 0x00;
	for (int i = 0; i < 4; i++)
	{
		m_page_select[i] = 0x00;
	}
	m_bank1->set_entry(0);
}


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

void cms_state::cms6502(machine_config &config)
{
	M6502(config, m_maincpu, 1_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &cms_state::cms6502_mem);

	INPUT_MERGER_ANY_HIGH(config, m_irqs).output_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	MOS6522(config, m_via, 1_MHz_XTAL);
	m_via->irq_handler().set("irqs", FUNC(input_merger_device::in_w<0>));

	M3002(config, "rtc", 32.768_kHz_XTAL);

	/* 7 Slot Backplane */
	ACORN_BUS(config, m_bus, 0);
	m_bus->out_irq_callback().set(m_irqs, FUNC(input_merger_device::in_w<1>));
	m_bus->out_nmi_callback().set_inputline(m_maincpu, M6502_NMI_LINE);
	ACORN_BUS_SLOT(config, "bus1", m_bus, cms_bus_devices, "4080term");
	ACORN_BUS_SLOT(config, "bus2", m_bus, cms_bus_devices, nullptr);
	ACORN_BUS_SLOT(config, "bus3", m_bus, cms_bus_devices, "fdc");
	ACORN_BUS_SLOT(config, "bus4", m_bus, cms_bus_devices, "hires");
	ACORN_BUS_SLOT(config, "bus5", m_bus, cms_bus_devices, nullptr);
	ACORN_BUS_SLOT(config, "bus6", m_bus, cms_bus_devices, nullptr);
}


/***************************************************************************
    ROM definitions
***************************************************************************/

ROM_START( cms6502 )
	ROM_REGION(0x04000, "mos", 0) /* 27128 */
	ROM_LOAD("6502 system os.m5", 0x0000, 0x4000, CRC(1b6da63d) SHA1(5c0afc55288ad86e1d6e653dda9c5dcccdaf0ff2))

	ROM_REGION(0x40000, "rom", 0)
	/* Socket M4 27513 */
	ROM_LOAD("basic.m4",       0x00000, 0x4000, CRC(e27b6146) SHA1(ae89ee695bed6f49402f009ded16ad3a36f64c28))
	ROM_LOAD("dfs-1.4.m4",     0x04000, 0x4000, CRC(0743ddb5) SHA1(cd61956a10510a6954cba9ba17f70c2991e81d9b))
	ROM_LOAD("multi-basic.m4", 0x08000, 0x4000, CRC(fb785563) SHA1(9f97582ba0b82f314008bf95eacb00e7bced3cac))
	ROM_LOAD("simon-2.02.m4",  0x0c000, 0x4000, CRC(d1f1b633) SHA1(991c3691a70f45fff3c828c8c0a0906b9f5e34fc))
	/* Socket M3 empty */
	/* Socket M2 27128 */
	ROM_LOAD("gdp.m2",         0x20000, 0x4000, CRC(f545293b) SHA1(532c45ac4661643c6c6f633d504ef72e800cc900))
	/* Socket M1 contains RAM, not mapped as ROM */

	ROM_REGION(0x200, "proms", 0)
	ROM_LOAD("bassys2.ic8", 0x0000, 0x0200, CRC(417ff0b4) SHA1(878718accb83f18456fefaf67a0e4a6a407113e4)) // 512x8
ROM_END

} // anonymous namespace


/*    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY                             FULLNAME                       FLAGS */
COMP( 1986, cms6502, 0,      0,      cms6502, 0,     cms_state, empty_init, "Cambridge Microprocessor Systems", "CMS 6502 Development System", MACHINE_NO_SOUND_HW )



cnchess.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Novag Chinese Chess (象棋, model 866)

Novag's first Xiangqi computer, mainly sold in Hong Kong. Model 8710 (棋王, Chess
King) was distributed by Yorter Electronics for the local market. It's the same
hardware as model 866. The newer model 9300 is also presumed to be the same.

Hardware notes:
- PCB label: 100054
- Hitachi HD6305Y0P @ ~8MHz (LC oscillator)
- 9*10 chessboard buttons, 19+4 leds, piezo

Strangely enough, it doesn't use the HD6305 internal timer. The only peripherals
it makes use of are the I/O ports.

BTANB:
- it uses 馬 and 車 for red horse and chariot instead of 傌 and 俥, newer Novag
  Xiangqi computers have this too, so it's a design choice?

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

#include "emu.h"

#include "cpu/m6805/hd6305.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "novag_cnchess.lh"


namespace {

class cnchess_state : public driver_device
{
public:
	cnchess_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.0")
	{ }

	void cnchess(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<hd6305y0_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport m_inputs;

	u16 m_inp_mux = 0;

	void init_board(u8 data);

	// I/O handlers
	u16 input_r();
	u8 input1_r();
	u8 input2_r();
	template<int N> void input_w(u8 data);
	void control_w(u8 data);
};



/*******************************************************************************
    Initialization
*******************************************************************************/

void cnchess_state::machine_start()
{
	save_item(NAME(m_inp_mux));
}

void cnchess_state::init_board(u8 data)
{
	// 1st row
	for (int i = 0; i < 5; i++)
	{
		m_board->write_piece(0, i, 3 + i);
		m_board->write_piece(0, 8 - i, 3 + i);
	}

	// cannons
	m_board->write_piece(2, 1, 2);
	m_board->write_piece(2, 7, 2);

	// soldiers
	for (int i = 0; i < 5; i++)
		m_board->write_piece(3, i * 2, 1);

	// mirrored for black pieces
	for (int y = 0; y < 4; y++)
		for (int x = 0; x < 9; x++)
		{
			u8 piece = m_board->read_piece(y, x);
			if (piece != 0)
				m_board->write_piece(9 - y, x, piece + 7);
		}
}



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

u16 cnchess_state::input_r()
{
	u16 data = 0;
	const u16 inp_mux = bitswap<10>(m_inp_mux,9,8,7,4,3,5,6,2,1,0);

	// read chessboard
	for (int i = 0; i < 10; i++)
		if (BIT(inp_mux, i))
			data |= m_board->read_file(i);

	// read buttons
	if (inp_mux & m_inputs->read())
		data |= 0x200;

	return data;
}

u8 cnchess_state::input1_r()
{
	// A0-A7: read inputs low
	return bitswap<8>(~input_r(),0,1,2,5,6,4,3,7);
}

u8 cnchess_state::input2_r()
{
	// B6,B7: read inputs high
	return bitswap<2>(~input_r(),8,9) << 6 | 0x3f;
}

template<int N>
void cnchess_state::input_w(u8 data)
{
	// Ex,F0,F1: input mux, led data
	const u8 shift = N * 8;
	const u16 mask = 0xff << shift;

	m_inp_mux = ((m_inp_mux & ~mask) | (~data << shift & mask)) & 0x3ff;
	m_display->write_mx(m_inp_mux);
}

void cnchess_state::control_w(u8 data)
{
	// G4: speaker out
	m_dac->write(BIT(~data, 4));

	// G5-G7: led select
	m_display->write_my(~data >> 5 & 7);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( cnchess )
	PORT_START("IN.0")
	PORT_BIT(0x00001, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("Go")
	PORT_BIT(0x00002, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Change Color")
	PORT_BIT(0x00004, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Set Up / Pawn")
	PORT_BIT(0x00008, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Verify / Cannon")
	PORT_BIT(0x00010, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x00020, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Take Back / Knight")
	PORT_BIT(0x00040, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Hint / Bishop")
	PORT_BIT(0x00080, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Sound / Minister")
	PORT_BIT(0x00100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Level / General")
	PORT_BIT(0x00200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game / Clear Board")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void cnchess_state::cnchess(machine_config &config)
{
	// basic machine hardware
	HD6305Y0(config, m_maincpu, 8'000'000); // approximation, no XTAL
	m_maincpu->read_porta().set(FUNC(cnchess_state::input1_r));
	m_maincpu->read_portb().set(FUNC(cnchess_state::input2_r));
	m_maincpu->write_porte().set(FUNC(cnchess_state::input_w<0>));
	m_maincpu->write_portf().set(FUNC(cnchess_state::input_w<1>));
	m_maincpu->write_portg().set(FUNC(cnchess_state::control_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(FUNC(cnchess_state::init_board));
	m_board->set_size(10, 9); // rotated by 90 degrees
	m_board->set_spawnpoints(14);
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(3, 10);
	config.set_default_layout(layout_novag_cnchess);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.5);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( cnchess )
	ROM_REGION( 0x1ec0, "maincpu", 0 )
	ROM_LOAD("novag_866_35y0b12p", 0x0000, 0x1ec0, CRC(234ef959) SHA1(9ab7310275017dd4b6b152f205d6cd65014da5a6) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1986, cnchess, 0,      0,      cnchess, cnchess, cnchess_state, empty_init, "Novag Industries", "Chinese Chess", MACHINE_SUPPORTS_SAVE )



coco12.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nathan Woods
/***************************************************************************

    coco12.cpp

    TRS-80 Radio Shack Color Computer 1/2

    Mathis Rosenhauer (original driver)
    Nate Woods (current maintainer)
    Tim Lindner (VHD and other work)

    TRS-80 Deluxe Color Computer

    Prototype Color Computer with added MOS6551 and AY-3-8913

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

#include "emu.h"
#include "coco12.h"

#include "bus/coco/cococart.h"
#include "bus/coco/coco_t4426.h"

#include "cpu/m6809/m6809.h"
#include "cpu/m6809/hd6309.h"
#include "imagedev/cassette.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "formats/coco_cas.h"

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

void coco12_state::coco_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(m_sam, FUNC(sam6883_device::read), FUNC(sam6883_device::write));
}

void coco12_state::coco_ram(address_map &map)
{
	// mapped by configure_sam
}

void coco12_state::coco_rom0(address_map &map)
{
	// $8000-$9FFF
	map(0x0000, 0x1fff).rom().region(MAINCPU_TAG, 0x0000).nopw();
}

void coco12_state::coco_rom1(address_map &map)
{
	// $A000-$BFFF
	map(0x0000, 0x1fff).rom().region(MAINCPU_TAG, 0x2000).nopw();
}

void coco12_state::coco_rom2(address_map &map)
{
	// $C000-$FEFF
	map(0x0000, 0x3eff).rw(m_cococart, FUNC(cococart_slot_device::cts_read), FUNC(cococart_slot_device::cts_write));
}

void deluxecoco_state::deluxecoco_rom2(address_map &map)
{
	// $C000-$FEFF
	map(0x0000, 0x3eff).view(m_rom_view);

	m_rom_view[0](0x0000, 0x3eff).rw(m_cococart, FUNC(cococart_slot_device::cts_read), FUNC(cococart_slot_device::cts_write));
	m_rom_view[1](0x0000, 0x3eff).rom().region(MAINCPU_TAG, 0x4000).nopw();
}

void coco12_state::coco_io0(address_map &map)
{
	// $FF00-$FF1F
	map(0x00, 0x03).mirror(0x1c).rw(m_pia_0, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
}

void coco12_state::coco_io1(address_map &map)
{
	// $FF20-$FF3F
	map(0x00, 0x03).mirror(0x1c).r(m_pia_1, FUNC(pia6821_device::read)).w(FUNC(coco12_state::ff20_write));
}

void coco12_state::coco_io2(address_map &map)
{
	// $FF40-$FF5F
	map(0x00, 0x1f).rw(FUNC(coco12_state::ff40_read), FUNC(coco12_state::ff40_write));
}

void coco12_state::coco_ff60(address_map &map)
{
	// $FF60-$FFDF
	map(0x00, 0x7f).rw(FUNC(coco12_state::ff60_read), FUNC(coco12_state::ff60_write));
}

void coco12_state::ms1600_rom2(address_map &map)
{
	// $C000-$FEFF
	map(0x0000, 0x2fff).rw(m_cococart, FUNC(cococart_slot_device::cts_read), FUNC(cococart_slot_device::cts_write));
	map(0x3000, 0x3eff).rom().region(MAINCPU_TAG, 0x7000).nopw();
}

void deluxecoco_state::deluxecoco_io1(address_map &map)
{
	// $FF20-$FF3F
	map(0x00, 0x03).r(m_pia_1, FUNC(pia6821_device::read)).w(FUNC(coco12_state::ff20_write));
	map(0x10, 0x10).w(FUNC(deluxecoco_state::ff30_write));
	map(0x18, 0x19).w(m_psg, FUNC(ay8913_device::data_address_w));
	map(0x1c, 0x1f).rw(m_acia, FUNC(mos6551_device::read), FUNC(mos6551_device::write));
}


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

//--------------------------------------------------------------------------
//  INPUT_PORTS( coco_analog_control )
//
//  Other CoCo controllers were compatible with the Dragon, even if no
//  software is known to make use of them We add them here, based on this
//  compatibility, in case anyone wants to use them in homebrew software
//--------------------------------------------------------------------------

INPUT_PORTS_START( coco_analog_control )
	PORT_START(CTRL_SEL_TAG)  /* Select Controller Type */
	PORT_CONFNAME( 0x0f, 0x01, "Right Controller Port (P1)")            PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco_state::joystick_mode_changed), 0)
	PORT_CONFSETTING(  0x00, "Unconnected" )
	PORT_CONFSETTING(  0x01, "Joystick" )
	PORT_CONFSETTING(  0x02, "The Rat Graphics Mouse" )                 PORT_CONDITION(CTRL_SEL_TAG, 0xf0, NOTEQUALS, 0x20)
	PORT_CONFSETTING(  0x03, "Diecom Light Gun Adaptor" )               PORT_CONDITION(CTRL_SEL_TAG, 0xf0, NOTEQUALS, 0x30)
	PORT_CONFNAME( 0xf0, 0x10, "Left Controller Port (P2)")             PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco_state::joystick_mode_changed), 0)
	PORT_CONFSETTING(  0x00, "Unconnected" )
	PORT_CONFSETTING(  0x10, "Joystick" )
	PORT_CONFSETTING(  0x20, "The Rat Graphics Mouse" )                 PORT_CONDITION(CTRL_SEL_TAG, 0x0f, NOTEQUALS, 0x02)
	PORT_CONFSETTING(  0x30, "Diecom Light Gun Adaptor" )               PORT_CONDITION(CTRL_SEL_TAG, 0x0f, NOTEQUALS, 0x03)

	PORT_START(HIRES_INTF_TAG)
	PORT_CONFNAME( 0x07, 0x00, "Hi-Res Joystick Interfaces" )
	PORT_CONFSETTING(    0x00, "None" )
	PORT_CONFSETTING(    0x01, "Hi-Res in Right Port" )                 PORT_CONDITION(CTRL_SEL_TAG, 0x0f, EQUALS, 0x01)
	PORT_CONFSETTING(    0x02, "Hi-Res CoCoMax 3 Style in Right Port" ) PORT_CONDITION(CTRL_SEL_TAG, 0x0f, EQUALS, 0x01)
	PORT_CONFSETTING(    0x03, "Hi-Res in Left Port" )                  PORT_CONDITION(CTRL_SEL_TAG, 0xf0, EQUALS, 0x10)
	PORT_CONFSETTING(    0x04, "Hi-Res CoCoMax 3 Style in Left Port" )  PORT_CONDITION(CTRL_SEL_TAG, 0xf0, EQUALS, 0x10)
INPUT_PORTS_END



//-------------------------------------------------
//  INPUT_PORTS( coco_joystick )
//-------------------------------------------------

INPUT_PORTS_START( coco_joystick )
	PORT_START(JOYSTICK_RX_TAG)
	PORT_BIT( 0x3ff, 0x200,  IPT_AD_STICK_X) PORT_NAME("Right Joystick X") PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0,1023) PORT_CODE_DEC(KEYCODE_4_PAD) PORT_CODE_INC(KEYCODE_6_PAD) PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH) PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH) PORT_PLAYER(1) PORT_CONDITION(CTRL_SEL_TAG, 0x0f, EQUALS, 0x01)
	PORT_START(JOYSTICK_RY_TAG)
	PORT_BIT( 0x3ff, 0x200,  IPT_AD_STICK_Y) PORT_NAME("Right Joystick Y") PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0,1023) PORT_CODE_DEC(KEYCODE_8_PAD) PORT_CODE_INC(KEYCODE_2_PAD) PORT_CODE_DEC(JOYCODE_Y_UP_SWITCH)   PORT_CODE_INC(JOYCODE_Y_DOWN_SWITCH)  PORT_PLAYER(1) PORT_CONDITION(CTRL_SEL_TAG, 0x0f, EQUALS, 0x01)
	PORT_START(JOYSTICK_LX_TAG)
	PORT_BIT( 0x3ff, 0x200,  IPT_AD_STICK_X) PORT_NAME("Left Joystick X") PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0,1023) PORT_CODE_DEC(KEYCODE_4_PAD) PORT_CODE_INC(KEYCODE_6_PAD) PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH) PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH) PORT_PLAYER(2) PORT_CONDITION(CTRL_SEL_TAG, 0xf0, EQUALS, 0x10)
	PORT_START(JOYSTICK_LY_TAG)
	PORT_BIT( 0x3ff, 0x200,  IPT_AD_STICK_Y) PORT_NAME("Left Joystick Y") PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0,1023) PORT_CODE_DEC(KEYCODE_8_PAD) PORT_CODE_INC(KEYCODE_2_PAD) PORT_CODE_DEC(JOYCODE_Y_UP_SWITCH)   PORT_CODE_INC(JOYCODE_Y_DOWN_SWITCH)  PORT_PLAYER(2) PORT_CONDITION(CTRL_SEL_TAG, 0xf0, EQUALS, 0x10)
	PORT_START(JOYSTICK_BUTTONS_TAG)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Right Button") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(JOYCODE_BUTTON1) PORT_CODE(MOUSECODE_BUTTON1) PORT_PLAYER(1) PORT_CONDITION(CTRL_SEL_TAG, 0x0f, EQUALS, 0x01)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Left Button")  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(JOYCODE_BUTTON1) PORT_CODE(MOUSECODE_BUTTON1) PORT_PLAYER(2) PORT_CONDITION(CTRL_SEL_TAG, 0xf0, EQUALS, 0x10)
INPUT_PORTS_END



//-------------------------------------------------
//  INPUT_PORTS( coco_beckerport )
//-------------------------------------------------

INPUT_PORTS_START( coco_beckerport )
	PORT_START(BECKERPORT_TAG)
	PORT_CONFNAME( 0x01, 0x00, "Becker Port" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ))
	PORT_CONFSETTING(    0x01, DEF_STR( On ))
INPUT_PORTS_END



//-------------------------------------------------
//  INPUT_PORTS( coco_keyboard )
//-------------------------------------------------
//
//  CoCo keyboard
//
//         PB0 PB1 PB2 PB3 PB4 PB5 PB6 PB7
//    PA6: Ent Clr Brk N/c N/c N/c N/c Shift
//    PA5: 8   9   :   ;   ,   -   .   /
//    PA4: 0   1   2   3   4   5   6   7
//    PA3: X   Y   Z   Up  Dwn Lft Rgt Space
//    PA2: P   Q   R   S   T   U   V   W
//    PA1: H   I   J   K   L   M   N   O
//    PA0: @   A   B   C   D   E   F   G
//-------------------------------------------------

static INPUT_PORTS_START( coco_keyboard )
	PORT_START("row0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')

	PORT_START("row1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("row2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')

	PORT_START("row3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("UP") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("DOWN") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN), 10)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("LEFT") PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(UCHAR_MAMEKEY(LEFT), 8)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("RIGHT") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT), 9)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("row4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!') PORT_CHAR('|')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#') PORT_CHAR('~')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'') PORT_CHAR('^')

	PORT_START("row5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(') PORT_CHAR('[')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')') PORT_CHAR(']')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_MINUS) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<') PORT_CHAR('{')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('-') PORT_CHAR('=') PORT_CHAR('_')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>') PORT_CHAR('}')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?') PORT_CHAR('\\')

	PORT_START("row6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("CLEAR") PORT_CODE(KEYCODE_HOME) PORT_CHAR(12, UCHAR_SHIFT_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("BREAK") PORT_CODE(KEYCODE_END) PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x78, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
INPUT_PORTS_END



//-------------------------------------------------
//  INPUT_PORTS( coco )
//-------------------------------------------------

static INPUT_PORTS_START( coco )
	PORT_INCLUDE( coco_keyboard )
	PORT_INCLUDE( coco_joystick )
	PORT_INCLUDE( coco_analog_control )
	PORT_INCLUDE( coco_beckerport )
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( deluxecoco )
//-------------------------------------------------

static INPUT_PORTS_START( deluxecoco )
	PORT_INCLUDE( coco )
	PORT_MODIFY("row6")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("ALT") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL), UCHAR_SHIFT_2)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
INPUT_PORTS_END



//-------------------------------------------------
//  INPUT_PORTS( cp400c2_keyboard )
//-------------------------------------------------
//
//  CP400 Color II keyboard
//
//         PB0   PB1   PB2   PB3   PB4   PB5   PB6   PB7
//    PA6: Enter Clear Break Ctrl  PA1   PA2   PA3   Shift
//    PA5: 8     9     :     ;     ,     -     .     /
//    PA4: 0     1     2     3     4     5     6     7
//    PA3: X     Y     Z     Up    Down  Left  Right Space
//    PA2: P     Q     R     S     T     U     V     W
//    PA1: H     I     J     K     L     M     N     O
//    PA0: @     A     B     C     D     E     F     G
//-------------------------------------------------

static INPUT_PORTS_START( cp400c2_keyboard )
	PORT_START("row0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')

	PORT_START("row1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("row2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')

	PORT_START("row3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("UP") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP), '^')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("DOWN") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN), 10)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("LEFT") PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(UCHAR_MAMEKEY(LEFT), 8)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("RIGHT") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT), 9)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("row4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("row5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_MINUS) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("row6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("CLEAR") PORT_CODE(KEYCODE_HOME) PORT_CHAR(12)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("BREAK") PORT_CODE(KEYCODE_END) PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("PA1") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("PA2") PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("PA3") PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco12_state::coco_state::keyboard_changed), 0) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( cp400c2 )
//-------------------------------------------------

static INPUT_PORTS_START( cp400c2 )
	PORT_INCLUDE( cp400c2_keyboard )
	PORT_INCLUDE( coco_joystick )
	PORT_INCLUDE( coco_analog_control )
	PORT_INCLUDE( coco_beckerport )
INPUT_PORTS_END


//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  coco_cart
//-------------------------------------------------

void coco_cart(device_slot_interface &device)
{
	coco_cart_add_basic_devices(device);
	coco_cart_add_fdcs(device);
	coco_cart_add_multi_pak(device);
}


//-------------------------------------------------
//  SLOT_INTERFACE_START(t4426_cart)
//-------------------------------------------------

void t4426_cart(device_slot_interface &device)
{
	device.option_add("t4426", COCO_T4426);
}

//-------------------------------------------------
//  machine_config( coco_sound )
//-------------------------------------------------

void coco_state::coco_sound(machine_config &config)
{
	SPEAKER(config, "speaker").front_center();

	// 6-bit D/A: R10-15 = 10K, 20K, 40.2K, 80.6K, 162K, 324K (according to parts list); output also controls joysticks
	DAC_6BIT_BINARY_WEIGHTED(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.125);

	// Single-bit sound: R22 = 10K
	DAC_1BIT(config, "sbs", 0).set_output_range(-1, 1).add_route(ALL_OUTPUTS, "speaker", 0.125);
}


//-------------------------------------------------
//  machine_config ( coco_floating )
//-------------------------------------------------

void coco_state::coco_floating_map(address_map &map)
{
	map(0x0000, 0xFFFF).r(FUNC(coco_state::floating_bus_r));
	map(0x0000, 0xFFFF).nopw(); /* suppress log warnings */
}


void coco_state::coco_floating(machine_config &config)
{
	ADDRESS_MAP_BANK(config, m_floating).set_map(&coco_state::coco_floating_map).set_options(ENDIANNESS_BIG, 8, 16);
}


//-------------------------------------------------
//  DEVICE_INPUT_DEFAULTS_START( printer )
//-------------------------------------------------

static DEVICE_INPUT_DEFAULTS_START( rs_printer )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END


//-------------------------------------------------
//  DEVICE_INPUT_DEFAULTS_START( acia )
//-------------------------------------------------

static DEVICE_INPUT_DEFAULTS_START(acia)
	DEVICE_INPUT_DEFAULTS("RS232_TXBAUD", 0xff, RS232_BAUD_300)
	DEVICE_INPUT_DEFAULTS("RS232_RXBAUD", 0xff, RS232_BAUD_300)
	DEVICE_INPUT_DEFAULTS("RS232_DATABITS", 0xff, RS232_DATABITS_8)
	DEVICE_INPUT_DEFAULTS("RS232_PARITY", 0xff, RS232_PARITY_NONE)
	DEVICE_INPUT_DEFAULTS("RS232_STOPBITS", 0xff, RS232_STOPBITS_1)
DEVICE_INPUT_DEFAULTS_END


//-------------------------------------------------
//  machine_config
//-------------------------------------------------

void coco12_state::coco(machine_config &config)
{
	this->set_clock(XTAL(14'318'181) / 16);

	// basic machine hardware
	MC6809E(config, m_maincpu, DERIVED_CLOCK(1, 1));
	m_maincpu->set_addrmap(AS_PROGRAM, &coco12_state::coco_mem);
	m_maincpu->set_dasm_override(FUNC(coco_state::dasm_override));

	// devices
	INPUT_MERGER_ANY_HIGH(config, m_irqs).output_handler().set_inputline(m_maincpu, M6809_IRQ_LINE);
	INPUT_MERGER_ANY_HIGH(config, m_firqs).output_handler().set_inputline(m_maincpu, M6809_FIRQ_LINE);

	PIA6821(config, m_pia_0);
	m_pia_0->writepa_handler().set(FUNC(coco_state::pia0_pa_w));
	m_pia_0->writepb_handler().set(FUNC(coco_state::pia0_pb_w));
	m_pia_0->tspb_handler().set_constant(0xff);
	m_pia_0->ca2_handler().set(FUNC(coco_state::pia0_ca2_w));
	m_pia_0->cb2_handler().set(FUNC(coco_state::pia0_cb2_w));
	m_pia_0->irqa_handler().set(m_irqs, FUNC(input_merger_device::in_w<0>));
	m_pia_0->irqb_handler().set(m_irqs, FUNC(input_merger_device::in_w<1>));

	PIA6821(config, m_pia_1);
	m_pia_1->readpa_handler().set(FUNC(coco_state::pia1_pa_r));
	m_pia_1->readpb_handler().set(FUNC(coco_state::pia1_pb_r));
	m_pia_1->writepa_handler().set(FUNC(coco_state::pia1_pa_w));
	m_pia_1->writepb_handler().set(FUNC(coco_state::pia1_pb_w));
	m_pia_1->ca2_handler().set(FUNC(coco_state::pia1_ca2_w));
	m_pia_1->cb2_handler().set(FUNC(coco_state::pia1_cb2_w));
	m_pia_1->irqa_handler().set(m_firqs, FUNC(input_merger_device::in_w<0>));
	m_pia_1->irqb_handler().set(m_firqs, FUNC(input_merger_device::in_w<1>));

	SAM6883(config, m_sam, XTAL(14'318'181), m_maincpu);
	m_sam->set_addrmap(0, &coco12_state::coco_ram);
	m_sam->set_addrmap(1, &coco12_state::coco_rom0);
	m_sam->set_addrmap(2, &coco12_state::coco_rom1);
	m_sam->set_addrmap(3, &coco12_state::coco_rom2);
	m_sam->set_addrmap(4, &coco12_state::coco_io0);
	m_sam->set_addrmap(5, &coco12_state::coco_io1);
	m_sam->set_addrmap(6, &coco12_state::coco_io2);
	m_sam->set_addrmap(7, &coco12_state::coco_ff60);

	// Becker Port device
	COCO_DWSOCK(config, m_beckerport, 0);

	// sound hardware
	coco_sound(config);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(coco_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);

	rs232_port_device &rs232(RS232_PORT(config, RS232_TAG, default_rs232_devices, "rs_printer"));
	rs232.dcd_handler().set(m_pia_1, FUNC(pia6821_device::ca1_w));
	rs232.set_option_device_input_defaults("rs_printer", DEVICE_INPUT_DEFAULTS_NAME(rs_printer));

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);

	MC6847(config, m_vdg, XTAL(14'318'181) / 4); // VClk output from MC6883
	m_vdg->set_screen(m_screen);
	m_vdg->hsync_wr_callback().set(FUNC(coco12_state::horizontal_sync));
	m_vdg->fsync_wr_callback().set(FUNC(coco12_state::field_sync));
	m_vdg->input_callback().set(FUNC(coco12_state::sam_read));

	// internal ram
	RAM(config, m_ram).set_default_size("64K").set_extra_options("4K,16K,32K");

	// floating space
	coco_floating(config);

	// cartridge
	COCOCART_SLOT(config, m_cococart, DERIVED_CLOCK(1, 1), coco_cart, "fdc");
	m_cococart->cart_callback().set([this] (int state) { cart_w(state != 0); }); // lambda because name is overloaded
	m_cococart->nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_cococart->halt_callback().set_inputline(m_maincpu, INPUT_LINE_HALT);

	// software lists
	SOFTWARE_LIST(config, "coco_cart_list").set_original("coco_cart").set_filter("COCO");
	SOFTWARE_LIST(config, "coco_flop_list").set_original("coco_flop").set_filter("COCO");
	SOFTWARE_LIST(config, "dragon_cart_list").set_compatible("dragon_cart");

	// virtual hard disks
	COCO_VHD(config, m_vhd_0, 0, m_maincpu);
	COCO_VHD(config, m_vhd_1, 0, m_maincpu);
}

void coco12_state::cocoh(machine_config &config)
{
	coco(config);

	HD6309E(config.replace(), m_maincpu, DERIVED_CLOCK(1, 1));
	m_maincpu->set_addrmap(AS_PROGRAM, &coco12_state::coco_mem);
}

void deluxecoco_state::deluxecoco(machine_config &config)
{
	coco2b(config);

	// Asynchronous Communications Interface Adapter
	MOS6551(config, m_acia, 0);
	m_acia->set_xtal(1.8432_MHz_XTAL);
	m_acia->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<2>));
	m_acia->txd_handler().set(ACIA_TAG, FUNC(rs232_port_device::write_txd));
	m_acia->rts_handler().set(ACIA_TAG, FUNC(rs232_port_device::write_rts));
	m_acia->dtr_handler().set(ACIA_TAG, FUNC(rs232_port_device::write_dtr));

	rs232_port_device &rs232(RS232_PORT(config, ACIA_TAG, default_rs232_devices, nullptr));
	rs232.set_option_device_input_defaults("null_modem", DEVICE_INPUT_DEFAULTS_NAME(acia));
	rs232.set_option_device_input_defaults("pty", DEVICE_INPUT_DEFAULTS_NAME(acia));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(acia));
	rs232.rxd_handler().set(m_acia, FUNC(mos6551_device::write_rxd));
	rs232.dcd_handler().set(m_acia, FUNC(mos6551_device::write_dcd));
	rs232.dsr_handler().set(m_acia, FUNC(mos6551_device::write_dsr));
	rs232.cts_handler().set(m_acia, FUNC(mos6551_device::write_cts));

	// Programable Sound Generator
	AY8913(config, m_psg, DERIVED_CLOCK(2, 1));
	m_psg->set_flags(AY8910_SINGLE_OUTPUT);
	m_psg->add_route(ALL_OUTPUTS, "speaker", 1.0);

	// Adjust Memory Map
	m_sam->set_addrmap(3, &deluxecoco_state::deluxecoco_rom2);
	m_sam->set_addrmap(5, &deluxecoco_state::deluxecoco_io1);

	// Configure Timer
	TIMER(config, m_timer).configure_generic(FUNC(deluxecoco_state::perodic_timer));
}

void coco12_state::coco2b(machine_config &config)
{
	coco(config);

	MC6847T1(config.replace(), m_vdg, XTAL(14'318'181) / 4);
	m_vdg->set_screen(m_screen);
	m_vdg->hsync_wr_callback().set(FUNC(coco12_state::horizontal_sync));
	m_vdg->fsync_wr_callback().set(FUNC(coco12_state::field_sync));
	m_vdg->input_callback().set(FUNC(coco12_state::sam_read));
}

void coco12_state::coco2bh(machine_config &config)
{
	coco2b(config);

	HD6309E(config.replace(), m_maincpu, DERIVED_CLOCK(1, 1));
	m_maincpu->set_addrmap(AS_PROGRAM, &coco12_state::coco_mem);
}

void coco12_state::cp400(machine_config &config)
{
	coco(config);

	m_cococart->set_default_option("cp450_fdc");
}

void coco12_state::t4426(machine_config &config)
{
	coco(config);

	m_cococart->option_reset();
	t4426_cart(*m_cococart);
	m_cococart->set_default_option("t4426");
	m_cococart->set_fixed(true); // This cart is fixed so no way to change it
}

void coco12_state::cd6809(machine_config &config)
{
	coco(config);

	m_cococart->set_default_option("cd6809_fdc");
}

void coco12_state::ms1600(machine_config &config)
{
	coco(config);

	m_sam->set_addrmap(3, &coco12_state::ms1600_rom2);
}

//**************************************************************************
//  ROMS
//**************************************************************************

ROM_START(coco)
	ROM_REGION(0x8000,MAINCPU_TAG,0)
	ROM_DEFAULT_BIOS("b12e11")
	ROM_SYSTEM_BIOS(0, "b10", "Color BASIC v1.0")
	ROMX_LOAD("bas10.rom",    0x2000, 0x2000, CRC(00b50aaa) SHA1(1f08455cd48ce6a06132aea15c4778f264e19539), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "b11", "Color BASIC v1.1")
	ROMX_LOAD("bas11.rom",    0x2000, 0x2000, CRC(6270955a) SHA1(cecb7c24ff1e0ab5836e4a7a8eb1b8e01f1fded3), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "b12", "Color BASIC v1.2")
	ROMX_LOAD("bas12.rom",    0x2000, 0x2000, CRC(54368805) SHA1(0f14dc46c647510eb0b7bd3f53e33da07907d04f), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "b10e10", "Color BASIC v1.0 / Extended Color BASIC v1.0")
	ROMX_LOAD("bas10.rom",    0x2000, 0x2000, CRC(00b50aaa) SHA1(1f08455cd48ce6a06132aea15c4778f264e19539), ROM_BIOS(3))
	ROMX_LOAD("extbas10.rom", 0x0000, 0x2000, CRC(6111a086) SHA1(8aa58f2eb3e8bcfd5470e3e35e2b359e9a72848e), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "b10e11", "Color BASIC v1.0 / Extended Color BASIC v1.1")
	ROMX_LOAD("bas10.rom",    0x2000, 0x2000, CRC(00b50aaa) SHA1(1f08455cd48ce6a06132aea15c4778f264e19539), ROM_BIOS(4))
	ROMX_LOAD("extbas11.rom", 0x0000, 0x2000, CRC(a82a6254) SHA1(ad927fb4f30746d820cb8b860ebb585e7f095dea), ROM_BIOS(4))
	ROM_SYSTEM_BIOS(5, "b11e10", "Color BASIC v1.1 / Extended Color BASIC v1.0")
	ROMX_LOAD("bas11.rom",    0x2000, 0x2000, CRC(6270955a) SHA1(cecb7c24ff1e0ab5836e4a7a8eb1b8e01f1fded3), ROM_BIOS(5))
	ROMX_LOAD("extbas10.rom", 0x0000, 0x2000, CRC(6111a086) SHA1(8aa58f2eb3e8bcfd5470e3e35e2b359e9a72848e), ROM_BIOS(5))
	ROM_SYSTEM_BIOS(6, "b11e11", "Color BASIC v1.1 / Extended Color BASIC v1.1")
	ROMX_LOAD("bas11.rom",    0x2000, 0x2000, CRC(6270955a) SHA1(cecb7c24ff1e0ab5836e4a7a8eb1b8e01f1fded3), ROM_BIOS(6))
	ROMX_LOAD("extbas11.rom", 0x0000, 0x2000, CRC(a82a6254) SHA1(ad927fb4f30746d820cb8b860ebb585e7f095dea), ROM_BIOS(6))
	ROM_SYSTEM_BIOS(7, "b12e10", "Color BASIC v1.2 / Extended Color BASIC v1.0")
	ROMX_LOAD("bas12.rom",    0x2000, 0x2000, CRC(54368805) SHA1(0f14dc46c647510eb0b7bd3f53e33da07907d04f), ROM_BIOS(7))
	ROMX_LOAD("extbas10.rom", 0x0000, 0x2000, CRC(6111a086) SHA1(8aa58f2eb3e8bcfd5470e3e35e2b359e9a72848e), ROM_BIOS(7))
	ROM_SYSTEM_BIOS(8, "b12e11", "Color BASIC v1.2 / Extended Color BASIC v1.1")
	ROMX_LOAD("bas12.rom",    0x2000, 0x2000, CRC(54368805) SHA1(0f14dc46c647510eb0b7bd3f53e33da07907d04f), ROM_BIOS(8))
	ROMX_LOAD("extbas11.rom", 0x0000, 0x2000, CRC(a82a6254) SHA1(ad927fb4f30746d820cb8b860ebb585e7f095dea), ROM_BIOS(8))
ROM_END

ROM_START(deluxecoco)
	ROM_REGION(0x8000,MAINCPU_TAG,0)
	ROM_LOAD("adv070_u24.rom", 0x0000, 0x2000, CRC(827fe698) SHA1(70052321688bbc9583b3e017957cc2085bb8d0ae))
	ROM_LOAD("adv071_u24.rom", 0x2000, 0x2000, CRC(0a3942e4) SHA1(2f3d67efd80c36533d0220324c254fbeea364aaa))
	ROM_LOAD("adv072_u24.rom", 0x4000, 0x2000, CRC(c0118da5) SHA1(feea48b6b7070f0ac0acb132ad85087c5ad79e3b))
	ROM_LOAD("adv073-2_u24.rom", 0x6000, 0x2000, CRC(61411227) SHA1(c3aba0eb359f7f40d40d7947f72c9ecae6e0525d))
ROM_END

ROM_START(coco2b)
	ROM_REGION(0x8000,MAINCPU_TAG,0)
	ROM_LOAD("bas13.rom",    0x2000, 0x2000, CRC(d8f4d15e) SHA1(28b92bebe35fa4f026a084416d6ea3b1552b63d3))
	ROM_LOAD("extbas11.rom", 0x0000, 0x2000, CRC(a82a6254) SHA1(ad927fb4f30746d820cb8b860ebb585e7f095dea))
ROM_END

ROM_START(cp400)
	ROM_REGION(0x8000,MAINCPU_TAG,0)
	ROM_LOAD("cp400bas.rom", 0x0000, 0x4000, CRC(878396a5) SHA1(292c545da3c77978e043b00a3dbc317201d18c3b))
ROM_END

ROM_START(cp400c2)
	ROM_REGION(0x8000,MAINCPU_TAG,0)
	ROM_LOAD("cp400bas.rom", 0x0000, 0x4000, CRC(878396a5) SHA1(292c545da3c77978e043b00a3dbc317201d18c3b))
ROM_END

ROM_START(mx1600)
	ROM_REGION(0x8000,MAINCPU_TAG,0)
	ROM_LOAD("mx1600bas.rom",    0x2000, 0x2000, CRC(d918156e) SHA1(70a464edf3a654ed4ffe687e6dee4f0d2acc758b))
	ROM_LOAD("mx1600extbas.rom", 0x0000, 0x2000, CRC(322a3d58) SHA1(9079a477c3f22e46cebb1e68b61df5bd607c71a4))
ROM_END

ROM_START(t4426)
	ROM_REGION(0x8000,MAINCPU_TAG,0)
	ROM_LOAD("soft4426-u13-1.2.bin", 0x2000, 0x2000, CRC(3c1af94a) SHA1(1dc57b3e4a6ef6a743ca21d8f111a74b1ea9d54e))
	ROM_LOAD("soft4426-u14-1.2.bin", 0x0000, 0x2000, CRC(e031d076) SHA1(7275f1e3f165ff6a4657e4e5e24cb8b817239f54))
ROM_END

ROM_START(lzcolor64)
	ROM_REGION(0x8000,MAINCPU_TAG,0)
	ROM_LOAD("color_basic.ci24", 0x2000, 0x2000, CRC(b0717d71) SHA1(ad1beef9d6f095ada69f91d0b8ad75985172d86f))
	ROM_LOAD("extendido.ci23",   0x0000, 0x2000, CRC(d1b1560d) SHA1(7252de9df405ade453282e992eb1f1910adc8e50))
ROM_END

ROM_START(cd6809)
	ROM_REGION(0x8000,MAINCPU_TAG,0)
	ROM_DEFAULT_BIOS("84")

	ROM_SYSTEM_BIOS( 0, "83", "1983" )
	ROMX_LOAD("cd6809bas83.rom",    0x2000, 0x2000, CRC(f8e64142) SHA1(c0fd689119e2619ec226a2d67aeeb32070c14e38), ROM_BIOS(0))
	ROMX_LOAD("cd6809extbas83.rom", 0x0000, 0x2000, CRC(e5d5aa15) SHA1(0cd4a3d9e4af1d0176964e35e3d15a9fa0e68ac4), ROM_BIOS(0))

	ROM_SYSTEM_BIOS( 1, "84", "1984" )
	ROMX_LOAD("cd6809bas84.rom",    0x2000, 0x2000, CRC(8a9971da) SHA1(5cb5f1ffc983a85ba92af68b1d571b270f6db559), ROM_BIOS(1))
	ROMX_LOAD("cd6809extbas84.rom", 0x0000, 0x2000, CRC(8dc853e2) SHA1(d572ce4497c115af53d2b0feeb52d3c7a7fec175), ROM_BIOS(1))
ROM_END

ROM_START(ms1600)
	ROM_REGION(0x8000,MAINCPU_TAG,0)
	ROM_LOAD("ms1600.rom", 0x0000, 0x8000, CRC(66f0fafc) SHA1(6e709b8b44aa34a3235a889eb1c3615c0235c3d0))
ROM_END

#define rom_cocoh rom_coco
#define rom_coco2bh rom_coco2b

//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR   NAME        PARENT  COMPAT  MACHINE     INPUT       CLASS             INIT        COMPANY                         FULLNAME                               FLAGS
COMP( 1980,  coco,       0,      0,      coco,       coco,       coco12_state,     empty_init, "Tandy Radio Shack",            "Color Computer 1/2",                  MACHINE_SUPPORTS_SAVE )
COMP( 19??,  cocoh,      coco,   0,      cocoh,      coco,       coco12_state,     empty_init, "Tandy Radio Shack",            "Color Computer 1/2 (HD6309)",         MACHINE_SUPPORTS_SAVE | MACHINE_UNOFFICIAL )
COMP( 1983,  deluxecoco, coco,   0,      deluxecoco, deluxecoco, deluxecoco_state, empty_init, "Tandy Radio Shack",            "Deluxe Color Computer",               MACHINE_SUPPORTS_SAVE )
COMP( 1985?, coco2b,     coco,   0,      coco2b,     coco,       coco12_state,     empty_init, "Tandy Radio Shack",            "Color Computer 2B",                   MACHINE_SUPPORTS_SAVE )
COMP( 19??,  coco2bh,    coco,   0,      coco2bh,    coco,       coco12_state,     empty_init, "Tandy Radio Shack",            "Color Computer 2B (HD6309)",          MACHINE_SUPPORTS_SAVE | MACHINE_UNOFFICIAL )
COMP( 1983,  cp400,      coco,   0,      cp400,      coco,       coco12_state,     empty_init, "Prológica",                    "CP400",                               MACHINE_SUPPORTS_SAVE )
COMP( 1985,  cp400c2,    coco,   0,      cp400,      cp400c2,    coco12_state,     empty_init, "Prológica",                    "CP400 Color II",                      MACHINE_SUPPORTS_SAVE )
COMP( 1984,  mx1600,     coco,   0,      coco,       coco,       coco12_state,     empty_init, "Dynacom",                      "MX-1600",                             MACHINE_SUPPORTS_SAVE )
COMP( 1986,  t4426,      coco,   0,      t4426,      coco,       coco12_state,     empty_init, "Terco AB",                     "Terco 4426 CNC Programming station",  MACHINE_SUPPORTS_SAVE )
COMP( 1983,  lzcolor64,  coco,   0,      coco,       coco,       coco12_state,     empty_init, "Novo Tempo / LZ Equipamentos", "Color64",                             MACHINE_SUPPORTS_SAVE )
COMP( 1983,  cd6809,     coco,   0,      cd6809,     coco,       coco12_state,     empty_init, "Codimex",                      "CD-6809",                             MACHINE_SUPPORTS_SAVE )
COMP( 1987,  ms1600,     coco,   0,      ms1600,     coco,       coco12_state,     empty_init, "ILCE / SEP",                   "Micro-SEP 1600",                      MACHINE_SUPPORTS_SAVE )



coco3.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nathan Woods
/***************************************************************************

    coco3.c

    TRS-80 Radio Shack Color Computer 3

    Mathis Rosenhauer (original driver)
    Nate Woods (current maintainer)
    Tim Lindner (VHD and other work)

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

#include "emu.h"

#include "coco3.h"

#include "cpu/m6809/m6809.h"
#include "cpu/m6809/hd6309.h"

#include "softlist_dev.h"

#include "formats/coco_cas.h"



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

//-------------------------------------------------
//  ADDRESS_MAP( coco3_mem )
//-------------------------------------------------

void coco3_state::coco3_mem(address_map &map)
{
	map(0x0000, 0x1FFF).bankr("rbank0").bankw("wbank0");
	map(0x2000, 0x3FFF).bankr("rbank1").bankw("wbank1");
	map(0x4000, 0x5FFF).bankr("rbank2").bankw("wbank2");
	map(0x6000, 0x7FFF).bankr("rbank3").bankw("wbank3");
	map(0x8000, 0x9FFF).bankr("rbank4").bankw("wbank4");
	map(0xA000, 0xBFFF).bankr("rbank5").bankw("wbank5");
	map(0xC000, 0xDFFF).bankr("rbank6").bankw("wbank6");
	map(0xE000, 0xFDFF).bankr("rbank7").bankw("wbank7");
	map(0xFE00, 0xFEFF).bankr("rbank8").bankw("wbank8");
	map(0xFF00, 0xFF0F).rw(m_pia_0, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xFF20, 0xFF2F).r(m_pia_1, FUNC(pia6821_device::read)).w(FUNC(coco3_state::ff20_write));
	map(0xFF40, 0xFF5F).rw(FUNC(coco3_state::ff40_read), FUNC(coco3_state::ff40_write));
	map(0xFF60, 0xFF8F).rw(FUNC(coco3_state::ff60_read), FUNC(coco3_state::ff60_write));
	map(0xFF90, 0xFFDF).rw(m_gime, FUNC(gime_device::read), FUNC(gime_device::write));

	// While Tepolt and other sources say that the interrupt vectors are mapped to
	// the same memory accessed at $BFFx, William Astle offered evidence that this
	// memory on a CoCo 3 is not the same.
	//
	// http://lost.l-w.ca/0x05/coco3-and-interrupt-vectors/
	map(0xFFE0, 0xFFFF).rom().region(MAINCPU_TAG, 0x7FE0);
}



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

//-------------------------------------------------
//  INPUT_PORTS( coco3_keyboard )
//-------------------------------------------------
//
//  CoCo 3 keyboard
//
//         PB0 PB1 PB2 PB3 PB4 PB5 PB6 PB7
//    PA6: Ent Clr Brk Alt Ctr F1  F2 Shift
//    PA5: 8   9   :   ;   ,   -   .   /
//    PA4: 0   1   2   3   4   5   6   7
//    PA3: X   Y   Z   Up  Dwn Lft Rgt Space
//    PA2: P   Q   R   S   T   U   V   W
//    PA1: H   I   J   K   L   M   N   O
//    PA0: @   A   B   C   D   E   F   G
//-------------------------------------------------

static INPUT_PORTS_START( coco3_keyboard )
	PORT_START("row0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')

	PORT_START("row1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("row2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')

	PORT_START("row3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_NAME("UP") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_NAME("DOWN") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN), 10)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_NAME("LEFT") PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(UCHAR_MAMEKEY(LEFT), 8)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_NAME("RIGHT") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT), 9)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("row4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!') PORT_CHAR('|')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#') PORT_CHAR('~')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'') PORT_CHAR('^')

	PORT_START("row5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(') PORT_CHAR('[')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')') PORT_CHAR(']')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_MINUS) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<') PORT_CHAR('{')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('-') PORT_CHAR('=') PORT_CHAR('_')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>') PORT_CHAR('}')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?') PORT_CHAR('\\')

	PORT_START("row6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_NAME("CLEAR") PORT_CODE(KEYCODE_HOME) PORT_CHAR(12)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_NAME("BREAK") PORT_CODE(KEYCODE_END) PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_NAME("ALT") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL), UCHAR_SHIFT_2)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
INPUT_PORTS_END



//-------------------------------------------------
//  INPUT_PORTS( coco3_joystick )
//-------------------------------------------------

static INPUT_PORTS_START( coco3_joystick )
	PORT_START(JOYSTICK_RX_TAG)
	PORT_BIT( 0x3ff, 0x200,  IPT_AD_STICK_X) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0, 1023) PORT_CODE_DEC(KEYCODE_4_PAD) PORT_CODE_INC(KEYCODE_6_PAD) PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH) PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH) PORT_PLAYER(1) PORT_CONDITION(CTRL_SEL_TAG, 0x0f, EQUALS, 0x01)
	PORT_START(JOYSTICK_RY_TAG)
	PORT_BIT( 0x3ff, 0x200,  IPT_AD_STICK_Y) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0, 1023) PORT_CODE_DEC(KEYCODE_8_PAD) PORT_CODE_INC(KEYCODE_2_PAD) PORT_CODE_DEC(JOYCODE_Y_UP_SWITCH)   PORT_CODE_INC(JOYCODE_Y_DOWN_SWITCH)  PORT_PLAYER(1) PORT_CONDITION(CTRL_SEL_TAG, 0x0f, EQUALS, 0x01)
	PORT_START(JOYSTICK_LX_TAG)
	PORT_BIT( 0x3ff, 0x200,  IPT_AD_STICK_X) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0, 1023) PORT_CODE_DEC(KEYCODE_4_PAD) PORT_CODE_INC(KEYCODE_6_PAD) PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH) PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH) PORT_PLAYER(2) PORT_CONDITION(CTRL_SEL_TAG, 0xf0, EQUALS, 0x10)
	PORT_START(JOYSTICK_LY_TAG)
	PORT_BIT( 0x3ff, 0x200,  IPT_AD_STICK_Y) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0, 1023) PORT_CODE_DEC(KEYCODE_8_PAD) PORT_CODE_INC(KEYCODE_2_PAD) PORT_CODE_DEC(JOYCODE_Y_UP_SWITCH)   PORT_CODE_INC(JOYCODE_Y_DOWN_SWITCH)  PORT_PLAYER(2) PORT_CONDITION(CTRL_SEL_TAG, 0xf0, EQUALS, 0x10)
	PORT_START(JOYSTICK_BUTTONS_TAG)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Right Button 1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(JOYCODE_BUTTON1) PORT_CODE(MOUSECODE_BUTTON1) PORT_PLAYER(1) PORT_CONDITION(CTRL_SEL_TAG, 0x0f, EQUALS, 0x01)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Right Button 2") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_DEL_PAD) PORT_CODE(JOYCODE_BUTTON2) PORT_CODE(MOUSECODE_BUTTON2) PORT_PLAYER(1) PORT_CONDITION(CTRL_SEL_TAG, 0x0f, EQUALS, 0x01)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Left Button 1")  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(JOYCODE_BUTTON1) PORT_CODE(MOUSECODE_BUTTON1) PORT_PLAYER(2) PORT_CONDITION(CTRL_SEL_TAG, 0xf0, EQUALS, 0x10)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Left Button 2")  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_DEL_PAD) PORT_CODE(JOYCODE_BUTTON2) PORT_CODE(MOUSECODE_BUTTON2) PORT_PLAYER(2) PORT_CONDITION(CTRL_SEL_TAG, 0xf0, EQUALS, 0x10)
INPUT_PORTS_END



//-------------------------------------------------
//  INPUT_PORTS( coco_rat_mouse )
//-------------------------------------------------

static INPUT_PORTS_START( coco_rat_mouse )
	PORT_START(RAT_MOUSE_RX_TAG)
	PORT_BIT( 0x03, 0x00,  IPT_MOUSE_X) PORT_NAME("Rat Mouse X (Right Port)") PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_CODE_DEC(KEYCODE_LEFT) PORT_CODE_INC(KEYCODE_RIGHT) PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH) PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH) PORT_PLAYER(1)  PORT_CONDITION(CTRL_SEL_TAG, 0x0f, EQUALS, 0x02)
	PORT_START(RAT_MOUSE_RY_TAG)
	PORT_BIT( 0x03, 0x00,  IPT_MOUSE_Y) PORT_NAME("Rat Mouse Y (Right Port)") PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_CODE_DEC(KEYCODE_UP) PORT_CODE_INC(KEYCODE_DOWN) PORT_CODE_DEC(JOYCODE_Y_UP_SWITCH) PORT_CODE_INC(JOYCODE_Y_DOWN_SWITCH) PORT_PLAYER(1) PORT_CONDITION(CTRL_SEL_TAG, 0x0f, EQUALS, 0x02)
	PORT_START(RAT_MOUSE_LX_TAG)
	PORT_BIT( 0x03, 0x00,  IPT_MOUSE_X) PORT_NAME("Rat Mouse X (Left Port)") PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_CODE_DEC(KEYCODE_LEFT) PORT_CODE_INC(KEYCODE_RIGHT) PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH) PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH) PORT_PLAYER(2)  PORT_CONDITION(CTRL_SEL_TAG, 0xf0, EQUALS, 0x20)
	PORT_START(RAT_MOUSE_LY_TAG)
	PORT_BIT( 0x03, 0x00,  IPT_MOUSE_Y) PORT_NAME("Rat Mouse Y (Left Port)") PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_CODE_DEC(KEYCODE_UP) PORT_CODE_INC(KEYCODE_DOWN) PORT_CODE_DEC(JOYCODE_Y_UP_SWITCH) PORT_CODE_INC(JOYCODE_Y_DOWN_SWITCH) PORT_PLAYER(2) PORT_CONDITION(CTRL_SEL_TAG, 0xf0, EQUALS, 0x20)
	PORT_START(RAT_MOUSE_BUTTONS_TAG)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Rat Mouse Button 1 (Right Port)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(JOYCODE_BUTTON1) PORT_CODE(MOUSECODE_BUTTON1) PORT_PLAYER(1) PORT_CONDITION(CTRL_SEL_TAG, 0x0f, EQUALS, 0x02)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Rat Mouse Button 2 (Right Port)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_DEL_PAD) PORT_CODE(JOYCODE_BUTTON2) PORT_CODE(MOUSECODE_BUTTON2) PORT_PLAYER(1) PORT_CONDITION(CTRL_SEL_TAG, 0x0f, EQUALS, 0x02)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Rat Mouse Button 1 (Left Port)")  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(JOYCODE_BUTTON1) PORT_CODE(MOUSECODE_BUTTON1) PORT_PLAYER(2) PORT_CONDITION(CTRL_SEL_TAG, 0xf0, EQUALS, 0x20)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Rat Mouse Button 2 (Left Port)")  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_DEL_PAD) PORT_CODE(JOYCODE_BUTTON2) PORT_CODE(MOUSECODE_BUTTON2) PORT_PLAYER(2) PORT_CONDITION(CTRL_SEL_TAG, 0xf0, EQUALS, 0x20)
INPUT_PORTS_END



//-------------------------------------------------
//  INPUT_PORTS( coco_lightgun )
//-------------------------------------------------

static INPUT_PORTS_START( coco_lightgun )
	PORT_START(DIECOM_LIGHTGUN_RX_TAG)
	PORT_BIT( 0xfff, 266, IPT_LIGHTGUN_X ) PORT_NAME("Lightgun X (Right Port)") PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_MINMAX(105,420) PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_PLAYER(1) PORT_CONDITION(CTRL_SEL_TAG, 0x0f, EQUALS, 0x03)
	PORT_START(DIECOM_LIGHTGUN_RY_TAG)
	PORT_BIT( 0xff, 121, IPT_LIGHTGUN_Y ) PORT_NAME("Lightgun Y (Right Port)") PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_MINMAX(0,242) PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_PLAYER(1) PORT_CONDITION(CTRL_SEL_TAG, 0x0f, EQUALS, 0x03)
	PORT_START(DIECOM_LIGHTGUN_LX_TAG)
	PORT_BIT( 0xfff, 266, IPT_LIGHTGUN_X ) PORT_NAME("Lightgun X (Left Port)") PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_MINMAX(105,420) PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_PLAYER(1) PORT_CONDITION(CTRL_SEL_TAG, 0xf0, EQUALS, 0x30)
	PORT_START(DIECOM_LIGHTGUN_LY_TAG)
	PORT_BIT( 0xff, 121, IPT_LIGHTGUN_Y ) PORT_NAME("Lightgun Y (Left Port)") PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_MINMAX(0,242) PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_PLAYER(1) PORT_CONDITION(CTRL_SEL_TAG, 0xf0, EQUALS, 0x30)
	PORT_START(DIECOM_LIGHTGUN_BUTTONS_TAG)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Lightgun Trigger (Right Port)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(JOYCODE_BUTTON1) PORT_CODE(MOUSECODE_BUTTON1) PORT_PLAYER(1) PORT_CONDITION(CTRL_SEL_TAG, 0x0f, EQUALS, 0x03)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Lightgun Trigger (Left Port)")  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(coco3_state::coco_state::keyboard_changed), 0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(JOYCODE_BUTTON1) PORT_CODE(MOUSECODE_BUTTON1) PORT_PLAYER(2) PORT_CONDITION(CTRL_SEL_TAG, 0xf0, EQUALS, 0x30)
INPUT_PORTS_END



//-------------------------------------------------
//  INPUT_PORTS( coco3 )
//-------------------------------------------------

static INPUT_PORTS_START( screen_config )
	PORT_START("screen_config")
	PORT_CONFNAME( 0x01, 0x00, "Monitor Type" )
	PORT_CONFSETTING(    0x00, "Composite" )
	PORT_CONFSETTING(    0x01, "RGB" )
INPUT_PORTS_END

static INPUT_PORTS_START( coco3 )
	PORT_INCLUDE( coco3_keyboard )
	PORT_INCLUDE( coco3_joystick )
	PORT_INCLUDE( coco_analog_control )
	PORT_INCLUDE( coco_rat_mouse )
	PORT_INCLUDE( coco_lightgun )
	PORT_INCLUDE( coco_beckerport )
	PORT_INCLUDE( screen_config )
INPUT_PORTS_END

static DEVICE_INPUT_DEFAULTS_START( rs_printer )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END


//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

void coco3_state::coco3(machine_config &config)
{
	this->set_clock(XTAL(28'636'363) / 32);

	// basic machine hardware
	MC6809E(config, m_maincpu, DERIVED_CLOCK(1, 1));
	m_maincpu->set_addrmap(AS_PROGRAM, &coco3_state::coco3_mem);
	m_maincpu->set_dasm_override(FUNC(coco_state::dasm_override));

	// devices
	INPUT_MERGER_ANY_HIGH(config, m_irqs).output_handler().set_inputline(m_maincpu, M6809_IRQ_LINE);
	INPUT_MERGER_ANY_HIGH(config, m_firqs).output_handler().set_inputline(m_maincpu, M6809_FIRQ_LINE);

	// memory and video hardware
	GIME_NTSC(config, m_gime, XTAL(28'636'363), MAINCPU_TAG, RAM_TAG, m_cococart, MAINCPU_TAG);
	m_gime->set_screen("screen");
	m_gime->hsync_wr_callback().set(m_pia_0, FUNC(pia6821_device::ca1_w));
	m_gime->fsync_wr_callback().set(m_pia_0, FUNC(pia6821_device::cb1_w));
	m_gime->irq_wr_callback().set(m_irqs, FUNC(input_merger_device::in_w<2>));
	m_gime->firq_wr_callback().set(m_firqs, FUNC(input_merger_device::in_w<2>));
	m_gime->floating_bus_rd_callback().set(FUNC(coco3_state::floating_bus_r));

	PIA6821(config, m_pia_0);
	m_pia_0->writepa_handler().set(FUNC(coco_state::pia0_pa_w));
	m_pia_0->writepb_handler().set(FUNC(coco_state::pia0_pb_w));
	m_pia_0->tspb_handler().set_constant(0xff);
	m_pia_0->ca2_handler().set(FUNC(coco_state::pia0_ca2_w));
	m_pia_0->cb2_handler().set(FUNC(coco_state::pia0_cb2_w));
	m_pia_0->irqa_handler().set(m_irqs, FUNC(input_merger_device::in_w<0>));
	m_pia_0->irqb_handler().set(m_irqs, FUNC(input_merger_device::in_w<1>));

	PIA6821(config, m_pia_1);
	m_pia_1->readpa_handler().set(FUNC(coco_state::pia1_pa_r));
	m_pia_1->readpb_handler().set(FUNC(coco_state::pia1_pb_r));
	m_pia_1->writepa_handler().set(FUNC(coco_state::pia1_pa_w));
	m_pia_1->writepb_handler().set(FUNC(coco_state::pia1_pb_w));
	m_pia_1->ca2_handler().set(FUNC(coco_state::pia1_ca2_w));
	m_pia_1->cb2_handler().set(FUNC(coco_state::pia1_cb2_w));
	m_pia_1->irqa_handler().set(m_firqs, FUNC(input_merger_device::in_w<0>));
	m_pia_1->irqb_handler().set(m_firqs, FUNC(input_merger_device::in_w<1>));

	// Becker Port device
	COCO_DWSOCK(config, m_beckerport, 0);

	// sound hardware
	coco_sound(config);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(coco_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);

	rs232_port_device &rs232(RS232_PORT(config, RS232_TAG, default_rs232_devices, "rs_printer"));
	rs232.dcd_handler().set(m_pia_1, FUNC(pia6821_device::ca1_w));
	rs232.set_option_device_input_defaults("rs_printer", DEVICE_INPUT_DEFAULTS_NAME(rs_printer));

	COCO_VHD(config, m_vhd_0, 0, m_maincpu);
	COCO_VHD(config, m_vhd_1, 0, m_maincpu);

	// monitor
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(28.636363_MHz_XTAL/2, 912, 0, 640, 262, 1, 240);
	m_screen->set_screen_update(FUNC(coco3_state::screen_update));

	// internal ram
	RAM(config, RAM_TAG).set_default_size("512K").set_extra_options("128K,2M,8M");

	// floating space
	coco_floating(config);

	// cartridge
	COCOCART_SLOT(config, m_cococart, DERIVED_CLOCK(1, 1), coco_cart, "fdc");
	m_cococart->cart_callback().set([this] (int state) { cart_w(state != 0); }); // lambda because name is overloaded
	m_cococart->nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_cococart->halt_callback().set_inputline(m_maincpu, INPUT_LINE_HALT);

	// software lists
	SOFTWARE_LIST(config, "cart_list").set_original("coco_cart").set_filter("COCO3");

	SOFTWARE_LIST(config, "flop_list").set_original("coco_flop").set_filter("COCO3");
}

void coco3_state::coco3p(machine_config &config)
{
	coco3(config);
	this->set_clock(XTAL(28'475'000) / 32);

	// An additional 4.433618 MHz XTAL is required for PAL color encoding
	GIME_PAL(config.replace(), m_gime, XTAL(28'475'000), MAINCPU_TAG, RAM_TAG, m_cococart, MAINCPU_TAG);
	m_gime->set_screen("screen");
	m_gime->hsync_wr_callback().set(m_pia_0, FUNC(pia6821_device::ca1_w));
	m_gime->fsync_wr_callback().set(m_pia_0, FUNC(pia6821_device::cb1_w));
	m_gime->irq_wr_callback().set(m_irqs, FUNC(input_merger_device::in_w<2>));
	m_gime->firq_wr_callback().set(m_firqs, FUNC(input_merger_device::in_w<2>));
	m_gime->floating_bus_rd_callback().set(FUNC(coco3_state::floating_bus_r));
}

void coco3_state::coco3h(machine_config &config)
{
	coco3(config);
	HD6309E(config.replace(), m_maincpu, DERIVED_CLOCK(1, 1));
	m_maincpu->set_addrmap(AS_PROGRAM, &coco3_state::coco3_mem);
}

//**************************************************************************
//  ROMS
//**************************************************************************

ROM_START(coco3)
	ROM_REGION(0x8000,MAINCPU_TAG,0)
	ROM_LOAD("coco3.rom",   0x0000, 0x8000, CRC(b4c88d6c) SHA1(e0d82953fb6fd03768604933df1ce8bc51fc427d))
ROM_END

ROM_START(coco3p)
	ROM_REGION(0x8000,MAINCPU_TAG,0)
	ROM_LOAD("coco3p.rom",  0x0000, 0x8000, CRC(ff050d80) SHA1(631e383068b1f52a8f419f4114b69501b21cf379))
ROM_END

ROM_START(msm3)
	ROM_REGION(0x8000,MAINCPU_TAG,0)
	ROM_LOAD("msm3.rom", 0x0000, 0x8000, CRC(26d67890) SHA1(271fd39b3eb3f521aa5484ae7ce3956ab9ef782c))
ROM_END

#define rom_coco3h  rom_coco3

//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME      PARENT COMPAT MACHINE   INPUT    CLASS        INIT        COMPANY              FULLNAME                            FLAGS
COMP( 1986, coco3,    coco,  0,     coco3,    coco3,   coco3_state, empty_init, "Tandy Radio Shack", "Color Computer 3 (NTSC)",          0 )
COMP( 1986, coco3p,   coco,  0,     coco3p,   coco3,   coco3_state, empty_init, "Tandy Radio Shack", "Color Computer 3 (PAL)",           0 )
COMP( 19??, coco3h,   coco,  0,     coco3h,   coco3,   coco3_state, empty_init, "Tandy Radio Shack", "Color Computer 3 (NTSC; HD6309)",  MACHINE_UNOFFICIAL )
COMP( 1987, msm3,     coco,  0,     coco3,    coco3,   coco3_state, empty_init, "ILCE / SEP",        "Micro-Sep Model 3",                0 )



codata.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

        Contel Codata Corporation Codata

        2010-01-11 Skeleton driver.
        2013-08-26 Connected to a terminal.

        Chips: uPD7201C, AM9513, SCN68000. Crystal: 16 MHz

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

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "bus/rs232/rs232.h"
#include "machine/am9513.h"
#include "machine/z80sio.h"


namespace {

class codata_state : public driver_device
{
public:
	codata_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_ram(*this, "mainram")
		, m_maincpu(*this, "maincpu")
	{ }

	void codata(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	required_shared_ptr<u16> m_ram;
	required_device<cpu_device> m_maincpu;
};

void codata_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x1fffff).ram().share("mainram");
	map(0x200000, 0x203fff).rom().region("bios", 0);
	map(0x400000, 0x403fff).rom().region("bios", 0x4000);
	map(0x600000, 0x600007).mirror(0x1ffff8).rw("uart", FUNC(upd7201_device::ba_cd_r), FUNC(upd7201_device::ba_cd_w)).umask16(0xff00);
	map(0x800000, 0x800003).mirror(0x1ffffc).rw("timer", FUNC(am9513_device::read16), FUNC(am9513_device::write16));
	map(0xe00000, 0xe00001).mirror(0x1ffffe).portr("INPUT");
	//map(0xa00000, 0xbfffff) page map (rw)
	//map(0xc00000, 0xdfffff) segment map (rw), context register (r)
	//map(0xe00000, 0xffffff) context register (w), 16-bit parallel input port (r)
}

/* Input ports */
static INPUT_PORTS_START( codata )
	PORT_START("INPUT")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END


void codata_state::machine_reset()
{
	uint8_t* bios = memregion("bios")->base();
	memcpy(m_ram, bios, 16);
}

void codata_state::codata(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, 16_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &codata_state::mem_map);

	upd7201_device &uart(UPD7201(config, "uart", 16_MHz_XTAL / 4));
	uart.out_txda_callback().set("rs423a", FUNC(rs232_port_device::write_txd));
	uart.out_dtra_callback().set("rs423a", FUNC(rs232_port_device::write_dtr));
	uart.out_rtsa_callback().set("rs423a", FUNC(rs232_port_device::write_rts));
	uart.out_txdb_callback().set("rs423b", FUNC(rs232_port_device::write_txd));
	uart.out_int_callback().set_inputline(m_maincpu, M68K_IRQ_5);

	am9513_device &timer(AM9513A(config, "timer", 16_MHz_XTAL / 4));
	timer.out1_cb().set_nop(); // Timer 1 = "Abort/Reset" (watchdog)
	timer.out2_cb().set_inputline(m_maincpu, M68K_IRQ_6); // Timer 2
	timer.out3_cb().set_inputline(m_maincpu, M68K_IRQ_7); // Refresh
	timer.out4_cb().set("uart", FUNC(upd7201_device::rxca_w));
	timer.out4_cb().append("uart", FUNC(upd7201_device::txca_w));
	timer.out5_cb().set("uart", FUNC(upd7201_device::rxcb_w));
	timer.out5_cb().append("uart", FUNC(upd7201_device::txcb_w));

	rs232_port_device &rs423a(RS232_PORT(config, "rs423a", default_rs232_devices, "terminal"));
	rs423a.rxd_handler().set("uart", FUNC(upd7201_device::rxa_w));
	rs423a.dsr_handler().set("uart", FUNC(upd7201_device::dcda_w));
	rs423a.cts_handler().set("uart", FUNC(upd7201_device::ctsa_w));

	rs232_port_device &rs423b(RS232_PORT(config, "rs423b", default_rs232_devices, nullptr));
	rs423b.rxd_handler().set("uart", FUNC(upd7201_device::rxb_w));
}

/* ROM definition */
ROM_START( codata )
	ROM_REGION16_BE( 0x8000, "bios", 0 )
	ROM_LOAD16_BYTE( "27-0042-01a boot 00 u101 rev 3.6.2 9-28-83.u101", 0x0001, 0x2000, CRC(70014b16) SHA1(19a82000894d79817358d40ae520200e976be310))
	ROM_LOAD16_BYTE( "27-0043-01a boot 01 u102 rev 3.6.2 9-28-83.u102", 0x4001, 0x2000, CRC(fca9c314) SHA1(2f8970fad479000f28536003867066d6df9e33d9))
	ROM_LOAD16_BYTE( "27-0044-01a boot e0 u103 rev 3.6.2 9-28-83.u103", 0x0000, 0x2000, CRC(dc5d5cea) SHA1(b3e9248abf89d674c463d21d2f7be34508cf16c2))
	ROM_LOAD16_BYTE( "27-0045-01a boot e1 u104 rev 3.6.2 9-28-83.u104", 0x4000, 0x2000, CRC(a937e7b3) SHA1(d809bbd437fe7d925325958072b9e0dc33dd36a6))

	ROM_REGION( 0x240, "proms", 0 )
	ROM_LOAD( "p0.u502", 0x0000, 0x0020, CRC(20eb1183) SHA1(9b268792b28d858d6b6a1b6c4148af88a8d6b735) )
	ROM_LOAD( "p1.u602", 0x0020, 0x0020, CRC(ee1e5a14) SHA1(0d3346cb3b647fa2475bd7b4fa36ea6ecfdaf805) )
	ROM_LOAD( "p2.u503", 0x0040, 0x0200, CRC(12d9a6be) SHA1(fca99f9c5afc630ac67cbd4e5ba4e5242b826848) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY                      FULLNAME  FLAGS
COMP( 1982, codata, 0,      0,      codata,  codata, codata_state, empty_init, "Contel Codata Corporation", "Codata", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



coleco.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Mike Balfour, Ben Bruscella, Sean Young, Frank Palazzolo
/*******************************************************************************************************

  coleco.c

  Driver file to handle emulation of the ColecoVision.

  Marat Fayzullin (ColEm source)
  Marcel de Kogel (AdamEm source)
  Mike Balfour
  Ben Bruscella
  Sean Young

  NEWS:
    - Modified memory map, now it has only 1k of RAM mapped on 8k Slot
    - Modified I/O map, now it is handled as on a real ColecoVision:
        The I/O map is broken into 4 write and 4 read ports:
            80-9F (W) = Set both controllers to keypad mode
            80-9F (R) = Not Connected

            A0-BF (W) = Video Chip (TMS9928A), A0=0 -> Write Register 0 , A0=1 -> Write Register 1
            A0-BF (R) = Video Chip (TMS9928A), A0=0 -> Read Register 0 , A0=1 -> Read Register 1

            C0-DF (W) = Set both controllers to joystick mode
            C0-DF (R) = Not Connected

            E0-FF (W) = Sound Chip (SN76489A)
            E0-FF (R) = Read Controller data, A1=0 -> read controller 1, A1=1 -> read controller 2

    - Modified paddle handler, now it is handled as on a real ColecoVision
    - Added support for a Roller Controller (Trackball), enabled via category
    - Added support for two Super Action Controller, enabled via category

    EXTRA CONTROLLERS INFO:

    -Driving Controller (Expansion Module #2). It consist of a steering wheel and a gas pedal. Only one
    can be used on a real ColecoVision. The gas pedal is not analog, internally it is just a switch.
    On a real ColecoVision, when the Driving Controller is enabled, the controller 1 do not work because
    have been replaced by the Driving Controller, and controller 2 have to be used to start game, gear
    shift, etc.
    Driving Controller is just a spinner on controller 1 socket similar to the one on Roller Controller
    and Super Action Controllers so you can use Roller Controller or Super Action Controllers to play
    games requiring Driving Controller.

    -Roller Controller. Basically a trackball with four buttons (the two fire buttons from player 1 and
    the two fire buttons from player 2). Only one Roller Controller can be used on a real ColecoVision.
    Roller Controller is connected to both controller sockets and both controllers are conected to the Roller
    Controller, it uses the spinner pins of both sockets to generate the X and Y signals (X from controller 1
    and the Y from controller 2)

    -Super Action Controllers. It is a hand controller with a keypad, four buttons (the two from
    the player pad and two more), and a spinner. This was made primarily for two player sport games, but
    will work for every other ColecoVision game.

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

/*

    TODO:

    - Dina SG-1000 mode

    - Bit90:
        Add support for external memory handling (documented)
        Add support for printer interface (documented)
        Add tape support

*/

#include "emu.h"

#include "coleco.h"

#include "bus/coleco/expansion/expansion.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "logmacro.h"

/* Read/Write Handlers */

uint8_t coleco_state::paddle_1_r()
{
	return m_joy_d7_state[0] | coleco_paddle_read(0, m_joy_mode, m_joy_analog_state[0]);
}

uint8_t coleco_state::paddle_2_r()
{
	// Tape notes:
	//     Signal is averaged to set the threshold voltage for a comparator
	//     Output of the comparator goes to bit 7
	return m_joy_d7_state[1] | coleco_paddle_read(1, m_joy_mode, m_joy_analog_state[1]);
}

void coleco_state::paddle_off_w(uint8_t data)
{
	m_joy_mode = 0;
}

void coleco_state::paddle_on_w(uint8_t data)
{
	m_joy_mode = 1;
}

void bit90_state::init()
{
	auto pgm = &m_maincpu->space(AS_PROGRAM);

	if(m_ram->size() == 32768)
		return;
	else if(m_ram->size() == 16384)
		pgm->unmap_readwrite(0xc000, 0xffff);
	else if(m_ram->size() == 1024)
		pgm->unmap_readwrite(0x8000, 0xffff);
}

uint8_t bit90_state::bankswitch_u4_r(address_space &space)
{
	if (!machine().side_effects_disabled()) {
		LOG("Bankswitch to u4\n");
		m_bank->set_entry(0);
	}
	return space.unmap();
}

uint8_t bit90_state::bankswitch_u3_r(address_space &space)
{
	if (!machine().side_effects_disabled()) {
		LOG("Bankswitch to u3\n");
		m_bank->set_entry(1);
	}
	return space.unmap();
}

uint8_t bit90_state::keyboard_r(address_space &space)
{
	if (m_keyselect < 8) {
		return m_io_keyboard[m_keyselect]->read();
	}
	return space.unmap();
}

void bit90_state::u32_w(uint8_t data)
{
	// Write to a 74LS174 at u32
	// Bits 4-7 are connected for keyboard scanning (actually only 4-6 are used)
	// Bits 0-1 Tape Write
	//     Notes: Tape waveform is generated using bits 0 and 1 connected as 2-bit DAC, with four output levels
	//            The analog output is put through an RC filter to approximate a sine wave
	//            Tape waveforms appear to be full cycles of one of 2 frequencies
	m_keyselect = (data >> 4) & 0x0f;
	uint8_t temp = data & 0x03;
	if (m_unknown != temp) {
		m_unknown = temp;
		LOG("m_unknown -> 0x%02x\n",m_unknown);
	}
}

/* Memory Maps */

void coleco_state::coleco_map(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x6000, 0x63ff).ram().mirror(0x1c00);
	map(0x8000, 0xffff).rw(FUNC(coleco_state::cart_r), FUNC(coleco_state::cart_w));
}

void bit90_state::bit90_map(address_map &map)
{
	map(0x0000, 0x1fff).bankr("bank");
	map(0x2000, 0x3fff).rom();
	map(0x4000, 0x5fff).rom();  // Decoded through pin 5 of the Bit90 expansion port
	map(0x6000, 0x67ff).ram().mirror(0x1800);
	map(0x8000, 0xffff).rw(FUNC(coleco_state::cart_r), FUNC(coleco_state::cart_w));
}

void coleco_state::coleco_io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high(); // comicbak relies on this
	map(0x80, 0x80).mirror(0x1f).w(FUNC(coleco_state::paddle_off_w));
	map(0xa0, 0xa1).mirror(0x1e).rw("tms9928a", FUNC(tms9928a_device::read), FUNC(tms9928a_device::write));
	map(0xc0, 0xc0).mirror(0x1f).w(FUNC(coleco_state::paddle_on_w));
	map(0xe0, 0xe0).mirror(0x1f).w("sn76489a", FUNC(sn76489a_device::write));
	map(0xe0, 0xe0).mirror(0x1d).r(FUNC(coleco_state::paddle_1_r));
	map(0xe2, 0xe2).mirror(0x1d).r(FUNC(coleco_state::paddle_2_r));
}

void bit90_state::bit90_io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x80, 0x80).mirror(0x17).r(FUNC(bit90_state::bankswitch_u4_r));
	map(0x88, 0x88).mirror(0x17).r(FUNC(bit90_state::bankswitch_u3_r));
	map(0x80, 0x80).mirror(0x1f).w(FUNC(coleco_state::paddle_off_w));
	map(0xa0, 0xa1).mirror(0x1e).rw("tms9928a", FUNC(tms9928a_device::read), FUNC(tms9928a_device::write));
	map(0xc0, 0xc0).mirror(0x1f).r(FUNC(bit90_state::keyboard_r));
	map(0xc0, 0xc0).mirror(0x1f).w(FUNC(coleco_state::paddle_on_w));
	map(0xe0, 0xe0).mirror(0x1d).r(FUNC(coleco_state::paddle_1_r));
	map(0xe0, 0xe0).mirror(0x1b).w(FUNC(bit90_state::u32_w));        // bits7-4 for keyscan, (to bcd decoder) and bits1-0 tape out
	map(0xe2, 0xe2).mirror(0x1d).r(FUNC(coleco_state::paddle_2_r));  // also, bit7 is tape in
	map(0xe4, 0xe4).mirror(0x1b).w("sn76489a", FUNC(sn76489a_device::write));

	// IORQ goes to pin 55 of the Bit90 expansion port,
	// So ports < 0x80 can be decoded there

	// External Printer Interface
	//map(0x48, 0x48).w(FUNC(bit90_state::printer_data_w));  // data to latch here
	//map(0x4a, 0x4a).r(FUNC(bit90_state::printer_control_r));  // bit0 is busy bit
	//map(0x4a, 0x4a).w(FUNC(bit90_state::printer_control_w));  // bit0 is reset (active low), bit1 is latch (active low)

	// External/(Internal?) RAM Interface
	//map(0x4e, 0x4f).w(FUNC(bit90_state::external_ram_control_w)); // 0x4e enable, 0x4f disable
	// RAM can appear here, starting at 0x8000 up to 0xffff
}

void coleco_state::czz50_map(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	map(0x6000, 0x63ff).ram().mirror(0x1c00);
	map(0x8000, 0xffff).rom();
}

/* Input Ports */

static INPUT_PORTS_START( czz50 )
	PORT_START("STD_KEYPAD1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("0") PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0')
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("1") PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1')
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("2") PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2')
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("3") PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3')
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("4") PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4')
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("5") PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5')
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("6") PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6')
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("7") PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7')
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("8") PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8')
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("9") PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9')
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("#") PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR('#')
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("*") PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR('*')
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0xb000, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("STD_JOY1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0xb0, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("STD_KEYPAD2")
	PORT_BIT( 0x00ff, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0f00, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0xb000, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("STD_JOY2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0xb0, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

static INPUT_PORTS_START( bit90 )
	PORT_INCLUDE( coleco )

	PORT_START("ROW0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("INS") PORT_CODE(KEYCODE_INSERT)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)

	PORT_START("ROW1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_TILDE)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BS") PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)

	PORT_START("ROW2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("UP") PORT_CODE(KEYCODE_UP)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)

	PORT_START("ROW3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RUBOUT") PORT_CODE(KEYCODE_HOME)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RIGHT") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)

	PORT_START("ROW4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_END)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CAPS") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)

	PORT_START("ROW5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)

	PORT_START("ROW6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)

	PORT_START("ROW7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("META") PORT_CODE(KEYCODE_RALT)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BASIC") PORT_CODE(KEYCODE_LALT)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LEFT") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DOWN") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("FCTN") PORT_CODE(KEYCODE_RCONTROL)
INPUT_PORTS_END

/* Interrupts */

void coleco_state::coleco_vdp_interrupt(int state)
{
	// NMI on rising edge
	if (state && !m_last_nmi_state)
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);

	m_last_nmi_state = state;
}

TIMER_CALLBACK_MEMBER(coleco_state::paddle_d7reset_callback)
{
	m_joy_d7_state[param] = 0;
	m_joy_analog_state[param] = 0;
}

TIMER_CALLBACK_MEMBER(coleco_state::paddle_irqreset_callback)
{
	m_joy_irq_state[param] = 0;

	if (!m_joy_irq_state[param ^ 1])
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
}

TIMER_CALLBACK_MEMBER(coleco_state::paddle_pulse_callback)
{
	if (m_joy_analog_reload[param])
	{
		m_joy_analog_state[param] = m_joy_analog_reload[param];

		// on movement, controller port d7 is set for a short period and an irq is fired on d7 rising edge
		m_joy_d7_state[param] = 0x80;
		m_joy_d7_timer[param]->adjust(attotime::from_usec(500), param); // TODO: measure duration

		// irq on rising edge, PULSE_LINE is not supported in this case, so clear it manually
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
		m_joy_irq_timer[param]->adjust(attotime::from_usec(11), param); // TODO: measure duration
		m_joy_irq_state[param] = 1;

		// reload timer
		m_joy_pulse_timer[param]->adjust(m_joy_pulse_reload[param], param);
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(coleco_state::paddle_update_callback)
{
	// arbitrary timer for reading analog controls
	coleco_scan_paddles(&m_joy_analog_reload[0], &m_joy_analog_reload[1]);

	for (int port = 0; port < 2; port++)
	{
		if (m_joy_analog_reload[port])
		{
			const int sensitivity = 500;
			int ipt = m_joy_analog_reload[port];
			if (ipt & 0x80) ipt = 0x100 - ipt;
			attotime freq = attotime::from_msec(sensitivity / ipt);

			// change pulse intervals relative to spinner/trackball speed
			m_joy_pulse_reload[port] = freq;
			m_joy_pulse_timer[port]->adjust(std::min(freq, m_joy_pulse_timer[port]->remaining()), port);
		}
	}
}

uint8_t coleco_state::cart_r(offs_t offset)
{
	return m_cart->read(offset, 0, 0, 0, 0);
}

void coleco_state::cart_w(offs_t offset, uint8_t data)
{
	m_cart->write(offset, data, 0, 0, 0, 0);
}

uint8_t coleco_state::coleco_scan_paddles(uint8_t *joy_status0, uint8_t *joy_status1)
{
	uint8_t ctrl_sel = m_ctrlsel.read_safe(0);

	/* which controller shall we read? */
	if ((ctrl_sel & 0x07) == 0x02)          // Super Action Controller P1
		*joy_status0 = m_sac_slide1.read_safe(0);
	else if ((ctrl_sel & 0x07) == 0x03)     // Driving Controller P1
		*joy_status0 = m_driv_wheel1.read_safe(0);

	if ((ctrl_sel & 0x70) == 0x20)          // Super Action Controller P2
		*joy_status1 = m_sac_slide2.read_safe(0);
	else if ((ctrl_sel & 0x70) == 0x30)     // Driving Controller P2
		*joy_status1 = m_driv_wheel2.read_safe(0);

	/* In principle, even if not supported by any game, I guess we could have two Super
	   Action Controllers plugged into the Roller controller ports. Since I found no info
	   about the behavior of sliders in such a configuration, we overwrite SAC sliders with
	   the Roller trackball inputs and actually use the latter ones, when both are selected. */
	if (ctrl_sel & 0x80)                    // Roller controller
	{
		*joy_status0 = m_roller_x.read_safe(0);
		*joy_status1 = m_roller_y.read_safe(0);
	}

	return *joy_status0 | *joy_status1;
}


uint8_t coleco_state::coleco_paddle_read(int port, int joy_mode, uint8_t joy_status)
{
	uint8_t ctrl_sel = m_ctrlsel.read_safe(0);
	uint8_t ctrl_extra = ctrl_sel & 0x80;
	ctrl_sel = ctrl_sel >> (port*4) & 7;

	/* Keypad and fire 1 (SAC Yellow Button) */
	if (joy_mode == 0)
	{
		/* No key pressed by default */
		uint8_t data = 0x0f;
		uint16_t ipt = 0xffff;

		if (ctrl_sel == 0)          // ColecoVision Controller
			ipt = port ? m_std_keypad2->read() : m_std_keypad1->read();
		else if (ctrl_sel == 2)     // Super Action Controller
			ipt = port ? m_sac_keypad2->read() : m_sac_keypad1->read();

		/* Numeric pad buttons are not independent on a real ColecoVision, if you push more
		   than one, a real ColecoVision think that it is a third button, so we are going to emulate
		   the right behaviour */
		/* Super Action Controller additional buttons are read in the same way */
		if (!(ipt & 0x0001)) data &= 0x0a; /* 0 */
		if (!(ipt & 0x0002)) data &= 0x0d; /* 1 */
		if (!(ipt & 0x0004)) data &= 0x07; /* 2 */
		if (!(ipt & 0x0008)) data &= 0x0c; /* 3 */
		if (!(ipt & 0x0010)) data &= 0x02; /* 4 */
		if (!(ipt & 0x0020)) data &= 0x03; /* 5 */
		if (!(ipt & 0x0040)) data &= 0x0e; /* 6 */
		if (!(ipt & 0x0080)) data &= 0x05; /* 7 */
		if (!(ipt & 0x0100)) data &= 0x01; /* 8 */
		if (!(ipt & 0x0200)) data &= 0x0b; /* 9 */
		if (!(ipt & 0x0400)) data &= 0x06; /* # */
		if (!(ipt & 0x0800)) data &= 0x09; /* * */
		if (!(ipt & 0x1000)) data &= 0x04; /* Blue Action Button */
		if (!(ipt & 0x2000)) data &= 0x08; /* Purple Action Button */

		return ((ipt & 0x4000) >> 8) | 0x30 | data;
	}
	/* Joystick and fire 2 (SAC Red Button) */
	else
	{
		uint8_t data = 0x7f;

		if (ctrl_sel == 0)          // ColecoVision Controller
			data = port ? m_std_joy2->read() : m_std_joy1->read();
		else if (ctrl_sel == 2)     // Super Action Controller
			data = port ? m_sac_joy2->read() : m_sac_joy1->read();
		else if (ctrl_sel == 3)     // Driving Controller
			data = port ? m_driv_pedal2->read() : m_driv_pedal1->read();

		/* If any extra analog contoller enabled */
		if (ctrl_extra || ctrl_sel == 2 || ctrl_sel == 3)
		{
			if (joy_status & 0x80) data ^= 0x30;
			else if (joy_status) data ^= 0x10;
		}

		return data & 0x7f;
	}
}

void coleco_state::machine_start()
{
	// init paddles
	for (int port = 0; port < 2; port++)
	{
		m_joy_pulse_timer[port] = timer_alloc(FUNC(coleco_state::paddle_pulse_callback), this);
		m_joy_d7_timer[port] = timer_alloc(FUNC(coleco_state::paddle_d7reset_callback), this);
		m_joy_irq_timer[port] = timer_alloc(FUNC(coleco_state::paddle_irqreset_callback), this);

		m_joy_irq_state[port] = 0;
		m_joy_d7_state[port] = 0;
		m_joy_analog_state[port] = 0;
	}

	save_item(NAME(m_joy_mode));
	save_item(NAME(m_last_nmi_state));
	save_item(NAME(m_joy_irq_state));
	save_item(NAME(m_joy_d7_state));
	save_item(NAME(m_joy_analog_state));
	save_item(NAME(m_joy_analog_reload));
}

void bit90_state::machine_start()
{
	coleco_state::machine_start();
	uint8_t *banked = memregion("banked")->base();
	m_bank->configure_entries(0, 0x02, banked, 0x2000);
}

void coleco_state::machine_reset()
{
	m_last_nmi_state = 0;
}

void bit90_state::machine_reset()
{
	coleco_state::machine_reset();
	m_bank->set_entry(0);
}

//static std::error_condition coleco_cart_verify(const uint8_t *cartdata, size_t size)
//{
//  std::error_condition retval = image_error::INVALIDIMAGE;
//
//  /* Verify the file is in Colecovision format */
//  if ((cartdata[0] == 0xAA) && (cartdata[1] == 0x55)) /* Production Cartridge */
//      retval = std::error_condition();
//  if ((cartdata[0] == 0x55) && (cartdata[1] == 0xAA)) /* "Test" Cartridge. Some games use this method to skip ColecoVision title screen and delay */
//      retval = std::error_condition();
//
//  return retval;
//}


/* Machine Drivers */

void coleco_state::coleco(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(7'159'090)/2); // 3.579545 MHz
	m_maincpu->z80_set_m1_cycles(4+1); // 1 WAIT CLK per M1
	m_maincpu->set_addrmap(AS_PROGRAM, &coleco_state::coleco_map);
	m_maincpu->set_addrmap(AS_IO, &coleco_state::coleco_io_map);

	/* video hardware */
	tms9928a_device &vdp(TMS9928A(config, "tms9928a", XTAL(10'738'635)));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	vdp.int_callback().set(FUNC(coleco_state::coleco_vdp_interrupt));
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	sn76489a_device &psg(SN76489A(config, "sn76489a", XTAL(7'159'090)/2)); // 3.579545 MHz
	psg.add_route(ALL_OUTPUTS, "mono", 1.00);
	psg.ready_cb().set_inputline("maincpu", Z80_INPUT_LINE_WAIT).invert();

	/* cartridge */
	COLECOVISION_CARTRIDGE_SLOT(config, m_cart, colecovision_cartridges, nullptr);

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("coleco");
	SOFTWARE_LIST(config, "homebrew_list").set_original("coleco_homebrew");

	TIMER(config, "paddle_timer").configure_periodic(FUNC(coleco_state::paddle_update_callback), attotime::from_msec(20));

	coleco_expansion_device &exp(COLECO_EXPANSION(config, "exp", nullptr));
	exp.set_program_space(m_maincpu, AS_PROGRAM);
	exp.set_io_space(m_maincpu, AS_IO);
	exp.int_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0); // TODO: Merge with other IRQs?
	exp.nmi_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);
	exp.add_route(ALL_OUTPUTS, "mono", 1.00);
}

void coleco_state::colecop(machine_config &config)
{
	coleco(config);

	/* video hardware */
	tms9929a_device &vdp(TMS9929A(config.replace(), "tms9928a", XTAL(10'738'635)));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	vdp.int_callback().set(FUNC(coleco_state::coleco_vdp_interrupt));
}

void bit90_state::bit90(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(7'159'090)/2); // 3.579545 MHz
	m_maincpu->z80_set_m1_cycles(4+1); // 1 WAIT CLK per M1
	m_maincpu->set_addrmap(AS_PROGRAM, &bit90_state::bit90_map);
	m_maincpu->set_addrmap(AS_IO, &bit90_state::bit90_io_map);

	/* video hardware */
	tms9929a_device &vdp(TMS9929A(config, "tms9928a", XTAL(10'738'635)));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	vdp.int_callback().set(FUNC(coleco_state::coleco_vdp_interrupt));
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	sn76489a_device &psg(SN76489A(config, "sn76489a", XTAL(7'159'090)/2)); // 3.579545 MHz
	psg.add_route(ALL_OUTPUTS, "mono", 1.00);
	psg.ready_cb().set_inputline("maincpu", Z80_INPUT_LINE_WAIT).invert();

	/* cartridge */
	COLECOVISION_CARTRIDGE_SLOT(config, m_cart, colecovision_cartridges, nullptr);

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("coleco");
	SOFTWARE_LIST(config, "homebrew_list").set_original("coleco_homebrew");

	/* internal ram */
	RAM(config, m_ram).set_default_size("32K").set_extra_options("1K,16K");

	TIMER(config, "paddle_timer").configure_periodic(FUNC(coleco_state::paddle_update_callback), attotime::from_msec(20));
}

void coleco_state::czz50(machine_config &config)
{
	coleco(config);

	config.device_remove("exp"); // this system has a different expansion port

	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &coleco_state::czz50_map); // note: cpu speed unverified, assume it's the same as ColecoVision
}

void coleco_state::dina(machine_config &config)
{
	czz50(config);

	/* video hardware */
	tms9929a_device &vdp(TMS9929A(config.replace(), "tms9928a", XTAL(10'738'635)));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	vdp.int_callback().set(FUNC(coleco_state::coleco_vdp_interrupt));
}


/* ROMs */

ROM_START (coleco)
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "313_10031-4005_73108a.u2", 0x0000, 0x2000, CRC(3aa93ef3) SHA1(45bedc4cbdeac66c7df59e9e599195c778d86a92) )
ROM_END

/*  ONYX (Prototype)
    Unreleased Brazilian Colecovision clone by Microdigital.

    It was never released and the only known prototypes were uncovered by an ex-employee of Microdigital
    called Cl??udio Cassens who donated it to collectors (Eduardo Luccas) in June 2015.
    -- Felipe Sanches
*/
ROM_START (onyx)
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "onyx.rom", 0x0000, 0x2000, CRC(011c32e7) SHA1(f44263221e330b2590dffc1a6f43ed2591fe19be) )
ROM_END

/*  PAL Colecovision BIOS

Country: Italy
Serial number: C0039036
Model number: 240020
Circuit board: (C) 1983 91162 rev D

Information about the chip

Motorola logo
(C)1983 COLECO
R72114A
8317   */

ROM_START (colecop)
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "r72114a_8317.u2", 0x0000, 0x2000, CRC(d393c0cc) SHA1(160077afb139943725c634d6539898db59f33657) )
ROM_END

ROM_START( czz50 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "czz50.rom", 0x0000, 0x2000, CRC(4999abc6) SHA1(96aecec3712c94517103d894405bc98a7dafa440) )
	ROM_CONTINUE( 0x8000, 0x2000 )
ROM_END

#define rom_dina rom_czz50
#define rom_prsarcde rom_czz50

/* Bit Corporation - BIT90

Circuit board is labelled: BIT90C-PAL-90002 or BIT90C-PAL-90003

BIT90C-PAL-90002 has 2K Internal RAM (<1K Usable from BASIC)
    Extra RAM can only be accessed via expansion port

BIT90C-PAL-90003 has sockets for additional internal 16K or 32K internal RAM

Units have 2764-compatible pinouts at U2,U3, and U4
Some units have 2764 EPROMS, Mask ROMs, or a combination

Mask ROMs are labelled:

@U4:
D32351E
BIT-99C1

@U3:
D32521E
MONITOR2

@U2:
D32522E
MONITOR3

*/

ROM_START( bit90 )
	ROM_DEFAULT_BIOS( "3.1" )
	ROM_SYSTEM_BIOS( 0, "3.0", "BASIC 3.0" )
	ROM_SYSTEM_BIOS( 1, "3.1", "BASIC 3.1" )

	ROM_REGION( 0x10000, "maincpu", 0 )
	ROMX_LOAD("bit90b3.u2",  0x2000, 0x2000, CRC(b992b940) SHA1(c7dd96a1944fac40cbae20630f303a69de7e6313), ROM_BIOS(0))
	ROMX_LOAD("d32522e.u2",  0x2000, 0x2000, CRC(66fc66b0) SHA1(6644c217860aa9940bef7c6aeb50768810d9035b), ROM_BIOS(1)) // MONITOR3

	ROM_REGION( 0x4000, "banked", 0 )
	ROMX_LOAD("bit90b3.u4", 0x0000, 0x2000, CRC(06d21fc2) SHA1(6d296b09b661babd4c2ef6993f8e768a67932388), ROM_BIOS(0))
	ROMX_LOAD("bit90b3.u3", 0x2000, 0x2000, CRC(61fdccbb) SHA1(25cac13627c0916d3ed2b92f0b2218b405de5be4), ROM_BIOS(0))
	ROMX_LOAD("d32351e.u4", 0x0000, 0x2000, CRC(d00c7137) SHA1(43328257136aff5a4984cceafdb5601200ac24b4), ROM_BIOS(1)) // BIT-99C1
	ROMX_LOAD("d32521e.u3", 0x2000, 0x2000, CRC(f6401dd8) SHA1(78bc7f0fe4f5eb114773d654c92598512481abec), ROM_BIOS(1)) // MONITOR2
ROM_END

/* System Drivers */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT   CLASS         INIT        COMPANY             FULLNAME                            FLAGS
CONS( 1982, coleco,   0,      0,      coleco,   coleco, coleco_state, empty_init, "Coleco",           "ColecoVision (NTSC)",              0 )
CONS( 1982, onyx,     coleco, 0,      coleco,   coleco, coleco_state, empty_init, "Microdigital",     "Onyx (Brazil/Prototype)",          0 )
CONS( 1983, colecop,  coleco, 0,      colecop,  coleco, coleco_state, empty_init, "Coleco",           "ColecoVision (PAL)",               0 )
CONS( 1986, czz50,    0,      coleco, czz50,    czz50,  coleco_state, empty_init, "Bit Corporation",  "Chuang Zao Zhe 50",                0 )
CONS( 1988, dina,     czz50,  0,      dina,     czz50,  coleco_state, empty_init, "Telegames",        "Dina",                             0 )
CONS( 1988, prsarcde, czz50,  0,      czz50,    czz50,  coleco_state, empty_init, "Telegames",        "Personal Arcade",                  0 )
COMP( 1983, bit90,    0,      coleco, bit90,    bit90,  bit90_state,  init,       "Bit Corporation",  "Bit90",                            0 )



companion.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

SciSys Chess Companion

The 1st one (this) has a 6504, the 'sequels' II and III have a 6301.
Not to be confused with Kasparov Chess Companion.

Hardware notes:
- PCB label: SciSys Y01 A
- Synertek 6504 @ ~1MHz
- Synertek SY6520/SY6820 PIA
- 4KB ROM (2332N, chip manufacturer unknown)
- 1KB RAM (2*HM472114P-4)
- beeper, button sensors chessboard, 16+12 leds

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

#include "emu.h"

#include "cpu/m6502/m6504.h"
#include "machine/6821pia.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "saitek_companion.lh"


namespace {

class compan_state : public driver_device
{
public:
	compan_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_pia(*this, "pia"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void compan(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<pia6821_device> m_pia;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<2> m_inputs;

	u8 m_inp_mux = 0;
	u8 m_led_data = 0;
	u8 m_led_direct = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_display();
	void control_w(u8 data);
	u8 input_r();
	void sled_w(int state);
	void cled_w(int state);
};

void compan_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_led_data));
	save_item(NAME(m_led_direct));
}



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

void compan_state::update_display()
{
	m_display->matrix((1 << m_inp_mux & 0x3ff) | (m_led_direct << 10), m_led_data);
}

void compan_state::sled_w(int state)
{
	// CA2: "sides swapped" led
	m_led_direct = (m_led_direct & ~2) | (state ? 2 : 0);
	update_display();
}

void compan_state::cled_w(int state)
{
	// CB2: "color" led
	m_led_direct = (m_led_direct & ~1) | (state ? 1 : 0);
	update_display();
}

void compan_state::control_w(u8 data)
{
	// PB0-PB3: input mux, led select
	m_inp_mux = data & 0xf;

	// PB4-PB6: led data
	m_led_data = ~data >> 4 & 7;
	update_display();

	// PB7: speaker out
	m_dac->write(BIT(data, 7));
}

u8 compan_state::input_r()
{
	u8 data = 0;

	// PA0-PA7: multiplexed inputs
	// read chessboard sensors
	if (m_inp_mux < 8)
		data = m_board->read_file(m_inp_mux);

	// read other buttons
	else if (m_inp_mux < 10)
		data = m_inputs[m_inp_mux - 8]->read();

	return ~data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void compan_state::main_map(address_map &map)
{
	map.global_mask(0x1fff);
	map(0x0000, 0x03ff).ram();
	map(0x0800, 0x0803).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x1000, 0x1fff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( compan )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Swap Sides")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Color")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Play")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("Clear Board")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Multi Move")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Change Level")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void compan_state::compan(machine_config &config)
{
	// basic machine hardware
	M6504(config, m_maincpu, 1'000'000); // approximation, no XTAL
	m_maincpu->set_addrmap(AS_PROGRAM, &compan_state::main_map);

	PIA6821(config, m_pia);
	m_pia->readpa_handler().set(FUNC(compan_state::input_r));
	m_pia->writepb_handler().set(FUNC(compan_state::control_w));
	m_pia->ca2_handler().set(FUNC(compan_state::sled_w));
	m_pia->cb2_handler().set(FUNC(compan_state::cled_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10+2, 3);
	config.set_default_layout(layout_saitek_companion);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( compan )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("2332n_yo1a", 0x1000, 0x1000, CRC(a715d51c) SHA1(3e1bd9dc119c914b502f1433ee2d6ce3f477b99a) ) // 2332
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1982, compan, 0,      0,      compan,  compan, compan_state, empty_init, "SciSys / Heuristic Software", "Chess Companion", MACHINE_SUPPORTS_SAVE )



companion2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

SciSys Chess Companion II family
CXG Enterprise "S" family

The chess engine is LogiChess (ported from Z80 to 6801), by Kaare Danielsen,
after founding the company LogiSoft ApS.

NOTE: It triggers an NMI when the power switch is changed from ON to MEMORY.
If this is not done, NVRAM won't save properly.

TODO:
- if/when MAME supports an exit callback, hook up power-off switch to that

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

SciSys Chess Companion II family hardware notes:

Chess Companion II:
- PCB label: YO1B-01 REV.B
- Hitachi HD6301V1P (0609V171) @ ~4MHz (LC oscillator)
- chessboard buttons, 16+5 leds, piezo

Explorer Chess:
- slightly different UI (12 buttons instead of 14, but same functionality)
- portable, peg board instead of button board
- rest is same as compan2

Concord / Concord II:
- PCB label: SCISYS ST3 REV.E
- MCU clock frequency is around twice higher than Companion II (measured ~7.32MHz
  on a Concord model 251, again no XTAL)
- rest is same as compan2, it just has the buttons/status leds at the bottom
  instead of at the right

Explorer Chess and Chess Companion II / Concord have the same MCU ROM, pin P23
is either VCC or GND to distinguish between the two.

0609V171 MCU is used in:
- SciSys Chess Companion II (2 revisions)
- SciSys Olympiade (French)
- SciSys Explorer Chess
- SciSys Concord
- SciSys Concord II
- SciSys Electronic Chess Mark 8
- Tandy 1650 Portable Sensory Chess (Tandy brand Explorer Chess)
- Tandy (Radio Shack) 1650 (Fast Response Time) Computerized Chess (Tandy brand Concord)

The Tandy clones run at a lower clock frequency, 3MHz and 6MHz respectively.

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

CXG Enterprise "S" / Star Chess is on very similar hardware, so it's emulated
in this driver too.

Hardware notes:
- Hitachi HD6301V1P (HD6301V1C42P), 7.15909MHz XTAL
- port 2 I/O is changed a bit, rest is same as compan2

HD6301V1C42P MCU is used in:
- CXG Enterprise "S" (model 208, black/brown/blue)
- CXG Star Chess (model 209, black/gray)
- CXG Computachess III (model 008)
- CXG Super Computachess (model 009)
- CXG Crown (model 228)
- CXG Sphinx Galaxy 2 (model 628, suspected)
- Fidelity Genesis (Fidelity brand Computachess III)
- Mephisto Merlin 4K (H+G brand Computachess III)
- Multitech Enterprise (Multitech brand Super Computachess)

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

#include "emu.h"

#include "cpu/m6800/m6801.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "cxg_enterprise.lh"
#include "saitek_companion2.lh"
#include "saitek_expchess.lh"


namespace {

class compan2_state : public driver_device
{
public:
	compan2_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void enterp(machine_config &config);
	void expchess(machine_config &config);
	void compan2(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(power_off);
	DECLARE_INPUT_CHANGED_MEMBER(change_cpu_freq);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<hd6301v1_cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_ioport_array<3> m_inputs;

	emu_timer *m_standbytimer;
	emu_timer *m_nmitimer;
	bool m_power = false;
	u8 m_inp_mux = 0;

	// I/O handlers
	u8 input1_r();
	u8 input2_r();
	u8 input2c_r();
	void mux_w(u8 data);
	u8 power_r();
	void led_w(u8 data);

	TIMER_CALLBACK_MEMBER(set_pin);
};

void compan2_state::machine_start()
{
	m_nmitimer = timer_alloc(FUNC(compan2_state::set_pin), this);
	m_standbytimer = timer_alloc(FUNC(compan2_state::set_pin), this);

	// register for savestates
	save_item(NAME(m_power));
	save_item(NAME(m_inp_mux));
}

INPUT_CHANGED_MEMBER(compan2_state::change_cpu_freq)
{
	// Concord MCU speed is around twice higher
	m_maincpu->set_unscaled_clock((newval & 1) ? 7'200'000 : 4'000'000);
}



/*******************************************************************************
    Power
*******************************************************************************/

void compan2_state::machine_reset()
{
	m_power = true;

	m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
	m_maincpu->set_input_line(M6801_STBY_LINE, CLEAR_LINE);
}

TIMER_CALLBACK_MEMBER(compan2_state::set_pin)
{
	m_maincpu->set_input_line(param, ASSERT_LINE);
}

INPUT_CHANGED_MEMBER(compan2_state::power_off)
{
	if (newval && m_power)
	{
		m_power = false;

		// when power switch is set to MEMORY, it triggers an NMI after a short delay
		attotime delay = attotime::from_msec(50);
		m_nmitimer->adjust(delay, INPUT_LINE_NMI);

		// afterwards, MCU STBY pin is asserted after a short delay
		delay += attotime::from_msec(10);
		m_standbytimer->adjust(delay, M6801_STBY_LINE);
	}
}



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

// common

u8 compan2_state::input1_r()
{
	u8 data = 0;

	// P10-P17: read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_file(i ^ 7);

	return ~data;
}

u8 compan2_state::input2_r()
{
	u8 data = 0;

	// P21,P22: read buttons
	for (int i = 0; i < 2; i++)
		if (m_inp_mux & m_inputs[i]->read())
			data |= 2 << i;

	// P23: button configuration
	data |= m_inputs[2]->read() << 3 & 8;
	return ~data;
}

void compan2_state::mux_w(u8 data)
{
	// P30-P37: input mux, led data
	m_inp_mux = ~data;
	m_display->write_mx(m_inp_mux);
}

u8 compan2_state::power_r()
{
	// P40: power switch state
	return m_power ? 0 : 1;
}

void compan2_state::led_w(u8 data)
{
	// P41-P45: direct leds
	// P46,P47: board leds
	m_display->write_my(~data >> 1 & 0x7f);
}


// enterp-specific

u8 compan2_state::input2c_r()
{
	// P20,P21: read buttons
	u8 data = input2_r() >> 1 & 3;

	// P24: power switch state
	return data | (power_r() << 4) | 0xec;
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( enterp )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Hint")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Enter Position")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Move")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Multi Move")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_C) PORT_NAME("Sound/Color")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("POWER") // needs to be triggered for nvram to work
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(compan2_state::power_off), 0)
INPUT_PORTS_END

static INPUT_PORTS_START( expchess )
	PORT_INCLUDE( enterp )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("White/Black")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game / Clear Board")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("2nd F")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")
	PORT_BIT(0x63, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_MODIFY("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level / Sound")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("Play / PVP")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")

	PORT_MODIFY("IN.2") // button config
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_CUSTOM)
INPUT_PORTS_END

static INPUT_PORTS_START( compan2 )
	PORT_INCLUDE( enterp )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Sound")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Color")

	PORT_MODIFY("IN.1")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("Play")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Enter Position")

	PORT_MODIFY("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_CUSTOM)

	PORT_START("CPU")
	PORT_CONFNAME( 0x01, 0x00, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(compan2_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "4MHz (original)" )
	PORT_CONFSETTING(    0x01, "7.2MHz (Concord)" )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void compan2_state::enterp(machine_config &config)
{
	// basic machine hardware
	HD6301V1(config, m_maincpu, 7.15909_MHz_XTAL);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(hd6301v1_cpu_device::nvram_set_battery));
	m_maincpu->standby_cb().append([this](int state) { if (state) m_display->clear(); });
	m_maincpu->in_p1_cb().set(FUNC(compan2_state::input1_r));
	m_maincpu->in_p2_cb().set(FUNC(compan2_state::input2c_r));
	m_maincpu->out_p2_cb().set("dac", FUNC(dac_1bit_device::write)).bit(2);
	m_maincpu->out_p3_cb().set(FUNC(compan2_state::mux_w));
	m_maincpu->out_p4_cb().set(FUNC(compan2_state::led_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(5+2, 8);
	config.set_default_layout(layout_cxg_enterprise);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, "dac").add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void compan2_state::expchess(machine_config &config)
{
	enterp(config);

	// basic machine hardware
	m_maincpu->set_clock(4'000'000); // approximation, no XTAL
	m_maincpu->in_p2_cb().set(FUNC(compan2_state::input2_r));
	m_maincpu->out_p2_cb().set("dac", FUNC(dac_1bit_device::write)).bit(0);
	m_maincpu->in_p4_cb().set(FUNC(compan2_state::power_r));

	config.set_default_layout(layout_saitek_expchess);
}

void compan2_state::compan2(machine_config &config)
{
	expchess(config);
	config.set_default_layout(layout_saitek_companion2);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( compan2 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("1983_te-1_scisys-w_0609v171.u1", 0x0000, 0x1000, CRC(a26632fd) SHA1(fb83dc2476500acaabd949d749e58adca01012ea) )
ROM_END

ROM_START( expchess )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("1983_te-1_scisys-w_0609v171.u1", 0x0000, 0x1000, CRC(a26632fd) SHA1(fb83dc2476500acaabd949d749e58adca01012ea) )
ROM_END

ROM_START( enterp )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("1984nc208_newcrest_hd6301v1c42p", 0x0000, 0x1000, CRC(b9cc7da7) SHA1(ca07ba072cc101aabeb0853f518053f48cc73a4d) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT     CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1983, compan2,  0,       0,      compan2,  compan2,  compan2_state, empty_init, "SciSys / LogiSoft", "Chess Companion II", MACHINE_SUPPORTS_SAVE )
SYST( 1983, expchess, compan2, 0,      expchess, expchess, compan2_state, empty_init, "SciSys / LogiSoft", "Explorer Chess", MACHINE_SUPPORTS_SAVE )

SYST( 1984, enterp,   0,       0,      enterp,   enterp,   compan2_state, empty_init, "CXG Systems / Newcrest Technology / LogiSoft", "Enterprise \"S\"", MACHINE_SUPPORTS_SAVE )



compc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Carl
// Commodore PC 10 / PC 20 / PC 30
/***************************************************************************
Commodore PC10 / PC20 / PC30
Links: http://www.zimmers.net/cbmpics/cpcs.html , https://de.wikipedia.org/wiki/Commodore_PC-10_bis_PC-60 , http://mingos-commodorepage.tumblr.com/post/123656301482/commodore-pc-20-beim-pc-20-handelt-es-sich-um
http://www.richardlagendijk.nl/cip/computer/item/pc20ii/de
Form Factor: Desktop
CPU: 8088 @ 4.77 MHz
RAM: 256K / 512K / 640K
BUS: 5x ISA
Video: MDA
Mass storage: PC10: 1 or 2x 5.25" 360K , PC20: 1x 360K + 10MB HD, PC30: 1x 360K + 20MB HD
On board ports: Floppy, serial, parallel, speaker
Options: 8087 FPU


Commodore PC-10 III
=============
Links: http://dostalgie.de/downloads/pc10III-20III/PC10III_OM_COMMODORE_EN_DE.pdf ; ftp://ftp.zimmers.net/pub/cbm-pc/documents/PC-8088-Information.txt
Info: PC10-III and PC20-III are the same machines - PC10 has two floppies, PC20 one floppy and one harddisk
Form Factor: Desktop
CPU: 8088 @ 4.77 MHz / 7.16 MHz / 9.54 MHz
RAM: 640K
Bus: 3x ISA
Video: On board: MDA/CGA/Hercules/Plantronics
Mass storage: 1x Floppy 5.25" 360K and (PC10) another 360K or (PC20) 3.5" harddisk
On board ports: Floppy, XTA(8-bit IDE) Harddisk, Mouse, serial, parallel, RTC, Speaker
Options: 8087 FPU
***************************************************************************/

#include "emu.h"

#include "cpu/i86/i86.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "machine/genpc.h"
#include "machine/nvram.h"
#include "softlist_dev.h"


namespace {

class compc_state : public driver_device
{
public:
	compc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_mb(*this, "mb"),
		m_dsw0(*this, "DSW0")
	{ }

	required_device<cpu_device> m_maincpu;
	required_device<pc_noppi_mb_device> m_mb;
	required_ioport m_dsw0;

	void machine_reset() override ATTR_COLD;

	u8 pioiii_portc_r();
	void pioiii_portc_w(u8 data);
	u8 pio_portc_r();

	void compc(machine_config &config);
	void pc10iii(machine_config &config);
	void compc1(machine_config &config);
	void compc_io(address_map &map) ATTR_COLD;
	void compc_map(address_map &map) ATTR_COLD;
	void compciii_io(address_map &map) ATTR_COLD;
private:
	u8 m_dips = 0;
};

void compc_state::machine_reset()
{
	m_dips = 0;
}


u8 compc_state::pio_portc_r()
{
	u8 data;
	if(BIT(m_mb->pc_ppi_portb_r(), 3))
	{
		/* read hi nibble of S2 */
		data = (m_dsw0->read() >> 4) & 0x0f;
	}
	else
	{
		/* read lo nibble of S2 */
		data = m_dsw0->read() & 0x0f;
	}
	if(m_mb->pit_out2())
		data |= 0x20;
	return data;
}

void compc_state::pioiii_portc_w(u8 data)
{
	m_dips = (data & ~0x30) | m_dsw0->read();
}


u8 compc_state::pioiii_portc_r()
{
	u8 data;
	if(!BIT(m_mb->pc_ppi_portb_r(), 2))
		data = (m_dips >> 4) & 0x0f;
	else
		data = m_dips & 0x0f;
	if(m_mb->pit_out2())
		data |= 0x30;
	return data;
}

static INPUT_PORTS_START(compciii)
	PORT_START("DSW0") /* IN1 */
	PORT_DIPNAME( 0x30, 0x30, "Graphics adapter")
	PORT_DIPSETTING(    0x00, "EGA/VGA" )
	PORT_DIPSETTING(    0x10, "Color 40x25" )
	PORT_DIPSETTING(    0x20, "Color 80x25" )
	PORT_DIPSETTING(    0x30, "Monochrome" )
INPUT_PORTS_END

static INPUT_PORTS_START(compc)
	PORT_START("DSW0") /* IN1 */
	PORT_DIPNAME( 0xc0, 0x40, "Number of floppy drives")
	PORT_DIPSETTING(    0x00, "1" )
	PORT_DIPSETTING(    0x40, "2" )
	PORT_DIPSETTING(    0x80, "3" )
	PORT_DIPSETTING(    0xc0, "4" )
	PORT_DIPNAME( 0x30, 0x30, "Graphics adapter")
	PORT_DIPSETTING(    0x00, "EGA/VGA" )
	PORT_DIPSETTING(    0x10, "Color 40x25" )
	PORT_DIPSETTING(    0x20, "Color 80x25" )
	PORT_DIPSETTING(    0x30, "Monochrome" )
	PORT_DIPNAME( 0x0c, 0x0c, "RAM banks")
	PORT_DIPSETTING(    0x00, "1 - 16/ 64/256K" )
	PORT_DIPSETTING(    0x04, "2 - 32/128/512K" )
	PORT_DIPSETTING(    0x08, "3 - 48/192/576K" )
	PORT_DIPSETTING(    0x0c, "4 - 64/256/640K" )
	PORT_DIPNAME( 0x02, 0x00, "8087 installed")
	PORT_DIPSETTING(    0x00, DEF_STR(No) )
	PORT_DIPSETTING(    0x02, DEF_STR(Yes) )
	PORT_DIPNAME( 0x01, 0x01, "Boot from floppy")
	PORT_DIPSETTING(    0x01, DEF_STR(Yes) )
	PORT_DIPSETTING(    0x00, DEF_STR(No) )
INPUT_PORTS_END

void compc_state::compc_map(address_map &map)
{
	map.unmap_value_high();
	map(0xf0000, 0xfffff).rom().region("bios", 0);
}

void compc_state::compc_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m(m_mb, FUNC(pc_noppi_mb_device::map));
	map(0x0062, 0x0062).r(FUNC(compc_state::pio_portc_r));
}

void compc_state::compciii_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m(m_mb, FUNC(pc_noppi_mb_device::map));
	map(0x0062, 0x0062).rw(FUNC(compc_state::pioiii_portc_r), FUNC(compc_state::pioiii_portc_w));
}

void compc_state::compc(machine_config &config)
{
	I8088(config, m_maincpu, 4772720*2);
	m_maincpu->set_addrmap(AS_PROGRAM, &compc_state::compc_map);
	m_maincpu->set_addrmap(AS_IO, &compc_state::compc_io);
	m_maincpu->set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	PCNOPPI_MOTHERBOARD(config, m_mb, 0).set_cputag(m_maincpu);
	m_mb->int_callback().set_inputline(m_maincpu, 0);
	m_mb->nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_mb->kbdclk_callback().set("keyboard", FUNC(pc_kbdc_device::clock_write_from_mb));
	m_mb->kbddata_callback().set("keyboard", FUNC(pc_kbdc_device::data_write_from_mb));
	config.device_remove("mb:pit8253");
	fe2010_pit_device &pit(FE2010_PIT(config, "mb:pit8253", 0));
	pit.set_clk<0>(XTAL(14'318'181)/12.0); /* heartbeat IRQ */
	pit.out_handler<0>().set("mb:pic8259", FUNC(pic8259_device::ir0_w));
	pit.set_clk<1>(XTAL(14'318'181)/12.0); /* dram refresh */
	pit.out_handler<1>().set(m_mb, FUNC(ibm5160_mb_device::pc_pit8253_out1_changed));
	pit.set_clk<2>(XTAL(14'318'181)/12.0); /* pio port c pin 4, and speaker polling enough */
	pit.out_handler<2>().set(m_mb, FUNC(ibm5160_mb_device::pc_pit8253_out2_changed));

	// FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa1", 0, "mb:isa", pc_isa8_cards, "mda", false);
	ISA8_SLOT(config, "isa2", 0, "mb:isa", pc_isa8_cards, "lpt", false);
	ISA8_SLOT(config, "isa3", 0, "mb:isa", pc_isa8_cards, "com", false);
	ISA8_SLOT(config, "isa4", 0, "mb:isa", pc_isa8_cards, "fdc_xt", false);

	pc_kbdc_device &pc_kbdc(PC_KBDC(config, "keyboard", pc_xt_keyboards, STR_KBD_IBM_PC_XT_83));
	pc_kbdc.out_clock_cb().set(m_mb, FUNC(pc_noppi_mb_device::keyboard_clock_w));
	pc_kbdc.out_data_cb().set(m_mb, FUNC(pc_noppi_mb_device::keyboard_data_w));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("256K").set_extra_options("512K, 640K");

	/* software lists */
	SOFTWARE_LIST(config, "disk_list").set_original("ibm5150");
}

void compc_state::pc10iii(machine_config &config)
{
	compc(config);
	m_maincpu->set_addrmap(AS_IO, &compc_state::compciii_io);
}

ROM_START(compc10)
	ROM_REGION(0x10000, "bios", 0)
	ROM_DEFAULT_BIOS("v205")
	ROM_SYSTEM_BIOS(0, "v201", "v2.01")
	ROMX_LOAD("bios2.01-380258-01.bin", 0xc000, 0x4000, CRC(921de6aa) SHA1(eb6c3fe4200cb40da20131b264521ba9f82021b2), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v203", "v2.03")
	ROMX_LOAD("380258-03", 0xc000, 0x4000, CRC(fbe53865) SHA1(a6d6433c055d1c328f71403a2ed2fd5908c23d40), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v205", "v2.05")
	ROMX_LOAD("380258-04", 0xc000, 0x4000, CRC(e61084da) SHA1(dfb360a6ec6cb1250d8a6243f12a0d702e8479cb), ROM_BIOS(2))
ROM_END

// Note: Commodore PC20-III, PC10-III and COLT share the same BIOS
ROM_START(pc10iii)
	ROM_DEFAULT_BIOS("v441")
	ROM_SYSTEM_BIOS(0, "v435", "v4.35")
	ROM_SYSTEM_BIOS(1, "v435c", "v4.35c")
	ROM_SYSTEM_BIOS(2, "v436", "v4.36")
	ROM_SYSTEM_BIOS(3, "v436b", "v4.36b")
	ROM_SYSTEM_BIOS(4, "v436c", "v4.36c")
	ROM_SYSTEM_BIOS(5, "v438", "v4.38")
	ROM_SYSTEM_BIOS(6, "v439", "v4.39")
	ROM_SYSTEM_BIOS(7, "v440", "v4.40")
	ROM_SYSTEM_BIOS(8, "v441", "v4.41")

	ROM_REGION(0x10000, "bios", 0)
	ROMX_LOAD("318085-01.u201", 0x8000, 0x8000, CRC(be752d1e) SHA1(5e5e63cd6d6269816cd691602e4c4d209fe3df67), ROM_BIOS(0))
	ROMX_LOAD("318085-01_pc-3_bios_4.35c.bin", 0x8000, 0x8000, CRC(adbc4ab7) SHA1(c157e5bc115906705849f185e4fe8e42ab28930c), ROM_BIOS(1))
	ROMX_LOAD("318085-02.u201", 0x8000, 0x8000, CRC(db0c9d04) SHA1(1314ce606840f4f78e58e5f78909e8971387f387), ROM_BIOS(2))
	ROMX_LOAD("318085-03.u201", 0x8000, 0x8000, CRC(559e6b76) SHA1(cdfd4781f3520db7f5111469ebb29c10b39ab587), ROM_BIOS(3))
	ROMX_LOAD("318085-04.u201", 0x8000, 0x8000, CRC(f81e67f0) SHA1(b46613cb5c6ac4beb769778bc35f81777ebe02e1), ROM_BIOS(4))
	ROMX_LOAD("318085-05.u201", 0x8000, 0x8000, CRC(ae9e6a31) SHA1(853ee251cf230818c407a8d13ef060a21c90a8c1), ROM_BIOS(5))
	ROMX_LOAD("318085-06.u201", 0x8000, 0x8000, CRC(1901993c) SHA1(f75060c1c442376bd42c61e74fa9eee053351791), ROM_BIOS(6))
	ROMX_LOAD("318085-07.u201", 0x8000, 0x8000, CRC(505d52b0) SHA1(f717c6ab791d51f35e1c38ffbc81a44075b5f2f8), ROM_BIOS(7))
	ROMX_LOAD("318085-08.u201", 0x8000, 0x8000, CRC(7e228dc8) SHA1(958dfdd637bd31c01b949fac729d6973a7e630bc), ROM_BIOS(8))

	ROM_REGION(0x8000, "gfx1", 0)
	ROMX_LOAD("318086-01_p10c_164a.bin", 0x0000, 0x4000, CRC(ee6c27f0) SHA1(e769cc3a49a1d708bd74eb4ac85bb6ea67220d38), ROM_BIOS(0)) // came with ROM 4.35c
	ROMX_LOAD("318086-01_p10c_164a.bin", 0x0000, 0x4000, CRC(ee6c27f0) SHA1(e769cc3a49a1d708bd74eb4ac85bb6ea67220d38), ROM_BIOS(1))
	ROMX_LOAD("318086-02.u607", 0x0000, 0x8000, CRC(b406651c) SHA1(856f58353391a74a06ebb8ec9f8333d7d69e5fd6), ROM_BIOS(2))
	ROMX_LOAD("318086-02.u607", 0x0000, 0x8000, CRC(b406651c) SHA1(856f58353391a74a06ebb8ec9f8333d7d69e5fd6), ROM_BIOS(3))
	ROMX_LOAD("318086-02.u607", 0x0000, 0x8000, CRC(b406651c) SHA1(856f58353391a74a06ebb8ec9f8333d7d69e5fd6), ROM_BIOS(4))
	ROMX_LOAD("318086-02.u607", 0x0000, 0x8000, CRC(b406651c) SHA1(856f58353391a74a06ebb8ec9f8333d7d69e5fd6), ROM_BIOS(5))
	ROMX_LOAD("318086-02.u607", 0x0000, 0x8000, CRC(b406651c) SHA1(856f58353391a74a06ebb8ec9f8333d7d69e5fd6), ROM_BIOS(6))
	ROMX_LOAD("318086-02.u607", 0x0000, 0x8000, CRC(b406651c) SHA1(856f58353391a74a06ebb8ec9f8333d7d69e5fd6), ROM_BIOS(7))
	ROMX_LOAD("318086-02.u607", 0x0000, 0x8000, CRC(b406651c) SHA1(856f58353391a74a06ebb8ec9f8333d7d69e5fd6), ROM_BIOS(8))
ROM_END


/*********************************************************** Commodore PC-1 ***

Links: http://www.amiga-stuff.com/hardware/pc-i.html , http://www.zimmers.net/cbmpics/cpci.html
Form Factor: Desktop
CPU: 8088 @ 4.77 MHz
RAM: 512K / 640K
Bus: Proprietary expansion slot, carrying almost all ISA signals
Video: On board, MDA/Hercules/CGA
Mass storage: 1x 5.25" 360K
On board ports: Floppy, floppy expansion (for Amiga A1010/1011 (720 KB, 3.5") or A1020 (360 KB, 5.25" drives), speaker (but no speaker fitted), mouse,
Options: 8087 FPU
Expansion: Expansion box: 2x ISA

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

ROM_START( compc1 )
	ROM_DEFAULT_BIOS("bios12")
	ROM_REGION(0x10000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "bios11", "PC-1 BIOS Rev. 1.1")
	ROMX_LOAD("pc1_bios.bin", 0xc000, 0x4000, CRC(e37367c8) SHA1(9aac9c38b4ebdb9a740e393199c2eff75a0bde03), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(1, "bios12", "PC-1 BIOS Rev. 1.2")
	ROMX_LOAD("cbm-pci-bios-v1.2-380270-02.bin", 0xc000, 0x4000, CRC(7f744f87) SHA1(07f94a7e8ca4ddd1c738b304d24358711b4cd2ca), ROM_BIOS(1))
	ROM_REGION(0x8000, "gfx1", 0)
	ROM_LOAD("pc1_char.bin", 0x0000, 0x4000, CRC(ee6c27f0) SHA1(e769cc3a49a1d708bd74eb4ac85bb6ea67220d38))
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT     CLASS        INIT        COMPANY                        FULLNAME               FLAGS
COMP( 1984, compc10, ibm5150, 0,      compc,   compc,    compc_state, empty_init, "Commodore Business Machines", "Commodore PC 10",     MACHINE_NOT_WORKING )
COMP( 1987, pc10iii, ibm5150, 0,      pc10iii, compciii, compc_state, empty_init, "Commodore Business Machines", "Commodore PC-10 III", MACHINE_NOT_WORKING )
COMP( 198?, compc1,  ibm5150, 0,      pc10iii, compciii, compc_state, empty_init, "Commodore Business Machines", "PC-1",                MACHINE_NOT_WORKING )



compis.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
// thanks-to:Per Ola Ingvarsson, Tomas Karlsson
/******************************************************************************

    drivers/compis.c
    machine driver

    Hardware:
        - Intel 80186 CPU 8MHz, integrated DMA(8237?), PIC(8259?), PIT(8253?)
                - Intel 80130 OSP Operating system processor (PIC 8259, PIT 8254)
        - Intel 8274 MPSC Multi-protocol serial communications controller (NEC 7201)
        - Intel 8255 PPI Programmable peripheral interface
        - Intel 8253 PIT Programmable interval timer
        - Intel 8251 USART Universal synchronous asynchronous receiver transmitter
        - National 58174 Real-time clock (compatible with 58274)
    Peripheral:
        - Intel 82720 GDC Graphic display processor (NEC uPD 7220)
        - Intel 8272 FDC Floppy disk controller (Intel iSBX-218A)
        - Western Digital WD1002-05 Winchester controller

    Memory map:

    00000-3FFFF RAM LMCS (Low Memory Chip Select)
    40000-4FFFF RAM MMCS 0 (Midrange Memory Chip Select)
    50000-5FFFF RAM MMCS 1 (Midrange Memory Chip Select)
    60000-6FFFF RAM MMCS 2 (Midrange Memory Chip Select)
    70000-7FFFF RAM MMCS 3 (Midrange Memory Chip Select)
    80000-EFFFF NOP
    F0000-FFFFF ROM UMCS (Upper Memory Chip Select)

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

/*

    TODO:

    - booting system2 with disk in drive b: fails
    - uhrg graphics are drawn wrong (upd7220 bugs?)
    - compis2
        - color graphics
        - 8087
        - programmable keyboard
    - hard disk

*/

#include "emu.h"
#include "bus/centronics/ctronics.h"
#include "bus/compis/graphics.h"
#include "bus/isbx/isbx.h"
#include "bus/rs232/rs232.h"
#include "cpu/i86/i186.h"
#include "cpu/mcs48/mcs48.h"
#include "imagedev/cassette.h"
#include "compiskb.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/i80130.h"
#include "machine/mm58174.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "machine/z80sio.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

#define I80186_TAG      "ic1"
#define I80130_TAG      "ic15"
#define I8251A_TAG      "ic59"
#define I8253_TAG       "ic60"
#define I8274_TAG       "ic65"
#define MM58174A_TAG    "ic66"
#define I8255_TAG       "ic69"
#define RS232_A_TAG     "rs232a"
#define RS232_B_TAG     "rs232b"
#define CASSETTE_TAG    "cassette"
#define CENTRONICS_TAG  "centronics"
#define ISBX_0_TAG      "isbx0"
#define ISBX_1_TAG      "isbx1"
#define GRAPHICS_TAG    "gfx"
#define COMPIS_KEYBOARD_TAG "compiskb"

class compis_state : public driver_device
{
public:
	compis_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, I80186_TAG),
		m_osp(*this, I80130_TAG),
		m_pit(*this, I8253_TAG),
		m_ppi(*this, I8255_TAG),
		m_mpsc(*this, I8274_TAG),
		m_centronics(*this, CENTRONICS_TAG),
		m_uart(*this, I8251A_TAG),
		m_rtc(*this, MM58174A_TAG),
		m_cassette(*this, CASSETTE_TAG),
		m_graphics(*this, GRAPHICS_TAG),
		m_isbx0(*this, ISBX_0_TAG),
		m_isbx1(*this, ISBX_1_TAG),
		m_ram(*this, RAM_TAG),
		m_s8(*this, "S8")
	{ }

	required_device<i80186_cpu_device> m_maincpu;
	required_device<i80130_device> m_osp;
	required_device<pit8253_device> m_pit;
	required_device<i8255_device> m_ppi;
	required_device<i8274_device> m_mpsc;
	required_device<centronics_device> m_centronics;
	required_device<i8251_device> m_uart;
	required_device<mm58174_device> m_rtc;
	required_device<cassette_image_device> m_cassette;
	required_device<compis_graphics_slot_device> m_graphics;
	required_device<isbx_slot_device> m_isbx0;
	required_device<isbx_slot_device> m_isbx1;
	required_device<ram_device> m_ram;
	required_ioport m_s8;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint16_t pcs6_0_1_r(offs_t offset, uint16_t mem_mask = ~0);
	void pcs6_0_1_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t pcs6_2_3_r(offs_t offset, uint16_t mem_mask = ~0);
	void pcs6_2_3_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t pcs6_4_5_r(offs_t offset, uint16_t mem_mask = ~0);
	void pcs6_4_5_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t pcs6_6_7_r(offs_t offset, uint16_t mem_mask = ~0);
	void pcs6_6_7_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t pcs6_8_9_r(offs_t offset, uint16_t mem_mask = ~0);
	void pcs6_8_9_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t pcs6_10_11_r(offs_t offset, uint16_t mem_mask = ~0);
	void pcs6_10_11_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t pcs6_12_13_r(offs_t offset, uint16_t mem_mask = ~0);
	void pcs6_12_13_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t pcs6_14_15_r(offs_t offset, uint16_t mem_mask = ~0);
	void pcs6_14_15_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

	uint8_t compis_irq_callback();

	uint8_t ppi_pb_r();
	void ppi_pc_w(uint8_t data);

	void tmr0_w(int state);
	void tmr1_w(int state);
	void tmr2_w(int state);
	void tmr5_w(int state);

	TIMER_DEVICE_CALLBACK_MEMBER( tape_tick );

	int m_centronics_busy;
	int m_centronics_select;

	void write_centronics_busy(int state);
	void write_centronics_select(int state);

	int m_tmr0;
	void compis(machine_config &config);
	void compis2(machine_config &config);
	void compis2_mem(address_map &map) ATTR_COLD;
	void compis_io(address_map &map) ATTR_COLD;
	void compis_mem(address_map &map) ATTR_COLD;
};



//**************************************************************************
//  READ/WRITE HANDLERS
//**************************************************************************

uint16_t compis_state::pcs6_0_1_r(offs_t offset, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
	{
		return 0xff;
	}
	else
	{
		return m_graphics->dma_ack_r(offset);
	}
}

void compis_state::pcs6_0_1_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
	{
		cassette_state state = BIT(data, 0) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED;

		m_cassette->change_state(state, CASSETTE_MASK_MOTOR);
	}
	else
	{
		m_graphics->dma_ack_w(offset, data);
	}
}

uint16_t compis_state::pcs6_2_3_r(offs_t offset, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
	{
		return m_mpsc->inta_r();
	}
	else
	{
		return m_uart->read(offset & 1) << 8;
	}
}

void compis_state::pcs6_2_3_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
	{
	}
	else
	{
		m_uart->write(offset & 1, data >> 8);
	}
}

uint16_t compis_state::pcs6_4_5_r(offs_t offset, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
	{
		return m_mpsc->cd_ba_r(offset & 0x03);
	}
	else
	{
		m_isbx0->tdma_w(0);
		m_isbx0->tdma_w(1);

		return 0xff;
	}
}

void compis_state::pcs6_4_5_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
	{
		m_mpsc->cd_ba_w(offset & 0x03, data);
	}
	else
	{
		m_isbx0->tdma_w(0);
		m_isbx0->tdma_w(1);
	}
}

uint16_t compis_state::pcs6_6_7_r(offs_t offset, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
	{
		return m_graphics->pcs6_6_r(offset);
	}
	else
	{
		m_isbx1->tdma_w(0);
		m_isbx1->tdma_w(1);

		return 0xff;
	}
}

void compis_state::pcs6_6_7_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
	{
		m_graphics->pcs6_6_w(offset, data);
	}
	else
	{
		m_isbx1->tdma_w(0);
		m_isbx1->tdma_w(1);
	}
}

uint16_t compis_state::pcs6_8_9_r(offs_t offset, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
	{
		return m_isbx0->mcs0_r(offset);
	}
	else
	{
		return m_isbx0->mcs1_r(offset) << 8;
	}
}

void compis_state::pcs6_8_9_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
	{
		m_isbx0->mcs0_w(offset, data);
	}
	else
	{
		m_isbx0->mcs1_w(offset, data >> 8);
	}
}

uint16_t compis_state::pcs6_10_11_r(offs_t offset, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
	{
		return m_isbx0->mcs1_r(offset);
	}
	else
	{
		return m_isbx0->mdack_r(offset) << 8;
	}
}

void compis_state::pcs6_10_11_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
	{
		m_isbx0->mcs1_w(offset, data);
	}
	else
	{
		m_isbx0->mdack_w(offset, data >> 8);
	}
}

uint16_t compis_state::pcs6_12_13_r(offs_t offset, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
	{
		return m_isbx1->mcs0_r(offset);
	}
	else
	{
		return m_isbx1->mcs1_r(offset) << 8;
	}
}

void compis_state::pcs6_12_13_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
	{
		m_isbx1->mcs0_w(offset, data);
	}
	else
	{
		m_isbx1->mcs1_w(offset, data >> 8);
	}
}

uint16_t compis_state::pcs6_14_15_r(offs_t offset, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
	{
		return m_isbx1->mcs1_r(offset);
	}
	else
	{
		return m_isbx1->mdack_r(offset) << 8;
	}
}

void compis_state::pcs6_14_15_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
	{
		m_isbx1->mcs1_w(offset, data);
	}
	else
	{
		m_isbx1->mdack_w(offset, data >> 8);
	}
}


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

//-------------------------------------------------
//  ADDRESS_MAP( compis_mem )
//-------------------------------------------------

void compis_state::compis_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x1ffff).ram();
	map(0x60000, 0x63fff).mirror(0x1c000).m(m_osp, FUNC(i80130_device::rom_map));
	map(0xe0000, 0xeffff).mirror(0x10000).rom().region(I80186_TAG, 0);
}


//-------------------------------------------------
//  ADDRESS_MAP( compis2_mem )
//-------------------------------------------------

void compis_state::compis2_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x3ffff).ram();
	map(0xe0000, 0xeffff).mirror(0x10000).rom().region(I80186_TAG, 0);
}


//-------------------------------------------------
//  ADDRESS_MAP( compis_io )
//-------------------------------------------------

void compis_state::compis_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0007) /* PCS0 */ .mirror(0x78).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0xff00);
	map(0x0080, 0x0087) /* PCS1 */ .mirror(0x78).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0x0100, 0x011f) /* PCS2 */ .mirror(0x60).rw(m_rtc, FUNC(mm58174_device::read), FUNC(mm58174_device::write)).umask16(0x00ff);
	map(0x0180, 0x01ff) /* PCS3 */ .rw(m_graphics, FUNC(compis_graphics_slot_device::pcs3_r), FUNC(compis_graphics_slot_device::pcs3_w));
	//map(0x0200, 0x0201) /* PCS4 */ .mirror(0x7e);
	map(0x0280, 0x028f) /* PCS5 */ .mirror(0x70).m(m_osp, FUNC(i80130_device::io_map));
	map(0x0300, 0x030f).rw(FUNC(compis_state::pcs6_0_1_r), FUNC(compis_state::pcs6_0_1_w));
	map(0x0310, 0x031f).rw(FUNC(compis_state::pcs6_2_3_r), FUNC(compis_state::pcs6_2_3_w));
	map(0x0320, 0x032f).rw(FUNC(compis_state::pcs6_4_5_r), FUNC(compis_state::pcs6_4_5_w));
	map(0x0330, 0x033f).rw(FUNC(compis_state::pcs6_6_7_r), FUNC(compis_state::pcs6_6_7_w));
	map(0x0340, 0x034f).rw(FUNC(compis_state::pcs6_8_9_r), FUNC(compis_state::pcs6_8_9_w));
	map(0x0350, 0x035f).rw(FUNC(compis_state::pcs6_10_11_r), FUNC(compis_state::pcs6_10_11_w));
	map(0x0360, 0x036f).rw(FUNC(compis_state::pcs6_12_13_r), FUNC(compis_state::pcs6_12_13_w));
	map(0x0370, 0x037f).rw(FUNC(compis_state::pcs6_14_15_r), FUNC(compis_state::pcs6_14_15_w));
}



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

//-------------------------------------------------
//  INPUT_PORTS( compis )
//-------------------------------------------------

static INPUT_PORTS_START( compis )
	PORT_START("S1")
	PORT_CONFNAME( 0x01, 0x00, "S1 ROM Type")
	PORT_CONFSETTING(    0x00, "27128" )
	PORT_CONFSETTING(    0x01, "27256" )

	PORT_START("S2")
	PORT_CONFNAME( 0x01, 0x00, "S2 IC36/IC40")
	PORT_CONFSETTING(    0x00, "ROM" )
	PORT_CONFSETTING(    0x01, "RAM" )

	PORT_START("S3")
	PORT_CONFNAME( 0x03, 0x00, "S3 J4 RxC")
	PORT_CONFSETTING(    0x00, "DCE" )
	PORT_CONFSETTING(    0x01, "Tmr3" )
	PORT_CONFSETTING(    0x02, "Tmr4" )

	PORT_START("S4")
	PORT_CONFNAME( 0x01, 0x01, "S4 iSBX0 Bus Width")
	PORT_CONFSETTING(    0x00, "8 Bit" )
	PORT_CONFSETTING(    0x01, "16 Bit" )

	PORT_START("S5")
	PORT_CONFNAME( 0x01, 0x01, "S5 iSBX1 Bus Width")
	PORT_CONFSETTING(    0x00, "8 Bit" )
	PORT_CONFSETTING(    0x01, "16 Bit" )

	PORT_START("S6")
	PORT_CONFNAME( 0x001, 0x001, "S6 INT 8274")
	PORT_CONFSETTING(     0x000, DEF_STR( Off ) )
	PORT_CONFSETTING(     0x001, DEF_STR( On ) )
	PORT_CONFNAME( 0x002, 0x000, "S6 TxRDY 8251")
	PORT_CONFSETTING(     0x000, DEF_STR( Off ) )
	PORT_CONFSETTING(     0x002, DEF_STR( On ) )
	PORT_CONFNAME( 0x004, 0x000, "S6 INT KB")
	PORT_CONFSETTING(     0x000, DEF_STR( Off ) )
	PORT_CONFSETTING(     0x004, DEF_STR( On ) )
	PORT_CONFNAME( 0x008, 0x008, "S6 DELAY 80150")
	PORT_CONFSETTING(     0x000, DEF_STR( Off ) )
	PORT_CONFSETTING(     0x008, DEF_STR( On ) )
	PORT_CONFNAME( 0x010, 0x000, "S6 INT0 iSBX1 (J9)")
	PORT_CONFSETTING(     0x000, DEF_STR( Off ) )
	PORT_CONFSETTING(     0x010, DEF_STR( On ) )
	PORT_CONFNAME( 0x020, 0x000, "S6 INT1 iSBX1 (J9)")
	PORT_CONFSETTING(     0x000, DEF_STR( Off ) )
	PORT_CONFSETTING(     0x020, DEF_STR( On ) )
	PORT_CONFNAME( 0x040, 0x040, "S6 ACK J7")
	PORT_CONFSETTING(     0x000, DEF_STR( Off ) )
	PORT_CONFSETTING(     0x040, DEF_STR( On ) )
	PORT_CONFNAME( 0x080, 0x000, "S6 SYSTICK 80150")
	PORT_CONFSETTING(     0x000, DEF_STR( Off ) )
	PORT_CONFSETTING(     0x080, DEF_STR( On ) )
	PORT_CONFNAME( 0x100, 0x100, "S6 RxRDY 8251")
	PORT_CONFSETTING(     0x000, DEF_STR( Off ) )
	PORT_CONFSETTING(     0x100, DEF_STR( On ) )
	PORT_CONFNAME( 0x200, 0x000, "S6 INT0 iSBX0 (J8)")
	PORT_CONFSETTING(     0x000, DEF_STR( Off ) )
	PORT_CONFSETTING(     0x200, DEF_STR( On ) )
	PORT_CONFNAME( 0x400, 0x400, "S6 INT1 iSBX0 (J8)")
	PORT_CONFSETTING(     0x000, DEF_STR( Off ) )
	PORT_CONFSETTING(     0x400, DEF_STR( On ) )

	PORT_START("S7")
	PORT_CONFNAME( 0x01, 0x00, "S7 ROM Type")
	PORT_CONFSETTING(    0x00, "27128" )
	PORT_CONFSETTING(    0x01, "27256" )

	PORT_START("S8")
	PORT_CONFNAME( 0x18, 0x00, "S8 Test Mode")
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x08, "Remote Test" )
	PORT_CONFSETTING(    0x10, "Standalone Test" )
	PORT_CONFSETTING(    0x18, "Reserved" )

	PORT_START("S9")
	PORT_CONFNAME( 0x03, 0x00, "S9 8274 TxCB")
	PORT_CONFSETTING(    0x00, "DCE-Rxc (J4-11)" )
	PORT_CONFSETTING(    0x01, "DCE-Txc (J4-13)" )
	PORT_CONFSETTING(    0x02, "Tmr3" )

	PORT_START("S10")
	PORT_CONFNAME( 0x01, 0x01, "S10 8274 RxCA")
	PORT_CONFSETTING(    0x00, "DCE (J2-11)" )
	PORT_CONFSETTING(    0x01, "Tmr5" )

	PORT_START("S11")
	PORT_CONFNAME( 0x03, 0x01, "S11 8274 TxCA")
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, "DCE (J2-13)" )
	PORT_CONFSETTING(    0x02, "Tmr5" )

	PORT_START("S12")
	PORT_CONFNAME( 0x01, 0x01, "S12 8274 TxDA")
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, "V24 (J2)" )

	PORT_START("S13")
	PORT_CONFNAME( 0x01, 0x01, "S13 8274 RxDA")
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, "V24 (J2)" )

	PORT_START("S14")
	PORT_CONFNAME( 0x01, 0x01, "S14 8274 TxCA")
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )

	PORT_START("S15")
	PORT_CONFNAME( 0x01, 0x00, "S15 Network")
	PORT_CONFSETTING(    0x00, "Server" )
	PORT_CONFSETTING(    0x01, "Client" )
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  I80186_INTERFACE( cpu_intf )
//-------------------------------------------------

uint8_t compis_state::compis_irq_callback()
{
	return m_osp->inta_r();
}

void compis_state::tmr0_w(int state)
{
	m_tmr0 = state;

	m_cassette->output(m_tmr0 ? -1 : 1);

	m_maincpu->tmrin0_w(state);
}

void compis_state::tmr1_w(int state)
{
	m_isbx0->mclk_w(state);
	m_isbx1->mclk_w(state);

	m_maincpu->tmrin1_w(state);
}


//-------------------------------------------------
//  I80130_INTERFACE( osp_intf )
//-------------------------------------------------

void compis_state::tmr2_w(int state)
{
	m_uart->write_rxc(state);
	m_uart->write_txc(state);
}


void compis_state::tmr5_w(int state)
{
	m_mpsc->rxca_w(state);
	m_mpsc->txca_w(state);
}

//-------------------------------------------------
//  I8255A interface
//-------------------------------------------------

void compis_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

void compis_state::write_centronics_select(int state)
{
	m_centronics_select = state;
}

uint8_t compis_state::ppi_pb_r()
{
	/*

	    bit     description

	    0       J5-4
	    1       J5-5
	    2       J6-3 Cassette read
	    3       J2-6 DSR / S8-4 Test
	    4       J4-6 DSR / S8-3 Test
	    5       J7-11 Centronics BUSY
	    6       J7-13 Centronics SELECT
	    7       Tmr0

	*/

	uint8_t data = 0;

	/* DIP switch - Test mode */
	data = m_s8->read();

	// cassette
	data |= (m_cassette->input() > 0.0) << 2;

	/* Centronics busy */
	data |= m_centronics_busy << 5;
	data |= m_centronics_select << 6;

	// TMR0
	data |= m_tmr0 << 7;

	return data;
}

void compis_state::ppi_pc_w(uint8_t data)
{
	/*

	    bit     description

	    0       J5-1
	    1       J5-2
	    2       Select: 1=time measure, DSR from J2/J4 pin 6. 0=read cassette
	    3       Datex: Tristate datex output (low)
	    4       V2-5 Floppy motor on/off
	    5       J7-1 Centronics STROBE
	    6       V2-4 Floppy Soft reset
	    7       V2-3 Floppy Terminal count

	*/

	m_isbx0->opt1_w(BIT(data, 4));

	m_centronics->write_strobe(BIT(data, 5));

	if (BIT(data, 6))
	{
		m_isbx0->reset();
	}

	m_isbx0->opt0_w(BIT(data, 7));
}

TIMER_DEVICE_CALLBACK_MEMBER( compis_state::tape_tick )
{
	m_maincpu->tmrin0_w(m_cassette->input() > 0.0);
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  machine_start
//-------------------------------------------------

void compis_state::machine_start()
{
	// RAM size
	switch (m_ram->size())
	{
	case 256*1024:
		m_maincpu->space(AS_PROGRAM).install_ram(0x20000, 0x3ffff, m_ram->pointer());
		break;

	case 512*1024:
		m_maincpu->space(AS_PROGRAM).install_ram(0x20000, 0x7ffff, m_ram->pointer());
		break;

	case 768*1024:
		m_maincpu->space(AS_PROGRAM).install_ram(0x20000, 0xbffff, m_ram->pointer());
		break;
	}

	// state saving
	save_item(NAME(m_centronics_busy));
	save_item(NAME(m_centronics_select));
	save_item(NAME(m_tmr0));
}


//-------------------------------------------------
//  machine_reset
//-------------------------------------------------

void compis_state::machine_reset()
{
	m_uart->reset();
	m_mpsc->reset();
	m_ppi->reset();
	m_graphics->reset();
	m_isbx0->reset();
	m_isbx1->reset();
}



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

//-------------------------------------------------
//  machine_config( compis )
//-------------------------------------------------

void compis_state::compis(machine_config &config)
{
	// basic machine hardware
	I80186(config, m_maincpu, 15.36_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &compis_state::compis_mem);
	m_maincpu->set_addrmap(AS_IO, &compis_state::compis_io);
	m_maincpu->read_slave_ack_callback().set(FUNC(compis_state::compis_irq_callback));
	m_maincpu->tmrout0_handler().set(FUNC(compis_state::tmr0_w));
	m_maincpu->tmrout1_handler().set(FUNC(compis_state::tmr1_w));

	// devices
	I80130(config, m_osp, 15.36_MHz_XTAL/2);
	m_osp->irq().set(m_maincpu, FUNC(i80186_cpu_device::int0_w));
	m_osp->systick().set(m_osp, FUNC(i80130_device::ir3_w));
	m_osp->delay().set(m_osp, FUNC(i80130_device::ir7_w));
	m_osp->baud().set(FUNC(compis_state::tmr2_w));

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(15.36_MHz_XTAL/8);
	m_pit->out_handler<0>().set(m_mpsc, FUNC(i8274_device::rxtxcb_w));
	m_pit->set_clk<1>(15.36_MHz_XTAL/8);
	m_pit->set_clk<2>(15.36_MHz_XTAL/8);
	m_pit->out_handler<2>().set(FUNC(compis_state::tmr5_w));

	I8255(config, m_ppi);
	m_ppi->out_pa_callback().set("cent_data_out", FUNC(output_latch_device::write));
	m_ppi->in_pb_callback().set(FUNC(compis_state::ppi_pb_r));
	m_ppi->out_pc_callback().set(FUNC(compis_state::ppi_pc_w));

	I8251(config, m_uart, 0);
	m_uart->txd_handler().set(COMPIS_KEYBOARD_TAG, FUNC(compis_keyboard_device::si_w));
	m_uart->rxrdy_handler().set(m_osp, FUNC(i80130_device::ir2_w));
	m_uart->txrdy_handler().set(m_maincpu, FUNC(i80186_cpu_device::int1_w));

	compis_keyboard_device &kb(COMPIS_KEYBOARD(config, COMPIS_KEYBOARD_TAG, 0));
	kb.out_tx_handler().set(m_uart, FUNC(i8251_device::write_rxd));

	I8274(config, m_mpsc, 15.36_MHz_XTAL/4);
	m_mpsc->out_txda_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_txd));
	m_mpsc->out_dtra_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_dtr));
	m_mpsc->out_rtsa_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_rts));
	m_mpsc->out_txdb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_txd));
	m_mpsc->out_dtrb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_dtr));
	m_mpsc->out_rtsb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_rts));
	m_mpsc->out_int_callback().set(m_maincpu, FUNC(i80186_cpu_device::int3_w));

	MM58174(config, m_rtc, 32.768_kHz_XTAL);

	SPEAKER(config, "cass_snd").front_center();
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "cass_snd", 0.05);

	TIMER(config, "tape").configure_periodic(FUNC(compis_state::tape_tick), attotime::from_hz(44100));

	rs232_port_device &rs232a(RS232_PORT(config, RS232_A_TAG, default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_mpsc, FUNC(i8274_device::rxa_w));
	rs232a.dcd_handler().set(m_mpsc, FUNC(i8274_device::dcda_w));
	rs232a.cts_handler().set(m_mpsc, FUNC(i8274_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, RS232_B_TAG, default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_mpsc, FUNC(i8274_device::rxb_w));
	rs232b.dcd_handler().set(m_mpsc, FUNC(i8274_device::dcdb_w));
	rs232b.cts_handler().set(m_mpsc, FUNC(i8274_device::ctsb_w));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(compis_state::write_centronics_busy));
	m_centronics->select_handler().set(FUNC(compis_state::write_centronics_select));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	COMPIS_GRAPHICS_SLOT(config, m_graphics, 15.36_MHz_XTAL/2, compis_graphics_cards, "hrg");

	ISBX_SLOT(config, m_isbx0, 0, isbx_cards, "fdc");
	m_isbx0->mintr0().set(m_osp, FUNC(i80130_device::ir1_w));
	m_isbx0->mintr1().set(m_osp, FUNC(i80130_device::ir0_w));
	m_isbx0->mdrqt().set(m_maincpu, FUNC(i80186_cpu_device::drq0_w));
	ISBX_SLOT(config, m_isbx1, 0, isbx_cards, nullptr);
	m_isbx1->mintr0().set(m_osp, FUNC(i80130_device::ir6_w));
	m_isbx1->mintr1().set(m_osp, FUNC(i80130_device::ir5_w));
	m_isbx1->mdrqt().set(m_maincpu, FUNC(i80186_cpu_device::drq1_w));

	// software lists
	SOFTWARE_LIST(config, "flop_list").set_original("compis");

	// internal ram
	RAM(config, m_ram).set_default_size("128K").set_extra_options("256K");
}


//-------------------------------------------------
//  machine_config( compis2 )
//-------------------------------------------------

void compis_state::compis2(machine_config &config)
{
	compis(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &compis_state::compis2_mem);
	// TODO 8087

	// internal ram
	m_ram->set_default_size("256K").set_extra_options("512K,768K");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( compis )
//-------------------------------------------------

ROM_START( compis )
	ROM_REGION16_LE( 0x10000, I80186_TAG, 0 )
	ROM_DEFAULT_BIOS( "v303" )

	ROM_SYSTEM_BIOS( 0, "v20", "Compis v2.0 (1985-05-15)" )
	ROMX_LOAD( "sa883003.u40", 0x0000, 0x4000, CRC(195ef6bf) SHA1(eaf8ae897e1a4b62d3038ff23777ce8741b766ef), ROM_BIOS(0) | ROM_SKIP(1) )
	ROMX_LOAD( "sa883003.u36", 0x0001, 0x4000, CRC(7c918f56) SHA1(8ba33d206351c52f44f1aa76cc4d7f292dcef761), ROM_BIOS(0) | ROM_SKIP(1) )
	ROMX_LOAD( "sa883003.u39", 0x8000, 0x4000, CRC(3cca66db) SHA1(cac36c9caa2f5bb42d7a6d5b84f419318628935f), ROM_BIOS(0) | ROM_SKIP(1) )
	ROMX_LOAD( "sa883003.u35", 0x8001, 0x4000, CRC(43c38e76) SHA1(f32e43604107def2c2259898926d090f2ed62104), ROM_BIOS(0) | ROM_SKIP(1) )

	ROM_SYSTEM_BIOS( 1, "v302", "Compis II v3.02 (1986-09-09)" )
	ROMX_LOAD( "comp302.u39", 0x0000, 0x8000, CRC(16a7651e) SHA1(4cbd4ba6c6c915c04dfc913ec49f87c1dd7344e3), ROM_BIOS(1) | ROM_SKIP(1) )
	ROMX_LOAD( "comp302.u35", 0x0001, 0x8000, CRC(ae546bef) SHA1(572e45030de552bb1949a7facbc885b8bf033fc6), ROM_BIOS(1) | ROM_SKIP(1) )

	ROM_SYSTEM_BIOS( 2, "v303", "Compis II v3.03 (1987-03-09)" )
	ROMX_LOAD( "rysa094.u39", 0x0000, 0x8000, CRC(e7302bff) SHA1(44ea20ef4008849af036c1a945bc4f27431048fb), ROM_BIOS(2) | ROM_SKIP(1) )
	ROMX_LOAD( "rysa094.u35", 0x0001, 0x8000, CRC(b0694026) SHA1(eb6b2e3cb0f42fd5ffdf44f70e652ecb9714ce30), ROM_BIOS(2) | ROM_SKIP(1) )
ROM_END

#define rom_compis2 rom_compis

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY     FULLNAME     FLAGS
COMP( 1985, compis,  0,      0,      compis,  compis, compis_state, empty_init, "Telenova", "Compis",    MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1986, compis2, compis, 0,      compis2, compis, compis_state, empty_init, "Telenova", "Compis II", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )



compmahj.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
// thanks-to:Sean Riddle
/***************************************************************************

Skeleton driver for Nintendo Computer Mahjong

2 PCBs, MJ-CPU and MJ-DR. Each chip is custom labeled.
MJ-CPU: MJ-01, MJ-02, MJ-04, 2*MJ-03
MJ-DR: 2*MJ-06, MJ-05

MJ-01 is definitely a Sharp LH5801 or LH5803.  I'm not sure of the
difference; the pinouts are the same.  The PC-1600 Technical Reference says
LH-5803 is an "upper" version of LH-5801 that supports most of the same
machine language instructions except SDP, RDP and OFF are NOPs.
MJ-02 is an LH5367 8Kx8 ROM.  It is edge-enabled, so one of the CE lines has
to toggle for the byte at the address to be output.  This is different from
most modern ROMs that you can dump by simply tying /CE low and setting
different addresses.  It's mapped at $E000 - $FFFF.
MJ-03 is LH5101 256x4 SRAM.  There are 2 of them to provide 256x8 RAM.  They
are mapped to $8000-$80FF.
MJ-04 is a parallel I/O controller, like LH5081 or LH5810/LH5811, but it's
in a QFP48, whereas LH5081 is QFP44 and LH5810/11 is QFP60.  The pinout
doesn't match either of those either, so it's not just one of those in a
different package.

MJ-05 is QFP44 and is likely LH5031 LCD dot matrix common driver.
MJ-06 is QFP96 and is likely LH5035A/LH5036A LCD dot matrix segment driver.

LCD display: 1st and 3rd row is 20 8*11-pixel characters. 2nd row is 19
8*10-pixel characters, and a couple of custom segments to the right.

Piezo speaker, 20 buttons, 3-pos switch. There's a 12-pin connector for
linking 2 Computer Mahjongs together, using MJ 8002 link cable.

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

#include "emu.h"
#include "cpu/lh5801/lh5801.h"


namespace {

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

	void compmahj(machine_config &config);

private:
	required_device<lh5801_cpu_device> m_maincpu;

	void main_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	u8 ita_r();
};

u8 compmahj_state::ita_r()
{
	// f75e/f760
	return 0;
}

void compmahj_state::main_map(address_map &map)
{
	map(0x8000, 0x80ff).ram();
	map(0xe000, 0xffff).rom().region("maincpu", 0);
}

void compmahj_state::io_map(address_map &map)
{
}

static INPUT_PORTS_START( compmahj )
INPUT_PORTS_END

void compmahj_state::compmahj(machine_config &config)
{
	LH5801(config, m_maincpu, 2.5_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &compmahj_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &compmahj_state::io_map);
	m_maincpu->in_func().set(FUNC(compmahj_state::ita_r));
}

ROM_START( compmahj )
	ROM_REGION( 0x2000, "maincpu", 0)
	ROM_LOAD( "mj-02", 0x0000, 0x2000, CRC(56d51944) SHA1(5923d72753642314f1e64bd30a6bd69da3985ce9) )
ROM_END

} // anonymous namespace


/*    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY     FULLNAME                     FLAGS */
CONS( 1983, compmahj, 0,      0,      compmahj, compmahj, compmahj_state, empty_init, "Nintendo", "Computer Mah-jong Yakuman", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



compuchess.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Peter Trauner, Wilbert Pol, hap
// thanks-to:Sean Riddle, Berger
/*******************************************************************************

DCS CompuChess / Novag Chess Champion MK I
Initial driver version by PeT mess@utanet.at 2000,2001.
Driver largely rewritten over the years.

To start playing, enter difficulty level (1-6), then when it says "bP", press A
for new game, B for empty board, or C for continue.

TODO:
- cncchess sound is wrong, it should be a long dual-tone alarm sound

BTANB:
- cmpchess/cmpchess2 accepts illegal moves (Conic fixed that)
- digits may flash briefly after entering a command, eg. the "b" or "P" digit
  after setting board preset, this happens on the real device

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

DataCash Systems's CompuChess released mid-1977. One of the first chess
computers, the first one being Fidelity Chess Challenger (fidelity/cc1.cpp)

Hardware notes:
- PCB label: CompuChess 1, STAID INC, REV A, 7-77
- Fairchild 3850PK, 2MHz XTAL
- Fairchild 3853PK
- 2KB ROM, 256 bytes RAM(2*D2101AL-4)
- 4-digit 7seg led display, no sound

CompuChess 2nd edition is on the same PCB, but with a 3.57MHz XTAL(!), a 3850PC,
and Mostek MK3853N. The MCU speed was also confirmed with move calculation time.

2nd edition added 3 new game modes: E for "Game of Knights", F for "Amazon Queen",
and G for "Survival".

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

The game underneath CompuChess is better known as Novag's MK I, it was an
unlicensed clone. The ROM is identical. DCS sued JS&A / Novag Industries for
copyright infringement and somehow didn't manage to win the case.
see: https://law.justia.com/cases/federal/district-courts/FSupp/480/1063/1531498/

Unlike CompuChess, MK I was a large success, we can assume that it kickstarted
Novag's chess computer generation. It was also distributed as "Computer Chess"
by JS&A, in the same housing as MK I.

MCU frequency is with an LC circuit. The first version was around 5% faster than
CompuChess, the JS&A version was measured 2.18MHz on average. The 2nd version was
much faster, judging from move calculation time: around 3.5MHz(give or take 0.2).

1st (slow) version:
- Novag MK I with Boris Spassky photo on the box
- JS&A Computer Chess

2nd (fast) version:
- Novag MK I with Karpov photo on the box, later advertised with message
  "Neu: Mit Quick-Electronic für Schnelle Gegenzüge"
- Waddingtons Videomaster Chess Champion

MK I hardware description:
- An F8 3850 CPU accompanied by a 3853 memory interface
  Variations seen:
  - MOSTEK MK 3853N 7915 Philippines (static memory interface for F8)
  - MOSTEK MK 3850N-3 7917 Philipines (Fairchild F8 CPU)
  - 3850PK F7901 SINGAPORE (Fairchild F8 CPU)
  - 3853PK F7851 SINGAPORE (static memory interface for F8)
- 2KB 2316 compatible ROM
  Variations seen:
  - Signetics 7916E C48091 82S210-1 COPYRIGHT
  - RO-3-8316A 8316A-4480 7904 TAIWAN
- 2 x 2111 256x4 SRAM to provide 256 bytes of RAM
  Variations seen:
  - AM9111 BPC / P2111A-4 7851
- 16 keys placed in a 4 x 4 matrix
- Power on switch
- L/S switch. This switch is directly tied to the RESET pin of the F8 CPU.
  This allows the user to reset the CPU without destroying the RAM contents.
- A 4 character 11 segment digit display using a 15 pin interface. Of the 15
  pins 3 pins are not connected, so three segments are never used and this
  leaves a standard 7 segments display with a dot in the lower right.
- The digit display is driven by two other components:
  - SN75492N MALAYSIA 7840B
  - ULN2033A 7847
- Hardware addressing is controlled by a HBF4001AE.
- No speaker.

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

Conic Computer Chess (aka Master I in Germany) is also based on DCS CompuChess,
this time the 2nd edition. Conic have done a few changes, not enough to hide that
they 'borrowed' code. They also added a piezo circuit.

Hardware notes:
- Fairchild 3850PK CPU @ 2MHz (LC circuit), 3853PK
- 2KB ROM (3216), 256 bytes RAM (2*2111A)
- discrete sound

"bP" buttons are F, G, H (instead of A, B, C)

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

#include "emu.h"
#include "cpu/f8/f8.h"
#include "machine/f3853.h"
#include "machine/timer.h"
#include "sound/beep.h"
#include "video/pwm.h"
#include "speaker.h"

// internal artwork
#include "cmpchess.lh"
#include "novag_mk1.lh"
#include "conic_cchess.lh"


namespace {

class cmpchess_state : public driver_device
{
public:
	cmpchess_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_beeper_off(*this, "beeper_off"),
		m_beeper(*this, "beeper"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(reset_switch) { update_reset(newval); }
	DECLARE_INPUT_CHANGED_MEMBER(change_cpu_freq);

	// machine configs
	void cmpchess(machine_config &config);
	void cmpchess2(machine_config &config);
	void mk1(machine_config &config);
	void cncchess(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	optional_device<timer_device> m_beeper_off;
	optional_device<beep_device> m_beeper;
	required_ioport_array<4> m_inputs;

	u8 m_inp_mux = 0;
	u8 m_digit_select = 0;
	u8 m_digit_data = 0;
	bool m_blink = false;

	// address maps
	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;
	void cncchess_map(address_map &map) ATTR_COLD;
	void cncchess_io(address_map &map) ATTR_COLD;

	TIMER_DEVICE_CALLBACK_MEMBER(beeper_off) { m_beeper->set_state(0); }
	TIMER_DEVICE_CALLBACK_MEMBER(blink) { m_blink = !m_blink; update_display(); }
	void update_display();
	void update_reset(ioport_value state);

	// I/O handlers
	void input_w(u8 data);
	u8 input_r();
	void digit_select_w(u8 data);
	void digit_data_w(u8 data);
	u8 digit_data_r();
	u8 beeper_r(offs_t offset);

	void input_digit_select_w(u8 data) { input_w(data); digit_select_w(data); }
	void input_digit_data_w(u8 data) { input_w(data); digit_data_w(data); }
};



/*******************************************************************************
    Initialization
*******************************************************************************/

void cmpchess_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_digit_select));
	save_item(NAME(m_digit_data));
	save_item(NAME(m_blink));
}

void cmpchess_state::machine_reset()
{
	update_reset(ioport("RESET")->read());
}

void cmpchess_state::update_reset(ioport_value state)
{
	// reset switch is tied to F3850 RESET pin
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? ASSERT_LINE : CLEAR_LINE);

	// clear display
	if (state)
		m_display->clear();
}

INPUT_CHANGED_MEMBER(cmpchess_state::change_cpu_freq)
{
	// 2 MK I versions, 2nd one was a lot faster
	const u32 freq = (newval & 1) ? 3'500'000 : 2'250'000;
	m_maincpu->set_unscaled_clock(freq);
	subdevice<f3853_device>("smi")->set_unscaled_clock(freq);
}



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

void cmpchess_state::update_display()
{
	// display panel goes into automated blink mode if DP segment is held high,
	// and DP segment itself by default only appears to be active if no other segments are
	u8 dmask = (m_digit_data == 1) ? 0x80 : 0x7f;
	u8 bmask = (m_blink && m_digit_data & 1) ? 0 : 0xff;
	u8 bstate = m_digit_data & bmask & 1; // DP state for ignoring dmask

	u8 digit_data = bitswap<8>(m_digit_data,0,2,1,3,4,5,6,7) & dmask & bmask;
	m_display->matrix(m_digit_select, bstate << 8 | digit_data);
}

void cmpchess_state::digit_data_w(u8 data)
{
	// digit segment data
	m_digit_data = data;
	update_display();
}

u8 cmpchess_state::digit_data_r()
{
	return m_digit_data;
}

void cmpchess_state::digit_select_w(u8 data)
{
	// d0-d3: digit select (active low)
	m_digit_select = ~data & 0xf;
	update_display();
}

void cmpchess_state::input_w(u8 data)
{
	// input matrix is shared with either digit_data_w, or digit_select_w
	m_inp_mux = data;
}

u8 cmpchess_state::input_r()
{
	u8 data = m_inp_mux;

	// d0-d3: multiplexed inputs from d4-d7
	for (int i = 0; i < 4; i++)
		if (m_inp_mux & m_inputs[i]->read() << 4)
			data |= 1 << i;

	// d4-d7: multiplexed inputs from d0-d3
	for (int i = 0; i < 4; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read() << 4;

	return data;
}

u8 cmpchess_state::beeper_r(offs_t offset)
{
	// cncchess: trigger beeper
	if (!machine().side_effects_disabled())
	{
		m_beeper->set_state(1);
		m_beeper_off->adjust(attotime::from_msec(50)); // wrong, see TODO
	}

	return m_maincpu->space(AS_PROGRAM).read_byte(offset);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void cmpchess_state::main_map(address_map &map)
{
	map.global_mask(0x0fff);
	map(0x0000, 0x07ff).rom();
	map(0x0800, 0x08ff).mirror(0x0700).ram();
}

void cmpchess_state::main_io(address_map &map)
{
	map(0x00, 0x00).rw(FUNC(cmpchess_state::input_r), FUNC(cmpchess_state::input_digit_data_w));
	map(0x01, 0x01).w(FUNC(cmpchess_state::digit_select_w));
	map(0x0c, 0x0f).rw("smi", FUNC(f3853_device::read), FUNC(f3853_device::write));
}

void cmpchess_state::cncchess_map(address_map &map)
{
	map(0x0000, 0x07ff).rom();
	map(0x1800, 0x18ff).ram();
	map(0x8000, 0xffff).r(FUNC(cmpchess_state::beeper_r));
}

void cmpchess_state::cncchess_io(address_map &map)
{
	map(0x00, 0x00).rw(FUNC(cmpchess_state::digit_data_r), FUNC(cmpchess_state::digit_data_w));
	map(0x01, 0x01).rw(FUNC(cmpchess_state::input_r), FUNC(cmpchess_state::input_digit_select_w));
	map(0x0c, 0x0f).rw("smi", FUNC(f3853_device::read), FUNC(f3853_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( cmpchess )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("D / Play")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("C / White Bishop")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("B / White Queen")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("A / White King")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_M) PORT_NAME("H / md") // more data
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("G / White Pawn")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("F / White Rook")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("E / White Knight")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4 / fp") // find piece(position)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3 / Black Bishop")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2 / Black Queen")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1 / Black King")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8 / ep") // enter piece(position)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7 / Black Pawn")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6 / Black Rook")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5 / Black Knight")

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_F1) PORT_TOGGLE PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cmpchess_state::reset_switch), 0) PORT_NAME("Reset Switch")
INPUT_PORTS_END

static INPUT_PORTS_START( mk1 )
	PORT_INCLUDE( cmpchess )

	PORT_MODIFY("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_F1) PORT_TOGGLE PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cmpchess_state::reset_switch), 0) PORT_NAME("L.S. Switch")

	PORT_START("CPU")
	PORT_CONFNAME( 0x01, 0x00, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cmpchess_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "2.25MHz (Spassky packaging)" )
	PORT_CONFSETTING(    0x01, "3.5MHz (Karpov packaging)" )
INPUT_PORTS_END

static INPUT_PORTS_START( cncchess )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4 / Black Bishop")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3 / Black Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2 / Black Rook")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1 / Black Pawn")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_I) PORT_NAME("8 / IP") // insert piece (same as ep)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_S) PORT_NAME("7 / SP") // search piece (same as fp)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6 / Black King")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5 / Black Queen")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("D / White Bishop")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("C / White Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("B / White Rook")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("A / White Pawn")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("H / GO")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_M) PORT_NAME("G / MD") // more data
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("F / White King")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("E / White Queen")

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cmpchess_state::reset_switch), 0) PORT_NAME("Reset")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void cmpchess_state::cmpchess(machine_config &config)
{
	// basic machine hardware
	F8(config, m_maincpu, 2_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &cmpchess_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &cmpchess_state::main_io);
	m_maincpu->set_irq_acknowledge_callback("smi", FUNC(f3853_device::int_acknowledge));

	f3853_device &smi(F3853(config, "smi", 2_MHz_XTAL));
	smi.int_req_callback().set_inputline("maincpu", F8_INPUT_LINE_INT_REQ);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 8+1);
	m_display->set_segmask(0xf, 0xff);
	config.set_default_layout(layout_cmpchess);

	const attotime blink_period = attotime::from_msec(200); // approximation
	TIMER(config, "blink_display").configure_periodic(FUNC(cmpchess_state::blink), blink_period);
}

void cmpchess_state::cmpchess2(machine_config &config)
{
	cmpchess(config);

	// basic machine hardware
	m_maincpu->set_clock(3.579545_MHz_XTAL);
	subdevice<f3853_device>("smi")->set_clock(3.579545_MHz_XTAL);
}

void cmpchess_state::mk1(machine_config &config)
{
	cmpchess(config);

	// basic machine hardware
	m_maincpu->set_clock(2'250'000); // see notes
	subdevice<f3853_device>("smi")->set_clock(2'250'000);

	config.set_default_layout(layout_novag_mk1);
}

void cmpchess_state::cncchess(machine_config &config)
{
	cmpchess2(config);

	// basic machine hardware
	m_maincpu->set_clock(2'000'000); // LC circuit, measured 2MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &cmpchess_state::cncchess_map);
	m_maincpu->set_addrmap(AS_IO, &cmpchess_state::cncchess_io);

	subdevice<f3853_device>("smi")->set_clock(2'000'000);

	config.set_default_layout(layout_conic_cchess);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	BEEP(config, m_beeper, 2000); // wrong, see TODO
	m_beeper->add_route(ALL_OUTPUTS, "speaker", 0.25);
	TIMER(config, "beeper_off").configure_generic(FUNC(cmpchess_state::beeper_off));
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( cmpchess )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD("32014-4950_cmcsi_staid.u3", 0x0000, 0x0800, CRC(278f7bf3) SHA1(b384c95ba691d52dfdddd35987a71e9746a46170) )
ROM_END

ROM_START( cmpchess2 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD("8316a-4952_cmc2_staid.u3", 0x0000, 0x0800, CRC(aee49c5f) SHA1(8a10f74207f2646164cc0deed81bd082143ac38a) )
ROM_END


ROM_START( ccmk1 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD("82c210-1", 0x0000, 0x0800, CRC(278f7bf3) SHA1(b384c95ba691d52dfdddd35987a71e9746a46170) )
ROM_END


ROM_START( cncchess )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD("c48170.u1", 0x0000, 0x0800, CRC(5beace32) SHA1(9df924037614831a86b73eb3a16bbc80c63257a2) ) // 2316B
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT    COMPAT  MACHINE    INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1977, cmpchess,  0,        0,      cmpchess,  cmpchess, cmpchess_state, empty_init, "DataCash Systems / Staid", "CompuChess", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE ) // aka CompuChess I
SYST( 1978, cmpchess2, 0,        0,      cmpchess2, cmpchess, cmpchess_state, empty_init, "DataCash Systems / Staid", "CompuChess: The Second Edition", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )

SYST( 1978, ccmk1,     cmpchess, 0,      mk1,       mk1,      cmpchess_state, empty_init, "bootleg (Novag Industries)", "Chess Champion: MK I", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )

SYST( 1979, cncchess,  0,        0,      cncchess,  cncchess, cmpchess_state, empty_init, "Conic", "Computer Chess (Conic, model 7011)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )



compucolor.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

    Compucolor II

    http://www.compucolor.org/index.html

*/

/*

    TODO:

    - interlaced video
    - blinking
    - add-on ROM
    - add-on RAM
    - add-on unit

*/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/i8085/i8085.h"
#include "bus/compucolor/floppy.h"
#include "machine/ram.h"
#include "machine/ripple_counter.h"
#include "machine/tms5501.h"
#include "video/tms9927.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "utf8.h"


namespace {

#define I8080_TAG   "ua2"
#define TMS5501_TAG "ud2"
#define CRT5027_TAG "uf9"
#define RS232_TAG   "rs232"

class compucolor2_state : public driver_device
{
public:
	compucolor2_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, I8080_TAG),
		m_mioc(*this, TMS5501_TAG),
		m_vtac(*this, CRT5027_TAG),
		m_palette(*this, "palette"),
		m_rs232(*this, RS232_TAG),
		m_floppy0(*this, "cd0"),
		m_floppy1(*this, "cd1"),
		m_char_rom(*this, "chargen"),
		m_video_ram(*this, "videoram"),
		m_y(*this, "Y%u", 0),
		m_y128(*this, "Y128")
	{ }

	required_device<cpu_device> m_maincpu;
	required_device<tms5501_device> m_mioc;
	required_device<crt5027_device> m_vtac;
	required_device<palette_device> m_palette;
	required_device<rs232_port_device> m_rs232;
	required_device<compucolor_floppy_port_device> m_floppy0;
	required_device<compucolor_floppy_port_device> m_floppy1;
	required_memory_region m_char_rom;
	required_shared_ptr<uint8_t> m_video_ram;
	required_ioport_array<16> m_y;
	required_ioport m_y128;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	uint8_t xi_r();
	void xo_w(uint8_t data);
	void xmt_w(int state);

	IRQ_CALLBACK_MEMBER( int_ack );

	uint8_t m_xo;
	void compucolor2(machine_config &config);
	void compucolor2_io(address_map &map) ATTR_COLD;
	void compucolor2_mem(address_map &map) ATTR_COLD;
};

void compucolor2_state::compucolor2_mem(address_map &map)
{
	map(0x0000, 0x3fff).rom().region(I8080_TAG, 0);
	map(0x6000, 0x6fff).mirror(0x1000).ram().share("videoram");
	map(0x8000, 0xffff).ram();
}

void compucolor2_state::compucolor2_io(address_map &map)
{
	map(0x00, 0x0f).mirror(0x10).m(m_mioc, FUNC(tms5501_device::io_map));
	map(0x60, 0x6f).mirror(0x10).rw(m_vtac, FUNC(crt5027_device::read), FUNC(crt5027_device::write));
	map(0x80, 0x9f).mirror(0x60).rom().region("ua1", 0);
}

static INPUT_PORTS_START( compucolor2 )
	PORT_START("Y0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("_ CRT") PORT_CHAR('_')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F15 PLOT ESC")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BL/A7 OFF")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BLINK ON")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("^ USER") PORT_CHAR('^')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F14 CHAR RECT")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A7 ON")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BG ON")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("m M TERM MODE") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("] BLCK") PORT_CHAR(']')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F13 POINT X")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RETURN ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR('\r')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("FG ON")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(UCHAR_MAMEKEY(EQUALS_PAD))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("l L LOCL") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\\ 45 UP") PORT_CHAR('\\')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F12 POINT Y")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ERASE PAGE")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("k K ROLL") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("[ VIS") PORT_CHAR('[')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F11 POINT INC")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ERASE LINE")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("z Z 45 DW") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F10 X BAR XO")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"Keypad ×") PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(ASTERISK))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("y Y TEST") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F9 X BAR Y")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TAB) PORT_CHAR(UCHAR_MAMEKEY(TAB))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("h H HALF") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("x X XMIT") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F8 X BAR XM")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("HOME")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y8")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("g G BELL") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("w W BASIC") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F7 X BAR INC")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LIST")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y9")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("v V DEL LINE") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F6 Y BAR YO")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("POKE")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y10")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("e E BSC RST") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("u U INS LINE") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F5 Y BAR X")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("AUTO") PORT_CODE(KEYCODE_PGDN)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PRINT")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y11")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("d D DISK FCS") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("t T TEXT EDIT") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F4 Y BAR YM")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DELETE CHAR")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LOAD")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y12")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("c C CURSOR X-Y") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("s S ASMB") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F3 Y BAR INC")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INSERT LINE")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SAVE")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y13")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("b B PLOT") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("r R BAUD RATE") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F2 VECT X1")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DELETE LINE")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PLOT")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y14")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("a A PROT BLND") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("q Q INS CHAR") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F1 VECT Y1")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INSERT CHAR")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PUT")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y15")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("@ NULL") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("p P CPU OP SYS") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F0 VECT INC")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BREAK ATTN")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("OUT")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y128")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CPU RESET")
	PORT_BIT( 0x0e, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("REPEAT")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CAPS LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
INPUT_PORTS_END

uint32_t compucolor2_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	for (int y = 0; y < 32*8; y++)
	{
		offs_t offset = (y / 8) * 128;

		for (int sx = 0; sx < 64; sx++)
		{
			uint8_t code = m_video_ram[offset++];
			uint8_t attr = m_video_ram[offset++];

			offs_t char_offs = ((code & 0x7f) << 3) | (y & 0x07);
			if (BIT(code, 7)) char_offs = ((code & 0x7f) << 3) | ((y >> 1) & 0x07);

			uint8_t data = m_char_rom->base()[char_offs];

			rgb_t const fg = m_palette->pen_color(attr & 0x07);
			rgb_t const bg = m_palette->pen_color((attr >> 3) & 0x07);

			for (int x = 0; x < 6; x++)
			{
				bitmap.pix(y, (sx * 6) + x) = BIT(data, 7) ? fg : bg;

				data <<= 1;
			}
		}
	}

	return 0;
}

uint8_t compucolor2_state::xi_r()
{
	uint8_t data = 0xff;

	switch ((m_xo >> 4) & 0x03)
	{
	case 0:
		data &= m_y[m_xo & 0x0f]->read();

		if (BIT(m_xo, 7))
		{
			data = (m_y128->read() & 0xf0) | (data & 0x0f);
		}
		break;

	default:
		data = 0;
	}

	return data;
}

void compucolor2_state::xo_w(uint8_t data)
{
	/*

	    bit     description

	    0       Keyboard A0, Stepper Phase 0
	    1       Keyboard A1, Stepper Phase 1
	    2       Keyboard A2, Stepper Phase 2
	    3       Keyboard A3, Disk _R/W
	    4       00=Keyboard/Modem, 01=Internal Drive (CD0), 10=External Drive (CD1)
	    5       ^
	    6       LED
	    7       0=Normal Keyboard, 1=SHIFT/CONTROL/REPEAT/CAPS LOCK

	*/

	m_xo = data;

	switch ((m_xo >> 4) & 0x03)
	{
	case 1:
		m_floppy0->select_w(0);
		m_floppy0->rw_w(BIT(data, 3));
		m_floppy0->stepper_w(data & 0x07);

		m_floppy1->select_w(1);
		break;

	case 2:
		m_floppy1->select_w(0);
		m_floppy1->rw_w(BIT(data, 3));
		m_floppy1->stepper_w(data & 0x07);

		m_floppy0->select_w(1);
		break;
	}
}

void compucolor2_state::xmt_w(int state)
{
	switch ((m_xo >> 4) & 0x03)
	{
	case 0:
		m_rs232->write_txd(state);
		break;

	case 1:
		m_floppy0->write_txd(state);
		break;

	case 2:
		m_floppy1->write_txd(state);
		break;
	}
}

IRQ_CALLBACK_MEMBER( compucolor2_state::int_ack )
{
	return m_mioc->get_vector();
}

void compucolor2_state::machine_start()
{
	// state saving
	save_item(NAME(m_xo));
}

void compucolor2_state::machine_reset()
{
	m_rs232->write_rts(1);
	m_rs232->write_dtr(1);
}

void compucolor2_state::compucolor2(machine_config &config)
{
	// basic machine hardware
	I8080(config, m_maincpu, XTAL(17'971'200)/9);
	m_maincpu->set_addrmap(AS_PROGRAM, &compucolor2_state::compucolor2_mem);
	m_maincpu->set_addrmap(AS_IO, &compucolor2_state::compucolor2_io);
	m_maincpu->set_irq_acknowledge_callback(FUNC(compucolor2_state::int_ack));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(XTAL(17'971'200)/2, 93*6, 0, 64*6, 268, 0, 256);
	screen.set_screen_update(FUNC(compucolor2_state::screen_update));

	PALETTE(config, m_palette, palette_device::RGB_3BIT);

	CRT5027(config, m_vtac, XTAL(17'971'200)/2/6);
	m_vtac->set_char_width(6);
	m_vtac->vsyn_callback().set("blink", FUNC(ripple_counter_device::clock_w));
	m_vtac->set_screen("screen");

	ripple_counter_device &blink(RIPPLE_COUNTER(config, "blink")); // 74LS393 at UG10
	blink.set_stages(8);
	blink.count_out_cb().set(m_mioc, FUNC(tms5501_device::sens_w)).bit(4);

	// devices
	TMS5501(config, m_mioc, XTAL(17'971'200)/9);
	m_mioc->int_callback().set_inputline(I8080_TAG, I8085_INTR_LINE);
	m_mioc->xmt_callback().set(FUNC(compucolor2_state::xmt_w));
	m_mioc->xi_callback().set(FUNC(compucolor2_state::xi_r));
	m_mioc->xo_callback().set(FUNC(compucolor2_state::xo_w));

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set(m_mioc, FUNC(tms5501_device::rcv_w));

	COMPUCOLOR_FLOPPY_PORT(config, m_floppy0, compucolor_floppy_port_devices, "floppy");
	m_floppy0->rxd_handler().set(m_mioc, FUNC(tms5501_device::rcv_w));

	COMPUCOLOR_FLOPPY_PORT(config, m_floppy1, compucolor_floppy_port_devices, nullptr);
	m_floppy1->rxd_handler().set(m_mioc, FUNC(tms5501_device::rcv_w));

	// internal ram
	RAM(config, RAM_TAG).set_default_size("32K").set_extra_options("8K,16K");

	// software list
	SOFTWARE_LIST(config, "flop_list").set_original("compclr2_flop");
}

ROM_START( compclr2 )
	ROM_REGION( 0x4000, I8080_TAG, 0 )
	ROM_SYSTEM_BIOS( 0, "678", "v6.78" )
	ROMX_LOAD( "v678.rom", 0x0000, 0x4000, BAD_DUMP CRC(5e559469) SHA1(fe308774aae1294c852fe24017e58d892d880cd3), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "879", "v8.79" )
	ROMX_LOAD( "v879.rom", 0x0000, 0x4000, BAD_DUMP CRC(4de8e652) SHA1(e5c55da3ac893b8a5a99c8795af3ca72b1645f3f), ROM_BIOS(1) )

	ROM_REGION( 0x800, "chargen", 0 )
	ROM_LOAD( "chargen.uf6", 0x000, 0x400, BAD_DUMP CRC(7eef135a) SHA1(be488ef32f54c6e5f551fb84ab12b881aef72dd9) )
	ROM_LOAD( "chargen.uf7", 0x400, 0x400, BAD_DUMP CRC(2bee7cf6) SHA1(808e0fc6f2332b4297de190eafcf84668703e2f4) )

	ROM_REGION( 0x20, "ua1", 0 )
	ROM_LOAD( "82s123.ua1", 0x00, 0x20, BAD_DUMP CRC(27ae54bc) SHA1(ccb056fbc1ec2132f2602217af64d77237494afb) ) // I/O PROM

	ROM_REGION( 0x20, "proms", 0 )
	ROM_LOAD( "82s129.ue2", 0x00, 0x20, NO_DUMP ) // Address Decoder/Timer for RAM
	ROM_LOAD( "82s129.uf3", 0x00, 0x20, NO_DUMP ) // System Decoder
	ROM_LOAD( "82s123.ub3", 0x00, 0x20, NO_DUMP ) // ROM/PROM Decoder
	ROM_LOAD( "82s129.uf8", 0x00, 0x20, NO_DUMP ) // CPU & Horizontal Decoder
	ROM_LOAD( "82s129.ug9", 0x00, 0x20, NO_DUMP ) // Scan Decoder
	ROM_LOAD( "82s129.ug5", 0x00, 0x20, NO_DUMP ) // Color PROM
ROM_END

} // anonymous namespace


COMP( 1977, compclr2, 0, 0, compucolor2, compucolor2, compucolor2_state, empty_init, "Intelligent Systems Corporation", "Compucolor II", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



computachess.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

CXG Sensor Computachess (CXG-001 or WA-001)
CXG Portachess, Portachess II, Computachess IV, Sphinx Chess Voyager

Sensor Computachess is White and Allcock's first original chess computer.
Cassia's Chess Mate (aka Computachess) doesn't really count since it was a
bootleg of Fidelity Chess Challenger 10.

It was programmed by Intelligent Software (formerly known as Philidor Software).
After loosening ties with SciSys, Intelligent Software provided the software for
various chess computer companies. The chess engine is by Mark Taylor, it's the
same one as in Mini Chess released by SciSys earlier that year.

Initially, it had a "Sound" button for turning the beeps off. This was later
changed to the more useful "New Game". With Portachess, they added a "Save"
switch which puts the MCU in halt state.

Hardware notes:

Sensor Computachess:
- PCB label: WA 001 600 002
- Hitachi 44801A50 MCU @ ~400kHz
- buzzer, 16 leds, button sensors chessboard

Portachess II:
- PCB label: CXG223-600-001 (main pcb), CXG 211 600 101 (led pcb taken from
  Advanced Star Chess, extra led row unused here)
- Hitachi HD44801C89 MCU @ ~400kHz (serial 202: from Portachess 1985 version)
- rest same as above

HD44801A50 MCU is used in:
- CXG Sensor Computachess (1981 version) - 1st use
- CXG Portachess (1983 version, has "Sound" button)
- CGL GrandMaster Travel Sensory
- Hanimex HCG 1500
- Schneider Sensor Chesspartner MK 3
- Systema Computachess
- Tandy Computerized 8-Level Beginner Sensory Chess

HD44801C89 MCU is used in:
- CXG Portachess (1985 version, "NEW 16 LEVELS") - 1st use
- CXG Sensor Computachess (1985 rerelease, "NEW 16 LEVELS")
- CXG Portachess II (1986)
- CXG Computachess IV (1986)
- CXG Sphinx Chess Voyager? (1992)
- Fidelity Computachess IV
- Fidelity Mini Chess Challenger (same housing as Portachess II)
- Schneider MK 7 (same housing as Portachess II)
- Schneider Sensor Chessmaster MK 6
- Schneider Sensor Chesspartner MK 4

Computachess II has a HD44840 MCU (see computachess2.cpp). Computachess III has
a HD6301V1 MCU and should be the same as Enterprise "S" (see saitek/companion2.cpp).

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

#include "emu.h"

#include "cpu/hmcs40/hmcs40.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "cxg_scpchess.lh"
#include "cxg_scpchessa.lh"


namespace {

class cpchess_state : public driver_device
{
public:
	cpchess_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.0")
	{ }

	void scpchess(machine_config &config);
	void scpchessa(machine_config &config);

	// New Game button is directly tied to MCU reset
	DECLARE_INPUT_CHANGED_MEMBER(reset_button) { m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE); }

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<hmcs40_cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport m_inputs;

	u8 m_inp_mux = 0;

	template<int N> void mux_w(u8 data);
	void control_w(u16 data);
	u16 input_r();
};

void cpchess_state::machine_start()
{
	save_item(NAME(m_inp_mux));
}



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

template<int N>
void cpchess_state::mux_w(u8 data)
{
	// R2x,R3x: input mux, led data
	const u8 shift = N * 4;
	m_inp_mux = (m_inp_mux & ~(0xf << shift)) | (data << shift);
	m_display->write_mx(m_inp_mux);
}

void cpchess_state::control_w(u16 data)
{
	// D0: speaker out
	m_dac->write(~data & 1);

	// D2,D3: led select
	m_display->write_my(~data >> 2 & 3);
}

u16 cpchess_state::input_r()
{
	u16 data = 0;

	// D7: read buttons
	if (m_inp_mux & m_inputs->read())
		data |= 0x80;

	// D8-D15: read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_file(i ^ 7) << 8;

	return ~data;
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( scpchess )
	PORT_START("IN.0")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Reverse Play")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cpchess_state::reset_button), 0) PORT_NAME("New Game")
INPUT_PORTS_END

static INPUT_PORTS_START( scpchessa )
	PORT_INCLUDE( scpchess )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Sound") // only hooked up on 1st version

	PORT_MODIFY("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void cpchess_state::scpchess(machine_config &config)
{
	// basic machine hardware
	HD44801(config, m_maincpu, 400'000); // approximation
	m_maincpu->write_r<2>().set(FUNC(cpchess_state::mux_w<0>));
	m_maincpu->write_r<3>().set(FUNC(cpchess_state::mux_w<1>));
	m_maincpu->write_d().set(FUNC(cpchess_state::control_w));
	m_maincpu->read_d().set(FUNC(cpchess_state::input_r));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(2, 8);
	config.set_default_layout(layout_cxg_scpchess);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void cpchess_state::scpchessa(machine_config &config)
{
	scpchess(config);

	m_maincpu->write_d().set(FUNC(cpchess_state::control_w)).exor(1);
	config.set_default_layout(layout_cxg_scpchessa);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( scpchess )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD("202_newcrest_16_hd44801c89", 0x0000, 0x2000, CRC(56b48f70) SHA1(84ec62323c6d3314e0515bccfde2f65f6d753e99) )
ROM_END

ROM_START( scpchessa )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD("white_allcock_44801a50", 0x0000, 0x2000, CRC(c5c53e05) SHA1(8fa9b8e48ca54f08585afd83ae78fb1970fbd382) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT    COMPAT  MACHINE    INPUT      CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1985, scpchess,  0,        0,      scpchess,  scpchess,  cpchess_state, empty_init, "CXG Systems / Newcrest Technology / Intelligent Chess Software", "Sensor Computachess (1985 version)", MACHINE_SUPPORTS_SAVE )
SYST( 1981, scpchessa, scpchess, 0,      scpchessa, scpchessa, cpchess_state, empty_init, "CXG Systems / White and Allcock / Intelligent Software", "Sensor Computachess (1981 version)", MACHINE_SUPPORTS_SAVE )



computachess2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

CXG Computachess II (CXG-002 or WA-002)

It's the sequel to Sensor Computachess, on similar hardware. The chess engine is
again by Intelligent Software.

Hardware notes:
- PCB label: W&A 002B-600-003
- Hitachi 44840A14 MCU @ ~650kHz (62K resistor)
- buzzer, 16 leds, button sensors chessboard

There's also an older revision (002 600 002 PCB, separate LED PCB), the rest of
the hardware is the same. Seen with either A13 or A14 MCU.

HD44840A13/A14 MCU is used in:
- CXG Computachess II
- CXG Advanced Portachess (white or red version)
- CGL Computachess 2
- CGL Grandmaster Sensory 2
- CGL Computachess Travel Sensory
- Hanimex Computachess II (HCG 1600)
- Hanimex Computachess III (HCG 1700)
- Schneider Sensor Chessmaster MK 5

It's not known yet how to differentiate between the 2 program revisions. Versions
with a higher serial number are more likely to have the A14 MCU.

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

#include "emu.h"

#include "cpu/hmcs40/hmcs40.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "cxg_cpchess2.lh"


namespace {

class cpchess2_state : public driver_device
{
public:
	cpchess2_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void cpchess2(machine_config &config);

	// New Game button is directly tied to MCU reset
	DECLARE_INPUT_CHANGED_MEMBER(reset_button) { m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE); }

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<hmcs40_cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<2> m_inputs;

	u8 m_inp_mux = 0;

	template<int N> void mux_w(u8 data);
	void control_w(u16 data);
	u16 input_r();
};

void cpchess2_state::machine_start()
{
	save_item(NAME(m_inp_mux));
}



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

template<int N>
void cpchess2_state::mux_w(u8 data)
{
	// R2x,R3x: input mux, led data
	const u8 shift = N * 4;
	m_inp_mux = (m_inp_mux & ~(0xf << shift)) | ((data ^ 0xf) << shift);
	m_display->write_mx(m_inp_mux);
}

void cpchess2_state::control_w(u16 data)
{
	// D4: speaker out
	m_dac->write(BIT(data, 4));

	// D2,D3: led select
	m_display->write_my(~data >> 2 & 3);
}

u16 cpchess2_state::input_r()
{
	u16 data = 0;

	// D6,D7: read buttons
	for (int i = 0; i < 2; i++)
		if (m_inp_mux & m_inputs[i]->read())
			data |= 0x40 << i;

	// D8-D15: read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_file(i ^ 7) << 8;

	return ~data;
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( cpchess2 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.1")
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Reverse Play")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Sound")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cpchess2_state::reset_button), 0) PORT_NAME("New Game")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void cpchess2_state::cpchess2(machine_config &config)
{
	// basic machine hardware
	HD44840(config, m_maincpu, 650'000); // approximation
	m_maincpu->write_r<2>().set(FUNC(cpchess2_state::mux_w<0>));
	m_maincpu->write_r<3>().set(FUNC(cpchess2_state::mux_w<1>));
	m_maincpu->write_d().set(FUNC(cpchess2_state::control_w));
	m_maincpu->read_d().set(FUNC(cpchess2_state::input_r));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(2, 8);
	config.set_default_layout(layout_cxg_cpchess2);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( cpchess2 )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("1982_nc201_newcrest_44840a14", 0x0000, 0x4000, CRC(c3d9c1e0) SHA1(4185b717a3b6fe916cc438fbdce35dcf32cab825) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1982, cpchess2, 0,      0,      cpchess2, cpchess2, cpchess2_state, empty_init, "CXG Systems / White and Allcock / Intelligent Software", "Computachess II", MACHINE_SUPPORTS_SAVE )



comquest.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Peter Trauner
/******************************************************************************
 Peter.Trauner@jk.uni-linz.ac.at September 2000

******************************************************************************/
/*
comquest plus
-------------
team concepts

laptop for childs

language german

lcd black/white, about 128x64, manual contrast control
keyboard and 2 button joypad
speaker 2, manual volume control:2 levels
cartridge slot, serial port

512 kbyte rom on print with little isolation/case
12 pin chip on print with little isolation/case (eeprom? at i2c bus)
cpu on print, soldered so nothing visible
32 kbyte sram

compuest a4 power printer
-------------------------
line oriented ink printer (12 pixel head)
for comquest serial port

3 buttons, 2 leds
bereit
druckqualitaet
zeilenvorschub/seitenvorschub

only chip on board (40? dil)
lsc43331op
team concepts
icq3250a-d
1f71lctctab973

*/

#include "emu.h"

#include "cpu/m6805/m68hc05.h"

#include "emupal.h"
#include "screen.h"


namespace {

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

	void comquest(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;

	//uint8_t m_data[128][8];

	[[maybe_unused]] uint8_t comquest_read(offs_t offset);
	[[maybe_unused]] void comquest_write(offs_t offset, uint8_t data);

	uint32_t screen_update_comquest(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void comquest_mem(address_map &map) ATTR_COLD;
};


uint8_t comquest_state::comquest_read(offs_t offset)
{
	uint8_t data=0;
	logerror("comquest read %.4x %.2x\n",offset,data);
	return data;
}

void comquest_state::comquest_write(offs_t offset, uint8_t data)
{
	logerror("comquest write %.4x %.2x\n",offset,data);
}

uint32_t comquest_state::screen_update_comquest(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (int y = 0; y < 128; y++) {
		for (int x = 0, j = 0; j < 8; j++, x += 8 * 4) {
#if 0
			m_gfxdecode->gfx(0)->opaque(bitmap,0, state->m_data[y][j],0,
					0,0,x,y);
#endif
		}
	}
	return 0;
}


void comquest_state::comquest_mem(address_map &map)
{
	map(0x8000, 0xffff).rom().region("gfx1", 0x4000);
}

static INPUT_PORTS_START( comquest )
	PORT_START("in0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("EIN")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F6") PORT_CODE(KEYCODE_F6)
	PORT_START("in1")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F7") PORT_CODE(KEYCODE_F7)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F8") PORT_CODE(KEYCODE_F8)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F9") PORT_CODE(KEYCODE_F9)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Druck") PORT_CODE(KEYCODE_PRTSCR)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("AUS")
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1          !") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2          \"") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3          Paragraph") PORT_CODE(KEYCODE_3)
	PORT_START("in2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4          $") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5          %%") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6          &") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7          /") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8          (") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9          )") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0          =") PORT_CODE(KEYCODE_0)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("sharp-s    ?") PORT_CODE(KEYCODE_EQUALS)
	PORT_START("in3")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("acute") PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("delete") PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("tab") PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T          +") PORT_CODE(KEYCODE_T)
	PORT_START("in4")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z          -") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U          4") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I          5") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O          6") PORT_CODE(KEYCODE_O)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Diaresis-U") PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+          *") PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("capslock") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_START("in5")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G          mul") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H          div") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J          1") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K          2") PORT_CODE(KEYCODE_K)
	PORT_START("in6")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L          3") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Diaresis-O")
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Diaresis-A")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("left-shift") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
	PORT_START("in7")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B          root") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N          square") PORT_CODE(KEYCODE_N)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(",          ;") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(".          :") PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("-          _") PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("right-shift") PORT_CODE(KEYCODE_RSHIFT)
	PORT_START("in8")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Entf") PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Strg") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Alt") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("AC")
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Spieler    left") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Stufe      up") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Antwort    down") PORT_CODE(KEYCODE_DOWN)
	PORT_START("in9")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("           right") PORT_CODE(KEYCODE_RIGHT)
#if 0
/*
  left button, right button
  joypad:
  left button, right button
  4 or 8 directions
*/
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("") PORT_CODE(KEYCODE_7)
#endif

INPUT_PORTS_END

static const gfx_layout comquest_charlayout =
{
		8,8,
		256*8,                                    /* 256 characters */
		1,                      /* 1 bits per pixel */
		{ 0 },                  /* no bitplanes; 1 bit per pixel */
		/* x offsets */
		{
			0,
			1,
			2,
			3,
			4,
			5,
			6,
			7,
		},
		/* y offsets */
		{
			0,
			8,
			16,
			24,
			32,
			40,
			48,
			56,
		},
		8*8
};

static GFXDECODE_START( gfx_comquest )
	GFXDECODE_ENTRY( "gfx1", 0x0000, comquest_charlayout, 0, 2 )
GFXDECODE_END


void comquest_state::machine_reset()
{
//  uint8_t *mem=memregion("user1")->base();
//  membank(1)->set_base(mem+0x00000);
}


void comquest_state::comquest(machine_config &config)
{
	/* basic machine hardware */
	M68HC05L11(config, m_maincpu, 4000000);     /* 4000000? */

/*
    8 bit bus, integrated io, serial io?,

    starts at address zero?

    not saturn, although very similar hardware compared to hp48g (build into big plastic case)
    not sc61860, 62015?
    not cdp1802
    not tms9900?
    not z80
    not 6502, mitsubishi 740
    not i86
    not 6809
    not 68008?
    not tms32010
    not t11
    not arm
    not 8039
    not tms370
    not lh5801
    not fujitsu mb89150
    not epson e0c88
*/

	m_maincpu->set_addrmap(AS_PROGRAM, &comquest_state::comquest_mem);


	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(30);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(64*4, 128); /* 160 x 102 */
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(comquest_state::screen_update_comquest));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_comquest);
	PALETTE(config, "palette", palette_device::MONOCHROME);


	/* sound hardware */
	/* unknown ? */
}

ROM_START(comquest)
	ROM_REGION(0x1000,"maincpu",0)
	ROM_LOAD("hc05_internal.bin", 0x0000, 0x1000, NO_DUMP)
/*
000 +16kbyte graphics data? (first bytes: 80 0d 04 00 00 08 04 00 0f 02 04 01 00 10 04 01)
040 16kbyte code (first bytes: 00 00 00 00 9a cd 7c 9b cd 7c 98 4f c7 f1 1d 4f)
080 8kbyte code (first bytes: 00 00 00 00 9a cd 7c 9b cd 7c 98 10 a9 4f c7 f1)
0a0 8kbyte code (first bytes: 00 00 00 00 9a a6 00 c7 f5 ca cd 7c 9b cd 7c 98)
0c0 16kbyte code (first bytes: 00 00 00 00 a6 0a cd 7c c8 cd 7c 9b cd 7c 98 a6)
100 8kbyte code (first bytes: 00 00 00 00 a6 0a cd 7c c8 cd 7c 9b cd 7c 98 a6)
120 8kbyte data
140 16kbyte code (first bytes: 00 00 00 00 9a cd 7c 9b cd 7c 98 a6 0d c7 fd 0f)
180 16kbyte code (first bytes: 00 00 00 00 9a cd 7c 9b cd 7c 98 a6 00 c7 f2 2c)
1c0 16kbyte code (first bytes: 00 00 00 00 9a a6 00 c7 f3 00 c7 f3 02 a6 ff c7)
200 16kbyte code (first bytes: 00 00 00 00 9a a6 0d c7 fd 0f cd 7c 92 cd 7c 9b)
240 8kbyte code  (first bytes: 00 00 00 00 9d 9d 9d 9d 9d 9d 9d 9d cd 7c 9b cd)
260 16kbyte code (first bytes: 00 00 00 00 9a cd 7c 9b cd 7c 98 a6 00 b7 75 b7)
2a0 8kbyte code  (first bytes: 00 00 00 00 a6 0a cd 7c c8 cd 7c 9b cd 7c 98 a6)
2c0 8kbyte code  (first bytes: 00 00 00 00 a6 03 c7 fd 0f cd 7c 92 cd 7c 9b cd)
2e0 8kbyte code? (first bytes: 00 00 00 00 a6 0d c7 fd 0f cd 7c 92 cc 80 f1 20)
300 luts?+text   (first bytes: 60 02 fb 08 9a 11 a1 1f 75 26 dc 2f 6a 3b 26 46)
720 empty
740 empty
760 empty
780 16kbyte code (first bytes: 00 00 00 00 cd 7c 9b cd 7c 98 4f cd 7c ad a6 0a)
7c0 16kbyte code (first bytes: 00 00 00 00 9a cd 7c 9b cd 7c 98 4f c7 f1 6e c7)
 */

//  ROM_REGION(0x100,"gfx1",0)
	ROM_REGION(0x80000,"gfx1",0)
	ROM_LOAD("comquest.bin", 0x00000, 0x80000, CRC(2bf4b1a8) SHA1(8d1821cbde37cca2055b18df001438f7d138a8c1))
ROM_END

} // anonymous namespace


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

  Game driver(s)

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

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY          FULLNAME                  FLAGS
CONS( 1995, comquest, 0,      0,      comquest, comquest, comquest_state, empty_init, "Data Concepts", "ComQuest Plus (German)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



comx35.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

    TODO:

    - tape input/output
    - PL-80 plotter
    - serial printer
    - thermal printer


    Cassette: PSAVE works, and the result can be loaded into Emma02 emulator.
              PLOAD works, but it may be necessary to unplug all slots to get
              a reliable load. This is the same as real hardware.

*/

#include "emu.h"
#include "comx35.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "multibyte.h"
#include "utf8.h"

/***************************************************************************
    PARAMETERS
***************************************************************************/

#define LOG 0

enum
{
	COMX_TYPE_BINARY = 1,
	COMX_TYPE_BASIC,
	COMX_TYPE_BASIC_FM,
	COMX_TYPE_RESERVED,
	COMX_TYPE_DATA
};

/***************************************************************************
    IMPLEMENTATION
***************************************************************************/

/*-------------------------------------------------
    image_fread_memory - read image to memory
-------------------------------------------------*/

void comx35_state::image_fread_memory(device_image_interface &image, uint16_t addr, uint32_t count)
{
	uint8_t *ram = m_ram->pointer() + (addr - 0x4000);

	image.fread(ram, count);
}

/*-------------------------------------------------
    QUICKLOAD_LOAD_MEMBER( comx35_state, comx )
-------------------------------------------------*/

QUICKLOAD_LOAD_MEMBER(comx35_state::quickload_cb)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	uint8_t header[16] = {0};
	int size = image.length();

	if (size > m_ram->size())
		return std::make_pair(image_error::INVALIDLENGTH, "Image is larger than RAM");

	image.fread( header, 5);

	if (header[1] != 'C' || header[2] != 'O' || header[3] != 'M' || header[4] != 'X' )
		return std::make_pair(image_error::INVALIDIMAGE, std::string());

	switch (header[0])
	{
	case COMX_TYPE_BINARY:
		/*

		    Type 1: pure machine code (i.e. no basic)

		    Byte 0 to 4: 1 - 'COMX'
		    Byte 5 and 6: Start address (1802 way; see above)
		    Byte 6 and 7: End address
		    Byte 9 and 10: Execution address

		    Byte 11 to Eof, should be stored in ram from start to end; execution address
		    'xxxx' for the CALL (@xxxx) basic statement to actually run the code.

		*/
		{
			uint16_t start_address, end_address, run_address;

			image.fread(header, 6);

			start_address = get_u16be(&header[0]);
			end_address = get_u16be(&header[2]);
			run_address = get_u16be(&header[4]);

			image_fread_memory(image, start_address, end_address - start_address);

			popmessage("Type CALL (@%04x) to start program", run_address);
		}
		break;

	case COMX_TYPE_BASIC:
		/*

		    Type 2: Regular basic code or machine code followed by basic

		    Byte 0 to 4: 2 - 'COMX'
		    Byte 5 and 6: DEFUS value, to be stored on 0x4281 and 0x4282
		    Byte 7 and 8: EOP value, to be stored on 0x4283 and 0x4284
		    Byte 9 and 10: End array, start string to be stored on 0x4292 and 0x4293
		    Byte 11 and 12: start array to be stored on 0x4294 and 0x4295
		    Byte 13 and 14: EOD and end string to be stored on 0x4299 and 0x429A

		    Byte 15 to Eof to be stored on 0x4400 and onwards

		    Byte 0x4281-0x429A (or at least the ones above) should be set otherwise
		    BASIC won't 'see' the code.

		*/

		image_fread_memory(image, 0x4281, 4);
		image_fread_memory(image, 0x4292, 4);
		image_fread_memory(image, 0x4299, 2);
		image_fread_memory(image, 0x4400, size);
		break;

	case COMX_TYPE_BASIC_FM:
		/*

		    Type 3: F&M basic load

		    Not the most important! But we designed our own basic extension, you can
		    find it in the F&M basic folder as F&M Basic.comx. When you run this all
		    basic code should start at address 0x6700 instead of 0x4400 as from
		    0x4400-0x6700 the F&M basic stuff is loaded. So format is identical to Type
		    2 except Byte 15 to Eof should be stored on 0x6700 instead. .comx files of
		    this format can also be found in the same folder as the F&M basic.comx file.

		*/

		image_fread_memory(image, 0x4281, 4);
		image_fread_memory(image, 0x4292, 4);
		image_fread_memory(image, 0x4299, 2);
		image_fread_memory(image, 0x6700, size);
		break;

	case COMX_TYPE_RESERVED:
		/*

		    Type 4: Incorrect DATA format, I suggest to forget this one as it won't work
		    in most cases. Instead I left this one reserved and designed Type 5 instead.

		*/
		break;

	case COMX_TYPE_DATA:
		/*

		    Type 5: Data load

		    Byte 0 to 4: 5 - 'COMX'
		    Byte 5 and 6: Array length
		    Byte 7 to Eof: Basic 'data'

		    To load this first get the 'start array' from the running COMX, i.e. address
		    0x4295/0x4296. Calculate the EOD as 'start array' + length of the data (i.e.
		    file length - 7). Store the EOD back on 0x4299 and ox429A. Calculate the
		    'Start String' as 'start array' + 'Array length' (Byte 5 and 6). Store the
		    'Start String' on 0x4292/0x4293. Load byte 7 and onwards starting from the
		    'start array' value fetched from 0x4295/0x4296.

		*/
		{
			uint16_t start_array, end_array, start_string, array_length;

			image.fread(header, 2);

			array_length = get_u16be(&header[0]);
			start_array = (program.read_byte(0x4295) << 8) | program.read_byte(0x4296);
			end_array = start_array + (size - 7);

			program.write_byte(0x4299, end_array >> 8);
			program.write_byte(0x429a, end_array & 0xff);

			start_string = start_array + array_length;

			program.write_byte(0x4292, start_string >> 8);
			program.write_byte(0x4293, start_string & 0xff);

			image_fread_memory(image, start_array, size);
		}
		break;
	}

	return std::make_pair(std::error_condition(), std::string());
}

//**************************************************************************
//  MEMORY ACCESS
//**************************************************************************

//-------------------------------------------------
//  mem_r - memory read
//-------------------------------------------------

uint8_t comx35_state::mem_r(offs_t offset)
{
	int extrom = 1;

	uint8_t data = m_exp->mrd_r(offset, &extrom);

	if (offset < 0x4000)
	{
		if (extrom) data = m_rom->base()[offset & 0x3fff];
	}
	else if (offset >= 0x4000 && offset < 0xc000)
	{
		data = m_ram->pointer()[offset - 0x4000];
	}
	else if (offset >= 0xf400 && offset < 0xf800)
	{
		data = m_vis->char_ram_r(offset & 0x3ff);
	}

	return data;
}


//-------------------------------------------------
//  mem_w - memory write
//-------------------------------------------------

void comx35_state::mem_w(offs_t offset, uint8_t data)
{
	m_exp->mwr_w(offset, data);

	if (offset >= 0x4000 && offset < 0xc000)
	{
		m_ram->pointer()[offset - 0x4000] = data;
	}
	else if (offset >= 0xf400 && offset < 0xf800)
	{
		m_vis->char_ram_w(offset & 0x3ff, data);
	}
	else if (offset >= 0xf800)
	{
		m_vis->page_ram_w(offset & 0x3ff, data);
	}
}


//-------------------------------------------------
//  io_r - I/O read
//-------------------------------------------------

uint8_t comx35_state::io_r(offs_t offset)
{
	uint8_t data = m_exp->io_r(offset);

	if (offset == 3)
	{
		data = m_kbe->read();
	}

	return data;
}


//-------------------------------------------------
//  io_w - I/O write
//-------------------------------------------------

void comx35_state::io_w(offs_t offset, uint8_t data)
{
	m_exp->io_w(offset, data);

	if (offset >= 3)
	{
		cdp1869_w(offset, data);
	}
}



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

//-------------------------------------------------
//  ADDRESS_MAP( comx35_mem )
//-------------------------------------------------

void comx35_state::comx35_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(comx35_state::mem_r), FUNC(comx35_state::mem_w));
}


//-------------------------------------------------
//  ADDRESS_MAP( comx35_io )
//-------------------------------------------------

void comx35_state::comx35_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x07).rw(FUNC(comx35_state::io_r), FUNC(comx35_state::io_w));
}



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

//-------------------------------------------------
//  INPUT_CHANGED_MEMBER( comx35_reset )
//-------------------------------------------------

INPUT_CHANGED_MEMBER( comx35_state::trigger_reset )
{
	if (newval && BIT(m_d6->read(), 7))
	{
		machine_reset();
	}
}


//-------------------------------------------------
//  INPUT_PORTS( comx35 )
//-------------------------------------------------

static INPUT_PORTS_START( comx35 )
	PORT_START("D1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("0 \xE2\x96\xA0") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('@')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR(39)

	PORT_START("D2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('[')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(']')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(':')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR(';')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('<') PORT_CHAR('(')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('=') PORT_CHAR('^')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('>') PORT_CHAR(')')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('\\') PORT_CHAR('_')

	PORT_START("D3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('?')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')

	PORT_START("D4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')

	PORT_START("D5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')

	PORT_START("D6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR('+')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('-')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('*')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("D7")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("D8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CR") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DEL") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("D9")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("D10")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("D11")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("MODIFIERS")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PORT_WRITE_LINE_DEVICE_MEMBER(CDP1871_TAG, FUNC(cdp1871_device::shift_w))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CNTL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_CHAR(UCHAR_MAMEKEY(RCONTROL)) PORT_WRITE_LINE_DEVICE_MEMBER(CDP1871_TAG, FUNC(cdp1871_device::control_w))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("RESET")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RT") PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(comx35_state::trigger_reset), 0)
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  COSMAC_INTERFACE( cosmac_intf )
//-------------------------------------------------

void comx35_state::check_interrupt()
{
	m_maincpu->set_input_line(COSMAC_INPUT_LINE_INT, m_cr1 || m_int);
}

int comx35_state::clear_r()
{
	return m_clear;
}

int comx35_state::ef2_r()
{
	if (m_iden)
	{
		// interrupts disabled: PAL/NTSC
		return m_vis->pal_ntsc_r();
	}
	else
	{
		// interrupts enabled: keyboard repeat
		return m_kbe->rpt_r();
	}
}

int comx35_state::ef4_r()
{
	return m_exp->ef4_r() | ((m_cassette->input() > 0.0f) ? 1 : 0);
}

void comx35_state::q_w(int state)
{
	m_q = state;

	if (m_iden && state)
	{
		// enable interrupts
		m_iden = 0;
	}

	// cassette output
	m_cassette->output(state ? +1.0 : -1.0);

	// expansion bus
	m_exp->q_w(state);
}

void comx35_state::sc_w(uint8_t data)
{
	switch (data)
	{
	case COSMAC_STATE_CODE_S0_FETCH:
		// not connected
		break;

	case COSMAC_STATE_CODE_S1_EXECUTE:
		// every other S1 triggers a DMAOUT request
		if (m_dma)
		{
			m_dma = 0;

			if (!m_iden)
			{
				m_maincpu->set_input_line(COSMAC_INPUT_LINE_DMAOUT, ASSERT_LINE);
			}
		}
		else
		{
			m_dma = 1;
		}
		break;

	case COSMAC_STATE_CODE_S2_DMA:
		// DMA acknowledge clears the DMAOUT request
		m_maincpu->set_input_line(COSMAC_INPUT_LINE_DMAOUT, CLEAR_LINE);
		break;

	case COSMAC_STATE_CODE_S3_INTERRUPT:
		// interrupt acknowledge clears the INT request
		m_maincpu->set_input_line(COSMAC_INPUT_LINE_INT, CLEAR_LINE);
		break;
	}
}


//-------------------------------------------------
//  COMX_EXPANSION_INTERFACE( expansion_intf )
//-------------------------------------------------

void comx35_state::irq_w(int state)
{
	m_int = state;
	check_interrupt();
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  reset_done - flag that reset is complete
//-------------------------------------------------

TIMER_CALLBACK_MEMBER(comx35_state::reset_done)
{
	m_clear = 1;
}


//-------------------------------------------------
//  machine_start
//-------------------------------------------------

void comx35_state::machine_start()
{
	// clear the RAM since DOS card will go crazy if RAM is not all zeroes
	uint8_t *ram = m_ram->pointer();
	memset(ram, 0, m_ram->size());

	// register for state saving
	save_item(NAME(m_clear));
	save_item(NAME(m_q));
	save_item(NAME(m_iden));
	save_item(NAME(m_dma));
	save_item(NAME(m_int));
	save_item(NAME(m_prd));
	save_item(NAME(m_cr1));

	m_reset_done_timer = timer_alloc(FUNC(comx35_state::reset_done), this);
}


void comx35_state::machine_reset()
{
	m_exp->reset();

	int t = RES_K(27) * CAP_U(1) * 1000; // t = R1 * C1

	m_clear = 0;
	m_iden = 1;
	m_cr1 = 1;
	m_int = CLEAR_LINE;
	m_prd = CLEAR_LINE;

	m_reset_done_timer->adjust(attotime::from_msec(t));
}



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

//-------------------------------------------------
//  machine_config( pal )
//-------------------------------------------------

void comx35_state::base(machine_config &config, const XTAL clock)
{
	// basic system hardware
	CDP1802(config, m_maincpu, clock);
	m_maincpu->set_addrmap(AS_PROGRAM, &comx35_state::comx35_mem);
	m_maincpu->set_addrmap(AS_IO, &comx35_state::comx35_io);
	m_maincpu->wait_cb().set_constant(1);
	m_maincpu->clear_cb().set(FUNC(comx35_state::clear_r));
	m_maincpu->ef2_cb().set(FUNC(comx35_state::ef2_r));
	m_maincpu->ef4_cb().set(FUNC(comx35_state::ef4_r));
	m_maincpu->q_cb().set(FUNC(comx35_state::q_w));
	m_maincpu->sc_cb().set(FUNC(comx35_state::sc_w));
	m_maincpu->sc_cb().append(EXPANSION_TAG, FUNC(comx_expansion_slot_device::sc_w));
	m_maincpu->tpb_cb().set(EXPANSION_TAG, FUNC(comx_expansion_slot_device::tpb_w));

	// peripheral hardware
	CDP1871(config, m_kbe, cdp1869_device::CPU_CLK_PAL/8);
	m_kbe->d1_callback().set_ioport("D1");
	m_kbe->d2_callback().set_ioport("D2");
	m_kbe->d3_callback().set_ioport("D3");
	m_kbe->d4_callback().set_ioport("D4");
	m_kbe->d5_callback().set_ioport("D5");
	m_kbe->d6_callback().set_ioport("D6");
	m_kbe->d7_callback().set_ioport("D7");
	m_kbe->d8_callback().set_ioport("D8");
	m_kbe->d9_callback().set_ioport("D9");
	m_kbe->d10_callback().set_ioport("D10");
	m_kbe->d11_callback().set_ioport("D11");
	m_kbe->da_callback().set_inputline(m_maincpu, COSMAC_INPUT_LINE_EF3);

	QUICKLOAD(config, "quickload", "comx").set_load_callback(FUNC(comx35_state::quickload_cb));

	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cassette).set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	// expansion bus
	COMX_EXPANSION_SLOT(config, m_exp, 0, comx_expansion_cards, "eb").irq_callback().set(FUNC(comx35_state::irq_w));

	// internal ram
	RAM(config, m_ram).set_default_size("32K");

	// software lists
	SOFTWARE_LIST(config, "flop_list").set_original("comx35_flop");
}

void comx35_state::pal(machine_config &config)
{
	base(config, cdp1869_device::CPU_CLK_PAL);
	comx35_pal_video(config);
}

void comx35_state::ntsc(machine_config &config)
{
	base(config, cdp1869_device::CPU_CLK_NTSC);
	comx35_ntsc_video(config);
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( comx35p )
//-------------------------------------------------

ROM_START( comx35p )
	ROM_REGION( 0x4000, CDP1802_TAG, 0 )
	ROM_DEFAULT_BIOS( "basic100" )
	ROM_SYSTEM_BIOS( 0, "basic100", "COMX BASIC V1.00" )
	ROMX_LOAD( "comx_10.u21", 0x0000, 0x4000, CRC(68d0db2d) SHA1(062328361629019ceed9375afac18e2b7849ce47), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "basic101", "COMX BASIC V1.01" )
	ROMX_LOAD( "comx_11.u21", 0x0000, 0x4000, CRC(609d89cd) SHA1(799646810510d8236fbfafaff7a73d5170990f16), ROM_BIOS(1) )
ROM_END


//-------------------------------------------------
//  ROM( comx35n )
//-------------------------------------------------

#define rom_comx35n rom_comx35p



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY                      FULLNAME          FLAGS
COMP( 1983, comx35p, 0,       0,      pal,     comx35, comx35_state, empty_init, "Comx World Operations Ltd", "COMX 35 (PAL)",  MACHINE_IMPERFECT_SOUND )
COMP( 1983, comx35n, comx35p, 0,      ntsc,    comx35, comx35_state, empty_init, "Comx World Operations Ltd", "COMX 35 (NTSC)", MACHINE_IMPERFECT_SOUND )



concept.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Raphael Nabet, Brett Wyer
/*
    Corvus Concept driver

    Relatively simple 68k-based system

    * 256 or 512 kbytes of DRAM
    * 4kbytes of SRAM
    * 8kbyte boot ROM
    * optional MacsBugs ROM
    * two serial ports, keyboard, bitmapped display, simple sound, omninet
      LAN port (seems more or less similar to AppleTalk)
    * 4 expansion ports enable to add expansion cards, namely floppy disk
      and hard disk controllers (the expansion ports are partially compatible
      with Apple 2 expansion ports; DMA is not supported)

    Video: monochrome bitmapped display, 720*560 visible area (bitmaps are 768
      pixels wide in memory).  One interesting feature is the fact that the
      monitor can be rotated to give a 560*720 vertical display (you need to
      throw a switch and reset the machine for the display rotation to be taken
      into account, though).  One oddity is that the video hardware scans the
      display from the lower-left corner to the upper-left corner (or from the
      upper-right corner to the lower-left if the screen is flipped).
    Sound: simpler buzzer connected to the via shift register
    Keyboard: intelligent controller, connected through an ACIA.  See CCOS
      manual pp. 76 through 78. and User Guide p. 2-1 through 2-9.
    Clock: mm58174 RTC

    Raphael Nabet, Brett Wyer, 2003-2005
    Major reworking by R. Belmont 2012-2013 resulting in bootable floppies

    ACIA baud rates are 1.36% slower than normal by design. The clock division
    used as the BRG input for all three is about 1.818 MHz, not the standard
    1.8432 MHz. The schematics indicate a PCB option to leave the 74LS161 at
    U212 unpopulated and use a secondary XTAL as the baud rate clock. This
    XTAL is also specified as 1.818 MHz.
*/

#include "emu.h"
#include "concept.h"
#include "concept_kbd.h"

#include "cpu/m68000/m68000.h"
#include "cpu/m6800/m6801.h"
#include "bus/a2bus/a2corvus.h"
#include "bus/a2bus/corvfdc01.h"
#include "bus/a2bus/corvfdc02.h"
#include "bus/rs232/rs232.h"
#include "machine/input_merger.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

void concept_state::concept_memmap(address_map &map)
{
	map(0x000000, 0x000007).rom().region("maincpu", 0x010000);  /* boot ROM mirror */
	map(0x000008, 0x000fff).ram();                                     /* static RAM */
	map(0x010000, 0x011fff).rom().region("maincpu", 0x010000);  /* boot ROM */
	map(0x020000, 0x021fff).rom().region("macsbug", 0x0);       /* macsbugs ROM (optional) */
	map(0x030000, 0x03ffff).rw(FUNC(concept_state::io_r), FUNC(concept_state::io_w)).umask16(0x00ff);    /* I/O space */

	map(0x080000, 0x0fffff).ram().share("videoram"); /* .bankrw(2); */ /* DRAM */
}


static INPUT_PORTS_START( corvus_concept )
	PORT_START("DSW0")  /* port 6: on-board DIP switches */
	PORT_DIPNAME(0x01, 0x00, "Omninet Address bit 0")
	PORT_DIPSETTING(0x00, DEF_STR( Off ))
	PORT_DIPSETTING(0x01, DEF_STR( On ))
	PORT_DIPNAME(0x02, 0x02, "Omninet Address bit 1")
	PORT_DIPSETTING(0x00, DEF_STR( Off ))
	PORT_DIPSETTING(0x02, DEF_STR( On ))
	PORT_DIPNAME(0x04, 0x00, "Omninet Address bit 2")
	PORT_DIPSETTING(0x00, DEF_STR( Off ))
	PORT_DIPSETTING(0x04, DEF_STR( On ))
	PORT_DIPNAME(0x08, 0x00, "Omninet Address bit 3")
	PORT_DIPSETTING(0x00, DEF_STR( Off ))
	PORT_DIPSETTING(0x08, DEF_STR( On ))
	PORT_DIPNAME(0x10, 0x00, "Omninet Address bit 4")
	PORT_DIPSETTING(0x00, DEF_STR( Off ))
	PORT_DIPSETTING(0x10, DEF_STR( On ))
	PORT_DIPNAME(0x20, 0x00, "Omninet Address bit 5")
	PORT_DIPSETTING(0x00, DEF_STR( Off ))
	PORT_DIPSETTING(0x20, DEF_STR( On ))
	PORT_DIPNAME(0xc0, 0x00, "Type of Boot")
	PORT_DIPSETTING(0x00, "Prompt for type of Boot")        // Documentation has 0x00 and 0xc0 reversed per boot PROM
	PORT_DIPSETTING(0x40, "Boot from Omninet")
	PORT_DIPSETTING(0x80, "Boot from Local Disk")
	PORT_DIPSETTING(0xc0, "Boot from Diskette")

#if 0
	PORT_START("DISPLAY")   /* port 7: Display orientation */
	PORT_DIPNAME(0x01, 0x00, "Screen Orientation")
	PORT_DIPSETTING(0x00, "Horizontal")
	PORT_DIPSETTING(0x01, "Vertical")
#endif

INPUT_PORTS_END


void concept_a2_cards(device_slot_interface &device)
{
	device.option_add("fchdd", A2BUS_CORVUS);       // Corvus flat-cable HDD interface (see notes in a2corvus.c)
	device.option_add("fdc01", A2BUS_CORVFDC01);    // Corvus WD1793 floppy controller
	device.option_add("fdc02", A2BUS_CORVFDC02);    // Corvus NEC765 buffered floppy controller
}


void concept_state::corvus_concept(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, 16.364_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &concept_state::concept_memmap);

	M6801(config, "omni", 10_MHz_XTAL / 2).set_disable();

	config.set_maximum_quantum(attotime::from_hz(60));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_video_attributes(VIDEO_UPDATE_BEFORE_VBLANK);
	screen.set_raw(16.364_MHz_XTAL * 2, 944, 0, 720, 578, 0, 560);
	// Horizontal sync is 34.669 kHz; refresh rate is ~50 or ~60 Hz, jumper-selectable
	screen.set_screen_update(FUNC(concept_state::screen_update));
	screen.set_palette("palette");

	/* Is the palette black on white or white on black??? */
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* sound */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);

	/* rtc */
	MM58174(config, m_mm58174, 32.768_kHz_XTAL);

	/* via */
	MOS6522(config, m_via0, 16.364_MHz_XTAL / 16);
	m_via0->readpa_handler().set(FUNC(concept_state::via_in_a));
	m_via0->readpb_handler().set(FUNC(concept_state::via_in_b));
	m_via0->writepa_handler().set(FUNC(concept_state::via_out_a));
	m_via0->writepb_handler().set(FUNC(concept_state::via_out_b));
	m_via0->cb2_handler().set(FUNC(concept_state::via_out_cb2));
	m_via0->irq_handler().set_inputline(m_maincpu, M68K_IRQ_5);

	/* ACIAs */
	MOS6551(config, m_acia0, 16.364_MHz_XTAL / 16);
	m_acia0->set_xtal(16.364_MHz_XTAL / 9);
	m_acia0->txd_handler().set("rs232a", FUNC(rs232_port_device::write_txd));
	m_acia0->irq_handler().set_inputline(m_maincpu, M68K_IRQ_4);

	MOS6551(config, m_acia1, 16.364_MHz_XTAL / 16);
	m_acia1->set_xtal(16.364_MHz_XTAL / 9);
	m_acia1->txd_handler().set("rs232b", FUNC(rs232_port_device::write_txd));
	m_acia1->irq_handler().set_inputline(m_maincpu, M68K_IRQ_2);

	MOS6551(config, m_kbdacia, 16.364_MHz_XTAL / 16);
	m_kbdacia->set_xtal(16.364_MHz_XTAL / 9);
	m_kbdacia->txd_handler().set("keyboard", FUNC(concept_keyboard_device::write_rxd));
	m_kbdacia->dtr_handler().set("keyrxd", FUNC(input_merger_device::in_w<0>)).invert(); // open collector
	m_kbdacia->irq_handler().set_inputline(m_maincpu, M68K_IRQ_6);

	CONCEPT_KEYBOARD(config, "keyboard").txd_callback().set("keyrxd", FUNC(input_merger_device::in_w<1>));

	INPUT_MERGER_ALL_HIGH(config, "keyrxd").output_handler().set(m_kbdacia, FUNC(mos6551_device::write_rxd));

	/* Apple II bus */
	A2BUS(config, m_a2bus, 0).set_space(m_maincpu, AS_PROGRAM);
	m_a2bus->nmi_w().set("iocint", FUNC(input_merger_device::in_w<0>));
	m_a2bus->irq_w().set("iocint", FUNC(input_merger_device::in_w<1>));
	A2BUS_SLOT(config, "sl1", 16.364_MHz_XTAL / 2, m_a2bus, concept_a2_cards, nullptr);
	A2BUS_SLOT(config, "sl2", 16.364_MHz_XTAL / 2, m_a2bus, concept_a2_cards, nullptr);
	A2BUS_SLOT(config, "sl3", 16.364_MHz_XTAL / 2, m_a2bus, concept_a2_cards, nullptr);
	A2BUS_SLOT(config, "sl4", 16.364_MHz_XTAL / 2, m_a2bus, concept_a2_cards, "fdc01");

	INPUT_MERGER_ANY_HIGH(config, "iocint").output_handler().set_inputline(m_maincpu, M68K_IRQ_1);

	/* 2x RS232 ports */
	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_acia0, FUNC(mos6551_device::write_rxd));
	//rs232a.dcd_handler().set("iocint", FUNC(input_merger_device::in_w<2>)).invert();
	//rs232a.dsr_handler().set("iocint", FUNC(input_merger_device::in_w<3>)).invert();
	//rs232a.cts_handler().set("iocint", FUNC(input_merger_device::in_w<4>)).invert();

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_acia1, FUNC(mos6551_device::write_rxd));
	//rs232b.dcd_handler().set("iocint", FUNC(input_merger_device::in_w<5>)).invert();
	//rs232b.dsr_handler().set("iocint", FUNC(input_merger_device::in_w<6>)).invert();
	//rs232b.cts_handler().set("iocint", FUNC(input_merger_device::in_w<7>)).invert();
}


ROM_START( concept )
	ROM_REGION16_BE(0x100000,"maincpu",0)   /* 68k rom and ram */

	// concept boot ROM
	ROM_SYSTEM_BIOS(0, "lvl8", "Level 8" )  // v0?
	ROMX_LOAD("bootl08h", 0x010000, 0x1000, CRC(ee479f51) SHA1(b20ba18564672196076e46507020c6d69a640a2f), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("bootl08l", 0x010001, 0x1000, CRC(acaefd07) SHA1(de0c7eaacaf4c0652aa45e523cebce2b2993c437), ROM_BIOS(0) | ROM_SKIP(1))

	ROM_SYSTEM_BIOS(1, "lvl7", "Level 7" )  // v0? v1?
	ROMX_LOAD("cc07h", 0x010000, 0x1000, CRC(455abac8) SHA1(b12e1580220242d34eafed1b486cebd89e823c8b), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("cc07l", 0x010001, 0x1000, CRC(107a3830) SHA1(0ea12ef13b0d11fcd83b306b3a1bb8014ba910c0), ROM_BIOS(1) | ROM_SKIP(1))

	ROM_SYSTEM_BIOS(2, "lvl6", "Level 6" )  // v0?
	ROMX_LOAD("cc06h", 0x010000, 0x1000, CRC(66b6b259) SHA1(1199a38ef3e94f695e8da6a7c80c6432da3cb80c), ROM_BIOS(2) | ROM_SKIP(1))
	ROMX_LOAD("cc06l", 0x010001, 0x1000, CRC(600940d3) SHA1(c3278bf23b3b1c35ea1e3da48a05e877862a8345), ROM_BIOS(2) | ROM_SKIP(1))

#if 0
	// version 1 lvl 7 release
	ROM_LOAD16_BYTE("bootl17h", 0x010000, 0x1000, CRC(6dd9718f))    // where does this come from?
	ROM_LOAD16_BYTE("bootl17l", 0x010001, 0x1000, CRC(107a3830) SHA1(0ea12ef13b0d11fcd83b306b3a1bb8014ba910c0))
#elif 0
	// version $F lvl 8 (development version found on a floppy disk along with
	// the source code)
	ROM_LOAD16_WORD("cc.prm", 0x010000, 0x2000, CRC(b5a87dab) SHA1(0da59af6cfeeb38672f71731527beac323d9c3d6))
#endif

	ROM_REGION(0x1000, "omni", 0)
	ROM_LOAD("sc85180l_3265-02762.u302", 0x0000, 0x1000, NO_DUMP)

	ROM_REGION(0x400, "proms", 0)
	ROM_LOAD("map04a.bin", 0x000, 0x400, CRC(1ae0db9b) SHA1(cdb6f63bb08072b454b4704e62de51c483ede734) )

	ROM_REGION16_BE(0x2000, "macsbug", 0)
	ROM_LOAD16_BYTE( "mb20h.bin",    0x000000, 0x001000, CRC(aa357112) SHA1(88211e5f59887928c557c27cdea674f48bf8eaf7) )
	ROM_LOAD16_BYTE( "mb20l.bin",    0x000001, 0x001000, CRC(b4b59de9) SHA1(3e8b8b5950b5359203c054f94af1fc5b8f0495b9) )
ROM_END

/*    YEAR  NAME     PARENT  COMPAT  MACHINE         INPUT           CLASS          INIT        COMPANY           FULLNAME */
COMP( 1982, concept, 0,      0,      corvus_concept, corvus_concept, concept_state, empty_init, "Corvus Systems", "Concept" , 0 )



conchess.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger, Mr. Lars
/*******************************************************************************

Conchess, a series of modular chess computers by Consumenta Computer.

Hardware development by Loproc (Germany), manufactured at Wallharn Electronics
(Ireland). The core people involved were Ulf Rathsman for the chess engine,
and Johan Enroth. After Consumenta went under in 1983, the Conchess brand was
continued by Systemhuset.

TODO:
- concglap/concglapa rom labels
- dump/add concvicp library module (L/L16 don't work, manual says it has its own add-on)
- concvicp unmapped reads/writes
- verify irq/beeper for concvicp, though it is probably correct

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

Hardware notes:

Chess boards released were Escorter, Ambassador, and Monarch, each should be the
same hardware, they just differ in size and material.
- TTL, 2 module slots
- 16+64 leds, 16 buttons, reed sensors for magnet chesspieces

All chess modules appear to be on similar PCBs, with room a 6502/65C02,
and 8 ROM/RAM chips.

For the newer programs, higher clocked versions were also available, the CPU speed
was labeled on a sticker on the module (eg. A2/5.5MHz). A German redistributor
named them "S"(speed) or "T"(top speed).

A0 (untitled standard pack-in module):
- SY6502A @ 2MHz (4MHz XTAL)
- 3*8KB ROM, 4KB RAM(2*TMM2016P)
- TTL, beeper

note: XTAL goes to 4020, 4020 /2 goes to CPU clock, and other dividers to
IRQ and beeper. On A0, IRQ is active for ~31.2us.

A1(P) + A0(M) (Princhess, aka Glasgow)
- dual-module, 2nd module has no CPU (according to the manual, A0 is modified and
  won't work stand-alone)
- dual-module Amsterdam version also exists

A2(C) (Plymate, aka Amsterdam)
- R65C02P4 @ 5.5MHz (11MHz XTAL) (5.5MHz version)
- 32KB ROM, rest similar to A0

A3 (Plymate Victoria)
- W65C02S8P-14(14? probably replaced chip from a repair) @ 6.144MHz (12.288MHz XTAL)
- 32KB ROM, rest similar to A0

Library modules:
- L: small PCB, PCB label: CCL L-2, 8KB EPROM no label
- L16: 2*8KB EPROM (have no photo of PCB)

There are also modern bootleg boards from SteveUK in circulation. The library ROM
is integrated.

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

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/m6502/m6502.h"
#include "cpu/m6502/r65c02.h"
#include "cpu/m6502/w65c02.h"
#include "machine/sensorboard.h"
#include "sound/beep.h"
#include "video/pwm.h"

#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "conchess.lh"


namespace {

class conchess_state : public driver_device
{
public:
	conchess_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_board(*this, "board"),
		m_beeper(*this, "beeper"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// machine configs
	void conc(machine_config &config);
	void concgla(machine_config &config);
	void concams(machine_config &config);
	void concams5(machine_config &config);
	void concvicp(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<sensorboard_device> m_board;
	required_device<beep_device> m_beeper;
	required_ioport_array<2> m_inputs;

	u8 m_inp_mux = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void clear_irq();
	u8 input_r();
	void leds_w(offs_t offset, u8 data);
	void sound_w(u8 data);
};

void conchess_state::machine_start()
{
	save_item(NAME(m_inp_mux));
}



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

void conchess_state::clear_irq()
{
	m_maincpu->set_input_line(0, CLEAR_LINE);
}

u8 conchess_state::input_r()
{
	if (!machine().side_effects_disabled())
		clear_irq();

	u8 data = 0;

	// read side panel buttons
	if (m_inp_mux == 0 || m_inp_mux == 9)
		data = m_inputs[m_inp_mux & 1]->read();

	// read chessboard sensors
	else
		data = m_board->read_file((m_inp_mux - 1) ^ 7);

	return ~data;
}

void conchess_state::leds_w(offs_t offset, u8 data)
{
	clear_irq();

	// a0-a3: CD4028B to led select/input mux
	m_inp_mux = offset;
	if (m_inp_mux & 8)
		m_inp_mux &= 9;

	// d0-d7: led data
	m_display->matrix(1 << m_inp_mux, data);
}

void conchess_state::sound_w(u8 data)
{
	// d7: enable beeper
	m_beeper->set_state(BIT(data, 7));
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void conchess_state::main_map(address_map &map)
{
	map(0x0000, 0x0fff).ram();
	map(0x1000, 0x1000).mirror(0x07ff).r(FUNC(conchess_state::input_r));
	map(0x1000, 0x100f).mirror(0x07f0).w(FUNC(conchess_state::leds_w));
	map(0x1800, 0x1800).mirror(0x07ff).w(FUNC(conchess_state::sound_w));
	map(0x4000, 0x7fff).r("cartslot", FUNC(generic_slot_device::read_rom));
	map(0x8000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( conchess )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("O. (Clear)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Stop")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Dice Symbol (Alternate)")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("?-Sign (Analyze)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Section Sign (Referee)")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("4-Way Arrow (Piece)")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("2-Way Arrow (Level)")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME(". (Continue)")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("King")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("White")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Black")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void conchess_state::conc(machine_config &config)
{
	// basic machine hardware
	M6502(config, m_maincpu, 4_MHz_XTAL/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &conchess_state::main_map);

	const attotime irq_period = attotime::from_hz(4_MHz_XTAL / 0x2000);
	m_maincpu->set_periodic_int(FUNC(conchess_state::irq0_line_assert), irq_period);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "conchess_cart");
	SOFTWARE_LIST(config, "cart_list").set_original("conchess").set_filter("l");

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 8);
	config.set_default_layout(layout_conchess);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 4_MHz_XTAL / 0x400);
	m_beeper->add_route(ALL_OUTPUTS, "mono", 0.25);
}

void conchess_state::concgla(machine_config &config)
{
	conc(config);

	// basic machine hardware
	R65C02(config.replace(), m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &conchess_state::main_map);

	const attotime irq_period = attotime::from_hz(4_MHz_XTAL / 0x2000);
	m_maincpu->set_periodic_int(FUNC(conchess_state::irq0_line_assert), irq_period);

	subdevice<software_list_device>("cart_list")->set_filter("none");

	// sound hardware
	BEEP(config.replace(), m_beeper, 4_MHz_XTAL / 0x400);
	m_beeper->add_route(ALL_OUTPUTS, "mono", 0.25);
}

void conchess_state::concams(machine_config &config)
{
	concgla(config);

	// basic machine hardware
	subdevice<software_list_device>("cart_list")->set_filter("l16");
}

void conchess_state::concams5(machine_config &config)
{
	concams(config);

	// basic machine hardware
	m_maincpu->set_clock(11_MHz_XTAL/2);

	const attotime irq_period = attotime::from_hz(11_MHz_XTAL / 0x4000);
	m_maincpu->set_periodic_int(FUNC(conchess_state::irq0_line_assert), irq_period);

	// sound hardware
	BEEP(config.replace(), m_beeper, 11_MHz_XTAL / 0x800);
	m_beeper->add_route(ALL_OUTPUTS, "mono", 0.25);
}

void conchess_state::concvicp(machine_config &config)
{
	concams5(config);

	// basic machine hardware
	W65C02(config.replace(), m_maincpu, 12.288_MHz_XTAL/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &conchess_state::main_map);

	const attotime irq_period = attotime::from_hz(12.288_MHz_XTAL / 0x4000);
	m_maincpu->set_periodic_int(FUNC(conchess_state::irq0_line_assert), irq_period);

	subdevice<software_list_device>("cart_list")->set_filter("l1024");

	// sound hardware
	BEEP(config.replace(), m_beeper, 12.288_MHz_XTAL / 0x1000);
	m_beeper->add_route(ALL_OUTPUTS, "mono", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( conc ) // 2 bytes different (bookrom fix)
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD("ccp2.a", 0xa000, 0x1000, CRC(fddd155b) SHA1(ccb67543f4d680b47d2956b284d5c6b7a8d21520) ) // MBM2732A-30
	ROM_LOAD("ccp2.b", 0xb000, 0x1000, CRC(81cf6b20) SHA1(7f6e30e50c6f9b633767a04b7d7a9c291d301614) ) // "
	ROM_LOAD("ccp2.c", 0xc000, 0x1000, CRC(151eca1a) SHA1(40b97bce2ff0cae528efa3a9bab5767d5d15d369) ) // "
	ROM_LOAD("ccp2.d", 0xd000, 0x1000, CRC(ed5dafeb) SHA1(3f0cea45bf26d9a1d120963480fc883a00185224) ) // "
	ROM_LOAD("ccp2.e", 0xe000, 0x1000, CRC(f07bf0e2) SHA1(e4f2f415144ed5e43f4940c7c9618e89707d19e9) ) // "
	ROM_LOAD("ccp2.f", 0xf000, 0x1000, CRC(7a04b000) SHA1(72d702b91adc643201ac6cea7aeb9394a3afc8be) ) // "
ROM_END

ROM_START( conca ) // 6 EPROMs version also exists, same ROM contents
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD("c87011_ccp2ab.b3", 0xa000, 0x2000, CRC(915e414c) SHA1(80c94712d1c79fa469576c37b80ab66f77c77cc4) ) // C2C076 serial also seen, same ROM contents
	ROM_LOAD("c87010_ccp2cd.b2", 0xc000, 0x2000, CRC(088c8737) SHA1(9f841b3c47de9ef1da8ce98c0a33a919cba873c6) )
	ROM_LOAD("c87009_ccp2ef.b1", 0xe000, 0x2000, CRC(e1c648e2) SHA1(725a6ac1c69f788a7bba0573e5609b55b12899ac) )
ROM_END

ROM_START( concgla ) // 8 EPROMs version also exists, same ROM contents
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("9128c-0133", 0x8000, 0x4000, CRC(a6ac88eb) SHA1(d1fcd990e5196c00210d380e0e04155a5ea19824) ) // GI 9128C
	ROM_LOAD("9128c-0134", 0xc000, 0x4000, CRC(b694a275) SHA1(e4e49379b4eb45402ca8bb82c20d0169db62ed7a) ) // "
ROM_END

ROM_START( concglap ) // 2 bytes different
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("ccplus", 0x8000, 0x8000, CRC(c62e522b) SHA1(28ea9af35f9f4e96e52c19597d62e0af135c7672) )
ROM_END

ROM_START( concglapa )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("ccplus", 0x8000, 0x8000, CRC(0bf4d2d0) SHA1(0ce670d3be1ae4da5467be6b062cfe73dcfbf229) )
ROM_END

ROM_START( concams )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("8b_4.0_133", 0x8000, 0x4000, CRC(e021e3d8) SHA1(7a2c079664ed5cbaa7e5c55d4d2e8936b7be4219) ) // SEEQ DQ5143-250
	ROM_LOAD("cf_4.0_134", 0xc000, 0x4000, CRC(fc827c7f) SHA1(51acd881c4d5fe29f2a83d35a48ba323344122db) ) // "
ROM_END

ROM_START( concams5 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("s5.a1", 0x8000, 0x8000, CRC(9a9d1ec1) SHA1(75dbd1f96502775ed304f6b085d958f1b07d08f9) ) // AT27C256
ROM_END

ROM_START( concvicp )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("cc8-f.a2", 0x8000, 0x8000, CRC(5b0a1d09) SHA1(07cbc970a8dfbca386396ce5d5cc8ce77ad4ee1b) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT    COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1982, conc,      0,        0,      conc,     conchess, conchess_state, empty_init, "Consumenta Computer / Loproc", "Conchess (standard, set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1982, conca,     conc,     0,      conc,     conchess, conchess_state, empty_init, "Consumenta Computer / Loproc", "Conchess (standard, set 2)", MACHINE_SUPPORTS_SAVE )

SYST( 1984, concgla,   0,        0,      concgla,  conchess, conchess_state, empty_init, "Systemhuset / Loproc", "Conchess Princhess Glasgow", MACHINE_SUPPORTS_SAVE )
SYST( 1984, concglap,  0,        0,      concgla,  conchess, conchess_state, empty_init, "Systemhuset / Loproc", "Conchess Plymate Glasgow Plus (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1984, concglapa, concglap, 0,      concgla,  conchess, conchess_state, empty_init, "Systemhuset / Loproc", "Conchess Plymate Glasgow Plus (set 2)", MACHINE_SUPPORTS_SAVE )

SYST( 1985, concams,   0,        0,      concams,  conchess, conchess_state, empty_init, "Systemhuset / Loproc", "Conchess Plymate Amsterdam", MACHINE_SUPPORTS_SAVE )
SYST( 1985, concams5,  concams,  0,      concams5, conchess, conchess_state, empty_init, "Systemhuset / Loproc", "Conchess Plymate Amsterdam 5.5MHz", MACHINE_SUPPORTS_SAVE )

SYST( 1990, concvicp,  0,        0,      concvicp, conchess, conchess_state, empty_init, "Systemhuset / Loproc", "Conchess Plymate Victoria (prototype)", MACHINE_SUPPORTS_SAVE )



conic_cchess2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:bataais
/*******************************************************************************

Conic Computer Chess (model 07012), also sold under the same name by
Hanimex (model HMG 1200) and Westrak (model CC 1). It's also known as
Tracer Chess, this title is from an advertisement flyer, the actual chess
computer was simply named Computer Chess.

The chessboard sensors aren't buttons or magnets, but 64 jacks. There is
a metal plug under each chesspiece. From MAME's perspective, it works the
same as a magnet sensorboard.

After the player's move, the user needs to press Enter. This does not apply
to the computer's move.

Hardware notes:
- Synertek 6504 @ ~1MHz
- 2*Motorola MC6821P PIA
- 4KB ROM(AMI), 1KB RAM(2*Synertek 1024x4)
- beeper, 8*8 leds, plug board

BTANB:
- Learn button still works when in modify-mode

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

#include "emu.h"

#include "cpu/m6502/m6504.h"
#include "machine/6821pia.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "conic_cchess2.lh"


namespace {

class cchess2_state : public driver_device
{
public:
	cchess2_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_pia(*this, "pia%u", 0),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// machine configs
	void cncchess2(machine_config &config);

	// assume that RESET button is tied to 6504 reset pin
	DECLARE_INPUT_CHANGED_MEMBER(reset_button) { m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE); }

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device_array<pia6821_device, 2> m_pia;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<8> m_inputs;

	u8 m_inp_mux = 0;
	u8 m_led_data = 0;
	u8 m_dac_on = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_display();
	void update_dac();
	void pia0_pa_w(u8 data);
	void pia0_pb_w(u8 data);
	u8 pia1_pa_r();
	u8 pia1_pb_r();
	void pia1_pb_w(u8 data);
};

void cchess2_state::machine_start()
{
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_led_data));
	save_item(NAME(m_dac_on));
}



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

void cchess2_state::update_display()
{
	m_display->matrix(m_inp_mux, m_led_data);
}

void cchess2_state::update_dac()
{
	m_dac->write(m_dac_on & BIT(m_inp_mux, 1));
}

void cchess2_state::pia0_pa_w(u8 data)
{
	// d0-d7: input mux/led select
	// d1: dac data
	m_inp_mux = data;
	update_display();
	update_dac();
}

void cchess2_state::pia0_pb_w(u8 data)
{
	// d0-d7: led data
	m_led_data = data;
	update_display();
}

u8 cchess2_state::pia1_pa_r()
{
	u8 data = 0;

	// d0-d7: chessboard sensors
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_rank(i);

	return ~data;
}

u8 cchess2_state::pia1_pb_r()
{
	u8 data = 0;

	// d0-d3: multiplexed inputs
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read();

	return data;
}

void cchess2_state::pia1_pb_w(u8 data)
{
	// d7: dac on
	m_dac_on = BIT(data, 7);
	update_dac();
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void cchess2_state::main_map(address_map &map)
{
	map.global_mask(0x1fff);
	map(0x0000, 0x03ff).ram();
	map(0x0a00, 0x0a03).mirror(0x00fc).rw(m_pia[0], FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x0c00, 0x0c03).mirror(0x00fc).rw(m_pia[1], FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x1000, 0x1fff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( cncchess2 )
	PORT_START("IN.0")
	PORT_BIT(0x03, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_CONFNAME( 0x0c, 0x08, "Mode" )
	PORT_CONFSETTING(    0x04, "Modify" )
	PORT_CONFSETTING(    0x08, "Play" )

	PORT_START("IN.1")
	PORT_CONFNAME( 0x03, 0x02, "Color" )
	PORT_CONFSETTING(    0x01, "Black" ) // from the bottom
	PORT_CONFSETTING(    0x02, "White" )
	PORT_BIT(0x0c, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.2")
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.3")
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("Learn")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("6 / White King")

	PORT_START("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("5 / White Queen")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("4 / White Rook")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("3 / White Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("2 / White Knight")

	PORT_START("IN.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("1 / White Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Clear")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("7 / Black Pawn")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("8 / Black Knight")

	PORT_START("IN.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_NAME("9 / Black Bishop")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_NAME("10 / Black Rook")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_MINUS) PORT_NAME("11 / Black Queen")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("12 / Black King")

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cchess2_state::reset_button), 0) PORT_NAME("Reset")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void cchess2_state::cncchess2(machine_config &config)
{
	// basic machine hardware
	M6504(config, m_maincpu, 1'000'000); // approximation, no XTAL
	m_maincpu->set_addrmap(AS_PROGRAM, &cchess2_state::main_map);

	PIA6821(config, m_pia[0]);
	m_pia[0]->writepa_handler().set(FUNC(cchess2_state::pia0_pa_w));
	m_pia[0]->writepb_handler().set(FUNC(cchess2_state::pia0_pb_w));

	PIA6821(config, m_pia[1]);
	m_pia[1]->readpa_handler().set(FUNC(cchess2_state::pia1_pa_r));
	m_pia[1]->readpb_handler().set(FUNC(cchess2_state::pia1_pb_r));
	m_pia[1]->writepb_handler().set(FUNC(cchess2_state::pia1_pb_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 8);
	config.set_default_layout(layout_conic_cchess2);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( cncchess2 )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD("c11485.u2", 0x1000, 0x1000, CRC(b179d536) SHA1(0b1f9c247a4a3e2ccbf8d3ae5efa62b8938f572f) ) // AMI 2332
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT      CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1980, cncchess2, 0,      0,      cncchess2, cncchess2, cchess2_state, empty_init, "Conic", "Computer Chess (Conic, model 7012)", MACHINE_SUPPORTS_SAVE )



conic_cchess3.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Conic Computer Chess (model 07013), more commonly known as Conic "Korchnoi".

The interface is similar to the previous model (07012), where the user needs
to press Enter after their move.

Hardware notes:
- Synertek 6502A @ 2MHz
- OKI MSM5840H-41RS @ 3.57MHz (2KB internal ROM)
- 2*4KB ROM(AMI), 1KB RAM(2*2114)
- beeper, 8*8+4 leds, magnets chessboard

TODO:
- It does not work, MSM5840 is unemulated, and the internal ROM is not dumped.
  MCU handles inputs, leds, sound, and it communicates with maincpu after
  triggering an IRQ.

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

#include "emu.h"

#include "cpu/m6502/m6502.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
//#include "conic_cchess3.lh"


namespace {

class cchess3_state : public driver_device
{
public:
	cchess3_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac")
	{ }

	// machine configs
	void cncchess3(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;

	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void main_comm_w(u8 data);
	u8 main_comm_r();
};

void cchess3_state::machine_start()
{
}



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

void cchess3_state::main_comm_w(u8 data)
{
}

u8 cchess3_state::main_comm_r()
{
	return 0;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void cchess3_state::main_map(address_map &map)
{
	map.global_mask(0x3fff);
	map(0x0000, 0x03ff).ram();
	map(0x1000, 0x1000).rw(FUNC(cchess3_state::main_comm_r), FUNC(cchess3_state::main_comm_w));
	map(0x2000, 0x3fff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( cncchess3 )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void cchess3_state::cncchess3(machine_config &config)
{
	// basic machine hardware
	M6502(config, m_maincpu, 2_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &cchess3_state::main_map);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8+1, 8);
	//config.set_default_layout(layout_conic_cchess3);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( cncchess3 )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("ci07013-1", 0x2000, 0x1000, CRC(3251a529) SHA1(729b22d7653761ff0951ce1da58fdfcd474a700d) ) // AMI 2332
	ROM_LOAD("ci07013-2", 0x3000, 0x1000, CRC(0f38dcef) SHA1(f8fb7e12b41753fe52dd2eb2edb843211b5ca7c1) ) // "

	ROM_REGION( 0x0800, "mcu", 0 )
	ROM_LOAD("msm5840h-41rs", 0x0000, 0x0800, NO_DUMP )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT      CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1981, cncchess3, 0,      0,      cncchess3, cncchess3, cchess3_state, empty_init, "Conic", "Computer Chess (Conic, model 7013)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



consola_emt.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    Indra / Amper "Consola EMT"

    On-board ticketing console used on public transport buses on Madrid
    from 1997 to 2007.

    CPU Intel N80C188XL25 with a 50.0 HMz xtal near it.
    1 Oki MSM62X42B real time clock with built-in xtal
    4 x HY62810DA 128kx8bit CMOS SRAM
    MCU (for ticket pinter?) Intel N87C51FA (unprotected) with 11.0592 MHz xtal near it
    2 x Zilog Z85230 with a 4.9152 MHZ xtal near them
    5 x 8 dips banks:
    -FUNCIONES = Functions
    -L-NUMBUS  = Low / Bus number
    -H-NUMBUS  = High / Bus number
    -L-NUMFAB  = Low / Manufacturer number
    -H-NUMFAB  = High / Manufacturer number
    LCD display = PC2002LRU-LSO-H

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

#include "emu.h"
#include "cpu/i86/i186.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/msm6242.h"
#include "machine/z80scc.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"


namespace {

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

class consoemt_state : public driver_device
{
public:
	consoemt_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_mcu(*this, "mcu"),
		m_lcdc(*this, "lcdc")
	{ }

	void consoemt(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<i80188_cpu_device> m_maincpu;
	required_device<mcs51_cpu_device> m_mcu;
	required_device<hd44780_device> m_lcdc;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	void consoemt_palette(palette_device &palette) const;
	HD44780_PIXEL_UPDATE(lcd_pixel_update);
};


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

void consoemt_state::mem_map(address_map &map)
{
	map(0x00000, 0x7ffff).ram();
	map(0xc0000, 0xfffff).rom().region("maincpu", 0);
}

void consoemt_state::io_map(address_map &map)
{
	map(0x000, 0x003).rw("uart1", FUNC(scc85230_device::ab_dc_r), FUNC(scc85230_device::ab_dc_w));
	map(0x010, 0x013).rw("uart2", FUNC(scc85230_device::ab_dc_r), FUNC(scc85230_device::ab_dc_w));
	map(0x180, 0x18f).rw("rtc", FUNC(msm6242_device::read), FUNC(msm6242_device::write));
	map(0x200, 0x200).portr("FUNCIONES");
	map(0x210, 0x210).portr("NUMBUS-L");
	map(0x220, 0x220).portr("NUMBUS-H");
	map(0x230, 0x230).portr("NUMFAB-L");
	map(0x240, 0x240).portr("NUMFAB-H");
//  map(0x250, 0x250).r // read on int2
//  map(0x260, 0x260).r // read on int1
	map(0x280, 0x281).rw(m_lcdc, FUNC(hd44780_device::read), FUNC(hd44780_device::write));
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( consoemt )
	PORT_START("FUNCIONES")
	PORT_DIPNAME(0x01, 0x00, "Canceladora 1") PORT_DIPLOCATION("FUNCIONES:1")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x01, DEF_STR( On ))
	PORT_DIPNAME(0x02, 0x00, "Canceladora 2") PORT_DIPLOCATION("FUNCIONES:2")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x02, DEF_STR( On ))
	PORT_DIPNAME(0x04, 0x00, "Canceladora 3") PORT_DIPLOCATION("FUNCIONES:3")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x04, DEF_STR( On ))
	PORT_DIPUNKNOWN_DIPLOC(0x08, 0x00, "FUNCIONES:4")
	PORT_DIPUNKNOWN_DIPLOC(0x10, 0x00, "FUNCIONES:5")
	PORT_DIPUNKNOWN_DIPLOC(0x20, 0x00, "FUNCIONES:6")
	PORT_DIPUNKNOWN_DIPLOC(0x40, 0x00, "FUNCIONES:7")
	PORT_DIPUNKNOWN_DIPLOC(0x80, 0x00, "FUNCIONES:8")

	PORT_START("NUMBUS-L")
	PORT_DIPNAME(0x01, 0x00, "Bus 1") PORT_DIPLOCATION("NUMBUS-L:1")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x01, DEF_STR( On ))
	PORT_DIPNAME(0x02, 0x00, "Bus 2") PORT_DIPLOCATION("NUMBUS-L:2")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x02, DEF_STR( On ))
	PORT_DIPNAME(0x04, 0x00, "Bus 4") PORT_DIPLOCATION("NUMBUS-L:3")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x04, DEF_STR( On ))
	PORT_DIPNAME(0x08, 0x00, "Bus 8") PORT_DIPLOCATION("NUMBUS-L:4")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x08, DEF_STR( On ))
	PORT_DIPNAME(0x10, 0x00, "Bus 16") PORT_DIPLOCATION("NUMBUS-L:5")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x10, DEF_STR( On ))
	PORT_DIPNAME(0x20, 0x00, "Bus 32") PORT_DIPLOCATION("NUMBUS-L:6")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x20, DEF_STR( On ))
	PORT_DIPNAME(0x40, 0x00, "Bus 64") PORT_DIPLOCATION("NUMBUS-L:7")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x40, DEF_STR( On ))
	PORT_DIPNAME(0x80, 0x00, "Bus 128") PORT_DIPLOCATION("NUMBUS-L:8")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x80, DEF_STR( On ))

	PORT_START("NUMBUS-H")
	PORT_DIPNAME(0x01, 0x00, "Bus 256") PORT_DIPLOCATION("NUMBUS-H:1")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x01, DEF_STR( On ))
	PORT_DIPNAME(0x02, 0x00, "Bus 512") PORT_DIPLOCATION("NUMBUS-H:2")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x02, DEF_STR( On ))
	PORT_DIPNAME(0x04, 0x00, "Bus 1024") PORT_DIPLOCATION("NUMBUS-H:3")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x04, DEF_STR( On ))
	PORT_DIPNAME(0x08, 0x00, "Bus 2048") PORT_DIPLOCATION("NUMBUS-H:4")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x08, DEF_STR( On ))
	PORT_DIPNAME(0x10, 0x00, "Bus 4096") PORT_DIPLOCATION("NUMBUS-H:5")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x10, DEF_STR( On ))
	PORT_DIPNAME(0x20, 0x00, "Bus 8192") PORT_DIPLOCATION("NUMBUS-H:6")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x20, DEF_STR( On ))
	PORT_DIPNAME(0x40, 0x00, "Bus 16384") PORT_DIPLOCATION("NUMBUS-H:7")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x40, DEF_STR( On ))
	PORT_DIPUNKNOWN_DIPLOC(0x80, 0x00, "NUMBUS-H:8")

	PORT_START("NUMFAB-L")
	PORT_DIPNAME(0x01, 0x00, "Fab 1") PORT_DIPLOCATION("NUMFAB-L:1")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x01, DEF_STR( On ))
	PORT_DIPNAME(0x02, 0x00, "Fab 2") PORT_DIPLOCATION("NUMFAB-L:2")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x02, DEF_STR( On ))
	PORT_DIPNAME(0x04, 0x00, "Fab 4") PORT_DIPLOCATION("NUMFAB-L:3")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x04, DEF_STR( On ))
	PORT_DIPNAME(0x08, 0x00, "Fab 8") PORT_DIPLOCATION("NUMFAB-L:4")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x08, DEF_STR( On ))
	PORT_DIPNAME(0x10, 0x00, "Fab 16") PORT_DIPLOCATION("NUMFAB-L:5")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x10, DEF_STR( On ))
	PORT_DIPNAME(0x20, 0x00, "Fab 32") PORT_DIPLOCATION("NUMFAB-L:6")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x20, DEF_STR( On ))
	PORT_DIPNAME(0x40, 0x00, "Fab 64") PORT_DIPLOCATION("NUMFAB-L:7")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x40, DEF_STR( On ))
	PORT_DIPNAME(0x80, 0x00, "Fab 128") PORT_DIPLOCATION("NUMFAB-L:8")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x80, DEF_STR( On ))

	PORT_START("NUMFAB-H")
	PORT_DIPNAME(0x01, 0x00, "Fab 256") PORT_DIPLOCATION("NUMFAB-H:1")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x01, DEF_STR( On ))
	PORT_DIPNAME(0x02, 0x00, "Fab 512") PORT_DIPLOCATION("NUMFAB-H:2")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x02, DEF_STR( On ))
	PORT_DIPNAME(0x04, 0x00, "Fab 1024") PORT_DIPLOCATION("NUMFAB-H:3")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x04, DEF_STR( On ))
	PORT_DIPNAME(0x08, 0x00, "Fab 2048") PORT_DIPLOCATION("NUMFAB-H:4")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x08, DEF_STR( On ))
	PORT_DIPNAME(0x10, 0x00, "Fab 4096") PORT_DIPLOCATION("NUMFAB-H:5")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x10, DEF_STR( On ))
	PORT_DIPNAME(0x20, 0x00, "Fab 8192") PORT_DIPLOCATION("NUMFAB-H:6")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x20, DEF_STR( On ))
	PORT_DIPNAME(0x40, 0x00, "Fab 16384") PORT_DIPLOCATION("NUMFAB-H:7")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x40, DEF_STR( On ))
	PORT_DIPNAME(0x80, 0x00, "Fab 32768") PORT_DIPLOCATION("NUMFAB-H:8")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x80, DEF_STR( On ))
INPUT_PORTS_END


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

void consoemt_state::consoemt_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148)); // background
	palette.set_pen_color(1, rgb_t( 92,  83,  88)); // lcd pixel on
	palette.set_pen_color(2, rgb_t(131, 136, 139)); // lcd pixel off
}

HD44780_PIXEL_UPDATE( consoemt_state::lcd_pixel_update )
{
	// char size is 5x8
	if (x > 4 || y > 7)
		return;

	if (line < 2 && pos < 20)
		bitmap.pix(1 + y + line*8 + line, 1 + pos*6 + x) = state ? 1 : 2;
}


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

void consoemt_state::machine_start()
{
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void consoemt_state::consoemt(machine_config &config)
{
	I80188(config, m_maincpu, 50_MHz_XTAL); // N80C188XL25
	m_maincpu->set_addrmap(AS_PROGRAM, &consoemt_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &consoemt_state::io_map);

	I87C51FA(config, m_mcu, 11.0592_MHz_XTAL);

	MSM6242(config, "rtc", XTAL(32'768));

	scc85230_device &uart1(SCC85230(config, "uart1", 4.9152_MHz_XTAL));
	uart1.out_int_callback().set(m_maincpu, FUNC(i80188_cpu_device::int0_w));

	scc85230_device &uart2(SCC85230(config, "uart2", 4.9152_MHz_XTAL));
	uart2.out_int_callback().set(m_maincpu, FUNC(i80188_cpu_device::int3_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(6*20+1, 19);
	screen.set_visarea_full();
	screen.set_screen_update(m_lcdc, FUNC(hd44780_device::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(consoemt_state::consoemt_palette), 3);

	KS0066(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_default_bios_tag("f05");
	m_lcdc->set_lcd_size(2, 20);
	m_lcdc->set_pixel_update_cb(FUNC(consoemt_state::lcd_pixel_update));
}


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

ROM_START( consoemt )
	ROM_REGION(0x40000, "maincpu", 0)
	ROM_LOAD("pupitre_emt_24_04_03_6adc.ic1", 0x00000, 0x40000, CRC(fbafc173) SHA1(c0366a553125d42f18c24faa71467144eae42972))

	ROM_REGION(0x2000, "mcu", 0)
	ROM_LOAD("v26_7caa_n87c51fa.ic20", 0x0000, 0x2000, CRC(37e6c202) SHA1(7b240ed6474240090c26de11048a40c5870886dd))
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY          FULLNAME       FLAGS
COMP( 2003, consoemt, 0,      0,      consoemt, consoemt, consoemt_state, empty_init, "Indra / Amper", "Consola EMT", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



const.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Novag Super Sensor IV (model 812)
Novag Constellation (model 831)
Novag Super Constellation (model 844)
Novag Constellation 3.6MHz (model 845)
Novag Constellation Quattro (model 862)

The chess engine is by David Kittinger, Super Sensor IV is his first one
working under Novag. The Constellation engine is completely different.

Hardware notes:

They are all on very similar hardware.
The more expensive chesscomputers have battery-backed RAM and support for
printer and chessclock peripherals.

Constellation:
- MOS MPS6502A @ 2MHz
- 2KB RAM (daughterboard with 4*2114), 2*8KB ROM
- TTL, buzzer, 24 LEDs, 8*8 chessboard buttons

Constellation 3.6MHz:
- PCB label: NOVAG CONSTELLATION Rev E 100037
- G65SC02P-3 or R65C02P3(newer version) @ 3.6MHz (7.2MHz XTAL)
- 2KB RAM (TC5516AP), 16KB ROM (custom label, assumed TMM23128)
- PCB supports "Memory Save", but components aren't installed

Constellation 3.6MHz manufactured in 1986 was fitted with the same ROM as
the Quattro, but PCB remains almost the same (Quatto PCB is very different).
Button labels are the same as the older version.

Constellation Quattro:
- R65C02P3 @ 4MHz (8MHz XTAL)
- 2KB RAM (D449C), 16KB ROM (custom label)

Super Sensor IV:
- MOS MPS6502A @ 2MHz
- 1KB battery-backed RAM (2*TC5514AP-3)
- 8KB ROM (TMM2364P)
- 2 ROM sockets for expansion (blue @ u6, white @ u5)

Known Super Sensor IV expansion ROMs:
- Chess Printer (came with the printer accessory)

Super Sensor IV triggers an NMI on power-off (or power-failure). If this isn't
done, NVRAM fails at next power-on.

Sensor Dynamic's ROM is identical to Super Sensor IV "1I", the hardware is
basically a low-budget version of it with peripheral ports removed.

Super Constellation:
- UMC UM6502C @ 4 MHz (8MHz XTAL)
- 4KB battery-backed RAM (2*TC5516APL-2)
- 2*32KB ROM custom label

Super Constellation also has a power-off NMI, but it doesn't do anything other
than writing 0x08 to control_w.

TODO:
- if/when MAME supports an exit callback, hook up power-off switch to that
- is Dynamic S a program update of ssensor4 or identical?

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

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/m6502/g65sc02.h"
#include "cpu/m6502/m6502.h"
#include "cpu/m6502/r65c02.h"
#include "machine/clock.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/beep.h"
#include "video/pwm.h"

#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "novag_const.lh"
#include "novag_constq.lh"
#include "novag_ssensor4.lh"
#include "novag_supercon.lh"


namespace {

class const_state : public driver_device
{
public:
	const_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_beeper(*this, "beeper"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(power_off);

	// machine configs
	void nconst(machine_config &config);
	void nconst36(machine_config &config);
	void nconst36a(machine_config &config);
	void nconstq(machine_config &config);
	void ssensor4(machine_config &config);
	void sconst(machine_config &config);

	void init_const();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD { m_power = true; }

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<beep_device> m_beeper;
	required_ioport_array<8> m_inputs;

	bool m_power = false;
	u8 m_inp_mux = 0;

	// address maps
	void const_map(address_map &map) ATTR_COLD;
	void ssensor4_map(address_map &map) ATTR_COLD;
	void sconst_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void mux_w(u8 data);
	void control_w(u8 data);
	u8 input1_r();
	u8 input2_r();
};



/*******************************************************************************
    Initialization
*******************************************************************************/

void const_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_power));
	save_item(NAME(m_inp_mux));
}

INPUT_CHANGED_MEMBER(const_state::power_off)
{
	// NMI at power-off (ssensor4 prepares nvram for next power-on)
	if (newval && m_power)
	{
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
		m_power = false;
	}
}

void const_state::init_const()
{
	// game relies on RAM filled with FF at power-on
	for (int i = 0; i < 0x800; i++)
		m_maincpu->space(AS_PROGRAM).write_byte(i, 0xff);
}



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

void const_state::mux_w(u8 data)
{
	// d0-d7: input mux, led data
	m_inp_mux = data;
	m_display->write_mx(data);
}

void const_state::control_w(u8 data)
{
	// d0-d2: ?
	// d3: ? (goes high at power-off NMI)

	// d4-d6: select led row
	m_display->write_my(data >> 4 & 7);

	// d7: enable beeper
	m_beeper->set_state(BIT(data, 7));
}

u8 const_state::input1_r()
{
	u8 data = 0;

	// d0-d7: multiplexed inputs (chessboard squares)
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_rank(i ^ 7, true);

	return ~data;
}

u8 const_state::input2_r()
{
	u8 data = 0;

	// d0-d2: printer
	// d3: timing related? seems unused (always high)
	// other: ?

	// d6,d7: multiplexed inputs (side panel)
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read() << 6;

	return ~data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void const_state::ssensor4_map(address_map &map)
{
	map(0x0000, 0x03ff).ram().share("nvram");
	map(0x2000, 0x2000).nopw(); // printer
	map(0x4000, 0x4000).nopw(); // printer
	map(0x6000, 0x6000).rw(FUNC(const_state::input2_r), FUNC(const_state::mux_w));
	map(0x8000, 0x8000).rw(FUNC(const_state::input1_r), FUNC(const_state::control_w));
	map(0xc000, 0xdfff).r("exrom", FUNC(generic_slot_device::read_rom));
	map(0xe000, 0xffff).rom();
}

void const_state::const_map(address_map &map)
{
	map(0x0000, 0x07ff).ram();
	map(0x6000, 0x6000).rw(FUNC(const_state::input2_r), FUNC(const_state::mux_w));
	map(0x8000, 0x8000).rw(FUNC(const_state::input1_r), FUNC(const_state::control_w));
	map(0xa000, 0xffff).rom();
}

void const_state::sconst_map(address_map &map)
{
	map(0x0000, 0x0fff).ram().share("nvram");
	map(0x1c00, 0x1c00).nopw(); // printer
	map(0x1d00, 0x1d00).nopw(); // printer
	map(0x1e00, 0x1e00).rw(FUNC(const_state::input2_r), FUNC(const_state::mux_w));
	map(0x1f00, 0x1f00).rw(FUNC(const_state::input1_r), FUNC(const_state::control_w));
	map(0x2000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( nconst )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("New Game")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Multi Move / Player/Player / King")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Verify / Set Up")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Best Move / Random / Queen")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Change Color")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Sound / Bishop")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Clear Board")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Solve Mate / Knight")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Rook")

	PORT_START("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Pawn")

	PORT_START("IN.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Hint / Show Moves")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Set Level")

	PORT_START("IN.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Go")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Take Back")
INPUT_PORTS_END

static INPUT_PORTS_START( ssensor4 )
	PORT_INCLUDE( nconst )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Accessory / King")

	PORT_MODIFY("IN.1")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Time Control / Queen")

	PORT_MODIFY("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Print Moves")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Print Board / Rook")

	PORT_MODIFY("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Form Size")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Print List / Pawn")

	PORT_MODIFY("IN.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Hint")

	PORT_START("POWER") // needs to be triggered for nvram to work
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(const_state::power_off), 0)
INPUT_PORTS_END

static INPUT_PORTS_START( nconstq )
	PORT_INCLUDE( nconst )

	PORT_MODIFY("IN.1")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Best Move/Random / Training Level / Queen")

	PORT_MODIFY("IN.2")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Sound / Depth Search / Bishop")
INPUT_PORTS_END

static INPUT_PORTS_START( sconst )
	PORT_INCLUDE( nconstq )

	PORT_MODIFY("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Print Moves")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Print Board / Rook")

	PORT_MODIFY("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Form Size")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Print List / Acc. Time / Pawn")

	PORT_START("POWER")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(const_state::power_off), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void const_state::nconst(machine_config &config)
{
	// basic machine hardware
	M6502(config, m_maincpu, 2_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &const_state::const_map);

	auto &irq_clock(CLOCK(config, "irq_clock", 2_MHz_XTAL / 0x2000)); // through 4020 IC, ~244Hz
	irq_clock.set_pulse_width(attotime::from_nsec(17200)); // active for ~17.2us
	irq_clock.signal_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(250));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(3, 8);
	config.set_default_layout(layout_novag_const);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 2_MHz_XTAL / 0x800); // ~976Hz
	m_beeper->add_route(ALL_OUTPUTS, "mono", 0.25);
}

void const_state::ssensor4(machine_config &config)
{
	nconst(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &const_state::ssensor4_map);

	subdevice<clock_device>("irq_clock")->set_pulse_width(attotime::from_usec(39)); // irq active for 39us

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);
	m_board->set_nvram_enable(true);

	config.set_default_layout(layout_novag_ssensor4);

	// expansion
	GENERIC_SOCKET(config, "exrom", generic_plain_slot, "novag_ssensor4");
	SOFTWARE_LIST(config, "cart_list").set_original("novag_ssensor4");
}

void const_state::nconst36(machine_config &config)
{
	nconst(config);

	// basic machine hardware
	G65SC02(config.replace(), m_maincpu, 7.2_MHz_XTAL/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &const_state::const_map);

	subdevice<clock_device>("irq_clock")->set_clock(7.2_MHz_XTAL/2 / 0x2000); // ~439Hz (pulse width same as nconst)

	m_board->set_delay(attotime::from_msec(200));

	// sound hardware
	BEEP(config.replace(), m_beeper, 7.2_MHz_XTAL/2 / 0x800); // ~1758Hz
	m_beeper->add_route(ALL_OUTPUTS, "mono", 0.25);
}

void const_state::nconst36a(machine_config &config)
{
	nconst36(config);

	// basic machine hardware
	R65C02(config.replace(), m_maincpu, 7.2_MHz_XTAL/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &const_state::const_map);

	// 4020 CLK is 7.2_MHz_XTAL/4, but with IRQ on Q11 instead of Q12, result
	// frequency and duty cycle are identical to nconst36

	// sound hardware
	BEEP(config.replace(), m_beeper, 7.2_MHz_XTAL/4 / 0x800); // ~879Hz
	m_beeper->add_route(ALL_OUTPUTS, "mono", 0.25);
}

void const_state::nconstq(machine_config &config)
{
	nconst36a(config);

	// basic machine hardware
	m_maincpu->set_clock(8_MHz_XTAL/2);

	subdevice<clock_device>("irq_clock")->set_clock(8_MHz_XTAL/4 / 0x1000); // ~488Hz (pulse width same as nconst)

	config.set_default_layout(layout_novag_constq);

	// sound hardware
	BEEP(config.replace(), m_beeper, 8_MHz_XTAL/4 / 0x800); // ~976Hz
	m_beeper->add_route(ALL_OUTPUTS, "mono", 0.25);
}

void const_state::sconst(machine_config &config)
{
	nconstq(config);

	// basic machine hardware
	M6502(config.replace(), m_maincpu, 8_MHz_XTAL/2); // UM6502C
	m_maincpu->set_addrmap(AS_PROGRAM, &const_state::sconst_map);

	subdevice<clock_device>("irq_clock")->set_pulse_width(attotime::from_nsec(10200)); // irq active for 10.2us

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);
	m_board->set_nvram_enable(true);

	config.set_default_layout(layout_novag_supercon);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( ssensor4 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("5611_1i_orange.u4", 0xe000, 0x2000, CRC(f4ee99d1) SHA1(f44144a26b92c51f4350da85858470e6c3b66fc1) ) // TMM2364P
ROM_END


ROM_START( const )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD("8315_white",  0xa000, 0x2000, CRC(76e6c97b) SHA1(55645e08f9f1258366c29a4ea2033bb86d860227) ) // TMM2364P
	ROM_LOAD("8314_orange", 0xe000, 0x2000, CRC(89395a86) SHA1(4807f196fec70abdaabff5bfc479a64d5cf2b0ad) ) // "
ROM_END

ROM_START( const36 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD("novag-831a_6133-8316.u2", 0xc000, 0x4000, CRC(7da760f3) SHA1(6172e0fa03377e911141a86747849bf25f20613f) )
ROM_END

ROM_START( const36a )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD("novag_854-501.u2", 0xc000, 0x4000, CRC(b083d5c4) SHA1(ecac8a599bd8ea8dd549c742ec45b94fb8b11af4) )
ROM_END

ROM_START( constq )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD("novag_854-501.u2", 0xc000, 0x4000, CRC(b083d5c4) SHA1(ecac8a599bd8ea8dd549c742ec45b94fb8b11af4) )
ROM_END


ROM_START( supercon )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("novag_8443", 0x0000, 0x8000, CRC(b853cf6e) SHA1(1a759072a5023b92c07f1fac01b7a21f7b5b45d0) )
	ROM_LOAD("novag_8442", 0x8000, 0x8000, CRC(c8f82331) SHA1(f7fd039f9a3344db9749931490ded9e9e309cfbe) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT   COMPAT  MACHINE    INPUT     CLASS        INIT        COMPANY, FULLNAME, FLAGS
SYST( 1981, ssensor4, 0,       0,      ssensor4,  ssensor4, const_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Super Sensor IV", MACHINE_SUPPORTS_SAVE )

SYST( 1983, const,    0,       0,      nconst,    nconst,   const_state, init_const, "Novag Industries / Intelligent Heuristic Programming", "Constellation", MACHINE_SUPPORTS_SAVE )
SYST( 1984, const36,  const,   0,      nconst36,  nconst,   const_state, init_const, "Novag Industries / Intelligent Heuristic Programming", "Constellation 3.6MHz (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1986, const36a, const,   0,      nconst36a, nconst,   const_state, init_const, "Novag Industries / Intelligent Heuristic Programming", "Constellation 3.6MHz (set 2)", MACHINE_SUPPORTS_SAVE )
SYST( 1986, constq,   const,   0,      nconstq,   nconstq,  const_state, init_const, "Novag Industries / Intelligent Heuristic Programming", "Constellation Quattro", MACHINE_SUPPORTS_SAVE )

SYST( 1984, supercon, 0,       0,      sconst,    sconst,   const_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Super Constellation", MACHINE_SUPPORTS_SAVE )



constjr.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Mychess
/*******************************************************************************

Novag Constellation Junior (aka Constellation Jr.)

NOTE: It triggers an NMI at power-off (or power-failure). If this isn't done,
NVRAM won't work properly.

The interface and look are very similar to Novag Presto (Micro II program).
The chess engine is by David Kittinger.

Constellation Junior model number is 852, but the MCU label says 846, and the MCU
datestamp is from 1984. There is no known Novag model 846, so perhaps they intended
to sell it in 1984, but changed their mind and delayed it until early 1985.

Hardware notes:
- PCB label: 100040/100039
- Hitachi HD6301V1P @ ~4MHz (LC oscillator)
- 8*8 chessboard buttons, 16+4 leds, piezo

TODO:
- if/when MAME supports an exit callback, hook up power-off switch to that

BTANB:
- slower chessboard button response when a piece moves 1 square vertically between
  the 4th and the 5th ranks, hence the 350ms set_delay

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

#include "emu.h"

#include "cpu/m6800/m6801.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "novag_constjr.lh"


namespace {

class constjr_state : public driver_device
{
public:
	constjr_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.0")
	{ }

	void constjr(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(power_off);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<hd6301v1_cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport m_inputs;

	emu_timer *m_standbytimer;
	u16 m_inp_mux = 0;

	// I/O handlers
	void board_w(u8 data);
	void control_w(u8 data);
	void ledsel_w(u8 data);
	u8 input_r();

	TIMER_CALLBACK_MEMBER(set_standby);
};

void constjr_state::machine_start()
{
	m_standbytimer = timer_alloc(FUNC(constjr_state::set_standby), this);

	// register for savestates
	save_item(NAME(m_inp_mux));
}



/*******************************************************************************
    Power
*******************************************************************************/

void constjr_state::machine_reset()
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
	m_maincpu->set_input_line(M6801_STBY_LINE, CLEAR_LINE);
}

TIMER_CALLBACK_MEMBER(constjr_state::set_standby)
{
	m_maincpu->set_input_line(M6801_STBY_LINE, ASSERT_LINE);
}

INPUT_CHANGED_MEMBER(constjr_state::power_off)
{
	if (newval && !m_maincpu->standby())
	{
		// NMI when power goes off, followed by STBY after a short delay
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
		m_standbytimer->adjust(attotime::from_msec(10));
	}
}



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

void constjr_state::board_w(u8 data)
{
	// P10-P17: input mux (chessboard), led data
	m_inp_mux = (m_inp_mux & 0x100) | (data ^ 0xff);
	m_display->write_mx(~data);
}

void constjr_state::control_w(u8 data)
{
	// P22: speaker out
	m_dac->write(BIT(~data, 2));

	// P24: input mux (buttons)
	m_inp_mux = (m_inp_mux & 0xff) | (~data << 4 & 0x100);
}

void constjr_state::ledsel_w(u8 data)
{
	// P35-P37: led select
	m_display->write_my(~data >> 5 & 7);
}

u8 constjr_state::input_r()
{
	// P40-P47: multiplexed inputs
	u8 data = 0;

	// read buttons
	if (m_inp_mux & 0x100)
		data |= m_inputs->read();

	// read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_rank(i, true);

	return ~data;
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( constjr )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_G) PORT_NAME("Go")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back / King")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_S) PORT_NAME("Sound / Queen")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_L) PORT_NAME("Set Level / Bishop")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game / Knight")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_U) PORT_NAME("Set Up / Rook")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_V) PORT_NAME("Verify / Pawn")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_B) PORT_NAME("Black/White")

	PORT_START("POWER") // needs to be triggered for nvram to work
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(constjr_state::power_off), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void constjr_state::constjr(machine_config &config)
{
	// basic machine hardware
	HD6301V1(config, m_maincpu, 4'000'000); // approximation, no XTAL
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(hd6301v1_cpu_device::nvram_set_battery));
	m_maincpu->standby_cb().append([this](int state) { if (state) m_display->clear(); });
	m_maincpu->out_p1_cb().set(FUNC(constjr_state::board_w));
	m_maincpu->out_p2_cb().set(FUNC(constjr_state::control_w));
	m_maincpu->out_p3_cb().set(FUNC(constjr_state::ledsel_w));
	m_maincpu->in_p4_cb().set(FUNC(constjr_state::input_r));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(350));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(3, 8);
	config.set_default_layout(layout_novag_constjr);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( constjr )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("hd6301v1p_d24_ucc-846", 0x0000, 0x1000, CRC(e73703e4) SHA1(03eb889a9981a6bdfca129ea84f984fbf7858d47) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1985, constjr, 0,      0,      constjr, constjr, constjr_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Constellation Junior", MACHINE_SUPPORTS_SAVE )



controlid.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holder:FelipeSanches
//
// Control ID x628
//
// This is a fingerprint reader device
//
// TODO: Emulate the other CPU board (supposedly runs the Linux kernel on
//       a dedicated SoC targeting image-processing typically used on
//       fingerprint readers). The SoC is labelled ZKSoftware ZK6001
//       and someone online suggested it may be a rebranded device from
//       Ingenic, so likely a MIPS32 or MIPS64.
//
//       It has a 32Mb flash-rom and an ethernet controller
//       (model is Realtek RTL8201BL)
//
//       While the 8051 board has a tiny buzzer (and a battery-backed RAM)
//       the other PCB interfaces with an audio speaker.
//
//       There's also an RJ45 connector (ethernet port) as well as a
//       DB9 connector which seems to be used for a serial interface.
//
//       Finally, there are 2 LEDs, a 128x64 LCD with blueish backlight
//       and a keypad with the following layout:
//
//       1  2  3   ESC
//       4  5  6   MENU
//       7  8  9   UP-ARROW
//       .  0  OK  DOWN-ARROW

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "video/nt7534.h"
#include "emupal.h"
#include "screen.h"


namespace {

class controlidx628_state : public driver_device
{
public:
	controlidx628_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_lcdc(*this, "nt7534")
	{ }

	void controlidx628(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void p0_w(uint8_t data);
	uint8_t p1_r();
	void p1_w(uint8_t data);
	uint8_t p2_r();
	uint8_t p3_r();
	void p3_w(uint8_t data);
	void controlidx628_palette(palette_device &palette) const;

	void io_map(address_map &map) ATTR_COLD;

	required_device<nt7534_device> m_lcdc;

	uint8_t m_p0_data = 0xff;
	uint8_t m_p1_data = 0xff;
	uint8_t m_p3_data = 0xff;
};


void controlidx628_state::machine_start()
{
	m_p0_data = 0xff;
	m_p1_data = 0xff;
	m_p3_data = 0xff;

	save_item(NAME(m_p0_data));
	save_item(NAME(m_p1_data));
	save_item(NAME(m_p3_data));
}

/*************************
* Memory map information *
*************************/

void controlidx628_state::io_map(address_map &map)
{
	map(0x8000, 0xffff).ram();
}


void controlidx628_state::p0_w(uint8_t data)
{
	m_p0_data = data;
}

uint8_t controlidx628_state::p1_r()
{
	// P1.1 is used for serial I/O; P1.4 and P1.5 are also used bidirectionally
	return 0xcd;
}

void controlidx628_state::p1_w(uint8_t data)
{
	if ((BIT(m_p1_data, 6) == 0) && (BIT(data, 6) == 1)) // on raising-edge of bit 6
	{
		m_lcdc->write(BIT(data, 7), m_p0_data);
	}
	// P1.0 is also used as a serial I/O clock
	m_p1_data = data;
}

uint8_t controlidx628_state::p2_r()
{
	// Low nibble used for input
	return 0xf0;
}

uint8_t controlidx628_state::p3_r()
{
	// P3.3 (INT1) and P3.4 (T0) used bidirectionally
	return 0xff;
}

void controlidx628_state::p3_w(uint8_t data)
{
	m_p3_data = data;
}

/*************************
*      Input ports       *
*************************/

//static INPUT_PORTS_START( controlidx628 )
//INPUT_PORTS_END

void controlidx628_state::controlidx628_palette(palette_device &palette) const
{
	// These colors were selected from a photo of the display
	// using the color-picker in Inkscape:
	palette.set_pen_color(0, rgb_t(0x06, 0x61, 0xee));
	palette.set_pen_color(1, rgb_t(0x00, 0x23, 0x84));
}

/*************************
*     Machine Driver     *
*************************/

void controlidx628_state::controlidx628(machine_config &config)
{
	// basic machine hardware
	at89s52_device &maincpu(AT89S52(config, "maincpu", XTAL(11'059'200)));
	maincpu.set_addrmap(AS_IO, &controlidx628_state::io_map);
	maincpu.port_out_cb<0>().set(FUNC(controlidx628_state::p0_w));
	maincpu.port_in_cb<1>().set(FUNC(controlidx628_state::p1_r));
	maincpu.port_out_cb<1>().set(FUNC(controlidx628_state::p1_w));
	maincpu.port_in_cb<2>().set(FUNC(controlidx628_state::p2_r));
	maincpu.port_in_cb<3>().set(FUNC(controlidx628_state::p3_r));
	maincpu.port_out_cb<3>().set(FUNC(controlidx628_state::p3_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // not accurate
	screen.set_size(132, 65);
	screen.set_visarea(3, 130, 0, 63);
	screen.set_screen_update("nt7534", FUNC(nt7534_device::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(controlidx628_state::controlidx628_palette), 2);

	NT7534(config, m_lcdc);
}


/*************************
*        Rom Load        *
*************************/

ROM_START( cidx628 )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "controlid_x628.u1",   0x0000, 0x2000, CRC(500d79b4) SHA1(5522115f2da622db389e067fcdd4bccb7aa8561a) )
ROM_END

} // anonymous namespace


COMP(200?, cidx628, 0, 0, controlidx628, 0, controlidx628_state, empty_init, "ControlID", "X628", MACHINE_NOT_WORKING|MACHINE_NO_SOUND)



corona.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Saitek Corona. Please refer to stratos.cpp for driver notes.

To be brief, Saitek Corona has two "HELIOS" chips, I/O addressing is completely
different compared to Stratos/Turbo King.

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

#include "emu.h"
#include "stratos.h"

#include "cpu/m6502/w65c02.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "saitek_corona.lh"


namespace {

// note: sub-class of stratos_base_state (see stratos.h, stratos.cpp)

class corona_state : public stratos_base_state
{
public:
	corona_state(const machine_config &mconfig, device_type type, const char *tag) :
		stratos_base_state(mconfig, type, tag),
		m_rombank(*this, "rombank"),
		m_board(*this, "board"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// machine configs
	void corona(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	memory_view m_rombank;
	required_device<sensorboard_device> m_board;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<8+1> m_inputs;

	u8 m_control1 = 0;
	u8 m_control2 = 0;
	u8 m_select1 = 0;
	u8 m_select2 = 0;
	u8 m_led_data1 = 0;
	u8 m_led_data2 = 0;

	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_leds();
	void leds1_w(u8 data);
	void leds2_w(u8 data);
	void select1_w(u8 data);
	void select2_w(u8 data);
	void control1_w(u8 data);
	void control2_w(u8 data);
	u8 control1_r();
	u8 control2_r();
	u8 chessboard_r();
	void lcd_reset_w(u8 data);
};

void corona_state::machine_start()
{
	stratos_base_state::machine_start();

	// register for savestates
	save_item(NAME(m_control1));
	save_item(NAME(m_control2));
	save_item(NAME(m_select1));
	save_item(NAME(m_select2));
	save_item(NAME(m_led_data1));
	save_item(NAME(m_led_data2));
}

void corona_state::machine_reset()
{
	stratos_base_state::machine_reset();

	m_control2 = 0;
	m_rombank.select(0);
}



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

// HELIOS

void corona_state::update_leds()
{
	// button leds
	m_display->matrix_partial(0, 2, 1 << (m_control1 >> 5 & 1), ~m_led_data1 & 0xff);
	m_display->write_row(2, ~m_select1 >> 4 & 0xf);

	// chessboard leds
	m_display->matrix_partial(3, 8, 1 << (m_select1 & 0xf), m_led_data2);
}

void corona_state::leds1_w(u8 data)
{
	// d0-d7: button led data
	m_led_data1 = data;
	update_leds();
}

void corona_state::leds2_w(u8 data)
{
	// d0-d7: chessboard led data
	m_led_data2 = data;
	update_leds();
}

void corona_state::select1_w(u8 data)
{
	// d0-d3: chessboard led select
	// d4-d7: black/white leds
	m_select1 = data;
	update_leds();
}

void corona_state::select2_w(u8 data)
{
	// d0-d3: input mux
	// d4-d7: lcd data
	m_select2 = data;
}

void corona_state::control1_w(u8 data)
{
	// d5: button led select
	m_control1 = data;
	update_leds();

	// d6: speaker out
	m_dac->write(data >> 6 & 1);
}

void corona_state::control2_w(u8 data)
{
	// d0,d1: rombank
	m_rombank.select(data & 3);

	// d2 rising edge: write to lcd
	if (~m_control2 & data & 4)
		lcd_data_w(m_select2 >> 4);

	// d6 rising edge: power-off request
	if (~m_control2 & data & 0x40)
		power_off();

	m_control2 = data;
}

u8 corona_state::control1_r()
{
	u8 data = 0;

	// d5: lcd status flag?
	if (m_lcd_ready)
		data |= 0x20;

	// d6: FREQ. SEL related?

	// d7: battery low
	data |= m_inputs[8]->read() << 7;

	return data;
}

u8 corona_state::control2_r()
{
	u8 data = 0;
	u8 sel = m_select2 & 0xf;

	// d5-d7: read button panel
	if (sel < 8)
		data |= m_inputs[sel]->read() << 5;

	return data;
}

u8 corona_state::chessboard_r()
{
	// d0-d7: chessboard sensors
	return ~m_board->read_file(m_select2 & 0xf);
}

void corona_state::lcd_reset_w(u8 data)
{
	// reset lcd?
	m_lcd_ready = true;
	m_lcd_count = 0;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void corona_state::main_map(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("nvram");
	map(0x2000, 0x2000).w(FUNC(corona_state::select1_w));
	map(0x2400, 0x2400).rw(FUNC(corona_state::chessboard_r), FUNC(corona_state::leds1_w));
	map(0x2600, 0x2600).rw(FUNC(corona_state::control1_r), FUNC(corona_state::control1_w));
	map(0x6000, 0x6000).w(FUNC(corona_state::select2_w));
	map(0x6200, 0x6200).w(FUNC(corona_state::lcd_reset_w));
	map(0x6400, 0x6400).w(FUNC(corona_state::leds2_w));
	map(0x6600, 0x6600).rw(FUNC(corona_state::control2_r), FUNC(corona_state::control2_w));

	map(0x8000, 0xffff).view(m_rombank);
	m_rombank[0](0x8000, 0xffff).rom().region("maincpu", 0x0000);
	m_rombank[1](0x8000, 0xffff).rom().region("maincpu", 0x8000);
	m_rombank[2](0x8000, 0xffff).r("extrom", FUNC(generic_slot_device::read_rom));
	m_rombank[3](0x8000, 0xffff).lr8(NAME([]() { return 0xff; }));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( corona )
	PORT_INCLUDE( saitek_stratos )

	PORT_MODIFY("IN.5")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("LCD Scroll")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void corona_state::corona(machine_config &config)
{
	// basic machine hardware
	W65C02(config, m_maincpu, 5_MHz_XTAL); // see change_cpu_freq
	m_maincpu->set_addrmap(AS_PROGRAM, &corona_state::main_map);
	m_maincpu->set_periodic_int(FUNC(corona_state::irq0_line_hold), attotime::from_hz(183));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(200));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(3+8, 8);
	config.set_default_layout(layout_saitek_corona);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);

	// extension rom
	GENERIC_SOCKET(config, "extrom", generic_plain_slot, "saitek_egr");
	SOFTWARE_LIST(config, "cart_list").set_original("saitek_egr").set_filter("egr2");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( corona )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("w2_708g_u2.u2", 0x0000, 0x8000, CRC(52568bb4) SHA1(83fe91787e17bbefc2b3ec651ddb11c88990060d) )
	ROM_LOAD("bw2_708a_u3.u3", 0x8000, 0x8000, CRC(32848f73) SHA1(a447543e3eb4757f9afed26fde77b66985eb96a7) )
ROM_END

ROM_START( coronaa )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("w2_a14c_u2.u2", 0x0000, 0x8000, CRC(be82e199) SHA1(cfcc573774b6907ed137dca01fa7f3fce493a89f) )
	ROM_LOAD("bw2_a14_u3.u3", 0x8000, 0x8000, CRC(abe87285) SHA1(b15f7ddeac78d252cf413ba4085523e44c6d15df) )
ROM_END

ROM_START( coronab )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("w2_a14_u2.u2", 0x0000, 0x8000, CRC(92a44b92) SHA1(06e25421c34cf9c30c585b261d04b823c4a39b36) )
	ROM_LOAD("bw2_a14_u3.u3", 0x8000, 0x8000, CRC(abe87285) SHA1(b15f7ddeac78d252cf413ba4085523e44c6d15df) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT   COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1990, corona,   0,       0,      corona,  corona, corona_state, empty_init, "Saitek / Heuristic Software", "Kasparov Corona (ver. D+)", MACHINE_SUPPORTS_SAVE ) // aka Corona II
SYST( 1988, coronaa,  corona,  0,      corona,  corona, corona_state, empty_init, "Saitek / Heuristic Software", "Kasparov Corona (ver. C, set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1988, coronab,  corona,  0,      corona,  corona, corona_state, empty_init, "Saitek / Heuristic Software", "Kasparov Corona (ver. C, set 2)", MACHINE_SUPPORTS_SAVE )



cortex.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Powertran Cortex

2012-04-20 Skeleton driver.

ftp://ftp.whtech.com/Powertran Cortex/
http://www.powertrancortex.com/index.html

Uses Texas Instruments parts and similar to other TI computers.
It was designed by TI engineers, so it may perhaps be a clone
of another TI or the Geneve.

Chips:
TMS9995   - CPU
TMS9929   - Video
TMS9911   - DMA to floppy (unemulated device)
TMS9909   - Floppy Disk Controller (unemulated device)
TMS9902   - UART (x2) (device not usable with rs232.h)
AY-5-2376 - Keyboard controller

All input to be in uppercase. Note that "lowercase" is just smaller uppercase,
and is not acceptable as input.

There's no option in BASIC to produce sound. It will beep if an invalid key
(usually a control key) is pressed.

To clear the screen press Ctrl L.

TODO:
- Unemulated devices
- Cassette
- Keyboard REPEAT circuit
- Memory mapping unit (74LS610)
- Various CRU I/O

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

#include "emu.h"

#include "cpu/tms9900/tms9995.h"
#include "machine/74259.h"
#include "machine/kr2376.h"
//#include "machine/tms9902.h"
#include "video/tms9928a.h"
#include "sound/beep.h"

#include "speaker.h"


namespace {

class cortex_state : public driver_device
{
public:
	cortex_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_bank1(*this, "bank1")
		, m_beep(*this, "beeper")
		, m_ay2376(*this, "ay2376")
		, m_pb(*this, "PB")
		, m_io_dsw(*this, "DSW")
	{ }

	void cortex(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void kbd_strobe(int state);
	void kbd_ack_w(int state);
	void romsw_w(int state);
	void vdp_int_w(int state);
	u8 pio_r(offs_t offset);
	u8 kbd_r(offs_t offset);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	bool m_kbd_ack = 0;
	bool m_vdp_int = 0;
	u8 m_kbd_data  = 0U;

	required_device<tms9995_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_memory_bank    m_bank1;
	required_device<beep_device> m_beep;
	required_device<kr2376_device> m_ay2376;
	required_ioport m_pb;
	required_ioport m_io_dsw;
};

void cortex_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).ram().share(m_ram).bankr(m_bank1);
	map(0x8000, 0xefff).ram();
	map(0xf100, 0xf11f).ram(); // memory mapping unit
	map(0xf120, 0xf121).rw("crtc", FUNC(tms9928a_device::read), FUNC(tms9928a_device::write));
	//map(0xf140, 0xf147) // fdc tms9909
}

void cortex_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x000f).mirror(0x30).w("control", FUNC(ls259_device::write_d0));
	map(0x0000, 0x000f).r(FUNC(cortex_state::pio_r));
	map(0x0010, 0x001f).r(FUNC(cortex_state::kbd_r));
	//map(0x0080, 0x00bf).rw("uart1", FUNC(tms9902_device::cruread), FUNC(tms9902_device::cruwrite)); // RS232 (r12 = 80-bf)
	//map(0x0180, 0x01bf).rw("uart2", FUNC(tms9902_device::cruread), FUNC(tms9902_device::cruwrite)); // Cassette (r12 = 180-1bf)
	//map(0x01c0, 0x01ff).rw("dma", FUNC(tms9911_device::read), FUNC(tms9911_device::write)); // r12 = 1c0-1fe
	//map(0x0800, 0x080f).w(cortex_state::cent_data_w)); // r12 = 800-80e
	//map(0x0810, 0x0811).w(FUNC(cortex_state::cent_strobe_w)); // r12 = 810
	//map(0x0812, 0x0813).r(FUNC(cortex_state::cent_stat_r)); // CRU 409 (r12 = 812)
}

/* Input ports */
static INPUT_PORTS_START( cortex )
	PORT_START("X0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_END)        PORT_CHAR(UCHAR_MAMEKEY(END))     PORT_NAME("Edit")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X1")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT)     PORT_CHAR(UCHAR_MAMEKEY(INSERT))  PORT_NAME("Insert")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)        PORT_CHAR(UCHAR_MAMEKEY(DEL))     PORT_NAME("Delete")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME)       PORT_CHAR(UCHAR_MAMEKEY(HOME))   PORT_NAME("Home")
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD)                               PORT_CHAR('_')

	PORT_START("X3")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_CHAR('0')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))  PORT_NAME(u8"\u2190") // U+2190 = ←
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)                   PORT_NAME("Return")
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))  PORT_NAME(u8"\u2193") // U+2193 = ↓
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(8)                    PORT_NAME("Rub Out")

	PORT_START("X4")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)          PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)          PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)          PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)          PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)          PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)          PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z') PORT_CHAR('Z')

	PORT_START("X5")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)          PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)          PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)          PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)          PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)          PORT_CHAR('c') PORT_CHAR('G')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)          PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)          PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)          PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP)       PORT_CHAR(UCHAR_MAMEKEY(PGUP))   PORT_NAME("Clear")
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC))    PORT_NAME("Escape")

	PORT_START("X6")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)          PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)          PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)          PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)          PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)          PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))  PORT_NAME(u8"\u2192") // U+2192 = →
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))     PORT_NAME(u8"\u2191") // U+2191 = ↑

	PORT_START("X7")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('\\') PORT_CHAR('|')

	PORT_START("PB")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift")      PORT_CODE(KEYCODE_LSHIFT)   PORT_CODE(KEYCODE_RSHIFT)    PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Ctrl")       PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)  PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Caps Lock")  PORT_CODE(KEYCODE_CAPSLOCK)                 PORT_TOGGLE  PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Graph")      PORT_CODE(KEYCODE_PGDN)                                  PORT_CHAR(UCHAR_MAMEKEY(PGDN))

	PORT_START("DSW")
	PORT_DIPNAME(0x04, 0x00, "DISK SIZE")
	PORT_DIPSETTING(   0x04, "20cm")
	PORT_DIPSETTING(   0x00, "13cm")
	PORT_DIPNAME(0x08, 0x08, "DISK DENSITY")
	PORT_DIPSETTING(   0x08, "Double")
	PORT_DIPSETTING(   0x00, "Single")
INPUT_PORTS_END

u8 cortex_state::pio_r(offs_t offset)
{
	switch (offset)
	{
	case 5:
		return m_kbd_ack;

	case 6:
		return m_vdp_int;

	case 2:
	case 3:
		return BIT(m_io_dsw->read(), offset);

	default:
		return 1;
	}
}

u8 cortex_state::kbd_r(offs_t offset)
{
	return BIT(m_kbd_data, offset);
}

void cortex_state::kbd_ack_w(int state)
{
	if (!state)
	{
		m_maincpu->set_input_line(INT_9995_INT4, CLEAR_LINE);
		m_kbd_ack = 1;
	}
}

void cortex_state::romsw_w(int state)
{
	m_bank1->set_entry(state ? 0 : 1);
}

void cortex_state::vdp_int_w(int state)
{
	m_vdp_int = state ? 0 : 1;  // change polarity to match mame
}

void cortex_state::kbd_strobe(int state)
{
	if (state)
	{
		m_kbd_data = m_ay2376->data_r() & 0x5f;

		// Caps Lock
		if (BIT(m_pb->read(), 2))
			m_kbd_data |= BIT(m_ay2376->data_r(), 7) << 5;
		else
			m_kbd_data |= BIT(m_ay2376->data_r(), 5) << 5;

		// Graphics
		m_kbd_data |= BIT(m_pb->read(), 3) << 7;

		m_kbd_ack = 0;
		m_maincpu->set_input_line(INT_9995_INT4, ASSERT_LINE);
	}
}

void cortex_state::machine_start()
{
	m_bank1->configure_entry(0, m_ram);
	m_bank1->configure_entry(1, m_rom);

	save_item(NAME(m_kbd_ack));
	save_item(NAME(m_vdp_int));
	save_item(NAME(m_kbd_data));
}

void cortex_state::machine_reset()
{
	m_kbd_ack = 1;
	m_vdp_int = 0;
	m_beep->set_state(0);
	m_bank1->set_entry(1);
	m_maincpu->ready_line(ASSERT_LINE);
	m_maincpu->reset_line(ASSERT_LINE);
}

void cortex_state::cortex(machine_config &config)
{
	/* basic machine hardware */
	/* TMS9995 CPU @ 12.0 MHz */
	// Standard variant, no overflow int
	// No lines connected yet
	TMS9995(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &cortex_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &cortex_state::io_map);

	ls259_device &control(LS259(config, "control")); // IC64
	//control.q_out_cb<0>().set(FUNC(cortex_state::basic_led_w));
	control.q_out_cb<1>().set(FUNC(cortex_state::kbd_ack_w));
	//control.q_out_cb<2>().set(FUNC(cortex_state::ebus_int_ack_w));
	//control.q_out_cb<3>().set(FUNC(cortex_state::ebus_to_en_w));
	//control.q_out_cb<4>().set(FUNC(cortex_state::disk_size_w));
	control.q_out_cb<5>().set(FUNC(cortex_state::romsw_w));
	control.q_out_cb<6>().set("beeper", FUNC(beep_device::set_state));

	/* video hardware */
	tms9929a_device &crtc(TMS9929A(config, "crtc", 10.738635_MHz_XTAL));
	crtc.set_screen("screen");
	crtc.int_callback().set_inputline(m_maincpu, INT_9995_INT1);
	crtc.int_callback().append(FUNC(cortex_state::vdp_int_w));
	crtc.set_vram_size(0x4000);
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	KR2376_ST(config, m_ay2376, 50000);
	m_ay2376->x<0>().set_ioport("X0");
	m_ay2376->x<1>().set_ioport("X1");
	m_ay2376->x<2>().set_ioport("X2");
	m_ay2376->x<3>().set_ioport("X3");
	m_ay2376->x<4>().set_ioport("X4");
	m_ay2376->x<5>().set_ioport("X5");
	m_ay2376->x<6>().set_ioport("X6");
	m_ay2376->x<7>().set_ioport("X7");
	m_ay2376->shift().set_ioport("PB").bit(0);
	m_ay2376->control().set_ioport("PB").bit(1);
	m_ay2376->strobe().set(FUNC(cortex_state::kbd_strobe));

	//tms9902_device &uart1(TMS9902(config, "uart1", 12_MHz_XTAL / 4));
	//uart1.int_cb().set_inputline(m_maincpu, INT_9995_INT4);
	//tms9902_device &uart2(TMS9902(config, "uart2", 12_MHz_XTAL / 4));
	//uart2.int_cb().set_inputline(m_maincpu, INT_9995_INT4);

	/* Sound */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 950); // guess
	m_beep->add_route(ALL_OUTPUTS, "mono", 0.05);
}

/* ROM definition */
ROM_START( cortex )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "basic", "Cortex BIOS")
	ROMX_LOAD( "cortex.ic47", 0x0000, 0x2000, CRC(bdb8c7bd) SHA1(340829dcb7a65f2e830fd5aff82a312e3ed7918f), ROM_BIOS(0))
	ROMX_LOAD( "cortex.ic46", 0x2000, 0x2000, CRC(4de459ea) SHA1(00a42fe556d4ffe1f85b2ce369f544b07fbd06d9), ROM_BIOS(0))
	ROMX_LOAD( "cortex.ic45", 0x4000, 0x2000, CRC(b0c9b6e8) SHA1(4e20c3f0b7546b803da4805cd3b8616f96c3d923), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "forth", "FIG-Forth")
	ROMX_LOAD( "forth.ic47",  0x0000, 0x2000, CRC(999034be) SHA1(0dcc7404c38aa0ae913101eb0aa98da82104b5d4), ROM_BIOS(1))
	ROMX_LOAD( "forth.ic46",  0x2000, 0x2000, CRC(8eca54cc) SHA1(0f1680e941ef60bb9bde9a4b843b78f30dff3202), ROM_BIOS(1))
ROM_END

} // anonymous namespace


//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT         COMPANY                  FULLNAME  FLAGS
COMP( 1982, cortex, 0,      0,      cortex,  cortex, cortex_state, empty_init, "Powertran Cybernetics", "Cortex", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



cosmicos.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

    COSMICOS

    http://retro.hansotten.nl/index.php?page=1802-cosmicos

    Press G to start, and to enable the debugger (if -debug used).
    The video options include 8-digit LEDs, 2-digit LEDs, and CRT,
    of which the default is the 8-digit LEDs. Unknown how to enable
    the others.

    HEX-monitor

    0 - start user program
    1 - inspect and/or change memory
    2 - write memory block to cassette
    3 - read memory block from cassette
    4 - move memory block
    5 - write memory block to EPROM
    C - start user program from address 0000

*/

/*

    TODO:

    - display interface INH
    - 2 segment display
    - single step
    - ascii monitor
    - PPI 8255
    - Floppy WD1793
    - COM8017 UART to printer

*/

#include "emu.h"

#include "cpu/cosmac/cosmac.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "machine/ram.h"
#include "machine/rescap.h"
#include "machine/timer.h"
#include "sound/cdp1864.h"
#include "sound/spkrdev.h"
#include "video/dm9368.h"
#include "screen.h"
#include "speaker.h"

#include "cosmicos.lh"

namespace {

#define CDP1802_TAG     "ic19"
#define CDP1864_TAG     "ic3"
#define DM9368_TAG      "ic10"
#define SCREEN_TAG      "screen"

enum
{
	LED_RUN = 0,
	LED_LOAD,
	LED_PAUSE,
	LED_RESET,
	LED_D7,
	LED_D6,
	LED_D5,
	LED_D4,
	LED_D3,
	LED_D2,
	LED_D1,
	LED_D0,
	LED_Q,
	LED_CASSETTE
};

class cosmicos_state : public driver_device
{
public:
	cosmicos_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_digit(0),
		m_maincpu(*this, CDP1802_TAG),
		m_cti(*this, CDP1864_TAG),
		m_led(*this, DM9368_TAG),
		m_cassette(*this, "cassette"),
		m_speaker(*this, "speaker"),
		m_ram(*this, RAM_TAG),
		m_rom(*this, CDP1802_TAG),
		m_key_row(*this, {"Y1", "Y2", "Y3", "Y4"}),
		m_io_data(*this, "DATA"),
		m_special(*this, "SPECIAL"),
		m_buttons(*this, "BUTTONS"),
		m_digits(*this, "digit%u", 0U),
		m_leds(*this, "led%u", 0U)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER( data );
	DECLARE_INPUT_CHANGED_MEMBER( enter );
	DECLARE_INPUT_CHANGED_MEMBER( single_step );
	DECLARE_INPUT_CHANGED_MEMBER( run );
	DECLARE_INPUT_CHANGED_MEMBER( load );
	DECLARE_INPUT_CHANGED_MEMBER( cosmicos_pause );
	DECLARE_INPUT_CHANGED_MEMBER( reset );
	DECLARE_INPUT_CHANGED_MEMBER( clear_data );
	DECLARE_INPUT_CHANGED_MEMBER( memory_protect );
	DECLARE_INPUT_CHANGED_MEMBER( memory_disable );

	void init_cosmicos();
	void cosmicos(machine_config &config);

private:
	uint8_t read(offs_t offset);
	void write(offs_t offset, uint8_t data);
	uint8_t video_off_r();
	uint8_t video_on_r();
	void audio_latch_w(uint8_t data);
	uint8_t hex_keyboard_r();
	void hex_keylatch_w(uint8_t data);
	uint8_t reset_counter_r();
	void segment_w(uint8_t data);
	uint8_t data_r();
	void display_w(uint8_t data);
	uint8_t dma_r();
	void sc_w(uint8_t data);
	void set_cdp1802_mode(int mode);
	void clear_input_data();
	void dmaout_w(int state);
	void efx_w(int state);
	int wait_r();
	int clear_r();
	int ef1_r();
	int ef2_r();
	int ef3_r();
	int ef4_r();
	void q_w(int state);
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);
	TIMER_DEVICE_CALLBACK_MEMBER(digit_tick);
	TIMER_DEVICE_CALLBACK_MEMBER(int_tick);
	void cosmicos_io(address_map &map) ATTR_COLD;
	void cosmicos_mem(address_map &map) ATTR_COLD;

	/* CPU state */
	int m_wait = 0;
	int m_clear = 0;
	int m_sc1 = 0;

	/* memory state */
	uint8_t m_data = 0U;
	int m_boot = 0;
	int m_ram_protect = 0;
	int m_ram_disable = 0;

	/* keyboard state */
	uint8_t m_keylatch = 0U;

	/* display state */
	uint8_t m_segment = 0U;
	int m_digit = 0;
	int m_counter = 0;
	int m_q = 0;
	int m_dmaout = 0;
	int m_efx = 0;
	int m_video_on = 0;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	required_device<cosmac_device> m_maincpu;
	required_device<cdp1864_device> m_cti;
	required_device<dm9368_device> m_led;
	required_device<cassette_image_device> m_cassette;
	required_device<speaker_sound_device> m_speaker;
	required_device<ram_device> m_ram;
	required_memory_region m_rom;
	required_ioport_array<4> m_key_row;
	required_ioport m_io_data;
	required_ioport m_special;
	required_ioport m_buttons;
	output_finder<10> m_digits;
	output_finder<14> m_leds;
};

enum
{
	MODE_RUN,
	MODE_LOAD,
	MODE_PAUSE,
	MODE_RESET
};

/* Read/Write Handlers */

uint8_t cosmicos_state::read(offs_t offset)
{
	if (m_boot) offset |= 0xc000;

	uint8_t data = 0;

	if (offset < 0xc000)
	{
		// TODO
	}
	else if (offset < 0xd000)
	{
		data = m_rom->base()[offset & 0xfff];
	}
	else if (!m_ram_disable && (offset >= 0xff00))
	{
		data = m_ram->pointer()[offset & 0xff];
	}

	return data;
}

void cosmicos_state::write(offs_t offset, uint8_t data)
{
	if (m_boot) offset |= 0xc000;

	if (offset < 0xc000)
	{
		// TODO
	}
	else if (!m_ram_disable && !m_ram_protect && (offset >= 0xff00))
	{
		m_ram->pointer()[offset & 0xff] = data;
	}
}

uint8_t cosmicos_state::video_off_r()
{
	uint8_t data = 0;

	if (!m_q)
	{
		data = m_cti->dispoff_r();
	}

	return data;
}

uint8_t cosmicos_state::video_on_r()
{
	uint8_t data = 0;

	if (!m_q)
	{
		data = m_cti->dispon_r();
	}

	return data;
}

void cosmicos_state::audio_latch_w(uint8_t data)
{
	if (m_q)
	{
		m_cti->tone_latch_w(data);
	}
}

uint8_t cosmicos_state::hex_keyboard_r()
{
	uint8_t data = 0;

	for (int i = 0; i < 4; i++)
	{
		if (BIT(m_keylatch, i))
		{
			uint8_t keydata = m_key_row[i]->read();

			if (BIT(keydata, 0)) data |= 0x01;
			if (BIT(keydata, 1)) data |= 0x02;
			if (BIT(keydata, 2)) data |= 0x04;
			if (BIT(keydata, 3)) data |= 0x06;
		}
	}

	return data;
}

void cosmicos_state::hex_keylatch_w(uint8_t data)
{
	m_keylatch = data & 0x0f;
}

uint8_t cosmicos_state::reset_counter_r()
{
	m_counter = 0;

	return 0;
}

void cosmicos_state::segment_w(uint8_t data)
{
	m_counter++;

	if (m_counter == 10)
	{
		m_counter = 0;
	}

	if ((m_counter > 0) && (m_counter < 9))
	{
		m_digits[10 - m_counter] = data;
	}
}

uint8_t cosmicos_state::data_r()
{
	return m_data;
}

void cosmicos_state::display_w(uint8_t data)
{
	m_segment = data;
}

/* Memory Maps */

void cosmicos_state::cosmicos_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(cosmicos_state::read), FUNC(cosmicos_state::write));
}

void cosmicos_state::cosmicos_io(address_map &map)
{
//  map(0x00, 0x00)
	map(0x01, 0x01).r(FUNC(cosmicos_state::video_on_r));
	map(0x02, 0x02).rw(FUNC(cosmicos_state::video_off_r), FUNC(cosmicos_state::audio_latch_w));
//  map(0x03, 0x03)
//  map(0x04, 0x04)
	map(0x05, 0x05).rw(FUNC(cosmicos_state::hex_keyboard_r), FUNC(cosmicos_state::hex_keylatch_w));
	map(0x06, 0x06).rw(FUNC(cosmicos_state::reset_counter_r), FUNC(cosmicos_state::segment_w));
	map(0x07, 0x07).rw(FUNC(cosmicos_state::data_r), FUNC(cosmicos_state::display_w));
}

/* Input Ports */

INPUT_CHANGED_MEMBER( cosmicos_state::data )
{
	uint8_t data = m_io_data->read();
	int i;

	for (i = 0; i < 8; i++)
	{
		if (!BIT(data, i))
		{
			m_data |= (1 << i);
			m_leds[LED_D0 - i] = 1;
		}
	}
}

INPUT_CHANGED_MEMBER( cosmicos_state::enter )
{
	if (!newval && !m_wait && !m_clear)
	{
		m_maincpu->set_input_line(COSMAC_INPUT_LINE_DMAIN, ASSERT_LINE);
	}
}

INPUT_CHANGED_MEMBER( cosmicos_state::single_step )
{
	// if in PAUSE mode, set RUN mode until TPB=active
}

void cosmicos_state::set_cdp1802_mode(int mode)
{
	m_leds[LED_RUN] = 0;
	m_leds[LED_LOAD] = 0;
	m_leds[LED_PAUSE] = 0;
	m_leds[LED_RESET] = 0;

	switch (mode)
	{
	case MODE_RUN:
		m_leds[LED_RUN] = 1;

		m_wait = 1;
		m_clear = 1;
		break;

	case MODE_LOAD:
		m_leds[LED_LOAD] = 1;

		m_wait = 0;
		m_clear = 0;
		break;

	case MODE_PAUSE:
		m_leds[LED_PAUSE] = 1;

		m_wait = 1;
		m_clear = 0;
		break;

	case MODE_RESET:
		m_maincpu->set_input_line(COSMAC_INPUT_LINE_INT, CLEAR_LINE);
		m_maincpu->set_input_line(COSMAC_INPUT_LINE_DMAIN, CLEAR_LINE);

		m_wait = 1;
		m_clear = 0;
		m_boot = 1;

		m_leds[LED_RESET] = 1;
		break;
	}
}

INPUT_CHANGED_MEMBER( cosmicos_state::run )             { if (!newval) set_cdp1802_mode(MODE_RUN); }
INPUT_CHANGED_MEMBER( cosmicos_state::load )            { if (!newval) set_cdp1802_mode(MODE_LOAD); }
INPUT_CHANGED_MEMBER( cosmicos_state::cosmicos_pause )  { if (!newval) set_cdp1802_mode(MODE_PAUSE); }
INPUT_CHANGED_MEMBER( cosmicos_state::reset )           { if (!newval) set_cdp1802_mode(MODE_RESET); }

void cosmicos_state::clear_input_data()
{
	int i;

	m_data = 0;

	for (i = 0; i < 8; i++)
	{
		m_leds[LED_D0 - i] = 0;
	}
}

INPUT_CHANGED_MEMBER( cosmicos_state::clear_data )
{
	clear_input_data();
}

INPUT_CHANGED_MEMBER( cosmicos_state::memory_protect )
{
	m_ram_protect = newval;
}

INPUT_CHANGED_MEMBER( cosmicos_state::memory_disable )
{
	m_ram_disable = newval;
}

static INPUT_PORTS_START( cosmicos )
	PORT_START("DATA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D0") PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cosmicos_state::data), 0)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D1") PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cosmicos_state::data), 0)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D2") PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cosmicos_state::data), 0)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D3") PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cosmicos_state::data), 0)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D4") PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cosmicos_state::data), 0)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D5") PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cosmicos_state::data), 0)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D6") PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cosmicos_state::data), 0)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D7") PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cosmicos_state::data), 0)

	PORT_START("BUTTONS")
	PORT_BIT( 0x001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cosmicos_state::enter), 0)
	PORT_BIT( 0x002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_NAME("Single Step") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cosmicos_state::single_step), 0)
	PORT_BIT( 0x004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_NAME("Run") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cosmicos_state::run), 0)
	PORT_BIT( 0x008, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_NAME("Load") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cosmicos_state::load), 0)
	PORT_BIT( 0x010, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_NAME(DEF_STR( Pause )) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cosmicos_state::cosmicos_pause), 0)
	PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_NAME("Reset") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cosmicos_state::reset), 0)
	PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME("Clear Data") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cosmicos_state::clear_data), 0)
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_NAME("Memory Protect") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cosmicos_state::memory_protect), 0) PORT_TOGGLE
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_NAME("Memory Disable") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cosmicos_state::memory_disable), 0) PORT_TOGGLE

	PORT_START("Y1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3')

	PORT_START("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7')

	PORT_START("Y3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')

	PORT_START("Y4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("SPECIAL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RET") PORT_CODE(KEYCODE_F1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DEC") PORT_CODE(KEYCODE_F2)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("REQ") PORT_CODE(KEYCODE_F3)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SEQ") PORT_CODE(KEYCODE_F4)
INPUT_PORTS_END

/* Video */

TIMER_DEVICE_CALLBACK_MEMBER(cosmicos_state::digit_tick)
{
	m_digit ^= 1;

	m_digits[m_digit] = m_segment;
}

TIMER_DEVICE_CALLBACK_MEMBER(cosmicos_state::int_tick)
{
	m_maincpu->set_input_line(COSMAC_INPUT_LINE_INT, ASSERT_LINE);
}

void cosmicos_state::dmaout_w(int state)
{
	m_dmaout = state;
}

void cosmicos_state::efx_w(int state)
{
	m_efx = state;
}

/* CDP1802 Configuration */

int cosmicos_state::wait_r()
{
	return m_wait;
}

int cosmicos_state::clear_r()
{
	return m_clear;
}

int cosmicos_state::ef1_r()
{
	uint8_t special = m_special->read();

	return BIT(special, 0);
}

int cosmicos_state::ef2_r()
{
	uint8_t special = m_special->read();
	int casin = (m_cassette)->input() < 0.0;

	m_leds[LED_CASSETTE] = casin;

	return BIT(special, 1) | BIT(special, 3) | casin;
}

int cosmicos_state::ef3_r()
{
	uint8_t special = m_special->read();

	return BIT(special, 2) | BIT(special, 3);
}

int cosmicos_state::ef4_r()
{
	return BIT(m_buttons->read(), 0);
}

void cosmicos_state::q_w(int state)
{
	/* cassette */
	m_cassette->output(state ? +1.0 : -1.0);

	/* boot */
	if (state) m_boot = 0;

	/* CDP1864 audio enable */
	m_cti->aoe_w(state);

	m_q = state;
}

uint8_t cosmicos_state::dma_r()
{
	return m_data;
}

void cosmicos_state::sc_w(uint8_t data)
{
	int sc1 = BIT(data, 1);

	if (m_sc1 && !sc1)
	{
		clear_input_data();
	}

	if (sc1)
	{
		m_maincpu->set_input_line(COSMAC_INPUT_LINE_INT, CLEAR_LINE);
		m_maincpu->set_input_line(COSMAC_INPUT_LINE_DMAIN, CLEAR_LINE);
	}

	m_sc1 = sc1;
}

/* Machine Initialization */

void cosmicos_state::machine_start()
{
	m_digits.resolve();
	m_leds.resolve();

	/* register for state saving */
	save_item(NAME(m_wait));
	save_item(NAME(m_clear));
	save_item(NAME(m_sc1));
	save_item(NAME(m_data));
	save_item(NAME(m_boot));
	save_item(NAME(m_ram_protect));
	save_item(NAME(m_ram_disable));
	save_item(NAME(m_keylatch));
	save_item(NAME(m_segment));
	save_item(NAME(m_digit));
	save_item(NAME(m_counter));
	save_item(NAME(m_q));
	save_item(NAME(m_dmaout));
	save_item(NAME(m_efx));
	save_item(NAME(m_video_on));
}

void cosmicos_state::machine_reset()
{
	set_cdp1802_mode(MODE_RESET);
}

/* Quickload */

QUICKLOAD_LOAD_MEMBER(cosmicos_state::quickload_cb)
{
	// FIXME: comment says "RAM" but data is loaded to ROM region - which should it be?
	int const size = image.length();
	if (size > m_rom->bytes())
	{
		return std::make_pair(
				image_error::INVALIDLENGTH,
				util::string_format("%u-byte image file is too large for %u-byte ROM", size, m_rom->bytes()));
	}

	uint8_t *const ptr = m_rom->base();

	// load image to RAM
	image.fread(ptr, size);

	return std::make_pair(std::error_condition(), std::string());
}

/* Machine Driver */

void cosmicos_state::cosmicos(machine_config &config)
{
	/* basic machine hardware */
	CDP1802(config, m_maincpu, 1.75_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &cosmicos_state::cosmicos_mem);
	m_maincpu->set_addrmap(AS_IO, &cosmicos_state::cosmicos_io);
	m_maincpu->wait_cb().set(FUNC(cosmicos_state::wait_r));
	m_maincpu->clear_cb().set(FUNC(cosmicos_state::clear_r));
	m_maincpu->ef1_cb().set(FUNC(cosmicos_state::ef1_r));
	m_maincpu->ef2_cb().set(FUNC(cosmicos_state::ef2_r));
	m_maincpu->ef3_cb().set(FUNC(cosmicos_state::ef3_r));
	m_maincpu->ef4_cb().set(FUNC(cosmicos_state::ef4_r));
	m_maincpu->q_cb().set(FUNC(cosmicos_state::q_w));
	m_maincpu->dma_rd_cb().set(FUNC(cosmicos_state::dma_r));
	m_maincpu->sc_cb().set(FUNC(cosmicos_state::sc_w));

	/* video hardware */
	config.set_default_layout(layout_cosmicos);
	DM9368(config, m_led);
	TIMER(config, "digit").configure_periodic(FUNC(cosmicos_state::digit_tick), attotime::from_hz(100));
	TIMER(config, "interrupt").configure_periodic(FUNC(cosmicos_state::int_tick), attotime::from_hz(1000));

	SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	CDP1864(config, m_cti, 1.75_MHz_XTAL).set_screen(SCREEN_TAG);
	m_cti->inlace_cb().set_constant(0);
	m_cti->int_cb().set_inputline(m_maincpu, COSMAC_INPUT_LINE_INT);
	m_cti->dma_out_cb().set(FUNC(cosmicos_state::dmaout_w));
	m_cti->efx_cb().set(FUNC(cosmicos_state::efx_w));
	m_cti->rdata_cb().set_constant(1);
	m_cti->gdata_cb().set_constant(1);
	m_cti->bdata_cb().set_constant(1);
	m_cti->set_chrominance(RES_K(2), 0, 0, 0); // R2
	m_cti->add_route(ALL_OUTPUTS, "mono", 0.25);

	/* devices */
	QUICKLOAD(config, "quickload", "bin").set_load_callback(FUNC(cosmicos_state::quickload_cb));
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("256").set_extra_options("4K,48K");
}

/* ROMs */

ROM_START( cosmicos )
	ROM_REGION( 0x1000, CDP1802_TAG, 0 )
	ROM_SYSTEM_BIOS( 0, "hex", "Hex Monitor" )
	ROMX_LOAD( "hex.ic6",   0x000, 0x400, BAD_DUMP CRC(d25124bf) SHA1(121215ba3a979e1962327ebe73cbadf784c568d9), ROM_BIOS(0) ) // typed in from manual
	ROMX_LOAD( "hex.ic7",   0x400, 0x400, BAD_DUMP CRC(364ac81b) SHA1(83936ee6a7ed44632eb290889b98fb9a500f15d4), ROM_BIOS(0) ) // typed in from manual
	ROM_SYSTEM_BIOS( 1, "ascii", "ASCII Monitor" )
	ROMX_LOAD( "ascii.ic6", 0x000, 0x400, NO_DUMP, ROM_BIOS(1) )
	ROMX_LOAD( "ascii.ic7", 0x400, 0x400, NO_DUMP, ROM_BIOS(1) )
ROM_END

} // anonymous namespace

/* System Drivers */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY           FULLNAME    FLAGS
COMP( 1979, cosmicos, 0,      0,      cosmicos, cosmicos, cosmicos_state, empty_init, "Radio Bulletin", "Cosmicos", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )



cp1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

        Kosmos CP1

        06/03/2012 Skeleton driver.

        on board there is also 8155
        KEYBOARD Membrane keyboard, 20+10 keys
        6 * 7 seg led display

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

#include "emu.h"

#include "cpu/mcs48/mcs48.h"
#include "machine/i8155.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "video/pwm.h"

#include "speaker.h"
#include "cp1.lh"


namespace {

class cp1_state : public driver_device
{
public:
	cp1_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_i8155(*this, "i8155"),
		m_i8155_cp3(*this, "i8155_cp3"),
		m_cassette(*this, "cassette"),
		m_display(*this, "display"),
		m_io_lines(*this, "LINE%u", 0U),
		m_io_config(*this, "CONFIG")
	{ }

	void cp1(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void cp1_io(address_map &map) ATTR_COLD;

	uint8_t port1_r();
	uint8_t port2_r();
	void port1_w(uint8_t data);
	void port2_w(uint8_t data);
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);

	uint8_t i8155_read(offs_t offset);
	void i8155_write(offs_t offset, uint8_t data);
	void i8155_porta_w(uint8_t data);
	uint8_t i8155_portb_r();
	void i8155_portb_w(uint8_t data);
	void i8155_portc_w(uint8_t data);

	required_device<cpu_device> m_maincpu;
	required_device<i8155_device> m_i8155;
	required_device<i8155_device> m_i8155_cp3;
	required_device<cassette_image_device> m_cassette;
	required_device<pwm_display_device> m_display;
	required_ioport_array<5> m_io_lines;
	required_ioport m_io_config;

	uint8_t m_port2 = 0;
	uint8_t m_matrix = 0;
};

uint8_t cp1_state::port1_r()
{
	logerror("Read from expansion port 1\n");

	uint8_t data = 0;

	if (m_io_config->read() & 0x01)
		data |= (m_cassette->input() > 0.03) ? 0x80 : 0x00;

	return data;
}

void cp1_state::port1_w(uint8_t data)
{
	logerror("Write to expansion port 1 %x\n", data);

	if (m_io_config->read() & 0x01)
		m_cassette->output(data & 0x80 ? +1.0 : -1.0);
}

uint8_t cp1_state::port2_r()
{
	// x--- ----   I8155 IO/M
	// -x-- ----   I8155 RESET
	// --x- ----   expansion port CE
	// ---x ----   I8155 CE
	// ---- xxxx   keyboard input

	uint8_t data = 0;

	for (int i = 0; i < 5; i++)
		if (BIT(m_matrix, i))
			data |= m_io_lines[i]->read();

	return (data & 0x0f) | (m_port2 & 0xf0);
}

void cp1_state::port2_w(uint8_t data)
{
	if (data & 0x40)
	{
		m_i8155->reset();

		if (m_io_config->read() & 0x02)
			m_i8155_cp3->reset();
	}

	m_port2 = data;
}

uint8_t cp1_state::i8155_read(offs_t offset)
{
	uint8_t data = 0;

	if (!(m_port2 & 0x10))
	{
		m_i8155->ale_w(BIT(m_port2, 7), offset);
		data |= m_i8155->data_r();
	}
	if ((m_io_config->read() & 0x02) && !(m_port2 & 0x20))
	{
		// CP3 RAM expansion
		m_i8155_cp3->ale_w(BIT(m_port2, 7), offset);
		data |= m_i8155_cp3->data_r();
	}

	return data;
}

void cp1_state::i8155_write(offs_t offset, uint8_t data)
{
	if (!(m_port2 & 0x10))
	{
		m_i8155->ale_w(BIT(m_port2, 7), offset);
		m_i8155->data_w(data);
	}
	if ((m_io_config->read() & 0x02) && !(m_port2 & 0x20))
	{
		// CP3 RAM expansion
		m_i8155_cp3->ale_w(BIT(m_port2, 7), offset);
		m_i8155_cp3->data_w(data);
	}
}

void cp1_state::i8155_porta_w(uint8_t data)
{
	m_display->write_mx(data | 0x80); // PA7 is not connected
}

uint8_t cp1_state::i8155_portb_r()
{
	logerror("read from expansion port 2\n");
	return 0;
}

void cp1_state::i8155_portb_w(uint8_t data)
{
	logerror("Write to expansion port 2 %x\n", data);
}

void cp1_state::i8155_portc_w(uint8_t data)
{
	// --xx xxxx   keyboard matrix, 7seg select
	m_matrix = ~data & 0x3f;
	m_display->write_my(m_matrix);
}


void cp1_state::cp1_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0xff).rw(FUNC(cp1_state::i8155_read), FUNC(cp1_state::i8155_write));
}

/* Input ports */
INPUT_PORTS_START( cp1 )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_K)   PORT_NAME("CAS [Cass. speichern]")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_NAME("CLR [Irrtum]")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C)   PORT_NAME("PC [Programmzahler]")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A)   PORT_NAME("ACC [Akku]")
	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L)   PORT_NAME("CAL [Cass. lesen]")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S)   PORT_NAME("STEP [Schritt]")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B)   PORT_NAME("STP [Stopp]")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R)   PORT_NAME("RUN [Lauf]")
	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8)   PORT_NAME("8")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9)   PORT_NAME("9")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O)   PORT_NAME("OUT [Auslesen]")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I)   PORT_NAME("INP [Eingeben]")
	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4)   PORT_NAME("4")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5)   PORT_NAME("5")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6)   PORT_NAME("6")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7)   PORT_NAME("7")
	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0)   PORT_NAME("0")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1)   PORT_NAME("1")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2)   PORT_NAME("2")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3)   PORT_NAME("3")

	PORT_START("CONFIG")
	PORT_CONFNAME( 0x01, 0x01, "CP2 Cassette Interface" )
	PORT_CONFSETTING( 0x00, DEF_STR( No ) )
	PORT_CONFSETTING( 0x01, DEF_STR( Yes ) )
	PORT_CONFNAME( 0x02, 0x00, "CP3 RAM Expansion" )
	PORT_CONFSETTING( 0x00, DEF_STR( No ) )
	PORT_CONFSETTING( 0x02, DEF_STR( Yes ) )
INPUT_PORTS_END

void cp1_state::machine_start()
{
	save_item(NAME(m_port2));
	save_item(NAME(m_matrix));
}

void cp1_state::machine_reset()
{
	m_cassette->change_state(CASSETTE_STOPPED, CASSETTE_MASK_UISTATE);
}

QUICKLOAD_LOAD_MEMBER(cp1_state::quickload_cb)
{
	uint8_t byte = 0;
	unsigned addr = 0;

	char ch = '\0';
	uint32_t actual = 0;
	while ((actual = image.fread(&ch, 1)) != 0 && addr < 0x100)
	{
		// Format: nn.nnn<CR><LF>
		if (ch >= '0' && ch <= '9')
			byte = (byte * 10) + (ch - '0');
		else if (ch == '.' && (addr & 1) == 0)
		{
			m_i8155->memory_w(addr++, byte);
			byte = 0;
		}
		else if (ch == '\r' || ch == '\n')
		{
			if ((addr & 1) != 0)
				m_i8155->memory_w(addr++, byte);
			byte = 0;
		}
		else
		{
			return std::make_pair(image_error::INVALIDIMAGE, std::string());
		}
	}

	return std::make_pair(std::error_condition(), std::string());
}

void cp1_state::cp1(machine_config &config)
{
	/* basic machine hardware */
	i8049_device &maincpu(I8049(config, m_maincpu, 6_MHz_XTAL));
	maincpu.set_addrmap(AS_IO, &cp1_state::cp1_io);
	maincpu.p1_in_cb().set(FUNC(cp1_state::port1_r));
	maincpu.p1_out_cb().set(FUNC(cp1_state::port1_w));
	maincpu.p2_in_cb().set(FUNC(cp1_state::port2_r));
	maincpu.p2_out_cb().set(FUNC(cp1_state::port2_w));

	i8155_device &i8155(I8155(config, "i8155", 0));
	i8155.out_pa_callback().set(FUNC(cp1_state::i8155_porta_w));
	i8155.in_pb_callback().set(FUNC(cp1_state::i8155_portb_r));
	i8155.out_pb_callback().set(FUNC(cp1_state::i8155_portb_w));
	i8155.out_pc_callback().set(FUNC(cp1_state::i8155_portc_w));

	I8155(config, "i8155_cp3", 0);

	PWM_DISPLAY(config, m_display).set_size(6, 8);
	m_display->set_segmask(0x3f, 0x7f);
	m_display->set_segmask(0x08, 0xff); // 3rd digit DP is always on
	config.set_default_layout(layout_cp1);

	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	QUICKLOAD(config, "quickload", "obj", attotime::from_seconds(1)).set_load_callback(FUNC(cp1_state::quickload_cb));
}

/* ROM definition */
/*
  KOSMOS B
  <Mitsubishi Logo> M5L8049-136P-6
  JAPAN 83F301
*/

ROM_START( cp1 )
	ROM_REGION( 0x0800, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "b", "b" )
	ROMX_LOAD( "cp1-kosmos-b.rom", 0x0000, 0x0800, CRC(fea8a2b2) SHA1(c987b79a7b90fcbd58b66a69e95913f2655a1f0d), ROM_BIOS(0))
	// This is from 2716 eprom that was on board with I8039 instead of I8049
	ROM_SYSTEM_BIOS( 1, "2716", "2716" )
	ROMX_LOAD( "cp1-2716.bin",     0x0000, 0x0800, CRC(3a2caf0e) SHA1(ff4befcf82a664950186d3af1843fdef70d2209f), ROM_BIOS(1))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY   FULLNAME                 FLAGS
COMP( 1980, cp1,  0,      0,      cp1,     cp1,   cp1_state, empty_init, "Kosmos", "CP1 / Computer-Praxis", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



cp2000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:bataais
/*******************************************************************************

SciSys Chess Partner 2000, also sold by Novag with the same name.
It's probably the last SciSys / Novag collaboration.

Entering moves is not as friendly as newer sensory games. The player is expected
to press ENTER after their own move, but if they (accidentally) press it after
doing the computer's move, the computer takes your turn.

Capturing pieces is also unintuitive, having to press the destination square twice.

Hardware notes:
- 3850PK CPU at ~2.77MHz(averaged), 3853PK memory interface
- 4KB ROM, 256 bytes RAM(2*2111N)
- 4-digit 7seg panel, sensory chessboard

3850 is officially rated 2MHz, and even the CP2000 manual says it runs at 2MHz,
but tests show that it runs at a much higher speed. Three individual CP2000 were
measured, by timing move calculation, and one recording to verify beeper pitch and
display blinking rate. Real CP2000 CPU frequency is in the 2.63MHz-2.91MHz range.

The 'sequels' CP3000-CP6000 are on HMCS40 (see minichess.cpp and tschess.cpp),
Chess Partner 1000 does not exist.

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

#include "emu.h"

#include "cpu/f8/f8.h"
#include "machine/f3853.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "saitek_cp2000.lh"


namespace {

class cp2000_state : public driver_device
{
public:
	cp2000_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_board(*this, "board"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void cp2000(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<sensorboard_device> m_board;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<4> m_inputs;

	u8 m_select = 0;
	u16 m_inp_mux = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	// I/O handlers
	void control_w(u8 data);
	void digit_w(u8 data);
	u8 input_r();
};

void cp2000_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_select));
	save_item(NAME(m_inp_mux));
}



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

void cp2000_state::control_w(u8 data)
{
	// d0-d3: digit select
	// d4: keypad/chessboard select
	m_select = ~data;
	m_display->write_my(m_select);

	// d5: speaker out
	m_dac->write(BIT(~data, 5));
}

u8 cp2000_state::input_r()
{
	u8 data = m_inp_mux;

	// read chessboard buttons
	if (m_select & 0x10)
	{
		u8 cb = m_board->read_rank((m_inp_mux >> 1 & 7) ^ 3);
		if (m_inp_mux & 1)
			cb <<= 4;
		data |= cb & 0xf0;
	}

	// read keypad buttons
	else
	{
		// d0-d3: multiplexed inputs from d4-d7
		for (int i = 0; i < 4; i++)
			if (BIT(m_inp_mux, i + 4))
				data |= m_inputs[i]->read();

		// d4-d7: multiplexed inputs from d0-d3
		for (int i = 0; i < 4; i++)
			if (m_inp_mux & m_inputs[i]->read())
				data |= 0x10 << i;
	}

	return data;
}

void cp2000_state::digit_w(u8 data)
{
	// d0-d3: chessboard input mux (demux)
	// d0-d7: keypad input mux (direct)
	m_inp_mux = data;

	// also digit segment data
	m_display->write_mx(bitswap<8>(data,0,2,1,3,4,5,6,7));
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void cp2000_state::main_map(address_map &map)
{
	map(0x0000, 0x0fff).rom();
	map(0x1000, 0x10ff).ram();
}

void cp2000_state::main_io(address_map &map)
{
	map(0x00, 0x00).rw(FUNC(cp2000_state::input_r), FUNC(cp2000_state::digit_w));
	map(0x01, 0x01).w(FUNC(cp2000_state::control_w));
	map(0x0c, 0x0f).rw("f3853", FUNC(f3853_device::read), FUNC(f3853_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( cp2000 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4 / Rook")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8 / Black")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Find Position")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3 / Bishop")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7 / White")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Enter Position")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2 / Knight")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6 / King")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Cancel EP")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0 / Clear Square / Level")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1 / Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5 / Queen")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Multi Move")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("Clear Board / New Game")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void cp2000_state::cp2000(machine_config &config)
{
	// basic machine hardware
	F8(config, m_maincpu, 2'750'000); // see driver notes
	m_maincpu->set_addrmap(AS_PROGRAM, &cp2000_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &cp2000_state::main_io);
	m_maincpu->set_irq_acknowledge_callback("f3853", FUNC(f3853_device::int_acknowledge));

	f3853_device &f3853(F3853(config, "f3853", 2'750'000));
	f3853.int_req_callback().set_inputline("maincpu", F8_INPUT_LINE_INT_REQ);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(100));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 7);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_saitek_cp2000);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( cp2000 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("c55126_y01-rom.u3", 0x0000, 0x1000, CRC(aa7b8536) SHA1(62fb2c5631541e9058e51eb6bdc6e69569baeef3) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1980, cp2000, 0,      0,      cp2000,  cp2000, cp2000_state, empty_init, "SciSys / Novag Industries / Philidor Software", "Chess Partner 2000", MACHINE_SUPPORTS_SAVE )



cps1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Paul Leaman
/***************************************************************************

Capcom System 1
===============

Driver provided by:
Paul Leaman (paul@vortexcomputing.demon.co.uk)

68000 for game, Z80, YM-2151 and OKIM6295 for sound.

68000 clock speeds are unknown for all games (except where commented)

todo: move the bootleg sets with modified hardware into their own
      drivers, like capcom/fcrash.cpp


Notes
-----
- Most of the original PALs are protected. The images loaded in the ROM definitions are
  mostly logically equivalent replacements with a GAL16V8 target. The actual pld types
  found on the boards can also be PAL16P8, PAL16L8 etc.

- There are five PALs on the A-Board, they are the same across all A-Board
  revisions, but their location varies.
  The A-Boards are interchangeable, so the IC locations are not specified in
  the ROM definitions and are listed here instead:

  PAL ID  88617A  89626A
  ------  ------  ------
  PRG1    12H     9K
  IOA1    12F     4A
  BUF1    16H     13K
  ROM1    15H     14K
  SOU1    13E     10F

  For Q-Sound games, the A-Board misses the Z80, Oki and amp, which are replaced
  by the D-Board; the SOU1 PAL is missing as well, while PRG1 is replaced by
  PRG2. The other PALs are the same.

- The BPRG1 PAL found on later B-boards allows the use of a larger program ROM space.

- The B-board usually has two PALs (later revisions have three). The first PAL
  is used to map tile codes to the graphics ROMs, and changes from game to game.
  The other doesn't change from game to game and there are only two versions,
  called LWIO and IOB1, which are almost identical, the only difference being
  that outputs o16 and o17 are R/W in IOB1 and Write only in LWIO. Since those
  outputs are not used anyway except that in Forgotten Worlds, one wonders why
  they were changed.

  SF2 revision "E" World and USA 910228 maps CPS-B at 8001C0 instead of 800140
  and seems to be the only one that uses the IOB2 and C632B PALs instead of the
  IOB1 and C632, while STF29 PAL is confirmed to be the same as the other SF2 sets.

  pin 1  CN2 D9  = /IOCS (i.e. address 8xxxxx)
  pin 2  CN2 D7  = /RDB
  pin 3  CN2 C6  = /UDSWR
  pin 4  CN2 D6  = /LDSWR
  pin 5  CN2 C19 = A8
  pin 6  CN2 C20 = A7
  pin 7  CN2 C21 = A6
  pin 8  CN2 C22 = A5
  pin 9  CN2 C23 = A4
  pin 10 GND
  pin 11 CN2 C24 = A3
  pin 12 CN2 C25 = A2

  LWIO:
  /o19 = /i1 * /i2 * /i5 * /i6 *  i7 *  i8 * /i9            800060-80006f R    not used
  /o18 = /i1 * /i2 * /i5 * /i6 *  i7 * /i8 *  i9            800050-80005f R    forgotten worlds dial (on 88618B & 88621B B-boards)
   o17 = /i1 * /i4 * /i5 * /i6 *  i7 * /i8 * /i9 * /i11     800040-800047 W    forgotten worlds dial (on 88618B & 88621B B-boards)
   o16 = /i1 * /i4 * /i5 * /i6 *  i7 * /i8 * /i9 *  i11     800048-80004f W    forgotten worlds dial (on 88618B & 88621B B-boards)
  /o15 = /i1 *  i5 * /i6 *  i7                              800140-80017f R/W  CPS-B
  /o14 = /i1 * /i4 * /i5 * /i6 * /i7 *  i8 *  i9 * /i11     800030-800037 W    not used (overlaps coinctrl on A-board)
  /o13 = /i1 * /i2 * /i5 * /i6 * /i7 * /i8 * /i9 *  i11     800008-80000f R    not used
  /o12 = /i1 * /i2 * /i5 * /i6 * /i7 * /i8 *  i9 *  i11     800018-80001f R    not used (overlaps system/dsw inputs on A-board)

  IOB1:
  /o19 = /i1 * /i2 * /i5 * /i6 *  i7 *  i8 * /i9            800060-80006f R    not used
  /o18 = /i1 * /i2 * /i5 * /i6 *  i7 * /i8 *  i9            800050-80005f R    not used
   o17 = /i1 * /i5 * /i6 *  i7 * /i8 * /i9 * /i11           800040-800047 R/W  not used
   o16 = /i1 * /i5 * /i6 *  i7 * /i8 * /i9 *  i11           800048-80004f R/W  not used
  /o15 = /i1 *  i5 * /i6 *  i7                              800140-80017f R/W  CPS-B
  /o14 = /i1 * /i4 * /i5 * /i6 * /i7 *  i8 *  i9 * /i11     800030-800037 W    not used (overlaps coinctrl on A-board)
  /o13 = /i1 * /i2 * /i5 * /i6 * /i7 * /i8 * /i9 *  i11     800008-80000f R    not used
  /o12 = /i1 * /i2 * /i5 * /i6 * /i7 * /i8 *  i9 *  i11     800018-80001f R    not used (overlaps system/dsw inputs on A-board)

- The C-board ioc1 has been dumped from an unprotected pal16l8 device.

Stephh's notes (based on the games M68000 code and some tests) :

1) 'forgottn' and clones

  - Keys in the "scroll 1 test" screen :
      * P1 UP    : char code += 0x0200
      * P1 DOWN  : char code -= 0x0200
      * P1 RIGHT : attribute ^= 0x20 ; also affects Hflip
      * P1 LEFT  : attribute ^= 0x40 ; also affects Vflip
      * P1 button 1 : (attribute++) & 0x1f ; also affects color
      * P1 button 2 : (attribute--) & 0x1f ; also affects color

2) 'ghouls' and clones

2a) 'ghouls'

  - NO debug features

2b) 'ghoulsu'

  - How to activate the debug features :
      * set "Game Mode" Dip Switch to "Game"
      * set both "Coin A" and "Coin B" Dip Switches to 1C_1C
      * reset the game (F3 key)
      * insert a coin
      * set "Game Mode" Dip Switch to "Test"
      * set the debug Dip Switches to what you want
      * start a 1 player game
  - Some debug features :
      * "Armor on New Life" is effective at the beginning of a new life
        Note that even when you start without armor, you need to be hit twice
      * "Starting Weapon" is effective only when you start a new game
        or when you continue play
      * "Starting Level" is effective only when you start a new game
        (you must NOT continue play !)
      * "Slow Motion" and "Invulnerability" can be changed at any time

2c) 'daimakai'

  - NO debug features
  - Different Dip Switches than in 'ghouls'

3) 'strider' and clones

  - TO DO !

4) 'dynwar' and clones

4a) 'dynwar'

  - According to code at 0x0125fa and 0x012634, bits 0 and 1 of DSWC also affect the energy cost
    (table at 0x012662) when you press button 3 ("tactics" in the manual) :

    bit 0  bit 1    cost         possible BCD values (based on kind of "tactics")
     OFF    OFF     normal       0x10 (rockslide) 0x08 (ambush) 0x14 (flame) 0x12 (explosion)
     ON     OFF     very low     0x03             0x02          0x05         0x04
     OFF    ON      low          0x06             0x04          0x10         0x08
     ON     ON      high         0x13             0x10          0x19         0x16

    So IMO, there might be an ingame bug at 0x000c7e which should read bits 2 and 3 of DSWC
    (instead of having them unused) :
    000C7E: 0240 0003                andi.w  #$3, D0
    should be
    000C7E: 0240 000c                andi.w  #$c, D0
    However, only bits 4 to 7 are tested while you are in the "test mode".

    Note that code at 0x000c7e is only executed once when you reset the machine;
    this means that you can change the "Freeze" and "Turbo Mode" Dip Switches before
    reseting the game to change the energy cost settings, then put them back to "Off"
    after the startup routine (when screen is black) so you can play the game.
  - Both "Coin A" and "Coin B" Dip Switches must be set to "2 Coins/1 Credit" to make
    this mode available. If only one Dip Switch is set to it, it is the same as 2C_1C.

4b) 'dynwarj'

  - The "energy cost" bug also exists in this version as well as code at 0x000c7e;
    in fact, only code that reads these settings is at a different address :
    check code at 0x01267a and 0x0126b4 and table at 0x0126e2.
  - Both "Coin A" and "Coin B" Dip Switches must be set to "2 Coins/1 Credit" to make
    this mode available. If only one Dip Switch is set to it, it is the same as 2C_1C.

5) 'willow' and clones

  - How to activate the debug features :
      * set "Game Mode" Dip Switch to "Game"
      * set both "Coin A" and "Coin B" Dip Switches to 1C_1C
      * reset the game (F3 key)
      * insert a coin
      * set "Game Mode" Dip Switch to "Test"
      * set the debug Dip Switches to what you want
      * start a 1 player game
  - Some debug features :
      * "Starting Level" is effective only when you start a new game
        (you must NOT continue play !)
      * Once you set "Maximum magic/sword power" to "On", setting it to "Off" won't have
        any effect until you start a new game (you must NOT continue play !)
      * "Slow Motion Delay" is effective only when "Slow Motion" is set to "On"
      * Even if "Freeze" Dip Switch is set to "On" a message will be displayed to its end.
      * I can't tell what kind of infos are displayed when "Display Debug Infos"
        Dip Switch is sry to "On" :( Any hint about them are welcome !
  - Both "Coin A" and "Coin B" Dip Switches must be set to "2 Coins/1 Credit" to make
    this mode available. If only one Dip Switch is set to it, it is the same as 2C_1C.
  - When the "Stage Magic Continue" Dip Switch is set to "On", your magic and sword power
    will be increased at the end of the level if you haven't bought the magic/sword item.
    But you won't notice this before you use the character again.
    For example, magic power will be increased at the end of level 1 but you won't notice
    it before level 3, and sword power will be increased at the end of level 2 but you
    won't notice it before level 4.

6) 'ganbare'

  - Using the payout setting dip switch results in some occasional hopper errors, if this
    happens, then the clear ram dip switch needs to be used. This needs to be resolved.

TO DO (2006.09.20) :

  - Check 'strider' and its clones and add debug features
  - Check other games to see if there are some debug or hidden features
  - Add addresses from routines with debug features in the notes
  - Look at what IN2 and IN3 do for the following sets :
      * 'cworld2j' (IN2 only)
      * 'qad' and 'qadjr'
      * 'qtono2j'
  - Check daimakar dip switches. E.g. changing the number of lives also changes the
    starting level.

Stephh's log (2006.09.20) :

  - Changed the "readinputport" reads by "readinputportbytag" reads.
    This way, inputs and Dip Switch can be in any order
    (and I don't have the nasty conditional Dip Switch bug).
    BTW, I've slightly changed the read memory map :
      * one handler for the 3 Dip Switches banks : cps1_dsw_r
      * one handler for the system inputs (IN0) : cps1_in0_r
      * one handler for the player inputs (IN1) : cps1_in1_r
      * renamed cps1_input2_r to cps_in2_r
      * renamed cps1_input3_r to cps_in3_r
  - Applied these changes to src/drivers/fcrash.c as well.
  - Added debug features in the following sets :
      * 'ghoulsu'
      * 'willowu', 'willowou' and 'willowj'
  - Checked sets with no debug features :
      * 'forgottnua' and 'lostwrld'
      * 'ghouls' and 'daimakai'
      * 'dynwar' and 'dynwarj'

2008-07:
  - replaced input read handler with direct AM_READ_PORT where suitable

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

#include "emu.h"
#include "cps1.h"

#include "cpu/z80/z80.h"
#include "cpu/pic16c5x/pic16c5x.h"
#include "machine/eepromser.h"
#include "machine/upd4701.h"
#include "sound/qsound.h"
#include "sound/ymopm.h"

#include "kabuki.h"
#include "speaker.h"



uint16_t cps_state::cps1_dsw_r(offs_t offset)
{
	static const char *const dswname[] = { "IN0", "DSWA", "DSWB", "DSWC" };
	int in = ioport(dswname[offset])->read();
	return (in << 8) | 0xff;
}

uint16_t cps_state::cps1_hack_dsw_r(offs_t offset)
{
	static const char *const dswname[] = { "IN0", "DSWA", "DSWB", "DSWC" };
	int in = ioport(dswname[offset])->read();
	return (in << 8) | in;
}

uint16_t cps_state::cps1_in1_r()
{
	int in = ioport("IN1")->read();
	return (in << 8) | in;
}

uint16_t cps_state::cps1_in2_r()
{
	int in = ioport("IN2")->read();
	return (in << 8) | in;
}

uint16_t cps_state::cps1_in3_r()
{
	int in = ioport("IN3")->read();
	return (in << 8) | in;
}


void cps_state::cps1_snd_bankswitch_w(uint8_t data)
{
	membank("bank1")->set_entry(data & 0x01);
}

void cps_state::cps1_oki_pin7_w(uint8_t data)
{
	m_oki->set_pin7(data & 1);
}

void cps_state::cps1_soundlatch_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
		m_soundlatch->write(data & 0xff);
	else
		m_soundlatch->write(data >> 8);
}

void cps_state::cps1_soundlatch2_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
		m_soundlatch2->write(data & 0xff);
}

void cps_state::cps1_coinctrl_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (ACCESSING_BITS_8_15)
	{
		machine().bookkeeping().coin_counter_w(0, data & 0x0100);
		machine().bookkeeping().coin_counter_w(1, data & 0x0200);
		machine().bookkeeping().coin_lockout_w(0, ~data & 0x0400);
		machine().bookkeeping().coin_lockout_w(1, ~data & 0x0800);

		// bit 15 = CPS-A custom reset?
	}
}

void cps_state::cpsq_coinctrl2_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
	{
		machine().bookkeeping().coin_counter_w(2, data & 0x01);
		machine().bookkeeping().coin_lockout_w(2, ~data & 0x02);
		machine().bookkeeping().coin_counter_w(3, data & 0x04);
		machine().bookkeeping().coin_lockout_w(3, ~data & 0x08);
	}
}


/********************************************************************
*
*  Interrupts
*  ==========
*
********************************************************************/

INTERRUPT_GEN_MEMBER(cps_state::cps1_interrupt)
{
	/* Strider also has a IRQ4 handler. It is input port related, but the game */
	/* works without it. It is the *only* CPS1 game to have that. */
	/* ...until we found out that ganbare relies on it, see below */
	device.execute().set_input_line(M68K_IRQ_IPL1, ASSERT_LINE);
}

/*
/IPL2 (IRQ4) is tied high on some early B boards (eg. 89624B-3),

On later B boards (90629B-2, 91634B-2, 91635B-2) it is passed to the C board via
connector CNA pin 9. On the later CPS-B-21 based C boards (92631C-6, 90631C-5,
92641C-1) it then passes to pin 57 of the CPS-B-21 via jumper JP1.

There are only a few newer CPS1 games that are known to use it, eg. Ganbare!
Marine Kun (B:91634B-2, C:92631C-6), otherwise you get a "HARD ERROR" after boot.
It requires a CPS-B-21 C-board (90631C-5, 92631C-6, 92641C-1) with JP1 closed.

Strider has a handler but no h/w support to call IRQ4, perhaps a remnant of some
dev tool?

And particularly on CPS2, CPS-B-21 triggers raster interrupts.
*/
TIMER_DEVICE_CALLBACK_MEMBER(cps_state::raster_scanline)
{
	int scanline = param;
	int raster_irq_pending = 0;

	// clock raster counters on each scanline increment
	for (int i = 0; i < 2; i++)
	{
		if (scanline == 0)
			m_raster_counter[i] = m_raster_reload[i];
		else
			m_raster_counter[i] = (m_raster_counter[i] - 1) & 0x1ff;

		if (m_raster_counter[i] == 0)
			raster_irq_pending |= 1 << i;
	}

	// schedule raster interrupt on IPL2 (IRQ4, or IRQ6 if it's at the same time as vblank)
	m_raster_counter[2] = m_raster_reload[2];
	if (raster_irq_pending)
		m_raster_irq->adjust(m_raster_counter[2] * 2 * m_screen->pixel_period());

	// vblank interrupt on IPL1 (IRQ2)
	if (scanline == 240)
		m_maincpu->set_input_line(M68K_IRQ_IPL1, ASSERT_LINE);
}

TIMER_CALLBACK_MEMBER(cps_state::raster_irq)
{
	m_maincpu->set_input_line(M68K_IRQ_IPL2, ASSERT_LINE);

	// note: normally it's vpos - 1, but let's give it some time before it actually writes to gfx registers
	m_screen->update_partial(m_screen->vpos());
}

uint16_t cps_state::irqack_r(offs_t offset)
{
	// FC0-FC2(any) + BGACK: VPA and clears both IPL1 and IPL2
	if (!machine().side_effects_disabled())
	{
		m_maincpu->set_input_line(M68K_IRQ_IPL1, CLEAR_LINE);
		m_maincpu->set_input_line(M68K_IRQ_IPL2, CLEAR_LINE);
	}

	return m68000_base_device::autovector(offset + 1);
}

void cps_state::cpu_space_map(address_map &map)
{
	map(0xfffff2, 0xffffff).before_time(m_maincpu, FUNC(m68000_device::vpa_sync)).after_delay(m_maincpu, FUNC(m68000_device::vpa_after)).r(FUNC(cps_state::irqack_r));
}


/********************************************************************
*
*  Q Sound
*  =======
*
********************************************************************/

uint16_t cps_state::qsound_rom_r(offs_t offset)
{
	if (memregion("user1") != nullptr)
	{
		uint8_t *rom = memregion("user1")->base();
		return rom[offset] | 0xff00;
	}
	else
	{
		popmessage("%06x: read sound ROM byte %04x", m_maincpu->pc(), offset);
		return 0;
	}
}

uint16_t cps_state::qsound_sharedram1_r(offs_t offset)
{
	return m_qsound_sharedram1[offset] | 0xff00;
}

void cps_state::qsound_sharedram1_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
		m_qsound_sharedram1[offset] = data;
}

uint16_t cps_state::qsound_sharedram2_r(offs_t offset)
{
	return m_qsound_sharedram2[offset] | 0xff00;
}

void cps_state::qsound_sharedram2_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
		m_qsound_sharedram2[offset] = data;
}

void cps_state::qsound_banksw_w(uint8_t data)
{
	/* Z80 bank register for music note data. It's odd that it isn't encrypted though. */
	int bank = data & 0x0f;
	if ((0x10000 + (bank * 0x4000)) >= memregion("audiocpu")->bytes())
	{
		logerror("WARNING: Q sound bank overflow (%02x)\n", data);
		bank = 0;
	}

	membank("bank1")->set_entry(bank);
}


/********************************************************************
*
*  EEPROM
*  ======
*
*   The EEPROM is accessed by a serial protocol using the register
*   0xf1c006
*
********************************************************************/

/*
PAL PRG1 (16P8B @ 12H):

I0 = AS
I1 = /BGACKB
I2 = A23
I3 = A22
I4 = A21
I5 = A20
I6 = A19
I7 = A18
I8 = A17
I9 = A16

n.c.      = pin19 =   ( !I0 &  I1 )
n.c.      = pin18 =   ( !I0 &  I1 )
n.c.      = pin17 = ! (  I0 &  I1 & (!I2 | !I3 | !I4 | !I5 | !I6 | !I7) )
n.c.      = pin16 = ! (  I2 &  I3 &  I4 &  I5 &  I6 &  I7 )
/IOCS     = pin15 = ! (  I0 &  I1 &  I2 & !I3 & !I4 & !I5 )
/ONE WAIT = pin14 = ! (  I0 &  I1 & ( I2 | !I3) )
/databus  = pin13 = ! (  I0 &  I1 & (!I2 | !I3 | !I4 | !I5 | !I6 | !I7) )
/workram  = pin12 = ! (  I0 &  I1 &  I2 &  I3 &  I4 &  I5 &  I6 &  I7 )


In Q-Sound games, PRG1 is replaced by PRG2:

/IOCS     = pin 15 = ! (  I0 &  I1 &  I2 & !I3 & !I4 & !I5 )
/ONE WAIT = pin 14 = ! (  I0 &  I1 & (!I2 | !I3 | !I4 | !I5 | ( I6 &  I7 &  I8 &  I9)) )
/databus  = pin 13 = ! (  I0 &  I1 & (!I2 | !I3 | !I4 | !I5 | (!I6 & !I7 & !I8)) )
/workram  = pin 12 = ! (  I0 &  I1 &  I2 &  I3 &  I4 &  I5 &  I6 &  I7 &  I8 &  I9 )





PAL IOA1 (16P8B @ 12F):

I0 = /IOCS
I1 = /RDB
I2 = /UDSWR
I3 = /LDSWR
I4 = AB8
I5 = AB7
I6 = AB6
I7 = AB5
I8 = AB4
I9 = AB3

player input      = pin19 = ! ( !I0 & !I1 & !I4 & !I5 & !I6 & !I7 & !I8 & !I9 )
system input/dips = pin18 = ! ( !I0 & !I1 & !I4 & !I5 & !I6 & !I7 &  I8 &  I9 )
outputs           = pin17 = ! ( !I0 & !I2 & !I4 & !I5 & !I6 &  I7 &  I8 & !I9 )
sound 1B          = pin16 = ! ( !I0 & !I3 &  I4 &  I5 & !I6 & !I7 & !I8 &  I9 )
sound 0B          = pin15 = ! ( !I0 & !I3 &  I4 &  I5 & !I6 & !I7 & !I8 & !I9 )
n.c.              = pin14 =   ( !I1 & !I2 )
/PPU1             = pin13 = ! ( !I0 &  I4 & !I5 & !I6 )
n.c.              = pin12 =   ( !I1 & !I2 )


PAL BUF1 (16P8B @ 16H):

I0 = A23 (all address lines can come both from 68000 and CPS-A custom)
I1 = A22
I2 = A21
I3 = A20
I4 = A19
I5 = A18
I6 = A17
I7 = A16
I8 = ASB

BUF0 = pin19 = ! ( I0 & !I1 & !I2 &  I3 & !I4 & !I5 & !I6 & !I7 )
BUF1 = pin18 = ! ( I0 & !I1 & !I2 &  I3 & !I4 & !I5 & !I6 &  I7 )
BUF2 = pin17 = ! ( I0 & !I1 & !I2 &  I3 & !I4 & !I5 &  I6 & !I7 )
BUF3 = pin16 = ! ( I0 & !I1 & !I2 &  I3 & !I4 & !I5 &  I6 &  I7 )
BUF4 = pin15 = ! ( I0 & !I1 & !I2 &  I3 & !I4 &  I5 & !I6 & !I7 )
BUF5 = pin14 = ! ( I0 & !I1 & !I2 &  I3 & !I4 &  I5 & !I6 &  I7 )
BUF6 = pin13 = ! ( I0 & !I1 & !I2 &  I3 & !I4 &  I5 &  I6 & !I7 )
/RDB = pin12 =   ( I0 & !I1 & !I2 &  I3 & !I8 )

BUF0-BUF2 are gfxram on A-board. BUF3-BUF6 go to B-board (provision for expansion, never used)


PAL ROM1 (16P8B @ 15H):

I0 = A23 (all address lines can come both from 68000 and CPS-A custom)
I1 = A22
I2 = A21
I3 = A20
I4 = A19
I5 = A18
I6 = A17
I7 = A16
I8 = ASB

PRG0 = pin17 = ! ( !I8 & !I0 & !I1 & !I2 & !I3 & !I4 & !I5 )
PRG1 = pin16 = ! ( !I8 & !I0 & !I1 & !I2 & !I3 & !I4 &  I5 )
PRG2 = pin15 = ! ( !I8 & !I0 & !I1 & !I2 & !I3 &  I4 & !I5 )
PRG3 = pin14 = ! ( !I8 & !I0 & !I1 & !I2 & !I3 &  I4 &  I5 )
PRG4 = pin19 = ! ( !I8 & !I0 & !I1 & !I2 &  I3 & !I4 )
PRG5 = pin18 = ! ( !I8 & !I0 & !I1 & !I2 &  I3 &  I4 )
PRG6 = pin13 = ! ( !I8 & !I0 & !I1 &  I2 & !I3 & !I4 )
/RDB = pin12 =   ( !I8 & !I0 & !I1 )

All PRGx go to B-board. Provision for up to 4MB of ROM space, which was never used in full.

*/

void cps_state::main_map(address_map &map)
{
	map(0x000000, 0x3fffff).rom();
	map(0x800000, 0x800007).portr("IN1");            /* Player input ports */
	/* forgottn, willow, cawing, nemo, varth read from 800010. Probably debug input leftover from development */
	map(0x800018, 0x80001f).r(FUNC(cps_state::cps1_dsw_r));            /* System input ports / Dip Switches */
	map(0x800020, 0x800021).nopr();                     /* ? Used by Rockman ? not mapped according to PAL */
	map(0x800030, 0x800037).w(FUNC(cps_state::cps1_coinctrl_w));
	/* Forgotten Worlds has dial controls on B-board mapped at 800040-80005f. See below */
	map(0x800100, 0x80013f).w(FUNC(cps_state::cps1_cps_a_w)).share("cps_a_regs");  /* CPS-A custom */
	/* CPS-B custom is mapped by the PAL IOB2 on the B-board. SF2 revision "E" World and USA 910228 has it at a different
	   address, see DRIVER_INIT */
	map(0x800140, 0x80017f).rw(FUNC(cps_state::cps1_cps_b_r), FUNC(cps_state::cps1_cps_b_w)).share("cps_b_regs");
	map(0x800180, 0x800187).w(FUNC(cps_state::cps1_soundlatch_w));    /* Sound command */
	map(0x800188, 0x80018f).w(FUNC(cps_state::cps1_soundlatch2_w));   /* Sound timer fade */
	map(0x900000, 0x92ffff).ram().w(FUNC(cps_state::cps1_gfxram_w)).share("gfxram"); /* SF2CE executes code from here */
	map(0xff0000, 0xffffff).ram().share("mainram");
}

/* Forgotten Worlds has a NEC uPD4701AC on the B-board handling dial inputs from the CN-MOWS connector. */
/* The memory mapping is handled by PAL LWIO */

void cps_state::forgottn_map(address_map &map)
{
	main_map(map);
	map(0x800041, 0x800041).w("upd4701", FUNC(upd4701_device::reset_x_w));
	map(0x800049, 0x800049).w("upd4701", FUNC(upd4701_device::reset_y_w));
	map(0x800052, 0x800055).r("upd4701", FUNC(upd4701_device::read_x)).umask16(0x00ff);
	map(0x80005a, 0x80005d).r("upd4701", FUNC(upd4701_device::read_y)).umask16(0x00ff);
}

/*
PAL SOU1 (16P8 @ 13E):

I0 = /MREQ
I1 = A15
I2 = A14
I3 = A13
I4 = A12
I5 = bank latch
I6 = /RD
I7 = /WR
I8 = /BANK

bank latch = pin19 = ! ( !I0 & !I7 & !I8 )
/SWR       = pin18 = ! ( !I0 & !I7 )
/SRD       = pin17 = ! ( !I0 & !I6 )
ls138      = pin16 = ! ( !I0 &  I1 &  I2 &  I3 &  I4 )
workram    = pin15 = ! ( !I0 &  I1 &  I2 & !I3 &  I4 )
SOUNDA14   = pin14 = ! ( !I0 & ((!I1 & !I2) | ( I1 & !I2 & !I5)) )
SOUNDA15   = pin13 =   (  I1 )
/SOUNDCE   = pin12 = ! ( !I0 & (!I1 | ( I1 & !I2)) )
*/

void cps_state::sub_map(address_map &map)
{
	map(0x0000, 0x7fff).rom();
	map(0x8000, 0xbfff).bankr("bank1");
	map(0xd000, 0xd7ff).ram();
	map(0xf000, 0xf001).rw("2151", FUNC(ym2151_device::read), FUNC(ym2151_device::write));
	map(0xf002, 0xf002).rw(m_oki, FUNC(okim6295_device::read), FUNC(okim6295_device::write));
	map(0xf004, 0xf004).w(FUNC(cps_state::cps1_snd_bankswitch_w));
	map(0xf006, 0xf006).w(FUNC(cps_state::cps1_oki_pin7_w)); /* controls pin 7 of OKI chip */
	map(0xf008, 0xf008).r(m_soundlatch, FUNC(generic_latch_8_device::read)); /* Sound command */
	map(0xf00a, 0xf00a).r(m_soundlatch2, FUNC(generic_latch_8_device::read)); /* Sound timer fade */
}

void cps_state::qsound_main_map(address_map &map)
{
	map(0x000000, 0x1fffff).rom();
	map(0x800000, 0x800007).portr("IN1");            /* Player input ports */
	map(0x800018, 0x80001f).r(FUNC(cps_state::cps1_dsw_r));            /* System input ports / Dip Switches */
	map(0x800030, 0x800037).w(FUNC(cps_state::cps1_coinctrl_w));
	map(0x800100, 0x80013f).w(FUNC(cps_state::cps1_cps_a_w)).share("cps_a_regs");  /* CPS-A custom */
	map(0x800140, 0x80017f).rw(FUNC(cps_state::cps1_cps_b_r), FUNC(cps_state::cps1_cps_b_w)).share("cps_b_regs");    /* CPS-B custom (mapped by LWIO/IOB1 PAL on B-board) */
	map(0x900000, 0x92ffff).ram().w(FUNC(cps_state::cps1_gfxram_w)).share("gfxram"); /* SF2CE executes code from here */
	map(0xf00000, 0xf0ffff).r(FUNC(cps_state::qsound_rom_r));          /* Slammasters protection */
	map(0xf18000, 0xf19fff).rw(FUNC(cps_state::qsound_sharedram1_r), FUNC(cps_state::qsound_sharedram1_w));  /* Q RAM */
	map(0xf1c000, 0xf1c001).portr("IN2");            /* Player 3 controls (later games) */
	map(0xf1c002, 0xf1c003).portr("IN3");            /* Player 4 controls ("Muscle Bombers") */
	map(0xf1c004, 0xf1c005).w(FUNC(cps_state::cpsq_coinctrl2_w));     /* Coin control2 (later games) */
	map(0xf1c006, 0xf1c007).portr("EEPROMIN").portw("EEPROMOUT");
	map(0xf1e000, 0xf1ffff).rw(FUNC(cps_state::qsound_sharedram2_r), FUNC(cps_state::qsound_sharedram2_w));  /* Q RAM */
	map(0xff0000, 0xffffff).ram().share("mainram");
}

void cps_state::qsound_sub_map(address_map &map)
{   // used by cps2.c too
	map(0x0000, 0x7fff).rom();
	map(0x8000, 0xbfff).bankr("bank1");    /* banked (contains music data) */
	map(0xc000, 0xcfff).ram().share("qsound_ram1");
	map(0xd000, 0xd002).w("qsound", FUNC(qsound_device::qsound_w));
	map(0xd003, 0xd003).w(FUNC(cps_state::qsound_banksw_w));
	map(0xd007, 0xd007).r("qsound", FUNC(qsound_device::qsound_r));
	map(0xf000, 0xffff).ram().share("qsound_ram2");
}

void cps_state::qsound_decrypted_opcodes_map(address_map &map)
{
	map(0x0000, 0x7fff).bankr("decrypted");
}

void cps_state::sf2m3_map(address_map &map)
{
	map(0x000000, 0x3fffff).rom();
	map(0x800010, 0x800011).portr("IN1");            /* Player input ports */
	map(0x800028, 0x80002f).r(FUNC(cps_state::cps1_hack_dsw_r));            /* System input ports / Dip Switches */
	map(0x800030, 0x800037).w(FUNC(cps_state::cps1_coinctrl_w));
	map(0x800100, 0x80013f).w(FUNC(cps_state::cps1_cps_a_w)).share("cps_a_regs");  /* CPS-A custom */
	map(0x800140, 0x80017f).rw(FUNC(cps_state::cps1_cps_b_r), FUNC(cps_state::cps1_cps_b_w)).share("cps_b_regs");   /* CPS-B custom */
	map(0x800186, 0x800187).r(FUNC(cps_state::cps1_in2_r));            /* Buttons 4,5,6 for both players */
	map(0x800190, 0x800197).w(FUNC(cps_state::cps1_soundlatch_w));    /* Sound command */
	map(0x800198, 0x80019f).w(FUNC(cps_state::cps1_soundlatch2_w));   /* Sound timer fade */
	map(0x8001a0, 0x8001c3).w(FUNC(cps_state::cps1_cps_a_w));
	map(0x8001c4, 0x8001c5).w(FUNC(cps_state::sf2m3_layer_w));
	map(0x900000, 0x92ffff).ram().w(FUNC(cps_state::cps1_gfxram_w)).share("gfxram");
	map(0xff0000, 0xffffff).ram();
}

void cps_state::sf2cems6_map(address_map &map)
{
	sf2m3_map(map);
	map(0x8001ee, 0x8001ef).nopw(); // writes 0xFFFF
}

void cps_state::sf2m10_map(address_map &map)
{
	map(0x000000, 0x3fffff).rom();
	map(0x800000, 0x800007).portr("IN1");
	map(0x800018, 0x80001f).r(FUNC(cps_state::cps1_hack_dsw_r));
	map(0x800020, 0x800021).nopr();
	map(0x800030, 0x800037).w(FUNC(cps_state::cps1_coinctrl_w));
	map(0x800100, 0x80013f).w(FUNC(cps_state::cps1_cps_a_w)).share("cps_a_regs");
	map(0x800140, 0x80017f).rw(FUNC(cps_state::cps1_cps_b_r), FUNC(cps_state::cps1_cps_b_w)).share("cps_b_regs");
	map(0x800180, 0x800187).w(FUNC(cps_state::cps1_soundlatch_w));
	map(0x800188, 0x80018f).w(FUNC(cps_state::cps1_soundlatch2_w));
	map(0x8001a2, 0x8001b3).w(FUNC(cps_state::cps1_cps_a_w)); // make 8001b2 point at 800110
	map(0x8001fe, 0x8001ff).nopw(); // writes FFFF here a lot
	map(0x900000, 0x92ffff).ram().w(FUNC(cps_state::cps1_gfxram_w)).share("gfxram");
	map(0xe00000, 0xefffff).ram(); // it writes to the whole range at start
	map(0xf1c000, 0xf1c001).r(FUNC(cps_state::cps1_in2_r));
	map(0xfeff00, 0xfeffff).ram(); // fix stack crash at start
	map(0xff0000, 0xffffff).ram().share("mainram");
}

void cps_state::varthb2_map(address_map &map)
{
	map(0x000000, 0x3fffff).rom();
	map(0x800000, 0x800007).portr("IN1");            /* Player input ports */
	map(0x800018, 0x80001f).r(FUNC(cps_state::cps1_hack_dsw_r));            /* System input ports / Dip Switches */
	map(0x800030, 0x800037).w(FUNC(cps_state::cps1_coinctrl_w));
	map(0x800100, 0x80013f).w(FUNC(cps_state::varthb2_cps_a_w)).share("cps_a_regs");  /* CPS-A custom */
	map(0x800140, 0x80017f).rw(FUNC(cps_state::cps1_cps_b_r), FUNC(cps_state::cps1_cps_b_w)).share("cps_b_regs");
	map(0x800180, 0x800187).w(FUNC(cps_state::cps1_soundlatch_w));    /* Sound command */
	map(0x800188, 0x80018f).w(FUNC(cps_state::cps1_soundlatch2_w));   /* Sound timer fade */
	map(0x900000, 0x92ffff).ram().w(FUNC(cps_state::cps1_gfxram_w)).share("gfxram");
	map(0xff0000, 0xffffff).ram().share("mainram");
}

void cps_state::varthb3_map(address_map &map) // TODO: check everything
{
	map(0x000000, 0x3fffff).rom();
	map(0x880000, 0x880007).portr("IN1");            /* Player input ports */
	map(0x880008, 0x88000f).r(FUNC(cps_state::cps1_hack_dsw_r));            /* System input ports / Dip Switches */
	map(0x800030, 0x800037).w(FUNC(cps_state::cps1_coinctrl_w));
	map(0x800100, 0x80013f).w(FUNC(cps_state::cps1_cps_a_w)).share("cps_a_regs");  /* CPS-A custom */
	map(0x800140, 0x80017f).rw(FUNC(cps_state::cps1_cps_b_r), FUNC(cps_state::cps1_cps_b_w)).share("cps_b_regs");
	map(0x800180, 0x800187).w(FUNC(cps_state::cps1_soundlatch_w));    /* Sound command */
	map(0x800188, 0x80018f).w(FUNC(cps_state::cps1_soundlatch2_w));   /* Sound timer fade */
	map(0x900000, 0x92ffff).ram().w(FUNC(cps_state::cps1_gfxram_w)).share("gfxram");
	map(0xff0000, 0xffffff).ram().share("mainram");
}

/***********************************************************
             INPUT PORTS, DIPs
***********************************************************/


#define CPS1_COINAGE_1(diploc) \
	PORT_DIPNAME( 0x07, 0x07, DEF_STR( Coin_A ) ) PORT_DIPLOCATION(diploc ":1,2,3") \
	PORT_DIPSETTING(    0x00, DEF_STR( 4C_1C ) ) \
	PORT_DIPSETTING(    0x01, DEF_STR( 3C_1C ) ) \
	PORT_DIPSETTING(    0x02, DEF_STR( 2C_1C ) ) \
	PORT_DIPSETTING(    0x07, DEF_STR( 1C_1C ) ) \
	PORT_DIPSETTING(    0x06, DEF_STR( 1C_2C ) ) \
	PORT_DIPSETTING(    0x05, DEF_STR( 1C_3C ) ) \
	PORT_DIPSETTING(    0x04, DEF_STR( 1C_4C ) ) \
	PORT_DIPSETTING(    0x03, DEF_STR( 1C_6C ) ) \
	PORT_DIPNAME( 0x38, 0x38, DEF_STR( Coin_B ) ) PORT_DIPLOCATION(diploc ":4,5,6") \
	PORT_DIPSETTING(    0x00, DEF_STR( 4C_1C ) ) \
	PORT_DIPSETTING(    0x08, DEF_STR( 3C_1C ) ) \
	PORT_DIPSETTING(    0x10, DEF_STR( 2C_1C ) ) \
	PORT_DIPSETTING(    0x38, DEF_STR( 1C_1C ) ) \
	PORT_DIPSETTING(    0x30, DEF_STR( 1C_2C ) ) \
	PORT_DIPSETTING(    0x28, DEF_STR( 1C_3C ) ) \
	PORT_DIPSETTING(    0x20, DEF_STR( 1C_4C ) ) \
	PORT_DIPSETTING(    0x18, DEF_STR( 1C_6C ) )

#define CPS1_COINAGE_2(diploc) \
	PORT_DIPNAME( 0x07, 0x07, DEF_STR( Coinage ) ) PORT_DIPLOCATION(diploc ":1,2,3") \
	PORT_DIPSETTING(    0x00, DEF_STR( 4C_1C ) ) \
	PORT_DIPSETTING(    0x01, DEF_STR( 3C_1C ) ) \
	PORT_DIPSETTING(    0x02, DEF_STR( 2C_1C ) ) \
	PORT_DIPSETTING(    0x07, DEF_STR( 1C_1C ) ) \
	PORT_DIPSETTING(    0x06, DEF_STR( 1C_2C ) ) \
	PORT_DIPSETTING(    0x05, DEF_STR( 1C_3C ) ) \
	PORT_DIPSETTING(    0x04, DEF_STR( 1C_4C ) ) \
	PORT_DIPSETTING(    0x03, DEF_STR( 1C_6C ) )

#define CPS1_COINAGE_3(diploc) \
	PORT_DIPNAME( 0x07, 0x07, DEF_STR( Coin_A ) ) PORT_DIPLOCATION(diploc ":1,2,3") \
	PORT_DIPSETTING(    0x01, DEF_STR( 4C_1C ) ) \
	PORT_DIPSETTING(    0x02, DEF_STR( 3C_1C ) ) \
	PORT_DIPSETTING(    0x03, DEF_STR( 2C_1C ) ) \
	PORT_DIPSETTING(    0x00, "2 Coins/1 Credit (1 to continue)" ) \
	PORT_DIPSETTING(    0x07, DEF_STR( 1C_1C ) ) \
	PORT_DIPSETTING(    0x06, DEF_STR( 1C_2C ) ) \
	PORT_DIPSETTING(    0x05, DEF_STR( 1C_3C ) ) \
	PORT_DIPSETTING(    0x04, DEF_STR( 1C_4C ) ) \
	PORT_DIPNAME( 0x38, 0x38, DEF_STR( Coin_B ) ) PORT_DIPLOCATION(diploc ":4,5,6") \
	PORT_DIPSETTING(    0x08, DEF_STR( 4C_1C ) ) \
	PORT_DIPSETTING(    0x10, DEF_STR( 3C_1C ) ) \
	PORT_DIPSETTING(    0x18, DEF_STR( 2C_1C ) ) \
	PORT_DIPSETTING(    0x00, "2 Coins/1 Credit (1 to continue)" ) \
	PORT_DIPSETTING(    0x38, DEF_STR( 1C_1C ) ) \
	PORT_DIPSETTING(    0x30, DEF_STR( 1C_2C ) ) \
	PORT_DIPSETTING(    0x28, DEF_STR( 1C_3C ) ) \
	PORT_DIPSETTING(    0x20, DEF_STR( 1C_4C ) )

#define CPS1_DIFFICULTY_1(diploc) \
	PORT_DIPNAME( 0x07, 0x04, DEF_STR( Difficulty ) ) PORT_DIPLOCATION(diploc ":1,2,3") \
	PORT_DIPSETTING(    0x07, "0 (Easiest)" ) \
	PORT_DIPSETTING(    0x06, "1" ) \
	PORT_DIPSETTING(    0x05, "2" ) \
	PORT_DIPSETTING(    0x04, "3 (Normal)" ) \
	PORT_DIPSETTING(    0x03, "4" ) \
	PORT_DIPSETTING(    0x02, "5" ) \
	PORT_DIPSETTING(    0x01, "6" ) \
	PORT_DIPSETTING(    0x00, "7 (Hardest)" )

#define CPS1_DIFFICULTY_2(diploc) \
	PORT_DIPNAME( 0x07, 0x07, DEF_STR( Difficulty ) ) PORT_DIPLOCATION(diploc ":1,2,3") \
	PORT_DIPSETTING(    0x04, "1 (Easiest)" ) \
	PORT_DIPSETTING(    0x05, "2" ) \
	PORT_DIPSETTING(    0x06, "3" ) \
	PORT_DIPSETTING(    0x07, "4 (Normal)" ) \
	PORT_DIPSETTING(    0x03, "5" ) \
	PORT_DIPSETTING(    0x02, "6" ) \
	PORT_DIPSETTING(    0x01, "7" ) \
	PORT_DIPSETTING(    0x00, "8 (Hardest)" )

/* CPS1 games with 2 players and 3 buttons each */
static INPUT_PORTS_START( cps1_3b )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_COIN1 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_COIN2 )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_SERVICE1 )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_START2 )
	PORT_SERVICE( 0x40, IP_ACTIVE_LOW )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("IN1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1)
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2)
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

/* CPS1 games with 2 players and 2 buttons each */
static INPUT_PORTS_START( cps1_2b )
	PORT_INCLUDE( cps1_3b )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_UNKNOWN )  // no button 3
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_UNKNOWN )  // no button 3
INPUT_PORTS_END

/* CPS1 games with 2 players, 4-way joysticks and 2 buttons each */
static INPUT_PORTS_START( cps1_2b_4way )
	PORT_INCLUDE( cps1_2b )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_4WAY PORT_PLAYER(1)
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_4WAY PORT_PLAYER(1)
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_4WAY PORT_PLAYER(1)
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_4WAY PORT_PLAYER(1)
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_4WAY PORT_PLAYER(2)
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_4WAY PORT_PLAYER(2)
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_4WAY PORT_PLAYER(2)
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_4WAY PORT_PLAYER(2)
INPUT_PORTS_END

/* CPS1 games with 2 players and 1 button each */
static INPUT_PORTS_START( cps1_1b )
	PORT_INCLUDE( cps1_2b )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_UNKNOWN )  // no button 2
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_UNKNOWN )  // no button 2
INPUT_PORTS_END

/* CPS1 games with 3 players and 2 buttons each */
static INPUT_PORTS_START( cps1_3players )
	PORT_INCLUDE( cps1_2b )

	PORT_START("IN2")      /* Player 3 */
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(3)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(3)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(3)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(3)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(3)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(3)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_COIN3 )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START3 )
INPUT_PORTS_END

/* CPS1 games with 4 players and 2 buttons each */
static INPUT_PORTS_START( cps1_4players )
	PORT_INCLUDE( cps1_3players )

	PORT_START("IN3")      /* Player 4 */
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(4)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(4)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(4)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(4)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(4)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(4)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_COIN4 )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START4 )
INPUT_PORTS_END

/* CPS1 games with 2 players and 6 buttons each */
static INPUT_PORTS_START( cps1_6b )
	PORT_INCLUDE( cps1_3b )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("P1 Jab Punch") PORT_PLAYER(1)
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("P1 Strong Punch") PORT_PLAYER(1)
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("P1 Fierce Punch") PORT_PLAYER(1)
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("P2 Jab Punch") PORT_PLAYER(2)
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("P2 Strong Punch") PORT_PLAYER(2)
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("P2 Fierce Punch") PORT_PLAYER(2)

	PORT_START("IN2")      /* Extra buttons */
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("P1 Short Kick") PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("P1 Forward Kick") PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("P1 Roundhouse Kick") PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("P2 Short Kick") PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("P2 Forward Kick") PORT_PLAYER(2)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("P2 Roundhouse Kick") PORT_PLAYER(2)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

/* CPS1 quiz games */
static INPUT_PORTS_START( cps1_quiz )
	PORT_INCLUDE( cps1_3b )

	PORT_MODIFY("IN1")  /* no joystick and 4th button */
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1)
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(2)
INPUT_PORTS_END


static INPUT_PORTS_START( forgottn )
	PORT_INCLUDE( cps1_1b )

	PORT_MODIFY("IN0")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("DSWC")                                  // The manual only mentions two DIP switch banks.
	PORT_DIPUNUSED( 0x80, 0x80 )                        // Is this port brought out to DIP switches or not?
	PORT_DIPUNKNOWN( 0x40, 0x40 )                       // Check code at 0x013c78 (0x013690 in 'lostwrld')
	PORT_DIPUNUSED( 0x20, 0x20 )
	PORT_DIPUNUSED( 0x10, 0x10 )
	PORT_DIPUNUSED( 0x08, 0x08 )
	PORT_DIPUNUSED( 0x04, 0x04 )
	PORT_DIPUNUSED( 0x02, 0x02 )
	PORT_DIPUNUSED( 0x01, 0x01 )

	PORT_START("DSWB")
	CPS1_DIFFICULTY_1( "DIP-B" )
	PORT_DIPUNUSED_DIPLOC( 0x20, 0x20, "DIP-B:4" )
	PORT_DIPUNUSED_DIPLOC( 0x10, 0x10, "DIP-B:5" )
	PORT_DIPUNKNOWN_DIPLOC( 0x08, 0x08, "DIP-B:6" )         // Check code at 0x00111c (0x00112c in 'lostwrld')
	PORT_SERVICE_DIPLOC( 0x40, IP_ACTIVE_LOW, "DIP-B:7" )
	PORT_DIPNAME( 0x80, 0x80, "Freeze" )                    PORT_DIPLOCATION("DIP-B:8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("DSWA")
	CPS1_COINAGE_1( "DIP-A" )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Demo_Sounds ) )      PORT_DIPLOCATION("DIP-A:7")
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Flip_Screen ) )      PORT_DIPLOCATION("DIP-A:8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("DIAL0")
	PORT_BIT( 0x0fff, 0x0000, IPT_DIAL ) PORT_SENSITIVITY(100) PORT_KEYDELTA(20) PORT_CODE_DEC(KEYCODE_Z) PORT_CODE_INC(KEYCODE_X) PORT_PLAYER(1)

	PORT_START("DIAL1")
	PORT_BIT( 0x0fff, 0x0000, IPT_DIAL ) PORT_SENSITIVITY(100) PORT_KEYDELTA(20) PORT_CODE_DEC(KEYCODE_N) PORT_CODE_INC(KEYCODE_M) PORT_PLAYER(2)
INPUT_PORTS_END


static INPUT_PORTS_START( forgottnj ) // Where's the Demo Sound????
	PORT_INCLUDE( cps1_1b )

	PORT_MODIFY("IN0")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("DSWC")                                  // The manual only mentions two DIP switch banks.
	PORT_DIPNAME( 0x01, 0x01, "Slow Motion" )           PORT_DIPLOCATION("DIP-C:1")
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, "Slowest Motion" )        PORT_DIPLOCATION("DIP-C:2")
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNKNOWN_DIPLOC( 0x04, 0x04, "DIP-C:3" )
	PORT_DIPUNKNOWN_DIPLOC( 0x08, 0x08, "DIP-C:4" )
	PORT_DIPUNKNOWN_DIPLOC( 0x10, 0x10, "DIP-C:5" )
	PORT_DIPUNKNOWN_DIPLOC( 0x20, 0x20, "DIP-C:6" )
	PORT_DIPNAME( 0xc0, 0xc0, "Freeze" )                    PORT_DIPLOCATION("DIP-C:7,8")
	PORT_DIPSETTING(    0xc0, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) ) // Gs only takes effect when Freeze is ON
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, "On with Alignment Character" ) // Shows a column of "G" on the leftside of the screen

	PORT_START("DSWA") // DSW banks A & B in reverse order?
	CPS1_DIFFICULTY_1( "DIP-A" ) // Not verified, going by other sets
	PORT_DIPUNKNOWN_DIPLOC( 0x20, 0x20, "DIP-A:4" )
	PORT_DIPUNKNOWN_DIPLOC( 0x10, 0x10, "DIP-A:5" )
	PORT_DIPUNKNOWN_DIPLOC( 0x08, 0x08, "DIP-A:6" )
	PORT_DIPUNKNOWN_DIPLOC( 0x40, 0x40, "DIP-A:7" )
	PORT_SERVICE_DIPLOC( 0x80, IP_ACTIVE_LOW, "DIP-A:8" )

	PORT_START("DSWB") // DSW banks A & B in reverse order?
	PORT_DIPNAME( 0x07, 0x07, DEF_STR( Coin_A ) ) PORT_DIPLOCATION("DIP-B:1,2,3")
	PORT_DIPSETTING(    0x00, DEF_STR( 4C_1C ) )
	PORT_DIPSETTING(    0x01, DEF_STR( 3C_1C ) )
	PORT_DIPSETTING(    0x02, DEF_STR( 2C_1C ) )
	PORT_DIPSETTING(    0x07, DEF_STR( 1C_1C ) )
	PORT_DIPSETTING(    0x06, DEF_STR( 2C_2C ) ) // Must insert 2 coins to get a credit, but credits come in 2s
	PORT_DIPSETTING(    0x05, DEF_STR( 1C_3C ) )
	PORT_DIPSETTING(    0x04, DEF_STR( 1C_4C ) )
	PORT_DIPSETTING(    0x03, DEF_STR( 1C_6C ) )
	PORT_DIPNAME( 0x38, 0x38, DEF_STR( Coin_B ) ) PORT_DIPLOCATION("DIP-B:4,5,6")
	PORT_DIPSETTING(    0x00, DEF_STR( 4C_1C ) )
	PORT_DIPSETTING(    0x08, DEF_STR( 3C_1C ) )
	PORT_DIPSETTING(    0x10, DEF_STR( 2C_1C ) )
	PORT_DIPSETTING(    0x38, DEF_STR( 1C_1C ) )
	PORT_DIPSETTING(    0x30, DEF_STR( 2C_2C ) ) // Must insert 2 coins to get a credit, but credits come in 2s
	PORT_DIPSETTING(    0x28, DEF_STR( 1C_3C ) )
	PORT_DIPSETTING(    0x20, DEF_STR( 1C_4C ) )
	PORT_DIPSETTING(    0x18, DEF_STR( 1C_6C ) )
	PORT_DIPNAME( 0x40, 0x40, "Speed up" )       PORT_DIPLOCATION("DIP-B:7") // Everything is faster including title screen / score wait times
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Flip_Screen ) )      PORT_DIPLOCATION("DIP-B:8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("DIAL0")
	PORT_BIT( 0x0fff, 0x0000, IPT_DIAL ) PORT_SENSITIVITY(100) PORT_KEYDELTA(20) PORT_CODE_DEC(KEYCODE_Z) PORT_CODE_INC(KEYCODE_X) PORT_PLAYER(1)

	PORT_START("DIAL1")
	PORT_BIT( 0x0fff, 0x0000, IPT_DIAL ) PORT_SENSITIVITY(100) PORT_KEYDELTA(20) PORT_CODE_DEC(KEYCODE_N) PORT_CODE_INC(KEYCODE_M) PORT_PLAYER(2)
INPUT_PORTS_END

static INPUT_PORTS_START( ghouls )
	PORT_INCLUDE( cps1_2b_4way )
	/* Service1 doesn't give any credit */

	PORT_START("DSWC")
	PORT_DIPNAME( 0x03, 0x03, DEF_STR( Lives ) )            PORT_DIPLOCATION("SW(C):1,2")
	PORT_DIPSETTING(    0x03, "3" )
	PORT_DIPSETTING(    0x02, "4" )
	PORT_DIPSETTING(    0x01, "5" )
	PORT_DIPSETTING(    0x00, "6" )
	PORT_DIPUNUSED_DIPLOC( 0x04, 0x04, "SW(C):3" )
	PORT_DIPUNUSED_DIPLOC( 0x08, 0x08, "SW(C):4" )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )      PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Demo_Sounds ) )      PORT_DIPLOCATION("SW(C):6") /* not much demo sound, just effects in gameplay section of attract sequence */
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Allow_Continue ) )   PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                  PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )

	PORT_START("DSWB") /* Manual states default difficulty "B" (2) which differs from the normal macro */
	PORT_DIPNAME( 0x07, 0x05, DEF_STR( Difficulty ) ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x80) PORT_DIPLOCATION("SW(B):1,2,3")
	PORT_DIPSETTING(    0x04, "1 (Easiest)" )
	PORT_DIPSETTING(    0x05, "2" )
	PORT_DIPSETTING(    0x06, "3" )
	PORT_DIPSETTING(    0x07, "4 (Normal)" )
	PORT_DIPSETTING(    0x03, "5" )
	PORT_DIPSETTING(    0x02, "6" )
	PORT_DIPSETTING(    0x01, "7" )
	PORT_DIPSETTING(    0x00, "8 (Hardest)" )
	PORT_DIPUNUSED_DIPLOC( 0x08, 0x08, "SW(B):4" )
	PORT_DIPNAME( 0x30, 0x30, DEF_STR( Bonus_Life ) )       PORT_DIPLOCATION("SW(B):5,6")
	PORT_DIPSETTING(    0x20, "10K, 30K and every 30K" )
	PORT_DIPSETTING(    0x10, "20K, 50K and every 70K" )
	PORT_DIPSETTING(    0x30, "30K, 60K and every 70K" )
	PORT_DIPSETTING(    0x00, "40K, 70K and every 80K" )
	PORT_DIPUNUSED_DIPLOC( 0x40, 0x40, "SW(B):7" )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(B):8" )

	PORT_START("DSWA")
	CPS1_COINAGE_1( "SW(A)" )
	PORT_DIPNAME( 0xc0, 0xc0, DEF_STR( Cabinet ) )          PORT_DIPLOCATION("SW(A):7,8")
	PORT_DIPSETTING(    0xc0, "Upright 1 Player" )
	PORT_DIPSETTING(    0x80, "Upright 2 Players" )
//  PORT_DIPSETTING(    0x40, DEF_STR( Cocktail ) )         // Manual says these are both valid settings
	PORT_DIPSETTING(    0x00, DEF_STR( Cocktail ) )         // for 2-player cocktail cabinet
INPUT_PORTS_END

/* Same as 'ghouls' but additional "Freeze" Dip Switch, different "Lives" Dip Switch,
   and LOTS of "debug" features (read the notes to know how to activate them) */
static INPUT_PORTS_START( ghoulsu )
	PORT_INCLUDE( ghouls )

	PORT_MODIFY("DSWC")
	PORT_DIPNAME( 0x03, 0x03, DEF_STR( Lives ) ) PORT_DIPLOCATION("SW(C):1,2")
	PORT_DIPSETTING(    0x00, "2" )
	PORT_DIPSETTING(    0x03, "3" )
	PORT_DIPSETTING(    0x02, "4" )
	PORT_DIPSETTING(    0x01, "5" )

	PORT_MODIFY("DSWB")
	/* Standard Dip Switches */
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unused ) ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x80) PORT_DIPLOCATION("SW(B):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x30, 0x00, DEF_STR( Bonus_Life ) )  PORT_CONDITION("DSWC", 0x80, EQUALS, 0x80) PORT_DIPLOCATION("SW(B):5,6")
	PORT_DIPSETTING(    0x20, "10K, 30K and every 30K" )
	PORT_DIPSETTING(    0x10, "20K, 50K and every 70K" )
	PORT_DIPSETTING(    0x30, "30K, 60K and every 70K" )
	PORT_DIPSETTING(    0x00, "40K, 70K and every 80K" )
/* Manuals states the following bonus settings
    PORT_DIPSETTING(    0x20, "20K, 50K and every 70K" )
    PORT_DIPSETTING(    0x10, "10K, 30K and every 30K" )
    PORT_DIPSETTING(    0x30, "40K, 70K and every 80K" )
    PORT_DIPSETTING(    0x00, "30K, 60K and every 70K" )
*/
	/* Debug Dip Switches */
	PORT_DIPNAME( 0x07, 0x07, "Starting Weapon" ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x00) PORT_DIPLOCATION("SW(B):1,2,3")
	PORT_DIPSETTING(    0x07, "Spear" )
	PORT_DIPSETTING(    0x06, "Knife" )
	PORT_DIPSETTING(    0x05, "Torch" )
	PORT_DIPSETTING(    0x04, "Sword" )
	PORT_DIPSETTING(    0x03, "Axe" )
	PORT_DIPSETTING(    0x02, "Shield" )
	PORT_DIPSETTING(    0x01, "Super Weapon" )
//  PORT_DIPSETTING(    0x00, "INVALID !" )
	PORT_DIPNAME( 0x38, 0x30, "Armor on New Life" ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x00) PORT_DIPLOCATION("SW(B):4,5,6")
//  PORT_DIPSETTING(    0x38, "Silver Armor" )
	PORT_DIPSETTING(    0x18, "Golden Armor" )
	PORT_DIPSETTING(    0x30, "Silver Armor" )
	PORT_DIPSETTING(    0x28, "None (young man)" )
	PORT_DIPSETTING(    0x20, "None (old man)" )
//  PORT_DIPSETTING(    0x10, "INVALID !" )
//  PORT_DIPSETTING(    0x08, "INVALID !" )
//  PORT_DIPSETTING(    0x00, "INVALID !" )

	PORT_DIPUNUSED_DIPLOC( 0x40, 0x40, "SW(B):7" )
	PORT_DIPNAME( 0x80, 0x80, "Freeze" ) PORT_DIPLOCATION("SW(B):8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_MODIFY("DSWA")
	/* Standard Dip Switches */
	PORT_DIPNAME( 0x07, 0x07, DEF_STR( Coin_A ) ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x80) PORT_DIPLOCATION("SW(A):1,2,3")
	PORT_DIPSETTING(    0x00, DEF_STR( 4C_1C ) )
	PORT_DIPSETTING(    0x01, DEF_STR( 3C_1C ) )
	PORT_DIPSETTING(    0x02, DEF_STR( 2C_1C ) )
	PORT_DIPSETTING(    0x07, DEF_STR( 1C_1C ) )
	PORT_DIPSETTING(    0x06, DEF_STR( 1C_2C ) )
	PORT_DIPSETTING(    0x05, DEF_STR( 1C_3C ) )
	PORT_DIPSETTING(    0x04, DEF_STR( 1C_4C ) )
	PORT_DIPSETTING(    0x03, DEF_STR( 1C_6C ) )
	PORT_DIPNAME( 0x38, 0x38, DEF_STR( Coin_B ) ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x80) PORT_DIPLOCATION("SW(A):4,5,6")
	PORT_DIPSETTING(    0x00, DEF_STR( 4C_1C ) )
	PORT_DIPSETTING(    0x08, DEF_STR( 3C_1C ) )
	PORT_DIPSETTING(    0x10, DEF_STR( 2C_1C ) )
	PORT_DIPSETTING(    0x38, DEF_STR( 1C_1C ) )
	PORT_DIPSETTING(    0x30, DEF_STR( 1C_2C ) )
	PORT_DIPSETTING(    0x28, DEF_STR( 1C_3C ) )
	PORT_DIPSETTING(    0x20, DEF_STR( 1C_4C ) )
	PORT_DIPSETTING(    0x18, DEF_STR( 1C_6C ) )
	/* Debug Dip Switches */
	PORT_DIPNAME( 0x0f, 0x0f, "Starting Level" ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x00) PORT_DIPLOCATION("SW(A):1,2,3,4")
	PORT_DIPSETTING(    0x0f, "Level 1 (1st half)" )
	PORT_DIPSETTING(    0x0e, "Level 1 (2nd half)" )
	PORT_DIPSETTING(    0x0d, "Level 2 (1st half)" )
	PORT_DIPSETTING(    0x0c, "Level 2 (2nd half)" )
	PORT_DIPSETTING(    0x0b, "Level 3 (1st half)" )
	PORT_DIPSETTING(    0x0a, "Level 3 (2nd half)" )
	PORT_DIPSETTING(    0x09, "Level 4 (1st half)" )
	PORT_DIPSETTING(    0x08, "Level 4 (2nd half)" )
	PORT_DIPSETTING(    0x07, "Level 5 (1st half)" )
	PORT_DIPSETTING(    0x06, "Level 5 (2nd half)" )
	PORT_DIPSETTING(    0x05, "Level 6" )
//  PORT_DIPSETTING(    0x04, "INVALID !" )
//  PORT_DIPSETTING(    0x03, "INVALID !" )
//  PORT_DIPSETTING(    0x02, "INVALID !" )
//  PORT_DIPSETTING(    0x01, "INVALID !" )
//  PORT_DIPSETTING(    0x00, "INVALID !" )
	PORT_DIPNAME( 0x10, 0x10, "Invulnerability" ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x00) PORT_DIPLOCATION("SW(A):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, "Slow Motion" ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x00) PORT_DIPLOCATION("SW(A):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_DIPNAME( 0xc0, 0xc0, DEF_STR( Cabinet ) ) PORT_DIPLOCATION("SW(A):7,8")
	PORT_DIPSETTING(    0xc0, "Upright 1 Player" )
	PORT_DIPSETTING(    0x80, "Upright 2 Players" )
//  PORT_DIPSETTING(    0x40, DEF_STR( Cocktail ) )             // Manual says these are both valid settings
	PORT_DIPSETTING(    0x00, DEF_STR( Cocktail ) )             // for 2-player cocktail cabinet
INPUT_PORTS_END

/* Same as 'ghouls' but additional "Freeze" Dip Switch */
static INPUT_PORTS_START( daimakai )
	PORT_INCLUDE(ghouls)

	PORT_MODIFY("DSWB")
	PORT_DIPNAME( 0x80, 0x80, "Freeze" )        PORT_DIPLOCATION("SW(B):8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )  // This switch isn't documented in the manual
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( daimakair )
	PORT_INCLUDE(ghouls)

	PORT_MODIFY("DSWC")
	PORT_DIPNAME( 0x0f, 0x0f, "Starting Level" ) PORT_DIPLOCATION("SW(C):1,2,3,4")
	PORT_DIPSETTING(    0x0f, "Level 1-1 (3 lives)" )
	PORT_DIPSETTING(    0x0e, "Level 1-2 (4 lives)" )
	PORT_DIPSETTING(    0x0d, "Level 2-1 (5 lives)" )
	PORT_DIPSETTING(    0x0c, "Level 2-2 (6 lives)" )
	PORT_DIPSETTING(    0x0b, "Level 3-1 (3 lives)" )
	PORT_DIPSETTING(    0x0a, "Level 3-2 (4 lives)" )
	PORT_DIPSETTING(    0x09, "Level 4-1 (5 lives)" )
	PORT_DIPSETTING(    0x08, "Level 4-2 (6 lives)" )
	PORT_DIPSETTING(    0x07, "Level 5-1 (3 lives)" )
	PORT_DIPSETTING(    0x06, "Level 5-2 (4 lives)" )
	PORT_DIPSETTING(    0x05, "Level 6 (5 lives)" )
	PORT_DIPSETTING(    0x04, "Level 6 (6 lives)" )
//  PORT_DIPSETTING(    0x03, "INVALID" )
//  PORT_DIPSETTING(    0x02, "INVALID" )
//  PORT_DIPSETTING(    0x01, "INVALID" )
//  PORT_DIPSETTING(    0x00, "INVALID" )

	PORT_MODIFY("DSWB")
	PORT_DIPNAME( 0x80, 0x80, "Freeze" ) PORT_DIPLOCATION("SW(B):8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

/* "Debug" features to be implemented */
static INPUT_PORTS_START( strider )
	PORT_INCLUDE( cps1_3b )

	PORT_START("DSWA")
	CPS1_COINAGE_1( "SW(A)" )
	PORT_DIPNAME( 0xc0, 0xc0, DEF_STR( Cabinet ) )              PORT_DIPLOCATION("SW(A):7,8")
	PORT_DIPSETTING(    0xc0, "Upright 1 Player" )              // These switches are not documented in the manual
	PORT_DIPSETTING(    0x80, "Upright 2 Players" )
//  PORT_DIPSETTING(    0x40, DEF_STR( Cocktail ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Cocktail ) )

	PORT_START("DSWB") /* Like Ghouls, Strider manual states "B" (or 2) as the recommended difficulty level. */
	PORT_DIPNAME( 0x07, 0x05, DEF_STR( Difficulty ) ) PORT_DIPLOCATION("SW(B):1,2,3")
	PORT_DIPSETTING(    0x04, "1 (Easiest)" )
	PORT_DIPSETTING(    0x05, "2" )
	PORT_DIPSETTING(    0x06, "3" )
	PORT_DIPSETTING(    0x07, "4 (Normal)" )
	PORT_DIPSETTING(    0x03, "5" )
	PORT_DIPSETTING(    0x02, "6" )
	PORT_DIPSETTING(    0x01, "7" )
	PORT_DIPSETTING(    0x00, "8 (Hardest)" )
	/* In 'striderj', bit 3 is stored at 0xff8e77 ($e77,A5) via code at 0x000a2a,
	   but this address is never checked again.
	   In 'strider' and 'stridrjr', this code even doesn't exist ! */
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unused ) )               PORT_DIPLOCATION("SW(B):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )                  // Manual says this is 2c start/1c continue but it
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )                   // doesn't work (see comment above)
	PORT_DIPNAME( 0x30, 0x00, DEF_STR( Bonus_Life ) )           PORT_DIPLOCATION("SW(B):5,6")
/* These show in test mode */
	PORT_DIPSETTING(    0x30, "20K, 40K then every 60K" )
	PORT_DIPSETTING(    0x20, "30K, 50K then every 70K" )
	PORT_DIPSETTING(    0x10, "20K & 60K only" )
	PORT_DIPSETTING(    0x00, "30K & 60K only" )
/* According to manual, these are the proper settings
    PORT_DIPSETTING(    0x30, "40K, 70K then every 80K" )
    PORT_DIPSETTING(    0x20, "20K, 50K then every 70K" )
    PORT_DIPSETTING(    0x10, "10k, 30k then every 30k" )
    PORT_DIPSETTING(    0x00, "30K, 60k then every 70k" )
*/

	PORT_DIPNAME( 0xc0, 0x80, "Internal Diff. on Life Loss" )   PORT_DIPLOCATION("SW(B):7,8")
	PORT_DIPSETTING(    0xc0, "-3" )                            // Check code at 0x00d15a
//  PORT_DIPSETTING(    0x40, "-1" )                            // These switches are not documented in the manual
	PORT_DIPSETTING(    0x00, "-1" )
	PORT_DIPSETTING(    0x80, "Default" )

	PORT_START("DSWC")
	PORT_DIPNAME( 0x03, 0x03, DEF_STR( Lives ) )                PORT_DIPLOCATION("SW(C):1,2")
	PORT_DIPSETTING(    0x00, "2" )                             // "6" in the "test mode" and manual
	PORT_DIPSETTING(    0x03, "3" )
	PORT_DIPSETTING(    0x02, "4" )
	PORT_DIPSETTING(    0x01, "5" )
	PORT_DIPNAME( 0x04, 0x04, "Freeze" )                        PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Free_Play ) )            PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )          PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Demo_Sounds ) )          PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Allow_Continue ) )       PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                      PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )                 // To enable the "debug" features
INPUT_PORTS_END

/* Same as 'strider' but additional "2 Coins to Start, 1 to Continue" Dip Switch */
/* "Debug" features to be implemented */
static INPUT_PORTS_START( stridrua )
	PORT_INCLUDE( strider )

	PORT_MODIFY("DSWB")
	/* In 'striderj', bit 3 is stored at 0xff8e77 ($e77,A5) via code at 0x000a2a,
	   but this address is never checked again.
	   In 'strider' and 'stridrjr', this code even doesn't exist ! */
	PORT_DIPNAME( 0x08, 0x08, "2 Coins to Start, 1 to Continue" )   PORT_DIPLOCATION("SW(B):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )                      // This works in this revision
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( dynwar )
	PORT_INCLUDE( cps1_3b )

	PORT_START("DSWC")
	PORT_DIPNAME( 0x01, 0x01, "Freeze" )                        PORT_DIPLOCATION("SW(C):1")
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )                  // Also affects energy cost - read notes
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )                   // This switch is not documented in the manual
	PORT_DIPNAME( 0x02, 0x02, "Turbo Mode" )                    PORT_DIPLOCATION("SW(C):2")
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )                  // Also affects energy cost - read notes
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )                   // This switch is not documented in the manual
	PORT_DIPUNUSED_DIPLOC( 0x04, 0x04, "SW(C):3" )              // This switch is not documented in the manual
	PORT_DIPUNUSED_DIPLOC( 0x08, 0x08, "SW(C):4" )              // This switch is not documented in the manual
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )          PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Demo_Sounds ) )          PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )                  // "ON"  in the "test mode"
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )                   // "OFF" in the "test mode"
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Allow_Continue ) )       PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )                   // "ON"  in the "test mode"
	PORT_DIPSETTING(    0x40, DEF_STR( Yes ) )                  // "OFF" in the "test mode"
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                      PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )

	PORT_START("DSWB")
	CPS1_DIFFICULTY_2( "SW(B)" )
	PORT_DIPUNUSED_DIPLOC( 0x08, 0x08, "SW(B):4" )              // These five switches are not documented in the
	PORT_DIPUNUSED_DIPLOC( 0x10, 0x10, "SW(B):5" )              // manual
	PORT_DIPUNUSED_DIPLOC( 0x20, 0x20, "SW(B):6" )
	PORT_DIPUNUSED_DIPLOC( 0x40, 0x40, "SW(B):7" )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(B):8" )

	PORT_START("DSWA")
	/* According to the manual, ALL switches 1 to 6 must be ON to have
	   "2 Coins/1 Credit (1 to continue)" for both coin slots */
	CPS1_COINAGE_3( "SW(A)" )
	PORT_DIPUNUSED_DIPLOC( 0x40, 0x40, "SW(A):7" )              // This switch is not documented in the manual
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Free_Play ) )            PORT_DIPLOCATION("SW(A):8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )                  // This switch is not documented in the manual
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

/* Read the notes to know how to activate the "debug" features */
static INPUT_PORTS_START( willow )
	PORT_INCLUDE( cps1_3b )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_START("DSWC")
	/* Standard Dip Switches */
	PORT_DIPNAME( 0x03, 0x02, DEF_STR( Lives ) ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x80) PORT_DIPLOCATION("SW(C):1,2")
	PORT_DIPSETTING(    0x02, "1" )
	PORT_DIPSETTING(    0x03, "2" )
	PORT_DIPSETTING(    0x01, "3" )
	PORT_DIPSETTING(    0x00, "4" )
	PORT_DIPNAME( 0x0c, 0x08, "Vitality" ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x80) PORT_DIPLOCATION("SW(C):3,4")
	PORT_DIPSETTING(    0x00, "2" )
	PORT_DIPSETTING(    0x0c, "3" )
	PORT_DIPSETTING(    0x08, "4" )
	PORT_DIPSETTING(    0x04, "5" )
	/* Debug Dip Switches */
	PORT_DIPNAME( 0x01, 0x01, "Turbo Mode" ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x00) PORT_DIPLOCATION("SW(C):1")
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, "Freeze" ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x00) PORT_DIPLOCATION("SW(C):2")
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, "Slow Motion" ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x00) PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Invulnerability" ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x00) PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )          PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	/* Standard Dip Switches */
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Demo_Sounds ) ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x80) PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	/* Debug Dip Switches */
	PORT_DIPNAME( 0x20, 0x20, "Display Debug Infos" ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x00) PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )

	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Allow_Continue ) )       PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                      PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )                 // To enable the "debug" features

	PORT_START("DSWB")
	/* Standard Dip Switches */
	PORT_DIPNAME( 0x07, 0x07, DEF_STR( Difficulty ) ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x80) PORT_DIPLOCATION("SW(B):1,2,3")
	PORT_DIPSETTING(    0x04, "1 (Easiest)" )
	PORT_DIPSETTING(    0x05, "2" )
	PORT_DIPSETTING(    0x06, "3" )
	PORT_DIPSETTING(    0x07, "4 (Normal)" )
	PORT_DIPSETTING(    0x03, "5" )
	PORT_DIPSETTING(    0x02, "6" )
	PORT_DIPSETTING(    0x01, "7" )
	PORT_DIPSETTING(    0x00, "8 (Hardest)" )
	PORT_DIPNAME( 0x18, 0x18, "Nando Speed" ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x80) PORT_DIPLOCATION("SW(B):4,5")
	PORT_DIPSETTING(    0x10, "Slow" )
	PORT_DIPSETTING(    0x18, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x08, "Fast" )
	PORT_DIPSETTING(    0x00, "Very Fast" )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unused ) ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x80) PORT_DIPLOCATION("SW(B):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unused ) ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x80) PORT_DIPLOCATION("SW(B):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, "Stage Magic Continue" ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x80) PORT_DIPLOCATION("SW(B):8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	/* Debug Dip Switches */
	PORT_DIPNAME( 0x01, 0x01, DEF_STR( Unused ) ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x00) PORT_DIPLOCATION("SW(B):1")
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x1e, 0x1e, "Slow Motion Delay" ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x00) PORT_DIPLOCATION("SW(B):2,3,4,5")
	PORT_DIPSETTING(    0x1e, "2 Frames" )
	PORT_DIPSETTING(    0x1c, "3 Frames" )
	PORT_DIPSETTING(    0x1a, "4 Frames" )
	PORT_DIPSETTING(    0x18, "5 Frames" )
	PORT_DIPSETTING(    0x16, "6 Frames" )
	PORT_DIPSETTING(    0x14, "7 Frames" )
	PORT_DIPSETTING(    0x12, "8 Frames" )
	PORT_DIPSETTING(    0x10, "9 Frames" )
	PORT_DIPSETTING(    0x0e, "10 Frames" )
	PORT_DIPSETTING(    0x0c, "11 Frames" )
	PORT_DIPSETTING(    0x0a, "12 Frames" )
	PORT_DIPSETTING(    0x08, "13 Frames" )
	PORT_DIPSETTING(    0x06, "14 Frames" )
	PORT_DIPSETTING(    0x04, "15 Frames" )
	PORT_DIPSETTING(    0x02, "16 Frames" )
	PORT_DIPSETTING(    0x00, "17 Frames" )
	PORT_DIPNAME( 0xe0, 0xe0, "Starting Level" ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x00) PORT_DIPLOCATION("SW(B):6,7,8")
	PORT_DIPSETTING(    0xe0, "Level 1" )
	PORT_DIPSETTING(    0xc0, "Level 2" )
	PORT_DIPSETTING(    0xa0, "Level 3" )
	PORT_DIPSETTING(    0x80, "Level 4" )
	PORT_DIPSETTING(    0x60, "Level 5" )
	PORT_DIPSETTING(    0x40, "Level 6" )
//  PORT_DIPSETTING(    0x20, "INVALID !" )
//  PORT_DIPSETTING(    0x00, "INVALID !" )

	PORT_START("DSWA")
	/* Standard Dip Switches */
	PORT_DIPNAME( 0x07, 0x07, DEF_STR( Coin_A ) ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x80) PORT_DIPLOCATION("SW(A):1,2,3")
	PORT_DIPSETTING(    0x01, DEF_STR( 4C_1C ) )
	PORT_DIPSETTING(    0x02, DEF_STR( 3C_1C ) )
	PORT_DIPSETTING(    0x03, DEF_STR( 2C_1C ) )
	PORT_DIPSETTING(    0x00, "2 Coins/1 Credit (1 to continue)" )
	PORT_DIPSETTING(    0x07, DEF_STR( 1C_1C ) )
	PORT_DIPSETTING(    0x06, DEF_STR( 1C_2C ) )
	PORT_DIPSETTING(    0x05, DEF_STR( 1C_3C ) )
	PORT_DIPSETTING(    0x04, DEF_STR( 1C_4C ) )
	PORT_DIPNAME( 0x38, 0x38, DEF_STR( Coin_B ) ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x80) PORT_DIPLOCATION("SW(A):4,5,6")
	PORT_DIPSETTING(    0x08, DEF_STR( 4C_1C ) )
	PORT_DIPSETTING(    0x10, DEF_STR( 3C_1C ) )
	PORT_DIPSETTING(    0x18, DEF_STR( 2C_1C ) )
	PORT_DIPSETTING(    0x00, "2 Coins/1 Credit (1 to continue)" )
	PORT_DIPSETTING(    0x38, DEF_STR( 1C_1C ) )
	PORT_DIPSETTING(    0x30, DEF_STR( 1C_2C ) )
	PORT_DIPSETTING(    0x28, DEF_STR( 1C_3C ) )
	PORT_DIPSETTING(    0x20, DEF_STR( 1C_4C ) )
	PORT_DIPNAME( 0xc0, 0xc0, DEF_STR( Cabinet ) ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x80) PORT_DIPLOCATION("SW(A):7,8")
	PORT_DIPSETTING(    0xc0, "Upright 1 Player" )
	PORT_DIPSETTING(    0x80, "Upright 2 Players" )
//  PORT_DIPSETTING(    0x40, DEF_STR( Cocktail ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Cocktail ) )
	/* Debug Dip Switches */
	PORT_DIPNAME( 0x3f, 0x3f, DEF_STR( Free_Play ) ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x00) PORT_DIPLOCATION("SW(A):1,2,3,4,5,6")
	PORT_DIPSETTING(    0x3f, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x38, DEF_STR( On ) )
	/* Other values don't give free play */
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Cabinet ) ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x00) PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, "Upright 1 Player" )
	PORT_DIPSETTING(    0x00, "Upright 2 Players" )
	PORT_DIPNAME( 0x80, 0x80, "Maximum magic/sword power" ) PORT_CONDITION("DSWC", 0x80, EQUALS, 0x00) PORT_DIPLOCATION("SW(A):8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

/* To enable extra choices in the "test mode", you must press "Coin 1" ('5') AND "Service Mode" ('F2') */
static INPUT_PORTS_START( unsquad )
	PORT_INCLUDE( cps1_3b )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_START("DSWA")
	CPS1_COINAGE_3( "SW(A)" )
	/* According to the manual, ALL bits 0 to 5 must be ON to have
	   "2 Coins/1 Credit (1 to continue)" for both coin slots */
	PORT_DIPUNUSED_DIPLOC( 0x40, 0x40, "SW(A):7" )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(A):8" )

	PORT_START("DSWB")
	CPS1_DIFFICULTY_1( "SW(B)" )
	PORT_DIPNAME( 0x18, 0x18, "Damage" )                    PORT_DIPLOCATION("SW(B):4,5")
	PORT_DIPSETTING(    0x10, "Small" )                 // Check code at 0x006f4e
	PORT_DIPSETTING(    0x18, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x08, "Big" )
	PORT_DIPSETTING(    0x00, "Biggest" )
	PORT_DIPUNUSED_DIPLOC( 0x20, 0x20, "SW(B):6" )
	PORT_DIPUNUSED_DIPLOC( 0x40, 0x40, "SW(B):7" )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(B):8" )

	PORT_START("DSWC")
	PORT_DIPUNUSED_DIPLOC( 0x01, 0x01, "SW(C):1" )
	PORT_DIPUNUSED_DIPLOC( 0x02, 0x02, "SW(C):2" )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Free_Play ) )        PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )                    PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )      PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )      PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Allow_Continue ) )   PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x40, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                  PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )
INPUT_PORTS_END

/* To access the hidden pattern test modes, turn the "Service Mode" dip to ON, and hold down "P1 Button 1"
   ('Ctrl') or "P1 Button 2" ('Alt') during the bootup test. Button 1 will load the Scroll (Background) test,
   and Button 2 will load an Obj (Sprite) viewer. */
static INPUT_PORTS_START( ffight )
	PORT_INCLUDE( cps1_2b )

#if 0
/* The button below is not officially documented and does not exist on the control panel, probably a leftover.
   Pressing it will allow you to escape from grabs and choke holds instantly. */

	PORT_MODIFY("IN1")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME ("P1 Button 3 (Cheat)")
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME ("P2 Button 3 (Cheat)")
#endif

	PORT_START("DSWA")
	CPS1_COINAGE_1( "SW(A)" )
	PORT_DIPNAME( 0x40, 0x40, "2 Coins to Start, 1 to Continue" )   PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(A):8" )

	PORT_START("DSWB")
	PORT_DIPNAME( 0x07, 0x04, "Difficulty Level 1" )                PORT_DIPLOCATION("SW(B):1,2,3")
	PORT_DIPSETTING(    0x07, DEF_STR( Easiest ) )      // "01"
	PORT_DIPSETTING(    0x06, DEF_STR( Easier ) )       // "02"
	PORT_DIPSETTING(    0x05, DEF_STR( Easy ) )         // "03"
	PORT_DIPSETTING(    0x04, DEF_STR( Normal ) )       // "04"
	PORT_DIPSETTING(    0x03, DEF_STR( Medium ) )       // "05"
	PORT_DIPSETTING(    0x02, DEF_STR( Hard ) )         // "06"
	PORT_DIPSETTING(    0x01, DEF_STR( Harder ) )       // "07"
	PORT_DIPSETTING(    0x00, DEF_STR( Hardest ) )      // "08"
	PORT_DIPNAME( 0x18, 0x10, "Difficulty Level 2" )                PORT_DIPLOCATION("SW(B):4,5")
	PORT_DIPSETTING(    0x18, DEF_STR( Easy ) )         // "01"
	PORT_DIPSETTING(    0x10, DEF_STR( Normal ) )       // "02"
	PORT_DIPSETTING(    0x08, DEF_STR( Hard ) )         // "03"
	PORT_DIPSETTING(    0x00, DEF_STR( Hardest ) )      // "04"
	PORT_DIPNAME( 0x60, 0x60, DEF_STR( Bonus_Life ) )               PORT_DIPLOCATION("SW(B):6,7")
	PORT_DIPSETTING(    0x60, "100k" )
	PORT_DIPSETTING(    0x40, "200k" )
	PORT_DIPSETTING(    0x20, "100k and every 200k" )
	PORT_DIPSETTING(    0x00, DEF_STR( None ) )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(B):8" )

	PORT_START("DSWC")
	PORT_DIPNAME( 0x03, 0x03, DEF_STR( Lives ) )                    PORT_DIPLOCATION("SW(C):1,2")
	PORT_DIPSETTING(    0x00, "1" )
	PORT_DIPSETTING(    0x03, "2" )
	PORT_DIPSETTING(    0x02, "3" )
	PORT_DIPSETTING(    0x01, "4" )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Free_Play ) )                PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )                            PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )              PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )              PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Allow_Continue ) )           PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x40, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                          PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )
INPUT_PORTS_END

static INPUT_PORTS_START( ffightae )
	PORT_INCLUDE( cps1_3players )

	PORT_START("DSWA")
	CPS1_COINAGE_1( "SW(A)" )
	PORT_DIPNAME( 0x40, 0x40, "2 Coins to Start, 1 to Continue" )   PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(A):8" )

	PORT_START("DSWB")
	PORT_DIPNAME( 0x07, 0x04, "Difficulty Level 1" )                PORT_DIPLOCATION("SW(B):1,2,3")
	PORT_DIPSETTING(    0x07, DEF_STR( Easiest ) )      // "01"
	PORT_DIPSETTING(    0x06, DEF_STR( Easier ) )       // "02"
	PORT_DIPSETTING(    0x05, DEF_STR( Easy ) )         // "03"
	PORT_DIPSETTING(    0x04, DEF_STR( Normal ) )       // "04"
	PORT_DIPSETTING(    0x03, DEF_STR( Medium ) )       // "05"
	PORT_DIPSETTING(    0x02, DEF_STR( Hard ) )         // "06"
	PORT_DIPSETTING(    0x01, DEF_STR( Harder ) )       // "07"
	PORT_DIPSETTING(    0x00, DEF_STR( Hardest ) )      // "08"
	PORT_DIPNAME( 0x18, 0x10, "Difficulty Level 2" )                PORT_DIPLOCATION("SW(B):4,5")
	PORT_DIPSETTING(    0x18, DEF_STR( Easy ) )         // "01"
	PORT_DIPSETTING(    0x10, DEF_STR( Normal ) )       // "02"
	PORT_DIPSETTING(    0x08, DEF_STR( Hard ) )         // "03"
	PORT_DIPSETTING(    0x00, DEF_STR( Hardest ) )      // "04"
	PORT_DIPNAME( 0x60, 0x60, DEF_STR( Bonus_Life ) )               PORT_DIPLOCATION("SW(B):6,7")
	PORT_DIPSETTING(    0x60, "100k" )
	PORT_DIPSETTING(    0x40, "200k" )
	PORT_DIPSETTING(    0x20, "100k and every 200k" )
	PORT_DIPSETTING(    0x00, DEF_STR( None ) )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(B):8" )

	PORT_START("DSWC")
	PORT_DIPNAME( 0x03, 0x03, DEF_STR( Lives ) )                    PORT_DIPLOCATION("SW(C):1,2")
	PORT_DIPSETTING(    0x00, "1" )
	PORT_DIPSETTING(    0x03, "2" )
	PORT_DIPSETTING(    0x02, "3" )
	PORT_DIPSETTING(    0x01, "4" )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Free_Play ) )                PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )                            PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )              PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )              PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Allow_Continue ) )           PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x40, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                          PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )
INPUT_PORTS_END

static INPUT_PORTS_START( 1941 )
	PORT_INCLUDE( cps1_2b )

	PORT_START("DSWA")
	CPS1_COINAGE_1( "SW(A)" )
	PORT_DIPNAME( 0x40, 0x40, "2 Coins to Start, 1 to Continue" )   PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(A):8" )

	PORT_START("DSWB")
	CPS1_DIFFICULTY_1( "SW(B)" )
	PORT_DIPNAME( 0x18, 0x18, "Level Up Timer" )                    PORT_DIPLOCATION("SW(B):4,5")
	PORT_DIPSETTING(    0x18, "More Slowly" )
	PORT_DIPSETTING(    0x10, "Slowly" )
	PORT_DIPSETTING(    0x08, "Quickly" )
	PORT_DIPSETTING(    0x00, "More Quickly" )
	PORT_DIPNAME( 0x60, 0x60, "Bullet's Speed" )                    PORT_DIPLOCATION("SW(B):6,7")
	PORT_DIPSETTING(    0x60, "Very Slow" )
	PORT_DIPSETTING(    0x40, "Slow" )
	PORT_DIPSETTING(    0x20, "Fast" )
	PORT_DIPSETTING(    0x00, "Very Fast" )
	PORT_DIPNAME( 0x80, 0x80, "Initial Vitality" )                  PORT_DIPLOCATION("SW(B):8")
	PORT_DIPSETTING(    0x80, "3 Bars" )
	PORT_DIPSETTING(    0x00, "4 Bars" )

	PORT_START("DSWC")
	PORT_DIPNAME( 0x01, 0x01, "Throttle Game Speed" )               PORT_DIPLOCATION("SW(C):1")
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )                      // turning this off will break the game
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPUNUSED_DIPLOC( 0x02, 0x02, "SW(C):2" )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Free_Play ) )                PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )                            PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )              PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )              PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Allow_Continue ) )           PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x40, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                          PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )
INPUT_PORTS_END

static INPUT_PORTS_START( mercs )
	PORT_INCLUDE( cps1_3players )

	PORT_MODIFY("IN0")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("DSWA")
	CPS1_COINAGE_2( "SW(A)" )
	PORT_DIPUNUSED_DIPLOC( 0x08, 0x08, "SW(A):4" )                  // These three switches are not documented in
	PORT_DIPUNUSED_DIPLOC( 0x10, 0x10, "SW(A):5" )                  // the manual
	PORT_DIPUNUSED_DIPLOC( 0x20, 0x20, "SW(A):6" )
	PORT_DIPNAME( 0x40, 0x40, "2 Coins to Start, 1 to Continue" )   PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(A):8" )                  // This switch is not documented in the manual

	PORT_START("DSWB")
	CPS1_DIFFICULTY_1( "SW(B)" )
	PORT_DIPNAME( 0x08, 0x08, "Coin Slots" )                        PORT_DIPLOCATION("SW(B):4")
	PORT_DIPSETTING(    0x00, "1" )
	PORT_DIPSETTING(    0x08, "3" )                                 // This setting can't be used in two-player mode
	PORT_DIPNAME( 0x10, 0x10, "Play Mode" )                         PORT_DIPLOCATION("SW(B):5")
	PORT_DIPSETTING(    0x00, "2 Players" )
	PORT_DIPSETTING(    0x10, "3 Players" )
	PORT_DIPUNUSED_DIPLOC( 0x20, 0x20, "SW(B):6" )                  // These three switches are not documented in
	PORT_DIPUNUSED_DIPLOC( 0x40, 0x40, "SW(B):7" )                  // the manual
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(B):8" )

	PORT_START("DSWC")
	PORT_DIPUNUSED_DIPLOC( 0x01, 0x01, "SW(C):1" )                  // These three switches are not documented in
	PORT_DIPUNUSED_DIPLOC( 0x02, 0x02, "SW(C):2" )                  // the manual
	PORT_DIPUNUSED_DIPLOC( 0x04, 0x04, "SW(C):3" )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )                            PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )              PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )              PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Allow_Continue ) )           PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x40, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_SERVICE_DIPLOC( 0x80, IP_ACTIVE_LOW, "SW(C):8" )
INPUT_PORTS_END

/* According to code at 0x001c4e ('mtwins') or ('chikij') , ALL bits 0 to 5 of DSWA
   must be ON to have "2 Coins/1 Credit (1 to continue)" for both coin slots.
   But according to routine starting at 0x06b27c ('mtwins') or 0x06b4fa ('chikij'),
   bit 6 of DSWA is tested to have the same "feature" in the "test mode".

   Bits 3 and 4 of DSWB affect the number of lives AND the level of damage when you get hit.
   When bit 5 of DSWB is ON you ALWAYS have 1 life but more energy (0x38 instead of 0x20).
   Useful addresses to know :
     - 0xff147b.b : lives  (player 1)
     - 0xff153b.b : lives  (player 2)
     - 0xff14ab.w : energy (player 1)
     - 0xff156b.w : energy (player 2)
*/
INPUT_PORTS_START( mtwins )
	PORT_INCLUDE( cps1_3b )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_START("DSWA")
	CPS1_COINAGE_1( "SW(A)" )
	PORT_DIPUNUSED_DIPLOC( 0x40, 0x40, "SW(A):7" )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(A):8" )

	PORT_START("DSWB")
	CPS1_DIFFICULTY_1( "SW(B)" )
	PORT_DIPNAME( 0x38, 0x18, DEF_STR( Lives ) )            PORT_DIPLOCATION("SW(B):4,5,6")
//  PORT_DIPSETTING(    0x30, "1" )                         // 0x38 energy, smallest damage
//  PORT_DIPSETTING(    0x38, "1" )                         // 0x38 energy, small damage
//  PORT_DIPSETTING(    0x28, "1" )                         // 0x38 energy, big damage
//  PORT_DIPSETTING(    0x20, "1" )                         // 0x38 energy, biggest damage
	PORT_DIPSETTING(    0x10, "1" )                         // 0x20 energy, smallest damage
	PORT_DIPSETTING(    0x18, "2" )                         // 0x20 energy, small damage
	PORT_DIPSETTING(    0x08, "3" )                         // 0x20 energy, big damage
	PORT_DIPSETTING(    0x00, "4" )                         // 0x20 energy, biggest damage
	PORT_DIPUNUSED_DIPLOC( 0x40, 0x40, "SW(B):7" )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(B):8" )

	PORT_START("DSWC")
	PORT_DIPUNUSED_DIPLOC( 0x01, 0x01, "SW(C):1" )
	PORT_DIPUNUSED_DIPLOC( 0x02, 0x02, "SW(C):2" )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Free_Play ) )        PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )                    PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )      PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )      PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Allow_Continue ) )   PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x40, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                  PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )
INPUT_PORTS_END

/* I guess that bit 8 of DSWB was used for debug purpose :
     - code at 0x001094 : move players during "attract mode"
     - code at 0x019b62 ('msword' and 'mswordr1'), 0x019bde ('mswordu') or 0x019c26 ('mswordj') : unknown effect
     - code at 0x01c322 ('msword' and 'mswordr1'), 0x01c39e ('mswordu') or 0x01c3e0 ('mswordj') : unknown effect
   These features are not available because of the 'bra' instruction after the test of bit 7. */
static INPUT_PORTS_START( msword )
	PORT_INCLUDE( cps1_3b )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_START("DSWA")
	CPS1_COINAGE_1( "SW(A)" )
	PORT_DIPNAME( 0x40, 0x40, "2 Coins to Start, 1 to Continue" )       PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(A):8" )

	PORT_START("DSWB")
	PORT_DIPNAME( 0x07, 0x04, "Player's vitality consumption" )         PORT_DIPLOCATION("SW(B):1,2,3") // "Level 1"
	PORT_DIPSETTING(    0x07, "1 (Easiest)" )                   // "Easy 3"             (-1 every 28 frames)
	PORT_DIPSETTING(    0x06, "2" )                             // "Easy 2"             (-1 every 24 frames)
	PORT_DIPSETTING(    0x05, "3" )                             // "Easy 1"             (-1 every 20 frames)
	PORT_DIPSETTING(    0x04, "4 (Normal)" )                    // DEF_STR( Normal )    (-1 every 18 frames)
	PORT_DIPSETTING(    0x03, "5" )                             // "Difficult 1"        (-1 every 16 frames)
	PORT_DIPSETTING(    0x02, "6" )                             // "Difficult 2"        (-1 every 14 frames)
	PORT_DIPSETTING(    0x01, "7" )                             // "Difficult 3"        (-1 every 12 frames)
	PORT_DIPSETTING(    0x00, "8 (Hardest)" )                   // "Difficult 4"        (-1 every 8 frames)
	PORT_DIPNAME( 0x38, 0x38, "Enemy's vitality and attacking power" )  PORT_DIPLOCATION("SW(B):4,5,6") // "Level 2"
	PORT_DIPSETTING(    0x20, "1 (Easiest)" )                   // "Easy 3"
	PORT_DIPSETTING(    0x28, "2" )                             // "Easy 2"
	PORT_DIPSETTING(    0x30, "3" )                             // "Easy 1"
	PORT_DIPSETTING(    0x38, "4 (Normal)" )                    // DEF_STR( Normal )
	PORT_DIPSETTING(    0x18, "5" )                             // "Difficult 1"
	PORT_DIPSETTING(    0x10, "6" )                             // "Difficult 2"
	PORT_DIPSETTING(    0x08, "7" )                             // "Difficult 3"
	PORT_DIPSETTING(    0x00, "8 (Hardest)" )                   // "Difficult 4"
	PORT_DIPNAME( 0x40, 0x00, "Stage Select" )                          PORT_DIPLOCATION("SW(B):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(B):8" )

	PORT_START("DSWC")
	PORT_DIPNAME( 0x03, 0x03, "Vitality Packs" )                        PORT_DIPLOCATION("SW(C):1,2")
	PORT_DIPSETTING(    0x00, "1" )                             // 0x0320
	PORT_DIPSETTING(    0x03, "2" )                             // 0x0640
	PORT_DIPSETTING(    0x02, "3 (2 when continue)" )           // 0x0960 (0x0640 when continue)
	PORT_DIPSETTING(    0x01, "4 (3 when continue)" )           // 0x0c80 (0x0960 when continue)
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Free_Play ) )                    PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )                                PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )                  PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )                  PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Allow_Continue ) )               PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x40, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                              PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )
INPUT_PORTS_END

static INPUT_PORTS_START( cawing )
	PORT_INCLUDE( cps1_3b )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_START("DSWA")
	CPS1_COINAGE_1( "SW(A)" )
	PORT_DIPNAME( 0x40, 0x40, "2 Coins to Start, 1 to Continue" )       PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )                          // Overrides all other coinage settings
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )                           // according to manual
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(A):8" )                      // This switch is not documented

	PORT_START("DSWB")
	PORT_DIPNAME( 0x07, 0x04, "Difficulty Level (Enemy's Strength)" )   PORT_DIPLOCATION("SW(B):1,2,3")
	PORT_DIPSETTING(    0x07, "1 (Easiest)" )
	PORT_DIPSETTING(    0x06, "2" )
	PORT_DIPSETTING(    0x05, "3" )
	PORT_DIPSETTING(    0x04, "4 (Normal)" )
	PORT_DIPSETTING(    0x03, "5" )
	PORT_DIPSETTING(    0x02, "6" )
	PORT_DIPSETTING(    0x01, "7" )
	PORT_DIPSETTING(    0x00, "8 (Hardest)" )
	PORT_DIPNAME( 0x18, 0x18, "Difficulty Level (Player's Strength)" )  PORT_DIPLOCATION("SW(B):4,5")
	PORT_DIPSETTING(    0x10, DEF_STR( Easy ) )
	PORT_DIPSETTING(    0x18, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Hard ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Hardest ) )
	PORT_DIPUNUSED_DIPLOC( 0x20, 0x20, "SW(B):6" )                      // This switch is not documented
	PORT_DIPUNUSED_DIPLOC( 0x40, 0x40, "SW(B):7" )                      // This switch is not documented
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(B):8" )                      // This switch is not documented

	PORT_START("DSWC")
	PORT_DIPUNUSED_DIPLOC( 0x01, 0x01, "SW(C):1" )                      // This switch is not documented
	PORT_DIPUNUSED_DIPLOC( 0x02, 0x02, "SW(C):2" )                      // This switch is not documented
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Free_Play ) )                    PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )                                PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )                  PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )                  PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Allow_Continue ) )               PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x40, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                              PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )
INPUT_PORTS_END

/* "Debug" features to be implemented */
static INPUT_PORTS_START( nemo )
	PORT_INCLUDE( cps1_3b )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_START("DSWA")
	CPS1_COINAGE_1( "SW(A)" )
	PORT_DIPNAME( 0x40, 0x40, "2 Coins to Start, 1 to Continue" )   PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(A):8" )

	PORT_START("DSWB")
	CPS1_DIFFICULTY_1( "SW(B)" )
	PORT_DIPNAME( 0x18, 0x18, "Life Bar" )                          PORT_DIPLOCATION("SW(B):4,5")
	PORT_DIPSETTING(    0x00, "Minimum" )
	PORT_DIPSETTING(    0x18, DEF_STR( Medium ) )
//  PORT_DIPSETTING(    0x10, DEF_STR( Medium ) )
	PORT_DIPSETTING(    0x08, "Maximum" )
	PORT_DIPUNUSED_DIPLOC( 0x20, 0x20, "SW(B):6" )
	PORT_DIPUNUSED_DIPLOC( 0x40, 0x40, "SW(B):7" )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(B):8" )

	PORT_START("DSWC")
	PORT_DIPNAME( 0x03, 0x03, DEF_STR( Lives ) )                    PORT_DIPLOCATION("SW(C):1,2")
	PORT_DIPSETTING(    0x02, "1" )
	PORT_DIPSETTING(    0x03, "2" )
	PORT_DIPSETTING(    0x01, "3" )
	PORT_DIPSETTING(    0x00, "4" )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Free_Play ) )                PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )                            PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )              PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )              PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Allow_Continue ) )           PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x40, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                          PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )                 // To enable the "debug" features
INPUT_PORTS_END

INPUT_PORTS_START( sf2 )
	PORT_INCLUDE( cps1_6b )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_START("DSWA")
	CPS1_COINAGE_1( "SW(A)" )
	PORT_DIPNAME( 0x40, 0x40, "2 Coins to Start, 1 to Continue" )   PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(A):8" )

	PORT_START("DSWB")
	CPS1_DIFFICULTY_1( "SW(B)" )
	PORT_DIPUNUSED_DIPLOC( 0x08, 0x08, "SW(B):4" )
	PORT_DIPUNUSED_DIPLOC( 0x10, 0x10, "SW(B):5" )
	PORT_DIPUNUSED_DIPLOC( 0x20, 0x20, "SW(B):6" )
	PORT_DIPUNUSED_DIPLOC( 0x40, 0x40, "SW(B):7" )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(B):8" )

	PORT_START("DSWC")
	PORT_DIPUNUSED_DIPLOC( 0x01, 0x01, "SW(C):1" )
	PORT_DIPUNUSED_DIPLOC( 0x02, 0x02, "SW(C):2" )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Free_Play ) )                PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )                            PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )              PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )              PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Allow_Continue ) )           PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x40, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                          PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )
INPUT_PORTS_END

static INPUT_PORTS_START( sf2j )
	PORT_INCLUDE( sf2 )

	PORT_MODIFY("DSWB")
	PORT_DIPNAME( 0x08, 0x00, "2 Players Game" )                    PORT_DIPLOCATION("SW(B):4")
	PORT_DIPSETTING(    0x08, "1 Credit/No Continue" ) // 1 Credit for versus play, and "Here comes a new challenger" option is disabled
	PORT_DIPSETTING(    0x00, "2 Credits/Winner Continue" ) //Winner stays, loser pays, in other words.
INPUT_PORTS_END

/* Same as sf2j but options are inverted. Default position here is 2 Credits for versus play */
static INPUT_PORTS_START( sf2cej )
	PORT_INCLUDE( sf2 )

	PORT_MODIFY("DSWB")
	PORT_DIPNAME( 0x08, 0x08, "2 Players Game" )                    PORT_DIPLOCATION("SW(B):4")
	PORT_DIPSETTING(    0x08, "2 Credits/Winner Continue" ) //Winner stays, loser pays, in other words.
	PORT_DIPSETTING(    0x00, "1 Credit/No Continue" )  // 1 Credit for versus play, and "Here comes a new challenger" option is disabled
INPUT_PORTS_END

static INPUT_PORTS_START( sf2rb )
	PORT_INCLUDE( sf2 )

	PORT_MODIFY("DSWB")
	PORT_DIPNAME( 0xf0, 0xf0, "Turbo Vs CPU" )                      PORT_DIPLOCATION("SW(B):5,6,7,8")
	PORT_DIPSETTING(    0xf0, DEF_STR( Off ) )
	PORT_DIPSETTING(    0xe0, "Fixed 1" )
	PORT_DIPSETTING(    0xd0, "Fixed 2" )
	PORT_DIPSETTING(    0xc0, "Fixed 3" )
	PORT_DIPSETTING(    0xb0, "Fixed 4" )
	PORT_DIPSETTING(    0xa0, "Fixed 5" )
	PORT_DIPSETTING(    0x90, "Fixed 6" )
	PORT_DIPSETTING(    0x80, "Fixed 7" )
	PORT_DIPSETTING(    0x70, "Progressive 1" )
	PORT_DIPSETTING(    0x60, "Progressive 2" )
	PORT_DIPSETTING(    0x50, "Progressive 3" )
	PORT_DIPSETTING(    0x40, "Progressive 4" )
	PORT_DIPSETTING(    0x30, "Progressive 5" )
	PORT_DIPSETTING(    0x20, "Progressive 6" )
	PORT_DIPSETTING(    0x10, "Progressive 7" )
	PORT_DIPSETTING(    0x00, "Progressive 8" )

	PORT_MODIFY("DSWC")
	PORT_DIPNAME( 0x01, 0x01, "Projectile Path" )                   PORT_DIPLOCATION("SW(C):1")
	PORT_DIPSETTING(    0x01, "Homing" )
	PORT_DIPSETTING(    0x00, "Zigzag" )
INPUT_PORTS_END

static INPUT_PORTS_START( sf2hack )
	PORT_INCLUDE( sf2 )

	PORT_MODIFY("IN2")      /* Extra buttons */
	PORT_BIT( 0x00ff, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("P1 Short Kick") PORT_PLAYER(1)
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("P1 Forward Kick") PORT_PLAYER(1)
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("P1 Roundhouse Kick") PORT_PLAYER(1)
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("P2 Short Kick") PORT_PLAYER(2)
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("P2 Forward Kick") PORT_PLAYER(2)
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("P2 Roundhouse Kick") PORT_PLAYER(2)
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

static INPUT_PORTS_START( sf2level )
	PORT_INCLUDE( sf2 )

	PORT_MODIFY("DSWB")
	PORT_DIPNAME( 0xf0, 0xf0, "Level" )  PORT_DIPLOCATION("SW(B):5,6,7,8")
	PORT_DIPSETTING(    0xf0, "0" )
	PORT_DIPSETTING(    0xe0, "1" )
	PORT_DIPSETTING(    0xd0, "2" )
	PORT_DIPSETTING(    0xc0, "3" )
	PORT_DIPSETTING(    0xb0, "4" )
	PORT_DIPSETTING(    0xa0, "5" )
	PORT_DIPSETTING(    0x90, "6" )
	PORT_DIPSETTING(    0x80, "7" )
	PORT_DIPSETTING(    0x70, "8" )
	PORT_DIPSETTING(    0x60, "9" )
	PORT_DIPSETTING(    0x50, "10" )
	PORT_DIPSETTING(    0x40, "11" )
	PORT_DIPSETTING(    0x30, "12" )
	PORT_DIPSETTING(    0x20, "13" )
	PORT_DIPSETTING(    0x10, "14" )
	PORT_DIPSETTING(    0x00, "15" )
INPUT_PORTS_END

static INPUT_PORTS_START( sf2m2 )
	PORT_INCLUDE( sf2hack )

	PORT_MODIFY("DSWB")
	PORT_DIPNAME( 0x10, 0x00, "It needs to be High" )                       PORT_DIPLOCATION("SW(B):5")
	PORT_DIPSETTING(    0x10, DEF_STR ( Low ) )
	PORT_DIPSETTING(    0x00, DEF_STR ( High ) )
INPUT_PORTS_END

static INPUT_PORTS_START( sf2m4 )
	PORT_INCLUDE( sf2hack )

	PORT_MODIFY("DSWB")
	PORT_DIPNAME( 0x08, 0x00, "2 Players Game" )                    PORT_DIPLOCATION("SW(B):4")
	PORT_DIPSETTING(    0x08, "1 Credit/No Continue" )
	PORT_DIPSETTING(    0x00, "2 Credits/Winner Continue" ) //Winner stays, loser pays, in other words.
INPUT_PORTS_END

/* SWB.4, SWB.5 and SWB.6 need to be enabled simultaneously for turbo mode */
static INPUT_PORTS_START( sf2amf )
	PORT_INCLUDE( sf2hack )

	PORT_MODIFY("DSWB")
	PORT_DIPNAME( 0x08, 0x08, "Turbo Mode Switch 1 of 3" )   PORT_DIPLOCATION("SW(B):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, "Turbo Mode Switch 2 of 3" )   PORT_DIPLOCATION("SW(B):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, "Turbo Mode Switch 3 of 3" )   PORT_DIPLOCATION("SW(B):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

/* SWB.6 enables turbo mode, SWB.4 and SWB.5 sets the speed */
static INPUT_PORTS_START( sf2amfx )
	PORT_INCLUDE( sf2hack )

	PORT_MODIFY("DSWB")
	PORT_DIPNAME( 0x18, 0x18, "Game Speed" )   PORT_DIPLOCATION("SW(B):4,5")
	PORT_DIPSETTING(    0x18, "Normal" )
	PORT_DIPSETTING(    0x10, "Fast" )
	PORT_DIPSETTING(    0x08, "Very Fast" )
	PORT_DIPSETTING(    0x00, "Extremely Fast" )
	PORT_DIPNAME( 0x20, 0x20, "Turbo Mode Enable" )   PORT_DIPLOCATION("SW(B):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )  // normal speed
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )   // the speed set by SWB.4 and SWB.5
INPUT_PORTS_END

static INPUT_PORTS_START( sf2accp2 )
	PORT_INCLUDE( sf2 )

	PORT_MODIFY("DSWA")
	PORT_DIPNAME( 0x80, 0x00, "Shot Type" )   PORT_DIPLOCATION("SW(A):8")
	PORT_DIPSETTING(    0x80, "Directional shots curve up or down" )
	PORT_DIPSETTING(    0x00, "3D wave shots slow-med-fast" )

	PORT_MODIFY("DSWB")
	PORT_DIPNAME( 0x38, 0x20, "Game speed" )   PORT_DIPLOCATION("SW(B):4,5,6") // Manual has some errors here
	PORT_DIPSETTING(    0x38, "Extremely fast" ) // loop counter 30
	PORT_DIPSETTING(    0x30, "Very fast" ) // loop counter 70
	PORT_DIPSETTING(    0x28, "Fast" ) // loop counter 90
	PORT_DIPSETTING(    0x20, "Normal" ) // loop counter 150
	PORT_DIPSETTING(    0x18, "Slow" ) // loop counter 190
	PORT_DIPSETTING(    0x10, "Very slow" ) // loop counter 230
	PORT_DIPSETTING(    0x00, "Slowest" ) // loop counter 310
	PORT_DIPSETTING(    0x08, "Speed test mode" ) // loop counter 1
	// Manual says: we suggest changing the "Special rapid multiple shots feature on a random basis,
	// never turning on more than 1 at any one time, as this feature will prolong the game time.
	PORT_DIPNAME( 0x40, 0x40, "Guile special rapid multiple shots" )   PORT_DIPLOCATION("SW(B):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, "Blanka special rapid multiple shots" )   PORT_DIPLOCATION("SW(B):8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_MODIFY("DSWC")
	PORT_DIPNAME( 0x01, 0x01, "Ken special rapid multiple shots" )   PORT_DIPLOCATION("SW(C):1")
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, "Ryu special rapid multiple shots" )   PORT_DIPLOCATION("SW(C):2")
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

INPUT_PORTS_START( sf2bhh )
	PORT_INCLUDE( sf2 )

	PORT_MODIFY("DSWC")
	PORT_DIPNAME( 0x01, 0x01, "Turbo Mode" )   PORT_DIPLOCATION("SW(C):1")
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( 3wonders )
	PORT_INCLUDE( cps1_3b )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_START("DSWA")
	CPS1_COINAGE_1( "SW(A)" )
	PORT_DIPNAME( 0x40, 0x40, "2 Coins to Start, 1 to Continue" ) PORT_CONDITION("DSWA", 0x3f,NOTEQUALS,0x00) PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Free_Play ) ) PORT_CONDITION("DSWA", 0x3f, EQUALS, 0x00) PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	/* Free Play: ALL bits 0 to 7 must be ON ; 4C_1C, 4C_1C, 2 Coins to Start, 1 to Continue ON */
	PORT_DIPNAME( 0x80, 0x80, "Freeze" )                            PORT_DIPLOCATION("SW(A):8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("DSWB")
	PORT_DIPNAME( 0x03, 0x02, "Lives (Midnight Wanderers)" )        PORT_DIPLOCATION("SW(B):1,2")
	PORT_DIPSETTING(    0x03, "1" )
	PORT_DIPSETTING(    0x02, "2" )
	PORT_DIPSETTING(    0x01, "3" )
	PORT_DIPSETTING(    0x00, "5" )
	PORT_DIPNAME( 0x0c, 0x08, "Difficulty (Midnight Wanderers)" )   PORT_DIPLOCATION("SW(B):3,4")
	PORT_DIPSETTING(    0x0c, DEF_STR( Easy ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Hard ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Hardest ) )
	PORT_DIPNAME( 0x30, 0x10, "Lives (Chariot)" )                   PORT_DIPLOCATION("SW(B):5,6")
	PORT_DIPSETTING(    0x30, "1" )
	PORT_DIPSETTING(    0x20, "2" )
	PORT_DIPSETTING(    0x10, "3" )
	PORT_DIPSETTING(    0x00, "5" )
	PORT_DIPNAME( 0xc0, 0x80, "Difficulty (Chariot)" )              PORT_DIPLOCATION("SW(B):7,8")
	PORT_DIPSETTING(    0xc0, DEF_STR( Easy ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Hard ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Hardest ) )

	PORT_START("DSWC")
	PORT_DIPNAME( 0x03, 0x01, "Lives (Don't Pull)" )                PORT_DIPLOCATION("SW(C):1,2")
	PORT_DIPSETTING(    0x03, "1" )
	PORT_DIPSETTING(    0x02, "2" )
	PORT_DIPSETTING(    0x01, "3" )
	PORT_DIPSETTING(    0x00, "5" )
	PORT_DIPNAME( 0x0c, 0x08, "Difficulty (Don't Pull)" )           PORT_DIPLOCATION("SW(C):3,4")
	PORT_DIPSETTING(    0x0c, DEF_STR( Easy ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Hard ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Hardest ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )              PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )              PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Allow_Continue ) )           PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x40, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                          PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )
INPUT_PORTS_END

static INPUT_PORTS_START( kod )
	PORT_INCLUDE( cps1_3players )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_START("DSWA")
	CPS1_COINAGE_2( "SW(A)" )
	PORT_DIPNAME( 0x08, 0x08, "Coin Slots" )                        PORT_DIPLOCATION("SW(A):4")
	PORT_DIPSETTING(    0x00, "1" )
	PORT_DIPSETTING(    0x08, "3" )
	PORT_DIPNAME( 0x10, 0x10, "Play Mode" )                         PORT_DIPLOCATION("SW(A):5")
	PORT_DIPSETTING(    0x00, "2 Players" )
	PORT_DIPSETTING(    0x10, "3 Players" )
	PORT_DIPUNUSED_DIPLOC( 0x20, 0x20, "SW(A):6" )
	PORT_DIPNAME( 0x40, 0x40, "2 Coins to Start, 1 to Continue" )   PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(A):8" )

	PORT_START("DSWB")
	CPS1_DIFFICULTY_1( "SW(B)" )
	PORT_DIPNAME( 0x38, 0x38, DEF_STR( Lives ) )                    PORT_DIPLOCATION("SW(B):4,5,6")
	PORT_DIPSETTING(    0x30, "1" )
	PORT_DIPSETTING(    0x38, "2" )
	PORT_DIPSETTING(    0x28, "3" )
	PORT_DIPSETTING(    0x20, "4" )
	PORT_DIPSETTING(    0x18, "5" )
	PORT_DIPSETTING(    0x10, "6" )
	PORT_DIPSETTING(    0x08, "7" )
	PORT_DIPSETTING(    0x00, "8" )
	PORT_DIPNAME( 0xc0, 0xc0, DEF_STR( Bonus_Life ) )               PORT_DIPLOCATION("SW(B):7,8")
	PORT_DIPSETTING(    0x80, "80k and every 400k" )
	PORT_DIPSETTING(    0x40, "160k and every 450k" )
	PORT_DIPSETTING(    0xc0, "200k and every 450k" )
	PORT_DIPSETTING(    0x00, DEF_STR( None ) )

	PORT_START("DSWC")
	PORT_DIPUNUSED_DIPLOC( 0x01, 0x01, "SW(C):1" )
	PORT_DIPUNUSED_DIPLOC( 0x02, 0x02, "SW(C):2" )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Free_Play ) )                PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )                            PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )              PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )              PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Allow_Continue ) )           PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x40, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                          PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )
INPUT_PORTS_END

/* Needs further checking
   Same as kod but different "Bonus_life" values */
static INPUT_PORTS_START( kodr1 )
	PORT_INCLUDE( kod )

	PORT_MODIFY("DSWB")
	PORT_DIPNAME( 0xc0, 0xc0, DEF_STR( Bonus_Life ) )               PORT_DIPLOCATION("SW(B):7,8")
	PORT_DIPSETTING(    0x80, "80k and every 400k" )
	PORT_DIPSETTING(    0xc0, "100k and every 450k" )
	PORT_DIPSETTING(    0x40, "160k and every 450k" )
	PORT_DIPSETTING(    0x00, DEF_STR( None ) )
INPUT_PORTS_END

INPUT_PORTS_START( captcomm )
	PORT_INCLUDE( cps1_4players )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_START("DSWA")
	CPS1_COINAGE_2( "SW(A)" )
	PORT_DIPUNUSED_DIPLOC( 0x08, 0x08, "SW(A):4" )                  // The manual says to leave these three
	PORT_DIPUNUSED_DIPLOC( 0x10, 0x10, "SW(A):5" )                  // switches off.  Does turning them on cause
	PORT_DIPUNUSED_DIPLOC( 0x20, 0x20, "SW(A):6" )                  // any "undesirable" behaviour?
	PORT_DIPNAME( 0x40, 0x40, "2 Coins to Start, 1 to Continue" )   PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(A):8" )                  // Unused according to manual

	PORT_START("DSWB")
	PORT_DIPNAME( 0x07, 0x04, "Difficulty 1" )                      PORT_DIPLOCATION("SW(B):1,2,3")
	PORT_DIPSETTING(    0x07, "1 (Easiest)" )
	PORT_DIPSETTING(    0x06, "2" )
	PORT_DIPSETTING(    0x05, "3" )
	PORT_DIPSETTING(    0x04, "4 (Normal)" )
	PORT_DIPSETTING(    0x03, "5" )
	PORT_DIPSETTING(    0x02, "6" )
	PORT_DIPSETTING(    0x01, "7" )
	PORT_DIPSETTING(    0x00, "8 (Hardest)" )
	PORT_DIPNAME( 0x18, 0x10, "Difficulty 2" )                      PORT_DIPLOCATION("SW(B):4,5")
	PORT_DIPSETTING(    0x18, DEF_STR( Easy ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Hard ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Hardest ) )
	PORT_DIPUNUSED_DIPLOC( 0x20, 0x20, "SW(B):6" )                  // Manual says to leave this switch off.
	PORT_DIPNAME( 0xc0, 0xc0, "Play Mode" )                         PORT_DIPLOCATION("SW(B):7,8")
	PORT_DIPSETTING(    0x40, "1 Players" ) // Actual setting is 4 players
	PORT_DIPSETTING(    0xc0, "2 Players" )
	PORT_DIPSETTING(    0x80, "3 Players" )
	PORT_DIPSETTING(    0x00, "4 Players" )

	PORT_START("DSWC")
	PORT_DIPNAME( 0x03, 0x03, DEF_STR( Lives ) )                    PORT_DIPLOCATION("SW(C):1,2")
	PORT_DIPSETTING(    0x00, "1" )
	PORT_DIPSETTING(    0x03, "2" )
	PORT_DIPSETTING(    0x02, "3" )
	PORT_DIPSETTING(    0x01, "4" )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Free_Play ) )                PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )                            PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )              PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )              PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Allow_Continue ) )           PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x40, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                          PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )
INPUT_PORTS_END

INPUT_PORTS_START( knights )
	PORT_INCLUDE( cps1_3players )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_START("DSWA")
	CPS1_COINAGE_2( "SW(A)" )
	PORT_DIPUNUSED_DIPLOC( 0x08, 0x08, "SW(A):4" )
	PORT_DIPUNUSED_DIPLOC( 0x10, 0x10, "SW(A):5" )
	PORT_DIPUNUSED_DIPLOC( 0x20, 0x20, "SW(A):6" )
	PORT_DIPNAME( 0x40, 0x40, "2 Coins to Start, 1 to Continue" )   PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(A):8" )

	PORT_START("DSWB")
	PORT_DIPNAME( 0x07, 0x04, "Enemy's attack frequency" )          PORT_DIPLOCATION("SW(B):1,2,3")
	PORT_DIPSETTING(    0x07, "1 (Easiest)" )
	PORT_DIPSETTING(    0x06, "2" )
	PORT_DIPSETTING(    0x05, "3" )
	PORT_DIPSETTING(    0x04, "4 (Normal)" )
	PORT_DIPSETTING(    0x03, "5" )
	PORT_DIPSETTING(    0x02, "6" )
	PORT_DIPSETTING(    0x01, "7" )
	PORT_DIPSETTING(    0x00, "8 (Hardest)" )
	PORT_DIPNAME( 0x38, 0x38, "Enemy's attack power" )              PORT_DIPLOCATION("SW(B):4,5,6")
	PORT_DIPSETTING(    0x00, "1 (Easiest)" )
	PORT_DIPSETTING(    0x08, "2" )
	PORT_DIPSETTING(    0x10, "3" )
	PORT_DIPSETTING(    0x38, "4 (Normal)" )
	PORT_DIPSETTING(    0x30, "5" )
	PORT_DIPSETTING(    0x28, "6" )
	PORT_DIPSETTING(    0x20, "7" )
	PORT_DIPSETTING(    0x18, "8 (Hardest)" )
	PORT_DIPNAME( 0x40, 0x40, "Coin Slots" )                        PORT_DIPLOCATION("SW(B):7")
	PORT_DIPSETTING(    0x00, "1" )
	PORT_DIPSETTING(    0x40, "3" )
	PORT_DIPNAME( 0x80, 0x80, "Play Mode" )                         PORT_DIPLOCATION("SW(B):8")
	PORT_DIPSETTING(    0x00, "2 Players" )
	PORT_DIPSETTING(    0x80, "3 Players" )

	PORT_START("DSWC")
	PORT_DIPNAME( 0x03, 0x03, DEF_STR( Lives ) )                    PORT_DIPLOCATION("SW(C):1,2")
	PORT_DIPSETTING(    0x00, "1" )
	PORT_DIPSETTING(    0x03, "2" )
	PORT_DIPSETTING(    0x02, "3" )
	PORT_DIPSETTING(    0x01, "4" )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Free_Play ) )                PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )                            PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )              PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )              PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Allow_Continue ) )           PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x40, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                          PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )
INPUT_PORTS_END

INPUT_PORTS_START( varth )
	PORT_INCLUDE( cps1_3b )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_START("DSWA")
	CPS1_COINAGE_1( "SW(A)" )
	PORT_DIPNAME( 0x40, 0x40, "2 Coins to Start, 1 to Continue" )   PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(A):8" )

	PORT_START("DSWB")
	CPS1_DIFFICULTY_1( "SW(B)" )
	PORT_DIPNAME( 0x18, 0x10, DEF_STR( Bonus_Life ) )               PORT_DIPLOCATION("SW(B):4,5")
	PORT_DIPSETTING(    0x18, "600k and every 1.400k" )
	PORT_DIPSETTING(    0x10, "600k 2.000k and 4500k" )
	PORT_DIPSETTING(    0x08, "1.200k 3.500k" )
	PORT_DIPSETTING(    0x00, "2000k only" )
	PORT_DIPUNUSED_DIPLOC( 0x20, 0x20, "SW(B):6" )
	PORT_DIPUNUSED_DIPLOC( 0x40, 0x40, "SW(B):7" )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(B):8" )

	PORT_START("DSWC")
	PORT_DIPNAME( 0x03, 0x03, DEF_STR( Lives ) )                    PORT_DIPLOCATION("SW(C):1,2")
	PORT_DIPSETTING(    0x02, "1" )
	PORT_DIPSETTING(    0x01, "2" )
	PORT_DIPSETTING(    0x03, "3" )
	PORT_DIPSETTING(    0x00, "4" )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Free_Play ) )                PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )                            PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )              PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )              PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Allow_Continue ) )           PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x40, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                          PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )
INPUT_PORTS_END

/* Needs further checking */
static INPUT_PORTS_START( cworld2j )
	PORT_INCLUDE( cps1_quiz )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_START("DSWA")
	CPS1_COINAGE_2( "SW(A)" )
	PORT_DIPUNKNOWN_DIPLOC( 0x08, 0x08, "SW(A):4" )
	PORT_DIPUNKNOWN_DIPLOC( 0x10, 0x10, "SW(A):5" )
	PORT_DIPUNKNOWN_DIPLOC( 0x20, 0x20, "SW(A):6" )
	PORT_DIPNAME( 0x40, 0x40, "2 Coins to Start, 1 to Continue" )   PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, "Extended Test Mode" )                PORT_DIPLOCATION("SW(A):8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("DSWB")
	PORT_DIPNAME( 0x07, 0x06, DEF_STR( Difficulty ) )               PORT_DIPLOCATION("SW(B):1,2,3")
	PORT_DIPSETTING(    0x06, "0" )
	PORT_DIPSETTING(    0x05, "1" )
	PORT_DIPSETTING(    0x04, "2" )
	PORT_DIPSETTING(    0x03, "3" )
	PORT_DIPSETTING(    0x02, "4" )
	PORT_DIPNAME( 0x18, 0x18, "Extend" )                            PORT_DIPLOCATION("SW(B):4,5")
	PORT_DIPSETTING(    0x18, "N" )
	PORT_DIPSETTING(    0x10, "E" )
	PORT_DIPSETTING(    0x00, "D" )
	PORT_DIPNAME( 0xe0, 0xe0, DEF_STR( Lives ) )                    PORT_DIPLOCATION("SW(B):6,7,8")
	PORT_DIPSETTING(    0x00, "1" )
	PORT_DIPSETTING(    0x80, "2" )
	PORT_DIPSETTING(    0xe0, "3" )
	PORT_DIPSETTING(    0xa0, "4" )
	PORT_DIPSETTING(    0xc0, "5" )

	PORT_START("DSWC")
	PORT_DIPUNKNOWN_DIPLOC( 0x01, 0x01, "SW(C):1" )
	PORT_DIPUNKNOWN_DIPLOC( 0x02, 0x02, "SW(C):2" )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Free_Play ) )                PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )                            PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )              PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )              PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Allow_Continue ) )           PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                          PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )

	PORT_START("IN2")  /* check code at 0x000614, 0x0008ac and 0x000e36 */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

/* Needs further checking */
INPUT_PORTS_START( wof )
	PORT_INCLUDE( cps1_3players )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_START("DSWA")      /* (not used, EEPROM) */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("DSWB")      /* (not used, EEPROM) */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("DSWC")
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_BIT( 0xf7, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("IN3")      /* Player 4 - not used */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START( "EEPROMIN" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::do_read))

	PORT_START( "EEPROMOUT" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::di_write))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::clk_write))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::cs_write))
INPUT_PORTS_END

INPUT_PORTS_START( dino )
	PORT_INCLUDE( cps1_3players )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_START("DSWA")      /* (not used, EEPROM) */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("DSWB")      /* (not used, EEPROM) */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("DSWC")
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_BIT( 0xf7, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("IN3")      /* Player 4 - not used */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START( "EEPROMIN" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::do_read))

	PORT_START( "EEPROMOUT" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::di_write))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::clk_write))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::cs_write))
INPUT_PORTS_END


static INPUT_PORTS_START( dinoh )
	PORT_INCLUDE( dino )

	PORT_MODIFY("DSWA")
	CPS1_COINAGE_2( "SW(A)" )
	PORT_DIPNAME( 0x08, 0x08, "Coin Slots" )                PORT_DIPLOCATION("SW(B):4")
	PORT_DIPSETTING(    0x00, "1" )
	PORT_DIPSETTING(    0x08, "3" )                 // This setting can't be used in two-player mode
	PORT_DIPNAME( 0x10, 0x10, "Play Mode" )                 PORT_DIPLOCATION("SW(B):5")
	PORT_DIPSETTING(    0x00, "2 Players" )
	PORT_DIPSETTING(    0x10, "3 Players" )
	PORT_DIPUNUSED_DIPLOC( 0x20, 0x20, "SW(A):6" )          // This switch is not documented in the manual
	PORT_DIPNAME( 0x40, 0x40, "2 Coins to Start, 1 to Continue" )       PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(A):8" )          // This switch is not documented in the manual

	PORT_MODIFY("DSWB")
	PORT_DIPNAME( 0x07, 0x04, "Difficulty Level 1" )            PORT_DIPLOCATION("SW(B):1,2,3")
	PORT_DIPSETTING(    0x07, DEF_STR( Easiest ) )          // "01"
	PORT_DIPSETTING(    0x06, DEF_STR( Easier ) )           // "02"
	PORT_DIPSETTING(    0x05, DEF_STR( Easy ) )         // "03"
	PORT_DIPSETTING(    0x04, DEF_STR( Normal ) )           // "04"
	PORT_DIPSETTING(    0x03, DEF_STR( Medium ) )           // "05"
	PORT_DIPSETTING(    0x02, DEF_STR( Hard ) )         // "06"
	PORT_DIPSETTING(    0x01, DEF_STR( Harder ) )           // "07"
	PORT_DIPSETTING(    0x00, DEF_STR( Hardest ) )          // "08"
	PORT_DIPNAME( 0x18, 0x10, "Difficulty Level 2" )            PORT_DIPLOCATION("SW(B):4,5")
	PORT_DIPSETTING(    0x18, DEF_STR( Easy ) )         // "01"
	PORT_DIPSETTING(    0x10, DEF_STR( Normal ) )           // "02"
	PORT_DIPSETTING(    0x08, DEF_STR( Hard ) )         // "03"
	PORT_DIPSETTING(    0x00, DEF_STR( Hardest ) )          // "04"
	PORT_DIPNAME( 0x60, 0x40, DEF_STR( Bonus_Life ) )           PORT_DIPLOCATION("SW(B):6,7")
	PORT_DIPSETTING(    0x60, "300k and 700k" )
	PORT_DIPSETTING(    0x40, "500k and 1000k" )
	PORT_DIPSETTING(    0x20, "1000k" )
	PORT_DIPSETTING(    0x00, DEF_STR( None ) )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(B):8" )

	PORT_MODIFY("DSWC")
	PORT_DIPNAME( 0x03, 0x02, DEF_STR( Lives ) )                PORT_DIPLOCATION("SW(C):1,2")
	PORT_DIPSETTING(    0x00, "4" )
	PORT_DIPSETTING(    0x01, "3" )
	PORT_DIPSETTING(    0x02, "2" )
	PORT_DIPSETTING(    0x03, "1" )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Free_Play ) )            PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )                    PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )          PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )          PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Allow_Continue ) )           PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x40, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                  PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT  ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN  ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_UP    ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1)
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT  ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN  ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_UP    ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2)
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_MODIFY("IN2")      /* Player 3 */
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(3)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(3)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(3)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(3)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(3)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(3)
//  PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(3)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_COIN3 )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START3 )
INPUT_PORTS_END

INPUT_PORTS_START( punisher )
	PORT_INCLUDE( cps1_2b )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_START("DSWA")      /* (not used, EEPROM) */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("DSWB")      /* (not used, EEPROM) */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("DSWC")
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_BIT( 0xf7, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("IN2")      /* Player 3 - not used */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN3")      /* Player 4 - not used */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START( "EEPROMIN" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::do_read))

	PORT_START( "EEPROMOUT" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::di_write))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::clk_write))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::cs_write))
INPUT_PORTS_END


static INPUT_PORTS_START( punisherbz )
	PORT_INCLUDE( punisher )

	PORT_MODIFY("DSWA")
	CPS1_COINAGE_2( "SW(A)" )
	PORT_DIPNAME( 0x08, 0x08, "2 Coins to Start, 1 to Continue" )   PORT_DIPLOCATION("SW(A):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x30, 0x20, DEF_STR( Lives ) )            PORT_DIPLOCATION("SW(A):5,6")
	PORT_DIPSETTING(    0x30, "1" )
	PORT_DIPSETTING(    0x20, "2" )
	PORT_DIPSETTING(    0x10, "3" )
	PORT_DIPSETTING(    0x00, "4" )
	PORT_DIPNAME( 0x40, 0x40, "Sound" )             PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, "Q Sound" )
	PORT_DIPSETTING(    0x00, "Monaural" )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Flip_Screen ) )      PORT_DIPLOCATION("SW(A):8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_MODIFY("DSWB")
	PORT_DIPNAME( 0x07, 0x04, DEF_STR( Difficulty ) )       PORT_DIPLOCATION("SW(B):1,2,3")
	PORT_DIPSETTING(    0x07, "Extra Easy" )
	PORT_DIPSETTING(    0x06, DEF_STR( Very_Easy) )
	PORT_DIPSETTING(    0x05, DEF_STR( Easy) )
	PORT_DIPSETTING(    0x04, DEF_STR( Normal) )
	PORT_DIPSETTING(    0x03, DEF_STR( Hard) )
	PORT_DIPSETTING(    0x02, DEF_STR( Very_Hard) )
	PORT_DIPSETTING(    0x01, "Extra Hard" )
	PORT_DIPSETTING(    0x00, DEF_STR( Hardest) )
	PORT_DIPNAME( 0x18, 0x10, "Extend" )                PORT_DIPLOCATION("SW(B):4,5")
	PORT_DIPSETTING(    0x18, "800000" )
	PORT_DIPSETTING(    0x10, "1800000" )
	PORT_DIPSETTING(    0x08, "2800000" )
	PORT_DIPSETTING(    0x00, "No Extend" )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Allow_Continue ) )       PORT_DIPLOCATION("SW(B):6")
	PORT_DIPSETTING(    0x20, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Demo_Sounds ) )      PORT_DIPLOCATION("SW(B):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNKNOWN_DIPLOC( 0x80, 0x80, "SW(B):8" )

	PORT_MODIFY("DSWC")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT  ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN  ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_UP    ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1)
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT  ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN  ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_UP    ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2)
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

/* Needs further checking */
INPUT_PORTS_START( slammast )
	PORT_INCLUDE( cps1_4players )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1)
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(3)
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2)
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(4)

	PORT_START("DSWA")      /* (not used, EEPROM) */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("DSWB")      /* (not used, EEPROM) */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("DSWC")
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_BIT( 0xf7, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START( "EEPROMIN" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::do_read))

	PORT_START( "EEPROMOUT" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::di_write))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::clk_write))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::cs_write))
INPUT_PORTS_END

/* Needs further checking */
static INPUT_PORTS_START( pnickj )
	PORT_INCLUDE( cps1_3b )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_START("DSWA")
	CPS1_COINAGE_2( "SW(A)" )
	PORT_DIPNAME( 0x08, 0x08, "Coin Slots" )                PORT_DIPLOCATION("SW(A):4")
	PORT_DIPSETTING(    0x08, "1" )
	PORT_DIPSETTING(    0x00, "2" )
	PORT_DIPUNKNOWN_DIPLOC( 0x10, 0x10, "SW(A):5" )
	PORT_DIPUNKNOWN_DIPLOC( 0x20, 0x20, "SW(A):6" )
	PORT_DIPUNKNOWN_DIPLOC( 0x40, 0x40, "SW(A):7" )
	PORT_DIPUNKNOWN_DIPLOC( 0x80, 0x80, "SW(A):8" )

	PORT_START("DSWB")
	CPS1_DIFFICULTY_1( "SW(B)" )
	PORT_DIPUNKNOWN_DIPLOC( 0x08, 0x08, "SW(B):4" )
	PORT_DIPUNKNOWN_DIPLOC( 0x10, 0x10, "SW(B):5" )
	PORT_DIPUNKNOWN_DIPLOC( 0x20, 0x20, "SW(B):6" )
	PORT_DIPNAME( 0xc0, 0xc0, "Vs Play Mode" )              PORT_DIPLOCATION("SW(B):7,8")
	PORT_DIPSETTING(    0xc0, "1 Game Match" )
	PORT_DIPSETTING(    0x80, "3 Games Match" )
	PORT_DIPSETTING(    0x40, "5 Games Match" )
	PORT_DIPSETTING(    0x00, "7 Games Match" )

	PORT_START("DSWC")
	PORT_DIPUNKNOWN_DIPLOC( 0x01, 0x01, "SW(C):1" )
	PORT_DIPUNKNOWN_DIPLOC( 0x02, 0x02, "SW(C):2" )
	PORT_DIPUNKNOWN_DIPLOC( 0x04, 0x04, "SW(C):3" )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )                    PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )      PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )      PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNKNOWN_DIPLOC( 0x40, 0x40, "SW(C):7" )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                  PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )
INPUT_PORTS_END

/* Needs further checking */
static INPUT_PORTS_START( qad )
	PORT_INCLUDE( cps1_quiz )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_START("DSWA")
	CPS1_COINAGE_2( "SW(A)" )
	PORT_DIPUNKNOWN_DIPLOC( 0x08, 0x08, "SW(A):4" )                 // Manual says these are for coin 2, but they
	PORT_DIPUNKNOWN_DIPLOC( 0x10, 0x10, "SW(A):5" )                 // coin to setting, but they clearly don't do
	PORT_DIPUNKNOWN_DIPLOC( 0x20, 0x20, "SW(A):6" )                 // that.
	PORT_DIPNAME( 0x40, 0x40, "2 Coins to Start, 1 to Continue" )   PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW(A):8" )

	PORT_START("DSWB")
	PORT_DIPNAME( 0x07, 0x04, DEF_STR( Difficulty ) )               PORT_DIPLOCATION("SW(B):1,2,3")
//  PORT_DIPSETTING(    0x07, DEF_STR( Easiest ) )                  // Controls overall difficulty
	PORT_DIPSETTING(    0x06, DEF_STR( Easiest ) )                  // Manual documents duplicate settings
	PORT_DIPSETTING(    0x05, DEF_STR( Easy ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x03, DEF_STR( Hard ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Hardest ) )
//  PORT_DIPSETTING(    0x01, DEF_STR( Hardest ) )
//  PORT_DIPSETTING(    0x00, DEF_STR( Hardest ) )
	PORT_DIPNAME( 0x18, 0x10, "Wisdom (questions to win game)" )    PORT_DIPLOCATION("SW(B):4,5")
	PORT_DIPSETTING(    0x18, DEF_STR( Easy ) )                     // Controls number of needed questions to finish
	PORT_DIPSETTING(    0x10, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Hard ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Hardest ) )
	PORT_DIPNAME( 0xe0, 0xe0, DEF_STR( Lives ) )                    PORT_DIPLOCATION("SW(B):6,7,8")
	PORT_DIPSETTING(    0x60, "1" )
	PORT_DIPSETTING(    0x80, "2" )
	PORT_DIPSETTING(    0xa0, "3" )
	PORT_DIPSETTING(    0xc0, "4" )
	PORT_DIPSETTING(    0xe0, "5" )
//  PORT_DIPSETTING(    0x40, "1" )                                 // These three settings are not documented
//  PORT_DIPSETTING(    0x20, "1" )
//  PORT_DIPSETTING(    0x00, "1" )

	PORT_START("DSWC")
	PORT_DIPUNUSED_DIPLOC( 0x01, 0x01, "SW(C):1" )
	PORT_DIPUNUSED_DIPLOC( 0x02, 0x02, "SW(C):2" )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Free_Play ) )                PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )                            PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )              PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Demo_Sounds ) )              PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Allow_Continue ) )           PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                          PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )

	PORT_START("IN2")  /* check code at 0x01d2d2 */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

/* Needs further checking */
static INPUT_PORTS_START( qadjr )
	PORT_INCLUDE( qad )

	PORT_MODIFY("DSWB")
	PORT_DIPNAME( 0x07, 0x05, DEF_STR( Difficulty ) )               PORT_DIPLOCATION("SW(B):1,2,3")
	PORT_DIPSETTING(    0x07, "0" )
	PORT_DIPSETTING(    0x06, "1" )
	PORT_DIPSETTING(    0x05, "2" )
	PORT_DIPSETTING(    0x04, "3" )
	PORT_DIPSETTING(    0x03, "4" )
//  PORT_DIPSETTING(    0x02, "4" )
//  PORT_DIPSETTING(    0x01, "4" )
//  PORT_DIPSETTING(    0x00, "4" )
	PORT_DIPUNKNOWN_DIPLOC( 0x08, 0x08, "SW(B):4" )                 // Unused according to manual
	PORT_DIPUNKNOWN_DIPLOC( 0x10, 0x10, "SW(B):5" )                 // Unused according to manual
	PORT_DIPNAME( 0xe0, 0xe0, DEF_STR( Lives ) )                    PORT_DIPLOCATION("SW(B):6,7,8")
	PORT_DIPSETTING(    0xa0, "1" )
	PORT_DIPSETTING(    0xc0, "2" )
	PORT_DIPSETTING(    0xe0, "3" )
//  PORT_DIPSETTING(    0x00, "1" )
//  PORT_DIPSETTING(    0x20, "1" )
//  PORT_DIPSETTING(    0x80, "1" )
//  PORT_DIPSETTING(    0x40, "2" )
//  PORT_DIPSETTING(    0x60, "3" )

	PORT_MODIFY("DSWC")
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )              PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )                      // Manual states default is OFF
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_MODIFY("IN2")  /* check code at 0x000c48 */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("IN3")  /* check code at 0x000c3e */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

/* Needs further checking */
static INPUT_PORTS_START( qtono2j )
	PORT_INCLUDE( cps1_quiz )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_START("DSWA")
	CPS1_COINAGE_2( "SW(A)" )
	PORT_DIPUNKNOWN_DIPLOC( 0x08, 0x08, "SW(A):4" )
	PORT_DIPUNKNOWN_DIPLOC( 0x10, 0x10, "SW(A):5" )
	PORT_DIPUNKNOWN_DIPLOC( 0x20, 0x20, "SW(A):6" )
	PORT_DIPNAME( 0x40, 0x40, "2 Coins to Start, 1 to Continue" )   PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNKNOWN_DIPLOC( 0x80, 0x80, "SW(A):8" )

	PORT_START("DSWB")
	CPS1_DIFFICULTY_1( "SW(B)" )
	PORT_DIPUNKNOWN_DIPLOC( 0x08, 0x08, "SW(B):4" )
	PORT_DIPUNKNOWN_DIPLOC( 0x10, 0x10, "SW(B):5" )
	PORT_DIPNAME( 0xe0, 0xe0, DEF_STR( Lives ) )                    PORT_DIPLOCATION("SW(B):6,7,8")
	PORT_DIPSETTING(    0x60, "1" )
	PORT_DIPSETTING(    0x80, "2" )
	PORT_DIPSETTING(    0xe0, "3" )
	PORT_DIPSETTING(    0xa0, "4" )
	PORT_DIPSETTING(    0xc0, "5" )
//  PORT_DIPSETTING(    0x40, "?" )
//  PORT_DIPSETTING(    0x20, "?" )
//  PORT_DIPSETTING(    0x00, "?" )

	PORT_START("DSWC")
	PORT_DIPUNKNOWN_DIPLOC( 0x01, 0x01, "SW(C):1" )
	PORT_DIPNAME( 0x02, 0x02, "Infinite Lives (Cheat)")             PORT_DIPLOCATION("SW(C):2")
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Free_Play ) )                PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )                            PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )              PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) )              PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Allow_Continue ) )           PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                          PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )

	PORT_START("IN2")  /* check code at 0x000f80 */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("IN3")  /* check code at 0x000f76 */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

/* Needs further checking */
static INPUT_PORTS_START( pang3 )
	// Though service mode shows diagonal inputs, the flyer and manual both specify 4-way joysticks
	PORT_INCLUDE( cps1_2b_4way )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	// As manual states, "Push 2 is not used," and is not even shown in service mode
	PORT_MODIFY("IN1")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("P1 Shot")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 Shot")
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("DSWA")      /* (not used, EEPROM) */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("DSWB")      /* (not used, EEPROM) */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("DSWC")
	PORT_DIPUNUSED( 0x01, 0x01 )
	PORT_DIPUNUSED( 0x02, 0x02 )
	PORT_DIPUNUSED( 0x04, 0x04 )
	PORT_DIPUNUSED( 0x08, 0x08 )
	PORT_DIPUNUSED( 0x10, 0x10 )
	PORT_DIPUNUSED( 0x20, 0x20 )
	PORT_DIPUNUSED( 0x40, 0x40 )
	PORT_DIPUNUSED( 0x80, 0x80 ) /* doubles up as an extra service switch */

	PORT_START( "EEPROMIN" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::do_read))

	PORT_START( "EEPROMOUT" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::di_write))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::clk_write))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::cs_write))
INPUT_PORTS_END

/* Needs further checking */
static INPUT_PORTS_START( pang3b )
	PORT_INCLUDE( pang3 )

	PORT_MODIFY("DSWC")
	PORT_DIPUNUSED( 0x01, 0x01 )
	PORT_DIPUNUSED( 0x02, 0x02 )
	PORT_DIPUNUSED( 0x04, 0x04 )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNUSED( 0x10, 0x10 )
	PORT_DIPUNUSED( 0x20, 0x20 )
	PORT_DIPUNUSED( 0x40, 0x40 )
	PORT_DIPUNUSED( 0x80, 0x80 )
INPUT_PORTS_END

/* The bootleggers hacked main code (@ 0x300 and 0xe0000) to use dip switches instead of the usual pang3 93C46 serial EPROM */
static INPUT_PORTS_START( pang3b4 )
	// Though service mode shows diagonal inputs, the flyer and manual both specify 4-way joysticks
	PORT_INCLUDE( cps1_2b_4way )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	// As manual states, "Push 2 is not used," and is not even shown in service mode
	PORT_MODIFY("IN1")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("P1 Shot")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 Shot")
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("DSWA")
	CPS1_COINAGE_2( "DIP-A" )
	PORT_DIPUNUSED_DIPLOC( 0x08, 0x08, "DIP-A:4" )
	PORT_DIPUNUSED_DIPLOC( 0x10, 0x10, "DIP-A:5" )
	PORT_DIPUNUSED_DIPLOC( 0x20, 0x20, "DIP-A:6" )
	PORT_DIPUNUSED_DIPLOC( 0x40, 0x40, "DIP-A:7" )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "DIP-A:8" )

	PORT_START("DSWB")
	PORT_DIPNAME( 0x07, 0x04, "Game level" )                      PORT_DIPLOCATION("DIP-B:1,2,3")
	PORT_DIPSETTING(    0x07, "Easy 3" )
	PORT_DIPSETTING(    0x06, "Easy 2" )
	PORT_DIPSETTING(    0x05, "Easy 1" )
	PORT_DIPSETTING(    0x04, "Normal" )
	PORT_DIPSETTING(    0x03, "Hard 1" )
	PORT_DIPSETTING(    0x02, "Hard 2" )
	PORT_DIPSETTING(    0x01, "Hard 3" )
	PORT_DIPSETTING(    0x00, "Hard 4" )
	PORT_DIPNAME( 0x18, 0x18, "Player" )                          PORT_DIPLOCATION("DIP-B:4,5")
	PORT_DIPSETTING(    0x08, "1" )
	PORT_DIPSETTING(    0x10, "2" )
	PORT_DIPSETTING(    0x18, "3" )
	PORT_DIPSETTING(    0x00, "4" )
	PORT_DIPNAME( 0x60, 0x20, "Extend" )                          PORT_DIPLOCATION("DIP-B:6,7")
	PORT_DIPSETTING(    0x00, "30K, 250K, 1M, 3M, 7M" )
	PORT_DIPSETTING(    0x20, "80K, 500k, 2M, 5M, 10M" )
	PORT_DIPSETTING(    0x40, "250K, 1M, 3M, 7M, 15M" )
	PORT_DIPSETTING(    0x60, "Not extend" )
	PORT_DIPNAME( 0x80, 0x80, "Free play" )                         PORT_DIPLOCATION("DIP-B:8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("DSWC")
	PORT_DIPUNUSED_DIPLOC( 0x01, 0x01, "DIP-C:1" )
	PORT_DIPUNUSED_DIPLOC( 0x02, 0x02, "DIP-C:2" )
	PORT_DIPUNUSED_DIPLOC( 0x04, 0x04, "DIP-C:3" )
	PORT_DIPUNUSED_DIPLOC( 0x08, 0x08, "DIP-C:4" )                  // Missing freeze code @ 0x020B74
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) )              PORT_DIPLOCATION("DIP-C:5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Demo_Sounds ) )              PORT_DIPLOCATION("DIP-C:6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Allow_Continue ) )           PORT_DIPLOCATION("DIP-C:7")
	PORT_DIPSETTING(    0x40, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "DIP-C:8" )
INPUT_PORTS_END

// Note: if you have service mode stuck then start button doesn't get recognized on title screen.
static INPUT_PORTS_START( gulunpa )
	PORT_INCLUDE( cps1_2b )

	PORT_START("DSWA")
	PORT_DIPNAME( 0x07, 0x07, DEF_STR( Coinage ) )  PORT_DIPLOCATION("SW(A):1,2,3")
	PORT_DIPSETTING(    0x00, DEF_STR( 4C_1C ) )
	PORT_DIPSETTING(    0x01, DEF_STR( 3C_1C ) )
	PORT_DIPSETTING(    0x02, DEF_STR( 2C_1C ) )
	PORT_DIPSETTING(    0x07, DEF_STR( 1C_1C ) )
	PORT_DIPSETTING(    0x06, DEF_STR( 1C_2C ) )
	PORT_DIPSETTING(    0x05, DEF_STR( 1C_3C ) )
	PORT_DIPSETTING(    0x04, DEF_STR( 1C_4C ) )
	PORT_DIPSETTING(    0x03, DEF_STR( 1C_6C ) )
	PORT_DIPNAME( 0x18, 0x18, "Coin slots" ) PORT_DIPLOCATION("SW(A):4,5")
	PORT_DIPSETTING(    0x18, "1, Common" )
	PORT_DIPSETTING(    0x00, "2, Common" )
	PORT_DIPSETTING(    0x10, "2, Common (duplicate 1)" )
	PORT_DIPSETTING(    0x08, "2, Common (duplicate 2)" )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) ) PORT_DIPLOCATION("SW(A):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) ) PORT_DIPLOCATION("SW(A):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) ) PORT_DIPLOCATION("SW(A):8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("DSWB")
	PORT_DIPNAME( 0x07, 0x07, DEF_STR( Difficulty ) )  PORT_DIPLOCATION("SW(B):1,2,3")
	PORT_DIPSETTING(    0x07, "1 Easiest" )
	PORT_DIPSETTING(    0x06, "2 Very Easy" )
	PORT_DIPSETTING(    0x05, "3 Easy" )
	PORT_DIPSETTING(    0x04, "4 Medium" )
	PORT_DIPSETTING(    0x03, "5 Medium Hard" )
	PORT_DIPSETTING(    0x02, "6 Hard" )
	PORT_DIPSETTING(    0x01, "7 Very Hard" )
	PORT_DIPSETTING(    0x00, "8 Hardest" )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) ) PORT_DIPLOCATION("SW(B):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) ) PORT_DIPLOCATION("SW(B):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	// flipping this on any screen causes scroll1 layer left edge to be filled with tile 0x0014
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) ) PORT_DIPLOCATION("SW(B):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) ) PORT_DIPLOCATION("SW(B):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) ) PORT_DIPLOCATION("SW(B):8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("DSWC")
	PORT_DIPNAME( 0x01, 0x01, DEF_STR( Unknown ) ) PORT_DIPLOCATION("SW(C):1")
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) ) PORT_DIPLOCATION("SW(C):2")
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) ) PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" ) PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) ) PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Demo_Sounds ) ) PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) ) PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")  PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )
INPUT_PORTS_END


/* Needs further checking */
static INPUT_PORTS_START( megaman )
	PORT_INCLUDE( cps1_3b )

	PORT_MODIFY("IN0")
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )

	PORT_START("DSWA")
	PORT_DIPNAME( 0x1f, 0x1f, DEF_STR( Coinage ) )                  PORT_DIPLOCATION("SW(A):1,2,3,4,5")
	PORT_DIPSETTING(    0x0f, DEF_STR( 9C_1C ) )
	PORT_DIPSETTING(    0x10, DEF_STR( 8C_1C ) )
	PORT_DIPSETTING(    0x11, DEF_STR( 7C_1C ) )
	PORT_DIPSETTING(    0x12, DEF_STR( 6C_1C ) )
	PORT_DIPSETTING(    0x13, DEF_STR( 5C_1C ) )
	PORT_DIPSETTING(    0x14, DEF_STR( 4C_1C ) )
	PORT_DIPSETTING(    0x15, DEF_STR( 3C_1C ) )
	PORT_DIPSETTING(    0x16, DEF_STR( 2C_1C ) )
	PORT_DIPSETTING(    0x0e, "2 Coins to Start, 1 to Continue" )
	PORT_DIPSETTING(    0x1f, DEF_STR( 1C_1C ) )
	PORT_DIPSETTING(    0x1e, DEF_STR( 1C_2C ) )
	PORT_DIPSETTING(    0x1d, DEF_STR( 1C_3C ) )
	PORT_DIPSETTING(    0x1c, DEF_STR( 1C_4C ) )
	PORT_DIPSETTING(    0x1b, DEF_STR( 1C_5C ) )
	PORT_DIPSETTING(    0x1a, DEF_STR( 1C_6C ) )
	PORT_DIPSETTING(    0x19, DEF_STR( 1C_7C ) )
	PORT_DIPSETTING(    0x18, DEF_STR( 1C_8C ) )
	PORT_DIPSETTING(    0x17, DEF_STR( 1C_9C ) )
	PORT_DIPSETTING(    0x0d, DEF_STR( Free_Play ) )
	/* 0x00 to 0x0c 1 Coin/1 Credit */
	PORT_DIPNAME( 0x60, 0x60, "Coin slots" )                        PORT_DIPLOCATION("SW(A):6,7")
//  PORT_DIPSETTING(    0x00, "Invalid" )
	PORT_DIPSETTING(    0x20, "1, Common" )
	PORT_DIPSETTING(    0x60, "2, Common" )
	PORT_DIPSETTING(    0x40, "2, Individual" )
	PORT_DIPUNKNOWN_DIPLOC( 0x80, 0x80, "SW(A):8" )

	PORT_START("DSWB")
	PORT_DIPNAME( 0x03, 0x02, DEF_STR( Difficulty ) )               PORT_DIPLOCATION("SW(B):1,2")
	PORT_DIPSETTING(    0x03, DEF_STR( Easy ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x01, DEF_STR( Hard ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Hardest ) )
	PORT_DIPNAME( 0x0c, 0x0c, "Time" )                              PORT_DIPLOCATION("SW(B):3,4")
	PORT_DIPSETTING(    0x0c, "100" )
	PORT_DIPSETTING(    0x08, "90" )
	PORT_DIPSETTING(    0x04, "70" )
	PORT_DIPSETTING(    0x00, "60" )
	PORT_DIPUNKNOWN_DIPLOC( 0x10, 0x10, "SW(B):5" )
	PORT_DIPUNKNOWN_DIPLOC( 0x20, 0x20, "SW(B):6" )
	PORT_DIPNAME( 0x40, 0x40, "Voice" )                             PORT_DIPLOCATION("SW(B):7")
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPUNKNOWN_DIPLOC( 0x80, 0x80, "SW(B):8" )

	PORT_START("DSWC")
	PORT_DIPNAME( 0x01, 0x01, DEF_STR( Flip_Screen ) )              PORT_DIPLOCATION("SW(C):1")
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Demo_Sounds ) )              PORT_DIPLOCATION("SW(C):2")
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Allow_Continue ) )           PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Yes ) )
	PORT_DIPUNKNOWN_DIPLOC( 0x08, 0x08, "SW(C):4" )
	PORT_DIPUNKNOWN_DIPLOC( 0x10, 0x10, "SW(C):5" )
	PORT_DIPUNKNOWN_DIPLOC( 0x20, 0x20, "SW(C):6" )
	PORT_DIPUNKNOWN_DIPLOC( 0x40, 0x40, "SW(C):7" )
	PORT_DIPNAME( 0x80, 0x80, "Game Mode")                          PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, "Game" )
	PORT_DIPSETTING(    0x00, DEF_STR( Test ) )
INPUT_PORTS_END

/* Needs further checking */
/* Same as 'megaman' but no "Voice" Dip Switch */
static INPUT_PORTS_START( rockmanj )
	PORT_INCLUDE(megaman)

	PORT_MODIFY("DSWB")
	PORT_DIPUNKNOWN_DIPLOC( 0x40, 0x40, "SW(B):7" )
INPUT_PORTS_END

static INPUT_PORTS_START( wofhfh )
	PORT_INCLUDE( wof )

	PORT_MODIFY("DSWA")
	PORT_DIPNAME( 0x03, 0x03, DEF_STR( Coin_A ) )           PORT_DIPLOCATION("SW(A):1,2")
	PORT_DIPSETTING(    0x03, DEF_STR( 1C_1C ) )
	PORT_DIPSETTING(    0x02, DEF_STR( 1C_2C ) )
	PORT_DIPSETTING(    0x01, DEF_STR( 1C_3C ) )
	PORT_DIPSETTING(    0x00, DEF_STR( 1C_4C ) )
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_MODIFY("DSWB")
	PORT_DIPNAME( 0x07, 0x04, DEF_STR( Difficulty ) )       PORT_DIPLOCATION("SW(B):1,2,3")
	PORT_DIPSETTING(    0x07, "Extra Easy" )
	PORT_DIPSETTING(    0x06, DEF_STR( Very_Easy) )
	PORT_DIPSETTING(    0x05, DEF_STR( Easy) )
	PORT_DIPSETTING(    0x04, DEF_STR( Normal) )
	PORT_DIPSETTING(    0x03, DEF_STR( Hard) )
	PORT_DIPSETTING(    0x02, DEF_STR( Very_Hard) )
	PORT_DIPSETTING(    0x01, "Extra Hard" )
	PORT_DIPSETTING(    0x00, DEF_STR( Hardest) )
	PORT_DIPNAME( 0x70, 0x60, DEF_STR( Lives ) )            PORT_DIPLOCATION("SW(B):4,5,6")
	PORT_DIPSETTING(    0x00, "Start 4 Continue 5" )
	PORT_DIPSETTING(    0x10, "Start 3 Continue 4" )
	PORT_DIPSETTING(    0x20, "Start 2 Continue 3" )
	PORT_DIPSETTING(    0x30, "Start 1 Continue 2" )
	PORT_DIPSETTING(    0x40, "Start 4 Continue 4" )
	PORT_DIPSETTING(    0x50, "Start 3 Continue 3" )
	PORT_DIPSETTING(    0x60, "Start 2 Continue 2" )
	PORT_DIPSETTING(    0x70, "Start 1 Continue 1" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_MODIFY("DSWC")
	PORT_DIPNAME( 0x03, 0x03, "Coin Slots" )            PORT_DIPLOCATION("SW(C):1,2")
//  PORT_DIPSETTING(    0x00, "2 Players 1 Shooter" )
	PORT_DIPSETTING(    0x01, "2 Players 1 Shooter" )
	PORT_DIPSETTING(    0x02, "3 Players 1 Shooter" )
	PORT_DIPSETTING(    0x03, "3 Players 3 Shooters" )
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1)
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2)

	PORT_MODIFY("IN2")      /* Player 3 */
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_COIN3 ) PORT_NAME("Coin 3 (P3 Button 3 in-game)")
INPUT_PORTS_END

static INPUT_PORTS_START( pmonster )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_COIN2 ) // medal switch in test mode
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_COIN1 )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_SERVICE1 )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON2 ) // hopper switch in test mode
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("IN1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON1 ) // select switch in test mode
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("DSWA")
	PORT_DIPNAME( 0x01, 0x01, "Medal Setup" )                   PORT_DIPLOCATION("SW(A):1")
	PORT_DIPSETTING(    0x01, "1 Medal 1 Credit" )
	PORT_DIPSETTING(    0x00, "Don't use" )
	PORT_DIPNAME( 0x02, 0x02, "Coin Setup" )                    PORT_DIPLOCATION("SW(A):2")
	PORT_DIPSETTING(    0x02, "100 Yen" )
	PORT_DIPSETTING(    0x00, "10 Yen" )
	PORT_DIPNAME( 0x1c, 0x1c, "Change Setup" )                  PORT_DIPLOCATION("SW(A):3,4,5")
	PORT_DIPSETTING(    0x04, "12" )
	PORT_DIPSETTING(    0x00, "11" )
	PORT_DIPSETTING(    0x1c, "10" )
	PORT_DIPSETTING(    0x18, "8" )
	PORT_DIPSETTING(    0x14, "7" )
	PORT_DIPSETTING(    0x10, "6" )
	PORT_DIPSETTING(    0x0c, "5" )
	PORT_DIPSETTING(    0x08, "No change" )
	PORT_DIPNAME( 0x60, 0x60, "10 Yen Setup" )                  PORT_DIPLOCATION("SW(A):6,7")
	PORT_DIPSETTING(    0x60, DEF_STR( 1C_1C ) )
	PORT_DIPSETTING(    0x40, DEF_STR( 2C_1C ) )
	PORT_DIPSETTING(    0x20, DEF_STR( 3C_1C ) )
	PORT_DIPSETTING(    0x00, "Don't use" )
	PORT_DIPUNKNOWN_DIPLOC( 0x80, 0x80, "SW(A):8" )

	PORT_START("DSWB")
	PORT_DIPNAME( 0x07, 0x07, "Payout Rate Setup" )             PORT_DIPLOCATION("SW(B):1,2,3")
	PORT_DIPSETTING(    0x01, "90%" )
	PORT_DIPSETTING(    0x00, "85%" )
	PORT_DIPSETTING(    0x07, "80%" )
	PORT_DIPSETTING(    0x06, "75%" )
	PORT_DIPSETTING(    0x05, "70%" )
	PORT_DIPSETTING(    0x04, "65%" )
	PORT_DIPSETTING(    0x03, "60%" )
	PORT_DIPSETTING(    0x02, "55%" )
	PORT_DIPUNKNOWN_DIPLOC( 0x08, 0x08, "SW(B):4" )
	PORT_DIPUNKNOWN_DIPLOC( 0x10, 0x10, "SW(B):5" )
	PORT_DIPUNKNOWN_DIPLOC( 0x20, 0x20, "SW(B):6" )
	PORT_DIPUNKNOWN_DIPLOC( 0x40, 0x40, "SW(B):7" )
	PORT_DIPUNKNOWN_DIPLOC( 0x80, 0x80, "SW(B):8" )

	PORT_START("DSWC")
	PORT_DIPNAME( 0x03, 0x03, DEF_STR( Demo_Sounds ) )          PORT_DIPLOCATION("SW(C):1,2")
	PORT_DIPSETTING(    0x03, DEF_STR( On ) )
	PORT_DIPSETTING(    0x02, "Every second sound" )
	PORT_DIPSETTING(    0x01, "Every third sound" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPUNKNOWN_DIPLOC( 0x04, 0x04, "SW(C):3" )
	PORT_DIPUNKNOWN_DIPLOC( 0x08, 0x08, "SW(C):4" )
	PORT_DIPUNKNOWN_DIPLOC( 0x10, 0x10, "SW(C):5" )
	PORT_DIPUNKNOWN_DIPLOC( 0x20, 0x20, "SW(C):6" )
	PORT_DIPNAME( 0x40, 0x40, "Clear RAM" )                     PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x40, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Tes Mode Display" )              PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( ganbare )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_COIN1 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_COIN2 ) /* definitely read here in test mode, coin lock prevents it though */
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_SERVICE1 )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_START2 )
	PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("IN1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("DSWA")
	PORT_DIPNAME( 0x01, 0x01, "Medal Setup" )                   PORT_DIPLOCATION("SW(A):1")
	PORT_DIPSETTING(    0x01, "1 Medal 1 Credit" )
	PORT_DIPSETTING(    0x00, "Don't use" )
	PORT_DIPNAME( 0x02, 0x02, "Coin Setup" )                    PORT_DIPLOCATION("SW(A):2")
	PORT_DIPSETTING(    0x02, "100 Yen" )
	PORT_DIPSETTING(    0x00, "10 Yen" )
	PORT_DIPNAME( 0x1c, 0x1c, "Change Setup" )                  PORT_DIPLOCATION("SW(A):3,4,5")
	PORT_DIPSETTING(    0x04, "12" )
	PORT_DIPSETTING(    0x00, "11" )
	PORT_DIPSETTING(    0x1c, "10" )
	PORT_DIPSETTING(    0x18, "8" )
	PORT_DIPSETTING(    0x14, "7" )
	PORT_DIPSETTING(    0x10, "6" )
	PORT_DIPSETTING(    0x0c, "5" )
	PORT_DIPSETTING(    0x08, "No change" )
	PORT_DIPNAME( 0x60, 0x60, "10 Yen Setup" )                  PORT_DIPLOCATION("SW(A):6,7")
	PORT_DIPSETTING(    0x60, DEF_STR( 1C_1C ) )
	PORT_DIPSETTING(    0x40, DEF_STR( 2C_1C ) )
	PORT_DIPSETTING(    0x20, DEF_STR( 3C_1C ) )
	PORT_DIPSETTING(    0x00, "Don't use" )
	PORT_DIPNAME( 0x80, 0x80, "Payout Setup" )                  PORT_DIPLOCATION("SW(A):8")
	PORT_DIPSETTING(    0x80, "Credit Mode" )
	PORT_DIPSETTING(    0x00, "Payout Mode" )

	PORT_START("DSWB")
	PORT_DIPNAME( 0x07, 0x07, "Payout Rate Setup" )             PORT_DIPLOCATION("SW(B):1,2,3")
	PORT_DIPSETTING(    0x01, "90%" )
	PORT_DIPSETTING(    0x00, "85%" )
	PORT_DIPSETTING(    0x07, "80%" )
	PORT_DIPSETTING(    0x06, "75%" )
	PORT_DIPSETTING(    0x05, "70%" )
	PORT_DIPSETTING(    0x04, "65%" )
	PORT_DIPSETTING(    0x03, "60%" )
	PORT_DIPSETTING(    0x02, "55%" )
	PORT_DIPUNKNOWN_DIPLOC( 0x08, 0x08, "SW(B):4" )
	PORT_DIPUNKNOWN_DIPLOC( 0x10, 0x10, "SW(B):5" )
	PORT_DIPUNKNOWN_DIPLOC( 0x20, 0x20, "SW(B):6" )
	PORT_DIPUNKNOWN_DIPLOC( 0x40, 0x40, "SW(B):7" )
	PORT_DIPUNKNOWN_DIPLOC( 0x80, 0x80, "SW(B):8" )

	PORT_START("DSWC")
	PORT_DIPNAME( 0x03, 0x03, DEF_STR( Demo_Sounds ) )          PORT_DIPLOCATION("SW(C):1,2")
	PORT_DIPSETTING(    0x03, DEF_STR( On ) )
	PORT_DIPSETTING(    0x02, "Every second sound" )
	PORT_DIPSETTING(    0x01, "Every third sound" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPUNKNOWN_DIPLOC( 0x04, 0x04, "SW(C):3" )
	PORT_DIPUNKNOWN_DIPLOC( 0x08, 0x08, "SW(C):4" )
	PORT_DIPUNKNOWN_DIPLOC( 0x10, 0x10, "SW(C):5" )
	PORT_DIPUNKNOWN_DIPLOC( 0x20, 0x20, "SW(C):6" )
	PORT_DIPNAME( 0x40, 0x40, "Clear RAM" )                     PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x40, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x80, "Tes Mode Display" )              PORT_DIPLOCATION("SW(C):8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END


// Hold any side select + start for service mode
static INPUT_PORTS_START( sfzch )
	PORT_START("IN0")     /* IN0 */
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_SELECT ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_SELECT ) PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_START2 )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(1)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(2)

	PORT_START("DSWA")
	PORT_DIPNAME( 0xff, 0xff, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0xff, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("DSWB")
	PORT_DIPNAME( 0xff, 0xff, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0xff, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("DSWC")
	PORT_DIPNAME( 0xff, 0xff, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0xff, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN1")     /* Player 1 & 2 */
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1)
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2)
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(2)

	PORT_START("IN2")      /* Read by wofch */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN3")      /* Player 4 - not used */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

INPUT_PORTS_END

static INPUT_PORTS_START( wofch )
	PORT_INCLUDE( sfzch )

	PORT_START( "EEPROMIN" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::do_read))

	PORT_START( "EEPROMOUT" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::di_write))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::clk_write))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::cs_write))
INPUT_PORTS_END

static INPUT_PORTS_START( pokonyan )
	PORT_INCLUDE( cps1_3b )

	PORT_START("DSWA")
	PORT_DIPNAME( 0x03, 0x02, DEF_STR( Coinage ) ) PORT_DIPLOCATION("SW(A):1,2")
	PORT_DIPSETTING(    0x00, DEF_STR( 4C_1C ) )
	PORT_DIPSETTING(    0x01, DEF_STR( 3C_1C ) )
	PORT_DIPSETTING(    0x03, DEF_STR( 2C_1C ) )
	PORT_DIPSETTING(    0x02, DEF_STR( 1C_1C ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unused ) ) PORT_DIPLOCATION("SW(A):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unused ) ) PORT_DIPLOCATION("SW(A):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unused ) ) PORT_DIPLOCATION("SW(A):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unused ) ) PORT_DIPLOCATION("SW(A):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("SW(A):7") // not listed in service mode, but left/right don't seem to work otherwise? maybe tied to some cabinet sensor?
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unused ) ) PORT_DIPLOCATION("SW(A):8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("DSWB")
	PORT_DIPNAME( 0x03, 0x03, DEF_STR( Demo_Sounds ) ) PORT_DIPLOCATION("SW(B):1,2")
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, "Every 4" )
	PORT_DIPSETTING(    0x02, "Every 2" )
	PORT_DIPSETTING(    0x03, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, "Prize Mode" ) PORT_DIPLOCATION("SW(B):3")
	PORT_DIPSETTING(    0x00, "Not Used" )
	PORT_DIPSETTING(    0x04, "Used" )
	PORT_DIPNAME( 0x08, 0x08, "Credit Mode" ) PORT_DIPLOCATION("SW(B):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, "Max Stage" ) PORT_DIPLOCATION("SW(B):5")
	PORT_DIPSETTING(    0x00, "2" )
	PORT_DIPSETTING(    0x10, "3" )
	PORT_DIPNAME( 0x20, 0x20, "Card Check" ) PORT_DIPLOCATION("SW(B):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unused ) ) PORT_DIPLOCATION("SW(B):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unused ) ) PORT_DIPLOCATION("SW(B):8")
	PORT_DIPSETTING(    0x00, "1.0 sec" )
	PORT_DIPSETTING(    0x80, "1.2 sec" )

	PORT_START("DSWC")
	PORT_DIPNAME( 0x01, 0x01, DEF_STR( Unknown ) ) PORT_DIPLOCATION("SW(C):1")
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, "Body Check") PORT_DIPLOCATION("SW(C):2")
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) ) PORT_DIPLOCATION("SW(C):3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Screen Stop" ) PORT_DIPLOCATION("SW(C):4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Flip_Screen ) ) PORT_DIPLOCATION("SW(C):5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) ) PORT_DIPLOCATION("SW(C):6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) ) PORT_DIPLOCATION("SW(C):7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_SERVICE_DIPLOC( 0x80, IP_ACTIVE_LOW, "SW(C):8" )
INPUT_PORTS_END

static const ioport_value mpumpkin_handle[] = { 0, 1, 3, 2 };

static INPUT_PORTS_START( mpumpkin )
	PORT_INCLUDE( pokonyan )

	PORT_MODIFY("IN0")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x0003, 0x0000, IPT_POSITIONAL ) PORT_POSITIONS(4) PORT_REMAP_TABLE(mpumpkin_handle) PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_CENTERDELTA(0) PORT_WRAPS
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON2 ) // Kitty
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON1 ) // Keroppi
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON3 ) // Badtz-Maru
	PORT_BIT( 0xff00, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

/*
A Final Fight board with mismatched USA and Japan GFX proves that the columns
of the 8x8 tilemap alternate between sides of the 16x16 tile resulting
in a corrupt WDUD screen. ffightub is now fixed since a genuine pcb surfaced.
*/

static const gfx_layout cps1_layout8x8 =
{
	8,8,
	RGN_FRAC(1,1),
	4,
	{ 24, 16, 8, 0 },
	{ STEP8(0, 1) },
	{ STEP8(0, 4*16) },
	64*8
};

static const gfx_layout cps1_layout8x8_2 =
{
	8,8,
	RGN_FRAC(1,1),
	4,
	{ 24, 16, 8, 0 },
	{ STEP8(32, 1) },
	{ STEP8(0, 4*16) },
	64*8
};

static const gfx_layout cps1_layout16x16 =
{
	16,16,
	RGN_FRAC(1,1),
	4,
	{ 24, 16, 8, 0 },
	{ STEP8(0, 1), STEP8(32, 1) },
	{ STEP16(0, 4*16) },
	4*16*16
};

static const gfx_layout cps1_layout32x32 =
{
	32,32,
	RGN_FRAC(1,1),
	4,
	{ 24, 16, 8, 0 },
	{ STEP8(0, 1), STEP8(32, 1), STEP8(64, 1), STEP8(96, 1) },
	{ STEP32(0, 4*32) },
	4*32*32
};

GFXDECODE_START( gfx_cps1 )
	GFXDECODE_ENTRY( "gfx", 0, cps1_layout8x8,   0, 0x100 )
	GFXDECODE_ENTRY( "gfx", 0, cps1_layout8x8_2, 0, 0x100 )
	GFXDECODE_ENTRY( "gfx", 0, cps1_layout16x16, 0, 0x100 )
	GFXDECODE_ENTRY( "gfx", 0, cps1_layout32x32, 0, 0x100 )
GFXDECODE_END



/********************************************************************
*
*  Machine Driver macro
*  ====================
*
*  Abusing the pre-processor.
*
********************************************************************/

MACHINE_START_MEMBER(cps_state,common)
{
	m_led_cboard.resolve();
}

MACHINE_START_MEMBER(cps_state,cps1)
{
	MACHINE_START_CALL_MEMBER(common);
	membank("bank1")->configure_entries(0, 2, memregion("audiocpu")->base() + 0x10000, 0x4000);
}

MACHINE_START_MEMBER(cps_state,qsound)
{
	MACHINE_START_CALL_MEMBER(common);
	membank("bank1")->configure_entries(0, 6, memregion("audiocpu")->base() + 0x10000, 0x4000);
}

void cps_state::cps1_10MHz(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, XTAL(10'000'000));    /* verified on pcb */
	m_maincpu->set_interrupt_mixer(false);
	m_maincpu->set_addrmap(AS_PROGRAM, &cps_state::main_map);
	m_maincpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &cps_state::cpu_space_map);
	m_maincpu->set_vblank_int("screen", FUNC(cps_state::cps1_interrupt));

	Z80(config, m_audiocpu, XTAL(3'579'545));  /* verified on pcb */
	m_audiocpu->set_addrmap(AS_PROGRAM, &cps_state::sub_map);

	MCFG_MACHINE_START_OVERRIDE(cps_state,cps1)

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(CPS_PIXEL_CLOCK, CPS_HTOTAL, CPS_HBEND, CPS_HBSTART, CPS_VTOTAL, CPS_VBEND, CPS_VBSTART);
	m_screen->set_screen_update(FUNC(cps_state::screen_update_cps1));
	m_screen->screen_vblank().set(FUNC(cps_state::screen_vblank_cps1));
	m_screen->screen_vblank().append(FUNC(cps_state::cps1_objram_latch));
	m_screen->set_palette(m_palette);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_cps1);
	PALETTE(config, m_palette, palette_device::BLACK).set_entries(0xc00);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	GENERIC_LATCH_8(config, m_soundlatch);
	GENERIC_LATCH_8(config, m_soundlatch2);

	ym2151_device &ym2151(YM2151(config, "2151", XTAL(3'579'545)));  /* verified on pcb */
	ym2151.irq_handler().set_inputline(m_audiocpu, 0);
	ym2151.add_route(0, "mono", 0.35);
	ym2151.add_route(1, "mono", 0.35);

	/* CPS PPU is fed by a 16mhz clock,pin 117 outputs a 4mhz clock which is divided by 4 using 2 74ls74 */
	OKIM6295(config, m_oki, XTAL(16'000'000)/4/4, okim6295_device::PIN7_HIGH).add_route(ALL_OUTPUTS, "mono", 0.30); // pin 7 can be changed by the game code, see f006 on z80
}

void cps_state::forgottn(machine_config &config)
{
	cps1_10MHz(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &cps_state::forgottn_map);

	upd4701_device &upd4701(UPD4701A(config, "upd4701"));
	upd4701.set_portx_tag("DIAL0");
	upd4701.set_porty_tag("DIAL1");
}

void cps_state::cps1_12MHz(machine_config &config)
{
	cps1_10MHz(config);

	/* basic machine hardware */
	m_maincpu->set_clock(XTAL(12'000'000));    /* verified on pcb */
}

void cps_state::pang3(machine_config &config)
{
	cps1_12MHz(config);

	/* basic machine hardware */
	EEPROM_93C46_16BIT(config, "eeprom");
}

void cps_state::ganbare(machine_config &config)
{
	cps1_10MHz(config);

	/* basic machine hardware */
	m_maincpu->remove_vblank_int();
	TIMER(config, "scantimer").configure_scanline(FUNC(cps_state::raster_scanline), "screen", 0, 1);

	M48T35(config, m_m48t35, 0);
}

void cps_state::qsound(machine_config &config)
{
	cps1_12MHz(config);

	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &cps_state::qsound_main_map);

	Z80(config.replace(), m_audiocpu, XTAL(8'000'000));  /* verified on pcb */
	m_audiocpu->set_addrmap(AS_PROGRAM, &cps_state::qsound_sub_map);
	m_audiocpu->set_addrmap(AS_OPCODES, &cps_state::qsound_decrypted_opcodes_map);
	m_audiocpu->set_periodic_int(FUNC(cps_state::irq0_line_hold), attotime::from_hz(250)); // measured (cps2.cpp)

	MCFG_MACHINE_START_OVERRIDE(cps_state, qsound)

	EEPROM_93C46_8BIT(config, "eeprom");

	/* sound hardware */
	config.device_remove("mono");
	SPEAKER(config, "speaker", 2).front();

	config.device_remove("soundlatch");
	config.device_remove("soundlatch2");
	config.device_remove("2151");
	config.device_remove("oki");

	qsound_device &qsound(QSOUND(config, "qsound"));
	qsound.add_route(0, "speaker", 1.0, 0);
	qsound.add_route(1, "speaker", 1.0, 1);
}

void cps_state::wofhfh(machine_config &config)
{
	cps1_12MHz(config);

	/* basic machine hardware */
	EEPROM_93C46_8BIT(config, "eeprom");
}

void cps_state::sf2m3(machine_config &config)
{
	cps1_12MHz(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &cps_state::sf2m3_map);
}

void cps_state::sf2cems6(machine_config &config)
{
	cps1_10MHz(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &cps_state::sf2cems6_map);
}

void cps_state::sf2m10(machine_config &config)
{
	cps1_12MHz(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &cps_state::sf2m10_map);
}

void cps_state::varthb2(machine_config &config)
{
	cps1_12MHz(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &cps_state::varthb2_map);
}

void cps_state::varthb3(machine_config &config)
{
	cps1_12MHz(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &cps_state::varthb3_map);
}


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

  Game driver(s)

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

#define CODE_SIZE 0x400000

/* B-Board 88621B-2 */
/*
   There are 4 surface mounted ROMs each on it's own small 88621B-sub satellite board, type HN62404FP-18 package is QFP44.
   The ROMs on the satellite boards are named and located as follows:

   LW-02 @ 6B
   LW-08 @ 9B
   LW-06 @ 9D
   LW-07 @ 10G

   OTHER:
   2 PALs labeled LW621 (near LW_1.2A) and LWI0 (near LW_00.13C)
   Custom chip -   CAPCOM CPS-B-01 (QFP160)
   NEC D4701AC
*/
/* Note that ROMs are labeled left to right, top to bottom, instead of top to bottom, left to right as usual. */
ROM_START( forgottn )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )
	ROM_LOAD16_BYTE( "lw40.12f",       0x00000, 0x20000, CRC(73e920b7) SHA1(2df12fc1a66f488d06b0927db909da81466d7d07) ) /* Higher program numbers indicates a later revision */
	ROM_LOAD16_BYTE( "lw41.12h",       0x00001, 0x20000, CRC(58210b9e) SHA1(416cb56a81e74fce6f86c2b2519ba620457b785a) ) /* 1 byte difference: 0x66D4 == 0x0C versus 0x04 in lw15.12h below */
	ROM_LOAD16_BYTE( "lw42.13f",       0x40000, 0x20000, CRC(bea45994) SHA1(c419f65c5e0c11ae7508ec54412bf6b62fac4f72) )
	ROM_LOAD16_BYTE( "lw43.13h",       0x40001, 0x20000, CRC(539b2339) SHA1(8a9e452ef8ed05e0b956d36990266657d3077470) )
	ROM_LOAD16_WORD_SWAP( "lw-07.10g", 0x80000, 0x80000, CRC(fd252a26) SHA1(5cfb097984912a5167a8c7ec4c2e119b642f9970) ) // == lw-07.13e

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_BYTE( "lw_2.2b",   0x000000, 0x20000, CRC(4bd75fee) SHA1(c27bfba951a0dc4f493937ceca335c50a1afeddf) ) // == lw-01.9d
	ROM_LOAD64_BYTE( "lw_1.2a",   0x000001, 0x20000, CRC(65f41485) SHA1(fb05dffc87ee2f2b1b6646d54b13671f8eee0429) ) // == lw-01.9d
	ROM_LOAD64_WORD( "lw-08.9b",  0x000002, 0x80000, CRC(25a8e43c) SHA1(d57cee1fc508db2677e84882fb814e4d9ad20543) ) // == lw-08.9f
	ROM_LOAD64_BYTE( "lw_18.5e",  0x000004, 0x20000, CRC(b4b6241b) SHA1(92b6b530e18ce27ba8739ebba0d8096b1551026c) )
	ROM_LOAD64_BYTE( "lw_17.5c",  0x000005, 0x20000, CRC(c5eea115) SHA1(22fe692eaf9dd00a56a76f46c19fb76bb5e5f0d6) )
	ROM_LOAD64_BYTE( "lw_30.8h",  0x000006, 0x20000, CRC(b385954e) SHA1(d33adb5842e7b85d304836bd92a7a96be4ff3694) ) // == lw-12.9g
	ROM_LOAD64_BYTE( "lw_29.8f",  0x000007, 0x20000, CRC(7bda1ac6) SHA1(5b8bd05f52798f98ae16efa2ff61c06e28a4e3a0) ) // == lw-12.9g
	ROM_LOAD64_BYTE( "lw_4.3b",   0x100000, 0x20000, CRC(50cf757f) SHA1(c70d7d34ac2d6671d40dd372e241ccb60bf3bf2b) ) // == lw-01.9d
	ROM_LOAD64_BYTE( "lw_3.3a",   0x100001, 0x20000, CRC(c03ef278) SHA1(ad33b01bd8194025a2ecf7755894d6d638da457a) ) // == lw-01.9d
	ROM_LOAD64_BYTE( "lw_20.7e",  0x100004, 0x20000, CRC(df1a3665) SHA1(7ba9c0edc64d4f9a8563533ce723a0a748352a15) )
	ROM_LOAD64_BYTE( "lw_19.7c",  0x100005, 0x20000, CRC(15af8440) SHA1(5afd0e833593f1a78487af489fe4384ab68f52b1) )
	ROM_LOAD64_BYTE( "lw_32.9h",  0x100006, 0x20000, CRC(30967a15) SHA1(6f6c6ca2f40aa9beec63ed64f0571bebc7c1aa50) ) // == lw-12.9g
	ROM_LOAD64_BYTE( "lw_31.9f",  0x100007, 0x20000, CRC(c49d37fb) SHA1(ce400261a0f8d5a9b95d3823f8f52de87b8007f1) ) // == lw-12.9g
	ROM_LOAD64_WORD( "lw-02.6b",  0x200000, 0x80000, CRC(43e6c5c8) SHA1(d3e6c971de0477ec4e178adc82508208dd8b397f) ) // == lw-02.12d
	ROM_LOAD64_BYTE( "lw_14.10b", 0x200002, 0x20000, CRC(82862cce) SHA1(727ca4ee55e076185b071a49afc87533fde9ec27) ) // == lw-09.12f
	ROM_LOAD64_BYTE( "lw_13.10a", 0x200003, 0x20000, CRC(b81c0e96) SHA1(09f4235786b8ff92a57112669c0385b64477eb01) ) // == lw-09.12f
	ROM_LOAD64_WORD( "lw-06.9d",  0x200004, 0x80000, CRC(5b9edffc) SHA1(6fd8f4a3ab070733b52365ab1945bf86acb2bf62) ) // == lw-06.12e
	ROM_LOAD64_BYTE( "lw_26.10e", 0x200006, 0x20000, CRC(57bcd032) SHA1(6db0f96fb909ed02fe4b7ee25fe662ea23f884d2) ) // == lw-13.12g
	ROM_LOAD64_BYTE( "lw_25.10c", 0x200007, 0x20000, CRC(bac91554) SHA1(52f5de144193e0f78b9824cc8fd6f934dc19bab0) ) // == lw-13.12g
	ROM_LOAD64_BYTE( "lw_16.11b", 0x300002, 0x20000, CRC(40b26554) SHA1(b4b27573d6c329bc2bc4c64fd857475bf2a10877) ) // == lw-09.12f
	ROM_LOAD64_BYTE( "lw_15.11a", 0x300003, 0x20000, CRC(1b7d2e07) SHA1(0edf4d4b314fd9c29e7915d5d1adef6f9617f921) ) // == lw-09.12f
	ROM_LOAD64_BYTE( "lw_28.11e", 0x300006, 0x20000, CRC(a805ad30) SHA1(baded4ab5fe4e87d53233b5df88edc693c292fc4) ) // == lw-13.12g
	ROM_LOAD64_BYTE( "lw_27.11c", 0x300007, 0x20000, CRC(103c1bd2) SHA1(fc7ce74e108c30554139e55651c5348b11e9e3bd) ) // == lw-13.12g

	ROM_REGION( 0x8000, "stars", 0 )
	ROM_COPY( "gfx", 0x200000, 0x000000, 0x8000 )

	ROM_REGION( 0x18000, "audiocpu", 0 )
	ROM_LOAD( "lw_37.13c",  0x00000, 0x08000, CRC(59df2a63) SHA1(dfe1fffc7a17179a80a2ae623e93b30a7d6df20d) )    // == lw_00b.13c
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "lw-03u.12e", 0x00000, 0x20000, CRC(807d051f) SHA1(720e4733787b9b11f4d1cdce0892b69475802844) )    // == lw-03u.14c
	ROM_LOAD( "lw-04u.13e", 0x20000, 0x20000, CRC(e6cd098e) SHA1(667f6e5736f76a1c4c450c4e2035574ea89d7910) )    // == lw-04u.13c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "lw621.1a",     0x0000, 0x0117, CRC(5eec6ce9) SHA1(5ec8b60f1f1bdba865b1fa2387987ce99ff4093a) )
	ROM_LOAD( "lwio.12b",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 88621B-2 */
/*
   These ROMs read from a dead and very unique top board. All EPROMs are type 27C1000, except LW_00.13C which is a 27C512.
   There are 5 surface mounted ROMs each on it's own small 88621B-sub satellite board, type HN62404FP-18 package is QFP44.
   The ROMs on the satellite boards are named and located as follows:

   LW-02 @ 6B
   LW-05 @ 6D (instead of LW_17.5C, LW_18.5E, LW_19.7C & LW_20.7E as above)
   LW-08 @ 9B
   LW-06 @ 9D
   LW-07 @ 10G

   Also known to have LW-13 @ 10D instead of LW_25.10C, LW_26.10E, LW_27.11C & LW_28.11E

   OTHER:
   2 PALs labeled LW621 (near LW_1.2A) and LWI0 (near LW_00.13C)
   Custom chip -   CAPCOM CPS-B-01 (QFP160)
   NEC D4701AC
*/
/* Note that ROMs are labeled left to right, top to bottom, instead of top to bottom, left to right as usual. */
ROM_START( forgottna )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )
	ROM_LOAD16_BYTE( "lw11.12f",       0x00000, 0x20000, CRC(73e920b7) SHA1(2df12fc1a66f488d06b0927db909da81466d7d07) )
	ROM_LOAD16_BYTE( "lw15.12h",       0x00001, 0x20000, CRC(50d7012d) SHA1(f82a28a835f0a83b26c2c8170b824447b1d7409f) )
	ROM_LOAD16_BYTE( "lw10.13f",       0x40000, 0x20000, CRC(bea45994) SHA1(c419f65c5e0c11ae7508ec54412bf6b62fac4f72) )
	ROM_LOAD16_BYTE( "lw14.13h",       0x40001, 0x20000, CRC(539b2339) SHA1(8a9e452ef8ed05e0b956d36990266657d3077470) )
	ROM_LOAD16_WORD_SWAP( "lw-07.10g", 0x80000, 0x80000, CRC(fd252a26) SHA1(5cfb097984912a5167a8c7ec4c2e119b642f9970) ) // == lw-07.13e

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_BYTE( "lw_2.2b",   0x000000, 0x20000, CRC(4bd75fee) SHA1(c27bfba951a0dc4f493937ceca335c50a1afeddf) ) // == lw-01.9d
	ROM_LOAD64_BYTE( "lw_1.2a",   0x000001, 0x20000, CRC(65f41485) SHA1(fb05dffc87ee2f2b1b6646d54b13671f8eee0429) ) // == lw-01.9d
	ROM_LOAD64_WORD( "lw-08.9b",  0x000002, 0x80000, CRC(25a8e43c) SHA1(d57cee1fc508db2677e84882fb814e4d9ad20543) ) // == lw-08.9f
	ROM_LOAD64_WORD( "lw-05.6d",  0x000004, 0x80000, CRC(e4552fd7) SHA1(11147afc475904848458425661473586dd6f60cc) ) // == lw-05.9e
	ROM_LOAD64_BYTE( "lw_30.8h",  0x000006, 0x20000, CRC(b385954e) SHA1(d33adb5842e7b85d304836bd92a7a96be4ff3694) ) // == lw-12.9g
	ROM_LOAD64_BYTE( "lw_29.8f",  0x000007, 0x20000, CRC(7bda1ac6) SHA1(5b8bd05f52798f98ae16efa2ff61c06e28a4e3a0) ) // == lw-12.9g
	ROM_LOAD64_BYTE( "lw_4.3b",   0x100000, 0x20000, CRC(50cf757f) SHA1(c70d7d34ac2d6671d40dd372e241ccb60bf3bf2b) ) // == lw-01.9d
	ROM_LOAD64_BYTE( "lw_3.3a",   0x100001, 0x20000, CRC(c03ef278) SHA1(ad33b01bd8194025a2ecf7755894d6d638da457a) ) // == lw-01.9d
	ROM_LOAD64_BYTE( "lw_32.9h",  0x100006, 0x20000, CRC(30967a15) SHA1(6f6c6ca2f40aa9beec63ed64f0571bebc7c1aa50) ) // == lw-12.9g
	ROM_LOAD64_BYTE( "lw_31.9f",  0x100007, 0x20000, CRC(c49d37fb) SHA1(ce400261a0f8d5a9b95d3823f8f52de87b8007f1) ) // == lw-12.9g
	ROM_LOAD64_WORD( "lw-02.6b",  0x200000, 0x80000, CRC(43e6c5c8) SHA1(d3e6c971de0477ec4e178adc82508208dd8b397f) ) // == lw-02.12d
	ROM_LOAD64_BYTE( "lw_14.10b", 0x200002, 0x20000, CRC(82862cce) SHA1(727ca4ee55e076185b071a49afc87533fde9ec27) ) // == lw-09.12f
	ROM_LOAD64_BYTE( "lw_13.10a", 0x200003, 0x20000, CRC(b81c0e96) SHA1(09f4235786b8ff92a57112669c0385b64477eb01) ) // == lw-09.12f
	ROM_LOAD64_WORD( "lw-06.9d",  0x200004, 0x80000, CRC(5b9edffc) SHA1(6fd8f4a3ab070733b52365ab1945bf86acb2bf62) ) // == lw-06.12e
	ROM_LOAD64_BYTE( "lw_26.10e", 0x200006, 0x20000, CRC(57bcd032) SHA1(6db0f96fb909ed02fe4b7ee25fe662ea23f884d2) ) // == lw-13.12g
	ROM_LOAD64_BYTE( "lw_25.10c", 0x200007, 0x20000, CRC(bac91554) SHA1(52f5de144193e0f78b9824cc8fd6f934dc19bab0) ) // == lw-13.12g
	ROM_LOAD64_BYTE( "lw_16.11b", 0x300002, 0x20000, CRC(40b26554) SHA1(b4b27573d6c329bc2bc4c64fd857475bf2a10877) ) // == lw-09.12f
	ROM_LOAD64_BYTE( "lw_15.11a", 0x300003, 0x20000, CRC(1b7d2e07) SHA1(0edf4d4b314fd9c29e7915d5d1adef6f9617f921) ) // == lw-09.12f
	ROM_LOAD64_BYTE( "lw_28.11e", 0x300006, 0x20000, CRC(a805ad30) SHA1(baded4ab5fe4e87d53233b5df88edc693c292fc4) ) // == lw-13.12g
	ROM_LOAD64_BYTE( "lw_27.11c", 0x300007, 0x20000, CRC(103c1bd2) SHA1(fc7ce74e108c30554139e55651c5348b11e9e3bd) ) // == lw-13.12g

	ROM_REGION( 0x8000, "stars", 0 )
	ROM_COPY( "gfx", 0x200000, 0x000000, 0x8000 )

	ROM_REGION( 0x18000, "audiocpu", 0 )
	ROM_LOAD( "lw_00.13c",  0x00000, 0x08000, CRC(59df2a63) SHA1(dfe1fffc7a17179a80a2ae623e93b30a7d6df20d) )    // == lw_00b.14a
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "lw-03u.12e", 0x00000, 0x20000, CRC(807d051f) SHA1(720e4733787b9b11f4d1cdce0892b69475802844) )    // == lw-03u.14c
	ROM_LOAD( "lw-04u.13e", 0x20000, 0x20000, CRC(e6cd098e) SHA1(667f6e5736f76a1c4c450c4e2035574ea89d7910) )    // == lw-04u.13c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "lw621.1a",     0x0000, 0x0117, CRC(5eec6ce9) SHA1(5ec8b60f1f1bdba865b1fa2387987ce99ff4093a) )
	ROM_LOAD( "lwio.12b",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 88621B-2 */
/* This set comes from a USA board with the same pcb layout of the parent forgottn, but uses one more surface mounted ROM
   named LW-13 and located @ 10D instead of LW_25.10C, LW_26.10E, LW_27.11C, LW_28.11E EPROMs. */
/* Note that ROMs are labeled left to right, top to bottom, instead of top to bottom, left to right as usual. */
ROM_START( forgottnu )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )
	ROM_LOAD16_BYTE( "lw11c.12f",      0x00000, 0x20000, CRC(e62742b6) SHA1(39dd3bdd405a8217d8816567d4f2014fc77f5ce8) )
	ROM_LOAD16_BYTE( "lw15c.12h",      0x00001, 0x20000, CRC(1b70f216) SHA1(f200f615dca8aa23d166e74b8baa9a8863ee7a95) )
	ROM_LOAD16_BYTE( "lw10c.13f",      0x40000, 0x20000, CRC(8f5ea3f5) SHA1(c3e43659bd7e03ec3d5c79647db380bde391b0f1) )
	ROM_LOAD16_BYTE( "lw14c.13h",      0x40001, 0x20000, CRC(708e7472) SHA1(44f82db41ffd1eece2d3f3c977f481fd84dfdbf3) )
	ROM_LOAD16_WORD_SWAP( "lw-07.10g", 0x80000, 0x80000, CRC(fd252a26) SHA1(5cfb097984912a5167a8c7ec4c2e119b642f9970) ) // == lw-07.13e

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_BYTE( "lw_2.2b",   0x000000, 0x20000, CRC(4bd75fee) SHA1(c27bfba951a0dc4f493937ceca335c50a1afeddf) ) // == lw-01.9d
	ROM_LOAD64_BYTE( "lw_1.2a",   0x000001, 0x20000, CRC(65f41485) SHA1(fb05dffc87ee2f2b1b6646d54b13671f8eee0429) ) // == lw-01.9d
	ROM_LOAD64_WORD( "lw-08.9b",  0x000002, 0x80000, CRC(25a8e43c) SHA1(d57cee1fc508db2677e84882fb814e4d9ad20543) ) // == lw-08.9f
	ROM_LOAD64_WORD( "lw-05.6d",  0x000004, 0x80000, CRC(e4552fd7) SHA1(11147afc475904848458425661473586dd6f60cc) ) // == lw-05.9e
	ROM_LOAD64_BYTE( "lw_30.8h",  0x000006, 0x20000, CRC(b385954e) SHA1(d33adb5842e7b85d304836bd92a7a96be4ff3694) ) // == lw-12.9g
	ROM_LOAD64_BYTE( "lw_29.8f",  0x000007, 0x20000, CRC(7bda1ac6) SHA1(5b8bd05f52798f98ae16efa2ff61c06e28a4e3a0) ) // == lw-12.9g
	ROM_LOAD64_BYTE( "lw_4.3b",   0x100000, 0x20000, CRC(50cf757f) SHA1(c70d7d34ac2d6671d40dd372e241ccb60bf3bf2b) ) // == lw-01.9d
	ROM_LOAD64_BYTE( "lw_3.3a",   0x100001, 0x20000, CRC(c03ef278) SHA1(ad33b01bd8194025a2ecf7755894d6d638da457a) ) // == lw-01.9d
	ROM_LOAD64_BYTE( "lw_32.9h",  0x100006, 0x20000, CRC(30967a15) SHA1(6f6c6ca2f40aa9beec63ed64f0571bebc7c1aa50) ) // == lw-12.9g
	ROM_LOAD64_BYTE( "lw_31.9f",  0x100007, 0x20000, CRC(c49d37fb) SHA1(ce400261a0f8d5a9b95d3823f8f52de87b8007f1) ) // == lw-12.9g
	ROM_LOAD64_WORD( "lw-02.6b",  0x200000, 0x80000, CRC(43e6c5c8) SHA1(d3e6c971de0477ec4e178adc82508208dd8b397f) ) // == lw-02.12d
	ROM_LOAD64_BYTE( "lw_14.10b", 0x200002, 0x20000, CRC(82862cce) SHA1(727ca4ee55e076185b071a49afc87533fde9ec27) ) // == lw-09.12f
	ROM_LOAD64_BYTE( "lw_13.10a", 0x200003, 0x20000, CRC(b81c0e96) SHA1(09f4235786b8ff92a57112669c0385b64477eb01) ) // == lw-09.12f
	ROM_LOAD64_WORD( "lw-06.9d",  0x200004, 0x80000, CRC(5b9edffc) SHA1(6fd8f4a3ab070733b52365ab1945bf86acb2bf62) ) // == lw-06.12e
	ROM_LOAD64_WORD( "lw-13.10d", 0x200006, 0x80000, CRC(8e058ef5) SHA1(00f2c0050fd106276ea5398511c5861ebfbc0d10) ) // == lw-13.12g
	ROM_LOAD64_BYTE( "lw_16.11b", 0x300002, 0x20000, CRC(40b26554) SHA1(b4b27573d6c329bc2bc4c64fd857475bf2a10877) ) // == lw-09.12f
	ROM_LOAD64_BYTE( "lw_15.11a", 0x300003, 0x20000, CRC(1b7d2e07) SHA1(0edf4d4b314fd9c29e7915d5d1adef6f9617f921) ) // == lw-09.12f

	ROM_REGION( 0x8000, "stars", 0 )
	ROM_COPY( "gfx", 0x200000, 0x000000, 0x8000 )

	ROM_REGION( 0x18000, "audiocpu", 0 )
	ROM_LOAD( "lw_00.13c",  0x00000, 0x08000, CRC(59df2a63) SHA1(dfe1fffc7a17179a80a2ae623e93b30a7d6df20d) )    // == lw_00b.14a
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "lw-03u.12e", 0x00000, 0x20000, CRC(807d051f) SHA1(720e4733787b9b11f4d1cdce0892b69475802844) )    // == lw-03u.14c
	ROM_LOAD( "lw-04u.13e", 0x20000, 0x20000, CRC(e6cd098e) SHA1(667f6e5736f76a1c4c450c4e2035574ea89d7910) )    // == lw-04u.13c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "lw621.1a",     0x0000, 0x0117, CRC(5eec6ce9) SHA1(5ec8b60f1f1bdba865b1fa2387987ce99ff4093a) )
	ROM_LOAD( "lwio.12b",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 88618B-2 */
ROM_START( forgottnue )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )
	ROM_LOAD16_BYTE( "lw11e.14f",      0x00000, 0x20000, CRC(82656910) SHA1(2a939b2bbd1696ce59a86c760eacde044c487274) )
	ROM_LOAD16_BYTE( "lw15e.14g",      0x00001, 0x20000, CRC(fb1e2bd0) SHA1(a8aca09428fd37845d828151886a2e96fa0a14b4) )
	ROM_LOAD16_BYTE( "lw10e.13f",      0x40000, 0x20000, CRC(3ce81dbe) SHA1(fad65a60eb673543ccea09a3c716b33d45d12dbb) )
	ROM_LOAD16_BYTE( "lw14e.13g",      0x40001, 0x20000, CRC(472eaad1) SHA1(71d4b1d7048689ec8d757600389d7592aec4a888) )
	ROM_LOAD16_WORD_SWAP( "lw-07.13e", 0x80000, 0x80000, CRC(fd252a26) SHA1(5cfb097984912a5167a8c7ec4c2e119b642f9970) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "lw-01.9d",  0x000000, 0x80000, CRC(0318f298) SHA1(178ffd6da7bf845e30abf1bfc38a469cd319a73f) )
	ROM_LOAD64_WORD( "lw-08.9f",  0x000002, 0x80000, CRC(25a8e43c) SHA1(d57cee1fc508db2677e84882fb814e4d9ad20543) )
	ROM_LOAD64_WORD( "lw-05.9e",  0x000004, 0x80000, CRC(e4552fd7) SHA1(11147afc475904848458425661473586dd6f60cc) )
	ROM_LOAD64_WORD( "lw-12.9g",  0x000006, 0x80000, CRC(8e6a832b) SHA1(d63a1331fda2365f090fa31950098f321a720ea8) )
	ROM_LOAD64_WORD( "lw-02.12d", 0x200000, 0x80000, CRC(43e6c5c8) SHA1(d3e6c971de0477ec4e178adc82508208dd8b397f) )
	ROM_LOAD64_WORD( "lw-09.12f", 0x200002, 0x80000, CRC(899cb4ad) SHA1(95e61af338945e690f2a82746feba3871ea224eb) )
	ROM_LOAD64_WORD( "lw-06.12e", 0x200004, 0x80000, CRC(5b9edffc) SHA1(6fd8f4a3ab070733b52365ab1945bf86acb2bf62) )
	ROM_LOAD64_WORD( "lw-13.12g", 0x200006, 0x80000, CRC(8e058ef5) SHA1(00f2c0050fd106276ea5398511c5861ebfbc0d10) )

	ROM_REGION( 0x8000, "stars", 0 )
	ROM_COPY( "gfx", 0x200000, 0x000000, 0x8000 )

	ROM_REGION( 0x18000, "audiocpu", 0 )
	ROM_LOAD( "lw_00.14a",  0x00000, 0x08000, CRC(59df2a63) SHA1(dfe1fffc7a17179a80a2ae623e93b30a7d6df20d) )    // == lw_00b.14a
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "lw-03u.14c", 0x00000, 0x20000, CRC(807d051f) SHA1(720e4733787b9b11f4d1cdce0892b69475802844) )
	ROM_LOAD( "lw-04u.13c", 0x20000, 0x20000, CRC(e6cd098e) SHA1(667f6e5736f76a1c4c450c4e2035574ea89d7910) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "lwchr.3a",     0x0000, 0x0117, CRC(54ed4c39) SHA1(961309335dc1c84482ebe99ea938b32d3a6ae9a8) )
	ROM_LOAD( "lwio.15e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 88618B-2 */
/* Note that this set is equivalent to forgottnu, but ROMs use the 88618B-2 B-Board layout. */
ROM_START( forgottnuc )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )
	ROM_LOAD16_BYTE( "lw11c.14f",      0x00000, 0x20000, CRC(e62742b6) SHA1(39dd3bdd405a8217d8816567d4f2014fc77f5ce8) )
	ROM_LOAD16_BYTE( "lw15c.14g",      0x00001, 0x20000, CRC(1b70f216) SHA1(f200f615dca8aa23d166e74b8baa9a8863ee7a95) )
	ROM_LOAD16_BYTE( "lw10c.13f",      0x40000, 0x20000, CRC(8f5ea3f5) SHA1(c3e43659bd7e03ec3d5c79647db380bde391b0f1) )
	ROM_LOAD16_BYTE( "lw14c.13g",      0x40001, 0x20000, CRC(708e7472) SHA1(44f82db41ffd1eece2d3f3c977f481fd84dfdbf3) )
	ROM_LOAD16_WORD_SWAP( "lw-07.13e", 0x80000, 0x80000, CRC(fd252a26) SHA1(5cfb097984912a5167a8c7ec4c2e119b642f9970) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "lw-01.9d",  0x000000, 0x80000, CRC(0318f298) SHA1(178ffd6da7bf845e30abf1bfc38a469cd319a73f) )
	ROM_LOAD64_WORD( "lw-08.9f",  0x000002, 0x80000, CRC(25a8e43c) SHA1(d57cee1fc508db2677e84882fb814e4d9ad20543) )
	ROM_LOAD64_WORD( "lw-05.9e",  0x000004, 0x80000, CRC(e4552fd7) SHA1(11147afc475904848458425661473586dd6f60cc) )
	ROM_LOAD64_WORD( "lw-12.9g",  0x000006, 0x80000, CRC(8e6a832b) SHA1(d63a1331fda2365f090fa31950098f321a720ea8) )
	ROM_LOAD64_WORD( "lw-02.12d", 0x200000, 0x80000, CRC(43e6c5c8) SHA1(d3e6c971de0477ec4e178adc82508208dd8b397f) )
	ROM_LOAD64_WORD( "lw-09.12f", 0x200002, 0x80000, CRC(899cb4ad) SHA1(95e61af338945e690f2a82746feba3871ea224eb) )
	ROM_LOAD64_WORD( "lw-06.12e", 0x200004, 0x80000, CRC(5b9edffc) SHA1(6fd8f4a3ab070733b52365ab1945bf86acb2bf62) )
	ROM_LOAD64_WORD( "lw-13.12g", 0x200006, 0x80000, CRC(8e058ef5) SHA1(00f2c0050fd106276ea5398511c5861ebfbc0d10) )

	ROM_REGION( 0x8000, "stars", 0 )
	ROM_COPY( "gfx", 0x200000, 0x000000, 0x8000 )

	ROM_REGION( 0x18000, "audiocpu", 0 )
	ROM_LOAD( "lw_00.14a",  0x00000, 0x08000, CRC(59df2a63) SHA1(dfe1fffc7a17179a80a2ae623e93b30a7d6df20d) )    // == lw_00b.14a
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "lw-03u.14c", 0x00000, 0x20000, CRC(807d051f) SHA1(720e4733787b9b11f4d1cdce0892b69475802844) )
	ROM_LOAD( "lw-04u.13c", 0x20000, 0x20000, CRC(e6cd098e) SHA1(667f6e5736f76a1c4c450c4e2035574ea89d7910) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "lwchr.3a",     0x0000, 0x0117, CRC(54ed4c39) SHA1(961309335dc1c84482ebe99ea938b32d3a6ae9a8) )
	ROM_LOAD( "lwio.15e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 88618B-2 */
ROM_START( forgottnua )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )
	ROM_LOAD16_BYTE( "lwu_11a.14f",    0x00000, 0x20000, CRC(ddf78831) SHA1(b9c815613efdfde933d4500b588798b7fb4c1854) )
	ROM_LOAD16_BYTE( "lwu_15a.14g",    0x00001, 0x20000, CRC(f7ce2097) SHA1(44c06fabdb6de7d8afc2164458c90b0be9cf945d) )
	ROM_LOAD16_BYTE( "lwu_10a.13f",    0x40000, 0x20000, CRC(8cb38c81) SHA1(1d36cab7d17ff778ee7dfcd9606a3a87f6906f21) )
	ROM_LOAD16_BYTE( "lwu_14a.13g",    0x40001, 0x20000, CRC(d70ef9fd) SHA1(b393aa2a7bea440fdcf057ffc6ff233fc0d35d4b) )
	ROM_LOAD16_WORD_SWAP( "lw-07.13e", 0x80000, 0x80000, CRC(fd252a26) SHA1(5cfb097984912a5167a8c7ec4c2e119b642f9970) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "lw-01.9d",  0x000000, 0x80000, CRC(0318f298) SHA1(178ffd6da7bf845e30abf1bfc38a469cd319a73f) )
	ROM_LOAD64_WORD( "lw-08.9f",  0x000002, 0x80000, CRC(25a8e43c) SHA1(d57cee1fc508db2677e84882fb814e4d9ad20543) )
	ROM_LOAD64_WORD( "lw-05.9e",  0x000004, 0x80000, CRC(e4552fd7) SHA1(11147afc475904848458425661473586dd6f60cc) )
	ROM_LOAD64_WORD( "lw-12.9g",  0x000006, 0x80000, CRC(8e6a832b) SHA1(d63a1331fda2365f090fa31950098f321a720ea8) )
	ROM_LOAD64_WORD( "lw-02.12d", 0x200000, 0x80000, CRC(43e6c5c8) SHA1(d3e6c971de0477ec4e178adc82508208dd8b397f) )
	ROM_LOAD64_WORD( "lw-09.12f", 0x200002, 0x80000, CRC(899cb4ad) SHA1(95e61af338945e690f2a82746feba3871ea224eb) )
	ROM_LOAD64_WORD( "lw-06.12e", 0x200004, 0x80000, CRC(5b9edffc) SHA1(6fd8f4a3ab070733b52365ab1945bf86acb2bf62) )
	ROM_LOAD64_WORD( "lw-13.12g", 0x200006, 0x80000, CRC(8e058ef5) SHA1(00f2c0050fd106276ea5398511c5861ebfbc0d10) )

	ROM_REGION( 0x8000, "stars", 0 )
	ROM_COPY( "gfx", 0x200000, 0x000000, 0x8000 )

	ROM_REGION( 0x18000, "audiocpu", 0 )
	ROM_LOAD( "lwu_00.14a", 0x00000, 0x08000, CRC(59df2a63) SHA1(dfe1fffc7a17179a80a2ae623e93b30a7d6df20d) )    // == lw_00b.14a
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "lw-03u.14c", 0x00000, 0x20000, CRC(807d051f) SHA1(720e4733787b9b11f4d1cdce0892b69475802844) )
	ROM_LOAD( "lw-04u.13c", 0x20000, 0x20000, CRC(e6cd098e) SHA1(667f6e5736f76a1c4c450c4e2035574ea89d7910) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "lwchr.3a",     0x0000, 0x0117, CRC(54ed4c39) SHA1(961309335dc1c84482ebe99ea938b32d3a6ae9a8) )
	ROM_LOAD( "lwio.15e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 88618B-2 */
ROM_START( forgottnuaa ) /* 1 byte difference to parent set. Region byte or pointer to the US "warning" screen at boot */
	ROM_REGION( CODE_SIZE, "maincpu", 0 )
	ROM_LOAD16_BYTE( "lwu_11aa.14f",   0x00000, 0x20000, CRC(73e920b7) SHA1(2df12fc1a66f488d06b0927db909da81466d7d07) ) // == lw11.12f
	ROM_LOAD16_BYTE( "lwu_15aa.14g",   0x00001, 0x20000, CRC(e47524b9) SHA1(2fad1e59432cb63017caf691bb28eb7cbcecb3c6) ) // 1 byte difference to lw15.12h 0x8B6 == 0x07
	ROM_LOAD16_BYTE( "lwu_10aa.13f",   0x40000, 0x20000, CRC(bea45994) SHA1(c419f65c5e0c11ae7508ec54412bf6b62fac4f72) ) // == lw10.13f
	ROM_LOAD16_BYTE( "lwu_14aa.13g",   0x40001, 0x20000, CRC(539b2339) SHA1(8a9e452ef8ed05e0b956d36990266657d3077470) ) // == lw14.13h
	ROM_LOAD16_WORD_SWAP( "lw-07.13e", 0x80000, 0x80000, CRC(fd252a26) SHA1(5cfb097984912a5167a8c7ec4c2e119b642f9970) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "lw-01.9d",  0x000000, 0x80000, CRC(0318f298) SHA1(178ffd6da7bf845e30abf1bfc38a469cd319a73f) )
	ROM_LOAD64_WORD( "lw-08.9f",  0x000002, 0x80000, CRC(25a8e43c) SHA1(d57cee1fc508db2677e84882fb814e4d9ad20543) )
	ROM_LOAD64_WORD( "lw-05.9e",  0x000004, 0x80000, CRC(e4552fd7) SHA1(11147afc475904848458425661473586dd6f60cc) )
	ROM_LOAD64_WORD( "lw-12.9g",  0x000006, 0x80000, CRC(8e6a832b) SHA1(d63a1331fda2365f090fa31950098f321a720ea8) )
	ROM_LOAD64_WORD( "lw-02.12d", 0x200000, 0x80000, CRC(43e6c5c8) SHA1(d3e6c971de0477ec4e178adc82508208dd8b397f) )
	ROM_LOAD64_WORD( "lw-09.12f", 0x200002, 0x80000, CRC(899cb4ad) SHA1(95e61af338945e690f2a82746feba3871ea224eb) )
	ROM_LOAD64_WORD( "lw-06.12e", 0x200004, 0x80000, CRC(5b9edffc) SHA1(6fd8f4a3ab070733b52365ab1945bf86acb2bf62) )
	ROM_LOAD64_WORD( "lw-13.12g", 0x200006, 0x80000, CRC(8e058ef5) SHA1(00f2c0050fd106276ea5398511c5861ebfbc0d10) )

	ROM_REGION( 0x8000, "stars", 0 )
	ROM_COPY( "gfx", 0x200000, 0x000000, 0x8000 )

	ROM_REGION( 0x18000, "audiocpu", 0 )
	ROM_LOAD( "lwu_00.14a", 0x00000, 0x08000, CRC(59df2a63) SHA1(dfe1fffc7a17179a80a2ae623e93b30a7d6df20d) )    // == lw_00b.14a
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "lw-03u.14c", 0x00000, 0x20000, CRC(807d051f) SHA1(720e4733787b9b11f4d1cdce0892b69475802844) )
	ROM_LOAD( "lw-04u.13c", 0x20000, 0x20000, CRC(e6cd098e) SHA1(667f6e5736f76a1c4c450c4e2035574ea89d7910) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "lwchr.3a",     0x0000, 0x0117, CRC(54ed4c39) SHA1(961309335dc1c84482ebe99ea938b32d3a6ae9a8) )
	ROM_LOAD( "lwio.15e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 88618B-2 */
ROM_START( forgottnj ) // comes from a PCB with a very early serial number:  LW00022
	ROM_REGION( CODE_SIZE, "maincpu", 0 )
	ROM_LOAD16_BYTE( "11.14f",         0x00000, 0x20000, CRC(c9a6b319) SHA1(152f65658af6333f10a0608a90330e1cc074d1b2) )
	ROM_LOAD16_BYTE( "15.14g",         0x00001, 0x20000, CRC(524f920e) SHA1(9c3ad6c8009ff55f81cc859c2e47fa6d6ede112b) )
	ROM_LOAD16_BYTE( "10.13f",         0x40000, 0x20000, CRC(ff7e41d9) SHA1(6fc0c9692233ce68068a515c000a976f82585f8b) )
	ROM_LOAD16_BYTE( "14.13g",         0x40001, 0x20000, CRC(80c3a813) SHA1(7d50a6de8a2634e4300985c459e190d1b4eddb10) )
	ROM_LOAD16_WORD_SWAP( "lw-07.13e", 0x80000, 0x80000, CRC(fd252a26) SHA1(5cfb097984912a5167a8c7ec4c2e119b642f9970) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "lw-01.9d",  0x000000, 0x80000, CRC(0318f298) SHA1(178ffd6da7bf845e30abf1bfc38a469cd319a73f) )
	ROM_LOAD64_WORD( "lw-08.9f",  0x000002, 0x80000, CRC(25a8e43c) SHA1(d57cee1fc508db2677e84882fb814e4d9ad20543) )
	ROM_LOAD64_WORD( "lw-05.9e",  0x000004, 0x80000, CRC(e4552fd7) SHA1(11147afc475904848458425661473586dd6f60cc) )
	ROM_LOAD64_WORD( "lw-12.9g",  0x000006, 0x80000, CRC(8e6a832b) SHA1(d63a1331fda2365f090fa31950098f321a720ea8) )
	ROM_LOAD64_WORD( "lw-02.12d", 0x200000, 0x80000, CRC(43e6c5c8) SHA1(d3e6c971de0477ec4e178adc82508208dd8b397f) )
	ROM_LOAD64_WORD( "lw-09.12f", 0x200002, 0x80000, CRC(899cb4ad) SHA1(95e61af338945e690f2a82746feba3871ea224eb) )
	ROM_LOAD64_WORD( "lw-06.12e", 0x200004, 0x80000, CRC(5b9edffc) SHA1(6fd8f4a3ab070733b52365ab1945bf86acb2bf62) )
	ROM_LOAD64_WORD( "lw-13.12g", 0x200006, 0x80000, CRC(8e058ef5) SHA1(00f2c0050fd106276ea5398511c5861ebfbc0d10) )

	ROM_REGION( 0x8000, "stars", 0 )
	ROM_COPY( "gfx", 0x200000, 0x000000, 0x8000 )

	ROM_REGION( 0x18000, "audiocpu", 0 )
	ROM_LOAD( "00.14a", 0x00000, 0x08000, CRC(3bc962ea) SHA1(d61240a4512c4b27a9d89758e05aeebef6326fec) )
	ROM_CONTINUE(       0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "lw-03u.14c", 0x00000, 0x20000, CRC(807d051f) SHA1(720e4733787b9b11f4d1cdce0892b69475802844) ) // these 2 were mask ROMs
	ROM_LOAD( "lw-04u.13c", 0x20000, 0x20000, CRC(e6cd098e) SHA1(667f6e5736f76a1c4c450c4e2035574ea89d7910) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "lwchr.3a",     0x0000, 0x0117, CRC(54ed4c39) SHA1(961309335dc1c84482ebe99ea938b32d3a6ae9a8) )
	ROM_LOAD( "lwio.15e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 88618B-2 */
ROM_START( lostwrld )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )
	ROM_LOAD16_BYTE( "lw_11c.14f",     0x00000, 0x20000, CRC(67e42546) SHA1(3e385661f71616180a26b74e443978077246fe66) )
	ROM_LOAD16_BYTE( "lw_15c.14g",     0x00001, 0x20000, CRC(402e2a46) SHA1(cbb7017e75a425706505717bf83c2615f53309f9) )
	ROM_LOAD16_BYTE( "lw_10c.13f",     0x40000, 0x20000, CRC(c46479d7) SHA1(84fd9ef33ae7d0af2110e8dc299de25c0f039cee) )
	ROM_LOAD16_BYTE( "lw_14c.13g",     0x40001, 0x20000, CRC(97670f4a) SHA1(f249977c814abdff85007216d7fa57db5684be0f) )
	ROM_LOAD16_WORD_SWAP( "lw-07.13e", 0x80000, 0x80000, CRC(fd252a26) SHA1(5cfb097984912a5167a8c7ec4c2e119b642f9970) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "lw-01.9d",  0x000000, 0x80000, CRC(0318f298) SHA1(178ffd6da7bf845e30abf1bfc38a469cd319a73f) )
	ROM_LOAD64_WORD( "lw-08.9f",  0x000002, 0x80000, CRC(25a8e43c) SHA1(d57cee1fc508db2677e84882fb814e4d9ad20543) )
	ROM_LOAD64_WORD( "lw-05.9e",  0x000004, 0x80000, CRC(e4552fd7) SHA1(11147afc475904848458425661473586dd6f60cc) )
	ROM_LOAD64_WORD( "lw-12.9g",  0x000006, 0x80000, CRC(8e6a832b) SHA1(d63a1331fda2365f090fa31950098f321a720ea8) )
	ROM_LOAD64_WORD( "lw-02.12d", 0x200000, 0x80000, CRC(43e6c5c8) SHA1(d3e6c971de0477ec4e178adc82508208dd8b397f) )
	ROM_LOAD64_WORD( "lw-09.12f", 0x200002, 0x80000, CRC(899cb4ad) SHA1(95e61af338945e690f2a82746feba3871ea224eb) )
	ROM_LOAD64_WORD( "lw-06.12e", 0x200004, 0x80000, CRC(5b9edffc) SHA1(6fd8f4a3ab070733b52365ab1945bf86acb2bf62) )
	ROM_LOAD64_WORD( "lw-13.12g", 0x200006, 0x80000, CRC(8e058ef5) SHA1(00f2c0050fd106276ea5398511c5861ebfbc0d10) )

	ROM_REGION( 0x8000, "stars", 0 )
	ROM_COPY( "gfx", 0x200000, 0x000000, 0x8000 )

	ROM_REGION( 0x18000, "audiocpu", 0 )
	ROM_LOAD( "lw_00b.14a", 0x00000, 0x08000, CRC(59df2a63) SHA1(dfe1fffc7a17179a80a2ae623e93b30a7d6df20d) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "lw-03.14c",  0x00000, 0x20000, CRC(ce2159e7) SHA1(77d564f8b768c1cbd6e5b334f7ee86c4c3f9d62e) )
	ROM_LOAD( "lw-04.13c",  0x20000, 0x20000, CRC(39305536) SHA1(ad24d7b6df2dc5e84a35aecb9ba9b0aaa27ab6e5) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "lwchr.3a",     0x0000, 0x0117, CRC(54ed4c39) SHA1(961309335dc1c84482ebe99ea938b32d3a6ae9a8) )
	ROM_LOAD( "lwio.15e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 88618B-2 */
ROM_START( lostwrldo )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )
	ROM_LOAD16_BYTE( "lw_11.14f",      0x00000, 0x20000, CRC(61e2cc56) SHA1(bc192e37806129dd00677ca2c2e4287aa670973d) )
	ROM_LOAD16_BYTE( "lw_15.14g",      0x00001, 0x20000, CRC(8a0c18d3) SHA1(7a4d80cefa0c2be427de43fbbbb906880876ed1d) )
	ROM_LOAD16_BYTE( "lw_10.13f",      0x40000, 0x20000, CRC(23bca4d5) SHA1(c9d356d052d82117b6c30e40aa02a5f1f5335a73) )
	ROM_LOAD16_BYTE( "lw_14.13g",      0x40001, 0x20000, CRC(3a023771) SHA1(30de9b76e104e897ddce64615f24bba0aa270d14) )
	ROM_LOAD16_WORD_SWAP( "lw-07.13e", 0x80000, 0x80000, CRC(fd252a26) SHA1(5cfb097984912a5167a8c7ec4c2e119b642f9970) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "lw-01.9d",  0x000000, 0x80000, CRC(0318f298) SHA1(178ffd6da7bf845e30abf1bfc38a469cd319a73f) )
	ROM_LOAD64_WORD( "lw-08.9f",  0x000002, 0x80000, CRC(25a8e43c) SHA1(d57cee1fc508db2677e84882fb814e4d9ad20543) )
	ROM_LOAD64_WORD( "lw-05.9e",  0x000004, 0x80000, CRC(e4552fd7) SHA1(11147afc475904848458425661473586dd6f60cc) )
	ROM_LOAD64_WORD( "lw-12.9g",  0x000006, 0x80000, CRC(8e6a832b) SHA1(d63a1331fda2365f090fa31950098f321a720ea8) )
	ROM_LOAD64_WORD( "lw-02.12d", 0x200000, 0x80000, CRC(43e6c5c8) SHA1(d3e6c971de0477ec4e178adc82508208dd8b397f) )
	ROM_LOAD64_WORD( "lw-09.12f", 0x200002, 0x80000, CRC(899cb4ad) SHA1(95e61af338945e690f2a82746feba3871ea224eb) )
	ROM_LOAD64_WORD( "lw-06.12e", 0x200004, 0x80000, CRC(5b9edffc) SHA1(6fd8f4a3ab070733b52365ab1945bf86acb2bf62) )
	ROM_LOAD64_WORD( "lw-13.12g", 0x200006, 0x80000, CRC(8e058ef5) SHA1(00f2c0050fd106276ea5398511c5861ebfbc0d10) )

	ROM_REGION( 0x8000, "stars", 0 )
	ROM_COPY( "gfx", 0x200000, 0x000000, 0x8000 )

	ROM_REGION( 0x18000, "audiocpu", 0 )
	ROM_LOAD( "lw_00b.14a", 0x00000, 0x08000, CRC(59df2a63) SHA1(dfe1fffc7a17179a80a2ae623e93b30a7d6df20d) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "lw-03.14c",  0x00000, 0x20000, CRC(ce2159e7) SHA1(77d564f8b768c1cbd6e5b334f7ee86c4c3f9d62e) )
	ROM_LOAD( "lw-04.13c",  0x20000, 0x20000, CRC(39305536) SHA1(ad24d7b6df2dc5e84a35aecb9ba9b0aaa27ab6e5) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "lwchr.3a",     0x0000, 0x0117, CRC(54ed4c39) SHA1(961309335dc1c84482ebe99ea938b32d3a6ae9a8) )
	ROM_LOAD( "lwio.15e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 88620-B-? */
/* Note that ROMs are labeled left to right, top to bottom, instead of top to bottom, left to right as usual. */
ROM_START( ghouls )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )
	ROM_LOAD16_BYTE( "dme_29.10h", 0x00000, 0x20000, CRC(166a58a2) SHA1(f21fcf88d2ebb7bc9e8885fde760a5d82f295c1a) )
	ROM_LOAD16_BYTE( "dme_30.10j", 0x00001, 0x20000, CRC(7ac8407a) SHA1(3613699213db47bfeabedf87f12eb0fa7e5973b6) )
	ROM_LOAD16_BYTE( "dme_27.9h",  0x40000, 0x20000, CRC(f734b2be) SHA1(fa230bf5503487ec11d767485a18f0a55dcc13d2) )
	ROM_LOAD16_BYTE( "dme_28.9j",  0x40001, 0x20000, CRC(03d3e714) SHA1(a07786062358c89f3b4634b8822173261802290b) )
	ROM_LOAD16_WORD( "dm-17.7j",   0x80000, 0x80000, CRC(3ea1b0f2) SHA1(c51f1c38cdaed77ad715cedd845617a291ab2441) )

	ROM_REGION( 0x300000, "gfx", 0 )
	ROM_LOAD64_WORD( "dm-05.3a", 0x000000, 0x80000, CRC(0ba9c0b0) SHA1(c4945b603115f32b7346d72426571dc2d361159f) )
	ROM_LOAD64_WORD( "dm-07.3f", 0x000002, 0x80000, CRC(5d760ab9) SHA1(212176947933fcfef991bc80ad5bd91718689ffe) )
	ROM_LOAD64_WORD( "dm-06.3c", 0x000004, 0x80000, CRC(4ba90b59) SHA1(35bc9dec5ddbf064c30c951627581c16764456ac) )
	ROM_LOAD64_WORD( "dm-08.3g", 0x000006, 0x80000, CRC(4bdee9de) SHA1(7d0c4736f16577afe9966447a18f039728f6fbdf) )
	ROM_LOAD64_BYTE( "09.4a",    0x200000, 0x10000, CRC(ae24bb19) SHA1(aa91c6ffe657b878e10e4e930457b530f7bb529b) )
	ROM_LOAD64_BYTE( "18.7a",    0x200001, 0x10000, CRC(d34e271a) SHA1(55211fc2861dce32951f41624c9c99c09bf3b184) )
	ROM_LOAD64_BYTE( "13.4e",    0x200002, 0x10000, CRC(3f70dd37) SHA1(9ecb4dec9d131e9fca15ded7d71986a9fcb62c19) )
	ROM_LOAD64_BYTE( "22.7e",    0x200003, 0x10000, CRC(7e69e2e6) SHA1(4e0b4d2474beaa5c869c8f1a91893c79ac6e7f39) )
	ROM_LOAD64_BYTE( "11.4c",    0x200004, 0x10000, CRC(37c9b6c6) SHA1(b2bb82537e335339846dbf9588cfacfdbdd75ee6) )
	ROM_LOAD64_BYTE( "20.7c",    0x200005, 0x10000, CRC(2f1345b4) SHA1(14c450abcf9defa29c6f48e5ffd0b9d1e4a66a1d) )
	ROM_LOAD64_BYTE( "15.4g",    0x200006, 0x10000, CRC(3c2a212a) SHA1(f8fa0e0e2d09ea37c54d1c2493752b4e97e3f534) )
	ROM_LOAD64_BYTE( "24.7g",    0x200007, 0x10000, CRC(889aac05) SHA1(9301dcecee699e7f7672bb498122e1f4831ce536) )
	ROM_LOAD64_BYTE( "10.4b",    0x280000, 0x10000, CRC(bcc0f28c) SHA1(02f587aa4ae71631f27b0e3aaf1829cdded1bdc2) )
	ROM_LOAD64_BYTE( "19.7b",    0x280001, 0x10000, CRC(2a40166a) SHA1(dc4e75d7ed87ae5386d721a09113bba364740465) )
	ROM_LOAD64_BYTE( "14.4f",    0x280002, 0x10000, CRC(20f85c03) SHA1(86385139a9b42270aade758bfe338525936f5671) )
	ROM_LOAD64_BYTE( "23.7f",    0x280003, 0x10000, CRC(8426144b) SHA1(2dbf9625413b302fcdad5bef8733a9dfbfaead52) )
	ROM_LOAD64_BYTE( "12.4d",    0x280004, 0x10000, CRC(da088d61) SHA1(67229eff2827a42af97a60ceb252e132e7f307bc) )
	ROM_LOAD64_BYTE( "21.7d",    0x280005, 0x10000, CRC(17e11df0) SHA1(42fb15e9300b07fc5f4bc21744484869859b130c) )
	ROM_LOAD64_BYTE( "16.4h",    0x280006, 0x10000, CRC(f187ba1c) SHA1(6d9441d04ecef2a9d9c7a2cc7781acd7904c2061) )
	ROM_LOAD64_BYTE( "25.7h",    0x280007, 0x10000, CRC(29f79c78) SHA1(26000a58454a06c3016f99ebc3a79c52911a7070) )

	ROM_REGION( 0x18000, "audiocpu", 0 )
	ROM_LOAD( "26.10a",    0x00000, 0x08000, CRC(3692f6e5) SHA1(61b8438d60a39b4cf5062dff0a53228e8a4e4b5f) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", ROMREGION_ERASEFF ) /* Samples (not present) */

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "dm620.2a",     0x0000, 0x0117, CRC(f6e5f727) SHA1(8d38c458721347272ccc14b2c0e9885c4f891477) )
	ROM_LOAD( "lwio.8i",      0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 88620-B-2 */
/* Note that ROMs are labeled left to right, top to bottom, instead of top to bottom, left to right as usual. */
ROM_START( ghoulsu )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )
	ROM_LOAD16_BYTE( "dmu_29.10h", 0x00000, 0x20000, CRC(334d85b2) SHA1(89bacc28b7c799c7568420e3de5a99060baa7b0f) )
	ROM_LOAD16_BYTE( "dmu_30.10j", 0x00001, 0x20000, CRC(cee8ceb5) SHA1(fc8db1ce0c143dfda0b5989d02d5e5a872e27cd2) )
	ROM_LOAD16_BYTE( "dmu_27.9h",  0x40000, 0x20000, CRC(4a524140) SHA1(cebd651293c3570912d5506c1c223c39bcccc802) )
	ROM_LOAD16_BYTE( "dmu_28.9j",  0x40001, 0x20000, CRC(94aae205) SHA1(514b3c1b9b0b22300a94229825c3be66332ea5ed) )
	ROM_LOAD16_WORD( "dm-17.7j",   0x80000, 0x80000, CRC(3ea1b0f2) SHA1(c51f1c38cdaed77ad715cedd845617a291ab2441) )

	ROM_REGION( 0x300000, "gfx", 0 )
	ROM_LOAD64_WORD( "dm-05.3a", 0x000000, 0x80000, CRC(0ba9c0b0) SHA1(c4945b603115f32b7346d72426571dc2d361159f) )
	ROM_LOAD64_WORD( "dm-07.3f", 0x000002, 0x80000, CRC(5d760ab9) SHA1(212176947933fcfef991bc80ad5bd91718689ffe) )
	ROM_LOAD64_WORD( "dm-06.3c", 0x000004, 0x80000, CRC(4ba90b59) SHA1(35bc9dec5ddbf064c30c951627581c16764456ac) )
	ROM_LOAD64_WORD( "dm-08.3g", 0x000006, 0x80000, CRC(4bdee9de) SHA1(7d0c4736f16577afe9966447a18f039728f6fbdf) )
	ROM_LOAD64_BYTE( "09.4a",    0x200000, 0x10000, CRC(ae24bb19) SHA1(aa91c6ffe657b878e10e4e930457b530f7bb529b) )
	ROM_LOAD64_BYTE( "18.7a",    0x200001, 0x10000, CRC(d34e271a) SHA1(55211fc2861dce32951f41624c9c99c09bf3b184) )
	ROM_LOAD64_BYTE( "13.4e",    0x200002, 0x10000, CRC(3f70dd37) SHA1(9ecb4dec9d131e9fca15ded7d71986a9fcb62c19) )
	ROM_LOAD64_BYTE( "22.7e",    0x200003, 0x10000, CRC(7e69e2e6) SHA1(4e0b4d2474beaa5c869c8f1a91893c79ac6e7f39) )
	ROM_LOAD64_BYTE( "11.4c",    0x200004, 0x10000, CRC(37c9b6c6) SHA1(b2bb82537e335339846dbf9588cfacfdbdd75ee6) )
	ROM_LOAD64_BYTE( "20.7c",    0x200005, 0x10000, CRC(2f1345b4) SHA1(14c450abcf9defa29c6f48e5ffd0b9d1e4a66a1d) )
	ROM_LOAD64_BYTE( "15.4g",    0x200006, 0x10000, CRC(3c2a212a) SHA1(f8fa0e0e2d09ea37c54d1c2493752b4e97e3f534) )
	ROM_LOAD64_BYTE( "24.7g",    0x200007, 0x10000, CRC(889aac05) SHA1(9301dcecee699e7f7672bb498122e1f4831ce536) )
	ROM_LOAD64_BYTE( "10.4b",    0x280000, 0x10000, CRC(bcc0f28c) SHA1(02f587aa4ae71631f27b0e3aaf1829cdded1bdc2) )
	ROM_LOAD64_BYTE( "19.7b",    0x280001, 0x10000, CRC(2a40166a) SHA1(dc4e75d7ed87ae5386d721a09113bba364740465) )
	ROM_LOAD64_BYTE( "14.4f",    0x280002, 0x10000, CRC(20f85c03) SHA1(86385139a9b42270aade758bfe338525936f5671) )
	ROM_LOAD64_BYTE( "23.7f",    0x280003, 0x10000, CRC(8426144b) SHA1(2dbf9625413b302fcdad5bef8733a9dfbfaead52) )
	ROM_LOAD64_BYTE( "12.4d",    0x280004, 0x10000, CRC(da088d61) SHA1(67229eff2827a42af97a60ceb252e132e7f307bc) )
	ROM_LOAD64_BYTE( "21.7d",    0x280005, 0x10000, CRC(17e11df0) SHA1(42fb15e9300b07fc5f4bc21744484869859b130c) )
	ROM_LOAD64_BYTE( "16.4h",    0x280006, 0x10000, CRC(f187ba1c) SHA1(6d9441d04ecef2a9d9c7a2cc7781acd7904c2061) )
	ROM_LOAD64_BYTE( "25.7h",    0x280007, 0x10000, CRC(29f79c78) SHA1(26000a58454a06c3016f99ebc3a79c52911a7070) )

	ROM_REGION( 0x18000, "audiocpu", 0 )
	ROM_LOAD( "26.10a",    0x00000, 0x08000, CRC(3692f6e5) SHA1(61b8438d60a39b4cf5062dff0a53228e8a4e4b5f) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", ROMREGION_ERASEFF ) /* Samples (not present) */

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "dm620.2a",     0x0000, 0x0117, CRC(f6e5f727) SHA1(8d38c458721347272ccc14b2c0e9885c4f891477) )
	ROM_LOAD( "lwio.8i",      0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 88622B-2 */
/* Note that ROMs are labeled left to right, top to bottom, instead of top to bottom, left to right as usual. */
ROM_START( daimakai )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )
	ROM_LOAD16_BYTE( "dmj_38.12f", 0x00000, 0x20000, CRC(82fd1798) SHA1(7a199384659d8e6602384b1953339f221d61a9e6) )
	ROM_LOAD16_BYTE( "dmj_39.12h", 0x00001, 0x20000, CRC(35366ccc) SHA1(42c7004a641f34b9dd1333be51b50639a97e2be9) )
	ROM_LOAD16_BYTE( "dmj_40.13f", 0x40000, 0x20000, CRC(a17c170a) SHA1(62a9cb65df90827334d453a98e826dc1bfc27136) )
	ROM_LOAD16_BYTE( "dmj_41.13h", 0x40001, 0x20000, CRC(6af0b391) SHA1(5a2d74d207c04e24bcea7eeffa1c8b96b6df77e1) )
	ROM_LOAD16_BYTE( "dm_33.10f",  0x80000, 0x20000, CRC(384d60c4) SHA1(258f9e2334b7240cf665b530f2c69b8826850687) ) // == dm-17.7j
	ROM_LOAD16_BYTE( "dm_34.10h",  0x80001, 0x20000, CRC(19abe30f) SHA1(aea7d5c8357201b68dec70d7cc8f87dfb8fce207) ) // == dm-17.7j
	ROM_LOAD16_BYTE( "dm_35.11f",  0xc0000, 0x20000, CRC(c04b85c8) SHA1(f8659624bb9d418d02f63f43478d3b53cfe18718) ) // == dm-17.7j
	ROM_LOAD16_BYTE( "dm_36.11h",  0xc0001, 0x20000, CRC(89be83de) SHA1(6dfd1380304a85dee7cac4d0b2cfd7625b9020bf) ) // == dm-17.7j

	ROM_REGION( 0x300000, "gfx", 0 )
	ROM_LOAD64_BYTE( "dm_02.4b",  0x000000, 0x20000, CRC(8b98dc48) SHA1(e827881e2ba5cccd907d1692a1945c1b75d46f12) ) // == dm-05.3a
	ROM_LOAD64_BYTE( "dm_01.4a",  0x000001, 0x20000, CRC(80896c33) SHA1(20ffc427c596828005e34cdd8e4efa0d332262e9) ) // == dm-05.3a
	ROM_LOAD64_BYTE( "dm_10.9b",  0x000002, 0x20000, CRC(c2e7d9ef) SHA1(52aae6cf373f8c7150833047be28b74dd5eb5af6) ) // == dm-07.3f
	ROM_LOAD64_BYTE( "dm_09.9a",  0x000003, 0x20000, CRC(c9c4afa5) SHA1(34571e3e49c86b87fa34eefbc5f9fe258aba5f1a) ) // == dm-07.3f
	ROM_LOAD64_BYTE( "dm_18.5e",  0x000004, 0x20000, CRC(1aa0db99) SHA1(69ac302b2f6f0b96f78cb57b0b4cdae464086262) ) // == dm-06.3c
	ROM_LOAD64_BYTE( "dm_17.5c",  0x000005, 0x20000, CRC(dc6ed8ad) SHA1(1ffc4a48a7ff9b542ab6f63a60bea3c1a7a6e63b) ) // == dm-06.3c
	ROM_LOAD64_BYTE( "dm_30.8h",  0x000006, 0x20000, CRC(d9d3f8bd) SHA1(6c6853a384f8d60ca46a0607fd47c76a83700fba) ) // == dm-08.3g
	ROM_LOAD64_BYTE( "dm_29.8f",  0x000007, 0x20000, CRC(49a48796) SHA1(76c18c684dba4aa91ee6caae0f38fe3e1cc50832) ) // == dm-08.3g
	ROM_LOAD64_BYTE( "dm_04.5b",  0x100000, 0x20000, CRC(a4f4f8f0) SHA1(edca0f61b40a18afe279f7007c233064130cfb4f) ) // == dm-05.3a
	ROM_LOAD64_BYTE( "dm_03.5a",  0x100001, 0x20000, CRC(b1033e62) SHA1(547fc281dd9e7a74ac86c3692508c7bde9b6167b) ) // == dm-05.3a
	ROM_LOAD64_BYTE( "dm_12.10b", 0x100002, 0x20000, CRC(10fdd76a) SHA1(aee774d6323292799dff7a30ef9559c92fe5507a) ) // == dm-07.3f
	ROM_LOAD64_BYTE( "dm_11.10a", 0x100003, 0x20000, CRC(9040cb04) SHA1(b32e9056fc20a5162868eade10f3ef5efc167a28) ) // == dm-07.3f
	ROM_LOAD64_BYTE( "dm_20.7e",  0x100004, 0x20000, CRC(281d0b3e) SHA1(70e1813de184ad0ec164145b7b843b5e387494e3) ) // == dm-06.3c
	ROM_LOAD64_BYTE( "dm_19.7c",  0x100005, 0x20000, CRC(2623b52f) SHA1(fc4200924452bfbff687934782398ed345dc0aa0) ) // == dm-06.3c
	ROM_LOAD64_BYTE( "dm_32.9h",  0x100006, 0x20000, CRC(99692344) SHA1(67dc70618568b7c0adcb00a612aaf5501f6c8c0f) ) // == dm-08.3g
	ROM_LOAD64_BYTE( "dm_31.9f",  0x100007, 0x20000, CRC(54acb729) SHA1(d1fca43db36253fd19db4337c49272a6cadff597) ) // == dm-08.3g
	ROM_LOAD64_BYTE( "dm_06.7b",  0x200000, 0x10000, CRC(ae24bb19) SHA1(aa91c6ffe657b878e10e4e930457b530f7bb529b) ) // == 09.4a
	ROM_LOAD64_BYTE( "dm_05.7a",  0x200001, 0x10000, CRC(d34e271a) SHA1(55211fc2861dce32951f41624c9c99c09bf3b184) ) // == 18.7a
	ROM_LOAD64_BYTE( "dm_14.11b", 0x200002, 0x10000, CRC(3f70dd37) SHA1(9ecb4dec9d131e9fca15ded7d71986a9fcb62c19) ) // == 13.4e
	ROM_LOAD64_BYTE( "dm_13.11a", 0x200003, 0x10000, CRC(7e69e2e6) SHA1(4e0b4d2474beaa5c869c8f1a91893c79ac6e7f39) ) // == 22.7e
	ROM_LOAD64_BYTE( "dm_22.8e",  0x200004, 0x10000, CRC(37c9b6c6) SHA1(b2bb82537e335339846dbf9588cfacfdbdd75ee6) ) // == 11.4c
	ROM_LOAD64_BYTE( "dm_21.8c",  0x200005, 0x10000, CRC(2f1345b4) SHA1(14c450abcf9defa29c6f48e5ffd0b9d1e4a66a1d) ) // == 20.7c
	ROM_LOAD64_BYTE( "dm_26.10e", 0x200006, 0x10000, CRC(3c2a212a) SHA1(f8fa0e0e2d09ea37c54d1c2493752b4e97e3f534) ) // == 15.4g
	ROM_LOAD64_BYTE( "dm_25.10c", 0x200007, 0x10000, CRC(889aac05) SHA1(9301dcecee699e7f7672bb498122e1f4831ce536) ) // == 24.7g
	ROM_LOAD64_BYTE( "dm_08.8b",  0x280000, 0x10000, CRC(bcc0f28c) SHA1(02f587aa4ae71631f27b0e3aaf1829cdded1bdc2) ) // == 10.4b
	ROM_LOAD64_BYTE( "dm_07.8a",  0x280001, 0x10000, CRC(2a40166a) SHA1(dc4e75d7ed87ae5386d721a09113bba364740465) ) // == 19.7b
	ROM_LOAD64_BYTE( "dm_16.12b", 0x280002, 0x10000, CRC(20f85c03) SHA1(86385139a9b42270aade758bfe338525936f5671) ) // == 14.4f
	ROM_LOAD64_BYTE( "dm_15.12a", 0x280003, 0x10000, CRC(8426144b) SHA1(2dbf9625413b302fcdad5bef8733a9dfbfaead52) ) // == 23.7f
	ROM_LOAD64_BYTE( "dm_24.9e",  0x280004, 0x10000, CRC(da088d61) SHA1(67229eff2827a42af97a60ceb252e132e7f307bc) ) // == 12.4d
	ROM_LOAD64_BYTE( "dm_23.9c",  0x280005, 0x10000, CRC(17e11df0) SHA1(42fb15e9300b07fc5f4bc21744484869859b130c) ) // == 21.7d
	ROM_LOAD64_BYTE( "dm_28.11e", 0x280006, 0x10000, CRC(f187ba1c) SHA1(6d9441d04ecef2a9d9c7a2cc7781acd7904c2061) ) // == 16.4h
	ROM_LOAD64_BYTE( "dm_27.11c", 0x280007, 0x10000, CRC(29f79c78) SHA1(26000a58454a06c3016f99ebc3a79c52911a7070) ) // == 25.7h

	ROM_REGION( 0x18000, "audiocpu", 0 )
	ROM_LOAD( "37.13c",     0x00000, 0x08000, CRC(3692f6e5) SHA1(61b8438d60a39b4cf5062dff0a53228e8a4e4b5f) )    // == 26.10a
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", ROMREGION_ERASEFF ) /* Samples (not present) */

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "dm22a.1a",     0x0000, 0x0117, CRC(d4776116) SHA1(802d8bc18ad2b8c998e5338a0aa4e74895de2c81) )
	ROM_LOAD( "lwio.12c",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 91634B-2, Japan Resale Ver. */
ROM_START( daimakair )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "damj_23.8f", 0x00000, 0x80000, CRC(c3b248ec) SHA1(5c016d2dcf882b2a9564e3c4502a0f51ee3d1803) )
	ROM_LOAD16_WORD_SWAP( "damj_22.7f", 0x80000, 0x80000, CRC(595ff2f3) SHA1(ac14b81e15f2c340526a03acbb4c28181d94d5b9) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "dam_01.3a",  0x000000, 0x80000, CRC(0ba9c0b0) SHA1(c4945b603115f32b7346d72426571dc2d361159f) )    // == dm-05.3a
	ROM_LOAD64_WORD( "dam_02.4a",  0x000002, 0x80000, CRC(5d760ab9) SHA1(212176947933fcfef991bc80ad5bd91718689ffe) )    // == dm-07.3f
	ROM_LOAD64_WORD( "dam_03.5a",  0x000004, 0x80000, CRC(4ba90b59) SHA1(35bc9dec5ddbf064c30c951627581c16764456ac) )    // == dm-06.3c
	ROM_LOAD64_WORD( "dam_04.6a",  0x000006, 0x80000, CRC(4bdee9de) SHA1(7d0c4736f16577afe9966447a18f039728f6fbdf) )    // == dm-08.3g
	ROM_LOAD64_WORD( "dam_05.7a",  0x200000, 0x80000, CRC(7dc61b94) SHA1(7796bae7555c541b3c80aacfa24788aeb2ccdfd5) )
	ROM_LOAD64_WORD( "dam_06.8a",  0x200002, 0x80000, CRC(fde89758) SHA1(9a6192f629cd1e74e225ef7426338c2816c6b977) )
	ROM_LOAD64_WORD( "dam_07.9a",  0x200004, 0x80000, CRC(ec351d78) SHA1(1005a83be4b5577612143ae7f64ca4a08aae7959) )
	ROM_LOAD64_WORD( "dam_08.10a", 0x200006, 0x80000, CRC(ee2acc1e) SHA1(4628a9b2447266349d97132003992a21e2bb423a) )

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "dam_09.12a",  0x00000, 0x08000, CRC(0656ff53) SHA1(063a8124dbe73d014b11f72007f1b877afd1a661) )   // == 26.10a + garbage
	ROM_CONTINUE(            0x10000, 0x18000 ) // second half of ROM is unused, not mapped in memory

	ROM_REGION( 0x40000, "oki", ROMREGION_ERASEFF ) /* Samples (not present) */

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "dam63b.1a",    0x0000, 0x0117, CRC(474b3c8a) SHA1(da364581685067fc955ed43b982a7aa7a2648286) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 89624B-2 */
ROM_START( strider )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "30.11f",        0x00000, 0x20000, CRC(da997474) SHA1(3e4ac98f9a6967d61899281b31c7de779723397b) )
	ROM_LOAD16_BYTE( "35.11h",        0x00001, 0x20000, CRC(5463aaa3) SHA1(e2d07ec2d818e9a2e2d7a77ff0309ae4011c0083) )
	ROM_LOAD16_BYTE( "31.12f",        0x40000, 0x20000, CRC(d20786db) SHA1(c9c75488e6bb37cfd0d56073faf87ff5713bc9a0) )
	ROM_LOAD16_BYTE( "36.12h",        0x40001, 0x20000, CRC(21aa2863) SHA1(446dc9280630318deb423531210a4eedfb4adfa6) )
	ROM_LOAD16_WORD_SWAP( "st-14.8h", 0x80000, 0x80000, CRC(9b3cfc08) SHA1(a7d7f270a097437affa845d80bed82a1fa874878) )  // in "32" socket

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "st-2.8a",   0x000000, 0x80000, CRC(4eee9aea) SHA1(5e619fd5f3f1181e32a8fd9dbb4661d74ff8a484) ) // in "6" socket
	ROM_LOAD64_WORD( "st-11.10a", 0x000002, 0x80000, CRC(2d7f21e4) SHA1(593cec513de40ff802084d54313bb25a4561e25d) ) // in "8" socket
	ROM_LOAD64_WORD( "st-5.4a",   0x000004, 0x80000, CRC(7705aa46) SHA1(6cbfa30b2852fd117d117beefba434ce41d24c2f) ) // in "2" socket
	ROM_LOAD64_WORD( "st-9.6a",   0x000006, 0x80000, CRC(5b18b722) SHA1(cf71c62348ca6b404279e87a6686cb3a842eb381) ) // in "4" socket
	ROM_LOAD64_WORD( "st-1.7a",   0x200000, 0x80000, CRC(005f000b) SHA1(e6f65af7cc3295be9efaaded352e7ae6320b4133) ) // in "5" socket
	ROM_LOAD64_WORD( "st-10.9a",  0x200002, 0x80000, CRC(b9441519) SHA1(bb0926dc484dae4f64c5e5a6bce20afdc7aeba55) ) // in "7" socket
	ROM_LOAD64_WORD( "st-4.3a",   0x200004, 0x80000, CRC(b7d04e8b) SHA1(5c5a079baa694927c33d0e0c23e5ff09d6c9d985) ) // in "1" socket
	ROM_LOAD64_WORD( "st-8.5a",   0x200006, 0x80000, CRC(6b4713b4) SHA1(759b8b1fc7a5c4b00d74a27c2dd11667db44b09e) ) // in "3" socket

	ROM_REGION( 0x8000, "stars", 0 )
	ROM_COPY( "gfx", 0x000000, 0x000000, 0x8000 )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "09.12b",     0x00000, 0x08000, CRC(2ed403bc) SHA1(4ce863ea40d789db5a7cfce91d2c7c720deb9be5) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "18.11c",     0x00000, 0x20000, CRC(4386bc80) SHA1(fb2b261995aeacfa13e7ee40b1a973dfb178f015) )
	ROM_LOAD( "19.12c",     0x20000, 0x20000, CRC(444536d7) SHA1(a14f5de2f6b5b29ae5161dca1f8c08c566301a91) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "st24m1.1a",    0x0000, 0x0117, CRC(a80d357e) SHA1(4cb79c99c62c8300e694f4cd26f41dab7818f17f) )
	ROM_LOAD( "lwio.11e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( striderua )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "30.11f", 0x00000, 0x20000, CRC(66aec273) SHA1(576b1e9062874e68d68f8725949c151509eb6d56) )    /* different CRC from strider, pcb verified */
	ROM_LOAD16_BYTE( "35.11h", 0x00001, 0x20000, CRC(50e0e865) SHA1(201ef385c228c124ed9412002233a501ea514efd) )    /* different CRC from strider, pcb verified */
	ROM_LOAD16_BYTE( "31.12f", 0x40000, 0x20000, CRC(eae93bd1) SHA1(b320a00b67ea3c7fffc6c37d57863163975f7b80) )    /* different CRC from strider, pcb verified */
	ROM_LOAD16_BYTE( "36.12h", 0x40001, 0x20000, CRC(b904a31d) SHA1(5509d1024151eb8548fd1b29e6c0c95775c61364) )    /* different CRC from strider, pcb verified */
	ROM_LOAD16_WORD_SWAP( "st-14.8h", 0x80000, 0x80000, CRC(9b3cfc08) SHA1(a7d7f270a097437affa845d80bed82a1fa874878) )    // in "32" socket

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "st-2.8a",   0x000000, 0x80000, CRC(4eee9aea) SHA1(5e619fd5f3f1181e32a8fd9dbb4661d74ff8a484) ) // in "6" socket
	ROM_LOAD64_WORD( "st-11.10a", 0x000002, 0x80000, CRC(2d7f21e4) SHA1(593cec513de40ff802084d54313bb25a4561e25d) ) // in "8" socket
	ROM_LOAD64_WORD( "st-5.4a",   0x000004, 0x80000, CRC(7705aa46) SHA1(6cbfa30b2852fd117d117beefba434ce41d24c2f) ) // in "2" socket
	ROM_LOAD64_WORD( "st-9.6a",   0x000006, 0x80000, CRC(5b18b722) SHA1(cf71c62348ca6b404279e87a6686cb3a842eb381) ) // in "4" socket
	ROM_LOAD64_WORD( "st-1.7a",   0x200000, 0x80000, CRC(005f000b) SHA1(e6f65af7cc3295be9efaaded352e7ae6320b4133) ) // in "5" socket
	ROM_LOAD64_WORD( "st-10.9a",  0x200002, 0x80000, CRC(b9441519) SHA1(bb0926dc484dae4f64c5e5a6bce20afdc7aeba55) ) // in "7" socket
	ROM_LOAD64_WORD( "st-4.3a",   0x200004, 0x80000, CRC(b7d04e8b) SHA1(5c5a079baa694927c33d0e0c23e5ff09d6c9d985) ) // in "1" socket
	ROM_LOAD64_WORD( "st-8.5a",   0x200006, 0x80000, CRC(6b4713b4) SHA1(759b8b1fc7a5c4b00d74a27c2dd11667db44b09e) ) // in "3" socket

	ROM_REGION( 0x8000, "stars", 0 )
	ROM_COPY( "gfx", 0x000000, 0x000000, 0x8000 )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "09.12b", 0x00000, 0x08000, CRC(08d63519) SHA1(c120ecfe25c3c50bc51bc7d5a9ef1c8ca6591240) )   /* different CRC from strider, pcb verified */
	ROM_CONTINUE(       0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "18.11c",     0x00000, 0x20000, CRC(4386bc80) SHA1(fb2b261995aeacfa13e7ee40b1a973dfb178f015) )
	ROM_LOAD( "19.12c",     0x20000, 0x20000, CRC(444536d7) SHA1(a14f5de2f6b5b29ae5161dca1f8c08c566301a91) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "st24b2.1a",    0x0000, 0x0117, NO_DUMP )
	ROM_LOAD( "lwio.11e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

// 90629B-3 PCB - Converted from an SF2 'inhouse' (but with no official labels etc.)
// actually an incomplete conversion, layer enables are wrong in a number of places which causes the game to break even on real hardware.
// this is essentially a Capcom made bootleg of a Capcom game.
ROM_START( strideruc )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "34.8f", 0x000000, 0x80000, CRC(e0fb5657) SHA1(6c7f668220de80169feea35371aff363f67f7b0c) )
	ROM_LOAD16_WORD_SWAP( "33.6f", 0x080000, 0x80000, CRC(9b3cfc08) SHA1(a7d7f270a097437affa845d80bed82a1fa874878) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "6.8a",   0x000000, 0x80000, CRC(4eee9aea) SHA1(5e619fd5f3f1181e32a8fd9dbb4661d74ff8a484) )
	ROM_LOAD64_WORD( "8.10a",  0x000002, 0x80000, CRC(2d7f21e4) SHA1(593cec513de40ff802084d54313bb25a4561e25d) )
	ROM_LOAD64_WORD( "5.7a",   0x000004, 0x80000, CRC(7705aa46) SHA1(6cbfa30b2852fd117d117beefba434ce41d24c2f) )
	ROM_LOAD64_WORD( "7.9a",   0x000006, 0x80000, CRC(5b18b722) SHA1(cf71c62348ca6b404279e87a6686cb3a842eb381) )
	ROM_LOAD64_WORD( "15.8c",  0x200000, 0x80000, CRC(005f000b) SHA1(e6f65af7cc3295be9efaaded352e7ae6320b4133) )
	ROM_LOAD64_WORD( "17.10c", 0x200002, 0x80000, CRC(b9441519) SHA1(bb0926dc484dae4f64c5e5a6bce20afdc7aeba55) )
	ROM_LOAD64_WORD( "14.7c",  0x200004, 0x80000, CRC(b7d04e8b) SHA1(5c5a079baa694927c33d0e0c23e5ff09d6c9d985) )
	ROM_LOAD64_WORD( "16.9c",  0x200006, 0x80000, CRC(6b4713b4) SHA1(759b8b1fc7a5c4b00d74a27c2dd11667db44b09e) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "9.12a",  0x00000, 0x08000, CRC(08d63519) SHA1(c120ecfe25c3c50bc51bc7d5a9ef1c8ca6591240) )
	ROM_CONTINUE(       0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "18.11c",  0x00000, 0x20000, CRC(4386bc80) SHA1(fb2b261995aeacfa13e7ee40b1a973dfb178f015) )
	ROM_LOAD( "19.12c",  0x20000, 0x20000, CRC(444536d7) SHA1(a14f5de2f6b5b29ae5161dca1f8c08c566301a91) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "unknown.1a",   0x0000, 0x0117, NO_DUMP ) // unknown custom pal or gal
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 88622B-3 */
ROM_START( striderj )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sth_36.12f", 0x00000, 0x20000, CRC(53c7b006) SHA1(30daa256a32b209b907e5d916a82068017862a01) )
	ROM_LOAD16_BYTE( "sth_42.12h", 0x00001, 0x20000, CRC(4037f65f) SHA1(490b9fb15f80772316101ea15e61ab32f42feaec) )
	ROM_LOAD16_BYTE( "sth_37.13f", 0x40000, 0x20000, CRC(80e8877d) SHA1(806a62c03007efe6d58fb24dac467d4fc39bb96a) )
	ROM_LOAD16_BYTE( "sth_43.13h", 0x40001, 0x20000, CRC(6b3fa466) SHA1(6a3c9bd491eecf864971f7fdf02d01112d5ef7dd) )
	ROM_LOAD16_BYTE( "sth_34.10f", 0x80000, 0x20000, CRC(bea770b5) SHA1(b1d3111c8878708b6d0589d6bdfd3b380842d98b) ) // == st-14.8h
	ROM_LOAD16_BYTE( "sth_40.10h", 0x80001, 0x20000, CRC(43b922dc) SHA1(441c932080ae2b19e3834e7173d46be2e8762119) ) // == st-14.8h
	ROM_LOAD16_BYTE( "sth_35.11f", 0xc0000, 0x20000, CRC(5cc429da) SHA1(1d3593444d556fcb7b209ef254b7733cb32dc502) ) // == st-14.8h
	ROM_LOAD16_BYTE( "sth_41.11h", 0xc0001, 0x20000, CRC(50af457f) SHA1(fb7248e41c09f137c929a2bd9ef17591f48b7009) ) // == st-14.8h

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_BYTE( "sth_09.4b",  0x000000, 0x20000, CRC(1ef6bfbd) SHA1(5e66168e2526b4b4569d9dabacd5602181b23874) )    // == st-2.8a
	ROM_LOAD64_BYTE( "sth_01.4a",  0x000001, 0x20000, CRC(1e21b0c1) SHA1(2ac543dccefa7e2438d7eb53cdf2e6aff09c735b) )    // == st-2.8a
	ROM_LOAD64_BYTE( "sth_13.9b",  0x000002, 0x20000, CRC(063263ae) SHA1(38ea256f9a0f3fd6786d21c8b540030b73742971) )    // == st-11.10a
	ROM_LOAD64_BYTE( "sth_05.9a",  0x000003, 0x20000, CRC(ec9f8714) SHA1(8eb2b92ad576849b8049f9c90b850f74c2a5cba2) )    // == st-11.10a
	ROM_LOAD64_BYTE( "sth_24.5e",  0x000004, 0x20000, CRC(6356f4d2) SHA1(8d6786e07dba3e59a609c1a13be8685e4fdb8879) )    // == st-5.4a
	ROM_LOAD64_BYTE( "sth_17.5c",  0x000005, 0x20000, CRC(b4f73d86) SHA1(0ffbcc93ce7eadfd29255a15bc5cbd6e24f98759) )    // == st-5.4a
	ROM_LOAD64_BYTE( "sth_38.8h",  0x000006, 0x20000, CRC(ee5abfc2) SHA1(1c162bc09991f1082b4d9d22fbce13f2c08e0ceb) )    // == st-9.6a
	ROM_LOAD64_BYTE( "sth_32.8f",  0x000007, 0x20000, CRC(44a206a3) SHA1(67ec4b9cf3ff181c8c4c4751a1d2e8ee8e56278a) )    // == st-9.6a
	ROM_LOAD64_BYTE( "sth_10.5b",  0x100000, 0x20000, CRC(df3dd3bc) SHA1(648218f94ecda25873103ac4e2d7132c79f1c5b2) )    // == st-2.8a
	ROM_LOAD64_BYTE( "sth_02.5a",  0x100001, 0x20000, CRC(c75f9ea0) SHA1(e9268bfa6f5254935fda726ab4b5d9acb0f1942a) )    // == st-2.8a
	ROM_LOAD64_BYTE( "sth_14.10b", 0x100002, 0x20000, CRC(6c03e19d) SHA1(83f892df551ea79534288643b07613a3c595d526) )    // == st-11.10a
	ROM_LOAD64_BYTE( "sth_06.10a", 0x100003, 0x20000, CRC(d84f5478) SHA1(c3812056ff2563a43d11746ec8327700f370a053) )    // == st-11.10a
	ROM_LOAD64_BYTE( "sth_25.7e",  0x100004, 0x20000, CRC(921e506a) SHA1(fd2b5e2121c2adedcb0325b4159273506dab27e8) )    // == st-5.4a
	ROM_LOAD64_BYTE( "sth_18.7c",  0x100005, 0x20000, CRC(5b318956) SHA1(b1415472bf45787219b3b7680057910b210d57f5) )    // == st-5.4a
	ROM_LOAD64_BYTE( "sth_39.9h",  0x100006, 0x20000, CRC(9321d6aa) SHA1(acec3c880d29692cf010540c120b9092d7dd8ce9) )    // == st-9.6a
	ROM_LOAD64_BYTE( "sth_33.9f",  0x100007, 0x20000, CRC(b47ddfc7) SHA1(12388c37abdde85d63305f86244fc3c07f8b6b0c) )    // == st-9.6a
	ROM_LOAD64_BYTE( "sth_11.7b",  0x200000, 0x20000, CRC(2484f241) SHA1(28c48526ec2577119cc3207e92138749124b5959) )    // == st-1.7a
	ROM_LOAD64_BYTE( "sth_03.7a",  0x200001, 0x20000, CRC(aaa07245) SHA1(64a1b75b7613c1949eee6f9ba865dbdd7ec34413) )    // == st-1.7a
	ROM_LOAD64_BYTE( "sth_15.11b", 0x200002, 0x20000, CRC(e415d943) SHA1(12069d02d3a6afa9241222b48420daaf97874271) )    // == st-10.9a
	ROM_LOAD64_BYTE( "sth_07.11a", 0x200003, 0x20000, CRC(97d072d2) SHA1(fb0e10464a878ec6c0f3e6c6ddb0ea542bfb87a8) )    // == st-10.9a
	ROM_LOAD64_BYTE( "sth_26.8e",  0x200004, 0x20000, CRC(0ebfcb02) SHA1(a7238e1c76dbc2de1b7ae0d2cc532170cd1ab6c2) )    // == st-4.3a
	ROM_LOAD64_BYTE( "sth_19.8c",  0x200005, 0x20000, CRC(257ce683) SHA1(762f22b5ba24864d69dda303310a310d8dbfcc1c) )    // == st-4.3a
	ROM_LOAD64_BYTE( "sth_28.10e", 0x200006, 0x20000, CRC(98ac8cd1) SHA1(53dbe418d5cb7af5ef4be91e5e6bcd4474d2fdfe) )    // == st-8.5a
	ROM_LOAD64_BYTE( "sth_21.10c", 0x200007, 0x20000, CRC(538d9423) SHA1(418ea54d6582723d3e568364787862a6df2d1523) )    // == st-8.5a
	ROM_LOAD64_BYTE( "sth_12.8b",  0x300000, 0x20000, CRC(f670a477) SHA1(de5154ca093a9e5f9adb836d9a933d14e939180d) )    // == st-1.7a
	ROM_LOAD64_BYTE( "sth_04.8a",  0x300001, 0x20000, CRC(853d3e01) SHA1(422cc9f539e79c2a9b3bda47eb1fc714d79838d1) )    // == st-1.7a
	ROM_LOAD64_BYTE( "sth_16.12b", 0x300002, 0x20000, CRC(4092019f) SHA1(2173e72a8325d12da70666bdc279409b23fb7024) )    // == st-10.9a
	ROM_LOAD64_BYTE( "sth_08.12a", 0x300003, 0x20000, CRC(2ce9b4c7) SHA1(f267d323c9310433852e3308b36100440bee33d7) )    // == st-10.9a
	ROM_LOAD64_BYTE( "sth_27.9e",  0x300004, 0x20000, CRC(f82c88d9) SHA1(200bd025800eb20c4a15af17e7c3effbfa6f00fa) )    // == st-4.3a
	ROM_LOAD64_BYTE( "sth_20.9c",  0x300005, 0x20000, CRC(eb584dd4) SHA1(aeee39c0fc9f234249253b14de88a8da494b18d0) )    // == st-4.3a
	ROM_LOAD64_BYTE( "sth_29.11e", 0x300006, 0x20000, CRC(34ae2997) SHA1(9449eb9c85b7cb4a12aa06cb65a9d849a528e633) )    // == st-8.5a
	ROM_LOAD64_BYTE( "sth_22.11c", 0x300007, 0x20000, CRC(78dd9c48) SHA1(35fbf3ca21f56c9899283ba08c89c0faf7a8f717) )    // == st-8.5a

	ROM_REGION( 0x8000, "stars", 0 )
	ROM_COPY( "gfx", 0x000000, 0x000000, 0x8000 )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sth_23.13c",  0x00000, 0x08000, CRC(2ed403bc) SHA1(4ce863ea40d789db5a7cfce91d2c7c720deb9be5) )   // == 09.12b
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sth_30.12e",  0x00000, 0x20000, CRC(4386bc80) SHA1(fb2b261995aeacfa13e7ee40b1a973dfb178f015) )   // == 18.11c
	ROM_LOAD( "sth_31.13e",  0x20000, 0x20000, CRC(444536d7) SHA1(a14f5de2f6b5b29ae5161dca1f8c08c566301a91) )   // == 19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "st22b.1a",     0x0000, 0x0117, CRC(68fecc55) SHA1(238f6781f972e834f2c6054cfe925ffec579dec3) )
	ROM_LOAD( "lwio.12c",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 91634B-2, Japan Resale Ver. */
ROM_START( striderjr )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "sthj_23.8f", 0x00000, 0x80000, CRC(046e7b12) SHA1(a5761f730f6844a7e93556a6aeae76240a99540c) )
	ROM_LOAD16_WORD_SWAP( "sthj_22.7f", 0x80000, 0x80000, CRC(9b3cfc08) SHA1(a7d7f270a097437affa845d80bed82a1fa874878) )    // == st-14.8h

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "sth_01.3a",  0x000000, 0x80000, CRC(4eee9aea) SHA1(5e619fd5f3f1181e32a8fd9dbb4661d74ff8a484) )    // == st-2.8a
	ROM_LOAD64_WORD( "sth_02.4a",  0x000002, 0x80000, CRC(2d7f21e4) SHA1(593cec513de40ff802084d54313bb25a4561e25d) )    // == st-11.10a
	ROM_LOAD64_WORD( "sth_03.5a",  0x000004, 0x80000, CRC(7705aa46) SHA1(6cbfa30b2852fd117d117beefba434ce41d24c2f) )    // == st-5.4a
	ROM_LOAD64_WORD( "sth_04.6a",  0x000006, 0x80000, CRC(5b18b722) SHA1(cf71c62348ca6b404279e87a6686cb3a842eb381) )    // == st-9.6a
	ROM_LOAD64_WORD( "sth_05.7a",  0x200000, 0x80000, CRC(005f000b) SHA1(e6f65af7cc3295be9efaaded352e7ae6320b4133) )    // == st-1.7a
	ROM_LOAD64_WORD( "sth_06.8a",  0x200002, 0x80000, CRC(b9441519) SHA1(bb0926dc484dae4f64c5e5a6bce20afdc7aeba55) )    // == st-10.9a
	ROM_LOAD64_WORD( "sth_07.9a",  0x200004, 0x80000, CRC(b7d04e8b) SHA1(5c5a079baa694927c33d0e0c23e5ff09d6c9d985) )    // == st-4.3a
	ROM_LOAD64_WORD( "sth_08.10a", 0x200006, 0x80000, CRC(6b4713b4) SHA1(759b8b1fc7a5c4b00d74a27c2dd11667db44b09e) )    // == st-8.5a

	ROM_REGION( 0x8000, "stars", 0 )
	ROM_COPY( "gfx", 0x000000, 0x000000, 0x8000 )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sth_09.12a",  0x00000, 0x08000, CRC(08d63519) SHA1(c120ecfe25c3c50bc51bc7d5a9ef1c8ca6591240) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sth_18.11c",  0x00000, 0x20000, CRC(4386bc80) SHA1(fb2b261995aeacfa13e7ee40b1a973dfb178f015) )
	ROM_LOAD( "sth_19.12c",  0x20000, 0x20000, CRC(444536d7) SHA1(a14f5de2f6b5b29ae5161dca1f8c08c566301a91) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "sth63b.1a",    0x0000, 0x0117, BAD_DUMP CRC(c706b773) SHA1(ddfe2e747637eec081a5125cfefcb478a4ba9e76) ) /* Handcrafted but works on actual PCB.  Redump needed */
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 89624B-? */
ROM_START( dynwar )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "30.11f",        0x00000, 0x20000, CRC(f9ec6d68) SHA1(02912db2b48f77489b0b841c2a5414bfb49b93f4) )
	ROM_LOAD16_BYTE( "35.11h",        0x00001, 0x20000, CRC(e41fff2f) SHA1(a960c39c69f97b46d5efcbcd3e2bc652888094c4) )
	ROM_LOAD16_BYTE( "31.12f",        0x40000, 0x20000, CRC(e3de76ff) SHA1(fdc552312e10c91dd00bfa72e4e686ac356d2244) )
	ROM_LOAD16_BYTE( "36.12h",        0x40001, 0x20000, CRC(7a13cfbf) SHA1(c6b4d775a2e507fdefbb895cc75bb5bdb442218d) )
	ROM_LOAD16_WORD_SWAP( "tkm-9.8h", 0x80000, 0x80000, CRC(93654bcf) SHA1(c72daeb2a98d350568555059a3225343c219a1d2) )  // in "32" socket

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "tkm-5.7a",  0x000000, 0x80000, CRC(f64bb6a0) SHA1(8c0ae0305704ad876bb1938e46550c68b0de8e8e) )  // in "5" socket
	ROM_LOAD64_WORD( "tkm-8.9a",  0x000002, 0x80000, CRC(21fe6274) SHA1(f92e509d88d5e264be9c7812966d64ad9ac518e7) )  // in "7" socket
	ROM_LOAD64_WORD( "tkm-6.3a",  0x000004, 0x80000, CRC(0bf228cb) SHA1(e72957155cb459c4dee50df2e53256f271528964) )  // in "1" socket
	ROM_LOAD64_WORD( "tkm-7.5a",  0x000006, 0x80000, CRC(1255dfb1) SHA1(c943e3c989d5b20fbe24e38e54ee8ca294b3d182) )  // in "3" socket
	ROM_LOAD64_WORD( "tkm-1.8a",  0x200000, 0x80000, CRC(44f7661e) SHA1(f29b5ad0c5dfd91a56a4a1084ce578cfe496dd6f) )  // in "6" socket
	ROM_LOAD64_WORD( "tkm-4.10a", 0x200002, 0x80000, CRC(a54c515d) SHA1(bfa457cef7e29ae56ee9b10f60e233d82b4efc61) )  // in "8" socket
	ROM_LOAD64_WORD( "tkm-2.4a",  0x200004, 0x80000, CRC(ca5c687c) SHA1(de47cb5a071ffb3ff408f60d45b79345032232a7) )  // in "2" socket
	ROM_LOAD64_WORD( "tkm-3.6a",  0x200006, 0x80000, CRC(f9fe6591) SHA1(260da5f9e305cccd621b8b5b2073c79e161ddeb0) )  // in "4" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "tke_17.12b", 0x00000, 0x08000, CRC(b3b79d4f) SHA1(2b960545741d3b9a53ffbf3ed83030392aa02698) )    // in "9" socket
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "tke_18.11c", 0x00000, 0x20000, CRC(ac6e307d) SHA1(b490ce625bb7ce0904b0fd121fbfbd5252790f7a) )
	ROM_LOAD( "tke_19.12c", 0x20000, 0x20000, CRC(068741db) SHA1(ab48aff639a7ac218b7d5304145e10e92d61fd9f) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "tk24b1.1a",    0x0000, 0x0117, CRC(ae4a7645) SHA1(593478a177ea1794c9dfff07c6c6d8624a883eae) )
	ROM_LOAD( "lwio.11e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )    // pal verification required
ROM_END

/* B-Board 88622B-3 */
ROM_START( dynwara )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "tke_36.12f", 0x00000, 0x20000, CRC(895991d1) SHA1(56b105b85ccab1c49c89ae8d4aa55c9374077df0) )
	ROM_LOAD16_BYTE( "tke_42.12h", 0x00001, 0x20000, CRC(c898d2e8) SHA1(c8b10685681bf155ea44e30f3cb0574df7d4f984) )
	ROM_LOAD16_BYTE( "tke_37.13f", 0x40000, 0x20000, CRC(b228d58c) SHA1(99a1f42d930f788c4f9b410addc95173fda801a2) )
	ROM_LOAD16_BYTE( "tke_43.13h", 0x40001, 0x20000, CRC(1a14375a) SHA1(2b1a62f7961dceabf98461266da37abfec13aaa9) )
	ROM_LOAD16_BYTE( "34.10f",     0x80000, 0x20000, CRC(8f663d00) SHA1(77811783c87c7aee058b8533e34049a01047258a) ) // == tkm-9.8h
	ROM_LOAD16_BYTE( "40.10h",     0x80001, 0x20000, CRC(1586dbf3) SHA1(d9f03e001effdef021a9ceda512e73a24726fca1) ) // == tkm-9.8h
	ROM_LOAD16_BYTE( "35.11f",     0xc0000, 0x20000, CRC(9db93d7a) SHA1(f75e3fb5273baef0cd5d8eea26f07d5acaa743ca) ) // == tkm-9.8h
	ROM_LOAD16_BYTE( "41.11h",     0xc0001, 0x20000, CRC(1aae69a4) SHA1(56e4761818f7857bc7520f2b8de90eabd857c577) ) // == tkm-9.8h

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_BYTE( "09.4b",     0x000000, 0x20000, CRC(c3e83c69) SHA1(bd361a39dc6428fea8f56ebbe5cdcc4bf63a51f0) ) // == tkm-5.7a
	ROM_LOAD64_BYTE( "01.4a",     0x000001, 0x20000, CRC(187b2886) SHA1(b16121f57926d9fd2c3bc82ae6babf6a2297f140) ) // == tkm-5.7a
	ROM_LOAD64_BYTE( "13.9b",     0x000002, 0x20000, CRC(0273d87d) SHA1(7803b04d72eedb4c8b39f63fea458cfef0034813) ) // == tkm-8.9a
	ROM_LOAD64_BYTE( "05.9a",     0x000003, 0x20000, CRC(339378b8) SHA1(c3dfe7039e4572b9ef56166346f3cbc6f6ab07c1) ) // == tkm-8.9a
	ROM_LOAD64_BYTE( "24.5e",     0x000004, 0x20000, CRC(c6909b6f) SHA1(2828bd6bdc8e3f87a4a37d4e20bdff86cb6850c9) ) // == tkm-6.3a
	ROM_LOAD64_BYTE( "17.5c",     0x000005, 0x20000, CRC(2e2f8320) SHA1(7bcb80447d9ce7cc9a38e2506196acd6bf50b49f) ) // == tkm-6.3a
	ROM_LOAD64_BYTE( "38.8h",     0x000006, 0x20000, CRC(cd7923ed) SHA1(29187b99847a4b56f2f1763d086b8e7dc5cebed7) ) // == tkm-7.5a
	ROM_LOAD64_BYTE( "32.8f",     0x000007, 0x20000, CRC(21a0a453) SHA1(ace38c5943f9f744212cfdb7caa2caa43312e82c) ) // == tkm-7.5a
	ROM_LOAD64_BYTE( "10.5b",     0x100000, 0x20000, CRC(ff28f8d0) SHA1(c8c4851816f17a4a0494164f5e8cc910f16669e8) ) // == tkm-5.7a
	ROM_LOAD64_BYTE( "02.5a",     0x100001, 0x20000, CRC(cc83c02f) SHA1(915e9d7acec1ba7a2035ae140f576839eba8694f) ) // == tkm-5.7a
	ROM_LOAD64_BYTE( "14.10b",    0x100002, 0x20000, CRC(58d9b32f) SHA1(c13a12afcb83159b284b95053951dfa1841bb612) ) // == tkm-8.9a
	ROM_LOAD64_BYTE( "06.10a",    0x100003, 0x20000, CRC(6f9edd75) SHA1(e8d43c0ec2165e88aefbb5c92048fbcd06fe578b) ) // == tkm-8.9a
	ROM_LOAD64_BYTE( "25.7e",     0x100004, 0x20000, CRC(152ea74a) SHA1(c0c56b1bdfa0d7fdea040dbcc6ff871e5957a5b6) ) // == tkm-6.3a
	ROM_LOAD64_BYTE( "18.7c",     0x100005, 0x20000, CRC(1833f932) SHA1(81f94d26bdb6758736ca02d7b1772801be4da181) ) // == tkm-6.3a
	ROM_LOAD64_BYTE( "39.9h",     0x100006, 0x20000, CRC(bc09b360) SHA1(de2c9a42490db79c8e5fe57b9107f1adbe5dd241) ) // == tkm-7.5a
	ROM_LOAD64_BYTE( "33.9f",     0x100007, 0x20000, CRC(89de1533) SHA1(e48312e37c0f98faeec91546acde5daf0da8f6b3) ) // == tkm-7.5a
	ROM_LOAD64_BYTE( "11.7b",     0x200000, 0x20000, CRC(29eaf490) SHA1(42fcb67c7014e0ad62cde9e77c79e61268647528) ) // == tkm-1.8a
	ROM_LOAD64_BYTE( "03.7a",     0x200001, 0x20000, CRC(7bf51337) SHA1(c21938029641ebcbc484680cf8a57186cdde220f) ) // == tkm-1.8a
	ROM_LOAD64_BYTE( "15.11b",    0x200002, 0x20000, CRC(d36cdb91) SHA1(66ab873ce285e857f30294dd1c9b1dda0c6c6b76) ) // == tkm-4.10a
	ROM_LOAD64_BYTE( "07.11a",    0x200003, 0x20000, CRC(e04af054) SHA1(f227b8a0a3d8f41e1922d184eaec7a1243c7c3af) ) // == tkm-4.10a
	ROM_LOAD64_BYTE( "26.8e",     0x200004, 0x20000, CRC(07fc714b) SHA1(eda97a3c5596ebdfa61bdd01d39647c89b9a2f13) ) // == tkm-2.4a
	ROM_LOAD64_BYTE( "19.8c",     0x200005, 0x20000, CRC(7114e5c6) SHA1(2f2925b942af50781857f4fe74e9a58f2cf7b883) ) // == tkm-2.4a
	ROM_LOAD64_BYTE( "28.10e",    0x200006, 0x20000, CRC(af62bf07) SHA1(a6e0f598de1fa8a4960e89d655b7514572ed6310) ) // == tkm-3.6a
	ROM_LOAD64_BYTE( "21.10c",    0x200007, 0x20000, CRC(523f462a) SHA1(b0fc9e29d6ca44aafb20a62355bde9f4b4cf1e43) ) // == tkm-3.6a
	ROM_LOAD64_BYTE( "12.8b",     0x300000, 0x20000, CRC(38652339) SHA1(930a035bbe34c81c26d774d2ab45f53a3a9205fb) ) // == tkm-1.8a
	ROM_LOAD64_BYTE( "04.8a",     0x300001, 0x20000, CRC(4951bc0f) SHA1(07f424c147d787321b668d787216733c35e2cff9) ) // == tkm-1.8a
	ROM_LOAD64_BYTE( "16.12b",    0x300002, 0x20000, CRC(381608ae) SHA1(666e15e61c7c59df5a97bdc2d77db611d60b3ca8) ) // == tkm-4.10a
	ROM_LOAD64_BYTE( "08.12a",    0x300003, 0x20000, CRC(b475d4e9) SHA1(dc5d223bc2a27904e6e38b68507d2e87fbbde158) ) // == tkm-4.10a
	ROM_LOAD64_BYTE( "27.9e",     0x300004, 0x20000, CRC(a27e81fa) SHA1(b25854d4a7e52d500c19445badb4cfe745d88d23) ) // == tkm-2.4a
	ROM_LOAD64_BYTE( "20.9c",     0x300005, 0x20000, CRC(002796dc) SHA1(2dba0434916dd82c59a66e2f3ce8d3165713c308) ) // == tkm-2.4a
	ROM_LOAD64_BYTE( "29.11e",    0x300006, 0x20000, CRC(6b41f82d) SHA1(111af606d8107d377e3af618584a75ed6cfc9bbd) ) // == tkm-3.6a
	ROM_LOAD64_BYTE( "22.11c",    0x300007, 0x20000, CRC(52145369) SHA1(bd422f0c51cdd62b69229f926569ad05d430bd57) ) // == tkm-3.6a

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "23.13c",     0x00000, 0x08000, CRC(b3b79d4f) SHA1(2b960545741d3b9a53ffbf3ed83030392aa02698) )    // == tke_17.12b
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "tke_30.12e", 0x00000, 0x20000, CRC(ac6e307d) SHA1(b490ce625bb7ce0904b0fd121fbfbd5252790f7a) )    // == tke_18.11c
	ROM_LOAD( "tke_31.13e", 0x20000, 0x20000, CRC(068741db) SHA1(ab48aff639a7ac218b7d5304145e10e92d61fd9f) )    // == tke_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "tk22b.1a",     0x0000, 0x0117, CRC(1a1ab6d7) SHA1(eacd3a21a09683e1d8ad36215c1f8ea96a1f7c12) )
	ROM_LOAD( "lwio.12c",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 88622B-3 */
ROM_START( dynwarj )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "36.12f", 0x00000, 0x20000, CRC(1a516657) SHA1(f5c7c3bfd482eb59221cfd3eec4d47e717b04efa) )
	ROM_LOAD16_BYTE( "42.12h", 0x00001, 0x20000, CRC(12a290a0) SHA1(29fd3f77c497ef8db48121301beab2862ca380b4) )
	ROM_LOAD16_BYTE( "37.13f", 0x40000, 0x20000, CRC(932fc943) SHA1(1bd1c696072e61db791c075fae8936dece73d1d8) )
	ROM_LOAD16_BYTE( "43.13h", 0x40001, 0x20000, CRC(872ad76d) SHA1(77cfb380dd358eb9e65894a026e0718918c5b68f) )
	ROM_LOAD16_BYTE( "34.10f", 0x80000, 0x20000, CRC(8f663d00) SHA1(77811783c87c7aee058b8533e34049a01047258a) ) // == tkm-9.8h
	ROM_LOAD16_BYTE( "40.10h", 0x80001, 0x20000, CRC(1586dbf3) SHA1(d9f03e001effdef021a9ceda512e73a24726fca1) ) // == tkm-9.8h
	ROM_LOAD16_BYTE( "35.11f", 0xc0000, 0x20000, CRC(9db93d7a) SHA1(f75e3fb5273baef0cd5d8eea26f07d5acaa743ca) ) // == tkm-9.8h
	ROM_LOAD16_BYTE( "41.11h", 0xc0001, 0x20000, CRC(1aae69a4) SHA1(56e4761818f7857bc7520f2b8de90eabd857c577) ) // == tkm-9.8h

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_BYTE( "09.4b",             0x000000, 0x20000, CRC(c3e83c69) SHA1(bd361a39dc6428fea8f56ebbe5cdcc4bf63a51f0) ) // == tkm-5.7a
	ROM_LOAD64_BYTE( "01.4a",             0x000001, 0x20000, CRC(187b2886) SHA1(b16121f57926d9fd2c3bc82ae6babf6a2297f140) ) // == tkm-5.7a
	ROM_LOAD64_BYTE( "13.9b",             0x000002, 0x20000, CRC(0273d87d) SHA1(7803b04d72eedb4c8b39f63fea458cfef0034813) ) // == tkm-8.9a
	ROM_LOAD64_BYTE( "05.9a",             0x000003, 0x20000, CRC(339378b8) SHA1(c3dfe7039e4572b9ef56166346f3cbc6f6ab07c1) ) // == tkm-8.9a
	ROM_LOAD64_BYTE( "24.5e",             0x000004, 0x20000, CRC(c6909b6f) SHA1(2828bd6bdc8e3f87a4a37d4e20bdff86cb6850c9) ) // == tkm-6.3a
	ROM_LOAD64_BYTE( "17.5c",             0x000005, 0x20000, CRC(2e2f8320) SHA1(7bcb80447d9ce7cc9a38e2506196acd6bf50b49f) ) // == tkm-6.3a
	ROM_LOAD64_BYTE( "38.8h",             0x000006, 0x20000, CRC(cd7923ed) SHA1(29187b99847a4b56f2f1763d086b8e7dc5cebed7) ) // == tkm-7.5a
	ROM_LOAD64_BYTE( "32.8f",             0x000007, 0x20000, CRC(21a0a453) SHA1(ace38c5943f9f744212cfdb7caa2caa43312e82c) ) // == tkm-7.5a
	ROM_LOAD64_BYTE( "10.5b",             0x100000, 0x20000, CRC(ff28f8d0) SHA1(c8c4851816f17a4a0494164f5e8cc910f16669e8) ) // == tkm-5.7a
	ROM_LOAD64_BYTE( "02.5a",             0x100001, 0x20000, CRC(cc83c02f) SHA1(915e9d7acec1ba7a2035ae140f576839eba8694f) ) // == tkm-5.7a
	ROM_LOAD64_BYTE( "14.10b",            0x100002, 0x20000, CRC(18fb232c) SHA1(c690ca668a56c756c04ef5db4900eb3fd34897e7) ) /* 1 byte different from dynwara, pcb verified */
	ROM_LOAD64_BYTE( "06.10a",            0x100003, 0x20000, CRC(6f9edd75) SHA1(e8d43c0ec2165e88aefbb5c92048fbcd06fe578b) ) // == tkm-8.9a
	ROM_LOAD64_BYTE( "25.7e",             0x100004, 0x20000, CRC(152ea74a) SHA1(c0c56b1bdfa0d7fdea040dbcc6ff871e5957a5b6) ) // == tkm-6.3a
	ROM_LOAD64_BYTE( "18.7c",             0x100005, 0x20000, CRC(1833f932) SHA1(81f94d26bdb6758736ca02d7b1772801be4da181) ) // == tkm-6.3a
	ROM_LOAD64_BYTE( "39.9h",             0x100006, 0x20000, CRC(bc09b360) SHA1(de2c9a42490db79c8e5fe57b9107f1adbe5dd241) ) // == tkm-7.5a
	ROM_LOAD64_BYTE( "33.9f",             0x100007, 0x20000, CRC(89de1533) SHA1(e48312e37c0f98faeec91546acde5daf0da8f6b3) ) // == tkm-7.5a
	ROM_LOAD64_BYTE( "11.7b",             0x200000, 0x20000, CRC(29eaf490) SHA1(42fcb67c7014e0ad62cde9e77c79e61268647528) ) // == tkm-1.8a
	ROM_LOAD64_BYTE( "03.7a",             0x200001, 0x20000, CRC(7bf51337) SHA1(c21938029641ebcbc484680cf8a57186cdde220f) ) // == tkm-1.8a
	ROM_LOAD64_BYTE( "15.11b",            0x200002, 0x20000, CRC(d36cdb91) SHA1(66ab873ce285e857f30294dd1c9b1dda0c6c6b76) ) // == tkm-4.10a
	ROM_LOAD64_BYTE( "07.11a",            0x200003, 0x20000, CRC(e04af054) SHA1(f227b8a0a3d8f41e1922d184eaec7a1243c7c3af) ) // == tkm-4.10a
	ROM_LOAD64_BYTE( "26.8e",             0x200004, 0x20000, CRC(07fc714b) SHA1(eda97a3c5596ebdfa61bdd01d39647c89b9a2f13) ) // == tkm-2.4a
	ROM_LOAD64_BYTE( "19.8c",             0x200005, 0x20000, CRC(7114e5c6) SHA1(2f2925b942af50781857f4fe74e9a58f2cf7b883) ) // == tkm-2.4a
	ROM_LOAD64_BYTE( "28.10e",            0x200006, 0x20000, CRC(af62bf07) SHA1(a6e0f598de1fa8a4960e89d655b7514572ed6310) ) // == tkm-3.6a
	ROM_LOAD64_BYTE( "21.10c",            0x200007, 0x20000, CRC(523f462a) SHA1(b0fc9e29d6ca44aafb20a62355bde9f4b4cf1e43) ) // == tkm-3.6a
	ROM_LOAD64_BYTE( "12.8b",             0x300000, 0x20000, CRC(38652339) SHA1(930a035bbe34c81c26d774d2ab45f53a3a9205fb) ) // == tkm-1.8a
	ROM_LOAD64_BYTE( "04.8a",             0x300001, 0x20000, CRC(4951bc0f) SHA1(07f424c147d787321b668d787216733c35e2cff9) ) // == tkm-1.8a
	ROM_LOAD64_BYTE( "16.12b",            0x300002, 0x20000, CRC(381608ae) SHA1(666e15e61c7c59df5a97bdc2d77db611d60b3ca8) ) // == tkm-4.10a
	ROM_LOAD64_BYTE( "08.12a",            0x300003, 0x20000, CRC(b475d4e9) SHA1(dc5d223bc2a27904e6e38b68507d2e87fbbde158) ) // == tkm-4.10a
	ROM_LOAD64_BYTE( "27.9e",             0x300004, 0x20000, CRC(a27e81fa) SHA1(b25854d4a7e52d500c19445badb4cfe745d88d23) ) // == tkm-2.4a
	ROM_LOAD64_BYTE( "20.9c",             0x300005, 0x20000, CRC(002796dc) SHA1(2dba0434916dd82c59a66e2f3ce8d3165713c308) ) // == tkm-2.4a
	ROM_LOAD64_BYTE( "29.11e",            0x300006, 0x20000, CRC(6b41f82d) SHA1(111af606d8107d377e3af618584a75ed6cfc9bbd) ) // == tkm-3.6a
	ROM_LOAD64_BYTE( "22.11c",            0x300007, 0x20000, CRC(52145369) SHA1(bd422f0c51cdd62b69229f926569ad05d430bd57) ) // == tkm-3.6a

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "23.13c",             0x00000, 0x08000, CRC(b3b79d4f) SHA1(2b960545741d3b9a53ffbf3ed83030392aa02698) )    // == tke_17.12b
	ROM_CONTINUE(                   0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "30.12e",             0x00000, 0x20000, CRC(7e5f6cb4) SHA1(c7b6b7d6dfe5f9f0e1521e7ce990229f480cf68d) )
	ROM_LOAD( "31.13e",             0x20000, 0x20000, CRC(4a30c737) SHA1(426eb90f2edf73eb468c94b4a094da3d46acbab2) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "tk22b.1a",     0x0000, 0x0117, CRC(1a1ab6d7) SHA1(eacd3a21a09683e1d8ad36215c1f8ea96a1f7c12) )
	ROM_LOAD( "lwio.12c",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 91634B-2, Japan Resale Ver. */
ROM_START( dynwarjr )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "tk1j_23.8f", 0x00000, 0x80000, CRC(088a3009) SHA1(d4c8273d19291d278d2ff895712dfbd8dfda6c84) )
	ROM_LOAD16_WORD_SWAP( "tk1j_22.7f", 0x80000, 0x80000, CRC(93654bcf) SHA1(c72daeb2a98d350568555059a3225343c219a1d2) )    // == tkm-9.8h

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "tk1_01.3a",  0x000000, 0x80000, CRC(f64bb6a0) SHA1(8c0ae0305704ad876bb1938e46550c68b0de8e8e) )    // == tkm-5.7a
	ROM_LOAD64_WORD( "tk1_02.4a",  0x000002, 0x80000, CRC(21fe6274) SHA1(f92e509d88d5e264be9c7812966d64ad9ac518e7) )    // == tkm-8.9a
	ROM_LOAD64_WORD( "tk1_03.5a",  0x000004, 0x80000, CRC(0bf228cb) SHA1(e72957155cb459c4dee50df2e53256f271528964) )    // == tkm-6.3a
	ROM_LOAD64_WORD( "tk1_04.6a",  0x000006, 0x80000, CRC(1255dfb1) SHA1(c943e3c989d5b20fbe24e38e54ee8ca294b3d182) )    // == tkm-7.5a
	ROM_LOAD64_WORD( "tk1_05.7a",  0x200000, 0x80000, CRC(44f7661e) SHA1(f29b5ad0c5dfd91a56a4a1084ce578cfe496dd6f) )    // == tkm-1.8a
	ROM_LOAD64_WORD( "tk1_06.8a",  0x200002, 0x80000, CRC(a54c515d) SHA1(bfa457cef7e29ae56ee9b10f60e233d82b4efc61) )    // == tkm-4.10a
	ROM_LOAD64_WORD( "tk1_07.9a",  0x200004, 0x80000, CRC(ca5c687c) SHA1(de47cb5a071ffb3ff408f60d45b79345032232a7) )    // == tkm-2.4a
	ROM_LOAD64_WORD( "tk1_08.10a", 0x200006, 0x80000, CRC(f9fe6591) SHA1(260da5f9e305cccd621b8b5b2073c79e161ddeb0) )    // == tkm-3.6a

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "tk1_09.12a",  0x00000, 0x08000, CRC(db77d899) SHA1(314a8134ed15f603f1c7894fd50b14aef5c9fc7a) )
	ROM_CONTINUE(            0x10000, 0x18000 ) // second half of ROM is empty, not mapped in memory

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "tk1_18.11c",  0x00000, 0x20000, CRC(7e5f6cb4) SHA1(c7b6b7d6dfe5f9f0e1521e7ce990229f480cf68d) )
	ROM_LOAD( "tk1_19.12c",  0x20000, 0x20000, CRC(4a30c737) SHA1(426eb90f2edf73eb468c94b4a094da3d46acbab2) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "tk163b.1a",    0x0000, 0x0117, NO_DUMP )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( willow )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "wle_30.11f",     0x00000, 0x20000, CRC(15372aa2) SHA1(ba8e1984180b0438255dfc68dc4eb560f3ecbe56) )
	ROM_LOAD16_BYTE( "wle_35.11h",     0x00001, 0x20000, CRC(2e64623b) SHA1(473f6fd10b2456553f1cbf92fd9a61ce94b1c59f) )
	ROM_LOAD16_BYTE( "wlu_31.12f",     0x40000, 0x20000, CRC(0eb48a83) SHA1(28c40c4b5d767f88922cd899e948abf11a85a864) )
	ROM_LOAD16_BYTE( "wlu_36.12h",     0x40001, 0x20000, CRC(36100209) SHA1(63c9338e71dba8b52daffba50b4bca31aaa10d9e) )
	ROM_LOAD16_WORD_SWAP( "wlm-32.8h", 0x80000, 0x80000, CRC(dfd9f643) SHA1(9c760c30af593a87e7fd39fb213a4c73c68ca440) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "wlm-7.7a", 0x000000, 0x80000, CRC(afa74b73) SHA1(09081926260c76986a13ac5351dddd2ea11d7a10) )  // in "5" socket
	ROM_LOAD64_WORD( "wlm-5.9a", 0x000002, 0x80000, CRC(12a0dc0b) SHA1(fea235ce9489f04919daf52f4d3f3bac9b558316) )  // in "7" socket
	ROM_LOAD64_WORD( "wlm-3.3a", 0x000004, 0x80000, CRC(c6f2abce) SHA1(ff5fcfe417c43b4747bbe12db6052fdb60f5f0e4) )  // in "1" socket
	ROM_LOAD64_WORD( "wlm-1.5a", 0x000006, 0x80000, CRC(4aa4c6d3) SHA1(7dd6f18f6126c380821a2ca8955439fd6864f4c6) )  // in "3" socket
	ROM_LOAD64_BYTE( "wl_24.7d", 0x200000, 0x20000, CRC(6f0adee5) SHA1(07b18e51b376001f25173b78e0e816f252400210) )
	ROM_LOAD64_BYTE( "wl_14.7c", 0x200001, 0x20000, CRC(9cf3027d) SHA1(1e8eb20d51a54f6f756c0ab9395ac38b96e67fb2) )
	ROM_LOAD64_BYTE( "wl_26.9d", 0x200002, 0x20000, CRC(f09c8ecf) SHA1(b39f83e80af010d6481693d9ec8b1d7e258b531d) )
	ROM_LOAD64_BYTE( "wl_16.9c", 0x200003, 0x20000, CRC(e35407aa) SHA1(7ddae9cef96839da72488c1fe73268c50e0262ff) )
	ROM_LOAD64_BYTE( "wl_20.3d", 0x200004, 0x20000, CRC(84992350) SHA1(f0ebd810ce099337cda94222dccce8ab9b3c3281) )
	ROM_LOAD64_BYTE( "wl_10.3c", 0x200005, 0x20000, CRC(b87b5a36) SHA1(25fb8f9698142473233ee509d4146089920e94e1) )
	ROM_LOAD64_BYTE( "wl_22.5d", 0x200006, 0x20000, CRC(fd3f89f0) SHA1(51ff95cff56ac78682ea56401b35a0aa63cef8cb) )
	ROM_LOAD64_BYTE( "wl_12.5c", 0x200007, 0x20000, CRC(7da49d69) SHA1(b0ae7ac4f858ee8d72e6877c4275da7a631e2e4c) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "wl_09.12b", 0x00000, 0x08000, CRC(f6b3d060) SHA1(0ed2e2f64ba53ba2c371b66ab1e52e40b16d8baf) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "wl_18.11c", 0x00000, 0x20000, CRC(bde23d4d) SHA1(d1fee2f99c858dfb07edcd600da491c7b656afe0) )
	ROM_LOAD( "wl_19.12c", 0x20000, 0x20000, CRC(683898f5) SHA1(316a77b663d78c8b9ff6d85756cb05aaaeef4003) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "wl24b.1a",     0x0000, 0x0117, CRC(7101cdf1) SHA1(c848f109d09641b3159dbbb2d2ee49cf30bc9e9c) )
	ROM_LOAD( "lwio.11e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( willowu )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "wlu_30.11f",     0x00000, 0x20000, CRC(d604dbb1) SHA1(b5d78871011ff11a67f1a0cad147cd4de8d67f35) )
	ROM_LOAD16_BYTE( "35.11h",         0x00001, 0x20000, CRC(7a791e77) SHA1(fe1429588b7eceab1d369abe03f2cad8de727f71) )
	ROM_LOAD16_BYTE( "wlu_31.12f",     0x40000, 0x20000, CRC(0eb48a83) SHA1(28c40c4b5d767f88922cd899e948abf11a85a864) )
	ROM_LOAD16_BYTE( "wlu_36.12h",     0x40001, 0x20000, CRC(36100209) SHA1(63c9338e71dba8b52daffba50b4bca31aaa10d9e) ) /* seen the same pcb with WL_36.12H */
	ROM_LOAD16_WORD_SWAP( "wlm-32.8h", 0x80000, 0x80000, CRC(dfd9f643) SHA1(9c760c30af593a87e7fd39fb213a4c73c68ca440) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "wlm-7.7a", 0x000000, 0x80000, CRC(afa74b73) SHA1(09081926260c76986a13ac5351dddd2ea11d7a10) )  // in "5" socket
	ROM_LOAD64_WORD( "wlm-5.9a", 0x000002, 0x80000, CRC(12a0dc0b) SHA1(fea235ce9489f04919daf52f4d3f3bac9b558316) )  // in "7" socket
	ROM_LOAD64_WORD( "wlm-3.3a", 0x000004, 0x80000, CRC(c6f2abce) SHA1(ff5fcfe417c43b4747bbe12db6052fdb60f5f0e4) )  // in "1" socket
	ROM_LOAD64_WORD( "wlm-1.5a", 0x000006, 0x80000, CRC(4aa4c6d3) SHA1(7dd6f18f6126c380821a2ca8955439fd6864f4c6) )  // in "3" socket
	ROM_LOAD64_BYTE( "wl_24.7d", 0x200000, 0x20000, CRC(6f0adee5) SHA1(07b18e51b376001f25173b78e0e816f252400210) )
	ROM_LOAD64_BYTE( "wl_14.7c", 0x200001, 0x20000, CRC(9cf3027d) SHA1(1e8eb20d51a54f6f756c0ab9395ac38b96e67fb2) )
	ROM_LOAD64_BYTE( "wl_26.9d", 0x200002, 0x20000, CRC(f09c8ecf) SHA1(b39f83e80af010d6481693d9ec8b1d7e258b531d) )
	ROM_LOAD64_BYTE( "wl_16.9c", 0x200003, 0x20000, CRC(e35407aa) SHA1(7ddae9cef96839da72488c1fe73268c50e0262ff) )
	ROM_LOAD64_BYTE( "wl_20.3d", 0x200004, 0x20000, CRC(84992350) SHA1(f0ebd810ce099337cda94222dccce8ab9b3c3281) )
	ROM_LOAD64_BYTE( "wl_10.3c", 0x200005, 0x20000, CRC(b87b5a36) SHA1(25fb8f9698142473233ee509d4146089920e94e1) )
	ROM_LOAD64_BYTE( "wl_22.5d", 0x200006, 0x20000, CRC(fd3f89f0) SHA1(51ff95cff56ac78682ea56401b35a0aa63cef8cb) )
	ROM_LOAD64_BYTE( "wl_12.5c", 0x200007, 0x20000, CRC(7da49d69) SHA1(b0ae7ac4f858ee8d72e6877c4275da7a631e2e4c) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "wl_09.12b", 0x00000, 0x08000, CRC(f6b3d060) SHA1(0ed2e2f64ba53ba2c371b66ab1e52e40b16d8baf) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "wl_18.11c", 0x00000, 0x20000, CRC(bde23d4d) SHA1(d1fee2f99c858dfb07edcd600da491c7b656afe0) )
	ROM_LOAD( "wl_19.12c", 0x20000, 0x20000, CRC(683898f5) SHA1(316a77b663d78c8b9ff6d85756cb05aaaeef4003) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "wl24b.1a",     0x0000, 0x0117, CRC(7101cdf1) SHA1(c848f109d09641b3159dbbb2d2ee49cf30bc9e9c) )
	ROM_LOAD( "lwio.11e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 89624B-3 */
/* Note that this set comes from a pcb running on an original Capcom USA Willow arcade cabinet, so even if there is
   the Japan "warning" it's confirmed to be a genuine USA set and almost certainly the first USA release. Then Capcom
   removed the incorrect "warning" releasing a new proper set of Willow (USA), as documented above. */
ROM_START( willowuo )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "wlu_30.11f",     0x00000, 0x20000, CRC(d604dbb1) SHA1(b5d78871011ff11a67f1a0cad147cd4de8d67f35) )
	ROM_LOAD16_BYTE( "wlu_35.11h",     0x00001, 0x20000, CRC(daee72fe) SHA1(2ec62f44394fac2887821881f56b6f24d05234b3) )
	ROM_LOAD16_BYTE( "wlu_31.12f",     0x40000, 0x20000, CRC(0eb48a83) SHA1(28c40c4b5d767f88922cd899e948abf11a85a864) )
	ROM_LOAD16_BYTE( "wlu_36.12h",     0x40001, 0x20000, CRC(36100209) SHA1(63c9338e71dba8b52daffba50b4bca31aaa10d9e) )
	ROM_LOAD16_WORD_SWAP( "wlm-32.8h", 0x80000, 0x80000, CRC(dfd9f643) SHA1(9c760c30af593a87e7fd39fb213a4c73c68ca440) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "wlm-7.7a", 0x000000, 0x80000, CRC(afa74b73) SHA1(09081926260c76986a13ac5351dddd2ea11d7a10) )  // in "5" socket
	ROM_LOAD64_WORD( "wlm-5.9a", 0x000002, 0x80000, CRC(12a0dc0b) SHA1(fea235ce9489f04919daf52f4d3f3bac9b558316) )  // in "7" socket
	ROM_LOAD64_WORD( "wlm-3.3a", 0x000004, 0x80000, CRC(c6f2abce) SHA1(ff5fcfe417c43b4747bbe12db6052fdb60f5f0e4) )  // in "1" socket
	ROM_LOAD64_WORD( "wlm-1.5a", 0x000006, 0x80000, CRC(4aa4c6d3) SHA1(7dd6f18f6126c380821a2ca8955439fd6864f4c6) )  // in "3" socket
	ROM_LOAD64_BYTE( "wl_24.7d", 0x200000, 0x20000, CRC(6f0adee5) SHA1(07b18e51b376001f25173b78e0e816f252400210) )
	ROM_LOAD64_BYTE( "wl_14.7c", 0x200001, 0x20000, CRC(9cf3027d) SHA1(1e8eb20d51a54f6f756c0ab9395ac38b96e67fb2) )
	ROM_LOAD64_BYTE( "wl_26.9d", 0x200002, 0x20000, CRC(f09c8ecf) SHA1(b39f83e80af010d6481693d9ec8b1d7e258b531d) )
	ROM_LOAD64_BYTE( "wl_16.9c", 0x200003, 0x20000, CRC(e35407aa) SHA1(7ddae9cef96839da72488c1fe73268c50e0262ff) )
	ROM_LOAD64_BYTE( "wl_20.3d", 0x200004, 0x20000, CRC(84992350) SHA1(f0ebd810ce099337cda94222dccce8ab9b3c3281) )
	ROM_LOAD64_BYTE( "wl_10.3c", 0x200005, 0x20000, CRC(b87b5a36) SHA1(25fb8f9698142473233ee509d4146089920e94e1) )
	ROM_LOAD64_BYTE( "wl_22.5d", 0x200006, 0x20000, CRC(fd3f89f0) SHA1(51ff95cff56ac78682ea56401b35a0aa63cef8cb) )
	ROM_LOAD64_BYTE( "wl_12.5c", 0x200007, 0x20000, CRC(7da49d69) SHA1(b0ae7ac4f858ee8d72e6877c4275da7a631e2e4c) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "wl_09.12b", 0x00000, 0x08000, CRC(f6b3d060) SHA1(0ed2e2f64ba53ba2c371b66ab1e52e40b16d8baf) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "wl_18.11c", 0x00000, 0x20000, CRC(bde23d4d) SHA1(d1fee2f99c858dfb07edcd600da491c7b656afe0) )
	ROM_LOAD( "wl_19.12c", 0x20000, 0x20000, CRC(683898f5) SHA1(316a77b663d78c8b9ff6d85756cb05aaaeef4003) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "wl24b.1a",     0x0000, 0x0117, CRC(7101cdf1) SHA1(c848f109d09641b3159dbbb2d2ee49cf30bc9e9c) )
	ROM_LOAD( "lwio.11e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 88622B-3 */
ROM_START( willowj )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "wl_36.12f", 0x00000, 0x20000, CRC(2b0d7cbc) SHA1(58172b4fdf856efa8d77abbde76738de2424f712) )
	ROM_LOAD16_BYTE( "wl_42.12h", 0x00001, 0x20000, CRC(1ac39615) SHA1(c9fa6d20418b9bdc5a08df1fb86368b40709280a) )
	ROM_LOAD16_BYTE( "wl_37.13f", 0x40000, 0x20000, CRC(30a717fa) SHA1(cb815e9ee2691761925898e3932b502f8f399cb4) )
	ROM_LOAD16_BYTE( "wl_43.13h", 0x40001, 0x20000, CRC(d0dddc9e) SHA1(1176b9a43b3355a5ba44e59abde01ee7eaa89c25) )
	ROM_LOAD16_BYTE( "wl_34.10f", 0x80000, 0x20000, CRC(23a84f7a) SHA1(f7667ffc85e2dfb78f5ed8ea54b6223e7375433a) )  // == wlm-32.8h
	ROM_LOAD16_BYTE( "wl_40.10h", 0x80001, 0x20000, CRC(c7a0ed21) SHA1(190aec48a8f528652b434425f64ea2e52029ad3d) )  // == wlm-32.8h
	ROM_LOAD16_BYTE( "wl_35.11f", 0xc0000, 0x20000, CRC(5eff7951) SHA1(f336bfd07f3083e3b3e72c61d03abb52d443204d) )  // == wlm-32.8h
	ROM_LOAD16_BYTE( "wl_41.11h", 0xc0001, 0x20000, CRC(8d6477a3) SHA1(1bdb98f89ec8c2dcd47ab90bdd1be862ea59a16b) )  // == wlm-32.8h

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_BYTE( "wl_09.4b",  0x000000, 0x20000, CRC(05aa71b4) SHA1(791f845928ff164eace07cfcc35d0bd9d7cbb2d0) ) // == wlm-7.7a
	ROM_LOAD64_BYTE( "wl_01.4a",  0x000001, 0x20000, CRC(08c2df12) SHA1(269ca05ff6f9928e3b46f43b262c03ac3dd768b8) ) // == wlm-7.7a
	ROM_LOAD64_BYTE( "wl_13.9b",  0x000002, 0x20000, CRC(1f7c87cd) SHA1(6817c8b42ef4e8183fc2fbd2765e49ac955cca80) ) // == wlm-5.9a
	ROM_LOAD64_BYTE( "wl_05.9a",  0x000003, 0x20000, CRC(f5254bf2) SHA1(a53973b98b8e8903c731eb2514cb9ce4060d5164) ) // == wlm-5.9a
	ROM_LOAD64_BYTE( "wl_24.5e",  0x000004, 0x20000, CRC(d9d73ba1) SHA1(5f7b5e7e6fe64106cd96a968a54556e4f5ad9387) ) // == wlm-3.3a
	ROM_LOAD64_BYTE( "wl_17.5c",  0x000005, 0x20000, CRC(a652f30c) SHA1(b150d8978d37907c6d17042476c4608c76c4ea3f) ) // == wlm-3.3a
	ROM_LOAD64_BYTE( "wl_38.8h",  0x000006, 0x20000, CRC(f6f9111b) SHA1(bd44d1b6084fe2c09cdd91b5ad6eff7a84f3e601) ) // == wlm-1.5a
	ROM_LOAD64_BYTE( "wl_32.8f",  0x000007, 0x20000, CRC(10f64027) SHA1(abf387ba7d8f0530a562a984c08345a149cfa226) ) // == wlm-1.5a
	ROM_LOAD64_BYTE( "wl_10.5b",  0x100000, 0x20000, CRC(dbba0a3f) SHA1(38c0cdd87e6631721f35a0b85fd5165940454603) ) // == wlm-7.7a
	ROM_LOAD64_BYTE( "wl_02.5a",  0x100001, 0x20000, CRC(86fba7a5) SHA1(9389ad4e0968b3edd4baeac8b29637618f49dbfb) ) // == wlm-7.7a
	ROM_LOAD64_BYTE( "wl_14.10b", 0x100002, 0x20000, CRC(7d5798b2) SHA1(7dfcc172e5df0e8f4b9188381463f9eca00945dc) ) // == wlm-5.9a
	ROM_LOAD64_BYTE( "wl_06.10a", 0x100003, 0x20000, CRC(1f052948) SHA1(ca3a6be94ae50fa50604ee52704792b22cbbc083) ) // == wlm-5.9a
	ROM_LOAD64_BYTE( "wl_25.7e",  0x100004, 0x20000, CRC(857d17d2) SHA1(e6b383df9571df6743a7aa3087f0632dc4e6143b) ) // == wlm-3.3a
	ROM_LOAD64_BYTE( "wl_18.7c",  0x100005, 0x20000, CRC(316c7fbc) SHA1(631682c7f14fa73f5f5a4fb747bfb45d9613e507) ) // == wlm-3.3a
	ROM_LOAD64_BYTE( "wl_39.9h",  0x100006, 0x20000, CRC(e6fce9b0) SHA1(46d50cf32c3518ab3dc717553c5375f3cb022cde) ) // == wlm-1.5a
	ROM_LOAD64_BYTE( "wl_33.9f",  0x100007, 0x20000, CRC(a15d5517) SHA1(03a30e216f8240383d5fa9736b4fe5119d231685) ) // == wlm-1.5a
	ROM_LOAD64_BYTE( "wl_11.7b",  0x200000, 0x20000, CRC(6f0adee5) SHA1(07b18e51b376001f25173b78e0e816f252400210) ) // == wl_24.7d
	ROM_LOAD64_BYTE( "wl_03.7a",  0x200001, 0x20000, CRC(9cf3027d) SHA1(1e8eb20d51a54f6f756c0ab9395ac38b96e67fb2) ) // == wl_14.7c
	ROM_LOAD64_BYTE( "wl_15.11b", 0x200002, 0x20000, CRC(f09c8ecf) SHA1(b39f83e80af010d6481693d9ec8b1d7e258b531d) ) // == wl_26.9d
	ROM_LOAD64_BYTE( "wl_07.11a", 0x200003, 0x20000, CRC(e35407aa) SHA1(7ddae9cef96839da72488c1fe73268c50e0262ff) ) // == wl_16.9c
	ROM_LOAD64_BYTE( "wl_26.8e",  0x200004, 0x20000, CRC(84992350) SHA1(f0ebd810ce099337cda94222dccce8ab9b3c3281) ) // == wl_20.3d
	ROM_LOAD64_BYTE( "wl_19.8c",  0x200005, 0x20000, CRC(b87b5a36) SHA1(25fb8f9698142473233ee509d4146089920e94e1) ) // == wl_10.3c
	ROM_LOAD64_BYTE( "wl_28.10e", 0x200006, 0x20000, CRC(fd3f89f0) SHA1(51ff95cff56ac78682ea56401b35a0aa63cef8cb) ) // == wl_22.5d
	ROM_LOAD64_BYTE( "wl_21.10c", 0x200007, 0x20000, CRC(7da49d69) SHA1(b0ae7ac4f858ee8d72e6877c4275da7a631e2e4c) ) // == wl_12.5c

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "wl_23.13c",  0x00000, 0x08000, CRC(f6b3d060) SHA1(0ed2e2f64ba53ba2c371b66ab1e52e40b16d8baf) )    // == wl_09.12b
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "wl_30.12e",  0x00000, 0x20000, CRC(bde23d4d) SHA1(d1fee2f99c858dfb07edcd600da491c7b656afe0) )    // == wl_18.11c
	ROM_LOAD( "wl_31.13e",  0x20000, 0x20000, CRC(683898f5) SHA1(316a77b663d78c8b9ff6d85756cb05aaaeef4003) )    // == wl_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "wl22b.1a",     0x0000, 0x0117, CRC(950cfa39) SHA1(c30eadfccf4129f2eece16f9df25cff50bdcfd53) )
	ROM_LOAD( "lwio.12c",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 89624B-2 */
ROM_START( unsquad )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "aru_30.11f",     0x00000, 0x20000, CRC(24d8f88d) SHA1(9c39aa1140e92307d6d9c0ca198003282bf78c78) )
	ROM_LOAD16_BYTE( "aru_35.11h",     0x00001, 0x20000, CRC(8b954b59) SHA1(33114f1417d48f60c6da3e14a094be7c0f0fd979) )
	ROM_LOAD16_BYTE( "aru_31.12f",     0x40000, 0x20000, CRC(33e9694b) SHA1(90db3052ac2ff859ede8473dd13e0f5be148590c) )
	ROM_LOAD16_BYTE( "aru_36.12h",     0x40001, 0x20000, CRC(7cc8fb9e) SHA1(f70118d1a368fd4795d9953c55d283305d1f9630) )
	ROM_LOAD16_WORD_SWAP( "ar-32m.8h", 0x80000, 0x80000, CRC(ae1d7fb0) SHA1(bb51e77574db5e2d807c4ca8e85a5d9661f5d3b3) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "ar-5m.7a",  0x000000, 0x80000, CRC(bf4575d8) SHA1(1b268e1698be8ff9c16f80f7b9081b6be9f72601) )
	ROM_LOAD64_WORD( "ar-7m.9a",  0x000002, 0x80000, CRC(a02945f4) SHA1(ff35cdbd6c1e43b16a906f68e416559cb3d5746b) )
	ROM_LOAD64_WORD( "ar-1m.3a",  0x000004, 0x80000, CRC(5965ca8d) SHA1(49abf80fc012a7f73306139a2871aeac7fd6a3d0) )
	ROM_LOAD64_WORD( "ar-3m.5a",  0x000006, 0x80000, CRC(ac6db17d) SHA1(78eef9ba6a392859f70467f6d7cb5aa91964abed) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ar_09.12b",  0x00000, 0x08000, CRC(f3dd1367) SHA1(09eef72e862ac6b1a5cce5a45938b45bf4e456ad) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "aru_18.11c", 0x00000, 0x20000, CRC(584b43a9) SHA1(7820815c8c67d484baf2fdad7e55d8c14b98b860) )
	/* 20000-3ffff empty */

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "ar24b.1a",     0x0000, 0x0117, CRC(09a51271) SHA1(147f53f426f258ad127157967fa59d4098e5ed16) )
	ROM_LOAD( "lwio.11e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 88622B-3 */
ROM_START( area88 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "ar_36.12f", 0x00000, 0x20000, CRC(65030392) SHA1(d9dea5cfde28345716b0e519ee033c475be0454b) )
	ROM_LOAD16_BYTE( "ar_42.12h", 0x00001, 0x20000, CRC(c48170de) SHA1(e968522dbdd217dd8e4cd6aaeaef801c63488c1d) )
	ROM_LOAD16_BYTE( "ar_37.13f", 0x40000, 0x20000, CRC(33e9694b) SHA1(90db3052ac2ff859ede8473dd13e0f5be148590c) )  // == aru_31.12f
	ROM_LOAD16_BYTE( "ar_43.13h", 0x40001, 0x20000, CRC(7cc8fb9e) SHA1(f70118d1a368fd4795d9953c55d283305d1f9630) )  // == aru_36.12h
	ROM_LOAD16_BYTE( "ar_34.10f", 0x80000, 0x20000, CRC(f6e80386) SHA1(462c1e9981b733df03e4d084df2d1fc58cf2022c) )  // == ar-32m.8h
	ROM_LOAD16_BYTE( "ar_40.10h", 0x80001, 0x20000, CRC(be36c145) SHA1(9ada7ac7361ff8871e2ae61f75e4e5d98936cdc3) )  // == ar-32m.8h
	ROM_LOAD16_BYTE( "ar_35.11f", 0xc0000, 0x20000, CRC(86d98ff3) SHA1(18137974fb7812b45f0d93e584ed14d0af2e6a3e) )  // == ar-32m.8h
	ROM_LOAD16_BYTE( "ar_41.11h", 0xc0001, 0x20000, CRC(758893d3) SHA1(1245bfd35b0f12bd701cd28c9ce2e85e166a4de2) )  // == ar-32m.8h

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_BYTE( "ar_09.4b",  0x000000, 0x20000, CRC(db9376f8) SHA1(501fc5543c66509f8fc1075c128fb01606ced2ad) ) // == ar-5m.7a
	ROM_LOAD64_BYTE( "ar_01.4a",  0x000001, 0x20000, CRC(392151b4) SHA1(ba3c69d852f750b43d8b4b2b58fcb7977cc0e5de) ) // == ar-5m.7a
	ROM_LOAD64_BYTE( "ar_13.9b",  0x000002, 0x20000, CRC(81436481) SHA1(85ceba63382959f7084bacb6aedcef80ddd8ad6b) ) // == ar-7m.9a
	ROM_LOAD64_BYTE( "ar_05.9a",  0x000003, 0x20000, CRC(e246ed9f) SHA1(aaaad5c81bf7c4ec4b2339fd8f0364096c86e903) ) // == ar-7m.9a
	ROM_LOAD64_BYTE( "ar_24.5e",  0x000004, 0x20000, CRC(9cd6e2a3) SHA1(186e756af496b0fb5b65cf7a106fe32c78d542c9) ) // == ar-1m.3a
	ROM_LOAD64_BYTE( "ar_17.5c",  0x000005, 0x20000, CRC(0b8e0df4) SHA1(ac8ab79e7237b72df9f42292a0c58aa56effe3a1) ) // == ar-1m.3a
	ROM_LOAD64_BYTE( "ar_38.8h",  0x000006, 0x20000, CRC(8b9e75b9) SHA1(eeeaa8f84167f7e8127b90318f052fe5e00c36ac) ) // == ar-3m.5a
	ROM_LOAD64_BYTE( "ar_32.8f",  0x000007, 0x20000, CRC(db6acdcf) SHA1(5842e29a0e29b4869b2c34a5f47f64c1a1f4609a) ) // == ar-3m.5a
	ROM_LOAD64_BYTE( "ar_10.5b",  0x100000, 0x20000, CRC(4219b622) SHA1(ecfc47687b466893b9c8587224830d600c754d17) ) // == ar-5m.7a
	ROM_LOAD64_BYTE( "ar_02.5a",  0x100001, 0x20000, CRC(bac5dec5) SHA1(e69f5c4e5c07db46f088d3eabfd394e7d639fea0) ) // == ar-5m.7a
	ROM_LOAD64_BYTE( "ar_14.10b", 0x100002, 0x20000, CRC(e6bae179) SHA1(31af958dedce5fcc1a7c159f96af4a3a1a4651fb) ) // == ar-7m.9a
	ROM_LOAD64_BYTE( "ar_06.10a", 0x100003, 0x20000, CRC(c8f04223) SHA1(c96eba0ce53e8505668dc646344e5b2456d60546) ) // == ar-7m.9a
	ROM_LOAD64_BYTE( "ar_25.7e",  0x100004, 0x20000, CRC(15ccf981) SHA1(2dd7a2d573089aa70b33586d6d9e6b8d816bd28e) ) // == ar-1m.3a
	ROM_LOAD64_BYTE( "ar_18.7c",  0x100005, 0x20000, CRC(9336db6a) SHA1(1704d6f0de08ed283c26ee0bcbb82a838060fe70) ) // == ar-1m.3a
	ROM_LOAD64_BYTE( "ar_39.9h",  0x100006, 0x20000, CRC(9b8e1363) SHA1(f830834305248446235cc6b17b17f7f0dd6baa03) ) // == ar-3m.5a
	ROM_LOAD64_BYTE( "ar_33.9f",  0x100007, 0x20000, CRC(3968f4b5) SHA1(42722c61c4b514b15f1594fdad688375e2c51e71) ) // == ar-3m.5a

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ar_23.13c",  0x00000, 0x08000, CRC(f3dd1367) SHA1(09eef72e862ac6b1a5cce5a45938b45bf4e456ad) )    // == ar_09.12b
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ar_30.12e",  0x00000, 0x20000, CRC(584b43a9) SHA1(7820815c8c67d484baf2fdad7e55d8c14b98b860) )    // == aru_18.11c
	/* 20000-3ffff empty */

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "ar22b.1a",     0x0000, 0x0117, CRC(f1db9030) SHA1(85a6eefb93e7bf7c7d3980737365a425c5324c08) )
	ROM_LOAD( "lwio.12c",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 91634B-2, Japan Resale Ver. */
ROM_START( area88r )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "araj_23.8f", 0x00000, 0x80000, CRC(7045d6cb) SHA1(5f8f3160e8e960ee67778232368d4f3430a0dade) )
	ROM_LOAD16_WORD_SWAP( "araj_22.7f", 0x80000, 0x80000, CRC(9913002e) SHA1(b2da8ad34bf4ea51679d35d91601faa91ef40ae5) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "ara_01.3a", 0x000000, 0x80000, CRC(bf4575d8) SHA1(1b268e1698be8ff9c16f80f7b9081b6be9f72601) ) // == ar-5m.7a
	ROM_LOAD64_WORD( "ara_02.4a", 0x000002, 0x80000, CRC(a02945f4) SHA1(ff35cdbd6c1e43b16a906f68e416559cb3d5746b) ) // == ar-7m.9a
	ROM_LOAD64_WORD( "ara_03.5a", 0x000004, 0x80000, CRC(5965ca8d) SHA1(49abf80fc012a7f73306139a2871aeac7fd6a3d0) ) // == ar-1m.3a
	ROM_LOAD64_WORD( "ara_04.6a", 0x000006, 0x80000, CRC(ac6db17d) SHA1(78eef9ba6a392859f70467f6d7cb5aa91964abed) ) // == ar-3m.5a

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ara_09.12a", 0x00000, 0x08000, CRC(af88359c) SHA1(5b7d872700a074fe1a4e11722e73374a23fa5e99) )    // == ar_09.12b + garbage
	ROM_CONTINUE(           0x10000, 0x18000 )  // second half of ROM is unused, not mapped in memory

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ara_18.11c", 0x00000, 0x20000, CRC(584b43a9) SHA1(7820815c8c67d484baf2fdad7e55d8c14b98b860) )    // == aru_18.11c
	/* 20000-3ffff empty */

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "ara63b.1a",    0x0000, 0x0117, BAD_DUMP CRC(f5569c93) SHA1(7db7cf23639036590eef1e5e309f08560859efaf) ) /* Handcrafted but works on actual PCB.  Redump needed */
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( ffight )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "ff_36.11f",      0x00000, 0x20000, CRC(f9a5ce83) SHA1(0756ae576a1f6d5b8b22f8630dca40ef38567ea6) ) // in "30" socket
	ROM_LOAD16_BYTE( "ff_42.11h",      0x00001, 0x20000, CRC(65f11215) SHA1(5045a467f3e228c02b4a355b52f58263ffa90113) ) // in "35" socket
	ROM_LOAD16_BYTE( "ff_37.12f",      0x40000, 0x20000, CRC(e1033784) SHA1(38f44434c8befd623953ae23d6e5ff4e201d6627) ) // in "31" socket
	ROM_LOAD16_BYTE( "ffe_43.12h",     0x40001, 0x20000, CRC(995e968a) SHA1(de16873d1639ac1738be0937270b108a9914f263) ) // in "36" socket
	ROM_LOAD16_WORD_SWAP( "ff-32m.8h", 0x80000, 0x80000, CRC(c747696e) SHA1(d3362dadded31ccb7eaf71ef282d698d18edd722) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "ff-5m.7a", 0x000000, 0x80000, CRC(9c284108) SHA1(7868f5801347340867720255f8380548ad1a65a7) )
	ROM_LOAD64_WORD( "ff-7m.9a", 0x000002, 0x80000, CRC(a7584dfb) SHA1(f7b00a3ca8cb85264ab293089f9f540a8292b49c) )
	ROM_LOAD64_WORD( "ff-1m.3a", 0x000004, 0x80000, CRC(0b605e44) SHA1(5ce16af72858a57aefbf6efed820c2c51935882a) )
	ROM_LOAD64_WORD( "ff-3m.5a", 0x000006, 0x80000, CRC(52291cd2) SHA1(df5f3d3aa96a7a33ff22f2a31382942c4c4f1111) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ff_09.12b", 0x00000, 0x08000, CRC(b8367eb5) SHA1(ec3db29fdd6200e9a8f4f8073a7e34aef731354f) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ff_18.11c", 0x00000, 0x20000, CRC(375c66e7) SHA1(36189e23209ce4ae5d9cbabd1574540d0591e7b3) )
	ROM_LOAD( "ff_19.12c", 0x20000, 0x20000, CRC(1ef137f9) SHA1(974b5e72aa28b87ebfa7438efbdfeda769dedf5e) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s224b.1a",     0x0000, 0x0117, CRC(cdc4413e) SHA1(c74c60f8f4eb125ffb6414aefba270676d9b8a2d) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )    /* seen the same pcb with LWIO.11E */
ROM_END

/* B-Board 89624B-3 */
ROM_START( ffighta )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "ffe_30.11f",     0x00000, 0x20000, CRC(2347bf51) SHA1(de05b347a8b4df4889d4e594e1abf741afdf0ab4) )
	ROM_LOAD16_BYTE( "ffe_35.11h",     0x00001, 0x20000, CRC(5f694ecc) SHA1(57c45ffcada0cdaf1f97c7e33c8300539828a238) )
	ROM_LOAD16_BYTE( "ffe_31.12f",     0x40000, 0x20000, CRC(6dc6b792) SHA1(553abebed2a1fa1ee2d85a4117f40d90e2321cea) )
	ROM_LOAD16_BYTE( "ffe_36.12h",     0x40001, 0x20000, CRC(b36a0b99) SHA1(cfb27987ee3d54c83c6fb73437a79dcdd96df22f) )
	ROM_LOAD16_WORD_SWAP( "ff-32m.8h", 0x80000, 0x80000, CRC(c747696e) SHA1(d3362dadded31ccb7eaf71ef282d698d18edd722) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "ff-5m.7a",  0x000000, 0x80000, CRC(9c284108) SHA1(7868f5801347340867720255f8380548ad1a65a7) )
	ROM_LOAD64_WORD( "ff-7m.9a",  0x000002, 0x80000, CRC(a7584dfb) SHA1(f7b00a3ca8cb85264ab293089f9f540a8292b49c) )
	ROM_LOAD64_WORD( "ff-1m.3a",  0x000004, 0x80000, CRC(0b605e44) SHA1(5ce16af72858a57aefbf6efed820c2c51935882a) )
	ROM_LOAD64_WORD( "ff-3m.5a",  0x000006, 0x80000, CRC(52291cd2) SHA1(df5f3d3aa96a7a33ff22f2a31382942c4c4f1111) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ffe_23.12b", 0x00000, 0x08000, CRC(b8367eb5) SHA1(ec3db29fdd6200e9a8f4f8073a7e34aef731354f) )    // == ff_09.12b /* label is FFE_23, pcb verified */
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ff_18.11c",  0x00000, 0x20000, CRC(375c66e7) SHA1(36189e23209ce4ae5d9cbabd1574540d0591e7b3) )
	ROM_LOAD( "ff_19.12c",  0x20000, 0x20000, CRC(1ef137f9) SHA1(974b5e72aa28b87ebfa7438efbdfeda769dedf5e) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s224b.1a",     0x0000, 0x0117, CRC(cdc4413e) SHA1(c74c60f8f4eb125ffb6414aefba270676d9b8a2d) )
	ROM_LOAD( "lwio.11e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( ffightu )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "ff_36.11f",      0x00000, 0x20000, CRC(f9a5ce83) SHA1(0756ae576a1f6d5b8b22f8630dca40ef38567ea6) ) // in "30" socket
	ROM_LOAD16_BYTE( "ff_42.11h",      0x00001, 0x20000, CRC(65f11215) SHA1(5045a467f3e228c02b4a355b52f58263ffa90113) ) // in "35" socket
	ROM_LOAD16_BYTE( "ff_37.12f",      0x40000, 0x20000, CRC(e1033784) SHA1(38f44434c8befd623953ae23d6e5ff4e201d6627) ) // in "31" socket
	ROM_LOAD16_BYTE( "ffu_43.12h",     0x40001, 0x20000, CRC(4ca65947) SHA1(74ffe00df96273770a24d9a46f13e53ea8812670) ) // in "36" socket   /* seen the same pcb with FF_43.12H */
	ROM_LOAD16_WORD_SWAP( "ff-32m.8h", 0x80000, 0x80000, CRC(c747696e) SHA1(d3362dadded31ccb7eaf71ef282d698d18edd722) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "ff-5m.7a", 0x000000, 0x80000, CRC(9c284108) SHA1(7868f5801347340867720255f8380548ad1a65a7) )
	ROM_LOAD64_WORD( "ff-7m.9a", 0x000002, 0x80000, CRC(a7584dfb) SHA1(f7b00a3ca8cb85264ab293089f9f540a8292b49c) )
	ROM_LOAD64_WORD( "ff-1m.3a", 0x000004, 0x80000, CRC(0b605e44) SHA1(5ce16af72858a57aefbf6efed820c2c51935882a) )
	ROM_LOAD64_WORD( "ff-3m.5a", 0x000006, 0x80000, CRC(52291cd2) SHA1(df5f3d3aa96a7a33ff22f2a31382942c4c4f1111) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ff_09.12b", 0x00000, 0x08000, CRC(b8367eb5) SHA1(ec3db29fdd6200e9a8f4f8073a7e34aef731354f) ) /* seen the same pcb with 23.12B */
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ff_18.11c", 0x00000, 0x20000, CRC(375c66e7) SHA1(36189e23209ce4ae5d9cbabd1574540d0591e7b3) )
	ROM_LOAD( "ff_19.12c", 0x20000, 0x20000, CRC(1ef137f9) SHA1(974b5e72aa28b87ebfa7438efbdfeda769dedf5e) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s224b.1a",     0x0000, 0x0117, CRC(cdc4413e) SHA1(c74c60f8f4eb125ffb6414aefba270676d9b8a2d) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 89624B-3 */
/* Note that this set is equivalent to ffightu, but the 4Mbit mask ROM FF-32M located @ 8H is replaced by 4 1Mbit EPROMs. */
ROM_START( ffightu1 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "ff_36.11f",  0x00000, 0x20000, CRC(f9a5ce83) SHA1(0756ae576a1f6d5b8b22f8630dca40ef38567ea6) ) // in "30" socket
	ROM_LOAD16_BYTE( "ff_42.11h",  0x00001, 0x20000, CRC(65f11215) SHA1(5045a467f3e228c02b4a355b52f58263ffa90113) ) // in "35" socket
	ROM_LOAD16_BYTE( "ff_37.12f",  0x40000, 0x20000, CRC(e1033784) SHA1(38f44434c8befd623953ae23d6e5ff4e201d6627) ) // in "31" socket
	ROM_LOAD16_BYTE( "ffu_43.12h", 0x40001, 0x20000, CRC(4ca65947) SHA1(74ffe00df96273770a24d9a46f13e53ea8812670) ) // in "36" socket
	ROM_LOAD16_BYTE( "ff_34.9f",   0x80000, 0x20000, CRC(0c8dc3fc) SHA1(edcce3efd9cdd131ef0c96df15a68722d5c3498e) ) // in "28" socket   // == ff-32m.8h
	ROM_LOAD16_BYTE( "ff_40.9h",   0x80001, 0x20000, CRC(8075bab9) SHA1(f9c7405133f6fc5557c90e60e8ccc459e4f6fd7d) ) // in "33" socket   // == ff-32m.8h
	ROM_LOAD16_BYTE( "ff_35.10f",  0xc0000, 0x20000, CRC(4a934121) SHA1(3982c261582755a0eac340d6d7ed96e6c263c8b6) ) // in "29" socket   // == ff-32m.8h
	ROM_LOAD16_BYTE( "ff_41.10h",  0xc0001, 0x20000, CRC(2af68154) SHA1(7d549cb38650b4b79c68ad6d0dfcefdd62be4e99) ) // in "34" socket   // == ff-32m.8h

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "ff-5m.7a", 0x000000, 0x80000, CRC(9c284108) SHA1(7868f5801347340867720255f8380548ad1a65a7) )
	ROM_LOAD64_WORD( "ff-7m.9a", 0x000002, 0x80000, CRC(a7584dfb) SHA1(f7b00a3ca8cb85264ab293089f9f540a8292b49c) )
	ROM_LOAD64_WORD( "ff-1m.3a", 0x000004, 0x80000, CRC(0b605e44) SHA1(5ce16af72858a57aefbf6efed820c2c51935882a) )
	ROM_LOAD64_WORD( "ff-3m.5a", 0x000006, 0x80000, CRC(52291cd2) SHA1(df5f3d3aa96a7a33ff22f2a31382942c4c4f1111) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ff_09.12b", 0x00000, 0x08000, CRC(b8367eb5) SHA1(ec3db29fdd6200e9a8f4f8073a7e34aef731354f) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ff_18.11c", 0x00000, 0x20000, CRC(375c66e7) SHA1(36189e23209ce4ae5d9cbabd1574540d0591e7b3) )
	ROM_LOAD( "ff_19.12c", 0x20000, 0x20000, CRC(1ef137f9) SHA1(974b5e72aa28b87ebfa7438efbdfeda769dedf5e) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s224b.1a",     0x0000, 0x0117, CRC(cdc4413e) SHA1(c74c60f8f4eb125ffb6414aefba270676d9b8a2d) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( ffightua )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "ffu_36.11f",     0x00000, 0x20000, CRC(e2a48af9) SHA1(11e06f95bdf575af396dded2b84d858f6c7388f1) ) // in "30" socket
	ROM_LOAD16_BYTE( "ffu_42.11h",     0x00001, 0x20000, CRC(f4bb480e) SHA1(32114df1d2f4f98a4a2280a330c7b6af8ab4d862) ) // in "35" socket
	ROM_LOAD16_BYTE( "ffu_37.12f",     0x40000, 0x20000, CRC(c371c667) SHA1(633977c91a8ff09b7fe83128eced7c4dee9aee1d) ) // in "31" socket
	ROM_LOAD16_BYTE( "ffu_43.12h",     0x40001, 0x20000, CRC(2f5771f9) SHA1(fb532402bc00b5619a23dfa7e4525f1717020303) ) // in "36" socket   /* different CRC from ffightu, ffightu1, pcb verified */
	ROM_LOAD16_WORD_SWAP( "ff-32m.8h", 0x80000, 0x80000, CRC(c747696e) SHA1(d3362dadded31ccb7eaf71ef282d698d18edd722) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "ff-5m.7a", 0x000000, 0x80000, CRC(9c284108) SHA1(7868f5801347340867720255f8380548ad1a65a7) )
	ROM_LOAD64_WORD( "ff-7m.9a", 0x000002, 0x80000, CRC(a7584dfb) SHA1(f7b00a3ca8cb85264ab293089f9f540a8292b49c) )
	ROM_LOAD64_WORD( "ff-1m.3a", 0x000004, 0x80000, CRC(0b605e44) SHA1(5ce16af72858a57aefbf6efed820c2c51935882a) )
	ROM_LOAD64_WORD( "ff-3m.5a", 0x000006, 0x80000, CRC(52291cd2) SHA1(df5f3d3aa96a7a33ff22f2a31382942c4c4f1111) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ff_09.12b", 0x00000, 0x08000, CRC(b8367eb5) SHA1(ec3db29fdd6200e9a8f4f8073a7e34aef731354f) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ff_18.11c", 0x00000, 0x20000, CRC(375c66e7) SHA1(36189e23209ce4ae5d9cbabd1574540d0591e7b3) )
	ROM_LOAD( "ff_19.12c", 0x20000, 0x20000, CRC(1ef137f9) SHA1(974b5e72aa28b87ebfa7438efbdfeda769dedf5e) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s224b.1a",     0x0000, 0x0117, CRC(cdc4413e) SHA1(c74c60f8f4eb125ffb6414aefba270676d9b8a2d) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( ffightub )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "ffu_30_3.11f",   0x00000, 0x20000, CRC(e619eb30) SHA1(41c2589a1b2cab2d0ded527a89f8e0e39e61efe1) )
	ROM_LOAD16_BYTE( "ffu_35_3.11h",   0x00001, 0x20000, CRC(bca85263) SHA1(249bc81426ee93cf2efa5594d6813d5dd896cea3) )
	ROM_LOAD16_BYTE( "ffu_31_3.12f",   0x40000, 0x20000, CRC(59abd207) SHA1(9bc1f4d5dabd02cebce84f56f848694591c0629d) )
	ROM_LOAD16_BYTE( "ffu_36_3.12h",   0x40001, 0x20000, CRC(df46ece8) SHA1(aa3081918d499f56664d6bb7b7ede2055d00375d) )
	ROM_LOAD16_WORD_SWAP( "ff-32m.8h", 0x80000, 0x80000, CRC(c747696e) SHA1(d3362dadded31ccb7eaf71ef282d698d18edd722) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "ff-5m.7a", 0x000000, 0x80000, CRC(9c284108) SHA1(7868f5801347340867720255f8380548ad1a65a7) )
	ROM_LOAD64_WORD( "ff-7m.9a", 0x000002, 0x80000, CRC(a7584dfb) SHA1(f7b00a3ca8cb85264ab293089f9f540a8292b49c) )
	ROM_LOAD64_WORD( "ff-1m.3a", 0x000004, 0x80000, CRC(0b605e44) SHA1(5ce16af72858a57aefbf6efed820c2c51935882a) )
	ROM_LOAD64_WORD( "ff-3m.5a", 0x000006, 0x80000, CRC(52291cd2) SHA1(df5f3d3aa96a7a33ff22f2a31382942c4c4f1111) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ff_09.12b", 0x00000, 0x08000, CRC(b8367eb5) SHA1(ec3db29fdd6200e9a8f4f8073a7e34aef731354f) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ff_18.11c", 0x00000, 0x20000, CRC(375c66e7) SHA1(36189e23209ce4ae5d9cbabd1574540d0591e7b3) )
	ROM_LOAD( "ff_19.12c", 0x20000, 0x20000, CRC(1ef137f9) SHA1(974b5e72aa28b87ebfa7438efbdfeda769dedf5e) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s224b.1a",     0x0000, 0x0117, CRC(cdc4413e) SHA1(c74c60f8f4eb125ffb6414aefba270676d9b8a2d) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( ffightuc )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "ffu_30.11f",     0x00000, 0x20000, CRC(ed988977) SHA1(c718e989206bd2b68832c8fcb5667397d500ebac) )
	ROM_LOAD16_BYTE( "ffu_35.11h",     0x00001, 0x20000, CRC(07bf1c21) SHA1(f21a939fd92607c7f54816dedbcb3c5818cf4183) )
	ROM_LOAD16_BYTE( "ffu_31.12f",     0x40000, 0x20000, CRC(dba5a476) SHA1(2f0176dd050f9630b914f1c1ca5d96215bcf567f) )
	ROM_LOAD16_BYTE( "ffu_36.12h",     0x40001, 0x20000, CRC(4d89f542) SHA1(0b7d483a2c5759715f99f287cbd8a36165b59de7) )
	ROM_LOAD16_WORD_SWAP( "ff-32m.8h", 0x80000, 0x80000, CRC(c747696e) SHA1(d3362dadded31ccb7eaf71ef282d698d18edd722) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "ff-5m.7a", 0x000000, 0x80000, CRC(9c284108) SHA1(7868f5801347340867720255f8380548ad1a65a7) )
	ROM_LOAD64_WORD( "ff-7m.9a", 0x000002, 0x80000, CRC(a7584dfb) SHA1(f7b00a3ca8cb85264ab293089f9f540a8292b49c) )
	ROM_LOAD64_WORD( "ff-1m.3a", 0x000004, 0x80000, CRC(0b605e44) SHA1(5ce16af72858a57aefbf6efed820c2c51935882a) )
	ROM_LOAD64_WORD( "ff-3m.5a", 0x000006, 0x80000, CRC(52291cd2) SHA1(df5f3d3aa96a7a33ff22f2a31382942c4c4f1111) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ff_23.12b", 0x00000, 0x08000, CRC(b8367eb5) SHA1(ec3db29fdd6200e9a8f4f8073a7e34aef731354f) ) // == ff_09.12b /* label is FF_23, pcb verified */
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ff_18.11c", 0x00000, 0x20000, CRC(375c66e7) SHA1(36189e23209ce4ae5d9cbabd1574540d0591e7b3) )
	ROM_LOAD( "ff_19.12c", 0x20000, 0x20000, CRC(1ef137f9) SHA1(974b5e72aa28b87ebfa7438efbdfeda769dedf5e) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s224b.1a",     0x0000, 0x0117, CRC(cdc4413e) SHA1(c74c60f8f4eb125ffb6414aefba270676d9b8a2d) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* FIXME B-Board uncertain but should be 88622B/89625B from the program ROM names */
ROM_START( ffightj )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "ff36.bin",    0x00000, 0x20000, CRC(f9a5ce83) SHA1(0756ae576a1f6d5b8b22f8630dca40ef38567ea6) )    // == ff_36.11f
	ROM_LOAD16_BYTE( "ff42.bin",    0x00001, 0x20000, CRC(65f11215) SHA1(5045a467f3e228c02b4a355b52f58263ffa90113) )    // == ff_42.11h
	ROM_LOAD16_BYTE( "ff37.bin",    0x40000, 0x20000, CRC(e1033784) SHA1(38f44434c8befd623953ae23d6e5ff4e201d6627) )    // == ff_37.12f
	ROM_LOAD16_BYTE( "ff43.bin",    0x40001, 0x20000, CRC(b6dee1c3) SHA1(3a85312a2e9d8e06259b73d91ccb5e66a6bad62d) )
	ROM_LOAD16_BYTE( "ffj_34.10f",  0x80000, 0x20000, CRC(0c8dc3fc) SHA1(edcce3efd9cdd131ef0c96df15a68722d5c3498e) )    // == ff-32m.8h
	ROM_LOAD16_BYTE( "ffj_40.10h",  0x80001, 0x20000, CRC(8075bab9) SHA1(f9c7405133f6fc5557c90e60e8ccc459e4f6fd7d) )    // == ff-32m.8h
	ROM_LOAD16_BYTE( "ffj_35.11f",  0xc0000, 0x20000, CRC(4a934121) SHA1(3982c261582755a0eac340d6d7ed96e6c263c8b6) )    // == ff-32m.8h
	ROM_LOAD16_BYTE( "ffj_41.11h",  0xc0001, 0x20000, CRC(2af68154) SHA1(7d549cb38650b4b79c68ad6d0dfcefdd62be4e99) )    // == ff-32m.8h

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_BYTE( "ffj_09.4b",  0x000000, 0x20000, CRC(5b116d0d) SHA1(a24e829fdfa043bd27b508d7cc0788ad80fd180e) )
	ROM_LOAD64_BYTE( "ffj_01.4a",  0x000001, 0x20000, CRC(815b1797) SHA1(549e5eefc8f607fec1c954ba715ff21b8e44a5aa) )
	ROM_LOAD64_BYTE( "ffj_13.9b",  0x000002, 0x20000, CRC(8721a7da) SHA1(39b2b324fd7810342503f23695e423f364a6294d) )
	ROM_LOAD64_BYTE( "ffj_05.9a",  0x000003, 0x20000, CRC(d0fcd4b5) SHA1(97ebcbead0cca7e425143c500c433dbcf9cadcc2) )
	ROM_LOAD64_BYTE( "ffj_24.5e",  0x000004, 0x20000, CRC(a1ab607a) SHA1(56784c028b82d9e2affd9610f56fde57063e4c28) )
	ROM_LOAD64_BYTE( "ffj_17.5c",  0x000005, 0x20000, CRC(2dc18cf4) SHA1(5e3bd895600cd30d561a75a2fcb6cc8bc84f4bd1) )
	ROM_LOAD64_BYTE( "ffj_38.8h",  0x000006, 0x20000, CRC(6535a57f) SHA1(f4da9ec13cad7e3287e34dcceb0eb2d20107bad6) )
	ROM_LOAD64_BYTE( "ffj_32.8f",  0x000007, 0x20000, CRC(c8bc4a57) SHA1(3eaf2b4e910fe1f79154020122d786d23a2e594a) )
	ROM_LOAD64_BYTE( "ffj_10.5b",  0x100000, 0x20000, CRC(624a924a) SHA1(48fd0498f9ed54003bf6578fbcbc8b7e90a195eb) )
	ROM_LOAD64_BYTE( "ffj_02.5a",  0x100001, 0x20000, CRC(5d91f694) SHA1(e0ea9ec82dec985d8bf5e7cebf5fe3d8ef7557b1) )
	ROM_LOAD64_BYTE( "ffj_14.10b", 0x100002, 0x20000, CRC(0a2e9101) SHA1(6c8d550d2066cd53355ccf14ac1fd35914982094) )
	ROM_LOAD64_BYTE( "ffj_06.10a", 0x100003, 0x20000, CRC(1c18f042) SHA1(f708296570fecad82a76dc59744873a2f5568ea1) )
	ROM_LOAD64_BYTE( "ffj_25.7e",  0x100004, 0x20000, CRC(6e8181ea) SHA1(2c32bc0364650ee6ca0d24754a7a3401295ffcd5) )
	ROM_LOAD64_BYTE( "ffj_18.7c",  0x100005, 0x20000, CRC(b19ede59) SHA1(7e79ad9f17b36e042d774bef3bbb44018332ca01) )
	ROM_LOAD64_BYTE( "ffj_39.9h",  0x100006, 0x20000, CRC(9416b477) SHA1(f2310dfcfe960e8b822c07849b594d54dfc2b2ca) )
	ROM_LOAD64_BYTE( "ffj_33.9f",  0x100007, 0x20000, CRC(7369fa07) SHA1(3b2750fe33729395217c96909b4b6c5f3d6e9943) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ff_23.bin",   0x00000, 0x08000, CRC(b8367eb5) SHA1(ec3db29fdd6200e9a8f4f8073a7e34aef731354f) )   // == ff_09.12b
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ffj_30.bin",  0x00000, 0x20000, CRC(375c66e7) SHA1(36189e23209ce4ae5d9cbabd1574540d0591e7b3) )   // == ff_18.11c
	ROM_LOAD( "ffj_31.bin",  0x20000, 0x20000, CRC(1ef137f9) SHA1(974b5e72aa28b87ebfa7438efbdfeda769dedf5e) )   // == ff_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s222b.1a",     0x0000, 0x0117, CRC(6d86b45e) SHA1(2b27646adaf1ca2f58e14754d6f7ef4fdca77fbe) )
	ROM_LOAD( "lwio.12c",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )    // pal & position verification required
ROM_END

/* B-Board 89625B-1 */
ROM_START( ffightj1 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "ffj_36.12f", 0x00000, 0x20000, CRC(e2a48af9) SHA1(11e06f95bdf575af396dded2b84d858f6c7388f1) ) // == ffu_36.11f
	ROM_LOAD16_BYTE( "ffj_42.12h", 0x00001, 0x20000, CRC(f4bb480e) SHA1(32114df1d2f4f98a4a2280a330c7b6af8ab4d862) ) // == ffu_42.11h
	ROM_LOAD16_BYTE( "ffj_37.13f", 0x40000, 0x20000, CRC(c371c667) SHA1(633977c91a8ff09b7fe83128eced7c4dee9aee1d) ) // == ffu_37.12f
	ROM_LOAD16_BYTE( "ffj_43.13h", 0x40001, 0x20000, CRC(6f81f194) SHA1(2cddf75a0a607cf57395583876cf81bcca005871) )
	ROM_LOAD16_BYTE( "ffj_34.10f", 0x80000, 0x20000, CRC(0c8dc3fc) SHA1(edcce3efd9cdd131ef0c96df15a68722d5c3498e) ) // == ff-32m.8h
	ROM_LOAD16_BYTE( "ffj_40.10h", 0x80001, 0x20000, CRC(8075bab9) SHA1(f9c7405133f6fc5557c90e60e8ccc459e4f6fd7d) ) // == ff-32m.8h
	ROM_LOAD16_BYTE( "ffj_35.11f", 0xc0000, 0x20000, CRC(4a934121) SHA1(3982c261582755a0eac340d6d7ed96e6c263c8b6) ) // == ff-32m.8h
	ROM_LOAD16_BYTE( "ffj_41.11h", 0xc0001, 0x20000, CRC(2af68154) SHA1(7d549cb38650b4b79c68ad6d0dfcefdd62be4e99) ) // == ff-32m.8h

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_BYTE( "ffj_09.4b",  0x000000, 0x20000, CRC(5b116d0d) SHA1(a24e829fdfa043bd27b508d7cc0788ad80fd180e) )
	ROM_LOAD64_BYTE( "ffj_01.4a",  0x000001, 0x20000, CRC(815b1797) SHA1(549e5eefc8f607fec1c954ba715ff21b8e44a5aa) )
	ROM_LOAD64_BYTE( "ffj_13.9b",  0x000002, 0x20000, CRC(8721a7da) SHA1(39b2b324fd7810342503f23695e423f364a6294d) )
	ROM_LOAD64_BYTE( "ffj_05.9a",  0x000003, 0x20000, CRC(d0fcd4b5) SHA1(97ebcbead0cca7e425143c500c433dbcf9cadcc2) )
	ROM_LOAD64_BYTE( "ffj_24.5e",  0x000004, 0x20000, CRC(a1ab607a) SHA1(56784c028b82d9e2affd9610f56fde57063e4c28) )
	ROM_LOAD64_BYTE( "ffj_17.5c",  0x000005, 0x20000, CRC(2dc18cf4) SHA1(5e3bd895600cd30d561a75a2fcb6cc8bc84f4bd1) )
	ROM_LOAD64_BYTE( "ffj_38.8h",  0x000006, 0x20000, CRC(6535a57f) SHA1(f4da9ec13cad7e3287e34dcceb0eb2d20107bad6) )
	ROM_LOAD64_BYTE( "ffj_32.8f",  0x000007, 0x20000, CRC(c8bc4a57) SHA1(3eaf2b4e910fe1f79154020122d786d23a2e594a) )
	ROM_LOAD64_BYTE( "ffj_10.5b",  0x100000, 0x20000, CRC(624a924a) SHA1(48fd0498f9ed54003bf6578fbcbc8b7e90a195eb) )
	ROM_LOAD64_BYTE( "ffj_02.5a",  0x100001, 0x20000, CRC(5d91f694) SHA1(e0ea9ec82dec985d8bf5e7cebf5fe3d8ef7557b1) )
	ROM_LOAD64_BYTE( "ffj_14.10b", 0x100002, 0x20000, CRC(0a2e9101) SHA1(6c8d550d2066cd53355ccf14ac1fd35914982094) )
	ROM_LOAD64_BYTE( "ffj_06.10a", 0x100003, 0x20000, CRC(1c18f042) SHA1(f708296570fecad82a76dc59744873a2f5568ea1) )
	ROM_LOAD64_BYTE( "ffj_25.7e",  0x100004, 0x20000, CRC(6e8181ea) SHA1(2c32bc0364650ee6ca0d24754a7a3401295ffcd5) )
	ROM_LOAD64_BYTE( "ffj_18.7c",  0x100005, 0x20000, CRC(b19ede59) SHA1(7e79ad9f17b36e042d774bef3bbb44018332ca01) )
	ROM_LOAD64_BYTE( "ffj_39.9h",  0x100006, 0x20000, CRC(9416b477) SHA1(f2310dfcfe960e8b822c07849b594d54dfc2b2ca) )
	ROM_LOAD64_BYTE( "ffj_33.9f",  0x100007, 0x20000, CRC(7369fa07) SHA1(3b2750fe33729395217c96909b4b6c5f3d6e9943) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ff_23.13b",   0x00000, 0x08000, CRC(b8367eb5) SHA1(ec3db29fdd6200e9a8f4f8073a7e34aef731354f) )   // == ff_09.12b
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ffj_30.12c",  0x00000, 0x20000, CRC(375c66e7) SHA1(36189e23209ce4ae5d9cbabd1574540d0591e7b3) )   // == ff_18.11c
	ROM_LOAD( "ffj_31.13c",  0x20000, 0x20000, CRC(1ef137f9) SHA1(974b5e72aa28b87ebfa7438efbdfeda769dedf5e) )   // == ff_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s222b.1a",     0x0000, 0x0117, CRC(6d86b45e) SHA1(2b27646adaf1ca2f58e14754d6f7ef4fdca77fbe) )
	ROM_LOAD( "lwio.12e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 88622B-3 */
ROM_START( ffightj2 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "ffj_36a.12f", 0x00000, 0x20000, CRC(088ed1c9) SHA1(7b96cd45f4d3d2c0fe94904882652814b7790869) )
	ROM_LOAD16_BYTE( "ffj_42a.12h", 0x00001, 0x20000, CRC(c4c491e6) SHA1(d0e34d7b94f67c33615710ea721da8fefe832e3a) )
	ROM_LOAD16_BYTE( "ffj_37a.13f", 0x40000, 0x20000, CRC(708557ff) SHA1(89e56bfd9486623a18fdbf984a72bb52054ca0e6) )
	ROM_LOAD16_BYTE( "ffj_43a.13h", 0x40001, 0x20000, CRC(c004004a) SHA1(10ccf27972591f65645a8dd2bb65989176ac07d5) )
	ROM_LOAD16_BYTE( "ff_34.10f",   0x80000, 0x20000, CRC(0c8dc3fc) SHA1(edcce3efd9cdd131ef0c96df15a68722d5c3498e) )    // == ff-32m.8h
	ROM_LOAD16_BYTE( "ff_40.10h",   0x80001, 0x20000, CRC(8075bab9) SHA1(f9c7405133f6fc5557c90e60e8ccc459e4f6fd7d) )    // == ff-32m.8h
	ROM_LOAD16_BYTE( "ff_35.11f",   0xc0000, 0x20000, CRC(4a934121) SHA1(3982c261582755a0eac340d6d7ed96e6c263c8b6) )    // == ff-32m.8h
	ROM_LOAD16_BYTE( "ff_41.11h",   0xc0001, 0x20000, CRC(2af68154) SHA1(7d549cb38650b4b79c68ad6d0dfcefdd62be4e99) )    // == ff-32m.8h

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_BYTE( "ff_09.4b",  0x000000, 0x20000, CRC(5b116d0d) SHA1(a24e829fdfa043bd27b508d7cc0788ad80fd180e) )
	ROM_LOAD64_BYTE( "ff_01.4a",  0x000001, 0x20000, CRC(815b1797) SHA1(549e5eefc8f607fec1c954ba715ff21b8e44a5aa) )
	ROM_LOAD64_BYTE( "ff_13.9b",  0x000002, 0x20000, CRC(8721a7da) SHA1(39b2b324fd7810342503f23695e423f364a6294d) )
	ROM_LOAD64_BYTE( "ff_05.9a",  0x000003, 0x20000, CRC(d0fcd4b5) SHA1(97ebcbead0cca7e425143c500c433dbcf9cadcc2) )
	ROM_LOAD64_BYTE( "ff_24.5e",  0x000004, 0x20000, CRC(a1ab607a) SHA1(56784c028b82d9e2affd9610f56fde57063e4c28) )
	ROM_LOAD64_BYTE( "ff_17.5c",  0x000005, 0x20000, CRC(2dc18cf4) SHA1(5e3bd895600cd30d561a75a2fcb6cc8bc84f4bd1) )
	ROM_LOAD64_BYTE( "ff_38.8h",  0x000006, 0x20000, CRC(6535a57f) SHA1(f4da9ec13cad7e3287e34dcceb0eb2d20107bad6) )
	ROM_LOAD64_BYTE( "ff_32.8f",  0x000007, 0x20000, CRC(c8bc4a57) SHA1(3eaf2b4e910fe1f79154020122d786d23a2e594a) )
	ROM_LOAD64_BYTE( "ff_10.5b",  0x100000, 0x20000, CRC(624a924a) SHA1(48fd0498f9ed54003bf6578fbcbc8b7e90a195eb) )
	ROM_LOAD64_BYTE( "ff_02.5a",  0x100001, 0x20000, CRC(5d91f694) SHA1(e0ea9ec82dec985d8bf5e7cebf5fe3d8ef7557b1) )
	ROM_LOAD64_BYTE( "ff_14.10b", 0x100002, 0x20000, CRC(0a2e9101) SHA1(6c8d550d2066cd53355ccf14ac1fd35914982094) )
	ROM_LOAD64_BYTE( "ff_06.10a", 0x100003, 0x20000, CRC(1c18f042) SHA1(f708296570fecad82a76dc59744873a2f5568ea1) )
	ROM_LOAD64_BYTE( "ff_25.7e",  0x100004, 0x20000, CRC(6e8181ea) SHA1(2c32bc0364650ee6ca0d24754a7a3401295ffcd5) )
	ROM_LOAD64_BYTE( "ff_18.7c",  0x100005, 0x20000, CRC(b19ede59) SHA1(7e79ad9f17b36e042d774bef3bbb44018332ca01) )
	ROM_LOAD64_BYTE( "ff_39.9h",  0x100006, 0x20000, CRC(9416b477) SHA1(f2310dfcfe960e8b822c07849b594d54dfc2b2ca) )
	ROM_LOAD64_BYTE( "ff_33.9f",  0x100007, 0x20000, CRC(7369fa07) SHA1(3b2750fe33729395217c96909b4b6c5f3d6e9943) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ff_23.13c",  0x00000, 0x08000, CRC(b8367eb5) SHA1(ec3db29fdd6200e9a8f4f8073a7e34aef731354f) )    // == ff_09.12b
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ff_30.12e",  0x00000, 0x20000, CRC(375c66e7) SHA1(36189e23209ce4ae5d9cbabd1574540d0591e7b3) )    // == ff_18.11c
	ROM_LOAD( "ff_31.13e",  0x20000, 0x20000, CRC(1ef137f9) SHA1(974b5e72aa28b87ebfa7438efbdfeda769dedf5e) )    // == ff_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s222b.1a",     0x0000, 0x0117, CRC(6d86b45e) SHA1(2b27646adaf1ca2f58e14754d6f7ef4fdca77fbe) )
	ROM_LOAD( "lwio.12c",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 88622B-2 */
ROM_START( ffightj3 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "ffj_36.12f",             0x00000, 0x20000, CRC(e619eb30) SHA1(41c2589a1b2cab2d0ded527a89f8e0e39e61efe1) ) // == ffu_30_3.11f - labeld FFJ 36 but had blue stripe across label like USA sets
	ROM_LOAD16_BYTE( "ffj_42.12h",             0x00001, 0x20000, CRC(bca85263) SHA1(249bc81426ee93cf2efa5594d6813d5dd896cea3) ) // == ffu_35_3.11h - labeld FFJ 42 but had blue stripe across label like USA sets
	ROM_LOAD16_BYTE( "ffj_37.13f",             0x40000, 0x20000, CRC(a8127e4e) SHA1(9864156ab2c96f4fc8bba2f3b965e3ed90b6bb94) ) // sldh - labeld FFJ 37 but had blue stripe across label like USA sets
	ROM_LOAD16_BYTE( "ffj_43.13h",             0x40001, 0x20000, CRC(56ccd34a) SHA1(3e00ec395a30697d5697bc6ace42b58c3f322c23) ) // sldh - labeld FFJ 43 but had blue stripe across label like USA sets
	ROM_LOAD16_BYTE( "ffj_34.10f",             0x80000, 0x20000, CRC(0c8dc3fc) SHA1(edcce3efd9cdd131ef0c96df15a68722d5c3498e) ) // == ff-32m.8h
	ROM_LOAD16_BYTE( "ffj_40.10h",             0x80001, 0x20000, CRC(8075bab9) SHA1(f9c7405133f6fc5557c90e60e8ccc459e4f6fd7d) ) // == ff-32m.8h
	ROM_LOAD16_BYTE( "ffj_35.11f",             0xc0000, 0x20000, CRC(4a934121) SHA1(3982c261582755a0eac340d6d7ed96e6c263c8b6) ) // == ff-32m.8h
	ROM_LOAD16_BYTE( "ffj_41.11h",             0xc0001, 0x20000, CRC(2af68154) SHA1(7d549cb38650b4b79c68ad6d0dfcefdd62be4e99) ) // == ff-32m.8h

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_BYTE( "ff_09.4b",  0x000000, 0x20000, CRC(5b116d0d) SHA1(a24e829fdfa043bd27b508d7cc0788ad80fd180e) )
	ROM_LOAD64_BYTE( "ff_01.4a",  0x000001, 0x20000, CRC(815b1797) SHA1(549e5eefc8f607fec1c954ba715ff21b8e44a5aa) )
	ROM_LOAD64_BYTE( "ff_13.9b",  0x000002, 0x20000, CRC(8721a7da) SHA1(39b2b324fd7810342503f23695e423f364a6294d) )
	ROM_LOAD64_BYTE( "ff_05.9a",  0x000003, 0x20000, CRC(d0fcd4b5) SHA1(97ebcbead0cca7e425143c500c433dbcf9cadcc2) )
	ROM_LOAD64_BYTE( "ff_24.5e",  0x000004, 0x20000, CRC(a1ab607a) SHA1(56784c028b82d9e2affd9610f56fde57063e4c28) )
	ROM_LOAD64_BYTE( "ff_17.5c",  0x000005, 0x20000, CRC(2dc18cf4) SHA1(5e3bd895600cd30d561a75a2fcb6cc8bc84f4bd1) )
	ROM_LOAD64_BYTE( "ff_38.8h",  0x000006, 0x20000, CRC(6535a57f) SHA1(f4da9ec13cad7e3287e34dcceb0eb2d20107bad6) )
	ROM_LOAD64_BYTE( "ff_32.8f",  0x000007, 0x20000, CRC(c8bc4a57) SHA1(3eaf2b4e910fe1f79154020122d786d23a2e594a) )
	ROM_LOAD64_BYTE( "ff_10.5b",  0x100000, 0x20000, CRC(624a924a) SHA1(48fd0498f9ed54003bf6578fbcbc8b7e90a195eb) )
	ROM_LOAD64_BYTE( "ff_02.5a",  0x100001, 0x20000, CRC(5d91f694) SHA1(e0ea9ec82dec985d8bf5e7cebf5fe3d8ef7557b1) )
	ROM_LOAD64_BYTE( "ff_14.10b", 0x100002, 0x20000, CRC(0a2e9101) SHA1(6c8d550d2066cd53355ccf14ac1fd35914982094) )
	ROM_LOAD64_BYTE( "ff_06.10a", 0x100003, 0x20000, CRC(1c18f042) SHA1(f708296570fecad82a76dc59744873a2f5568ea1) )
	ROM_LOAD64_BYTE( "ff_25.7e",  0x100004, 0x20000, CRC(6e8181ea) SHA1(2c32bc0364650ee6ca0d24754a7a3401295ffcd5) )
	ROM_LOAD64_BYTE( "ff_18.7c",  0x100005, 0x20000, CRC(b19ede59) SHA1(7e79ad9f17b36e042d774bef3bbb44018332ca01) )
	ROM_LOAD64_BYTE( "ff_39.9h",  0x100006, 0x20000, CRC(9416b477) SHA1(f2310dfcfe960e8b822c07849b594d54dfc2b2ca) )
	ROM_LOAD64_BYTE( "ff_33.9f",  0x100007, 0x20000, CRC(7369fa07) SHA1(3b2750fe33729395217c96909b4b6c5f3d6e9943) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ff_23.13b",  0x00000, 0x08000, CRC(b8367eb5) SHA1(ec3db29fdd6200e9a8f4f8073a7e34aef731354f) )   // == ff_09.12b
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ffj_30.12e",  0x00000, 0x20000, CRC(375c66e7) SHA1(36189e23209ce4ae5d9cbabd1574540d0591e7b3) )   // == ff_18.11c
	ROM_LOAD( "ffj_31.13e",  0x20000, 0x20000, CRC(1ef137f9) SHA1(974b5e72aa28b87ebfa7438efbdfeda769dedf5e) )   // == ff_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s222b.1a",     0x0000, 0x0117, CRC(6d86b45e) SHA1(2b27646adaf1ca2f58e14754d6f7ef4fdca77fbe) )
	ROM_LOAD( "lwio.12e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 89625B-1 */
ROM_START( ffightj4 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "ff_36.12f",              0x00000, 0x20000, CRC(ed988977) SHA1(c718e989206bd2b68832c8fcb5667397d500ebac) ) // == ffu_30.11f
	ROM_LOAD16_BYTE( "ffj_42.12h",             0x00001, 0x20000, CRC(07bf1c21) SHA1(f21a939fd92607c7f54816dedbcb3c5818cf4183) ) // sldh - == ffu_35.11h
	ROM_LOAD16_BYTE( "ff_37.13f",              0x40000, 0x20000, CRC(dba5a476) SHA1(2f0176dd050f9630b914f1c1ca5d96215bcf567f) ) // == ffu_31.12f
	ROM_LOAD16_BYTE( "ffj_43.13h",             0x40001, 0x20000, CRC(fbeca028) SHA1(85eeed6a25b401d73d12896ca1e2bf7402c921ee) ) // sldh
	ROM_LOAD16_BYTE( "ff_34.10f",              0x80000, 0x20000, CRC(0c8dc3fc) SHA1(edcce3efd9cdd131ef0c96df15a68722d5c3498e) ) // == ff-32m.8h
	ROM_LOAD16_BYTE( "ffj_40.10h",             0x80001, 0x20000, CRC(8075bab9) SHA1(f9c7405133f6fc5557c90e60e8ccc459e4f6fd7d) ) // == ff-32m.8h
	ROM_LOAD16_BYTE( "ff_35.11f",              0xc0000, 0x20000, CRC(4a934121) SHA1(3982c261582755a0eac340d6d7ed96e6c263c8b6) ) // == ff-32m.8h
	ROM_LOAD16_BYTE( "ffj_41.11h",             0xc0001, 0x20000, CRC(2af68154) SHA1(7d549cb38650b4b79c68ad6d0dfcefdd62be4e99) ) // == ff-32m.8h

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_BYTE( "ff_09.4b",  0x000000, 0x20000, CRC(5b116d0d) SHA1(a24e829fdfa043bd27b508d7cc0788ad80fd180e) )
	ROM_LOAD64_BYTE( "ff_01.4a",  0x000001, 0x20000, CRC(815b1797) SHA1(549e5eefc8f607fec1c954ba715ff21b8e44a5aa) )
	ROM_LOAD64_BYTE( "ff_13.9b",  0x000002, 0x20000, CRC(8721a7da) SHA1(39b2b324fd7810342503f23695e423f364a6294d) )
	ROM_LOAD64_BYTE( "ff_05.9a",  0x000003, 0x20000, CRC(d0fcd4b5) SHA1(97ebcbead0cca7e425143c500c433dbcf9cadcc2) )
	ROM_LOAD64_BYTE( "ff_24.5e",  0x000004, 0x20000, CRC(a1ab607a) SHA1(56784c028b82d9e2affd9610f56fde57063e4c28) )
	ROM_LOAD64_BYTE( "ff_17.5c",  0x000005, 0x20000, CRC(2dc18cf4) SHA1(5e3bd895600cd30d561a75a2fcb6cc8bc84f4bd1) )
	ROM_LOAD64_BYTE( "ffj_38.8h", 0x000006, 0x20000, CRC(6535a57f) SHA1(f4da9ec13cad7e3287e34dcceb0eb2d20107bad6) ) // == ff_38.8h
	ROM_LOAD64_BYTE( "ff_32.8f",  0x000007, 0x20000, CRC(c8bc4a57) SHA1(3eaf2b4e910fe1f79154020122d786d23a2e594a) )
	ROM_LOAD64_BYTE( "ff_10.5b",  0x100000, 0x20000, CRC(624a924a) SHA1(48fd0498f9ed54003bf6578fbcbc8b7e90a195eb) )
	ROM_LOAD64_BYTE( "ff_02.5a",  0x100001, 0x20000, CRC(5d91f694) SHA1(e0ea9ec82dec985d8bf5e7cebf5fe3d8ef7557b1) )
	ROM_LOAD64_BYTE( "ff_14.10b", 0x100002, 0x20000, CRC(0a2e9101) SHA1(6c8d550d2066cd53355ccf14ac1fd35914982094) )
	ROM_LOAD64_BYTE( "ff_06.10a", 0x100003, 0x20000, CRC(1c18f042) SHA1(f708296570fecad82a76dc59744873a2f5568ea1) )
	ROM_LOAD64_BYTE( "ff_25.7e",  0x100004, 0x20000, CRC(6e8181ea) SHA1(2c32bc0364650ee6ca0d24754a7a3401295ffcd5) )
	ROM_LOAD64_BYTE( "ff_18.7c",  0x100005, 0x20000, CRC(b19ede59) SHA1(7e79ad9f17b36e042d774bef3bbb44018332ca01) )
	ROM_LOAD64_BYTE( "ffj_39.9h", 0x100006, 0x20000, CRC(9416b477) SHA1(f2310dfcfe960e8b822c07849b594d54dfc2b2ca) ) // == ff_39.9h
	ROM_LOAD64_BYTE( "ff_33.9f",  0x100007, 0x20000, CRC(7369fa07) SHA1(3b2750fe33729395217c96909b4b6c5f3d6e9943) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ff_23.13b",  0x00000, 0x08000, CRC(b8367eb5) SHA1(ec3db29fdd6200e9a8f4f8073a7e34aef731354f) )   // == ff_09.12b
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ff_30.12c",  0x00000, 0x20000, CRC(375c66e7) SHA1(36189e23209ce4ae5d9cbabd1574540d0591e7b3) )   // == ff_18.11c
	ROM_LOAD( "ff_31.13c",  0x20000, 0x20000, CRC(1ef137f9) SHA1(974b5e72aa28b87ebfa7438efbdfeda769dedf5e) )   // == ff_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s222b.1a",     0x0000, 0x0117, CRC(6d86b45e) SHA1(2b27646adaf1ca2f58e14754d6f7ef4fdca77fbe) )
	ROM_LOAD( "lwio.12e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )    // pal verification required
ROM_END

/* B-Board 91634B-2 */
/* This hack uses an official and original Final Fight JAP CPS-B-21 Board. The EPROMS have been modified and phoenixed.
   The Board uses a GAL instead of the classic PAL located in position "1A". There is no ROM check at the start and no logo
   Capcom (instead a chinese logo... maybe the bootleggers logo). Even if it's a JAP Board, the game intro is in english. */
ROM_START( ffightjh )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "ff_23.8f", 0x00000, 0x80000, CRC(ae3dda7f) SHA1(5f08ce1e6b6b0d45994d3354d59ef79f489c7ad7) )
	ROM_LOAD16_WORD_SWAP( "ff_22.7f", 0x80000, 0x80000, CRC(b2d5a3aa) SHA1(b60f7480d3d3ceebad4c21025394bacc154d7042) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "ff_1.3a",  0x000000, 0x80000, CRC(969d18e2) SHA1(7a30b7434e34ec98838a9ab8d953b2ef467d562e) )
	ROM_LOAD64_WORD( "ff_2.4a",  0x000002, 0x80000, CRC(02b59f99) SHA1(c1cb2bbf95c2006baad58b0a5a7278f1a8999901) )
	ROM_LOAD64_WORD( "ff_3.5a",  0x000004, 0x80000, CRC(01d507ae) SHA1(d7813daa5bd74bebdbbea578acb20ad3c8816997) )
	ROM_LOAD64_WORD( "ff_4.6a",  0x000006, 0x80000, CRC(f7c4ceb0) SHA1(3e9cf6379f06c5a736b4a0aadb7fbda914b91190) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ff_9.12a",  0x00000, 0x08000, CRC(b8367eb5) SHA1(ec3db29fdd6200e9a8f4f8073a7e34aef731354f) ) // == ff_09.12b
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ff_18.11c", 0x00000, 0x20000, CRC(375c66e7) SHA1(36189e23209ce4ae5d9cbabd1574540d0591e7b3) )
	ROM_LOAD( "ff_19.12c", 0x20000, 0x20000, CRC(1ef137f9) SHA1(974b5e72aa28b87ebfa7438efbdfeda769dedf5e) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "gal16v8a-15lp.1a",     0x0000, 0x0117, NO_DUMP )
	ROM_LOAD( "iob1.12d",             0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",            0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
ROM_END

ROM_START( ffightae )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "ff-23m.8h", 0x00000, 0x80000, CRC(86def74f) SHA1(5206cc13bfe40fb4f9c3677629aee89099623ee6) )
	ROM_LOAD16_WORD_SWAP( "ff-22m.7h", 0x80000, 0x80000, CRC(cbdd8689) SHA1(a75918ee837dfccdd4fd02b716928a2de2003103) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "ff-5m.7a", 0x000000, 0x80000, CRC(91a909bd) SHA1(09621cb33a9c26798b1bba186dceb02e5f126e1a) )
	ROM_LOAD64_WORD( "ff-7m.9a", 0x000002, 0x80000, CRC(89f8b4cd) SHA1(c169c445686d3c79eae2dc42460b8194c491ccb0) )
	ROM_LOAD64_WORD( "ff-1m.3a", 0x000004, 0x80000, CRC(d5469303) SHA1(0c1e33a87eb3ef79e6a5ba80753eb495284e666c) )
	ROM_LOAD64_WORD( "ff-3m.5a", 0x000006, 0x80000, CRC(0c6302bf) SHA1(03ee13a67a8a3b92fac462623ace752d77b9e9f1) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ff_09.12b", 0x00000, 0x08000, CRC(b8367eb5) SHA1(ec3db29fdd6200e9a8f4f8073a7e34aef731354f) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ff_18.11c", 0x00000, 0x20000, CRC(375c66e7) SHA1(36189e23209ce4ae5d9cbabd1574540d0591e7b3) )
	ROM_LOAD( "ff_19.12c", 0x20000, 0x20000, CRC(1ef137f9) SHA1(974b5e72aa28b87ebfa7438efbdfeda769dedf5e) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s224bn.1a",    0x0000, 0x0117, CRC(31367e94) SHA1(87b42d20bfe078c8352f7c2a40f7ce6b1be4a1af) ) /* GAL16V8 */
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( 1941 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "41em_30.11f",    0x00000, 0x20000, CRC(4249ec61) SHA1(5323cfa6938e6d95db8469f09b2fb5b5c5068bfc) ) /* label is 41EM_30, pcb verified */
	ROM_LOAD16_BYTE( "41em_35.11h",    0x00001, 0x20000, CRC(ddbee5eb) SHA1(0fef53398f4e2cd6ccc7bc122dd893e0a4e2e052) ) /* label is 41EM_35, pcb verified */
	ROM_LOAD16_BYTE( "41em_31.12f",    0x40000, 0x20000, CRC(584e88e5) SHA1(af254408d939cc439b5653e60afbccdf59777085) ) /* label is 41EM_31, pcb verified */
	ROM_LOAD16_BYTE( "41em_36.12h",    0x40001, 0x20000, CRC(3cfc31d0) SHA1(7c0614e4e25502ff2d18aa4a97c91bf05ead875f) ) /* label is 41EM_36, pcb verified */
	ROM_LOAD16_WORD_SWAP( "41-32m.8h", 0x80000, 0x80000, CRC(4e9648ca) SHA1(d8e67e6e3a6dc79053e4f56cfd83431385ea7611) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "41-5m.7a", 0x000000, 0x80000, CRC(01d1cb11) SHA1(621e5377d1aaa9f7270d85bea1bdeef6721cdd05) )
	ROM_LOAD64_WORD( "41-7m.9a", 0x000002, 0x80000, CRC(aeaa3509) SHA1(6124ef06d9dfdd879181856bd49853f1800c3b87) )
	ROM_LOAD64_WORD( "41-1m.3a", 0x000004, 0x80000, CRC(ff77985a) SHA1(7e08df3a829bf9617470a46c79b713d4d9ebacae) )
	ROM_LOAD64_WORD( "41-3m.5a", 0x000006, 0x80000, CRC(983be58f) SHA1(83a4decdd775f859240771269b8af3a5981b244c) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "41_9.12b",  0x00000, 0x08000, CRC(0f9d8527) SHA1(3a00dd5772f38081fde11d8d61ba467379e2a636) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "41_18.11c", 0x00000, 0x20000, CRC(d1f15aeb) SHA1(88089383f2d54fc97026a67f067d448eee5bd0c2) )
	ROM_LOAD( "41_19.12c", 0x20000, 0x20000, CRC(15aec3a6) SHA1(8153c03aba005bab62bf0e8b3d15ec1c346326fd) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "yi24b.1a",     0x0000, 0x0117, CRC(3004dcdf) SHA1(a73d86d1545af1d99a03d88af83449ec82b03c5c) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( 1941r1 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "41e_30.11f",     0x00000, 0x20000, CRC(9deb1e75) SHA1(68d9f91bef6a5c9e1bcbf286629aed6b37b4acb9) )
	ROM_LOAD16_BYTE( "41e_35.11h",     0x00001, 0x20000, CRC(d63942b3) SHA1(b4bc7d06dcefbc075d316f2d31abbd4c7a99dbae) )
	ROM_LOAD16_BYTE( "41e_31.12f",     0x40000, 0x20000, CRC(df201112) SHA1(d84f63bffeb9255cbabc02f23d7511f9b3c6a96c) )
	ROM_LOAD16_BYTE( "41e_36.12h",     0x40001, 0x20000, CRC(816a818f) SHA1(3e491a30352b71ddd775142f3a80cdde480b669f) )
	ROM_LOAD16_WORD_SWAP( "41-32m.8h", 0x80000, 0x80000, CRC(4e9648ca) SHA1(d8e67e6e3a6dc79053e4f56cfd83431385ea7611) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "41-5m.7a", 0x000000, 0x80000, CRC(01d1cb11) SHA1(621e5377d1aaa9f7270d85bea1bdeef6721cdd05) )
	ROM_LOAD64_WORD( "41-7m.9a", 0x000002, 0x80000, CRC(aeaa3509) SHA1(6124ef06d9dfdd879181856bd49853f1800c3b87) )
	ROM_LOAD64_WORD( "41-1m.3a", 0x000004, 0x80000, CRC(ff77985a) SHA1(7e08df3a829bf9617470a46c79b713d4d9ebacae) )
	ROM_LOAD64_WORD( "41-3m.5a", 0x000006, 0x80000, CRC(983be58f) SHA1(83a4decdd775f859240771269b8af3a5981b244c) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "41_9.12b",  0x00000, 0x08000, CRC(0f9d8527) SHA1(3a00dd5772f38081fde11d8d61ba467379e2a636) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "41_18.11c", 0x00000, 0x20000, CRC(d1f15aeb) SHA1(88089383f2d54fc97026a67f067d448eee5bd0c2) )
	ROM_LOAD( "41_19.12c", 0x20000, 0x20000, CRC(15aec3a6) SHA1(8153c03aba005bab62bf0e8b3d15ec1c346326fd) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "yi24b.1a",     0x0000, 0x0117, CRC(3004dcdf) SHA1(a73d86d1545af1d99a03d88af83449ec82b03c5c) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( 1941u )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "41u_30.11f",     0x00000, 0x20000, CRC(be5439d0) SHA1(a62e0aa4f13f504c0eded99d731f7c820c51cb3d) )
	ROM_LOAD16_BYTE( "41u_35.11h",     0x00001, 0x20000, CRC(6ac96595) SHA1(efff8185fd1aee29d9b5164a9aac61f77f769ae4) )
	ROM_LOAD16_BYTE( "41u_31.12f",     0x40000, 0x20000, CRC(9811d6eb) SHA1(6dda86a3a726a2b5459f0b90f58343966e9783cc) )
	ROM_LOAD16_BYTE( "41u_36.12h",     0x40001, 0x20000, CRC(a87e6137) SHA1(d4b63a0c14a35ebd61cddd01cdc6fc955d521ae2) )
	ROM_LOAD16_WORD_SWAP( "41-32m.8h", 0x80000, 0x80000, CRC(4e9648ca) SHA1(d8e67e6e3a6dc79053e4f56cfd83431385ea7611) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "41-5m.7a",  0x000000, 0x80000, CRC(01d1cb11) SHA1(621e5377d1aaa9f7270d85bea1bdeef6721cdd05) )
	ROM_LOAD64_WORD( "41-7m.9a",  0x000002, 0x80000, CRC(aeaa3509) SHA1(6124ef06d9dfdd879181856bd49853f1800c3b87) )
	ROM_LOAD64_WORD( "41-1m.3a",  0x000004, 0x80000, CRC(ff77985a) SHA1(7e08df3a829bf9617470a46c79b713d4d9ebacae) )
	ROM_LOAD64_WORD( "41-3m.5a",  0x000006, 0x80000, CRC(983be58f) SHA1(83a4decdd775f859240771269b8af3a5981b244c) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "41_9.12b",   0x00000, 0x08000, CRC(0f9d8527) SHA1(3a00dd5772f38081fde11d8d61ba467379e2a636) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "41e_18.11c", 0x00000, 0x20000, CRC(d1f15aeb) SHA1(88089383f2d54fc97026a67f067d448eee5bd0c2) )    // == 41_18.11c /* label is 41E_18, pcb verified */
	ROM_LOAD( "41_19.12c",  0x20000, 0x20000, CRC(15aec3a6) SHA1(8153c03aba005bab62bf0e8b3d15ec1c346326fd) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "yi24b.1a",     0x0000, 0x0117, CRC(3004dcdf) SHA1(a73d86d1545af1d99a03d88af83449ec82b03c5c) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 89625B-1 */
ROM_START( 1941j )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "41_36.12f", 0x00000, 0x20000, CRC(7fbd42ab) SHA1(4e52a599e3099bf3cccabb89152c69f216fde79e) )
	ROM_LOAD16_BYTE( "41_42.12h", 0x00001, 0x20000, CRC(c7781f89) SHA1(7e99c433de0c903791ae153a3cc8632042b0a90d) )
	ROM_LOAD16_BYTE( "41_37.13f", 0x40000, 0x20000, CRC(c6464b0b) SHA1(abef422d891d32334a858d49599f1ef7cf0db45d) )
	ROM_LOAD16_BYTE( "41_43.13h", 0x40001, 0x20000, CRC(440fc0b5) SHA1(e725535533c25a2c80a45a2200bbfd0dcda5ed97) )
	ROM_LOAD16_BYTE( "41_34.10f", 0x80000, 0x20000, CRC(b5f341ec) SHA1(95c740332e9e05250979a098e951600ca89cfde4) )  // == 41-32m.8h
	ROM_LOAD16_BYTE( "41_40.10h", 0x80001, 0x20000, CRC(3979837d) SHA1(e35bca5cd6f7559447c553c7866a3934080ca018) )  // == 41-32m.8h
	ROM_LOAD16_BYTE( "41_35.11f", 0xc0000, 0x20000, CRC(95cc979a) SHA1(96a22d2accdcd1ed3829ab0e1f17c1615f8bb2a9) )  // == 41-32m.8h
	ROM_LOAD16_BYTE( "41_41.11h", 0xc0001, 0x20000, CRC(57496819) SHA1(fe705a25a2c6523c3bc0105f4b183931c5ffcc81) )  // == 41-32m.8h

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_BYTE( "41_09.4b",  0x000000, 0x20000, CRC(be1b6bc2) SHA1(0b11efb7adf6bb9c5fdef702171edd38c149e338) ) // == 41-5m.7a
	ROM_LOAD64_BYTE( "41_01.4a",  0x000001, 0x20000, CRC(d8946fc1) SHA1(e18d2bd5517a9a74973f5a505e59e400db3843e2) ) // == 41-5m.7a
	ROM_LOAD64_BYTE( "41_13.9b",  0x000002, 0x20000, CRC(2e06d0ec) SHA1(b7c886dce6bc5eefa34e39faec658dd9df34f82c) ) // == 41-7m.9a
	ROM_LOAD64_BYTE( "41_05.9a",  0x000003, 0x20000, CRC(d8ba28e0) SHA1(dd1908166b32cf3bb3222f6d3febc21371463395) ) // == 41-7m.9a
	ROM_LOAD64_BYTE( "41_24.5e",  0x000004, 0x20000, CRC(5aa43cee) SHA1(15e1aa2fee8deb25eefba067395961fee3e4c84d) ) // == 41-1m.3a
	ROM_LOAD64_BYTE( "41_17.5c",  0x000005, 0x20000, CRC(bbeff902) SHA1(1bf421072387ab7d59b57ecd33cb5ae6fd8ecd06) ) // == 41-1m.3a
	ROM_LOAD64_BYTE( "41_38.8h",  0x000006, 0x20000, CRC(8889c0aa) SHA1(23b74cc78fe1057c1776ea1f7371801441125a85) ) // == 41-3m.5a
	ROM_LOAD64_BYTE( "41_32.8f",  0x000007, 0x20000, CRC(f0168249) SHA1(1ce2330ea4460e7e72f16a1f7edbfa8abba6b5a0) ) // == 41-3m.5a
	ROM_LOAD64_BYTE( "41_10.5b",  0x100000, 0x20000, CRC(b7eb6a6d) SHA1(444b49e81cd46e90a9e1dfcf465f19c38189b2d7) ) // == 41-5m.7a
	ROM_LOAD64_BYTE( "41_02.5a",  0x100001, 0x20000, CRC(802e8153) SHA1(baf19220c53949dac0d0b75368d5f1ee6c32b619) ) // == 41-5m.7a
	ROM_LOAD64_BYTE( "41_14.10b", 0x100002, 0x20000, CRC(5a33f676) SHA1(96794463ddfae7ce4a7d7e6b65ff1b8da7b3dd74) ) // == 41-7m.9a
	ROM_LOAD64_BYTE( "41_06.10a", 0x100003, 0x20000, CRC(4e53650b) SHA1(a5b19b1ce38f04296dee38d35015869607ecc260) ) // == 41-7m.9a
	ROM_LOAD64_BYTE( "41_25.7e",  0x100004, 0x20000, CRC(94add360) SHA1(6f3542f028bdc453d0254de599b124240ea98259) ) // == 41-1m.3a
	ROM_LOAD64_BYTE( "41_18.7c",  0x100005, 0x20000, CRC(a5e1c1f3) SHA1(1961ad3c8965c51da8a19a7283333f3a19bb2030) ) // == 41-1m.3a
	ROM_LOAD64_BYTE( "41_39.9h",  0x100006, 0x20000, CRC(5b5c3949) SHA1(485b1baecee6bb9a6667baf404375fc758bdfb76) ) // == 41-3m.5a
	ROM_LOAD64_BYTE( "41_33.9f",  0x100007, 0x20000, CRC(7a31b0e2) SHA1(f2eca12f4e89266d923a94d0837c555bc1c6cb29) ) // == 41-3m.5a

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "41_23.13b",  0x00000, 0x08000, CRC(0f9d8527) SHA1(3a00dd5772f38081fde11d8d61ba467379e2a636) )    // == 41_9.12b
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "41_30.12c",  0x00000, 0x20000, CRC(d1f15aeb) SHA1(88089383f2d54fc97026a67f067d448eee5bd0c2) )    // == 41_18.11c
	ROM_LOAD( "41_31.13c",  0x20000, 0x20000, CRC(15aec3a6) SHA1(8153c03aba005bab62bf0e8b3d15ec1c346326fd) )    // == 41_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "yi22b.1a",     0x0000, 0x0117, CRC(b5cad2a0) SHA1(3b1232cf821617bc9819fa7b9b3f4e7fc66976f3) )
	ROM_LOAD( "lwio.12e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( mercs )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "so2_30e.11f",     0x00000, 0x20000, CRC(e17f9bf7) SHA1(f44bb378de428b429c97a21f74829182d3187ace) )
	ROM_LOAD16_BYTE( "so2_35e.11h",     0x00001, 0x20000, CRC(78e63575) SHA1(5776de0daaaedd0dec2cec8d088a0fd8bb3d4dbe) )
	ROM_LOAD16_BYTE( "so2_31e.12f",     0x40000, 0x20000, CRC(51204d36) SHA1(af288fc369d092f38ea73be967705aacade06f28) )
	ROM_LOAD16_BYTE( "so2_36e.12h",     0x40001, 0x20000, CRC(9cfba8b4) SHA1(df8ee5e3a68f056f68f096c46fdb548f63d29446) )
	ROM_LOAD16_WORD_SWAP( "so2-32m.8h", 0x80000, 0x80000, CRC(2eb5cf0c) SHA1(e0d765fb6957d156ffd40cabf51ba6098cbbeb19) )

	ROM_REGION( 0x300000, "gfx", 0 )
	ROM_LOAD64_WORD( "so2-6m.8a",  0x000000, 0x80000, CRC(aa6102af) SHA1(4a45f3547a3640f256e5e20bfd72784f880f03f5) )
	ROM_LOAD64_WORD( "so2-8m.10a", 0x000002, 0x80000, CRC(839e6869) SHA1(7741141a9f1b1e2956edc1d11f9cc3974390c4ed) )
	ROM_LOAD64_WORD( "so2-2m.4a",  0x000004, 0x80000, CRC(597c2875) SHA1(440bd04db2c121a6976e5e1027071d28812942d3) )
	ROM_LOAD64_WORD( "so2-4m.6a",  0x000006, 0x80000, CRC(912a9ca0) SHA1(b226a4a388e57e23d7a7559773ebee434125a2e4) )
	ROM_LOAD64_BYTE( "so2_24.7d",  0x200000, 0x20000, CRC(3f254efe) SHA1(5db36eb98a6d3c7acccb561d92c1988d1330cbbf) )
	ROM_LOAD64_BYTE( "so2_14.7c",  0x200001, 0x20000, CRC(f5a8905e) SHA1(fada8b635d490c06b75711ed505a025bb0aa4454) )
	ROM_LOAD64_BYTE( "so2_26.9d",  0x200002, 0x20000, CRC(f3aa5a4a) SHA1(bcb3396de5524fffd4110bfbeeeca1c936990eb3) )
	ROM_LOAD64_BYTE( "so2_16.9c",  0x200003, 0x20000, CRC(b43cd1a8) SHA1(01c2bb802469848a172968802a674c0045a8b8dc) )
	ROM_LOAD64_BYTE( "so2_20.3d",  0x200004, 0x20000, CRC(8ca751a3) SHA1(e93bbe7311f14e7e3cbfb42b83fd7fee4bb9cefc) )
	ROM_LOAD64_BYTE( "so2_10.3c",  0x200005, 0x20000, CRC(e9f569fd) SHA1(39ae9eacdf1f35ef90d131444c37958d7aaf7238) )
	ROM_LOAD64_BYTE( "so2_22.5d",  0x200006, 0x20000, CRC(fce9a377) SHA1(5de5f696f63326f2cb4c38bcb05e07bcf2246071) )
	ROM_LOAD64_BYTE( "so2_12.5c",  0x200007, 0x20000, CRC(b7df8a06) SHA1(b42cb0d3f55a1e8fe8afbbd9aeae50074cdc5f08) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "so2_09.12b",  0x00000, 0x08000, CRC(d09d7c7a) SHA1(8e8532be08818c855d9c3ce45716eb07cfab5767) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "so2_18.11c",  0x00000, 0x20000, CRC(bbea1643) SHA1(d43d68a120550067bf0b181f88687ad230cd7908) )
	ROM_LOAD( "so2_19.12c",  0x20000, 0x20000, CRC(ac58aa71) SHA1(93102272e358bc49d3936302efdc5bb68df84d68) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "o224b.1a",     0x0000, 0x0117, CRC(c211c8cd) SHA1(d9464792e663549e6ad20aac6484622298f88a78) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c628",         0x0000, 0x0117, CRC(662e090f) SHA1(3e3cb3b2ddfaac4772f38b859946ced717b84698) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( mercsu )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "so2_30a.11f",     0x00000, 0x20000, CRC(e4e725d7) SHA1(b0454dedeb741a7dd4ceb18bac958417ca74a7e6) )
	ROM_LOAD16_BYTE( "so2_35a.11h",     0x00001, 0x20000, CRC(e7843445) SHA1(192c85ced637e05b37ed889246ebb73e792e984b) )
	ROM_LOAD16_BYTE( "so2_31a.12f",     0x40000, 0x20000, CRC(c0b91dea) SHA1(5c1d086ae09e4f66384a03994b3c5e12d80582ff) )
	ROM_LOAD16_BYTE( "so2_36a.12h",     0x40001, 0x20000, CRC(591edf6c) SHA1(68d77e21fe32e0b95d2fabe40bc1cadd419ab0bd) )
	ROM_LOAD16_WORD_SWAP( "so2-32m.8h", 0x80000, 0x80000, CRC(2eb5cf0c) SHA1(e0d765fb6957d156ffd40cabf51ba6098cbbeb19) )

	ROM_REGION( 0x300000, "gfx", 0 )
	ROM_LOAD64_WORD( "so2-6m.8a",  0x000000, 0x80000, CRC(aa6102af) SHA1(4a45f3547a3640f256e5e20bfd72784f880f03f5) )
	ROM_LOAD64_WORD( "so2-8m.10a", 0x000002, 0x80000, CRC(839e6869) SHA1(7741141a9f1b1e2956edc1d11f9cc3974390c4ed) )
	ROM_LOAD64_WORD( "so2-2m.4a",  0x000004, 0x80000, CRC(597c2875) SHA1(440bd04db2c121a6976e5e1027071d28812942d3) )
	ROM_LOAD64_WORD( "so2-4m.6a",  0x000006, 0x80000, CRC(912a9ca0) SHA1(b226a4a388e57e23d7a7559773ebee434125a2e4) )
	ROM_LOAD64_BYTE( "so2_24.7d",  0x200000, 0x20000, CRC(3f254efe) SHA1(5db36eb98a6d3c7acccb561d92c1988d1330cbbf) )
	ROM_LOAD64_BYTE( "so2_14.7c",  0x200001, 0x20000, CRC(f5a8905e) SHA1(fada8b635d490c06b75711ed505a025bb0aa4454) )
	ROM_LOAD64_BYTE( "so2_26.9d",  0x200002, 0x20000, CRC(f3aa5a4a) SHA1(bcb3396de5524fffd4110bfbeeeca1c936990eb3) )
	ROM_LOAD64_BYTE( "so2_16.9c",  0x200003, 0x20000, CRC(b43cd1a8) SHA1(01c2bb802469848a172968802a674c0045a8b8dc) )
	ROM_LOAD64_BYTE( "so2_20.3d",  0x200004, 0x20000, CRC(8ca751a3) SHA1(e93bbe7311f14e7e3cbfb42b83fd7fee4bb9cefc) )
	ROM_LOAD64_BYTE( "so2_10.3c",  0x200005, 0x20000, CRC(e9f569fd) SHA1(39ae9eacdf1f35ef90d131444c37958d7aaf7238) )
	ROM_LOAD64_BYTE( "so2_22.5d",  0x200006, 0x20000, CRC(fce9a377) SHA1(5de5f696f63326f2cb4c38bcb05e07bcf2246071) )
	ROM_LOAD64_BYTE( "so2_12.5c",  0x200007, 0x20000, CRC(b7df8a06) SHA1(b42cb0d3f55a1e8fe8afbbd9aeae50074cdc5f08) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "so2_09.12b",  0x00000, 0x08000, CRC(d09d7c7a) SHA1(8e8532be08818c855d9c3ce45716eb07cfab5767) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "so2_18.11c",  0x00000, 0x20000, CRC(bbea1643) SHA1(d43d68a120550067bf0b181f88687ad230cd7908) )
	ROM_LOAD( "so2_19.12c",  0x20000, 0x20000, CRC(ac58aa71) SHA1(93102272e358bc49d3936302efdc5bb68df84d68) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "o224b.1a",     0x0000, 0x0117, CRC(c211c8cd) SHA1(d9464792e663549e6ad20aac6484622298f88a78) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c628",         0x0000, 0x0117, CRC(662e090f) SHA1(3e3cb3b2ddfaac4772f38b859946ced717b84698) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( mercsur1 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "so2_30.11f",      0x00000, 0x20000, CRC(e17f9bf7) SHA1(f44bb378de428b429c97a21f74829182d3187ace) )    // == so2_30e.11f
	ROM_LOAD16_BYTE( "so2_35.11h",      0x00001, 0x20000, CRC(4477df61) SHA1(e9b42357c7073c098e8fde7e7d0e4a6e3062fd0d) )
	ROM_LOAD16_BYTE( "so2_31.12f",      0x40000, 0x20000, CRC(51204d36) SHA1(af288fc369d092f38ea73be967705aacade06f28) )    // == so2_31e.12f
	ROM_LOAD16_BYTE( "so2_36.12h",      0x40001, 0x20000, CRC(9cfba8b4) SHA1(df8ee5e3a68f056f68f096c46fdb548f63d29446) )    // == so2_36e.12h
	ROM_LOAD16_WORD_SWAP( "so2-32m.8h", 0x80000, 0x80000, CRC(2eb5cf0c) SHA1(e0d765fb6957d156ffd40cabf51ba6098cbbeb19) )

	ROM_REGION( 0x300000, "gfx", 0 )
	ROM_LOAD64_WORD( "so2-6m.8a",  0x000000, 0x80000, CRC(aa6102af) SHA1(4a45f3547a3640f256e5e20bfd72784f880f03f5) )
	ROM_LOAD64_WORD( "so2-8m.10a", 0x000002, 0x80000, CRC(839e6869) SHA1(7741141a9f1b1e2956edc1d11f9cc3974390c4ed) )
	ROM_LOAD64_WORD( "so2-2m.4a",  0x000004, 0x80000, CRC(597c2875) SHA1(440bd04db2c121a6976e5e1027071d28812942d3) )
	ROM_LOAD64_WORD( "so2-4m.6a",  0x000006, 0x80000, CRC(912a9ca0) SHA1(b226a4a388e57e23d7a7559773ebee434125a2e4) )
	ROM_LOAD64_BYTE( "so2_24.7d",  0x200000, 0x20000, CRC(3f254efe) SHA1(5db36eb98a6d3c7acccb561d92c1988d1330cbbf) )
	ROM_LOAD64_BYTE( "so2_14.7c",  0x200001, 0x20000, CRC(f5a8905e) SHA1(fada8b635d490c06b75711ed505a025bb0aa4454) )
	ROM_LOAD64_BYTE( "so2_26.9d",  0x200002, 0x20000, CRC(f3aa5a4a) SHA1(bcb3396de5524fffd4110bfbeeeca1c936990eb3) )
	ROM_LOAD64_BYTE( "so2_16.9c",  0x200003, 0x20000, CRC(b43cd1a8) SHA1(01c2bb802469848a172968802a674c0045a8b8dc) )
	ROM_LOAD64_BYTE( "so2_20.3d",  0x200004, 0x20000, CRC(8ca751a3) SHA1(e93bbe7311f14e7e3cbfb42b83fd7fee4bb9cefc) )
	ROM_LOAD64_BYTE( "so2_10.3c",  0x200005, 0x20000, CRC(e9f569fd) SHA1(39ae9eacdf1f35ef90d131444c37958d7aaf7238) )
	ROM_LOAD64_BYTE( "so2_22.5d",  0x200006, 0x20000, CRC(fce9a377) SHA1(5de5f696f63326f2cb4c38bcb05e07bcf2246071) )
	ROM_LOAD64_BYTE( "so2_12.5c",  0x200007, 0x20000, CRC(b7df8a06) SHA1(b42cb0d3f55a1e8fe8afbbd9aeae50074cdc5f08) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "so2_09.12b",  0x00000, 0x08000, CRC(d09d7c7a) SHA1(8e8532be08818c855d9c3ce45716eb07cfab5767) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "so2_18.11c",  0x00000, 0x20000, CRC(bbea1643) SHA1(d43d68a120550067bf0b181f88687ad230cd7908) )
	ROM_LOAD( "so2_19.12c",  0x20000, 0x20000, CRC(ac58aa71) SHA1(93102272e358bc49d3936302efdc5bb68df84d68) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "o224b.1a",     0x0000, 0x0117, CRC(c211c8cd) SHA1(d9464792e663549e6ad20aac6484622298f88a78) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c628",         0x0000, 0x0117, CRC(662e090f) SHA1(3e3cb3b2ddfaac4772f38b859946ced717b84698) )
ROM_END

/* B-Board 89625B-1 */
ROM_START( mercsj )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "so2_36.12f", 0x00000, 0x20000, CRC(e17f9bf7) SHA1(f44bb378de428b429c97a21f74829182d3187ace) ) // == so2_30e.11f
	ROM_LOAD16_BYTE( "so2_42.12h", 0x00001, 0x20000, CRC(2c3884c6) SHA1(98c3e93741d2344fe0a699aacdc5038bdd9007a0) )
	ROM_LOAD16_BYTE( "so2_37.13f", 0x40000, 0x20000, CRC(51204d36) SHA1(af288fc369d092f38ea73be967705aacade06f28) ) // == so2_31e.12f
	ROM_LOAD16_BYTE( "so2_43.13h", 0x40001, 0x20000, CRC(9cfba8b4) SHA1(df8ee5e3a68f056f68f096c46fdb548f63d29446) ) // == so2_36e.12h
	ROM_LOAD16_BYTE( "so2_34.10f", 0x80000, 0x20000, CRC(b8dae95f) SHA1(2db4a20afd40b772a16f1bee999a0b82d3379ac7) ) // == so2-32m.8h
	ROM_LOAD16_BYTE( "so2_40.10h", 0x80001, 0x20000, CRC(de37771c) SHA1(45e1e2ef4e46dbe8881e809d700fdd3d06a03c92) ) // == so2-32m.8h
	ROM_LOAD16_BYTE( "so2_35.11f", 0xc0000, 0x20000, CRC(7d24394d) SHA1(2f4cf51fcfc1b960b68cfe3f1b75914402f2c702) ) // == so2-32m.8h
	ROM_LOAD16_BYTE( "so2_41.11h", 0xc0001, 0x20000, CRC(914f85e0) SHA1(0b32adf2d3c83e187a5f670de18728726fabb731) ) // == so2-32m.8h

	ROM_REGION( 0x300000, "gfx", 0 )
	ROM_LOAD64_BYTE( "so2_09.4b",  0x000000, 0x20000, CRC(690c261d) SHA1(27219101fb62a0c0378e6f5d2f9c0bb5c9397193) )    // == so2-6m.8a
	ROM_LOAD64_BYTE( "so2_01.4a",  0x000001, 0x20000, CRC(31fd2715) SHA1(d80b7a93c3b4e5e482fe6bb9ed9d261377980351) )    // == so2-6m.8a
	ROM_LOAD64_BYTE( "so2_13.9b",  0x000002, 0x20000, CRC(b5e48282) SHA1(5f387929b4f1ebb8cb8c24138317d4208e2cf7c2) )    // == so2-8m.10a
	ROM_LOAD64_BYTE( "so2_05.9a",  0x000003, 0x20000, CRC(54bed82c) SHA1(4a45ceaec3f6162443b2c62b816612c19a609341) )    // == so2-8m.10a
	ROM_LOAD64_BYTE( "so2_24.5e",  0x000004, 0x20000, CRC(78b6f0cb) SHA1(679c39d71d3b73db088d0ab017e80fd8316045b5) )    // == so2-2m.4a
	ROM_LOAD64_BYTE( "so2_17.5c",  0x000005, 0x20000, CRC(e78bb308) SHA1(7c0c2cf4c79e0bb3c401ba8fdcc88ccc3ed64246) )    // == so2-2m.4a
	ROM_LOAD64_BYTE( "so2_38.8h",  0x000006, 0x20000, CRC(0010a9a2) SHA1(03fedaaa939b56afb1b376243542e68da68e2690) )    // == so2-4m.6a
	ROM_LOAD64_BYTE( "so2_32.8f",  0x000007, 0x20000, CRC(75dffc9a) SHA1(ef296e1c0742e0b5a6e104032f0492151e631691) )    // == so2-4m.6a
	ROM_LOAD64_BYTE( "so2_10.5b",  0x100000, 0x20000, CRC(2f871714) SHA1(8a39f120e3f50fc9a7e6cee659260b2f823fb0e0) )    // == so2-6m.8a
	ROM_LOAD64_BYTE( "so2_02.5a",  0x100001, 0x20000, CRC(b4b2a0b7) SHA1(ee42ed3de9021e8d08d6c7115f2de73476b93452) )    // == so2-6m.8a
	ROM_LOAD64_BYTE( "so2_14.10b", 0x100002, 0x20000, CRC(737a744b) SHA1(8e1477a67862f7c0c598d3d1a1f633946e7ab31b) )    // == so2-8m.10a
	ROM_LOAD64_BYTE( "so2_06.10a", 0x100003, 0x20000, CRC(9d756f51) SHA1(4e9773ee25f6a952fb4f541d37e5e46e4089fd07) )    // == so2-8m.10a
	ROM_LOAD64_BYTE( "so2_25.7e",  0x100004, 0x20000, CRC(6d0e05d6) SHA1(47cbec235bd4b250db007218dc357101ae453d1a) )    // == so2-2m.4a
	ROM_LOAD64_BYTE( "so2_18.7c",  0x100005, 0x20000, CRC(96f61f4e) SHA1(954334bd8c2d2b02175de60d6a181a23e723d040) )    // == so2-2m.4a
	ROM_LOAD64_BYTE( "so2_39.9h",  0x100006, 0x20000, CRC(d52ba336) SHA1(49550d316e575a4e64ea6a5f769f3cd716be6df5) )    // == so2-4m.6a
	ROM_LOAD64_BYTE( "so2_33.9f",  0x100007, 0x20000, CRC(39b90d25) SHA1(1089cca168a4abeb398fa93eddd4d9fff70d5db5) )    // == so2-4m.6a
	ROM_LOAD64_BYTE( "so2_11.7b",  0x200000, 0x20000, CRC(3f254efe) SHA1(5db36eb98a6d3c7acccb561d92c1988d1330cbbf) )    // == so2_24.7d
	ROM_LOAD64_BYTE( "so2_03.7a",  0x200001, 0x20000, CRC(f5a8905e) SHA1(fada8b635d490c06b75711ed505a025bb0aa4454) )    // == so2_14.7c
	ROM_LOAD64_BYTE( "so2_15.11b", 0x200002, 0x20000, CRC(f3aa5a4a) SHA1(bcb3396de5524fffd4110bfbeeeca1c936990eb3) )    // == so2_26.9d
	ROM_LOAD64_BYTE( "so2_07.11a", 0x200003, 0x20000, CRC(b43cd1a8) SHA1(01c2bb802469848a172968802a674c0045a8b8dc) )    // == so2_16.9c
	ROM_LOAD64_BYTE( "so2_26.8e",  0x200004, 0x20000, CRC(8ca751a3) SHA1(e93bbe7311f14e7e3cbfb42b83fd7fee4bb9cefc) )    // == so2_20.3d
	ROM_LOAD64_BYTE( "so2_19.8c",  0x200005, 0x20000, CRC(e9f569fd) SHA1(39ae9eacdf1f35ef90d131444c37958d7aaf7238) )    // == so2_10.3c
	ROM_LOAD64_BYTE( "so2_28.10e", 0x200006, 0x20000, CRC(fce9a377) SHA1(5de5f696f63326f2cb4c38bcb05e07bcf2246071) )    // == so2_22.5d
	ROM_LOAD64_BYTE( "so2_21.10c", 0x200007, 0x20000, CRC(b7df8a06) SHA1(b42cb0d3f55a1e8fe8afbbd9aeae50074cdc5f08) )    // == so2_12.5c

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "so2_23.13b",  0x00000, 0x08000, CRC(d09d7c7a) SHA1(8e8532be08818c855d9c3ce45716eb07cfab5767) )   // == so2_09.12b
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "so2_30.12c",  0x00000, 0x20000, CRC(bbea1643) SHA1(d43d68a120550067bf0b181f88687ad230cd7908) )   // == so2_18.11c
	ROM_LOAD( "so2_31.13c",  0x20000, 0x20000, CRC(ac58aa71) SHA1(93102272e358bc49d3936302efdc5bb68df84d68) )   // == so2_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "o222b.1a",     0x0000, 0x0117, NO_DUMP )
	ROM_LOAD( "lwio.12e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )    // pal verification required

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c628",         0x0000, 0x0117, CRC(662e090f) SHA1(3e3cb3b2ddfaac4772f38b859946ced717b84698) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( mtwins )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "che_30.11f",     0x00000, 0x20000, CRC(9a2a2db1) SHA1(57524e76311afc8ab5d5affa76c85cb1be5a1faf) )
	ROM_LOAD16_BYTE( "che_35.11h",     0x00001, 0x20000, CRC(a7f96b02) SHA1(b5fda02e5069f9e1cdafbacf98334510e9af8fcd) )
	ROM_LOAD16_BYTE( "che_31.12f",     0x40000, 0x20000, CRC(bbff8a99) SHA1(1f931fad9f43a1494f3b8dbcf910156d5b0bd458) )
	ROM_LOAD16_BYTE( "che_36.12h",     0x40001, 0x20000, CRC(0fa00c39) SHA1(6404d91590c5c521c8fe944a0aa7091e35b664ae) )
	ROM_LOAD16_WORD_SWAP( "ck-32m.8h", 0x80000, 0x80000, CRC(9b70bd41) SHA1(28ec37d9d0ace5b9fd212fdc02e0f13dc280c068) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "ck-5m.7a", 0x000000, 0x80000, CRC(4ec75f15) SHA1(a4669e3864009b01894406db784116ad5cd2eced) )
	ROM_LOAD64_WORD( "ck-7m.9a", 0x000002, 0x80000, CRC(d85d00d6) SHA1(ca6ddcbfbb0f9ad98dc19f09e879fdac5b62d168) )
	ROM_LOAD64_WORD( "ck-1m.3a", 0x000004, 0x80000, CRC(f33ca9d4) SHA1(480d90ff16f27777cc7d7de6925ed6378b35dc27) )
	ROM_LOAD64_WORD( "ck-3m.5a", 0x000006, 0x80000, CRC(0ba2047f) SHA1(efee13b955c2ded52700025cecbb9fb301098b61) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ch_09.12b", 0x00000, 0x08000, CRC(4d4255b7) SHA1(81a76b58043af7252a854b7efc4109957ef0e679) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ch_18.11c", 0x00000, 0x20000, CRC(f909e8de) SHA1(2dd5bd4076e7d5ded98b72919f868ea700df2e4f) )
	ROM_LOAD( "ch_19.12c", 0x20000, 0x20000, CRC(fc158cf7) SHA1(294b93d0aea60663ffe96364671552e944a1264b) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "ck24b.1a",     0x0000, 0x0117, CRC(bd99c448) SHA1(2692c158f76769b0743103cc3a6d1c5d1f4f52ec) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 89625B-1 */
ROM_START( chikij )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "chj_36a.12f", 0x00000, 0x20000, CRC(ec1328d8) SHA1(a7111f9c264c56d1c6474ec3ad90e394a32a86f6) )
	ROM_LOAD16_BYTE( "chj_42a.12h", 0x00001, 0x20000, CRC(4ae13503) SHA1(c47db0445e107ad4fb62b74e277a7dc2b4d9b7ea) )
	ROM_LOAD16_BYTE( "chj_37a.13f", 0x40000, 0x20000, CRC(46d2cf7b) SHA1(5cb7ed3003d89a08882d4dcd326c8fd9430f0eac) )
	ROM_LOAD16_BYTE( "chj_43a.13h", 0x40001, 0x20000, CRC(8d387fe8) SHA1(7832ecd487b5ef4e49b5ea78e80e52f8e2dcaa17) )
	ROM_LOAD16_BYTE( "ch_34.10f",   0x80000, 0x20000, CRC(609ed2f9) SHA1(869924ff1bc78ac4b50bcfd37a8e76820a9fddf1) )    // == ck-32m.8h
	ROM_LOAD16_BYTE( "ch_40.10h",   0x80001, 0x20000, CRC(be0d8301) SHA1(28dbbb4176800b31068b1beecf54a78085092e5a) )    // == ck-32m.8h
	ROM_LOAD16_BYTE( "ch_35.11f",   0xc0000, 0x20000, CRC(b810867f) SHA1(c971d286c60a9b61f42ea3b792cf59847aacb965) )    // == ck-32m.8h
	ROM_LOAD16_BYTE( "ch_41.11h",   0xc0001, 0x20000, CRC(8ad96155) SHA1(673a5b5eb7330fbbc02aabcdb164efea193613a3) )    // == ck-32m.8h

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_BYTE( "ch_09.4b",  0x000000, 0x20000, CRC(567ab3ca) SHA1(b3d1531d9307285fcecff937dce0bed7ce9e4253) ) // == ck-5m.7a
	ROM_LOAD64_BYTE( "ch_01.4a",  0x000001, 0x20000, CRC(7f3b7b56) SHA1(cf78c3122628aa0e14f7b5017429aee35e9b266d) ) // == ck-5m.7a
	ROM_LOAD64_BYTE( "ch_13.9b",  0x000002, 0x20000, CRC(12a7a8ba) SHA1(22eaa1c667213a37b2c700c5c80dbf9cb81b9f5f) ) // == ck-7m.9a
	ROM_LOAD64_BYTE( "ch_05.9a",  0x000003, 0x20000, CRC(6c1afb9a) SHA1(cef28a63550c6ec3fe7cd7ec478a7fb726df4e27) ) // == ck-7m.9a
	ROM_LOAD64_BYTE( "ch_24.5e",  0x000004, 0x20000, CRC(9cb6e6bc) SHA1(af241438de5bd754e176eec8ad45941f9bf30523) ) // == ck-1m.3a
	ROM_LOAD64_BYTE( "ch_17.5c",  0x000005, 0x20000, CRC(fe490846) SHA1(0d1ddb79c1ee2a7ff4bcdb960e18fc3cfb115e75) ) // == ck-1m.3a
	ROM_LOAD64_BYTE( "ch_38.8h",  0x000006, 0x20000, CRC(6e5c8cb6) SHA1(438b897c14dccc0a185032b1ae2b93d71eed305a) ) // == ck-3m.5a
	ROM_LOAD64_BYTE( "ch_32.8f",  0x000007, 0x20000, CRC(317d27b0) SHA1(5d8a3ab24fcf65b30e1c0affd80301e29e3bf208) ) // == ck-3m.5a
	ROM_LOAD64_BYTE( "ch_10.5b",  0x100000, 0x20000, CRC(e8251a9b) SHA1(e0d5eaba20dc1132643b9ea334b36034ce97fc6d) ) // == ck-5m.7a
	ROM_LOAD64_BYTE( "ch_02.5a",  0x100001, 0x20000, CRC(7c8c88fb) SHA1(29d1e5d6780b7d6875efff6b086fd03bef779df7) ) // == ck-5m.7a
	ROM_LOAD64_BYTE( "ch_14.10b", 0x100002, 0x20000, CRC(4012ec4b) SHA1(041e08e1f407528da84b973d16c5f64f02bd14fe) ) // == ck-7m.9a
	ROM_LOAD64_BYTE( "ch_06.10a", 0x100003, 0x20000, CRC(81884b2b) SHA1(1e4682183c167c95b2fb3986887c31d3e8911484) ) // == ck-7m.9a
	ROM_LOAD64_BYTE( "ch_25.7e",  0x100004, 0x20000, CRC(1dfcbac5) SHA1(a7e419326a4bb7062c5bc7d0b3194b96e00a92d0) ) // == ck-1m.3a
	ROM_LOAD64_BYTE( "ch_18.7c",  0x100005, 0x20000, CRC(516a34d1) SHA1(6516fa19d251898c6f55ab0fda760fc5404bef06) ) // == ck-1m.3a
	ROM_LOAD64_BYTE( "ch_39.9h",  0x100006, 0x20000, CRC(872fb2a4) SHA1(1f5d12b78100dfba7f6c9a076f7923811467aa2b) ) // == ck-3m.5a
	ROM_LOAD64_BYTE( "ch_33.9f",  0x100007, 0x20000, CRC(30dc5ded) SHA1(34a7a6f9aa61ce47116d63c2feb7ea8a427b93e0) ) // == ck-3m.5a

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ch_23.13b",  0x00000, 0x08000, CRC(4d4255b7) SHA1(81a76b58043af7252a854b7efc4109957ef0e679) )    // == ch_09.12b
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ch_30.12c",  0x00000, 0x20000, CRC(f909e8de) SHA1(2dd5bd4076e7d5ded98b72919f868ea700df2e4f) )    // == ch_18.11c
	ROM_LOAD( "ch_31.13c",  0x20000, 0x20000, CRC(fc158cf7) SHA1(294b93d0aea60663ffe96364671552e944a1264b) )    // == ch_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "ck22b.1a",     0x0000, 0x0117, CRC(24fdfdeb) SHA1(850b760de694dc5455e63c89983266ecda836400) )
	ROM_LOAD( "iob1.12e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( msword )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "mse_30.11f",     0x00000, 0x20000, CRC(03fc8dbc) SHA1(a9e4e8a06e2d170faeae75a8b17fd65e6e5fecd4) )
	ROM_LOAD16_BYTE( "mse_35.11h",     0x00001, 0x20000, CRC(d5bf66cd) SHA1(37c5bc4deafd7037ec5cf09c88bb89f35ea3d95c) )
	ROM_LOAD16_BYTE( "mse_31.12f",     0x40000, 0x20000, CRC(30332bcf) SHA1(1c77c06028b77473276cb5dde5ecf414b43a7b78) )
	ROM_LOAD16_BYTE( "mse_36.12h",     0x40001, 0x20000, CRC(8f7d6ce9) SHA1(7694c940023c12520663593f973ddb4168a6bfa5) )
	ROM_LOAD16_WORD_SWAP( "ms-32m.8h", 0x80000, 0x80000, CRC(2475ddfc) SHA1(cc34dfae8124aa781320be6870a1929495eee456) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "ms-5m.7a", 0x000000, 0x80000, CRC(c00fe7e2) SHA1(1ce82ea36996908620d3ac8aabd3650118d6c255) )
	ROM_LOAD64_WORD( "ms-7m.9a", 0x000002, 0x80000, CRC(4ccacac5) SHA1(f2e30edf6ad100da411584bb0b828420256a9d5c) )
	ROM_LOAD64_WORD( "ms-1m.3a", 0x000004, 0x80000, CRC(0d2bbe00) SHA1(dca13fc7ff63ad7fb175a71ada1ee22d21a8811d) )
	ROM_LOAD64_WORD( "ms-3m.5a", 0x000006, 0x80000, CRC(3a1a5bf4) SHA1(88a7cc0bf29b3516a97f661691500ff28e91a362) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ms_09.12b", 0x00000, 0x08000, CRC(57b29519) SHA1(a6b4fc2b9595d1a49f2b93581f107b68d484d156) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ms_18.11c", 0x00000, 0x20000, CRC(fb64e90d) SHA1(d1a596ce2f8ac14a80b34335b173369a14b45f55) )
	ROM_LOAD( "ms_19.12c", 0x20000, 0x20000, CRC(74f892b9) SHA1(bf48db5c438154e7b96fd31fde1be4aad5cf25eb) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "ms24b.1a",     0x0000, 0x0117, CRC(636dbe6d) SHA1(6622a2294f82e70e9eb5ff24f84e0dc13e9168b5) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( mswordr1 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "ms_30.11f",      0x00000, 0x20000, CRC(21c1f078) SHA1(f32bd3b462cc84466244b362a66510b9d40ac2fd) )
	ROM_LOAD16_BYTE( "ms_35.11h",      0x00001, 0x20000, CRC(a540a73a) SHA1(1c91241ba0d17d13adaa68e231b95dfd49d93b6d) )
	ROM_LOAD16_BYTE( "ms_31.12f",      0x40000, 0x20000, CRC(d7e762b5) SHA1(6977130e9c0cd36d8a67e242c132df38f7aea5b7) )
	ROM_LOAD16_BYTE( "ms_36.12h",      0x40001, 0x20000, CRC(66f2dcdb) SHA1(287508b1c96762d0048a10272cf2cbd39a7fba5c) )
	ROM_LOAD16_WORD_SWAP( "ms-32m.8h", 0x80000, 0x80000, CRC(2475ddfc) SHA1(cc34dfae8124aa781320be6870a1929495eee456) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "ms-5m.7a", 0x000000, 0x80000, CRC(c00fe7e2) SHA1(1ce82ea36996908620d3ac8aabd3650118d6c255) )
	ROM_LOAD64_WORD( "ms-7m.9a", 0x000002, 0x80000, CRC(4ccacac5) SHA1(f2e30edf6ad100da411584bb0b828420256a9d5c) )
	ROM_LOAD64_WORD( "ms-1m.3a", 0x000004, 0x80000, CRC(0d2bbe00) SHA1(dca13fc7ff63ad7fb175a71ada1ee22d21a8811d) )
	ROM_LOAD64_WORD( "ms-3m.5a", 0x000006, 0x80000, CRC(3a1a5bf4) SHA1(88a7cc0bf29b3516a97f661691500ff28e91a362) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ms_09.12b", 0x00000, 0x08000, CRC(57b29519) SHA1(a6b4fc2b9595d1a49f2b93581f107b68d484d156) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ms_18.11c", 0x00000, 0x20000, CRC(fb64e90d) SHA1(d1a596ce2f8ac14a80b34335b173369a14b45f55) )
	ROM_LOAD( "ms_19.12c", 0x20000, 0x20000, CRC(74f892b9) SHA1(bf48db5c438154e7b96fd31fde1be4aad5cf25eb) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "ms24b.1a",     0x0000, 0x0117, CRC(636dbe6d) SHA1(6622a2294f82e70e9eb5ff24f84e0dc13e9168b5) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( mswordu )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "msu_30.11f",     0x00000, 0x20000, CRC(d963c816) SHA1(e23844a60ddfc0a8a98c0ada9c3d58fce71c5484) )
	ROM_LOAD16_BYTE( "msu_35.11h",     0x00001, 0x20000, CRC(72f179b3) SHA1(8d31cdc84b02fc345fc78e8f231410adeb834c28) )
	ROM_LOAD16_BYTE( "msu_31.12f",     0x40000, 0x20000, CRC(20cd7904) SHA1(cea2db01be97f69dc10e9da80f3b46f6ddaa953a) )
	ROM_LOAD16_BYTE( "msu_36.12h",     0x40001, 0x20000, CRC(bf88c080) SHA1(b8cd0b127fd3e1afc45402e667ff4b4b01602384) )
	ROM_LOAD16_WORD_SWAP( "ms-32m.8h", 0x80000, 0x80000, CRC(2475ddfc) SHA1(cc34dfae8124aa781320be6870a1929495eee456) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "ms-5m.7a", 0x000000, 0x80000, CRC(c00fe7e2) SHA1(1ce82ea36996908620d3ac8aabd3650118d6c255) )
	ROM_LOAD64_WORD( "ms-7m.9a", 0x000002, 0x80000, CRC(4ccacac5) SHA1(f2e30edf6ad100da411584bb0b828420256a9d5c) )
	ROM_LOAD64_WORD( "ms-1m.3a", 0x000004, 0x80000, CRC(0d2bbe00) SHA1(dca13fc7ff63ad7fb175a71ada1ee22d21a8811d) )
	ROM_LOAD64_WORD( "ms-3m.5a", 0x000006, 0x80000, CRC(3a1a5bf4) SHA1(88a7cc0bf29b3516a97f661691500ff28e91a362) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ms_09.12b", 0x00000, 0x08000, CRC(57b29519) SHA1(a6b4fc2b9595d1a49f2b93581f107b68d484d156) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ms_18.11c", 0x00000, 0x20000, CRC(fb64e90d) SHA1(d1a596ce2f8ac14a80b34335b173369a14b45f55) )
	ROM_LOAD( "ms_19.12c", 0x20000, 0x20000, CRC(74f892b9) SHA1(bf48db5c438154e7b96fd31fde1be4aad5cf25eb) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "ms24b.1a",     0x0000, 0x0117, CRC(636dbe6d) SHA1(6622a2294f82e70e9eb5ff24f84e0dc13e9168b5) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 89625B-1 */
ROM_START( mswordj )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "msj_36.12f", 0x00000, 0x20000, CRC(04f0ef50) SHA1(76dac695732ade1873bb6da51834ef90c6595689) )
	ROM_LOAD16_BYTE( "msj_42.12h", 0x00001, 0x20000, CRC(9fcbb9cd) SHA1(bfbf805ddecd3fa9e209a658526e1430ad9e459a) )
	ROM_LOAD16_BYTE( "msj_37.13f", 0x40000, 0x20000, CRC(6c060d70) SHA1(7fe56f125bc11156955bf0defc956fe7c18a1c72) )
	ROM_LOAD16_BYTE( "msj_43.13h", 0x40001, 0x20000, CRC(aec77787) SHA1(3260f9a80b67394dd90dbabdd544c9b8b31e5817) )
	ROM_LOAD16_BYTE( "ms_34.10f",  0x80000, 0x20000, CRC(0e59a62d) SHA1(d109e5edfb32ce3dc7c32e10a78fc3e943029a73) ) // == ms-32m.8h
	ROM_LOAD16_BYTE( "ms_40.10h",  0x80001, 0x20000, CRC(babade3a) SHA1(00acdcb5b316611a6df55e54f6ac4ec3503e1cac) ) // == ms-32m.8h
	ROM_LOAD16_BYTE( "ms_35.11f",  0xc0000, 0x20000, CRC(03da99d1) SHA1(f21a27f1122e1ee237a53b06ecd24737ac0d2c0e) ) // == ms-32m.8h
	ROM_LOAD16_BYTE( "ms_41.11h",  0xc0001, 0x20000, CRC(fadf99ea) SHA1(a3f3ef357f02c30b7f76941e5f854746774b0114) ) // == ms-32m.8h

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_BYTE( "ms_09.4b",  0x000000, 0x20000, CRC(4adee6f6) SHA1(3becf055313f2fb90c42b839124d8ba4ccc047e0) ) // == ms-5m.7a
	ROM_LOAD64_BYTE( "ms_01.4a",  0x000001, 0x20000, CRC(f7ab1b88) SHA1(9dae1c21c5379413e173fb13521821339ef0852e) ) // == ms-5m.7a
	ROM_LOAD64_BYTE( "ms_13.9b",  0x000002, 0x20000, CRC(e01adc4b) SHA1(1eaf3511a3ffbd4b26a755d5f818e604cfd7764a) ) // == ms-7m.9a
	ROM_LOAD64_BYTE( "ms_05.9a",  0x000003, 0x20000, CRC(f62c2369) SHA1(568d05c256889a2450b53373e68858ea6ae52007) ) // == ms-7m.9a
	ROM_LOAD64_BYTE( "ms_24.5e",  0x000004, 0x20000, CRC(be64a3a1) SHA1(92a8f44a7f141fa189543f7b2564b9c0e44fb44f) ) // == ms-1m.3a
	ROM_LOAD64_BYTE( "ms_17.5c",  0x000005, 0x20000, CRC(0bc1665f) SHA1(4b92e4f1f423964ece9d5ecbe960be3a06c42565) ) // == ms-1m.3a
	ROM_LOAD64_BYTE( "ms_38.8h",  0x000006, 0x20000, CRC(904a2ed5) SHA1(8954c13f5c008ab6f28fa3adfba811c1173a2d88) ) // == ms-3m.5a
	ROM_LOAD64_BYTE( "ms_32.8f",  0x000007, 0x20000, CRC(3d89c530) SHA1(7a9d82e1ccd0dd3b27d91013ef127233a0dd42a1) ) // == ms-3m.5a
	ROM_LOAD64_BYTE( "ms_10.5b",  0x100000, 0x20000, CRC(f02c0718) SHA1(6055673fff3b57b7ba69eea8b45d7df36dfc5ba7) ) // == ms-5m.7a
	ROM_LOAD64_BYTE( "ms_02.5a",  0x100001, 0x20000, CRC(d071a405) SHA1(205d706a992efa4bd772699472ab40eedd70f686) ) // == ms-5m.7a
	ROM_LOAD64_BYTE( "ms_14.10b", 0x100002, 0x20000, CRC(dfb2e4df) SHA1(371c2a8d97eb0592b7b8767c1b992b4375933ac4) ) // == ms-7m.9a
	ROM_LOAD64_BYTE( "ms_06.10a", 0x100003, 0x20000, CRC(d3ce2a91) SHA1(21ed0f7d4a1e9d0b1eb6a1cf4e5d082b773eb36c) ) // == ms-7m.9a
	ROM_LOAD64_BYTE( "ms_25.7e",  0x100004, 0x20000, CRC(0f199d56) SHA1(0df4eda96b3327bd1d1fe6416e75e8b76b6593ac) ) // == ms-1m.3a
	ROM_LOAD64_BYTE( "ms_18.7c",  0x100005, 0x20000, CRC(1ba76df2) SHA1(db7c16e6fde29c764278bdf76fc04c05567666e5) ) // == ms-1m.3a
	ROM_LOAD64_BYTE( "ms_39.9h",  0x100006, 0x20000, CRC(01efce86) SHA1(665182bb61b1efb300422f7076d8538d2ca514ce) ) // == ms-3m.5a
	ROM_LOAD64_BYTE( "ms_33.9f",  0x100007, 0x20000, CRC(ce25defc) SHA1(885eef9bac1d401f3e49c46294e573dd9cfad3a1) ) // == ms-3m.5a

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ms_23.13b",  0x00000, 0x08000, CRC(57b29519) SHA1(a6b4fc2b9595d1a49f2b93581f107b68d484d156) )    // == ms_09.12b
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ms_30.12c",  0x00000, 0x20000, CRC(fb64e90d) SHA1(d1a596ce2f8ac14a80b34335b173369a14b45f55) )    // == ms_18.11c
	ROM_LOAD( "ms_31.13c",  0x20000, 0x20000, CRC(74f892b9) SHA1(bf48db5c438154e7b96fd31fde1be4aad5cf25eb) )    // == ms_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "ms22b.1a",     0x0000, 0x0117, CRC(dde86cb0) SHA1(d0b93a0b62a7cc3c3473da31fc00043392bc8f75) )
	ROM_LOAD( "iob1.12e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( cawing )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "cae_30a.11f",    0x00000, 0x20000, CRC(91fceacd) SHA1(4845999a96fee829264346ca399fdd64a8408001) )
	ROM_LOAD16_BYTE( "cae_35a.11h",    0x00001, 0x20000, CRC(3ef03083) SHA1(297dfc9ec1e0f07d6083bf5efaa0de8d0fb361fa) )
	ROM_LOAD16_BYTE( "cae_31a.12f",    0x40000, 0x20000, CRC(e5b75caf) SHA1(4d04220c78620867b7598deea5685bbe88298ae6) )
	ROM_LOAD16_BYTE( "cae_36a.12h",    0x40001, 0x20000, CRC(c73fd713) SHA1(fa202c252b2cc5972d42d634c466d89cf8b5d178) )
	ROM_LOAD16_WORD_SWAP( "ca-32m.8h", 0x80000, 0x80000, CRC(0c4837d4) SHA1(1c61958b43066b59d86eb4bae0b52c3109be4b07) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "ca-5m.7a", 0x000000, 0x80000, CRC(66d4cc37) SHA1(d355ea64ff29d228dcbfeee72bcf11882bf1cd9d) )
	ROM_LOAD64_WORD( "ca-7m.9a", 0x000002, 0x80000, CRC(b6f896f2) SHA1(bdb6820b81fbce77d7eacb01777af7c380490402) )
	ROM_LOAD64_WORD( "ca-1m.3a", 0x000004, 0x80000, CRC(4d0620fd) SHA1(5f62cd551b6a230edefd81fa60c10c84186ca804) )
	ROM_LOAD64_WORD( "ca-3m.5a", 0x000006, 0x80000, CRC(0b0341c3) SHA1(c31f0e78f49d94ea9dea20eb0cbd98a6c613bcbf) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ca_9.12b",  0x00000, 0x08000, CRC(96fe7485) SHA1(10466889dfc6bc8afd3075385e241a16372efbeb) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ca_18.11c", 0x00000, 0x20000, CRC(4a613a2c) SHA1(06e10644fc60925b85d2ca0888c9fa057bfe996a) )
	ROM_LOAD( "ca_19.12c", 0x20000, 0x20000, CRC(74584493) SHA1(5cfb15f1b9729323707972646313aee8ab3ac4eb) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "ca24b.1a",     0x0000, 0x0117, CRC(76ec0b1c) SHA1(71a7e22613981182fd5b1156f4e495337ab8a172) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( cawingr1 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "cae_30.11f",     0x00000, 0x20000, CRC(23305cd5) SHA1(59cbcb79c171b433f278e128c73cdd3635876370) )
	ROM_LOAD16_BYTE( "cae_35.11h",     0x00001, 0x20000, CRC(69419113) SHA1(cfbb6dbbe224ffaf7747fd70b65a7dbd4f696fe9) )
	ROM_LOAD16_BYTE( "cae_31.12f",     0x40000, 0x20000, CRC(9008dfb3) SHA1(81fdd21606caabe9e0df773fc33377c958ab80f6) )
	ROM_LOAD16_BYTE( "cae_36.12h",     0x40001, 0x20000, CRC(4dbf6f8e) SHA1(a2da49dce72c2366381bd8bea8ce4eba0b70d78c) )
	ROM_LOAD16_WORD_SWAP( "ca-32m.8h", 0x80000, 0x80000, CRC(0c4837d4) SHA1(1c61958b43066b59d86eb4bae0b52c3109be4b07) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "ca-5m.7a",  0x000000, 0x80000, CRC(66d4cc37) SHA1(d355ea64ff29d228dcbfeee72bcf11882bf1cd9d) )
	ROM_LOAD64_WORD( "ca-7m.9a",  0x000002, 0x80000, CRC(b6f896f2) SHA1(bdb6820b81fbce77d7eacb01777af7c380490402) )
	ROM_LOAD64_WORD( "ca-1m.3a",  0x000004, 0x80000, CRC(4d0620fd) SHA1(5f62cd551b6a230edefd81fa60c10c84186ca804) )
	ROM_LOAD64_WORD( "ca-3m.5a",  0x000006, 0x80000, CRC(0b0341c3) SHA1(c31f0e78f49d94ea9dea20eb0cbd98a6c613bcbf) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "cae_09.12b", 0x00000, 0x08000, CRC(96fe7485) SHA1(10466889dfc6bc8afd3075385e241a16372efbeb) )    // == ca_9.12b
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "cae_18.11c", 0x00000, 0x20000, CRC(4a613a2c) SHA1(06e10644fc60925b85d2ca0888c9fa057bfe996a) )    // == ca_18.11c
	ROM_LOAD( "cae_19.12c", 0x20000, 0x20000, CRC(74584493) SHA1(5cfb15f1b9729323707972646313aee8ab3ac4eb) )    // == ca_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "ca24b.1a",     0x0000, 0x0117, CRC(76ec0b1c) SHA1(71a7e22613981182fd5b1156f4e495337ab8a172) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 89625B-1 */
ROM_START( cawingu )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "cau_36.12f",  0x00000, 0x20000, CRC(c2574c0c) SHA1(dc74a02cc81f7ef283e4c750b1f3c08a85c7f9a4) )   // these 4 had a red stripe across label
	ROM_LOAD16_BYTE( "cau_42.12h",  0x00001, 0x20000, CRC(d89e00be) SHA1(4c64648f0f80c7e85170a50f5e558a100b937c8f) )   // these 4 had a red stripe across label
	ROM_LOAD16_BYTE( "cau_37.13f",  0x40000, 0x20000, CRC(8e6d4f8a) SHA1(dd9c429e753b8e4c10a16f5121ebd727c7c653a4) )   // these 4 had a red stripe across label
	ROM_LOAD16_BYTE( "cau_43.13h",  0x40001, 0x20000, CRC(ece07955) SHA1(46e3f0123c9f651b3d7c3798938ca14dd0227f76) )   // these 4 had a red stripe across label
	ROM_LOAD16_BYTE( "cau_34.10f",  0x80000, 0x20000, CRC(5fda906e) SHA1(7b3ef17d494a2f92e58ab7e34a3beaad8c149fca) )
	ROM_LOAD16_BYTE( "cau_40.10h",  0x80001, 0x20000, CRC(736c1835) SHA1(a91f479fab30603a111304adc0478d430faa80fc) )
	ROM_LOAD16_BYTE( "cau_35.11f",  0xc0000, 0x20000, CRC(74c2ddf0) SHA1(df1b50649fdad9cc57dfddbf12345056a2f1d121) )
	ROM_LOAD16_BYTE( "cau_41.11h",  0xc0001, 0x20000, CRC(2a44bfe5) SHA1(f29cf5548e753c79b081e742cf30af9c177c2cc7) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_BYTE( "cau_09.4b",  0x000000, 0x20000, CRC(d4b17c3a) SHA1(e5be3afbe0071e3a7c424bae0e9d9445aedd4f4b) )
	ROM_LOAD64_BYTE( "cau_01.4a",  0x000001, 0x20000, CRC(34c3094e) SHA1(281b52b1d0b6ff5af47d28f405d09803d8db54f8) )
	ROM_LOAD64_BYTE( "cau_13.9b",  0x000002, 0x20000, CRC(9d5c7911) SHA1(de67245cb097c60c3a5ea0fc6d0a427fc0167338) )
	ROM_LOAD64_BYTE( "cau_05.9a",  0x000003, 0x20000, CRC(f042cc7b) SHA1(904b9df5170e86ebc64d61812ff9affdb4722993) )
	ROM_LOAD64_BYTE( "cau_24.5e",  0x000004, 0x20000, CRC(0eac450f) SHA1(0ff0bd0835c91003d0a3ebd6b2b36928a835462e) )
	ROM_LOAD64_BYTE( "cau_17.5c",  0x000005, 0x20000, CRC(4fab0d0c) SHA1(f05cde37e9860008a777b0def7477203444a84dd) )
	ROM_LOAD64_BYTE( "cau_38.8h",  0x000006, 0x20000, CRC(cb96ed24) SHA1(9888181030b0cdd3be34fbae1a178f362187988a) )
	ROM_LOAD64_BYTE( "cau_32.8f",  0x000007, 0x20000, CRC(433a0859) SHA1(76534bf08d0779bd018c2400ac3f1b4b2de27e1c) )
	ROM_LOAD64_BYTE( "cau_10.5b",  0x100000, 0x20000, CRC(4af10ef2) SHA1(a118f0ab73b1580ac1e860b615417d2ad423571c) )
	ROM_LOAD64_BYTE( "cau_02.5a",  0x100001, 0x20000, CRC(3e1f5b34) SHA1(baf2a29fd56c24314dca2d1ea0b6eb9e7db1445e) )
	ROM_LOAD64_BYTE( "cau_14.10b", 0x100002, 0x20000, CRC(2bef78c4) SHA1(a72e6ac95b533b19df22272e98af5fa859f6b810) )
	ROM_LOAD64_BYTE( "cau_06.10a", 0x100003, 0x20000, CRC(3777ede1) SHA1(46344a5696492351f0ee81b94e9e324f882222ca) )
	ROM_LOAD64_BYTE( "cau_25.7e",  0x100004, 0x20000, CRC(859ee531) SHA1(59174d333f8a97119e0e733743c06b1fad4d9214) )
	ROM_LOAD64_BYTE( "cau_18.7c",  0x100005, 0x20000, CRC(4c52edf1) SHA1(acc8e79881d97e0404316c58609577fab4dcb0bb) )
	ROM_LOAD64_BYTE( "cau_39.9h",  0x100006, 0x20000, CRC(147be975) SHA1(69dbe31ea3bb0ef310a4e02e43aa863e072cd762) )
	ROM_LOAD64_BYTE( "cau_33.9f",  0x100007, 0x20000, CRC(8560c130) SHA1(dc4d3f7699f208fbf2284efa37fe8634d6881a97) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "cau_23.13b",  0x00000, 0x08000, CRC(96fe7485) SHA1(10466889dfc6bc8afd3075385e241a16372efbeb) )   // == ca_9.12b
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "cau_30.12c",  0x00000, 0x20000, CRC(4a613a2c) SHA1(06e10644fc60925b85d2ca0888c9fa057bfe996a) )   // == ca_18.11c
	ROM_LOAD( "cau_31.13c",  0x20000, 0x20000, CRC(74584493) SHA1(5cfb15f1b9729323707972646313aee8ab3ac4eb) )   // == ca_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "ca22b.1a",     0x0000, 0x0117, CRC(5152e678) SHA1(ac61df30cd073b26f2145e3ea0c513ec804d047a) )   // photo shows this to be labeled CA222B
	ROM_LOAD( "iob1.12e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )    /* seen the same pcb with LWIO.12E */
ROM_END

/* B-Board 89624B-3 */
ROM_START( cawingur1 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "cau_30a.11f",    0x00000, 0x20000, CRC(91fceacd) SHA1(4845999a96fee829264346ca399fdd64a8408001) ) // == cae_30a.11f
	ROM_LOAD16_BYTE( "cau_35a.11h",    0x00001, 0x20000, CRC(f090d9b2) SHA1(261dc4ac79507299a7f9a1ad5edb8425345db06c) )
	ROM_LOAD16_BYTE( "cau_31a.12f",    0x40000, 0x20000, CRC(e5b75caf) SHA1(4d04220c78620867b7598deea5685bbe88298ae6) ) // == cae_31a.12f
	ROM_LOAD16_BYTE( "cau_36a.12h",    0x40001, 0x20000, CRC(c73fd713) SHA1(fa202c252b2cc5972d42d634c466d89cf8b5d178) ) // == cae_36a.12h
	ROM_LOAD16_WORD_SWAP( "ca-32m.8h", 0x80000, 0x80000, CRC(0c4837d4) SHA1(1c61958b43066b59d86eb4bae0b52c3109be4b07) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "ca-5m.7a",  0x000000, 0x80000, CRC(66d4cc37) SHA1(d355ea64ff29d228dcbfeee72bcf11882bf1cd9d) )
	ROM_LOAD64_WORD( "ca-7m.9a",  0x000002, 0x80000, CRC(b6f896f2) SHA1(bdb6820b81fbce77d7eacb01777af7c380490402) )
	ROM_LOAD64_WORD( "ca-1m.3a",  0x000004, 0x80000, CRC(4d0620fd) SHA1(5f62cd551b6a230edefd81fa60c10c84186ca804) )
	ROM_LOAD64_WORD( "ca-3m.5a",  0x000006, 0x80000, CRC(0b0341c3) SHA1(c31f0e78f49d94ea9dea20eb0cbd98a6c613bcbf) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "cau_09.12b", 0x00000, 0x08000, CRC(96fe7485) SHA1(10466889dfc6bc8afd3075385e241a16372efbeb) )    // == ca_9.12b
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "cau_18.11c", 0x00000, 0x20000, CRC(4a613a2c) SHA1(06e10644fc60925b85d2ca0888c9fa057bfe996a) )    // == ca_18.11c
	ROM_LOAD( "cau_19.12c", 0x20000, 0x20000, CRC(74584493) SHA1(5cfb15f1b9729323707972646313aee8ab3ac4eb) )    // == ca_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "ca24b.1a",     0x0000, 0x0117, CRC(76ec0b1c) SHA1(71a7e22613981182fd5b1156f4e495337ab8a172) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 89625B-1 */
ROM_START( cawingj )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "caj_36a.12f", 0x00000, 0x20000, CRC(91fceacd) SHA1(4845999a96fee829264346ca399fdd64a8408001) )    // == cae_30a.11f
	ROM_LOAD16_BYTE( "caj_42a.12h", 0x00001, 0x20000, CRC(039f8362) SHA1(3fc7a642ddeaf94abdfdd5788a4b3c3b1f1b4c5e) )
	ROM_LOAD16_BYTE( "caj_37a.13f", 0x40000, 0x20000, CRC(e5b75caf) SHA1(4d04220c78620867b7598deea5685bbe88298ae6) )    // == cae_31a.12f
	ROM_LOAD16_BYTE( "caj_43a.13h", 0x40001, 0x20000, CRC(c73fd713) SHA1(fa202c252b2cc5972d42d634c466d89cf8b5d178) )    // == cae_36a.12h
	ROM_LOAD16_BYTE( "caj_34.10f",  0x80000, 0x20000, CRC(51ea57f4) SHA1(7d7080dbf4b6f9b801b796937e9c3c45afed602f) )
	ROM_LOAD16_BYTE( "caj_40.10h",  0x80001, 0x20000, CRC(2ab71ae1) SHA1(23814b58322902b23c4bdd744e60d819811462cc) )
	ROM_LOAD16_BYTE( "caj_35.11f",  0xc0000, 0x20000, CRC(01d71973) SHA1(1f5fc0d47f1456a6338284f883dabc89697f8aa5) )
	ROM_LOAD16_BYTE( "caj_41.11h",  0xc0001, 0x20000, CRC(3a43b538) SHA1(474a701500632cbd395ae404ede1d10a9969b342) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_BYTE( "caj_09.4b",  0x000000, 0x20000, CRC(41b0f9a6) SHA1(5a59df64d0c665d5d479ef2d9e7ec191ca0e7a92) )
	ROM_LOAD64_BYTE( "caj_01.4a",  0x000001, 0x20000, CRC(1002d0b8) SHA1(896bec683c1164c6f1fa1d81cadb8a1c549d4a4e) )
	ROM_LOAD64_BYTE( "caj_13.9b",  0x000002, 0x20000, CRC(6f3948b2) SHA1(942d37b84c727074941316b042679110594ae249) )
	ROM_LOAD64_BYTE( "caj_05.9a",  0x000003, 0x20000, CRC(207373d7) SHA1(4fa67c847f65e2657900f4fc93f1d8a7b95c12e6) )
	ROM_LOAD64_BYTE( "caj_24.5e",  0x000004, 0x20000, CRC(e356aad7) SHA1(55f1489044e70a57ad15e1f2c20567bd6c770f71) )
	ROM_LOAD64_BYTE( "caj_17.5c",  0x000005, 0x20000, CRC(540f2fd8) SHA1(4f500c7795aa41d472c59d2594fc84f6b17ed137) )
	ROM_LOAD64_BYTE( "caj_38.8h",  0x000006, 0x20000, CRC(2464d4ab) SHA1(092864551e7c1c6adbeb901a556f650ccf6ca2f4) )
	ROM_LOAD64_BYTE( "caj_32.8f",  0x000007, 0x20000, CRC(9b5836b3) SHA1(3fda5409d99104f355fa42ec413ccb799d1506c9) )
	ROM_LOAD64_BYTE( "caj_10.5b",  0x100000, 0x20000, CRC(bf8a5f52) SHA1(8cdd31a58de560d282e708c57cda0fefa7d6c92f) )
	ROM_LOAD64_BYTE( "caj_02.5a",  0x100001, 0x20000, CRC(125b018d) SHA1(edb3271f668e0328efd59e0929ee86efd5aa7b1f) )
	ROM_LOAD64_BYTE( "caj_14.10b", 0x100002, 0x20000, CRC(8458e7d7) SHA1(7ebe0a3597a7d8482c2d33640ed1b994fd3a02b2) )
	ROM_LOAD64_BYTE( "caj_06.10a", 0x100003, 0x20000, CRC(cf80e164) SHA1(d83573947fec01a9814919df719474aa3e6ae9a0) )
	ROM_LOAD64_BYTE( "caj_25.7e",  0x100004, 0x20000, CRC(cdd0204d) SHA1(7cbc129bc148718f8c36e27f05583cdecc57b63e) )
	ROM_LOAD64_BYTE( "caj_18.7c",  0x100005, 0x20000, CRC(29c1d4b1) SHA1(d0109ab2f521786a64548910947ca24976ec1218) )
	ROM_LOAD64_BYTE( "caj_39.9h",  0x100006, 0x20000, CRC(eea23b67) SHA1(79d35c15b4b4430d90cd6c270cdd3a064bc2e1a3) )
	ROM_LOAD64_BYTE( "caj_33.9f",  0x100007, 0x20000, CRC(dde3891f) SHA1(25b8069a9c8615323b94157b1ce39805559b68f4) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "caj_23.13b",  0x00000, 0x08000, CRC(96fe7485) SHA1(10466889dfc6bc8afd3075385e241a16372efbeb) )   // == ca_9.12b
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "caj_30.12c",  0x00000, 0x20000, CRC(4a613a2c) SHA1(06e10644fc60925b85d2ca0888c9fa057bfe996a) )   // == ca_18.11c
	ROM_LOAD( "caj_31.13c",  0x20000, 0x20000, CRC(74584493) SHA1(5cfb15f1b9729323707972646313aee8ab3ac4eb) )   // == ca_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "ca22b.1a",     0x0000, 0x0117, CRC(5152e678) SHA1(ac61df30cd073b26f2145e3ea0c513ec804d047a) )
	ROM_LOAD( "iob1.12e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )    /* seen the same pcb with LWIO.12E */
ROM_END

/* B-Board 89624B-3 */
ROM_START( nemo )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "nme_30a.11f",    0x00000, 0x20000, CRC(d2c03e56) SHA1(df468e3b5deba01a6825b742f1cc87bfb26c1981) )
	ROM_LOAD16_BYTE( "nme_35a.11h",    0x00001, 0x20000, CRC(5fd31661) SHA1(12f92a7255e8cae6975452db956670cf72d51768) )
	ROM_LOAD16_BYTE( "nme_31a.12f",    0x40000, 0x20000, CRC(b2bd4f6f) SHA1(82a59b5f36cb4c23dca05297e2a643842fc12609) )
	ROM_LOAD16_BYTE( "nme_36a.12h",    0x40001, 0x20000, CRC(ee9450e3) SHA1(a5454268ef58533e71fe07167b4c3fd263363f77) )
	ROM_LOAD16_WORD_SWAP( "nm-32m.8h", 0x80000, 0x80000, CRC(d6d1add3) SHA1(61c3013d322dbb7622cca032adcd020ba318e885) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "nm-5m.7a",  0x000000, 0x80000, CRC(487b8747) SHA1(f14339b02b8f7ec2002632349e88fed4afc30050) )
	ROM_LOAD64_WORD( "nm-7m.9a",  0x000002, 0x80000, CRC(203dc8c6) SHA1(d52577500e822b89904d1510d559f8575c2aaa78) )
	ROM_LOAD64_WORD( "nm-1m.3a",  0x000004, 0x80000, CRC(9e878024) SHA1(9a5ce3a6a7952a8954d0709b9473db9253793d70) )
	ROM_LOAD64_WORD( "nm-3m.5a",  0x000006, 0x80000, CRC(bb01e6b6) SHA1(3883e28f721d0278b2f4f877a804e95ee14f53e4) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "nme_09.12b", 0x00000, 0x08000, CRC(0f4b0581) SHA1(2e5a2885149c632abfaf4292a1bf032c13c8da6c) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "nme_18.11c", 0x00000, 0x20000, CRC(bab333d4) SHA1(c1d0fb61ec46f17eb7edf69e1ad5ac91b5d51daa) )
	ROM_LOAD( "nme_19.12c", 0x20000, 0x20000, CRC(2650a0a8) SHA1(e9e8cc1b27a2cb3e87124061fabcf42982f0611f) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "nm24b.1a",     0x0000, 0x0117, CRC(7b25bac6) SHA1(fa0083c59c8d6da07798cb3a4fc25d388065b7cd) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( nemor1 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "nme_30.11f",     0x00000, 0x20000, CRC(71b333db) SHA1(8bd8b62d8fef2ad9d2788ccf46e89da953e5817c) )
	ROM_LOAD16_BYTE( "nme_35.11h",     0x00001, 0x20000, CRC(d153bc18) SHA1(2b4a9a50081d403a6485b7311e179a31b659b74d) )
	ROM_LOAD16_BYTE( "nme_31.12f",     0x40000, 0x20000, CRC(7e83dbd2) SHA1(d4c7e3786faab4dd9ded3c1a8f6fea114d423e64) )
	ROM_LOAD16_BYTE( "nme_36.12h",     0x40001, 0x20000, CRC(6aeeec81) SHA1(d7dce52d0f2225d844d852d2c3e81a48cb53a43d) )
	ROM_LOAD16_WORD_SWAP( "nm-32m.8h", 0x80000, 0x80000, CRC(d6d1add3) SHA1(61c3013d322dbb7622cca032adcd020ba318e885) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "nm-5m.7a",  0x000000, 0x80000, CRC(487b8747) SHA1(f14339b02b8f7ec2002632349e88fed4afc30050) )
	ROM_LOAD64_WORD( "nm-7m.9a",  0x000002, 0x80000, CRC(203dc8c6) SHA1(d52577500e822b89904d1510d559f8575c2aaa78) )
	ROM_LOAD64_WORD( "nm-1m.3a",  0x000004, 0x80000, CRC(9e878024) SHA1(9a5ce3a6a7952a8954d0709b9473db9253793d70) )
	ROM_LOAD64_WORD( "nm-3m.5a",  0x000006, 0x80000, CRC(bb01e6b6) SHA1(3883e28f721d0278b2f4f877a804e95ee14f53e4) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "nme_09.12b", 0x00000, 0x08000, CRC(0f4b0581) SHA1(2e5a2885149c632abfaf4292a1bf032c13c8da6c) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "nme_18.11c", 0x00000, 0x20000, CRC(bab333d4) SHA1(c1d0fb61ec46f17eb7edf69e1ad5ac91b5d51daa) )
	ROM_LOAD( "nme_19.12c", 0x20000, 0x20000, CRC(2650a0a8) SHA1(e9e8cc1b27a2cb3e87124061fabcf42982f0611f) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "nm24b.1a",     0x0000, 0x0117, CRC(7b25bac6) SHA1(fa0083c59c8d6da07798cb3a4fc25d388065b7cd) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 88622B-3 */
ROM_START( nemoj )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "nmj_36a.12f", 0x00000, 0x20000, CRC(daeceabb) SHA1(ebd44922be9d07a3d3411af52edee8a60cb11dad) )
	ROM_LOAD16_BYTE( "nmj_42a.12h", 0x00001, 0x20000, CRC(55024740) SHA1(4bacbd191bb435de5dc548ac7fa16ed286bd2d3b) )
	ROM_LOAD16_BYTE( "nmj_37a.13f", 0x40000, 0x20000, CRC(619068b6) SHA1(2507c6f77a06a80f913c848dcb6816bcbf4bba8a) )
	ROM_LOAD16_BYTE( "nmj_43a.13h", 0x40001, 0x20000, CRC(a948a53b) SHA1(65c2abf321cf8b171bbfbb51ed57bc99eb552ca9) )
	ROM_LOAD16_BYTE( "nmj_34.10f",  0x80000, 0x20000, CRC(5737feed) SHA1(2635715cc21381e9f0a4ae4227eb5896886ee3e2) ) // == nm-32m.8h
	ROM_LOAD16_BYTE( "nmj_40.10h",  0x80001, 0x20000, CRC(8a4099f3) SHA1(d1af73d8992aa9ef6dcd729675a2fbea8c290311) ) // == nm-32m.8h
	ROM_LOAD16_BYTE( "nmj_35.11f",  0xc0000, 0x20000, CRC(bd11a7f8) SHA1(1c09db7cbd132866d4f08720cdd60707069f8580) ) // == nm-32m.8h
	ROM_LOAD16_BYTE( "nmj_41.11h",  0xc0001, 0x20000, CRC(6309603d) SHA1(51bee785ddb87340ad56960ad816c0513bc93eb8) ) // == nm-32m.8h

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_BYTE( "nmj_09.4b",  0x000000, 0x20000, CRC(9d60d286) SHA1(69ea9d584d735c3629e1c017cbd966df264e4324) ) // == nm-5m.7a
	ROM_LOAD64_BYTE( "nmj_01.4a",  0x000001, 0x20000, CRC(8a83f7c4) SHA1(ef24f47517d22dfecd3e6b5ef76e38073f6b81ba) ) // == nm-5m.7a
	ROM_LOAD64_BYTE( "nmj_13.9b",  0x000002, 0x20000, CRC(a4909fe0) SHA1(6b1a0e8c2bd2979f7fcc363a86de28d3e365709c) ) // == nm-7m.9a
	ROM_LOAD64_BYTE( "nmj_05.9a",  0x000003, 0x20000, CRC(16db1e61) SHA1(76ff93edd4d40a2527744585a87052a3fc0c77f6) ) // == nm-7m.9a
	ROM_LOAD64_BYTE( "nmj_24.5e",  0x000004, 0x20000, CRC(3312c648) SHA1(9e4f584fa360de16d42d65619dbe9426d4322c00) ) // == nm-1m.3a
	ROM_LOAD64_BYTE( "nmj_17.5c",  0x000005, 0x20000, CRC(ccfc50e2) SHA1(a238f050d11e925b1c0037bb860289ab746b7039) ) // == nm-1m.3a
	ROM_LOAD64_BYTE( "nmj_38.8h",  0x000006, 0x20000, CRC(ae98a997) SHA1(d2d499395e43aa85d9098966d04fde6bd055900b) ) // == nm-3m.5a
	ROM_LOAD64_BYTE( "nmj_32.8f",  0x000007, 0x20000, CRC(b3704dde) SHA1(d107fecb45f34e877faabffcdaba437935754906) ) // == nm-3m.5a
	ROM_LOAD64_BYTE( "nmj_10.5b",  0x100000, 0x20000, CRC(33c1388c) SHA1(cd1ec3e8d6d2b5a65648c749426ec4e254f93d8c) ) // == nm-5m.7a
	ROM_LOAD64_BYTE( "nmj_02.5a",  0x100001, 0x20000, CRC(84c69469) SHA1(700cf7be644056b1dbc5d8bed37caf6383a81cfe) ) // == nm-5m.7a
	ROM_LOAD64_BYTE( "nmj_14.10b", 0x100002, 0x20000, CRC(66612270) SHA1(0c996571459ac44d5ca5683bdcb6a6f08dd83480) ) // == nm-7m.9a
	ROM_LOAD64_BYTE( "nmj_06.10a", 0x100003, 0x20000, CRC(8b9bcf95) SHA1(e03c6dc4946a37bdab68d929722b1e10a2aca31a) ) // == nm-7m.9a
	ROM_LOAD64_BYTE( "nmj_25.7e",  0x100004, 0x20000, CRC(acfc84d2) SHA1(4cd9f3bc32ef62cb3b414de68db34f950d10f406) ) // == nm-1m.3a
	ROM_LOAD64_BYTE( "nmj_18.7c",  0x100005, 0x20000, CRC(4347deed) SHA1(fdd9b3f1ddad42464dcc7298e5b740ffe1622343) ) // == nm-1m.3a
	ROM_LOAD64_BYTE( "nmj_39.9h",  0x100006, 0x20000, CRC(6a274ecd) SHA1(66259fd6e71cfdb618c189b7f18749a996aacfdf) ) // == nm-3m.5a
	ROM_LOAD64_BYTE( "nmj_33.9f",  0x100007, 0x20000, CRC(c469dc74) SHA1(d06956eef5f9b31779f218d597a1a504c1e16bad) ) // == nm-3m.5a

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "nmj_23.13b", 0x00000, 0x08000, CRC(8d3c5a42) SHA1(cc7477da80f3d08cf014379318e39cb75b5d3205) ) // 1 byte different from nemo, pcb verified
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "nmj_30.12c", 0x00000, 0x20000, CRC(bab333d4) SHA1(c1d0fb61ec46f17eb7edf69e1ad5ac91b5d51daa) ) // == nme_18.11c
	ROM_LOAD( "nmj_31.13c", 0x20000, 0x20000, CRC(2650a0a8) SHA1(e9e8cc1b27a2cb3e87124061fabcf42982f0611f) ) // == nme_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "nm22b.1a",     0x0000, 0x0117, CRC(378881e1) SHA1(0359a5936be87d38913a0fb881269685b6cab31c) )
	ROM_LOAD( "lwio.12c",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
ROM_END

/* B-Board 89625B-1 */
ROM_START( nemoja )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "nmj_36a.12f", 0x00000, 0x20000, CRC(daeceabb) SHA1(ebd44922be9d07a3d3411af52edee8a60cb11dad) ) // data identical to nemoj, only 4 program ROMs explicitly labeled with "J"
	ROM_LOAD16_BYTE( "nmj_42a.12h", 0x00001, 0x20000, CRC(55024740) SHA1(4bacbd191bb435de5dc548ac7fa16ed286bd2d3b) )
	ROM_LOAD16_BYTE( "nmj_37a.13f", 0x40000, 0x20000, CRC(619068b6) SHA1(2507c6f77a06a80f913c848dcb6816bcbf4bba8a) )
	ROM_LOAD16_BYTE( "nmj_43a.13h", 0x40001, 0x20000, CRC(a948a53b) SHA1(65c2abf321cf8b171bbfbb51ed57bc99eb552ca9) )
	ROM_LOAD16_BYTE( "nm_34.10f",   0x80000, 0x20000, CRC(5737feed) SHA1(2635715cc21381e9f0a4ae4227eb5896886ee3e2) ) // == nm-32m.8h
	ROM_LOAD16_BYTE( "nm_40.10h",   0x80001, 0x20000, CRC(8a4099f3) SHA1(d1af73d8992aa9ef6dcd729675a2fbea8c290311) ) // == nm-32m.8h
	ROM_LOAD16_BYTE( "nm_35.11f",   0xc0000, 0x20000, CRC(bd11a7f8) SHA1(1c09db7cbd132866d4f08720cdd60707069f8580) ) // == nm-32m.8h
	ROM_LOAD16_BYTE( "nm_41.11h",   0xc0001, 0x20000, CRC(6309603d) SHA1(51bee785ddb87340ad56960ad816c0513bc93eb8) ) // == nm-32m.8h

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_BYTE( "nm_09.4b",   0x000000, 0x20000, CRC(9d60d286) SHA1(69ea9d584d735c3629e1c017cbd966df264e4324) ) // == nm-5m.7a
	ROM_LOAD64_BYTE( "nm_01.4a",   0x000001, 0x20000, CRC(8a83f7c4) SHA1(ef24f47517d22dfecd3e6b5ef76e38073f6b81ba) ) // == nm-5m.7a
	ROM_LOAD64_BYTE( "nm_13.9b",   0x000002, 0x20000, CRC(a4909fe0) SHA1(6b1a0e8c2bd2979f7fcc363a86de28d3e365709c) ) // == nm-7m.9a
	ROM_LOAD64_BYTE( "nm_05.9a",   0x000003, 0x20000, CRC(16db1e61) SHA1(76ff93edd4d40a2527744585a87052a3fc0c77f6) ) // == nm-7m.9a
	ROM_LOAD64_BYTE( "nm_24.5e",   0x000004, 0x20000, CRC(3312c648) SHA1(9e4f584fa360de16d42d65619dbe9426d4322c00) ) // == nm-1m.3a
	ROM_LOAD64_BYTE( "nm_17.5c",   0x000005, 0x20000, CRC(ccfc50e2) SHA1(a238f050d11e925b1c0037bb860289ab746b7039) ) // == nm-1m.3a
	ROM_LOAD64_BYTE( "nm_38.8h",   0x000006, 0x20000, CRC(ae98a997) SHA1(d2d499395e43aa85d9098966d04fde6bd055900b) ) // == nm-3m.5a
	ROM_LOAD64_BYTE( "nm_32.8f",   0x000007, 0x20000, CRC(b3704dde) SHA1(d107fecb45f34e877faabffcdaba437935754906) ) // == nm-3m.5a
	ROM_LOAD64_BYTE( "nm_10.5b",   0x100000, 0x20000, CRC(33c1388c) SHA1(cd1ec3e8d6d2b5a65648c749426ec4e254f93d8c) ) // == nm-5m.7a
	ROM_LOAD64_BYTE( "nm_02.5a",   0x100001, 0x20000, CRC(84c69469) SHA1(700cf7be644056b1dbc5d8bed37caf6383a81cfe) ) // == nm-5m.7a
	ROM_LOAD64_BYTE( "nm_14.10b",  0x100002, 0x20000, CRC(66612270) SHA1(0c996571459ac44d5ca5683bdcb6a6f08dd83480) ) // == nm-7m.9a
	ROM_LOAD64_BYTE( "nm_06.10a",  0x100003, 0x20000, CRC(8b9bcf95) SHA1(e03c6dc4946a37bdab68d929722b1e10a2aca31a) ) // == nm-7m.9a
	ROM_LOAD64_BYTE( "nm_25.7e",   0x100004, 0x20000, CRC(acfc84d2) SHA1(4cd9f3bc32ef62cb3b414de68db34f950d10f406) ) // == nm-1m.3a
	ROM_LOAD64_BYTE( "nm_18.7c",   0x100005, 0x20000, CRC(4347deed) SHA1(fdd9b3f1ddad42464dcc7298e5b740ffe1622343) ) // == nm-1m.3a
	ROM_LOAD64_BYTE( "nm_39.9h",   0x100006, 0x20000, CRC(6a274ecd) SHA1(66259fd6e71cfdb618c189b7f18749a996aacfdf) ) // == nm-3m.5a
	ROM_LOAD64_BYTE( "nm_33.9f",   0x100007, 0x20000, CRC(c469dc74) SHA1(d06956eef5f9b31779f218d597a1a504c1e16bad) ) // == nm-3m.5a

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "nm_23.13b", 0x00000, 0x08000, CRC(8d3c5a42) SHA1(cc7477da80f3d08cf014379318e39cb75b5d3205) ) // 1 byte different from nemo, pcb verified
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "nm_30.12c", 0x00000, 0x20000, CRC(bab333d4) SHA1(c1d0fb61ec46f17eb7edf69e1ad5ac91b5d51daa) ) // == nme_18.11c
	ROM_LOAD( "nm_31.13c", 0x20000, 0x20000, CRC(2650a0a8) SHA1(e9e8cc1b27a2cb3e87124061fabcf42982f0611f) ) // == nme_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "nm22b.1a",     0x0000, 0x0117, CRC(378881e1) SHA1(0359a5936be87d38913a0fb881269685b6cab31c) )
	ROM_LOAD( "iob1.12e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) ) // uses IOB1 due to different ROM board, otherwise identical to nemoj
ROM_END

/* B-Board 90629B-3 */
ROM_START( sf2 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2e_30g.11e", 0x00000, 0x20000, CRC(fe39ee33) SHA1(22558eb15e035b09b80935a32b8425d91cd79669) )
	ROM_LOAD16_BYTE( "sf2e_37g.11f", 0x00001, 0x20000, CRC(fb92cd74) SHA1(bf1ccfe7cc1133f0f65556430311108722add1f2) )
	ROM_LOAD16_BYTE( "sf2e_31g.12e", 0x40000, 0x20000, CRC(69a0a301) SHA1(86a3954335310865b14ce8b4e0e4499feb14fc12) )
	ROM_LOAD16_BYTE( "sf2e_38g.12f", 0x40001, 0x20000, CRC(5e22db70) SHA1(6565946591a18eaf46f04c1aa449ee0ae9ac2901) )
	ROM_LOAD16_BYTE( "sf2e_28g.9e",  0x80000, 0x20000, CRC(8bf9f1e5) SHA1(bbcef63f35e5bff3f373968ba1278dd6bd86b593) )
	ROM_LOAD16_BYTE( "sf2e_35g.9f",  0x80001, 0x20000, CRC(626ef934) SHA1(507bda3e4519de237aca919cf72e543403ec9724) )
	ROM_LOAD16_BYTE( "sf2_29b.10e",  0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )
	ROM_LOAD16_BYTE( "sf2_36b.10f",  0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2-5m.4a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // in "2" socket
	ROM_LOAD64_WORD( "sf2-7m.6a",  0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // in "4" socket
	ROM_LOAD64_WORD( "sf2-1m.3a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // in "1" socket
	ROM_LOAD64_WORD( "sf2-3m.5a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // in "3" socket
	ROM_LOAD64_WORD( "sf2-6m.4c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // in "11" socket
	ROM_LOAD64_WORD( "sf2-8m.6c",  0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // in "13" socket
	ROM_LOAD64_WORD( "sf2-2m.3c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // in "10" socket
	ROM_LOAD64_WORD( "sf2-4m.5c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // in "12" socket
	ROM_LOAD64_WORD( "sf2-13m.4d", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // in "21" socket
	ROM_LOAD64_WORD( "sf2-15m.6d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // in "23" socket
	ROM_LOAD64_WORD( "sf2-9m.3d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // in "20" socket
	ROM_LOAD64_WORD( "sf2-11m.5d", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // in "22" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_9.12a",   0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-2 */
ROM_START( sf2ea )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2e_30a.11e", 0x00000, 0x20000, CRC(bc02c14c) SHA1(83198317c3838d9a031f78c866fe690d185647f6) ) // had a red stripe through label
	ROM_LOAD16_BYTE( "sf2e_37a.11f", 0x00001, 0x20000, CRC(1c1266b3) SHA1(756f54e82305af6b4374b748d486ae587bf7571b) ) // had a red stripe through label
	ROM_LOAD16_BYTE( "sf2e_31a.12e", 0x40000, 0x20000, CRC(8b8221e6) SHA1(98d143943ea830a046f0f799b1cf27cc4024729b) ) // had a red stripe through label
	ROM_LOAD16_BYTE( "sf2e_38a.12f", 0x40001, 0x20000, CRC(2d42d82a) SHA1(4a1f23efef6ae6fe4bebd475f7429cf8ae291adf) ) // had a red stripe through label
	ROM_LOAD16_BYTE( "sf2_28a.9e",   0x80000, 0x20000, CRC(852e10ec) SHA1(a39e0feac1ba9db209aae89b1d33a3a6a1bec2e1) ) // had a red stripe through label
	ROM_LOAD16_BYTE( "sf2_35a.9f",   0x80001, 0x20000, CRC(3b075de1) SHA1(8dcadee5bbf8c91c60d6775d107543fb5e36d75d) ) // had a red stripe through label
	ROM_LOAD16_BYTE( "sf2_29.10e",   0xc0000, 0x20000, CRC(fdd0b5c1) SHA1(af9051d77f8e7039ea9b55482fcdbb0bad8bb63c) )
	ROM_LOAD16_BYTE( "sf2_36.10f",   0xc0001, 0x20000, CRC(db66b127) SHA1(cbf6aba28e24d5a7cd73733ee4c04ddc5e15e2a7) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2-5m.4a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // in "2" socket
	ROM_LOAD64_WORD( "sf2-7m.6a",  0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // in "4" socket
	ROM_LOAD64_WORD( "sf2-1m.3a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // in "1" socket
	ROM_LOAD64_WORD( "sf2-3m.5a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // in "3" socket
	ROM_LOAD64_WORD( "sf2-6m.4c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // in "11" socket
	ROM_LOAD64_WORD( "sf2-8m.6c",  0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // in "13" socket
	ROM_LOAD64_WORD( "sf2-2m.3c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // in "10" socket
	ROM_LOAD64_WORD( "sf2-4m.5c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // in "12" socket
	ROM_LOAD64_WORD( "sf2-13m.4d", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // in "21" socket
	ROM_LOAD64_WORD( "sf2-15m.6d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // in "23" socket
	ROM_LOAD64_WORD( "sf2-9m.3d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // in "20" socket
	ROM_LOAD64_WORD( "sf2-11m.5d", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // in "22" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_9.12a",   0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-2 */
ROM_START( sf2eb )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2e_30b.11e", 0x00000, 0x20000, CRC(57bd7051) SHA1(5e211e75b1649b07723cabc03cf15636dbbae595) )
	ROM_LOAD16_BYTE( "sf2e_37b.11f", 0x00001, 0x20000, CRC(62691cdd) SHA1(328703c3e737ada544e67c36119eeb4a100ca740) )   // only rom different from sf2ub
	ROM_LOAD16_BYTE( "sf2e_31b.12e", 0x40000, 0x20000, CRC(a673143d) SHA1(e565f0ec23d6deb543c72af5a83f070c07319477) )
	ROM_LOAD16_BYTE( "sf2e_38b.12f", 0x40001, 0x20000, CRC(4c2ccef7) SHA1(77b119c70c255622b023de25d9af3b3aac52ea47) )
	ROM_LOAD16_BYTE( "sf2_28b.9e",   0x80000, 0x20000, CRC(4009955e) SHA1(7842dbef7650485639fbae49b9f4db7494d4f73d) )
	ROM_LOAD16_BYTE( "sf2_35b.9f",   0x80001, 0x20000, CRC(8c1f3994) SHA1(5e1d334399d05a837c2d80f79eada543e83afaf7) )
	ROM_LOAD16_BYTE( "sf2_29b.10e",  0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )
	ROM_LOAD16_BYTE( "sf2_36b.10f",  0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2-5m.4a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // in "2" socket
	ROM_LOAD64_WORD( "sf2-7m.6a",  0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // in "4" socket
	ROM_LOAD64_WORD( "sf2-1m.3a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // in "1" socket
	ROM_LOAD64_WORD( "sf2-3m.5a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // in "3" socket
	ROM_LOAD64_WORD( "sf2-6m.4c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // in "11" socket
	ROM_LOAD64_WORD( "sf2-8m.6c",  0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // in "13" socket
	ROM_LOAD64_WORD( "sf2-2m.3c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // in "10" socket
	ROM_LOAD64_WORD( "sf2-4m.5c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // in "12" socket
	ROM_LOAD64_WORD( "sf2-13m.4d", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // in "21" socket
	ROM_LOAD64_WORD( "sf2-15m.6d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // in "23" socket
	ROM_LOAD64_WORD( "sf2-9m.3d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // in "20" socket
	ROM_LOAD64_WORD( "sf2-11m.5d", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // in "22" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_9.12a",   0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-3 */
ROM_START( sf2ed )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2e_30d.11e", 0x00000, 0x20000, CRC(4bb2657c) SHA1(b2d077296b77be7db371f953b7fc446a67d8a9d6) )
	ROM_LOAD16_BYTE( "sf2e_37d.11f", 0x00001, 0x20000, CRC(102f4561) SHA1(2fc77cd3b2ecf8fadc4f8614cb200cf2cba4c616) )   // only rom different from sf2ud
	ROM_LOAD16_BYTE( "sf2e_31d.12e", 0x40000, 0x20000, CRC(d57b67d7) SHA1(43d0b47c9fada8d9b445caa4b96ac8493061aa8b) )
	ROM_LOAD16_BYTE( "sf2e_38d.12f", 0x40001, 0x20000, CRC(9c8916ef) SHA1(a4629356a816454bcc1d7b41e70e147d4769a682) )
	ROM_LOAD16_BYTE( "sf2e_28d.9e",  0x80000, 0x20000, CRC(175819d1) SHA1(c98b6b7af4e57735dbfb3d1e61ba1bfb9f145d33) )
	ROM_LOAD16_BYTE( "sf2e_35d.9f",  0x80001, 0x20000, CRC(82060da4) SHA1(7487cfc28cce3d76772ece657aef83b56034011e) )
	ROM_LOAD16_BYTE( "sf2_29b.10e",  0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )
	ROM_LOAD16_BYTE( "sf2_36b.10f",  0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2-5m.4a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // in "2" socket
	ROM_LOAD64_WORD( "sf2-7m.6a",  0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // in "4" socket
	ROM_LOAD64_WORD( "sf2-1m.3a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // in "1" socket
	ROM_LOAD64_WORD( "sf2-3m.5a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // in "3" socket
	ROM_LOAD64_WORD( "sf2-6m.4c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // in "11" socket
	ROM_LOAD64_WORD( "sf2-8m.6c",  0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // in "13" socket
	ROM_LOAD64_WORD( "sf2-2m.3c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // in "10" socket
	ROM_LOAD64_WORD( "sf2-4m.5c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // in "12" socket
	ROM_LOAD64_WORD( "sf2-13m.4d", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // in "21" socket
	ROM_LOAD64_WORD( "sf2-15m.6d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // in "23" socket
	ROM_LOAD64_WORD( "sf2-9m.3d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // in "20" socket
	ROM_LOAD64_WORD( "sf2-11m.5d", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // in "22" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_9.12a",   0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-3 */
/* Note that this revision seems to be the only one that uses the IOB2 and C632B PALs instead of the IOB1 and C632,
   while STF29 PAL is confirmed to be the same as the other Street Fighter II: The World Warrior sets. */
ROM_START( sf2ee )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2e_30e.11e", 0x00000, 0x20000, CRC(f37cd088) SHA1(48b71e44ce88d5f682ed679c737e7ec5262bb0df) )
	ROM_LOAD16_BYTE( "sf2e_37e.11f", 0x00001, 0x20000, CRC(c39468e6) SHA1(0b1ce83dbc4dcf9b205c67fa6a4b074570baf59b) )   // only rom different from sf2ue
	ROM_LOAD16_BYTE( "sf2e_31e.12e", 0x40000, 0x20000, CRC(7c4771b4) SHA1(6637b24194c86ec72a1775d4e976891243cd66fd) )
	ROM_LOAD16_BYTE( "sf2e_38e.12f", 0x40001, 0x20000, CRC(a4bd0cd9) SHA1(32a2bc18d1f860668141e53cbca862ceec238c19) )
	ROM_LOAD16_BYTE( "sf2e_28e.9e",  0x80000, 0x20000, CRC(e3b95625) SHA1(f7277f9980040f96434d1bd162eaf9ba0dfbb005) )
	ROM_LOAD16_BYTE( "sf2e_35e.9f",  0x80001, 0x20000, CRC(3648769a) SHA1(74e5934b0e3b4da35ff48086f41e7502b42731c6) )
	ROM_LOAD16_BYTE( "sf2_29b.10e",  0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )
	ROM_LOAD16_BYTE( "sf2_36b.10f",  0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2-5m.4a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // in "2" socket
	ROM_LOAD64_WORD( "sf2-7m.6a",  0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // in "4" socket
	ROM_LOAD64_WORD( "sf2-1m.3a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // in "1" socket
	ROM_LOAD64_WORD( "sf2-3m.5a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // in "3" socket
	ROM_LOAD64_WORD( "sf2-6m.4c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // in "11" socket
	ROM_LOAD64_WORD( "sf2-8m.6c",  0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // in "13" socket
	ROM_LOAD64_WORD( "sf2-2m.3c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // in "10" socket
	ROM_LOAD64_WORD( "sf2-4m.5c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // in "12" socket
	ROM_LOAD64_WORD( "sf2-13m.4d", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // in "21" socket
	ROM_LOAD64_WORD( "sf2-15m.6d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // in "23" socket
	ROM_LOAD64_WORD( "sf2-9m.3d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // in "20" socket
	ROM_LOAD64_WORD( "sf2-11m.5d", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // in "22" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_9.12a",   0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob2.11d",     0x0000, 0x0117, CRC(d26f0a27) SHA1(22bb5647ff98df22fd19ae079ff98b9d100855f9) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632b.ic1",    0x0000, 0x0117, CRC(5c3cbb67) SHA1(e947078640e0b1a6cc51958cbf84b7e407213452) )
ROM_END

/* B-Board 90629B-3 */
ROM_START( sf2ef )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2e_30f.11e", 0x00000, 0x20000, CRC(fe39ee33) SHA1(22558eb15e035b09b80935a32b8425d91cd79669) )
	ROM_LOAD16_BYTE( "sf2e_37f.11f", 0x00001, 0x20000, CRC(b58a741b) SHA1(0ff6edbd828ab0e5b3766684c9d93a4e0fb88f6d) )   // only rom different from sf2uf
	ROM_LOAD16_BYTE( "sf2e_31f.12e", 0x40000, 0x20000, CRC(69a0a301) SHA1(86a3954335310865b14ce8b4e0e4499feb14fc12) )
	ROM_LOAD16_BYTE( "sf2e_38f.12f", 0x40001, 0x20000, CRC(1510e4e2) SHA1(fbfdd4e42c4bc894592dbe5a84c88d5f13d21da4) )
	ROM_LOAD16_BYTE( "sf2e_28f.9e",  0x80000, 0x20000, CRC(acd8175b) SHA1(504991c46fa568d31ce69bd63e2a67926a06b5a9) )
	ROM_LOAD16_BYTE( "sf2e_35f.9f",  0x80001, 0x20000, CRC(c0a80bd1) SHA1(ac25a9ed488e03baf4115541fdcce3973ce6a442) )
	ROM_LOAD16_BYTE( "sf2_29b.10e",  0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )
	ROM_LOAD16_BYTE( "sf2_36b.10f",  0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2-5m.4a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // in "2" socket
	ROM_LOAD64_WORD( "sf2-7m.6a",  0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // in "4" socket
	ROM_LOAD64_WORD( "sf2-1m.3a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // in "1" socket
	ROM_LOAD64_WORD( "sf2-3m.5a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // in "3" socket
	ROM_LOAD64_WORD( "sf2-6m.4c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // in "11" socket
	ROM_LOAD64_WORD( "sf2-8m.6c",  0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // in "13" socket
	ROM_LOAD64_WORD( "sf2-2m.3c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // in "10" socket
	ROM_LOAD64_WORD( "sf2-4m.5c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // in "12" socket
	ROM_LOAD64_WORD( "sf2-13m.4d", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // in "21" socket
	ROM_LOAD64_WORD( "sf2-15m.6d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // in "23" socket
	ROM_LOAD64_WORD( "sf2-9m.3d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // in "20" socket
	ROM_LOAD64_WORD( "sf2-11m.5d", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // in "22" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_9.12a",   0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-2 */
ROM_START( sf2em )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2e_30.11e", 0x00000, 0x20000, CRC(997bdac4) SHA1(63ca347db8dd8804823a74989250eefec7907cad) )
	ROM_LOAD16_BYTE( "sf2e_37.11f", 0x00001, 0x20000, CRC(f11b3d64) SHA1(a65ebe82bfd8e592e08ed2ab1ef707412f9d9645) )
	ROM_LOAD16_BYTE( "sf2e_31.12e", 0x40000, 0x20000, CRC(53e54744) SHA1(aa93d49809da04f1de4475c6bf9e5f939aaeeb4e) )
	ROM_LOAD16_BYTE( "sf2e_38.12f", 0x40001, 0x20000, CRC(5ff4dc81) SHA1(fe84248611f96e5e304de5bf261374d3dc2ceacd) )
	ROM_LOAD16_BYTE( "sf2_28.9e",   0x80000, 0x20000, CRC(55d88c35) SHA1(5f14ad1f31685eb565c0e10d8e4daf3167afd89e) )
	ROM_LOAD16_BYTE( "sf2_35.9f",   0x80001, 0x20000, CRC(4b964478) SHA1(4ec73e7e8de8a5b4352d9ef5beca643ce83ee37c) )
	ROM_LOAD16_BYTE( "sf2_29.10e",  0xc0000, 0x20000, CRC(fdd0b5c1) SHA1(af9051d77f8e7039ea9b55482fcdbb0bad8bb63c) )
	ROM_LOAD16_BYTE( "sf2_36.10f",  0xc0001, 0x20000, CRC(db66b127) SHA1(cbf6aba28e24d5a7cd73733ee4c04ddc5e15e2a7) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2-5m.4a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // in "2" socket
	ROM_LOAD64_WORD( "sf2-7m.6a",  0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // in "4" socket
	ROM_LOAD64_WORD( "sf2-1m.3a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // in "1" socket
	ROM_LOAD64_WORD( "sf2-3m.5a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // in "3" socket
	ROM_LOAD64_WORD( "sf2-6m.4c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // in "11" socket
	ROM_LOAD64_WORD( "sf2-8m.6c",  0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // in "13" socket
	ROM_LOAD64_WORD( "sf2-2m.3c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // in "10" socket
	ROM_LOAD64_WORD( "sf2-4m.5c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // in "12" socket
	ROM_LOAD64_WORD( "sf2-13m.4d", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // in "21" socket
	ROM_LOAD64_WORD( "sf2-15m.6d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // in "23" socket
	ROM_LOAD64_WORD( "sf2-9m.3d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // in "20" socket
	ROM_LOAD64_WORD( "sf2-11m.5d", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // in "22" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_9.12a",   0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 89625B-1, Same data as sf2ea, converted for the 89625B ROM board, doesn't have original Capcom labels */
ROM_START( sf2en )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code, all 27c010 */
	ROM_LOAD16_BYTE( "epr-b-05.12f", 0x00000, 0x20000, CRC(bc02c14c) SHA1(83198317c3838d9a031f78c866fe690d185647f6) ) // == sf2e_30a.11e
	ROM_LOAD16_BYTE( "epr-b-09.12h", 0x00001, 0x20000, CRC(1c1266b3) SHA1(756f54e82305af6b4374b748d486ae587bf7571b) ) // == sf2e_37a.11f
	ROM_LOAD16_BYTE( "epr-b-06.13f", 0x40000, 0x20000, CRC(8b8221e6) SHA1(98d143943ea830a046f0f799b1cf27cc4024729b) ) // == sf2e_31a.12e
	ROM_LOAD16_BYTE( "epr-b-10.13h", 0x40001, 0x20000, CRC(2d42d82a) SHA1(4a1f23efef6ae6fe4bebd475f7429cf8ae291adf) ) // == sf2e_38a.12f
	ROM_LOAD16_BYTE( "epr-b-03.10f", 0x80000, 0x20000, CRC(852e10ec) SHA1(a39e0feac1ba9db209aae89b1d33a3a6a1bec2e1) ) // == sf2_28a.9e
	ROM_LOAD16_BYTE( "epr-b-07.10h", 0x80001, 0x20000, CRC(3b075de1) SHA1(8dcadee5bbf8c91c60d6775d107543fb5e36d75d) ) // == sf2_35a.9f
	ROM_LOAD16_BYTE( "epr-b-04.11f", 0xc0000, 0x20000, CRC(fdd0b5c1) SHA1(af9051d77f8e7039ea9b55482fcdbb0bad8bb63c) ) // == sf2_29.10e
	ROM_LOAD16_BYTE( "epr-b-08.11h", 0xc0001, 0x20000, CRC(db66b127) SHA1(cbf6aba28e24d5a7cd73733ee4c04ddc5e15e2a7) ) // == sf2_36.10f

	ROM_REGION( 0x600000, "gfx", 0 ) // all 27c020, data matches parent, but arranged differently
	ROM_LOAD64_BYTE( "epr-b-18.5e",  0x000000, 0x40000, CRC(88f3485a) SHA1(12df3a823a29eb5cc69993015108cb3da2c961f2) )
	ROM_LOAD64_BYTE( "epr-b-13.5c",  0x000001, 0x40000, CRC(63d63e0c) SHA1(5ed5cad35663d0fad37b917685c59f7f27fa434f) )
	ROM_LOAD64_BYTE( "epr-b-24.8h",  0x000002, 0x40000, CRC(25ae23bc) SHA1(b728cc97d656415fb89bcf4416d7309639c31e8c) )
	ROM_LOAD64_BYTE( "epr-b-23.8f",  0x000003, 0x40000, CRC(e5819676) SHA1(655d148a1b5adbe56008ba992fee0db8bb3be639) )
	ROM_LOAD64_BYTE( "epr-b-07.4b",  0x000004, 0x40000, CRC(84afb959) SHA1(ff8f3fb39203d229552649b09046a509aa399b7d) )
	ROM_LOAD64_BYTE( "epr-b-01.4a",  0x000005, 0x40000, CRC(ab21635d) SHA1(c6294b06c482a43cc1736674b0c1bc790da8cc8c) )
	ROM_LOAD64_BYTE( "epr-b-10.9b",  0x000006, 0x40000, CRC(2ce56f9f) SHA1(5bd23823df52aeff8e84a6eb3199a5e75f38cd42) )
	ROM_LOAD64_BYTE( "epr-b-04.9a",  0x000007, 0x40000, CRC(0ad7fb2b) SHA1(51265b331cbbe9d133274bafdbf043151240a20d) )
	ROM_LOAD64_BYTE( "epr-b-19.8e",  0x200000, 0x40000, CRC(031525cc) SHA1(65e280607c058cf9c005a6fc809ed375d379c0bf) )
	ROM_LOAD64_BYTE( "epr-b-14.8c",  0x200001, 0x40000, CRC(c97046a5) SHA1(88c7becaa2713c85e22ddd3e2305770525a9132b) )
	ROM_LOAD64_BYTE( "epr-b-21.10e", 0x200002, 0x40000, CRC(acbbdb09) SHA1(5d25f407c99727d166bcda570047cadd162f181d) )
	ROM_LOAD64_BYTE( "epr-b-16.10c", 0x200003, 0x40000, CRC(fa6f32d9) SHA1(e845365fe3af2f585f0a5d78dbedb97726da1a50) )
	ROM_LOAD64_BYTE( "epr-b-08.7b",  0x200004, 0x40000, CRC(14756473) SHA1(5ae69385cc77466404718974516bb6e87c03c11c) )
	ROM_LOAD64_BYTE( "epr-b-02.7a",  0x200005, 0x40000, CRC(ee3d878a) SHA1(624a18322d88fc4983f0008e1c8b21ed75f34ffc) )
	ROM_LOAD64_BYTE( "epr-b-11.11b", 0x200006, 0x40000, CRC(dbbfd400) SHA1(fc25928b6ed025fa2397d924bae93fd28a4e728f) )
	ROM_LOAD64_BYTE( "epr-b-05.11a", 0x200007, 0x40000, CRC(37635e97) SHA1(b4e5dd6e93174af76aaeeb47471ded0d0bc253e2) )
	ROM_LOAD64_BYTE( "epr-b-20.9e",  0x400000, 0x40000, CRC(27cae573) SHA1(f4aba951e8c5452f7d25f5117df4c1370c1790f9) )
	ROM_LOAD64_BYTE( "epr-b-15.9c",  0x400001, 0x40000, CRC(14e46ab1) SHA1(56c04199f03c2929e479f820c40fddbd0fc29e1d) )
	ROM_LOAD64_BYTE( "epr-b-22.11e", 0x400002, 0x40000, CRC(f241f0c7) SHA1(6fc89c1a83b7043e989c03bdad411b3e2a688528) )
	ROM_LOAD64_BYTE( "epr-b-17.11c", 0x400003, 0x40000, CRC(f187086b) SHA1(90966690d1fe415f73aaff77a029205b34dbb7e4) )
	ROM_LOAD64_BYTE( "epr-b-09.8b",  0x400004, 0x40000, CRC(4894aa8f) SHA1(33c8406550eb8ed1489686924e34c906329445f3) )
	ROM_LOAD64_BYTE( "epr-b-03.8a",  0x400005, 0x40000, CRC(e8877e9d) SHA1(6401ef0dbf88c8f402e49c1499123e60669bd9d5) )
	ROM_LOAD64_BYTE( "epr-b-12.12b", 0x400006, 0x40000, CRC(c9d4ed76) SHA1(c79ab721cb7ca3769e99e8db256e2ee0d9df7e09) )
	ROM_LOAD64_BYTE( "epr-b-06.12a", 0x400007, 0x40000, CRC(67dcc295) SHA1(0bcec5d2dcd7e3423a577eeb458b5f7cff3c571c) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "27c512.13b",  0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) ) // == sf2_9.12a (not labeled)
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples, 27c010, data matches parent */
	ROM_LOAD( "epr-b-01.12c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) ) // == sf2_18.11c
	ROM_LOAD( "epr-b-02.13c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) ) // == sf2_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "unknown.1a",   0x0000, 0x0117, NO_DUMP ) // unknown custom gal
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-2 */
ROM_START( sf2ua )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2u_30a.11e", 0x00000, 0x20000, CRC(08beb861) SHA1(d47f16d0d692dc6405df0aecd7d9fc3f9718c0d1) )
	ROM_LOAD16_BYTE( "sf2u_37a.11f", 0x00001, 0x20000, CRC(b7638d69) SHA1(b615a2e0e8772462fd875b2e8d2ccba82a8b3c47) )
	ROM_LOAD16_BYTE( "sf2u_31a.12e", 0x40000, 0x20000, CRC(0d5394e0) SHA1(e1d88ff3669f1dbe1e3fbdf8aa9e2c63adbbcb48) )
	ROM_LOAD16_BYTE( "sf2u_38a.12f", 0x40001, 0x20000, CRC(42d6a79e) SHA1(5f1e2c176d065325883a60767d05b1a542372b6a) )
	ROM_LOAD16_BYTE( "sf2u_28a.9e",  0x80000, 0x20000, CRC(387a175c) SHA1(2635bb82758cf217cee63b254a537b02275a6838) )
	ROM_LOAD16_BYTE( "sf2u_35a.9f",  0x80001, 0x20000, CRC(a1a5adcc) SHA1(47874e6d403256d828474b29e3d93c92efd9e1ce) )
	ROM_LOAD16_BYTE( "sf2_29b.10e",  0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )
	ROM_LOAD16_BYTE( "sf2_36b.10f",  0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2-5m.4a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // in "2" socket
	ROM_LOAD64_WORD( "sf2-7m.6a",  0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // in "4" socket
	ROM_LOAD64_WORD( "sf2-1m.3a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // in "1" socket
	ROM_LOAD64_WORD( "sf2-3m.5a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // in "3" socket
	ROM_LOAD64_WORD( "sf2-6m.4c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // in "11" socket
	ROM_LOAD64_WORD( "sf2-8m.6c",  0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // in "13" socket
	ROM_LOAD64_WORD( "sf2-2m.3c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // in "10" socket
	ROM_LOAD64_WORD( "sf2-4m.5c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // in "12" socket
	ROM_LOAD64_WORD( "sf2-13m.4d", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // in "21" socket
	ROM_LOAD64_WORD( "sf2-15m.6d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // in "23" socket
	ROM_LOAD64_WORD( "sf2-9m.3d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // in "20" socket
	ROM_LOAD64_WORD( "sf2-11m.5d", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // in "22" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_9.12a",   0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-3 */
ROM_START( sf2ub )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2u_30b.11e", 0x00000, 0x20000, CRC(57bd7051) SHA1(5e211e75b1649b07723cabc03cf15636dbbae595) )   // == sf2e_30b.11e
	ROM_LOAD16_BYTE( "sf2u_37b.11f", 0x00001, 0x20000, CRC(4a54d479) SHA1(eaff7a0d3c858a567c02086fde163850f0f5631e) )
	ROM_LOAD16_BYTE( "sf2u_31b.12e", 0x40000, 0x20000, CRC(a673143d) SHA1(e565f0ec23d6deb543c72af5a83f070c07319477) )   // == sf2e_31b.12e
	ROM_LOAD16_BYTE( "sf2u_38b.12f", 0x40001, 0x20000, CRC(4c2ccef7) SHA1(77b119c70c255622b023de25d9af3b3aac52ea47) )   // == sf2e_38b.12f
	ROM_LOAD16_BYTE( "sf2u_28b.9e",  0x80000, 0x20000, CRC(4009955e) SHA1(7842dbef7650485639fbae49b9f4db7494d4f73d) )   // == sf2_28b.9e
	ROM_LOAD16_BYTE( "sf2u_35b.9f",  0x80001, 0x20000, CRC(8c1f3994) SHA1(5e1d334399d05a837c2d80f79eada543e83afaf7) )   // == sf2_35b.9f
	ROM_LOAD16_BYTE( "sf2_29b.10e",  0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )
	ROM_LOAD16_BYTE( "sf2_36b.10f",  0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2-5m.4a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // in "2" socket
	ROM_LOAD64_WORD( "sf2-7m.6a",  0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // in "4" socket
	ROM_LOAD64_WORD( "sf2-1m.3a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // in "1" socket
	ROM_LOAD64_WORD( "sf2-3m.5a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // in "3" socket
	ROM_LOAD64_WORD( "sf2-6m.4c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // in "11" socket
	ROM_LOAD64_WORD( "sf2-8m.6c",  0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // in "13" socket
	ROM_LOAD64_WORD( "sf2-2m.3c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // in "10" socket
	ROM_LOAD64_WORD( "sf2-4m.5c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // in "12" socket
	ROM_LOAD64_WORD( "sf2-13m.4d", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // in "21" socket
	ROM_LOAD64_WORD( "sf2-15m.6d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // in "23" socket
	ROM_LOAD64_WORD( "sf2-9m.3d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // in "20" socket
	ROM_LOAD64_WORD( "sf2-11m.5d", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // in "22" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_9.12a",   0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-3 */
ROM_START( sf2uc )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2u_30c.11e", 0x00000, 0x20000, CRC(6cb59385) SHA1(37750e8369298b597590d956f5fb7319301e6c7d) )
	ROM_LOAD16_BYTE( "sf2u_37c.11f", 0x00001, 0x20000, CRC(32e2c278) SHA1(6cc5e0a9179163136b8104a1583da85a53d537f4) )
	ROM_LOAD16_BYTE( "sf2u_31c.12e", 0x40000, 0x20000, CRC(c4fff4a9) SHA1(4b593ace201fe7f5a00b5cd7f4e8fc3f8dd4ceed) )
	ROM_LOAD16_BYTE( "sf2u_38c.12f", 0x40001, 0x20000, CRC(8210fc0e) SHA1(7d6cfb99afa89d0e6e991d9f7c1808b740def125) )
	ROM_LOAD16_BYTE( "sf2u_28c.9e",  0x80000, 0x20000, CRC(6eddd5e8) SHA1(62bd1c2fc0321809421c9a592f691b5b1a1d8807) )
	ROM_LOAD16_BYTE( "sf2u_35c.9f",  0x80001, 0x20000, CRC(6bcb404c) SHA1(b5f24556c633c521aadd94e016d78db6922e3dfa) )
	ROM_LOAD16_BYTE( "sf2_29b.10e",  0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )
	ROM_LOAD16_BYTE( "sf2_36b.10f",  0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2-5m.4a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // in "2" socket
	ROM_LOAD64_WORD( "sf2-7m.6a",  0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // in "4" socket
	ROM_LOAD64_WORD( "sf2-1m.3a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // in "1" socket
	ROM_LOAD64_WORD( "sf2-3m.5a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // in "3" socket
	ROM_LOAD64_WORD( "sf2-6m.4c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // in "11" socket
	ROM_LOAD64_WORD( "sf2-8m.6c",  0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // in "13" socket
	ROM_LOAD64_WORD( "sf2-2m.3c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // in "10" socket
	ROM_LOAD64_WORD( "sf2-4m.5c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // in "12" socket
	ROM_LOAD64_WORD( "sf2-13m.4d", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // in "21" socket
	ROM_LOAD64_WORD( "sf2-15m.6d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // in "23" socket
	ROM_LOAD64_WORD( "sf2-9m.3d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // in "20" socket
	ROM_LOAD64_WORD( "sf2-11m.5d", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // in "22" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_9.12a",   0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )   // could be sf2_09.12a
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-3 */
ROM_START( sf2ud )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2u_30d.11e", 0x00000, 0x20000, CRC(4bb2657c) SHA1(b2d077296b77be7db371f953b7fc446a67d8a9d6) )   // == sf2e_30d.11e
	ROM_LOAD16_BYTE( "sf2u_37d.11f", 0x00001, 0x20000, CRC(b33b42f2) SHA1(2e0babc8734c79dc2b51a6be64433bb2411c3da5) )
	ROM_LOAD16_BYTE( "sf2u_31d.12e", 0x40000, 0x20000, CRC(d57b67d7) SHA1(43d0b47c9fada8d9b445caa4b96ac8493061aa8b) )   // == sf2e_31d.12e
	ROM_LOAD16_BYTE( "sf2u_38d.12f", 0x40001, 0x20000, CRC(9c8916ef) SHA1(a4629356a816454bcc1d7b41e70e147d4769a682) )   // == sf2e_38d.12f
	ROM_LOAD16_BYTE( "sf2u_28d.9e",  0x80000, 0x20000, CRC(175819d1) SHA1(c98b6b7af4e57735dbfb3d1e61ba1bfb9f145d33) )   // == sf2e_28d.9e
	ROM_LOAD16_BYTE( "sf2u_35d.9f",  0x80001, 0x20000, CRC(82060da4) SHA1(7487cfc28cce3d76772ece657aef83b56034011e) )   // == sf2e_35d.9f
	ROM_LOAD16_BYTE( "sf2_29b.10e",  0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )
	ROM_LOAD16_BYTE( "sf2_36b.10f",  0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2-5m.4a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // in "2" socket
	ROM_LOAD64_WORD( "sf2-7m.6a",  0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // in "4" socket
	ROM_LOAD64_WORD( "sf2-1m.3a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // in "1" socket
	ROM_LOAD64_WORD( "sf2-3m.5a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // in "3" socket
	ROM_LOAD64_WORD( "sf2-6m.4c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // in "11" socket
	ROM_LOAD64_WORD( "sf2-8m.6c",  0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // in "13" socket
	ROM_LOAD64_WORD( "sf2-2m.3c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // in "10" socket
	ROM_LOAD64_WORD( "sf2-4m.5c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // in "12" socket
	ROM_LOAD64_WORD( "sf2-13m.4d", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // in "21" socket
	ROM_LOAD64_WORD( "sf2-15m.6d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // in "23" socket
	ROM_LOAD64_WORD( "sf2-9m.3d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // in "20" socket
	ROM_LOAD64_WORD( "sf2-11m.5d", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // in "22" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_9.12a",   0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-3 */
/* Note that this revision seems to be the only one that uses the IOB2 and C632B PALs instead of the IOB1 and C632,
   while STF29 PAL is confirmed to be the same as the other Street Fighter II: The World Warrior sets. */
ROM_START( sf2ue )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2u_30e.11e", 0x00000, 0x20000, CRC(f37cd088) SHA1(48b71e44ce88d5f682ed679c737e7ec5262bb0df) )   // == sf2e_30e.11e
	ROM_LOAD16_BYTE( "sf2u_37e.11f", 0x00001, 0x20000, CRC(6c61a513) SHA1(6dc9ccd58fd5ef15ff9df20c865ff6c850f2b7dc) )
	ROM_LOAD16_BYTE( "sf2u_31e.12e", 0x40000, 0x20000, CRC(7c4771b4) SHA1(6637b24194c86ec72a1775d4e976891243cd66fd) )   // == sf2e_31e.12e
	ROM_LOAD16_BYTE( "sf2u_38e.12f", 0x40001, 0x20000, CRC(a4bd0cd9) SHA1(32a2bc18d1f860668141e53cbca862ceec238c19) )   // == sf2e_38e.12f
	ROM_LOAD16_BYTE( "sf2u_28e.9e",  0x80000, 0x20000, CRC(e3b95625) SHA1(f7277f9980040f96434d1bd162eaf9ba0dfbb005) )   // == sf2e_28e.9e
	ROM_LOAD16_BYTE( "sf2u_35e.9f",  0x80001, 0x20000, CRC(3648769a) SHA1(74e5934b0e3b4da35ff48086f41e7502b42731c6) )   // == sf2e_35e.9f
	ROM_LOAD16_BYTE( "sf2_29b.10e",  0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )
	ROM_LOAD16_BYTE( "sf2_36b.10f",  0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2-5m.4a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // in "2" socket
	ROM_LOAD64_WORD( "sf2-7m.6a",  0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // in "4" socket
	ROM_LOAD64_WORD( "sf2-1m.3a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // in "1" socket
	ROM_LOAD64_WORD( "sf2-3m.5a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // in "3" socket
	ROM_LOAD64_WORD( "sf2-6m.4c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // in "11" socket
	ROM_LOAD64_WORD( "sf2-8m.6c",  0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // in "13" socket
	ROM_LOAD64_WORD( "sf2-2m.3c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // in "10" socket
	ROM_LOAD64_WORD( "sf2-4m.5c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // in "12" socket
	ROM_LOAD64_WORD( "sf2-13m.4d", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // in "21" socket
	ROM_LOAD64_WORD( "sf2-15m.6d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // in "23" socket
	ROM_LOAD64_WORD( "sf2-9m.3d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // in "20" socket
	ROM_LOAD64_WORD( "sf2-11m.5d", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // in "22" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_9.12a",   0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob2.11d",     0x0000, 0x0117, CRC(d26f0a27) SHA1(22bb5647ff98df22fd19ae079ff98b9d100855f9) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632b.ic1",    0x0000, 0x0117, CRC(5c3cbb67) SHA1(e947078640e0b1a6cc51958cbf84b7e407213452) )
ROM_END

/* B-Board 90629B-3 */
ROM_START( sf2uf )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2u_30f.11e", 0x00000, 0x20000, CRC(fe39ee33) SHA1(22558eb15e035b09b80935a32b8425d91cd79669) )   // == sf2e_30f.11e
	ROM_LOAD16_BYTE( "sf2u_37f.11f", 0x00001, 0x20000, CRC(169e7388) SHA1(c7cb2de529d94cea4a018ed3bd611037fe54abe7) )
	ROM_LOAD16_BYTE( "sf2u_31f.12e", 0x40000, 0x20000, CRC(69a0a301) SHA1(86a3954335310865b14ce8b4e0e4499feb14fc12) )   // == sf2e_31f.12e
	ROM_LOAD16_BYTE( "sf2u_38f.12f", 0x40001, 0x20000, CRC(1510e4e2) SHA1(fbfdd4e42c4bc894592dbe5a84c88d5f13d21da4) )   // == sf2e_38f.12f
	ROM_LOAD16_BYTE( "sf2u_28f.9e",  0x80000, 0x20000, CRC(acd8175b) SHA1(504991c46fa568d31ce69bd63e2a67926a06b5a9) )   // == sf2e_28f.9e
	ROM_LOAD16_BYTE( "sf2u_35f.9f",  0x80001, 0x20000, CRC(c0a80bd1) SHA1(ac25a9ed488e03baf4115541fdcce3973ce6a442) )   // == sf2e_35f.9f
	ROM_LOAD16_BYTE( "sf2_29b.10e",  0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )
	ROM_LOAD16_BYTE( "sf2_36b.10f",  0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2-5m.4a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // in "2" socket
	ROM_LOAD64_WORD( "sf2-7m.6a",  0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // in "4" socket
	ROM_LOAD64_WORD( "sf2-1m.3a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // in "1" socket
	ROM_LOAD64_WORD( "sf2-3m.5a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // in "3" socket
	ROM_LOAD64_WORD( "sf2-6m.4c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // in "11" socket
	ROM_LOAD64_WORD( "sf2-8m.6c",  0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // in "13" socket
	ROM_LOAD64_WORD( "sf2-2m.3c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // in "10" socket
	ROM_LOAD64_WORD( "sf2-4m.5c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // in "12" socket
	ROM_LOAD64_WORD( "sf2-13m.4d", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // in "21" socket
	ROM_LOAD64_WORD( "sf2-15m.6d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // in "23" socket
	ROM_LOAD64_WORD( "sf2-9m.3d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // in "20" socket
	ROM_LOAD64_WORD( "sf2-11m.5d", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // in "22" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_9.12a",   0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-3 */
ROM_START( sf2ug )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2u_30g.11e", 0x00000, 0x20000, CRC(fe39ee33) SHA1(22558eb15e035b09b80935a32b8425d91cd79669) )   // == sf2e_30g.11e
	ROM_LOAD16_BYTE( "sf2u_37g.11f", 0x00001, 0x20000, CRC(5886cae7) SHA1(8a621d267dfcf5d214b1bbec12b98a06153c86d3) )
	ROM_LOAD16_BYTE( "sf2u_31g.12e", 0x40000, 0x20000, CRC(69a0a301) SHA1(86a3954335310865b14ce8b4e0e4499feb14fc12) )   // == sf2e_31g.12e
	ROM_LOAD16_BYTE( "sf2u_38g.12f", 0x40001, 0x20000, CRC(5e22db70) SHA1(6565946591a18eaf46f04c1aa449ee0ae9ac2901) )   // == sf2e_38g.12f
	ROM_LOAD16_BYTE( "sf2u_28g.9e",  0x80000, 0x20000, CRC(8bf9f1e5) SHA1(bbcef63f35e5bff3f373968ba1278dd6bd86b593) )   // == sf2e_28g.9e
	ROM_LOAD16_BYTE( "sf2u_35g.9f",  0x80001, 0x20000, CRC(626ef934) SHA1(507bda3e4519de237aca919cf72e543403ec9724) )   // == sf2e_35g.9f
	ROM_LOAD16_BYTE( "sf2_29b.10e",  0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )
	ROM_LOAD16_BYTE( "sf2_36b.10f",  0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2-5m.4a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // in "2" socket
	ROM_LOAD64_WORD( "sf2-7m.6a",  0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // in "4" socket
	ROM_LOAD64_WORD( "sf2-1m.3a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // in "1" socket
	ROM_LOAD64_WORD( "sf2-3m.5a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // in "3" socket
	ROM_LOAD64_WORD( "sf2-6m.4c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // in "11" socket
	ROM_LOAD64_WORD( "sf2-8m.6c",  0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // in "13" socket
	ROM_LOAD64_WORD( "sf2-2m.3c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // in "10" socket
	ROM_LOAD64_WORD( "sf2-4m.5c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // in "12" socket
	ROM_LOAD64_WORD( "sf2-13m.4d", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // in "21" socket
	ROM_LOAD64_WORD( "sf2-15m.6d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // in "23" socket
	ROM_LOAD64_WORD( "sf2-9m.3d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // in "20" socket
	ROM_LOAD64_WORD( "sf2-11m.5d", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // in "22" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_9.12a",   0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-3 */
ROM_START( sf2uh ) /* same as sf2jh - Street Fighter II: The World Warrior (Japan 910522) except for region byte */
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2u_30h.11e", 0x00000, 0x20000, CRC(fe39ee33) SHA1(22558eb15e035b09b80935a32b8425d91cd79669) )   // == sf2j_30h.11e / sf2e_30g.11e
	ROM_LOAD16_BYTE( "sf2u_37h.11f", 0x00001, 0x20000, CRC(e4dffbfe) SHA1(79c523d11be8a2b4991221c03020e671f48f5c14) )   // 1 byte difference to sf2j_37h.11f - 0x4C0 == 0x02 vs 0x00 for Japanese set
	ROM_LOAD16_BYTE( "sf2u_31h.12e", 0x40000, 0x20000, CRC(69a0a301) SHA1(86a3954335310865b14ce8b4e0e4499feb14fc12) )   // == sf2j_31h.12e / sf2e_31g.12e
	ROM_LOAD16_BYTE( "sf2u_38h.12f", 0x40001, 0x20000, CRC(a659f678) SHA1(f3b99ebaa59edb889498cf5c9d7ceb939da1dedc) )   // == sf2j_38h.12f
	ROM_LOAD16_BYTE( "sf2u_28h.9e",  0x80000, 0x20000, CRC(8a5c8ee0) SHA1(a6df46e96bf7596665177b18213a3aee4cc7c378) )   // == sf2j_28h.9e
	ROM_LOAD16_BYTE( "sf2u_35h.9f",  0x80001, 0x20000, CRC(c828fc4d) SHA1(0025630bef45862d8b72a2d19a6c5adf96905bd5) )   // == sf2j_35h.9f
	ROM_LOAD16_BYTE( "sf2_29b.10e",  0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )
	ROM_LOAD16_BYTE( "sf2_36b.10f",  0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2-5m.4a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // in "2" socket
	ROM_LOAD64_WORD( "sf2-7m.6a",  0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // in "4" socket
	ROM_LOAD64_WORD( "sf2-1m.3a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // in "1" socket
	ROM_LOAD64_WORD( "sf2-3m.5a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // in "3" socket
	ROM_LOAD64_WORD( "sf2-6m.4c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // in "11" socket
	ROM_LOAD64_WORD( "sf2-8m.6c",  0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // in "13" socket
	ROM_LOAD64_WORD( "sf2-2m.3c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // in "10" socket
	ROM_LOAD64_WORD( "sf2-4m.5c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // in "12" socket
	ROM_LOAD64_WORD( "sf2-13m.4d", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // in "21" socket
	ROM_LOAD64_WORD( "sf2-15m.6d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // in "23" socket
	ROM_LOAD64_WORD( "sf2-9m.3d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // in "20" socket
	ROM_LOAD64_WORD( "sf2-11m.5d", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // in "22" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_9.12a",   0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-3 */
ROM_START( sf2ui )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2u_30i.11e", 0x00000, 0x20000, CRC(fe39ee33) SHA1(22558eb15e035b09b80935a32b8425d91cd79669) )   // == sf2e_30g.11e
	ROM_LOAD16_BYTE( "sf2u_37i.11f", 0x00001, 0x20000, CRC(9df707dd) SHA1(b148ea450f9e96f3c20f487010a3c57f778e40c1) )
	ROM_LOAD16_BYTE( "sf2u_31i.12e", 0x40000, 0x20000, CRC(69a0a301) SHA1(86a3954335310865b14ce8b4e0e4499feb14fc12) )   // == sf2e_31g.12e
	ROM_LOAD16_BYTE( "sf2u_38i.12f", 0x40001, 0x20000, CRC(4cb46daf) SHA1(dee103ae1391cd9ac150f787187233cd8c06ea1e) )
	ROM_LOAD16_BYTE( "sf2u_28i.9e",  0x80000, 0x20000, CRC(1580be4c) SHA1(d89ed0ff4bf14ff2eaae4609f55970b6b37c8e32) )   /* seen the same pcb with SF2_28I.9E */
	ROM_LOAD16_BYTE( "sf2u_35i.9f",  0x80001, 0x20000, CRC(1468d185) SHA1(750de0cad3859e4917aebb02c2e137dea619f201) )
	ROM_LOAD16_BYTE( "sf2_29b.10e",  0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )
	ROM_LOAD16_BYTE( "sf2_36b.10f",  0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2-5m.4a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // in "2" socket
	ROM_LOAD64_WORD( "sf2-7m.6a",  0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // in "4" socket
	ROM_LOAD64_WORD( "sf2-1m.3a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // in "1" socket
	ROM_LOAD64_WORD( "sf2-3m.5a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // in "3" socket
	ROM_LOAD64_WORD( "sf2-6m.4c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // in "11" socket
	ROM_LOAD64_WORD( "sf2-8m.6c",  0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // in "13" socket
	ROM_LOAD64_WORD( "sf2-2m.3c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // in "10" socket
	ROM_LOAD64_WORD( "sf2-4m.5c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // in "12" socket
	ROM_LOAD64_WORD( "sf2-13m.4d", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // in "21" socket
	ROM_LOAD64_WORD( "sf2-15m.6d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // in "23" socket
	ROM_LOAD64_WORD( "sf2-9m.3d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // in "20" socket
	ROM_LOAD64_WORD( "sf2-11m.5d", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // in "22" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_9.12a",   0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-3 */
ROM_START( sf2uk )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2u_30k.11e", 0x00000, 0x20000, CRC(8f66076c) SHA1(f9653b36bb5012e6bde5fe3bcade4a6a7a7e7def) )
	ROM_LOAD16_BYTE( "sf2u_37k.11f", 0x00001, 0x20000, CRC(4e1f6a83) SHA1(ee679b79ff3c3165979d3de23e0f668839cf465f) )
	ROM_LOAD16_BYTE( "sf2u_31k.12e", 0x40000, 0x20000, CRC(f9f89f60) SHA1(c3b71482b85c83576518f300be768655412276b0) )
	ROM_LOAD16_BYTE( "sf2u_38k.12f", 0x40001, 0x20000, CRC(6ce0a85a) SHA1(567fd18cd626c94496d9123ecef87dc638f0041a) )
	ROM_LOAD16_BYTE( "sf2u_28k.9e",  0x80000, 0x20000, CRC(8e958f31) SHA1(81359bc988c4e9e375b5bbd960921d425b77f706) )
	ROM_LOAD16_BYTE( "sf2u_35k.9f",  0x80001, 0x20000, CRC(fce76fad) SHA1(66f881ba600c7e6bbe960cfd0772ed16208b79c8) )
	ROM_LOAD16_BYTE( "sf2u_29a.10e", 0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )   // == sf2_29b.10e
	ROM_LOAD16_BYTE( "sf2u_36a.10f", 0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )   // == sf2_36b.10f

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2-5m.4a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // in "2" socket
	ROM_LOAD64_WORD( "sf2-7m.6a",  0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // in "4" socket
	ROM_LOAD64_WORD( "sf2-1m.3a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // in "1" socket
	ROM_LOAD64_WORD( "sf2-3m.5a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // in "3" socket
	ROM_LOAD64_WORD( "sf2-6m.4c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // in "11" socket
	ROM_LOAD64_WORD( "sf2-8m.6c",  0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // in "13" socket
	ROM_LOAD64_WORD( "sf2-2m.3c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // in "10" socket
	ROM_LOAD64_WORD( "sf2-4m.5c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // in "12" socket
	ROM_LOAD64_WORD( "sf2-13m.4d", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // in "21" socket
	ROM_LOAD64_WORD( "sf2-15m.6d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // in "23" socket
	ROM_LOAD64_WORD( "sf2-9m.3d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // in "20" socket
	ROM_LOAD64_WORD( "sf2-11m.5d", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // in "22" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_09.12a",  0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )   // == sf2_9.12a
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-2 */
ROM_START( sf2um ) /* same as sf2jl - Street Fighter II: The World Warrior (Japan 920312) except for region byte */
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf-2u_30m.11e", 0x00000, 0x20000, CRC(34a1ce02) SHA1(6875813c49a92b8650f444c1857459adc86bc0d0) )   // == sf-2_30l.11e
	ROM_LOAD16_BYTE( "sf-2u_37m.11f", 0x00001, 0x20000, CRC(8cbff19c) SHA1(48386d4dfb3a592094e6c3ee245b6f2c84d5d9af) )   // 1 byte difference to sf-2_37l.11f - 0x4C0 == 0x02 vs 0x00 for Japanese set
	ROM_LOAD16_BYTE( "sf-2u_31m.12e", 0x40000, 0x20000, CRC(64ebc8d2) SHA1(1817de5dcca47a1bb94838d26a9a3adf6b75c911) )   // == sf-2_31l.12e
	ROM_LOAD16_BYTE( "sf-2u_38m.12f", 0x40001, 0x20000, CRC(73847443) SHA1(72fb5472a04061e4a50cb57e4f65ba8ec022a965) )   // == sf-2_38l.12f
	ROM_LOAD16_BYTE( "sf-2u_28m.9e",  0x80000, 0x20000, CRC(eee2b426) SHA1(1d7b5073b9473d3c92fc95c0d0625716c82fc944) )   // == sf-2_28l.9e
	ROM_LOAD16_BYTE( "sf-2u_35m.9f",  0x80001, 0x20000, CRC(eca8b452) SHA1(f3d06bd32ef2649a8278d8a0517372067e3f99b7) )   // == sf-2_35l.9f
	ROM_LOAD16_BYTE( "sf-2u_29m.10e", 0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )   // == sf2_29b.10e - actually labeled as "M" revision
	ROM_LOAD16_BYTE( "sf-2u_36m.10f", 0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )   // == sf2_36b.10f - actually labeled as "M" revision

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2-5m.4a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // in "2" socket
	ROM_LOAD64_WORD( "sf2-7m.6a",  0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // in "4" socket
	ROM_LOAD64_WORD( "sf2-1m.3a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // in "1" socket
	ROM_LOAD64_WORD( "sf2-3m.5a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // in "3" socket
	ROM_LOAD64_WORD( "sf2-6m.4c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // in "11" socket
	ROM_LOAD64_WORD( "sf2-8m.6c",  0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // in "13" socket
	ROM_LOAD64_WORD( "sf2-2m.3c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // in "10" socket
	ROM_LOAD64_WORD( "sf2-4m.5c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // in "12" socket
	ROM_LOAD64_WORD( "sf2-13m.4d", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // in "21" socket
	ROM_LOAD64_WORD( "sf2-15m.6d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // in "23" socket
	ROM_LOAD64_WORD( "sf2-9m.3d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // in "20" socket
	ROM_LOAD64_WORD( "sf2-11m.5d", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // in "22" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_09.12a", 0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )   // == sf2_9.12a
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c", 0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c", 0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B */
ROM_START( sf2j )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2j30.bin",    0x00000, 0x20000, CRC(79022b31) SHA1(b7cfe0498260cdd2779580c47829dd02435ffff4) )
	ROM_LOAD16_BYTE( "sf2j37.bin",    0x00001, 0x20000, CRC(516776ec) SHA1(4f8b63c4d4265a105751fa72b50bd0fa538bf881) )
	ROM_LOAD16_BYTE( "sf2j31.bin",    0x40000, 0x20000, CRC(fe15cb39) SHA1(383478524881ea70d9e04c9b6143b8735b637eee) )
	ROM_LOAD16_BYTE( "sf2j38.bin",    0x40001, 0x20000, CRC(38614d70) SHA1(39c58096f3a8e01fb439639b742b83102bbaa7f6) )
	ROM_LOAD16_BYTE( "sf2j28.bin",    0x80000, 0x20000, CRC(d283187a) SHA1(5ea83d2652e43e46b831b614d1fe06d465bac9a3) )
	ROM_LOAD16_BYTE( "sf2j35.bin",    0x80001, 0x20000, CRC(d28158e4) SHA1(bf2bca6068e374011afa95e99809d262f522df18) )
	ROM_LOAD16_BYTE( "sf2_29a.bin",   0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )
	ROM_LOAD16_BYTE( "sf2_36a.bin",   0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2_06.8a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // == sf2-5m.4a
	ROM_LOAD64_WORD( "sf2_08.10a", 0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // == sf2-7m.6a
	ROM_LOAD64_WORD( "sf2_05.7a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // == sf2-1m.3a
	ROM_LOAD64_WORD( "sf2_07.9a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // == sf2-3m.5a
	ROM_LOAD64_WORD( "sf2_15.8c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // == sf2-6m.4c
	ROM_LOAD64_WORD( "sf2_17.10c", 0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // == sf2-8m.6c
	ROM_LOAD64_WORD( "sf2_14.7c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // == sf2-2m.3c
	ROM_LOAD64_WORD( "sf2_16.9c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // == sf2-4m.5c
	ROM_LOAD64_WORD( "sf2_25.8d",  0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // == sf2-13m.4d
	ROM_LOAD64_WORD( "sf2_27.10d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // == sf2-15m.6d
	ROM_LOAD64_WORD( "sf2_24.7d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // == sf2-9m.3d
	ROM_LOAD64_WORD( "sf2_26.9d",  0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // == sf2-11m.5d

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_09.bin",    0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-3 */
ROM_START( sf2j17 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2_30l.11e",    0x00000, 0x20000, CRC(79022b31) SHA1(b7cfe0498260cdd2779580c47829dd02435ffff4) )
	ROM_LOAD16_BYTE( "sf2j_37l.11f",   0x00001, 0x20000, CRC(04ba20c7) SHA1(e9e2829262de90e991e7d978fe952763e576d4ac) )
	ROM_LOAD16_BYTE( "sf2_31l.12e",    0x40000, 0x20000, CRC(fe15cb39) SHA1(383478524881ea70d9e04c9b6143b8735b637eee) )
	ROM_LOAD16_BYTE( "sf2_38l.12f",    0x40001, 0x20000, CRC(65cb1883) SHA1(59ddabd3b64df2699ca982c443e909b7ac4f3b28) )
	ROM_LOAD16_BYTE( "sf2_28l.9e",     0x80000, 0x20000, CRC(d283187a) SHA1(5ea83d2652e43e46b831b614d1fe06d465bac9a3) )
	ROM_LOAD16_BYTE( "sf2_35l.9f",     0x80001, 0x20000, CRC(e3266622) SHA1(77f981644f1e3af8a4327eab56ba6b39631b3421) )
	ROM_LOAD16_BYTE( "sf2_29l.10e",    0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )
	ROM_LOAD16_BYTE( "sf2_36l.10f",    0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2_06.8a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // == sf2-5m.4a
	ROM_LOAD64_WORD( "sf2_08.10a", 0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // == sf2-7m.6a
	ROM_LOAD64_WORD( "sf2_05.7a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // == sf2-1m.3a
	ROM_LOAD64_WORD( "sf2_07.9a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // == sf2-3m.5a
	ROM_LOAD64_WORD( "sf2_15.8c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // == sf2-6m.4c
	ROM_LOAD64_WORD( "sf2_17.10c", 0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // == sf2-8m.6c
	ROM_LOAD64_WORD( "sf2_14.7c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // == sf2-2m.3c
	ROM_LOAD64_WORD( "sf2_16.9c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // == sf2-4m.5c
	ROM_LOAD64_WORD( "sf2_25.8d",  0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // == sf2-13m.4d
	ROM_LOAD64_WORD( "sf2_27.10d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // == sf2-15m.6d
	ROM_LOAD64_WORD( "sf2_24.7d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // == sf2-9m.3d
	ROM_LOAD64_WORD( "sf2_26.9d",  0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // == sf2-11m.5d

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_09.12a",    0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-2 */
ROM_START( sf2ja )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2j_30a.11e", 0x00000, 0x20000, CRC(57bd7051) SHA1(5e211e75b1649b07723cabc03cf15636dbbae595) )   // == sf2e_30b.11e
	ROM_LOAD16_BYTE( "sf2j_37a.11f", 0x00001, 0x20000, CRC(1e1f6844) SHA1(c80e5ac6a6cea39511c38e31ea55b6cd3888024f) )
	ROM_LOAD16_BYTE( "sf2j_31a.12e", 0x40000, 0x20000, CRC(a673143d) SHA1(e565f0ec23d6deb543c72af5a83f070c07319477) )   // == sf2e_31b.12e
	ROM_LOAD16_BYTE( "sf2j_38a.12f", 0x40001, 0x20000, CRC(4c2ccef7) SHA1(77b119c70c255622b023de25d9af3b3aac52ea47) )   // == sf2e_38b.12f
	ROM_LOAD16_BYTE( "sf2j_28a.9e",  0x80000, 0x20000, CRC(4009955e) SHA1(7842dbef7650485639fbae49b9f4db7494d4f73d) )   // == sf2_28b.9e
	ROM_LOAD16_BYTE( "sf2j_35a.9f",  0x80001, 0x20000, CRC(8c1f3994) SHA1(5e1d334399d05a837c2d80f79eada543e83afaf7) )   // == sf2_35b.9f
	ROM_LOAD16_BYTE( "sf2j_29a.10e", 0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )   // == sf2_29b.10e
	ROM_LOAD16_BYTE( "sf2j_36a.10f", 0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )   // == sf2_36b.10f

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2_06.8a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // == sf2-5m.4a
	ROM_LOAD64_WORD( "sf2_08.10a", 0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // == sf2-7m.6a
	ROM_LOAD64_WORD( "sf2_05.7a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // == sf2-1m.3a
	ROM_LOAD64_WORD( "sf2_07.9a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // == sf2-3m.5a
	ROM_LOAD64_WORD( "sf2_15.8c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // == sf2-6m.4c
	ROM_LOAD64_WORD( "sf2_17.10c", 0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // == sf2-8m.6c
	ROM_LOAD64_WORD( "sf2_14.7c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // == sf2-2m.3c
	ROM_LOAD64_WORD( "sf2_16.9c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // == sf2-4m.5c
	ROM_LOAD64_WORD( "sf2_25.8d",  0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // == sf2-13m.4d
	ROM_LOAD64_WORD( "sf2_27.10d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // == sf2-15m.6d
	ROM_LOAD64_WORD( "sf2_24.7d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // == sf2-9m.3d
	ROM_LOAD64_WORD( "sf2_26.9d",  0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // == sf2-11m.5d

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2j_09.12a", 0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )   // == sf2_9.12a
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2j_18.11c", 0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )   // == sf2_18.11c
	ROM_LOAD( "sf2j_19.12c", 0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )   // == sf2_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-3 */
ROM_START( sf2jc )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2j_30c.11e", 0x00000, 0x20000, CRC(8add35ec) SHA1(b08428ff262ca4feddd3c72058b4b674a5401aba) )
	ROM_LOAD16_BYTE( "sf2j_37c.11f", 0x00001, 0x20000, CRC(0d74a256) SHA1(587fd0ee1c2ef54554237486eb5b0d1ec30c2868) )
	ROM_LOAD16_BYTE( "sf2j_31c.12e", 0x40000, 0x20000, CRC(c4fff4a9) SHA1(4b593ace201fe7f5a00b5cd7f4e8fc3f8dd4ceed) )   // == sf2u_31c.12e
	ROM_LOAD16_BYTE( "sf2j_38c.12f", 0x40001, 0x20000, CRC(8210fc0e) SHA1(7d6cfb99afa89d0e6e991d9f7c1808b740def125) )   // == sf2u_38c.12f
	ROM_LOAD16_BYTE( "sf2j_28c.9e",  0x80000, 0x20000, CRC(6eddd5e8) SHA1(62bd1c2fc0321809421c9a592f691b5b1a1d8807) )   // == sf2u_28c.9e
	ROM_LOAD16_BYTE( "sf2j_35c.9f",  0x80001, 0x20000, CRC(6bcb404c) SHA1(b5f24556c633c521aadd94e016d78db6922e3dfa) )   // == sf2u_35c.9f
	ROM_LOAD16_BYTE( "sf2j_29a.10e", 0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )   // == sf2_29b.10e
	ROM_LOAD16_BYTE( "sf2j_36a.10f", 0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )   // == sf2_36b.10f

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2_06.8a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // == sf2-5m.4a
	ROM_LOAD64_WORD( "sf2_08.10a", 0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // == sf2-7m.6a
	ROM_LOAD64_WORD( "sf2_05.7a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // == sf2-1m.3a
	ROM_LOAD64_WORD( "sf2_07.9a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // == sf2-3m.5a
	ROM_LOAD64_WORD( "sf2_15.8c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // == sf2-6m.4c
	ROM_LOAD64_WORD( "sf2_17.10c", 0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // == sf2-8m.6c
	ROM_LOAD64_WORD( "sf2_14.7c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // == sf2-2m.3c
	ROM_LOAD64_WORD( "sf2_16.9c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // == sf2-4m.5c
	ROM_LOAD64_WORD( "sf2_25.8d",  0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // == sf2-13m.4d
	ROM_LOAD64_WORD( "sf2_27.10d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // == sf2-15m.6d
	ROM_LOAD64_WORD( "sf2_24.7d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // == sf2-9m.3d
	ROM_LOAD64_WORD( "sf2_26.9d",  0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // == sf2-11m.5d

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_09.12a",  0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )   // == sf2_9.12a
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2j_18.11c", 0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )   // == sf2_18.11c
	ROM_LOAD( "sf2j_19.12c", 0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )   // == sf2_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-3 */
ROM_START( sf2jf )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2j_30f.11e", 0x00000, 0x20000, CRC(fe39ee33) SHA1(22558eb15e035b09b80935a32b8425d91cd79669) )   // == sf2e_30g.11e
	ROM_LOAD16_BYTE( "sf2j_37f.11f", 0x00001, 0x20000, CRC(c1428cc6) SHA1(6a3f21de57c3dd60a7c991ca667dc2a77d813039) )
	ROM_LOAD16_BYTE( "sf2j_31f.12e", 0x40000, 0x20000, CRC(69a0a301) SHA1(86a3954335310865b14ce8b4e0e4499feb14fc12) )   // == sf2e_31g.12e
	ROM_LOAD16_BYTE( "sf2j_38f.12f", 0x40001, 0x20000, CRC(1510e4e2) SHA1(fbfdd4e42c4bc894592dbe5a84c88d5f13d21da4) )
	ROM_LOAD16_BYTE( "sf2j_28f.9e",  0x80000, 0x20000, CRC(acd8175b) SHA1(504991c46fa568d31ce69bd63e2a67926a06b5a9) )
	ROM_LOAD16_BYTE( "sf2j_35f.9f",  0x80001, 0x20000, CRC(c0a80bd1) SHA1(ac25a9ed488e03baf4115541fdcce3973ce6a442) )
	ROM_LOAD16_BYTE( "sf2j_29a.10e", 0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )   // == sf2_29b.10e
	ROM_LOAD16_BYTE( "sf2j_36a.10f", 0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )   // == sf2_36b.10f

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2_06.8a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // == sf2-5m.4a
	ROM_LOAD64_WORD( "sf2_08.10a", 0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // == sf2-7m.6a
	ROM_LOAD64_WORD( "sf2_05.7a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // == sf2-1m.3a
	ROM_LOAD64_WORD( "sf2_07.9a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // == sf2-3m.5a
	ROM_LOAD64_WORD( "sf2_15.8c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // == sf2-6m.4c
	ROM_LOAD64_WORD( "sf2_17.10c", 0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // == sf2-8m.6c
	ROM_LOAD64_WORD( "sf2_14.7c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // == sf2-2m.3c
	ROM_LOAD64_WORD( "sf2_16.9c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // == sf2-4m.5c
	ROM_LOAD64_WORD( "sf2_25.8d",  0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // == sf2-13m.4d
	ROM_LOAD64_WORD( "sf2_27.10d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // == sf2-15m.6d
	ROM_LOAD64_WORD( "sf2_24.7d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // == sf2-9m.3d
	ROM_LOAD64_WORD( "sf2_26.9d",  0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // == sf2-11m.5d

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_09.12a",  0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )   // == sf2_9.12a
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-3 */
ROM_START( sf2jh )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf2j_30h.11e", 0x00000, 0x20000, CRC(fe39ee33) SHA1(22558eb15e035b09b80935a32b8425d91cd79669) )   // == sf2e_30g.11e
	ROM_LOAD16_BYTE( "sf2j_37h.11f", 0x00001, 0x20000, CRC(330304b0) SHA1(93bf761804228a79d16afa2fcfbe28e6942dff51) )
	ROM_LOAD16_BYTE( "sf2j_31h.12e", 0x40000, 0x20000, CRC(69a0a301) SHA1(86a3954335310865b14ce8b4e0e4499feb14fc12) )   // == sf2e_31g.12e
	ROM_LOAD16_BYTE( "sf2j_38h.12f", 0x40001, 0x20000, CRC(a659f678) SHA1(f3b99ebaa59edb889498cf5c9d7ceb939da1dedc) )
	ROM_LOAD16_BYTE( "sf2j_28h.9e",  0x80000, 0x20000, CRC(8a5c8ee0) SHA1(a6df46e96bf7596665177b18213a3aee4cc7c378) )
	ROM_LOAD16_BYTE( "sf2j_35h.9f",  0x80001, 0x20000, CRC(c828fc4d) SHA1(0025630bef45862d8b72a2d19a6c5adf96905bd5) )
	ROM_LOAD16_BYTE( "sf2j_29a.10e", 0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )   // == sf2_29b.10e
	ROM_LOAD16_BYTE( "sf2j_36a.10f", 0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )   // == sf2_36b.10f

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2_06.8a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // == sf2-5m.4a
	ROM_LOAD64_WORD( "sf2_08.10a", 0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // == sf2-7m.6a
	ROM_LOAD64_WORD( "sf2_05.7a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // == sf2-1m.3a
	ROM_LOAD64_WORD( "sf2_07.9a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // == sf2-3m.5a
	ROM_LOAD64_WORD( "sf2_15.8c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // == sf2-6m.4c
	ROM_LOAD64_WORD( "sf2_17.10c", 0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // == sf2-8m.6c
	ROM_LOAD64_WORD( "sf2_14.7c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // == sf2-2m.3c
	ROM_LOAD64_WORD( "sf2_16.9c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // == sf2-4m.5c
	ROM_LOAD64_WORD( "sf2_25.8d",  0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // == sf2-13m.4d
	ROM_LOAD64_WORD( "sf2_27.10d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // == sf2-15m.6d
	ROM_LOAD64_WORD( "sf2_24.7d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // == sf2-9m.3d
	ROM_LOAD64_WORD( "sf2_26.9d",  0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // == sf2-11m.5d

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_09.12a",  0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )   // == sf2_9.12a
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-2 */
ROM_START( sf2jl )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "sf-2_30l.11e", 0x00000, 0x20000, CRC(34a1ce02) SHA1(6875813c49a92b8650f444c1857459adc86bc0d0) )   // These "L" revision roms are verified as being
	ROM_LOAD16_BYTE( "sf-2_37l.11f", 0x00001, 0x20000, CRC(5b630ed2) SHA1(b14ef94b86f55c82391db8aca4d6f9487b9758a7) )   // labeled "SF-2" without "J" for Japan region
	ROM_LOAD16_BYTE( "sf-2_31l.12e", 0x40000, 0x20000, CRC(64ebc8d2) SHA1(1817de5dcca47a1bb94838d26a9a3adf6b75c911) )
	ROM_LOAD16_BYTE( "sf-2_38l.12f", 0x40001, 0x20000, CRC(73847443) SHA1(72fb5472a04061e4a50cb57e4f65ba8ec022a965) )
	ROM_LOAD16_BYTE( "sf-2_28l.9e",  0x80000, 0x20000, CRC(eee2b426) SHA1(1d7b5073b9473d3c92fc95c0d0625716c82fc944) )
	ROM_LOAD16_BYTE( "sf-2_35l.9f",  0x80001, 0x20000, CRC(eca8b452) SHA1(f3d06bd32ef2649a8278d8a0517372067e3f99b7) )
	ROM_LOAD16_BYTE( "sf2j_29a.10e", 0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )   // == sf2_29b.10e
	ROM_LOAD16_BYTE( "sf2j_36a.10f", 0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )   // == sf2_36b.10f

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2_06.8a",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // == sf2-5m.4a
	ROM_LOAD64_WORD( "sf2_08.10a", 0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // == sf2-7m.6a
	ROM_LOAD64_WORD( "sf2_05.7a",  0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // == sf2-1m.3a
	ROM_LOAD64_WORD( "sf2_07.9a",  0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // == sf2-3m.5a
	ROM_LOAD64_WORD( "sf2_15.8c",  0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // == sf2-6m.4c
	ROM_LOAD64_WORD( "sf2_17.10c", 0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // == sf2-8m.6c
	ROM_LOAD64_WORD( "sf2_14.7c",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // == sf2-2m.3c
	ROM_LOAD64_WORD( "sf2_16.9c",  0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // == sf2-4m.5c
	ROM_LOAD64_WORD( "sf2_25.8d",  0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // == sf2-13m.4d
	ROM_LOAD64_WORD( "sf2_27.10d", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // == sf2-15m.6d
	ROM_LOAD64_WORD( "sf2_24.7d",  0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // == sf2-9m.3d
	ROM_LOAD64_WORD( "sf2_26.9d",  0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // == sf2-11m.5d

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2j_09.12a", 0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )   // == sf2_9.12a
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2j_18.11c", 0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )   // == sf2_18.11c
	ROM_LOAD( "sf2j_19.12c", 0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )   // == sf2_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "stf29.1a",     0x0000, 0x0117, CRC(043309c5) SHA1(f6b9610c9f896c495041ba56a654776a521c356b) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/*  Bootleg manufactured by TAB Austria

 -------------------------------------------------------
 |    ROM1    *2  *3                         ROM5 ROM9 |
 |    ROM2                                   ROM6 ROM8 |
 |            *1                              1B   1D  |
 |      6295                                  1A   1C  |
 |  YM                               ROM4     1F   1H  |
 |    ROM3                                    1E   1G  |
--     Z80                                    1J   1L  |
--                                            1I   1K  |
--                                                     |
--                                                     |
--                     TAB                             |
--                    AUSTRIA                          |
 | DIP8 6                                              |
 |      8  ROM10  ROM9                                 |
 | DIP8 0  ROM12  ROM11                                |
 |      0                                              |
 | DIP8 0                                              |
 |------------------------------------------------------

   *1 = 12.000 Mhz
   *2 = 3.579545 Mhz
   *3 = 16.000 Mhz

*/
ROM_START( sf2ebbl )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "12.bin",   0x00000, 0x40000, CRC(a258b4d5) SHA1(3433b6493794c98bb35c1b27cc65bb5f13d52e9b) )
	ROM_LOAD16_BYTE( "09.bin",   0x00001, 0x40000, CRC(59ccd474) SHA1(7bb28c28ee722435fdbb18eb73e52bd65b419103) )
	ROM_LOAD16_BYTE( "11.bin",   0x80000, 0x40000, CRC(82097d63) SHA1(881e7ffb78197f6794b5d41f5c2c87da35e8cb15) )
	ROM_LOAD16_BYTE( "10.bin",   0x80001, 0x40000, CRC(0c83844d) SHA1(4c25ba4a50d62c62789d026e3d304ed1dfb3c248) )

	ROM_REGION( 0x600000, "gfx", 0 )
	/* The 12 mask ROMs on this PCB match the original roms exactly */
	ROM_LOAD64_WORD( "1b_yf082.bin", 0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )
	ROM_LOAD64_WORD( "1d_yf028.bin", 0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )
	ROM_LOAD64_WORD( "1a_yf087.bin", 0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )
	ROM_LOAD64_WORD( "1c_yf088.bin", 0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )
	ROM_LOAD64_WORD( "1f_yf085.bin", 0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )
	ROM_LOAD64_WORD( "1h_yf115.bin", 0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )
	ROM_LOAD64_WORD( "1e_yf111.bin", 0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )
	ROM_LOAD64_WORD( "1g_yf002.bin", 0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )
	ROM_LOAD64_WORD( "1j_yf117.bin", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )
	ROM_LOAD64_WORD( "1l_ye040.bin", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )
	ROM_LOAD64_WORD( "1i_yf038.bin", 0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )
	ROM_LOAD64_WORD( "1k_ye039.bin", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )
	/* These map over the mask ROMs on this bootleg to get rid of the CAPCOM logo (wasteful, but correct) */
	ROM_LOAD64_BYTE( "05.bin", 0x400000, 0x10000, CRC(a505621e) SHA1(8ffa8cedad54948870bbd8f629d927332dc9fcf6) )
	ROM_CONTINUE(              0x400004, 0x10000 )
	ROM_LOAD64_BYTE( "07.bin", 0x400002, 0x10000, CRC(de6271fb) SHA1(574ec5d9992941a405fd00abe52da41aba4b29a7) )
	ROM_CONTINUE(              0x400006, 0x10000 )
	ROM_LOAD64_BYTE( "06.bin", 0x400001, 0x10000, CRC(23775344) SHA1(0d6b54f66cd64c3f48c88c8e17a19fdb2002afb0) )
	ROM_CONTINUE(              0x400005, 0x10000 )
	ROM_LOAD64_BYTE( "08.bin", 0x400003, 0x10000, CRC(81c9550f) SHA1(2d75e329148caadfff35c8f2f91b352f14dbe08a) )
	ROM_CONTINUE(              0x400007, 0x10000 )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "03.bin",    0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x20000, "user1", 0 ) /* unknown (bootleg priority?) */
	ROM_LOAD( "04.bin",    0x00000, 0x10000, CRC(13ea1c44) SHA1(5b05fe4c3920e33d94fac5f59e09ff14b3e427fe) )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "02.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "01.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

ROM_START( sf2ebbl2 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "27c020.7",   0x00000, 0x40000, CRC(a258b4d5) SHA1(3433b6493794c98bb35c1b27cc65bb5f13d52e9b) )
	ROM_LOAD16_BYTE( "27c020.5",   0x00001, 0x40000, CRC(59ccd474) SHA1(7bb28c28ee722435fdbb18eb73e52bd65b419103) )
	ROM_LOAD16_BYTE( "27c020.6",   0x80000, 0x40000, CRC(82097d63) SHA1(881e7ffb78197f6794b5d41f5c2c87da35e8cb15) )
	ROM_LOAD16_BYTE( "27c020.4",   0x80001, 0x40000, CRC(0c83844d) SHA1(4c25ba4a50d62c62789d026e3d304ed1dfb3c248) )

	ROM_REGION( 0x600000, "gfx", 0 )
	/* 5 mask ROMs on this PCB match the original roms exactly, */
	ROM_LOAD64_WORD( "a-se235.bin", 0x000000, 0x80000, CRC(a258de13) SHA1(2e477948c4c8a2fb7cfdc4a739766bc4a4e01c49) )
	ROM_CONTINUE(                   0x000004, 0x80000)
	ROM_LOAD64_WORD( "c-se005.bin", 0x000002, 0x80000, CRC(c781bf87) SHA1(034baa9807c2ce8dc800200963a38cd9262b21fb) )
	ROM_CONTINUE(                   0x000006, 0x80000)
	ROM_LOAD64_WORD( "b-se194.bin", 0x200000, 0x80000, CRC(5726cab8) SHA1(0b2243a9a7184d53d42ddab7a8c51b63001c2f56) )
	ROM_CONTINUE(                   0x200004, 0x80000)
	ROM_LOAD64_WORD( "d-se064.bin", 0x200002, 0x80000, CRC(4dd24197) SHA1(548beaa0a6f1c3c88f4fc83169d1a3c86e0755d4) )   //sf2-8m.6c 99.999809%
	ROM_CONTINUE(                   0x200006, 0x80000)
	ROM_LOAD64_WORD( "e-sf004.bin", 0x400000, 0x80000, CRC(187667cc) SHA1(fae65bf23f49a32903fda8080659ccf8d42b911f) )
	ROM_CONTINUE(                   0x400004, 0x80000)
	ROM_LOAD64_WORD( "f-sf001.bin", 0x400002, 0x80000, CRC(5b585071) SHA1(ad3371b1ba0441c67d9fcbb23b09464710e4e28a) )
	ROM_CONTINUE(                   0x400006, 0x80000)

	// These map over the mask ROMs on this bootleg why? isn't it a waste of eprom?
	ROM_LOAD64_WORD( "27c1024.10", 0x400000, 0x20000, CRC(84427d1b) SHA1(f988a2b53c8cc46eeb8032084f24966a539b3734) )//e-sf004.bin [1/8]      IDENTICAL
	ROM_LOAD64_WORD( "27c1024.12", 0x400002, 0x20000, CRC(55bc790c) SHA1(a1114b89f6fa4487210477676984c77ad94b5ef8) )//f-sf001.bin [1/8]      IDENTICAL
	ROM_LOAD64_WORD( "27c1024.9",  0x400004, 0x20000, CRC(f8725add) SHA1(fa3fcf6637ee4dd7667bd89766074b3c6ba4f166) )//e-sf004.bin [5/8]      IDENTICAL
	ROM_LOAD64_WORD( "27c1024.11", 0x400006, 0x20000, CRC(c2a5373e) SHA1(602b32e5ecc7007efe9ad30751040ee52b81f59a) )//f-sf001.bin [5/8]      IDENTICAL


	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "27c512.3",    0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x20000, "user1", 0 ) /* unknown (bootleg priority?) */
	ROM_LOAD( "27c512.8",    0x00000, 0x10000, CRC(13ea1c44) SHA1(5b05fe4c3920e33d94fac5f59e09ff14b3e427fe) )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "27c010.2",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "27c010.1",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

ROM_START( sf2ebbl3 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "ce91e-b.bin",   0x00000, 0x80000, CRC(963200d2) SHA1(3fa23fdee6d43f01066a94b4b01b1811168637a2) )
	ROM_LOAD16_BYTE( "ce91e-a.bin",   0x00001, 0x80000, CRC(02e88ec7) SHA1(1898e110108301a08686945f449fd084e394fc17) )

	ROM_REGION( 0x600000, "gfx", 0 )
	/* There are 8 mask  and 8 otp eprom on this board, data match sf2 set in the end */
	ROM_LOAD64_WORD( "1-b-yf197.07",  0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )
	ROM_LOAD64_WORD( "1-d-yf207.12",  0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )
	ROM_LOAD64_BYTE( "27020.1",       0x000004, 0x40000, CRC(84afb959) SHA1(ff8f3fb39203d229552649b09046a509aa399b7d) )
	ROM_LOAD64_BYTE( "27020.3",       0x000005, 0x40000, CRC(ab21635d) SHA1(c6294b06c482a43cc1736674b0c1bc790da8cc8c) )
	ROM_LOAD64_BYTE( "27020.5",       0x000006, 0x40000, CRC(2ce56f9f) SHA1(5bd23823df52aeff8e84a6eb3199a5e75f38cd42) )
	ROM_LOAD64_BYTE( "27020.7",       0x000007, 0x40000, CRC(0ad7fb2b) SHA1(51265b331cbbe9d133274bafdbf043151240a20d) )

	ROM_LOAD64_BYTE( "27020.2",       0x200000, 0x40000, CRC(031525cc) SHA1(65e280607c058cf9c005a6fc809ed375d379c0bf) )
	ROM_LOAD64_BYTE( "27020.4",       0x200001, 0x40000, CRC(c97046a5) SHA1(88c7becaa2713c85e22ddd3e2305770525a9132b) )
	ROM_LOAD64_WORD( "1-h-yg010.10",  0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )
	ROM_LOAD64_WORD( "1-e-yg003.02",  0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )
	ROM_LOAD64_BYTE( "27020.6",       0x200006, 0x40000, CRC(dbbfd400) SHA1(fc25928b6ed025fa2397d924bae93fd28a4e728f) )
	ROM_LOAD64_BYTE( "27020.8",       0x200007, 0x40000, CRC(37635e97) SHA1(b4e5dd6e93174af76aaeeb47471ded0d0bc253e2) )

	ROM_LOAD64_WORD( "1-j-yf213.09",      0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )
	ROM_LOAD64_WORD( "wm91m-11-yd025.11", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )
	ROM_LOAD64_WORD( "1-i-yf224.03",      0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )
	ROM_LOAD64_WORD( "1-k-yf036.06",      0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )

	/* These map over the mask ROMs on this bootleg to get rid of the CAPCOM logo (wasteful, but correct) */
	ROM_LOAD64_BYTE( "27010.09hi", 0x400000, 0x10000, CRC(a505621e) SHA1(8ffa8cedad54948870bbd8f629d927332dc9fcf6) )
	ROM_CONTINUE(                  0x400004, 0x10000 )
	ROM_LOAD64_BYTE( "27010.11",   0x400002, 0x10000, CRC(de6271fb) SHA1(574ec5d9992941a405fd00abe52da41aba4b29a7) )
	ROM_CONTINUE(                  0x400006, 0x10000 )
	ROM_LOAD64_BYTE( "27010.09lo", 0x400001, 0x10000, CRC(23775344) SHA1(0d6b54f66cd64c3f48c88c8e17a19fdb2002afb0) )
	ROM_CONTINUE(                  0x400005, 0x10000 )
	ROM_LOAD64_BYTE( "27010.06",   0x400003, 0x10000, CRC(81c9550f) SHA1(2d75e329148caadfff35c8f2f91b352f14dbe08a) )
	ROM_CONTINUE(                  0x400007, 0x10000 )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "27512.3",    0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x20000, "user1", 0 ) /* unknown (bootleg priority?) */
	ROM_LOAD( "27512.8",    0x00000, 0x10000, CRC(13ea1c44) SHA1(5b05fe4c3920e33d94fac5f59e09ff14b3e427fe) )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "27010.2",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "27010.1",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

ROM_START( sf2stt )
	/* the PCB is not working on real hardware */
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	/* do not move this outside comments, this is only for testing purpose
	ROM_LOAD16_BYTE( "12.bin",   0x00000, 0x40000, CRC(a258b4d5) SHA1(3433b6493794c98bb35c1b27cc65bb5f13d52e9b) )
	ROM_LOAD16_BYTE( "09.bin",   0x00001, 0x40000, CRC(59ccd474) SHA1(7bb28c28ee722435fdbb18eb73e52bd65b419103) )
	*/
	ROM_LOAD16_BYTE( "prg part 1.stt", 0x00000, 0x40000, NO_DUMP )
	ROM_LOAD16_BYTE( "prg part 2.stt", 0x00001, 0x40000, NO_DUMP )
	/* missing first part of program roms, so it can not boot */
	ROM_LOAD16_BYTE( "ce91e-b",        0x80000, 0x40000, CRC(0862386e) SHA1(9fcfbcbbc17529de75d5419018e7b1dd90b397c0) )
	ROM_LOAD16_BYTE( "ce91e-a",        0x80001, 0x40000, CRC(0c83844d) SHA1(4c25ba4a50d62c62789d026e3d304ed1dfb3c248) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "stt-a", 0x000000, 0x80000, CRC(10a7036d) SHA1(5f7780154d12dc202ec4bf2f3666727820745ab0) )
	ROM_CONTINUE(             0x000002, 0x80000)
	ROM_LOAD64_WORD( "stt-d", 0x000004, 0x80000, CRC(3580b124) SHA1(55b101e7be3d1244d0b9961ad3c6ff5d682214ce) )
	ROM_CONTINUE(             0x000006, 0x80000)
	ROM_LOAD64_WORD( "stt-b", 0x200000, 0x80000, CRC(7a09224e) SHA1(77a95b931f0a1ebd38c9aaf51a3b59139e0aedeb) )
	ROM_CONTINUE(             0x200002, 0x80000)
	ROM_LOAD64_WORD( "stt-e", 0x200004, 0x80000, CRC(382a612c) SHA1(ecdcb66ed9d47d850501abc5f77eb5ad87b868d3) )
	ROM_CONTINUE(             0x200006, 0x80000)
	ROM_LOAD64_WORD( "stt-c", 0x400000, 0x80000, CRC(11701b8f) SHA1(4f0c897349a0f1701a144ec64a75fa23fec32ae5) )
	ROM_CONTINUE(             0x400002, 0x80000)
	ROM_LOAD64_WORD( "stt-f", 0x400004, 0x80000, CRC(101a0b72) SHA1(9e166a4110a583e722566852e27cb0b074ec97c6) )
	ROM_CONTINUE(             0x400006, 0x80000)
	/* extra gfx layer roms loaded over the former ones to remove the capcom copyright logo */
	ROM_LOAD64_BYTE( "5.stt", 0x400000, 0x10000, CRC(a505621e) SHA1(8ffa8cedad54948870bbd8f629d927332dc9fcf6) )
	ROM_CONTINUE(             0x400004, 0x10000 )
	ROM_LOAD64_BYTE( "7.stt", 0x400002, 0x10000, CRC(de6271fb) SHA1(574ec5d9992941a405fd00abe52da41aba4b29a7) )
	ROM_CONTINUE(             0x400006, 0x10000 )
	ROM_LOAD64_BYTE( "6.stt", 0x400001, 0x10000, CRC(23775344) SHA1(0d6b54f66cd64c3f48c88c8e17a19fdb2002afb0) )
	ROM_CONTINUE(             0x400005, 0x10000 )
	ROM_LOAD64_BYTE( "8.stt", 0x400003, 0x10000, CRC(81c9550f) SHA1(2d75e329148caadfff35c8f2f91b352f14dbe08a) )
	ROM_CONTINUE(             0x400007, 0x10000 )
	/* end of extra gfx layer roms */

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "3.stt", 0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(      0x10000, 0x08000 )

	ROM_REGION( 0x20000, "user1", 0 ) /* unknown (bootleg priority?) */
	ROM_LOAD( "4.stt", 0x00000, 0x10000, CRC(13ea1c44) SHA1(5b05fe4c3920e33d94fac5f59e09ff14b3e427fe) )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "2.stt", 0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "1.stt", 0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

ROM_START( sf2re ) // combines sf2m8 program ROMs with sf2cems6a ROMs for most GFX and common ROMs for sound
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "4.u222", 0x000000, 0x80000, CRC(db567b66) SHA1(315bfbf2786ef67a95afb87de836ab348523dbbe) )
	ROM_LOAD16_BYTE( "3.u196", 0x000001, 0x80000, CRC(95ea597e) SHA1(5eb82feaa1de5611a96888e4670744bbb7d90393) )
	ROM_LOAD16_BYTE( "2.u221", 0x100000, 0x20000, CRC(1073b7b6) SHA1(81ca1eab65ceac69520584bb23a684ccb9d92f89) )
	ROM_LOAD16_BYTE( "1.u195", 0x100001, 0x20000, CRC(924c6ce2) SHA1(676a912652bd75da5087f0c7eae047b7681a993c) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "1mre125.u70",   0x000000, 0x80000, CRC(baa0f81f) SHA1(5e55a5c4ad64be17089670a3d73c1c0d9082351b) )
	ROM_CONTINUE(                     0x000004, 0x80000)
	ROM_LOAD64_WORD( "3mre121.u68",   0x000002, 0x80000, CRC(8edff95a) SHA1(8db35c5940dcc1f09f11be26051b2f98445d10e7) )
	ROM_CONTINUE(                     0x000006, 0x80000)
	ROM_LOAD64_WORD( "5mre148.u69",   0x200000, 0x80000, CRC(468962b1) SHA1(fdfd2a7cbbcafaa37e972da425446d471e1e1dae) )
	ROM_CONTINUE(                     0x200004, 0x80000)
	ROM_LOAD64_WORD( "7mrd413.u64",   0x200002, 0x80000, CRC(8165f536) SHA1(8178fe2240c73c7283592aa31dd24aec5bf9429b) )
	ROM_CONTINUE(                     0x200006, 0x80000)
	ROM_LOAD64_WORD( "sfiire143.u19", 0x400000, 0x80000, CRC(39d763d3) SHA1(a2a0bddecaca6046785ccddfd20b8356a6ec36f0) )
	ROM_CONTINUE(                     0x400004, 0x80000)
	ROM_LOAD64_WORD( "sfiire073.u18", 0x400002, 0x80000, CRC(93ec42ae) SHA1(7c8b481d61a4e9977cac35236835f4aa5badf992) )
	ROM_CONTINUE(                     0x400006, 0x80000)
	/* extra gfx layer roms loaded over the former ones to remove the capcom copyright logo */
	ROM_LOAD64_BYTE( "27c010.u31",    0x400004, 0x10000, CRC(6de44671) SHA1(dc6abba639e0c27033e391c7438d88dc89a93351) )
	ROM_CONTINUE(                     0x400000, 0x10000 )
	ROM_LOAD64_BYTE( "27c010.u30",    0x400005, 0x10000, CRC(bf0cd819) SHA1(f04a098fce07949277268327871c5e5520e3bb3c) )
	ROM_CONTINUE(                     0x400001, 0x10000 )
	ROM_LOAD64_BYTE( "27c010.u29",    0x400006, 0x10000, CRC(e8f14362) SHA1(a20eb75e322011e2a8d8bf2acebe713bef3d3941) )
	ROM_CONTINUE(                     0x400002, 0x10000 )
	ROM_LOAD64_BYTE( "27c010.u28",    0x400007, 0x10000, CRC(76f9f91f) SHA1(58a34062d2c8378558a7f1629140330279af9a43) )
	ROM_CONTINUE(                     0x400003, 0x10000 )
	/* end of extra gfx layer roms */

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "csicat27c512.u191", 0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(                  0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "27c020.u210", 0x00000, 0x40000, CRC(6cfffb11) SHA1(995526183ffd35f92e9096500a3fe6237faaa2dd) )
ROM_END

// PCB is marked: "100P003" and "054-034" on solder side
// PCB is labeled: "10037SI 7", "STREET FIGHTER 2" and "STREET FIGHTER II MAGIC TURBO" on component side
ROM_START( sf2mkot )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )
	ROM_LOAD16_BYTE( "u222.bin", 0x000000, 0x80000, CRC(b01a94b6) SHA1(a23682ccb463d766fd81a53cbc29b31f3fd4e5e0) )
	ROM_LOAD16_BYTE( "u196.bin", 0x000001, 0x80000, CRC(20461c47) SHA1(485aa19d4d0a4a849f81b0d3e29137e461129fd6) )
	ROM_LOAD16_BYTE( "u221.bin", 0x100000, 0x20000, CRC(64e6e091) SHA1(32ec05db955e538d4ada26d19ee50926f74b684f) )
	ROM_LOAD16_BYTE( "u195.bin", 0x100001, 0x20000, CRC(c95e4443) SHA1(28417dee9ccdfa65b0f4a92aa29b90279fe8cd85) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "d21.u70", 0x000000, 0x80000, CRC(baa0f81f) SHA1(5e55a5c4ad64be17089670a3d73c1c0d9082351b) )
	ROM_CONTINUE(               0x000004, 0x80000)
	ROM_LOAD64_WORD( "d24.u68", 0x000002, 0x80000, CRC(8edff95a) SHA1(8db35c5940dcc1f09f11be26051b2f98445d10e7) )
	ROM_CONTINUE(               0x000006, 0x80000)
	ROM_LOAD64_WORD( "d22.u69", 0x200000, 0x80000, CRC(468962b1) SHA1(fdfd2a7cbbcafaa37e972da425446d471e1e1dae) )
	ROM_CONTINUE(               0x200004, 0x80000)
	ROM_LOAD64_WORD( "d25.u64", 0x200002, 0x80000, CRC(8165f536) SHA1(8178fe2240c73c7283592aa31dd24aec5bf9429b) )
	ROM_CONTINUE(               0x200006, 0x80000)
	ROM_LOAD64_WORD( "d23.u19", 0x400000, 0x80000, CRC(39d763d3) SHA1(a2a0bddecaca6046785ccddfd20b8356a6ec36f0) )
	ROM_CONTINUE(               0x400004, 0x80000)
	ROM_LOAD64_WORD( "d26.u18", 0x400002, 0x80000, CRC(93ec42ae) SHA1(7c8b481d61a4e9977cac35236835f4aa5badf992) )
	ROM_CONTINUE(               0x400006, 0x80000)
	// extra gfx layer roms loaded over the former ones
	ROM_LOAD64_WORD( "moon-1.c173.u30", 0x400004, 0x20000, CRC(7e36ec84) SHA1(ab6ad48726ca3649db77b9971105374a10e0aa22) )
	ROM_CONTINUE(                       0x400000, 0x20000)
	ROM_LOAD64_WORD( "moon-2.c132.u29", 0x400006, 0x20000, CRC(66403570) SHA1(fbd276784df8754bf4f2c6a72060e14af4cc5729) )
	ROM_CONTINUE(                       0x400002, 0x20000)
	// end of extra gfx layer roms

	ROM_REGION( 0x18000, "audiocpu", 0 )
	ROM_LOAD( "conv2.u191", 0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(                  0x10000, 0x08000 )

	ROM_REGION( 0x20000, "user1", 0 ) // unknown (bootleg priority?)
	ROM_LOAD( "u133.bin", 0x00000, 0x10000, CRC(13ea1c44) SHA1(5b05fe4c3920e33d94fac5f59e09ff14b3e427fe) )

	ROM_REGION( 0x40000, "oki", 0 )
	ROM_LOAD( "voice.u210", 0x00000, 0x40000, CRC(6cfffb11) SHA1(995526183ffd35f92e9096500a3fe6237faaa2dd) )

	ROM_REGION( 0x4000, "plds", ROMREGION_ERASE00 ) // all read-protected
	ROM_LOAD( "gal16v8a-25lp.u6",   0x0000, 0x0117, NO_DUMP )
	ROM_LOAD( "gal16v8a-25lp.u15",  0x0200, 0x0117, NO_DUMP )
	ROM_LOAD( "gal16v8a-25lp.n03",  0x0400, 0x0117, NO_DUMP )
	ROM_LOAD( "gal16v8a-25lp.n04",  0x0600, 0x0117, NO_DUMP )
	ROM_LOAD( "gal16v8a-25lp.n05",  0x0800, 0x0117, NO_DUMP )
	ROM_LOAD( "gal16v8a-25lp.n06",  0x0a00, 0x0117, NO_DUMP )
	ROM_LOAD( "gal16v8a-25lp.u95",  0x0c00, 0x0117, NO_DUMP )
	ROM_LOAD( "gal16v8a-25lp.u96",  0x0e00, 0x0117, NO_DUMP )
	ROM_LOAD( "gal16v8a-25lp.u107", 0x1000, 0x0117, NO_DUMP )
	ROM_LOAD( "gal16v8a-25lp.u125", 0x1200, 0x0117, NO_DUMP )
	ROM_LOAD( "gal16v8a-25lp.u139", 0x1400, 0x0117, NO_DUMP )
	ROM_LOAD( "gal16v8a-25lp.u151", 0x1600, 0x0117, NO_DUMP )
	ROM_LOAD( "gal16v8a-25lp.u173", 0x1800, 0x0117, NO_DUMP )
	ROM_LOAD( "gal16v8a-25lp.u176", 0x1a00, 0x0117, NO_DUMP )
	ROM_LOAD( "gal16v8a-25lp.u177", 0x1c00, 0x0117, NO_DUMP )
	ROM_LOAD( "gal16v8a-25lp.u178", 0x1e00, 0x0117, NO_DUMP )
	ROM_LOAD( "gal16v8a-25lp.u183", 0x2000, 0x0117, NO_DUMP )
	ROM_LOAD( "gal16v8a-25lp.u198", 0x2200, 0x0117, NO_DUMP )
	ROM_LOAD( "gal16v8a-25lp.u218", 0x2400, 0x0117, NO_DUMP )
	ROM_LOAD( "gal20v8a-25lp.u20",  0x2600, 0x0157, NO_DUMP )
	ROM_LOAD( "gal20v8a-25lp.u21",  0x2800, 0x0157, NO_DUMP )
	ROM_LOAD( "gal20v8a-25lp.u34",  0x2a00, 0x0157, NO_DUMP )
	ROM_LOAD( "gal20v8a-25lp.u35",  0x2c00, 0x0157, NO_DUMP )
	ROM_LOAD( "gal20v8a-25lp.u39",  0x2e00, 0x0157, NO_DUMP )
	ROM_LOAD( "gal20v8a-25lp.n07",  0x3000, 0x0157, NO_DUMP )
	ROM_LOAD( "gal20v8a-25lp.n08",  0x3200, 0x0157, NO_DUMP )
	ROM_LOAD( "gal20v8a-25lp.u104", 0x3400, 0x0157, NO_DUMP )
	ROM_LOAD( "gal20v8a-25lp.u131", 0x3600, 0x0157, NO_DUMP )
	ROM_LOAD( "gal20v8a-25lp.u135", 0x3800, 0x0157, NO_DUMP )
	ROM_LOAD( "gal20v8a-25lp.u140", 0x3a00, 0x0157, NO_DUMP )
	ROM_LOAD( "gal22v10-25lp.u134", 0x3c00, 0x02e5, NO_DUMP )
ROM_END

ROM_START( sf2rk )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "w6.u222", 0x000000, 0x80000, CRC(49422b6f) SHA1(69fe9147c7ee3f6fa29077df16f4ef1224495be3) )
	ROM_LOAD16_BYTE( "w5.u196", 0x000001, 0x80000, CRC(7e9c8c2f) SHA1(3d34a3920a771e1d62a41c104c8b16e3c6ac9405) )

	/* The dark screen issue is present on the real pcb, although much less noticeable than in mame.
	   The bootleggers have patched out the code which modifies the brightness/fade component of the palette word.
	   This is somewhat strange as unlike some bootlegs, this board DOES have the brightness circuitry (2x 7407's, 12x resistor dac) populated!
	   The below patch fixes the issue:   (for pcb fix burn a 27c040 of w6.u222 with 0x8f7=0x6b)
	*/
	//ROM_FILL( 0x11ee, 1, 0x6b)

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "01 rk098", 0x000000, 0x80000, CRC(4296de4d) SHA1(2bd5a0ebe2a20c745b11da9c7dc4f13f20efdda7) )
	ROM_CONTINUE(                0x000004, 0x80000)
	ROM_LOAD64_WORD( "03 rk097", 0x000002, 0x80000, CRC(16cf11d0) SHA1(e1adf34467e0a6902bcda63718885fe0bea831b1) )
	ROM_CONTINUE(                0x000006, 0x80000)
	ROM_LOAD64_WORD( "02 rk037", 0x200000, 0x80000, CRC(68ca7fce) SHA1(f6a37e0ca4d9490f66c3d29308c531f2134fd7aa) )
	ROM_CONTINUE(                0x200004, 0x80000)
	ROM_LOAD64_WORD( "04 rk033", 0x200002, 0x80000, CRC(9f46f926) SHA1(f1ddf8ce6d895358979631fbdecbeff51376b604) )
	ROM_CONTINUE(                0x200006, 0x80000)
	ROM_LOAD64_WORD( "05 rk116", 0x400000, 0x80000, CRC(4c161fa9) SHA1(c3b2f642d3d3be5aab6ff1482d82c2858944d07a) )
	ROM_CONTINUE(                0x400004, 0x80000)
	ROM_LOAD64_WORD( "06 rk077", 0x400002, 0x80000, CRC(ec949f8c) SHA1(34ea3d6d85486a5ff25c774dbc6a4b16037a7347) )
	ROM_CONTINUE(                0x400006, 0x80000)
	/* extra sprite roms loaded over the former ones (identical data) */
	ROM_LOAD64_BYTE( "w-1",      0x400004, 0x10000, CRC(6de44671) SHA1(dc6abba639e0c27033e391c7438d88dc89a93351) )
	ROM_CONTINUE(                0x400000, 0x10000 )
	ROM_LOAD64_BYTE( "w-3",      0x400005, 0x10000, CRC(bf0cd819) SHA1(f04a098fce07949277268327871c5e5520e3bb3c) )
	ROM_CONTINUE(                0x400001, 0x10000 )
	ROM_LOAD64_BYTE( "w-2",      0x400006, 0x10000, CRC(e8f14362) SHA1(a20eb75e322011e2a8d8bf2acebe713bef3d3941) )
	ROM_CONTINUE(                0x400002, 0x10000 )
	ROM_LOAD64_BYTE( "w-4",      0x400007, 0x10000, CRC(76f9f91f) SHA1(58a34062d2c8378558a7f1629140330279af9a43) )
	ROM_CONTINUE(                0x400003, 0x10000 )
	/* end of extra sprite roms */

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "1.rk", 0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(     0x10000, 0x08000 )

	ROM_REGION( 0x20000, "user1", 0 ) /* unknown (bootleg priority?) */
	ROM_LOAD( "2.rk", 0x00000, 0x10000, CRC(13ea1c44) SHA1(5b05fe4c3920e33d94fac5f59e09ff14b3e427fe) )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "w7.u210", 0x00000, 0x40000, CRC(6cfffb11) SHA1(995526183ffd35f92e9096500a3fe6237faaa2dd) )
ROM_END

ROM_START( sf2qp1 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "stfii-qkn-cps-17.33", 0x000000, 0x40000, CRC(3a9458ee) SHA1(cbf56115c22a45e5ce1cde313fa5a5ba496316eb) )
	ROM_CONTINUE(                                0x0c0000, 0x40000 )
	ROM_LOAD16_WORD_SWAP( "stfii-qkn-cps-17.34", 0x080000, 0x40000, CRC(4ed215d8) SHA1(b09075f883c3a4976ab47203acc04f8f993969a8) )
	ROM_CONTINUE(                                0x040000, 0x40000 )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2_06.bin", 0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // == sf2-5m.4a
	ROM_LOAD64_WORD( "sf2_08.bin", 0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // == sf2-7m.6a
	ROM_LOAD64_WORD( "sf2_05.bin", 0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // == sf2-1m.3a
	ROM_LOAD64_WORD( "sf2_07.bin", 0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // == sf2-3m.5a
	ROM_LOAD64_WORD( "sf2_15.bin", 0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // == sf2-6m.4c
	ROM_LOAD64_WORD( "sf2_17.bin", 0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // == sf2-8m.6c
	ROM_LOAD64_WORD( "sf2_14.bin", 0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // == sf2-2m.3c
	ROM_LOAD64_WORD( "sf2_16.bin", 0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // == sf2-4m.5c
	ROM_LOAD64_WORD( "sf2_25.bin", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // == sf2-13m.4d
	ROM_LOAD64_WORD( "sf2_27.bin", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // == sf2-15m.6d
	ROM_LOAD64_WORD( "sf2_24.bin", 0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // == sf2-9m.3d
	ROM_LOAD64_WORD( "sf2_26.bin", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // == sf2-11m.5d

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_09.bin",    0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )
ROM_END

ROM_START( sf2qp2 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "qkn.33", 0x00000, 0x40000, CRC(43aa343d) SHA1(501f14a68143344f8ff7f9091d7233ab53e0d531) )
	ROM_CONTINUE(                   0xc0000, 0x40000 )
	ROM_LOAD16_WORD_SWAP( "qkn.34", 0x80000, 0x40000, CRC(d03b553f) SHA1(edce2f9792ec2eb7da8c755d01616b874fbdef8e) )
	ROM_CONTINUE(                   0x40000, 0x40000 )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2_06.bin", 0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // == sf2-5m.4a
	ROM_LOAD64_WORD( "sf2_08.bin", 0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // == sf2-7m.6a
	ROM_LOAD64_WORD( "sf2_05.bin", 0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // == sf2-1m.3a
	ROM_LOAD64_WORD( "sf2_07.bin", 0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // == sf2-3m.5a
	ROM_LOAD64_WORD( "sf2_15.bin", 0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // == sf2-6m.4c
	ROM_LOAD64_WORD( "sf2_17.bin", 0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // == sf2-8m.6c
	ROM_LOAD64_WORD( "sf2_14.bin", 0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // == sf2-2m.3c
	ROM_LOAD64_WORD( "sf2_16.bin", 0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // == sf2-4m.5c
	ROM_LOAD64_WORD( "sf2_25.bin", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // == sf2-13m.4d
	ROM_LOAD64_WORD( "sf2_27.bin", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // == sf2-15m.6d
	ROM_LOAD64_WORD( "sf2_24.bin", 0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // == sf2-9m.3d
	ROM_LOAD64_WORD( "sf2_26.bin", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // == sf2-11m.5d

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_09.bin",    0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )
ROM_END

ROM_START( sf2thndr ) // speed modifiable via dsw
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "17_30.11e",     0x00000, 0x20000, CRC(d3cd6d18) SHA1(c7efe9e7a7dee29bfe40a571960145efa26ac22e) )
	ROM_LOAD16_BYTE( "17_37.11f",     0x00001, 0x20000, CRC(e892716e) SHA1(b3836b33bc026a0bccc5ed7cdecf8d2ba2b5607a) )
	ROM_LOAD16_BYTE( "sf2u_31b.12e",  0x40000, 0x20000, CRC(a673143d) SHA1(e565f0ec23d6deb543c72af5a83f070c07319477) )
	ROM_LOAD16_BYTE( "sf2u_38b.12f",  0x40001, 0x20000, CRC(4c2ccef7) SHA1(77b119c70c255622b023de25d9af3b3aac52ea47) )
	ROM_LOAD16_BYTE( "sf2u_28b.9e",   0x80000, 0x20000, CRC(4009955e) SHA1(7842dbef7650485639fbae49b9f4db7494d4f73d) )
	ROM_LOAD16_BYTE( "sf2u_35b.9f",   0x80001, 0x20000, CRC(8c1f3994) SHA1(5e1d334399d05a837c2d80f79eada543e83afaf7) )
	ROM_LOAD16_BYTE( "17_29.10e",     0xc0000, 0x20000, CRC(8830b54d) SHA1(fe5e958f69d52fa12c31c1a7ccf0dcfd340045cb) )
	ROM_LOAD16_BYTE( "17_36.10f",     0xc0001, 0x20000, CRC(3f13ada3) SHA1(412572f220f64e5b0ee2cad0a9204142ae2f7c90) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2_06.bin", 0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // == sf2-5m.4a
	ROM_LOAD64_WORD( "sf2_08.bin", 0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // == sf2-7m.6a
	ROM_LOAD64_WORD( "sf2_05.bin", 0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // == sf2-1m.3a
	ROM_LOAD64_WORD( "sf2_07.bin", 0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // == sf2-3m.5a
	ROM_LOAD64_WORD( "sf2_15.bin", 0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // == sf2-6m.4c
	ROM_LOAD64_WORD( "sf2_17.bin", 0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // == sf2-8m.6c
	ROM_LOAD64_WORD( "sf2_14.bin", 0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // == sf2-2m.3c
	ROM_LOAD64_WORD( "sf2_16.bin", 0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // == sf2-4m.5c
	ROM_LOAD64_WORD( "sf2_25.bin", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // == sf2-13m.4d
	ROM_LOAD64_WORD( "sf2_27.bin", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // == sf2-15m.6d
	ROM_LOAD64_WORD( "sf2_24.bin", 0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // == sf2-9m.3d
	ROM_LOAD64_WORD( "sf2_26.bin", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // == sf2-11m.5d

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_09.bin",    0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )
ROM_END

ROM_START( sf2thndr2 ) // has interruptible and aerial special moves, speed modifiable via dsw, only the program ROMs were provided, rest taken from sf2thundr
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "31.bin",  0x00000, 0x20000, CRC(eec8cbe3) SHA1(cabbb03b16978f3f036ccba871d8d5ac97238641) )
	ROM_LOAD16_BYTE( "38.bin",  0x00001, 0x20000, CRC(1c0fc4e1) SHA1(e347eb40a7e8ca98cc79672fb9e2a4d19c920ac0) )
	ROM_LOAD16_BYTE( "30.bin",  0x40000, 0x20000, CRC(a673143d) SHA1(e565f0ec23d6deb543c72af5a83f070c07319477) )
	ROM_LOAD16_BYTE( "37.bin",  0x40001, 0x20000, CRC(4c2ccef7) SHA1(77b119c70c255622b023de25d9af3b3aac52ea47) )
	ROM_LOAD16_BYTE( "28.bin",  0x80000, 0x20000, CRC(4009955e) SHA1(7842dbef7650485639fbae49b9f4db7494d4f73d) )
	ROM_LOAD16_BYTE( "35.bin",  0x80001, 0x20000, CRC(8c1f3994) SHA1(5e1d334399d05a837c2d80f79eada543e83afaf7) )
	ROM_LOAD16_BYTE( "29.bin",  0xc0000, 0x20000, CRC(bb4af315) SHA1(75f0827f4f7e9f292add46467f8d4fe19b2514c9) )
	ROM_LOAD16_BYTE( "36.bin",  0xc0001, 0x20000, CRC(c02a13eb) SHA1(b807cc495bff3f95d03b061fc629c95f965cb6d8) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "sf2_06.bin", 0x000000, 0x80000, CRC(22c9cc8e) SHA1(b9194fb337b30502c1c9501cd6c64ae4035544d4) )    // == sf2-5m.4a
	ROM_LOAD64_WORD( "sf2_08.bin", 0x000002, 0x80000, CRC(57213be8) SHA1(3759b851ac0904ec79cbb67a2264d384b6f2f9f9) )    // == sf2-7m.6a
	ROM_LOAD64_WORD( "sf2_05.bin", 0x000004, 0x80000, CRC(ba529b4f) SHA1(520840d727161cf09ca784919fa37bc9b54cc3ce) )    // == sf2-1m.3a
	ROM_LOAD64_WORD( "sf2_07.bin", 0x000006, 0x80000, CRC(4b1b33a8) SHA1(2360cff890551f76775739e2d6563858bff80e41) )    // == sf2-3m.5a
	ROM_LOAD64_WORD( "sf2_15.bin", 0x200000, 0x80000, CRC(2c7e2229) SHA1(357c2275af9133fd0bd6fbb1fa9ad5e0b490b3a2) )    // == sf2-6m.4c
	ROM_LOAD64_WORD( "sf2_17.bin", 0x200002, 0x80000, CRC(b5548f17) SHA1(baa92b91cf616bc9e2a8a66adc777ffbf962a51b) )    // == sf2-8m.6c
	ROM_LOAD64_WORD( "sf2_14.bin", 0x200004, 0x80000, CRC(14b84312) SHA1(2eea16673e60ba7a10bd4d8f6c217bb2441a5b0e) )    // == sf2-2m.3c
	ROM_LOAD64_WORD( "sf2_16.bin", 0x200006, 0x80000, CRC(5e9cd89a) SHA1(f787aab98668d4c2c54fc4ba677c0cb808e4f31e) )    // == sf2-4m.5c
	ROM_LOAD64_WORD( "sf2_25.bin", 0x400000, 0x80000, CRC(994bfa58) SHA1(5669b845f624b10e7be56bfc89b76592258ce48b) )    // == sf2-13m.4d
	ROM_LOAD64_WORD( "sf2_27.bin", 0x400002, 0x80000, CRC(3e66ad9d) SHA1(9af9df0826988872662753e9717c48d46f2974b0) )    // == sf2-15m.6d
	ROM_LOAD64_WORD( "sf2_24.bin", 0x400004, 0x80000, CRC(c1befaa8) SHA1(a6a7f4725e52678cbd8d557285c01cdccb2c2602) )    // == sf2-9m.3d
	ROM_LOAD64_WORD( "sf2_26.bin", 0x400006, 0x80000, CRC(0627c831) SHA1(f9a92d614e8877d648449de2612fc8b43c85e4c2) )    // == sf2-11m.5d

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sf2_09.bin",    0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sf2_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "sf2_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( 3wonders )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "rte_30a.11f", 0x00000, 0x20000, CRC(ef5b8b33) SHA1(2313168e5f10505ceece5fdaada0d30df3ca146c) )
	ROM_LOAD16_BYTE( "rte_35a.11h", 0x00001, 0x20000, CRC(7d705529) SHA1(b456629b5755b701cca8a438d24957367a260ec5) )
	ROM_LOAD16_BYTE( "rte_31a.12f", 0x40000, 0x20000, CRC(32835e5e) SHA1(9ec530561030a75a1283ff2aacc21e55613b682b) )
	ROM_LOAD16_BYTE( "rte_36a.12h", 0x40001, 0x20000, CRC(7637975f) SHA1(56935032eebd3e1c5059f6842b97001dae0aa55f) )
	ROM_LOAD16_BYTE( "rt_28a.9f",   0x80000, 0x20000, CRC(054137c8) SHA1(e4c406e0a32198323a5931093fbaa6836510b8ad) )
	ROM_LOAD16_BYTE( "rt_33a.9h",   0x80001, 0x20000, CRC(7264cb1b) SHA1(b367acb9f6579569321ecaa98a14e29dd775b9db) )
	ROM_LOAD16_BYTE( "rte_29a.10f", 0xc0000, 0x20000, CRC(cddaa919) SHA1(0c98e95ad5033d2c5ade7651243e7ccdb4e35463) )
	ROM_LOAD16_BYTE( "rte_34a.10h", 0xc0001, 0x20000, CRC(ed52e7e5) SHA1(352433ae484967d26376141e3a8a0f968b98fde6) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "rt-5m.7a",  0x000000, 0x80000, CRC(86aef804) SHA1(723927ef3bf992d12395c52db051ece7bf57d5e5) )
	ROM_LOAD64_WORD( "rt-7m.9a",  0x000002, 0x80000, CRC(4f057110) SHA1(b7d35c883a74cf4bfb242d9f15a0e40ed1ec111f) )
	ROM_LOAD64_WORD( "rt-1m.3a",  0x000004, 0x80000, CRC(902489d0) SHA1(748ba416a8b9343059a3e7d8b93f02162feb1d0b) )
	ROM_LOAD64_WORD( "rt-3m.5a",  0x000006, 0x80000, CRC(e35ce720) SHA1(6c1a87a1f819bdc20408b5a7823cf35a79d34110) )
	ROM_LOAD64_WORD( "rt-6m.8a",  0x200000, 0x80000, CRC(13cb0e7c) SHA1(e429d594d9a7ff4cc6306e2796a9d6ad0fa25569) )
	ROM_LOAD64_WORD( "rt-8m.10a", 0x200002, 0x80000, CRC(1f055014) SHA1(d64f5be9bb2ef761ca9b2e797dbc3554cf996a79) )
	ROM_LOAD64_WORD( "rt-2m.4a",  0x200004, 0x80000, CRC(e9a034f4) SHA1(deb4cb5886705380b57d4fe9b9bf3c032e1d6227) )
	ROM_LOAD64_WORD( "rt-4m.6a",  0x200006, 0x80000, CRC(df0eea8b) SHA1(5afa05654cccb0504bd44569d42fd68f08fd172f) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "rt_9.12b",   0x00000, 0x08000, CRC(abfca165) SHA1(428069d3bdc45775854cd0e8abe447f134fe5492) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "rt_18.11c",  0x00000, 0x20000, CRC(26b211ab) SHA1(0ea03fdd9edff41eacfc52aa9e0421c10968356b) )
	ROM_LOAD( "rt_19.12c",  0x20000, 0x20000, CRC(dbe64ad0) SHA1(09f2ad522fe75d7bcca094b8c6696c3733b539d5) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "rt24b.1a",     0x0000, 0x0117, CRC(54b85159) SHA1(c6f4fb5d747a215f4f50e4f2258e35d3f9bdbb2e) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )    /* seen the same pcb with LWIO.11E */

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( 3wondersr1 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "rte_30.11f", 0x00000, 0x20000, CRC(0d541519) SHA1(ce0f9ebaea038c98f8866624d3061c65c84cf20d) )
	ROM_LOAD16_BYTE( "rte_35.11h", 0x00001, 0x20000, CRC(73dd0e20) SHA1(b4f4086574176befa70863a85a3c0a8fd6e4bd90) )
	ROM_LOAD16_BYTE( "rte_31.12f", 0x40000, 0x20000, CRC(33e0337d) SHA1(53e5795baf307a789b4337f081a932359e569a98) )
	ROM_LOAD16_BYTE( "rte_36.12h", 0x40001, 0x20000, CRC(a8865243) SHA1(0a4d7e8e07d88104aa01457a10a8a9abd3ed4ce1) )
	ROM_LOAD16_BYTE( "rte_28.9f",  0x80000, 0x20000, CRC(054137c8) SHA1(e4c406e0a32198323a5931093fbaa6836510b8ad) ) // == rt_28a.9f
	ROM_LOAD16_BYTE( "rte_33.9h",  0x80001, 0x20000, CRC(7264cb1b) SHA1(b367acb9f6579569321ecaa98a14e29dd775b9db) ) // == rt_33a.9h
	ROM_LOAD16_BYTE( "rte_29.10f", 0xc0000, 0x20000, CRC(9a8df1e4) SHA1(5b61d2d729145d524e23036bb27ba3ba75c208fe) )
	ROM_LOAD16_BYTE( "rte_34.10h", 0xc0001, 0x20000, CRC(6348a79d) SHA1(2e4b58f9c6c1ad6a89c90d8ebb46cee2613f1397) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "rt-5m.7a",  0x000000, 0x80000, CRC(86aef804) SHA1(723927ef3bf992d12395c52db051ece7bf57d5e5) )
	ROM_LOAD64_WORD( "rt-7m.9a",  0x000002, 0x80000, CRC(4f057110) SHA1(b7d35c883a74cf4bfb242d9f15a0e40ed1ec111f) )
	ROM_LOAD64_WORD( "rt-1m.3a",  0x000004, 0x80000, CRC(902489d0) SHA1(748ba416a8b9343059a3e7d8b93f02162feb1d0b) )
	ROM_LOAD64_WORD( "rt-3m.5a",  0x000006, 0x80000, CRC(e35ce720) SHA1(6c1a87a1f819bdc20408b5a7823cf35a79d34110) )
	ROM_LOAD64_WORD( "rt-6m.8a",  0x200000, 0x80000, CRC(13cb0e7c) SHA1(e429d594d9a7ff4cc6306e2796a9d6ad0fa25569) )
	ROM_LOAD64_WORD( "rt-8m.10a", 0x200002, 0x80000, CRC(1f055014) SHA1(d64f5be9bb2ef761ca9b2e797dbc3554cf996a79) )
	ROM_LOAD64_WORD( "rt-2m.4a",  0x200004, 0x80000, CRC(e9a034f4) SHA1(deb4cb5886705380b57d4fe9b9bf3c032e1d6227) )
	ROM_LOAD64_WORD( "rt-4m.6a",  0x200006, 0x80000, CRC(df0eea8b) SHA1(5afa05654cccb0504bd44569d42fd68f08fd172f) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "rt_9.12b",   0x00000, 0x08000, CRC(7d5a77a7) SHA1(4f2a6534d7a9e518061102c1dd7d7aca39600c33) )   /* 1 byte different from 3wonders, pcb verified */
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "rt_18.11c", 0x00000, 0x20000, CRC(f6dc0d3d) SHA1(9959fc3339613042c95b90aad2bc1c3dafdb605e) )   /* 6 bytes different from 3wonders, pcb verified */
	ROM_LOAD( "rt_19.12c", 0x20000, 0x20000, CRC(dbe64ad0) SHA1(09f2ad522fe75d7bcca094b8c6696c3733b539d5) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "rt24b.1a",     0x0000, 0x0117, CRC(54b85159) SHA1(c6f4fb5d747a215f4f50e4f2258e35d3f9bdbb2e) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( 3wondersu )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "rtu_30a.11f", 0x00000, 0x20000, CRC(0b156fd8) SHA1(1ec811cd7cbd12066f876db7255394e754ceb25e) )
	ROM_LOAD16_BYTE( "rtu_35a.11h", 0x00001, 0x20000, CRC(57350bf4) SHA1(33e8685cce82eee7bcb7c2787318a130764e97e2) )
	ROM_LOAD16_BYTE( "rtu_31a.12f", 0x40000, 0x20000, CRC(0e723fcc) SHA1(91eeab6376a5aa852152af9920aef60bc7c689dd) )
	ROM_LOAD16_BYTE( "rtu_36a.12h", 0x40001, 0x20000, CRC(523a45dc) SHA1(6d6743803016fa5ba713e0d6f61affce8a3255ec) )
	ROM_LOAD16_BYTE( "rt_28a.9f",   0x80000, 0x20000, CRC(054137c8) SHA1(e4c406e0a32198323a5931093fbaa6836510b8ad) )
	ROM_LOAD16_BYTE( "rt_33a.9h",   0x80001, 0x20000, CRC(7264cb1b) SHA1(b367acb9f6579569321ecaa98a14e29dd775b9db) )
	ROM_LOAD16_BYTE( "rtu_29a.10f", 0xc0000, 0x20000, CRC(37ba3e20) SHA1(a128b1a17639b06a4fd8acffe0357f1dbd1d4fe9) )
	ROM_LOAD16_BYTE( "rtu_34a.10h", 0xc0001, 0x20000, CRC(f99f46c0) SHA1(cda24a6baa3f861e7078fb2fa91328cc1cddc866) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "rt-5m.7a",  0x000000, 0x80000, CRC(86aef804) SHA1(723927ef3bf992d12395c52db051ece7bf57d5e5) )
	ROM_LOAD64_WORD( "rt-7m.9a",  0x000002, 0x80000, CRC(4f057110) SHA1(b7d35c883a74cf4bfb242d9f15a0e40ed1ec111f) )
	ROM_LOAD64_WORD( "rt-1m.3a",  0x000004, 0x80000, CRC(902489d0) SHA1(748ba416a8b9343059a3e7d8b93f02162feb1d0b) )
	ROM_LOAD64_WORD( "rt-3m.5a",  0x000006, 0x80000, CRC(e35ce720) SHA1(6c1a87a1f819bdc20408b5a7823cf35a79d34110) )
	ROM_LOAD64_WORD( "rt-6m.8a",  0x200000, 0x80000, CRC(13cb0e7c) SHA1(e429d594d9a7ff4cc6306e2796a9d6ad0fa25569) )
	ROM_LOAD64_WORD( "rt-8m.10a", 0x200002, 0x80000, CRC(1f055014) SHA1(d64f5be9bb2ef761ca9b2e797dbc3554cf996a79) )
	ROM_LOAD64_WORD( "rt-2m.4a",  0x200004, 0x80000, CRC(e9a034f4) SHA1(deb4cb5886705380b57d4fe9b9bf3c032e1d6227) )
	ROM_LOAD64_WORD( "rt-4m.6a",  0x200006, 0x80000, CRC(df0eea8b) SHA1(5afa05654cccb0504bd44569d42fd68f08fd172f) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "rt_9.12b",   0x00000, 0x08000, CRC(abfca165) SHA1(428069d3bdc45775854cd0e8abe447f134fe5492) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "rt_18.11c",  0x00000, 0x20000, CRC(26b211ab) SHA1(0ea03fdd9edff41eacfc52aa9e0421c10968356b) )
	ROM_LOAD( "rt_19.12c",  0x20000, 0x20000, CRC(dbe64ad0) SHA1(09f2ad522fe75d7bcca094b8c6696c3733b539d5) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "rt24b.1a",     0x0000, 0x0117, CRC(54b85159) SHA1(c6f4fb5d747a215f4f50e4f2258e35d3f9bdbb2e) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )    /* seen the same pcb with LWIO.11E */

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
ROM_END

/* B-Board 89625B-1 */
ROM_START( wonder3 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "rtj_36.12f", 0x00000, 0x20000, CRC(e3741247) SHA1(4deb0f667697631693fbefddaeb8cf98fd0b90ce) )
	ROM_LOAD16_BYTE( "rtj_42.12h", 0x00001, 0x20000, CRC(b4baa117) SHA1(44486b3d50f9b0a8c32c2c2dc5f1a046aface7b6) )
	ROM_LOAD16_BYTE( "rtj_37.13f", 0x40000, 0x20000, CRC(a1f677b0) SHA1(e1511ea0fa4a689d1355119ac37c075192880dde) )
	ROM_LOAD16_BYTE( "rtj_43.13h", 0x40001, 0x20000, CRC(85337a47) SHA1(0a247aa56c5cc17b1e888df7b502f65e88715469) )
	ROM_LOAD16_BYTE( "rt_34.10f",  0x80000, 0x20000, CRC(054137c8) SHA1(e4c406e0a32198323a5931093fbaa6836510b8ad) ) // == rt_28a.9f
	ROM_LOAD16_BYTE( "rt_40.10h",  0x80001, 0x20000, CRC(7264cb1b) SHA1(b367acb9f6579569321ecaa98a14e29dd775b9db) ) // == rt_33a.9h
	ROM_LOAD16_BYTE( "rtj_35.11f", 0xc0000, 0x20000, CRC(e72f9ea3) SHA1(c63df200416bd61af73e8589204f7daef743041e) )
	ROM_LOAD16_BYTE( "rtj_41.11h", 0xc0001, 0x20000, CRC(a11ee998) SHA1(b892398e2ff4e40e51b858cfdbce866a75c670e6) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_BYTE( "rt_09.4b",  0x000000, 0x20000, CRC(2c40e480) SHA1(823a36aa6dba7a028d4da5faef752366aa18fe57) ) // == rt-5m.7a
	ROM_LOAD64_BYTE( "rt_01.4a",  0x000001, 0x20000, CRC(3e11f8cd) SHA1(88d7edd7102b1abec2b6f87029d39414f3ebc094) ) // == rt-5m.7a
	ROM_LOAD64_BYTE( "rt_13.9b",  0x000002, 0x20000, CRC(51009117) SHA1(55549a2bd1abb3aea5dc17f17a2bd4a46c6cf12a) ) // == rt-7m.9a
	ROM_LOAD64_BYTE( "rt_05.9a",  0x000003, 0x20000, CRC(283fd470) SHA1(2d7ff9c5a747565f2186b9252b703ebac3117beb) ) // == rt-7m.9a
	ROM_LOAD64_BYTE( "rt_24.5e",  0x000004, 0x20000, CRC(ee4484ce) SHA1(5bb27c119a4e69cb50425ca73556a2e4e4482728) ) // == rt-1m.3a
	ROM_LOAD64_BYTE( "rt_17.5c",  0x000005, 0x20000, CRC(e5dcddeb) SHA1(9a1c740cdd66ee2f58737eda613e79ff6357142a) ) // == rt-1m.3a
	ROM_LOAD64_BYTE( "rt_38.8h",  0x000006, 0x20000, CRC(b2940c2d) SHA1(62ea57fb0203dd8f0e123e55eef7637702f8466d) ) // == rt-3m.5a
	ROM_LOAD64_BYTE( "rt_32.8f",  0x000007, 0x20000, CRC(08e2b758) SHA1(f56a4f16454fe528c358fd212449a1ecb0f826e0) ) // == rt-3m.5a
	ROM_LOAD64_BYTE( "rt_10.5b",  0x100000, 0x20000, CRC(e3f3ff94) SHA1(d5e46da2d25ca5347037b9859227c949209b30a6) ) // == rt-5m.7a
	ROM_LOAD64_BYTE( "rt_02.5a",  0x100001, 0x20000, CRC(fcffd73c) SHA1(64830ffc053bd97f22b406f53b1e2e4a78db6a97) ) // == rt-5m.7a
	ROM_LOAD64_BYTE( "rt_14.10b", 0x100002, 0x20000, CRC(5c546d9a) SHA1(1e0d0451e83dddb3371bffae6af7e17908816aec) ) // == rt-7m.9a
	ROM_LOAD64_BYTE( "rt_06.10a", 0x100003, 0x20000, CRC(d9650bc4) SHA1(d28d85595bee9f6d4a697486a9db3a71ce60de50) ) // == rt-7m.9a
	ROM_LOAD64_BYTE( "rt_25.7e",  0x100004, 0x20000, CRC(11b28831) SHA1(bb8f97871ca15184dbed3a90f8968a40b83a4480) ) // == rt-1m.3a
	ROM_LOAD64_BYTE( "rt_18.7c",  0x100005, 0x20000, CRC(ce1afb7c) SHA1(50a330bb2d748f1a738fa7895aba81d9f0c14579) ) // == rt-1m.3a
	ROM_LOAD64_BYTE( "rt_39.9h",  0x100006, 0x20000, CRC(ea7ac9ee) SHA1(bc21d8e59eb190608a87072c22be9cb1cf1227cc) ) // == rt-3m.5a
	ROM_LOAD64_BYTE( "rt_33.9f",  0x100007, 0x20000, CRC(d6a99384) SHA1(552b012eb911b8739ee859af13e176a8396cecf2) ) // == rt-3m.5a
	ROM_LOAD64_BYTE( "rt_11.7b",  0x200000, 0x20000, CRC(04f3c298) SHA1(1f3f8713ed8a2ad2bf4afce4c733eb9cb850ca9f) ) // == rt-6m.8a
	ROM_LOAD64_BYTE( "rt_03.7a",  0x200001, 0x20000, CRC(98087e08) SHA1(6a13786a62e11d77c4da8469422e402df1299162) ) // == rt-6m.8a
	ROM_LOAD64_BYTE( "rt_15.11b", 0x200002, 0x20000, CRC(b6aba565) SHA1(a166c853a5b4bc2602ce14974c11f570ba29df6a) ) // == rt-8m.10a
	ROM_LOAD64_BYTE( "rt_07.11a", 0x200003, 0x20000, CRC(c62defa1) SHA1(2533f39251c99d5a184d72a5b96b5603466c0d11) ) // == rt-8m.10a
	ROM_LOAD64_BYTE( "rt_26.8e",  0x200004, 0x20000, CRC(532f542e) SHA1(c894b385aa10a5e80b548c01817958739e2afa89) ) // == rt-2m.4a
	ROM_LOAD64_BYTE( "rt_19.8c",  0x200005, 0x20000, CRC(1f0f72bd) SHA1(ad1afcde397a3273afc7c7a0b084a9b68e9e736e) ) // == rt-2m.4a
	ROM_LOAD64_BYTE( "rt_28.10e", 0x200006, 0x20000, CRC(6064e499) SHA1(3bc30b9d8dde5f5e8dda31afbdadb5b2e4d50932) ) // == rt-4m.6a
	ROM_LOAD64_BYTE( "rt_21.10c", 0x200007, 0x20000, CRC(20012ddc) SHA1(4389f2554c429f0a421425a6645dd8e719f4995f) ) // == rt-4m.6a
	ROM_LOAD64_BYTE( "rt_12.8b",  0x300000, 0x20000, CRC(e54664cc) SHA1(e3b5ff0e9af20580cb4228f644f23a05aad20478) ) // == rt-6m.8a
	ROM_LOAD64_BYTE( "rt_04.8a",  0x300001, 0x20000, CRC(4d7b9a1a) SHA1(1a9dd66bb97e2a02f3264d5766b674b588ad7dfc) ) // == rt-6m.8a
	ROM_LOAD64_BYTE( "rt_16.12b", 0x300002, 0x20000, CRC(37c96cfc) SHA1(270f824757c0f536b02fef147d8e0af07e8d7147) ) // == rt-8m.10a
	ROM_LOAD64_BYTE( "rt_08.12a", 0x300003, 0x20000, CRC(75f4975b) SHA1(1cd78828db97931ab0bfe0339e7051c58b3eff60) ) // == rt-8m.10a
	ROM_LOAD64_BYTE( "rt_27.9e",  0x300004, 0x20000, CRC(ec6edc0f) SHA1(6dc13d692ca7bc989cd9b40bab8a2943425b7d61) ) // == rt-2m.4a
	ROM_LOAD64_BYTE( "rt_20.9c",  0x300005, 0x20000, CRC(4fe52659) SHA1(1bf22ae192b57cd62e92f290313cc9d3234b2700) ) // == rt-2m.4a
	ROM_LOAD64_BYTE( "rt_29.11e", 0x300006, 0x20000, CRC(8fa77f9f) SHA1(2f6b37d8e5eed38a8847c9ad736a7cdbe9958a70) ) // == rt-4m.6a
	ROM_LOAD64_BYTE( "rt_22.11c", 0x300007, 0x20000, CRC(228a0d4a) SHA1(bcaf12d01abe1d3cd5731bd5341cb22c4ca6139e) ) // == rt-4m.6a

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "rt_23.13b",        0x00000, 0x08000, CRC(abfca165) SHA1(428069d3bdc45775854cd0e8abe447f134fe5492) )    // == rt_9.12b
//  ROM_LOAD( "rt_23.13b",        0x00000, 0x08000, CRC(7d5a77a7) SHA1(4f2a6534d7a9e518061102c1dd7d7aca39600c33) )    /* dumped from another board, 1 byte different, pcb verified */
	ROM_CONTINUE(                 0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "rt_30.12c",  0x00000, 0x20000, CRC(26b211ab) SHA1(0ea03fdd9edff41eacfc52aa9e0421c10968356b) )    // == rt_18.11c
	ROM_LOAD( "rt_31.13c",  0x20000, 0x20000, CRC(dbe64ad0) SHA1(09f2ad522fe75d7bcca094b8c6696c3733b539d5) )    // == rt_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "rt22b.1a",     0x0000, 0x0117, CRC(89560d6a) SHA1(0f88920536eb131948339becb14557d77e02b9f8) )
	ROM_LOAD( "iob1.12e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
ROM_END

/* Three Wonders (bootleg) */
ROM_START( 3wondersb )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "274001.4", 0x00000, 0x80000, CRC(47887cf3) SHA1(cdb2df67c99fd698845484869608bfaafa782bd6) )
	ROM_LOAD16_BYTE( "274001.3", 0x00001, 0x80000, CRC(e79eacb3) SHA1(cb2712b4e566d7d2ab5639f2e62f6e15244a7f09) )

	// The remainder of the roms in this set have the same content as those from '3wondersh'.
	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_BYTE( "274001.12", 0x000000, 0x80000, CRC(47cf8dfb) SHA1(05fed8e61d8f8c0382d820e7bdef36db85ad416b) )
	ROM_LOAD64_BYTE( "274001.8",  0x000001, 0x80000, CRC(3bc2ef5e) SHA1(6864838e8f116bc56e7d205674240763a55d7907) )
	ROM_LOAD64_BYTE( "274001.10", 0x000002, 0x80000, CRC(a0d27605) SHA1(7f2a7d3cc8802e778a98a29a4992a223932bff2c) )
	ROM_LOAD64_BYTE( "274001.6",  0x000003, 0x80000, CRC(312d790c) SHA1(8739ba0784447f4050da1e6de8e31df3537b307d) )
	ROM_LOAD64_BYTE( "274001.11", 0x000004, 0x80000, CRC(8112bbb4) SHA1(5a93cee8627afeb255eec1d26ec043cd2c4d2218) )
	ROM_LOAD64_BYTE( "274001.7",  0x000005, 0x80000, CRC(58307167) SHA1(e253b64e144ccf3c4a0268e4be68468f11b2b071) )
	ROM_LOAD64_BYTE( "274001.9",  0x000006, 0x80000, CRC(cb73759d) SHA1(e6fa263bcd7876f83f499fb95fbbc45ebce950fe) )
	ROM_LOAD64_BYTE( "274001.5",  0x000007, 0x80000, CRC(3f765ae8) SHA1(a37cccbf2212eaa38a77b2f712688ce45b17e3b5) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "27512.2",    0x00000, 0x08000, CRC(abfca165) SHA1(428069d3bdc45775854cd0e8abe447f134fe5492) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "27020.1",    0x00000, 0x40000, CRC(3c4348cf) SHA1(cfa9fd2f9692ddfef856106de191d3bdb5289297) )
ROM_END

/* Three Wonders (Italian bootleg) -  "Wonder3 rev.2" PCB
   The program has only minor differences from 3wondersb (scroll x base in RAM and a routine that currently stops the game from booting)
   GFX ROMs use a rather different format */
ROM_START( 3wondersbi )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "3.u11", 0x00000, 0x80000, CRC(65ae8c7e) SHA1(fd38e9953f10fc6ab4ae681b2644f09482af351f) ) // 27C4001
	ROM_LOAD16_BYTE( "4.u8",  0x00001, 0x80000, CRC(67a60fe6) SHA1(3e7d0e6090ccc961d85e460cdefcd069dc851d20) ) // 27C4001

	ROM_REGION( 0x500000, "gfx", 0 ) // all 27C4001 TODO: correct ROM loading
	ROM_LOAD( "5.u140",  0x000000, 0x80000, CRC(156c487b) SHA1(f6f5cccd0cad48b0e1082c57cea31b933a7244bd) )
	ROM_LOAD( "6.u128",  0x080000, 0x80000, CRC(b671f752) SHA1(3c766a3456183d7e2276fcdf210aa171ffb6a9ef) )
	ROM_LOAD( "7.u126",  0x100000, 0x80000, CRC(2f835213) SHA1(457ceeb3a970124e5cf588c85a1bf9f32e9c9383) )
	ROM_LOAD( "8.u139",  0x180000, 0x80000, CRC(e9421cba) SHA1(171966bb3127d0d860b6e780ba6ad09fe3689345) )
	ROM_LOAD( "9.u127",  0x200000, 0x80000, CRC(8d08103c) SHA1(0878edf5ca09922fbd4361d2e0bf793eccb86b81) )
	ROM_LOAD( "10.u125", 0x280000, 0x80000, CRC(da529062) SHA1(e46359d8e6dfb5b75103fc16cda45f75ea8036ce) )
	ROM_LOAD( "11.u138", 0x300000, 0x80000, CRC(2007b45a) SHA1(6a55b55f92bd0a02a5c4fbcac8caab915a4de584) )
	ROM_LOAD( "12.u137", 0x380000, 0x80000, CRC(7dff1790) SHA1(f4d83c81eab3ba8a538a9a89995d10c1e8119c54) )
	ROM_LOAD( "13.u130", 0x400000, 0x80000, CRC(1054d40c) SHA1(68145385c01a5d9d94f17f74f36f5939484308e8) )
	ROM_LOAD( "14.u129", 0x480000, 0x80000, CRC(2e32610b) SHA1(ff4b351a53a16191dc3ef6e0fce2421191b5312b) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "1.u18", 0x00000, 0x08000, CRC(9f4017fb) SHA1(a4c652c406dd521cc26ef21ad4a2b7091ed9defe) ) // 27c512
	ROM_CONTINUE(      0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "2.u24", 0x00000, 0x40000, CRC(db01f9fe) SHA1(b78c77ab29fb667e796b07dc5f3a53b47250e713) ) // 27C4001
	ROM_CONTINUE(      0x00000, 0x40000 ) // 0xxxxxxxxxxxxxxxxxx = 0xFF
ROM_END

/* Three Wonders (hack) */
ROM_START( 3wondersh )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "22.bin",      0x00000, 0x20000, CRC(e6071884) SHA1(5cf1a859739cf98846cb049b64fd083733acb29c) )
	ROM_LOAD16_BYTE( "26.bin",      0x00001, 0x20000, CRC(a28447b7) SHA1(e3f11911f1d3d115c03edf1bb6c8a68fccd9e9b3) )
	ROM_LOAD16_BYTE( "23.bin",      0x40000, 0x20000, CRC(fd3d6509) SHA1(0824ec397d12c2b832c9e694c23b59c2e489ed3b) )
	ROM_LOAD16_BYTE( "27.bin",      0x40001, 0x20000, CRC(999cba3d) SHA1(99dfb902c0c77f798a868eb13340eb54fb4a84d3) )
	ROM_LOAD16_BYTE( "rt_28a.9f",   0x80000, 0x20000, CRC(054137c8) SHA1(e4c406e0a32198323a5931093fbaa6836510b8ad) )
	ROM_LOAD16_BYTE( "rt_33a.9h",   0x80001, 0x20000, CRC(7264cb1b) SHA1(b367acb9f6579569321ecaa98a14e29dd775b9db) )
	ROM_LOAD16_BYTE( "rte_29a.10f", 0xc0000, 0x20000, CRC(cddaa919) SHA1(0c98e95ad5033d2c5ade7651243e7ccdb4e35463) )
	ROM_LOAD16_BYTE( "rte_34a.10h", 0xc0001, 0x20000, CRC(ed52e7e5) SHA1(352433ae484967d26376141e3a8a0f968b98fde6) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_BYTE( "16.bin",    0x000000, 0x40000, CRC(c997bca2) SHA1(6b6cd9c2fc56690c42a3368cd5e6c98d6bff5b5b) )
	ROM_LOAD64_BYTE( "6.bin",     0x000001, 0x40000, CRC(3eea321a) SHA1(4368aaf8a532c29f4b950adb2daedd3069d84cf1) )
	ROM_LOAD64_BYTE( "18.bin",    0x000002, 0x40000, CRC(98acdfd4) SHA1(910dfd6742e166530388c700c0797e692e501f97) )
	ROM_LOAD64_BYTE( "8.bin",     0x000003, 0x40000, CRC(dc9ca6f9) SHA1(77549e9a128d1b7ca0c4547cdc56f43450d426e0) )
	ROM_LOAD64_BYTE( "12.bin",    0x000004, 0x40000, CRC(0d8a6007) SHA1(48d61cbd91df01ba4b2f0e80e60375a50ddb065e) )
	ROM_LOAD64_BYTE( "2.bin",     0x000005, 0x40000, CRC(d75563b9) SHA1(7b7b105b84dc5d7b17838961fdd8be5bac90cbc6) )
	ROM_LOAD64_BYTE( "14.bin",    0x000006, 0x40000, CRC(84369a28) SHA1(3877186371fe289522133fd99be034b141a974ca) )
	ROM_LOAD64_BYTE( "4.bin",     0x000007, 0x40000, CRC(d4831578) SHA1(9ff5860f22976e9e4c023946f35e24fde84fe8ea) )
	ROM_LOAD64_BYTE( "17.bin",    0x200000, 0x40000, CRC(040edff5) SHA1(9747d67b980cc357c6fa732300a84ae55150bc51) )
	ROM_LOAD64_BYTE( "7.bin",     0x200001, 0x40000, CRC(c7c0468c) SHA1(e7a14cf579b023e8954b7e06aa2337db4f53bedc) )
	ROM_LOAD64_BYTE( "19.bin",    0x200002, 0x40000, CRC(9fef114f) SHA1(394afb083ce7c46d9a39097d0040f9e18aaab508) )
	ROM_LOAD64_BYTE( "9.bin",     0x200003, 0x40000, CRC(48cbfba5) SHA1(74047433e50795e29d8299526ae2c424610f0a5e) )
	ROM_LOAD64_BYTE( "13.bin",    0x200004, 0x40000, CRC(8fc3d7d1) SHA1(e2784e0fccfe062ea8dc440e4a884fc665f4a846) )
	ROM_LOAD64_BYTE( "3.bin",     0x200005, 0x40000, CRC(c65e9a86) SHA1(359ab1e2dd0fcf38ed9815a6a50294cbeca8223c) )
	ROM_LOAD64_BYTE( "15.bin",    0x200006, 0x40000, CRC(f239341a) SHA1(b1858f5f7a5d210c5327b84a797ed7e898250596) )
	ROM_LOAD64_BYTE( "5.bin",     0x200007, 0x40000, CRC(947ac944) SHA1(d962f49ba532fc60209bb3957ff8a456855ef67f) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "rt_9.12b",   0x00000, 0x08000, CRC(abfca165) SHA1(428069d3bdc45775854cd0e8abe447f134fe5492) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "rt_18.11c",  0x00000, 0x20000, CRC(26b211ab) SHA1(0ea03fdd9edff41eacfc52aa9e0421c10968356b) )
	ROM_LOAD( "rt_19.12c",  0x20000, 0x20000, CRC(dbe64ad0) SHA1(09f2ad522fe75d7bcca094b8c6696c3733b539d5) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "rt24b.1a",     0x0000, 0x0117, CRC(54b85159) SHA1(c6f4fb5d747a215f4f50e4f2258e35d3f9bdbb2e) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
ROM_END

/* B-Board 90629B-3 */
ROM_START( kod )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "kde_30a.11e", 0x00000, 0x20000, CRC(fcb5efe2) SHA1(6122e4852633876ff2ccd4b72296fce96446b3ee) )
	ROM_LOAD16_BYTE( "kde_37a.11f", 0x00001, 0x20000, CRC(f22e5266) SHA1(2fbadce701218f4a56bfd6dfd758abacb34a2181) )
	ROM_LOAD16_BYTE( "kde_31a.12e", 0x40000, 0x20000, CRC(c710d722) SHA1(a2e9b84d3e7d835a910ab9f584bdc64c2559995a) )
	ROM_LOAD16_BYTE( "kde_38a.12f", 0x40001, 0x20000, CRC(57d6ed3a) SHA1(a47da5068723c8e16ed458fbfa3e3db57b32d87d) )
	ROM_LOAD16_BYTE( "kd_28.9e",    0x80000, 0x20000, CRC(9367bcd9) SHA1(8243b4b9bb9756f3fa726717e19a166cb2f5b50a) )
	ROM_LOAD16_BYTE( "kd_35.9f",    0x80001, 0x20000, CRC(4ca6a48a) SHA1(9d440ecd8d2d0e293fecf64ca3915252b94e7aef) )
	ROM_LOAD16_BYTE( "kd_29.10e",   0xc0000, 0x20000, CRC(0360fa72) SHA1(274769c8717a874397cf37369e3ef80a682d9ef2) )
	ROM_LOAD16_BYTE( "kd_36a.10f",  0xc0001, 0x20000, CRC(95a3cef8) SHA1(9b75c1ed0eafacc230197ffd9b81e0c8f4f2c464) )    /* seen the same pcb with KDE_36A.10F */

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "kd-5m.4a", 0x000000, 0x80000, CRC(e45b8701) SHA1(604e39e455e81695ee4f899f102d0bcd789cedd0) )  // in "2" socket
	ROM_LOAD64_WORD( "kd-7m.6a", 0x000002, 0x80000, CRC(a7750322) SHA1(3c583496a53cd64edf377db35f7f40f02b59b7e7) )  // in "4" socket
	ROM_LOAD64_WORD( "kd-1m.3a", 0x000004, 0x80000, CRC(5f74bf78) SHA1(b7c43eea9bf77a0fb571dcd53f8be719e6655fd9) )  // in "1" socket
	ROM_LOAD64_WORD( "kd-3m.5a", 0x000006, 0x80000, CRC(5e5303bf) SHA1(d9f90b898ffdf4398b2bbeb48247f06f728e7c00) )  // in "3" socket
	ROM_LOAD64_WORD( "kd-6m.4c", 0x200000, 0x80000, CRC(113358f3) SHA1(9d98eafa74a046f65bf3847fe1d88ea1b0c82b0c) )  // in "11" socket
	ROM_LOAD64_WORD( "kd-8m.6c", 0x200002, 0x80000, CRC(38853c44) SHA1(a6e552fb0138a76a7878b90d202904e2b44ae7ec) )  // in "13" socket
	ROM_LOAD64_WORD( "kd-2m.3c", 0x200004, 0x80000, CRC(9ef36604) SHA1(b42ca0a910b65e1e7bb6e7d734e853ce67e821bf) )  // in "10" socket
	ROM_LOAD64_WORD( "kd-4m.5c", 0x200006, 0x80000, CRC(402b9b4f) SHA1(4c11976976eadf1ad293b31b0a4d047d05032b06) )  // in "12" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "kd_9.12a",  0x00000, 0x08000, CRC(bac6ec26) SHA1(6cbb6d55660150ae3f5270e023328275ee1bbf50) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "kd_18.11c", 0x00000, 0x20000, CRC(4c63181d) SHA1(270f27534a95cb0be3ff3f9ca71c502320d8090b) )
	ROM_LOAD( "kd_19.12c", 0x20000, 0x20000, CRC(92941b80) SHA1(5fa7c2793e6febee54a83042d118ddd4f2b7d127) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "kd29b.1a",     0x0000, 0x0117, CRC(6b892f82) SHA1(c0b5c285c45bcc8d885681efa8e0c0e564142684) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-3 */
ROM_START( kodr1 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "kde_30.11e", 0x00000, 0x20000, CRC(c7414fd4) SHA1(37d27fbe7c617a26b53bfdfcd532cf573d42f33b) )
	ROM_LOAD16_BYTE( "kde_37.11f", 0x00001, 0x20000, CRC(a5bf40d2) SHA1(cd34dbeabd0974709411579e669f01d0d799c2a1) )
	ROM_LOAD16_BYTE( "kde_31.12e", 0x40000, 0x20000, CRC(1fffc7bd) SHA1(822c9ad996ca51a99a2bb1fe08fa19e18413030d) )
	ROM_LOAD16_BYTE( "kde_38.12f", 0x40001, 0x20000, CRC(89e57a82) SHA1(aad35f86a8b1b7e3a0b5f3e6efd0e844b3d3d82f) )
	ROM_LOAD16_BYTE( "kde_28.9e",  0x80000, 0x20000, CRC(9367bcd9) SHA1(8243b4b9bb9756f3fa726717e19a166cb2f5b50a) ) // == kd_28.9e
	ROM_LOAD16_BYTE( "kde_35.9f",  0x80001, 0x20000, CRC(4ca6a48a) SHA1(9d440ecd8d2d0e293fecf64ca3915252b94e7aef) ) // == kd_35.9f
	ROM_LOAD16_BYTE( "kde_29.10e", 0xc0000, 0x20000, CRC(6a0ba878) SHA1(82e4037d73889a76b0cdc7a4f8e77e585d38e56e) )
	ROM_LOAD16_BYTE( "kde_36.10f", 0xc0001, 0x20000, CRC(b509b39d) SHA1(6023855e54b170e55abf0f607600031e19e5e722) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "kd-5m.4a", 0x000000, 0x80000, CRC(e45b8701) SHA1(604e39e455e81695ee4f899f102d0bcd789cedd0) )  // in "2" socket
	ROM_LOAD64_WORD( "kd-7m.6a", 0x000002, 0x80000, CRC(a7750322) SHA1(3c583496a53cd64edf377db35f7f40f02b59b7e7) )  // in "4" socket
	ROM_LOAD64_WORD( "kd-1m.3a", 0x000004, 0x80000, CRC(5f74bf78) SHA1(b7c43eea9bf77a0fb571dcd53f8be719e6655fd9) )  // in "1" socket
	ROM_LOAD64_WORD( "kd-3m.5a", 0x000006, 0x80000, CRC(5e5303bf) SHA1(d9f90b898ffdf4398b2bbeb48247f06f728e7c00) )  // in "3" socket
	ROM_LOAD64_WORD( "kd-6m.4c", 0x200000, 0x80000, CRC(113358f3) SHA1(9d98eafa74a046f65bf3847fe1d88ea1b0c82b0c) )  // in "11" socket
	ROM_LOAD64_WORD( "kd-8m.6c", 0x200002, 0x80000, CRC(38853c44) SHA1(a6e552fb0138a76a7878b90d202904e2b44ae7ec) )  // in "13" socket
	ROM_LOAD64_WORD( "kd-2m.3c", 0x200004, 0x80000, CRC(9ef36604) SHA1(b42ca0a910b65e1e7bb6e7d734e853ce67e821bf) )  // in "10" socket
	ROM_LOAD64_WORD( "kd-4m.5c", 0x200006, 0x80000, CRC(402b9b4f) SHA1(4c11976976eadf1ad293b31b0a4d047d05032b06) )  // in "12" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "kd_9.12a",           0x00000, 0x08000, CRC(f5514510) SHA1(07e9c836adf9ef2f7e7729e99015f71e3b5f16e0) )    /* different CRC from kod, pcb verified */
	ROM_CONTINUE(                   0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "kd_18.11c", 0x00000, 0x20000, CRC(69ecb2c8) SHA1(fadf266b6b20bd6329a3e638918c5a3106413476) )    /* different CRC from kod, pcb verified */
	ROM_LOAD( "kd_19.12c", 0x20000, 0x20000, CRC(02d851c1) SHA1(c959a6fc3e7d893557f319debae91f28471f4be2) )    /* different CRC from kod, pcb verified */

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "kd29b.1a",     0x0000, 0x0117, CRC(6b892f82) SHA1(c0b5c285c45bcc8d885681efa8e0c0e564142684) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-3 */
ROM_START( kodr2 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "kde_30.11e", 0x00000, 0x20000, CRC(f8dc4ce3) SHA1(3efc4ffd61b399235f27c97ffb6d556000d59b33) ) /* different CRC from kodr1, pcb verified */
	ROM_LOAD16_BYTE( "kde_37.11f", 0x00001, 0x20000, CRC(d1276c1c) SHA1(1f70972eb5b854ef58c6d0eaccda6e12611cf763) ) /* different CRC from kodr1, pcb verified */
	ROM_LOAD16_BYTE( "kde_31.12e", 0x40000, 0x20000, CRC(309debd8) SHA1(96aa498fe04c1b8f76dab1c0ab074eedd48053fa) ) /* different CRC from kodr1, pcb verified */
	ROM_LOAD16_BYTE( "kde_38.12f", 0x40001, 0x20000, CRC(76cd5738) SHA1(cb81f8db8400c9972f05ace38530f040fb66f93e) ) /* different CRC from kodr1, pcb verified */
	ROM_LOAD16_BYTE( "kd_28.9e",   0x80000, 0x20000, CRC(9367bcd9) SHA1(8243b4b9bb9756f3fa726717e19a166cb2f5b50a) )
	ROM_LOAD16_BYTE( "kd_35.9f",   0x80001, 0x20000, CRC(4ca6a48a) SHA1(9d440ecd8d2d0e293fecf64ca3915252b94e7aef) )
	ROM_LOAD16_BYTE( "kd_29.10e",  0xc0000, 0x20000, CRC(0360fa72) SHA1(274769c8717a874397cf37369e3ef80a682d9ef2) )
	ROM_LOAD16_BYTE( "kd_36.10f",  0xc0001, 0x20000, CRC(3c66c32b) SHA1(16f15a266979acccede0ae0268d692797a2f33cd) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "kd-5m.4a", 0x000000, 0x80000, CRC(e45b8701) SHA1(604e39e455e81695ee4f899f102d0bcd789cedd0) )  // in "2" socket
	ROM_LOAD64_WORD( "kd-7m.6a", 0x000002, 0x80000, CRC(a7750322) SHA1(3c583496a53cd64edf377db35f7f40f02b59b7e7) )  // in "4" socket
	ROM_LOAD64_WORD( "kd-1m.3a", 0x000004, 0x80000, CRC(5f74bf78) SHA1(b7c43eea9bf77a0fb571dcd53f8be719e6655fd9) )  // in "1" socket
	ROM_LOAD64_WORD( "kd-3m.5a", 0x000006, 0x80000, CRC(5e5303bf) SHA1(d9f90b898ffdf4398b2bbeb48247f06f728e7c00) )  // in "3" socket
	ROM_LOAD64_WORD( "kd-6m.4c", 0x200000, 0x80000, CRC(113358f3) SHA1(9d98eafa74a046f65bf3847fe1d88ea1b0c82b0c) )  // in "11" socket
	ROM_LOAD64_WORD( "kd-8m.6c", 0x200002, 0x80000, CRC(38853c44) SHA1(a6e552fb0138a76a7878b90d202904e2b44ae7ec) )  // in "13" socket
	ROM_LOAD64_WORD( "kd-2m.3c", 0x200004, 0x80000, CRC(9ef36604) SHA1(b42ca0a910b65e1e7bb6e7d734e853ce67e821bf) )  // in "10" socket
	ROM_LOAD64_WORD( "kd-4m.5c", 0x200006, 0x80000, CRC(402b9b4f) SHA1(4c11976976eadf1ad293b31b0a4d047d05032b06) )  // in "12" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "kd_9.12a",  0x00000, 0x08000, CRC(bac6ec26) SHA1(6cbb6d55660150ae3f5270e023328275ee1bbf50) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "kd_18.11c", 0x00000, 0x20000, CRC(4c63181d) SHA1(270f27534a95cb0be3ff3f9ca71c502320d8090b) )
	ROM_LOAD( "kd_19.12c", 0x20000, 0x20000, CRC(92941b80) SHA1(5fa7c2793e6febee54a83042d118ddd4f2b7d127) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "kd29b.1a",     0x0000, 0x0117, CRC(6b892f82) SHA1(c0b5c285c45bcc8d885681efa8e0c0e564142684) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-3 */
ROM_START( kodu )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "kdu_30b.11e", 0x00000, 0x20000, CRC(825817f9) SHA1(250f61effcbe59f8b70baaf26eb8aef419fed66b) )
	ROM_LOAD16_BYTE( "kdu_37b.11f", 0x00001, 0x20000, CRC(d2422dfb) SHA1(6e369a62012f3c480755b700d4d4f4c112c79483) )
	ROM_LOAD16_BYTE( "kdu_31b.12e", 0x40000, 0x20000, CRC(9af36039) SHA1(f2645178a042689a387f916b4ecd7d1d859d758a) )
	ROM_LOAD16_BYTE( "kdu_38b.12f", 0x40001, 0x20000, CRC(be8405a1) SHA1(8d4f9a0489dc4b2971b20170713284151bc10eb7) )
	ROM_LOAD16_BYTE( "kdu_28.9e",   0x80000, 0x20000, CRC(9367bcd9) SHA1(8243b4b9bb9756f3fa726717e19a166cb2f5b50a) )    // == kd_28.9e
	ROM_LOAD16_BYTE( "kdu_35.9f",   0x80001, 0x20000, CRC(4ca6a48a) SHA1(9d440ecd8d2d0e293fecf64ca3915252b94e7aef) )    // == kd_35.9f
	ROM_LOAD16_BYTE( "kdu_29.10e",  0xc0000, 0x20000, CRC(0360fa72) SHA1(274769c8717a874397cf37369e3ef80a682d9ef2) )    // == kd_29.10e
	ROM_LOAD16_BYTE( "kdu_36a.10f", 0xc0001, 0x20000, CRC(95a3cef8) SHA1(9b75c1ed0eafacc230197ffd9b81e0c8f4f2c464) )    // == kd_36a.10f

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "kd-5m.4a", 0x000000, 0x80000, CRC(e45b8701) SHA1(604e39e455e81695ee4f899f102d0bcd789cedd0) )  // in "2" socket
	ROM_LOAD64_WORD( "kd-7m.6a", 0x000002, 0x80000, CRC(a7750322) SHA1(3c583496a53cd64edf377db35f7f40f02b59b7e7) )  // in "4" socket
	ROM_LOAD64_WORD( "kd-1m.3a", 0x000004, 0x80000, CRC(5f74bf78) SHA1(b7c43eea9bf77a0fb571dcd53f8be719e6655fd9) )  // in "1" socket
	ROM_LOAD64_WORD( "kd-3m.5a", 0x000006, 0x80000, CRC(5e5303bf) SHA1(d9f90b898ffdf4398b2bbeb48247f06f728e7c00) )  // in "3" socket
	ROM_LOAD64_WORD( "kd-6m.4c", 0x200000, 0x80000, CRC(113358f3) SHA1(9d98eafa74a046f65bf3847fe1d88ea1b0c82b0c) )  // in "11" socket
	ROM_LOAD64_WORD( "kd-8m.6c", 0x200002, 0x80000, CRC(38853c44) SHA1(a6e552fb0138a76a7878b90d202904e2b44ae7ec) )  // in "13" socket
	ROM_LOAD64_WORD( "kd-2m.3c", 0x200004, 0x80000, CRC(9ef36604) SHA1(b42ca0a910b65e1e7bb6e7d734e853ce67e821bf) )  // in "10" socket
	ROM_LOAD64_WORD( "kd-4m.5c", 0x200006, 0x80000, CRC(402b9b4f) SHA1(4c11976976eadf1ad293b31b0a4d047d05032b06) )  // in "12" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "kd_09.12a", 0x00000, 0x08000, CRC(bac6ec26) SHA1(6cbb6d55660150ae3f5270e023328275ee1bbf50) ) // == kd_9.12a
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "kd_18.11c", 0x00000, 0x20000, CRC(4c63181d) SHA1(270f27534a95cb0be3ff3f9ca71c502320d8090b) )
	ROM_LOAD( "kd_19.12c", 0x20000, 0x20000, CRC(92941b80) SHA1(5fa7c2793e6febee54a83042d118ddd4f2b7d127) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "kd29b.1a",     0x0000, 0x0117, CRC(6b892f82) SHA1(c0b5c285c45bcc8d885681efa8e0c0e564142684) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-3 */
ROM_START( kodj )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "kdj_30a.11e",  0x00000, 0x20000, CRC(ebc788ad) SHA1(f4a297e409fcdbb2c15a13b39a16e4a83e7e060b) )
	ROM_LOAD16_BYTE( "kdj_37a.11f",  0x00001, 0x20000, CRC(e55c3529) SHA1(a5254895499a53b4fbaac6fd50464b9e08175b8d) )
	ROM_LOAD16_BYTE( "kdj_31a.12e",  0x40000, 0x20000, CRC(c710d722) SHA1(a2e9b84d3e7d835a910ab9f584bdc64c2559995a) )   // == kde_31a.12e
	ROM_LOAD16_BYTE( "kdj_38a.12f",  0x40001, 0x20000, CRC(57d6ed3a) SHA1(a47da5068723c8e16ed458fbfa3e3db57b32d87d) )   // == kde_38a.12f
	ROM_LOAD16_WORD_SWAP("kd_33.6f", 0x80000, 0x80000, CRC(9bd7ad4b) SHA1(7bece5d408fd13116bd5518014b632ecc9a2feaa) )   // incorrect label, could be kdj_33a, kdj_33, kd_33a

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "kd_06.8a",  0x000000, 0x80000, CRC(e45b8701) SHA1(604e39e455e81695ee4f899f102d0bcd789cedd0) ) // == kd-5m.4a
	ROM_LOAD64_WORD( "kd_08.10a", 0x000002, 0x80000, CRC(a7750322) SHA1(3c583496a53cd64edf377db35f7f40f02b59b7e7) ) // == kd-7m.6a
	ROM_LOAD64_WORD( "kd_05.7a",  0x000004, 0x80000, CRC(5f74bf78) SHA1(b7c43eea9bf77a0fb571dcd53f8be719e6655fd9) ) // == kd-1m.3a
	ROM_LOAD64_WORD( "kd_07.9a",  0x000006, 0x80000, CRC(5e5303bf) SHA1(d9f90b898ffdf4398b2bbeb48247f06f728e7c00) ) // == kd-3m.5a
	ROM_LOAD64_WORD( "kd_15.8c",  0x200000, 0x80000, CRC(113358f3) SHA1(9d98eafa74a046f65bf3847fe1d88ea1b0c82b0c) ) // == kd-6m.4c
	ROM_LOAD64_WORD( "kd_17.10c", 0x200002, 0x80000, CRC(38853c44) SHA1(a6e552fb0138a76a7878b90d202904e2b44ae7ec) ) // == kd-8m.6c
	ROM_LOAD64_WORD( "kd_14.7c",  0x200004, 0x80000, CRC(9ef36604) SHA1(b42ca0a910b65e1e7bb6e7d734e853ce67e821bf) ) // == kd-2m.3c
	ROM_LOAD64_WORD( "kd_16.9c",  0x200006, 0x80000, CRC(402b9b4f) SHA1(4c11976976eadf1ad293b31b0a4d047d05032b06) ) // == kd-4m.5c

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "kd_09.12a",  0x00000, 0x08000, CRC(bac6ec26) SHA1(6cbb6d55660150ae3f5270e023328275ee1bbf50) )    // == kd_9.12a
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "kd_18.11c",  0x00000, 0x20000, CRC(4c63181d) SHA1(270f27534a95cb0be3ff3f9ca71c502320d8090b) )
	ROM_LOAD( "kd_19.12c",  0x20000, 0x20000, CRC(92941b80) SHA1(5fa7c2793e6febee54a83042d118ddd4f2b7d127) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "kd29b.1a",     0x0000, 0x0117, CRC(6b892f82) SHA1(c0b5c285c45bcc8d885681efa8e0c0e564142684) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 89625B-1 */
/* Note that this set is equivalent to kodj, but each 4Mbit EPROM is replaced by 4 1Mbit EPROMs. */
ROM_START( kodja )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "kdj_36a.12f", 0x00000, 0x20000, CRC(ebc788ad) SHA1(f4a297e409fcdbb2c15a13b39a16e4a83e7e060b) )    // == kdj_30a.11e
	ROM_LOAD16_BYTE( "kdj_42a.12h", 0x00001, 0x20000, CRC(e55c3529) SHA1(a5254895499a53b4fbaac6fd50464b9e08175b8d) )    // == kdj_37a.11f
	ROM_LOAD16_BYTE( "kdj_37a.13f", 0x40000, 0x20000, CRC(c710d722) SHA1(a2e9b84d3e7d835a910ab9f584bdc64c2559995a) )    // == kdj_31a.12e
	ROM_LOAD16_BYTE( "kdj_43a.13h", 0x40001, 0x20000, CRC(57d6ed3a) SHA1(a47da5068723c8e16ed458fbfa3e3db57b32d87d) )    // == kdj_38a.12f
	ROM_LOAD16_BYTE( "kd_34.10f",   0x80000, 0x20000, CRC(9367bcd9) SHA1(8243b4b9bb9756f3fa726717e19a166cb2f5b50a) )    // == kd_33.6f
	ROM_LOAD16_BYTE( "kd_40.10h",   0x80001, 0x20000, CRC(4ca6a48a) SHA1(9d440ecd8d2d0e293fecf64ca3915252b94e7aef) )    // == kd_33.6f
	ROM_LOAD16_BYTE( "kd_35.11f",   0xc0000, 0x20000, CRC(0360fa72) SHA1(274769c8717a874397cf37369e3ef80a682d9ef2) )    // == kd_33.6f
	ROM_LOAD16_BYTE( "kd_41a.11h",  0xc0001, 0x20000, CRC(95a3cef8) SHA1(9b75c1ed0eafacc230197ffd9b81e0c8f4f2c464) )    // == kd_33.6f

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_BYTE( "kd_9.4b",   0x000000, 0x20000, CRC(401a98e3) SHA1(b762d0f0e38ad33b5135646a107c1d4279c02193) ) // == kd_06.8a
	ROM_LOAD64_BYTE( "kd_1.4a",   0x000001, 0x20000, CRC(5894399a) SHA1(ee0bff29d313b4d5a318bc7345cc356d6e7318fc) ) // == kd_06.8a
	ROM_LOAD64_BYTE( "kd_13.9b",  0x000002, 0x20000, CRC(b6685131) SHA1(c1fa66cb695cfcb61e8495031ce18e4c6736b561) ) // == kd_08.10a
	ROM_LOAD64_BYTE( "kd_5.9a",   0x000003, 0x20000, CRC(c29b9ab3) SHA1(3ffa906d22563c9ad29b9ba4cd22b4ce5605b66a) ) // == kd_08.10a
	ROM_LOAD64_BYTE( "kd_24.5e",  0x000004, 0x20000, CRC(97008fdb) SHA1(150a7907f48dede2de0c40f1bea46e95d2e31e9c) ) // == kd_05.7a
	ROM_LOAD64_BYTE( "kd_17.5c",  0x000005, 0x20000, CRC(dc9a83d3) SHA1(6ceb5e6b11e7a57933c03fb308e1c6a850cf5ac7) ) // == kd_05.7a
	ROM_LOAD64_BYTE( "kd_38.8h",  0x000006, 0x20000, CRC(9c3dd2d1) SHA1(3ab77954fbde45797fcb1c5d8a500b5ec2673e5f) ) // == kd_07.9a
	ROM_LOAD64_BYTE( "kd_32.8f",  0x000007, 0x20000, CRC(1b2a802a) SHA1(f9b9e6da6a49aaf7c88b7b806c273b464a584a7b) ) // == kd_07.9a
	ROM_LOAD64_BYTE( "kd_10.5b",  0x100000, 0x20000, CRC(e788ae96) SHA1(1c257a4c20842b8750e3ba3cf78adad0bc5e6f2c) ) // == kd_06.8a
	ROM_LOAD64_BYTE( "kd_2.5a",   0x100001, 0x20000, CRC(b022e3e3) SHA1(a1e3700638e99677d4ee9aea5a250eb2380413c2) ) // == kd_06.8a
	ROM_LOAD64_BYTE( "kd_14.10b", 0x100002, 0x20000, CRC(4840c5ef) SHA1(fcd7ee778ab4519887bd37ced5e04f7aa8748e5e) ) // == kd_08.10a
	ROM_LOAD64_BYTE( "kd_6.10a",  0x100003, 0x20000, CRC(519faee4) SHA1(038afb9d2df872a0b45a7a6a43f7bb1389e8539a) ) // == kd_08.10a
	ROM_LOAD64_BYTE( "kd_25.7e",  0x100004, 0x20000, CRC(5d0fa853) SHA1(81e69bfac5ad6909d47db0927b7387bd392b4ef3) ) // == kd_05.7a
	ROM_LOAD64_BYTE( "kd_18.7c",  0x100005, 0x20000, CRC(6ad3b2bb) SHA1(c8d3bd2ce6305772224b5419301b59b9e78e7d44) ) // == kd_05.7a
	ROM_LOAD64_BYTE( "kd_39.9h",  0x100006, 0x20000, CRC(d7920213) SHA1(ac7de3f6c8503e06ae27f7db43bf9884f244bf7a) ) // == kd_07.9a
	ROM_LOAD64_BYTE( "kd_33.9f",  0x100007, 0x20000, CRC(65c2bed6) SHA1(932be92a418a2d3960f0b071eb5ffe6dae60d933) ) // == kd_07.9a
	ROM_LOAD64_BYTE( "kd_11.7b",  0x200000, 0x20000, CRC(147e3310) SHA1(e371db6c6621d09c43325edf9e3e40777afb830c) ) // == kd_15.8c
	ROM_LOAD64_BYTE( "kd_3.7a",   0x200001, 0x20000, CRC(5d18bc83) SHA1(49191e1073cdfb32272e6daeddedc4b309cf9fe6) ) // == kd_15.8c
	ROM_LOAD64_BYTE( "kd_15.11b", 0x200002, 0x20000, CRC(57359746) SHA1(2f83b77e6ebbceffab10653018a1b54a91d39a3f) ) // == kd_17.10c
	ROM_LOAD64_BYTE( "kd_7.11a",  0x200003, 0x20000, CRC(7fe03079) SHA1(be3be96d63a53cd8b6dc67eadc718c4b3b182123) ) // == kd_17.10c
	ROM_LOAD64_BYTE( "kd_26.8e",  0x200004, 0x20000, CRC(57e5fab5) SHA1(e665ab581372b48b6a7a494fd65ed6c807b5c00a) ) // == kd_14.7c
	ROM_LOAD64_BYTE( "kd_19.8c",  0x200005, 0x20000, CRC(b1f30f7c) SHA1(80308091a0af6ca46011a9174838c4d9dbeb96ee) ) // == kd_14.7c
	ROM_LOAD64_BYTE( "kd_28.10e", 0x200006, 0x20000, CRC(3a424135) SHA1(05d77253f454d69553a39a5dca0798f6ccea6da0) ) // == kd_16.9c
	ROM_LOAD64_BYTE( "kd_21.10c", 0x200007, 0x20000, CRC(ce10d2c3) SHA1(b694325b370d451cb3bcfe7d753c53ef68b83276) ) // == kd_16.9c
	ROM_LOAD64_BYTE( "kd_12.8b",  0x300000, 0x20000, CRC(a6042aa2) SHA1(daa4e52574b53d125414401ad0fca6eea36abe64) ) // == kd_15.8c
	ROM_LOAD64_BYTE( "kd_4.8a",   0x300001, 0x20000, CRC(0ce0ba30) SHA1(a19049b5fc58921452bf7b69d2d86d0faa5362bf) ) // == kd_15.8c
	ROM_LOAD64_BYTE( "kd_16.12b", 0x300002, 0x20000, CRC(63dcb7e0) SHA1(1524321f31813452aabfef133fb64fd0cef165cf) ) // == kd_17.10c
	ROM_LOAD64_BYTE( "kd_8.12a",  0x300003, 0x20000, CRC(c69b77ae) SHA1(31cacace828d4e376347ecd896f3454f79285a8b) ) // == kd_17.10c
	ROM_LOAD64_BYTE( "kd_27.9e",  0x300004, 0x20000, CRC(40d7bfed) SHA1(924d3f981d85434d2a22d4ede45131720d7521c0) ) // == kd_14.7c
	ROM_LOAD64_BYTE( "kd_20.9c",  0x300005, 0x20000, CRC(01c1f399) SHA1(ea25d4efc9e538ee8ed09acb6e467c5b444bf728) ) // == kd_14.7c
	ROM_LOAD64_BYTE( "kd_29.11e", 0x300006, 0x20000, CRC(a1eeac03) SHA1(fdec33ec0becc4badf89c9019f3217c22e54e842) ) // == kd_16.9c
	ROM_LOAD64_BYTE( "kd_22.11c", 0x300007, 0x20000, CRC(5ade98eb) SHA1(5d2474864bf0e41e89fe44c69025101b8ef2b482) ) // == kd_16.9c

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "kd_23.13b",  0x00000, 0x08000, CRC(bac6ec26) SHA1(6cbb6d55660150ae3f5270e023328275ee1bbf50) )    // == kd_09.12a
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "kd_30.12c",  0x00000, 0x20000, CRC(4c63181d) SHA1(270f27534a95cb0be3ff3f9ca71c502320d8090b) )    // == kd_18.11c
	ROM_LOAD( "kd_31.13c",  0x20000, 0x20000, CRC(92941b80) SHA1(5fa7c2793e6febee54a83042d118ddd4f2b7d127) )    // == kd_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "kd22b.1a",     0x0000, 0x0117, CRC(bd1a6035) SHA1(16f36373eee5823733b0bbf01aafc4a07e3d1667) )
	ROM_LOAD( "iob1.12e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( captcomm )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "cce_23f.8f", 0x000000, 0x80000, CRC(42c814c5) SHA1(60e6ae6b8a89cdaa1abf1749c60a5e3b3972e1ab) )
	ROM_LOAD16_WORD_SWAP( "cc_22f.7f",  0x080000, 0x80000, CRC(0fd34195) SHA1(fb2b9a53af43507f13c4f94eaebbf0b538b2e754) )
	ROM_LOAD16_BYTE( "cc_24f.9e",       0x100000, 0x20000, CRC(3a794f25) SHA1(7f3722a4ef0c1d7acb73e6bac9dd6ae7b35e6374) )
	ROM_LOAD16_BYTE( "cc_28f.9f",       0x100001, 0x20000, CRC(fc3c2906) SHA1(621c3b79b6fdea1665bb316eb539e5916e890656) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "cc-5m.3a",  0x000000, 0x80000, CRC(7261d8ba) SHA1(4b66292e42d20d0b79a756f0e445492ddb9c6bbc) ) // in "1" socket
	ROM_LOAD64_WORD( "cc-7m.5a",  0x000002, 0x80000, CRC(6a60f949) SHA1(87391ff92abaf3e451f70d789a938cffbd1fd222) ) // in "3" socket
	ROM_LOAD64_WORD( "cc-1m.4a",  0x000004, 0x80000, CRC(00637302) SHA1(2c554b59cceec2de67a9a4bc6281fe846d3c8cd2) ) // in "2" socket
	ROM_LOAD64_WORD( "cc-3m.6a",  0x000006, 0x80000, CRC(cc87cf61) SHA1(7fb1f49494cc1a08aded20754bb0cefb1c323198) ) // in "4" socket
	ROM_LOAD64_WORD( "cc-6m.7a",  0x200000, 0x80000, CRC(28718bed) SHA1(dfdc4dd14dc609783bad94d608a9e9b137dea944) ) // in "5" socket
	ROM_LOAD64_WORD( "cc-8m.9a",  0x200002, 0x80000, CRC(d4acc53a) SHA1(d03282ebbde362e679cc97f772aa9baf163d7606) ) // in "7" socket
	ROM_LOAD64_WORD( "cc-2m.8a",  0x200004, 0x80000, CRC(0c69f151) SHA1(a170b8e568439e4a26d84376d53560e4248e4e2f) ) // in "6" socket
	ROM_LOAD64_WORD( "cc-4m.10a", 0x200006, 0x80000, CRC(1f9ebb97) SHA1(023d00cb7b6a52d1b29e2052abe08ef34cb0c55c) ) // in "8" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "cc_09.11a",  0x00000, 0x08000, CRC(698e8b58) SHA1(b7a3d905a7ed2c430426ca2e185e3d7e75e752a1) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "cc_18.11c",  0x00000, 0x20000, CRC(6de2c2db) SHA1(9a1eaba8d104f59a5e61f89679bb5de0c0c64364) )
	ROM_LOAD( "cc_19.12c",  0x20000, 0x20000, CRC(b99091ae) SHA1(b19197c7ad3aeaf5f41c26bf853b0c9b502ecfca) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "cc63b.1a",     0x0000, 0x0117, CRC(cae8f0f9) SHA1(eadbd45e184195b2d170cd71a68e5caed64b69f7) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "ccprg1.11d",   0x0000, 0x0117, CRC(e1c225c4) SHA1(97146451ca9aa3cecd443cc6881151ed8df47fbf) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( captcommr1 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "cce_23d.8f", 0x000000, 0x80000, CRC(19c58ece) SHA1(6e23e87db29b2c5698b7cead99d1106a2e190648) )
	ROM_LOAD16_WORD_SWAP( "cc_22d.7f",  0x080000, 0x80000, CRC(a91949b7) SHA1(c027af89cd8f6bd3aaed61114582322c42e0c74f) )
	ROM_LOAD16_BYTE( "cc_24d.9e",       0x100000, 0x20000, CRC(680e543f) SHA1(cfa963ab6329f615807db213bf53841860ed3149) )
	ROM_LOAD16_BYTE( "cc_28d.9f",       0x100001, 0x20000, CRC(8820039f) SHA1(d68ce0b34ade75b8c5214168b2b1e0cdff45cd52) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "cc-5m.3a",  0x000000, 0x80000, CRC(7261d8ba) SHA1(4b66292e42d20d0b79a756f0e445492ddb9c6bbc) ) // in "1" socket
	ROM_LOAD64_WORD( "cc-7m.5a",  0x000002, 0x80000, CRC(6a60f949) SHA1(87391ff92abaf3e451f70d789a938cffbd1fd222) ) // in "3" socket
	ROM_LOAD64_WORD( "cc-1m.4a",  0x000004, 0x80000, CRC(00637302) SHA1(2c554b59cceec2de67a9a4bc6281fe846d3c8cd2) ) // in "2" socket
	ROM_LOAD64_WORD( "cc-3m.6a",  0x000006, 0x80000, CRC(cc87cf61) SHA1(7fb1f49494cc1a08aded20754bb0cefb1c323198) ) // in "4" socket
	ROM_LOAD64_WORD( "cc-6m.7a",  0x200000, 0x80000, CRC(28718bed) SHA1(dfdc4dd14dc609783bad94d608a9e9b137dea944) ) // in "5" socket
	ROM_LOAD64_WORD( "cc-8m.9a",  0x200002, 0x80000, CRC(d4acc53a) SHA1(d03282ebbde362e679cc97f772aa9baf163d7606) ) // in "7" socket
	ROM_LOAD64_WORD( "cc-2m.8a",  0x200004, 0x80000, CRC(0c69f151) SHA1(a170b8e568439e4a26d84376d53560e4248e4e2f) ) // in "6" socket
	ROM_LOAD64_WORD( "cc-4m.10a", 0x200006, 0x80000, CRC(1f9ebb97) SHA1(023d00cb7b6a52d1b29e2052abe08ef34cb0c55c) ) // in "8" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "cc_09.11a",  0x00000, 0x08000, CRC(698e8b58) SHA1(b7a3d905a7ed2c430426ca2e185e3d7e75e752a1) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "cc_18.11c",  0x00000, 0x20000, CRC(6de2c2db) SHA1(9a1eaba8d104f59a5e61f89679bb5de0c0c64364) )
	ROM_LOAD( "cc_19.12c",  0x20000, 0x20000, CRC(b99091ae) SHA1(b19197c7ad3aeaf5f41c26bf853b0c9b502ecfca) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "cc63b.1a",     0x0000, 0x0117, CRC(cae8f0f9) SHA1(eadbd45e184195b2d170cd71a68e5caed64b69f7) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "ccprg.11d",    0x0000, 0x0117, CRC(e1c225c4) SHA1(97146451ca9aa3cecd443cc6881151ed8df47fbf) )    // == ccprg1.11d

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( captcommu )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "ccu_23b.8f", 0x000000, 0x80000, CRC(03da44fd) SHA1(0bf382933b4b44082bbaf63e96acd83ab8808a34) )
	ROM_LOAD16_WORD_SWAP( "ccu_22c.7f", 0x080000, 0x80000, CRC(9b82a052) SHA1(8247fe45fea8c47072a66d6707202bcdb8c62923) )
	ROM_LOAD16_BYTE( "ccu_24b.9e",      0x100000, 0x20000, CRC(84ff99b2) SHA1(5b02c91f3d0f8fb46db9596136b683f5a22dc15f) )
	ROM_LOAD16_BYTE( "ccu_28b.9f",      0x100001, 0x20000, CRC(fbcec223) SHA1(daf484baece5b3a11f3dcabb758b8bdd736a1fb6) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "cc-5m.3a",  0x000000, 0x80000, CRC(7261d8ba) SHA1(4b66292e42d20d0b79a756f0e445492ddb9c6bbc) ) // in "1" socket
	ROM_LOAD64_WORD( "cc-7m.5a",  0x000002, 0x80000, CRC(6a60f949) SHA1(87391ff92abaf3e451f70d789a938cffbd1fd222) ) // in "3" socket
	ROM_LOAD64_WORD( "cc-1m.4a",  0x000004, 0x80000, CRC(00637302) SHA1(2c554b59cceec2de67a9a4bc6281fe846d3c8cd2) ) // in "2" socket
	ROM_LOAD64_WORD( "cc-3m.6a",  0x000006, 0x80000, CRC(cc87cf61) SHA1(7fb1f49494cc1a08aded20754bb0cefb1c323198) ) // in "4" socket
	ROM_LOAD64_WORD( "cc-6m.7a",  0x200000, 0x80000, CRC(28718bed) SHA1(dfdc4dd14dc609783bad94d608a9e9b137dea944) ) // in "5" socket
	ROM_LOAD64_WORD( "cc-8m.9a",  0x200002, 0x80000, CRC(d4acc53a) SHA1(d03282ebbde362e679cc97f772aa9baf163d7606) ) // in "7" socket
	ROM_LOAD64_WORD( "cc-2m.8a",  0x200004, 0x80000, CRC(0c69f151) SHA1(a170b8e568439e4a26d84376d53560e4248e4e2f) ) // in "6" socket
	ROM_LOAD64_WORD( "cc-4m.10a", 0x200006, 0x80000, CRC(1f9ebb97) SHA1(023d00cb7b6a52d1b29e2052abe08ef34cb0c55c) ) // in "8" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ccu_09.11a", 0x00000, 0x08000, CRC(698e8b58) SHA1(b7a3d905a7ed2c430426ca2e185e3d7e75e752a1) )    // == cc_09.11a
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ccu_18.11c", 0x00000, 0x20000, CRC(6de2c2db) SHA1(9a1eaba8d104f59a5e61f89679bb5de0c0c64364) )    // == cc_18.11c
	ROM_LOAD( "ccu_19.12c", 0x20000, 0x20000, CRC(b99091ae) SHA1(b19197c7ad3aeaf5f41c26bf853b0c9b502ecfca) )    // == cc_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "cc63b.1a",     0x0000, 0x0117, CRC(cae8f0f9) SHA1(eadbd45e184195b2d170cd71a68e5caed64b69f7) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "ccprg1.11d",   0x0000, 0x0117, CRC(e1c225c4) SHA1(97146451ca9aa3cecd443cc6881151ed8df47fbf) )    /* seen the same pcb with CCPRG.11D */

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91634B-2 */
ROM_START( captcommj )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "ccj_23f.8f", 0x000000, 0x80000, CRC(5b482b62) SHA1(c871aa1eb9ecc117c3079995d1f5212193bd2e12) )
	ROM_LOAD16_WORD_SWAP( "ccj_22f.7f", 0x080000, 0x80000, CRC(0fd34195) SHA1(fb2b9a53af43507f13c4f94eaebbf0b538b2e754) )   // == cc_22f.7f
	ROM_LOAD16_BYTE( "ccj_24f.9e",      0x100000, 0x20000, CRC(3a794f25) SHA1(7f3722a4ef0c1d7acb73e6bac9dd6ae7b35e6374) )   // == cc_24f.9e
	ROM_LOAD16_BYTE( "ccj_28f.9f",      0x100001, 0x20000, CRC(fc3c2906) SHA1(621c3b79b6fdea1665bb316eb539e5916e890656) )   // == cc_28f.9f

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "cc_01.3a",  0x000000, 0x80000, CRC(7261d8ba) SHA1(4b66292e42d20d0b79a756f0e445492ddb9c6bbc) ) // == cc-5m.3a
	ROM_LOAD64_WORD( "cc_02.4a",  0x000002, 0x80000, CRC(6a60f949) SHA1(87391ff92abaf3e451f70d789a938cffbd1fd222) ) // == cc-7m.5a
	ROM_LOAD64_WORD( "cc_03.5a",  0x000004, 0x80000, CRC(00637302) SHA1(2c554b59cceec2de67a9a4bc6281fe846d3c8cd2) ) // == cc-1m.4a
	ROM_LOAD64_WORD( "cc_04.6a",  0x000006, 0x80000, CRC(cc87cf61) SHA1(7fb1f49494cc1a08aded20754bb0cefb1c323198) ) // == cc-3m.6a
	ROM_LOAD64_WORD( "cc_05.7a",  0x200000, 0x80000, CRC(28718bed) SHA1(dfdc4dd14dc609783bad94d608a9e9b137dea944) ) // == cc-6m.7a
	ROM_LOAD64_WORD( "cc_06.8a",  0x200002, 0x80000, CRC(d4acc53a) SHA1(d03282ebbde362e679cc97f772aa9baf163d7606) ) // == cc-8m.9a
	ROM_LOAD64_WORD( "cc_07.9a",  0x200004, 0x80000, CRC(0c69f151) SHA1(a170b8e568439e4a26d84376d53560e4248e4e2f) ) // == cc-2m.8a
	ROM_LOAD64_WORD( "cc_08.10a", 0x200006, 0x80000, CRC(1f9ebb97) SHA1(023d00cb7b6a52d1b29e2052abe08ef34cb0c55c) ) // == cc-4m.10a

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ccj_09.12a", 0x00000, 0x08000, CRC(698e8b58) SHA1(b7a3d905a7ed2c430426ca2e185e3d7e75e752a1) )    // == cc_09.11a
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ccj_18.11c", 0x00000, 0x20000, CRC(6de2c2db) SHA1(9a1eaba8d104f59a5e61f89679bb5de0c0c64364) )    // == cc_18.11c
	ROM_LOAD( "ccj_19.12c", 0x20000, 0x20000, CRC(b99091ae) SHA1(b19197c7ad3aeaf5f41c26bf853b0c9b502ecfca) )    // == cc_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "cc63b.1a",     0x0000, 0x0117, CRC(cae8f0f9) SHA1(eadbd45e184195b2d170cd71a68e5caed64b69f7) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "ccprg1.11d",   0x0000, 0x0117, CRC(e1c225c4) SHA1(97146451ca9aa3cecd443cc6881151ed8df47fbf) )    /* seen the same pcb with CCPRG.11D */

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91634B-2 */
ROM_START( captcommjr1 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "ccj_23b.8f", 0x000000, 0x80000, CRC(e2a2d80e) SHA1(ec3c455974b77cd2e4357546aea4cb25690a041f) )
	ROM_LOAD16_WORD_SWAP( "ccj_22c.7f", 0x080000, 0x80000, CRC(9b82a052) SHA1(8247fe45fea8c47072a66d6707202bcdb8c62923) )   // == ccu_22c.7f
	ROM_LOAD16_BYTE( "ccj_24b.9e",      0x100000, 0x20000, CRC(84ff99b2) SHA1(5b02c91f3d0f8fb46db9596136b683f5a22dc15f) )   // == ccu_24b.9e
	ROM_LOAD16_BYTE( "ccj_28b.9f",      0x100001, 0x20000, CRC(fbcec223) SHA1(daf484baece5b3a11f3dcabb758b8bdd736a1fb6) )   // == ccu_28b.9f

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "cc_01.3a",  0x000000, 0x80000, CRC(7261d8ba) SHA1(4b66292e42d20d0b79a756f0e445492ddb9c6bbc) ) // == cc-5m.3a
	ROM_LOAD64_WORD( "cc_02.4a",  0x000002, 0x80000, CRC(6a60f949) SHA1(87391ff92abaf3e451f70d789a938cffbd1fd222) ) // == cc-7m.5a
	ROM_LOAD64_WORD( "cc_03.5a",  0x000004, 0x80000, CRC(00637302) SHA1(2c554b59cceec2de67a9a4bc6281fe846d3c8cd2) ) // == cc-1m.4a
	ROM_LOAD64_WORD( "cc_04.6a",  0x000006, 0x80000, CRC(cc87cf61) SHA1(7fb1f49494cc1a08aded20754bb0cefb1c323198) ) // == cc-3m.6a
	ROM_LOAD64_WORD( "cc_05.7a",  0x200000, 0x80000, CRC(28718bed) SHA1(dfdc4dd14dc609783bad94d608a9e9b137dea944) ) // == cc-6m.7a
	ROM_LOAD64_WORD( "cc_06.8a",  0x200002, 0x80000, CRC(d4acc53a) SHA1(d03282ebbde362e679cc97f772aa9baf163d7606) ) // == cc-8m.9a
	ROM_LOAD64_WORD( "cc_07.9a",  0x200004, 0x80000, CRC(0c69f151) SHA1(a170b8e568439e4a26d84376d53560e4248e4e2f) ) // == cc-2m.8a
	ROM_LOAD64_WORD( "cc_08.10a", 0x200006, 0x80000, CRC(1f9ebb97) SHA1(023d00cb7b6a52d1b29e2052abe08ef34cb0c55c) ) // == cc-4m.10a

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "ccj_09.12a", 0x00000, 0x08000, CRC(698e8b58) SHA1(b7a3d905a7ed2c430426ca2e185e3d7e75e752a1) )    // == cc_09.11a
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "ccj_18.11c", 0x00000, 0x20000, CRC(6de2c2db) SHA1(9a1eaba8d104f59a5e61f89679bb5de0c0c64364) )    // == cc_18.11c
	ROM_LOAD( "ccj_19.12c", 0x20000, 0x20000, CRC(b99091ae) SHA1(b19197c7ad3aeaf5f41c26bf853b0c9b502ecfca) )    // == cc_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "cc63b.1a",     0x0000, 0x0117, CRC(cae8f0f9) SHA1(eadbd45e184195b2d170cd71a68e5caed64b69f7) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "ccprg.11d",    0x0000, 0x0117, CRC(e1c225c4) SHA1(97146451ca9aa3cecd443cc6881151ed8df47fbf) )    // == ccprg1.11d

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* Captain Commando bootleg
 - there are 2 dumps of this, one has bad (half size) gfx roms tho, otherwise identical

ROMs from a Captain Commando bootleg PCB

Large single PCB containing.....
68000 @ 10MHz
Z80 @ 3.579545MHz
YM2151 @ 3.579545MHz
M6295 @ 1MHz (16/16), pin 7 HIGH
xtals 10MHz, 3.579545MHz, 16MHz
8-position DSWs x3
6116 (2kx8) SRAM x6
62256 (32kx8) SRAM x6
681000 (128kx8) SRAM x2
a few PALs
LOTS of logic
no special chips
no custom chips
no PLD/CPLD/FPGA
no PROMs

*/

ROM_START( captcommb )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "25.bin",        0x000000, 0x80000, CRC(cb71ed7a) SHA1(84f76b4861a3c7a59e67f38777f2d68749f19337) )
	ROM_LOAD16_BYTE( "27.bin",        0x000001, 0x80000, CRC(47cb2e87) SHA1(8a990a3a122045b50dd73d2e7b02fe60ab9af0a3) )
	ROM_LOAD16_BYTE( "24.bin",        0x100000, 0x40000, CRC(79794279) SHA1(5a43a4cef6653454ba9a81f2dd7f3f91c8a3354c) )
	ROM_LOAD16_BYTE( "26.bin",        0x100001, 0x40000, CRC(b01077ba) SHA1(0698fbfca7beea8e6a676aa19fcbf5ddea3defb1) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_BYTE( "c91e-01.bin", 0x000000, 0x40000, CRC(f863071c) SHA1(c5154c4001f8e447623f9d71bf3e68a16f039e8f) )
	ROM_CONTINUE(                   0x000004, 0x40000)
	ROM_CONTINUE(                   0x200000, 0x40000)
	ROM_CONTINUE(                   0x200004, 0x40000)
	ROM_LOAD64_BYTE( "c91e-02.bin", 0x000001, 0x40000, CRC(4b03c308) SHA1(d28d3ebba2571bea56b057cb3e09315a17d78b42) )
	ROM_CONTINUE(                   0x000005, 0x40000)
	ROM_CONTINUE(                   0x200001, 0x40000)
	ROM_CONTINUE(                   0x200005, 0x40000)
	ROM_LOAD64_BYTE( "c91e-03.bin", 0x000002, 0x40000, CRC(3383ea96) SHA1(2a583d87c6d80919c97640f6f2e756cecc3e38ec) )
	ROM_CONTINUE(                   0x000006, 0x40000)
	ROM_CONTINUE(                   0x200002, 0x40000)
	ROM_CONTINUE(                   0x200006, 0x40000)
	ROM_LOAD64_BYTE( "c91e-04.bin", 0x000003, 0x40000, CRC(b8e1f4cf) SHA1(6686df700c7ce49fe4ac7007aa4d622645e0e348) )
	ROM_CONTINUE(                   0x000007, 0x40000)
	ROM_CONTINUE(                   0x200003, 0x40000)
	ROM_CONTINUE(                   0x200007, 0x40000)

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "l.bin",     0x00000, 0x08000, CRC(698e8b58) SHA1(b7a3d905a7ed2c430426ca2e185e3d7e75e752a1) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "c91e-05.bin",     0x00000, 0x40000, CRC(096115fb) SHA1(b496550f61b3d4b54ba43522d31efd0b09057493) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( knights )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "kr_23e.8f", 0x00000, 0x80000, CRC(1b3997eb) SHA1(724b68eff319fcdf0dd3bc1eb6662996c1f6ecd9) )
	ROM_LOAD16_WORD_SWAP( "kr_22.7f",  0x80000, 0x80000, CRC(d0b671a9) SHA1(9865472c5fc3f617345e23b5de5a9ba177945b5a) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "kr-5m.3a",  0x000000, 0x80000, CRC(9e36c1a4) SHA1(772daae74e119371dfb76fde9775bda78a8ba125) ) // in "1" socket
	ROM_LOAD64_WORD( "kr-7m.5a",  0x000002, 0x80000, CRC(c5832cae) SHA1(a188cf401cd3a2909b377d3059f14d22ec3b0643) ) // in "3" socket
	ROM_LOAD64_WORD( "kr-1m.4a",  0x000004, 0x80000, CRC(f095be2d) SHA1(0427d1574062f277a9d04440019d5638b05de561) ) // in "2" socket
	ROM_LOAD64_WORD( "kr-3m.6a",  0x000006, 0x80000, CRC(179dfd96) SHA1(b1844e69da7ab13474da569978d5b47deb8eb2be) ) // in "4" socket
	ROM_LOAD64_WORD( "kr-6m.7a",  0x200000, 0x80000, CRC(1f4298d2) SHA1(4b162a7f649b0bcd676f8ca0c5eee9a1250d6452) ) // in "5" socket
	ROM_LOAD64_WORD( "kr-8m.9a",  0x200002, 0x80000, CRC(37fa8751) SHA1(b88b39d1f08621f15a5620095aef998346fa9891) ) // in "7" socket
	ROM_LOAD64_WORD( "kr-2m.8a",  0x200004, 0x80000, CRC(0200bc3d) SHA1(c900b1be2b4e49b951e5c1e3fd1e19d21b82986e) ) // in "6" socket
	ROM_LOAD64_WORD( "kr-4m.10a", 0x200006, 0x80000, CRC(0bb2b4e7) SHA1(983b800925d58e4aeb4e5105f93ed5faf66d009c) ) // in "8" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "kr_09.11a",  0x00000, 0x08000, CRC(5e44d9ee) SHA1(47a7503321be8d52b5c44af838e3bb82ee15a415) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "kr_18.11c",  0x00000, 0x20000, CRC(da69d15f) SHA1(9616207e693bae85705f786cef60b9f6951b5067) )
	ROM_LOAD( "kr_19.12c",  0x20000, 0x20000, CRC(bfc654e9) SHA1(01b3d92e4dedf55ea3933d387c7ddb9ba2549773) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "kr63b.1a",     0x0000, 0x0117, CRC(fd5b6522) SHA1(5e6ebb2d736415402920a30d331d4b6dab557e5e) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( knightsu )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "kr_23u.8f", 0x00000, 0x80000, CRC(252bc2ba) SHA1(4f4901c253bd64bbe68ea01994ae663fe2ccd056) )
	ROM_LOAD16_WORD_SWAP( "kr_22.7f",  0x80000, 0x80000, CRC(d0b671a9) SHA1(9865472c5fc3f617345e23b5de5a9ba177945b5a) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "kr-5m.3a",  0x000000, 0x80000, CRC(9e36c1a4) SHA1(772daae74e119371dfb76fde9775bda78a8ba125) ) // in "1" socket
	ROM_LOAD64_WORD( "kr-7m.5a",  0x000002, 0x80000, CRC(c5832cae) SHA1(a188cf401cd3a2909b377d3059f14d22ec3b0643) ) // in "3" socket
	ROM_LOAD64_WORD( "kr-1m.4a",  0x000004, 0x80000, CRC(f095be2d) SHA1(0427d1574062f277a9d04440019d5638b05de561) ) // in "2" socket
	ROM_LOAD64_WORD( "kr-3m.6a",  0x000006, 0x80000, CRC(179dfd96) SHA1(b1844e69da7ab13474da569978d5b47deb8eb2be) ) // in "4" socket
	ROM_LOAD64_WORD( "kr-6m.7a",  0x200000, 0x80000, CRC(1f4298d2) SHA1(4b162a7f649b0bcd676f8ca0c5eee9a1250d6452) ) // in "5" socket
	ROM_LOAD64_WORD( "kr-8m.9a",  0x200002, 0x80000, CRC(37fa8751) SHA1(b88b39d1f08621f15a5620095aef998346fa9891) ) // in "7" socket
	ROM_LOAD64_WORD( "kr-2m.8a",  0x200004, 0x80000, CRC(0200bc3d) SHA1(c900b1be2b4e49b951e5c1e3fd1e19d21b82986e) ) // in "6" socket
	ROM_LOAD64_WORD( "kr-4m.10a", 0x200006, 0x80000, CRC(0bb2b4e7) SHA1(983b800925d58e4aeb4e5105f93ed5faf66d009c) ) // in "8" socket

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "kr_09.11a",  0x00000, 0x08000, CRC(5e44d9ee) SHA1(47a7503321be8d52b5c44af838e3bb82ee15a415) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "kr_18.11c",  0x00000, 0x20000, CRC(da69d15f) SHA1(9616207e693bae85705f786cef60b9f6951b5067) )
	ROM_LOAD( "kr_19.12c",  0x20000, 0x20000, CRC(bfc654e9) SHA1(01b3d92e4dedf55ea3933d387c7ddb9ba2549773) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "kr63b.1a",     0x0000, 0x0117, CRC(fd5b6522) SHA1(5e6ebb2d736415402920a30d331d4b6dab557e5e) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91634B-2 */
ROM_START( knightsj )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "kr_23j.8f", 0x00000, 0x80000, CRC(eae7417f) SHA1(2ec808265a9a231922e2397d7e8f3c3841a90859) )
	ROM_LOAD16_WORD_SWAP( "kr_22.7f",  0x80000, 0x80000, CRC(d0b671a9) SHA1(9865472c5fc3f617345e23b5de5a9ba177945b5a) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "kr_01.3a",  0x000000, 0x80000, CRC(9e36c1a4) SHA1(772daae74e119371dfb76fde9775bda78a8ba125) ) // == kr-5m.3a
	ROM_LOAD64_WORD( "kr_02.4a",  0x000002, 0x80000, CRC(c5832cae) SHA1(a188cf401cd3a2909b377d3059f14d22ec3b0643) ) // == kr-7m.5a
	ROM_LOAD64_WORD( "kr_03.5a",  0x000004, 0x80000, CRC(f095be2d) SHA1(0427d1574062f277a9d04440019d5638b05de561) ) // == kr-1m.4a
	ROM_LOAD64_WORD( "kr_04.6a",  0x000006, 0x80000, CRC(179dfd96) SHA1(b1844e69da7ab13474da569978d5b47deb8eb2be) ) // == kr-3m.6a
	ROM_LOAD64_WORD( "kr_05.7a",  0x200000, 0x80000, CRC(1f4298d2) SHA1(4b162a7f649b0bcd676f8ca0c5eee9a1250d6452) ) // == kr-6m.7a
	ROM_LOAD64_WORD( "kr_06.8a",  0x200002, 0x80000, CRC(37fa8751) SHA1(b88b39d1f08621f15a5620095aef998346fa9891) ) // == kr-8m.9a
	ROM_LOAD64_WORD( "kr_07.9a",  0x200004, 0x80000, CRC(0200bc3d) SHA1(c900b1be2b4e49b951e5c1e3fd1e19d21b82986e) ) // == kr-2m.8a
	ROM_LOAD64_WORD( "kr_08.10a", 0x200006, 0x80000, CRC(0bb2b4e7) SHA1(983b800925d58e4aeb4e5105f93ed5faf66d009c) ) // == kr-4m.10a

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "kr_09.12a",  0x00000, 0x08000, CRC(5e44d9ee) SHA1(47a7503321be8d52b5c44af838e3bb82ee15a415) )    // == kr_09.11a
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "kr_18.11c",  0x00000, 0x20000, CRC(da69d15f) SHA1(9616207e693bae85705f786cef60b9f6951b5067) )
	ROM_LOAD( "kr_19.12c",  0x20000, 0x20000, CRC(bfc654e9) SHA1(01b3d92e4dedf55ea3933d387c7ddb9ba2549773) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "kr63b.1a",     0x0000, 0x0117, CRC(fd5b6522) SHA1(5e6ebb2d736415402920a30d331d4b6dab557e5e) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 89625B-1 */
/* Note that this set is equivalent to knightsj, but each 4Mbit EPROM is replaced by 4 1Mbit EPROMs. */
ROM_START( knightsja )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "krj_36.12f", 0x00000, 0x20000, CRC(ad3d1a8e) SHA1(327f9e818f1500836fc549afeffbb2a3c5aafe8c) ) // == kr_23j.8f
	ROM_LOAD16_BYTE( "krj_42.12h", 0x00001, 0x20000, CRC(e694a491) SHA1(5a4d27c879c10032c49880019501de3e45ab1b35) ) // == kr_23j.8f
	ROM_LOAD16_BYTE( "krj_37.13f", 0x40000, 0x20000, CRC(85596094) SHA1(74ad294de63aa6b60aa8b885c45c3d41a07ce19a) ) // == kr_23j.8f
	ROM_LOAD16_BYTE( "krj_43.13h", 0x40001, 0x20000, CRC(9198bf8f) SHA1(aa3610600286ab25ce81705ea1319d42e7cc7f6c) ) // == kr_23j.8f
	ROM_LOAD16_BYTE( "kr_34.10f",  0x80000, 0x20000, CRC(fe6eb08d) SHA1(a67ca33895ff7f41e8521ff7b39e629048d04d30) ) // == kr_22.7f
	ROM_LOAD16_BYTE( "kr_40.10h",  0x80001, 0x20000, CRC(1172806d) SHA1(5ad08bf9bada036d89a55f5a542ee1db22df45b2) ) // == kr_22.7f
	ROM_LOAD16_BYTE( "kr_35.11f",  0xc0000, 0x20000, CRC(f854b020) SHA1(3d8bd0ab5e0fa2da3c868b591edff5c66f1f08ed) ) // == kr_22.7f
	ROM_LOAD16_BYTE( "kr_41.11h",  0xc0001, 0x20000, CRC(eb52e78d) SHA1(b9462c9d97848a6dc4d55475053824df8c9e7f02) ) // == kr_22.7f

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_BYTE( "kr_09.4b",  0x000000, 0x20000, CRC(08b76e10) SHA1(b18833c4794ef59f8eaf7aef4d62790c3fec9d4d) ) // == kr_01.3a
	ROM_LOAD64_BYTE( "kr_01.4a",  0x000001, 0x20000, CRC(40cecf5c) SHA1(a628199e1808664c403d0de183d1c6ae01670c57) ) // == kr_01.3a
	ROM_LOAD64_BYTE( "kr_13.9b",  0x000002, 0x20000, CRC(435aaa03) SHA1(60af266dccab12220d7efce68887b64826aba37d) ) // == kr_02.4a
	ROM_LOAD64_BYTE( "kr_05.9a",  0x000003, 0x20000, CRC(5b8a615b) SHA1(42aebdb7f338eac9a29380cb0b7553f0d3eaac04) ) // == kr_02.4a
	ROM_LOAD64_BYTE( "kr_24.5e",  0x000004, 0x20000, CRC(de65153e) SHA1(f28fcde0d08634d036d07c2a699026007d19718f) ) // == kr_03.5a
	ROM_LOAD64_BYTE( "kr_17.5c",  0x000005, 0x20000, CRC(b171c968) SHA1(cdbd7b0dc9fd2f3f027796d8f5cabf0a92a11941) ) // == kr_03.5a
	ROM_LOAD64_BYTE( "kr_38.8h",  0x000006, 0x20000, CRC(f4466bf4) SHA1(86e1ffc354b189504ca8f96143a62344a530d267) ) // == kr_04.6a
	ROM_LOAD64_BYTE( "kr_32.8f",  0x000007, 0x20000, CRC(87380ddd) SHA1(01e864060adcdafad987c5758db593525faed589) ) // == kr_04.6a
	ROM_LOAD64_BYTE( "kr_10.5b",  0x100000, 0x20000, CRC(37006d66) SHA1(0efec364ed5b0e121023bc3424d51241a4b208e0) ) // == kr_01.3a
	ROM_LOAD64_BYTE( "kr_02.5a",  0x100001, 0x20000, CRC(b54612e3) SHA1(1d33cf99fdaf3bf5c8d212d1a7c71f085c6c2cd4) ) // == kr_01.3a
	ROM_LOAD64_BYTE( "kr_14.10b", 0x100002, 0x20000, CRC(0ae88766) SHA1(5e3954b51b030d6e7cabd8f94cb849e963cc7d52) ) // == kr_02.4a
	ROM_LOAD64_BYTE( "kr_06.10a", 0x100003, 0x20000, CRC(ecb1a09a) SHA1(bbd96260774f52e50377ad020fc94d1b120f0a37) ) // == kr_02.4a
	ROM_LOAD64_BYTE( "kr_25.7e",  0x100004, 0x20000, CRC(9aace189) SHA1(95332a14dbe9d24a7e294a26530913b8cb5237f2) ) // == kr_03.5a
	ROM_LOAD64_BYTE( "kr_18.7c",  0x100005, 0x20000, CRC(09fa14a5) SHA1(36e7412a317d5f8b528d914d7e6c3c1872eb8533) ) // == kr_03.5a
	ROM_LOAD64_BYTE( "kr_39.9h",  0x100006, 0x20000, CRC(fd8a9aeb) SHA1(a38ea74de95a78fc72c6ad165157e4c22aaa5aed) ) // == kr_04.6a
	ROM_LOAD64_BYTE( "kr_33.9f",  0x100007, 0x20000, CRC(11803e95) SHA1(71603ba4a85967dd434d8499738014a9227d1d26) ) // == kr_04.6a
	ROM_LOAD64_BYTE( "kr_11.7b",  0x200000, 0x20000, CRC(a967ceb3) SHA1(43cdc0e0114f7ccedc7db5cd49b9041ed1a2614f) ) // == kr_05.7a
	ROM_LOAD64_BYTE( "kr_03.7a",  0x200001, 0x20000, CRC(ea10db07) SHA1(e537f3983ac32abcef06fa45afe51f82c87bfa86) ) // == kr_05.7a
	ROM_LOAD64_BYTE( "kr_15.11b", 0x200002, 0x20000, CRC(8140b83b) SHA1(e9cec517d84aa688d60f4201bcee00dbcdd44798) ) // == kr_06.8a
	ROM_LOAD64_BYTE( "kr_07.11a", 0x200003, 0x20000, CRC(6af10648) SHA1(b1d6c908496b1197e23571f5230f017502d5018c) ) // == kr_06.8a
	ROM_LOAD64_BYTE( "kr_26.8e",  0x200004, 0x20000, CRC(8865d86b) SHA1(a79b528d2f94ee350a39eb59860189809267cfad) ) // == kr_07.9a
	ROM_LOAD64_BYTE( "kr_19.8c",  0x200005, 0x20000, CRC(029f4abe) SHA1(e0f02d18e2a51eb02cabe507fbe77031ee3e67e7) ) // == kr_07.9a
	ROM_LOAD64_BYTE( "kr_28.10e", 0x200006, 0x20000, CRC(5f84f92f) SHA1(77b254e8159024490497533073fe302a82531835) ) // == kr_08.10a
	ROM_LOAD64_BYTE( "kr_21.10c", 0x200007, 0x20000, CRC(01b35065) SHA1(8a98860ec453ac88a3c0fc19c117bd4131d0c97c) ) // == kr_08.10a
	ROM_LOAD64_BYTE( "kr_12.8b",  0x300000, 0x20000, CRC(03d945b1) SHA1(88588891422247e6ab42f35a1b91a5801b8fd5b0) ) // == kr_05.7a
	ROM_LOAD64_BYTE( "kr_04.8a",  0x300001, 0x20000, CRC(e30c8388) SHA1(aa519f298f4156b339eb81767ca585e62dc9f4b5) ) // == kr_05.7a
	ROM_LOAD64_BYTE( "kr_16.12b", 0x300002, 0x20000, CRC(40c39d1b) SHA1(578d081f5e1bbfdc30b286cdd5165474b3ed79f7) ) // == kr_06.8a
	ROM_LOAD64_BYTE( "kr_08.12a", 0x300003, 0x20000, CRC(d310c9e8) SHA1(e23a4b318e7d3d6ea3bc6dd4509f49cd656d3d63) ) // == kr_06.8a
	ROM_LOAD64_BYTE( "kr_27.9e",  0x300004, 0x20000, CRC(3e041444) SHA1(7f84158bf2466625ff50befed7e3008b8fe09f70) ) // == kr_07.9a
	ROM_LOAD64_BYTE( "kr_20.9c",  0x300005, 0x20000, CRC(bd4bffb8) SHA1(b8f1162427a46ca9d2833390edc5723d28817a08) ) // == kr_07.9a
	ROM_LOAD64_BYTE( "kr_29.11e", 0x300006, 0x20000, CRC(1387a076) SHA1(5fe32ee8e8557c5bc6c865b2054f3c6c2a1eae7d) ) // == kr_08.10a
	ROM_LOAD64_BYTE( "kr_22.11c", 0x300007, 0x20000, CRC(fd351922) SHA1(7e08a9b23bf524d5fd566d81745aee1cd3a0f026) ) // == kr_08.10a

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "kr_23.13b",  0x00000, 0x08000, CRC(5e44d9ee) SHA1(47a7503321be8d52b5c44af838e3bb82ee15a415) )    // == kr_09.12a
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "kr_30.12c",  0x00000, 0x20000, CRC(da69d15f) SHA1(9616207e693bae85705f786cef60b9f6951b5067) )    // == kr_18.11c
	ROM_LOAD( "kr_31.13c",  0x20000, 0x20000, CRC(bfc654e9) SHA1(01b3d92e4dedf55ea3933d387c7ddb9ba2549773) )    // == kr_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "kr22b.1a",     0x0000, 0x0117, CRC(f15b2c0f) SHA1(07113a14a60b5ba232451bc73e36f7e6a7fee572) )
	ROM_LOAD( "lwio.12e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

// dumper's note: This board has bad priority layers - The 'flag' is behind the characters on one demo mode level for example

ROM_START( knightsb2 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "left.code.040",   0x00000, 0x80000, CRC(95d00a7e) SHA1(2da1c47aa15d44aa842a8d79e030e7e7b69bec19) )
	ROM_LOAD16_BYTE( "right.code.040",  0x00001, 0x80000, CRC(5a9d0b64) SHA1(6ff312879c3d675ceee6fd3e262f797fabb9871e) )

	ROM_REGION( 0x400000, "gfx", 0 ) // same ROM arrangement as sfm8. Same bootlegger?
	ROM_LOAD64_BYTE( "spe-a.japan9207d.mask1.801",    0x000000, 0x40000, CRC(14a15fcd) SHA1(8bb9d79145dd652e9a7c6e433a3f423d5518cd41) )
	ROM_CONTINUE(                                     0x000004, 0x40000)
	ROM_CONTINUE(                                     0x200000, 0x40000)
	ROM_CONTINUE(                                     0x200004, 0x40000)
	ROM_LOAD64_BYTE( "spe-b.japan9207d.mask2.801",    0x000001, 0x40000, CRC(250d2957) SHA1(067bc46e3fc8b9166da1fb25f7fe9dbd80d9fda4) )
	ROM_CONTINUE(                                     0x000005, 0x40000)
	ROM_CONTINUE(                                     0x200001, 0x40000)
	ROM_CONTINUE(                                     0x200005, 0x40000)
	ROM_LOAD64_BYTE( "spe-c.japan9207d.mask4.801",    0x000002, 0x40000, CRC(0721c26d) SHA1(6211a723520fd1c2594e95b353ea6deb70d7ce90) )
	ROM_CONTINUE(                                     0x000006, 0x40000)
	ROM_CONTINUE(                                     0x200002, 0x40000)
	ROM_CONTINUE(                                     0x200006, 0x40000)
	ROM_LOAD64_BYTE( "spe-d.japan9207d.mask3.801",    0x000003, 0x40000, CRC(db97f56a) SHA1(bf61dd97b0e3790b27b0f67bb98ecae69ffa048f) )
	ROM_CONTINUE(                                     0x000007, 0x40000)
	ROM_CONTINUE(                                     0x200003, 0x40000)
	ROM_CONTINUE(                                     0x200007, 0x40000)

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sound.code.512",  0x00000, 0x08000, CRC(5e44d9ee) SHA1(47a7503321be8d52b5c44af838e3bb82ee15a415) )
	ROM_CONTINUE(                0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "spe-e.japan9208d.snd.mask.020",  0x00000, 0x40000, CRC(85f837a0) SHA1(21a3fe8fdad10bfc994777e0b85c2c4b23943534) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( sf2ce )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "s92e_23b.8f", 0x000000, 0x80000, CRC(0aaa1a3a) SHA1(774a2b52f7c1876c0e10d8d57a0850ad2d016cf6) )
	ROM_LOAD16_WORD_SWAP( "s92_22b.7f",  0x080000, 0x80000, CRC(2bbe15ed) SHA1(a8e2edef62fa99c5ef701b28bfb6bc42f3af183d) )
	ROM_LOAD16_WORD_SWAP( "s92_21a.6f",  0x100000, 0x80000, CRC(925a7877) SHA1(1960dca35f0ca6f2b399a9fccfbc0132ac6425d1) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92-1m.3a",  0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92-3m.5a",  0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92-2m.4a",  0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92-4m.6a",  0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92-5m.7a",  0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92-7m.9a",  0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92-6m.8a",  0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92-8m.10a", 0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92-10m.3c", 0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92-12m.5c", 0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92-11m.4c", 0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92-13m.6c", 0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.11a",  0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s9263b.1a",    0x0000, 0x0117, CRC(0a7ecfe0) SHA1(f75e7eed4604fcf68273197fe3dd7f0d7a313ada) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( sf2ceea )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "s92e_23a.8f", 0x000000, 0x80000, CRC(3f846b74) SHA1(c8d7a01b626771870123f1663a01a81f9c8fe582) )
	ROM_LOAD16_WORD_SWAP( "s92_22a.7f",  0x080000, 0x80000, CRC(99f1cca4) SHA1(64111eba81d743fc3fd51d7a89cd0b2eefcc900d) )
	ROM_LOAD16_WORD_SWAP( "s92_21a.6f",  0x100000, 0x80000, CRC(925a7877) SHA1(1960dca35f0ca6f2b399a9fccfbc0132ac6425d1) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92-1m.3a",  0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92-3m.5a",  0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92-2m.4a",  0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92-4m.6a",  0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92-5m.7a",  0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92-7m.9a",  0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92-6m.8a",  0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92-8m.10a", 0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92-10m.3c", 0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92-12m.5c", 0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92-11m.4c", 0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92-13m.6c", 0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.11a",  0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s9263b.1a",    0x0000, 0x0117, CRC(0a7ecfe0) SHA1(f75e7eed4604fcf68273197fe3dd7f0d7a313ada) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( sf2ceec ) // single byte difference, may be legit or may be a region hack
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "s92e_23c.8f", 0x000000, 0x80000, BAD_DUMP CRC(994b408d) SHA1(f4b9880b3668e09b1250fbd2ff5e58f181d0402f) ) // E seems hand-written, bad dump cause it would be better to have it confirmed from other board
	ROM_LOAD16_WORD_SWAP( "s92_22c.7f",  0x080000, 0x80000, CRC(5fd8630b) SHA1(f0ef9c5ab91a4b421fb4b1747eef99c964c15de3) )
	ROM_LOAD16_WORD_SWAP( "s92_21a.6f",  0x100000, 0x80000, CRC(925a7877) SHA1(1960dca35f0ca6f2b399a9fccfbc0132ac6425d1) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92-1m.3a",  0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92-3m.5a",  0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92-2m.4a",  0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92-4m.6a",  0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92-5m.7a",  0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92-7m.9a",  0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92-6m.8a",  0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92-8m.10a", 0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92-10m.3c", 0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92-12m.5c", 0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92-11m.4c", 0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92-13m.6c", 0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.11a",  0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s9263b.1a",    0x0000, 0x0117, CRC(0a7ecfe0) SHA1(f75e7eed4604fcf68273197fe3dd7f0d7a313ada) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( sf2ceua )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "s92u_23a.8f", 0x000000, 0x80000, CRC(ac44415b) SHA1(218f8b1886eb72b8547127042b5ae47600e18944) )
	ROM_LOAD16_WORD_SWAP( "s92_22a.7f",  0x080000, 0x80000, CRC(99f1cca4) SHA1(64111eba81d743fc3fd51d7a89cd0b2eefcc900d) )
	ROM_LOAD16_WORD_SWAP( "s92_21a.6f",  0x100000, 0x80000, CRC(925a7877) SHA1(1960dca35f0ca6f2b399a9fccfbc0132ac6425d1) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92-1m.3a",  0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92-3m.5a",  0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92-2m.4a",  0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92-4m.6a",  0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92-5m.7a",  0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92-7m.9a",  0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92-6m.8a",  0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92-8m.10a", 0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92-10m.3c", 0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92-12m.5c", 0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92-11m.4c", 0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92-13m.6c", 0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.11a",  0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s9263b.1a",    0x0000, 0x0117, CRC(0a7ecfe0) SHA1(f75e7eed4604fcf68273197fe3dd7f0d7a313ada) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( sf2ceub )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "s92u_23b.8f", 0x000000, 0x80000, CRC(996a3015) SHA1(fdf45da54b1c14478a60f2b86e37ffe32a98b135) )
	ROM_LOAD16_WORD_SWAP( "s92_22b.7f",  0x080000, 0x80000, CRC(2bbe15ed) SHA1(a8e2edef62fa99c5ef701b28bfb6bc42f3af183d) )
	ROM_LOAD16_WORD_SWAP( "s92_21a.6f",  0x100000, 0x80000, CRC(925a7877) SHA1(1960dca35f0ca6f2b399a9fccfbc0132ac6425d1) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92-1m.3a",  0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92-3m.5a",  0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92-2m.4a",  0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92-4m.6a",  0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92-5m.7a",  0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92-7m.9a",  0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92-6m.8a",  0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92-8m.10a", 0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92-10m.3c", 0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92-12m.5c", 0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92-11m.4c", 0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92-13m.6c", 0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.11a",  0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s9263b.1a",    0x0000, 0x0117, CRC(0a7ecfe0) SHA1(f75e7eed4604fcf68273197fe3dd7f0d7a313ada) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( sf2ceuc )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "s92u_23c.8f", 0x000000, 0x80000, CRC(0a8b6aa2) SHA1(a19871271172119e1cf1ff47700bb1917b08514b) )
	ROM_LOAD16_WORD_SWAP( "s92_22c.7f",  0x080000, 0x80000, CRC(5fd8630b) SHA1(f0ef9c5ab91a4b421fb4b1747eef99c964c15de3) )
	ROM_LOAD16_WORD_SWAP( "s92_21a.6f",  0x100000, 0x80000, CRC(925a7877) SHA1(1960dca35f0ca6f2b399a9fccfbc0132ac6425d1) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92-1m.3a",  0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92-3m.5a",  0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92-2m.4a",  0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92-4m.6a",  0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92-5m.7a",  0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92-7m.9a",  0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92-6m.8a",  0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92-8m.10a", 0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92-10m.3c", 0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92-12m.5c", 0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92-11m.4c", 0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92-13m.6c", 0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.11a",  0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s9263b.1a",    0x0000, 0x0117, CRC(0a7ecfe0) SHA1(f75e7eed4604fcf68273197fe3dd7f0d7a313ada) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( sf2cet )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "s92t_23a.8f", 0x000000, 0x80000, CRC(d7c28ade) SHA1(4fe8201d8861f9ea9c62fd97b7396bc180a9f3ce) )
	ROM_LOAD16_WORD_SWAP( "s92_22a.7f",  0x080000, 0x80000, CRC(99f1cca4) SHA1(64111eba81d743fc3fd51d7a89cd0b2eefcc900d) )
	ROM_LOAD16_WORD_SWAP( "s92_21a.6f",  0x100000, 0x80000, CRC(925a7877) SHA1(1960dca35f0ca6f2b399a9fccfbc0132ac6425d1) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92-1m.3a",  0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92-3m.5a",  0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92-2m.4a",  0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92-4m.6a",  0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92-5m.7a",  0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92-7m.9a",  0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92-6m.8a",  0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92-8m.10a", 0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92-10m.3c", 0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92-12m.5c", 0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92-11m.4c", 0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92-13m.6c", 0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.11a",  0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s9263b.1a",    0x0000, 0x0117, CRC(0a7ecfe0) SHA1(f75e7eed4604fcf68273197fe3dd7f0d7a313ada) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91634B-2 */
ROM_START( sf2ceja )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "s92j_23a.8f", 0x000000, 0x80000, CRC(4f42bb5a) SHA1(59d0587c554e06ea45d4092ea4299ff086509d4b) )
	ROM_LOAD16_WORD_SWAP( "s92j_22a.7f", 0x080000, 0x80000, CRC(c4f64bcd) SHA1(262c0419bf727da80c2ac52b877a19276d9aac3c) )
	ROM_LOAD16_WORD_SWAP( "s92_21a.6f",  0x100000, 0x80000, CRC(925a7877) SHA1(1960dca35f0ca6f2b399a9fccfbc0132ac6425d1) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.3a",  0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )    // == s92-1m.3a
	ROM_LOAD64_WORD( "s92_02.4a",  0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )    // == s92-3m.5a
	ROM_LOAD64_WORD( "s92_03.5a",  0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )    // == s92-2m.4a
	ROM_LOAD64_WORD( "s92_04.6a",  0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )    // == s92-4m.6a
	ROM_LOAD64_WORD( "s92_05.7a",  0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )    // == s92-5m.7a
	ROM_LOAD64_WORD( "s92_06.8a",  0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )    // == s92-7m.9a
	ROM_LOAD64_WORD( "s92_07.9a",  0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )    // == s92-6m.8a
	ROM_LOAD64_WORD( "s92_08.10a", 0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )    // == s92-8m.10a
	ROM_LOAD64_WORD( "s92_10.3c",  0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )    // == s92-10m.3c
	ROM_LOAD64_WORD( "s92_11.4c",  0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )    // == s92-12m.5c
	ROM_LOAD64_WORD( "s92_12.5c",  0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )    // == s92-11m.4c
	ROM_LOAD64_WORD( "s92_13.6c",  0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )    // == s92-13m.6c

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.12a",  0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )   // == s92_09.11a
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s9263b.1a",    0x0000, 0x0117, CRC(0a7ecfe0) SHA1(f75e7eed4604fcf68273197fe3dd7f0d7a313ada) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91634B-2 */
ROM_START( sf2cejb )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "s92j_23b.8f", 0x000000, 0x80000, CRC(140876c5) SHA1(304630e6d8bae9f8d29090e05f7e013c7dafe9cc) )
	ROM_LOAD16_WORD_SWAP( "s92j_22b.7f", 0x080000, 0x80000, CRC(2fbb3bfe) SHA1(e364564a12022730c2c0d0e8fd435e2c30ef9410) )
	ROM_LOAD16_WORD_SWAP( "s92_21a.6f",  0x100000, 0x80000, CRC(925a7877) SHA1(1960dca35f0ca6f2b399a9fccfbc0132ac6425d1) )  // could be s92j_21a.6f

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.3a",  0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )    // == s92-1m.3a
	ROM_LOAD64_WORD( "s92_02.4a",  0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )    // == s92-3m.5a
	ROM_LOAD64_WORD( "s92_03.5a",  0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )    // == s92-2m.4a
	ROM_LOAD64_WORD( "s92_04.6a",  0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )    // == s92-4m.6a
	ROM_LOAD64_WORD( "s92_05.7a",  0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )    // == s92-5m.7a
	ROM_LOAD64_WORD( "s92_06.8a",  0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )    // == s92-7m.9a
	ROM_LOAD64_WORD( "s92_07.9a",  0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )    // == s92-6m.8a
	ROM_LOAD64_WORD( "s92_08.10a", 0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )    // == s92-8m.10a
	ROM_LOAD64_WORD( "s92_10.3c",  0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )    // == s92-10m.3c
	ROM_LOAD64_WORD( "s92_11.4c",  0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )    // == s92-12m.5c
	ROM_LOAD64_WORD( "s92_12.5c",  0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )    // == s92-11m.4c
	ROM_LOAD64_WORD( "s92_13.6c",  0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )    // == s92-13m.6c

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.12a",  0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )   // == s92_09.11a
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s9263b.1a",    0x0000, 0x0117, CRC(0a7ecfe0) SHA1(f75e7eed4604fcf68273197fe3dd7f0d7a313ada) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91634B-2 */
ROM_START( sf2cejc )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "s92j_23c.8f", 0x000000, 0x80000, CRC(f0120635) SHA1(5e4a9a4b0f65c6139e76ee4ffa02b9db245b1858) )
	ROM_LOAD16_WORD_SWAP( "s92j_22c.7f", 0x080000, 0x80000, CRC(8c0b2ed6) SHA1(408db039b4dad72b41458723575ed5352b71e10b) )
	ROM_LOAD16_WORD_SWAP( "s92j_21a.6f", 0x100000, 0x80000, CRC(925a7877) SHA1(1960dca35f0ca6f2b399a9fccfbc0132ac6425d1) )  // == s92_21a.6f

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.3a",  0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )    // == s92-1m.3a
	ROM_LOAD64_WORD( "s92_02.4a",  0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )    // == s92-3m.5a
	ROM_LOAD64_WORD( "s92_03.5a",  0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )    // == s92-2m.4a
	ROM_LOAD64_WORD( "s92_04.6a",  0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )    // == s92-4m.6a
	ROM_LOAD64_WORD( "s92_05.7a",  0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )    // == s92-5m.7a
	ROM_LOAD64_WORD( "s92_06.8a",  0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )    // == s92-7m.9a
	ROM_LOAD64_WORD( "s92_07.9a",  0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )    // == s92-6m.8a
	ROM_LOAD64_WORD( "s92_08.10a", 0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )    // == s92-8m.10a
	ROM_LOAD64_WORD( "s92_10.3c",  0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )    // == s92-10m.3c
	ROM_LOAD64_WORD( "s92_11.4c",  0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )    // == s92-12m.5c
	ROM_LOAD64_WORD( "s92_12.5c",  0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )    // == s92-11m.4c
	ROM_LOAD64_WORD( "s92_13.6c",  0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )    // == s92-13m.6c

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.12a",  0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )   // == s92_09.11a
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s9263b.1a",    0x0000, 0x0117, CRC(0a7ecfe0) SHA1(f75e7eed4604fcf68273197fe3dd7f0d7a313ada) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( sf2bhh )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "23",           0x000000, 0x40000, CRC(5cf63a9e) SHA1(af26165cacb4838572ddf1cd7a2ff22a5dddbf3c) ) // sldh
	ROM_CONTINUE(                         0x0c0000, 0x40000 )
	ROM_LOAD16_WORD_SWAP( "22",           0x080000, 0x40000, CRC(3ed72bca) SHA1(5565aee82e6b4734c4c0d06a73d8141abb1e4119) ) // sldh
	ROM_CONTINUE(                         0x040000, 0x40000 )
	ROM_LOAD16_WORD_SWAP( "s92_21a.6f",   0x100000, 0x80000, CRC(925a7877) SHA1(1960dca35f0ca6f2b399a9fccfbc0132ac6425d1) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92-1m.3a",  0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92-3m.5a",  0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92-2m.4a",  0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92-4m.6a",  0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92-5m.7a",  0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92-7m.9a",  0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92-6m.8a",  0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92-8m.10a", 0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92-10m.3c", 0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92-12m.5c", 0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92-11m.4c", 0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92-13m.6c", 0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.11a",  0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s9263b.1a",    0x0000, 0x0117, CRC(0a7ecfe0) SHA1(f75e7eed4604fcf68273197fe3dd7f0d7a313ada) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

ROM_START( sf2rb )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD( "sf2d__23.rom",      0x000000, 0x80000, CRC(450532b0) SHA1(14d5ff44ce97247ef4c42147157856d16c5fb4b8) )
	ROM_LOAD16_WORD( "sf2d__22.rom",      0x080000, 0x80000, CRC(fe9d9cf5) SHA1(91afb25d8c0fd1a721f982cebf8fdf563fe11760) )
	ROM_LOAD16_WORD_SWAP( "s92_21a.bin",  0x100000, 0x80000, CRC(925a7877) SHA1(1960dca35f0ca6f2b399a9fccfbc0132ac6425d1) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.bin",   0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92_02.bin",   0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92_03.bin",   0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92_04.bin",   0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92_05.bin",   0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92_06.bin",   0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92_07.bin",   0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92_08.bin",   0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92_10.bin",   0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92_11.bin",   0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92_12.bin",   0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92_13.bin",   0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.bin",    0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

ROM_START( sf2rb2 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "27.bin",            0x000000, 0x20000, CRC(40296ecd) SHA1(6006b9bc7e2e8ccec47f87b51791d3c0512620df) )
	ROM_LOAD16_BYTE( "31.bin",            0x000001, 0x20000, CRC(87954a41) SHA1(67225f180e1f954f0bebba49b618f793a973af14) )
	ROM_LOAD16_BYTE( "26.bin",            0x040000, 0x20000, CRC(a6974195) SHA1(f7e0fd43bd75229d49d5c330820bdc5c3b11ab03) )
	ROM_LOAD16_BYTE( "30.bin",            0x040001, 0x20000, CRC(8141fe32) SHA1(e6ea1ee331f674c64e63a776ad4e428f6081c79c) )
	ROM_LOAD16_BYTE( "25.bin",            0x080000, 0x20000, CRC(9ef8f772) SHA1(3ee271413521cc2d6ac9544e401ff38eff8a1347) )
	ROM_LOAD16_BYTE( "29.bin",            0x080001, 0x20000, CRC(7d9c479c) SHA1(a1195444caac5230a1f74f3444b024ceaf1d0667) )
	ROM_LOAD16_BYTE( "24.bin",            0x0c0000, 0x20000, CRC(93579684) SHA1(9052b46f635cae7843e9d37a601db0189a89e0f9) )
	ROM_LOAD16_BYTE( "28.bin",            0x0c0001, 0x20000, CRC(ff728865) SHA1(ad4522294ff2e02b594d960b45940a3e57a5d1ec) )
	ROM_LOAD16_WORD_SWAP( "s92_21a.bin",  0x100000, 0x80000, CRC(925a7877) SHA1(1960dca35f0ca6f2b399a9fccfbc0132ac6425d1) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.bin",   0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92_02.bin",   0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92_03.bin",   0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92_04.bin",   0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92_05.bin",   0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92_06.bin",   0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92_07.bin",   0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92_08.bin",   0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92_10.bin",   0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92_11.bin",   0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92_12.bin",   0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92_13.bin",   0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.bin",    0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

/* this rainbow set DOES NOT require a custom PLD to work, runs on standard board with roms replaced */
ROM_START( sf2rb3 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "sf2_ce_rb.23",      0x000000, 0x80000, CRC(202f9e50) SHA1(8f0259ade1bc4df65abf4ad0961db24ca27e3f4b) )
	ROM_LOAD16_WORD_SWAP( "sf2_ce_rb.22",      0x080000, 0x80000, CRC(145e5219) SHA1(0b1251ad817a395f37f6c9acee393c3fce07777a) )
	ROM_LOAD16_WORD_SWAP( "s92_21a.bin",       0x100000, 0x80000, CRC(925a7877) SHA1(1960dca35f0ca6f2b399a9fccfbc0132ac6425d1) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.bin",   0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92_02.bin",   0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92_03.bin",   0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92_04.bin",   0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92_05.bin",   0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92_06.bin",   0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92_07.bin",   0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92_08.bin",   0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92_10.bin",   0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92_11.bin",   0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92_12.bin",   0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92_13.bin",   0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.bin",    0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

ROM_START( sf2red )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "sf2red.23",    0x000000, 0x80000, CRC(40276abb) SHA1(a991661f5a1a3116445594bcfed3150e36971dd7) )
	ROM_LOAD16_WORD_SWAP( "sf2red.22",    0x080000, 0x80000, CRC(18daf387) SHA1(1a9e4c04ca54e8b33f19dd7bedbe05a200249701) )
	ROM_LOAD16_WORD_SWAP( "sf2red.21",    0x100000, 0x80000, CRC(52c486bb) SHA1(b7df7b10faa4c9a2f86ebf64cd63ac148d62dd09) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.bin",   0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92_02.bin",   0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92_03.bin",   0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92_04.bin",   0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92_05.bin",   0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92_06.bin",   0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92_07.bin",   0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92_08.bin",   0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92_10.bin",   0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92_11.bin",   0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92_12.bin",   0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92_13.bin",   0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.bin",    0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

// uses original Capcom 91635B-2 B-Board + Capcom 92631C-6 C-Board, has hacked program ROMs and an (undumped) PAL probably for addressing them
// actually flashes 'Red Wave' during attract while sf2red doesn't
ROM_START( sf2reda )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "stf champ wave rom 21.6f", 0x000000, 0x40000, CRC(04fff17b) SHA1(e133686f4795c85b531864ea17bf8b2715e2ed6b) )
	ROM_CONTINUE(                                     0x0c0000, 0x40000 )
	ROM_LOAD16_WORD_SWAP( "stf champ wave rom 23.8f", 0x080000, 0x40000, CRC(eb265dc7) SHA1(191748223aa8650f4ed6e7d1885fe8056c910720) )
	ROM_CONTINUE(                                     0x140000, 0x40000 )
	ROM_LOAD16_WORD_SWAP( "stf champ wave rom 22.7f", 0x100000, 0x40000, CRC(27e80cb1) SHA1(d32eba8c7c887d6149020ab103ecda13448e6608) )
	ROM_CONTINUE(                                     0x040000, 0x40000 )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92-1m.3a",  0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92-3m.5a",  0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92-2m.4a",  0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92-4m.6a",  0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92-5m.7a",  0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92-7m.9a",  0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92-6m.8a",  0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92-8m.10a", 0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92-10m.3c", 0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92-12m.5c", 0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92-11m.4c", 0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92-13m.6c", 0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.bin",    0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s9263b.1a",    0x0000, 0x0117, CRC(0a7ecfe0) SHA1(f75e7eed4604fcf68273197fe3dd7f0d7a313ada) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "pw.11d",       0x0000, 0x0117, NO_DUMP )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

ROM_START( sf2redp2 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "sf2red.23",    0x000000, 0x80000, CRC(2d3c4f72) SHA1(79f5f953d2dbea72450dc1b18e1ce8c5e63c9370) )
	ROM_LOAD16_WORD_SWAP( "sf2red.22",    0x080000, 0x80000, CRC(18daf387) SHA1(1a9e4c04ca54e8b33f19dd7bedbe05a200249701) )
	ROM_LOAD16_WORD_SWAP( "sf2red.21",    0x100000, 0x80000, CRC(aaf693b5) SHA1(5d42d39c7c08470fc7bc7ca987c910f5848d344e) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.bin",   0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92_02.bin",   0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92_03.bin",   0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92_04.bin",   0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92_05.bin",   0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92_06.bin",   0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92_07.bin",   0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92_08.bin",   0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92_10.bin",   0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92_11.bin",   0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92_12.bin",   0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92_13.bin",   0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.bin",    0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

ROM_START( sf2v004 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "sf2v004.23",   0x000000, 0x80000, CRC(52d19f2c) SHA1(6a77b9244dc9b7d9a0ca8a642d4257cc944ac566) )
	ROM_LOAD16_WORD_SWAP( "sf2v004.22",   0x080000, 0x80000, CRC(4b26fde7) SHA1(48e3aacbf9147f2374a93e10f945291c87f24855) )
	ROM_LOAD16_WORD_SWAP( "sf2red.21",    0x100000, 0x80000, CRC(52c486bb) SHA1(b7df7b10faa4c9a2f86ebf64cd63ac148d62dd09) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.bin",   0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92_02.bin",   0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92_03.bin",   0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92_04.bin",   0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92_05.bin",   0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92_06.bin",   0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92_07.bin",   0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92_08.bin",   0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92_10.bin",   0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92_11.bin",   0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92_12.bin",   0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92_13.bin",   0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.bin",    0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

ROM_START( sf2acc )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "sf2ca_23-c.bin", 0x000000, 0x80000, CRC(e7c8c5a6) SHA1(247fb38c041f4d516899db32545d8b4d8debc4cf) )
	ROM_LOAD16_WORD_SWAP( "sf2ca_22-c.bin", 0x080000, 0x80000, CRC(99f1cca4) SHA1(64111eba81d743fc3fd51d7a89cd0b2eefcc900d) )
	ROM_LOAD16_WORD_SWAP( "sf2ca_21-c.bin", 0x100000, 0x40000, CRC(cf7fcc8c) SHA1(47d338634dd72d1ef912fbdb8c1a7ae945ca1a5f) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.bin",   0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92_02.bin",   0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92_03.bin",   0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92_04.bin",   0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92_05.bin",   0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92_06.bin",   0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92_07.bin",   0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92_08.bin",   0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92_10.bin",   0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92_11.bin",   0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92_12.bin",   0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92_13.bin",   0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.bin",    0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END



ROM_START( sf2acca )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "23-c.8f", 0x000000, 0x80000, CRC(35f9517b) SHA1(0976870c65d69a6d8ca0232a48e042588a9ec5d8) )
	ROM_LOAD16_WORD_SWAP( "22-c.7f", 0x080000, 0x80000, CRC(99f1cca4) SHA1(64111eba81d743fc3fd51d7a89cd0b2eefcc900d) )
	ROM_LOAD16_WORD_SWAP( "21-c.6f", 0x100000, 0x80000, CRC(2ab2034f) SHA1(4dc90ba524e76529700945605e02068d49b56aba) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.bin",   0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92_02.bin",   0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92_03.bin",   0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92_04.bin",   0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92_05.bin",   0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92_06.bin",   0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92_07.bin",   0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92_08.bin",   0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92_10.bin",   0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92_11.bin",   0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92_12.bin",   0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92_13.bin",   0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.bin",    0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

ROM_START( sf2accp2 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "sf2ca-23.bin", 0x000000, 0x80000, CRC(36c3ba2f) SHA1(a3ddc479b725ddb3521757c8efa7aed125004997) )
	ROM_LOAD16_WORD_SWAP( "sf2ca-22.bin", 0x080000, 0x80000, CRC(0550453d) SHA1(f9efed86528dd10f142636278f098584d33ccde6) )
	ROM_LOAD16_WORD_SWAP( "sf2ca-21.bin", 0x100000, 0x40000, CRC(4c1c43ba) SHA1(16abce268373eddd7c3b93fe8d44b200a8c140fe) )
	/* ROM space ends at 13ffff, but the code checks 180ca6 and */
	/* crashes if it isn't 0 - protection? */

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.bin",   0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92_02.bin",   0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92_03.bin",   0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92_04.bin",   0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92_05.bin",   0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92_06.bin",   0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92_07.bin",   0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92_08.bin",   0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92_10.bin",   0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92_11.bin",   0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92_12.bin",   0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92_13.bin",   0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.bin",    0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END


/*
SF2' CE "bootleg"

CPS1 motherboard (no dash)
Original 88622-c-2 C board

Bootleg B board with
    PIC16c55 near PRG roms seem protected
    8 dipswith near both sound roms and gfx roms
    1,4,5,8 ON
    2,3,6,7 OFF

Sound rom match various romset
*/

ROM_START( sf2ceblp )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "1k.31.e13", 0x00000, 0x20000, CRC(ea78f9b4) SHA1(967f84615f6ef4d046df3593d5ffce0d43a3ac64) ) // 27c010
	ROM_LOAD16_BYTE( "se.36.j13", 0x00001, 0x20000, CRC(d30c263e) SHA1(6d8b13f82dde3237115306d91f37ac95b8238e0a) ) // 27c010
	ROM_LOAD16_BYTE( "0e.30.e11", 0x40000, 0x20000, CRC(005b54cc) SHA1(3d3d7742c11f31cc62d81931dc970cd1f4a26835) ) // 27c010
	ROM_LOAD16_BYTE( "de.35.j11", 0x40001, 0x20000, CRC(c184d26d) SHA1(b1011e869d0b84c1ab609fcf193b46c7d9cc3b28) ) // 27c010
	ROM_LOAD16_BYTE( "bk.29.e10", 0x80000, 0x20000, CRC(524f5c55) SHA1(67adfd36bb325c4a6d3e1e5b668c4dc983ac0a62) ) // 27c010
	ROM_LOAD16_BYTE( "ff.34.j10", 0x80001, 0x20000, CRC(f06a12f2) SHA1(ddc431ce01392d4a7562760743abd9ea73b06cf3) ) // 27c010
	ROM_LOAD16_BYTE( "8k.28.e9",  0xc0000, 0x40000, CRC(b7ad5214) SHA1(17b05e0aa9a4eb5f1aaafe35fa029d2a9aea530d) ) // 27c020
	ROM_LOAD16_BYTE( "tf.33.j9",  0xc0001, 0x40000, CRC(6340b914) SHA1(443e37a06058548c8ce7a15ecd10a6635e69d09f) ) // 27c020

	ROM_REGION( 0x600000, "gfx", 0 ) // all 27c020
	ROM_LOAD64_BYTE( "24.e7",  0x000000, 0x40000, CRC(a8b5633a) SHA1(6548a89d616910d06db126eb1a9c6b5979baff03) ) // sldh
	ROM_LOAD64_BYTE( "14.c7",  0x000001, 0x40000, CRC(5db24ca7) SHA1(0543e89174fecc866a08e0ecc7c31a6efca15da5) )
	ROM_LOAD64_BYTE( "26.e9",  0x000002, 0x40000, CRC(82e8e384) SHA1(a8849bdb97c496777ba87e8f1942585c0fc9a29b) ) // sldh
	ROM_LOAD64_BYTE( "16.c9",  0x000003, 0x40000, CRC(1fd98ad0) SHA1(c0edf8ad305d5f8ba72498024cacf28b08491a57) )
	ROM_LOAD64_BYTE( "20.e3",  0x000004, 0x40000, CRC(8d5d0045) SHA1(74691132c740cc28585ebe9469fa36fea28c1cef) )
	ROM_LOAD64_BYTE( "10.c3",  0x000005, 0x40000, CRC(eb48f7f2) SHA1(f1d269949a8e06434e2206842535d86a70bcf58f) )
	ROM_LOAD64_BYTE( "22.e5",  0x000006, 0x40000, CRC(4109d637) SHA1(f7b0961ae3053a0751749ab77f27503e03ae1e5b) )
	ROM_LOAD64_BYTE( "12.c5",  0x000007, 0x40000, CRC(5d21d8b3) SHA1(1e75a4bf8d750e0858d70a489b59b50839b8c720) )

	ROM_LOAD64_BYTE( "25.e8",  0x200000, 0x40000, CRC(72e923df) SHA1(f8db61f8a768fbf99a0be54af712aa19161978d1) ) // sldh
	ROM_LOAD64_BYTE( "15.c8",  0x200001, 0x40000, CRC(9a96be48) SHA1(c0a46aefba442196ead30d4f5de2b0370b1c03f0) )
	ROM_LOAD64_BYTE( "27.e10", 0x200002, 0x40000, CRC(4a3a8d09) SHA1(eaa7307963c7a5381a4d84a4ebcc483b55cc1411) ) // sldh
	ROM_LOAD64_BYTE( "17.c10", 0x200003, 0x40000, CRC(a917a922) SHA1(2186095f8473538756ca22dbdb2c68d17cbf24ab) )
	ROM_LOAD64_BYTE( "21.e4",  0x200004, 0x40000, CRC(55c2b455) SHA1(677ac8f988ea3c0564b2dd178ee2ab7077aeafc8) )
	ROM_LOAD64_BYTE( "11.c4",  0x200005, 0x40000, CRC(ff36859e) SHA1(7f5ab9daacfdb338913d6bca723efe3128dc931f) )
	ROM_LOAD64_BYTE( "23.e6",  0x200006, 0x40000, CRC(ef9c2d4d) SHA1(35c3ab5a3cc3ce2219b596c4c3e97d28b9cc2297) )
	ROM_LOAD64_BYTE( "13.c6",  0x200007, 0x40000, CRC(bc937c96) SHA1(772ff4e06093043f693729d8e94e7f8e3f8e866f) )

	ROM_LOAD64_BYTE( "6.a8",   0x400000, 0x40000, CRC(023baa18) SHA1(671600e629aff1cc161567ee4a5ceb2228731a36) )
	ROM_LOAD64_BYTE( "5.a7",   0x400001, 0x40000, CRC(a6ad6ef3) SHA1(c670931a98dfc6ca39d54d8cb8848421232d8c8c) )
	ROM_LOAD64_BYTE( "8.a10",  0x400002, 0x40000, CRC(26fb340c) SHA1(270455c10a0c7e4ba95a38ab7d815faedfe51fde) )
	ROM_LOAD64_BYTE( "7.a9",   0x400003, 0x40000, CRC(f56085ba) SHA1(ce8c33d7be271a501e770da5249778f62ca0e0a1) )
	ROM_LOAD64_BYTE( "1.a3",   0x400004, 0x40000, CRC(877b2b18) SHA1(36ebef5b534d2449b163df38b9a98f769c1d85ed) )
	ROM_LOAD64_BYTE( "2.a4",   0x400005, 0x40000, CRC(144aa4c9) SHA1(f00bf10f04ae774dda7e3a1c862aba67f70d61c3) )
	ROM_LOAD64_BYTE( "4.a6",   0x400006, 0x40000, CRC(f2c400b4) SHA1(090ff7184cd2a1b992cc9f1f8c03f8832bc11416) )
	ROM_LOAD64_BYTE( "3.a5",   0x400007, 0x40000, CRC(8053335d) SHA1(c653cb249401162fbf39f293e1999fe5e09c35c4) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "snd.9.b13",    0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) ) // 27c512
	ROM_CONTINUE(             0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples, both 27c010 */
	ROM_LOAD( "9k.18.c11",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "lk.19.c13",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x2000, "protection_pic", 0 )
	ROM_LOAD( "pic16c55",    0x00000, 0x2000, BAD_DUMP CRC(f22e2311) SHA1(320edfba140728599e91c01e863a8b6d071e4bbf) )
ROM_END



ROM_START( sf2cebltw )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "27.12e",           0x000000, 0x20000, CRC(035ee5d9) SHA1(9d268333db96dc6d1539ea938d3b213482e276c6) )
	ROM_LOAD16_BYTE( "31.12f",           0x000001, 0x20000, CRC(353dbde1) SHA1(039785f8863fda945267fc8b9cf640d954ea9803) )
	ROM_LOAD16_BYTE( "24.9e",            0x040000, 0x20000, CRC(005b54cc) SHA1(3d3d7742c11f31cc62d81931dc970cd1f4a26835) )
	ROM_LOAD16_BYTE( "28.9f",            0x040001, 0x20000, CRC(c184d26d) SHA1(b1011e869d0b84c1ab609fcf193b46c7d9cc3b28) )
	ROM_LOAD16_BYTE( "25.10e",           0x080000, 0x20000, CRC(524f5c55) SHA1(67adfd36bb325c4a6d3e1e5b668c4dc983ac0a62) )
	ROM_LOAD16_BYTE( "29.10f",           0x080001, 0x20000, CRC(f06a12f2) SHA1(ddc431ce01392d4a7562760743abd9ea73b06cf3) )
	ROM_LOAD16_BYTE( "26.11e",           0x0c0000, 0x20000, CRC(8312d055) SHA1(e36c23c197096d50d71ceefbf93205d5758153e0) )
	ROM_LOAD16_BYTE( "30.11f",           0x0c0001, 0x20000, CRC(d0580ff2) SHA1(1b2e4c4abbe90a68283c86e7cb5328b242be5683) )
	ROM_LOAD16_WORD_SWAP( "s92_21a.5f",  0x100000, 0x80000, CRC(925a7877) SHA1(1960dca35f0ca6f2b399a9fccfbc0132ac6425d1) )

	ROM_REGION( 0x40000, "pal", 0 )
	ROM_LOAD( "bruteforce.palce16v8h-25.11d",    0x00000, 0x40000, CRC(430f722d) SHA1(b0c0570057c782b1114819fae907f45a01c55065) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.bin",   0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92_02.bin",   0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92_03.bin",   0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92_04.bin",   0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92_05.bin",   0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92_06.bin",   0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92_07.bin",   0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92_08.bin",   0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92_10.bin",   0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92_11.bin",   0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92_12.bin",   0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92_13.bin",   0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.bin",    0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END



ROM_START( sf2amf )
	/* the PCB is not working on real hardware */
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "5.amf",          0x000000, 0x80000, CRC(03991fba) SHA1(6c42bf15248640fdb3e98fb01b0a870649deb410) )
	ROM_LOAD16_BYTE( "4.amf",          0x000001, 0x80000, CRC(39f15a1e) SHA1(901c4fea76bf5bff7330ed07ffde54cdccdaa680) )
	/* missing last part(s) of program roms, some gfx loading instructions are missing */
	ROM_LOAD16_BYTE( "prg part 3.amf", 0x100000, 0x40000, NO_DUMP )
	ROM_LOAD16_BYTE( "prg part 4.amf", 0x100001, 0x40000, NO_DUMP )
	/* do not move this outside comments, this is only for testing purpose
	ROM_LOAD16_BYTE( "u221.rom",   0x100000, 0x20000, CRC(64e6e091) SHA1(32ec05db955e538d4ada26d19ee50926f74b684f) )
	ROM_LOAD16_BYTE( "u195.rom",   0x100001, 0x20000, CRC(c95e4443) SHA1(28417dee9ccdfa65b0f4a92aa29b90279fe8cd85) )
	*/

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "y.c.e.c m.k.r-001", 0x000000, 0x80000, CRC(a258de13) SHA1(2e477948c4c8a2fb7cfdc4a739766bc4a4e01c49) )
	ROM_CONTINUE(                         0x000004, 0x80000)
	ROM_LOAD64_WORD( "y.c.e.c m.k.r-003", 0x000002, 0x80000, CRC(c781bf87) SHA1(034baa9807c2ce8dc800200963a38cd9262b21fb) )
	ROM_CONTINUE(                         0x000006, 0x80000)
	ROM_LOAD64_WORD( "y.c.e.c m.k.r-002", 0x200000, 0x80000, CRC(5726cab8) SHA1(0b2243a9a7184d53d42ddab7a8c51b63001c2f56) )
	ROM_CONTINUE(                         0x200004, 0x80000)
	ROM_LOAD64_WORD( "y.c.e.c d.w.c-011", 0x200002, 0x80000, CRC(bc90c12f) SHA1(ecdb776239b22bd56b7c3a87c9e561f650a4dfea) )
	ROM_CONTINUE(                         0x200006, 0x80000)
	ROM_LOAD64_WORD( "y.c.e.c d.w.c-012", 0x400000, 0x80000, CRC(187667cc) SHA1(fae65bf23f49a32903fda8080659ccf8d42b911f) )
	ROM_CONTINUE(                         0x400004, 0x80000)
	ROM_LOAD64_WORD( "y.c.e.c d.w.c-013", 0x400002, 0x80000, CRC(5b585071) SHA1(ad3371b1ba0441c67d9fcbb23b09464710e4e28a) )
	ROM_CONTINUE(                         0x400006, 0x80000)
	/* extra gfx layer roms loaded over the former ones to remove the capcom copyright logo */
	ROM_LOAD64_BYTE( "6.amf",             0x400000, 0x10000, CRC(3a85a275) SHA1(01907d69f912abffe3ad9745638ce3f282cfb2e8) )
	ROM_CONTINUE(                         0x400004, 0x10000 )
	ROM_LOAD64_BYTE( "9.amf",             0x400002, 0x10000, CRC(9156472f) SHA1(5db2acfc54308d4d26e0459f9486620a968c81d8) )
	ROM_CONTINUE(                         0x400006, 0x10000 )
	ROM_LOAD64_BYTE( "8.amf",             0x400001, 0x10000, CRC(ecdb083b) SHA1(899894c1db004e98f755ffbdf28d32296b9c0a86) )
	ROM_CONTINUE(                         0x400005, 0x10000 )
	ROM_LOAD64_BYTE( "10.amf",            0x400003, 0x10000, CRC(8fea8384) SHA1(8b31fd8d16cbafb5144f772653336b41db8f64fc) )
	ROM_CONTINUE(                         0x400007, 0x10000 )
	/* end of extra gfx layer roms */

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "3.amf", 0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(      0x10000, 0x08000 )

	ROM_REGION( 0x20000, "user1", 0 ) /* unknown (bootleg priority?) */
	ROM_LOAD( "7.amf", 0x00000, 0x10000, CRC(13ea1c44) SHA1(5b05fe4c3920e33d94fac5f59e09ff14b3e427fe) )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "2.amf", 0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "1.amf", 0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

ROM_START( sf2amf2 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "m5m27c401.u222",          0x000000, 0x80000, CRC(03991fba) SHA1(6c42bf15248640fdb3e98fb01b0a870649deb410) ) // ==  5.amf  sf2amf
	ROM_LOAD16_BYTE( "m5m27c401.u196",          0x000001, 0x80000, CRC(39f15a1e) SHA1(901c4fea76bf5bff7330ed07ffde54cdccdaa680) ) // ==  4.amf  sf2amf

	ROM_LOAD16_BYTE( "27020.u221",   0x100000, 0x40000, CRC(aa4d55a6) SHA1(8fd1c21816886a7734aae42e9336d5f66ddab7bc) ) // different
	ROM_LOAD16_BYTE( "27020.u195",   0x100001, 0x40000, CRC(2bffa6f9) SHA1(eb1222356d89849edb08ea1898399cf90cf127f5) ) // different

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "fun-u70.bin", 0x000004, 0x80000, CRC(a94a8b19) SHA1(49ba9e6032a0b33d7db9fe609710575f2f75e695) )  // different
	ROM_CONTINUE(                   0x000000, 0x80000)
	ROM_LOAD64_WORD( "fun-u68.bin", 0x000006, 0x80000, CRC(0405f21f) SHA1(dbebd2c2c46d5aae8db905f2eb51abd4a5c4ea97) )  // different
	ROM_CONTINUE(                   0x000002, 0x80000)
	ROM_LOAD64_WORD( "fun-u69.bin", 0x200004, 0x80000, CRC(05dc2043) SHA1(d16b89a48d2dd7cdfafc79567ce1e230d4bd41c1) )  // different
	ROM_CONTINUE(                   0x200000, 0x80000)
	ROM_LOAD64_WORD( "fun-u67.bin", 0x200006, 0x80000, CRC(055b64f1) SHA1(3dd68f52b81ed1b300b65c900ef6bfe435d41e4b) )  // different
	ROM_CONTINUE(                   0x200002, 0x80000)
	ROM_LOAD64_WORD( "fun-u19.bin", 0x400004, 0x80000, CRC(1a518609) SHA1(18ffca70d6cefb399ba6e3008e5c29dc37de52a0) )  // different
	ROM_CONTINUE(                   0x400000, 0x80000)
	ROM_LOAD64_WORD( "fun-u18.bin", 0x400006, 0x80000, CRC(84f9354f) SHA1(ecc190950b1f45b268da380c17859a8d0715b58f) )  // different
	ROM_CONTINUE(                   0x400002, 0x80000)
	/* extra gfx layer roms loaded over the former ones to remove the capcom copyright logo */
	ROM_LOAD64_BYTE( "grp1.u31",    0x400004, 0x10000, CRC(6de44671) SHA1(dc6abba639e0c27033e391c7438d88dc89a93351) )  // different
	ROM_CONTINUE(                   0x400000, 0x10000 )
	ROM_LOAD64_BYTE( "grp3.u29",    0x400006, 0x10000, CRC(e8f14362) SHA1(a20eb75e322011e2a8d8bf2acebe713bef3d3941) )  // different
	ROM_CONTINUE(                   0x400002, 0x10000 )
	ROM_LOAD64_BYTE( "grp2.u30",    0x400005, 0x10000, CRC(bf0cd819) SHA1(f04a098fce07949277268327871c5e5520e3bb3c) )  // different
	ROM_CONTINUE(                   0x400001, 0x10000 )
	ROM_LOAD64_BYTE( "grp4.u28",    0x400007, 0x10000, CRC(76f9f91f) SHA1(58a34062d2c8378558a7f1629140330279af9a43) )  // different
	ROM_CONTINUE(                   0x400003, 0x10000 )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "27512.u191", 0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x20000, "user1", 0 ) /* unknown (bootleg priority?) */
	ROM_LOAD( "27512.u133", 0x00000, 0x10000, CRC(13ea1c44) SHA1(5b05fe4c3920e33d94fac5f59e09ff14b3e427fe) )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "fun-u210.bin", 0x00000, 0x40000, CRC(6cfffb11) SHA1(995526183ffd35f92e9096500a3fe6237faaa2dd) )
ROM_END

/* This set is identical to sf2amf2 except for program roms, the pcb has some kind of mod around the rom area with cut traces
   and a17 pins of u221 and u195 bent out of their sockets and connected together with a wire.
   Perhaps it's an "upgraded" sf2amf2 board ? */
ROM_START( sf2amf3 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "u222.bin", 0x000000, 0x80000, CRC(0d305e8b) SHA1(7094160abbf24c119a575d93e3fe1ab84b537de0) )
	ROM_LOAD16_BYTE( "u196.bin", 0x000001, 0x80000, CRC(137d8665) SHA1(cf4805a11ab614ce5b7e1302ac14ba50fb01e5f4) )
	ROM_LOAD16_BYTE( "u221.bin", 0x100000, 0x40000, CRC(0b3fe5dd) SHA1(9b66cb867da61595f53d1c9e6b48c6bb7e06e1e0) )
	ROM_LOAD16_BYTE( "u195.bin", 0x100001, 0x40000, CRC(dbee7b18) SHA1(e56af12fc9d30e92d37e688ff621ea09abb94b53) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "fun-u70.bin", 0x000004, 0x80000, CRC(a94a8b19) SHA1(49ba9e6032a0b33d7db9fe609710575f2f75e695) )
	ROM_CONTINUE(                   0x000000, 0x80000)
	ROM_LOAD64_WORD( "fun-u68.bin", 0x000006, 0x80000, CRC(0405f21f) SHA1(dbebd2c2c46d5aae8db905f2eb51abd4a5c4ea97) )
	ROM_CONTINUE(                   0x000002, 0x80000)
	ROM_LOAD64_WORD( "fun-u69.bin", 0x200004, 0x80000, CRC(05dc2043) SHA1(d16b89a48d2dd7cdfafc79567ce1e230d4bd41c1) )
	ROM_CONTINUE(                   0x200000, 0x80000)
	ROM_LOAD64_WORD( "fun-u67.bin", 0x200006, 0x80000, CRC(055b64f1) SHA1(3dd68f52b81ed1b300b65c900ef6bfe435d41e4b) )
	ROM_CONTINUE(                   0x200002, 0x80000)
	ROM_LOAD64_WORD( "fun-u19.bin", 0x400004, 0x80000, CRC(1a518609) SHA1(18ffca70d6cefb399ba6e3008e5c29dc37de52a0) )
	ROM_CONTINUE(                   0x400000, 0x80000)
	ROM_LOAD64_WORD( "fun-u18.bin", 0x400006, 0x80000, CRC(84f9354f) SHA1(ecc190950b1f45b268da380c17859a8d0715b58f) )
	ROM_CONTINUE(                   0x400002, 0x80000)
	/* extra gfx layer roms loaded over the former ones to remove the capcom copyright logo */
	ROM_LOAD64_BYTE( "grp1.u31",    0x400004, 0x10000, CRC(6de44671) SHA1(dc6abba639e0c27033e391c7438d88dc89a93351) )
	ROM_CONTINUE(                   0x400000, 0x10000 )
	ROM_LOAD64_BYTE( "grp3.u29",    0x400006, 0x10000, CRC(e8f14362) SHA1(a20eb75e322011e2a8d8bf2acebe713bef3d3941) )
	ROM_CONTINUE(                   0x400002, 0x10000 )
	ROM_LOAD64_BYTE( "grp2.u30",    0x400005, 0x10000, CRC(bf0cd819) SHA1(f04a098fce07949277268327871c5e5520e3bb3c) )
	ROM_CONTINUE(                   0x400001, 0x10000 )
	ROM_LOAD64_BYTE( "grp4.u28",    0x400007, 0x10000, CRC(76f9f91f) SHA1(58a34062d2c8378558a7f1629140330279af9a43) )
	ROM_CONTINUE(                   0x400003, 0x10000 )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "27512.u191", 0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x20000, "user1", 0 ) /* unknown (bootleg priority?) */
	ROM_LOAD( "27512.u133", 0x00000, 0x10000, CRC(13ea1c44) SHA1(5b05fe4c3920e33d94fac5f59e09ff14b3e427fe) )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "fun-u210.bin", 0x00000, 0x40000, CRC(6cfffb11) SHA1(995526183ffd35f92e9096500a3fe6237faaa2dd) )
ROM_END

ROM_START( sf2rules ) //
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "prh2.u222",          0x000000, 0x80000, CRC(fff85f9b) SHA1(5e5bc7da471fe15011b91f8c27823fbdace3eac1) )
	ROM_LOAD16_BYTE( "prl1.u196",          0x000001, 0x80000, CRC(65c28bc9) SHA1(4f9c0e5062f00f115c3b471c7649d0b537cb3575) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "ycecmkr001.u70", 0x000000, 0x80000, CRC(a258de13) SHA1(2e477948c4c8a2fb7cfdc4a739766bc4a4e01c49) )
	ROM_CONTINUE(                      0x000004, 0x80000)
	ROM_LOAD64_WORD( "ycecmkr003.u69", 0x000002, 0x80000, CRC(c781bf87) SHA1(034baa9807c2ce8dc800200963a38cd9262b21fb) )
	ROM_CONTINUE(                      0x000006, 0x80000)
	ROM_LOAD64_WORD( "ycecmkr002.u68", 0x200000, 0x80000, CRC(5726cab8) SHA1(0b2243a9a7184d53d42ddab7a8c51b63001c2f56) )
	ROM_CONTINUE(                      0x200004, 0x80000)
	ROM_LOAD64_WORD( "ycecdwc011.u64", 0x200002, 0x80000, CRC(bc90c12f) SHA1(ecdb776239b22bd56b7c3a87c9e561f650a4dfea) )
	ROM_CONTINUE(                      0x200006, 0x80000)
	ROM_LOAD64_WORD( "ycecdwc012.u19", 0x400000, 0x80000, CRC(187667cc) SHA1(fae65bf23f49a32903fda8080659ccf8d42b911f) )
	ROM_CONTINUE(                      0x400004, 0x80000)
	ROM_LOAD64_WORD( "ycecdwc013.u18", 0x400002, 0x80000, CRC(5b585071) SHA1(ad3371b1ba0441c67d9fcbb23b09464710e4e28a) )
	ROM_CONTINUE(                      0x400006, 0x80000)
	/* extra gfx layer roms loaded over the former ones to remove the capcom copyright logo */
	ROM_LOAD64_BYTE( "grp1.u31",       0x400004, 0x10000, CRC(6de44671) SHA1(dc6abba639e0c27033e391c7438d88dc89a93351) )  // different
	ROM_CONTINUE(                      0x400000, 0x10000 )
	ROM_LOAD64_BYTE( "grp3.u29",       0x400006, 0x10000, CRC(e8f14362) SHA1(a20eb75e322011e2a8d8bf2acebe713bef3d3941) )  // different
	ROM_CONTINUE(                      0x400002, 0x10000 )
	ROM_LOAD64_BYTE( "grp2.u30",       0x400005, 0x10000, CRC(bf0cd819) SHA1(f04a098fce07949277268327871c5e5520e3bb3c) )  // different
	ROM_CONTINUE(                      0x400001, 0x10000 )
	ROM_LOAD64_BYTE( "grp4.u28",       0x400007, 0x10000, CRC(76f9f91f) SHA1(58a34062d2c8378558a7f1629140330279af9a43) )  // different
	ROM_CONTINUE(                      0x400003, 0x10000 )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sound.u191", 0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x20000, "user1", 0 ) /* unknown (bootleg priority?) */
	ROM_LOAD( "conv.u133", 0x00000, 0x10000, CRC(13ea1c44) SHA1(5b05fe4c3920e33d94fac5f59e09ff14b3e427fe) )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "voice.u210", 0x00000, 0x40000, CRC(6cfffb11) SHA1(995526183ffd35f92e9096500a3fe6237faaa2dd) )
ROM_END

ROM_START( sf2dkot2 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	/* The game reads values from 0x201201 and 0x281201 (at PC=E5452) and uses their difference to form a jump offset. */
	/* This arrangement seems to work */
	ROM_LOAD16_WORD_SWAP( "turboii.23", 0x000000, 0x80000, CRC(9bbfe420) SHA1(299ab5625f2d4ebc89bcf19a2b2d88edc8054747) )
	ROM_RELOAD(                         0x280000, 0x80000 )
	ROM_LOAD16_WORD_SWAP( "turboii.22", 0x080000, 0x80000, CRC(3e57ba19) SHA1(ae8170ac890c14ba7746e29c0f21dfb38c4c9837) )
	ROM_RELOAD(                         0x200000, 0x80000 )

	ROM_LOAD16_WORD_SWAP( "turboii.21", 0x100000, 0x80000, CRC(ed4186bd) SHA1(f3dfe91d8f4384275190b0d86488843c1161d86f) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.bin",   0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92_02.bin",   0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92_03.bin",   0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92_04.bin",   0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92_05.bin",   0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92_06.bin",   0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92_07.bin",   0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92_08.bin",   0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92_10.bin",   0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92_11.bin",   0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92_12.bin",   0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92_13.bin",   0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.bin",    0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

ROM_START( sf2m2 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "ch222esp",          0x000000, 0x80000, CRC(9e6d058a) SHA1(8c9adca7b65dc929c325c0a62304d24dc0902c08) )
	ROM_LOAD16_BYTE( "ch196esp",          0x000001, 0x80000, CRC(ed2ff437) SHA1(e76fc2953b6c800d5955c8fb442b80142e40e375) )
	ROM_LOAD16_WORD_SWAP( "s92_21a.bin",  0x100000, 0x80000, CRC(925a7877) SHA1(1960dca35f0ca6f2b399a9fccfbc0132ac6425d1) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.bin",   0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92_02.bin",   0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92_03.bin",   0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92_04.bin",   0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92_05.bin",   0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92_06.bin",   0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92_07.bin",   0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92_08.bin",   0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92_10.bin",   0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92_11.bin",   0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92_12.bin",   0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92_13.bin",   0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.bin",    0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

ROM_START( sf2m3 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "u222chp",           0x000000, 0x80000, CRC(db567b66) SHA1(315bfbf2786ef67a95afb87de836ab348523dbbe) )
	ROM_LOAD16_BYTE( "u196chp",           0x000001, 0x80000, CRC(95ea597e) SHA1(5eb82feaa1de5611a96888e4670744bbb7d90393) )
	ROM_LOAD16_WORD_SWAP( "s92_21a.bin",  0x100000, 0x80000, CRC(925a7877) SHA1(1960dca35f0ca6f2b399a9fccfbc0132ac6425d1) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.bin",   0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92_02.bin",   0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92_03.bin",   0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92_04.bin",   0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92_05.bin",   0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92_06.bin",   0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92_07.bin",   0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92_08.bin",   0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92_10.bin",   0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92_11.bin",   0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92_12.bin",   0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92_13.bin",   0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.bin",    0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

ROM_START( sf2m4 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "u222ne",            0x000000, 0x80000, CRC(7133489e) SHA1(036ef100c64c6e912c911340b32eea0da0b6f6d9) )
	ROM_LOAD16_BYTE( "u196ne",            0x000001, 0x80000, CRC(b07a4f90) SHA1(7a4a800bddc43cfa60f9097723b44a05c9d290ae) )
	ROM_LOAD16_WORD_SWAP( "s92_21a.bin",  0x100000, 0x80000, CRC(925a7877) SHA1(1960dca35f0ca6f2b399a9fccfbc0132ac6425d1) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.bin",   0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92_02.bin",   0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92_03.bin",   0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92_04.bin",   0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92_05.bin",   0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92_06.bin",   0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92_07.bin",   0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92_08.bin",   0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92_10.bin",   0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92_11.bin",   0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92_12.bin",   0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92_13.bin",   0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.bin",    0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

ROM_START( sf2m5 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "u222",              0x000000, 0x80000, CRC(03991fba) SHA1(6c42bf15248640fdb3e98fb01b0a870649deb410) )
	ROM_LOAD16_BYTE( "u196",              0x000001, 0x80000, CRC(39f15a1e) SHA1(901c4fea76bf5bff7330ed07ffde54cdccdaa680) )
	ROM_LOAD16_WORD_SWAP( "s92_21a.bin",  0x100000, 0x80000, CRC(925a7877) SHA1(1960dca35f0ca6f2b399a9fccfbc0132ac6425d1) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.bin",   0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92_02.bin",   0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92_03.bin",   0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92_04.bin",   0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92_05.bin",   0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92_06.bin",   0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92_07.bin",   0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92_08.bin",   0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92_10.bin",   0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92_11.bin",   0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92_12.bin",   0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92_13.bin",   0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.bin",    0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

ROM_START( sf2m6 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "27c040.u222", 0x000000, 0x80000, CRC(0a3692be) SHA1(7b937b7b0130e460b5f12188b19f464c55b507c9) )
	ROM_LOAD16_BYTE( "27c040.u196", 0x000001, 0x80000, CRC(80454da7) SHA1(64f6dba14d342c9933ce632aa7ca126b34b4ee8b) )
	ROM_LOAD16_BYTE( "27c010.u221", 0x100000, 0x20000, CRC(8226c11c) SHA1(9588bd64e338901394805aca8a234f880674dc60) )
	ROM_LOAD16_BYTE( "27c010.u195", 0x100001, 0x20000, CRC(924c6ce2) SHA1(676a912652bd75da5087f0c7eae047b7681a993c) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "ycecmkr001.u70", 0x000000, 0x80000, CRC(a258de13) SHA1(2e477948c4c8a2fb7cfdc4a739766bc4a4e01c49) )
	ROM_CONTINUE(                      0x000004, 0x80000)
	ROM_LOAD64_WORD( "ycecmkr003.u69", 0x000002, 0x80000, CRC(c781bf87) SHA1(034baa9807c2ce8dc800200963a38cd9262b21fb) )
	ROM_CONTINUE(                      0x000006, 0x80000)
	ROM_LOAD64_WORD( "ycecmkr002.u68", 0x200000, 0x80000, CRC(5726cab8) SHA1(0b2243a9a7184d53d42ddab7a8c51b63001c2f56) )
	ROM_CONTINUE(                      0x200004, 0x80000)
	ROM_LOAD64_WORD( "ycecdwc011.u64", 0x200002, 0x80000, CRC(bc90c12f) SHA1(ecdb776239b22bd56b7c3a87c9e561f650a4dfea) )
	ROM_CONTINUE(                      0x200006, 0x80000)
	ROM_LOAD64_WORD( "ycecdwc012.u19", 0x400000, 0x80000, CRC(187667cc) SHA1(fae65bf23f49a32903fda8080659ccf8d42b911f) )
	ROM_CONTINUE(                      0x400004, 0x80000)
	ROM_LOAD64_WORD( "ycecdwc013.u18", 0x400002, 0x80000, CRC(5b585071) SHA1(ad3371b1ba0441c67d9fcbb23b09464710e4e28a) )
	ROM_CONTINUE(                      0x400006, 0x80000)
	/* extra gfx layer roms loaded over the former ones to remove the capcom copyright logo */
	ROM_LOAD64_BYTE( "grp1.u31",       0x400004, 0x10000, CRC(6de44671) SHA1(dc6abba639e0c27033e391c7438d88dc89a93351) )  // different
	ROM_CONTINUE(                      0x400000, 0x10000 )
	ROM_LOAD64_BYTE( "grp3.u29",       0x400006, 0x10000, CRC(e8f14362) SHA1(a20eb75e322011e2a8d8bf2acebe713bef3d3941) )  // different
	ROM_CONTINUE(                      0x400002, 0x10000 )
	ROM_LOAD64_BYTE( "grp2.u30",       0x400005, 0x10000, CRC(bf0cd819) SHA1(f04a098fce07949277268327871c5e5520e3bb3c) )  // different
	ROM_CONTINUE(                      0x400001, 0x10000 )
	ROM_LOAD64_BYTE( "grp4.u28",       0x400007, 0x10000, CRC(76f9f91f) SHA1(58a34062d2c8378558a7f1629140330279af9a43) )  // different
	ROM_CONTINUE(                      0x400003, 0x10000 )

	ROM_REGION( 0x20000, "user1", 0 ) /* unknown (bootleg priority?) */
	ROM_LOAD( "conv.u133", 0x00000, 0x10000, CRC(13ea1c44) SHA1(5b05fe4c3920e33d94fac5f59e09ff14b3e427fe) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sound.u191", 0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "voice.u210", 0x00000, 0x40000, CRC(6cfffb11) SHA1(995526183ffd35f92e9096500a3fe6237faaa2dd) )
ROM_END

ROM_START( sf2m7 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "u222-2i",           0x000000, 0x40000, CRC(1ca7adbd) SHA1(45e9dc05766ad156edcfc9e59a9804f74f90dc68) )
	ROM_LOAD16_BYTE( "u196-2i",           0x000001, 0x40000, CRC(f758408c) SHA1(aac44a7287bb3b7ba35d68aff279e265dbd3f6d3) )
	ROM_LOAD16_BYTE( "u222-2s",           0x080000, 0x40000, CRC(720cea3e) SHA1(ec4f22159d44a8abc40643b986b88a4f947d6aea) )
	ROM_LOAD16_BYTE( "u196-2s",           0x080001, 0x40000, CRC(9932832c) SHA1(0da0f5ebab91b0759c5fc00902cfe4b12a856466) )
	ROM_LOAD16_WORD_SWAP( "s92_21a.bin",  0x100000, 0x80000, CRC(925a7877) SHA1(1960dca35f0ca6f2b399a9fccfbc0132ac6425d1) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.bin",   0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92_02.bin",   0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92_03.bin",   0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92_04.bin",   0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92_05.bin",   0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92_06.bin",   0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92_07.bin",   0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92_08.bin",   0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92_10.bin",   0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92_11.bin",   0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92_12.bin",   0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92_13.bin",   0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.bin",    0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

ROM_START( sf2m8 )
/* unconfirmed if working on real hardware */
	/* this board has unsupported pals */
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "yyc-2.2", 0x000000, 0x80000, CRC(db567b66) SHA1(315bfbf2786ef67a95afb87de836ab348523dbbe) )
	ROM_LOAD16_BYTE( "yyc-3.4", 0x000001, 0x80000, CRC(95ea597e) SHA1(5eb82feaa1de5611a96888e4670744bbb7d90393) )
	ROM_LOAD16_BYTE( "yyc-4.1", 0x100000, 0x20000, CRC(1073b7b6) SHA1(81ca1eab65ceac69520584bb23a684ccb9d92f89) )
	ROM_LOAD16_BYTE( "yyc-5.3", 0x100001, 0x20000, CRC(924c6ce2) SHA1(676a912652bd75da5087f0c7eae047b7681a993c) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_BYTE( "yyc-a",    0x000000, 0x40000, CRC(8242621f) SHA1(9d566176462bd25f9a377104b0c78a962708bc2b) )
	ROM_CONTINUE(                0x000004, 0x40000)
	ROM_CONTINUE(                0x200000, 0x40000)
	ROM_CONTINUE(                0x200004, 0x40000)
	ROM_LOAD64_BYTE( "yyc-c",    0x000001, 0x40000, CRC(0793a960) SHA1(f351163dd1090f8cd8d4c77e2a29764fee627b13) )
	ROM_CONTINUE(                0x000005, 0x40000)
	ROM_CONTINUE(                0x200001, 0x40000)
	ROM_CONTINUE(                0x200005, 0x40000)
	ROM_LOAD64_BYTE( "yyc-b",    0x000002, 0x40000, CRC(b0159973) SHA1(2a236b9d98fa0acddd844aa94bc5118012a6fb2f) )
	ROM_CONTINUE(                0x000006, 0x40000)
	ROM_CONTINUE(                0x200002, 0x40000)
	ROM_CONTINUE(                0x200006, 0x40000)
	ROM_LOAD64_BYTE( "yyc-d",    0x000003, 0x40000, CRC(92a8b572) SHA1(cbad24e519f0152989764c054da914f55e2b118c) )
	ROM_CONTINUE(                0x000007, 0x40000)
	ROM_CONTINUE(                0x200003, 0x40000)
	ROM_CONTINUE(                0x200007, 0x40000)
	/* extra gfx layer roms loaded over the former ones to remove the capcom copyright logo */
	ROM_LOAD64_BYTE( "yyc-6.1",  0x400000, 0x10000, CRC(94778332) SHA1(c0b9a05c710b89864ee5df1a53b39de30c994e2d) )
	ROM_CONTINUE(                0x400004, 0x10000 )
	ROM_LOAD64_BYTE( "yyc-8.9",  0x400002, 0x10000, CRC(f95bc505) SHA1(385beb2f9f8a473d928d729d722372ae49f410e7) )
	ROM_CONTINUE(                0x400006, 0x10000 )
	ROM_LOAD64_BYTE( "yyc-7.10", 0x400001, 0x10000, CRC(d1e452d3) SHA1(794f7ebf6c46c2938a5477451cf05f2e0c7b9049) )
	ROM_CONTINUE(                0x400005, 0x10000 )
	ROM_LOAD64_BYTE( "yyc-9.8",  0x400003, 0x10000, CRC(155824a9) SHA1(74d7e86be22c11234f5d9d0b25fa709b59ef471b) )
	ROM_CONTINUE(                0x400007, 0x10000 )
	/* end of extra gfx layer roms */

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "a-15.5", 0x00000, 0x08000, CRC(6f07d2cb) SHA1(8ef1338d04c1a0b43e24303085105cfdced0bd5e) )
	ROM_CONTINUE(       0x10000, 0x08000 )

	ROM_REGION( 0x10000, "user1", 0 ) /* unknown (bootleg priority?) */
	ROM_LOAD( "c-27.7", 0x00000, 0x10000, CRC(13ea1c44) SHA1(5b05fe4c3920e33d94fac5f59e09ff14b3e427fe) )

	ROM_REGION( 0x200000, "user2", 0 )
	ROM_LOAD( "yyc-e",    0x000000, 0x100000, CRC(61138469) SHA1(dec3b3af6e3f4fedf51600ddf0515f61b2122493) )
	ROM_LOAD( "yyc-f",    0x100000, 0x100000, CRC(b800dcdb) SHA1(2ec3251b78159b15032d55a5ee5138f159e67190) )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "b-16.6", 0x00000, 0x40000, CRC(6cfffb11) SHA1(995526183ffd35f92e9096500a3fe6237faaa2dd) )
ROM_END

/* SF2M10
 From HBMAME (SF2H14)
 Same as sf2ebbl2, except it has program roms unique to sf2h14.

 Using sf2ce gfx roms to get clean gfx (the board was a bodgy
 half-done conversion attempt).

** PCB DETAILS ***
Street Fighter II Champion Edition (bootleg)

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

|-----------------------------------------------------|
|LM324 ROM1  3.579545MHz                   ROM10 ROM12|
|LM324 ROM2  16MHz 2018                    ROM9  ROM11|
|     M6295  12MHz 2018                    SE235 SE005|
|                  2018                               |
|YM2151  2018      2018             ROM8              |
|    ROM3                                  SE194 SE064|
|J    Z80                                  SF004 SF001|
|A                             2018 2018              |
|M     6116 6116               2018 2018              |
|M                                                    |
|A      |--|                                          |
|       |6 |                   62256                  |
|DSWA(8)|8 |62256 62256        62256                  |
|       |0 |ROM4  ROM6                                |
|DSWB(8)|0 |ROM5  ROM7                                |
|       |0 |                                          |
|DSWC(8)|--|                                          |
|-----------------------------------------------------|
Notes:
      68000 - Clock 12MHz
        Z80 - Clock 3.579545MHz
      M6295 - Clock 1.000MHz [16/16]. Pin 7 HIGH
     YM2151 - Clock 3.579545MHz
      62256 - 32k x8 SRAM
       2018 - 2k x8 SRAM
       6116 - 2k x8 SRAM
*/

ROM_START( sf2m10 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )
	ROM_LOAD16_BYTE( "sf2h14.7", 0x000000, 0x80000, CRC(74803532) SHA1(c1f774bbc4c7b18fcac15417711a86eb852b9957) )
	ROM_LOAD16_BYTE( "sf2h14.5", 0x000001, 0x80000, CRC(66c91972) SHA1(219aecad1feb60bb758190ea82223171075c858e) )
	ROM_LOAD16_BYTE( "yyc-4.1", 0x100000, 0x20000, CRC(1073b7b6) SHA1(81ca1eab65ceac69520584bb23a684ccb9d92f89) )
	ROM_LOAD16_BYTE( "yyc-5.3", 0x100001, 0x20000, CRC(924c6ce2) SHA1(676a912652bd75da5087f0c7eae047b7681a993c) )

	ROM_REGION( 0x600000, "gfx", 0 )
	// using the proper roms for this program
	ROM_LOAD64_WORD( "s92-1m.3a",  0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92-3m.5a",  0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92-2m.4a",  0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92-4m.6a",  0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92-5m.7a",  0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92-7m.9a",  0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92-6m.8a",  0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92-8m.10a", 0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92-10m.3c", 0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92-12m.5c", 0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92-11m.4c", 0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92-13m.6c", 0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )
	// and overwrite some gfx with supplied extra roms
	ROM_LOAD64_WORD( "27c1024.10", 0x400000, 0x20000, CRC(84427d1b) SHA1(f988a2b53c8cc46eeb8032084f24966a539b3734) )
	ROM_LOAD64_WORD( "27c1024.12", 0x400002, 0x20000, CRC(55bc790c) SHA1(a1114b89f6fa4487210477676984c77ad94b5ef8) )
	ROM_LOAD64_WORD( "27c1024.9",  0x400004, 0x20000, CRC(f8725add) SHA1(fa3fcf6637ee4dd7667bd89766074b3c6ba4f166) )
	ROM_LOAD64_WORD( "27c1024.11", 0x400006, 0x20000, CRC(c2a5373e) SHA1(602b32e5ecc7007efe9ad30751040ee52b81f59a) )

	ROM_REGION( 0x18000, "audiocpu", 0 )
	ROM_LOAD( "27c512.3",    0x00000, 0x08000, CRC(a4823a1b) SHA1(7b6bf59dfd578bfbbdb64c27988796783442d659) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x20000, "user1", 0 )
	ROM_LOAD( "27c512.8",    0x00000, 0x10000, CRC(13ea1c44) SHA1(5b05fe4c3920e33d94fac5f59e09ff14b3e427fe) )

	ROM_REGION( 0x40000, "oki", 0 )
	ROM_LOAD( "27c010.2",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "27c010.1",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

ROM_START( sf2yyc )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "b12.rom", 0x000000, 0x80000, CRC(8f742fd5) SHA1(a78a00e686856481011d8b5f5e60ed18197a5225) )
	ROM_LOAD16_BYTE( "b14.rom", 0x000001, 0x80000, CRC(8831ec7f) SHA1(0293ff189cbacf90098e734fb31fcbf3c3165e6b) )
	ROM_LOAD16_BYTE( "b11.rom", 0x100000, 0x20000, CRC(94a46525) SHA1(2712b979ce2bfd87e74da3369e0fceaae2a0654c) )
	ROM_RELOAD(                 0x140000, 0x20000 )
	ROM_LOAD16_BYTE( "b13.rom", 0x100001, 0x20000, CRC(8fb3dd47) SHA1(ebf30ad7ae60eeda446e23bd74f6e2d98dde4158) )
	ROM_RELOAD(                 0x140001, 0x20000 )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.bin",   0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92_02.bin",   0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92_03.bin",   0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92_04.bin",   0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92_05.bin",   0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92_06.bin",   0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92_07.bin",   0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92_08.bin",   0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92_10.bin",   0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92_11.bin",   0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92_12.bin",   0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92_13.bin",   0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.bin",    0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

ROM_START( sf2koryu )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "u222.rom",   0x000000, 0x80000, CRC(9236a79a) SHA1(39c47b0b0ca2f5f569ff07ebb91040b95d0cb43b) )
	ROM_LOAD16_BYTE( "u196.rom",   0x000001, 0x80000, CRC(b23a869d) SHA1(24247d412f20d069919cc8a7fff208af3f7aa1d2) )
	ROM_LOAD16_BYTE( "u221.rom",   0x100000, 0x20000, CRC(64e6e091) SHA1(32ec05db955e538d4ada26d19ee50926f74b684f) )
	ROM_LOAD16_BYTE( "u195.rom",   0x100001, 0x20000, CRC(c95e4443) SHA1(28417dee9ccdfa65b0f4a92aa29b90279fe8cd85) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.bin",   0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92_02.bin",   0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92_03.bin",   0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92_04.bin",   0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92_05.bin",   0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92_06.bin",   0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92_07.bin",   0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92_08.bin",   0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92_10.bin",   0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92_11.bin",   0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92_12.bin",   0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92_13.bin",   0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.bin",    0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.bin",    0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.bin",    0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

ROM_START( sf2ceupl ) // only the program ROMs were dumped
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "s2.u222",  0x000000, 0x80000, CRC(0804f973) SHA1(3f87ccc9020523f3d9d390f165793f5e426185a3) ) // 27C040
	ROM_LOAD16_BYTE( "s1.u196",  0x000001, 0x80000, CRC(2bc76a02) SHA1(6a51a4be33e61d5c70e53d5f832298ef48572a17) ) // 27C040
	ROM_LOAD16_BYTE( "10.u221",  0x100000, 0x20000, CRC(d1707134) SHA1(5bfdf7bc57bdb85183647ebb175346070dd102ee) ) // 27C010
	ROM_LOAD16_BYTE( "9.u195",   0x100001, 0x20000, CRC(cd1d5666) SHA1(8befd2c324eb29bdad6fc8fb2554cdfaf9808f9b) ) // 27C010

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.3a",  0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )    // == s92-1m.3a
	ROM_LOAD64_WORD( "s92_02.4a",  0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )    // == s92-3m.5a
	ROM_LOAD64_WORD( "s92_03.5a",  0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )    // == s92-2m.4a
	ROM_LOAD64_WORD( "s92_04.6a",  0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )    // == s92-4m.6a
	ROM_LOAD64_WORD( "s92_05.7a",  0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )    // == s92-5m.7a
	ROM_LOAD64_WORD( "s92_06.8a",  0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )    // == s92-7m.9a
	ROM_LOAD64_WORD( "s92_07.9a",  0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )    // == s92-6m.8a
	ROM_LOAD64_WORD( "s92_08.10a", 0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )    // == s92-8m.10a
	ROM_LOAD64_WORD( "s92_10.3c",  0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )    // == s92-10m.3c
	ROM_LOAD64_WORD( "s92_11.4c",  0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )    // == s92-12m.5c
	ROM_LOAD64_WORD( "s92_12.5c",  0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )    // == s92-11m.4c
	ROM_LOAD64_WORD( "s92_13.6c",  0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )    // == s92-13m.6c

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.12a",  0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )   // == s92_09.11a
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s9263b.1a",    0x0000, 0x0117, CRC(0a7ecfe0) SHA1(f75e7eed4604fcf68273197fe3dd7f0d7a313ada) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

ROM_START( sf2dongb )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "1.8f",        0x000000, 0x80000, CRC(19fffa37) SHA1(db464c64de3aebe929c85ee167961de05d2c16be) )
	ROM_LOAD16_WORD_SWAP( "1.7f",        0x080000, 0x80000, CRC(99f1cca4) SHA1(64111eba81d743fc3fd51d7a89cd0b2eefcc900d) ) // same as sf2ceea
	ROM_LOAD16_WORD_SWAP( "1.6f",        0x100000, 0x80000, CRC(65c2c719) SHA1(d407cae5f04fc8d05776d478a6eb4363b3c36805) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92-1m.3a",  0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92-3m.5a",  0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92-2m.4a",  0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92-4m.6a",  0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92-5m.7a",  0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92-7m.9a",  0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92-6m.8a",  0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92-8m.10a", 0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92-10m.3c", 0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92-12m.5c", 0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92-11m.4c", 0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92-13m.6c", 0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.11a",  0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )
ROM_END

ROM_START( sf2level ) // program very similar to sf2dkot2
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "rj313.u196.800", 0x000000, 0x100000, CRC(435153d5) SHA1(3f6f318a9b3def8d62ee576dbaaef623d55c1c64) )
	ROM_LOAD16_WORD_SWAP( "ci030.u10.400",  0x100000, 0x080000, CRC(ed4186bd) SHA1(f3dfe91d8f4384275190b0d86488843c1161d86f) )

	ROM_REGION( 0x600000, "gfx", 0 ) // not dumped for this bootleg. Note from dumper: 'GFX is slightly different mask rom layout than others. (2x mask instead of a rack of 010's)'
	ROM_LOAD64_WORD( "s92_01.3a",  0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92_02.4a",  0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92_03.5a",  0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92_04.6a",  0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92_05.7a",  0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92_06.8a",  0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92_07.9a",  0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92_08.10a", 0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92_10.3c",  0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92_11.4c",  0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92_12.5c",  0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92_13.6c",  0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x20000, "user1", 0 ) /* unknown (bootleg priority?) */
	ROM_LOAD( "km6264-10.u133",    0x00000, 0x10000, CRC(13ea1c44) SHA1(5b05fe4c3920e33d94fac5f59e09ff14b3e427fe) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "km6264b-10.u191",  0x00000, 0x08000, CRC(6f07d2cb) SHA1(8ef1338d04c1a0b43e24303085105cfdced0bd5e) )
	ROM_CONTINUE(                 0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "km418c256z-80.u210",  0x00000, 0x40000, CRC(6cfffb11) SHA1(995526183ffd35f92e9096500a3fe6237faaa2dd) )
ROM_END

/*
    05/13/92 MSTREET-6

    There are quite a few variations of this board. The main differences seem to be the amount and size of roms around the 68k.
    Some variations have an additional "patch" rom and palce or gal that patches various chunks of code over the main program roms.
    This adds the usual "rainbow edition" style gameplay hacks, such as air fireballs, change character mid-game, etc.
    The boards can be run without the patch rom by removing it and the pal/gal and linking pins 1-19, 2-18, 11-16 of the empty gal socket.
    (There is also a jumper that can be soldered instead for the third connection.)

    Luckily one of my boards has an unprotected gal so I was able to decode the rom patching:

    patch rom            program space
    --------------------------------------
    0x00000-0x07fff  ->  0x000000-0x007fff
    0x08000-0x27fff  ->  0x030000-0x04ffff
    0x28000-0x37fff  ->  0x170000-0x17ffff

    Gfx:
    There are six 8Mbit roms which match the official sf2ce set almost exactly (7 bytes diff in one rom).
    There are also another two 2Mbit roms which contain very near identical data.
    Not sure why the bootleg hardware needs this or how it should be represented in emulation.
    There is also a 512Kbit rom that's purpose is unknown (possibly related to priority according to notes in other bootleg sets).

    Sound:
    YM2151 is clone marked "KA51".
    YM3012 is clone marked "KA12".
    MSM6295 is clone marked "TD735".

    Other:
    All roms have nonsense markings such as KM418C256, KM416C256 etc.
    Obviously they are not Samsung soj/tsop FPM DRAMs ;)
    Main clock is 10MHz, rather than usual 12MHZ for champion edition.
    Sets b and c:
      Turbo mode on SW(C):1.
      Press start to change character mid-game. (bug: screen goes dark when changing character, happens in attract mode as well).

    MSTREET-6 repair info:
    Frequent cause of dead board is u104 (gal/palce20v8) becoming corrupted somehow. Luckily a working unsecured chip was found and dumped :)
    Likely to also work on other similar boards (reference number may vary).

    B/C sets patch pals have different equations but are logically equivalent.
*/

#define SF2CEMS6_PLD_DEVICES \
	/* 16x PALCE16V8H-25PC/4 or TIBPAL16L8-25CN (OTP) */ \
	ROM_LOAD( "ms6_gal16v8.u6",    0x000, 0x117, NO_DUMP ) \
	ROM_LOAD( "ms6_gal16v8.u95",   0x000, 0x117, NO_DUMP ) \
	ROM_LOAD( "ms6_gal16v8.u96",   0x000, 0x117, NO_DUMP ) \
	ROM_LOAD( "ms6_gal16v8.u107",  0x000, 0x117, NO_DUMP ) \
	ROM_LOAD( "ms6_gal16v8.u125",  0x000, 0x117, NO_DUMP ) \
	ROM_LOAD( "ms6_gal16v8.u139",  0x000, 0x117, NO_DUMP ) \
	ROM_LOAD( "ms6_gal16v8.u151",  0x000, 0x117, NO_DUMP ) \
	ROM_LOAD( "ms6_gal16v8.u173",  0x000, 0x117, CRC(32dec205) SHA1(aaf1579b00f30b5bec86e89db8c7f0c3ad7a790d) )  /* unprotected */ \
	ROM_LOAD( "ms6_gal16v8.u176",  0x200, 0x117, CRC(deb37f27) SHA1(776d5185eece102d6e340ab5c2f7ddc4483ccda5) )  /* unprotected */ \
	ROM_LOAD( "ms6_pal16l8.u177",  0x000, 0x117, NO_DUMP ) \
	ROM_LOAD( "ms6_pal16l8.u178",  0x000, 0x117, NO_DUMP ) \
	ROM_LOAD( "ms6_pal16l8.u183",  0x000, 0x117, NO_DUMP ) \
	ROM_LOAD( "ms6_gal16v8.u198",  0x400, 0x117, CRC(cd1246fe) SHA1(b6d311f3c8dd6b411059eb60b22b33483b59bff8) )  /* unprotected */ \
	ROM_LOAD( "ms6_pal16l8.u218",  0x000, 0x117, NO_DUMP ) \
	ROM_LOAD( "ms6_pal16l8.u231",  0x000, 0x117, NO_DUMP ) \
	ROM_LOAD( "ms6_gal16v8.u245",  0x000, 0x117, NO_DUMP ) \
	/* 14x PALCE20V8H-25PC/4 */ \
	ROM_LOAD( "ms6_gal20v8.u15",   0x000, 0x157, NO_DUMP ) \
	ROM_LOAD( "ms6_gal20v8.u16",   0x000, 0x157, NO_DUMP ) \
	ROM_LOAD( "ms6_gal20v8.u20",   0x000, 0x157, NO_DUMP ) \
	ROM_LOAD( "ms6_gal20v8.u21",   0x000, 0x157, NO_DUMP ) \
	ROM_LOAD( "ms6_gal20v8.u39",   0x000, 0x157, NO_DUMP ) \
	ROM_LOAD( "ms6_gal20v8.u40",   0x000, 0x157, NO_DUMP ) \
	ROM_LOAD( "ms6_gal20v8.u104",  0x600, 0x157, CRC(67b56d29) SHA1(261ae6e968100d5a9c1ee68ea684bb2b1eef3cf8) )  /* unprotected */ \
	ROM_LOAD( "ms6_gal20v8.u131",  0x000, 0x157, NO_DUMP ) \
	ROM_LOAD( "ms6_gal20v8.u135",  0x000, 0x157, NO_DUMP ) \
	ROM_LOAD( "ms6_gal20v8.u140",  0x000, 0x157, NO_DUMP ) \
	ROM_LOAD( "ms6_gal20v8.u232",  0x000, 0x157, NO_DUMP ) \
	ROM_LOAD( "ms6_gal20v8.u233",  0x000, 0x157, NO_DUMP ) \
	ROM_LOAD( "ms6_gal20v8.u234",  0x800, 0x157, CRC(2c16b7c6) SHA1(f73ad0253a861d5306a09b1f97dfce622b19cdcf) )  /* unprotected */ \
	ROM_LOAD( "ms6_gal20v8.u235",  0x000, 0x157, NO_DUMP ) \
	/* 2x PALCE22V10H-25PC/4 */ \
	ROM_LOAD( "ms6_gal22v10.u50",  0xa00, 0x2e5, CRC(dc665408) SHA1(a7161a03add24a01d547189e9bfaf11474bbe878) )  /* unprotected */ \
	ROM_LOAD( "ms6_gal22v10.u134", 0xe00, 0x2e5, CRC(b66848bb) SHA1(edef02bc7fc0195f56cf815c1b862bee095951c8) )  /* unprotected */

ROM_START( sf2cems6a )  /* 920313 USA (this set matches "sf2ceuab4" in FBA) */
	ROM_REGION( CODE_SIZE, "maincpu", 0 ) /* 68k code */
	ROM_LOAD16_WORD_SWAP( "ms6.u196", 0x000000, 0x100000, CRC(596609d4) SHA1(4d876e6e44554eccbd0c5ea2d2d09e5024af0f9f) )  // == sf2m3: u196chp + u222chp (interleaved)
	ROM_LOAD16_WORD_SWAP( "ms6.u10",  0x100000,  0x80000, CRC(ed4186bd) SHA1(f3dfe91d8f4384275190b0d86488843c1161d86f) )  // == sf2ce: s92_21a.6f 1st half doubled, also == sf2dkot2: turboii.21

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "ms6.u70", 0x000000, 0x80000, CRC(baa0f81f) SHA1(5e55a5c4ad64be17089670a3d73c1c0d9082351b) )  // == sf2ce: s92-1m.3a + s92-2m.4a
	ROM_CONTINUE(               0x000004, 0x80000 )
	ROM_LOAD64_WORD( "ms6.u68", 0x000002, 0x80000, CRC(8edff95a) SHA1(8db35c5940dcc1f09f11be26051b2f98445d10e7) )  // == sf2ce: s92-3m.5a + s92-4m.6a
	ROM_CONTINUE(               0x000006, 0x80000 )
	ROM_LOAD64_WORD( "ms6.u69", 0x200000, 0x80000, CRC(468962b1) SHA1(fdfd2a7cbbcafaa37e972da425446d471e1e1dae) )  // == sf2ce: s92-5m.7a + s92-6m.8a
	ROM_CONTINUE(               0x200004, 0x80000 )
	ROM_LOAD64_WORD( "ms6.u64", 0x200002, 0x80000, CRC(8165f536) SHA1(8178fe2240c73c7283592aa31dd24aec5bf9429b) )  // == sf2ce: s92-7m.9a + s92-8m.10a
	ROM_CONTINUE(               0x200006, 0x80000 )
	ROM_LOAD64_WORD( "ms6.u19", 0x400000, 0x80000, CRC(39d763d3) SHA1(a2a0bddecaca6046785ccddfd20b8356a6ec36f0) )  // == sf2ce: s92-10m.3c + s92-11m.4c
	ROM_CONTINUE(               0x400004, 0x80000 )
	ROM_LOAD64_WORD( "ms6.u18", 0x400002, 0x80000, CRC(2ddfe46e) SHA1(517a76166d387375a75a36b2785de86898bdc777) )  // == sf2ce: s92-12m.5c + s92-13m.6c (1st half differs from s92-12m.5c by 7 bytes, CRC: 19fd3c2d)
	ROM_CONTINUE(               0x400006, 0x80000 )
	/* extra gfx data, purpose unknown */
	/* mapping over 0x400000, not sure if correct */
	ROM_LOAD64_WORD( "ms6.u31", 0x400000, 0x20000, CRC(35486f2d) SHA1(abdcfc73d2d42a7f3523e1a383c1ce5563c4fbd7) )  // == sf2m8: yyc-6.1 + yyc-7.10 (interleaved)
	ROM_CONTINUE(               0x400004, 0x20000 )  // 1st half == ms6.u19 0x00000-0x20000, 2nd half == ms6.u19 0x80000-0xa0000
	ROM_LOAD64_WORD( "ms6.u29", 0x400002, 0x20000, CRC(e4eca601) SHA1(acee4988f12a037a3b50f3923892fdac65f35805) )  // == sf2m8: yyc-8.9 + yyc-9.8 (interleaved)
	ROM_CONTINUE(               0x400006, 0x20000 )  // 1st half != ms6.u18 0x00000-0x20000 (4 bytes diff), 2nd half == ms6.u18 0x80000-0xa0000

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* z80 code */
	ROM_LOAD( "ms6.u191", 0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )  // == sf2ce: s92_09.11a
	ROM_CONTINUE(         0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* samples */
	ROM_LOAD( "ms6.u210", 0x00000, 0x40000, CRC(6cfffb11) SHA1(995526183ffd35f92e9096500a3fe6237faaa2dd) )  // == sf2ce: s92_18.11c + s92_19.12c, also == sf2amf2: fun-u210.bin, sf2rules: voice.u210, sf2m8: b-16.6

	ROM_REGION( 0x10000, "user1", 0 ) /* unknown, priority? */
	ROM_LOAD( "ms6.u133", 0x00000, 0x10000, CRC(13ea1c44) SHA1(5b05fe4c3920e33d94fac5f59e09ff14b3e427fe) )  // == loads other bootleg sets

	ROM_REGION( 0x1200, "plds", 0 ) /* pld devices */
	SF2CEMS6_PLD_DEVICES
ROM_END

ROM_START( sf2cems6b )  /* 920322 USA */
	ROM_REGION( 0x40000, "patch", 0 ) /* patch rom */
	ROM_LOAD16_WORD_SWAP( "ms6b.u0", 0x00000, 0x40000, CRC(b6f3724b) SHA1(aa8eea819fdaf205ca068067a4624715a8cf6c8c) )  // 27c220

	ROM_REGION( 0x0200, "patchpld", 0 ) /* patch pld, palce16v8, protected but bruteforced ok */
	ROM_LOAD( "ms6b.44", 0x0000, 0x0117, CRC(5f05a861) SHA1(26b8cab0e66b596ef54768b2811c1c1939d3135c) )

	ROM_REGION( CODE_SIZE, "maincpu", 0 ) /* 68k code */
	ROM_LOAD16_WORD_SWAP( "ms6b.u196", 0x000000, 0x100000, CRC(435153d5) SHA1(3f6f318a9b3def8d62ee576dbaaef623d55c1c64) )
	ROM_LOAD16_WORD_SWAP( "ms6b.u10",  0x100000,  0x40000, CRC(c812b7b2) SHA1(23ed0e1bd8b2015b39ad5e452dff0e372df0d5c9) )

	ROM_COPY( "patch", 0x00000, 0x000000,  0x8000 )
	ROM_COPY( "patch", 0x08000, 0x030000, 0x20000 )
	ROM_COPY( "patch", 0x28000, 0x170000, 0x10000 )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "ms6.u70", 0x000000, 0x80000, CRC(baa0f81f) SHA1(5e55a5c4ad64be17089670a3d73c1c0d9082351b) )
	ROM_CONTINUE(               0x000004, 0x80000 )
	ROM_LOAD64_WORD( "ms6.u68", 0x000002, 0x80000, CRC(8edff95a) SHA1(8db35c5940dcc1f09f11be26051b2f98445d10e7) )
	ROM_CONTINUE(               0x000006, 0x80000 )
	ROM_LOAD64_WORD( "ms6.u69", 0x200000, 0x80000, CRC(468962b1) SHA1(fdfd2a7cbbcafaa37e972da425446d471e1e1dae) )
	ROM_CONTINUE(               0x200004, 0x80000 )
	ROM_LOAD64_WORD( "ms6.u64", 0x200002, 0x80000, CRC(8165f536) SHA1(8178fe2240c73c7283592aa31dd24aec5bf9429b) )
	ROM_CONTINUE(               0x200006, 0x80000 )
	ROM_LOAD64_WORD( "ms6.u19", 0x400000, 0x80000, CRC(39d763d3) SHA1(a2a0bddecaca6046785ccddfd20b8356a6ec36f0) )
	ROM_CONTINUE(               0x400004, 0x80000 )
	ROM_LOAD64_WORD( "ms6.u18", 0x400002, 0x80000, CRC(2ddfe46e) SHA1(517a76166d387375a75a36b2785de86898bdc777) )
	ROM_CONTINUE(               0x400006, 0x80000 )
	ROM_LOAD64_WORD( "ms6.u31", 0x400000, 0x20000, CRC(35486f2d) SHA1(abdcfc73d2d42a7f3523e1a383c1ce5563c4fbd7) )
	ROM_CONTINUE(               0x400004, 0x20000 )
	ROM_LOAD64_WORD( "ms6.u29", 0x400002, 0x20000, CRC(e4eca601) SHA1(acee4988f12a037a3b50f3923892fdac65f35805) )
	ROM_CONTINUE(               0x400006, 0x20000 )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* z80 code */
	ROM_LOAD( "ms6.u191", 0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(         0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* samples */
	ROM_LOAD( "ms6.u210", 0x00000, 0x40000, CRC(6cfffb11) SHA1(995526183ffd35f92e9096500a3fe6237faaa2dd) )

	ROM_REGION( 0x10000, "user1", 0 ) /* unknown, priority? */
	ROM_LOAD( "ms6.u133", 0x00000, 0x10000, CRC(13ea1c44) SHA1(5b05fe4c3920e33d94fac5f59e09ff14b3e427fe) )

	ROM_REGION( 0x1200, "plds", 0 ) /* pld devices */
	SF2CEMS6_PLD_DEVICES
ROM_END

ROM_START( sf2cems6c )  /* 920322 USA */
	ROM_REGION( 0x40000, "patch", 0 ) /* patch rom */
	ROM_LOAD16_WORD_SWAP( "ms6c.u0", 0x00000, 0x40000, CRC(04088b61) SHA1(03c361a0c9c70c21ef53351d5f975b06f51ce2e0) )  // 27c2048 OTP

	ROM_REGION( 0x0200, "patchpld", 0 ) /* patch pld, gal16v8, unprotected */
	ROM_LOAD( "ms6c.44", 0x0000, 0x0117, CRC(8ceec769) SHA1(d646ed075182f3724c0c581065665b1c99ce180d) )

	ROM_REGION( CODE_SIZE, "maincpu", 0 ) /* 68k code */
	ROM_LOAD16_WORD_SWAP( "ms6b.u196", 0x000000, 0x100000, CRC(435153d5) SHA1(3f6f318a9b3def8d62ee576dbaaef623d55c1c64) )
	ROM_LOAD16_WORD_SWAP( "ms6b.u10",  0x100000,  0x40000, CRC(c812b7b2) SHA1(23ed0e1bd8b2015b39ad5e452dff0e372df0d5c9) )

	ROM_COPY( "patch", 0x00000, 0x000000,  0x8000 )
	ROM_COPY( "patch", 0x08000, 0x030000, 0x20000 )
	ROM_COPY( "patch", 0x28000, 0x170000, 0x10000 )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "ms6.u70", 0x000000, 0x80000, CRC(baa0f81f) SHA1(5e55a5c4ad64be17089670a3d73c1c0d9082351b) )
	ROM_CONTINUE(               0x000004, 0x80000 )
	ROM_LOAD64_WORD( "ms6.u68", 0x000002, 0x80000, CRC(8edff95a) SHA1(8db35c5940dcc1f09f11be26051b2f98445d10e7) )
	ROM_CONTINUE(               0x000006, 0x80000 )
	ROM_LOAD64_WORD( "ms6.u69", 0x200000, 0x80000, CRC(468962b1) SHA1(fdfd2a7cbbcafaa37e972da425446d471e1e1dae) )
	ROM_CONTINUE(               0x200004, 0x80000 )
	ROM_LOAD64_WORD( "ms6.u64", 0x200002, 0x80000, CRC(8165f536) SHA1(8178fe2240c73c7283592aa31dd24aec5bf9429b) )
	ROM_CONTINUE(               0x200006, 0x80000 )
	ROM_LOAD64_WORD( "ms6.u19", 0x400000, 0x80000, CRC(39d763d3) SHA1(a2a0bddecaca6046785ccddfd20b8356a6ec36f0) )
	ROM_CONTINUE(               0x400004, 0x80000 )
	ROM_LOAD64_WORD( "ms6.u18", 0x400002, 0x80000, CRC(2ddfe46e) SHA1(517a76166d387375a75a36b2785de86898bdc777) )
	ROM_CONTINUE(               0x400006, 0x80000 )
	ROM_LOAD64_WORD( "ms6.u31", 0x400000, 0x20000, CRC(35486f2d) SHA1(abdcfc73d2d42a7f3523e1a383c1ce5563c4fbd7) )
	ROM_CONTINUE(               0x400004, 0x20000 )
	ROM_LOAD64_WORD( "ms6.u29", 0x400002, 0x20000, CRC(e4eca601) SHA1(acee4988f12a037a3b50f3923892fdac65f35805) )
	ROM_CONTINUE(               0x400006, 0x20000 )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* z80 code */
	ROM_LOAD( "ms6.u191", 0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(         0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* samples */
	ROM_LOAD( "ms6.u210", 0x00000, 0x40000, CRC(6cfffb11) SHA1(995526183ffd35f92e9096500a3fe6237faaa2dd) )

	ROM_REGION( 0x10000, "user1", 0 ) /* unknown, priority? */
	ROM_LOAD( "ms6.u133", 0x00000, 0x10000, CRC(13ea1c44) SHA1(5b05fe4c3920e33d94fac5f59e09ff14b3e427fe) )

	ROM_REGION( 0x1200, "plds", 0 ) /* pld devices */
	SF2CEMS6_PLD_DEVICES
ROM_END

ROM_START( sf2ceds6 ) // 10/17/92 DSTREET-6 on PCB, labels similar to the ones used by Subsino
	ROM_REGION( 0x40000, "patch", 0 )
	ROM_LOAD16_BYTE( "n.010.u12", 0x00000, 0x20000, CRC(275b67ac) SHA1(b3189713de8aed61c12d6f2469dbe9386cc30983) )
	ROM_LOAD16_BYTE( "i.010.u11", 0x00001, 0x20000, CRC(ca403ac1) SHA1(0748a8b88029cbbb8cae40a4ba93843aeed261ae) )

	ROM_REGION( 0x0200, "patchpld", 0 )
	ROM_LOAD( "palce20v8h.u28", 0x0000, 0x0117, NO_DUMP )

	ROM_REGION( CODE_SIZE, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "6st-u196.2m1", 0x000000, 0x100000, CRC(596609d4) SHA1(4d876e6e44554eccbd0c5ea2d2d09e5024af0f9f) )
	ROM_LOAD16_WORD_SWAP( "6st-u210.2m1", 0x100000,  0x80000, CRC(ed4186bd) SHA1(f3dfe91d8f4384275190b0d86488843c1161d86f) )

	// TODO: the following patches weren't verified via pal analysis
	ROM_COPY( "patch", 0x00000, 0x000000, 0x18000 )
	ROM_COPY( "patch", 0x18000, 0x030000, 0x18000 )
	ROM_COPY( "patch", 0x30000, 0x0e0000, 0x08000 )
	ROM_COPY( "patch", 0x38000, 0x108000, 0x08000 )
	//ROM_COPY( "patch", 0x38000, 0x148000, 0x08000 ) // is this needed? if bugs (random resets) are reported try to enable this

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "6st.u70", 0x000000, 0x80000, CRC(baa0f81f) SHA1(5e55a5c4ad64be17089670a3d73c1c0d9082351b) )
	ROM_CONTINUE(               0x000004, 0x80000 )
	ROM_LOAD64_WORD( "6st.u68", 0x000002, 0x80000, CRC(8edff95a) SHA1(8db35c5940dcc1f09f11be26051b2f98445d10e7) )
	ROM_CONTINUE(               0x000006, 0x80000 )
	ROM_LOAD64_WORD( "6st.u69", 0x200000, 0x80000, CRC(468962b1) SHA1(fdfd2a7cbbcafaa37e972da425446d471e1e1dae) )
	ROM_CONTINUE(               0x200004, 0x80000 )
	ROM_LOAD64_WORD( "6st.u64", 0x200002, 0x80000, CRC(8165f536) SHA1(8178fe2240c73c7283592aa31dd24aec5bf9429b) )
	ROM_CONTINUE(               0x200006, 0x80000 )
	ROM_LOAD64_WORD( "6st.u19", 0x400000, 0x80000, CRC(39d763d3) SHA1(a2a0bddecaca6046785ccddfd20b8356a6ec36f0) )
	ROM_CONTINUE(               0x400004, 0x80000 )
	ROM_LOAD64_WORD( "6st.u18", 0x400002, 0x80000, CRC(2ddfe46e) SHA1(517a76166d387375a75a36b2785de86898bdc777) )
	ROM_CONTINUE(               0x400006, 0x80000 )
	// extra gfx data, purpose unknown
	// mapping over 0x400000, not sure if correct
	ROM_LOAD64_WORD( "6st.u31", 0x400000, 0x20000, CRC(35486f2d) SHA1(abdcfc73d2d42a7f3523e1a383c1ce5563c4fbd7) )
	ROM_CONTINUE(               0x400004, 0x20000 )
	ROM_LOAD64_WORD( "6st.u29", 0x400002, 0x20000, CRC(e4eca601) SHA1(acee4988f12a037a3b50f3923892fdac65f35805) )
	ROM_CONTINUE(               0x400006, 0x20000 )

	ROM_REGION( 0x18000, "audiocpu", 0 )
	ROM_LOAD( "o.u191", 0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(         0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 )
	ROM_LOAD( "fun-s3-j1.u210", 0x00000, 0x40000, CRC(6cfffb11) SHA1(995526183ffd35f92e9096500a3fe6237faaa2dd) )

	ROM_REGION( 0x10000, "user1", 0 ) // unknown, priority?
	ROM_LOAD( "ms6.u133", 0x00000, 0x10000, CRC(13ea1c44) SHA1(5b05fe4c3920e33d94fac5f59e09ff14b3e427fe) )  // == loads other bootleg sets

	ROM_REGION( 0x0200, "plds", 0 )
	ROM_LOAD( "gal20v8a.u104", 0x0000, 0x0157, NO_DUMP )  // M-STREET6 probably the same?
ROM_END

/* B-Board 89625B-1 */
ROM_START( cworld2j )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "q5_36.12f",        0x00000, 0x20000, CRC(38a08099) SHA1(961f733baa2bbf8419e4de70f18660098dba7d52) )
	ROM_LOAD16_BYTE( "q5_42.12h",        0x00001, 0x20000, CRC(4d29b3a4) SHA1(bf40fc22c0161fe131ca69100b2a4d102e86bde6) )
	ROM_LOAD16_BYTE( "q5_37.13f",        0x40000, 0x20000, CRC(eb547ebc) SHA1(fce470b05ce095badd180c3740677146f52f6080) )
	ROM_LOAD16_BYTE( "q5_43.13h",        0x40001, 0x20000, CRC(3ef65ea8) SHA1(2348d84b380c0e8ebe270a37d4ff3ce5204abc8c) )
	ROM_LOAD16_BYTE( "q5_34.10f",        0x80000, 0x20000, CRC(7fcc1317) SHA1(672ca45d3fad5eec4d65bbbbd1d21cbf6be4ec8b) )
	ROM_LOAD16_BYTE( "q5_40.10h",        0x80001, 0x20000, CRC(7f14b7b4) SHA1(5564eb9f65dad76ebe40d12d5c39fec5e246adf0) )
	ROM_LOAD16_BYTE( "q5_35.11f",        0xc0000, 0x20000, CRC(abacee26) SHA1(2f513c02f715ffeec12a6d1c292619e214155cbc) )
//  ROM_LOAD16_BYTE( "q5_35.11f",        0xc0000, 0x20000, CRC(59961612) SHA1(ded5144746dd9612f2db1415e96a826e215ad176) ) /* dumped from another board, 1 byte different, pcb verified */
	ROM_LOAD16_BYTE( "q5_41.11h",        0xc0001, 0x20000, CRC(d3654067) SHA1(0b597483e136ff19b031171941cb8439bcd7f145) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_BYTE( "q5_09.4b",  0x000000, 0x20000, CRC(48496d80) SHA1(bdfaca6375c8275b06b2bc170a25ff6aa62394dc) )
	ROM_LOAD64_BYTE( "q5_01.4a",  0x000001, 0x20000, CRC(c5453f56) SHA1(113fe6cc6c830352df5992be9fa34c4d70bf32ed) )
	ROM_LOAD64_BYTE( "q5_13.9b",  0x000002, 0x20000, CRC(c741ac52) SHA1(d8b4aeacfd62586b98a1381da357dcc5ab16c1c6) )
	ROM_LOAD64_BYTE( "q5_05.9a",  0x000003, 0x20000, CRC(143e068f) SHA1(24cdc49c09a9f0c93e04b37cf7ebba09a929c9b0) )
	ROM_LOAD64_BYTE( "q5_24.5e",  0x000004, 0x20000, CRC(b419d139) SHA1(46cd97da2413eb5fbd38fa2c20914f3c5f1c6ec8) )
	ROM_LOAD64_BYTE( "q5_17.5c",  0x000005, 0x20000, CRC(bd3b4d11) SHA1(bb62169bc52562715878a33cc4f8558e05d581d3) )
	ROM_LOAD64_BYTE( "q5_38.8h",  0x000006, 0x20000, CRC(9c24670c) SHA1(3b98078b7360e21b3905fd973e01b88b02090759) )
	ROM_LOAD64_BYTE( "q5_32.8f",  0x000007, 0x20000, CRC(3ef9c7c2) SHA1(52a18d7b12f0c14c5cf68a3dd63571e955005f4c) )
	ROM_LOAD64_BYTE( "q5_10.5b",  0x100000, 0x20000, CRC(119e5e93) SHA1(b5b6c2e3516ebe555a26ecfb5934f3b65371bf36) )
	ROM_LOAD64_BYTE( "q5_02.5a",  0x100001, 0x20000, CRC(a2cadcbe) SHA1(3d1079f62cce628cbc5b810c0bd51c67c87f4eca) )
	ROM_LOAD64_BYTE( "q5_14.10b", 0x100002, 0x20000, CRC(a8755f82) SHA1(0a2fbc8b96651f9ab72eb451723e56ca0a859868) )
	ROM_LOAD64_BYTE( "q5_06.10a", 0x100003, 0x20000, CRC(c92a91fc) SHA1(dfe9682349cf94be414b7e1895b632de41729194) )
	ROM_LOAD64_BYTE( "q5_25.7e",  0x100004, 0x20000, CRC(979237cb) SHA1(9534b05523317a220b3b957a18fec51f1d4e37b3) )
	ROM_LOAD64_BYTE( "q5_18.7c",  0x100005, 0x20000, CRC(c57da03c) SHA1(ad7bce859f56d201d229032baf4fb9f65b54765b) )
	ROM_LOAD64_BYTE( "q5_39.9h",  0x100006, 0x20000, CRC(a5839b25) SHA1(20c4c4f24f21a325a03538306de799df2a89f6cb) )
	ROM_LOAD64_BYTE( "q5_33.9f",  0x100007, 0x20000, CRC(04d03930) SHA1(37f2556eeb52f8edfcddd3f3642fa24565d5a7bd) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "q5_23.13b",  0x00000, 0x08000, CRC(e14dc524) SHA1(0020a9002572002458fbfe45e8a959cb90de3f03) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "q5_30.12c",  0x00000, 0x20000, CRC(d10c1b68) SHA1(2423241f3340d8ab1b6bf9514ca8c3bba1273873) )
	ROM_LOAD( "q5_31.13c",  0x20000, 0x20000, CRC(7d17e496) SHA1(a274b94ec4f042dddc239ecb9ac2e1e2375f5eb2) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "q522b.1a",     0x0000, 0x0117, CRC(0a1527ab) SHA1(116bab946ebe87fa0e913c210604910f9af0389f) )
	ROM_LOAD( "lwio.12e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )    /* seen the same pcb with IOB1.12E */

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
ROM_END

/* B-Board 90629B-3  - all roms have 90629B on the labels, no battery, possibly unofficial / desuicided with reproduction stickers */
ROM_START( cworld2ja )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP("q5 - 34_90629b.8f", 0x00000, 0x80000, CRC(de54487f) SHA1(75b228a6c702c82d4d9a2a992933b5c3c420f6c2) )
	ROM_LOAD16_WORD_SWAP("q5 - 33_90629b.6f", 0x80000, 0x80000, CRC(93248458) SHA1(9dcdc6838f52efc9a0a6333fd0d734946db12dbd) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "q5 - 06_90629b.8a",  0x000000, 0x80000, CRC(09d0e7ce) SHA1(ea502b975986222acce82ce8396348af72e1df72) )
	ROM_LOAD64_WORD( "q5 - 08_90629b.10a", 0x000002, 0x80000, CRC(22e4ce9a) SHA1(9e49aec8e1d6d15a68da63e69765b82fd53a9562) )
	ROM_LOAD64_WORD( "q5 - 05_90629b.7a",  0x000004, 0x80000, CRC(f7b3aed6) SHA1(bdfb4d5988307b07ad878ac9129954d14da8769b) )
	ROM_LOAD64_WORD( "q5 - 07_90629b.9a",  0x000006, 0x80000, CRC(520c6c88) SHA1(19ba8ca3d75aae71cdf471e6307e86a5df8a2851) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "q5 - 09_90629b.12a",  0x00000, 0x08000, CRC(e14dc524) SHA1(0020a9002572002458fbfe45e8a959cb90de3f03) )
	ROM_CONTINUE(                    0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "q5 - 18_90629b.11c",  0x00000, 0x20000, CRC(d10c1b68) SHA1(2423241f3340d8ab1b6bf9514ca8c3bba1273873) )
	ROM_LOAD( "q5 - 19_90629b.12c",  0x20000, 0x20000, CRC(7d17e496) SHA1(a274b94ec4f042dddc239ecb9ac2e1e2375f5eb2) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "q529b.1a",     0x0000, 0x0117, NO_DUMP )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
ROM_END

/* B-Board 91634B-2  - all roms have 91634B on the labels */
ROM_START( cworld2jb )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "q5 - 23_91634b.8f", 0x00000, 0x80000, CRC(709f577f) SHA1(3e0615d01f22eb1bf75cbd26dc80ca5a6d08120e) )
	ROM_LOAD16_WORD_SWAP( "q5 - 22_91634b.7f", 0x80000, 0x80000, CRC(93248458) SHA1(9dcdc6838f52efc9a0a6333fd0d734946db12dbd) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "q5 - 01_91634b.3a", 0x000000, 0x80000, CRC(09d0e7ce) SHA1(ea502b975986222acce82ce8396348af72e1df72) )
	ROM_LOAD64_WORD( "q5 - 02_91634b.4a", 0x000002, 0x80000, CRC(22e4ce9a) SHA1(9e49aec8e1d6d15a68da63e69765b82fd53a9562) )
	ROM_LOAD64_WORD( "q5 - 03_91634b.5a", 0x000004, 0x80000, CRC(f7b3aed6) SHA1(bdfb4d5988307b07ad878ac9129954d14da8769b) )
	ROM_LOAD64_WORD( "q5 - 04_91634b.6a", 0x000006, 0x80000, CRC(520c6c88) SHA1(19ba8ca3d75aae71cdf471e6307e86a5df8a2851) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "q5 - 09_91634b.12a", 0x00000, 0x08000, CRC(e14dc524) SHA1(0020a9002572002458fbfe45e8a959cb90de3f03) )
	ROM_CONTINUE(                   0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "q5 - 18_91634b.11c", 0x00000, 0x20000, CRC(d10c1b68) SHA1(2423241f3340d8ab1b6bf9514ca8c3bba1273873) )
	ROM_LOAD( "q5 - 19_91634b.12c", 0x20000, 0x20000, CRC(7d17e496) SHA1(a274b94ec4f042dddc239ecb9ac2e1e2375f5eb2) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "q563b.1a",     0x0000, 0x0117, NO_DUMP )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 ) // checkme
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 89624B-3 */
ROM_START( varth )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "vae_30b.11f", 0x00000, 0x20000, CRC(adb8d391) SHA1(5e7160509e0315eb32cc390ddd7e4ef7a4a1a70a) )
	ROM_LOAD16_BYTE( "vae_35b.11h", 0x00001, 0x20000, CRC(44e5548f) SHA1(17b4be1f4159f6b6803d8c2950823ece0bdde8b2) )
	ROM_LOAD16_BYTE( "vae_31b.12f", 0x40000, 0x20000, CRC(1749a71c) SHA1(bd9bfd5bbe2d426c94df755c977faa92a28f16ab) )
	ROM_LOAD16_BYTE( "vae_36b.12h", 0x40001, 0x20000, CRC(5f2e2450) SHA1(676e8d96406d81ceadd4a0a69959cdcb6d5d9ac8) )
	ROM_LOAD16_BYTE( "vae_28b.9f",  0x80000, 0x20000, CRC(e524ca50) SHA1(487d5ddabe852872f331362034c4fa16e0926e3d) )
	ROM_LOAD16_BYTE( "vae_33b.9h",  0x80001, 0x20000, CRC(c0bbf8c9) SHA1(447540b856776770af8022a291d46612c1bb5909) )
	ROM_LOAD16_BYTE( "vae_29b.10f", 0xc0000, 0x20000, CRC(6640996a) SHA1(3ed7bd947dc8224435680dedf4955ed6041c6028) )
	ROM_LOAD16_BYTE( "vae_34b.10h", 0xc0001, 0x20000, CRC(fa59be8a) SHA1(86a3d3a7126c021e2ca8ac20238695396367e098) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "va-5m.7a", 0x000000, 0x80000, CRC(b1fb726e) SHA1(5ac0876b6c49d0a99710dda68653664f4d8c1167) )
	ROM_LOAD64_WORD( "va-7m.9a", 0x000002, 0x80000, CRC(4c6588cd) SHA1(d14e8cf051ac934ccc989d8c571c6cc9eed34af5) )
	ROM_LOAD64_WORD( "va-1m.3a", 0x000004, 0x80000, CRC(0b1ace37) SHA1(6f9493c22f667f683db2789972fd16bb94724679) )
	ROM_LOAD64_WORD( "va-3m.5a", 0x000006, 0x80000, CRC(44dfe706) SHA1(a013a434df3161a91aafbb35dc4e20dfb3f177f4) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "va_09.12b", 0x00000, 0x08000, CRC(7a99446e) SHA1(ca027f41e3e58be5abc33ad7380746658cb5380a) ) // missing sticker, could be va_9.12b
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "va_18.11c", 0x00000, 0x20000, CRC(de30510e) SHA1(8e878696192606b76a3a0e53553e638d9621cff7) )
	ROM_LOAD( "va_19.12c", 0x20000, 0x20000, CRC(0610a4ac) SHA1(3da02ea6a7a56c85de898806d2a1cf6bc526c1b3) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "va24b.1a",     0x0000, 0x0117, CRC(cc476650) SHA1(a417f7971b8709023932fc2b68c9e3cd699ab6f2) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 89624B-? */
ROM_START( varthr1 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "vae_30a.11f", 0x00000, 0x20000, CRC(7fcd0091) SHA1(7bed452736eda4a26c43c5dd54ec6799afa6e770) )
	ROM_LOAD16_BYTE( "vae_35a.11h", 0x00001, 0x20000, CRC(35cf9509) SHA1(a189ca7740d77262413ec2891af034d0057892be) )
	ROM_LOAD16_BYTE( "vae_31a.12f", 0x40000, 0x20000, CRC(15e5ee81) SHA1(6c6248b07f7e956a37d5dcb4b67d026f57fae13b) )
	ROM_LOAD16_BYTE( "vae_36a.12h", 0x40001, 0x20000, CRC(153a201e) SHA1(5936e447d5cd02ff13802cf78393b521431ad06c) )
	ROM_LOAD16_BYTE( "vae_28a.9f",  0x80000, 0x20000, CRC(7a0e0d25) SHA1(203692ef1daeef7ba08b154cf029cc07a2e0e23d) )
	ROM_LOAD16_BYTE( "vae_33a.9h",  0x80001, 0x20000, CRC(f2365922) SHA1(efb2221033e4b46fedaf3d8c850e208f849e6af0) )
	ROM_LOAD16_BYTE( "vae_29a.10f", 0xc0000, 0x20000, CRC(5e2cd2c3) SHA1(eff955c7dc0d8ae215e7188cc4865726104c7777) )
	ROM_LOAD16_BYTE( "vae_34a.10h", 0xc0001, 0x20000, CRC(3d9bdf83) SHA1(d655803a6f07b90e44aacaa3e6059ac330ef2ec6) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "va-5m.7a", 0x000000, 0x80000, CRC(b1fb726e) SHA1(5ac0876b6c49d0a99710dda68653664f4d8c1167) )
	ROM_LOAD64_WORD( "va-7m.9a", 0x000002, 0x80000, CRC(4c6588cd) SHA1(d14e8cf051ac934ccc989d8c571c6cc9eed34af5) )
	ROM_LOAD64_WORD( "va-1m.3a", 0x000004, 0x80000, CRC(0b1ace37) SHA1(6f9493c22f667f683db2789972fd16bb94724679) )
	ROM_LOAD64_WORD( "va-3m.5a", 0x000006, 0x80000, CRC(44dfe706) SHA1(a013a434df3161a91aafbb35dc4e20dfb3f177f4) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "va_09.12b", 0x00000, 0x08000, CRC(7a99446e) SHA1(ca027f41e3e58be5abc33ad7380746658cb5380a) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "va_18.11c", 0x00000, 0x20000, CRC(de30510e) SHA1(8e878696192606b76a3a0e53553e638d9621cff7) )
	ROM_LOAD( "va_19.12c", 0x20000, 0x20000, CRC(0610a4ac) SHA1(3da02ea6a7a56c85de898806d2a1cf6bc526c1b3) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "va24b.1a",     0x0000, 0x0117, CRC(cc476650) SHA1(a417f7971b8709023932fc2b68c9e3cd699ab6f2) )
	ROM_LOAD( "iob1.11e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( varthu )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "vau_23a.8f", 0x00000, 0x80000, CRC(fbe68726) SHA1(68917d366551d2203400adc3261355dd3b332bcb) )
	ROM_LOAD16_WORD_SWAP( "vau_22a.7f", 0x80000, 0x80000, CRC(0ed71bbd) SHA1(e7f0f0edf0936a774e122842b09f5c5ce25a96ad) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "va-5m.3a", 0x000000, 0x80000, CRC(b1fb726e) SHA1(5ac0876b6c49d0a99710dda68653664f4d8c1167) )  // in "1" socket    // == va-5m.7a
	ROM_LOAD64_WORD( "va-7m.5a", 0x000002, 0x80000, CRC(4c6588cd) SHA1(d14e8cf051ac934ccc989d8c571c6cc9eed34af5) )  // in "3" socket    // == va-7m.9a
	ROM_LOAD64_WORD( "va-1m.4a", 0x000004, 0x80000, CRC(0b1ace37) SHA1(6f9493c22f667f683db2789972fd16bb94724679) )  // in "2" socket    // == va-1m.3a
	ROM_LOAD64_WORD( "va-3m.6a", 0x000006, 0x80000, CRC(44dfe706) SHA1(a013a434df3161a91aafbb35dc4e20dfb3f177f4) )  // in "4" socket    // == va-3m.5a

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "va_09.11a", 0x00000, 0x08000, CRC(7a99446e) SHA1(ca027f41e3e58be5abc33ad7380746658cb5380a) ) // == va_09.12b
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "va_18.11c", 0x00000, 0x20000, CRC(de30510e) SHA1(8e878696192606b76a3a0e53553e638d9621cff7) )
	ROM_LOAD( "va_19.12c", 0x20000, 0x20000, CRC(0610a4ac) SHA1(3da02ea6a7a56c85de898806d2a1cf6bc526c1b3) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "va63b.1a",     0x0000, 0x0117, CRC(132ab7c5) SHA1(54b78b02cdd9430c90e2289d42585ee71cf71cdc) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )
ROM_END

/* B-Board 88622B-3 */
ROM_START( varthj )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "vaj_36b.12f", 0x00000, 0x20000, CRC(1d798d6a) SHA1(b12579e9dcb88416bc00653b143832d9347adbaf) )
	ROM_LOAD16_BYTE( "vaj_42b.12h", 0x00001, 0x20000, CRC(0f720233) SHA1(2d9442ceafd5e2208aa8cd4bcb66861bff6aec47) )
	ROM_LOAD16_BYTE( "vaj_37b.13f", 0x40000, 0x20000, CRC(24414b17) SHA1(6c0b24cf8045fc033217c737dba2c046d7d0a09a) )
	ROM_LOAD16_BYTE( "vaj_43b.13h", 0x40001, 0x20000, CRC(34b4b06c) SHA1(3033d1d053ba97d6da17064d7b944a10817b93b1) )
	ROM_LOAD16_BYTE( "vaj_34b.10f", 0x80000, 0x20000, CRC(87c79aed) SHA1(bb90720d1d04ed6ad276a5230cb078229aa8a40a) )
	ROM_LOAD16_BYTE( "vaj_40b.10h", 0x80001, 0x20000, CRC(210b4bd0) SHA1(15771c32af9fb4760953ef5475de228200851b42) )
	ROM_LOAD16_BYTE( "vaj_35b.11f", 0xc0000, 0x20000, CRC(6b0da69f) SHA1(5883bea31a22a44ad7494d6acd523c88b62f8743) )
	ROM_LOAD16_BYTE( "vaj_41b.11h", 0xc0001, 0x20000, CRC(6542c8a4) SHA1(5f828cf28ef905e4701c92f317e1257a40964a65) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_BYTE( "va_09.4b",  0x000000, 0x20000, CRC(183dfaa8) SHA1(230c65c1b11b3a5d1733455e0130dad2740e2d2d) ) // == va-5m.7a
	ROM_LOAD64_BYTE( "va_01.4a",  0x000001, 0x20000, CRC(c41312b5) SHA1(4077cb8200fc403953a08d94652fa8f572dc202d) ) // == va-5m.7a
	ROM_LOAD64_BYTE( "va_13.9b",  0x000002, 0x20000, CRC(45537e69) SHA1(18581cbf09b1ec35ea388dce73db7099a1790f60) ) // == va-7m.9a
	ROM_LOAD64_BYTE( "va_05.9a",  0x000003, 0x20000, CRC(7065d4e9) SHA1(0e16b4ba2309cca609eaa906c99c61172ca273d0) ) // == va-7m.9a
	ROM_LOAD64_BYTE( "va_24.5e",  0x000004, 0x20000, CRC(57191ccf) SHA1(8247b6ca36dd114ea2d030141ce48ea881ea648c) ) // == va-1m.3a
	ROM_LOAD64_BYTE( "va_17.5c",  0x000005, 0x20000, CRC(054f5a5b) SHA1(28fc6ff2144daad18b5aed8c08d0b65e6fc2b06f) ) // == va-1m.3a
	ROM_LOAD64_BYTE( "va_38.8h",  0x000006, 0x20000, CRC(e117a17e) SHA1(576ec580050e9ce3e3be96b849247288411ff68c) ) // == va-3m.5a
	ROM_LOAD64_BYTE( "va_32.8f",  0x000007, 0x20000, CRC(3b4f40b2) SHA1(7033d0f754381fe8d5ed29b58ebbd665a0ba1725) ) // == va-3m.5a
	ROM_LOAD64_BYTE( "va_10.5b",  0x100000, 0x20000, CRC(d62750cd) SHA1(0b792f806ed5ab7f6ec0c53bb9bf9965d7ddc47e) ) // == va-5m.7a
	ROM_LOAD64_BYTE( "va_02.5a",  0x100001, 0x20000, CRC(11590325) SHA1(9d776f4008db76f8f141db5024a3eed78e364b6a) ) // == va-5m.7a
	ROM_LOAD64_BYTE( "va_14.10b", 0x100002, 0x20000, CRC(dc2f4783) SHA1(f9c274d1ab24159980f29db7da5bcc179761237f) ) // == va-7m.9a
	ROM_LOAD64_BYTE( "va_06.10a", 0x100003, 0x20000, CRC(06e833ac) SHA1(e8df6e2ef8300b5e412dd74cfe329b5535056e62) ) // == va-7m.9a
	ROM_LOAD64_BYTE( "va_25.7e",  0x100004, 0x20000, CRC(51d90690) SHA1(9079d56007aae257f56ce47bbb24873dc18c5bd6) ) // == va-1m.3a
	ROM_LOAD64_BYTE( "va_18.7c",  0x100005, 0x20000, CRC(a17817c0) SHA1(23d9ae2ae68e4c8be72da7013109ecdfc30d4b53) ) // == va-1m.3a
	ROM_LOAD64_BYTE( "va_39.9h",  0x100006, 0x20000, CRC(b0b12f51) SHA1(68a33736dcb0703e46ba48918a29ecd559575a97) ) // == va-3m.5a
	ROM_LOAD64_BYTE( "va_33.9f",  0x100007, 0x20000, CRC(4b003af7) SHA1(0c1d18a3ee7f3a48219f73eb21f88a260a9a001e) ) // == va-3m.5a

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "va_23.13c",  0x00000, 0x08000, CRC(7a99446e) SHA1(ca027f41e3e58be5abc33ad7380746658cb5380a) )    // == va_09.12b
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "va_30.12e",  0x00000, 0x20000, CRC(de30510e) SHA1(8e878696192606b76a3a0e53553e638d9621cff7) )    // == va_18.11c
	ROM_LOAD( "va_31.13e",  0x20000, 0x20000, CRC(0610a4ac) SHA1(3da02ea6a7a56c85de898806d2a1cf6bc526c1b3) )    // == va_19.12c

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "va22b.1a",     0x0000, 0x0117, CRC(bd7cd574) SHA1(00e49631aceb2871e9313f40264fa55eaaa3538c) )
	ROM_LOAD( "lwio.12c",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
ROM_END

/* B-Board 91634B-2, Japan Resale Ver. */
ROM_START( varthjr )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "vaj_23b.8f", 0x00000, 0x80000, CRC(ad3d3522) SHA1(db627233f9d8a03c2d4bb31614951a0cdc81600d) )
	ROM_LOAD16_WORD_SWAP( "vaj_22b.7f", 0x80000, 0x80000, CRC(034e3e55) SHA1(eeb85a827cf18dafbdf0a2828aa39128352857f3) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "va_01.3a", 0x000000, 0x80000, CRC(b1fb726e) SHA1(5ac0876b6c49d0a99710dda68653664f4d8c1167) )  // == va-5m.7a
	ROM_LOAD64_WORD( "va_02.4a", 0x000002, 0x80000, CRC(4c6588cd) SHA1(d14e8cf051ac934ccc989d8c571c6cc9eed34af5) )  // == va-7m.9a
	ROM_LOAD64_WORD( "va_03.5a", 0x000004, 0x80000, CRC(0b1ace37) SHA1(6f9493c22f667f683db2789972fd16bb94724679) )  // == va-1m.3a
	ROM_LOAD64_WORD( "va_04.6a", 0x000006, 0x80000, CRC(44dfe706) SHA1(a013a434df3161a91aafbb35dc4e20dfb3f177f4) )  // == va-3m.5a

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "va_09.12a", 0x00000, 0x08000, CRC(7a99446e) SHA1(ca027f41e3e58be5abc33ad7380746658cb5380a) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "va_18.11c", 0x00000, 0x20000, CRC(de30510e) SHA1(8e878696192606b76a3a0e53553e638d9621cff7) )
	ROM_LOAD( "va_19.12c", 0x20000, 0x20000, CRC(0610a4ac) SHA1(3da02ea6a7a56c85de898806d2a1cf6bc526c1b3) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "va63b.1a",     0x0000, 0x0117, CRC(132ab7c5) SHA1(54b78b02cdd9430c90e2289d42585ee71cf71cdc) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
ROM_END

/* varthr1 bootleg (runs on identical pcb to sf2amf3)
   only prg roms have been dumped, everything else is from parent set for now...
   some minor priority and transparency issues, unknown if present on real pcb
*/
ROM_START( varthb2 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "varth-u222", 0x00000, 0x80000, CRC(fd2f93a8) SHA1(d76c2f063f9e1ed32b451c97ff94ec80aef5e9ba) )
	ROM_LOAD16_BYTE( "varth-u196", 0x00001, 0x80000, CRC(3b187dc3) SHA1(0fcd17e934e89928f46b73f3eebbb5ad71a7cf2f) )

	// pcb picture shows 4x 42-pin mask roms which suggests 4x 27c800 (4MB) for gfx vs originals 4x 27c400 (2MB)
	// roms u18,19 might just be duplicates of u68,70 (although markings are different?) or just half empty
	// games expects sprites to start at 0x8000 so reloading in upper region
	ROM_REGION( 0x600000, "gfx", ROMREGION_ERASE00 )
	ROM_LOAD64_WORD( "va-5m.7a", 0x000000, 0x80000, BAD_DUMP CRC(b1fb726e) SHA1(5ac0876b6c49d0a99710dda68653664f4d8c1167) )
	ROM_RELOAD(                  0x400000, 0x80000)
	ROM_LOAD64_WORD( "va-7m.9a", 0x000002, 0x80000, BAD_DUMP CRC(4c6588cd) SHA1(d14e8cf051ac934ccc989d8c571c6cc9eed34af5) )
	ROM_RELOAD(                  0x400002, 0x80000)
	ROM_LOAD64_WORD( "va-1m.3a", 0x000004, 0x80000, BAD_DUMP CRC(0b1ace37) SHA1(6f9493c22f667f683db2789972fd16bb94724679) )
	ROM_RELOAD(                  0x400004, 0x80000)
	ROM_LOAD64_WORD( "va-3m.5a", 0x000006, 0x80000, BAD_DUMP CRC(44dfe706) SHA1(a013a434df3161a91aafbb35dc4e20dfb3f177f4) )
	ROM_RELOAD(                  0x400006, 0x80000)

	// a 27c512 in some way related to gfx  NO DUMP

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "va_09.12b", 0x00000, 0x08000, CRC(7a99446e) SHA1(ca027f41e3e58be5abc33ad7380746658cb5380a) )
	ROM_CONTINUE(          0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "va_18.11c", 0x00000, 0x20000, CRC(de30510e) SHA1(8e878696192606b76a3a0e53553e638d9621cff7) )
	ROM_LOAD( "va_19.12c", 0x20000, 0x20000, CRC(0610a4ac) SHA1(3da02ea6a7a56c85de898806d2a1cf6bc526c1b3) )
ROM_END

ROM_START( varthb3 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )
	ROM_LOAD16_BYTE( "pd1.bin", 0x000000, 0x80000, CRC(8208c0d7) SHA1(335cbc183abac9a79d7a84ef9c713217ccbf27bb) )
	ROM_LOAD16_BYTE( "c6.bin",  0x000001, 0x80000, CRC(7f654421) SHA1(05e87540bfe32a286c663782a788c5d573616dc1) )
	ROM_LOAD16_BYTE( "pd2.bin", 0x100000, 0x20000, CRC(d8325c94) SHA1(59c09948ad48e434782b97cfb1eefee0b23dc3da) )
	ROM_LOAD16_BYTE( "c5.bin",  0x100001, 0x20000, CRC(6152277d) SHA1(e93ae5c74cfe8d8c4bf6f3cd802f0e30192ffa2c) )

	ROM_REGION( 0x600000, "gfx", ROMREGION_ERASE00 )
	ROM_LOAD32_BYTE( "rom1.bin", 0x000000, 0x80000, CRC(473961b3) SHA1(e969ead42629607cecd8f38005d65085a9dd5ee9) )
	ROM_LOAD32_BYTE( "rom3.bin", 0x000001, 0x80000, CRC(9b50384f) SHA1(a06efe61a4b74e3240807140d7704f7bffeb5f81) )
	ROM_LOAD32_BYTE( "rom5.bin", 0x000002, 0x80000, CRC(516a4eea) SHA1(2e9b2d32344db926df2a980f1b0a8b34eda70126) )
	ROM_LOAD32_BYTE( "rom7.bin", 0x000003, 0x80000, CRC(81023052) SHA1(a96dd88483807b6f7520fb42dbc0cdd7bfa105c9) )

	ROM_REGION( 0x18000, "audiocpu", 0 )
	ROM_LOAD( "j4.bin", 0x00000, 0x08000, CRC(7a99446e) SHA1(ca027f41e3e58be5abc33ad7380746658cb5380a) )
	ROM_CONTINUE(       0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 )
	ROM_LOAD( "j1.bin", 0x00000, 0x40000, CRC(1547e595) SHA1(27f47b1afd9700afd9e8167d7e4e2888be34a9e5) )
ROM_END

/* B-Board 89625B-1 */
ROM_START( qad )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "qdu_36a.12f", 0x00000, 0x20000, CRC(de9c24a0) SHA1(458962943e8d97d1f4e5a15ac1c8d3bcaa32918b) )
	ROM_LOAD16_BYTE( "qdu_42a.12h", 0x00001, 0x20000, CRC(cfe36f0c) SHA1(370a47461b2dbb7807f547f5b4b33296572c5d78) )
	ROM_LOAD16_BYTE( "qdu_37a.13f", 0x40000, 0x20000, CRC(10d22320) SHA1(73b2876d5447f50a850c466789d9297269f732d6) )
	ROM_LOAD16_BYTE( "qdu_43a.13h", 0x40001, 0x20000, CRC(15e6beb9) SHA1(68d11e9bdd82775060281c5880f249e3515dc235) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_BYTE( "qd_09.4b",  0x000000, 0x20000, CRC(8c3f9f44) SHA1(b5ab20515b7f3e7db023be42d4c7ed1941b37d9b) )
	ROM_LOAD64_BYTE( "qd_01.4a",  0x000001, 0x20000, CRC(f688cf8f) SHA1(1b20095e536a24406513715cded249c9be1aa1d2) )
	ROM_LOAD64_BYTE( "qd_13.9b",  0x000002, 0x20000, CRC(afbd551b) SHA1(02e2f12196c542a004325689bda8949213ef0333) )
	ROM_LOAD64_BYTE( "qd_05.9a",  0x000003, 0x20000, CRC(c3db0910) SHA1(cf3aa3d3b64031dea92a80e5650151315cf871bf) )
	ROM_LOAD64_BYTE( "qd_24.5e",  0x000004, 0x20000, CRC(2f1bd0ec) SHA1(017e0dc521bf402c700775ee06cbc124f7ce0e3f) )
	ROM_LOAD64_BYTE( "qd_17.5c",  0x000005, 0x20000, CRC(a812f9e2) SHA1(9b7ceb347fbe00c40338b97ee6e8e4d1db9e7cb3) )
	ROM_LOAD64_BYTE( "qd_38.8h",  0x000006, 0x20000, CRC(ccdddd1f) SHA1(8304c4cdfaa1ae6b37e2733e9a6ddce9252fd43a) )
	ROM_LOAD64_BYTE( "qd_32.8f",  0x000007, 0x20000, CRC(a8d295d3) SHA1(d4d0bdaeb40f652ef33b317cb2b566b4c3550242) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "qd_23.13b",  0x00000, 0x08000, CRC(cfb5264b) SHA1(e662ed5555d02ccf4e62cdbcfa0bbfc019734ee1) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "qdu_30.12c", 0x00000, 0x20000, CRC(f190da84) SHA1(d5cd4c69b5d135a2f2fea8ca9631251c9da79e70) )
	ROM_LOAD( "qdu_31.13c", 0x20000, 0x20000, CRC(b7583f73) SHA1(3896e0fcf375e9e5d9ba70cc1ed001cd702f9ff7) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "qd22b.1a",     0x0000, 0x0117, CRC(783c53ab) SHA1(1bf87e5fe7e7cbcec0d76ed094dcac823e45af14) )
	ROM_LOAD( "iob1.12e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )    /* seen the same pcb with LWIO.12E */

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
ROM_END

/* B-Board 91634B-2, Japan Resale Ver. */
ROM_START( qadjr )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "qad_23a.8f", 0x00000, 0x80000, CRC(4d3553de) SHA1(07eabcb02fbbe11397ce91405a2e6bb53b3d5d4f) )
	ROM_LOAD16_WORD_SWAP( "qad_22a.7f", 0x80000, 0x80000, CRC(3191ddd0) SHA1(2806021a5dc809ca43692bbe9c4f5ef690c9ac14) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_WORD( "qad_01.3a", 0x000000, 0x80000, CRC(9d853b57) SHA1(380b41a3eced1f4a5523999b63d80b7593a85eca) )
	ROM_LOAD64_WORD( "qad_02.4a", 0x000002, 0x80000, CRC(b35976c4) SHA1(3e128db89186c4e88c46be9da310b755ae5b816c) )
	ROM_LOAD64_WORD( "qad_03.5a", 0x000004, 0x80000, CRC(cea4ca8c) SHA1(5c50758647419129f2b35ab4dc712796fa801c12) )
	ROM_LOAD64_WORD( "qad_04.6a", 0x000006, 0x80000, CRC(41b74d1b) SHA1(78aa2faec512c505f98b4e8053fc161941d41773) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "qad_09.12a", 0x00000, 0x08000, CRC(733161cc) SHA1(dfb8c5a1037bd3b2712fb327122ec39ceb993b8d) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "qad_18.11c", 0x00000, 0x20000, CRC(2bfe6f6a) SHA1(b2a98ac034c65b7ac8167431f05f35d4799032ea) )
	ROM_LOAD( "qad_19.12c", 0x20000, 0x20000, CRC(13d3236b) SHA1(785d49de484e9ac6971eaceebebfecb8e58563f6) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "qad63b.1a",    0x0000, 0x0117, CRC(b3312b13) SHA1(24bd6235fe273a672de2d4749e57280994f11819) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( wof )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "tk2e_23c.8f", 0x000000, 0x80000, CRC(0d708505) SHA1(10b8cb53a4600e3e76f471a3eee8a600e93096fc) )
	ROM_LOAD16_WORD_SWAP( "tk2e_22c.7f", 0x080000, 0x80000, CRC(608c17e3) SHA1(52c2d05279623d93b27856e6b76830796a089eae) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "tk2-1m.3a",  0x000000, 0x80000, CRC(0d9cb9bf) SHA1(cc7140e9a01a14b252cb1090bcea32b0de461928) )
	ROM_LOAD64_WORD( "tk2-3m.5a",  0x000002, 0x80000, CRC(45227027) SHA1(b21afc593f0d4d8909dfa621d659cbb40507d1b2) )
	ROM_LOAD64_WORD( "tk2-2m.4a",  0x000004, 0x80000, CRC(c5ca2460) SHA1(cbe14867f7b94b638ca80db7c8e0c60881183469) )
	ROM_LOAD64_WORD( "tk2-4m.6a",  0x000006, 0x80000, CRC(e349551c) SHA1(1d977bdf256accf750ad9930ec4a0a19bbf86964) )
	ROM_LOAD64_WORD( "tk2-5m.7a",  0x200000, 0x80000, CRC(291f0f0b) SHA1(094baf0f960f25fc2525b3b1cc378a49d9a0955d) )
	ROM_LOAD64_WORD( "tk2-7m.9a",  0x200002, 0x80000, CRC(3edeb949) SHA1(c155698dd9ee9eb24bbc97a21118ef2e897ea82f) )
	ROM_LOAD64_WORD( "tk2-6m.8a",  0x200004, 0x80000, CRC(1abd14d6) SHA1(dffff3126f102b4ec028a81405fc5b9bd7bb65b3) )
	ROM_LOAD64_WORD( "tk2-8m.10a", 0x200006, 0x80000, CRC(b27948e3) SHA1(870d5d23f56798831c641e877ea94217058b2ddc) )

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* QSound Z80 code */
	ROM_LOAD( "tk2_qa.5k",   0x00000, 0x08000, CRC(c9183a0d) SHA1(d8b1d41c572f08581f8ab9eb878de77d6ea8615d) )
	ROM_CONTINUE(            0x10000, 0x18000 )

	ROM_REGION( 0x200000, "qsound", 0 ) /* QSound samples */
	ROM_LOAD( "tk2-q1.1k",   0x000000, 0x80000, CRC(611268cf) SHA1(83ab059f2110fb25fdcff928d56b790fc1f5c975) )
	ROM_LOAD( "tk2-q2.2k",   0x080000, 0x80000, CRC(20f55ca9) SHA1(90134e9a9c4749bb65c728b66ea4dac1fd4d88a4) )
	ROM_LOAD( "tk2-q3.3k",   0x100000, 0x80000, CRC(bfcf6f52) SHA1(2a85ff3fc89b4cbabd20779ec12da2e116333c7c) )
	ROM_LOAD( "tk2-q4.4k",   0x180000, 0x80000, CRC(36642e88) SHA1(8ab25b19e2b67215a5cb1f3aa81b9d26009cfeb8) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg2",         0x0000, 0x0117, CRC(4386879a) SHA1(c36896d169d8c78393609acbbe4397931292a033) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "tk263b.1a",    0x0000, 0x0117, CRC(c4b0349b) SHA1(b4873dd5ad8735048deb3475222dde3c0b67eaaf) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )

	ROM_REGION( 0x0200, "dboardplds", 0 )
	ROM_LOAD( "d7l1.7l",      0x0000, 0x0117, CRC(27b7410d) SHA1(06d0cba0226850f100ff1f539bd7d5db0f90c730) )
	ROM_LOAD( "d8l1.8l",      0x0000, 0x0117, CRC(539fc7da) SHA1(cad5c91629c6247e49ccbbcbfe6b08229eafae07) )
	ROM_LOAD( "d9k1.9k",      0x0000, 0x0117, CRC(6c35c805) SHA1(641b9003b8e7fc969d0cd679f98e413ed2abe474) )
	ROM_LOAD( "d10f1.10f",    0x0000, 0x0117, CRC(6619c494) SHA1(3aef656c07182a2186f810f30e0d854dd5bd8d18) )
ROM_END

/* B-Board 91635B-2 */
/* Dumped from 2 different sets. Dumper's note for the second set: 'the c board was unmodified b21 with dead battery' */
ROM_START( wofr1 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "tk2e_23b.8f", 0x000000, 0x80000, CRC(11fb2ed1) SHA1(19e09ad6f9edc7997b030cddfe1d9c96d88135f2) )
	ROM_LOAD16_WORD_SWAP( "tk2e_22b.7f", 0x080000, 0x80000, CRC(479b3f24) SHA1(9fb8ae06856fe115addfb6794c28978a4f6716ec) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "tk2-1m.3a",  0x000000, 0x80000, CRC(0d9cb9bf) SHA1(cc7140e9a01a14b252cb1090bcea32b0de461928) )
	ROM_LOAD64_WORD( "tk2-3m.5a",  0x000002, 0x80000, CRC(45227027) SHA1(b21afc593f0d4d8909dfa621d659cbb40507d1b2) )
	ROM_LOAD64_WORD( "tk2-2m.4a",  0x000004, 0x80000, CRC(c5ca2460) SHA1(cbe14867f7b94b638ca80db7c8e0c60881183469) )
	ROM_LOAD64_WORD( "tk2-4m.6a",  0x000006, 0x80000, CRC(e349551c) SHA1(1d977bdf256accf750ad9930ec4a0a19bbf86964) )
	ROM_LOAD64_WORD( "tk2-5m.7a",  0x200000, 0x80000, CRC(291f0f0b) SHA1(094baf0f960f25fc2525b3b1cc378a49d9a0955d) )
	ROM_LOAD64_WORD( "tk2-7m.9a",  0x200002, 0x80000, CRC(3edeb949) SHA1(c155698dd9ee9eb24bbc97a21118ef2e897ea82f) )
	ROM_LOAD64_WORD( "tk2-6m.8a",  0x200004, 0x80000, CRC(1abd14d6) SHA1(dffff3126f102b4ec028a81405fc5b9bd7bb65b3) )
	ROM_LOAD64_WORD( "tk2-8m.10a", 0x200006, 0x80000, CRC(b27948e3) SHA1(870d5d23f56798831c641e877ea94217058b2ddc) )

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* QSound Z80 code */
	ROM_LOAD( "tk2_qa.5k",   0x00000, 0x08000, CRC(c9183a0d) SHA1(d8b1d41c572f08581f8ab9eb878de77d6ea8615d) )
	ROM_CONTINUE(            0x10000, 0x18000 )

	ROM_REGION( 0x200000, "qsound", 0 ) /* QSound samples */
	ROM_LOAD( "tk2-q1.1k",   0x000000, 0x80000, CRC(611268cf) SHA1(83ab059f2110fb25fdcff928d56b790fc1f5c975) )
	ROM_LOAD( "tk2-q2.2k",   0x080000, 0x80000, CRC(20f55ca9) SHA1(90134e9a9c4749bb65c728b66ea4dac1fd4d88a4) )
	ROM_LOAD( "tk2-q3.3k",   0x100000, 0x80000, CRC(bfcf6f52) SHA1(2a85ff3fc89b4cbabd20779ec12da2e116333c7c) )
	ROM_LOAD( "tk2-q4.4k",   0x180000, 0x80000, CRC(36642e88) SHA1(8ab25b19e2b67215a5cb1f3aa81b9d26009cfeb8) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg2",         0x0000, 0x0117, CRC(4386879a) SHA1(c36896d169d8c78393609acbbe4397931292a033) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "tk263b.1a",    0x0000, 0x0117, CRC(c4b0349b) SHA1(b4873dd5ad8735048deb3475222dde3c0b67eaaf) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )

	ROM_REGION( 0x0200, "dboardplds", 0 )
	ROM_LOAD( "d7l1.7l",      0x0000, 0x0117, CRC(27b7410d) SHA1(06d0cba0226850f100ff1f539bd7d5db0f90c730) )
	ROM_LOAD( "d8l1.8l",      0x0000, 0x0117, CRC(539fc7da) SHA1(cad5c91629c6247e49ccbbcbfe6b08229eafae07) )
	ROM_LOAD( "d9k1.9k",      0x0000, 0x0117, CRC(6c35c805) SHA1(641b9003b8e7fc969d0cd679f98e413ed2abe474) )
	ROM_LOAD( "d10f1.10f",    0x0000, 0x0117, CRC(6619c494) SHA1(3aef656c07182a2186f810f30e0d854dd5bd8d18) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( wofu )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "tk2u_23c.8f", 0x000000, 0x80000, CRC(29b89c12) SHA1(2b474b4f45a4ccb0db2a4d5e7ef30e28b5c6cc3a) )
	ROM_LOAD16_WORD_SWAP( "tk2u_22c.7f", 0x080000, 0x80000, CRC(f5af4774) SHA1(f6d53cf5b330e6d68f84da3e8c831a475585b93e) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "tk2-1m.3a",  0x000000, 0x80000, CRC(0d9cb9bf) SHA1(cc7140e9a01a14b252cb1090bcea32b0de461928) )
	ROM_LOAD64_WORD( "tk2-3m.5a",  0x000002, 0x80000, CRC(45227027) SHA1(b21afc593f0d4d8909dfa621d659cbb40507d1b2) )
	ROM_LOAD64_WORD( "tk2-2m.4a",  0x000004, 0x80000, CRC(c5ca2460) SHA1(cbe14867f7b94b638ca80db7c8e0c60881183469) )
	ROM_LOAD64_WORD( "tk2-4m.6a",  0x000006, 0x80000, CRC(e349551c) SHA1(1d977bdf256accf750ad9930ec4a0a19bbf86964) )
	ROM_LOAD64_WORD( "tk2-5m.7a",  0x200000, 0x80000, CRC(291f0f0b) SHA1(094baf0f960f25fc2525b3b1cc378a49d9a0955d) )
	ROM_LOAD64_WORD( "tk2-7m.9a",  0x200002, 0x80000, CRC(3edeb949) SHA1(c155698dd9ee9eb24bbc97a21118ef2e897ea82f) )
	ROM_LOAD64_WORD( "tk2-6m.8a",  0x200004, 0x80000, CRC(1abd14d6) SHA1(dffff3126f102b4ec028a81405fc5b9bd7bb65b3) )
	ROM_LOAD64_WORD( "tk2-8m.10a", 0x200006, 0x80000, CRC(b27948e3) SHA1(870d5d23f56798831c641e877ea94217058b2ddc) )

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* QSound Z80 code */
	ROM_LOAD( "tk2_qa.5k",   0x00000, 0x08000, CRC(c9183a0d) SHA1(d8b1d41c572f08581f8ab9eb878de77d6ea8615d) )
	ROM_CONTINUE(            0x10000, 0x18000 )

	ROM_REGION( 0x200000, "qsound", 0 ) /* QSound samples */
	ROM_LOAD( "tk2-q1.1k",   0x000000, 0x80000, CRC(611268cf) SHA1(83ab059f2110fb25fdcff928d56b790fc1f5c975) )
	ROM_LOAD( "tk2-q2.2k",   0x080000, 0x80000, CRC(20f55ca9) SHA1(90134e9a9c4749bb65c728b66ea4dac1fd4d88a4) )
	ROM_LOAD( "tk2-q3.3k",   0x100000, 0x80000, CRC(bfcf6f52) SHA1(2a85ff3fc89b4cbabd20779ec12da2e116333c7c) )
	ROM_LOAD( "tk2-q4.4k",   0x180000, 0x80000, CRC(36642e88) SHA1(8ab25b19e2b67215a5cb1f3aa81b9d26009cfeb8) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg2",         0x0000, 0x0117, CRC(4386879a) SHA1(c36896d169d8c78393609acbbe4397931292a033) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "tk263b.1a",    0x0000, 0x0117, CRC(c4b0349b) SHA1(b4873dd5ad8735048deb3475222dde3c0b67eaaf) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )

	ROM_REGION( 0x0200, "dboardplds", 0 )
	ROM_LOAD( "d7l1.7l",      0x0000, 0x0117, CRC(27b7410d) SHA1(06d0cba0226850f100ff1f539bd7d5db0f90c730) )
	ROM_LOAD( "d8l1.8l",      0x0000, 0x0117, CRC(539fc7da) SHA1(cad5c91629c6247e49ccbbcbfe6b08229eafae07) )
	ROM_LOAD( "d9k1.9k",      0x0000, 0x0117, CRC(6c35c805) SHA1(641b9003b8e7fc969d0cd679f98e413ed2abe474) )
	ROM_LOAD( "d10f1.10f",    0x0000, 0x0117, CRC(6619c494) SHA1(3aef656c07182a2186f810f30e0d854dd5bd8d18) )
ROM_END

/* B-Board 91634B-2 */
/* FIXME Probably this set uses a patched program ROM coming from a desuicided board, or simply the original C-Board
   is not a 92641C-1. A verification and a new fresh dump are needed to confirm if it's genuine or not. */
ROM_START( wofa )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "tk2a_23c.8f", 0x000000, 0x80000, CRC(2e024628) SHA1(647f8700fe3b410d798a823bac2e4a89cc9ad8d5) )  // patched?
	ROM_LOAD16_WORD_SWAP( "tk2a_22c.7f", 0x080000, 0x80000, CRC(900ad4cd) SHA1(988007447f93f3467029b9c29fd9670a7ecadaa3) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "tk2-1m.3a",  0x000000, 0x80000, CRC(0d9cb9bf) SHA1(cc7140e9a01a14b252cb1090bcea32b0de461928) )
	ROM_LOAD64_WORD( "tk2-3m.5a",  0x000002, 0x80000, CRC(45227027) SHA1(b21afc593f0d4d8909dfa621d659cbb40507d1b2) )
	ROM_LOAD64_WORD( "tk2-2m.4a",  0x000004, 0x80000, CRC(c5ca2460) SHA1(cbe14867f7b94b638ca80db7c8e0c60881183469) )
	ROM_LOAD64_WORD( "tk2-4m.6a",  0x000006, 0x80000, CRC(e349551c) SHA1(1d977bdf256accf750ad9930ec4a0a19bbf86964) )
	ROM_LOAD64_WORD( "tk2-5m.7a",  0x200000, 0x80000, CRC(291f0f0b) SHA1(094baf0f960f25fc2525b3b1cc378a49d9a0955d) )
	ROM_LOAD64_WORD( "tk2-7m.9a",  0x200002, 0x80000, CRC(3edeb949) SHA1(c155698dd9ee9eb24bbc97a21118ef2e897ea82f) )
	ROM_LOAD64_WORD( "tk2-6m.8a",  0x200004, 0x80000, CRC(1abd14d6) SHA1(dffff3126f102b4ec028a81405fc5b9bd7bb65b3) )
	ROM_LOAD64_WORD( "tk2-8m.10a", 0x200006, 0x80000, CRC(b27948e3) SHA1(870d5d23f56798831c641e877ea94217058b2ddc) )

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* QSound Z80 code */
	ROM_LOAD( "tk2_qa.5k",   0x00000, 0x08000, CRC(c9183a0d) SHA1(d8b1d41c572f08581f8ab9eb878de77d6ea8615d) )
	ROM_CONTINUE(            0x10000, 0x18000 )

	ROM_REGION( 0x200000, "qsound", 0 ) /* QSound samples */
	ROM_LOAD( "tk2-q1.1k",   0x000000, 0x80000, CRC(611268cf) SHA1(83ab059f2110fb25fdcff928d56b790fc1f5c975) )
	ROM_LOAD( "tk2-q2.2k",   0x080000, 0x80000, CRC(20f55ca9) SHA1(90134e9a9c4749bb65c728b66ea4dac1fd4d88a4) )
	ROM_LOAD( "tk2-q3.3k",   0x100000, 0x80000, CRC(bfcf6f52) SHA1(2a85ff3fc89b4cbabd20779ec12da2e116333c7c) )
	ROM_LOAD( "tk2-q4.4k",   0x180000, 0x80000, CRC(36642e88) SHA1(8ab25b19e2b67215a5cb1f3aa81b9d26009cfeb8) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg2",         0x0000, 0x0117, CRC(4386879a) SHA1(c36896d169d8c78393609acbbe4397931292a033) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "tk263b.1a",    0x0000, 0x0117, CRC(c4b0349b) SHA1(b4873dd5ad8735048deb3475222dde3c0b67eaaf) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )

	ROM_REGION( 0x0200, "dboardplds", 0 )
	ROM_LOAD( "d7l1.7l",      0x0000, 0x0117, CRC(27b7410d) SHA1(06d0cba0226850f100ff1f539bd7d5db0f90c730) )
	ROM_LOAD( "d8l1.8l",      0x0000, 0x0117, CRC(539fc7da) SHA1(cad5c91629c6247e49ccbbcbfe6b08229eafae07) )
	ROM_LOAD( "d9k1.9k",      0x0000, 0x0117, CRC(6c35c805) SHA1(641b9003b8e7fc969d0cd679f98e413ed2abe474) )
	ROM_LOAD( "d10f1.10f",    0x0000, 0x0117, CRC(6619c494) SHA1(3aef656c07182a2186f810f30e0d854dd5bd8d18) )
ROM_END

/* B-Board 91634B-2 */
ROM_START( wofj )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "tk2j_23c.8f", 0x000000, 0x80000, CRC(9b215a68) SHA1(fc83ed26441fbfb15e21b093c7a6bed44b586e51) )
	ROM_LOAD16_WORD_SWAP( "tk2j_22c.7f", 0x080000, 0x80000, CRC(b74b09ac) SHA1(3a44d6db5f51e1b5d2b43ef0ad1191da21e48427) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "tk2_01.3a",  0x000000, 0x80000, CRC(0d9cb9bf) SHA1(cc7140e9a01a14b252cb1090bcea32b0de461928) )    // == tk2-1m.3a
	ROM_LOAD64_WORD( "tk2_02.4a",  0x000002, 0x80000, CRC(45227027) SHA1(b21afc593f0d4d8909dfa621d659cbb40507d1b2) )    // == tk2-3m.5a
	ROM_LOAD64_WORD( "tk2_03.5a",  0x000004, 0x80000, CRC(c5ca2460) SHA1(cbe14867f7b94b638ca80db7c8e0c60881183469) )    // == tk2-2m.4a
	ROM_LOAD64_WORD( "tk2_04.6a",  0x000006, 0x80000, CRC(e349551c) SHA1(1d977bdf256accf750ad9930ec4a0a19bbf86964) )    // == tk2-4m.6a
	ROM_LOAD64_WORD( "tk2_05.7a",  0x200000, 0x80000, CRC(e4a44d53) SHA1(b747679f4d63e5e62d9fd81b3120fba0401fadfb) )
	ROM_LOAD64_WORD( "tk2_06.8a",  0x200002, 0x80000, CRC(58066ba8) SHA1(c93af968e21094d020e4b2002e0c6fc0d746af0b) )
	ROM_LOAD64_WORD( "tk2_07.9a",  0x200004, 0x80000, CRC(d706568e) SHA1(7886414dc86c42e35d24b85c4bfa41a9f0c167ac) )
	ROM_LOAD64_WORD( "tk2_08.10a", 0x200006, 0x80000, CRC(d4a19a02) SHA1(ff396b1d33d9b4842140f2c6d085fe05748e3244) )

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* QSound Z80 code */
	ROM_LOAD( "tk2_qa.5k",   0x00000, 0x08000, CRC(c9183a0d) SHA1(d8b1d41c572f08581f8ab9eb878de77d6ea8615d) )
	ROM_CONTINUE(            0x10000, 0x18000 )

	ROM_REGION( 0x200000, "qsound", 0 ) /* QSound samples */
	ROM_LOAD( "tk2-q1.1k",   0x000000, 0x80000, CRC(611268cf) SHA1(83ab059f2110fb25fdcff928d56b790fc1f5c975) )
	ROM_LOAD( "tk2-q2.2k",   0x080000, 0x80000, CRC(20f55ca9) SHA1(90134e9a9c4749bb65c728b66ea4dac1fd4d88a4) )
	ROM_LOAD( "tk2-q3.3k",   0x100000, 0x80000, CRC(bfcf6f52) SHA1(2a85ff3fc89b4cbabd20779ec12da2e116333c7c) )
	ROM_LOAD( "tk2-q4.4k",   0x180000, 0x80000, CRC(36642e88) SHA1(8ab25b19e2b67215a5cb1f3aa81b9d26009cfeb8) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg2",         0x0000, 0x0117, CRC(4386879a) SHA1(c36896d169d8c78393609acbbe4397931292a033) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "tk263b.1a",    0x0000, 0x0117, CRC(c4b0349b) SHA1(b4873dd5ad8735048deb3475222dde3c0b67eaaf) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )

	ROM_REGION( 0x0200, "dboardplds", 0 )
	ROM_LOAD( "d7l1.7l",      0x0000, 0x0117, CRC(27b7410d) SHA1(06d0cba0226850f100ff1f539bd7d5db0f90c730) )
	ROM_LOAD( "d8l1.8l",      0x0000, 0x0117, CRC(539fc7da) SHA1(cad5c91629c6247e49ccbbcbfe6b08229eafae07) )
	ROM_LOAD( "d9k1.9k",      0x0000, 0x0117, CRC(6c35c805) SHA1(641b9003b8e7fc969d0cd679f98e413ed2abe474) )
	ROM_LOAD( "d10f1.10f",    0x0000, 0x0117, CRC(6619c494) SHA1(3aef656c07182a2186f810f30e0d854dd5bd8d18) )
ROM_END

/* Chinese bootleg board without QSound */
ROM_START( wofhfh )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "23",       0x000000, 0x80000, CRC(6ae4b312) SHA1(fa39f69385d180d90bccd8c5dc9262edd04a6457) )
	ROM_LOAD16_WORD_SWAP( "22",       0x080000, 0x80000, CRC(94e8d01a) SHA1(875763f6b22734c1a5a890e6c8063515c134045b) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "1",   0x000000, 0x80000, CRC(0d9cb9bf) SHA1(cc7140e9a01a14b252cb1090bcea32b0de461928) )
	ROM_LOAD64_WORD( "2",   0x000002, 0x80000, CRC(45227027) SHA1(b21afc593f0d4d8909dfa621d659cbb40507d1b2) )
	ROM_LOAD64_WORD( "3",   0x000004, 0x80000, CRC(c5ca2460) SHA1(cbe14867f7b94b638ca80db7c8e0c60881183469) )
	ROM_LOAD64_WORD( "4",   0x000006, 0x80000, CRC(e349551c) SHA1(1d977bdf256accf750ad9930ec4a0a19bbf86964) )
	ROM_LOAD64_WORD( "5",   0x200000, 0x80000, CRC(34949d7b) SHA1(90925a77b08c97cfdbf0dbfbdaa359d1b33b6ae4) )
	ROM_LOAD64_WORD( "6",   0x200002, 0x80000, CRC(dfa70971) SHA1(477b99687de38220f0aec9fbba44db03f72cb62a) )
	ROM_LOAD64_WORD( "7",   0x200004, 0x80000, CRC(073686a6) SHA1(b774a8d4c6cdbedb123ac01455f718305f23b619) )
	ROM_LOAD64_WORD( "8",   0x200006, 0x80000, CRC(5300f8db) SHA1(b23a19910f680d60ff8afcbc15c471e74ee3569a) )

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* 128k for the audio CPU (+banks) */
	ROM_LOAD( "9",              0x00000, 0x08000, CRC(86fe8a97) SHA1(cab82bcd0f49bcb40201b439cfdd10266f46752a) )
	ROM_CONTINUE(               0x10000, 0x18000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* samples */
	ROM_LOAD( "18",             0x00000, 0x20000, CRC(c04be720) SHA1(2e544e0a0358b6afbdf826d35d9c4c59e4787a93) )
	ROM_LOAD( "19",             0x20000, 0x20000, CRC(fbb8d8c1) SHA1(8a7689bb7ed56243333133cbacf01a0ae825201e) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( sf2hf )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "s2te_23.8f", 0x000000, 0x80000, CRC(2dd72514) SHA1(4411353c389669299c27ac183c7e1caa3d4cec90) )
	ROM_LOAD16_WORD_SWAP( "s2te_22.7f", 0x080000, 0x80000, CRC(aea6e035) SHA1(ce5fe961b2c1c95d231d1235bfc03b47de489f2a) )
	ROM_LOAD16_WORD_SWAP( "s2te_21.6f", 0x100000, 0x80000, CRC(fd200288) SHA1(3817b67ab77c7b3d4a573a63f18671bea6905e26) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92-1m.3a",  0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92-3m.5a",  0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92-2m.4a",  0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92-4m.6a",  0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92-5m.7a",  0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92-7m.9a",  0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92-6m.8a",  0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92-8m.10a", 0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92-10m.3c", 0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92-12m.5c", 0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92-11m.4c", 0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92-13m.6c", 0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.11a",  0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s9263b.1a",    0x0000, 0x0117, CRC(0a7ecfe0) SHA1(f75e7eed4604fcf68273197fe3dd7f0d7a313ada) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( sf2hfu )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "s2tu_23.8f", 0x000000, 0x80000, CRC(89a1fc38) SHA1(aafb40fc311e318250973be8c6aa0d3f7902cb3c) )
	ROM_LOAD16_WORD_SWAP( "s2tu_22.7f", 0x080000, 0x80000, CRC(aea6e035) SHA1(ce5fe961b2c1c95d231d1235bfc03b47de489f2a) )   // == s2te_22.7f
	ROM_LOAD16_WORD_SWAP( "s2tu_21.6f", 0x100000, 0x80000, CRC(fd200288) SHA1(3817b67ab77c7b3d4a573a63f18671bea6905e26) )   // == s2te_21.6f

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92-1m.3a",  0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )
	ROM_LOAD64_WORD( "s92-3m.5a",  0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )
	ROM_LOAD64_WORD( "s92-2m.4a",  0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )
	ROM_LOAD64_WORD( "s92-4m.6a",  0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )
	ROM_LOAD64_WORD( "s92-5m.7a",  0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )
	ROM_LOAD64_WORD( "s92-7m.9a",  0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )
	ROM_LOAD64_WORD( "s92-6m.8a",  0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )
	ROM_LOAD64_WORD( "s92-8m.10a", 0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )
	ROM_LOAD64_WORD( "s92-10m.3c", 0x400000, 0x80000, CRC(960687d5) SHA1(2868c31121b1c7564e9767b9a19cdbf655c7ed1d) )
	ROM_LOAD64_WORD( "s92-12m.5c", 0x400002, 0x80000, CRC(978ecd18) SHA1(648a59706b93c84b4206a968ecbdc3e834c476f6) )
	ROM_LOAD64_WORD( "s92-11m.4c", 0x400004, 0x80000, CRC(d6ec9a0a) SHA1(ed6143f8737013b6ef1684e37c05e037e7a80dae) )
	ROM_LOAD64_WORD( "s92-13m.6c", 0x400006, 0x80000, CRC(ed2c67f6) SHA1(0083c0ffaf6fe7659ff0cf822be4346cd6e61329) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.11a",  0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s9263b.1a",    0x0000, 0x0117, CRC(0a7ecfe0) SHA1(f75e7eed4604fcf68273197fe3dd7f0d7a313ada) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91634B-2 */
ROM_START( sf2hfj )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "s2tj_23.8f", 0x000000, 0x80000, CRC(ea73b4dc) SHA1(efbc73277d00bac86505755db35225e14ea25a36) )
	ROM_LOAD16_WORD_SWAP( "s2tj_22.7f", 0x080000, 0x80000, CRC(aea6e035) SHA1(ce5fe961b2c1c95d231d1235bfc03b47de489f2a) )   // == s2te_22.7f
	ROM_LOAD16_WORD_SWAP( "s2tj_21.6f", 0x100000, 0x80000, CRC(fd200288) SHA1(3817b67ab77c7b3d4a573a63f18671bea6905e26) )   // == s2te_21.6f

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "s92_01.3a",  0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) )    // == s92-1m.3a
	ROM_LOAD64_WORD( "s92_02.4a",  0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) )    // == s92-3m.5a
	ROM_LOAD64_WORD( "s92_03.5a",  0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) )    // == s92-2m.4a
	ROM_LOAD64_WORD( "s92_04.6a",  0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) )    // == s92-4m.6a
	ROM_LOAD64_WORD( "s92_05.7a",  0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) )    // == s92-5m.7a
	ROM_LOAD64_WORD( "s92_06.8a",  0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) )    // == s92-7m.9a
	ROM_LOAD64_WORD( "s92_07.9a",  0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) )    // == s92-6m.8a
	ROM_LOAD64_WORD( "s92_08.10a", 0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) )    // == s92-8m.10a
	ROM_LOAD64_WORD( "s2t_10.3c",  0x400000, 0x80000, CRC(3c042686) SHA1(307e1ca8ad0b11f3265b7e5467ba4c90f90ec97f) )
	ROM_LOAD64_WORD( "s2t_11.4c",  0x400002, 0x80000, CRC(8b7e7183) SHA1(c8eaedfbddbf0b83311d2dbb9e19a1efef0dffa9) )
	ROM_LOAD64_WORD( "s2t_12.5c",  0x400004, 0x80000, CRC(293c888c) SHA1(5992ea9aa90fdd8b9dacca9d2a1fdaf25ac2cb65) )
	ROM_LOAD64_WORD( "s2t_13.6c",  0x400006, 0x80000, CRC(842b35a4) SHA1(35864a140a0c8d76501e69b2e01bc4ad76f27909) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "s92_09.12a",  0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )   // == s92_09.11a
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "s92_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
	ROM_LOAD( "s92_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "s9263b.1a",    0x0000, 0x0117, CRC(0a7ecfe0) SHA1(f75e7eed4604fcf68273197fe3dd7f0d7a313ada) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( dino )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "cde_23a.8f", 0x000000, 0x80000, CRC(8f4e585e) SHA1(55ecba3652066cdafad140c4524b1fc81228e69b) )
	ROM_LOAD16_WORD_SWAP( "cde_22a.7f", 0x080000, 0x80000, CRC(9278aa12) SHA1(58cbbd53a98abe640ccb233f8dbd8ca6d63475e7) )
	ROM_LOAD16_WORD_SWAP( "cde_21a.6f", 0x100000, 0x80000, CRC(66d23de2) SHA1(19b8a365f630411d524d055459020f4c8cf930f1) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "cd-1m.3a",  0x000000, 0x80000, CRC(8da4f917) SHA1(4f7b2304b7d9b545d6707d7ec921d3e28200699d) )
	ROM_LOAD64_WORD( "cd-3m.5a",  0x000002, 0x80000, CRC(6c40f603) SHA1(cdbd11dfcec08e87355d7e21e9fd39f7eacab016) )
	ROM_LOAD64_WORD( "cd-2m.4a",  0x000004, 0x80000, CRC(09c8fc2d) SHA1(d0c0a1258ec5dd484ab6ec1c5663425431f929ee) )
	ROM_LOAD64_WORD( "cd-4m.6a",  0x000006, 0x80000, CRC(637ff38f) SHA1(859926b33b9955b3ed67471c61faa442d42b9696) )
	ROM_LOAD64_WORD( "cd-5m.7a",  0x200000, 0x80000, CRC(470befee) SHA1(a42e38319e9b7424381352512f11bd8edf0bbb96) )
	ROM_LOAD64_WORD( "cd-7m.9a",  0x200002, 0x80000, CRC(22bfb7a3) SHA1(c44959bd3d42b9fc8ecb482dfaf63fbd469d2c3e) )
	ROM_LOAD64_WORD( "cd-6m.8a",  0x200004, 0x80000, CRC(e7599ac4) SHA1(0e788a38547a8701115d01190ddeaca64388db4d) )
	ROM_LOAD64_WORD( "cd-8m.10a", 0x200006, 0x80000, CRC(211b4b15) SHA1(374f6b185faa0f14f5c45b9b1d60d0772d93fb17) )

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* QSound Z80 code */
	ROM_LOAD( "cd_q.5k",    0x00000, 0x08000, CRC(605fdb0b) SHA1(9da90ddc6513aaaf2260f0c69719c6b0e585ba8c) )
	ROM_CONTINUE(           0x10000, 0x18000 )

	ROM_REGION( 0x200000, "qsound", 0 ) /* QSound samples */
	ROM_LOAD( "cd-q1.1k",   0x000000, 0x80000, CRC(60927775) SHA1(f8599bc84c38573ebbe8685822c58b6a38b50462) )
	ROM_LOAD( "cd-q2.2k",   0x080000, 0x80000, CRC(770f4c47) SHA1(fec8ef00a6669d4d5e37787ecc7b58ee46709326) )
	ROM_LOAD( "cd-q3.3k",   0x100000, 0x80000, CRC(2f273ffc) SHA1(f0de462f6c4d251911258e0ebd886152c14d1586) )
	ROM_LOAD( "cd-q4.4k",   0x180000, 0x80000, CRC(2c67821d) SHA1(6e2528d0b22508300a6a142a796dd3bf53a66946) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg2",         0x0000, 0x0117, CRC(4386879a) SHA1(c36896d169d8c78393609acbbe4397931292a033) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "cd63b.1a",     0x0000, 0x0117, CRC(ef72e902) SHA1(82fea3f63869c245d0dce2809085208fe719b57a) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )

	ROM_REGION( 0x0200, "dboardplds", 0 )
	ROM_LOAD( "d7l1.7l",      0x0000, 0x0117, CRC(27b7410d) SHA1(06d0cba0226850f100ff1f539bd7d5db0f90c730) )
	ROM_LOAD( "d8l1.8l",      0x0000, 0x0117, CRC(539fc7da) SHA1(cad5c91629c6247e49ccbbcbfe6b08229eafae07) )
	ROM_LOAD( "d9k2.9k",      0x0000, 0x0117, CRC(cd85a156) SHA1(a88f8939c5d93e65d7bcc0eb3ee5b6f4f1114e3a) )
	ROM_LOAD( "d10f1.10f",    0x0000, 0x0117, CRC(6619c494) SHA1(3aef656c07182a2186f810f30e0d854dd5bd8d18) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( dinou )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "cdu_23a.8f", 0x000000, 0x80000, CRC(7c2543cd) SHA1(6b7a90392fe4c31b2d57620b0ddcb3412401efc3) )
	ROM_LOAD16_WORD_SWAP( "cdu_22a.7f", 0x080000, 0x80000, CRC(d19f981e) SHA1(acb951caba3867c21149286185b94beb37721bd2) )
	ROM_LOAD16_WORD_SWAP( "cdu_21a.6f", 0x100000, 0x80000, CRC(66d23de2) SHA1(19b8a365f630411d524d055459020f4c8cf930f1) )   // == cde_21a.6f

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "cd-1m.3a",  0x000000, 0x80000, CRC(8da4f917) SHA1(4f7b2304b7d9b545d6707d7ec921d3e28200699d) )
	ROM_LOAD64_WORD( "cd-3m.5a",  0x000002, 0x80000, CRC(6c40f603) SHA1(cdbd11dfcec08e87355d7e21e9fd39f7eacab016) )
	ROM_LOAD64_WORD( "cd-2m.4a",  0x000004, 0x80000, CRC(09c8fc2d) SHA1(d0c0a1258ec5dd484ab6ec1c5663425431f929ee) )
	ROM_LOAD64_WORD( "cd-4m.6a",  0x000006, 0x80000, CRC(637ff38f) SHA1(859926b33b9955b3ed67471c61faa442d42b9696) )
	ROM_LOAD64_WORD( "cd-5m.7a",  0x200000, 0x80000, CRC(470befee) SHA1(a42e38319e9b7424381352512f11bd8edf0bbb96) )
	ROM_LOAD64_WORD( "cd-7m.9a",  0x200002, 0x80000, CRC(22bfb7a3) SHA1(c44959bd3d42b9fc8ecb482dfaf63fbd469d2c3e) )
	ROM_LOAD64_WORD( "cd-6m.8a",  0x200004, 0x80000, CRC(e7599ac4) SHA1(0e788a38547a8701115d01190ddeaca64388db4d) )
	ROM_LOAD64_WORD( "cd-8m.10a", 0x200006, 0x80000, CRC(211b4b15) SHA1(374f6b185faa0f14f5c45b9b1d60d0772d93fb17) )

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* QSound Z80 code */
	ROM_LOAD( "cd_q.5k",    0x00000, 0x08000, CRC(605fdb0b) SHA1(9da90ddc6513aaaf2260f0c69719c6b0e585ba8c) )
	ROM_CONTINUE(           0x10000, 0x18000 )

	ROM_REGION( 0x200000, "qsound", 0 ) /* QSound samples */
	ROM_LOAD( "cd-q1.1k",   0x000000, 0x80000, CRC(60927775) SHA1(f8599bc84c38573ebbe8685822c58b6a38b50462) )
	ROM_LOAD( "cd-q2.2k",   0x080000, 0x80000, CRC(770f4c47) SHA1(fec8ef00a6669d4d5e37787ecc7b58ee46709326) )
	ROM_LOAD( "cd-q3.3k",   0x100000, 0x80000, CRC(2f273ffc) SHA1(f0de462f6c4d251911258e0ebd886152c14d1586) )
	ROM_LOAD( "cd-q4.4k",   0x180000, 0x80000, CRC(2c67821d) SHA1(6e2528d0b22508300a6a142a796dd3bf53a66946) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg2",         0x0000, 0x0117, CRC(4386879a) SHA1(c36896d169d8c78393609acbbe4397931292a033) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "cd63b.1a",     0x0000, 0x0117, CRC(ef72e902) SHA1(82fea3f63869c245d0dce2809085208fe719b57a) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )

	ROM_REGION( 0x0200, "dboardplds", 0 )
	ROM_LOAD( "d7l1.7l",      0x0000, 0x0117, CRC(27b7410d) SHA1(06d0cba0226850f100ff1f539bd7d5db0f90c730) )
	ROM_LOAD( "d8l1.8l",      0x0000, 0x0117, CRC(539fc7da) SHA1(cad5c91629c6247e49ccbbcbfe6b08229eafae07) )
	ROM_LOAD( "d9k2.9k",      0x0000, 0x0117, CRC(cd85a156) SHA1(a88f8939c5d93e65d7bcc0eb3ee5b6f4f1114e3a) )
	ROM_LOAD( "d10f1.10f",    0x0000, 0x0117, CRC(6619c494) SHA1(3aef656c07182a2186f810f30e0d854dd5bd8d18) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( dinoa )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "cdt_23.8f", 0x000000, 0x80000, CRC(f477f7a0) SHA1(bad4979093ced8ae09273454c80de704b798e590) )
	ROM_LOAD16_WORD_SWAP( "cdt_22.7f", 0x080000, 0x80000, CRC(1e534ca5) SHA1(8233a1a5de2a1fdd2b1c2b8616eda29db4be725a) )
	ROM_LOAD16_WORD_SWAP( "cdt_21.6f", 0x100000, 0x80000, CRC(74b04329) SHA1(138db4749acfb922bb31023cc0203df40e467fba) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "cd-1m.3a",  0x000000, 0x80000, CRC(8da4f917) SHA1(4f7b2304b7d9b545d6707d7ec921d3e28200699d) )
	ROM_LOAD64_WORD( "cd-3m.5a",  0x000002, 0x80000, CRC(6c40f603) SHA1(cdbd11dfcec08e87355d7e21e9fd39f7eacab016) )
	ROM_LOAD64_WORD( "cd-2m.4a",  0x000004, 0x80000, CRC(09c8fc2d) SHA1(d0c0a1258ec5dd484ab6ec1c5663425431f929ee) )
	ROM_LOAD64_WORD( "cd-4m.6a",  0x000006, 0x80000, CRC(637ff38f) SHA1(859926b33b9955b3ed67471c61faa442d42b9696) )
	ROM_LOAD64_WORD( "cd-5m.7a",  0x200000, 0x80000, CRC(470befee) SHA1(a42e38319e9b7424381352512f11bd8edf0bbb96) )
	ROM_LOAD64_WORD( "cd-7m.9a",  0x200002, 0x80000, CRC(22bfb7a3) SHA1(c44959bd3d42b9fc8ecb482dfaf63fbd469d2c3e) )
	ROM_LOAD64_WORD( "cd-6m.8a",  0x200004, 0x80000, CRC(e7599ac4) SHA1(0e788a38547a8701115d01190ddeaca64388db4d) )
	ROM_LOAD64_WORD( "cd-8m.10a", 0x200006, 0x80000, CRC(211b4b15) SHA1(374f6b185faa0f14f5c45b9b1d60d0772d93fb17) )

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* QSound Z80 code */
	ROM_LOAD( "cd_q.5k",    0x00000, 0x08000, CRC(605fdb0b) SHA1(9da90ddc6513aaaf2260f0c69719c6b0e585ba8c) )
	ROM_CONTINUE(           0x10000, 0x18000 )

	ROM_REGION( 0x200000, "qsound", 0 ) /* QSound samples */
	ROM_LOAD( "cd-q1.1k",   0x000000, 0x80000, CRC(60927775) SHA1(f8599bc84c38573ebbe8685822c58b6a38b50462) )
	ROM_LOAD( "cd-q2.2k",   0x080000, 0x80000, CRC(770f4c47) SHA1(fec8ef00a6669d4d5e37787ecc7b58ee46709326) )
	ROM_LOAD( "cd-q3.3k",   0x100000, 0x80000, CRC(2f273ffc) SHA1(f0de462f6c4d251911258e0ebd886152c14d1586) )
	ROM_LOAD( "cd-q4.4k",   0x180000, 0x80000, CRC(2c67821d) SHA1(6e2528d0b22508300a6a142a796dd3bf53a66946) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg2",         0x0000, 0x0117, CRC(4386879a) SHA1(c36896d169d8c78393609acbbe4397931292a033) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "cd63b.1a",     0x0000, 0x0117, CRC(ef72e902) SHA1(82fea3f63869c245d0dce2809085208fe719b57a) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )

	ROM_REGION( 0x0200, "dboardplds", 0 )
	ROM_LOAD( "d7l1.7l",      0x0000, 0x0117, CRC(27b7410d) SHA1(06d0cba0226850f100ff1f539bd7d5db0f90c730) )
	ROM_LOAD( "d8l1.8l",      0x0000, 0x0117, CRC(539fc7da) SHA1(cad5c91629c6247e49ccbbcbfe6b08229eafae07) )
	ROM_LOAD( "d9k2.9k",      0x0000, 0x0117, CRC(cd85a156) SHA1(a88f8939c5d93e65d7bcc0eb3ee5b6f4f1114e3a) )
	ROM_LOAD( "d10f1.10f",    0x0000, 0x0117, CRC(6619c494) SHA1(3aef656c07182a2186f810f30e0d854dd5bd8d18) )
ROM_END

/* B-Board 91634B-2 */
ROM_START( dinoj )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "cdj_23a.8f", 0x000000, 0x80000, CRC(5f3ece96) SHA1(33ffb08ff8c5d3bfb2fa17fa00f254da2fc61f44) )
	ROM_LOAD16_WORD_SWAP( "cdj_22a.7f", 0x080000, 0x80000, CRC(a0d8de29) SHA1(79d916f181804b6176581efe2a1b7f210ec79c07) )
	ROM_LOAD16_WORD_SWAP( "cdj_21a.6f", 0x100000, 0x80000, CRC(66d23de2) SHA1(19b8a365f630411d524d055459020f4c8cf930f1) )   // == cde_21a.6f

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "cd_01.3a",  0x000000, 0x80000, CRC(8da4f917) SHA1(4f7b2304b7d9b545d6707d7ec921d3e28200699d) ) // == cd-1m.3a
	ROM_LOAD64_WORD( "cd_02.4a",  0x000002, 0x80000, CRC(6c40f603) SHA1(cdbd11dfcec08e87355d7e21e9fd39f7eacab016) ) // == cd-3m.5a
	ROM_LOAD64_WORD( "cd_03.5a",  0x000004, 0x80000, CRC(09c8fc2d) SHA1(d0c0a1258ec5dd484ab6ec1c5663425431f929ee) ) // == cd-2m.4a
	ROM_LOAD64_WORD( "cd_04.6a",  0x000006, 0x80000, CRC(637ff38f) SHA1(859926b33b9955b3ed67471c61faa442d42b9696) ) // == cd-4m.6a
	ROM_LOAD64_WORD( "cd_05.7a",  0x200000, 0x80000, CRC(470befee) SHA1(a42e38319e9b7424381352512f11bd8edf0bbb96) ) // == cd-5m.7a
	ROM_LOAD64_WORD( "cd_06.8a",  0x200002, 0x80000, CRC(22bfb7a3) SHA1(c44959bd3d42b9fc8ecb482dfaf63fbd469d2c3e) ) // == cd-7m.9a
	ROM_LOAD64_WORD( "cd_07.9a",  0x200004, 0x80000, CRC(e7599ac4) SHA1(0e788a38547a8701115d01190ddeaca64388db4d) ) // == cd-6m.8a
	ROM_LOAD64_WORD( "cd_08.10a", 0x200006, 0x80000, CRC(211b4b15) SHA1(374f6b185faa0f14f5c45b9b1d60d0772d93fb17) ) // == cd-8m.10a

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* QSound Z80 code */
	ROM_LOAD( "cd_q.5k",    0x00000, 0x08000, CRC(605fdb0b) SHA1(9da90ddc6513aaaf2260f0c69719c6b0e585ba8c) )
	ROM_CONTINUE(           0x10000, 0x18000 )

	ROM_REGION( 0x200000, "qsound", 0 ) /* QSound samples */
	ROM_LOAD( "cd-q1.1k",   0x000000, 0x80000, CRC(60927775) SHA1(f8599bc84c38573ebbe8685822c58b6a38b50462) )
	ROM_LOAD( "cd-q2.2k",   0x080000, 0x80000, CRC(770f4c47) SHA1(fec8ef00a6669d4d5e37787ecc7b58ee46709326) )
	ROM_LOAD( "cd-q3.3k",   0x100000, 0x80000, CRC(2f273ffc) SHA1(f0de462f6c4d251911258e0ebd886152c14d1586) )
	ROM_LOAD( "cd-q4.4k",   0x180000, 0x80000, CRC(2c67821d) SHA1(6e2528d0b22508300a6a142a796dd3bf53a66946) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg2",         0x0000, 0x0117, CRC(4386879a) SHA1(c36896d169d8c78393609acbbe4397931292a033) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "cd63b.1a",     0x0000, 0x0117, CRC(ef72e902) SHA1(82fea3f63869c245d0dce2809085208fe719b57a) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )

	ROM_REGION( 0x0200, "dboardplds", 0 )
	ROM_LOAD( "d7l1.7l",      0x0000, 0x0117, CRC(27b7410d) SHA1(06d0cba0226850f100ff1f539bd7d5db0f90c730) )
	ROM_LOAD( "d8l1.8l",      0x0000, 0x0117, CRC(539fc7da) SHA1(cad5c91629c6247e49ccbbcbfe6b08229eafae07) )
	ROM_LOAD( "d9k2.9k",      0x0000, 0x0117, CRC(cd85a156) SHA1(a88f8939c5d93e65d7bcc0eb3ee5b6f4f1114e3a) )
	ROM_LOAD( "d10f1.10f",    0x0000, 0x0117, CRC(6619c494) SHA1(3aef656c07182a2186f810f30e0d854dd5bd8d18) )
ROM_END

/* Chinese bootleg board */
ROM_START( dinohunt )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "u23",  0x000000, 0x80000, CRC(8d5ddc5d) SHA1(3ed1da71d73425308d0c3ac1a01a7242e60b6677) )
	ROM_LOAD16_WORD_SWAP( "u22",  0x080000, 0x80000, CRC(f72cd219) SHA1(a3f580bd328b54d2546ae7e94262c1a706d27172) )
	ROM_LOAD16_WORD_SWAP( "u21",  0x100000, 0x80000, CRC(bc275b76) SHA1(1f7f7533b4ccb511dc479f531d9d6740d3ca6712) )
	ROM_LOAD16_WORD_SWAP( "u20",  0x180000, 0x80000, CRC(8987c975) SHA1(3fd856805d627f855f72acc7aacc2e8ecf0f12bd) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "u1",   0x000000, 0x80000, CRC(a01a9fb5) SHA1(d1ab4c7ead04932c60923363a0a661d0d1472aaa) )
	ROM_LOAD64_WORD( "u2",   0x000002, 0x80000, CRC(bdf02c17) SHA1(046ba4c589a22a7ac65c86baaae18eff0bff6ecb) )
	ROM_LOAD64_WORD( "u3",   0x000004, 0x80000, CRC(058beefa) SHA1(0d09ab5a200643b5a16e0313228f7ea243752bd0) )
	ROM_LOAD64_WORD( "u4",   0x000006, 0x80000, CRC(5028a9f1) SHA1(ff3a988153860e76beb79918ad896e702c3ecd3d) )
	ROM_LOAD64_WORD( "u5",   0x200000, 0x80000, CRC(d77f89ea) SHA1(1dd8c2a18742fd8483ecade31aed384c686ed244) )
	ROM_LOAD64_WORD( "u6",   0x200002, 0x80000, CRC(bfbcb034) SHA1(c1eb4d420b10ffa05197d9a82c7adac4d265c546) )
	ROM_LOAD64_WORD( "u7",   0x200004, 0x80000, CRC(a2544d4e) SHA1(def704055e041bc7c3b8cf7977249b69aa7924a8) )
	ROM_LOAD64_WORD( "u8",   0x200006, 0x80000, CRC(8869bbb1) SHA1(e53f77baffb913bf3ef5396d84fbfbaec6a90ca2) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "u9",             0x00000, 0x08000, CRC(2eb16a83) SHA1(067ea0bfc2c1e73520d6b836c72fbb9da9998311) )
	ROM_CONTINUE(               0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* samples */
	ROM_LOAD( "u18",            0x00000, 0x20000, CRC(8d2899ba) SHA1(0b3ac6cf2ce0323e3bfc9da3ebfcb0fd14bc405b) )
	ROM_LOAD( "u19",            0x20000, 0x20000, CRC(b34a4b42) SHA1(3eeb9e33bb911359e03d44949ac58439a3d3d54b) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( punisher )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "pse_26.11e",    0x000000, 0x20000, CRC(389a99d2) SHA1(e97f4225554e4603cb0e3edd296a90bb2e467ca7) )
	ROM_LOAD16_BYTE( "pse_30.11f",    0x000001, 0x20000, CRC(68fb06ac) SHA1(189e25ca7e4aaa80492c03ce06696952cc1b1553) )
	ROM_LOAD16_BYTE( "pse_27.12e",    0x040000, 0x20000, CRC(3eb181c3) SHA1(a2442449f4bbe3be03d2be7d4e2cbb69f9741dac) )
	ROM_LOAD16_BYTE( "pse_31.12f",    0x040001, 0x20000, CRC(37108e7b) SHA1(78aaa6e2913e6b1b852b39416557ac4a394d7d8b) )
	ROM_LOAD16_BYTE( "pse_24.9e",     0x080000, 0x20000, CRC(0f434414) SHA1(aaacf835a93551fc792571d6e824a01f3c5d4469) )
	ROM_LOAD16_BYTE( "pse_28.9f",     0x080001, 0x20000, CRC(b732345d) SHA1(472d84f846e9f73f129562d78352376194e0211e) )
	ROM_LOAD16_BYTE( "pse_25.10e",    0x0c0000, 0x20000, CRC(b77102e2) SHA1(2e39b2c2c0eed5ca2320a57e69bcf377f809a20c) )
	ROM_LOAD16_BYTE( "pse_29.10f",    0x0c0001, 0x20000, CRC(ec037bce) SHA1(f86e7feb63d7662a38048e6d51d7b5a69dafaffb) )
	ROM_LOAD16_WORD_SWAP( "ps_21.6f", 0x100000, 0x80000, CRC(8affa5a9) SHA1(268760b83b1723ff50a019ec51ef7af2e49935bf) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "ps-1m.3a",  0x000000, 0x80000, CRC(77b7ccab) SHA1(e08e5d55a79e4c0c8ca819d6d7d2a14f753c6ec3) )
	ROM_LOAD64_WORD( "ps-3m.5a",  0x000002, 0x80000, CRC(0122720b) SHA1(5f0d3097e097f64106048156fbb0d343fe78fffa) )
	ROM_LOAD64_WORD( "ps-2m.4a",  0x000004, 0x80000, CRC(64fa58d4) SHA1(d4a774285ed15273195b6b26d2965ce370e54e73) )
	ROM_LOAD64_WORD( "ps-4m.6a",  0x000006, 0x80000, CRC(60da42c8) SHA1(95eec4a58d9628a2d9764951dd8dc11e4860a899) )
	ROM_LOAD64_WORD( "ps-5m.7a",  0x200000, 0x80000, CRC(c54ea839) SHA1(0733f37329edd9d0cace1319a7544b40aa7ecb0b) )
	ROM_LOAD64_WORD( "ps-7m.9a",  0x200002, 0x80000, CRC(04c5acbd) SHA1(fddc94b0f36d4d22d7c357856ae15b7514c342d3) )
	ROM_LOAD64_WORD( "ps-6m.8a",  0x200004, 0x80000, CRC(a544f4cc) SHA1(9552df8934ba25f19a22f2e07783712d8c8ef03c) )
	ROM_LOAD64_WORD( "ps-8m.10a", 0x200006, 0x80000, CRC(8f02f436) SHA1(a2f0ebb7e9593469c7b843f8962a66f3d77f79e5) )

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* QSound Z80 code */
	ROM_LOAD( "ps_q.5k",    0x00000, 0x08000, CRC(49ff4446) SHA1(87af12f87a940a6c5428b4574ad44a4b54867bc3) )
	ROM_CONTINUE(           0x10000, 0x18000 )

	ROM_REGION( 0x200000, "qsound", 0 ) /* QSound samples */
	ROM_LOAD( "ps-q1.1k",   0x000000, 0x80000, CRC(31fd8726) SHA1(1d73a76682e9fb908db0c55b9a18163f7539fea1) )
	ROM_LOAD( "ps-q2.2k",   0x080000, 0x80000, CRC(980a9eef) SHA1(36571381f349bc726508a7e618ba1c635ec9d271) )
	ROM_LOAD( "ps-q3.3k",   0x100000, 0x80000, CRC(0dd44491) SHA1(903cea1d7f3120545ea3229d30fbd687d11ad68f) )
	ROM_LOAD( "ps-q4.4k",   0x180000, 0x80000, CRC(bed42f03) SHA1(21302f7e75f9c795392a3b34e16a959fc5f6e4e9) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg2",         0x0000, 0x0117, CRC(4386879a) SHA1(c36896d169d8c78393609acbbe4397931292a033) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "ps63b.1a",     0x0000, 0x0117, CRC(03a758b0) SHA1(f0035f0dac927af50e21f5c57b7b4462856aa50c) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )

	ROM_REGION( 0x0200, "dboardplds", 0 )
	ROM_LOAD( "d7l1.7l",      0x0000, 0x0117, CRC(27b7410d) SHA1(06d0cba0226850f100ff1f539bd7d5db0f90c730) )
	ROM_LOAD( "d8l1.8l",      0x0000, 0x0117, CRC(539fc7da) SHA1(cad5c91629c6247e49ccbbcbfe6b08229eafae07) )
	ROM_LOAD( "d9k2.9k",      0x0000, 0x0117, CRC(cd85a156) SHA1(a88f8939c5d93e65d7bcc0eb3ee5b6f4f1114e3a) )
	ROM_LOAD( "d10f1.10f",    0x0000, 0x0117, CRC(6619c494) SHA1(3aef656c07182a2186f810f30e0d854dd5bd8d18) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( punisheru )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE ( "psu_26.11e",   0x000000, 0x20000, CRC(9236d121) SHA1(52d5d00009f61089157319943cde8f1a1ed48ad4) )
	ROM_LOAD16_BYTE ( "psu_30.11f",   0x000001, 0x20000, CRC(8320e501) SHA1(bb3b74135df9dd494a277a1bc3bef2917351203f) )
	ROM_LOAD16_BYTE ( "psu_27.12e",   0x040000, 0x20000, CRC(61c960a1) SHA1(f8fe651283cc1f138d013cab65b833505de6df9f) )
	ROM_LOAD16_BYTE ( "psu_31.12f",   0x040001, 0x20000, CRC(78d4c298) SHA1(6e7fbaed9ad9230a6e5035c6eda64b2f1f83048c) )
	ROM_LOAD16_BYTE ( "psu_24.9e",    0x080000, 0x20000, CRC(1cfecad7) SHA1(f4dcf5066dc59507cece0c53ccc208e4323ae26f) )
	ROM_LOAD16_BYTE ( "psu_28.9f",    0x080001, 0x20000, CRC(bdf921c1) SHA1(89a6709756c7c32e7c888806f983ce5af61cfcef) )
	ROM_LOAD16_BYTE ( "psu_25.10e",   0x0c0000, 0x20000, CRC(c51acc94) SHA1(34ffd6392914e3e67d7d0804215bd1193846b554) )
	ROM_LOAD16_BYTE ( "psu_29.10f",   0x0c0001, 0x20000, CRC(52dce1ca) SHA1(45277abe34feacdcaedaec56f513b7437d4260e9) )
	ROM_LOAD16_WORD_SWAP( "ps_21.6f", 0x100000, 0x80000, CRC(8affa5a9) SHA1(268760b83b1723ff50a019ec51ef7af2e49935bf) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "ps-1m.3a",  0x000000, 0x80000, CRC(77b7ccab) SHA1(e08e5d55a79e4c0c8ca819d6d7d2a14f753c6ec3) )
	ROM_LOAD64_WORD( "ps-3m.5a",  0x000002, 0x80000, CRC(0122720b) SHA1(5f0d3097e097f64106048156fbb0d343fe78fffa) )
	ROM_LOAD64_WORD( "ps-2m.4a",  0x000004, 0x80000, CRC(64fa58d4) SHA1(d4a774285ed15273195b6b26d2965ce370e54e73) )
	ROM_LOAD64_WORD( "ps-4m.6a",  0x000006, 0x80000, CRC(60da42c8) SHA1(95eec4a58d9628a2d9764951dd8dc11e4860a899) )
	ROM_LOAD64_WORD( "ps-5m.7a",  0x200000, 0x80000, CRC(c54ea839) SHA1(0733f37329edd9d0cace1319a7544b40aa7ecb0b) )
	ROM_LOAD64_WORD( "ps-7m.9a",  0x200002, 0x80000, CRC(04c5acbd) SHA1(fddc94b0f36d4d22d7c357856ae15b7514c342d3) )
	ROM_LOAD64_WORD( "ps-6m.8a",  0x200004, 0x80000, CRC(a544f4cc) SHA1(9552df8934ba25f19a22f2e07783712d8c8ef03c) )
	ROM_LOAD64_WORD( "ps-8m.10a", 0x200006, 0x80000, CRC(8f02f436) SHA1(a2f0ebb7e9593469c7b843f8962a66f3d77f79e5) )

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* QSound Z80 code */
	ROM_LOAD( "ps_q.5k",    0x00000, 0x08000, CRC(49ff4446) SHA1(87af12f87a940a6c5428b4574ad44a4b54867bc3) )
	ROM_CONTINUE(           0x10000, 0x18000 )

	ROM_REGION( 0x200000, "qsound", 0 ) /* QSound samples */
	ROM_LOAD( "ps-q1.1k",   0x000000, 0x80000, CRC(31fd8726) SHA1(1d73a76682e9fb908db0c55b9a18163f7539fea1) )
	ROM_LOAD( "ps-q2.2k",   0x080000, 0x80000, CRC(980a9eef) SHA1(36571381f349bc726508a7e618ba1c635ec9d271) )
	ROM_LOAD( "ps-q3.3k",   0x100000, 0x80000, CRC(0dd44491) SHA1(903cea1d7f3120545ea3229d30fbd687d11ad68f) )
	ROM_LOAD( "ps-q4.4k",   0x180000, 0x80000, CRC(bed42f03) SHA1(21302f7e75f9c795392a3b34e16a959fc5f6e4e9) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg2",         0x0000, 0x0117, CRC(4386879a) SHA1(c36896d169d8c78393609acbbe4397931292a033) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "ps63b.1a",     0x0000, 0x0117, CRC(03a758b0) SHA1(f0035f0dac927af50e21f5c57b7b4462856aa50c) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )

	ROM_REGION( 0x0200, "dboardplds", 0 )
	ROM_LOAD( "d7l1.7l",      0x0000, 0x0117, CRC(27b7410d) SHA1(06d0cba0226850f100ff1f539bd7d5db0f90c730) )
	ROM_LOAD( "d8l1.8l",      0x0000, 0x0117, CRC(539fc7da) SHA1(cad5c91629c6247e49ccbbcbfe6b08229eafae07) )
	ROM_LOAD( "d9k2.9k",      0x0000, 0x0117, CRC(cd85a156) SHA1(a88f8939c5d93e65d7bcc0eb3ee5b6f4f1114e3a) )
	ROM_LOAD( "d10f1.10f",    0x0000, 0x0117, CRC(6619c494) SHA1(3aef656c07182a2186f810f30e0d854dd5bd8d18) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( punisherh )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "psh_26.11e",    0x000000, 0x20000, CRC(6ad2bb83) SHA1(c1c5e8922af948955f53eb56d1b9902dd8944a71) )
	ROM_LOAD16_BYTE( "psh_30.11f",    0x000001, 0x20000, CRC(058d3659) SHA1(b3fad151f80e0cc21afff3ec00b7871f21d40437) )
	ROM_LOAD16_BYTE( "psh_27.12e",    0x040000, 0x20000, CRC(579f4fd3) SHA1(5459da68e844db6e6249186aa31fe71aa72d9ff4) )
	ROM_LOAD16_BYTE( "psh_31.12f",    0x040001, 0x20000, CRC(2c9f70b5) SHA1(c5d89f37def4b9340a0325e616306f0e9e68ab13) )
	ROM_LOAD16_BYTE( "psh_24.9e",     0x080000, 0x20000, CRC(faa14841) SHA1(d998f1cdfba3305eb1398909d4b07e7492dbdec0) )
	ROM_LOAD16_BYTE( "psh_28.9f",     0x080001, 0x20000, CRC(5c5b1f20) SHA1(47ee27dc9849a69c54a5a0e269b20e61f17f34c0) )
	ROM_LOAD16_BYTE( "psh_25.10e",    0x0c0000, 0x20000, CRC(724fdfda) SHA1(23d75e7864dfa0da8c2231785878fb05a5aac628) )
	ROM_LOAD16_BYTE( "psh_29.10f",    0x0c0001, 0x20000, CRC(779cf901) SHA1(6761edcf26084e44783f3eecd5822e50f1a099bd) )
	ROM_LOAD16_WORD_SWAP( "ps_21.6f", 0x100000, 0x80000, CRC(8affa5a9) SHA1(268760b83b1723ff50a019ec51ef7af2e49935bf) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "ps-1m.3a",  0x000000, 0x80000, CRC(77b7ccab) SHA1(e08e5d55a79e4c0c8ca819d6d7d2a14f753c6ec3) )
	ROM_LOAD64_WORD( "ps-3m.5a",  0x000002, 0x80000, CRC(0122720b) SHA1(5f0d3097e097f64106048156fbb0d343fe78fffa) )
	ROM_LOAD64_WORD( "ps-2m.4a",  0x000004, 0x80000, CRC(64fa58d4) SHA1(d4a774285ed15273195b6b26d2965ce370e54e73) )
	ROM_LOAD64_WORD( "ps-4m.6a",  0x000006, 0x80000, CRC(60da42c8) SHA1(95eec4a58d9628a2d9764951dd8dc11e4860a899) )
	ROM_LOAD64_WORD( "ps-5m.7a",  0x200000, 0x80000, CRC(c54ea839) SHA1(0733f37329edd9d0cace1319a7544b40aa7ecb0b) )
	ROM_LOAD64_WORD( "ps-7m.9a",  0x200002, 0x80000, CRC(04c5acbd) SHA1(fddc94b0f36d4d22d7c357856ae15b7514c342d3) )
	ROM_LOAD64_WORD( "ps-6m.8a",  0x200004, 0x80000, CRC(a544f4cc) SHA1(9552df8934ba25f19a22f2e07783712d8c8ef03c) )
	ROM_LOAD64_WORD( "ps-8m.10a", 0x200006, 0x80000, CRC(8f02f436) SHA1(a2f0ebb7e9593469c7b843f8962a66f3d77f79e5) )

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* QSound Z80 code */
	ROM_LOAD( "ps_q.5k",    0x00000, 0x08000, CRC(49ff4446) SHA1(87af12f87a940a6c5428b4574ad44a4b54867bc3) )
	ROM_CONTINUE(           0x10000, 0x18000 )

	ROM_REGION( 0x200000, "qsound", 0 ) /* QSound samples */
	ROM_LOAD( "ps-q1.1k",   0x000000, 0x80000, CRC(31fd8726) SHA1(1d73a76682e9fb908db0c55b9a18163f7539fea1) )
	ROM_LOAD( "ps-q2.2k",   0x080000, 0x80000, CRC(980a9eef) SHA1(36571381f349bc726508a7e618ba1c635ec9d271) )
	ROM_LOAD( "ps-q3.3k",   0x100000, 0x80000, CRC(0dd44491) SHA1(903cea1d7f3120545ea3229d30fbd687d11ad68f) )
	ROM_LOAD( "ps-q4.4k",   0x180000, 0x80000, CRC(bed42f03) SHA1(21302f7e75f9c795392a3b34e16a959fc5f6e4e9) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg2",         0x0000, 0x0117, CRC(4386879a) SHA1(c36896d169d8c78393609acbbe4397931292a033) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "ps63b.1a",     0x0000, 0x0117, CRC(03a758b0) SHA1(f0035f0dac927af50e21f5c57b7b4462856aa50c) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )

	ROM_REGION( 0x0200, "dboardplds", 0 )
	ROM_LOAD( "d7l1.7l",      0x0000, 0x0117, CRC(27b7410d) SHA1(06d0cba0226850f100ff1f539bd7d5db0f90c730) )
	ROM_LOAD( "d8l1.8l",      0x0000, 0x0117, CRC(539fc7da) SHA1(cad5c91629c6247e49ccbbcbfe6b08229eafae07) )
	ROM_LOAD( "d9k2.9k",      0x0000, 0x0117, CRC(cd85a156) SHA1(a88f8939c5d93e65d7bcc0eb3ee5b6f4f1114e3a) )
	ROM_LOAD( "d10f1.10f",    0x0000, 0x0117, CRC(6619c494) SHA1(3aef656c07182a2186f810f30e0d854dd5bd8d18) )
ROM_END

/* B-Board 91634B-2 */
ROM_START( punisherj )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "psj_23.8f", 0x000000, 0x80000, CRC(6b2fda52) SHA1(5f95a79b7b802609ae9ddd6641cc52610d428bf4) )
	ROM_LOAD16_WORD_SWAP( "psj_22.7f", 0x080000, 0x80000, CRC(e01036bc) SHA1(a01886014dabe8f9ab45619865c6bd9f27472eae) )
	ROM_LOAD16_WORD_SWAP( "psj_21.6f", 0x100000, 0x80000, CRC(8affa5a9) SHA1(268760b83b1723ff50a019ec51ef7af2e49935bf) )    // == ps_21.6f

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "ps_01.3a",  0x000000, 0x80000, CRC(77b7ccab) SHA1(e08e5d55a79e4c0c8ca819d6d7d2a14f753c6ec3) ) // == ps-1m.3a
	ROM_LOAD64_WORD( "ps_02.4a",  0x000002, 0x80000, CRC(0122720b) SHA1(5f0d3097e097f64106048156fbb0d343fe78fffa) ) // == ps-3m.5a
	ROM_LOAD64_WORD( "ps_03.5a",  0x000004, 0x80000, CRC(64fa58d4) SHA1(d4a774285ed15273195b6b26d2965ce370e54e73) ) // == ps-2m.4a
	ROM_LOAD64_WORD( "ps_04.6a",  0x000006, 0x80000, CRC(60da42c8) SHA1(95eec4a58d9628a2d9764951dd8dc11e4860a899) ) // == ps-4m.6a
	ROM_LOAD64_WORD( "ps_05.7a",  0x200000, 0x80000, CRC(c54ea839) SHA1(0733f37329edd9d0cace1319a7544b40aa7ecb0b) ) // == ps-5m.7a
	ROM_LOAD64_WORD( "ps_06.8a",  0x200002, 0x80000, CRC(04c5acbd) SHA1(fddc94b0f36d4d22d7c357856ae15b7514c342d3) ) // == ps-7m.9a
	ROM_LOAD64_WORD( "ps_07.9a",  0x200004, 0x80000, CRC(a544f4cc) SHA1(9552df8934ba25f19a22f2e07783712d8c8ef03c) ) // == ps-6m.8a
	ROM_LOAD64_WORD( "ps_08.10a", 0x200006, 0x80000, CRC(8f02f436) SHA1(a2f0ebb7e9593469c7b843f8962a66f3d77f79e5) ) // == ps-8m.10a

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* QSound Z80 code */
	ROM_LOAD( "ps_q.5k",    0x00000, 0x08000, CRC(49ff4446) SHA1(87af12f87a940a6c5428b4574ad44a4b54867bc3) )
	ROM_CONTINUE(           0x10000, 0x18000 )

	ROM_REGION( 0x200000, "qsound", 0 ) /* QSound samples */
	ROM_LOAD( "ps-q1.1k",   0x000000, 0x80000, CRC(31fd8726) SHA1(1d73a76682e9fb908db0c55b9a18163f7539fea1) )
	ROM_LOAD( "ps-q2.2k",   0x080000, 0x80000, CRC(980a9eef) SHA1(36571381f349bc726508a7e618ba1c635ec9d271) )
	ROM_LOAD( "ps-q3.3k",   0x100000, 0x80000, CRC(0dd44491) SHA1(903cea1d7f3120545ea3229d30fbd687d11ad68f) )
	ROM_LOAD( "ps-q4.4k",   0x180000, 0x80000, CRC(bed42f03) SHA1(21302f7e75f9c795392a3b34e16a959fc5f6e4e9) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg2",         0x0000, 0x0117, CRC(4386879a) SHA1(c36896d169d8c78393609acbbe4397931292a033) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "ps63b.1a",     0x0000, 0x0117, CRC(03a758b0) SHA1(f0035f0dac927af50e21f5c57b7b4462856aa50c) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )

	ROM_REGION( 0x0200, "dboardplds", 0 )
	ROM_LOAD( "d7l1.7l",      0x0000, 0x0117, CRC(27b7410d) SHA1(06d0cba0226850f100ff1f539bd7d5db0f90c730) )
	ROM_LOAD( "d8l1.8l",      0x0000, 0x0117, CRC(539fc7da) SHA1(cad5c91629c6247e49ccbbcbfe6b08229eafae07) )
	ROM_LOAD( "d9k2.9k",      0x0000, 0x0117, CRC(cd85a156) SHA1(a88f8939c5d93e65d7bcc0eb3ee5b6f4f1114e3a) )
	ROM_LOAD( "d10f1.10f",    0x0000, 0x0117, CRC(6619c494) SHA1(3aef656c07182a2186f810f30e0d854dd5bd8d18) )
ROM_END

/* Chinese bootleg board */
ROM_START( punisherbz )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "23.096",   0x000000, 0x80000, CRC(bfa45d23) SHA1(56d04d02c9b9b22603205b1ce9e376874d2eaa05) )
	ROM_LOAD16_WORD_SWAP( "22.096",   0x080000, 0x80000, CRC(092578a4) SHA1(c3c8f98f8b8fc9c71b33fb906b4c591067b4355b) )
	ROM_LOAD16_WORD_SWAP( "21.096",   0x100000, 0x80000, CRC(d21ccddb) SHA1(295c886cd14ee54e12acfe3f1e8f13513442c8c0) )
	ROM_LOAD16_WORD_SWAP( "20.096",   0x180000, 0x80000, CRC(f9f334ce) SHA1(22e58ce5096b95138a30e9726e1169895118e990) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "1.096",         0x000000, 0x80000, CRC(ad468e07) SHA1(1ee8ecfd001b447982bc16972575eb2c5e01bc51) )
	ROM_LOAD64_WORD( "2.096",         0x000002, 0x80000, CRC(b9fdb6b5) SHA1(991fe0ded5c859f1e25766e9417e1ad691205e59) )
	ROM_LOAD64_WORD( "3.096",         0x000004, 0x80000, CRC(be0b1a78) SHA1(d002bd90d0f239d29f5ea481e455d85a1c088516) )
	ROM_LOAD64_WORD( "4.096",         0x000006, 0x80000, CRC(bba67a43) SHA1(9cd51a06a7661b2f78059ac392423f73de5cf56f) )
	ROM_LOAD64_WORD( "ps_gfx5.rom",   0x200000, 0x80000, CRC(c54ea839) SHA1(0733f37329edd9d0cace1319a7544b40aa7ecb0b) )
	ROM_LOAD64_WORD( "ps_gfx7.rom",   0x200002, 0x80000, CRC(04c5acbd) SHA1(fddc94b0f36d4d22d7c357856ae15b7514c342d3) )
	ROM_LOAD64_WORD( "ps_gfx6.rom",   0x200004, 0x80000, CRC(a544f4cc) SHA1(9552df8934ba25f19a22f2e07783712d8c8ef03c) )
	ROM_LOAD64_WORD( "ps_gfx8.rom",   0x200006, 0x80000, CRC(8f02f436) SHA1(a2f0ebb7e9593469c7b843f8962a66f3d77f79e5) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "9.512",          0x00000, 0x08000, CRC(b8367eb5) SHA1(ec3db29fdd6200e9a8f4f8073a7e34aef731354f) )
	ROM_CONTINUE(               0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* samples */
	ROM_LOAD( "18.010",         0x00000, 0x20000, CRC(375c66e7) SHA1(36189e23209ce4ae5d9cbabd1574540d0591e7b3) )
	ROM_LOAD( "19.010",         0x20000, 0x20000, CRC(eb5ca884) SHA1(3592c69f77c7cd6ee241d6c1079c34a3e58abb5b) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( slammast )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "mbe_23e.8f", 0x000000, 0x80000, CRC(5394057a) SHA1(57f8b40c0a15e82c98ce5f0a8c4bdf60a1bc3107) )
	ROM_LOAD16_BYTE( "mbe_24b.9e",      0x080000, 0x20000, CRC(95d5e729) SHA1(df3be896e55c92eb50887a4317178a3d11048433) )
	ROM_LOAD16_BYTE( "mbe_28b.9f",      0x080001, 0x20000, CRC(b1c7cbcb) SHA1(cf5ad72be4a055db876e7347b1826325b9bf81d9) )
	ROM_LOAD16_BYTE( "mbe_25b.10e",     0x0c0000, 0x20000, CRC(a50d3fd4) SHA1(dc3d108c3bc27f45b8b2e11919ba2a86e05b41d1) )
	ROM_LOAD16_BYTE( "mbe_29b.10f",     0x0c0001, 0x20000, CRC(08e32e56) SHA1(70ad78b079f777ec02089f0df20ce2baad7adce5) )
	ROM_LOAD16_WORD_SWAP( "mbe_21a.6f", 0x100000, 0x80000, CRC(d5007b05) SHA1(c55e55908aeda40ca2318c76ccbc05d333676875) )
	ROM_LOAD16_WORD_SWAP( "mbe_20a.5f", 0x180000, 0x80000, CRC(aeb557b0) SHA1(530551942961d776f0a85852e02bb243840ca671) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "mb-1m.3a",  0x000000, 0x80000, CRC(41468e06) SHA1(fb365798f2889a20eebaea2393c9c2c8827003c4) )
	ROM_LOAD64_WORD( "mb-3m.5a",  0x000002, 0x80000, CRC(f453aa9e) SHA1(24a103dc6f0dc96f8d0f6164ad732909c9cd2d6a) )
	ROM_LOAD64_WORD( "mb-2m.4a",  0x000004, 0x80000, CRC(2ffbfea8) SHA1(13e30133664a009686e1114c92b558bdbb91ea32) )
	ROM_LOAD64_WORD( "mb-4m.6a",  0x000006, 0x80000, CRC(1eb9841d) SHA1(685da3e011a96b36be9f639a241b2f8f27da4629) )
	ROM_LOAD64_WORD( "mb-5m.7a",  0x200000, 0x80000, CRC(506b9dc9) SHA1(933bf2fb9bcc1a408f961f0e7052da80144bddad) )
	ROM_LOAD64_WORD( "mb-7m.9a",  0x200002, 0x80000, CRC(aff8c2fb) SHA1(ce37a6d5b1eb58c2d74f23f84ec824c214c93217) )
	ROM_LOAD64_WORD( "mb-6m.8a",  0x200004, 0x80000, CRC(b76c70e9) SHA1(c21e255815ec9a985919dbd760ed266c28bd47cd) )
	ROM_LOAD64_WORD( "mb-8m.10a", 0x200006, 0x80000, CRC(e60c9556) SHA1(b91c14092aa8dbb0922d96998123ef1970a658f6) )
	ROM_LOAD64_WORD( "mb-10m.3c", 0x400000, 0x80000, CRC(97976ff5) SHA1(ec9d3460816ab971a02fbce42960283091777e47) )
	ROM_LOAD64_WORD( "mb-12m.5c", 0x400002, 0x80000, CRC(b350a840) SHA1(2b8b996cd08051e7e8e134bff5448775d78058a0) )
	ROM_LOAD64_WORD( "mb-11m.4c", 0x400004, 0x80000, CRC(8fb94743) SHA1(294f6182c8a41b640d1f57cb5e3a2abce3b06482) )
	ROM_LOAD64_WORD( "mb-13m.6c", 0x400006, 0x80000, CRC(da810d5f) SHA1(392bbd405244b8c99024c9228cfec6a7ef0accdb) )

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* QSound Z80 code */
	ROM_LOAD( "mb_qa.5k",   0x00000, 0x08000, CRC(e21a03c4) SHA1(98c03fd2c9b6bf8a4fc25a4edca87fff7c3c3819) )
	ROM_CONTINUE(           0x10000, 0x18000 )

	ROM_REGION( 0x8000, "user1", 0 )
	ROM_COPY( "audiocpu", 0x000000, 0x00000, 0x8000 )

	ROM_REGION( 0x400000, "qsound", 0 ) /* QSound samples */
	ROM_LOAD( "mb-q1.1k",   0x000000, 0x80000, CRC(0630c3ce) SHA1(520fc74c5c3638f611fa2f1b5efb08b91747e29b) )
	ROM_LOAD( "mb-q2.2k",   0x080000, 0x80000, CRC(354f9c21) SHA1(1dc6b39791fd0f760697f409a6b62361a7bf62e9) )
	ROM_LOAD( "mb-q3.3k",   0x100000, 0x80000, CRC(7838487c) SHA1(056b7da05cfca46873edacd674ca25c70855c6db) )
	ROM_LOAD( "mb-q4.4k",   0x180000, 0x80000, CRC(ab66e087) SHA1(066ea69a0157e8647eea3c44d0a1843898860678) )
	ROM_LOAD( "mb-q5.1m",   0x200000, 0x80000, CRC(c789fef2) SHA1(10d1e3d92288fccd4e064a3716a788a165efc3c9) )
	ROM_LOAD( "mb-q6.2m",   0x280000, 0x80000, CRC(ecb81b61) SHA1(e339f21ae47de4782f3b338befcdac659c3503f6) )
	ROM_LOAD( "mb-q7.3m",   0x300000, 0x80000, CRC(041e49ba) SHA1(3220b033a5c0cfbbe75c0c113cf2db39fb093a7e) )
	ROM_LOAD( "mb-q8.4m",   0x380000, 0x80000, CRC(59fe702a) SHA1(807178dfc6d864e49fd7aabb5c4895835cf0e85b) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg2",         0x0000, 0x0117, CRC(4386879a) SHA1(c36896d169d8c78393609acbbe4397931292a033) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "mb63b.1a",     0x0000, 0x0117, CRC(b8392f02) SHA1(8dedf9f43b30991694f8009302ca628cb50a0b1a) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )

	ROM_REGION( 0x0200, "dboardplds", 0 )
	ROM_LOAD( "d7l1.7l",      0x0000, 0x0117, CRC(27b7410d) SHA1(06d0cba0226850f100ff1f539bd7d5db0f90c730) )
	ROM_LOAD( "d8l1.8l",      0x0000, 0x0117, CRC(539fc7da) SHA1(cad5c91629c6247e49ccbbcbfe6b08229eafae07) )
	ROM_LOAD( "d9k2.9k",      0x0000, 0x0117, CRC(cd85a156) SHA1(a88f8939c5d93e65d7bcc0eb3ee5b6f4f1114e3a) )
	ROM_LOAD( "d10f1.10f",    0x0000, 0x0117, CRC(6619c494) SHA1(3aef656c07182a2186f810f30e0d854dd5bd8d18) )
ROM_END

/* B-Board 91635B-2 */
ROM_START( slammastu )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "mbu_23e.8f", 0x000000, 0x80000, CRC(224f0062) SHA1(d961f2e7db7acac576539c24a69e7dd9bf8fc406) )
	ROM_LOAD16_BYTE( "mbu_24b.9e",      0x080000, 0x20000, CRC(95d5e729) SHA1(df3be896e55c92eb50887a4317178a3d11048433) )   // == mbe_24b.9e
	ROM_LOAD16_BYTE( "mbu_28b.9f",      0x080001, 0x20000, CRC(b1c7cbcb) SHA1(cf5ad72be4a055db876e7347b1826325b9bf81d9) )   // == mbe_28b.9f
	ROM_LOAD16_BYTE( "mbu_25b.10e",     0x0c0000, 0x20000, CRC(a50d3fd4) SHA1(dc3d108c3bc27f45b8b2e11919ba2a86e05b41d1) )   // == mbe_25b.10e
	ROM_LOAD16_BYTE( "mbu_29b.10f",     0x0c0001, 0x20000, CRC(08e32e56) SHA1(70ad78b079f777ec02089f0df20ce2baad7adce5) )   // == mbe_29b.10f
	ROM_LOAD16_WORD_SWAP( "mbu_21a.6f", 0x100000, 0x80000, CRC(d5007b05) SHA1(c55e55908aeda40ca2318c76ccbc05d333676875) )   // == mbe_21a.6f
	ROM_LOAD16_WORD_SWAP( "mbu_20a.5f", 0x180000, 0x80000, CRC(fc848af5) SHA1(cd3f6e50779b89ee57a9d08bfa1d58dea286457c) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "mb-1m.3a",  0x000000, 0x80000, CRC(41468e06) SHA1(fb365798f2889a20eebaea2393c9c2c8827003c4) )
	ROM_LOAD64_WORD( "mb-3m.5a",  0x000002, 0x80000, CRC(f453aa9e) SHA1(24a103dc6f0dc96f8d0f6164ad732909c9cd2d6a) )
	ROM_LOAD64_WORD( "mb-2m.4a",  0x000004, 0x80000, CRC(2ffbfea8) SHA1(13e30133664a009686e1114c92b558bdbb91ea32) )
	ROM_LOAD64_WORD( "mb-4m.6a",  0x000006, 0x80000, CRC(1eb9841d) SHA1(685da3e011a96b36be9f639a241b2f8f27da4629) )
	ROM_LOAD64_WORD( "mb-5m.7a",  0x200000, 0x80000, CRC(506b9dc9) SHA1(933bf2fb9bcc1a408f961f0e7052da80144bddad) )
	ROM_LOAD64_WORD( "mb-7m.9a",  0x200002, 0x80000, CRC(aff8c2fb) SHA1(ce37a6d5b1eb58c2d74f23f84ec824c214c93217) )
	ROM_LOAD64_WORD( "mb-6m.8a",  0x200004, 0x80000, CRC(b76c70e9) SHA1(c21e255815ec9a985919dbd760ed266c28bd47cd) )
	ROM_LOAD64_WORD( "mb-8m.10a", 0x200006, 0x80000, CRC(e60c9556) SHA1(b91c14092aa8dbb0922d96998123ef1970a658f6) )
	ROM_LOAD64_WORD( "mb-10m.3c", 0x400000, 0x80000, CRC(97976ff5) SHA1(ec9d3460816ab971a02fbce42960283091777e47) )
	ROM_LOAD64_WORD( "mb-12m.5c", 0x400002, 0x80000, CRC(b350a840) SHA1(2b8b996cd08051e7e8e134bff5448775d78058a0) )
	ROM_LOAD64_WORD( "mb-11m.4c", 0x400004, 0x80000, CRC(8fb94743) SHA1(294f6182c8a41b640d1f57cb5e3a2abce3b06482) )
	ROM_LOAD64_WORD( "mb-13m.6c", 0x400006, 0x80000, CRC(da810d5f) SHA1(392bbd405244b8c99024c9228cfec6a7ef0accdb) )

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* QSound Z80 code */
	ROM_LOAD( "mb_qa.5k",   0x00000, 0x08000, CRC(e21a03c4) SHA1(98c03fd2c9b6bf8a4fc25a4edca87fff7c3c3819) )
	ROM_CONTINUE(           0x10000, 0x18000 )

	ROM_REGION( 0x8000, "user1", 0 )
	ROM_COPY( "audiocpu", 0x000000, 0x00000, 0x8000 )

	ROM_REGION( 0x400000, "qsound", 0 ) /* QSound samples */
	ROM_LOAD( "mb-q1.1k",   0x000000, 0x80000, CRC(0630c3ce) SHA1(520fc74c5c3638f611fa2f1b5efb08b91747e29b) )
	ROM_LOAD( "mb-q2.2k",   0x080000, 0x80000, CRC(354f9c21) SHA1(1dc6b39791fd0f760697f409a6b62361a7bf62e9) )
	ROM_LOAD( "mb-q3.3k",   0x100000, 0x80000, CRC(7838487c) SHA1(056b7da05cfca46873edacd674ca25c70855c6db) )
	ROM_LOAD( "mb-q4.4k",   0x180000, 0x80000, CRC(ab66e087) SHA1(066ea69a0157e8647eea3c44d0a1843898860678) )
	ROM_LOAD( "mb-q5.1m",   0x200000, 0x80000, CRC(c789fef2) SHA1(10d1e3d92288fccd4e064a3716a788a165efc3c9) )
	ROM_LOAD( "mb-q6.2m",   0x280000, 0x80000, CRC(ecb81b61) SHA1(e339f21ae47de4782f3b338befcdac659c3503f6) )
	ROM_LOAD( "mb-q7.3m",   0x300000, 0x80000, CRC(041e49ba) SHA1(3220b033a5c0cfbbe75c0c113cf2db39fb093a7e) )
	ROM_LOAD( "mb-q8.4m",   0x380000, 0x80000, CRC(59fe702a) SHA1(807178dfc6d864e49fd7aabb5c4895835cf0e85b) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg2",         0x0000, 0x0117, CRC(4386879a) SHA1(c36896d169d8c78393609acbbe4397931292a033) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "mb63b.1a",     0x0000, 0x0117, CRC(b8392f02) SHA1(8dedf9f43b30991694f8009302ca628cb50a0b1a) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )

	ROM_REGION( 0x0200, "dboardplds", 0 )
	ROM_LOAD( "d7l1.7l",      0x0000, 0x0117, CRC(27b7410d) SHA1(06d0cba0226850f100ff1f539bd7d5db0f90c730) )
	ROM_LOAD( "d8l1.8l",      0x0000, 0x0117, CRC(539fc7da) SHA1(cad5c91629c6247e49ccbbcbfe6b08229eafae07) )
	ROM_LOAD( "d9k2.9k",      0x0000, 0x0117, CRC(cd85a156) SHA1(a88f8939c5d93e65d7bcc0eb3ee5b6f4f1114e3a) )
	ROM_LOAD( "d10f1.10f",    0x0000, 0x0117, CRC(6619c494) SHA1(3aef656c07182a2186f810f30e0d854dd5bd8d18) )
ROM_END

/* B-Board 91634B-2 */
ROM_START( mbomberj )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "mbj_23e.8f", 0x000000, 0x80000, CRC(0d06036a) SHA1(e1b102888038b4bb612a41ac94a43333d468a245) )
	ROM_LOAD16_WORD_SWAP( "mbj_22b.7f", 0x080000, 0x80000, CRC(acd38478) SHA1(0bed74b52838006c6ec3661f92058dc2aa3e8193) )
	ROM_LOAD16_WORD_SWAP( "mbj_21a.6f", 0x100000, 0x80000, CRC(d5007b05) SHA1(c55e55908aeda40ca2318c76ccbc05d333676875) )   // == mbe_21a.6f
	ROM_LOAD16_WORD_SWAP( "mbj_20a.5f", 0x180000, 0x80000, CRC(aeb557b0) SHA1(530551942961d776f0a85852e02bb243840ca671) )   // == mbe_20a.5f

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "mb_01.3a",  0x000000, 0x80000, CRC(a53b1c81) SHA1(d1efb88eeaf6e30e51aaf1432078003e52454dd9) )
	ROM_LOAD64_WORD( "mb_02.4a",  0x000002, 0x80000, CRC(23fe10f6) SHA1(deefa7cac4394b0642f7fb444f9374dbe0bc8843) )
	ROM_LOAD64_WORD( "mb_03.5a",  0x000004, 0x80000, CRC(cb866c2f) SHA1(b087f52e3b2a514a209612319d1d7c4f1c12b8bd) )
	ROM_LOAD64_WORD( "mb_04.6a",  0x000006, 0x80000, CRC(c9143e75) SHA1(e30090625ef6ac971a4f65d53f5458cebb5f146c) )
	ROM_LOAD64_WORD( "mb_05.7a",  0x200000, 0x80000, CRC(506b9dc9) SHA1(933bf2fb9bcc1a408f961f0e7052da80144bddad) ) // == mb-5m.7a
	ROM_LOAD64_WORD( "mb_06.8a",  0x200002, 0x80000, CRC(aff8c2fb) SHA1(ce37a6d5b1eb58c2d74f23f84ec824c214c93217) ) // == mb-7m.9a
	ROM_LOAD64_WORD( "mb_07.9a",  0x200004, 0x80000, CRC(b76c70e9) SHA1(c21e255815ec9a985919dbd760ed266c28bd47cd) ) // == mb-6m.8a
	ROM_LOAD64_WORD( "mb_08.10a", 0x200006, 0x80000, CRC(e60c9556) SHA1(b91c14092aa8dbb0922d96998123ef1970a658f6) ) // == mb-8m.10a
	ROM_LOAD64_WORD( "mb_10.3c",  0x400000, 0x80000, CRC(97976ff5) SHA1(ec9d3460816ab971a02fbce42960283091777e47) ) // == mb-10m.3c
	ROM_LOAD64_WORD( "mb_11.4c",  0x400002, 0x80000, CRC(b350a840) SHA1(2b8b996cd08051e7e8e134bff5448775d78058a0) ) // == mb-12m.5c
	ROM_LOAD64_WORD( "mb_12.5c",  0x400004, 0x80000, CRC(8fb94743) SHA1(294f6182c8a41b640d1f57cb5e3a2abce3b06482) ) // == mb-11m.4c
	ROM_LOAD64_WORD( "mb_13.6c",  0x400006, 0x80000, CRC(da810d5f) SHA1(392bbd405244b8c99024c9228cfec6a7ef0accdb) ) // == mb-13m.6c

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* QSound Z80 code */
	ROM_LOAD( "mb_qa.5k",   0x00000, 0x08000, CRC(e21a03c4) SHA1(98c03fd2c9b6bf8a4fc25a4edca87fff7c3c3819) )
	ROM_CONTINUE(           0x10000, 0x18000 )

	ROM_REGION( 0x8000, "user1", 0 )
	ROM_COPY( "audiocpu", 0x000000, 0x00000, 0x8000 )

	ROM_REGION( 0x400000, "qsound", 0 ) /* QSound samples */
	ROM_LOAD( "mb-q1.1k",   0x000000, 0x80000, CRC(0630c3ce) SHA1(520fc74c5c3638f611fa2f1b5efb08b91747e29b) )
	ROM_LOAD( "mb-q2.2k",   0x080000, 0x80000, CRC(354f9c21) SHA1(1dc6b39791fd0f760697f409a6b62361a7bf62e9) )
	ROM_LOAD( "mb-q3.3k",   0x100000, 0x80000, CRC(7838487c) SHA1(056b7da05cfca46873edacd674ca25c70855c6db) )
	ROM_LOAD( "mb-q4.4k",   0x180000, 0x80000, CRC(ab66e087) SHA1(066ea69a0157e8647eea3c44d0a1843898860678) )
	ROM_LOAD( "mb-q5.1m",   0x200000, 0x80000, CRC(c789fef2) SHA1(10d1e3d92288fccd4e064a3716a788a165efc3c9) )
	ROM_LOAD( "mb-q6.2m",   0x280000, 0x80000, CRC(ecb81b61) SHA1(e339f21ae47de4782f3b338befcdac659c3503f6) )
	ROM_LOAD( "mb-q7.3m",   0x300000, 0x80000, CRC(041e49ba) SHA1(3220b033a5c0cfbbe75c0c113cf2db39fb093a7e) )
	ROM_LOAD( "mb-q8.4m",   0x380000, 0x80000, CRC(59fe702a) SHA1(807178dfc6d864e49fd7aabb5c4895835cf0e85b) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg2",         0x0000, 0x0117, CRC(4386879a) SHA1(c36896d169d8c78393609acbbe4397931292a033) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "mb63b.1a",     0x0000, 0x0117, CRC(b8392f02) SHA1(8dedf9f43b30991694f8009302ca628cb50a0b1a) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )

	ROM_REGION( 0x0200, "dboardplds", 0 )
	ROM_LOAD( "d7l1.7l",      0x0000, 0x0117, CRC(27b7410d) SHA1(06d0cba0226850f100ff1f539bd7d5db0f90c730) )
	ROM_LOAD( "d8l1.8l",      0x0000, 0x0117, CRC(539fc7da) SHA1(cad5c91629c6247e49ccbbcbfe6b08229eafae07) )
	ROM_LOAD( "d9k2.9k",      0x0000, 0x0117, CRC(cd85a156) SHA1(a88f8939c5d93e65d7bcc0eb3ee5b6f4f1114e3a) )
	ROM_LOAD( "d10f1.10f",    0x0000, 0x0117, CRC(6619c494) SHA1(3aef656c07182a2186f810f30e0d854dd5bd8d18) )
ROM_END

/* FIXME B-Board uncertain but should be 91635B from the program ROM names */
ROM_START( mbombrd )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "mbde_26.11e",     0x000000, 0x20000, CRC(72b7451c) SHA1(380ef57bb00f1c92d2f29e551b0a670eb5a56cb7) )
	ROM_LOAD16_BYTE( "mbde_30.11f",     0x000001, 0x20000, CRC(a036dc16) SHA1(a68cf74976f482dbc581734e143669511a9a4bee) )
	ROM_LOAD16_BYTE( "mbde_27.12e",     0x040000, 0x20000, CRC(4086f534) SHA1(a2b949f00035b06cb1cd01185902daca3d89d0e3) )
	ROM_LOAD16_BYTE( "mbde_31.12f",     0x040001, 0x20000, CRC(085f47f0) SHA1(ac93a196faf17b7dbe7179ce1e850d9cd7293a21) )
	ROM_LOAD16_BYTE( "mbde_24.9e",      0x080000, 0x20000, CRC(c20895a5) SHA1(35116f7ef8576753ec989647ca2f6a6131d6909f) )
	ROM_LOAD16_BYTE( "mbde_28.9f",      0x080001, 0x20000, CRC(2618d5e1) SHA1(50797c6dda04df95267ff9ef08933c17c3ce7057) )
	ROM_LOAD16_BYTE( "mbde_25.10e",     0x0c0000, 0x20000, CRC(9bdb6b11) SHA1(fbfbd6b5a72ca3237713ce43a798660f899b707d) )
	ROM_LOAD16_BYTE( "mbde_29.10f",     0x0c0001, 0x20000, CRC(3f52d5e5) SHA1(0b1ed8e876a6ec2cfb83676afe43a81e8a033e52) )
	ROM_LOAD16_WORD_SWAP( "mbde_21.6f", 0x100000, 0x80000, CRC(690c026a) SHA1(80ad780743b50750b6bfe1d4e28efe98e562233e) )
	ROM_LOAD16_WORD_SWAP( "mbde_20.5f", 0x180000, 0x80000, CRC(b8b2139b) SHA1(88c9169a9979b711ab7afb8272df0a1c80bb357b) )

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "mb-1m.3a",  0x000000, 0x80000, CRC(41468e06) SHA1(fb365798f2889a20eebaea2393c9c2c8827003c4) )
	ROM_LOAD64_WORD( "mb-3m.5a",  0x000002, 0x80000, CRC(f453aa9e) SHA1(24a103dc6f0dc96f8d0f6164ad732909c9cd2d6a) )
	ROM_LOAD64_WORD( "mb-2m.4a",  0x000004, 0x80000, CRC(2ffbfea8) SHA1(13e30133664a009686e1114c92b558bdbb91ea32) )
	ROM_LOAD64_WORD( "mb-4m.6a",  0x000006, 0x80000, CRC(1eb9841d) SHA1(685da3e011a96b36be9f639a241b2f8f27da4629) )
	ROM_LOAD64_WORD( "mb-5m.7a",  0x200000, 0x80000, CRC(506b9dc9) SHA1(933bf2fb9bcc1a408f961f0e7052da80144bddad) )
	ROM_LOAD64_WORD( "mb-7m.9a",  0x200002, 0x80000, CRC(aff8c2fb) SHA1(ce37a6d5b1eb58c2d74f23f84ec824c214c93217) )
	ROM_LOAD64_WORD( "mb-6m.8a",  0x200004, 0x80000, CRC(b76c70e9) SHA1(c21e255815ec9a985919dbd760ed266c28bd47cd) )
	ROM_LOAD64_WORD( "mb-8m.10a", 0x200006, 0x80000, CRC(e60c9556) SHA1(b91c14092aa8dbb0922d96998123ef1970a658f6) )
	ROM_LOAD64_WORD( "mb-10m.3c", 0x400000, 0x80000, CRC(97976ff5) SHA1(ec9d3460816ab971a02fbce42960283091777e47) )
	ROM_LOAD64_WORD( "mb-12m.5c", 0x400002, 0x80000, CRC(b350a840) SHA1(2b8b996cd08051e7e8e134bff5448775d78058a0) )
	ROM_LOAD64_WORD( "mb-11m.4c", 0x400004, 0x80000, CRC(8fb94743) SHA1(294f6182c8a41b640d1f57cb5e3a2abce3b06482) )
	ROM_LOAD64_WORD( "mb-13m.6c", 0x400006, 0x80000, CRC(da810d5f) SHA1(392bbd405244b8c99024c9228cfec6a7ef0accdb) )

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* QSound Z80 code */
	ROM_LOAD( "mb_q.5k",    0x00000, 0x08000, CRC(d6fa76d1) SHA1(3bfcb703e0e458ef1bb843230f8537167f1d4c3c) )
	ROM_CONTINUE(           0x10000, 0x18000 )

	ROM_REGION( 0x400000, "qsound", 0 ) /* QSound samples */
	ROM_LOAD( "mb-q1.1k",   0x000000, 0x80000, CRC(0630c3ce) SHA1(520fc74c5c3638f611fa2f1b5efb08b91747e29b) )
	ROM_LOAD( "mb-q2.2k",   0x080000, 0x80000, CRC(354f9c21) SHA1(1dc6b39791fd0f760697f409a6b62361a7bf62e9) )
	ROM_LOAD( "mb-q3.3k",   0x100000, 0x80000, CRC(7838487c) SHA1(056b7da05cfca46873edacd674ca25c70855c6db) )
	ROM_LOAD( "mb-q4.4k",   0x180000, 0x80000, CRC(ab66e087) SHA1(066ea69a0157e8647eea3c44d0a1843898860678) )
	ROM_LOAD( "mb-q5.1m",   0x200000, 0x80000, CRC(c789fef2) SHA1(10d1e3d92288fccd4e064a3716a788a165efc3c9) )
	ROM_LOAD( "mb-q6.2m",   0x280000, 0x80000, CRC(ecb81b61) SHA1(e339f21ae47de4782f3b338befcdac659c3503f6) )
	ROM_LOAD( "mb-q7.3m",   0x300000, 0x80000, CRC(041e49ba) SHA1(3220b033a5c0cfbbe75c0c113cf2db39fb093a7e) )
	ROM_LOAD( "mb-q8.4m",   0x380000, 0x80000, CRC(59fe702a) SHA1(807178dfc6d864e49fd7aabb5c4895835cf0e85b) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg2",         0x0000, 0x0117, CRC(4386879a) SHA1(c36896d169d8c78393609acbbe4397931292a033) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "mb63b.1a",     0x0000, 0x0117, CRC(b8392f02) SHA1(8dedf9f43b30991694f8009302ca628cb50a0b1a) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )

	ROM_REGION( 0x0200, "dboardplds", 0 )
	ROM_LOAD( "d7l1.7l",      0x0000, 0x0117, CRC(27b7410d) SHA1(06d0cba0226850f100ff1f539bd7d5db0f90c730) )
	ROM_LOAD( "d8l1.8l",      0x0000, 0x0117, CRC(539fc7da) SHA1(cad5c91629c6247e49ccbbcbfe6b08229eafae07) )
	ROM_LOAD( "d9k2.9k",      0x0000, 0x0117, CRC(cd85a156) SHA1(a88f8939c5d93e65d7bcc0eb3ee5b6f4f1114e3a) )    // pal verification required
	ROM_LOAD( "d10f1.10f",    0x0000, 0x0117, CRC(6619c494) SHA1(3aef656c07182a2186f810f30e0d854dd5bd8d18) )
ROM_END

/* B-Board 91634B-2 */
ROM_START( mbombrdj )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "mbdj_26.11e",     0x000000, 0x20000, CRC(72b7451c) SHA1(380ef57bb00f1c92d2f29e551b0a670eb5a56cb7) )   // == mbde_26.11e
	ROM_LOAD16_BYTE( "mbdj_30.11f",     0x000001, 0x20000, CRC(beff31cf) SHA1(8a3a1fa848fe8fad239d21aef1871e54bbcb582f) )
	ROM_LOAD16_BYTE( "mbdj_27.12e",     0x040000, 0x20000, CRC(4086f534) SHA1(a2b949f00035b06cb1cd01185902daca3d89d0e3) )   // == mbde_27.12e
	ROM_LOAD16_BYTE( "mbdj_31.12f",     0x040001, 0x20000, CRC(085f47f0) SHA1(ac93a196faf17b7dbe7179ce1e850d9cd7293a21) )   // == mbde_31.12f
	ROM_LOAD16_BYTE( "mbdj_24.9e",      0x080000, 0x20000, CRC(c20895a5) SHA1(35116f7ef8576753ec989647ca2f6a6131d6909f) )   // == mbde_24.9e
	ROM_LOAD16_BYTE( "mbdj_28.9f",      0x080001, 0x20000, CRC(2618d5e1) SHA1(50797c6dda04df95267ff9ef08933c17c3ce7057) )   // == mbde_28.9f
	ROM_LOAD16_BYTE( "mbdj_25.10e",     0x0c0000, 0x20000, CRC(9bdb6b11) SHA1(fbfbd6b5a72ca3237713ce43a798660f899b707d) )   // == mbde_25.10e
	ROM_LOAD16_BYTE( "mbdj_29.10f",     0x0c0001, 0x20000, CRC(3f52d5e5) SHA1(0b1ed8e876a6ec2cfb83676afe43a81e8a033e52) )   // == mbde_29.10f
	ROM_LOAD16_WORD_SWAP( "mbdj_21.6f", 0x100000, 0x80000, CRC(690c026a) SHA1(80ad780743b50750b6bfe1d4e28efe98e562233e) )   // == mbde_21.6f
	ROM_LOAD16_WORD_SWAP( "mbdj_20.5f", 0x180000, 0x80000, CRC(b8b2139b) SHA1(88c9169a9979b711ab7afb8272df0a1c80bb357b) )   // == mbde_20.5f

	ROM_REGION( 0x600000, "gfx", 0 )
	ROM_LOAD64_WORD( "mb_01.3a",  0x000000, 0x80000, CRC(a53b1c81) SHA1(d1efb88eeaf6e30e51aaf1432078003e52454dd9) )
	ROM_LOAD64_WORD( "mb_02.4a",  0x000002, 0x80000, CRC(23fe10f6) SHA1(deefa7cac4394b0642f7fb444f9374dbe0bc8843) )
	ROM_LOAD64_WORD( "mb_03.5a",  0x000004, 0x80000, CRC(cb866c2f) SHA1(b087f52e3b2a514a209612319d1d7c4f1c12b8bd) )
	ROM_LOAD64_WORD( "mb_04.6a",  0x000006, 0x80000, CRC(c9143e75) SHA1(e30090625ef6ac971a4f65d53f5458cebb5f146c) )
	ROM_LOAD64_WORD( "mb_05.7a",  0x200000, 0x80000, CRC(506b9dc9) SHA1(933bf2fb9bcc1a408f961f0e7052da80144bddad) ) // == mb-5m.7a
	ROM_LOAD64_WORD( "mb_06.8a",  0x200002, 0x80000, CRC(aff8c2fb) SHA1(ce37a6d5b1eb58c2d74f23f84ec824c214c93217) ) // == mb-7m.9a
	ROM_LOAD64_WORD( "mb_07.9a",  0x200004, 0x80000, CRC(b76c70e9) SHA1(c21e255815ec9a985919dbd760ed266c28bd47cd) ) // == mb-6m.8a
	ROM_LOAD64_WORD( "mb_08.10a", 0x200006, 0x80000, CRC(e60c9556) SHA1(b91c14092aa8dbb0922d96998123ef1970a658f6) ) // == mb-8m.10a
	ROM_LOAD64_WORD( "mb_10.3c",  0x400000, 0x80000, CRC(97976ff5) SHA1(ec9d3460816ab971a02fbce42960283091777e47) ) // == mb-10m.3c
	ROM_LOAD64_WORD( "mb_11.4c",  0x400002, 0x80000, CRC(b350a840) SHA1(2b8b996cd08051e7e8e134bff5448775d78058a0) ) // == mb-12m.5c
	ROM_LOAD64_WORD( "mb_12.5c",  0x400004, 0x80000, CRC(8fb94743) SHA1(294f6182c8a41b640d1f57cb5e3a2abce3b06482) ) // == mb-11m.4c
	ROM_LOAD64_WORD( "mb_13.6c",  0x400006, 0x80000, CRC(da810d5f) SHA1(392bbd405244b8c99024c9228cfec6a7ef0accdb) ) // == mb-13m.6c

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* QSound Z80 code */
	ROM_LOAD( "mb_qa.5k",   0x00000, 0x08000, CRC(e21a03c4) SHA1(98c03fd2c9b6bf8a4fc25a4edca87fff7c3c3819) )
	ROM_CONTINUE(           0x10000, 0x18000 )

	ROM_REGION( 0x400000, "qsound", 0 ) /* QSound samples */
	ROM_LOAD( "mb-q1.1k",   0x000000, 0x80000, CRC(0630c3ce) SHA1(520fc74c5c3638f611fa2f1b5efb08b91747e29b) )
	ROM_LOAD( "mb-q2.2k",   0x080000, 0x80000, CRC(354f9c21) SHA1(1dc6b39791fd0f760697f409a6b62361a7bf62e9) )
	ROM_LOAD( "mb-q3.3k",   0x100000, 0x80000, CRC(7838487c) SHA1(056b7da05cfca46873edacd674ca25c70855c6db) )
	ROM_LOAD( "mb-q4.4k",   0x180000, 0x80000, CRC(ab66e087) SHA1(066ea69a0157e8647eea3c44d0a1843898860678) )
	ROM_LOAD( "mb-q5.1m",   0x200000, 0x80000, CRC(c789fef2) SHA1(10d1e3d92288fccd4e064a3716a788a165efc3c9) )
	ROM_LOAD( "mb-q6.2m",   0x280000, 0x80000, CRC(ecb81b61) SHA1(e339f21ae47de4782f3b338befcdac659c3503f6) )
	ROM_LOAD( "mb-q7.3m",   0x300000, 0x80000, CRC(041e49ba) SHA1(3220b033a5c0cfbbe75c0c113cf2db39fb093a7e) )
	ROM_LOAD( "mb-q8.4m",   0x380000, 0x80000, CRC(59fe702a) SHA1(807178dfc6d864e49fd7aabb5c4895835cf0e85b) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg2",         0x0000, 0x0117, CRC(4386879a) SHA1(c36896d169d8c78393609acbbe4397931292a033) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "mb63b.1a",     0x0000, 0x0117, CRC(b8392f02) SHA1(8dedf9f43b30991694f8009302ca628cb50a0b1a) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic1",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )

	ROM_REGION( 0x0200, "dboardplds", 0 )
	ROM_LOAD( "d7l1.7l",      0x0000, 0x0117, CRC(27b7410d) SHA1(06d0cba0226850f100ff1f539bd7d5db0f90c730) )
	ROM_LOAD( "d8l1.8l",      0x0000, 0x0117, CRC(539fc7da) SHA1(cad5c91629c6247e49ccbbcbfe6b08229eafae07) )
	ROM_LOAD( "d9k2.9k",      0x0000, 0x0117, CRC(cd85a156) SHA1(a88f8939c5d93e65d7bcc0eb3ee5b6f4f1114e3a) )
	ROM_LOAD( "d10f1.10f",    0x0000, 0x0117, CRC(6619c494) SHA1(3aef656c07182a2186f810f30e0d854dd5bd8d18) )
ROM_END

/* B-Board 89625B-1 */
ROM_START( pnickj )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "pnij_36.12f", 0x00000, 0x20000, CRC(2d4ffb2b) SHA1(6e49cf89a36834fd1de8b4b860fe66f3d7d67a84) )
	ROM_LOAD16_BYTE( "pnij_42.12h", 0x00001, 0x20000, CRC(c085dfaf) SHA1(a31ededc3413ec4f3f5e3a1fb615b60c6197f4a5) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_BYTE( "pnij_09.4b",  0x000000, 0x20000, CRC(48177b0a) SHA1(eba5de6cd9bb0c4ad76a13bddc9cdeb2e4380122) )
	ROM_LOAD64_BYTE( "pnij_01.4a",  0x000001, 0x20000, CRC(01a0f311) SHA1(9bcd8716f90ccd410543ffcdc5c2916077b8d4c3) )
	ROM_LOAD64_BYTE( "pnij_13.9b",  0x000002, 0x20000, CRC(406451b0) SHA1(5a7a7fecba7de8b8cf4a284b2ae7adae901623f6) )
	ROM_LOAD64_BYTE( "pnij_05.9a",  0x000003, 0x20000, CRC(8c515dc0) SHA1(aa1e13cf9e7cf0458bb5c4332b1ea73034f9a874) )
	ROM_LOAD64_BYTE( "pnij_26.5e",  0x000004, 0x20000, CRC(e2af981e) SHA1(3c2b28b4a4d457aa94a760dfca0181a9f050c319) )
	ROM_LOAD64_BYTE( "pnij_18.5c",  0x000005, 0x20000, CRC(f17a0e56) SHA1(7c89aca230f176e12f995892f9d1bce22c57fbdf) )
	ROM_LOAD64_BYTE( "pnij_38.8h",  0x000006, 0x20000, CRC(eb75bd8c) SHA1(2129460e06eb64019fc5f7eab6334ff43229b995) )
	ROM_LOAD64_BYTE( "pnij_32.8f",  0x000007, 0x20000, CRC(84560bef) SHA1(9e94ae434b50ecf82781080e11d0c4741e992d0d) )
	ROM_LOAD64_BYTE( "pnij_10.5b",  0x100000, 0x20000, CRC(c2acc171) SHA1(7c86db3f2acca1252d403c5f12c871d0357fa109) )
	ROM_LOAD64_BYTE( "pnij_02.5a",  0x100001, 0x20000, CRC(0e21fc33) SHA1(c4a29d45c4257c8871038d3c9b13140e874db0c1) )
	ROM_LOAD64_BYTE( "pnij_14.10b", 0x100002, 0x20000, CRC(7fe59b19) SHA1(a273b8b8fbfd5d31d25479a9ede09ce35e1cc873) )
	ROM_LOAD64_BYTE( "pnij_06.10a", 0x100003, 0x20000, CRC(79f4bfe3) SHA1(bc17cc1c8535e3d202588893713926b6c06f92fd) )
	ROM_LOAD64_BYTE( "pnij_27.7e",  0x100004, 0x20000, CRC(83d5cb0e) SHA1(44c93fa5eedcafc8dc6d88ee827c6cadc9c671f0) )
	ROM_LOAD64_BYTE( "pnij_19.7c",  0x100005, 0x20000, CRC(af08b230) SHA1(a3b5b3013012efa1860699648518f8d8031c5f30) )
	ROM_LOAD64_BYTE( "pnij_39.9h",  0x100006, 0x20000, CRC(70fbe579) SHA1(b5b7ed5588ecd884b20dd50bfc5385a9af03c5d8) )
	ROM_LOAD64_BYTE( "pnij_33.9f",  0x100007, 0x20000, CRC(3ed2c680) SHA1(0afe84d8d89f8d45afc79f6172337e622e29a8a2) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "pnij_17.13b",  0x00000, 0x08000, CRC(e86f787a) SHA1(de04cbe89c655faf04afe169bfd9913049ccc4a8) )
	ROM_CONTINUE(             0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "pnij_24.12c",  0x00000, 0x20000, CRC(5092257d) SHA1(95dc9d10940653b2fb37baf5c1ed27145b02104e) )
	ROM_LOAD( "pnij_25.13c",  0x20000, 0x20000, CRC(22109aaa) SHA1(cf21e75674d81b2daae2083d02f9f4b6e52722c6) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "pkb10b.1a",    0x0000, 0x0117, NO_DUMP )
	ROM_LOAD( "iob1.12e",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 90629B-3 */
ROM_START( qtono2j )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "tn2j_30.11e", 0x00000, 0x20000, CRC(9226eb5e) SHA1(91649974f9652caed90eb28ec6caf4fe61f5d279) )
	ROM_LOAD16_BYTE( "tn2j_37.11f", 0x00001, 0x20000, CRC(d1d30da1) SHA1(7ca1695ed804b5860d4c15964cdbb922db3918ee) )
	ROM_LOAD16_BYTE( "tn2j_31.12e", 0x40000, 0x20000, CRC(015e6a8a) SHA1(0835bec4867438a167bd01e3550090c88e7ae779) )
	ROM_LOAD16_BYTE( "tn2j_38.12f", 0x40001, 0x20000, CRC(1f139bcc) SHA1(ee907f1bfef1a887e2c768648fe811e0733eddf7) )
	ROM_LOAD16_BYTE( "tn2j_28.9e",  0x80000, 0x20000, CRC(86d27f71) SHA1(89d6d18e05deaaa1ac7deb70ca03d051d2fde472) )
	ROM_LOAD16_BYTE( "tn2j_35.9f",  0x80001, 0x20000, CRC(7a1ab87d) SHA1(f1729a8c0c82cf42f60644a7796dc8a39bf7c6fa) )
	ROM_LOAD16_BYTE( "tn2j_29.10e", 0xc0000, 0x20000, CRC(9c384e99) SHA1(3d3961f625ccc4776531eff50fc1b4bee062370e) )
	ROM_LOAD16_BYTE( "tn2j_36.10f", 0xc0001, 0x20000, CRC(4c4b2a0a) SHA1(9a25fcfb9358ea42d9bc662df2cafea08febb411) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "tn2-02m.4a", 0x000000, 0x80000, CRC(f2016a34) SHA1(3862960fa14742547f6a6deacf0b9f409d08fee8) )
	ROM_LOAD64_WORD( "tn2-04m.6a", 0x000002, 0x80000, CRC(094e0fb1) SHA1(7c9a9a7d03e226109002dd389c872e3d4be43287) )
	ROM_LOAD64_WORD( "tn2-01m.3a", 0x000004, 0x80000, CRC(cb950cf9) SHA1(8337a500141c1aec82b6636ad79ecafbdbebd691) )
	ROM_LOAD64_WORD( "tn2-03m.5a", 0x000006, 0x80000, CRC(18a5bf59) SHA1(afbfcb28c40551747bb5276aac2b9c15a24328e1) )
	ROM_LOAD64_WORD( "tn2-11m.4c", 0x200000, 0x80000, CRC(d0edd30b) SHA1(a76d7f134f9e52f79a485402d17dcc7a1fe99f29) )
	ROM_LOAD64_WORD( "tn2-13m.6c", 0x200002, 0x80000, CRC(426621c3) SHA1(89156bc9d585f546cd619db419dd1f4d9871d930) )
	ROM_LOAD64_WORD( "tn2-10m.3c", 0x200004, 0x80000, CRC(a34ece70) SHA1(15864d6b280f624245add8a611f1699da570392b) )
	ROM_LOAD64_WORD( "tn2-12m.5c", 0x200006, 0x80000, CRC(e04ff2f4) SHA1(774c19909a2ae2c691f5d3f15b6e19cc94baf799) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "tn2j_09.12a", 0x00000, 0x08000, CRC(e464b969) SHA1(18a0b9fd3a1eb4d1364b0e8601cd49a9574406c8) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "tn2j_18.11c", 0x00000, 0x20000, CRC(a40bf9a7) SHA1(07cb1076262a281e31a621cbcc10be0cae883175) )
	ROM_LOAD( "tn2j_19.12c", 0x20000, 0x20000, CRC(5b3b931e) SHA1(cf28891f84814cbfaa3adaade8bb08b1e0546a3d) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "tn2292.1a",    0x0000, 0x0117, CRC(3d899539) SHA1(9ffa76229408a2d7438b1b3efa0ec7166bfcc751) )
	ROM_LOAD( "iob1.11d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 94916-10 */
ROM_START( pang3 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "pa3e_17a.11l", 0x00000, 0x80000, CRC(a213fa80) SHA1(fee6b0b21e2ef573ebfb35eaa1785533101c9170) )
	ROM_LOAD16_WORD_SWAP( "pa3e_16a.10l", 0x80000, 0x80000, CRC(7169ea67) SHA1(1076afaff7b05c9009798a0cea13e71fa27f2b7e) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "pa3-01m.2c", 0x000000, 0x100000, CRC(068a152c) SHA1(fa491874068924c39bcc7de93dfda3b27f5d9613) )
	ROM_CONTINUE(                  0x000004, 0x100000 )
	ROM_LOAD64_WORD( "pa3-07m.2f", 0x000002, 0x100000, CRC(3a4a619d) SHA1(cfe68e24632b53fb6cd6d03b2166d6b5ba28b778) )
	ROM_CONTINUE(                  0x000006, 0x100000 )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "pa3_11.11f",  0x00000, 0x08000, CRC(cb1423a2) SHA1(3191bf5d340168647881738cb2aed09b1d86146e) )
	ROM_IGNORE( 0x18000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "pa3_05.10d",  0x00000, 0x20000, CRC(73a10d5d) SHA1(999465e4fbc35a34746d2db61ad49f61403d5af7) )
	ROM_LOAD( "pa3_06.11d",  0x20000, 0x20000, CRC(affa4f82) SHA1(27b9292bbc121cf585f53297a79fe8f0d0a729ae) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "cp1b1f.1f",    0x0000, 0x0117, CRC(3979b8e3) SHA1(07c9819d68b4d93bc37b96bd15d689ce54fe034e) )
	ROM_LOAD( "cp1b8k.8k",    0x0000, 0x0117, CRC(8a52ea7a) SHA1(47a59abc54a83292cfd6faa2d293c8f948c7ea03) )
	ROM_LOAD( "cp1b9ka.9k",   0x0000, 0x0117, CRC(238d3ff4) SHA1(597f429d6a0ea485746322592604188c1ec87595) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 94916-10 */
ROM_START( pang3r1 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "pa3e_17.11l", 0x00000, 0x80000, CRC(d7041d32) SHA1(b021f3defe7fc58030ba907125c713f987724187) )
	ROM_LOAD16_WORD_SWAP( "pa3e_16.10l", 0x80000, 0x80000, CRC(1be9a483) SHA1(6cff1dd15ca163237bc82fb4a3e1d469d35e7be8) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "pa3-01m.2c", 0x000000, 0x100000, CRC(068a152c) SHA1(fa491874068924c39bcc7de93dfda3b27f5d9613) )
	ROM_CONTINUE(                  0x000004, 0x100000 )
	ROM_LOAD64_WORD( "pa3-07m.2f", 0x000002, 0x100000, CRC(3a4a619d) SHA1(cfe68e24632b53fb6cd6d03b2166d6b5ba28b778) )
	ROM_CONTINUE(                  0x000006, 0x100000 )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "pa3_11.11f",  0x00000, 0x08000, CRC(cb1423a2) SHA1(3191bf5d340168647881738cb2aed09b1d86146e) )
	ROM_IGNORE( 0x18000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "pa3_05.10d",  0x00000, 0x20000, CRC(73a10d5d) SHA1(999465e4fbc35a34746d2db61ad49f61403d5af7) )
	ROM_LOAD( "pa3_06.11d",  0x20000, 0x20000, CRC(affa4f82) SHA1(27b9292bbc121cf585f53297a79fe8f0d0a729ae) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "cp1b1f.1f",    0x0000, 0x0117, CRC(3979b8e3) SHA1(07c9819d68b4d93bc37b96bd15d689ce54fe034e) )
	ROM_LOAD( "cp1b8k.8k",    0x0000, 0x0117, CRC(8a52ea7a) SHA1(47a59abc54a83292cfd6faa2d293c8f948c7ea03) )
	ROM_LOAD( "cp1b9k.9k",    0x0000, 0x0117, CRC(a754bdc3) SHA1(9267b24cbddee4858b219468cc92f9df8f5fd0ef) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 94916-10 */
ROM_START( pang3j )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "pa3j_17.11l", 0x00000, 0x80000, CRC(21f6e51f) SHA1(b447e05261f59b3b2e89bbc0f606d7136b29cb56) )
	ROM_LOAD16_WORD_SWAP( "pa3j_16.10l", 0x80000, 0x80000, CRC(ca1d7897) SHA1(46aa9232e81a838f3eff1e9b992492a264914fd5) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "pa3-01m.2c", 0x000000, 0x100000, CRC(068a152c) SHA1(fa491874068924c39bcc7de93dfda3b27f5d9613) )
	ROM_CONTINUE(                  0x000004, 0x100000 )
	ROM_LOAD64_WORD( "pa3-07m.2f", 0x000002, 0x100000, CRC(3a4a619d) SHA1(cfe68e24632b53fb6cd6d03b2166d6b5ba28b778) )
	ROM_CONTINUE(                  0x000006, 0x100000 )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "pa3_11.11f",  0x00000, 0x08000, CRC(cb1423a2) SHA1(3191bf5d340168647881738cb2aed09b1d86146e) )
	ROM_IGNORE( 0x18000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "pa3_05.10d",  0x00000, 0x20000, CRC(73a10d5d) SHA1(999465e4fbc35a34746d2db61ad49f61403d5af7) )
	ROM_LOAD( "pa3_06.11d",  0x20000, 0x20000, CRC(affa4f82) SHA1(27b9292bbc121cf585f53297a79fe8f0d0a729ae) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "cp1b1f.1f",    0x0000, 0x0117, CRC(3979b8e3) SHA1(07c9819d68b4d93bc37b96bd15d689ce54fe034e) )
	ROM_LOAD( "cp1b8k.8k",    0x0000, 0x0117, CRC(8a52ea7a) SHA1(47a59abc54a83292cfd6faa2d293c8f948c7ea03) )
	ROM_LOAD( "cp1b9k.9k",    0x0000, 0x0117, CRC(a754bdc3) SHA1(9267b24cbddee4858b219468cc92f9df8f5fd0ef) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 94916-10 */
/* This set comes from a not encrypted bootleg that uses a very well reproduced Mitchell 94916-10 B-Board surmounted by an
   original Capcom 92631C-6 C-Board. Mitchell mark has been erased, original 32 pin EPROM PA3_11.11F resized and replaced by
   one with 28 pin while the protection chip MACH215 mounted on the B-Board has been replaced by a 22 pin PAL. */
ROM_START( pang3b )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "pa3w_17.11l", 0x00000, 0x80000, CRC(12138234) SHA1(956a2c847a3cfb94007d1a636167fd2bb9f826ec) )
	ROM_LOAD16_WORD_SWAP( "pa3w_16.10l", 0x80000, 0x80000, CRC(d1ba585c) SHA1(c6d04441fe97abf0a72b23c917777a7b58e94a85) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "pa3-01m.2c", 0x000000, 0x100000, CRC(068a152c) SHA1(fa491874068924c39bcc7de93dfda3b27f5d9613) )
	ROM_CONTINUE(                  0x000004, 0x100000 )
	ROM_LOAD64_WORD( "pa3-07m.2f", 0x000002, 0x100000, CRC(3a4a619d) SHA1(cfe68e24632b53fb6cd6d03b2166d6b5ba28b778) )
	ROM_CONTINUE(                  0x000006, 0x100000 )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "pa3_11.11f",  0x00000, 0x08000, CRC(90a08c46) SHA1(7544adab2d7e052e0d21c920bff7841d9d718345) )  // == pa3_11.11f but different size, resized by bootlegger

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "pa3_05.10d",  0x00000, 0x20000, CRC(73a10d5d) SHA1(999465e4fbc35a34746d2db61ad49f61403d5af7) )
	ROM_LOAD( "pa3_06.11d",  0x20000, 0x20000, CRC(affa4f82) SHA1(27b9292bbc121cf585f53297a79fe8f0d0a729ae) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "cp1b1f.1f",    0x0000, 0x0117, CRC(3979b8e3) SHA1(07c9819d68b4d93bc37b96bd15d689ce54fe034e) )
	ROM_LOAD( "cp1b8k.8k",    0x0000, 0x0117, CRC(8a52ea7a) SHA1(47a59abc54a83292cfd6faa2d293c8f948c7ea03) )
	ROM_LOAD( "cp1b9k.9k",    0x0000, 0x0117, CRC(a754bdc3) SHA1(9267b24cbddee4858b219468cc92f9df8f5fd0ef) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

// This bootleg uses an original B-13 chip top and original main board. ROM content is the same as pang3, but split differently
ROM_START( pang3b2 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )
	ROM_LOAD16_BYTE( "16.u70", 0x00000, 0x40000, CRC(8d3fd82c) SHA1(e4417898053b085afc7feddc251ab7ba7ff4ea5a) )
	ROM_LOAD16_BYTE( "14.u72", 0x00001, 0x40000, CRC(654904c8) SHA1(7a457ee1e52454293a211265b26c834bc0767b6a) )
	ROM_LOAD16_BYTE( "15.u71", 0x80000, 0x40000, CRC(42774e37) SHA1(34c08db803bf07c1b22c3e6629f0070cac3196ed) )
	ROM_LOAD16_BYTE( "13.u73", 0x80001, 0x40000, CRC(531ea745) SHA1(a084ee119023e690b241feca76d691db8095d07b) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_BYTE( "4.u68",  0x000000, 0x80000, CRC(45fc0a81) SHA1(5adf1cd74f192d1ca330f59f7fddcf3ac52f33c2) )
	ROM_LOAD64_BYTE( "2.u66",  0x000001, 0x80000, CRC(27668828) SHA1(1e518163710b80521158ddf9927f02a4d9a51052) )
	ROM_LOAD64_BYTE( "10.u69", 0x000002, 0x80000, CRC(e051b2e9) SHA1(9546a0414902e9592917a46585ad27f5bfbd9fda) )
	ROM_LOAD64_BYTE( "8.u63",  0x000003, 0x80000, CRC(4a68b194) SHA1(f8e67c70bd9059a2f26f7bad7c1dd2573f5b0d28) )
	ROM_LOAD64_BYTE( "3.u67",  0x000004, 0x80000, CRC(91c8d782) SHA1(8240211cf4075a111e0905e985d65689c82964b9) )
	ROM_LOAD64_BYTE( "1.u65",  0x000005, 0x80000, CRC(fe32af5d) SHA1(38df0e7f5ba166d1b795f4baa242a5c55aa73eb8) )
	ROM_LOAD64_BYTE( "9.u62",  0x000006, 0x80000, CRC(f0bba5c7) SHA1(596ffdb46a78a13a03ef5865d39ac70b5f17ca71) )
	ROM_LOAD64_BYTE( "7.u64",  0x000007, 0x80000, CRC(c60c5e75) SHA1(904915ac11ee4553558323aeb57fbb3fa5414f73) )

	ROM_REGION( 0x18000, "audiocpu", 0 )
	ROM_LOAD( "11.u32",  0x00000, 0x08000, CRC(cb1423a2) SHA1(3191bf5d340168647881738cb2aed09b1d86146e) )
	ROM_IGNORE( 0x18000 )

	ROM_REGION( 0x40000, "oki", 0 )
	ROM_LOAD( "5.u34",  0x00000, 0x20000, CRC(73a10d5d) SHA1(999465e4fbc35a34746d2db61ad49f61403d5af7) )
	ROM_LOAD( "6.u33",  0x20000, 0x20000, CRC(affa4f82) SHA1(27b9292bbc121cf585f53297a79fe8f0d0a729ae) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )
ROM_END

/* An interesting single board bootleg which is an almost exact replica of the official Pang3 A/B board circuitry condensed onto a single pcb.
   All component refs match the official 89626A short 10MHz A board and 94916-10 pang3 exclusive B board.
   Uses pin compatible clone CPS-A/B chips which are marked 27C634 and 27C633 respectively, the clone B is a CPS-B-17.
   They appear to be 3.3v chips as the common bootlegger trick of stepping down the supply voltage with power diodes is used.
   Main code has been hacked to use the CPS-B-17 rather than official CPS-B-21 config.
   All PALs confirmed to match official A/B board PALs.
   Uses same encryption and mach215 security pld as official.
*/
ROM_START( pang3b3 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "u11l1_17.bin", 0x00000, 0x80000, CRC(dd3b95c0) SHA1(f5cc414f92e6ee805957ca00512ad112586bc4ff) )
	ROM_LOAD16_WORD_SWAP( "u10l1_16.bin", 0x80000, 0x80000, CRC(1be9a483) SHA1(6cff1dd15ca163237bc82fb4a3e1d469d35e7be8) )  // == pa3e_16.10l

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "pa3-01m.2c", 0x000000, 0x100000, CRC(068a152c) SHA1(fa491874068924c39bcc7de93dfda3b27f5d9613) )
	ROM_CONTINUE(                  0x000004, 0x100000 )
	ROM_LOAD64_WORD( "pa3-07m.2f", 0x000002, 0x100000, CRC(3a4a619d) SHA1(cfe68e24632b53fb6cd6d03b2166d6b5ba28b778) )
	ROM_CONTINUE(                  0x000006, 0x100000 )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "u11f1.bin",  0x00000, 0x08000, CRC(278d786c) SHA1(bf226adf766b7d24a60e20d19d586c9fafe0973d) )  // == pa3_11.11f with empty space cut to fit into a 27c512
	ROM_IGNORE( 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "pa3_05.10d",  0x00000, 0x20000, CRC(73a10d5d) SHA1(999465e4fbc35a34746d2db61ad49f61403d5af7) )
	ROM_LOAD( "pa3_06.11d",  0x20000, 0x20000, CRC(affa4f82) SHA1(27b9292bbc121cf585f53297a79fe8f0d0a729ae) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "cp1b1f.1f",    0x0000, 0x0117, CRC(3979b8e3) SHA1(07c9819d68b4d93bc37b96bd15d689ce54fe034e) )
	ROM_LOAD( "cp1b8k.8k",    0x0000, 0x0117, CRC(8a52ea7a) SHA1(47a59abc54a83292cfd6faa2d293c8f948c7ea03) )
	ROM_LOAD( "cp1b9k.9k",    0x0000, 0x0117, CRC(a754bdc3) SHA1(9267b24cbddee4858b219468cc92f9df8f5fd0ef) )
ROM_END

/* This bootleg uses a bootlegged B board nearly identical to that used in sf2ceblp.
   In my set:
     - Board-A is missing so I don't know if a normal or a dash board was used.
     - Board-C is an hacked 88622-C-5 with an unusual CPS-B-12 and clearly it's not its C-board because
       code analysis led to identify the CPS_B_21_DEF as the right configuration.
     - Board-B has some missing components:
      - audio cpu code EPROM @B13 (pang3b pa3_11.11f used instead)
      - 28 pin DIP component @F8, probably a PIC16C55/7 looking the pinout (VCC on pin2, GND on pin 4, ...)

   The program code is almost the same as of pang3b with some minor, yet interesting, hacks:
      - Dip switch use for board configuration (code@ 0x000300, 0xe0000) instead of a serial EPROM
      - Removed freeze from dip switch (code@ 020B74)
      - Some code to handle the PIC (protection?).
        Read and write to addresses 0x5762b0 and 0x57a2b0 occurs in the code but the return value is never really used (nop or bra skips relevant parts)
        and so seems that the PIC protection is ineffective.
      - Read and write to port 0x80017a (EEPROM in pang3) still are present in the code, but are filtered by the PAL16V8 @ E13 so there is no need to
        create a port for that address here in mame (although this causes a popmessage "CPS-B read port 3A contact MAMEDEV" to occur at startup if compiled with
        DEBUG=1)

   Board-B has five PALs:
      - PALCE16V8  @A2
      - PAL16L8    @E11
      - PALCE16V8  @E13
      - PAL16L8    @J8
      - PALCE22V10 @J8

*/
ROM_START( pang3b4 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 ) /* 68000 code */
	ROM_LOAD16_BYTE( "22.u30", 0x00000, 0x20000, CRC(bf3ebe68) SHA1(10f3eb3f6e747eaed1821c611fa19350c3528362) )
	ROM_LOAD16_BYTE( "26.u35", 0x00001, 0x20000, CRC(d20db83c) SHA1(ac97ad76848bb6069e6d07d1f2feb2c328578228) )
	ROM_LOAD16_BYTE( "23.u31", 0x40000, 0x20000, CRC(94d494c2) SHA1(33a2c1ea041a68655b7c3c44b1c5d58dec3add89) )
	ROM_LOAD16_BYTE( "27.u36", 0x40001, 0x20000, CRC(38e43390) SHA1(ae346c5e21b3aad86b118a0c99a550304916debf) )
	ROM_LOAD16_BYTE( "20.u28", 0x80000, 0x20000, CRC(8daf3814) SHA1(a83a0434b9f281c70dae35362741f52a8b3ea622) )
	ROM_LOAD16_BYTE( "24.u33", 0x80001, 0x20000, CRC(bb34e444) SHA1(2cfb135dbd763f327fe3c5ed2d8d291fd97118c2) )
	ROM_LOAD16_BYTE( "21.u29", 0xC0000, 0x20000, CRC(54d0b680) SHA1(5527673286dc9db82eed357cda1f53ccdd3e2d1f) )
	ROM_LOAD16_BYTE( "25.u34", 0xC0001, 0x20000, CRC(d666ec70) SHA1(3305fa5c6c78bdfc5ef5393f001e710ce3c4d28a) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_BYTE( "14.u25",  0x000000, 0x40000, CRC(d2b764a9) SHA1(bb13821a1a3059c2f98d5c36f830116b87583312) )
	ROM_LOAD64_BYTE( "06.u15",  0x000001, 0x40000, CRC(fb68be4e) SHA1(5d7e34bfa5427a1b05b64e174dd26e2153b56ec0) )
	ROM_LOAD64_BYTE( "13.u24",  0x200000, 0x40000, CRC(0c73a1c7) SHA1(c3d3f9115b24805f038bb4b808beef342c87eff3) )
	ROM_LOAD64_BYTE( "05.u14",  0x200001, 0x40000, CRC(475a5ef1) SHA1(0dcd8d747a84037e27c5b360733a4c3d03cc43fa) )

	ROM_LOAD64_BYTE( "10.u21",  0x000004, 0x40000, CRC(79673708) SHA1(9b4d972d7ef6592763271976349d65519e0d0dfa) )
	ROM_LOAD64_BYTE( "02.u11",  0x000005, 0x40000, CRC(f706b466) SHA1(ecdfe160bdfa9c5d4533c43f52868f4e71ee6c8b) )
	ROM_LOAD64_BYTE( "09.u20",  0x200004, 0x40000, CRC(6e36e963) SHA1(2e46c57516603322ee16e7a7ffa4792c3569f087) )
	ROM_LOAD64_BYTE( "01.u10",  0x200005, 0x40000, CRC(7d15b9d7) SHA1(b6b39bffb2440c4cfedcdda19e6bcb0a85038ee3) )

	ROM_LOAD64_BYTE( "16.u27",  0x000002, 0x40000, CRC(3e293482) SHA1(c5896b92126f05cfcef46a1d6e4a8986c054d1db) )
	ROM_LOAD64_BYTE( "08.u17",  0x000003, 0x40000, CRC(7e0ca927) SHA1(eee7df6f8fc11dda0d403b7d43934e3233cf257e) )
	ROM_LOAD64_BYTE( "15.u26",  0x200002, 0x40000, CRC(a933434f) SHA1(60807e16b87f3e07f59fd14d29a5c54efe54f4af) )
	ROM_LOAD64_BYTE( "07.u16",  0x200003, 0x40000, CRC(83b3fa5e) SHA1(95c79cfe88902a36a60ebd73490b435c57c9d60e) )

	ROM_LOAD64_BYTE( "12.u23",  0x000006, 0x40000, CRC(3c4dfb4f) SHA1(638078080d49edb8c711dfb10ee6029c58e82801) )
	ROM_LOAD64_BYTE( "04.u13",  0x000007, 0x40000, CRC(44cd5e95) SHA1(53ba6dd7ab2313b5a0ee1210a1ec24e57d1d5d86) )
	ROM_LOAD64_BYTE( "11.u22",  0x200006, 0x40000, CRC(29194b90) SHA1(479bbfbc26cd18440fdf9b71f85e3776add61cc4) )
	ROM_LOAD64_BYTE( "03.u12",  0x200007, 0x40000, CRC(7e28974e) SHA1(d1eb177fe37784c6bcd5cb7df3b06b55c4d069b8) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "pa3_11.11f",  0x00000, 0x08000, BAD_DUMP CRC(cb1423a2) SHA1(3191bf5d340168647881738cb2aed09b1d86146e) ) // 19.u9 missing, used pang3b audio code instead
	ROM_IGNORE( 0x18000 )

	ROM_REGION( 0x40000, "oki", 0 )  /* Samples */
	ROM_LOAD( "pa3_05.10d",  0x00000, 0x20000, CRC(73a10d5d) SHA1(999465e4fbc35a34746d2db61ad49f61403d5af7) ) // 18.u18 has the same content of pa3_05.10d from pang3 romset
	ROM_LOAD( "pa3_06.11d",  0x20000, 0x20000, CRC(affa4f82) SHA1(27b9292bbc121cf585f53297a79fe8f0d0a729ae) ) // 17.u19 has the same content of pa3_06.11d from pang3 romset

	ROM_REGION( 0x0b00, "bboardplds", 0 )
	ROM_LOAD( "pal16l8.11e", 0x0000, 0x0117, CRC(27617943) SHA1(eb34cfba18fcc2b67ee214c681ea86bfe1bb75e0) ) // Bruteforced
	ROM_LOAD( "pal16v8.1a",  0x0200, 0x0117, CRC(78c3161f) SHA1(7ae85dfca59387f4874b0ef1218bfd14c393fb85) ) // Bruteforced
	ROM_LOAD( "pal16v8.13e", 0x0400, 0x0117, CRC(5406caf1) SHA1(6bebafd4a4bad9ed766abce2fef9c30cbf61772e) ) // Bruteforced
	ROM_LOAD( "pal22v10.j8", 0x0600, 0x02dd, CRC(a9445f88) SHA1(caacee5f3d5502cd51060c0769914cad4317838c) ) // Bruteforced
	ROM_LOAD( "pal16l8.j8",  0x0900, 0x0117, NO_DUMP ) // Bad PAL, all outputs are fixed high
ROM_END

/* B-Board Mitchell 94916-10 */
/* This set comes from an encrypted bootleg that uses a very well reproduced Mitchell 94916-10 B-Board surmounted by an
   original Capcom 90631C-5 C-Board taken from a Knights of the round board (there's a sticker on it).
   Protection chip MACH215 is present. */
ROM_START( pang3b5 )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "pa3e_17.11l", 0x00000, 0x80000, CRC(d7041d32) SHA1(b021f3defe7fc58030ba907125c713f987724187) ) // 17.11l has the same content of pa3e_17.11l from pang3r1 romset
	ROM_LOAD16_WORD_SWAP( "pa3e_16.10l", 0x80000, 0x80000, CRC(1be9a483) SHA1(6cff1dd15ca163237bc82fb4a3e1d469d35e7be8) ) // 16.10l has the same content of pa3e_16.10l from pang3r1 romset

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "1.2c",  0x000000, 0x80000, CRC(22c934c4) SHA1(93a3c3cfdbfd1321b785f08d1da30d9b1445e14b) )
	ROM_LOAD64_WORD( "7.2f",  0x000002, 0x80000, CRC(51031180) SHA1(a38a77da2ca452843a3b0cb02baaaf7df2e3d9a4) )
	ROM_LOAD64_WORD( "3.4c",  0x000004, 0x80000, CRC(ac119a46) SHA1(e8a07349b6106f712a7543ab52e9d5ced756fdbd) )
	ROM_LOAD64_WORD( "9.4f",  0x000006, 0x80000, CRC(2e1d35f2) SHA1(1b4a9cf9ed91fed532d44582c598982dae06253c) )
	ROM_LOAD64_WORD( "2.3c",  0x200000, 0x80000, CRC(07c85e9b) SHA1(5b9bc1f5708d03c2458a1dbb781084c2af8f91ee) )
	ROM_LOAD64_WORD( "8.3f",  0x200002, 0x80000, CRC(325cc0b7) SHA1(ef464abfbadb680eac51daab4adf0bb239785679) )
	ROM_LOAD64_WORD( "4.5c",  0x200004, 0x80000, CRC(4ad13297) SHA1(59565f9819e1cdd405103d5e3621c747116f653c) )
	ROM_LOAD64_WORD( "10.5f", 0x200006, 0x80000, CRC(026d0cd2) SHA1(44f7718851e0b1f43682afafcbd844e6b1ce3430) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "pa3_11.11f",  0x00000, 0x08000, CRC(cb1423a2) SHA1(3191bf5d340168647881738cb2aed09b1d86146e) )  // == 11.11f has the same content of pa3w_16.10l from pang3 romset
	ROM_IGNORE( 0x18000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "pa3_05.10d",  0x00000, 0x20000, CRC(73a10d5d) SHA1(999465e4fbc35a34746d2db61ad49f61403d5af7) ) // 5.10c has the same content of pa3_05.10d from pang3 romset
	ROM_LOAD( "pa3_06.11d",  0x20000, 0x20000, CRC(affa4f82) SHA1(27b9292bbc121cf585f53297a79fe8f0d0a729ae) ) // 6.11c has the same content of pa3_06.11d from pang3 romset

	ROM_REGION( 0x0a00, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0200, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0400, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0600, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0800, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0600, "bboardplds", 0 )
	ROM_LOAD( "cp1b1f_boot.1f", 0x0000, 0x0117, CRC(658849dc) SHA1(4fc386fa33322ce52334dee1391d617e0754bde0) )
	ROM_LOAD( "cp1b8k.8k",      0x0200, 0x0117, CRC(8a52ea7a) SHA1(47a59abc54a83292cfd6faa2d293c8f948c7ea03) ) // I was not able to dump the original PAL. Tried to swap it with a legit CP1B8K on real hw
																											   // and it's working, so I suppose they're functionally equivalent
	ROM_LOAD( "cp1b9ka.9k",     0x0400, 0x0117, CRC(238d3ff4) SHA1(597f429d6a0ea485746322592604188c1ec87595) ) // The PAL @ 9k was bruteforced and verified to be to be functionally equivalent to a standard cp1b9ka

	ROM_REGION( 0x0400, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0200, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91635B-2 */
/* Note that this USA set seems to be the only one where GFX are stored into EPROMs instead of the usual mask ROMs. */
ROM_START( megaman )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "rcmu_23b.8f", 0x000000, 0x80000, CRC(1cd33c7a) SHA1(687fb3b6d660d7350447193f1911c47972e7a020) )
	ROM_LOAD16_WORD_SWAP( "rcmu_22b.7f", 0x080000, 0x80000, CRC(708268c4) SHA1(554e011cad285b95dd1b6aa19be61b2413662a3a) )
	ROM_LOAD16_WORD_SWAP( "rcmu_21a.6f", 0x100000, 0x80000, CRC(4376ea95) SHA1(7370ceffca513aa9f68a74f6869d561476589200) )

	ROM_REGION( 0x800000, "gfx", 0 )
	ROM_LOAD64_WORD( "rcm_01.3a",  0x000000, 0x80000, CRC(6ecdf13f) SHA1(2a8fe06bf5011e3f990f90d9224f91d8631ec0cc) )
	ROM_LOAD64_WORD( "rcm_02.4a",  0x000002, 0x80000, CRC(944d4f0f) SHA1(665dc9a537e9c9b565f6136f939ff5c2861f875f) )    // Dumped as rcm_03.5a
	ROM_LOAD64_WORD( "rcm_03.5a",  0x000004, 0x80000, CRC(36f3073c) SHA1(457d68e63599d06a136e152a9ad60adac1c91edd) )    // Dumped as rcm_02.4a
	ROM_LOAD64_WORD( "rcm_04.6a",  0x000006, 0x80000, CRC(54e622ff) SHA1(36f6297e3d410f041be5e582919478b0d52520ca) )
	ROM_LOAD64_WORD( "rcm_05.7a",  0x200000, 0x80000, CRC(5dd131fd) SHA1(1a7fc8cf38901245d40901996e946e7ad9c0e0c5) )
	ROM_LOAD64_WORD( "rcm_06.8a",  0x200002, 0x80000, CRC(f0faf813) SHA1(adff01c2ecc4c8ce6f8a50cbd07d8f8bb9f48168) )    // Dumped as rcm_07.9a
	ROM_LOAD64_WORD( "rcm_07.9a",  0x200004, 0x80000, CRC(826de013) SHA1(47f36b1d92a487c43c8dadc8293b8e6f40649286) )    // Dumped as rcm_06.8a
	ROM_LOAD64_WORD( "rcm_08.10a", 0x200006, 0x80000, CRC(fbff64cf) SHA1(f0cb531ef195dc1dcd224a208906a62fb5d199a1) )
	ROM_LOAD64_WORD( "rcm_10.3c",  0x400000, 0x80000, CRC(4dc8ada9) SHA1(776c2b3ef24c2b8f390c05a9c6728b14ceec696e) )
	ROM_LOAD64_WORD( "rcm_11.4c",  0x400002, 0x80000, CRC(f2b9ee06) SHA1(db315b00d1caed1a8c0f6e0ae726e8fa05b011fa) )    // Dumped as rcm_12.5c
	ROM_LOAD64_WORD( "rcm_12.5c",  0x400004, 0x80000, CRC(fed5f203) SHA1(23db14490519b5e2d0bb92ffe6e14540d1999e4b) )    // Dumped as rcm_11.4c
	ROM_LOAD64_WORD( "rcm_13.6c",  0x400006, 0x80000, CRC(5069d4a9) SHA1(b832b98be94371af52bd4bb911e18ec57430a7db) )
	ROM_LOAD64_WORD( "rcm_14.7c",  0x600000, 0x80000, CRC(303be3bd) SHA1(1e5c3fd71966ea9f457840c40582795b501c323e) )
	ROM_LOAD64_WORD( "rcm_15.8c",  0x600002, 0x80000, CRC(4f2d372f) SHA1(db6a94d1f92c1b96e404b38ebcb1eedbec3ae6cc) )    // Dumped as rcm_16.9c
	ROM_LOAD64_WORD( "rcm_16.9c",  0x600004, 0x80000, CRC(93d97fde) SHA1(e4be5216f98ad08a9118d629d398be2bd54e2e2a) )    // Dumped as rcm_15.8c
	ROM_LOAD64_WORD( "rcm_17.10c", 0x600006, 0x80000, CRC(92371042) SHA1(c55833cbaddcc986edd23c009a3e3c7ff09c2708) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "rcm_09.11a",  0x00000, 0x08000, CRC(22ac8f5f) SHA1(d1441d880f98034645cb4fcecd7bb746bde638af) )   /* different size from megamana, rockmanj but same label, pcb verified */
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "rcm_18.11c",  0x00000, 0x20000, CRC(80f1f8aa) SHA1(4a5b7b2a6941ad68da7472c63362c7bcd353fa54) )
	ROM_LOAD( "rcm_19.12c",  0x20000, 0x20000, CRC(f257dbe1) SHA1(967def6b6f93039dbc46373caabeb3301577be75) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "rcm63b.1a",    0x0000, 0x0117, CRC(84acd494) SHA1(20c861714c8c68bc8cf3bde9d051969807e9b3a3) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91634B-2 */
ROM_START( megamana )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "rcma_23b.8f", 0x000000, 0x80000, CRC(61e4a397) SHA1(a90b1cbef4206a4554398bc458a4b3e2c46d4c4f) )
	ROM_LOAD16_WORD_SWAP( "rcma_22b.7f", 0x080000, 0x80000, CRC(708268c4) SHA1(554e011cad285b95dd1b6aa19be61b2413662a3a) )  // == rcmu_22b.7f
	ROM_LOAD16_WORD_SWAP( "rcma_21a.6f", 0x100000, 0x80000, CRC(4376ea95) SHA1(7370ceffca513aa9f68a74f6869d561476589200) )  // == rcmu_21a.6f

	ROM_REGION( 0x800000, "gfx", 0 )
	ROM_LOAD64_WORD( "rcm_01.3a",  0x000000, 0x80000, CRC(6ecdf13f) SHA1(2a8fe06bf5011e3f990f90d9224f91d8631ec0cc) )
	ROM_LOAD64_WORD( "rcm_02.4a",  0x000002, 0x80000, CRC(944d4f0f) SHA1(665dc9a537e9c9b565f6136f939ff5c2861f875f) )
	ROM_LOAD64_WORD( "rcm_03.5a",  0x000004, 0x80000, CRC(36f3073c) SHA1(457d68e63599d06a136e152a9ad60adac1c91edd) )
	ROM_LOAD64_WORD( "rcm_04.6a",  0x000006, 0x80000, CRC(54e622ff) SHA1(36f6297e3d410f041be5e582919478b0d52520ca) )
	ROM_LOAD64_WORD( "rcm_05.7a",  0x200000, 0x80000, CRC(5dd131fd) SHA1(1a7fc8cf38901245d40901996e946e7ad9c0e0c5) )
	ROM_LOAD64_WORD( "rcm_06.8a",  0x200002, 0x80000, CRC(f0faf813) SHA1(adff01c2ecc4c8ce6f8a50cbd07d8f8bb9f48168) )
	ROM_LOAD64_WORD( "rcm_07.9a",  0x200004, 0x80000, CRC(826de013) SHA1(47f36b1d92a487c43c8dadc8293b8e6f40649286) )
	ROM_LOAD64_WORD( "rcm_08.10a", 0x200006, 0x80000, CRC(fbff64cf) SHA1(f0cb531ef195dc1dcd224a208906a62fb5d199a1) )
	ROM_LOAD64_WORD( "rcm_10.3c",  0x400000, 0x80000, CRC(4dc8ada9) SHA1(776c2b3ef24c2b8f390c05a9c6728b14ceec696e) )
	ROM_LOAD64_WORD( "rcm_11.4c",  0x400002, 0x80000, CRC(f2b9ee06) SHA1(db315b00d1caed1a8c0f6e0ae726e8fa05b011fa) )
	ROM_LOAD64_WORD( "rcm_12.5c",  0x400004, 0x80000, CRC(fed5f203) SHA1(23db14490519b5e2d0bb92ffe6e14540d1999e4b) )
	ROM_LOAD64_WORD( "rcm_13.6c",  0x400006, 0x80000, CRC(5069d4a9) SHA1(b832b98be94371af52bd4bb911e18ec57430a7db) )
	ROM_LOAD64_WORD( "rcm_14.7c",  0x600000, 0x80000, CRC(303be3bd) SHA1(1e5c3fd71966ea9f457840c40582795b501c323e) )
	ROM_LOAD64_WORD( "rcm_15.8c",  0x600002, 0x80000, CRC(4f2d372f) SHA1(db6a94d1f92c1b96e404b38ebcb1eedbec3ae6cc) )
	ROM_LOAD64_WORD( "rcm_16.9c",  0x600004, 0x80000, CRC(93d97fde) SHA1(e4be5216f98ad08a9118d629d398be2bd54e2e2a) )
	ROM_LOAD64_WORD( "rcm_17.10c", 0x600006, 0x80000, CRC(92371042) SHA1(c55833cbaddcc986edd23c009a3e3c7ff09c2708) )

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "rcm_09.12a",  0x00000, 0x08000, CRC(9632d6ef) SHA1(2bcb6f17005ffbc9ef8fa4478a814f24b2e6e0b6) )
	ROM_CONTINUE(            0x10000, 0x18000 ) // second half of ROM is empty, not mapped in memory

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "rcm_18.11c",  0x00000, 0x20000, CRC(80f1f8aa) SHA1(4a5b7b2a6941ad68da7472c63362c7bcd353fa54) )
	ROM_LOAD( "rcm_19.12c",  0x20000, 0x20000, CRC(f257dbe1) SHA1(967def6b6f93039dbc46373caabeb3301577be75) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "rcm63b.1a",    0x0000, 0x0117, CRC(84acd494) SHA1(20c861714c8c68bc8cf3bde9d051969807e9b3a3) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91634B-2 */
ROM_START( rockmanj )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "rcm_23a.8f", 0x000000, 0x80000, CRC(efd96cb2) SHA1(cbe81554f60d0c897f3f2ebc5bc966bb03cc23fe) )
	ROM_LOAD16_WORD_SWAP( "rcm_22a.7f", 0x080000, 0x80000, CRC(8729a689) SHA1(14ddb34d8201c544ea9d3d8c2cc52d380bc72930) )
	ROM_LOAD16_WORD_SWAP( "rcm_21a.6f", 0x100000, 0x80000, CRC(517ccde2) SHA1(492256c192f0c4814efa1ee1dd390453dd2e5865) )

	ROM_REGION( 0x800000, "gfx", 0 )
	ROM_LOAD64_WORD( "rcm_01.3a",  0x000000, 0x80000, CRC(6ecdf13f) SHA1(2a8fe06bf5011e3f990f90d9224f91d8631ec0cc) )
	ROM_LOAD64_WORD( "rcm_02.4a",  0x000002, 0x80000, CRC(944d4f0f) SHA1(665dc9a537e9c9b565f6136f939ff5c2861f875f) )
	ROM_LOAD64_WORD( "rcm_03.5a",  0x000004, 0x80000, CRC(36f3073c) SHA1(457d68e63599d06a136e152a9ad60adac1c91edd) )
	ROM_LOAD64_WORD( "rcm_04.6a",  0x000006, 0x80000, CRC(54e622ff) SHA1(36f6297e3d410f041be5e582919478b0d52520ca) )
	ROM_LOAD64_WORD( "rcm_05.7a",  0x200000, 0x80000, CRC(5dd131fd) SHA1(1a7fc8cf38901245d40901996e946e7ad9c0e0c5) )
	ROM_LOAD64_WORD( "rcm_06.8a",  0x200002, 0x80000, CRC(f0faf813) SHA1(adff01c2ecc4c8ce6f8a50cbd07d8f8bb9f48168) )
	ROM_LOAD64_WORD( "rcm_07.9a",  0x200004, 0x80000, CRC(826de013) SHA1(47f36b1d92a487c43c8dadc8293b8e6f40649286) )
	ROM_LOAD64_WORD( "rcm_08.10a", 0x200006, 0x80000, CRC(fbff64cf) SHA1(f0cb531ef195dc1dcd224a208906a62fb5d199a1) )
	ROM_LOAD64_WORD( "rcm_10.3c",  0x400000, 0x80000, CRC(4dc8ada9) SHA1(776c2b3ef24c2b8f390c05a9c6728b14ceec696e) )
	ROM_LOAD64_WORD( "rcm_11.4c",  0x400002, 0x80000, CRC(f2b9ee06) SHA1(db315b00d1caed1a8c0f6e0ae726e8fa05b011fa) )
	ROM_LOAD64_WORD( "rcm_12.5c",  0x400004, 0x80000, CRC(fed5f203) SHA1(23db14490519b5e2d0bb92ffe6e14540d1999e4b) )
	ROM_LOAD64_WORD( "rcm_13.6c",  0x400006, 0x80000, CRC(5069d4a9) SHA1(b832b98be94371af52bd4bb911e18ec57430a7db) )
	ROM_LOAD64_WORD( "rcm_14.7c",  0x600000, 0x80000, CRC(303be3bd) SHA1(1e5c3fd71966ea9f457840c40582795b501c323e) )
	ROM_LOAD64_WORD( "rcm_15.8c",  0x600002, 0x80000, CRC(4f2d372f) SHA1(db6a94d1f92c1b96e404b38ebcb1eedbec3ae6cc) )
	ROM_LOAD64_WORD( "rcm_16.9c",  0x600004, 0x80000, CRC(93d97fde) SHA1(e4be5216f98ad08a9118d629d398be2bd54e2e2a) )
	ROM_LOAD64_WORD( "rcm_17.10c", 0x600006, 0x80000, CRC(92371042) SHA1(c55833cbaddcc986edd23c009a3e3c7ff09c2708) )

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "rcm_09.12a",  0x00000, 0x08000, CRC(9632d6ef) SHA1(2bcb6f17005ffbc9ef8fa4478a814f24b2e6e0b6) )
	ROM_CONTINUE(            0x10000, 0x18000 ) // second half of ROM is empty, not mapped in memory

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "rcm_18.11c",  0x00000, 0x20000, CRC(80f1f8aa) SHA1(4a5b7b2a6941ad68da7472c63362c7bcd353fa54) )
	ROM_LOAD( "rcm_19.12c",  0x20000, 0x20000, CRC(f257dbe1) SHA1(967def6b6f93039dbc46373caabeb3301577be75) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "rcm63b.1a",    0x0000, 0x0117, CRC(84acd494) SHA1(20c861714c8c68bc8cf3bde9d051969807e9b3a3) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

/* B-Board 91634B-2 */
ROM_START( ganbare )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "mrnj_23d.8f", 0x00000, 0x80000, CRC(f929be72) SHA1(d175bdcace469277479ef85bf4e1b9d5a63cffde) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "mrnj_01.3a",  0x000000, 0x80000, CRC(3f878020) SHA1(b18faa50d88c76d19db1af73cf4b3095e928f51f) )
	ROM_LOAD64_WORD( "mrnj_02.4a",  0x000002, 0x80000, CRC(3e5624d8) SHA1(502e4897916af1c9e121b096de1369d06f1ffe87) )
	ROM_LOAD64_WORD( "mrnj_03.5a",  0x000004, 0x80000, CRC(d1e61f96) SHA1(5f6dee8adbf83c697416e440fbdd3a84a6e698da) )
	ROM_LOAD64_WORD( "mrnj_04.6a",  0x000006, 0x80000, CRC(d241971b) SHA1(b641740b40a043affbb79ea91ba12f821a259bad) )
	ROM_LOAD64_WORD( "mrnj_05.7a",  0x200000, 0x80000, CRC(c0a14562) SHA1(2fb6cf98fed83ac92c33df9526102a101454e276) )
	ROM_LOAD64_WORD( "mrnj_06.8a",  0x200002, 0x80000, CRC(e6a71dfc) SHA1(67178b020f87fb28ef35292d008ce9b80e02a2db) )
	ROM_LOAD64_WORD( "mrnj_07.9a",  0x200004, 0x80000, CRC(99afb6c7) SHA1(5caead2b71cd54f6b53765f09829cc9e92e1e2d6) )
	ROM_LOAD64_WORD( "mrnj_08.10a", 0x200006, 0x80000, CRC(52882c20) SHA1(5e3fca6da3470aeb78534f01e1575d8c0e067c0e) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "mrnj_09.12a",  0x00000, 0x08000, CRC(62470d72) SHA1(1de357a20f794defb49ed01af5b95ad00e2aa1d9) )
	ROM_CONTINUE(             0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "mrnj_18.11c",  0x00000, 0x20000, CRC(08e13940) SHA1(5c7dd7ff6a66f100b59cf9244e78f2c8702faca1) )
	ROM_LOAD( "mrnj_19.12c",  0x20000, 0x20000, CRC(5fa59927) SHA1(f05246cf566c214b008a91816c71e7c03b7cc218) )

	ROM_REGION( 0x8000, "timekeeper", 0) /* Timekeeper internal RAM was dumped (but game overwrites it - should I keep this here or remove it?) */
	ROM_LOAD( "m48t35y-70pc1.9n", 0x00000, 0x8000, CRC(96107b4a) SHA1(be9149736030e06c96083dcac73b5be3dbc318ac) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "gbpr2.1a",     0x0000, 0x0117, CRC(486e8ca0) SHA1(4554befd49ec322af7dadd198188255ae7c7c059) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END


/* B-Board 91634B-2 */
ROM_START( pmonster )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "gbpj_23a.8f", 0x00000, 0x80000, CRC(52ef2d85) SHA1(e6ec80159474e974987c6057a989d71ccb9b1b89) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "gbpj_01.3a",  0x000000, 0x80000, CRC(a7bea5bb) SHA1(57b594afcc4b42d7c61bab90574d45a53c2a4237) )
	ROM_LOAD64_WORD( "gbpj_02.4a",  0x000002, 0x80000, CRC(357b76ec) SHA1(247e18e0a289d833d6c0268265f775148c498378) )
	ROM_LOAD64_WORD( "gbpj_03.5a",  0x000004, 0x80000, CRC(bcbc1881) SHA1(15793dda4b72125a2c9e14b1d931648e4fb490fe) )
	ROM_LOAD64_WORD( "gbpj_04.6a",  0x000006, 0x80000, CRC(b1126fde) SHA1(5258226018ab8233d245f3b2f8fbe205dd338f27) )
	ROM_LOAD64_WORD( "gbpj_05.7a",  0x200000, 0x80000, CRC(bb5be4b0) SHA1(7febfb78606912419a65d66aef89772fa2fdd26c) ) // x1xxxxxxxxxxxxxxxxx = 0xFF
	ROM_LOAD64_WORD( "gbpj_06.8a",  0x200002, 0x80000, CRC(1be8fd86) SHA1(4b1dac47c15dd08e60f47c36d110844937a18c14) ) // x1xxxxxxxxxxxxxxxxx = 0xFF
	ROM_LOAD64_WORD( "gbpj_07.9a",  0x200004, 0x80000, CRC(deb8ef02) SHA1(25217e988ea26c8af1ba877f914185abd3bd70b2) ) // x1xxxxxxxxxxxxxxxxx = 0xFF
	ROM_LOAD64_WORD( "gbpj_08.10a", 0x200006, 0x80000, CRC(9f90359d) SHA1(cb673c9d902a75ed833248e5abacba4bb785930d) ) // x1xxxxxxxxxxxxxxxxx = 0xFF

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "gbpj_09.12a",  0x00000, 0x08000,  CRC(fc431024) SHA1(58e90c1b98cd3c2e2597f2f11374395952b8a8f3) )
	ROM_CONTINUE(             0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "gbpj_18.11c",  0x00000, 0x20000, CRC(cc54778d) SHA1(bc31397d6b0ae7357c1df9e537bbe5acbea430b2) )
	ROM_LOAD( "gbpj_19.12c",  0x20000, 0x20000, CRC(ea8b56d8) SHA1(ac0b2a32f5ac1c624bf7a80636a6b0e3944b9acc) )

	ROM_REGION( 0x8000, "timekeeper", ROMREGION_ERASE00 )
	ROM_LOAD( "m48t35y-70pc1.9n", 0x00000, 0x8000, NO_DUMP )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "gbp63b.1a",    0x0000, 0x0117, CRC(5077d37e) SHA1(7ec45f246e525f8d59624ff297ba264aeea9cb2b) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

ROM_START( pokonyan )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "xmqq-12f.bin", 0x00000, 0x20000, CRC(196297bf) SHA1(4724c4ccb755d3d457f5224d4c4ea860664d3b10) )
	ROM_LOAD16_BYTE( "xmqq-12h.bin", 0x00001, 0x20000, CRC(2d7ee2e9) SHA1(af9f8c346e36b4bd6284913129782187e242aa3f) )
	ROM_LOAD16_BYTE( "xmqq-13f.bin", 0x40000, 0x20000, CRC(8f6abf26) SHA1(e9918abc5ca02c8b3f3b5b2410aedc350dc12ec2) )
	ROM_LOAD16_BYTE( "xmqq-13h.bin", 0x40001, 0x20000, CRC(3fefe432) SHA1(42763c047fd976c9a8fec3a38f2b132b6e2e15a9) )

	ROM_REGION( 0x200000, "gfx", 0 )
	ROM_LOAD64_BYTE( "xmqq-4b.bin",  0x000000, 0x20000, CRC(933ab76d) SHA1(a51536b0ad89e08356cbb500c60099bd2a192a76) )
	ROM_LOAD64_BYTE( "xmqq-4a.bin",  0x000001, 0x20000, CRC(b098e7a9) SHA1(c848ddc0e6559dac6ca23d29c12bc65ea19b7dc4) )
	ROM_LOAD64_BYTE( "xmqq-9b.bin",  0x000002, 0x20000, CRC(b66d62d4) SHA1(1be1b89aa24382b4a3d4585b22b83f35b2a27333) )
	ROM_LOAD64_BYTE( "xmqq-9a.bin",  0x000003, 0x20000, CRC(9c23e40b) SHA1(9b5de73c4c6679a53871a45743a60c7cdefc8aaa) )
	ROM_LOAD64_BYTE( "xmqq-5e.bin",  0x000004, 0x20000, CRC(63d06d6f) SHA1(364d81aafa4d97e8d709e13b481585f655924254) )
	ROM_LOAD64_BYTE( "xmqq-5c.bin",  0x000005, 0x20000, CRC(e2169bb5) SHA1(2ca3d6eeee27606af0b21c69491c3bfbe1f82ed6) )
	ROM_LOAD64_BYTE( "xmqq-8h.bin",  0x000006, 0x20000, CRC(113121f5) SHA1(d5b3c97e70c3f53d4d68ed56d98e84df59f42073) )
	ROM_LOAD64_BYTE( "xmqq-8f.bin",  0x000007, 0x20000, CRC(beb00e07) SHA1(ab0264cb98fe9d77c5acff86800273ed4bdf2e1f) )
	ROM_LOAD64_BYTE( "xmqq-5b.bin",  0x100000, 0x20000, CRC(05354905) SHA1(6a53c47c9d87f76bf73b8cab540910cdcfcb9133) )
	ROM_LOAD64_BYTE( "xmqq-5a.bin",  0x100001, 0x20000, CRC(bd40215e) SHA1(7350e56ecef8e87cf5a4d4f50231fada6fc5356b) )
	ROM_LOAD64_BYTE( "xmqq-10b.bin", 0x100002, 0x20000, CRC(9fa773ef) SHA1(4f0cf813cfe7fbdf20403cfab81b1c01907eb62f) )
	ROM_LOAD64_BYTE( "xmqq-10a.bin", 0x100003, 0x20000, CRC(638d4bc7) SHA1(fca924ad53ad1d00dc2e70436fd22de81c08115f) )
	ROM_LOAD64_BYTE( "xmqq-7e.bin",  0x100004, 0x20000, CRC(72c45858) SHA1(e1ea89568077739b891c95f57c867c02f6c109ff) )
	ROM_LOAD64_BYTE( "xmqq-7c.bin",  0x100005, 0x20000, CRC(d91cda18) SHA1(ef40cb78484355be74d2a08fdd870b49263ab57d) )
	ROM_LOAD64_BYTE( "xmqq-9h.bin",  0x100006, 0x20000, CRC(3cd8594b) SHA1(a099428d0f0231f91881beee0bd6a80ba2c3d693) )
	ROM_LOAD64_BYTE( "xmqq-9f.bin",  0x100007, 0x20000, CRC(1ec10bed) SHA1(139563c41450591b5674d885e3260cb99b206e25) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "xmqq-13b.bin",   0x00000, 0x08000, CRC(4e8b81a8) SHA1(4a837345223c51d668ec7f4d7460871c00f71482) )
	ROM_CONTINUE(               0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "xmqq-12c.bin",  0x00000, 0x20000, CRC(71ac69ad) SHA1(401fe0294c6b7bc79426ab1922e02a97415cd2ef) )
	ROM_LOAD( "xmqq-13c.bin",  0x20000, 0x20000, CRC(71e29699) SHA1(8f19a925bbd47ef965c41af233359a1552c7f8b0) )

	/*
	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )
	*/

	/* not sure
	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "unknown.1a",     0x0000, 0x0117, NO_DUMP )
	ROM_LOAD( "lwio.12e",     0x0000, 0x0117, CRC(ad52b90c) SHA1(f0fd6aeea515ee449320fe15684e6b3ab7f97bf4) )
	*/
ROM_END

ROM_START( mpumpkin ) // B board: 91634B-?  C board: 92631C-6
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "mpa_23.8f", 0x00000, 0x80000, CRC(38b9883a) SHA1(94a89a360a502f209aa905e7eb2f1d472960850f) )
	// remaining sockets are empty

	ROM_REGION( 0x600000, "gfx", 0 ) // 7a - 9a and  8a - 10a have same contents?
	ROM_LOAD64_WORD( "mpa_01.3a",  0x000000, 0x80000, CRC(7c8c0c22) SHA1(29659c37a519470452afcd553a501788ad56dc1e) )
	ROM_LOAD64_WORD( "mpa_02.4a",  0x000002, 0x80000, CRC(23f95339) SHA1(09227f55ecdf86265e85ea3d09e0a829a1220f3a) )
	ROM_LOAD64_WORD( "mpa_03.5a",  0x000004, 0x80000, CRC(107842a6) SHA1(8410370d97ac2e6e23774375c4e9d5d000a6eda4) )
	ROM_LOAD64_WORD( "mpa_04.6a",  0x000006, 0x80000, CRC(fce457ae) SHA1(7e2116aeeb6204311a78af5f3c1736fbff4e6723) )
	ROM_LOAD64_WORD( "mpa_05.7a",  0x200000, 0x80000, CRC(ba8f3585) SHA1(7f43328d8a921478a24909eed18c8c921177add0) )
	ROM_LOAD64_WORD( "mpa_06.8a",  0x200002, 0x80000, CRC(037f20cc) SHA1(d702d648bcf47fe6f9fa033aa5b9fa1238191cf2) )
	ROM_LOAD64_WORD( "mpa_07.9a",  0x200004, 0x80000, CRC(ba8f3585) SHA1(7f43328d8a921478a24909eed18c8c921177add0) )
	ROM_LOAD64_WORD( "mpa_08.10a", 0x200006, 0x80000, CRC(037f20cc) SHA1(d702d648bcf47fe6f9fa033aa5b9fa1238191cf2) )
	ROM_LOAD64_WORD( "mpa_10.3c",  0x400000, 0x80000, CRC(870f3a2a) SHA1(7e852cb7a0d2c49aeb3ff28676b2f66bb42646ed) )
	ROM_LOAD64_WORD( "mpa_11.4c",  0x400002, 0x80000, CRC(8923fc3a) SHA1(619ac7218ef14d863180e70c9d55729025f14d7c) )
	ROM_LOAD64_WORD( "mpa_12.5c",  0x400004, 0x80000, CRC(87b88629) SHA1(85fed8a00e4c960456732ba6e6ae45ce23a60470) )
	ROM_LOAD64_WORD( "mpa_13.6c",  0x400006, 0x80000, CRC(a09a6acf) SHA1(cb6b75dabb5422572123be60356765cedcdcbfe9) )
	// 7c to 10c empty

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "mpa_09.12a", 0x00000, 0x08000, CRC(0b5b1b72) SHA1(a8883f141a00870c98a74d9d57378c4a607dc590) )
	ROM_CONTINUE(           0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "mpa_18.11c",     0x00000, 0x20000, CRC(cef6d39e) SHA1(3180a3f397b5c4fbbc2b8f84cf461764ddd10d4e) )
	ROM_LOAD( "mpa_19.12c",     0x20000, 0x20000, CRC(24947f8e) SHA1(e26270962cc4b0f9a6a5c306fa09d2cdd524cfa9) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "sfz63b.1a",    0x0000, 0x0104, CRC(f5a351da) SHA1(a867947d784167b5284efb76a8634ca5713dafdb) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END

ROM_START( gulunpa ) // ROMs could do with 2nd pass dump verification
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_BYTE( "26",           0x00000, 0x020000, CRC(f30ffa29) SHA1(9e70daf4229485dc5700b074dba55839c7357351) )
	ROM_LOAD16_BYTE( "30",           0x00001, 0x020000, CRC(5d35f737) SHA1(47b6bfa6eaa512684e12c23162243d1a21cb1a7a) )

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "9",            0x00000, 0x08000, CRC(15afd06f) SHA1(1a4ff3e11e55266e7c93743b6564c226eaaba142) )
	ROM_CONTINUE(             0x10000, 0x08000 )

	ROM_REGION( 0x200000, "gfx", ROMREGION_ERASE00 )
	ROM_LOAD64_WORD( "1",            0x000000, 0x080000, CRC(b55e648f) SHA1(e22eec707b3b1ad8fb93c0f2df41ccf72cd03440) )
	ROM_LOAD64_WORD( "2",            0x000002, 0x080000, CRC(ad033bce) SHA1(b37b1d341e61502aa4213b049b14974fab8a0445) )
	ROM_LOAD64_WORD( "3",            0x000004, 0x080000, CRC(36c3951a) SHA1(74edaca2c78dd6a304ea702091a9f0b7f6036e41) )
	ROM_LOAD64_WORD( "4",            0x000006, 0x080000, BAD_DUMP CRC(ff0cb826) SHA1(fec7833652e6789e886a1ec7b4680a608ddbbe20) )
	ROM_FILL(0x000006, 1, 0xff) // patch first byte of ROM 4 pending redump (corrupt top line of 0 character, 8x8 tile data should match between ROM pairs)
//  ROM_COPY( "gfx", 0x000000, 0x200000, 0x200000 )

	ROM_REGION( 0x40000, "oki", ROMREGION_ERASEFF ) /* Samples */
	ROM_LOAD( "18",          0x00000, 0x20000, CRC(9997a34f) SHA1(8e107d6413836c48fc57e4a9b89ae99a9e381e8b) )
	ROM_LOAD( "19",          0x20000, 0x20000, CRC(e95270ac) SHA1(dc684abfa1ea276a00ec541ab8f3f9f131375faa) )

	// TODO: plds etc.
ROM_END

/* Home 'CPS Changer' Unit */

/* B-Board 91635B-2 */
ROM_START( wofch )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "tk2=ch=_23.8f", 0x000000, 0x80000, CRC(4e0b8dee) SHA1(d2fb716d62b7a259f46bbc74c1976a18d56696ea) )
	ROM_LOAD16_WORD_SWAP( "tk2=ch=_22.7f", 0x080000, 0x80000, CRC(d0937a8d) SHA1(01d7be446e2e3ef8ca767f59c178240dfd52dd93) )

	ROM_REGION( 0x400000, "gfx", 0 )
	ROM_LOAD64_WORD( "tk2-1m.3a",      0x000000, 0x80000, CRC(0d9cb9bf) SHA1(cc7140e9a01a14b252cb1090bcea32b0de461928) )
	ROM_LOAD64_WORD( "tk2-3m.5a",      0x000002, 0x80000, CRC(45227027) SHA1(b21afc593f0d4d8909dfa621d659cbb40507d1b2) )
	ROM_LOAD64_WORD( "tk2-2m.4a",      0x000004, 0x80000, CRC(c5ca2460) SHA1(cbe14867f7b94b638ca80db7c8e0c60881183469) )
	ROM_LOAD64_WORD( "tk2-4m.6a",      0x000006, 0x80000, CRC(e349551c) SHA1(1d977bdf256accf750ad9930ec4a0a19bbf86964) )
	ROM_LOAD64_WORD( "tk2=ch=_05.7a",  0x200000, 0x80000, CRC(e4a44d53) SHA1(b747679f4d63e5e62d9fd81b3120fba0401fadfb) )    // == tk2_05.7a
	ROM_LOAD64_WORD( "tk2=ch=_06.8a",  0x200002, 0x80000, CRC(58066ba8) SHA1(c93af968e21094d020e4b2002e0c6fc0d746af0b) )    // == tk2_06.8a
	ROM_LOAD64_WORD( "tk2=ch=_07.9a",  0x200004, 0x80000, CRC(cc9006c9) SHA1(cfcbec3a67052268a7739538aa28a6391fe5400e) )    /* 1 byte different from wofj, pcb verified */
	ROM_LOAD64_WORD( "tk2=ch=_08.10a", 0x200006, 0x80000, CRC(d4a19a02) SHA1(ff396b1d33d9b4842140f2c6d085fe05748e3244) )    // == tk2_08.10a

	ROM_REGION( 0x28000, "audiocpu", 0 ) /* QSound Z80 code */
	ROM_LOAD( "tk2_qa.5k",       0x00000, 0x08000, CRC(c9183a0d) SHA1(d8b1d41c572f08581f8ab9eb878de77d6ea8615d) )
	ROM_CONTINUE(                0x10000, 0x18000 )

	ROM_REGION( 0x200000, "qsound", 0 ) /* QSound samples */
	ROM_LOAD( "tk2-q1.1k",       0x000000, 0x80000, CRC(611268cf) SHA1(83ab059f2110fb25fdcff928d56b790fc1f5c975) )
	ROM_LOAD( "tk2-q2.2k",       0x080000, 0x80000, CRC(20f55ca9) SHA1(90134e9a9c4749bb65c728b66ea4dac1fd4d88a4) )
	ROM_LOAD( "tk2-q3.3k",       0x100000, 0x80000, CRC(bfcf6f52) SHA1(2a85ff3fc89b4cbabd20779ec12da2e116333c7c) )
	ROM_LOAD( "tk2-q4.4k",       0x180000, 0x80000, CRC(36642e88) SHA1(8ab25b19e2b67215a5cb1f3aa81b9d26009cfeb8) )

	ROM_REGION( 0x0200, "aboardplds", 0 )
	ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
	ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
	ROM_LOAD( "prg2",         0x0000, 0x0117, CRC(4386879a) SHA1(c36896d169d8c78393609acbbe4397931292a033) )
	ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
	ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "tk263b.1a",    0x0000, 0x0117, CRC(c4b0349b) SHA1(b4873dd5ad8735048deb3475222dde3c0b67eaaf) )
	ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
	ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

	ROM_REGION( 0x0200, "cboardplds", 0 )
	ROM_LOAD( "ioc1.ic7",     0x0000, 0x0104, CRC(a399772d) SHA1(55471189db573dd61e3087d12c55564291672c77) )
	ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )

	ROM_REGION( 0x0200, "dboardplds", 0 )
	ROM_LOAD( "d7l1.7l",      0x0000, 0x0117, CRC(27b7410d) SHA1(06d0cba0226850f100ff1f539bd7d5db0f90c730) )
	ROM_LOAD( "d8l1.8l",      0x0000, 0x0117, CRC(539fc7da) SHA1(cad5c91629c6247e49ccbbcbfe6b08229eafae07) )
	ROM_LOAD( "d9k1.9k",      0x0000, 0x0117, CRC(6c35c805) SHA1(641b9003b8e7fc969d0cd679f98e413ed2abe474) )
	ROM_LOAD( "d10f1.10f",    0x0000, 0x0117, CRC(6619c494) SHA1(3aef656c07182a2186f810f30e0d854dd5bd8d18) )
ROM_END

ROM_START( sfach )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "sfach23",        0x000000, 0x80000, CRC(02a1a853) SHA1(d92b9e774844fdcc9d9946b3e892b021e672d876) )
	ROM_LOAD16_WORD_SWAP( "sfza22",         0x080000, 0x80000, CRC(8d9b2480) SHA1(405305c1572908d00eab735f28676fbbadb4fac6) )
	ROM_LOAD16_WORD_SWAP( "sfzch21",        0x100000, 0x80000, CRC(5435225d) SHA1(6b1156fd82d0710e244ede39faaae0847c598376) )
	ROM_LOAD16_WORD_SWAP( "sfza20",         0x180000, 0x80000, CRC(806e8f38) SHA1(b6d6912aa8f2f590335d7ff9a8214648e7131ebb) )

	ROM_REGION( 0x800000, "gfx", 0 )
	ROM_LOAD64_WORD( "sfz01",         0x000000, 0x80000, CRC(0dd53e62) SHA1(5f3bcf5ca0fd564d115fe5075a4163d3ee3226df) )
	ROM_LOAD64_WORD( "sfz02",         0x000002, 0x80000, CRC(94c31e3f) SHA1(2187b3d4977514f2ae486eb33ed76c86121d5745) )
	ROM_LOAD64_WORD( "sfz03",         0x000004, 0x80000, CRC(9584ac85) SHA1(bbd62d66b0f6909630e801ce5d6331d43f44d741) )
	ROM_LOAD64_WORD( "sfz04",         0x000006, 0x80000, CRC(b983624c) SHA1(841106bb9453e3dfb7869c4b0e9149cc610d515a) )
	ROM_LOAD64_WORD( "sfz05",         0x200000, 0x80000, CRC(2b47b645) SHA1(bc6426eff5df9417f32666586744626fa544f7b5) )
	ROM_LOAD64_WORD( "sfz06",         0x200002, 0x80000, CRC(74fd9fb1) SHA1(7945472591f3c06970e96611a0363ed8f3d52c36) )
	ROM_LOAD64_WORD( "sfz07",         0x200004, 0x80000, CRC(bb2c734d) SHA1(97a06935f86f31755d2ffdc5b56bef53944bdecd) )
	ROM_LOAD64_WORD( "sfz08",         0x200006, 0x80000, CRC(454f7868) SHA1(eecccba7542d893bc41676246a20aa4914b79bbc) )
	ROM_LOAD64_WORD( "sfz10",         0x400000, 0x80000, CRC(2a7d675e) SHA1(0144ba34a29fb08b41c780ce65bb06d25724e88f) )
	ROM_LOAD64_WORD( "sfz11",         0x400002, 0x80000, CRC(e35546c8) SHA1(7b08aa3413494d12c5c550263a5f00b64b98e6ab) )
	ROM_LOAD64_WORD( "sfz12",         0x400004, 0x80000, CRC(f122693a) SHA1(71ce901d8d30207e506b6a8d6a4e0fcf3a1b0eac) )
	ROM_LOAD64_WORD( "sfz13",         0x400006, 0x80000, CRC(7cf942c8) SHA1(a7109facb97a8a11ddf1b4e07de6ff3164d713a1) )
	ROM_LOAD64_WORD( "sfz14",         0x600000, 0x80000, CRC(09038c81) SHA1(3461d70902fbfb92ce40f804be6388276a01d153) )
	ROM_LOAD64_WORD( "sfz15",         0x600002, 0x80000, CRC(1aa17391) SHA1(b4d0f760a430b7fc4443b6c94da2659315c5b926) )
	ROM_LOAD64_WORD( "sfz16",         0x600004, 0x80000, CRC(19a5abd6) SHA1(73ba1de15c883fdc69fd7dccdb58d00ca512d4ea) )
	ROM_LOAD64_WORD( "sfz17",         0x600006, 0x80000, CRC(248b3b73) SHA1(95810a17b1caf6372b33ed3e4ee8a7e51482c70d) )

	ROM_REGION( 0x8000, "stars", 0 )
	ROM_COPY( "gfx", 0x000000, 0x000000, 0x8000 )   /* stars */

	ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sfz09",         0x00000, 0x08000, CRC(c772628b) SHA1(ebc5b7c173caf1e151f733f23c1b20abec24e16d) )
	ROM_CONTINUE(              0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
	ROM_LOAD( "sfz18",         0x00000, 0x20000, CRC(61022b2d) SHA1(6369d0c1d08a30ee19b94e52ab1463a7784b9de5) )
	ROM_LOAD( "sfz19",         0x20000, 0x20000, CRC(3b5886d5) SHA1(7e1b7d40ef77b5df628dd663d45a9a13c742cf58) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "sfz63b.1a",     0x0000, 0x0104, CRC(f5a351da) SHA1(a867947d784167b5284efb76a8634ca5713dafdb) )
ROM_END

/* FIXME B-Board uncertain but should be 91634B from the program ROM names */
ROM_START( sfzch )
	ROM_REGION( CODE_SIZE, "maincpu",0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "sfzch23",        0x000000, 0x80000, CRC(1140743f) SHA1(10bcedb5cca266f2aa3ed99ede6f9a64fc877539) )
	ROM_LOAD16_WORD_SWAP( "sfza22",         0x080000, 0x80000, CRC(8d9b2480) SHA1(405305c1572908d00eab735f28676fbbadb4fac6) )
	ROM_LOAD16_WORD_SWAP( "sfzch21",        0x100000, 0x80000, CRC(5435225d) SHA1(6b1156fd82d0710e244ede39faaae0847c598376) )
	ROM_LOAD16_WORD_SWAP( "sfza20",         0x180000, 0x80000, CRC(806e8f38) SHA1(b6d6912aa8f2f590335d7ff9a8214648e7131ebb) )

	ROM_REGION( 0x800000, "gfx", 0 )
	ROM_LOAD64_WORD( "sfz_01.3a",  0x000000, 0x80000, CRC(0dd53e62) SHA1(5f3bcf5ca0fd564d115fe5075a4163d3ee3226df) )
	ROM_LOAD64_WORD( "sfz_02.4a",  0x000002, 0x80000, CRC(94c31e3f) SHA1(2187b3d4977514f2ae486eb33ed76c86121d5745) )
	ROM_LOAD64_WORD( "sfz_03.5a",  0x000004, 0x80000, CRC(9584ac85) SHA1(bbd62d66b0f6909630e801ce5d6331d43f44d741) )
	ROM_LOAD64_WORD( "sfz_04.6a",  0x000006, 0x80000, CRC(b983624c) SHA1(841106bb9453e3dfb7869c4b0e9149cc610d515a) )
	ROM_LOAD64_WORD( "sfz_05.7a",  0x200000, 0x80000, CRC(2b47b645) SHA1(bc6426eff5df9417f32666586744626fa544f7b5) )
	ROM_LOAD64_WORD( "sfz_06.8a",  0x200002, 0x80000, CRC(74fd9fb1) SHA1(7945472591f3c06970e96611a0363ed8f3d52c36) )
	ROM_LOAD64_WORD( "sfz_07.9a",  0x200004, 0x80000, CRC(bb2c734d) SHA1(97a06935f86f31755d2ffdc5b56bef53944bdecd) )
	ROM_LOAD64_WORD( "sfz_08.10a", 0x200006, 0x80000, CRC(454f7868) SHA1(eecccba7542d893bc41676246a20aa4914b79bbc) )
	ROM_LOAD64_WORD( "sfz_10.3c",  0x400000, 0x80000, CRC(2a7d675e) SHA1(0144ba34a29fb08b41c780ce65bb06d25724e88f) )
	ROM_LOAD64_WORD( "sfz_11.4c",  0x400002, 0x80000, CRC(e35546c8) SHA1(7b08aa3413494d12c5c550263a5f00b64b98e6ab) )
	ROM_LOAD64_WORD( "sfz_12.5c",  0x400004, 0x80000, CRC(f122693a) SHA1(71ce901d8d30207e506b6a8d6a4e0fcf3a1b0eac) )
	ROM_LOAD64_WORD( "sfz_13.6c",  0x400006, 0x80000, CRC(7cf942c8) SHA1(a7109facb97a8a11ddf1b4e07de6ff3164d713a1) )
	ROM_LOAD64_WORD( "sfz_14.7c",  0x600000, 0x80000, CRC(09038c81) SHA1(3461d70902fbfb92ce40f804be6388276a01d153) )
	ROM_LOAD64_WORD( "sfz_15.8c",  0x600002, 0x80000, CRC(1aa17391) SHA1(b4d0f760a430b7fc4443b6c94da2659315c5b926) )
	ROM_LOAD64_WORD( "sfz_16.9c",  0x600004, 0x80000, CRC(19a5abd6) SHA1(73ba1de15c883fdc69fd7dccdb58d00ca512d4ea) )
	ROM_LOAD64_WORD( "sfz_17.10c", 0x600006, 0x80000, CRC(248b3b73) SHA1(95810a17b1caf6372b33ed3e4ee8a7e51482c70d) )

	ROM_REGION( 0x8000, "stars", 0 )
	ROM_COPY( "gfx", 0x000000, 0x000000, 0x8000 )

	ROM_REGION( 0x18000, "audiocpu",0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sfz_09.12a",  0x00000, 0x08000, CRC(c772628b) SHA1(ebc5b7c173caf1e151f733f23c1b20abec24e16d) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki",0 )  /* Samples */
	ROM_LOAD( "sfz_18.11c",  0x00000, 0x20000, CRC(61022b2d) SHA1(6369d0c1d08a30ee19b94e52ab1463a7784b9de5) )
	ROM_LOAD( "sfz_19.12c",  0x20000, 0x20000, CRC(3b5886d5) SHA1(7e1b7d40ef77b5df628dd663d45a9a13c742cf58) )

	// SFZ63B was found on mpumpkin, assumed to be from SFZ
	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "sfz63b.1a",   0x0000, 0x0104, CRC(f5a351da) SHA1(a867947d784167b5284efb76a8634ca5713dafdb) )  // PAL16L8

	// TODO... confirm other pals (could be unique as this is the "cps changer" home console)
ROM_END

/* FIXME B-Board uncertain but should be 91634B from the program ROM names */
ROM_START( sfzbch )
	ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "sfzbch23",       0x000000, 0x80000, CRC(53699f68) SHA1(d7f132faf8c31b5e79c32e6b0cce45377ec8d474) )
	ROM_LOAD16_WORD_SWAP( "sfza22",         0x080000, 0x80000, CRC(8d9b2480) SHA1(405305c1572908d00eab735f28676fbbadb4fac6) )
	ROM_LOAD16_WORD_SWAP( "sfzch21",        0x100000, 0x80000, CRC(5435225d) SHA1(6b1156fd82d0710e244ede39faaae0847c598376) )
	ROM_LOAD16_WORD_SWAP( "sfza20",         0x180000, 0x80000, CRC(806e8f38) SHA1(b6d6912aa8f2f590335d7ff9a8214648e7131ebb) )

	ROM_REGION( 0x800000, "gfx", 0 )
	ROM_LOAD64_WORD( "sfz_01.3a",  0x000000, 0x80000, CRC(0dd53e62) SHA1(5f3bcf5ca0fd564d115fe5075a4163d3ee3226df) )
	ROM_LOAD64_WORD( "sfz_02.4a",  0x000002, 0x80000, CRC(94c31e3f) SHA1(2187b3d4977514f2ae486eb33ed76c86121d5745) )
	ROM_LOAD64_WORD( "sfz_03.5a",  0x000004, 0x80000, CRC(9584ac85) SHA1(bbd62d66b0f6909630e801ce5d6331d43f44d741) )
	ROM_LOAD64_WORD( "sfz_04.6a",  0x000006, 0x80000, CRC(b983624c) SHA1(841106bb9453e3dfb7869c4b0e9149cc610d515a) )
	ROM_LOAD64_WORD( "sfz_05.7a",  0x200000, 0x80000, CRC(2b47b645) SHA1(bc6426eff5df9417f32666586744626fa544f7b5) )
	ROM_LOAD64_WORD( "sfz_06.8a",  0x200002, 0x80000, CRC(74fd9fb1) SHA1(7945472591f3c06970e96611a0363ed8f3d52c36) )
	ROM_LOAD64_WORD( "sfz_07.9a",  0x200004, 0x80000, CRC(bb2c734d) SHA1(97a06935f86f31755d2ffdc5b56bef53944bdecd) )
	ROM_LOAD64_WORD( "sfz_08.10a", 0x200006, 0x80000, CRC(454f7868) SHA1(eecccba7542d893bc41676246a20aa4914b79bbc) )
	ROM_LOAD64_WORD( "sfz_10.3c",  0x400000, 0x80000, CRC(2a7d675e) SHA1(0144ba34a29fb08b41c780ce65bb06d25724e88f) )
	ROM_LOAD64_WORD( "sfz_11.4c",  0x400002, 0x80000, CRC(e35546c8) SHA1(7b08aa3413494d12c5c550263a5f00b64b98e6ab) )
	ROM_LOAD64_WORD( "sfz_12.5c",  0x400004, 0x80000, CRC(f122693a) SHA1(71ce901d8d30207e506b6a8d6a4e0fcf3a1b0eac) )
	ROM_LOAD64_WORD( "sfz_13.6c",  0x400006, 0x80000, CRC(7cf942c8) SHA1(a7109facb97a8a11ddf1b4e07de6ff3164d713a1) )
	ROM_LOAD64_WORD( "sfz_14.7c",  0x600000, 0x80000, CRC(09038c81) SHA1(3461d70902fbfb92ce40f804be6388276a01d153) )
	ROM_LOAD64_WORD( "sfz_15.8c",  0x600002, 0x80000, CRC(1aa17391) SHA1(b4d0f760a430b7fc4443b6c94da2659315c5b926) )
	ROM_LOAD64_WORD( "sfz_16.9c",  0x600004, 0x80000, CRC(19a5abd6) SHA1(73ba1de15c883fdc69fd7dccdb58d00ca512d4ea) )
	ROM_LOAD64_WORD( "sfz_17.10c", 0x600006, 0x80000, CRC(248b3b73) SHA1(95810a17b1caf6372b33ed3e4ee8a7e51482c70d) )

	ROM_REGION( 0x8000, "stars", 0 )
	ROM_COPY( "gfx", 0x000000, 0x000000, 0x8000 )   /* stars */

	ROM_REGION( 0x18000, "audiocpu",0 ) /* 64k for the audio CPU (+banks) */
	ROM_LOAD( "sfz_09.12a",  0x00000, 0x08000, CRC(c772628b) SHA1(ebc5b7c173caf1e151f733f23c1b20abec24e16d) )
	ROM_CONTINUE(            0x10000, 0x08000 )

	ROM_REGION( 0x40000, "oki",0 )  /* Samples */
	ROM_LOAD( "sfz_18.11c",  0x00000, 0x20000, CRC(61022b2d) SHA1(6369d0c1d08a30ee19b94e52ab1463a7784b9de5) )
	ROM_LOAD( "sfz_19.12c",  0x20000, 0x20000, CRC(3b5886d5) SHA1(7e1b7d40ef77b5df628dd663d45a9a13c742cf58) )

	ROM_REGION( 0x0200, "bboardplds", 0 )
	ROM_LOAD( "sfz63b.1a",   0x0000, 0x0104, CRC(f5a351da) SHA1(a867947d784167b5284efb76a8634ca5713dafdb) )
ROM_END

ROM_START( cps1mult )
	ROM_REGION( 0x4000000, "maincpu", 0 )      /* 68000 code */
	ROM_LOAD16_WORD_SWAP( "prg_0.bin", 0x0000000, 0x800000, CRC(cbb4062c) SHA1(468bd4c381da299491807daad0a2f1a4128d8fce) )
	ROM_LOAD16_WORD_SWAP( "prg_1.bin", 0x0800000, 0x800000, CRC(e434b882) SHA1(d66c92aaaf43e37fc224013e4c93dc02550616e6) )
	ROM_LOAD16_WORD_SWAP( "prg_2.bin", 0x1000000, 0x800000, CRC(2ce2fc75) SHA1(0fc038f0188e91e3490c127877a17b57f5bb10fd) )
	ROM_LOAD16_WORD_SWAP( "prg_3.bin", 0x1800000, 0x800000, CRC(0a93b43e) SHA1(bf3669c3970beacabbe4876d6d302ddcb531aa21) )
	ROM_LOAD16_WORD_SWAP( "prg_4.bin", 0x2000000, 0x800000, CRC(80b6dfb3) SHA1(332daea56d4dc00cceea8b84d9a0872de7f5f48d) )
	ROM_LOAD16_WORD_SWAP( "prg_5.bin", 0x2800000, 0x800000, CRC(bfa503e7) SHA1(0efecaf34832e6db5aad429513df1188a1086e6a) )
	ROM_LOAD16_WORD_SWAP( "prg_6.bin", 0x3000000, 0x800000, CRC(977d2d34) SHA1(1b9ed488f600aa3352ad6a4ce88b385a44f5b598) )
	ROM_LOAD16_WORD_SWAP( "prg_7.bin", 0x3800000, 0x800000, CRC(65b0d8fd) SHA1(1ae4bf558c5884b4429a6bd1b3130076bf0de47a) )

	ROM_REGION( 0x8000000, "gfx", 0 )
	ROM_LOAD( "ca_0.bin", 0x0000000, 0x0800000, CRC(6819f572) SHA1(08026fe527a3e3e98c4b490f677a6daad9013752) )
	ROM_LOAD( "ca_1.bin", 0x0800000, 0x0800000, CRC(f18256d2) SHA1(0ba49b2f1abc67a08965e43df70aed42b3bc1c31) )
	ROM_LOAD( "ca_2.bin", 0x1000000, 0x0800000, CRC(1f474165) SHA1(c0c3632c1dbadeaea1334cfe8954d091458d773a) )
	ROM_LOAD( "ca_3.bin", 0x1800000, 0x0800000, CRC(1f7f36a7) SHA1(33fb5e280111ce7f1dcf18093285d8834ac76be9) )
	ROM_LOAD( "ca_4.bin", 0x2000000, 0x0800000, CRC(b6c2ae5c) SHA1(e7769bb593d37934280bf7d16d95b867854d4a4b) )
	ROM_LOAD( "ca_5.bin", 0x2800000, 0x0800000, CRC(68e7b97e) SHA1(f2457e0341d8571a81f31497f7a54c6b9b312276) )
	ROM_LOAD( "ca_6.bin", 0x3000000, 0x0800000, CRC(d7df2079) SHA1(ab1ae0fa4b8f3db1390b7504e62b2db2540ab003) )
	ROM_LOAD( "ca_7.bin", 0x3800000, 0x0800000, CRC(76c35a1f) SHA1(8139a759ba9edaad44962a73f494e2af20a49787) )
	ROM_LOAD( "cb_0.bin", 0x4000000, 0x0800000, CRC(c9ca6efc) SHA1(7c2c8b62fd2e10e968e7f739536ba0bc99826ffe) )
	ROM_LOAD( "cb_1.bin", 0x4800000, 0x0800000, CRC(c53de0e5) SHA1(518407210e3afcc43dea776f0cafe735f5b1d0fb) )
	ROM_LOAD( "cb_2.bin", 0x5000000, 0x0800000, CRC(2934a28c) SHA1(ab0a800adee07d485fd2b93be3f56d49cf18078f) )
	ROM_LOAD( "cb_3.bin", 0x5800000, 0x0800000, CRC(ca41ae19) SHA1(ef8fc3b2f7027be4431bcd5954490392b40af329) )
	ROM_LOAD( "cb_4.bin", 0x6000000, 0x0800000, CRC(b9ea6163) SHA1(9759ee1ec434406c1640743266a8576ca112dd92) )
	ROM_LOAD( "cb_5.bin", 0x6800000, 0x0800000, CRC(74a402f2) SHA1(1d04ae19d0ed81532a01c8c069267fc6474fabdd) )
	ROM_LOAD( "cb_6.bin", 0x7000000, 0x0800000, CRC(73f471e1) SHA1(d0a56fb43c0b8f37d60c728bd4fc8abeeb294774) )
	ROM_LOAD( "cb_7.bin", 0x7800000, 0x0800000, CRC(7570e1f0) SHA1(e19243d21ee126c62f5e96150dd04c1ff8a18868) )

	ROM_REGION( 0x100000, "audiocpu", 0 )
	ROM_LOAD( "m1_074733.bin",  0x000000, 0x100000, CRC(fbb43b64) SHA1(099e02ba2e3e2b04bb99144009927dac584f89f0) )

	ROM_REGION( 0x400000, "oki", 0 ) /* Samples */
	ROM_LOAD( "mx29f1610mcpsop44.u18",  0x000000, 0x200000, CRC(69cd2a53) SHA1(f5d78d7f4a807d5124cc8d48e57d283c3ddf972b) )
	ROM_LOAD( "mx29f1610mcpsop44.u19a", 0x200000, 0x200000, CRC(4a0cebaa) SHA1(9384780fad82310122ac930bafe2e2c48b562676) )
ROM_END


uint16_t cps_state::sf2rb_prot_r(offs_t offset)
{
	switch (offset)
	{
		case 0x01201/2:
			return 0x0002;

		case 0x81201/2:
			return 0x0040;
	}

	return 0;
}

void cps_state::init_sf2rb()
{
	m_maincpu->space(AS_PROGRAM).install_read_handler(0x200000, 0x2fffff, read16sm_delegate(*this, FUNC(cps_state::sf2rb_prot_r)));
}

uint16_t cps_state::sf2rb2_prot_r(offs_t offset)
{
	switch (offset)
	{
		case 0x01201/2:
			return 0x0000;

		case 0x81201/2:
			return 0x0040;
	}

	return 0;
}

void cps_state::init_sf2rb2()
{
	m_maincpu->space(AS_PROGRAM).install_read_handler(0x200000, 0x2fffff, read16sm_delegate(*this, FUNC(cps_state::sf2rb2_prot_r)));
}

void cps_state::init_sf2ee()
{
	/* This specific revision of SF2 has the CPS-B custom mapped at a different address. */
	/* The mapping is handled by the PAL IOB2 on the B-board */
	m_maincpu->space(AS_PROGRAM).unmap_readwrite(0x800140, 0x80017f);
	m_maincpu->space(AS_PROGRAM).install_read_handler(0x8001c0, 0x8001ff, read16sm_delegate(*this, FUNC(cps_state::cps1_cps_b_r)));
	m_maincpu->space(AS_PROGRAM).install_write_handler(0x8001c0, 0x8001ff, write16s_delegate(*this, FUNC(cps_state::cps1_cps_b_w)));
}

void cps_state::init_sf2thndr()
{
	/* This particular hack uses a modified B-board PAL which mirrors the CPS-B registers at an alternate address */
	m_maincpu->space(AS_PROGRAM).install_read_handler(0x8001c0, 0x8001ff, read16sm_delegate(*this, FUNC(cps_state::cps1_cps_b_r)));
	m_maincpu->space(AS_PROGRAM).install_write_handler(0x8001c0, 0x8001ff, write16s_delegate(*this, FUNC(cps_state::cps1_cps_b_w)));
}

void cps_state::init_sf2hack()
{
	/* some SF2 hacks have some inputs wired to the LSB instead of MSB */
	m_maincpu->space(AS_PROGRAM).install_read_handler(0x800018, 0x80001f, read16sm_delegate(*this, FUNC(cps_state::cps1_hack_dsw_r)));
}

void cps_state::init_sf2rk()
{
	init_sf2hack();

	uint8_t *gfx = memregion("gfx")->base();

	// descramble the GFX ROMs
	for (int i = 0; i < 0x400000; i++)
	{
		uint8_t x = gfx[i];
		gfx[i] = bitswap(x, 0, 6 ,5, 4, 3, 2, 1, 7);
	}

	// the 0x400000 - 0x480000 range is covered by the extra GFX layer ROMs, which aren't scrambled

	for (int i = 0x480000; i < 0x600000; i++)
	{
		uint8_t x = gfx[i];
		gfx[i] = bitswap(x, 0, 6 ,5, 4, 3, 2, 1, 7);
	}
}

uint16_t cps_state::sf2dongb_prot_r(offs_t offset)
{
	switch (offset)
	{
		case 0x00000/2: // (ret & 0x0f00) == 0x0200
		case 0x77040/2: // (ret & 0x0ff0) == 0x0210
			return 0x0210;
	}

	return 0;
}

void cps_state::init_sf2dongb()
{
	// There is a hacked up Altera EP910PC-30 DIP in the 5f socket instead of a 4th EPROM
	m_maincpu->space(AS_PROGRAM).install_read_handler(0x180000, 0x1fffff, read16sm_delegate(*this, FUNC(cps_state::sf2dongb_prot_r)));
}

uint16_t cps_state::sf2ceblp_prot_r()
{
	if (m_sf2ceblp_prot == 0x0)
		return 0x1992;
	if (m_sf2ceblp_prot == 0x04)
		return 0x0408;
	return 0xffff;
}

void cps_state::sf2ceblp_prot_w(uint16_t data)
{
	m_sf2ceblp_prot = data;
}


void cps_state::init_sf2ceblp()
{
	m_maincpu->space(AS_PROGRAM).install_write_handler(0x5762b0, 0x5762b1, write16smo_delegate(*this, FUNC(cps_state::sf2ceblp_prot_w)));
	m_maincpu->space(AS_PROGRAM).install_read_handler(0x57A2b0, 0x57A2b1, read16smo_delegate(*this, FUNC(cps_state::sf2ceblp_prot_r)));
}

void cps_state::init_sf2m8()
{
	// unscramble gfx
	uint8_t *grom = memregion("gfx")->base();
	uint8_t *urom = memregion("user2")->base();
	int i = 0x480000, j = 0;

	for (j = 0x20000; j < 0x80000; j+=2)
	{
		grom[i++] = urom[j];
		grom[i++] = urom[j|0x100000];
		grom[i++] = urom[j|0x000001];
		grom[i++] = urom[j|0x100001];
		grom[i++] = urom[j|0x080000];
		grom[i++] = urom[j|0x180000];
		grom[i++] = urom[j|0x080001];
		grom[i++] = urom[j|0x180001];
	}
}

void cps_state::kabuki_setup(void (*decode)(uint8_t *src, uint8_t *dst))
{
	m_decrypt_kabuki = std::make_unique<uint8_t[]>(0x8000);
	uint8_t *rom = memregion("audiocpu")->base();
	decode(rom, m_decrypt_kabuki.get());
	membank("decrypted")->set_base(m_decrypt_kabuki.get());
}

void cps_state::init_wof()
{
	kabuki_setup(wof_decode);
}

void cps_state::init_dino()
{
	kabuki_setup(dino_decode);
}

void cps_state::init_punisher()
{
	kabuki_setup(punisher_decode);
}

void cps_state::init_slammast()
{
	kabuki_setup(slammast_decode);
}

void cps_state::init_pang3b()
{
	/* Pang 3 is the only non-QSound game to have an EEPROM. */
	/* It is mapped in the CPS-B address range and is on the B-board. */
	/* The Mach215 security chip outputs 2 control signals (pins 4, 6) which switch the eeprom in/out serial data lines onto the main 68k data bus when required */
	m_maincpu->space(AS_PROGRAM).install_readwrite_port(0x80017a, 0x80017b, "EEPROMIN", "EEPROMOUT");
}

void cps_state::init_pang3()
{
	// an AMD or Lattice Mach215 pld chip performs the decryption (and controls eeprom).
	// it needs to be "kicked" (by reading port 80017a) every vblank else it seems to stop decrypting code.
	// encryption is switched on/off by pin 37.
	// encrypted code range is controlled by PAL CP1B9K which sets pin 37 such that: 00000-7ffff unencrypted, >=80000 encrypted.
	// the mach215 responds to R/W 800174-80017B enabled by pin 36, range set by PAL CP1B8K.

	uint16_t *rom = (uint16_t *)memregion("maincpu")->base();
	int A, src, dst;

	for (A = 0x80000; A < 0x100000; A += 2)
	{
		/* only the low 8 bits of each word are encrypted */
		src = rom[A / 2];
		dst = src & 0xff00;
		if ( src & 0x01) dst ^= 0x04;
		if ( src & 0x02) dst ^= 0x21;
		if ( src & 0x04) dst ^= 0x01;
		if (~src & 0x08) dst ^= 0x50;
		if ( src & 0x10) dst ^= 0x40;
		if ( src & 0x20) dst ^= 0x06;
		if ( src & 0x40) dst ^= 0x08;
		if (~src & 0x80) dst ^= 0x88;
		rom[A/2] = dst;
	}

	init_pang3b();
}

void cps_state::init_cps1mult()
{
	uint8_t *rom = memregion("maincpu")->base();
	int rom_size = memregion("maincpu")->bytes();
	std::vector<uint8_t> buffer(rom_size);
	memcpy(&buffer[0], rom, rom_size);

	for (int i = 0; i < rom_size; i++)
		rom[i] = buffer[bitswap<28>(i, 27, 26, 18, 23, 16, 17, 21, 24, 19, 25, 22, 20, 5, 6, 7, 8, 13, 15, 14, 9, 10, 12, 11, 1, 2, 3, 4, 0)];

	uint8_t *gfxrom = memregion("gfx")->base();
	rom_size = memregion("gfx")->bytes();
	std::vector<uint8_t> gfxbuffer(rom_size);
	memcpy(&gfxbuffer[0], gfxrom, rom_size);
	for (int i = 0; i < rom_size; i++)
		gfxrom[i] = gfxbuffer[bitswap<28>(i, 27, 1, 20, 21, 18, 19, 23, 25, 2, 26, 24, 22, 7, 8, 9, 10, 15, 17, 16, 11, 12, 14, 13, 3, 4, 5, 6, 0)]; // TODO: 25 and 26 to be verified
}

/*
Pang 3b4 - code access to $5762b0 and $57a2b0 (PIC)

--------- Dip switch decoding ---------
0E001A: move.b  $80001c.l, D0    ; !Dip switch B
0E0020: not.b   D0               ; Dip switch B
0E0022: move.b  D0, D1           ; D1 = D0  - Save DSWB for later use
0E0024: andi.b  #$7, D0          ; D0 = D0 & $7 - first 3 bits - Game level -- 7<= D0 <= 0
0E0028: move.w  D0, $5762b0.l    ; [$5762b0] = D0
0E002E: nop                      ;
0E0030: move.w  $57a2b0.l, D2    ; D2 = [$57a2b0]
0E0036: move.b  D1, D0           ; D0 = D1 - restore value read from DSWB
0E0038: andi.w  #$60, D0         ; D0 = D0 & $60 - bit 5, 6

--------- Copy protection ? ---------
000300: move.w  #$17, $5762b0.l  ; [5762B0] = 17 (Pic?)
000308: nop                      ;
00030A: move.w  #$3, D2          ; D2 = 3
00030E: move.w  $57a2b0.l, D0    ; D0 = [$57A2B0]
000314: cmpi.w  #$7321, D0       ; DO <= $7321 (not used?)
000318: bra     $360             ; jmp 360
; unused code
00031A: dbra    D2, $30e         ; loop until D2=0 to $30E
00031E: move.l  #$0, $ff10fa.l   ; [$ff10fa] = 0 ; reset values initialized at 238F0-23922
000328: move.l  #$0, $ff113a.l   ; [$ff113a] = 0 ; reset values initialized at 238F0-23922
000332: move.l  #$0, $ff10fe.l   ; [$ff10fe] = 0 ; reset values initialized at 238F0-23922
00033C: move.l  #$0, $ff113e.l   ; [$ff113e] = 0 ; reset values initialized at 238F0-23922
000346: move.l  #$0, $ff10d2.l   ; [$ff10d2] = 0 ; reset values initialized at 238F0-23922
000350: move.l  #$0, $ff1112.l   ; [$ff1112] = 0 ; reset values initialized at 238F0-23922
00035A: jmp     $20acc.l         ; jmp $20ACC
; unused code [END]
000360: jmp     $e0000.l         ; jmp $E0000
*/
uint16_t cps_state::pang3b4_prot_r()
{
	if ((m_pang3b4_prot & 0xff) <= 7)
		return (m_pang3b4_prot & 0xff) + 0x20;  // Game level + extend
	if (m_pang3b4_prot == 0x17)
		return 0x7321;                      // Guessed from code @0x314
	return 0xffff;
}

void cps_state::pang3b4_prot_w(uint16_t data)
{
	m_pang3b4_prot = data;
}


void cps_state::init_pang3b4()
{
	m_maincpu->space(AS_PROGRAM).install_write_handler(0x5762b0, 0x5762b1, write16smo_delegate(*this, FUNC(cps_state::pang3b4_prot_w)));
	m_maincpu->space(AS_PROGRAM).install_read_handler(0x57a2b0, 0x57a2b1, read16smo_delegate(*this, FUNC(cps_state::pang3b4_prot_r)));

	/* In pang3 the Mach215 security chip outputs 2 control signals (pins 4, 6) which switch the eeprom in/out serial data lines onto the main 68k data bus when required
	   They're mapped in the CPS-B address range but there is not the EPROM on the board
	   Read and write to port 0x80017a still are present in the code, but they are filtered by the PAL16V8 @ E13 */
	m_maincpu->space(AS_PROGRAM).nop_readwrite(0x80017a, 0x80017b);
}


uint16_t cps_state::ganbare_ram_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t result = 0xffff;

	if (ACCESSING_BITS_0_7)
		result = (result & ~0x00ff) | m_m48t35->read(offset);
	if (ACCESSING_BITS_8_15)
		result = (result & ~0xff00) | (m_mainram[offset] & 0xff00);

	return result;
}

void cps_state::ganbare_ram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	COMBINE_DATA(&m_mainram[offset]);

	if (ACCESSING_BITS_0_7)
		m_m48t35->write(offset, data & 0xff);
}

void cps_state::init_rasters()
{
	save_item(NAME(m_raster_counter));
	save_item(NAME(m_raster_reload));

	m_raster_reload[0] = m_raster_reload[1] = 0x1ff;

	m_raster_irq = timer_alloc(FUNC(cps_state::raster_irq), this);
}

void cps_state::init_ganbare()
{
	init_rasters();

	/* ram is shared between the CPS work ram and the timekeeper ram */
	m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0xff0000, 0xffffff, read16s_delegate(*this, FUNC(cps_state::ganbare_ram_r)), write16s_delegate(*this, FUNC(cps_state::ganbare_ram_w)));
}

uint16_t cps_state::dinohunt_sound_r()
{
	/*TODO: understand what's really going on here. According to MT05805;
	"I think that the values written are only qsound leftovers (after a lot of 0xFF values,
	there is the same qsound starting sequence, eg: 0x88, 0xFF, 0x0B, 0x00, 0x00, 0x00, ...)."*/
	return 0xff;
}

void cps_state::init_dinohunt()
{
	// is this shared with the new sound hw?
	m_maincpu->space(AS_PROGRAM).install_read_handler(0xf18000, 0xf19fff, read16smo_delegate(*this, FUNC(cps_state::dinohunt_sound_r)));
	m_maincpu->space(AS_PROGRAM).install_read_port(0xfc0000, 0xfc0001, "IN2");
	// the ym2151 doesn't seem to be used. Is it actually on the PCB?
}

void cps_state::sf2m3_layer_w(offs_t offset, uint16_t data)
{
	cps1_cps_b_w(0x0a,data);
}

void cps_state::varthb2_cps_a_w(offs_t offset, uint16_t data)
{
	// cps-a regs are updated as normal by original code,
	// but bootleg code ignores them and uses these regions instead:
	if (offset == 0x00 / 2)
	{
		m_cps_a_regs[offset] = 0x9100;
		return;
	}
	if (offset == 0x02 / 2)
	{
		m_cps_a_regs[offset] = 0x90c0;
		return;
	}
	if (offset == 0x04 / 2)
	{
		m_cps_a_regs[offset] = 0x9040;
		return;
	}
	if (offset == 0x06 / 2)
	{
		m_cps_a_regs[offset] = 0x9080;
		return;
	}
	if (offset == 0x0a / 2)
	{
		m_cps_a_regs[offset] = 0x9000;
		return;
	}

	m_cps_a_regs[offset] = data;
}


/*
    A note regarding bootlegs:
    In order to keep the cps source in some sort of order, the idea is to group similar bootleg hardware into separate
    derived classes and source files.

    Rom swaps, hacks etc.  (on original Capcom hardware)  ->  cps1.cpp
    Sound: Z80, 2x YM2203, 2x m5205 ("Final Crash" h/w)   ->  fcrash.cpp
    Sound: Z80, 1x YM2151, 2x m5205                       ->  cps1bl_5205.cpp
    Sound: PIC, 1x M6295            *1                    ->  cps1bl_pic.cpp
    Sound: Z80, 1x YM2151, 1x M6295 *2                    ->  fcrash.cpp      (for now...)

    *1 these seem to be only CPS1.5/Q sound games?
    *2 this is original configuration, but non-Capcom (usually single-board) hardware.


    This file currently contains games in first and last categories.
    Eventually only official/genuine/non-bootleg Capcom-hardware games and those in first category will remain here.
*/


/*************************************************** Game Macros *****************************************************/

GAME( 1988, forgottn,    0,        forgottn,   forgottn,   cps_state, empty_init,    ROT0,   "Capcom", "Forgotten Worlds (World, newer)", MACHINE_SUPPORTS_SAVE )  // (c) Capcom U.S.A. but World "warning"
GAME( 1988, forgottna,   forgottn, forgottn,   forgottn,   cps_state, empty_init,    ROT0,   "Capcom", "Forgotten Worlds (World)", MACHINE_SUPPORTS_SAVE )  // (c) Capcom U.S.A. but World "warning"
GAME( 1988, forgottnu,   forgottn, forgottn,   forgottn,   cps_state, empty_init,    ROT0,   "Capcom", "Forgotten Worlds (USA, B-Board 88621B-2, Rev. C)", MACHINE_SUPPORTS_SAVE )
GAME( 1988, forgottnue,  forgottn, forgottn,   forgottn,   cps_state, empty_init,    ROT0,   "Capcom", "Forgotten Worlds (USA, B-Board 88618B-2, Rev. E)", MACHINE_SUPPORTS_SAVE )
GAME( 1988, forgottnuc,  forgottn, forgottn,   forgottn,   cps_state, empty_init,    ROT0,   "Capcom", "Forgotten Worlds (USA, B-Board 88618B-2, Rev. C)", MACHINE_SUPPORTS_SAVE )
GAME( 1988, forgottnua,  forgottn, forgottn,   forgottn,   cps_state, empty_init,    ROT0,   "Capcom", "Forgotten Worlds (USA, B-Board 88618B-2, Rev. A)", MACHINE_SUPPORTS_SAVE )
GAME( 1988, forgottnuaa, forgottn, forgottn,   forgottn,   cps_state, empty_init,    ROT0,   "Capcom", "Forgotten Worlds (USA, B-Board 88618B-2, Rev. AA)", MACHINE_SUPPORTS_SAVE )
GAME( 1988, forgottnj,   forgottn, forgottn,   forgottnj,  cps_state, empty_init,    ROT0,   "Capcom", "Forgotten Worlds (Japan) (English prototype)", MACHINE_SUPPORTS_SAVE ) // shows a Japan warning but runs an English localized version of the game, many changes, unfinished levels, ends abruptly.
GAME( 1988, lostwrld,    forgottn, forgottn,   forgottn,   cps_state, empty_init,    ROT0,   "Capcom", "Lost Worlds (Japan)", MACHINE_SUPPORTS_SAVE )
GAME( 1988, lostwrldo,   forgottn, forgottn,   forgottn,   cps_state, empty_init,    ROT0,   "Capcom", "Lost Worlds (Japan Old Ver.)", MACHINE_SUPPORTS_SAVE )
GAME( 1988, ghouls,      0,        cps1_10MHz, ghouls,     cps_state, empty_init,    ROT0,   "Capcom", "Ghouls'n Ghosts (World)", MACHINE_SUPPORTS_SAVE )   // "EXPORT" // Wed.26.10.1988 in the ROMs
GAME( 1988, ghoulsu,     ghouls,   cps1_10MHz, ghoulsu,    cps_state, empty_init,    ROT0,   "Capcom", "Ghouls'n Ghosts (USA)", MACHINE_SUPPORTS_SAVE ) // "EXPORT" // Wed.26.10.1988 in the ROMs
GAME( 1988, daimakai,    ghouls,   cps1_10MHz, daimakai,   cps_state, empty_init,    ROT0,   "Capcom", "Daimakaimura (Japan)", MACHINE_SUPPORTS_SAVE )              // Wed.26.10.1988 in the ROMs
GAME( 1988, daimakair,   ghouls,   cps1_12MHz, daimakair,  cps_state, empty_init,    ROT0,   "Capcom", "Daimakaimura (Japan Resale Ver.)", MACHINE_SUPPORTS_SAVE )      // Wed.26.10.1988 in the ROMs   // 12MHz verified
GAME( 1989, strider,     0,        cps1_10MHz, strider,    cps_state, empty_init,    ROT0,   "Capcom", "Strider (USA, B-Board 89624B-2)", MACHINE_SUPPORTS_SAVE )
GAME( 1989, striderua,   strider,  cps1_10MHz, stridrua,   cps_state, empty_init,    ROT0,   "Capcom", "Strider (USA, B-Board 89624B-3)", MACHINE_SUPPORTS_SAVE )
GAME( 1989, strideruc,   strider,  cps1_10MHz, stridrua,   cps_state, empty_init,    ROT0,   "bootleg (Capcom)", "Strider (USA, B-Board 90629B-3, buggy Street Fighter II conversion)", MACHINE_SUPPORTS_SAVE ) // various bugs even on PCB, see ROM load
GAME( 1989, striderj,    strider,  cps1_10MHz, strider,    cps_state, empty_init,    ROT0,   "Capcom", "Strider Hiryu (Japan)", MACHINE_SUPPORTS_SAVE )
GAME( 1989, striderjr,   strider,  cps1_12MHz, strider,    cps_state, empty_init,    ROT0,   "Capcom", "Strider Hiryu (Japan Resale Ver.)", MACHINE_SUPPORTS_SAVE ) // 12MHz verified
GAME( 1989, dynwar,      0,        cps1_10MHz, dynwar,     cps_state, empty_init,    ROT0,   "Capcom", "Dynasty Wars (USA, B-Board 89624B-?)", MACHINE_SUPPORTS_SAVE )  // (c) Capcom U.S.A.
GAME( 1989, dynwara,     dynwar,   cps1_10MHz, dynwar,     cps_state, empty_init,    ROT0,   "Capcom", "Dynasty Wars (USA, B-Board 88622B-3)", MACHINE_SUPPORTS_SAVE )  // (c) Capcom U.S.A.
GAME( 1989, dynwarj,     dynwar,   cps1_10MHz, dynwar,     cps_state, empty_init,    ROT0,   "Capcom", "Tenchi wo Kurau (Japan)", MACHINE_SUPPORTS_SAVE )
GAME( 1989, dynwarjr,    dynwar,   cps1_12MHz, dynwar,     cps_state, empty_init,    ROT0,   "Capcom", "Tenchi wo Kurau (Japan Resale Ver.)", MACHINE_SUPPORTS_SAVE )   // 12MHz verified
GAME( 1989, willow,      0,        cps1_10MHz, willow,     cps_state, empty_init,    ROT0,   "Capcom", "Willow (World)", MACHINE_SUPPORTS_SAVE ) // No "Warning" (c) Capcom U.S.A., genuine export ROM labels
GAME( 1989, willowu,     willow,   cps1_10MHz, willow,     cps_state, empty_init,    ROT0,   "Capcom", "Willow (USA)", MACHINE_SUPPORTS_SAVE )
GAME( 1989, willowuo,    willow,   cps1_10MHz, willow,     cps_state, empty_init,    ROT0,   "Capcom", "Willow (USA Old Ver.)", MACHINE_SUPPORTS_SAVE ) // Japan "warning" but (c) Capcom U.S.A.
GAME( 1989, willowj,     willow,   cps1_10MHz, willow,     cps_state, empty_init,    ROT0,   "Capcom", "Willow (Japan)", MACHINE_SUPPORTS_SAVE )
GAME( 1989, unsquad,     0,        cps1_10MHz, unsquad,    cps_state, empty_init,    ROT0,   "Capcom", "U.N. Squadron (USA)", MACHINE_SUPPORTS_SAVE )
GAME( 1989, area88,      unsquad,  cps1_10MHz, unsquad,    cps_state, empty_init,    ROT0,   "Capcom", "Area 88 (Japan)", MACHINE_SUPPORTS_SAVE ) // note: Daipro was the copyright holder of Area 88 manga
GAME( 1989, area88r,     unsquad,  cps1_12MHz, unsquad,    cps_state, empty_init,    ROT0,   "Capcom", "Area 88 (Japan Resale Ver.)", MACHINE_SUPPORTS_SAVE )  // 12MHz verified
GAME( 1989, ffight,      0,        cps1_10MHz, ffight,     cps_state, empty_init,    ROT0,   "Capcom", "Final Fight (World, set 1)", MACHINE_SUPPORTS_SAVE )
GAME( 1989, ffighta,     ffight,   cps1_10MHz, ffight,     cps_state, empty_init,    ROT0,   "Capcom", "Final Fight (World, set 2)", MACHINE_SUPPORTS_SAVE )
GAME( 1989, ffightu,     ffight,   cps1_10MHz, ffight,     cps_state, empty_init,    ROT0,   "Capcom", "Final Fight (USA, set 1)", MACHINE_SUPPORTS_SAVE )
GAME( 1989, ffightu1,    ffight,   cps1_10MHz, ffight,     cps_state, empty_init,    ROT0,   "Capcom", "Final Fight (USA, set 2)", MACHINE_SUPPORTS_SAVE )
GAME( 1989, ffightua,    ffight,   cps1_10MHz, ffight,     cps_state, empty_init,    ROT0,   "Capcom", "Final Fight (USA 900112)", MACHINE_SUPPORTS_SAVE )
GAME( 1989, ffightub,    ffight,   cps1_10MHz, ffight,     cps_state, empty_init,    ROT0,   "Capcom", "Final Fight (USA 900424)", MACHINE_SUPPORTS_SAVE )
GAME( 1989, ffightuc,    ffight,   cps1_10MHz, ffight,     cps_state, empty_init,    ROT0,   "Capcom", "Final Fight (USA 900613)", MACHINE_SUPPORTS_SAVE )
GAME( 1989, ffightj,     ffight,   cps1_10MHz, ffight,     cps_state, empty_init,    ROT0,   "Capcom", "Final Fight (Japan)", MACHINE_SUPPORTS_SAVE )
GAME( 1989, ffightj1,    ffight,   cps1_10MHz, ffight,     cps_state, empty_init,    ROT0,   "Capcom", "Final Fight (Japan 900112)", MACHINE_SUPPORTS_SAVE )
GAME( 1989, ffightj2,    ffight,   cps1_10MHz, ffight,     cps_state, empty_init,    ROT0,   "Capcom", "Final Fight (Japan 900305)", MACHINE_SUPPORTS_SAVE )
GAME( 1989, ffightj3,    ffight,   cps1_10MHz, ffight,     cps_state, empty_init,    ROT0,   "Capcom", "Final Fight (Japan 900405)", MACHINE_SUPPORTS_SAVE )
GAME( 1989, ffightj4,    ffight,   cps1_10MHz, ffight,     cps_state, empty_init,    ROT0,   "Capcom", "Final Fight (Japan 900613)", MACHINE_SUPPORTS_SAVE )
GAME( 1989, ffightjh,    ffight,   cps1_10MHz, ffight,     cps_state, empty_init,    ROT0,   "bootleg", "Street Smart / Final Fight (Japan, hack)", MACHINE_SUPPORTS_SAVE )
GAME( 2019, ffightae,    ffight,   cps1_12MHz, ffightae,   cps_state, empty_init,    ROT0,   "hack",   "Final Fight 30th Anniversary Edition (World, hack)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, 1941,        0,        cps1_10MHz, 1941,       cps_state, empty_init,    ROT270, "Capcom", "1941: Counter Attack (World 900227)", MACHINE_SUPPORTS_SAVE )   // "ETC"
GAME( 1990, 1941r1,      1941,     cps1_10MHz, 1941,       cps_state, empty_init,    ROT270, "Capcom", "1941: Counter Attack (World)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, 1941u,       1941,     cps1_10MHz, 1941,       cps_state, empty_init,    ROT270, "Capcom", "1941: Counter Attack (USA 900227)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, 1941j,       1941,     cps1_10MHz, 1941,       cps_state, empty_init,    ROT270, "Capcom", "1941: Counter Attack (Japan)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, mercs,       0,        cps1_10MHz, mercs,      cps_state, empty_init,    ROT270, "Capcom", "Mercs (World 900302)", MACHINE_SUPPORTS_SAVE )  // "ETC"
GAME( 1990, mercsu,      mercs,    cps1_10MHz, mercs,      cps_state, empty_init,    ROT270, "Capcom", "Mercs (USA 900608)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, mercsur1,    mercs,    cps1_10MHz, mercs,      cps_state, empty_init,    ROT270, "Capcom", "Mercs (USA 900302)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, mercsj,      mercs,    cps1_10MHz, mercs,      cps_state, empty_init,    ROT270, "Capcom", "Senjou no Ookami II (Japan 900302)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, mtwins,      0,        cps1_10MHz, mtwins,     cps_state, empty_init,    ROT0,   "Capcom", "Mega Twins (World 900619)", MACHINE_SUPPORTS_SAVE ) // "ETC" - (c) Capcom U.S.A. but World "warning"
GAME( 1990, chikij,      mtwins,   cps1_10MHz, mtwins,     cps_state, empty_init,    ROT0,   "Capcom", "Chiki Chiki Boys (Japan 900619)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, msword,      0,        cps1_10MHz, msword,     cps_state, empty_init,    ROT0,   "Capcom", "Magic Sword: Heroic Fantasy (World 900725)", MACHINE_SUPPORTS_SAVE )    // 25.07.1990  "OTHER COUNTRY"
GAME( 1990, mswordr1,    msword,   cps1_10MHz, msword,     cps_state, empty_init,    ROT0,   "Capcom", "Magic Sword: Heroic Fantasy (World 900623)", MACHINE_SUPPORTS_SAVE )    // 23.06.1990  "OTHER COUNTRY"
GAME( 1990, mswordu,     msword,   cps1_10MHz, msword,     cps_state, empty_init,    ROT0,   "Capcom", "Magic Sword: Heroic Fantasy (USA 900725)", MACHINE_SUPPORTS_SAVE )  // 25.07.1990  "U.S.A."
GAME( 1990, mswordj,     msword,   cps1_10MHz, msword,     cps_state, empty_init,    ROT0,   "Capcom", "Magic Sword: Heroic Fantasy (Japan 900623)", MACHINE_SUPPORTS_SAVE )    // 23.06.1990  "JAPAN"
GAME( 1990, cawing,      0,        cps1_10MHz, cawing,     cps_state, empty_init,    ROT0,   "Capcom", "Carrier Air Wing (World 901012)", MACHINE_SUPPORTS_SAVE )   // "ETC"
GAME( 1990, cawingr1,    cawing,   cps1_10MHz, cawing,     cps_state, empty_init,    ROT0,   "Capcom", "Carrier Air Wing (World 901009)", MACHINE_SUPPORTS_SAVE )   // "ETC"
GAME( 1990, cawingu,     cawing,   cps1_10MHz, cawing,     cps_state, empty_init,    ROT0,   "Capcom", "Carrier Air Wing (USA 901130)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, cawingur1,   cawing,   cps1_10MHz, cawing,     cps_state, empty_init,    ROT0,   "Capcom", "Carrier Air Wing (USA 901012)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, cawingj,     cawing,   cps1_10MHz, cawing,     cps_state, empty_init,    ROT0,   "Capcom", "U.S. Navy (Japan 901012)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, nemo,        0,        cps1_10MHz, nemo,       cps_state, empty_init,    ROT0,   "Capcom", "Nemo (World 901130)", MACHINE_SUPPORTS_SAVE )   // "ETC"
GAME( 1990, nemor1,      nemo,     cps1_10MHz, nemo,       cps_state, empty_init,    ROT0,   "Capcom", "Nemo (World 901109)", MACHINE_SUPPORTS_SAVE )   // "ETC"
GAME( 1990, nemoj,       nemo,     cps1_10MHz, nemo,       cps_state, empty_init,    ROT0,   "Capcom", "Nemo (Japan 901120, 88622B-3 ROM board)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, nemoja,      nemo,     cps1_10MHz, nemo,       cps_state, empty_init,    ROT0,   "Capcom", "Nemo (Japan 901120, 89625B-1 ROM board)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, sf2,         0,        cps1_10MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (World 910522)", MACHINE_SUPPORTS_SAVE )   // "ETC"
GAME( 1991, sf2ea,       sf2,      cps1_10MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (World 910204)", MACHINE_SUPPORTS_SAVE )   // "ETC"
GAME( 1991, sf2eb,       sf2,      cps1_10MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (World 910214)", MACHINE_SUPPORTS_SAVE )   // "ETC"
GAME( 1991, sf2ed,       sf2,      cps1_10MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (World 910318)", MACHINE_SUPPORTS_SAVE )   // "ETC"
GAME( 1991, sf2ee,       sf2,      cps1_10MHz, sf2,        cps_state, init_sf2ee,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (World 910228)", MACHINE_SUPPORTS_SAVE )   // "ETC"
GAME( 1991, sf2ef,       sf2,      cps1_10MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (World 910411)", MACHINE_SUPPORTS_SAVE )   // "ETC"
GAME( 1991, sf2em,       sf2,      cps1_10MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (World 910129)", MACHINE_SUPPORTS_SAVE )   // "ETC"
GAME( 1991, sf2en,       sf2,      cps1_10MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (World 910204, conversion)", MACHINE_SUPPORTS_SAVE )   // "ETC"
GAME( 1991, sf2ua,       sf2,      cps1_10MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (USA 910206)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, sf2ub,       sf2,      cps1_10MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (USA 910214)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, sf2uc,       sf2,      cps1_10MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (USA 910306)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, sf2ud,       sf2,      cps1_10MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (USA 910318)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, sf2ue,       sf2,      cps1_10MHz, sf2,        cps_state, init_sf2ee,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (USA 910228)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, sf2uf,       sf2,      cps1_10MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (USA 910411)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, sf2ug,       sf2,      cps1_10MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (USA 910522, Rev. G)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, sf2uh,       sf2,      cps1_10MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (USA 910522, Rev. H)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, sf2ui,       sf2,      cps1_10MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (USA 910522, Rev. I)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, sf2uk,       sf2,      cps1_10MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (USA 911101)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, sf2um,       sf2,      cps1_10MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (USA 920312)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, sf2j,        sf2,      cps1_10MHz, sf2j,       cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (Japan 911210, CPS-B-13)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, sf2j17,      sf2,      cps1_10MHz, sf2j,       cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (Japan 911210, CPS-B-17)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, sf2ja,       sf2,      cps1_10MHz, sf2j,       cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (Japan 910214)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, sf2jc,       sf2,      cps1_10MHz, sf2j,       cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (Japan 910306)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, sf2jf,       sf2,      cps1_10MHz, sf2j,       cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (Japan 910411)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, sf2jh,       sf2,      cps1_10MHz, sf2j,       cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (Japan 910522)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, sf2jl,       sf2,      cps1_10MHz, sf2j,       cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II: The World Warrior (Japan 920312)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, sf2ebbl,     sf2,      cps1_10MHz, sf2hack,    cps_state, init_sf2hack,  ROT0,   "bootleg", "Street Fighter II: The World Warrior (TAB Austria, bootleg, set 1)", MACHINE_SUPPORTS_SAVE )       // 910214 - based on World version
GAME( 1992, sf2ebbl2,    sf2,      cps1_10MHz, sf2hack,    cps_state, init_sf2hack,  ROT0,   "bootleg", "Street Fighter II: The World Warrior (TAB Austria, bootleg, set 3)", MACHINE_SUPPORTS_SAVE )       // 910214 - based on World version
GAME( 1992, sf2ebbl3,    sf2,      cps1_10MHz, sf2hack,    cps_state, init_sf2hack,  ROT0,   "bootleg", "Street Fighter II: The World Warrior (TAB Austria, bootleg, set 4)", MACHINE_SUPPORTS_SAVE )       // 910214 - based on World version
GAME( 1992, sf2stt,      sf2,      cps1_10MHz, sf2hack,    cps_state, init_sf2hack,  ROT0,   "bootleg", "Street Fighter II: The World Warrior (TAB Austria, bootleg, set 2)", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )    // 910214 - based on World version
GAME( 1992, sf2rk,       sf2,      cps1_10MHz, sf2hack,    cps_state, init_sf2rk,    ROT0,   "bootleg", "Street Fighter II: The World Warrior (RK, bootleg)", MACHINE_SUPPORTS_SAVE )               // 910214 - based on World version, dark screen issue confirmed present on real pcb
GAME( 1991, sf2qp1,      sf2,      cps1_10MHz, sf2,        cps_state, empty_init,    ROT0,   "bootleg", "Street Fighter II: The World Warrior (Quicken Pt-I, bootleg)", MACHINE_SUPPORTS_SAVE )     // 910214 - based on World version
GAME( 1991, sf2qp2,      sf2,      cps1_10MHz, sf2,        cps_state, empty_init,    ROT0,   "bootleg", "Street Fighter II: The World Warrior (Quicken, bootleg)", MACHINE_SUPPORTS_SAVE )          // 910522 - based on USA Rev.I? version
GAME( 1991, sf2thndr,    sf2,      cps1_10MHz, sf2,        cps_state, init_sf2thndr, ROT0,   "bootleg", "Street Fighter II: The World Warrior (Thunder Edition, bootleg, set 1)", MACHINE_SUPPORTS_SAVE )  // 910214 - based on World version
GAME( 1991, sf2thndr2,   sf2,      cps1_10MHz, sf2,        cps_state, init_sf2thndr, ROT0,   "bootleg", "Street Fighter II: The World Warrior (Thunder Edition, bootleg, set 2)", MACHINE_SUPPORTS_SAVE )  // 910214 - based on World version
GAME( 1992, sf2rules,    sf2,      cps1_10MHz, sf2hack,    cps_state, init_sf2hack,  ROT0,   "bootleg", "Street Fighter II: The World Warrior (bootleg with rules screen)", MACHINE_SUPPORTS_SAVE )     // 910214 - based on World version, shows the rules of the game instead of the warning screen
GAME( 1991, 3wonders,    0,        cps1_10MHz, 3wonders,   cps_state, empty_init,    ROT0,   "Capcom", "Three Wonders (World 910520)", MACHINE_SUPPORTS_SAVE )  // "ETC"
GAME( 1991, 3wondersr1,  3wonders, cps1_10MHz, 3wonders,   cps_state, empty_init,    ROT0,   "Capcom", "Three Wonders (World 910513)", MACHINE_SUPPORTS_SAVE )  // "ETC"
GAME( 1991, 3wondersu,   3wonders, cps1_10MHz, 3wonders,   cps_state, empty_init,    ROT0,   "Capcom", "Three Wonders (USA 910520)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, wonder3,     3wonders, cps1_10MHz, 3wonders,   cps_state, empty_init,    ROT0,   "Capcom", "Wonder 3 (Japan 910520)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, 3wondersb,   3wonders, cps1_10MHz, 3wonders,   cps_state, empty_init,    ROT0,   "bootleg", "Three Wonders (bootleg)", MACHINE_SUPPORTS_SAVE )   // 910520 - based on World version
GAME( 1991, 3wondersbi,  3wonders, cps1_10MHz, 3wonders,   cps_state, empty_init,    ROT0,   "bootleg (Electronic Devices)", "Three Wonders (Italian bootleg)", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )   // 910520 - based on World version
GAME( 1991, 3wondersh,   3wonders, cps1_10MHz, 3wonders,   cps_state, empty_init,    ROT0,   "bootleg", "Three Wonders (hack)", MACHINE_SUPPORTS_SAVE ) // 910520 - based on World version
GAME( 1991, kod,         0,        cps1_10MHz, kod,        cps_state, empty_init,    ROT0,   "Capcom", "The King of Dragons (World 910805)", MACHINE_SUPPORTS_SAVE )    // "ETC"
GAME( 1991, kodr1,       kod,      cps1_10MHz, kodr1,      cps_state, empty_init,    ROT0,   "Capcom", "The King of Dragons (World 910711)", MACHINE_SUPPORTS_SAVE )    // "ETC"
GAME( 1991, kodr2,       kod,      cps1_10MHz, kodr1,      cps_state, empty_init,    ROT0,   "Capcom", "The King of Dragons (World 910731)", MACHINE_SUPPORTS_SAVE )    // "ETC"
GAME( 1991, kodu,        kod,      cps1_10MHz, kod,        cps_state, empty_init,    ROT0,   "Capcom", "The King of Dragons (USA 910910)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, kodj,        kod,      cps1_10MHz, kod,        cps_state, empty_init,    ROT0,   "Capcom", "The King of Dragons (Japan 910805, B-Board 90629B-3)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, kodja,       kod,      cps1_10MHz, kod,        cps_state, empty_init,    ROT0,   "Capcom", "The King of Dragons (Japan 910805, B-Board 89625B-1)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, captcomm,    0,        cps1_10MHz, captcomm,   cps_state, empty_init,    ROT0,   "Capcom", "Captain Commando (World 911202)", MACHINE_SUPPORTS_SAVE )   // "OTHER COUNTRY"
GAME( 1991, captcommr1,  captcomm, cps1_10MHz, captcomm,   cps_state, empty_init,    ROT0,   "Capcom", "Captain Commando (World 911014)", MACHINE_SUPPORTS_SAVE )   // "OTHER COUNTRY"
GAME( 1991, captcommu,   captcomm, cps1_10MHz, captcomm,   cps_state, empty_init,    ROT0,   "Capcom", "Captain Commando (USA 910928)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, captcommj,   captcomm, cps1_10MHz, captcomm,   cps_state, empty_init,    ROT0,   "Capcom", "Captain Commando (Japan 911202)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, captcommjr1, captcomm, cps1_10MHz, captcomm,   cps_state, empty_init,    ROT0,   "Capcom", "Captain Commando (Japan 910928)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, captcommb,   captcomm, cps1_10MHz, captcomm,   cps_state, empty_init,    ROT0,   "bootleg", "Captain Commando (bootleg)", MACHINE_SUPPORTS_SAVE )    // 911014 - based on World version
GAME( 1991, knights,     0,        cps1_10MHz, knights,    cps_state, empty_init,    ROT0,   "Capcom", "Knights of the Round (World 911127)", MACHINE_SUPPORTS_SAVE )   // "ETC"
GAME( 1991, knightsu,    knights,  cps1_10MHz, knights,    cps_state, empty_init,    ROT0,   "Capcom", "Knights of the Round (USA 911127)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, knightsj,    knights,  cps1_10MHz, knights,    cps_state, empty_init,    ROT0,   "Capcom", "Knights of the Round (Japan 911127, B-Board 91634B-2)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, knightsja,   knights,  cps1_10MHz, knights,    cps_state, empty_init,    ROT0,   "Capcom", "Knights of the Round (Japan 911127, B-Board 89625B-1)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, knightsb2,   knights,  cps1_10MHz, knights,    cps_state, empty_init,    ROT0,   "bootleg", "Knights of the Round (bootleg, World 911127)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE ) // i.e. player selection screen problems
GAME( 1992, sf2ce,       0,        cps1_12MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II': Champion Edition (World 920513)", MACHINE_SUPPORTS_SAVE )   // "ETC"
GAME( 1992, sf2ceea,     sf2ce,    cps1_12MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II': Champion Edition (World 920313)", MACHINE_SUPPORTS_SAVE )   // "ETC"
GAME( 1992, sf2ceec,     sf2ce,    cps1_12MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II': Champion Edition (World 920803)", MACHINE_SUPPORTS_SAVE )   // "ETC"
GAME( 1992, sf2ceua,     sf2ce,    cps1_12MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II': Champion Edition (USA 920313)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, sf2ceub,     sf2ce,    cps1_12MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II': Champion Edition (USA 920513)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, sf2ceuc,     sf2ce,    cps1_12MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II': Champion Edition (USA 920803)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, sf2cet,      sf2ce,    cps1_12MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II': Champion Edition (Taiwan 920313)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, sf2ceja,     sf2ce,    cps1_12MHz, sf2cej,     cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II': Champion Edition (Japan 920322)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, sf2cejb,     sf2ce,    cps1_12MHz, sf2cej,     cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II': Champion Edition (Japan 920513)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, sf2cejc,     sf2ce,    cps1_12MHz, sf2cej,     cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II': Champion Edition (Japan 920803)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, sf2bhh,      sf2ce,    cps1_12MHz, sf2bhh,     cps_state, init_sf2rb,    ROT0,   "bootleg", "Street Fighter II': Champion Edition (Hung Hsi, bootleg)", MACHINE_SUPPORTS_SAVE ) // derived from sf2cet, was on actual Capcom hw with changed, unlabeled ROMs. Has turbo mode.
GAME( 1992, sf2cebltw,   sf2ce,    cps1_12MHz, sf2bhh,     cps_state, empty_init,    ROT0,   "bootleg", "Street Fighter II': Champion Edition ('Taiwan' bootleg with PAL)", MACHINE_SUPPORTS_SAVE ) // 'Taiwan', similar to sf2bhh but without Hung Hsi copyright
GAME( 1992, sf2rb,       sf2ce,    cps1_12MHz, sf2rb,      cps_state, init_sf2rb,    ROT0,   "bootleg", "Street Fighter II': Champion Edition (Rainbow, bootleg, set 1)", MACHINE_SUPPORTS_SAVE )           // 920322 - based on World version
GAME( 1992, sf2rb2,      sf2ce,    cps1_12MHz, sf2rb,      cps_state, init_sf2rb2,   ROT0,   "bootleg", "Street Fighter II': Champion Edition (Rainbow, bootleg, set 2)", MACHINE_SUPPORTS_SAVE )           // 920322 - based on World version
GAME( 1992, sf2rb3,      sf2ce,    cps1_12MHz, sf2rb,      cps_state, empty_init,    ROT0,   "bootleg", "Street Fighter II': Champion Edition (Rainbow, bootleg, set 3)", MACHINE_SUPPORTS_SAVE )           // 920322 - based on World version
GAME( 1992, sf2red,      sf2ce,    cps1_12MHz, sf2,        cps_state, empty_init,    ROT0,   "bootleg", "Street Fighter II': Champion Edition (Red Wave, bootleg, set 1)", MACHINE_SUPPORTS_SAVE )         // 920313 - based on World version
GAME( 1992, sf2reda,     sf2ce,    cps1_12MHz, sf2,        cps_state, empty_init,    ROT0,   "bootleg", "Street Fighter II': Champion Edition (Red Wave, bootleg, set 2)", MACHINE_SUPPORTS_SAVE )         // 920313 - based on World version
GAME( 1992?,sf2redp2,    sf2ce,    cps1_12MHz, sf2,        cps_state, empty_init,    ROT0,   "bootleg", "Street Fighter II': Champion Edition (Red Wave PtII, bootleg)", MACHINE_SUPPORTS_SAVE )    // 920313 - further modification of sf2red program
GAME( 1992, sf2v004,     sf2ce,    cps1_12MHz, sf2,        cps_state, empty_init,    ROT0,   "bootleg", "Street Fighter II': Champion Edition (V004, bootleg)", MACHINE_SUPPORTS_SAVE )             // 102092 !!! - based on (heavily modified) World version
GAME( 1992, sf2acc,      sf2ce,    cps1_12MHz, sf2,        cps_state, empty_init,    ROT0,   "bootleg", "Street Fighter II': Champion Edition (Accelerator!, bootleg, set 1)", MACHINE_SUPPORTS_SAVE )          // 920313 - based on World version
GAME( 1992, sf2acca,     sf2ce,    cps1_12MHz, sf2,        cps_state, empty_init,    ROT0,   "bootleg", "Street Fighter II': Champion Edition (Accelerator!, bootleg, set 2)", MACHINE_SUPPORTS_SAVE )          // 920313 - based on World version
GAME( 1992, sf2accp2,    sf2ce,    cps1_12MHz, sf2accp2,   cps_state, empty_init,    ROT0,   "bootleg (Testron)", "Street Fighter II': Champion Edition (Accelerator Pt.II, bootleg)", MACHINE_SUPPORTS_SAVE )        // 920313 - based on World version
GAME( 1992, sf2amf,      sf2ce,    cps1_12MHz, sf2amf,     cps_state, init_sf2hack,  ROT0,   "bootleg", "Street Fighter II': Champion Edition (Alpha Magic-F, bootleg)", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )     // 920313 - based on World version
GAME( 1992, sf2amf2,     sf2ce,    cps1_12MHz, sf2amfx,    cps_state, init_sf2hack,  ROT0,   "bootleg", "Street Fighter II': Champion Edition (L735 Test Rom, bootleg, set 1)", MACHINE_SUPPORTS_SAVE )     // 920313 - based on World version
GAME( 1992, sf2amf3,     sf2ce,    cps1_10MHz, sf2amfx,    cps_state, init_sf2hack,  ROT0,   "bootleg", "Street Fighter II': Champion Edition (L735 Test Rom, bootleg, set 2)", MACHINE_SUPPORTS_SAVE )     // 920313 - based on World version, confirmed 10MHz
GAME( 1992, sf2dkot2,    sf2ce,    cps1_12MHz, sf2,        cps_state, empty_init,    ROT0,   "bootleg", "Street Fighter II': Champion Edition (Double K.O. Turbo II, bootleg)", MACHINE_SUPPORTS_SAVE ) // 902140 !!! - based on USA version
GAME( 1992, sf2level,    sf2ce,    sf2m3,      sf2level,   cps_state, empty_init,    ROT0,   "bootleg", "Street Fighter II': Champion Edition (bootleg with level selection)", MACHINE_SUPPORTS_SAVE ) // 920322 - based on USA version
GAME( 1992, sf2ceblp,    sf2ce,    cps1_10MHz, sf2,        cps_state, init_sf2ceblp, ROT0,   "bootleg", "Street Fighter II': Champion Edition (protected bootleg on non-dash board)", MACHINE_SUPPORTS_SAVE )          // 920313 - based on USA version
GAME( 1992, sf2m2,       sf2ce,    cps1_12MHz, sf2m2,      cps_state, init_sf2hack,  ROT0,   "bootleg", "Street Fighter II': Champion Edition (M2, bootleg)", MACHINE_SUPPORTS_SAVE )               // 920313 - based on World version
GAME( 1992, sf2m3,       sf2ce,    sf2m3,      sf2,        cps_state, empty_init,    ROT0,   "bootleg", "Street Fighter II': Champion Edition (M3, bootleg)", MACHINE_SUPPORTS_SAVE )               // 920313 - based on USA version
GAME( 1992, sf2m4,       sf2ce,    cps1_12MHz, sf2m4,      cps_state, init_sf2hack,  ROT0,   "bootleg", "Street Fighter II': Champion Edition (M4, bootleg)", MACHINE_SUPPORTS_SAVE )               // 920322 - based on Japan version
GAME( 1992, sf2m5,       sf2ce,    cps1_12MHz, sf2hack,    cps_state, init_sf2hack,  ROT0,   "bootleg", "Street Fighter II': Champion Edition (M5, bootleg)", MACHINE_SUPPORTS_SAVE )               // 920313 - based on World version
GAME( 1992, sf2m6,       sf2ce,    cps1_12MHz, sf2hack,    cps_state, init_sf2hack,  ROT0,   "bootleg", "Street Fighter II': Champion Edition (M6, bootleg)", MACHINE_SUPPORTS_SAVE )               // 811102 !!! - based on World version
GAME( 1992, sf2m7,       sf2ce,    cps1_12MHz, sf2hack,    cps_state, init_sf2hack,  ROT0,   "bootleg", "Street Fighter II': Champion Edition (M7, bootleg)", MACHINE_SUPPORTS_SAVE )               // 920313 - based on World version
GAME( 1992, sf2m8,       sf2ce,    sf2m3,      sf2,        cps_state, init_sf2m8,    ROT0,   "bootleg", "Street Fighter II': Champion Edition (M8, bootleg)", MACHINE_SUPPORTS_SAVE )               // 920313 - based on USA version
GAME( 1992, sf2m10,      sf2ce,    sf2m10,     sf2hack,    cps_state, empty_init,    ROT0,   "bootleg", "Street Fighter II': Champion Edition (M10, bootleg)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, sf2yyc,      sf2ce,    cps1_12MHz, sf2hack,    cps_state, init_sf2hack,  ROT0,   "bootleg", "Street Fighter II': Champion Edition (YYC, bootleg)", MACHINE_SUPPORTS_SAVE )              // 920313 - based on World version
GAME( 1992, sf2koryu,    sf2ce,    cps1_12MHz, sf2hack,    cps_state, init_sf2hack,  ROT0,   "bootleg", "Street Fighter II': Champion Edition (Xiang Long, Chinese bootleg)", MACHINE_SUPPORTS_SAVE )       // 811102 !!! - based on World version
GAME( 1992, sf2dongb,    sf2ce,    cps1_12MHz, sf2,        cps_state, init_sf2dongb, ROT0,   "bootleg", "Street Fighter II': Champion Edition (Dongfang Bubai protection, bootleg)", MACHINE_SUPPORTS_SAVE ) // 920313 - based on World version
GAME( 1992, sf2ceupl,    sf2ce,    sf2m10,     sf2hack,    cps_state, empty_init,    ROT0,   "bootleg (UPL?)", "Street Fighter II': Champion Edition (Japan 920322, UPL bootleg?)", MACHINE_SUPPORTS_SAVE ) // FIXME: pinpoint a full PCB and deambiguate
GAME( 1992, sf2cems6a,   sf2ce,    sf2cems6,   sf2,        cps_state, empty_init,    ROT0,   "bootleg", "Street Fighter II': Champion Edition (Mstreet-6, bootleg, set 1)", MACHINE_SUPPORTS_SAVE ) // 920313 USA
GAME( 1992, sf2cems6b,   sf2ce,    sf2cems6,   sf2bhh,     cps_state, empty_init,    ROT0,   "bootleg", "Street Fighter II': Champion Edition (Mstreet-6, bootleg, set 2)", MACHINE_SUPPORTS_SAVE ) // 920322 USA
GAME( 1992, sf2cems6c,   sf2ce,    sf2cems6,   sf2bhh,     cps_state, empty_init,    ROT0,   "bootleg", "Street Fighter II': Champion Edition (Mstreet-6, bootleg, set 3)", MACHINE_SUPPORTS_SAVE ) // 920322 USA
GAME( 1992, sf2ceds6,    sf2ce,    sf2cems6,   sf2,        cps_state, empty_init,    ROT0,   "bootleg", "Street Fighter II': Champion Edition (Dstreet-6, bootleg)", MACHINE_SUPPORTS_SAVE ) // 920313 USA
GAME( 1992, sf2re,       sf2ce,    sf2m3,      sf2,        cps_state, empty_init,    ROT0,   "bootleg", "Street Fighter II': Champion Edition (RE, bootleg)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )    // 920313 - based on USA version, glitch on title screen confirmed not to happen on PCB so MIG
GAME( 1992, sf2mkot,     sf2ce,    cps1_10MHz, sf2hack,    cps_state, init_sf2hack,  ROT0,   "bootleg", "Street Fighter II': Magic KO Turbo!! - Nightmare Crack", MACHINE_SUPPORTS_SAVE ) // 920313 - based on World version
GAME( 1992, cworld2j,    0,        cps1_12MHz, cworld2j,   cps_state, empty_init,    ROT0,   "Capcom", "Adventure Quiz Capcom World 2 (Japan 920611)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, cworld2ja,   cworld2j, cps1_12MHz, cworld2j,   cps_state, empty_init,    ROT0,   "Capcom", "Adventure Quiz Capcom World 2 (Japan 920611, B-Board 90629B-3, no battery)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, cworld2jb,   cworld2j, cps1_12MHz, cworld2j,   cps_state, empty_init,    ROT0,   "Capcom", "Adventure Quiz Capcom World 2 (Japan 920611, B-Board 91634B-2)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, varth,       0,        cps1_12MHz, varth,      cps_state, empty_init,    ROT270, "Capcom", "Varth: Operation Thunderstorm (World 920714)", MACHINE_SUPPORTS_SAVE )  // "ETC"    // 12MHz verified
GAME( 1992, varthr1,     varth,    cps1_12MHz, varth,      cps_state, empty_init,    ROT270, "Capcom", "Varth: Operation Thunderstorm (World 920612)", MACHINE_SUPPORTS_SAVE )  // "ETC"
GAME( 1992, varthu,      varth,    cps1_12MHz, varth,      cps_state, empty_init,    ROT270, "Capcom (Romstar license)", "Varth: Operation Thunderstorm (USA 920612)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, varthj,      varth,    cps1_12MHz, varth,      cps_state, empty_init,    ROT270, "Capcom", "Varth: Operation Thunderstorm (Japan 920714)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, varthjr,     varth,    cps1_12MHz, varth,      cps_state, empty_init,    ROT270, "Capcom", "Varth: Operation Thunderstorm (Japan Resale Ver. 920714)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, varthb2,     varth,    varthb2,    varth,      cps_state, empty_init,    ROT270, "bootleg", "Varth: Operation Thunderstorm (bootleg, set 2)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )  // World 920612
GAME( 1992, varthb3,     varth,    varthb3,    varth,      cps_state, empty_init,    ROT270, "bootleg", "Varth: Operation Thunderstorm (bootleg, set 3)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )  // USA 920612, different 'mapper'?
GAME( 1992, qad,         0,        cps1_12MHz, qad,        cps_state, empty_init,    ROT0,   "Capcom", "Quiz & Dragons: Capcom Quiz Game (USA 920701)", MACHINE_SUPPORTS_SAVE ) // 12MHz verified
GAME( 1994, qadjr,       qad,      cps1_12MHz, qadjr,      cps_state, empty_init,    ROT0,   "Capcom", "Quiz & Dragons: Capcom Quiz Game (Japan Resale Ver. 940921)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, wof,         0,        qsound,     wof,        cps_state, init_wof,      ROT0,   "Capcom", "Warriors of Fate (World 921031)", MACHINE_SUPPORTS_SAVE )   // "ETC"
GAME( 1992, wofr1,       wof,      qsound,     wof,        cps_state, init_wof,      ROT0,   "Capcom", "Warriors of Fate (World 921002)", MACHINE_SUPPORTS_SAVE )   // "ETC"
GAME( 1992, wofu,        wof,      qsound,     wof,        cps_state, init_wof,      ROT0,   "Capcom", "Warriors of Fate (USA 921031)", MACHINE_SUPPORTS_SAVE ) // World "warning"
GAME( 1992, wofa,        wof,      qsound,     wof,        cps_state, init_wof,      ROT0,   "Capcom", "Sangokushi II (Asia 921005)", MACHINE_SUPPORTS_SAVE )   // World "warning"
GAME( 1992, wofj,        wof,      qsound,     wof,        cps_state, init_wof,      ROT0,   "Capcom", "Tenchi wo Kurau II: Sekiheki no Tatakai (Japan 921031)", MACHINE_SUPPORTS_SAVE )
GAME( 1999, wofhfh,      wof,      wofhfh,     wofhfh,     cps_state, empty_init,    ROT0,   "bootleg", "Huo Feng Huang (Chinese bootleg of Sangokushi II)", MACHINE_SUPPORTS_SAVE )    // 921005 - based on Asia version
GAME( 1992, sf2hf,       0,        cps1_12MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II': Hyper Fighting (World 921209)", MACHINE_SUPPORTS_SAVE ) // "ETC"
GAME( 1992, sf2hfu,      sf2hf,    cps1_12MHz, sf2,        cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II': Hyper Fighting (USA 921209)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, sf2hfj,      sf2hf,    cps1_12MHz, sf2cej,     cps_state, empty_init,    ROT0,   "Capcom", "Street Fighter II' Turbo: Hyper Fighting (Japan 921209)", MACHINE_SUPPORTS_SAVE )
GAME( 1993, dino,        0,        qsound,     dino,       cps_state, init_dino,     ROT0,   "Capcom", "Cadillacs and Dinosaurs (World 930201)", MACHINE_SUPPORTS_SAVE )    // "ETC"
GAME( 1993, dinou,       dino,     qsound,     dino,       cps_state, init_dino,     ROT0,   "Capcom", "Cadillacs and Dinosaurs (USA 930201)", MACHINE_SUPPORTS_SAVE )
GAME( 1993, dinoa,       dino,     qsound,     dino,       cps_state, init_dino,     ROT0,   "Capcom", "Cadillacs and Dinosaurs (Asia TW 930223)", MACHINE_SUPPORTS_SAVE ) // Title screen shows "distributed by Hung Hsi Enterprise".
GAME( 1993, dinoj,       dino,     qsound,     dino,       cps_state, init_dino,     ROT0,   "Capcom", "Cadillacs: Kyouryuu Shin Seiki (Japan 930201)", MACHINE_SUPPORTS_SAVE )
GAME( 1993, dinohunt,    dino,     wofhfh,     dinoh,      cps_state, init_dinohunt, ROT0,   "bootleg", "Dinosaur Hunter (Chinese bootleg of Cadillacs and Dinosaurs)", MACHINE_SUPPORTS_SAVE )  // 930223 -  based on Asia TW version
GAME( 1993, punisher,    0,        qsound,     punisher,   cps_state, init_punisher, ROT0,   "Capcom", "The Punisher (World 930422)", MACHINE_SUPPORTS_SAVE )   // "ETC"
GAME( 1993, punisheru,   punisher, qsound,     punisher,   cps_state, init_punisher, ROT0,   "Capcom", "The Punisher (USA 930422)", MACHINE_SUPPORTS_SAVE )
GAME( 1993, punisherh,   punisher, qsound,     punisher,   cps_state, init_punisher, ROT0,   "Capcom", "The Punisher (Hispanic 930422)", MACHINE_SUPPORTS_SAVE )
GAME( 1993, punisherj,   punisher, qsound,     punisher,   cps_state, init_punisher, ROT0,   "Capcom", "The Punisher (Japan 930422)", MACHINE_SUPPORTS_SAVE )
GAME( 2002, punisherbz,  punisher, wofhfh,     punisherbz, cps_state, empty_init,    ROT0,   "bootleg", "Biaofeng Zhanjing (Chinese bootleg of The Punisher)", MACHINE_SUPPORTS_SAVE )  // (c) 2002, they ripped the sound from Final Fight!
GAME( 1993, slammast,    0,        qsound,     slammast,   cps_state, init_slammast, ROT0,   "Capcom", "Saturday Night Slam Masters (World 930713)", MACHINE_SUPPORTS_SAVE )    // "ETC"
GAME( 1993, slammastu,   slammast, qsound,     slammast,   cps_state, init_slammast, ROT0,   "Capcom", "Saturday Night Slam Masters (USA 930713)", MACHINE_SUPPORTS_SAVE )
GAME( 1993, mbomberj,    slammast, qsound,     slammast,   cps_state, init_slammast, ROT0,   "Capcom", "Muscle Bomber: The Body Explosion (Japan 930713)", MACHINE_SUPPORTS_SAVE )
GAME( 1993, mbombrd,     0,        qsound,     slammast,   cps_state, init_slammast, ROT0,   "Capcom", "Muscle Bomber Duo: Ultimate Team Battle (World 931206)", MACHINE_SUPPORTS_SAVE )    // "ETC"
GAME( 1993, mbombrdj,    mbombrd,  qsound,     slammast,   cps_state, init_slammast, ROT0,   "Capcom", "Muscle Bomber Duo: Heat Up Warriors (Japan 931206)", MACHINE_SUPPORTS_SAVE )
GAME( 1993, gulunpa,     0,        cps1_10MHz, gulunpa,    cps_state, empty_init,    ROT0,   "Capcom", "Gulun.Pa! (Japan 931220 L) (prototype)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS ) // assume Capcom from logo in gfx bank, unknown board origin
GAME( 1994, pnickj,      0,        cps1_12MHz, pnickj,     cps_state, empty_init,    ROT0,   "Capcom", "Pnickies (Japan 940608)", MACHINE_SUPPORTS_SAVE ) // Puyo Puyo puzzlegame concept licensed from Compile, this game is by Capcom
GAME( 1995, qtono2j,     0,        cps1_12MHz, qtono2j,    cps_state, empty_init,    ROT0,   "Capcom", "Quiz Tonosama no Yabou 2: Zenkoku-ban (Japan 950123)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, megaman,     0,        cps1_12MHz, megaman,    cps_state, empty_init,    ROT0,   "Capcom", "Mega Man: The Power Battle (CPS1, USA 951006)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, megamana,    megaman,  cps1_12MHz, megaman,    cps_state, empty_init,    ROT0,   "Capcom", "Mega Man: The Power Battle (CPS1, Asia 951006)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, rockmanj,    megaman,  cps1_12MHz, rockmanj,   cps_state, empty_init,    ROT0,   "Capcom", "Rockman: The Power Battle (CPS1, Japan 950922)", MACHINE_SUPPORTS_SAVE )
GAME( 1999, pmonster,    0,        ganbare,    pmonster,   cps_state, init_ganbare,  ROT0,   "Capcom", "Gamushara Battle! Puchi Monster (Japan 990519)", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) // Needs hopper emulation
GAME( 2000, ganbare,     0,        ganbare,    ganbare,    cps_state, init_ganbare,  ROT0,   "Capcom", "Ganbare! Marine Kun (Japan 2K0411)", MACHINE_SUPPORTS_SAVE )
GAME( 1994, pokonyan,    0,        cps1_10MHz, pokonyan,   cps_state, empty_init,    ROT0,   "Capcom", "Pokonyan! Balloon (Japan 940322)", MACHINE_SUPPORTS_SAVE ) // 2002-10-24 was on the ROM labels, 940322 on the startup screen... take your pick
GAME( 1996, mpumpkin,    0,        cps1_10MHz, mpumpkin,   cps_state, empty_init,    ROT0,   "Capcom", "Magical Pumpkin: Puroland de Daibouken (Japan 960712)", MACHINE_SUPPORTS_SAVE )

/* Games released on CPS-1 hardware by Mitchell */

GAME( 1995, pang3,       0,        pang3,      pang3,      cps_state, init_pang3,    ROT0,   "Mitchell", "Pang! 3 (Europe 950601)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, pang3r1,     pang3,    pang3,      pang3,      cps_state, init_pang3,    ROT0,   "Mitchell", "Pang! 3 (Europe 950511)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, pang3j,      pang3,    pang3,      pang3,      cps_state, init_pang3,    ROT0,   "Mitchell", "Pang! 3: Kaitou Tachi no Karei na Gogo (Japan 950511)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, pang3b,      pang3,    pang3,      pang3b,     cps_state, init_pang3b,   ROT0,   "bootleg",  "Pang! 3 (bootleg, set 1)", MACHINE_SUPPORTS_SAVE )    // 950511 - based on Euro version
GAME( 1995, pang3b2,     pang3,    pang3,      pang3,      cps_state, init_pang3,    ROT0,   "bootleg",  "Pang! 3 (bootleg, set 2)", MACHINE_SUPPORTS_SAVE )    // 950601 - based on Euro version
GAME( 1995, pang3b3,     pang3,    pang3,      pang3,      cps_state, init_pang3,    ROT0,   "bootleg",  "Pang! 3 (bootleg, set 3)", MACHINE_SUPPORTS_SAVE )    // 950511 - based on Euro version, hacked to use cps-b-17
GAME( 1995, pang3b4,     pang3,    cps1_12MHz, pang3b4,    cps_state, init_pang3b4,  ROT0,   "bootleg",  "Pang! 3 (bootleg, set 4)", MACHINE_SUPPORTS_SAVE )    // 950601 - based on Euro version, unencrypted, protection PIC, no serial EPROM
GAME( 1995, pang3b5,     pang3,    pang3,      pang3,      cps_state, init_pang3,    ROT0,   "bootleg",  "Pang! 3 (bootleg, set 5)", MACHINE_SUPPORTS_SAVE )    // 950511 - based on Euro version

/* CPS1 multi game bootleg */
GAME( 20??, cps1mult,    0,        cps1_12MHz, sf2,        cps_state, init_cps1mult, ROT0,   "bootleg", "CPS1 Multi Game", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) // needs game selection support via DIP switches

/* Home 'CPS Changer' Unit */
CONS( 1994, wofch,  0,     0, qsound,     wofch, cps_state, init_wof,   "Capcom", "Tenchi wo Kurau II: Sekiheki no Tatakai (CPS Changer, Japan 921031)", MACHINE_SUPPORTS_SAVE )
CONS( 1995, sfzch,  0,     0, cps1_12MHz, sfzch, cps_state, empty_init, "Capcom", "Street Fighter Zero (CPS Changer, Japan 951020)", MACHINE_SUPPORTS_SAVE )
// are these 2 legit sets, or did somebody region hack it?
CONS( 1995, sfach,  sfzch, 0, cps1_12MHz, sfzch, cps_state, empty_init, "Capcom", "Street Fighter Alpha: Warriors' Dreams (CPS Changer, Publicity USA 950727)", MACHINE_SUPPORTS_SAVE )
CONS( 1995, sfzbch, sfzch, 0, cps1_12MHz, sfzch, cps_state, empty_init, "Capcom", "Street Fighter Zero (CPS Changer, Brazil 950727)", MACHINE_SUPPORTS_SAVE )

// Ken Sei Mogura: Street Fighter II - see capcom/kenseim.cpp



cps2000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Devin Acker
/*
    Skeleton driver for Casio CPS-2000 piano.

    Sound hardware:
    - 2x uPD932 consonant-vowel synth
    - uPD934 PCM rhythm
    - monophonic square wave bass w/ RC envelope and filter
*/

#include "emu.h"

#include "cpu/upd7810/upd7810.h"
#include "machine/msm6200.h"
#include "sound/dac.h"
#include "sound/flt_rc.h"
#include "sound/upd934g.h"
#include "sound/va_eg.h"
#include "sound/va_vca.h"

#include "speaker.h"

namespace {

class cps2000_state : public driver_device
{
public:
	cps2000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_kbd(*this, "kbd")
		, m_bass_env(*this, "bass_env")
		, m_pcm(*this, "pcm")
		, m_keys(*this, "KC%u", 0)
	{ }

	void cps2000(machine_config &config);

	void keys_w(ioport_value val) { m_key_sel = val; }
	ioport_value keys_r();

	void bass_w(ioport_value val);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void maincpu_map(address_map &map) ATTR_COLD;

	void io_w(offs_t offset, u8 data);

	required_device<upd78c10_device> m_maincpu;
	required_device<msm6200_device> m_kbd;
	required_device<va_rc_eg_device> m_bass_env;
	required_device<upd934g_device> m_pcm;

	required_ioport_array<5> m_keys;

	ioport_value m_key_sel;

	u8 m_932a_regs[256];
	u8 m_932b_regs[256];
};

/**************************************************************************/
static INPUT_PORTS_START(cps2000)
	PORT_START("PA")
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(cps2000_state::keys_w))
	PORT_BIT( 0xf8, IP_ACTIVE_HIGH, IPT_OUTPUT ) // LED outputs

	PORT_START("PB")
	PORT_BIT( 0x3f, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(cps2000_state::keys_r))
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_OUTPUT ) // LED outputs

	PORT_START("PC")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_OUTPUT ) // LED outputs
	PORT_BIT( 0x10, IP_ACTIVE_LOW,  IPT_CUSTOM ) // 934 busy
	PORT_BIT( 0x20, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(cps2000_state::bass_w))
	PORT_BIT( 0xc0, IP_ACTIVE_LOW,  IPT_UNUSED )

	PORT_START("kbd:KI1")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G7")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#7")

	PORT_START("kbd:KI2")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F7")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E7")

	PORT_START("kbd:KI3")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#7")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D7")

	PORT_START("kbd:KI4")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#7")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C7")

	PORT_START("kbd:KI5")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B6")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#6")

	PORT_START("kbd:KI6")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A6")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#6")

	PORT_START("kbd:KI7")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G6")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#6")

	PORT_START("kbd:KI8")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F6")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E6")

	PORT_START("kbd:KI9")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#6")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D6")

	PORT_START("kbd:KI10")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#6")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C6")

	PORT_START("kbd:KI11")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B5")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#5")

	PORT_START("kbd:KI12")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A5")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#5")

	PORT_START("kbd:KI13")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G5")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#5")

	PORT_START("kbd:KI14")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F5")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E5")

	PORT_START("kbd:KI15")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#5")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D5")

	PORT_START("kbd:KI16")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#5")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C5")

	PORT_START("kbd:KI17")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B4")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#4")

	PORT_START("kbd:KI18")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A4")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#4")

	PORT_START("kbd:KI19")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G4")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#4")

	PORT_START("kbd:KI20")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F4")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E4")

	PORT_START("kbd:KI21")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#4")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D4")

	PORT_START("kbd:KI22")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#4")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C4")

	PORT_START("kbd:KI23")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B3")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#3")

	PORT_START("kbd:KI24")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A3")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#3")

	PORT_START("kbd:KI25")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G3")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#3")

	PORT_START("kbd:KI26")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F3")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E3")

	PORT_START("kbd:KI27")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#3")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D3")

	PORT_START("kbd:KI28")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#3")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C3")

	PORT_START("kbd:KI29")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B2")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#2")

	PORT_START("kbd:KI30")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A2")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#2")

	PORT_START("kbd:KI31")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G2")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#2")

	PORT_START("kbd:KI32")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F2")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E2")

	PORT_START("kbd:KI33")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#2")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D2")

	PORT_START("kbd:KI34")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#2")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C2")

	PORT_START("kbd:KI35")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B1")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#1")

	PORT_START("kbd:KI36")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A1")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#1")

	PORT_START("kbd:KI37")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G1")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#1")

	PORT_START("kbd:KI38")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F1")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E1")

	PORT_START("kbd:VELOCITY")
	PORT_BIT(0x3f, 0x3f, IPT_POSITIONAL_V) PORT_NAME("Key Velocity") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_CENTERDELTA(0) PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH) PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH)

	PORT_START("AN0")
	PORT_BIT(0xff, 0x80, IPT_POSITIONAL_V) PORT_NAME("Tempo") PORT_SENSITIVITY(10) PORT_KEYDELTA(5) PORT_CENTERDELTA(0) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)

	PORT_START("AN1")
	PORT_BIT(0xff, 0x80, IPT_POSITIONAL_H) PORT_NAME("Tuning") PORT_SENSITIVITY(10) PORT_KEYDELTA(5) PORT_CENTERDELTA(0) PORT_PLAYER(2) PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH) PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH)

	PORT_START("KC0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Octave Unison")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Sustain")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Chorus")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Fingered")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Casio Chord On")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Casio Chord Off")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KC1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Start / Stop")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Synchro")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Intro / Fill-In")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Super Accomp")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Memory")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Variation")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KC2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone 1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone 2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone 3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone 4")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Rhythm Select")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Touch Response")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KC3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone 5")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone 6")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone 7")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Rhythm 1")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Rhythm 2")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Rhythm 3")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KC4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone Select")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Sustain Pedal")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Rhythm 4")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Rhythm 5")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Rhythm 6")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

INPUT_PORTS_END

/**************************************************************************/
void cps2000_state::maincpu_map(address_map &map)
{
	map(0x0000, 0x7fff).rom();
	map(0x8000, 0xbfff).r(m_kbd, FUNC(msm6200_device::read));
	map(0x8000, 0xffff).w(FUNC(cps2000_state::io_w));
}

/**************************************************************************/
void cps2000_state::cps2000(machine_config &config)
{
	UPD78C10(config, m_maincpu, 10'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &cps2000_state::maincpu_map);
	m_maincpu->pa_out_cb().set_ioport("PA");
	m_maincpu->pb_in_cb().set_ioport("PB");
	m_maincpu->pb_out_cb().set_ioport("PB");
	m_maincpu->pc_in_cb().set_ioport("PC");
	m_maincpu->pc_out_cb().set_ioport("PC");
	m_maincpu->an0_func().set_ioport("AN0");
	m_maincpu->an1_func().set_ioport("AN1");

	MSM6200(config, m_kbd, 4.9468_MHz_XTAL);
	m_kbd->irq_cb().set_inputline(m_maincpu, UPD7810_INTF1);

	SPEAKER(config, "speaker", 2).front();

	// UPD932(config, "upd932a", 4.9468_MHz_XTAL);
	// UPD932(config, "upd932b", 4.9468_MHz_XTAL);

	DAC_1BIT(config, "bass").add_route(0, "bass_vca", 1.0, 0);
	m_maincpu->co0_func().set("bass", FUNC(dac_1bit_device::write));

	VA_RC_EG(config, m_bass_env).set_c(CAP_U(3.3)).add_route(0, "bass_vca", 1.0 / 5, 1);
	VA_VCA(config, "bass_vca").add_route(0, "bass_rc1", 1.0);

	FILTER_RC(config, "bass_rc1").set_lowpass(RES_K(47), CAP_N(47)).add_route(0, "bass_rc2", 1.0);
	FILTER_RC(config, "bass_rc2").set_lowpass(RES_K(22), CAP_N(47))
		.add_route(0, "speaker", 0.25, 0).add_route(0, "speaker", 0.25, 1);

	UPD934G(config, m_pcm, 4.9468_MHz_XTAL / 4);
	m_pcm->add_route(0, "speaker", 0.25,  0);
	m_pcm->add_route(0, "speaker", 0.125, 1);
	m_pcm->add_route(1, "speaker", 0.25,  0);
	m_pcm->add_route(1, "speaker", 0.125, 1);
	m_pcm->add_route(2, "speaker", 0.125, 0);
	m_pcm->add_route(2, "speaker", 0.25,  1);
	m_pcm->add_route(3, "speaker", 0.125, 0);
	m_pcm->add_route(3, "speaker", 0.25,  1);
}

/**************************************************************************/
void cps2000_state::machine_start()
{
	m_key_sel = 0;

	std::fill(std::begin(m_932a_regs), std::end(m_932a_regs), 0);
	std::fill(std::begin(m_932b_regs), std::end(m_932b_regs), 0);

	save_item(NAME(m_key_sel));
	save_item(NAME(m_932a_regs));
	save_item(NAME(m_932b_regs));
}

/**************************************************************************/
void cps2000_state::machine_reset()
{
}

/**************************************************************************/
ioport_value cps2000_state::keys_r()
{
	if (m_key_sel < 5)
		return m_keys[m_key_sel]->read();

	return 0xff;
}

/**************************************************************************/
void cps2000_state::bass_w(ioport_value val)
{
	if (val)
		m_bass_env->set_r(RES_R(270)).set_target_v(5.0);
	else
		m_bass_env->set_r(RES_K(22)).set_target_v(0.0);
}

/**************************************************************************/
void cps2000_state::io_w(offs_t offset, u8 data)
{
	if (!BIT(offset, 14))
		m_kbd->write(offset, data);
	if (!BIT(offset, 13))
		m_pcm->write(offset >> 8, offset);
	if (!BIT(offset, 12))
		m_932a_regs[offset & 0xff] = data;
	if (!BIT(offset, 11))
		m_932b_regs[offset & 0xff] = data;
}

/**************************************************************************/
ROM_START( cps2000 )
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD( "hn613256p-db6.bin", 0x0000, 0x8000, CRC(92b68074) SHA1(eda13d59a15077b06d95004b5f2a2a8432109893) )

	ROM_REGION(0x10000, "pcm", 0)
	ROM_LOAD( "hn613256p-da5.bin", 0x0000, 0x8000, CRC(63d6dd54) SHA1(231ffb6a4cc113133accd19dd9ed1e5165e3837e) )
ROM_END

} // anonymous namespace

SYST( 1986, cps2000, 0,    0, cps2000, cps2000, cps2000_state, empty_init, "Casio", "CPS-2000", MACHINE_NOT_WORKING )



crei680.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/********************************************************************************

CREI680 trainer.

This has 2 boards, the CPU board with the buttons, LEDs etc,
and the video board.

Like many machines of this era, the cassette circuit is grossly overcomplicated,
using a 3301 op-amp, a 75140 line receiver and a bunch of CMOS logic ICs. Since
the first of these (a 14040B) divides the E clock down to 300 * 16 to generate
the ACIA's TXC input, the format can be assumed to be Kansas City.

The schematic is missing some information, so we've guessed a few things.
We don't have any instructions, user manual or anything - just the schematic.

TODO:
- Cassette to test when we find out how to use it.
- Single-step circuit
- Add paste facility, although it will be difficult with having to set each
   individual bit of each byte. If the byte already is non-zero, it won't
   know what to do.


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


#include "emu.h"
#include "cpu/m6800/m6800.h"
#include "imagedev/cassette.h"
#include "machine/6821pia.h"
#include "machine/6850acia.h"
#include "machine/clock.h"
#include "machine/timer.h"
#include "video/mc6845.h"
#include "sound/beep.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"
#include "crei680.lh"


namespace {

class crei680_state : public driver_device
{
public:
	crei680_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cass(*this, "cassette")
		, m_pia0(*this, "pia0")
		, m_pia1(*this, "pia1")
		, m_uart(*this, "uart")
		, m_crtc(*this, "crtc")
		, m_vram(*this, "videoram")
		, m_cgen(*this, "chargen")
		, m_palette(*this, "palette")
		, m_beep(*this, "beeper")
		, m_leds(*this, "led%u", 0U)
		{ }


	void crei680(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(reset_button);

private:
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_w);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);
	MC6845_UPDATE_ROW(crtc_update_row);

	void mem_map(address_map &map) ATTR_COLD;

	void pia0b_w(u8);
	void pia1b_w(u8);
	void piaset_w(offs_t, u8);
	bool m_cassbit = 0;
	bool m_cassold = 0;
	u8 m_cass_data[4]{};
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_device<cassette_image_device> m_cass;
	required_device<pia6821_device> m_pia0;
	required_device<pia6821_device> m_pia1;
	required_device<acia6850_device> m_uart;
	required_device<mc6845_device> m_crtc;
	required_shared_ptr<u8> m_vram;
	required_region_ptr<u8> m_cgen;
	required_device<palette_device> m_palette;
	required_device<beep_device> m_beep;
	output_finder<8> m_leds;
};


/* Memory Maps */

void crei680_state::mem_map(address_map &map)
{
	map(0x0080, 0x017f).ram();    // mc6810
	map(0x3000, 0x3fff).ram();    // 8x 2114
	map(0xc004, 0xc007).rw(m_pia0, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xc008, 0xc009).rw(m_uart, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xc010, 0xc010).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0xc011, 0xc011).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0xc020, 0xc023).rw(m_pia1, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xc024, 0xc027).w(FUNC(crei680_state::piaset_w));
	map(0xd000, 0xd3ff).ram().share("videoram");    // 6x 2102
	map(0xe000, 0xefff).rom().region("basic", 0);   // 68A332
	map(0xf000, 0xffff).rom().region("maincpu", 0);  // 68A332
}

/* Input Ports */

static INPUT_PORTS_START( crei680 )
	PORT_START("X0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_NAME("bit0")    // toggle bit0 of data
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_NAME("bit1")    // ...etc...
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_NAME("bit2")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_NAME("bit3")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_NAME("bit4")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_NAME("bit5")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_NAME("bit6")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_NAME("bit7")

	PORT_START("X1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_NAME("REG")   // Examine or set register
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_NAME("LAD")   // Set low byte of address
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_NAME("HAD")   // Set high byte of address
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_NAME("RUN")    // Execute
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DOWN) PORT_NAME("DEC")   // Previous address or register
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_UP) PORT_NAME("INC")    // Next address or register
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("DATA")   // Switch between address and data modes
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("RESET")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RST") PORT_CODE(KEYCODE_LALT) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(crei680_state::reset_button), 0)
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(crei680_state::reset_button)
{
	if (newval)
	{
		m_pia0->reset();
		m_pia1->reset();
	}
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}

/* Video */
/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 8,                  /* 8 x 8 characters */
	128,                    /* 64 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_crei680 )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END

MC6845_UPDATE_ROW( crei680_state::crtc_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u32 *p = &bitmap.pix(y);

	for (u16 x = 0; x < x_count; x++)
	{
		u16 mem = (ma + x) & 0x3ff;
		u8 chr = m_vram[mem] & 0x3f;  // video ram is 6 bit.
		if (chr < 0x20)
			chr |= 0x40;
		u8 gfx = (ra < 8) ? m_cgen[(chr<<3) | ra] : 0;

		/* Display a scanline of a character (6 pixels) */
		*p++ = palette[BIT(gfx, 5)];
		*p++ = palette[BIT(gfx, 4)];
		*p++ = palette[BIT(gfx, 3)];
		*p++ = palette[BIT(gfx, 2)];
		*p++ = palette[BIT(gfx, 1)];
		*p++ = palette[BIT(gfx, 0)];
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(crei680_state::kansas_w)
{
	m_cass_data[3]++;

	if (m_cassbit != m_cassold)
	{
		m_cass_data[3] = 0;
		m_cassold = m_cassbit;
	}

	if (m_cassbit)
		m_cass->output(BIT(m_cass_data[3], 0) ? -1.0 : +1.0); // 2400Hz
	else
		m_cass->output(BIT(m_cass_data[3], 1) ? -1.0 : +1.0); // 1200Hz
}

TIMER_DEVICE_CALLBACK_MEMBER(crei680_state::kansas_r)
{
	/* cassette - turn 1200/2400Hz to a bit */
	m_cass_data[1]++;
	u8 cass_ws = (m_cass->input() > +0.03) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		m_uart->write_rxd((m_cass_data[1] < 12) ? 1 : 0);
		m_cass_data[1] = 0;
	}
}

void crei680_state::pia0b_w(u8 data)
{
	/* d0/1 = both low for beep
	   d4 = cassette relay
	   others are mystery circuits */
	m_beep->set_state(BIT(data, 0, 2) ? 0 : 1);
	m_cass->change_state((BIT(data,4)) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
}

void crei680_state::pia1b_w(u8 data)
{
	for (u8 i = 0; i < 8; i++)
		m_leds[i] = !BIT(data, i);
}

// The various devices in the cxxx range are selected by an address line. This means things clash
// if 2 or more are selected at the same time. Normally this could be ignored, except that the
// bootup sequence writes to the 2 PIAs at the same time in one particular instruction.
void crei680_state::piaset_w(offs_t offset, u8 data)
{
	m_pia0->write(offset, data);
	m_pia1->write(offset, data);
}

/* Machine Initialization */

void crei680_state::machine_start()
{
	m_leds.resolve();
	save_item(NAME(m_cassold));
	save_item(NAME(m_cass_data));
	save_item(NAME(m_cassbit));
}

void crei680_state::machine_reset()
{
	m_beep->set_state(0);
	m_cass_data[0] = 0;
	m_cass_data[1] = 0;
	m_cass_data[3] = 0;
}

/* Machine Drivers */

void crei680_state::crei680(machine_config &config)
{
	/* basic machine hardware */
	M6802(config, m_maincpu, 2.4576_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &crei680_state::mem_map);

	/* video hardware */
	config.set_default_layout(layout_crei680);
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	PALETTE(config, m_palette, palette_device::MONOCHROME);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_crei680);

	MC6845(config, m_crtc, 5281920/6);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(6);
	m_crtc->set_update_row_callback(FUNC(crei680_state::crtc_update_row));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, "beeper", 900).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* devices */
	PIA6821(config, m_pia0);
	m_pia0->readpa_handler().set_ioport("X1");
	m_pia0->writepb_handler().set(FUNC(crei680_state::pia0b_w));
	//m_pia0->cb2_handler().set(FUNC(crei680_state::screen_w));
	m_pia0->irqa_handler().set_inputline("maincpu", M6800_IRQ_LINE);
	m_pia0->irqb_handler().set_inputline("maincpu", M6800_IRQ_LINE);

	PIA6821(config, m_pia1);
	m_pia1->readpa_handler().set_ioport("X0");
	m_pia1->writepb_handler().set(FUNC(crei680_state::pia1b_w));
	//m_pia1->cb2_handler().set(FUNC(crei680_state::screen_w));
	m_pia1->irqa_handler().set_inputline("maincpu", M6800_IRQ_LINE);
	m_pia1->irqb_handler().set_inputline("maincpu", M6800_IRQ_LINE);

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	ACIA6850(config, m_uart, 0);
	m_uart->txd_handler().set([this] (bool state) { m_cassbit = state; });
	//m_uart->rts_handler().set(FUNC(crei680_state::acia_rts_w));
	m_uart->irq_handler().set_inputline("maincpu", M6800_IRQ_LINE);

	clock_device &acia_clock(CLOCK(config, "acia_clock", 2.4576_MHz_XTAL / 4 / 128));
	acia_clock.signal_handler().set("uart", FUNC(acia6850_device::write_txc)); // connected directly
	acia_clock.signal_handler().append("uart", FUNC(acia6850_device::write_rxc)); // FIXME: modulated by RXD

	TIMER(config, "kansas_w").configure_periodic(FUNC(crei680_state::kansas_w), attotime::from_hz(4800));
	TIMER(config, "kansas_r").configure_periodic(FUNC(crei680_state::kansas_r), attotime::from_hz(40000));
}

/* ROMs */

ROM_START( crei680 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "monitor.u19", 0x0000, 0x1000, CRC(0f95918e) SHA1(8cd675cc3a7b38ccd0975ef44f3e954d21711e0a) )  // cpu board

	ROM_REGION( 0x1000, "basic", 0 )
	ROM_LOAD( "basic.u13",   0x0000, 0x1000, CRC(339d5557) SHA1(74763e6416a124dda5c18e6cfa71c4c6848c0ac9) )  // video board

	ROM_REGION( 0x0400, "chargen", 0 )
	ROM_LOAD( "mcm6674p.u9", 0x0000, 0x0400, CRC(1c22088a) SHA1(b5f0bd0cfdec0cd5c1cb764506bef3c17d6af0eb) )  // video board
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT  COMPAT  MACHINE    INPUT    CLASS          INIT          COMPANY                               FULLNAME      FLAGS
COMP( 19??, crei680, 0,      0,      crei680,   crei680, crei680_state, empty_init, "Capital Radio Engineering Institute", "CREI 680", MACHINE_SUPPORTS_SAVE )



crimson.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/**********************************************************************

    SGI Crimson deskside skeleton driver

    To Do: Everything

    Memory map:
    1fc00000 - 1fc7ffff      Boot ROM

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

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/mips/mips3.h"
#include "machine/mc68681.h"
#include "machine/nvram.h"
#include "machine/pit8253.h"

#define LOG_UNKNOWN     (1U << 1)
#define LOG_ALL         (LOG_UNKNOWN)

#define VERBOSE         (0)
#include "logmacro.h"


namespace {

class crimson_state : public driver_device
{
public:
	crimson_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_nvram(*this, "nvram")
		, m_duart(*this, "duart")
	{
	}

	void crimson(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

	u8 nvram_r(offs_t offset);
	void nvram_w(offs_t offset, u8 data);
	u8 duart_r(offs_t offset);
	void duart_w(offs_t offset, u8 data);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<r4000be_device> m_maincpu;
	required_device<nvram_device> m_nvram;
	required_device<scn2681_device> m_duart;

	std::unique_ptr<u8[]> m_nvram_data;
};

void crimson_state::machine_start()
{
	m_nvram_data = std::make_unique<u8[]>(0x800);
	m_nvram->set_base(m_nvram_data.get(), 0x800);
	save_pointer(NAME(m_nvram_data), 0x800);
}

u8 crimson_state::nvram_r(offs_t offset)
{
	return m_nvram_data[offset];
}

void crimson_state::nvram_w(offs_t offset, u8 data)
{
	m_nvram_data[offset] = data;
}

u8 crimson_state::duart_r(offs_t offset)
{
	return m_duart->read(offset >> 1);
}

void crimson_state::duart_w(offs_t offset, u8 data)
{
	m_duart->write(offset >> 1, data);
}

void crimson_state::mem_map(address_map &map)
{
	map(0x00000000, 0x00002fff).ram();
	map(0x00500000, 0x009fffff).ram();
	map(0x17f10000, 0x17f13fff).rw(FUNC(crimson_state::nvram_r), FUNC(crimson_state::nvram_w)).umask64(0xff00000000000000LLU);
	map(0x1f600000, 0x1f60000f).rw("pit", FUNC(pit8254_device::read), FUNC(pit8254_device::write)).umask64(0xff000000ff000000LLU);
	map(0x1fa00000, 0x1fa000ff).rw(FUNC(crimson_state::duart_r), FUNC(crimson_state::duart_w)).umask64(0xff00000000000000LLU);
	map(0x1fc00000, 0x1fc7ffff).rom().region("user1", 0);
}

static INPUT_PORTS_START( crimson )
INPUT_PORTS_END

void crimson_state::crimson(machine_config &config)
{
	R4000BE(config, m_maincpu, 50000000*2);
	m_maincpu->set_icache_size(32768);
	m_maincpu->set_dcache_size(32768);
	m_maincpu->set_addrmap(AS_PROGRAM, &crimson_state::mem_map);

	NVRAM(config, m_nvram, nvram_device::DEFAULT_ALL_0); // CXK5816PN-15 + battery?

	SCN2681(config, m_duart, 3.6864_MHz_XTAL); // TODO: there are two more of these
	m_duart->a_tx_cb().set("rs232", FUNC(rs232_port_device::write_txd));

	PIT8254(config, "pit");

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_duart, FUNC(scn2681_device::rx_a_w));
}

ROM_START( crimson )
	ROM_REGION64_BE( 0x80000, "user1", 0 )
	ROMX_LOAD( "ip17prom.070-081x-005.bin", 0x000000, 0x080000, CRC(d62e8c8e) SHA1(b335213ecfd02ca3185b6ba1874a8b76f908c68b), ROM_GROUPDWORD )
ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT    CLASS          INIT        COMPANY                 FULLNAME                               FLAGS
COMP( 1992, crimson,  0,      0,      crimson,  crimson, crimson_state, empty_init, "Silicon Graphics Inc", "Crimson (R4000, 100MHz, Ver. 4.0.3)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



crvision.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol, Curt Coder
/*
    This 1980s computer was manufactured by VTech of Hong Kong.
    known as: CreatiVision, Dick Smith Wizzard, Funvision, Rameses, VZ 2000 and possibly others.

    There is also a CreatiVision Mk 2, possibly also known as the Laser 500. This was a hardware variant,
    sort of "evolved" hardware made compatible for the scheduled "ColecoVision expansion module", which
    never actually shipped for CreatiVision, but for the Laser 2001 home computer (successor to
    CreatiVision).

    TODO:

    CreatiVision

    - fix Diagnostic A (video) sprites generator test
    - proper keyboard emulation, need keyboard schematics
    - memory expansion 16K, can be chained
    - centronics control/status port
    - non-working cartridges:
        * Diagnostic B (keyboard)
    - homebrew roms with graphics issues:
        * Christmas Demo 1.0
        * Titanic Frogger Demo 1.0
        * Titanic Frogger Demo 1.1

    Salora Manager

    - RAM mapping
    - cassette (figure out correct input level) - it can load its own recordings if you try a few times;
    -- The "sonicinvader.wav" found on the net works perfectly with CRUN.
    - floppy (interface cartridge needed)

*/

/*

Salora Manager

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

Main board

35 0352 02
700391F

|-------------------------------------------------------------------------------|
|               |-----CN1-----|             |-----CN2-----|                     |
|                                                                   10.738MHz   --|
|                                   4116                                    CN3   |
|       ROM01                                               VDC                   |
|                                   4116                                        --|
|                                                                               |
|       ROM23                       4116    -                                   |
|                                           |                                   |
|                                   4116    |                                   |
|   LS04                                   CN6                                  --|
|                                   4116    |                  6821               |
|   LS32                                    |                                     |
|                                   4116    -                                     |
|   LS139                                           PSG                           |
|                                   4116                                          |
|   LS138                                                                         |
|                                   4116                                    CN4   |
|   LS244                                                                         |
|                                                                                 |
|   LS245                                                                         |
|                                                                                 |
|                   LS244                                                         |
|       6502                                                                    --|
|                   LS244                                                       |
|                                           |-------------CN5-------------|     |
|-------------------------------------------------------------------------------|

Notes:
All IC's shown. Prototype-ish board, with many jumper wires and extra capacitors.

ROM01   - Toshiba TMM2464P 8Kx8 one-time PROM, labeled "0.1"
ROM23   - Toshiba TMM2464P 8Kx8 one-time PROM, labeled "23"
6502    - Rockwell R6502AP 8-bit Microprocessor
6821    - Hitachi HD468B21P Peripheral Interface Adaptor
VDC     - Texas Instruments TMS9929A Video Display Controller (covered w/heatsink)
PSG     - Texas Instruments SN76489AN Programmable Sound Generator
4116    - Toshiba TMM416P-3 16Kx1 RAM (covered w/heatsink)
CN1     - sub board connector (17x2 pin header)
CN2     - RF board connector (17x1 pin header)
CN3     - printer connector (7x2 PCB edge male)
CN4     - expansion connector (30x2 PCB edge male)
CN5     - cartridge connector (18x2 PCB edge female)
CN6     - keyboard connector (16x1 pin header)


Sub board

700472
35 0473 03

|---------------------------------------|
|   17.73447MHz |-----CN1-----|         |
|                                       |
|   74S04                       4116    |
|                                       |
|   LS90                        4116    |
|                                       |
|   LS10                        4116    |
|                                       |
|   LS367                       4116    |
|                                       |
|   LS393                       4116    |
|                                       |
|   LS244                       4116    |
|                                       |
|   LS257                       4116    |
|                                       |
|   LS257                       4116    |
|                                       |
|                               LS139   |
|                                       |
|---------------------------------------|

Notes:
All IC's shown.

4116    - Toshiba TMM416P-3 16Kx1 RAM
CN1     - main board connector (17x2 pin header)


Left Keyboard           Right Keyboard
1    2 3 4 5 6          7 8 9 0 :  -
CTRL Q W E R T          Y U I O P  RETN
<-   A S D F G          H J K L dn ->
SHFT Z X C V B          N M . , /  SHFT

- laser2001, manager: cassette format is incompatible with crvision.
- manager: appears the joystick is 4-way only.
- crvision: if you get "ERROR 00" while loading a tape, you can continue to load the remainder of it with CLOAD or CRUN.
            The bad line(s) will be missing. (behaviour depends on version of Basic)

TODO:
- manager: paste can lose a character or 2 at the start of a line
- crvision: paste is very poor
- crvision: in natural key mode & in paste, shifted characters do not appear.
- crvision: there is a buzzing noise while carts are running.

*/

#include "emu.h"
#include "crvision.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "utf8.h"


/***************************************************************************
    MEMORY MAPS
***************************************************************************/

/*-------------------------------------------------
    ADDRESS_MAP( crvision_map )
-------------------------------------------------*/

void crvision_state::crvision_map(address_map &map)
{
	map(0x0000, 0x03ff).mirror(0x0c00).ram();
	map(0x1000, 0x1003).mirror(0x0ffc).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x2000, 0x2001).mirror(0x0ffe).r(TMS9929_TAG, FUNC(tms9928a_device::read));
	map(0x3000, 0x3001).mirror(0x0ffe).w(TMS9929_TAG, FUNC(tms9928a_device::write));
	map(0x4000, 0x7fff).rom().share(BANK_ROM2);
	map(0x8000, 0xbfff).rom().share(BANK_ROM1);
//  map(0xc000, 0xe7ff).bankrw(3);
	map(0xe800, 0xe800).w(m_cent_data_out, FUNC(output_latch_device::write));
	map(0xe801, 0xe801).r("cent_status_in", FUNC(input_buffer_device::read));
	map(0xe801, 0xe801).w("cent_ctrl_out", FUNC(output_latch_device::write));
//  map(0xe802, 0xf7ff).bankrw(4);
	map(0xf800, 0xffff).rom().region(M6502_TAG, 0);
}

/*-------------------------------------------------
    ADDRESS_MAP( lasr2001_map )
-------------------------------------------------*/

void laser2001_state::lasr2001_map(address_map &map)
{
	map(0x0000, 0x03ff).mirror(0x0c00).ram();
	map(0x1000, 0x1003).mirror(0x0ffc).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x2000, 0x2001).mirror(0x0ffe).r(TMS9929_TAG, FUNC(tms9928a_device::read));
	map(0x3000, 0x3001).mirror(0x0ffe).w(TMS9929_TAG, FUNC(tms9928a_device::write));
	map(0x4000, 0x7fff).ram().share(BANK_ROM2);
	map(0x8000, 0xbfff).ram().share(BANK_ROM1);
	map(0xc000, 0xffff).rom().region(M6502_TAG, 0);
}

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

/*-------------------------------------------------
    INPUT_CHANGED_MEMBER( trigger_nmi )
-------------------------------------------------*/

INPUT_CHANGED_MEMBER( crvision_state::trigger_nmi )
{
	m_maincpu->set_input_line(m6502_device::NMI_LINE, newval ? ASSERT_LINE : CLEAR_LINE);
}

/*-------------------------------------------------
    INPUT_PORTS( crvision )
    Each joystick has 8 direction pads. Further,
    by activating 2 adjacent pads at once, 16
    directions can be obtained. BASIC only handles
    the 8 pads. Direction codes per the manual:
    0 - no direction
    1 - down
    2 - down/right
    3 - right
    4 - up/right
    5 - up
    6 - up/left
    7 - left
    8 - down/left
    when using PRINT JOY(1) [or JOY(2)].
    As you can see, there are multiple choices as
    which input to choose. I've taken a guess; if
    it turns out to be wrong use another option.
-------------------------------------------------*/

static INPUT_PORTS_START( crvision )
	// Player 1 Joystick

	PORT_START("PA0.0")
	//PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICKRIGHT_DOWN ) PORT_16WAY // 2
	//PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_16WAY // 3
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA0.1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICKRIGHT_DOWN ) PORT_16WAY PORT_CODE(KEYCODE_PGDN) // 2
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_16WAY // 1
	//PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_16WAY // 1
	//PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_16WAY // 1
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA0.2")
	//PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_16WAY // 3
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_16WAY // 3
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICKRIGHT_UP ) PORT_16WAY PORT_CODE(KEYCODE_PGUP) // 4
	//PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_16WAY // 3
	PORT_BIT( 0xb3, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA0.3")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_16WAY // 5
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICKLEFT_UP ) PORT_16WAY PORT_CODE(KEYCODE_HOME) // 6
	//PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_16WAY // 5
	//PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_16WAY // 5
	PORT_BIT( 0xe7, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA0.4")
	//PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICKLEFT_UP ) PORT_16WAY // 6
	//PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_16WAY // 7
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA0.5")
	//PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_16WAY // 7
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_16WAY // 7
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICKLEFT_DOWN ) PORT_16WAY PORT_CODE(KEYCODE_END) // 8
	//PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_16WAY // 7
	PORT_BIT( 0x9f, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA0.6")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA0.7")
	PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("P1 Button 2 / CNT'L") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)

	// Player 1 Keyboard

	PORT_START("PA1.0")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x81, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA1.1")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x83, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA1.2")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x87, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA1.3")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x8f, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA1.4")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x9f, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA1.5")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0xbf, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA1.6")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA1.7")
	PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("P1 Button 1 / SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)

	// Player 2 Joystick

	PORT_START("PA2.0")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA2.1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICKRIGHT_DOWN ) PORT_16WAY PORT_PLAYER(2) PORT_CODE(KEYCODE_3_PAD) // 2
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_16WAY PORT_PLAYER(2) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA2.2")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_16WAY PORT_PLAYER(2) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICKRIGHT_UP ) PORT_16WAY PORT_PLAYER(2) PORT_CODE(KEYCODE_9_PAD) // 4
	PORT_BIT( 0xb3, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA2.3")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_16WAY PORT_PLAYER(2) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICKLEFT_UP ) PORT_16WAY PORT_PLAYER(2) PORT_CODE(KEYCODE_7_PAD) // 6
	PORT_BIT( 0xe7, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA2.4")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA2.5")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_16WAY PORT_PLAYER(2) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICKLEFT_DOWN ) PORT_16WAY PORT_PLAYER(2) PORT_CODE(KEYCODE_1_PAD) // 8
	PORT_BIT( 0x9f, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA2.6")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA2.7")
	PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME(u8"P2 Button 2 / \u2192") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9) PORT_PLAYER(2) // U+2192 = →

	// Player 2 Keyboard

	PORT_START("PA3.0")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RET'N") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT( 0x81, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA3.1")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('@')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x83, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA3.2")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x87, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA3.3")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x8f, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA3.4")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x9f, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA3.5")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0xbf, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA3.6")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PA3.7")
	PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("P2 Button 1 / - =") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=') PORT_PLAYER(2)

	PORT_START("NMI")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START ) PORT_NAME("Reset") PORT_CODE(KEYCODE_F10) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(crvision_state::trigger_nmi), 0)
INPUT_PORTS_END

/*-------------------------------------------------
    INPUT_PORTS( manager )
-------------------------------------------------*/

static INPUT_PORTS_START( manager )
	PORT_START("Y.0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR('\r')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('-') PORT_CHAR('#')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')

	PORT_START("Y.1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"Ä  ä") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(U'Ä') PORT_CHAR(U'ä') PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"') PORT_CHAR('@')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')

	PORT_START("Y.2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED) // I
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"Å  å") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(U'Å') PORT_CHAR(U'å') PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("Y.3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED) // N
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED) // X
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"Ö  ö") PORT_CODE(KEYCODE_COLON) PORT_CHAR(U'Ö') PORT_CHAR(U'ö') PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')

	PORT_START("Y.4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')

	PORT_START("Y.5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED) // J
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l') PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('=')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')

	PORT_START("Y.6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')

	PORT_START("Y.7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED) // N
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')

	PORT_START("JOY.0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON2 )

	PORT_START("JOY.1")
	PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON1 )

	PORT_START("JOY.2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)

	PORT_START("JOY.3")
	PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)

	PORT_START("NMI")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START ) PORT_NAME("Reset") PORT_CODE(KEYCODE_F10) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(crvision_state::trigger_nmi), 0)
INPUT_PORTS_END

/***************************************************************************
    DEVICE CONFIGURATION
***************************************************************************/

void crvision_state::pia_pa_w(uint8_t data)
{
	/*
	    Signal  Description

	    PA0     Keyboard raster player 1 output (joystick)
	    PA1     Keyboard raster player 1 output (hand keys)
	    PA2     Keyboard raster player 2 output (joystick)
	    PA3     Keyboard raster player 2 output (hand keys)
	    PA4     ?
	    PA5     ?
	    PA6     Cassette motor
	    PA7     Cassette data in/out
	*/

	/* keyboard raster */
	m_keylatch = ~data & 0x0f;

	/* cassette motor */
	m_cassette->change_state(BIT(data,6) ? CASSETTE_MOTOR_DISABLED : CASSETTE_MOTOR_ENABLED, CASSETTE_MASK_MOTOR);

	/* cassette data output */
	m_cassette->output( BIT(data, 7) ? +1.0 : -1.0);
}

uint8_t crvision_state::read_keyboard(u8 pa)
{
	uint8_t value = 0xff;

	// Get shift/ctrl keys
	u8 modifier = m_io_keypad[pa][7]->read() & 0x80;

	// Get normal keys/joystick
	for (u8 i = 0; i < 6; i++)
	{
		value = m_io_keypad[pa][i]->read() & 0x7f;

		if (value < 0x7f)
		{
			if (value == 0x7f - (1 << i))
				break;
			else
			{
				value -= (1 << i);
				break;
			}
		}
	}

	value |= modifier;

	//if (value < 0xff) printf("%01X%02X ",pa,value);
	return value;
}

uint8_t crvision_state::pia_pa_r()
{
	/*
	    PA0     Keyboard raster player 1 output (joystick)
	    PA1     Keyboard raster player 1 output (hand keys)
	    PA2     Keyboard raster player 2 output (joystick)
	    PA3     Keyboard raster player 2 output (hand keys)
	    PA4     ?
	    PA5     ?
	    PA6     Cassette motor
	    PA7     Cassette data in/out
	*/

	uint8_t data = 0x7f;

	if ((m_cassette)->input() > -0.1469) data |= 0x80;

	return data;
}

uint8_t crvision_state::pia_pb_r()
{
	/*
	    Signal  Description

	    PB0     Keyboard input
	    PB1     Keyboard input
	    PB2     Keyboard input
	    PB3     Keyboard input
	    PB4     Keyboard input
	    PB5     Keyboard input
	    PB6     Keyboard input
	    PB7     Keyboard input
	*/

	uint8_t data = 0xff;

	for (u8 i = 0; i < 4; i++)
		if (BIT(m_keylatch, i))
			data &= read_keyboard(i);

	return data;
}

uint8_t laser2001_state::pia_pa_r()
{
	/*
	    Signal  Description

	    PA0     Keyboard column 0
	    PA1     Keyboard column 1
	    PA2     Keyboard column 2
	    PA3     Keyboard column 3
	    PA4     Keyboard column 4
	    PA5     Keyboard column 5
	    PA6     Keyboard column 6
	    PA7     Keyboard column 7
	*/

	uint8_t data = 0xff;

	for (u8 i = 0; i < 8; i++)
		if (!BIT(m_keylatch, i))
			data &= m_inp_y[i]->read();

	return data;
}

void laser2001_state::pia_pa_w(uint8_t data)
{
	/*
	    PA0     Joystick player 1 output 0
	    PA1     Joystick player 1 output 1
	    PA2     Joystick player 2 output 0
	    PA3     Joystick player 2 output 1
	    PA4     ?
	    PA5     ?
	    PA6     ?
	    PA7     ?
	*/

	m_joylatch = data;
}

uint8_t laser2001_state::pia_pb_r()
{
	uint8_t data = 0xff;

	for (u8 i = 0; i < 4; i++)
		if (~m_joylatch >> i & 1)
			data &= m_inp_joy[i]->read();

	return data;
}

void laser2001_state::pia_pb_w(uint8_t data)
{
	/*
	    Signal  Description

	    PB0     Keyboard row 0, PSG data 7, centronics data 0
	    PB1     Keyboard row 1, PSG data 6, centronics data 1
	    PB2     Keyboard row 2, PSG data 5, centronics data 2
	    PB3     Keyboard row 3, PSG data 4, centronics data 3
	    PB4     Keyboard row 4, PSG data 3, centronics data 4
	    PB5     Keyboard row 5, PSG data 2, centronics data 5
	    PB6     Keyboard row 6, PSG data 1, centronics data 6
	    PB7     Keyboard row 7, PSG data 0, centronics data 7
	*/

	/* keyboard latch */
	m_keylatch = data;

	/* centronics data */
	m_cent_data_out->write(data);
}

int laser2001_state::pia_ca1_r()
{
	return (m_cassette->input() > 0.16) ? 0 : 1;
}

void laser2001_state::pia_ca2_w(int state)
{
	m_cassette->output(state ? +1.0 : -1.0);
}


void laser2001_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
	m_pia->cb1_w(pia_cb1_r());
}

void laser2001_state::write_psg_ready(int state)
{
	m_psg_ready = state;
	m_pia->cb1_w(pia_cb1_r());
}

int laser2001_state::pia_cb1_r()
{
	/* actually this is a diode-AND (READY & _BUSY), but ctronics.c returns busy status if no device is mounted -> Manager won't boot */
	return m_psg_ready && (!m_centronics_busy || m_pia->ca2_output_z());
}

void laser2001_state::pia_cb2_w(int state)
{
	if (m_pia->ca2_output_z())
	{
		if (!state) m_psg->write(m_keylatch);
	}
	else
	{
		m_centronics->write_strobe(state);
	}
}

/***************************************************************************
    MACHINE INITIALIZATION
***************************************************************************/

void crvision_state::machine_start()
{
	// zerofill/state saving
	m_keylatch = 0;
	save_item(NAME(m_keylatch));

	if (m_cart->exists())
	{
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x4000, 0x7fff, read8sm_delegate(*m_cart, FUNC(crvision_cart_slot_device::read_rom40)));
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x8000, 0xbfff, read8sm_delegate(*m_cart, FUNC(crvision_cart_slot_device::read_rom80)));
	}
}

void laser2001_state::machine_start()
{
	crvision_state::machine_start();

	// zerofill/state saving
	m_joylatch = 0;
	m_centronics_busy = 0;
	m_psg_ready = 0;

	save_item(NAME(m_joylatch));
	save_item(NAME(m_centronics_busy));
	save_item(NAME(m_psg_ready));
}


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

static void crvision_cart(device_slot_interface &device)
{
	device.option_add_internal("crv_rom4k",  CRVISION_ROM_4K);
	device.option_add_internal("crv_rom6k",  CRVISION_ROM_6K);
	device.option_add_internal("crv_rom8k",  CRVISION_ROM_8K);
	device.option_add_internal("crv_rom10k", CRVISION_ROM_10K);
	device.option_add_internal("crv_rom12k", CRVISION_ROM_12K);
	device.option_add_internal("crv_rom16k", CRVISION_ROM_16K);
	device.option_add_internal("crv_rom18k", CRVISION_ROM_18K);
}

/*-------------------------------------------------
    creativision machine configuration
-------------------------------------------------*/

void crvision_state::creativision(machine_config &config)
{
	// basic machine hardware
	M6502(config, m_maincpu, XTAL(2'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &crvision_state::crvision_map);

	// devices
	PIA6821(config, m_pia);
	m_pia->readpa_handler().set(FUNC(crvision_state::pia_pa_r));
	m_pia->readpb_handler().set(FUNC(crvision_state::pia_pb_r));
	m_pia->writepa_handler().set(FUNC(crvision_state::pia_pa_w));
	m_pia->writepb_handler().set(SN76489_TAG, FUNC(sn76496_base_device::write));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SN76489A(config, m_psg, XTAL(2'000'000));
	m_psg->ready_cb().set(m_pia, FUNC(pia6821_device::cb1_w));
	m_psg->add_route(ALL_OUTPUTS, "mono", 1.00);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set("cent_status_in", FUNC(input_buffer_device::write_bit7));

	INPUT_BUFFER(config, "cent_status_in", 0);

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	OUTPUT_LATCH(config, "cent_ctrl_out").bit_handler<4>().set(m_centronics, FUNC(centronics_device::write_strobe));

	// cartridge
	CRVISION_CART_SLOT(config, m_cart, crvision_cart, nullptr);

	// internal ram
	RAM(config, m_ram);
	m_ram->set_default_size("1K"); // main RAM
	m_ram->set_extra_options("15K"); // 16K expansion (lower 14K available only, upper 2K shared with BIOS ROM)

	// software lists
	SOFTWARE_LIST(config, "cart_list").set_original("crvision");
}

/*-------------------------------------------------
    machine_config( ntsc )
-------------------------------------------------*/

void crvision_state::ntsc(machine_config &config)
{
	creativision(config);
	// video hardware
	tms9918_device &vdp(TMS9918(config, TMS9929_TAG, XTAL(10'738'635)));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	vdp.int_callback().set_inputline(M6502_TAG, m6502_device::IRQ_LINE);
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);
}

/*-------------------------------------------------
    machine_config( pal )
-------------------------------------------------*/

void crvision_pal_state::pal(machine_config &config)
{
	creativision(config);
	// video hardware
	tms9929_device &vdp(TMS9929(config, TMS9929_TAG, XTAL(10'738'635)));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	vdp.int_callback().set_inputline(M6502_TAG, m6502_device::IRQ_LINE);
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);
}

/*-------------------------------------------------
    machine_config( lasr2001 )
-------------------------------------------------*/

void laser2001_state::lasr2001(machine_config &config)
{
	// basic machine hardware
	M6502(config, m_maincpu, XTAL(17'734'470)/9);
	m_maincpu->set_addrmap(AS_PROGRAM, &laser2001_state::lasr2001_map);

	// devices
	PIA6821(config, m_pia);
	m_pia->readpa_handler().set(FUNC(laser2001_state::pia_pa_r));
	m_pia->readpb_handler().set(FUNC(laser2001_state::pia_pb_r));
	m_pia->readca1_handler().set(FUNC(laser2001_state::pia_ca1_r));
	m_pia->writepa_handler().set(FUNC(laser2001_state::pia_pa_w));
	m_pia->writepb_handler().set(FUNC(laser2001_state::pia_pb_w));
	m_pia->ca2_handler().set(FUNC(laser2001_state::pia_ca2_w));
	m_pia->cb2_handler().set(FUNC(laser2001_state::pia_cb2_w));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SN76489A(config, m_psg, XTAL(17'734'470)/9);
	m_psg->ready_cb().set(FUNC(laser2001_state::write_psg_ready));
	m_psg->add_route(ALL_OUTPUTS, "mono", 1.00);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(laser2001_state::write_centronics_busy));

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	// video hardware
	tms9929a_device &vdp(TMS9929A(config, TMS9929_TAG, XTAL(10'738'635)));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	vdp.int_callback().set_inputline(M6502_TAG, m6502_device::IRQ_LINE);
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	// cartridge
	CRVISION_CART_SLOT(config, m_cart, crvision_cart, nullptr);

	// internal ram
	RAM(config, m_ram);
	m_ram->set_default_size("16K");
	m_ram->set_extra_options("32K");

	// software list
	SOFTWARE_LIST(config, "cart_list").set_original("crvision");
	SOFTWARE_LIST(config, "cart_list2").set_original("laser2001_cart");
	SOFTWARE_LIST(config, "flop_list").set_original("laser2001_flop");
}

/***************************************************************************
    ROMS
***************************************************************************/

ROM_START( crvision )
	ROM_REGION( 0x10000, M6502_TAG, 0 )
	ROM_LOAD( "crvision.u20", 0x0000, 0x0800, CRC(c3c590c6) SHA1(5ac620c529e4965efb5560fe824854a44c983757) )
ROM_END

ROM_START( fnvision )
	ROM_REGION( 0x10000, M6502_TAG, 0 )
	ROM_LOAD( "funboot.rom",  0x0000, 0x0800, CRC(05602697) SHA1(c280b20c8074ba9abb4be4338b538361dfae517f) )
ROM_END

#define rom_wizzard rom_crvision
#define rom_crvisioj rom_crvision
#define rom_crvisio2 rom_crvision
#define rom_rameses rom_fnvision
#define rom_vz2000 rom_fnvision

ROM_START( lasr2001 )
	ROM_REGION( 0x10000, M6502_TAG, 0 )
	ROM_LOAD( "laser2001.rom", 0x0000, 0x4000, CRC(4dc35c39) SHA1(c12e098c14ac0724869053df2b63277a3e413802) )
ROM_END

ROM_START( manager )
	ROM_REGION( 0x10000, M6502_TAG, 0 )
	ROM_LOAD( "01", 0x0000, 0x2000, CRC(702f4cf5) SHA1(cd14ee74e787d24b76c166de484dae24206e219b) )
	ROM_LOAD( "23", 0x2000, 0x2000, CRC(46489d88) SHA1(467f5bcd62d0b4117c443e13373df8f3c45df7b2) )
ROM_END

/***************************************************************************
    SYSTEM DRIVERS
***************************************************************************/

//    YEAR  NAME      PARENT    COMPAT  MACHINE   INPUT     CLASS               INIT        COMPANY                   FULLNAME                       FLAGS
CONS( 1982, crvision, 0,        0,      pal,      crvision, crvision_pal_state, empty_init, "Video Technology",       "CreatiVision",                MACHINE_SUPPORTS_SAVE )
CONS( 1982, fnvision, crvision, 0,      pal,      crvision, crvision_pal_state, empty_init, "Video Technology",       "FunVision",                   MACHINE_SUPPORTS_SAVE )
CONS( 1982, crvisioj, crvision, 0,      ntsc,     crvision, crvision_state,     empty_init, "Cheryco",                "CreatiVision (Japan)",        MACHINE_SUPPORTS_SAVE )
CONS( 1982, wizzard,  crvision, 0,      pal,      crvision, crvision_pal_state, empty_init, "Dick Smith Electronics", "Wizzard (Oceania)",           MACHINE_SUPPORTS_SAVE )
CONS( 1982, rameses,  crvision, 0,      pal,      crvision, crvision_pal_state, empty_init, "Hanimex",                "Rameses HVC6502 (Oceania)",   MACHINE_SUPPORTS_SAVE )
CONS( 1983, vz2000,   crvision, 0,      pal,      crvision, crvision_pal_state, empty_init, "Dick Smith Electronics", "VZ 2000 (Oceania)",           MACHINE_SUPPORTS_SAVE )
CONS( 1983, crvisio2, crvision, 0,      pal,      crvision, crvision_pal_state, empty_init, "Video Technology",       "CreatiVision MK-II (Europe)", MACHINE_SUPPORTS_SAVE )
COMP( 1983, lasr2001, 0,        0,      lasr2001, manager,  laser2001_state,    empty_init, "Video Technology",       "Laser 2001",                  MACHINE_SUPPORTS_SAVE )
//COMP( 1983, vz2001,   lasr2001, 0,      lasr2001, lasr2001, laser2001_state,    empty_init, "Dick Smith Electronics", "VZ 2001 (Oceania)",           MACHINE_SUPPORTS_SAVE )
COMP( 1983, manager,  0,        0,      lasr2001, manager, laser2001_state,     empty_init, "Salora",                 "Manager (Finland)",           MACHINE_SUPPORTS_SAVE )



csc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Kevin Horton, Jonathan Gevaryahu, Sandro Ronco, hap
// thanks-to:Berger, yoyo_chessboard
/*******************************************************************************

Fidelity CSC(and derived) hardware
- Champion Sensory Chess Challenger
- Elite Champion Challenger
- Super 9 Sensory Chess Challenger
- Reversi Sensory Challenger

TODO:
- do ca1_w/cb1_w better, it's annoying since it's going through sensorboard_device
  (it works fine though, since PIA interrupts are not connected)
- hook up csce I/O properly, it doesn't have PIAs
- verify super9cc maskrom dump

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

Champion Sensory Chess Challenger (CSC)
---------------------------------------
RE notes by Kevin Horton

Memory map:
-----------
0000-07FF: 2K of RAM
0800-0FFF: 1K of RAM (note: mirrored twice)
1000-17FF: PIA 1 (display, TSI speech chip)
1800-1FFF: PIA 0 (keypad, LEDs)
2000-3FFF: 101-64019 or 101-1025A04 ROM*
4000-7FFF: mirror of 0000-3FFF
8000-9FFF: not used
A000-BFFF: 101-1025A03 ROM (A12 tied high)
C000-DFFF: 101-1025A02 ROM
E000-FDFF: 101-1025A01 ROM
FE00-FFFF: 512 byte 74S474 or N82S141N PROM

*: 101-64019 is also used on the VSC(vsc.cpp). It contains the opening book
and "64 greatest games", as well as some Z80 code. Obviously the latter is unused
on the CSC. Also seen with 101-1025A04 label, same ROM contents.

Labels with 1024A0x instead of 1025A0x were also found, with the same ROM contents.
101-1025A03 might be optional, one (untampered) Spanish PCB was seen with a socket
instead of this ROM. Most of the opening book is in here.

PCB label: 510-1326B01
CPU is a 6502 running at 1.95MHz (3.9MHz resonator, divided by 2)

NMI is not used.
IRQ is connected to a 600Hz oscillator (38.4KHz divided by 64).
Reset is connected to a power-on reset circuit.

PIA 1:
------
PA0 - 7seg segments E, TSI A0
PA1 - 7seg segments D, TSI A1
PA2 - 7seg segments C, TSI A2
PA3 - 7seg segments H, TSI A3
PA4 - 7seg segments G, TSI A4
PA5 - 7seg segments F, TSI A5
PA6 - 7seg segments B
PA7 - 7seg segments A

PB0 - A12 on speech ROM (if used... not used on this model, ROM is 4K)
PB1 - START line on TSI
PB2 - white wire
PB3 - BUSY line from TSI
PB4 - hi/lo TSI speaker volume
PB5 - button row 9
PB6 - selection jumper (resistor to 5V)
PB7 - selection jumper (resistor to ground)

CA1 - NC
CA2 - violet wire

CB1 - NC
CB2 - NC (connects to pin 14 of soldered connector)

PIA 0:
------
PA0 - button row 1
PA1 - button row 2
PA2 - button row 3
PA3 - button row 4
PA4 - button row 5
PA5 - button row 6
PA6 - 7442 selector bit 0
PA7 - 7442 selector bit 1

PB0 - LED row 1
PB1 - LED row 2
PB2 - LED row 3
PB3 - LED row 4
PB4 - LED row 5
PB5 - LED row 6
PB6 - LED row 7
PB7 - LED row 8

CA1 - button row 7
CA2 - selector bit 3

CB1 - button row 8
CB2 - selector bit 2

Selector: (attached to PIA 0, outputs 1 of 10 pins low. 7442)
---------
output # (selected turns this column on, and all others off)
0 - LED column A, button column A, 7seg digit 1
1 - LED column B, button column B, 7seg digit 2
2 - LED column C, button column C, 7seg digit 3
3 - LED column D, button column D, 7seg digit 4
4 - LED column E, button column E
5 - LED column F, button column F
6 - LED column G, button column G
7 - LED column H, button column H
8 - button column I
9 - Tone line (toggle to make a tone in the buzzer)

The rows/columns are indicated on the game board:

 ABCDEFGH   I
--------------
|            | 8
|            | 7
|            | 6
|            | 5
|            | 4
|            | 3
|            | 2
|            | 1
--------------

The "lone LED" is above the control column.
column I is the "control column" on the right for starting a new game, etc.

The upper 6 buttons are connected as such:

column A - speak
column B - RV
column C - TM
column D - LV
column E - DM
column F - ST

these 6 buttons use row 9 (connects to PIA 1)

LED display:
------------
43 21 (digit number)
-----
88:88

The LED display is four 7 segment digits. Normal ABCDEFG lettering is used for segments.

The upper dot is connected to digit 3 common
The lower dot is connected to digit 4 common
The lone LED is connected to digit 1 common

All three of the above are called "segment H".

Printer:
--------
It's the same printer port as VSC. It expects a baud rate of 600, 7 data bits,
1 stop bit, and no parity.

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

Elite Champion Challenger (ELITE)
This is a limited-release chess computer based on the CSC. They removed the PIAs
and did the I/O with TTL instead (PIAs will still work from software point of view).
---------------------------------
PCB label: 510-1041B01
MPS 6502C CPU @ 4MHz
20KB total ROM size, 4KB RAM(8*HM6147P)

The "Fidelity X" that won the 1981 Travemünde contest is also on this hardware, with
a 5MHz CPU and 32KB total ROM size. In the 90s, Wilfried Bucke provided an upgrade
kit for csce to make it similar to this version, CPU was changed to a R65C02P4.

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

Super 9 Sensory Chess Challenger (SU9/DS9)
This is basically the Fidelity Elite A/S program on CSC hardware.
Model DS9(Deluxe) has a 5MHz XTAL, but is otherwise same.
Septennial(SCC) is the same as well, but clocked even higher.
---------------------------------
R6502AP CPU, 1.95MHz(3.9MHz resonator)
2 RAM chips (2KB + 1KB)
2*8KB ROM + 1*2KB ROM
built-in CB9 module

See CSC description above for more information.

Like with EAS, the new game command for SU9 is: RE -> D6 (or D8) -> CL.

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

Reversi Sensory Challenger (RSC)
The 1st version came out in 1980, a program revision was released in 1981.
Another distinction is the board color and layout, the 1981 version is green.
Not sure if the 1st version was even released, or just a prototype.
---------------------------------
PCB label: 510-1035A01
MOS MPS 6502B CPU, frequency unknown
MOS MPS 6520 PIA, I/O is nearly same as CSC's PIA 0
1KB RAM(2*2114), 4KB ROM
8*(8+1) buttons, 8*8+1 LEDs

To play it on MAME with the sensorboard device, it is recommended to set up
keyboard shortcuts for the spawn inputs. Then hold the spawn input down while
clicking on the game board. Alternatively, it's also possible to flip pieces
that are on the board by clicking while holding ALT.

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

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/m6502/m6502.h"
#include "machine/6821pia.h"
#include "machine/clock.h"
#include "machine/sensorboard.h"
#include "sound/s14001a.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "fidel_csc.lh"
#include "fidel_rsc.lh"
#include "fidel_su9.lh"


namespace {

class csc_state : public driver_device
{
public:
	csc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_pia(*this, "pia%u", 0),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_speech(*this, "speech"),
		m_language(*this, "language"),
		m_inputs(*this, "IN.%u", 0),
		m_board_inp(*this, "BOARD")
	{ }

	// machine drivers
	void csc(machine_config &config);
	void csce(machine_config &config);
	void cscet(machine_config &config);
	void su9(machine_config &config);
	void rsc(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(su9_change_cpu_freq);
	DECLARE_INPUT_CHANGED_MEMBER(rsc_init_board);

protected:
	virtual void machine_start() override ATTR_COLD;

	// devices/pointers
	required_device<cpu_device> m_maincpu;
	optional_device_array<pia6821_device, 2> m_pia;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	optional_device<s14001a_device> m_speech;
	optional_region_ptr<u8> m_language;
	optional_ioport_array<9> m_inputs;
	optional_ioport m_board_inp;

	u8 m_led_data = 0;
	u8 m_7seg_data = 0;
	u8 m_inp_mux = 0;

	// address maps
	void csc_map(address_map &map) ATTR_COLD;
	void csce_map(address_map &map) ATTR_COLD;
	void rsc_map(address_map &map) ATTR_COLD;

	u8 rsc_board_sensor_cb(offs_t offset);

	// I/O handlers
	u16 read_inputs();
	void update_inputs();
	void update_display();
	void update_sound();

	u8 pia0_read(offs_t offset);
	void pia0_write(offs_t offset, u8 data);
	void pia0_pa_w(u8 data);
	void pia0_pb_w(u8 data);
	u8 pia0_pa_r();
	void pia0_ca2_w(int state);
	void pia0_cb2_w(int state);
	void pia1_pa_w(u8 data);
	void pia1_pb_w(u8 data);
	u8 pia1_pb_r();
};

void csc_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_led_data));
	save_item(NAME(m_7seg_data));
	save_item(NAME(m_inp_mux));
}

INPUT_CHANGED_MEMBER(csc_state::su9_change_cpu_freq)
{
	// SU9 CPU is clocked 1.95MHz, DS9 is 2.5MHz, SCC is 3MHz
	static const XTAL xtal[3] = { 3.9_MHz_XTAL/2, 5_MHz_XTAL/2, 3_MHz_XTAL };
	m_maincpu->set_unscaled_clock(xtal[newval % 3]);
}



/*******************************************************************************
    Sensorboard
*******************************************************************************/

INPUT_CHANGED_MEMBER(csc_state::rsc_init_board)
{
	if (!newval)
		return;

	m_board->cancel_sensor();
	m_board->cancel_hand();
	m_board->clear_board();

	// 2 possible initial board positions
	if (param)
	{
		m_board->write_piece(3, 3, 2);
		m_board->write_piece(4, 3, 1);
		m_board->write_piece(3, 4, 2);
		m_board->write_piece(4, 4, 1);
	}
	else
	{
		m_board->write_piece(3, 3, 1);
		m_board->write_piece(4, 3, 2);
		m_board->write_piece(3, 4, 2);
		m_board->write_piece(4, 4, 1);
	}

	m_board->refresh();
}

u8 csc_state::rsc_board_sensor_cb(offs_t offset)
{
	if (~m_board_inp->read() & 4)
		return 0;

	u8 x = offset & 0xf;
	u8 y = offset >> 4 & 0xf;
	u8 piece = m_board->read_piece(x, y);

	// flip piece
	if (piece && offset != m_board->get_handpos())
		m_board->write_piece(x, y, (piece & 1) + 1);

	return 3;
}



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

// misc handlers

u16 csc_state::read_inputs()
{
	u16 data = 0;

	// read (chess)board sensors
	if (m_inp_mux < 8)
		data = m_board->read_file(m_inp_mux);

	// read other buttons
	if (m_inp_mux < 9)
		data |= m_inputs[m_inp_mux].read_safe(0);

	return ~data;
}

void csc_state::update_inputs()
{
	// PIA 0 CA1/CB1: button row 6/7
	if (!machine().side_effects_disabled())
	{
		m_pia[0]->ca1_w(BIT(read_inputs(), 6));
		m_pia[0]->cb1_w(BIT(read_inputs(), 7));
	}
}

void csc_state::update_display()
{
	// 7442 0-8: led select (also input mux)
	// 7seg leds+H (not on all models), 8*8(+1) chessboard leds
	m_display->matrix(1 << m_inp_mux, m_led_data << 8 | m_7seg_data);
}

void csc_state::update_sound()
{
	// 7442 9: speaker out
	m_dac->write(BIT(1 << m_inp_mux, 9));
}


// 6821 PIA 0

u8 csc_state::pia0_read(offs_t offset)
{
	update_inputs();
	return m_pia[0]->read(offset);
}

void csc_state::pia0_write(offs_t offset, u8 data)
{
	update_inputs();
	m_pia[0]->write(offset, data);
}

u8 csc_state::pia0_pa_r()
{
	// d0-d5: button row 0-5
	return (read_inputs() & 0x3f) | 0xc0;
}

void csc_state::pia0_pa_w(u8 data)
{
	// d6,d7: 7442 A0,A1
	m_inp_mux = (m_inp_mux & ~3) | (data >> 6 & 3);
	update_display();
	update_sound();
}

void csc_state::pia0_pb_w(u8 data)
{
	// d0-d7: led row data
	m_led_data = data;
	update_display();
}

void csc_state::pia0_cb2_w(int state)
{
	// 7442 A2
	m_inp_mux = (m_inp_mux & ~4) | (state ? 4 : 0);
	update_display();
	update_sound();
}

void csc_state::pia0_ca2_w(int state)
{
	// 7442 A3
	m_inp_mux = (m_inp_mux & ~8) | (state ? 8 : 0);
	update_display();
	update_sound();
}


// 6821 PIA 1

void csc_state::pia1_pa_w(u8 data)
{
	// d0-d5: S14001A C0-C5
	m_speech->data_w(data & 0x3f);

	// d0-d7: data for the 4 7seg leds, bits are ABFGHCDE (H is extra led)
	m_7seg_data = bitswap<8>(data,0,1,5,6,7,2,3,4);
	update_display();
}

void csc_state::pia1_pb_w(u8 data)
{
	// d0: speech ROM A12
	m_speech->set_rom_bank(data & 1);

	// d1: S14001A start pin
	m_speech->start_w(BIT(data, 1));

	// d4: lower S14001A volume
	m_speech->set_output_gain(0, (data & 0x10) ? 0.25 : 1.0);
}

u8 csc_state::pia1_pb_r()
{
	// d2: printer busy?
	u8 data = 0x04;

	// d3: S14001A busy pin
	if (m_speech->busy_r())
		data |= 0x08;

	// d5: button row 8
	data |= (read_inputs() >> 3 & 0x20);

	// d6,d7: language jumpers (hardwired)
	return data | (*m_language << 6 & 0xc0);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void csc_state::csc_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).mirror(0x4000).ram();
	map(0x0800, 0x0bff).mirror(0x4400).ram();
	map(0x1000, 0x1003).mirror(0x47fc).rw(m_pia[1], FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x1800, 0x1803).mirror(0x47fc).rw(FUNC(csc_state::pia0_read), FUNC(csc_state::pia0_write));
	map(0x2000, 0x3fff).mirror(0x4000).rom();
	map(0xa000, 0xafff).mirror(0x1000).rom();
	map(0xc000, 0xffff).rom();
}

void csc_state::csce_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).ram();
	map(0x1000, 0x1003).rw(m_pia[1], FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x1800, 0x1803).rw(FUNC(csc_state::pia0_read), FUNC(csc_state::pia0_write));
	map(0x2000, 0x3fff).rom();
	map(0xa000, 0xffff).rom();
}

void csc_state::rsc_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x03ff).ram();
	map(0x2000, 0x2003).rw(FUNC(csc_state::pia0_read), FUNC(csc_state::pia0_write));
	map(0xf000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( csc )
	PORT_START("IN.0")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_SPACE) PORT_NAME("Speaker")

	PORT_START("IN.1")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("RV")

	PORT_START("IN.2")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("TM")

	PORT_START("IN.3")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("LV")

	PORT_START("IN.4")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("DM")

	PORT_START("IN.5")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("ST")

	PORT_START("IN.8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Rook")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Knight")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("King")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CL")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_N) PORT_NAME("RE")
INPUT_PORTS_END

static INPUT_PORTS_START( su9 )
	PORT_INCLUDE( csc )

	PORT_MODIFY("IN.8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("RV / Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("DM / Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("TB / Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("LV / Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("PV / Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("PB / King")

	PORT_START("CPU")
	PORT_CONFNAME( 0x03, 0x00, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(csc_state::su9_change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "1.95MHz (original)" )
	PORT_CONFSETTING(    0x01, "2.5MHz (Deluxe)" )
	PORT_CONFSETTING(    0x02, "3MHz (Septennial)" )
INPUT_PORTS_END

static INPUT_PORTS_START( rsc )
	PORT_START("IN.8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("ST")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("RV")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("DM")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("CL")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("LV")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("PV")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_SPACE) PORT_NAME("Speaker")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_N) PORT_NAME("RE")

	PORT_START("BOARD")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(csc_state::rsc_init_board), 0) PORT_NAME("Board Reset A")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(csc_state::rsc_init_board), 1) PORT_NAME("Board Reset B")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT) PORT_NAME("Modifier 3 / Flip Piece")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void csc_state::csc(machine_config &config)
{
	// basic machine hardware
	M6502(config, m_maincpu, 3.9_MHz_XTAL/2); // from 3.9MHz resonator
	m_maincpu->set_addrmap(AS_PROGRAM, &csc_state::csc_map);

	auto &irq_clock(CLOCK(config, "irq_clock", 38.4_kHz_XTAL/64)); // through 4060 IC, 600Hz
	irq_clock.set_pulse_width(attotime::from_nsec(42750)); // measured ~42.75us
	irq_clock.signal_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	PIA6821(config, m_pia[0]);
	m_pia[0]->readpa_handler().set(FUNC(csc_state::pia0_pa_r));
	m_pia[0]->writepa_handler().set(FUNC(csc_state::pia0_pa_w));
	m_pia[0]->writepb_handler().set(FUNC(csc_state::pia0_pb_w));
	m_pia[0]->ca2_handler().set(FUNC(csc_state::pia0_ca2_w));
	m_pia[0]->cb2_handler().set(FUNC(csc_state::pia0_cb2_w));

	PIA6821(config, m_pia[1]);
	m_pia[1]->readpb_handler().set(FUNC(csc_state::pia1_pb_r));
	m_pia[1]->writepa_handler().set(FUNC(csc_state::pia1_pa_w));
	m_pia[1]->writepb_handler().set(FUNC(csc_state::pia1_pb_w));
	m_pia[1]->ca2_handler().set("rs232", FUNC(rs232_port_device::write_txd)).invert();

	RS232_PORT(config, "rs232", default_rs232_devices, nullptr);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(200));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 16);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_fidel_csc);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	S14001A(config, m_speech, 25000); // R/C circuit, around 25khz
	m_speech->add_route(ALL_OUTPUTS, "speaker", 0.75);

	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void csc_state::csce(machine_config &config)
{
	csc(config);

	m_maincpu->set_clock(4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &csc_state::csce_map);

	// shorter irq, 74LS221 (4.7K, 5nF), measured ~10.48us
	subdevice<clock_device>("irq_clock")->set_pulse_width(attotime::from_nsec(10480));
}

void csc_state::cscet(machine_config &config)
{
	csce(config);
	m_maincpu->set_clock(5_MHz_XTAL);
}

void csc_state::su9(machine_config &config)
{
	csc(config);
	config.set_default_layout(layout_fidel_su9);
}

void csc_state::rsc(machine_config &config)
{
	// basic machine hardware
	M6502(config, m_maincpu, 1'800'000); // measured approx 1.81MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &csc_state::rsc_map);

	auto &irq_clock(CLOCK(config, "irq_clock", 546)); // from 555 timer, measured
	irq_clock.set_pulse_width(attotime::from_usec(38)); // active for 38us
	irq_clock.signal_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	PIA6821(config, m_pia[0]); // MOS 6520
	m_pia[0]->readpa_handler().set(FUNC(csc_state::pia0_pa_r));
	m_pia[0]->writepa_handler().set(FUNC(csc_state::pia0_pa_w));
	m_pia[0]->writepb_handler().set(FUNC(csc_state::pia0_pb_w));
	m_pia[0]->ca2_handler().set(FUNC(csc_state::pia0_ca2_w));
	m_pia[0]->cb2_handler().set(FUNC(csc_state::pia0_cb2_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->sensor_cb().set(FUNC(csc_state::rsc_board_sensor_cb));
	m_board->set_spawnpoints(2);
	m_board->set_delay(attotime::from_msec(300));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 16);
	config.set_default_layout(layout_fidel_rsc);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( csc )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101-64019",   0x2000, 0x2000, CRC(08a3577c) SHA1(69fe379d21a9d4b57c84c3832d7b3e7431eec341) )
	ROM_LOAD("101-1025a03", 0xa000, 0x1000, CRC(63982c07) SHA1(5ed4356323d5c80df216da55994abe94ba4aa94c) )
	ROM_CONTINUE(           0xa000, 0x1000 ) // 1st half empty
	ROM_LOAD("101-1025a02", 0xc000, 0x2000, CRC(9e6e7c69) SHA1(4f1ed9141b6596f4d2b1217d7a4ba48229f3f1b0) )
	ROM_LOAD("101-1025a01", 0xe000, 0x2000, CRC(57f068c3) SHA1(7d2ac4b9a2fba19556782863bdd89e2d2d94e97b) )
	ROM_LOAD("74s474",      0xfe00, 0x0200, CRC(4511ba31) SHA1(e275b1739f8c3aa445cccb6a2b597475f507e456) )

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END


ROM_START( csce )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_FILL(          0x2000, 0x2000, 0xff) // unpopulated
	ROM_LOAD("orange", 0xa000, 0x1000, CRC(53348363) SHA1(8465cb15d7d25e7172150774bb1c38caed9c720d) ) // MCM2532C
	ROM_RELOAD(        0xb000, 0x1000)
	ROM_LOAD("blue",   0xc000, 0x0800, CRC(49a915c8) SHA1(cfc04dbc2bc780297e5fd24f756c3d1e635b25e6) ) // N82S191N
	ROM_LOAD("red",    0xc800, 0x0800, CRC(fcdd072b) SHA1(41571d96a7465d3e4a6ce7746e4002fe71173216) ) // N82S191N
	ROM_LOAD("green",  0xd000, 0x0800, CRC(7537f682) SHA1(6db262aeea6686a65fe4f6f6c3de034bc9859748) ) // N82S191N
	ROM_LOAD("brown",  0xd800, 0x0800, CRC(a70f4c20) SHA1(a990336726504d480dbb52e695483d39fb00a60e) ) // D2716
	ROM_LOAD("black",  0xe000, 0x1000, CRC(4eec7f71) SHA1(b11e10492451fe6790deb6177c90a9fb037e4ec6) ) // MCM2532C
	ROM_LOAD("yellow", 0xf000, 0x1000, CRC(51b9694b) SHA1(46582eb168f1e33fd05dd1554590351355e8afa4) ) // MCM2532C

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

ROM_START( cscet )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("03", 0x2000, 0x2000, CRC(22e43531) SHA1(696dc019bea3812ae6cf9c2b2c4d3a7b9017807d) )
	ROM_LOAD("02", 0xa000, 0x2000, CRC(e593f114) SHA1(4dc5a2456a87c128235958f046cee9502cb3ac65) )
	ROM_LOAD("06", 0xc000, 0x0800, CRC(5d41b1e5) SHA1(fe95d8811d8894688336b798212c397bdb216956) )
	ROM_LOAD("07", 0xc800, 0x0800, CRC(9078d40a) SHA1(4ffd36a4fcde1988e42543652e29463bc6ad5a8f) )
	ROM_LOAD("08", 0xd000, 0x0800, CRC(c9472cc1) SHA1(ef4b1ae99e81689efeae323fe6ed58cf2c773fd6) )
	ROM_LOAD("09", 0xd800, 0x0800, CRC(255c94a0) SHA1(d8e79213b69710e9d94c698492ec7b7420c9c7d8) )
	ROM_LOAD("04", 0xe000, 0x1000, CRC(098873bd) SHA1(86001129a57db390e565f59a5677ec0b34b41d99) )
	ROM_LOAD("05", 0xf000, 0x1000, CRC(1a516bfe) SHA1(2a2b252ca5d425fdf162cbc53077aee448b94437) )

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END


ROM_START( super9cc )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101-1050a01", 0x2000, 0x2000, CRC(421147e8) SHA1(ccf62f6f218e8992baf30973fe41b35e14a1cc1a) )
	ROM_LOAD("101-1024b03", 0xa000, 0x0800, CRC(e8c97455) SHA1(ed2958fc5474253ee8c2eaf27fc64226e12f80ea) )
	ROM_RELOAD(             0xa800, 0x0800)
	ROM_LOAD("101-1024b02", 0xc000, 0x2000, CRC(95004699) SHA1(ea79f43da73267344545df8ad61730f613876c2e) )
	ROM_LOAD("101-1024c01", 0xe000, 0x2000, CRC(03904e86) SHA1(bfa0dd9d8541e3ec359a247a3eba543501f727bc) )

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END


ROM_START( reversic )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101-1000a01", 0xf000, 0x1000, CRC(ca7723a7) SHA1(bd92330f2d9494fa408f5a2ca300d7a755bdf489) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY, FULLNAME, FLAGS
SYST( 1981, csc,      0,      0,      csc,     csc,   csc_state, empty_init, "Fidelity Electronics", "Champion Sensory Chess Challenger", MACHINE_SUPPORTS_SAVE )
SYST( 1981, csce,     0,      0,      csce,    csc,   csc_state, empty_init, "Fidelity Electronics", "Elite Champion Challenger", MACHINE_SUPPORTS_SAVE )
SYST( 1981, cscet,    csce,   0,      cscet,   csc,   csc_state, empty_init, "Fidelity Electronics", u8"Elite Champion Challenger (WMCCC 1981 Travemünde TM)", MACHINE_SUPPORTS_SAVE )

SYST( 1983, super9cc, 0,      0,      su9,     su9,   csc_state, empty_init, "Fidelity Electronics", "Super \"9\" Sensory Chess Challenger", MACHINE_SUPPORTS_SAVE )

SYST( 1981, reversic, 0,      0,      rsc,     rsc,   csc_state, empty_init, "Fidelity Electronics", "Reversi Sensory Challenger", MACHINE_SUPPORTS_SAVE )



css11501sk9.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:

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

Skeleton driver for Cisco CSS11501S-K9 Content Services Switch with SSL.

The appliance has eight 10/100 Ethernet ports, one 10Base-T port for management, one RJ45 serial port for console, two
PCMCIA slots for storage and one Gigabit Ethernet port (GBIC).

Hardware components:

Main board.
 -Sipex SP3243ECA Intelligent RS-232 Transceiver.
 -Crystal CrystalLAN CS8900A Ethernet ISA LAN Controller.
 -Exar ST16C1551 UART.
 -Microchip PIC16LF877PT next to a 10 MHz crystal (U35).
 -Xilinx Spartan XC2S50 (U23).
 -Atmel 24C02N SEEPROM (U11) next to the Xilinx Spartan XC2S50 at U23.
 -Two PCMCIA slots for Microdrive or Flash Drive.
 -Intel SPD6722QCCE PC-Card/PCMCIA controller.
 -Two 24C04R6 SEEPROMs near the two PCMCIA slots (U25 and U32).
 -Sipex 3223ECY RS-232 transceiver.
 -ST M4T32-BR12SH6 Timekeeper.
 -Xilinx Spartan XC2S50 (another one, U33).
 -Another 24C02N (U28), near the Xilinx Spartan XC2S50 at U33.
 -Vitesse VSC2102-08UQ Network Processor / Intelligent Packet Processor, near a 25 MHz crystal.
 -Intel LXT9785EHC 8-port Fast Ethernet PHY Transceiver near a 50 MHz crystal.
 -PMC RM7000A 400T 64-Bit MIPS RISC Microprocessor with Integrated L2 cache.
 -AMD AM29LV3200B Flash Memory.
 -PMC PM2329-BC.
 -Vitesse VSC2708-00UR.
 -AMCC S2068TB.
 -Cypress CY7C1360B-166AC 9-Mbit (256K x 36) Pipelined SRAM.
 -Samsung K7N403601B-QC13 128Kx36 Pipelined NtRAM.
 -SORIMM slot with a 256MB RAM module (Samsung MS18R1628EH0-CM8CI).
 -Cypress CY7C1360B-166AC 9-Mbit (256K x 36) Pipelines SRAM (another one).
 -Intel SPD6722QCCE PC-Card/PCMCIA controller.
 -Xilinx XC18V02 next to the first Xilinx Spartan XC2S50 (U23).

Sub-board (SSL acceleration).
 -Broadcom BCM5821A1KTB Super E-Commerce Processor (high-performance public-key processor) next to a 100 MHz crystal.
 -Martel GT-64120A-BN-3 (System Controller for 7000 CPU) next to another 100 MHz crystal.
 -PMC RM7000A 400T 64-Bit MIPS RISC Microprocessor with Integrated L2 cache.
 -AMD AM29LV1600B Flash Memory.
 -Altera Max EPM3128ATC100-5
 -Broadcom BCM5700C2KPB PCI-X 10/100/1000 Base-T controller, near a 125 MHz crystal.
 -Vitesse VSC2102-08UQ Network Processor / Intelligent Packet Processor, near a 25 MHz crystal.
 -IDT 71V546S133PF 128K x 36, 3.3V Synchronous SRAM with ZBT Feature, Burst Counter and Pipelined Outputs.
 -24C04R SEEPROM between the BCM5700C2KPB, the VSC2102-08UQ, and the 71V546S133PF.
 -Microchip PIC16LF872 next to a 10 MHz crystal.
 -Xilinx Spartan XC2S50.
 -Xilinx 18V01JC In-System Programmable Configuration PROM next to the Xilinx Spartan XC2S50.
 -SORIMM slot with a 64MB RAM module (Samsung MS18R1622EH0-CM8).
 -SODIMM slot with a 512MB RAM module (Cisco CIS00-21160-111CD).

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

#include "emu.h"
#include "cpu/mips/mips3.h"


namespace {

class css11501sk9_state : public driver_device
{
public:
	css11501sk9_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_sslcpu(*this, "sslcpu")
	{ }

	void css11501sk9(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<cpu_device> m_sslcpu;
};


// Input ports
static INPUT_PORTS_START( css11501sk9 )
INPUT_PORTS_END


void css11501sk9_state::machine_reset()
{
}

void css11501sk9_state::machine_start()
{
}

void css11501sk9_state::css11501sk9(machine_config &config)
{
	// basic machine hardware
	RM7000BE(config, m_maincpu, 400'000'000); // Main CPU, PMC RM7000A 400T
	RM7000BE(config, m_sslcpu, 400'000'000); // SSL CPU, PMC RM7000A 400T
}


ROM_START( css11501sk9 )
	ROM_REGION(0x410000, "maincpu", 0) // Main
	ROM_LOAD( "css11501s-k9_73-8174-04_17-7009-03_cs-a393_0514_hm_am29lv320db.u46",      0x000000, 0x410000, CRC(fdc00a8f) SHA1(cc89c39462e783d874abebbed2a80bd9ec095047) )

	ROM_REGION(0x200000, "sslcpu", 0) // SSL
	ROM_LOAD( "css11501s-k9_73-6917-06_17-6860-03_cs-7edd_0525_am29lv160db.u36",         0x000000, 0x200000, CRC(6dfe6620) SHA1(06cc9015907c74284a6b85393c0024729146a19b) )

	ROM_REGION(0x001eb3, "plds", 0)
	ROM_LOAD( "css11501s-k9_73-6917-06_17-6858-01_172f5a_2305_i4ps_epm3128atc100-5.u11", 0x000000, 0x001eb3, CRC(a094a0d3) SHA1(a4897096fd7b26b3a3e3c928fa8d1cfd2a2f5641) ) // Altera Max EPM3128ATC100-5 on SSL sub-board

	ROM_REGION(0x040000, "fpga", 0)
	ROM_LOAD( "css11501s-k9_73-6917-06_17-6859-02_cs-32cb_0519_xilinx_18v01jc.u2",       0x000000, 0x020000, CRC(bd69fa75) SHA1(9ff93c1f202ad1c178996e7ffd2afd644afd3fe6) ) // Xilinx 18V01JC next to the Xilinx Spartan XC2S50, on SSL sub-board
	ROM_LOAD( "css11501s-k9_73-8174-04_17-7008-02_cs-3be7_0524_xc18v02cg44.u43",         0x000000, 0x040000, CRC(a258b636) SHA1(f1cd8409362572cc45fa077179d98a1bb0df383f) ) // Xilinx XC18V02 next to the Xilinx Spartan XC2S50 at U23, on main PCB

	ROM_REGION(0x000200, "seeprom", 0)
	ROM_LOAD( "css11501s-k9_73-8174-04_24c02n.u11",                                      0x000000, 0x000100, CRC(bbe4ba95) SHA1(a56ebd9a98a51d63178da7c93c2b9e557e987a5c) ) // 24C02N SEEPROM near the Xilinx Spartan XC2S50 at U23, on main PCB
	ROM_LOAD( "css11501s-k9_73-8174-04_24c02n.u28",                                      0x000000, 0x000100, CRC(a10ac3ca) SHA1(c68610c51106d2351570c1c77ab1cf1fb210ffb1) ) // 24C02N SEEPROM near the Xilinx Spartan XC2S50 at U33, on main PCB
	ROM_LOAD( "css11501s-k9_73-8174-04_24c04r6.u25",                                     0x000000, 0x000200, CRC(cfe0b9c1) SHA1(2af0645dc5aaab26c0330ef97b97c4958f3ff173) ) // 24C04R6 SEEPROM near the PCMCIA slots, on main PCB
	ROM_LOAD( "css11501s-k9_73-8174-04_24c04r6.u32",                                     0x000000, 0x000200, CRC(d2b2c2d3) SHA1(ea2ec6863749e1020a79710b36d1ac42528c7855) ) // 24C04R6 SEEPROM near the PCMCIA slots, on main PCB
	ROM_LOAD( "css11501s-k9_73-6917-06_24c04r6.u33",                                     0x000000, 0x000200, CRC(bd7bc39f) SHA1(9d0ac37bb3ec8c95990fd37a962a17a95ce97aa0) ) // 24C04R SEEPROM between the BCM5700C2KPB, the VSC2102-08UQ, and the 71V546S133PF, on SSL sub-board
	ROM_LOAD( "css11501s-k9_73-6917-06_24c02n.u8",                                       0x000000, 0x000100, CRC(dcbe3083) SHA1(81934196733a8878cc19ced4f1791dfaa4da494a) ) // 24C02N SEEPROM between the BCM5700C2KPB, the 71V546S133PF, and the XC2S50, on SSL sub-board

	ROM_REGION(0x004400, "pic", 0)
	// CONFIG = 3eh, ID = ff3fff3fff3fff3fh
	ROM_LOAD( "css11501s-k9_73-8174-04_17-6486-03_2605_27af12_pic16lf877pt_user.u35",    0x000000, 0x004000, CRC(22b03fe9) SHA1(984c14fb013763aa4fb981888480d19a6058843b) ) // PIC16LF877, on main PCB
	ROM_LOAD( "css11501s-k9_73-8174-04_17-6486-03_2605_27af12_pic16lf877pt_data.u35",    0x000000, 0x000200, CRC(14bfe63a) SHA1(ebe833da793930303ac57c873a97f8383494a939) ) // PIC16LF877, on main PCB

	// CONFIG = 3eh, ID = 7f007f007f007f00h
	ROM_LOAD( "css11501s-k9_73-8174-04_17-6494-02_1605_92817_pic16lf872_user.u7",        0x000000, 0x001000, CRC(6deebf3a) SHA1(c8f030657b7959c6d3aa5b1583786eb84ed199c1) ) // PIC16LF872, on SSL sub-board
	ROM_LOAD( "css11501s-k9_73-8174-04_17-6494-02_1605_92817_pic16lf872_data.u7",        0x000000, 0x000080, CRC(903500ff) SHA1(d33277afda835773cfcc9bc137690cbe79943f0f) ) // PIC16LF872, on SSL sub-board
ROM_END

} // Anonymous namespace

//    YEAR  NAME         PARENT  COMPAT  MACHINE      INPUT        CLASS              INIT        COMPANY  FULLNAME        FLAGS
SYST( 2002, css11501sk9, 0,      0,      css11501sk9, css11501sk9, css11501sk9_state, empty_init, "Cisco", "CSS11501S-K9", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



ct486.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Dirk Best
/***************************************************************************

    PC/AT 486 with Chips & Technologies CS4031 chipset

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

#include "emu.h"

#include "cpu/i386/i386.h"
#include "machine/at_keybc.h"
#include "machine/cs4031.h"
#include "machine/ram.h"
#include "sound/spkrdev.h"

#include "bus/isa/isa.h"
#include "bus/isa/isa_cards.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"

#include "emupal.h"
#include "softlist.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

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

class ct486_state : public driver_device
{
public:
	ct486_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_cs4031(*this, "cs4031"),
		m_isabus(*this, "isabus"),
		m_speaker(*this, "speaker")
	{ }

	required_device<cpu_device> m_maincpu;
	required_device<cs4031_device> m_cs4031;
	required_device<isa16_device> m_isabus;
	required_device<speaker_sound_device> m_speaker;

	void ct486(machine_config &config);
	void ast6000(machine_config &config);
	void ast611(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;

	uint16_t cs4031_ior(offs_t offset);
	void cs4031_iow(offs_t offset, uint16_t data);
	void cs4031_hold(int state);
	void cs4031_tc(offs_t offset, uint8_t data) { m_isabus->eop_w(offset, data); }
	void cs4031_spkr(int state) { m_speaker->level_w(state); }
	void ct486_io(address_map &map) ATTR_COLD;
	void ct486_map(address_map &map) ATTR_COLD;
};


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

void ct486_state::machine_start()
{
}

uint16_t ct486_state::cs4031_ior(offs_t offset)
{
	if (offset < 4)
		return m_isabus->dack_r(offset);
	else
		return m_isabus->dack16_r(offset);
}

void ct486_state::cs4031_iow(offs_t offset, uint16_t data)
{
	if (offset < 4)
		m_isabus->dack_w(offset, data);
	else
		m_isabus->dack16_w(offset, data);
}

void ct486_state::cs4031_hold(int state)
{
	// halt cpu
	m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);

	// and acknowledge hold
	m_cs4031->hlda_w(state);
}


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

void ct486_state::ct486_map(address_map &map)
{
}

void ct486_state::ct486_io(address_map &map)
{
	map.unmap_value_high();
}


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

void ct486_state::ct486(machine_config &config)
{
	I486(config, m_maincpu, XTAL(25'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &ct486_state::ct486_map);
	m_maincpu->set_addrmap(AS_IO, &ct486_state::ct486_io);
	m_maincpu->set_irq_acknowledge_callback("cs4031", FUNC(cs4031_device::int_ack_r));

	CS4031(config, m_cs4031, XTAL(25'000'000), "maincpu", "isa", "bios", "keybc", RAM_TAG);
	// cpu connections
	m_cs4031->hold().set(FUNC(ct486_state::cs4031_hold));
	m_cs4031->nmi().set_inputline("maincpu", INPUT_LINE_NMI);
	m_cs4031->intr().set_inputline("maincpu", INPUT_LINE_IRQ0);
	m_cs4031->cpureset().set_inputline("maincpu", INPUT_LINE_RESET);
	m_cs4031->a20m().set_inputline("maincpu", INPUT_LINE_A20);
	// isa dma
	m_cs4031->ior().set(FUNC(ct486_state::cs4031_ior));
	m_cs4031->iow().set(FUNC(ct486_state::cs4031_iow));
	m_cs4031->tc().set(FUNC(ct486_state::cs4031_tc));
	// speaker
	m_cs4031->spkr().set(FUNC(ct486_state::cs4031_spkr));

	RAM(config, RAM_TAG).set_default_size("4M").set_extra_options("1M,2M,8M,16M,32M,64M");

	at_kbc_device_base &keybc(AT_KEYBOARD_CONTROLLER(config, "keybc", XTAL(12'000'000)));
	keybc.hot_res().set("cs4031", FUNC(cs4031_device::kbrst_w));
	keybc.gate_a20().set("cs4031", FUNC(cs4031_device::gatea20_w));
	keybc.kbd_irq().set("cs4031", FUNC(cs4031_device::irq01_w));
	keybc.kbd_clk().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	keybc.kbd_data().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));

	pc_kbdc_device &pc_kbdc(PC_KBDC(config, "kbd", pc_at_keyboards, STR_KBD_MICROSOFT_NATURAL));
	pc_kbdc.out_clock_cb().set(keybc, FUNC(at_kbc_device_base::kbd_clk_w));
	pc_kbdc.out_data_cb().set(keybc, FUNC(at_kbc_device_base::kbd_data_w));

	ISA16(config, m_isabus, 0);
	m_isabus->set_memspace(m_maincpu, AS_PROGRAM);
	m_isabus->set_iospace(m_maincpu, AS_IO);
	m_isabus->iochck_callback().set(m_cs4031, FUNC(cs4031_device::iochck_w));
	m_isabus->irq2_callback().set(m_cs4031, FUNC(cs4031_device::irq09_w));
	m_isabus->irq3_callback().set(m_cs4031, FUNC(cs4031_device::irq03_w));
	m_isabus->irq4_callback().set(m_cs4031, FUNC(cs4031_device::irq04_w));
	m_isabus->irq5_callback().set(m_cs4031, FUNC(cs4031_device::irq05_w));
	m_isabus->irq6_callback().set(m_cs4031, FUNC(cs4031_device::irq06_w));
	m_isabus->irq7_callback().set(m_cs4031, FUNC(cs4031_device::irq07_w));
	m_isabus->irq10_callback().set(m_cs4031, FUNC(cs4031_device::irq10_w));
	m_isabus->irq11_callback().set(m_cs4031, FUNC(cs4031_device::irq11_w));
	m_isabus->irq12_callback().set(m_cs4031, FUNC(cs4031_device::irq12_w));
	m_isabus->irq14_callback().set(m_cs4031, FUNC(cs4031_device::irq14_w));
	m_isabus->irq15_callback().set(m_cs4031, FUNC(cs4031_device::irq15_w));
	m_isabus->drq0_callback().set(m_cs4031, FUNC(cs4031_device::dreq0_w));
	m_isabus->drq1_callback().set(m_cs4031, FUNC(cs4031_device::dreq1_w));
	m_isabus->drq2_callback().set(m_cs4031, FUNC(cs4031_device::dreq2_w));
	m_isabus->drq3_callback().set(m_cs4031, FUNC(cs4031_device::dreq3_w));
	m_isabus->drq5_callback().set(m_cs4031, FUNC(cs4031_device::dreq5_w));
	m_isabus->drq6_callback().set(m_cs4031, FUNC(cs4031_device::dreq6_w));
	m_isabus->drq7_callback().set(m_cs4031, FUNC(cs4031_device::dreq7_w));
	ISA16_SLOT(config, "board1", 0, "isabus", pc_isa16_cards, "fdc_smc", true);
	ISA16_SLOT(config, "board2", 0, "isabus", pc_isa16_cards, "comat", true);
	ISA16_SLOT(config, "board3", 0, "isabus", pc_isa16_cards, "ide", true);
	ISA16_SLOT(config, "board4", 0, "isabus", pc_isa16_cards, "lpt", true);
	ISA16_SLOT(config, "isa1", 0, "isabus", pc_isa16_cards, "svga_et4kw32i", false);
	ISA16_SLOT(config, "isa2", 0, "isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa3", 0, "isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa4", 0, "isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa5", 0, "isabus", pc_isa16_cards, nullptr, false);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* software lists */
	SOFTWARE_LIST(config, "pc_disk_list").set_original("ibm5150");
	SOFTWARE_LIST(config, "at_disk_list").set_original("ibm5170");
	SOFTWARE_LIST(config, "at_cdrom_list").set_original("ibm5170_cdrom");
	SOFTWARE_LIST(config, "at_hdd_list").set_original("ibm5170_hdd");
	SOFTWARE_LIST(config, "midi_disk_list").set_compatible("midi_flop");
	SOFTWARE_LIST(config, "photocd_list").set_compatible("photo_cd");
}

void ct486_state::ast6000(machine_config &config)
{
	ct486_state::ct486(config);
	// Socket 1 CPU
	// 1 ISA slot only, with ISA bridge x3
	// CL-GD5428 on-board
	// Chips & Technologies F82C721 Super I/O
	ISA16_SLOT(config, "board5", 0, "isabus", pc_isa16_cards, "clgd542x", true);
	ISA16_SLOT(config.replace(), "isa1", 0, "isabus", pc_isa16_cards, nullptr, false);
}

void ct486_state::ast611(machine_config &config)
{
	ct486_state::ct486(config);
	// Socket 3 CPU
	I486DX4(config.replace(), m_maincpu, XTAL(33'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &ct486_state::ct486_map);
	m_maincpu->set_addrmap(AS_IO, &ct486_state::ct486_io);
	m_maincpu->set_irq_acknowledge_callback("cs4031", FUNC(cs4031_device::int_ack_r));

	m_cs4031->set_clock(XTAL(33'000'000));

	// 1 ISA slot only, with ISA bridge x3
	// CL-GD5428 on-board
	// SMC/SMSC FDC37C653** Super I/O
	ISA16_SLOT(config, "board5", 0, "isabus", pc_isa16_cards, "clgd542x", true);
	// TODO: Creative CT2504 / Vibra 16S on-board
//  ISA16_SLOT(config, "board6", 0, "isabus", pc_isa16_cards, "vibra16s", true);

	ISA16_SLOT(config.replace(), "isa1", 0, "isabus", pc_isa16_cards, nullptr, false);
}


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

ROM_START( ct486 )
	ROM_REGION(0x40000, "isa", ROMREGION_ERASEFF)
	ROM_REGION(0x100000, "bios", 0)
	ROM_LOAD("chips_1.ami", 0xf0000, 0x10000, CRC(a14a7511) SHA1(b88d09be66905ed2deddc26a6f8522e7d2d6f9a8))
ROM_END

ROM_START( ast6000 )
	ROM_REGION(0x40000, "isa", ROMREGION_ERASEFF)
	ROM_REGION(0x100000, "bios", 0)
	ROM_LOAD("ast6000.bin", 0xe0000, 0x20000, CRC(8982ac34) SHA1(07c10a8857bb91fc673c4299b17214e9ebad4524))
ROM_END

ROM_START( ast611 )
	ROM_REGION(0x40000, "isa", ROMREGION_ERASEFF)
	ROM_REGION(0x100000, "bios", 0)
	ROM_LOAD("ast_611s.bin", 0xe0000, 0x20000, CRC(5e7a4eef) SHA1(955a346d882298ad623d9a31079b059d28e43b9e))
ROM_END


} // anonymous namespace


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

COMP( 1993, ct486,   0, 0, ct486,   0, ct486_state, empty_init, "<unknown Taiwanese vendor 8029>", "PC/AT 486 with CS4031 chipset", 0 )
COMP( 1994, ast6000, 0, 0, ast6000, 0, ct486_state, empty_init, "AST", "Advantage! 6050d/6066d", MACHINE_NOT_WORKING ) // long beep, hangs after testing memory, will jump to empty reset vector at PC=ffff'fff0 (???)
COMP( 1994, ast611, 0, 0,  ast611, 0, ct486_state, empty_init, "AST", "Advantage! 610/611", MACHINE_NOT_WORKING ) // constant beep with FDC access (different Super I/O?)



ct8000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Devin Acker
// thanks-to:BCM

/***************************************************************************
    Casiotone 8000 / "Symphonytron" system

    The Symphonytron was a modular electronic organ produced by Casio in 1983.
    The full system consists of:

    - up to two Casiotone 8000 keyboards (8049 CPU, 2x uPD931 "vowel-consonant synthesis")
    - RC-1 accompaniment unit (uPD7801 CPU, uPD930 rhythm generator, analog percussion)
    - MB-1 memory unit (8049 CPU, 2x uPD931, RAM cartridge slot)
    - FK-1 pedal keyboard (8049 CPU, single uPD931)
    - CS-100 or CS-200 keyboard stand with built-in mixer

    The keyboards and memory unit all connect to the RC-1 via 14-pin DIN connectors.
    Although the RAM cart slot is located on the MB-1, all actual access to the cart is controlled
    remotely by the RC-1, which uses the cart to record and play back both rhythm/chord and melody
    data. The MB-1's sound hardware is then used to play back recorded melody data independently of
    the keyboards. The RC-1 also has a "tone mix" feature, where note data received from one keyboard
    is automatically forwarded to the other.

    The individual units can also be used on their own; the MB-1 will also respond to notes and tone
    selection commands via the DIN connector, but it needs the RC-1 present to do much else.
    It's marked as "not working" for this reason.

    This driver also features MIDI in/thru support via an "adapter" device which translates a subset
    of MIDI messages into the protocol used with the original connectors.

    TODO:
    - volume/expression pedal (for all systems)
    - fix aliasing in BBD output for some presets

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

#include "emu.h"

#include "ct8000_midi.h"
#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/i8243.h"
#include "machine/rescap.h"
#include "sound/bbd.h"
#include "sound/flt_biquad.h"
#include "sound/flt_rc.h"
#include "sound/flt_vol.h"
#include "sound/mixer.h"
#include "sound/upd931.h"

#include "speaker.h"

#define LOG_VCO (1<<1)

// #define VERBOSE (LOG_GENERAL | LOG_VCO)

#include "logmacro.h"

#include "ct8000.lh"
#include "ctfk1.lh"

namespace {

class ct8000_state : public driver_device
{
public:
	ct8000_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_io(*this, "ioport%c", 'a')
		, m_midi(*this, "midi")
		, m_931a(*this, "upd931a")
		, m_931b(*this, "upd931b")
		, m_mixer(*this, "mixer")
		, m_filter_rc(*this, "filter_rc%u", 0)
		, m_filter_bq(*this, "filter_bq%u", 0)
		, m_bbd(*this, "bbd")
		, m_bank(*this, "bank")
		, m_inputs(*this, "KC%X", 0) // schematic uses KC0-9, A-C
	{
	}

	void config_base(machine_config &config) ATTR_COLD;
	void ctmb1(machine_config &config) ATTR_COLD;
	void ct8000(machine_config &config) ATTR_COLD;
	void ctfk1(machine_config &config) ATTR_COLD;

	ioport_value switch_r() { return m_switch; }
	DECLARE_INPUT_CHANGED_MEMBER(switch_w);
	DECLARE_INPUT_CHANGED_MEMBER(switch_clear_w);

protected:
	void ct8000_map(address_map &map) ATTR_COLD;
	void ct8000_io_map(address_map &map) ATTR_COLD;

	virtual void driver_start() override ATTR_COLD;
	virtual void driver_reset() override ATTR_COLD;

	void p1_w(u8 data);
	u8 p1_r();
	void p2_w(u8 data);
	u8 p2_r();

	void p4a_w(u8 data);
	void p5a_w(u8 data);
	void p6a_w(u8 data);
	void p7a_w(u8 data);

	void p4b_w(u8 data);
	void p7b_w(u8 data);

	u8 keys_r();

	void filter_main_w(u8 data);
	void filter_sub_w(u8 data);
	void filter_bass_w(u8 data);

	void pll_w(offs_t offset, u8 data);
	virtual void update_clocks();

	required_device<i8049_device> m_maincpu;
	required_device_array<i8243_device, 2> m_io;

	required_device<ct8000_midi_device> m_midi;

	required_device<upd931_device> m_931a;
	optional_device<upd931_device> m_931b;
	required_device<mixer_device> m_mixer;
	optional_device_array<filter_rc_device, 3> m_filter_rc;
	optional_device_array<filter_biquad_device, 3> m_filter_bq;
	optional_device<mn3207_device> m_bbd;

	required_memory_bank m_bank;

	optional_ioport_array<13> m_inputs;

	TIMER_CALLBACK_MEMBER(bbd_tick);
	void bbd_setup_next_tick();

	emu_timer *m_bbd_timer;

	u16 m_key_select;
	u8 m_key_enable;

	ioport_value m_switch;

	u16 m_pll_counter[2];
	u16 m_pll_ref[2];

	u8 m_clock_select;
	u8 m_clock_div;
};

void ct8000_state::ct8000_map(address_map &map)
{
	map(0x800, 0xfff).bankr("bank");
}

void ct8000_state::ct8000_io_map(address_map &map)
{
	map(0x00, 0xff).rw(FUNC(ct8000_state::keys_r), FUNC(ct8000_state::pll_w));
}

//**************************************************************************
void ct8000_state::config_base(machine_config &config)
{
	I8049(config, m_maincpu, 4.946864_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &ct8000_state::ct8000_map);
	m_maincpu->set_addrmap(AS_IO, &ct8000_state::ct8000_io_map);
	m_maincpu->p1_out_cb().set(FUNC(ct8000_state::p1_w));
	m_maincpu->p1_in_cb().set(FUNC(ct8000_state::p1_r));
	m_maincpu->p2_out_cb().set(FUNC(ct8000_state::p2_w));
	m_maincpu->p2_in_cb().set(FUNC(ct8000_state::p2_r));

	CT8000_MIDI(config, m_midi);
	m_midi->int_cb().set_inputline(m_maincpu, MCS48_INPUT_IRQ);
	m_maincpu->t0_in_cb().set(m_midi, FUNC(ct8000_midi_device::ack_r));

	midi_port_device &mdin(MIDI_PORT(config, "mdin", midiin_slot, "midiin"));
	mdin.rxd_handler().set(m_midi, FUNC(ct8000_midi_device::rx_w));

	MIDI_PORT(config, "mdthru", midiout_slot, "midiout");
	mdin.rxd_handler().append("mdthru", FUNC(midi_port_device::write_txd));

	I8243(config, m_io[0]);
	m_maincpu->prog_out_cb().set(m_io[0], FUNC(i8243_device::prog_w));
	m_io[0]->p4_out_cb().set(FUNC(ct8000_state::p4a_w));
	m_io[0]->p5_out_cb().set(FUNC(ct8000_state::p5a_w));
	m_io[0]->p6_out_cb().set(FUNC(ct8000_state::p6a_w));
	m_io[0]->p7_out_cb().set(FUNC(ct8000_state::p7a_w));

	I8243(config, m_io[1]);
	m_maincpu->prog_out_cb().append(m_io[1], FUNC(i8243_device::prog_w));
	m_io[1]->p4_out_cb().set(FUNC(ct8000_state::p4b_w));
	m_io[1]->p5_in_cb().set(m_midi, FUNC(ct8000_midi_device::data_r));
	m_io[1]->p5_out_cb().set_nop();
	m_io[1]->p6_out_cb().set(m_midi, FUNC(ct8000_midi_device::data_w));
	m_io[1]->p7_out_cb().set(FUNC(ct8000_state::p7b_w));
}

//**************************************************************************
void ct8000_state::ctmb1(machine_config &config)
{
	config_base(config);

	SPEAKER(config, "speaker", 2).front();

	MIXER(config, m_mixer);
	m_mixer->add_route(0, "speaker", 1.0, 0);
	m_mixer->add_route(0, "speaker", 1.0, 1);

	// 931 A - sub (consonant) waveform
	UPD931(config, m_931a, m_maincpu->clock());
	m_931a->set_master(false);
	m_931a->filter_cb().set(FUNC(ct8000_state::filter_sub_w));
	/*
	Both of the uPD931s have a fairly large amount of headroom in their outputs,
	which is partly compensated for via adjustable gain (see filter_main_w and filter_sub_w),
	but the base output gain should be boosted as well to provide a decent volume.
	*/
	m_931a->add_route(0, m_filter_rc[0], 2.0);

	// sub HPF
	FILTER_RC(config, m_filter_rc[0]).add_route(0, m_filter_bq[0], 1.0);
	FILTER_BIQUAD(config, m_filter_bq[0]).add_route(0, m_filter_rc[1], 1.0);

	// sub LPF
	FILTER_RC(config, m_filter_rc[1]).add_route(0, m_filter_bq[1], 1.0);
	FILTER_BIQUAD(config, m_filter_bq[1]).add_route(0, "filter_ac0", 1.0);

	FILTER_RC(config, "filter_ac0").set_ac().add_route(0, m_mixer, 1.0);

	// 931 B - main (vowel) waveform
	UPD931(config, m_931b, m_maincpu->clock());
	m_931b->sync_cb().set(m_931a, FUNC(upd931_device::sync_w));
	m_931b->filter_cb().set(FUNC(ct8000_state::filter_main_w));
	m_931b->add_route(0, m_filter_rc[2], 2.0);

	// main LPF
	FILTER_RC(config, m_filter_rc[2]).add_route(0, m_filter_bq[2], 1.0);
	FILTER_BIQUAD(config, m_filter_bq[2]).add_route(0, "filter_ac1", 1.0);

	FILTER_RC(config, "filter_ac1").set_ac().add_route(0, m_mixer, 1.0);

	MN3207(config, m_bbd);
	m_mixer->add_route(0, m_bbd, 1.0);
	m_bbd->add_route(ALL_OUTPUTS, "chorus", 0.5);

	auto &bbd_mixer = MIXER(config, "chorus");
	bbd_mixer.add_route(0, "speaker", 0.4, 0);
	bbd_mixer.add_route(0, "speaker", -0.4, 1);
}

//**************************************************************************
void ct8000_state::ct8000(machine_config &config)
{
	ctmb1(config);
	config.set_default_layout(layout_ct8000);
}

//**************************************************************************
void ct8000_state::ctfk1(machine_config &config)
{
	config_base(config);
	m_io[0]->p4_out_cb().set_nop(); // no switchable clock

	// valid program/patch numbers on FK-1 start at 1 instead of 0 for some reason
	m_midi->set_base_program(1);

	SPEAKER(config, "speaker").front_center();

	MIXER(config, m_mixer).add_route(0, "speaker", 1.0);

	UPD931(config, m_931a, m_maincpu->clock());
	m_931a->filter_cb().set(FUNC(ct8000_state::filter_bass_w));
	m_931a->add_route(0, m_filter_rc[0], 4.0); // boost volume even more since the FK-1 is monophonic

	FILTER_RC(config, m_filter_rc[0]).add_route(0, m_filter_bq[0], 1.0);
	FILTER_BIQUAD(config, m_filter_bq[0]).add_route(0, "filter_ac", 1.0);

	FILTER_RC(config, "filter_ac").set_ac().add_route(0, m_mixer, 1.0);

	config.set_default_layout(layout_ctfk1);
}

//**************************************************************************
void ct8000_state::driver_start()
{
	if (m_bbd)
		m_bbd_timer = timer_alloc(FUNC(ct8000_state::bbd_tick), this);

	m_bank->configure_entries(0, 2, memregion("bankrom")->base(), 0x800);

	m_key_select = 0xffff;
	m_key_enable = 0;
	m_switch = 0;

	m_pll_counter[0] = m_pll_counter[1] = 0;
	m_pll_ref[0] = m_pll_ref[1] = 0;

	m_clock_select = m_clock_div = 0;

	save_item(NAME(m_key_select));
	save_item(NAME(m_key_enable));
	save_item(NAME(m_switch));

	save_item(NAME(m_pll_counter));
	save_item(NAME(m_pll_ref));

	save_item(NAME(m_clock_select));
	save_item(NAME(m_clock_div));
}

void ct8000_state::driver_reset()
{
	if (m_bbd)
		bbd_setup_next_tick();
}

//**************************************************************************
void ct8000_state::p1_w(u8 data)
{
	// bit 0-3: 931 data
	// bit 4: 931 strobe 1 (I1)
	// bit 5: 931 strobe 2 (I2)
	// bit 6: 931 B select (ct8000) / 931 select (fk1)
	// bit 7: 931 A select (ct8000) / unused (fk1)
	m_931a->db_w(data & 0xf);
	m_931a->i1_w(BIT(data, 4));
	m_931a->i2_w(BIT(data, 5));

	if (m_931b)
	{
		m_931b->db_w(data & 0xf);
		m_931b->i1_w(BIT(data, 4));
		m_931b->i2_w(BIT(data, 5));
		m_931b->i3_w(BIT(data, 6));
		m_931a->i3_w(BIT(data, 7));
	}
	else
	{
		m_931a->i3_w(BIT(data, 6));
	}
}

//**************************************************************************
u8 ct8000_state::p1_r()
{
	u8 status = m_931a->db_r();
	if (m_931b)
		status &= m_931b->db_r();

	return 0xf0 | status;
}

//**************************************************************************
void ct8000_state::p2_w(u8 data)
{
	m_io[0]->p2_w(data & 0xf);
	m_io[1]->p2_w(data & 0xf);
	m_io[0]->cs_w(BIT(data, 4));
	m_io[1]->cs_w(BIT(data, 6));
	m_bank->set_entry(BIT(data, 7));
}

//**************************************************************************
u8 ct8000_state::p2_r()
{
	return 0xf0 | (m_io[0]->p2_r() & m_io[1]->p2_r());
}

//**************************************************************************
void ct8000_state::p4a_w(u8 data)
{
	// bit 0: master 931 clock select (1 = master VCO, 0 = slave VCO)
	// bit 1-2: master 931 clock divider (3, 6, 12, 4)
	// bit 3: 931 reset
	m_clock_select = BIT(~data, 0);
	m_clock_div = BIT(data, 1, 2);

	update_clocks();
}

//**************************************************************************
void ct8000_state::p5a_w(u8 data)
{
	// key matrix out bits 4-7
	m_key_select &= 0xff0f;
	m_key_select |= (data << 4);
}

//**************************************************************************
void ct8000_state::p6a_w(u8 data)
{
	// bit 0: unused
	// bit 1: key matrix in enable (active low)
	// bit 2-3: key matrix out bits 8-9
	m_key_enable = BIT(~data, 1);
	m_key_select &= 0xfcff;
	m_key_select |= (BIT(data, 2, 2) << 8);
}

//**************************************************************************
void ct8000_state::p7a_w(u8 data)
{
	// key matrix out bits 0-3
	m_key_select &= 0xfff0;
	m_key_select |= data;
}

//**************************************************************************
void ct8000_state::p4b_w(u8 data)
{
	// bit 0: data out strobe to RC-1
	// bit 1: data in ack to RC-1 (active low)
	// bit 2: stereo chorus enable (active low)
	// bit 3: stereo chorus enable for line in (active low)
	m_midi->strobe_w(BIT(data, 0));
	m_midi->ack_w(BIT(data, 1));

	if (m_bbd)
		m_bbd->set_input_gain(0, BIT(data, 2) ? 0 : 1.0);
}

//**************************************************************************
void ct8000_state::p7b_w(u8 data)
{
	// bit 0-2: key matrix out bits 10-12
	// bit 3: mute
	m_key_select &= 0xe3ff;
	m_key_select |= ((data & 0x7) << 10);

	// mute is applied to mixed/filtered 931 output before the stereo chorus
	const double gain = BIT(data, 3) ? 1.0 : 0.0;
	m_mixer->set_input_gain(0, gain);
}

//**************************************************************************
u8 ct8000_state::keys_r()
{
	u8 data = 0xff;

	if (m_key_enable)
	{
		for (int i = 0; i < m_inputs.size(); i++)
			if (!BIT(m_key_select, i))
				data &= m_inputs[i].read_safe(0xff);
	}

	return data;
}

//**************************************************************************
INPUT_CHANGED_MEMBER(ct8000_state::switch_w)
{
	if (!oldval && newval)
		m_switch |= param;
}

//**************************************************************************
INPUT_CHANGED_MEMBER(ct8000_state::switch_clear_w)
{
	if (!oldval && newval)
		m_switch = 0;
}

//**************************************************************************
void ct8000_state::filter_main_w(u8 data)
{
	// bit 0-1 = main LPF cutoff
	// bit 2-3 = absolute gain
	double r, c;
	if (BIT(data, 1))
	{
		r = RES_K(68);
		c = CAP_P(470) + BIT(data, 0) ? CAP_P(330) : 0;
	}
	else
	{
		r = RES_K(43);
		c = CAP_P(390);
	}

	m_filter_rc[2]->filter_rc_set_RC(filter_rc_device::LOWPASS, r, 0, 0, CAP_N(2.2));
	m_filter_bq[2]->opamp_sk_lowpass_modify(r, r, RES_M(999.99), RES_R(0.001), CAP_N(5.6), c);

	static const double gain[] = { 3.79, 2.82, 1.94, 1 };
	m_mixer->set_output_gain(0, gain[BIT(data, 2, 2)]);
}

//**************************************************************************
void ct8000_state::filter_sub_w(u8 data)
{
	// bit 0 = sub HPF enable
	// bit 1 = sub LPF cutoff
	// bit 2 = sub LPF disable
	// bit 3 = consonant gain
	if (BIT(data, 0))
	{
		const double c = CAP_N(2.2);
		m_filter_rc[0]->filter_rc_set_RC(filter_rc_device::HIGHPASS, RES_K(18), 0, 0, c);
		m_filter_bq[0]->opamp_sk_highpass_modify(RES_K(6.8), RES_K(220), RES_M(999.99), RES_R(0.001), c, c);
	}
	else
	{
		m_filter_rc[0]->filter_rc_set_RC(filter_rc_device::HIGHPASS, 0, 0, 0, 0);
		m_filter_bq[0]->modify_raw(0, 0, 1, 0, 0);
	}

	if (BIT(data, 2))
	{
		m_filter_rc[1]->filter_rc_set_RC(filter_rc_device::LOWPASS, 0, 0, 0, 0);
		m_filter_bq[1]->modify_raw(0, 0, 1, 0, 0);
	}
	else
	{
		const double r = BIT(data, 1) ? RES_K(22) : RES_K(56);
		const double c = BIT(data, 1) ? CAP_P(430) : CAP_P(330);

		m_filter_rc[1]->filter_rc_set_RC(filter_rc_device::LOWPASS, r, 0, 0, CAP_N(2.2));
		m_filter_bq[1]->opamp_sk_lowpass_modify(r, r, RES_M(999.99), RES_R(0.001), CAP_N(5.6), c);
	}

	m_filter_bq[1]->set_output_gain(0, BIT(data, 3) ? 0.468 : 1);
}

//**************************************************************************
void ct8000_state::filter_bass_w(u8 data)
{
	// bit 0 = LPF cutoff
	// bit 1 = LPF enable (seems to be incorrectly inverted in the schematic)
	// bit 2-3 = gain

	if (BIT(data, 1))
	{
		const double r = RES_K(12);
		const double c = CAP_N(3.3) + BIT(data, 0) ? CAP_N(10) : 0;

		m_filter_rc[0]->filter_rc_set_RC(filter_rc_device::LOWPASS, r, 0, 0, CAP_N(22));
		m_filter_bq[0]->opamp_sk_lowpass_modify(r, r, RES_M(999.99), RES_R(0.001), CAP_N(56), c);
	}
	else
	{
		m_filter_rc[0]->filter_rc_set_RC(filter_rc_device::LOWPASS, 0, 0, 0, 0);
		m_filter_bq[0]->modify_raw(0, 0, 1, 0, 0);
	}

	static const double gain[] = { 3.79, 2.82, 1.94, 1 };
	m_mixer->set_output_gain(ALL_OUTPUTS, gain[BIT(data, 2, 2)]);
}

//**************************************************************************
void ct8000_state::pll_w(offs_t offset, u8 data)
{
	data &= 0xf;

	for (int i = 0; i < 2; i++)
	{
		if (!BIT(offset, 3 + i))
		{
			switch (offset & 7)
			{
			case 0:
				m_pll_counter[i] &= 0x3ff0;
				m_pll_counter[i] |= data;
				break;

			case 1:
				m_pll_counter[i] &= 0x3f0f;
				m_pll_counter[i] |= (data << 4);
				break;

			case 2:
				m_pll_counter[i] &= 0x30ff;
				m_pll_counter[i] |= (data << 8);
				break;

			case 3:
				m_pll_counter[i] &= 0x0fff;
				m_pll_counter[i] |= ((data & 3) << 12);
				break;

			case 4:
				m_pll_ref[i] &= 0xff0;
				m_pll_ref[i] |= data;
				break;

			case 5:
				m_pll_ref[i] &= 0xf0f;
				m_pll_ref[i] |= (data << 4);
				break;

			case 6:
				m_pll_ref[i] &= 0x0ff;
				m_pll_ref[i] |= (data << 8);
				break;

			default:
				break;
			}
		}
	}

	update_clocks();
}

//**************************************************************************
void ct8000_state::update_clocks()
{
	double clock_scale[2] = { 1.0, 1.0 };

	// master VCO freq (controls 931 B)
	const int sel = m_clock_select & 1;
	if (m_931b && m_pll_counter[sel] && m_pll_ref[sel])
	{
		// PLLs are tuned so that increasing/decreasing the counter by 1 raises or lowers pitch by ~1.5 cents
		clock_scale[0] = (2.0 * m_pll_counter[sel]) / m_pll_ref[sel];

		switch (m_clock_div & 3)
		{
		case 0: clock_scale[0] /= 3; break;
		case 1: clock_scale[0] /= 6; break;
		case 2: clock_scale[0] /= 12; break;
		case 3: clock_scale[0] /= 4; break;
		}

		m_931b->set_clock_scale(clock_scale[0]);
	}

	// slave VCO freq (controls 931 A and CPU)
	if (m_pll_counter[1] && m_pll_ref[1])
	{
		clock_scale[1] = (2.0 * m_pll_counter[1]) / (3 * m_pll_ref[1]);

		m_maincpu->set_clock_scale(clock_scale[1]);
		m_931a->set_clock_scale(clock_scale[1]);
	}

	LOGMASKED(LOG_VCO, "VCO #1 %.3f MHz, #2 %.3f MHz\n",
		clock_scale[0] * m_maincpu->unscaled_clock() / 1'000'000,
		clock_scale[1] * m_maincpu->unscaled_clock() / 1'000'000);
}

//**************************************************************************
TIMER_CALLBACK_MEMBER(ct8000_state::bbd_tick)
{
	m_bbd->tick();
	bbd_setup_next_tick();
}

void ct8000_state::bbd_setup_next_tick()
{
	// 62.5 to 80 kHz, varies at 0.6666... Hz
	double pos = machine().time().as_double() / 1.5;
	pos -= std::floor(pos);
	pos = (pos < 0.5) ? (2 * pos) : 2 * (1.0 - pos);
	const double bbd_freq = 62500 + (80000 - 62500) * pos;

	m_bbd_timer->adjust(attotime::from_ticks(1, bbd_freq));
}


INPUT_PORTS_START(ct8000)
	PORT_START("KC0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C2")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C#2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D2")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D#2")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("E2")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F2")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F#2")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G#2")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A2")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A#2")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("B2")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C3")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C#3")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D3")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D#3")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("E3")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F3")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F#3")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G3")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G#3")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A3")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A#3")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("B3")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C4")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C#4")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D4")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D#4")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("E4")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F4")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F#4")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G4")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G#4")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A4")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A#4")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("B4")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C5")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C#5")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D5")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D#5")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("E5")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F5")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F#5")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G5")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G#5")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A5")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A#5")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("B5")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C6")
	PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Sustain Pedal")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tune Up")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tune Down")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Stereo Chorus") PORT_TOGGLE
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KCA")
	PORT_BIT(0x07, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_CUSTOM_MEMBER(FUNC(ct8000_state::switch_r))
	PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KCB")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tone Memory 1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tone Memory 2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tone Memory 3")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tone Memory 4")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tone Set") PORT_TOGGLE
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KCC")
	PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Vibrato") PORT_TOGGLE
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("SWITCH")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Effect Cancel") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ct8000_state::switch_clear_w), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Sustain")       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ct8000_state::switch_w), 0x1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Reverb")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ct8000_state::switch_w), 0x4)
INPUT_PORTS_END

INPUT_PORTS_START(ctfk1)
	PORT_START("KC0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C#1")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D1")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D#1")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("E1")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F1")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F#1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G1")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G#1")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A1")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A#1")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("B1")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C2")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C#2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D2")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D#2")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("E2")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F2")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tune Up")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tune Down")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Bass Select") PORT_TOGGLE
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KCA")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Sustain") PORT_TOGGLE
	PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KCB")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED) // KI0->KCB diode
	PORT_BIT(0x0e, IP_ACTIVE_LOW,  IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW,  IPT_KEYPAD) PORT_NAME("Tone Set") PORT_TOGGLE
	PORT_BIT(0xe0, IP_ACTIVE_LOW,  IPT_UNUSED)
INPUT_PORTS_END

INPUT_PORTS_START(ctmb1)
	PORT_START("KC0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Reverse")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Play")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Forward")
	PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Keyboard Synchro")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Record")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Reset")
	PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Pause")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Demonstration")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Number Select")
	PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tune Up")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tune Down")
	PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END


ROM_START(ct8000)
	ROM_REGION(0x800, "maincpu", 0)
	ROM_LOAD("upd8049c-364.bin", 0x000, 0x800, CRC(8c9b3f40) SHA1(5507117671300fe350df39b4ca70239079a1b0e7))

	ROM_REGION(0x1000, "bankrom", 0)
	ROM_LOAD("ct8000_2732.bin", 0x0000, 0x1000, CRC(e6f1f3f9) SHA1(637a9d2b6be2f12240724c8aa5744c5f28ac4af0))
ROM_END

ROM_START(ctfk1)
	ROM_REGION(0x800, "maincpu", 0)
	ROM_LOAD("upd8049c-364.bin", 0x000, 0x800, CRC(8c9b3f40) SHA1(5507117671300fe350df39b4ca70239079a1b0e7))

	ROM_REGION(0x1000, "bankrom", 0)
	ROM_LOAD("fk1_2732.bin", 0x0000, 0x1000, CRC(f7416356) SHA1(6d92aa5f377769327451334324f1f32b52951968))
ROM_END

ROM_START(ctmb1)
	ROM_REGION(0x800, "maincpu", 0)
	ROM_LOAD("upd8049c-364.bin", 0x000, 0x800, CRC(8c9b3f40) SHA1(5507117671300fe350df39b4ca70239079a1b0e7))

	ROM_REGION(0x1000, "bankrom", 0)
	ROM_LOAD("mb1_2732.bin", 0x0000, 0x1000, CRC(fc128110) SHA1(8ff31fdccb8200836cf414bc709d5b9a68279205))
ROM_END

} // anonymous namespace

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY  FULLNAME           FLAGS
SYST( 1983, ct8000,  0,      0,      ct8000,  ct8000,  ct8000_state,  empty_init, "Casio", "Casiotone 8000",  MACHINE_SUPPORTS_SAVE )
SYST( 1983, ctfk1,   0,      0,      ctfk1,   ctfk1,   ct8000_state,  empty_init, "Casio", "Casiotone FK-1",  MACHINE_SUPPORTS_SAVE )
SYST( 1983, ctmb1,   0,      0,      ctmb1,   ctmb1,   ct8000_state,  empty_init, "Casio", "Casiotone MB-1",  MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



ct909e_segadvd.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood


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

  Portable DVD Player with built in Sega Master System games
  has AT Games strings in ROM
  sold in Spain as MeGaTrix

  uses a Cheertek CT909E-LF System on a Chip
  CPU core is likely LEON, a VHDL implementation of SPARC v8

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


#include "emu.h"

#include "cpu/sparc/sparc.h"

#include "screen.h"
#include "speaker.h"


namespace {

class ct909e_megatrix_state : public driver_device
{
public:
	ct909e_megatrix_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen")
	{ }

	void megatrix(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void uart1_data_w(u32 data);
	u32 uart1_status_r();

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};


void ct909e_megatrix_state::uart1_data_w(u32 data)
{
	data &= 0x000000ff;
	if (data >= 0x20 && data < 0x7f)
		logerror("UART 1 sending '%c'\n", data);
	else
		logerror("UART 1 sending 0x%02X\n", data);
}

u32 ct909e_megatrix_state::uart1_status_r()
{
	return 0x00000006;
}

void ct909e_megatrix_state::mem_map(address_map &map)
{
	map(0x00000000, 0x003fffff).rom().region("maincpu", 0);
	map(0x40000000, 0x407fffff).ram().share("dram");
	map(0x80000070, 0x80000073).w(FUNC(ct909e_megatrix_state::uart1_data_w));
	map(0x80000074, 0x80000077).r(FUNC(ct909e_megatrix_state::uart1_status_r));
}

uint32_t ct909e_megatrix_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void ct909e_megatrix_state::machine_start()
{
	m_maincpu->set_state_int(SPARC_PSR, 0xa0000000);
}

void ct909e_megatrix_state::machine_reset()
{
}

static INPUT_PORTS_START( megatrix )
INPUT_PORTS_END

void ct909e_megatrix_state::megatrix(machine_config &config)
{
	SPARCV8(config, m_maincpu, 100'000'000); // unknown frequency
	m_maincpu->set_addrmap(0, &ct909e_megatrix_state::mem_map);
	m_maincpu->set_addrmap(0x19, &ct909e_megatrix_state::mem_map);
	m_maincpu->set_addrmap(0x1b, &ct909e_megatrix_state::mem_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320, 262);
	m_screen->set_visarea(0, 320-1, 0, 240-1);
	m_screen->set_screen_update(FUNC(ct909e_megatrix_state::screen_update));

	SPEAKER(config, "speaker", 2).front();
}

ROM_START( megatrix )
	ROM_REGION( 0x8400000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "segadvd_en29lv320b_007f22f9.bin", 0x000000, 0x400000, CRC(33fea2ff) SHA1(85295fd31a06149112295a9b6a8218a4a4c50893) )
ROM_END

} // anonymous namespace


CONS( 2007, megatrix,    0,       0,      megatrix, megatrix, ct909e_megatrix_state, empty_init, "<unknown>", "MeGaTrix (Spain)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



ctk2000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Devin Acker
/*
    Casio CTK-2000 keyboard (and related models)

    - CTK-2000 (2008)
      Basic 61-key model
    - CTK-3000
      Adds velocity-sensitive keys and pitch wheel
    - CTK-2100 (2009)
      More flexible sampling feature, lesson buttons double as voice/drum pads
      (based on CTK-2000, not 3000)

    Main board (M800-MDA1):

    IC1: CPU (NEC uPD800468)
        Custom chip (ARM-based), built in peripheral controllers & sound generator

    LSI2: 16Mbit ROM (OKI MR27T1602L)

    Console PCB (M800-CNA):

    IC401: LCD controller (Sitronix ST7066U-0A, HD44780 compatible)

    CTK-2000 service manual with schematics, pinouts, etc.:
    https://www.manualslib.com/manual/933451/Casio-Ctk-2000.html

 */

#include "emu.h"

#include "cpu/arm7/upd800468.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"

namespace {

class ctk2000_state : public driver_device
{
public:
	ctk2000_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "lcdc")
	{
	}

	void ctk2000(machine_config &config);

	ioport_value lcd_r() { return m_lcdc->db_r() >> 4; }
	void lcd_w(int state) { m_lcdc->db_w(state << 4); }

	void apo_w(int state);

private:
	void ctk2000_map(address_map &map) ATTR_COLD;

	virtual void driver_start() override;
	virtual void driver_reset() override;

	HD44780_PIXEL_UPDATE(lcd_update);
	void palette_init(palette_device &palette);

	required_device<upd800468_device> m_maincpu;
	required_device<hd44780_device> m_lcdc;
};

void ctk2000_state::apo_w(int state)
{
	logerror("apo_w: %x\n", state);
	/* TODO: when 0, this should turn off the LCD, speakers, etc. */
}

HD44780_PIXEL_UPDATE(ctk2000_state::lcd_update)
{
	if (x < 6 && y < 8 && line < 2 && pos < 8)
		bitmap.pix(line * 8 + y, pos * 6 + x) = state;
}

void ctk2000_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(255, 255, 255));
	palette.set_pen_color(1, rgb_t(0, 0, 0));
}

void ctk2000_state::ctk2000_map(address_map &map)
{
	// ROM is based at 0x18000000, but needs to be mirrored to the beginning of memory in order to boot
	map(0x00000000, 0x001fffff).rom().mirror(0x18e00000);
}

void ctk2000_state::driver_start()
{
}

void ctk2000_state::driver_reset()
{
}

void ctk2000_state::ctk2000(machine_config &config)
{
	// CPU
	UPD800468(config, m_maincpu, 48'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &ctk2000_state::ctk2000_map);
	m_maincpu->port_in_cb<2>().set_ioport("P2_R");
	m_maincpu->port_out_cb<2>().set_ioport("P2_W");
	m_maincpu->port_out_cb<3>().set_ioport("P3");
	// ADCs 0, 5, 6, 7 are connected to the mic input
	// ADC 4 is connected to the pitch wheel (for ctk3000)
	m_maincpu->adc_cb<1>().set_ioport("AIN1");
	m_maincpu->adc_cb<2>().set_ioport("AIN2");
	m_maincpu->adc_cb<3>().set_ioport("AIN3");

	// LCD
	HD44780(config, m_lcdc, 270'000); // TODO: Wrong device type, should be ST7066U_0A; clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 8);
	m_lcdc->set_pixel_update_cb(FUNC(ctk2000_state::lcd_update));

	// screen (for testing only)
	// TODO: the actual LCD with custom segments
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6 * 8, 8 * 2);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(ctk2000_state::palette_init), 2);
}

INPUT_PORTS_START(ctk2100)
	PORT_START("maincpu:kbd:FI0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D2
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS2
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E2
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F2
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS2
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G2

	PORT_START("maincpu:kbd:FI1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS2
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B2
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C3
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS3
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D3
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS3

	PORT_START("maincpu:kbd:FI2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F3
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G3
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS3
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A3
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS3
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B3

	PORT_START("maincpu:kbd:FI3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D4
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS4
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E4
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F4
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS4
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G4

	PORT_START("maincpu:kbd:FI4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS4
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B4
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS5
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D5
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS5

	PORT_START("maincpu:kbd:FI5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E5
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F5
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS5
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G5
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A5
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS5
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B5

	PORT_START("maincpu:kbd:FI6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C6
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS6
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D6
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS6
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E6
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F6
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS6
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G6

	PORT_START("maincpu:kbd:FI7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS6
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A6
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS6
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B6
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C7
	PORT_BIT( 0xe0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("maincpu:kbd:FI8")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("maincpu:kbd:FI9")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad -") PORT_CODE(KEYCODE_MINUS_PAD)

	PORT_START("maincpu:kbd:FI10")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad +") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Voice Pad 5 / Auto")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Synchro / Ending / Pause")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Accomp On/Off / Chord / Part Select")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Sampling")

	PORT_START("maincpu:kbd:KI0")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("maincpu:kbd:KI1")
	// "song bank" and "tone" seem to be swapped in the schematic
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Song Bank")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Tone")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Tempo Up")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Voice Set Select / Music Challenge")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Voice Pad 4 / Next")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Play / Stop")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Metronome")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Intro / Repeat")

	PORT_START("maincpu:kbd:KI2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Rhythm")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Tempo Down")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Voice Pad 2 / Watch")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Voice Pad 3 / Remember")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Voice Pad 1 / Listen")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Normal / Fill In / Rewind")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Function")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Variation / Fill In / Fast Forward")

	PORT_START("P2_R")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_CUSTOM )  PORT_CUSTOM_MEMBER(FUNC(ctk2000_state::lcd_r))
	PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2_W")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_OUTPUT )  PORT_WRITE_LINE_MEMBER(FUNC(ctk2000_state::lcd_w))
	PORT_BIT( 0x30, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OUTPUT )  PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::e_w))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OUTPUT )  PORT_WRITE_LINE_MEMBER(FUNC(ctk2000_state::apo_w))

	PORT_START("P3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OUTPUT )  PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::rw_w))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OUTPUT )  PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::rs_w))
	PORT_BIT( 0xf8, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("AIN1")
	PORT_BIT( 0x3ff, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("Pedal")

	PORT_START("AIN2")
	PORT_CONFNAME( 0x3ff, 0x000, "Power Source" )
	PORT_CONFSETTING(     0x000, "AC Adapter" )
	PORT_CONFSETTING(     0x3ff, "Battery" )

	PORT_START("AIN3")
	PORT_BIT( 0x3ff, IP_ACTIVE_LOW, IPT_UNUSED )   PORT_CONDITION("AIN2", 0x3ff, EQUALS, 0)
	PORT_CONFNAME( 0x3ff, 0x3ff, "Battery Level" ) PORT_CONDITION("AIN2", 0x3ff, NOTEQUALS, 0)
	// values here are somewhat arbitrary - ctk2100 only checks if the value is above/below a certain threshold
	PORT_CONFSETTING(     0x100, "Low" )
	PORT_CONFSETTING(     0x3ff, "Normal" )

INPUT_PORTS_END

ROM_START(ctk2100)
	ROM_REGION(0x200000, "maincpu", 0)
	ROM_LOAD("ctk2100.ic2", 0x000000, 0x200000, CRC(daf62f81) SHA1(4edc76ea04b59090d02646d92f9fc635a43140e9))
ROM_END

} // anonymous namespace

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY  FULLNAME     FLAGS
SYST( 2009, ctk2100, 0,      0,      ctk2000, ctk2100, ctk2000_state, empty_init, "Casio", "CTK-2100",  MACHINE_NO_SOUND | MACHINE_NODEVICE_MICROPHONE | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



ctk551.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Devin Acker
/*
    Casio GT913-based keyboards and MIDI modules

-------------------------------------------------------------------------------

    Celviano AP-10 digital piano (1995)

    Main board (JCM358-MA1M):
        LSI301: CPU (Casio/NEC uPD912GF)
        LSI302: DSP (Hitachi HG51B277FB)
        LSI303: 8Mbit ROM (Macronix MX23C8100MC-12)
        LSI304: 64kbit SRAM for CPU (Sanyo LC3564SM-85), battery backed
        LSI305: 256kbit SRAM for DSP (Sanyo LC333832M-70)
        LSI306: stereo DAC (NEC uPD6379GR)
        X301:   24MHz crystal for CPU
        X302:   16MHz ceramic for DSP

    Service manual with schematics, pinouts, etc.:
    https://revenant1.net/casio/manuals/upd91x/ap10.pdf

    To access the test mode (not mentioned in the service manual):
    Hold both pedals and "Transpose/Tune/MIDI" while turning on the keyboard, then release the button.
    Afterwards, press one of these buttons:
    - Transpose: LED test
    - Effect: switch test (press all front panel buttons left to right)
    - Piano: key test (press all keys left to right)
    - E.Piano: ROM test
    - Organ/Strings/Song: sound volume test
    - Record/Start/Stop: stereo test
    - Demo: MIDI loopback test
    - Harpsichord: exit test mode

    TODO: fix backup RAM getting re-initialized on every boot.
    Depends on the power switch being implemented correctly - turning the power off
    is supposed to trigger a NMI which updates the RAM checksum, but the NMI handler
    always proceeds to fully start up the system as if the power is being turned on

-------------------------------------------------------------------------------

    CTK-530/540 (1995)

    Main board (JCM460-MA1M):
        LSI101: CPU (Casio/NEC uPD912GF)
        LSI102: 8Mbit ROM (Macronix MX23C8100PC-12)
        IC103:  stereo DAC (NEC uPD6379GR)
        X301:   20MHz crystal

    Service manual with schematics, pinouts, etc.:
    https://revenant1.net/casio/manuals/upd91x/ctk530.pdf

    To access the test mode (not mentioned in the service manual):
    Hold the keypad 0 button while turning on the keyboard, then release the button.
    "TST" will appear on the LCD. Afterwards, press one of these buttons:
    - Keypad 0: switch test (press all front panel buttons in each column, top to bottom and left to right)
    - Keypad 1: key test
    - Keypad 2: ROM test
    - Keypad 4/5/6: sound volume test
    - Keypad 7/8: stereo test
    - Keypad 9: MIDI loopback test
    - Keypad +: LED/display test
    - Mode: power off

-------------------------------------------------------------------------------

    General MIDI modules (1996)

    - GZ-30M
      Basic model, small desktop module
      No 5-pin MIDI jack, only mini-DIN for RS-232 or RS-422
    - GZ-70SP
      MIDI module built into a pair of speakers w/ karaoke mic input
      Provides both standard MIDI and mini-DIN connectors
    - WG-130
      WaveBlaster-style PC daughterboard

    WG-130 board:
        LSI101: stereo DAC (NEC uPD6379GR)
        LSI102: CPU (Casio GT913F)
        LSI103: 16Mbit ROM (Casio GM16000N-C40)
        LSI104: 64kbit SRAM (Sanyo LC3564SM-85)
        LSI105: unpopulated, for DSP SRAM
        LSI106: unpopulated, for DSP
        X101: 30MHz crystal
        X102: unpopulated, for DSP

    All three of these apparently use the same mask ROM.
    This ROM was also distributed as part of Casio's SW-10 softsynth for Windows,
    which it released in early 1997 as part of the "LANA Lite" karaoke system.
    http://web.archive.org/web/20011122112757/www.casio.co.jp/lanalite/LanaSw10.exe

    The WG-130 (and possibly others) have unpopulated footprints for the same DSP
    used in some keyboards (e.g. the CTK-601). The ROM does actually support
    using the DSP if it's present, and responds to the same sysex message used to
    enable reverb on the CTK-601 and similar models (F0 44 0E 09 0x F7).

    Pulling CPU pin 53 (KI0/P24) low starts a ROM checksum test.
    The result is indicated both by sound as well as output on pin 55 (KI2/P11).

    More info and photos:
    https://piano.tyonmage.com/casio/gz-30m.html
    https://piano.tyonmage.com/casio/gz-70sp.html
    http://www.yjfy.com/museum/sound/WG-130.htm

-------------------------------------------------------------------------------

    CTK-601/611 / Concertmate 990 (1997)

    Main board (JCM462-MA1M):
        LSI1: CPU (Casio GT913F)
        LSI2: DSP (Casio GD277F / Hitachi HG51B277FB)
        LSI3: 16Mbit ROM (Macronix MX23C1610MC-12)
        LSI4: 256kbit SRAM for CPU (Toshiba TC55257DFL-70L)
        LSI5: 256kbit SRAM for DSP (same as LSI4)
        LSI6: stereo DAC (NEC uPD6379GR)
        X1:   30MHz crystal for CPU
        X2:   20MHz ceramic for DSP

    Display board (JCM462-LCD1M):
        LSI401: LCD controller (Epson SED1278F2A)

    Service manuals with schematics, pinouts, etc.:
    https://revenant1.net/casio/manuals/upd91x/ctk601.pdf
    https://revenant1.net/casio/manuals/upd91x/ctk611.pdf

    To access the test mode (not mentioned in the service manual):
    Hold the keypad 0 button while turning on the keyboard, then release the button.
    "TST" will appear on the LCD. Afterwards, press one of these buttons:
    - Keypad 0: switch test (press all front panel buttons in a specific order, generally left to right)
    - Keypad 1: pedal and key test
    - Keypad 2: ROM test
    - Keypad 4/5/6: sound volume test
    - Keypad 7/8: stereo test
    - Keypad 9: MIDI loopback test
    - Keypad +: power source test
    - Cursor Left: LCD test (all segments at once)
    - Cursor Right: LCD test (all segments individually)
    - Cursor Down: power off

-------------------------------------------------------------------------------

    CTK-551 (and related models)

    - CTK-531, CTK-533 (1999)
      Basic 61-key model
    - CTK-541, Optimus MD-1150 (1999)
      Adds velocity-sensitive keys
    - CTK-551, CTK-558, Radio Shack MD-1160 (2000)
      Adds pitch wheel and different selection of demo songs
    - CT-588 (2001)
      Chinese localized version of CTK-541
    - CT-688 (2001)
      Chinese localized version of CTK-551

    Main board (JCM453-MA1M / JCM456-MA1M):
        LSI1: CPU (Casio GT913F)
        LSI2: 8Mbit ROM (OKI MSM538002E)
        LSI3: LCD controller (HD44780 compatible)
              May be either a Samsung KS0066U-10B or Epson SED1278F2A.
        IC1:  stereo DAC (NEC uPD6379GR)
        X1:   30MHz ceramic

    Service manuals with schematics, pinouts, etc.:
    https://revenant1.net/casio/manuals/upd91x/ctk531.pdf
    https://revenant1.net/casio/manuals/upd91x/ctk541.pdf

    To access the test mode (not mentioned in the service manual):
    Hold the "Start/Stop" and keypad 0 buttons together when turning on the keyboard.
    "215dTEST" will appear on the LCD. Afterwards, press one of these buttons:
    - Tone: LCD test (press repeatedly)
    - Keypad 0: switch test (press all front panel buttons in a specific order, generally left to right)
    - Keypad 1 or Rhythm: pedal and key test
    - Keypad 2: ROM test
    - Keypad 4/5/6: sound volume test
    - Keypad 7/8: stereo test
    - Keypad 9: MIDI loopback test
    - Keypad + or Song Bank: power source test
    - Keypad -: pitch wheel test
    - FFWD: exit test mode
    - Stop: power off

 */

#include "emu.h"

#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/h8/gt913.h"
#include "machine/nvram.h"
#include "video/hd44780.h"
#include "video/pwm.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "ap10.lh"
#include "ctk530.lh"

namespace {

class ctk551_state : public driver_device
{
public:
	ctk551_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pwm(*this, "pwm")
		, m_lcdc(*this, "lcdc")
		, m_inputs(*this, "IN%u", 0U)
		, m_outputs(*this, "%02x.%d.%d", 0U, 0U, 0U)
		, m_led_touch(*this, "led_touch")
		, m_led_console(*this, "led_console_%d", 0U)
		, m_led_power(*this, "led_power")
	{
	}

	void ap10(machine_config& config);
	void ctk530(machine_config& config);
	void gz70sp(machine_config& config);
	void ctk601(machine_config& config);
	void ctk551(machine_config &config);

	void init_ap10();
	void init_ctk530();
	void init_gz70sp();

	TIMER_CALLBACK_MEMBER(nmi_clear) { m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE); }

	void pwm_row_w(int state) { m_pwm->write_my(state); }
	void pwm_col_w(int state) { m_pwm->write_mx(state ^ 0xff);  }

	ioport_value lcd_r()   { return m_lcdc->db_r() >> 4; }
	void lcd_w(int state)
	{
		m_lcd_data = state << 4;
		m_lcdc->db_w(m_lcd_data);
	}

	// some models have all 4 LCD bits wired to adjacent port bits, but some don't
	// (and even those don't always have them wired the same way -
	//  in some cases they're not even all connected to the same port)
	template <unsigned Bit>
	ioport_value lcd_bit_r() { return BIT(m_lcdc->db_r(), Bit); }
	template <unsigned Bit>
	void lcd_bit_w(int state)
	{
		m_lcd_data = (m_lcd_data & ~(1 << Bit)) | (state << Bit);
		m_lcdc->db_w(m_lcd_data);
	}

	// handle the 4-position mode switch
	// some models treat this as 3 modes plus power off,
	// while others have 4 modes and move power to a separate button instead
	ioport_value switch_r()  { return m_switch; }
	DECLARE_INPUT_CHANGED_MEMBER(switch_w);
	DECLARE_INPUT_CHANGED_MEMBER(power_w);
	DECLARE_INPUT_CHANGED_MEMBER(switch_power_w);

	void inputs_w(int state) { m_input_sel = state; }
	ioport_value inputs_r();

	void dsp_data_w(uint8_t data);
	void dsp_cmd_w(uint8_t cmd);

	void led_touch_w(int state) { m_led_touch = state; }
	void led_console_w(uint8_t state);
	void apo_w(int state);

private:
	void ap10_map(address_map &map) ATTR_COLD;
	void ctk530_map(address_map &map) ATTR_COLD;
	void gz70sp_map(address_map &map) ATTR_COLD;
	void ctk601_map(address_map &map) ATTR_COLD;

	virtual void driver_start() override;

	required_device<gt913_device> m_maincpu;
	optional_device<pwm_display_device> m_pwm;
	optional_device<hd44780_device> m_lcdc;

	emu_timer* m_nmi_timer = nullptr;

	optional_ioport_array<4> m_inputs;

	output_finder<64, 8, 5> m_outputs;
	output_finder<> m_led_touch;
	output_finder<6> m_led_console;
	output_finder<> m_led_power;

	ioport_value m_switch{};
	ioport_value m_input_sel{};

	uint8_t m_lcd_data{};
	uint32_t m_dsp_data{};

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};

INPUT_CHANGED_MEMBER(ctk551_state::switch_w)
{
	if (!oldval && newval)
		m_switch = param;
}

INPUT_CHANGED_MEMBER(ctk551_state::power_w)
{
	if (newval)
	{
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
		m_nmi_timer->adjust(attotime::never);
	}
	else
	{
		// give the CPU enough time to switch NMI to active-high so it fires again
		// otherwise, releasing the power button too quickly may be ignored
		m_nmi_timer->adjust(attotime::from_msec(100));
	}
}

INPUT_CHANGED_MEMBER(ctk551_state::switch_power_w)
{
	if (!oldval && newval)
	{
		if (m_switch == 0x1 && param != m_switch)
			m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
		else
			m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);

		m_switch = param;
	}
}

ioport_value ctk551_state::inputs_r()
{
	uint8_t result = 0xff;
	for (unsigned i = 0U; i < m_inputs.size(); i++)
		if (!BIT(m_input_sel, i))
			result &= m_inputs[i].read_safe(0xff);

	return result;
}

void ctk551_state::dsp_data_w(uint8_t data)
{
	m_dsp_data >>= 8;
	m_dsp_data |= (data << 24);
}

void ctk551_state::dsp_cmd_w(uint8_t data)
{
	logerror("dsp_cmd_w: addr = %02x, data = %08x\n", data, m_dsp_data);
}

void ctk551_state::led_console_w(uint8_t state)
{
	for (unsigned i = 0; i < 6; i++)
		m_led_console[i] = !BIT(state, i);
}

void ctk551_state::apo_w(int state)
{
	logerror("apo_w: %x\n", state);
	/* auto power off - disable the LCD and speakers
	the CPU will go to sleep until the power switch triggers a NMI */
	if (!state)
	{
		if (m_pwm.found())
			m_pwm->clear();
		if (m_lcdc.found())
			m_lcdc->reset();
	}
	m_led_power = state;
	m_maincpu->set_output_gain(ALL_OUTPUTS, state ? 1.0 : 0.0);
}


u32 ctk551_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const u8 *render = m_lcdc->render();
	for(int x=0; x != 64; x++) {
		for(int y=0; y != 8; y++) {
			u8 v = *render++;
			for(int z=0; z != 5; z++)
				m_outputs[x][y][z] = (v >> z) & 1;
		}
		render += 8;
	}

	return 0;
}


void ctk551_state::ap10_map(address_map& map)
{
	map(0x000000, 0x0fffff).rom().region("maincpu", 0).mirror(0x100000);
	map(0x300000, 0x301fff).ram().share("nvram").mirror(0x07e000);
	// TODO: DSP
	map(0x380000, 0x380000).w(FUNC(ctk551_state::dsp_data_w));
	map(0x380001, 0x380001).w(FUNC(ctk551_state::dsp_cmd_w));
	map(0x380002, 0x380003).noprw();
	map(0x380003, 0x380003).w(FUNC(ctk551_state::led_console_w));
}

void ctk551_state::ctk530_map(address_map& map)
{
	map(0x000000, 0x0fffff).rom().region("maincpu", 0).mirror(0x100000);
}

void ctk551_state::gz70sp_map(address_map& map)
{
	map(0x000000, 0x1fffff).rom().region("maincpu", 0);
	map(0x300000, 0x301fff).ram().mirror(0x07e000);
	map(0x380000, 0x380003).noprw(); // DSP is mapped here, but not actually present
}

void ctk551_state::ctk601_map(address_map& map)
{
	map(0x000000, 0x1fffff).rom().region("maincpu", 0);
	map(0x300000, 0x307fff).ram().mirror(0x078000);
	// TODO: DSP
	map(0x380000, 0x380000).w(FUNC(ctk551_state::dsp_data_w));
	map(0x380001, 0x380001).w(FUNC(ctk551_state::dsp_cmd_w));
	map(0x380002, 0x380003).noprw();
	map(0x380002, 0x380003).portr("PB").portw("PA").umask16(0x00ff);
}

void ctk551_state::driver_start()
{
	m_led_touch.resolve();
	m_led_console.resolve();
	m_led_power.resolve();
	m_outputs.resolve();

	m_nmi_timer = timer_alloc(FUNC(ctk551_state::nmi_clear), this);

	m_input_sel = 0xf;

	save_item(NAME(m_switch));
	save_item(NAME(m_input_sel));
	save_item(NAME(m_lcd_data));
	save_item(NAME(m_dsp_data));
}


void ctk551_state::ap10(machine_config& config)
{
	// CPU
	GT913(config, m_maincpu, 24_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_DATA, &ctk551_state::ap10_map);
	m_maincpu->add_route(0, "speaker", 1.0, 0);
	m_maincpu->add_route(1, "speaker", 1.0, 1);
	m_maincpu->read_adc<0>().set_constant(0);
	m_maincpu->read_adc<1>().set_constant(0);
	m_maincpu->read_port1().set_ioport("P1");
	m_maincpu->write_port1().set_ioport("P1");
	m_maincpu->read_port2().set_constant(0);
	m_maincpu->write_port2().set_nop();
	m_maincpu->read_port3().set_constant(0);
	m_maincpu->write_port3().set_nop();
	m_maincpu->write_ple().set_nop();

	NVRAM(config, "nvram");

	// TODO: DSP

	// MIDI
	auto& mdin(MIDI_PORT(config, "mdin"));
	midiin_slot(mdin);
	mdin.rxd_handler().set(m_maincpu, FUNC(gt913_device::sci_rx_w<0>));

	auto& mdout(MIDI_PORT(config, "mdout"));
	midiout_slot(mdout);
	m_maincpu->write_sci_tx<0>().set(mdout, FUNC(midi_port_device::write_txd));

	SPEAKER(config, "speaker", 2).front();

	config.set_default_layout(layout_ap10);
}

void ctk551_state::ctk530(machine_config& config)
{
	// CPU
	GT913(config, m_maincpu, 20_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_DATA, &ctk551_state::ctk530_map);
	m_maincpu->add_route(0, "speaker", 1.0, 0);
	m_maincpu->add_route(1, "speaker", 1.0, 1);
	m_maincpu->read_adc<0>().set_constant(0);
	m_maincpu->read_adc<1>().set_constant(0);
	m_maincpu->read_port1().set_ioport("P1");
	m_maincpu->write_port1().set_ioport("P1");
	m_maincpu->read_port2().set_constant(0);
	m_maincpu->write_port2().set_nop();
	m_maincpu->read_port3().set_constant(0);
	m_maincpu->write_ple().set_ioport("PLE");

	// MIDI
	auto& mdin(MIDI_PORT(config, "mdin"));
	midiin_slot(mdin);
	mdin.rxd_handler().set(m_maincpu, FUNC(gt913_device::sci_rx_w<0>));

	auto& mdout(MIDI_PORT(config, "mdout"));
	midiout_slot(mdout);
	m_maincpu->write_sci_tx<0>().set(mdout, FUNC(midi_port_device::write_txd));

	PWM_DISPLAY(config, m_pwm, 0);
	m_pwm->set_size(4, 8);
	m_pwm->set_segmask(0x7, 0xff);

	SPEAKER(config, "speaker", 2).front();

	config.set_default_layout(layout_ctk530);
}

void ctk551_state::gz70sp(machine_config& config)
{
	// CPU
	GT913(config, m_maincpu, 30_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_DATA, &ctk551_state::gz70sp_map);
	m_maincpu->add_route(0, "speaker", 1.0, 0);
	m_maincpu->add_route(1, "speaker", 1.0, 1);
	m_maincpu->read_adc<0>().set_constant(0);
	m_maincpu->read_adc<1>().set_constant(0);
	m_maincpu->read_port1().set_ioport("P1");
	m_maincpu->write_port1().set_ioport("P1");
	m_maincpu->read_port2().set_ioport("P2");
	m_maincpu->write_port2().set_ioport("P2");
	m_maincpu->read_port3().set_constant(0);
	m_maincpu->write_port3().set_nop();
	m_maincpu->write_ple().set_nop();

	// MIDI (sci0 for RS232/422, sci1 for standard MIDI)
	auto& mdin(MIDI_PORT(config, "mdin"));
	midiin_slot(mdin);
	mdin.rxd_handler().set(m_maincpu, FUNC(gt913_device::sci_rx_w<1>));

	SPEAKER(config, "speaker", 2).front();
}

void ctk551_state::ctk601(machine_config& config)
{
	// CPU
	GT913(config, m_maincpu, 30_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_DATA, &ctk551_state::ctk601_map);
	m_maincpu->add_route(0, "speaker", 1.0, 0);
	m_maincpu->add_route(1, "speaker", 1.0, 1);
	m_maincpu->read_adc<0>().set_constant(0);
	m_maincpu->read_adc<1>().set_constant(0);
	m_maincpu->read_port1().set_ioport("P1_R");
	m_maincpu->write_port1().set_ioport("P1_W");
	m_maincpu->read_port2().set_ioport("P2");
	m_maincpu->write_port2().set_ioport("P2");
	m_maincpu->read_port3().set_constant(0); // port 3 pins are shared w/ key matrix
	m_maincpu->write_port3().set_nop();
	m_maincpu->write_ple().set_nop();

	// TODO: DSP

	// MIDI
	auto& mdin(MIDI_PORT(config, "mdin"));
	midiin_slot(mdin);
	mdin.rxd_handler().set(m_maincpu, FUNC(gt913_device::sci_rx_w<0>));

	auto& mdout(MIDI_PORT(config, "mdout"));
	midiout_slot(mdout);
	m_maincpu->write_sci_tx<0>().set(mdout, FUNC(midi_port_device::write_txd));

	// LCD
	HD44780(config, m_lcdc, 270'000); // TODO: Wrong device type, should be SED1278F2A (custom mask variant of SED1278F0A?); clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 8);

	auto& screen = SCREEN(config, "screen", SCREEN_TYPE_SVG);
	screen.set_refresh_hz(60);
	screen.set_size(1000, 424);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(ctk551_state::screen_update));

	SPEAKER(config, "speaker", 2).front();

	m_switch = 0x8;
}

void ctk551_state::ctk551(machine_config &config)
{
	// CPU
	GT913(config, m_maincpu, 30'000'000 / 2);
	m_maincpu->set_addrmap(AS_DATA, &ctk551_state::ctk530_map);
	m_maincpu->add_route(0, "speaker", 1.0, 0);
	m_maincpu->add_route(1, "speaker", 1.0, 1);
	m_maincpu->read_adc<0>().set_ioport("AN0");
	m_maincpu->read_adc<1>().set_ioport("AN1");
	m_maincpu->read_port1().set_ioport("P1_R");
	m_maincpu->write_port1().set_ioport("P1_W");
	m_maincpu->read_port2().set_ioport("P2");
	m_maincpu->write_port2().set_ioport("P2");
	m_maincpu->read_port3().set_constant(0); // port 3 pins are shared w/ key matrix
	m_maincpu->write_port3().set_nop();
	m_maincpu->write_ple().set_nop();

	// MIDI
	auto &mdin(MIDI_PORT(config, "mdin"));
	midiin_slot(mdin);
	mdin.rxd_handler().set(m_maincpu, FUNC(gt913_device::sci_rx_w<0>));

	auto &mdout(MIDI_PORT(config, "mdout"));
	midiout_slot(mdout);
	m_maincpu->write_sci_tx<0>().set(mdout, FUNC(midi_port_device::write_txd));

	// LCD
	HD44780(config, m_lcdc, 270'000); // TODO: Wrong device type, should be SED1278F2A (custom mask variant of SED1278F0A?); clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 8);

	auto &screen = SCREEN(config, "screen", SCREEN_TYPE_SVG);
	screen.set_refresh_hz(60);
	screen.set_size(1000, 737);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(ctk551_state::screen_update));

	SPEAKER(config, "speaker", 2).front();

	m_switch = 0x2;
}

INPUT_PORTS_START(base_velocity)
	PORT_START("maincpu:kbd:VELOCITY")
	PORT_BIT( 0x7f, 0x7f, IPT_POSITIONAL ) PORT_NAME("Key Velocity") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_CENTERDELTA(0) PORT_CODE_DEC(KEYCODE_PGDN) PORT_CODE_INC(KEYCODE_PGUP)
INPUT_PORTS_END

INPUT_PORTS_START(ap10)
	PORT_INCLUDE(base_velocity)

	PORT_START("maincpu:kbd:FI0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A0
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS0
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B0
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C1
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS1
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D1
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS1
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E1

	PORT_START("maincpu:kbd:FI1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F1
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G1
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS1
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A1
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS1
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B1
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C2

	PORT_START("maincpu:kbd:FI2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS2
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E2
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F2
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS2
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G2
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS2

	PORT_START("maincpu:kbd:FI3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B2
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C3
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS3
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D3
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS3
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E3

	PORT_START("maincpu:kbd:FI4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS3
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS3
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A3
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS3
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B3
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C4

	PORT_START("maincpu:kbd:FI5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS4
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E4
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F4
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS4
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G4
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS4

	PORT_START("maincpu:kbd:FI6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B4
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C5
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D5
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS5
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E5

	PORT_START("maincpu:kbd:FI7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F5
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS5
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G5
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS5
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS5
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B5
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C6

	PORT_START("maincpu:kbd:FI8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS6
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D6
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS6
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E6
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F6
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS6
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G6
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS6

	PORT_START("maincpu:kbd:FI9")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A6
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS6
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B6
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C7
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS7
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D7
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS7
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E7

	PORT_START("maincpu:kbd:FI10")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F7
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS7
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G7
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS7
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A7
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS7
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B7
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C8

	PORT_START("maincpu:kbd:KI0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Transpose / Tune / MIDI") PORT_CODE(KEYCODE_1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Digital Effect")          PORT_CODE(KEYCODE_2)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Piano")                   PORT_CODE(KEYCODE_3)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("E. Piano")                PORT_CODE(KEYCODE_4)
	PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("maincpu:kbd:KI1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Harpsichord") PORT_CODE(KEYCODE_5)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Pipe Organ")  PORT_CODE(KEYCODE_6)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Strings")     PORT_CODE(KEYCODE_7)
	PORT_BIT( 0xf8, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("maincpu:kbd:KI2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Song")         PORT_CODE(KEYCODE_8)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Record")       PORT_CODE(KEYCODE_9)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Start / Stop") PORT_CODE(KEYCODE_0)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Demo")         PORT_CODE(KEYCODE_MINUS)
	PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P1")
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(ctk551_state::apo_w))
	PORT_BIT( 0x38, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("Damper Pedal")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("Soft/Sostenuto Pedal")
INPUT_PORTS_END

INPUT_PORTS_START(gz70sp)
	PORT_START("maincpu:kbd:FI0")
	PORT_START("maincpu:kbd:FI1")
	PORT_START("maincpu:kbd:FI2")
	PORT_START("maincpu:kbd:FI3")
	PORT_START("maincpu:kbd:FI4")
	PORT_START("maincpu:kbd:FI5")
	PORT_START("maincpu:kbd:FI6")
	PORT_START("maincpu:kbd:FI7")
	PORT_START("maincpu:kbd:FI8")
	PORT_START("maincpu:kbd:FI9")
	PORT_START("maincpu:kbd:FI10")
	PORT_START("maincpu:kbd:KI0")
	PORT_START("maincpu:kbd:KI1")
	PORT_START("maincpu:kbd:KI2")

	PORT_START("P1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OUTPUT ) // test mode output (1 = in progress / OK, 0 = error)
	PORT_BIT( 0xfc, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_ON ) PORT_NAME("Demo") PORT_TOGGLE
	PORT_BIT( 0x0e, IP_ACTIVE_LOW, IPT_UNUSED )
	/*
	this is actually a serial mode switch for the mini-DIN connector
	PORT_CONFNAME( 0x0e, 0x0e, "Connection to Host" )
	PORT_CONFSETTING(    0x06, "PC 1 (RS-232, 31250 baud)" )
	PORT_CONFSETTING(    0x0a, "PC 2 (RS-232, 38400 baud)" )
	PORT_CONFSETTING(    0x0c, "Mac (RS-422, 31250 baud)" )
	PORT_CONFSETTING(    0x0e, "MIDI" )
	*/
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_SERVICE )  PORT_NAME("ROM Test")
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

INPUT_PORTS_START(base_61key)
	PORT_START("maincpu:kbd:FI0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D2
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS2
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E2
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F2
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS2
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G2

	PORT_START("maincpu:kbd:FI1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS2
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B2
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C3
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS3
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D3
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS3

	PORT_START("maincpu:kbd:FI2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F3
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G3
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS3
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A3
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS3
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B3

	PORT_START("maincpu:kbd:FI3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D4
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS4
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E4
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F4
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS4
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G4

	PORT_START("maincpu:kbd:FI4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS4
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B4
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS5
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D5
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS5

	PORT_START("maincpu:kbd:FI5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E5
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F5
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS5
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G5
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A5
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS5
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B5

	PORT_START("maincpu:kbd:FI6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C6
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS6
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D6
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS6
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E6
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F6
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS6
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G6

	PORT_START("maincpu:kbd:FI7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS6
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A6
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS6
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B6
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C7
	PORT_BIT( 0xe0, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

INPUT_PORTS_START(ctk530)
	PORT_INCLUDE(base_61key)
	PORT_INCLUDE(base_velocity)

	PORT_START("maincpu:kbd:FI8")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("maincpu:kbd:FI9")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("maincpu:kbd:FI10")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad +") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad -") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Accomp Volume Up")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Main Volume Up")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Mode")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("maincpu:kbd:KI0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Demo")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tempo Up")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Main Volume Down")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Intro / Fill In")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("maincpu:kbd:KI1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Transpose / Tune / MIDI")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Rhythm")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Accomp Volume Down")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Synchro / Ending")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("maincpu:kbd:KI2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Touch Response")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tempo Down")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Start / Stop")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("SWITCH")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_ON ) PORT_NAME("Power") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ctk551_state::power_w), 0)

	PORT_START("P1")
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(ctk551_state::apo_w))
	PORT_BIT( 0x78, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x80, 0x80, "Power Source" )
	PORT_CONFSETTING(    0x80, "AC Adapter" )
	PORT_CONFSETTING(    0x00, "Battery" )

	PORT_START("PLE")
	PORT_BIT( 0x00ff, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(ctk551_state::pwm_col_w))
	PORT_BIT( 0x0f00, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(ctk551_state::pwm_row_w))
	PORT_BIT( 0xf000, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

INPUT_PORTS_START(ctk601)
	PORT_INCLUDE(base_61key)
	PORT_INCLUDE(base_velocity)

	PORT_START("maincpu:kbd:FI8")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("maincpu:kbd:FI9")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("maincpu:kbd:FI10")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Drum Pad 3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Drum Pad 1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Pitch Bend Down")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Cursor Down / Enter") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad +") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone")

	PORT_START("maincpu:kbd:KI0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Drum Pad 4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Drum Pad 2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Pitch Bend Up")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Start / Stop")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad -") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Rhythm")

	PORT_START("maincpu:kbd:KI1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Drum Pad 5")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Step")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tempo Down")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Cursor Right") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Demo")

	PORT_START("maincpu:kbd:KI2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Drum Pad 6")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Memory")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tempo Up")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Cursor Left") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Transpose / Tune / MIDI")

	PORT_START("P1_R")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(ctk551_state::lcd_bit_r<4>))
	PORT_BIT( 0x06, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(ctk551_state::lcd_bit_r<5>))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(ctk551_state::lcd_bit_r<6>))
	PORT_BIT( 0x60, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(ctk551_state::lcd_bit_r<7>))

	PORT_START("P1_W")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(ctk551_state::lcd_bit_w<4>))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(ctk551_state::apo_w))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(ctk551_state::lcd_bit_w<5>))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(ctk551_state::lcd_bit_w<6>))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OUTPUT ) // DSP reset
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(ctk551_state::lcd_bit_w<7>))

	PORT_START("P2")
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OUTPUT )  PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::e_w))
	PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("AN0")
	PORT_CONFNAME( 0xff, 0x00, "Power Source" )
	PORT_CONFSETTING(    0x00, "AC Adapter" )
	PORT_CONFSETTING(    0xff, "Battery" )

	PORT_START("AN1")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	// DSP ports
	PORT_START("PA")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(ctk551_state::inputs_w))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::rs_w))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::rw_w))
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("PB")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(ctk551_state::inputs_r))
	PORT_BIT( 0x10, IP_ACTIVE_LOW,  IPT_OTHER )  PORT_NAME("Pedal")
	PORT_BIT( 0xe0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("SWITCH")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )    PORT_NAME("Mode (Full Range Chord)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ctk551_state::switch_w), 0x1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )    PORT_NAME("Mode (Fingered)")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ctk551_state::switch_w), 0x2)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )    PORT_NAME("Mode (Casio Chord)")      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ctk551_state::switch_w), 0x4)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )    PORT_NAME("Mode (Normal)")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ctk551_state::switch_w), 0x8)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_POWER_ON ) PORT_NAME("Power")                   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ctk551_state::power_w), 0)

	PORT_START("IN0")
	PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_CUSTOM )  PORT_CUSTOM_MEMBER(FUNC(ctk551_state::switch_r))

	PORT_START("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD )  PORT_NAME("Intro")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD )  PORT_NAME("Normal / Fill In")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD )  PORT_NAME("Synchro / Ending")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD )  PORT_NAME("Variation / Fill In")

	PORT_START("IN2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD )  PORT_NAME("Touch Response")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD )  PORT_NAME("Free Session")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD )  PORT_NAME("Layer")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD )  PORT_NAME("Split")

	PORT_START("IN3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD )  PORT_NAME("Reverb")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD )  PORT_NAME("Accomp Volume")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD )  PORT_NAME("Synth")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD )  PORT_NAME("Mixer")
INPUT_PORTS_END

INPUT_PORTS_START(ctk551)
	PORT_INCLUDE(base_61key)
	PORT_INCLUDE(base_velocity)

	PORT_START("maincpu:kbd:FI8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad +") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Touch Response")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Song Bank")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Rhythm")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone")

	PORT_START("maincpu:kbd:FI9")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad -") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Accomp Volume")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Transpose / Tune / MIDI")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Chord Book")

	PORT_START("maincpu:kbd:FI10")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Play / Pause")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Rewind")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Start / Stop")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Sync / Fill In")

	PORT_START("maincpu:kbd:KI0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Right Hand On/Off")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Left Hand On/Off")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Fast Forward")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Stop")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tempo Down")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Volume Down")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tempo Up")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Volume Up")

	PORT_START("maincpu:kbd:KI1")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(ctk551_state::switch_r))

	PORT_START("maincpu:kbd:KI2")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("SWITCH")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Power Off")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ctk551_state::switch_power_w), 0x1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Mode (Normal)")      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ctk551_state::switch_power_w), 0x2)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Mode (Casio Chord)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ctk551_state::switch_power_w), 0x4)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Mode (Fingered)")    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ctk551_state::switch_power_w), 0x8)

	PORT_START("P1_R")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW,  IPT_OTHER )   PORT_NAME("Pedal")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_CUSTOM )  PORT_CUSTOM_MEMBER(FUNC(ctk551_state::lcd_r))

	PORT_START("P1_W")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OUTPUT )  PORT_WRITE_LINE_MEMBER(FUNC(ctk551_state::led_touch_w))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OUTPUT )  PORT_WRITE_LINE_MEMBER(FUNC(ctk551_state::apo_w))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OUTPUT )  PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::e_w))
	PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_OUTPUT )  PORT_WRITE_LINE_MEMBER(FUNC(ctk551_state::lcd_w))

	PORT_START("P2")
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OUTPUT )  PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::rs_w))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OUTPUT )  PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::rw_w))
	PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("AN0")
	PORT_CONFNAME( 0xff, 0x00, "Power Source" )
	PORT_CONFSETTING(    0x00, "AC Adapter" )
	PORT_CONFSETTING(    0xff, "Battery" )

	PORT_START("AN1")
	PORT_BIT( 0xff, 0x7f, IPT_PADDLE ) PORT_NAME("Pitch Wheel") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00, 0xff)
INPUT_PORTS_END


ROM_START(ap10)
	ROM_REGION(0x100000, "maincpu", 0)
	ROM_LOAD16_WORD_SWAP("ap10.lsi303", 0x000000, 0x100000, CRC(39caa214) SHA1(3b484628c1e6f0ad7c11e2ec7eff664294f9ec83)) // MX23C8100MC-12CA27
ROM_END

ROM_START(ctk530)
	ROM_REGION(0x100000, "maincpu", ROMREGION_ERASE00)

	ROM_REGION16_BE(0x100000, "lsi102", 0)
	ROM_LOAD16_WORD_SWAP("ctk530.lsi102", 0x000000, 0x100000, CRC(961bff85) SHA1(adfd46ef96fb53981b1b66cb89e3d716b0792ef0)) // MX23C8100PC-12CA19
ROM_END

ROM_START(gz70sp)
	ROM_REGION(0x200000, "maincpu", 0)
	ROM_LOAD("romsxgm.bin", 0x000000, 0x200000, CRC(c392cf89) SHA1(93ebe213ea7a085c67d88974ed39ac3e9bf8059b)) // from the SW-10 softsynth
ROM_END

ROM_START(ctk601)
	ROM_REGION(0x200000, "maincpu", 0)
	ROM_LOAD16_WORD_SWAP("ctk601.lsi3", 0x000000, 0x200000, CRC(23ae6ab1) SHA1(c1a8a1b9af19888360b56587c58602c26ad5029e)) // MX23C1610MC-12CA62

	ROM_REGION(366949, "screen", 0)
	ROM_LOAD("ctk601.svg", 0, 366949, CRC(f150ca5a) SHA1(203fc05171ae6f5ef69c13dc4c0f538fb1ea152b))
ROM_END

ROM_START(ctk551)
	ROM_REGION(0x100000, "maincpu", 0)
	ROM_LOAD16_WORD_SWAP("ctk551.lsi2", 0x000000, 0x100000, CRC(66fc34cd) SHA1(47e9559edc106132f8a83462ed17a6c5c3872157)) // MSM538002E-T6

	ROM_REGION(285279, "screen", 0)
	ROM_LOAD("ctk551lcd.svg", 0, 285279, CRC(1bb5da03) SHA1(a0cf22c6577c4ff0119ee7bb4ba8b487e23872d4))
ROM_END


void ctk551_state::init_ap10()
{
	uint16_t* rom = (uint16_t*)memregion("maincpu")->base();
	for (uint32_t addr = 0; addr < 0x80000; addr++)
		rom[addr] = bitswap(rom[addr], 15, 14, 13, 10, 11, 12, 9, 8, 7, 6, 2, 3, 4, 5, 1, 0);
}

void ctk551_state::init_ctk530()
{
	uint16_t* dest = (uint16_t*)memregion("maincpu")->base();
	const uint16_t* src = (uint16_t*)memregion("lsi102")->base();
	for (uint32_t i = 0; i < 0x80000; i++)
	{
		const uint32_t addr = bitswap(i, 8, 9, 0, 2, 4, 6, 17, 16, 14, 12, 10, 11, 13, 15, 18, 7, 5, 3, 1);
		dest[addr] = bitswap(src[i], 0, 2, 15, 13, 4, 6, 11, 9, 1, 3, 14, 12, 5, 7, 10, 8);
	}
}

void ctk551_state::init_gz70sp()
{
	/*
	the version of this ROM bundled with the SW-10 softsynth has little endian samples, so byteswap them
	(and stop at the end of sample data, not the end of the whole ROM, otherwise the ROM test fails)
	*/
	uint16_t* rom = (uint16_t*)memregion("maincpu")->base();
	for (uint32_t addr = 0x2f000 >> 1; addr < 0x1fe8c2 >> 1; addr++)
		rom[addr] = swapendian_int16(rom[addr]);
}

} // anonymous namespace

// models with MACHINE_IMPERFECT_SOUND are missing DSP emulation
//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT         COMPANY  FULLNAME          FLAGS
SYST( 1995, ap10,    0,      0,      ap10,    ap10,   ctk551_state, init_ap10,   "Casio", "Celviano AP-10", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )
SYST( 1995, ctk530,  0,      0,      ctk530,  ctk530, ctk551_state, init_ctk530, "Casio", "CTK-530",        MACHINE_SUPPORTS_SAVE )
SYST( 1996, gz70sp,  0,      0,      gz70sp,  gz70sp, ctk551_state, init_gz70sp, "Casio", "GZ-70SP",        MACHINE_SUPPORTS_SAVE )
SYST( 1997, ctk601,  0,      0,      ctk601,  ctk601, ctk551_state, empty_init,  "Casio", "CTK-601",        MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )
SYST( 2000, ctk551,  0,      0,      ctk551,  ctk551, ctk551_state, empty_init,  "Casio", "CTK-551",        MACHINE_SUPPORTS_SAVE )



ctvboy.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
/*******************************************************************************

Gakken Compact Vision TV Boy (TVボーイ)

Hardware notes:
- MC6801U4 or HD6801V0P @ 3.57MHz
- MC6847P, MC1372P
- 2KB RAM (HM6116P-4)
- 1-bit sound with volume decay
- cartridge slot

The MCU is inside the cartridge, not the console, in theory it could have any MCU.
The console itself has the video hardware and controls.

TODO:
- change cartridge to slot device? there are free homebrew games by Inufuto that
  currently won't work, since cartridge PCB has an MC6803 + 32KB ROM + 8KB RAM

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

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/m6800/m6801.h"
#include "sound/dac.h"
#include "sound/flt_vol.h"
#include "video/mc6847.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

namespace {

class ctvboy_state : public driver_device
{
public:
	ctvboy_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_vram(*this, "vram"),
		m_mc6847(*this, "mc6847"),
		m_screen(*this, "screen"),
		m_dac(*this, "dac"),
		m_volume(*this, "volume"),
		m_cart(*this, "cartslot"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void ctvboy(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<m6801u4_cpu_device> m_maincpu;
	required_shared_ptr<u8> m_vram;
	required_device<mc6847_device> m_mc6847;
	required_device<screen_device> m_screen;
	required_device<dac_bit_interface> m_dac;
	required_device<filter_volume_device> m_volume;
	required_device<generic_slot_device> m_cart;
	required_ioport_array<2> m_inputs;

	emu_timer *m_decaytimer;
	bool m_speaker_on = false;
	u8 m_inp_mux = 0;

	void speaker_decay_sim(s32 param);

	void main_map(address_map &map) ATTR_COLD;
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	void p1_w(u8 data);
	u8 p1_r();
	u8 p2_r();

	void mc6847_w(u8 data);
	u8 mc6847_vram_r(offs_t offset);
	void vblank_irq(int state);
};



/*******************************************************************************
    Initialization
*******************************************************************************/

void ctvboy_state::machine_start()
{
	m_decaytimer = timer_alloc(FUNC(ctvboy_state::speaker_decay_sim), this);

	// register for savestates
	save_item(NAME(m_speaker_on));
	save_item(NAME(m_inp_mux));
}

DEVICE_IMAGE_LOAD_MEMBER(ctvboy_state::cart_load)
{
	u32 size = m_cart->common_get_size("rom");

	if (size != 0x1000)
		return std::make_pair(image_error::INVALIDLENGTH, "Invalid ROM file size (must be 4096 bytes)");

	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_BIG);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");
	memcpy(memregion("maincpu")->base(), m_cart->get_rom_base(), size);

	return std::make_pair(std::error_condition(), std::string());
}



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

void ctvboy_state::speaker_decay_sim(s32 param)
{
	// volume decays when speaker is off (divisor and timer period determine duration)
	m_volume->set_gain(m_volume->gain() / 1.01);
	m_decaytimer->adjust(attotime::from_msec(1));
}

void ctvboy_state::p1_w(u8 data)
{
	// P10,P11: input mux
	m_inp_mux = ~data & 3;

	// P15: speaker on
	bool speaker_on = bool(data & 0x20);
	if (speaker_on)
	{
		m_volume->set_gain(1.0);
		m_decaytimer->adjust(attotime::never);
	}
	else if (m_speaker_on)
		m_decaytimer->adjust(attotime::zero);

	m_speaker_on = speaker_on;

	// P16: speaker out
	m_dac->write(BIT(data, 6));
}

u8 ctvboy_state::p1_r()
{
	// P17: MC6847 HS
	return m_mc6847->hs_r() ? 0x80 : 0;
}

u8 ctvboy_state::p2_r()
{
	u8 data = 0;

	// P21-P24: multiplexed inputs
	for (int i = 0; i < 2; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read();

	return ~data;
}

void ctvboy_state::mc6847_w(u8 data)
{
	// write to MC6847 pins
	m_mc6847->gm1_w(BIT(data, 0));
	m_mc6847->gm0_w(BIT(data, 1));
	m_mc6847->intext_w(BIT(data, 2));
	m_mc6847->as_w(BIT(data, 3));
	m_mc6847->ag_w(BIT(data, 4));
	m_mc6847->css_w(BIT(data, 5));
}

u8 ctvboy_state::mc6847_vram_r(offs_t offset)
{
	u8 data = m_vram[offset & 0x7ff];
	m_mc6847->inv_w(BIT(data, 6));

	return data;
}

void ctvboy_state::vblank_irq(int state)
{
	if (!state)
		m_maincpu->pulse_input_line(M6801_IRQ1_LINE, attotime::from_usec(15));
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void ctvboy_state::main_map(address_map &map)
{
	map(0x1000, 0x17ff).ram().share(m_vram);
	map(0x2000, 0x2000).w(FUNC(ctvboy_state::mc6847_w));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( ctvboy )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON1) // A
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_BUTTON2) // START / B
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_SELECT) PORT_NAME("Pause")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void ctvboy_state::ctvboy(machine_config &config)
{
	// basic machine hardware
	M6801U4(config, m_maincpu, 3.579545_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &ctvboy_state::main_map);
	m_maincpu->out_p1_cb().set(FUNC(ctvboy_state::p1_w));
	m_maincpu->in_p1_cb().set(FUNC(ctvboy_state::p1_r));
	m_maincpu->in_p2_cb().set(FUNC(ctvboy_state::p2_r));

	// video hardware
	MC6847(config, m_mc6847, 3.579545_MHz_XTAL);
	m_mc6847->input_callback().set(FUNC(ctvboy_state::mc6847_vram_r));
	m_mc6847->fsync_wr_callback().set(FUNC(ctvboy_state::vblank_irq));
	m_mc6847->set_screen(m_screen);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	FILTER_VOLUME(config, m_volume).set_gain(0.0).add_route(ALL_OUTPUTS, "speaker", 1.0);
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "volume", 0.25);

	// cartridge
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "ctvboy_cart");
	m_cart->set_must_be_loaded(true);
	m_cart->set_device_load(FUNC(ctvboy_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("ctvboy");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( ctvboy )
	// nothing here yet, ROM is on the cartridge
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASEFF )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1983, ctvboy, 0,      0,      ctvboy,  ctvboy, ctvboy_state, empty_init, "Gakken", "Compact Vision TV Boy", MACHINE_SUPPORTS_SAVE )



cubieboard4.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************

Skeleton driver for "Cubieboard4" (CC A-80) and arcade games based on it.

Cubieboard4, also named CC-A80, is a open source mini PC or single board computer.
The main chipset Allwinner A80 is a 28nm Octa-Core A15/A7 big.LITTLE architecture application processor
with a CPU dominant frequency of 2GHz. It also has the GPU 64-core GPU graphics core PowerVR G6230 which
supports openGL ES, OpenGL, and OpenCL.
CC-A80 has the standard interfaces like desktop computer, such as HDMI & VGA, 100M/1000M RJ45, 4 USB2.0
host ports, 1 USB3.0 OTG port, audio output, microphone input, dual-band wifi and bluetooth 4.0, micro SD
card. It has 2GB DDR3 on-board memory and support Li-Po battery UPS powerinput.

Main Cubieboard4 components:
-Allwinner A80.
-Realtek RTL8211E integrated Ethernet transceiver.
-AMPAK Technology AP6330 WiFi + Bluetooth 4.0(HS) + FM Rx Module.
-SKhynix KLM4G1YEMD-B031 (embedded MultiMediaCard Ver. 5.0 compatible, soldered to the back side of the PCB).
-4 x H5TQ4G63AFR 4Gb DDR3 SDRAM.
-X-Powers AXP809 PMIC.
-X-Powers AC100 audio codec and RTC subsystem.

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

#include "emu.h"
#include "cpu/arm7/arm7.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "softlist_dev.h"

namespace {

class cubiecca80_state : public driver_device
{
public:
	cubiecca80_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_cart_ext(*this, "emmcslot"),
		m_cart_int(*this, "sdcardslot"),
		m_maincpu(*this, "maincpu")

	{ }

	void cubiecca80(machine_config &config);

protected:
	optional_device<generic_slot_device> m_cart_ext;
	optional_device<generic_slot_device> m_cart_int;

private:
	required_device<cpu_device> m_maincpu;
};

static INPUT_PORTS_START( cubiecca80 )
INPUT_PORTS_END

void cubiecca80_state::cubiecca80(machine_config &config)
{
	// Basic machine hardware
	ARM9(config, m_maincpu, 200'000'000); // Actually an Allwinner A80 2 GHz

	// Video hardware
	//SCREEN(...)

	// Audio hardware
	//SPEAKER(...)

	GENERIC_CARTSLOT(config, m_cart_ext, generic_plain_slot, "sdcard"); // Removable MicroSD
	GENERIC_CARTSLOT(config, m_cart_int, generic_plain_slot, "emmc");   // Internal eMMC

	// Software list for adding other compatible software (Linux distros, etc.).
	SOFTWARE_LIST(config, "software_list").set_original("cubieboard4");
}

// Main machine, for booting SD card and eMMC images from the software list

ROM_START( cubieboard4 )
ROM_END

// Arcade games

/* Monkey Jump. Android-based arcade from the Spanish company Falgas.
   Has a separate I/O board, with a PIC18F46K22, connected by USB to the Cubieboard4 (using a RS-232 to USB adapter).
   More info: https://www.recreativas.org/monkey-jump-4029-falgas */
ROM_START( monkeyjmp )
	DISK_REGION( "nand" )
	DISK_IMAGE( "mmcblk1", 0, SHA1(5c005005b2ca17b916b2cccd48d291b19337d4cc) )

	ROM_REGION( 0x2000, "io", 0 )
	ROM_LOAD( "pic18f46k22.bin", 0x0000, 0x2000, NO_DUMP ) // 1024 bytes internal ROM
ROM_END

} // anonymous namespace

// Main machine
COMP( 20??, cubieboard4, 0, 0, cubiecca80, cubiecca80, cubiecca80_state, empty_init, "Cubietech Limited", "Cubieboard4 (CC A-20)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )

// Arcade games
GAME( 20??, monkeyjmp, 0, cubiecca80, cubiecca80, cubiecca80_state, empty_init, ROT90, "Falgas", "Monkey Jump", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



cvicny.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

CVICNY8080 - Practice-z80 - a homebrew from Czechoslavakia.

More data at :
    https://web.archive.org/web/20190202185251/http://www.nostalcomp.cz/cvicny8080.php

2011-10-21 New working driver. [Robbbert]

Keys:
    0-9,A-F : hexadecimal numbers
    ADR : enter an address to work with. After the 4 digits are entered,
          the data at that address shows, and you can modify the data.
    + (inc) : Enter the data into memory, and increment the address by 1.
    GO : execute the program located at the current address.

Pasting:
    0-F : as is
    + (inc) : ^
    ADR : -
    GO : X

Test Paste:
    11^22^33^44^55^66^77^88^99^-0800
    Now press up-arrow to confirm the data has been entered.


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

#include "emu.h"
#include "cpu/z80/z80.h"
#include "video/pwm.h"
#include "cvicny.lh"


namespace {

class cvicny_state : public driver_device
{
public:
	cvicny_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_display(*this, "display")
		, m_io_keyboard(*this, "X%u", 0U)
	{ }

	void cvicny(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void cvicny_mem(address_map &map) ATTR_COLD;
	u8 key_r();
	void digit_w(u8 data);
	void segment_w(u8 data);

	u8 m_digit = 0U;
	u8 m_seg = 0U;
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_ioport_array<8> m_io_keyboard;
};

void cvicny_state::segment_w(u8 data) // output segments on the selected digit
{
	m_seg = data;
	m_display->matrix(1<<m_digit, m_seg);
}

void cvicny_state::digit_w(u8 data) // set keyboard scanning row; set digit to display
{
	m_digit = data & 7;
	m_display->matrix(1<<m_digit, m_seg);
}

u8 cvicny_state::key_r()
{
	u8 data = m_io_keyboard[m_digit]->read();
	return ((data << 4) ^ 0xf0) | data;
}

void cvicny_state::machine_start()
{
	save_item(NAME(m_digit));
	save_item(NAME(m_seg));
}

void cvicny_state::cvicny_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).rom(); // 1 x 2716
	map(0x0800, 0x0bff).ram().mirror(0x400); // 2x 2114 static ram
	map(0x1000, 0x17ff).r(FUNC(cvicny_state::key_r));
	map(0x1800, 0x1fff).w(FUNC(cvicny_state::digit_w));
	map(0x2000, 0x27ff).w(FUNC(cvicny_state::segment_w));
}


/* Input ports */
static INPUT_PORTS_START( cvicny )
	PORT_START("X0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("X1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')

	PORT_START("X2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')

	PORT_START("X3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')

	PORT_START("X4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("+") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ADR") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GO") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X5")
	PORT_BIT( 0x0F, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X6")
	PORT_BIT( 0x0F, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X7")
	PORT_BIT( 0x0F, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END


void cvicny_state::cvicny(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(2'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &cvicny_state::cvicny_mem);

	/* video hardware */
	config.set_default_layout(layout_cvicny);
	PWM_DISPLAY(config, m_display).set_size(8, 8);
	m_display->set_segmask(0xff, 0xff);
}

/* ROM definition */
ROM_START( cvicny )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD("cvicny8080.bin", 0x0000, 0x05ea, CRC(e6119052) SHA1(d03c2cbfd047f0d090a787fbbde6353593cc2dd8) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY      FULLNAME        FLAGS
COMP( 1984, cvicny, 0,      0,      cvicny,  cvicny, cvicny_state, empty_init, "<unknown>", "Practice-z80", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



cxhumax.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Lukasz Markowski
/***************************************************************************
    HUMAX HDCI-2000 ( Conexant CX2417x )

    http://www.humaxdigital.com/global/products/product_stb_satellite_hdci2000.asp

    Running on Nucleus PLUS - ARM7TDMI ADS v. 1.14
    some Conexant/Nucleus goodies may be found at http://code.google.com/p/cherices/

    runs up to frame 280 or so...

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

#include "emu.h"
#include "cxhumax.h"

#include "emupal.h"
#include "screen.h"


#define VERBOSE ( 0 )
#include "logmacro.h"

uint32_t cxhumax_state::cx_gxa_r(offs_t offset)
{
	uint32_t res = m_gxa_cmd_regs[offset];
	LOG("%s: (GXA) %08X -> %08X\n", machine().describe_context(), 0xE0600000 + (offset << 2), res);
/*  uint8_t gxa_command_number = (offset >> 9) & 0x7F;
    LOG("      Command: %08X\n", gxa_command_number);
    switch (gxa_command_number) {
        case GXA_CMD_RW_REGISTER:
            switch(offset) {
                case GXA_CFG2_REG:
                    break;
                default:
                    LOG("      Unimplemented register - TODO?\n");
                    break;
            }
            break;
        default:
            // do we need it?
            LOG("      Unimplemented read command - TODO?\n");
            break;
    }*/
	return res;
}

void cxhumax_state::cx_gxa_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (GXA) %08X <- %08X\n", machine().describe_context(), 0xE0600000 + (offset << 2), data);
	uint8_t gxa_command_number = (offset >> 9) & 0x7F;
	LOG("      Command: %08X\n", gxa_command_number);

	/* Clear non persistent data */
	m_gxa_cmd_regs[GXA_CMD_REG] &= 0xfffc0000;

	if (gxa_command_number == GXA_CMD_RW_REGISTER) {
		LOG("      Register Number: %08X\n", offset & 0xff);
	} else {
		m_gxa_cmd_regs[GXA_CMD_REG] |= (offset << 2) & 0x3ffff;
		LOG("      Source Bitmap Selector: %08X\n", (offset >> 6) & 0x7);
		LOG("      Destination Bitmap Selector: %08X\n", (offset >> 3) & 0x7);
		LOG("      Parameter Count: %08X\n", offset & 0x7);
	}
	switch (gxa_command_number) {
		case GXA_CMD_RW_REGISTER:
			switch(offset) {
				case GXA_CFG2_REG:
					// clear IRQ_STAT bits if requested
					m_gxa_cmd_regs[GXA_CFG2_REG] = (m_gxa_cmd_regs[GXA_CFG2_REG]&(0xfff00000 & ~(data&0x00300000))) | (data & 0x000fffff);
					break;
				default:
					LOG("      Unimplemented register - TODO?\n");
					COMBINE_DATA(&m_gxa_cmd_regs[offset]);
					break;
			}
			break;
		case GXA_CMD_QMARK:
			LOG("      QMARK - TODO?\n");

			/* Set value and copy of WAIT4_VERTICAL bit written by QMARK */
			m_gxa_cmd_regs[GXA_CMD_REG] = (m_gxa_cmd_regs[GXA_CMD_REG] & 0x3ffff) | (data<<24) | ((data&0x10)?1<<23:0);

			/* QMARK command has completed */
			m_gxa_cmd_regs[GXA_CFG2_REG] |= (1<<IRQ_STAT_QMARK);

			// Interrupt
			if (m_gxa_cmd_regs[GXA_CFG2_REG] & (1<<IRQ_EN_QMARK)) {
				m_intctrl_regs[INTREG(INTGROUP2, INTIRQ)] |= 1<<18;
				m_intctrl_regs[INTREG(INTGROUP2, INTSTATCLR)] |= 1<<18;
				m_intctrl_regs[INTREG(INTGROUP2, INTSTATSET)] |= 1<<18;
				LOG("      QMARK INT - TODO?\n");
			}

			if((m_intctrl_regs[INTREG(INTGROUP2, INTIRQ)] & m_intctrl_regs[INTREG(INTGROUP2, INTENABLE)])
				|| (m_intctrl_regs[INTREG(INTGROUP1, INTIRQ)] & m_intctrl_regs[INTREG(INTGROUP1, INTENABLE)]))
					m_maincpu->set_input_line(arm7_cpu_device::ARM7_IRQ_LINE, ASSERT_LINE);

			break;
		default:
			LOG("      Unimplemented command - TODO?\n");
			break;
	}
}

void cxhumax_state::flash_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	offset *= 2;
	if(ACCESSING_BITS_0_15)
		m_flash->write(offset, data);
	if(ACCESSING_BITS_16_31)
		m_flash->write(offset+1, data >> 16);
	LOG("%s: (FLASH) %08X <- %08X\n", machine().describe_context(), 0xF0000000 + (offset << 2), data);
}

uint32_t cxhumax_state::flash_r(offs_t offset, uint32_t mem_mask)
{
	uint32_t res = 0;
	offset *= 2;
	if(ACCESSING_BITS_0_15)
		res |= m_flash->read(offset);
	if(ACCESSING_BITS_16_31)
		res |= m_flash->read(offset+1) << 16;
	//if(m_flash->m_flash_mode!=FM_NORMAL) LOG("%s: (FLASH) %08X -> %08X\n", machine().describe_context(), 0xF0000000 + (offset << 2), res);
	return res;
}

uint32_t cxhumax_state::dummy_flash_r()
{
	return 0xFFFFFFFF;
}

void cxhumax_state::cx_remap_w(offs_t offset, uint32_t data)
{
	if(!(data&1)) {
		LOG("%s: (REMAP) %08X -> %08X\n", machine().describe_context(), 0xE0400014 + (offset << 2), data);
		memset(m_ram, 0, 0x400000); // workaround :P
	}
}

uint32_t cxhumax_state::cx_scratch_r(offs_t offset)
{
	uint32_t data = m_scratch_reg;
	LOG("%s: (SCRATCH) %08X -> %08X\n", machine().describe_context(), 0xE0400024 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_scratch_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (SCRATCH) %08X <- %08X\n", machine().describe_context(), 0xE0400024 + (offset << 2), data);
	COMBINE_DATA(&m_scratch_reg);
}

uint32_t cxhumax_state::cx_hsx_r(offs_t offset)
{
	uint32_t data = 0; // dummy
	LOG("%s: (HSX) %08X -> %08X\n", machine().describe_context(), 0xE0000000 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_hsx_w(offs_t offset, uint32_t data)
{
	LOG("%s: (HSX) %08X <- %08X\n", machine().describe_context(), 0xE0000000 + (offset << 2), data);
}

uint32_t cxhumax_state::cx_romdescr_r(offs_t offset)
{
	uint32_t data = m_romdescr_reg;
	LOG("%s: (ROMDESC0) %08X -> %08X\n", machine().describe_context(), 0xE0010000 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_romdescr_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (ROMDESC0) %08X <- %08X\n", machine().describe_context(), 0xE0010000 + (offset << 2), data);
	COMBINE_DATA(&m_romdescr_reg);
}

uint32_t cxhumax_state::cx_isaromdescr_r(offs_t offset)
{
	uint32_t data = m_isaromdescr_regs[offset];
	LOG("%s: (ISAROMDESC%d) %08X -> %08X\n", machine().describe_context(), offset+1, 0xE0010004 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_isaromdescr_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (ISAROMDESC%d) %08X <- %08X\n", machine().describe_context(), offset+1, 0xE0010004 + (offset << 2), data);
	COMBINE_DATA(&m_isaromdescr_regs[offset]);
}

uint32_t cxhumax_state::cx_isadescr_r(offs_t offset)
{
	uint32_t data = m_isaromdescr_regs[offset];
	LOG("%s: (ISA_DESC%d) %08X -> %08X\n", machine().describe_context(), offset+4, 0xE0010010 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_isadescr_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (ISA_DESC%d) %08X <- %08X\n", machine().describe_context(), offset+4, 0xE0010010 + (offset << 2), data);
	COMBINE_DATA(&m_isaromdescr_regs[offset]);
}

uint32_t cxhumax_state::cx_rommap_r(offs_t offset)
{
	uint32_t data = 0;
	LOG("%s: (ROM%d_MAP) %08X -> %08X\n", machine().describe_context(), offset, 0xE0010020 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_rommap_w(offs_t offset, uint32_t data)
{
	LOG("%s: (ROM%d_MAP) %08X <- %08X\n", machine().describe_context(), offset, 0xE0010020 + (offset << 2), data);
}

uint32_t cxhumax_state::cx_rommode_r(offs_t offset)
{
	uint32_t data = m_rommode_reg;
	LOG("%s: (ROMMODE) %08X -> %08X\n", machine().describe_context(), 0xE0010034 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_rommode_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (ROMMODE) %08X <- %08X\n", machine().describe_context(), 0xE0010034 + (offset << 2), data);
	COMBINE_DATA(&m_rommode_reg);
}

uint32_t cxhumax_state::cx_xoemask_r(offs_t offset)
{
	uint32_t data = m_xoemask_reg;
	LOG("%s: (XOEMASK) %08X -> %08X\n", machine().describe_context(), 0xE0010034 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_xoemask_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (XOEMASK) %08X <- %08X\n", machine().describe_context(), 0xE0010034 + (offset << 2), data);
	COMBINE_DATA(&m_xoemask_reg);
}

uint32_t cxhumax_state::cx_pci_r(offs_t offset)
{
	uint32_t data = 0;
	switch (offset) {
		case PCI_CFG_ADDR_REG:
			data = m_pci_regs[offset]; break;
		case PCI_CFG_DATA_REG:
			{
				switch (m_pci_regs[PCI_CFG_ADDR_REG]) {
					case 0: data = (0x4170<<16) /*Device ID*/ | 0x14f1 /* Vendor ID */; break;
					case 8: data = (0x060000 << 8) /* Class Code */ | 0x1f /* Revision ID */; break;
				}
			} break;
	}
	LOG("%s: (PCI) %08X -> %08X\n", machine().describe_context(), 0xE0010040 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_pci_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (PCI) %08X <- %08X\n", machine().describe_context(), 0xE0010040 + (offset << 2), data);
	COMBINE_DATA(&m_pci_regs[offset]);
}

uint32_t cxhumax_state::cx_extdesc_r(offs_t offset)
{
	uint32_t data = m_extdesc_regs[offset];
	LOG("%s: (EXTDESC) %08X -> %08X\n", machine().describe_context(), 0xE0010080 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_extdesc_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (EXTDESC) %08X <- %08X\n", machine().describe_context(), 0xE0010080 + (offset << 2), data);
	COMBINE_DATA(&m_extdesc_regs[offset]);
}

TIMER_CALLBACK_MEMBER(cxhumax_state::timer_tick)
{
	m_timer_regs.timer[param].value++;
	if(m_timer_regs.timer[param].value==m_timer_regs.timer[param].limit) {
		/* Reset counter when reaching limit and RESET_CNTR bit is cleared */
		if(!(m_timer_regs.timer[param].mode & 2))
			m_timer_regs.timer[param].value=0;

		/* Indicate interrupt request if EN_INT bit is set */
		if (m_timer_regs.timer[param].mode & 8) {
			//printf( "IRQ on Timer %d\n", param );
			LOG("%s: (TIMER%d) Interrupt\n", machine().time().to_string(), param);
			m_intctrl_regs[INTREG(INTGROUP2, INTIRQ)] |= INT_TIMER_BIT;     /* Timer interrupt */
			m_intctrl_regs[INTREG(INTGROUP2, INTSTATCLR)] |= INT_TIMER_BIT; /* Timer interrupt */
			m_intctrl_regs[INTREG(INTGROUP2, INTSTATSET)] |= INT_TIMER_BIT; /* Timer interrupt */

			m_timer_regs.timer_irq |= 1<<param; /* Indicate which timer interrupted */

			/* Interrupt if Timer interrupt is not masked in ITC_INTENABLE_REG */
			if (m_intctrl_regs[INTREG(INTGROUP2, INTENABLE)] & INT_TIMER_BIT)
				m_maincpu->set_input_line(arm7_cpu_device::ARM7_IRQ_LINE, ASSERT_LINE);
		}
	}
	attotime period = attotime::from_hz(XTAL(54'000'000))*m_timer_regs.timer[param].timebase;
	m_timer_regs.timer[param].timer->adjust(period,param);
}

uint32_t cxhumax_state::cx_timers_r(offs_t offset)
{
	uint32_t data = 0;
	uint8_t index = offset>>2;
	if(index==16) {
		data = m_timer_regs.timer_irq;
		//m_timer_regs.timer_irq=0;
		LOG("%s: (TIMERIRQ) %08X -> %08X\n", machine().describe_context(), 0xE0430000 + (offset << 2), data);
	}
	else {
		switch (offset&3) {
			case TIMER_VALUE:
				data = m_timer_regs.timer[index].value; break;
			case TIMER_LIMIT:
				data = m_timer_regs.timer[index].limit; break;
			case TIMER_MODE:
				data = m_timer_regs.timer[index].mode; break;
			case TIMER_TIMEBASE:
				data = m_timer_regs.timer[index].timebase; break;
		}
		LOG("%s: (TIMER%d) %08X -> %08X\n", machine().describe_context(), offset>>2, 0xE0430000 + (offset << 2), data);
	}
	return data;
}

void cxhumax_state::cx_timers_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	uint8_t index = offset>>2;
	if(index==16) {
		LOG("%s: (TIMERIRQ) %08X <- %08X\n", machine().describe_context(), 0xE0430000 + (offset << 2), data);
		COMBINE_DATA(&m_timer_regs.timer_irq);
	}
	else {
		LOG("%s: (TIMER%d) %08X <- %08X\n", machine().describe_context(), index, 0xE0430000 + (offset << 2), data);
		switch(offset&3) {
			case TIMER_VALUE:
				COMBINE_DATA(&m_timer_regs.timer[index].value); break;
			case TIMER_LIMIT:
				COMBINE_DATA(&m_timer_regs.timer[index].limit); break;
			case TIMER_MODE:
				COMBINE_DATA(&m_timer_regs.timer[index].mode);
				if(data&1) {
					attotime period = attotime::from_hz(XTAL(54'000'000))*m_timer_regs.timer[index].timebase;
					m_timer_regs.timer[index].timer->adjust(period,index);
				} else {
					m_timer_regs.timer[index].timer->adjust(attotime::never,index);
				} break;
			case TIMER_TIMEBASE:
				COMBINE_DATA(&m_timer_regs.timer[index].timebase); break;
		}
		/* A timer will hold an interrupt active until any one of that timer?s registers is written. */
		if(m_timer_regs.timer_irq & (1<<index)) {
			m_timer_regs.timer_irq &= ~(1<<index);
		}
	}
}

uint32_t cxhumax_state::cx_uart2_r(offs_t offset)
{
	uint32_t data;
	switch (offset) {
		case UART_STAT_REG:
			/* Transmitter Idle */
			data = UART_STAT_TID_BIT | UART_STAT_TSR_BIT; break;
		default:
			data = m_uart2_regs[offset]; break;
	}
	LOG("%s: (UART2) %08X -> %08X\n", machine().describe_context(), 0xE0411000 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_uart2_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (UART2) %08X <- %08X\n", machine().describe_context(), 0xE0411000 + (offset << 2), data);
	switch (offset) {
		case UART_FIFO_REG:
			if(!(m_uart2_regs[UART_FRMC_REG]&UART_FRMC_BDS_BIT)) {
				/* Sending byte... add logging */
				m_terminal->write(data);

				/* Transmitter Idle Interrupt Enable */
				if(m_uart2_regs[UART_IRQE_REG]&UART_IRQE_TIDE_BIT) {
					/* Signal pending INT */
					m_intctrl_regs[INTREG(INTGROUP1, INTIRQ)] |= INT_UART2_BIT;
					m_intctrl_regs[INTREG(INTGROUP1, INTSTATCLR)] |= INT_UART2_BIT;
					m_intctrl_regs[INTREG(INTGROUP1, INTSTATSET)] |= INT_UART2_BIT;

					/* If INT is enabled at INT Ctrl raise it */
					if(m_intctrl_regs[INTREG(INTGROUP1, INTENABLE)]&INT_UART2_BIT) {
						m_maincpu->set_input_line(arm7_cpu_device::ARM7_IRQ_LINE, ASSERT_LINE);
					}
				}
			}
			[[fallthrough]];
		default:
			COMBINE_DATA(&m_uart2_regs[offset]); break;
	}
}

uint32_t cxhumax_state::cx_pll_r(offs_t offset)
{
	uint32_t data = m_pll_regs[offset];
	LOG("%s: (PLL) %08X -> %08X\n", machine().describe_context(), 0xE0440000 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_pll_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (PLL) %08X <- %08X\n", machine().describe_context(), 0xE0440000 + (offset << 2), data);
	COMBINE_DATA(&m_pll_regs[offset]);
}

uint32_t cxhumax_state::cx_pllprescale_r(offs_t offset)
{
	uint32_t data = m_pllprescale_reg;
	LOG("%s: (PLLPRESCALE) %08X -> %08X\n", machine().describe_context(), 0xE0440094 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_pllprescale_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (PLLPRESCALE) %08X <- %08X\n", machine().describe_context(), 0xE0440094 + (offset << 2), data);
	COMBINE_DATA(&m_pllprescale_reg);
}

uint32_t cxhumax_state::cx_clkdiv_r(offs_t offset)
{
	uint32_t data = m_clkdiv_regs[offset];
	LOG("%s: (CLKDIV) %08X -> %08X\n", machine().describe_context(), 0xE0440020 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_clkdiv_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (CLKDIV) %08X <- %08X\n", machine().describe_context(), 0xE0440020 + (offset << 2), data);
	COMBINE_DATA(&m_clkdiv_regs[offset]);
}

uint32_t cxhumax_state::cx_chipcontrol_r(offs_t offset)
{
	uint32_t data = m_chipcontrol_regs[offset];
	LOG("%s: (CHIPCONTROL) %08X -> %08X\n", machine().describe_context(), 0xE0440100 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_chipcontrol_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (CHIPCONTROL) %08X <- %08X\n", machine().describe_context(), 0xE0440100 + (offset << 2), data);
	COMBINE_DATA(&m_chipcontrol_regs[offset]);
}

uint32_t cxhumax_state::cx_intctrl_r(offs_t offset)
{
	uint32_t data = m_intctrl_regs[offset];
	LOG("%s: (INTCTRL) %08X -> %08X\n", machine().describe_context(), 0xE0450000 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_intctrl_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (INTCTRL) %08X <- %08X\n", machine().describe_context(), 0xE0450000 + (offset << 2), data);
	switch (offset >> 3) { // Decode the group
		case 0: // Group 1
			switch(offset & 7) {
				case INTSTATCLR:    // ITC_INTSTATCLR_REG Group 1
					/*
					    Bits 15 (PWM), 14 (PIO103) of Group 1 are the logical OR of their lower level interrupt
					    status bits down within the interrupting module and are not registered.

					    The source registers must be cleared to clear these interrupt bits.
					*/
					data &= ~(INT_PWM_BIT|INT_PIO103_BIT);

					m_intctrl_regs[INTREG(INTGROUP1, INTSTATCLR)] &= ~data;
					m_intctrl_regs[INTREG(INTGROUP1, INTSTATSET)] &= ~data;
					m_intctrl_regs[INTREG(INTGROUP1, INTIRQ)] &= ~data;
					break;
				default:
					COMBINE_DATA(&m_intctrl_regs[offset]);
					break;
			}
			break;
		case 1: // Group 2
			switch(offset & 7) {
				case INTSTATCLR:    // ITC_INTSTATCLR_REG Group 2
					/*
					    The timer interrupt service routine must write to one of the timer
					    registers before clearing the corresponding Interrupt Controller ISR timer
					    interrupt bit.

					    Bit 7 (Timers) of Group 2 is the logical OR of its lower level interrupt
					    status bits down within the interrupting module and are not registered.

					    The source registers must be cleared to clear these interrupt bits.
					*/
					if(m_timer_regs.timer_irq) data &= ~INT_TIMER_BIT;

					m_intctrl_regs[INTREG(INTGROUP2, INTSTATCLR)] &= ~data;
					m_intctrl_regs[INTREG(INTGROUP2, INTSTATSET)] &= ~data;
					m_intctrl_regs[INTREG(INTGROUP2, INTIRQ)] &= ~data;
					break;
				default:
					COMBINE_DATA(&m_intctrl_regs[offset]);
					break;
			}
			break;
		default:
			break;
	}

	if(m_i2c1_regs[I2C_STAT_REG]&I2C_INT_BIT)
	{
		m_intctrl_regs[INTREG(INTGROUP1, INTIRQ)] |= 1<<7;
		m_intctrl_regs[INTREG(INTGROUP1, INTSTATCLR)] |= 1<<7;
		m_intctrl_regs[INTREG(INTGROUP1, INTSTATSET)] |= 1<<7;
	}

	/* check if */
	if((m_intctrl_regs[INTREG(INTGROUP2, INTIRQ)] & m_intctrl_regs[INTREG(INTGROUP2, INTENABLE)])
		|| (m_intctrl_regs[INTREG(INTGROUP1, INTIRQ)] & m_intctrl_regs[INTREG(INTGROUP1, INTENABLE)]))
		m_maincpu->set_input_line(arm7_cpu_device::ARM7_IRQ_LINE, ASSERT_LINE);
	else
		m_maincpu->set_input_line(arm7_cpu_device::ARM7_IRQ_LINE, CLEAR_LINE);

}

uint32_t cxhumax_state::cx_ss_r(offs_t offset)
{
	uint32_t data = 0;
	switch(offset) {
		case SS_FIFC_REG:
			data = m_ss_regs[offset] & 0xFFF0;
			break;
		default:
			data = m_ss_regs[offset];
			break;
	}
	LOG("%s: (SS) %08X -> %08X\n", machine().describe_context(), 0xE0490000 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_ss_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (SS) %08X <- %08X\n", machine().describe_context(), 0xE0490000 + (offset << 2), data);
	switch(offset) {
		case SS_CNTL_REG:
			if (data&1) {
				// "Send" pending data
				uint8_t tfd = (m_ss_regs[SS_STAT_REG]>>4) & 0xF;
				if ((tfd>1) && (m_ss_tx_fifo[0] == 0) && (m_ss_tx_fifo[1] != 0xFF)) {
					// ASCII
					printf("%s\n", &m_ss_tx_fifo[1]);
				} else {
					// UNKNOWN
					for (int i=0; i<tfd; i++) {
						printf("%02X ", m_ss_tx_fifo[i]);
					}
					printf("\n");
				}
				// Clear TX FIFO
				memset(m_ss_tx_fifo,0,sizeof(m_ss_tx_fifo));
				m_ss_regs[SS_STAT_REG] &= 0xFF0F;
			}
			COMBINE_DATA(&m_ss_regs[offset]);
			break;
		case SS_FIFO_REG:
			{
				// Push data into TX FIFO (if it's not full) and adjust transmit FIFO depth
				uint8_t tfd = (m_ss_regs[SS_STAT_REG]>>4) & 0xF;
				if (tfd<8) {
					m_ss_tx_fifo[tfd++] = data;
					m_ss_regs[SS_STAT_REG] = (m_ss_regs[SS_STAT_REG] & 0xFF0F) | (tfd<<4);
				}
			}
			break;
		case SS_STAT_REG:
			// read-only
			break;
		default:
			COMBINE_DATA(&m_ss_regs[offset]);
			break;
	};
}

uint32_t cxhumax_state::cx_i2c0_r(offs_t offset)
{
	uint32_t data = m_i2c0_regs[offset];
	LOG("%s: (I2C0) %08X -> %08X\n", machine().describe_context(), 0xE04E0000 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_i2c0_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (I2C0) %08X <- %08X\n", machine().describe_context(), 0xE04E0000 + (offset << 2), data);
	COMBINE_DATA(&m_i2c0_regs[offset]);
}

uint8_t cxhumax_state::i2cmem_read_byte(int last)
{
	uint8_t data = 0;
	int i;
	m_i2cmem->write_sda(1);
	for (i = 0; i < 8; i++)
	{
		m_i2cmem->write_scl(1);
		data = (data << 1) + (m_i2cmem->read_sda() ? 1 : 0);
		m_i2cmem->write_scl(0);
	}
	m_i2cmem->write_sda(last);
	m_i2cmem->write_scl(1);
	m_i2cmem->write_scl(0);
	return data;
}

void cxhumax_state::i2cmem_write_byte(uint8_t data)
{
	int i;
	for (i = 0; i < 8; i++)
	{
		m_i2cmem->write_sda((data & 0x80) ? 1 : 0);
		data = data << 1;
		m_i2cmem->write_scl(1);
		m_i2cmem->write_scl(0);
	}
	m_i2cmem->write_sda(1); // ack bit
	m_i2cmem->write_scl(1);
	m_i2cmem->write_scl(0);
}

void cxhumax_state::i2cmem_start()
{
	m_i2cmem->write_sda(1);
	m_i2cmem->write_scl(1);
	m_i2cmem->write_sda(0);
	m_i2cmem->write_scl(0);
}

void cxhumax_state::i2cmem_stop()
{
	m_i2cmem->write_sda(0);
	m_i2cmem->write_scl(1);
	m_i2cmem->write_sda(1);
	m_i2cmem->write_scl(0);
}

uint32_t cxhumax_state::cx_i2c1_r(offs_t offset)
{
	uint32_t data=0;
	switch(offset) {
		case I2C_STAT_REG:
			data |= m_i2cmem->read_sda()<<3;
			[[fallthrough]];
		default:
			data |= m_i2c1_regs[offset]; break;
	}
	LOG("%s: (I2C1) %08X -> %08X\n", machine().describe_context(), 0xE04E1000 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_i2c1_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (I2C1) %08X <- %08X\n", machine().describe_context(), 0xE04E1000 + (offset << 2), data);
	switch(offset) {
		case I2C_CTRL_REG:
			if(data&0x10) {// START
				i2cmem_start();
			}
			if((data&0x4) || ((data&3)==3)) // I2C READ
			{
				m_i2c1_regs[I2C_RDATA_REG] = 0;
				if(data&0x10) i2cmem_write_byte((data>>24)&0xFF);
				if(m_i2c1_regs[I2C_MODE_REG]&(1<<5)) // BYTE_ORDER
				{
					for(int i=0; i<(data&3); i++) {
						m_i2c1_regs[I2C_RDATA_REG] |= i2cmem_read_byte(0) << (i*8);
					}
					m_i2c1_regs[I2C_RDATA_REG] |= i2cmem_read_byte((data&0x20)?1:0) << ((data&3)*8);
				}
				else
				{
					for(int i=0; i<(data&3); i++) {
						m_i2c1_regs[I2C_RDATA_REG] |= i2cmem_read_byte(0);
						m_i2c1_regs[I2C_RDATA_REG] <<= 8;
					}
					m_i2c1_regs[I2C_RDATA_REG] |= i2cmem_read_byte((data&0x20)?1:0);
				}
			}
			else
			{
				for(int i=0; i<=(data&3); i++) {
					i2cmem_write_byte((data>>(24-(i*8))&0xFF));
				}
			}
			if(data&0x20) {// STOP
				i2cmem_stop();
			}

			/* The interrupt status bit is set at the end of an I2C read or write operation. */
			m_i2c1_regs[I2C_STAT_REG] |= I2C_INT_BIT;
			m_i2c1_regs[I2C_STAT_REG] |= I2C_WACK_BIT;

			m_intctrl_regs[INTREG(INTGROUP1, INTIRQ)] |= 1<<7;
			m_intctrl_regs[INTREG(INTGROUP1, INTSTATCLR)] |= 1<<7;
			m_intctrl_regs[INTREG(INTGROUP1, INTSTATSET)] |= 1<<7;
			if (m_intctrl_regs[INTREG(INTGROUP1, INTENABLE)] & (1<<7)) {
					LOG("%s: (I2C1) Int\n",  machine().describe_context());
					m_maincpu->set_input_line(arm7_cpu_device::ARM7_IRQ_LINE, ASSERT_LINE);
			}
			break;
		case I2C_STAT_REG:
			/* The interrupt status bit may be cleared by writing (anything) to the status register, which also clears the acknowledge status. */
			data&=~(I2C_WACK_BIT|I2C_INT_BIT);
			[[fallthrough]];
		default:
			COMBINE_DATA(&m_i2c1_regs[offset]);
	}
}

uint32_t cxhumax_state::cx_i2c2_r(offs_t offset)
{
	uint32_t data = m_i2c2_regs[offset];
	LOG("%s: (I2C2) %08X -> %08X\n", machine().describe_context(), 0xE04E2000 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_i2c2_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (I2C2) %08X <- %08X\n", machine().describe_context(), 0xE04E2000 + (offset << 2), data);
	COMBINE_DATA(&m_i2c2_regs[offset]);
}

uint32_t cxhumax_state::cx_mc_cfg_r(offs_t offset)
{
	uint32_t data = m_mccfg_regs[offset];
	LOG("%s: (MC_CFG) %08X -> %08X\n", machine().describe_context(), 0xE0500300 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_mc_cfg_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (MC_CFG) %08X <- %08X\n", machine().describe_context(), 0xE0500300 + (offset << 2), data);
	COMBINE_DATA(&m_mccfg_regs[offset]);
}

uint32_t cxhumax_state::cx_drm0_r(offs_t offset)
{
	uint32_t data = m_drm0_regs[offset];
	LOG("%s: (DRM0) %08X -> %08X\n", machine().describe_context(), 0xE0560000 + (offset << 2), data);
	switch(offset) {
		case 0x14/4: // DRM_STATUS_REG
			data |= 1<<21;
			data |= 1<<20;
	}
	return data;
}

void cxhumax_state::cx_drm0_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (DRM0) %08X <- %08X\n", machine().describe_context(), 0xE0560000 + (offset << 2), data);
	COMBINE_DATA(&m_drm0_regs[offset]);
}

uint32_t cxhumax_state::cx_drm1_r(offs_t offset)
{
	uint32_t data = m_drm1_regs[offset];
	LOG("%s: (DRM1) %08X -> %08X\n", machine().describe_context(), 0xE0570000 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_drm1_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (DRM1) %08X <- %08X\n", machine().describe_context(), 0xE0570000 + (offset << 2), data);
	COMBINE_DATA(&m_drm1_regs[offset]);
}

uint32_t cxhumax_state::cx_hdmi_r(offs_t offset)
{
	uint32_t data = m_hdmi_regs[offset];
	LOG("%s: (HDMI) %08X -> %08X\n", machine().describe_context(), 0xE05D0800 + (offset << 2), data);
	return data;
}

void cxhumax_state::cx_hdmi_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("%s: (HDMI) %08X <- %08X\n", machine().describe_context(), 0xE05D0800 + (offset << 2), data);
	switch(offset) {
		case 0x40/4: // HDMI_CONFIG_REG
			if(data&8) m_hdmi_regs[0xc0/4] |= 0x80;
	}
	COMBINE_DATA(&m_hdmi_regs[offset]);
}

void cxhumax_state::video_start()
{
}

/* copy from emu/rendersw.inc */
/*------------------------------------------------------------------------
    ycc_to_rgb - convert YCC to RGB; the YCC pixel
    contains Y in the LSB, Cb << 8, and Cr << 16
    This actually a YCbCr conversion,
    details my be found in chapter 6.4 ff of
    http://softwarecommunity.intel.com/isn/downloads/softwareproducts/pdfs/346495.pdf
    The document also contains the constants below as floats.
--------------------------------------------------------------------------*/

static inline uint8_t clamp16_shift8(uint32_t x)
{
	return (((int32_t) x < 0) ? 0 : (x > 65535 ? 255: x >> 8));
}

static inline uint32_t ycc_to_rgb(uint32_t ycc)
{
	/* original equations:

	    C = Y - 16
	    D = Cb - 128
	    E = Cr - 128

	    R = clip(( 298 * C           + 409 * E + 128) >> 8)
	    G = clip(( 298 * C - 100 * D - 208 * E + 128) >> 8)
	    B = clip(( 298 * C + 516 * D           + 128) >> 8)

	    R = clip(( 298 * (Y - 16)                    + 409 * (Cr - 128) + 128) >> 8)
	    G = clip(( 298 * (Y - 16) - 100 * (Cb - 128) - 208 * (Cr - 128) + 128) >> 8)
	    B = clip(( 298 * (Y - 16) + 516 * (Cb - 128)                    + 128) >> 8)

	    R = clip(( 298 * Y - 298 * 16                        + 409 * Cr - 409 * 128 + 128) >> 8)
	    G = clip(( 298 * Y - 298 * 16 - 100 * Cb + 100 * 128 - 208 * Cr + 208 * 128 + 128) >> 8)
	    B = clip(( 298 * Y - 298 * 16 + 516 * Cb - 516 * 128                        + 128) >> 8)

	    R = clip(( 298 * Y - 298 * 16                        + 409 * Cr - 409 * 128 + 128) >> 8)
	    G = clip(( 298 * Y - 298 * 16 - 100 * Cb + 100 * 128 - 208 * Cr + 208 * 128 + 128) >> 8)
	    B = clip(( 298 * Y - 298 * 16 + 516 * Cb - 516 * 128                        + 128) >> 8)

	    Now combine constants:

	    R = clip(( 298 * Y            + 409 * Cr - 56992) >> 8)
	    G = clip(( 298 * Y - 100 * Cb - 208 * Cr + 34784) >> 8)
	    B = clip(( 298 * Y + 516 * Cb            - 70688) >> 8)

	    Define common = 298 * y - 56992. This will save one addition

	    R = clip(( common            + 409 * Cr -     0) >> 8)
	    G = clip(( common - 100 * Cb - 208 * Cr + 91776) >> 8)
	    B = clip(( common + 516 * Cb            - 13696) >> 8)

	*/
	uint8_t y = ycc;
	uint8_t cb = ycc >> 8;
	uint8_t cr = ycc >> 16;
	uint32_t r, g, b, common;

	common = 298 * y - 56992;
	r = (common +            409 * cr);
	g = (common - 100 * cb - 208 * cr + 91776);
	b = (common + 516 * cb - 13696);

	/* Now clamp and shift back */
	return rgb_t(clamp16_shift8(r), clamp16_shift8(g), clamp16_shift8(b));
}

uint32_t cxhumax_state::screen_update_cxhumax(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	uint32_t osd_pointer = m_drm1_regs[DRM_OSD_PTR_REG];

	if(osd_pointer)
	{
		uint32_t const *ram = m_ram;
		uint32_t const *osd_header = &ram[osd_pointer/4];
		uint8_t  const *vbuf = (uint8_t*)(&ram[osd_header[3]/4]);
		uint32_t const *palette = &ram[osd_header[7]/4];

		uint32_t x_disp_start_and_width = osd_header[1];
		uint32_t xdisp_width = (x_disp_start_and_width >> 16) & 0x1fff;
		uint32_t xdisp_start = x_disp_start_and_width & 0xfff;

		uint32_t image_height_and_width = osd_header[2];
		uint32_t yimg_height = (image_height_and_width >> 16) & 0x7ff;
		uint32_t ximg_width = image_height_and_width & 0x7ff;

		uint32_t y_position_and_region_alpha = osd_header[5];
		uint32_t ydisp_last = (y_position_and_region_alpha >> 12) & 0x7ff;
		uint32_t ydisp_start = y_position_and_region_alpha & 0x7ff;

	/*  uint32_t first_x = m_drm0_regs[DRM_ACTIVE_X_REG] & 0xffff;
	    uint32_t last_x = (m_drm0_regs[DRM_ACTIVE_X_REG] >> 16) & 0xffff;

	    uint32_t first_y = m_drm0_regs[DRM_ACTIVE_Y_REG] & 0xfff;
	    uint32_t last_y = (m_drm0_regs[DRM_ACTIVE_Y_REG] >> 16) & 0xfff;*/

		for (int j=ydisp_start; j <= ydisp_last; j++)
		{
			uint32_t *const bmp = &bitmap.pix(j);

			for (int i=xdisp_start; i <= (xdisp_start + xdisp_width); i++)
			{
				if ((i <= (xdisp_start + ximg_width)) && (j <= (ydisp_start + yimg_height))) {
					bmp[i] = palette[vbuf[i+((j-ydisp_start)*ximg_width)]]; // FIXME: need BYTE4_XOR_?E for endianness
				} else {
					bmp[i] = ycc_to_rgb(m_drm1_regs[DRM_BCKGND_REG]);
				}
			}
		}
	}
	return 0;
}

void cxhumax_state::cxhumax_map(address_map &map)
{
	map(0x00000000, 0x03ffffff).ram().share("ram").mirror(0x40000000);           // 64?MB RAM
	map(0xe0000000, 0xe000ffff).rw(FUNC(cxhumax_state::cx_hsx_r), FUNC(cxhumax_state::cx_hsx_w));                       // HSX
	map(0xe0010000, 0xe0010003).rw(FUNC(cxhumax_state::cx_romdescr_r), FUNC(cxhumax_state::cx_romdescr_w));             // ROM Descriptor
	map(0xe0010004, 0xe001000f).rw(FUNC(cxhumax_state::cx_isaromdescr_r), FUNC(cxhumax_state::cx_isaromdescr_w));       // ISA/ROM Descriptors
	map(0xe0010010, 0xe001001f).rw(FUNC(cxhumax_state::cx_isadescr_r), FUNC(cxhumax_state::cx_isadescr_w));             // ISA Descriptors
	map(0xe0010020, 0xe001002f).rw(FUNC(cxhumax_state::cx_rommap_r), FUNC(cxhumax_state::cx_rommap_w));                 // ROM Mapping
	map(0xe0010030, 0xe0010033).rw(FUNC(cxhumax_state::cx_rommode_r), FUNC(cxhumax_state::cx_rommode_w));               // ISA Mode
	map(0xe0010034, 0xe0010037).rw(FUNC(cxhumax_state::cx_xoemask_r), FUNC(cxhumax_state::cx_xoemask_w));               // XOE Mask
	map(0xe0010040, 0xe0010047).rw(FUNC(cxhumax_state::cx_pci_r), FUNC(cxhumax_state::cx_pci_w));                       // PCI
	map(0xe0010080, 0xe00100ff).rw(FUNC(cxhumax_state::cx_extdesc_r), FUNC(cxhumax_state::cx_extdesc_w));               // Extended Control
	map(0xe0400014, 0xe0400017).w(FUNC(cxhumax_state::cx_remap_w));                                   // RST_REMAP_REG
	map(0xe0400024, 0xe0400027).rw(FUNC(cxhumax_state::cx_scratch_r), FUNC(cxhumax_state::cx_scratch_w));               // RST_SCRATCH_REG - System Scratch Register
	map(0xe0430000, 0xe0430103).rw(FUNC(cxhumax_state::cx_timers_r), FUNC(cxhumax_state::cx_timers_w));                 // Timers
	map(0xe0411000, 0xe0411033).rw(FUNC(cxhumax_state::cx_uart2_r), FUNC(cxhumax_state::cx_uart2_w));                   // UART2
	map(0xe0440000, 0xe0440013).rw(FUNC(cxhumax_state::cx_pll_r), FUNC(cxhumax_state::cx_pll_w));                       // PLL Registers
	map(0xe0440020, 0xe0440037).rw(FUNC(cxhumax_state::cx_clkdiv_r), FUNC(cxhumax_state::cx_clkdiv_w));                 // Clock Divider Registers
	map(0xe0440094, 0xe0440097).rw(FUNC(cxhumax_state::cx_pllprescale_r), FUNC(cxhumax_state::cx_pllprescale_w));       // PLL Prescale
	map(0xe0440100, 0xe0440173).rw(FUNC(cxhumax_state::cx_chipcontrol_r), FUNC(cxhumax_state::cx_chipcontrol_w));       // Chip Control Registers
	map(0xe0450000, 0xe0450037).rw(FUNC(cxhumax_state::cx_intctrl_r), FUNC(cxhumax_state::cx_intctrl_w));               // Interrupt Controller Registers
	map(0xe0490000, 0xe0490017).rw(FUNC(cxhumax_state::cx_ss_r), FUNC(cxhumax_state::cx_ss_w));                         // Synchronous Serial Port
	map(0xe04e0000, 0xe04e001f).rw(FUNC(cxhumax_state::cx_i2c0_r), FUNC(cxhumax_state::cx_i2c0_w));                     // I2C0
	map(0xe04e1000, 0xe04e101f).rw(FUNC(cxhumax_state::cx_i2c1_r), FUNC(cxhumax_state::cx_i2c1_w));                     // I2C1
	map(0xe04e2000, 0xe04e201f).rw(FUNC(cxhumax_state::cx_i2c2_r), FUNC(cxhumax_state::cx_i2c2_w));                     // I2C2
	map(0xe0500300, 0xe050030b).rw(FUNC(cxhumax_state::cx_mc_cfg_r), FUNC(cxhumax_state::cx_mc_cfg_w));                 // Memory Controller configuration
	map(0xe0560000, 0xe05600fb).rw(FUNC(cxhumax_state::cx_drm0_r), FUNC(cxhumax_state::cx_drm0_w));                     // DRM0
	map(0xe0570000, 0xe05700fb).rw(FUNC(cxhumax_state::cx_drm1_r), FUNC(cxhumax_state::cx_drm1_w));                     // DRM1
	map(0xe05d0800, 0xe05d0bff).rw(FUNC(cxhumax_state::cx_hdmi_r), FUNC(cxhumax_state::cx_hdmi_w));                     // HDMI
	map(0xe0600000, 0xe063ffff).rw(FUNC(cxhumax_state::cx_gxa_r), FUNC(cxhumax_state::cx_gxa_w));                       // GXA
	map(0xe4017000, 0xe40173ff).ram();                                                 // HSX - BSP - 1K Video Shared Dual Port RAM (shared with MVP)
	map(0xe4080000, 0xe4083fff).ram();                                                 // HSX - TSP 0 - 16K Private Instructions/Data and Host-Shared Data
	map(0xf0000000, 0xf03fffff).rw(FUNC(cxhumax_state::flash_r), FUNC(cxhumax_state::flash_w)).mirror(0x08000000);   // 4MB FLASH (INTEL 28F320J3D)
	map(0xf4000000, 0xf43fffff).r(FUNC(cxhumax_state::dummy_flash_r));                                 // do we need it?
}

static INPUT_PORTS_START( cxhumax )
INPUT_PORTS_END

void cxhumax_state::machine_start()
{
	int index = 0;
	for(index = 0; index < MAX_CX_TIMERS; index++)
	{
		m_timer_regs.timer[index].timer = timer_alloc(FUNC(cxhumax_state::timer_tick), this);
		m_timer_regs.timer[index].timer->adjust(attotime::never, index);
	}
}

void cxhumax_state::machine_reset()
{
	m_i2c0_regs[0x08/4] = 0x08; // SDA high
	m_i2c2_regs[0x08/4] = 0x08; // SDA high

	uint8_t* FLASH = memregion("flash")->base();
	memcpy(m_ram,FLASH,0x400000);

	m_chipcontrol_regs[PIN_CONFIG_0_REG] =
		1 << 0  | /* Short Reset: 0=200ms delay ; 1=1ms delay */
		1 << 1  | /* Software config bit. OK */
		1 << 4  | /* SDRAM memory controller data width. 0=16bit 1=32bit */
		1 << 11 | /* I/O Addr bus width 11=23 bit 10=22bit 01=21bit 00=20bit / PCImode: 0=held in reset 1=normal reset OK? */
		0 << 16 | /* 0=PCI mode 1=Standard I/O mode */
		1 << 23 | /* 0=PCI device 1=PCI host bridge OK */
		1 << 26 | /* 0=SC1 used for NDS 1=SC1 not used for NDS */
		1 << 27 | /* 0=8bit ROM 1=16bit ROM */
		1 << 28 | /* 0=SC0 used for NDS 1=SC0 not used for NDS */
		0 << 29 | /* 0=using SC2 1=not using SC2 */
		0 << 30 | /* 0=using SC1 (TDA8004) 1=not using SC1 */
		1 << 31;  /* 0=Ext clk for boot 1=Int PLL for boot OK */
	m_chipcontrol_regs[SREG_MODE_REG] = 0x0000020F;

	memset(m_isaromdescr_regs,0,sizeof(m_isaromdescr_regs));
	memset(m_isadescr_regs,0,sizeof(m_isadescr_regs));
	m_rommode_reg=0;
	m_xoemask_reg=0;
	memset(m_extdesc_regs,0,sizeof(m_extdesc_regs));
	memset(m_drm1_regs,0,sizeof(m_drm1_regs));

	m_pll_regs[SREG_MPG_0_INTFRAC_REG] = (0x1A << 25) /* integer */ | 0x5D1764 /* fraction */;
	m_pll_regs[SREG_MPG_1_INTFRAC_REG] = (0x1A << 25) /* integer */ | 0x5D1764 /* fraction */;
	m_pll_regs[SREG_ARM_INTFRAC_REG] = (0x28 << 25) /* integer */ | 0xCEDE62 /* fraction */;
	m_pll_regs[SREG_MEM_INTFRAC_REG] = (0x13 << 25) /* integer */ | 0xC9B26D /* fraction */;
	m_pll_regs[SREG_USB_INTFRAC_REG] = (0x08 << 25) /* integer */ | 0x52BF5B /* fraction */;

	m_clkdiv_regs[SREG_DIV_0_REG] = (2<<0)|(1<<6)|(2<<8)|(2<<14)|(10<<16)|(1<<22)|(10<<24)|(1<<30);
	m_clkdiv_regs[SREG_DIV_1_REG] = (5<<0)|(0<<6)|(12<<8)|(0<<14)|(4<<16)|(1<<22)|(5<<24)|(1<<30);
	m_clkdiv_regs[SREG_DIV_2_REG] = (22<<0)|(0<<6)|(12<<8)|(1<<14)|(4<<16)|(3<<22); //|(5<<24)|(1<<30);???
	m_clkdiv_regs[SREG_DIV_3_REG] = (5<<0)|(0<<6)|(5<<8)|(0<<14)|(5<<16)|(0<<22)|(5<<24)|(0<<30);
	m_clkdiv_regs[SREG_DIV_4_REG] = (8<<0)|(0<<6)|(5<<8)|(0<<14)|(5<<16)|(0<<22)|(5<<24)|(0<<30);
	m_clkdiv_regs[SREG_DIV_5_REG] = (8<<0)|(0<<6)|(5<<8)|(0<<14)|(5<<16)|(0<<22);

	m_pllprescale_reg=0xFFF;

	m_mccfg_regs[MC_CFG0] = ((m_chipcontrol_regs[PIN_CONFIG_0_REG]>>4)&1)<<16;
	m_mccfg_regs[MC_CFG1] = 0;
	m_mccfg_regs[MC_CFG2] = (7<<8)|(7<<0);

	// UART2
	m_uart2_regs[UART_FIFC_REG] = 0x30;

	// Clear SS TX FIFO
	memset(m_ss_tx_fifo,0,sizeof(m_ss_tx_fifo));
	m_ss_regs[SS_BAUD_REG] = 1; // Default SS clock = 13,5MHz

	memset(m_intctrl_regs,0,sizeof(m_intctrl_regs));

	memset(m_hdmi_regs,0,sizeof(m_hdmi_regs));

	memset(m_gxa_cmd_regs,0,sizeof(m_gxa_cmd_regs));
}

void cxhumax_state::cxhumax(machine_config &config)
{
	ARM920T(config, m_maincpu, 180000000); // CX24175 (RevC up?)
	m_maincpu->set_addrmap(AS_PROGRAM, &cxhumax_state::cxhumax_map);


	INTEL_28F320J3D(config, "flash");
	I2C_24C64(config, "eeprom", 0); // 24LC64

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(1920, 1080);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(cxhumax_state::screen_update_cxhumax));

	PALETTE(config, "palette", palette_device::MONOCHROME);

	GENERIC_TERMINAL(config, m_terminal, 0);
}

ROM_START( hxhdci2k )
	ROM_REGION( 0x400000, "flash", 0 )
	ROM_SYSTEM_BIOS( 0, "fw10005", "HDCI REV 1.0 RHDXSCI 1.00.05" ) /* 19 AUG 2008 */
	ROM_LOAD16_WORD_SWAP( "28f320j3d.bin", 0x000000, 0x400000, BAD_DUMP CRC(63d98942) SHA1(c5b8d701677a3edc25f203854f44953b19c9158d) )

	ROM_REGION( 0x2000, "eeprom", 0 )
	ROM_LOAD( "24lc64.bin", 0x0000, 0x2000, NO_DUMP)
ROM_END

//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY  FULLNAME           FLAGS
SYST( 2008, hxhdci2k, 0,      0,      cxhumax, cxhumax, cxhumax_state, empty_init, "HUMAX", "HUMAX HDCI-2000", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



cybiko.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Tim Schuerewegen
/*************************************************************************************

    Cybiko Wireless Inter-tainment System

    (c) 2001-2007 Tim Schuerewegen

    Cybiko Classic (V1)
    Cybiko Classic (V2)
    Cybiko Xtreme

ToDo:
- Remove the memory leak in the nvram handler.
- Provide facility to load games via a "cart".
- Need instructions! The black screen doesn't fill me with confidence..unable to test.

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


#include "emu.h"
#include "cybiko.h"

#include "screen.h"
#include "speaker.h"

//  +------------------------------------------------------+
//  | Cybiko Classic (CY6411)                         | V2 |
//  +------------------------------------------------------+
//  | - CYBIKO | CY-OS 1.1.7 | 6432241M04FA | 0028R JAPAN  |
//  | - SST 39VF020 | 90-4C-WH | 0012175-D                 |
//  | - ATMEL 0027 | AT90S2313-4SC                         |
//  | - ATMEL | AT45DB041A | TC | 0027                     |
//  | - RF2915 | RFMD0028 | 0F540BT                        |
//  | - EliteMT | LP62S2048X-70LLT | 0026B H4A27HA         |
//  | - MP02AB | LMX2315 | TMD                             |
//  +------------------------------------------------------+

//  +------------------------------------------------------+
//  | Cybiko Xtreme (CY44802)                              |
//  +------------------------------------------------------+
//  | - CYBIKO | CYBOOT 1.5A | HD6432323G03F | 0131 JAPAN  |
//  | - SST 39VF400A | 70-4C-EK                            |
//  | - ATMEL 0033 | AT90S2313-4SC                         |
//  | - SAMSUNG 129 | K4F171612D-TL60                      |
//  | - 2E16AB | USBN9604-28M | NSC00A1                    |
//  +------------------------------------------------------+

//  +------------------------------------------------------+
//  | Cybiko MP3 Player (CY65P10)                          |
//  +------------------------------------------------------+
//  | - H8S/2246 | 0G1 | HD6472246FA20 | JAPAN             |
//  | - MICRONAS | DAC3550A C2 | 0394 22 HM U | 089472.000 |
//  | - 2E08AJ | USBN9603-28M | NSC99A1                    |
//  +------------------------------------------------------+

///////////////////////////
// ADDRESS MAP - PROGRAM //
///////////////////////////

// 512 kbyte ram + no memory mapped flash
void cybiko_state::cybikov1_mem(address_map &map)
{
	map(0x000000, 0x007fff).rom();
	map(0x600000, 0x600001).rw(FUNC(cybiko_state::cybiko_lcd_r), FUNC(cybiko_state::cybiko_lcd_w));
//  map(0xe00000, 0xe07fff).r(FUNC(cybiko_state::cybikov1_key_r));
}

/*

v1 shutdown:
242628: sleep of powerdown
-> 2425d2
242ad2 bsr 2425d2
check 242ac4
counter at 235fa0
242a2e?
24297a -> 294e32 -> thread start?
ptr: 243150=24297a
21cf3e writes the pointer

*/


//  +-------------------------------------+
//  | Cybiko Classic (V2) - Memory Map    |
//  +-------------------------------------+
//  | 000000 - 007FFF | rom               |
//  | 008000 - 00FFFF | 17 51 17 51 ..    |
//  | 010000 - 0FFFFF | flash mirror      |
//  | 100000 - 13FFFF | flash             |
//  | 140000 - 1FFFFF | flash mirror      |
//  | 200000 - 23FFFF | ram               |
//  | 240000 - 3FFFFF | ram mirror        |
//  | 400000 - 5FFFFF | FF FF FF FF ..    |
//  | 600000 - 600001 | lcd               |
//  | 600002 - DFFFFF | FF FF FF FF ..    |
//  | E00000 - FFDBFF | keyboard          |
//  | FFDC00 - FFFFFF | onchip ram & regs |
//  +-------------------------------------+

// 256 kbyte ram + 256 kbyte memory mapped flash
void cybiko_state::cybikov2_mem(address_map &map)
{
	map(0x000000, 0x007fff).rom();
	map(0x100000, 0x13ffff).r("flash2", FUNC(sst_39vf020_device::read)).mirror(0x0c0000);
	map(0x600000, 0x600001).rw(FUNC(cybiko_state::cybiko_lcd_r), FUNC(cybiko_state::cybiko_lcd_w)).mirror(0x1ffffe);
	map(0xe00000, 0xffdbff).r(FUNC(cybiko_state::cybikov2_key_r));
}

// 2048 kbyte ram + 512 kbyte memory mapped flash
void cybiko_state::cybikoxt_mem(address_map &map)
{
	map(0x000000, 0x007fff).rom().mirror(0x038000);
	map(0x100000, 0x100001).rw(FUNC(cybiko_state::cybiko_lcd_r), FUNC(cybiko_state::cybiko_lcd_w));
	map(0x200000, 0x200003).w(FUNC(cybiko_state::cybiko_usb_w));
	map(0x600000, 0x67ffff).r("flashxt", FUNC(sst_39vf400a_device::read)).mirror(0x180000);
	map(0xe00000, 0xefffff).r(FUNC(cybiko_state::cybikoxt_key_r));
}

void cybiko_state::serflash_w(uint8_t data)
{
	m_flash1->cs_w((data & 0x10) ? 0 : 1);
}

uint8_t cybiko_state::clock_r()
{
	if (m_rtc->sda_r())
	{
		return (0x01|0x04);
	}

	return 0x04;
}

void cybiko_state::clock_w(uint8_t data)
{
	m_rtc->scl_w((data & 0x02) ? 1 : 0);
	m_rtc->sda_w((data & 0x01) ? 0 : 1);
}

uint8_t cybiko_state::xtclock_r()
{
	if (m_rtc->sda_r())
	{
		return 0x40;
	}

	return 0;
}

void cybiko_state::xtclock_w(uint8_t data)
{
	m_rtc->scl_w((data & 0x02) ? 1 : 0);
	m_rtc->sda_w((data & 0x40) ? 0 : 1);
}

uint8_t cybiko_state::xtpower_r()
{
	// bit 7 = on/off button
	// bit 6 = battery charged if "1"
	return 0xc0;
}

uint16_t cybiko_state::adc1_r()
{
	return 0x01;
}

uint16_t cybiko_state::adc2_r()
{
	return 0x00;
}

uint8_t cybiko_state::port0_r()
{
	// bit 3 = on/off button
	return 0x08;
}



/////////////////
// INPUT PORTS //
/////////////////


static INPUT_PORTS_START( cybiko )
	PORT_START("A.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Del") PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CHAR('`')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("A.1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("As") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Fn") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)

	PORT_START("A.2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Help") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END))

	PORT_START("A.3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')

	PORT_START("A.4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Select") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')

	PORT_START("A.5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR( 13 )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')

	PORT_START("A.6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("BkSp") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')

	PORT_START("A.7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')

	PORT_START("A.8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')

INPUT_PORTS_END

static INPUT_PORTS_START( cybikoxt )
	PORT_START("A.0")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')

	PORT_START("A.1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')

	PORT_START("A.2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')

	PORT_START("A.3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')

	PORT_START("A.4")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR( 13 )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Select") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("A.5")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Del") PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("As") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))

	PORT_START("A.6")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))

	PORT_START("A.7")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Fn") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)

	PORT_START("A.8")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("A.13")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Help") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')

	PORT_START("A.14")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("On/Off") PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))

INPUT_PORTS_END

////////////////////
// MACHINE DRIVER //
////////////////////

static DEVICE_INPUT_DEFAULTS_START( debug_serial ) // set up debug port to default to 57600
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_57600 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_57600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void cybiko_state::cybikov1_debug_serial(machine_config &config)
{
	m_debug_serial->rxd_handler().set(m_maincpu, FUNC(h8_device::sci_rx_w<2>));
	m_maincpu->write_sci_tx<2>().set(m_debug_serial, FUNC(rs232_port_device::write_txd));
}

void cybiko_state::cybikov1_base(machine_config &config)
{
	// screen
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_size(hd66421_device::WIDTH, hd66421_device::HEIGHT);
	screen.set_visarea(0, hd66421_device::WIDTH - 1, 0, hd66421_device::HEIGHT - 1);
	screen.set_screen_update("hd66421", FUNC(hd66421_device::update_screen));
	screen.set_palette("hd66421:palette");

	// video
	HD66421(config, m_crtc);

	// sound
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);

	// machine
	PCF8593(config, m_rtc);

	NVRAM(config, m_nvram, nvram_device::DEFAULT_ALL_0);

	// internal ram
	RAM(config, m_ram).set_default_size("512K").set_extra_options("1M");

	// serial debug port
	RS232_PORT(config, m_debug_serial, default_rs232_devices, nullptr);
	m_debug_serial->set_option_device_input_defaults("null_modem", DEVICE_INPUT_DEFAULTS_NAME(debug_serial));
	m_debug_serial->set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(debug_serial));
	m_debug_serial->set_option_device_input_defaults("pty", DEVICE_INPUT_DEFAULTS_NAME(debug_serial));

	// quickload
	QUICKLOAD(config, "quickload", "bin,nv").set_load_callback(FUNC(cybiko_state::quickload_cybiko));
}

void cybiko_state::cybikov1_flash(machine_config &config)
{
	AT45DB041(config, m_flash1, 0);
	m_flash1->so_callback().set(m_maincpu, FUNC(h8_device::sci_rx_w<1>));
}

void cybiko_state::cybikov1(machine_config &config)
{
	cybikov1_base(config);

	// cpu
	auto &maincpu(H8S2241(config, m_maincpu, XTAL(11'059'200)));
	maincpu.set_addrmap(AS_PROGRAM, &cybiko_state::cybikov1_mem);
	maincpu.read_adc<1>().set(FUNC(cybiko_state::adc1_r));
	maincpu.read_adc<2>().set(FUNC(cybiko_state::adc2_r));
	maincpu.write_port3().set(FUNC(cybiko_state::serflash_w));
	maincpu.read_portf().set(FUNC(cybiko_state::clock_r));
	maincpu.write_portf().set(FUNC(cybiko_state::clock_w));

	m_maincpu->write_sci_tx<1>().set("flash1", FUNC(at45db041_device::si_w));
	m_maincpu->write_sci_clk<1>().set("flash1", FUNC(at45db041_device::sck_w));

	// machine
	cybikov1_flash(config);
	cybikov1_debug_serial(config);
}

void cybiko_state::cybikov2(machine_config &config)
{
	cybikov1_base(config);
	cybikov1_flash(config);

	// cpu
	auto &maincpu(H8S2246(config, m_maincpu, XTAL(11'059'200)));
	maincpu.set_addrmap(AS_PROGRAM, &cybiko_state::cybikov2_mem);
	maincpu.read_adc<1>().set(FUNC(cybiko_state::adc1_r));
	maincpu.read_adc<2>().set(FUNC(cybiko_state::adc2_r));
	maincpu.read_port1().set(FUNC(cybiko_state::port0_r));
	maincpu.write_port3().set(FUNC(cybiko_state::serflash_w));
	maincpu.read_portf().set(FUNC(cybiko_state::clock_r));
	maincpu.write_portf().set(FUNC(cybiko_state::clock_w));

	m_maincpu->write_sci_tx<1>().set("flash1", FUNC(at45db041_device::si_w));
	m_maincpu->write_sci_clk<1>().set("flash1", FUNC(at45db041_device::sck_w));

	// machine
	SST_39VF020(config, "flash2");
	cybikov1_debug_serial(config);

	// internal ram
	m_ram->set_default_size("256K").set_extra_options("512K,1M");

	// serial debug port
	m_debug_serial->rxd_handler().set(m_maincpu, FUNC(h8_device::sci_rx_w<2>));
	m_maincpu->write_sci_tx<2>().set(m_debug_serial, FUNC(rs232_port_device::write_txd));
}

void cybiko_state::cybikoxt(machine_config &config)
{
	cybikov1_base(config);

	// cpu
	auto &maincpu(H8S2323(config, m_maincpu, XTAL(18'432'000)));
	maincpu.set_addrmap(AS_PROGRAM, &cybiko_state::cybikoxt_mem);
	maincpu.read_porta().set(FUNC(cybiko_state::xtpower_r));
	maincpu.read_portf().set(FUNC(cybiko_state::xtclock_r));
	maincpu.write_portf().set(FUNC(cybiko_state::xtclock_w));

	// machine
	SST_39VF400A(config, "flashxt");

	// internal ram
	m_ram->set_default_size("2M");

	// serial debug port
	m_debug_serial->rxd_handler().set(m_maincpu, FUNC(h8_device::sci_rx_w<2>));
	m_maincpu->write_sci_tx<2>().set(m_debug_serial, FUNC(rs232_port_device::write_txd));

	// quickload
	QUICKLOAD(config.replace(), "quickload", "bin,nv").set_load_callback(FUNC(cybiko_state::quickload_cybikoxt));
}

/////////
// ROM //
/////////

ROM_START( cybikov1 )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD( "cyrom112.bin", 0, 0x8000, CRC(9e1f1a0f) SHA1(6fc08de6b2c67d884ec78f748e4a4bad27ee8045) )

	ROM_REGION( 0x84000, "flash1", 0 )
	ROM_LOAD( "flash_v1246.bin", 0, 0x84000, CRC(3816d0ab) SHA1(19be4fed8d95112568adf93219afe9406d7baecf) )
ROM_END

ROM_START( cybikov2 )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD( "cyrom117.bin", 0, 0x8000, CRC(268da7bf) SHA1(135eaf9e3905e69582aabd9b06bc4de0a66780d5) )

	ROM_SYSTEM_BIOS( 0, "v1358", "v1.3.58" )
	ROM_SYSTEM_BIOS( 1, "v1357", "v1.3.57" )
	ROM_SYSTEM_BIOS( 2, "v1355", "v1.3.55" )

	ROM_REGION( 0x84000, "flash1", 0 )
	ROMX_LOAD( "flash_v1358.bin", 0, 0x84000, CRC(e485880f) SHA1(e414d6d2f876c7c811946bcdfcb6212999412381), ROM_BIOS(0) )
	ROMX_LOAD( "flash_v1357.bin", 0, 0x84000, CRC(9fd3c058) SHA1(dad0c3db0f11c91747db6ccc1900004432afb881), ROM_BIOS(1) )
	ROMX_LOAD( "flash_v1355.bin", 0, 0x84000, CRC(497a5bbe) SHA1(0af611424cbf287b26c668a3109fb0861a27f603), ROM_BIOS(2) )

	ROM_REGION( 0x40000, "flash2", 0 )
	ROMX_LOAD( "cyos_v1358.bin", 0, 0x40000, CRC(05ca4ece) SHA1(eee329e8541e1e36c22acb1317378ce23ccd1e12), ROM_BIOS(0) )
	ROMX_LOAD( "cyos_v1357.bin", 0, 0x40000, CRC(54ba7d43) SHA1(c6e0f7982e0f7a5fa65f2cecc8b27cb21909a407), ROM_BIOS(1) )
	ROMX_LOAD( "cyos_v1355.bin", 0, 0x40000, CRC(02d3dba5) SHA1(4ed728940bbcb3d2464fc7fba14d17924ece94aa), ROM_BIOS(2) )
ROM_END

ROM_START( cybikoxt )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD( "cyrom150.bin", 0, 0x8000, CRC(18b9b21f) SHA1(28868d6174eb198a6cec6c3c70b6e494517229b9) )

	ROM_SYSTEM_BIOS( 0, "v1508", "v1.5.08" )

	ROM_REGION16_BE( 0x80000, "flashxt", 0 )
	ROMX_LOAD( "cyos_v1508.bin", 0, 0x80000, CRC(f79400ba) SHA1(537a88e238746b3944b0cdfd4b0a9396460b2977), ROM_BIOS(0) )
ROM_END

//////////////
// DRIVERS  //
//////////////

/*    YEAR  NAME      PARENT    COMPAT  MACHINE   INPUT     CLASS         INIT           COMPANY       FULLNAME               FLAGS */
COMP( 2000, cybikov1, 0,        0,      cybikov1, cybiko,   cybiko_state, init_cybiko,   "Cybiko Inc", "Cybiko Classic (V1)", MACHINE_IMPERFECT_SOUND )
COMP( 2000, cybikov2, cybikov1, 0,      cybikov2, cybiko,   cybiko_state, init_cybiko,   "Cybiko Inc", "Cybiko Classic (V2)", MACHINE_IMPERFECT_SOUND )
COMP( 2001, cybikoxt, cybikov1, 0,      cybikoxt, cybikoxt, cybiko_state, init_cybikoxt, "Cybiko Inc", "Cybiko Xtreme",       MACHINE_IMPERFECT_SOUND )



cz1.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Devin Acker
/***************************************************************************

    Casio CZ-1 Digital Synthesizer

    Also includes support for the MZ-1, an unreleased rack mount version supported by
    the same ROM. This has no key/wheel/pedal inputs and never sends the corresponding
    MIDI messages, but otherwise works identically to the CZ-1.

    Misc. notes:

    Hold all three Line 1 envelope buttons (DCO/DCW/DCA) on boot to perform a RAM test.

    Afterwards, the firmware will attempt to load and run a program from cartridge
    if a valid 5-byte header is detected at the beginning:
    - bytes 0-1: ignored
    - bytes 2-3: program load address (valid within 0x8000-9fff, includes this header)
    - byte 4: constant 0xD1
    - bytes 5+: start of program

    TODO:

    Both machines have MACHINE_IMPERFECT_SOUND due to unemulated stereo chorus.

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

#include "emu.h"

#include "ra3.h"
#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/mcs48/mcs48.h"
#include "cpu/upd7810/upd7810.h"
#include "machine/clock.h"
#include "machine/input_merger.h"
#include "machine/msm6200.h"
#include "machine/nvram.h"
#include "sound/mixer.h"
#include "sound/upd933.h"
#include "video/hd44780.h"

#include "emupal.h"
#include "screen.h"
#include "softlist.h"
#include "speaker.h"

#include <cmath>

#include "cz1.lh"
#include "mz1.lh"

namespace {

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

class cz1_state : public driver_device
{
public:
	cz1_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_subcpu(*this, "subcpu"),
		m_mcu(*this, "mcu"),
		m_hd44780(*this, "hd44780"),
		m_upd933(*this, "upd933_%u", 0U),
		m_cart(*this, "cart"),
		m_mixer(*this, "mixer%u", 0U),
		m_keys(*this, "KC%u", 0U),
		m_leds(*this, "led_%u.%u", 0U, 0U),
		m_led_env(*this, "led_env%u", 0U),
		m_led_bank(*this, "led_bank%u", 0U),
		m_led_tone(*this, "led_tone%u", 0U)
	{ }

	void mz1(machine_config &config);
	void cz1(machine_config &config);

	int cont_r();
	int sync_r();

	int cont49_r();
	int sync49_r();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void cz1_palette(palette_device &palette) const;
	HD44780_PIXEL_UPDATE(lcd_pixel_update);

	void mz1_main_map(address_map &map) ATTR_COLD;
	void cz1_main_map(address_map &map) ATTR_COLD;
	void sub_map(address_map &map) ATTR_COLD;
	void mcu_map(address_map &map) ATTR_COLD;

	// main CPU r/w methods
	u8 keys_r();
	void led_w(offs_t offset, u8 data);
	void volume_w(u8 data);
	void stereo_w(u8 data);

	void cart_addr_w(u8 data);
	void cart_addr_hi_w(u8 data);

	void main_pa_w(u8 data);
	u8 main_pa_r();
	void main_pb_w(u8 data);
	void main_pc_w(u8 data);

	// sub CPU r/w methods
	void sound_w(u8 data);
	u8 sound_r();

	void sub_pa_w(u8 data);
	void sub_pb_w(u8 data);
	void sub_pc_w(u8 data);

	// main/sub CPU comm methods
	void main_to_sub_0_w(u8 data);
	TIMER_CALLBACK_MEMBER(main_to_sub_0_cb);
	u8 main_to_sub_0_r();
	void main_to_sub_1_w(u8 data);
	TIMER_CALLBACK_MEMBER(main_to_sub_1_cb);
	u8 main_to_sub_1_r();
	void sub_to_main_w(u8 data);
	TIMER_CALLBACK_MEMBER(sub_to_main_cb);
	u8 sub_to_main_r();

	void sync_clr_w(u8);
	TIMER_CALLBACK_MEMBER(sync_clr_cb);

	void main_irq_w(u8);
	void main_irq_ack_w(u8);

	// main CPU / key MCU comm methods
	u8 mcu_r();
	void mcu_p2_w(u8 data);

	required_device<upd7810_device> m_maincpu;
	required_device<upd7810_device> m_subcpu;
	optional_device<i8049_device> m_mcu;
	required_device<hd44780_device> m_hd44780;
	required_device_array<upd933_device, 2> m_upd933;
	required_device<casio_ram_cart_device> m_cart;
	required_device_array<mixer_device, 2> m_mixer;
	optional_ioport_array<16> m_keys;
	output_finder<5, 6> m_leds;
	output_finder<16> m_led_env;
	output_finder<8> m_led_bank, m_led_tone;

	float m_volume[0x40];

	u8 m_main_port[3];
	u8 m_mcu_p2;
	u8 m_midi_rx;

	u8 m_main_to_sub[2];
	u8 m_sub_to_main;
	u8 m_sync, m_sync49;

	u16 m_cart_addr;
};


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

void cz1_state::mz1_main_map(address_map &map)
{
	map.unmap_value_high();

	map(0x0000, 0x7fff).rom();
	map(0x8000, 0x9fff).ram().share("mainram");
	map(0xb000, 0xbfff).w(FUNC(cz1_state::volume_w));
	map(0xc000, 0xc000).mirror(0x1ff0).w(FUNC(cz1_state::main_to_sub_0_w));
	map(0xc001, 0xc001).mirror(0x1ff0).w(FUNC(cz1_state::main_to_sub_1_w));
	map(0xc002, 0xc002).mirror(0x1ff0).r(FUNC(cz1_state::sub_to_main_r));
	map(0xc004, 0xc004).mirror(0x1ff0).r(FUNC(cz1_state::keys_r));
	map(0xc005, 0xc005).mirror(0x1ff0).w(FUNC(cz1_state::sync_clr_w));
	map(0xc006, 0xc006).mirror(0x1ff0).w(FUNC(cz1_state::main_irq_ack_w));
	map(0xc007, 0xc00b).mirror(0x1ff0).w(FUNC(cz1_state::led_w));
	map(0xc00c, 0xc00c).mirror(0x1ff0).w(FUNC(cz1_state::stereo_w));
	map(0xc00e, 0xc00e).mirror(0x1ff0).w(FUNC(cz1_state::cart_addr_w));
	map(0xc00f, 0xc00f).mirror(0x1ff0).w(FUNC(cz1_state::cart_addr_hi_w));
}

/**************************************************************************/
void cz1_state::cz1_main_map(address_map &map)
{
	mz1_main_map(map);
	map(0xc00d, 0xc00d).mirror(0x1ff0).r(FUNC(cz1_state::mcu_r));
}

/**************************************************************************/
void cz1_state::sub_map(address_map &map)
{
	map.unmap_value_high();

	map(0x0000, 0x3fff).rom();
	map(0x4000, 0x7fff).ram().share("subram");
	map(0x8000, 0x9fff).rw(FUNC(cz1_state::sound_r), FUNC(cz1_state::sound_w));
	map(0xa000, 0xbfff).rw(FUNC(cz1_state::main_to_sub_1_r), FUNC(cz1_state::sub_to_main_w));
	map(0xc000, 0xdfff).rw(FUNC(cz1_state::main_to_sub_0_r), FUNC(cz1_state::main_irq_w));
}

/**************************************************************************/
void cz1_state::mcu_map(address_map &map)
{
	map(0x00, 0xff).rw("kbd", FUNC(msm6200_device::read), FUNC(msm6200_device::write));
}

//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( mz1 )

	PORT_START("KC0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Normal")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tone Mix")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Key Split")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Operation Memory")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Solo")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("MIDI")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Portamento On/Off")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Glide On/Off")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Key Transpose")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Master Tune")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Exchange")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Cartridge")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Bank A")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Bank B")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Bank C")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Bank D")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Bank E")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Bank F")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Memory 5")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Memory 6")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Memory 7")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Memory 8")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Bank G")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Bank H")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_LEFT)  PORT_NAME("Cursor Left / No")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Cursor Right / Yes")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Memory 1")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Memory 2")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Memory 3")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Memory 4")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("Page Down")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_PLUS_PAD)  PORT_NAME("Page Up")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Env. Point Sustain")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Env. Point End")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_UP)   PORT_NAME("Value Down / Save")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Value Up / Load")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Wheel / Aftertouch")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Bend Range")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Glide")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Portamento")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Name")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Cartridge/MIDI Save/Load")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Initialize")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Octave")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Vibrato")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Line Select")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Ring")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Noise")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCO 1 Wave")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCO 1 Env")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCW 1 Key Follow")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCW 1 Env")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCA 1 Key Follow")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCA 1 Env")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCO 2 Wave")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCO 2 Env")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCW 2 Key Follow")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCW 2 Env")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCA 2 Key Follow")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCA 2 Env")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCA 1 Velocity")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCA 2 Velocity")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCA 1 Level")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DCA 2 Level")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Parameter Copy")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Detune")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC11")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("MIDI On/Off")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Compare/Recall")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Write")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Modulation On/Off")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER)  PORT_TOGGLE PORT_NAME("Memory Protect")
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC12")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("cart", FUNC(casio_ram_cart_device::sense))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_CUSTOM) // low = MZ-1, high = CZ-1
	PORT_BIT(0xfc, IP_ACTIVE_LOW,  IPT_UNUSED)

	PORT_START("MAIN_PB")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(cz1_state::sync_r))
	PORT_BIT(0xfe, IP_ACTIVE_LOW,  IPT_UNUSED)

	PORT_START("SUB_PB")
	PORT_BIT(0x01, IP_ACTIVE_LOW,  IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("upd933_0", FUNC(upd933_device::rq_r))
	PORT_BIT(0x02, IP_ACTIVE_LOW,  IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("upd933_1", FUNC(upd933_device::rq_r))
	PORT_BIT(0xfc, IP_ACTIVE_LOW,  IPT_UNUSED)

	PORT_START("SUB_PC")
	PORT_BIT(0x01, IP_ACTIVE_LOW,  IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(cz1_state::sync_r))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(cz1_state::cont_r))
	PORT_BIT(0xf8, IP_ACTIVE_LOW,  IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( cz1 )
	PORT_INCLUDE(mz1)

	PORT_START("kbd:KI8")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C6")

	PORT_START("kbd:KI9")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B5")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#5")

	PORT_START("kbd:KI10")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A5")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#5")

	PORT_START("kbd:KI11")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G5")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#5")

	PORT_START("kbd:KI12")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F5")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E5")

	PORT_START("kbd:KI13")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#5")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D5")

	PORT_START("kbd:KI14")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#5")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C5")

	PORT_START("kbd:KI15")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B4")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#4")

	PORT_START("kbd:KI16")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A4")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#4")

	PORT_START("kbd:KI17")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G4")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#4")

	PORT_START("kbd:KI18")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F4")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E4")

	PORT_START("kbd:KI19")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#4")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D4")

	PORT_START("kbd:KI20")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#4")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C4")

	PORT_START("kbd:KI21")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B3")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#3")

	PORT_START("kbd:KI22")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A3")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#3")

	PORT_START("kbd:KI23")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G3")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#3")

	PORT_START("kbd:KI24")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F3")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E3")

	PORT_START("kbd:KI25")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#3")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D3")

	PORT_START("kbd:KI26")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#3")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C3")

	PORT_START("kbd:KI27")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B2")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#2")

	PORT_START("kbd:KI28")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A2")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#2")

	PORT_START("kbd:KI29")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G2")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#2")

	PORT_START("kbd:KI30")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F2")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E2")

	PORT_START("kbd:KI31")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#2")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D2")

	PORT_START("kbd:KI32")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#2")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C2")

	PORT_START("kbd:KI33")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B1")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#1")

	PORT_START("kbd:KI34")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A1")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#1")

	PORT_START("kbd:KI35")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G1")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#1")

	PORT_START("kbd:KI36")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F1")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E1")

	PORT_START("kbd:KI37")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#1")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D1")

	PORT_START("kbd:KI38")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#1")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C1")

	PORT_START("kbd:VELOCITY")
	PORT_BIT(0x3f, 0x3f, IPT_POSITIONAL_V) PORT_NAME("Key Velocity") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_CENTERDELTA(0) PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH) PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH)

	PORT_START("AN0")
	PORT_BIT(0xff, 0x7f, IPT_PADDLE)       PORT_NAME("Pitch Wheel") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00, 0xff) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)

	PORT_START("AN1")
	PORT_BIT(0xff, 0x00, IPT_POSITIONAL_V) PORT_NAME("Modulation Wheel") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_CENTERDELTA(0) PORT_PLAYER(2) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)

	PORT_START("AN2")
	PORT_BIT(0xff, 0xff, IPT_POSITIONAL_V) PORT_NAME("Aftertouch") PORT_REVERSE PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_CENTERDELTA(0) PORT_PLAYER(3) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)

	PORT_MODIFY("KC11")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Sustain Pedal")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_MODIFY("KC12")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_CUSTOM) // low = MZ-1, high = CZ-1

	PORT_START("MAIN_PC")
	PORT_BIT(0x0f, IP_ACTIVE_LOW,  IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(cz1_state::cont49_r))
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(cz1_state::sync49_r))
	PORT_BIT(0xc0, IP_ACTIVE_LOW,  IPT_UNUSED)
INPUT_PORTS_END


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

void cz1_state::cz1_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148)); // background
	palette.set_pen_color(1, rgb_t( 63,  59,  62)); // LCD pixel on
	palette.set_pen_color(2, rgb_t(131, 136, 139)); // LCD pixel off
}

/**************************************************************************/
HD44780_PIXEL_UPDATE( cz1_state::lcd_pixel_update )
{
	// char size is 5x8
	if (x > 4 || y > 7)
		return;

	if (line < 2 && pos < 16)
		bitmap.pix(1 + y + line*8 + line, 1 + pos*6 + x) = state ? 1 : 2;
}


/**************************************************************************/
u8 cz1_state::keys_r()
{
	return m_keys[m_main_port[0] & 0xf].read_safe(0xffff);
}

/**************************************************************************/
void cz1_state::led_w(offs_t offset, u8 data)
{
	for (int i = 0; i < 6; i++)
		m_leds[offset][i] = BIT(data, i);
}

/**************************************************************************/
void cz1_state::volume_w(u8 data)
{
	const float vol = m_volume[~data & 0x3f];
	m_mixer[0]->set_output_gain(ALL_OUTPUTS, vol);
	m_mixer[1]->set_output_gain(ALL_OUTPUTS, vol);
}

/**************************************************************************/
void cz1_state::stereo_w(u8 data)
{
	/*
	bit 0: sound chip #1 routing (0: center, 1: left)
	bit 1: sound chip #2 routing (0: center, 2: right)
	bit 2: center channel stereo chorus (0: on, 1: off)
	*/
	m_mixer[0]->set_input_gain(1, BIT(data, 1) ? 0.0 : 1.0);
	m_mixer[1]->set_input_gain(0, BIT(data, 0) ? 0.0 : 1.0);
}

/**************************************************************************/
void cz1_state::cart_addr_w(u8 data)
{
	m_cart_addr &= 0x3f00;
	m_cart_addr |= ~data;
}

/**************************************************************************/
void cz1_state::cart_addr_hi_w(u8 data)
{
	m_cart_addr &= 0xff;
	m_cart_addr |= (~data & 0x3f) << 8;
}

/**************************************************************************/
void cz1_state::main_pa_w(u8 data)
{
	m_hd44780->db_w(data);
	m_main_port[0] = data;
}

/**************************************************************************/
u8 cz1_state::main_pa_r()
{
	u8 data = m_hd44780->db_r();
	if (!BIT(m_main_port[1], 2))
		data &= m_cart->read(m_cart_addr);
	return data;
}

/**************************************************************************/
void cz1_state::main_pb_w(u8 data)
{
	if (BIT(data ^ m_main_port[1], 2))
		m_subcpu->set_input_line(UPD7810_INTF1, BIT(data, 2));

	if (BIT(data, 4) && !BIT(m_main_port[1], 4) && BIT(m_main_port[1], 6))
		m_cart->write(m_cart_addr, m_main_port[0]);

	m_hd44780->e_w(BIT(~data, 7));
	m_hd44780->rw_w(BIT(data, 6));
	m_hd44780->rs_w(BIT(data, 5));

	m_main_port[1] = data;
}

/**************************************************************************/
void cz1_state::main_pc_w(u8 data)
{
	m_main_port[2] = data;
}


/**************************************************************************/
void cz1_state::sound_w(u8 data)
{
	m_upd933[0]->write(data);
	m_upd933[1]->write(data);
}

/**************************************************************************/
u8 cz1_state::sound_r()
{
	return m_upd933[0]->read() & m_upd933[1]->read();
}

/**************************************************************************/
void cz1_state::sub_pa_w(u8 data)
{
	for (int i = 0; i < 15; i++)
		m_led_env[i] = (BIT(data, 0, 4) == i);
	for (int i = 0; i < 8; i++)
		m_led_tone[i] = !BIT(data, 7) && (BIT(data, 4, 3) == i);
}

/**************************************************************************/
void cz1_state::sub_pb_w(u8 data)
{
	for (int i = 0; i < 2; i++)
	{
		m_upd933[i]->id_w(BIT(data, 5));
		m_upd933[i]->cs_w(BIT(data, 2 + i));

		m_upd933[i]->set_output_gain(ALL_OUTPUTS, BIT(data, 6) ? 0.0 : 1.0);
	}
}

/**************************************************************************/
void cz1_state::sub_pc_w(u8 data)
{
	for (int i = 0; i < 8; i++)
		m_led_bank[i] = !BIT(data, 0) && (BIT(data, 5, 3) == i);
}

/**************************************************************************/
void cz1_state::main_to_sub_0_w(u8 data)
{
	machine().scheduler().synchronize(timer_expired_delegate(FUNC(cz1_state::main_to_sub_0_cb), this), data);
}

/**************************************************************************/
TIMER_CALLBACK_MEMBER(cz1_state::main_to_sub_0_cb)
{
	m_main_to_sub[0] = param;
	m_sync = 1;
}

/**************************************************************************/
u8 cz1_state::main_to_sub_0_r()
{
	if (!machine().side_effects_disabled())
		m_sync = 0;
	return m_main_to_sub[0];
}

/**************************************************************************/
void cz1_state::main_to_sub_1_w(u8 data)
{
	machine().scheduler().synchronize(timer_expired_delegate(FUNC(cz1_state::main_to_sub_1_cb), this), data);
}

/**************************************************************************/
TIMER_CALLBACK_MEMBER(cz1_state::main_to_sub_1_cb)
{
	m_main_to_sub[1] = param;
}

/**************************************************************************/
u8 cz1_state::main_to_sub_1_r()
{
	return m_main_to_sub[1];
}

/**************************************************************************/
void cz1_state::sub_to_main_w(u8 data)
{
	machine().scheduler().synchronize(timer_expired_delegate(FUNC(cz1_state::sub_to_main_cb), this), data);
}

/**************************************************************************/
TIMER_CALLBACK_MEMBER(cz1_state::sub_to_main_cb)
{
	m_sub_to_main = param;
}

/**************************************************************************/
u8 cz1_state::sub_to_main_r()
{
	return m_sub_to_main;
}

/**************************************************************************/
int cz1_state::cont_r()
{
	return BIT(m_main_port[1], 3);
}

/**************************************************************************/
int cz1_state::sync_r()
{
	return m_sync;
}

/**************************************************************************/
void cz1_state::sync_clr_w(u8)
{
	machine().scheduler().synchronize(timer_expired_delegate(FUNC(cz1_state::sync_clr_cb), this), 0);
}

/**************************************************************************/
TIMER_CALLBACK_MEMBER(cz1_state::sync_clr_cb)
{
	m_sync = 0;
}

/**************************************************************************/
void cz1_state::main_irq_w(u8)
{
	m_maincpu->set_input_line(UPD7810_INTF1, ASSERT_LINE);
}

/**************************************************************************/
void cz1_state::main_irq_ack_w(u8)
{
	m_maincpu->set_input_line(UPD7810_INTF1, CLEAR_LINE);
}

/**************************************************************************/
u8 cz1_state::mcu_r()
{
	if (!machine().side_effects_disabled())
		m_sync49 = 0;

	return ~m_mcu->p1_r();
}

/**************************************************************************/
void cz1_state::mcu_p2_w(u8 data)
{
	if (BIT(data ^ m_mcu_p2, 6))
		m_maincpu->set_input_line(UPD7810_INTF2, BIT(~data, 6));

	if (BIT(~data & m_mcu_p2, 7))
		m_sync49 = 1;

	m_mcu_p2 = data;
}

/**************************************************************************/
int cz1_state::cont49_r()
{
	return BIT(m_mcu_p2, 5);
}

/**************************************************************************/
int cz1_state::sync49_r()
{
	return m_sync49;
}

/**************************************************************************/
void cz1_state::machine_start()
{
	m_leds.resolve();
	m_led_env.resolve();
	m_led_bank.resolve();
	m_led_tone.resolve();

	// aftertouch amp levels (TODO: are these correct?)
	for (int i = 0; i < 0x40; i++)
		m_volume[i] = pow(2, (float)i / 0x3f) - 1.0;

	m_main_port[0] = m_main_port[1] = m_main_port[2] = 0xff;
	m_mcu_p2 = 0xff;

	// register for save states
	save_item(NAME(m_main_port));
	save_item(NAME(m_mcu_p2));
	save_item(NAME(m_midi_rx));
	save_item(NAME(m_cart_addr));
	save_item(NAME(m_main_to_sub));
	save_item(NAME(m_sub_to_main));
	save_item(NAME(m_sync));
	save_item(NAME(m_sync49));
}

/**************************************************************************/
void cz1_state::machine_reset()
{
	m_main_to_sub[0] = m_main_to_sub[1] = 0;
	m_sub_to_main = 0;
	m_sync = 0;
	m_sync49 = 1;

	m_cart_addr = 0;
	m_midi_rx = 1;
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void cz1_state::mz1(machine_config &config)
{
	UPD7810(config, m_maincpu, 15_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &cz1_state::mz1_main_map);
	m_maincpu->pa_in_cb().set(FUNC(cz1_state::main_pa_r));
	m_maincpu->pa_out_cb().set(FUNC(cz1_state::main_pa_w));
	m_maincpu->pb_in_cb().set_ioport("MAIN_PB");
	m_maincpu->pb_out_cb().set(FUNC(cz1_state::main_pb_w));
	m_maincpu->pc_out_cb().set(FUNC(cz1_state::main_pc_w));

	CLOCK(config, "midi_clock", 2_MHz_XTAL).signal_handler().set(m_maincpu, FUNC(upd7810_device::sck_w));

	UPD7810(config, m_subcpu, 15_MHz_XTAL);
	m_subcpu->set_addrmap(AS_PROGRAM, &cz1_state::sub_map);
	m_subcpu->pa_out_cb().set(FUNC(cz1_state::sub_pa_w));
	m_subcpu->pb_in_cb().set_ioport("SUB_PB");
	m_subcpu->pb_out_cb().set(FUNC(cz1_state::sub_pb_w));
	m_subcpu->pc_in_cb().set_ioport("SUB_PC");
	m_subcpu->pc_out_cb().set(FUNC(cz1_state::sub_pc_w));

	INPUT_MERGER_ANY_HIGH(config, "irq").output_handler().set_inputline(m_subcpu, UPD7810_INTF2);

	NVRAM(config, "mainram");
	NVRAM(config, "subram");
	CASIO_RA6(config, m_cart);
	SOFTWARE_LIST(config, "cart_list").set_original("cz1_cart");

	midi_port_device &mdin(MIDI_PORT(config, "mdin", midiin_slot, "midiin"));
	mdin.rxd_handler().set([this] (int state) { m_midi_rx = state; });
	m_maincpu->rxd_func().set([this] () { return m_midi_rx; });

	MIDI_PORT(config, "mdout", midiout_slot, "midiout");
	m_maincpu->txd_func().set("mdout", FUNC(midi_port_device::write_txd));

	MIDI_PORT(config, "mdthru", midiout_slot, "midiout");
	mdin.rxd_handler().append("mdthru", FUNC(midi_port_device::write_txd));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(6*16 + 1, 19);
	screen.set_visarea_full();
	screen.set_screen_update("hd44780", FUNC(hd44780_device::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(cz1_state::cz1_palette), 3);

	HD44780(config, m_hd44780, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_hd44780->set_lcd_size(2, 16);
	m_hd44780->set_function_set_at_any_time();
	m_hd44780->set_pixel_update_cb(FUNC(cz1_state::lcd_pixel_update));

	config.set_default_layout(layout_mz1);

	// sound hardware
	SPEAKER(config, "speaker", 2).front();

	MIXER(config, m_mixer[0]).add_route(ALL_OUTPUTS, "speaker", 1.0, 0);
	MIXER(config, m_mixer[1]).add_route(ALL_OUTPUTS, "speaker", 1.0, 1);

	UPD933(config, m_upd933[0], 8.96_MHz_XTAL / 2);
	m_upd933[0]->irq_cb().set("irq",  FUNC(input_merger_any_high_device::in_w<0>));
	m_upd933[0]->add_route(0, m_mixer[0], 1.0, 0);
	m_upd933[0]->add_route(0, m_mixer[1], 1.0, 0);

	UPD933(config, m_upd933[1], 8.96_MHz_XTAL / 2);
	m_upd933[1]->irq_cb().set("irq",  FUNC(input_merger_any_high_device::in_w<1>));
	m_upd933[1]->add_route(0, m_mixer[0], 1.0, 1);
	m_upd933[1]->add_route(0, m_mixer[1], 1.0, 1);
}

/**************************************************************************/
void cz1_state::cz1(machine_config &config)
{
	mz1(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &cz1_state::cz1_main_map);
	m_maincpu->pc_in_cb().set_ioport("MAIN_PC");
	m_maincpu->an0_func().set_ioport("AN0");
	m_maincpu->an1_func().set_ioport("AN1");
	m_maincpu->an2_func().set_ioport("AN2");

	I8049(config, m_mcu, 8.96_MHz_XTAL);
	m_mcu->set_addrmap(AS_IO, &cz1_state::mcu_map);
	m_mcu->p2_out_cb().set(FUNC(cz1_state::mcu_p2_w));
	m_mcu->t0_in_cb().set(FUNC(cz1_state::sync49_r));
	m_mcu->t1_in_cb().set([this] () { return BIT(m_main_port[2], 7); });

	MSM6200(config, "kbd", 2.47_MHz_XTAL).irq_cb().set_inputline(m_mcu, MCS48_INPUT_IRQ); // CSA2.47MG ceramic oscillator

	config.set_default_layout(layout_cz1);
}

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

ROM_START( cz1 )
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD("upd27c256c-20a154.bin", 0x0000, 0x8000, CRC(a970ee65) SHA1(269f2e823ac6353eca9fdb682deebeb7d4d0f585))

	ROM_REGION(0x2000, "mainram", 0)
	ROM_LOAD("init_main.bin", 0x0000, 0x2000, CRC(25fbf88a) SHA1(b7eee5af1d3470ea951df3a019ba2e2a055e84c7))

	ROM_REGION(0x4000, "subcpu", 0)
	ROM_LOAD("upd23c128ec-036.bin", 0x0000, 0x4000, CRC(3cf23c4e) SHA1(b27ee664c31526058defd8e8666ec8e7828059a2))

	ROM_REGION(0x4000, "subram", 0)
	ROM_LOAD("init_sub.bin", 0x0000, 0x4000,  CRC(c0b498af) SHA1(73c48bf5df0d3660c50c370286559a8d4cdb6b99))

	ROM_REGION(0x800, "mcu", 0) // this dump is actually uPD80C49HC-187 from the HT-6000, though it appears functionally identical
	ROM_LOAD("upd8049hc-672.bin", 0x000, 0x800, BAD_DUMP CRC(47b47af7) SHA1(8f0515f95dcc6e224a8a59e0c2cd7ddb4796e34e))
ROM_END

#define rom_mz1 rom_cz1

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY  FULLNAME            FLAGS
SYST( 1986, cz1,    0,      0,      cz1,     cz1,    cz1_state,    empty_init, "Casio", "CZ-1",             MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )
SYST( 1986, mz1,    cz1,    0,      mz1,     mz1,    cz1_state,    empty_init, "Casio", "MZ-1 (prototype)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )



cz101.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best, Devin Acker
/***************************************************************************

    Casio CZ-101

    Digital Synthesizer

    Misc. notes:

    To run a (currently undumped) test/diagnostic/debug cartridge:
    Hold "env step +", "env step -", "initialize", and "write" all at once, then press "load".
    If a cartridge is inserted that begins with the 8 bytes "5a 96 5a 96 5a 96 5a 96", then
    the CZ-101 firmware will copy the first 2kb of the cart to $8800-8fff and then call $8810.
    This works even when the normal "cart detect" signal isn't present.
    (Note that this will wipe out all patches saved to the internal RAM.)

    Unused input matrix bits:
    Bit 7 of KC15 is normally unused, but if pulled low using a diode, then bits 2-5 of KC8
    (also normally unused) become DIP switches that override the normal MIDI "basic channel"
    setting from the front panel (possibly planned for use on a screenless MIDI module).

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

#include "emu.h"

#include "ra3.h"
#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/upd7810/upd7810.h"
#include "machine/clock.h"
#include "machine/nvram.h"
#include "sound/upd933.h"
#include "video/hd44780.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

//#define VERBOSE 1
#include "logmacro.h"

#include "cz101.lh"


namespace {

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

class cz101_state : public driver_device
{
public:
	cz101_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_hd44780(*this, "hd44780"),
		m_upd933(*this, "upd933"),
		m_cart(*this, "cart"),
		m_keys(*this, "kc%u", 0),
		m_leds(*this, "led_%u", 0U),
		m_led_env(*this, "led_env%u.%u", 0U, 0U),
		m_led_tone(*this, "led_tone%u.%u", 0U, 0U),
		m_power(0),
		m_port_b(0),
		m_port_c(0),
		m_midi_rx(1)
	{ }

	void cz101(machine_config &config);

	void cz101_palette(palette_device &palette) const;
	HD44780_PIXEL_UPDATE(lcd_pixel_update);

	DECLARE_INPUT_CHANGED_MEMBER(power_w);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void maincpu_map(address_map &map) ATTR_COLD;

	void port_b_w(uint8_t data);
	void port_c_w(uint8_t data);

	void led_1_w(uint8_t data);
	void led_2_w(uint8_t data);
	void led_3_w(uint8_t data);
	void led_4_w(uint8_t data);
	uint8_t keys_r();

	required_device<upd7810_device> m_maincpu;
	required_device<hd44780_device> m_hd44780;
	required_device<upd933_device> m_upd933;
	required_device<casio_ram_cart_device> m_cart;
	required_ioport_array<16> m_keys;
	output_finder<16> m_leds;
	output_finder<3, 4> m_led_env;
	output_finder<3, 3> m_led_tone;

	uint8_t m_power;
	uint8_t m_port_b;
	uint8_t m_port_c;
	uint8_t m_midi_rx;
};


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

void cz101_state::maincpu_map(address_map &map)
{
	map.unmap_value_high();

	map(0x0000, 0x7fff).rom().region("program", 0);
	map(0x8000, 0x8fff).ram().share("nvram");
	map(0x9000, 0x97ff).rw(m_cart, FUNC(casio_ram_cart_device::read), FUNC(casio_ram_cart_device::write));
	map(0x9800, 0x9fff).w(FUNC(cz101_state::led_4_w));
	map(0xa000, 0xa7ff).w(FUNC(cz101_state::led_3_w));
	map(0xa800, 0xafff).w(FUNC(cz101_state::led_2_w));
	map(0xb000, 0xb7ff).w(FUNC(cz101_state::led_1_w));
	map(0xb800, 0xbfff).r(FUNC(cz101_state::keys_r));
	map(0xc000, 0xfeff).rw(m_upd933, FUNC(upd933_device::read), FUNC(upd933_device::write));
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( cz101 )
	PORT_START("kc0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C2")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C#2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D2")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D#2")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("E2")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F2")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kc1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F#2")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G#2")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A2")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A#2")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("B2")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kc2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C3")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C#3")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D3")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D#3")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("E3")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F3")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kc3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F#3")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G3")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G#3")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A3")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A#3")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("B3")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kc4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C4")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C#4")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D4")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D#4")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("E4")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F4")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kc5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F#4")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G4")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G#4")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A4")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A#4")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("B4")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kc6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C5")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C#5")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D5")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D#5")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("E5")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F5")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kc7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F#5")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G5")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G#5")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A5")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A#5")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("B5")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kc8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C6")
	PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kc9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Portamento On/Off")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Portamento Time")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Vibrato On/Off")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Bend Range")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Preset")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Internal")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Cartridge")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Compare/Recall")

	PORT_START("kc10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_E)     PORT_NAME("Solo")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_R)     PORT_NAME("Tone Mix")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_T)     PORT_NAME("Key Transpose")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_Y)     PORT_NAME("Write")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_U)     PORT_NAME("MIDI")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER)  PORT_TOGGLE              PORT_NAME("Memory Protect")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("Select")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_MEMORY_RESET)                    PORT_NAME("P (Reset RAM)")

	PORT_START("kc11")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Tone 1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Tone 2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Tone 3")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Tone 4")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Tone 5")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Tone 6")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Tone 7")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Tone 8")

	PORT_START("kc12")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_DOWN)   PORT_NAME(u8"Value \u25bd / Save") // ▽
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_UP)     PORT_NAME(u8"Value \u25b3 / Load") // △
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_LEFT)   PORT_NAME(u8"Cursor \u25c1") // ◁
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_RIGHT)  PORT_NAME(u8"Cursor \u25b7") // ▷
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_END)    PORT_NAME(u8"Env. Step \u25bd") // ▽
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_HOME)   PORT_NAME(u8"Env. Step \u25b3") // △
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_INSERT) PORT_NAME("Env. Point Sustain")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL)    PORT_NAME("Env. Point End")

	PORT_START("kc13")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("cart", FUNC(casio_ram_cart_device::sense))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Vibrato")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("DCO1 Wave Form")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("DCO1 Envelope")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("DCW1 Key Follow")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("DCW1 Envelope")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("DCA1 Key Follow")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_J) PORT_NAME("DCA1 Envelope")

	PORT_START("kc14")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_COMMA) PORT_NAME("Initialize")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_Z)     PORT_NAME("Octave")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_X)     PORT_NAME("DCO2 Wave Form")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_C)     PORT_NAME("DCO2 Envelope")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_V)     PORT_NAME("DCW2 Key Follow")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_B)     PORT_NAME("DCW2 Envelope")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_N)     PORT_NAME("DCA2 Key Follow")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_M)     PORT_NAME("DCA2 Envelope")

	PORT_START("kc15")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_K)      PORT_NAME("Detune")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_L)      PORT_NAME("Line Select")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_STOP)   PORT_NAME("Ring Modulation")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_SLASH)  PORT_NAME("Noise Modulation")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_MINUS)  PORT_NAME(u8"Master Tune \u25bd") // ▽
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_EQUALS) PORT_NAME(u8"Master Tune \u25b3") // △
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER)  PORT_TOGGLE               PORT_NAME("Auto Power Off")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("PB")
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW,  IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("upd933", FUNC(upd933_device::rq_r))
	PORT_BIT(0x60, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_NAME("Power") PORT_TOGGLE PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz101_state::power_w), 0)

	PORT_START("AN1")
	PORT_BIT(0xff, 0x7f, IPT_PADDLE) PORT_NAME("Pitch Wheel") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00, 0xff) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)

	PORT_START("AN2")
	PORT_CONFNAME(0xff, 0xff, "Battery Level")
	PORT_CONFSETTING(   0xff, "Normal")
	PORT_CONFSETTING(   0x00, "Low")
INPUT_PORTS_END


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

void cz101_state::cz101_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148)); // background
	palette.set_pen_color(1, rgb_t( 63,  59,  62)); // LCD pixel on
	palette.set_pen_color(2, rgb_t(131, 136, 139)); // LCD pixel off
}

HD44780_PIXEL_UPDATE( cz101_state::lcd_pixel_update )
{
	// char size is 5x8
	if (!m_power || x > 4 || y > 7)
		return;

	if (line < 2 && pos < 16)
		bitmap.pix(1 + y + line*8 + line, 1 + pos*6 + x) = state ? 1 : 2;
}

INPUT_CHANGED_MEMBER(cz101_state::power_w)
{
	if (!newval)
	{
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
	}
	else
	{
		m_power = 1;
		m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
		m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
	}
}

void cz101_state::led_4_w(uint8_t data)
{
	if (!m_power) data = 0xff;

	for (int i = 0; i < 7; i++)
		m_leds[i] = BIT(~data, i);
}

void cz101_state::led_3_w(uint8_t data)
{
	if (!m_power) data = 0xff;

	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 4; j++)
			m_led_env[i][j] = BIT(data, 4+i) & BIT(~data, j);
}

void cz101_state::led_2_w(uint8_t data)
{
	if (!m_power) data = 0xff;

	m_leds[7] = BIT(~data, 7);

	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
			m_led_tone[i][j] = BIT(data, 1+i) & BIT(~data, 4+j);
}

void cz101_state::led_1_w(uint8_t data)
{
	if (!m_power) data = 0xff;

	for (int i = 0; i < 8; i++)
		m_leds[8+i] = BIT(~data, i);
}

uint8_t cz101_state::keys_r()
{
	return m_keys[m_port_b & 0x0f]->read();
}

// 7-------  power switch input (also connected to /NMI)
// -6------  music lsi write enable
// --5-----  music lsi chip select
// ---4----  music lsi irq input
// ----3210  key select output

void cz101_state::port_b_w(uint8_t data)
{
	LOG("port_b_w: %02x\n", data);

	m_upd933->cs_w(BIT(data, 5));

	m_port_b = data;
}

// 7-------  lcd e
// -6------  lcd rw
// --5-----  lcd rs
// ---4----  not used / debug? (MIDI CC #7 writes data to port A and then toggles this bit)
// ----3---  power down output
// -----2--  midi clock
// ------1-  midi input
// -------0  midi output

void cz101_state::port_c_w(uint8_t data)
{
	LOG("port_c_w: %02x\n", data);

	m_port_c = data;
	if (BIT(data, 3))
	{
		m_hd44780->e_w(BIT(~data, 7));
		m_hd44780->rw_w(BIT(data, 6));
		m_hd44780->rs_w(BIT(data, 5));
	}
	else
	{
		m_power = 0;
		m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
		m_hd44780->reset();
		m_upd933->reset();
		led_1_w(0xff);
		led_2_w(0xff);
		led_3_w(0xff);
		led_4_w(0xff);
	}
}

void cz101_state::machine_start()
{
	m_leds.resolve();
	m_led_env.resolve();
	m_led_tone.resolve();

	// register for save states
	save_item(NAME(m_power));
	save_item(NAME(m_port_b));
	save_item(NAME(m_port_c));
	save_item(NAME(m_midi_rx));
}

void cz101_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void cz101_state::cz101(machine_config &config)
{
	UPD7811(config, m_maincpu, 10_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &cz101_state::maincpu_map);
	m_maincpu->pa_in_cb().set(m_hd44780, FUNC(hd44780_device::db_r));
	m_maincpu->pa_out_cb().set(m_hd44780, FUNC(hd44780_device::db_w));
	m_maincpu->pb_in_cb().set_ioport("PB");
	m_maincpu->pb_out_cb().set(FUNC(cz101_state::port_b_w));
	m_maincpu->pc_out_cb().set(FUNC(cz101_state::port_c_w));
	m_maincpu->an1_func().set_ioport("AN1");
	m_maincpu->an2_func().set_ioport("AN2");

	CLOCK(config, "midi_clock", 2_MHz_XTAL).signal_handler().set(m_maincpu, FUNC(upd7810_device::sck_w));

	NVRAM(config, "nvram"); // backed by external battery when RAM cartridge is inserted
	CASIO_RA3(config, m_cart);

	midi_port_device& mdin(MIDI_PORT(config, "mdin", midiin_slot, "midiin"));
	mdin.rxd_handler().set([this] (int state) { m_midi_rx = state; });
	m_maincpu->rxd_func().set([this] () { return m_midi_rx; });

	MIDI_PORT(config, "mdout", midiout_slot, "midiout");
	m_maincpu->txd_func().set("mdout", FUNC(midi_port_device::write_txd));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(6*16 + 1, 19);
	screen.set_visarea_full();
	screen.set_screen_update("hd44780", FUNC(hd44780_device::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(cz101_state::cz101_palette), 3);

	HD44780(config, m_hd44780, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_hd44780->set_lcd_size(2, 16);
	m_hd44780->set_function_set_at_any_time();
	m_hd44780->set_pixel_update_cb(FUNC(cz101_state::lcd_pixel_update));

	config.set_default_layout(layout_cz101);

	SPEAKER(config, "speaker").front_center();

	UPD933(config, m_upd933, 8.96_MHz_XTAL / 2);
	m_upd933->irq_cb().set_inputline(m_maincpu, UPD7810_INTF1);
	m_upd933->add_route(0, "speaker", 1.0);
}


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

ROM_START( cz101 )
	ROM_REGION(0x1000, "maincpu", 0) // same internal ROM as RZ-1 and SZ-1, but disabled by mode pins
	ROM_LOAD("upd7811g-120.bin", 0x0000, 0x1000, CRC(597ac04a) SHA1(96451a764296eaa22aaad3cba121226dcba865f4))

	/*
	* according to a 1986 Casio service bulletin (T.N. #84 / 84A), three ROM revisions were released to fix MIDI bugs:
	* - "Version I":   HN613256PN-26 mask ROM
	* - "Version II":  HN613256PS-40 mask ROM (misprinted as N-40 in service bulletin)
	* - "Version III": uPD27C256D-20 EPROM
	*/
	ROM_REGION(0x8000, "program", 0)
	ROM_SYSTEM_BIOS(0, "v2", "Version II" )
	ROMX_LOAD("hn613256ps40.bin", 0x0000, 0x8000, CRC(c417bc57) SHA1(2aa5bfb76dc0a56797cf5dd547197816cedfa370), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v1", "Version I" )
	ROMX_LOAD("hn613256pn26.bin", 0x0000, 0x8000, CRC(e6c7780e) SHA1(52ff2c280392e104e84b05288c2b952fc29b50f4), ROM_BIOS(1))

	ROM_REGION(0x1000, "nvram", 0)
	ROM_LOAD("init_ram.bin", 0x0000, 0x1000, CRC(11010fa5) SHA1(a5f33408cc2852c8429b828849675d231c370831))
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME  FLAGS
CONS( 1984, cz101, 0,      0,      cz101,   cz101, cz101_state, empty_init, "Casio", "CZ-101", MACHINE_SUPPORTS_SAVE )



cz230s.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Devin Acker
/***************************************************************************
    Casio CZ-230S digital synthesizer and SZ-1 MIDI sequencer

    Misc. stuff:
    Both of these devices have a way of loading and running external code.
    - CZ-230S:
      Hold "portamento speed", "value up", and "value down" together on boot. This will cause the
      LCD to display "L-", as when loading from tape. At this point, the unit will try to load
      $700 bytes over MIDI/serial to address $3800 and then jump to it.
    - SZ-1:
      While not recording or playing, pressing the Rest + Dot + Triplet buttons at the same time will
      cause the firmware to check for a JMP instruction (54) at the first byte of cartridge memory
      ($e000), and execute it if there is one.

    TODO: auto power off. Even after activating this, both units still continue executing as normal
    (and the power switch itself is not connected to the CPU, unlike on the CZ-101/1000)

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

#include "emu.h"

#include "ra3.h"

#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/upd7810/upd7810.h"
#include "imagedev/cassette.h"
#include "machine/clock.h"
#include "machine/nvram.h"
#include "sound/beep.h"
#include "sound/upd933.h"
#include "sound/upd934g.h"
#include "video/mn1252.h"

#include "screen.h"
#include "speaker.h"

#include "cz230s.lh"
#include "sz1.lh"

namespace {

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

class cz230s_state : public driver_device
{
public:
	cz230s_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_lcdc(*this, "lcdc"),
		m_cassette(*this, "cassette"),
		m_pd(*this, "pd"),
		m_pcm(*this, "pcm"),
		m_keys(*this, "KC%u", 0U),
		m_lcd_seg(*this, "%u.%u", 0U, 0U),
		m_led(*this, "led%u.%u", 0U, 0U),
		m_rhythm(*this, "rhythm_pos"),
		m_mode(*this, "mode_pos")
	{ }

	void config_base(machine_config &config, u16 screen_w, u16 screen_h, bool midi_thru = true);
	void cz230s(machine_config &config);
	void sz1(machine_config &config);

	void keys_w(int state) { m_key_sel = state; }
	void keys_mux_w(int state) { m_key_mux = state; }
	template <int Row> ioport_value keys_row_r();
	template <int Row> u8 keys_analog_r();

	DECLARE_INPUT_CHANGED_MEMBER(rhythm_w);
	template <int Bit> ioport_value rhythm_r() { return m_rhythm >> Bit; }
	DECLARE_INPUT_CHANGED_MEMBER(mode_w);
	ioport_value mode_r() { return m_mode; }

	void cassette_w(int state);
	void cassette_motor_w(int state);
	ioport_value cassette_r();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void cz230s_map(address_map &map) ATTR_COLD;
	void cz230s_pcm_map(address_map &map) ATTR_COLD;
	void sz1_map(address_map &map) ATTR_COLD;

	void pcm_w(offs_t offset, u8 data);
	template <int Num> void led_w(u8 data);
	void port_a_w(u8 data);
	u8 keys_r();

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	required_device<upd7811_device> m_maincpu;
	required_device<mn1252_device> m_lcdc;
	required_device<cassette_image_device> m_cassette;
	optional_device<upd933_device> m_pd;
	optional_device<upd934g_device> m_pcm;

	optional_ioport_array<12> m_keys;

	output_finder<6, 9> m_lcd_seg;
	output_finder<2, 8> m_led;
	output_finder<> m_rhythm;
	output_finder<> m_mode;

	u8 m_port_a;
	u8 m_key_sel;
	u8 m_key_mux;
	u8 m_midi_rx;
};


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

void cz230s_state::cz230s_map(address_map &map)
{
	map.unmap_value_high();

//  map(0x0000, 0x0fff).rom(); - internal
	map(0x1000, 0x1fff).w(FUNC(cz230s_state::pcm_w));
	map(0x2000, 0x3fff).ram().share("nvram");
	map(0x4000, 0x7fff).rw(m_pd, FUNC(upd933_device::read), FUNC(upd933_device::write));
	map(0x8000, 0xffff).rom().region("program", 0);
}

/**************************************************************************/
void cz230s_state::cz230s_pcm_map(address_map &map)
{
	map(0x0000, 0x7fff).rom();
}

/**************************************************************************/
void cz230s_state::sz1_map(address_map &map)
{
	map.unmap_value_high();

//  map(0x0000, 0x0fff).rom(); - internal
	map(0x4000, 0x7fff).rom().region("program", 0);
	map(0x8000, 0x9fff).mirror(0x2000).ram().share("nvram");
	map(0xc000, 0xcfff).w(FUNC(cz230s_state::led_w<0>));
	map(0xd000, 0xdfff).w(FUNC(cz230s_state::led_w<1>));
	map(0xe000, 0xffff).rw("cart", FUNC(casio_ram_cart_device::read), FUNC(casio_ram_cart_device::write));
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( cz230s )
	PORT_START("KC0")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C2")
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C#2")
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D2")
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D#2")
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("E2")
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F2")
	PORT_BIT(0x1c0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Solo / Insert")
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Portamento On/Off")
	PORT_BIT(0x800, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Bend Range / Check")

	PORT_START("KC1")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F#2")
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G2")
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G#2")
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A2")
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A#2")
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("B2")
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Rhythm 1")
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Rhythm 2")
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Rhythm 3")
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Rhythm 4")
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Rhythm 5")
	PORT_BIT(0x800, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("Preset 1")

	PORT_START("KC2")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C3")
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C#3")
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D3")
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D#3")
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("E3")
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F3")
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Rhythm 6")
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Rhythm 7")
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Rhythm 8")
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_NAME("Rhythm 9")
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_NAME("Rhythm 10")
	PORT_BIT(0x800, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Preset 2")

	PORT_START("KC3")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F#3")
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G3")
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G#3")
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A3")
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A#3")
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("B3")
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_J) PORT_NAME("Program")
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Intro / Fill In")
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_LEFT)  PORT_NAME("Tempo Down")
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Tempo Up")
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Start / Stop / Record")
	PORT_BIT(0x800, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Synchro / Clear")

	PORT_START("KC4")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C4")
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C#4")
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D4")
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D#4")
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("E4")
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F4")
	PORT_BIT(0xfc0, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_CUSTOM_MEMBER(FUNC(cz230s_state::rhythm_r<0>))

	PORT_START("KC5")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F#4")
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G4")
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G#4")
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A4")
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A#4")
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("B4")
	PORT_BIT(0xfc0, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_CUSTOM_MEMBER(FUNC(cz230s_state::rhythm_r<6>))

	PORT_START("KC6")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C5")
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C#5")
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D5")
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("D#5")
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("E5")
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F5")
	PORT_BIT(0x3c0, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_CUSTOM_MEMBER(FUNC(cz230s_state::rhythm_r<12>))
	PORT_BIT(0xc00, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC7")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("F#5")
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G5")
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("G#5")
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A5")
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("A#5")
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("B5")
	PORT_BIT(0x7c0, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_CUSTOM_MEMBER(FUNC(cz230s_state::mode_r))
	PORT_BIT(0x800, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC8")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("C6")
	PORT_BIT(0x03e, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("MT")
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("MIDI Channel")
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Portamento Speed")
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Transpose")
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Value Down / Save")
	PORT_BIT(0x800, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_UP)   PORT_NAME("Value Up / Load")

	PORT_START("KC9")
	PORT_BIT(0x03f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Tone 4")
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Tone 5")
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Tone 6")
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Tone 7")
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Tone 8")
	PORT_BIT(0x800, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("Tone 9")

	PORT_START("KC10")
	PORT_BIT(0x03f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("Tone 0")
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Tone 1")
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Tone 2")
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Tone 3")
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME("Cancel")
	PORT_BIT(0x800, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC11")
	PORT_BIT(0x03f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tune Down")
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tune Up")
	PORT_BIT(0x100, 0x000, IPT_OTHER ) PORT_TOGGLE PORT_NAME("MIDI Clock")
	PORT_DIPSETTING(0x100, "External")
	PORT_DIPSETTING(0x000, "Internal")
	PORT_BIT(0x200, 0x000, IPT_OTHER ) PORT_TOGGLE PORT_NAME("MIDI")
	PORT_DIPSETTING(0x200, DEF_STR(Off)) // this should be on by default
	PORT_DIPSETTING(0x000, DEF_STR(On))
	PORT_BIT(0x400, IP_ACTIVE_LOW,  IPT_UNUSED)
	PORT_BIT(0x800, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_TOGGLE PORT_NAME("Auto Power Off")

	PORT_START("RHYTHM")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Rhythm Sound (BD)")    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::rhythm_w), 0x0001)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Rhythm Sound (SD)")    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::rhythm_w), 0x0002)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Rhythm Sound (LT)")    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::rhythm_w), 0x0004)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Rhythm Sound (HT)")    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::rhythm_w), 0x0008)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Rhythm Sound (LB)")    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::rhythm_w), 0x0010)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Rhythm Sound (HB)")    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::rhythm_w), 0x0020)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Rhythm Sound (CH)")    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::rhythm_w), 0x0040)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Rhythm Sound (Rim)")   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::rhythm_w), 0x0080)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Rhythm Sound (OH)")    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::rhythm_w), 0x0100)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Rhythm Sound (CB)")    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::rhythm_w), 0x0200)
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Rhythm Sound (Ride)")  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::rhythm_w), 0x0400)
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Rhythm Sound (Claps)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::rhythm_w), 0x0800)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Rhythm Sound (PD 1)")  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::rhythm_w), 0x1000)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Rhythm Sound (PD 2)")  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::rhythm_w), 0x2000)
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Rhythm Sound (PD 3)")  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::rhythm_w), 0x4000)
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Rhythm Sound (PD 4)")  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::rhythm_w), 0x8000)

	PORT_START("MODE")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Mode (Pattern Play)")       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::mode_w), 0x01)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Mode (Pattern Memory 4/4)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::mode_w), 0x02)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Mode (Pattern Memory 3/4)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::mode_w), 0x04)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Mode (Song Play)")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::mode_w), 0x08)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Mode (Song Memory)")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cz230s_state::mode_w), 0x10)

	PORT_START("PB")
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_OUTPUT) PORT_WRITE_LINE_MEMBER(FUNC(cz230s_state::keys_w))
	PORT_BIT(0x10, IP_ACTIVE_LOW,  IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("pd", FUNC(upd933_device::rq_r))
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OUTPUT) PORT_WRITE_LINE_DEVICE_MEMBER("pd", FUNC(upd933_device::cs_w))
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OUTPUT) // TODO: auto power off

	PORT_START("PC")
	PORT_BIT(0x07, IP_ACTIVE_HIGH, IPT_UNUSED) // MIDI in/out/clock
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_CUSTOM_MEMBER(FUNC(cz230s_state::cassette_r))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OUTPUT) PORT_WRITE_LINE_MEMBER(FUNC(cz230s_state::cassette_w))
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OUTPUT) PORT_WRITE_LINE_MEMBER(FUNC(cz230s_state::cassette_motor_w))
	PORT_BIT(0xc0, IP_ACTIVE_LOW,  IPT_OUTPUT) PORT_WRITE_LINE_MEMBER(FUNC(cz230s_state::keys_mux_w))

	PORT_START("AN1")
	PORT_BIT(0xff, 0x7f, IPT_PADDLE) PORT_NAME("Pitch Wheel") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)

	PORT_START("AN2")
	PORT_CONFNAME(0xff, 0xff, "Battery Level")
	PORT_CONFSETTING(   0x00, "Low")
	PORT_CONFSETTING(   0xff, "Normal")

	PORT_START("AN3")
	PORT_BIT(0xff, 0xff, IPT_POSITIONAL_H) PORT_NAME("PD Rhythm Volume") PORT_REVERSE PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_CENTERDELTA(0) PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH) PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH)

INPUT_PORTS_END

static INPUT_PORTS_START( sz1 )
	PORT_START("KC0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("Rest")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Dot")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Triplet")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Tie")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_LEFT)  PORT_NAME("Reverse / Save")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Forward / Load")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("Play / Check")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Stop")

	PORT_START("KC1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("8th Note")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("16th Note")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("32nd Note")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_DOWN ) PORT_NAME("Tempo Down")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Real Time")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Manual")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Record")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Reset")

	PORT_START("KC2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Quarter Note")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Half Note")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("Whole Note")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_UP) PORT_NAME("Tempo Up")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Track 1")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Track 2")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Track 3")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Track 4")

	PORT_START("KC3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Copy")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_INSERT) PORT_NAME("Insert")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL)    PORT_NAME("Delete")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER)  PORT_NAME("Metronome / Enter")
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KC4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Repeat")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("MIDI")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_TOGGLE PORT_NAME("Auto Power Off")
	PORT_BIT(0x20, 0x00, IPT_OTHER ) PORT_TOGGLE PORT_NAME("MIDI Clock")
	PORT_DIPSETTING(0x20, "External")
	PORT_DIPSETTING(0x00, "Internal")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER ) PORT_TOGGLE PORT_NAME("Touch Data")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Cartridge / MT")

	PORT_START("PA")
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_OUTPUT) PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(mn1252_device::data_w))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OUTPUT) PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(mn1252_device::ce_w))
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OUTPUT) PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(mn1252_device::std_w))
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_CUSTOM_MEMBER(FUNC(cz230s_state::keys_row_r<7>))

	PORT_START("PB")
	PORT_BIT(0x1f, IP_ACTIVE_LOW,  IPT_OUTPUT) PORT_WRITE_LINE_MEMBER(FUNC(cz230s_state::keys_w))
	PORT_BIT(0x60, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OUTPUT) // TODO: auto power off

	PORT_START("PC")
	PORT_BIT(0x07, IP_ACTIVE_HIGH, IPT_UNUSED) // MIDI in/out/clock
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_CUSTOM_MEMBER(FUNC(cz230s_state::cassette_r))
	PORT_BIT(0x10, IP_ACTIVE_LOW,  IPT_OUTPUT) PORT_WRITE_LINE_DEVICE_MEMBER("beep", FUNC(beep_device::set_state))
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OUTPUT) PORT_WRITE_LINE_MEMBER(FUNC(cz230s_state::cassette_motor_w))
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OUTPUT) PORT_WRITE_LINE_MEMBER(FUNC(cz230s_state::cassette_w))
	PORT_BIT(0x80, IP_ACTIVE_LOW,  IPT_OTHER ) PORT_NAME("Foot Switch")
INPUT_PORTS_END


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

void cz230s_state::machine_start()
{
	m_lcd_seg.resolve();
	m_led.resolve();
	m_rhythm.resolve();
	m_mode.resolve();

	m_rhythm = 1;
	m_mode = 1;

	m_port_a = 0;

	save_item(NAME(m_port_a));
	save_item(NAME(m_key_sel));
	save_item(NAME(m_key_mux));
	save_item(NAME(m_midi_rx));
}

/**************************************************************************/
void cz230s_state::machine_reset()
{
	m_key_sel = m_key_mux = 0;
	m_midi_rx = 1;
}


/**************************************************************************/
void cz230s_state::pcm_w(offs_t offset, u8 data)
{
	data = (BIT(offset, 0, 6) << 2) | BIT(offset, 8, 2);
	m_pcm->write(offset >> 10, data);
}

/**************************************************************************/
template <int Num>
void cz230s_state::led_w(u8 data)
{
	for (int i = 0; i < 8; i++)
		m_led[Num][i] = BIT(data, i);
}

/**************************************************************************/
void cz230s_state::port_a_w(u8 data)
{
	m_lcdc->data_w(data & 0xf);
	m_lcdc->std_w(BIT(data, 5));
	m_lcdc->ce_w(BIT(data, 6));

	if (BIT(data, 7) && !BIT(m_port_a, 7))
		led_w<0>(~data & 0x3f);

	m_port_a = data;
}

/**************************************************************************/
u8 cz230s_state::keys_r()
{
	u8 data = 0x3f;

	if (m_key_sel < m_keys.size())
	{
		const u16 input = m_keys[m_key_sel].read_safe(0xfff);
		if (BIT(m_key_mux, 0))
			data &= (input & 0x3f);
		if (BIT(m_key_mux, 1))
			data &= (input >> 6);
	}

	return data;
}

/**************************************************************************/
template <int Row>
ioport_value cz230s_state::keys_row_r()
{
	u8 data = 0xff;

	for (int i = 0; i < 5; i++)
		if (BIT(m_key_sel, i))
			data &= m_keys[i].read_safe(0xff);

	return BIT(data, Row);
}

/**************************************************************************/
template <int Row>
u8 cz230s_state::keys_analog_r()
{
	return keys_row_r<Row>() ? 0xff : 0x00;
}

/**************************************************************************/
INPUT_CHANGED_MEMBER(cz230s_state::rhythm_w)
{
	if (!oldval && newval)
		m_rhythm = param;
}

/**************************************************************************/
INPUT_CHANGED_MEMBER(cz230s_state::mode_w)
{
	if (!oldval && newval)
		m_mode = param;
}

/**************************************************************************/
void cz230s_state::cassette_w(int state)
{
	m_cassette->output(state ? -1.0 : 1.0);
}

/**************************************************************************/
void cz230s_state::cassette_motor_w(int state)
{
	m_cassette->change_state(state ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
}

/**************************************************************************/
ioport_value cz230s_state::cassette_r()
{
	return m_cassette->input() > 0 ? 0 : 1;
}

/**************************************************************************/
u32 cz230s_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	for (int digit = 0; digit < 6; digit++)
	{
		const u16 data = m_lcdc->output(digit);
		for (int seg = 0; seg < 9; seg++)
			m_lcd_seg[digit][seg] = BIT(data, seg);
	}

	return 0;
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void cz230s_state::config_base(machine_config &config, u16 screen_w, u16 screen_h, bool midi_thru)
{
	UPD7811(config, m_maincpu, 10_MHz_XTAL);

	CLOCK(config, "midi_clock", 2_MHz_XTAL).signal_handler().set(m_maincpu, FUNC(upd7810_device::sck_w));

	midi_port_device &mdin(MIDI_PORT(config, "mdin", midiin_slot, "midiin"));
	mdin.rxd_handler().set([this](int state) { m_midi_rx = state; });
	m_maincpu->rxd_func().set([this]() { return m_midi_rx; });

	MIDI_PORT(config, "mdout", midiout_slot, "midiout");
	m_maincpu->txd_func().set("mdout", FUNC(midi_port_device::write_txd));

	if (midi_thru)
	{
		MIDI_PORT(config, "mdthru", midiout_slot, "midiout");
		mdin.rxd_handler().append("mdthru", FUNC(midi_port_device::write_txd));
	}

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	MN1252(config, m_lcdc);

	auto &screen = SCREEN(config, "screen", SCREEN_TYPE_SVG);
	screen.set_refresh_hz(60);
	screen.set_size(screen_w, screen_h);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(cz230s_state::screen_update));

	SPEAKER(config, "speaker").front_center();

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "speaker", 0.05);
}

/**************************************************************************/
void cz230s_state::cz230s(machine_config &config)
{
	config_base(config, 975, 205);

	m_maincpu->set_addrmap(AS_PROGRAM, &cz230s_state::cz230s_map);
	m_maincpu->pa_in_cb().set(FUNC(cz230s_state::keys_r));
	m_maincpu->pa_out_cb().set(FUNC(cz230s_state::port_a_w));
	m_maincpu->pb_in_cb().set_ioport("PB");
	m_maincpu->pb_out_cb().set_ioport("PB");
	m_maincpu->pc_in_cb().set_ioport("PC");
	m_maincpu->pc_out_cb().set_ioport("PC");
	m_maincpu->an1_func().set_ioport("AN1");
	m_maincpu->an2_func().set_ioport("AN2");
	m_maincpu->an3_func().set_ioport("AN3");

	UPD933(config, m_pd, 8.96_MHz_XTAL / 2);
	m_pd->irq_cb().set_inputline(m_maincpu, UPD7810_INTF1);
	m_pd->add_route(0, "speaker", 1.0);

	UPD934G(config, m_pcm, 1'280'000);
	m_pcm->set_addrmap(0, &cz230s_state::cz230s_pcm_map);
	m_pcm->add_route(ALL_OUTPUTS, "speaker", 0.5);

	config.set_default_layout(layout_cz230s);
}

/**************************************************************************/
void cz230s_state::sz1(machine_config &config)
{
	config_base(config, 938, 205, false);

	m_maincpu->set_addrmap(AS_PROGRAM, &cz230s_state::sz1_map);
	m_maincpu->pa_in_cb().set_ioport("PA");
	m_maincpu->pa_out_cb().set_ioport("PA");
	m_maincpu->pb_out_cb().set_ioport("PB");
	m_maincpu->pc_in_cb().set_ioport("PC");
	m_maincpu->pc_out_cb().set_ioport("PC");
	m_maincpu->an0_func().set(FUNC(cz230s_state::keys_analog_r<0>));
	m_maincpu->an1_func().set(FUNC(cz230s_state::keys_analog_r<1>));
	m_maincpu->an2_func().set(FUNC(cz230s_state::keys_analog_r<2>));
	m_maincpu->an3_func().set(FUNC(cz230s_state::keys_analog_r<3>));
	m_maincpu->an4_func().set(FUNC(cz230s_state::keys_analog_r<4>));
	m_maincpu->an5_func().set(FUNC(cz230s_state::keys_analog_r<5>));
	m_maincpu->an6_func().set(FUNC(cz230s_state::keys_analog_r<6>));

	CASIO_RA5(config, "cart");

	BEEP(config, "beep", 2000).add_route(ALL_OUTPUTS, "speaker", 0.5); // TODO: verify freq

	config.set_default_layout(layout_sz1);
}


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

ROM_START( cz230s )
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("upd7811g-301.bin", 0x0000, 0x1000, CRC(506b008c) SHA1(2d91d817bd0fa4688591160e53cbc6e14acd7014))

	ROM_REGION(0x8000, "program", 0)
	ROM_LOAD("hn613256pda4.bin", 0x0000, 0x8000, CRC(f58758ec) SHA1(11e5c95e51e1c77c89682ea3db85b9457f8b6cf6))

	ROM_REGION(0x8000, "pcm", 0)
	ROM_LOAD("hn613256pct1.bin", 0x0000, 0x8000, CRC(97b9805b) SHA1(f3502a26b6a9bccb60bea11ae940619ab9960e05))

	ROM_REGION(0x2000, "nvram", 0)
	ROM_LOAD("init_ram.bin", 0x0000, 0x2000, CRC(eb756425) SHA1(3a21b45269a00d27d5943de50825edc329062c60))

	ROM_REGION(0x7bb5, "screen", 0)
	ROM_LOAD("cz230s.svg", 0x0000, 0x7bb5, CRC(e35cc3d3) SHA1(36cb369414f1e65843cd0ea318ad27f536b582be))
ROM_END

ROM_START( sz1 )
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("upd7811g-120.bin", 0x0000, 0x1000, CRC(597ac04a) SHA1(96451a764296eaa22aaad3cba121226dcba865f4))

	ROM_REGION(0x4000, "program", 0)
	ROM_LOAD("program.bin", 0x0000, 0x4000, CRC(15f83fa5) SHA1(cb0d8d8390266f247dc7718b95bc658d1719d105))

	ROM_REGION(0x6437, "screen", 0)
	ROM_LOAD("sz1.svg", 0x0000, 0x6437, CRC(fd14625b) SHA1(069790868b382725d309fcab0148147f76ff82cc))
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY  FULLNAME   FLAGS
SYST( 1985, cz230s,  0,      0,      cz230s,  cz230s, cz230s_state, empty_init, "Casio", "CZ-230S", MACHINE_SUPPORTS_SAVE )
SYST( 1985, sz1,     0,      0,      sz1,     sz1,    cz230s_state, empty_init, "Casio", "SZ-1",    MACHINE_SUPPORTS_SAVE )



d400.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************************

Skeleton driver for Data General Dasher 400 series terminals.

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

#include "emu.h"
#include "cpu/m6809/m6809.h"
#include "machine/mc68681.h"
#include "machine/x2212.h"
#include "video/crt9007.h"
#include "screen.h"


namespace {

class d400_state : public driver_device
{
public:
	d400_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_novram(*this, "novram")
	{ }

	void d461(machine_config &config);
private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	u8 novram_recall_r();
	u8 novram_store_r();
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<x2210_device> m_novram;
};

u32 d400_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

u8 d400_state::novram_recall_r()
{
	if (!machine().side_effects_disabled())
	{
		m_novram->recall(1);
		m_novram->recall(0);
	}
	return 0xff;
}

u8 d400_state::novram_store_r()
{
	if (!machine().side_effects_disabled())
	{
		m_novram->store(1);
		m_novram->store(0);
	}
	return 0xff;
}

void d400_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).ram();
	map(0x4000, 0x403f).rw("vpac", FUNC(crt9007_device::read), FUNC(crt9007_device::write));
	map(0x4800, 0x48ff).ram();
	map(0x5000, 0x50ff).ram();
	map(0x6000, 0x6fff).ram();
	map(0x7800, 0x780f).rw("duart", FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0x7880, 0x78bf).rw("novram", FUNC(x2210_device::read), FUNC(x2210_device::write));
	map(0x7900, 0x7900).r(FUNC(d400_state::novram_recall_r));
	map(0x7980, 0x7980).r(FUNC(d400_state::novram_store_r));
	map(0x7c00, 0x7c00).nopw();
	map(0x8000, 0xffff).rom().region("maincpu", 0);
}

static INPUT_PORTS_START(d461)
INPUT_PORTS_END

void d400_state::d461(machine_config &config)
{
	MC6809E(config, m_maincpu, 59.292_MHz_XTAL / 30); // HD68B09EP (clock not verified)
	m_maincpu->set_addrmap(AS_PROGRAM, &d400_state::mem_map);

	X2210(config, "novram");

	scn2681_device &duart(SCN2681(config, "duart", 3.6864_MHz_XTAL));
	duart.irq_cb().set_inputline(m_maincpu, M6809_FIRQ_LINE);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(59.292_MHz_XTAL / 3, 1080, 0, 810, 305, 0, 300); // yes, 81 columns
	//screen.set_raw(59.292_MHz_XTAL / 2, 1620, 0, 1215, 305, 0, 300); // for 135-column mode
	screen.set_screen_update(FUNC(d400_state::screen_update));

	crt9007_device &vpac(CRT9007(config, "vpac", 59.292_MHz_XTAL / 30));
	vpac.set_screen("screen");
	vpac.set_character_width(10); // 9 in 135-column mode
	vpac.int_callback().set_inputline(m_maincpu, M6809_IRQ_LINE);
}



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

Data General D461.
Chips: SCN2681A, X2210P, 2x HM6116P-2, 2x HM6264P-20, HD68B09EP, CRT9007, 1x 8-sw dip.
Crystals: 3.6864, 59.2920

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

ROM_START( d461 )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD( "dgc_100_5776-05.bin", 0x0000, 0x8000, CRC(fdce2132) SHA1(82eac1751c31f99d4490505e16af5e7e7a52b310) )
ROM_END

} // anonymous namespace


COMP( 1986, d461, 0, 0, d461, d461, d400_state, empty_init, "Data General", "Dasher D461", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



d6800.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/********************************************************************************

    The Dream 6800 is a CHIP-8 computer roughly modelled on the Cosmac VIP.
    It was described in Electronics Australia magazine in 4 articles starting
    in May 1979. It has 1k of ROM and 1k of RAM. The video consists of 64x32
    pixels. The keyboard is a hexcode 4x4 matrix, plus a Function key.

    Designed by Michael Bauer, of the Division of Computing and Mathematics
    at Deakin University, Australia.

    NOTE that the display only updates after each 4 digits is entered, and
    you can't see what you type as you change bytes. This is by design.

    The cassette has no checksum, header or blocks. It is simply a stream
    of pulses. The successful loading of a tape is therefore a matter of luck.

    To modify memory, press RST, then enter the 4-digit address (nothing happens
    until the 4th digit is pressed), then press FN, then 0, then the 2 digit data.
    It will enter the data (you won't see anything), then the address will increment.
    Enter the data for this new address. If you want to skip this address, press FN.
    When you're done, press RST. NOTE!!! Do NOT change any of these addresses:
    0000,0001,0006-007F, or the system may crash. It's recommended to start all
    your programs at 0200.

    Function keys:
    FN 0 - Modify memory - see above paragraph.

    FN 1 - Tape load. You must have entered the start address at 0002, and
           the end address+1 at 0004 (big-endian).

    FN 2 - Tape save. You must have entered the start address at 0002, and
           the end address+1 at 0004 (big-endian).

    FN 3 - Run. To use, press RST, then enter the 4-digit start address
           (nothing happens until the 4th digit is pressed), then FN, then 3.

    All CHIP-8 programs load at 0200 (max size 4k), and exec address
    is C000.

    Information and programs can be found at http://chip8.com/?page=78

    Due to the way the keyboard is scanned, Natural Keyboard/Paste is not possible.

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


#include "emu.h"
#include "cpu/m6800/m6800.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "machine/6821pia.h"
#include "machine/timer.h"
#include "sound/beep.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class d6800_state : public driver_device
{
public:
	d6800_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cass(*this, "cassette")
		, m_pia(*this, "pia")
		, m_beeper(*this, "beeper")
		, m_videoram(*this, "videoram")
		, m_io_keyboard(*this, "X%u", 0U)
		, m_io_shift(*this, "SHIFT")
		{ }


	void d6800(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(reset_button);

private:
	uint8_t d6800_cassette_r();
	void d6800_cassette_w(uint8_t data);
	uint8_t d6800_keyboard_r();
	void d6800_keyboard_w(uint8_t data);
	void d6800_screen_w(int state);
	uint32_t screen_update_d6800(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	INTERRUPT_GEN_MEMBER(rtc_interrupt);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_w);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);

	void d6800_map(address_map &map) ATTR_COLD;

	bool m_cb2 = 0;
	bool m_cassold = 0;
	uint8_t m_cass_data[4]{};
	uint8_t m_portb = 0U;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_device<cassette_image_device> m_cass;
	required_device<pia6821_device> m_pia;
	required_device<beep_device> m_beeper;
	required_shared_ptr<uint8_t> m_videoram;
	required_ioport_array<4> m_io_keyboard;
	required_ioport m_io_shift;
};


/* Memory Maps */

void d6800_state::d6800_map(address_map &map)
{
	map(0x0000, 0x00ff).ram();
	map(0x0100, 0x01ff).ram().share("videoram");
	map(0x0200, 0x17ff).ram();
	//map(0x1800, 0x1fff).rom().region("maincpu",0x800);   // for dreamsoft_1, if we can find a good copy
	map(0x8010, 0x8013).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xc000, 0xc7ff).mirror(0x3800).rom().region("maincpu",0);
}

/* Input Ports */

static INPUT_PORTS_START( d6800 )
	PORT_START("X0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_NAME("0") // ee
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_NAME("1") // ed
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_UP) PORT_NAME("2") // eb
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_NAME("3") // e7
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_LEFT) PORT_NAME("4") // de
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_NAME("5") // dd
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("6") // db
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_NAME("7") // d7
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_DOWN) PORT_NAME("8") // be
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_NAME("9") // bd
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_NAME("A") // bb
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_NAME("B") // b7
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_NAME("C") // 7e
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_NAME("D") // 7d
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_NAME("E") // 7b
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_NAME("F") // 77
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("SHIFT")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("FN") PORT_CODE(KEYCODE_LSHIFT)

	PORT_START("RESET")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RST") PORT_CODE(KEYCODE_LALT) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(d6800_state::reset_button), 0)

	PORT_START("VS")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("screen", FUNC(screen_device::vblank))
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(d6800_state::reset_button)
{
	// RESET button wired to POR on the mc6875, which activates the Reset output pin which in turn connects to the CPU's Reset pin.
	if (newval)
		m_pia->reset();
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}

/* Video */
uint32_t d6800_state::screen_update_d6800(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint8_t gfx=0;

	for (uint8_t y = 0; y < 32; y++)
	{
		uint16_t *p = &bitmap.pix(y);

		for (uint8_t x = 0; x < 8; x++)
		{
			if (m_cb2)
				gfx = m_videoram[x | (y<<3)];

			*p++ = BIT(gfx, 7);
			*p++ = BIT(gfx, 6);
			*p++ = BIT(gfx, 5);
			*p++ = BIT(gfx, 4);
			*p++ = BIT(gfx, 3);
			*p++ = BIT(gfx, 2);
			*p++ = BIT(gfx, 1);
			*p++ = BIT(gfx, 0);
		}
	}
	return 0;
}

/* NE556 */

TIMER_DEVICE_CALLBACK_MEMBER(d6800_state::kansas_w)
{
	m_cass_data[3]++;

	if (BIT(m_portb, 0) != m_cassold)
	{
		m_cass_data[3] = 0;
		m_cassold = BIT(m_portb, 0);
	}

	if (BIT(m_portb, 0))
		m_cass->output(BIT(m_cass_data[3], 0) ? -1.0 : +1.0); // 2400Hz
	else
		m_cass->output(BIT(m_cass_data[3], 1) ? -1.0 : +1.0); // 1200Hz
}

/* PIA6821 Interface */

TIMER_DEVICE_CALLBACK_MEMBER(d6800_state::kansas_r)
{
	uint8_t data = m_io_keyboard[0]->read() & m_io_keyboard[1]->read() & m_io_keyboard[2]->read() & m_io_keyboard[3]->read();
	int ca1 = (data == 255) ? 0 : 1;
	int ca2 = m_io_shift->read();

	m_pia->ca1_w(ca1);
	m_pia->ca2_w(ca2);

	/* cassette - turn 1200/2400Hz to a bit */
	m_cass_data[1]++;
	uint8_t cass_ws = (m_cass->input() > +0.03) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		m_cass_data[2] = ((m_cass_data[1] < 12) ? 128 : 0);
		m_cass_data[1] = 0;
	}
}


void d6800_state::d6800_screen_w(int state)
{
	m_cb2 = state;
	m_maincpu->set_unscaled_clock(state ? 589744 : 1e6); // effective clock is ~590kHz while screen is on
}

uint8_t d6800_state::d6800_cassette_r()
{
	/*
	Cassette circuit consists of a 741 op-amp, a 74121 oneshot, and a 74LS74.
	When a pulse arrives, the oneshot is set. After a preset time, it triggers
	and the 74LS74 compares this pulse to the output of the 741. Therefore it
	knows if the tone is 1200 or 2400 Hz. Input to PIA is bit 7.
	*/

	return m_cass_data[2] | m_portb;
}

void d6800_state::d6800_cassette_w(uint8_t data)
{
	/*
	    A NE556 runs at either 1200 or 2400 Hz, depending on the state of bit 0.
	    This output drives the speaker and the output signal to the cassette player.
	    Bit 6 enables the speaker. Also the speaker is silenced when cassette operations
	    are in progress (DMA/CB2 line low).
	*/

	m_beeper->set_clock(BIT(data, 0) ? 2400 : 1200);
	m_beeper->set_state(BIT(data, 6) & (m_cb2 ? 1 : 0));

	m_portb = data & 0x7f;
}

uint8_t d6800_state::d6800_keyboard_r()
{
	/*
	This system reads the key matrix one way, then swaps the input and output
	lines around and reads it another way. This isolates the key that was pressed.
	*/

	u8 y = 8;
	for (u8 i = 0; i < 4; i++)
	{
		u8 data = m_io_keyboard[i]->read() & 15;
		y <<= 1;
		if (data < 15)
			for (u8 j = 0; j < 4; j++)
				if (!BIT(data, j))
					return data | (0xf0 - y);
	}

	return 0xff;
}

void d6800_state::d6800_keyboard_w(uint8_t data)
{
	/*

	    bit     description

	    PA0     keyboard column 0
	    PA1     keyboard column 1
	    PA2     keyboard column 2
	    PA3     keyboard column 3
	    PA4     keyboard row 0
	    PA5     keyboard row 1
	    PA6     keyboard row 2
	    PA7     keyboard row 3

	*/

}

INTERRUPT_GEN_MEMBER(d6800_state::rtc_interrupt)
{
	m_pia->cb1_w(1);
	m_pia->cb1_w(0);
}

/* Machine Initialization */

void d6800_state::machine_start()
{
	save_item(NAME(m_cb2));
	save_item(NAME(m_cassold));
	save_item(NAME(m_cass_data));
	save_item(NAME(m_portb));
}

void d6800_state::machine_reset()
{
	m_beeper->set_state(0);
	m_cass_data[0] = 0;
	m_cass_data[1] = 0;
	m_cass_data[2] = 128;
	m_cass_data[3] = 0;
}

/* Machine Drivers */

QUICKLOAD_LOAD_MEMBER(d6800_state::quickload_cb)
{
	constexpr u16 QUICK_ADDR = 0x200;

	u32 const quick_length = image.length();
	if (quick_length > 0xe00)
		return std::make_pair(image_error::INVALIDIMAGE, "File exceeds 3584 bytes");

	address_space &space = m_maincpu->space(AS_PROGRAM);
	for (u32 i = 0; i < quick_length; i++)
	{
		u8 ch;
		image.fread(&ch, 1);
		space.write_byte(i + QUICK_ADDR, ch);
	}

	u16 exec_addr = 0xc000;
	if (image.is_filetype("bin"))
		exec_addr = QUICK_ADDR;

	// display a message about the loaded quickload
	image.message(" Quickload: size=%04X : start=%04X : end=%04X : exec=%04X", quick_length, QUICK_ADDR, QUICK_ADDR+quick_length, exec_addr);

	// Start the quickload
	m_maincpu->set_pc(exec_addr);

	return std::make_pair(std::error_condition(), std::string());
}

void d6800_state::d6800(machine_config &config)
{
	/* basic machine hardware */
	M6800(config, m_maincpu, XTAL(4'000'000)/4);
	m_maincpu->set_addrmap(AS_PROGRAM, &d6800_state::d6800_map);
	m_maincpu->set_vblank_int("screen", FUNC(d6800_state::rtc_interrupt));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_size(64, 32);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(d6800_state::screen_update_d6800));
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(300)); // verified
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, "beeper", 1200).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* devices */
	PIA6821(config, m_pia);
	m_pia->readpa_handler().set(FUNC(d6800_state::d6800_keyboard_r));
	m_pia->readpb_handler().set(FUNC(d6800_state::d6800_cassette_r));
	m_pia->writepa_handler().set(FUNC(d6800_state::d6800_keyboard_w));
	m_pia->writepb_handler().set(FUNC(d6800_state::d6800_cassette_w));
	m_pia->cb2_handler().set(FUNC(d6800_state::d6800_screen_w));
	m_pia->irqa_handler().set_inputline("maincpu", M6800_IRQ_LINE);
	m_pia->irqb_handler().set_inputline("maincpu", M6800_IRQ_LINE);

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	TIMER(config, "kansas_w").configure_periodic(FUNC(d6800_state::kansas_w), attotime::from_hz(4800));
	TIMER(config, "kansas_r").configure_periodic(FUNC(d6800_state::kansas_r), attotime::from_hz(40000));

	/* quickload */
	quickload_image_device &quickload(QUICKLOAD(config, "quickload", "bin,c8", attotime::from_seconds(2)));
	quickload.set_load_callback(FUNC(d6800_state::quickload_cb));
	quickload.set_interface("chip8quik");
	SOFTWARE_LIST(config, "quik_list").set_original("chip8_quik").set_filter("D");
}

/* ROMs */

ROM_START( d6800 )
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "0", "Original")   // "chipos"
	ROMX_LOAD( "d6800.bin",     0x0000, 0x0400, CRC(3f97ca2e) SHA1(60f26e57a058262b30befceceab4363a5d65d877), ROM_BIOS(0) )
	ROM_RELOAD(                 0x0400, 0x0400 )
	//ROMX_LOAD( "d6800d1.bin",   0x0800, 0x0800, BAD_DUMP CRC(e552cae3) SHA1(0b90504922d46b9c46278924768c45b1b276709f), ROM_BIOS(0) )   // need a good dump, this one is broken
	ROM_SYSTEM_BIOS(1, "d2", "Dreamsoft2")
	ROMX_LOAD( "d6800d2.bin",   0x0000, 0x0800, CRC(ded5712f) SHA1(f594f313a74d7135c9fdd0bcb0093fc5771a9b7d), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS(2, "d2m", "Dreamsoft2m")
	ROMX_LOAD( "d6800d2m.bin",  0x0000, 0x0800, CRC(eec8e56f) SHA1(f587ccbc0872f2982d61120d033f481a862b902b), ROM_BIOS(2) )
ROM_END

} // anonymous namespace


//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY          FULLNAME      FLAGS
COMP( 1979, d6800, 0,      0,      d6800,   d6800, d6800_state, empty_init, "Michael Bauer", "Dream 6800", MACHINE_SUPPORTS_SAVE )



d6809.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/*******************************************************************************

Dunfield 6809 Portable

2009-05-12 Skeleton driver.
2011-09-21 connected to terminal, notes added [Robbbert]

Chips used (planned?):
- 6809E CPU
- 6845 CRTC
- 6840 CTC
- 6551 ACIA Console
- 6551 ACIA Aux
- 6850 ACIA Unknown purpose
- uPD765 FDC
- 2764 8K ROM for CPU
- 2732 4K ROM for Chargen (not dumped)
- 6x 6264 RAM
- 3x 5516 RAM
- XTALs: 14.745MHz, 16MHz

So much for the official documentation.

In practice, it reads/writes to a terminal, and doesn't use most of the other
devices.

'maincpu' (F9DD): unmapped program memory write to 00F0 = 05 & FF
'maincpu' (F9E3): unmapped program memory read from 0001 & FF <----- these 2 are CLR 0001
'maincpu' (F9E3): unmapped program memory write to 0001 = 00 & FF
'maincpu' (F9E6): unmapped program memory read from 0005 & FF <----- these 2 are CLR 0005
'maincpu' (F9E6): unmapped program memory write to 0005 = 00 & FF
'maincpu' (F9E9): unmapped program memory write to 0002 = 0B & FF <-- these 2 are STD 0002
'maincpu' (F9E9): unmapped program memory write to 0003 = 1E & FF
'maincpu' (F9EC): unmapped program memory write to 0006 = 0B & FF <-- these 2 are STD 0006
'maincpu' (F9EC): unmapped program memory write to 0007 = 1E & FF
'maincpu' (FA4D): unmapped program memory write to 00F2 = 00 & FF <-- the remainder seems to be disk related
'maincpu' (FA52): unmapped program memory write to 00F3 = 00 & FF
'maincpu' (FA57): unmapped program memory write to 00F4 = 00 & FF
'maincpu' (FA5C): unmapped program memory write to 00F5 = 00 & FF
'maincpu' (FA61): unmapped program memory write to 00F6 = 02 & FF
'maincpu' (FA66): unmapped program memory write to 00F7 = 09 & FF
'maincpu' (FA6B): unmapped program memory write to 00F0 = 01 & FF
'maincpu' (FA6E): unmapped program memory read from 00F0 & FF
'maincpu' (FA4D): unmapped program memory write to 00F2 = 00 & FF
'maincpu' (FA52): unmapped program memory write to 00F3 = 00 & FF
'maincpu' (FA57): unmapped program memory write to 00F4 = 00 & FF
'maincpu' (FA5C): unmapped program memory write to 00F5 = 00 & FF
'maincpu' (FA61): unmapped program memory write to 00F6 = 02 & FF
'maincpu' (FA66): unmapped program memory write to 00F7 = 09 & FF
'maincpu' (FA6B): unmapped program memory write to 00F0 = 01 & FF
'maincpu' (FA6E): unmapped program memory read from 00F0 & FF
'maincpu' (FA4D): unmapped program memory write to 00F2 = 00 & FF
'maincpu' (FA52): unmapped program memory write to 00F3 = 00 & FF
'maincpu' (FA57): unmapped program memory write to 00F4 = 00 & FF
'maincpu' (FA5C): unmapped program memory write to 00F5 = 00 & FF
'maincpu' (FA61): unmapped program memory write to 00F6 = 02 & FF
'maincpu' (FA66): unmapped program memory write to 00F7 = 09 & FF
'maincpu' (FA6B): unmapped program memory write to 00F0 = 01 & FF
'maincpu' (FA6E): unmapped program memory read from 00F0 & FF
'maincpu' (FA41): unmapped program memory write to 00F2 = 00 & FF
'maincpu' (FA46): unmapped program memory write to 00F0 = 04 & FF
'maincpu' (FA82): unmapped program memory read from 00F0 & FF
'maincpu' (FA4D): unmapped program memory write to 00F2 = 00 & FF
'maincpu' (FA52): unmapped program memory write to 00F3 = 00 & FF
'maincpu' (FA57): unmapped program memory write to 00F4 = 00 & FF
'maincpu' (FA5C): unmapped program memory write to 00F5 = 00 & FF
'maincpu' (FA61): unmapped program memory write to 00F6 = 02 & FF
'maincpu' (FA66): unmapped program memory write to 00F7 = 09 & FF
'maincpu' (FA6B): unmapped program memory write to 00F0 = 01 & FF
'maincpu' (FA6E): unmapped program memory read from 00F0 & FF
'maincpu' (FA4D): unmapped program memory write to 00F2 = 00 & FF
'maincpu' (FA52): unmapped program memory write to 00F3 = 00 & FF
'maincpu' (FA57): unmapped program memory write to 00F4 = 00 & FF
'maincpu' (FA5C): unmapped program memory write to 00F5 = 00 & FF
'maincpu' (FA61): unmapped program memory write to 00F6 = 02 & FF
'maincpu' (FA66): unmapped program memory write to 00F7 = 09 & FF
'maincpu' (FA6B): unmapped program memory write to 00F0 = 01 & FF
'maincpu' (FA6E): unmapped program memory read from 00F0 & FF <-- now it gives up & prints an error


Layout of devices differences:
                  Schematic       Actual
6551 1              0000           0000
6551 2              0100           0004
Disk status         0400           00F0
Disk commands       0401           00F0-00F7
Parallel terminal   not there      00FF
CE on UPD765        0400           0200
TC on UPD765        0500           0300

Also, pins 16,17,18 on the UPD765 are incorrect in the schematic.


ToDo:
- Need better documentation
- Need a boot disk image

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

#include "emu.h"
#include "cpu/m6809/m6809.h"
#include "imagedev/floppy.h"
#include "machine/mos6551.h"
#include "machine/upd765.h"
#include "machine/terminal.h"


namespace {

class d6809_state : public driver_device
{
public:
	d6809_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_terminal(*this, "terminal")
		, m_fdc(*this, "fdc")
		, m_floppy0(*this, "fdc:0")
	{ }

	void d6809(machine_config &config);

private:
	u8 term_r();
	void term_w(u8 data);
	void kbd_put(u8 data);

	void mem_map(address_map &map) ATTR_COLD;

	u8 m_term_data = 0U;
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_device<generic_terminal_device> m_terminal;
	required_device<upd765a_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
};

u8 d6809_state::term_r()
{
	u8 ret = m_term_data;
	m_term_data = 0;
	return ret;
}

void d6809_state::term_w(u8 data)
{
	if ((data > 0) && (data < 0x80))
		m_terminal->write(data);
}

void d6809_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	// 00-FF is for various devices.
	map(0x0000, 0x0003).rw("acia1", FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x0004, 0x0007).rw("acia2", FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x00f0, 0x00f7).ram(); // for now
	//map(0x00f0, 0x00f0).r(m_fdc, FUNC(upd765a_device::msr_r));
	map(0x00ff, 0x00ff).rw(FUNC(d6809_state::term_r), FUNC(d6809_state::term_w));
	map(0x0200, 0x0201).mirror(0xfe).m(m_fdc, FUNC(upd765a_device::map));
	map(0x0300, 0x0300).mirror(0xff).lw8(NAME([this] (u8 data){ m_fdc->tc_w(1); m_fdc->tc_w(0); }));
	map(0x1000, 0xdfff).ram();
	map(0xe000, 0xffff).rom().region("roms", 0);
}


/* Input ports */
static INPUT_PORTS_START( d6809 )
INPUT_PORTS_END


void d6809_state::kbd_put(u8 data)
{
	m_term_data = data;
}

void d6809_state::machine_start()
{
	save_item(NAME(m_term_data));
}

void d6809_state::machine_reset()
{
	m_fdc->set_ready_line_connected(1);
	m_fdc->set_unscaled_clock(8_MHz_XTAL / 2); // 4MHz for minifloppy
	floppy_image_device *floppy = m_floppy0->get_device();
	m_fdc->set_floppy(floppy);
	floppy->mon_w(0);
}

static void floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}


void d6809_state::d6809(machine_config &config)
{
	/* basic machine hardware */
	MC6809E(config, m_maincpu, XTAL(14'745'600) / 8); // MC68B09EP
	m_maincpu->set_addrmap(AS_PROGRAM, &d6809_state::mem_map);

	MOS6551(config, "acia1", XTAL(14'745'600) / 8); // uses Q clock
	MOS6551(config, "acia2", XTAL(14'745'600) / 8); // uses Q clock

	/* video hardware */
	GENERIC_TERMINAL(config, m_terminal, 0);
	m_terminal->set_keyboard_callback(FUNC(d6809_state::kbd_put));

	// Floppy
	UPD765A(config, m_fdc, 8'000'000, true, true);
	//m_fdc->drq_wr_callback().set(m_fdc, FUNC(upd765a_device::dack_w));   // pin not emulated
	FLOPPY_CONNECTOR(config, "fdc:0", floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
}

/* ROM definition */
ROM_START( d6809 )
	ROM_REGION( 0x2000, "roms", 0 )
	ROM_LOAD( "d6809.rom", 0x0000, 0x2000, CRC(2ceb40b8) SHA1(780111541234b4f0f781a118d955df61daa56e7e))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY     FULLNAME         FLAGS
COMP( 1983, d6809, 0,      0,      d6809,   d6809, d6809_state, empty_init, "Dunfield", "6809 Portable", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



dai.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Krzysztof Strzecha, Nathan Woods
/*******************************************************************************

DAI driver by Krzysztof Strzecha and Nathan Woods

What's new:
-----------
21.05.2004  TMS5501 fixes. Debug code cleanups.
06.03.2004  Stack overflow interrupt added.
05.09.2003  Random number generator added. Few video hardware bugs fixed.
        Fixed few I8080 instructions, making much more BASIC games playable.

Notes on emulation status and to do list:
-----------------------------------------
1. A lot to do. Too much to list.

DAI technical information
==========================

CPU:
----
    I8080 2MHz


Memory map:
-----------
    0000-bfff RAM
    c000-dfff ROM (non-switchable)
    e000-efff ROM (4 switchable banks)
    f000-f7ff ROM extension (optional)
    f800-f8ff SRAM (stack)
    f900-ffff I/O
        f900-faff spare
        fb00-fbff AMD9511 math chip (optional)
        fc00-fcff 8253 programmable interval timer
        fd00-fdff discrete devices
        fe00-feff 8255 PIO (DCE bus)
        ff00-ffff timer + 5501 interrupt controller

Interrupts:
-----------


Keyboard:
---------


Video:
-----


Sound:
------


Timings:
--------


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

#include "emu.h"
#include "dai.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


/* memory w/r functions */
void dai_state::mem_map(address_map &map)
{
	map(0x0000, 0xbfff).ram().share("mainram");
	map(0xc000, 0xdfff).rom().region("maincpu",0);
	map(0xe000, 0xefff).bankr("bank2");
	map(0xf000, 0xf7ff).w(FUNC(dai_state::stack_interrupt_circuit_w));
	map(0xf800, 0xf8ff).ram();
	map(0xfb00, 0xfbff).rw(FUNC(dai_state::amd9511_r), FUNC(dai_state::amd9511_w));
	map(0xfc00, 0xfcff).rw(FUNC(dai_state::pit_r), FUNC(dai_state::pit_w)); // .rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xfd00, 0xfdff).rw(FUNC(dai_state::io_discrete_devices_r), FUNC(dai_state::io_discrete_devices_w));
	map(0xfe00, 0xfeff).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xff00, 0xff0f).mirror(0xf0).m(m_tms5501, FUNC(tms5501_device::io_map));
}


/* keyboard input */
static INPUT_PORTS_START (dai)
	PORT_START("IN0") /* [0] - port ff07 bit 0 */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)           PORT_CHAR('0')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)           PORT_CHAR('8') PORT_CHAR('(')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)           PORT_CHAR('H') PORT_CHAR('h')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)           PORT_CHAR('P') PORT_CHAR('p')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)           PORT_CHAR('X') PORT_CHAR('x')
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)          PORT_CHAR(UCHAR_MAMEKEY(UP))
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("IN1") /* [1] - port ff07 bit 1 */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)           PORT_CHAR('1') PORT_CHAR('!')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)           PORT_CHAR('9') PORT_CHAR(')')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)           PORT_CHAR('A') PORT_CHAR('a')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)           PORT_CHAR('I') PORT_CHAR('i')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)           PORT_CHAR('Q') PORT_CHAR('q')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)           PORT_CHAR('Y') PORT_CHAR('y')
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)        PORT_CHAR(UCHAR_MAMEKEY(DOWN))
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("IN2") /* [2] - port ff07 bit 2 */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)           PORT_CHAR('2') PORT_CHAR('"')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)       PORT_CHAR(':') PORT_CHAR('*')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)           PORT_CHAR('B') PORT_CHAR('b')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)           PORT_CHAR('J') PORT_CHAR('j')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)           PORT_CHAR('R') PORT_CHAR('r')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)           PORT_CHAR('Z') PORT_CHAR('z')
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)        PORT_CHAR(UCHAR_MAMEKEY(LEFT))
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("IN3") /* [3] - port ff07 bit 3 */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)           PORT_CHAR('3') PORT_CHAR('#')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)       PORT_CHAR(';') PORT_CHAR('+')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)           PORT_CHAR('C') PORT_CHAR('c')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)           PORT_CHAR('K') PORT_CHAR('k')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)           PORT_CHAR('S') PORT_CHAR('s')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)       PORT_CHAR('[') PORT_CHAR(']')
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)       PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("IN4") /* [4] - port ff07 bit 4 */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)           PORT_CHAR('4') PORT_CHAR('$')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)       PORT_CHAR(',') PORT_CHAR('<')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)           PORT_CHAR('D') PORT_CHAR('d')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)           PORT_CHAR('L') PORT_CHAR('l')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)           PORT_CHAR('T') PORT_CHAR('t')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)   PORT_CHAR('^') PORT_CHAR('~')
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)         PORT_CHAR('\t')
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("IN5") /* [5] - port ff07 bit 5 */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)           PORT_CHAR('5') PORT_CHAR('%')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)      PORT_CHAR('-') PORT_CHAR('=')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)           PORT_CHAR('E') PORT_CHAR('e')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)           PORT_CHAR('M') PORT_CHAR('m')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)           PORT_CHAR('U') PORT_CHAR('u')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)       PORT_CHAR(' ')
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("IN6") /* [6] - port ff07 bit 6 */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)           PORT_CHAR('6') PORT_CHAR('&')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)        PORT_CHAR('.') PORT_CHAR('>')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)           PORT_CHAR('F') PORT_CHAR('f')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)           PORT_CHAR('N') PORT_CHAR('n')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)           PORT_CHAR('V') PORT_CHAR('v')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Rept") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("IN7") /* [7] - port ff07 bit 7 */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)           PORT_CHAR('7') PORT_CHAR('\'')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)       PORT_CHAR('/') PORT_CHAR('?')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)           PORT_CHAR('G') PORT_CHAR('g')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)           PORT_CHAR('O') PORT_CHAR('o')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)           PORT_CHAR('W') PORT_CHAR('w')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Char del") PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(DEL))
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("IN8") /* [8] */
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("screen", FUNC(screen_device::vblank))
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_PLAYER(1)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_PLAYER(2)
		PORT_BIT(0xcb, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

/* F4 Character Displayer */
static const gfx_layout dai_charlayout =
{
	8, 16,                  /* 8 x 16 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_dai )
	GFXDECODE_ENTRY( "chargen", 0x0000, dai_charlayout, 0, 8 )
GFXDECODE_END

/* machine definition */
void dai_state::dai(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, 2000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &dai_state::mem_map);
	m_maincpu->set_irq_acknowledge_callback(FUNC(dai_state::int_ack));
	config.set_maximum_quantum(attotime::from_hz(60));

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(2000000);
	m_pit->out_handler<0>().set(m_sound, FUNC(dai_sound_device::set_input_ch0));
	m_pit->set_clk<1>(2000000);
	m_pit->out_handler<1>().set(m_sound, FUNC(dai_sound_device::set_input_ch1));
	m_pit->set_clk<2>(2000000);
	m_pit->out_handler<2>().set(m_sound, FUNC(dai_sound_device::set_input_ch2));

	I8255(config, "ppi");

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(1056, 542);
	screen.set_visarea(0, 1056-1, 0, 302-1);
	screen.set_screen_update(FUNC(dai_state::screen_update));
	screen.set_palette(m_palette);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_dai);
	PALETTE(config, m_palette, FUNC(dai_state::dai_palette), std::size(s_palette));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER(config, "speaker", 2).front();
	DAI_SOUND(config, m_sound).add_route(0, "speaker", 0.50, 0).add_route(1, "speaker", 0.50, 1);

	/* cassette */
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("dai_cass");

	/* tms5501 */
	TMS5501(config, m_tms5501, 2000000);
	m_tms5501->int_callback().set_inputline("maincpu", I8085_INTR_LINE);
	m_tms5501->xi_callback().set(FUNC(dai_state::keyboard_r));
	m_tms5501->xo_callback().set(FUNC(dai_state::keyboard_w));
	TIMER(config, m_tms_timer).configure_generic(FUNC(dai_state::tms_timer));

	/* software lists */
	SOFTWARE_LIST(config, "cass_list").set_original("dai_cass");
}


ROM_START(dai)
	ROM_REGION(0x6000,"maincpu",0)
	ROM_LOAD("dai.bin",   0x0000, 0x2000, CRC(ca71a7d5) SHA1(6bbe2336c717354beab2ae201debeb4fd055bdcb))
	ROM_LOAD("dai00.bin", 0x2000, 0x1000, CRC(fa7d39ac) SHA1(3d1824a1f273882f934249ef3cb1b38ef99de7b9))
	ROM_LOAD("dai01.bin", 0x3000, 0x1000, CRC(cb5809f2) SHA1(523656f0a9d98888cd3e2bd66886c589e9ae75b4))
	ROM_LOAD("dai02.bin", 0x4000, 0x1000, CRC(03f72d4a) SHA1(573d65dc82321970dcaf81d7638a02252ea18a7a))
	ROM_LOAD("dai03.bin", 0x5000, 0x1000, CRC(c475c96f) SHA1(96fc3cc4b8a2873f0d044bd8033d1e7b7197dd97))

	ROM_REGION(0x2000, "chargen",0)
	ROM_LOAD ("nch.bin", 0x0000, 0x1000, CRC(a9f5b30b) SHA1(24119b2984ab4e50dc0dabae1065ff6d6c1f237d))
ROM_END

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY                            FULLNAME */
COMP( 1978, dai,  0,      0,      dai,     dai,   dai_state, empty_init, "Data Applications International", "DAI Personal Computer", MACHINE_SUPPORTS_SAVE )



dames.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:yoyo_chessboard
/*******************************************************************************

Fidelity Dame Sensory Challenger (DSC)

Instead of chess, it's a checkers game for once (international rules).

When playing it on MAME with the sensorboard device, use the modifier keys
(eg. hold CTRL to ignore sensor). The game expects the player to press a sensor
only once when doing a multiple capture.

Hardware notes:
- PCB label: 510-1030A01
- Z80A CPU @ 3.9MHz
- 8KB ROM(MOS 2364), 1KB RAM(2*TMM314APL)
- 4-digit 7seg panel, sensory board with 50 buttons

TODO:
- doesn't announce winner/loser when the game ends, or is this normal?

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

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/clock.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "fidel_dsc.lh"


namespace {

class dsc_state : public driver_device
{
public:
	dsc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void dsc(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<2> m_inputs;

	u8 m_inp_mux = 0;

	void main_map(address_map &map) ATTR_COLD;

	void init_board(u8 data);
	u8 read_board_row(u8 row);

	// I/O handlers
	void control_w(u8 data);
	void select_w(u8 data);
	u8 input_r();
};

void dsc_state::machine_start()
{
	save_item(NAME(m_inp_mux));
}



/*******************************************************************************
    Sensorboard
*******************************************************************************/

void dsc_state::init_board(u8 data)
{
	for (int i = 0; i < 20; i++)
	{
		m_board->write_piece(i % 5, i / 5, 1); // white
		m_board->write_piece(i % 5, i / 5 + 6, 3); // black
	}
}

u8 dsc_state::read_board_row(u8 row)
{
	u8 data = 0;

	// inputs to sensorboard translation table (0xff is invalid)
	static const u8 lut_board[64] =
	{
		0x00, 0x50, 0x60, 0x70, 0x40, 0x30, 0x20, 0x10,
		0x01, 0x51, 0x61, 0x71, 0x41, 0x31, 0x21, 0x11,
		0x02, 0x52, 0x62, 0x72, 0x42, 0x32, 0x22, 0x12,
		0x03, 0x83, 0x84, 0x82, 0x91, 0x81, 0x90, 0x80,
		0xff, 0x93, 0x94, 0x92, 0xff, 0xff, 0xff, 0xff,
		0x04, 0x53, 0x63, 0x73, 0x43, 0x33, 0x23, 0x13,
		0xff, 0x54, 0x64, 0x74, 0x44, 0x34, 0x24, 0x14,
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
	};

	for (int i = 0; i < 8; i++)
	{
		u8 pos = lut_board[row * 8 + i];
		data = data << 1 | m_board->read_sensor(pos & 0xf, pos >> 4);
	}

	return data;
}



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

void dsc_state::control_w(u8 data)
{
	// d0-d7: input mux, 7seg data
	m_inp_mux = ~data;
	m_display->write_mx(data);
}

void dsc_state::select_w(u8 data)
{
	// d0-d3: digit select
	m_display->write_my(data & 0xf);

	// d4: speaker out
	m_dac->write(BIT(~data, 4));
}

u8 dsc_state::input_r()
{
	u8 data = 0;

	// d0-d7: multiplexed inputs (active low)
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
		{
			// read checkerboard
			data |= read_board_row(i);

			// read other buttons
			if (i >= 6)
				data |= m_inputs[i - 6]->read();
		}

	return ~data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void dsc_state::main_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom();
	map(0x4000, 0x4000).mirror(0x1fff).w(FUNC(dsc_state::control_w));
	map(0x6000, 0x6000).mirror(0x1fff).w(FUNC(dsc_state::select_w));
	map(0x8000, 0x8000).mirror(0x1fff).r(FUNC(dsc_state::input_r));
	map(0xa000, 0xa3ff).mirror(0x1c00).ram();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( dsc )
	PORT_START("IN.0")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Black King")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Black Man")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("White King")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("White Man")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("RV")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("RE")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("PB")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("LV")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("CL")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void dsc_state::dsc(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 3.9_MHz_XTAL); // 3.9MHz resonator
	m_maincpu->set_addrmap(AS_PROGRAM, &dsc_state::main_map);

	auto &irq_clock(CLOCK(config, "irq_clock", 523)); // from 555 timer (22nF, 120K, 2.7K)
	irq_clock.set_pulse_width(attotime::from_usec(41)); // active for 41us
	irq_clock.signal_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(FUNC(dsc_state::init_board));
	m_board->set_size(5, 10); // 2 columns per x (eg. square 1 & 6 are same x)
	m_board->set_spawnpoints(4);
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 8);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_fidel_dsc);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( damesc ) // model DSC
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "101-1027a01", 0x0000, 0x2000, CRC(d86c985c) SHA1(20f923a24420050fd16e1172f5e889f144d17ac9) ) // MOS 2364
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY, FULLNAME, FLAGS
SYST( 1981, damesc, 0,      0,      dsc,     dsc,   dsc_state, empty_init, "Fidelity Electronics", "Dame Sensory Challenger", MACHINE_SUPPORTS_SAVE )



daruma.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders: Felipe Sanches
/***************************************************************************

    Sigtron Daruma DS348 dot-matrix printer
    https://www.hardstand.com.br/daruma/ds348

    Driver by Felipe Correa da Silva Sanches <juca@members.fsf.org>

    Model: Print Plus - DS348
    Manufacturer: Sigtron Daruma
    Firmware version 1.1
    Release Date: May 8th/1998
    PCB: SIGTRON DS348 REV.B

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

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "sound/spkrdev.h"
#include "speaker.h"
//TODO: #include "ds348.lh"


namespace {

class daruma_state : public driver_device
{
public:
	daruma_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_speaker(*this, "speaker") { }

	void daruma(machine_config &config);

private:
	uint8_t dev0_r();
	void dev1_w(uint8_t data);
	[[maybe_unused]] void dev2_w(uint8_t data);
	uint8_t dev4_r();
	required_device<cpu_device> m_maincpu;
	required_device<speaker_sound_device> m_speaker;
	void mem_io(address_map &map) ATTR_COLD;
	void mem_prg(address_map &map) ATTR_COLD;
};

uint8_t daruma_state::dev0_r()
{
	return 0xFF;
}

uint8_t daruma_state::dev4_r()
{
	return ioport("switches")->read();
}

void daruma_state::dev1_w(uint8_t data)
{
	//while attempting to identify which bit is used for
	//controlling the buzzer, here's what I heard from each of
	//the signals on this address:

	//0x80 serial comm.? (noise)
	//0x20 LED? (3 clicks)
	//0x10 LED? (1 click)
	//0x08 serial comm.? click & noise
	//0x04 LED? (2 clicks)
	//0x02 motor control or printer heads? (I hear a series of rhythmic pulses)
	//0x01 LED? (2 clicks)
	m_speaker->level_w(data & 0x02);
}

void daruma_state::dev2_w(uint8_t data)
{
	//while attempting to identify which bit is used for
	//controlling the buzzer, here's what I heard from each of
	//the signals on this address:

	//0x80: LED? (3 clicks)
	//0x40: unused?
	//0x20: unused?
	//0x10: low freq brief beep followed by a click
	//0x08: low freq brief noise followed by a click
	//0x04: low freq brief beep followed by a click
	//0x02: low freq brief beep followed by a click
	//0x01: low freq brief noise
	//m_speaker->level_w(data & 0x01);
}

void daruma_state::mem_prg(address_map &map)
{
	map(0x0000, 0xffff).rom();
}

void daruma_state::mem_io(address_map &map)
{
	map(0x0000, 0x0000).r(FUNC(daruma_state::dev0_r));
	map(0x1000, 0x1000).w(FUNC(daruma_state::dev1_w));
//    map(0x2000, 0x2000).w(FUNC(daruma_state:dev2_w));
//    map(0x3000, 0x3000).w(FUNC(daruma_state:dev3_w));
	map(0x4000, 0x4000).r(FUNC(daruma_state::dev4_r));
	map(0x8000, 0xffff).ram(); /* 32K CMOS SRAM (HYUNDAY hy62256a) */
}

//TODO: These buttons and switches are all guesses. We'll need to further investigate this.
static INPUT_PORTS_START( daruma )
	PORT_START("buttons")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Paper A") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Paper B") PORT_CODE(KEYCODE_B)

	PORT_START("switches")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Limit Switch") PORT_CODE(KEYCODE_S)

INPUT_PORTS_END

void daruma_state::daruma(machine_config &config)
{
	/* basic machine hardware */
	I80C32(config, m_maincpu, 11059200); //verified on pcb
	m_maincpu->set_addrmap(AS_PROGRAM, &daruma_state::mem_prg);
	m_maincpu->set_addrmap(AS_IO, &daruma_state::mem_io);
	// TODO: ports

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(0, "mono", 1.00);

/*  TODO:
    config.set_default_layout(layout_daruma);

    Motors: MTA011
    http://pdf.datasheetcatalog.com/datasheet/Shindengen/mXstzvq.pdf

    The motor controller supposedly is used to cut the paper strip out after finishing printing something.
*/
}

ROM_START( ds348 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "daruma_ds348_v1_1.rom",   0x0000, 0x10000, CRC(10bf9036) SHA1(d654a13bc582f5384e759ec6fe5309a642bd8e18) )
ROM_END

} // anonymous namespace


//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY           FULLNAME                                 FLAGS
COMP( 1998, ds348, 0,      0,      daruma,  daruma, daruma_state, empty_init, "Sigtron Daruma", "Print Plus DS348 - Dot matrix printer", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



dash4.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************
Skeleton driver for Cross Products Ltd. DASH4 Debug Adapter.

DASH4 is the hardware debug adapter of CodeScape development environment for the Hitachi SuperH family of
microprocessors.
It's an intelligent debug pod that offers a complete low-level debug capability. The DASH4 uses the JTAG
interface to communicate with the target at 1.5 Mbytes/s via the Hitachi User Debug Interface (HUDI)
built-in to the SH7750. The DASH4 connects to the host computer via Ethernet to allow high-speed
communications with the debugging software.

The CodeScape Development system encompasses both software tools and hardware interfaces to provide
development support for Hitachi SH microprocessors from initial low-level system troubleshooting through
to the development of complex applications in C++.

It came bundled with the Altera Design Software for PCs "Quartus II":
https://archive.org/details/altera-quartus-ii-version-2.2-manuals-software/

Also, it came with a MemoHASP-1 parallel port security dongle labeled:
"MemoHASP-1 R3b CCYRW NA 23967", "HASP ID= 75BB4C39", "SH4".

DASH4 Rev. B PCB (1999):
      ____________________________________________________________________
     |                                  _____________       ______       |
     |     ____________         Xtal    |NEC         |     |LT1129       |___
     |    |Altera     |      11.059MHz  |D4564323G5__|     |_____|          _|__
    _|_   |Flex       |  __________              __________  __________     |   | <- Power
    | | EPF10K10TC144-3 |_CY2292F_|  __________  S32XR861Q1  S32XR861Q1     |___|
    | |   |           |              S32XR861Q1                              |
    | |   |___________|            _________         _________     ___       |
I/O | |  _______   ____________   |74HCT244|        |_HCT374A|    93LC56B  O <- Yellow LED
    | | |_P92AB|  |SH-2       |   ________                    _________    O <- Green LED
    | |  _______  |HD6417604F28  |AS29F040   ________        Crystal LAN    _|___
    |_| |_P92AB|  |           |  |       |  |GAL22V10D       |CT8900A-IQ    |   |
     |   _______  |           |  |_______|  |       |        |         |    |   | <- LAN RJ45
     |  |74HCT138 |___________|             |_______|        |_________|    |___|
     |_____            __                                   __            ___|
           |___________| |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_| |___________|
                            Unused edge connector

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

#include "emu.h"
#include "cpu/sh/sh7604.h"


namespace {

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


	void dash4(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
};


static INPUT_PORTS_START(dash4)
INPUT_PORTS_END

void dash4_state::dash4(machine_config &config)
{
	SH7604(config, m_maincpu, 11'059'000); // Actually an Hitachi SH-2 HD6417604F28
}


ROM_START(dash4)
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_LOAD( "as29f040.u2",       0x00000, 0x80000, CRC(507c2a4c) SHA1(7eea8acf196a6c249fda5fe41b197070d426cffc) )

	ROM_REGION(0x00100, "eeprom", 0)
	ROM_LOAD( "93lc56b.u17",       0x00000, 0x00100, CRC(a05ee68a) SHA1(5dd120cb4a818826ebec8bde59225ccdbf28a57d) )

	ROM_REGION(0x002e5, "pld", 0)
	ROM_LOAD( "gal22v10d-15lj.u6", 0x00000, 0x002e5, CRC(80cc0146) SHA1(e28da16e18d0c9de48a08651f1c670f946d188e7) )
ROM_END

} // anonymous namespace

SYST( 1999, dash4, 0, 0, dash4, dash4, dash4_state, empty_init, "Cross Products", "DASH4 Debug Adapter", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



datacast.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/****************************************************************************

    Datacast Controller by Defence Products

    The PCB is named Datacast Controller, and the outer casing is customised
    for 'The Stock Exchange'. Maybe other ROM variants exist for this
    controller.

    Hardware:
    ---------
    CPU:    80186

    RAM:    HM62256 x 8
            PCF8582

    Video:  SAA5240 + 2K RAM - Teletext Decoder
            SAA5250 + 2K RAM - Teletext Acquisition and Control
            SAA5231          - Teletext Video Processor

    Serial: D8251 x 2 - USART
            AY-5-8116 - Baud Rate Generator
            MC145406  - Driver/Receiver

    Ports:  Printer
            Modem
            Monitor
            Keypad

    I2C Slave Devices:
    ------------------
      22  SAA5240
      A2  PCF8582
      C0  SAB3036

    TODO:
    - implement keypad
    - complete SAA5240 emulation
    - feed SAA5240 with teletext packets

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

#include "emu.h"
#include "cpu/i86/i186.h"
#include "machine/i2cmem.h"
#include "machine/i8251.h"
#include "machine/com8116.h"
#include "machine/input_merger.h"
#include "machine/mm74c922.h"
#include "video/saa5240.h"
#include "bus/rs232/rs232.h"
#include "imagedev/bitbngr.h"
#include "screen.h"


namespace {

class datacast_state : public driver_device
{
public:
	datacast_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_kb(*this, "74c923")
		, m_cct(*this, "saa5240")
		, m_ttd(*this , "ttd")
		, m_i2cmem(*this, "i2cmem")
		, m_usart(*this, "usart%u", 0)
		, m_dbrg(*this, "dbrg")
	{
	}

	void datacast(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	uint8_t keypad_r();
	void keypad_w(uint8_t data);
	uint8_t i2c_r();
	void i2c_w(uint8_t data);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<i80186_cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<mm74c923_device> m_kb;
	required_device<saa5240a_device> m_cct;
	required_device<bitbanger_device> m_ttd;
	required_device<i2c_pcf8582_device> m_i2cmem;
	required_device_array<i8251_device, 2> m_usart;
	required_device<com8116_device> m_dbrg;

	uint8_t m_key_col = 0;
};


void datacast_state::machine_start()
{
}


void datacast_state::mem_map(address_map &map)
{
	map(0x00000, 0x7ffff).ram();
	map(0xc0000, 0xc0000).rw(FUNC(datacast_state::keypad_r), FUNC(datacast_state::keypad_w)).umask16(0x00ff);
	map(0xc0080, 0xc0080).lr8([]() { return 0xff; }, "dips"); // unknown purpose
	map(0xc0100, 0xc010f).noprw(); //.rw(m_cidac, FUNC(saa5250_device::read), FUNC(saa5250_device::write)).umask16(0x00ff);
	map(0xc0180, 0xc0180).rw(FUNC(datacast_state::i2c_r), FUNC(datacast_state::i2c_w)).umask16(0x00ff);
	map(0xc0200, 0xc0203).rw(m_usart[0], FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0xc0280, 0xc0280).w(m_dbrg, FUNC(com8116_device::stt_str_w));
	map(0xc0300, 0xc0303).rw(m_usart[1], FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0xf0000, 0xfffff).rom().region("rom", 0);
}


static INPUT_PORTS_START(datacast)
	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("1")  PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("2")  PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("3")  PORT_CODE(KEYCODE_3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("4")  PORT_CODE(KEYCODE_4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("5")  PORT_CODE(KEYCODE_5)

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Q")  PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("W")  PORT_CODE(KEYCODE_W)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("E")  PORT_CODE(KEYCODE_E)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("R")  PORT_CODE(KEYCODE_R)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("T")  PORT_CODE(KEYCODE_T)

	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("A")  PORT_CODE(KEYCODE_A)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("S")  PORT_CODE(KEYCODE_S)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("D")  PORT_CODE(KEYCODE_D)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("F")  PORT_CODE(KEYCODE_F)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("G")  PORT_CODE(KEYCODE_G)

	PORT_START("X4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Z")  PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("X")  PORT_CODE(KEYCODE_X)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("C")  PORT_CODE(KEYCODE_C)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("V")  PORT_CODE(KEYCODE_V)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("B")  PORT_CODE(KEYCODE_B)
INPUT_PORTS_END


uint8_t datacast_state::keypad_r()
{
	uint8_t data = (m_kb->read() << 0) | (m_kb->da_r() << 5);
	logerror("%s keypad_r: %02x\n", machine().describe_context(), data);
	return data;
}

void datacast_state::keypad_w(uint8_t data)
{
	m_key_col = data;

	logerror("%s keypad_w: %02x\n", machine().describe_context(), data);
}


uint8_t datacast_state::i2c_r()
{
	return m_i2cmem->read_sda() & m_cct->read_sda();
}

void datacast_state::i2c_w(uint8_t data)
{
	m_i2cmem->write_sda(BIT(data, 0));
	m_i2cmem->write_scl(BIT(data, 1));

	m_cct->write_sda(BIT(data, 0));
	m_cct->write_scl(BIT(data, 1));
}


void datacast_state::datacast(machine_config &config)
{
	I80186(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &datacast_state::mem_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(6_MHz_XTAL, 768, 0, 480, 312, 0, 250);
	m_screen->set_screen_update("saa5240", FUNC(saa5240a_device::screen_update));

	MM74C923(config, m_kb, 0);
	m_kb->x1_rd_callback().set_ioport("X1");
	m_kb->x2_rd_callback().set_ioport("X2");
	m_kb->x3_rd_callback().set_ioport("X3");
	m_kb->x4_rd_callback().set_ioport("X4");

	//SAA5231(config, m_saa5231, 13.875_MHz_XTAL);

	SAA5240A(config, m_cct, 6_MHz_XTAL);
	m_cct->set_ram_size(0x800);

	//SAA5250(config, m_cidac, 0);
	//m_cidac->set_buffer_size(0x800);

	I2C_PCF8582(config, m_i2cmem).set_e0(1);

	COM8116(config, m_dbrg, 5.0688_MHz_XTAL); // AY-5-8116
	m_dbrg->fr_handler().set(m_usart[0], FUNC(i8251_device::write_txc));
	m_dbrg->fr_handler().append(m_usart[0], FUNC(i8251_device::write_rxc));
	m_dbrg->ft_handler().set(m_usart[1], FUNC(i8251_device::write_txc));
	m_dbrg->ft_handler().append(m_usart[1], FUNC(i8251_device::write_rxc));

	input_merger_device &usartint(INPUT_MERGER_ANY_HIGH(config, "usartint"));
	usartint.output_handler().set(m_maincpu, FUNC(i80186_cpu_device::int0_w));

	I8251(config, m_usart[0], 0);
	m_usart[0]->txd_handler().set("modem", FUNC(rs232_port_device::write_txd));
	m_usart[0]->dtr_handler().set("modem", FUNC(rs232_port_device::write_dtr));
	m_usart[0]->rts_handler().set("modem", FUNC(rs232_port_device::write_rts));
	m_usart[0]->rxrdy_handler().set("usartint", FUNC(input_merger_device::in_w<0>));
	m_usart[0]->txrdy_handler().set("usartint", FUNC(input_merger_device::in_w<1>));

	rs232_port_device &modem(RS232_PORT(config, "modem", default_rs232_devices, "null_modem"));
	modem.rxd_handler().set(m_usart[0], FUNC(i8251_device::write_rxd));
	modem.dsr_handler().set(m_usart[0], FUNC(i8251_device::write_dsr));

	I8251(config, m_usart[1], 0);
	m_usart[1]->txd_handler().set("printer", FUNC(rs232_port_device::write_txd));
	m_usart[1]->dtr_handler().set("printer", FUNC(rs232_port_device::write_dtr));
	m_usart[1]->rts_handler().set("printer", FUNC(rs232_port_device::write_rts));
	m_usart[1]->rxrdy_handler().set("usartint", FUNC(input_merger_device::in_w<2>));
	m_usart[1]->txrdy_handler().set("usartint", FUNC(input_merger_device::in_w<3>));

	rs232_port_device &printer(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	printer.rxd_handler().set(m_usart[1], FUNC(i8251_device::write_rxd));
	printer.dsr_handler().set(m_usart[1], FUNC(i8251_device::write_dsr));

	// Teletext data is extracted from video signal by SAA5231.
	// Use bitbanger to read a T42 teletext stream.
	BITBANGER(config, m_ttd, 0);
}


ROM_START(datacast)
	ROM_REGION16_LE(0x10000, "rom", 0)
	ROM_LOAD16_BYTE("v29.ic9",  0x0000, 0x8000, CRC(650e6d34) SHA1(0c8709ce8220bda5da8fd41b81e531fc6f7b8ee4))
	ROM_LOAD16_BYTE("v29.ic10", 0x0001, 0x8000, CRC(bd39d683) SHA1(9d6cc2d3d6a26ca35e4e38beac64d31e345d6808))
ROM_END

} // anonymous namespace


//   YEAR  NAME      PARENT  COMPAT  MACHINE     INPUT     CLASS           INIT        COMPANY             FULLNAME                                    FLAGS
SYST(1987, datacast, 0,      0,      datacast,   datacast, datacast_state, empty_init, "Defence Products", "Datacast Controller (The Stock Exchange)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



datum.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/**********************************************************************************************

Gammatron Datum (1982)

2016-10-28.

Described in Electronics Australia magazine in 1982/1983.

- The ROM was keyed in by hand from a printout, so is therefore considered a bad dump.
- It has routines for adc, dac, cassette, but there's no hardware to support these.
- The basic machine is fully emulated as per the schematic.

Memory Map:
0000-007F = RAM inside the cpu
1000-13FF = RAM (2x 2114)
3E00-3E01 = ADC (in monitor as EQU but not further referenced) not in schematic
3F00-3F01 = 10-bit DAC (supported in monitor with routines for the user) not in schematic
4000-4001 = ACIA (in monitor for cassette usage, but schematic shows it for rs232)
            Schematic has no provision of baud clocks
5000-5003 = PIA2 for expansion, monitor does not use it at all.
6000-6003 = PIA1 for keyboard and display
7000-77FF = ROM

Note about the acia6850: The monitor sends it a master reset, then ignores it thereafter.
                         None of the pins are connected to anything - including the clocks.
                         It's up to the owner to write a TTY interface, and wire it up.

Sample program to paste in: Pick-up-sticks (Nim) All inputs are in HEX.
1. Paste this in
2. Enter number of sticks to begin with (2 digits)
3. Enter max number of sticks to be removed per turn (1 digit)
4. Your turn - remove some sticks
5. After a pause, player's turn.
6. Keep doing steps 4 and 5 till you lose (it says 10b3 3F)
7. When tested, the game seems buggy - prepare to lose sooner than expected

1000M
BD^71^07^CE^00^10^86^7E^A7^01^A7^05^BD^71^5E^B7^12^00^BD^72^7D^97^10^BD^71^5E^
B7^12^01^BD^72^7D^97^11^CE^12^00^BD^72^4D^B7^12^00^BD^71^5E^B7^12^02^BD^72^7D^
97^15^BD^71^5E^B7^12^03^27^08^7C^12^02^B1^12^02^2B^18^CE^00^10^86^06^97^12^
86^15^97^13^8D^61^CE^00^10^86^7F^A7^02^A7^03^20^D8^B6^12^00^B0^12^03^B7^12^00^
BD^72^6E^D7^10^97^11^8D^44^B6^12^00^4A^27^3C^7F^12^05^7C^12^02^7C^12^05^
B0^12^02^2E^F8^7A^12^05^5F^FB^12^02^7A^12^05^26^F8^B6^12^00^10^16^5A^
26^02^C6^01^B6^12^00^10^B7^12^00^BD^72^6E^D7^10^97^11^B6^12^00^81^01^27^04^
7E^10^36^3F^3F^86^80^B7^12^04^BD^71^DD^7A^12^04^26^F8^39^
Z1000G


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

#include "emu.h"
#include "cpu/m6800/m6800.h"
#include "machine/6821pia.h"
#include "machine/6850acia.h"
#include "bus/rs232/rs232.h"
#include "video/pwm.h"
#include "datum.lh"


namespace {

class datum_state : public driver_device
{
public:
	datum_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_pia1(*this, "pia1")
		, m_pia2(*this, "pia2")
		, m_acia(*this, "acia")
		, m_maincpu(*this, "maincpu")
		, m_display(*this, "display")
		, m_io_keyboard(*this, "X%u", 0U)
	{ }

	void datum(machine_config &config);
	DECLARE_INPUT_CHANGED_MEMBER(trigger_reset);
	DECLARE_INPUT_CHANGED_MEMBER(trigger_nmi);

private:
	uint8_t pa_r();
	void pa_w(uint8_t data);
	void pb_w(uint8_t data);
	void datum_mem(address_map &map) ATTR_COLD;
	uint8_t m_digit = 0U;
	uint8_t m_seg = 0U;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	required_device<pia6821_device> m_pia1;
	required_device<pia6821_device> m_pia2;
	required_device<acia6850_device> m_acia;
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_ioport_array<4> m_io_keyboard;
};


void datum_state::datum_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x1000, 0x13ff).mirror(0x8c00).ram(); // main ram 2x 2114
	map(0x4000, 0x4001).mirror(0x8ffe).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0x5000, 0x5003).mirror(0x8ffc).rw(m_pia2, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x6000, 0x6003).mirror(0x8ffc).rw(m_pia1, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x7000, 0x77ff).mirror(0x8800).rom().region("roms", 0);
}


/* Input ports */
static INPUT_PORTS_START( datum )
	PORT_START("X0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("2F") PORT_CODE(KEYCODE_S) PORT_CHAR('S') // "Second Function" i.e Shift-lock
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("M/R") PORT_CODE(KEYCODE_M) PORT_CHAR('M') // "Memory / Registers"
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("I/D") PORT_CODE(KEYCODE_UP) PORT_CHAR('^') // "Increment / Decrement"
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("G/S") PORT_CODE(KEYCODE_G) PORT_CHAR('G') // "Goto / Single-step"
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("SPECIAL")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Reset") PORT_CODE(KEYCODE_F3) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(datum_state::trigger_reset), 0)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Escape") PORT_CODE(KEYCODE_ESC)  PORT_CHAR('Z') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(datum_state::trigger_nmi), 0)
INPUT_PORTS_END

INPUT_CHANGED_MEMBER( datum_state::trigger_reset )
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? CLEAR_LINE : ASSERT_LINE);
}

INPUT_CHANGED_MEMBER( datum_state::trigger_nmi )
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, newval ? CLEAR_LINE : ASSERT_LINE);
}


void datum_state::machine_start()
{
	save_item(NAME(m_digit));
	save_item(NAME(m_seg));
}

void datum_state::machine_reset()
{
	m_digit = 0;
	m_seg = 0;
}

// read keyboard
uint8_t datum_state::pa_r()
{
	if (m_digit < 4)
		return m_io_keyboard[m_digit]->read();

	return 0xff;
}

// write display segments
void datum_state::pa_w(uint8_t data)
{
	m_seg = bitswap<8>(~data, 7, 0, 5, 6, 4, 2, 1, 3);
	m_display->matrix(1<<m_digit, m_seg);
}

// select keyboard row, select a digit
void datum_state::pb_w(uint8_t data)
{
	m_digit = bitswap<8>(data, 7, 6, 5, 4, 0, 1, 2, 3) & 15;
	m_display->matrix(1<<m_digit, m_seg);
}


void datum_state::datum(machine_config &config)
{
	/* basic machine hardware */
	M6802(config, m_maincpu, XTAL(4'000'000)); // internally divided to 1 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &datum_state::datum_mem);

	/* video hardware */
	config.set_default_layout(layout_datum);
	PWM_DISPLAY(config, m_display).set_size(10, 7);
	m_display->set_segmask(0x3f0, 0x7f);

	/* Devices */
	PIA6821(config, m_pia1); // keyboard & display
	m_pia1->readpa_handler().set(FUNC(datum_state::pa_r));
	m_pia1->writepa_handler().set(FUNC(datum_state::pa_w));
	m_pia1->writepb_handler().set(FUNC(datum_state::pb_w));
	m_pia1->irqa_handler().set_inputline("maincpu", M6802_IRQ_LINE);
	m_pia1->irqb_handler().set_inputline("maincpu", M6802_IRQ_LINE);

	PIA6821(config, m_pia2); // expansion
	m_pia2->irqa_handler().set_inputline("maincpu", M6802_IRQ_LINE);
	m_pia2->irqb_handler().set_inputline("maincpu", M6802_IRQ_LINE);

	ACIA6850(config, m_acia, 0); // rs232
	m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_acia->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_acia, FUNC(acia6850_device::write_rxd));
	rs232.cts_handler().set(m_acia, FUNC(acia6850_device::write_cts));
}


/* ROM definition */
ROM_START( datum )
	ROM_REGION( 0x800, "roms", 0 )
	ROM_LOAD( "datum.bin", 0x0000, 0x0800, BAD_DUMP CRC(6fb11628) SHA1(8a77a846b62eee0d12848da76e16b4c66ef445d8) )
ROM_END

} // anonymous namespace


//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY      FULLNAME  FLAGS
COMP( 1982, datum, 0,      0,      datum,   datum, datum_state, empty_init, "Gammatron", "Datum",  MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



dbox.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Joakim Larsson Edstrom
/*
 * The Dbox-1 is a DVB Satellite and cable digital television set-top box.
 *
 * The CPU board:
 *__________________________________________________________________________________________________________
 *                                                                                                          |
 *                                                                                                          |
 *                                                         _______________                                  |
 *                ___                                     |               |       ________    ________      |
 *               |   |                                    |_______________|      |        |  |        |     |
 *               |   |                                                           | FLASH  |  | FLASH  |     |
 *               |   |                                                           | 29F800B|  | 29F800B|     |
 *               |   |                                                           |        |  |        |     |
 *               |   |                 ____________                              |  1Mb   |  |  1Mb   |     |
 *               |   |                |            |                             |        |  |        |     |
 *               |___|                |   CPU32    |                             |        |  |        |     |
 *                                    | 68340PV16E |                             |________|  |________|     |
 *                                    |            |                                                        |
 *                                    |            |                                                        |
 *             _______________        |____________|                    _____                               |
 *            |               |                                        |     |                              |
 *            |  SCSI         |                                        |_____|         _____       _____    |
 *            |  CONTROLLER   |        +-+                              _____         |     |     |     |   |
 *            | LSI 53CF54-2  |        |X|                             |     |        |DRAM |     |DRAM |   |
 *            |               |        |T|                             |_____|        |42460|     |42460|   |
 *            |               |        |A|                                            |     |     |     |   |
 *            |               |        |L|                  ----------------          |512Kb|     |512Kb|   |
 *            |_______________|        +-+                  |              |          |     |     |     |   |
 *                                    3.6864                ----------------          |_____|     |_____|   |
 *                                                                                                          |
 *__________________________________________________________________________________________________________|
 *
 * History of Nokia Multimedia Division
 *-------------------------------------
 * Luxor AB was a Swedish home electronics and computer manufacturer located in Motala from 1923 and acquired
 * by Nokia 1985. Luxor designed among other things TV sets, Radios and the famous ABC-80. The Nokia Multimedia
 * Division was formed in Linköping as a result of the Luxor acquisition. Their main design was a satellite
 * receiver, the first satellite in Europe was launched in 1988 and the market was growing fast however it took
 * a long time, almost 10 years before the breakthrough came for Nokia, a deal with the Kirch Gruppe was struck and
 * in 1996 the 68340 based Dbox-1 was released in Germany. The original design was expensive, so soon a cost reduced
 * version based on PPC, the Dbox-2, was released. The boxes sold in millions but the margins were negative or very
 * low at best and the Kirch Gruppe went bankrupt in 2002 and Nokia decided to shutdown the facility in Linköping.
 *
 * The heavily subsidised Dbox was very popular in Holland since Kirch Gruppe didn't lock usage to themselves. This was
 * corrected in a forced firmware upgrade leaving the "customers" in Holland without a working box. Pretty soon a
 * shareware software developed by Uli Hermann appeared called DVB98 and later DVB2000 re-enabling the boxes in  Holland
 * and blocking upgrades. Uli's software was by many considered better than the original software.
 *
 * Misc links about Nokia Multimedia Division and this board:
 * http://www.siliconinvestor.com/readmsg.aspx?msgid=5097482
 * http://www.sat-digest.com/SatXpress/Digital/MM/Mediamaster.htm
 * http://www.telecompaper.com/news/beta-research-publishes-dbox-specifications--163443
 * https://de.wikipedia.org/wiki/D-box
 * http://dvb2000.org/dvb2000/
 *
 * Misc findings
 *--------------
 * - Serial port on the back runs at 19200 and issues modem commands when attached to terminal
 * - It is possible to attach a BDM emulator and retrieve the ROM through it.
 * - It is possible to flash new firmware through BDM by adding jumper XP06 (under the modem board)
 * - The bootstrap is based on RTXC 3.2g RTOS
 * - The bootstrap jumps to firmware from 0xb82 to RAM at 0x800000
 *
 * Identified chips/devices
 *-----------------
 * Motorola 68340 CPU
 * Philips SAA7124 Digital Video Encoder
 * LSI 53CF54-2 SCSI controller
 * Crystal CL4922-CL Mpeg Audio Decoder System
 * Rockwell R6653-16 Single Device Data/Fax Modem Data Pump
 * Philips 8020401TM100 <modem related, unknown purpose>
 * C-cube CL9100 MPEG2 decoder
 * Lucent AV6220A MPEG2 demultiplexer w. crypto interface
 * CI Common Interface module
 * LSI L2A0371 Tuner
 * 2 x 29F800B-90 (2Mb FLASH) - schematics shows a 29F400 as second device so firmware checks device ID
 * 2 x 42260-60  (1Mb DRAM)
 * Siemens SDA5708 dot matrix display, SPI like connection
 *  - http://arduinotehniq.blogspot.se/2015/07/sda5708-display-8-character-7x5-dot.html
 *  - charset stored at 0x808404 to 0x808780, 7 bytes per character
 *
 * Known Nokia Receivers
 * -----------------------------
 * D-box SCART, built-in Irdeto CAM, modem, SCSI
 * 9200 SCART, SCSI
 * 9500 SCART, built-in Irdeto CAM, modem, SCSI
 * 9600 SCART
 * 9602 SCART, modem
 * 9610 SCART, modem, SCSI
 * 8200 RF, SCSI
 * 8500 RF, built-in Irdeto CAM, modem, SCSI
 * 8600 RF
 *
 * Known board revs and changes
 * -----------------------------
 * Main Board 55 31893-11 DVB 9500 S
 * Main Board 55 31893-33 DVB 9500 S - GALs named after functions in schematics
 * Main Board 55 31893-46 D-box      - OE* IP09 tied to GND, only one flash IP02
 * Main Board 55 31893-89 DVB 9500 S - OE* IP09 tied to GND
 *
 * Address Map
 * --------------------------------------------------------------------------
 * Address Range     Memory Space (physical)   Notes
 * --------------------------------------------------------------------------
 * 0xffffffff                                  (top of memory)
 * 0x00FFF780-0x00FFF7BF DMA                   offset to SIM40
 * 0x00FFF700-0x00FFF721 Serial devices        offset to SIM40
 * 0x00FFF600-0x00FFF67F Timers                offset to SIM40
 * 0x00FFF000-0x00FFF07F SIM40                 programmed base adress (MCR)
 * 0x00700000-0x0077ffff RAM
 * 0x00780000-0x007807ff I/O area
 * 0x00800000-0x008fffff RAM
 * 0x00000000-0x0001ffff bootstrap
 * --------------------------------------------------------------------------
 *
 * Init sequence
 * -------------
 *  MCR           : 0x6301     Timer/wd disabled, show cycles, ext arbit,
 *                             user access to SIM40, IARB = 1
 *  MBAR          : 0x00FFF101 SIM40 base = 0x00fff000
 *  VBR           : 0x008096F8 VBR - Vector Base Register
 *  SIM40 + 0x0006: 0xE8       AVR - Auto Vector Register
 *  SIM40 + 0x0021: 0x0C       SWIV_SYPCR
 *  SIM40 + 0x001F: 0x00       PPARB - Port B pin assignment
 *  SIM40 + 0x0011: 0xFF       PORTA - Port A Data
 *  SIM40 + 0x0013: 0x16       DDRA  - Port A Data direction
 *  SIM40 + 0x0700: 0x00       Serial port
 *  SIM40 + 0x071F: 0xFF       Serial port
 *  SIM40 + 0x0004: 0x7F03     SYNCR
 *  SIM40 + 0x0040: 0x003FFFF5 CS0 mask 1 - block size = 4194304 (4MB)
 *  SIM40 + 0x0044: 0x00000003 CS0 base 1 - base addr = 0x000000
 *  SIM40 + 0x0048: 0x003FFFF5 CS1 mask 1 - block size = 4194304 (4MB)
 *  SIM40 + 0x004C: 0x00800003 CS1 base 1 - base addr = 0x800000
 *  SIM40 + 0x0050: 0x00007FFF CS2 mask 1 - block size = 65536 (64KB)
 *  SIM40 + 0x0054: 0x00700003 CS2 base 1
 *  SIM40 + 0x0058: 0x000007F2 CS3 mask 1 - block size = 2048 (2KB)
 *  SIM40 + 0x005C: 0x00780003 CS3 base 1
 *  SIM40 + 0x001F: 0x40       PBARB Port B Pin Assignment
 *  -------------------------------------------------------------
 *  The bootstrap copies the firmware to RAM and jumps to it
 *  -------------------------------------------------------------
 *
 *  --- PC > 0x700000
 *  SIM40 + 0x0022: 0x0140     PICR Periodic Interrupt Control Register
 *  SIM40 + 0x0024: 0x0029     PICR Periodic Interrupt Timer Register
 *
 *  Serial port setup
 * ------------------
 *  --- PC < 0x1FFFF so bootstrap code
 *  SIM40 + 0x0700: 0x00     Serial Module - MCR High Byte
 *                            - The serial module is enabled
 *                            - ignore FREEZE
 *                            - The crystal clock is the clear-to-send input capture clock for both channels
 *  SIM40 + 0x071F: 0xFF     Serial Module - OUTPUT PORT (OP)4 BIT RESET - all cleared
 *  SIM40 + 0x0700: 0x00     Serial Module - MCR High Byte
 *                            - The serial module is enabled
 *                            - ignore FREEZE
 *                            - The crystal clock is the clear-to-send input capture clock for both channels
 *  SIM40 + 0x0701: 0x8A     Serial Module - MCR Low Byte
 *                            - The serial control registers are only accessible from supervisor mode
 *                            - IARB = 0x0A - serial module has priority level 10d
 *  SIM40 + 0x0704: 0x01     Serial Module - ILR Interrupt Level
 *  SIM40 + 0x0705: 0x44     Serial Module - IVR Interrupt Vector
 *  SIM40 + 0x0714: 0x80     Serial Module - ACR Auxiliary Control
 *                                           - Set 2 of the available baud rates is selected.
 *                                           - CTS state change has no effect
 *  --- setup Channel A
 *  SIM40 + 0x0712: 0x20     Serial Module - CRA Command Register A
 *                                           - Reset the receiver
 *  SIM40 + 0x0712: 0x30     Serial Module - CRA Command Register A
 *                                           - Reset the transmitter
 *  SIM40 + 0x0710: 0x93     Serial Module - MR1A Mode Register 1A
 *                                           - Upon receipt of a valid start bit, RTS≈ is negated if the channel's FIFO is full.
 *                                             RTS≈ is reasserted when the FIFO has an empty position available.
 *                                           - No Parity
 *                                           - Eight bits
 *  SIM40 + 0x0720: 0x07     Serial Module - MR2A Mode Register 2A
 *                                           - No CTS or RTS controll
 *                                           - 1 STOP bit
 *  SIM40 + 0x0711: 0xCC     Serial Module - CSRA Clock Select Register A
 *                                           - 19200 baud TXc
 *                                           - 19200 baud Rxc
 *  SIM40 + 0x0712: 0x41     Serial Module - CRA Command Register A
 *                                           - Reset Error status
 *                                           - Enable Receiver
 *  - Check for charcters in channel A
 *  SIM40 + 0x0711: btst #0  Serial Module - SRA Status Register A
 *  --- if there is
 *        store all characters in buffer at (A6) 0x88FFD0
 *  --- setup Channel B (See details as for channel A above)
 *  SIM40 + 0x071A: 0x20     Serial Module - CRB Command Register B
 *  SIM40 + 0x071A: 0x30     Serial Module - CRB Command Register B
 *  SIM40 + 0x0718: 0x93     Serial Module - MR1B Mode Register 1B
 *  SIM40 + 0x0721: 0x07     Serial Module - MR2B Mode Register 2B
 *  SIM40 + 0x0719: 0xCC     Serial Module - CSRB Clock Select Register B
 *  SIM40 + 0x071A: 0x41     Serial Module - CRB Command Register B
 *  - Check for characters in channel B
 *  SIM40 + 0x0719: btst #0  Serial Module - SRB Status Register B
 *  --- if there is
 *        store all characters in buffer at (A6) 0x88FFD0
 *  ---
 *  - Check bit 0 set on Input Port
 *  SIM40 + 0x071D: btst #0  Input Port - IP
 *  --- if bit 0 is set
 *      0x00801208: 0x80
 *  SIM40 + 0x071A: 0x81     Serial Module - CRB Command Register B
 *  ---
 *  SIM40 + 0x0720: 0x41     Serial Module - MR2A Mode register 2A
 *  SIM40 + 0x071D: 0x03     OPCR Output Port Control Register
 *  SIM40 + 0x0715: 0x03     IER Interrupt Enable Register
 *
 *  Timer setup
 * ------------
 *  SIM40 + 0x0640: 0x03     MCR Timer 2
 *  tbc...
 *
 *  -------------------------------------------------------------
 *  The bootstrap copies the firmware to RAM and jumps to it
 *  -------------------------------------------------------------
 *
 *  --- PC > 0x700000 so probably init of the RTXC tick timer setup?!
 *  SIM40 + 0x0022: 0x0140     PICR Periodic Interrupt Control Register
 *  SIM40 + 0x0024: 0x0029     PICR Periodic Interrupt Timer Register
 *
 *  Serial port setup
 * ------------------
 *  --- PC < 0x1FFFF so bootstrap code
 *  SIM40 + 0x0700: 0x00     Serial Module - MCR High Byte
 *                            - The serial module is enabled
 *                            - ignore FREEZE
 *                            - The crystal clock is the clear-to-send input capture clock for both channels
 *  SIM40 + 0x071F: 0xFF     Serial Module - OUTPUT PORT (OP)4 BIT RESET - all cleared
 *  SIM40 + 0x0700: 0x00     Serial Module - MCR High Byte
 *                            - The serial module is enabled
 *                            - ignore FREEZE
 *                            - The crystal clock is the clear-to-send input capture clock for both channels
 *  SIM40 + 0x0701: 0x8A     Serial Module - MCR Low Byte
 *                            - The serial control registers are only accessible from supervisor mode
 *                            - IARB = 0x0A - serial module has priority level 10d
 *  SIM40 + 0x0704: 0x01     Serial Module - ILR Interrupt Level
 *  SIM40 + 0x0705: 0x44     Serial Module - IVR Interrupt Vector
 *  SIM40 + 0x0714: 0x80     Serial Module - ACR Auxiliary Control
 *                                           - Set 2 of the available baud rates is selected.
 *                                           - CTS state change has no effect
 *  --- setup Channel A
 *  SIM40 + 0x0712: 0x20     Serial Module - CRA Command Register A
 *                                           - Reset the receiver
 *  SIM40 + 0x0712: 0x30     Serial Module - CRA Command Register A
 *                                           - Reset the transmitter
 *  SIM40 + 0x0710: 0x93     Serial Module - MR1A Mode Register 1A
 *                                           - Upon receipt of a valid start bit, RTS≈ is negated if the channel's FIFO is full.
 *                                             RTS≈ is reasserted when the FIFO has an empty position available.
 *                                           - No Parity
 *                                           - Eight bits
 *  SIM40 + 0x0720: 0x07     Serial Module - MR2A Mode Register 2A
 *                                           - No CTS or RTS control
 *                                           - 1 STOP bit
 *  SIM40 + 0x0711: 0xCC     Serial Module - CSRA Clock Select Register A
 *                                           - 19200 baud TXc
 *                                           - 19200 baud Rxc
 *  SIM40 + 0x0712: 0x41     Serial Module - CRA Command Register A
 *                                           - Reset Error status
 *                                           - Enable Receiver
 *  - Check for charcters in channel A
 *  SIM40 + 0x0711: btst #0  Serial Module - SRA Status Register A
 *  --- if there is
 *        store all characters in buffer at (A6) 0x88FFD0
 *  --- setup Channel B (See details as for channel A above)
 *  SIM40 + 0x071A: 0x20     Serial Module - CRB Command Register B
 *  SIM40 + 0x071A: 0x30     Serial Module - CRB Command Register B
 *  SIM40 + 0x0718: 0x93     Serial Module - MR1B Mode Register 1B
 *  SIM40 + 0x0721: 0x07     Serial Module - MR2B Mode Register 2B
 *  SIM40 + 0x0719: 0xCC     Serial Module - CSRB Clock Select Register B
 *  SIM40 + 0x071A: 0x41     Serial Module - CRB Command Register B
 *  - Check for characters in channel B
 *  SIM40 + 0x0719: btst #0  Serial Module - SRB Status Register B
 *  --- if there is
 *        store all characters in buffer at (A6) 0x88FFD0
 *  ---
 *  - Check bit 0 set on Input Port
 *  SIM40 + 0x071D: btst #0  Input Port - IP
 *  --- if bit 0 is set
 *      0x00801208: 0x80
 *  SIM40 + 0x071A: 0x81     Serial Module - CRB Command Register B
 *  ---
 *  SIM40 + 0x0720: 0x41     Serial Module - MR2A Mode register 2A
 *  SIM40 + 0x071D: 0x03     OPCR Output Port Control Register
 *  SIM40 + 0x0715: 0x03     IER Interrupt Enable Register
 *
 *  Timer setup
 * ------------
 *  SIM40 + 0x0640: 0x03     MCR Timer 2
 *  tbc...
 *
 * // Tricks with the CS0 and the GAL:s
 * 008004d8 SIM40 + 0x0044: 0x00000053 CS0 base 1 - base addr = 0x000000, Supervisor Data Space, No CPU Space, Valid CS
 * 008004e2 SIM40 + 0x0040: 0x003FFF05 CS0 mask 1 - block size = 4194304 (4MB),
 * ... strange series of operations between 800834 and 8008D4, suspecting GAL:s to be involved in some magic here
 * 008004ee SIM40 + 0x0040: 0x003FFFF5 CS0 mask 1 - block size = 4194304 (4MB), Mask all accesses
 * 008004f8 SIM40 + 0x0044: 0x0000005b CS0 base 1 - base addr = 0x000000, Supervisor Data Space, Write Protect, No CPU Space, Valid CS
 *
 *  LED Dot Matrix Display hookup
 *  -------------------------------------
 *  "DISPLAY CONN ALT" connected to the SDA5708
 *  pin signal   connected to
 *   1   VCC      +5v
 *   2   LOAD1*   PA2 68340 pin 121 IP01
 *   3   DATA      D8 68340 pin 134 IP01
 *   4   SDCLK     Q6 74138 pin 9   IP12
 *   5   RESET*    Q4 74259 pin 9   IP16
 *   6   GND      GND
 *
 *  IP12 74138 hookup
 *  pin signal   connected to
 *   1   A0       A8 68340 pin 46  IP01
 *   2   A1       A9 68340 pin 47  IP01
 *   3   A2      A10 68340 pin 48  IP01
 *   4   E1*     CS3 68340 pin  5  IP01
 *   5   E2*     CS3 68340 pin  5  IP01
 *   6   E3      +5v
 *   9   Q6    SDCLK SDA5708 pin 4
 *  14   Q1  Enable* 74259 pin 14  IP16
 *
 *  IP16 74259 hookup
 *  pin signal   connected to
 *   1   A0       A0 68340 pin 113 IP01
 *   2   A1       A1 68340 pin  37 IP01
 *   3   A2       A2 68340 pin  38 IP01
 *   9   Q4   Reset* SDA5708 pin 5
 *  13   D        D8 68340 pin 134 IP01
 *  14   Enable*  Q1 74138 pin  14 IP12
 *  15   Clear*   System reset
 *
 *  Address map decoding
 *  --------------------
 *  IP06 GAL16V8 - DRAM-PS8V0.9
 *  pin signal   connected to
 *   1   Q       V-FIFO-CLK inverted SCSI_CLK, origin to be found
 *   2   I1      SCSI_CLK
 *   3   I2      A0   68340 pin 113 IP01
 *   4   I3      CS1  68340 pin   2 IP01
 *   5   I4      A19  68340 pin  62 IP01
 *   6   I5      A21  68340 pin  64 IP01
 *   7   I6      BG   68340 pin 101 IP01
 *   8   I7      SIZ0 68340 pin 105 IP01
 *   9   I8      R/W  68340 pin 107 IP01
 *  11   I9/OE*  GND  via an 100R resistor
 *  12   O0      FC2  68340 pin  71 IP01
 *  13   O1      I8   GAL168V pin 9 IP07 SCSI GAL
 *  14   O2      nc
 *  15   O3      RAS1 514260 pin 14 IP10-11 512KB DRAM each (IP11 is empty socket)
 *  16   O4      LCAS 514260 pin 29 IP09-11 512KB DRAM each (IP11 is empty socket)
 *  17   O5      UCAS 514260 pin 28 IP09-11 512KB DRAM each (IP11 is empty socket)
 *  18   O6      RAS0 514260 pin 14 IP09    512KB DRAM
 *  19   O7      I2 GAL16V8 pin 3 IP07 SCSI GAL (ANDed with !CS1) DMA_REQ
 *              +I1 GAL16V8 pin 2 IP08 LOGC GAL (ANDed with !CS1) DMA_REQ
 *              +G1 74257 pin 1 IP04-05 MUXes
 *
 *  IP07 GAL16V8 - SCSI-PS8V0.8
 *  pin signal   connected to
 *   1   Q       V-FIFO-CLK inverted SCSI_CLK, origin to be found
 *   2   I1      R/W  68340 pin 107 IP01
 *   3   I2      DMA_REQ
 *   4   I3      Q    7474  pin   5 IP25
 *   5   I4      DACK1 68340 pin 15
 *   6   I5      D0   74138 pin  15 IP12
 *   7   I6      !BG  7404<-68340 pin 101 IP01 Inverted Bus Grant
 *   8   I7      3    7408 pin 3 IP23 OE for IP10
 *   9   I8      O1   GAL16V pin 18 IP06 DRAM GAL
 *  11   I9/OE*  GND via an 100R resistor
 *  12   O0      DREQ1 68340 pin 16
 *  13   O1      WE   514260 pin 13 IP10 512KB DRAM
 *  14   O2      DMA_ACK
 *  15   O3      CS_SCSI
 *  16   O4      RD_SCSI
 *  17   O5      WE   514260 pin 13 IP11 empty socket
 *  18   O6      WR_SCSI
 *  19   O7      MWE
 *              +WE   514260 pin 13 IP09 512KB DRAM
 *
 * Interrupt sources
 * ----------------------------------------------------------
 * Description               Device  Lvl  IRQ
 *                           /Board      Vector
 * ----------------------------------------------------------
 *  IRQ7 CTRL20 IP19 circuit
 *  IRQ6 SCSI/DEMUX INT
 *  IRQ5 CAM module INT
 *  IRQ3 Audio/Video INT
 *
 *  TODO:
 *  - Dump/understand the address decoder GAL:s IP06-IP08 (3 x 16V8)
 *  - Fix debug terminal
 *  - write demuxer
 *
 ****************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "machine/68340.h"
#include "machine/74259.h"
#include "machine/intelfsh.h"
#include "video/sda5708.h"

#include "sda5708.lh"

//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

#define LOG_SETUP   (1U << 1)
#define LOG_DISPLAY (1U << 2)
#define LOG_FLASH   (1U << 3)

#define VERBOSE  (LOG_FLASH)
#define LOG_OUTPUT_FUNC printf

#include "logmacro.h"

#define LOGSETUP(...)   LOGMASKED(LOG_SETUP,   __VA_ARGS__)
#define LOGDISPLAY(...) LOGMASKED(LOG_DISPLAY, __VA_ARGS__)
#define LOGFLASH(...)  LOGMASKED(LOG_FLASH,   __VA_ARGS__)

#ifdef _MSC_VER
#define FUNCNAME __func__
#else
#define FUNCNAME __PRETTY_FUNCTION__
#endif


namespace {

#define LOCALFLASH 0 //  1 = local flash rom implementation 0 = intelflash_device

class dbox_state : public driver_device
{
public:
	dbox_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_display(*this, "display")
		, m_ip16_74259(*this, "hct259.ip16")
	{ }

	void dbox(machine_config &config);

	void init_dbox();

private:
	required_device<m68340_cpu_device> m_maincpu;
	required_device<sda5708_device> m_display;
	required_device<hct259_device> m_ip16_74259;

	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	void sda5708_reset(uint8_t data);
	void sda5708_clk(uint8_t data);
	void write_pa(uint8_t data);

	void dbox_map(address_map &map) ATTR_COLD;

#if LOCALFLASH
	uint16_t sysflash_r(offs_t offset);
	void sysflash_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
private:
	uint16_t * m_sysflash = nullptr;
	uint32_t m_sf_mode = 0;
	uint32_t m_sf_state = 0;
#endif
};

void dbox_state::machine_start()
{
	LOG("%s\n", FUNCNAME);

#if LOCALFLASH
	save_pointer (NAME (m_sysflash), sizeof(m_sysflash));

	m_sysflash = (uint16_t*)(memregion ("flash0")->base());
	m_sf_mode  = 0;
	m_sf_state = 0;
#endif
}

void dbox_state::machine_reset()
{
	LOG("%s\n", FUNCNAME);
	m_maincpu->tgate2_w(ASSERT_LINE); // Pulled high by resistor when not used as Rx from descrambler device
}

// TODO: Hookup the reset latch correctly
void dbox_state::sda5708_reset(uint8_t data) {
	LOGDISPLAY("%s - not implemented\n", FUNCNAME);
}

void dbox_state::sda5708_clk(uint8_t data) {
	LOGDISPLAY("%s\n", FUNCNAME);
	m_display->sdclk_w(CLEAR_LINE);
	m_display->data_w((0x80 & data) != 0 ? ASSERT_LINE : CLEAR_LINE);
	m_display->sdclk_w(ASSERT_LINE);
}

void dbox_state::write_pa(uint8_t data) {
	LOGDISPLAY("%s\n", FUNCNAME);
	m_display->load_w((0x04 & data) == 0 ? ASSERT_LINE : CLEAR_LINE);
}

#if LOCALFLASH
/* Local emulation of the 29F800B 8Mbit flashes if the intelflsh bugs, relies on a complete command cycle is done per device, not in parallel */
/* TODO: Make a flash device of this and support programming per sector and persistence, as settings etc may be stored in a 8Kb sector  */
void dbox_state::sysflash_w(offs_t offset, uint16_t data, uint16_t mem_mask) {
	LOGFLASH("%s pc:%08x offset:%08x data:%08x mask:%08x\n", FUNCNAME, m_maincpu->pc(), offset, data, mem_mask);

	/*Data bits DQ15–DQ8 are don’t cares for unlock and command cycles.*/
	m_sf_state = ((m_sf_state << 8) & 0xffffff00) | (data & 0xff);
	switch (m_sf_state)
	{
	case 0xf0:// Reset command, to get back to reading flash data
		m_sf_mode = 0;
		m_sf_state = 0;
		LOGFLASH("- Reset command\n");
		break;
	case 0xaa: // Building a multi byte command
		m_sf_mode = 1;
		break;
	case 0xaa55: // Building a multi byte command
	case 0xaa55a0: // Program Data
	case 0xaa5580: // Erase
	case 0xaa5580aa: // Chip or Sector Erase
		break;
	case 0xaa5590: // Autoselect mode
		m_sf_mode = 4;
		m_sf_state = 0;
		LOGFLASH("- Autoselect Mode\n");
		break;
	case 0xb0: // Erase Suspend Mode
		m_sf_mode = 2;
		m_sf_state = 0;
		LOGFLASH("- Erase Suspend Mode\n");
		break;
	case 0x30: // Erase Resume Mode
		m_sf_mode = 3;
		m_sf_state = 0;
		LOGFLASH("- Erase Resume Mode\n");
		break;
	}
}

uint16_t dbox_state::sysflash_r(offs_t offset) {

	if (m_sf_mode == 0)
	{
		return m_sysflash[offset];
	}
	else
	{
		if (m_sf_mode == 4)
		{
			switch (offset & 0xff)
			{
			case 0x00: LOGFLASH("- Manufacturer ID\n"); return 01; break; // Manufacturer ID
			//case 0x01: LOGFLASH("- Device ID\n"); return 0x22d6; break; // Device ID (Top Boot Block) 29F800TA
			case 0x01: LOGFLASH("- Device ID\n"); return 0x2258; break; // Device ID (Bottom Boot Block) 29F800BA
			case 0x02: LOGFLASH("- Sector %02x protection: 1 (hardcoded)\n", offset >> 12); return 01; break;
			default:   LOGFLASH(" - Unhandled Mode:%d State:%08x\n", m_sf_mode, m_sf_state);
			}
		}
	}
	return 0;
}
/* End of flash emulation */
#endif

void dbox_state::dbox_map(address_map &map)
{
	// CS0 - bootrom
	// 008004ee Address mask CS0 00000040, 003ffff5 (ffffffff) - Mask: 003fff00 FCM:0f DD:1 PS: 16-Bit
	// 008004f8 Base address CS0 00000044, 0000005b (ffffffff) - Base: 00000000 BFC:05 WP:1 FTE:0 NCS:1 Valid: Yes
#if LOCALFLASH
	map(0x000000, 0x3fffff).rom().r(FUNC(dbox_state::sysflash_r)).region("flash0", 0);
	map(0x000000, 0x3fffff).w(FUNC(dbox_state::sysflash_w));
#else
	map(0x000000, 0x0fffff).rw("flash0", FUNC(intelfsh16_device::read), FUNC(intelfsh16_device::write));
	map(0x100000, 0x1fffff).rw("flash1", FUNC(intelfsh16_device::read), FUNC(intelfsh16_device::write));
#endif
	// CS2 - CS demux
	// 0000009a Address mask CS2 00000050, 00007fff (ffffffff) - Mask: 00007f00 FCM:0f DD:3 PS: External DSACK response
	// 000000a2 Base address CS2 00000054, 00700003 (ffffffff) - Base: 00700000 BFC:00 WP:0 FTE:0 NCS:1 Valid: Yes
	//map(0x700000, 0x77ffff)
	// CS3 - 8 bit devices
	// 000000aa Address mask CS3 00000058, 000007f2 (ffffffff) - Mask: 00000700 FCM:0f DD:0 PS: 8-bit
	// 000000b2 Base address CS3 0000005c, 00780003 (ffffffff) - Base: 00780000 BFC:00 WP:0 FTE:0 NCS:1 Valid: Yes
	// map(0x780000, 0x7807ff)
	map(0x780100, 0x7801ff).w(FUNC(dbox_state::sda5708_reset));
	map(0x780600, 0x7806ff).w(FUNC(dbox_state::sda5708_clk));
	// CS1 - RAM area
	// 0000008a Address mask CS1 00000048, 003ffff5 (ffffffff) - Mask: 003fff00 FCM:0f DD:1 PS: 16-Bit
	// 00000092 Base address CS1 0000004c, 00800003 (ffffffff) - Base: 00800000 BFC:00 WP:0 FTE:0 NCS:1 Valid: Yes
	map(0x800000, 0xcfffff).ram();
}

/* Input ports */
static INPUT_PORTS_START( dbox )
INPUT_PORTS_END

void dbox_state::dbox(machine_config &config)
{
	M68340(config, m_maincpu, 0);       // The 68340 has an internal VCO as clock source, hence need no CPU clock
	m_maincpu->set_crystal(XTAL(32'768)); // The dbox uses the VCO and has a crystal as VCO reference and to synthesize internal clocks from
	m_maincpu->set_addrmap(AS_PROGRAM, &dbox_state::dbox_map);
	m_maincpu->pa_out_callback().set(FUNC(dbox_state::write_pa));

	/* Timer 2 is used to communicate with the descrambler module TODO: Write the descrambler module */
	//m_maincpu->tout2_out_callback().set("dcs", FUNC(descrambler_device::txd_receiver));
	//m_maincpu->tgate2_in_callback().set("dsc", FUNC(descrambler_device::rxd_receiver));

	/* Configure the serial ports */
	subdevice<mc68340_serial_module_device>("maincpu:serial")->a_tx_cb().set("rs232", FUNC(rs232_port_device::write_txd));
	subdevice<mc68340_serial_module_device>("maincpu:serial")->b_tx_cb().set("modem", FUNC(rs232_port_device::write_txd));
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set("maincpu:serial", FUNC(mc68340_serial_module_device::rx_a_w));
	rs232_port_device &modem(RS232_PORT(config, "modem", default_rs232_devices, nullptr));
	modem.rxd_handler().set("maincpu:serial", FUNC(mc68340_serial_module_device::rx_b_w));

	/* Add the boot flash */
	AMD_29F800B_16BIT(config, "flash0");
	AMD_29F800B_16BIT(config, "flash1");

	/* LED Matrix Display */
	SDA5708(config, m_display, 0);
	config.set_default_layout(layout_sda5708);

	/* IP16 74259 8 bit latch */
	HCT259(config, m_ip16_74259);
	m_ip16_74259->q_out_cb<4>().set("display", FUNC(sda5708_device::reset_w));
}

void dbox_state::init_dbox()
{
}

// TODO: Figure out correct ROM address map
// TODO: Figure out what DVB2000 is doing
ROM_START( dbox )
	ROM_REGION16_BE(0x400000, "flash0", ROMREGION_ERASEFF) // should be 0x100000
	ROM_DEFAULT_BIOS("b200uns")

	ROM_SYSTEM_BIOS(0, "b200uns", "Nokia Bootloader B200uns")
	ROMX_LOAD( "b200uns.bin",   0x000000, 0x020000, CRC(0ff53e1f) SHA1(52002ee22c032775dac383d408c44abe9244724f), ROM_BIOS(0) )

	ROM_SYSTEM_BIOS(1, "b210uns", "Nokia Bootloader B210uns")
	ROMX_LOAD( "b210uns.bin",   0x000000, 0x020000, CRC(e8de221c) SHA1(db6e20ae73b11e8051f389968803732bd73fc1e4), ROM_BIOS(1) )

	ROM_SYSTEM_BIOS(2, "nbc106.bin", "Nokia Bootloader CI v1.06")
	ROMX_LOAD( "bootci106.bin", 0x000000, 0x020000, BAD_DUMP CRC(641762a9) SHA1(7c5233390cc66d3ddf4c730a3418ccfba1dc2905), ROM_BIOS(2) )
ROM_END

} // anonymous namespace


COMP( 1996, dbox, 0, 0, dbox, dbox, dbox_state, init_dbox, "Nokia Multimedia", "D-box 1 (Kirch-Gruppe)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



dbridgec.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Micro-Concepts Ltd Diamond Bridge Computer (model M1011)
Also sold by Nu Vations as Nu Va Bridge Computer (model NV211), and by Systema
as BG2 Bridge Computer.

Hardware notes:
- PCB label: MCL, M1011
- Hitachi HD44860 @ ~800kHz (33K resistor)
- LCD with custom segments, no sound
- comms jack for playing against another Diamond Bridge Computer

Diamond Bridge Computer II (model M1021) has a HMCS400 MCU instead.

TODO:
- add comms port

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

#include "emu.h"

#include "cpu/hmcs40/hmcs40.h"
#include "video/pwm.h"

#include "screen.h"

// internal artwork
#include "dbridgec.lh"


namespace {

class dbridgec_state : public driver_device
{
public:
	dbridgec_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void dbridgec(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(in0_changed) { refresh_irq(); }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD { refresh_irq(); }

private:
	// devices/pointers
	required_device<hmcs40_cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_ioport_array<4> m_inputs;

	u8 m_inp_mux = 0;
	u8 m_lcd_com = 0;
	u64 m_lcd_segs = 0;

	// I/O handlers
	void update_lcd();
	template<int N> void lcd1_w(u8 data);
	void lcd2_w(u16 data);

	u8 read_buttons();
	void refresh_irq();
	u16 input_r();
	template<int N> void input_w(u8 data);
};

void dbridgec_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_lcd_com));
	save_item(NAME(m_lcd_segs));
}



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

void dbridgec_state::update_lcd()
{
	const u8 com1 = BIT(m_lcd_com, 4);
	const u8 com2 = m_lcd_com & 0xf;
	const u64 data = com1 ? m_lcd_segs : ~m_lcd_segs;

	for (int i = 0; i < 4; i++)
		m_display->write_row(i, (BIT(com2, i) == com1) ? data : 0);
}

template<int N>
void dbridgec_state::lcd1_w(u8 data)
{
	// R0x-R6x: LCD segments
	const u8 shift = N * 4;
	m_lcd_segs = (m_lcd_segs & ~(u64(0xf << shift))) | (u64(data) << shift);
	update_lcd();
}

void dbridgec_state::lcd2_w(u16 data)
{
	// D0-D4: LCD common
	m_lcd_com = data & 0x1f;

	// D9,D10,D12-D15: more LCD segments
	const u8 segs = (data >> 9 & 3) | (data >> 10 & 0x3c);
	m_lcd_segs = (m_lcd_segs & 0x0fff'ffffULL) | u64(segs) << 28;
	update_lcd();

	// D5: comms port
}

u8 dbridgec_state::read_buttons()
{
	u8 data = 0;

	for (int i = 0; i < 4; i++)
		if (m_inp_mux & m_inputs[i]->read())
			data |= 1 << i;

	return data;
}

void dbridgec_state::refresh_irq()
{
	// right button column goes to MCU INT0
	m_maincpu->set_input_line(0, (read_buttons() & 1) ? CLEAR_LINE : ASSERT_LINE);
}

u16 dbridgec_state::input_r()
{
	// D6-D8: read buttons
	u16 data = read_buttons() << 5 & 0x1c0;
	return ~data;

	// D5: comms port
}

template<int N>
void dbridgec_state::input_w(u8 data)
{
	// R4x,R5x: input mux
	const u8 shift = N * 4;
	m_inp_mux = (m_inp_mux & ~(0xf << shift)) | ((data ^ 0xf) << shift);
	refresh_irq();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

#define IN0_CHANGED(x) \
	PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dbridgec_state::in0_changed), 0)

static INPUT_PORTS_START( dbridgec )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) IN0_CHANGED() PORT_CODE(KEYCODE_U) PORT_NAME("Dummy")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) IN0_CHANGED() PORT_CODE(KEYCODE_T) PORT_NAME("Partner")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) IN0_CHANGED() PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) IN0_CHANGED() PORT_CODE(KEYCODE_H) PORT_NAME("Change Side")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) IN0_CHANGED() PORT_CODE(KEYCODE_S) PORT_NAME("Score")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) IN0_CHANGED() PORT_CODE(KEYCODE_R) PORT_NAME("New Rubber")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) IN0_CHANGED() PORT_CODE(KEYCODE_D) PORT_NAME("Deal")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("Pass")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Double")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("No Trump")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("Clubs")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Diamonds")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Hearts")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("Spades")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("A")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_K) PORT_NAME("K")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Q")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_J) PORT_NAME("J")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("10")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void dbridgec_state::dbridgec(machine_config &config)
{
	// basic machine hardware
	HD44860(config, m_maincpu, 800'000); // approximation
	m_maincpu->write_r<0>().set(FUNC(dbridgec_state::lcd1_w<0>));
	m_maincpu->write_r<1>().set(FUNC(dbridgec_state::lcd1_w<1>));
	m_maincpu->write_r<2>().set(FUNC(dbridgec_state::lcd1_w<2>));
	m_maincpu->write_r<3>().set(FUNC(dbridgec_state::lcd1_w<3>));
	m_maincpu->write_r<4>().set(FUNC(dbridgec_state::lcd1_w<4>));
	m_maincpu->write_r<4>().append(FUNC(dbridgec_state::input_w<0>));
	m_maincpu->write_r<5>().set(FUNC(dbridgec_state::lcd1_w<5>));
	m_maincpu->write_r<5>().append(FUNC(dbridgec_state::input_w<1>));
	m_maincpu->write_r<6>().set(FUNC(dbridgec_state::lcd1_w<6>));
	m_maincpu->write_d().set(FUNC(dbridgec_state::lcd2_w));
	m_maincpu->read_d().set(FUNC(dbridgec_state::input_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 34);
	m_display->set_bri_levels(0.1);
	config.set_default_layout(layout_dbridgec);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/1.5, 1068/1.5);
	screen.set_visarea_full();
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( dbridgec )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD("hd44860_b29.u1", 0x0000, 0x2000, CRC(9ebb51e0) SHA1(0e99f4247ba516cd93bec889faebf1b6ba22f361) )
	ROM_IGNORE( 0x2000 ) // ignore factory test banks

	ROM_REGION( 234761, "screen", 0 )
	ROM_LOAD("dbridgec.svg", 0, 234761, CRC(24834e57) SHA1(3e10afefa6ef112cf1bb339bf9dd25ed0ca4c150) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1987, dbridgec, 0,      0,      dbridgec, dbridgec, dbridgec_state, empty_init, "Micro-Concepts", "Diamond Bridge Computer", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW | MACHINE_NODEVICE_LAN )



dccons.cpp
<---------------------------------------------------------------------->
// license:LGPL-2.1+
// copyright-holders:Angelo Salese, R. Belmont
/*
    dccons.cpp - Sega Dreamcast driver
    by R. Belmont & Angelo Salese

    SH-4 @ 200 MHz
    ARM7DI @ 2.8223 MHz (no T or M extensions)
    PowerVR 3D video
    AICA audio
    GD-ROM drive (modified ATAPI interface)

            NTSC/N  NTSC/I   PAL/N   PAL/I   VGA
        (x/240) (x/480) (x/240)  (x/480) (640x480)
    VTOTAL   262     524      312     624    524
    HTOTAL   857     857      863     863    857

    PCLKs = 26917135 (NTSC 480 @ 59.94), 26944080 (VGA 480 @ 60.0), 13458568 (NTSC 240 @ 59.94),
            25925600 (PAL 480 @ 50.00), 13462800 (PAL 240 @ 50.00)

    TODO:
    - cfr. naomi.cpp header for general DC notes;
    - https://github.com/flyinghead/flycast/blob/master/docs/Notable%20game%20bugs.md
      For a comprehensive list of issues to be verified;
    - Fix Check-GD disk usability, cfr. notes in dc.xml;
    - G1 i/f for GD-ROM needs to be converted in device class and hunted for unsupported features;
    - VMU;
    - Modem;
    - regtest dc.xml against region setting, find the bad chance that a SW item
      managed to brick a dcjp and turning into PAL. Notable symptoms:
      - crzytaxi2j will start with 50Hz/60Hz screen selector;
      - jojobaj will run in PAL mode;

    Notes:
    - RTC error pops up at start-up. (btanb)
      System saves to flash the AICA RTC timestamp and prompt user to change the clock in
      case that drifts out significantly during boot.
      Solution is to insert the actual host PC timestamp and have MAME nvram_save on,
      that will lift the RTC prompt at next boot.

    Old TODO (to be rechecked and moved in XML notes):
    - Inputs doesn't work most of the time;
    - Candy Stripe: fills the log with "ATAPI_FEATURES_FLAG_OVL not supported", black screen
    - Close To: Hangs at FMV
    - Power Stone: hangs at Capcom logo;
    - Sega GT: no cursor on main menu;
    - Tetris 4D: hangs at BPS FMV (bp 0C0B0C4E)

    Notes:
    - 0x1a002 of flash ROM returns the region type (0x30=Japan, 0x31=USA, 0x32=Europe). Amusingly, if the value
      on a non-jp console is different than these ones, the system shows a black swirl (and nothing boots).

*/

#include "emu.h"
#include "dccons.h"

#include "dc-ctrl.h"

#include "bus/ata/gdrom.h"
#include "cpu/arm7/arm7.h"
#include "cpu/sh/sh4.h"
#include "imagedev/cdromimg.h"
#include "machine/aicartc.h"

//#include "emupal.h"
#include "screen.h"
#include "speaker.h"
#include "softlist.h"

#define CPU_CLOCK (200000000)
// cfr. sh4.cpp m_mmuhack
#define DC_MMU_HACK_MODE (1)

void dc_cons_state::init_dc()
{
	m_maincpu->sh2drc_set_options(SH2DRC_STRICT_VERIFY | SH2DRC_STRICT_PCREL);
	m_maincpu->sh2drc_add_fastram(0x00000000, 0x001fffff, true, memregion("maincpu")->base());
	m_maincpu->sh2drc_add_fastram(0x0c000000, 0x0cffffff, false, dc_ram);
	dreamcast_atapi_init();
}

void dc_cons_state::init_tream()
{
	// Modchip connected to BIOS ROM chip changes 4 bytes (actually bits) as shown below, which allow to boot any region games.
	u8 *rom = (u8 *)memregion("maincpu")->base();
	rom[0x503] |= 0x40;
	rom[0x50f] |= 0x40;
	rom[0x523] |= 0x40;
	rom[0x531] |= 0x40;

	init_dc();
}

uint64_t dc_cons_state::dc_pdtra_r()
{
	uint64_t out = PCTRA<<32;

	out |= PDTRA & ~0x0303;

	// if both bits are inputs
	if (!(PCTRA & 0x5))
	{
		out |= 0x03;
	}

	// one's input one's output, always pull up both bits
	if (((PCTRA & 5) == 1) || ((PCTRA & 5) == 4))
	{
		if (PDTRA & 3)
		{
			out |= 0x03;
		}
	}


	// cable setting, (0) VGA, (2) TV RGB (3) TV VBS/Y + S/C.
	// Note: several games doesn't like VGA setting,
	// default to composite for max possible compatibility
	// (i.e. Idol Janshi wo Tsukucchaou, Airforce Delta)
	// TODO: identify via script
	out |= ioport("SCREEN_TYPE")->read() << 8;

	return out;
}

void dc_cons_state::dc_pdtra_w(uint64_t data)
{
	PCTRA = (data>>16) & 0xffff;
	PDTRA = (data & 0xffff);
}

uint8_t dc_cons_state::dc_flash_r(offs_t offset)
{
	return m_dcflash->read(offset+0x20000);
}

void dc_cons_state::dc_flash_w(offs_t offset, uint8_t data)
{
	m_dcflash->write(offset+0x20000,data);
}

void dc_cons_state::dc_map(address_map &map)
{
	map(0x00000000, 0x001fffff).rom().nopw();             // BIOS
	map(0x00200000, 0x0021ffff).rw(FUNC(dc_cons_state::dc_flash_r), FUNC(dc_cons_state::dc_flash_w));
	map(0x005f6800, 0x005f69ff).rw(FUNC(dc_cons_state::dc_sysctrl_r), FUNC(dc_cons_state::dc_sysctrl_w));
	map(0x005f6c00, 0x005f6cff).m(m_maple, FUNC(maple_dc_device::amap));
	map(0x005f7000, 0x005f701f).rw(m_ata, FUNC(ata_interface_device::cs1_r), FUNC(ata_interface_device::cs1_w)).umask64(0x0000ffff0000ffff);
	map(0x005f7080, 0x005f709f).rw(m_ata, FUNC(ata_interface_device::cs0_r), FUNC(ata_interface_device::cs0_w)).umask64(0x0000ffff0000ffff);
	map(0x005f7400, 0x005f74ff).rw(FUNC(dc_cons_state::dc_mess_g1_ctrl_r), FUNC(dc_cons_state::dc_mess_g1_ctrl_w));
	map(0x005f7800, 0x005f78ff).m(m_g2if, FUNC(dc_g2if_device::amap));
	map(0x005f7c00, 0x005f7cff).m(m_powervr2, FUNC(powervr2_device::pd_dma_map));
	map(0x005f8000, 0x005f9fff).m(m_powervr2, FUNC(powervr2_device::ta_map));
	map(0x00600000, 0x006007ff).rw(FUNC(dc_cons_state::dc_modem_r), FUNC(dc_cons_state::dc_modem_w));
	map(0x00700000, 0x00707fff).rw(FUNC(dc_cons_state::dc_aica_reg_r), FUNC(dc_cons_state::dc_aica_reg_w));
	map(0x00710000, 0x0071000f).mirror(0x02000000).rw("aicartc", FUNC(aicartc_device::read), FUNC(aicartc_device::write)).umask64(0x0000ffff0000ffff);
	map(0x00800000, 0x009fffff).mirror(0x02000000).rw(FUNC(dc_cons_state::soundram_r), FUNC(dc_cons_state::soundram_w));
//  map(0x01000000, 0x01ffffff) G2 Ext Device #1
//  map(0x02700000, 0x02707fff) AICA reg mirror
//  map(0x02800000, 0x02ffffff) AICA wave mem mirror (loopchk g2 bus DMA test)

//  map(0x03000000, 0x03ffffff) G2 Ext Device #2

	/* Area 1 */
	map(0x04000000, 0x04ffffff).ram().share("dc_texture_ram");      // texture memory 64 bit access
	map(0x05000000, 0x05ffffff).ram().share("frameram"); // apparently this actually accesses the same memory as the 64-bit texture memory access, but in a different format, keep it apart for now

	/* Area 3 */
	map(0x0c000000, 0x0cffffff).ram().share("dc_ram");
	map(0x0d000000, 0x0dffffff).ram().share("dc_ram");// extra ram on Naomi (mirror on DC)
	map(0x0e000000, 0x0effffff).ram().share("dc_ram");// mirror
	map(0x0f000000, 0x0fffffff).ram().share("dc_ram");// mirror

	/* Area 4 */
	map(0x10000000, 0x107fffff).w(m_powervr2, FUNC(powervr2_device::ta_fifo_poly_w));
	map(0x10800000, 0x10ffffff).w(m_powervr2, FUNC(powervr2_device::ta_fifo_yuv_w));
	map(0x11000000, 0x117fffff).w(m_powervr2, FUNC(powervr2_device::ta_texture_directpath0_w)).mirror(0x00800000);  // access to texture / framebuffer memory (either 32-bit or 64-bit area depending on SB_LMMODE0 register - cannot be written directly, only through dma / store queue

	map(0x12000000, 0x127fffff).w(m_powervr2, FUNC(powervr2_device::ta_fifo_poly_w));
	map(0x12800000, 0x12ffffff).w(m_powervr2, FUNC(powervr2_device::ta_fifo_yuv_w));
	map(0x13000000, 0x137fffff).w(m_powervr2, FUNC(powervr2_device::ta_texture_directpath1_w)).mirror(0x00800000); // access to texture / framebuffer memory (either 32-bit or 64-bit area depending on SB_LMMODE1 register - cannot be written directly, only through dma / store queue

//  map(0x14000000, 0x17ffffff) G2 Ext Device #3

	map(0xf4000000, 0xf4003fff).noprw(); // SH-4 operand cache address array
}

void dc_cons_state::dc_port(address_map &map)
{
	map(0x00000000, 0x00000007).rw(FUNC(dc_cons_state::dc_pdtra_r), FUNC(dc_cons_state::dc_pdtra_w));
}

void dc_cons_state::dc_audio_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000000, 0x001fffff).rw(FUNC(dc_cons_state::soundram_r), FUNC(dc_cons_state::soundram_w));        /* shared with SH-4 */
	map(0x00800000, 0x00807fff).rw(FUNC(dc_cons_state::dc_arm_aica_r), FUNC(dc_cons_state::dc_arm_aica_w));
}

void dc_cons_state::aica_map(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x1fffff).ram().share("dc_sound_ram");
}

static INPUT_PORTS_START( dc )
	PORT_START("P1:0")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(1) PORT_NAME("P1 RIGHT")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(1) PORT_NAME("P1 LEFT")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(1) PORT_NAME("P1 DOWN")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(1) PORT_NAME("P1 UP")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START1 ) PORT_NAME("P1 START")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("P1 A")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("P1 B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) //PORT_NAME("P1 C")

	PORT_START("P1:1")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) // 2nd directional pad
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED ) //PORT_NAME("P1 D")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("P1 X")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("P1 Y")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) //PORT_NAME("P1 Z")

	PORT_START("P1:A0")
	PORT_BIT( 0xff, 0x00, IPT_PEDAL2 ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(25) PORT_KEYDELTA(10) PORT_CENTERDELTA(10) PORT_NAME("P1 R") PORT_PLAYER(1)

	PORT_START("P1:A1")
	PORT_BIT( 0xff, 0x00, IPT_PEDAL ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(25) PORT_KEYDELTA(10) PORT_CENTERDELTA(10) PORT_NAME("P1 L") PORT_PLAYER(1)

	PORT_START("P1:A2")
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_X ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(25) PORT_KEYDELTA(10) PORT_CENTERDELTA(10) PORT_PLAYER(1)

	PORT_START("P1:A3")
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_Y ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(25) PORT_KEYDELTA(10) PORT_CENTERDELTA(10) PORT_PLAYER(1)

	//A4 - A5, second analog stick, unused on DC

	PORT_START("P2:0")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(2) PORT_NAME("P2 RIGHT")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(2) PORT_NAME("P2 LEFT")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(2) PORT_NAME("P2 DOWN")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(2) PORT_NAME("P2 UP")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START2 ) PORT_NAME("P2 START")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 A")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) //PORT_NAME("P2 C")

	PORT_START("P2:1")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) // 2nd directional pad
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED ) //PORT_NAME("P1 D")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("P2 X")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("P2 Y")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) //PORT_NAME("P1 Z")

	PORT_START("P2:A0")
	PORT_BIT( 0xff, 0x00, IPT_PEDAL2 ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(25) PORT_KEYDELTA(10) PORT_CENTERDELTA(10) PORT_NAME("P2 R") PORT_PLAYER(2)

	PORT_START("P2:A1")
	PORT_BIT( 0xff, 0x00, IPT_PEDAL ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(25) PORT_KEYDELTA(10) PORT_CENTERDELTA(10) PORT_NAME("P2 L") PORT_PLAYER(2)

	PORT_START("P2:A2")
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_X ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(25) PORT_KEYDELTA(10) PORT_CENTERDELTA(10) PORT_PLAYER(2)

	PORT_START("P2:A3")
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_Y ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(25) PORT_KEYDELTA(10) PORT_CENTERDELTA(10) PORT_PLAYER(2)

	//A4 - A5, second analog stick, unused on DC

	PORT_START("P3:0")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(3) PORT_NAME("P3 RIGHT")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(3) PORT_NAME("P3 LEFT")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(3) PORT_NAME("P3 DOWN")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(3) PORT_NAME("P3 UP")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START3 ) PORT_NAME("P3 START")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(3) PORT_NAME("P3 A")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(3) PORT_NAME("P3 B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) //PORT_NAME("P3 C")

	PORT_START("P3:1")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) // 2nd directional pad
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED ) //PORT_NAME("P3 D")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(3) PORT_NAME("P3 X")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(3) PORT_NAME("P3 Y")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) //PORT_NAME("P3 Z")

	PORT_START("P3:A0")
	PORT_BIT( 0xff, 0x00, IPT_PEDAL2 ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(25) PORT_KEYDELTA(10) PORT_CENTERDELTA(10) PORT_NAME("P3 R") PORT_PLAYER(3)

	PORT_START("P3:A1")
	PORT_BIT( 0xff, 0x00, IPT_PEDAL ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(25) PORT_KEYDELTA(10) PORT_CENTERDELTA(10) PORT_NAME("P3 L") PORT_PLAYER(3)

	PORT_START("P3:A2")
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_X ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(25) PORT_KEYDELTA(10) PORT_CENTERDELTA(10) PORT_PLAYER(3)

	PORT_START("P3:A3")
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_Y ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(25) PORT_KEYDELTA(10) PORT_CENTERDELTA(10) PORT_PLAYER(3)

	//A4 - A5, second analog stick, unused on DC

	PORT_START("P4:0")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(4) PORT_NAME("P4 RIGHT")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(4) PORT_NAME("P4 LEFT")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(4) PORT_NAME("P4 DOWN")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(4) PORT_NAME("P4 UP")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START4 ) PORT_NAME("P4 START")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(4) PORT_NAME("P4 A")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(4) PORT_NAME("P4 B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) //PORT_NAME("P4 C")

	PORT_START("P4:1")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) // 2nd directional pad
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED ) //PORT_NAME("P3 D")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(4) PORT_NAME("P4 X")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(4) PORT_NAME("P4 Y")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) //PORT_NAME("P3 Z")

	PORT_START("P4:A0")
	PORT_BIT( 0xff, 0x00, IPT_PEDAL2 ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(25) PORT_KEYDELTA(10) PORT_CENTERDELTA(10) PORT_NAME("P4 R") PORT_PLAYER(4)

	PORT_START("P4:A1")
	PORT_BIT( 0xff, 0x00, IPT_PEDAL ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(25) PORT_KEYDELTA(10) PORT_CENTERDELTA(10) PORT_NAME("P4 L") PORT_PLAYER(4)

	PORT_START("P4:A2")
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_X ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(25) PORT_KEYDELTA(10) PORT_CENTERDELTA(10) PORT_PLAYER(4)

	PORT_START("P4:A3")
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_Y ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(25) PORT_KEYDELTA(10) PORT_CENTERDELTA(10) PORT_PLAYER(4)

	//A4 - A5, second analog stick, unused on DC

	PORT_START("SCREEN_TYPE")
	PORT_CONFNAME( 0x03, 0x03, "Screen Connection Type" )
	PORT_CONFSETTING(    0x00, "VGA" )
	PORT_CONFSETTING(    0x02, "Composite" )
	PORT_CONFSETTING(    0x03, "S-Video" )
INPUT_PORTS_END

static INPUT_PORTS_START( dcfish )
	PORT_START("P1:0")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START1 ) PORT_NAME("CONFIG")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("SCREEN_TYPE")
	PORT_CONFNAME( 0x03, 0x03, "Screen Connection Type" )
	PORT_CONFSETTING(    0x00, "VGA" )
	PORT_CONFSETTING(    0x02, "Composite" )
	PORT_CONFSETTING(    0x03, "S-Video" )
INPUT_PORTS_END

void dc_cons_state::gdrom_config(device_t *device)
{
	cdda_device *cdda = device->subdevice<cdda_device>("cdda");
	cdda->audio_end_cb().set(*device, FUNC(gdrom_device::cdda_end_mark_cb));
	cdda->add_route(0, "^^aica", 1.0, 0);
	cdda->add_route(1, "^^aica", 1.0, 1);
}

void dc_cons_state::dc_base(machine_config &config)
{
	/* basic machine hardware */
	SH7091(config, m_maincpu, CPU_CLOCK);
	m_maincpu->set_md(0, 1);
	m_maincpu->set_md(1, 0);
	m_maincpu->set_md(2, 1);
	m_maincpu->set_md(3, 0);
	m_maincpu->set_md(4, 0);
	m_maincpu->set_md(5, 1);
	m_maincpu->set_md(6, 0);
	m_maincpu->set_md(7, 1);
	m_maincpu->set_md(8, 0);
	m_maincpu->set_sh4_clock(CPU_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &dc_cons_state::dc_map);
	m_maincpu->set_addrmap(AS_IO, &dc_cons_state::dc_port);
	m_maincpu->set_mmu_hacktype(DC_MMU_HACK_MODE);

	TIMER(config, "scantimer").configure_scanline(FUNC(dc_state::dc_scanline), "screen", 0, 1);

	system_bus_config(config, "maincpu");

	ARM7(config, m_soundcpu, ((XTAL(33'868'800)*2)/3)/8);   // AICA bus clock is 2/3rds * 33.8688.  ARM7 gets 1 bus cycle out of each 8.
	m_soundcpu->set_addrmap(AS_PROGRAM, &dc_cons_state::dc_audio_map);

	MCFG_MACHINE_RESET_OVERRIDE(dc_cons_state,dc_console )

	FUJITSU_29LV002TC(config, "dcflash");

	MAPLE_DC(config, m_maple, 0, m_maincpu);
	m_maple->irq_callback().set(FUNC(dc_state::maple_irq));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	// TODO: find exact pclk source
	screen.set_raw(13458568*2, 857, 0, 640, 524, 0, 480);
	screen.set_screen_update("powervr2", FUNC(powervr2_device::screen_update));

	POWERVR2(config, m_powervr2, 0);
	m_powervr2->irq_callback().set(FUNC(dc_state::pvr_irq));

	SPEAKER(config, "speaker", 2).front();

	AICA(config, m_aica, (XTAL(33'868'800)*2)/3); // 67.7376MHz(2*33.8688MHz), div 3 for audio block
	m_aica->irq().set(FUNC(dc_state::aica_irq));
	m_aica->main_irq().set(FUNC(dc_state::sh4_aica_irq));
	m_aica->set_addrmap(0, &dc_cons_state::aica_map);
	m_aica->add_route(0, "speaker", 0.4, 0);
	m_aica->add_route(1, "speaker", 0.4, 1);

	AICARTC(config, "aicartc", XTAL(32'768));

	ATA_INTERFACE(config, m_ata, 0);
	m_ata->irq_handler().set(FUNC(dc_cons_state::ata_interrupt));

	ata_slot_device &ata_0(*subdevice<ata_slot_device>("ata:0"));
	ata_0.option_add("gdrom", ATAPI_GDROM);
	ata_0.set_option_machine_config("gdrom", gdrom_config);
	ata_0.set_default_option("gdrom");
}

void dc_cons_state::dc(machine_config &config)
{
	dc_base(config);

	dc_controller_device &dcctrl0(DC_CONTROLLER(config, "dcctrl0", 0, m_maple, 0));
	dcctrl0.set_port_tags("P1:0", "P1:1", "P1:A0", "P1:A1", "P1:A2", "P1:A3", "P1:A4", "P1:A5");
	dc_controller_device &dcctrl1(DC_CONTROLLER(config, "dcctrl1", 0, m_maple, 1));
	dcctrl1.set_port_tags("P2:0", "P2:1", "P2:A0", "P2:A1", "P2:A2", "P2:A3", "P2:A4", "P2:A5");
	dc_controller_device &dcctrl2(DC_CONTROLLER(config, "dcctrl2", 0, m_maple, 2));
	dcctrl2.set_port_tags("P3:0", "P3:1", "P3:A0", "P3:A1", "P3:A2", "P3:A3", "P3:A4", "P3:A5");
	dc_controller_device &dcctrl3(DC_CONTROLLER(config, "dcctrl3", 0, m_maple, 3));
	dcctrl3.set_port_tags("P4:0", "P4:1", "P4:A0", "P4:A1", "P4:A2", "P4:A3", "P4:A4", "P4:A5");

	SOFTWARE_LIST(config, "gdrom_list").set_original("dc");
	// TODO: hookup Mil-CD/multisession CD-ROMs SW list (later DC models don't support this)
	// TODO: hookup Video CD SW list (thru DreamMovie VCD/MP3 player disc + remote dongle)
}

void dc_cons_state::dc_fish(machine_config &config)
{
	dc_base(config);

	dc_controller_device &dcctrl0(DC_CONTROLLER(config, "dcctrl0", 0, m_maple, 0));
	dcctrl0.set_port_tag<0>("P1:0");
}

#define ROM_LOAD_BIOS(bios,name,offset,length,hash) \
		ROMX_LOAD(name, offset, length, hash, ROM_GROUPWORD | ROM_BIOS(bios))

// known undumped or private BIOS revisions:
// "MPR-21068 SEGA JAPAN / 9850 D" from VA0 837-13392-02 (171-7782B) NTSC-J unit
// KABUTO Ver.1.011 CRC 34DA5C88 from pre-release US unit (private)

// MPR-21933 (5v/VA0) confirmed match MPR-21931 (3.3v/VA1) - v1.01d
// actual mask rom labels may have -X1 or -X2 added depending on chip manufacturer, contents is the same

#define DREAMCAST_COMMON_BIOS \
	ROM_REGION(0x200000, "maincpu", 0) \
	ROM_SYSTEM_BIOS(0, "101d", "v1.01d (World)") \
	ROM_LOAD_BIOS(0, "mpr-21931.ic501", 0x000000, 0x200000, CRC(89f2b1a1) SHA1(8951d1bb219ab2ff8583033d2119c899cc81f18c) ) \
	ROM_SYSTEM_BIOS(1, "1022", "v1.022 (World)") \
	ROM_LOAD_BIOS(1, "mpr-23588.ic501", 0x000000, 0x200000, CRC(786168f9) SHA1(ba8bbb90fdb29525f24f17055dc2c7b2d7674437) ) \
	ROM_SYSTEM_BIOS(2, "101c", "v1.01c (World)") \
	ROM_LOAD_BIOS(2, "mpr-21871.ic501", 0x000000, 0x200000, CRC(2f551bc5) SHA1(1ede8d5be49116a4c6f3fe0961175469537a0434) ) \
	ROM_SYSTEM_BIOS(3, "101dch", "v1.01d (Chinese hack)") \
	ROM_LOAD_BIOS(3, "dc101d_ch.bin",   0x000000, 0x200000, CRC(a2564fad) SHA1(edc5d3d70a93c935703d26119b37731fd317d2bf) )
// ^^^ dc101d_ch.bin ^^^ is selfmade Chinese translation, doesn't work on real hardware, does it must be here at all ?

/* note: Dreamcast Flash ROMs actually 256KB MBM29F002TC (5v/VA0) or MBM29LV002TC (3.3v/VA1) devices, only 2nd 128KB half is used, A17 pin tied to VCC
   sector SA5 (1A000 - 1BFFF) is read-only, contain information written during manufacture or repair, fully generated by software tool (except predefined list of creators)
struct factory_sector
{
    struct factory_record {
        // everything 'char' below is decimal numbers in ASCII, unless noted else
        char machine_code1; // '0' - Dreamcast, 0xFF - dev.box
        char machine_code2; // '0' - Dreamcast, 0xFF - dev.box
        char country_code;  // 0 - Japan, 1 - America, 2 - Europe
        char language;      // 0 - Japanese, 1 - English, etc
        char broadcast_format;  // 0 - NTSC, 1 - PAL, 2 - PAL-M, 3 - PAL-N
        char machine_name[32];  // ASCII text 'Dreamcast', trail is 0x20 filled
        char tool_number[4];    // software tool #
        char tool_version[2];   // software tool version
        char tool_type[2];  // software tool type: 0 - for MP(mass production?), 1 - for Repair, 2 - for PP
        char year[4];
        char month[2];
        char day[2];
        char hour[2];
        char min[2];
        char serial_number[8];
        char factory_code[4];
        char total_number[16];
        uint8_t sum;        // byte sum of above
        struct {
            uint8_t sum_inv;    // ~(UID byte sum)
            uint8_t sum;        // UID byte sum
            uint8_t id[6];      // UID
        } machine_id;
        uint8_t machine_type;   // FF - Dreamcast
        uint8_t machine_version;// FF - VA0, FE - VA1, FD - VA2, NOTE: present in 1st factory record only, in 2nd always FF
        uint8_t unused[0x40]    // FF filled
    } factory_records[2];       // 2 copies
    uint8_t unused_0[0x36];     // FF filled
    uint8_t unk_version;        // not clear if hardware or bios version, A0 - VA0, 9F - VA1, 9E - VA2
    uint8_t unused_1[9];        // FF filled
    char staff_roll[0xca0];     // list of creators
    uint8_t unused_2[0x420];    // FF filled
    uint8_t random[0xdc0];      // output of RNG {static u32 seed; seed=(seed*0x83d+0x2439)&0x7fff; return (u16)(seed+0xc000);}, where initial seed value is serial_number[7] & 0xf
};

Besides factory sector, each new Dreamcast have "Flash Partition 2" header in SA6 (@1C000) followed by "CID" record:
struct cid_record
{
    uint16_t record_type;           // 0, can be 0-4
    struct cid_data
    {
        uint8_t date[4];            // BCD YYYY/MM/DD
        char t_inferior_code[4];    // '0'-filled in all dumps we have
        char repair_voucher_no[8];  // '0'-filled in all dumps we have
        uint8_t serial_no[8];
        uint8_t factory_code;
        uint8_t order_no[5];
    } cid[2];
    uint16_t crc16;
};
*/

ROM_START(dc)
	DREAMCAST_COMMON_BIOS

	ROM_REGION64_LE(0x040000, "dcflash", ROMREGION_ERASEFF)
	ROM_LOAD( "dcus_ntsc.bin", 0x020000, 0x020000, CRC(4136c25b) SHA1(1efa00ab9d8357a9f91e5be931a3efd6236f2b79) )  // dumped from VA2.4 mobo with 1.022 BIOS
ROM_END

ROM_START( dceu )
	DREAMCAST_COMMON_BIOS

	ROM_REGION64_LE(0x040000, "dcflash", ROMREGION_ERASEFF)
	ROM_LOAD( "dceu_pal.bin",  0x020000, 0x020000, CRC(7a102d05) SHA1(13e444e613dffe0a8bce073a01efa9a1d4626ba7) ) // VA1
	ROM_LOAD( "dceu_pala.bin", 0x020000, 0x020000, CRC(2e8dfa07) SHA1(ca5fd977bbf8f48c28c1027a023b038123d57d39) ) // from VA1 with 1.01d BIOS
ROM_END

ROM_START( dcjp )
	DREAMCAST_COMMON_BIOS
	ROM_SYSTEM_BIOS(4, "1004", "v1.004 (Japan)")    // oldest known mass production version, supports Japan region only
	ROM_LOAD_BIOS(4, "mpr-21068.ic501", 0x000000, 0x200000, CRC(5454841f) SHA1(1ea132c0fbbf07ef76789eadc07908045c089bd6) )

	ROM_REGION64_LE(0x040000, "dcflash", ROMREGION_ERASEFF)
	ROM_LOAD( "dcjp_ntsc.bin", 0x020000, 0x020000, CRC(306023ab) SHA1(5fb66adb6d1b54a552fe9c2bb736e4c6960e447d) ) // from refurbished VA0 with 1.004 BIOS
ROM_END

// unauthorised portable modification
ROM_START( dctream )
	ROM_REGION(0x200000, "maincpu", 0)
	// uses regular mpr-21931 BIOS chip, have region-free mod-chip installed, see driver init.
	ROM_LOAD( "mpr-21931.ic501", 0x000000, 0x200000, CRC(89f2b1a1) SHA1(8951d1bb219ab2ff8583033d2119c899cc81f18c) )

	ROM_REGION64_LE(0x040000, "dcflash", ROMREGION_ERASEFF)
	ROM_LOAD( "dc_flash.bin", 0x020000, 0x020000, CRC(9d5515c4) SHA1(78a86fd4e8b58fc9d3535eef6591178f1b97ecf9) ) // VA1 NTSC-US
ROM_END

// normally, with DIP switch 4 off, HKT-0100/0110/0120 AKA "Katana Set 5.xx", will be booted from flash ROM IC507 (first 2 dumps below)
// otherwise it boots from EPROM which contain system checker software (last 2 dumps)
ROM_START( dcdev )
	ROM_REGION(0x200000, "maincpu", 0)
	ROM_SYSTEM_BIOS(0, "1011", "Katana Set5 v1.011 (World)")    // BOOT flash rom update from Katana SDK R9-R11, WinCE SDK v2.1
	ROM_LOAD_BIOS(0, "set5v1.011.ic507", 0x000000, 0x200000, CRC(2186e0e5) SHA1(6bd18fb83f8fdb56f1941e079580e5dd672a6dad) )
	ROM_SYSTEM_BIOS(1, "1001", "Katana Set5 v1.001 (Japan)")    // BOOT flash rom update from Katana SDK 1.42J and WinCE SDK v1.0
	ROM_LOAD_BIOS(1, "set5v1.001.ic507", 0x000000, 0x200000, CRC(5702d38f) SHA1(ea7a3ae1de73683008dd795c252941a4fc81b42e) )
	ROM_SYSTEM_BIOS(2, "0976", "Katana Set5 v0.976 (Japan)")    // BOOT flash rom update from Katana SDK 1.20J
	ROM_LOAD_BIOS(2, "set5v0.976.ic507", 0x000000, 0x200000, CRC(dcb2e86f) SHA1(c88b4b6704811e3a428ee225727e4f7df467a3b5) )
	ROM_SYSTEM_BIOS(3, "0972", "Katana Set5 v0.972 (Japan)")    // BOOT flash rom update from Katana SDK 1.00b2
	ROM_LOAD_BIOS(3, "set5v0.972.ic507", 0x000000, 0x200000, CRC(1a2f2a91) SHA1(08df891f02cf959189bc9b7c4ac1a4e6a4475b50) )

	// 27C160 EPROM (DIP42) IC??? labeled
	// SET5 7676
	// V0.71 98/11/13
	ROM_SYSTEM_BIOS(4, "071", "Katana Set5 Checker v0.71")
	ROM_LOAD_BIOS(4, "set5v0.71.bin", 0x000000, 0x200000, CRC(52d01969) SHA1(28aec4a01419d2d2a664c540bef30ea289ca0644) )
	// SET5 FC52
	// V0.41 98/08/27
	ROM_SYSTEM_BIOS(5, "041", "Katana Set5 Checker v0.41")
	ROM_LOAD_BIOS(5, "set5v0.41.bin", 0x000000, 0x200000, CRC(485877bd) SHA1(dc1af1f1248ffa87d57bc5ef2ea41aac95ecfc5e) )

	ROM_REGION64_LE(0x040000, "dcflash", ROMREGION_ERASEFF)
	// Dev.Boxes have empty (FF filled) flash ROM
ROM_END

/*    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT  CLASS          INIT       COMPANY FULLNAME */
CONS( 1999, dc,      dcjp,   0,      dc,      dc,    dc_cons_state, init_dc,   "Sega", "Dreamcast (USA, NTSC)", MACHINE_NOT_WORKING )
CONS( 1998, dcjp,    0,      0,      dc,      dc,    dc_cons_state, init_dc,   "Sega", "Dreamcast (Japan, NTSC)", MACHINE_NOT_WORKING )
CONS( 1999, dceu,    dcjp,   0,      dc,      dc,    dc_cons_state, init_dc,   "Sega", "Dreamcast (Europe, PAL)", MACHINE_NOT_WORKING )
CONS( 200?, dctream, dcjp,   0,      dc,      dc,    dc_cons_state, init_tream,"<unknown>", "Treamcast", MACHINE_NOT_WORKING )
CONS( 1998, dcdev,   0,      0,      dc,      dc,    dc_cons_state, init_dc,   "Sega", "HKT-0120 Sega Dreamcast Development Box", MACHINE_NOT_WORKING )

/*
Fish Life - interactive aquarium simulator
Consists of HKS-0300 main unit and HKS-0100 LCD with touch screen

  HKS-0300
  Fish Life
  670-14239A
  (c) 2000 Sega
  components:
    Dreamcast VA1 motherboard
    GD-ROM drive
    PSU
    173-8100B / 837-14049 IC BD SW FL
      backplate with up/down/left/right/A/B/Start buttons
    171-8097B / 837-14046 IC BD FL
      main components:
        315-6211-AB - Dreamcast game controller IC
        315-6182    - Dreamcast microphone controller IC
        connectors

 HKS-0100 touch screen wired to SH-4's SCIF serial port. Communication is one-way, touch packet format is:
 0100000T 00xxxxxx 00XXXXXX 00yyyyyy 00YYYYYY 00--zzzz 00------
  T: 1 - touch, 0 - release
  x: X value low bits
  X: X value high bits
  y: Y value low bits
  Y: Y value high bits
  z: unused
 X/Y values range seems 3000x2294

 HKS-0200 software GD-ROMs:
  HDR-0093 673-01613 Fish Life Red Sea Playful Edition
 *HDR-0094 673-01672 Fish Life Amazon Playful Edition
  HDR-0095 673-01??? Fish Life Episode 1 Basic Edition
  HDR-0096 673-01??? Fish Life Episode 2 Basic Edition
  HDR-0097 673-01??? Fish Life Episode 3 Basic Edition
  MSD-0001 ???-????? Fish Life Red Sea & Amazon PDP Ver.
 * denotes these games are archived.

 Machines high likely based on Fish Life:
  タッチであそぼ！ / Play with a touch! (2001) - touch screen cabinet for McDonald's Japan https://www.famitsu.com/game/news/2001/09/13/103,1000362656,1276,0,0.html
  タッチでポン！ / Pong by touch! (2001) - sushi ordering system https://web.archive.org/web/20180421214402/sega.jp/fb/creators/vol_13/1.html

 notes:
  Some sources claims Playful and Basic editions hardware is not the same, has to be verified.
  Press down+B for Test Mode
*/
ROM_START( dcfish )
	ROM_REGION64_LE(0x200000, "maincpu", 0)
	ROM_LOAD( "mpr-21931.ic501", 0x000000, 0x200000, CRC(89f2b1a1) SHA1(8951d1bb219ab2ff8583033d2119c899cc81f18c) ) // regular v1.0d 3.3v BIOS

	// similar Dreamcast flashes, machine_name was changed to "Fish Life", machine_code2 is 0xff (verified  by software)
	ROM_REGION64_LE(0x040000, "dcflash", ROMREGION_ERASEFF)
	ROM_LOAD( "fish_flash.bin", 0x020000, 0x020000, CRC(f7f36b7b) SHA1(f49d18de85c519c16d5447ca8ae39b62d1b8e483) ) // VA1 NTSC-JP

	DISK_REGION( "ata:0:gdrom" )
	DISK_IMAGE_READONLY( "fish_life_amazon", 0, SHA1(2cbba727b219bbbeddf551d0f3e80c5f8ecbe21f) ) // HDR-0094
ROM_END

/*    YEAR  NAME     PARENT  MACHINE  INPUT  CLASS          INIT       ROT   COMPANY FULLNAME */
GAME( 2000, dcfish,  0,      dc_fish, dcfish,dc_cons_state, init_dc, ROT0, "Sega", "Fish Life Amazon Playful Edition (Japan)", MACHINE_NOT_WORKING ) // requires SH-4 touch screen, crashes on attract mode with DRC



dcebridge.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Vas Crabb
// Simple RS-232 DCE-DCE bridge
#include "emu.h"

#include "bus/rs232/rs232.h"

#include "dcebridge.lh"


namespace {

enum : ioport_value
{
	DTR_SOURCE_MASK         = 0x07,
	DTR_SOURCE_DCD_REMOTE   = 0x00,
	DTR_SOURCE_DSR_REMOTE   = 0x01,
	DTR_SOURCE_CTS_REMOTE   = 0x02,
	DTR_SOURCE_DCD_LOCAL    = 0x03,
	DTR_SOURCE_DSR_LOCAL    = 0x04,
	DTR_SOURCE_CTS_LOCAL    = 0x05,
	DTR_SOURCE_ASSERT       = 0x06,
	DTR_SOURCE_DEASSERT     = 0x07,

	RTS_SOURCE_MASK         = 0x38,
	RTS_SOURCE_DCD_REMOTE   = 0x00,
	RTS_SOURCE_DSR_REMOTE   = 0x08,
	RTS_SOURCE_CTS_REMOTE   = 0x10,
	RTS_SOURCE_DCD_LOCAL    = 0x18,
	RTS_SOURCE_DSR_LOCAL    = 0x20,
	RTS_SOURCE_CTS_LOCAL    = 0x28,
	RTS_SOURCE_ASSERT       = 0x30,
	RTS_SOURCE_DEASSERT     = 0x38,
};


class dcebridge_state : public driver_device
{
public:
	dcebridge_state(machine_config const &mconfig, device_type &type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_ports(*this, "%c", 'a')
		, m_conf(*this, "CONF_%c", 'A')
	{
	}

	template <unsigned N> DECLARE_INPUT_CHANGED_MEMBER(dtr_source);
	template <unsigned N> DECLARE_INPUT_CHANGED_MEMBER(rts_source);

	void dcebridge(machine_config &config);

protected:
	virtual void driver_start() override;
	virtual void driver_reset() override;

private:
	template <unsigned N> void dcd(int state);
	template <unsigned N> void dsr(int state);
	template <unsigned N> void cts(int state);

	template <unsigned N> void update_dtr();
	template <unsigned N> void update_rts();

	required_device_array<rs232_port_device, 2> m_ports;
	required_ioport_array<2> m_conf;

	u8 m_dcd[2] = { 0U, 0U };
	u8 m_dsr[2] = { 0U, 0U };
	u8 m_cts[2] = { 0U, 0U };
};


template <unsigned N> INPUT_CHANGED_MEMBER(dcebridge_state::dtr_source)
{
	update_dtr<N>();
}


template <unsigned N> INPUT_CHANGED_MEMBER(dcebridge_state::rts_source)
{
	update_rts<N>();
}


void dcebridge_state::dcebridge(machine_config &config)
{
	RS232_PORT(config, m_ports[0], default_rs232_devices, nullptr);
	m_ports[0]->rxd_handler().set(m_ports[1], FUNC(rs232_port_device::write_txd));
	m_ports[0]->dcd_handler().set(FUNC(dcebridge_state::dcd<0>));
	m_ports[0]->dcd_handler().append_output("dcd_a");
	m_ports[0]->dsr_handler().set(FUNC(dcebridge_state::dsr<0>));
	m_ports[0]->dsr_handler().append_output("dsr_a");
	m_ports[0]->ri_handler().set_output("ri_a");
	m_ports[0]->cts_handler().set(FUNC(dcebridge_state::cts<0>));
	m_ports[0]->cts_handler().append_output("cts_a");

	RS232_PORT(config, m_ports[1], default_rs232_devices, "null_modem");
	m_ports[1]->rxd_handler().set(m_ports[0], FUNC(rs232_port_device::write_txd));
	m_ports[1]->dcd_handler().set(FUNC(dcebridge_state::dcd<1>));
	m_ports[1]->dcd_handler().append_output("dcd_b");
	m_ports[1]->dsr_handler().set(FUNC(dcebridge_state::dsr<1>));
	m_ports[1]->dsr_handler().append_output("dsr_b");
	m_ports[1]->ri_handler().set_output("ri_b");
	m_ports[1]->cts_handler().set(FUNC(dcebridge_state::cts<1>));
	m_ports[1]->cts_handler().append_output("cts_b");

	config.set_default_layout(layout_dcebridge);
}


void dcebridge_state::driver_start()
{
	save_item(NAME(m_dcd));
	save_item(NAME(m_dsr));
	save_item(NAME(m_cts));
}


void dcebridge_state::driver_reset()
{
	update_dtr<0>();
	update_rts<0>();
	update_dtr<1>();
	update_rts<1>();
}


template <unsigned N> void dcebridge_state::dcd(int state)
{
	if (m_dcd[N] != state)
	{
		m_dcd[N] = state;
		if (started())
		{
			ioport_value const conf_local(m_conf[N]->read());
			if ((conf_local & DTR_SOURCE_MASK) == DTR_SOURCE_DCD_LOCAL)
				m_ports[N]->write_dtr(state);
			if ((conf_local & RTS_SOURCE_MASK) == RTS_SOURCE_DCD_LOCAL)
				m_ports[N]->write_rts(state);

			ioport_value const conf_remote(m_conf[N ^ 1U]->read());
			if ((conf_remote & DTR_SOURCE_MASK) == DTR_SOURCE_DCD_REMOTE)
				m_ports[N ^ 1U]->write_dtr(state);
			if ((conf_remote & RTS_SOURCE_MASK) == RTS_SOURCE_DCD_REMOTE)
				m_ports[N ^ 1U]->write_rts(state);
		}
	}
}


template <unsigned N> void dcebridge_state::dsr(int state)
{
	if (m_dsr[N] != state)
	{
		m_dsr[N] = state;
		if (started())
		{
			ioport_value const conf_local(m_conf[N]->read());
			if ((conf_local & DTR_SOURCE_MASK) == DTR_SOURCE_DSR_LOCAL)
				m_ports[N]->write_dtr(state);
			if ((conf_local & RTS_SOURCE_MASK) == RTS_SOURCE_DSR_LOCAL)
				m_ports[N]->write_rts(state);

			ioport_value const conf_remote(m_conf[N ^ 1U]->read());
			if ((conf_remote & DTR_SOURCE_MASK) == DTR_SOURCE_DSR_REMOTE)
				m_ports[N ^ 1U]->write_dtr(state);
			if ((conf_remote & RTS_SOURCE_MASK) == RTS_SOURCE_DSR_REMOTE)
				m_ports[N ^ 1U]->write_rts(state);
		}
	}
}


template <unsigned N> void dcebridge_state::cts(int state)
{
	if (m_cts[N] != state)
	{
		m_cts[N] = state;
		if (started())
		{
			ioport_value const conf_local(m_conf[N]->read());
			if ((conf_local & DTR_SOURCE_MASK) == DTR_SOURCE_CTS_LOCAL)
				m_ports[N]->write_dtr(state);
			if ((conf_local & RTS_SOURCE_MASK) == RTS_SOURCE_CTS_LOCAL)
				m_ports[N]->write_rts(state);

			ioport_value const conf_remote(m_conf[N ^ 1U]->read());
			if ((conf_remote & DTR_SOURCE_MASK) == DTR_SOURCE_CTS_REMOTE)
				m_ports[N ^ 1U]->write_dtr(state);
			if ((conf_remote & RTS_SOURCE_MASK) == RTS_SOURCE_CTS_REMOTE)
				m_ports[N ^ 1U]->write_rts(state);
		}
	}
}


template <unsigned N> void dcebridge_state::update_dtr()
{
	ioport_value const conf(m_conf[N]->read());
	switch (conf & DTR_SOURCE_MASK)
	{
	case DTR_SOURCE_DCD_REMOTE:
		m_ports[N]->write_dtr(m_dcd[N ^ 1U]);
		break;
	case DTR_SOURCE_DSR_REMOTE:
		m_ports[N]->write_dtr(m_dsr[N ^ 1U]);
		break;
	case DTR_SOURCE_CTS_REMOTE:
		m_ports[N]->write_dtr(m_cts[N ^ 1U]);
		break;
	case DTR_SOURCE_DCD_LOCAL:
		m_ports[N]->write_dtr(m_dcd[N]);
		break;
	case DTR_SOURCE_DSR_LOCAL:
		m_ports[N]->write_dtr(m_dsr[N]);
		break;
	case DTR_SOURCE_CTS_LOCAL:
		m_ports[N]->write_dtr(m_cts[N]);
		break;
	case DTR_SOURCE_ASSERT:
		m_ports[N]->write_dtr(0);
		break;
	case DTR_SOURCE_DEASSERT:
		m_ports[N]->write_dtr(1);
		break;
	}
}


template <unsigned N> void dcebridge_state::update_rts()
{
	ioport_value const conf(m_conf[N]->read());
	switch (conf & RTS_SOURCE_MASK)
	{
	case RTS_SOURCE_DCD_REMOTE:
		m_ports[N]->write_rts(m_dcd[N ^ 1U]);
		break;
	case RTS_SOURCE_DSR_REMOTE:
		m_ports[N]->write_rts(m_dsr[N ^ 1U]);
		break;
	case RTS_SOURCE_CTS_REMOTE:
		m_ports[N]->write_rts(m_cts[N ^ 1U]);
		break;
	case RTS_SOURCE_DCD_LOCAL:
		m_ports[N]->write_rts(m_dcd[N]);
		break;
	case RTS_SOURCE_DSR_LOCAL:
		m_ports[N]->write_rts(m_dsr[N]);
		break;
	case RTS_SOURCE_CTS_LOCAL:
		m_ports[N]->write_rts(m_cts[N]);
		break;
	case RTS_SOURCE_ASSERT:
		m_ports[N]->write_rts(0);
		break;
	case RTS_SOURCE_DEASSERT:
		m_ports[N]->write_rts(1);
		break;
	}
}


INPUT_PORTS_START(dcebridge)
	PORT_START("CONF_A")
	PORT_CONFNAME(DTR_SOURCE_MASK, DTR_SOURCE_DSR_REMOTE, "DTR A Source") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dcebridge_state::dtr_source<0>), 0)
	PORT_CONFSETTING(DTR_SOURCE_DCD_REMOTE, "DCD B")
	PORT_CONFSETTING(DTR_SOURCE_DSR_REMOTE, "DSR B")
	PORT_CONFSETTING(DTR_SOURCE_CTS_REMOTE, "CTS B")
	PORT_CONFSETTING(DTR_SOURCE_DCD_LOCAL,  "DCD A Loopback")
	PORT_CONFSETTING(DTR_SOURCE_DSR_LOCAL,  "DSR A Loopback")
	PORT_CONFSETTING(DTR_SOURCE_CTS_LOCAL,  "CTS A Loopback")
	PORT_CONFSETTING(DTR_SOURCE_ASSERT,     "Asserted")
	PORT_CONFSETTING(DTR_SOURCE_DEASSERT,   "Deasserted")
	PORT_CONFNAME(RTS_SOURCE_MASK, RTS_SOURCE_CTS_REMOTE, "RTS A Source") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dcebridge_state::rts_source<0>), 0)
	PORT_CONFSETTING(RTS_SOURCE_DCD_REMOTE, "DCD B")
	PORT_CONFSETTING(RTS_SOURCE_DSR_REMOTE, "DSR B")
	PORT_CONFSETTING(RTS_SOURCE_CTS_REMOTE, "CTS B")
	PORT_CONFSETTING(RTS_SOURCE_DCD_LOCAL,  "DCD A Loopback")
	PORT_CONFSETTING(RTS_SOURCE_DSR_LOCAL,  "DSR A Loopback")
	PORT_CONFSETTING(RTS_SOURCE_CTS_LOCAL,  "CTS A Loopback")
	PORT_CONFSETTING(RTS_SOURCE_ASSERT,     "Asserted")
	PORT_CONFSETTING(RTS_SOURCE_DEASSERT,   "Deasserted")

	PORT_START("CONF_B")
	PORT_CONFNAME(DTR_SOURCE_MASK, DTR_SOURCE_DSR_REMOTE, "DTR B Source") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dcebridge_state::dtr_source<1>), 0)
	PORT_CONFSETTING(DTR_SOURCE_DCD_REMOTE, "DCD A")
	PORT_CONFSETTING(DTR_SOURCE_DSR_REMOTE, "DSR A")
	PORT_CONFSETTING(DTR_SOURCE_CTS_REMOTE, "CTS A")
	PORT_CONFSETTING(DTR_SOURCE_DCD_LOCAL,  "DCD B Loopback")
	PORT_CONFSETTING(DTR_SOURCE_DSR_LOCAL,  "DSR B Loopback")
	PORT_CONFSETTING(DTR_SOURCE_CTS_LOCAL,  "CTS B Loopback")
	PORT_CONFSETTING(DTR_SOURCE_ASSERT,     "Asserted")
	PORT_CONFSETTING(DTR_SOURCE_DEASSERT,   "Deasserted")
	PORT_CONFNAME(RTS_SOURCE_MASK, RTS_SOURCE_CTS_REMOTE, "RTS B Source") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dcebridge_state::rts_source<1>), 0)
	PORT_CONFSETTING(RTS_SOURCE_DCD_REMOTE, "DCD A")
	PORT_CONFSETTING(RTS_SOURCE_DSR_REMOTE, "DSR A")
	PORT_CONFSETTING(RTS_SOURCE_CTS_REMOTE, "CTS A")
	PORT_CONFSETTING(RTS_SOURCE_DCD_LOCAL,  "DCD B Loopback")
	PORT_CONFSETTING(RTS_SOURCE_DSR_LOCAL,  "DSR B Loopback")
	PORT_CONFSETTING(RTS_SOURCE_CTS_LOCAL,  "CTS B Loopback")
	PORT_CONFSETTING(RTS_SOURCE_ASSERT,     "Asserted")
	PORT_CONFSETTING(RTS_SOURCE_DEASSERT,   "Deasserted")
INPUT_PORTS_END


ROM_START(dcebridge)
ROM_END

} // anonymous namespace

//    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY      FULLNAME                 FLAGS
SYST( 197?, dcebridge, 0,      0,      dcebridge, dcebridge, dcebridge_state, empty_init, "<generic>", "RS-232 DCE-DCE Bridge", MACHINE_NO_SOUND_HW )



dct11em.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic,Robbbert
/***************************************************************************

DEC DCT11-EM (Evaluation Module)

2010-12-03 Skeleton driver.

System currently works, but with some hacks to replace the unemulated DC319
DLART (Digital Link Asynchronous Receiver/Transmitter).


TODO:
- user LED (it's there but it doesn't work)
- DLART device to be emulated
- hookups between DLART (and its DL terminal), UART and remaining interrupts


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

#include "emu.h"
#include "cpu/t11/t11.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "bus/rs232/rs232.h"
#include "machine/clock.h"
#include "machine/terminal.h"
#include "dct11em.lh"


namespace {

class dct11em_state : public driver_device
{
public:
	dct11em_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ppi(*this, "ppi")
		, m_uart(*this, "uart")
		, m_terminal(*this, "terminal")
		, m_io_keyboard(*this, "X%u", 0U)
		, m_digits(*this, "digit%d", 0U)
		, m_led(*this, "led0")
	{ }

	void dct11em(machine_config &config);
	DECLARE_INPUT_CHANGED_MEMBER(halt_button);
	DECLARE_INPUT_CHANGED_MEMBER(int_button);

private:
	void machine_reset() override ATTR_COLD;
	void machine_start() override ATTR_COLD;

	void porta_w(u8);
	void portc_w(u8);
	u8 portc_r();
	void irq_encoder(u8, bool);
	u8 dlart_r(offs_t);
	void dlart_w(offs_t, u8);
	void kbd_put(u8 data);

	u8 m_term_data = 0U;
	u8 m_seg_lower = 0U;
	u8 m_seg_upper = 0U;
	u8 m_portc = 0U;
	u16 m_irqs = 0U;
	bool m_dlart_maintmode = 0;

	void mem_map(address_map &map) ATTR_COLD;

	required_device<t11_device> m_maincpu;
	required_device<i8255_device> m_ppi;
	required_device<i8251_device> m_uart;
	required_device<generic_terminal_device> m_terminal;
	required_ioport_array<5> m_io_keyboard;
	output_finder<12> m_digits;
	output_finder<> m_led;
};

void dct11em_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).ram();   // 2x 6116
	map(0x1000, 0x1fff).noprw(); // the ram test reads/writes here even though there's no ram
	map(0x2000, 0x2fff).ram();   // expansion sockets, 2x 6116
	map(0xa000, 0xdfff).rom();
	map(0xff20, 0xff27).nopr().lw8(NAME([this] (offs_t offset, u8 data) { m_ppi->write(offset>>1, data); }));
	map(0xff28, 0xff2b).nopr().lw8(NAME([this] (offs_t offset, u8 data) { m_uart->write(offset>>1, data); }));
	map(0xff60, 0xff67).lr8(NAME([this] (offs_t offset) { return m_ppi->read(offset>>1); }));
	map(0xff68, 0xff6b).lr8(NAME([this] (offs_t offset) { return m_uart->read(offset>>1); }));
	//map(0xff70, 0xff7f).   // DC319 DLART unemulated device - uart to terminal
	map(0xff70, 0xff7f).rw(FUNC(dct11em_state::dlart_r), FUNC(dct11em_state::dlart_w));
}

// dummy routines to pass the DLART test and to talk to the ascii terminal
u8 dct11em_state::dlart_r(offs_t offset)
{
	offset >>= 1;
	switch (offset)
	{
		case 0:
			if (m_term_data)
				return 0xff;
			else
				return 0xfd;
		case 1:
			{
				u8 ret = m_term_data;
				irq_encoder(3, 0); // Release IRQ3
				m_term_data = 0;
				return ret;
			}
		default:
			return 0xfd;
	}
}

void dct11em_state::dlart_w(offs_t offset, u8 data)
{
	offset >>= 1;
	switch (offset)
	{
		case 3:
			if (m_dlart_maintmode)
				m_term_data = data;
			else
				m_terminal->write(data);
			break;
		case 2:
			m_dlart_maintmode = BIT(data, 4);
			break;
		default:
			break;
	}
}

void dct11em_state::porta_w(u8 data)
{
	m_seg_lower = data;
	if (BIT(m_portc, 3))
		m_seg_upper = data;
}

void dct11em_state::portc_w(u8 data)
{
	data &= 15;
	m_portc = data;
	if (BIT(data, 3))
	{
		m_seg_upper = m_seg_lower;
		irq_encoder(10, 0); // Release IRQ10
	}
	if (data < 6)
	{
		m_digits[data] = m_seg_lower;
		m_digits[data+6] = m_seg_upper;
	}
	m_led = (data!=9);
}

u8 dct11em_state::portc_r()
{
	if (m_portc < 5)
		return m_io_keyboard[m_portc]->read();
	else
		return 0;
}

/*
 * interrupts (p. 101)
 *
 * IRQ  CPx  Pri Vec Device
 * ---  ---  --- --- ------
 * 15   LLLL 7   140 DLART receiver break
 * 11   LHLL 6   100 External interrupt
 * 10   LHLH 6   104 Keypad/LED scanning
 * 7    HLLL 5   120 8251 receiver
 * 6    HLLH 5   124 8251 transmitter
 * 3    HHLL 4   060 DLART receiver
 * 2    HHLH 4   064 DLART transmitter */
 void dct11em_state::irq_encoder(u8 irq, bool state)
{
	if (state)
		m_irqs |= (1 << irq);
	else
		m_irqs &= ~(1 << irq);

	int i;
	for (i = 15; i > 0; i--)
		if (BIT(m_irqs, i))
			break;

	m_maincpu->set_input_line(t11_device::CP3_LINE, BIT(i, 3) ? ASSERT_LINE : CLEAR_LINE);
	m_maincpu->set_input_line(t11_device::CP2_LINE, BIT(i, 2) ? ASSERT_LINE : CLEAR_LINE);
	m_maincpu->set_input_line(t11_device::CP1_LINE, BIT(i, 1) ? ASSERT_LINE : CLEAR_LINE);
	m_maincpu->set_input_line(t11_device::CP0_LINE, BIT(i, 0) ? ASSERT_LINE : CLEAR_LINE);
}

void dct11em_state::machine_reset()
{
	m_irqs = 0;
}

void dct11em_state::machine_start()
{
	m_digits.resolve();
	m_led.resolve();

	save_item(NAME(m_seg_lower));
	save_item(NAME(m_seg_upper));
	save_item(NAME(m_portc));
	save_item(NAME(m_irqs));
	save_item(NAME(m_term_data));
	save_item(NAME(m_dlart_maintmode));
}


/* Input ports */
static INPUT_PORTS_START( dct11em )
	PORT_START("X0")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("CLR")       PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("1/GOLED")   PORT_CODE(KEYCODE_1) // Go with LEDs
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("4/PROTEC")  PORT_CODE(KEYCODE_4) // Release protection
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("7/PC")      PORT_CODE(KEYCODE_7)
	PORT_START("X1")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("0/CANCEL")  PORT_CODE(KEYCODE_0) // Cancel breakpoints
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("2/CONSOL")  PORT_CODE(KEYCODE_2) // Start console
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("5")         PORT_CODE(KEYCODE_5)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("8/PS")      PORT_CODE(KEYCODE_8)
	PORT_START("X2")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("EXA/ENTER") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("3/BAUD")    PORT_CODE(KEYCODE_3) // Set baud rates
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("6/SP")      PORT_CODE(KEYCODE_6)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("9/WP")      PORT_CODE(KEYCODE_9)
	PORT_START("X3")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("ADV")       PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("BAC")       PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("REG")       PORT_CODE(KEYCODE_R)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("ADR")       PORT_CODE(KEYCODE_MINUS) // Address
	PORT_START("X4")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("SST")       PORT_CODE(KEYCODE_S) // Single-step
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("GO")        PORT_CODE(KEYCODE_X)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("BPT")       PORT_CODE(KEYCODE_B) // Breakpoint
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("FNC")       PORT_CODE(KEYCODE_LALT) // Function
	PORT_START("X5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("HALT")      PORT_CODE(KEYCODE_H) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dct11em_state::halt_button), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("INT")       PORT_CODE(KEYCODE_I) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dct11em_state::int_button), 0)
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(dct11em_state::halt_button)
{
	m_maincpu->set_input_line(t11_device::HLT_LINE, newval ? ASSERT_LINE : CLEAR_LINE);
}

INPUT_CHANGED_MEMBER(dct11em_state::int_button)
{
	m_maincpu->set_input_line(t11_device::PF_LINE, newval ? ASSERT_LINE : CLEAR_LINE);
}

void dct11em_state::kbd_put(u8 data)
{
	m_term_data = data;
	irq_encoder(3, 1); // Assert IRQ3
}

void dct11em_state::dct11em(machine_config &config)
{
	/* basic machine hardware */
	T11(config, m_maincpu, 7'500'000); // 7.5MHz XTAL
	m_maincpu->set_initial_mode(0x1403);  /* according to specs */
	m_maincpu->set_addrmap(AS_PROGRAM, &dct11em_state::mem_map);

	config.set_default_layout(layout_dct11em);

	I8255(config, m_ppi);
	m_ppi->out_pa_callback().set(FUNC(dct11em_state::porta_w));  // segments
	// port B - expansion interface
	m_ppi->in_pc_callback().set(FUNC(dct11em_state::portc_r));   // keyboard
	m_ppi->out_pc_callback().set(FUNC(dct11em_state::portc_w));  // digits

	I8251(config, m_uart, 2'457'600 / 8);
	m_uart->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_uart->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	m_uart->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_uart->rxrdy_handler().set([this] (bool state) { irq_encoder(7, state); });
	m_uart->txrdy_handler().set([this] (bool state) { irq_encoder(6, state); });

	clock_device &inta_clock(CLOCK(config, "inta_clock", 614'400 / 768)); // 800Hz, from DLART pin 25
	inta_clock.signal_handler().set([this] (bool state) { if (state) irq_encoder(10, 1); }); // Assert IRQ10

	//clock_device &dlart_clock(CLOCK(config, "dlart_clock", 2'457'600 / 4)); --> to DLART CLK pin 32

	clock_device &uart_clock(CLOCK(config, "uart_clock", 2'457'600 / 32));   // from DLART pin 34
	uart_clock.signal_handler().set(m_uart, FUNC(i8251_device::write_txc));
	uart_clock.signal_handler().append(m_uart, FUNC(i8251_device::write_rxc));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr)); // connection to host mainframe, does nothing for us
	rs232.rxd_handler().set(m_uart, FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set(m_uart, FUNC(i8251_device::write_dsr));
	rs232.cts_handler().set(m_uart, FUNC(i8251_device::write_cts));

	GENERIC_TERMINAL(config, m_terminal, 0); // Main terminal for now
	m_terminal->set_keyboard_callback(FUNC(dct11em_state::kbd_put));
}

/* ROM definition */
ROM_START( dct11em )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	// Highest address line inverted
	ROM_LOAD16_BYTE( "23-213e4.e53", 0x8000, 0x2000, CRC(bdd82f39) SHA1(347deeff77596b67eee27a39a9c40075fcf5c10d))
	ROM_LOAD16_BYTE( "23-214e4.e45", 0x8001, 0x2000, CRC(b523dae8) SHA1(cd1a64a2bce9730f7a9177d391663919c7f56073))
	ROM_COPY("maincpu", 0x8000, 0xc000, 0x2000)
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY                          FULLNAME    FLAGS */
COMP( 1983, dct11em, 0,      0,      dct11em, dct11em, dct11em_state, empty_init, "Digital Equipment Corporation", "DCT11-EM", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



debut.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Дебют / Дебют-М (Debut) Chess Computer

Released in 1994 in Russian Federation by ЭНЕРГОПРИБОР (Energopribor), Moscow.
It's running the Mirage chess engine by Vladimir Rybinkin, originally made for MS-DOS.
Also sold in 1996 as Феникс (Fenix), same ROM contents as Debut-M.

TODO:
- where does the interrupt come from?
- Debut-M is an updated version? Or is it the same program as Debut with a redesigned case?

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

Hardware notes:
- КР1810ВМ86 (i8086 clone), 16200K XTAL
- КР1810ГФ84 (i8284 clock divider /3)
- 2*КР537РУ10 (2KB*8 RAM), 2*КС573РФ4А or similar (8KB ROM)
- lcd panel (4 7seg digits), 64 chessboard buttons, 16 leds

A bit more detailed, list of other Soviet standard TTL chips used and their equivalents:
- 2*КР580ИР82 = Intel 8282, aka 74573
- 2*К555ЛЛ1 = 7432
- К555ИЛ7 = 74138
- К555ИД10В = 74145
- КМ555КЛ15 = 74251
- К561ЛЕ5А = CD4001
- PC74HC259P = the odd one out

Keypad legend:

АН  - анализ (analysis, switches view info)
ХОД - ходи (force move)
ИНТ - интерактивность (interactivity, switches 1P vs CPU, or 2P)
ПОЗ - позиция (position mode)
ВФ  - выбор фигуры (select piece)
ВП  - возврат (take back 2 plies)
УР  - уровень игры (level)
ВВ  - ввод позиции (enter position)
СБ  - сброс / новая игра (reset / new game)

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

#include "emu.h"

#include "cpu/i86/i86.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "debutm.lh"


namespace {

class debut_state : public driver_device
{
public:
	debut_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_out_digit(*this, "digit%u", 0U),
		m_inputs(*this, "IN.0")
	{ }

	// assume that RESET button is tied to CPU RESET pin
	DECLARE_INPUT_CHANGED_MEMBER(reset_button) { m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE); }

	void debutm(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<i8086_cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	output_finder<4> m_out_digit;
	required_ioport m_inputs;

	u8 m_latch[5] = { };
	u8 m_dac_data = 0;
	u8 m_lcd_update = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	// I/O handlers
	INTERRUPT_GEN_MEMBER(interrupt);
	u8 input_r(offs_t offset);
	void latch_w(offs_t offset, u8 data);
	void lcd_update_w(int state);
};

void debut_state::machine_start()
{
	m_out_digit.resolve();

	// register for savestates
	save_item(NAME(m_latch));
	save_item(NAME(m_dac_data));
	save_item(NAME(m_lcd_update));
}



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

INTERRUPT_GEN_MEMBER(debut_state::interrupt)
{
	m_maincpu->set_input_line_and_vector(0, HOLD_LINE, 0xff); // I8086
}

u8 debut_state::input_r(offs_t offset)
{
	u8 data = 0;
	u8 sel = m_latch[0] & 0xf;

	// a1-a3,d0: multiplexed inputs
	// read keypad
	if (sel == 0)
		data = BIT(m_inputs->read(), offset);

	// read chessboard sensors
	else if (sel < 9)
		data = BIT(m_board->read_rank(sel - 1), offset);

	return ~data;
}

void debut_state::latch_w(offs_t offset, u8 data)
{
	u8 mask = 1 << offset;
	u8 prev = m_latch[0];

	// a1-a3,d0: 74259
	// a1-a3,d1-d4: lcd panel 7seg data
	for (int i = 0; i < 5; i++)
		m_latch[i] = (m_latch[i] & ~mask) | (BIT(data, i) ? mask : 0);

	// 74259 q0-q3: input mux/led select
	// 74259 q4,q5: led data
	m_display->matrix(~m_latch[0] >> 4 & 3, 1 << (m_latch[0] & 0xf));

	// 74259 q7: toggle speaker
	if (~m_latch[0] & prev & 0x80)
	{
		m_dac_data ^= 1;
		m_dac->write(m_dac_data);
	}
}

void debut_state::lcd_update_w(int state)
{
	// 8086 S5 also goes to the lcd panel
	if (!state && m_lcd_update)
	{
		u8 xorval = (m_latch[4] & 1) ? 0xff : 0;

		for (int i = 0; i < 4; i++)
			m_out_digit[i] = bitswap<8>(m_latch[i+1] ^ xorval,0,7,4,3,2,1,6,5);
	}

	m_lcd_update = state;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void debut_state::main_map(address_map &map)
{
	map.global_mask(0x7fff);
	map(0x0000, 0x0fff).mirror(0x3000).ram();
	map(0x4000, 0x7fff).rom();
}

void debut_state::main_io(address_map &map)
{
	map(0x00, 0x0f).rw(FUNC(debut_state::input_r), FUNC(debut_state::latch_w)).umask16(0x00ff);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( debutm )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME(u8"АН (Analysis)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME(u8"ХОД (Force Move)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME(u8"ИНТ (Switch 1P/2P)")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME(u8"ПОЗ (Position Mode)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME(u8"ВФ (Select Piece)")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME(u8"ВП (Take Back)")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME(u8"УР (Level)")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME(u8"ВВ (Enter Position)")

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(debut_state::reset_button), 0) PORT_NAME(u8"СБ (Reset)")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void debut_state::debutm(machine_config &config)
{
	// basic machine hardware
	I8086(config, m_maincpu, 16.2_MHz_XTAL / 3);
	m_maincpu->set_periodic_int(FUNC(debut_state::interrupt), attotime::from_hz(380));
	m_maincpu->set_addrmap(AS_PROGRAM, &debut_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &debut_state::main_io);
	m_maincpu->if_handler().set(FUNC(debut_state::lcd_update_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(2, 9);
	m_display->set_bri_maximum(0.5);
	config.set_default_layout(layout_debutm);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( debutm )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("dd12", 0x4000, 0x2000, CRC(0f64ebab) SHA1(e069c387ec01e8786e4fb720630196ac27fac449) ) // no ROM labels, PCB ICs location = "DDxx"
	ROM_LOAD16_BYTE("dd13", 0x4001, 0x2000, CRC(c171f5ac) SHA1(62dc030e3d60172f31f0d14944437b0fd4b53a2a) ) // "
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS        INIT        COMPANY, FULLNAME, FLAGS
SYST( 1994, debutm, 0,      0,      debutm,  debutm, debut_state, empty_init, "Energopribor", "Debut-M", MACHINE_SUPPORTS_SAVE )



decmate2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Preliminary driver for DECmate II & III business PCs.

    The DECmate II & III are the last and in some ways least PDP-8-like
    members of DEC's 12-bit computer line. Based on a Harris HD-6120 CMOS
    LSI CPU with 64K words (advertised as 96 KB) of RAM, they were designed
    mostly to run WPS on OS/278 and are not very compatible with older
    software.

    DECmate II (PC278) abandoned the system-in-a-terminal form factor of the
    VT78 DECstation and VT278 DECmate, adopting the BA25 chassis also used
    in the Rainbow 100. Instead of the custom RX01 or RX02 8-inch disk
    drives (whose use is supported only through the optional PC27X-BA
    adapter), the one or two internal 400 KB RX50 drives are basically
    industry-standard 5.25-inch floppy disk drives, with a CPU interface
    that simulates the old RX8-E using a 8051 MCU with 2 KB of buffer SRAM
    and an off-the-shelf FDC. The CRT9007 CRT controller (which may be
    configured for 80-column or 132-column modes) and LK201 keyboard are the
    same as used in the VT220 display terminal, which the monochrome (white,
    green or amber) VR201 monitor also physically resembles. (As with the
    DECmate's VT100 keyboard, the LK201 may have its PF1 key is painted gold
    to highlight its importance to DECmate software.) DECmate II also differs
    from its CMOS-8 predecessors in using generic 8-bit EPROMs or mask ROMs
    rather than specialized 12-bit ROMs for its "control panel" program.

    DECmate II had three different types of expansion options: the
    aforementioned storage adapter, which also supported a 10 MB hard disk
    interface (RDC51-CA) with its own 8051; a graphics board supporting
    monochrome or color graphics on a second monitor; and an Auxiliary
    Processor Unit. The basic APU (PC27X-AA or -AB) ran CP/M on a Z80 with
    64K of its own RAM. The upgraded XPU (PC27X-AH or PC27X-AJ) could also
    run MS-DOS 2.11 using an additional 8086 CPU with 256K or 512K of RAM.

    DECmate III (PC238) was a repackaging of the DECmate II in a smaller but
    taller box that provides space for only one internal RX50 floppy drive.
    Much of the timing and support logic is compressed into two large PLCC
    gate arrays (DC381 and DC382), with a PLL and PAL16R8 to assist the FDC.
    (One consequence of this reduction is that the printer port is fixed to
    the same baud rate as the keyboard.) The DECmate III's own APU and
    graphics options share a single and unique expansion connector; the
    PC23X-CA graphics card supported RGB color output on a VR241 monitor.
    DEC also later released an enhanced version called DECmate III Plus
    (PC24P), which included a 20 MB RD31 hard disk underneath a half-height
    RX33 floppy drive.

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

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/pdp8/hd6120.h"
#include "cpu/mcs51/mcs51.h"
#include "imagedev/floppy.h"
#include "machine/ay31015.h"
#include "machine/clock.h"
#include "machine/com8116.h"
#include "lk201.h"
#include "machine/wd_fdc.h"
#include "machine/z80sio.h"
#include "video/crt9007.h"
#include "screen.h"


namespace {

class decmate2_state : public driver_device
{
public:
	decmate2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rxcpu(*this, "rxcpu")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, {"fdc:a0", "fdc:a1", "fdc:b0", "fdc:b1"})
		, m_kbduart(*this, "kbduart")
		, m_prtuart(*this, "prtuart")
		, m_mpscc(*this, "mpscc")
		, m_brg(*this, "brg%u", 0U)
		, m_crtc(*this, "crtc")
		, m_ram(*this, "ram")
		, m_cprom(*this, "cprom")
		, m_chargen(*this, "chargen")
		, m_cprom_iview(*this, "cpromi")
		, m_cprom_dview(*this, "cpromd")
		, m_cpsel(false)
		, m_vint(false)
		, m_cpromsel(false)
		, m_eadd0(false)
		, m_com_control_read(false)
		, m_kbd_rflg(false)
		, m_kbd_tflg(false)
		, m_prt_rflg(false)
		, m_prt_tflg(false)
		, m_crtc_addr(0)
		, m_rxdata(0)
		, m_rx_control(0)
		, m_rx_status(0)
	{
	}

	void pc278(machine_config &config);
	void pc238(machine_config &config);
	void init_pc278();
	void init_pc238();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void vint_w(int state);

	void lxmar_w(offs_t offset, u16 data);
	void lxpar_w(offs_t offset, u16 data);
	void lxdar_w(offs_t offset, u16 data);
	void wsr_w(u16 data);
	u16 cprom_switch_r(offs_t offset);
	void pc278_ioclr_w(int state);
	void pc238_ioclr_w(int state);

	void keyboard_dr_w(int state);
	void keyboard_dr_ff_w(int state);
	void keyboard_tbre_w(int state);
	void keyboard_tbre_ff_w(int state);
	void printer_dr_w(int state);
	void printer_dr_ff_w(int state);
	void printer_tbre_w(int state);
	void printer_tbre_ff_w(int state);
	u8 kbdrflg_devctl_r();
	void kbdrflg_set_w(u16 data);
	void kbdrflg_clear_w(u16 data);
	u16 kbduart_r();
	u8 kbdtflg_devctl_r();
	void kbdtflg_set_w(u16 data);
	void kbdtflg_clear_w(u16 data);
	u8 prtrflg_devctl_r();
	void prtrflg_set_w(u16 data);
	void prtrflg_clear_w(u16 data);
	u16 prtuart_r();
	void prttflg_set_w(u16 data);
	void prttflg_clear_w(u16 data);
	u8 prttflg_devctl_r();
	u8 apten_r();

	u8 comreg_devctl_r();
	u16 comreg_r();
	void comreg_w(u16 data);
	void cominit_w(u16 data);

	u8 vint_devctl_r();
	void lscreg_w(u16 data);
	void wrcrtc_w(u16 data);
	u16 rdcrtc_r();
	void video_mod_w(u16 data);

	void modem_w(u16 data);

	void sel_w(u16 data);
	void lcd_w(u16 data);
	u16 xdr_r();
	void xdr_w(u16 data);
	u8 xdr_devctl_r();
	u8 str_devctl_r();
	u8 ser_devctl_r();
	u8 sdn_devctl_r();
	void intr_w(u16 data);
	void rxinit_w(u16 data);

	u8 rx_ldata_r();
	void rx_ldata_w(u8 data);
	u8 rx_hdata_r();
	void rx_hdata_w(u8 data);
	void rx_control_w(u8 data);
	u8 rx_status_r();
	u8 rx_intr_r();
	void rx_sel_w(u8 data);
	u8 rx_rdy_r();

	void inst_map(address_map &map) ATTR_COLD;
	void data_map(address_map &map) ATTR_COLD;
	void pc278_io_map(address_map &map) ATTR_COLD;
	void pc238_io_map(address_map &map) ATTR_COLD;
	void devctl_map(address_map &map) ATTR_COLD;
	void rx_map(address_map &map) ATTR_COLD;

	required_device<hd6120_device> m_maincpu;
	required_device<i8051_device> m_rxcpu;
	required_device<fd1793_device> m_fdc;
	optional_device_array<floppy_connector, 4> m_floppy;
	required_device<ay31015_device> m_kbduart;
	required_device<ay31015_device> m_prtuart;
	required_device<upd7201_device> m_mpscc;
	optional_device_array<com8116_device, 2> m_brg;
	required_device<crt9007_device> m_crtc;
	required_shared_ptr<u16> m_ram;
	required_region_ptr<u16> m_cprom;
	required_region_ptr<u8> m_chargen;
	memory_view m_cprom_iview;
	memory_view m_cprom_dview;

	bool m_cpsel;
	bool m_vint;
	bool m_cpromsel;
	bool m_eadd0;
	bool m_com_control_read;

	bool m_kbd_rflg;
	bool m_kbd_tflg;
	bool m_prt_rflg;
	bool m_prt_tflg;

	u8 m_crtc_addr;
	u16 m_rxdata; // 74LS298, writable from both 6120 and 8051
	u8 m_rx_control;
	u8 m_rx_status;
};

void decmate2_state::init_pc278()
{
	for (u16 addr = 0; addr < 04000; addr++)
	{
		// Shift E113 and E114 ROMs into their proper places and separate even and odd bits of E115
		m_cprom[addr | 04000] = (m_cprom[addr | 04000] & 0xff00) >> 4 | bitswap<4>(m_cprom[addr], 6, 4, 2, 0);
		m_cprom[addr] = (m_cprom[addr] & 0xff00) >> 4 | bitswap<4>(m_cprom[addr], 7, 5, 3, 1);
	}
}

void decmate2_state::init_pc238()
{
	for (u16 addr = 0; addr < 010000; addr++)
		m_cprom[addr] &= 07777;
}

void decmate2_state::machine_start()
{
	if (m_brg[1].found())
	{
		m_brg[1]->str_w(12);
		m_brg[1]->stt_w(0);
	}

	m_kbduart->write_np(1);
	m_kbduart->write_nb2(1);
	m_kbduart->write_nb1(1);
	m_kbduart->write_eps(1);
	m_kbduart->write_tsb(0);
	m_kbduart->write_cs(1);
	m_kbduart->write_swe(0);

	m_prtuart->write_np(1);
	m_prtuart->write_nb2(1);
	m_prtuart->write_nb1(1);
	m_prtuart->write_eps(1);
	m_prtuart->write_tsb(0);
	m_prtuart->write_cs(1);
	m_prtuart->write_swe(0);

	m_fdc->dden_w(0);

	// 7201 modem signal inputs are either pulled up or grounded
	m_mpscc->ctsa_w(0);
	m_mpscc->dcda_w(0);
	m_mpscc->dcdb_w(1);
	m_mpscc->rxb_w(0);
	m_mpscc->synca_w(1);
	m_mpscc->syncb_w(1);

	save_item(NAME(m_cpsel));
	save_item(NAME(m_vint));
	save_item(NAME(m_cpromsel));
	save_item(NAME(m_eadd0));
	save_item(NAME(m_com_control_read));
	save_item(NAME(m_kbd_rflg));
	save_item(NAME(m_kbd_tflg));
	save_item(NAME(m_prt_rflg));
	save_item(NAME(m_prt_tflg));
	save_item(NAME(m_crtc_addr));
	save_item(NAME(m_rxdata));
	save_item(NAME(m_rx_control));
	save_item(NAME(m_rx_status));
}

void decmate2_state::machine_reset()
{
	m_cprom_iview.select(0);
	m_cprom_dview.select(0);
	m_cpromsel = false;
}

u32 decmate2_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	// TODO
	return 0;
}

void decmate2_state::vint_w(int state)
{
	// TODO: synchronize
	m_vint = state;
}

void decmate2_state::lxmar_w(offs_t offset, u16 data)
{
	if (offset == hd6120_device::IFETCH)
		m_cpsel = false;
}

void decmate2_state::lxpar_w(offs_t offset, u16 data)
{
	if (offset == hd6120_device::IFETCH)
		m_cpsel = true;
}

void decmate2_state::lxdar_w(offs_t offset, u16 data)
{
	m_eadd0 = BIT(data, 12); // latched from EMA2
}

void decmate2_state::wsr_w(u16 data)
{
	if (m_cpsel)
	{
		if (BIT(data, 0) != m_cpromsel) // DX11
		{
			m_cprom_iview.select(1);
			m_cpromsel = BIT(data, 0);
		}

		if (BIT(data, 11)) // DX0
			m_cprom_dview.disable();
		else
			m_cprom_dview.select(0);
	}
}

// This is not a precise implementation of the actual RAMDIS control circuit, but is close enough to work just as well
u16 decmate2_state::cprom_switch_r(offs_t offset)
{
	if (m_cpromsel)
	{
		if (!machine().side_effects_disabled())
			m_cprom_iview.disable();
		return m_cprom[offset & 07777];
	}
	else
	{
		if (!machine().side_effects_disabled())
			m_cprom_iview.select(0);
		return m_ram[offset];
	}
}

void decmate2_state::pc278_ioclr_w(int state)
{
	if (!state)
	{
		m_kbd_rflg = false;
		m_kbd_tflg = false;
		m_prt_rflg = false;
		m_prt_tflg = false;
		rxinit_w(0);
		cominit_w(0);
	}
}

void decmate2_state::pc238_ioclr_w(int state)
{
	if (!state)
	{
		rxinit_w(0);
		cominit_w(0);
	}
}

void decmate2_state::keyboard_dr_w(int state)
{
	m_kbd_rflg = state;
}

void decmate2_state::keyboard_dr_ff_w(int state)
{
	// TODO: edge trigger
	if (state)
		m_kbd_rflg = true;
}

void decmate2_state::keyboard_tbre_w(int state)
{
	m_kbd_tflg = state;
}

void decmate2_state::keyboard_tbre_ff_w(int state)
{
	// TODO: edge trigger
	if (state)
		m_kbd_tflg = true;
}

void decmate2_state::printer_dr_w(int state)
{
	m_prt_rflg = state;
}

void decmate2_state::printer_dr_ff_w(int state)
{
	// TODO: edge trigger
	if (state)
		m_prt_rflg = true;
}

void decmate2_state::printer_tbre_w(int state)
{
	m_prt_tflg = state;
}

void decmate2_state::printer_tbre_ff_w(int state)
{
	// TODO: edge trigger
	if (state)
		m_prt_tflg = true;
}

u8 decmate2_state::kbdrflg_devctl_r()
{
	return m_kbd_rflg ? hd6120_device::SKIP : 0;
}

void decmate2_state::kbdrflg_set_w(u16 data)
{
	m_kbd_rflg = true;
}

void decmate2_state::kbdrflg_clear_w(u16 data)
{
	m_kbd_rflg = false;
}

u16 decmate2_state::kbduart_r()
{
	return m_kbduart->receive();
}

u8 decmate2_state::kbdtflg_devctl_r()
{
	return m_kbd_tflg ? hd6120_device::SKIP : 0;
}

void decmate2_state::kbdtflg_set_w(u16 data)
{
	m_kbd_tflg = true;
}

void decmate2_state::kbdtflg_clear_w(u16 data)
{
	m_kbd_tflg = false;
}

u8 decmate2_state::prtrflg_devctl_r()
{
	return m_prt_rflg ? hd6120_device::SKIP : 0;
}

void decmate2_state::prtrflg_set_w(u16 data)
{
	m_prt_rflg = true;
}

void decmate2_state::prtrflg_clear_w(u16 data)
{
	m_prt_rflg = false;
}

u16 decmate2_state::prtuart_r()
{
	return m_prtuart->receive();
}

u8 decmate2_state::prttflg_devctl_r()
{
	return m_prt_tflg ? hd6120_device::SKIP : 0;
}

void decmate2_state::prttflg_set_w(u16 data)
{
	m_prt_tflg = true;
}

void decmate2_state::prttflg_clear_w(u16 data)
{
	m_prt_tflg = false;
}

u8 decmate2_state::apten_r()
{
	// TODO: from pin 4 of printer connector J2
	return 0;
}

u8 decmate2_state::comreg_devctl_r()
{
	if (m_com_control_read)
		return hd6120_device::C1;
	else
		return 0;
}

u16 decmate2_state::comreg_r()
{
	if (m_eadd0)
		return m_mpscc->cb_r();
	else
		return m_mpscc->ca_r();
}

void decmate2_state::comreg_w(u16 data)
{
	if (!m_com_control_read)
	{
		if (m_eadd0)
			m_mpscc->cb_w(data & 0377);
		else
			m_mpscc->ca_w(data & 0377);
	}
	m_com_control_read = BIT(data, 11);
}

void decmate2_state::cominit_w(u16 data)
{
	m_mpscc->reset();
	m_com_control_read = false;
}

u8 decmate2_state::vint_devctl_r()
{
	return m_vint ? hd6120_device::SKIP : 0;
}

void decmate2_state::lscreg_w(u16 data)
{
	m_crtc_addr = data & 077;
}

void decmate2_state::wrcrtc_w(u16 data)
{
	m_crtc->write(m_crtc_addr, data & 0377);
}

u16 decmate2_state::rdcrtc_r()
{
	return m_crtc->read(m_crtc_addr);
}

void decmate2_state::video_mod_w(u16 data)
{
	logerror("%s: Loading %04o into video mod register (loopback %s)\n", machine().describe_context(), data, BIT(data, 4) ? "enabled" : "disabled");
}

void decmate2_state::modem_w(u16 data)
{
	logerror("%s: Writing %04o to modem control register\n", machine().describe_context(), data);
}

void decmate2_state::sel_w(u16 data)
{
	logerror("%s: RX drive pair %c selected\n", machine().describe_context(), BIT(data, 0) ? 'B' : 'A');
	if (BIT(data, 0))
		m_rx_status |= 0x80;
	else
		m_rx_status &= 0x7f;
}

void decmate2_state::lcd_w(u16 data)
{
	logerror("%s: RX %d-bit command %d, drive %d\n", machine().describe_context(), BIT(data, 6) ? 8 : 12, BIT(data, 1, 3), BIT(data, 4));
	m_rxdata = data;
	m_rx_status &= 0xfc;
	if (!BIT(data, 6))
		m_rx_status |= 0x01;
	m_rxcpu->set_input_line(MCS51_INT1_LINE, CLEAR_LINE);
}

u16 decmate2_state::xdr_r()
{
	if (BIT(m_rx_status, 1))
	{
		if (!machine().side_effects_disabled())
			logerror("%s: XDR received %04o from 8051\n", machine().describe_context(), m_rxdata);
		return m_rxdata;
	}
	else
		return 0;
}

void decmate2_state::xdr_w(u16 data)
{
	if (!BIT(m_rx_status, 1))
	{
		logerror("%s: XDR transmit %04o to 8051\n", machine().describe_context(), data);
		m_rxdata = data;
	}
	m_rxcpu->set_input_line(MCS51_INT1_LINE, CLEAR_LINE);
}

u8 decmate2_state::xdr_devctl_r()
{
	if (BIT(m_rx_status, 1))
		return hd6120_device::C1 | (BIT(m_rx_status, 0) ? hd6120_device::C0 : 0);
	else
		return 0;
}

u8 decmate2_state::str_devctl_r()
{
	if (BIT(m_rx_control, 3))
	{
		if (!machine().side_effects_disabled())
			m_rx_control &= 0x07;
		return hd6120_device::SKIP;
	}
	else
		return 0;
}

u8 decmate2_state::ser_devctl_r()
{
	if (BIT(m_rx_control, 2))
	{
		if (!machine().side_effects_disabled())
			m_rx_control &= 0x0b;
		return hd6120_device::SKIP;
	}
	else
		return 0;
}

u8 decmate2_state::sdn_devctl_r()
{
	if (BIT(m_rx_control, 0))
	{
		if (!machine().side_effects_disabled())
			m_rx_control &= 0x0e;
		return hd6120_device::SKIP;
	}
	else
		return 0;
}

void decmate2_state::intr_w(u16 data)
{
	logerror("%s: RX interrupt enable %s\n", machine().describe_context(), BIT(data, 0) ? "set" : "cleared");
}

void decmate2_state::rxinit_w(u16 data)
{
	m_rx_control = 0;
	m_rx_status = (m_rx_status & 0x02) | 0x01;
	m_rxcpu->set_input_line(MCS51_INT0_LINE, ASSERT_LINE);
}

u8 decmate2_state::rx_ldata_r()
{
	return m_rxdata & 0xff;
}

void decmate2_state::rx_ldata_w(u8 data)
{
	m_rxdata = (m_rxdata & 07400) | data;
}

u8 decmate2_state::rx_hdata_r()
{
	return m_rxdata >> 8;
}

void decmate2_state::rx_hdata_w(u8 data)
{
	m_rxdata = (m_rxdata & 0377) | (data & 0x0f) << 8;
}

void decmate2_state::rx_control_w(u8 data)
{
	m_rx_control = data & 0x0d;
	m_rx_status = (m_rx_status & 0xfd) | (data & 0x02);
	m_rxcpu->set_input_line(MCS51_INT0_LINE, CLEAR_LINE);
	m_rxcpu->set_input_line(MCS51_INT1_LINE, ASSERT_LINE);
}

u8 decmate2_state::rx_status_r()
{
	return m_rx_status;
}

u8 decmate2_state::rx_intr_r()
{
	return m_fdc->intrq_r() << 5 | 0xdf;
}

void decmate2_state::rx_sel_w(u8 data)
{
	const int n = BIT(~data, 1, 2);
	if (BIT(data, 3) && m_floppy[n].found())
	{
		floppy_image_device *const img = m_floppy[n]->get_device();
		m_fdc->set_floppy(img);
		if (img != nullptr)
			img->ss_w(BIT(data, 0)); // or inverted?
	}
	else
		m_fdc->set_floppy(nullptr);

	for (int i = 0; i < 4 && m_floppy[i].found(); i++)
	{
		floppy_image_device *img = m_floppy[i]->get_device();
		if (img != nullptr)
			img->mon_w((i & 2) == (~data & 0x14) >> 1 ? 0 : 1);
	}
}

u8 decmate2_state::rx_rdy_r()
{
	return m_fdc->drq_r() << 6 | 1 /*m_fdc->ready_r()*/ << 7 | 0x3f;
}

void decmate2_state::inst_map(address_map &map)
{
	map(0000000, 0177777).ram().share("ram");
	map(0000000, 0177777).view(m_cprom_iview);
	m_cprom_iview[0](0000000, 0007777).mirror(0170000).rom().region("cprom", 0);
	m_cprom_iview[1](0000000, 0177777).r(FUNC(decmate2_state::cprom_switch_r));
}

void decmate2_state::data_map(address_map &map)
{
	map(0000000, 0177777).ram().share("ram");
	map(0000000, 0177777).view(m_cprom_dview);
	m_cprom_dview[0](0000000, 0007777).mirror(0170000).rom().region("cprom", 0);
}

// TODO: almost all I/O devices
// 03/04/07: Control panel interrupt
// 05: Keyboard input
// 06: Video interrupt
// 11: Keyboard output
// 12: CRTC
// 13: Clock
// 14: APU option
// 30: Communications receive
// 31: Communications transmit
// 32: Printer input
// 33: Printer output and baud rate
// 36: Modem control and baud rate
// 75: RX Simulator

void decmate2_state::pc278_io_map(address_map &map)
{
	map(0050, 0050).w(FUNC(decmate2_state::kbdtflg_set_w));
	map(0051, 0051).w(FUNC(decmate2_state::kbdtflg_clear_w));
	map(0054, 0054).mirror(2).w(m_kbduart, FUNC(ay31015_device::transmit)).umask16(0377);
	map(0061, 0061).nopw();
	map(0110, 0110).w(FUNC(decmate2_state::kbdrflg_set_w));
	map(0111, 0111).w(FUNC(decmate2_state::kbdrflg_clear_w));
	map(0112, 0112).nopw();
	map(0114, 0114).mirror(2).r(FUNC(decmate2_state::kbduart_r)).nopw();
	map(0121, 0121).nopw();
	map(0122, 0122).w(FUNC(decmate2_state::lscreg_w));
	map(0124, 0124).w(FUNC(decmate2_state::wrcrtc_w));
	map(0126, 0126).w(FUNC(decmate2_state::video_mod_w));
	map(0127, 0127).r(FUNC(decmate2_state::rdcrtc_r)).nopw();
	map(0320, 0320).w(FUNC(decmate2_state::prtrflg_set_w));
	map(0321, 0321).w(FUNC(decmate2_state::prtrflg_clear_w));
	map(0322, 0322).nopw();
	map(0324, 0324).mirror(2).r(FUNC(decmate2_state::prtuart_r)).nopw();
	map(0330, 0330).w(FUNC(decmate2_state::prttflg_set_w));
	map(0331, 0331).w(FUNC(decmate2_state::prttflg_clear_w));
	map(0334, 0334).mirror(2).w(m_prtuart, FUNC(ay31015_device::transmit)).umask16(0377);
	map(0362, 0362).w(FUNC(decmate2_state::modem_w));
	map(0366, 0366).rw(FUNC(decmate2_state::comreg_r), FUNC(decmate2_state::comreg_w));
	map(0367, 0367).w(FUNC(decmate2_state::cominit_w));
	map(0750, 0750).w(FUNC(decmate2_state::sel_w));
	map(0751, 0751).w(FUNC(decmate2_state::lcd_w));
	map(0752, 0752).rw(FUNC(decmate2_state::xdr_r), FUNC(decmate2_state::xdr_w));
	map(0753, 0755).nopw();
	map(0756, 0756).w(FUNC(decmate2_state::intr_w));
	map(0757, 0757).w(FUNC(decmate2_state::rxinit_w));
}

void decmate2_state::pc238_io_map(address_map &map)
{
	pc278_io_map(map);
	map(0050, 0051).nopw();
	map(0110, 0111).nopw();
	map(0320, 0321).nopw();
	map(0330, 0331).nopw();
}

void decmate2_state::devctl_map(address_map &map)
{
	map(0050, 0050).lr8(NAME([]() { return 0; }));
	map(0051, 0051).r(FUNC(decmate2_state::kbdtflg_devctl_r));
	map(0054, 0054).lr8(NAME([]() { return 0; }));
	map(0056, 0056).lr8(NAME([]() { return hd6120_device::C0; }));
	map(0061, 0061).r(FUNC(decmate2_state::vint_devctl_r));
	map(0110, 0110).lr8(NAME([]() { return 0; }));
	map(0111, 0111).r(FUNC(decmate2_state::kbdrflg_devctl_r));
	map(0112, 0112).lr8(NAME([]() { return hd6120_device::C0; }));
	map(0114, 0114).lr8(NAME([]() { return hd6120_device::C1; }));
	map(0116, 0116).lr8(NAME([]() { return hd6120_device::C0 | hd6120_device::C1; }));
	map(0121, 0121).r(FUNC(decmate2_state::apten_r));
	map(0122, 0126).lr8(NAME([]() { return 0; }));
	map(0127, 0127).lr8(NAME([]() { return hd6120_device::C1; }));
	map(0302, 0302).lr8(NAME([]() { return hd6120_device::C0; }));
	map(0304, 0304).lr8(NAME([]() { return hd6120_device::C1; }));
	map(0306, 0306).lr8(NAME([]() { return hd6120_device::C0 | hd6120_device::C1; }));
	map(0314, 0314).lr8(NAME([]() { return 0; }));
	map(0316, 0316).lr8(NAME([]() { return hd6120_device::C0; }));
	map(0320, 0320).lr8(NAME([]() { return 0; }));
	map(0321, 0321).r(FUNC(decmate2_state::prtrflg_devctl_r));
	map(0322, 0322).lr8(NAME([]() { return hd6120_device::C0; }));
	map(0324, 0324).lr8(NAME([]() { return hd6120_device::C1; }));
	map(0326, 0326).lr8(NAME([]() { return hd6120_device::C0 | hd6120_device::C1; }));
	map(0330, 0330).lr8(NAME([]() { return 0; }));
	map(0331, 0331).r(FUNC(decmate2_state::prttflg_devctl_r));
	map(0334, 0334).lr8(NAME([]() { return 0; }));
	map(0336, 0336).lr8(NAME([]() { return hd6120_device::C0; }));
	map(0362, 0363).lr8(NAME([]() { return 0; }));
	map(0366, 0366).r(FUNC(decmate2_state::comreg_devctl_r));
	map(0367, 0367).lr8(NAME([]() { return 0; }));
	map(0750, 0750).lr8(NAME([]() { return 0; }));
	map(0751, 0751).lr8(NAME([]() { return hd6120_device::C0; }));
	map(0752, 0752).r(FUNC(decmate2_state::xdr_devctl_r));
	map(0753, 0753).r(FUNC(decmate2_state::str_devctl_r));
	map(0754, 0754).r(FUNC(decmate2_state::ser_devctl_r));
	map(0755, 0755).r(FUNC(decmate2_state::sdn_devctl_r));
	map(0756, 0757).lr8(NAME([]() { return 0; }));
}

void decmate2_state::rx_map(address_map &map)
{
	map.global_mask(0x7ff);
	map(0x000, 0x000).mirror(0x3f0).rw(FUNC(decmate2_state::rx_ldata_r), FUNC(decmate2_state::rx_ldata_w));
	map(0x001, 0x001).mirror(0x3f0).rw(FUNC(decmate2_state::rx_hdata_r), FUNC(decmate2_state::rx_hdata_w));
	map(0x002, 0x002).mirror(0x3f0).w(FUNC(decmate2_state::rx_control_w));
	map(0x003, 0x003).mirror(0x3f0).r(FUNC(decmate2_state::rx_status_r));
	map(0x008, 0x00b).mirror(0x3f4).rw(m_fdc, FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	map(0x400, 0x7ff).ram();
}

static INPUT_PORTS_START(decmate2)
	PORT_START("LAS")
	PORT_BIT(00010, IP_ACTIVE_LOW, IPT_UNKNOWN) // APU present
	PORT_BIT(00004, IP_ACTIVE_LOW, IPT_UNKNOWN) // storage adapter present
	PORT_BIT(00002, IP_ACTIVE_LOW, IPT_UNKNOWN) // graphics controller present
	PORT_BIT(07761, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

static void rx50_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD); // MFM, 2 sides, 80 tracks
}

// XTALs: 5.0688 MHz, 15.741 MHz, 22.896 MHz, 16.000 MHz
void decmate2_state::pc278(machine_config &config)
{
	HD6120(config, m_maincpu, 16_MHz_XTAL / 2);
	// TODO: CPU clock is throttled from 8 MHz to 4 MHz while accessing I/O devices or when CPROM is enabled
	m_maincpu->set_addrmap(AS_PROGRAM, &decmate2_state::inst_map);
	m_maincpu->set_addrmap(AS_DATA, &decmate2_state::data_map);
	m_maincpu->set_addrmap(AS_IO, &decmate2_state::pc278_io_map);
	m_maincpu->set_addrmap(hd6120_device::AS_DEVCTL, &decmate2_state::devctl_map);
	m_maincpu->lxmar_callback().set(FUNC(decmate2_state::lxmar_w));
	m_maincpu->lxpar_callback().set(FUNC(decmate2_state::lxpar_w));
	m_maincpu->lxdar_callback().set(FUNC(decmate2_state::lxdar_w));
	m_maincpu->rsr_callback().set_ioport("LAS");
	m_maincpu->wsr_callback().set(FUNC(decmate2_state::wsr_w));
	m_maincpu->ioclr_callback().set(FUNC(decmate2_state::pc278_ioclr_w));
	m_maincpu->strtup_callback().set_constant(0);

	I8051(config, m_rxcpu, 16_MHz_XTAL / 2);
	m_rxcpu->set_addrmap(AS_IO, &decmate2_state::rx_map);
	m_rxcpu->port_in_cb<1>().set(FUNC(decmate2_state::rx_intr_r));
	m_rxcpu->port_out_cb<1>().set(FUNC(decmate2_state::rx_sel_w));
	m_rxcpu->port_in_cb<2>().set(FUNC(decmate2_state::rx_rdy_r));

	FD1793(config, m_fdc, 16_MHz_XTAL / 16);
	m_fdc->set_force_ready(false);

	FLOPPY_CONNECTOR(config, m_floppy[0], rx50_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], rx50_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[2], rx50_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[3], rx50_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	AY31015(config, m_kbduart); // 6402
	m_kbduart->write_so_callback().set("keyboard", FUNC(lk201_device::rx_w));
	m_kbduart->write_dav_callback().set(FUNC(decmate2_state::keyboard_dr_ff_w));
	m_kbduart->write_tbmt_callback().set(FUNC(decmate2_state::keyboard_tbre_ff_w));
	m_kbduart->set_auto_rdav(true);

	AY31015(config, m_prtuart); // 6402
	m_prtuart->write_so_callback().set("printer", FUNC(rs232_port_device::write_txd));
	m_prtuart->write_fe_callback().set_nop(); // TODO: output on connector pin 9
	m_prtuart->write_dav_callback().set(FUNC(decmate2_state::printer_dr_ff_w));
	m_prtuart->write_tbmt_callback().set(FUNC(decmate2_state::printer_tbre_ff_w));
	m_prtuart->set_auto_rdav(true);

	UPD7201(config, m_mpscc, 16_MHz_XTAL / 8);
	m_mpscc->out_txda_callback().set("com", FUNC(rs232_port_device::write_txd));

	COM8116(config, m_brg[0], 5.0688_MHz_XTAL); // 5016T
	m_brg[0]->fr_handler().set(m_prtuart, FUNC(ay31015_device::write_rcp));
	m_brg[0]->fr_handler().append(m_prtuart, FUNC(ay31015_device::write_tcp));
	m_brg[0]->ft_handler().set(m_mpscc, FUNC(upd7201_device::txca_w));
	m_brg[0]->ft_handler().append(m_mpscc, FUNC(upd7201_device::rxca_w));

	COM8116(config, m_brg[1], 5.0688_MHz_XTAL); // 5016T
	m_brg[1]->fr_handler().set(m_kbduart, FUNC(ay31015_device::write_rcp));
	m_brg[1]->fr_handler().append(m_kbduart, FUNC(ay31015_device::write_tcp));
	// TODO: other output is divided down to 100 Hz

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(15.741_MHz_XTAL, 990, 0, 800, 265, 0, 240); // 24x80, 10x10 character cell
	//screen.set_raw(22.896_MHz_XTAL, 1440, 0, 1188, 265, 0, 240); // 24x132, 9x10 character cell?
	screen.set_screen_update(FUNC(decmate2_state::screen_update));

	CRT9007(config, m_crtc, 15.741_MHz_XTAL / 10);
	m_crtc->set_character_width(10); // 9 in 132-column mode
	m_crtc->set_screen("screen");
	m_crtc->int_callback().set(FUNC(decmate2_state::vint_w));

	LK201(config, "keyboard").tx_handler().set(m_kbduart, FUNC(ay31015_device::write_si));

	rs232_port_device &com(RS232_PORT(config, "com", default_rs232_devices, nullptr));
	com.rxd_handler().set(m_mpscc, FUNC(upd7201_device::rxa_w));

	rs232_port_device &printer(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	printer.rxd_handler().set(m_prtuart, FUNC(ay31015_device::write_si)); // TODO: loopback control
}

void decmate2_state::pc238(machine_config &config)
{
	pc278(config);
	config.device_remove("brg0");
	config.device_remove("brg1");
	config.device_remove("fdc:b0");
	config.device_remove("fdc:b1");

	m_maincpu->set_addrmap(AS_IO, &decmate2_state::pc238_io_map);
	m_maincpu->ioclr_callback().set(FUNC(decmate2_state::pc238_ioclr_w));

	m_kbduart->write_dav_callback().set(FUNC(decmate2_state::keyboard_dr_w));
	m_kbduart->write_tbmt_callback().set(FUNC(decmate2_state::keyboard_tbre_w));
	m_prtuart->write_dav_callback().set(FUNC(decmate2_state::printer_dr_w));
	m_prtuart->write_tbmt_callback().set(FUNC(decmate2_state::printer_tbre_w));

	clock_device &pclk(CLOCK(config, "pclk", 16_MHz_XTAL / 208)); // Generated on pin 65 of DC382
	pclk.signal_handler().set(m_kbduart, FUNC(ay31015_device::write_rcp));
	pclk.signal_handler().append(m_kbduart, FUNC(ay31015_device::write_tcp));
	pclk.signal_handler().append(m_prtuart, FUNC(ay31015_device::write_rcp));
	pclk.signal_handler().append(m_prtuart, FUNC(ay31015_device::write_tcp));

	// TODO: COMCLK (generated on pin 39 of DC382, rate presumably programmable)

	// DECmate III has no 15.741 MHz XTAL
	m_crtc->set_clock(22.896_MHz_XTAL / 15);
	subdevice<screen_device>("screen")->set_raw(22.896_MHz_XTAL * 2 / 3, 960, 0, 800, 265, 0, 240);
}

ROM_START(decmate2)
	ROM_REGION16_BE(0x2000, "cprom", 0)
	ROM_SYSTEM_BIOS(0, "19n", "19N (2316)")
	ROMX_LOAD("23-399e2.e113", 0x0000, 0x0800, CRC(2881252e) SHA1(5825bc00833dd33f294f79df11200dce76e78740), ROM_BIOS(0) | ROM_SKIP(1)) // 0000-3777, bits 0:7 (to be shifted)
	ROMX_LOAD("23-400e2.e114", 0x1000, 0x0800, CRC(b6f79f77) SHA1(131a1c28c7d1c90dc1b6c85194f84f120cc60bd9), ROM_BIOS(0) | ROM_SKIP(1)) // 4000-7777, bits 0:7 (to be shifted)
	ROMX_LOAD("23-401e2.e115", 0x0001, 0x0800, CRC(1076b630) SHA1(7be4c30d512b4f86f645cb7a3a20122ee4187759), ROM_BIOS(0) | ROM_SKIP(1)) // 0000-3777 & 4000-7777, bits 8:11 (to be split up)
	ROM_SYSTEM_BIOS(1, "19h", "19H (2310)")
	ROMX_LOAD("23-390e2.e113", 0x0000, 0x0800, CRC(9b19451a) SHA1(ed26557f17f59ce05ca08b34a05a24a97388dfe0), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("23-391e2.e114", 0x1000, 0x0800, CRC(3a09ada1) SHA1(3093bd926d49c2fd62a773e8019e3755aa165ae9), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("23-392e2.e115", 0x0001, 0x0800, CRC(19901cb6) SHA1(82a642d2b5b56250611f69321d0251e27fa639fc), ROM_BIOS(1) | ROM_SKIP(1))
	ROM_SYSTEM_BIOS(2, "31z", "31Z (3732)") // Regenerated from source code listing
	ROMX_LOAD("23-358e2.e113", 0x0000, 0x0800, BAD_DUMP CRC(459231ea) SHA1(608342b7129d54a2a0f4a8e0645dad1ae26fccf4), ROM_BIOS(2) | ROM_SKIP(1))
	ROMX_LOAD("23-359e2.e114", 0x1000, 0x0800, BAD_DUMP CRC(3d1825b7) SHA1(753af9657eef1a284801c9765f4899563d3d9a20), ROM_BIOS(2) | ROM_SKIP(1))
	ROMX_LOAD("23-360e2.e115", 0x0001, 0x0800, BAD_DUMP CRC(ed8162e6) SHA1(4ab993c00a5afc6465153ca4f52faa76e02a0ef3), ROM_BIOS(2) | ROM_SKIP(1))

	ROM_REGION(0x1000, "rxcpu", 0)
	ROM_LOAD("23-008m1.e27", 0x0000, 0x1000, CRC(fae4026b) SHA1(388e093d952ce1f6fcf2dcdab5b5099a6aafad0f))

	ROM_REGION(0x100, "precomp", 0) // 256x4 bipolar ROM (only one bit used)
	ROM_LOAD("23-640a2.e13", 0x000, 0x100, NO_DUMP)

	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD("23-114e4.e173", 0x0000, 0x2000, NO_DUMP)
ROM_END

ROM_START(decmate3)
	ROM_REGION16_BE(0x2000, "cprom", 0)
	ROM_LOAD16_BYTE("23-330e4.e33", 0x0000, 0x1000, CRC(4258e0d0) SHA1(09a2f5f25620b491aed87b3c6465fb0a4c4211ff))
	ROM_CONTINUE(0x0001, 0x1000)

	ROM_REGION(0x1000, "rxcpu", 0)
	ROM_LOAD("23-008m1.e4", 0x0000, 0x1000, CRC(fae4026b) SHA1(388e093d952ce1f6fcf2dcdab5b5099a6aafad0f))

	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD("23-331e4.e42", 0x0000, 0x2000, CRC(dca00fae) SHA1(5beff80611149cbae3e91c813d302f09a82fc3dd))

	// TODO: add NO_DUMP entry for PAL (23-097K5)
ROM_END

} // anonymous namespace


COMP(1982, decmate2, 0, 0, pc278, decmate2, decmate2_state, init_pc278, "Digital Equipment Corporation", "DECmate II (PC278)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP(1984, decmate3, 0, 0, pc238, decmate2, decmate2_state, init_pc238, "Digital Equipment Corporation", "DECmate III (PC238)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



dectalk.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Jonathan Gevaryahu
/******************************************************************************
*
*  DECtalk DTC-01 Driver
*  Copyright (C) 2009-2013 Jonathan Gevaryahu AKA Lord Nightmare
*  with major help (dumping, tech questions answered, etc)
*  from Kevin 'kevtris' Horton, without whom this driver would
*  have been impossible.
*  Special thanks to Al Kossow for archiving the DTC-01 schematic at bitsavers,
*  http://bitsavers.org/pdf/dec/dectalk/MP-01820_DTC01_EngrDrws_Nov83.pdf
*  which has been invaluable for work on this driver.
*  Special thanks to leeeeee for helping figure out what the led selftest codes actually mean
*
*
*  This driver dedicated in memory of Dennis Klatt and Jonathan Allen, without whose
*  original work MITalk and hence KlattTalk and DECtalk would never have existed,
*  in memory of Martin Minow, who wrote much of the DECtalk DTC-01 code, and
*  in memory of Tony Vitale, who was one of the architects of the DECtalk project.
*
*  Staff behind DECtalk itself: (mostly from http://amhistory.si.edu/archives/speechsynthesis/ss_dec.htm ):
*     John C. Broihier
*     Edward A. Bruckert (who followed the DECtalk IP from DEC->Compaq->HP->Force Computers->Fonix inc from where he retired in 2014)
*     Dave Conroy (dtc-03 letter to sound rules; logic design, worked on dtc-01 and dtc-03, see correspondence below)
*     Michael J. Crowley
*     Dennis H. Klatt [1938 - Dec 30, 1988] (worked on klsyn and other parts of MITalk, wrote KLATTtalk which was exclusively licensed to DEC in 1982, worked on dtc-01 and dtc-03)
*     Martin A. Minow [Nov 6, 1940 - Dec 21, 2000] (dtc-01 conversion of hunnicutt's letter to sound rules, phonetics and general programming)
*     Walter Tetschner (DECtalk project director; as of 2013 publishes ASRnews: http://www.asrnews.com/)
*     Anthony J. Vitale [Apr 30, 1945 - Aug 5, 2002] (DECtalk project architect; dtc-03 letter to sound rules; worked on dtc-01 and dtc-03)
*
*  Staff behind MITalk: (also see http://amhistory.si.edu/archives/speechsynthesis/ss_mit.htm )
*     Dennis H. Klatt (see above)
*     Jonathan Allen [Jun 4, 1934 - Apr 24, 2000] (speech synthesis theory and development)
*     M. Sharon 'Sheri' Hunnicutt (worked on letter to phoneme rules, which were licensed non-exclusively to DEC in 1982; still works as a docent at KTH in Sweden as of 2013)
*
*  TODO:
*  * DUART:
*      * The duart self tests are EXTENSIVE and make an excellent check of many of the duart internal bits.
*        To enable the self tests rather than bypassing them, under dipswitches, set 'Skip Self Test(IP4)' to 'Open (VCC)'
*        and under system configuration set 'Hack to prevent hang when skip self test is shorted' to 'Off'
*    * <DONE> DUART needs to be reset on reset line activation. as is it works ok, but it should be done anyway.
*    * DUART needs its i/o pins connected as well:
*    * pins IP0, IP2, and IP3 are connected to the primary serial port:
*      * IP0 is CTS
*      * IP2 is DSR
*      * IP3 is RLS (received line signal, this pin is rarely used on rs232)
*    * <DONE> pins IP4, IP5 and IP6 are on jumpers on the DUART, tied high normally but jumperable low, should be handled as dipswitches:
*      * IP4 low: skip hardware self tests
*      * IP5 low: unknown
*      * IP6 low: unknown
*    * pins OP0 and OP2 are connected to the primary serial port:
*      * OP0 is RTS
*      * OP2 is DTR
*  * <DONE> Figure out why the v1.8 dectalk firmware clips/screeches like hell (it requires the older dsp code to work properly)
*  * <DONE> Figure out why the older -165/-166 and newer -409/-410 tms32010 dsp firmwares don't produce any sound, while the middle -204/-205 one does (fifo implementations were busted)
*  * <DONE> Actually store the X2212 nvram's eeprom data to disk rather than throwing it out on exit
*    * Get setup mode with the serial BREAK int working enough to actually properly save the default nvram back to the chip in emulation, and get rid of the (currently unused) nvram default image in the rom definitions
*  * emulate/simulate the MT8860 DTMF decoder and MT8865 DTMF filter as a 16-key input device? or hook it to some simple fft code? Francois Jalbert's fftmorse code ran full speed on a 12mhz 80286, maybe use that?
*    Sarayan suggested this can be done in one of two ways:
*    1. Standalone 'canned' DTMF detector and discriminator code (francois' and peter jennings' 286 code, or modern fft code)
*    2. Emulate the MT8865 as a set of two 7th order bandpass filters with 2
*       outputs for the low and high band (as in real life as documented on the
*       datasheet) and emulate the MT8860 exactly as in real life as well, as a
*       dual-input pulse-width measurement device to distinguish the 4 low and
*       high band DTMF tones as well as their combined presence.
*       The latter is clearly more accurate but likely slower.
*  * figure out how to plumb diserial/rs232 to have an external application send data to the two serial ports to be spoken; this shouldn't be too hard at this point.
*
* LED error code list (found by experimentation and help from leeeeee):
*    Startup Self tests:
*    FF 00 - M68k address register check fail or data register check fail (test code at $21E)
*     (for some data register failures the processor just spins forever and no led code is generated)
*    FF 01 - ROM check fail @ 0x00000, rom at E8 or E22 (test code at $278)
*    FF 02 - ROM check fail @ 0x08000, rom at E7 or E21 "
*    FF 03 - ROM check fail @ 0x10000, rom at E6 or E20 "
*    FF 04 - ROM check fail @ 0x18000, rom at E5 or E19 "
*    FF 05 - ROM check fail @ 0x20000, rom at E4 or E18 "
*    FF 06 - ROM check fail @ 0x28000, rom at E3 or E17 "
*    FF 07 - ROM check fail @ 0x30000, rom at E2 or E16 "
*    FF 08 - ROM check fail @ 0x38000, rom at E1 or E15 "
*    FF 0F - ROM check fail at multiple addresses
*    FE 01 - RAM check fail @ 0x80000-0x83fff, ram at E36 or E49 (test code at $338)
*    FE 02 - RAM check fail @ 0x84000-0x87fff, ram at E35 or E48 "
*    FE 03 - RAM check fail @ 0x88000-0x8bfff, ram at E34 or E47 "
*    FE 04 - RAM check fail @ 0x8c000-0x8ffff, ram at E33 or E46 "
*    FE 05 - RAM check fail @ 0x90000-0x93fff, ram at E32 or E44 "
*    FE 0F - RAM check fail at multiple addresses
*    FD 00 - DUART test & DUART interrupt test (test code at $046C)
*    FC 00 - This test doesn't exist. Some vestiges of it may remain in code for the FD and FB tests.
*    FB 00 - TMS32010 extensive tests (test code at $051E): test spc interrupt [works] and make dtmf tone to test tlc interrupt [fails in mess, requires dtmf detection on output; this test is actually patented! US 4,552,992]
*    Jump to $102C to skip the self tests
*    During normal operation:
(table taken from http://www3.sympatico.ca/n.rieck/docs/DECtalk_notes.html )
DTC-01 LEDs

   76543210         LEDs
   ||||||||
   |||||||+-------- set if host asserting CTS
   ||||||+--------- set if DECtalk asserting RTS
   |||||+---------- set if host asserting DSR
   ||||+----------- set if host asserting DCD
   |||+------------ set if DECtalk asserting DTR
   +++------------- 3 bit state code

   000              in state 5, first 500 ms; waiting for CD & CTS in first 500 ms when DECtalk is on-line
   001              timing 2 second CD=0 while in state 6 (moving data)
   010              waiting for DSR=0 while disconnecting (part of state 7)
   011              waiting for DSR=1 while connecting (state 3)
   100              delaying for UK modems during disconnect (part of state 7)
   101              waiting for CD and CTS (main part of state 5)
   110              moving data (state 6)
   111              disconnecting (start of state 7)
*    DECtalk dtc-01 hardware and rom version history, from DTC-01 schematic at bitsavers and with additional info from
     http://americanhistory.si.edu/archives/speechsynthesis/ss_dec1.htm
*    August 1983: Hardware version A done
*    unknown date: Version 1.0 roms finalized
*    unknown date: Version 1.1 roms released to fix a bug with insufficient stack space, see ss_dec1 above
*    October 11 1983: Second half of Version 1.8 rom finalized
*    December 05 1983: First half of Version 1.8 rom finalized
*    March 1984: Hardware version B done
       (integrates the output fifo sync error check onto the pcb;
       Version A units are retrofitted when sent in for firmware upgrades)
       (most of the schematics come from this time, and have the version 1.8 roms listed on them)
*    July 02 1984: Second half of Version 2.0 rom finalized
*    July 23 1984: First half of Version 2.0 rom finalized
*    October 1984 (the rest of the schematics come from this time)
*    sometime after 6th week of 1985: dsp roms updated to the -409/-410 set
*
* NVRAM related stuff found by leeeeee (in v2.0):
* $10402 - nvram recall/reset based on byte on stack (0 = recall, 1 = write default to nvram)
* $10f2E - nvram recall
* $10f52 - entry point for nvram check routine
* $11004(entry point)-$11030, $11032-$111B4 - nvram store routine:
*    pass with a blank word on stack and be sure the test and branch at 11016 & 1101a passes (jumps to 1106a)
*    It will then write currently loaded settings to nvram and execute a store, so do it after the defaults are loaded
* $1a7ae - default nvram image, without checksum (0x80 bytes)
*******************************************************************************/

/*
interrupts:
68k has an interrupt priority decoder attached to it:
TLC is INT level 4
SPC is INT level 5
DUART is INT level 6
*/
/* dtc-03 post by dave conroy from usenet comp.sys.dec on 12/29/2011:
> Wow.  were they better than the DTC01? (OK, I guess they had to be.)

I worked on both of these at DEC (in fact, I think if you look in the
options and modules
list, you will see my initials in the "responsible engineer" column
for the DTC03).

The goals of the DTC03 were lower cost, better letter to sound rules,
and better packaging for large systems (it was pretty inconvenient to
set up 30-40 of
the big DTC01 boxes).

The hardware was quite different. The DTC01 used a Motorola 68000 and
a TI DSP, connected
together by a big bank of (expensive) fifo chips. The DTC03 used then
(then new) Intel 80186 and the same
TI DSP, connected together by DMA (the DTC03 design was done in a way
that used *all* of the
capabilities of the 80186 to reduce the cost). The DTC01 used the
packaging of the VT240, and the DTC03
used the packaging of a family of rack-mounted modems whose part
number escapes me. The
same guy (Rich Ellison) designed both of them. The DTC03 also had an
"option module" system which
was intended to allow non-US systems to be built without needing to
change the common
parts (because it was on an independent module, and could override all
of the telephone control ESC
sequences, it could be taken through the approval process in
isolation), although it was used
to build some semi-custom systems as well.

The code in the DSP and the code that transformed a stream of phonemes
into a stream
of control commands for the DSP was pretty much the same in DTC01/
DTC03, and was based on the work of
Dennis Klatt of MIT (Dennis actually wrote a lot of this code). The
letter to sound system
in DTC01 was the final step in the evolution of a set of letter-to-
sound rules that had been floating around DEC
for a long time; the bulk of the work getting them to work in the
DTC01 was done by
Martin Minow. The letter to sound system in DTC03 was a new design by
myself and Tony Vitale,
an ex-professor of linguistics from Cornell. Most people thought it
worked much
better; in reality, it's big advantage was it made far fewer stupid
stress-placement mistakes because
it did a much better job of understanding prefixes and suffixes.

Dave Conroy
*/
/*
There are 3 things on the pins. Serial I/O. The telephone line. Power.

I believe the power is +5 and +12/-12. The +5 is for the logic,
and the +12/-12 is for the RS232 buffers and all of the analog stuff
in the anti-aliasing
filter, which is built from a pile of op-amps and stuff.

The serial I/O pins go straight to the RS232 buffers and
onward to the UART (an SCN2661, if my memory is correct).

The telephone pins go to the option connectors and the on-board
telephone line interface for the USA/Canada. Audio is available
somewhere on the
option connectors, but a really easy way to get at it is to take the
phone off-hook (send a "dial" command with an empty phone number
string)
and use the telephone pins as a transformer-coupled audio output. This
is what we
used to do in the lab all the time.

dgc (dg(no!spam)cx@mac.com)
*/

// USE_LOOSE_TIMING makes the cpu interleave much lower and boosts it on fifo and flag writes by the 68k and semaphore sets by the dsp
#define USE_LOOSE_TIMING 1
// makes use_loose_timing boost interleave when the outfifo is about to run out. slightly slows things down but may prevent some glitching
#undef USE_LOOSE_TIMING_OUTPUT
// generic logs like led state, and common writes for dsp and spc such as the speech int
#undef VERBOSE
// logs reads and writes to nvram, and nvram store/recall flag messages
#undef NVRAM_LOG
// logs reads and writes to TLC regs
#undef TLC_LOG
// logs reads and writes to SPC regs, 68k side only
#undef SPC_LOG_68K
// logs reads and writes to SPC regs, dsp side only
#undef SPC_LOG_DSP
// logs txa, txb and related serial lines to stderr
#undef SERIAL_TO_STDERR

/* Core includes */
#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
#include "cpu/tms32010/tms32010.h"
#include "machine/mc68681.h"
#include "machine/x2212.h"
#include "sound/dac.h"
#include "speaker.h"


namespace {

class dectalk_state : public driver_device
{
public:
	dectalk_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_dsp(*this, "dsp"),
		m_duart(*this, "duart"),
		m_nvram(*this, "x2212"),
		m_dac(*this, "dac")
	{
	}

	void dectalk(machine_config &config);

private:
	// input fifo, between m68k and tms32010
	uint16_t m_infifo[32]{}; // technically eight 74LS224 4bit*16stage FIFO chips, arranged as a 32 stage, 16-bit wide fifo
	uint8_t m_infifo_count = 0;
	uint8_t m_infifo_tail_ptr = 0;
	uint8_t m_infifo_head_ptr = 0;
	// output fifo, between tms32010 and 10khz sample latch for dac
	uint16_t m_outfifo[16]{}; // technically three 74LS224 4bit*16stage FIFO chips, arranged as a 16 stage, 12-bit wide fifo
	uint8_t m_outfifo_count = 0;
	uint8_t m_outfifo_tail_ptr = 0;
	uint8_t m_outfifo_head_ptr = 0;
	bool m_infifo_semaphore = false; // latch for status of output fifo, d-latch 74ls74 @ E64 'lower half'
	bool m_spc_error_latch = false; // latch for error status of speech dsp, d-latch 74ls74 @ E64 'upper half'
	uint8_t m_m68k_spcflags_latch = 0; // latch for initializing the speech dsp, d-latch 74ls74 @ E29 'lower half', AND latch for spc irq enable, d-latch 74ls74 @ E29 'upper half'; these are stored in bits 0 and 6 respectively, the rest of the bits stored here MUST be zeroed! // TODO: Split this into two separate booleans!
	uint8_t m_m68k_tlcflags_latch = 0; // latch for telephone interface stuff, d-latches 74ls74 @ E93 'upper half' and @ 103 'upper and lower halves' // TODO: Split this into three separate booleans!
	bool m_simulate_outfifo_error = 0; // simulate an error on the outfifo, which does something unusual to the dsp latches
	bool m_tlc_tonedetect = false;
	bool m_tlc_ringdetect = false;
	uint8_t m_tlc_dtmf = 0; // dtmf holding reg
	uint8_t m_duart_inport = 0; // low 4 bits of duart input
	uint8_t m_duart_outport = 0; // most recent duart output
	bool m_hack_self_test_is_second_read = false; // temp variable for hack below

	required_device<m68000_base_device> m_maincpu;
	required_device<tms32010_device> m_dsp;
	required_device<scn2681_device> m_duart;
	required_device<x2212_device> m_nvram;
	required_device<dac_word_interface> m_dac;
	void duart_txa(int state);
	uint8_t duart_input();
	void duart_output(uint8_t data);
	uint8_t nvram_recall(offs_t offset);
	void led_write(uint8_t data);
	void nvram_store(offs_t offset, uint8_t data);
	void m68k_infifo_w(uint16_t data);
	uint16_t m68k_spcflags_r();
	void m68k_spcflags_w(uint16_t data);
	uint16_t m68k_tlcflags_r();
	void m68k_tlcflags_w(uint16_t data);
	uint16_t m68k_tlc_dtmf_r();
	void spc_latch_outfifo_error_stats(uint16_t data);
	uint16_t spc_infifo_data_r();
	void spc_outfifo_data_w(uint16_t data);
	int spc_semaphore_r();
	virtual void machine_start() override ATTR_COLD;
	TIMER_CALLBACK_MEMBER(outfifo_read_cb);
	emu_timer *m_outfifo_read_timer = nullptr;
	void outfifo_check();
	void clear_all_fifos();
	void dsp_semaphore_w(bool state);
	uint16_t dsp_outfifo_r();
	void dectalk_reset(int state);

	void m68k_mem(address_map &map) ATTR_COLD;
	void tms32010_io(address_map &map) ATTR_COLD;
	void tms32010_mem(address_map &map) ATTR_COLD;
};


/* 2681 DUART */
uint8_t dectalk_state::duart_input()
{
	uint8_t data = 0;
	data |= m_duart_inport&0xf;
	data |= (ioport("duart_in")->read()&0xf0);
	if ((m_hack_self_test_is_second_read) && (ioport("hacks")->read()&0x01)) data |= 0x10; // hack to prevent hang if selftest disable bit is kept low past the first read; i suppose the proper use of this bit was an incremental switch, or perhaps its expecting an interrupt later from serial in or tone in? added a dipswitch to disable the hack for testing
		m_hack_self_test_is_second_read = true;
	return data;
}

void dectalk_state::duart_output(uint8_t data)
{
	m_duart_outport = data;
#ifdef SERIAL_TO_STDERR
	fprintf(stderr, "RTS: %01X, DTR: %01X\n", data&1, (data&4)>>2);
#endif
}

void dectalk_state::duart_txa(int state)
{
	//TODO: this needs to be plumbed so it shows up optionally on a second terminal somehow, or connects to diserial
	// it is the second 'alternate' serial connection on the DTC-01, used for a serial passthru and other stuff.
#ifdef SERIAL_TO_STDERR
	fprintf(stderr, "TXA:%02X ",data);
#endif
}

/* FIFO and TMS32010 stuff */
#define SPC_INITIALIZE state->m_m68k_spcflags_latch&0x1 // speech initialize flag
#define SPC_IRQ_ENABLED ((state->m_m68k_spcflags_latch&0x40)>>6) // irq enable flag

void dectalk_state::outfifo_check()
{
	// check if output fifo is full; if it isn't, set the int on the dsp
	if (m_outfifo_count < 16)
		m_dsp->set_input_line(0, ASSERT_LINE); // TMS32010 INT
	else
		m_dsp->set_input_line(0, CLEAR_LINE); // TMS32010 INT
}

void dectalk_state::clear_all_fifos()
{
	// clear fifos (TODO: memset would work better here...)
	int i;
	for (i=0; i<16; i++) m_outfifo[i] = 0;
	m_outfifo_count = 0;
	m_outfifo_tail_ptr = m_outfifo_head_ptr = 0;
	for (i=0; i<32; i++) m_infifo[i] = 0;
	m_infifo_count = 0;
	m_infifo_tail_ptr = m_infifo_head_ptr = 0;
	outfifo_check();
}

// helper for dsp infifo_semaphore flag to make dealing with interrupts easier
void dectalk_state::dsp_semaphore_w(bool state)
{
	m_infifo_semaphore = state;
	if ((m_infifo_semaphore) && (m_m68k_spcflags_latch&0x40))
	{
#ifdef VERBOSE
		logerror("speech int fired!\n");
#endif
		m_maincpu->set_input_line(M68K_IRQ_5, ASSERT_LINE);
	}
	else
		m_maincpu->set_input_line(M68K_IRQ_5, CLEAR_LINE);
}

// read the output fifo and set the interrupt line active on the dsp
uint16_t dectalk_state::dsp_outfifo_r (  )
{
	uint16_t data = 0xffff;
#ifdef USE_LOOSE_TIMING_OUTPUT
	// if outfifo count is less than two, boost the interleave to prevent running the fifo out
	if (m_outfifo_count < 2)
	machine().scheduler().perfect_quantum(attotime::from_usec(25));
#endif
#ifdef VERBOSE
	if (m_outfifo_count == 0) logerror("output fifo is EMPTY! repeating previous sample!\n");
#endif
	data = m_outfifo[m_outfifo_tail_ptr];
	// if fifo is empty (tail ptr == head ptr), do not increment the tail ptr, otherwise do.
	if (m_outfifo_count > 0)
	{
		m_outfifo_tail_ptr++;
		m_outfifo_count--;
	}
	m_outfifo_tail_ptr&=0xf;
	outfifo_check();
	return ((data&0xfff0)^0x8000); // yes this is right, top bit is inverted and bottom 4 are ignored
	//return data; // not right but want to get it working first
}

/* Machine reset and friends: stuff that needs setting up which IS directly affected by reset */
void dectalk_state::dectalk_reset(int state)
{
	m_hack_self_test_is_second_read = false; // hack
	// stuff that is DIRECTLY affected by the RESET line
	m_nvram->recall(0);
	m_nvram->recall(1);
	m_nvram->recall(0); // nvram recall
	m_m68k_spcflags_latch = 1; // initial status is speech reset(d0) active and spc int(d6) disabled
	m_m68k_tlcflags_latch = 0; // initial status is tone detect int(d6) off, answer phone(d8) off, ring detect int(d14) off
	m_duart->reset(); // reset the DUART
	// stuff that is INDIRECTLY affected by the RESET line
	clear_all_fifos(); // speech reset clears the fifos, though we have to do it explicitly here since we're not actually in the m68k_spcflags_w function.
	dsp_semaphore_w(false); // on the original DECtalk DTC-01 pcb revision, this is a semaphore for the INPUT fifo, later dec hacked on a check for the 3 output fifo chips to see if they're in sync, and set both of these latches if true.
	m_spc_error_latch = false; // spc error latch is cleared on /reset
	m_dsp->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); // speech reset forces the CLR line active on the tms32010
	m_tlc_tonedetect = false; // TODO, needed for selftest pass
	m_tlc_ringdetect = false; // TODO
	m_tlc_dtmf = 0; // TODO
	m_duart_inport = 0xf;
	m_duart_outport = 0;
}

void dectalk_state::machine_start()
{
	m_outfifo_read_timer = timer_alloc(FUNC(dectalk_state::outfifo_read_cb), this);
	m_outfifo_read_timer->adjust(attotime::from_hz(10000));
	save_item(NAME(m_infifo));
	save_item(NAME(m_infifo_count));
	save_item(NAME(m_infifo_tail_ptr));
	save_item(NAME(m_infifo_head_ptr));
	save_item(NAME(m_outfifo));
	save_item(NAME(m_outfifo_count));
	save_item(NAME(m_outfifo_tail_ptr));
	save_item(NAME(m_outfifo_head_ptr));
	save_item(NAME(m_infifo_semaphore));
	save_item(NAME(m_spc_error_latch));
	save_item(NAME(m_m68k_spcflags_latch));
	save_item(NAME(m_m68k_tlcflags_latch));
	save_item(NAME(m_simulate_outfifo_error));
	save_item(NAME(m_tlc_tonedetect));
	save_item(NAME(m_tlc_ringdetect));
	save_item(NAME(m_tlc_dtmf));
	save_item(NAME(m_duart_inport));
	save_item(NAME(m_duart_outport));
	save_item(NAME(m_hack_self_test_is_second_read));
	clear_all_fifos();
	m_simulate_outfifo_error = false; // TODO: HACK for now, should be hooked to a fake dipswitch to simulate fifo errors
}

/* Begin 68k i/o handlers */

uint8_t dectalk_state::nvram_recall(offs_t offset)// recall from x2212 nvram chip
{
#ifdef NVRAM_LOG
	fprintf(stderr,"NVRAM RECALL executed: offset %03x\n", offset);
#endif
	m_nvram->recall(0);
	m_nvram->recall(1);
	m_nvram->recall(0);
	return 0xff;
}

void dectalk_state::led_write(uint8_t data)
{
	popmessage("LED status: %02X\n", data&0xff);
#ifdef VERBOSE
	logerror("m68k: LED status: %02X\n", data&0xff);
#endif
	//popmessage("LED status: %x %x %x %x %x %x %x %x\n", data&0x80, data&0x40, data&0x20, data&0x10, data&0x8, data&0x4, data&0x2, data&0x1);
}

void dectalk_state::nvram_store(offs_t offset, uint8_t data) // store to X2212 NVRAM chip
{
#ifdef NVRAM_LOG
		fprintf(stderr,"NVRAM STORE executed: offset %03x, data written (and ignored) is %02x\n", offset, data);
#endif
	m_nvram->store(0);
	m_nvram->store(1);
	m_nvram->store(0);
}

void dectalk_state::m68k_infifo_w(uint16_t data)// 68k write to the speech input fifo
{
#ifdef USE_LOOSE_TIMING
	machine().scheduler().perfect_quantum(attotime::from_usec(25));
#endif
#ifdef SPC_LOG_68K
	logerror("m68k: SPC infifo written with data = %04X, fifo head was: %02X; fifo tail: %02X\n",data, m_infifo_head_ptr, m_infifo_tail_ptr);
#endif
	// if fifo is full (head ptr = tail ptr-1), do not increment the head ptr and do not store the data
	if (m_infifo_count == 32)
	{
#ifdef SPC_LOG_68K
		logerror("infifo was full, write ignored!\n");
#endif
		return;
	}
	m_infifo[m_infifo_head_ptr] = data;
	m_infifo_head_ptr++;
	m_infifo_count++;
	m_infifo_head_ptr&=0x1f;
}

uint16_t dectalk_state::m68k_spcflags_r()// 68k read from the speech flags
{
	uint8_t data = 0;
	data |= m_m68k_spcflags_latch; // bits 0 and 6
	data |= m_spc_error_latch?0x20:0; // bit 5
	data |= m_infifo_semaphore?0x80:0; // bit 7
#ifdef SPC_LOG_68K
	logerror("m68k: SPC flags read, returning data = %04X\n",data);
#endif
	return data;
}

void dectalk_state::m68k_spcflags_w(uint16_t data)// 68k write to the speech flags (only 3 bits do anything)
{
#ifdef USE_LOOSE_TIMING
	machine().scheduler().perfect_quantum(attotime::from_usec(25));
#endif
#ifdef SPC_LOG_68K
	logerror("m68k: SPC flags written with %04X, only storing %04X\n",data, data&0x41);
#endif
	m_m68k_spcflags_latch = data&0x41; // ONLY store bits 6 and 0!
	// d0: initialize speech flag (reset tms32010 and clear infifo and outfifo if high)
	if ((data&0x1) == 0x1) // bit 0
	{
#ifdef SPC_LOG_68K
		logerror(" | 0x01: initialize speech: fifos reset, clear error+semaphore latches and dsp reset\n");
#endif
		clear_all_fifos();
		m_dsp->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); // speech reset forces the CLR line active on the tms32010
		// clear the two speech side latches
		m_spc_error_latch = false;
		dsp_semaphore_w(false);
	}
	else // (data&0x1) == 0
	{
#ifdef SPC_LOG_68K
		logerror(" | 0x01 = 0: initialize speech off, dsp running\n");
#endif
		m_dsp->set_input_line(INPUT_LINE_RESET, CLEAR_LINE); // speech reset deassert clears the CLR line on the tms32010
	}
	if ((data&0x2) == 0x2) // bit 1 - clear error and semaphore latches
	{
#ifdef SPC_LOG_68K
		logerror(" | 0x02: clear error+semaphore latches\n");
#endif
		// clear the two speech side latches
		m_spc_error_latch = false;
		dsp_semaphore_w(false);
	}
	if ((data&0x40) == 0x40) // bit 6 - spc irq enable
	{
#ifdef SPC_LOG_68K
		logerror(" | 0x40: speech int enabled\n");
#endif
		if (m_infifo_semaphore)
		{
#ifdef SPC_LOG_68K
			logerror("    speech int fired!\n");
#endif
			m_maincpu->set_input_line(M68K_IRQ_5, ASSERT_LINE); // set int because semaphore was set
		}
	}
	else // data&0x40 == 0
	{
#ifdef SPC_LOG_68K
		logerror(" | 0x40 = 0: speech int disabled\n");
#endif
		m_maincpu->set_input_line(M68K_IRQ_5, CLEAR_LINE); // clear int because int is now disabled
	}
}

uint16_t dectalk_state::m68k_tlcflags_r()// dtmf flags read
{
	uint16_t data = 0;
	data |= m_m68k_tlcflags_latch; // bits 6, 8, 14: tone detected int enable, answer phone relay enable, and ring int enable respectively
	data |= m_tlc_tonedetect?0x0080:0; // bit 7 is tone detected
	data |= m_tlc_ringdetect?0x8000:0; // bit 15 is ring detected
#ifdef TLC_LOG
	logerror("m68k: TLC flags read, returning data = %04X\n",data);
#endif
	return data;
}

void dectalk_state::m68k_tlcflags_w(uint16_t data)// dtmf flags write
{
#ifdef TLC_LOG
	logerror("m68k: TLC flags written with %04X, only storing %04X\n",data, data&0x4140);
#endif
	m_m68k_tlcflags_latch = data&0x4140; // ONLY store bits 6 8 and 14!
	if (data&0x40) // bit 6: tone detect interrupt enable
	{
#ifdef TLC_LOG
		logerror(" | 0x40: tone detect int enabled\n");
#endif
		if (m_tlc_tonedetect)
		{
#ifdef TLC_LOG
			logerror("    TLC int fired!\n");
#endif
			m_maincpu->set_input_line(M68K_IRQ_4, ASSERT_LINE); // set int because tone detect was set
		}
	}
	else // data&0x40 == 0
	{
#ifdef TLC_LOG
		logerror(" | 0x40 = 0: tone detect int disabled\n");
#endif
	if ((!(data&0x4000)) || (!m_tlc_ringdetect)) // check to be sure we don't disable int if both ints fired at once
		m_maincpu->set_input_line(M68K_IRQ_4, CLEAR_LINE); // clear int because int is now disabled
	}
	if (data&0x100) // bit 8: answer phone relay enable
	{
#ifdef TLC_LOG
		logerror(" | 0x100: answer phone relay enabled\n");
#endif
	}
	else // data&0x100 == 0
	{
#ifdef TLC_LOG
		logerror(" | 0x100 = 0: answer phone relay disabled\n");
#endif
	}
	if (data&0x4000) // bit 14: ring int enable
	{
#ifdef TLC_LOG
		logerror(" | 0x4000: ring detect int enabled\n");
#endif
		if (m_tlc_ringdetect == 1)
		{
#ifdef TLC_LOG
			logerror("    TLC int fired!\n");
#endif
			m_maincpu->set_input_line(M68K_IRQ_4, ASSERT_LINE); // set int because tone detect was set
		}
	}
	else // data&0x4000 == 0
	{
#ifdef TLC_LOG
		logerror(" | 0x4000 = 0: ring detect int disabled\n");
#endif
	if ((!(data&0x40)) || (!m_tlc_tonedetect)) // check to be sure we don't disable int if both ints fired at once
		m_maincpu->set_input_line(M68K_IRQ_4, CLEAR_LINE); // clear int because int is now disabled
	}
}

uint16_t dectalk_state::m68k_tlc_dtmf_r()// dtmf chip read
{
#ifdef TLC_LOG
	uint16_t data = 0xffff;
	data = m_tlc_dtmf&0xf;
	logerror("m68k: TLC dtmf detector read, returning data = %02X", data);
#endif
	return 0;
}
/* End 68k i/o handlers */

/* Begin tms32010 i/o handlers */
void dectalk_state::spc_latch_outfifo_error_stats(uint16_t data)// latch 74ls74 @ E64 upper and lower halves with d0 and 1 respectively
{
#ifdef USE_LOOSE_TIMING
	machine().scheduler().perfect_quantum(attotime::from_usec(25));
#endif
#ifdef SPC_LOG_DSP
	logerror("dsp: set fifo semaphore and set error status = %01X\n",data&1);
#endif
	dsp_semaphore_w(m_simulate_outfifo_error?false:true); // always set to true here, unless outfifo desync-between-the-three-parallel-fifo-chips error occurs.
	m_spc_error_latch = (data&1); // latch the dsp 'soft error' state aka "ERROR DETECTED D5 H" on schematics (different from the outfifo error state above!)
}

uint16_t dectalk_state::spc_infifo_data_r()
{
	uint16_t data = 0xffff;
	data = m_infifo[m_infifo_tail_ptr];
#ifdef SPC_LOG_DSP
	logerror("dsp: SPC infifo read with data = %04X, fifo head: %02X; fifo tail was: %02X\n",data, m_infifo_head_ptr, m_infifo_tail_ptr);
#endif
	// if fifo is empty (tail ptr == head ptr), do not increment the tail ptr, otherwise do.
	if (m_infifo_count > 0)
	{
		m_infifo_tail_ptr++;
		m_infifo_count--;
	}
	m_infifo_tail_ptr&=0x1f;
	return data;
}

void dectalk_state::spc_outfifo_data_w(uint16_t data)
{
	// the low 4 data bits are thrown out on the real unit due to use of a 12 bit dac (and to save use of another 16x4 fifo chip), though technically they're probably valid, and with suitable hacking a dtc-01 could probably output full 16 bit samples at 10khz.
#ifdef SPC_LOG_DSP
	logerror("dsp: SPC outfifo write, data = %04X, fifo head was: %02X; fifo tail: %02X\n", data, m_outfifo_head_ptr, m_outfifo_tail_ptr);
#endif
	m_dsp->set_input_line(0, CLEAR_LINE); //TMS32010 INT (cleared because LDCK inverts the IR line, clearing int on any outfifo write... for a moment at least.)
	// if fifo is full (head ptr = tail ptr-1), do not increment the head ptr and do not store the data
	if (m_outfifo_count == 16)
	{
#ifdef SPC_LOG_DSP
		logerror("outfifo was full, write ignored!\n");
#endif
		return;
	}
	m_outfifo[m_outfifo_head_ptr] = data;
	m_outfifo_head_ptr++;
	m_outfifo_count++;
	m_outfifo_head_ptr&=0xf;
	//outfifo_check(); // outfifo check should only be done in the audio 10khz polling function
}

int dectalk_state::spc_semaphore_r()// Return state of d-latch 74ls74 @ E64 'lower half' in d0 which indicates whether infifo is readable
{
#ifdef SPC_LOG_DSP
	//logerror("dsp: read infifo semaphore, returned %d\n", m_infifo_semaphore); // commented due to extreme annoyance factor
	if (!m_infifo_semaphore) logerror("dsp: read infifo semaphore, returned %d\n", m_infifo_semaphore);
#endif
	return m_infifo_semaphore;
}
/* end tms32010 i/o handlers */


/******************************************************************************
 Address Maps
******************************************************************************/
/*
Address maps (x = ignored; * = selects address within this range; a = see description at right of row)
68k address map:
a23 a22 a21 a20 a19 a18 a17 a16 a15 a14 a13 a12 a11 a10 a9  a8  a7  a6  a5  a4  a3  a2  a1  (a0 via UDS/LDS)
0   x   x   x   0   x   0   0   0   *   *   *   *   *   *   *   *   *   *   *   *   *   *   a       R   ROM a=0:E8, a=1:E22
0   x   x   x   0   x   0   0   1   *   *   *   *   *   *   *   *   *   *   *   *   *   *   a       R   ROM E7,E21
0   x   x   x   0   x   0   1   0   *   *   *   *   *   *   *   *   *   *   *   *   *   *   a       R   ROM E6,E20
0   x   x   x   0   x   0   1   1   *   *   *   *   *   *   *   *   *   *   *   *   *   *   a       R   ROM E5,E19
0   x   x   x   0   x   1   0   0   *   *   *   *   *   *   *   *   *   *   *   *   *   *   a       R   ROM E4,E18
0   x   x   x   0   x   1   0   1   *   *   *   *   *   *   *   *   *   *   *   *   *   *   a       R   ROM E3,E17
0   x   x   x   0   x   1   1   0   *   *   *   *   *   *   *   *   *   *   *   *   *   *   a       R   ROM E2,E16
0   x   x   x   0   x   1   1   1   *   *   *   *   *   *   *   *   *   *   *   *   *   *   a       R   ROM E1,E15
0   x   x   x   1   x   x   0   0   0   *   *   *   *   *   *   *   *   *   *   *   *   *   a       RW  RAM E36,E49
0   x   x   x   1   x   x   0   0   1   *   *   *   *   *   *   *   *   *   *   *   *   *   a       RW  RAM E35,E48
0   x   x   x   1   x   x   0   1   0   *   *   *   *   *   *   *   *   *   *   *   *   *   a       RW  RAM E34,E47
0   x   x   x   1   x   x   0   1   1   *   *   *   *   *   *   *   *   *   *   *   *   *   a       RW  RAM E33,E46
0   x   x   x   1   x   x   1   0   0   *   *   *   *   *   *   *   *   *   *   *   *   *   a       RW  RAM E32,E45
0   x   x   x   1   x   x   1   0   1   x   x   x   x   x   x   x   x   x   x   x   x   x   0       W   Status LED <d7-d0>
0   x   x   x   1   x   x   1   0   1   x   x   x   x   0   *   *   *   *   *   *   *   *   1       RW  NVRAM (read/write volatile ram, does not store to eeprom)
0   x   x   x   1   x   x   1   0   1   x   x   x   x   1   *   *   *   *   *   *   *   *   1       RW  NVRAM (all reads do /recall from eeprom, all writes do /store to eeprom)
0   x   x   x   1   x   x   1   1   0   x   x   x   x   x   x   x   x   x   *   *   *   *   x       RW  DUART (keep in mind that a0 is not connected)
0   x   x   x   1   x   x   1   1   1   x   x   x   x   x   x   x   x   x   x   x   0   0   0?       RW  SPC SR (flags): fifo-not-full (spc writable) flag (readonly, d7), fifo-not-full spc irq mask (readwrite, d6), fifo error status (readonly, d5), 'fifo release'/clear-tms-fifo-error-status-bits (writeonly, d1), speech initialize/clear (readwrite, d0) [see schematic sheet 4]
0   x   x   x   1   x   x   1   1   1   x   x   x   x   x   x   x   x   x   x   x   0   1   *       W   SPC DR fifo write (clocks fifo)
0   x   x   x   1   x   x   1   1   1   x   x   x   x   x   x   x   x   x   x   x   1   0   *       RW  TLC SR (flags): ring detect (readonly, d15), ring detected irq enable (readwrite, d14), answer phone (readwrite, d8), tone detected (readonly, d7), tone detected irq enable (readwrite, d6) [see schematic sheet 6]
0   x   x   x   1   x   x   1   1   1   x   x   x   x   x   x   x   x   x   x   x   1   1   *       R   TLC DR tone chip read, reads on bits d0-d7 only, d4-d7 are tied low; d15-d8 are probably open bus
1   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x           OPEN BUS
              |               |               |               |               |
*/

void dectalk_state::m68k_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x03ffff).mirror(0x740000).rom(); /* ROM */
	map(0x080000, 0x093fff).mirror(0x760000).ram(); /* RAM */
	map(0x094000, 0x0943ff).mirror(0x763c00).w(FUNC(dectalk_state::led_write)).umask16(0x00ff);  /* LED array */
	map(0x094000, 0x0941ff).mirror(0x763c00).rw(m_nvram, FUNC(x2212_device::read), FUNC(x2212_device::write)).umask16(0xff00); /* Xicor X2212 NVRAM */
	map(0x094200, 0x0943ff).mirror(0x763c00).rw(FUNC(dectalk_state::nvram_recall), FUNC(dectalk_state::nvram_store)).umask16(0xff00); /* Xicor X2212 NVRAM */
	map(0x098000, 0x09801f).mirror(0x763fe0).rw(m_duart, FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask16(0x00ff); /* DUART */
	map(0x09c000, 0x09c001).mirror(0x763ff8).rw(FUNC(dectalk_state::m68k_spcflags_r), FUNC(dectalk_state::m68k_spcflags_w)); /* SPC flags reg */
	map(0x09c002, 0x09c003).mirror(0x763ff8).w(FUNC(dectalk_state::m68k_infifo_w)); /* SPC fifo reg */
	map(0x09c004, 0x09c005).mirror(0x763ff8).rw(FUNC(dectalk_state::m68k_tlcflags_r), FUNC(dectalk_state::m68k_tlcflags_w)); /* telephone status flags */
	map(0x09c006, 0x09c007).mirror(0x763ff8).r(FUNC(dectalk_state::m68k_tlc_dtmf_r)); /* telephone dtmf read */
}

void dectalk_state::tms32010_mem(address_map &map)
{
	map(0x000, 0x7ff).rom(); /* ROM */
}

void dectalk_state::tms32010_io(address_map &map)
{
	map(0, 0).w(FUNC(dectalk_state::spc_latch_outfifo_error_stats)); // *set* the outfifo_status_r semaphore, and also latch the error bit at D0.
	map(1, 1).rw(FUNC(dectalk_state::spc_infifo_data_r), FUNC(dectalk_state::spc_outfifo_data_w)); //read from input fifo, write to sound fifo
	//map(8, 8) //the newer firmware seems to want something mapped here?
}

/******************************************************************************
 Input Ports
******************************************************************************/
static INPUT_PORTS_START( dectalk )
PORT_START("duart_in") // IP4, IP5, IP6 bits on duart are dipswitches (really unsoldered holes in the pcb where jumper wires can be soldered or shorted over)
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // these bits are dealt with elsewhere
	PORT_DIPNAME( 0x10, 0x00, "Skip Self Test (IP4)" )
	PORT_DIPSETTING(    0x10, "Open (VCC)" )
	PORT_DIPSETTING(    0x00, "Short to GND" )
	PORT_DIPNAME( 0x20, 0x20, "Unknown (IP5)" )
	PORT_DIPSETTING(    0x20, "Open (VCC)" )
	PORT_DIPSETTING(    0x00, "Short to GND" )
	PORT_DIPNAME( 0x40, 0x40, "Unknown (IP6)" )
	PORT_DIPSETTING(    0x40, "Open (VCC)" )
	PORT_DIPSETTING(    0x00, "Short to GND" )
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN ) // this pin (IP7) doesn't actually exist as a pin at all, reads as 1

PORT_START("hacks")
	PORT_CONFNAME( 0x01, 0x01, "Hack to prevent hang when skip self test is shorted" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )

INPUT_PORTS_END

/******************************************************************************
 Machine Drivers
******************************************************************************/

TIMER_CALLBACK_MEMBER(dectalk_state::outfifo_read_cb)
{
	uint16_t data = dsp_outfifo_r();
#ifdef VERBOSE
	if (data!= 0x8000) logerror("sample output: %04X\n", data);
#endif
	m_outfifo_read_timer->adjust(attotime::from_hz(10000));
	m_dac->write(data >> 4);
	// hack for break key, requires hacked up duart core so disabled for now
	// also it doesn't work well, the setup menu is badly corrupt
	/*if (machine.input().code_pressed(KEYCODE_F1))
	    m_duart->duart_rx_break(1, 1);
	else
	    m_duart->duart_rx_break(1, 0);*/
}

void dectalk_state::dectalk(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, XTAL(20'000'000)/2); /* E74 20MHz OSC (/2) */
	m_maincpu->set_addrmap(AS_PROGRAM, &dectalk_state::m68k_mem);
	m_maincpu->reset_cb().set(FUNC(dectalk_state::dectalk_reset));

	SCN2681(config, m_duart, XTAL(3'686'400)); // MC2681 DUART ; Y3 3.6864MHz xtal */
	m_duart->irq_cb().set_inputline(m_maincpu, M68K_IRQ_6);
	m_duart->a_tx_cb().set(FUNC(dectalk_state::duart_txa));
	m_duart->b_tx_cb().set("rs232", FUNC(rs232_port_device::write_txd));
	m_duart->inport_cb().set(FUNC(dectalk_state::duart_input));
	m_duart->outport_cb().set(FUNC(dectalk_state::duart_output));

	TMS32010(config, m_dsp, XTAL(20'000'000)); /* Y1 20MHz xtal */
	m_dsp->set_addrmap(AS_PROGRAM, &dectalk_state::tms32010_mem);
	m_dsp->set_addrmap(AS_IO, &dectalk_state::tms32010_io);
	m_dsp->bio().set(FUNC(dectalk_state::spc_semaphore_r)); //read infifo-has-data-in-it fifo readable status

#ifdef USE_LOOSE_TIMING
	config.set_maximum_quantum(attotime::from_hz(100));
#else
	config.m_perfect_cpu_quantum = subtag("dsp");
#endif

	X2212(config, "x2212");

	/* video hardware */

	/* sound hardware */
	SPEAKER(config, "speaker").front_center();
	AD7541(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.9); // ad7541.e107 (E88 10KHz OSC, handled by timer)

	/* Y2 is a 3.579545 MHz xtal for the dtmf decoder chip */

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_duart, FUNC(scn2681_device::rx_b_w));
}



/******************************************************************************
 ROM Definitions
******************************************************************************/

ROM_START( dectalk )
	ROM_REGION16_BE(0x40000,"maincpu", 0)
	// DECtalk DTC-01 firmware v2.0 (first half: 23Jul84 tag; second half: 02Jul84 tag), all roms are 27128 eproms
	// technically the correct rom names are 23-123e5.e8, etc, but the chips they were dumped from were NOT labeled that way
	// labels were SP8510123E5 etc, which means the chips were burned at dec in week 10, 1985
	/* the labels dec uses for most non-factory-programmed eproms, proms and pals is something like SPddddnnnto or WBddddnnto
	 * where:
	 * SP or WB = ?
	 * dddd is a 4 digit datecode with yyww (i.e. 8510 = week 10, 1985)
	 * nnn = the dec internal rom number for that type
	 * t = programmable chip type (e = eprom, otprom or mask rom; a,b,f = proms of various sorts; there are others for plas and pals)
	 * o = size of rom
	 *   for eproms/otproms or mask roms it is: e1 = 0x400, e2 = 0x800, e3 = 0x1000, e4 = 0x2000, e5 = 0x4000, e6 = 0x8000, etc)
	 *   for proms it is: a1 = 82s123(0x20, 8b TS); a2 = 82s129(0x100 4b TS); a9 = 82s131(0x200 4b TS); b1 = 82s135(0x100 8b TS); f1 = 82s137(0x400 4b TS); f4 = 82s191(0x800 8b TS); s0 = MC68HC05; m2 = i8051 or other MCS-51; (there are more)
	 */
	ROM_SYSTEM_BIOS( 0, "v20", "DTC-01 Version 2.0")
	ROMX_LOAD("23-123e5.e8", 0x00000, 0x4000, CRC(03e1eefa) SHA1(e586de03e113683c2534fca1f3f40ba391193044), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "SP8510123E5" @ E8
	ROMX_LOAD("23-119e5.e22", 0x00001, 0x4000, CRC(af20411f) SHA1(7954bb56b7591f8954403a22d34de31c7d5441ac), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "SP8510119E5" @ E22
	ROMX_LOAD("23-124e5.e7", 0x08000, 0x4000, CRC(9edeafcb) SHA1(7724babf4ae5d77c0b4200f608d599058d04b25c), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "SP8510124E5" @ E7
	ROMX_LOAD("23-120e5.e21", 0x08001, 0x4000, CRC(f2a346a6) SHA1(af5e4ea0b3631f7d6f16c22e86a33fa2cb520ee0), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "SP8510120E5" @ E21
	ROMX_LOAD("23-125e5.e6", 0x10000, 0x4000, CRC(1c0100d1) SHA1(1b60cd71dfa83408b17e13f683b6bf3198c905cc), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "SP8510125E5" @ E6
	ROMX_LOAD("23-121e5.e20", 0x10001, 0x4000, CRC(4cb081bd) SHA1(4ad0b00628a90085cd7c78a354256c39fd14db6c), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "SP8510121E5" @ E20
	ROMX_LOAD("23-126e5.e5", 0x18000, 0x4000, CRC(7823dedb) SHA1(e2b2415eec838ddd46094f2fea93fd289dd0caa2), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "SP8510126E5" @ E5
	ROMX_LOAD("23-122e5.e19", 0x18001, 0x4000, CRC(b86370e6) SHA1(92ab22a24484ad0d0f5c8a07347105509999f3ee), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "SP8510122E5" @ E19
	ROMX_LOAD("23-103e5.e4", 0x20000, 0x4000, CRC(35aac6b9) SHA1(b5aec0bf37a176ff4d66d6a10357715957662ebd), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "SP8510103E5" @ E4
	ROMX_LOAD("23-095e5.e18", 0x20001, 0x4000, CRC(2296fe39) SHA1(891f3a3b4ce75ef14001257bc8f1f60463a9a7cb), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "SP8510095E5" @ E18
	ROMX_LOAD("23-104e5.e3", 0x28000, 0x4000, CRC(9658b43c) SHA1(4d6808f67cbdd316df23adc8ddf701df57aa854a), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "SP8510104E5" @ E3
	ROMX_LOAD("23-096e5.e17", 0x28001, 0x4000, CRC(cf236077) SHA1(496c69e52cfa013173f7b9c500ce544a03ad01f7), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "SP8510096E5" @ E17
	ROMX_LOAD("23-105e5.e2", 0x30000, 0x4000, CRC(09cddd28) SHA1(de0c25687bab3ff0c88c98622092e0b58331aa16), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "SP8510105E5" @ E2
	ROMX_LOAD("23-097e5.e16", 0x30001, 0x4000, CRC(49434da1) SHA1(c450abae0ccf372d7eb87370b8a8c97a45e164d3), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "SP8510097E5" @ E16
	ROMX_LOAD("23-106e5.e1", 0x38000, 0x4000, CRC(a389ab31) SHA1(355348bfc96a04193136cdde3418366e6476c3ca), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "SP8510106E5" @ E1
	ROMX_LOAD("23-098e5.e15", 0x38001, 0x4000, CRC(3d8910e7) SHA1(01921e77b46c2d4845023605239c45ffa4a35872), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "SP8510098E5" @ E15

	// DECtalk DTC-01 firmware v1.8 (first half: 05Dec83 tag; second half: 11Oct83 tag), all roms are 27128 eproms
	ROM_SYSTEM_BIOS( 1, "v18", "DTC-01 Version 1.8")
	ROMX_LOAD("23-063e5.e8", 0x00000, 0x4000, CRC(9f5ca045) SHA1(1b1b9c1e092c44329b385fb04001e13422eb8d39), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("23-059e5.e22", 0x00001, 0x4000, CRC(b299cf64) SHA1(84bbe9ff303ea6ce7b1c0b1ad05421edd18fae49), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("23-064e5.e7", 0x08000, 0x4000, CRC(e4ff20f4) SHA1(fdd91e4d2ef92608a08b2e78b6108e31ff53a1f9), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("23-060e5.e21", 0x08001, 0x4000, CRC(213c65ba) SHA1(c95662d0d40499af01cdc23f05936762ab54081a), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("23-065e5.e6", 0x10000, 0x4000, CRC(38ea0c75) SHA1(232b622cef6d69a493db1ed02e5236235c68daba), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("23-061e5.e20", 0x10001, 0x4000, CRC(44f6fe5c) SHA1(81daa4abae273c7f0aead902b5c3c842f7e7f116), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("23-066e5.e5", 0x18000, 0x4000, CRC(957aa8cf) SHA1(5f9f916b99867d1adbafd58d411feb630f6e4b6d), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("23-062e5.e19", 0x18001, 0x4000, CRC(10ab969c) SHA1(46ee22a295b8709b6f829751aca5f92e4f459a9f), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("23-032e5.e4", 0x20000, 0x4000, CRC(0f805e3a) SHA1(1d8008e30a448358224364fd8237dbb08907b219), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("23-031e5.e18", 0x20001, 0x4000, CRC(846b5b68) SHA1(55c759b3fb927d2dfc9d77e8e080748866bea854), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("23-034e5.e3", 0x28000, 0x4000, CRC(90700738) SHA1(738337c5b6acd3f30c3c4be2457370d2ce9313f9), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("23-033e5.e17", 0x28001, 0x4000, CRC(48756a4d) SHA1(5946ccd367d88a484bb1549d0cc990b9b7d88f0c), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("23-036e5.e2", 0x30000, 0x4000, CRC(5c2d4f73) SHA1(30f95e5383c4f71bc700346e2d49e8ad70b94c8c), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("23-035e5.e16", 0x30001, 0x4000, CRC(80116443) SHA1(7b3b68e61b421dedaad88b5600c739943a316c9e), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("23-038e5.e1", 0x38000, 0x4000, CRC(c840493f) SHA1(abd6af442690e981a9089f19febffc8f3fb52717), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("23-037e5.e15", 0x38001, 0x4000, CRC(d62ab309) SHA1(a743a23625feadf6e46ef889e2bb04af88589992), ROM_SKIP(1) | ROM_BIOS(1))

	ROM_REGION(0x2000,"dsp", 0)
	// older dsp firmware from earlier dectalk firmware 2.0 units, both proms are 82s191 equivalent; this dsp firmware clips with the 1.8 dectalk firmware. this lacks the debug code?
	ROMX_LOAD("23-205f4.e70", 0x000, 0x800, CRC(ed76a3ad) SHA1(3136bae243ef48721e21c66fde70dab5fc3c21d0), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "LM8506205F4 // M1-76161-5" @ E70
	ROMX_LOAD("23-204f4.e69", 0x001, 0x800, CRC(79bb54ff) SHA1(9409f90f7a397b041e4440341f2d7934cb479285), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "LM8504204F4 // 78S191" @ E69
	// Final? firmware from 2.0 dectalk firmware units; this dsp firmware clips with the 1.8 dectalk firmware
	// this firmware seems to have some leftover test garbage mapped into its space, which is not present on the dtc-01 board
	// it writes 0x0000 to 0x90 on start, and it writes a sequence of values to 0xFF down to 0xE9
	// it also wants something readable mapped at 0x08 (for debug purposes?) or else it waits for an interrupt (as the older firmware always does)
	ROMX_LOAD("23-410f4.e70", 0x000, 0x800, CRC(121e2ec3) SHA1(3fabe018d0e0b478093951cb20501853358faa18), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("23-409f4.e69", 0x001, 0x800, CRC(61f67c79) SHA1(9a13426c92f879f2953f180f805990a91c37ac43), ROM_SKIP(1) | ROM_BIOS(0))
	// older dsp firmware from dectalk firmware 1.8 units; while this dsp firmware works with 2.0 dectalk firmware, its a bit quieter than the proper one.
	ROMX_LOAD("23-166f4.e70", 0x000, 0x800, CRC(2d036ffc) SHA1(e8c25ca092dde2dc0aec73921af806026bdfbbc3), ROM_SKIP(1) | ROM_BIOS(1)) // HM1-76161-5
	ROMX_LOAD("23-165f4.e69", 0x001, 0x800, CRC(a3019ca4) SHA1(249f269c38f7f44edb6d025bcc867c8ca0de3e9c), ROM_SKIP(1) | ROM_BIOS(1)) // HM1-76161-5

	// TODO: load this as default if the nvram file is missing, OR get the setup page working enough that it can be saved properly to the chip from an NVR FAULT state!
	// NOTE: this nvram image is ONLY VALID for v2.0; v1.8 expects a different image.
	ROM_REGION(0x100,"nvram", 0) // default nvram image is at 0x1A7AE in main rom, read lsn first so 0x0005 in rom becomes 05 00 00 00 etc for all words of main rom
	ROM_FILL(0x00, 0xff, 0x00) // blank it first;
	ROM_FILL(0x00, 0x01, 0x05)
	ROM_FILL(0x04, 0x01, 0x00)
	ROM_FILL(0x08, 0x01, 0x06)
	ROM_FILL(0x0c, 0x01, 0x01)
	ROM_FILL(0x10, 0x01, 0x06)
	ROM_FILL(0x14, 0x01, 0x0b)
	ROM_FILL(0x18, 0x01, 0x02)
	ROM_FILL(0x1c, 0x01, 0x02)
	ROM_FILL(0x20, 0x01, 0x01)
	ROM_FILL(0x24, 0x01, 0x01)
	ROM_FILL(0x28, 0x01, 0x00)
	ROM_FILL(0x2c, 0x01, 0x01)
	ROM_FILL(0xfc, 0x01, 0x0d) // checksum, calculated some weird way which I haven't figured out yet
	ROM_FILL(0xfd, 0x01, 0x02) // "
	ROM_FILL(0xfe, 0x01, 0x05) // "
	ROM_FILL(0xff, 0x01, 0x0b) // "
ROM_END

} // anonymous namespace


/******************************************************************************
 Drivers
******************************************************************************/

/*    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY                          FULLNAME          FLAGS */
COMP( 1984, dectalk, 0,      0,      dectalk, dectalk, dectalk_state, empty_init, "Digital Equipment Corporation", "DECtalk DTC-01", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



decwritr.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Jonathan Gevaryahu
/*************************************************************************

    decwritr.c
    Digital Equipment Corporation
    DECwriter III (LA120) Teletype/Teleprinter, 1978

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

// tech manual: http://manx.classiccmp.org/mirror/vt100.net/docs/la120-tm/la120tm1.pdf

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/i8085/i8085.h"
#include "machine/74259.h"
#include "dc305.h"
#include "machine/er1400.h"
#include "machine/i8251.h"
#include "machine/input_merger.h"
#include "sound/beep.h"
#include "screen.h"
#include "speaker.h"


namespace {

#define KBD_VERBOSE 1
#define LED_VERBOSE 0
#define DC305_VERBOSE 0

//**************************************************************************
//  DRIVER STATE
//**************************************************************************

class decwriter_state : public driver_device
{
public:
	// constructor
	decwriter_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_speaker(*this, "beeper"),
		m_usart(*this, "usart"),
		m_nvm(*this, "nvm"),
		m_ledlatch(*this, "ledlatch"),
		m_prtlsi(*this, "prtlsi"),
		m_col_array(*this, "COL%X", 0U)
	{
	}

	void la120(machine_config &config);
private:
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
	{
		bitmap.fill(rgb_t::black(), cliprect);
		return 0;
	}

	IRQ_CALLBACK_MEMBER(inta_cb);

	uint8_t la120_KBD_r(offs_t offset);
	void la120_LED_w(offs_t offset, uint8_t data);
	uint8_t la120_NVR_r();
	void la120_NVR_w(offs_t offset, uint8_t data);
	uint8_t la120_DC305_r(offs_t offset);
	void la120_DC305_w(offs_t offset, uint8_t data);

	void la120_io(address_map &map) ATTR_COLD;
	void la120_mem(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	//virtual void machine_reset();

	required_device<cpu_device> m_maincpu;
	required_device<beep_device> m_speaker;
	required_device<i8251_device> m_usart;
	required_device<er1400_device> m_nvm;
	required_device<ls259_device> m_ledlatch;
	required_device<dc305_device> m_prtlsi;

	required_ioport_array<16> m_col_array;
	uint8_t m_led_7seg_counter = 0;
	uint8_t m_led_7seg[4]{};
};

IRQ_CALLBACK_MEMBER( decwriter_state::inta_cb )
{
	// one wait state inserted
	if (!machine().side_effects_disabled())
		m_maincpu->adjust_icount(-1);

	return m_prtlsi->inta();
}

uint8_t decwriter_state::la120_KBD_r(offs_t offset)
{
	/* for reading the keyboard array, addr bits 5-11 are ignored.
	 * a15 a14 a13 a12 a11 a10  a9  a8  a7  a6  a5  a4  a3  a2  a1  a0
	 *   0   0   1   1   x   x   x   x   x   x   x   *   *   *   *   *
	 *                                               |   |   \---\---\--- column select
	 *                                               |   \--------------- kbd_banksel0
	 *                                               \------------------- kbd_banksel1
	 *      if both banks are selected, the resulting row read is a binary OR of both (LA120-TM1, page 4-5)
	 *
	 * d7 d6 d5 d4 d3 d2 d1 d0
	 *  \--\--\--\--\--\--\--\-- read from rows
	 */
	uint8_t code = 0;
	if (offset&0x8) code |= m_col_array[offset&0x7]->read();
	if (offset&0x10) code |= m_col_array[(offset&0x7)+8]->read();
	if (KBD_VERBOSE)
		logerror("Keyboard column %X read, returning %02X\n", offset&0xF, code);
	return code;
}

void decwriter_state::la120_LED_w(offs_t offset, uint8_t data)
{
	/* for writing the keyboard array, addr bits 5-11 are ignored.
	 * a15 a14 a13 a12 a11 a10  a9  a8  a7  a6  a5  a4  a3  a2  a1  a0
	 *   0   0   1   1   x   x   x   x   x   x   x   0   *   *   *   0    turn OFF LED # <a3:a1> and clear 7seg counter
	 *   0   0   1   1   x   x   x   x   x   x   x   0   *   *   *   1    turn ON LED # <a3:a1> and clear 7seg counter
	 *   0   0   1   1   x   x   x   x   x   x   x   1   *   *   *   *    display a digit <a3:a0> in the digit pointed to by 7seg counter and increment counter
	 * data bus is UNUSED.
	 */
	if (!(offset&0x10)) // we're updating an led state
	{
		if (LED_VERBOSE)
			logerror("Updated LED status array: LED #%d is now %d\n", ((offset&0xe)>>1), (offset&1));
		m_ledlatch->write_bit((~offset & 0xe) >> 1, !BIT(offset, 0));
		m_led_7seg_counter = 0;
	}
	else // we're updating the 7segment display
	{
		m_led_7seg_counter++;
		m_led_7seg_counter &= 0xF;
		if (LED_VERBOSE)
			logerror("Updated 7seg display: displaying a digit of %d in position %d\n", (offset&0xF)^0xF, m_led_7seg_counter-1);
		if ((m_led_7seg_counter >= 1) && (m_led_7seg_counter <= 4))
		{
			m_led_7seg[m_led_7seg_counter-1] = (offset&0xF)^0xF;
		}
	}
	popmessage("LEDs: %c %c %c %c : %s  %s  %s  %s  %s  %s  %s  %s\n",
	m_led_7seg[3]+0x30, m_led_7seg[2]+0x30, m_led_7seg[1]+0x30, m_led_7seg[0]+0x30,
	(!m_ledlatch->q0_r())?"ON LINE":"-------",
	(!m_ledlatch->q1_r())?"LOCAL":"-----",
	(!m_ledlatch->q2_r())?"ALT CHR SET":"-----------",
	(!m_ledlatch->q3_r())?"<LED4>":"-----",
	(!m_ledlatch->q4_r())?"CTS":"---",
	(!m_ledlatch->q5_r())?"DSR":"---",
	(!m_ledlatch->q6_r())?"SET-UP":"------",
	(!m_ledlatch->q7_r())?"PAPER OUT":"---------" );
}

/* control lines:
   3 2 1
   0 0 0 Standby
   0 0 1 Read
   0 1 0 Erase
   0 1 1 Write
   1 0 0 <unused>
   1 0 1 Shift data out
   1 1 0 Accept address
   1 1 1 Accept data
   */
uint8_t decwriter_state::la120_NVR_r()
{
	// one wait state inserted
	if (!machine().side_effects_disabled())
		m_maincpu->adjust_icount(-1);

	return (!m_nvm->data_r() << 7) | 0x7f;
}

void decwriter_state::la120_NVR_w(offs_t offset, uint8_t data)
{
	// one wait state inserted
	if (!machine().side_effects_disabled())
		m_maincpu->adjust_icount(-1);

	// ER1400 has negative logic, but 7406 inverters are used
	m_nvm->c3_w(BIT(offset, 10));
	m_nvm->c2_w(BIT(offset, 9));
	m_nvm->c1_w(BIT(offset, 8));
	m_nvm->clock_w(BIT(offset, 0));

	// C2 is used to disable pullup on data line
	m_nvm->data_w(BIT(offset, 9) ? !BIT(data, 7) : 1);
}

/* todo: fully reverse engineer DC305 ASIC */
/* read registers: all 4 registers read the same set of 8 bits, but what register is being read may be selectable by writing
   Tech manual implies this register is an 8-bit position counter of where the carriage head currently is located.
   0 = 1 = 2 = 3
   data bits:
   76543210
   |||||||\- ?
   ||||||\-- ?
   |||||\--- ?
   ||||\---- ?
   |||\----- ?
   ||\------ ?
   |\------- ?
   \-------- ?
 */
uint8_t decwriter_state::la120_DC305_r(offs_t offset)
{
	// one wait state inserted
	if (!machine().side_effects_disabled())
		m_maincpu->adjust_icount(-1);

	uint8_t data = m_prtlsi->read(offset);
	if (DC305_VERBOSE)
		logerror("DC305 Read from offset %01x, returning %02X\n", offset, data);
	return data;
}
/* write registers:
   0 = ? (a bunch of data written here on start, likely motor control and setup bits)
   1 = ? (one byte written here, possibly voltage control, 0x00 or could be dot fifo write?)
   2 = ?
   3 = ?
   there are at least two bits in here to enable the 2.5ms tick interrupt(rst3) and the dot interrupt/linefeed(rtc expired) interrupt(rst5)
   the dot fifo is 4 bytes long, dot int fires when it is half empty
   at least 3 bits control the speaker/buzzer which can be on or off, at least two volume levels, and at least two frequencies, 400hz or 2400hz
   two quadrature lines from the head servomotor connect here to allow the dc305 to determine motor position; one pulses when the motor turns clockwise and one when it turns counterclockwise. the head stop is found when the pulses stop, which firmware uses to find the zero position.
 */
void decwriter_state::la120_DC305_w(offs_t offset, uint8_t data)
{
	// one wait state inserted
	if (!machine().side_effects_disabled())
		m_maincpu->adjust_icount(-1);

	m_prtlsi->write(offset, data);
	if (DC305_VERBOSE)
		logerror("DC305 Write of %02X to offset %01X\n", data, offset);
}

/*
 * 8080  address map (x = ignored; * = selects address within this range)
   a15 a14 a13 a12 a11 a10 a9  a8  a7  a6  a5  a4  a3  a2  a1  a0
   0   0   0   0   *   *   *   *   *   *   *   *   *   *   *   *     R      ROMS0 (e6 first half OR e6,e8)
   0   0   0   1   *   *   *   *   *   *   *   *   *   *   *   *     R      ROMS1 (e6 second half OR e12,e17)
   0   0   1   0   0   *   *   *   *   *   *   *   *   *   *   *     R      ROMS2 (e4)
   0   0   1   0   1   *   *   *   *   *   *   *   *   *   *   *     R      ROMS2 (open bus)
   0   0   1   1   x   x   x   x   x   x   x   *   *   *   *   *     RW     KBD(R)/LED(W)
   0   1   0   0   x   x   *   *   *   *   *   *   *   *   *   *     RW     RAM0 (e7,e13)
   0   1   0   1   x   x   *   *   *   *   *   *   *   *   *   *     RW     RAM1 (e9,e18)
   0   1   1   0   x   *   *   *   x   x   x   x   x   x   x   *     RW     NVM (ER1400,e39)
   0   1   1   1   x   x   x   x   x   x   x   x   x   x   *   *     RW     PTR (DC305 ASIC,e25)
   1   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *            Expansion space (open bus)
 */
void decwriter_state::la120_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x27ff).rom();
	map(0x3000, 0x301f).rw(FUNC(decwriter_state::la120_KBD_r), FUNC(decwriter_state::la120_LED_w)).mirror(0xFE0); // keyboard read, write to status and 7seg LEDS
	map(0x4000, 0x43ff).mirror(0x0c00).ram(); // 1k 'low ram'
	map(0x5000, 0x53ff).mirror(0x0c00).ram(); // 1k 'high ram'
	map(0x6000, 0x67ff) /*.mirror(0x08fe)*/ .mirror(0x800).rw(FUNC(decwriter_state::la120_NVR_r), FUNC(decwriter_state::la120_NVR_w)); // ER1400 EAROM; a10,9,8 are c3,2,1, a0 is clk, data i/o on d7, d0 always reads as 0 (there may have once been a second er1400 with data i/o on d0, sharing same address controls as the d7 one, not populated on shipping boards), d1-d6 read open bus
	map(0x7000, 0x7003).mirror(0x0ffc).rw(FUNC(decwriter_state::la120_DC305_r), FUNC(decwriter_state::la120_DC305_w)); // DC305 printer controller ASIC stuff; since this can generate interrupts (dot interrupt, lf interrupt, 2.5ms interrupt) this needs to be split to its own device.
	// 8000-ffff is reserved for expansion (i.e. unused, open bus)
}

/*
 * 8080 IO address map (x = ignored; * = selects address within this range)
 * (a15 to a8 are latched the same value as a7-a0 on the 8080 and 8085)
   a7  a6  a5  a4  a3  a2  a1  a0
   0   x   x   x   x   x   0   0     RW     8251 Data
   0   x   x   x   x   x   0   1     RW     8251 Status/Control
   0   x   x   x   x   x   1   x     RW     Flags Read/Write
   1   x   x   x   x   x   x   x     RW     Expansion (Open bus)
 */
void decwriter_state::la120_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x01).mirror(0x7C).rw(m_usart, FUNC(i8251_device::read), FUNC(i8251_device::write)); // 8251 Status/Control
	//map(0x02, 0x02).mirror(0x7D); // other io ports, serial loopback etc, see table 4-9 in TM
	// 0x80-0xff are reserved for expansion (i.e. unused, open bus)
	map.global_mask(0xff);
}

/* Input ports */
static INPUT_PORTS_START( la120 )
	PORT_START("COL0")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Num .") PORT_CODE(KEYCODE_DEL_PAD)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("PF4") PORT_CODE(KEYCODE_F4)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Local LF") PORT_CODE(KEYCODE_F12)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_F6)
		PORT_BIT(0x60, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_START("COL1")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("PF3") PORT_CODE(KEYCODE_F3)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Local FF") PORT_CODE(KEYCODE_F11)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("\\ |") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Delete") PORT_CODE(KEYCODE_DEL) PORT_CHAR(127)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
		PORT_BIT(0x60, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED) // this is actually the cover open interlock sensor
	PORT_START("COL2")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("PF2") PORT_CODE(KEYCODE_F2)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("HERE IS") PORT_CODE(KEYCODE_F10)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(alternate) Opt LF") PORT_CODE(KEYCODE_RALT)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("` ~") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('`') PORT_CHAR('~')
		PORT_BIT(0x60, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED) // this is actually the paper out interlock sensor
	PORT_START("COL3")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("PF1") PORT_CODE(KEYCODE_F1)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LF") PORT_CODE(KEYCODE_LALT) PORT_CHAR(10)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LINE LOCAL") PORT_CODE(KEYCODE_F9)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("] }") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("= +") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
		PORT_BIT(0x60, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_START("COL4")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Num -") PORT_CODE(KEYCODE_MINUS_PAD)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Num ,") PORT_CODE(KEYCODE_PLUS_PAD)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("' \"") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("[ {") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("- _") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
		PORT_BIT(0x60, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_TOGGLE PORT_NAME("Caps lock") PORT_CODE(KEYCODE_CAPSLOCK) // This key has a physical toggle
	PORT_START("COL5")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Num 0") PORT_CODE(KEYCODE_0_PAD)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("; :") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0 )") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
		PORT_BIT(0x60, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_START("COL6")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Num 9") PORT_CODE(KEYCODE_9_PAD)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9 (") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
		PORT_BIT(0x60, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_START("COL7")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Num 8") PORT_CODE(KEYCODE_8_PAD)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8 *") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
		PORT_BIT(0x60, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Set Up") PORT_CODE(KEYCODE_F5)
	PORT_START("COL8")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Num 7") PORT_CODE(KEYCODE_7_PAD)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7 &") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
		PORT_BIT(0xe0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COL9")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Num 6") PORT_CODE(KEYCODE_6_PAD)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6 ^") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
		PORT_BIT(0xe0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COLA")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Num 5") PORT_CODE(KEYCODE_5_PAD)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5 %") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
		PORT_BIT(0xe0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COLB")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Num 4") PORT_CODE(KEYCODE_4_PAD)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4 $") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
		PORT_BIT(0xe0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COLC")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Num 3") PORT_CODE(KEYCODE_3_PAD)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3 #") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
		PORT_BIT(0xe0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COLD")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Num 2") PORT_CODE(KEYCODE_2_PAD)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2 @") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
		PORT_BIT(0xe0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COLE")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Num 1") PORT_CODE(KEYCODE_1_PAD)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1 !") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
		PORT_BIT(0xe0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COLF")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Num Enter") PORT_CODE(KEYCODE_ENTER_PAD)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED) // this is actually the RO flag
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("View") PORT_CODE(KEYCODE_LWIN)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
		PORT_BIT(0xe0, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

void decwriter_state::machine_start()
{
	m_led_7seg_counter = 0;
	m_led_7seg[0] = m_led_7seg[1] = m_led_7seg[2] = m_led_7seg[3] = 0xF;
}

/*
void decwriter_state::machine_reset()
{
}
*/

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

void decwriter_state::la120(machine_config &config)
{
	I8080A(config, m_maincpu, XTAL(18'000'000) / 9); // 18Mhz xtal on schematics, using an i8224 clock divider/reset sanitizer IC
	m_maincpu->set_addrmap(AS_PROGRAM, &decwriter_state::la120_mem);
	m_maincpu->set_addrmap(AS_IO, &decwriter_state::la120_io);
	m_maincpu->set_irq_acknowledge_callback(FUNC(decwriter_state::inta_cb));

	/* video hardware */
	//TODO: no actual screen! has 8 leds above the keyboard (similar to vt100/vk100) and has 4 7segment leds for showing an error code.
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_screen_update(FUNC(decwriter_state::screen_update));
	screen.set_size(640,480);
	screen.set_visarea_full();
	screen.set_refresh_hz(30);

	DC305(config, m_prtlsi, XTAL(18'000'000) / 9);
	m_prtlsi->rxc_callback().set("usart", FUNC(i8251_device::write_rxc));
	m_prtlsi->txc_callback().set("usart", FUNC(i8251_device::write_txc));
	m_prtlsi->int_callback().set("mainint", FUNC(input_merger_device::in_w<0>));

	LS259(config, m_ledlatch); // E2 on keyboard
	m_ledlatch->q_out_cb<0>().set_output("led1").invert(); // ON LINE
	m_ledlatch->q_out_cb<1>().set_output("led2").invert(); // LOCAL
	m_ledlatch->q_out_cb<2>().set_output("led3").invert(); // ALT CHAR SET
	m_ledlatch->q_out_cb<3>().set_output("led4").invert();
	m_ledlatch->q_out_cb<4>().set_output("led5").invert(); // CTS
	m_ledlatch->q_out_cb<5>().set_output("led6").invert(); // DSR
	m_ledlatch->q_out_cb<6>().set_output("led7").invert(); // SETUP
	m_ledlatch->q_out_cb<7>().set_output("led8").invert(); // PAPER OUT

	//config.set_default_layout(layout_la120);

	/* audio hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_speaker, 786).add_route(ALL_OUTPUTS, "mono", 0.50); // TODO: LA120 speaker is controlled by asic; VT100 has: 7.945us per serial clock = ~125865.324hz, / 160 clocks per char = ~ 786 hz

	/* i8251 */
	i8251_device &usart(I8251(config, "usart", XTAL(18'000'000) / 9));
	usart.txd_handler().set("eia", FUNC(rs232_port_device::write_txd));
	usart.dtr_handler().set("eia", FUNC(rs232_port_device::write_dtr));
	usart.rts_handler().set("eia", FUNC(rs232_port_device::write_rts));
	usart.rxrdy_handler().set("mainint", FUNC(input_merger_device::in_w<1>));

	INPUT_MERGER_ANY_HIGH(config, "mainint").output_handler().set_inputline(m_maincpu, 0);

	rs232_port_device &rs232(RS232_PORT(config, "eia", default_rs232_devices, nullptr));
	rs232.rxd_handler().set("usart", FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set("usart", FUNC(i8251_device::write_dsr));

	ER1400(config, m_nvm);
}



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

ROM_START( la120 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	// later romset, with 23-003e2.e6, 23-004e2.e8, 23-005e2.e12, 23-006e2.e17 replaced by one rom, 23-038e4.e6 which may be a concatenation of the old roms, unclear.
	ROM_LOAD( "23-038e4-00.e6", 0x0000, 0x2000, CRC(cad4eb09) SHA1(d5db117da363d36817476f906251ea4ee1cb14b8))
	ROM_LOAD( "23-007e2-00.e4", 0x2000, 0x0800, CRC(41eaebf1) SHA1(c7d05417b24b853280d1636776d399a0aea34720)) // used by both earlier and later romset
	// there is an optional 3 roms, european and APL (and BOTH) rom which goes from 2000-2fff in e4, all undumped.
	// there is another romset used on the Bell Teleprinter 1000 (Model LAS12) which I believe is 23-004e4.e6 and 23-086e2.e4
ROM_END

} // anonymous namespace


//**************************************************************************
//  DRIVERS
//**************************************************************************
/*    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS            INIT        COMPANY                          FULLNAME                 FLAGS */
COMP( 1978, la120, 0,      0,      la120,   la120, decwriter_state, empty_init, "Digital Equipment Corporation", "DECwriter III (LA120)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



deecoseal.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Carl
/***************************************************************************

    Lucas Deeco SealTouch

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

#include "emu.h"
#include "cpu/i86/i186.h"
#include "cpu/mcs51/mcs51.h"
#include "screen.h"
#include "emupal.h"
#include "video/upd7220.h"
#include "bus/rs232/rs232.h"
#include "machine/mc68681.h"
#include "machine/i2cmem.h"
#include "machine/nvram.h"

namespace {

class deecoseal_state : public driver_device
{
public:
	deecoseal_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_mcs51(*this, "mcs51"),
		m_crtc(*this, "crtc"),
		m_duart(*this, "duart"),
		m_palette(*this, "palette"),
		m_eeprom(*this, "eeprom"),
		m_vram(*this, "vram")
	{ }

	void deecoseal(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	void map(address_map &map);
	void io(address_map &map);
	void mcs51_map(address_map &map);
	void crtc_map(address_map &map);
	UPD7220_DISPLAY_PIXELS_MEMBER(draw);
	u8 i2c_r();
	void i2c_w(u8 data);

	bool m_port0;

	required_device<i80186_cpu_device> m_maincpu;
	required_device<cpu_device> m_mcs51;
	required_device<upd7220_device> m_crtc;
	required_device<scn2681_device> m_duart;
	required_device<palette_device> m_palette;
	required_device<i2c_x2404p_device> m_eeprom;
	required_shared_ptr<u16> m_vram;
};

void deecoseal_state::machine_start()
{
	u16 *rom = (u16 *)memregion("bios")->base();
	u16 swapbuf[128];

	for(int i = 0; i < 32768; i += 128)
	{
		u16 *cur = rom + i;
		memcpy(swapbuf, cur, 256);
		for(int j = 0; j < 128; j++)
		{
			int addr = 0;
			addr |= ((BIT(j, 3) & !BIT(j, 5)) | (BIT(j, 4) & BIT(j, 5)));
			addr |= ((BIT(j, 3) & BIT(j, 5)) | (BIT(j, 4) & !BIT(j, 5))) << 1;
			addr |= ((BIT(j, 0) & BIT(j, 6)) | (BIT(j, 5) & !BIT(j, 6))) << 2;
			addr |= ((BIT(j, 0) & !BIT(j, 6)) | (BIT(j, 5) & BIT(j, 6))) << 3;
			addr |= BIT(j, 6) << 4;
			addr |= ((BIT(j, 1) & !BIT(j, 4)) | (BIT(j, 2) & BIT(j, 4))) << 5;
			addr |= ((BIT(j, 1) & BIT(j, 4)) | (BIT(j, 2) & !BIT(j, 4))) << 6;
			cur[j] = swapbuf[addr];
		}
	}

	m_port0 = false;
}

void deecoseal_state::map(address_map &map)
{
	map(0x00000, 0x0ffff).ram();
	map(0x40000, 0x4ffff).ram().share("nvram");
	map(0xf0000, 0xfffff).rom().region("bios", 0);
}

void deecoseal_state::io(address_map &map)
{
	map(0x0000, 0x0001).lr16([this](){ m_port0 = !m_port0; return m_port0 ? 0x10 : 0; }, "port0");
	map(0x0110, 0x011f).rw(m_duart, FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask16(0x00ff);
	map(0x0180, 0x0183).rw(m_crtc, FUNC(upd7220_device::read), FUNC(upd7220_device::write)).umask16(0x00ff);
	map(0x0200, 0x0200).rw(FUNC(deecoseal_state::i2c_r), FUNC(deecoseal_state::i2c_w));
}

void deecoseal_state::mcs51_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("mcs51", 0);
}

void deecoseal_state::crtc_map(address_map &map)
{
	map(0x0000, 0xffff).ram().share("vram");
}

UPD7220_DISPLAY_PIXELS_MEMBER( deecoseal_state::draw )
{
	u16 const gfx = m_vram[address];
	pen_t const *const pen = m_palette->pens();

	for(u16  i = 0; i < 16; i++)
		bitmap.pix(y, x + i) = pen[BIT(gfx, i)];
}

u8 deecoseal_state::i2c_r()
{
	return m_eeprom->read_sda() ? 0x10 : 0;
}

void deecoseal_state::i2c_w(u8 data)
{
	m_eeprom->write_scl(data & 0x40 ? 1 : 0);
	m_eeprom->write_sda(data & 0x10 ? 1 : 0);
}

void deecoseal_state::deecoseal(machine_config &config)
{
	I80186(config, m_maincpu, XTAL(24'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &deecoseal_state::map);
	m_maincpu->set_addrmap(AS_IO, &deecoseal_state::io);

	I80C31(config, m_mcs51, XTAL(24'000'000) / 4).set_disable(); // clock?
	m_mcs51->set_addrmap(AS_PROGRAM, &deecoseal_state::mcs51_map);

	I2C_X2404P(config, m_eeprom);

	NVRAM(config, "nvram",  nvram_device::DEFAULT_ALL_1); // 2x 28c256

	UPD7220(config, m_crtc, XTAL(16'257'000) / 2); // unknown clock
	m_crtc->set_addrmap(AS_PROGRAM, &deecoseal_state::crtc_map);
	m_crtc->set_screen("screen");
	m_crtc->set_display_pixels(FUNC(deecoseal_state::draw));

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	SCN2681(config, m_duart, XTAL(3'686'400)); // scn2692

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD, rgb_t::amber()));
	screen.set_raw(XTAL(16'257'000), 848, 0, 640, 440, 0, 400);
	screen.set_screen_update(m_crtc, FUNC(upd7220_device::screen_update));
}

ROM_START(deecoseal)
	ROM_REGION16_LE(0x10000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "bios", "bios")
	ROMX_LOAD("u28_27256_lo_rev_k_lucas_1990.bin", 0x0000, 0x8000, CRC(4f5e0d47) SHA1(646e369464b66705dbab888c8f351121ffc4bc37), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("u10_27256_hi_rev_k_lucas_1990.bin", 0x0001, 0x8000, CRC(20f7d631) SHA1(797482bc6b4427b8bf750608c5a83185fae02405), ROM_SKIP(1) | ROM_BIOS(0))

	ROM_REGION(0x8000, "mcs51", 0)
	ROM_LOAD("u34_27256_rev_b_lucas_1989.bin", 0x0000, 0x8000, CRC(01574116) SHA1(75d8dc739bf4ef1db91341f2fb02e0c698e39536))
ROM_END

} // anonymous namespace


COMP(1990, deecoseal, 0, 0, deecoseal, 0, deecoseal_state, empty_init, "Lucas", "Deeco SealTouch ST3220", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



delta1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

SciSys Delta-1, it was sold by both Novag and SciSys, ROM has "COPY RIGHT WINKLER
HK 1979", Winkler was the founder of SciSys(later renamed to Saitek). It was
released in late-1980, even though it says 1979 in the ROM and manual.

Hardware notes:
- 3850PK CPU at ~2MHz, 3853PK memory interface
- 4KB ROM(2332A), 256 bytes RAM(2*2111A-4)
- 4-digit 7seg panel, no sound, no chessboard

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

Program origin notes by bataais:

They (some SciSys engineers) took the rom of Boris Master (rev. 1) and tried to
rewrite it for a 4x 7-Segment LED, unfortunately they botched the hack job, the
timer interrupt calls the new display routine much more frequently and so loses
time for chess calculations; and more weird stuff is going on.

No wonder it plays so weak.

In the Delta-1 ROM is even some fragmented code remaining of the message:

0878:BORIS AWAITS YOUR MOVE
01 CD 7E 53 09 37 AE 50 (BC FD 59 C0 86 0)

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

#include "emu.h"

#include "cpu/f8/f8.h"
#include "machine/f3853.h"
#include "machine/timer.h"
#include "video/pwm.h"

// internal artwork
#include "saitek_delta1.lh"


namespace {

class delta1_state : public driver_device
{
public:
	delta1_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// New Game button is directly tied to F3850 EXT RES pin
	DECLARE_INPUT_CHANGED_MEMBER(reset_button) { m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE); }

	// machine configs
	void delta1(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_ioport_array<5> m_inputs;

	u8 m_mux_data = 0;
	u8 m_led_select = 0;
	u8 m_inp_mux = 0;
	u8 m_7seg_data = 0;
	bool m_7seg_rc = false;
	bool m_blink = false;

	// address maps
	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	TIMER_DEVICE_CALLBACK_MEMBER(blink) { m_blink = !m_blink; update_display(); }

	// I/O handlers
	void update_display();
	void mux_w(u8 data);
	void digit_w(u8 data);
	u8 input_r();
};

void delta1_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_mux_data));
	save_item(NAME(m_led_select));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_7seg_data));
	save_item(NAME(m_7seg_rc));
	save_item(NAME(m_blink));

	// game reads from uninitialized RAM while it's thinking
	for (int i = 0; i < 0x100; i++)
		m_maincpu->space(AS_PROGRAM).write_byte(i + 0x2000, machine().rand());
}


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

void delta1_state::update_display()
{
	m_display->matrix(m_led_select, (m_blink && m_7seg_rc) ? 0 : m_7seg_data);
}

void delta1_state::mux_w(u8 data)
{
	// IO00-02: MC14028B A-C (D to GND)
	// MC14028B Q3-Q7: input mux
	// MC14028B Q4-Q7: digit select through 75492
	u8 sel = 1 << (~data & 7);
	m_inp_mux = sel >> 3 & 0x1f;
	m_led_select = sel >> 4;
	update_display();

	m_mux_data = data;
}

u8 delta1_state::input_r()
{
	u8 data = 0;

	// IO04-07: multiplexed inputs
	// IO03: GND
	for (int i = 0; i < 5; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read();

	return data << 4 | m_mux_data | 8;
}

void delta1_state::digit_w(u8 data)
{
	// IO17: R/C circuit to segment commons (for automated blinking)
	// IO10-16: digit segments A-G
	m_7seg_rc = bool(BIT(data, 7));
	m_7seg_data = bitswap<7>(data,0,1,2,3,4,5,6);
	update_display();
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void delta1_state::main_map(address_map &map)
{
	map.global_mask(0x3fff);
	map(0x0000, 0x0fff).mirror(0x1000).rom(); // _A13
	map(0x2000, 0x20ff).mirror(0x1f00).ram(); // A13
}

void delta1_state::main_io(address_map &map)
{
	map(0x00, 0x00).rw(FUNC(delta1_state::input_r), FUNC(delta1_state::mux_w));
	map(0x01, 0x01).w(FUNC(delta1_state::digit_w));
	map(0x0c, 0x0f).rw("f3853", FUNC(f3853_device::read), FUNC(f3853_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( delta1 )
	PORT_START("IN.0")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Time Set")
	PORT_BIT(0x0d, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_A) PORT_NAME("A 1 / White King")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_B) PORT_NAME("B 2 / White Queen")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_C) PORT_NAME("C 3 / White Rook")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_D) PORT_NAME("D 4")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_E) PORT_NAME("E 5 / White Bishop")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_F) PORT_NAME("F 6 / White Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_G) PORT_NAME("G 7 / White Pawn")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_H) PORT_NAME("H 8")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9 / Black King")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0 / Black Queen")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Time Display / Black Rook")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE") // clear entry

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("FP / Black Bishop") // find position
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("EP / Black Knight") // enter position
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Change Color / Black Pawn")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")

	PORT_START("RESET") // not on matrix
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(delta1_state::reset_button), 0) PORT_NAME("New Game")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void delta1_state::delta1(machine_config &config)
{
	// basic machine hardware
	F8(config, m_maincpu, 2'000'000); // LC circuit, measured 2MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &delta1_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &delta1_state::main_io);
	m_maincpu->set_irq_acknowledge_callback("f3853", FUNC(f3853_device::int_acknowledge));

	f3853_device &f3853(F3853(config, "f3853", 2'000'000));
	f3853.int_req_callback().set_inputline("maincpu", F8_INPUT_LINE_INT_REQ);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 7);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_saitek_delta1);

	TIMER(config, "display_blink").configure_periodic(FUNC(delta1_state::blink), attotime::from_msec(200)); // approximation
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( ccdelta1 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("ma_winke_y1d", 0x0000, 0x1000, CRC(ddc04aca) SHA1(bbf334c82bc89b2f131f5a50f0a617bc3bc4c329) ) // 2332a
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1980, ccdelta1, 0,      0,      delta1,  delta1, delta1_state, empty_init, "SciSys / Novag Industries", "Chess Champion: Delta-1", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )



desdis.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger, yoyo_chessboard
/*******************************************************************************

Fidelity Designer Display series, 6502 and 68000
(6502-based displayless Designer is the same as Par Excellence, see excel.cpp)

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

Designer 2100 Display (model 6106) overview:
- PCB label: 510.1130A01
- WDC W65C02P-6 CPU, 6MHz XTAL
- 8KB RAM(MS6264L-10), 2*32KB ROM(27C256)
- 4-digit LCD panel

Designer 2000 Display (model 6105): same hardware, no bookrom, 3MHz

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

Designer Mach III Master 2265 (model 6113) overview:
- PCB label: 510.1134A02
- MC68HC000P12F CPU, 16MHz XTAL
- 80KB RAM(2*KM6264AL-10, 2*KM62256AP-10), 64KB ROM(2*WSI 27C256L-12)
- IRQ(IPL2) from 555 timer, 1.67ms low, 6us high

ROM address/data lines are scrambled, presumed for easy placement on PCB and not
for obfuscation. I/O is nearly the same as Designer Display on 6502 hardware.

Designer Mach IV Master 2325 (model 6129) overview:
- PCB label: 510.1149A01
- MC68EC020RP25 CPU, 20MHz XTAL
- 32KB(4*P5164-70) + 512KB(TC518512PL-80) RAM, 64KB ROM(TMS 27C512-120JL)
- It has a green "Shift" led instead of red, and ROM is not scrambled.

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

#include "emu.h"

#include "cpu/m6502/r65c02.h"
#include "cpu/m6502/w65c02.h"
#include "cpu/m68000/m68000.h"
#include "cpu/m68000/m68020.h"
#include "machine/clock.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "fidel_desdis.lh"
#include "fidel_desdis_68kg.lh"
#include "fidel_desdis_68kr.lh"


namespace {

// Designer Display / shared

class desdis_state : public driver_device
{
public:
	desdis_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_rombank(*this, "rombank"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.0")
	{ }

	// machine configs
	void fdes2000d(machine_config &config);
	void fdes2100d(machine_config &config);

	void init_fdes2100d();

protected:
	virtual void machine_start() override ATTR_COLD;

	// devices/pointers
	required_device<cpu_device> m_maincpu;
	optional_memory_bank m_rombank;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport m_inputs;

	u8 m_select = 0;
	u32 m_lcd_data = 0;

	// address maps
	void fdes2100d_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_lcd();
	virtual void control_w(offs_t offset, u8 data);
	virtual void lcd_w(offs_t offset, u8 data);
	virtual u8 input_r(offs_t offset);
};

void desdis_state::init_fdes2100d()
{
	m_rombank->configure_entries(0, 2, memregion("rombank")->base(), 0x4000);
}

void desdis_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_select));
	save_item(NAME(m_lcd_data));
}


// Designer Master

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

	// machine configs
	void fdes2265(machine_config &config);
	void fdes2325(machine_config &config);

	void init_fdes2265();

private:
	// address maps
	void fdes2265_map(address_map &map) ATTR_COLD;
	void fdes2325_map(address_map &map) ATTR_COLD;

	// I/O handlers, slightly different (control_w is d0 instead of d7)
	virtual void control_w(offs_t offset, u8 data) override { desdis_state::control_w(offset, data << 7); }
};

void desmas_state::init_fdes2265()
{
	u16 *rom = (u16*)memregion("maincpu")->base();
	const u32 len = memregion("maincpu")->bytes() / 2;

	// descramble data lines
	for (int i = 0; i < len; i++)
		rom[i] = bitswap<16>(rom[i], 15,14,8,13,9,12,10,11, 3,4,5,7,6,0,1,2);

	// descramble address lines
	std::vector<u16> buf(len);
	memcpy(&buf[0], rom, len*2);
	for (int i = 0; i < len; i++)
		rom[i] = buf[bitswap<24>(i, 23,22,21,20,19,18,17,16, 15,14,13,12,11,8,10,9, 7,6,5,4,3,2,1,0)];
}



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

void desdis_state::update_lcd()
{
	u8 mask = (m_select & 8) ? 0 : 0xff;
	for (int i = 0; i < 4; i++)
		m_display->write_row(i+2, (m_lcd_data >> (8*i) & 0xff) ^ mask);
}

void desdis_state::control_w(offs_t offset, u8 data)
{
	// a0-a2,d7: 74259
	u8 mask = 1 << offset;
	m_select = (m_select & ~mask) | ((data & 0x80) ? mask : 0);

	// 74259 Q4-Q7: 7442 a0-a3
	// 7442 0-8: led data, input mux
	u16 sel = 1 << (m_select >> 4 & 0xf);
	u16 led_data = sel & 0x1ff;

	// 7442 9: speaker out
	m_dac->write(BIT(sel, 9));

	// 74259 Q0,Q1: led select (active low)
	m_display->matrix_partial(0, 2, ~m_select & 3, led_data);

	// 74259 Q2: book rom A14
	if (m_rombank != nullptr)
		m_rombank->set_entry(m_select >> 2 & 1);

	// 74259 Q3: lcd polarity
	update_lcd();
}

void desdis_state::lcd_w(offs_t offset, u8 data)
{
	// a0-a2,d0-d3: 4*74259 to lcd digit segments
	u32 mask = bitswap<8>(1 << offset,3,7,6,0,1,2,4,5);
	for (int i = 0; i < 4; i++)
	{
		m_lcd_data = (m_lcd_data & ~mask) | ((data >> i & 1) ? 0 : mask);
		mask <<= 8;
	}

	update_lcd();
}

u8 desdis_state::input_r(offs_t offset)
{
	u8 sel = m_select >> 4 & 0xf;
	u8 data = 0;

	// a0-a2,d7: multiplexed inputs (active low)
	// read chessboard sensors
	if (sel < 8)
		data = m_board->read_rank(sel ^ 7, true);

	// read button panel
	else if (sel == 8)
		data = m_inputs->read();

	return (data >> offset & 1) ? 0 : 0x80;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void desdis_state::fdes2100d_map(address_map &map)
{
	map(0x0000, 0x1fff).ram();
	map(0x2000, 0x2007).mirror(0x1ff8).rw(FUNC(desdis_state::input_r), FUNC(desdis_state::control_w));
	map(0x4000, 0x7fff).bankr(m_rombank);
	map(0x6000, 0x6007).mirror(0x1ff8).w(FUNC(desdis_state::lcd_w));
	map(0x8000, 0xffff).rom();
}

void desmas_state::fdes2265_map(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x00ffff).rom();
	map(0x000000, 0x00000f).w(FUNC(desmas_state::lcd_w)).umask16(0x00ff);
	map(0x044000, 0x047fff).ram();
	map(0x100000, 0x10ffff).ram();
	map(0x140000, 0x14000f).r(FUNC(desmas_state::input_r)).umask16(0xff00);
	map(0x140000, 0x14000f).w(FUNC(desmas_state::control_w)).umask16(0x00ff);
}

void desmas_state::fdes2325_map(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x00ffff).rom();
	map(0x100000, 0x10000f).w(FUNC(desmas_state::lcd_w)).umask32(0x00ff00ff);
	map(0x140000, 0x14000f).w(FUNC(desmas_state::control_w)).umask32(0x00ff00ff);
	map(0x180000, 0x18000f).r(FUNC(desmas_state::input_r)).umask32(0xff00ff00);
	map(0x300000, 0x37ffff).ram();
	map(0x500000, 0x507fff).ram();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( desdis )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_NAME("Clear")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Move / Alternate")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("Hint / Info")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Take Back / Replay")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Level / New")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("Option / Time")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Verify / Problem")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Shift")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void desdis_state::fdes2100d(machine_config &config)
{
	// basic machine hardware
	W65C02(config, m_maincpu, 6_MHz_XTAL); // W65C02P-6
	m_maincpu->set_addrmap(AS_PROGRAM, &desdis_state::fdes2100d_map);

	auto &irq_clock(CLOCK(config, "irq_clock", 600)); // from 556 timer (22nF, 102K, 1K), ideal frequency is 600Hz
	irq_clock.set_pulse_width(attotime::from_nsec(15250)); // active for 15.25us
	irq_clock.signal_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(2+4, 9);
	m_display->set_segmask(0x3c, 0x7f);
	config.set_default_layout(layout_fidel_desdis);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void desdis_state::fdes2000d(machine_config &config)
{
	fdes2100d(config);

	// basic machine hardware
	R65C02(config.replace(), m_maincpu, 3_MHz_XTAL); // R65C02P3
	m_maincpu->set_addrmap(AS_PROGRAM, &desdis_state::fdes2100d_map);
}

void desmas_state::fdes2265(machine_config &config)
{
	fdes2100d(config);

	// basic machine hardware
	M68000(config.replace(), m_maincpu, 16_MHz_XTAL); // MC68HC000P12F
	m_maincpu->set_addrmap(AS_PROGRAM, &desmas_state::fdes2265_map);

	auto &irq_clock(CLOCK(config.replace(), "irq_clock", 600)); // from 555 timer, ideal frequency is 600Hz (measured 597Hz)
	irq_clock.set_pulse_width(attotime::from_usec(6)); // active for 6us
	irq_clock.signal_handler().set_inputline(m_maincpu, M68K_IRQ_4);

	config.set_default_layout(layout_fidel_desdis_68kr);
}

void desmas_state::fdes2325(machine_config &config)
{
	fdes2265(config);

	// basic machine hardware
	M68EC020(config.replace(), m_maincpu, 20_MHz_XTAL); // MC68EC020RP25
	m_maincpu->set_addrmap(AS_PROGRAM, &desmas_state::fdes2325_map);

	config.set_default_layout(layout_fidel_desdis_68kg);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( fdes2100d ) // model 6106, PCB label 510.1130A01, serial 000646xx
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("i9_orange.ic9", 0x8000, 0x8000, CRC(83fec02a) SHA1(6f43ab05bc605061989b05d0592dbd184efff9d4) ) // WSI 27C256L-12

	ROM_REGION( 0x8000, "rombank", 0 )
	ROM_LOAD("bk_3_white.ic10", 0x0000, 0x8000, CRC(3857cc35) SHA1(f073dafb9fd885c7ddb7fbff10e3653f343ef1c6) ) // "
ROM_END

ROM_START( fdes2100da ) // model 6106, PCB label 510.1130A01, serial 914611xx
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("i9_orange.ic9", 0x8000, 0x8000, CRC(83fec02a) SHA1(6f43ab05bc605061989b05d0592dbd184efff9d4) ) // Microchip 27C256-15

	ROM_REGION( 0x8000, "rombank", 0 )
	ROM_LOAD("bk_white.ic10", 0x0000, 0x8000, CRC(10debf37) SHA1(b4ceeeb64436af77ab9b6a34a8d2e39deaf3000d) ) // "
ROM_END

ROM_START( fdes2000d ) // model 6105, PCB label 510.1130A01
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("27c256.ic9", 0x8000, 0x8000, CRC(b136d1a1) SHA1(8438790a62f45284ff33a0255c5c89f526726d3e) ) // 27C256, no label

	ROM_REGION( 0x8000, "rombank", ROMREGION_ERASEFF ) // no rom in ic10
ROM_END


ROM_START( fdes2265 ) // model 6113, PCB label 510.1134A02
	ROM_REGION16_BE( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("13_e3_red.ic11",  0x00000, 0x08000, CRC(fc352636) SHA1(e87350eab0c9ec7ea6321d82246c500c9d7b463e) ) // WSI 27C256L-12
	ROM_LOAD16_BYTE("13_o3_blue.ic10", 0x00001, 0x08000, CRC(f335a2d5) SHA1(e0b861d77f394d39ab68ca2ef9ac1ceb9e829aed) ) // "
ROM_END

ROM_START( fdes2265a ) // "
	ROM_REGION16_BE( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("13_e_red.ic11",  0x00000, 0x08000, CRC(15a35628) SHA1(8213862e129951c6943a80f73cd0b63a31bb1357) ) // WSI 27C256L-12
	ROM_LOAD16_BYTE("13_o_blue.ic10", 0x00001, 0x08000, CRC(81ce7ab2) SHA1(f01a70bcf2fbfe66c7a77d3c4437d897e5cc682d) ) // "
ROM_END


ROM_START( fdes2325 ) // model 6129, PCB label 510.1149A01
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("61_29_white.ic10", 0x00000, 0x10000, CRC(f74157e1) SHA1(87f3f2d584e292f81593e053240d022cc477834d) ) // 27C512

	ROM_REGION( 0x100, "pals", 0 )
	ROM_LOAD("101-1097a01.ic19", 0x000, 0x100, NO_DUMP ) // PALCE16V8Q-25PC
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME        PARENT     COMPAT  MACHINE    INPUT   CLASS         INIT            COMPANY, FULLNAME, FLAGS
SYST( 1988, fdes2100d,  0,         0,      fdes2100d, desdis, desdis_state, init_fdes2100d, "Fidelity International", "Designer 2100 Display (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1988, fdes2100da, fdes2100d, 0,      fdes2100d, desdis, desdis_state, init_fdes2100d, "Fidelity International", "Designer 2100 Display (set 2)", MACHINE_SUPPORTS_SAVE )
SYST( 1988, fdes2000d,  fdes2100d, 0,      fdes2000d, desdis, desdis_state, init_fdes2100d, "Fidelity International", "Designer 2000 Display", MACHINE_SUPPORTS_SAVE )

SYST( 1989, fdes2265,   0,         0,      fdes2265,  desdis, desmas_state, init_fdes2265,  "Fidelity Electronics International", "Designer Mach III Master 2265 (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, fdes2265a,  fdes2265,  0,      fdes2265,  desdis, desmas_state, init_fdes2265,  "Fidelity Electronics International", "Designer Mach III Master 2265 (set 2)", MACHINE_SUPPORTS_SAVE )
SYST( 1991, fdes2325,   fdes2265,  0,      fdes2325,  desdis, desmas_state, empty_init,     "Fidelity Electronics International", "Designer Mach IV 68020 Master 2325", MACHINE_SUPPORTS_SAVE )



design.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    Azkoyen "Design"

    Tobacco vending machines

    Hardware:
    - Intel P8051
    - 27C256 EPROM
    - NEC D446C-2 SRAM
    - OKI M62X428 RTC
    - Rockwell 10937P-50 A8201-17 display controller

    TODO:
    - The coin mech isn't emulated. It's an Azkoyen L66S coin selector,
      which uses a PIC16C76/PIC16F76 (undumped).

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

#include "emu.h"
#include "cpu/mcs48/mcs48.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/74259.h"
#include "machine/bankdev.h"
#include "machine/i8279.h"
#include "machine/msm5832.h"
#include "machine/msm6242.h"
#include "machine/nvram.h"
#include "machine/roc10937.h"

#include "design6.lh"


namespace {

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

class design6_state : public driver_device
{
public:
	design6_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_iobank(*this, "iobank"),
		m_vfd(*this, "vfd"),
		m_buttons(*this, "in2_%u", 1U),
		m_input_sel(0)
	{ }

	void design6(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<i8051_device> m_maincpu;
	required_device<address_map_bank_device> m_iobank;
	required_device<roc10937_device> m_vfd;
	required_ioport_array<4> m_buttons;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void iobanked_map(address_map &map) ATTR_COLD;

	void port1_w(uint8_t data);
	uint8_t in2_r();

	uint8_t m_input_sel;
};

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

	void azkoyent(machine_config &config);
	void azkoyent61(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
};


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

void design6_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("maincpu", 0);
}

void design6_state::io_map(address_map &map)
{
	map(0x0000, 0xffff).rw(m_iobank, FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
}

void design6_state::iobanked_map(address_map &map)
{
	map(0x00000, 0x00000).portr("in0");
	map(0x00000, 0x00007).w("outlatch0", FUNC(cd4099_device::write_d0));
	map(0x00010, 0x00010).portr("in1");
	map(0x00010, 0x00017).w("outlatch1", FUNC(cd4099_device::write_d0));
	map(0x00020, 0x00020).r(FUNC(design6_state::in2_r));
	map(0x00020, 0x00027).w("outlatch2", FUNC(cd4099_device::write_d0));
	map(0x00030, 0x00030).portr("in3");
	map(0x00030, 0x00037).w("outlatch3", FUNC(cd4099_device::write_d0));
	map(0x00040, 0x00047).w("outlatch4", FUNC(cd4099_device::write_d0));
	map(0x00060, 0x0006f).rw("rtc", FUNC(msm6242_device::read), FUNC(msm6242_device::write));
	map(0x10000, 0x107ff).ram().share("nvram");
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( design6 )
	PORT_START("in0")
	PORT_DIPUNKNOWN(0x01, IP_ACTIVE_HIGH) PORT_NAME("in0:0")
	PORT_DIPUNKNOWN(0x02, IP_ACTIVE_HIGH) PORT_NAME("in0:1")
	PORT_DIPUNKNOWN(0x04, IP_ACTIVE_HIGH) PORT_NAME("in0:2")
	PORT_DIPUNKNOWN(0x08, IP_ACTIVE_HIGH) PORT_NAME("in0:3")
	PORT_DIPUNKNOWN(0x10, IP_ACTIVE_HIGH) PORT_NAME("in0:4")
	PORT_DIPUNKNOWN(0x20, IP_ACTIVE_HIGH) PORT_NAME("in0:5")
	PORT_SERVICE(0x40, IP_ACTIVE_HIGH)
	PORT_DIPUNKNOWN(0x80, IP_ACTIVE_HIGH) PORT_NAME("in0:7")

	PORT_START("in1")
	PORT_DIPNAME(0x01, 0x00, "Coin Return Pulse 3")
	PORT_DIPSETTING(   0x01, DEF_STR( On ))
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPNAME(0x02, 0x00, "Coin Return Pulse 2")
	PORT_DIPSETTING(   0x02, DEF_STR( On ))
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPNAME(0x04, 0x00, "Coin Return Pulse 1")
	PORT_DIPSETTING(   0x04, DEF_STR( On ))
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPNAME(0x08, 0x08, "Empty Return 3")
	PORT_DIPSETTING(   0x08, DEF_STR( Off ))
	PORT_DIPSETTING(   0x00, DEF_STR( On ))
	PORT_DIPNAME(0x10, 0x10, "Empty Return 2")
	PORT_DIPSETTING(   0x10, DEF_STR( Off ))
	PORT_DIPSETTING(   0x00, DEF_STR( On ))
	PORT_DIPNAME(0x20, 0x20, "Empty Return 1")
	PORT_DIPSETTING(   0x20, DEF_STR( Off ))
	PORT_DIPSETTING(   0x00, DEF_STR( On ))
	PORT_DIPNAME(0x40, 0x00, "Averia Tramp. 1")
	PORT_DIPSETTING(   0x40, DEF_STR( On ))
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPUNKNOWN(0x80, IP_ACTIVE_HIGH) PORT_NAME("in1:7")

	PORT_START("in2_1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_GAMBLE_PAYOUT) PORT_NAME("Coin Recovery")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_Q) PORT_NAME("Canal 1")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_W) PORT_NAME("Canal 2")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_E) PORT_NAME("Canal 3")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_R) PORT_NAME("Canal 4")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_T) PORT_NAME("Canal 5")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_Y) PORT_NAME("Canal 6")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("in2_2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_DIPUNKNOWN(0x04, IP_ACTIVE_HIGH) PORT_NAME("in2_2:2")
	PORT_DIPUNKNOWN(0x08, IP_ACTIVE_HIGH) PORT_NAME("in2_2:3")
	PORT_DIPUNKNOWN(0x10, IP_ACTIVE_HIGH) PORT_NAME("in2_2:4")
	PORT_DIPUNKNOWN(0x20, IP_ACTIVE_HIGH) PORT_NAME("in2_2:5")
	PORT_DIPUNKNOWN(0x40, IP_ACTIVE_HIGH) PORT_NAME("in2_2:6")
	PORT_DIPUNKNOWN(0x80, IP_ACTIVE_HIGH) PORT_NAME("in2_2:7")

	PORT_START("in2_3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("in2_4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_DIPUNKNOWN(0x04, IP_ACTIVE_HIGH) PORT_NAME("in2_4:2")
	PORT_DIPUNKNOWN(0x08, IP_ACTIVE_HIGH) PORT_NAME("in2_4:3")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_A) PORT_NAME("Remote Control A")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_B) PORT_NAME("Remote Control B")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_C) PORT_NAME("Remote Control C")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_D) PORT_NAME("Remote Control D")

	PORT_START("in3")
	PORT_DIPUNKNOWN(0x01, IP_ACTIVE_HIGH) PORT_NAME("in3:0")
	PORT_DIPUNKNOWN(0x02, IP_ACTIVE_HIGH) PORT_NAME("in3:1")
	PORT_DIPUNKNOWN(0x04, IP_ACTIVE_HIGH) PORT_NAME("in3:2")
	PORT_DIPUNKNOWN(0x08, IP_ACTIVE_HIGH) PORT_NAME("in3:3")
	PORT_DIPUNKNOWN(0x10, IP_ACTIVE_HIGH) PORT_NAME("in3:4")
	PORT_DIPUNKNOWN(0x20, IP_ACTIVE_HIGH) PORT_NAME("in3:5")
	PORT_DIPUNKNOWN(0x40, IP_ACTIVE_HIGH) PORT_NAME("in3:6")
	PORT_DIPUNKNOWN(0x80, IP_ACTIVE_HIGH) PORT_NAME("in3:7")
INPUT_PORTS_END

static INPUT_PORTS_START( designe )
	PORT_INCLUDE(design6)

	PORT_MODIFY("in2_1")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_U) PORT_NAME("Canal 7")

	PORT_MODIFY("in2_2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_F) PORT_NAME("Canal 8")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_G) PORT_NAME("Canal 9")

	PORT_MODIFY("in2_3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_H) PORT_NAME("Canal 10")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_J) PORT_NAME("Canal 11")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_K) PORT_NAME("Canal 12")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_L) PORT_NAME("Canal 13")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_Y) PORT_NAME("Canal 14")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_X) PORT_NAME("Canal 15")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_C) PORT_NAME("Canal 16")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_V) PORT_NAME("Canal 17")

	PORT_MODIFY("in2_4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_N) PORT_NAME("Canal 18")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_M) PORT_NAME("Canal 19")
INPUT_PORTS_END

static INPUT_PORTS_START(azkoyent)
INPUT_PORTS_END


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

void design6_state::port1_w(uint8_t data)
{
	// 7-------  watchdog?
	// -6------  ram write protect?
	// --5-----  ram enable?
	// ---4----  not used?
	// ----3210  input select

	m_iobank->set_bank(BIT(data, 5));
	m_input_sel = data & 0x0f;
}

uint8_t design6_state::in2_r()
{
	uint8_t data = 0;

	if (BIT(m_input_sel, 0)) data |= m_buttons[0]->read();
	if (BIT(m_input_sel, 1)) data |= m_buttons[1]->read();
	if (BIT(m_input_sel, 2)) data |= m_buttons[2]->read();
	if (BIT(m_input_sel, 3)) data |= m_buttons[3]->read();

	return data;
}

void design6_state::machine_start()
{
	// register for save states
	save_item(NAME(m_input_sel));
}

void design6_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void design6_state::design6(machine_config &config)
{
	I8051(config, m_maincpu, 6_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &design6_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &design6_state::io_map);
	m_maincpu->port_out_cb<1>().set(FUNC(design6_state::port1_w));

	ADDRESS_MAP_BANK(config, m_iobank, 0);
	m_iobank->set_map(&design6_state::iobanked_map);
	m_iobank->set_addr_width(17);
	m_iobank->set_data_width(8);
	m_iobank->set_stride(0x10000);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	CD4099(config, "outlatch0");
//  outlatch0.q_out_cb<0>() // enable coin return motor 1
//  outlatch0.q_out_cb<1>() // enable coin return motor 2
//  outlatch0.q_out_cb<2>() // enable coin return motor 3
//  outlatch0.q_out_cb<3>() // master enable coin return motor?
//  outlatch0.q_out_cb<6>() // ?

	cd4099_device &outlatch1(CD4099(config, "outlatch1"));
	outlatch1.q_out_cb<5>().set("vfd", FUNC(roc10937_device::data));
	outlatch1.q_out_cb<6>().set("vfd", FUNC(roc10937_device::sclk));
	outlatch1.q_out_cb<7>().set("vfd", FUNC(roc10937_device::por));

	CD4099(config, "outlatch2");
	CD4099(config, "outlatch3");
	CD4099(config, "outlatch4");

	ROC10937(config, m_vfd);

	MSM6242(config, "rtc", 32.768_kHz_XTAL);

	config.set_default_layout(layout_design6);
}

void azkoyent_state::azkoyent(machine_config &config)
{
	I8039(config, m_maincpu, 6.144_MHz_XTAL);
	I8279(config, "i8279", 6.144_MHz_XTAL); // Unknown clock
}

void azkoyent_state::azkoyent61(machine_config &config)
{
	I8051(config, m_maincpu, 6_MHz_XTAL);
	I8279(config, "i8279", 6_MHz_XTAL); // Unknown clock
	MSM5832(config, "rtc", 6_MHz_XTAL); // Unknown clock, has its own oscillator (unknown frequency)
}


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

ROM_START( design6 )
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD("1.bin", 0x0000, 0x8000, CRC(1155999c) SHA1(2896af89011c496f905ed0e57d7035a3b612c718))

	ROM_REGION(0x4000, "coinsel", 0)
	ROM_LOAD("pic16x76_l56s-l66s.bin", 0x0000, 0x4000, NO_DUMP)
ROM_END

ROM_START( designe )
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD("designe.bin", 0x0000, 0x8000, CRC(693d40bd) SHA1(9596bbf9c367bc919393923460da15563d9447ca))

	ROM_REGION(0x4000, "coinsel", 0)
	ROM_LOAD("pic16x76_l56s-l66s.bin", 0x0000, 0x4000, NO_DUMP)
ROM_END


// Different Azkoyen tobacco vending machines on similar hardware

/* Azkoyen models T6, T8, and T12 (Azkoyen PCB 104-4455-02-80/1). MCS-48-based.
  ___________________________________________________________
 |                                       __________         |
_|_           ___                       | BATT    |        _|_
_|_         LM555CN                     |_________|        _|_
 |   ___          ____________________                     _|_
 |  BDX53A  Xtal | PCB 80C39 11P     |    __________       _|_
 |     6.144 MHz |___________________|   |_MC14069U|       _|_
_|_                                          _____________ _|_
_|_               ____________________      | EPROM      |  |
_|_              | NEC D8279C-5      |      |____________| =|
 |        ___    |___________________|       __________    =|
 |=      |..|                               |M74HC373B1    =|
 |=      |..|                                _________     =|
 |=      |..|                               |TC4011BP|     =|
 |=      |..|                        ___     ___            |
 |       |..|              TC4011BP->|  |    |  |          =|
 |=      |..|                        |  |    |  <-TC4011BP =|
 |=      |..|                        |  |    |  |          =|
 |                                   |__|    |__|          =|
 |__________________________________________________________|

*/

// T6 uses a 4 digits 7-segments display.
ROM_START( azkoyent6 )
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("43504560-0_t-6.u04",   0x0000, 0x2000, CRC(a4289b26) SHA1(40587094b11c6cf9308673ffac2ed9d445d458e9))
ROM_END

// T8 uses a 3 digits 7-segments display.
ROM_START( azkoyent8 )
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("43504570-2_t8_3.u04",  0x0000, 0x2000, CRC(76ac54bf) SHA1(da4c4a9f1c9c85d59169d62682bb7b73a9dd133b))
ROM_END

ROM_START( azkoyent12 )
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("43504580-0_t12-17.u04", 0x0000, 0x2000, CRC(10d4d4a7) SHA1(96804bc173abf2d51de7e7f84decba286916eba7))
ROM_END

/* Azkoyen model T61 (with OKI M5832 RTC, Azkoyen PCB 131000060-1). MCS-51-based. Unknown display.

  ___|||_||||||||||____________________________________
 |   ||| ||||||||||          ||||||||   |||||||||||   |
 | _____________                    _____             |
 ||::::::::::::|     __________     ·····             |
 |                  |ULN2803A_|                       |
 |      __________   __________   __________         =|
 |     |CD4099BCN|  |CD4099BCN|  |CF74HC240E         =|
 |                                                   =|
 |  L7805CV          __________                      =|
 |                  |_UM6104__|                      =|
 |      __________   __________   __________          |
 |     |_TC4011BP|  |_TC4071BP|  |CD4099BCN|          |
 |      __________   __________   __________          |
 |     |_TC4011BP|  |GD74HC138|  |TC4099BP_|<-Not present on some versions
 |            ______________      __________         =|
 |           | EPROM       |     |TC4099BP_|<-Not present on some versions
 |           |_____________|                         =|
 |          ___   __________                         =|
 |       LM555CN |TC4069UBP|                         =|
 | ______        ___________           ___________    |
 || BATT|       |MM74HC373N|          |TD62083AP_|    |
 ||_____|        ___________                          |
 |              |MM74HC373N|    _____________         |
 | Osc                         |::::::::::::|         |
 | xxx MHz      _________________   _________________ |
 | __________  | Intel P80C51AH |  | NEC D8279C-2   | |
 ||OKI_M5832|  |________________|  |________________| |
 |                ____     Xtal     __________        |
 |                BDX53  6.000 MHz |SN74HC240N        |
 |                                                    |
 |_____________|_|____|_|__|__|||_|||||||||___||||____|

*/

ROM_START( azkoyent61 )
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("t-61.u4",       0x0000, 0x1000, CRC(16d9b843) SHA1(7c6f177eca9163b5284d2cbe1bdeb3b0bf1a6698))
ROM_END

ROM_START( azkoyent61a )
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("t-61-6_t-m.u4", 0x0000, 0x1000, CRC(ce1ed720) SHA1(42cb78ddd8d06764599e97b72b557d164940f7df))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR   NAME         PARENT      MACHINE     INPUT     CLASS           INIT        ROT   COMPANY    FULLNAME                             FLAGS
GAME( 1995?, design6,     0,          design6,    design6,  design6_state,  empty_init, ROT0, "Azkoyen", "Design D6",                         MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )
GAME( 1995?, designe,     0,          design6,    designe,  design6_state,  empty_init, ROT0, "Azkoyen", "Design (Euro)",                     MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )

GAME( 19??,  azkoyent6,   0,          azkoyent,   azkoyent, azkoyent_state, empty_init, ROT0, "Azkoyen", "Vending machine model T6",          MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
GAME( 19??,  azkoyent8,   0,          azkoyent,   azkoyent, azkoyent_state, empty_init, ROT0, "Azkoyen", "Vending machine model T8",          MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
GAME( 19??,  azkoyent12,  0,          azkoyent,   azkoyent, azkoyent_state, empty_init, ROT0, "Azkoyen", "Vending machine model T12",         MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
GAME( 19??,  azkoyent61,  0,          azkoyent61, azkoyent, azkoyent_state, empty_init, ROT0, "Azkoyen", "Vending machine model T61 (set 1)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
GAME( 19??,  azkoyent61a, azkoyent61, azkoyent61, azkoyent, azkoyent_state, empty_init, ROT0, "Azkoyen", "Vending machine model T61 (set 2)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



design_master.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/*******************************************************************************

 __________________________________IR___
 |                          ____  RX/TX |
 |   _____      ________    |   |    __ |
 |   |    |     |       |   |   |    | ||
 |   |IC3 |     | IC2   |   |   |CN2->_||
 |   |    |     |       |   |   |       |
 |   |____|     |_______|   |   |       |
 |   ____                   |   |       |
 |   |   |       CART SLOT->|   |       |
 |   |IC5|                  |   |       |
 |   |___|      ________    |   |       |
 |   ____       |       |   |   |       |
 |   |   |      | IC1   |   |   |       |
 |   |IC4|      |       |   |   |       |
 |   |___|      |_______|   |___|       |
 |                                      |
 | SW2                                  |
 |______________________________________|

 IC1 = Hitachi H8/328 (24K-byte internal ROM + 1K-byte RAM)
 IC2 = Hitachi HG62G010R21FBN Gate Array (low gatecount and low I/O-count packages)
 IC3 = Hitachi HM62256LFP-10T 256kbit CMOS SRAM
 IC4 = BA10324AF Ground Sense Operational Amplifier
 IC5 = Hitachi 74HC00 (5B2T HC00)


 TODO:
 - cartridge pinouts / information
 - needs H8/329 family emulation

 NOTE: cartridge dumps contain boot vectors so Internal ROM likely only used when no cartridge is present

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

#include "emu.h"
#include "cpu/h8/h83337.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "screen.h"
#include "softlist_dev.h"
//#include "speaker.h"


namespace {

class bdsm_state : public driver_device
{
public:
	bdsm_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_cartslot(*this, "cartslot"),
		m_cartslot_region(nullptr),
		m_bank(*this, "cartbank"),
		m_screen(*this, "screen")
	{ }

	void bdesignm(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void mem_map(address_map &map) ATTR_COLD;

	uint16_t io_p7_r();

	uint32_t screen_update(screen_device& screen, bitmap_rgb32& bitmap, const rectangle& cliprect);

	[[maybe_unused]] DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load_bdesignm);

	required_device<h83334_device> m_maincpu;
	required_device<generic_slot_device> m_cartslot;
	memory_region *m_cartslot_region;
	required_memory_bank m_bank;
	required_device<screen_device> m_screen;
};

void bdsm_state::machine_start()
{
	if (m_cartslot && m_cartslot->exists())
	{
		std::string region_tag;
		m_cartslot_region = memregion(region_tag.assign(m_cartslot->tag()).append(GENERIC_ROM_REGION_TAG).c_str());
		m_bank->configure_entries(0, (m_cartslot_region->bytes() / 0x8000), m_cartslot_region->base(), 0x8000);

		// Only the first bank seems to contain a valid reset vector '0x50' which points at the first code in the ROM.
		// The other banks contain 0x5a00 as the reset vector.  IRQ vector seems valid in all banks.
		m_bank->set_entry(0);
	}
	else
		m_bank->set_base(memregion("roms")->base());
}

DEVICE_IMAGE_LOAD_MEMBER(bdsm_state::cart_load_bdesignm)
{
	uint32_t size = m_cartslot->common_get_size("rom");

	m_cartslot->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_BIG);
	m_cartslot->common_load_rom(m_cartslot->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

void bdsm_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).bankr("cartbank");
	map(0x8000, 0x895f).ram().share("unkram");
}

uint16_t bdsm_state::io_p7_r()
{
	return machine().rand();
}

static INPUT_PORTS_START( bdesignm )
INPUT_PORTS_END

uint32_t bdsm_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}


void bdsm_state::bdesignm(machine_config &config)
{
	// basic machine hardware
	H83334(config, m_maincpu, 16_MHz_XTAL / 2); // H8/328 (24kbytes internal ROM, 1kbyte internal ROM)
	m_maincpu->set_addrmap(AS_PROGRAM, &bdsm_state::mem_map);
	m_maincpu->read_port7().set(FUNC(bdsm_state::io_p7_r));

	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(160, 150); // resolution unknown
	m_screen->set_visarea(0, 160-1, 0, 150-1);
	m_screen->set_screen_update(FUNC(bdsm_state::screen_update));
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(0));

	// TODO: this should be a custom bus type with capability to plug the 'design' carts into it
	GENERIC_CARTSLOT(config, m_cartslot, generic_linear_slot, "bdesignm_cart");
	//m_cartslot->set_must_be_loaded(true);

	// Game carts, these appear to disable the Internal ROM
	SOFTWARE_LIST(config, "cart_list_game").set_original("bdesignm_game_cart");

	// You can also plug a design cart directly into the unit for use by the Internal ROM program (they contain no program)
	SOFTWARE_LIST(config, "cart_list_design").set_original("bdesignm_design_cart");
}

ROM_START( bdesignm )
	ROM_REGION16_BE(0x88000, "roms", ROMREGION_ERASE00)
	// Internal ROM (When the console is booted up without a cart it enters the default (builtin) art / drawing program,
	// otherwise probably not used as carts contain boot vectors etc.)
	ROM_LOAD( "h8_328_hd6433288f8_l04.ic1", 0x00000, 0x6000, CRC(2c6b8fb0) SHA1(b958b0bc27f18b7dda4fe852b3fd070a66586edb) )
ROM_END

} // anonymous namespace


CONS( 1995, bdesignm, 0, 0, bdesignm, bdesignm, bdsm_state, empty_init, "Bandai", "Design Master Denshi Mangajuku", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



dfs500.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Felipe Sanches
/****************************************************************************

    Sony DFS-500 DME Video Mixer

    An artwork layout for this is under development at:
    https://github.com/felipesanches/dfs500_MAME_artwork

    ===== Current status of this driver =====
    Upon power up, the ROM version number is displayed: "1.03"
    And after a while, we get an error: "Er02"

    This is likely due to bugs in this driver rendering the control panel unable to
    communicate properly with the video mixer maincpu via a serial interface.

    Apparently the maincpu does not respond to the control panel commands because it gets
    lost earlier, while trying to communicate with the secondary cpu (the "effects" cpu)
    via a pair of 16 bit latches.

    The effects CPU does not yet reply due to some other problem in the driver.
    I guess it is related to the "OREG2" in its memory map which have not yet been
    properly declared. We'll need to further study that portion of the circuit in the
    service manual in order to finish declaring the effects_cpu memory map layout.

    ===== Usage: =====
    According to the service manual, the LED test mode can be enabled by simultaneously
    pressing these buttons in the control panel:
    * FOREGROUND VTR A
    * BACKGROUND VTR B
    * Location
    With the default key mappings in this driver, this can be achieved by pressing Q + 2 + L

    * All LEDs and 7-seg displays blink sequentialy.
    * If you press any button on the control panel, the sequence of blinking resumes from
      that selected area of the panel.
    * To stop the blinking sequence you can press the keypad ENTER key.

    ===== Notes on the video hardware: =====
    This video mixer accepts 4 input video signals to be mixed. This driver currently emulates
    this by using static PNG images provided in the command line with the -inputN flags (with N = 1 to 4)

    The DSP circuitry was not yet emulated, and the minimal video code in this driver currently
    simply bypasses the input #1 into the output. Once the serial communication between control panel and
    maincpu is fixed, we should at least see the video output changing when pressing the BACKGROUND input
    selector buttons VTR A, VTR B, 3 and 4 (to select ammong the 4 video inputs).

****************************************************************************/
#include "emu.h"
#include "cpu/nec/nec.h"
#include "machine/gen_latch.h"
#include "machine/pit8253.h"
#include "machine/pic8259.h"
#include "machine/i8251.h"
#include "machine/upd7004.h"
#include "imagedev/picture.h"
#include "sound/beep.h"
#include "speaker.h"
#include "screen.h"
#include "dfs500.lh"


namespace {

#define VIDEO_WIDTH 768
#define VIDEO_HEIGHT 256

class dfs500_state : public driver_device
{
public:
	dfs500_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_cpanelcpu(*this, "cpanelcpu")
		, m_maincpu(*this, "maincpu")
		, m_effectcpu(*this, "effectcpu")
		, m_pit(*this, "pit")
		, m_pic(*this, "pic")
		, m_serial1(*this, "serial1")
		, m_serial2(*this, "serial2")
		, m_cpanel_serial(*this, "cpanel_serial")
		, m_cpanel_pit(*this, "cpanel_pit")
		, m_adc(*this, "adc")
		, m_rombank1(*this, "rombank1")
		, m_rombank2(*this, "rombank2")
		, m_buzzer(*this, "buzzer")
		, m_screen(*this, "screen")
		, m_input(*this, "input%u", 1U)
		, m_DSW_S1(*this, "DSW_S1")
		, m_DSW_S2(*this, "DSW_S2")
		, m_DSW_S3(*this, "DSW_S3")
		, m_RD(*this, "RD%u", 0U)
		, m_transition(*this, "transition_%u", 0U)
		, m_7seg_status(*this, "status")
		, m_7seg_edit(*this, "edit")
		, m_7seg_trail_shadow_frames(*this, "trail_shadow_frames_%u", 0U)
		, m_7seg_snapshot(*this, "snapshot_%u", 0U)
		, m_7seg_trans_rate(*this, "trans_rate_%u", 0U)
		, m_7seg_pattern_number(*this, "pattern_number_%u", 0U)
		, m_LD(*this, "LD%u", 0U)
		, m_LD_effect_ctrl_shift(*this, "LD234")
		, m_LD_effect_ctrl_mask(*this, "LD235")
	{
	}

	void dfs500(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void cpanelcpu_mem_map(address_map &map) ATTR_COLD;
	void cpanelcpu_io_map(address_map &map) ATTR_COLD;
	void maincpu_mem_map(address_map &map) ATTR_COLD;
	[[maybe_unused]] void maincpu_io_map(address_map &map) ATTR_COLD;
	void effectcpu_mem_map(address_map &map) ATTR_COLD;
	uint8_t pit_r(offs_t offset);
	void pit_w(offs_t offset, uint8_t data);
	uint8_t cpanel_pit_r(offs_t offset);
	void cpanel_pit_w(offs_t offset, uint8_t data);
	uint8_t pic_r(offs_t offset);
	void pic_w(offs_t offset, uint8_t data);
	void rombank1_entry_w(offs_t offset, uint8_t data);
	void rombank2_entry_w(offs_t offset, uint8_t data);
	void input_select_w(offs_t offset, uint8_t data);
	uint16_t RA0_r(offs_t offset);
	uint16_t RB0_r(offs_t offset);
	void WA0_w(offs_t offset, uint16_t data);
	void WB0_w(offs_t offset, uint16_t data);
	uint16_t RA1_r(offs_t offset);
	uint8_t RB1_r(offs_t offset);
	uint8_t RB2_r(offs_t offset);
	uint8_t cpanel_reg0_r(offs_t offset);
	uint8_t cpanel_reg2_r(offs_t offset);
	void cpanel_reg0_w(offs_t offset, uint8_t data);
	void cpanel_reg1_w(offs_t offset, uint8_t data);
	void cpanel_reg2_w(offs_t offset, uint8_t data);
	uint8_t cpanel_buzzer_r(offs_t offset);
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect);
	IRQ_CALLBACK_MEMBER(irq_callback);

	uint8_t m_int_vector;
	uint8_t m_sel10;
	uint8_t m_sel32;
	uint8_t m_sel54;
	uint8_t m_input_sel_A;
	uint8_t m_input_sel_B;
	uint16_t m_maincpu_latch16;
	uint16_t m_effectcpu_latch16;
	bool m_TOC;
	bool m_TOE;
	// TODO: bool m_BVS;
	uint8_t m_trans_rate[2];
	uint8_t m_pattern_number[3];
	uint8_t m_userprogram_status;
	uint8_t m_userprogram_edit;
	uint8_t m_trail_duration;
	uint8_t m_snapshot;
	uint8_t m_dot_points1;
	uint8_t m_dot_points2;
	bool m_buzzer_state;

	required_device<v20_device> m_cpanelcpu;
	required_device<v30_device> m_maincpu;
	required_device<v30_device> m_effectcpu;
	required_device<pit8254_device> m_pit;
	required_device<pic8259_device> m_pic;
	required_device<i8251_device> m_serial1;
	required_device<i8251_device> m_serial2;
	required_device<i8251_device> m_cpanel_serial;
	required_device<pit8254_device> m_cpanel_pit;
	required_device<upd7004_device> m_adc;
	required_memory_bank m_rombank1;
	required_memory_bank m_rombank2;
	required_device<beep_device> m_buzzer;
	required_device<screen_device> m_screen;
	required_device_array<picture_image_device, 4> m_input;
	required_ioport m_DSW_S1;
	required_ioport m_DSW_S2;
	required_ioport m_DSW_S3;
	optional_ioport_array<13> m_RD;
	output_finder<20> m_transition;
	output_finder<> m_7seg_status;
	output_finder<> m_7seg_edit;
	output_finder<2> m_7seg_trail_shadow_frames;
	output_finder<2> m_7seg_snapshot;
	output_finder<3> m_7seg_trans_rate;
	output_finder<4> m_7seg_pattern_number;
	output_finder<110> m_LD;
	output_finder<> m_LD_effect_ctrl_shift;
	output_finder<> m_LD_effect_ctrl_mask;
};

IRQ_CALLBACK_MEMBER(dfs500_state::irq_callback)
{
	return m_int_vector;
}

void dfs500_state::machine_start()
{
	m_serial1->write_cts(0);
	m_serial2->write_cts(0);
	m_cpanel_serial->write_cts(0);
	m_rombank1->configure_entries(0, 128, memregion("effectdata")->base(), 0x4000);
	m_rombank2->configure_entries(0, 128, memregion("effectdata")->base(), 0x4000);

	m_transition.resolve();
	m_7seg_status.resolve();
	m_7seg_edit.resolve();
	m_7seg_trail_shadow_frames.resolve();
	m_7seg_snapshot.resolve();
	m_7seg_trans_rate.resolve();
	m_7seg_pattern_number.resolve();
	m_LD.resolve();
	m_LD_effect_ctrl_shift.resolve();
	m_LD_effect_ctrl_mask.resolve();
}

void dfs500_state::machine_reset()
{
	m_buzzer_state = false;
	m_buzzer->set_state(0);
	m_rombank1->set_entry(0);
	m_rombank2->set_entry(0);
	m_maincpu_latch16 = 0x0000;
	m_effectcpu_latch16 = 0x0000;
	m_TOC = false;
	m_TOE = false;
	m_int_vector = 0x00;
	m_sel10 = 0;
	m_sel32 = 0;
	m_sel54 = 0;
}

uint32_t dfs500_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect)
{
	const bitmap_argb32 &input_bitmap = m_input[m_input_sel_A & 3]->get_bitmap();
	// FIXME: This is simply bypassing the inputs directly into the output.
	//        Emulation of the video hardware (DSP signal path) for GFX processing is still needed here.
	if (input_bitmap.valid())
	{
		// convert arbitrary sized ARGB32 image to a full-screen image
		double stepx = double (input_bitmap.width()) / VIDEO_WIDTH;
		double stepy = double (input_bitmap.height()) / VIDEO_HEIGHT;

		for (unsigned screen_y = screen.visible_area().min_y; screen_y <= screen.visible_area().max_y; screen_y++)
		{
			for (unsigned screen_x = screen.visible_area().min_x; screen_x <= screen.visible_area().max_x; screen_x++)
			{
				bitmap.pix(screen_y, screen_x) = input_bitmap.pix(
					int(double (screen_y % VIDEO_HEIGHT) * stepy),
					int(double (screen_x % VIDEO_WIDTH) * stepx));
			}
		}
	}
	return 0;
}

uint8_t dfs500_state::pit_r(offs_t offset)
{
	// CXQ71054P (Programmable Timer / Counter)
	return m_pit->read((offset >> 1) & 3); // addressed by CPU's address bits AB2 and AB1
}

void dfs500_state::pit_w(offs_t offset, uint8_t data)
{
	// CXQ71054P (Programmable Timer / Counter)
	m_pit->write((offset >> 1) & 3, data); // addressed by CPU's address bits AB2 and AB1
}

uint8_t dfs500_state::cpanel_pit_r(offs_t offset)
{
	// CXQ71054P (Programmable Timer / Counter)
	return m_cpanel_pit->read((offset >> 1) & 3); // addressed by CPU's address bits AB2 and AB1
}

void dfs500_state::cpanel_pit_w(offs_t offset, uint8_t data)
{
	// CXQ71054P (Programmable Timer / Counter)
	m_cpanel_pit->write((offset >> 1) & 3, data); // addressed by CPU's address bits AB2 and AB1
}

uint8_t dfs500_state::pic_r(offs_t offset)
{
	// PD71059C (Programmable Interrupt Controller)
	return m_pic->read((offset >> 1) & 1); // addressed by CPU's address bit AB1
}

void dfs500_state::pic_w(offs_t offset, uint8_t data)
{
	// PD71059C (Programmable Interrupt Controller)
	m_pic->write((offset >> 1) & 1, data); // addressed by CPU's address bit AB1
}

void dfs500_state::rombank1_entry_w(offs_t offset, uint8_t data)
{
	m_rombank1->set_entry(((data >> 1) & 0x40) | (data & 0x3f));
}

void dfs500_state::rombank2_entry_w(offs_t offset, uint8_t data)
{
	m_rombank2->set_entry(((data >> 1) & 0x40) | (data & 0x3f));
}

void dfs500_state::input_select_w(offs_t offset, uint8_t data)
{
	// Selects sources of video input on the AD-76 board.
	m_input_sel_A = (data >> 3) & 0x7;
	m_input_sel_B = data & 0x7;
}

void dfs500_state::WA0_w(offs_t offset, uint16_t data)
{
	m_effectcpu_latch16 = data;
	m_TOC = true;
}

uint16_t dfs500_state::RA0_r(offs_t offset)
{
	m_TOE = false;
	return m_maincpu_latch16;
}

uint16_t dfs500_state::RA1_r(offs_t offset)
{
	// "TEST, 1, OPT2, OPT1, RFLD, VD, TOC, TOE"
	uint8_t value = 0x40;
	// FIXME! Add other signals.
	if (m_TOC) value |= 0x02;
	if (m_TOE) value |= 0x01;
	value |= ((m_DSW_S3->read() & 0x0f) << 8); // "DSW_S3": (Unknown)
	return value;
}

void dfs500_state::WB0_w(offs_t offset, uint16_t data)
{
	m_maincpu_latch16 = data;
	m_TOE = true;
}

uint16_t dfs500_state::RB0_r(offs_t offset)
{
	m_TOC = false;
	return m_effectcpu_latch16;
}

uint8_t dfs500_state::RB1_r(offs_t offset)
{
	//"TEST, OPT2, OPT1, VD, T2, T1, TOE, TOC"
	uint8_t value = 0;
	// FIXME! Add other signals.
	if (m_TOE) value |= 0x02;
	if (m_TOC) value |= 0x01;
	return value;
}

uint8_t dfs500_state::RB2_r(offs_t offset)
{
	uint8_t value = 0;
	value |= ((m_DSW_S1->read() & 0x0f) << 4); // ("DSW_S1": Editing Control Unit Select)
	value |= (m_DSW_S2->read() & 0x0f);        // ("DSW_S2": Freeze Timing)
	// TODO:
	// if (m_BVS) value |= 0x10;
	return value;
}

uint8_t dfs500_state::cpanel_reg0_r(offs_t offset)
{
	uint8_t data;
	switch (offset & 0x0f)
	{
		case 0: // RD0
			data = 0x00;
			//TODO: if (m_RVD) data |= 0x02;
			//TODO: if (m_adc->eoc_r()) data |= 0x01;
			return data;
		case 6: // RD6
		case 7: // RD7
			return 0xff; //FIXME: Implement these ports
		default:
			if (m_RD[offset & 0x0f])
				return m_RD[offset & 0x0f]->read();
			else
				return 0xff;
	}
}

uint8_t dfs500_state::cpanel_reg2_r(offs_t offset)
{
	if ((offset & 0x07) > 2) return 0xff; // unused ports
	return m_RD[10 + (offset & 0x07)]->read(); // ports RD10, RD11 and RD12
}

void dfs500_state::cpanel_reg0_w(offs_t offset, uint8_t data)
{
	static const uint8_t ls247_map[16] =
		{ 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x58,0x4c,0x62,0x69,0x78,0x00 };

	switch (offset & 0x0f)
	{
		case 0: // WR0 on IC48 KY-223
			// Dot-points on 7seg digits:
			// D7: Not connected
			// D6: LD206 = DP on trans_rate_2
			// D5: LD198 = DP on trans_rate_1
			// D4: LD190 = DP on trans_rate_0
			// D3: LD182 = DP on pattern_number_1
			// D2: LD174 = DP on pattern_number_0
			// D1: LD186 or LD168 (?) = DP on pattern_number_1
			// D0: LD158 = DP on pattern_number_0
			m_dot_points2 = data;
			m_7seg_trans_rate[2] = ls247_map[m_trans_rate[1] & 0x0f] | (BIT(data >> 6, 0) << 7);
			m_7seg_trans_rate[1] = ls247_map[(m_trans_rate[0] >> 4) & 0x0f] | (BIT(data >> 5, 0) << 7);
			m_7seg_trans_rate[0] = ls247_map[m_trans_rate[0] & 0x0f] | (BIT(data >> 4, 0) << 7);
			m_7seg_pattern_number[3] = (m_pattern_number[2] & 0x7f) | (BIT(data >> 3, 0) << 7);
			m_7seg_pattern_number[2] = (m_pattern_number[1] & 0x7f) | (BIT(data >> 2, 0) << 7);
			m_7seg_pattern_number[1] = ls247_map[(m_pattern_number[0] >> 4) & 0x0f] | (BIT(data >> 1, 0) << 7);
			m_7seg_pattern_number[0] = ls247_map[m_pattern_number[0] & 0x0f] | (BIT(data >> 0, 0) << 7);
			break;
		case 1: // WR1 on IC50 KY-223
			m_pattern_number[2] = data;
			m_7seg_pattern_number[3] = (data & 0x7f) | (BIT(m_dot_points2 >> 3, 0) << 7);
			break;
		case 2: // WR2 on IC52 KY-223
			m_pattern_number[1] = data;
			m_7seg_pattern_number[2] = (data & 0x7f) | (BIT(m_dot_points2 >> 2, 0) << 7);
			break;
		case 3: // WR3 on IC53 KY-223
			m_pattern_number[0] = data;
			m_7seg_pattern_number[1] = ls247_map[(data >> 4) & 0x0f] | (BIT(m_dot_points2 >> 1, 0) << 7);
			m_7seg_pattern_number[0] = ls247_map[data & 0x0f] | (BIT(m_dot_points2 >> 0, 0) << 7);
			break;
		case 4: // WR4 on IC56 KY-223
			m_LD[63] = BIT(data, 7); // matte_copy
			m_LD[62] = BIT(data, 6); // mattes_col_bkgd
			m_LD[64] = BIT(data, 5); // pattern_number_set
			m_LD[65] = BIT(data, 4); // effect_ctrl_title
			m_LD[66] = BIT(data, 3); // effect_ctrl_dsk
			m_LD[69] = BIT(data, 2); // effect_ctrl_modify
			m_LD[68] = BIT(data, 1); // effect_ctrl_linear
			m_LD[67] = BIT(data, 0); // effect_ctrl_nonlin
			break;
		case 5: // WR5 on IC58 KY-223
			m_LD[61] = BIT(data, 7); // mattes_bord_mat
			m_LD[60] = BIT(data, 6); // mattes_shad_mat
			m_LD[59] = BIT(data, 5); // mattes_dsk_mat
			m_LD[58] = BIT(data, 4); // mattes_dsk_bord
			m_LD[57] = BIT(data, 3); // top_left
			m_LD[56] = BIT(data, 2); // top_right
			m_LD[55] = BIT(data, 1); // btm_left    // The service manual seems to be incorrect here.
			m_LD[54] = BIT(data, 0); // btm_right  // It seems to mistakenly swap LD54 and LD55.
			break;
		case 6: // WR6 on IC60 KY-223
			m_LD[53] = BIT(data, 6); // wide_bord
			m_LD[52] = BIT(data, 5); // narw_bord
			m_LD[51] = BIT(data, 4); // drop_bord
			m_LD[50] = BIT(data, 3); // double
			m_LD[48] = BIT(data, 2); // dsk_fill_video
			m_LD[47] = BIT(data, 1); // dsk_fill_mat
			m_LD[46] = BIT(data, 0); // dsk_fill_none
			break;
		case 7: // WR7 on IC82 KY-223
			m_LD[42] = BIT(data, 7); // dsk_mask_normal
			m_LD[44] = BIT(data, 6); // dsk_mask_invert
			m_LD[41] = BIT(data, 5); // dsk_key_inv
			m_LD[43] = BIT(data, 4); // dsk_ext_key
			m_LD[49] = BIT(data, 3); // border
			m_LD[40] = BIT(data, 2); // title_frgd_bus
			m_LD[39] = BIT(data, 1); // title_bord_mat
			m_LD[38] = BIT(data, 0); // title_shad_mat
			break;
		case 8: // WR8 on IC64 KY-223
			m_LD[78] = BIT(data, 7) << 1 | BIT(data, 3); // background_4
			m_LD[77] = BIT(data, 6) << 1 | BIT(data, 2); // background_3
			m_LD[76] = BIT(data, 5) << 1 | BIT(data, 1); // background_vtr_b
			m_LD[75] = BIT(data, 4) << 1 | BIT(data, 0); // background_vtr_a
			break;
		case 9: // WR9 on IC66 KY-223
			m_LD[73] = BIT(data, 7) << 1 | BIT(data, 3); // foreground_4
			m_LD[72] = BIT(data, 6) << 1 | BIT(data, 2); // foreground_3
			m_LD[71] = BIT(data, 5) << 1 | BIT(data, 1); // foreground_vtr_b
			m_LD[70] = BIT(data, 4) << 1 | BIT(data, 0); // foreground_vtr_a
			break;
		case 10: // WR10 on IC68 KY-223
			m_LD[34] = BIT(data, 7); // mask_normal
			m_LD[36] = BIT(data, 6); // mask_invert
			m_LD[33] = BIT(data, 5); // key_inv
			m_LD[35] = BIT(data, 4); // ext_key
			m_LD[37] = BIT(data, 3); // title
			m_LD[79] = BIT(data, 2) << 1 | BIT(data, 1); // background_int_video
			break;
		case 11: // WR11 on IC70 KY-223
			m_LD[82] = BIT(data, 7); // int_video_col_bkgd
			m_LD[81] = BIT(data, 6); // int_video_col_bar
			m_LD[80] = BIT(data, 5); // int_video_grid
			m_LD[74] = BIT(data, 4) << 1 | BIT(data, 3); // foreground_int_video
			m_LD[83] = BIT(data, 2); // freeze_field
			m_LD[84] = BIT(data, 1); // freeze_frame
			m_LD[88] = BIT(data, 0); // trans_rate_effect
			break;
		case 12: // WR12 on IC72 KY-223
			m_trans_rate[0] = data;
			m_7seg_trans_rate[1] = ls247_map[(data >> 4) & 0x0f] | (BIT(m_dot_points2 >> 5, 0) << 7);
			m_7seg_trans_rate[0] = ls247_map[data & 0x0f] | (BIT(m_dot_points2 >> 4, 0) << 7);
			break;
		case 13: // WR13 on IC75 KY-223
			m_trans_rate[0] = data & 0x0f;
			m_LD_effect_ctrl_shift = BIT(data, 5); // LD234
			m_LD_effect_ctrl_mask = BIT(data, 4); // LD235
			m_LD[45] = BIT(data, 7) << 1 | BIT(data, 6); // dsk_mix
			m_7seg_trans_rate[2] = (ls247_map[data & 0x0F]) | (BIT(m_dot_points2 >> 6, 0) << 7);
			break;
		case 14: // WR14 on IC77 KY-223
			m_LD[85] = BIT(data, 7); // transition_effect
			m_LD[86] = BIT(data, 6); // trans_rate_dsk
			m_LD[87] = BIT(data, 5); // transition_dsk
			m_LD[91] = BIT(data, 4); // transition_reverse
			m_transition[0] = BIT(data, 3); // LD233
			m_transition[1] = BIT(data, 2); // LD232
			m_transition[2] = BIT(data, 1); // LD231
			m_transition[3] = BIT(data, 0); // LD230
			break;
		case 15: // WR15 on IC79 KY-223
			m_transition[4] = BIT(data, 7); // LD229
			m_transition[5] = BIT(data, 6); // LD228
			m_transition[6] = BIT(data, 5); // LD227
			m_transition[7] = BIT(data, 4); // LD226
			m_transition[8] = BIT(data, 3); // LD225
			m_transition[9] = BIT(data, 2); // LD224
			m_transition[10] = BIT(data, 1); // LD223
			m_transition[11] = BIT(data, 0); // LD222
			break;
	}
}


void dfs500_state::cpanel_reg1_w(offs_t offset, uint8_t data)
{
	switch (offset & 7)
	{
		case 0: // WR16 on IC81 KY-223
			m_transition[12] = BIT(data, 7); // LD221
			m_transition[13] = BIT(data, 6); // LD220
			m_transition[14] = BIT(data, 5); // LD219
			m_transition[15] = BIT(data, 4); // LD218
			m_transition[16] = BIT(data, 3); // LD217
			m_transition[17] = BIT(data, 2); // LD216
			m_transition[18] = BIT(data, 1); // LD215
			m_transition[19] = BIT(data, 0); // LD214
			break;
		case 1: // WR17 on IC83 KY-223
			m_LD[90] = BIT(data, 7); // trans_rate_norm_rev
			m_LD[89] = BIT(data, 6); // transition_auto_trans
			m_LD[96] = BIT(data, 5); // direct_pattern
			m_LD[97] = BIT(data, 4); // keypad_0
			m_LD[98] = BIT(data, 3); // keypad_1
			m_LD[99] = BIT(data, 2); // keypad_2
			m_LD[100] = BIT(data, 1); // keypad_3
			m_LD[101] = BIT(data, 0); // keypad_rst
			break;
		case 2: // WR18 on IC81 KY-223
			m_LD[102] = BIT(data, 7); // keypad_4
			m_LD[103] = BIT(data, 6); // keypad_5
			m_LD[104] = BIT(data, 5); // keypad_6
			m_LD[105] = BIT(data, 4); // keypad_del
			m_LD[106] = BIT(data, 3); // keypad_7
			m_LD[107] = BIT(data, 2); // keypad_8
			m_LD[108] = BIT(data, 1); // keypad_9
			m_LD[109] = BIT(data, 0); // keypad_ins
			break;
		case 3: // WR19 on IC87 KY-223
			m_LD[95] = BIT(data, 7); // mode_pattern
			m_LD[94] = BIT(data, 6); // mode_trans
			m_LD[93] = BIT(data, 5); // mode_user_pgm
			m_LD[92] = BIT(data, 4); // mode_snap_shot

			//Selectors for analog inputs to ADC:
			m_sel10 = data & 0x03;
			m_sel32 = (data >> 2) & 0x03;
			break;
		case 7: // WR20 on IC47 KY-223
			// Here "data" holds the 8-bit value of the "interrupt vector number"
						//  to be put on the data bus when the CPU asserts /INTAK (Interrupt Acknowledge)
			m_int_vector = data;
			// FIXME: Is this an alternative way of doing it?
			//        m_cpanelcpu->set_input_line_and_vector(0, HOLD_LINE, data);
			break;
		default:
			break;
	}
}

void dfs500_state::cpanel_reg2_w(offs_t offset, uint8_t data)
{
	static const uint8_t ls247_map[16] =
		{ 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x58,0x4c,0x62,0x69,0x78,0x00 };

	switch (offset&7)
	{
		case 0: // WR21 on IC7 KY-225
			m_snapshot = data;
			m_7seg_snapshot[1] = ls247_map[(data >> 4) & 0x0F] | (BIT(m_dot_points1 >> 5, 0) << 7);
			m_7seg_snapshot[0] = ls247_map[data & 0x0F] | (BIT(m_dot_points1 >> 4, 0) << 7);
			break;
		case 1: // WR22 on IC10 KY-225
			m_LD[1] = BIT(data, 7); // editor_enable
			m_LD[4] = BIT(data, 6); // learn
			m_LD[3] = BIT(data, 5); // recall
			m_LD[2] = BIT(data, 4); // hold_input
			m_LD[5] = BIT(data, 3); // lighting
			m_LD[8] = BIT(data, 2); // lighting_spot
			m_LD[7] = BIT(data, 1); // lighting_line
			m_LD[6] = BIT(data, 0); // lighting_plane
			break;
		case 2: // WR23 on IC12 KY-225
			m_LD[15] = BIT(data, 7); // trail
			m_LD[20] = BIT(data, 6); // drop_border
			m_LD[11] = BIT(data, 5); // lighting_width_wide
			m_LD[10] = BIT(data, 4); // lighting_width_medium
			m_LD[9] = BIT(data, 3); // lighting_width_narrow
			m_LD[14] = BIT(data, 2); // lighting_intensity_high
			m_LD[13] = BIT(data, 1); // lighting_intensity_medium
			m_LD[12] = BIT(data, 0); // lighting_intensity_low
			break;
		case 3: // WR24 on IC14 KY-225
			m_LD[19] = BIT(data, 7); // trail_drop_type_hard
			m_LD[18] = BIT(data, 6); // trail_drop_type_soft
			m_LD[17] = BIT(data, 5); // trail_drop_type_hard_star
			m_LD[16] = BIT(data, 4); // trail_drop_type_soft_star
			m_LD[24] = BIT(data, 3); // trail_drop_fill_self
			m_LD[23] = BIT(data, 2); // trail_drop_fill_bord_mat
			m_LD[22] = BIT(data, 1); // trail_drop_fill_shad_mat
			m_LD[21] = BIT(data, 0); // trail_drop_fill_rndm_mat
			break;
		case 4: // WR25 on IC18 KY-225
			m_trail_duration = data;
			m_7seg_trail_shadow_frames[1] = ls247_map[(data >> 4) & 0x0F] | (BIT(m_dot_points1 >> 3, 0) << 7);
			m_7seg_trail_shadow_frames[0] = ls247_map[data & 0x0F] | (BIT(m_dot_points1 >> 2, 0) << 7);
			break;
		case 5: // WR26 on IC16 KY-225
			m_LD[25] = BIT(data, 7); // shadow
			m_LD[28] = BIT(data, 6); // trail_frames_duration
			m_LD[27] = BIT(data, 5); // trail_frames_wid_pos
			m_LD[26] = BIT(data, 4); // trail_frames_density
			m_LD[29] = BIT(data, 3); // edge_border
			m_LD[30] = BIT(data, 2); // edge_soft
			m_LD[31] = BIT(data, 1); // edit_led
			m_LD[32] = BIT(data, 0); // location
			break;
		case 6: // WR27 on IC21 KY-225
			m_sel54 = (data >> 6) & 3; // D7/D6 = SEL5/SEL4 signals to IC26
			// Dot-points on 7seg digits:
			// D5 = LD150 = DP on user program status
			// D4 = LD142 = DP on user program edit
			// D3 = LD134 = DP on trail_duration_1
			// D2 = LD126 = DP on trail_duration_0
			// D1 = LD118 = DP on snap_shot_1
			// D0 = LD110 = DP on snap_shot_0
			m_dot_points1 = data;
			m_7seg_status = ls247_map[m_userprogram_status] | (BIT(data >> 5, 0) << 7);
			m_7seg_edit = ls247_map[m_userprogram_edit] | (BIT(data >> 4, 0) << 7);
			m_7seg_trail_shadow_frames[1] = ls247_map[(m_trail_duration >> 4) & 0x0F] | (BIT(data >> 3, 0) << 7);
			m_7seg_trail_shadow_frames[0] = ls247_map[m_trail_duration & 0x0F] | (BIT(data >> 2, 0) << 7);
			m_7seg_snapshot[1] = ls247_map[(m_snapshot >> 4) & 0x0F] | (BIT(data >> 1, 0) << 7);
			m_7seg_snapshot[0] = ls247_map[m_snapshot & 0x0F] | (BIT(data >> 0, 0) << 7);
			break;
		case 7: // WR28 on IC23 KY-225
			m_userprogram_status = (data >> 4) & 0x0F;
			m_userprogram_edit = data & 0x0F;
			m_7seg_status = ls247_map[(data >> 4) & 0x0F] | (BIT(m_dot_points1 >> 5, 0) << 7);
			m_7seg_edit = ls247_map[data & 0x0F] | (BIT(m_dot_points1 >> 4, 0) << 7);
			break;
	}
}

uint8_t dfs500_state::cpanel_buzzer_r(offs_t offset)
{
// FIXME: Not sure yet what to do here...
// TODO:    m_buzzer_state = !m_buzzer_state;
// TODO:    m_buzzer->set_state(m_buzzer_state);
	return 0;
}

void dfs500_state::cpanelcpu_mem_map(address_map &map)
{
	map(0x00000, 0x07fff).mirror(0xe0000).ram();                        // 32kb SRAM chip at IC15 on KY-223
	map(0x08000, 0x08000).mirror(0xe0ffd).rw(m_cpanel_serial, FUNC(i8251_device::data_r), FUNC(i8251_device::data_w));       // "IO" IC17 on KY-223
	map(0x08002, 0x08002).mirror(0xe0ffd).rw(m_cpanel_serial, FUNC(i8251_device::status_r), FUNC(i8251_device::control_w));
	map(0x09000, 0x09007).mirror(0xe0ff8).rw(FUNC(dfs500_state::cpanel_pit_r), FUNC(dfs500_state::cpanel_pit_w));       // "TIMER" IC16 on KY-223
	map(0x0a000, 0x0a001).mirror(0xe0ffe).rw("adc", FUNC(upd7004_device::read), FUNC(upd7004_device::write));       // ADC at IC19 on KY-223
	map(0x0b000, 0x0b00f).mirror(0xe0ff0).rw(FUNC(dfs500_state::cpanel_reg0_r), FUNC(dfs500_state::cpanel_reg0_w));       // "REG0" Switches(?) & LEDs on KY-223
	map(0x0c000, 0x0c007).mirror(0xe0ff8).w(FUNC(dfs500_state::cpanel_reg1_w));       // "REG1" LEDs on KY-223
	map(0x0d000, 0x0d007).mirror(0xe0ff8).rw(FUNC(dfs500_state::cpanel_reg2_r), FUNC(dfs500_state::cpanel_reg2_w));       // "REG2" Switches(?) & LEDs on KY-225
	map(0x0f000, 0x0f007).mirror(0xe0ff8).r(FUNC(dfs500_state::cpanel_buzzer_r)); // "BUZZER" IC89
	map(0x10000, 0x17fff).mirror(0xe8000).rom().region("cpanelcpu", 0); // 32kb EPROM at IC14
}

void dfs500_state::cpanelcpu_io_map(address_map &map)
{
	//FIXME! map(0x0000, 0x0007).mirror(0x8ff8).rw(FUNC(dfs500_state::...), FUNC(dfs500_state::...));
}

void dfs500_state::maincpu_mem_map(address_map &map)
{
	//FIXME: The RAM mirror should be 0xe0000 according to IC49 on board SY-172 (as it is wired on the service manual schematics)
	//       but I saw unmapped accesses to the A15 mirror of this range on the MAME debugger, which suggests the 0xe8000 value used below:
	map(0x00000, 0x07fff).mirror(0xe8000).ram();                      // 4x 8kb SRAM chips at IC59/IC60/IC61/IC62
	map(0x10000, 0x1ffff).mirror(0xe0000).rom().region("maincpu", 0); // 2x 32kb EPROMs at IC1/IC2
}

void dfs500_state::maincpu_io_map(address_map &map)
{
	map(0x0000, 0x0007).mirror(0x8ff8).rw(FUNC(dfs500_state::pit_r), FUNC(dfs500_state::pit_w));                    // "IO1" IC51
	map(0x1000, 0x1001).mirror(0x8ffc).rw(FUNC(dfs500_state::pic_r), FUNC(dfs500_state::pic_w));                    // "IO2" IC52
	map(0x2000, 0x2000).mirror(0x8ffd).rw(m_serial1, FUNC(i8251_device::data_r), FUNC(i8251_device::data_w));       // "IO3" IC53
	map(0x2002, 0x2002).mirror(0x8ffd).rw(m_serial1, FUNC(i8251_device::status_r), FUNC(i8251_device::control_w));
	map(0x3000, 0x3000).mirror(0x8ffd).rw(m_serial2, FUNC(i8251_device::data_r), FUNC(i8251_device::data_w));       // "IO4" IC54
	map(0x3002, 0x3002).mirror(0x8ffd).rw(m_serial2, FUNC(i8251_device::status_r), FUNC(i8251_device::control_w));
	map(0x4000, 0x4001).mirror(0x8ff0).r(FUNC(dfs500_state::RB0_r));                                                // "RB0" IC32/IC33
	map(0x4002, 0x4003).mirror(0x8ff0).r(FUNC(dfs500_state::RB1_r));                                                // "RB1" IC55
	map(0x4004, 0x4005).mirror(0x8ff0).r(FUNC(dfs500_state::RB2_r));                                                // "RB2" IC56
	map(0x5000, 0x5001).mirror(0x8ff0).w(FUNC(dfs500_state::WB0_w));                                                // "WB0" IC26/IC27
}

void dfs500_state::effectcpu_mem_map(address_map &map)
{
	// Note: As far as I can tell by the schematics in the service manual, the ram mirror should be 0x90000.
	//       But I see unmapped read acesses to 0x4800, which induces me to make the mirror 0x94000, instead.
	//   FIXME: This should be double-checked!
	map(0x00000, 0x03fff).mirror(0x94000).ram();                        // 2x 8kb SRAM chips at IC23/IC24
	map(0x08000, 0x0bfff).mirror(0x90000).bankr("rombank1");            // Effect data on 4x 512kb EPROMs at IC5/IC6/IC7/IC8
	map(0x0c000, 0x0ffff).mirror(0x90000).bankr("rombank2");            // Second banked view of the contents of the same effect data EPROMs
	map(0x20000, 0x20001).mirror(0x9fffe).noprw(); // FIXME: Do something with the MTRX signal generated by this memory access
	map(0x46000, 0x46fff).mirror(0x91000).ram().share("sgram"); // IC80/IC81 at FM-29 (4/6)
																// Selected by IC201 at FM-29 (3/6)
	map(0x68000, 0x68001).mirror(0x907fe).rw(FUNC(dfs500_state::RA0_r), FUNC(dfs500_state::WA0_w)); // "RA0" IC26/IC27 & "WA0" IC32/IC33
																									// 16-bit data latches for communication between CPUs
	map(0x68800, 0x68801).mirror(0x907fe).r(FUNC(dfs500_state::RA1_r));            // "RA1" IC25/IC64
	map(0x69000, 0x69000).mirror(0x907ff).w(FUNC(dfs500_state::rombank2_entry_w)); // "WA2" IC29
	map(0x69800, 0x69800).mirror(0x907ff).w(FUNC(dfs500_state::rombank1_entry_w)); // "WA3" IC30
	map(0x6a000, 0x6a000).mirror(0x907ff).w(FUNC(dfs500_state::input_select_w));   // "WA4" IC31
	map(0x70000, 0x7ffff).mirror(0x80000).rom().region("effectcpu", 0); // 2x 64kb EPROMs at IC3/IC4,
																		// Note: the 1st half of each is entirely made of 0xFF

	// ==== "ORG1" registers ====
	// "controlsignals" (Not sure yet of their actual use)
	// D13: PS2
	// D12: PS1
	// D11: FGS1
	// D10: FGS0
	// D9: BGS1
	// D8: BGS0
	// D7: TKON
	// D6: TKCONT
	// D5: AM2
	// D4: AM1
	// D3: AM0
	// D2: BM2
	// D1: BM1
	// D0: BM0
	map(0x6b078, 0x6b079).mirror(0x90700).w("controlsignals", FUNC(generic_latch_16_device::write));

	// "Reg7_5":
	//  Not sure yet of their actual use...
	map(0x6b07a, 0x6b07b).mirror(0x90700).w("reg7_5", FUNC(generic_latch_16_device::write));

	// XFLT: Feeds into chips IC107 (CKD8070K "Horizontal Variable Filter") and IC108 (CXD8276Q "CMOS Linear Interpolation")
	//        at PCB FM-29 (6/6); Foreground Bus Digital Lowpass Filter
	map(0x6b07c, 0x6b07d).mirror(0x90700).w("xflt", FUNC(generic_latch_16_device::write));

	// YFLT: Feeds into chips IC114 (CKD8263Q "Vertical Variable Filter") and IC115 (CXD8276Q "CMOS Linear Interpolation")
	//        at PCB FM-29 (6/6); Foreground Bus Digital Lowpass Filter
	map(0x6b07e, 0x6b07f).mirror(0x90700).w("yflt", FUNC(generic_latch_16_device::write));

	// ==== "ORG2" registers: ====
	// VE,6-8B/DA,2-23B
	//map(0x6B800, 0x6B800).mirror(0x907ff).w(...);
	map(0xc0000, 0xdffff).nopw(); // FIXME! Temporarily quieting unmapped access log messages on this range...

	// TODO: CKD8263Q: "Color Bar Pattern Generator"

	// ==== Under development ====
	// Here are some temporary notes on the bit-patterns for decoding the addresses
	// of portions of the circuit related to the remainder of this memory map
	//
	// IC73:
	// BA15..12 = 0110 MEMPRG 0x06000

	// REGA: AA18=0 AA17=0 AA15=0 AA14=1
	// !REGA: AA18=1 AA17=1 AA15=1 AA14=0
	// WRA:  /WR
	// WAn:  !REGA & !WRA & n=AA13/12/11
	// WAn:  ?11? 10nn n??? ???? ????

	// ARAM = AA18/17=10
	// ARAMW = ?
	// MTRX = ?
	// OREG1 = REGA=0 (AA18...14=00x01) AA13..11=110 => x00x 0111 0xxx xxxx xxxx => map(0x07000, 0x07001).mirror(0x90000)
	// OREG2 = REGA=0 (AA18...14=00x01) AA13..11=111 => x00x 0111 1xxx xxxx xxxx => map(0x07800, 0x07801).mirror(0x90000)
	// OBUS = active-low "ARAM or MTR or OREG1 or OREG2"
}

static INPUT_PORTS_START(dfs500)
	PORT_START("DSW_S1")
	PORT_DIPNAME( 0x0f, 0x02, "Editing Control Unit Select" )   PORT_DIPLOCATION("S1:4,3,2,1")
	PORT_DIPSETTING(    0x08, "BVE-600" )
	PORT_DIPSETTING(    0x04, "ONE-GPI" )
	PORT_DIPSETTING(    0x02, "BVE-900" )
	PORT_DIPSETTING(    0x01, "BVS-300" )

	PORT_START("DSW_S2")
	PORT_DIPNAME( 0x0f, 0x07, "Freeze Timing" )   PORT_DIPLOCATION("S2:4,3,2,1")
	PORT_DIPSETTING(    0x07, "8" )
	PORT_DIPSETTING(    0x0b, "4" )
	PORT_DIPSETTING(    0x0d, "2" )
	PORT_DIPSETTING(    0x0e, "1" )

	PORT_START("DSW_S3")
	PORT_DIPNAME( 0x08, 0x08, "Field freeze" )   PORT_DIPLOCATION("S3:4")
	PORT_DIPSETTING(    0x00, "Odd Field" )
	PORT_DIPSETTING(    0x08, "Even Freeze" )
	PORT_DIPNAME( 0x04, 0x04, "Color-Matte Compensation" )   PORT_DIPLOCATION("S3:3")
	PORT_DIPSETTING(    0x00, "Illegal compensation" )
	PORT_DIPSETTING(    0x04, "Limit compensation" )
	PORT_DIPNAME( 0x02, 0x02, "Set up" )   PORT_DIPLOCATION("S3:2")
	PORT_DIPSETTING(    0x00, "7.5%" )
	PORT_DIPSETTING(    0x02, "0%" )
	PORT_DIPNAME( 0x01, 0x00, "Freeze (When changing the crosspoint)" )   PORT_DIPLOCATION("S3:1")
	PORT_DIPSETTING(    0x00, "2 Frames" )
	PORT_DIPSETTING(    0x01, "0 Frame" )

	PORT_START("RD1")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("EFFECT CONTROL: SHIFT") // SW74
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DSK BORDER") // SW32
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("MATTES/BKGD: SELECT") // SW35
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("MATTES/BKGD: MATTE COPY") // SW36
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("PATTERN NUMBER: SET") // SW37
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("EFFECT CONTROL: TITLE") // SW38
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("EFFECT CONTROL: DSK") // SW39

	PORT_START("RD2")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DSK KEY INV") // SW26
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DSK EXT KEY") // SW28
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DSK NORMAL") // SW27
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DSK INVERT") // SW29
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DOWNSTREAM KEYER: FILL") // SW31
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DSK MIX") // SW30
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DOWNSTREAM KEYER: TYPE") // SW33
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DOWNSTREAM KEYER: POSITION") // SW34

	PORT_START("RD3")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("BKGD BUS INT VIDEO") PORT_CODE(KEYCODE_5) // SW49
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("FRGD BUS INT VIDEO") PORT_CODE(KEYCODE_T) // SW44
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("KEY INV") // SW20
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("EXT KEY") // SW22
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("TITLE: FILL") // SW25
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("TITLE") // SW24
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("NORMAL") // SW21
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("INVERT") // SW23

	PORT_START("RD4")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("BKGD BUS 4") PORT_CODE(KEYCODE_4) // SW48
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("BKGD BUS 3") PORT_CODE(KEYCODE_3) // SW47
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("BKGD BUS 2") PORT_CODE(KEYCODE_2) // SW46
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("BKGD BUS 1") PORT_CODE(KEYCODE_1) // SW45
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("FRGD BUS 4") PORT_CODE(KEYCODE_R) // SW43
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("FRGD BUS 3") PORT_CODE(KEYCODE_E) // SW42
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("FRGD BUS 2") PORT_CODE(KEYCODE_W) // SW41
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("FRGD BUS 1") PORT_CODE(KEYCODE_Q) // SW40

	PORT_START("RD5")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("KEYPAD: DIRECT") // SW57
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("EFFECT TRANSITION: REVERSE") // SW56
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("EFFECT TRANSITION: AUTO TRANS") // SW55
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("TRANS RATE: EFFECT") // SW53
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("TRANS RATE: DSK") // SW54
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("FREEZE FIELD") // SW51
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("FREEZE FRAME") // SW52
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("INT VIDEO SELECT") PORT_CODE(KEYCODE_I)// SW50

	PORT_START("RD8")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("KEYPAD 0") PORT_CODE(KEYCODE_0_PAD) // SW58
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("KEYPAD DOWN") PORT_CODE(KEYCODE_DOWN) // SW59
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("KEYPAD UP") PORT_CODE(KEYCODE_UP) // SW60
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("KEYPAD ENTER") PORT_CODE(KEYCODE_ENTER_PAD) // SW61
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("KEYPAD 1") PORT_CODE(KEYCODE_1_PAD) // SW62
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("KEYPAD 2") PORT_CODE(KEYCODE_2_PAD) // SW63
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("KEYPAD 3") PORT_CODE(KEYCODE_3_PAD) // SW64
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("KEYPAD RST") // SW65

	PORT_START("RD9")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("KEYPAD 4") PORT_CODE(KEYCODE_4_PAD) // SW66
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("KEYPAD 5") PORT_CODE(KEYCODE_5_PAD) // SW67
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("KEYPAD 6") PORT_CODE(KEYCODE_6_PAD) // SW68
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("KEYPAD DEL") PORT_CODE(KEYCODE_DEL)// SW69
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("KEYPAD 7") PORT_CODE(KEYCODE_7_PAD) // SW70
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("KEYPAD 8") PORT_CODE(KEYCODE_8_PAD) // SW71
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("KEYPAD 9") PORT_CODE(KEYCODE_9_PAD) // SW72
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("KEYPAD INS") PORT_CODE(KEYCODE_INSERT)// SW73

	PORT_START("RD10")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("LIGHTING INTENSITY") // SW8
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("LIGHTING WIDTH") // SW7
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("LIGHTING TYPE") // SW6
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("LIGHTING") // SW5
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("LEARN") // SW4
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("RECALL") // SW3
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("HOLD INPUT") // SW2
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("EDITOR ENABLE") // SW1

	PORT_START("RD11")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("EDGE SOFT") // SW16
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("EDGE BORDER") // SW15
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("TRAIL/SHADOW DENSITY/POSITION") // SW14
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("SHADOW") // SW13
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("TRAIL/SHADOW FILL") // SW12
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DROP BORDER") // SW11
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("TRAIL/SHADOW TYPE") // SW10
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("TRAIL") // SW9

	PORT_START("RD12")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("USER PGM LOCATION") PORT_CODE(KEYCODE_L) // SW19
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("USER PGM EDIT") // SW17
INPUT_PORTS_END

void dfs500_state::dfs500(machine_config &config)
{
	/******************* Control Panel ******************************/
	// NEC D70108C-8 at IC10 (a CPU compatible with Intel 8088)
	V20(config, m_cpanelcpu, 8_MHz_XTAL);
	m_cpanelcpu->set_addrmap(AS_PROGRAM, &dfs500_state::cpanelcpu_mem_map);
	m_cpanelcpu->set_addrmap(AS_IO, &dfs500_state::cpanelcpu_io_map);
	m_cpanelcpu->set_irq_acknowledge_callback(FUNC(dfs500_state::irq_callback));

	// CXQ71054P at IC16 (Programmable Timer / Counter)
	PIT8254(config, m_cpanel_pit, 0);
	m_cpanel_pit->set_clk<0>(8_MHz_XTAL);
	m_cpanel_pit->set_clk<1>(8_MHz_XTAL/2);
	m_cpanel_pit->out_handler<1>().set(m_cpanel_pit, FUNC(pit8254_device::write_clk2));
	m_cpanel_pit->out_handler<0>().set(m_cpanel_serial, FUNC(i8251_device::write_txc));
	m_cpanel_pit->out_handler<0>().append(m_cpanel_serial, FUNC(i8251_device::write_rxc));

	// CXQ71051P at IC17 (Serial Interface Unit)
	I8251(config, m_cpanel_serial, 8_MHz_XTAL/2);
	m_cpanel_serial->txd_handler().set(m_serial1, FUNC(i8251_device::write_rxd));
	m_cpanel_serial->rxrdy_handler().set_inputline(m_cpanelcpu, 0);

	UPD7004(config, m_adc, 8_MHz_XTAL/2);
	// FIXME! m_adc->eoc_ff_callback(). [...] Set bit D0 on register RD0 IC35 KY223
	// TODO: m_adc->in_callback<7>().set_ioport("XCOM");
	// TODO: m_adc->in_callback<6>().set_ioport("YCOM");
	// TODO: m_adc->in_callback<5>().set_ioport("XCOM1");
	// TODO: m_adc->in_callback<4>().set_ioport("YCOM1");
	// TODO: m_adc->in_callback<3>().set_ioport("XCOM0");
	// TODO: m_adc->in_callback<2>().set_ioport("YCOM0");
	// TODO: m_adc->in_callback<1>().set_ioport("TCLIP2");
	// TODO: m_adc->in_callback<0>().set_ioport("TCLIP1");

	//Buzzer
	SPEAKER(config, "mono").front_center();
	BEEP(config, "buzzer", 4000).add_route(ALL_OUTPUTS, "mono", 0.05); // incorrect/arbitrary freq.
																	   // I did not calculate the correct one yet.

	/******************* Effects Processing Unit ********************/
	// CXQ70116P-10 at IC40 (same as V20, but with a 16-bit data bus)
	V30(config, m_maincpu, 8_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &dfs500_state::maincpu_mem_map);
	m_maincpu->set_irq_acknowledge_callback("pic", FUNC(pic8259_device::inta_cb));

	// CXQ70116P-10 at IC9
	V30(config, m_effectcpu, 8_MHz_XTAL);
	m_effectcpu->set_addrmap(AS_PROGRAM, &dfs500_state::effectcpu_mem_map);

	// CXQ71054P at IC51 (Programmable Timer / Counter)
	PIT8254(config, m_pit, 0);
	m_pit->set_clk<0>(8_MHz_XTAL);
	m_pit->set_clk<1>(8_MHz_XTAL);
	m_pit->out_handler<1>().set(m_pit, FUNC(pit8254_device::write_clk2));
	m_pit->out_handler<0>().set(m_serial1, FUNC(i8251_device::write_txc));
	m_pit->out_handler<0>().append(m_serial1, FUNC(i8251_device::write_rxc));
	m_pit->out_handler<0>().append(m_serial2, FUNC(i8251_device::write_txc));
	m_pit->out_handler<0>().append(m_serial2, FUNC(i8251_device::write_rxc));

	// NEC D71059C at IC52 (Programmable Interruption Controller)
	PIC8259(config, m_pic, 0);
	m_pic->out_int_callback().set_inputline(m_maincpu, 0);

	// CXQ71051P at IC53 (Serial Interface Unit)
	I8251(config, m_serial1, 8_MHz_XTAL);
	m_serial1->txd_handler().set(m_cpanel_serial, FUNC(i8251_device::write_rxd));
	m_serial1->txrdy_handler().set(m_pic, FUNC(pic8259_device::ir7_w));

	// CXQ71051P at IC54 (Serial Interface Unit)
	I8251(config, m_serial2, 8_MHz_XTAL);
	m_serial2->txrdy_handler().set(m_pic, FUNC(pic8259_device::ir6_w));
	// FIXME: Declare an interface to hook this up to another emulated device in MAME (such as pve500)
	//
	// This goes to the CN21 EDITOR D-SUB CONNECTOR on board CN-573
	// I think the purpose of this is to connect to an editor such as the Sony PVE-500.
	//
	// m_serial2->txd_handler().set(m_..., FUNC(..._device::write_txd)); "XMIT"
	// m_...->rxd_handler().set(m_serial2, FUNC(i8251_device::write_rxd)); "RCV"

	GENERIC_LATCH_16(config, "controlsignals");
	GENERIC_LATCH_16(config, "reg7_5");
	GENERIC_LATCH_16(config, "xflt");
	GENERIC_LATCH_16(config, "yflt");

	// In the future this could become IMAGE_AVIVIDEO (or even, perhaps, we
	// should add support for capturing frames from real video devices such
	// as a webcam on /dev/video0)
	IMAGE_PICTURE(config, m_input[0]);
	IMAGE_PICTURE(config, m_input[1]);
	IMAGE_PICTURE(config, m_input[2]);
	IMAGE_PICTURE(config, m_input[3]);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(VIDEO_WIDTH, VIDEO_HEIGHT);
	m_screen->set_visarea(0, VIDEO_WIDTH-1, 0, VIDEO_HEIGHT-1);
	m_screen->set_screen_update(FUNC(dfs500_state::screen_update));

	config.set_default_layout(layout_dfs500);
}

ROM_START(dfs500)
	// Process Unit System Control:
	ROM_REGION(0x8000, "cpanelcpu", 0)
	ROM_LOAD("27c256b_npky14_v1.03_293-83_5500_ky223_sony94.ic14", 0x0000, 0x8000, CRC(8b9e564a) SHA1(aa8a1f211a7834fb15f7ecbc58570f566c0ef5ab))

	// Process Unit System Control:
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD16_BYTE("27c256b_npsys1_v1.03_293-84_3eb5_sy172_sony94.ic1", 0x0001, 0x8000, CRC(4604e7c0) SHA1(80f965b69a163a6278d6f54db741f4c5ada1cb59))
	ROM_LOAD16_BYTE("27c256b_npsys2_v1.03_293-85_3ecd_sy172_sony94.ic2", 0x0000, 0x8000, CRC(b80a66e6) SHA1(407ddc5fee61920bfbe90c20faf4482ceef1ad4f))

	// Process Unit Effect Control:
	ROM_REGION(0x10000, "effectcpu", 0)
	ROM_LOAD16_BYTE("27c512_npsys3_v1.04_293-86_b7d0_sy172_sony94.ic3", 0x0001, 0x8000, CRC(69238d02) SHA1(288babc7547858a3ca3f65af0be76f72335392ea))
	ROM_CONTINUE(0x0001, 0x8000)
	ROM_LOAD16_BYTE("27c512_npsys4_v1.04_293-87_b771_sy172_sony94.ic4", 0x0000, 0x8000, CRC(541abd4f) SHA1(e51f5ca6416c17535f2d2a13a7bedfb3b4b4a58b))
	ROM_CONTINUE(0x0000, 0x8000)

	// Process Unit Effect Data:
	ROM_REGION(0x200000, "effectdata", 0)
	ROM_LOAD("27c4001-12f1_sy172_v1.01_d216.ic5", 0x000000,  0x80000, CRC(ae094fcb) SHA1(c29c27b3c80e67caba2078bb60696c1b8692eb8b))
	ROM_LOAD("27c4001-12f1_sy172_v1.01_d225.ic6", 0x080000,  0x80000, CRC(caa6ccb2) SHA1(9b72dc47cf4cc9c2f9915ea4f1bd7b5136e29db5))
	ROM_LOAD("27c4001-12f1_sy172_v1.01_cc13.ic7", 0x100000,  0x80000, CRC(e1fe8606) SHA1(a573c7023daeb84d5a1182db4051b1bccfcfc1f8))
	ROM_LOAD("27c4001-12f1_sy172_v1.01_c42d.ic8", 0x180000,  0x80000, CRC(66e0f20f) SHA1(e82562ae1eeecc5c97b0f40e01102c2ebe0d6276))
ROM_END

} // anonymous namespace


//   YEAR  NAME   PARENT/COMPAT MACHINE  INPUT    CLASS          INIT     COMPANY  FULLNAME                     FLAGS
SYST(1994, dfs500,    0, 0,     dfs500, dfs500, dfs500_state, empty_init, "Sony", "DFS-500 DME Video Switcher", MACHINE_NOT_WORKING)



dg680.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

DG680

2013-01-14 Driver created

All input must be in uppercase.

DG680 (ETI-680), using the DGOS-Z80 operating system.

This is a S100 card.

In some ways, this system is the ancestor of the original Microbee.

No schematic available, most of this is guesswork.

Port 0 is the input from an ascii keyboard.

Port 2 is the cassette interface.

Port 8 controls some kind of memory protection scheme.
The code indicates that B is the page to protect, and
A is the code (0x08 = inhibit; 0x0B = unprotect;
0x0C = enable; 0x0E = protect). There are 256 pages so
each page is 256 bytes.

The clock is controlled by the byte in D80D.

Monitor Commands:
C (compare)*
E (edit)*
F (fill)*
G - Go to address
I - Inhibit CTC
M (move)*
P (clear screen)*
R (read tape)*
S (search)*
T hhmm [ss] - Set the time
W (write tape)*
X - protection status
XC - clear ram
XD - same as X
XE - enable facilities
XF - disable facilities
XP - protect block
XU - unprotect block
Z - go to 0000.

* These commands are identical to the Microbee ones.

ToDo:
- dips
- leds
- need schematic to find out what else is missing

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

#include "emu.h"
#include "machine/keyboard.h"
#include "machine/z80daisy.h"
#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "machine/clock.h"
#include "machine/timer.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "bus/s100/s100.h"
#include "bus/s100/dg640.h"
#include "speaker.h"


namespace {

class dg680_state : public driver_device
{
public:
	dg680_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cass(*this, "cassette")
		, m_pio(*this, "pio")
		, m_ctc(*this, "ctc")
		, m_clock(*this, "cass_clock")
		, m_s100(*this, "s100")
	{ }

	void dg680(machine_config &config);

private:
	u8 porta_r();
	u8 portb_r();
	void portb_w(u8 data);
	u8 port08_r();
	void port08_w(u8 data);
	u8 mem_r(offs_t offset);
	void mem_w(offs_t offset, u8 data);
	void kansas_w(int state);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);
	void kbd_put(u8 data);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	void machine_reset() override ATTR_COLD;
	void machine_start() override ATTR_COLD;

	u8 m_pio_b = 0U;
	u8 m_term_data = 0U;
	u8 m_protection[0x100]{};
	u8 m_cass_data[4]{};
	bool m_cassold = 0, m_cassinbit = 0, m_cassoutbit = 0;

	required_device<cpu_device> m_maincpu;
	required_device<cassette_image_device> m_cass;
	required_device<z80pio_device> m_pio;
	required_device<z80ctc_device> m_ctc;
	required_device<clock_device> m_clock;
	required_device<s100_bus_device> m_s100;
};

void dg680_state::kansas_w(int state)
{
	if ((m_cass->get_state() & CASSETTE_MASK_UISTATE) != CASSETTE_RECORD)
		return;

	u8 twobit = m_cass_data[3] & 15;

	if (state)
	{
		if (twobit == 0)
			m_cassold = m_cassoutbit;

		if (m_cassold)
			m_cass->output(BIT(m_cass_data[3], 0) ? -1.0 : +1.0); // 2400Hz
		else
			m_cass->output(BIT(m_cass_data[3], 1) ? -1.0 : +1.0); // 1200Hz

		m_cass_data[3]++;
	}
}

TIMER_DEVICE_CALLBACK_MEMBER( dg680_state::kansas_r )
{
	// no tape - set to idle
	m_cass_data[1]++;
	if (m_cass_data[1] > 32)
	{
		m_cass_data[1] = 32;
		m_cassinbit = 1;
	}

	if ((m_cass->get_state() & CASSETTE_MASK_UISTATE) != CASSETTE_PLAY)
		return;

	/* cassette - turn 1200/2400Hz to a bit */
	u8 cass_ws = (m_cass->input() > +0.04) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		m_cassinbit = (m_cass_data[1] < 12) ? 1 : 0;
		m_cass_data[1] = 0;
		m_pio->pb0_w(m_cassinbit);
	}
}

u8 dg680_state::mem_r(offs_t offset)
{
	return m_s100->smemr_r(offset + 0xf000);
}

void dg680_state::mem_w(offs_t offset, u8 data)
{
	m_s100->mwrt_w(offset + 0xf000, data);
}


void dg680_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xcfff).ram();
	map(0xd000, 0xd7ff).rom().region("maincpu", 0);
	map(0xd800, 0xefff).ram();
	map(0xf000, 0xffff).rw(FUNC(dg680_state::mem_r),FUNC(dg680_state::mem_w));
}

void dg680_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x03).rw(m_pio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x04, 0x07).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x08, 0x08).rw(FUNC(dg680_state::port08_r), FUNC(dg680_state::port08_w)); //SWP Control and Status
	//map(0x09,0x09) parallel input port
	// Optional AM9519 Programmable Interrupt Controller (port c = data, port d = control)
	//map(0x0c,0x0d).rw("am9519", FUNC(am9519_device::read), FUNC(am9519_device::write));
}

void dg680_state::machine_start()
{
	save_item(NAME(m_pio_b));
	save_item(NAME(m_term_data));
	save_item(NAME(m_protection));
	save_item(NAME(m_cass_data));
	save_item(NAME(m_cassold));
	save_item(NAME(m_cassinbit));
	save_item(NAME(m_cassoutbit));
}

void dg680_state::machine_reset()
{
	m_maincpu->set_pc(0xd000);
	m_pio_b = 0xFF;
}

// this is a guess there is no information available
static const z80_daisy_config dg680_daisy_chain[] =
{
	{ "ctc" },
	{ "pio" },
	{ 0x00 }
};


/* Input ports */
static INPUT_PORTS_START( dg680 )
INPUT_PORTS_END

void dg680_state::kbd_put(u8 data)
{
	if (data == 8)
		data = 127;   // fix backspace
	m_term_data = data;
	/* strobe in keyboard data */
	m_pio->strobe_a(0);
	m_pio->strobe_a(1);
}

u8 dg680_state::porta_r()
{
	u8 data = m_term_data;
	m_term_data = 0;
	return data;
}

u8 dg680_state::portb_r()
{
	return m_pio_b | m_cassinbit;
}

// bit 1 = cassout; bit 2 = motor on
void dg680_state::portb_w(u8 data)
{
	if (BIT(m_pio_b ^ data, 2))
		m_cass->change_state(BIT(data, 2) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
	m_pio_b = data & 0xfe;
	m_cassoutbit = BIT(data, 1);
}

u8 dg680_state::port08_r()
{
	u8 breg = m_maincpu->state_int(Z80_B);
	return m_protection[breg];
}

void dg680_state::port08_w(u8 data)
{
	u8 breg = m_maincpu->state_int(Z80_B);
	m_protection[breg] = data;
}


static void dg680_s100_devices(device_slot_interface &device)
{
	device.option_add("dg640", S100_DG640);
}

DEVICE_INPUT_DEFAULTS_START(dg680_dg640_f000)
	DEVICE_INPUT_DEFAULTS("DSW", 0x1f, 0x1e) // F000-F7FF
DEVICE_INPUT_DEFAULTS_END

void dg680_state::dg680(machine_config &config)
{
	SPEAKER(config, "mono").front_center();

	/* Cassette */
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	TIMER(config, "kansas_r").configure_periodic(FUNC(dg680_state::kansas_r), attotime::from_hz(40000));

	CLOCK(config, m_clock, 4'800); // 300 baud x 16(divider) = 4800
	m_clock->signal_handler().set(FUNC(dg680_state::kansas_w));
	m_clock->signal_handler().append(m_ctc, FUNC(z80ctc_device::trg2));
	m_clock->signal_handler().append(m_ctc, FUNC(z80ctc_device::trg3));

	/* basic machine hardware */
	z80_device& maincpu(Z80(config, m_maincpu, XTAL(8'000'000) / 4));
	maincpu.set_addrmap(AS_PROGRAM, &dg680_state::mem_map);
	maincpu.set_addrmap(AS_IO, &dg680_state::io_map);
	maincpu.set_daisy_config(dg680_daisy_chain);

	/* Keyboard */
	generic_keyboard_device &keyb(GENERIC_KEYBOARD(config, "keyb", 0));
	keyb.set_keyboard_callback(FUNC(dg680_state::kbd_put));

	/* Devices */
	Z80CTC(config, m_ctc, XTAL(8'000'000) / 4);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->set_clk<0>(200);
	m_ctc->zc_callback<0>().set(m_ctc, FUNC(z80ctc_device::trg1));

	Z80PIO(config, m_pio, XTAL(8'000'000) / 4);
	m_pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio->in_pa_callback().set(FUNC(dg680_state::porta_r));
	// OUT_ARDY - this activates to ask for kbd data but not known if actually used
	m_pio->in_pb_callback().set(FUNC(dg680_state::portb_r));
	m_pio->out_pb_callback().set(FUNC(dg680_state::portb_w));

	S100_BUS(config, m_s100, 1_MHz_XTAL);
	S100_SLOT(config, "s100:1", dg680_s100_devices, "dg640")
		.set_option_device_input_defaults("dg640", DEVICE_INPUT_DEFAULTS_NAME(dg680_dg640_f000));
}

/* ROM definition */
ROM_START( dg680 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "dg680.rom", 0x0000, 0x0800, BAD_DUMP CRC(c1aaef6a) SHA1(1508ca8315452edfb984718e795ccbe79a0c0b58) )

	ROM_REGION( 0x0020, "proms", 0 )
	ROM_LOAD( "82s123.bin", 0x0000, 0x0020, NO_DUMP )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY            FULLNAME                   FLAGS
COMP( 1980, dg680, 0,      0,      dg680,   dg680, dg680_state, empty_init, "David Griffiths", "DG680 with DGOS-Z80 1.4", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



dgn_beta.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nathan Woods
/***************************************************************************

    drivers/dgn_beta.cpp

    Dragon Beta prototype, based on two 68B09E processors, WD2797, 6845.

Project Beta was the second machine that Dragon Data had in development at
the time they ceased trading, the first being Project Alpha (also know as the
Dragon Professional).

The machine uses dual 68B09 CPUs which both sit on the same bus and access
the same memory and IO chips ! The first is the main processor, used to run
user code, the second is uses as a DMA controller to amongst other things
disk data transfers. The first processor controlled the second by having the
halt and nmi lines from the second CPU connected to PIA output lines so
that the could be changed under OS control. The first CPU just passed
instructions for the block to be transferred in 5 low ram addresses and
generated an NMI on the second CPU.

Project Beta like the other Dragons used a WD2797 floppy disk controller
which is memory mapped, and controlled by the second CPU.

Unlike the other Dragon machines, project Beta used a 68b45 to generate video,
and totally did away with the SAM.

The machine has a 6551 ACIA chip, but I have not yet found where this is
memory mapped.

Project Beta, had a custom MMU built from a combination of LSTTL logic, and
PAL programmable logic. This MMU could address 256 blocks of 4K, giving a
total addressable range of 1 megabyte, of this the first 768KB could be RAM,
however the machine by default, came with 256K or ram, and a 16K boot ROM,
which contained an OS-9 Level 2 bootstrap.

A lot of the information required to start work on this driver has been
inferred from disassembly of the boot ROM, and from what little hardware
documentation still exists.

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

#include "emu.h"
#include "dgn_beta.h"

#include "cpu/m6809/m6809.h"
#include "machine/6821pia.h"
#include "machine/mos6551.h"
#include "machine/ram.h"
#include "video/mc6845.h"
#include "softlist_dev.h"
#include "screen.h"

#include "formats/vdk_dsk.h"
#include "formats/dmk_dsk.h"
#include "imagedev/floppy.h"


/*
 Colour codes are as below according to os-9 headers, however the precise values
 may not be quite correct, also this will need changing as the palette seems to
 be controlled by a 4x4bit register file in the video hardware
 The text video ram seems to be arranged of words of character, attribute
 The colour codes are stored in the attribute byte along with :
    Underline bit   $40
    Flash bit   $80

These are yet to be implemented.

*/

static constexpr rgb_t dgnbeta_pens[] =
{
	//normal brightness
	{ 0x00, 0x00, 0x00 },   // black
	{ 0x80, 0x00, 0x00 },   // red
	{ 0x00, 0x80, 0x00 },   // green
	{ 0x80, 0x80, 0x00 },   // yellow
	{ 0x00, 0x00, 0x80 },   // blue
	{ 0x80, 0x00, 0x80 },   // magenta
	{ 0x00, 0x80, 0x80 },   // cyan
	{ 0x80, 0x80, 0x80 },   // white

	//enhanced brightness
	{ 0x00, 0x00, 0x00 },   // black
	{ 0xff, 0x00, 0x00 },   // red
	{ 0x00, 0xff, 0x00 },   // green
	{ 0xff, 0xff, 0x00 },   // yellow
	{ 0x00, 0x00, 0xff },   // blue
	{ 0xff, 0x00, 0xff },   // magenta
	{ 0x00, 0xff, 0xff },   // cyan
	{ 0xff, 0xff, 0xff }    // white
};

/*
    2005-05-10

    I *THINK* I know how the memory paging works, the 64K memory map is divided
    into 16x 4K pages, what is mapped into each page is controlled by the IO at
    FE00-FE0F like so :-

    Location    Memory page     Initialised to
    $FE00       $0000-$0FFF     $00
    $FE01       $1000-$1FFF     $00 (used as ram test page, when sizing memory)
    $FE02       $2000-$2FFF     $00
    $FE03       $3000-$3FFF     $00
    $FE04       $4000-$4FFF     $00
    $FE05       $5000-$5FFF     $00
    $FE06       $6000-$6FFF     $1F ($1F000)
    $FE07       $7000-$7FFF     $00
    $FE08       $8000-$8FFF     $00
    $FE09       $9000-$9FFF     $00
    $FE0A       $A000-$AFFF     $00
    $FE0B       $B000-$BFFF     $00
    $FE0C       $C000-$CFFF     $00
    $FE0D       $D000-$DFFF     $00
    $FE0E       $E000-$EFFF     $FE
    $FE0F       $F000-$FFFF     $FF

    The value stored at each location maps it's page to a 4K page within a 1M address
    space. According to the Beta product descriptions released by Dragon Data, the
    machine could have up to 768K of RAM, if this where true then pages $00-$BF could
    potentially be RAM, and pages $C0-$FF would be ROM. The initialisation code maps in
    the memory as described above.

    At reset time the Paging would of course be disabled, as the boot rom needs to be
    mapped in at $C000, the initialisation code would set up the mappings above and then
    enable the paging hardware.

    It appears to be more complicated than this, whilst the above is true, there appear to
    be 16 sets of banking registers, the active set is controlled by the bottom 4 bits of
    FCC0, bit 6 has something to do with enabling and disabling banking.

    2005-11-28

    The value $C0 is guaranteed not to have any memory in it according to the os9 headers,
    quite how the MMU deals with this is still unknown to me.

    Bit 7 of $FCC0, sets maps in the system task which has fixed values for some pages,
    the precise nature of this is yet to be discovered.

*/

void dgn_beta_state::dgnbeta_map(address_map &map)
{
	map(0xfC00, 0xfC1F).noprw();
	map(0xFC20, 0xFC23).rw(m_pia_0, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xFC24, 0xFC27).rw(m_pia_1, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xFC28, 0xfC7F).noprw();
	map(0xfc80, 0xfc80).w(m_mc6845, FUNC(mc6845_device::address_w));
	map(0xfc81, 0xfc81).rw(m_mc6845, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0xfc82, 0xfC9F).noprw();
	map(0xFCA0, 0xFCA3).nopr().w(FUNC(dgn_beta_state::dgnbeta_colour_ram_w));         /* 4x4bit colour ram for graphics modes */
	map(0xFCC0, 0xFCC3).rw(m_pia_2, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xfcC4, 0xfcdf).noprw();
	map(0xfce0, 0xfce3).rw(FUNC(dgn_beta_state::dgnbeta_wd2797_r), FUNC(dgn_beta_state::dgnbeta_wd2797_w));  /* Onboard disk interface */
	map(0xfce4, 0xfdff).noprw();
	map(0xFE00, 0xFE0F).rw(FUNC(dgn_beta_state::dgn_beta_page_r), FUNC(dgn_beta_state::dgn_beta_page_w));
	map(0xfe10, 0xfEff).noprw();
}



static INPUT_PORTS_START( dgnbeta )
	PORT_START("KEY0") /* Key ROw 0 */
	/* Return shift if either shift key pressed */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)

	/* Return FuNction key, if either ALT key pressed */
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ALT") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CLEAR") PORT_CODE(KEYCODE_HOME) PORT_CHAR(12)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8  (") PORT_CODE(KEYCODE_8) PORT_CHAR('8')


	/* Set control on either CTRL key */
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)

	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CapsLock") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(0x1B)

/*  PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CLEAR") PORT_CODE(KEYCODE_HOME) PORT_CHAR(12)
    PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8  (") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
    PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(0x1B)
*/

	PORT_START("KEY1") /* Key row 2 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("b") PORT_CODE(KEYCODE_B) PORT_CHAR('b')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("j") PORT_CODE(KEYCODE_J) PORT_CHAR('j')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("i") PORT_CODE(KEYCODE_I) PORT_CHAR('i')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("u") PORT_CODE(KEYCODE_U) PORT_CHAR('u')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("h") PORT_CODE(KEYCODE_H) PORT_CHAR('h')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')

	PORT_START("KEY2") /* Key row 3 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("n") PORT_CODE(KEYCODE_N) PORT_CHAR('n')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("k") PORT_CODE(KEYCODE_K) PORT_CHAR('k')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("o") PORT_CODE(KEYCODE_O) PORT_CHAR('o')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("g") PORT_CODE(KEYCODE_G) PORT_CHAR('g')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')

	PORT_START("KEY3") /* Key row  4 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("m") PORT_CODE(KEYCODE_M) PORT_CHAR('m')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("l") PORT_CODE(KEYCODE_L) PORT_CHAR('l')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("p") PORT_CODE(KEYCODE_P) PORT_CHAR('p')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("t") PORT_CODE(KEYCODE_T) PORT_CHAR('t')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f") PORT_CODE(KEYCODE_F) PORT_CHAR('f')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')

	PORT_START("KEY4") /* Key row  5 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(';')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR('@')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("v") PORT_CODE(KEYCODE_V) PORT_CHAR('v')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("d") PORT_CODE(KEYCODE_D) PORT_CHAR('d')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')

	PORT_START("KEY5") /* Key row  6 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_COLON) PORT_CHAR(':')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("r") PORT_CODE(KEYCODE_R) PORT_CHAR('r')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("s") PORT_CODE(KEYCODE_S) PORT_CHAR('s')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')

	PORT_START("KEY6") /* Key row  7 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DELETE") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("c") PORT_CODE(KEYCODE_C) PORT_CHAR('c')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("x") PORT_CODE(KEYCODE_X) PORT_CHAR('x')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("KEY7") /* Key row  8 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ASTERISK") PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR('*')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PAD_7") PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PAD_4") PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PAD_1") PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("e") PORT_CODE(KEYCODE_E) PORT_CHAR('e')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')

	PORT_START("KEY8") /* Key row  9 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PAD_0") PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PAD_8") PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PAD_5") PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PAD_2") PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("w") PORT_CODE(KEYCODE_W) PORT_CHAR('w')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("a") PORT_CODE(KEYCODE_A) PORT_CHAR('a')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')

	PORT_START("KEY9") /* Key row  10 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PAD_HASH") PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR('#')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PAD_9") PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PAD_6") PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PAD_3") PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TAB") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DOT_PAD") PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR('.')

INPUT_PORTS_END


void dgn_beta_state::dgn_beta_palette(palette_device &palette) const
{
	palette.set_pen_colors(0, dgnbeta_pens);
}

/* F4 Character Displayer */
static const gfx_layout dgnbeta_charlayout =
{
	8, 10,                  /* 8 x 10 characters */
	256,                    /* 256 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_dgnbeta )
	GFXDECODE_ENTRY( "gfx1", 0x0000, dgnbeta_charlayout, 0, 8 )
GFXDECODE_END

void dgn_beta_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_VDK_FORMAT);
	fr.add(FLOPPY_DMK_FORMAT);
}

static void dgnbeta_floppies(device_slot_interface &device)
{
	device.option_add("dd", FLOPPY_35_DD);
}

void dgn_beta_state::dgnbeta(machine_config &config)
{
	/* basic machine hardware */
	MC6809E(config, m_maincpu, DGNBETA_CPU_SPEED_HZ);        /* 2 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &dgn_beta_state::dgnbeta_map);
	m_maincpu->set_dasm_override(FUNC(dgn_beta_state::dgnbeta_dasm_override));

	/* both cpus in the beta share the same address/data buses */
	MC6809E(config, m_dmacpu, DGNBETA_CPU_SPEED_HZ);        /* 2 MHz */
	m_dmacpu->set_addrmap(AS_PROGRAM, &dgn_beta_state::dgnbeta_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(DGNBETA_FRAMES_PER_SECOND);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(100));
	screen.set_size(700,550);
	screen.set_visarea(0, 699, 0, 549);
	screen.set_screen_update("crtc", FUNC(hd6845s_device::screen_update));
	screen.set_video_attributes(VIDEO_UPDATE_AFTER_VBLANK);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_dgnbeta);
	PALETTE(config, m_palette, FUNC(dgn_beta_state::dgn_beta_palette), std::size(dgnbeta_pens));

	/* PIA 0 at $FC20-$FC23 I46 */
	PIA6821(config, m_pia_0);
	m_pia_0->readpa_handler().set(FUNC(dgn_beta_state::d_pia0_pa_r));
	m_pia_0->readpb_handler().set(FUNC(dgn_beta_state::d_pia0_pb_r));
	m_pia_0->writepa_handler().set(FUNC(dgn_beta_state::d_pia0_pa_w));
	m_pia_0->writepb_handler().set(FUNC(dgn_beta_state::d_pia0_pb_w));
	m_pia_0->cb2_handler().set(FUNC(dgn_beta_state::d_pia0_cb2_w));
	m_pia_0->irqa_handler().set(FUNC(dgn_beta_state::d_pia0_irq_a));
	m_pia_0->irqb_handler().set(FUNC(dgn_beta_state::d_pia0_irq_b));

	/* PIA 1 at $FC24-$FC27 I63 */
	PIA6821(config, m_pia_1);
	m_pia_1->readpa_handler().set(FUNC(dgn_beta_state::d_pia1_pa_r));
	m_pia_1->readpb_handler().set(FUNC(dgn_beta_state::d_pia1_pb_r));
	m_pia_1->writepa_handler().set(FUNC(dgn_beta_state::d_pia1_pa_w));
	m_pia_1->writepb_handler().set(FUNC(dgn_beta_state::d_pia1_pb_w));
	m_pia_1->irqa_handler().set(FUNC(dgn_beta_state::d_pia1_irq_a));
	m_pia_1->irqb_handler().set(FUNC(dgn_beta_state::d_pia1_irq_b));

	/* PIA 2 at FCC0-FCC3 I28 */
	/* This seems to control the RAM paging system, and have the DRQ */
	/* from the WD2797 */
	PIA6821(config, m_pia_2);
	m_pia_2->readpa_handler().set(FUNC(dgn_beta_state::d_pia2_pa_r));
	m_pia_2->readpb_handler().set(FUNC(dgn_beta_state::d_pia2_pb_r));
	m_pia_2->writepa_handler().set(FUNC(dgn_beta_state::d_pia2_pa_w));
	m_pia_2->writepb_handler().set(FUNC(dgn_beta_state::d_pia2_pb_w));
	m_pia_2->irqa_handler().set(FUNC(dgn_beta_state::d_pia2_irq_a));
	m_pia_2->irqb_handler().set(FUNC(dgn_beta_state::d_pia2_irq_b));

	WD2797(config, m_fdc, 1_MHz_XTAL);
	m_fdc->intrq_wr_callback().set(FUNC(dgn_beta_state::dgnbeta_fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(dgn_beta_state::dgnbeta_fdc_drq_w));

	FLOPPY_CONNECTOR(config, FDC_TAG ":0", dgnbeta_floppies, "dd", dgn_beta_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, FDC_TAG ":1", dgnbeta_floppies, "dd", dgn_beta_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, FDC_TAG ":2", dgnbeta_floppies, nullptr, dgn_beta_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, FDC_TAG ":3", dgnbeta_floppies, nullptr, dgn_beta_state::floppy_formats).enable_sound(true);

	HD6845S(config, m_mc6845, 12.288_MHz_XTAL / 16);    //XTAL is guessed
	m_mc6845->set_screen("screen");
	m_mc6845->set_show_border_area(false);
	m_mc6845->set_char_width(16); /*?*/
	m_mc6845->set_update_row_callback(FUNC(dgn_beta_state::crtc_update_row));
	m_mc6845->out_vsync_callback().set(FUNC(dgn_beta_state::dgnbeta_vsync_changed));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("256K").set_extra_options("128K,384K,512K,640K,768K");
	/* Ram size can now be configured, since the machine was known as either the Dragon Beta or */
	/* the Dragon 128, I have added a config for 128K, however, the only working machine known  */
	/* to exist was fitted with 256K, so I have made this the default. Also available           */
	/* documentation seems to suggest a maximum of 768K, so I have included configs increasing  */
	/* in blocks of 128K up to this maximum.                                                    */

	/* software lists */
	SOFTWARE_LIST(config, "flop_list").set_original("dgnbeta_flop");
}

ROM_START(dgnbeta)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_SYSTEM_BIOS( 0, "bootrom", "Dragon Beta OS-9 Boot ROM (15.6.84)" )
	ROMX_LOAD("beta_bt.rom"     ,0x0000 ,0x4000 ,CRC(4c54c1de) SHA1(141d9fcd2d187c305dff83fce2902a30072aed76), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "testrom", "Dragon Beta Test ROM (1984?)" )
	ROMX_LOAD("beta_tst.rom"    ,0x2000 ,0x2000 ,CRC(01d79d00) SHA1(343e08cf7656b5e8970514868df37ea0af1e2362), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "cfiles", "cfiles rom" )
	ROMX_LOAD("beta_cfi.rom"    ,0x2000 ,0x2000 ,CRC(d312e4c0) SHA1(5c00daac488eaf8d36d66de6ec6c746ab7b78ecf), ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "dfiles", "dfiles rom" )
	ROMX_LOAD("beta_dfi.rom"    ,0x2000 ,0x2000 ,CRC(c4ad7f64) SHA1(50aa92a1c383321485d5a1aa41dfe4f90b3beaed), ROM_BIOS(3))

	ROM_REGION (0x2000, "gfx1", 0)
	ROM_LOAD("betachar.rom" ,0x0000 ,0x2000 ,CRC(ca79d66c) SHA1(8e2090d471dd97a53785a7f44a49d3c8c85b41f2))
ROM_END

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS           INIT        COMPANY            FULLNAME             FLAGS
COMP( 1984, dgnbeta, 0,      0,      dgnbeta, dgnbeta, dgn_beta_state, empty_init, "Dragon Data Ltd", "Dragon 128 (Beta)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



dgnalpha.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nathan Woods
/***************************************************************************

    Dragon Alpha

    The Dragon Alpha was a prototype in development when Dragon Data went bust,
    it is basically an enhanced Dragon 64, with built in modem, disk system, and
    graphical boot rom.

    It has the following extra hardware :-
    A third 6821 PIA mapped between FF24 and FF27
        An AY-8912, connected to the PIA.

    Port A of the PIA is connected as follows :-

        b0  BDIR of AY8912
        b1  BC1 of AY8912
        b2  Rom select, High= boot rom, low=BASIC rom
        b3..7 not used.

    Port B
        b0..7 connected to D0..7 of the AY8912.

    CB1 DRQ of WD2797.

    /irqa
    /irqb   both connected to 6809 FIRQ.


    The analog outputs of the AY-8912 are connected to the standard sound multiplexer.
    The AY8912 output port is used as follows :-

        b0..b3  /DS0../DS3 for the drive interface (through an inverter first).
        b4      /motor for the drive interface (through an inverter first).
        b5..b7  not used as far as I can tell.

    A 6850 for the modem.

    A WD2797, used as an internal disk interface, this is however connected in a slightly strange
    way that I am yet to completely determine.
    19/10/2004, WD2797 is mapped between FF2C and FF2F, however the order of the registers is
    reversed so the command Register is at the highest address instead of the lowest. The Data
    request pin is connected to CB1(pin 18) of PIA2, to cause an firq, the INTRQ, is connected via
    an inverter to the 6809's NMI.

    All these are as yet un-emulated.

    29-Oct-2004, AY-8912 is now emulated.
    30-Oct-2004, Internal disk interface now emulated, Normal DragonDos rom replaced with a re-assembled
                version, that talks to the alpha hardware (verified on a clone of the real machine).

Dragon Alpha code added 21-Oct-2004,
            Phill Harvey-Smith (afra@aurigae.demon.co.uk)

            Added AY-8912 and FDC code 30-Oct-2004.

Fixed Dragon Alpha NMI enable/disable, following circuit traces on a real machine.
    P.Harvey-Smith, 11-Aug-2005.

Re-implemented Alpha NMI enable/disable, using direct PIA reads, rather than
keeping track of it in a variable in the driver.
    P.Harvey-Smith, 25-Sep-2006.

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

#include "emu.h"
#include "dragon.h"

#include "imagedev/floppy.h"
#include "machine/wd_fdc.h"
#include "sound/ay8910.h"

#include "softlist_dev.h"

#include "formats/dmk_dsk.h"
#include "formats/sdf_dsk.h"
#include "formats/vdk_dsk.h"


namespace {

//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

/* devices */
#define WD2797_TAG                  "wd2797"



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

class dragon_alpha_state : public dragon64_state
{
public:
	dragon_alpha_state(const machine_config &mconfig, device_type type, const char *tag) :
		dragon64_state(mconfig, type, tag),
		m_pia_2(*this, "pia2"),
		m_ay8912(*this, "ay8912"),
		m_fdc(*this, WD2797_TAG),
		m_floppy(*this, WD2797_TAG ":%u", 0U),
		m_nmis(*this, "nmis")
	{
	}

	void dgnalpha(machine_config &config);

private:
	static void dragon_formats(format_registration &fr);

	/* pia2 */
	void pia2_pa_w(uint8_t data);

	/* psg */
	uint8_t psg_porta_read();
	void psg_porta_write(uint8_t data);

	/* fdc */
	void fdc_intrq_w(int state);
	void fdc_drq_w(int state);

	void dgnalpha_io1(address_map &map) ATTR_COLD;

	required_device<pia6821_device> m_pia_2;
	required_device<ay8912_device> m_ay8912;
	required_device<wd2797_device> m_fdc;
	required_device_array<floppy_connector, 4> m_floppy;
	required_device<input_merger_device> m_nmis;

	/* modem */
	uint8_t modem_r(offs_t offset);
	void modem_w(offs_t offset, uint8_t data);
};



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

void dragon_alpha_state::dgnalpha_io1(address_map &map)
{
	// $FF20-$FF3F
	map(0x00, 0x03).mirror(0x10).r(m_pia_1, FUNC(pia6821_device::read)).w(FUNC(coco12_state::ff20_write));
	map(0x04, 0x07).mirror(0x10).rw(m_pia_2, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x08, 0x0b).mirror(0x10).rw(FUNC(dragon_alpha_state::modem_r), FUNC(dragon_alpha_state::modem_w));
	map(0x0c, 0x0c).mirror(0x10).rw(m_fdc, FUNC(wd2797_device::data_r), FUNC(wd2797_device::data_w));
	map(0x0d, 0x0d).mirror(0x10).rw(m_fdc, FUNC(wd2797_device::sector_r), FUNC(wd2797_device::sector_w));
	map(0x0e, 0x0e).mirror(0x10).rw(m_fdc, FUNC(wd2797_device::track_r), FUNC(wd2797_device::track_w));
	map(0x0f, 0x0f).mirror(0x10).rw(m_fdc, FUNC(wd2797_device::status_r), FUNC(wd2797_device::cmd_w));
}


/***************************************************************************
  MODEM
***************************************************************************/

//-------------------------------------------------
//  modem_r
//-------------------------------------------------

uint8_t dragon_alpha_state::modem_r(offs_t offset)
{
	return 0xFF;
}



//-------------------------------------------------
//  modem_w
//-------------------------------------------------

void dragon_alpha_state::modem_w(offs_t offset, uint8_t data)
{
}



/***************************************************************************
  PIA2 ($FF24-$FF28) on Dragon Alpha/Professional

    PIA2 PA0        bcdir to AY-8912
    PIA2 PA1        bc0 to AY-8912
    PIA2 PA2        Rom switch, 0=basic rom, 1=boot rom.
    PIA2 PA3-PA7    Unknown/unused ?
    PIA2 PB0-PB7    connected to D0..7 of the AY8912.
    CB1             DRQ from WD2797 disk controller.
***************************************************************************/

//-------------------------------------------------
//  pia2_pa_w
//-------------------------------------------------

void dragon_alpha_state::pia2_pa_w(uint8_t data)
{
	uint8_t ddr = ~m_pia_2->port_b_z_mask();

	/* If bit 2 of the pia2 ddra is 1 then this pin is an output so use it */
	/* to control the paging of the boot and basic roms */
	/* Otherwise it set as an input, with an internal pull-up so it should */
	/* always be high (enabling boot rom) */
	/* PIA FIXME if (pia_get_ddr_a(2) & 0x04) */
	if(ddr & 0x04)
	{
		page_rom(data & 0x04 ? true : false);   /* bit 2 controls boot or basic rom */
	}

	/* Bits 0 and 1 for pia2 port a control the BCDIR and BC1 lines of the */
	/* AY-8912 */
	switch (data & 0x03)
	{
		case 0x00:      /* Inactive, do nothing */
			break;
		case 0x01:      /* Write to selected port */
			m_ay8912->data_w(m_pia_2->b_output());
			break;
		case 0x02:      /* Read from selected port */
			m_pia_2->portb_w(m_ay8912->data_r());
			break;
		case 0x03:      /* Select port to write to */
			m_ay8912->address_w(m_pia_2->b_output());
			break;
	}
}



/***************************************************************************
  AY8912
***************************************************************************/

//-------------------------------------------------
//  psg_porta_read
//-------------------------------------------------

uint8_t dragon_alpha_state::psg_porta_read()
{
	return 0;
}



//-------------------------------------------------
//  psg_porta_read
//-------------------------------------------------

void dragon_alpha_state::psg_porta_write(uint8_t data)
{
	/* Bits 0..3 are the drive select lines for the internal floppy interface */
	/* Bit 4 is the motor on, in the real hardware these are inverted on their way to the drive */
	/* Bits 5,6,7 are connected to /DDEN, ENP and 5/8 on the WD2797 */

	floppy_image_device *floppy = nullptr;

	for (int n = 0; n < 4; n++)
		if (BIT(data, n))
			floppy = m_floppy[n]->get_device();

	m_fdc->set_floppy(floppy);

	// todo: turning the motor on with bit 4 isn't giving the drive enough
	// time to spin up, how does it work in hardware?
	for (auto &f : m_floppy)
		if (f->get_device()) f->get_device()->mon_w(0);

	m_fdc->dden_w(BIT(data, 5));
}

/***************************************************************************
  FDC
***************************************************************************/

//-------------------------------------------------
//  fdc_intrq_w - The NMI line on the Alpha is gated
//  through IC16 (early PLD), and is gated by pia2 CA2
//-------------------------------------------------

void dragon_alpha_state::fdc_intrq_w(int state)
{
	if (state)
	{
		if (m_pia_2->ca2_output_z())
			m_nmis->in_w<1>(1);
	}
	else
	{
		m_nmis->in_w<1>(0);
	}
}



//-------------------------------------------------
//  fdc_drq_w - The DRQ line goes through pia2 CB1,
//  in exactly the same way as DRQ from DragonDos
//  does for pia1 CB1
//-------------------------------------------------

void dragon_alpha_state::fdc_drq_w(int state)
{
	m_pia_2->cb1_w(state ? ASSERT_LINE : CLEAR_LINE);
}


void dragon_alpha_state::dragon_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_VDK_FORMAT);
	fr.add(FLOPPY_DMK_FORMAT);
	fr.add(FLOPPY_SDF_FORMAT);
}

static void dragon_alpha_floppies(device_slot_interface &device)
{
	device.option_add("dd", FLOPPY_35_DD);
}


void dragon_alpha_state::dgnalpha(machine_config &config)
{
	dragon_base(config);

	// internal ram
	RAM(config, RAM_TAG).set_default_size("64K");

	sam().set_addrmap(1, &dragon_alpha_state::d64_rom0);
	sam().set_addrmap(2, &dragon_alpha_state::d64_rom1);
	sam().set_addrmap(4, &dragon_alpha_state::d64_io0);
	sam().set_addrmap(5, &dragon_alpha_state::dgnalpha_io1);

	// input merger
	INPUT_MERGER_ANY_HIGH(config, m_nmis).output_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);

	// cartridge
	COCOCART_SLOT(config, m_cococart, DERIVED_CLOCK(1, 1), &dragon_alpha_state::dragon_cart, nullptr);
	m_cococart->cart_callback().set([this] (int state) { cart_w(state != 0); }); // lambda because name is overloaded
	m_cococart->nmi_callback().set(m_nmis, FUNC(input_merger_device::in_w<0>));
	m_cococart->halt_callback().set_inputline(m_maincpu, INPUT_LINE_HALT);

	// acia
	mos6551_device &acia(MOS6551(config, "acia", 0));
	acia.set_xtal(1.8432_MHz_XTAL);

	// floppy
	WD2797(config, m_fdc, 4_MHz_XTAL/4);
	m_fdc->intrq_wr_callback().set(FUNC(dragon_alpha_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(dragon_alpha_state::fdc_drq_w));

	FLOPPY_CONNECTOR(config, WD2797_TAG ":0", dragon_alpha_floppies, "dd", dragon_alpha_state::dragon_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, WD2797_TAG ":1", dragon_alpha_floppies, "dd", dragon_alpha_state::dragon_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, WD2797_TAG ":2", dragon_alpha_floppies, nullptr, dragon_alpha_state::dragon_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, WD2797_TAG ":3", dragon_alpha_floppies, nullptr, dragon_alpha_state::dragon_formats).enable_sound(true);

	// sound hardware
	AY8912(config, m_ay8912, 4_MHz_XTAL/4);
	m_ay8912->port_a_read_callback().set(FUNC(dragon_alpha_state::psg_porta_read));
	m_ay8912->port_a_write_callback().set(FUNC(dragon_alpha_state::psg_porta_write));
	m_ay8912->add_route(ALL_OUTPUTS, "speaker", 0.75);

	// pia 2
	PIA6821(config, m_pia_2);
	m_pia_2->writepa_handler().set(FUNC(dragon_alpha_state::pia2_pa_w));
	m_pia_2->irqa_handler().set(m_firqs, FUNC(input_merger_device::in_w<2>));
	m_pia_2->irqb_handler().set(m_firqs, FUNC(input_merger_device::in_w<3>));

	// software lists
	SOFTWARE_LIST(config, "dgnalpha_flop_list").set_original("dgnalpha_flop");
	SOFTWARE_LIST(config, "dragon_flex_list").set_original("dragon_flex");
	SOFTWARE_LIST(config, "dragon_os9_list").set_original("dragon_os9");
}

ROM_START(dgnalpha)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_DEFAULT_BIOS("boot10")
	ROM_SYSTEM_BIOS(0, "boot10", "Boot v1.0")
	ROMX_LOAD("alpha_bt_10.rom", 0x2000,  0x2000, CRC(c3dab585) SHA1(4a5851aa66eb426e9bb0bba196f1e02d48156068), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "boot04", "Boot v0.4")
	ROMX_LOAD("alpha_bt_04.rom", 0x2000,  0x2000, CRC(d6172b56) SHA1(69ea376dbc7418f69e9e809b448d22a4de012344), ROM_BIOS(1))
	ROM_LOAD("alpha_ba.rom",    0x8000,  0x4000, CRC(84f68bf9) SHA1(1983b4fb398e3dd9668d424c666c5a0b3f1e2b69))
ROM_END

} // anonymous namespace


COMP( 1984, dgnalpha,   dragon32, 0,      dgnalpha,   dragon,     dragon_alpha_state, empty_init, "Dragon Data Ltd",              "Dragon Professional (Alpha)",    0 )



diablo.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:yoyo_chessboard, Berger
/*******************************************************************************

Novag Diablo 68000 (model 908)
Novag Scorpio 68000 (model 909)

Hardware notes (Diablo 68000):
- PCB label: 100126 REV-2
- TMP68HC000P-16 @ 16MHz, IPL1 256Hz, IPL2 from ACIA IRQ(always high)
- 2*8KB RAM TC5565 battery-backed, 2*32KB hashtable RAM TC55257
- 3*32KB ROM (27C256 or equivalent)
- HD44780A00 LCD controller (16x1)
- R65C51P2 ACIA @ 1.8432MHz, RS232 for Novag Super System
- magnetic sensors, 8*8 chessboard leds

Scorpio 68000 hardware is very similar, but with chessboard buttons and side leds.

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

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
#include "machine/clock.h"
#include "machine/mos6551.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/beep.h"
#include "video/hd44780.h"
#include "video/pwm.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

// internal artwork
#include "novag_diablo68.lh"
#include "novag_scorpio68.lh"


namespace {

class diablo_state : public driver_device
{
public:
	diablo_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen"),
		m_display(*this, "display"),
		m_lcd(*this, "hd44780"),
		m_board(*this, "board"),
		m_acia(*this, "acia"),
		m_rs232(*this, "rs232"),
		m_beeper(*this, "beeper"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// machine configs
	void diablo68(machine_config &config);
	void scorpio68(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<m68000_base_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<pwm_display_device> m_display;
	required_device<hd44780_device> m_lcd;
	required_device<sensorboard_device> m_board;
	required_device<mos6551_device> m_acia;
	required_device<rs232_port_device> m_rs232;
	required_device<beep_device> m_beeper;
	required_ioport_array<8> m_inputs;

	u8 m_inp_mux = 0;
	u8 m_lcd_control = 0;
	u8 m_lcd_data = 0;

	// address maps
	void diablo68_map(address_map &map) ATTR_COLD;
	void scorpio68_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void control_w(u8 data);
	void control2_w(u8 data);
	void lcd_data_w(u8 data);
	void leds_w(u8 data);
	u8 input1_r();
	u8 input2_r();

	HD44780_PIXEL_UPDATE(lcd_pixel_update);
	void lcd_palette(palette_device &palette) const;
};

void diablo_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_lcd_control));
	save_item(NAME(m_lcd_data));
}



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

// LCD

void diablo_state::lcd_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(0xff, 0xff, 0xff)); // background
	palette.set_pen_color(1, rgb_t(0x00, 0x00, 0x00)); // lcd pixel on
	palette.set_pen_color(2, rgb_t(0xe8, 0xe8, 0xe8)); // lcd pixel off
}

HD44780_PIXEL_UPDATE(diablo_state::lcd_pixel_update)
{
	// char size is 5x8
	if (x > 4 || y > 7)
		return;

	if (line < 2 && pos < 8)
	{
		// internal: (8+8)*1, external: 1*16
		bitmap.pix(1 + y, 1 + line*8*6 + pos*6 + x) = state ? 1 : 2;
	}
}


// misc

void diablo_state::control_w(u8 data)
{
	// d0: HD44780 E
	// d1: HD44780 RS
	if (m_lcd_control & ~data & 1)
		m_lcd->write(m_lcd_control >> 1 & 1, m_lcd_data);
	m_lcd_control = data & 3;

	// d7: enable beeper
	m_beeper->set_state(BIT(data, 7));

	// d4-d6: input mux, led select
	m_inp_mux = data >> 4 & 7;
	m_display->write_my(1 << m_inp_mux);
}

void diablo_state::control2_w(u8 data)
{
	control_w(data);

	// d2,d3: side leds (scorpio68)
	m_display->write_mx(~data >> 2 & 3);
}

void diablo_state::lcd_data_w(u8 data)
{
	// d0-d7: HD44780 data
	m_lcd_data = data;
}

void diablo_state::leds_w(u8 data)
{
	// d0-d7: chessboard leds (diablo68)
	m_display->write_mx(data);
}

u8 diablo_state::input1_r()
{
	// d0-d7: multiplexed inputs (chessboard squares)
	return ~m_board->read_rank(m_inp_mux, true);
}

u8 diablo_state::input2_r()
{
	// d0-d2: multiplexed inputs (side panel)
	// other: ?
	return ~m_inputs[m_inp_mux]->read() & 7;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void diablo_state::diablo68_map(address_map &map)
{
	map(0x000000, 0x00ffff).rom();
	map(0x200000, 0x20ffff).rom().region("maincpu", 0x10000);
	map(0x280000, 0x28ffff).ram();
	map(0x300000, 0x300007).rw("acia", FUNC(mos6551_device::read), FUNC(mos6551_device::write)).umask16(0xff00);
	map(0x380000, 0x380001).nopr();
	map(0x380000, 0x380000).w(FUNC(diablo_state::leds_w));
	map(0x3a0000, 0x3a0000).w(FUNC(diablo_state::lcd_data_w));
	map(0x3c0000, 0x3c0000).rw(FUNC(diablo_state::input2_r), FUNC(diablo_state::control_w));
	map(0x3e0000, 0x3e0000).r(FUNC(diablo_state::input1_r));
	map(0xff8000, 0xffbfff).ram().share("nvram");
}

void diablo_state::scorpio68_map(address_map &map)
{
	diablo68_map(map);
	map(0x380000, 0x380000).w(FUNC(diablo_state::control2_w));
	map(0x3c0000, 0x3c0001).nopw();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( diablo68 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Go")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Take Back / Analyze Games")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Right")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Set Level")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Flip Display / Time Control")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Left")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Hint / Next Best")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Priority / Tournament Book / Pawn")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Yes/Start / Start of Game")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Trace Forward / AutoPlay")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Pro-Op / Restore Game / Rook")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("No/End / End of Game")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("Clear Board / Delete Pro-Op")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Best Move/Random / Review / Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Print Book / Store Game")

	PORT_START("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Change Color")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Sound / Info / Bishop")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Print Moves / Print Evaluations")

	PORT_START("IN.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_J) PORT_NAME("Verify/Set Up / Pro-Op Book/Both Books")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Solve Mate / Infinite / Queen")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Print List / Acc. Time")

	PORT_START("IN.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_K) PORT_NAME("New Game")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Player/Player / Gambit Book / King")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Print Board / Interface")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void diablo_state::diablo68(machine_config &config)
{
	// basic machine hardware
	M68000(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_interrupt_mixer(false);
	m_maincpu->set_addrmap(AS_PROGRAM, &diablo_state::diablo68_map);

	auto &irq_clock(CLOCK(config, "irq_clock", 32.768_kHz_XTAL/128)); // 256Hz
	irq_clock.set_pulse_width(attotime::from_nsec(1380)); // active for 1.38us
	irq_clock.signal_handler().set_inputline(m_maincpu, M68K_IRQ_IPL1);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(100));
	m_board->set_nvram_enable(true);

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60); // arbitrary
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	m_screen->set_size(6*16+1, 10);
	m_screen->set_visarea_full();
	m_screen->set_screen_update("hd44780", FUNC(hd44780_device::screen_update));
	m_screen->set_palette("palette");

	PALETTE(config, "palette", FUNC(diablo_state::lcd_palette), 3);

	HD44780(config, m_lcd, 270'000); // OSC = 91K resistor
	m_lcd->set_lcd_size(2, 8);
	m_lcd->set_pixel_update_cb(FUNC(diablo_state::lcd_pixel_update));

	PWM_DISPLAY(config, m_display).set_size(8, 8);
	config.set_default_layout(layout_novag_diablo68);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 32.768_kHz_XTAL/32); // 1024Hz
	m_beeper->add_route(ALL_OUTPUTS, "mono", 0.25);

	// uart (configure after video)
	MOS6551(config, m_acia).set_xtal(1.8432_MHz_XTAL);
	m_acia->irq_handler().set_inputline("maincpu", M68K_IRQ_IPL2);
	m_acia->rts_handler().set("acia", FUNC(mos6551_device::write_cts));
	m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_acia->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set("acia", FUNC(mos6551_device::write_rxd));
	m_rs232->dsr_handler().set("acia", FUNC(mos6551_device::write_dsr));
}

void diablo_state::scorpio68(machine_config &config)
{
	diablo68(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &diablo_state::scorpio68_map);

	m_board->set_type(sensorboard_device::BUTTONS);
	m_board->set_delay(attotime::from_msec(150));

	m_display->set_width(2);
	config.set_default_layout(layout_novag_scorpio68);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( diablo68 ) // ID = D 1.08, serial 91017x
	ROM_REGION16_BE( 0x20000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_BYTE("d_904.u3", 0x00000, 0x8000, CRC(03477746) SHA1(8bffcb159a61e59bfc45411e319aea6501ebe2f9) )
	ROM_LOAD16_BYTE("d_c18.u2", 0x00001, 0x8000, CRC(7a957540) SHA1(756ad377e2425be02be49603d571df2fcc5f06a6) )
	ROM_LOAD16_BYTE("502.u4",   0x10000, 0x8000, CRC(553a5c8c) SHA1(ccb5460ff10766a5ca8008ae2cffcff794318108) ) // no odd rom
ROM_END

ROM_START( diablo68a ) // ID = D 1.08, serial 90999x
	ROM_REGION16_BE( 0x20000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_BYTE("d_904.u3", 0x00000, 0x8000, CRC(03477746) SHA1(8bffcb159a61e59bfc45411e319aea6501ebe2f9) )
	ROM_LOAD16_BYTE("d_a09.u2", 0x00001, 0x8000, CRC(bcd27647) SHA1(2de5bccd4e7b9a32957811c0028dbca1a407b9b0) ) // only 2 bytes different from diablo68
	ROM_LOAD16_BYTE("502.u4",   0x10000, 0x8000, CRC(553a5c8c) SHA1(ccb5460ff10766a5ca8008ae2cffcff794318108) ) // no odd rom
ROM_END

ROM_START( diablo68b ) // ID = D 1.08, serial 90984x
	ROM_REGION16_BE( 0x20000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_BYTE("d_904.u3", 0x00000, 0x8000, CRC(03477746) SHA1(8bffcb159a61e59bfc45411e319aea6501ebe2f9) )
	ROM_LOAD16_BYTE("d_924.u2", 0x00001, 0x8000, CRC(e182dbdd) SHA1(24dacbef2173fa737636e4729ff22ec1e6623ca5) ) // only 4 bytes different from diablo68a
	ROM_LOAD16_BYTE("502.u4",   0x10000, 0x8000, CRC(553a5c8c) SHA1(ccb5460ff10766a5ca8008ae2cffcff794318108) ) // no odd rom
ROM_END

ROM_START( diablo68c ) // ID = D 1.08, serial 90973x
	ROM_REGION16_BE( 0x20000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_BYTE("d_evn_904.u3", 0x00000, 0x8000, CRC(03477746) SHA1(8bffcb159a61e59bfc45411e319aea6501ebe2f9) )
	ROM_LOAD16_BYTE("d_odd_904.u2", 0x00001, 0x8000, CRC(d46fcc7a) SHA1(8ed69cd0fec07bf5451eaa882c87cf7cf70c87eb) ) // only 2 bytes different from diablo68b
	ROM_LOAD16_BYTE("ds_bk.u4",     0x10000, 0x8000, CRC(553a5c8c) SHA1(ccb5460ff10766a5ca8008ae2cffcff794318108) ) // no odd rom
ROM_END


ROM_START( scorpio68 ) // ID = S 1.08
	ROM_REGION16_BE( 0x20000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_BYTE("s_evn_904.u3", 0x00000, 0x8000, CRC(a8f63245) SHA1(0ffdc6eb8ecad730440b0bfb2620fb00820e1aea) )
	ROM_LOAD16_BYTE("s_odd_c18.u2", 0x00001, 0x8000, CRC(4f033319) SHA1(fce228b1705b7156d4d01ef92b22a875d0f6f321) )
	ROM_LOAD16_BYTE("502.u4",       0x10000, 0x8000, CRC(553a5c8c) SHA1(ccb5460ff10766a5ca8008ae2cffcff794318108) ) // no odd rom
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT    COMPAT  MACHINE    INPUT      CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1991, diablo68,  0,        0,      diablo68,  diablo68,  diablo_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Diablo 68000 (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1991, diablo68a, diablo68, 0,      diablo68,  diablo68,  diablo_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Diablo 68000 (set 2)", MACHINE_SUPPORTS_SAVE )
SYST( 1991, diablo68b, diablo68, 0,      diablo68,  diablo68,  diablo_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Diablo 68000 (set 3)", MACHINE_SUPPORTS_SAVE )
SYST( 1991, diablo68c, diablo68, 0,      diablo68,  diablo68,  diablo_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Diablo 68000 (set 4)", MACHINE_SUPPORTS_SAVE )

SYST( 1991, scorpio68, 0,        0,      scorpio68, diablo68,  diablo_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Scorpio 68000", MACHINE_SUPPORTS_SAVE )



diablo1300.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Joakim Larsson Edstrom
/*

 Diablo Printer Series 1300 HyType II driver

 - Microprocessor based control logic for increased capacity and flexibility, plus provision for implementation of additional features.
 - Advanced servo design for improved efficiency and performance.
 - Rigid one piece cast aluminium frame to better maintain print quality, and reduce maintenance requirements.
 - Rugged highly stable carriage assembly for enhanced print position accuracy and reduced maintenance.
 - Plug-in interchangeable printed pircuit boards (PCB's), readily accessible for ease and simplicity of service, and implementation
   of options and interfaces.
 - Operator control of print hammer energy (Impression Control Switch) to shift the printer's internal hammer energy scale up
   for multiple carbon forms or down for smaller lighter print font styles.
 - 1/120 inch (.212 mm) horizontal spacing on command.
 - 88/82 or 96 character metal wheel
 - Optional interface access to directly address print hammer energy levels character by character.
 - Optional interface access to command ribbon advance.
 - Optional Paper Out Switch installation for either normal top or an optional bottom paper feed.
 - Optional Cover Open Switch installation.
 - Optional End Of Ribbon sensor installation for use with mu1tistrike carbon ribbon cartridges which are not the recirculating type.
 - Carriage Return takes max 300 mS
 - Tabulation can be set as right or left
 - Column spacing 60 pt/inch by operator or 120 pt/inch by controller
 - Print Line: 13.1 inch (332.74mm)
 - Paper Feed: 4 inch/sec
 - Dimensions: 591x365x400mm
 - Weight: 12Kg

Model performance
-----------------
                     1345A    1355HS    1355WP
Print Speed char/sec  45       55        40
Character Set         96       96        88/92/96

Configurations
--------------
There are many options that comes with the Diablo 1300 series and while many are mechanical the electronics are built up with cards
interconnected by a backplane. The backplane has well defined slots for each type of cards and there are also many external cables
between the cards, sensors and motors of the printer. The backplane consists of up to 8 female connectors for 56 signals card edge
connectors numbered A-H ordered in two rows, D,C,B,A on top with the fans to the left and H,G,F,E bellow. The signals are routed as
needed and the slots are NOT generic, a specific card goes in at a specific slot but can be interchanged to accommodate improved
performance or replaced for repair. Slots E and F are used for feature expansions such as serial, network cards etc.

The slots are populated as follows:

A: Logic #1 Command buffering and host signalling over a 50 pin ribbon cable. Sends commands to Logic #2 as needed
B: Logic #2 TTL CPU that interpretes commands from Logic #1 and controls all motors in the system
C: Servo
D: Carriage Power Amp
E: Optional 8080/Z80interface board, connects to Logic #1 board acting as host over the bus or the 50 pin ribbon cable
F: Optional slot with all signals of slot F
G: Transducer
H: Print Wheel Power Amp

In case the serial/IEEE488/network interface card is missing in the printer the host computer is supposed to drive which
connects to the printer over the 50 pin ribbon cable instead of the printer hosted interface card.

Logic #1 Card - printer command management
------------------------------------------
The board is marked 40505 and has an option field at the top and a J7 connector for the 50 pin ribbon cable. It produces the
system clock of 5 MHz that is used by the TTL CPU at Logic #2 Card,

 Identified IC:s
 ---------------
 1 74LS221       Dual Monostable multivibrator
 7 74LS74   7907-7908 Dual D-type pos edg trg flip-flops w clr and preset
 3 74LS367  7849 Non inverted 3 state outputs, 2 and 4 line enabled inputs
 1 7451     7849 Dual AND+OR invert gates
 1 7486     7849 Quad XOR gates
 3 74LS170  7906 4 by 4 register file
 4 8837     7736
 2 7408     7906 Quad AND gales
 2 74LS42   7906 BCD to decimal decoder
 1 7426     7906 Quad NAND gates
 1 74LS174  7836 Hex D-type flip flops
 1 7432     7901 QUAD OR gates
 2 74LSI07  7906 Dual J-K M/S flip flops w clear
 1 7404     7901 Hex Inverters
 5 75452    7840-7901
 2 7400     7849 Quad NAND gates

Logic #2 Card - printer command execution (TTL CPU)
---------------------------------------------------
The board is marked 40510 and has no connectors except the 56 signal bus edge connector

 Identified IC:s
 ---------------
 4 7400     7848-7902 Quad NAND gates
 3 74LS04   7850 Hex Inverters
 1 7408     7901 Quad AND gales
 1 7410     7840 Tripple 3-input NAND gates
 2 7453     7903 Expandable 4 wide AND+OR invert gates
 1 74LS74   7908 Dual D-type pos edg trg flip-flops w clr and preset
 2 74LS83   7901 4 bit binary full addres with fast carry
 4 74S289        4x16 bit RAM
 1 74107         Dual J-K M/S flip flops w clear
 1 74LS155  7731 1/2/3 to 4/8 lines decoder nwih totem pole ouputs
 2 74161    7904 Synchronous binary 4 bit counter
 4 74LS259  7906 8 bit addressable latches
 4 74298    7849 Quad 2 input mux with storage
 1 74367    7840 Non inverted 3 state outputs, 2 and 4 line enabled inputs
 1 74LS174       Hex D-type flip flops

RS232 Serial Interface Card
----------------------------
The serial interface card is z80 based and marked DIABLO-1300-V24

 Identified ICs:
 ---------------
 1 Z80-CPU 7904 Zilog CPU
 1 TMS2716 7906 2KB EPROM
 1 AM9551  7850 8251 USART
 2 Z80-PIO 7852 Zilog Paralell IO interface
10 74367   7845 Non inverted 3 state outputs, 2 and 4 line enabled inputs
 2 UPB7400 7845 Quad NAND gates
 3 7432N   7832 QUAD OR gates
 1 1489    7841 Quad line receivers
 1 1488    7823 Quad line tranceivers
 1 74163   7827 Synchronous 4 bit counters
 2 7493    7822 4 bit binary counters
 2 7404    7849 Hex inverters
 1 7410    7849 Tripple 3-input NAND gates
 2 2114         1024 x 4 bit SRAM
 1 9602    7423 Dual retriggable resetable one shots


 Address decoding
 ----------------
 Z80 A0 30 -> 74367 -> Z80 PIO* Port A/B     6
 Z80 A1 31 -> 74367 -> Z80 PIO* Control/Data 5
 (Z80 A5 35 -> 74367) OR (Z80 IORQ 20) -> Z80 PIO1 CE* 4
 (Z80 A4 34 -> 74367) OR (Z80 IORQ 20) -> Z80 PIO2 CE* 4
*/

#include "emu.h"
#include "cpu/diablo/diablo1300.h"


namespace {

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

	void diablo1300(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;

	void diablo1300_map(address_map &map) ATTR_COLD;
	void diablo1300_data_map(address_map &map) ATTR_COLD;
};

void diablo1300_state::diablo1300_map(address_map &map)
{
	map(0x0000, 0x01ff).rom();
}

void diablo1300_state::diablo1300_data_map(address_map &map)
{
	map(0x00, 0x1f).ram();
}

static INPUT_PORTS_START( diablo1300 )
INPUT_PORTS_END

void diablo1300_state::machine_start()
{
}

void diablo1300_state::machine_reset()
{
}

void diablo1300_state::diablo1300(machine_config &config)
{
	/* basic machine hardware */
	DIABLO1300(config, m_maincpu, XTAL(1'689'600));
	m_maincpu->set_addrmap(AS_PROGRAM, &diablo1300_state::diablo1300_map);
	m_maincpu->set_addrmap(AS_DATA, &diablo1300_state::diablo1300_data_map);
}

ROM_START( diablo )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_16BIT )
	ROM_DEFAULT_BIOS("diablo1300")

	ROM_SYSTEM_BIOS(0, "diablo1300", "Diablo Printer Series 1300 14510-xx CPU microcode") // Recreated 82S115 binaries using Jeffs documentation
	ROMX_LOAD ("diablo1300.odd",  0x0001, 0x200, CRC (5e295350) SHA1 (6ea9a22b23b8bab93ae57671541d65dba698c722), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD ("diablo1300.even", 0x0000, 0x200, CRC (85562eb1) SHA1 (9335eeeabdd37255d6ffee153a027944a4519126), ROM_SKIP(1) | ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "diablo1355", "Diablo Printer 1355WP 14510-xx CPU microcode")  // Dumped 82S115 using Arduino
	ROMX_LOAD ("13067-36.bin", 0x0001, 0x200, CRC (b8bf070f) SHA1 (c51f9f4009a771b1d0e9c948592c988b1f4e840f), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD ("13066-35.bin", 0x0000, 0x200, CRC (73698143) SHA1 (5d2b3e956ae0d2b606d081a242fc64928e68687d), ROM_SKIP(1) | ROM_BIOS(1))

	ROM_REGION(0x1000, "trom", 0)
	/* Table ROM, holds data for specific mechnical configuration of the printer engine, such as the number of characters and hammer energy for each character */
	ROM_LOAD( "55wp92ch.bin",  0x0000, 0x200,   CRC(58ba7913) SHA1(968e87318b49ad05a66a8148e9048bfbeba2a97f) ) // Dumped 82S115 using Arduino

ROM_END

} // anonymous namespace


//   YEAR  NAME    PARENT  COMPAT  MACHINE     INPUT       CLASS             INIT        COMPANY               FULLNAME
COMP(1976, diablo, 0,      0,      diablo1300, diablo1300, diablo1300_state, empty_init, "Diablo Systems Inc", "Diablo HyType II Series 1300 CPU", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



diamond.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Novag Diamond (model 9303) / Diamond II (model 38602)

Hardware notes:

Diamond:
- PCB label: 100165 REV B
- Hitachi H8/325 MCU (mode 2), 26.601712MHz XTAL
- 32KB EPROM (TC57256AD-12), 128KB SRAM (HM628128ALP-7)
- LCD with 6 7segs and custom segments (same as Novag VIP)
- RJ-12 port for Novag Super System (always 9600 baud)
- piezo, 16 LEDs, button sensors chessboard

Diamond II:
- PCB label: 100208 REV B
- Hitachi H8/325 MCU (mode 2), 32MHz XTAL
- 128KB EPROM (27C010), 128KB SRAM (KM681000BLG-7)
- Sapphire II LCD instead of VIP, the rest is the same as Diamond

Diamond II MCU and EPROM are the same as Sapphire II. MCU pin P62 determines
which hardware it runs on, see sapphire.cpp for Sapphire II.

TODO:
- Novag Super System peripherals don't work due to serial clock drift, baud rate
  differs a bit between host and client, m6801 serial emulation issue (to work
  around it, underclock diamond to exactly 26.4192MHz, diamond2 to 31.9488MHz)
- it does a cold boot at every reset, so nvram won't work properly unless MAME
  adds some kind of auxillary autosave state feature at power-off

BTANB:
- diamond has the same AT level bug as sapphire (it works fine in diamond2)

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

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/h8/h8325.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "novag_diamond.lh"
#include "novag_diamond2.lh"


namespace {

class diamond_state : public driver_device
{
public:
	diamond_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_memory(*this, "memory"),
		m_nvram(*this, "nvram", 0x20000, ENDIANNESS_BIG),
		m_rambank(*this, "rambank"),
		m_rombank(*this, "rombank"),
		m_board(*this, "board"),
		m_led_pwm(*this, "led_pwm"),
		m_lcd_pwm(*this, "lcd_pwm"),
		m_dac(*this, "dac"),
		m_rs232(*this, "rs232"),
		m_inputs(*this, "IN.%u", 0),
		m_out_lcd(*this, "s%u.%u", 0U, 0U)
	{ }

	void diamond(machine_config &config);
	void diamond2(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(power_switch);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD { set_power(true); }

private:
	// devices/pointers
	required_device<h8325_device> m_maincpu;
	memory_view m_memory;
	memory_share_creator<u8> m_nvram;
	required_memory_bank m_rambank;
	optional_memory_bank m_rombank;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_led_pwm;
	required_device<pwm_display_device> m_lcd_pwm;
	required_device<dac_1bit_device> m_dac;
	required_device<rs232_port_device> m_rs232;
	required_ioport_array<3> m_inputs;
	output_finder<4, 10+6> m_out_lcd;

	bool m_power = false;
	u8 m_inp_mux = 0;
	u8 m_led_select = 0;
	u8 m_lcd_sclk = 0;
	u32 m_lcd_data = 0;
	u8 m_lcd_segs2 = 0;

	void diamond_map(address_map &map) ATTR_COLD;
	void diamond2_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void standby(int state);
	void set_power(bool power);
	u8 power_r();

	void lcd_pwm_w(offs_t offset, u8 data);
	void update_lcd();
	void d1_lcd_data_w(u8 data);
	void d2_lcd_data_w(u8 data);

	u8 read_buttons();
	u8 read_board();
	u8 input_r();
	u8 input2_r();
	void cs_w(u8 data);
	void bank_w(u8 data);
	void update_leds();
	template <int N> void leds_w(u8 data);
	void p4_w(u8 data);
	void p5_w(u8 data);
};

void diamond_state::machine_start()
{
	m_out_lcd.resolve();

	if (m_rombank)
		m_rombank->configure_entries(0, 4, memregion("eprom")->base(), 0x8000);

	m_rambank->configure_entries(0, 4, m_nvram, 0x8000);
	m_memory.select(0);

	// register for savestates
	save_item(NAME(m_power));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_led_select));
	save_item(NAME(m_lcd_sclk));
	save_item(NAME(m_lcd_data));
	save_item(NAME(m_lcd_segs2));
}



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

// power

void diamond_state::standby(int state)
{
	// clear display
	if (state)
	{
		m_lcd_pwm->clear();
		m_led_pwm->clear();
	}
}

void diamond_state::set_power(bool power)
{
	// power switch is tied to IRQ2
	m_maincpu->set_input_line(INPUT_LINE_IRQ2, power ? ASSERT_LINE : CLEAR_LINE);
	m_power = power;
}

INPUT_CHANGED_MEMBER(diamond_state::power_switch)
{
	if (newval)
		set_power(bool(param));
}

u8 diamond_state::power_r()
{
	// P66: power switch (IRQ2)
	return m_power ? 0xbf : 0xff;
}


// LCD

void diamond_state::lcd_pwm_w(offs_t offset, u8 data)
{
	m_out_lcd[offset & 0x3f][offset >> 6] = data;
}

void diamond_state::update_lcd()
{
	for (int i = 0; i < 4; i++)
	{
		const u8 shift = m_lcd_pwm->width() & 0x18;

		// LCD common is analog (voltage level)
		const u8 com = population_count_32(m_lcd_data >> (shift + (i * 2)) & 3);
		u16 segs = m_lcd_data & ((1 << shift) - 1);
		segs |= m_lcd_segs2 << shift; // diamond

		m_lcd_pwm->write_row(i, (com == 0) ? segs : (com == 2) ? ~segs : 0);
	}
}

void diamond_state::d1_lcd_data_w(u8 data)
{
	// P60,P61: same as diamond2
	d2_lcd_data_w(data);

	// P62: 3*14015B R
	if (data & 4)
		m_lcd_data = 0;

	// 2 more LCD segments after common
	m_lcd_segs2 = m_lcd_data >> 16 & 3;
	update_lcd();
}

void diamond_state::d2_lcd_data_w(u8 data)
{
	// P60: 3*14015B C (chained)
	if (data & 1 && !m_lcd_sclk)
	{
		// P61: 14015B D, outputs to LCD
		m_lcd_data = m_lcd_data << 1 | BIT(data, 1);
		update_lcd();
	}
	m_lcd_sclk = data & 1;
}


// misc

u8 diamond_state::read_buttons()
{
	u8 data = 0;

	for (int i = 0; i < 3; i++)
		if (m_inp_mux & m_inputs[i]->read())
			data |= 1 << i;

	return ~data;
}

u8 diamond_state::read_board()
{
	u8 data = 0;

	// priority encoded (either a 74148 on d1, or 2*7421 on d2)
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= (count_leading_zeros_32(m_board->read_rank(i)) - 24) ^ 8;

	return ~data;
}

u8 diamond_state::input_r()
{
	// P71-P73: multiplexed inputs
	return (read_buttons() & read_board()) << 1 | 0xf1;
}

u8 diamond_state::input2_r()
{
	// P27: chessboard active (P47 on d2)
	return read_board() << 4 | 0x7f;
}

void diamond_state::cs_w(u8 data)
{
	// P41: ROM/RAM CS (P63 on d2)
	m_memory.select(data & 1);
}

void diamond_state::bank_w(u8 data)
{
	// P64,P65: ROM/RAM bankswitch
	const u8 bank = data >> 4 & 3;
	m_rambank->set_entry(bank);

	// only diamond2 has ROM banks
	if (m_rombank)
		m_rombank->set_entry(bank);

	// other: LCD (see above)
}

void diamond_state::update_leds()
{
	m_led_pwm->matrix(m_led_select, m_inp_mux);
}

template <int N>
void diamond_state::leds_w(u8 data)
{
	// P63/P41,P70: select LEDs
	const u8 mask = 1 << N;
	m_led_select = (m_led_select & ~mask) | ((data & 1) ? 0 : mask);
	update_leds();
}

void diamond_state::p4_w(u8 data)
{
	// P40: speaker out
	m_dac->write(data & 1);

	// P42-P45: input mux low
	m_inp_mux = (m_inp_mux & 0xf0) | (~data >> 2 & 0xf);
	update_leds();
}

void diamond_state::p5_w(u8 data)
{
	// P52-P55: input mux high
	m_inp_mux = (m_inp_mux & 0x0f) | (~data << 2 & 0xf0);
	update_leds();
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void diamond_state::diamond_map(address_map &map)
{
	map(0x8000, 0xffff).view(m_memory);
	m_memory[0](0x8000, 0xffff).rom().region("eprom", 0);
	m_memory[1](0x8000, 0xffff).bankrw(m_rambank);

	map(0xff90, 0xff9f).unmaprw(); // reserved for H8 registers
	map(0xffb0, 0xffff).unmaprw(); // "
}

void diamond_state::diamond2_map(address_map &map)
{
	diamond_map(map);
	m_memory[0](0x8000, 0xffff).bankr(m_rombank);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( diamond )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Go")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Info")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Hint")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Training")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("Next Best")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Set Level")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_J) PORT_NAME("Clear / Clear Board")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_K) PORT_NAME("New Game")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Take Back / Auto/Demo")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Trace Forward / Autoclock")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Easy / Replay")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Random / Human")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Restore / Video")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Book Select / Print Moves")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Sound / Print Game")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Referee / Print Board")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Color")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Verify / Setup")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Pro-op Print / King")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Pro-op Priority / Queen")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Pro-op Delete / Rook")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Pro-op Save / Bishop")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Load Game / Knight")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Save Game / Pawn")

	PORT_START("POWER")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_ON) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(diamond_state::power_switch), 1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(diamond_state::power_switch), 0)
INPUT_PORTS_END

static INPUT_PORTS_START( diamond2 )
	PORT_INCLUDE( diamond )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("Option 1/2")

	PORT_MODIFY("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Next Best / Take Back / Print Board")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Trace Forward / Print Game")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Random / Video")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Restore / Human")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Book Select / Auto/Demo")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Sound / Auto Clock")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Referee / Print Moves")

	PORT_MODIFY("IN.2")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Verify / Setup / Rating")

	PORT_START("BATT")
	PORT_CONFNAME( 0x80, 0x80, "Battery Status" )
	PORT_CONFSETTING(    0x00, "Low" )
	PORT_CONFSETTING(    0x80, DEF_STR( Normal ) )
	PORT_BIT(0x7f, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void diamond_state::diamond(machine_config &config)
{
	// basic machine hardware
	H8325(config, m_maincpu, 26.601712_MHz_XTAL);
	m_maincpu->set_mode(2);
	m_maincpu->set_addrmap(AS_PROGRAM, &diamond_state::diamond_map);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(h8325_device::nvram_set_battery));
	m_maincpu->standby_cb().append(FUNC(diamond_state::standby));
	m_maincpu->write_sci_tx<0>().set(m_rs232, FUNC(rs232_port_device::write_txd));
	m_maincpu->read_port2().set(FUNC(diamond_state::input2_r));
	m_maincpu->write_port4().set(FUNC(diamond_state::p4_w));
	m_maincpu->write_port4().append(FUNC(diamond_state::cs_w)).bit(1);
	m_maincpu->write_port5().set(FUNC(diamond_state::p5_w));
	m_maincpu->read_port6().set(FUNC(diamond_state::power_r));
	m_maincpu->write_port6().set(FUNC(diamond_state::bank_w));
	m_maincpu->write_port6().append(FUNC(diamond_state::leds_w<1>)).bit(3);
	m_maincpu->write_port6().append(FUNC(diamond_state::d1_lcd_data_w));
	m_maincpu->read_port7().set(FUNC(diamond_state::input_r));
	m_maincpu->write_port7().set(FUNC(diamond_state::leds_w<0>)).bit(0);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(250));
	//m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_lcd_pwm).set_size(4, 10);
	m_lcd_pwm->output_x().set(FUNC(diamond_state::lcd_pwm_w));
	m_lcd_pwm->set_bri_levels(0.05);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/3, 606/3);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_led_pwm).set_size(2, 8);
	config.set_default_layout(layout_novag_diamond);

	// rs232 (configure after video)
	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set(m_maincpu, FUNC(h8325_device::sci_rx_w<0>));

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void diamond_state::diamond2(machine_config &config)
{
	diamond(config);

	// basic machine hardware
	m_maincpu->set_clock(32_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &diamond_state::diamond2_map);
	m_maincpu->read_port2().set_ioport("BATT").invert();
	m_maincpu->read_port4().set(FUNC(diamond_state::input2_r));
	m_maincpu->write_port4().set(FUNC(diamond_state::p4_w));
	m_maincpu->write_port4().append(FUNC(diamond_state::leds_w<1>)).bit(1); // pin swapped with cs_w

	// P62 input is forced low (0 = Diamond II, 1 = Sapphire II)
	m_maincpu->read_port6().set(FUNC(diamond_state::power_r)).mask(0xfb);
	m_maincpu->write_port6().set(FUNC(diamond_state::bank_w));
	m_maincpu->write_port6().append(FUNC(diamond_state::cs_w)).bit(3);
	m_maincpu->write_port6().append(FUNC(diamond_state::d2_lcd_data_w));

	// video hardware
	m_lcd_pwm->set_width(16);

	screen_device &screen(*subdevice<screen_device>("screen"));
	screen.set_size(1920/3, 671/3);
	screen.set_visarea_full();

	config.set_default_layout(layout_novag_diamond2);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( diamond ) // ID = DIAMOND 1.01
	ROM_REGION16_BE( 0x8000, "maincpu", 0 )
	ROM_LOAD("novag_9303-010052_6433258b47p.u6", 0x0000, 0x8000, CRC(cbc544ee) SHA1(43bc09a296f23f23f6a2656a12a9f8d4519fec10) )

	ROM_REGION16_BE( 0x8000, "eprom", 0 )
	ROM_LOAD("bk301_26601.u4", 0x0000, 0x8000, CRC(648ebe8f) SHA1(2883f962a0bf17426fd809b9f2c01ce3dec0df1b) )

	ROM_REGION( 36256, "screen", 0 )
	ROM_LOAD("nvip.svg", 0, 36256, CRC(3373e0d5) SHA1(25bfbf0405017388c30f4529106baccb4723bc6b) )
ROM_END

ROM_START( diamond2 ) // ID = DIAMOND II 1.02
	ROM_REGION16_BE( 0x8000, "maincpu", 0 )
	ROM_LOAD("ihp_7600109_hd6433258c67f.u1", 0x0000, 0x8000, CRC(10970123) SHA1(8f72e756915de6569c3936140c775d24730e9065) )

	ROM_REGION16_BE( 0x20000, "eprom", 0 )
	ROM_LOAD("32dsii_060597.u3", 0x00000, 0x20000, CRC(c1be39c6) SHA1(e33ee655bd342fed736c2b2093a89752e695aff3) )

	ROM_REGION( 72533, "screen", 0 )
	ROM_LOAD("sapphire2.svg", 0, 72533, CRC(34944b61) SHA1(4a0536ac07790cced9f9bf15522b17ebc375ff8a) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1994, diamond,  0,      0,      diamond,  diamond,  diamond_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Diamond", MACHINE_SUPPORTS_SAVE )

SYST( 1997, diamond2, 0,      0,      diamond2, diamond2, diamond_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Diamond II", MACHINE_SUPPORTS_SAVE )



didact.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Joakim Larsson Edstrom
/*
 *
 * History of Didact and Esselte Studium
 *--------------------------------------
 * Didact Laromedelsproduktion was started in Linkoping in Sweden by Anders Andersson, Arne Kullbjer and
 * Lars Bjorklund. They constructed a series of microcomputers for educational purposes such as "Mikrodator 6802",
 * Esselte 100 and the Candela computer for the swedish schools to educate the students in assembly programming
 * and BASIC for electro mechanical applications such as stepper motors, simple process control, buttons
 * and LED:s. Didact designs were marketed by Esselte Studium to the swedish schools. Late designs like the
 * "Modulab v2" appears to have been owned or licensed to Esselte and enhanced with more modular monitor routines
 * in a project driven by Alf Karlsson.
 *
 * The Esselte 1000 was an educational package based on Apple II plus software and literature
 * but the relation to Didact is at this point unknown so it is probably a pure Esselte software production.
 *
 * Misc links about the boards supported by this driver.
 *-----------------------------------------------------
 * http://elektronikforumet.com/forum/viewtopic.php?f=11&t=51424
 * http://kilroy71.fastmail.fm/gallery/Miscellaneous/20120729_019.jpg
 * http://elektronikforumet.com/forum/download/file.php?id=63988&mode=view
 * http://elektronikforumet.com/forum/viewtopic.php?f=2&t=79576&start=150#p1203915
 *
 *  TODO:
 *  Didact designs:    mp68a, md6802, Modulab
 * ------------------------------------------
 *  - Add PCB layouts   OK     OK     OK
 *  - Dump ROM:s,       OK     OK     OK
 *  - Keyboard          OK     OK     OK
 *  - Display/CRT       OK     OK     OK
 *  - Clickable Artwork RQ     RQ     OK
 *  - Sound             NA     NA
 *  - Cassette i/f
 *  - Expansion bus
 *  - Expansion overlay
 *  - Interrupts        OK
 *  - Serial                   XX
 *   XX = needs debug
 *********************************************/

#include "emu.h"
#include "cpu/m6800/m6800.h" // For all boards
#include "machine/6821pia.h" // For all boards
#include "machine/74145.h"   // For the md6802
#include "video/dm9368.h"    // For the mp68a
#include "machine/ins8154.h" // For the modulab
#include "machine/mm74c922.h"// For the modulab
#include "machine/rescap.h"  // For the modulab

// Features
#include "imagedev/cassette.h"
#include "bus/rs232/rs232.h"
#include "screen.h"
#include "speaker.h"

// Generated artwork includes
#include "mp68a.lh"
#include "md6802.lh"
#include "modulab.lh"

//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

#define LOG_SETUP    (1U << 1)
#define LOG_READ     (1U << 2)
#define LOG_DISPLAY  (1U << 3)
#define LOG_KEYBOARD (1U << 4)

//#define VERBOSE (LOG_KEYBOARD)
//#define LOG_OUTPUT_FUNC printf
#include "logmacro.h"

#define LOGSETUP(...)   LOGMASKED(LOG_SETUP,    __VA_ARGS__)
#define LOGREAD(...)    LOGMASKED(LOG_READ,     __VA_ARGS__)
#define LOGDISPLAY(...) LOGMASKED(LOG_DISPLAY,  __VA_ARGS__)
#define LOGKBD(...)     LOGMASKED(LOG_KEYBOARD, __VA_ARGS__)

#ifdef _MSC_VER
#define FUNCNAME __func__
#else
#define FUNCNAME __PRETTY_FUNCTION__
#endif


namespace {

#define PIA1_TAG "pia1"
#define PIA2_TAG "pia2"
#define PIA3_TAG "pia3"
#define PIA4_TAG "pia4"
#define MM74C923_TAG "74c923"

/* Didact base class */
class didact_state : public driver_device
{
public:
	didact_state(const machine_config &mconfig, device_type type, const char * tag)
		: driver_device(mconfig, type, tag)
		, m_cass(*this, "cassette")
		, m_io_lines(*this, "LINE%u", 0U)
		, m_lines{ 0, 0, 0, 0 }
		, m_rs232(*this, "rs232")
		, m_led(*this, "led1")
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(trigger_reset);
	DECLARE_INPUT_CHANGED_MEMBER(trigger_shift);

protected:
	virtual void machine_start() override { m_led.resolve(); }

	optional_device<cassette_image_device> m_cass;
	required_ioport_array<5> m_io_lines;
	uint8_t m_lines[4]{};
	uint8_t m_reset = 0;
	uint8_t m_shift = 0;
	optional_device<rs232_port_device> m_rs232;
	output_finder<> m_led;
};


/*  _____________________________________________________________________________________________   ___________________________________________________________________________
 * |The Didact Mikrodator 6802 CPU board by Lars Bjorklund 1983                            (  ) |  |The Didact Mikrodator 6802 TB16 board by Lars Bjorklund 1983               |
 * |                                                                                     +----= |  |             +-|||||||-+                                         ______    |
 * |                                                                                     |    = |  | CA2 Tx      |terminal |                                        |  ()  |   |
 * |                                                                                     |    = |  | PA7 Rx      +---------+               +----------+  C1nF,<=R18k|      |   |
 * |     Photo of CPU board mainly covered by TB16 Keypad/Display board                  +--- = |  | CA1 DTR               +-----------+   |          |   CB2->CB1  |  E   |   |
 * |                                                                                            |  |               PA4-PA6 |           | 1 | BCD      |    +----+   |  X   |   |
 * |                                                                                            |  |               ------->| 74LS145   |   | digit 5  |    |LS  |   |  P   |   |
 * |                                                                                            |  |                       +-----------+   |----------|    | 122|   |  A   |   |
 * |                                                                                     +-----=|  |                                   |   |          |    |    |   |  N   |   |
 * |                                                                          +-------+  |     =|  |------ +--------+                  | 2 | BCD      |    |    |   |  S   |   |
 * |                                                                          |       |  |     =|  | RES*  | SHIFT  |  LED( )          |   | digit 4  |    |    |   |  I   |   |
 * |                                                                          |       |  |     =|  |       |  '*'   |    CA2           v   |----------|    +----+   |  O   |   |
 * |                                                                          | 6821  |  |     =|  |   PA3 |PA7 PA2 | PA1      PA0         |          |        +----|  N   |   |
 * |                                                                          | PIA   |  |     =|  |----|--+-----|--+--|-----+--|---+    3 |          |    PB0-|LS  |      |   |
 * |                                                                          |       |  |     =|  |    v  |     v  |  v     |  v   |      | BCD      |     PB7| 244|  C   |   |
 * |                                                                          |       |  |     =|  | ADR   | RUN    | SST    | CON  | 1    | digit 3  |    --->|    |  O   |   |
 * |                                                                          |       |  |     =|  |  0    |  4     |  8     |  C   |      |----------|        |    |  N   |   |
 * |                                                                          |       |  |     =|  |-------+--------+--------+------+      |          |<-------|    |  N   |   |
 * |                                                                          |       |  |     =|  |       |        |        |      |    4 |          |        +----|  E   |   |
 * |                                                                          |       |  |     =|  | STA   | BPS    | USERV  |      | 2    | BCD      |             |  C   |   |
 * |                                                                          |       |  |     =|  |  1    |  5     |  9     |  D   |      | digit 2  |             |  T   |   |
 * |                                                                          |       |  |     =|  |-------+--------+--------+------+      |----------|             |  O   |   |
 * |                                                                          |       |  |     =|  |       |        |        |      |      |          |             |  R   |   |
 * |                                                                          |       |  |     =|  | EXF   | EXB    | MOV    | LOAD | 3  5 | BCD      |             |      |   |
 * |                                                                          |       |  |     =|  |  2    |  6     |  A     |  E   |      | digit 1  |             |      |   |
 * |                                                                          +-------+  |     =|  |-------+--------+--------+------+      |----------|             |      |   |
 * |                                                                                     |     =|  |       |        |        |      |      |          |             |      |   |
 * |                                                                                     +-----=|  | CLR   |  SP    | USERJ  | FLAG | 4  6 | BCD      |             |      |   |
 * |                                                                                            |  |  3    |  7     |  B     |  F   |      | digit 0  |             |  ()  |   |
 * |                                                                                            |  |-------+--------+--------+------+      +----------+             +------+   |
 * |                                                                                            |  |                                                                           |
 * |                                                                                            |  |                                                                           |
 * |____________________________________________________________________________________________|  |___________________________________________________________________________|
 */

/* Mikrodator 6802 driver class */
class md6802_state : public didact_state
{
public:
	md6802_state(const machine_config &mconfig, device_type type, const char * tag)
		: didact_state(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_tb16_74145(*this, "tb16_74145")
		, m_pia1(*this, PIA1_TAG)
		, m_pia2(*this, PIA2_TAG)
		, m_7segs(*this, "digit%u", 0U)
		, m_segments(0)
	{ }

	void md6802(machine_config &config);

protected:
	uint8_t pia2_kbA_r();
	void pia2_kbA_w(uint8_t data);
	uint8_t pia2_kbB_r();
	void pia2_kbB_w(uint8_t data);
	void pia2_ca2_w(int state);

	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

	void md6802_map(address_map &map) ATTR_COLD;

private:
	required_device<m6802_cpu_device> m_maincpu;
	required_device<ttl74145_device> m_tb16_74145;
	required_device<pia6821_device> m_pia1;
	required_device<pia6821_device> m_pia2;
	output_finder<6> m_7segs;
	uint8_t m_segments;
};

/* Keyboard */
uint8_t md6802_state::pia2_kbA_r()
{
	uint8_t ls145;
	uint8_t pa = 0xff;

	// Read out the selected column
	ls145 = m_tb16_74145->read() & 0x0f;

	// read out the artwork, line04 is handled by the timer
	for (unsigned i = 0U; 4U > i; ++i)
	{
		m_lines[i] = m_io_lines[i]->read();

		// Mask out those rows that has a button pressed
		pa &= ~(((~m_lines[i] & ls145) != 0) ? (1 << i) : 0);
	}

	if (m_shift)
	{
		pa &= 0x7f;   // Clear shift bit if button being pressed (PA7) to ground (internal pullup)
		LOGKBD("SHIFT is pressed\n");
	}

	// Serial IN - needs debug/verification
	pa &= (m_rs232->rxd_r() != 0 ? 0xff : 0x7f);

	return pa;
}

/* Pull the cathodes low enabling the correct digit and lit the segments held by port B */
void md6802_state::pia2_kbA_w(uint8_t data)
{
//  LOG("--->%s(%02x)\n", FUNCNAME, data);

	uint8_t const digit_nbr((data >> 4) & 0x07);
	m_tb16_74145->write(digit_nbr);
	if (digit_nbr < 6)
		m_7segs[digit_nbr] = m_segments;
}

/* PIA 2 Port B is all outputs to drive the display so it is very unlikely that this function is called */
uint8_t md6802_state::pia2_kbB_r()
{
	LOG("Warning, trying to read from Port B designated to drive the display, please check why\n");
	logerror("Warning, trying to read from Port B designated to drive the display, please check why\n");
	return 0;
}

/* Port B is fully used outputting the segment pattern to the display */
void  md6802_state::pia2_kbB_w(uint8_t data)
{
//  LOG("--->%s(%02x)\n", FUNCNAME, data);

	/* Store the segment pattern but do not lit up the digit here, done by pulling the correct cathode low on Port A */
	m_segments = bitswap<8>(data, 0, 4, 5, 3, 2, 1, 7, 6);
}

void md6802_state::pia2_ca2_w(int state)
{
	LOGKBD("--->%s(%02x) LED is connected through resisitor to +5v so logical 0 will lit it\n", FUNCNAME, state);
	m_led = state ? 0 :1;

	// Serial Out - needs debug/verification
	m_rs232->write_txd(state);

	m_shift = !state;
}

void md6802_state::machine_start()
{
	LOG("--->%s()\n", FUNCNAME);

	didact_state::machine_start();
	m_7segs.resolve();

	save_item(NAME(m_reset));
	save_item(NAME(m_shift));
}

void md6802_state::machine_reset()
{
	LOG("--->%s()\n", FUNCNAME);
	m_maincpu->reset();
}

// This address map is traced from schema
void md6802_state::md6802_map(address_map &map)
{
	map(0x0000, 0x07ff).ram().mirror(0x1800);
	map(0xa000, 0xa003).rw(m_pia1, FUNC(pia6821_device::read), FUNC(pia6821_device::write)).mirror(0x1ffc);
	map(0xc000, 0xc003).rw(m_pia2, FUNC(pia6821_device::read), FUNC(pia6821_device::write)).mirror(0x1ffc);
	map(0xe000, 0xe7ff).rom().mirror(0x1800).region("maincpu", 0xe000);
}

/*
 *  ___________________________________________________________________________________________________________           _____________________________________________________
 * | The Didact Mp68A CPU board, by Anders Andersson 1979                                                      |         |The Didact Mp68A keypad/display  PB6   +oooo+        |
 * |                  +------+ +-------+     +--+                                                              |         |  by Anders Andersson 1979  +-------+  |cass|        |
 * |                  | 7402 | | 74490 |     |  |      +-------+               +--+                            |         |                    +--+    | 9368  |  +----+    +--+|
 * |       +-------+  +------+ +-------+     |  |      |       |               |  |                            |         |+-------+    2x5082-|B |    +-------+            |  ||
 * |       |       |    2112   2112          |  |      | EXP   |               |  |                            |         || 74132 |       7433|CD| 145  PA0-PA3            |E ||
 * |       | ROM   |    +--+   +--+          +--+      | ANS   |               |P |                            |         |+-------+           |DI| +--+               132  |X ||
 * |       | 7641  |    |  |   |  |                    | ION   |               |I |                            |         |+------+------+     | S| |  |               +--+ |P ||
 * |       |       |    |A |   |B |       +-----+      | BUSES |               |A |                            |         ||      |SHIFT |     | P| |  | PA4-PA6       |  | |A ||
 * |       | 512x8 |    |  |   |  |       |     |      | (2 x) |               |  |                            |         || RES  |(led) |     +--+ |  |               |  | |N ||
 * |       |       |    +--+   +--+       |     |      | FOR   |               |A |                            |         ||      |  *   |          +--+               |  | |S ||
 * |       +-------+    RAMS 4x256x4      |     |      |       |               |  |                            |         |+------+------+------+------+               +--+ |I ||
 * |     ROMS 2x512x8   2112   2112       |     |      | KEY   |               |E |                            |         ||      |      |      |      |                    |O ||
 * |       +-------+    +--+   +--+       |CPU  |      | BOARD | +------+      |X |                            |         || ADR  | RUN  | SST  | REG  |                    |N ||
 * |       |       |    |  |   |  |       |6800 |      |       | |      |      |P |                            |         ||  0   |  4   |  8   |  C   |                    |  ||
 * |       | ROM   |    |A |   |B |       |     |      | AND   | |      |      |A |                            |         |+------+------+------+------+                    |C ||
 * |       | 7641  |    |  |   |  |       |     |      |       | |      |      |N |                            |         ||      |      |      |      |                    |O ||
 * |       |       |    +--+   +--+       |     |      | I/O   | | 6820 |      |S |                            |         || STA  | STO  | BPR  | BPS  |                    |N ||
 * |       | 512x8 |    512 bytes RAM     |     |      | BOARDS| | PIA  |      |I |                            |         ||  1   |  5   |  9   |  D   |                    |N ||
 * |       +-------+                      |     |      |       | |  #1  |      |O |                         +-----+      |+------+------+------+------+           +------+ |E ||
 * |     1024 bytes ROM                   |     |      |       | |      |      |N |                         |     |      ||      |      |      |      |           |      | |C ||
 * |                                      +-----+      |       | |      |      |  |                  PIA A  |    |       || EXF  | EXB  | MOV  | PRM  |           |      | |T ||
 * |        7402  7412                                 |       | |      |      |B |                EXPANSION|    |       ||  2   |  6   |  A   |  E   |           |      | |O ||
 * |        +--+  +--+                                 |       | |      |      |U |                CONNECTOR|    |       |+------+------+------+------+           | 6820 | |R ||
 * |        |  |  |  |                                 |       | |      |      |S |                         |   _|       ||      |      |      |      |           | PIA  | |  ||
 * |        |  |  |  |                                 |       | |      |      |  |                     J4  |  |         || CLR  | REL  | REC  | PLA  |           |  #2  | |  ||
 * |        |  |  |  |                                 |       | +------+      |  |                         |  |_        ||  3   |  7   |  B   |  F   |           |      | |  ||
 * |        +--+  +--+         +--------+              |       |               |  |                         |    |       |+------+------+------+------+           |      | |  ||
 * |                  +-+      | 96LS02 |              |       |               |  |                         |    |       | +-------+ +-------+  +------+          |      | |  ||
 * |       R * * * R  |T|      +--------+              |       |               |  |                         |    |       | | 74148 | | 74148 |  | 7400 |          |      | |  ||
 * |       O  X    A  |R|                              |       |               |  |                         |    |       | +-------+ +-------+  +------+          |      | +--+|
 * |       M * * * M  |M|  Oscillator circuits         +-------+               +--+                         |     |      |                PB3    PB0-PB2          |      |     |
 * |                  |_|                               J1   J2                 J3                          +-----+      |       +---------+                      +------+  J1 |
 * |____________________________________________________________________________________________________________|        |______ |  _|||_  |___________________________________|
 *
 */
/* Didact mp68a driver class */

// The real mp68a hardware was designed with 6820 and not 6821.
// They are functional equivalents BUT has different electrical characteristics.
// 2019-07-27 Cassette added: saves ok, load is unreliable, probably an original design problem.
class mp68a_state : public didact_state
{
public:
	mp68a_state(const machine_config &mconfig, device_type type, const char * tag)
		: didact_state(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_digits(*this, "digit%u", 0U)
		, m_7segs(*this, "digit%u", 0U)
		, m_pia1(*this, PIA1_TAG)
		, m_pia2(*this, PIA2_TAG)
	{ }

	required_device<m6800_cpu_device> m_maincpu;

	// The display segment driver device (there is actually just one, needs rewrite to be correct)
	required_device_array<dm9368_device, 6> m_digits;
	output_finder<6> m_7segs;

	uint8_t pia2_kbA_r();
	void pia2_kbA_w(uint8_t data);
	uint8_t pia2_kbB_r();
	void pia2_kbB_w(uint8_t data);
	int pia2_cb1_r();
	template <unsigned N> void digit_w(uint8_t data) { m_7segs[N] = data; }

	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	void mp68a(machine_config &config);
	void mp68a_map(address_map &map) ATTR_COLD;

protected:
	required_device<pia6821_device> m_pia1;
	required_device<pia6821_device> m_pia2;
};

INPUT_CHANGED_MEMBER(didact_state::trigger_shift)
{
	if (newval == CLEAR_LINE)
	{
		LOGKBD("SHIFT is released\n");
	}
	else
	{
		LOGKBD("SHIFT is pressed\n");
		m_shift = 1;
		m_led = 1;
	}
}

uint8_t mp68a_state::pia2_kbA_r()
{
	LOG("--->%s\n", FUNCNAME);

	return 0;
}

void mp68a_state::pia2_kbA_w(uint8_t data)
{
	/* Display memory is at $702 to $708 in AAAADD format (A=address digit, D=Data digit)
	   but we are using data read from the port. */
	uint8_t const digit_nbr = (data >> 4) & 0x07;

	/* There is actually only one 9368 and a 74145 to drive the cathode of the right digit low */
	/* This can be emulated by pretending there are one 9368 per digit, at least for now      */
	switch (digit_nbr)
	{
	case 0:
	case 1:
	case 2:
	case 3:
	case 4:
	case 5:
		m_digits[digit_nbr]->a_w(data & 0x0f);
		break;
	case 7: // used as an 'unselect' by the ROM between digit accesses.
		break;
	default:
		logerror("Invalid digit index %d\n", digit_nbr);
	}
}

uint8_t mp68a_state::pia2_kbB_r()
{
	uint8_t a012, line, pb;

	LOGKBD("--->%s %02x %02x %02x %02x %02x => ", FUNCNAME, m_lines[0], m_lines[1], m_lines[2], m_lines[3], m_shift);

	a012 = 0;
	if ((line = (m_lines[0] | m_lines[1])) != 0)
	{
		a012 = 8;
		while (a012 > 0 && !(line & (1 << --a012)));
		a012 += 8;
	}
	if (a012 == 0 && (line = ((m_lines[2]) | m_lines[3])) != 0)
	{
		a012 = 8;
		while (a012 > 0 && !(line & (1 << --a012)));
	}

	pb = a012;       // A0-A2 -> PB0-PB3

	if (m_shift)
	{
		pb |= 0x80;   // Set shift bit (PB7)
		m_shift = 0;  // Reset flip flop
		m_led = 0;
		LOGKBD(" SHIFT is released\n");
	}

	pb |= (m_cass->input() < 0.04) ? 0x20 : 0;
	LOGKBD("%02x\n", pb);

	return pb;
}

void mp68a_state::pia2_kbB_w(uint8_t data)
{
	LOG("--->%s(%02x)\n", FUNCNAME, data);
	m_cass->output(BIT(data, 4) ? -1.0 : +1.0);
}

int mp68a_state::pia2_cb1_r()
{
	for (unsigned i = 0U; 4U > i; ++i)
		m_lines[i] = m_io_lines[i]->read();

	if ((VERBOSE & LOG_GENERAL) && (m_lines[0] | m_lines[1] | m_lines[2] | m_lines[3]))
		LOG("%s()-->%02x %02x %02x %02x\n", FUNCNAME, m_lines[0], m_lines[1], m_lines[2], m_lines[3]);

	return (m_lines[0] | m_lines[1] | m_lines[2] | m_lines[3]) ? 0 : 1;
}

void mp68a_state::machine_reset()
{
	LOG("--->%s()\n", FUNCNAME);
	m_maincpu->reset();
}

void mp68a_state::machine_start()
{
	LOG("--->%s()\n", FUNCNAME);

	didact_state::machine_start();
	m_7segs.resolve();

	/* register for state saving */
	save_item(NAME(m_shift));
	save_item(NAME(m_reset));
}

// This address map is traced from pcb
void mp68a_state::mp68a_map(address_map &map)
{
	map(0x0000, 0x00ff).ram().mirror(0xf000);
	map(0x0500, 0x0503).rw(m_pia1, FUNC(pia6821_device::read), FUNC(pia6821_device::write)).mirror(0xf0fc);
	map(0x0600, 0x0603).rw(m_pia2, FUNC(pia6821_device::read), FUNC(pia6821_device::write)).mirror(0xf0fc);
	map(0x0700, 0x07ff).ram().mirror(0xf000);
	map(0x0800, 0x0bff).rom().mirror(0xf400).region("maincpu", 0x0800);
}

//===================

/*   The Modulab CPU board, by Didact/Esselte ca 1984
 *  __________________________________________________________________________________________
 * |                                                    ADRESS               DATA             |
 * |              PORT A                      +-_--++-_--++-_--++-_--+   +-_--++-_--+   VCC   |
 * |    o   o   o   o   o   o   o   o         || | ||| | ||| | ||| | |   || | ||| | |    O    |
 * |    7   6   5   4   3   2   1   0         | -  || -  || -  || -  |   | -  || -  |         |
 * |    o   o   o   o   o   o   o   o         ||_|.|||_|.|||_|.|||_|.|   ||_|.|||_|.|   GND   |
 * |              PORT B                      +----++----++----++----+   +----++----+    O    |
 * |  o VCC                                    +--+  +--+  +--+  +--+     +--+  +--+          |
 * |                                           |LS|  |LS|  |LS|  |LS|     |LS|  |LS|          |
 * |  o GND                                    |164  |164  |164  |164     |164  |164          |
 * \\                                          |-5|<-|-4|<-|-3|<-|-2| <-  |-1|<-|-0|<- DB0    |
 * |\\ ____                                    +--+  +--+  +--+  +--+     +--+  +--+          |
 * | \/o  O|                                          +-------+-------+-------+-------+-------+
 * | |     |E           +--------------------+ +--+   |       |       |       |       |       |
 * | |     |X   +----+  |  PIA + 128x8 SRAM  | |LS|   |  RUN  |  ADS  |  FWD  | C/B   | RESET |
 * | |     |P   |4MHz|  |  INS8154N          | |14|   |       |       |       |       |       |
 * | |     |A   |XTAL|  +--------------------+ |  |   +-------+-------+-------+-------+-------+
 * | |     |N   |____|                         +--+   |       |       |       |       |       |
 * | |__   |S    |  |   +--------------------+ +--+   |   C   |   D   |   E   |   F   |       |
 * |  __|  |I           |  CPU               | |LS|   |       |       |       |       |       |
 * | |     |O           |  MC6802P           | |138   +-------+-------+-------+-------+       |
 * | |     |N           +--------------------+ |  |   |       |       |       |       |       |
 * | |     |B                                  +--+   |   8   |   9   |   A   |   B   |       |
 * | |     |U    IRQ    +-------------+        +--+   |       |       |       |       |       |
 * | |     |S    o      |  EPROM      |        |74|   +-------+-------+-------+-------+       |
 * | /\o  O|            |  2764       |        |C |   |       |       |       |       |       |
 * |// ----             +-------------+        |923   |   4   |   5   |   6   |   7   |       |
 * //                     +-----------+        |  |   |       |       |       |       |       |
 * |                      | 2KB SRAM  |        +--+   +-------+-------+-------+-------+       |
 * |                      | 6116      |        +--+   |       |       |       |       |       |
 * |                      +-----------+        |LS|   |   0   |   1   |   2   |   3   |       |
 * | ESSELTE       +-------+ +---+ +--------+  |138   |       |       |       |       |       |
 * | STUDIUM       |74LS123| |TRM| |SN74367 |  |  |   +-------+-------+-------+-------+       |
 * |               +-------+ +---+ +--------+  +--+
 * |__________________________________________________________________________________________|
 *
 */

/* Didact modulab driver class */
class modulab_state : public didact_state
{
public:
	modulab_state(const machine_config &mconfig, device_type type, const char * tag)
		: didact_state(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_7segs(*this, "digit%u", 0U)
		, m_pia1(*this, PIA1_TAG)
		, m_kb(*this, MM74C923_TAG)
		, m_da(0)
	{ }

	required_device<m6802_cpu_device> m_maincpu;

	output_finder<6> m_7segs;

	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	void modulab(machine_config &config);

protected:
	uint8_t io_r(offs_t offset);
	void io_w(offs_t offset, u8 data);
	void da_w(int state);

private:
	void modulab_map(address_map &map) ATTR_COLD;
	// Offsets for display and keyboard i/o
	enum
	{
		DISPLAY = 0,
		KEY_DATA = 2,
		KEY_STROBE = 3
	};

	// Simple emulation of 6 cascaded 74164 that drives the AAAADD BCD display elements, right to left
	class shift8
	{
	public:
		shift8() { byte = 0; }
		void shiftIn(uint8_t in){ byte = ((byte << 1) & 0xfe) | (in & 1 ? 1 : 0); }
		uint8_t byte;
	};
	shift8 m_74164[6];

	required_device<ins8154_device> m_pia1;
	required_device<mm74c922_device> m_kb;
	uint8_t m_da;
};

void modulab_state::da_w(int state)
{
	LOG("--->%s()\n", FUNCNAME);
	m_da = state == CLEAR_LINE ? 0 : 1; // Capture data available signal
}

uint8_t modulab_state::io_r(offs_t offset)
{
	switch (offset)
	{
	case 3: // Poll Data available signal
		return m_da & 0x01; // Data Available signal gated by an 8097 hexbuffer to DB0
		break;
	case 2:
		LOG("--->%s Read Keyboard @ %04x\n", FUNCNAME, offset);
		return m_kb->read();
		break;
	default:
		LOG("--->%s BAD access @ %04x\n", FUNCNAME, offset);
		break;
	}
	return 0;
}

void modulab_state::io_w(offs_t offset, u8 data)
{
	LOG("--->%s()\n", FUNCNAME);
	uint8_t b = data & 1;
	switch (offset)
	{
	case DISPLAY:
		// Update the BCD elements with a data bit b shifted in right to left, CS is used as clock for all 164's
		for (int i = 0; i < 6; i++)
		{
			uint8_t c = (m_74164[i].byte & 0x80) ? 1 : 0; // Bit 7 is connected to the next BCD right to left
			m_74164[i].shiftIn(b);
			m_7segs[i] = ~m_74164[i].byte & 0x7f;  // Bit 0 to 6 drives the 7 seg display
			b = c; // bit 7 prior shift will be shifted in next (simultaneous in real life)
		}
		LOGDISPLAY("Shifted: %02x %02x %02x %02x %02x %02x\n",
				~m_74164[0].byte & 0x7f, ~m_74164[1].byte & 0x7f, ~m_74164[2].byte & 0x7f,
				~m_74164[3].byte & 0x7f, ~m_74164[4].byte & 0x7f, ~m_74164[5].byte & 0x7f);
		break;
	default:
		break;
	};
}

void modulab_state::machine_reset()
{
	LOG("--->%s()\n", FUNCNAME);

	m_maincpu->reset();
}

void modulab_state::machine_start()
{
	LOG("--->%s()\n", FUNCNAME);

	didact_state::machine_start();
	m_7segs.resolve();

	/* register for state saving */
	save_item(NAME(m_shift));
	save_item(NAME(m_reset));
}

// This address map is traced from pcb
void modulab_state::modulab_map(address_map &map)
{
	map(0x0000, 0x03ff).ram().mirror(0xe000); // RAM0 always present 2114
	map(0x0400, 0x07ff).ram().mirror(0xe000); // RAM1 optional 2114
	// map(0x0800, 0x13ff).ram().mirror(0xe000); // expansion port area consisting of 3 chip selects each selecting 0x3ff byte addresses
	map(0x1400, 0x17ff).rom().mirror(0xe000).region("maincpu", 0x0000);
	map(0x1800, 0x187f).rw(FUNC(modulab_state::io_r), FUNC(modulab_state::io_w)).mirror(0xe200);
	map(0x1900, 0x197f).rw(m_pia1, FUNC(ins8154_device::read_io), FUNC(ins8154_device::write_io)).mirror(0xe200);
	map(0x1980, 0x19ff).rw(m_pia1, FUNC(ins8154_device::read_ram), FUNC(ins8154_device::write_ram)).mirror(0xe200);
	map(0x1c00, 0x1fff).rom().mirror(0xe000).region("maincpu", 0x0400);
}

//===================

static INPUT_PORTS_START( modulab )
	PORT_START("LINE0") // X1
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RUN") PORT_CODE(KEYCODE_R) PORT_CHAR('R')

	PORT_START("LINE1") // X2
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ADS") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')

	PORT_START("LINE2") // X3
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("FWD") PORT_CODE(KEYCODE_W) PORT_CHAR('W')

	PORT_START("LINE3") // X4
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C/B") PORT_CODE(KEYCODE_X) PORT_CHAR('X')

	PORT_START("LINE4") /* Special KEY ROW for reset key */
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_F12) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(modulab_state::trigger_reset), 0)
	PORT_BIT(0xfb, 0x00, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( md6802 )
	PORT_START("LINE0") /* KEY ROW 0 */
	PORT_BIT(0x01, 0x01, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)  PORT_CHAR('0')
	PORT_BIT(0x02, 0x02, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)  PORT_CHAR('1')
	PORT_BIT(0x04, 0x04, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)  PORT_CHAR('2')
	PORT_BIT(0x08, 0x08, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)  PORT_CHAR('3')
	PORT_BIT(0xf0, 0x00, IPT_UNUSED )

	PORT_START("LINE1") /* KEY ROW 1 */
	PORT_BIT(0x01, 0x01, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)  PORT_CHAR('4')
	PORT_BIT(0x02, 0x02, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)  PORT_CHAR('5')
	PORT_BIT(0x04, 0x04, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)  PORT_CHAR('6')
	PORT_BIT(0x08, 0x08, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)  PORT_CHAR('7')
	PORT_BIT(0xf0, 0x00, IPT_UNUSED )

	PORT_START("LINE2") /* KEY ROW 2 */
	PORT_BIT(0x01, 0x01, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)  PORT_CHAR('8')
	PORT_BIT(0x02, 0x02, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)  PORT_CHAR('9')
	PORT_BIT(0x04, 0x04, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)  PORT_CHAR('A')
	PORT_BIT(0x08, 0x08, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)  PORT_CHAR('B')
	PORT_BIT(0xf0, 0x00, IPT_UNUSED )

	PORT_START("LINE3") /* KEY ROW 3 */
	PORT_BIT(0x01, 0x01, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)  PORT_CHAR('C')
	PORT_BIT(0x02, 0x02, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)  PORT_CHAR('D')
	PORT_BIT(0x04, 0x04, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)  PORT_CHAR('E')
	PORT_BIT(0x08, 0x08, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)  PORT_CHAR('F')
	PORT_BIT(0xf0, 0x00, IPT_UNUSED )

	PORT_START("LINE4") /* Special KEY ROW for reset and Shift/'*' keys */
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("*") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR('*') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(md6802_state::trigger_shift), 0)
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_F12) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(md6802_state::trigger_reset), 0)
	PORT_BIT(0xf3, 0x00, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( mp68a )
	PORT_START("LINE0") /* KEY ROW 0 */
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)    PORT_CHAR('C')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)    PORT_CHAR('D')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)    PORT_CHAR('E')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)    PORT_CHAR('F')
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("LINE1") /* KEY ROW 1 */
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)    PORT_CHAR('8')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)    PORT_CHAR('9')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)    PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)    PORT_CHAR('B')
	PORT_BIT(0xf0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("LINE2") /* KEY ROW 2 */
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)    PORT_CHAR('4')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)    PORT_CHAR('5')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)    PORT_CHAR('6')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)    PORT_CHAR('7')
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("LINE3") /* KEY ROW 3 */
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)    PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)    PORT_CHAR('1')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)    PORT_CHAR('2')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)    PORT_CHAR('3')
	PORT_BIT(0xf0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("LINE4") /* Special KEY ROW for reset and Shift/'*' keys, they are hard wired */
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("*") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR('*') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mp68a_state::trigger_shift), 0)
	//PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_F12)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_F12) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mp68a_state::trigger_reset), 0)
	PORT_BIT(0xf3, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(didact_state::trigger_reset)
{
	if (newval == CLEAR_LINE)
	{
		LOGKBD("RESET is released, resetting the CPU\n");
		machine_reset();
		m_shift = 0;
		m_led = 0;
	}
}


void modulab_state::modulab(machine_config &config)
{
	m6802_cpu_device &maincpu(M6802(config, m_maincpu, XTAL(4'000'000)));
	maincpu.set_ram_enable(false); // Schematics holds RAM enable low so that the M6802 internal RAM is disabled.
	maincpu.set_addrmap(AS_PROGRAM, &modulab_state::modulab_map);
	config.set_default_layout(layout_modulab);

	/* Devices */
	MM74C923(config, m_kb, 0);
	m_kb->set_cap_osc(CAP_U(0.10));
	m_kb->set_cap_debounce(CAP_U(1));
	m_kb->da_wr_callback().set(FUNC(modulab_state::da_w));
	m_kb->x1_rd_callback().set_ioport("LINE0");
	m_kb->x2_rd_callback().set_ioport("LINE1");
	m_kb->x3_rd_callback().set_ioport("LINE2");
	m_kb->x4_rd_callback().set_ioport("LINE3");

	/* PIA #1 0x????-0x??? -  */
	INS8154(config, m_pia1);
	//m_ins8154->in_a().set(FUNC(modulab_state::ins8154_pa_r));
	//m_ins8154->out_a().set(FUNC(modulab_state::ins8154_pa_w));

	//RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
}

void md6802_state::md6802(machine_config &config)
{
	m6802_cpu_device &maincpu(M6802(config, m_maincpu, XTAL(4'000'000)));
	maincpu.set_ram_enable(false);
	maincpu.set_addrmap(AS_PROGRAM, &md6802_state::md6802_map);
	config.set_default_layout(layout_md6802);

	/* Devices */
	TTL74145(config, m_tb16_74145, 0);
	/* PIA #1 0xA000-0xA003 - used differently by laborations and loaded software */
	PIA6821(config, m_pia1);

	/* PIA #2 Keyboard & Display 0xC000-0xC003 */
	PIA6821(config, m_pia2);
	/* --PIA init----------------------- */
	/* 0xE007 0xC002 (DDR B)     = 0xFF - Port B all outputs and set to 0 (zero) */
	/* 0xE00B 0xC000 (DDR A)     = 0x70 - Port A three outputs and set to 0 (zero) */
	/* 0xE00F 0xC001 (Control A) = 0x3C - */
	/* 0xE013 0xC003 (Control B) = 0x3C - */
	/* --execution-wait for key loop-- */
	/* 0xE026 0xC000             = (Reading Port A)  */
	/* 0xE033 0xC000             = (Reading Port A)  */
	/* 0xE068 0xC000 (Port A)    = 0x60 */
	/* 0xE08A 0xC002 (Port B)    = 0xEE - updating display */
	/* 0xE090 0xC000 (Port A)    = 0x00 - looping in 0x10,0x20,0x30,0x40,0x50 */
	m_pia2->writepa_handler().set(FUNC(md6802_state::pia2_kbA_w));
	m_pia2->readpa_handler().set(FUNC(md6802_state::pia2_kbA_r));
	m_pia2->writepb_handler().set(FUNC(md6802_state::pia2_kbB_w));
	m_pia2->readpb_handler().set(FUNC(md6802_state::pia2_kbB_r));
	m_pia2->ca2_handler().set(FUNC(md6802_state::pia2_ca2_w));

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
}

void mp68a_state::mp68a(machine_config &config)
{
	// Clock source is based on a N9602N Dual Retriggerable Resettable Monostable Multivibrator oscillator at aprox 505KHz.
	// Trimpot seems broken/stuck at 5K Ohm thu. ROM code 1Ms delay loops suggest 1MHz+
	M6800(config, m_maincpu, 505000);
	m_maincpu->set_addrmap(AS_PROGRAM, &mp68a_state::mp68a_map);
	config.set_default_layout(layout_mp68a);

	/* Devices */
	/* PIA #1 0x500-0x503 - used differently by laborations and loaded software */
	PIA6821(config, m_pia1, 0); // actually 6820

	/* PIA #2 Keyboard & Display 0x600-0x603 */
	PIA6821(config, m_pia2, 0); // actually 6820
	/* --PIA inits----------------------- */
	/* 0x0BAF 0x601 (Control A) = 0x30 - CA2 is low and enable DDRA */
	/* 0x0BB1 0x603 (Control B) = 0x30 - CB2 is low and enable DDRB */
	/* 0x0BB5 0x600 (DDR A)     = 0xFF - Port A all outputs and set to 0 (zero) */
	/* 0x0BB9 0x602 (DDR B)     = 0x50 - Port B two outputs and set to 0 (zero) */
	/* 0x0BBD 0x601 (Control A) = 0x34 - CA2 is low and lock DDRA */
	/* 0x0BBF 0x603 (Control B) = 0x34 - CB2 is low and lock DDRB */
	/* 0x0BC3 0x602 (Port B)    = 0x40 - Turn on display via RBI* on  */
	/* --execution-wait for key loop-- */
	/* 0x086B Update display sequnc, see below                            */
	/* 0x0826 CB1 read          = 0x603 (Control B)  - is a key pressed? */
	m_pia2->writepa_handler().set(FUNC(mp68a_state::pia2_kbA_w));
	m_pia2->readpa_handler().set(FUNC(mp68a_state::pia2_kbA_r));
	m_pia2->writepb_handler().set(FUNC(mp68a_state::pia2_kbB_w));
	m_pia2->readpb_handler().set(FUNC(mp68a_state::pia2_kbB_r));
	m_pia2->readcb1_handler().set(FUNC(mp68a_state::pia2_cb1_r));
	m_pia2->irqa_handler().set_inputline("maincpu", M6800_IRQ_LINE); /* Not used by ROM. Combined trace to CPU IRQ with IRQB */
	m_pia2->irqb_handler().set_inputline("maincpu", M6800_IRQ_LINE); /* Not used by ROM. Combined trace to CPU IRQ with IRQA */

	/* Display - sequence outputting all '0':s at start */
	/* 0x086B 0x600 (Port A)    = 0x00 */
	/* 0x086B 0x600 (Port A)    = 0x70 */
	/* 0x086B 0x600 (Port A)    = 0x10 */
	/* 0x086B 0x600 (Port A)    = 0x70 */
	/* 0x086B 0x600 (Port A)    = 0x20 */
	/* 0x086B 0x600 (Port A)    = 0x70 */
	/* 0x086B 0x600 (Port A)    = 0x30 */
	/* 0x086B 0x600 (Port A)    = 0x70 */
	/* 0x086B 0x600 (Port A)    = 0x40 */
	/* 0x086B 0x600 (Port A)    = 0x70 */
	/* 0x086B 0x600 (Port A)    = 0x50 */
	/* 0x086B 0x600 (Port A)    = 0x70 */
	DM9368(config, m_digits[0]).update_cb().set(FUNC(mp68a_state::digit_w<0>));
	DM9368(config, m_digits[1]).update_cb().set(FUNC(mp68a_state::digit_w<1>));
	DM9368(config, m_digits[2]).update_cb().set(FUNC(mp68a_state::digit_w<2>));
	DM9368(config, m_digits[3]).update_cb().set(FUNC(mp68a_state::digit_w<3>));
	DM9368(config, m_digits[4]).update_cb().set(FUNC(mp68a_state::digit_w<4>));
	DM9368(config, m_digits[5]).update_cb().set(FUNC(mp68a_state::digit_w<5>));

	/* Cassette */
	SPEAKER(config, "mono").front_center();
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
}

ROM_START( modulab )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_DEFAULT_BIOS("modulabvl")

	ROM_SYSTEM_BIOS(0, "modulabv1", "Modulab Version 1")
	ROMX_LOAD( "mlab1_00.bin", 0x0000, 0x0800, NO_DUMP, ROM_BIOS(0) )

	ROM_SYSTEM_BIOS(1, "modulabv2", "Modulab Version 2")
	ROMX_LOAD( "mlab2_00.bin", 0x0000, 0x0800, NO_DUMP, ROM_BIOS(1) )

	ROM_SYSTEM_BIOS(2, "modulabvl", "Modulab Prototype")
	ROMX_LOAD( "modulab_levererad.bin", 0x0000, 0x0800, CRC(40774ef4) SHA1(9cf188342993fbcff13dbbecc62d1ee49010d6f4), ROM_BIOS(2) )
ROM_END

// TODO split ROM image into proper ROM set
ROM_START( md6802 ) // ROM image from http://elektronikforumet.com/forum/viewtopic.php?f=2&t=79576&start=135#p1203640
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD( "didact.bin", 0xe000, 0x0800, CRC(50430b1d) SHA1(8e2172a9ae95b04f20aa14177df2463a286c8465) )
ROM_END

ROM_START( mp68a ) // ROM image from http://elektronikforumet.com/forum/viewtopic.php?f=2&t=79576&start=135#p1203640
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD( "didacta.bin", 0x0800, 0x0200, CRC(aa05e1ce) SHA1(9ce8223efd274045b43ceca3529e037e16e99fdf) )
	ROM_LOAD( "didactb.bin", 0x0a00, 0x0200, CRC(592898dc) SHA1(2962f4817712cae97f3ab37b088fc73e66535ff8) )
ROM_END

} // anonymous namespace


//    YEAR  NAME    PARENT  COMPAT   MACHINE  INPUT    CLASS          INIT        COMPANY               FULLNAME           FLAGS
COMP( 1979, mp68a,   0,      0,      mp68a,   mp68a,   mp68a_state,   empty_init, "Didact AB",          "mp68a",           MACHINE_NO_SOUND_HW )
COMP( 1983, md6802,  0,      0,      md6802,  md6802,  md6802_state,  empty_init, "Didact AB",          "Mikrodator 6802", MACHINE_NO_SOUND_HW )
COMP( 1984, modulab, 0,      0,      modulab, modulab, modulab_state, empty_init, "Esselte Studium AB", "Modulab",         MACHINE_NO_SOUND_HW )



digel804.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:balrog,Jonathan Gevaryahu,Sandro Ronco
/******************************************************************************
*
*  Wavetek/Digelec model 804/EP804 (eprom programmer) driver
*  By balrog and Jonathan Gevaryahu AKA Lord Nightmare
*  Code adapted from zexall.c
*
*  DONE:
*  figure out z80 address space and hook up roms and rams (done)
*  figure out where 10937 vfd controller lives (port 0x44 bits 7 and 0, POR has not been found yet)
*  figure out how keypresses are detected (/INT?, port 0x43, and port 0x46)
*  figure out how the banked ram works (writes to port 0x43 select a ram bank, fw2.2 supports 64k and fw4.1 supports 256k)
*  tentatively figure out how flow control from ACIA works (/NMI)?
*  hook up the speaker/beeper (port 0x45)
*  hook up vfd controller (done to stderr, no artwork)
*  hook up leds on front panel (done to stderr for now)
*  hook up r6551 serial and attach terminal for sending to ep804
*  /nmi is emulated correctly (i.e. its tied to +5v. that was easy!)
*  hook up keypad via 74C923 and mode buttons via logic gate mess
*
*  TODO:
*  remove ACIA hack in digel804_state::acia_command_w
*  minor finishing touches to i/o map
*  EPROM socket stuff (ports 0x40, 0x41, 0x42 and 0x47)
*  artwork
*
******************************************************************************/

/* Core includes */
#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/mm74c922.h"
#include "machine/mos6551.h"
#include "machine/ram.h"
#include "machine/roc10937.h"
#include "sound/spkrdev.h"
#include "speaker.h"

#include "digel804.lh"


namespace {

// port 40 read reads eprom socket pins 11-13, 15-19 (i.e. eprom pin D0 to pin D7)

// port 40 write writes eprom socket pins 11-13, 15-19 (i.e. eprom pin D0 to pin D7)

// port 41 write controls eprom socket pins 10 to 3 (d0 to d8 to eprom pins A0 to A7) and SIM pins 7, 20/11, 8/12, 27, 23, and 21 (d0 to d5, corresponding to SIM pins for A9, A10, A11, A12, A13, and A14+A15)
// Note: the v2.0 hardware has bit d6 controlling one of the pins going to the expansion ram socket
// The 2.0 hardware may also have separate SIM controls for A14 and A15, possibly using bit d7 to control sim A15 somehow

// port 42 write controls eprom socket pins 25(d0), 2(d4), 27(d6)

// port 43 read is status/mode
#undef PORT43_R_VERBOSE
// port 43 write is ram banking and ctl1-7; it also clears overload state
#define PORT43_W_VERBOSE 1
// port 44 write is vfd serial/reset, as well as power and z80 control and various enables; it also clears powerfail state
#define PORT44_W_VERBOSE 1
// port 45 write is speaker control
#undef PORT45_W_VERBOSE
// port 46 read is keypad

// port 46 write is LED control
#undef PORT46_W_VERBOSE

// port 47 write is tim0-tim7


class digel804_state : public driver_device
{
public:
	digel804_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_ram(*this, RAM_TAG),
		m_maincpu(*this, "maincpu"),
		m_acia(*this, "acia"),
		m_speaker(*this, "speaker"),
		m_vfd(*this, "vfd"),
		m_kb(*this, "74c923"),
		m_rambank(*this, "bankedram"),
		m_func_leds(*this, "func_led%u", 0U),
		m_input_led(*this, "input_led"),
		m_busy_led(*this, "busy_led"),
		m_error_led(*this, "error_led")
	{
	}

	DECLARE_INPUT_CHANGED_MEMBER(mode_change);

	void digel804(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void op00(uint8_t data);
	uint8_t ip40();
	void op40(uint8_t data);
	void op41(uint8_t data);
	void op42(uint8_t data);
	uint8_t ip43();
	void op43(uint8_t data);
	void op43_1_4(uint8_t data);
	void op44(uint8_t data);
	void op45(uint8_t data);
	uint8_t ip46();
	void op46(uint8_t data);
	void op47(uint8_t data);
	uint8_t acia_rxd_r();
	void acia_txd_w(uint8_t data);
	uint8_t acia_status_r();
	void acia_reset_w(uint8_t data);
	uint8_t acia_command_r();
	void acia_command_w(uint8_t data);
	uint8_t acia_control_r();
	void acia_control_w(uint8_t data);
	void acia_irq_w(int state);
	void da_w(int state);

	void z80_mem_804_1_4(address_map &map) ATTR_COLD;
	void z80_io_1_4(address_map &map) ATTR_COLD;

	required_device<ram_device> m_ram;
	required_device<cpu_device> m_maincpu;
	required_device<mos6551_device> m_acia;

private:
	required_device<speaker_sound_device> m_speaker;
	required_device<roc10937_device> m_vfd;
	required_device<mm74c922_device> m_kb;
	required_memory_bank m_rambank;

	output_finder<16> m_func_leds;
	output_finder<> m_input_led;
	output_finder<> m_busy_led;
	output_finder<> m_error_led;

	// current speaker state for port 45
	uint8_t m_speaker_state;
	// ram stuff for banking
	uint8_t m_ram_bank;
	// states
	uint8_t m_acia_intq;
	uint8_t m_overload_state;
	uint8_t m_key_intq;
	uint8_t m_remote_mode;
	uint8_t m_key_mode;
	uint8_t m_sim_mode;
	uint8_t m_powerfail_state;
	uint8_t m_chipinsert_state;
	uint8_t m_keyen_state;
	uint8_t m_op41;
};


class ep804_state : public digel804_state
{
public:
	using digel804_state::digel804_state;

	void ep804(machine_config &config);

protected:
	void ep804_acia_irq_w(int state);

	void z80_mem_804_1_2(address_map &map) ATTR_COLD;
	void z80_io_1_2(address_map &map) ATTR_COLD;
};


enum { MODE_OFF, MODE_KEY, MODE_REM, MODE_SIM };


void digel804_state::machine_start()
{
	m_func_leds.resolve();
	m_input_led.resolve();
	m_busy_led.resolve();
	m_error_led.resolve();

	save_item(NAME(m_speaker_state));
	save_item(NAME(m_ram_bank));
	save_item(NAME(m_acia_intq));
	save_item(NAME(m_overload_state));
	save_item(NAME(m_key_intq));
	save_item(NAME(m_remote_mode));
	save_item(NAME(m_key_mode));
	save_item(NAME(m_sim_mode));
	save_item(NAME(m_powerfail_state));
	save_item(NAME(m_chipinsert_state));
	save_item(NAME(m_keyen_state));
	save_item(NAME(m_op41));

	m_speaker_state = 0;
	//port43_rtn = 0xEE;//0xB6;
	m_acia_intq = 1; // /INT source 1
	m_overload_state = 0; // OVLD
	m_key_intq = 1; // /INT source 2
	m_remote_mode = 1; // /REM
	m_key_mode = 0; // /KEY
	m_sim_mode = 1; // /SIM
	m_powerfail_state = 1; // "O11"
	m_chipinsert_state = 0; // PIN
	m_ram_bank = 0;
	m_op41 = 0;
	m_keyen_state = 1; // /KEYEN

	m_rambank->set_base(m_ram->pointer());
}

void digel804_state::machine_reset()
{
	m_vfd->reset();
}

uint8_t digel804_state::ip40() // eprom data bus read
{
	// TODO: would be nice to have a 'fake eprom' here
	return 0xFF;
}

void digel804_state::op40(uint8_t data) // eprom data bus write
{
	// TODO: would be nice to have a 'fake eprom' here
	logerror("Digel804: port 40, eprom databus had %02X written to it!\n", data);
}

void digel804_state::op41(uint8_t data) // eprom address low write AND SIM write, d6 also controls memory map somehow
{
	// TODO: would be nice to have a 'fake eprom' here
	logerror("Digel804: port 41, eprom address low/sim/memorybank had %02X written to it!\n", data);
	//fprintf(stderr,"op41 write of %02X, ram mapper bit is %d\n", data, (data&0x40)?1:0);
	m_op41 = data;
}

void digel804_state::op42(uint8_t data) // eprom address hi and control write
{
	// TODO: would be nice to have a 'fake eprom' here
	logerror("Digel804: port 42, eprom address hi/control had %02X written to it!\n", data);
}

uint8_t digel804_state::ip43()
{
	/* Register 0x43: status/mode register read
	 bits 76543210
	      |||||||\- overload state (0 = not overloaded; 1 = overload detected, led on and power disconnected to ic, writing to R43 sets this to ok)
	      ||||||\-- debug jumper X5 on the board; reads as 1 unless jumper is present
	      |||||\--- /INT status (any key pressed on keypad (0 = one or more pressed, 1 = none pressed) OR ACIA has thrown an int)
	      ||||\---- remote mode selected (0 = selected, 1 = not) \
	      |||\----- key mode selected (0 = selected, 1 = not)     > if all 3 of these are 1, unit is going to standby
	      ||\------ sim mode selected (0 = selected, 1 = not)    /
	      |\------- power failure status (1 = power has failed, 0 = ok; writes to R44 set this to ok)
	      \-------- chip insert detect state 'PIN' (1 = no chip or cmos chip which ammeter cannot detect; 0 = nmos or detectable chip inserted)
	 after power failure (in key mode):
	 0xEE 11101110 when no keypad key pressed
	 0xEA 11101010 when keypad key pressed
	 in key mode:
	 0xAE 10101110 when no keypad key pressed
	 0xAA 10101010 when keypad key pressed
	 in remote mode:
	 0xB6 10110110 when no keypad key pressed
	 0xB2 10110010 when keypad key pressed
	 in sim mode:
	 0x9E 10011110 when no keypad key pressed
	 0x9A 10011010 when keypad key pressed
	 in off mode (before z80 is powered down):
	 0xFE 11111110

	*/

#ifdef PORT43_R_VERBOSE
	logerror("Digel804: returning %02X for port 43 status read\n", port43_rtn);
#endif
	return ((m_overload_state<<0)
		|((ioport("DEBUG")->read()&1)<<1)
		|((m_key_intq&m_acia_intq)<<2)
		|(m_remote_mode<<3)
		|(m_key_mode<<4)
		|(m_sim_mode<<5)
		|(m_powerfail_state<<6)
		|(m_chipinsert_state<<7));

}

void digel804_state::op00(uint8_t data)
{
	m_ram_bank = data;
	m_rambank->set_base(m_ram->pointer() + ((m_ram_bank * 0x8000) & m_ram->mask()));
}

void digel804_state::op43(uint8_t data)
{
	/* writes to 0x43 control the ram banking on firmware which supports it
	 * bits:76543210
	 *      |||||\\\- select ram bank for 4000-bfff area based on these bits (2.0 hardware)
	 *      \\\\\\\-- CTL lines

	 * all writes to port 43 will reset the overload state unless the ammeter detects the overload is ongoing
	 */
	m_overload_state = 0; // writes to port 43 clear overload state
#ifdef PORT43_W_VERBOSE
	logerror("Digel804: port 0x43 ram bank had %02x written to it!\n", data);
	logerror("          op41 bit 6 is %d\n", (m_op41 & 0x40)?1:0);
#endif
	m_ram_bank = data&7;
	if ((data&0xF8)!=0)
		logerror("Digel804: port 0x43 ram bank had unexpected data %02x written to it!\n", data);

	m_rambank->set_base(m_ram->pointer() + ((m_ram_bank * 0x8000) & m_ram->mask()));
}

void digel804_state::op43_1_4(uint8_t data)
{
	m_overload_state = 0; // writes to port 43 clear overload state
}

void digel804_state::op44(uint8_t data) // state write
{
	/* writes to 0x44 control the 10937 vfd chip, z80 power/busrq, eprom driving and some eprom power ctl lines
	 * bits:76543210
	 *      |||||||\- 10937 VFDC '/SCK' serial clock '/CDIS'
	 *      ||||||\-- controls '/KEYEN' (which enables the four mode buttons when active)
	 *      |||||\--- z80 and system power control (0 = power on, 1 = power off/standby), also controls '/MEMEN' which secondarily controls POR(power on/reset) for the VFDC chip
	 *      ||||\---- controls the z80 /BUSRQ line (0 = idle/high, 1 = asserted/low) '/BRQ'
	 *      |||\----- when 1, enables the output drivers of op40 to the rom data pins
	 *      ||\------ controls 'CTL8'
	 *      |\------- controls 'CTL9'
	 *      \-------- 10937 VFDC 'DATA' serial data '/DDIS'

	 * all writes to port 44 will reset the powerfail state
	 */
	m_powerfail_state = 0; // writes to port 44 clear powerfail state
	m_keyen_state = BIT(data, 1);
#ifdef PORT44_W_VERBOSE
	logerror("Digel804: port 0x44 vfd/state control had %02x written to it!\n", data);
#endif
	m_vfd->por(!(data&0x04));
	m_vfd->data(data&0x80);
	m_vfd->sclk(data&1);
}

void digel804_state::op45(uint8_t data) // speaker write
{
	// all writes to here invert the speaker state, verified from schematics
#ifdef PORT45_W_VERBOSE
	logerror("Digel804: port 0x45 speaker had %02x written to it!\n", data);
#endif
	m_speaker_state ^= 0xFF;
	m_speaker->level_w(m_speaker_state);
}

uint8_t digel804_state::ip46() // keypad read
{
	/* reads E* for a keypad number 0-F
	 * reads F0 for enter
	 * reads F4 for next
	 * reads F8 for rept
	 * reads FC for clear
	 * F* takes precedence over E*
	 * higher numbers take precedence over lower ones
	 * this value auto-latches on a key press and remains through multiple reads
	 * this is done by a 74C923 integrated circuit
	*/
	uint8_t kbd = m_kb->read();
#ifdef PORT46_R_VERBOSE
	logerror("Digel804: returning %02X for port 46 keypad read\n", kbd);
#endif

	return bitswap<8>(kbd,7,6,5,4,1,0,3,2);   // verified from schematics
}

void digel804_state::op46(uint8_t data)
{
	/* writes to 0x46 control the LEDS on the front panel
	 * bits:76543210
	 *      ||||\\\\- these four bits choose which of the 16 function leds is lit; the number is INVERTED first
	 *      |||\----- if this bit is 1, the function leds are disabled
	 *      ||\------ this bit controls the 'error' led; 1 = on
	 *      |\------- this bit controls the 'busy' led; 1 = on
	 *      \-------- this bit controls the 'input' led; 1 = on
	 */
#ifdef PORT46_W_VERBOSE
		logerror("Digel804: port 0x46 LED control had %02x written to it!\n", data);
#endif
		//popmessage("LEDS: %s %s %s Func: %s%d\n", (data&0x80)?"INPUT":"-----", (data&0x40)?"BUSY":"----", (data&0x20)?"ERROR":"-----", (data&0x10)?"None":"", (data&0x10)?-1:(~data&0xF));
		//fprintf("LEDS: %s %s %s Func: %s%d\n", (data&0x80)?"INPUT":"-----", (data&0x40)?"BUSY":"----", (data&0x20)?"ERROR":"-----", (data&0x10)?"None":"", (data&0x10)?-1:(~data&0xF));

	m_input_led = BIT(data,7);
	m_busy_led  = BIT(data,6);
	m_error_led = BIT(data,5);

	for (int i = 0; i < 16; i++)
		m_func_leds[i] = (!(data & 0x10) && ((~data & 0x0f) == i)) ? 1 : 0;
}

void digel804_state::op47(uint8_t data) // eprom timing/power and control write
{
	// TODO: would be nice to have a 'fake eprom' here
	logerror("Digel804: port 47, eprom timing/power and control had %02X written to it!\n", data);
}

INPUT_CHANGED_MEMBER( digel804_state::mode_change )
{
	if (!newval && !m_keyen_state)
	{
		switch (param)
		{
			case MODE_OFF:
				m_key_mode = m_remote_mode = m_sim_mode = 1;
				break;
			case MODE_KEY:
				m_remote_mode = m_sim_mode = 1;
				m_key_mode = 0;
				break;
			case MODE_REM:
				m_key_mode = m_sim_mode = 1;
				m_remote_mode = 0;
				break;
			case MODE_SIM:
				m_remote_mode = m_key_mode = 1;
				m_sim_mode = 0;
				break;
		}

		m_acia->reset();
	}

	// press one of those keys reset the Z80
	m_maincpu->set_input_line(INPUT_LINE_RESET, (!newval && !m_keyen_state) ? ASSERT_LINE : CLEAR_LINE);
}

/* ACIA Trampolines */
uint8_t digel804_state::acia_rxd_r()
{
	return m_acia->read(0);
}

void digel804_state::acia_txd_w(uint8_t data)
{
	m_acia->write(0, data);
}

uint8_t digel804_state::acia_status_r()
{
	return m_acia->read(1);
}

void digel804_state::acia_reset_w(uint8_t data)
{
	m_acia->write(1, data);
}

uint8_t digel804_state::acia_command_r()
{
	return m_acia->read(2);
}

void digel804_state::acia_command_w(uint8_t data)
{
	data |= 0x08;   // HACK for ep804 remote mode

	m_acia->write(2, data);
}

uint8_t digel804_state::acia_control_r()
{
	return m_acia->read(3);
}

void digel804_state::acia_control_w(uint8_t data)
{
	m_acia->write(3, data);
}


/******************************************************************************
 Address Maps
******************************************************************************/

void digel804_state::z80_mem_804_1_4(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).rom(); // 3f in mapper = rom J3
	//map(0x4000, 0x5fff).ram().share("main_ram"); // 6f in mapper = RAM D43 (6164)
	//map(0x6000, 0x7fff).ram().share("main_ram"); // 77 in mapper = RAM D44 (6164)
	//map(0x8000, 0x9fff).ram().share("main_ram"); // 7b in mapper = RAM D45 (6164)
	//map(0xa000, 0xbfff).ram().share("main_ram"); // 7d in mapper = RAM D46 (6164)
	map(0x4000, 0xbfff).bankrw("bankedram");
	// c000-cfff is open bus in mapper, 7f
	map(0xd000, 0xd7ff).ram(); // 7e in mapper = RAM P3 (6116)
	map(0xd800, 0xf7ff).rom(); // 5f in mapper = rom K3
	// f800-ffff is open bus in mapper, 7f
}

void ep804_state::z80_mem_804_1_2(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom(); // 3f in mapper = rom D41
	map(0x2000, 0x3fff).rom(); // 5f in mapper = rom D42
	//map(0x4000, 0x5fff).ram().share("main_ram"); // 6f in mapper = RAM D43 (6164)
	//map(0x6000, 0x7fff).ram().share("main_ram"); // 77 in mapper = RAM D44 (6164)
	//map(0x8000, 0x9fff).ram().share("main_ram"); // 7b in mapper = RAM D45 (6164)
	//map(0xa000, 0xbfff).ram().share("main_ram"); // 7d in mapper = RAM D46 (6164)
	map(0x4000, 0xbfff).bankrw("bankedram");
	// c000-cfff is open bus in mapper, 7f
	//map(0xc000, 0xc7ff).ram(); // hack for now to test, since sometimes it writes to c3ff
	map(0xd000, 0xd7ff).ram(); // 7e in mapper = RAM D47 (6116)
	// d800-ffff is open bus in mapper, 7f
}

void digel804_state::z80_io_1_4(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	// io bits: x 1 x x x * * *
	// writes to 47, 4e, 57, 5e, 67, 6e, 77, 7e, c7, ce, d7, de, e7, ee, f7, fe all go to 47, same with reads
	map(0x00, 0x00).mirror(0x38).w(FUNC(digel804_state::op00)); // W, banked ram
	map(0x40, 0x40).mirror(0xB8).rw(FUNC(digel804_state::ip40), FUNC(digel804_state::op40)); // RW, eprom socket data bus input/output value
	map(0x41, 0x41).mirror(0xB8).w(FUNC(digel804_state::op41)); // W, eprom socket address low out
	map(0x42, 0x42).mirror(0xB8).w(FUNC(digel804_state::op42)); // W, eprom socket address hi/control out
	map(0x43, 0x43).mirror(0xB8).rw(FUNC(digel804_state::ip43), FUNC(digel804_state::op43_1_4)); // RW, mode and status register, also checks if keypad is pressed; write is unknown
	map(0x44, 0x44).mirror(0xB8).w(FUNC(digel804_state::op44)); // W, 10937 vfd controller, z80 power and state control reg
	map(0x45, 0x45).mirror(0xB8).w(FUNC(digel804_state::op45)); // W, speaker bit; every write inverts state
	map(0x46, 0x46).mirror(0xB8).rw(FUNC(digel804_state::ip46), FUNC(digel804_state::op46)); // RW, reads keypad, writes controls the front panel leds.
	map(0x47, 0x47).mirror(0xB8).w(FUNC(digel804_state::op47)); // W eprom socket power and timing control
	// io bits: 1 0 x x x * * *
	// writes to 80, 88, 90, 98, a0, a8, b0, b8 all go to 80, same with reads
	map(0x80, 0x80).mirror(0x38).w(FUNC(digel804_state::acia_txd_w)); // (ACIA xmit reg)
	map(0x81, 0x81).mirror(0x38).r(FUNC(digel804_state::acia_rxd_r)); // (ACIA rcv reg)
	map(0x82, 0x82).mirror(0x38).w(FUNC(digel804_state::acia_reset_w)); // (ACIA reset reg)
	map(0x83, 0x83).mirror(0x38).r(FUNC(digel804_state::acia_status_r)); // (ACIA status reg)
	map(0x84, 0x84).mirror(0x38).w(FUNC(digel804_state::acia_command_w)); // (ACIA command reg)
	map(0x85, 0x85).mirror(0x38).r(FUNC(digel804_state::acia_command_r)); // (ACIA command reg)
	map(0x86, 0x86).mirror(0x38).w(FUNC(digel804_state::acia_control_w)); // (ACIA control reg)
	map(0x87, 0x87).mirror(0x38).r(FUNC(digel804_state::acia_control_r)); // (ACIA control reg)
	//map(0x80,0x87).mirror(0x38).shift(-1).rw("acia", FUNC(mos6551_device::read, FUNC(mos6551_device::write)); // this doesn't work since we lack a shift() command

}

void ep804_state::z80_io_1_2(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	// io bits: x 1 x x x * * *
	// writes to 47, 4e, 57, 5e, 67, 6e, 77, 7e, c7, ce, d7, de, e7, ee, f7, fe all go to 47, same with reads
	map(0x40, 0x40).mirror(0xB8).rw(FUNC(ep804_state::ip40), FUNC(ep804_state::op40)); // RW, eprom socket data bus input/output value
	map(0x41, 0x41).mirror(0xB8).w(FUNC(ep804_state::op41)); // W, eprom socket address low out
	map(0x42, 0x42).mirror(0xB8).w(FUNC(ep804_state::op42)); // W, eprom socket address hi/control out
	map(0x43, 0x43).mirror(0xB8).rw(FUNC(ep804_state::ip43), FUNC(ep804_state::op43)); // RW, mode and status register, also checks if keypad is pressed; write is unknown
	map(0x44, 0x44).mirror(0xB8).w(FUNC(ep804_state::op44)); // W, 10937 vfd controller, z80 power and state control reg
	map(0x45, 0x45).mirror(0xB8).w(FUNC(ep804_state::op45)); // W, speaker bit; every write inverts state
	map(0x46, 0x46).mirror(0xB8).rw(FUNC(ep804_state::ip46), FUNC(ep804_state::op46)); // RW, reads keypad, writes controls the front panel leds.
	map(0x47, 0x47).mirror(0xB8).w(FUNC(ep804_state::op47)); // W eprom socket power and timing control
	// io bits: 1 0 x x x * * *
	// writes to 80, 88, 90, 98, a0, a8, b0, b8 all go to 80, same with reads
	map(0x80, 0x80).mirror(0x38).w(FUNC(ep804_state::acia_txd_w)); // (ACIA xmit reg)
	map(0x81, 0x81).mirror(0x38).r(FUNC(ep804_state::acia_rxd_r)); // (ACIA rcv reg)
	map(0x82, 0x82).mirror(0x38).w(FUNC(ep804_state::acia_reset_w)); // (ACIA reset reg)
	map(0x83, 0x83).mirror(0x38).r(FUNC(ep804_state::acia_status_r)); // (ACIA status reg)
	map(0x84, 0x84).mirror(0x38).w(FUNC(ep804_state::acia_command_w)); // (ACIA command reg)
	map(0x85, 0x85).mirror(0x38).r(FUNC(ep804_state::acia_command_r)); // (ACIA command reg)
	map(0x86, 0x86).mirror(0x38).w(FUNC(ep804_state::acia_control_w)); // (ACIA control reg)
	map(0x87, 0x87).mirror(0x38).r(FUNC(ep804_state::acia_control_r)); // (ACIA control reg)
	//map(0x80,0x87).mirror(0x38).shift(-1).rw("acia", FUNC(mos6551_device::read, FUNC(mos6551_device::write)); // this doesn't work since we lack a shift() command

}


/******************************************************************************
 Input Ports
******************************************************************************/
static INPUT_PORTS_START( digel804 )
	PORT_START("LINE0") /* KEY ROW 0, 'X1' */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTR/INCR") PORT_CODE(KEYCODE_ENTER) PORT_CHAR('^')

	PORT_START("LINE1") /* KEY ROW 1, 'X2' */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NEXT/DECR") PORT_CODE(KEYCODE_DOWN)  PORT_CHAR('V')

	PORT_START("LINE2") /* KEY ROW 2, 'X3' */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("REPT") PORT_CODE(KEYCODE_X)  PORT_CHAR('X')

	PORT_START("LINE3") /* KEY ROW 3, 'X4' */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CLR") PORT_CODE(KEYCODE_MINUS)   PORT_CHAR('-')

	PORT_START("MODE") // TODO, connects entirely separately from the keypad through some complicated latching logic
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("KEY") PORT_CODE(KEYCODE_K)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(digel804_state::mode_change), MODE_KEY)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("REM") PORT_CODE(KEYCODE_R)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(digel804_state::mode_change), MODE_REM)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SIM") PORT_CODE(KEYCODE_S)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(digel804_state::mode_change), MODE_SIM)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("OFF") PORT_CODE(KEYCODE_O)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(digel804_state::mode_change), MODE_OFF)

	PORT_START("DEBUG") // debug jumper on the board
	PORT_DIPNAME( 0x01, 0x01, "Debug Mode" )
	PORT_DIPSETTING( 0x01, DEF_STR( Off ) )
	PORT_DIPSETTING( 0x00, DEF_STR( On ) )
INPUT_PORTS_END

static DEVICE_INPUT_DEFAULTS_START( digel804_rs232_defaults )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_ODD )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

/******************************************************************************
 Machine Drivers
******************************************************************************/

void digel804_state::da_w(int state)
{
	m_key_intq = state ? 0 : 1;
	m_maincpu->set_input_line(0, (m_key_intq & m_acia_intq) ? CLEAR_LINE : ASSERT_LINE);
}

void digel804_state::acia_irq_w(int state)
{
	m_acia_intq = state ? 0 : 1;
	m_maincpu->set_input_line(0, (m_key_intq & m_acia_intq) ? CLEAR_LINE : ASSERT_LINE);
}

void ep804_state::ep804_acia_irq_w(int state)
{
}

void digel804_state::digel804(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 3.6864_MHz_XTAL/2); /* Z80A, X1(aka E0 on schematics): 3.6864Mhz */
	m_maincpu->set_addrmap(AS_PROGRAM, &digel804_state::z80_mem_804_1_4);
	m_maincpu->set_addrmap(AS_IO, &digel804_state::z80_io_1_4);
	config.set_maximum_quantum(attotime::from_hz(60));

	ROC10937(config, m_vfd); // RIGHT_TO_LEFT

	/* video hardware */
	config.set_default_layout(layout_digel804);

	MM74C923(config, m_kb, 0);
	m_kb->da_wr_callback().set(FUNC(digel804_state::da_w));
	m_kb->x1_rd_callback().set_ioport("LINE0");
	m_kb->x2_rd_callback().set_ioport("LINE1");
	m_kb->x3_rd_callback().set_ioport("LINE2");
	m_kb->x4_rd_callback().set_ioport("LINE3");

	/* acia */
	MOS6551(config, m_acia, 0);
	m_acia->set_xtal(3.6864_MHz_XTAL/2);
	m_acia->irq_handler().set(FUNC(digel804_state::acia_irq_w));
	m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_acia->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_acia->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "null_modem"));
	rs232.rxd_handler().set(m_acia, FUNC(mos6551_device::write_rxd));
	rs232.dsr_handler().set(m_acia, FUNC(mos6551_device::write_dsr));
	rs232.cts_handler().set(m_acia, FUNC(mos6551_device::write_cts));
	rs232.set_option_device_input_defaults("null_modem", DEVICE_INPUT_DEFAULTS_NAME(digel804_rs232_defaults));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(digel804_rs232_defaults));

	RAM(config, m_ram).set_default_size("256K").set_extra_options("32K,64K,128K");

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

void ep804_state::ep804(machine_config &config)
{
	digel804(config);

	/* basic machine hardware */
	/* Z80, X1(aka E0 on schematics): 3.6864Mhz */
	m_maincpu->set_addrmap(AS_PROGRAM, &ep804_state::z80_mem_804_1_2);
	m_maincpu->set_addrmap(AS_IO, &ep804_state::z80_io_1_2);

	m_acia->irq_handler().set(FUNC(ep804_state::ep804_acia_irq_w));

	m_ram->set_default_size("32K").set_extra_options("64K");
}



/******************************************************************************
 ROM Definitions
******************************************************************************/

/*
"Hardware Revisions"
For pcb 1.0, there are at least 6 hardware revisions (i.e. small changes/component changes/greenwire fixes).
The hardware revision is shown on the sticker on the bottom.

Known features:

1.0 - ?
1.1 - does not support driving pin 1 of the socket, i.e. max size is 27256; pin 1 is pulled high; several components unpopulated
1.2 - ?
1.3 - ?
1.4 - ?
1.5 - does support driving pin 1
1.6 - does support driving pin 1

*/

/*
For the 82S23A/MMI6330-equivalent mapper PROM at D30:

z80 a11 -> prom a0
z80 a12 -> prom a1
z80 a13 -> prom a2
z80 a14 -> prom a3
z80 a15 -> prom a4

prom d0 -> ram 6116 /CE (pin 18?) at d47
prom d1 -> ram 6116/64 /CE (pin 20) at d46
prom d2 -> ram 6116/64 /CE (pin 20) at d45
prom d3 -> ram 6116/64 /CE (pin 20) at d44
prom d4 -> ram 6116/64 /CE (pin 20) at d43
prom d5 -> rom /CE at d42
prom d6 -> rom /CE at d41
prom d7 -> N/C (unused)


On the v2.0 804-0197B mainboard, the added connector X9 (for the ram expansion daughterboard)
near to the mapper prom at D30 has the pinout:
 ___
|1 10
|2 9|
|3 8|
|4 7|
|5_6|

1 -> pin 15 of CD74HC374E at D23 & eprom socket pin A6 (i.e. op41 bit 6)
2 -> N/C
3 -> ram 6116/64 /CE (pin 20) of D45 & mapper prom D2 (pin 3)
4 -> ram 6116/64 /CE (pin 20) of D43 & mapper prom D4 (pin 5)
5 -> CE2 (pin 26) of all four ram chips (D43 D44 D45 D46)
6 -> +5v
7 -> ram 6116/64 /CE (pin 20) of D44 & mapper prom D3 (pin 4)
8 -> ram 6116/64 /CE (pin 20) of D46 & mapper prom D1 (pin 2)
9 -> N/C
10 -> mapper prom /CE (pin 15)

If the ram expansion is not installed (i.e. only 32k of base ram on the mainboard), there is a jumper present between pins 5 and 6
*/

ROM_START(digel804) // pcb v2.0; address mapper 804-1-4
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD("1-04__76f1.27128.d41", 0x0000, 0x4000, CRC(61b50b61) SHA1(ad717fcbf3387b0a8fb0546025d3c527792eb3f0))
	// the second rom here is loaded bizarrely: the first 3/4 appears at e000-f7ff and the last 1/4 appears at d800-dfff
	ROM_LOAD("2-04__d6cc.2764a.d42", 0xe000, 0x1800, CRC(098cb008) SHA1(9f04e12489ab5f714d2fd4af8158969421557e75))
	ROM_CONTINUE(0xd800, 0x800)
	ROM_REGION(0x20, "proms", 0)
	ROM_LOAD("804-1-4.82s23a.d30", 0x0000, 0x0020, CRC(f961beb1) SHA1(f2ec89375e656eeabc30246d42741cf718fb0f91)) // Address mapper prom, 82s23/mmi6330/tbp18sa030 equivalent 32x8 open collector
ROM_END

ROM_START(ep804) // pcb v1.0; address mapper 804-1-2
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_DEFAULT_BIOS("ep804_v1.6")
	ROM_SYSTEM_BIOS( 0, "ep804_v1.6", "Wavetek/Digelec EP804 FWv1.6") // hardware 1.1
	ROMX_LOAD("804-2__rev_1.6__07f7.hn482764g-4.d41", 0x0000, 0x2000, CRC(2d4c334c) SHA1(1be70ed5f4f315a8d2e38a17327a049e00fa174e), ROM_BIOS(0))
	ROMX_LOAD("804-3__rev_1.6__265c.hn482764g-4.d42", 0x2000, 0x2000, CRC(9c14906b) SHA1(41996039e604011c7c0f757397f82b6479ee3f62), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "ep804_v1.4", "Wavetek/Digelec EP804 FWv1.4") // hardware 1.1
	ROMX_LOAD("804-2_rev_1.4__7a7e.hn482764g-4.d41", 0x0000, 0x2000, CRC(fdc0d2e3) SHA1(da1bc1e8c4cb2a2d8cd2273f3e1a9f318ae8cb87), ROM_BIOS(1))
	ROMX_LOAD("804-3_rev_1.4__f240.2732.d42", 0x2000, 0x1000, CRC(29827e29) SHA1(4c7fadf81bcf32349a564d946f5d215de50315c5), ROM_BIOS(1))
	ROMX_LOAD("804-3_rev_1.4__f240.2732.d42", 0x3000, 0x1000, CRC(29827e29) SHA1(4c7fadf81bcf32349a564d946f5d215de50315c5), ROM_BIOS(1)) // load this twice
	ROM_SYSTEM_BIOS( 2, "ep804_v2.21", "Wavetek/Digelec EP804 FWv2.21") // hardware 1.5 NOTE: this may use the address mapper 804-1-3 which is not dumped!
	ROMX_LOAD("804-2_rev2.21__cs_ab50.hn482764g.d41", 0x0000, 0x2000, CRC(ffbc95f6) SHA1(b12aa97e23d546064f1d17aa9b90772017fec5ec), ROM_BIOS(2))
	ROMX_LOAD("804-3_rev2.21__cs_6b98.hn482764g.d42", 0x2000, 0x2000, CRC(a4acb9fe) SHA1(bbc7e3e2e6b3b1abe747380909dcddc985ef8d0d), ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "promicron2k_v2.3", "Celectronic Berlin/Digelec promicron 2000 FWv2.3") // hardware 1.6
	ROMX_LOAD("2023__1-03__be7d.m5l2764k.d41", 0x0000, 0x2000, CRC(8e5182f1) SHA1(e8409b6ace80fdaad862e6c06975aeabcf728f97), ROM_BIOS(3))
	ROMX_LOAD("2023__2-03__c73e.m5l2764k.d42", 0x2000, 0x2000, CRC(ff7d959b) SHA1(75718fc1d98969739911cc51b6d5fef74b530e36), ROM_BIOS(3))
	ROM_SYSTEM_BIOS( 4, "ep804_v1.7", "Wavetek/Digelec EP804 FWv1.7") // hardware 1.1, all labels handwritten
	ROMX_LOAD("804.2-1.7_3bc0.2764.d41", 0x0000, 0x2000, CRC(d39c2b51) SHA1(6e00d9c8f5037936618fd17475b72261f3e54826), ROM_BIOS(4))
	ROMX_LOAD("804-3-1.7_1c96.2764.d42", 0x2000, 0x2000, CRC(994df376) SHA1(93bbc48b72e2126a9f40312e66ca2a87c7f67bf6), ROM_BIOS(4))
	// on the promicron 2000, the bprom at d30 is an unlabeled TB18S030N part, but has the same contents as 804-1-2.mmi_6330-in.d30 below. it is possible the sticker fell off.
	ROM_REGION(0x20, "proms", 0)
	ROM_LOAD("804-1-2.mmi_6330-in.d30", 0x0000, 0x0020, CRC(30dd4721) SHA1(e4b2f5756118be4c8ab56c708dc4f42469c7e51b)) // Address mapper prom, 82s23/mmi6330/tbp18sa030 equivalent 32x8 open collector
ROM_END

} // anonymous namespace


/******************************************************************************
 Drivers
******************************************************************************/

//    YEAR  NAME      PARENT    COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY                 FULLNAME                        FLAGS
COMP( 1985, digel804, 0,        0,      digel804, digel804, digel804_state, empty_init, "Digelec, Inc",         "Digelec 804 EPROM Programmer", MACHINE_NOT_WORKING )
COMP( 1982, ep804,    digel804, 0,      ep804,    digel804, ep804_state,    empty_init, "Wavetek/Digelec, Inc", "EP804 EPROM Programmer",       MACHINE_NOT_WORKING )



digiblast.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

// a generic looking s3c2410 SoC setup, with very little of note in the system, and a NAND ROM in the cartriges
// if you copy the NAND plumbing from ghosteo.cpp (and add the ID command) the software begins to boot, but
// the NAND should be included in the cartridge bus device, not the driver.

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/arm7/arm7.h"
#include "machine/s3c2410.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class digiblast_state : public driver_device
{
public:
	digiblast_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_s3c2410(*this, "s3c2410")
		, m_system_memory(*this, "systememory")
		, m_cart(*this, "cartslot")
	{
	}

	void digiblast(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<s3c2410_device> m_s3c2410;
	required_shared_ptr<uint32_t> m_system_memory;

	required_device<generic_slot_device> m_cart;

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	void digiblast_map(address_map &map) ATTR_COLD;
};

void digiblast_state::digiblast_map(address_map &map)
{
	map(0x30000000, 0x31ffffff).ram().share("systememory").mirror(0x02000000);
}

static INPUT_PORTS_START( digiblast )
INPUT_PORTS_END

void digiblast_state::machine_start()
{
}

void digiblast_state::machine_reset()
{
}

DEVICE_IMAGE_LOAD_MEMBER(digiblast_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}


void digiblast_state::digiblast(machine_config &config)
{
	/* basic machine hardware */
	ARM9(config, m_maincpu, 200000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &digiblast_state::digiblast_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(455, 262);
	screen.set_visarea(0, 320-1, 0, 256-1);
	screen.set_screen_update("s3c2410", FUNC(s3c2410_device::screen_update));

	PALETTE(config, "palette").set_entries(256);

	S3C2410(config, m_s3c2410, 12000000);
	m_s3c2410->set_palette_tag("palette");
	m_s3c2410->set_screen_tag("screen");
	//m_s3c2410->core_pin_r_callback().set(FUNC(digiblast_state::s3c2410_core_pin_r));
	//m_s3c2410->gpio_port_r_callback().set(FUNC(digiblast_state::s3c2410_gpio_port_r));
	//m_s3c2410->gpio_port_w_callback().set(FUNC(digiblast_state::s3c2410_gpio_port_w));
	//m_s3c2410->i2c_scl_w_callback().set(FUNC(digiblast_state::s3c2410_i2c_scl_w));
	//m_s3c2410->i2c_sda_r_callback().set(FUNC(digiblast_state::s3c2410_i2c_sda_r));
	//m_s3c2410->i2c_sda_w_callback().set(FUNC(digiblast_state::s3c2410_i2c_sda_w));
	//m_s3c2410->nand_command_w_callback().set(FUNC(digiblast_state::s3c2410_nand_command_w));
	//m_s3c2410->nand_address_w_callback().set(FUNC(digiblast_state::s3c2410_nand_address_w));
	//m_s3c2410->nand_data_r_callback().set(FUNC(digiblast_state::s3c2410_nand_data_r));
	//m_s3c2410->nand_data_w_callback().set(FUNC(digiblast_state::s3c2410_nand_data_w));

	/* sound hardware */
	SPEAKER(config, "front").front_center();

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "digiblast_cart");
	m_cart->set_width(GENERIC_ROM8_WIDTH);
	m_cart->set_device_load(FUNC(digiblast_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("digiblast_cart");

}

ROM_START( digiblst )
	//ROM_REGION( 0x4200000, "flash", ROMREGION_ERASEFF )
ROM_END

} // Anonymous namespace

CONS( 2005, digiblst,  0, 0, digiblast,  digiblast,  digiblast_state, empty_init, "Nikko Entertainment B.V. / Grey Innovation", "digiBLAST", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



digijet.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/*************************************************************************

    drivers/digijet.cpp

    Skeleton driver for the Volkswagen Digijet series of automotive ECUs

    The Digijet Engine Control Unit (ECU) was used in Volkswagen vehicles
    from the early 1980s.

    Currently, the only dump is from a 1985 Volkswagen Vanagon (USA CA).

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

/*
    TODO:

    - Everything
*/

#include "emu.h"
#include "cpu/mcs48/mcs48.h"


namespace {

#define I8049_TAG   "i8049"

class digijet_state : public driver_device
{
public:
	digijet_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, I8049_TAG)
	{
	}

	void digijet(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;

	virtual void machine_start() override { }
	virtual void machine_reset() override { }
	void io_map(address_map &map) ATTR_COLD;
};

void digijet_state::io_map(address_map &map)
{
}

static INPUT_PORTS_START( digijet )
INPUT_PORTS_END

void digijet_state::digijet(machine_config &config)
{
	/* basic machine hardware */
	I8049(config, m_maincpu, XTAL(11'000'000));
	m_maincpu->set_addrmap(AS_IO, &digijet_state::io_map);
}

ROM_START( digijet )
	ROM_REGION( 0x800, I8049_TAG, 0 )
	ROM_LOAD( "vanagon_85_usa_ca.bin", 0x000, 0x800, CRC(2ed7c4c5) SHA1(ae48d8892b44fe76b48bcefd293c15cd47af3fba) ) // Volkswagen Vanagon, 1985, USA, California
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY       FULLNAME   FLAGS
CONS( 1985, digijet, 0,      0,      digijet, digijet, digijet_state, empty_init, "Volkswagen", "Digijet", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



digilog320.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/****************************************************************************

    Digilog 320

    Protocol analyzer

    Hardware:

    PCB 24-0710-02B:
    - Speaker
    - XTAL 1.344 MHz
    - R80186-10
    - XTAL 20 MHz
    - DS1241
    - HM62256LP-15 x4
    - AT27C512R x6

    PCB 24-0706-02C:
    - MB8877A
    - FDC9229BT
    - XTAL 16 MHz
    - P8251A
    - XTAL 3.6864 MHz
    - SCN2681AC1N40
    - HD46505SP-2
    - HM6264ALP-12 x2
    - AM27128DC "24-1140-00 A"
    - XTAL 5.659200 MHz
    - PAL labeled "20-1110-1 A"

    PCB 24-0709-02D:
    - XTAL 16 MHz
    - Z0840008PSC Z80 CPU
    - HM62256LP-12
    - Z8530H-8PC x2
    - AT27C256 "24-0196-01 C"
    - AM9519A-1PC
    - XTAL 2.688 MHz
    - XTAL 3.6864 MHz

    TODO:
    - Finish floppy hookup
    - SCC interrupts
    - Unknown DUART inputs/outputs
    - RS232
    - RTC
    - Artwork with LEDs

    Notes:
    - Everything here guessed, no manuals available

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

#include "emu.h"
#include "cpu/i86/i186.h"
#include "cpu/z80/z80.h"
#include "machine/am9519.h"
#include "digilog_kbd.h"
#include "machine/i8251.h"
#include "machine/mc68681.h"
#include "machine/nvram.h"
#include "machine/wd_fdc.h"
#include "machine/z80scc.h"
#include "video/mc6845.h"
#include "imagedev/floppy.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"


namespace {


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

class digilog320_state : public driver_device
{
public:
	digilog320_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_subcpu(*this, "subcpu"),
		m_uic(*this, "uic"),
		m_crtc(*this, "crtc"),
		m_palette(*this, "palette"),
		m_duart(*this, "duart"),
		m_scc(*this, "scc%u", 0U),
		m_fdc(*this, "fdc"),
		m_floppy(*this, "fdc:0"),
		m_vram(*this, "vram"),
		m_chargen(*this, "chargen")
	{ }

	void digilog320(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<i80186_cpu_device> m_maincpu;
	required_device<z80_device> m_subcpu;
	required_device<am9519_device> m_uic;
	required_device<mc6845_device> m_crtc;
	required_device<palette_device> m_palette;
	required_device<scn2681_device> m_duart;
	required_device_array<scc8530_device, 2> m_scc;
	required_device<mb8877_device> m_fdc;
	required_device<floppy_connector> m_floppy;
	required_shared_ptr<uint16_t> m_vram;
	required_region_ptr<uint8_t> m_chargen;

	void main_mem_map(address_map &map) ATTR_COLD;
	void main_io_map(address_map &map) ATTR_COLD;
	void sub_mem_map(address_map &map) ATTR_COLD;
	void sub_io_map(address_map &map) ATTR_COLD;

	MC6845_UPDATE_ROW(update_row);

	uint8_t fdc_r(offs_t offset);
	void fdc_w(offs_t offset, uint8_t data);
	void fdc_ctrl_w(uint8_t data);

	uint8_t subcpu_to_maincpu_r();
	void maincpu_to_subcpu_w(uint8_t data);
	uint8_t maincpu_to_subcpu_r();
	void subcpu_to_maincpu_w(uint8_t data);
	uint8_t maincpu_status_r();

	uint8_t m_subcpu_to_maincpu = 0;
	uint8_t m_maincpu_to_subcpu = 0;
	uint8_t m_maincpu_status = 0;
};


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

void digilog320_state::main_mem_map(address_map &map)
{
	map(0x00000, 0x1ffff).ram();
	map(0x80000, 0x83fff).ram().share("vram");
	map(0x90000, 0x91fff).ram().share("nvram");
	map(0xa0000, 0xfffff).rom().region("maincpu", 0);
}

void digilog320_state::main_io_map(address_map &map)
{
	map(0x000, 0x003).rw("usart", FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0x080, 0x09f).rw(m_duart, FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask16(0x00ff);
	map(0x100, 0x107).rw(FUNC(digilog320_state::fdc_r), FUNC(digilog320_state::fdc_w)).umask16(0x00ff);
	map(0x180, 0x180).w(m_crtc, FUNC(mc6845_device::address_w));
	map(0x182, 0x182).w(m_crtc, FUNC(mc6845_device::register_w));
	map(0x200, 0x200).w(FUNC(digilog320_state::fdc_ctrl_w));
	map(0x280, 0x280).rw(FUNC(digilog320_state::subcpu_to_maincpu_r), FUNC(digilog320_state::maincpu_to_subcpu_w));
}

void digilog320_state::sub_mem_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("subcpu", 0);
	map(0x8000, 0xffff).ram();
}

void digilog320_state::sub_io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x00).rw(m_uic, FUNC(am9519_device::data_r), FUNC(am9519_device::data_w));
	map(0x01, 0x01).rw(m_uic, FUNC(am9519_device::stat_r), FUNC(am9519_device::cmd_w));
	map(0x04, 0x07).rw(m_scc[0], FUNC(z80scc_device::dc_ab_r), FUNC(z80scc_device::dc_ab_w));
	map(0x08, 0x0b).rw(m_scc[1], FUNC(z80scc_device::dc_ab_r), FUNC(z80scc_device::dc_ab_w));
	map(0x10, 0x10).rw(FUNC(digilog320_state::maincpu_to_subcpu_r), FUNC(digilog320_state::subcpu_to_maincpu_w));
	map(0x11, 0x11).r(FUNC(digilog320_state::maincpu_status_r));
}


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

MC6845_UPDATE_ROW( digilog320_state::update_row )
{
	pen_t const *const pen = m_palette->pens();

	for (int x = 0; x < x_count; x++)
	{
		// attributes
		// 76------  unknown
		// --5-----  half intensity
		// ---4----  unknown
		// ----3---  reverse
		// -----2--  unknown
		// ------10  chargen high bits

		uint16_t const data = (m_vram[ma + x]);
		uint8_t gfx = m_chargen[((data & 0x3ff) << 4) | ra];
		uint8_t attr = data >> 8;

		if (x == cursor_x)
			gfx ^= 0xff;

		if (BIT(attr, 3))
			gfx ^= 0xff;

		// foreground/background colors
		rgb_t fg = BIT(attr, 5) ? pen[1] : pen[2];
		rgb_t bg = pen[0];

		// draw 8 pixels of the character
		for (int i = 0; i < 8; i++)
			bitmap.pix(y, x * 8 + i) = BIT(gfx, i) ? fg : bg;
	}
}

static const gfx_layout char_layout =
{
	8, 12,
	RGN_FRAC(1, 1),
	1,
	{ 0 },
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8 * 16
};

static GFXDECODE_START(chars)
	GFXDECODE_ENTRY("chargen", 0, char_layout, 0, 1)
GFXDECODE_END


//**************************************************************************
//  FLOPPY
//**************************************************************************

// either cpu too fast or fdc too slow, assume some waitstates to pass check
uint8_t digilog320_state::fdc_r(offs_t offset)
{
	m_maincpu->adjust_icount(-2);
	return m_fdc->read(offset);
}

void digilog320_state::fdc_w(offs_t offset, uint8_t data)
{
	m_maincpu->adjust_icount(-2);
	m_fdc->write(offset, data);
}

void digilog320_state::fdc_ctrl_w(uint8_t data)
{
	// 7654----  unknown (not used?)
	// ----3---  unknown (motor on?)
	// -----2--  side select
	// ------10  unknown (drive select?)

	logerror("fdc_ctrl_w: %02x\n", data);

	floppy_image_device *floppy = m_floppy->get_device();

	m_fdc->set_floppy(floppy);

	if (floppy)
	{
		floppy->ss_w(BIT(data, 2));

		// motor always on for now
		floppy->mon_w(0);
	}

	// set to mfm
	m_fdc->dden_w(0);
}

static void digilog320_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
}


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

uint8_t digilog320_state::subcpu_to_maincpu_r()
{
	logerror("subcpu_to_maincpu_r: %02x\n", m_subcpu_to_maincpu);

	m_maincpu_status &= ~0x40;
	m_duart->ip3_w(0);

	return m_subcpu_to_maincpu;
}

void digilog320_state::maincpu_to_subcpu_w(uint8_t data)
{
	logerror("maincpu_to_subcpu_w: %02x\n", data);

	m_maincpu_to_subcpu = data;

	m_duart->ip0_w(1);
	m_uic->ireq4_w(0);
}

uint8_t digilog320_state::maincpu_to_subcpu_r()
{
	logerror("maincpu_to_subcpu_r: %02x\n", m_maincpu_to_subcpu);

	m_duart->ip0_w(0);
	m_uic->ireq4_w(1);

	return m_maincpu_to_subcpu;
}

void digilog320_state::subcpu_to_maincpu_w(uint8_t data)
{
	logerror("subcpu_to_maincpu_w: %02x\n", data);

	m_subcpu_to_maincpu = data;

	m_maincpu_status |= 0x40;
	m_duart->ip3_w(1);
}

uint8_t digilog320_state::maincpu_status_r()
{
	// 7-------  unknown
	// -6------  maincpu ready to receive data (0 = ready)
	// --543210  unknown

	return m_maincpu_status;
}

void digilog320_state::machine_start()
{
	// register for save states
	save_item(NAME(m_subcpu_to_maincpu));
	save_item(NAME(m_maincpu_to_subcpu));
	save_item(NAME(m_maincpu_status));
}

void digilog320_state::machine_reset()
{
	m_maincpu_status = 0x00;
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void digilog320_state::digilog320(machine_config &config)
{
	I80186(config, m_maincpu, 20_MHz_XTAL); // R80186-10
	m_maincpu->set_addrmap(AS_PROGRAM, &digilog320_state::main_mem_map);
	m_maincpu->set_addrmap(AS_IO, &digilog320_state::main_io_map);

	Z80(config, m_subcpu, 16_MHz_XTAL / 2); // Z0840008PSC
	m_subcpu->set_addrmap(AS_PROGRAM, &digilog320_state::sub_mem_map);
	m_subcpu->set_addrmap(AS_IO, &digilog320_state::sub_io_map);
	m_subcpu->set_irq_acknowledge_callback(m_uic, FUNC(am9519_device::iack_cb));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	AM9519(config, m_uic, 0);
	m_uic->out_int_callback().set_inputline(m_subcpu, INPUT_LINE_IRQ0);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(5.6592_MHz_XTAL, 320, 0, 256, 262, 0, 192);
	screen.set_screen_update(m_crtc, FUNC(mc6845_device::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	GFXDECODE(config, "gfxdecode", m_palette, chars);

	MC6845(config, m_crtc, 5.6592_MHz_XTAL / 8); // HD46505SP-2
	m_crtc->set_char_width(8);
	m_crtc->set_show_border_area(false);
	m_crtc->set_update_row_callback(FUNC(digilog320_state::update_row));

	SCN2681(config, m_duart, 3.6864_MHz_XTAL);
	m_duart->irq_cb().set(m_maincpu, FUNC(i80186_cpu_device::int0_w));
	m_duart->outport_cb().set("usart", FUNC(i8251_device::write_txc)).bit(3);
	m_duart->outport_cb().append("usart", FUNC(i8251_device::write_rxc)).bit(3);

	I8251(config, "usart", 0);

	SCC8530(config, m_scc[0], 3.6864_MHz_XTAL);

	SCC8530(config, m_scc[1], 3.6864_MHz_XTAL);

	MB8877(config, m_fdc, 16_MHz_XTAL / 16);
	m_fdc->intrq_wr_callback().set(m_maincpu, FUNC(i80186_cpu_device::int3_w));
	m_fdc->drq_wr_callback().set(m_maincpu, FUNC(i80186_cpu_device::drq1_w));
	FLOPPY_CONNECTOR(config, "fdc:0", digilog320_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats);

	SOFTWARE_LIST(config, "floppy_list").set_original("digilog320");

	digilog_kbd_device &kbd(DIGILOG_KBD(config, "kbd"));
	kbd.tx_handler().set(m_duart, FUNC(scn2681_device::rx_a_w));
}


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

ROM_START( digilog320 )
	ROM_REGION(0x60000, "maincpu", 0)
	ROM_LOAD16_BYTE("24-0197-01_e.u33", 0x00000, 0x10000, CRC(d1be8c1f) SHA1(5cef9d54e341da4cfa6ca50939c889c6d502240a))
	ROM_LOAD16_BYTE("24-0197-02_e.u24", 0x00001, 0x10000, CRC(1bbe293d) SHA1(69d22456e6b20fc3ce1c5208af169310e414e955))
	ROM_LOAD16_BYTE("24-0197-03_e.u34", 0x20000, 0x10000, CRC(d492b6fd) SHA1(474220128e63133eeb8591401edb3ad65d71e027))
	ROM_LOAD16_BYTE("24-0197-04_e.u25", 0x20001, 0x10000, CRC(a5667746) SHA1(ebecd69ebf394365e9bedfcbe98d71ce4ec7feb1))
	ROM_LOAD16_BYTE("24-0197-05_e.u35", 0x40000, 0x10000, CRC(17273a19) SHA1(0c28b304e1447d4afd821b3b1a4fb6029bf2a24d))
	ROM_LOAD16_BYTE("24-0197-06_e.u26", 0x40001, 0x10000, CRC(cb29ca28) SHA1(8827de70c006250ba2989c4dc57e402ed222f948))

	ROM_REGION(0x8000, "subcpu", 0)
	ROM_LOAD("24-0196-01_c.u12", 0x0000, 0x8000, CRC(20291d21) SHA1(1151414531040af59f4f692aab31bfc04e7fc56c))

	ROM_REGION(0x4000, "chargen", 0)
	ROM_LOAD("24-1140-00_a.bin", 0x0000, 0x4000, CRC(7a4d0b82) SHA1(15952655cef77918a76c0c268b749be34b28634b))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME        PARENT      COMPAT  MACHINE     INPUT  CLASS             INIT        COMPANY    FULLNAME  FLAGS
COMP( 1988, digilog320, 0,          0,      digilog320, 0,     digilog320_state, empty_init, "Digilog", "320",    MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



digilog400.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/****************************************************************************

    Digilog 400

    Protocol analyzer

    Skeleton driver

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

#include "emu.h"
#include "cpu/i86/i186.h"
#include "digilog_kbd.h"
#include "emupal.h"


namespace {


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

class digilog400_state : public driver_device
{
public:
	digilog400_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_subcpu(*this, "subcpu")
	{ }

	void digilog400(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<i80186_cpu_device> m_maincpu;
	required_device<i80186_cpu_device> m_subcpu;

	void main_mem_map(address_map &map) ATTR_COLD;
	void main_io_map(address_map &map) ATTR_COLD;
	void sub_mem_map(address_map &map) ATTR_COLD;
	void sub_io_map(address_map &map) ATTR_COLD;
};


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

void digilog400_state::main_mem_map(address_map &map)
{
	map(0xd0000, 0xfffff).rom().region("maincpu", 0);
}

void digilog400_state::main_io_map(address_map &map)
{
}

void digilog400_state::sub_mem_map(address_map &map)
{
	map(0xe0000, 0xfffff).rom().region("subcpu", 0);
}

void digilog400_state::sub_io_map(address_map &map)
{
}


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

static const gfx_layout char_layout =
{
	8, 12,
	RGN_FRAC(1, 1),
	1,
	{ 0 },
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8 * 16
};

static GFXDECODE_START(chars)
	GFXDECODE_ENTRY("chargen", 0, char_layout, 0, 1)
GFXDECODE_END


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

void digilog400_state::machine_start()
{
}

void digilog400_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void digilog400_state::digilog400(machine_config &config)
{
	I80186(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &digilog400_state::main_mem_map);
	m_maincpu->set_addrmap(AS_IO, &digilog400_state::main_io_map);

	I80186(config, m_subcpu, 16_MHz_XTAL);
	m_subcpu->set_addrmap(AS_PROGRAM, &digilog400_state::sub_mem_map);
	m_subcpu->set_addrmap(AS_IO, &digilog400_state::sub_io_map);

	PALETTE(config, "palette", palette_device::MONOCHROME_HIGHLIGHT);

	GFXDECODE(config, "gfxdecode", "palette", chars);

	DIGILOG_KBD(config, "kbd");
}


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

ROM_START( digilog400 )
	ROM_REGION(0x30000, "maincpu", 0)
	ROM_LOAD16_BYTE("24-1155-02_j.bin", 0x00000, 0x08000, CRC(2c59f3f3) SHA1(7f2b611645ffd1d560e2fbdf76f8c5e3d07ea381))
	ROM_LOAD16_BYTE("24-1155-01_j.bin", 0x00001, 0x08000, CRC(f4e92608) SHA1(c10b72b287a9f25c3075a9b17648565a1ed3ffb1))
	ROM_LOAD16_BYTE("24-1155-04_j.bin", 0x10000, 0x08000, CRC(b5fb0ad6) SHA1(fa11efc45d7bdf8364ae7457519785b6638da222))
	ROM_LOAD16_BYTE("24-1155-03_j.bin", 0x10001, 0x08000, CRC(bef7d565) SHA1(a34e4798a7111ffea5aa1c608d192884cae6a80b))
	ROM_LOAD16_BYTE("24-1155-06_j.bin", 0x20000, 0x08000, CRC(a38d1a9b) SHA1(b5bf733757fe54d4dd5e876f46fdd674e57f9525))
	ROM_LOAD16_BYTE("24-1155-05_j.bin", 0x20001, 0x08000, CRC(e7238778) SHA1(a56613589886f713904e4e610292971f55afa67a))

	ROM_REGION(0x20000, "subcpu", 0)
	ROM_LOAD16_BYTE("24-1154-01_j.bin", 0x00000, 0x08000, CRC(a2424fb1) SHA1(1b7c76402f8644cd051899e49a96755429692626))
	ROM_LOAD16_BYTE("24-1154-03_j.bin", 0x00001, 0x08000, CRC(71821d4a) SHA1(d137df960f7ffd9c3e14258fc59e15f0997d56bc))
	ROM_LOAD16_BYTE("24-1154-02_j.bin", 0x10000, 0x08000, CRC(0d027d41) SHA1(0a4ffc0d68f632d70def8ff009dedad971b38d31))
	ROM_LOAD16_BYTE("24-1154-04_j.bin", 0x10001, 0x08000, CRC(e5e6fe2e) SHA1(0f130e808ed75efd7525ced5ae30193e60f33644))

	ROM_REGION(0x4000, "chargen", 0)
	ROM_LOAD("24-1140-00_a.bin", 0x0000, 0x4000, CRC(7a4d0b82) SHA1(15952655cef77918a76c0c268b749be34b28634b))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME        PARENT      COMPAT  MACHINE     INPUT  CLASS             INIT        COMPANY    FULLNAME  FLAGS
COMP( 1987, digilog400, 0,          0,      digilog400, 0,     digilog400_state, empty_init, "Digilog", "400",    MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



digitech_gsp5.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for DigiTech GSP 5 guitar effects processor.

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

#include "emu.h"
//#include "bus/midi/midi.h"
#include "cpu/m6800/m6801.h"
#include "machine/nvram.h"

namespace {

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

	void gsp5(machine_config &config);

private:
	void ls_w(offs_t offset, u8 data);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};

void gsp5_state::ls_w(offs_t offset, u8 data)
{
	logerror("%s: Writing $%02X to LS%c latch\n", machine().describe_context(), data, 'A' + offset);
}

void gsp5_state::mem_map(address_map &map)
{
	map(0x2000, 0x2006).mirror(0x1ff8).w(FUNC(gsp5_state::ls_w));
	map(0x4000, 0x5fff).ram().share("nvram");
	map(0x8000, 0xffff).rom().region("eprom", 0);
}

static INPUT_PORTS_START(gsp5)
INPUT_PORTS_END

void gsp5_state::gsp5(machine_config &config)
{
	HD6303R(config, m_maincpu, 4_MHz_XTAL); // HD6303RP
	m_maincpu->set_addrmap(AS_PROGRAM, &gsp5_state::mem_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // KM6264A-10 + 3V battery

	//DSP256(config, "dsp", 32_MHz_XTAL);
}

ROM_START(gsp5)
	ROM_REGION(0x8000, "eprom", 0)
	ROM_LOAD("gsp-5_v1.02_1190.u53", 0x0000, 0x8000, CRC(02868045) SHA1(2e0682e3020ef8a5e1aa0fa28ac84450e08cbf1b)) // 27C256
	// Socket for second EPROM (27C128) exists at U63 but is unpopulated
ROM_END

} // anonymous namespace

SYST(1989, gsp5, 0, 0, gsp5, gsp5, gsp5_state, empty_init, "DigiTech", "GSP 5 Guitar Effects Processor/Preamp", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



dim68k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

        Micro Craft Dimension 68000

        This computer had the facility for plug-in cards to emulate popular
        computers of the time such as the Apple II Plus, TRS80, Kaypro, etc.
        The floppy disk parameters could be set to be able to read the disks
        of the emulated systems, or you create any other format you wished.

        TODO:
        - Graphics display (including colour and video_control options)
        - RTC (is this a thing?  the manual indicates it just uses the DUART's timers to track time)
        - Centronics printer
        - Video-high
        - Video-reset
        - The plug-in boards
        - Emulator trap function

        DUART I/O port bits:
        INPUT:
        bit 0 = Centronics RDY
        bit 4 = RS-232C DCD

        OUTPUT:
        all bits = Centronics data

        Colors (from COLORDEM.BAS)
        0 = black
        1 = dark blue
        2 = red
        3 = magenta
        4 = brown
        5 = grey
        6 = orange
        7 = pink
        8 = dark aqua
        9 = blue
        A = grey 2
        B = light blue
        C = green
        D = aqua
        E = yellow
        F = white

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

#include "emu.h"
#include "dim68k_kbd.h"
#include "cpu/m68000/m68000.h"
#include "imagedev/floppy.h"
#include "machine/mc68681.h"
#include "machine/upd765.h"
#include "sound/spkrdev.h"
#include "video/mc6845.h"
#include "bus/pc_joy/pc_joy.h"
#include "bus/rs232/rs232.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class dim68k_state : public driver_device
{
public:
	dim68k_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_crtc(*this, "crtc")
		, m_speaker(*this, "speaker")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0U)
		, m_gamectrl(*this, "gamectrl")
		, m_ram(*this, "ram")
		, m_palette(*this, "palette")
		, m_p_chargen(*this, "chargen")
		, m_duart(*this, "duart")
		, m_bootview(*this, "bootview")
	{ }

	void dim68k(machine_config &config);

private:
	void dim68k_palette(palette_device &palette);
	u16 dim68k_fdc_r();
	u8 dim68k_game_switches_r(offs_t offset);
	u16 dim68k_speaker_r();
	u16 dim68k_banksw_r();
	void dim68k_banksw_w(u16 data);
	void dim68k_fdc_w(u16 data);
	void dim68k_printer_strobe_w(u16 data);
	void dim68k_speaker_w(u16 data);
	void dim68k_video_control_w(u16 data);
	void dim68k_video_high_w(u16 data);
	void dim68k_video_reset_w(u16 data);
	MC6845_UPDATE_ROW(crtc_update_row);

	void fdc_irq_w(int state);

	void mem_map(address_map &map) ATTR_COLD;

	bool m_speaker_bit = 0;
	u8 m_video_control = 0U;
	u8 m_fdc_irq = 0U;
	void machine_reset() override ATTR_COLD;
	void machine_start() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<mc6845_device> m_crtc;
	required_device<speaker_sound_device> m_speaker;
	required_device<upd765a_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<pc_joy_device> m_gamectrl;
	required_shared_ptr<uint16_t> m_ram;
	required_device<palette_device> m_palette;
	required_region_ptr<u8> m_p_chargen;
	required_device<scn2681_device> m_duart;
	memory_view m_bootview;
};

void dim68k_state::dim68k_palette(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t::black());
	palette.set_pen_color(1, rgb_t(0x40, 0x1c, 0xf7)); /* Dark Blue */
	palette.set_pen_color(2, rgb_t(0xa7, 0x0b, 0x40)); /* Dark Red */
	palette.set_pen_color(3, rgb_t(0xe6, 0x28, 0xff)); /* Purple */
	palette.set_pen_color(4, rgb_t(0x40, 0x63, 0x00)); /* Brown */
	palette.set_pen_color(5, rgb_t(0x80, 0x80, 0x80)); /* Dark Gray */
	palette.set_pen_color(6, rgb_t(0xe6, 0x6f, 0x00)); /* Orange */
	palette.set_pen_color(7, rgb_t(0xff, 0x8b, 0xbf)); /* Pink */
	palette.set_pen_color(8, rgb_t(0x00, 0x74, 0x40)); /* Dark Green */
	palette.set_pen_color(9, rgb_t(0x19, 0x90, 0xff)); /* Medium Blue */
	palette.set_pen_color(10, rgb_t(0x80, 0x80, 0x80)); /* Light Grey */
	palette.set_pen_color(11, rgb_t(0xbf, 0x9c, 0xff)); /* Light Blue */
	palette.set_pen_color(12, rgb_t(0x19, 0xd7, 0x00)); /* Light Green */
	palette.set_pen_color(13, rgb_t(0x58, 0xf4, 0xbf)); /* Aquamarine */
	palette.set_pen_color(14, rgb_t(0xbf, 0xe3, 0x08)); /* Yellow */
	palette.set_pen_color(15, rgb_t(0xff, 0xff, 0xff));  /* White */
};

u16 dim68k_state::dim68k_fdc_r()
{
	return 0;
}

u8 dim68k_state::dim68k_game_switches_r(offs_t offset)
// Reading the game port switches
// FFCC11 = switch 0; FFCC13 = switch 1, etc to switch 3
// FFCC19 = paddle 0; FFCC1B = paddle 1, etc to paddle 3
{
	return BIT(m_gamectrl->joy_port_r(), offset ^ 4) ? 0 : 0x80;
}

u16 dim68k_state::dim68k_speaker_r()
// Any read or write of this address will toggle the position of the speaker cone
{
	if (!machine().side_effects_disabled())
	{
		m_speaker_bit ^= 1;
		m_speaker->level_w(m_speaker_bit);
	}
	return 0;
}

void dim68k_state::dim68k_speaker_w(u16 data)
{
	m_speaker_bit ^= 1;
	m_speaker->level_w(m_speaker_bit);
}

void dim68k_state::fdc_irq_w(int state)
{
	if (state)
	{
		m_fdc_irq = 0;
	}
	else
	{
		m_fdc_irq = 0x80;
	}
}

void dim68k_state::dim68k_fdc_w(u16 data)
{
	m_fdc->tc_w(BIT(data, 5));

	if (BIT(data, 1))
	{
		m_floppy[BIT(data, 0)^1]->get_device()->mon_w(true);
	}
	else
	{
		m_floppy[BIT(data, 0)^1]->get_device()->mon_w(false);
	}
}

void dim68k_state::dim68k_video_high_w(u16 data)
// "write high byte of address in memory of start of display buffer"
{
}

void dim68k_state::dim68k_video_control_w(u16 data)
{
/* D7 0 = Hires/Graphics; 1= Lores/Text [not emulated yet]
   D6 0 = 8 dots per character; 1 = 7 dots [emulated]
   D5 0 = CRTC and CPU run Asynchronously; 1 = Synchronously [won't be emulated]
   D4,D3  Dot clock: 00=14MHz; 01=3.58MHz; 10=7MHz; 11=1.79MHz [emulated]
   D2 0 = Screen On; 1 = Off [emulated]
   D1 0 = Standard Chars & LoRes; 1 = Alternate Chars & HiRes [not emulated yet]
   D0 0 = Non-Mixed (all text or all Graphics); 1 = Mixed (Colour Graphics and Monochrome Text) [not emulated yet]
*/
	unsigned dots = (data & 0x40) ? 7 : 8;
	m_crtc->set_hpixels_per_column(dots);
	m_video_control = data;

	switch (data & 0x18)
	{
		case 0x00: m_crtc->set_unscaled_clock(XTAL(14'000'000) / dots);
					break;
		case 0x08: m_crtc->set_unscaled_clock(XTAL(3'579'545) / dots);
					break;
		case 0x10: m_crtc->set_unscaled_clock(XTAL(14'000'000) / dots / 2);
					break;
		case 0x18: m_crtc->set_unscaled_clock(XTAL(3'579'545) / dots / 2);
					break;
	}
}

void dim68k_state::dim68k_video_reset_w(u16 data)
{
}

void dim68k_state::dim68k_printer_strobe_w(u16 data)
// anything sent here will trigger a one-shot for a strobe pulse
{
}

u16 dim68k_state::dim68k_banksw_r()
{
	m_bootview.disable();
	return 0xffff;
}

void dim68k_state::dim68k_banksw_w(u16 data)
{
	m_bootview.disable();
}

void dim68k_state::mem_map(address_map &map)
{
	map(0x000000, 0x7fffff).ram().share("ram"); // 8 MiB of RAM
	map(0x000000, 0x00ffff).view(m_bootview);
	m_bootview[0](0x000000, 0x001fff).rom().region("bootrom", 0);
	// graphics VRAM is not used by the stub before the bankswitch occurs, so it's not mapped here
	m_bootview[0](0x008001, 0x008001).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	m_bootview[0](0x008003, 0x008003).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	m_bootview[0](0x008004, 0x008005).w(FUNC(dim68k_state::dim68k_video_high_w));
	m_bootview[0](0x008008, 0x008009).w(FUNC(dim68k_state::dim68k_video_control_w));
	m_bootview[0](0x00800a, 0x00800b).w(FUNC(dim68k_state::dim68k_video_reset_w));
	m_bootview[0](0x00dc00, 0x00dc01).rw(FUNC(dim68k_state::dim68k_banksw_r), FUNC(dim68k_state::dim68k_banksw_w));

	map(0xff0000, 0xff1fff).rom().region("bootrom", 0);
	map(0xff2000, 0xff7fff).ram(); // Graphics Video RAM
	map(0xff8001, 0xff8001).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0xff8003, 0xff8003).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0xff8004, 0xff8005).w(FUNC(dim68k_state::dim68k_video_high_w));
	map(0xff8008, 0xff8009).w(FUNC(dim68k_state::dim68k_video_control_w));
	map(0xff800a, 0xff800b).w(FUNC(dim68k_state::dim68k_video_reset_w));
	map(0xff8800, 0xff8fff).rom().region("cop6512", 0); // slot 1 controller rom
	map(0xff9000, 0xff97ff).rom().region("copz80", 0); // slot 2 controller rom
	map(0xff9800, 0xff9fff).rom().region("cop8086", 0); // slot 3 controller rom
#if 0
	map(0xffa000, 0xffa7ff).rom(); // slot 4 controller rom
	map(0xffa800, 0xffafff).rom(); // slot 5 controller rom
	map(0xffb000, 0xffb7ff).rom(); // slot 6 controller rom
#endif
	map(0xffc400, 0xffc41f).rw(m_duart, FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask16(0x00ff);
	map(0xffc800, 0xffc801).rw(FUNC(dim68k_state::dim68k_speaker_r), FUNC(dim68k_state::dim68k_speaker_w));
	map(0xffcc00, 0xffcc00).w(m_gamectrl, FUNC(pc_joy_device::joy_port_w));
	map(0xffcc10, 0xffcc1f).r(FUNC(dim68k_state::dim68k_game_switches_r)).umask16(0x00ff);
	map(0xffd000, 0xffd003).m(m_fdc, FUNC(upd765a_device::map)).umask16(0x00ff); // NEC uPD765A
	map(0xffd000, 0xffd000).lr8([this]() -> u8 { return m_fdc_irq; }, "free0");
	map(0xffd004, 0xffd005).rw(FUNC(dim68k_state::dim68k_fdc_r), FUNC(dim68k_state::dim68k_fdc_w));
	//map(0x00ffd400, 0x00ffd403) emulation trap control
	map(0xffd800, 0xffd801).w(FUNC(dim68k_state::dim68k_printer_strobe_w));
	map(0xffdc00, 0xffdc01).rw(FUNC(dim68k_state::dim68k_banksw_r), FUNC(dim68k_state::dim68k_banksw_w));
}

/* Input ports */
static INPUT_PORTS_START( dim68k )
INPUT_PORTS_END


void dim68k_state::machine_reset()
{
	m_bootview.select(0);
	m_fdc_irq = 0;
}

// Text-only; graphics isn't emulated yet. Need to find out if hardware cursor is used.
MC6845_UPDATE_ROW( dim68k_state::crtc_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	uint16_t chr16=0x2020; // set to spaces if screen is off
	uint32_t *p = &bitmap.pix(y);
	u8 screen_on = ~m_video_control & 4;
	u8 dot8 = ~m_video_control & 40;

	// need to divide everything in half to cater for 16-bit reads
	x_count /= 2;
	ma /= 2;
	u8 xx = 0;

	for (u8 x = 0; x < x_count; x++)
	{
		u8 inv, chr, gfx;
		if (screen_on)
			chr16 = m_ram[ma+x]; // reads 2 characters

		inv=0;
		if (xx == cursor_x && screen_on) inv=0xff;
		xx++;

		chr = chr16>>8;
		if (m_video_control & 0x80)
		{
			gfx = m_p_chargen[(chr<<4) | ra] ^ inv ^ ((chr & 0x80) ? 0xff : 0);
			*p++ = palette[BIT(gfx, 7)*15];
			*p++ = palette[BIT(gfx, 6)*15];
			*p++ = palette[BIT(gfx, 5)*15];
			*p++ = palette[BIT(gfx, 4)*15];
			*p++ = palette[BIT(gfx, 3)*15];
			*p++ = palette[BIT(gfx, 2)*15];
			*p++ = palette[BIT(gfx, 1)*15];
			if (dot8) *p++ = palette[BIT(gfx, 0)*15];
		}
		else
		{
			*p++ = palette[(chr>>4) & 0xf];
			*p++ = palette[(chr>>4) & 0xf];
			*p++ = palette[(chr>>4) & 0xf];
			*p++ = palette[(chr>>4) & 0xf];
			*p++ = palette[chr & 0xf];
			*p++ = palette[chr & 0xf];
			*p++ = palette[chr & 0xf];
			if (dot8) *p++ = palette[chr & 0xf];
		}

		inv = 0;
		if (xx == cursor_x) inv=0xff;
		xx++;

		chr = chr16;
		if (m_video_control & 0x80)
		{
			gfx = m_p_chargen[(chr << 4) | ra] ^ inv ^ ((chr & 0x80) ? 0xff : 0);
			*p++ = palette[BIT(gfx, 7)*15];
			*p++ = palette[BIT(gfx, 6)*15];
			*p++ = palette[BIT(gfx, 5)*15];
			*p++ = palette[BIT(gfx, 4)*15];
			*p++ = palette[BIT(gfx, 3)*15];
			*p++ = palette[BIT(gfx, 2)*15];
			*p++ = palette[BIT(gfx, 1)*15];
			if (dot8) *p++ = palette[BIT(gfx, 0)*15];
		}
		else
		{
			*p++ = palette[(chr >> 4) & 0xf];
			*p++ = palette[(chr >> 4) & 0xf];
			*p++ = palette[(chr >> 4) & 0xf];
			*p++ = palette[(chr >> 4) & 0xf];
			*p++ = palette[chr & 0xf];
			*p++ = palette[chr & 0xf];
			*p++ = palette[chr & 0xf];
			if (dot8) *p++ = palette[chr & 0xf];
		}
	}
}

/* F4 Character Displayer */
static const gfx_layout dim68k_charlayout =
{
	8, 16,                  /* 8 x 16 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_dim68k )
	GFXDECODE_ENTRY( "chargen", 0x0000, dim68k_charlayout, 0, 1 )
GFXDECODE_END

static void dim68k_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

void dim68k_state::machine_start()
{
	save_item(NAME(m_speaker_bit));
	save_item(NAME(m_video_control));
}

void dim68k_state::dim68k(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, 14.318181_MHz_XTAL / 2); // part rated for 8 MHz; clock verified from manual
	m_maincpu->set_addrmap(AS_PROGRAM, &dim68k_state::mem_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(14.318181_MHz_XTAL, 896, 0, 640, 279, 0, 224);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	PALETTE(config, m_palette, FUNC(dim68k_state::dim68k_palette), 16);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_dim68k);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* Devices */
	UPD765A(config, m_fdc, 16_MHz_XTAL / 4, true, true); // clocked through FDC9229BT; options unknown
	FLOPPY_CONNECTOR(config, m_floppy[0], dim68k_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[1], dim68k_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	m_fdc->intrq_wr_callback().set(FUNC(dim68k_state::fdc_irq_w));

	MC6845(config, m_crtc, 14.318181_MHz_XTAL / 8);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(dim68k_state::crtc_update_row));

	SCN2681(config, m_duart, 3.6864_MHz_XTAL);

	DIM68K_KEYBOARD(config, "keyboard").txd_callback().set(m_duart, FUNC(scn2681_device::rx_a_w));

	PC_JOY(config, m_gamectrl);

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_duart, FUNC(scn2681_device::rx_b_w));
	rs232.dcd_handler().set(m_duart, FUNC(scn2681_device::ip4_w));

	m_duart->b_tx_cb().set(rs232, FUNC(rs232_port_device::write_txd));

	// software lists
	SOFTWARE_LIST(config, "flop_list").set_original("dim68k");
}

/*
68000

MC101A  82S100
MC102B  82S100
MC103E  2732A
MC104   2732A   label "4050" underneath
MC105   2732A   char gen

6512

MC106   82LS135 U24
MC107   82LS135 U20
MC108   82S137  U23
MC109   82S131  U16
MC110   82LS135 U35

Z80

MC111   82S123  U11

8086

MC112   82LS135 U18
MC113   82S153  U16
*/
/* ROM definition */
ROM_START( dim68k )
	ROM_REGION16_BE( 0x2000, "bootrom", ROMREGION_ERASEFF )
	ROM_LOAD16_BYTE( "mc103e.bin", 0x0001, 0x1000, CRC(4730c902) SHA1(5c4bb79ad22def721a22eb63dd05e0391c8082be))
	ROM_LOAD16_BYTE( "mc104.bin",  0x0000, 0x1000, CRC(14b04575) SHA1(43e15d9ebe1c9c1bf1bcfc1be3899a49e6748200))

	ROM_REGION( 0x1000, "chargen", ROMREGION_ERASEFF )
	ROM_LOAD( "mc105e.bin", 0x0000, 0x1000, CRC(7a09daa8) SHA1(844bfa579293d7c3442fcbfa21bda75fff930394))

	// The remaining roms may not be in the correct positions or being loaded correctly
	ROM_REGION16_BE( 0x1000, "cop6512", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD( "mc106.bin", 0x0000, 0x0100, CRC(11530d8a) SHA1(e3eae266535383bcaee2d84d7bed6052d40e4e4a))
	ROM_LOAD16_WORD( "mc107.bin", 0x0100, 0x0100, CRC(966db11b) SHA1(3c3105ac842602d8e01b0f924152fd672a85f00c))
	ROM_LOAD16_WORD( "mc108.bin", 0x0200, 0x0400, CRC(687f9b0a) SHA1(ed9f1265b25f89f6d3cf8cd0a7b0fb73cb129f9f))
	ROM_LOAD16_WORD( "mc109.bin", 0x0600, 0x0200, CRC(4a857f98) SHA1(9f2bbc2171fc49f65aa798c9cd7799a26afd2ddf))
	ROM_LOAD16_WORD( "mc110.bin", 0x0800, 0x0100, CRC(e207b457) SHA1(a8987ba3d1bbdb3d8b3b11cec90c532ff09e762e))

	ROM_REGION16_BE( 0x1000, "copz80", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD( "mc111.bin", 0x0000, 0x0020, CRC(6a380057) SHA1(6522a7b3e0af9db14a6ed04d4eec3ee6e44c2dab))

	ROM_REGION16_BE( 0x1000, "cop8086", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD( "mc112.bin", 0x0000, 0x0100, CRC(dfd4cdbb) SHA1(a7831d415943fa86c417066807038bccbabb2573))
	ROM_LOAD( "mc113.bin", 0x0100, 0x00ef, CRC(594bdf05) SHA1(36db911a27d930e023fa12683e86e9eecfffdba6))

	ROM_REGION( 0x1000, "mb", ROMREGION_ERASEFF )   // mainboard unknown
	ROM_LOAD( "mc102.bin", 0x0000, 0x00fa, CRC(38e2abac) SHA1(0d7e730b46fc162764c69c51dea3bbe8337b1a7d))
	ROM_LOAD( "mc101.bin", 0x0100, 0x00fa, CRC(caffb3a0) SHA1(36f5140b306565794c4d856e0c20589b8f2a37f4))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY        FULLNAME           FLAGS
COMP( 1984, dim68k, 0,      0,      dim68k,  dim68k, dim68k_state, empty_init, "Micro Craft", "Dimension 68000", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



dm7000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Lukasz Markowski
/***************************************************************************

        Dream Multimedia Dreambox 7000/5620/500

        20/03/2010 Skeleton driver.


    DM7000 -    CPU STB04500 at 252 MHz
                RAM 64MB
                Flash 8MB
                1 x DVB-S
                1 x IDE interface
                1 x Common Interface (CI)
                1 x Compact flash
                2 x Smart card reader
                1 x USB
                1 x RS232
                1 x LAN 100Mbit/s
                1 x LCD display

    DM56x0 -    CPU STB04500 at 252 MHz
                RAM 64MB
                Flash 8MB
                1 x DVB-S
                2 x Common Interface (CI)
                1 x Smart card reader
                1 x LAN 100Mbit/s (just on 5620)
                1 x LCD display

    DM500 -     CPU STBx25xx at 252 MHz
                RAM 96MB
                Flash 32MB
                1 x DVB-S
                1 x Smart card reader
                1 x LAN 100Mbit/s

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

#include "emu.h"

#include "cpu/powerpc/ppc.h"
#include "machine/terminal.h"
#include "screen.h"

#define VERBOSE ( 1 )
#include "logmacro.h"

namespace {

class dm7000_state : public driver_device
{
public:
	dm7000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_terminal(*this, "terminal")
	{
	}

	void dm7000(machine_config &config);

private:
	required_device<ppc4xx_device> m_maincpu;
	required_device<generic_terminal_device> m_terminal;

	void dm7000_iic0_w(offs_t offset, uint8_t data);
	uint8_t dm7000_iic0_r(offs_t offset);
	void dm7000_iic1_w(offs_t offset, uint8_t data);
	uint8_t dm7000_iic1_r(offs_t offset);

	void dm7000_scc0_w(offs_t offset, uint8_t data);
	uint8_t dm7000_scc0_r(offs_t offset);
	void kbd_put(u8 data);
	uint8_t m_scc0_lcr = 0U;
	uint8_t m_scc0_lsr = 0U;
	uint8_t m_term_data = 0U;


	void dm7000_gpio0_w(offs_t offset, uint8_t data);
	uint8_t dm7000_gpio0_r(offs_t offset);

	void dm7000_scp0_w(offs_t offset, uint8_t data);
	uint8_t dm7000_scp0_r(offs_t offset);

	void dm7000_enet_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t dm7000_enet_r(offs_t offset);

	uint32_t dcr_r(offs_t offset);
	void dcr_w(offs_t offset, uint32_t data);


	uint16_t          m_enet_regs[32]{};

	uint32_t          dcr[1024]{};
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;
	uint32_t screen_update_dm7000(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void dm7000_mem(address_map &map) ATTR_COLD;
};

/* */
#define UART_DLL    0
#define UART_RBR    0
#define UART_THR    0
#define UART_DLH    1
#define UART_IER    1
#define UART_IIR    2
#define UART_FCR    2
#define UART_LCR    3
#define     UART_LCR_DLAB   0x80
#define UART_MCR    4
#define UART_LSR    5
#define     UART_LSR_TEMT   0x20
#define     UART_LSR_THRE   0x40
#define UART_MSR    6
#define UART_SCR    7

/* */
#define SCP_SPMODE 0
#define SCP_RXDATA 1
#define SCP_TXDATA 2
#define SCP_SPCOM 3
#define SCP_STATUS 4
#define     SCP_STATUS_RXRDY 1
#define SCP_CDM 6

/* STB045xxx DCRs */

#define DCRSTB045_CICVCR            0x033       /* CIC Video Control Register */
#define DCRSTB045_SCCR              0x120       /* Serial Clock Control Register */
#define DCRSTB045_VIDEO_CNTL        0x140       /* Video Control Register */
#define DCRSTB045_CMD_STAT          0x14a       /* Command status */
#define DCRSTB045_DISP_MODE         0x154       /* Display Mode Register */
#define DCRSTB045_FRAME_BUFR_BASE   0x179       /* Frame Buffers Base Address Register */

uint8_t dm7000_state::dm7000_iic0_r(offs_t offset)
{
	uint8_t data = 0; // dummy
	LOG("%s: (IIC0) %08X -> %08X\n", machine().describe_context(), 0x40030000 + offset, data);
	return data;
}

void dm7000_state::dm7000_iic0_w(offs_t offset, uint8_t data)
{
	LOG("%s: (IIC0) %08X <- %08X\n", machine().describe_context(), 0x40030000 + offset, data);
}

uint8_t dm7000_state::dm7000_iic1_r(offs_t offset)
{
	uint8_t data = 0; // dummy
	LOG("%s: (IIC1) %08X -> %08X\n", machine().describe_context(), 0x400b0000 + offset, data);
	return data;
}

void dm7000_state::dm7000_iic1_w(offs_t offset, uint8_t data)
{
	LOG("%s: (IIC1) %08X <- %08X\n", machine().describe_context(), 0x400b0000 + offset, data);
}

uint8_t dm7000_state::dm7000_scc0_r(offs_t offset)
{
	uint8_t data = 0;
	switch(offset) {
		case UART_THR:
			data = m_term_data;
			if(m_term_data == 0xd) {
				m_term_data = 0xa;
			} else {
				m_term_data = 0;
				m_scc0_lsr = 0;
			}
			break;
		case UART_LSR:
			data = UART_LSR_THRE | UART_LSR_TEMT | m_scc0_lsr;
			break;
	}
	LOG("%s: (SCC0) %08X -> %08X\n", machine().describe_context(), 0x40040000 + offset, data);
	return data;
}

void dm7000_state::dm7000_scc0_w(offs_t offset, uint8_t data)
{
	switch(offset) {
		case UART_THR:
			if(!(m_scc0_lcr & UART_LCR_DLAB)) {
				m_terminal->write(data);
				m_scc0_lsr = 1;
			}
			break;
		case UART_LCR:
			m_scc0_lcr = data;
			break;
	}
	LOG("%s: (SCC0) %08X <- %08X\n", machine().describe_context(), 0x40040000 + offset, data);
}

uint8_t dm7000_state::dm7000_gpio0_r(offs_t offset)
{
	uint8_t data = 0; // dummy
	LOG("%s: (GPIO0) %08X -> %08X\n", machine().describe_context(), 0x40060000 + offset, data);
	return data;
}

void dm7000_state::dm7000_gpio0_w(offs_t offset, uint8_t data)
{
	LOG("%s: (GPIO0) %08X <- %08X\n", machine().describe_context(), 0x40060000 + offset, data);
}

uint8_t dm7000_state::dm7000_scp0_r(offs_t offset)
{
	uint8_t data = 0; // dummy
	switch(offset) {
		case SCP_STATUS:
			data = SCP_STATUS_RXRDY;
			break;
	}
	LOG("%s: (SCP0) %08X -> %08X\n", machine().describe_context(), 0x400c0000 + offset, data);
	return data;
}

void dm7000_state::dm7000_scp0_w(offs_t offset, uint8_t data)
{
	LOG("%s: (SCP0) %08X <- %08X\n", machine().describe_context(), 0x400c0000 + offset, data);
	switch(offset) {
		case SCP_TXDATA:
			//printf("%02X ", data);
			break;
	}
}

uint16_t dm7000_state::dm7000_enet_r(offs_t offset)
{
	uint16_t data;
	switch (offset) {
		case 0x01:
			data = 0x1801;
			break;
		case 0x05:
			data = 0x3330;
			break;
		case 0x07:
			data = 0x3300;
			break;
		default:
			data = m_enet_regs[offset];
			break;
	}
	LOG("%s: (ENET) %08X -> %08X\n", machine().describe_context(), 0x72000600 + (offset), data);
	return data;
}

void dm7000_state::dm7000_enet_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	LOG("%s: (ENET) %08X <- %08X\n", machine().describe_context(), 0x72000600 + (offset), data);
	COMBINE_DATA(&m_enet_regs[offset]);
}

/*
 Memory map for the IBM "Redwood-4" STB03xxx evaluation board.

 The  STB03xxx internal i/o addresses don't work for us 1:1,
 so we need to map them at a well know virtual address.

 4000 000x   uart1
 4001 00xx   ppu
 4002 00xx   smart card
 4003 000x   iic
 4004 000x   uart0
 4005 0xxx   timer
 4006 00xx   gpio
 4007 00xx   smart card
 400b 000x   iic
 400c 000x   scp
 400d 000x   modem

 STBx25xx

 4000 000x   Serial1 Controller
 4001 000x   Serial2 Controller
 4002 00xx   Smart Card Interface 0
 4003 000x   IIC Interface 0
 4004 000x   Serial0 Controller
 4005 0xxx   General Purpose Timers
 4006 00xx   General Purpose Input / Output
 4007 00xx   Smart Card Interface 1
 400c 000x   Serial Controller Port
 400d 00xx   Synchronous Serial Port

 STB04xxx

 4000 00xx   Serial1/Infrared Controller
 4001 00xx   Universal Serial Bus
 4002 00xx   Smart Card Interface 0
 4003 000x   IIC Interface 0
 4004 000x   Serial0/Uart750 Controller
 4005 0xxx   General Purpose Timers
 4006 00xx   General Purpose Input / Output
 4007 00xx   Smart Card Interface 1
 400b 000x   IIC Interface 1
 400c 000x   Serial Controller Port
 400d 00xx   Synchronous Serial Port
 400e 000x   Serial2/UART750 Controller
 400f 0xxx   IDE Controller

*/
void dm7000_state::dm7000_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000000, 0x01ffffff).ram();                                     // RAM page 0 - 32MB
	map(0x20000000, 0x21ffffff).ram();                                     // RAM page 1 - 32MB

	map(0x40030000, 0x4003000f).rw(FUNC(dm7000_state::dm7000_iic0_r), FUNC(dm7000_state::dm7000_iic0_w));
	map(0x40040000, 0x40040007).rw(FUNC(dm7000_state::dm7000_scc0_r), FUNC(dm7000_state::dm7000_scc0_w));
	map(0x40060000, 0x40060047).rw(FUNC(dm7000_state::dm7000_gpio0_r), FUNC(dm7000_state::dm7000_gpio0_w));
	map(0x400b0000, 0x400b000f).rw(FUNC(dm7000_state::dm7000_iic1_r), FUNC(dm7000_state::dm7000_iic1_w));
	map(0x400c0000, 0x400c0007).rw(FUNC(dm7000_state::dm7000_scp0_r), FUNC(dm7000_state::dm7000_scp0_w));

	/* ENET - ASIX AX88796 */
	map(0x72000300, 0x720003ff).rw(FUNC(dm7000_state::dm7000_enet_r), FUNC(dm7000_state::dm7000_enet_w));

	map(0x7f800000, 0x7ffdffff).rom().region("user2", 0);
	map(0x7ffe0000, 0x7fffffff).rom().region("user1", 0);
	//map(0xfffe0000, 0xffffffff).rom().region("user1",0);
}

/* Input ports */
static INPUT_PORTS_START( dm7000 )
INPUT_PORTS_END


void dm7000_state::machine_reset()
{
	dcr[DCRSTB045_CICVCR] = 0x00000001;
	dcr[DCRSTB045_SCCR] = 0x00420080 /* default for serial divs */ | 0x3f /* undocumented?? used to print clocks */;
	dcr[DCRSTB045_VIDEO_CNTL] = 0x00009000;
	dcr[DCRSTB045_DISP_MODE] = 0x00880000;
	dcr[DCRSTB045_FRAME_BUFR_BASE] = 0x0f000000;
	m_scc0_lsr = UART_LSR_THRE | UART_LSR_TEMT;

	m_maincpu->ppc4xx_set_dcr_read_handler(read32sm_delegate(*this, FUNC(dm7000_state::dcr_r)));
	m_maincpu->ppc4xx_set_dcr_write_handler(write32sm_delegate(*this, FUNC(dm7000_state::dcr_w)));
}

void dm7000_state::video_start()
{
}

uint32_t dm7000_state::screen_update_dm7000(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

uint32_t dm7000_state::dcr_r(offs_t offset)
{
	osd_printf_debug("DCR %03X read\n", offset);
	if(offset>=1024) {printf("get %04X\n", offset); return 0;} else
	switch(offset) {
		case DCRSTB045_CMD_STAT:
			return 0; // assume that video dev is always ready
		default:
			return dcr[offset];
	}

}

void dm7000_state::dcr_w(offs_t offset, uint32_t data)
{
	osd_printf_debug("DCR %03X write = %08X\n", offset, data);
	if(offset>=1024) {printf("get %04X\n", offset); } else
	dcr[offset] = data;
}

void dm7000_state::kbd_put(u8 data)
{
	//printf("%02X\n", data);
	m_term_data = data;
	m_scc0_lsr = 1;
}

void dm7000_state::dm7000(machine_config &config)
{
	/* basic machine hardware */
	PPC405GP(config, m_maincpu, 252000000 / 10); // Should be PPC405D4?
	// Slowed down 10 times in order to get normal response for now
	m_maincpu->set_bus_frequency(252000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &dm7000_state::dm7000_mem);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	screen.set_screen_update(FUNC(dm7000_state::screen_update_dm7000));

	GENERIC_TERMINAL(config, m_terminal, 0);
	m_terminal->set_keyboard_callback(FUNC(dm7000_state::kbd_put));
}


/* ROM definition */
ROM_START( dm7000 )
	ROM_REGION( 0x20000, "user1", ROMREGION_32BIT | ROMREGION_BE  )
	ROMX_LOAD("dm7000.bin", 0x0000, 0x20000, CRC(8a410f67) SHA1(9d6c9e4f5b05b28453d3558e69a207f05c766f54), ROM_GROUPWORD)
	ROM_REGION( 0x800000, "user2", ROMREGION_32BIT | ROMREGION_BE | ROMREGION_ERASEFF  )
	ROM_LOAD("rel108_dm7000.img", 0x0000, 0x5e0000, CRC(e78b6407) SHA1(aaa786d341c629eec92fcf04bfafc1de43f6dabf))
ROM_END

ROM_START( dm5620 )
	ROM_REGION( 0x20000, "user1", ROMREGION_32BIT | ROMREGION_BE  )
	ROMX_LOAD("dm5620.bin", 0x0000, 0x20000, CRC(ccddb822) SHA1(3ecf553ced0671599438368f59d8d30df4d13ade), ROM_GROUPWORD)
	ROM_REGION( 0x800000, "user2", ROMREGION_32BIT | ROMREGION_BE | ROMREGION_ERASEFF  )
	ROM_LOAD("rel106_dm5620.img", 0x0000, 0x57b000, CRC(2313d71d) SHA1(0d3d99ab3b3266624f237b7b67e045d7910c44a5))
ROM_END

ROM_START( dm500 )
	ROM_REGION( 0x20000, "user1", ROMREGION_32BIT | ROMREGION_BE )
	ROM_SYSTEM_BIOS( 0, "alps", "Alps" )
	ROMX_LOAD("dm500-alps-boot.bin",   0x0000, 0x20000, CRC(daf2da34) SHA1(68f3734b4589fcb3e73372e258040bc8b83fd739), ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "phil", "Philips" )
	ROMX_LOAD("dm500-philps-boot.bin", 0x0000, 0x20000, CRC(af3477c7) SHA1(9ac918f6984e6927f55bea68d6daaf008787136e), ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(1))
	ROM_REGION( 0x800000, "user2", ROMREGION_32BIT | ROMREGION_BE | ROMREGION_ERASEFF  )
	ROM_LOAD("rel108_dm500.img", 0x0000, 0x5aa000, CRC(44be2376) SHA1(1f360572998b1bc4dc10c5210a2aed573a75e2fa))
ROM_END

} // anonymous namespace

/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY             FULLNAME         FLAGS
SYST( 2003, dm7000, 0,      0,      dm7000,  dm7000, dm7000_state, empty_init, "Dream Multimedia", "Dreambox 7000", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
SYST( 2004, dm5620, dm7000, 0,      dm7000,  dm7000, dm7000_state, empty_init, "Dream Multimedia", "Dreambox 5620", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
SYST( 2006, dm500,  dm7000, 0,      dm7000,  dm7000, dm7000_state, empty_init, "Dream Multimedia", "Dreambox 500",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



dmax8000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/******************************************************************************************************************

Datamax 8000

2016-07-24 Skeleton driver

This computer was designed and made by Datamax Pty Ltd, in Manly, Sydney, Australia.

It could have up to 4x 8 inch floppy drives, in the IBM format.
There were a bunch of optional extras such as 256kB RAM, numeric coprocessor, RTC, etc.


ToDo:
- Parallel port
- Centronics port
- AUX serial port
- FDC/FDD/HD setup - no schematics available of this section, so guessing...

What there is of the schematic shows no sign of a daisy chain or associated interrupts.


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

#include "emu.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/wd_fdc.h"
#include "machine/z80daisy.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"
#include "machine/z80ctc.h"
#include "machine/mm58174.h"
#include "bus/rs232/rs232.h"

namespace {

class dmax8000_state : public driver_device
{
public:
	dmax8000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_bank1(*this, "bank1")
		, m_fdc(*this, "fdc")
		, m_floppy0(*this, "fdc:0")
	{ }

	void dmax8000(machine_config &config);

private:
	void machine_reset() override ATTR_COLD;
	void machine_start() override ATTR_COLD;
	void port0c_w(u8 data);
	void port0d_w(u8 data);
	void port14_w(u8 data);
	void port40_w(u8 data);
	void fdc_drq_w(int state);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_memory_bank    m_bank1;
	required_device<fd1793_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
};


void dmax8000_state::fdc_drq_w(int state)
{
	if (state) printf("DRQ ");
}

void dmax8000_state::port0c_w(u8 data)
{
	printf("Port0c=%X\n", data);
	m_fdc->dden_w(BIT(data, 6));
	floppy_image_device *floppy = nullptr;
	if (BIT(data, 0)) floppy = m_floppy0->get_device();
	m_fdc->set_floppy(floppy);
	if (floppy)
	{
		floppy->mon_w(0);
		floppy->ss_w(0); // no idea
	}
}

void dmax8000_state::port0d_w(u8 data)
{
	printf("Port0d=%X\n", data);
}

void dmax8000_state::port14_w(u8 data)
{
	printf("Port14=%X\n", data);
}

void dmax8000_state::port40_w(u8 data)
{
	m_bank1->set_entry(BIT(~data, 0));
}

void dmax8000_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).ram().share("mainram");
	map(0x0000, 0x0fff).bankr("bank1");
}

void dmax8000_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x03).rw(m_fdc, FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	map(0x04, 0x07).rw("dart1", FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
	map(0x08, 0x0b).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x0c, 0x0f).rw("pio1", FUNC(z80pio_device::read), FUNC(z80pio_device::write)); // fdd controls
	map(0x10, 0x13).rw("pio2", FUNC(z80pio_device::read), FUNC(z80pio_device::write)); // centronics & parallel ports
	map(0x14, 0x17).w(FUNC(dmax8000_state::port14_w)); // control lines for the centronics & parallel ports
	//map(0x18, 0x19).mirror(2).rw("am9511", FUNC(am9512_device::read), FUNC(am9512_device::write)); // optional numeric coprocessor
	//map(0x1c, 0x1d).mirror(2);  // optional hard disk controller (1C=status, 1D=data)
	map(0x20, 0x23).rw("dart2", FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
	map(0x40, 0x40).w(FUNC(dmax8000_state::port40_w)); // memory bank control
	//map(0x60, 0x67) // optional IEEE488 GPIB
	map(0x70, 0x7f).rw("rtc", FUNC(mm58174_device::read), FUNC(mm58174_device::write)); // optional RTC
}

/* Input ports */
static INPUT_PORTS_START( dmax8000 )
INPUT_PORTS_END

void dmax8000_state::machine_reset()
{
	m_bank1->set_entry(1);
	m_maincpu->set_input_line_vector(0, 0xee); // Z80 - fdc vector
}

void dmax8000_state::machine_start()
{
	m_bank1->configure_entry(0, m_ram);
	m_bank1->configure_entry(1, m_rom);
}

static void floppies(device_slot_interface &device)
{
	device.option_add("8dsdd", FLOPPY_8_DSDD);
}


void dmax8000_state::dmax8000(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 4'000'000); // no idea what crystal is used, but 4MHz clock is confirmed
	m_maincpu->set_addrmap(AS_PROGRAM, &dmax8000_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &dmax8000_state::io_map);

	z80ctc_device &ctc(Z80CTC(config, "ctc", 4_MHz_XTAL));
	ctc.set_clk<0>(4_MHz_XTAL / 2); // 2MHz
	ctc.zc_callback<0>().set("dart1", FUNC(z80dart_device::rxca_w));
	ctc.zc_callback<0>().append("dart1", FUNC(z80dart_device::txca_w));
	ctc.zc_callback<0>().append("dart2", FUNC(z80dart_device::rxca_w));
	ctc.zc_callback<0>().append("dart2", FUNC(z80dart_device::txca_w));
	ctc.set_clk<1>(4_MHz_XTAL / 2); // 2MHz
	ctc.zc_callback<1>().set("dart2", FUNC(z80dart_device::rxtxcb_w));
	ctc.set_clk<2>(4_MHz_XTAL / 2); // 2MHz
	ctc.zc_callback<2>().set("dart1", FUNC(z80dart_device::rxtxcb_w));

	z80dart_device& dart1(Z80DART(config, "dart1", 4'000'000)); // A = terminal; B = aux
	dart1.out_txda_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	dart1.out_dtra_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	dart1.out_rtsa_callback().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("dart1", FUNC(z80dart_device::rxa_w));
	rs232.dcd_handler().set("dart1", FUNC(z80dart_device::dcda_w));
	rs232.ri_handler().set("dart1", FUNC(z80dart_device::ria_w));
	rs232.cts_handler().set("dart1", FUNC(z80dart_device::ctsa_w));

	Z80DART(config, "dart2", 4'000'000); // RS232 ports

	z80pio_device& pio1(Z80PIO(config, "pio1", 4'000'000));
	pio1.out_pa_callback().set(FUNC(dmax8000_state::port0c_w));
	pio1.out_pb_callback().set(FUNC(dmax8000_state::port0d_w));

	Z80PIO(config, "pio2", 4'000'000);

	FD1793(config, m_fdc, 2'000'000); // no idea
	m_fdc->intrq_wr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_fdc->drq_wr_callback().set(FUNC(dmax8000_state::fdc_drq_w));
	FLOPPY_CONNECTOR(config, "fdc:0", floppies, "8dsdd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	MM58174(config, "rtc", 0);
}


/* ROM definition */
ROM_START( dmax8000 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "rev1_0.rom", 0x0000, 0x001000, CRC(acbec83f) SHA1(fce0a4307a791250dbdc6bb6a190f7fec3619d82) )
	// ROM_LOAD( "rev1_1.rom", 0x0000, 0x001000, CRC(2eb98a61) SHA1(cdd9a58f63ee7e3d3dd1c4ae3fd4376b308fd10f) )  // this is a hacked rom to speed up the serial port
ROM_END

} // Anonymous namespace

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT           COMPANY    FULLNAME        FLAGS
COMP( 1981, dmax8000, 0,      0,      dmax8000, dmax8000, dmax8000_state, empty_init, "Datamax", "Datamax 8000", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



dms5000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

Digital Microsystems DMS-5000

2010-01-11 Skeleton driver.

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

#include "emu.h"
#include "cpu/i86/i86.h"
#include "machine/74259.h"
//#include "machine/z80sio.h"
#include "emupal.h"
#include "screen.h"


namespace {

class dms5000_state : public driver_device
{
public:
	dms5000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
	{
	}

	void dms5000(machine_config &config);
private:
	uint8_t status_r(offs_t offset);
	void brightness_w(uint8_t data);
	uint32_t screen_update_dms5000(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
};


uint8_t dms5000_state::status_r(offs_t offset)
{
	switch (offset)
	{
	case 0:
		return m_screen->vblank();

	case 2:
		return m_screen->frame_number() & 1;

	default:
		return 0;
	}
}

void dms5000_state::brightness_w(uint8_t data)
{
}

void dms5000_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x1ffff).ram();
	map(0x40000, 0x4ffff).ram();
	map(0xfc000, 0xfffff).rom().region("maincpu", 0);
}

void dms5000_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x20, 0x2f).r(FUNC(dms5000_state::status_r)).umask16(0xff00);
	map(0x40, 0x4f).w("cntlatch", FUNC(ls259_device::write_d0)).umask16(0x00ff);
	map(0x50, 0x57).w(FUNC(dms5000_state::brightness_w)).umask16(0x00ff);
}

/* Input ports */
static INPUT_PORTS_START( dms5000 )
INPUT_PORTS_END


uint32_t dms5000_state::screen_update_dms5000(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void dms5000_state::dms5000(machine_config &config)
{
	/* basic machine hardware */
	I8086(config, m_maincpu, XTAL(9'830'400));
	m_maincpu->set_addrmap(AS_PROGRAM, &dms5000_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &dms5000_state::io_map);

	LS259(config, "cntlatch", 0); // V34

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_size(640, 480);
	m_screen->set_visarea_full();
	m_screen->set_screen_update(FUNC(dms5000_state::screen_update_dms5000));
	m_screen->set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);
}

/* ROM definition */
ROM_START( dms5000 )
	ROM_REGION16_LE( 0x4000, "maincpu", 0 )
	ROM_LOAD16_BYTE( "dms-5000_54-8673o.bin", 0x0001, 0x2000, CRC(dce9823e) SHA1(d36ab87d2e6f5e9f02d59a6a7724ad3ce2428a2f))
	ROM_LOAD16_BYTE( "dms-5000_54-8672e.bin", 0x0000, 0x2000, CRC(94d64c06) SHA1(be5a53da7bb29a5fa9ac31efe550d5d6ff8b77cd))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY                 FULLNAME    FLAGS */
COMP( 1982, dms5000, 0,      0,      dms5000, dms5000, dms5000_state, empty_init, "Digital Microsystems", "DMS-5000", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



dms86.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Digital Microsystems DMS-86

2010-01-11 Skeleton driver.

Monitor commands:
A Display incoming terminal data in hex
B boot to HiNet
C copy memory
D dump memory to screen
F fill memory
G go (eg G address [breakpoint])
I in port
M memory test
O out port (e.g. O 84 77)
P set segment
S write a byte to memory
T type ascii string into the address set by D or S
X display registers
? help
: load intel hex format file


Note that bit 3 of port 82 is tested at boot. If low, the computer bypasses
the monitor and goes straight to "Joining HiNet".

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

#include "emu.h"
#include "cpu/i86/i86.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "machine/clock.h"
#include "bus/rs232/rs232.h"
#include "machine/terminal.h"


namespace {

class dms86_state : public driver_device
{
public:
	dms86_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_terminal(*this, "terminal")
		, m_sio(*this, "sio%u", 1U)
		, m_ctc(*this, "ctc")
	{ }

	void dms86(machine_config &config);
	void nmi_w(int state);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void m1_ack_w(u8 data);

	u16 port9a_r();
	u16 port9c_r();
	void kbd_put(u8 data);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	u8 m_term_data = 0U;
	required_device<cpu_device> m_maincpu;
	required_device<generic_terminal_device> m_terminal;
	required_device_array<z80sio_device, 2> m_sio;
	required_device<z80ctc_device> m_ctc;
};


void dms86_state::nmi_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, state);
}


void dms86_state::m1_ack_w(u8 data)
{
	m_sio[0]->z80daisy_decode(data);
	m_sio[1]->z80daisy_decode(data);
	m_ctc->z80daisy_decode(data);
}


u16 dms86_state::port9a_r()
{
	return m_term_data ? 0x40 : 0;
}

u16 dms86_state::port9c_r()
{
	u8 ret = m_term_data;
	m_term_data = 0;
	return ret;
}


void dms86_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x1ffff).ram();
	map(0xfe000, 0xfffff).rom().region("roms", 0);
	map(0xfed03, 0xfed03).w(FUNC(dms86_state::m1_ack_w));
}

void dms86_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x80, 0x87).rw(m_sio[0], FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w)).umask16(0x00ff);
	map(0x88, 0x8f).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write)).umask16(0x00ff);
	map(0x90, 0x97).rw(m_sio[1], FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w)).umask16(0x00ff);
	//map(0x98, 0x99)  // r SASI data read ; w SASI SEL probe
	map(0x9A, 0x9B).r(FUNC(dms86_state::port9a_r)); // r parallel ports, status byte ; w SASI ACK strobe, SASI data write
	// 9C,9D - no setup bytes; meant for printer // r Port P data read ; Port P data write
	map(0x9c, 0x9d).r(FUNC(dms86_state::port9c_r));
	map(0x9c, 0x9c).w(m_terminal, FUNC(generic_terminal_device::write));
	//map(0x9e, 0x9f)  // r SASI software reset ; w Port P AUX strobe
}

/* Input ports */
static INPUT_PORTS_START( dms86 )
	PORT_START("FRONT")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Interrupt") PORT_CODE(KEYCODE_F2) PORT_WRITE_LINE_MEMBER(FUNC(dms86_state::nmi_w))
INPUT_PORTS_END


void dms86_state::machine_start()
{
	save_item(NAME(m_term_data));

	m_term_data = 0;
}

void dms86_state::kbd_put(u8 data)
{
	m_term_data = data;
}

void dms86_state::dms86(machine_config &config)
{
	/* basic machine hardware */
	I8086(config, m_maincpu, XTAL(14'745'600) / 3); // according to the manual... hmm
	m_maincpu->set_addrmap(AS_PROGRAM, &dms86_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &dms86_state::io_map);

	// According to the manual the clock is 14,765,600 / 4 but that's wrong
	Z80CTC(config, m_ctc, XTAL(14'745'600) / 3);
	//m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);             // frame rate interrupt to maincpu
	m_ctc->zc_callback<0>().set(m_sio[0], FUNC(z80sio_device::rxtxcb_w));  // SIO1 Ch B
	m_ctc->zc_callback<1>().set(m_sio[0], FUNC(z80sio_device::txca_w));    // SIO1 Ch A
	m_ctc->zc_callback<1>().append(m_sio[0], FUNC(z80sio_device::rxca_w));
	m_ctc->zc_callback<2>().set(m_sio[1], FUNC(z80sio_device::rxtxcb_w));  // SIO2
	m_ctc->zc_callback<2>().append(m_sio[1], FUNC(z80sio_device::rxca_w));
	m_ctc->zc_callback<2>().append(m_sio[1], FUNC(z80sio_device::rxca_w));

	Z80SIO(config, m_sio[0], XTAL(14'745'600) / 3);
	m_sio[0]->out_txdb_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	m_sio[0]->out_dtrb_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	m_sio[0]->out_rtsb_callback().set("rs232", FUNC(rs232_port_device::write_rts));

	Z80SIO(config, m_sio[1], XTAL(14'745'600) / 3);

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_sio[0], FUNC(z80sio_device::rxb_w));
	rs232.dcd_handler().set(m_sio[0], FUNC(z80sio_device::dcdb_w)); // HiNet / Monitor switch
	rs232.cts_handler().set(m_sio[0], FUNC(z80sio_device::ctsb_w)).invert();

	GENERIC_TERMINAL(config, m_terminal, 0);
	m_terminal->set_keyboard_callback(FUNC(dms86_state::kbd_put));
}

/* ROM definition */
ROM_START( dms86 )
	ROM_REGION16_LE( 0x2000, "roms", ROMREGION_ERASEFF )
	ROM_LOAD16_BYTE( "hns-86_54-8678.bin", 0x0000, 0x1000, CRC(95f58e1c) SHA1(6fc8f087f0c887d8b429612cd035c6c1faab570c))
	ROM_LOAD16_BYTE( "hns-86_54-8677.bin", 0x0001, 0x1000, CRC(78fad756) SHA1(ddcbff1569ec6975b8489935cdcfa80eba413502))
ROM_END

} // Anonymous namespace


/* Driver */

/*    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY                 FULLNAME  FLAGS */
COMP( 1982, dms86, 0,      0,      dms86,   dms86, dms86_state, empty_init, "Digital Microsystems", "DMS-86", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



dmv.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
// thanks-to:rfka01
/***************************************************************************

        NCR Decision Mate V

        04/01/2012 Skeleton driver.

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


#include "emu.h"

#include "cpu/mcs48/mcs48.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/am9517a.h"
#include "dmv_keyb.h"
#include "machine/pit8253.h"
#include "machine/upd765.h"
#include "sound/spkrdev.h"
#include "video/upd7220.h"
#include "emupal.h"

// expansion slots
#include "bus/dmv/dmvbus.h"
#include "bus/dmv/k012.h"
#include "bus/dmv/k210.h"
#include "bus/dmv/k220.h"
#include "bus/dmv/k230.h"
#include "bus/dmv/k233.h"
#include "bus/dmv/k801.h"
#include "bus/dmv/k803.h"
#include "bus/dmv/k806.h"
#include "bus/dmv/ram.h"

#include "screen.h"
#include "softlist.h"
#include "speaker.h"

#include "formats/dmv_dsk.h"
#include "imagedev/snapquik.h"

#include "dmv.lh"


namespace {

class dmv_state : public driver_device
{
public:
	dmv_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_hgdc(*this, "upd7220")
		, m_dmac(*this, "dma8237")
		, m_pit(*this, "pit8253")
		, m_fdc(*this, "i8272")
		, m_floppy0(*this, "i8272:0")
		, m_floppy1(*this, "i8272:1")
		, m_keyboard(*this, "keyboard")
		, m_speaker(*this, "speaker")
		, m_video_ram(*this, "video_ram")
		, m_palette(*this, "palette")
		, m_ram(*this, "ram")
		, m_bootrom(*this, "boot")
		, m_chargen(*this, "chargen")
		, m_slot1(*this, "slot1")
		, m_slot2(*this, "slot2")
		, m_slot2a(*this, "slot2a")
		, m_slot3(*this, "slot3")
		, m_slot4(*this, "slot4")
		, m_slot5(*this, "slot5")
		, m_slot6(*this, "slot6")
		, m_slot7(*this, "slot7")
		, m_slot7a(*this, "slot7a")
		, m_leds(*this, "led%u", 1U)
	{ }

	void dmv(machine_config &config);

private:
	void update_halt_line();

	void leds_w(uint8_t data);
	void dma_hrq_changed(int state);
	void dmac_eop(int state);
	void dmac_dack3(int state);
	void fdc_irq(int state);
	void pit_out0(int state);
	void timint_w(int state);
	void fdd_motor_w(uint8_t data);
	uint8_t sys_status_r();
	void tc_set_w(uint8_t data);
	void switch16_w(uint8_t data);
	uint8_t ramsel_r();
	uint8_t romsel_r();
	void ramsel_w(uint8_t data);
	void romsel_w(uint8_t data);
	uint8_t kb_mcu_port1_r();
	void kb_mcu_port1_w(uint8_t data);
	void kb_mcu_port2_w(uint8_t data);
	void rambank_w(offs_t offset, uint8_t data);
	uint8_t program_r(offs_t offset);
	void program_w(offs_t offset, uint8_t data);
	uint8_t exp_program_r(offs_t offset);
	void exp_program_w(offs_t offset, uint8_t data);
	void thold7_w(int state);

	void update_busint(int slot, int state);
	void busint2_w(int state)    { update_busint(0, state); }
	void busint2a_w(int state)   { update_busint(1, state); }
	void busint3_w(int state)    { update_busint(2, state); }
	void busint4_w(int state)    { update_busint(3, state); }
	void busint5_w(int state)    { update_busint(4, state); }
	void busint6_w(int state)    { update_busint(5, state); }
	void busint7_w(int state)    { update_busint(6, state); }
	void busint7a_w(int state)   { update_busint(7, state); }

	void update_irqs(int slot, int state);
	void irq2_w(int state)       { update_irqs(0, state); }
	void irq2a_w(int state)      { update_irqs(1, state); }
	void irq3_w(int state)       { update_irqs(2, state); }
	void irq4_w(int state)       { update_irqs(3, state); }
	void irq5_w(int state)       { update_irqs(4, state); }
	void irq6_w(int state)       { update_irqs(5, state); }
	void irq7_w(int state)       { update_irqs(6, state); }
	void irq7a_w(int state)      { update_irqs(7, state); }

	static void floppy_formats(format_registration &fr);
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);

	uint8_t program_read(int cas, offs_t offset);
	void program_write(int cas, offs_t offset, uint8_t data);

	void ifsel_r(int ifsel, offs_t offset, uint8_t &data);
	void ifsel_w(int ifsel, offs_t offset, uint8_t data);
	uint8_t ifsel0_r(offs_t offset)  { uint8_t data = 0xff;   ifsel_r(0, offset, data);   return data; }
	uint8_t ifsel1_r(offs_t offset)  { uint8_t data = 0xff;   ifsel_r(1, offset, data);   return data; }
	uint8_t ifsel2_r(offs_t offset)  { uint8_t data = 0xff;   ifsel_r(2, offset, data);   return data; }
	uint8_t ifsel3_r(offs_t offset)  { uint8_t data = 0xff;   ifsel_r(3, offset, data);   return data; }
	uint8_t ifsel4_r(offs_t offset)  { uint8_t data = 0xff;   ifsel_r(4, offset, data);   return data; }
	void ifsel0_w(offs_t offset, uint8_t data) { ifsel_w(0, offset, data); }
	void ifsel1_w(offs_t offset, uint8_t data) { ifsel_w(1, offset, data); }
	void ifsel2_w(offs_t offset, uint8_t data) { ifsel_w(2, offset, data); }
	void ifsel3_w(offs_t offset, uint8_t data) { ifsel_w(3, offset, data); }
	void ifsel4_w(offs_t offset, uint8_t data) { ifsel_w(4, offset, data); }

	UPD7220_DISPLAY_PIXELS_MEMBER( hgdc_display_pixels );
	UPD7220_DRAW_TEXT_LINE_MEMBER( hgdc_draw_text );

	void dmv_io(address_map &map) ATTR_COLD;
	void dmv_mem(address_map &map) ATTR_COLD;
	void upd7220_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<upd7220_device> m_hgdc;
	required_device<am9517a_device> m_dmac;
	required_device<pit8253_device> m_pit;
	required_device<i8272a_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	required_device<floppy_connector> m_floppy1;
	required_device<dmv_keyboard_device> m_keyboard;
	required_device<speaker_sound_device> m_speaker;
	required_shared_ptr<uint16_t> m_video_ram;
	required_device<palette_device> m_palette;
	required_memory_region m_ram;
	required_memory_region m_bootrom;
	required_memory_region m_chargen;

	required_device<dmvcart_slot_device> m_slot1;
	required_device<dmvcart_slot_device> m_slot2;
	required_device<dmvcart_slot_device> m_slot2a;
	required_device<dmvcart_slot_device> m_slot3;
	required_device<dmvcart_slot_device> m_slot4;
	required_device<dmvcart_slot_device> m_slot5;
	required_device<dmvcart_slot_device> m_slot6;
	required_device<dmvcart_slot_device> m_slot7;
	required_device<dmvcart_slot_device> m_slot7a;
	output_finder<8> m_leds;

	bool        m_ramoutdis;
	int         m_switch16;
	int         m_thold7;
	int         m_dma_hrq;
	int         m_ram_bank;
	bool        m_color_mode;
	int         m_eop_line;
	int         m_dack3_line;
	int         m_sd_poll_state;
	int         m_floppy_motor;
	int         m_busint[8];
	int         m_irqs[8];
};

void dmv_state::tc_set_w(uint8_t data)
{
	m_fdc->tc_w(true);
}

void dmv_state::switch16_w(uint8_t data)
{
	m_switch16 = !m_switch16;
	update_halt_line();
}

void dmv_state::leds_w(uint8_t data)
{
	/*
	    LEDs    Value       Significance
	    ---------------------------------------
	    None    0xFF        Check complete
	    1+8     0x7E        Sumcheck error
	    2+8     0xBE        GDC error
	    3+8     0xDE        Disk drive error
	    4+8     0xEE        16-bit processor error
	    5+8     0xF6        Keyboard error
	    6+8     0xFA        DMA error
	    7+8     0xFC        Memory error
	    All     0x00        Processor error
	*/

	for(int i=0; i<8; i++)
		m_leds[7-i] = BIT(data, i);
}

uint8_t dmv_state::ramsel_r()
{
	m_ramoutdis = false;
	return 0;
}

uint8_t dmv_state::romsel_r()
{
	m_ramoutdis = true;
	return 0;
}

void dmv_state::ramsel_w(uint8_t data)
{
	m_ramoutdis = false;
}

void dmv_state::romsel_w(uint8_t data)
{
	m_ramoutdis = true;
}

void dmv_state::rambank_w(offs_t offset, uint8_t data)
{
	m_ram_bank = offset;
}

void dmv_state::fdd_motor_w(uint8_t data)
{
	m_pit->write_gate0(1);
	m_pit->write_gate0(0);

	m_floppy_motor = 0;
	if (m_floppy0->get_device()) m_floppy0->get_device()->mon_w(m_floppy_motor);
	if (m_floppy1->get_device()) m_floppy1->get_device()->mon_w(m_floppy_motor);
}

uint8_t dmv_state::sys_status_r()
{
	/*
	    Main system status
	    x--- ---- FDD index
	    -x--- --- IRQ 2
	    --x--- -- IRQ 3
	    ---x--- - IRQ 4
	    ---- x--- FDC interrupt
	    ---- -x-- FDD ready
	    ---- --x- 16-bit CPU available (active low)
	    ---- ---x FDD motor (active low)
	*/
	uint8_t data = 0x00;

	if (m_floppy_motor)
		data |= 0x01;

	// 16-bit CPU
	if (!(m_slot7->av16bit() || m_slot7a->av16bit()))
		data |= 0x02;

	if (m_floppy0->get_device() && !m_floppy0->get_device()->ready_r())
		data |= 0x04;

	if (m_fdc->get_irq())
		data |= 0x08;

	if (m_irqs[3])
		data |= 0x10;   // IRQ 4

	if (m_irqs[2])
		data |= 0x20;   // IRQ 3

	if (m_irqs[0])
		data |= 0x40;   // IRQ 2

	return data;
}

UPD7220_DISPLAY_PIXELS_MEMBER( dmv_state::hgdc_display_pixels )
{
	if (m_color_mode)
	{
		// 96KB videoram (32KB green + 32KB red + 32KB blue)
		uint16_t green = m_video_ram[(0x00000 + (address & 0x3fff))];
		uint16_t red   = m_video_ram[(0x04000 + (address & 0x3fff))];
		uint16_t blue  = m_video_ram[(0x08000 + (address & 0x3fff))];

		for(int xi=0; xi<16; xi++)
		{
			int r = BIT(red,   xi) ? 255 : 0;
			int g = BIT(green, xi) ? 255 : 0;
			int b = BIT(blue,  xi) ? 255 : 0;

			if (bitmap.cliprect().contains(x + xi, y))
				bitmap.pix(y, x + xi) = rgb_t(r, g, b);
		}
	}
	else
	{
		rgb_t const *const palette = m_palette->palette()->entry_list_raw();

		// 32KB videoram
		uint16_t gfx = m_video_ram[(address & 0x3fff)];

		for(int xi=0;xi<16;xi++)
		{
			if (bitmap.cliprect().contains(x + xi, y))
				bitmap.pix(y, x + xi) = ((gfx >> xi) & 1) ? palette[2] : palette[0];
		}
	}
}

UPD7220_DRAW_TEXT_LINE_MEMBER( dmv_state::hgdc_draw_text )
{
	for( int x = 0; x < pitch; x++ )
	{
		uint8_t tile = m_video_ram[(((addr+x)*2) & 0x1ffff) >> 1] & 0xff;
		uint8_t attr = m_video_ram[(((addr+x)*2) & 0x1ffff) >> 1] >> 8;

		rgb_t bg, fg;
		if (m_color_mode)
		{
			bg = rgb_t(attr & 0x20 ? 0 : 255, attr & 0x40 ? 0 : 255, attr & 0x80 ? 0 : 255);
			fg = rgb_t(attr & 0x04 ? 255 : 0, attr & 0x08 ? 255 : 0, attr & 0x10 ? 255 : 0);
		}
		else
		{
			const rgb_t *palette = m_palette->palette()->entry_list_raw();
			bg = palette[(attr & 1) ? 2 : 0];
			fg = palette[(attr & 1) ? 0 : 2];
		}

		for( int yi = 0; yi < lr; yi++)
		{
			uint8_t tile_data = m_chargen->base()[(tile*16+yi) & 0x7ff];

			if((attr & 2) && (m_screen->frame_number() & 0x10)) // FIXME: blink freq
				tile_data = 0;

			if(cursor_on && cursor_addr == addr+x) //TODO
				tile_data^=0xff;

			for( int xi = 0; xi < 8; xi++)
			{
				int pen = (tile_data >> xi) & 1 ? 1 : 0;

				int res_x = x * 8 + xi;
				int res_y = y + yi;

				if(!m_screen->visible_area().contains(res_x, res_y))
					continue;

				if(yi >= 16) { pen = 0; }

				bitmap.pix(res_y, res_x) = pen ? fg : bg;
			}
		}
	}
}

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

    Quickload

    This loads a .COM file to address 0x100 then jumps
    there. Sometimes .COM has been renamed to .CPM to
    prevent windows going ballistic. These can be loaded
    as well.

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

QUICKLOAD_LOAD_MEMBER(dmv_state::quickload_cb)
{
	// Avoid loading a program if CP/M-80 is not in memory
	if ((m_ram->base()[0] != 0xc3) || (m_ram->base()[5] != 0xc3))
		return std::make_pair(image_error::UNSUPPORTED, "CP/M must already be running");

	const int mem_avail = 256 * m_ram->base()[7] + m_ram->base()[6] - 512;
	if (mem_avail < image.length())
		return std::make_pair(image_error::UNSPECIFIED, "Insufficient memory available");

	// Load image to the TPA (Transient Program Area)
	uint16_t quickload_size = image.length();
	for (uint16_t i = 0; i < quickload_size; i++)
	{
		uint8_t data;
		if (image.fread(&data, 1) != 1)
			return std::make_pair(image_error::UNSPECIFIED, "Problem reading the image at offset " + std::to_string(i));
		m_ram->base()[i + 0x100] = data;
	}

	m_ram->base()[0x80] = m_ram->base()[0x81] = 0; // clear out command tail

	m_maincpu->set_state_int(Z80_SP, mem_avail + 384); // put the stack a bit before BDOS
	m_maincpu->set_pc(0x100); // start program

	return std::make_pair(std::error_condition(), std::string());
}

static void dmv_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
	device.option_add("525qd", FLOPPY_525_QD);
}


void dmv_state::ifsel_r(int ifsel, offs_t offset, uint8_t &data)
{
	for (auto &slot : { m_slot2, m_slot2a, m_slot3, m_slot4, m_slot5, m_slot6, m_slot7, m_slot7a })
		slot->io_read(ifsel, offset, data);
}

void dmv_state::ifsel_w(int ifsel, offs_t offset, uint8_t data)
{
	for(auto &slot : { m_slot2, m_slot2a, m_slot3, m_slot4, m_slot5, m_slot6, m_slot7, m_slot7a })
		slot->io_write(ifsel, offset, data);
}

void dmv_state::exp_program_w(offs_t offset, uint8_t data)
{
	program_write((offset >> 16) & 0x07, offset, data);
}

uint8_t dmv_state::exp_program_r(offs_t offset)
{
	return program_read((offset >> 16) & 0x07, offset);
}

void dmv_state::program_w(offs_t offset, uint8_t data)
{
	program_write(m_ram_bank, offset, data);
}

uint8_t dmv_state::program_r(offs_t offset)
{
	return program_read(m_ram_bank, offset);
}

void dmv_state::thold7_w(int state)
{
	if (m_thold7 != state)
	{
		m_thold7 = state;
		update_halt_line();
	}
}

void dmv_state::update_busint(int slot, int state)
{
	m_busint[slot] = state;

	int new_state = CLEAR_LINE;
	for (auto & elem : m_busint)
		if (elem != CLEAR_LINE)
		{
			new_state = ASSERT_LINE;
			break;
		}

	m_slot7a->busint_w(new_state);
	m_slot7->busint_w(new_state);
	m_maincpu->set_input_line(0, new_state);
}

void dmv_state::update_irqs(int slot, int state)
{
	m_irqs[slot] = state;

	switch(slot)
	{
	case 0: // slot 2
		m_slot7->irq2_w(state);
		m_slot7a->irq2_w(state);
		break;
	case 1: // slot 2a
		m_slot7->irq2a_w(state);
		m_slot7a->irq2a_w(state);
		break;
	case 2: // slot 3
		m_slot7->irq3_w(state);
		m_slot7a->irq3_w(state);
		break;
	case 3: // slot 4
		m_slot7->irq4_w(state);
		m_slot7a->irq4_w(state);
		break;
	case 4: // slot 5
		m_slot7->irq5_w(state);
		m_slot7a->irq5_w(state);
		break;
	case 5: // slot 6
		m_slot7->irq6_w(state);
		m_slot7a->irq6_w(state);
		break;
	}
}

void dmv_state::program_write(int cas, offs_t offset, uint8_t data)
{
	bool tramd = false;
	dmvcart_slot_device *slots[] = { m_slot2, m_slot2a, m_slot3, m_slot4, m_slot5, m_slot6, m_slot7, m_slot7a };
	for(int i=0; i<8 && !tramd; i++)
		tramd = slots[i]->write(offset, data);

	if (!tramd)
	{
		if (cas == 0)
			m_ram->base()[offset & 0xffff] = data;
		else
			m_slot1->ram_write(cas, offset & 0xffff, data);
	}
}

uint8_t dmv_state::program_read(int cas, offs_t offset)
{
	uint8_t data = 0xff;
	if (m_ramoutdis && offset < 0x2000)
	{
		data = m_bootrom->base()[offset];
	}
	else
	{
		bool tramd = false;
		dmvcart_slot_device *slots[] = { m_slot2, m_slot2a, m_slot3, m_slot4, m_slot5, m_slot6, m_slot7, m_slot7a };
		for(int i=0; i<8 && !tramd; i++)
			tramd = slots[i]->read(offset, data);

		if (!tramd)
		{
			if (cas == 0)
				data = m_ram->base()[offset & 0xffff];
			else
				m_slot1->ram_read(cas, offset & 0xffff, data);
		}
	}

	return data;
}

void dmv_state::dmv_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(dmv_state::program_r), FUNC(dmv_state::program_w));
}

void dmv_state::dmv_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x00).w(FUNC(dmv_state::leds_w));
	map(0x10, 0x10).rw(FUNC(dmv_state::ramsel_r), FUNC(dmv_state::ramsel_w));
	map(0x11, 0x11).rw(FUNC(dmv_state::romsel_r), FUNC(dmv_state::romsel_w));
	map(0x12, 0x12).w(FUNC(dmv_state::tc_set_w));
	map(0x13, 0x13).r(FUNC(dmv_state::sys_status_r));
	map(0x14, 0x14).w(FUNC(dmv_state::fdd_motor_w));
	map(0x20, 0x2f).rw(m_dmac, FUNC(am9517a_device::read), FUNC(am9517a_device::write));
	map(0x40, 0x41).rw("kb_ctrl_mcu", FUNC(upi41_cpu_device::upi41_master_r), FUNC(upi41_cpu_device::upi41_master_w));
	map(0x50, 0x51).m(m_fdc, FUNC(i8272a_device::map));
	map(0x80, 0x83).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xa0, 0xa1).rw(m_hgdc, FUNC(upd7220_device::read), FUNC(upd7220_device::write));
	map(0xd0, 0xd7).w(FUNC(dmv_state::switch16_w));
	map(0xe0, 0xe7).w(FUNC(dmv_state::rambank_w));

	map(0x60, 0x6f).rw(FUNC(dmv_state::ifsel0_r), FUNC(dmv_state::ifsel0_w));
	map(0x70, 0x7f).rw(FUNC(dmv_state::ifsel1_r), FUNC(dmv_state::ifsel1_w));
	map(0x30, 0x3f).rw(FUNC(dmv_state::ifsel2_r), FUNC(dmv_state::ifsel2_w));
	map(0xb0, 0xbf).rw(FUNC(dmv_state::ifsel3_r), FUNC(dmv_state::ifsel3_w));
	map(0xc0, 0xcf).rw(FUNC(dmv_state::ifsel4_r), FUNC(dmv_state::ifsel4_w));
}

uint8_t dmv_state::kb_mcu_port1_r()
{
	return !(m_keyboard->sd_poll_r() & !m_sd_poll_state);
}

void dmv_state::kb_mcu_port1_w(uint8_t data)
{
	m_sd_poll_state = BIT(data, 1);
	m_keyboard->sd_poll_w(!m_sd_poll_state);
}

void dmv_state::kb_mcu_port2_w(uint8_t data)
{
	m_speaker->level_w(BIT(data, 0));
	m_slot7a->keyint_w(BIT(data, 4));
	m_slot7->keyint_w(BIT(data, 4));
}

void dmv_state::upd7220_map(address_map &map)
{
	map.global_mask(0xffff);
	map(0x0000, 0xffff).ram().share("video_ram");
}

/* Input ports */
INPUT_PORTS_START( dmv )
	PORT_START("CONFIG")
	PORT_CONFNAME( 0x01, 0x01, "Video Board" )
	PORT_CONFSETTING( 0x00, "Monochrome" )
	PORT_CONFSETTING( 0x01, "Color" )
INPUT_PORTS_END

void dmv_state::machine_start()
{
	m_leds.resolve();

	// register for state saving
	save_item(NAME(m_ramoutdis));
	save_item(NAME(m_switch16));
	save_item(NAME(m_thold7));
	save_item(NAME(m_dma_hrq));
	save_item(NAME(m_ram_bank));
	save_item(NAME(m_color_mode));
	save_item(NAME(m_eop_line));
	save_item(NAME(m_dack3_line));
	save_item(NAME(m_sd_poll_state));
	save_item(NAME(m_floppy_motor));
	save_item(NAME(m_busint));
	save_item(NAME(m_irqs));
	save_pointer(NAME(m_ram->base()), m_ram->bytes());
}

void dmv_state::machine_reset()
{
	m_color_mode = ioport("CONFIG")->read() & 0x01;

	m_ramoutdis = true;
	m_ram_bank = 0;
	m_eop_line = 0;
	m_dack3_line = 0;
	m_sd_poll_state = 0;
	m_floppy_motor = 1;
	m_switch16 = 0;
	m_thold7 = 0;
	m_dma_hrq = 0;
	memset(m_busint, 0, sizeof(m_busint));
	memset(m_irqs, 0, sizeof(m_irqs));

	update_halt_line();
}

void dmv_state::update_halt_line()
{
	m_slot7->hold_w(m_dma_hrq);
	m_slot7->switch16_w(m_switch16);
	m_slot7a->hold_w(m_dma_hrq);
	m_slot7a->switch16_w(m_switch16);

	m_maincpu->set_input_line(INPUT_LINE_HALT, (m_thold7 || m_switch16 || m_dma_hrq) ? ASSERT_LINE : CLEAR_LINE);
}

/* F4 Character Displayer */
static const gfx_layout dmv_charlayout =
{
	8, 16,                  /* 8 x 16 characters */
	128,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	/* y offsets */
	{ STEP16(0,8) },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_dmv )
	GFXDECODE_ENTRY("chargen", 0x0000, dmv_charlayout, 0, 1)
GFXDECODE_END


//------------------------------------------------------------------------------------
//   I8237
//------------------------------------------------------------------------------------

void dmv_state::dma_hrq_changed(int state)
{
	m_dma_hrq = state;
	update_halt_line();

	// Assert HLDA
	m_dmac->hack_w(state);
}

void dmv_state::dmac_eop(int state)
{
	if (!(m_dack3_line || m_eop_line) && (m_dack3_line || state))
		m_fdc->tc_w(true);

	m_eop_line = state;
}

void dmv_state::dmac_dack3(int state)
{
	if (!(m_dack3_line || m_eop_line) && (state || m_eop_line))
		m_fdc->tc_w(true);

	m_dack3_line = state;
}

void dmv_state::pit_out0(int state)
{
	if (!state)
	{
		m_floppy_motor = 1;
		if (m_floppy0->get_device()) m_floppy0->get_device()->mon_w(m_floppy_motor);
		if (m_floppy1->get_device()) m_floppy1->get_device()->mon_w(m_floppy_motor);
	}
}

void dmv_state::timint_w(int state)
{
	m_slot7a->timint_w(state);
	m_slot7->timint_w(state);
}

void dmv_state::fdc_irq(int state)
{
	m_slot7a->flexint_w(state);
	m_slot7->flexint_w(state);

	if (state)
		m_fdc->tc_w(false);
}


void dmv_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_DMV_FORMAT);
}


static void dmv_slot1(device_slot_interface &device)
{
	device.option_add("k200", DMV_K200);        // K200 64K RAM expansion
	device.option_add("k202", DMV_K202);        // K202 192K RAM expansion
	device.option_add("k208", DMV_K208);        // K208 448K RAM expansion
}

static void dmv_slot2_6(device_slot_interface &device)
{
	device.option_add("k210", DMV_K210);        // K210 Centronics
	device.option_add("k211", DMV_K211);        // K211 RS-232 Communications Interface
	device.option_add("k212", DMV_K212);        // K212 RS-232 Printer Interface
	device.option_add("k213", DMV_K213);        // K213 RS-232 Plotter Interface
	device.option_add("k233", DMV_K233);        // K233 16K Shared RAM
	device.option_add("k801", DMV_K801);        // K801 RS-232 Switchable Interface
	device.option_add("k803", DMV_K803);        // K803 RTC module
	device.option_add("k806", DMV_K806);        // K806 Mouse module
	device.option_add("c3282", DMV_C3282);      // C3282 External HD Interface
}

static void dmv_slot7(device_slot_interface &device)
{
	device.option_add("k220", DMV_K220);        // K220 Diagnostic Module
	device.option_add("k231", DMV_K231);        // K231 External 8088 module without interrupt controller
	device.option_add("k234", DMV_K234);        // K234 External 68008 module
}


static void dmv_slot2a(device_slot_interface &device)
{
	device.option_add("k012", DMV_K012);        // K012 Internal HD Interface
}

static void dmv_slot7a(device_slot_interface &device)
{
	device.option_add("k230", DMV_K230);        // K230 Internal 8088 module without interrupt controller
	device.option_add("k235", DMV_K235);        // K235 Internal 8088 module with interrupt controller
}

void dmv_state::dmv(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(24'000'000) / 6);
	m_maincpu->set_addrmap(AS_PROGRAM, &dmv_state::dmv_mem);
	m_maincpu->set_addrmap(AS_IO, &dmv_state::dmv_io);

	i8741a_device &kbmcu(I8741A(config, "kb_ctrl_mcu", XTAL(6'000'000)));
	kbmcu.p1_in_cb().set(FUNC(dmv_state::kb_mcu_port1_r)); // bit 0 data from kb
	kbmcu.p1_out_cb().set(FUNC(dmv_state::kb_mcu_port1_w)); // bit 1 data to kb
	kbmcu.p2_out_cb().set(FUNC(dmv_state::kb_mcu_port2_w));

	config.set_perfect_quantum(m_maincpu);

	DMV_KEYBOARD(config, m_keyboard, 0);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_screen_update("upd7220", FUNC(upd7220_device::screen_update));
	m_screen->set_size(640, 400);
	m_screen->set_visarea(0, 640-1, 0, 400-1);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_dmv);
	PALETTE(config, m_palette, palette_device::RGB_3BIT);
	config.set_default_layout(layout_dmv);

	// devices
	UPD7220(config, m_hgdc, XTAL(5'000'000)/2); // unk clock
	m_hgdc->set_addrmap(0, &dmv_state::upd7220_map);
	m_hgdc->set_display_pixels(FUNC(dmv_state::hgdc_display_pixels));
	m_hgdc->set_draw_text(FUNC(dmv_state::hgdc_draw_text));

	AM9517A(config, m_dmac, 4_MHz_XTAL);
	m_dmac->out_hreq_callback().set(FUNC(dmv_state::dma_hrq_changed));
	m_dmac->out_eop_callback().set(FUNC(dmv_state::dmac_eop));
	m_dmac->in_memr_callback().set(FUNC(dmv_state::program_r));
	m_dmac->out_memw_callback().set(FUNC(dmv_state::program_w));
	m_dmac->in_ior_callback<0>().set([this] () { logerror("Read DMA CH1"); return u8(0); });
	m_dmac->out_iow_callback<0>().set([this] (u8 data) { logerror("Write DMA CH1 %02X", data); });
	m_dmac->in_ior_callback<1>().set([this] () { logerror("Read DMA CH2"); return u8(0); });
	m_dmac->out_iow_callback<1>().set([this] (u8 data) { logerror("Write DMA CH2 %02X", data); });
	m_dmac->in_ior_callback<2>().set(m_hgdc, FUNC(upd7220_device::dack_r));
	m_dmac->out_iow_callback<2>().set(m_hgdc, FUNC(upd7220_device::dack_w));
	m_dmac->in_ior_callback<3>().set(m_fdc, FUNC(i8272a_device::dma_r));
	m_dmac->out_iow_callback<3>().set(m_fdc, FUNC(i8272a_device::dma_w));
	m_dmac->out_dack_callback<3>().set(FUNC(dmv_state::dmac_dack3));

	I8272A(config, m_fdc, 8'000'000, true);
	m_fdc->intrq_wr_callback().set(FUNC(dmv_state::fdc_irq));
	m_fdc->drq_wr_callback().set(m_dmac, FUNC(am9517a_device::dreq3_w));
	FLOPPY_CONNECTOR(config, "i8272:0", dmv_floppies, "525dd", dmv_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "i8272:1", dmv_floppies, "525dd", dmv_state::floppy_formats);

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(50);
	m_pit->out_handler<0>().set(FUNC(dmv_state::pit_out0));
	m_pit->set_clk<2>(XTAL(24'000'000) / 3 / 16);
	m_pit->out_handler<2>().set(FUNC(dmv_state::timint_w));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	DMVCART_SLOT(config, m_slot1, dmv_slot1, nullptr);
	DMVCART_SLOT(config, m_slot2, dmv_slot2_6, nullptr);
	m_slot2->out_int().set(FUNC(dmv_state::busint2_w));
	m_slot2->out_irq().set(FUNC(dmv_state::irq2_w));
	DMVCART_SLOT(config, m_slot2a, dmv_slot2a, nullptr);
	m_slot2a->out_int().set(FUNC(dmv_state::busint2a_w));
	m_slot2a->out_irq().set(FUNC(dmv_state::irq2a_w));
	DMVCART_SLOT(config, m_slot3, dmv_slot2_6, nullptr);
	m_slot3->out_int().set(FUNC(dmv_state::busint3_w));
	m_slot3->out_irq().set(FUNC(dmv_state::irq3_w));
	DMVCART_SLOT(config, m_slot4, dmv_slot2_6, nullptr);
	m_slot4->out_int().set(FUNC(dmv_state::busint4_w));
	m_slot4->out_irq().set(FUNC(dmv_state::irq4_w));
	DMVCART_SLOT(config, m_slot5, dmv_slot2_6, nullptr);
	m_slot5->out_int().set(FUNC(dmv_state::busint5_w));
	m_slot5->out_irq().set(FUNC(dmv_state::irq5_w));
	DMVCART_SLOT(config, m_slot6, dmv_slot2_6, nullptr);
	m_slot6->out_int().set(FUNC(dmv_state::busint6_w));
	m_slot6->out_irq().set(FUNC(dmv_state::irq6_w));

	DMVCART_SLOT(config, m_slot7, dmv_slot7, nullptr);
	m_slot7->prog_read().set(FUNC(dmv_state::exp_program_r));
	m_slot7->prog_write().set(FUNC(dmv_state::exp_program_w));
	m_slot7->out_thold().set(FUNC(dmv_state::thold7_w));
	m_slot7->out_int().set(FUNC(dmv_state::busint7_w));
	m_slot7->out_irq().set(FUNC(dmv_state::irq7_w));
	DMVCART_SLOT(config, m_slot7a, dmv_slot7a, "k230");
	m_slot7a->prog_read().set(FUNC(dmv_state::exp_program_r));
	m_slot7a->prog_write().set(FUNC(dmv_state::exp_program_w));
	m_slot7a->out_thold().set(FUNC(dmv_state::thold7_w));
	m_slot7a->out_int().set(FUNC(dmv_state::busint7a_w));
	m_slot7a->out_irq().set(FUNC(dmv_state::irq7a_w));

	for (auto &slot : { m_slot1, m_slot2, m_slot2a, m_slot3, m_slot4, m_slot5, m_slot6, m_slot7, m_slot7a })
	{
		slot->set_memspace(m_maincpu, AS_PROGRAM);
		slot->set_iospace(m_maincpu, AS_IO);
	}

	SOFTWARE_LIST(config, "flop_list").set_original("dmv");

	QUICKLOAD(config, "quickload", "com,cpm", attotime::from_seconds(3)).set_load_callback(FUNC(dmv_state::quickload_cb));
}

/* ROM definition */
ROM_START( dmv )
	ROM_REGION( 0x2000, "boot", 0 )
	ROM_SYSTEM_BIOS(0, "c07", "C.07.00")    // ROM bears the handwritten note "Color 7.0", this is from the machine that originally had Color, 68K and internal 8088
	ROM_SYSTEM_BIOS(1, "c06", "C.06.00")    // Color machine with older BIOS revision
	ROM_SYSTEM_BIOS(2, "m07", "M.07.00")    // Mono machine with internal 8088 and internal HD
	ROM_SYSTEM_BIOS(3, "m06", "M.06.00")    // Mono machine
	ROM_SYSTEM_BIOS(4, "m05", "M.05.00")    // Mono machine, marked "updated"

	ROMX_LOAD( "dmv_mb_rom_33610.bin", 0x00000,    0x02000,    CRC(bf25f3f0) SHA1(0c7dd37704db4799e340cc836f887cd543e5c964), ROM_BIOS(0) )
	ROMX_LOAD( "dmv_mb_rom_32838.bin", 0x00000,    0x02000,    CRC(d5ceb559) SHA1(e3a05e43aa1b09f0a857b8d54b00bcd321215bf6), ROM_BIOS(1) )
	ROMX_LOAD( "dmv_mb_rom_33609.bin", 0x00000,    0x02000,    CRC(120951b6) SHA1(57bef9cc6379dea5730bc1477e8896508e00a349), ROM_BIOS(2) )
	ROMX_LOAD( "dmv_mb_rom_32676.bin", 0x00000,    0x02000,    CRC(7796519e) SHA1(8d5dd9c1e66c96fcca271b6f673d6a0e784acb33), ROM_BIOS(3) )
	ROMX_LOAD( "dmv_mb_rom_32664.bin", 0x00000,    0x02000,    CRC(6624610e) SHA1(e9226be897d2c5f875784ab77dad8807f14c7714), ROM_BIOS(4) )

	ROM_REGION(0x400, "kb_ctrl_mcu", 0)
	ROMX_LOAD( "dmv_mb_8741_32678.bin",    0x00000,    0x00400,    CRC(50d1dc4c) SHA1(2c8251d6c8df9f507e11bf920869657f4d074db1), ROM_BIOS(0) )
	ROMX_LOAD( "dmv_mb_8741_32678.bin",    0x00000,    0x00400,    CRC(50d1dc4c) SHA1(2c8251d6c8df9f507e11bf920869657f4d074db1), ROM_BIOS(1) )
	ROMX_LOAD( "dmv_mb_8741_32678.bin",    0x00000,    0x00400,    CRC(50d1dc4c) SHA1(2c8251d6c8df9f507e11bf920869657f4d074db1), ROM_BIOS(2) )
	ROMX_LOAD( "dmv_mb_8741_32678.bin",    0x00000,    0x00400,    CRC(50d1dc4c) SHA1(2c8251d6c8df9f507e11bf920869657f4d074db1), ROM_BIOS(3) )
	ROMX_LOAD( "dmv_mb_8741_32121.bin",    0x00000,    0x00400,    CRC(a03af298) SHA1(144cba41294c46f5ca79b7ad8ced0e4408168775), ROM_BIOS(4) )

	ROM_REGION(0x800, "chargen", 0)
	ROM_LOAD( "76161.bin",    0x00000,    0x00800,  CRC(6e4df4f9) SHA1(20ff4fc48e55eaf5131f6573fff93e7f97d2f45d)) // same for both color and monochrome board

	ROM_REGION(0x10000, "ram", ROMREGION_ERASE) // 64K RAM on mainboard
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY  FULLNAME           FLAGS
COMP( 1984, dmv,  0,      0,      dmv,     dmv,   dmv_state, empty_init, "NCR",   "Decision Mate V", MACHINE_SUPPORTS_SAVE)



dmx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:m1macrophage

/*
The Oberheim DMX is a digital drum machine.

The firmware, running on a Z80, implements the drum machine functionality (e.g.
sequencer), controls the UI (scans for button presses, drives the display),
triggers voices, and controls voice variations. All hardware functionality is
mapped to I/O ports. Only ROM and RAM are mapped to memory.

There are 8 voice cards, each of which can produce 3 variations, for a total of
24 sounds. The 8 voice cards can play back simultaneously, but only 1 variation
per card can be active at a time, for a total of 8 voices of polyphony.

The drum sounds are samples stored in ROMs. During playback, they are
post-processed by an analog VCA, which is modulated by a Release envelope
genereator. Pitch and volume variations for each voice are also controlled by
analog circuitry.

With the exception of the Cymbal voice, the bare voice PCBs are identical.
Other than having different ROMs, the voice cards are further configured by
multiple jumpers and differing component values. This configuration controls
nominal pitch, variation type (pitch, volume, decay, sample), and the
frequency response of the reconstruction filter.

The Cymbal voice consists of 2 voice cards. One implements most of the circuit
found in the other voice cards, while the other holds the 8 2732 ROMS.

Currently, this driver emulates the early version of the DMX. A later hardware
revision added additional memory, and the final hardware revision added MIDI.
Furthermore, later versions shipped with the "Mark II" voice cards.
These subsequent hardware revisions and the Mark II voice cards are not yet
emulated.

The driver is based on the DMX's service manual, DMX schematics, and
http://www.electrongate.com/dmxfiles/dmxcards.html. It is intended as an
educational tool.

PCBoards:
- Processor Board (includes the power supply and connectors)
- Switch Board (buttons, faders and display)
- 7 x voice cards: Bass, Snare, Hi-hat, Tom 1, Tom 2, Perc 1, Perc 2.
- 2 x cymbal cards: A single voice that occupies two card slots.

Possible audio inaccuracies:
- Some uncertainty on component values for HIHAT and PERC2 (see comments in
  HIHAT_CONFIG and PERC_CONFIG).
- Envelope decay ignores diodes in capacitor discharge path. Given the quick
  decay, and that the error is larger at low volumes, this might not be
  noticeable.
- Simplified diode model in pitch control. Very unlikely to be noticeable.
  Error is within component tolerance margins.
- Simplified diode model in volume control. Very unlikely to be noticeable.
  Error is within component tolerance margins. It is very small at high volumes,
  and approaches component tolerance extremes at low volumes.

Usage notes:
- Interactive layout included.
- The mixer faders can be controlled with the mouse, or from the "Slider
  Controls" menu.
- Voices can be tuned with the mouse, or the "Sider Controls" menu.
- The drum keys are mapped to the keyboard, starting at "Q". Specifically:
  Q - Bass 1, W - Snare 1, ...
  A - Bass 2, S - Snare 2, ...
  Z - Bass 3, X - Snare 3, ...
- The number buttons on the layout are mapped to the numeric keypad.
- Can choose between the stereo (hardcoded voice panning) and mono outputs
  from the Machine Configuration menu.
- Can run with a high sample rate: ./mame -window obdmx -samplerate 96000
*/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/nvram.h"
#include "machine/output_latch.h"
#include "machine/rescap.h"
#include "machine/timer.h"
#include "sound/dac76.h"
#include "sound/flt_biquad.h"
#include "sound/flt_rc.h"
#include "sound/mixer.h"
#include "sound/spkrdev.h"
#include "sound/va_eg.h"
#include "video/dl1416.h"
#include "speaker.h"

#include "oberheim_dmx.lh"

#define LOG_TRIGGERS      (1U << 1)
#define LOG_INT_TIMER     (1U << 2)
#define LOG_FADERS        (1U << 3)
#define LOG_SOUND         (1U << 4)
#define LOG_PITCH         (1U << 5)
#define LOG_VOLUME        (1U << 6)
#define LOG_METRONOME     (1U << 7)

#define VERBOSE (LOG_GENERAL)
//#define LOG_OUTPUT_FUNC osd_printf_info

#include "logmacro.h"

static constexpr const float VCC = 5;

// Voice card configuration. Captures jumper configuration and component values.
struct dmx_voice_card_config
{
	const float c2;  // Pitch control capacitor. In Farads.
	const float c3;  // EG capacitor. In Farads. 0 if unused.

	// R12 and R17 control how the different trigger modes affect pitch (for
	// voices wired for pitch control), or volume and decay (for voices wired
	// for volume control).
	// The values of R12 and R17 in the 1981 service manual are flipped compared
	// to the values in the 1983 schematics and in
	// http://www.electrongate.com/dmxfiles/dmxcards.html. The values in the
	// 1983 schematic (and electrongate.com) are most probably the right ones,
	// since they create more variation, especially for volume. Using those.
	const float r12;
	const float r17;

	// ROM contains 2 different samples.
	const bool split_rom;

	// Contains 8 ROMs instead of 1. Used for Cymbal voice cards.
	const bool multi_rom;

	// The triggers control pitch variations (rather than volume variations).
	const bool pitch_control;

	enum class decay_mode : s8  // Controlled by jumper Z on voice card.
	{
		DISABLED,  // 4.7K resistor connected to position 1 (+5V).
		ENABLED,  // Jumper disconnected.
		ENABLED_ON_TR12,  // 4.7K resistor connected to position 2 (/Q of U1B).
						  // Decay enabled when trigger mode is 1 or 2.
	};
	const decay_mode decay;

	// This jumper won't make a difference when `decay` is `DISABLED`.
	enum class early_decay_mode : s8  // Controlled by jumper S on voice card.
	{
		ENABLED,  // Jumper connected to position 2 (+5V).
		ENABLED_ON_TR1,  // Jumper connected to position 1 (Q of U1A).
						 // Early decay enabled when trigger mode is 1.
	};
	const early_decay_mode early_decay;

	// A cascade of 3 Sallen-Key filters. Components ordered from left to right
	// on the schematic, which also matches the order in opamp_sk_lowpass_setup.
	struct filter_components
	{
		const float r15;
		const float r14;
		const float c5;
		const float c4;

		const float r13;
		const float r18;
		const float c6;
		const float c7;

		const float r19;
		const float r20;
		const float c8;
		const float c9;
	};
	const filter_components filter;
};

// Emulates the original DMX voice cards, including the cymbal card. Later
// DMX models shipped with the "Mark II" voice cards for the Tom voices.
// The Mark II cards are not yet emulated.
class dmx_voice_card_device : public device_t, public device_sound_interface
{
public:
	dmx_voice_card_device(const machine_config &mconfig, const char *tag, device_t *owner, const dmx_voice_card_config &config, required_memory_region *sample_rom) ATTR_COLD;
	dmx_voice_card_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0) ATTR_COLD;

	void trigger(bool tr0, bool tr1);
	void set_pitch_adj(s32 t1_percent);  // Valid values: 0-100.

protected:
	void device_add_mconfig(machine_config &config) override ATTR_COLD;
	void device_start() override ATTR_COLD;
	void device_reset() override ATTR_COLD;
	void sound_stream_update(sound_stream &stream) override;

private:
	void reset_counter();
	void init_pitch() ATTR_COLD;
	void compute_pitch_variations();
	void select_pitch();

	void init_gain_and_decay_variations() ATTR_COLD;
	bool has_decay() const { return !m_decay_r.empty(); }
	bool has_decay_variations() const { return m_decay_r.size() > 1; }
	bool has_gain_variations() const { return !m_config.pitch_control; }

	bool is_decay_enabled() const;
	bool is_early_decay_enabled() const;
	TIMER_DEVICE_CALLBACK_MEMBER(clock_callback);

	sound_stream *m_stream = nullptr;

	required_device<timer_device> m_timer;  // 555, U5.
	required_device<dac76_device> m_dac;  // AM6070, U8. Compatible with DAC76.
	optional_device<va_rc_eg_device> m_eg;  // Volume envelope generator. Input to U8 Iref.
	required_device_array<filter_biquad_device, 3> m_filters;

	// Configuration. Do not include in save state.
	const dmx_voice_card_config m_config;
	required_memory_region *m_sample_rom = nullptr;
	std::vector<float> m_cv;  // 555 CV (pin 5) voltage variations.
	std::vector<attotime> m_sample_t;  // Sample period variations.
	s32 m_t1_percent = 0;  // Tuning trimmer.
	std::vector<float> m_iref;  // DAC reference currents for gain variations.
	std::vector<float> m_decay_r;  // Decay resistance variations.

	// Device state.
	bool m_counting = false;
	u16 m_counter = 0;  // 4040 counter.
	u8 m_trigger_mode = 0;  // Valid modes: 1-3. 0 OK after reset.
	bool m_decaying = false;
};

DEFINE_DEVICE_TYPE(DMX_VOICE_CARD, dmx_voice_card_device, "dmx_voice_card", "DMX Voice Card");

dmx_voice_card_device::dmx_voice_card_device(const machine_config &mconfig, const char *tag, device_t *owner, const dmx_voice_card_config &config, required_memory_region *sample_rom)
	: device_t(mconfig, DMX_VOICE_CARD, tag, owner, 0)
	, device_sound_interface(mconfig, *this)
	, m_timer(*this, "555_u5")
	, m_dac(*this, "dac_u8")
	, m_eg(*this, "envelope_generator")
	, m_filters(*this, "aa_sk_filter_%d", 0)
	, m_config(config)
	, m_sample_rom(sample_rom)
{
	init_pitch();
	init_gain_and_decay_variations();
}

dmx_voice_card_device::dmx_voice_card_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, DMX_VOICE_CARD, tag, owner, clock)
	, device_sound_interface(mconfig, *this)
	, m_timer(*this, "555_u5")
	, m_dac(*this, "dac_u8")
	, m_eg(*this, "envelope_generator")
	, m_filters(*this, "aa_sk_filter_%d", 0)
	// Need non-zero entries for the filter for validation to pass.
	, m_config(dmx_voice_card_config{.filter={1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}})
{
}

void dmx_voice_card_device::trigger(bool tr0, bool tr1)
{
	assert(tr0 || tr1);
	if (tr1 && tr0)
		m_trigger_mode = 3;
	else if (tr1)
		m_trigger_mode = 2;
	else if (tr0)
		m_trigger_mode = 1;
	else
		return

	m_stream->update();
	m_counter = 0;
	m_counting = true;
	m_decaying = false;

	if (m_config.pitch_control)
		select_pitch();

	const float iref = has_gain_variations() ? m_iref[m_trigger_mode] : m_iref[0];
	if (has_decay())  // Gain is controled by an envelope generator.
		m_eg->set_instant_v(iref);
	else  // Constant gain.
		m_dac->set_fixed_iref(iref);

	LOGMASKED(LOG_SOUND, "Trigger: (%d, %d) %d, %f uA\n", tr0, tr1, m_trigger_mode, iref * 1e6F);
}

void dmx_voice_card_device::set_pitch_adj(s32 t1_percent)
{
	m_stream->update();
	m_t1_percent = t1_percent;
	compute_pitch_variations();
}

void dmx_voice_card_device::device_add_mconfig(machine_config &config)
{
	static constexpr const double SK_R3 = RES_M(999.99);
	static constexpr const double SK_R4 = RES_R(0.001);

	TIMER(config, m_timer).configure_generic(FUNC(dmx_voice_card_device::clock_callback));
	DAC76(config, m_dac, 0U).configure_voltage_output(RES_K(2.4), RES_K(2.4));  // R16, R11 on voice card.

	if (has_decay())
	{
		VA_RC_EG(config, m_eg).set_c(m_config.c3);
		m_eg->add_route(0, m_dac, 1.0);
	}

	FILTER_BIQUAD(config, m_filters[0]).opamp_sk_lowpass_setup(
		m_config.filter.r15, m_config.filter.r14, SK_R3, SK_R4,
		m_config.filter.c5, m_config.filter.c4);
	FILTER_BIQUAD(config, m_filters[1]).opamp_sk_lowpass_setup(
		m_config.filter.r13, m_config.filter.r18, SK_R3, SK_R4,
		m_config.filter.c6, m_config.filter.c7);
	FILTER_BIQUAD(config, m_filters[2]).opamp_sk_lowpass_setup(
		m_config.filter.r19, m_config.filter.r20, SK_R3, SK_R4,
		m_config.filter.c8, m_config.filter.c9);

	m_dac->add_route(0, m_filters[0], 1.0);
	m_filters[0]->add_route(0, m_filters[1], 1.0);
	m_filters[1]->add_route(0, m_filters[2], 1.0);
	m_filters[2]->add_route(0, *this, 1.0);
}

void dmx_voice_card_device::device_start()
{
	m_stream = stream_alloc(1, 1, machine().sample_rate());

	save_item(NAME(m_counting));
	save_item(NAME(m_counter));
	save_item(NAME(m_trigger_mode));
	save_item(NAME(m_decaying));
}

void dmx_voice_card_device::device_reset()
{
	m_trigger_mode = 0;
	m_decaying = false;
	reset_counter();
	compute_pitch_variations();
	if (m_eg)
		m_eg->set_instant_v(0);
}

void dmx_voice_card_device::sound_stream_update(sound_stream &stream)
{
	stream.copy(0, 0);
}

void dmx_voice_card_device::reset_counter()
{
	m_counter = 0;
	m_counting = false;
}

void dmx_voice_card_device::init_pitch()
{
	// Precompute all variations of CV (pin 5 of 555 timer).

	// The CV equations were derived from Kirchhoff analysis and verified with
	// simulations: https://tinyurl.com/26x8oq75

	// Diode drop for pitch circuit. The value is based on simulations with the
	// various pitch configurations, instead of picking the typical 0.6V. This
	// should help with accuracy.
	static constexpr const float VD = 0.48F;
	static constexpr const float R_555 = RES_K(5);
	static constexpr const float R5 = RES_K(3.3);

	m_cv.clear();
	m_cv.push_back(VCC * 2 / 3); // The 555 default, if pin 5 is floating.

	if (m_config.pitch_control)
	{
		const float r12 = m_config.r12;

		// For trigger mode 1.
		const float alpha = 1.0F + r12 / m_config.r17;
		m_cv.push_back((alpha * R5 + r12) * (2 * VCC - 3 * VD) /
					   (3 * alpha * R5 + 3 * r12 + 2 * alpha * R_555) + VD);

		// For trigger mode 2.
		m_cv.push_back((R5 + r12) * (2 * VCC - 3 * VD) /
					   (3 * R5 + 3 * r12 + 2 * R_555) + VD);

		// For trigger mode 3.
		m_cv.push_back(m_cv[0]);
	}

	for (int i = 0; i < m_cv.size(); ++i)
		LOGMASKED(LOG_PITCH, "%s 555 CV %d: %f\n", tag(), i, m_cv[i]);

	// m_sample_t will be populated by subsequent calls to configure_pitch().
	m_sample_t.clear();
	m_sample_t.resize(m_cv.size());
}

void dmx_voice_card_device::compute_pitch_variations()
{
	static constexpr const float R3 = RES_K(1);
	static constexpr const float R4 = RES_K(10);
	static constexpr const float T1_MAX = RES_K(10);

	// The baseline pitch (and sampling rate) for all voice cards is controlled
	// by a 555 timer (U5). Users can adjust the pitch with a trimpot (T1).

	// For voice cards configured for pitch control (m_config.pitch_control is
	// true), pitch variations are accomplished by changing the Control Voltage
	// (pin 5) of the 555 (see init_pitch()).

	// Computing the timer period is a bit involved, because of the use of CV,
	// and because the 555 is not configured in the typical astable mode.
	// For an RC circuit, V(t) = Vstart + (Vend - Vstart) * (1 - exp(-t / RC)).
	// Solving for t: t = -RC * log( (Vend - V) / (Vend - Vstart) ).
	// Let t_high be the time interval for which the 555 output is high. This is
	// the time it takes for the capacitor to charge from CV/2 to CV.
	// Let t_low be the time interval for which the 555 output is low. This is
	// the time it takes for the capacitor to discharge from CV to CV/2.
	// The timer period is then: t_high + t_low. t_* can be computed by
	// substituting appropriate values in the function for `t`, keeping in mind
	// that RC, Vstart, Vend and V are different for charging and discharging.

	// Compute RC time constant for charging.
	assert(m_t1_percent >= 0 && m_t1_percent <= 100);
	const float r_charge = m_t1_percent * T1_MAX / 100 + R4;
	const float rc_charge = r_charge * m_config.c2;

	// Compute Vend and RC time constant for discharging, taking into account
	// the atypical 555 configuration.
	const float rc_discharge = RES_2_PARALLEL(r_charge, R3) * m_config.c2;
	const float ve_discharge = VCC * RES_VOLTAGE_DIVIDER(r_charge, R3);

	// Optimization: when m_config.pitch_control is true,
	// m_sample_t[0] = m_sample_t[3]. So skip index 0 and copy after the loop.
	const int start_i = m_config.pitch_control ? 1 : 0;

	for (int i = start_i; i < m_sample_t.size(); ++i)
	{
		const float cv = m_cv[i];
		const float half_cv = 0.5F * cv;

		// Time for C2 to charge from CV/2 (Vstart) to CV (V). Vend = VCC
		const float t_high = -rc_charge * logf((VCC - cv) / (VCC - half_cv));
		assert(t_high > 0);

		// Time for C2 to discharge from CV (Vstart) to CV/2 (V).
		const float t_low = -rc_discharge * logf((ve_discharge - half_cv) / (ve_discharge - cv));
		assert(t_low > 0);

		m_sample_t[i] = attotime::from_double(t_high + t_low);
		LOGMASKED(LOG_PITCH, "%s Pitch variation %d: %f (%f, %f)\n",
				tag(), i, 1.0 / m_sample_t[i].as_double(), t_high, t_low);
	}

	if (m_config.pitch_control)
		m_sample_t[0] = m_sample_t[3];

	select_pitch();
}

void dmx_voice_card_device::select_pitch()
{
	attotime sampling_t;
	if (m_config.pitch_control)
		sampling_t = m_sample_t[m_trigger_mode];
	else
		sampling_t = m_sample_t[0];

	if (sampling_t == m_timer->period())
		return;  // Avoid resetting the timer in this case.

	m_timer->adjust(sampling_t, 0, sampling_t);
	LOGMASKED(LOG_PITCH, "Setting sampling frequency: %f\n",
			1.0 / sampling_t.as_double());
}

void dmx_voice_card_device::init_gain_and_decay_variations()
{
	static constexpr const float VD = 0.6;  // Diode drop.
	static constexpr const float R8 = RES_K(2.7);
	static constexpr const float R9 = RES_K(5.6);
	static constexpr const float MAX_IREF = VCC / (R8 + R9);

	const float r12 = m_config.r12;
	const float r17 = m_config.r17;
	const float c3 = m_config.c3;

	// Precompute gain variations. Different gains are set by supplying
	// different reference currents to the MDAC.
	m_iref.clear();
	m_iref.push_back(MAX_IREF);
	if (has_gain_variations())  // Configured gain variations.
	{
		// The equations below were derived from Kirchhoff analysis and verified
		// with simulations: https://tinyurl.com/22wxwh8h

		// For trigger mode 1.
		m_iref.push_back((r12 * r17 * VCC + R8 * r12 * VD + R8 * r17 * VD) /
						 ((R8 * r12 * r17) + (r12 * r17 * R9) + (R8 * r17 * R9) + (R8 * r12 * R9)));
		// For trigger mode 2.
		m_iref.push_back((r12 * VCC + R8 * VD) / (r12 * R8 + R8 * R9 + r12 * R9));
		// For trigger mode 3.
		m_iref.push_back(m_iref[0]);
	}
	for (int i = 0; i < m_iref.size(); ++i)
	{
		LOGMASKED(LOG_VOLUME, "%s: Gain / Iref variation %d: %f uA\n",
				tag(), i, m_iref[i] * 1e6F);
	}

	// Precompute decay resistance variations.
	m_decay_r.clear();
	if (m_config.decay != dmx_voice_card_config::decay_mode::DISABLED && c3 > 0)
	{
		m_decay_r.push_back(R8 + R9);  // For when there are no variations.
		if (has_gain_variations())  // Gain variations imply decay variations.
		{
			m_decay_r.push_back(R8 + RES_3_PARALLEL(R9, r12, r17));  // For trigger mode 1.
			m_decay_r.push_back(R8 + RES_2_PARALLEL(R9, r12)); // For trigger mode 2.
			m_decay_r.push_back(R8 + R9);  // For trigger mode 3.
		}
		for (int i = 0; i < m_decay_r.size(); ++i)
			LOGMASKED(LOG_VOLUME, "%s: Decay R variation %d: %f\n", tag(), i, m_decay_r[i]);
	}
}

bool dmx_voice_card_device::is_decay_enabled() const
{
	switch (m_config.decay)
	{
		case dmx_voice_card_config::decay_mode::ENABLED:
			return true;
		case dmx_voice_card_config::decay_mode::ENABLED_ON_TR12:
			return m_trigger_mode == 1 || m_trigger_mode == 2;
		case dmx_voice_card_config::decay_mode::DISABLED:
			return false;
	}
	return false;
}

bool dmx_voice_card_device::is_early_decay_enabled() const
{
	switch (m_config.early_decay)
	{
		case dmx_voice_card_config::early_decay_mode::ENABLED:
			return true;
		case dmx_voice_card_config::early_decay_mode::ENABLED_ON_TR1:
			return m_trigger_mode == 1;
	}
	return false;
}

TIMER_DEVICE_CALLBACK_MEMBER(dmx_voice_card_device::clock_callback)
{
	if (!m_counting)
		return;

	++m_counter;

	const u16 rom_size = m_config.multi_rom ? 8 * 0x1000 : 0x1000;
	const u16 max_count = m_config.split_rom ? rom_size / 2 : rom_size;
	if (m_counter >= max_count)
	{
		reset_counter();
		LOGMASKED(LOG_SOUND, "Done counting %d\n\n", m_config.split_rom);
	}

	const u16 offset = (m_config.split_rom && m_trigger_mode != 3) ? max_count : 0;
	const u8 sample = (*m_sample_rom)->as_u8(m_counter + offset);
	m_dac->update();
	m_dac->sb_w(BIT(sample, 7));
	m_dac->b1_w(BIT(sample, 6));
	m_dac->b2_w(BIT(sample, 5));
	m_dac->b3_w(BIT(sample, 4));
	m_dac->b4_w(BIT(sample, 3));
	m_dac->b5_w(BIT(sample, 2));
	m_dac->b6_w(BIT(sample, 1));
	m_dac->b7_w(BIT(sample, 0));

	// Early decay starts when the counter's bit 6 transitions to 1.
	static constexpr const u16 EARLY_DECAY_START = 1 << 6;
	// If early decay is not enabled, but decay is enabled, it will start when
	// the counter's bit 10 transitions to 1.
	static constexpr const u16 LATE_DECAY_START = 1 << 10;

	if (!m_decaying && is_decay_enabled())
	{
		if ((is_early_decay_enabled() && m_counter >= EARLY_DECAY_START) ||
			m_counter >= LATE_DECAY_START)
		{
			assert(has_decay());
			m_decaying = true;
			if (has_decay_variations())
				m_eg->set_r(m_decay_r[m_trigger_mode]);
			else
				m_eg->set_r(m_decay_r[0]);
			m_eg->set_target_v(0);
			LOGMASKED(LOG_SOUND, "%s: Start decay\n", tag());
		}
	}
}

namespace {

constexpr const char MAINCPU_TAG[] = "z80";
constexpr const char NVRAM_TAG[] = "nvram";

// There are two anti-aliasing / reconstruction filter setups used. One for
// lower- and one for higher-frequency voices.

// ~10Khz cuttoff.
constexpr const dmx_voice_card_config::filter_components FILTER_CONFIG_LOW =
{
	.r15 = RES_K(6.8),
	.r14 = RES_K(6.2),
	.c5 = CAP_U(0.01),
	.c4 = CAP_U(0.0047),

	.r13 = RES_K(9.1),
	.r18 = RES_K(9.1),
	.c6 = CAP_U(0.01),
	.c7 = CAP_P(560),

	.r19 = RES_K(5.1),
	.r20 = RES_K(5.1),
	.c8 = CAP_U(0.047),
	.c9 = CAP_P(200),
};

// ~16 KHz cuttoff.
constexpr const dmx_voice_card_config::filter_components FILTER_CONFIG_HIGH =
{
	.r15 = RES_K(4.7),
	.r14 = RES_K(4.7),
	.c5 = CAP_U(0.01),
	.c4 = CAP_U(0.0047),

	.r13 = RES_K(5.6),
	.r18 = RES_K(5.6),
	.c6 = CAP_U(0.01),
	.c7 = CAP_P(560),

	.r19 = RES_K(3.3),
	.r20 = RES_K(3.3),
	.c8 = CAP_U(0.047),
	.c9 = CAP_P(200),
};

// Voice card configurations below reverse R12 and R17 compared to the 1981
// schematics. See comments in dmx_voice_card_config for more on this.

constexpr const dmx_voice_card_config BASS_CONFIG =
{
	.c2 = CAP_U(0.0033),
	.c3 = CAP_U(3.3),
	.r12 = RES_R(750),
	.r17 = RES_R(47),
	.split_rom = false,
	.multi_rom = false,
	.pitch_control = false,
	.decay = dmx_voice_card_config::decay_mode::ENABLED,
	.early_decay = dmx_voice_card_config::early_decay_mode::ENABLED,
	.filter = FILTER_CONFIG_LOW,
};

constexpr const dmx_voice_card_config SNARE_CONFIG =
{
	.c2 = CAP_U(0.0022),
	.c3 = 0,
	.r12 = RES_K(1.5),
	.r17 = RES_R(15),
	.split_rom = false,
	.multi_rom = false,
	.pitch_control = false,
	.decay = dmx_voice_card_config::decay_mode::DISABLED,
	.early_decay = dmx_voice_card_config::early_decay_mode::ENABLED_ON_TR1,
	.filter = FILTER_CONFIG_HIGH,
};

// The 1981 schematic shows 6.8uF for c3. electrongate.com reports 2.2uF. The
// service manual states that c3 can be changed, and offers a useful range of
// 2uF - 10uF.
// Similarly, resistor values in electrongate.com disagree with schematics.
// - electrongate.com: R12 = 6.8K, R17 not installed.
// - schematics: R12 = 1.5K, R17 = 15.
// Using the electrongate values below.
constexpr const dmx_voice_card_config HIHAT_CONFIG =
{
	.c2 = CAP_U(0.0033),
	.c3 = CAP_U(2.2),
	.r12 = RES_K(6.8),
	.r17 = RES_M(100),  // Not connected. Using a huge resistor.
	.split_rom = false,
	.multi_rom = false,
	.pitch_control = false,
	.decay = dmx_voice_card_config::decay_mode::ENABLED_ON_TR12,
	.early_decay = dmx_voice_card_config::early_decay_mode::ENABLED_ON_TR1,
	.filter = FILTER_CONFIG_HIGH,
};

constexpr const dmx_voice_card_config TOM_CONFIG =
{
	.c2 = CAP_U(0.0033),
	.c3 = CAP_U(6.8),
	.r12 = RES_K(33),
	.r17 = RES_K(18),
	.split_rom = false,
	.multi_rom = false,
	.pitch_control = true,
	.decay = dmx_voice_card_config::decay_mode::ENABLED,
	.early_decay = dmx_voice_card_config::early_decay_mode::ENABLED,
	.filter = FILTER_CONFIG_LOW,
};

constexpr dmx_voice_card_config PERC_CONFIG(float c2, bool filter_high)
{
	// The schematic and electrongate.com agree on all components for PERC1,
	// but disagree for PERC2:
	// * C2: 0.0033uF in schematic, but 0.0047uF on electrongate.com.
	// * Filter: "high" configuration in the schematic, but "low" on
	//   electrongate.com.
	return
	{
		.c2 = c2,
		.c3 = 0,
		.r12 = RES_K(1.5),
		.r17 = RES_R(30),
		.split_rom = true,
		.multi_rom = false,
		.pitch_control = false,
		.decay = dmx_voice_card_config::decay_mode::DISABLED,
		.early_decay = dmx_voice_card_config::early_decay_mode::ENABLED_ON_TR1,
		.filter = filter_high ? FILTER_CONFIG_HIGH : FILTER_CONFIG_LOW,
	};
}

constexpr const dmx_voice_card_config CYMBAL_CONFIG =
{
	.c2 = CAP_U(0.0033),
	.c3 = 0,
	.r12 = RES_R(680),
	.r17 = RES_R(100),
	.split_rom = true,
	.multi_rom = true,
	.pitch_control = false,
	.decay = dmx_voice_card_config::decay_mode::DISABLED,
	.early_decay = dmx_voice_card_config::early_decay_mode::ENABLED_ON_TR1,
	.filter = FILTER_CONFIG_HIGH,
};


class dmx_state : public driver_device
{
public:
	enum voice_card_indices
	{
		VC_BASS = 0,
		VC_SNARE,
		VC_HIHAT,
		VC_SMALL_TOMS,
		VC_LARGE_TOMS,
		VC_PERC1,
		VC_PERC2,
		VC_CYMBAL,
		NUM_VOICE_CARDS
	};
	static constexpr const int METRONOME_INDEX = NUM_VOICE_CARDS;

	static constexpr feature_type unemulated_features() { return feature::TAPE; }

	dmx_state(const machine_config &mconfig, device_type type, const char *tag) ATTR_COLD
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, MAINCPU_TAG)
		, m_digit_device(*this, "dl1414_%d", 0)
		, m_digit_output(*this, "digit_%d", 0U)
		, m_metronome(*this, "metronome_sound")
		, m_metronome_out(*this, "METRONOME_OUT")
		, m_metronome_timer(*this, "metronome_timer")
		, m_buttons(*this, "buttons_%d", 0)
		, m_faders(*this, "fader_p%d", 1)
		, m_pitch_trimmers(*this, "pitch_adj_%d", 1)
		, m_master_volume(*this, "fader_p10")
		, m_switches(*this, "switches")
		, m_external_triggers(*this, "external_triggers")
		, m_output_select(*this, "output_select")
		, m_clk_in(*this, "clk_in")
		, m_clk_out_tip(*this, "CLK_OUT_tip")
		, m_voices(*this, "voice_%d", 0)
		, m_voice_rc(*this, "voice_rc_filter_%d", 0)
		, m_left_mixer(*this, "left_mixer")
		, m_right_mixer(*this, "right_mixer")
		, m_mono_mixer(*this, "mono_mixer")
		, m_speaker(*this, "speaker")
		, m_samples(*this, "sample_%d", 0)
	{
	}

	void dmx(machine_config &config) ATTR_COLD;

	DECLARE_INPUT_CHANGED_MEMBER(clk_in_changed);
	DECLARE_INPUT_CHANGED_MEMBER(selected_output_changed);
	DECLARE_INPUT_CHANGED_MEMBER(voice_volume_changed);
	DECLARE_INPUT_CHANGED_MEMBER(master_volume_changed);
	DECLARE_INPUT_CHANGED_MEMBER(pitch_adj_changed);

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

private:
	void refresh_int_flipflop();
	void int_timer_preset_w(u8 data);
	void int_timer_enable_w(int state);
	TIMER_DEVICE_CALLBACK_MEMBER(int_timer_tick);

	void update_metronome();
	void metronome_mix_w(u8 data);
	void metronome_level_w(int state);
	void metronome_trigger_w(u8 data);
	TIMER_DEVICE_CALLBACK_MEMBER(metronome_timer_tick);

	void display_w(offs_t offset, u8 data);
	template<int DISPLAY> void display_output_w(offs_t offset, u16 data);

	u8 buttons_r(offs_t offset);
	u8 external_triggers_r();
	u8 cassette_and_switch_r();
	template<int GROUP> void gen_trigger_w(u8 data);

	void update_output();
	void update_mix_level(int voice);
	void update_master_volume();
	void update_pitch_adj(int voice);

	void memory_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_device_array<dl1414_device, 4> m_digit_device;
	output_finder<16> m_digit_output;
	required_device<speaker_sound_device> m_metronome;
	output_finder<> m_metronome_out;
	required_device<timer_device> m_metronome_timer;
	required_ioport_array<6> m_buttons;
	required_ioport_array<9> m_faders;
	required_ioport_array<8> m_pitch_trimmers;
	required_ioport m_master_volume;
	required_ioport m_switches;  // Includes foot switches.
	required_ioport m_external_triggers;
	required_ioport m_output_select;
	required_ioport m_clk_in;
	output_finder<> m_clk_out_tip;  // Tip conncetion of "CLK OUT" TRS jack.

	required_device_array<dmx_voice_card_device, 8> m_voices;
	required_device_array<filter_rc_device, 9> m_voice_rc;
	required_device<mixer_device> m_left_mixer;
	required_device<mixer_device> m_right_mixer;
	required_device<mixer_device> m_mono_mixer;
	required_device<speaker_device> m_speaker;
	required_memory_region_array<8> m_samples;

	// 40103 timer (U11 in Processor Board)
	u8 m_int_timer_preset = 0xff;
	bool m_int_timer_enabled = true;
	s16 m_int_timer_value = 0xff;
	u8 m_int_timer_out = 1;
	u8 m_int_flipflop_clock = 0;

	// Metronome state.
	bool m_metronome_on = false;
	bool m_metronome_mix = false;
	bool m_metronome_level_high = false;

	// The service manual states that the crystal is 4.912MHz, in the section
	// that describes the clock circuitry. However, the parts list specifies
	// this as a 4.9152 MHz crystal. The parts list value is likely the correct
	// one.
	// Divided by U35, 74LS74.
	static constexpr const XTAL CPU_CLOCK = 4.9152_MHz_XTAL / 2;
	// Divided by U34, 74LS393.
	static constexpr const XTAL INT_TIMER_CLOCK = CPU_CLOCK / 256;

	// Configuration for each voice needs to appear in the index specified by
	// the voice_card_indices enum for that voice.
	static constexpr const dmx_voice_card_config VOICE_CONFIGS[NUM_VOICE_CARDS] =
	{
		BASS_CONFIG,    // VC_BASS
		SNARE_CONFIG,   // VC_SNARE
		HIHAT_CONFIG,   // VC_HIHAT
		TOM_CONFIG,     // VC_SMALL_TOMS
		TOM_CONFIG,     // VC_LARGE_TOMS
		PERC_CONFIG(CAP_U(0.0033), true),   // VC_PERC1
		PERC_CONFIG(CAP_U(0.0047), false),  // VC_PERC2
		CYMBAL_CONFIG,  // VC_CYMBAL
	};

	// The loud click is a ~2.5V pulse, while the quiet one is a ~1.25V pulse.
	// See update_metronome().
	static constexpr double METRONOME_LEVELS[3] = { 0.0, 0.5, 1.0 };

	// The mixer's inputs are the 8 voices and the metronome.
	static constexpr const int NUM_MIXED_VOICES = NUM_VOICE_CARDS + 1;

	// The left and right channel mixing resistors for each voice. The voice
	// will be panned towards the side with lower resistance. Furthermore, these
	// resistors control the relative volume of the voices, where lower
	// resistance means louder.
	// Each entry needs to appear in the index specified by voice_card_indices,
	// except for the metronome entry, which appears last.
	// All resistors are located on the switchboard.
	static constexpr const std::tuple<float, float> MIX_RESISTORS[NUM_MIXED_VOICES] =
	{
		{ RES_K(10),  RES_K(10)  },  // R10, R9  - VC_BASS
		{ RES_K(8.2), RES_K(20)  },  // R12, R11 - VC_SNARE
		{ RES_K(20),  RES_K(8.2) },  // R14, R13 - VC_HIHAT
		{ RES_K(6.8), RES_K(100) },  // R16, R15 - VC_SMALL_TOMS
		{ RES_K(100), RES_K(6.8) },  // R18, R17 - VC_LARGE_TOMS
		{ RES_K(6.8), RES_K(100) },  // R22, R21 - VC_PERC1
		{ RES_K(100), RES_K(6.8) },  // R24, R23 - VC_PERC2
		{ RES_K(8.2), RES_K(20)  },  // R20, R19 - VC_CYMBAL
		{ RES_K(10),  RES_K(10)  },  // R26, R25 - METRONOME_INDEX
									 // ECO 304 values (see update_metronome()).
	};

	static constexpr const int VOICE_TO_FADER_MAP[NUM_MIXED_VOICES] =
	{
		0, 1, 2, 3, 4, 6, 7, 5, 8
	};
};

void dmx_state::refresh_int_flipflop()
{
	// All component designations refer to the Processor Board.

	// Interrups are used for maintaining tempo. They are generated by an SR
	// flipflop (U23B, 4013), with /Q connected to /INT. D is always 1.
	// If CLK IN is not connected, the flipflop is clocked by the timer's output
	// (U11, 40103).
	// If CLK IN is connected, it will clock the flipflop, and the timer's
	// output will be ignored.
	// The Z80's interrupt acknowledge cycle clears the flipfop (flipflop
	// "clear" connected to NOR(/M1, /IORQ) (U32, 4001)).
	// CSYN (cassette sync) will also trigger interrupts, via the flipflop's
	// "set" input.
	// The circuit ensures that either the timer is enabled, or CSYN signals
	// are generated, but not both. This prevents potentially conflicting
	// interrupts.

	u8 new_int_flipflop_clock = 0;
	if (m_clk_in->read() & 0x01)  // Is CLK IN plugged?
	{
		new_int_flipflop_clock = (m_clk_in->read() & 0x02) ? 1 : 0;
	}
	else
	{
		// 40103 (U11) timer output is inverted by Q1, R20 and R43.
		new_int_flipflop_clock = m_int_timer_out ? 0 : 1;
	}

	if (!m_int_flipflop_clock && new_int_flipflop_clock)  // 0->1 transition.
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, HOLD_LINE);

	m_int_flipflop_clock = new_int_flipflop_clock;
	m_clk_out_tip = m_int_flipflop_clock;
}

void dmx_state::int_timer_preset_w(u8 data)
{
	if (m_int_timer_preset == data)
		return;
	m_int_timer_preset = data;
	LOGMASKED(LOG_INT_TIMER, "INT timer preset: %02x\n", m_int_timer_preset);
}

void dmx_state::int_timer_enable_w(int state)
{
	// When the INT timer is enabled, the cassette sync INT generator is
	// disabled, and vice versa. Cassette functionality (including sync) is
	// not currently emulated.
	if (m_int_timer_enabled == bool(state))
		return;

	// The CLEN signal is inverted by U28 (4011 NAND), but it is active low
	// since it is connected on the /CE input of a 40103. So, logically,
	// "1" == "enabled".
	m_int_timer_enabled = bool(state);
	LOGMASKED(LOG_INT_TIMER, "INT timer enabled: %d\n", m_int_timer_enabled);
}

TIMER_DEVICE_CALLBACK_MEMBER(dmx_state::int_timer_tick)
{
	if (!m_int_timer_enabled)
		return;

	if (m_int_timer_value <= 0)  // We reached 0 in the previous clock cycle.
	{
		// The 40103 timer is configured for synchronous loading of the preset
		// value. So it starts over in the next clock cycle after reaching 0.
		// TODO: Actually verify that a preset of 0 means 256. Not clear in
		// the datasheet.
		if (m_int_timer_preset == 0)
			m_int_timer_value = 256;
		else
			m_int_timer_value = m_int_timer_preset;

		// The timer's output transitions to 1 in the clock cycle after reaching
		// 0 (the one we are handling here).
		m_int_timer_out = 1;
		refresh_int_flipflop();
		return;
	}

	--m_int_timer_value;

	if (m_int_timer_value <= 0)
	{
		// The timer's output goes low when its counter reaches 0.
		m_int_timer_out = 0;
		refresh_int_flipflop();
	}
}

void dmx_state::update_metronome()
{
	// The metronome "click" is a 1ms pulse. The metronome is active during
	// playback and recording, and the pulses appear on the "metronome out"
	// connection. The metronome can also be mixed with the rest of the voices,
	// but this only happens during recording, not playback. The loudness of
	// the "click" can be controlled by the firmware (2 distinct levels).
	// The metronome circuit is also used to generate "beep" tones.

	// The emulated circuitry is that of Engineering Change Order 304 (ECO 304,
	// January 1982), which improved the metronome.

	m_metronome_out = m_metronome_on ? 1 : 0;  // ~10 Volt pulse on "metronome out".

	int level = 0;
	if (m_metronome_on && m_metronome_mix)
		level = m_metronome_level_high ? 2 : 1;
	m_metronome->level_w(level);

	LOGMASKED(LOG_METRONOME, "Metronome update - on:%d, mix:%d, level:%d\n",
			m_metronome_on, m_metronome_mix, level);
}

void dmx_state::metronome_mix_w(u8 data)
{
	// D0 connected to D of U35 74LS74 (D-flipflop), which is clocked by
	// CLICK* from the address decoder (U7, 74LS42), acting as a 1-bit latch.
	const bool new_value = BIT(data, 0);
	if (m_metronome_mix == new_value)
		return;
	m_metronome_mix = new_value;
	update_metronome();
}

void dmx_state::metronome_level_w(int state)
{
	// LV1 signal. D3 of U20 (74C174 latch, processor board).
	const bool new_value = state;
	if (m_metronome_level_high == new_value)
		return;
	m_metronome_level_high = new_value;
	update_metronome();
}

void dmx_state::metronome_trigger_w(u8 /*data*/)
{
	// Writing to this port clocks U33A (D-flipflop). R62 and C32 form an RC
	// network that resets the flipflop after ~1ms. This becomes a pulse (via
	// Q8, R55, R54) on the "metronome out" connection, and in some cases also
	// mixed with the other voices (see update_metronome()).
	m_metronome_timer->adjust(attotime::from_msec(1));
	m_metronome_on = true;
	update_metronome();
}

TIMER_DEVICE_CALLBACK_MEMBER(dmx_state::metronome_timer_tick)
{
	m_metronome_on = false;
	update_metronome();
}

void dmx_state::display_w(offs_t offset, u8 data)
{
	// Decoding between different displays is done by U2 (74LS42). The
	// m_digit_device array is ordered from right to left (U6-U3). All
	// components are on the Switch Board.
	m_digit_device[offset >> 2]->bus_w(offset & 0x03, data);
}

template<int DISPLAY> void dmx_state::display_output_w(offs_t offset, u16 data)
{
	// The displays and the digits within each display are addressed from right
	// to left with increassing offset. Reversing that order in m_digit_output,
	// since left-to-right is more convenient for building the layout.
	static_assert(DISPLAY >= 0 && DISPLAY < 4);
	const int display_in_layout = 3 - DISPLAY;
	const int digit_in_layout = 3 - (offset & 0x03);
	m_digit_output[4 * display_in_layout + digit_in_layout] = data;
}

u8 dmx_state::buttons_r(offs_t offset)
{
	// Switches are inverted and buffered by 2 x 80C98 (inverting 3-state
	// buffers), U7 and U8 on Switch Board.
	return ~m_buttons[offset]->read();
}

u8 dmx_state::external_triggers_r()
{
	// External triggers are inverted by 2 X CA3086 NPN arrays (U4 for D0-D3 and
	// U5 for D4-D7) and buffered by U6 (74LS244). All components are on the
	// Processor Board.
	return ~m_external_triggers->read();
}

u8 dmx_state::cassette_and_switch_r()
{
	// All components are on the Processor Board.
	// Inputs buffered by U19 (75LS244). This is a mix of cassette-related
	// inputs and the states of foot-switches and toggle-switches.

	const u8 data = m_switches->read();

	const u8 d0 = 0;  // CD0: not yet implemented.
	const u8 d1 = 0;  // CD1: not yet implemented.
	const u8 d2 = 0;  // CD2: not yet implemented.

	const u8 d3 = BIT(data, 3);  // CASEN* (cassette enable switch. Active low).

	// START footswitch input is pulled high (R41) and ANDed with 1 (U22,
	// 74LS08, with the other input tied high via R42). So U22 is logically a
	// no-op. However, the NEXT footswitch is tied directly to U19.
	const u8 d4 = BIT(data, 4);  // START* (output of U22).
	const u8 d5 = BIT(data, 5);  // NEXT (pulled high by R58).
	const u8 d6 = BIT(data, 6);  // TSYN* (pulled high by R23).
	const u8 d7 = BIT(data, 7);  // PROT* (memory protect switch. Active low).

	return (d7 << 7) | (d6 << 6) | (d5 << 5) | (d4 << 4) |
		   (d3 << 3) | (d2 << 2) | (d1 << 1) | d0;
}

template<int GROUP> void dmx_state::gen_trigger_w(u8 data)
{
	// Triggers are active-high and only last for the duration of the write.
	// They are implemented by enabling an 74LS244 (U8 and U9) whose outputs are
	// normally pulled low.

	static_assert(GROUP >= 0 && GROUP <= 1);
	static constexpr const int TRIGGER_MAPPINGS[2][NUM_VOICE_CARDS] =
	{
		{0, 2, 4, 6, 1, 3, 5, 7},
		{8, 14, 10, 12, 9, 15, 11, 13},
	};

	if (data == 0)
		return;

	std::vector<bool> triggers(2 * NUM_VOICE_CARDS, false);
	for (int i = 0; i < NUM_VOICE_CARDS; ++i)
	{
		if (BIT(data, i))
		{
			const int trig_index = TRIGGER_MAPPINGS[GROUP][i];
			triggers[trig_index] = true;
			LOGMASKED(LOG_TRIGGERS, "Trigerred TR%d\n", trig_index);
		}
	}

	for (int i = 0; i < NUM_VOICE_CARDS; ++i)
	{
		const bool tr0 = triggers[2 * i];
		const bool tr1 = triggers[2 * i + 1];
		if (tr0 || tr1)
			m_voices[i]->trigger(tr0, tr1);
	}
}

void dmx_state::update_output()
{
	const float stereo_gain = (m_output_select->read() & 0x01) ? 1 : 0;
	m_left_mixer ->set_route_gain(0, m_speaker, 0, stereo_gain);
	m_mono_mixer ->set_route_gain(0, m_speaker, 0, 1 - stereo_gain);
	m_right_mixer->set_route_gain(0, m_speaker, 1, stereo_gain);
	m_mono_mixer ->set_route_gain(0, m_speaker, 1, 1 - stereo_gain);
	LOGMASKED(LOG_FADERS, "Output changed to: %d\n", m_output_select->read());
}

void dmx_state::update_mix_level(int voice)
{
	// Computes the gain of a voice for the left and right channels, taking
	// into account the fader position and the loading from the mixing
	// resistors. Each voice has a hardcoded pan and relative mixing ratio based
	// on the resistors in MIX_RESISTORS.
	// Also reconfigures the voice's output RC filter (high-pass), since its `R`
	// changes when the volume fader moves. This won't be audible, since the
	// cutoff frequency is mostly <10 HZ, except for low volumes.
	assert(voice >= 0 && voice < NUM_MIXED_VOICES);

	static constexpr const float P_MAX = RES_K(10);  // Volume potentiometer.
	static constexpr const float VC_R21 = RES_R(47);  // R21 on voice cards.
	static constexpr const float VC_C10 = CAP_U(33);  // C10 on voice cards.
	static constexpr const float PB_C24 = CAP_U(6.8);  // C24 on processor board.

	// Feedback resistors on the left and right summing op-amps (U1B, U1C).
	static constexpr const float R_FEEDBACK_LEFT = RES_K(27);  // R30.
	static constexpr const float R_FEEDBACK_RIGHT = RES_K(27);  // R29.

	const s32 pot_percent = m_faders[VOICE_TO_FADER_MAP[voice]]->read();
	const float r_pot_bottom = P_MAX * RES_AUDIO_POT_LAW(pot_percent / 100.0F);
	const float r_pot_top = P_MAX - r_pot_bottom;
	const float r_mix_left = std::get<0>(MIX_RESISTORS[voice]);
	const float r_mix_right = std::get<1>(MIX_RESISTORS[voice]);
	const float r_gnd = RES_3_PARALLEL(r_pot_bottom, r_mix_left, r_mix_right);
	const float r_top_extra = (voice == METRONOME_INDEX) ? 0 : VC_R21;
	const float v_pot = RES_VOLTAGE_DIVIDER(r_pot_top + r_top_extra, r_gnd);

	// -v_pot because the summing opamp mixer is inverting.
	const float gain_left = -v_pot * R_FEEDBACK_LEFT / r_mix_left;
	const float gain_right = -v_pot * R_FEEDBACK_RIGHT / r_mix_right;

	// Resistance as seen from the output side of the DC-blocking capacitor.
	const float rc_r = r_pot_top + r_gnd;
	const float rc_c = (voice == METRONOME_INDEX) ? PB_C24 : VC_C10;

	m_voice_rc[voice]->filter_rc_set_RC(filter_rc_device::HIGHPASS, rc_r, 0, 0, rc_c);
	m_voice_rc[voice]->set_route_gain(0, m_left_mixer, 0, gain_left);
	m_voice_rc[voice]->set_route_gain(0, m_right_mixer, 0, gain_right);

	LOGMASKED(LOG_FADERS, "Voice %d volume changed to: %d (gain L:%f, R:%f), HPF cutoff: %.2f Hz\n",
			voice, pot_percent, gain_left, gain_right, 1.0F / (2 * float(M_PI) * r_gnd * rc_c));
}

void dmx_state::update_master_volume()
{
	// The audio pipeline operates on voltage magnitudes. This scaler normalizes
	// the final output's range to approximately: -1 - 1.
	static constexpr const float VOLTAGE_TO_SOUND_SCALER = 0.04F;

	const s32 volume_value = m_master_volume->read();
	const float gain = VOLTAGE_TO_SOUND_SCALER * RES_AUDIO_POT_LAW(volume_value / 100.0F);
	m_left_mixer->set_output_gain(0, gain);
	m_right_mixer->set_output_gain(0, gain);
	LOGMASKED(LOG_FADERS, "Master volume changed: %d - %f\n", volume_value, gain);
}

void dmx_state::update_pitch_adj(int voice)
{
	const s32 trimmer_value = m_pitch_trimmers[VOICE_TO_FADER_MAP[voice]]->read();
	// Using "100 -" so that larger values increase pitch.
	m_voices[voice]->set_pitch_adj(100 - trimmer_value);
	LOGMASKED(LOG_PITCH, "Voice %d pitch adjustment changed: %d\n", voice, trimmer_value);
}

void dmx_state::memory_map(address_map &map)
{
	// Component designations refer to the Processor Board.
	// Memory decoding done by U2 (74LS42) and U22 (74LS08).
	map.global_mask(0x3fff);  // A14 and A15 not used.
	map(0x0000, 0x1fff).rom();  // 2 x 2732 (4K x 8bit) ROMs, U18-U17.
	// 4 x 6116 (2K x 8bit) RAMs, U16-U13. U3 (4071, OR-gate) will only allow
	// RAM select signals to go through if fully powered up.
	map(0x2000, 0x3fff).ram().share(NVRAM_TAG);
}

void dmx_state::io_map(address_map &map)
{
	// Signal names (e.g. GEN0*) match those in the schematics.

	map.global_mask(0x7f);  // A7-A15 not used for port decoding or I/O.

	// Write decoding done by U7 (74LS42) on the Processor Board.
	map(0x00, 0x0f).w(FUNC(dmx_state::display_w));  // ID*.
	map(0x10, 0x10).mirror(0x0f).w(FUNC(dmx_state::gen_trigger_w<0>));  // GEN0*
	map(0x20, 0x20).mirror(0x0f).w(FUNC(dmx_state::gen_trigger_w<1>));  // GEN1*
	map(0x30, 0x30).mirror(0x0f).w(FUNC(dmx_state::int_timer_preset_w));  // STIM*
	map(0x40, 0x40).mirror(0x0f).w(FUNC(dmx_state::metronome_trigger_w));  // METRONOME OUT
	map(0x50, 0x50).mirror(0x0f).w("cas_latch", FUNC(output_latch_device::write));  // CAS0*
	map(0x60, 0x60).mirror(0x0f).w(FUNC(dmx_state::metronome_mix_w));  // CLICK*

	// Read decoding done by U9 (74LS42) on the Switch Board.
	map(0x00, 0x05).mirror(0x78).r(FUNC(dmx_state::buttons_r));  // IORD*
	map(0x06, 0x06).mirror(0x78).r(FUNC(dmx_state::external_triggers_r));  // SW6*
	map(0x07, 0x07).mirror(0x78).r(FUNC(dmx_state::cassette_and_switch_r));  // CASI*
}

void dmx_state::machine_start()
{
	m_clk_out_tip.resolve();
	m_metronome_out.resolve();
	m_digit_output.resolve();

	save_item(NAME(m_int_timer_preset));
	save_item(NAME(m_int_timer_enabled));
	save_item(NAME(m_int_timer_value));
	save_item(NAME(m_int_timer_out));
	save_item(NAME(m_int_flipflop_clock));

	save_item(NAME(m_metronome_on));
	save_item(NAME(m_metronome_mix));
	save_item(NAME(m_metronome_level_high));
}

void dmx_state::machine_reset()
{
	update_output();
	update_master_volume();
	for (int i = 0; i < NUM_MIXED_VOICES; ++i)
		update_mix_level(i);
	for (int i = 0; i < NUM_VOICE_CARDS; ++i)
		update_pitch_adj(i);
}

void dmx_state::dmx(machine_config &config)
{
	Z80(config, m_maincpu, CPU_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &dmx_state::memory_map);
	m_maincpu->set_addrmap(AS_IO, &dmx_state::io_map);

	NVRAM(config, NVRAM_TAG, nvram_device::DEFAULT_ALL_0);

	// 40103 timer (U11 on Processor Board). See refresh_int_flipflop() and
	// int_timer_tick().
	TIMER(config, "int_timer_u11").configure_periodic(
		FUNC(dmx_state::int_timer_tick), attotime::from_hz(INT_TIMER_CLOCK));

	// A one-shot timer based on an RC-cicruit. See metronome_trigger_w().
	TIMER(config, m_metronome_timer).configure_generic(
		FUNC(dmx_state::metronome_timer_tick));

	DL1414T(config, m_digit_device[0], 0U).update().set(FUNC(dmx_state::display_output_w<0>));
	DL1414T(config, m_digit_device[1], 0U).update().set(FUNC(dmx_state::display_output_w<1>));
	DL1414T(config, m_digit_device[2], 0U).update().set(FUNC(dmx_state::display_output_w<2>));
	DL1414T(config, m_digit_device[3], 0U).update().set(FUNC(dmx_state::display_output_w<3>));

	config.set_default_layout(layout_oberheim_dmx);

	// 74C174 (U20, on Processor Board), 6-bit latch.
	output_latch_device &cas_latch(OUTPUT_LATCH(config, "cas_latch"));
	cas_latch.bit_handler<0>().set_output("CDATO");
	cas_latch.bit_handler<1>().set_output("ARM");
	cas_latch.bit_handler<2>().set(FUNC(dmx_state::int_timer_enable_w));
	// Bit 3 is an open-collector connection to the Ring of the "CLK OUT" TRS jack.
	cas_latch.bit_handler<3>().set_output("CLK_OUT_ring").invert();
	cas_latch.bit_handler<4>().set(FUNC(dmx_state::metronome_level_w));  // LV1.
	// Bit 5 not connected.

	MIXER(config, m_left_mixer);
	MIXER(config, m_right_mixer);
	for (int i = 0; i < NUM_VOICE_CARDS; ++i)
	{
		DMX_VOICE_CARD(config, m_voices[i], VOICE_CONFIGS[i], &m_samples[i]);
		FILTER_RC(config, m_voice_rc[i]);
		// The RC filters are initialized in machine_reset() (specifically:
		// update_mix_level()).
		m_voices[i]->add_route(ALL_OUTPUTS, m_voice_rc[i], 1.0);
		m_voice_rc[i]->add_route(ALL_OUTPUTS, m_left_mixer, 1.0);
		m_voice_rc[i]->add_route(ALL_OUTPUTS, m_right_mixer, 1.0);
	}

	SPEAKER_SOUND(config, m_metronome);
	FILTER_RC(config, m_voice_rc[METRONOME_INDEX]);
	m_metronome->set_levels(3, METRONOME_LEVELS);
	m_metronome->add_route(ALL_OUTPUTS, m_voice_rc[METRONOME_INDEX], 1.0);
	m_voice_rc[METRONOME_INDEX]->add_route(ALL_OUTPUTS, m_left_mixer, 1.0);
	m_voice_rc[METRONOME_INDEX]->add_route(ALL_OUTPUTS, m_right_mixer, 1.0);

	// Passive mixer using 1K resistors (R33 and R34).
	MIXER(config, m_mono_mixer);
	m_left_mixer->add_route(ALL_OUTPUTS, m_mono_mixer, 0.5);
	m_right_mixer->add_route(ALL_OUTPUTS, m_mono_mixer, 0.5);

	// Only one of the left (right) or mono will be active for each speaker at
	// runtime. Controlled by a config setting (see update_output()).

	SPEAKER(config, m_speaker, 2).front();
	m_left_mixer->add_route(ALL_OUTPUTS, m_speaker, 1.0, 0);
	m_mono_mixer->add_route(ALL_OUTPUTS, m_speaker, 1.0, 0);
	m_right_mixer->add_route(ALL_OUTPUTS, m_speaker, 1.0, 1);
	m_mono_mixer->add_route(ALL_OUTPUTS, m_speaker, 1.0, 1);
}

DECLARE_INPUT_CHANGED_MEMBER(dmx_state::clk_in_changed)
{
	refresh_int_flipflop();
}

DECLARE_INPUT_CHANGED_MEMBER(dmx_state::selected_output_changed)
{
	update_output();
}

DECLARE_INPUT_CHANGED_MEMBER(dmx_state::voice_volume_changed)
{
	update_mix_level(param);
}

DECLARE_INPUT_CHANGED_MEMBER(dmx_state::master_volume_changed)
{
	update_master_volume();
}

DECLARE_INPUT_CHANGED_MEMBER(dmx_state::pitch_adj_changed)
{
	update_pitch_adj(param);
}

INPUT_PORTS_START(dmx)
	PORT_START("buttons_0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("9") PORT_CODE(KEYCODE_9_PAD)

	PORT_START("buttons_1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("<") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME(">") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SWING") PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SONG") PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("EDIT") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("STEP") PORT_CODE(KEYCODE_PGUP)

	PORT_START("buttons_2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("RECORD") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("ERASE") PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("COPY") PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("LENGTH") PORT_CODE(KEYCODE_END)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PLAY") PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("TEMPO") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("QUANT") PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SIGN") PORT_CODE(KEYCODE_COLON)

	PORT_START("buttons_3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("BASS 3") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SNARE 3") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("HI-HAT OPEN") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("TOM 3") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("TOM 6") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("CYMB 3") PORT_CODE(KEYCODE_N)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("RIMSHOT") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("CLAPS") PORT_CODE(KEYCODE_COMMA)

	PORT_START("buttons_4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("BASS 2") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SNARE 2") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("HI-HAT ACC") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("TOM 2") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("TOM 5") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("CYMB 2") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("TAMP 2") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SHAKE 2") PORT_CODE(KEYCODE_K)

	PORT_START("buttons_5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("BASS 1") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SNARE 1") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("HI-HAT CLOSED") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("TOM 1") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("TOM 4") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("CYMB 1") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("TAMB 1") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SHAKE 1") PORT_CODE(KEYCODE_I)

	PORT_START("switches")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("CASSETTE ENABLE") PORT_TOGGLE
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("START")  // Footswitch.
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("NEXT")  // Footswitch.
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SYNC IN middle")  // Plug input.
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("MEMORY PROTECT") PORT_TOGGLE

	PORT_START("external_triggers")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("BASS TRIG")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PERC1 TRIG")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("CYMBAL TRIG")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TOM2 TRIG")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TOM1 TRIG")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("HI-HAT TRIG")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("SNARE TRIG")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PERC2 TRIG")

	PORT_START("clk_in")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("CLK IN plugged") PORT_CODE(KEYCODE_QUOTE) PORT_TOGGLE
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("CLK IN") PORT_CODE(KEYCODE_SLASH)
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dmx_state::clk_in_changed), 0)

	PORT_START("output_select")
	PORT_CONFNAME(0x01, 0x01, "Output")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dmx_state::selected_output_changed), 0)
	PORT_CONFSETTING(   0x00, "Mono")
	PORT_CONFSETTING(   0x01, "Stereo")

	// Fader potentiometers. P1-P10 on the Switch Board.

	PORT_START("fader_p1")
	PORT_ADJUSTER(100, "BASS volume")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dmx_state::voice_volume_changed), dmx_state::VC_BASS)

	PORT_START("fader_p2")
	PORT_ADJUSTER(100, "SNARE volume")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dmx_state::voice_volume_changed), dmx_state::VC_SNARE)

	PORT_START("fader_p3")
	PORT_ADJUSTER(100, "HI-HAT volume")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dmx_state::voice_volume_changed), dmx_state::VC_HIHAT)

	PORT_START("fader_p4")
	PORT_ADJUSTER(100, "TOM1 volume")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dmx_state::voice_volume_changed), dmx_state::VC_SMALL_TOMS)

	PORT_START("fader_p5")
	PORT_ADJUSTER(100, "TOM2 volume")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dmx_state::voice_volume_changed), dmx_state::VC_LARGE_TOMS)

	PORT_START("fader_p6")
	PORT_ADJUSTER(100, "CYMBAL volume")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dmx_state::voice_volume_changed), dmx_state::VC_CYMBAL)

	PORT_START("fader_p7")
	PORT_ADJUSTER(100, "PERC1 volume")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dmx_state::voice_volume_changed), dmx_state::VC_PERC1)

	PORT_START("fader_p8")
	PORT_ADJUSTER(100, "PERC2 volume")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dmx_state::voice_volume_changed), dmx_state::VC_PERC2)

	PORT_START("fader_p9")
	PORT_ADJUSTER(100, "MET volume")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dmx_state::voice_volume_changed), dmx_state::METRONOME_INDEX)

	PORT_START("fader_p10")
	PORT_ADJUSTER(100, "VOLUME")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dmx_state::master_volume_changed), 0)

	// Tunning potentiomenters. One per voice card, designated as T1 and labeled
	// as "PITCH ADJ."

	PORT_START("pitch_adj_1")
	PORT_ADJUSTER(50, "BASS pitch")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dmx_state::pitch_adj_changed), dmx_state::VC_BASS)

	PORT_START("pitch_adj_2")
	PORT_ADJUSTER(50, "SNARE pitch")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dmx_state::pitch_adj_changed), dmx_state::VC_SNARE)

	PORT_START("pitch_adj_3")
	PORT_ADJUSTER(50, "HI-HAT pitch")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dmx_state::pitch_adj_changed), dmx_state::VC_HIHAT)

	PORT_START("pitch_adj_4")
	PORT_ADJUSTER(50, "TOM1 pitch")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dmx_state::pitch_adj_changed), dmx_state::VC_SMALL_TOMS)

	PORT_START("pitch_adj_5")
	PORT_ADJUSTER(50, "TOM2 pitch")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dmx_state::pitch_adj_changed), dmx_state::VC_LARGE_TOMS)

	PORT_START("pitch_adj_6")
	PORT_ADJUSTER(50, "CYMBAL pitch")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dmx_state::pitch_adj_changed), dmx_state::VC_CYMBAL)

	PORT_START("pitch_adj_7")
	PORT_ADJUSTER(50, "PERC1 pitch")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dmx_state::pitch_adj_changed), dmx_state::VC_PERC1)

	PORT_START("pitch_adj_8")
	PORT_ADJUSTER(50, "PERC2 pitch")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dmx_state::pitch_adj_changed), dmx_state::VC_PERC2)
INPUT_PORTS_END

ROM_START(obdmx)
	ROM_REGION(0x2000, MAINCPU_TAG, 0)  // 2 x 2732 (4K x 8bit) ROMs, U18 and U17.
	ROM_DEFAULT_BIOS("rev_f")

	ROM_SYSTEM_BIOS(0, "debug", "DMX debug firmware")
	ROMX_LOAD("dmx_dbg.u18", 0x000000, 0x001000, CRC(b3a24d64) SHA1(99a64b92f1520aaf2e7117b2fc75bdbd847f3a7f), ROM_BIOS(0))
	ROMX_FILL(0x001000, 0x001000, 0xff, ROM_BIOS(0))  // No ROM needed in U17.

	ROM_SYSTEM_BIOS(1, "rev_b", "DMX 1.00 (Revision B), 1981")
	ROMX_LOAD("dmx_b0.u18", 0x000000, 0x001000, CRC(69116c5b) SHA1(29448b60d7f864b0a9df2a786b877feb7b019c05), ROM_BIOS(1))
	ROMX_LOAD("dmx_b1.u17", 0x001000, 0x001000, CRC(49084dee) SHA1(fffe4e6592dc586c0a0f03bed18308df38b991da), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "rev_f", "DMX 2.21 (Revision F), 1982")
	ROMX_LOAD("dmx_f0.u18", 0x000000, 0x001000, CRC(a01e5e7d) SHA1(74faf9c056c7e3eb89ed7657d8b64b1d3fe0ec22), ROM_BIOS(2))
	ROMX_LOAD("dmx_f1.u17", 0x001000, 0x001000, CRC(b24d4040) SHA1(74e7ae87cdcf9442cf76b696970c0efe52a30262), ROM_BIOS(2))

	// Sample ROMs. The number appended to "sample_" needs to match the
	// corresponding voice in the dmx_state::voice_card_indices enum.

	ROM_REGION(0x1000, "sample_0", 0)  // Bass (dmx_state::VC_BASS).
	ROM_LOAD("21kick.u7", 0x000000, 0x001000, CRC(3ab63275) SHA1(83b0e61e42c9d277f205f4ff5dfcb796c05084b8))

	ROM_REGION(0x1000, "sample_1", 0)  // Snare (dmx_state::VC_SNARE).
	ROM_LOAD("snare6.u7", 0x000000, 0x001000, CRC(eca57ad7) SHA1(f65dcae905e0f7db9bf9324153ddd16a33dbdc8b))

	ROM_REGION(0x1000, "sample_2", 0)  // Hi-Hat (dmx_state::VC_HIHAT).
	ROM_LOAD("hat1a.u7", 0x000000, 0x001000, CRC(36e934f9) SHA1(e97d65bfb2d98cbf463a92d12e2ab91938757d37))

	ROM_REGION(0x1000, "sample_3", 0)  // Small Toms (dmx_state::VC_SMALL_TOMS).
	ROM_LOAD("stom5.u7", 0x000000, 0x001000, CRC(e8022d72) SHA1(61a14da4d96db12b040ab9529208b165212b3693))

	ROM_REGION(0x1000, "sample_4", 0)  // Large Toms (dmx_state::VC_LARGE_TOMS).
	ROM_LOAD("sfltom2.u7", 0x000000, 0x001000, CRC(f5b48db0) SHA1(a85e72a9f87838c26538e5873f6938e51a517c90))

	ROM_REGION(0x1000, "sample_5", 0)  // Perc 1: Tambourine / Rimshot (dmx_state::VC_PERC1).
	ROM_LOAD("stik.u7", 0x000000, 0x001000, CRC(61af39e3) SHA1(5648674854a8db80656bf729c4f353b75d101d7b))

	ROM_REGION(0x1000, "sample_6", 0)  // Perc 2: Shaker / Claps (dmx_state::VC_PERC2).
	ROM_LOAD("shake6.u7", 0x000000, 0x001000, CRC(00549f07) SHA1(d41c3357d486c398f882774130789c9756dfb9af))

	ROM_REGION(0x8000, "sample_7", 0)  // Cymbal: Ride / Crash (dmx_state::VC_CYMBAL).
	ROM_LOAD("crash1.u12", 0x000000, 0x001000, CRC(40d584af) SHA1(98702946e80892eb834433268164404fab3b0e28))
	ROM_LOAD("crash2.u11", 0x001000, 0x001000, CRC(8daf2447) SHA1(59c0af6c46c04ccd0eff90070de4fe08c5a47b42))
	ROM_LOAD("crash3.u10", 0x002000, 0x001000, CRC(4a41c86d) SHA1(baffb30659bb628d4fd9a382dc851683aad92b88))
	ROM_LOAD("crash4.u9", 0x003000, 0x001000, CRC(b30876a9) SHA1(fe9f906f38ad9354d043794d6c22d07c78672261))
	ROM_LOAD("ride2a1.u8", 0x004000, 0x001000, CRC(17c960e8) SHA1(5f4681979c6339ff1c38dc90257d14a0a7516c52))
	ROM_LOAD("ride2a2.u7", 0x005000, 0x001000, CRC(5b2198d0) SHA1(5e008c76f949a6357c1160e00d43631585702d45))
	ROM_LOAD("ride2a3.u6", 0x006000, 0x001000, CRC(4f67cacb) SHA1(8abc2c6769bd315c489969486de02b567348ea94))
	ROM_LOAD("ride2a4.u5", 0x007000, 0x001000, CRC(25656751) SHA1(46e5f70da82f8845dd820cbcf942bd0e7e328d56))
ROM_END

}  // anonymous namespace

SYST(1980, obdmx, 0, 0, dmx, dmx, dmx_state, empty_init, "Oberheim", "DMX", MACHINE_SUPPORTS_SAVE)



dolphunk.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Dolphin / Dauphin

2010-04-08 Skeleton driver.
2012-05-20 Fixed keyboard, added notes & speaker [Robbbert]
2013-11-03 Added cassette [Robbbert]

Minimal Setup:
    0000-00FF ROM "MO" (74S471)
    0100-01FF ROM "MONI" (74S471)
    0200-02FF RAM (2x 2112)
    18 pushbuttons for programming (0-F, ADR, NXT).
    4-digit LED display.

Other options:
    0400-07FF Expansion RAM (8x 2112)
    0800-08FF Pulse for operation of an optional EPROM programmer
    0C00-0FFF ROM "MONA" (2708)
    LEDs connected to all Address and Data Lines
    LEDs connected to WAIT and FLAG lines.
    Speaker with a LED wired across it.
    PAUSE switch.
    RUN/STOP switch.
    STEP switch.
    CLOCK switch.

Cassette player connected to SENSE and FLAG lines.

Keyboard encoder: AY-5-2376 (57 keys)

CRT interface: (512 characters on a separate bus)
    2114 video ram (one half holds the lower 4 data bits, other half the upper bits)
    74LS175 holds the upper bits for the 74LS472
    74LS472 Character Generator

NOTE: The cassette rom is missing, when the ADR button (- key) is pressed,
      it causes a freeze in nodebug mode, and a crash in debug mode.
      To see it, start in debug mode. g 6c. In the emulation, press the
      minus key. The debugger will stop and you can see an instruction
      referencing location 0100, which is in the missing rom.
      The machine is marked MNW because it is not possible to save/load programs.

Keys:
    0-9,A-F hexadecimal numbers
    UP - (NXT) to enter data and advance to the next address
    MINUS - (ADR) to change the address to what is shown in the data side
    Special keys:
        Hold UP, hold 0, release UP, release 0 - execute program at the current address (i.e. 2xx)
        Hold UP, hold 1, release UP, release 1 - execute program at address 0C00 (rom MONA)
        Hold UP, hold 2, release UP, release 2 - play a tune with the keys
        Hold UP, hold 3, release UP, release 3 - decrement the address by 2
        Hold MINUS, hold any hex key, release MINUS, release other key - execute program
          at the current address-0x100 (i.e. 1xx).

If you want to scan through other areas of memory (e.g. the roms), alter the
data at address 2F9 (high byte) and 2FA (low byte).

How to Use:
    The red digits are the address, and the orange digits are the data.
    The address range is 200-2FF (the 2 isn't displayed). To select an address,
    either press the UP key until you get there, or type the address and press
    minus. The orange digits show the current data at that address. To alter
    data, just type it in and press UP.

To play the reflexes game, hold UP, press 1, release UP, release 1.
    The display will show A--0 (or some random number in the last position).
    Press any odd-numbered key (B is convenient), and read off the reaction time.
    After a short delay it will show '--' again, this is the signal to react.
    It doesn't seem to reset the counters each time around.

Test Paste:
    Paste this: 11^22^33^44^55^66^77^88^99^00-
    Now press up-arrow to review the data that was entered.

TODO:
    - Find missing roms. Once the cassette rom is found, need to work out
      how to use it, and find out if the cassette interface works.
    - Add optional hardware listed above

Thanks to Amigan site for various documents.


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

#include "emu.h"
#include "cpu/s2650/s2650.h"
#include "imagedev/cassette.h"
#include "machine/timer.h"
#include "sound/spkrdev.h"
#include "speaker.h"
#include "video/pwm.h"

#include "dolphunk.lh"


namespace {

class dauphin_state : public driver_device
{
public:
	dauphin_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_speaker(*this, "speaker")
		, m_cass(*this, "cassette")
		, m_display(*this, "display")
	{ }

	void dauphin(machine_config &config);

private:
	int cass_r();
	u8 port07_r();
	void port00_w(offs_t offset, u8 data);
	void port06_w(u8 data);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_w);
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	void machine_start() override ATTR_COLD;

	u8 m_cass_data = 0U;
	u8 m_last_key = 0U;
	bool m_cassbit = 0;
	bool m_cassold = 0;
	bool m_speaker_state = 0;
	required_device<s2650_device> m_maincpu;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cass;
	required_device<pwm_display_device> m_display;
};

int dauphin_state::cass_r()
{
	return (m_cass->input() > 0.03) ? 1 : 0;
}

void dauphin_state::port00_w(offs_t offset, u8 data)
{
	m_display->matrix(1<<offset, data);
}

void dauphin_state::port06_w(u8 data)
{
	m_speaker_state ^=1;
	m_speaker->level_w(m_speaker_state);
}

u8 dauphin_state::port07_r()
{
	u8 keyin, i, data = 0x40;

	keyin = ioport("X0")->read();
	if (keyin != 0xff)
		for (i = 0; i < 8; i++)
			if (BIT(~keyin, i))
				data = i | 0xc0;

	keyin = ioport("X1")->read();
	if (keyin != 0xff)
		for (i = 0; i < 8; i++)
			if (BIT(~keyin, i))
				data = i | 0xc8;

	if (data == m_last_key)
		data &= 0x7f;
	else
		m_last_key = data;

	data |= ioport("X2")->read();

	return data;
}

TIMER_DEVICE_CALLBACK_MEMBER(dauphin_state::kansas_w)
{
	m_cass_data++;

	if (m_cassbit != m_cassold)
	{
		m_cass_data = 0;
		m_cassold = m_cassbit;
	}

	if (m_cassbit)
		m_cass->output(BIT(m_cass_data, 1) ? -1.0 : +1.0); // 1000Hz
	else
		m_cass->output(BIT(m_cass_data, 0) ? -1.0 : +1.0); // 2000Hz
}

void dauphin_state::machine_start()
{
	save_item(NAME(m_cass_data));
	save_item(NAME(m_last_key));
	save_item(NAME(m_cassbit));
	save_item(NAME(m_cassold));
	save_item(NAME(m_speaker_state));
}

void dauphin_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x01ff).rom();
	map(0x0200, 0x02ff).ram();
	map(0x0c00, 0x0fff).rom();
}

void dauphin_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x03).w(FUNC(dauphin_state::port00_w)); // 4-led display
	map(0x06, 0x06).w(FUNC(dauphin_state::port06_w));  // speaker (NOT a keyclick)
	map(0x07, 0x07).r(FUNC(dauphin_state::port07_r)); // pushbuttons
}

/* Input ports */
static INPUT_PORTS_START( dauphin )
	PORT_START("X0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')

	PORT_START("X1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("X2")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("NXT") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("ADR") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
INPUT_PORTS_END


void dauphin_state::dauphin(machine_config &config)
{
	/* basic machine hardware */
	S2650(config, m_maincpu, XTAL(1'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &dauphin_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &dauphin_state::io_map);
	m_maincpu->sense_handler().set(FUNC(dauphin_state::cass_r));
	m_maincpu->flag_handler().set([this] (bool state) { m_cassbit = state; });

	/* video hardware */
	config.set_default_layout(layout_dolphunk);
	PWM_DISPLAY(config, m_display).set_size(4, 8);
	m_display->set_segmask(0x0f, 0xff);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);

	/* cassette */
	CASSETTE(config, m_cass);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	TIMER(config, "kansas_w").configure_periodic(FUNC(dauphin_state::kansas_w), attotime::from_hz(4000));
}

/* ROM definition */
ROM_START( dauphin )
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "dolphin_mo.rom", 0x0000, 0x0100, CRC(a8811f48) SHA1(233c629dc20fac286c8c1559e461bb0b742a675e) )
	// This one is used in winarcadia but it is a bad dump, we use the corrected one above
	//ROM_LOAD( "dolphin_mo.rom", 0x0000, 0x0100, BAD_DUMP CRC(1ac4ac18) SHA1(62a63de6fcd6cd5fcee930d31c73fe603647f06c) )

	// Cassette rom
	ROM_LOAD( "dolphin_moni.rom", 0x0100, 0x0100, NO_DUMP )

	//ROM_LOAD_OPTIONAL( "dolphin_mona.rom", 0x0c00, 0x0400, NO_DUMP )
	// This rom is a bugfixed and relocated version of the game found on the Amigan site
	ROM_LOAD_OPTIONAL( "reflexes.bin", 0x0c00, 0x0400, CRC(14a1557d) SHA1(789d10551f1bb3472057901fa3cee0c6bfe220ac) )
	// This the original
	//ROM_LOAD( "reflexes.bin", 0x0c00, 0x0072, CRC(c4bed94b) SHA1(cf525755a1dbce6a4681353be692ddf0346f323b) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY              FULLNAME   FLAGS
COMP( 1979, dauphin, 0,      0,      dauphin, dauphin, dauphin_state, empty_init, "LCD EPFL Stoppani", "Dauphin", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



dominator.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger, Mr. Lars
/*******************************************************************************

CXG Sphinx Dominator, chess computer.

The chess engine is by Frans Morsch, older versions (before 2.05) were buggy.
Hold Pawn + Knight buttons at boot for test mode, it will tell the version number.
This engine was also used in the newer Mephisto Modena.

Hardware notes:
- R65C02P4 @ 4MHz
- 32KB ROM, 8KB RAM battery-backed
- Sanyo LC7582, 2 LCD panels (each 4-digit, some unused segments)
- TTL, piezo, 8*8+8 LEDs, button sensors

Sphinx Commander also uses the Dominator program, and is on similar hardware,
but with a Chess 3008 housing (wooden chessboard, magnet sensors).
Sphinx Galaxy is on similar hardware too, with less leds.

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

#include "emu.h"

#include "cpu/m6502/r65c02.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/lc7580.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "cxg_dominator.lh"
#include "cxg_galaxy.lh"
#include "cxg_commander.lh"


namespace {

class dominator_state : public driver_device
{
public:
	dominator_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_lcd(*this, "lcd"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_out_digit(*this, "digit%u", 0U),
		m_out_lcd(*this, "s%u.%u", 0U, 0U)
	{ }

	// machine configs
	void dominator(machine_config &config);
	void galaxy(machine_config &config);
	void commander(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<lc7582_device> m_lcd;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<2> m_inputs;
	output_finder<8> m_out_digit;
	output_finder<2, 52> m_out_lcd;

	// address maps
	void dominator_map(address_map &map) ATTR_COLD;
	void galaxy_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void lcd_output_w(offs_t offset, u64 data);
	void control_w(u8 data);
	void leds_w(offs_t offset, u8 data);
	u8 input_r(offs_t offset);
};

void dominator_state::machine_start()
{
	m_out_digit.resolve();
	m_out_lcd.resolve();
}



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

// LC7582 LCD

void dominator_state::lcd_output_w(offs_t offset, u64 data)
{
	// output individual segments
	for (int i = 0; i < 52; i++)
		m_out_lcd[offset][i] = BIT(data, i);

	// unscramble digit 7segs
	static const u8 seg2digit[4*7] =
	{
		0x03, 0x04, 0x00, 0x40, 0x41, 0x02, 0x42,
		0x05, 0x06, 0x07, 0x48, 0x44, 0x45, 0x46,
		0x0c, 0x0d, 0x0b, 0x0a, 0x4a, 0x4c, 0x4b,
		0x0e, 0x0f, 0x10, 0x50, 0x4d, 0x4e, 0x4f
	};

	for (int i = 0; i < 8; i++)
	{
		u8 digit = 0;
		for (int seg = 0; seg < 7; seg++)
		{
			u8 bit = seg2digit[7 * (i & 3) + seg] + 26 * (i >> 2);
			digit |= m_out_lcd[BIT(bit, 6)][bit & 0x3f] << seg;
		}
		m_out_digit[i] = digit;
	}
}


// TTL

void dominator_state::control_w(u8 data)
{
	// d0: LC7582 DATA
	// d1: LC7582 CLK
	// d2: LC7582 CE
	m_lcd->data_w(BIT(data, 0));
	m_lcd->clk_w(BIT(data, 1));
	m_lcd->ce_w(BIT(data, 2));

	// d3: speaker out
	m_dac->write(BIT(data, 3));
}

void dominator_state::leds_w(offs_t offset, u8 data)
{
	// led data (sgalaxy only uses d4-d6)
	m_display->matrix(1 << offset, data);
}

u8 dominator_state::input_r(offs_t offset)
{
	u8 data = 0;

	// read chessboard sensors
	if (offset < 8)
		data = m_board->read_rank(offset);

	// read other buttons
	else if (offset < 10)
		data = m_inputs[offset - 8]->read();

	return ~data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void dominator_state::dominator_map(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("nvram");
	map(0x4000, 0x400f).rw(FUNC(dominator_state::input_r), FUNC(dominator_state::leds_w));
	map(0x4010, 0x4010).w(FUNC(dominator_state::control_w));
	map(0x7f00, 0x7fff).nopr(); // dummy read on 6502 absolute X page wrap
	map(0x8000, 0xffff).rom();
}

void dominator_state::galaxy_map(address_map &map)
{
	dominator_map(map);

	map(0x4008, 0x4008).mirror(0x0007).w(FUNC(dominator_state::control_w));
	map(0x4010, 0x4010).unmapw();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( dominator )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Move")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Knight")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Rook")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Queen")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("King")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Multi Move")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Hint")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Replay")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Library/Clear")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_C) PORT_NAME("Sound/Colour")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Enter Position")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
INPUT_PORTS_END

static INPUT_PORTS_START( commander )
	PORT_INCLUDE( dominator )

	PORT_MODIFY("IN.1")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Library")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_C) PORT_NAME("Sound/Color")
INPUT_PORTS_END

static INPUT_PORTS_START( galaxy )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("King")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Queen")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Rook")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Move")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Knight")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Enter Position")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_C) PORT_NAME("Sound/Color")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Library/Clearboard")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Multi Move")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Hint")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Replay")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void dominator_state::dominator(machine_config &config)
{
	// basic machine hardware
	R65C02(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &dominator_state::dominator_map);

	const attotime nmi_period = attotime::from_hz(4_MHz_XTAL / 0x2000); // from 4020
	m_maincpu->set_periodic_int(FUNC(dominator_state::nmi_line_pulse), nmi_period);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	LC7582(config, m_lcd, 0);
	m_lcd->write_segs().set(FUNC(dominator_state::lcd_output_w));

	PWM_DISPLAY(config, m_display).set_size(8+1, 8);
	config.set_default_layout(layout_cxg_dominator);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void dominator_state::galaxy(machine_config &config)
{
	dominator(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &dominator_state::galaxy_map);

	m_display->set_size(8, 8);
	config.set_default_layout(layout_cxg_galaxy);
}

void dominator_state::commander(machine_config &config)
{
	dominator(config);

	// basic machine hardware
	m_board->set_type(sensorboard_device::MAGNETS);
	m_board->set_delay(attotime::from_msec(250));

	config.set_default_layout(layout_cxg_commander);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( sdtor )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("22p_2.05", 0x8000, 0x8000, CRC(9707119c) SHA1(d7cde835a37bd5d9ff349a871c890ea4cd9b2c26) ) // M27C256B
ROM_END

ROM_START( sdtora )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("1988_newcrest_239_2.04", 0x8000, 0x8000, CRC(f9548d92) SHA1(77340b32a33fc2a49c97d60774f395548b99294c) ) // TMS27C256-20JL
ROM_END


ROM_START( sgalaxy )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("cxg_230_20_oct_88_ver_2.03", 0x8000, 0x8000, CRC(b93996e3) SHA1(d616620393154373b613060901e233eea4fd4c55) )
ROM_END

ROM_START( sgalaxya )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("sgalaxya.bin", 0x8000, 0x8000, CRC(e373b2ef) SHA1(b72720ea404ae2bd59f1bdba147bb48f424d8c18) ) // label unknown
ROM_END

ROM_START( sgalaxyb )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("1988_newcrest_230_1.03", 0x8000, 0x8000, CRC(30d81c4e) SHA1(ab3a9b58db26d87d3e66600b77c9435313b632f2) )
ROM_END


ROM_START( scmder )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("scmder.bin", 0x8000, 0x8000, CRC(7a3f27b4) SHA1(6b3bcca707f034124b8b4bd061b500ce1a75faf4) ) // label unknown
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT   COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY, FULLNAME, FLAGS
SYST( 1989, sdtor,    0,       0,      dominator, dominator, dominator_state, empty_init, "CXG Systems / Newcrest Technology", "Sphinx Dominator (v2.05)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, sdtora,   sdtor,   0,      dominator, dominator, dominator_state, empty_init, "CXG Systems / Newcrest Technology", "Sphinx Dominator (v2.04)", MACHINE_SUPPORTS_SAVE )

SYST( 1988, sgalaxy,  0,       0,      galaxy,    galaxy,    dominator_state, empty_init, "CXG Systems / Newcrest Technology", "Sphinx Galaxy (v2.03)", MACHINE_SUPPORTS_SAVE )
SYST( 1988, sgalaxya, sgalaxy, 0,      galaxy,    galaxy,    dominator_state, empty_init, "CXG Systems / Newcrest Technology", "Sphinx Galaxy (v2.00)", MACHINE_SUPPORTS_SAVE )
SYST( 1988, sgalaxyb, sgalaxy, 0,      galaxy,    galaxy,    dominator_state, empty_init, "CXG Systems / Newcrest Technology", "Sphinx Galaxy (v1.03)", MACHINE_SUPPORTS_SAVE )

SYST( 1989, scmder,   0,       0,      commander, commander, dominator_state, empty_init, "CXG Systems / Newcrest Technology", "Sphinx Commander (v2.00)", MACHINE_SUPPORTS_SAVE )



dpb7000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/***************************************************************************

    DPB-7001 (c) 1981 Quantel

    Known issues/possible improvements:
    - Disk Sequencer Card is currently implemented via HLE.
    - Tablet is currently implemented via HLE.
    - Blanking PAL is currently disabled, which results in on-screen
      garbage when scrolling.
    - Size Card and Filter Card functionality is not yet implemented.
    - Video input functionality is not yet implemented.
    - There are numerous Brush Processor Card functions that are not yet
      implemented.

    To create a hard disk image compatible with the "330Mb Fujitsu" DIP
    setting:
    - chdman createhd -o [output name] -chs 1024,16,80 -ss 256 -c none

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

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
#include "cpu/m6800/m6800.h"
#include "cpu/m6800/m6801.h"
#include "cpu/mcs48/mcs48.h"
#include "cpu/z8/z8.h"
#include "imagedev/floppy.h"
#include "imagedev/harddriv.h"
#include "machine/6850acia.h"
#include "machine/am25s55x.h"
#include "machine/am2901b.h"
#include "machine/am2910.h"
#include "machine/com8116.h"
#include "machine/fdc_pll.h"
#include "machine/input_merger.h"
#include "machine/mc68681.h"
#include "machine/tdc1008.h"
#include "video/mc6845.h"

#include "screen.h"
#include "emupal.h"

#include <algorithm>
#include <deque>

#define LOG_UNKNOWN         (1U << 1)
#define LOG_UCODE           (1U << 2)
#define LOG_MORE_UCODE      (1U << 3)
#define LOG_CSR             (1U << 4)
#define LOG_CTRLBUS         (1U << 5)
#define LOG_SYS_CTRL        (1U << 6)
#define LOG_FDC_CTRL        (1U << 7)
#define LOG_FDC_PORT        (1U << 8)
#define LOG_FDC_CMD         (1U << 9)
#define LOG_FDC_MECH        (1U << 10)
#define LOG_OUTPUT_TIMING   (1U << 11)
#define LOG_BRUSH_ADDR      (1U << 12)
#define LOG_STORE_ADDR      (1U << 13)
#define LOG_COMBINER        (1U << 14)
#define LOG_SIZE_CARD       (1U << 15)
#define LOG_FILTER_CARD     (1U << 16)
#define LOG_KEYBC           (1U << 17)
#define LOG_TDS             (1U << 18)
#define LOG_TABLET          (1U << 19)
#define LOG_COMMANDS        (1U << 20)
#define LOG_HDD             (1U << 21)
#define LOG_FDD             (1U << 22)
#define LOG_DDB             (1U << 23)
#define LOG_IRQ             (1U << 24)
#define LOG_BRUSH_LATCH     (1U << 25)
#define LOG_BRUSH_DRAWS     (1U << 26)
#define LOG_BRUSH_WRITES    (1U << 27)
#define LOG_STORE_READS     (1U << 28)
#define LOG_ALL             (LOG_UNKNOWN | LOG_CSR | LOG_CTRLBUS | LOG_SYS_CTRL | LOG_BRUSH_ADDR | \
							 LOG_STORE_ADDR | LOG_COMBINER | LOG_SIZE_CARD | LOG_FILTER_CARD | LOG_COMMANDS | LOG_OUTPUT_TIMING | \
							 LOG_BRUSH_LATCH | LOG_FDC_PORT | LOG_FDC_CMD | LOG_FDC_MECH | LOG_BRUSH_WRITES | LOG_STORE_READS)

//#define VERBOSE             (LOG_CSR | LOG_CTRLBUS | LOG_STORE_ADDR | LOG_COMBINER | LOG_SIZE_CARD | LOG_FILTER_CARD | LOG_BRUSH_ADDR | LOG_COMMANDS | LOG_OUTPUT_TIMING)
#define VERBOSE (0)
#include "logmacro.h"

namespace
{

class dpb7000_state : public driver_device
{
public:
	dpb7000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_acia(*this, "fd%u", 0U)
		, m_p_int(*this, "p_int")
		, m_brg(*this, "brg")
		, m_rs232(*this, "rs232%u", 0U)
		, m_crtc(*this, "crtc")
		, m_palette(*this, "palette")
		, m_vdu_ram(*this, "vduram")
		, m_vdu_char_rom(*this, "vduchar")
		, m_baud_dip(*this, "BAUD")
		, m_auto_start(*this, "AUTOSTART")
		, m_config_sw12(*this, "CONFIGSW12")
		, m_config_sw34(*this, "CONFIGSW34")
		, m_fddcpu(*this, "fddcpu")
		, m_fdd_serial(*this, "fddserial")
		, m_floppy0(*this, "0")
		, m_hdd(*this, "hdd")
		, m_floppy(nullptr)
		, m_output_cursor(*this, "output_timing_cursor")
		, m_output_hlines(*this, "output_timing_hlines")
		, m_output_hflags(*this, "output_timing_hflags")
		, m_output_vlines(*this, "output_timing_vlines")
		, m_output_vflags(*this, "output_timing_vflags")
		, m_brushaddr_pal(*this, "brushaddr_pal")
		, m_storeaddr_protx(*this, "storeaddr_prom_protx")
		, m_storeaddr_proty(*this, "storeaddr_prom_proty")
		, m_storeaddr_xlnib(*this, "storeaddr_prom_xlnib")
		, m_storeaddr_xmnib(*this, "storeaddr_prom_xmnib")
		, m_storeaddr_xhnib(*this, "storeaddr_prom_xhnib")
		, m_storeaddr_pal_blank(*this, "storeaddr_pal_blank")
		, m_keybcpu(*this, "keybcpu")
		, m_keybc_cols(*this, "KEYB_COL%u", 0U)
		, m_tds_cpu(*this, "tds")
		, m_tds_duart(*this, "tds_duart")
		, m_tds_dips(*this, "TDSDIPS")
		, m_pen_switches(*this, "PENSW")
		, m_tablet_cpu(*this, "tablet")
		, m_pen_prox(*this, "PENPROX")
		, m_pen_x(*this, "PENX")
		, m_pen_y(*this, "PENY")
		, m_pen_press(*this, "PENPRESS")
		, m_brushproc_prom(*this, "brushproc_prom")
		, m_brushproc_pal(*this, "brushproc_pal")
	{
	}

	void dpb7000(machine_config &config);

	//DECLARE_INPUT_CHANGED_MEMBER(pen_prox_changed);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	template <int StoreNum> uint32_t store_debug_screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	uint32_t stencil_debug_screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	uint32_t brush_debug_screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	uint32_t combined_screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void main_map(address_map &map) ATTR_COLD;
	void fddcpu_map(address_map &map) ATTR_COLD;
	void keybcpu_map(address_map &map) ATTR_COLD;
	void tds_cpu_map(address_map &map) ATTR_COLD;
	void tablet_program_map(address_map &map) ATTR_COLD;
	void tablet_data_map(address_map &map) ATTR_COLD;

	void csr_w(uint8_t data);
	uint8_t csr_r();

	uint16_t cpu_ctrlbus_r();
	void cpu_ctrlbus_w(uint16_t data);

	TIMER_CALLBACK_MEMBER(req_a_w);
	TIMER_CALLBACK_MEMBER(req_b_w);
	TIMER_CALLBACK_MEMBER(cmd_done);
	TIMER_CALLBACK_MEMBER(execute_hdd_command);

	bool is_disk_group_fdd(int group_index);
	bool is_disk_group_hdd(int group_index);
	int get_heads_for_disk_group(int group_index);
	void fdd_index_callback(floppy_image_device *floppy, int state);
	void seek_fdd_to_cylinder();
	uint8_t fdd_ctrl_r();
	uint8_t fdd_cmd_r();
	void fddcpu_p1_w(uint8_t data);
	uint8_t fddcpu_p2_r();
	void fddcpu_p2_w(uint8_t data);
	void fddcpu_debug_rx(int state);

	bool handle_command(uint16_t data);
	void store_address_w(uint8_t card, uint16_t data);
	void combiner_reg_w(uint16_t data);

	enum : uint16_t
	{
		SYSCTRL_AUTO_START      = 0x0001,
		SYSCTRL_REQ_B_EN        = 0x0020,
		SYSCTRL_REQ_A_EN        = 0x0040,
		SYSCTRL_REQ_A_IN        = 0x0080,
		SYSCTRL_REQ_B_IN        = 0x8000
	};

	uint16_t cpu_sysctrl_r();
	void cpu_sysctrl_w(uint16_t data);
	void update_req_irqs();

	MC6845_UPDATE_ROW(crtc_update_row);
	MC6845_ON_UPDATE_ADDR_CHANGED(crtc_addr_changed);

	void advance_line_count();
	void toggle_line_clock();
	void process_sample();
	void process_byte_from_disc(uint8_t data_byte);
	uint8_t process_byte_to_disc();

	required_device<m68000_device> m_maincpu;
	required_device_array<acia6850_device, 3> m_acia;
	required_device<input_merger_device> m_p_int;
	required_device<com8116_device> m_brg;
	required_device_array<rs232_port_device, 2> m_rs232;
	required_device<sy6545_1_device> m_crtc;
	required_device<palette_device> m_palette;
	required_shared_ptr<uint16_t> m_vdu_ram;
	required_region_ptr<uint8_t> m_vdu_char_rom;
	required_ioport m_baud_dip;
	required_ioport m_auto_start;

	required_ioport m_config_sw12;
	required_ioport m_config_sw34;

	required_device<m6803_cpu_device> m_fddcpu;
	required_device<rs232_port_device> m_fdd_serial;
	required_device<floppy_connector> m_floppy0;
	required_device<harddisk_image_device> m_hdd;

	// Floppy Drive
	floppy_image_device *m_floppy;
	std::deque<int> m_fdd_debug_rx_bits;
	uint32_t m_fdd_debug_rx_bit_count;
	uint32_t m_fdd_debug_rx_byte_count;
	uint32_t m_fdd_debug_rx_recv_count;
	uint8_t m_fdd_ctrl;
	uint8_t m_fdd_port1;
	uint8_t m_fdd_track;
	uint8_t m_fdd_side;
	fdc_pll_t m_fdd_pll;

	// Timers
	emu_timer *m_diskseq_complete_clk;
	emu_timer *m_field_in_clk;
	emu_timer *m_field_out_clk;
	emu_timer *m_cmd_done_timer;
	emu_timer *m_hdd_command_timer;

	enum : size_t
	{
		FRAMESTORE_A0 = 0,
		FRAMESTORE_A1,
		FRAMESTORE_A2,
		FRAMESTORE_B0,
		FRAMESTORE_B1,
		FRAMESTORE_B2,
		FRAMESTORE_COUNT
	};

	enum : uint16_t
	{
		DGROUP_TYPE_INVALID0        = 0,
		DGROUP_TYPE_80MB_CDC        = 1,
		DGROUP_TYPE_INVALID1        = 2,
		DGROUP_TYPE_FLOPPY          = 3,
		DGROUP_TYPE_INVALID2        = 4,
		DGROUP_TYPE_330MB_FUJITSU   = 5,
		DGROUP_TYPE_160MB_FUJITSU   = 6,
		DGROUP_TYPE_NONE            = 7,

		DGROUP_0_SHIFT              = 2,
		DGROUP_1_SHIFT              = 7,
		DGROUP_2_SHIFT              = 12
	};

	// Computer Card
	uint8_t m_csr;
	uint16_t m_sys_ctrl;

	// Disc Sequencer Card
	uint16_t m_diskseq_cyl_from_cpu;    // AE/BH
	uint16_t m_diskseq_cmd_word_from_cpu; // DD/CC
	uint8_t m_diskseq_cmd;
	uint8_t m_diskseq_cmd_to_ctrl;
	uint8_t m_diskseq_status;       // BC
	bool m_diskseq_cyl_read_pending;
	bool m_diskseq_cyl_write_pending;
	bool m_diskseq_use_hdd_pending;
	uint16_t m_diskseq_command_stride;

	// Disc Data Buffer Card
	uint16_t m_diskbuf_ram_addr;
	uint8_t m_diskbuf_ram[14 * 0x800];
	uint16_t m_diskbuf_data_count;

	// Output Timing Card
	uint16_t m_cursor_origin_x;
	uint16_t m_cursor_origin_y;
	uint16_t m_cursor_size_x;
	uint16_t m_cursor_size_y;
	uint8_t m_output_csflags;
	uint8_t m_output_cpflags;
	required_region_ptr<uint8_t> m_output_cursor;
	required_region_ptr<uint8_t> m_output_hlines;
	required_region_ptr<uint8_t> m_output_hflags;
	required_region_ptr<uint8_t> m_output_vlines;
	required_region_ptr<uint8_t> m_output_vflags;

	// Brush Address Card
	required_region_ptr<uint8_t> m_brushaddr_pal;
	uint8_t m_line_clock;
	uint16_t m_line_count;
	uint16_t m_line_length;
	uint16_t m_brush_addr_func;
	uint8_t m_brush_addr_cmd;
	uint8_t m_bif;
	uint8_t m_bixos;
	uint8_t m_biyos;
	uint16_t m_bxlen;
	uint16_t m_bylen;
	uint16_t m_bxlen_counter;
	uint16_t m_bylen_counter;
	uint8_t m_brush_press_lum;
	uint8_t m_brush_press_chr;
	std::unique_ptr<uint8_t[]> m_framestore_chr[2];
	std::unique_ptr<uint8_t[]> m_framestore_lum[2];
	std::unique_ptr<uint8_t[]> m_framestore_ext[2];

	// Brush Store Card
	uint8_t m_bs_y_latch;
	uint8_t m_bs_u_latch;
	uint8_t m_bs_v_latch;
	std::unique_ptr<uint8_t[]> m_brushstore_chr;
	std::unique_ptr<uint8_t[]> m_brushstore_lum;
	std::unique_ptr<uint8_t[]> m_brushstore_ext;

	// Store Address Card
	uint16_t m_rhscr[2];
	uint16_t m_rvscr[2];
	uint16_t m_rzoom[2];
	uint16_t m_fld_sel[2];
	uint16_t m_window_enable[2];
	uint16_t m_cxpos[2];
	uint16_t m_cypos[2];
	uint16_t m_ca0;
	required_region_ptr<uint8_t> m_storeaddr_protx;
	required_region_ptr<uint8_t> m_storeaddr_proty;
	required_region_ptr<uint8_t> m_storeaddr_xlnib;
	required_region_ptr<uint8_t> m_storeaddr_xmnib;
	required_region_ptr<uint8_t> m_storeaddr_xhnib;
	required_region_ptr<uint8_t> m_storeaddr_pal_blank;

	// Combiner Card
	uint8_t m_cursor_y;
	uint8_t m_cursor_u;
	uint8_t m_cursor_v;
	uint8_t m_ext_store_flags;
	bool m_select_matte[2];
	uint8_t m_matte_ext[2];
	uint8_t m_matte_y[2];
	uint8_t m_matte_u[2];
	uint8_t m_matte_v[2];

	// Keyboard
	required_device<i8039_device> m_keybcpu;
	required_ioport_array<8> m_keybc_cols;
	uint8_t keyboard_p1_r();
	uint8_t keyboard_p2_r();
	void keyboard_p1_w(uint8_t data);
	void keyboard_p2_w(uint8_t data);
	int keyboard_t0_r();
	int keyboard_t1_r();
	uint8_t m_keybc_latched_bit;
	uint8_t m_keybc_p1_data;
	uint8_t m_keybc_tx;

	// TDS Box
	enum tds_press_state : uint32_t
	{
		STATE_IDLE,
		STATE_PRESS_IN,
		STATE_PRESSED,
		STATE_PRESS_OUT
	};

	required_device<m6803_cpu_device> m_tds_cpu;
	required_device<scn2681_device> m_tds_duart;
	required_ioport m_tds_dips;
	required_ioport m_pen_switches;
	uint8_t tds_p1_r();
	uint8_t tds_p2_r();
	uint8_t tds_p3_r();
	uint8_t tds_p4_r();
	void tds_p1_w(uint8_t data);
	void tds_p2_w(uint8_t data);
	void tds_dac_w(uint8_t data);
	void tds_convert_w(uint8_t data);
	uint8_t tds_adc_r();
	uint8_t tds_pen_switches_r();
	void duart_b_w(int state);
	TIMER_CALLBACK_MEMBER(tablet_tx_tick);
	TIMER_CALLBACK_MEMBER(tds_adc_tick);
	TIMER_CALLBACK_MEMBER(tds_press_tick);
	uint8_t m_tds_dac_value;
	uint8_t m_tds_dac_offset;
	float m_tds_dac_percent;
	bool m_tds_adc_busy;
	uint8_t m_tds_adc_value;
	uint8_t m_tds_p1_data;
	uint32_t m_tds_press_state;
	uint8_t m_tds_pressure_shift;
	emu_timer *m_tds_adc_timer;
	emu_timer *m_tds_press_timer;
	bool m_tds_in_proximity;

	// Tablet
	required_device<z8681_device> m_tablet_cpu;
	required_ioport m_pen_prox;
	required_ioport m_pen_x;
	required_ioport m_pen_y;
	required_ioport m_pen_press;
	uint8_t tablet_p2_r(offs_t offset, uint8_t mem_mask);
	void tablet_p2_w(offs_t offset, uint8_t data, uint8_t mem_mask);
	uint8_t tablet_p3_r(offs_t offset, uint8_t mem_mask);
	void tablet_p3_w(offs_t offset, uint8_t data, uint8_t mem_mask);
	TIMER_CALLBACK_MEMBER(tablet_irq_tick);
	uint8_t tablet_rdl_r();
	uint8_t tablet_rdh_r();
	uint8_t m_tablet_p2_data;
	uint8_t m_tablet_p3_data;
	uint8_t m_tablet_mux;
	uint8_t m_tablet_drq;
	uint16_t m_tablet_dip_shifter;
	uint16_t m_tablet_counter_latch;
	emu_timer *m_tablet_tx_timer;
	emu_timer *m_tablet_irq_timer;
	uint8_t m_tablet_tx_bit;
	uint16_t m_tablet_pen_x;
	uint16_t m_tablet_pen_y;
	uint16_t m_tablet_pen_switches;
	uint8_t m_tablet_state;

	// Table HLE
	TIMER_CALLBACK_MEMBER(tablet_hle_tick);
	std::deque<int> m_tablet_hle_tx_bits;
	emu_timer *m_tablet_hle_timer;

	// Filter Card
	int8_t m_filter_x_coeffs[12];
	int8_t m_filter_y_coeffs[8];
	uint8_t m_incoming_lum;
	uint8_t m_incoming_chr;
	bool m_buffer_lum;

	// Size Card
	uint32_t m_size_dsx_ddx;
	uint32_t m_size_dsy_ddx;
	uint32_t m_size_dsx_ddy;
	uint32_t m_size_dsy_ddy;
	uint8_t m_size_frac_origin_sx_dx;
	uint8_t m_size_frac_origin_sy_dx;
	uint8_t m_size_frac_origin_sx_dy;
	uint8_t m_size_frac_origin_sy_dy;
	int16_t m_size_dxdx_counter;
	int16_t m_size_dydy_counter;
	uint16_t m_size_dest_x;
	uint16_t m_size_dest_y;

	// Brush Processor Card
	required_region_ptr<uint8_t> m_brushproc_prom;
	required_region_ptr<uint16_t> m_brushproc_pal;

	// Utility
	std::unique_ptr<uint32_t[]> m_yuv_lut;
};

void dpb7000_state::main_map(address_map &map)
{
	map(0x000000, 0x09ffff).rom().region("monitor", 0);
	map(0x0006aa, 0x0006ab).nopw();
	map(0xb00000, 0xb7ffff).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0xb80000, 0xbfffff).ram();
	//map(0xb00000, 0xbfffff).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	//map(0xfc0000, 0xffd3ff).ram();
	map(0xffd000, 0xffd3ff).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0xffe000, 0xffefff).ram().share("vduram").umask16(0x00ff);
	map(0xfff801, 0xfff801).rw(m_crtc, FUNC(sy6545_1_device::status_r), FUNC(sy6545_1_device::address_w)).cswidth(16);
	map(0xfff803, 0xfff803).rw(m_crtc, FUNC(sy6545_1_device::register_r), FUNC(sy6545_1_device::register_w)).cswidth(16);
	map(0xfff805, 0xfff805).rw(m_acia[0], FUNC(acia6850_device::status_r), FUNC(acia6850_device::control_w)).cswidth(16);
	map(0xfff807, 0xfff807).rw(m_acia[0], FUNC(acia6850_device::data_r), FUNC(acia6850_device::data_w)).cswidth(16);
	map(0xfff808, 0xfff808).r(FUNC(dpb7000_state::csr_r)).cswidth(16);
	map(0xfff809, 0xfff809).w(FUNC(dpb7000_state::csr_w)).cswidth(16);
	map(0xfff80a, 0xfff80b).rw(FUNC(dpb7000_state::cpu_ctrlbus_r), FUNC(dpb7000_state::cpu_ctrlbus_w));
	map(0xfff80c, 0xfff80d).rw(FUNC(dpb7000_state::cpu_sysctrl_r), FUNC(dpb7000_state::cpu_sysctrl_w));
	map(0xfff811, 0xfff811).rw(m_acia[1], FUNC(acia6850_device::status_r), FUNC(acia6850_device::control_w)).cswidth(16);
	map(0xfff813, 0xfff813).rw(m_acia[1], FUNC(acia6850_device::data_r), FUNC(acia6850_device::data_w)).cswidth(16);
	map(0xfff815, 0xfff815).rw(m_acia[2], FUNC(acia6850_device::status_r), FUNC(acia6850_device::control_w)).cswidth(16);
	map(0xfff817, 0xfff817).rw(m_acia[2], FUNC(acia6850_device::data_r), FUNC(acia6850_device::data_w)).cswidth(16);
}

void dpb7000_state::fddcpu_map(address_map &map)
{
	map(0x4000, 0x4000).r(FUNC(dpb7000_state::fdd_ctrl_r));
	map(0x4001, 0x4001).r(FUNC(dpb7000_state::fdd_cmd_r));
	map(0xf800, 0xffff).rom().region("fddprom", 0);
}

void dpb7000_state::keybcpu_map(address_map &map)
{
	map(0x000, 0x7ff).rom().region("keyboard", 0);
}

void dpb7000_state::tds_cpu_map(address_map &map)
{
	map(0x2000, 0x2000).w(FUNC(dpb7000_state::tds_dac_w));
	map(0x2001, 0x2001).w(FUNC(dpb7000_state::tds_convert_w));
	map(0x2003, 0x2003).r(FUNC(dpb7000_state::tds_adc_r));
	map(0x2007, 0x2007).r(FUNC(dpb7000_state::tds_pen_switches_r));
	map(0x4000, 0x400f).rw(m_tds_duart, FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0xe000, 0xffff).rom().region("tds", 0);
}

void dpb7000_state::tablet_program_map(address_map &map)
{
	map(0x0000, 0x1fff).mirror(0xe000).rom().region("tablet", 0);
}

void dpb7000_state::tablet_data_map(address_map &map)
{
	map(0x4000, 0x4000).mirror(0x1fff).r(FUNC(dpb7000_state::tablet_rdl_r));
	map(0x6000, 0x6000).mirror(0x1fff).r(FUNC(dpb7000_state::tablet_rdh_r));
}

static INPUT_PORTS_START( dpb7000 )
	PORT_START("KEYB_COL0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"9  )  \u2013") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')') // – (en dash)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"8  (  \u2014") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(') // — (em dash)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')

	PORT_START("KEYB_COL1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"7  \u2019  \u2018") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'') // ’ ‘
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"6  &  »") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')

	PORT_START("KEYB_COL2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5  %  >") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%') PORT_CHAR('>')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4  $  <") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') PORT_CHAR('<')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')

	PORT_START("KEYB_COL3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8".  :  æ") PORT_CODE(KEYCODE_STOP) PORT_CHAR ('.') PORT_CHAR(':')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"¹  =  ~") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('=')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8",  ;") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR(';')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('-')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\ufb02  +  Æ") PORT_CODE(KEYCODE_COLON) // ﬂ
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')

	PORT_START("KEYB_COL4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"3  £  «") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2  \"") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')

	PORT_START("KEYB_COL5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!') PORT_CHAR('#')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEYB_COL6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"`  `  Å") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8" \ua788  ˆ  ø") PORT_CODE(KEYCODE_EQUALS) // modifier letter low circumflex accent - leave the preceding space
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"/ ?  œ") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8" \u0324  ¨  Ø") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('~') // combining diaeresis below - leave the preceding space
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"ß  ¸  å") PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"´  ´  ~") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\ufb01  *  ϒ") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('*') // ﬁ

	PORT_START("KEYB_COL7")
	PORT_BIT(0x03, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC) // 0x7f
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Delete") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(0x08) // 0x08
	PORT_BIT(0x30, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(0x0d)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("PENSW")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Pen Button 1") PORT_CODE(MOUSECODE_BUTTON1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_NAME("Pen Button 2") PORT_CODE(MOUSECODE_BUTTON3)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_BUTTON4) PORT_NAME("Pen Button 3") PORT_CODE(MOUSECODE_BUTTON4)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_BUTTON5) PORT_NAME("Pen Button 4") PORT_CODE(MOUSECODE_BUTTON5)
	PORT_BIT(0xf0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("PENPROX")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("Pen Proximity") PORT_CODE(MOUSECODE_BUTTON2)
	PORT_BIT(0xfe, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("PENX")
	PORT_BIT( 0xffff, 3800, IPT_LIGHTGUN_X) PORT_NAME("Pen X") PORT_MINMAX(500, 4599) PORT_SENSITIVITY(50) PORT_CROSSHAIR(X, 1.385, -0.17, 0)

	PORT_START("PENY")
	PORT_BIT( 0xffff, 2048, IPT_LIGHTGUN_Y) PORT_NAME("Pen Y") PORT_MINMAX(1000, 4399) PORT_SENSITIVITY(50) PORT_CROSSHAIR(Y, 1.53, -0.26, 0)

	PORT_START("PENPRESS")
	PORT_BIT( 0xff, 0x02, IPT_PEDAL ) PORT_MINMAX(0x02,0xaf) PORT_SENSITIVITY(100) PORT_KEYDELTA(10)

	PORT_START("TDSDIPS")
	PORT_DIPNAME(0x08, 0x08, "TDS Box Encoding")
	PORT_DIPSETTING(   0x08, "Binary")
	PORT_DIPSETTING(   0x00, "ASCII")
	PORT_DIPNAME(0x10, 0x10, "TDS Box Mode")
	PORT_DIPSETTING(   0x10, "Normal")
	PORT_DIPSETTING(   0x00, "Monitor")
	PORT_DIPNAME(0x40, 0x00, "TDS Box Standard")
	PORT_DIPSETTING(   0x40, "NTSC")
	PORT_DIPSETTING(   0x00, "PAL")
	PORT_BIT(0xa7, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("BAUD")
	PORT_DIPNAME(0x0f, 0x0e, "Baud Rate for Terminal")
	PORT_DIPSETTING(   0x00, "50")
	PORT_DIPSETTING(   0x01, "75")
	PORT_DIPSETTING(   0x02, "110")
	PORT_DIPSETTING(   0x03, "134.5")
	PORT_DIPSETTING(   0x04, "150")
	PORT_DIPSETTING(   0x05, "300")
	PORT_DIPSETTING(   0x06, "600")
	PORT_DIPSETTING(   0x07, "1200")
	PORT_DIPSETTING(   0x08, "1800")
	PORT_DIPSETTING(   0x09, "2000")
	PORT_DIPSETTING(   0x0a, "2400")
	PORT_DIPSETTING(   0x0b, "3600")
	PORT_DIPSETTING(   0x0c, "4800")
	PORT_DIPSETTING(   0x0e, "9600")
	PORT_DIPSETTING(   0x0f, "19200")

	PORT_START("AUTOSTART")
	PORT_DIPNAME(0x0001, 0x0001, "Auto-Start")
	PORT_DIPSETTING(     0x0000, DEF_STR( Off ))
	PORT_DIPSETTING(     0x0001, DEF_STR( On ))
	PORT_BIT(0xfffe, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("CONFIGSW12")
	PORT_DIPNAME( 0x0001, 0x0001, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0000, "NTSC/PAL" )
	PORT_DIPSETTING(    0x0002, "NTSC" )
	PORT_DIPSETTING(    0x0000, "PAL" )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, "Diagnostic Mode" )
	PORT_DIPSETTING(    0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x0000, DEF_STR( On ) )

	PORT_START("CONFIGSW34")
	PORT_DIPNAME( 0x0003, 0x0003, "Disc Group 0 Count" )
	PORT_DIPSETTING(    0x0003, "One" )
	PORT_DIPSETTING(    0x0002, "Two" )
	PORT_DIPSETTING(    0x0001, "Three" )
	PORT_DIPSETTING(    0x0000, "Four" )
	PORT_DIPNAME( 0x001c, 0x000c, "Disc Group 0 Type" )
	PORT_DIPSETTING(    0x001c, "None" )
	PORT_DIPSETTING(    0x0018, "160Mb Fujitsu" )
	PORT_DIPSETTING(    0x0014, "330Mb Fujitsu" )
	PORT_DIPSETTING(    0x000c, "Floppy Disc" )
	PORT_DIPSETTING(    0x0008, "NTSC/Floppy/PAL/Conv" )
	PORT_DIPSETTING(    0x0004, "80Mb CDC Removable" )
	PORT_DIPSETTING(    0x0000, "160Mb CDC Fixed" )
	PORT_DIPNAME( 0x0060, 0x0060, "Disc Group 1 Count" )
	PORT_DIPSETTING(    0x0060, "One" )
	PORT_DIPSETTING(    0x0040, "Two" )
	PORT_DIPSETTING(    0x0020, "Three" )
	PORT_DIPSETTING(    0x0000, "Four" )
	PORT_DIPNAME( 0x0380, 0x0380, "Disc Group 1 Type" )
	PORT_DIPSETTING(    0x0380, "None" )
	PORT_DIPSETTING(    0x0300, "160Mb Fujitsu" )
	PORT_DIPSETTING(    0x0280, "330Mb Fujitsu" )
	PORT_DIPSETTING(    0x0180, "Floppy Disc" )
	PORT_DIPSETTING(    0x0100, "NTSC/Floppy/PAL/Conv" )
	PORT_DIPSETTING(    0x0080, "80Mb CDC Removable" )
	PORT_DIPSETTING(    0x0000, "160Mb CDC Fixed" )
	PORT_DIPNAME( 0x0c00, 0x0c00, "Disc Group 2 Count" )
	PORT_DIPSETTING(    0x0c00, "One" )
	PORT_DIPSETTING(    0x0800, "Two" )
	PORT_DIPSETTING(    0x0400, "Three" )
	PORT_DIPSETTING(    0x0000, "Four" )
	PORT_DIPNAME( 0x7000, 0x7000, "Disc Group 2 Type" )
	PORT_DIPSETTING(    0x7000, "None" )
	PORT_DIPSETTING(    0x6000, "160Mb Fujitsu" )
	PORT_DIPSETTING(    0x5000, "330Mb Fujitsu" )
	PORT_DIPSETTING(    0x3000, "Floppy Disc" )
	PORT_DIPSETTING(    0x2000, "NTSC/Floppy/PAL/Conv" )
	PORT_DIPSETTING(    0x1000, "80Mb CDC Removable" )
	PORT_DIPSETTING(    0x0000, "160Mb CDC Fixed" )
	PORT_DIPNAME( 0x8000, 0x8000, "Start Up In Dialogue" )
	PORT_DIPSETTING(    0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x0000, DEF_STR( On ) )
INPUT_PORTS_END

void dpb7000_state::machine_start()
{
	// Computer Card
	save_item(NAME(m_csr));
	save_item(NAME(m_sys_ctrl));

	m_field_in_clk = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dpb7000_state::req_a_w), this));
	m_field_in_clk->adjust(attotime::never);

	m_field_out_clk = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dpb7000_state::req_a_w), this));
	m_field_out_clk->adjust(attotime::never);

	m_cmd_done_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dpb7000_state::cmd_done), this));
	m_cmd_done_timer->adjust(attotime::never);

	m_hdd_command_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dpb7000_state::execute_hdd_command), this));
	m_hdd_command_timer->adjust(attotime::never);

	// Disc Sequencer Card
	save_item(NAME(m_diskseq_cyl_from_cpu));
	save_item(NAME(m_diskseq_cmd_word_from_cpu));
	save_item(NAME(m_diskseq_cmd));
	save_item(NAME(m_diskseq_cmd_to_ctrl));
	save_item(NAME(m_diskseq_status));
	save_item(NAME(m_diskseq_cyl_read_pending));
	save_item(NAME(m_diskseq_cyl_write_pending));
	save_item(NAME(m_diskseq_use_hdd_pending));
	save_item(NAME(m_diskseq_command_stride));
	m_diskseq_complete_clk = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dpb7000_state::req_b_w), this));
	m_diskseq_complete_clk->adjust(attotime::never);

	// Floppy Disc Controller Card
	save_item(NAME(m_fdd_debug_rx_bit_count));
	save_item(NAME(m_fdd_debug_rx_byte_count));
	save_item(NAME(m_fdd_debug_rx_recv_count));
	save_item(NAME(m_fdd_ctrl));
	save_item(NAME(m_fdd_port1));
	save_item(NAME(m_fdd_track));
	save_item(NAME(m_fdd_side));

	// Disc Data Buffer Card
	save_item(NAME(m_diskbuf_ram_addr));
	save_item(NAME(m_diskbuf_ram));
	save_item(NAME(m_diskbuf_data_count));

	// Output Timing Card
	save_item(NAME(m_cursor_origin_x));
	save_item(NAME(m_cursor_origin_y));
	save_item(NAME(m_cursor_size_x));
	save_item(NAME(m_cursor_size_y));
	save_item(NAME(m_output_csflags));
	save_item(NAME(m_output_cpflags));

	// Brush Address Card
	save_item(NAME(m_line_clock));
	save_item(NAME(m_line_count));
	save_item(NAME(m_line_length));
	save_item(NAME(m_brush_addr_func));
	save_item(NAME(m_brush_addr_cmd));
	save_item(NAME(m_bif));
	save_item(NAME(m_bixos));
	save_item(NAME(m_biyos));
	save_item(NAME(m_bxlen));
	save_item(NAME(m_bylen));
	save_item(NAME(m_bxlen_counter));
	save_item(NAME(m_bylen_counter));
	save_item(NAME(m_brush_press_lum));
	save_item(NAME(m_brush_press_chr));

	// Frame Store Cards, 640x1024
	for (int i = 0; i < 2; i++)
	{
		m_framestore_chr[i] = std::make_unique<uint8_t[]>(10 * 0x10000);
		m_framestore_lum[i] = std::make_unique<uint8_t[]>(10 * 0x10000);
		m_framestore_ext[i] = std::make_unique<uint8_t[]>(10 * 0x10000);
	}
	save_pointer(&m_framestore_chr[0][0], "m_framestore_chr[0]", 10 * 0x10000);
	save_pointer(&m_framestore_lum[0][0], "m_framestore_lum[0]", 10 * 0x10000);
	save_pointer(&m_framestore_ext[0][0], "m_framestore_ext[0]", 10 * 0x10000);
	save_pointer(&m_framestore_chr[1][0], "m_framestore_chr[1]", 10 * 0x10000);
	save_pointer(&m_framestore_lum[1][0], "m_framestore_lum[1]", 10 * 0x10000);
	save_pointer(&m_framestore_ext[1][0], "m_framestore_ext[1]", 10 * 0x10000);

	// Brush Store Card
	save_item(NAME(m_bs_y_latch));
	save_item(NAME(m_bs_u_latch));
	save_item(NAME(m_bs_v_latch));
	m_brushstore_chr = std::make_unique<uint8_t[]>(0x10000);
	m_brushstore_lum = std::make_unique<uint8_t[]>(0x10000);
	m_brushstore_ext = std::make_unique<uint8_t[]>(0x10000);
	save_pointer(&m_brushstore_chr[0], "m_brushstore_chr", 0x10000);
	save_pointer(&m_brushstore_lum[0], "m_brushstore_lum", 0x10000);
	save_pointer(&m_brushstore_ext[0], "m_brushstore_ext", 0x10000);

	// Store Address Cards
	save_item(NAME(m_rhscr));
	save_item(NAME(m_rvscr));
	save_item(NAME(m_rzoom));
	save_item(NAME(m_fld_sel));
	save_item(NAME(m_window_enable));
	save_item(NAME(m_cxpos));
	save_item(NAME(m_cypos));
	save_item(NAME(m_ca0));

	// Combiner Card
	save_item(NAME(m_cursor_y));
	save_item(NAME(m_cursor_u));
	save_item(NAME(m_cursor_v));
	save_item(NAME(m_ext_store_flags));
	save_item(NAME(m_select_matte));
	save_item(NAME(m_matte_ext));
	save_item(NAME(m_matte_y));
	save_item(NAME(m_matte_u));
	save_item(NAME(m_matte_v));

	// Size Card
	save_item(NAME(m_size_dsx_ddx));
	save_item(NAME(m_size_dsy_ddx));
	save_item(NAME(m_size_dsx_ddy));
	save_item(NAME(m_size_dsy_ddy));
	save_item(NAME(m_size_frac_origin_sx_dx));
	save_item(NAME(m_size_frac_origin_sy_dx));
	save_item(NAME(m_size_frac_origin_sx_dy));
	save_item(NAME(m_size_frac_origin_sy_dy));
	save_item(NAME(m_size_dxdx_counter));
	save_item(NAME(m_size_dydy_counter));
	save_item(NAME(m_size_dest_x));
	save_item(NAME(m_size_dest_y));

	// Filter Card
	save_item(NAME(m_filter_x_coeffs));
	save_item(NAME(m_filter_y_coeffs));
	save_item(NAME(m_incoming_lum));
	save_item(NAME(m_incoming_chr));
	save_item(NAME(m_buffer_lum));

	// Keyboard
	save_item(NAME(m_keybc_latched_bit));
	save_item(NAME(m_keybc_p1_data));
	save_item(NAME(m_keybc_tx));

	// TDS Box
	save_item(NAME(m_tds_dac_value));
	save_item(NAME(m_tds_dac_offset));
	save_item(NAME(m_tds_dac_percent));
	save_item(NAME(m_tds_adc_busy));
	save_item(NAME(m_tds_adc_value));
	save_item(NAME(m_tds_press_state));
	save_item(NAME(m_tds_pressure_shift));
	save_item(NAME(m_tds_in_proximity));
	m_tds_adc_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dpb7000_state::tds_adc_tick), this));
	m_tds_adc_timer->adjust(attotime::never);
	m_tds_press_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dpb7000_state::tds_press_tick), this));
	m_tds_press_timer->adjust(attotime::never);

	// Tablet
	save_item(NAME(m_tablet_p2_data));
	save_item(NAME(m_tablet_p3_data));
	save_item(NAME(m_tablet_dip_shifter));
	save_item(NAME(m_tablet_counter_latch));
	save_item(NAME(m_tablet_tx_bit));
	save_item(NAME(m_tablet_pen_x));
	save_item(NAME(m_tablet_pen_y));
	save_item(NAME(m_tablet_pen_switches));
	save_item(NAME(m_tablet_state));
	m_tablet_tx_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dpb7000_state::tablet_tx_tick), this));
	m_tablet_tx_timer->adjust(attotime::never);
	m_tablet_irq_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dpb7000_state::tablet_irq_tick), this));
	m_tablet_irq_timer->adjust(attotime::never);

	// Tablet HLE
	m_tablet_hle_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dpb7000_state::tablet_hle_tick), this));
	m_tablet_hle_timer->adjust(attotime::never);

	m_yuv_lut = std::make_unique<uint32_t[]>(0x1000000);
	for (uint16_t u = 0; u < 256; u++)
	{
		for (uint16_t v = 0; v < 256; v++)
		{
			for (uint16_t y = 0; y < 256; y++)
			{
				float fr = y + 1.4075f * (v - 128);
				float fg = y - 0.3455f * (u - 128) - (0.7169f * (v - 128));
				float fb = y + 1.7790f * (u - 128);
				uint8_t r = (fr < 0.0f) ? 0 : (fr > 255.0f ? 255 : (uint8_t)fr);
				uint8_t g = (fg < 0.0f) ? 0 : (fg > 255.0f ? 255 : (uint8_t)fg);
				uint8_t b = (fb < 0.0f) ? 0 : (fb > 255.0f ? 255 : (uint8_t)fb);
				m_yuv_lut[(u << 16) | (v << 8) | y] = 0xff000000 | (r << 16) | (g << 8) | b;
			}
		}
	}
}

void dpb7000_state::machine_reset()
{
	m_cmd_done_timer->adjust(attotime::never);

	// Computer Card
	m_brg->stt_w(m_baud_dip->read());
	m_csr = 0;
	m_sys_ctrl = 0;
	for (int i = 0; i < 3; i++)
	{
		m_acia[i]->write_cts(0);
		m_acia[i]->write_dcd(0);
	}

	m_field_in_clk->adjust(attotime::from_hz(59.94), 0, attotime::from_hz(59.94));
	m_field_out_clk->adjust(attotime::from_hz(59.94) + attotime::from_hz(15734.0 / 1.0), 1, attotime::from_hz(59.94));

	// Disc Sequencer Card
	m_diskseq_cyl_from_cpu = 0;
	m_diskseq_cmd_word_from_cpu = 0;
	m_diskseq_cmd = 0;
	m_diskseq_cmd_to_ctrl = 0;
	m_diskseq_status = 0xf9;
	m_diskseq_cyl_read_pending = false;
	m_diskseq_cyl_write_pending = false;
	m_diskseq_use_hdd_pending = false;
	m_diskseq_command_stride = 1;
	m_diskseq_complete_clk->adjust(attotime::never);
	m_hdd_command_timer->adjust(attotime::never);

	// Floppy Disc Controller Card
	m_fdd_debug_rx_bits.clear();
	m_fdd_debug_rx_bit_count = 0;
	m_fdd_debug_rx_byte_count = 0;
	m_fdd_debug_rx_recv_count = 0;
	m_fdd_ctrl = 0;
	m_fdd_port1 = 0;
	m_fdd_track = 20;
	m_fdd_side = 0;
	m_fdd_pll.set_clock(attotime::from_hz(1000000));
	m_fdd_pll.reset(machine().time());
	m_floppy = nullptr;

	// Disc Data Buffer Card
	m_diskbuf_ram_addr = 0;
	memset(m_diskbuf_ram, 0, 14 * 0x800);
	m_diskbuf_data_count = 0;

	// Output Timing Card
	m_cursor_origin_x = 0;
	m_cursor_origin_y = 0;
	m_cursor_size_x = 0;
	m_cursor_size_y = 0;
	m_output_csflags = 0;
	m_output_cpflags = 0;

	// Brush Address Card
	m_line_clock = 0;
	m_line_count = 0;
	m_line_length = 0;
	m_brush_addr_func = 0;
	m_brush_addr_cmd = 0;
	m_bif = 0;
	m_bixos = 0;
	m_biyos = 0;
	m_bxlen = 0;
	m_bylen = 0;
	m_bxlen_counter = 0;
	m_bylen_counter = 0;
	m_brush_press_lum = 0;
	m_brush_press_chr = 0;

	// Frame Store Cards, 640x1024
	for (int i = 0; i < 2; i++)
	{
		memset(&m_framestore_chr[i][0], 0, 10 * 0x10000);
		memset(&m_framestore_lum[i][0], 0, 10 * 0x10000);
		memset(&m_framestore_ext[i][0], 0, 10 * 0x10000);
	}

	// Brush Store Card
	m_bs_y_latch = 0;
	m_bs_u_latch = 0;
	m_bs_v_latch = 0;
	memset(&m_brushstore_chr[0], 0, 0x10000);
	memset(&m_brushstore_lum[0], 0, 0x10000);
	memset(&m_brushstore_ext[0], 0, 0x10000);

	// Store Address Card
	memset(m_rhscr, 0, sizeof(m_rhscr));
	memset(m_rvscr, 0, sizeof(m_rvscr));
	memset(m_rzoom, 0, sizeof(m_rzoom));
	memset(m_fld_sel, 0, sizeof(m_fld_sel));
	memset(m_window_enable, 0, sizeof(m_window_enable));
	memset(m_cxpos, 0, sizeof(m_cxpos));
	memset(m_cypos, 0, sizeof(m_cypos));
	m_ca0 = 0;

	// Combiner Card
	m_cursor_y = 0;
	m_cursor_u = 0;
	m_cursor_v = 0;
	m_ext_store_flags = 0;
	memset(m_select_matte, 0, 2);
	memset(m_matte_ext, 0, 2);
	memset(m_matte_y, 0, 2);
	memset(m_matte_u, 0, 2);
	memset(m_matte_v, 0, 2);

	// Filter Card
	memset(m_filter_x_coeffs, 0, sizeof(m_filter_x_coeffs));
	memset(m_filter_y_coeffs, 0, sizeof(m_filter_y_coeffs));
	m_incoming_lum = 0;
	m_incoming_chr = 0;
	m_buffer_lum = true;

	// Size Card
	m_size_dsx_ddx = 0;
	m_size_dsy_ddx = 0;
	m_size_dsx_ddy = 0;
	m_size_dsy_ddy = 0;
	m_size_frac_origin_sx_dx = 0;
	m_size_frac_origin_sy_dx = 0;
	m_size_frac_origin_sx_dy = 0;
	m_size_frac_origin_sy_dy = 0;
	m_size_dxdx_counter = 0;
	m_size_dydy_counter = 0;
	m_size_dest_x = 0;
	m_size_dest_y = 0;

	// Keyboard
	m_keybc_latched_bit = 1;
	m_keybc_p1_data = 0;
	m_keybc_tx = 0;

	// TDS Box
	m_tds_dac_value = 0;
	m_tds_dac_offset = 0;
	m_tds_dac_percent = 0.0f;
	m_tds_adc_busy = false;
	m_tds_adc_value = 0x00;
	m_tds_pressure_shift = 0;
	m_tds_in_proximity = false;
	m_tds_press_state = STATE_IDLE;
	m_tds_adc_timer->adjust(attotime::never);
	m_tds_press_timer->adjust(attotime::from_hz(120), 0, attotime::from_hz(120));

	// Tablet
	m_tablet_p2_data = 0;
	m_tablet_p3_data = 0;
	m_tablet_dip_shifter = 0;
	m_tablet_mux = 0;
	m_tablet_drq = 0;
	m_tablet_counter_latch = 0;
	m_tablet_tx_timer->adjust(attotime::from_hz(9600), 0, attotime::from_hz(9600));
	m_tablet_irq_timer->adjust(attotime::never);
	m_tablet_tx_bit = 1;
	m_tablet_pen_x = 0;
	m_tablet_pen_y = 0;
	m_tablet_pen_switches = 0;
	m_tablet_state = 0;

	// Tablet HLE
	m_tablet_hle_tx_bits.clear();
	m_tablet_hle_timer->adjust(attotime::from_hz(40), 0, attotime::from_hz(40));
}

MC6845_UPDATE_ROW(dpb7000_state::crtc_update_row)
{
	const pen_t *const pen = m_palette->pens();

	for (int column = 0; column < x_count; column++)
	{
		uint8_t code = (uint8_t)m_vdu_ram[((ma + column) & 0x7ff)];
		uint16_t addr = code << 4 | (ra & 0x0f);
		uint8_t data = m_vdu_char_rom[addr & 0xfff];

		if (column == cursor_x)
		{
			data = 0xff;
		}

		for (int bit = 0; bit < 8; bit++)
		{
			int x = (column * 8) + bit;
			int color = BIT(data, 7) && de;

			bitmap.pix(y, x) = pen[color];

			data <<= 1;
		}
	}
}

MC6845_ON_UPDATE_ADDR_CHANGED(dpb7000_state::crtc_addr_changed)
{
}

void dpb7000_state::csr_w(uint8_t data)
{
	LOGMASKED(LOG_CSR, "%s: Card Select write: %02x (%04x)\n", machine().describe_context(), data & 0x0f, data);
	m_csr = data & 0x0f;
}

uint8_t dpb7000_state::csr_r()
{
	LOGMASKED(LOG_CSR, "%s: Card Select read(?): %02x\n", machine().describe_context(), m_csr);
	return m_csr;
}

uint16_t dpb7000_state::cpu_ctrlbus_r()
{
	uint16_t ret = 0;
	switch (m_csr)
	{
	case 0:
		if (!machine().side_effects_disabled())
			LOGMASKED(LOG_CTRLBUS, "%s: CPU read from Control Bus, Brush Address Card status: %04x\n", machine().describe_context(), m_brush_addr_func);
		ret = m_brush_addr_func;
		break;
	case 1:
		if (!machine().side_effects_disabled())
		{
			LOGMASKED(LOG_CTRLBUS, "%s: CPU read from Control Bus, Disk Sequencer Card status: %02x\n", machine().describe_context(), m_diskseq_status);
		}
		ret = m_diskseq_status;
		break;
	case 7:
		ret = m_diskbuf_ram[m_diskbuf_ram_addr];
		LOGMASKED(LOG_DDB, "%s: CPU read from Control Bus, Disc Data Buffer Card RAM read: %04x = %02x\n", machine().describe_context(), m_diskbuf_ram_addr, ret);
		m_diskbuf_ram_addr++;
		break;
	case 8:
	{
		if (m_brush_addr_cmd == 3) // Framestore Read
		{
			const uint16_t x = m_cxpos[1] + (m_bxlen_counter - m_bxlen);
			const uint16_t y = m_cypos[1] + (m_bylen_counter - m_bylen);

			uint32_t pix_idx = y * 800 + x;
			uint8_t &chr = m_ca0 ? m_bs_v_latch : m_bs_u_latch;
			if (!BIT(m_brush_addr_func, 5))
			{
				if (BIT(m_brush_addr_func, 7))
					m_bs_y_latch = m_framestore_lum[0][pix_idx];
				if (BIT(m_brush_addr_func, 8))
					chr = m_framestore_chr[0][pix_idx];
				if (BIT(m_brush_addr_func, 9))
					m_bs_y_latch = m_framestore_ext[0][pix_idx];
			}
			if (!BIT(m_brush_addr_func, 6))
			{
				if (BIT(m_brush_addr_func, 7))
					m_bs_y_latch = m_framestore_lum[1][pix_idx];
				if (BIT(m_brush_addr_func, 8))
					chr = m_framestore_chr[1][pix_idx];
				if (BIT(m_brush_addr_func, 9))
					m_bs_y_latch = m_framestore_ext[1][pix_idx];
			}

			m_bxlen_counter++;
			if (m_bxlen_counter == 0x1000)
			{
				m_bxlen_counter = m_bxlen;
				m_bylen_counter++;
				if (m_bylen_counter == 0x1000)
				{
					cmd_done(0);
				}
			}
		}

		uint16_t data = (uint16_t)m_bs_y_latch;
		if (m_ca0)
		{
			data |= (uint16_t)m_bs_v_latch << 8;
			LOGMASKED(LOG_CTRLBUS | LOG_BRUSH_ADDR | LOG_BRUSH_LATCH, "%s: Brush Store Card color latches read (VY): %04x\n\n", machine().describe_context(), data);
		}
		else
		{
			data |= (uint16_t)m_bs_u_latch << 8;
			LOGMASKED(LOG_CTRLBUS | LOG_BRUSH_ADDR | LOG_BRUSH_LATCH, "%s: Brush Store Card color latches read (UY): %04x\n", machine().describe_context(), data);
		}
		m_ca0 = 1 - m_ca0;
		return data;
	}
	case 12:
		ret = m_config_sw34->read();
		if (!machine().side_effects_disabled())
			LOGMASKED(LOG_CTRLBUS, "%s: CPU read from Control Bus, Config Switches 3/4: %04x\n", machine().describe_context(), ret);
		break;
	case 14:
		ret = m_config_sw12->read();
		if (!machine().side_effects_disabled())
			LOGMASKED(LOG_CTRLBUS, "%s: CPU read from Control Bus, Config Switches 1/2: %04x\n", machine().describe_context(), ret);
		break;
	default:
		if (!machine().side_effects_disabled())
			LOGMASKED(LOG_CTRLBUS | LOG_UNKNOWN, "%s: CPU read from Control Bus, unknown CSR %d\n", machine().describe_context(), m_csr);
		break;
	}
	return ret;
}

void dpb7000_state::store_address_w(uint8_t card, uint16_t data)
{
	switch ((data >> 12) & 7)
	{
	case 0:
		LOGMASKED(LOG_STORE_ADDR, "%s: Store Address Card %d, set RHSCR: %03x\n", machine().describe_context(), card + 1, data & 0xfff);
		m_rhscr[card] = data & 0xfff;
		if (!card)
		{
			LOGMASKED(LOG_STORE_ADDR, "%s: Setting Store Address Card 2 RHSCR also: %03x\n", machine().describe_context(), data & 0xfff);
			m_rhscr[1] = data & 0xfff;
		}
		break;
	case 1:
		LOGMASKED(LOG_STORE_ADDR, "%s: Store Address Card %d, set RVSCR: %03x\n", machine().describe_context(), card + 1, data & 0xfff);
		m_rvscr[card] = data & 0xfff;
		if (!card)
		{
			LOGMASKED(LOG_STORE_ADDR, "%s: Setting Store Address Card 2 RVSCR also: %03x\n", machine().describe_context(), data & 0xfff);
			m_rvscr[1] = data & 0xfff;
		}
		break;
	case 2:
		LOGMASKED(LOG_STORE_ADDR, "%s: Store Address Card %d, set R ZOOM: %03x\n", machine().describe_context(), card + 1, data & 0xfff);
		m_rzoom[card] = data & 0xf;
		break;
	case 3:
		LOGMASKED(LOG_STORE_ADDR, "%s: Store Address Card %d, set FLDSEL: %03x\n", machine().describe_context(), card + 1, data & 0xfff);
		m_fld_sel[card] = data & 0xf;
		m_window_enable[card] = BIT(m_fld_sel[card], 2);
		break;
	case 4:
		LOGMASKED(LOG_STORE_ADDR, "%s: Store Address Card %d, set CXPOS: %03x\n", machine().describe_context(), card + 1, data & 0xfff);
		m_cxpos[0] = data & 0xfff;
		m_cxpos[1] = data & 0xfff;
		//m_ca0 = data & 1;
		break;
	case 5:
		LOGMASKED(LOG_STORE_ADDR, "%s: Store Address Card %d, set CYPOS: %03x\n", machine().describe_context(), card + 1, data & 0xfff);
		m_cypos[0] = data & 0xfff;
		m_cypos[1] = data & 0xfff;
		break;
	default:
		LOGMASKED(LOG_STORE_ADDR, "%s: Store Address Card %d, unknown register: %04x\n", machine().describe_context(), card + 1, data);
		break;
	}
}

void dpb7000_state::combiner_reg_w(uint16_t data)
{
	const uint8_t reg_idx = (data >> 10) & 0xf;
	switch (reg_idx)
	{
		case 0: // Output Timing CS Flags
			LOGMASKED(LOG_OUTPUT_TIMING, "%s: Output Timing Card CS Register write: %02x\n", machine().describe_context(), (uint8_t)data);
			m_output_csflags = (uint8_t)(data & 0x0003);
			break;
		case 1: // CY
			LOGMASKED(LOG_COMBINER, "%s: Combiner Card Constant Y write: %02x\n", machine().describe_context(), (uint8_t)data);
			m_cursor_y = (uint8_t)data;
			break;
		case 2: // CV
			LOGMASKED(LOG_COMBINER, "%s: Combiner Card Constant V write: %02x\n", machine().describe_context(), (uint8_t)data);
			m_cursor_v = (uint8_t)data;
			break;
		case 3: // CU
			LOGMASKED(LOG_COMBINER, "%s: Combiner Card Constant U write: %02x\n", machine().describe_context(), (uint8_t)data);
			m_cursor_u = (uint8_t)data;
			break;
		case 4: // ES
			LOGMASKED(LOG_COMBINER, "%s: Combiner Card Ext. Store Flags (ES) write: %02x\n", machine().describe_context(), (uint8_t)data);
			m_ext_store_flags = (uint8_t)data;
			break;
		case 5: // EI
			LOGMASKED(LOG_COMBINER, "%s: Combiner Card Ext I Matte (EI) write: %02x\n", machine().describe_context(), (uint8_t)data);
			m_matte_ext[0] = (uint8_t)data;
			break;
		case 6: // EII
			LOGMASKED(LOG_COMBINER, "%s: Combiner Card Ext II Matte (EII) write: %02x\n", machine().describe_context(), (uint8_t)data);
			m_matte_ext[1] = (uint8_t)data;
			break;
		case 7: // Output Timing CP Flags
			LOGMASKED(LOG_OUTPUT_TIMING, "%s: Output Timing Card CP Register write: %02x\n", machine().describe_context(), (uint8_t)data);
			m_output_cpflags = (uint8_t)(data & 0x001f);
			break;
		case 8: // IS
			LOGMASKED(LOG_COMBINER, "%s: Combiner Card Matte I Select (IS) write: %02x\n", machine().describe_context(), (uint8_t)data);
			m_select_matte[0] = BIT(data, 0);
			break;
		case 9: // IY
			LOGMASKED(LOG_COMBINER, "%s: Combiner Card Matte I Y (IY) write: %02x\n", machine().describe_context(), (uint8_t)data);
			m_matte_y[0] = (uint8_t)data;
			break;
		case 10: // IU
			LOGMASKED(LOG_COMBINER, "%s: Combiner Card Matte I U (IU) write: %02x\n", machine().describe_context(), (uint8_t)data);
			m_matte_u[0] = (uint8_t)data;
			break;
		case 11: // IV
			LOGMASKED(LOG_COMBINER, "%s: Combiner Card Matte I V (IV) write: %02x\n", machine().describe_context(), (uint8_t)data);
			m_matte_v[0] = (uint8_t)data;
			break;
		case 12: // IIS
			LOGMASKED(LOG_COMBINER, "%s: Combiner Card Matte II Select (IIS) write: %02x\n", machine().describe_context(), (uint8_t)data);
			m_select_matte[1] = BIT(data, 0);
			break;
		case 13: // IIY
			LOGMASKED(LOG_COMBINER, "%s: Combiner Card Matte II Y (IIY) write: %02x\n", machine().describe_context(), (uint8_t)data);
			m_matte_y[1] = (uint8_t)data;
			break;
		case 14: // IIU
			LOGMASKED(LOG_COMBINER, "%s: Combiner Card Matte II U (IIU) write: %02x\n", machine().describe_context(), (uint8_t)data);
			m_matte_u[1] = (uint8_t)data;
			break;
		case 15: // IIV
			LOGMASKED(LOG_COMBINER, "%s: Combiner Card Matte II V (IIV) write: %02x\n", machine().describe_context(), (uint8_t)data);
			m_matte_v[1] = (uint8_t)data;
			break;
	}
}

bool dpb7000_state::handle_command(uint16_t data)
{
	//printf("handle_command %d, cxpos %d, cypos %d\n", m_brush_addr_cmd, m_cxpos[1], m_cypos[1]);
	switch (m_brush_addr_cmd)
	{
	case 0: // Live Video
		LOGMASKED(LOG_COMMANDS, "Unsupported command: Live Video\n");
		break;
	case 1: // Brush Store Read
		LOGMASKED(LOG_COMMANDS, "Unsupported command: Brush Store Read\n");
		break;
	case 2: // Brush Store Write
	case 3: // Framestore Read
	case 4: // Framestore Write
		m_bxlen_counter = m_bxlen;
		m_bylen_counter = m_bylen;
		return false;
	case 5: // Fast Wipe Video
		LOGMASKED(LOG_COMMANDS, "Unsupported command: Fast Wipe Video\n");
		break;
	case 6: // Fast Wipe Brush Store
		LOGMASKED(LOG_COMMANDS, "Unsupported command: Fast Wipe Brush Store\n");
		break;
	/*case 7: // Fast Wipe Framestore
	    for (int i = 0; i < 2; i++)
	    {
	        if (!BIT(data, 5 + i))
	        {
	            m_bylen_counter = m_bylen;
	            for (uint16_t y = m_cypos[1]; m_bylen_counter != 0x1000; m_bylen_counter++, y = (y + 1) & 0xfff)
	            {
	                if (y >= 768)
	                    continue;

	                uint8_t *lum = &m_framestore_lum[i][y * 800];
	                uint8_t *chr = &m_framestore_chr[i][y * 800];
	                uint8_t *ext = &m_framestore_ext[i][y * 800];
	                m_bxlen_counter = m_bxlen;
	                for (uint16_t x = m_cxpos[1]; m_bxlen_counter != 0x1000; m_bxlen_counter++, x = (x + 1) & 0xfff)
	                {
	                    if (x >= 800)
	                        continue;

	                    lum[x] = m_bs_y_latch;
	                    chr[x] = (x & 1) ? m_bs_v_latch : m_bs_u_latch;
	                    if (BIT(data, 9))
	                        ext[x] = m_bs_y_latch;
	                }
	            }
	        }
	    }
	    break;*/
	case 7: // Fast Wipe Framestore
	case 8: // Draw
	{
		const bool use_brush = (m_brush_addr_cmd == 8);
		const bool lum_en = BIT(data, 7);
		const bool chr_en = BIT(data, 8);
		const bool ext_en = BIT(data, 9);
		bool const write_en[2] = { lum_en, chr_en };

		const bool s1_disable = BIT(data, 5);
		const bool s2_disable = BIT(data, 6);

		const uint16_t sel_1 = (uint16_t)s1_disable << 5;
		const uint16_t sel_2 = (uint16_t)s2_disable << 4;
		const uint16_t sel_ext = ext_en << 6;
		const uint16_t fcs = BIT(data, 13);

		const uint16_t prom_addr = (fcs << 8) | (use_brush ? 0x80 : 0x00) | sel_ext | sel_1 | sel_2 | bitswap<4>(data, 1, 2, 3, 4);
		const uint8_t prom_data = m_brushproc_prom[prom_addr];

		const bool brush_zero = BIT(data, 12);
		const bool oe1 = BIT(prom_data, 0);
		const bool oe2 = BIT(prom_data, 1);
		const bool oe3 = BIT(prom_data, 2);
		const bool oe4 = BIT(prom_data, 7);

		const bool use_s1_data = (oe3 == oe4);
		const bool use_s2_data = !oe4;
		const bool use_ext_data = !oe3;
		const bool mask_sel = BIT(prom_data, 3);
		const bool word_width = BIT(prom_data, 4);
		const bool use_k_data = BIT(~prom_data, 6);
		const bool brush_invert = BIT(~data, 11);
		const uint16_t pal_addr_extra = (BIT(prom_data, 5) << 8) | (brush_invert << 9);

		const uint16_t brushaddr_pal_addr = (uint16_t)bitswap<4>(data, 1, 2, 3, 4) | (1 << 4) | (1 << 5) | (0 << 7) | (BIT(data, 5) << 8) | (1 << 9) | (1 << 10);
		const uint8_t brushaddr_pal_val = m_brushaddr_pal[brushaddr_pal_addr];

		int ext_idx = BIT(brushaddr_pal_val, 1) ? 1 : 0;

		uint8_t *store_1[2] = { &m_framestore_lum[0][0], &m_framestore_chr[0][0] };
		uint8_t *store_2[2] = { &m_framestore_lum[1][0], &m_framestore_chr[1][0] };
		uint8_t pressure[2] = { m_brush_press_lum, m_brush_press_chr };

		uint16_t bxlen = m_bxlen;//(((m_bxlen << 3) | (m_bixos & 7)) >> (m_bif & 3)) & 0xfff;
		uint16_t bylen = m_bylen;//(((m_bylen << 3) | (m_biyos & 7)) >> ((m_bif >> 2) & 3)) & 0xfff;
		//uint16_t width = 0x1000 - bxlen;
		//if (m_brush_addr_cmd == 8)
		m_ca0 = 0;
		//printf("\n%sing %dx%d brush at %d,%d (data %04x) (Store %d)\n", m_brush_addr_cmd == 8 ? "Draw" : "Wip", 0x1000 - m_bxlen, 0x1000 - m_bylen, m_cxpos[1], m_cypos[1], data, !s1_disable ? 1 : (!s2_disable ? 2 : 0));
		LOGMASKED(LOG_BRUSH_DRAWS, "%sing %dx%d brush at %d,%d (Store %d)\n", m_brush_addr_cmd == 8 ? "Draw" : "Wip", 0x1000 - m_bxlen, 0x1000 - m_bylen, m_cxpos[1], m_cypos[1], !s1_disable ? 1 : (!s2_disable ? 2 : 0));
		if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1)
		{
			LOGMASKED(LOG_BRUSH_DRAWS, "    BAddrPALAddr:%03x BAddrPALVal:%02x ExtIdx:%d\n", brushaddr_pal_addr, brushaddr_pal_val, ext_idx);
			LOGMASKED(LOG_BRUSH_DRAWS, "    /S1:%d /S2:%d LUM:%d CHR:%d EXT:%d BZ:%d /BI:%d FCS:%d\n", s1_disable, s2_disable, lum_en, chr_en, BIT(data, 9), brush_zero, brush_invert, fcs);
			LOGMASKED(LOG_BRUSH_DRAWS, "    PROMAddr:%03x PROMVal:%02x OE1:%d OE2:%d OE3:%d OE4:%d MSEL:%d 16BIT:%d\n", prom_addr, prom_data, oe1, oe2, oe3, oe4, mask_sel, word_width);
		}

		for (uint16_t y = m_cypos[1], bly = bylen; bly != 0x1000; bly++, y = (y + 1) & 0xfff)
		{
			if (y >= 768)
				continue;

			uint32_t line_idx = y * 800;
			for (uint16_t x = m_cxpos[1], blx = bxlen; blx != 0x1000; blx++, x = (x + 1) & 0xfff)
			{
				if (x >= 800)
					continue;

				if (m_output_cpflags & 0x0c)
				{
					const uint16_t x_nib_addr = (x >> 1) & 0x3ff;
					uint16_t x_xlnib = m_storeaddr_xlnib[x_nib_addr] & 0xe;
					uint16_t x_xmnib = (m_storeaddr_xmnib[x_nib_addr] & 0x0f) << 4;
					uint16_t x_xhnib = (m_storeaddr_xhnib[x_nib_addr] & 0x0f) << 8;
					uint16_t remapped_x = x_xhnib | x_xmnib | x_xlnib | (x & 1);

					uint16_t prot_addr = (BIT(~m_output_cpflags, 2) << 8) | (BIT(~m_output_cpflags, 3) << 9);
					uint16_t prot_x_addr = (remapped_x >> 4) & 0xff;
					uint16_t prot_y_addr = ((y - 8) >> 2) & 0xff;
					const bool prot_x = m_storeaddr_protx[prot_addr | prot_x_addr] & 1;
					const bool prot_y = m_storeaddr_proty[prot_addr | prot_y_addr] & 1;

					if (prot_x || prot_y)
					{
						LOGMASKED(LOG_CSR, "Prot Reject: %03x, %03x (%d, %d)\n", prot_addr | prot_x_addr, prot_addr | prot_y_addr, prot_x, prot_y);
						continue;
					}
				}

				uint32_t pix_idx = line_idx + x;

				uint8_t uv_bsel = x & 1;

				uint8_t fixed_chr = (uv_bsel ? m_bs_v_latch : m_bs_u_latch);
				uint8_t brush_values[2] = { m_bs_y_latch, fixed_chr };
				uint8_t brush_ext = 0xff;

				if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1)
				{
					LOGMASKED(LOG_BRUSH_DRAWS, "    Out x,y: %d,%d\n", x, y);
				}
				if (use_brush)
				{
					uint16_t bx = ((((blx - bxlen) << 3) | (m_bixos & 7)) >> (3 - (m_bif & 3))) & 0xfff;
					uint16_t by = ((((bly - bylen) << 3) | (m_biyos & 7)) >> ((3 - (m_bif >> 2)) & 3)) & 0xfff;
					uint16_t uv_bx = ((x & 1) ? (bx | 1) : (bx & ~1));
					uint8_t brush_lum = m_brushstore_lum[by * 256 + bx];
					uint8_t brush_chr = m_brushstore_chr[by * 256 + uv_bx];
					brush_ext = ext_en ? m_brushstore_ext[by * 256 + bx] : brush_lum;
					brush_values[0] = (fcs || !lum_en) ? m_bs_y_latch : brush_lum;
					brush_values[1] = (fcs || !chr_en) ? fixed_chr : brush_chr;

					if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1)
					{
						LOGMASKED(LOG_BRUSH_DRAWS, "    Brush pixel %d,%d (bl %03x,%03x) (blen %03x,%03x) (store %d,%d), Brush LCE %02x %02x %02x:\n", 0x1000 - blx, 0x1000 - bly, blx, bly, bxlen, bylen, bx, by, brush_values[0], brush_values[1], brush_ext);
					}
				}
				else if (s1_disable && !s2_disable && x == 22 && y == 8)
				{
					LOGMASKED(LOG_BRUSH_DRAWS, "    Fixed brush value LCE %02x %02x %02x (YUV %02x %02x %02x)\n\n", brush_values[0], brush_values[1], brush_ext, m_bs_y_latch, m_bs_u_latch, m_bs_v_latch);
				}

				for (int ch = 0; ch < 2; ch++)
				{
					uint8_t s1_data = store_1[ch][pix_idx];
					uint8_t s2_data = store_2[ch][pix_idx];
					//uint8_t sext_data = m_framestore_ext[!s2_disable ? 1 : 0][pix_idx];
					uint8_t sext_data = m_framestore_ext[ext_idx][pix_idx];

					uint8_t brush_data = brush_values[ch];
					uint8_t brush_ext_data = use_k_data ? brush_ext : 0xff;
					uint8_t other_data = 0x00;
					uint8_t pressure_data = pressure[ch];
					uint8_t press_product = (uint8_t)((brush_ext_data * pressure_data + 0x0080) >> 8);
					if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1)
					{
						LOGMASKED(LOG_BRUSH_DRAWS, "        brush ext (%02x) * pressure (%02x) = %02x\n", brush_ext_data, pressure_data, press_product);
					}

					uint8_t mask_multiplicand = 0x00;
					if (brush_zero)
						mask_multiplicand = (mask_sel ? sext_data : 0xff);

					uint8_t press_result = (uint8_t)((press_product * mask_multiplicand + 0x0080) >> 8);
					if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1)
					{
						LOGMASKED(LOG_BRUSH_DRAWS, "        press product (%02x) * mask multiplicand (%02x) = %02x\n\n", press_product, mask_multiplicand, press_result);
					}
					uint16_t pal_addr = (uint8_t)bitswap<8>(press_result, 0, 1, 2, 3, 4, 5, 6, 7) | pal_addr_extra;
					uint16_t pal_data = m_brushproc_pal[pal_addr];
					uint8_t pal_value = (uint8_t)pal_data;
					bool pal_sel = BIT(pal_data, 8);
					bool pal_invert = BIT(pal_data, 9);

					if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1)
					{
						LOGMASKED(LOG_BRUSH_DRAWS, "        Brush %s data: %02x\n", ch ? "chr" : "lum", brush_data);
						LOGMASKED(LOG_BRUSH_DRAWS, "        Bext:%02x, PressDat:%02x, PressProd:%02x, MaskMult:%02x, PressRes:%02x, PALAddr:%03x, PALData:%03x, PALVal:%02x\n", brush_ext_data, pressure_data, press_product, mask_multiplicand, press_result, pal_addr, pal_data, pal_value);
						LOGMASKED(LOG_BRUSH_DRAWS, "        S1:%02x S2:%02x SE:%02x\n", s1_data, s2_data, sext_data);
					}

					if (use_s1_data)
					{
						other_data = s1_data;
						if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1) LOGMASKED(LOG_BRUSH_DRAWS, "        Using Store I for other data, %02x\n", other_data);
					}
					else if (use_s2_data)
					{
						other_data = s2_data;
						if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1) LOGMASKED(LOG_BRUSH_DRAWS, "        Using Store II for other data, %02x\n", other_data);
					}
					else if (use_ext_data)
					{
						other_data = sext_data;
						if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1) LOGMASKED(LOG_BRUSH_DRAWS, "        Using Stencil for other data, %02x\n", other_data);
					}

					if (!oe1)
					{
						brush_data = s1_data;
						if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1) LOGMASKED(LOG_BRUSH_DRAWS, "        Using Store I for brush data, %02x\n", other_data);
					}
					else if (!oe2)
					{
						brush_data = s2_data;
						if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1) LOGMASKED(LOG_BRUSH_DRAWS, "        Using Store II for brush data, %02x\n", other_data);
					}

					if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1)
					{
						LOGMASKED(LOG_BRUSH_DRAWS, "        Before-comp brush data %02x, other data %02x\n", brush_data, other_data);
					}

					uint8_t comp_a = brush_data;
					uint8_t comp_b = other_data;

					uint8_t alu_out = 0x00;
					bool final_add = false;
					if (comp_a < comp_b)
					{
						if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1)
						{
							LOGMASKED(LOG_BRUSH_DRAWS, "        comp_a %02x < comp_b %02x, final ALU pass should subtract\n", comp_a, comp_b);
						}
						alu_out = other_data - brush_data;
					}
					else
					{
						if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1)
						{
							LOGMASKED(LOG_BRUSH_DRAWS, "        comp_a %02x > comp_b %02x, final ALU pass should add, calculating %02x - %02x = %02x\n", comp_a, comp_b, comp_a, comp_b, comp_a - comp_b);
						}
						alu_out = brush_data - other_data;
						final_add = true;
					}

					if (pal_invert)
						brush_data = ~brush_data;

					uint16_t out_product = (uint16_t)alu_out * (uint16_t)pal_value;
					if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1)
					{
						LOGMASKED(LOG_BRUSH_DRAWS, "        ALU out %02x, out product %04x (%02x * %02x), PALSel:%d, PALInv:%d\n", alu_out, out_product, alu_out, pal_value, pal_sel, pal_invert);
					}
					uint8_t final_msb = 0;
					uint8_t final_lsb = 0;
					if (!word_width)
					{
						uint16_t alu_a = (other_data << 8) | s2_data;
						uint16_t alu_b = out_product;
						uint16_t final_alu_out = (final_add ? (alu_a + alu_b) : (alu_a - alu_b));

						final_msb = (pal_sel ? (final_alu_out >> 8) : brush_data);
						final_lsb = final_msb;

						if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1)
						{
							if (final_add)
							{
								LOGMASKED(LOG_BRUSH_DRAWS, "        8bpp, S1D %02x, S2D %02x, ALUA %04x, ALUB %04x, ALU final %04x + %04x = %04x, MSB %02x\n", s1_data, s2_data, alu_a, alu_b, alu_a, alu_b, final_alu_out, final_msb);
							}
							else
							{
								LOGMASKED(LOG_BRUSH_DRAWS, "        8bpp, S1D %02x, S2D %02x, ALUA %04x, ALUB %04x, ALU final %04x - %04x = %04x, MSB %02x\n", s1_data, s2_data, alu_a, alu_b, alu_a, alu_b, final_alu_out, final_msb);
							}
						}
					}
					else
					{
						uint16_t alu_a = (other_data << 8) | s2_data;
						uint16_t alu_b = out_product;
						uint16_t final_alu_out = (final_add ? (alu_a + alu_b) : (alu_a - alu_b));
						final_msb = (pal_sel ? (final_alu_out >> 8) : brush_data);
						final_lsb = (uint8_t)final_alu_out;

						if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1)
						{
							if (final_add)
							{
								LOGMASKED(LOG_BRUSH_DRAWS, "        16bpp, ALUA %04x, ALUB %04x, %04x+%04x=%04x, MSB %02x, LSB %02x\n", alu_a, alu_b, alu_a, alu_b, final_alu_out, final_msb, final_lsb);
							}
							else
							{
								LOGMASKED(LOG_BRUSH_DRAWS, "        16bpp, ALUA %04x, ALUB %04x, %04x-%04x=%04x, MSB %02x, LSB %02x\n", alu_a, alu_b, alu_a, alu_b, final_alu_out, final_msb, final_lsb);
							}
						}
					}

					if (write_en[ch])
					{
						if (!s1_disable)
						{
							if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1)
							{
								LOGMASKED(LOG_BRUSH_DRAWS, "        Storing %02x in Store 1 %s\n", final_msb, ch ? "chr" : "lum");
							}
							store_1[ch][pix_idx] = s2_disable ? final_lsb : final_msb;
						}
						if (!s2_disable)
						{
							store_2[ch][pix_idx] = final_lsb;
							if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1)
							{
								LOGMASKED(LOG_BRUSH_DRAWS, "        Storing %02x in Store 2 %s\n", final_lsb, ch ? "chr" : "lum");
							}
						}
					}

					if (ch == 0 && ext_en)
					{
						if (!s1_disable)
						{
							if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1)
							{
								LOGMASKED(LOG_BRUSH_DRAWS, "        Storing %02x in EXT 1\n", final_msb);
							}
							m_framestore_ext[0][pix_idx] = final_msb;
						}
						if (!s2_disable)
						{
							if ((m_cxpos[1] == 0x16 && m_cypos[1] == 0xda) || (m_cxpos[1] == 0x216 && m_cypos[1] == 0x58) || data == 0x39d1)
							{
								LOGMASKED(LOG_BRUSH_DRAWS, "        Storing %02x in EXT 2\n", final_lsb);
							}
							m_framestore_ext[1][pix_idx] = final_lsb;
						}
					}
				}
			}
		}
		break;
	}
	case 9: // Draw with Stencil I
		LOGMASKED(LOG_COMMANDS, "Unsupported command: Draw with Stencil I\n");
		break;
	case 10: // Draw with Stencil II
		LOGMASKED(LOG_COMMANDS, "Unsupported command: Draw with Stencil II\n");
		break;
	case 11: // Copy to Framestore
		LOGMASKED(LOG_COMMANDS, "Unsupported command: Copy to Framestore\n");
		break;
	case 12: // Copy to Brush Store
	{
		uint8_t *store_lum = !BIT(data, 5) ? &m_framestore_lum[0][0] : (!BIT(data, 6) ? &m_framestore_lum[1][0] : nullptr);
		uint8_t *store_chr = !BIT(data, 5) ? &m_framestore_chr[0][0] : (!BIT(data, 6) ? &m_framestore_chr[1][0] : nullptr);
		uint8_t *store_ext = !BIT(data, 5) ? &m_framestore_ext[0][0] : (!BIT(data, 6) ? &m_framestore_ext[1][0] : nullptr);
		if (!BIT(data, 7) || true)
			store_lum = nullptr;
		if (!BIT(data, 8))
			store_chr = nullptr;
		if (!BIT(data, 9))
			store_ext = nullptr;

		m_ca0 = 0;
		for (uint32_t y = m_cypos[1], bly = m_bylen; bly != 0x1000; bly++, y = (y + 1) & 0xfff)
		{
			if (y >= 256)
				continue;

			const uint32_t line_idx = y * 800;
			for (uint32_t x = m_cxpos[1], blx = m_bxlen; blx != 0x1000; blx++, x = (x + 1) & 0xfff)
			{
				if (x >= 256)
					continue;

				const uint32_t pix_idx = line_idx + x;
				const uint16_t bx = x;//((((blx - m_bxlen) << 3) | (m_bixos & 7)) >> (3 - (m_bif & 3))) & 0xfff;
				const uint16_t by = y;//((((bly - m_bylen) << 3) | (m_biyos & 7)) >> ((3 - (m_bif >> 2)) & 3)) & 0xfff;
				const uint16_t uv_bx = ((x & 1) ? (bx | 1) : (bx & ~1));
				if (store_lum)
					m_brushstore_lum[by * 256 + bx] = store_lum[pix_idx];
				if (store_chr)
					m_brushstore_chr[by * 256 + uv_bx] = store_chr[pix_idx];
				if (store_ext)
					m_brushstore_ext[by * 256 + bx] = store_ext[pix_idx];
			}
		}
		break;
	}
	case 13: // Paste with Stencil I
		LOGMASKED(LOG_COMMANDS, "Unsupported command: Paste with Stencil I\n");
		break;
	case 14: // Paste with Stencil II
		LOGMASKED(LOG_COMMANDS, "Unsupported command: Paste with Stencil II\n");
		break;
	case 15: // Copy to same Framestore (Invert)
		LOGMASKED(LOG_COMMANDS, "Unsupported command: Copy to Framestore (Invert)\n");
		break;
	}
	return true;
}

void dpb7000_state::cpu_ctrlbus_w(uint16_t data)
{
	switch (m_csr)
	{
	case 0: // Brush Address Card, function select
	{
		static const char* const func_names[16] =
		{
			"Live Video",           "Brush Store Read",     "Brush Store Write",        "Framestore Read",
			"Framestore Write",     "Fast Wipe Video",      "Fast Wipe Brush Store",    "Fast Wipe Framestore",
			"Draw",                 "Draw with Stencil I",  "Draw with Stencil II",     "Copy to Framestore",
			"Copy to Brush Store",  "Paste with Stencil I", "Paste with Stencil II",    "Copy to same Framestore (Invert)"
		};
		LOGMASKED(LOG_BRUSH_ADDR, "%s: Brush Address Card, Function Select: %04x\n", machine().describe_context(), data);
		LOGMASKED(LOG_BRUSH_ADDR, "                Function:           %s\n", func_names[(data >> 1) & 0xf]);
		LOGMASKED(LOG_BRUSH_ADDR, "                /S1:                %d\n", BIT(data, 5));
		LOGMASKED(LOG_BRUSH_ADDR, "                /S2:                %d\n", BIT(data, 6));
		LOGMASKED(LOG_BRUSH_ADDR, "                LUMEN:              %d\n", BIT(data, 7));
		LOGMASKED(LOG_BRUSH_ADDR, "                CHREN:              %d\n", BIT(data, 8));
		LOGMASKED(LOG_BRUSH_ADDR, "                KSEL:               %d\n", BIT(data, 9));
		LOGMASKED(LOG_BRUSH_ADDR, "                DISCEN:             %d\n", BIT(data, 10));
		LOGMASKED(LOG_BRUSH_ADDR, "                /KINV:              %d\n", BIT(data, 11));
		LOGMASKED(LOG_BRUSH_ADDR, "                /KZERO:             %d\n", BIT(data, 12));
		LOGMASKED(LOG_BRUSH_ADDR, "                FCS:                %d\n", BIT(data, 13));
		LOGMASKED(LOG_BRUSH_ADDR, "                Go:                 %d\n", BIT(data, 0));
		m_brush_addr_func = data & ~1;
		if (BIT(data, 0))
		{
			m_brush_addr_func |= 0x8000;
			m_brush_addr_cmd = (data >> 1) & 0xf;
			if (handle_command(data)) {
				m_cmd_done_timer->adjust(attotime::from_usec(500));
			}
		}
		break;
	}

	case 1: // Disk Sequencer Card, disk access
	{
		const uint8_t hi_nybble = data >> 12;
		if (hi_nybble == 0)
		{
			m_diskseq_cyl_from_cpu = data & 0x3ff;
			LOGMASKED(LOG_CTRLBUS, "%s: CPU write to Control Bus, Disk Sequencer Card, Cylinder Number: %04x (%04x)\n", machine().describe_context(), m_diskseq_cyl_from_cpu, data);
			if (m_diskseq_cyl_from_cpu == 0)
				m_diskseq_status |= 0x04;
			else
				m_diskseq_status &= ~0x04;
		}
		else if (hi_nybble == 1)
		{
			LOGMASKED(LOG_CTRLBUS, "%s: CPU write to Control Bus, Disc Data Buffer Card, Preset RAM Address: %04x\n", machine().describe_context(), data & 0xfff);
			m_diskbuf_ram_addr = (data & 0xfff) << 3;
		}
		else if (hi_nybble == 2)
		{
			m_diskseq_cmd_word_from_cpu = data & 0xfff;
			m_diskseq_cmd = (data >> 8) & 0xf;
			int group = (int)(data >> 5) & 3;
			LOGMASKED(LOG_CTRLBUS, "%s: CPU write to Control Bus, Disk Sequencer Card, Command: %x (%04x)\n", machine().describe_context(), (data >> 8) & 0xf, data);
			LOGMASKED(LOG_CTRLBUS, "%s                                                    Head: %x\n", machine().describe_context(), data & 0xf);
			LOGMASKED(LOG_CTRLBUS, "%s                                                   Drive: %x\n", machine().describe_context(), (data >> 5) & 7);
			switch (m_diskseq_cmd)
			{
			case 1:
			case 5:
			case 9:
			case 13:
				LOGMASKED(LOG_CTRLBUS, "%s: Disk Sequencer Card Command: No command\n", machine().describe_context());
				break;

			case 0:
				LOGMASKED(LOG_CTRLBUS, "%s: Disk Sequencer Card Command: Read track to buffer RAM\n", machine().describe_context());
				if (!BIT(m_diskseq_status, 3))
				{
					req_b_w(0); // Flag ourselves as in-use
					m_diskseq_cyl_read_pending = true;
					m_diskseq_command_stride = 1;
					if (is_disk_group_fdd(group))
					{
						m_fdd_side = 0;
						seek_fdd_to_cylinder();
					}
					else if (is_disk_group_hdd(group))
					{
						m_diskseq_use_hdd_pending = true;
						m_hdd_command_timer->adjust(attotime::from_double((double)19200 / 1012000));
					}
				}
				break;

			case 2:
				LOGMASKED(LOG_CTRLBUS, "%s: Disk Sequencer Card Command: Read track, stride 2, to buffer RAM\n", machine().describe_context());
				if (!BIT(m_diskseq_status, 3))
				{
					req_b_w(0); // Flag ourselves as in-use
					m_diskseq_cyl_read_pending = true;
					m_diskseq_command_stride = 2;
					if (is_disk_group_fdd(group))
					{
						m_fdd_side = 0;
						seek_fdd_to_cylinder();
					}
					else if (is_disk_group_hdd(group))
					{
						m_diskseq_use_hdd_pending = true;
						m_hdd_command_timer->adjust(attotime::from_double((double)19200 / 1012000));
					}
				}
				break;

			case 3:
				LOGMASKED(LOG_CTRLBUS, "%s: Disk Sequencer Card Command: Restore\n", machine().describe_context());
				m_diskseq_cyl_from_cpu = 0;
				req_b_w(0); // Flag ourselves as in-use
				m_diskseq_complete_clk->adjust(attotime::from_msec(1), 1);
				break;

			case 4:
				LOGMASKED(LOG_CTRLBUS, "%s: Disk Sequencer Card Command: Read Track\n", machine().describe_context());
				if (!BIT(m_diskseq_status, 3))
				{
					req_b_w(0); // Flag ourselves as in-use
					m_line_count = 0;
					m_line_clock = 0;

					m_diskseq_cyl_read_pending = true;
					m_diskseq_command_stride = 1;
					if (is_disk_group_fdd(group))
					{
						m_diskbuf_data_count = 0x2700;
						m_fdd_side = 0;
						seek_fdd_to_cylinder();
					}
					else if (is_disk_group_hdd(group))
					{
						m_diskbuf_data_count = 0x4b00;
						m_diskseq_use_hdd_pending = true;
						m_hdd_command_timer->adjust(attotime::from_double((double)19200 / 1012000));
					}
				}
				break;

			case 6:
				LOGMASKED(LOG_CTRLBUS, "%s: Disk Sequencer Card Command: Disc Clear, Read Track\n", machine().describe_context());
				if (!BIT(m_diskseq_status, 3))
				{
					m_size_dxdx_counter = 0;
					m_size_dydy_counter = 0;
					m_size_dest_x = (m_brush_addr_cmd != 2) ? m_cxpos[1] : 0;
					m_size_dest_y = (m_brush_addr_cmd != 2) ? m_cypos[1] : 0;

					req_b_w(0); // Flag ourselves as in-use
					m_diskseq_cyl_read_pending = true;
					m_diskseq_command_stride = 1;
					if (is_disk_group_fdd(group))
					{
						m_diskbuf_data_count = 0x2700;
						m_fdd_side = 0;
						seek_fdd_to_cylinder();
					}
					else if (is_disk_group_hdd(group))
					{
						m_diskbuf_data_count = 0x4b00;
						m_diskseq_use_hdd_pending = true;
						m_hdd_command_timer->adjust(attotime::from_double((double)19200 / 1012000));
					}
				}
				break;

			case 8:
				LOGMASKED(LOG_CTRLBUS, "%s: Disk Sequencer Card Command: Write Track from Buffer RAM\n", machine().describe_context());
				if (!BIT(m_diskseq_status, 3))
				{
					req_b_w(0); // Flag ourselves as in-use
					m_diskseq_cyl_write_pending = true;
					m_diskseq_command_stride = 1;
					if (is_disk_group_fdd(group))
					{
						m_diskbuf_data_count = 0x2700;
						m_fdd_side = 0;
						seek_fdd_to_cylinder();
					}
					else if (is_disk_group_hdd(group))
					{
						m_diskbuf_data_count = 0x4b00;
						m_diskseq_use_hdd_pending = true;
						m_hdd_command_timer->adjust(attotime::from_double((double)19200 / 1012000));
					}
				}
				break;

			case 10:
				LOGMASKED(LOG_CTRLBUS, "%s: Disk Sequencer Card Command: Write Track, stride 2, from Buffer RAM\n", machine().describe_context());
				if (!BIT(m_diskseq_status, 3))
				{
					req_b_w(0); // Flag ourselves as in-use
					m_diskseq_cyl_write_pending = true;
					m_diskseq_command_stride = 2;
					if (is_disk_group_fdd(group))
					{
						m_diskbuf_data_count = 0x2700;
						m_fdd_side = 0;
						seek_fdd_to_cylinder();
					}
					else if (is_disk_group_hdd(group))
					{
						m_diskbuf_data_count = 0x4b00;
						m_diskseq_use_hdd_pending = true;
						m_hdd_command_timer->adjust(attotime::from_double((double)19200 / 1012000));
					}
				}
				break;

			case 12:
				LOGMASKED(LOG_CTRLBUS, "%s: Disk Sequencer Card Command: Write Track\n", machine().describe_context());
				if (!BIT(m_diskseq_status, 3) && is_disk_group_hdd(group))
				{
					req_b_w(0); // Flag ourselves as in-use

					m_diskseq_cyl_write_pending = true;
					m_diskseq_command_stride = 1;

					m_diskbuf_data_count = 0x4b00;
					m_diskseq_use_hdd_pending = true;
					m_hdd_command_timer->adjust(attotime::from_double((double)19200 / 1012000));
				}
				break;

			case 14:
				LOGMASKED(LOG_CTRLBUS, "%s: Disk Sequencer Card Command: Disc Clear, Write Track\n", machine().describe_context());
				if (!BIT(m_diskseq_status, 3) && is_disk_group_hdd(group))
				{
					req_b_w(0); // Flag ourselves as in-use
					m_line_count = 0;
					m_line_clock = 0;

					m_diskseq_cyl_write_pending = true;
					m_diskseq_command_stride = 1;

					m_diskbuf_data_count = 0x4b00;
					m_diskseq_use_hdd_pending = true;
					m_hdd_command_timer->adjust(attotime::from_double((double)19200 / 1012000));
				}
				break;

			default:
				LOGMASKED(LOG_CTRLBUS, "%s: Unknown Disk Sequencer Card command.\n", machine().describe_context());
				req_b_w(0); // Flag ourselves as in-use
				m_diskseq_complete_clk->adjust(attotime::from_msec(1), 1);
				break;
			}
		}
		else
		{
			LOGMASKED(LOG_CTRLBUS | LOG_UNKNOWN, "%s: CPU write to Control Bus, Disk Sequencer Card, Unrecognized hi nybble: %04x\n", machine().describe_context(), data);
		}
		break;
	}

	case 2: // Store Address Card
		if (BIT(data, 15))
			store_address_w(0, data);
		else
			store_address_w(1, data);
		break;

	case 3: // Size Card, Y6/Y7
		if (BIT(data, 15))
		{
			LOGMASKED(LOG_CTRLBUS | LOG_SIZE_CARD, "%s:    Size Card Y7 (Flags): %04x\n", machine().describe_context(), data & 0x7fff);
		}
		else
		{
			switch (data & 0x3300)
			{
			case 0x1000: // Change in source X per change in dest X, MSB
				LOGMASKED(LOG_CTRLBUS | LOG_SIZE_CARD, "%s:    Size Card delta source X per delta dest X, MSB: %02x (%04x)\n", machine().describe_context(), data & 0xff, data);
				m_size_dsx_ddx &= 0x00ff;
				m_size_dsx_ddx |= (data & 0x00ff) << 8;
				break;
			case 0x0000: // Change in source X per change in dest X, LSB
				LOGMASKED(LOG_CTRLBUS | LOG_SIZE_CARD, "%s:    Size Card delta source X per delta dest X, LSB: %02x (%04x)\n", machine().describe_context(), data & 0xff, data);
				m_size_dsx_ddx &= 0xff00;
				m_size_dsx_ddx |= data & 0x00ff;
				break;
			case 0x3000: // Change in source Y per change in dest X, MSB
				LOGMASKED(LOG_CTRLBUS | LOG_SIZE_CARD, "%s:    Size Card delta source Y per delta dest X, MSB: %02x (%04x)\n", machine().describe_context(), data & 0xff, data);
				m_size_dsy_ddx &= 0x00ff;
				m_size_dsy_ddx |= (data & 0x00ff) << 8;
				break;
			case 0x2000: // Change in source Y per change in dest X, LSB
				LOGMASKED(LOG_CTRLBUS | LOG_SIZE_CARD, "%s:    Size Card delta source Y per delta dest X, LSB: %02x (%04x)\n", machine().describe_context(), data & 0xff, data);
				m_size_dsy_ddx &= 0xff00;
				m_size_dsy_ddx |= data & 0x00ff;
				break;
			case 0x1100: // Change in source X per change in dest Y, MSB
				LOGMASKED(LOG_CTRLBUS | LOG_SIZE_CARD, "%s:    Size Card delta source X per delta dest Y, MSB: %02x (%04x)\n", machine().describe_context(), data & 0xff, data);
				m_size_dsx_ddy &= 0x00ff;
				m_size_dsx_ddy |= (data & 0x00ff) << 8;
				break;
			case 0x0100: // Change in source X per change in dest Y, LSB
				LOGMASKED(LOG_CTRLBUS | LOG_SIZE_CARD, "%s:    Size Card delta source X per delta dest Y, LSB: %02x (%04x)\n", machine().describe_context(), data & 0xff, data);
				m_size_dsx_ddy &= 0xff00;
				m_size_dsx_ddy |= data & 0x00ff;
				break;
			case 0x3100: // Change in source Y per change in dest Y, MSB
				LOGMASKED(LOG_CTRLBUS | LOG_SIZE_CARD, "%s:    Size Card delta source Y per delta dest Y, MSB: %02x (%04x)\n", machine().describe_context(), data & 0xff, data);
				m_size_dsy_ddy &= 0x00ff;
				m_size_dsy_ddy |= (data & 0x00ff) << 8;
				break;
			case 0x2100: // Change in source Y per change in dest Y, LSB
				LOGMASKED(LOG_CTRLBUS | LOG_SIZE_CARD, "%s:    Size Card delta source Y per delta dest Y, LSB: %02x (%04x)\n", machine().describe_context(), data & 0xff, data);
				m_size_dsy_ddy &= 0xff00;
				m_size_dsy_ddy |= data & 0x00ff;
				break;
			case 0x1300: // Origin, source X / dest X
				LOGMASKED(LOG_CTRLBUS | LOG_SIZE_CARD, "%s:    Size Card origin, source X / dest X: %02x (%04x)\n", machine().describe_context(), data & 0xff, data);
				m_size_frac_origin_sx_dx = data & 0x00ff;
				break;
			case 0x0300: // Origin, source X / dest Y
				LOGMASKED(LOG_CTRLBUS | LOG_SIZE_CARD, "%s:    Size Card origin, source X / dest Y: %02x (%04x)\n", machine().describe_context(), data & 0xff, data);
				m_size_frac_origin_sx_dy = data & 0x00ff;
				break;
			case 0x3300: // Origin, source Y / dest X
				LOGMASKED(LOG_CTRLBUS | LOG_SIZE_CARD, "%s:    Size Card origin, source Y / dest X: %02x (%04x)\n", machine().describe_context(), data & 0xff, data);
				m_size_frac_origin_sy_dx = data & 0x00ff;
				break;
			case 0x2300: // Origin, source Y / dest Y
				LOGMASKED(LOG_CTRLBUS | LOG_SIZE_CARD, "%s:    Size Card origin, source Y / dest Y: %02x (%04x)\n", machine().describe_context(), data & 0xff, data);
				m_size_frac_origin_sy_dy = data & 0x00ff;
				break;
			}
		}
		break;

	case 5: // Filter Card
		if (BIT(data, 15))
		{
			static const char *const coeff_names[8] = { "Y-3", "Y-2", "Y-1", "Y (LSB)", "Y (MSB)", "Y+1", "Y+2", "Y+3" };
			const uint16_t coeff_idx = ((data >> 8) & 0xf) - 2;
			switch ((data >> 8) & 0xf)
			{
			case 2: // Y-3
			case 3: // Y-2
			case 4: // Y-1
			case 5: // Y (LSB)
			case 6: // Y (MSB)
			case 7: // Y+1
			case 8: // Y+2
			case 9: // Y+3
				LOGMASKED(LOG_CTRLBUS | LOG_FILTER_CARD, "%s: Filter Card %s Coefficient Write: %02x\n", machine().describe_context(), coeff_names[coeff_idx], data & 0xff);
				m_filter_y_coeffs[coeff_idx] = data & 0xff;
				break;
			default: // Ignored
				LOGMASKED(LOG_CTRLBUS | LOG_FILTER_CARD, "%s: Filter Card Coefficient (ignored Y) Write: %04x\n", machine().describe_context(), data);
				break;
			}
		}
		else
		{
			static const char *const coeff_names[12] = { "X-5", "X-4", "X-3", "X-2", "X-1", "X (LSB)", "X (MSB)", "X+1", "X+2", "X+3", "X+4", "X+5" };
			const uint16_t coeff_idx = ((data >> 8) & 0xf) - 1;
			switch ((data >> 8) & 0xf)
			{
			case 1: // X-5
			case 2: // X-4
			case 3: // X-3
			case 4: // X-2
			case 5: // X-1
			case 6: // X (LSB)
			case 7: // X (MSB)
			case 8: // X+1
			case 9: // X+2
			case 10: // X+3
			case 11: // X+4
			case 12: // X+5
				LOGMASKED(LOG_CTRLBUS | LOG_FILTER_CARD, "%s: Filter Card %s Coefficient Write: %02x\n", machine().describe_context(), coeff_names[coeff_idx], data & 0xff);
				m_filter_x_coeffs[coeff_idx] = data & 0xff;
				break;
			default: // Ignored
				LOGMASKED(LOG_CTRLBUS | LOG_FILTER_CARD, "%s: Filter Card Coefficient (ignored X) Write: %04x\n", machine().describe_context(), data);
				break;
			}
		}
		break;

	case 7: // Disk Data Buffer RAM
		LOGMASKED(LOG_DDB, "%s: Disc Data Buffer Card RAM write: %04x = %04x\n", machine().describe_context(), m_diskbuf_ram_addr, data);
		m_diskbuf_ram[m_diskbuf_ram_addr++] = (uint8_t)data;
		break;

	case 8: // Brush Store Card color latches
		m_bs_y_latch = (uint8_t)data;
		if (m_ca0)
		{
			LOGMASKED(LOG_CTRLBUS | LOG_BRUSH_ADDR | LOG_BRUSH_LATCH, "%s: Brush Store Card color latches (VY): %04x\n\n", machine().describe_context(), data);
			m_bs_v_latch = (uint8_t)(data >> 8);
		}
		else
		{
			LOGMASKED(LOG_CTRLBUS | LOG_BRUSH_ADDR | LOG_BRUSH_LATCH, "%s: Brush Store Card color latches (UY): %04x\n", machine().describe_context(), data);
			m_bs_u_latch = (uint8_t)(data >> 8);
		}
		if (m_brush_addr_cmd == 4) // Framestore Write
		{
			const uint8_t chr = m_ca0 ? m_bs_v_latch : m_bs_u_latch;
			if (!BIT(m_brush_addr_func, 5))
			{
				const uint16_t x = m_cxpos[0] + (m_bxlen_counter - m_bxlen);
				const uint16_t y = m_cypos[0] + (m_bylen_counter - m_bylen);
				uint32_t pix_idx = y * 800 + x;

				if (BIT(m_brush_addr_func, 7))
				{
					LOGMASKED(LOG_CTRLBUS | LOG_BRUSH_ADDR | LOG_BRUSH_LATCH, "%s: Writing %02x to Store 1 luma at %d,%d\n", machine().describe_context(), m_bs_y_latch, x, y);
					m_framestore_lum[0][pix_idx] = m_bs_y_latch;
				}
				if (BIT(m_brush_addr_func, 8))
				{
					LOGMASKED(LOG_CTRLBUS | LOG_BRUSH_ADDR | LOG_BRUSH_LATCH, "%s: Writing %02x to Store 1 chroma at %d,%d\n", machine().describe_context(), chr, x, y);
					m_framestore_chr[0][pix_idx] = chr;
				}
				if (BIT(m_brush_addr_func, 9))
				{
					LOGMASKED(LOG_CTRLBUS | LOG_BRUSH_ADDR | LOG_BRUSH_LATCH, "%s: Writing %02x to Store 1 stencil at %d,%d\n", machine().describe_context(), m_bs_y_latch, x, y);
					m_framestore_ext[0][pix_idx] = m_bs_y_latch;
				}
			}
			if (!BIT(m_brush_addr_func, 6))
			{
				const uint16_t x = m_cxpos[1] + (m_bxlen_counter - m_bxlen);
				const uint16_t y = m_cypos[1] + (m_bylen_counter - m_bylen);
				uint32_t pix_idx = y * 800 + x;

				if (BIT(m_brush_addr_func, 7))
				{
					LOGMASKED(LOG_CTRLBUS | LOG_BRUSH_ADDR | LOG_BRUSH_LATCH, "%s: Writing %02x to Store 2 luma at %d,%d\n", machine().describe_context(), m_bs_y_latch, x, y);
					m_framestore_lum[1][pix_idx] = m_bs_y_latch;
				}
				if (BIT(m_brush_addr_func, 8))
				{
					LOGMASKED(LOG_CTRLBUS | LOG_BRUSH_ADDR | LOG_BRUSH_LATCH, "%s: Writing %02x to Store 2 chroma at %d,%d\n", machine().describe_context(), chr, x, y);
					m_framestore_chr[1][pix_idx] = chr;
				}
				if (BIT(m_brush_addr_func, 9))
				{
					LOGMASKED(LOG_CTRLBUS | LOG_BRUSH_ADDR | LOG_BRUSH_LATCH, "%s: Writing %02x to Store 2 stencil at %d,%d\n", machine().describe_context(), m_bs_y_latch, x, y);
					m_framestore_ext[1][pix_idx] = m_bs_y_latch;
				}
			}

			m_bxlen_counter++;
			if (m_bxlen_counter == 0x1000)
			{
				m_bxlen_counter = m_bxlen;
				m_bylen_counter++;
				if (m_bylen_counter == 0x1000)
				{
					cmd_done(0);
				}
			}
		}
		m_ca0 = 1 - m_ca0;
		break;

	case 9: // Brush Address Card, register write/select
		switch (data >> 12)
		{
		case 1:
			LOGMASKED(LOG_CTRLBUS | LOG_BRUSH_ADDR, "%s: Brush Address Card, Register Write: BIF = %02x\n", machine().describe_context(), data & 0xff);
			m_bif = data & 0xff;
			break;
		case 2:
			LOGMASKED(LOG_CTRLBUS | LOG_BRUSH_ADDR, "%s: Brush Address Card, Register Write: BIXOS = %d\n", machine().describe_context(), data & 0x7);
			m_bixos = data & 0x7;
			break;
		case 3:
			LOGMASKED(LOG_CTRLBUS | LOG_BRUSH_ADDR, "%s: Brush Address Card, Register Write: BIYOS = %d\n", machine().describe_context(), data & 0x7);
			m_biyos = data & 0x7;
			break;
		case 4:
			LOGMASKED(LOG_CTRLBUS | LOG_BRUSH_ADDR, "%s: Brush Address Card, Register Write: BXLEN = %03x\n", machine().describe_context(), data & 0xfff);
			m_bxlen = data & 0xfff;
			m_bxlen_counter = data & 0xfff;
			break;
		case 5:
			LOGMASKED(LOG_CTRLBUS | LOG_BRUSH_ADDR, "%s: Brush Address Card, Register Write: BYLEN = %03x\n", machine().describe_context(), data & 0xfff);
			m_bylen = data & 0xfff;
			m_bylen_counter = data & 0xfff;
			break;
		case 6:
			LOGMASKED(LOG_CTRLBUS | LOG_BRUSH_ADDR | LOG_BRUSH_LATCH, "%s: Brush Address Card, Register Write: PLUM = %03x\n", machine().describe_context(), data & 0xff);
			m_brush_press_lum = data & 0xff;
			break;
		case 7:
			LOGMASKED(LOG_CTRLBUS | LOG_BRUSH_ADDR | LOG_BRUSH_LATCH, "%s: Brush Address Card, Register Write: PCHR = %03x\n", machine().describe_context(), data & 0xff);
			m_brush_press_chr = data & 0xff;
			break;
		default:
			LOGMASKED(LOG_CTRLBUS | LOG_BRUSH_ADDR, "%s: Brush Address Card, Register Write: Unknown (%04x)\n", machine().describe_context(), data);
			break;
		}
		break;

	case 10: // Output Timing Card - cursor registers; Combiner Card
	{
		const uint8_t hi_bits = (data >> 14) & 3;
		if (hi_bits == 0) // Cursor Parameters
		{
			const uint8_t reg = (data >> 12) & 3;
			switch (reg)
			{
			case 0: // Cursor X Origin
				LOGMASKED(LOG_CTRLBUS | LOG_OUTPUT_TIMING, "%s: CPU write to Output Timing Card: Cursor Origin X = %03x\n", machine().describe_context(), data & 0xfff);
				m_cursor_origin_x = data & 0xfff;
				break;
			case 1: // Cursor Y Origin
				LOGMASKED(LOG_CTRLBUS | LOG_OUTPUT_TIMING, "%s: CPU write to Output Timing Card: Cursor Origin Y = %03x\n", machine().describe_context(), data & 0xfff);
				m_cursor_origin_y = data & 0xfff;
				break;
			case 2: // Cursor X Size
				LOGMASKED(LOG_CTRLBUS | LOG_OUTPUT_TIMING, "%s: CPU write to Output Timing Card: Cursor Size X = %03x\n", machine().describe_context(), data & 0xfff);
				m_cursor_size_x = data & 0xfff;
				break;
			case 3: // Cursor Y Size
				LOGMASKED(LOG_CTRLBUS | LOG_OUTPUT_TIMING, "%s: CPU write to Output Timing Card: Cursor Size Y = %03x\n", machine().describe_context(), data & 0xfff);
				m_cursor_size_y = data & 0xfff;
				break;
			}
		}
		else if (hi_bits == 3) // Combiner Card, constant registers
		{
			combiner_reg_w(data);
		}
		else
		{
			LOGMASKED(LOG_CTRLBUS | LOG_OUTPUT_TIMING | LOG_UNKNOWN, "%s: CPU write to Output Timing Card, unknown select value: %04x\n", machine().describe_context(), data);
		}
		break;
	}

	case 15: // Disk Sequencer Card, panic reset
		LOGMASKED(LOG_CTRLBUS, "%s: CPU write to Control Bus, Disk Sequencer Card, panic reset\n", machine().describe_context());
		break;

	default:
		LOGMASKED(LOG_CTRLBUS | LOG_UNKNOWN, "%s: CPU write to Control Bus, unknown CSR %d: %04x\n", machine().describe_context(), m_csr, data);
		break;
	}
}

TIMER_CALLBACK_MEMBER(dpb7000_state::req_a_w)
{
	//if (machine().input().code_pressed(KEYCODE_LALT))
	{
		if (param)
		{
			m_sys_ctrl |= SYSCTRL_REQ_A_IN;
		}
		else
		{
			m_sys_ctrl &= ~SYSCTRL_REQ_A_IN;
		}

		update_req_irqs();
	}
	/*else
	{
	    m_sys_ctrl &= ~SYSCTRL_REQ_A_IN;
	    update_req_irqs();
	}*/
}

TIMER_CALLBACK_MEMBER(dpb7000_state::req_b_w)
{
	if (param)
	{
		m_sys_ctrl |= SYSCTRL_REQ_B_IN;
	}
	else
	{
		m_sys_ctrl &= ~SYSCTRL_REQ_B_IN;
	}

	update_req_irqs();
}

TIMER_CALLBACK_MEMBER(dpb7000_state::cmd_done)
{
	m_brush_addr_func &= ~0x8000;
}

TIMER_CALLBACK_MEMBER(dpb7000_state::execute_hdd_command)
{
	constexpr int SECTORS_PER_TRACK = 20480 / 256;
	int group = (int)(m_diskseq_cmd_word_from_cpu >> 5) & 3;
	int head_count = get_heads_for_disk_group(group);
	int head_index = m_diskseq_cmd_word_from_cpu & 0xf;
	int image_lba = SECTORS_PER_TRACK * head_count * (int)m_diskseq_cyl_from_cpu + SECTORS_PER_TRACK * head_index;

	if (m_hdd->exists())
	{
		if (m_diskseq_cyl_write_pending)
		{
			unsigned char sector_buffer[256];
			int start_sector = m_diskbuf_ram_addr >> 8;
			uint16_t ram_addr = m_diskbuf_ram_addr;
			if (m_diskseq_command_stride != 1)
			{
				for (int sector = start_sector; sector < 19200 / 256; sector++, image_lba++)
				{
					m_hdd->read(image_lba, sector_buffer);
					for (int stride_idx = 0; stride_idx < 256; stride_idx += 2)
					{
						sector_buffer[stride_idx] = m_diskbuf_ram[ram_addr];
						ram_addr += 2;
					}
					LOGMASKED(LOG_HDD, "Performing write to LBA %d: Cylinder %03x, head %x, command word %03x, Stride 2 (RAM address %04x, offset %04x)\n", image_lba, m_diskseq_cyl_from_cpu, head_index, m_diskseq_cmd_word_from_cpu, m_diskbuf_ram_addr, sector * 256);
					m_hdd->write(image_lba, sector_buffer);
				}
			}
			else
			{
				if (m_diskseq_cmd == 12 || m_diskseq_cmd == 14)
				{
					for (int sector = 0; sector < 19200 / 256; sector++, image_lba++)
					{
						LOGMASKED(LOG_HDD, "Performing write to LBA %d: Cylinder %03x, head %x, command word %03x\n", image_lba, m_diskseq_cyl_from_cpu, head_index, m_diskseq_cmd_word_from_cpu);
						for (int i = 0; i < 256; i += 2)
						{
							sector_buffer[i + 0] = process_byte_to_disc();
							sector_buffer[i + 1] = process_byte_to_disc();
						}
						m_hdd->write(image_lba, sector_buffer);
					}
				}
				else
				{
					for (int sector = start_sector; sector < 19200 / 256; sector++, image_lba++)
					{
						LOGMASKED(LOG_HDD, "Performing write to LBA %d: Cylinder %03x, head %x, command word %03x (RAM address %04x, offset %04x)\n", image_lba, m_diskseq_cyl_from_cpu, head_index, m_diskseq_cmd_word_from_cpu, m_diskbuf_ram_addr, sector * 256);
						m_hdd->write(image_lba, m_diskbuf_ram + sector * 256);
					}
				}
			}
			m_diskseq_cyl_write_pending = false;
		}
		else if (m_diskseq_cyl_read_pending)
		{
			unsigned char sector_buffer[256];
			if (m_diskseq_command_stride != 1)
			{
				for (int sector = 0; sector < 19200 / 256; sector++, image_lba++)
				{
					LOGMASKED(LOG_HDD, "Performing read of LBA %d: Cylinder %03x, head %x, command word %03x\n", image_lba, m_diskseq_cyl_from_cpu, head_index, m_diskseq_cmd_word_from_cpu);
					m_hdd->read(image_lba, sector_buffer);
					for (int clear_idx = 0; clear_idx < 256; clear_idx += 2)
					{
						sector_buffer[clear_idx] = 0;
					}
					if (m_diskseq_cmd == 4 || m_diskseq_cmd == 6)
					{
						for (int i = 0; i < 256; i++)
						{
							process_byte_from_disc(sector_buffer[i]);
						}
					}
					else
					{
						memcpy(m_diskbuf_ram + sector * 256, sector_buffer, 256);
					}
				}
			}
			else
			{
				int start_sector = BIT(m_diskseq_cmd, 2) ? 0 : (m_diskbuf_ram_addr >> 8);
				int partial_bytes = m_diskbuf_ram_addr & 0x00ff;
				if (partial_bytes && !BIT(m_diskseq_cmd, 2))
				{
					LOGMASKED(LOG_HDD, "Performing partial read of sector into disk buffer address %04x\n", m_diskbuf_ram_addr);
					m_hdd->read(image_lba, sector_buffer);
					memcpy(m_diskbuf_ram + m_diskbuf_ram_addr, sector_buffer + partial_bytes, 0x100 - partial_bytes);
					m_diskbuf_ram_addr += 0x100;
					m_diskbuf_ram_addr &= 0xff00;
					image_lba += start_sector;
				}

				for (int sector = start_sector; sector < 19200 / 256; sector++, image_lba++)
				{
					LOGMASKED(LOG_HDD, "Performing read of LBA %d: Cylinder %03x, head %x, command word %03x\n", image_lba, m_diskseq_cyl_from_cpu, head_index, m_diskseq_cmd_word_from_cpu);
					if (BIT(m_diskseq_cmd, 2))
					{
						m_hdd->read(image_lba, sector_buffer);
						for (int i = 0; i < 256; i++)
						{
							process_byte_from_disc(sector_buffer[i]);
						}
					}
					else
					{
						m_hdd->read(image_lba, m_diskbuf_ram + sector * 256);
					}
				}
			}
			m_diskseq_cyl_read_pending = false;
		}
		m_diskseq_command_stride = 2;
	}

	m_diskseq_use_hdd_pending = false;
	req_b_w(1);
}

uint16_t dpb7000_state::cpu_sysctrl_r()
{
	const uint16_t ctrl = m_sys_ctrl &~ SYSCTRL_AUTO_START;
	const uint16_t auto_start = m_auto_start->read() ? SYSCTRL_AUTO_START : 0;
	const uint16_t ret = ctrl | auto_start;
	//LOGMASKED(LOG_SYS_CTRL, "%s: CPU read from System Control: %04x\n", machine().describe_context(), ret);
	return ret;
}

void dpb7000_state::cpu_sysctrl_w(uint16_t data)
{
	const uint16_t mask = (SYSCTRL_REQ_A_EN | SYSCTRL_REQ_B_EN);
	LOGMASKED(LOG_IRQ, "%s: CPU to Control Bus write: %04x\n", machine().describe_context(), data);
	m_sys_ctrl &= ~mask;
	m_sys_ctrl |= (data & mask);

	update_req_irqs();
}

void dpb7000_state::update_req_irqs()
{
	const bool take_irq_a = (m_sys_ctrl & SYSCTRL_REQ_A_IN) && (m_sys_ctrl & SYSCTRL_REQ_A_EN);
	const bool take_irq_b = (m_sys_ctrl & SYSCTRL_REQ_B_IN) && (m_sys_ctrl & SYSCTRL_REQ_B_EN);
	if (take_irq_a)
	{
		LOGMASKED(LOG_IRQ, "Flagging to take IRQ A\n");
	}
	m_maincpu->set_input_line(5, take_irq_a ? ASSERT_LINE : CLEAR_LINE);
	if (take_irq_b)
	{
		LOGMASKED(LOG_IRQ, "Flagging to take IRQ B\n");
	}
	m_maincpu->set_input_line(4, take_irq_b ? ASSERT_LINE : CLEAR_LINE);
}

uint8_t dpb7000_state::fdd_ctrl_r()
{
	// D4: Command Tag Flag
	// D5: Restore Flag
	// D6: Cylinder Tag Flag
	// D7: Debug Switch
	const uint8_t ret = m_fdd_ctrl;
	m_fdd_ctrl &= ~0x70;
	LOGMASKED(LOG_FDC_CTRL, "%s: Floppy CPU Ctrl Read: %02x\n", machine().describe_context(), ret);
	return ret;
}

void dpb7000_state::advance_line_count()
{
	m_line_length = 0;
	m_line_count++;
	toggle_line_clock();
}

void dpb7000_state::toggle_line_clock()
{
	m_line_clock ^= 1;
}

void dpb7000_state::process_sample()
{
	const uint16_t x = m_size_dest_x;
	const uint16_t y = m_size_dest_y;

	//m_size_dsx_ddx = 0x100;
	//m_size_dsy_ddy = 0x100;

	//if (m_size_dsx_ddx != 0x100) printf("Processing sample %d,%d (%04x:%04x, %04x:%04x) LC:%d dxdx:%06x, dydy:%06x\n", x, y, m_bxlen_counter, m_bxlen, m_bylen_counter, m_bylen, m_line_count, m_size_dxdx_counter, m_size_dydy_counter);
	bool process_sample = m_size_dxdx_counter <= 0 && m_size_dydy_counter <= 0;
	if (process_sample)
	{
		switch (m_brush_addr_cmd)
		{
			case 2: // Brush Store Write
				LOGMASKED(LOG_BRUSH_WRITES, "Processing %02x,%02x into Brush Store L/C/E at %d, %d\n", m_incoming_lum, m_incoming_chr, x, y);
				if (BIT(m_brush_addr_func, 7))
					m_brushstore_lum[y * 256 + x] = m_incoming_lum;
				if (BIT(m_brush_addr_func, 8))
					m_brushstore_chr[y * 256 + x] = m_incoming_chr;
				if (BIT(m_brush_addr_func, 9))
					m_brushstore_ext[y * 256 + x] = m_incoming_lum;
				break;

			case 4: // Framestore Write
			{
				uint32_t pix_idx = y * 800 + x;
				if (!BIT(m_brush_addr_func, 5))
				{
					if (BIT(m_brush_addr_func, 7))
						m_framestore_lum[0][pix_idx] = m_incoming_lum;
					if (BIT(m_brush_addr_func, 8))
						m_framestore_chr[0][pix_idx] = m_incoming_chr;
					if (BIT(m_brush_addr_func, 9))
						m_framestore_ext[0][pix_idx] = m_incoming_lum;
				}
				if (!BIT(m_brush_addr_func, 6))
				{
					if (BIT(m_brush_addr_func, 7))
						m_framestore_lum[1][pix_idx] = m_incoming_lum;
					if (BIT(m_brush_addr_func, 8))
						m_framestore_chr[1][pix_idx] = m_incoming_chr;
					if (BIT(m_brush_addr_func, 9))
						m_framestore_ext[1][pix_idx] = m_incoming_lum;
				}
				break;
			}
		}
	}

	if (m_size_dxdx_counter <= 0)
	{
		m_size_dxdx_counter += 0x100;//(int16_t)(m_size_dsx_ddx & 0x7fff);
		m_size_dest_x++;
	}
	m_size_dxdx_counter -= 0x100;

	m_bxlen_counter++;
	if (m_bxlen_counter == 0x1000)
	{
		//if (m_size_dsx_ddx != 0x100) printf("Advancing to next line due to bxlen counter elapsing\n");
		m_bxlen_counter = m_bxlen;
		m_bylen_counter++;

		if (m_size_dydy_counter <= 0)
		{
			m_size_dydy_counter += 0x100;//(int16_t)(m_size_dsy_ddy & 0x7fff);
			m_size_dest_y++;
			//if (m_size_dsx_ddx != 0x100) printf("dydy counter is <= 0, resetting dydy counter to %04x, dxdx counter to 0, dest X to %d, dest Y to %d\n", m_size_dydy_counter, m_size_dest_x, m_size_dest_y);
		}
		m_size_dydy_counter -= 0x100;

		m_size_dxdx_counter = 0;
		m_size_dest_x = (m_brush_addr_cmd != 2) ? m_cxpos[1] : 0;

		//if (m_size_dsx_ddx != 0x100) printf("At end of line, dydy counter is now %04x\n", m_size_dydy_counter);

		if (m_bylen_counter == 0x1000)
		{
			m_diskseq_cyl_read_pending = false;
			cmd_done(0);
		}
	}
}

uint8_t dpb7000_state::process_byte_to_disc()
{
	if (!m_diskseq_cyl_write_pending)
		return 0;

	uint8_t lum = 0;
	uint8_t chr = 0;
	switch (m_brush_addr_cmd)
	{
		case 3: // Framestore Read
		{
			if (!BIT(m_brush_addr_func, 5))
			{
				const uint16_t x = m_cxpos[0] + (m_bxlen_counter - m_bxlen);
				const uint16_t y = m_cypos[0] + (m_bylen_counter - m_bylen);
				uint32_t pix_idx = y * 800 + x;

				if (BIT(m_brush_addr_func, 9))
				{
					lum = m_framestore_ext[0][pix_idx];
					LOGMASKED(LOG_STORE_READS, "Reading %02x from Store 1 Stencil at %d, %d to disk [%03x %03x]\n", lum, x, y, m_bxlen_counter, m_bylen_counter);
				}
				if (BIT(m_brush_addr_func, 8))
				{
					chr = m_framestore_chr[0][pix_idx];
					LOGMASKED(LOG_STORE_READS, "Reading %02x from Store 1 Chroma at %d, %d to disk [%03x %03x]\n", chr, x, y, m_bxlen_counter, m_bylen_counter);
				}
				if (BIT(m_brush_addr_func, 7))
				{
					lum = m_framestore_lum[0][pix_idx];
					LOGMASKED(LOG_STORE_READS, "Reading %02x from Store 1 Luma at %d, %d to disk [%03x %03x]\n", lum, x, y, m_bxlen_counter, m_bylen_counter);
				}
			}

			if (!BIT(m_brush_addr_func, 6))
			{
				const uint16_t x = m_cxpos[1] + (m_bxlen_counter - m_bxlen);
				const uint16_t y = m_cypos[1] + (m_bylen_counter - m_bylen);
				uint32_t pix_idx = y * 800 + x;

				if (BIT(m_brush_addr_func, 9))
				{
					lum = m_framestore_ext[1][pix_idx];
					LOGMASKED(LOG_STORE_READS, "Reading %02x from Store 2 Stencil at %d, %d to disk [%03x %03x]\n", lum, x, y, m_bxlen_counter, m_bylen_counter);
				}
				if (BIT(m_brush_addr_func, 8))
				{
					chr = m_framestore_chr[1][pix_idx];
					LOGMASKED(LOG_STORE_READS, "Reading %02x from Store 2 Chroma at %d, %d to disk [%03x %03x]\n", chr, x, y, m_bxlen_counter, m_bylen_counter);
				}
				if (BIT(m_brush_addr_func, 7))
				{
					lum = m_framestore_lum[1][pix_idx];
					LOGMASKED(LOG_STORE_READS, "Reading %02x from Store 2 Luma at %d, %d to disk [%03x %03x]\n", lum, x, y, m_bxlen_counter, m_bylen_counter);
				}
			}
			break;
		}
	}

	const uint8_t ret = m_buffer_lum ? lum : chr;

	m_buffer_lum = !m_buffer_lum;
	if (m_buffer_lum)
	{
		m_line_length++;
		if (m_line_length == 0x300)
		{
			advance_line_count();
		}

		m_bxlen_counter++;
		if (m_bxlen_counter == 0x1000)
		{
			m_bxlen_counter = m_bxlen;
			m_bylen_counter++;
			if (m_bylen_counter == 0x1000)
			{
				if (m_brush_addr_cmd == 3)
				{
					LOGMASKED(LOG_STORE_READS, "Done with Store Read command\n");
				}
				m_diskseq_cyl_write_pending = false;
				cmd_done(0);
			}
		}
	}
	return ret;
}

void dpb7000_state::process_byte_from_disc(uint8_t data_byte)
{
	if (!m_diskseq_cyl_read_pending)
		return;

	if (m_buffer_lum)
	{
		m_incoming_lum = data_byte;
	}
	else
	{
		m_incoming_chr = data_byte;
		process_sample();
	}

	m_buffer_lum = !m_buffer_lum;
	m_line_length++;
	if (m_line_length == 0x300)
	{
		advance_line_count();
	}
}

bool dpb7000_state::is_disk_group_fdd(int group_index)
{
	constexpr uint16_t GROUP_SHIFTS[3] = { DGROUP_0_SHIFT, DGROUP_1_SHIFT, DGROUP_2_SHIFT };
	uint16_t val = (m_config_sw34->read() >> GROUP_SHIFTS[group_index]) & 7;
	return val == DGROUP_TYPE_FLOPPY;
}

bool dpb7000_state::is_disk_group_hdd(int group_index)
{
	constexpr uint16_t GROUP_SHIFTS[3] = { DGROUP_0_SHIFT, DGROUP_1_SHIFT, DGROUP_2_SHIFT };
	uint16_t val = (m_config_sw34->read() >> GROUP_SHIFTS[group_index]) & 7;
	return val == DGROUP_TYPE_80MB_CDC || val == DGROUP_TYPE_160MB_FUJITSU || val == DGROUP_TYPE_330MB_FUJITSU;
}

int dpb7000_state::get_heads_for_disk_group(int group_index)
{
	constexpr uint16_t GROUP_SHIFTS[3] = { DGROUP_0_SHIFT, DGROUP_1_SHIFT, DGROUP_2_SHIFT };
	uint16_t val = (m_config_sw34->read() >> GROUP_SHIFTS[group_index]) & 7;
	switch (val)
	{
		case DGROUP_TYPE_FLOPPY:
			return 1;
		case DGROUP_TYPE_80MB_CDC:
			return 5;
		case DGROUP_TYPE_160MB_FUJITSU:
			return 10;
		case DGROUP_TYPE_330MB_FUJITSU:
			return 16;
		default:
			return 0;
	}
}

void dpb7000_state::fdd_index_callback(floppy_image_device *floppy, int state)
{
	if (m_diskseq_use_hdd_pending)
		return;

	if (!state && m_diskseq_cyl_read_pending && m_floppy && m_fdd_side < 2)
	{
		LOGMASKED(LOG_COMMANDS, "Reading cylinder from floppy\n");
		m_fdd_pll.read_reset(machine().time());

		constexpr uint16_t PREGAP_MARK = 0xaaaa;
		constexpr uint16_t SYNC_MARK = 0x9125;

		m_floppy->ss_w(m_fdd_side);

		bool seen_pregap = false;
		bool in_track = false;
		int curr_bit = -1;
		uint16_t curr_window = 0;
		uint16_t bit_idx = 0;

		attotime tm = machine().time();
		attotime limit = machine().time() + attotime::from_ticks(1, 6); // One revolution at 360rpm on a Shugart SA850
		do
		{
			curr_bit = m_fdd_pll.get_next_bit(tm, m_floppy, limit);
			if (curr_bit < 0)
			{
				LOGMASKED(LOG_FDC_MECH, "Warning: Unable to retrieve full track %d side %d, curr_bit returned -1\n", m_floppy->get_cyl(), m_fdd_side);
			}
			else
			{
				curr_window <<= 1;
				curr_window |= curr_bit;
				bit_idx++;

				if (!seen_pregap && curr_window == PREGAP_MARK)
				{
					seen_pregap = true;
					bit_idx = 0;
					curr_window = 0;
				}
				else if (seen_pregap && !in_track && curr_window == SYNC_MARK)
				{
					in_track = true;
					bit_idx = 0;
					curr_window = 0;
				}
				else if (seen_pregap && in_track && bit_idx == 16)
				{
					uint8_t data_byte = (uint8_t)bitswap<16>((uint16_t)curr_window, 15, 13, 11, 9, 7, 5, 3, 1, 14, 12, 10, 8, 6, 4, 2, 0);
					switch (m_diskseq_cmd)
					{
					case 4: // Read Track
					case 6: // Disc Clear, Read Track
						m_diskbuf_data_count--;
						process_byte_from_disc(data_byte);
						if (!m_diskseq_cyl_read_pending)
						{
							curr_bit = -1;
							m_fdd_side = 2;
						}
						else if (m_fdd_side == 0 && m_diskbuf_data_count == 0)
						{
							curr_bit = -1;
							m_floppy->ss_w(1);
							m_fdd_side++;
							m_diskbuf_data_count = 0x2400;
						}
						else if (m_fdd_side == 1 && m_diskbuf_data_count == 0)
						{
							curr_bit = -1;
							m_fdd_side++;
						}
						break;
					case 0: // Read Track to Buffer RAM
					case 2: // Read Track, stride 2, to Buffer RAM
						if (BIT(m_diskseq_cmd, 1))
						{
							if (!BIT(m_diskbuf_ram_addr, 0))
							{
								m_diskbuf_ram[m_diskbuf_ram_addr >> 1] = data_byte;
							}
							m_diskbuf_ram_addr++;
						}
						else
						{
							m_diskbuf_ram[m_diskbuf_ram_addr] = data_byte;
							m_diskbuf_ram_addr++;
						}

						if (m_diskbuf_ram_addr >= 0x2700 && m_fdd_side == 0)
						{
							// If we've read the side 0 portion of the cylinder, yield out and wait for the next index pulse
							curr_bit = -1;
							m_floppy->ss_w(1);
							m_fdd_side++;
						}
						else if(m_diskbuf_ram_addr >= 0x4b00 && m_fdd_side == 1)
						{
							// If we've read the side 1 portion of the cylinder, yield out, we're done
							curr_bit = -1;
							m_fdd_side++;
						}
						break;
					}
					bit_idx = 0;
					curr_window = 0;
				}
			}
		} while (curr_bit != -1);
	}

	if (m_fdd_side == 2)
	{
		m_diskseq_cyl_read_pending = false;
		req_b_w(1);
	}
}

void dpb7000_state::seek_fdd_to_cylinder()
{
	uint16_t floppy_cyl = (uint16_t)m_floppy->get_cyl();
	if (floppy_cyl != m_diskseq_cyl_from_cpu && m_diskseq_cyl_from_cpu < 78 && m_floppy != nullptr)
	{
		if (m_diskseq_cyl_from_cpu < floppy_cyl)
		{
			m_floppy->dir_w(1);
			for (uint16_t i = m_diskseq_cyl_from_cpu; i < floppy_cyl; i++)
			{
				m_floppy->stp_w(1);
				m_floppy->stp_w(0);
				m_floppy->stp_w(1);
			}
		}
		else
		{
			m_floppy->dir_w(0);
			for (uint16_t i = floppy_cyl; i < m_diskseq_cyl_from_cpu; i++)
			{
				m_floppy->stp_w(1);
				m_floppy->stp_w(0);
				m_floppy->stp_w(1);
			}
		}
		LOGMASKED(LOG_FDD, "%s: New floppy cylinder: %04x\n", machine().describe_context(), floppy_cyl);
	}
}

void dpb7000_state::fddcpu_p1_w(uint8_t data)
{
	LOGMASKED(LOG_FDC_PORT, "%s: Floppy CPU Port 1 Write: %02x\n", machine().describe_context(), data);
	const uint8_t old_value = m_fdd_port1;
	m_fdd_port1 = data;

	floppy_image_device *newflop = m_floppy0->get_device();
	if (newflop != m_floppy)
	{
		if (m_floppy)
		{
			m_floppy->mon_w(1);
			m_floppy->setup_index_pulse_cb(floppy_image_device::index_pulse_cb());
		}
		if (newflop)
		{
			newflop->set_rpm(360);
			newflop->mon_w(BIT(old_value, 2));
			newflop->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&dpb7000_state::fdd_index_callback, this));
			m_fdd_track = newflop->get_cyl();
		}
		m_floppy = newflop;
	}

	if (m_floppy)
	{
		m_floppy->mon_w(BIT(m_fdd_port1, 2));
		m_floppy->dir_w(1 - BIT(m_fdd_port1, 1));
		m_floppy->stp_w(1 - BIT(m_fdd_port1, 0));
		m_fdd_track = m_floppy->get_cyl();

		if (m_fdd_track == 0)
		{
			m_fdd_ctrl |= 0x04; // On Cylinder
		}
		else
		{
			m_fdd_ctrl &= ~0x04;
		}
	}
	else
	{
		m_fdd_ctrl &= ~0x04;
	}

	// C5 D READY
	if (BIT(m_fdd_port1, 7))
	{
		m_diskseq_status &= ~(1 << 3);
		m_diskseq_status |= 0x04;
	}
	else
	{
		m_diskseq_status |= (1 << 3);
	}
}

void dpb7000_state::fddcpu_p2_w(uint8_t data)
{
	m_fdd_serial->write_txd(BIT(data, 4));
}

uint8_t dpb7000_state::fddcpu_p2_r()
{
	uint8_t ret = 0;
	if (m_fdd_debug_rx_byte_count)
	{
		ret |= m_fdd_debug_rx_bits[0] << 3;
		m_fdd_debug_rx_bits.pop_front();

		m_fdd_debug_rx_recv_count++;
		if (m_fdd_debug_rx_recv_count == 10)
		{
			m_fdd_debug_rx_recv_count = 0;
			m_fdd_debug_rx_byte_count--;
		}
	}
	else
	{
		ret |= 1 << 3;
	}
	return ret;
}

uint8_t dpb7000_state::fdd_cmd_r()
{
	LOGMASKED(LOG_FDC_CMD, "%s: Floppy CPU command read: %02x\n", m_diskseq_cmd_to_ctrl);
	return m_diskseq_cmd_to_ctrl;
}

void dpb7000_state::fddcpu_debug_rx(int state)
{
	if (m_fdd_debug_rx_bit_count < 10)
	{
		m_fdd_debug_rx_bits.push_back(state);
		m_fdd_debug_rx_bit_count++;
	}
	else
	{
		m_fdd_debug_rx_bit_count = 0;
		m_fdd_debug_rx_byte_count++;
	}
}

uint8_t dpb7000_state::keyboard_p1_r()
{
	LOGMASKED(LOG_KEYBC, "%s: Port 1 read\n", machine().describe_context());
	return 0;
}

uint8_t dpb7000_state::keyboard_p2_r()
{
	LOGMASKED(LOG_KEYBC, "%s: Port 2 read\n", machine().describe_context());
	return 0;
}

void dpb7000_state::keyboard_p1_w(uint8_t data)
{
	LOGMASKED(LOG_KEYBC, "%s: Port 1 write: %02x\n", machine().describe_context(), data);
	const uint8_t old_data = m_keybc_p1_data;
	m_keybc_p1_data = data;
	const uint8_t col = data & 0x0f;
	const uint8_t lowered = old_data & ~data;
	if (BIT(lowered, 7))
	{
		m_keybc_latched_bit = BIT(m_keybc_cols[col]->read(), (data >> 4) & 7);
	}
	else
	{
		m_keybc_latched_bit = 1;
	}
}

void dpb7000_state::keyboard_p2_w(uint8_t data)
{
	LOGMASKED(LOG_KEYBC, "%s: Port 2 write: %02x\n", machine().describe_context(), data);
	m_keybc_tx = BIT(~data, 7);
}

int dpb7000_state::keyboard_t0_r()
{
	LOGMASKED(LOG_KEYBC, "%s: T0 read\n", machine().describe_context());
	return 0;
}

int dpb7000_state::keyboard_t1_r()
{
	uint8_t data = m_keybc_latched_bit;
	//m_keybc_latched_bit = 0;
	LOGMASKED(LOG_KEYBC, "%s: T1 read: %d\n", machine().describe_context(), data);
	return data;
}

void dpb7000_state::tds_dac_w(uint8_t data)
{
	m_tds_dac_value = ~data;
	uint16_t offset = 0x67 * m_tds_dac_value;
	m_tds_dac_offset = (uint8_t)(offset >> 8);
	m_tds_dac_percent = std::clamp(m_tds_dac_value / 255.0f, 0.0f, 1.0f);
	LOGMASKED(LOG_TDS, "%s: TDS DAC Write: %02x (%f)\n", machine().describe_context(), data, m_tds_dac_percent);
}

TIMER_CALLBACK_MEMBER(dpb7000_state::tds_press_tick)
{
	m_tds_in_proximity = !m_pen_prox->read();

	switch (m_tds_press_state)
	{
	case STATE_IDLE:
		if (m_tds_in_proximity)
		{
			m_tds_press_state = STATE_PRESS_IN;
			m_tds_pressure_shift = 6;
		}
		break;
	case STATE_PRESS_IN:
		if (!m_tds_in_proximity)
			m_tds_press_state = STATE_PRESS_OUT;
		else if (m_tds_pressure_shift > 0)
			m_tds_pressure_shift--;
		else
			m_tds_press_state = STATE_PRESSED;
		break;
	case STATE_PRESSED:
		if (!m_tds_in_proximity)
			m_tds_press_state = STATE_PRESS_OUT;
		break;
	case STATE_PRESS_OUT:
		if (m_tds_in_proximity)
			m_tds_press_state = STATE_PRESS_IN;
		else if (m_tds_pressure_shift < 6)
			m_tds_pressure_shift++;
		else
			m_tds_press_state = STATE_IDLE;
		break;
	}
}

TIMER_CALLBACK_MEMBER(dpb7000_state::tds_adc_tick)
{
	m_tds_adc_busy = false;

	uint16_t value = m_tds_dac_offset;
	if (m_tds_press_state != STATE_IDLE)
	{
		value += (m_pen_press->read() >> m_tds_pressure_shift);
	}
	m_tds_adc_value = (value >= 0xf8 ? 0xf7 : (uint8_t)value);
}

void dpb7000_state::tds_convert_w(uint8_t data)
{
	LOGMASKED(LOG_TDS, "%s: TDS ADC Convert Start: %02x\n", machine().describe_context(), data);

	m_tds_adc_busy = true;
	m_tds_adc_value = 0x80;
	m_tds_adc_timer->adjust(attotime::from_ticks(8, 120000));
}

uint8_t dpb7000_state::tds_adc_r()
{
	LOGMASKED(LOG_TDS, "%s: TDS ADC Read: %02x (DAC value %02x)\n", machine().describe_context(), m_tds_adc_value, m_tds_dac_value);
	return m_tds_adc_value;
}

uint8_t dpb7000_state::tds_pen_switches_r()
{
	uint8_t data = 0;//m_pen_switches->read() << 4;
	LOGMASKED(LOG_TDS, "%s: TDS Pen Switches Read: %02x\n", machine().describe_context(), data);
	return data;
}

TIMER_CALLBACK_MEMBER(dpb7000_state::tablet_tx_tick)
{
	//m_tds_duart->rx_b_w(m_tablet_tx_bit);
	if (!m_tablet_hle_tx_bits.empty())
	{
		m_tds_duart->rx_b_w(m_tablet_hle_tx_bits[0]);
		m_tablet_hle_tx_bits.pop_front();
	}
	else
	{
		m_tds_duart->rx_b_w(1);
	}
}

void dpb7000_state::duart_b_w(int state)
{
	//printf("B%d ", state);
}

uint8_t dpb7000_state::tds_p1_r()
{
	const uint8_t latched = m_tds_p1_data & 0x80;
	const uint8_t adc_not_busy = m_tds_adc_busy ? 0x00 : 0x20;
	uint8_t data = m_tds_dips->read() | adc_not_busy | latched;
	LOGMASKED(LOG_TDS, "%s: TDS Port 1 Read, %02x\n", machine().describe_context(), data);
	return data;
}

uint8_t dpb7000_state::tds_p2_r()
{
	uint8_t data = 0x02 | (m_keybc_tx << 3);
	LOGMASKED(LOG_TDS, "%s: TDS Port 2 Read, %02x\n", machine().describe_context(), data);
	return data;
}

uint8_t dpb7000_state::tds_p3_r()
{
	LOGMASKED(LOG_TDS, "%s: TDS Port 3 Read, %02x\n", machine().describe_context(), 0);
	return 0;
}

uint8_t dpb7000_state::tds_p4_r()
{
	LOGMASKED(LOG_TDS, "%s: TDS Port 4 Read, %02x\n", machine().describe_context(), 0);
	return 0;
}

void dpb7000_state::tds_p1_w(uint8_t data)
{
	LOGMASKED(LOG_TDS, "%s: TDS Port 1 Write = %02x\n", machine().describe_context(), data);
	const uint8_t old = m_tds_p1_data;
	m_tds_p1_data = data;
	if (BIT(old & ~data, 0))
	{
		m_tds_duart->reset();
		m_tablet_tx_timer->adjust(attotime::from_hz(9600), 0, attotime::from_hz(9600));
	}
}

void dpb7000_state::tds_p2_w(uint8_t data)
{
	LOGMASKED(LOG_TDS, "%s: TDS Port 2 Write = %02x\n", machine().describe_context(), data);
}

/*INPUT_CHANGED_MEMBER(dpb7000_state::pen_prox_changed)
{
    m_tds_in_proximity = newval ? false : true;
    if (m_tds_in_proximity)
    {
        LOGMASKED(LOG_TABLET, "Triggering IRQ due to proximity\n");
        m_tablet_cpu->set_input_line(INPUT_LINE_IRQ1, ASSERT_LINE);
    }
}*/

TIMER_CALLBACK_MEMBER(dpb7000_state::tablet_hle_tick)
{
	if (m_tds_press_state != STATE_IDLE || (m_pen_switches->read() == 1))
	{
		m_tablet_pen_x = m_pen_x->read();
		m_tablet_pen_y = 4799 - m_pen_y->read();
		m_tablet_pen_switches = m_pen_switches->read();
		uint8_t data[5] =
		{
			(uint8_t)(0x40 | (BIT(m_tablet_pen_switches, 1) << 2) | (BIT(m_tablet_pen_y, 12) << 1) | BIT(m_tablet_pen_x, 12)),
			(uint8_t)(m_tablet_pen_x & 0x003f),
			(uint8_t)((m_tablet_pen_x & 0x0fc0) >> 6),
			(uint8_t)(m_tablet_pen_y & 0x003f),
			(uint8_t)((m_tablet_pen_y & 0x0fc0) >> 6)
		};

		for (int i = 0; i < 5; i++)
		{
			data[i] |= (population_count_32(data[i]) & 1) ? 0x80 : 0x00;
			m_tablet_hle_tx_bits.push_back(0);
			for (int bit = 0; bit < 8; bit++)
			{
				m_tablet_hle_tx_bits.push_back(BIT(data[i], bit));
			}
			m_tablet_hle_tx_bits.push_back(1);
		}
	}
}

uint8_t dpb7000_state::tablet_p2_r(offs_t offset, uint8_t mem_mask)
{
	const uint8_t dip_bit = BIT(m_tablet_dip_shifter, 15) << 7;
	const uint8_t led_bit = (BIT(~m_tablet_p3_data, 5) << 6);
	const uint8_t pen_prox = m_pen_prox->read() << 5;
	uint8_t data = dip_bit | led_bit | pen_prox | (m_tablet_p2_data & 0x1f);

	LOGMASKED(LOG_TABLET, "%s: Tablet Port 2 Read = %02x & %02x\n", machine().describe_context(), data, mem_mask);
	return data;
}

void dpb7000_state::tablet_p2_w(offs_t offset, uint8_t data, uint8_t mem_mask)
{
	LOGMASKED(LOG_TABLET, "%s: Tablet Port 2 Write: %02x & %02x\n", machine().describe_context(), data, mem_mask);

	m_tablet_p2_data = data;

	if (mem_mask & 0x06)
	{
		m_tablet_mux = (data & 0x06) >> 1;
	}

	if (BIT(mem_mask, 4))
	{
		uint8_t old_drq = m_tablet_drq;
		m_tablet_drq = BIT(data, 4);
		LOGMASKED(LOG_TABLET, "Tablet DRQ: %d\n", m_tablet_drq);
		LOGMASKED(LOG_TABLET, "Clearing tablet CPU IRQ\n");
		m_tablet_cpu->set_input_line(INPUT_LINE_IRQ1, CLEAR_LINE);
		if (old_drq && !m_tablet_drq && m_tds_in_proximity) // DRQ transition to low
		{
			if (m_tablet_state == 0) // We're idle
			{
				m_tablet_state = 1; // We're reading
				m_tablet_cpu->set_input_line(INPUT_LINE_IRQ1, ASSERT_LINE);
				LOGMASKED(LOG_TABLET, "Triggering IRQ for read (initial)\n");
			}
		}
		if (!old_drq && m_tablet_drq) // DRQ transition back to high
		{
			if (m_tablet_state == 1)
			{
				m_tablet_irq_timer->adjust(attotime::from_ticks(1, 1200000), 1);
				LOGMASKED(LOG_TABLET, "Setting up IRQ timer for read (continuous)\n");
			}
		}
	}
}

TIMER_CALLBACK_MEMBER(dpb7000_state::tablet_irq_tick)
{
	LOGMASKED(LOG_TABLET, "Triggering tablet CPU IRQ\n");
	m_tablet_cpu->set_input_line(INPUT_LINE_IRQ1, ASSERT_LINE);
	m_tablet_cpu->set_input_line(INPUT_LINE_IRQ1, CLEAR_LINE);
}

uint8_t dpb7000_state::tablet_p3_r(offs_t offset, uint8_t mem_mask)
{
	uint8_t data = m_tablet_p3_data;
	LOGMASKED(LOG_TABLET, "%s: Tablet Port 3 Read = %02x & %02x\n", machine().describe_context(), data, mem_mask);
	return data;
}

void dpb7000_state::tablet_p3_w(offs_t offset, uint8_t data, uint8_t mem_mask)
{
	LOGMASKED(LOG_TABLET, "%s: Tablet Port 3 Write: %02x & %02x\n", machine().describe_context(), data, mem_mask);

	if (BIT(mem_mask, 7))
	{
		m_tablet_tx_bit = BIT(data, 7);
	}

	const uint8_t old = m_tablet_p3_data;
	m_tablet_p3_data = data;

	const uint8_t lowered = old & ~data;
	const uint8_t raised = ~old & data;

	if (BIT(lowered, 5))
	{
		// HACK: We hard-code the DIPs to the config from a known tablet setup, as the meaning of the individual
		// DIP switches is undocumented.
		m_tablet_dip_shifter = 0x6e1e; // (m_tablet_dips[0]->read() << 8) | m_tablet_dips[1]->read();
	}

	if (BIT(raised, 6))
	{
		m_tablet_dip_shifter <<= 1;
	}
}

uint8_t dpb7000_state::tablet_rdh_r()
{
	uint8_t data = (m_tablet_counter_latch >> 8);
	LOGMASKED(LOG_TABLET, "%s: Tablet RDH Read (Mux %d): %02x\n", machine().describe_context(), m_tablet_mux, data);
	return data;
}

uint8_t dpb7000_state::tablet_rdl_r()
{
	//if (m_tablet_mux == 3)
		//m_tablet_state = 0;

	m_tablet_counter_latch = machine().rand() & 0xfff;
	LOGMASKED(LOG_TABLET, "%s: Random latch: %04x\n", machine().describe_context(), m_tablet_counter_latch);
	uint8_t data = (uint8_t)m_tablet_counter_latch;
	LOGMASKED(LOG_TABLET, "%s: Tablet RDL Read (Mux %d): %02x\n", machine().describe_context(), m_tablet_mux, data);
	return data;
}

uint32_t dpb7000_state::combined_screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const uint16_t upper_flag_addr = (uint16_t)((m_output_cpflags & 3) << 8);

	// TODO: Implement the Store Address Card in a more accurate manner, and implement framestore scan-out and flag handling accordingly.
	int32_t y_addr[2] = { m_rvscr[0], m_rvscr[1] };
	int32_t dest_y = 0;
	uint8_t matte_chr[2][2] = { { m_matte_u[0], m_matte_v[0] }, { m_matte_u[1], m_matte_v[1] } };
	bool seen_rvr = false;
	bool started_scanout = false;
	for (int32_t y = 0; y < 625; y++)
	{
		const uint8_t vlines = m_output_vlines[y + 0x18f] & 0x0f;

		const uint8_t hlines = m_output_hlines[0xa0 + 70] & 0x0f;
		const uint16_t flag_addr = (hlines & 0x0f) | ((vlines & 0x0f) << 4);
		const uint8_t hflags = m_output_hflags[flag_addr | upper_flag_addr] & 0x0f;
		const bool palette = BIT(hflags, 3);

		if (!BIT(hflags, 1))
		{
			seen_rvr = true;
			if (palette)
			{
				y_addr[0] = 0;
				y_addr[1] = 0;
			}
			else
			{
				y_addr[0] = m_rvscr[0];
				y_addr[1] = m_rvscr[1];
			}
		}
		else if (!started_scanout && seen_rvr)
		{
			started_scanout = true;
			dest_y = 0;
		}

		// TODO: Enabling the blanking PAL causes strangeness when bringing up either the menu or the palette.
		// Most likely scenario is that assuming a horizontal coordinate of 0 coming into the PAL puts us in a blanking region.
		//const bool blank_1_q = BIT(m_storeaddr_pal_blank[((y_addr[0] << 3) & 0x1800) | ((y_addr[0] << 2) & 0x3f0)], 0);
		//const bool blank_2_q = BIT(m_storeaddr_pal_blank[((y_addr[1] << 3) & 0x1800) | ((y_addr[1] << 2) & 0x3f0)], 0);
		const bool blank_1_q = true;
		const bool blank_2_q = true;
		const bool use_store1_matte = !palette && (!blank_1_q || m_select_matte[0]);
		const bool use_store2_matte = !palette && (!blank_2_q || m_select_matte[1]);
		const bool use_ext_store1_matte = !(blank_1_q && !BIT(m_ext_store_flags, 0));
		const bool use_ext_store2_matte = !(blank_2_q && !BIT(m_ext_store_flags, 1));
		const bool invert_a = palette ? BIT(~m_ext_store_flags, 3) : BIT(m_ext_store_flags, 2);
		const bool invert_b = !invert_a;
		const uint8_t ext_src_invert = BIT(m_ext_store_flags, 4) ? 0xff : 0x00;
		const uint8_t ext_a_mask = (invert_a ? 0xff : 0x00);
		const uint8_t ext_b_mask = (invert_b ? 0xff : 0x00);

		const int32_t y_line1 = y_addr[0] * 800;
		const int32_t y_line2 = y_addr[1] * 800;

		uint32_t *d = &bitmap.pix(dest_y);
		int32_t x_addr[2] = { m_rhscr[0] & ~1, m_rhscr[1] & ~1 };
		for (int32_t x = 0; x < 800; x++, x_addr[0] = (x_addr[0] + 1) % 800, x_addr[1] = (x_addr[1] + 1) % 800)
		{
			const uint8_t uv_sel1 = x_addr[0] & 1;
			const uint8_t uv_sel2 = x_addr[1] & 1;

			const uint32_t pix_idx1 = y_line1 + x_addr[0];
			const uint32_t pix_idx2 = y_line2 + x_addr[1];

			const uint16_t lum1 = use_store1_matte ? m_matte_y[0] : m_framestore_lum[0][pix_idx1];
			const uint16_t chr1 = use_store1_matte ? matte_chr[0][uv_sel1] : m_framestore_chr[0][pix_idx1];
			const uint16_t ext1 = use_ext_store1_matte ? m_matte_ext[0] : (m_framestore_ext[0][pix_idx1] ^ ext_src_invert);

			const uint16_t lum2 = use_store2_matte ? m_matte_y[1] : m_framestore_lum[1][pix_idx2];
			const uint16_t chr2 = use_store2_matte ? matte_chr[0][uv_sel2] : m_framestore_chr[1][pix_idx2];
			const uint16_t ext2 = use_ext_store2_matte ? m_matte_ext[1] : (m_framestore_ext[1][pix_idx2] ^ ext_src_invert);

			const uint8_t ext_product = (uint8_t)(((ext1 * ext2) + 0x0080) >> 8);
			const uint8_t ext_a = ext_product ^ ext_a_mask;
			const uint8_t ext_b = ext_product ^ ext_b_mask;

			const uint16_t lum1_product = ((lum1 * ext_a) + 0x0080) >> 8;
			const uint16_t lum2_product = ((lum2 * ext_b) + 0x0080) >> 8;
			const uint16_t chr1_product = ((chr1 * ext_a) + 0x0080) >> 8;
			const uint16_t chr2_product = ((chr2 * ext_b) + 0x0080) >> 8;

			const uint8_t lum_sum = (uint8_t)std::min<uint16_t>(lum1_product + lum2_product, 255);
			const uint8_t chr_sum = (uint8_t)std::min<uint16_t>(chr1_product + chr2_product, 255);

			*d++ = 0xff000000 | (lum_sum << 8) | chr_sum;
		}

		if (BIT(hflags, 1))
		{
			dest_y++;

			y_addr[0]++;
			if (y_addr[0] == 4096)
				y_addr[0] = 0;

			y_addr[1]++;
			if (y_addr[1] == 4096)
				y_addr[1] = 0;
		}
	}

	const uint32_t cursor_rgb = m_yuv_lut[(m_cursor_u << 16) | (m_cursor_v << 8) | m_cursor_y];
	uint16_t cursor_origin_y_counter = m_cursor_origin_y;
	uint16_t cursor_y_size = m_cursor_size_y;
	uint16_t cursor_y_index = 0;

	for (int32_t dst_y = 0; dst_y < 625; dst_y++)
	{
		uint16_t cursor_origin_x_counter = m_cursor_origin_x;
		uint16_t cursor_x_size = m_cursor_size_x;
		uint16_t cursor_x_index = 0;

		uint32_t *d = &bitmap.pix(dst_y);
		for (int32_t x = 0; x < 800; x += 2)
		{
			const uint32_t u = (d[x] << 16) & 0xff0000;
			const uint32_t v = (d[x | 1] << 8) & 0x00ff00;
			const uint32_t y0 = (d[x] >> 8) & 0x0000ff;
			const uint32_t y1 = (d[x | 1] >> 8) & 0x0000ff;

			uint32_t combined[2] = { m_yuv_lut[u | v | y0], m_yuv_lut[u | v | y1] };
			for (int32_t h = 0; h < 2; h++)
			{
				if (m_output_csflags)
				{
					const bool cursor_in_window = (cursor_origin_x_counter == 0x1000 && cursor_origin_y_counter == 0x1000);
					const bool cursor_enable = cursor_in_window && (cursor_x_index < 0x20 && cursor_y_index < 0x20);
					if (cursor_enable)
					{
						const uint16_t cursor_addr_x = cursor_x_index ^ 0x10;
						const uint16_t cursor_addr_y = cursor_y_index ^ 0x10;
						const uint16_t cursor_addr = (cursor_addr_y << 5) | cursor_addr_x;

						const uint8_t cursor_data = m_output_cursor[cursor_addr];

						bool cursor_bit = true;
						bool cursor_colored = true;
						switch (m_output_csflags)
						{
						case 1:
							cursor_bit = BIT(cursor_data, 0);
							cursor_colored = BIT(cursor_data, 1);
							break;
						case 2:
							cursor_bit = BIT(cursor_data, 2);
							cursor_colored = BIT(cursor_data, 3);
							break;
						case 3:
							cursor_bit = ((cursor_addr_x & cursor_addr_y) & 0x10) ? 0 : 1;
							break;
						default:
							// Cursor inactive
							break;
						}

						if (!cursor_bit)
						{
							combined[h] = cursor_colored ? cursor_rgb : 0xff000000;
						}
					}

					if (cursor_origin_x_counter < 0x1000)
					{
						cursor_origin_x_counter++;
					}
					else if (cursor_x_size < 0x1000)
					{
						cursor_x_size++;
						cursor_x_index++;
					}
					else if (cursor_x_index < 0x20)
					{
						cursor_x_index++;
					}
				}

				d[x + h] = combined[h];
			}
		}

		if (m_output_csflags)
		{
			if (cursor_origin_y_counter < 0x1000)
			{
				cursor_origin_y_counter++;
			}
			else if (cursor_y_size < 0x1000)
			{
				cursor_y_size++;
				cursor_y_index++;
			}
			else if (cursor_y_index < 0x20)
			{
				cursor_y_index++;
			}
		}
	}

	return 0;
}

template <int StoreNum>
uint32_t dpb7000_state::store_debug_screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if (0)
	{
		for (int py = 0; py < 768; py++)
		{
			const uint8_t *src_lum = &m_framestore_lum[StoreNum][py * 800];
			const uint8_t *src_chr = &m_framestore_chr[StoreNum][py * 800];
			uint32_t *dst = &bitmap.pix(py);
			for (int px = 0; px < 800; px += 2)
			{
				const uint32_t u = *src_chr++ << 16;
				const uint32_t v = *src_chr++ << 8;
				*dst++ = m_yuv_lut[u | v | *src_lum++];
				*dst++ = m_yuv_lut[u | v | *src_lum++];
			}
		}
	}
	return 0;
}

uint32_t dpb7000_state::stencil_debug_screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if (0)
	{
		for (int py = 0; py < 768; py++)
		{
			const uint8_t *src_ext1 = &m_framestore_ext[0][py * 800];
			const uint8_t *src_ext2 = &m_framestore_ext[1][py * 800];
			uint32_t *dst = &bitmap.pix(py);
			for (int px = 0; px < 800; px++, src_ext1++, src_ext2++)
			{
				const uint16_t h = (uint8_t)px >> 4;
				const uint16_t pal_blank_addr = ((h << 12) & 0xe000) | ((py << 3) & 0x1800) | (BIT(h, 0) << 10) | ((py << 2) & 0x3f0) | ((h >> 4) & 0xf);
				const uint8_t blank_val = m_storeaddr_pal_blank[pal_blank_addr];
				const uint32_t blank_color = ((blank_val & 3) != 3) ? 0x000000ff : 0;
				*dst++ = 0xff000000 | (*src_ext1 << 16) | (*src_ext2 << 8) | blank_color;
			}
		}
	}
	return 0;
}

uint32_t dpb7000_state::brush_debug_screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if (0)
	{
		for (int y = 0; y < 256; y++)
		{
			const uint8_t *src_lum = &m_brushstore_lum[y * 256];
			const uint8_t *src_chr = &m_brushstore_chr[y * 256];
			const uint8_t *src_ext = &m_brushstore_ext[y * 256];
			uint32_t *dst_lc = &bitmap.pix(y);
			uint32_t *dst_ext = &bitmap.pix(y + 256);
			for (int x = 0; x < 256; x++)
			{
				dst_lc[x] = (0xff << 24) | (src_lum[x] << 16) | (src_lum[x] << 8) | src_lum[x];
				dst_lc[x + 256] = (0xff << 24) | (src_chr[x] << 16) | (src_chr[x] << 8) | src_chr[x];
				dst_ext[x] = (0xff << 24) | (src_ext[x] << 16) | (src_ext[x] << 8) | src_ext[x];
			}
		}
	}
	return 0;
}

static void dpb7000_floppies(device_slot_interface &device)
{
	device.option_add("8", FLOPPY_8_DSDD);
}

void dpb7000_state::dpb7000(machine_config &config)
{
	// Computer Card 1 & 2
	M68000(config, m_maincpu, 16_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &dpb7000_state::main_map);

	INPUT_MERGER_ANY_HIGH(config, m_p_int).output_handler().set_inputline(m_maincpu, 3);

	ACIA6850(config, m_acia[0], 0);
	m_acia[0]->txd_handler().set(m_rs232[0], FUNC(rs232_port_device::write_txd));
	m_acia[0]->rts_handler().set(m_rs232[0], FUNC(rs232_port_device::write_rts));
	m_acia[0]->irq_handler().set_inputline(m_maincpu, 6);

	ACIA6850(config, m_acia[1], 0);
	m_acia[1]->txd_handler().set(m_tds_duart, FUNC(scn2681_device::rx_a_w));
	m_acia[1]->irq_handler().set(m_p_int, FUNC(input_merger_device::in_w<0>));

	ACIA6850(config, m_acia[2], 0);
	m_acia[2]->txd_handler().set(m_rs232[1], FUNC(rs232_port_device::write_txd));
	m_acia[2]->rts_handler().set(m_rs232[1], FUNC(rs232_port_device::write_rts));
	m_acia[2]->irq_handler().set(m_p_int, FUNC(input_merger_device::in_w<1>));

	RS232_PORT(config, m_rs232[0], default_rs232_devices, nullptr);
	m_rs232[0]->rxd_handler().set(m_acia[0], FUNC(acia6850_device::write_rxd));
	m_rs232[0]->dsr_handler().set(m_acia[0], FUNC(acia6850_device::write_dcd));
	m_rs232[0]->cts_handler().set(m_acia[0], FUNC(acia6850_device::write_cts));

	RS232_PORT(config, m_rs232[1], default_rs232_devices, nullptr);
	m_rs232[1]->rxd_handler().set(m_acia[2], FUNC(acia6850_device::write_rxd));
	m_rs232[1]->dcd_handler().set(m_acia[2], FUNC(acia6850_device::write_dcd));
	m_rs232[1]->cts_handler().set(m_acia[2], FUNC(acia6850_device::write_cts));

	COM8116(config, m_brg, 5.0688_MHz_XTAL);   // K1355A/B
	m_brg->ft_handler().set(m_acia[0], FUNC(acia6850_device::write_txc));
	m_brg->ft_handler().append(m_acia[0], FUNC(acia6850_device::write_rxc));
	m_brg->ft_handler().append(m_acia[1], FUNC(acia6850_device::write_txc));
	m_brg->ft_handler().append(m_acia[1], FUNC(acia6850_device::write_rxc));
	m_brg->ft_handler().append(m_acia[2], FUNC(acia6850_device::write_txc));
	m_brg->ft_handler().append(m_acia[2], FUNC(acia6850_device::write_rxc));

	screen_device &combined_screen(SCREEN(config, "combined_screen", SCREEN_TYPE_RASTER));
	combined_screen.set_refresh_hz(50);
	combined_screen.set_size(800, 625);
	combined_screen.set_visarea(0, 709, 0, 574);
	combined_screen.set_screen_update(FUNC(dpb7000_state::combined_screen_update));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_size(696, 276);
	screen.set_visarea(56, 695, 36, 275);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	if (0)
	{
		screen_device &store_screen1(SCREEN(config, "store_screen1", SCREEN_TYPE_RASTER));
		store_screen1.set_refresh_hz(50);
		store_screen1.set_size(800, 768);
		store_screen1.set_visarea(0, 799, 0, 767);
		store_screen1.set_screen_update(FUNC(dpb7000_state::store_debug_screen_update<0>));

		screen_device &store_screen2(SCREEN(config, "store_screen2", SCREEN_TYPE_RASTER));
		store_screen2.set_refresh_hz(50);
		store_screen2.set_size(800, 768);
		store_screen2.set_visarea(0, 799, 0, 767);
		store_screen2.set_screen_update(FUNC(dpb7000_state::store_debug_screen_update<1>));

		screen_device &ext_screen(SCREEN(config, "ext_screen", SCREEN_TYPE_RASTER));
		ext_screen.set_refresh_hz(50);
		ext_screen.set_size(800, 768);
		ext_screen.set_visarea(0, 799, 0, 767);
		ext_screen.set_screen_update(FUNC(dpb7000_state::stencil_debug_screen_update));

		screen_device &brush_screen(SCREEN(config, "brush_screen", SCREEN_TYPE_RASTER));
		brush_screen.set_refresh_hz(50);
		brush_screen.set_size(512, 512);
		brush_screen.set_visarea(0, 511, 0, 511);
		brush_screen.set_screen_update(FUNC(dpb7000_state::brush_debug_screen_update));
	}

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	// The 6545's clock is driven by the QD output of a 4-bit binary counter, which has its preset wired to 0010.
	// It therefore operates as a divide-by-six counter for the master clock of 22.248MHz.
	SY6545_1(config, m_crtc, 22.248_MHz_XTAL / 6);
	m_crtc->set_char_width(8);
	m_crtc->set_show_border_area(false);
	m_crtc->set_screen("screen");
	m_crtc->set_update_row_callback(FUNC(dpb7000_state::crtc_update_row));
	m_crtc->set_on_update_addr_change_callback(FUNC(dpb7000_state::crtc_addr_changed));

	// Floppy Disc Unit
	M6803(config, m_fddcpu, 4.9152_MHz_XTAL);
	m_fddcpu->set_addrmap(AS_PROGRAM, &dpb7000_state::fddcpu_map);
	m_fddcpu->out_p1_cb().set(FUNC(dpb7000_state::fddcpu_p1_w));
	m_fddcpu->in_p2_cb().set(FUNC(dpb7000_state::fddcpu_p2_r));
	m_fddcpu->out_p2_cb().set(FUNC(dpb7000_state::fddcpu_p2_w));

	FLOPPY_CONNECTOR(config, m_floppy0, dpb7000_floppies, "8", floppy_image_device::default_mfm_floppy_formats);

	RS232_PORT(config, m_fdd_serial, default_rs232_devices, nullptr);
	m_fdd_serial->rxd_handler().set(FUNC(dpb7000_state::fddcpu_debug_rx));

	config.set_perfect_quantum(m_fddcpu);

	// Hard Disk
	HARDDISK(config, "hdd", 0);

	// Keyboard
	I8039(config, m_keybcpu, 4.608_MHz_XTAL);
	m_keybcpu->set_addrmap(AS_PROGRAM, &dpb7000_state::keybcpu_map);
	m_keybcpu->p1_in_cb().set(FUNC(dpb7000_state::keyboard_p1_r));
	m_keybcpu->p2_in_cb().set(FUNC(dpb7000_state::keyboard_p2_r));
	m_keybcpu->p1_out_cb().set(FUNC(dpb7000_state::keyboard_p1_w));
	m_keybcpu->p2_out_cb().set(FUNC(dpb7000_state::keyboard_p2_w));
	m_keybcpu->t0_in_cb().set(FUNC(dpb7000_state::keyboard_t0_r));
	m_keybcpu->t1_in_cb().set(FUNC(dpb7000_state::keyboard_t1_r));

	// TDS Box
	M6803(config, m_tds_cpu, 4.9152_MHz_XTAL);
	m_tds_cpu->set_addrmap(AS_PROGRAM, &dpb7000_state::tds_cpu_map);
	m_tds_cpu->in_p1_cb().set(FUNC(dpb7000_state::tds_p1_r));
	m_tds_cpu->out_p1_cb().set(FUNC(dpb7000_state::tds_p1_w));
	m_tds_cpu->in_p2_cb().set(FUNC(dpb7000_state::tds_p2_r));
	m_tds_cpu->out_p2_cb().set(FUNC(dpb7000_state::tds_p2_w));
	m_tds_cpu->in_p3_cb().set(FUNC(dpb7000_state::tds_p3_r));
	m_tds_cpu->in_p4_cb().set(FUNC(dpb7000_state::tds_p4_r));

	SCN2681(config, m_tds_duart, 3.6864_MHz_XTAL);
	m_tds_duart->irq_cb().set_inputline(m_tds_cpu, M6803_IRQ1_LINE);
	m_tds_duart->a_tx_cb().set(m_acia[1], FUNC(acia6850_device::write_rxd));
	m_tds_duart->b_tx_cb().set(FUNC(dpb7000_state::duart_b_w));

	// Tablet
	Z8681(config, m_tablet_cpu, 7.3728_MHz_XTAL);
	m_tablet_cpu->set_addrmap(AS_PROGRAM, &dpb7000_state::tablet_program_map);
	m_tablet_cpu->set_addrmap(AS_DATA, &dpb7000_state::tablet_data_map);
	m_tablet_cpu->p2_in_cb().set(FUNC(dpb7000_state::tablet_p2_r));
	m_tablet_cpu->p2_out_cb().set(FUNC(dpb7000_state::tablet_p2_w));
	m_tablet_cpu->p3_in_cb().set(FUNC(dpb7000_state::tablet_p3_r));
	m_tablet_cpu->p3_out_cb().set(FUNC(dpb7000_state::tablet_p3_w));
}

ROM_START( dpb7000 )
	ROM_REGION16_BE(0xa0000, "monitor", 0)
	ROM_SYSTEM_BIOS( 0, "v406", "V4.06A" )
	ROMX_LOAD("01616a-nad-4af2.bin", 0x00001,  0x8000, CRC(a42eace6) SHA1(78c629a8afb48a95fc0a86ca762cc5b84bd9929b), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("01616a-ncd-58de.bin", 0x00000,  0x8000, CRC(f70fff2a) SHA1(a6f85d086a0c53d156eeeb157184ebcad4adecb3), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("01616a-mad-f512.bin", 0x10001,  0x8000, CRC(4c3e39f6) SHA1(443095c56481fbcadd4dcec1757d889c8f78805d), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("01616a-mcd-91f2.bin", 0x10000,  0x8000, CRC(4b6b6eb3) SHA1(1bef443d78197d33e44c708ead9604020881f67f), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("01616a-lad-0059.bin", 0x20001,  0x8000, CRC(0daf670d) SHA1(2342a43054ed141de298a1c1a6867949297bb52a), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("01616a-lcd-5639.bin", 0x20000,  0x8000, CRC(c8977d3f) SHA1(4ee9f3a883400b4771e6ae33c6e4edcd5c0b49e7), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("01616a-kad-1d9b.bin", 0x30001,  0x8000, CRC(bda7e309) SHA1(377edf2675a6736fe7ec775894858967b0e9247e), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("01616a-kcd-e51c.bin", 0x30000,  0x8000, CRC(aa05a5cc) SHA1(85dce335a72643f7640524b18cfe480a3c299f23), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("01616a-jad-47a8.bin", 0x40001,  0x8000, CRC(60fff4c9) SHA1(ba60281c0dd8627dffe07e7ea66f4eb688e74001), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("01616a-jcd-7825.bin", 0x40000,  0x8000, CRC(bb258ede) SHA1(ab8042391cd361bcd874b2f9d8fcaf20d4b2ebe7), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("01616a-iad-73a8.bin", 0x50001,  0x8000, CRC(98709fd2) SHA1(bd0f4689600e9fc49dbd8f2f326e18f8d602825e), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("01616a-icd-0562.bin", 0x50000,  0x8000, CRC(bab5274e) SHA1(3e51977da3dfe8fda089b9d2c3199acb4fed3212), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("01616a-had-d6fa.bin", 0x60001,  0x8000, CRC(70d791c5) SHA1(c281e4f27404e58ad5a80d6de1c5583cd9f3fe0e), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("01616a-hcd-9c0e.bin", 0x60000,  0x8000, CRC(938cb614) SHA1(ea7ea8a13e0ab1497691bab53090296ba51d271f), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("01616a-gad-397d.bin", 0x70001,  0x8000, CRC(0b95f9ed) SHA1(77126ee6c1f3dcdb8aa669ab74ff112e3f01918a), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("01616a-gcd-3ab8.bin", 0x70000,  0x8000, CRC(e9c21438) SHA1(1784ab2de1bb6023565b2e27872a0fcda25e1b1f), ROM_BIOS(0) | ROM_SKIP(1))
	ROM_SYSTEM_BIOS( 1, "v411", "V4.11A" )
	ROMX_LOAD("01993c-naa-0608.bin", 0x00001, 0x10000, CRC(9e4cfd96) SHA1(faae2ba849d30c894d9f929bae371ae76e05bdac), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("01993c-nca-3964.bin", 0x00000, 0x10000, CRC(04d75a15) SHA1(79f706296ba0c399b4e2d3acf947cae7706f9d63), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("01993c-maa-fa58.bin", 0x20001, 0x10000, CRC(88dc8ee0) SHA1(a2f6b80022ddb5adf687b678c621c8f5db1f3575), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("01993c-mca-b2b0.bin", 0x20000, 0x10000, CRC(16bf9db4) SHA1(a4696ee5a68b8a304c2433f16a6d57d7108f6406), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("01993c-laa-6be3.bin", 0x40001, 0x10000, CRC(56e693cc) SHA1(b4f3e0f8b635fe2b3fa975ed57f966e8ade7fbb1), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("01993c-lca-7c0d.bin", 0x40000, 0x10000, CRC(9540fa68) SHA1(79e46c4b6e86f6e53e3f5fea72e3be8c64de03af), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("01993c-kaa-3b12.bin", 0x60001, 0x10000, CRC(41e2ec71) SHA1(37112f878b3db24b198e878f5dba90fcbb4175d2), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("01993c-kca-f705.bin", 0x60000, 0x10000, CRC(00bfbd62) SHA1(17a5f2cbc91cabf1113c7d6e26124b67e5ece848), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("01993c-jaa-3cc9.bin", 0x80001, 0x10000, CRC(9e477845) SHA1(b89e5d2b69a6043452a56685a1227df86c83a328), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("01993c-jca-0b41.bin", 0x80000, 0x10000, CRC(58e8f302) SHA1(b37953046d81027b069b7a7a1834cd8c94fd9dca), ROM_BIOS(1) | ROM_SKIP(1))
	ROM_SYSTEM_BIOS( 2, "diags", "Diagnostics" )
	ROMX_LOAD("cat7000_diagnostic_rom_merged.bin", 0x00000, 0x20000, CRC(666767d6) SHA1(1dad9747321302bca7e03728c63034070f9ebf13), ROM_BIOS(2) )

	ROM_REGION(0x1000, "vduchar", 0)
	ROM_LOAD("bw14char.ic1",  0x0000, 0x1000, BAD_DUMP CRC(f9dd68b5) SHA1(50132b759a6d84c22c387c39c0f57535cd380411))

	ROM_REGION(0x700, "diskseq_ucode", 0)
	ROM_LOAD("17704a-hga-256.bin", 0x000, 0x100, CRC(ab59d8fd) SHA1(6dcbbb48838a5e370e22a708cea44e3db80d6505))
	ROM_LOAD("17704a-hfa-256.bin", 0x100, 0x100, CRC(f2475396) SHA1(b525ba58d37128cadf1e7285fb421c14e2495587))
	ROM_LOAD("17704a-hea-256.bin", 0x200, 0x100, CRC(0a19cc11) SHA1(13b72368d7cb5b7f685adfa07f07029026426b80))
	ROM_LOAD("17704a-hda-256.bin", 0x300, 0x100, CRC(a431cdf6) SHA1(027eea87ccafde727208277b40e3a3f88433343a))
	ROM_LOAD("17704a-hca-256.bin", 0x400, 0x100, CRC(610ef462) SHA1(f495647cc8420b7470ad68bccc069370f8f2af93))
	ROM_LOAD("17704a-hba-256.bin", 0x500, 0x100, CRC(94c16baf) SHA1(13baef1359bf92a8ebb9a0c39f4223e810c3cdc1))
	ROM_LOAD("17704a-haa-256.bin", 0x600, 0x100, CRC(0080f2a9) SHA1(63c7b31e5f65cc6e2c5fc67883c84284652cb4a7))

	ROM_REGION(0x700, "diskseq_prom", 0)
	ROM_LOAD("17704a-dfa-32.bin", 0x00, 0x20, CRC(ce5b8b46) SHA1(49a1e619f52101b6078e2f51d82ce5947fe2c011))

	ROM_REGION(0x800, "fddprom", 0)
	ROM_LOAD("17446a-gd-m2716.bin", 0x000, 0x800, CRC(a0be00ca) SHA1(48c4f8c07b9f6bc9b68698e1e326782e0b01e1b0))

	ROM_REGION(0x400, "output_timing_cursor", 0)
	ROM_LOAD("pb-037-17418-cda.bin", 0x000, 0x400, CRC(a31f3793) SHA1(4e74e528088c155e2c2592fa937e4cabfe6324c8))

	ROM_REGION(0x400, "output_timing_hlines", 0)
	ROM_LOAD("pb-037-17418-bga.bin", 0x000, 0x400, CRC(3b2c3635) SHA1(2038d616dd7f65ba55497bd037b0ad69aaa801ed))

	ROM_REGION(0x400, "output_timing_hflags", 0)
	ROM_LOAD("pb-037-17418-bea.bin", 0x000, 0x400, CRC(644e82a3) SHA1(d7634e03809abe2db924571c05821c1b2aca051b))

	ROM_REGION(0x400, "output_timing_vlines", 0)
	ROM_LOAD("pb-037-17418-dfa.bin", 0x000, 0x400, CRC(ca2ec308) SHA1(4232f44ea5bd3fa240eaf7c14e4b925140f90a1e))

	ROM_REGION(0x200, "output_timing_vflags", 0)
	ROM_LOAD("pb-037-17418-bfa.bin", 0x000, 0x100, CRC(a4486aac) SHA1(3834d550da3f1865b921807300a94612149f69d4))

	ROM_REGION(0x800, "keyboard", 0)
	ROM_LOAD("etc2716 63b2.bin", 0x000, 0x800, CRC(04614a50) SHA1(e547458f2c9cf29cf52f02b8824b32e5e91807fd))

	ROM_REGION(0x2000, "tds", 0)
	ROM_LOAD("hn482764g.bin", 0x0000, 0x2000, CRC(3626059c) SHA1(1a4f5c8b337f31c7b2b93096b59234ffbc2f1f00))

	ROM_REGION(0x2000, "tablet", 0)
	ROM_LOAD("nmc27c64q.bin", 0x0000, 0x2000, CRC(a453928f) SHA1(f4a25298fb446f0046c6f9f3ce70e7169dcebd01))

	ROM_REGION(0x800, "brushaddr_pal", 0)
	ROM_LOAD("pb-029-17419a-bea.bin", 0x000, 0x800, CRC(d30015c3) SHA1(fb5856df3ace452dfb1c1882b2adb6562e3a6800))

	ROM_REGION(0x200, "brushproc_prom", 0)
	ROM_LOAD("pb-02c-17593-baa.bin", 0x000, 0x200, CRC(6e31339f) SHA1(72a92d97412be19c884c3b854f7d9831435391a5))

	ROM_REGION16_BE(0x800, "brushproc_pal", 0)
	ROMX_LOAD("pb-02c-17593-hba.bin", 0x000, 0x800, CRC(76018e4f) SHA1(73d995e2e78410676061d45857756d5305a9984a), ROM_GROUPWORD)

	ROM_REGION(0x100, "brushstore_pal", 0)
	ROM_LOAD("pb-02a-17421-ada.bin", 0x000, 0x100, CRC(84bf7029) SHA1(9d58322994f6f7e99a9c6478577559c8171670ed))

	ROM_REGION(0x400, "framestore_back_pal", 0)
	ROM_LOAD("pb-02f-01748a-aba.bin", 0x000, 0x400, CRC(24b31494) SHA1(f9185a00e5470ec95d234a76c15acbf33cfb285d))

	ROM_REGION(0x400, "framestore_front_pal", 0)
	ROM_LOAD("pb-02f-01748a-bba.bin", 0x000, 0x400, CRC(8f06b632) SHA1(233b841c3957a6df229f3a693f9288cb8feec58c))

	ROM_REGION(0x100, "filter_signalprom", 0)
	ROM_LOAD("pb-027-17427a-dab.bin", 0x000, 0x100, CRC(6deac5cc) SHA1(184c34267e1b390bad0ca4a94befceece979e677))

	ROM_REGION(0x200, "filter_multprom", 0)
	ROM_LOAD("pb-027-17427a-afa-bfa.bin", 0x000, 0x200, CRC(58164a20) SHA1(c32997350bae22be075a8eee7fe4d1bdd8a34fd2))

	ROM_REGION(0x100, "size_pal", 0)
	ROM_LOAD("pb-012-17428a-aca.bin", 0x000, 0x100, CRC(0003b9ec) SHA1(8484e4c3b57069c5166201a7cb43d028c87e06cc))

	ROM_REGION(0x200, "size_prom_yh", 0)
	ROM_LOAD("pb-012-17428a-fda.bin", 0x000, 0x200, CRC(159418f4) SHA1(4e4c9be6d2e3ccdfdf8edfcb5cad106887e03fd2))

	ROM_REGION(0x200, "size_prom_yl", 0)
	ROM_LOAD("pb-012-17428a-gda.bin", 0x000, 0x200, CRC(1207291a) SHA1(ea44cf510169fdba2cfb0570a508fb3b0c93b06e))

	ROM_REGION(0x200, "size_prom_xh", 0)
	ROM_LOAD("pb-012-17428a-ecb.bin", 0x000, 0x200, CRC(1e324bf1) SHA1(efd88294dcb9fba58b2bb2b8d4715ba2c16f511d))

	ROM_REGION(0x200, "size_prom_xl", 0)
	ROM_LOAD("pb-012-17428a-gca.bin", 0x000, 0x200, CRC(ec7d26f3) SHA1(8bdbec33218f903294c135554d61271fa18d1a8d))

	ROM_REGION(0x10000, "storeaddr_pal_blank", 0)
	ROM_LOAD("pb-032-17425b-igb.bin", 0x00000, 0x10000, CRC(cdd80590) SHA1(fecb64695b61e8ec740af1480240088d5447688d))

	ROM_REGION(0x400, "storeaddr_prom_xlnib", 0)
	ROM_LOAD("pb-032-17425b-bbb.bin", 0x000, 0x400, CRC(2051a6e4) SHA1(3bd8a9015e77b034a94fe072a9753649b76f9f69))

	ROM_REGION(0x400, "storeaddr_prom_xmnib", 0)
	ROM_LOAD("pb-032-17425b-bcb.bin", 0x000, 0x400, CRC(01aaa6f7) SHA1(e31bff0c68f74996368443bfb58a3524a838f270))

	ROM_REGION(0x400, "storeaddr_prom_xhnib", 0)
	ROM_LOAD("pb-032-17425b-bdb.bin", 0x000, 0x400, CRC(20e2fb9e) SHA1(c4c77ec02ab6d3a1a28edf5543e57235a64a9d8d))

	ROM_REGION(0x400, "storeaddr_prom_protx", 0)
	ROM_LOAD("pb-032-17425b-deb.bin", 0x000, 0x400, CRC(faeb44dd) SHA1(3eaf981245824332d216e97095bdc02ff04e4800))

	ROM_REGION(0x400, "storeaddr_prom_proty", 0)
	ROM_LOAD("pb-032-17425b-edb.bin", 0x000, 0x400, CRC(83585876) SHA1(7c244adcd365f7b2ec347255100fa3597857905c))
ROM_END

} // anonymous namespace

COMP( 1981, dpb7000, 0, 0, dpb7000, dpb7000, dpb7000_state, empty_init, "Quantel", "DPB-7000", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



dps1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/****************************************************************************************************************

Ithaca Intersystems DPS-1

The last commercial release of a computer fitted with a front panel.

It needs to boot from floppy before anything appears on screen.

ToDo:
- Need artwork of the front panel switches and LEDs, and port FF.

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

#include "emu.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/am9519.h"
#include "machine/scn_pci.h"
#include "machine/upd765.h"
#include "bus/rs232/rs232.h"
//#include "bus/s100/s100.h"
#include "softlist_dev.h"


namespace {

class dps1_state : public driver_device
{
public:
	dps1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_bank1(*this, "bank1")
		, m_fdc(*this, "fdc")
		, m_floppy0(*this, "fdc:0")
		//, m_floppy1(*this, "fdc:1")
	{ }

	void dps1(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void portb2_w(u8 data);
	void portb4_w(u8 data);
	void portb6_w(u8 data);
	void portb8_w(u8 data);
	void portba_w(u8 data);
	void portbc_w(u8 data);
	void portbe_w(u8 data);
	u8 portff_r();
	void portff_w(u8 data);
	void fdc_drq_w(int state);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	bool m_dma_dir = 0;
	u16 m_dma_adr = 0U;
	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_memory_bank    m_bank1;
	required_device<upd765_family_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	//required_device<floppy_connector> m_floppy1;
};

void dps1_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).ram().share(m_ram);
	map(0x0000, 0x03ff).bankr(m_bank1);
}

void dps1_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x03).rw("uart", FUNC(scn2651_device::read), FUNC(scn2651_device::write));
	map(0xb0, 0xb1).m(m_fdc, FUNC(upd765_family_device::map));
	map(0xb2, 0xb3).w(FUNC(dps1_state::portb2_w)); // set dma fdc->memory
	map(0xb4, 0xb5).w(FUNC(dps1_state::portb4_w)); // set dma memory->fdc
	map(0xb6, 0xb7).w(FUNC(dps1_state::portb6_w)); // enable eprom
	map(0xb8, 0xb9).w(FUNC(dps1_state::portb8_w)); // set A16-23
	map(0xba, 0xbb).w(FUNC(dps1_state::portba_w)); // set A8-15
	map(0xbc, 0xbd).w(FUNC(dps1_state::portbc_w)); // set A0-7
	map(0xbe, 0xbf).w(FUNC(dps1_state::portbe_w)); // disable eprom
	map(0xff, 0xff).rw(FUNC(dps1_state::portff_r), FUNC(dps1_state::portff_w));
	// other allocated ports, optional
	// map(0x04, 0x07).rw("uart2", FUNC(scn2651_device::read), FUNC(scn2651_device::write));
	// map(0x08, 0x0b) parallel ports
	// map(0x10, 0x11) // interrupt response
	map(0x14, 0x14).rw("am9519a", FUNC(am9519_device::data_r), FUNC(am9519_device::data_w));
	map(0x15, 0x15).rw("am9519a", FUNC(am9519_device::stat_r), FUNC(am9519_device::cmd_w));
	map(0x16, 0x16).rw("am9519b", FUNC(am9519_device::data_r), FUNC(am9519_device::data_w));
	map(0x17, 0x17).rw("am9519b", FUNC(am9519_device::stat_r), FUNC(am9519_device::cmd_w));
	// map(0x18, 0x1f) control lines 0 to 7
	map(0xe0, 0xe3).noprw(); //unknown device
}


// read from disk, to memory
void dps1_state::portb2_w(u8 data)
{
	m_dma_dir = 1;
}

// write to disk, from memory
void dps1_state::portb4_w(u8 data)
{
	m_dma_dir = 0;
}

// enable eprom
void dps1_state::portb6_w(u8 data)
{
	m_bank1->set_entry(1);
}

// set A16-23
void dps1_state::portb8_w(u8 data)
{
}

// set A8-15
void dps1_state::portba_w(u8 data)
{
	m_dma_adr = (data << 8) | (m_dma_adr & 0xff);
}

// set A0-7
void dps1_state::portbc_w(u8 data)
{
	m_dma_adr = (m_dma_adr & 0xff00) | data;
}

// disable eprom
void dps1_state::portbe_w(u8 data)
{
	m_bank1->set_entry(0);
}

// read 8 front-panel switches
u8 dps1_state::portff_r()
{
	return 0x0e;
}

// write to 8 leds
void dps1_state::portff_w(u8 data)
{
}

// do dma
void dps1_state::fdc_drq_w(int state)
{
	if (state)
	{
		// acknowledge drq by taking /dack low (unsupported)
		// then depending on direction, transfer a byte
		address_space& mem = m_maincpu->space(AS_PROGRAM);
		if (m_dma_dir)
		{ // disk to mem
			mem.write_byte(m_dma_adr, m_fdc->dma_r());
		}
		else
		{ // mem to disk
			m_fdc->dma_w(mem.read_byte(m_dma_adr));
		}
		m_dma_adr++;
	}
	// else take /dack high (unsupported)
}

void dps1_state::machine_start()
{
	m_bank1->configure_entry(0, m_ram);
	m_bank1->configure_entry(1, m_rom);

	save_item(NAME(m_dma_dir));
	save_item(NAME(m_dma_adr));
}

void dps1_state::machine_reset()
{
	m_bank1->set_entry(1);
	// set fdc for 8 inch floppies
	m_fdc->set_rate(500000);
	// turn on the motor
	floppy_image_device *floppy = m_floppy0->get_device();
	m_fdc->set_floppy(floppy);
	floppy->mon_w(0);
}

static INPUT_PORTS_START( dps1 )
INPUT_PORTS_END

static void floppies(device_slot_interface &device)
{
	device.option_add("floppy0", FLOPPY_8_DSDD);
}

void dps1_state::dps1(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &dps1_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &dps1_state::io_map);

	/* video hardware */
	scn2651_device &uart(SCN2651(config, "uart", 5.0688_MHz_XTAL)); // Signetics 2651N
	uart.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	uart.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	uart.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(uart, FUNC(scn2651_device::rxd_w));
	rs232.dsr_handler().set(uart, FUNC(scn2651_device::dsr_w));
	rs232.cts_handler().set(uart, FUNC(scn2651_device::cts_w));

	AM9519(config, "am9519a", 0);
	AM9519(config, "am9519b", 0);

	// floppy
	UPD765A(config, m_fdc, 16_MHz_XTAL / 2, false, true);
	//m_fdc->intrq_wr_callback().set(FUNC(dps1_state::fdc_int_w)); // doesn't appear to be used
	m_fdc->drq_wr_callback().set(FUNC(dps1_state::fdc_drq_w));
	FLOPPY_CONNECTOR(config, "fdc:0", floppies, "floppy0", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	//FLOPPY_CONNECTOR(config, "fdc:1", floppies, "floppy1", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	// software lists
	SOFTWARE_LIST(config, "flop_list").set_original("dps1");
}

ROM_START( dps1 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "boot 1280", 0x0000, 0x0400, CRC(9c2e98fa) SHA1(78e6c9d00aa6e8f6c4d3c65984cfdf4e99434c66) ) // actually on the FDC-2 board
ROM_END

} // anonymous namespace


COMP( 1979, dps1, 0, 0, dps1, dps1, dps1_state, empty_init, "Ithaca InterSystems", "DPS-1", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



dpsv55.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Sony DPS-V55/V55M effects processor.

    V55 and V55M are identical except for the power supply (120 V, 60 Hz for
    the former, 230 V, 50/60 Hz for the latter).

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

#include "emu.h"
#include "cpu/f2mc16/mb90640a.h"
#include "machine/nvram.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"

namespace {

class dpsv55_state : public driver_device
{
public:
	dpsv55_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "lcdc")
		, m_p5(0)
	{
	}

	void dpsv55(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	HD44780_PIXEL_UPDATE(pixel_update);
	u8 p5_r();
	void p5_w(u8 data);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<mb90641a_device> m_maincpu;
	required_device<hd44780_device> m_lcdc;

	u8 m_p5;
};

void dpsv55_state::machine_start()
{
	save_item(NAME(m_p5));
}

HD44780_PIXEL_UPDATE(dpsv55_state::pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos >= 4 && pos < 20)
		bitmap.pix(line * 8 + y, (pos - 4) * 6 + x) = state;
}

u8 dpsv55_state::p5_r()
{
	return m_p5;
}

void dpsv55_state::p5_w(u8 data)
{
	m_lcdc->e_w(BIT(data, 3));
	m_lcdc->rw_w(BIT(data, 2));
	m_lcdc->rs_w(BIT(data, 1));

	m_p5 = data;
}


void dpsv55_state::mem_map(address_map &map)
{
	map(0xfc0000, 0xfc7fff).mirror(0x18000).ram().share("nvram"); // CS1
	map(0xfe0000, 0xffffff).rom().region("eprom", 0); // CS0
}


static INPUT_PORTS_START(dpsv55)
INPUT_PORTS_END

void dpsv55_state::dpsv55(machine_config &config)
{
	MB90641A(config, m_maincpu, 4_MHz_XTAL); // MB90641APF-G-105BND
	m_maincpu->set_addrmap(AS_PROGRAM, &dpsv55_state::mem_map);
	m_maincpu->port<1>().read().set(m_lcdc, FUNC(hd44780_device::db_r));
	m_maincpu->port<1>().write().set(m_lcdc, FUNC(hd44780_device::db_w));
	m_maincpu->port<5>().read().set(FUNC(dpsv55_state::p5_r));
	m_maincpu->port<5>().write().set(FUNC(dpsv55_state::p5_w));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // CY62256LL-70SNC-T2 + 3V lithium battery + M62021FP-600C reset generator + M5239L voltage detector

	// LCD unit (LC0801)
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6*16, 8*2);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 20);
	m_lcdc->set_pixel_update_cb(FUNC(dpsv55_state::pixel_update));

	//CXD2707(config, "dsp", 49.152_MHz_XTAL);
}

/*
Sony DPS-V55

Version 1.02

Chip: MX 27C1000DC-90

Sticker:
~~~~~~~~~~~~
 DPS-V55/M
 Ver.1.02
 759-499074
~~~~~~~~~~~~

Multi Processor  DPS-V55
Sony Corporation (c)1998
*/

ROM_START(dpsv55)
	ROM_REGION16_LE(0x20000, "eprom", 0)
	ROM_LOAD("dps-v55_m__ver.1.02__759-499-74.ic704", 0x00000, 0x20000, CRC(138c2fe0) SHA1(0916ccb1d7567639b382a19240a56274c5c2fa4a))
ROM_END

} // anonymous namespace

SYST(1998, dpsv55, 0, 0, dpsv55, dpsv55, dpsv55_state, empty_init, "Sony", "DPS-V55 Multi-Effect Processor", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



dragon.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nathan Woods
/***************************************************************************

    Dragon Family

    Bug? dragon200e hangs when single-quote is typed.

Dragon Alpha code added 21-Oct-2004,
            Phill Harvey-Smith (afra@aurigae.demon.co.uk)

            Added AY-8912 and FDC code 30-Oct-2004.

Fixed Dragon Alpha NMI enable/disable, following circuit traces on a real machine.
    P.Harvey-Smith, 11-Aug-2005.

Re-implemented Alpha NMI enable/disable, using direct PIA reads, rather than
keeping track of it in a variable in the driver.
    P.Harvey-Smith, 25-Sep-2006.

Radically re-wrote memory emulation code for CoCo 1/2 & Dragon machines, the
new code emulates the memory mapping of the SAM, dependent on what size of
RAM chips it is programmed to use, including proper mirroring of the RAM.

Replaced the kludged emulation of the cart line, with a timer based trigger
this is set to toggle at 1Hz, this seems to be good enough to trigger the
cartline, but is so slow in real terms that it should have very little
impact on the emulation speed.

Re-factored the code common to all machines, and separated the code different,
into callbacks/functions unique to the machines, in preparation for splitting
the code for individual machine types into separate files, I have preposed, that
the CoCo 1/2 should stay in coco.c, and that the coco3 and dragon specific code
should go into coco3.c and dragon.c which should (hopefully) make the code
easier to manage.
    P.Harvey-Smith, Dec 2006-Feb 2007

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

#include "emu.h"
#include "dragon.h"

#include "bus/coco/cococart.h"
#include "bus/rs232/rs232.h"
#include "cpu/m6809/hd6309.h"
#include "cpu/m6809/m6809.h"
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"

#include "softlist_dev.h"

#include "formats/coco_cas.h"
#include "formats/dmk_dsk.h"
#include "formats/sdf_dsk.h"
#include "formats/vdk_dsk.h"


/***************************************************************************
  DRAGON32
***************************************************************************/

//-------------------------------------------------
//  pia1_pa_changed - called when PIA1 PA changes
//-------------------------------------------------

void dragon_state::pia1_pa_changed(uint8_t data)
{
	/* call inherited function */
	coco12_state::pia1_pa_changed(data);

	/* if strobe bit is high send data from pia0 port b to dragon parallel printer */
	if (data & 0x02)
	{
		uint8_t output = pia_0().b_output();
		m_printer->output(output);
	}
}


/***************************************************************************
  DRAGON64
***************************************************************************/

//-------------------------------------------------
//  device_start
//-------------------------------------------------

void dragon64_state::machine_start()
{
	dragon_state::machine_start();

	uint8_t *rom = memregion("maincpu")->base();
	m_rombank[0]->configure_entries(0, 2, &rom[0x0000], 0x4000);
	m_rombank[1]->configure_entries(0, 2, &rom[0x2000], 0x4000);
}


//-------------------------------------------------
//  device_reset
//-------------------------------------------------

void dragon64_state::machine_reset()
{
	dragon_state::machine_reset();

	m_rombank[0]->set_entry(0);
	m_rombank[1]->set_entry(0);
}


//-------------------------------------------------
//  pia1_pb_changed
//-------------------------------------------------

void dragon64_state::pia1_pb_changed(uint8_t data)
{
	dragon_state::pia1_pb_changed(data);

	uint8_t ddr = ~pia_1().port_b_z_mask();

	/* If bit 2 of the pia1 ddrb is 1 then this pin is an output so use it */
	/* to control the paging of the 32k and 64k basic roms */
	/* Otherwise it set as an input, with an EXTERNAL pull-up so it should */
	/* always be high (enabling 32k basic rom) */
	if (ddr & 0x04)
	{
		page_rom(data & 0x04 ? true : false);
	}
}


//-------------------------------------------------
//  page_rom - Controls rom paging in Dragon 64,
//  and Dragon Alpha.
//
//  On 64, switches between the two versions of the
//  basic rom mapped in at 0x8000
//
//  On the alpha switches between the
//  Boot/Diagnostic rom and the basic rom
//-------------------------------------------------

void dragon64_state::page_rom(bool romswitch)
{
	int bank = romswitch
		? 0    // This is the 32k mode basic(64)/boot rom(alpha)
		: 1;   // This is the 64k mode basic(64)/basic rom(alpha)
	m_rombank[0]->set_entry(bank);      // 0x8000-0x9FFF
	m_rombank[1]->set_entry(bank);      // 0xA000-0xBFFF
}


/***************************************************************************
  DRAGON200-E
***************************************************************************/

uint8_t dragon200e_state::sam_read(offs_t offset)
{
	uint8_t data = sam().display_read(offset);
	m_vdg->as_w(data & 0x80 ? ASSERT_LINE : CLEAR_LINE);
	m_vdg->intext_w(data & 0x80 ? CLEAR_LINE : ASSERT_LINE);
	m_vdg->inv_w(m_lk1->read() ? ASSERT_LINE : CLEAR_LINE);
	return data;
}

MC6847_GET_CHARROM_MEMBER(dragon200e_state::char_rom_r)
{
	uint16_t addr = (line << 8) | (BIT(pia_1().b_output(), 4) << 7) | ch;
	return m_char_rom->base()[addr & 0xfff];
}


/***************************************************************************
  DRAGON64 PLUS
***************************************************************************/

//-------------------------------------------------
//  d64plus_6845_disp_r
//
//  The status of the 6845 display is determined by bit 0 of the $FFE2
//  register as follows:-
//    1  Video display busy, ie. not blanking
//    0  Video display available, ie. blanking
//  The display is blanking during a horizontal or vertical retrace period.
//-------------------------------------------------

uint8_t d64plus_state::d64plus_6845_disp_r()
{
	return m_crtc->de_r() ? 0xff : 0xfe;
}


//-------------------------------------------------
//  d64plus_bank_w
//-------------------------------------------------

void d64plus_state::d64plus_bank_w(uint8_t data)
{
	switch (data & 0x06)
	{
	case 0:  // Standard Dragon 32 Dynamic bank
		m_pram_bank->set_entry(0);
		m_vram_bank->set_entry(0);
		break;
	case 2:  // First extra 32K bank (A)
		m_pram_bank->set_entry(1);
		m_vram_bank->set_entry(1);
		break;
	case 6:  // Second extra 32K bank (B)
		m_pram_bank->set_entry(2);
		m_vram_bank->set_entry(2);
		break;
	default:
		logerror("unknown bank register $FFE2 = %02x\n", data);
		break;
	}
	if (data & 0x01)
	{
		m_vram_bank->set_entry(3);  // Video RAM bank (C)
	}
}


MC6845_UPDATE_ROW(d64plus_state::crtc_update_row)
{
	for (int column = 0; column < x_count; column++)
	{
		uint8_t code = m_video_ram[((ma + column) & 0x7ff)];
		uint16_t addr = (code << 4) | (ra & 0x0f);
		uint8_t data = m_char_rom->base()[addr & 0xfff];

		if (column == cursor_x)
		{
			data = 0xff;
		}

		for (int bit = 0; bit < 8; bit++)
		{
			int x = (column * 8) + bit;
			bitmap.pix(y, x) = m_palette->pen(BIT(data, 7) && de);
			data <<= 1;
		}
	}
}


//-------------------------------------------------
//  device_start
//-------------------------------------------------

void d64plus_state::machine_start()
{
	dragon64_state::machine_start();

	m_sam->space(0).install_readwrite_bank(0x0000, 0x7fff, m_pram_bank);
	m_sam->space(0).install_readwrite_bank(0x0000, 0x07ff, m_vram_bank);

	m_pram_bank->configure_entry(0, m_ram->pointer());
	m_pram_bank->configure_entries(1, 2, m_plus_ram, 0x8000);

	m_vram_bank->configure_entry(0, m_ram->pointer());
	m_vram_bank->configure_entries(1, 2, m_plus_ram, 0x8000);
	m_vram_bank->configure_entry(3, m_video_ram);

	address_space &space = m_maincpu->space(AS_PROGRAM);
	space.install_readwrite_handler(0xffe0, 0xffe0, read8smo_delegate(*m_crtc, FUNC(mc6845_device::status_r)), write8smo_delegate(*m_crtc, FUNC(mc6845_device::address_w)));
	space.install_readwrite_handler(0xffe1, 0xffe1, read8smo_delegate(*m_crtc, FUNC(mc6845_device::register_r)), write8smo_delegate(*m_crtc, FUNC(mc6845_device::register_w)));
	space.install_readwrite_handler(0xffe2, 0xffe2, read8smo_delegate(*this, FUNC(d64plus_state::d64plus_6845_disp_r)), write8smo_delegate(*this, FUNC(d64plus_state::d64plus_bank_w)));
}


//-------------------------------------------------
//  device_reset
//-------------------------------------------------

void d64plus_state::machine_reset()
{
	dragon64_state::machine_reset();

	m_pram_bank->set_entry(0);
	m_vram_bank->set_entry(0);
}


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

void dragon_state::dragon_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(m_sam, FUNC(sam6883_device::read), FUNC(sam6883_device::write));
}


void dragon64_state::d64_rom0(address_map &map)
{
	// $8000-$9FFF
	map(0x0000, 0x1fff).bankr("rombank0");
}

void dragon64_state::d64_rom1(address_map &map)
{
	// $A000-$BFFF
	map(0x0000, 0x1fff).bankr("rombank1");
}

void dragon64_state::d64_io0(address_map &map)
{
	// $FF00-$FF1F
	map(0x00, 0x03).mirror(0x18).rw(m_pia_0, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x04, 0x07).mirror(0x18).rw(m_acia, FUNC(mos6551_device::read), FUNC(mos6551_device::write));
}


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

/* Dragon keyboard

             PB0 PB1 PB2 PB3 PB4 PB5 PB6 PB7
    PA6: Ent Clr Brk N/c N/c N/c N/c Shift
    PA5: X   Y   Z   Up  Dwn Lft Rgt Space
    PA4: P   Q   R   S   T   U   V   W
    PA3: H   I   J   K   L   M   N   O
    PA2: @   A   B   C   D   E   F   G
    PA1: 8   9   :   ;   ,   -   .   /
    PA0: 0   1   2   3   4   5   6   7
 */
static INPUT_PORTS_START( dragon_keyboard )
	PORT_START("row0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("row1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_MINUS) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("row2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_G) PORT_CHAR('G')

	PORT_START("row3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_O) PORT_CHAR('O')

	PORT_START("row4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_W) PORT_CHAR('W')

	PORT_START("row5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_NAME(u8"\u2191") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP), '^')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_NAME(u8"\u2193") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN), 10) PORT_CHAR('[')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_NAME(u8"\u2190") PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(UCHAR_MAMEKEY(LEFT), 8)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_NAME(u8"\u2192") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT), 9) PORT_CHAR(']')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("row6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_NAME("CLEAR") PORT_CODE(KEYCODE_HOME) PORT_CHAR(12)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_NAME("BREAK") PORT_CODE(KEYCODE_END) PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x78, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
INPUT_PORTS_END

static INPUT_PORTS_START( dragon200e_keyboard )
	PORT_INCLUDE(dragon_keyboard)

	PORT_MODIFY("row0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_NAME(u8"0 \u00c7") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(0xC7)

	PORT_MODIFY("row1")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_NAME(u8"\u00d1") PORT_CODE(KEYCODE_COLON) PORT_CHAR(0xD1)

	PORT_MODIFY("row2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(';') PORT_CHAR('+')

	PORT_MODIFY("row5")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_NAME(u8"\u2191") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP), '^') PORT_CHAR('_')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_NAME(u8"\u2193 \u00a1") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(10) PORT_CHAR(0xA1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_NAME(u8"\u2192 \u00bf") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(9) PORT_CHAR(0xBF)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') PORT_CHAR(0xA7)

	PORT_MODIFY("row6")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_NAME("CLEAR @") PORT_CODE(KEYCODE_HOME) PORT_CHAR(12) PORT_CHAR('@')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dragon_state::keyboard_changed), 0) PORT_NAME(u8"BREAK \u00fc") PORT_CODE(KEYCODE_END) PORT_CODE(KEYCODE_ESC) PORT_CHAR(27) PORT_CHAR(0xFC)
INPUT_PORTS_END

INPUT_PORTS_START( dragon )
	PORT_INCLUDE(dragon_keyboard)
	PORT_INCLUDE(coco_joystick)
	PORT_INCLUDE(coco_analog_control)
INPUT_PORTS_END

static INPUT_PORTS_START( dragon200e )
	PORT_INCLUDE(dragon200e_keyboard)
	PORT_INCLUDE(coco_joystick)
	PORT_INCLUDE(coco_analog_control)

	PORT_START("LK1")
	PORT_CONFNAME(0x01, 0x01, "Inverse Video")
	PORT_CONFSETTING(0x00, "Inverse")
	PORT_CONFSETTING(0x01, "Normal")
INPUT_PORTS_END

void dragon_state::dragon_cart(device_slot_interface &device)
{
	dragon_cart_add_basic_devices(device);
	dragon_cart_add_fdcs(device);
	dragon_cart_add_multi_pak(device);
}


// F4 Character Displayer
static const gfx_layout d64plus_charlayout =
{
	8, 12,                  // 8 x 12 characters
	256,                    // 256 characters
	1,                      // 1 bits per pixel
	{ 0 },                  // no bitplanes
	// x offsets
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	// y offsets
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8 * 16                  // every char takes 16 bytes
};

static GFXDECODE_START(gfx_d64plus)
	GFXDECODE_ENTRY("chargen", 0, d64plus_charlayout, 0, 1)
GFXDECODE_END


void dragon_state::dragon_base(machine_config &config)
{
	this->set_clock(14.218_MHz_XTAL / 16);

	// basic machine hardware
	MC6809E(config, m_maincpu, DERIVED_CLOCK(1, 1));
	m_maincpu->set_addrmap(AS_PROGRAM, &dragon_state::dragon_mem);

	// devices
	INPUT_MERGER_ANY_HIGH(config, m_irqs).output_handler().set_inputline(m_maincpu, M6809_IRQ_LINE);
	INPUT_MERGER_ANY_HIGH(config, m_firqs).output_handler().set_inputline(m_maincpu, M6809_FIRQ_LINE);

	PIA6821(config, m_pia_0);
	m_pia_0->writepa_handler().set(FUNC(coco_state::pia0_pa_w));
	m_pia_0->writepb_handler().set(FUNC(coco_state::pia0_pb_w));
	m_pia_0->tspb_handler().set_constant(0xff);
	m_pia_0->ca2_handler().set(FUNC(coco_state::pia0_ca2_w));
	m_pia_0->cb2_handler().set(FUNC(coco_state::pia0_cb2_w));
	m_pia_0->irqa_handler().set(m_irqs, FUNC(input_merger_device::in_w<0>));
	m_pia_0->irqb_handler().set(m_irqs, FUNC(input_merger_device::in_w<1>));

	PIA6821(config, m_pia_1);
	m_pia_1->readpa_handler().set(FUNC(coco_state::pia1_pa_r));
	m_pia_1->readpb_handler().set(FUNC(coco_state::pia1_pb_r));
	m_pia_1->writepa_handler().set(FUNC(coco_state::pia1_pa_w));
	m_pia_1->writepb_handler().set(FUNC(coco_state::pia1_pb_w));
	m_pia_1->ca2_handler().set(FUNC(coco_state::pia1_ca2_w));
	m_pia_1->cb2_handler().set(FUNC(coco_state::pia1_cb2_w));
	m_pia_1->irqa_handler().set(m_firqs, FUNC(input_merger_device::in_w<0>));
	m_pia_1->irqb_handler().set(m_firqs, FUNC(input_merger_device::in_w<1>));

	SAM6883(config, m_sam, 14.218_MHz_XTAL, m_maincpu);
	m_sam->set_addrmap(0, &dragon_state::coco_ram);
	m_sam->set_addrmap(1, &dragon_state::coco_rom0);
	m_sam->set_addrmap(2, &dragon_state::coco_rom1);
	m_sam->set_addrmap(3, &dragon_state::coco_rom2);
	m_sam->set_addrmap(4, &dragon_state::coco_io0);
	m_sam->set_addrmap(5, &dragon_state::coco_io1);
	m_sam->set_addrmap(6, &dragon_state::coco_io2);
	m_sam->set_addrmap(7, &dragon_state::coco_ff60);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(coco_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->set_interface("dragon_cass");

	PRINTER(config, m_printer, 0);

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);

	MC6847(config, m_vdg, 14.218_MHz_XTAL / 4, true);
	m_vdg->set_screen(m_screen);
	m_vdg->hsync_wr_callback().set(FUNC(dragon_state::horizontal_sync));
	m_vdg->fsync_wr_callback().set(FUNC(dragon_state::field_sync));
	m_vdg->input_callback().set(FUNC(dragon_state::sam_read));

	// sound hardware
	coco_sound(config);

	// floating space
	coco_floating(config);

	// software lists
	SOFTWARE_LIST(config, "dragon_cart_list").set_original("dragon_cart");
	SOFTWARE_LIST(config, "dragon_cass_list").set_original("dragon_cass");
	SOFTWARE_LIST(config, "dragon_flop_list").set_original("dragon_flop");
	SOFTWARE_LIST(config, "coco_cart_list").set_compatible("coco_cart");
}

void dragon_state::dragon32(machine_config &config)
{
	dragon_base(config);

	// internal ram
	RAM(config, m_ram).set_default_size("32K").set_extra_options("64K");

	// cartridge
	COCOCART_SLOT(config, m_cococart, DERIVED_CLOCK(1, 1), &dragon_state::dragon_cart, "dragon_fdc");
	m_cococart->cart_callback().set([this] (int state) { cart_w(state != 0); }); // lambda because name is overloaded
	m_cococart->nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_cococart->halt_callback().set_inputline(m_maincpu, INPUT_LINE_HALT);
}

void dragon64_state::dragon64(machine_config &config)
{
	dragon_base(config);

	// internal ram
	RAM(config, m_ram).set_default_size("64K");

	sam().set_addrmap(1, &dragon64_state::d64_rom0);
	sam().set_addrmap(2, &dragon64_state::d64_rom1);
	sam().set_addrmap(4, &dragon64_state::d64_io0);

	// cartridge
	COCOCART_SLOT(config, m_cococart, DERIVED_CLOCK(1, 1), &dragon64_state::dragon_cart, "dragon_fdc");
	m_cococart->cart_callback().set([this] (int state) { cart_w(state != 0); }); // lambda because name is overloaded
	m_cococart->nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_cococart->halt_callback().set_inputline(m_maincpu, INPUT_LINE_HALT);

	// acia
	MOS6551(config, m_acia, 0);
	m_acia->set_xtal(1.8432_MHz_XTAL);
	m_acia->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<2>));
	m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_acia, FUNC(mos6551_device::write_rxd));
	rs232.dcd_handler().set(m_acia, FUNC(mos6551_device::write_dcd));
	rs232.dsr_handler().set(m_acia, FUNC(mos6551_device::write_dsr));
	rs232.cts_handler().set(m_acia, FUNC(mos6551_device::write_cts));

	// software lists
	SOFTWARE_LIST(config, "dragon_flex_list").set_original("dragon_flex");
	SOFTWARE_LIST(config, "dragon_os9_list").set_original("dragon_os9");
}

void dragon64_state::dragon64h(machine_config &config)
{
	dragon64(config);
	// Replace M6809 with HD6309
	HD6309E(config.replace(), m_maincpu, DERIVED_CLOCK(1, 1));
	m_maincpu->set_addrmap(AS_PROGRAM, &dragon64_state::dragon_mem);
	m_ram->set_default_size("64K");
}

void dragon200e_state::dragon200e(machine_config &config)
{
	dragon64(config);
	// video hardware
	m_vdg->set_get_char_rom(FUNC(dragon200e_state::char_rom_r));
	m_vdg->input_callback().set(FUNC(dragon200e_state::sam_read));
}

void d64plus_state::d64plus(machine_config &config)
{
	dragon64(config);
	// video hardware
	screen_device &plus_screen(SCREEN(config, "plus_screen", SCREEN_TYPE_RASTER));
	plus_screen.set_raw(14.218_MHz_XTAL, 912, 0, 640, 316, 0, 264);
	plus_screen.set_screen_update("crtc", FUNC(hd6845s_device::screen_update));

	GFXDECODE(config, "gfxdecode", "palette", gfx_d64plus);

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	// crtc
	HD6845S(config, m_crtc, 14.218_MHz_XTAL / 4 / 2);
	m_crtc->set_screen("plus_screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(d64plus_state::crtc_update_row));
}

void dragon64_state::tanodr64(machine_config &config)
{
	dragon64(config);
	this->set_clock(14.318181_MHz_XTAL / 16);

	m_sam->set_clock(14.318181_MHz_XTAL);

	// video hardware
	MC6847(config.replace(), m_vdg, 14.318181_MHz_XTAL / 4);
	m_vdg->set_screen(m_screen);
	m_vdg->hsync_wr_callback().set(FUNC(dragon_state::horizontal_sync));
	m_vdg->fsync_wr_callback().set(FUNC(dragon_state::field_sync));
	m_vdg->input_callback().set(FUNC(dragon_state::sam_read));

	// cartridge
	m_cococart->set_default_option("sdtandy_fdc");
}

void dragon64_state::tanodr64h(machine_config &config)
{
	tanodr64(config);
	// Replace M6809 CPU with HD6309 CPU
	HD6309E(config.replace(), m_maincpu, DERIVED_CLOCK(1, 1));
	m_maincpu->set_addrmap(AS_PROGRAM, &dragon64_state::dragon_mem);
	m_ram->set_default_size("64K");
}

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

  Game driver(s)

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

ROM_START(dragon32)
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD("dragon_data_ltd_1-0.ic18", 0x0000, 0x2000, CRC(e192b3eb) SHA1(9fbba5128b8a53c65ee0586c10513a0a6fb05a7d))
	ROM_LOAD("dragon_data_ltd_1-1.ic17", 0x2000, 0x2000, CRC(0e1d7bf0) SHA1(7088d75995cc2ec80a7eed9b9cc3d62f0f820a43))
ROM_END

ROM_START(dragon64)
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD("d64_1.rom",    0x0000, 0x4000, CRC(60a4634c) SHA1(f119506eaa3b4b70b9aa0dd83761e8cbe043d042))
	ROM_LOAD("d64_2.rom",    0x4000, 0x4000, CRC(17893a42) SHA1(e3c8986bb1d44269c4587b04f1ca27a70b0aaa2e))
ROM_END

ROM_START(dragon200)
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD( "ic18.rom",    0x0000, 0x4000, CRC(84f68bf9) SHA1(1983b4fb398e3dd9668d424c666c5a0b3f1e2b69))
	ROM_LOAD( "ic17.rom",    0x4000, 0x4000, CRC(17893a42) SHA1(e3c8986bb1d44269c4587b04f1ca27a70b0aaa2e))
ROM_END

ROM_START(dragon200e)
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD( "ic18_v1.4e.ic34",    0x0000, 0x4000, CRC(95af0a0a) SHA1(628543ee8b47a56df2b2175cfb763c0051517b90))
	ROM_LOAD( "ic17_v1.4e.ic37",    0x4000, 0x4000, CRC(48b985df) SHA1(c25632f3c2cfd1af3ee26b2f233a1ce1eccc365d))
	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "rom26.ic1",          0x0000, 0x1000, CRC(565724bc) SHA1(da5b756ba2a9c9ecebaa7daa8ba8bfd984d56a6f))
ROM_END

ROM_START(d64plus)
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD("d64_1.rom",      0x0000, 0x4000, CRC(60a4634c) SHA1(f119506eaa3b4b70b9aa0dd83761e8cbe043d042))
	ROM_LOAD("d64_2.rom",      0x4000, 0x4000, CRC(17893a42) SHA1(e3c8986bb1d44269c4587b04f1ca27a70b0aaa2e))
	ROM_REGION(0x0200, "prom", 0)
	ROM_LOAD("n82s147an.ic12", 0x0000, 0x0200, CRC(92b6728d) SHA1(bcf7c60c4e5608a58587044458d9cacaca4568aa))
	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD("chargen.ic22",   0x0000, 0x2000, CRC(514f1450) SHA1(956c99fcca1b52e79bb5d91dbafc817c992e324a))
ROM_END

ROM_START(tanodr64)
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD("tano_1.ic18",    0x0000, 0x4000, CRC(84f68bf9) SHA1(1983b4fb398e3dd9668d424c666c5a0b3f1e2b69))
	ROM_LOAD("tano_2.ic17",    0x4000, 0x4000, CRC(17893a42) SHA1(e3c8986bb1d44269c4587b04f1ca27a70b0aaa2e))
ROM_END

#define rom_dragon64h rom_dragon64
#define rom_tanodr64h rom_tanodr64


//    YEAR  NAME        PARENT    COMPAT  MACHINE     INPUT       CLASS               INIT        COMPANY                         FULLNAME                          FLAGS
COMP( 1982, dragon32,   0,        0,      dragon32,   dragon,     dragon_state,       empty_init, "Dragon Data Ltd",              "Dragon 32",                      0 )
COMP( 1983, dragon64,   dragon32, 0,      dragon64,   dragon,     dragon64_state,     empty_init, "Dragon Data Ltd",              "Dragon 64",                      0 )
COMP( 19??, dragon64h,  dragon32, 0,      dragon64h,  dragon,     dragon64_state,     empty_init, "Dragon Data Ltd",              "Dragon 64 (HD6309E)",            MACHINE_UNOFFICIAL )
COMP( 1985, dragon200,  dragon32, 0,      dragon64,   dragon,     dragon64_state,     empty_init, "Eurohard S.A.",                "Dragon 200",                     0 )
COMP( 1985, dragon200e, dragon32, 0,      dragon200e, dragon200e, dragon200e_state,   empty_init, "Eurohard S.A.",                "Dragon 200-E",                   0 )
COMP( 1985, d64plus,    dragon32, 0,      d64plus,    dragon,     d64plus_state,      empty_init, "Dragon Data Ltd / Compusense", "Dragon 64 Plus",                 0 )
COMP( 1983, tanodr64,   dragon32, 0,      tanodr64,   dragon,     dragon64_state,     empty_init, "Dragon Data Ltd / Tano Ltd",   "Tano Dragon 64 (NTSC)",          0 )
COMP( 19??, tanodr64h,  dragon32, 0,      tanodr64h,  dragon,     dragon64_state,     empty_init, "Dragon Data Ltd / Tano Ltd",   "Tano Dragon 64 (NTSC; HD6309E)", MACHINE_UNOFFICIAL )



drumsta.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Novation Drum Station drum machine.

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

#include "emu.h"
#include "cpu/adsp2100/adsp2100.h"
#include "cpu/mn1880/mn1880.h"
#include "machine/eeprompar.h"


namespace {

class drumsta_state : public driver_device
{
public:
	drumsta_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_dsp(*this, "dsp")
	{
	}

	void drumsta(machine_config &config);

private:
	void drumsta_prog(address_map &map) ATTR_COLD;
	void drumsta_data(address_map &map) ATTR_COLD;

	required_device<mn1880_device> m_maincpu;
	required_device<adsp2181_device> m_dsp;
};


void drumsta_state::drumsta_prog(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("program", 0);
}

void drumsta_state::drumsta_data(address_map &map)
{
	map(0x0001, 0x0001).noprw();
	map(0x0003, 0x0003).noprw();
	map(0x000e, 0x000f).noprw();
	map(0x0060, 0x031f).ram();
	map(0xb000, 0xb7ff).rw("eeprom", FUNC(eeprom_parallel_28xx_device::read), FUNC(eeprom_parallel_28xx_device::write));
}


static INPUT_PORTS_START(drumsta)
INPUT_PORTS_END

void drumsta_state::drumsta(machine_config &config)
{
	MN1880(config, m_maincpu, 8000000); // type and clock unknown (custom silkscreen)
	m_maincpu->set_addrmap(AS_PROGRAM, &drumsta_state::drumsta_prog);
	m_maincpu->set_addrmap(AS_DATA, &drumsta_state::drumsta_data);

	ADSP2181(config, m_dsp, 16000000).set_disable(); // clock unknown

	EEPROM_2864(config, "eeprom"); // Atmel AT28C64
}

ROM_START(drumsta)
	ROM_REGION(0x8000, "program", 0)
	ROM_LOAD("v1.2.u15", 0x0000, 0x8000, CRC(ab9a8654) SHA1(1914254c714d35031a456c71b1f9f9191df9126d))

	ROM_REGION(0x80000, "samples", 0)
	ROM_LOAD("v1.2.u28", 0x00000, 0x80000, CRC(dbbc9cfe) SHA1(61474c0bc6cfff3efe95527c57e4891f886b02aa))
ROM_END

} // anonymous namespace


SYST(1995, drumsta, 0, 0, drumsta, drumsta, drumsta_state, empty_init, "Novation", "Drum Station", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



ds5002fp_programmer.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/******************************************************************************

Small device for programming / reprogramming the DS5002 internal SRAM
used by Gaelco (was it designed by Gaelco, or is this a standard device provided
with the DS5002?)

Two different PCB versions.

One labeled "WR-1". This one had the PAL protected (no dump):
     _____________________________________      _____________________________________
  __|    Green LED->(_)(_)(_)<-Yellow LED |    |                                    |__
 |                    Red LED             |    |     ___________________________       |
 | ___   Xtal    ºººººººººººººººººººº     |    |    |                          |       |
 ||  |  12 MHz                            |    |    | Motorola                 |       |
 ||__|<- Button                <-EPROM socket  |    | MC68000P12               |       |
 |               ºººººººººººººººººººº     |    |    |__________________________|       |
 |      ___________                       |    |                                       |
 |__   |PAL16R6ACN|                       |    |                                     __|
    |_____________________________________|    |____________________________________|

Another one labeled "WR-2". This one has a 32MHz xtal (instead of 12 MHz), an unknown
chip with its Surface scratched out in the middle of the EPROM socket (the chips sets
behind the EPROM once on the socket) and a MC68000P12F 16MHz instead of a MC68000P12.
This one had the PAL unprotected (dumped).
     _____________________________________      _____________________________________
  __|    Green LED->(_)(_)(_)<-Yellow LED |    |                                    |__
 |                    Red LED             |    |     ___________________________       |
 | ___   Xtal    ºººººººººººººººººººº     |    |    |                          |       |
 ||  |  32 MHz    _________               |    |    | Motorola                 |       |
 ||__|<- Button  |_unknown_|    <-EPROM socket |    | MC68000P12F 16MHz        |       |
 |               ºººººººººººººººººººº     |    |    |__________________________|       |
 |      ___________                       |    |                                       |
 |__   |PAL16R6ACN|                       |    |                                     __|
    |_____________________________________|    |____________________________________|

Both are silkcreened as "68K-DS5002"

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

#include "emu.h"

#include "cpu/m68000/m68000.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "softlist_dev.h"

namespace {

class ds5002fp_programmer_state : public driver_device
{
public:
	ds5002fp_programmer_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cart(*this, "cartslot")
	{ }

	void ds5002fp_programmer(machine_config &config) ATTR_COLD;

private:
	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);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load) ATTR_COLD;

	void prg_map(address_map &map);

	required_device<cpu_device> m_maincpu;
	required_device<generic_slot_device> m_cart;
};

void ds5002fp_programmer_state::machine_start()
{
}

void ds5002fp_programmer_state::machine_reset()
{
}

void ds5002fp_programmer_state::prg_map(address_map &map)
{
	map(0x000000, 0x01ffff).r(m_cart, FUNC(generic_slot_device::read16_rom));
}


DEVICE_IMAGE_LOAD_MEMBER(ds5002fp_programmer_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	if ((size > 0x020000) | (size & 0x000001))
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported ROM size (must be 16 bits wide and no larger than 128K)");

	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_BIG);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

static INPUT_PORTS_START( ds5002fp_programmer )
INPUT_PORTS_END

void ds5002fp_programmer_state::ds5002fp_programmer(machine_config &config)
{
	M68000(config, m_maincpu, XTAL(32'000'000) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &ds5002fp_programmer_state::prg_map);

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "gaelcods_rom");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(ds5002fp_programmer_state::cart_load));
	m_cart->set_must_be_loaded(true);

	SOFTWARE_LIST(config, "cart_list").set_original("gaelco_ds5002fp_rom");
}

ROM_START( gaelcods )
	ROM_REGION( 0x200000, "pals", 0 )
	ROM_LOAD( "wr-2_pal16r6a-2.bin", 0x000, 0x104, CRC(4f027013) SHA1(8261665259a52f05e2682f2841fe788ec0b9e4ae) )
ROM_END

} // anonymous namespace

SYST( 1992, gaelcods, 0, 0, ds5002fp_programmer, ds5002fp_programmer, ds5002fp_programmer_state, empty_init, "Gaelco", "Gaelco DS5002FP Programmer", MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )



ds90.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder

/*

    Luxor X37 prototype

    (Luxor DS90-10 + ABC 1600 video)

*/

#include "emu.h"
#include "softlist_dev.h"
#include "bus/nscsi/devices.h"
#include "cpu/m68000/m68010.h"
#include "formats/abc1600_dsk.h"
#include "imagedev/floppy.h"
#include "machine/e0516.h"
#include "machine/hd63450.h"
#include "machine/nmc9306.h"
#include "machine/ns32081.h"
#include "machine/nscsi_bus.h"
#include "machine/nscsi_cb.h"
#include "machine/wd_fdc.h"
#include "machine/z80scc.h"
#include "machine/z8536.h"
#include "abc1600_v.h"


namespace {

#define MC68010_TAG  "14m"
#define NS32081_TAG  "06o"
#define MC68450_TAG  "11m"
#define Z8536A_TAG   "06l"
#define NMC9306_TAG  "05k"
#define E050_16_TAG  "03j"
#define Z8530A_0_TAG "16m"
#define Z8530A_1_TAG "16h"
#define Z8530A_2_TAG "16o"
#define FD1797_TAG   "15g"

class x37_state : public driver_device
{
public:
	x37_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_cpu(*this, MC68010_TAG),
		m_fpu(*this, NS32081_TAG),
		m_dmac(*this, MC68450_TAG),
		m_cio(*this, Z8536A_TAG),
		m_nvram(*this, NMC9306_TAG),
		m_scc(*this, {Z8530A_0_TAG, Z8530A_1_TAG, Z8530A_2_TAG}),
		m_fdc(*this, FD1797_TAG),
		m_floppy(*this, FD1797_TAG":%u", 0U),
		m_sasi(*this, "sasi:7:scsicb")
	{ }

	void x37(machine_config &config);

private:
	required_device<m68000_base_device> m_cpu;
	required_device<ns32081_device> m_fpu;
	required_device<hd63450_device> m_dmac;
	required_device<z8536_device> m_cio;
	required_device<nmc9306_device> m_nvram;
	required_device_array<scc8530_device, 3> m_scc;
	required_device<fd1797_device> m_fdc;
	required_device_array<floppy_connector, 3> m_floppy;
	required_device<nscsi_callback_device> m_sasi;

	virtual void machine_reset() override ATTR_COLD;

	static void floppy_formats(format_registration &fr);

	void program_map(address_map &map) ATTR_COLD;
	void cpu_space_map(address_map &map) ATTR_COLD;
};

void x37_state::program_map(address_map &map)
{
	map(0x000000, 0x007fff).rom().region(MC68010_TAG, 0);
}

void x37_state::cpu_space_map(address_map &map)
{
	map(0xffff0, 0xfffff).m(m_cpu, FUNC(m68010_device::autovectors_map));
}

static INPUT_PORTS_START( x37 )
INPUT_PORTS_END

void x37_state::machine_reset()
{
	m_fpu->reset();
}

static void x37_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

void x37_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_ABC1600_FORMAT);
}

void x37_state::x37(machine_config &config)
{
	// basic machine hardware
	M68010(config, m_cpu, 20'000'000/2);
	m_cpu->set_addrmap(AS_PROGRAM, &x37_state::program_map);
	m_cpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &x37_state::cpu_space_map);

	NS32081(config, m_fpu, 20'000'000/2);

	HD63450(config, m_dmac, 20'000'000/2, m_cpu);

	Z8536(config, m_cio, 6000000);
	NMC9306(config, m_nvram, 0);
	E0516(config, E050_16_TAG, 32'768);

	SCC8530(config, m_scc[0], 6000000);
	SCC8530(config, m_scc[1], 6000000);
	SCC8530(config, m_scc[2], 6000000);

	FD1797(config, m_fdc, 16'000'000/16);

	FLOPPY_CONNECTOR(config, m_floppy[0], x37_floppies, nullptr, x37_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], x37_floppies, nullptr, x37_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[2], x37_floppies, "525qd", x37_state::floppy_formats).enable_sound(true);

	NSCSI_BUS(config, "sasi");
	NSCSI_CONNECTOR(config, "sasi:0", default_scsi_devices, "s1410");
	NSCSI_CONNECTOR(config, "sasi:7", default_scsi_devices, "scsicb", true)
		.option_add_internal("scsicb", NSCSI_CB);

	// video hardware
	ABC1600_MOVER(config, ABC1600_MOVER_TAG, 0);

	// software list
	SOFTWARE_LIST(config, "flop_list").set_original("x37_flop");
}

ROM_START( x37 )
	ROM_REGION( 0x8000, MC68010_TAG, 0 )
	ROM_LOAD( "x37.07o", 0x0000, 0x8000, CRC(d505e7e7) SHA1(a3ad839e47b1f71c394e5ce28bce199e5e4810d2) )

	ROM_REGION( 0xa28, "plds", 0 )
	ROM_LOAD( "pat8003.12l", 0x000, 0x104, CRC(7c7b6dd1) SHA1(ab98fe70d589273b6a0437a818d9ae4bf9319ad5) ) // SCC decoder and clock multiplexor control
	ROM_LOAD( "pat8031.05h", 0x104, 0x104, CRC(2836e65b) SHA1(305feb8dff7d6762f2ab50d25316ad43140456eb) ) // DS60 MAPPER CONTROL
	ROM_LOAD( "pat8032.07h", 0x208, 0x104, CRC(356118d2) SHA1(e8e1dc6accdb8f0de481b91aa844f4b95f967826) ) // DS60 MAIN FUNCTION ENCODER
	ROM_LOAD( "pat8033.06h", 0x30c, 0x104, CRC(5f61f902) SHA1(b151621af0d9e851437ef4e3a02ecb78a6e102dd) ) // DS60 RAM REFRESH AND MAPPER WRITE CONTROL
	ROM_LOAD( "pat8034.12g", 0x410, 0x104, CRC(1105f161) SHA1(1923c0c954d3c812197d40f51bf3f53a158f87db) ) // DS60 CIO, SCC, FDC AND BOOTPROM CONTROLLER
	ROM_LOAD( "pat8035.05g", 0x514, 0x104, CRC(f25be5d9) SHA1(ed51b5cedea34c81b8cbdefd994e13aabd44a036) ) // DS60 RAM DATA PATH CONTROL
	ROM_LOAD( "pat8036.07g", 0x618, 0x104, CRC(350ff68e) SHA1(2d239bf324209adc7677eeb76b22c476ae0e6523) ) // DS60 RAM CONTROL SEQUENCER
	ROM_LOAD( "pat8037.10k", 0x71c, 0x104, CRC(a0e818b3) SHA1(a0d49ba0f09e235b28037539044e133f777fa4c7) ) // DS60 SASI INTERFACE CONTROL
	ROM_LOAD( "pat8038.04n", 0x820, 0x104, CRC(46ff5ce3) SHA1(c4a9025162b623bfcb74ac52f39de25bd53e448b) ) // DS60 PARITY GENERATION/DETECTION CONTROL
	ROM_LOAD( "pat8039.12h", 0x924, 0x104, CRC(d3f6974f) SHA1(98dc1bac1c822fe7af0edd683acfc2e5c51f0451) ) // DS60 NS32081 FLOATING POINT PROCESSOR INTERFACE
ROM_END

} // anonymous namespace


COMP( 1985, x37, 0,      0,      x37, x37, x37_state, empty_init, "Luxor", "X37 (prototype)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



dsb46.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder, Robbbert
/********************************************************************************************************************

2013-07-31 Skeleton Driver [Curt Coder]
2013-07-31 Connected to terminal [Robbbert]
2016-07-11 After 10 seconds the monitor program will start [Robbbert]

Commands: (no spaces allowed)
B - Boot the disk
D - Dump memory to screen
F - Fill Memory
G - Go To
H - Help
P - Alter port values
S - Alter memory


The photos show 3 boards:
- A "SASI Interface" board (all 74-series TTL)
- CPU board (64k dynamic RAM, Z80A CPU, 2x Z80CTC, 2x Z80SIO/0, MB8877A, Z80DMA, 4x MC1488,
  4x MC1489, XTALS 1.8432MHz and 24MHz)
- ADES board (Adaptec Inc AIC-100, AIC-250, AIC-300, Intel D8085AH, unknown crystal)


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

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/z80ctc.h"
#include "machine/z80daisy.h"
#include "machine/z80sio.h"


namespace {

class dsb46_state : public driver_device
{
public:
	dsb46_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_bank1(*this, "bank1")
	{ }

	void dsb46(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	void port1a_w(u8 data);
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	required_device<z80_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_memory_bank    m_bank1;
};

void dsb46_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).ram().share(m_ram);
	map(0x0000, 0x07ff).bankr(m_bank1);
}

void dsb46_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x03).rw("sio", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x08, 0x0b).rw("ctc1", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x1a, 0x1a).w(FUNC(dsb46_state::port1a_w));
	//map(0x10, 0x10) disk related
	//map(0x14, 0x14) ?? (read after CTC1 TRG3)
	//map(0x18, 0x18) ??
	//map(0x1c, 0x1c) disk data
	//map(0x1d, 0x1d) disk status (FF = no fdc)
}

static INPUT_PORTS_START( dsb46 )
INPUT_PORTS_END

void dsb46_state::machine_start()
{
	m_bank1->configure_entry(0, m_ram);
	m_bank1->configure_entry(1, m_rom);
}

void dsb46_state::machine_reset()
{
	m_bank1->set_entry(1);
}

void dsb46_state::port1a_w(u8 data)
{
	m_bank1->set_entry(BIT(~data, 0));
}

static const z80_daisy_config daisy_chain[] =
{
	{ "ctc1" },
	{ "sio" },
	{ nullptr }
};


void dsb46_state::dsb46(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 24_MHz_XTAL / 6);
	m_maincpu->set_addrmap(AS_PROGRAM, &dsb46_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &dsb46_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	/* Devices */
	z80sio_device& sio(Z80SIO(config, "sio", 24_MHz_XTAL / 6));
	sio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	sio.out_txda_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	sio.out_dtra_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	sio.out_rtsa_callback().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("sio", FUNC(z80sio_device::rxa_w));
	rs232.cts_handler().set("sio", FUNC(z80sio_device::ctsa_w));

	z80ctc_device &ctc1(Z80CTC(config, "ctc1", 24_MHz_XTAL / 6));
	ctc1.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	ctc1.set_clk<0>(1.8432_MHz_XTAL);
	ctc1.zc_callback<0>().set("sio", FUNC(z80sio_device::rxca_w));
	ctc1.zc_callback<0>().append("sio", FUNC(z80sio_device::txca_w));
	ctc1.set_clk<2>(1.8432_MHz_XTAL);
	ctc1.zc_callback<2>().set("sio", FUNC(z80sio_device::rxcb_w));
	ctc1.zc_callback<2>().append("sio", FUNC(z80sio_device::txcb_w));
}

ROM_START( dsb46 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "1538a.bin", 0x0000, 0x0800, CRC(65b3e26e) SHA1(afe1f03f266b7d13fdb1f1bc6762df5e0aa5c764) )

	ROM_REGION( 0x4000, "ades", 0 )
	ROM_LOAD( "ades.bin", 0x0000, 0x4000, CRC(d374abf0) SHA1(331f51a2bb81375aeffbe63c1ebc1d7cd779b9c3) )
ROM_END

} // anonymous namespace


COMP( 198?, dsb46, 0, 0, dsb46, dsb46, dsb46_state, empty_init, "Davidge", "DSB-4/6",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



dtc03.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Jonathan Gevaryahu, Carl
/***************************************************************************

        DECtalk DTC-03

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

// LED error codes (repeating pattern of a 4 bit number and 0xF blinking on the 4 LEDs):
// 0x2 - bad ROM at f0000-fffff (E21/E44) c3b0a
// 0x4 - bad ROM at d0000-dffff (E33/E56) c3adf
// 0x6 - bad RAM at a0000-a3fff (E59?)    c3be8
// 0x8 - bad ROM at c0000-cffff (E41/E60) c3af6
// 0xa - bad RAM at 00000-03fff (E32/E55) c3b7e
// 0xc - bad ROM at e0000-effff (E29/E50) c3b21
// in test order:
// 0x4 - bad ROM at d0000-dffff (E33/E56) c3adf
// 0x8 - bad ROM at c0000-cffff (E41/E60) c3af6
// 0x2 - bad ROM at f0000-fffff (E21/E44) c3b0a
// 0xc - bad ROM at e0000-effff (E29/E50) c3b21
// 0xa - bad RAM at 00000-03fff (E32/E55) c3b7e
// 0x6 - bad RAM at a0000-a3fff (E59?)    c3be8
// 0x3 - DSP Fault




// 6264 SRAMs are at E32, E55 and E59, unclear which two are a pair (likely E32/E55 or E55/E59)

#include "emu.h"
#include "cpu/i86/i186.h"
#include "cpu/tms32010/tms32010.h"
#include "bus/rs232/rs232.h"
#include "machine/input_merger.h"
#include "machine/scn_pci.h"
#include "sound/dac.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class dtc03_state : public driver_device
{
public:
	dtc03_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mainram(*this, "mainram")
		, m_dac(*this, "dac")
		, m_dsp(*this, "dsp")
		, m_epci(*this, "epci")
		, m_epci_irq(*this, "epci_irq")
		, m_rs232(*this, "rs232")
		, m_dsp_dma(0)
		, m_bio(ASSERT_LINE)
		, m_ctl(0)
		//, m_dbgclk(attotime::never)
	{
	}

	void dtc03(machine_config &config);

private:
	required_device<i80186_cpu_device> m_maincpu;
	required_shared_ptr<uint16_t> m_mainram;
	required_device<dac_12bit_r2r_device> m_dac;
	required_device<tms32010_device> m_dsp;
	required_device<scn2661c_device> m_epci;
	required_device<input_merger_device> m_epci_irq;
	required_device<rs232_port_device> m_rs232;
	void dtc03_io(address_map &map) ATTR_COLD;
	void dtc03_mem(address_map &map) ATTR_COLD;
	void dsp_io(address_map &map) ATTR_COLD;
	void dsp_mem(address_map &map) ATTR_COLD;

	u16 m_dsp_dma;
	u8 m_bio;
	u16 m_ctl;
	attotime m_dbgclk;

	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

	void dac_w(uint16_t data);
	uint16_t dsp_dma_r();
	void dsp_dma_w(uint16_t data);
	int bio_line_r();
	void dsp_clock_w(int state);
	void epci_txrx_clock_w(int state);
	void ctl_w(uint16_t data);
};


void dtc03_state::machine_reset()
{
	m_bio = ASSERT_LINE; // ?
	//m_bio = CLEAR_LINE;
}

void dtc03_state::machine_start()
{
	save_item(NAME(m_dsp_dma));
	save_item(NAME(m_bio));
	save_item(NAME(m_ctl));
}

void dtc03_state::dac_w(uint16_t data)
{
	m_dac->write(data >> 4);
}

uint16_t dtc03_state::dsp_dma_r()
{
	//m_bio = ASSERT_LINE;
	m_maincpu->drq1_w(0);
	logerror("dsp read dma\n");
	return m_dsp_dma;
}

void dtc03_state::dsp_dma_w(uint16_t data)
{
	m_bio = data&1; // CLEAR_LINE; ???
	//m_dsp_dma = data;
}

int dtc03_state::bio_line_r()
{
	// TODO: reading the bio line doesn't cause any direct external effects so this is wrong
	//if(m_bio == ASSERT_LINE)
	//  m_maincpu->drq0_w(1);
	return m_bio;
}

/* "ctl" bits known:
 fedcba9876543210
 ||||||||\\\\\\\\- unknown, unclear if used
 ||||\\\\--------- 4 bit LED value for the front panel, unclear which LED is which
 |||\------------- unknown
 ||\-------------- unknown, used/set after ROM/RAM tests, almost certainly 'DSP interrupt gate'
 |\--------------- unknown, probably 'DSP RESET'
 \---------------- unknown
 */
void dtc03_state::ctl_w(u16 data)
{
	logerror("CTL Write: %04X\n", data);
	popmessage("dsp out clock: %s; LED status: %01X\n", (data&0x2000)?"On":"Off",(data&0x0f00)>>8);
	//m_dsp->set_input_line(INPUT_LINE_RESET, (data & 0x4000) ? CLEAR_LINE : ASSERT_LINE); // this should be right but doesn't work?
	m_ctl = data;
}

/* port 500 read bits meaning
 fedcba9876543210
 ||||||||\\\\\\\\- DSW1 dip switches
 ||||||\\--------- open bus(reads as 1?)
 |||||\----------- X2 button?, read/checked at c3aac/c3aad (before rom test) and c3c32/c3c33
 ||||\------------ X1 button?, read/checked at c3aac/c3ab2 (before rom test)
 |||\------------- ST button
 ||\-------------- CL button
 |\--------------- DL button?, read/checked at c3b98/c3b99 (after rom test)
 \---------------- DM button
*/

void dtc03_state::dsp_clock_w(int state)
{
#if 0
	if (((m_ctl&0x2000) && state))
	{
		attotime dbgtime = (machine().time() - m_dbgclk);
		m_dbgclk = machine().time();
		logerror("dsp clock asserted, timing = %f hz\n", ATTOSECONDS_TO_HZ(dbgtime.attoseconds()));
	}
	else
	{
		logerror("dsp clock cleared\n");
	}
#endif
	//m_dsp->set_input_line(INPUT_LINE_IRQ0, (!(m_ctl & 0x2000) || state) ? CLEAR_LINE : ASSERT_LINE);
	m_dsp->set_input_line(INPUT_LINE_IRQ0, ((m_ctl&0x2000) && state) ? ASSERT_LINE : CLEAR_LINE);
	//m_dsp->set_input_line(0, ((m_ctl&0x2000) && state) ? ASSERT_LINE : CLEAR_LINE)); // TMS32010 INT
}

void dtc03_state::epci_txrx_clock_w(int state)
{
	m_epci->txc_w(state);
	m_epci->rxc_w(state);
}

void dtc03_state::dtc03_mem(address_map &map)
{
/* 80186 peripheral regs:
UMCS: C03C - 1100 0000 0011 1100 - address: c0000-fffff - purpose: rom area, no waitstates, no RDY
LMCS: 3FFC - 0011 1111 1111 1100 - address: 00000-3ffff - purpose: ram area 1, no waitstates, no RDY
PACS: 007C - 0000 0000 0111 1100 - peripheral base is 00000, no waitstates, no RDY
MMCS: 81FC - 1000 0001 1111 1100 - base address is 1000 000x xxxx xxxx xxxx i.e. 0x80000, no waitstates, no RDY
MPCS: A0BF - 1010 0000 1011 1111 - 256k block size, 64k select size (i.e. on a 0x10000 boundary), EX=1, MS=0, 3 waitstates, no RDY
This implies a memory mapped /cs for 80000, 90000, a0000, b0000 for the four lines
*/
/*
                   |           |           |           |
        19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0           a=0/a=1
/LCS     0  0  x  x  x  x  *  *  *  *  *  *  *  *  *  *  *  *  *  a  RW  SRAM E55/E32(loc?)
         0  1  x  x  x  x  x  x  x  x  x  x  x  x  x  x  x  x  x  x      OPEN BUS (maybe used by the unreleased(?) expansion module)
/MCS1    1  0  0  0                                                      open footprint at E43(loc?)
/MCS2    1  0  0  1                                                      open footprint at E49(loc?)
/MCS3    1  0  1  0  x  x  *  *  *  *  *  *  *  *  *  *  *  *  *  0? RW  SRAM E59/xxx(loc?) (low(?) byte only)
/MCS4    1  0  1  1  x  x  *  *  *  *  *  *  *  *  *  *  *  *  *  a  RW  SRAM E55/E32(loc?), again
/UCS     1  1  0  0  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  a  R  EPROM E60/E41
/UCS     1  1  0  1  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  a  R  EPROM E56/E33
/UCS     1  1  1  0  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  a  R  EPROM E50/E29
/UCS     1  1  1  1  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  a  R  EPROM E44/E21
*/
	map(0x00000, 0x03fff).mirror(0x3c000).ram().share("mainram");
	map(0xa0000, 0xa3fff).mirror(0x0c000).ram(); // should be only low or high order byte?
	map(0xb0000, 0xb3fff).mirror(0x0c000).ram().share("mainram");
	map(0xc0000, 0xfffff).rom().region("maincpu", 0xc0000);
}

void dtc03_state::dtc03_io(address_map &map)
{
	/* DTC-07
	map(0x0400, 0x0401).rw(FUNC(dectalk_isa_device::cmd_r), FUNC(dectalk_isa_device::status_w)); //PCS0
	map(0x0480, 0x0481).rw(FUNC(dectalk_isa_device::data_r), FUNC(dectalk_isa_device::data_w)); //PCS1
	map(0x0500, 0x0501).w(FUNC(dectalk_isa_device::dsp_dma_w)); //PCS2
	map(0x0580, 0x0581).r(FUNC(dectalk_isa_device::host_irq_r)); //PCS3
	map(0x0600, 0x0601).w(FUNC(dectalk_isa_device::output_ctl_w)); //PCS4
	map(0x0680, 0x0680).rw(FUNC(dectalk_isa_device::dma_r), FUNC(dectalk_isa_device::dma_w)); //PCS5
	map(0x0700, 0x0701).w(FUNC(dectalk_isa_device::irq_line_w)); //PCS6
	*/
	// DTC-03
	// dsp dma w is mapped somewhere here
	// the 6 front panel buttons are mapped somewhere here
	// the MT8870 DTMF decoder is mapped somewhere here
	// the control for the on-hook relay is mapped somewhere here
	// some pins on the expansion port might be mapped somewhere here as well.
	//?? // PCS0
	map(0x480, 0x481).w(FUNC(dtc03_state::ctl_w)).portr("IN1"); // PCS1
	map(0x500, 0x501).portr("IN1"); // PCS2
	//?? // PCS3
	//?? // PCS4
	map(0x680, 0x681).w(FUNC(dtc03_state::dsp_dma_w)); // PCS5
	map(0x700, 0x708).rw(m_epci, FUNC(scn2661c_device::read), FUNC(scn2661c_device::write)).umask16(0x00ff); // PCS6
}

void dtc03_state::dsp_mem(address_map &map) // guessing this hookup is the same as DTC-07
{
	map(0x0000, 0x0fff).rom().region("dsp", 0);
}

void dtc03_state::dsp_io(address_map &map) // guessing this hookup is the same as DTC-07
{
	map(0x0, 0x0).r(FUNC(dtc03_state::dsp_dma_r));
	map(0x1, 0x1).rw(FUNC(dtc03_state::dsp_dma_r), FUNC(dtc03_state::dac_w));
}


/* Input ports */
static INPUT_PORTS_START( dtc03 )
PORT_START("IN1")
	PORT_CONFNAME( 0x8000, 0x0000, "DM (Busy Out/Disable Module)")
	PORT_CONFSETTING(      0x0000, DEF_STR( Off ) )
	PORT_CONFSETTING(      0x8000, DEF_STR( On ) )
	PORT_CONFNAME( 0x4000, 0x0000, "DL (Data Loopback)")
	PORT_CONFSETTING(      0x0000, DEF_STR( Off ) )
	PORT_CONFSETTING(      0x4000, DEF_STR( On ) )
	PORT_CONFNAME( 0x2000, 0x0000, "CL (Modem Control Loopback)")
	PORT_CONFSETTING(      0x0000, DEF_STR( Off ) )
	PORT_CONFSETTING(      0x2000, DEF_STR( On ) )
	PORT_CONFNAME( 0x1000, 0x0000, "ST (Self Tests)")
	PORT_CONFSETTING(      0x0000, DEF_STR( Off ) )
	PORT_CONFSETTING(      0x1000, DEF_STR( On ) )
	PORT_CONFNAME( 0x0800, 0x0000, "X1 (Expansion Module Option 1)")
	PORT_CONFSETTING(      0x0000, DEF_STR( Off ) )
	PORT_CONFSETTING(      0x0800, DEF_STR( On ) )
	PORT_CONFNAME( 0x0400, 0x0000, "X2 (Expansion Module Option 2)")
	PORT_CONFSETTING(      0x0000, DEF_STR( Off ) )
	PORT_CONFSETTING(      0x0400, DEF_STR( On ) )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_DIPNAME(  0x0080, 0x0000, "Enable DM button" ) PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(       0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(       0x0000, DEF_STR( On ) )
	PORT_DIPUNUSED_DIPLOC( 0x0040, 0x0000, "SW1:2" )
	PORT_DIPNAME(  0x0020, 0x0000, "Keypad Mask on Power-Up" ) PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(       0x0020, "All Zeroes" )
	PORT_DIPSETTING(       0x0000, "All Ones" )
	PORT_DIPNAME(  0x0018, 0x0000, "EIA Serial Format" ) PORT_DIPLOCATION("SW1:4,5")
	PORT_DIPSETTING(       0x0000, "7 bits, odd parity" )
	PORT_DIPSETTING(       0x0008, "7 bits, even parity" )
	PORT_DIPSETTING(       0x0010, "8 bits, no parity" )
	PORT_DIPSETTING(       0x0018, "7 bits, ignore parity" )
	PORT_DIPNAME(  0x0007, 0x0007, "EIA Serial Baud Rate" ) PORT_DIPLOCATION("SW1:6,7,8")
	PORT_DIPSETTING(       0x0007, "9600" )
	PORT_DIPSETTING(       0x0006, "4800" )
	PORT_DIPSETTING(       0x0005, "2400" )
	PORT_DIPSETTING(       0x0004, "1200" )
	PORT_DIPSETTING(       0x0003, "600" )
	PORT_DIPSETTING(       0x0002, "300" )
	PORT_DIPSETTING(       0x0001, "150" )
	PORT_DIPSETTING(       0x0000, "110" ) /* technically this is the default, but for ease of use set it to 9600 */
INPUT_PORTS_END

void dtc03_state::dtc03(machine_config &config)
{
	/* basic machine hardware */
	I80186(config, m_maincpu, XTAL(16'000'000)); // AMD 80186, 16MHz xtal
	m_maincpu->set_addrmap(AS_PROGRAM, &dtc03_state::dtc03_mem);
	m_maincpu->set_addrmap(AS_IO, &dtc03_state::dtc03_io);
	m_maincpu->tmrout0_handler().set(FUNC(dtc03_state::dsp_clock_w));
	m_maincpu->tmrout1_handler().set(FUNC(dtc03_state::epci_txrx_clock_w));

	INPUT_MERGER_ANY_HIGH(config, m_epci_irq).output_handler().set(m_maincpu, FUNC(i80186_cpu_device::int0_w));

	TMS32010(config, m_dsp, XTAL(20'000'000)); // 20MHz xtal
	m_dsp->set_addrmap(AS_PROGRAM, &dtc03_state::dsp_mem);
	m_dsp->set_addrmap(AS_IO, &dtc03_state::dsp_io);
	m_dsp->bio().set(FUNC(dtc03_state::bio_line_r)); // guessing this hookup is the same as DTC-07

	SCN2661C(config, m_epci, 5068800); // this is wrong as there's no dedicated 5.0688MHz xtal, probably actually just XTAL(20'000'000)/4
	m_epci->txd_handler().set(m_rs232, FUNC(rs232_port_device::write_txd));
	m_epci->rts_handler().set(m_rs232, FUNC(rs232_port_device::write_rts));
	m_epci->dtr_handler().set(m_rs232, FUNC(rs232_port_device::write_dtr));
	m_epci->rxrdy_handler().set(m_epci_irq, FUNC(input_merger_device::in_w<0>)); // these are a guess
	m_epci->txrdy_handler().set(m_epci_irq, FUNC(input_merger_device::in_w<1>));

	RS232_PORT(config, m_rs232, default_rs232_devices, "terminal");
	m_rs232->rxd_handler().set(m_epci, FUNC(scn2661c_device::rxd_w));
	m_rs232->dcd_handler().set(m_epci, FUNC(scn2661c_device::dcd_w));
	m_rs232->dsr_handler().set(m_epci, FUNC(scn2661c_device::dsr_w));
	m_rs232->cts_handler().set(m_epci, FUNC(scn2661c_device::cts_w));

	SPEAKER(config, "speaker").front_center();
	DAC_12BIT_R2R(config, m_dac, 0).add_route(0, "speaker", 1.0); // AD7541 DAC
}

/* ROM definition */
ROM_START( dtc03 )
	ROM_REGION( 0x100000, "maincpu", ROMREGION_ERASEFF )
	// dictionary and boot vector, common for all versions
	ROMX_LOAD( "23-028e6.e50",       0x0e0000, 0x008000, CRC(2788190e) SHA1(13af6d1cf5a69a0fc160ebd75ce891f7938a9cad), ROM_SKIP(1) )
	ROMX_LOAD( "23-032e6.e29",       0x0e0001, 0x008000, CRC(d6f4edd0) SHA1(1268a938c44f2332173edab4a3b41a9fb58f31ae), ROM_SKIP(1) )
	ROMX_LOAD( "23-029e6.e44",       0x0f0000, 0x008000, CRC(49d0ff90) SHA1(7398c50cf10bc918ca8de55aec3032bf0d00e5c4), ROM_SKIP(1) )
	ROMX_LOAD( "23-033e6.e21",       0x0f0001, 0x008000, CRC(38516d0c) SHA1(228c6d644b8e3d9b9e649b3de2e24780a189e661), ROM_SKIP(1) )

	ROM_SYSTEM_BIOS( 0, "v43", "DTC-03 Version 4.3")
	ROMX_LOAD( "23-425e6.e60",       0x0c0000, 0x008000, CRC(a7c11541) SHA1(b0b5b8633849369d9a50a04498ebdf3597b74051), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "23-427e6.e41",       0x0c0001, 0x008000, CRC(8e45da49) SHA1(64e0b840365e1094e574841f22e9107a754dd70c), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "23-426e6.e56",       0x0d0000, 0x008000, CRC(7191c670) SHA1(32e18f00b459e868189a084abe528868f07705f5), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "23-428e6.e33",       0x0d0001, 0x008000, CRC(23a12574) SHA1(44c87987f7b6c8d7e5bec4bf39259ff7edc986ed), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v42", "DTC-03 Version 4.2")
	ROMX_LOAD( "23-305e6.e60",       0x0c0000, 0x008000, CRC(1c7fd2a0) SHA1(d64cb27a2583b33857ccfb0babc3e528d7bd5c1d), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "23-307e6.e41",       0x0c0001, 0x008000, CRC(aea1d679) SHA1(dae9a599c411c7c757c74f3ececa55209b0bf066), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "23-306e6.e56",       0x0d0000, 0x008000, CRC(83411f4d) SHA1(c0844fc2777b79f3997f97948f1cf1bc2a568fe9), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "23-308e6.e33",       0x0d0001, 0x008000, CRC(8e12680a) SHA1(d7b5870ded2568d5d9bad0cafa3996995e3bc3c5), ROM_SKIP(1) | ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "v41", "DTC-03 Version 4.1")
	ROMX_LOAD( "23-114e6.e60",       0x0c0000, 0x008000, CRC(122c69c0) SHA1(d6a2b6c57c966d07c3cfee346d3d0b4baca2426e), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD( "23-116e6.e41",       0x0c0001, 0x008000, CRC(3641f5e2) SHA1(6c93a697caf59f027d8a23ac34e5e356a1cba524), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD( "23-115e6.e56",       0x0d0000, 0x008000, CRC(21c4c6f8) SHA1(6796adfec81fa0bfa83f5f61c81e57dae97085c7), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD( "23-117e6.e33",       0x0d0001, 0x008000, CRC(3c65dfab) SHA1(d430fe04172f30aa196ef9480545575c7ec8fbb1), ROM_SKIP(1) | ROM_BIOS(2))

	ROM_REGION( 0x2000, "dsp", 0)
	ROMX_LOAD( "23-230f4.82s191.e11",0x000000, 0x000800, CRC(e32aff61) SHA1(7c014a988c9e583096ef1cfc142c7c0a4444168e), ROM_SKIP(1) )
	ROMX_LOAD( "23-229f4.82s191.e18",0x000001, 0x000800, CRC(0beddc21) SHA1(5eb4948c49ee40613dce93f2017ba55ba2b59767), ROM_SKIP(1) )
ROM_END

} // anonymous namespace

/* Driver */

/*    YEAR   NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS         INIT        COMPANY                          FULLNAME   FLAGS */
COMP( 1985,  dtc03,  0,      0,      dtc03,   dtc03, dtc03_state,  empty_init, "Digital Equipment Corporation", "DECtalk DTC-03",   MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



dual68.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

        Dual Systems 68000

        09/12/2009 Skeleton driver.

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

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "cpu/i8085/i8085.h"
#include "bus/rs232/rs232.h"
//#include "bus/s100/s100.h"
#include "machine/input_merger.h"
#include "machine/scn_pci.h"


namespace {

class dual68_state : public driver_device
{
public:
	dual68_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_usart(*this, "usart%u", 1U)
		, m_p_ram(*this, "ram")
	{ }

	void dual68(machine_config &config);

private:
	uint8_t sio_direct_r(offs_t offset);
	void sio_direct_w(offs_t offset, uint8_t data);
	uint8_t sio_status_r();
	uint8_t fdc_status_r();

	void dual68_mem(address_map &map) ATTR_COLD;
	void sio4_io(address_map &map) ATTR_COLD;
	void sio4_mem(address_map &map) ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_device_array<scn_pci_device, 4> m_usart;
	required_shared_ptr<uint16_t> m_p_ram;
};



uint8_t dual68_state::sio_direct_r(offs_t offset)
{
	return m_usart[0]->read(offset);
}

void dual68_state::sio_direct_w(offs_t offset, uint8_t data)
{
	m_usart[0]->write(offset, data);
}

uint8_t dual68_state::sio_status_r()
{
	return 0x00;
}

uint8_t dual68_state::fdc_status_r()
{
	return 0x40;
}

void dual68_state::dual68_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x00ffff).ram().share("ram");
	map(0x080000, 0x081fff).rom().region("mainbios", 0);
	map(0x7f0000, 0x7f0001).rw(FUNC(dual68_state::sio_direct_r), FUNC(dual68_state::sio_direct_w));
	map(0x7f00c0, 0x7f00c0).r(FUNC(dual68_state::fdc_status_r));
	map(0x800000, 0x801fff).rom().region("mainbios", 0);
}

void dual68_state::sio4_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).rom().region("siocpu", 0);
	map(0x8000, 0x87ff).mirror(0x7800).ram();
	//map(0xffe3, 0xffe3).lr8("usart0_rx_hack", []() { return 0x02; });
}

void dual68_state::sio4_io(address_map &map)
{
	map.unmap_value_high();
	map(0x06, 0x06).r(FUNC(dual68_state::sio_status_r));
	map(0x18, 0x18).nopw();
	map(0x20, 0x23).rw("usart1", FUNC(scn_pci_device::read), FUNC(scn_pci_device::write));
	map(0x28, 0x2b).rw("usart2", FUNC(scn_pci_device::read), FUNC(scn_pci_device::write));
	map(0x30, 0x33).rw("usart3", FUNC(scn_pci_device::read), FUNC(scn_pci_device::write));
	map(0x38, 0x3b).rw("usart4", FUNC(scn_pci_device::read), FUNC(scn_pci_device::write));
}

/* Input ports */
static INPUT_PORTS_START( dual68 )
INPUT_PORTS_END

void dual68_state::machine_reset()
{
	uint8_t *mainbios = memregion("mainbios")->base();

	memcpy((uint8_t*)m_p_ram.target(),mainbios,0x2000);
}

void dual68_state::dual68(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, 16_MHz_XTAL / 2); // MC68000L8
	m_maincpu->set_addrmap(AS_PROGRAM, &dual68_state::dual68_mem);

	i8085a_cpu_device &siocpu(I8085A(config, "siocpu", 9.8304_MHz_XTAL)); // NEC D8085AC-2
	siocpu.set_addrmap(AS_PROGRAM, &dual68_state::sio4_mem);
	siocpu.set_addrmap(AS_IO, &dual68_state::sio4_io);

	for (auto &usart : m_usart)
		SCN2661B(config, usart, 9.8304_MHz_XTAL / 2);
	m_usart[0]->rxrdy_handler().set("usartint", FUNC(input_merger_device::in_w<0>));
	m_usart[1]->rxrdy_handler().set("usartint", FUNC(input_merger_device::in_w<1>));
	m_usart[2]->rxrdy_handler().set("usartint", FUNC(input_merger_device::in_w<2>));
	m_usart[3]->rxrdy_handler().set("usartint", FUNC(input_merger_device::in_w<3>));
	m_usart[0]->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_usart[0]->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_usart[0]->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));

	INPUT_MERGER_ANY_HIGH(config, "usartint").output_handler().set_inputline("siocpu", I8085_RST65_LINE);

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_usart[0], FUNC(scn_pci_device::rxd_w));
	rs232.dsr_handler().set(m_usart[0], FUNC(scn_pci_device::dsr_w));
	rs232.dcd_handler().set(m_usart[0], FUNC(scn_pci_device::dcd_w));
	rs232.cts_handler().set(m_usart[0], FUNC(scn_pci_device::cts_w));
}

/* ROM definition */
ROM_START( dual68 )
	ROM_REGION16_BE( 0x2000, "mainbios", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v1", "2 * 4KB" )
	ROMX_LOAD("dual_cpu68000_1.bin", 0x0000, 0x1000, CRC(d1785c08) SHA1(73c1f68875f1d8eb5e92f4347f509c61103da90f),ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("dual_cpu68000_2.bin", 0x0001, 0x1000, CRC(b9f1ba3c) SHA1(8fd02936ad06d5a22d435d96f06e2442fc7d00ec),ROM_SKIP(1) | ROM_BIOS(0))

	ROM_SYSTEM_BIOS( 1, "v2", "2 * 2KB" )
	ROMX_LOAD("dual.u2.bin", 0x0000, 0x0800, CRC(e9c44fcd) SHA1(d5cc609d6f5e6745d5f0af1aa6dc66012333ed60),ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("dual.u3.bin", 0x0001, 0x0800, CRC(827b049f) SHA1(8209f8ab3d1068e5bab51e7eb12be46d4ea28354),ROM_SKIP(1) | ROM_BIOS(1))

	ROM_REGION( 0x10000, "siocpu", ROMREGION_ERASEFF )
	ROM_LOAD("dual_sio4.bin", 0x0000, 0x0800, CRC(6b0a1965) SHA1(5d2dc6c6a315293ded4b9fc95c8ac1599bf31dd3))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY                     FULLNAME              FLAGS
COMP( 1981, dual68, 0,      0,      dual68,  dual68, dual68_state, empty_init, "Dual Systems Corporation", "Dual Systems 68000", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



duet16.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Carl

#include "emu.h"
#include "cpu/i86/i86.h"
#include "cpu/mcs48/mcs48.h"
#include "imagedev/floppy.h"
#include "machine/i8087.h"
#include "machine/i8251.h"
#include "machine/input_merger.h"
#include "machine/pit8253.h"
#include "machine/pic8259.h"
#include "machine/upd765.h"
#include "machine/msm58321.h"
#include "machine/6840ptm.h"
#include "machine/z80sio.h"
#include "machine/pit8253.h"
#include "machine/am9517a.h"
#include "video/mc6845.h"
#include "emupal.h"
#include "screen.h"
#include "bus/rs232/rs232.h"
#include "bus/rs232/keyboard.h"


namespace {

class duet16_state : public driver_device
{
public:
	duet16_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_pic(*this, "pic"),
		m_fdc(*this, "fdc"),
		m_dmac(*this, "dmac"),
		m_fd(*this, "fdc:%u", 0),
		m_pal(*this, "palette"),
		m_chrpal(*this, "chrpal"),
		m_rtc(*this, "rtc"),
		m_tmint(*this, "tmint"),
		m_screen(*this, "screen"),
		m_chrrom(*this, "char"),
		m_cvram(*this, "cvram"),
		m_gvram(*this, "gvram")
	{ }

	void duet16(machine_config &config);
protected:
	void machine_reset() override ATTR_COLD;
private:
	u8 pic_r(offs_t offset);
	void pic_w(offs_t offset, u8 data);
	u8 dma_mem_r(offs_t offset);
	void dma_mem_w(offs_t offset, u8 data);
	u8 dmapg_r();
	void dmapg_w(u8 data);
	void fdcctrl_w(u8 data);
	void dispctrl_w(u8 data);
	void pal_w(offs_t offset, u8 data);
	void hrq_w(int state);
	u8 rtc_r();
	void rtc_w(u8 data);
	u8 rtc_stat_r();
	void rtc_addr_w(u8 data);
	u16 sysstat_r();
	void rtc_d0_w(int state);
	void rtc_d1_w(int state);
	void rtc_d2_w(int state);
	void rtc_d3_w(int state);
	void rtc_busy_w(int state);
	void rtc_irq_reset();
	MC6845_UPDATE_ROW(crtc_update_row);
	void duet16_io(address_map &map) ATTR_COLD;
	void duet16_mem(address_map &map) ATTR_COLD;
	required_device<i8086_cpu_device> m_maincpu;
	required_device<pic8259_device> m_pic;
	required_device<upd765a_device> m_fdc;
	required_device<am9517a_device> m_dmac;
	required_device_array<floppy_connector, 2> m_fd;
	required_device<palette_device> m_pal;
	required_device<palette_device> m_chrpal;
	required_device<msm58321_device> m_rtc;
	required_device<input_merger_device> m_tmint;
	required_device<screen_device> m_screen;
	required_memory_region m_chrrom;
	required_shared_ptr<u16> m_cvram;
	required_shared_ptr<u16> m_gvram;
	u8 m_dmapg, m_dispctrl;
	u8 m_rtc_d;
	bool m_rtc_busy, m_rtc_irq;
};

void duet16_state::machine_reset()
{
	m_rtc->cs1_w(ASSERT_LINE);
	rtc_irq_reset();
}

u8 duet16_state::pic_r(offs_t offset)
{
	return m_pic->read(offset ^ 1);
}

void duet16_state::pic_w(offs_t offset, u8 data)
{
	m_pic->write(offset ^ 1, data);
}

void duet16_state::fdcctrl_w(u8 data)
{
	floppy_image_device *f = m_fd[BIT(data, 2) ? 1 : 0]->get_device();
	m_fdc->set_floppy(f);

	m_fd[0]->get_device()->mon_w(!BIT(data, 0));
	m_fd[1]->get_device()->mon_w(!BIT(data, 0));
	m_fdc->reset_w(!BIT(data, 1));

	// TODO: bit 3 = LSPD
}

u8 duet16_state::dma_mem_r(offs_t offset)
{
	return m_maincpu->space(AS_PROGRAM).read_byte((m_dmapg << 16) | offset);
}

void duet16_state::dma_mem_w(offs_t offset, u8 data)
{
	m_maincpu->space(AS_PROGRAM).write_byte((m_dmapg << 16) | offset, data);
}

u8 duet16_state::dmapg_r()
{
	return m_dmapg;
}

void duet16_state::dmapg_w(u8 data)
{
	m_dmapg = data & 0xf;
}

void duet16_state::hrq_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state);
	m_dmac->hack_w(state);
}

u16 duet16_state::sysstat_r()
{
	return 0xb484;
}

void duet16_state::duet16_mem(address_map &map)
{
	map(0x00000, 0x8ffff).ram();
	map(0xa8000, 0xbffff).ram().share("gvram");
	map(0xc0000, 0xc0fff).ram().share("cvram");
	map(0xf8000, 0xf801f).rw(FUNC(duet16_state::dmapg_r), FUNC(duet16_state::dmapg_w)).umask16(0x00ff);
	map(0xf8000, 0xf801f).rw("dmac", FUNC(am9517a_device::read), FUNC(am9517a_device::write)).umask16(0xff00);
	map(0xf8020, 0xf8023).rw(FUNC(duet16_state::pic_r), FUNC(duet16_state::pic_w)).umask16(0x00ff);
	map(0xf8040, 0xf804f).rw("itm", FUNC(ptm6840_device::read), FUNC(ptm6840_device::write)).umask16(0x00ff);
	map(0xf8060, 0xf8067).rw("bgpit", FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0xf8080, 0xf8087).rw("sio", FUNC(upd7201_device::ba_cd_r), FUNC(upd7201_device::ba_cd_w)).umask16(0x00ff);
	map(0xf80a0, 0xf80a3).rw("kbusart", FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0xf80c0, 0xf80c0).rw("crtc", FUNC(hd6845s_device::status_r), FUNC(hd6845s_device::address_w));
	map(0xf80c2, 0xf80c2).rw("crtc", FUNC(hd6845s_device::register_r), FUNC(hd6845s_device::register_w));
	map(0xf80e0, 0xf80e3).rw("i8741", FUNC(upi41_cpu_device::upi41_master_r), FUNC(upi41_cpu_device::upi41_master_w)).umask16(0x00ff);
	map(0xf8100, 0xf8103).m(m_fdc, FUNC(upd765a_device::map)).umask16(0x00ff);
	map(0xf8120, 0xf8120).rw(FUNC(duet16_state::rtc_r), FUNC(duet16_state::rtc_w));
	map(0xf8160, 0xf819f).w(FUNC(duet16_state::pal_w));
	map(0xf8200, 0xf8201).r(FUNC(duet16_state::sysstat_r));
	map(0xf8220, 0xf8220).w(FUNC(duet16_state::fdcctrl_w));
	map(0xf8260, 0xf8260).w(FUNC(duet16_state::rtc_addr_w));
	map(0xf8280, 0xf8280).r(FUNC(duet16_state::rtc_stat_r));
	map(0xf8280, 0xf8280).w(FUNC(duet16_state::dispctrl_w));
	map(0xfe000, 0xfffff).rom().region("rom", 0);
}

void duet16_state::duet16_io(address_map &map)
{
}

void duet16_state::pal_w(offs_t offset, u8 data)
{
	int entry = (BIT(offset, 0) ? 2 : 0) | (BIT(offset, 5) ? 0 : 4);
	m_pal->set_pen_color(entry, pal1bit(BIT(data, 1)), pal1bit(BIT(data, 2)), pal1bit(BIT(data, 0)));
	m_pal->set_pen_color(entry + 1, pal1bit(BIT(data, 5)), pal1bit(BIT(data, 6)), pal1bit(BIT(data, 4)));
}

void duet16_state::dispctrl_w(u8 data)
{
	m_dispctrl = data;
}

MC6845_UPDATE_ROW(duet16_state::crtc_update_row)
{
	if(!de)
		return;
	u8 const *const gvram = (u8 *)&m_gvram[0];
	for(int i = 0; i < x_count; i++)
	{
		u16 coffset = (ma + i) & 0x07ff;
		u16 goffset = (((ma * 16) + (ra * 80) + i) & 0x7fff) ^ 1;
		u8 g2 = gvram[goffset];
		u8 g1 = gvram[goffset + 0x08000];
		u8 g0 = gvram[goffset + 0x10000];
		u8 attr = m_cvram[coffset] >> 8;
		u8 chr = m_cvram[coffset & ~BIT(attr, 6)] & 0xff;
		u8 data = m_chrrom->base()[(chr * 16) + ra + (BIT(m_dispctrl, 3) * 0x1000)];
		if(BIT(attr, 6))
		{
			if(!(i & 1))
				data = bitswap<8>(data, 7, 7, 6, 6, 5, 5, 4, 4);
			else
				data = bitswap<8>(data, 3, 3, 2, 2, 1, 1, 0, 0);
		}
		if(BIT(attr, 4) && (m_screen->frame_number() & 32)) // ~1.7 Hz
			data = 0;
		if(BIT(attr, 3))
			data ^= 0xff;
		if(BIT(attr, 5) && (ra > 12)) // underline start?
		{
			attr |= 7;
			data = 0xff;
		}
		if(((i & ~BIT(attr, 6)) == cursor_x) && (m_screen->frame_number() & 16)) // ~3.4 Hz
			data ^= 0xff;

		rgb_t fg = m_chrpal->pen_color(attr & 7);

		for(int xi = 0; xi < 8; xi++)
		{
			rgb_t color;
			if((data & (0x80 >> xi)) && BIT(m_dispctrl, 1))
				color = fg;
			else if(BIT(m_dispctrl, 0))
				color = m_pal->pen_color((BIT(g2, 7 - xi) << 2) | (BIT(g1, 7 - xi) << 1) | BIT(g0, 7 - xi));
			else
				color = 0;
			bitmap.pix(y, (i * 8) + xi) = color;
		}
	}
}

void duet16_state::rtc_d0_w(int state)
{
	m_rtc_d = (m_rtc_d & ~1) | (state ? 1 : 0);
}

void duet16_state::rtc_d1_w(int state)
{
	m_rtc_d = (m_rtc_d & ~2) | (state ? 2 : 0);
}

void duet16_state::rtc_d2_w(int state)
{
	m_rtc_d = (m_rtc_d & ~4) | (state ? 4 : 0);
}

void duet16_state::rtc_d3_w(int state)
{
	m_rtc_d = (m_rtc_d & ~8) | (state ? 8 : 0);
}

void duet16_state::rtc_busy_w(int state)
{
	if (state && !m_rtc_busy && !m_rtc_irq)
	{
		m_rtc_irq = true;
		m_tmint->in_w<1>(1);
	}
	m_rtc_busy = state;
}

void duet16_state::rtc_irq_reset()
{
	m_rtc_irq = false;
	m_tmint->in_w<1>(0);
}

u8 duet16_state::rtc_r()
{
	u8 ret;
	m_rtc->cs2_w(ASSERT_LINE);
	m_rtc->read_w(ASSERT_LINE);
	ret = m_rtc_d;
	m_rtc->read_w(CLEAR_LINE);
	m_rtc->cs2_w(CLEAR_LINE);
	return ret;
}

void duet16_state::rtc_w(u8 data)
{
	m_rtc->d0_w(BIT(data, 0));
	m_rtc->d1_w(BIT(data, 1));
	m_rtc->d2_w(BIT(data, 2));
	m_rtc->d3_w(BIT(data, 3));
	m_rtc->cs2_w(ASSERT_LINE);
	m_rtc->write_w(ASSERT_LINE);
	m_rtc->write_w(CLEAR_LINE);
	m_rtc->cs2_w(CLEAR_LINE);
}

u8 duet16_state::rtc_stat_r()
{
	u8 status = m_rtc_irq ? 0x80 : 0;
	if (!machine().side_effects_disabled())
		rtc_irq_reset();
	return status;
}

void duet16_state::rtc_addr_w(u8 data)
{
	m_rtc->d0_w(BIT(data, 0));
	m_rtc->d1_w(BIT(data, 1));
	m_rtc->d2_w(BIT(data, 2));
	m_rtc->d3_w(BIT(data, 3));
	m_rtc->cs2_w(ASSERT_LINE);
	m_rtc->address_write_w(ASSERT_LINE);
	m_rtc->address_write_w(CLEAR_LINE);
	m_rtc->cs2_w(CLEAR_LINE);
}

static const gfx_layout duet16_charlayout =
{
	8, 16,                   /* 8 x 16 characters */
	512,                    /* 512 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ STEP8(0,1) },
	/* y offsets */
	{ STEP16(0,8) },
	8*16                 /* every char takes 8 bytes */
};

static GFXDECODE_START(gfx_duet16)
	GFXDECODE_ENTRY( "char", 0x0000, duet16_charlayout, 0, 1 )
GFXDECODE_END


static void duet16_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

void duet16_keyboard_devices(device_slot_interface &device)
{
	device.option_add("keyboard", SERIAL_KEYBOARD);
}

static DEVICE_INPUT_DEFAULTS_START(keyboard)
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_1200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END

void duet16_state::duet16(machine_config &config)
{
	I8086(config, m_maincpu, 24_MHz_XTAL / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &duet16_state::duet16_mem);
	m_maincpu->set_addrmap(AS_IO, &duet16_state::duet16_io);
	m_maincpu->set_irq_acknowledge_callback("pic", FUNC(pic8259_device::inta_cb));
	m_maincpu->esc_opcode_handler().set("i8087", FUNC(i8087_device::insn_w));
	m_maincpu->esc_data_handler().set("i8087", FUNC(i8087_device::addr_w));

	i8087_device &i8087(I8087(config, "i8087", 24_MHz_XTAL / 3));
	i8087.set_space_86(m_maincpu, AS_PROGRAM);
	i8087.irq().set(m_pic, FUNC(pic8259_device::ir2_w)); // INT87
	i8087.busy().set_inputline(m_maincpu, INPUT_LINE_TEST);

	I8741A(config, "i8741", 20_MHz_XTAL / 4);

	PIC8259(config, m_pic, 0);
	m_pic->out_int_callback().set_inputline(m_maincpu, 0);

	AM9517A(config, m_dmac, 20_MHz_XTAL / 4);
	m_dmac->out_hreq_callback().set(FUNC(duet16_state::hrq_w));
	m_dmac->in_memr_callback().set(FUNC(duet16_state::dma_mem_r));
	m_dmac->out_memw_callback().set(FUNC(duet16_state::dma_mem_w));
	m_dmac->in_ior_callback<0>().set(m_fdc, FUNC(upd765a_device::dma_r));
	m_dmac->out_iow_callback<0>().set(m_fdc, FUNC(upd765a_device::dma_w));
	m_dmac->out_eop_callback().set(m_fdc, FUNC(upd765a_device::tc_line_w));

	pit8253_device &bgpit(PIT8253(config, "bgpit", 0));
	bgpit.set_clk<0>(8_MHz_XTAL / 13);
	bgpit.set_clk<1>(8_MHz_XTAL / 13);
	bgpit.set_clk<2>(8_MHz_XTAL / 13);
	bgpit.out_handler<0>().set("sio", FUNC(upd7201_device::txca_w)); // TODO: selected through LS153
	bgpit.out_handler<0>().append("sio", FUNC(upd7201_device::rxca_w));
	bgpit.out_handler<1>().set("sio", FUNC(upd7201_device::txcb_w));
	bgpit.out_handler<1>().append("sio", FUNC(upd7201_device::rxcb_w));
	bgpit.out_handler<2>().set("kbusart", FUNC(i8251_device::write_txc));
	bgpit.out_handler<2>().append("kbusart", FUNC(i8251_device::write_rxc));

	ptm6840_device &itm(PTM6840(config, "itm", 8_MHz_XTAL / 8));
	itm.set_external_clocks(0.0, 0.0, (8_MHz_XTAL / 8).dvalue()); // C3 = 1MHz
	itm.o3_callback().set("itm", FUNC(ptm6840_device::set_c1)); // C1 = C2 = O3
	itm.o3_callback().append("itm", FUNC(ptm6840_device::set_c2));
	itm.irq_callback().set(m_tmint, FUNC(input_merger_device::in_w<0>));

	upd7201_device& sio(UPD7201(config, "sio", 8_MHz_XTAL / 2));
	sio.out_int_callback().set("pic", FUNC(pic8259_device::ir1_w)); // INT5

	i8251_device &kbusart(I8251(config, "kbusart", 8_MHz_XTAL / 4));
	kbusart.txd_handler().set("kbd", FUNC(rs232_port_device::write_txd));
	kbusart.rts_handler().set("kbusart", FUNC(i8251_device::write_cts));
	kbusart.rxrdy_handler().set("kbint", FUNC(input_merger_device::in_w<0>));
	kbusart.txrdy_handler().set("kbint", FUNC(input_merger_device::in_w<1>));

	rs232_port_device &kbd(RS232_PORT(config, "kbd", duet16_keyboard_devices, "keyboard"));
	kbd.rxd_handler().set("kbusart", FUNC(i8251_device::write_rxd));
	kbd.set_option_device_input_defaults("keyboard", DEVICE_INPUT_DEFAULTS_NAME(keyboard));

	INPUT_MERGER_ANY_HIGH(config, "kbint").output_handler().set(m_pic, FUNC(pic8259_device::ir5_w)); // INT2

	INPUT_MERGER_ANY_HIGH(config, m_tmint).output_handler().set(m_pic, FUNC(pic8259_device::ir0_w)); // INT6

	UPD765A(config, m_fdc, 8_MHz_XTAL, true, false);
	m_fdc->drq_wr_callback().set(m_dmac, FUNC(am9517a_device::dreq0_w));
	m_fdc->intrq_wr_callback().set(m_pic, FUNC(pic8259_device::ir3_w)); // INT4
	FLOPPY_CONNECTOR(config, "fdc:0", duet16_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats, true);
	FLOPPY_CONNECTOR(config, "fdc:1", duet16_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats, true);

	hd6845s_device &crtc(HD6845S(config, "crtc", 2000000)); // "46505S" on schematics
	crtc.set_char_width(8);
	crtc.set_update_row_callback(FUNC(duet16_state::crtc_update_row));

	PALETTE(config, m_pal).set_entries(8);
	PALETTE(config, m_chrpal, palette_device::BRG_3BIT);

	GFXDECODE(config, "gfxdecode", m_chrpal, gfx_duet16);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(640, 480);
	m_screen->set_visarea_full();
	m_screen->set_screen_update("crtc", FUNC(hd6845s_device::screen_update));

	MSM58321(config, m_rtc, 32768_Hz_XTAL);
	m_rtc->d0_handler().set(FUNC(duet16_state::rtc_d0_w));
	m_rtc->d1_handler().set(FUNC(duet16_state::rtc_d1_w));
	m_rtc->d2_handler().set(FUNC(duet16_state::rtc_d2_w));
	m_rtc->d3_handler().set(FUNC(duet16_state::rtc_d3_w));
	m_rtc->busy_handler().set(FUNC(duet16_state::rtc_busy_w));
	m_rtc->set_year0(1980);
	m_rtc->set_default_24h(true);
}

ROM_START(duet16)
	ROM_REGION16_LE(0x2000, "rom", 0)
	ROM_LOAD16_BYTE("duet16_h516a_3.bin", 0x0001, 0x1000, CRC(936706aa) SHA1(412ff9c7bf4443d2ed29a8d792fc3c849c9393cc))
	ROM_LOAD16_BYTE("duet16_h517a_z.bin", 0x0000, 0x1000, CRC(1633cce8) SHA1(5145d04a48921cacfed17a94873e8988772fc8d4))

	ROM_REGION(0x2000, "char", 0)
	ROM_LOAD("duet16_char_j500a_4.bin", 0x0000, 0x2000, CRC(edf860f8) SHA1(0dcc584db701d21b7c3304cd2296562ebda6fb4c))

	ROM_REGION(0x400, "i8741", 0)
	ROM_LOAD("duet16_key_8741ak001b_z.bin", 0x000, 0x400, CRC(d23ee68d) SHA1(3b6a86fe2a304823c5385cd673f9580a35199dac))
ROM_END

} // anonymous namespace


COMP( 1983, duet16, 0, 0, duet16, 0, duet16_state, empty_init, "Panafacom (Panasonic/Fujitsu)", "Duet-16", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



dvd-n5xx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:

/*************************************************************************
NUON Enhanced DVD Player / Samsung DVD-N501
Hardware info by Guru

Brief Overview (summarized from info found on the internet)
--------------
NUON was a co-processor (i.e. Integrated Circuit) that was released around 2000 by VM Labs, Inc. It was initially announced in 1999 as 'Project X'.
It was added to a very limited number of DVD players so they could play special DVD discs with interactive content (usually games).
These players have features like most other DVD players and can also play VCD, CD Audio discs and MP3 audio.
There is a built-in graphical interface with a 'Light Show' known as the "Virtual Light Machine 2" written by Jeff Minter of Llamasoft fame which
shows graphical effects when CDs and MP3s are played. This is very similar to the VLM on the Atari Jaguar.
The NUON players have the ability to smooth fast forward and fast rewind and zoom/pan DVD Video discs.
The technology was abandoned after a few years due to poor sales because it was competing (and losing) against the Sony Playstation 2
and other much more powerful and well supported video game consoles that had a lot more game titles available.
Additionally VM Labs went bankrupt in 2002 so there was zero support for it after that point.

Externally the units look like any other DVD player but have a NUON logo on the front.
There were several models of NUON DVD players manufactured with Samsung being the most common with the most models released....
Motorola Blackbird (only for developers)
Motorola Streamaster 5000 (set top box)
Raite Optoelectronics RDP-741
RCA (Thomson multimedia) DRC300N
RCA (Thomson multimedia) DRC480N
Samsung - DVD-N2000 (Extiva)
          DVD-N501  Service manual available with technical info, block diagrams and schematics. Search 'dvdn501.pdf service manual'.
          DVD-N504  Asia/Europe only
          DVD-N505  Asia/Europe only
          DVD-N591  Korea only
Toshiba - SD2300
There were also two models announced but not released: Samsung DVD-N705, Oritron DVD900.

Some of the players came bundled with a game controller and a few 3rd-party controllers were produced....
Warrior Digital Gamepad HPI-2000 - Bundled with Samsung DVD-N2000. Digital only with 10 buttons and a D-pad.
Stealth Controller (HPI)         - Resembles the N64 controller and has digital buttons/D-pad and an analog stick.
Logitech Gamepad for NUON        - Digital buttons/D-pad and an analog stick.
Samsung NUON controller          - Digital only with 10 buttons and a D-pad.


NUON-Specific Discs
-------------------

Games Released                        NUON-Enhanced Movies Released
--------------                        -----------------------------
Tempest 3000 (dumped)                 The Adventures of Buckaroo Banzai Across the 8th Dimension
Freefall 3050 A.D. (dumped)           Bedazzled (2000 remake)
Merlin Racing (dumped)                Dr. Dolittle 2
Space Invaders X.L. (dumped)          Planet of the Apes (2001 remake)
Iron Soldier 3 (dumped)
Ballistic (dumped)                    Samplers/Demos Released
The Next Tetris (dumped)              -----------------------
Crayon Shin-chan 3 (dumped)           Interactive Sampler (three different versions)
                                      Nuon Games + Demos (collection from Nuon-Dome)
                                      Nuon-Dome PhillyClassic 5 Demo Disc (give-away collection)
                                      Motorola BlackBird Demonstration Pack

There were also a number of homebrew titles made available.
It was noted that only the Samsung and RCA units can run homebrew software and the Samsung DVD-N501 is the most compatible unit
although news on May 29th 2022 states that the authentication keys were discovered so that's no longer a limitation and now all other
Nuon models should be able to run homebrew titles.
More NUON information can be found at https://www.nuon-dome.com


Assembled Unit Layout (for Samsung DVD-N501)
---------------------

Rear Plate: Samsung Digital Video Disc Player DVD-N501 XAA 2001.05
Front Identification: Samsung NUON Enhanced DVD Player / DVD-N501

                               (ANALOG   )   (DIGITAL  )
  |-------- VIDEO OUT--------| (AUDIO OUT)   (AUDIO OUT)
  Y Pr Pb    SVIDEO   COMPOSITE  L     R      COAX  OPTICAL
|-||-||-||-----||-------||-------||----||------||---||-----------------------|
| |                                                  |                       |
| |                                                  |  |------------------| |
| |                      |------------------------|  |  |                  | |
| |                      |                        |  |  |                  | |
| |   DVD EXTIVA JACK    |                        |  |  |                  | |
| | AH41-00322A REV:01   |                        |  |  |                  | |
| |                      |                        |  |  |                  | |
| |    (connectors       |     DVD DRIVE UNIT     |  |  |    DVD EXTIVA    | |
| |   and power supply)  |       ASSEMBLY         |  |  |     MAIN B/D     | |
| |                      |       (ABOVE)          |  |  |                  | |
| |                      |                        |  |  |                  | |
| |                      |                        |  |  |                  | |
| |                      |                        |  |  |                  | |
| |                      |                        |  |  |                  | |
| |                      |                        |  |  |                  | |
| |----------------------|------------------------|--|  |                  | |
| |---------------|      |                        |     |                  | |
| |PWR+LED+JOY BD |      |------------------------|     |------------------| |
| |---------------|          |---TRAY FACE-----|                             |
|--||-----||-||--------------|-----------------|-||---||---||---||----||--||-|
 POWER    JOYSTICK            VF DISPLAY       OPEN   FIT  PLAY STOP BACK FWD
 BUTTON   CONNECTORS              (BELOW)      CLOSE       PAUSE


PCB Info
--------

Power Board:
DVD EXTIVA JACK AH41-00322A REV:01

The JACK PCB contains these main parts....
All of the external jacks
Switch-Mode Power Supply
Vacuum-Fluorescent Display
NEC uPD78F0233 Micro-controller with 24kB internal flash ROM and 912 bytes internal RAM; 768b + 32b + 112b VFD RAM. Clock input 5.000MHz.
  - Labelled on PCB as FIC1
  - Stock NEC part pre-programmed at the factory as a VFD driver.
  - Processes the infrared signal from the remote.
  - Processes the front panel button presses.
    Note this IC very common and used in many models of DVD players.
5.000MHz Oscillator for the uPD78F0233
ROHM BA4560 Dual Operational Amplifier
ASAHI KASEI AK4382A 24-bit 2-channel DAC
Mitsumi MM1540A Video Output Driver which provides S-Video, Y Pb Pr Component Video and Composite Video. Labelled on PCB as VIC1


Main Board:
CODE NO : AH41-00324A REV. 00
DVD EXTIVA MAIN B/D 2001.03.27
TVE1-1E/0030CY (sticker)
|-------------------------------------------------------------|
|  |------|    MIC2          20MHz             33.8688MHz     |
|  | NIC7 |                |------|                        SW7|
|  |      |                | MIC1 |           |--------| SIC2 |
|  |------|         NIC2   |      |           |        |      |
|                          |------|           | SIC1   |   SW6|
|        |--------|                           |        |      |
|  NIC12 |  NIC1  |                           |--------|   SW5|
|        |        | NIC13                                     |
|        |--------|                                        SW4|
|                                                   |-----|   |
|       108MHz  |----|                     |-----|  |SIC3 |   |
|          NIC5 |VIC1|                     |RIC1 |  |-----|SW3|
|  NIC11        |----|CN9                  |-----|   DCN2     |
|           CN8        PCN1               DCN1        DCN3 SW2|
|-------------------------------------------------------------|
Notes:
      All parts have xICy location on the PCB.
      y is the IC# and x is for the IC function group.
      M for Main processor related parts
      N for NUON related parts
      S for Digital Servo/DSP related parts
      R for Laser pick-up / RF related parts
      V for Video related parts
      F for front panel related parts (see JACK PCB above)

      MIC1 - Toshiba TMP91C219F TLCS-900/L1-Series 16-bit Micro-controller. Clock input 20.000MHz
             Has internal 2kB RAM, no main ROM and 2kB boot ROM. A vanilla boot program may have been provided by default in all chips
             produced but it is unknown if the internal boot ROM has been programmed or is used/not used.
             On the PCB the BOOT pin is tied HIGH so the chip is set to run in Multi-Chip Mode. The datasheet states....
             "Multi-Chip Mode: After a reset, the device starts executing the external memory program."
             When BOOT is tied low the datasheet states....
             "Multi-Boot Mode: This mode is used to rewrite the external flash memory by serial transfer (UART) or ATAPI transfer.
             After a reset, internal boot program starts up, executing an on-board rewrite program."
             This suggests the boot ROM is only used for re-writing the flash ROM and possibly only used for factory programming or
             not used at all when using a common external EPROM.
             Since BOOT is hardwired high the boot ROM (if programmed) is not used when the DVD player operates normally and is therefore
             not required for emulation purposes.
             The Memory Map changes slightly depending on how the BOOT pin is set but basically they are identical except for the boot
             ROM memory location.

                                Multi-Chip Mode           Multi-Boot Mode
                                ---------------           ---------------
             000000h-000fffh    Internal I/O (4kB)        Same
             001000h-0017ffh    Internal RAM (2kB)        Same
             001800h-01f7ffh    External Memory           Same
             01f800h-01ffffh    Boot ROM (2kB)            External Memory
             020000h-fff7ffh    External Memory           Same
             fff800h-fffeffh    External Memory           Boot ROM (2kB)
             ffff00h-ffffffh    Vector Table (256 bytes)  Same

      MIC2 - AMIC A290021T 256kBx8-bit DIP32 Flash ROM (compatible with 29F020/28F020/27C020 etc). Dumped as DVD-N501_XAA_VER1.2.MIC2
      NIC1 - "NUON (M) XCMMP-L3CZP 0K93E ZKAA0102 TAIWAN" in BGA272 package. (M)=Motorola logo
             This is the NUON graphics co-processor. The specific revision in the Samsung DVD-N501 is ARIES 2.1 according to the schematics.
             Clock input 108.000MHz on pin 114 X-PLL_CLKI. The chip is noted on the internet as running internally at 54MHz [108/2]
             It was stated that a tiny boot-loader inside the chip loads part of the TSOP48 flash ROM into RAM then jumps to it.
NIC2/NIC12 - Samsung K4S641632D-TC80 1Mx16-bit x4 Banks (64Mbit) SDRAM
     NIC11 - Atmel AT49BV1614 2MBx8-bit (16Mbit) TSOP48 Flash ROM. Dumped as DVD-N501_XAA_VER1.2.NIC11
     NIC13 - Burr-Brown PLL1700E Multi-Clock Generator Phase Lock Loop (PLL). Master clock input 27.000MHz [108/4] is generated
             by NIC1 on pin 237 X_VCLK. Pin 2 is tied LOW so the chip is operated in 3-wire software mode.
      NIC5 - Samsung KS24C021 256byte x8-bit (2Kbit) Serial EEPROM. Dumped as DVD-N501_XAA_VER1.2.NIC5
             Data appears to be country/region, language and other audio/video settings. The default parental lock code 9999 is present at byte 0x44h.
      NIC7 - Samsung KS999F. Unknown purpose but appears to be a PLCC44 CPLD. Also marked 'ORBIT 61744 0117 U46373.3'
             The block diagram shows this chip as 'Sally' which is likely the code name of the same chip used on the Samsung DVD-N2000 Main PCB.
      VIC1 - Philips SAA7128H Digital Video Encoder. This chip is very common and used in many models of DVD players.
      RIC1 - Samsung S5L1462A01-Q0 KA1462X-Series RF Signal Processor. This receives the optical signal from the laser pick-up
             and produces the data-generating RF signal, the servo error signal for stable servo control and the monitor signal.
             This chip is very common and used in many models of DVD players.
      SIC1 - Samsung S5L1454A01-Q0 KA1454X-Series Digital Video Disc Player/Decoder, Digital Servo Controller and Digital Signal Processor (DSP)
             Clock input 33.8688MHz on pin 95. The datasheet block diagram states there is internal ROM and SRAM.
             The SRAM stores the digital servo internal data. The amount of internal RAM and ROM is unknown.
             This chip is very common and used in many models of DVD players.
      SIC2 - EliteMT M11B416256A-35J 256kB x16-bit (4Mbit) 72MHz EDO Page Mode DRAM
      SIC3 - Fairchild KA3017 Spindle + 4-Channel Motor Driver for tracking actuator, focus actuator, sled servo motor, tray motor
             and 3-phase BLDC spindle motor.
       CN8 - 35-pin connector joining to the JACK PCB (Digital Audio and Video Output Signals)
       CN9 - 7-pin connector joining to front panel joystick PCB
      DCN1 - 35-pin connector joining to DVD drive (Data from DVD & Motor/Servo Control Signals)
      DCN2 - 5-pin connector joining to DVD drive (Tray Motor)
      DCN3 - 3-pin connector for outputs from the front panel buttons joining to CN2 on the JACK PCB. These connect to the uPD78F0233 MCU.
      PCN1 - 10-pin power input connector joining to the JACK PCB
       SW2 - Push button for DVD tray Open/Close
       SW3 - Push button for Play/Pause                                \
       SW4 - Push button labeled 'FIT'. Stretches video to fill screen | SW# taken from schematic as all buttons
       SW5 - Push button for Stop                                      | on the PCB are labelled only 'SW3'
       SW6 - Push button for skipping chapters/tracks backward         |
       SW7 - Push button for skipping chapters/tracks forward          /

Note the same basic Samsung DVD chipset was used in many DVD players. For example Hitachi DV-P415U, which appears to be a near identical
unit with identical chips and near identical VFD (minus 'MP3') compared to the Samsung DVD-N501 but without the NUON features.

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


#include "emu.h"

#include "cpu/nuon/nuon.h"
#include "cpu/tlcs900/tmp95c061.h"
#include "cpu/upd78k/upd78k0.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

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

	void n501(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void main_map(address_map &map) ATTR_COLD;
};

uint32_t n5xx_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void n5xx_state::main_map(address_map &map)
{
	map(0xfc0000, 0xffffff).rom().region("maincpu", 0);
}


static INPUT_PORTS_START( n501 )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN )
	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 )
INPUT_PORTS_END


void n5xx_state::n501(machine_config &config)
{
	// basic machine hardware
	TMP95C061(config, m_maincpu, 20_MHz_XTAL); // actually TMP91C219F
	m_maincpu->set_addrmap(AS_PROGRAM, &n5xx_state::main_map);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER)); // TODO: all wrong
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(0));
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	screen.set_screen_update(FUNC(n5xx_state::screen_update));

	// NUON(config, "nuon", 108_MHz_XTAL); // ARIES 2.1, internally divided by 2

	UPD78053(config, "upd", 5_MHz_XTAL); // actually uPD78F0233

	// MM1540A

	// SAA7128H

	// S5L1462A01-Q0

	// S5L1454A01-Q0

	// sound hardware
	SPEAKER(config, "mono").front_center();

	// AK4382A

	SOFTWARE_LIST(config, "dvd_list").set_original("nuon");
}


ROM_START( n501 )
	ROM_REGION( 0x40000, "maincpu", 0 )
	ROM_LOAD( "dvd-n501_xaa_ver1.2.mic2", 0x00000, 0x40000, CRC(7308046b) SHA1(c33a77aa12908a9506495a67488349f536eee0b9) )

	ROM_REGION( 0x200000, "nuon", 0 )
	ROM_LOAD( "dvd-n501_xaa_ver1.2.nic11", 0x000000, 0x200000, CRC(0285411e) SHA1(cd8c1c210c3c15a2d88e508792c5fa58257aee43) )

	ROM_REGION( 0x6000, "upd", ROMREGION_ERASE00 )
	ROM_LOAD( "upd78f0233.flash", 0x0000, 0x6000, NO_DUMP ) // undumped internal flash ROM

	ROM_REGION( 0x100, "ks24c021", 0 ) // serial EEPROM
	ROM_LOAD( "dvd-n501_xaa_ver1.2.nic5",  0x000, 0x100, CRC(1afb3e8f) SHA1(7284b1fb1394842edb97fe2decaf0e71a0643b35) )
ROM_END

} // anonymous namespace


SYST( 2001, n501, 0, 0, n501, n501, n5xx_state, empty_init, "Samsung", "Samsung NUON Enhanced DVD Player / DVD-N501", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



dvk_kcgd.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************

    KCGD (Kontroller Cvetnogo Graficheskogo Displeya = Colour Graphics
    Display Controller), a replacement for KSM (dvk_ksm.c) in later
    models of DVK desktops.

    MPI (Q-Bus clone) board. Interfaces with MS7004 (DEC LK201 workalike)
    keyboard, mouse, and monochrome or color CRT.  Host interface is a
    serial port; there is no direct framebuffer access.

    VRAM is 128K and is word-addressable, so address fits into 16 bits.
    Lower 32K of VRAM are not used to store pixel data.

    To do:
    - K1801VM2 CPU core (interrupts and EVNT pin, full EIS set, other insns)
    - verify hsync/vsync frequencies
    - interlace
    - scanline table selection
    - interrupts
    - mouse
    - mono/color CRT
    - KeyGP ROM

    Hardware notes:
    - PBA3.660.259TO manual (1987) does not document mouse interface
    - PBA3.660.259E3 schematic (KCGD_E3-PE3.djvu) has mouse interface
        but specifies ROM "181" in parts list?
    - MPSS journal article (1988) does document mouse interface
    - ROM "181" does not use rx interrupts, has no setup menu
    - ROM "182" uses? 500 hz timer, rx interrupts, interlace mode
    - add-on "KeyGP" ROM supports "save settings" -- to where?

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

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/t11/t11.h"
#include "machine/dl11.h"
#include "ms7004.h"
#include "machine/timer.h"
#include "emupal.h"
#include "screen.h"


#define LOG_VRAM      (1U << 1)
#define LOG_DEBUG     (1U << 2)

//#define VERBOSE (LOG_DEBUG)
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"

#define LOGVRAM(...) LOGMASKED(LOG_VRAM, __VA_ARGS__)
#define LOGDBG(...) LOGMASKED(LOG_DEBUG, __VA_ARGS__)


namespace {

// these are unverified
static constexpr int KCGD_TOTAL_HORZ = 977;
static constexpr int KCGD_DISP_HORZ = 800;
static constexpr int KCGD_HORZ_START = 0;

static constexpr int KCGD_TOTAL_VERT = 525;
static constexpr int KCGD_DISP_VERT = 480;
static constexpr int KCGD_VERT_START = 0;

static constexpr int KCGD_PAGE_0 = 015574;
static constexpr int KCGD_PAGE_1 = 005574;


class kcgd_state : public driver_device
{
public:
	kcgd_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_dl11host(*this, "dl11host")
		, m_rs232(*this, "rs232")
		, m_dl11kbd(*this, "dl11kbd")
		, m_ms7004(*this, "ms7004")
		, m_palette(*this, "palette")
		, m_screen(*this, "screen")
	{ }

	void kcgd(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

	//TIMER_CALLBACK_MEMBER(vsync_tick);
	TIMER_CALLBACK_MEMBER(toggle_500hz);

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	TIMER_DEVICE_CALLBACK_MEMBER(scanline_callback);
	void kcgd_palette(palette_device &palette) const;

	enum status_bits : unsigned
	{
		KCGD_STATUS_PAGE = 0,
		KCGD_STATUS_INTERLACE = 1,
		KCGD_STATUS_TIMER_INT = 5,
		KCGD_STATUS_MODE_INT = 6,
		KCGD_STATUS_MODE_LAST = 7,
		KCGD_STATUS_TIMER_VAL = 15
	};

	uint16_t vram_addr_r();
	uint16_t vram_data_r();
	uint16_t vram_mmap_r(offs_t offset);
	void vram_addr_w(uint16_t data);
	void vram_data_w(uint16_t data);
	void vram_mmap_w(offs_t offset, uint16_t data);
	uint16_t status_r();
	void status_w(uint16_t data);
	uint8_t palette_index_r();
	uint8_t palette_data_r();
	void palette_index_w(uint8_t data);
	void palette_data_w(uint8_t data);

	//emu_timer *m_vsync_on_timer;
	emu_timer *m_500hz_timer = nullptr;

	void kcgd_mem(address_map &map) ATTR_COLD;

	void draw_scanline(uint16_t *p, uint16_t offset);
	rectangle m_tmpclip;
	bitmap_ind16 m_tmpbmp;

	struct
	{
		uint16_t status = 0; // 167770
		uint8_t control = 0; // 167772
		int palette_index = 0, vram_addr = 0;
		uint8_t palette[16]{};
	} m_video;
	std::unique_ptr<uint32_t[]> m_videoram;

protected:
	required_device<k1801vm2_device> m_maincpu;
	required_device<k1801vp065_device> m_dl11host;
	required_device<rs232_port_device> m_rs232;
	required_device<k1801vp065_device> m_dl11kbd;
	required_device<ms7004_device> m_ms7004;
	required_device<palette_device> m_palette;
	required_device<screen_device> m_screen;
};

void kcgd_state::kcgd_mem(address_map &map)
{
	map.unmap_value_high();
	map(0000000, 0077777).rw(FUNC(kcgd_state::vram_mmap_r), FUNC(kcgd_state::vram_mmap_w));
	map(0100000, 0157777).rom();
	// 1802VV1 chips
	map(0160000, 0160001).mirror(03774).rw(FUNC(kcgd_state::vram_addr_r), FUNC(kcgd_state::vram_addr_w));
	map(0160002, 0160003).mirror(03774).rw(FUNC(kcgd_state::vram_data_r), FUNC(kcgd_state::vram_data_w));
	// 1801VP1-033 "pic" chip
	map(0167770, 0167771).rw(FUNC(kcgd_state::status_r), FUNC(kcgd_state::status_w));
	map(0167772, 0167772).rw(FUNC(kcgd_state::palette_index_r), FUNC(kcgd_state::palette_index_w)); // reads always return 0
	map(0167773, 0167773).rw(FUNC(kcgd_state::palette_data_r), FUNC(kcgd_state::palette_data_w));
	// 1801VP1-065 chips, not 100% DL11 compatible (error bits are in RCSR, not RBUF)
	map(0176560, 0176567).rw(m_dl11host, FUNC(dl11_device::read), FUNC(dl11_device::write));
	map(0177560, 0177567).rw(m_dl11kbd, FUNC(dl11_device::read), FUNC(dl11_device::write));
}

// future
static const z80_daisy_config daisy_chain[] =
{
//  { "pic" },
	{ "dl11kbd" },
	{ "dl11host" },
	{ nullptr }
};

static DEVICE_INPUT_DEFAULTS_START( host_rs232_defaults )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_57600 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_57600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
	DEVICE_INPUT_DEFAULTS( "FLOW_CONTROL", 0x07, 0x01 )
DEVICE_INPUT_DEFAULTS_END


/*
TIMER_CALLBACK_MEMBER(kcgd_state::vsync_tick)
{
    m_maincpu->set_input_line(INPUT_LINE_EVNT, ASSERT_LINE);
}
*/

TIMER_CALLBACK_MEMBER(kcgd_state::toggle_500hz)
{
	m_video.status ^= (1 << KCGD_STATUS_TIMER_VAL);
}

void kcgd_state::machine_reset()
{
	m_video = decltype(m_video)();
}

void kcgd_state::machine_start()
{
	// 64 kwords, word size is 17 bits
	m_videoram = std::make_unique<uint32_t[]>(65536);

	m_tmpclip = rectangle(0, KCGD_DISP_HORZ - 1, 0, KCGD_DISP_VERT - 1);
	m_tmpbmp.allocate(KCGD_DISP_HORZ, KCGD_DISP_VERT);
// future
//  m_vsync_on_timer = timer_alloc(FUNC(kcgd_state::vsync_tick), this);
//  m_vsync_on_timer->adjust(m_screen->time_until_pos(0, 0), 0, m_screen->frame_period());
	m_500hz_timer = timer_alloc(FUNC(kcgd_state::toggle_500hz), this);
	m_500hz_timer->adjust(attotime::from_hz(500), 0, attotime::from_hz(500));
}

void kcgd_state::kcgd_palette(palette_device &palette) const
{
	// for debugging only -- palette is overwritten by firmware
	for (int i = 0; i < 16; i++)
		palette.set_pen_color(i, i ? i : 255, i ? i : 255, i ? i : 255);
}

void kcgd_state::vram_addr_w(uint16_t data)
{
	LOGDBG("VRAM WA %06o\n", data);
	m_video.vram_addr = data;
}

uint16_t kcgd_state::vram_addr_r()
{
	LOGDBG("VRAM RA %06o\n", m_video.vram_addr);
	return m_video.vram_addr;
}

void kcgd_state::vram_data_w(uint16_t data)
{
	LOGVRAM("VRAM W2 %06o <- %04XH\n", m_video.vram_addr, data);
	m_videoram[m_video.vram_addr] = data | (BIT(m_video.control, 7) << 16);
}

uint16_t kcgd_state::vram_data_r()
{
	LOGVRAM("VRAM R2 %06o\n", m_video.vram_addr);
	m_video.status = (m_video.status & 0xff7f) | (BIT(m_videoram[m_video.vram_addr], 16) << 7);
	return (uint16_t)(m_videoram[m_video.vram_addr] & 0xffff);
}

void kcgd_state::vram_mmap_w(offs_t offset, uint16_t data)
{
	LOGVRAM("VRAM W1 %06o <- %04XH\n", offset, data);
	m_videoram[offset] = data | (BIT(m_video.control, 7) << 16);
}

uint16_t kcgd_state::vram_mmap_r(offs_t offset)
{
	LOGVRAM("VRAM R1 %06o\n", offset);
	return (uint16_t)m_videoram[offset];
}

void kcgd_state::status_w(uint16_t data)
{
	LOG("Status W data %04XH (useful %02XH)\n", data, data & 0x63);
	// bits 7 and 15 are read-only
	m_video.status = (m_video.status & 0x8080) | (data & 0x7f7f);
}

uint16_t kcgd_state::status_r()
{
	uint16_t data = m_video.status ^ (BIT(m_video.control, 6) << 7);
	LOG("Status R data %04X index %d\n", data, m_video.palette_index);
	return data;
}

void kcgd_state::palette_index_w(uint8_t data)
{
	m_video.control = data;
	m_video.palette_index = ((data >> 2) & 15);
	LOG("Palette index, Control W data %02XH index %d\n", data, m_video.palette_index);
}

void kcgd_state::palette_data_w(uint8_t data)
{
	LOG("Palette data W data %02XH index %d\n", data, m_video.palette_index);
	m_video.palette[m_video.palette_index] = data;
	m_palette->set_pen_color(m_video.palette_index,
		85*(data & 3), 85*((data >> 2) & 3), 85*((data >> 4) & 3));
}

uint8_t kcgd_state::palette_index_r()
{
	return 0;
}

uint8_t kcgd_state::palette_data_r()
{
	LOG("Palette data R index %d\n", m_video.palette_index);
	return m_video.palette[m_video.palette_index];
}

/*
    Raster sizes:
    - 800(400)x480 in hires(lores) 60 Hz interlaced mode
    - 800(400)x240 in hires(lores) 30 Hz progressive mode

    Video memory is 17 bits wide. Bit 16 indicates hi/lo res mode for each word,
    host writes it separately (via bit 7 in 167772).
*/

void kcgd_state::draw_scanline(uint16_t *p, uint16_t offset)
{
	int i;

	for (i = 0; i < 100; i++)
	{
		uint32_t data = m_videoram[offset++];
		if (BIT(data, 16))
		{
			*p = ( data >> 12) & 0x0F; p++;
			*p = ( data >> 12) & 0x0F; p++;
			*p = ( data >> 8 ) & 0x0F; p++;
			*p = ( data >> 8 ) & 0x0F; p++;
			*p = ( data >> 4 ) & 0x0F; p++;
			*p = ( data >> 4 ) & 0x0F; p++;
			*p =   data        & 0x0F; p++;
			*p =   data        & 0x0F; p++;
		}
		else
		{
			*p = 5*(( data >> 14) & 0x03); p++;
			*p = 5*(( data >> 12) & 0x03); p++;
			*p = 5*(( data >> 10) & 0x03); p++;
			*p = 5*(( data >> 8 ) & 0x03); p++;
			*p = 5*(( data >> 6 ) & 0x03); p++;
			*p = 5*(( data >> 4 ) & 0x03); p++;
			*p = 5*(( data >> 2 ) & 0x03); p++;
			*p = 5*(  data        & 0x03); p++;
		}
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(kcgd_state::scanline_callback)
{
	uint16_t y = m_screen->vpos();

	if (y < KCGD_VERT_START) return;
	y -= KCGD_VERT_START;
	if (y >= KCGD_DISP_VERT) return;

	uint16_t const offset = BIT(m_video.status, KCGD_STATUS_PAGE) ? (KCGD_PAGE_1 >> 1) : (KCGD_PAGE_0 >> 1);

	LOGDBG("scanline_cb frame %d y %.3d page %d offset %04X *offset %04X\n",
		m_screen->frame_number(), BIT(m_video.status, KCGD_STATUS_PAGE),
		y, offset + y, m_videoram[offset + y]);

	draw_scanline(&m_tmpbmp.pix(y), m_videoram[offset + (KCGD_DISP_VERT - 1) - y]);
}

uint32_t kcgd_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	copybitmap(bitmap, m_tmpbmp, 0, 0, KCGD_HORZ_START, KCGD_VERT_START, cliprect);
	return 0;
}

/* F4 Character Displayer */
static const gfx_layout kcgd_charlayout =
{
	8, 10,                  /* 8x10 pixels */
	256,                    /* 256 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8 },
	8*10                    /* every char takes 10 bytes */
};

static GFXDECODE_START( gfx_kcgd )
	GFXDECODE_ENTRY("maincpu", 0112236, kcgd_charlayout, 0, 1)
GFXDECODE_END

void kcgd_state::kcgd(machine_config &config)
{
	K1801VM2(config, m_maincpu, XTAL(30'800'000) / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &kcgd_state::kcgd_mem);
	m_maincpu->set_initial_mode(0100000);
// future
//  m_maincpu->set_daisy_config(daisy_chain);

	timer_device &scantimer(TIMER(config, "scantimer"));
	scantimer.configure_periodic(FUNC(kcgd_state::scanline_callback), attotime::from_hz(50 * 28 * 11));
	scantimer.set_start_delay(attotime::from_ticks(KCGD_HORZ_START, XTAL(30'800'000)));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_screen_update(FUNC(kcgd_state::screen_update));
	m_screen->set_raw(XTAL(30'800'000), KCGD_TOTAL_HORZ, KCGD_HORZ_START,
		KCGD_HORZ_START+KCGD_DISP_HORZ, KCGD_TOTAL_VERT, KCGD_VERT_START,
		KCGD_VERT_START+KCGD_DISP_VERT);
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(kcgd_state::kcgd_palette), 16);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_kcgd);

	K1801VP065(config, m_dl11host, XTAL(4'608'000));
	m_dl11host->set_rxc(57600);
	m_dl11host->set_txc(57600);
	m_dl11host->set_rxvec(0360);
	m_dl11host->set_txvec(0364);
	m_dl11host->txd_wr_callback().set(m_rs232, FUNC(rs232_port_device::write_txd));
	m_dl11host->rts_wr_callback().set(m_rs232, FUNC(rs232_port_device::write_rts));
	m_dl11host->txrdy_wr_callback().set_inputline(m_maincpu, t11_device::VEC_LINE);
	m_dl11host->rxrdy_wr_callback().set_inputline(m_maincpu, t11_device::VEC_LINE);

	RS232_PORT(config, m_rs232, default_rs232_devices, "null_modem");
	m_rs232->rxd_handler().set(m_dl11host, FUNC(dl11_device::rx_w));
	m_rs232->set_option_device_input_defaults("null_modem", DEVICE_INPUT_DEFAULTS_NAME(host_rs232_defaults));

	K1801VP065(config, m_dl11kbd, XTAL(4'608'000));
	m_dl11kbd->set_rxc(4800);
	m_dl11kbd->set_txc(4800);
	m_dl11kbd->set_rxvec(060);
	m_dl11kbd->set_txvec(064);
	m_dl11kbd->txd_wr_callback().set(m_ms7004, FUNC(ms7004_device::write_rxd));
	m_dl11kbd->txrdy_wr_callback().set_inputline(m_maincpu, t11_device::VEC_LINE);
	m_dl11kbd->rxrdy_wr_callback().set_inputline(m_maincpu, t11_device::VEC_LINE);

	MS7004(config, m_ms7004, 0);
	m_ms7004->tx_handler().set(m_dl11kbd, FUNC(dl11_device::rx_w));
}

ROM_START( dvk_kcgd )
	ROM_REGION16_BE(0x100000, "maincpu", ROMREGION_ERASE00)
	ROM_DEFAULT_BIOS("181")
	ROM_SYSTEM_BIOS(0, "181", "mask 181")
	ROMX_LOAD("kr1801re2-181.bin", 0100000, 020000, CRC(acac124f) SHA1(412c3eb71bece6f791fc5a9d707cf4692fd0b45b), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "182", "mask 182")
	ROMX_LOAD("kr1801re2-182.bin", 0100000, 020000, CRC(3ca2921a) SHA1(389b30c40ed7e41dae71d58c7bff630359a48153), ROM_BIOS(1))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY  FULLNAME    FLAGS */
COMP( 1987, dvk_kcgd, 0,      0,      kcgd,    0,     kcgd_state, empty_init, "USSR",  "DVK KCGD", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



dvk_ksm.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************

    KSM (Kontroller Simvolnogo Monitora = Character Display Controller),
    a single-board replacement for standalone 15IE-00-013 terminal (ie15.c
    driver in MAME) in later-model DVK desktops.

    MPI (Q-Bus clone) board, consumes only power from the bus.
    Interfaces with MS7004 (DEC LK201 workalike) keyboard and monochrome CRT.

    Emulates a VT52 without copier (ESC Z response is ESC / M), with
    Hold Screen mode and Graphics character set (but it is unique and
    mapped to a different range -- 100..137).

    F4 + 0..9 on numeric keypad = setup mode.  0 changes serial port speed,
    1..9 toggle one of mode bits:

    1   XON/XOFF    0: Off  1: On
    2   Character set   0: N0/N1  2: N2
    3   Auto LF     0: Off  1: On
    4   Auto repeat 0: On  1: Off
    5   Auto wraparound 0: On  1: Off
    6   Interpret controls  0: Interpret  1: Display
    7   Parity check    0: Off  1: On
    8   Parity bits 0: None  1: Even
    9   Stop bits

    N0/N1 charset has regular ASCII in C0 page and Cyrillic in C1 page,
    switching between them via SI/SO.   N2 charset has uppercase Cyrillic
    chars in place of lowercase Latin ones.

    ESC toggles Cyrillic/Latin mode (depends in the host's terminal driver)
    F1 toggles Hold Screen mode (also depends in the host's terminal driver)
    F9 resets terminal (clears memory).
    F20 toggles on/off-line mode.

    Terminfo description:

ksm|DVK KSM,
    am, bw, dch1=\EP, ich1=\EQ,
    acsc=hRiTjXkClJmFnNqUtEuPv\174wKxW.M\054Q\055S\053\136~_{@}Z0\177,
    use=vt52,

    To do:
    - verify if pixel stretching is done by hw
    - verify details of hw revisions.  known ones:
      - decimal 7.102.076 -- has DIP switches, model name "KSM".
      - decimal 7.102.228 -- no DIP switches, model name "KSM-01" -- no dump.

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

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/i8085/i8085.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "ms7004.h"
#include "machine/pic8259.h"
#include "machine/timer.h"

#include "emupal.h"
#include "screen.h"


#define LOG_KEYBOARD  (1U << 1)
#define LOG_DEBUG     (1U << 2)

//#define VERBOSE (LOG_DEBUG)
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"

#define LOGKBD(...) LOGMASKED(LOG_KEYBOARD, __VA_ARGS__)
#define LOGDBG(...) LOGMASKED(LOG_DEBUG, __VA_ARGS__)


namespace {

static constexpr int SCREEN_PAGE = 80 * 48;

static constexpr int KSM_TOTAL_HORZ = 1000;
static constexpr int KSM_DISP_HORZ = 800;
static constexpr int KSM_HORZ_START = 200;

static constexpr int KSM_TOTAL_VERT = (28 * 11);
static constexpr int KSM_DISP_VERT = (25 * 11);
static constexpr int KSM_VERT_START = (2 * 11);

static constexpr int KSM_STATUSLINE_TOTAL = 11;
static constexpr int KSM_STATUSLINE_VRAM = 0xF8B0;


class ksm_state : public driver_device
{
public:
	ksm_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_p_videoram(*this, "videoram")
		, m_maincpu(*this, "maincpu")
		, m_pic8259(*this, "pic8259")
		, m_i8251line(*this, "i8251line")
		, m_rs232(*this, "rs232")
		, m_i8251kbd(*this, "i8251kbd")
		, m_ms7004(*this, "ms7004")
		, m_screen(*this, "screen")
		, m_p_chargen(*this, "chargen")
	{ }

	void ksm(machine_config &config);

private:
	TIMER_DEVICE_CALLBACK_MEMBER(scanline_callback);

	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	TIMER_CALLBACK_MEMBER(clock_brg);

	void write_keyboard_clock(int state);

	void write_brga(int state);
	void write_brgb(int state);
	void write_brgc(int state);

	void ksm_ppi_porta_w(uint8_t data);
	void ksm_ppi_portc_w(uint8_t data);

	void ksm_io(address_map &map) ATTR_COLD;
	void ksm_mem(address_map &map) ATTR_COLD;

	uint32_t draw_scanline(uint16_t *p, uint16_t offset, uint8_t scanline);
	rectangle m_tmpclip;
	bitmap_ind16 m_tmpbmp;

	struct
	{
		uint8_t line = 0;
		uint16_t ptr = 0;
	} m_video;

	bool brg_state = false;
	int brga = 0, brgb = 0, brgc = 0;
	emu_timer *m_brg = nullptr;

	void update_brg(bool a, bool b, int c);

	required_shared_ptr<uint8_t> m_p_videoram;
	required_device<i8080_cpu_device> m_maincpu;
	required_device<pic8259_device> m_pic8259;
	required_device<i8251_device> m_i8251line;
	required_device<rs232_port_device> m_rs232;
	required_device<i8251_device> m_i8251kbd;
	required_device<ms7004_device> m_ms7004;
	required_device<screen_device> m_screen;
	required_region_ptr<u8> m_p_chargen;
};

void ksm_state::ksm_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).rom();
	map(0x2000, 0x20ff).ram().mirror(0x0700);
	map(0xc000, 0xffff).ram().share("videoram");
}

void ksm_state::ksm_io(address_map &map)
{
	map.unmap_value_high();
	map(0x5e, 0x5f).rw(m_pic8259, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x6e, 0x6f).rw(m_i8251kbd, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x76, 0x77).rw(m_i8251line, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x78, 0x7b).rw("ppi8255", FUNC(i8255_device::read), FUNC(i8255_device::write));
}

/* Input ports */
static INPUT_PORTS_START( ksm )
	PORT_START("SA1")
	PORT_DIPNAME(0x01, 0x01, "Stop bits")
	PORT_DIPSETTING(0x00, "2 bits")
	PORT_DIPSETTING(0x01, "1 bit")
	PORT_DIPNAME(0x02, 0x00, "Parity bits")
	PORT_DIPSETTING(0x00, "0 bits")
	PORT_DIPSETTING(0x02, "1 bit")
	PORT_DIPNAME(0x04, 0x00, "Parity check")
	PORT_DIPSETTING(0x00, "Off")
	PORT_DIPSETTING(0x04, "On")
	PORT_DIPNAME(0x08, 0x00, "Interpret controls")
	PORT_DIPSETTING(0x00, "Interpret")
	PORT_DIPSETTING(0x08, "Display")
	PORT_DIPNAME(0x10, 0x00, "Auto wraparound")
	PORT_DIPSETTING(0x00, "On")
	PORT_DIPSETTING(0x10, "Off")
	PORT_DIPNAME(0x20, 0x00, "Auto repeat")
	PORT_DIPSETTING(0x00, "On")
	PORT_DIPSETTING(0x20, "Off")
	PORT_DIPNAME(0x40, 0x00, "Auto CR/LF")
	PORT_DIPSETTING(0x00, "Off")
	PORT_DIPSETTING(0x40, "On")
	PORT_DIPNAME(0x80, 0x00, "Character set")
	PORT_DIPSETTING(0x00, "KOI-8 N0/N1")
	PORT_DIPSETTING(0x80, "KOI-8 N2")
	PORT_START("SA2")
	PORT_DIPNAME(0x01, 0x00, "XON/XOFF")
	PORT_DIPSETTING(0x00, "Off")
	PORT_DIPSETTING(0x01, "On")
	PORT_DIPNAME(0x0E, 0x00, "Baud rate")
	PORT_DIPSETTING(0x00, "9600")
	PORT_DIPSETTING(0x02, "4800")
	PORT_DIPSETTING(0x04, "2400")
	PORT_DIPSETTING(0x06, "1200")
	PORT_DIPSETTING(0x08, "600")
	PORT_DIPSETTING(0x0A, "300")
	PORT_DIPSETTING(0x0C, "150")
	PORT_DIPSETTING(0x0E, "75")
INPUT_PORTS_END

TIMER_CALLBACK_MEMBER(ksm_state::clock_brg)
{
	brg_state = !brg_state;
	m_i8251line->write_txc(brg_state);
	m_i8251line->write_rxc(brg_state);
}

void ksm_state::machine_reset()
{
	m_video = decltype(m_video)();
	brga = 0;
	brgb = 0;
	brgc = 0;
	brg_state = 0;
}

void ksm_state::machine_start()
{
	m_tmpclip = rectangle(0, KSM_DISP_HORZ - 1, 0, KSM_DISP_VERT - 1);
	m_tmpbmp.allocate(KSM_DISP_HORZ, KSM_DISP_VERT);

	m_brg = timer_alloc(FUNC(ksm_state::clock_brg), this);
}

void ksm_state::ksm_ppi_porta_w(uint8_t data)
{
	LOG("PPI port A line %d\n", data);
	m_video.line = data;
}

void ksm_state::ksm_ppi_portc_w(uint8_t data)
{
	brgc = (data >> 5) & 3;

	LOG("PPI port C raw %02x blink %d speed %d\n", data, BIT(data, 7), brgc);

	update_brg(brga, brgb, brgc);
}

void ksm_state::write_keyboard_clock(int state)
{
	m_i8251kbd->write_txc(state);
	m_i8251kbd->write_rxc(state);
}

void ksm_state::write_brga(int state)
{
	brga = state;
	update_brg(brga, brgb, brgc);
}

void ksm_state::write_brgb(int state)
{
	brgb = state;
	update_brg(brga, brgb, brgc);
}

void ksm_state::update_brg(bool a, bool b, int c)
{
	LOGDBG("brg %d %d %d\n", a, b, c);

	if (a && b) return;

	switch ((a << 3) + (b << 2) + c)
	{
	case 0xa:
		m_brg->adjust(attotime::from_hz(9600*16*2), 0, attotime::from_hz(9600*16*2));
		break;

	case 0x8:
		m_brg->adjust(attotime::from_hz(4800*16*2), 0, attotime::from_hz(4800*16*2));
		break;

	case 0x4:
		m_brg->adjust(attotime::from_hz(2400*16*2), 0, attotime::from_hz(2400*16*2));
		break;

	case 0x5:
		m_brg->adjust(attotime::from_hz(1200*16*2), 0, attotime::from_hz(1200*16*2));
		break;

	case 0x6:
		m_brg->adjust(attotime::from_hz(600*16*2), 0, attotime::from_hz(600*16*2));
		break;

	case 0x7:
		m_brg->adjust(attotime::from_hz(300*16*2), 0, attotime::from_hz(300*16*2));
		break;
	}
}

/*
    Raster size is 28x11 scan lines.
    XXX VBlank is active for 2 topmost on-screen rows and 1 at the bottom.

    Usable raster is 800 x 275 pixels (80 x 25 characters).  24 lines are
    available to the user and 25th (topmost) line is the status line.
    Status line displays current serial port speed and 9 setup bits.

    No character attributes are available, but in 'display controls' mode
    control characters stored in memory are shown as blinking chars.

    Character cell is 10 x 11; character generator provides 7 x 8 of that.
    3 extra horizontal pixels are always XXX blank.  Blinking XXX cursor may be
    displayed on 3 extra scan lines.
*/

uint32_t ksm_state::draw_scanline(uint16_t *p, uint16_t offset, uint8_t scanline)
{
	uint8_t gfx, fg, bg, ra, blink;
	uint16_t x, chr;

	bg = 0;
	fg = 1;
	ra = scanline % 8;
	blink = (m_screen->frame_number() % 10) > 4;
	if (scanline > 7)
	{
		offset -= 0x2000;
	}

	for (x = offset; x < offset + 80; x++)
	{
		chr = m_p_videoram[x] << 3;
		gfx = m_p_chargen[chr | ra];

		if ((scanline > 7 && blink) || ((chr < (0x20 << 3)) && !blink)) gfx = 0;

		for (int i = 6; i >= 0; i--)
		{
			*p++ = BIT(gfx, i) ? fg : bg;
		}
		*p++ = bg;
		*p++ = bg;
		*p++ = bg;
	}
	return 0;
}

TIMER_DEVICE_CALLBACK_MEMBER(ksm_state::scanline_callback)
{
	uint16_t y = m_screen->vpos();

	LOGDBG("scanline_cb addr %02x frame %d x %.4d y %.3d row %.2d\n",
		m_video.line, (int)m_screen->frame_number(), m_screen->hpos(), y, y%11);

	if (y < KSM_VERT_START) return;
	y -= KSM_VERT_START;
	if (y >= KSM_DISP_VERT) return;

	uint16_t offset;
	if (y < KSM_STATUSLINE_TOTAL)
	{
		offset = KSM_STATUSLINE_VRAM - 0xC000;
	}
	else
	{
		offset = 0x2000 + 0x30 + (((m_video.line + y / 11 - 1) % 48) << 7);
	}

	draw_scanline(&m_tmpbmp.pix(y), offset, y % 11);
}

uint32_t ksm_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	copybitmap(bitmap, m_tmpbmp, 0, 0, KSM_HORZ_START, KSM_VERT_START, cliprect);
	return 0;
}


/* F4 Character Displayer */
static const gfx_layout ksm_charlayout =
{
	7, 8,                   /* 7x8 pixels in 10x11 cell */
	256,                    /* 256 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_ksm )
	GFXDECODE_ENTRY("chargen", 0x0000, ksm_charlayout, 0, 1)
GFXDECODE_END

void ksm_state::ksm(machine_config &config)
{
	I8080(config, m_maincpu, XTAL(15'400'000) / 10);
	m_maincpu->set_addrmap(AS_PROGRAM, &ksm_state::ksm_mem);
	m_maincpu->set_addrmap(AS_IO, &ksm_state::ksm_io);
	m_maincpu->in_inta_func().set("pic8259", FUNC(pic8259_device::acknowledge));

	TIMER(config, "scantimer").configure_scanline(FUNC(ksm_state::scanline_callback), "screen", 0, 1);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER, rgb_t::green());
	m_screen->set_screen_update(FUNC(ksm_state::screen_update));
	m_screen->set_raw(XTAL(15'400'000), KSM_TOTAL_HORZ, KSM_HORZ_START,
		KSM_HORZ_START+KSM_DISP_HORZ, KSM_TOTAL_VERT, KSM_VERT_START,
		KSM_VERT_START+KSM_DISP_VERT);
	m_screen->set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_ksm);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	PIC8259(config, m_pic8259, 0);
	m_pic8259->out_int_callback().set_inputline(m_maincpu, 0);

	// D30
	i8255_device &ppi(I8255(config, "ppi8255"));
	ppi.out_pa_callback().set(FUNC(ksm_state::ksm_ppi_porta_w));
	ppi.in_pb_callback().set_ioport("SA1");
	ppi.in_pc_callback().set_ioport("SA2");
	ppi.out_pc_callback().set(FUNC(ksm_state::ksm_ppi_portc_w));

	// D42 - serial connection to host
	I8251(config, m_i8251line, 0);
	m_i8251line->txd_handler().set(m_rs232, FUNC(rs232_port_device::write_txd));
	m_i8251line->rxrdy_handler().set(m_pic8259, FUNC(pic8259_device::ir3_w));

	RS232_PORT(config, m_rs232, default_rs232_devices, "null_modem");
	m_rs232->rxd_handler().set(m_i8251line, FUNC(i8251_device::write_rxd));
	m_rs232->cts_handler().set(m_i8251line, FUNC(i8251_device::write_cts));
	m_rs232->dsr_handler().set(m_i8251line, FUNC(i8251_device::write_dsr));

	// D41 - serial connection to MS7004 keyboard
	I8251(config, m_i8251kbd, 0);
	m_i8251kbd->rxrdy_handler().set(m_pic8259, FUNC(pic8259_device::ir1_w));
	m_i8251kbd->rts_handler().set(FUNC(ksm_state::write_brga));
	m_i8251kbd->dtr_handler().set(FUNC(ksm_state::write_brgb));

	MS7004(config, m_ms7004, 0);
	m_ms7004->tx_handler().set(m_i8251kbd, FUNC(i8251_device::write_rxd));

	clock_device &keyboard_clock(CLOCK(config, "keyboard_clock", 4800 * 16));
	keyboard_clock.signal_handler().set(FUNC(ksm_state::write_keyboard_clock));
}

ROM_START( dvk_ksm )
	ROM_REGION(0x1000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD( "ksm_04_rom0_d32.bin", 0x0000, 0x0800, CRC(6ad62715) SHA1(20f8f95119bc7fc6e0f16c67864e339a86edb44d))
	ROM_LOAD( "ksm_05_rom1_d33.bin", 0x0800, 0x0800, CRC(5b29bcd2) SHA1(1f4f82c2f88f1e8615ec02076559dc606497e654))

	ROM_REGION(0x0800, "chargen", ROMREGION_ERASE00)
	ROM_LOAD("ksm_03_cg_d31.bin", 0x0000, 0x0800, CRC(6a8477e2) SHA1(c7871a96f135db05c3c8d718fbdf1728e22e72b7))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY  FULLNAME   FLAGS */
COMP( 1986, dvk_ksm, 0,      0,      ksm,     ksm,   ksm_state, empty_init, "USSR",  "DVK KSM", 0 )



e100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Joakim Larsson Edstrom
/*
 *
 * History of Didact
 *------------------
 * See didact.cpp
 *
 * The Esselte 100 was an original design with a CRT and a full Keyboard that also had a BASIC interpreter
 * extended with commands suitable for educational experiments using the expansion bus and its built in
 * io control capabilities.
 *
 * The Esselte 1000 was an educational package based on Apple II plus software and literature but the relation
 * to Didact is at this point unknown so it is probably a pure Esselte software production. If this branded
 * distribution is recovered it will be added as a clone of the Apple II driver or just as softlist item.
 *
 * Misc links about the boards supported by this driver.
 *-----------------------------------------------------
 * http://elektronikforumet.com/forum/download/file.php?id=63988&mode=view
 * http://elektronikforumet.com/forum/viewtopic.php?f=2&t=79576&start=150#p1203915
 *
 *  TODO:
 * -------------------------------
 *  - Dump more ROM:s
 *  - Keyboard for early rev PCB
 *  - Expansion bus
 *  - Expansion overlay
 *  - Serial
 ****************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/m6800/m6800.h"
#include "imagedev/cassette.h"
#include "machine/6821pia.h"
#include "machine/74145.h"
#include "machine/timer.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

#define LOG_SETUP   (1U << 1)
#define LOG_SCAN    (1U << 2)
#define LOG_BANK    (1U << 3)
#define LOG_SCREEN  (1U << 4)
#define LOG_READ    (1U << 5)
#define LOG_CS      (1U << 6)

//#define VERBOSE (LOG_READ | LOG_GENERAL | LOG_SETUP | LOG_BANK)
//#define LOG_OUTPUT_FUNC printf
#include "logmacro.h"

#define LOGSETUP(...)   LOGMASKED(LOG_SETUP,   __VA_ARGS__)
#define LOGSCAN(...)    LOGMASKED(LOG_SCAN,    __VA_ARGS__)
#define LOGBANK(...)    LOGMASKED(LOG_BANK,    __VA_ARGS__)
#define LOGSCREEN(...)  LOGMASKED(LOG_SCREEN,  __VA_ARGS__)
#define LOGR(...)       LOGMASKED(LOG_READ,    __VA_ARGS__)
#define LOGCS(...)      LOGMASKED(LOG_CS,      __VA_ARGS__)

#ifdef _MSC_VER
#define FUNCNAME __func__
#else
#define FUNCNAME __PRETTY_FUNCTION__
#endif


/*  __________________________________________________________________________________________________________________________________________
 * | The Didact Esselte 100 CPU board rev1, 14/8 1980                                                                          in-PCB coil     +----
 * |   +--+     +--+     +--+     +--+        +--+     +--+                                                                 +--------+    |VHF
 * |   74       74       74       74          74       74                   7805CT              7805CT        trim 3,5-13pF |+-----+ |    |  TV
 * |    157      393       04       10          00       03                                                        2N2369 | || o-+ | |    +----
 * |   +--+     +--+     +--+     +--+        +--+     +--+                                                               | |+---+ | |       |
 * |1Kohm                                                                                                                 | +------+ |    +----
 * |trim                                                                                                                  +----------+    |CVS
 * | 8 +--+              +--+          +--+                                                              7805CP                           | MON
 * | 0 74                74            74                                                                                                 +----
 * | 0  132               157            93                                                                                                  |
 * | 8 +--+              +--+          +--+                                                                          J401                    |
 * | 1 +--+                                                                          +--+  +--+                                     LM339    |
 * | 4 74                +--+          +--+                                          74    74     +--+ +--+                   J402           |
 * |    165              74            74                                             122    00   74   74    4Mhz                            |
 * | J +--+               157           393                                          +--+  +--+    138  138  XTAL                         +----
 * | G                   +--+          +--+                                                       +--+ +--+    +----+  +----+  +----+     |TAPE
 * | +----+      +----+                                                                                               optional            |
 * |  CHAR       VIDEO                 +--+    +----+ +----+ +----+ +----+ +----+ +----+ +----+ +----+ +====++  CPU     PIA2    PIA1      +----
 * |   ROM        RAM                  74      6116   6116   6116   6116                                    ||                               |
 * |  2716       MK4118                 245      alt    alt    alt    alt                                2x ||  6802    6821    6821      +----
 * | +----+      +----+                +--+    MK4118 MK4118 MK4118 MK4118  2716   2716   2716   2716   2716||                            |PRNT
 * |DIDACT ESS 100 CPU                         +----+ +----+ +----+ +----+ +----+ +----+ +----+ +----+ +----++                            |
 * |___________________________________________________________________________________________________________+----+__+----+__+----+_____+----
 *
 * rev2 board had 4Kb more ROM memory, 2 x 2764 instead of the 6 x 2716 (note the rev1 piggyback on rightmost 2716) with funny address decoding.
 * Once we get a rom dump for rev 1 the driver need to accommodate another keymap too so probably needs to be split somehow.
 *  __________________________________________________________________________________________________________________________________________
 * | The Didact Esselte 100 CPU board rev2, 15/4 1983                                                                     in-PCB coil     +----
 * |           +--+     +--+     +--+     +--+     +--+     +--+                                                            +--------+    |VHF
 * |           74       74       74       74       74       74              7805CT              7805CT        trim 3,5-13pF |+-----+ |    |  TV
 * |             93      393       10      393       00       03                                                   2N2369 | || o-+ | |    +----
 * |           +--+     +--+     +--+     +--+     +--+     +--+                                                          | |+---+ | |       |
 * |1Kohm                                                                                                                 | +------+ |    +----
 * |trim                                                                                                                  +----------+    |CVS
 * |   +--+    +--+     +--+    +--+   +--+   J                                                          7805CP                           | MON
 * |   74      74       74      74     74     2                                                                                           +----
 * |    165     132      157     157     04   0                                                                                              |
 * |   +--+    +--+     +--+    +--+   +--+   1       J 2 0 2                     +----+ +----+                      J401                    |
 * |                                                                                                                                         |
 * | +----+            +----+          +--+             +--+   +--+   +--+  +--+   U202   U201                                J402           |
 * |  CHAR             VIDEO           74               74     74     74    74                               4Mhz                            |
 * |   ROM              RAM             157              138    08     00    138   2764   2764               XTAL                         +----
 * |  2716             HM6116          +--+             +--+   +--+   +--+  +--+                               +----+  +----+  +----+     |TAPE
 * | +----+            +----+                J                                    +----+ +----+                       optional            |
 * |                                   +--+  2        +----+ +----+ +----+ +----+ +----+ +----+ +----+ +----+   CPU     PIA2    PIA1      +----
 * |                                   74    0        6116   6116   6116   6116   6116   6116   6116   6116                          +--+    |
 * |   8169 830415                      245  3                                     opt    opt    opt    opt     6802    6821    6821 LM   +----
 * |  ESSELTE 100                      +--+                                                                                           339 |PRNT
 * |  CPU 100                                         +----+ +----+ +----+ +----+ +----+ +----+ +----+ +----+                        +--+ |
 * |___________________________________________________________________________________________________________+----+__+----+__+----+_____+----
 *
 *   Both rev1 and rev2 has a matrix keyboard PCB with a 74LS145 connected to J402 (PIA2)
 */


namespace {

class e100_state : public driver_device
{
public:
	e100_state(const machine_config &mconfig, device_type type, const char * tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_kbd_74145(*this, "kbd_74145")
		, m_vram(*this, "vram")
		, m_cassette(*this, "cassette")
		, m_rs232(*this, "rs232")
		, m_chargen(*this, "chargen")
		, m_io_line(*this, "LINE%u", 0)
		, m_pia(*this, "pia%u", 1)
		, m_pia1_B(0)
		, m_50hz(0)
	{ }

	void e100(machine_config &config);

protected:
	virtual void machine_reset() override { m_maincpu->reset(); LOG("--->%s()\n", FUNCNAME); };
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<m6802_cpu_device> m_maincpu;
	required_device<ttl74145_device> m_kbd_74145;
	required_shared_ptr<uint8_t> m_vram;
	required_device<cassette_image_device> m_cassette;
	optional_device<rs232_port_device> m_rs232;
	required_region_ptr<uint8_t> m_chargen;
	required_ioport_array<10> m_io_line;
	required_device_array<pia6821_device, 2> m_pia;

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	uint8_t pia_r(offs_t offset);
	void pia_w(offs_t offset, uint8_t data);
	uint8_t pia1_kbA_r();
	void pia1_kbA_w(uint8_t data);
	uint8_t pia1_kbB_r();
	void pia1_kbB_w(uint8_t data);
	void pia1_ca2_w(int state);
	void pia1_cb2_w(int state);

	TIMER_DEVICE_CALLBACK_MEMBER(rtc_w);

	void e100_map(address_map &map) ATTR_COLD;

	uint8_t m_pia1_B;
	uint8_t m_50hz;
};

TIMER_DEVICE_CALLBACK_MEMBER(e100_state::rtc_w)
{
	m_pia[1]->ca1_w(m_50hz++ & 1);
}

void e100_state::machine_start()
{
	LOG("%s()\n", FUNCNAME);

	/* register for state saving */
	save_item(NAME(m_50hz));
	save_item(NAME(m_pia1_B));
}

uint32_t e100_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	LOGSCREEN("%s()\n", FUNCNAME);
	for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
	{
		uint16_t vram_addr = (y >> 3) * 32;

		for (int col = 0; col < 32 * 6; col += 6)
		{
			for (int x = 0; x < 6; x++)
			{
				bitmap.pix(y, col + x) = BIT(m_chargen[(m_vram[vram_addr] << 3) | (y & 7)], x);
			}
			vram_addr++;
		}
	}
	return 0;
}

/* PIA write - the Esselte 100 allows the PIA:s to be accessed simultaneously */
void e100_state::pia_w(offs_t offset, uint8_t data)
{
	LOG("%s(%02x)\n", FUNCNAME, data);
	if ((offset & 0x08) == 0x08)
	{
		LOG("- PIA1\n");
		m_pia[0]->write(offset, data);
	}
	if ((offset & 0x10) == 0x10)
	{
		LOG("- PIA2\n");
		m_pia[1]->write(offset, data);
	}
	if (VERBOSE && (offset & 0x18) == 0x18)
	{
		LOGCS("- Dual device write access!\n");
	}
	if (VERBOSE && (offset & 0x18) == 0x00)
	{
		logerror("- Funny write at offset %02x!\n", offset);
	}
}

/* PIA read  - the Esselte 100 allows the PIA:s to be accessed simultaneously */
uint8_t e100_state::pia_r(offs_t offset)
{
	uint8_t data = 0;

	switch (offset & 0x18)
	{
	case 0x18: // read PIA1 and PIA2 at the same time, should really only happen for writes...
		{
			uint8_t data1 = m_pia[0]->read(offset);
			uint8_t data2 = m_pia[1]->read(offset);
			logerror("%s: Dual device read may have caused unpredictable results on real hardware\n", FUNCNAME);
			data = data1 & data2; // We assume that the stable behaviour is that data lines with a low level by either device succeeds
			LOGCS("%s %s[%02x] %02x & %02x -> %02x Dual device read!!\n", "pia1/pia2", FUNCNAME, offset, data1, data2, data);
		}
		break;
	case 0x08: // PIA1
		data = m_pia[0]->read(offset);
		LOGCS("%s %s(%02x)\n", "pia1", FUNCNAME, data);
		break;
	case 0x10: // PIA2
		data = m_pia[1]->read(offset);
		LOGCS("%s %s(%02x)\n", "pia2", FUNCNAME, data);
		break;
	default: // None of the devices are selected
		logerror("%s: Funny read at offset %02x\n", FUNCNAME, offset);
		break;
	}
	return data;
}

void e100_state::pia1_kbA_w(uint8_t data)
{
	LOG("%s(%02x)\n", FUNCNAME, data);
}

uint8_t e100_state::pia1_kbA_r()
{
	uint16_t ls145;
	uint8_t pa = 0xff;

	// Read out the selected column
	ls145 = m_kbd_74145->read() & 0x3ff;

	// read out the keyboard
	for (int i = 0; i < 10; i++)
	{
		if (BIT(ls145, i))
		{
			pa = (m_io_line[i]->read()) & 0xff;
			break;
		}
	}
	if (VERBOSE && ls145 && pa) LOGSCAN("%s  [%03x]%04x\n", FUNCNAME, ls145, pa);

	return pa;
}

/*
  PB0-PB3 is connected to U601 (74LS145) which select a column to scan
  PB4-PB5 together with CA1, CA2, CB1 and CB2 are used for the printer interface
  PB6-PB7 forms the cassette interface

  The serial bitbanging performs unreliably atm, can be poor original code or inexact CPU timing.
  Best results is achieved with 8 bit at 9600 baud as follows:

    mame e100 -rs232 null_modem -bitbngr socket.127.0.0.1:4321

  Start the favourite Telnet client towards the 4321 port and exit the startup screen of MAME.
  At the "Esselte 100 #" prompt change to 8 bit communication and start the terminal mode:

   POKE (69,1)
   TERM(9600)

  It is now possible to send characters from the Esselte screen to the Telnet terminal. When a
  carriage return has been sent to the terminal the Esselte 100 goes into receiving mode until
  it receives a carriage return from the terminal at which point it will start sending again.

  TODO:
  - Fix key mapping of the Ctrl-PI exit sequence to get out of the TERM mode.
  - Fix timing issues for the PIA bit banging, could be related to that the CPU emulation is not
    cycle exact or the ROM code is buggy
*/

#define SERIAL_OUT 0x10
#define SERIAL_IN  0x20
#define CASS_OUT   0x40
#define CASS_IN    0x80
void e100_state::pia1_kbB_w(uint8_t data)
{
	// Keyboard
	//  if (VERBOSE && data != m_pia1_B) LOGSCAN("%s(%02x)\n", FUNCNAME, data);
	m_pia1_B = data;
	m_kbd_74145->write(data & 0x0f);

	// Cassette
	m_cassette->output(data & CASS_OUT ? 1.0 : -1.0);

	// Serial
	m_rs232->write_txd(data & SERIAL_OUT ? 0 : 1);
}

uint8_t e100_state::pia1_kbB_r()
{
	m_pia1_B &= ~(CASS_IN|SERIAL_IN);

	m_pia1_B |= (m_cassette->input() > 0.03 ? CASS_IN : 0x00);

	m_pia1_B |= (m_rs232->rxd_r() != 0 ? SERIAL_IN : 0x00);

	return m_pia1_B;
}

void e100_state::pia1_ca2_w(int state)
{
	// TODO: Make this a slot device to trigger time measurements
}

void e100_state::pia1_cb2_w(int state)
{
	m_rs232->write_txd(!state);
}

// This map is derived from info in "TEMAL 100 - Teknisk manual Esselte 100"
void e100_state::e100_map(address_map &map)
{
	map(0x0000, 0x1fff).ram();
	map(0x8000, 0x87ff).rom().region("roms", 0);
	map(0xc000, 0xc3ff).ram().share(m_vram);
	map(0xc800, 0xc81f).mirror(0x07e0).rw(FUNC(e100_state::pia_r), FUNC(e100_state::pia_w));
	map(0xd000, 0xffff).rom().region("roms", 0x1000);
}

/* E100 Input ports
 * Four e100 keys are not mapped yet,
 * - The redundant '*' on the keyboard together with the '\'' single quote, both on same e100 key
 * - The 'E' key on the keypad, presumably used for calculator applications to remove the last entered number
 * - The 'Break' key on rev2 will be mapped to NMI at some point, a recommended modification of the rev1 mother board
 * - The 'REPT' key has a so far unknown function
 */
static INPUT_PORTS_START( e100 )
/*  Bits read on PIA1 A when issuing line number on PIA1 B bits 0-3 through a 74145 demultiplexer */
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_LOW,   IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW,   IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_SPACE)        PORT_CHAR(' ')
	PORT_BIT(0x08, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_LSHIFT)       PORT_CODE(KEYCODE_RSHIFT)       PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x10, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_LCONTROL)     PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x20, IP_ACTIVE_LOW,   IPT_KEYBOARD)   PORT_NAME("REPT")           PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x40, IP_ACTIVE_LOW,   IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW,   IPT_UNUSED)

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_LOW,   IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW,   IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_Z)            PORT_CHAR('z')  PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_A)            PORT_CHAR('a')  PORT_CHAR('A')
	PORT_BIT(0x10, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_Q)            PORT_CHAR('q')  PORT_CHAR('Q')
	PORT_BIT(0x20, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_1)            PORT_CHAR('1')  PORT_CHAR('!')
	PORT_BIT(0x40, IP_ACTIVE_LOW,   IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW,   IPT_UNUSED)

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_PLUS_PAD)     PORT_CHAR('+')
	PORT_BIT(0x02, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_MINUS_PAD)    PORT_CHAR('-')
	PORT_BIT(0x04, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_X)            PORT_CHAR('x')  PORT_CHAR('X')
	PORT_BIT(0x08, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_S)            PORT_CHAR('s')  PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_W)            PORT_CHAR('w')  PORT_CHAR('W')
	PORT_BIT(0x20, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_2)            PORT_CHAR('2')  PORT_CHAR('"')
	PORT_BIT(0x40, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_SLASH_PAD)    PORT_CHAR('/')
	PORT_BIT(0x80, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_ASTERISK)     PORT_CHAR('*')

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_COLON)        PORT_CHAR(U'ö') PORT_CHAR(U'Ö')
	PORT_BIT(0x02, IP_ACTIVE_LOW,   IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_STOP)         PORT_CHAR('.')  PORT_CHAR(':')
	PORT_BIT(0x08, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_L)            PORT_CHAR('l')  PORT_CHAR('L')
	PORT_BIT(0x10, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_O)            PORT_CHAR('o')  PORT_CHAR('O')
	PORT_BIT(0x20, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_9)            PORT_CHAR('9')  PORT_CHAR(')')
	PORT_BIT(0x40, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_P)            PORT_CHAR('p')  PORT_CHAR('P')
	PORT_BIT(0x80, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_0)            PORT_CHAR('0')  PORT_CHAR('=')

	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_QUOTE)        PORT_CHAR(U'ä') PORT_CHAR(U'Ä')
	PORT_BIT(0x02, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_SLASH)        PORT_CHAR('-')  PORT_CHAR('_')
	PORT_BIT(0x04, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_COMMA)        PORT_CHAR(',')  PORT_CHAR(';')
	PORT_BIT(0x08, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_K)            PORT_CHAR('k')  PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_I)            PORT_CHAR('i')  PORT_CHAR('I')
	PORT_BIT(0x20, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_8)            PORT_CHAR('8')  PORT_CHAR('(')
	PORT_BIT(0x40, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_OPENBRACE)    PORT_CHAR(U'å') PORT_CHAR(U'Å')
	PORT_BIT(0x80, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_MINUS)        PORT_CHAR('+')  PORT_CHAR('?')

	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_BACKSLASH)    PORT_CHAR('\'')  PORT_CHAR('*')
	PORT_BIT(0x02, IP_ACTIVE_LOW,   IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_M)            PORT_CHAR('m')  PORT_CHAR('M')
	PORT_BIT(0x08, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_J)            PORT_CHAR('j')  PORT_CHAR('J')
	PORT_BIT(0x10, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_U)            PORT_CHAR('u')  PORT_CHAR('U')
	PORT_BIT(0x20, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_7)            PORT_CHAR('7')  PORT_CHAR('/')
	PORT_BIT(0x40, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_CHAR('^')
	PORT_BIT(0x80, IP_ACTIVE_LOW,   IPT_KEYBOARD) PORT_NAME(u8"π")              PORT_CODE(KEYCODE_ESC)          PORT_CHAR(27)

	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_LOW,   IPT_KEYBOARD) PORT_NAME(u8"\u2190 \u2191")  PORT_CODE(KEYCODE_LEFT)         PORT_CHAR(UCHAR_MAMEKEY(LEFT))  PORT_CHAR(UCHAR_MAMEKEY(UP))  // U+2190 = ←, U+2191 = ↑
	PORT_BIT(0x02, IP_ACTIVE_LOW,   IPT_KEYBOARD) PORT_NAME(u8"\u2192 \u2193")  PORT_CODE(KEYCODE_RIGHT)        PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR(UCHAR_MAMEKEY(DOWN))// U+2192 = →, U+2193 = ↓
	PORT_BIT(0x04, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_N)            PORT_CHAR('n')  PORT_CHAR('N')
	PORT_BIT(0x08, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_H)            PORT_CHAR('h')  PORT_CHAR('H')
	PORT_BIT(0x10, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_Y)            PORT_CHAR('y')  PORT_CHAR('Y')
	PORT_BIT(0x20, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_6)            PORT_CHAR('6')  PORT_CHAR('&')
	PORT_BIT(0x40, IP_ACTIVE_LOW,   IPT_KEYBOARD) PORT_NAME("Return")           PORT_CODE(KEYCODE_ENTER)        PORT_CHAR('\r')
	PORT_BIT(0x80, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_BACKSLASH2)   PORT_CHAR('<')  PORT_CHAR('>')

	PORT_START("LINE7")
	PORT_BIT(0x01, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_1_PAD)        PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x02, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_0_PAD)        PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT(0x04, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_B)            PORT_CHAR('b')  PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_G)            PORT_CHAR('g')  PORT_CHAR('G')
	PORT_BIT(0x10, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_T)            PORT_CHAR('t')  PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_5)            PORT_CHAR('5')  PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_4_PAD)        PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_7_PAD)        PORT_CHAR(UCHAR_MAMEKEY(7_PAD))

	PORT_START("LINE8")
	PORT_BIT(0x01, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_2_PAD)        PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x02, IP_ACTIVE_LOW,   IPT_KEYBOARD) PORT_NAME("Keypad .")         PORT_CODE(KEYCODE_DEL_PAD)      PORT_CHAR(UCHAR_MAMEKEY(STOP))
	PORT_BIT(0x04, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_V)            PORT_CHAR('v')  PORT_CHAR('V')
	PORT_BIT(0x08, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_F)            PORT_CHAR('f')  PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_R)            PORT_CHAR('r')  PORT_CHAR('R')
	PORT_BIT(0x20, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_4)            PORT_CHAR('4')  PORT_CHAR('$')
	PORT_BIT(0x40, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_5_PAD)        PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_8_PAD)        PORT_CHAR(UCHAR_MAMEKEY(8_PAD))

	PORT_START("LINE9")
	PORT_BIT(0x01, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_3_PAD)        PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x02, IP_ACTIVE_LOW,   IPT_KEYBOARD) PORT_NAME("Keypad E")         PORT_CODE(KEYCODE_ENTER_PAD)    PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
	PORT_BIT(0x04, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_C)            PORT_CHAR('c')  PORT_CHAR('C')
	PORT_BIT(0x08, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_D)            PORT_CHAR('d')  PORT_CHAR('D')
	PORT_BIT(0x10, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_E)            PORT_CHAR('e')  PORT_CHAR('E')
	PORT_BIT(0x20, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_3)            PORT_CHAR('3')  PORT_CHAR('#')
	PORT_BIT(0x40, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_6_PAD)        PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW,   IPT_KEYBOARD)                               PORT_CODE(KEYCODE_9_PAD)        PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
INPUT_PORTS_END

void e100_state::e100(machine_config &config)
{
	M6802(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_ram_enable(false);
	m_maincpu->set_addrmap(AS_PROGRAM, &e100_state::e100_map);

	/* Devices */
	TTL74145(config, m_kbd_74145, 0);

	/* --PIA inits----------------------- */
	/* 0xF883 0xC818 (PIA1 DDR A)     = 0x00 - Port A all inputs */
	/* 0xF883 0xC818 (PIA2 DDR A)     = 0x00 - Port A all inputs */
	/* 0xF883 0xC818 (PIA1 Control A) = 0x00 - Channel A IRQ disabled */
	/* 0xF883 0xC818 (PIA2 Control A) = 0x00 - Channel A IRQ disabled */
	/* 0xF886 0xC81A (PIA1 DDR B)     = 0x00 - Port B all inputs */
	/* 0xF886 0xC81A (PIA2 DDR B)     = 0x00 - Port B all inputs */
	/* 0xF886 0xC81A (PIA1 Control B) = 0x00 - Channel B IRQ disabled */
	/* 0xF886 0xC81A (PIA2 Control B) = 0x00 - Channel B IRQ disabled */
	/* 0xF88e 0xC80A (PIA1 DDR B)     = 0x4F - Port B 5 outputs set to 0 */
	/* 0xF890 0xC812 (PIA2 DDR B)     = 0xFF - Port B all outputs set to 0 */
	/* 0xF894 0xC818 (PIA1 Control A) = 0x34 - CA2 is low and lock DDRA */
	/* 0xF894 0xC818 (PIA2 Control A) = 0x34 - CA2 is low and lock DDRA */
	/* 0xF896 0xC818 (PIA1 Control B) = 0x34 - CB2 is low and lock DDRB */
	/* 0xF896 0xC818 (PIA2 Control B) = 0x34 - CB2 is low and lock DDRB */
	PIA6821(config, m_pia[0]);
	m_pia[0]->writepa_handler().set(FUNC(e100_state::pia1_kbA_w));
	m_pia[0]->readpa_handler().set(FUNC(e100_state::pia1_kbA_r));
	m_pia[0]->writepb_handler().set(FUNC(e100_state::pia1_kbB_w));
	m_pia[0]->readpb_handler().set(FUNC(e100_state::pia1_kbB_r));
	m_pia[0]->ca1_w(ASSERT_LINE); // TODO: Make this a slot device for time measurements. Default is handshake for serial port TODO: Fix RS232 handshake as default
	m_pia[0]->ca2_handler().set(FUNC(e100_state::pia1_ca2_w));
	m_pia[0]->cb2_handler().set(FUNC(e100_state::pia1_cb2_w));

	/* The optional second PIA enables the expansion port on CA1 and a software RTC with 50Hz resolution */
	PIA6821(config, m_pia[1]);
	m_pia[1]->irqa_handler().set_inputline("maincpu", M6800_IRQ_LINE);

	/* Serial port support */
	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set(m_pia[0], FUNC(pia6821_device::cb1_w));

	SPEAKER(config, "mono").front_center();
	/* Cassette support - E100 uses 300 baud Kansas City Standard with 1200/2400 Hz modulation */
	/* NOTE on usage: mame e100 -cass <wav file>
	 * Once running enable/disable internal UI by pressing Scroll Lock in case it interferes with target keys
	 * Open the internal UI by pressing TAB and then select 'Tape Control' or use F2/Shift F2 for PLAY/PAUSE
	 * In order to use a wav file it has first to be created using TAB and select the 'File manager'
	 * Once created it may be given on the commandline or mounted via TAB and select
	 * E100 supports cassette through the 'LOAD' and 'SAVE' commands with no arguments
	 */
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	/* screen TODO: simplify the screen config, look at zx.cpp */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(4_MHz_XTAL, 265, 0, 256, 265, 0, 256);
	screen.set_screen_update(FUNC(e100_state::screen_update));
	screen.set_palette("palette");
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* There is a 50Hz signal from the video circuit to CA1 which generates interrupts and drives a software RTC */
	TIMER(config, "video50hz").configure_periodic(FUNC(e100_state::rtc_w), attotime::from_hz(100)); /* Will be divided by two through toggle in the handler */
}

/* ROM sets from Didact was not versioned in general, so the numbering are just assumptions */
ROM_START( e100 )
	ROM_REGION(0x4000, "roms", 0)
	ROM_DEFAULT_BIOS("rev2-basic")

	/* TODO: Get the original ROMs */
	ROM_SYSTEM_BIOS(0, "rev1-basic", "Esselte 100 rev1 BASIC")
	ROMX_LOAD( "e100r1u201.bin", 0x1000, 0x0800, NO_DUMP, ROM_BIOS(0) )
	ROMX_LOAD( "e100r1u202.bin", 0x1800, 0x0800, NO_DUMP, ROM_BIOS(0) )
	ROMX_LOAD( "e100r1u203.bin", 0x2000, 0x0800, NO_DUMP, ROM_BIOS(0) )
	ROMX_LOAD( "e100r1u204.bin", 0x2800, 0x0800, NO_DUMP, ROM_BIOS(0) )
	ROMX_LOAD( "e100r1u205.bin", 0x3000, 0x0800, NO_DUMP, ROM_BIOS(0) )
	ROMX_LOAD( "e100r1u206.bin", 0x3800, 0x0800, NO_DUMP, ROM_BIOS(0) )

	/* This is a prototype ROM, commercial release not verified. The prototype also have different keyboard and supports
	   more ram so might need to be split out as a clone later */
	ROM_SYSTEM_BIOS(1, "rev2-basic", "Esselte 100 rev2 BASIC")
	ROMX_LOAD( "e100r2u201.bin", 0x0000, 0x2000, CRC(53513b67) SHA1(a91c5c32aead82dcc87db5d818ff286a7fc6a5c8), ROM_BIOS(1) )
	ROMX_LOAD( "e100r2u202.bin", 0x2000, 0x2000, CRC(eab3adf2) SHA1(ff3f5f5c8ea8732702a39cff76d0706ab6b751ee), ROM_BIOS(1) )

	ROM_REGION(0x0800, "chargen",0)
	ROM_LOAD( "e100u506.bin", 0x0000, 0x0800, CRC(fff9f288) SHA1(2dfb3eb551fe1ef67da328f61ef51ae8d1abdfb8) )
ROM_END

} // anonymous namespace


//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY      FULLNAME       FLAGS
COMP( 1982, e100, 0,      0,      e100,    e100,  e100_state, empty_init, "Didact AB", "Esselte 100", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE)



e9161.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/************************************************************************************************************

    Skeleton driver for Ericsson/Datasaab 9161 Display Processor Unit (DPU).

    This is a high-end video terminal that can connect to a Datasaab E2500 mainframe system over a coaxial
    cable. It also has an unusual differential connector for its display (which operates at 50 Hz).

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

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "machine/hd63450.h"
#include "machine/z80sio.h"
#include "video/am8052.h"


namespace {

class e9161_state : public driver_device
{
public:
	e9161_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_dmac(*this, "dmac")
	{
	}

	void e9161(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<m68000_device> m_maincpu;
	required_device<hd63450_device> m_dmac;
};

void e9161_state::mem_map(address_map &map)
{
	map(0x000000, 0x000007).rom().region("program", 0);
	map(0x000008, 0x01ffff).ram();
	map(0x020000, 0x020001).w(m_maincpu, FUNC(m68000_device::berr_w));
	map(0xa00000, 0xa00001).mirror(0x1ffffe).r(m_maincpu, FUNC(m68000_device::berr_r));
	map(0xc00000, 0xc03fff).rom().region("program", 0);
	map(0xe00000, 0xe03fff).ram();
	map(0xffe000, 0xffe03f).rw(m_dmac, FUNC(hd63450_device::read), FUNC(hd63450_device::write));
	map(0xffe802, 0xffe803).w("crtc", FUNC(am8052_device::pointer_w));
	map(0xffe804, 0xffe805).w("crtc", FUNC(am8052_device::data_w));
	map(0xfff600, 0xfff63f).rw("sio", FUNC(mk68564_device::read), FUNC(mk68564_device::write)).umask16(0x00ff);
}


static INPUT_PORTS_START(e9161) // TODO: serial keyboard
INPUT_PORTS_END


void e9161_state::e9161(machine_config &config)
{
	M68000(config, m_maincpu, 8'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &e9161_state::mem_map);

	HD63450(config, m_dmac, 8'000'000, m_maincpu);

	AM8052(config, "crtc", 8'000'000);

	MK68564(config, "sio", 4'000'000);
}


// CPU: MC68000G8
// DMAC: HD68450-8
// DRAM: 16 x M5K4164ANP-12
// DRAM controller: TMS4500A-15NL
// SIO: MK68564N-4A
// CRTC: Am8052-6LC (under heatsink)
// XTALs: 19.1700 MHz (middle of board), ??.???? MHz (near video connector)
ROM_START(e9161)
	ROM_REGION16_BE(0x4000, "program", 0)
	ROM_LOAD16_BYTE("e3405_87080_7403.bin", 0x0000, 0x2000, CRC(97f72404) SHA1(ced003ce294cd7370051e1f774d5120062390647))
	ROM_LOAD16_BYTE("e3405_87080_7303.bin", 0x0001, 0x2000, CRC(ec94aec4) SHA1(f41ae1b7f04ca3a2d0def6ff9aad3ff41782589a))
ROM_END

} // anonymous namespace


COMP(198?, e9161, 0, 0, e9161, e9161, e9161_state, empty_init, "Ericsson", "9161 Display Processor Unit", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



eacc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/******************************************************************************
*
*    EA Car Computer
*        by Robbbert, March 2011.
*
*    Described in Electronics Australia magazine during 1982.
*    Construction and usage: http://messui.polygonal-moogle.com/comp/eacc.pdf
*
*    The only RAM is the 128 bytes that comes inside the CPU.
*
*    This computer is mounted in a car, and various sensors (fuel flow, etc)
*    are connected up. By pressing the appropriate buttons various statistics
*    may be obtained.
*
*    Memory Map
*    0000-007F internal ram
*    4000-7FFF ROM
*    8000-BFFF 6821
*    C000-FFFF ROM (mirror)
*
*    The ROM was typed in twice from the dump in the magazine article, and the
*    results compared. Only one byte was different, so I can be confident that
*    it has been typed in properly.
*
*    Setting up: You need to enter the number of expected pulses from the fuel
*    and distance sensors. Paste this: 5 6M123N 7M400N  (start, set litres cal to
*    123 pulses. set km cal to 400 pulses). Then paste this: 1950M0N 1845M0N (set
*    petrol tank capacity to 50 litres, set current amount of petrol to 45).
*    Now enter: 28M100N (the journey is 100km). Press 5 to start the journey.
*    All settings are saved in nvram.
*
*    Stats you can see while travelling:
*    0  - time elapsed
*    08 - time remaining
*    1  - fuel used
*    18 - fuel left
*    2  - km travelled
*    28 - km remaining
*    29 - km that could be travelled with the fuel you have left
*    3  - speed now
*    39 - average speed
*    4  - fuel consumption now (litres per 100km)
*    49 - fuel average consumption
*
******************************************************************************/

/* Core includes */
#include "emu.h"
#include "cpu/m6800/m6800.h"
#include "eacc.lh"
#include "machine/6821pia.h"
#include "machine/7474.h"
#include "machine/clock.h"
#include "machine/nvram.h"


namespace {

class eacc_state : public driver_device
{
public:
	eacc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pia(*this, "pia")
		, m_p_nvram(*this, "nvram")
		, m_7474(*this, "7474")
		, m_io_keyboard(*this, "X%u", 0U)
		, m_digits(*this, "digit%u", 0U)
		, m_leds(*this, "led%u", 0U)
	{ }

	void eacc(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	void scan_w(int state);
	void cb2_w(int state);
	void inputs_w(int state);
	uint8_t keyboard_r();
	void digit_w(uint8_t data);
	void segment_w(uint8_t data);
	void mem_map(address_map &map) ATTR_COLD;
	void do_nmi(bool, bool);
	uint8_t m_digit = 0U;
	bool m_cb2 = false;
	bool m_scan = false;
	bool m_disp = false;

	required_device<m6802_cpu_device> m_maincpu;
	required_device<pia6821_device> m_pia;
	required_shared_ptr<uint8_t> m_p_nvram;
	required_device<ttl7474_device> m_7474;
	required_ioport_array<4> m_io_keyboard;
	output_finder<7> m_digits;
	output_finder<8> m_leds;
};




/******************************************************************************
 Address Maps
******************************************************************************/

void eacc_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xc7ff); // A11,A12,A13 not connected
	map(0x0000, 0x001f).ram().share("nvram"); // inside cpu, battery-backed
	map(0x0020, 0x007f).ram(); // inside cpu
	map(0x4000, 0x47ff).rom().mirror(0x8000).region("maincpu",0);
	map(0x8000, 0x8003).mirror(0x7fc).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
}


/******************************************************************************
 Input Ports
******************************************************************************/

static INPUT_PORTS_START(eacc)
	PORT_START("X0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("6 Litres Cal") PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR('M')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("2 km") PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2')
	PORT_BIT( 0xf8, 0, IPT_UNUSED )

	PORT_START("X1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("5 START") PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("END") PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR('N')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("3 km/h") PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3')
	PORT_BIT( 0xf8, 0, IPT_UNUSED )

	PORT_START("X2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("7 Km Cal") PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("0 hour.min") PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("4 l/100km") PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4')
	PORT_BIT( 0xf8, 0, IPT_UNUSED )

	PORT_START("X3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("8 REM") PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("1 litres") PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("9 AV") PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9')
	PORT_BIT( 0xf8, 0, IPT_UNUSED )
INPUT_PORTS_END

void eacc_state::machine_reset()
{
	m_cb2 = false; // pia is supposed to set this low at reset
	m_digit = 0;
}

void eacc_state::machine_start()
{
	m_digits.resolve();
	m_leds.resolve();

	save_item(NAME(m_cb2));
	save_item(NAME(m_scan));
	save_item(NAME(m_digit));
	save_item(NAME(m_disp));
}

void eacc_state::inputs_w(int state)
{
	if (state)
		m_pia->ca1_w(machine().rand() & 1); // movement
	else
		m_pia->ca2_w(machine().rand() & 1); // fuel usage
}

void eacc_state::do_nmi(bool in_scan, bool in_cb2)
{
	if (in_scan && in_cb2)
	{
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
		m_7474->clock_w(0);
	}
	else
	{
		m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
		m_7474->clock_w(1);
	}
}

void eacc_state::cb2_w(int state)
{
	m_cb2 = state ? 1 : 0;
	do_nmi(m_scan, m_cb2);
}

void eacc_state::scan_w(int state)
{
	m_scan = state ? 1 : 0;
	do_nmi(m_scan, m_cb2);
}

uint8_t eacc_state::keyboard_r()
{
	uint8_t data = m_digit;

	for (uint8_t i = 3; i < 7; i++)
		if (BIT(m_digit, i))
			data |= m_io_keyboard[i - 3]->read();

	return data;
}

void eacc_state::segment_w(uint8_t data)
{
	//d7 segment dot
	//d6 segment c
	//d5 segment d
	//d4 segment e
	//d3 segment a
	//d2 segment b
	//d1 segment f
	//d0 segment g

	if (m_disp)
	{
		m_disp = false;
		if (BIT(m_digit, 7))
		{
			data ^= 0xff;

			for (uint8_t i = 0; i < 8; i++)
				m_leds[i] = BIT(data, i);
		}
		else
		{
			for (uint8_t i = 3; i < 7; i++)
				if (BIT(m_digit, i))
					m_digits[i] = bitswap<8>(data, 7, 0, 1, 4, 5, 6, 2, 3);
		}
	}
}

void eacc_state::digit_w(uint8_t data)
{
	m_digit = data & 0xf8;
	m_disp = true;
}


/******************************************************************************
 Machine Drivers
******************************************************************************/

void eacc_state::eacc(machine_config &config)
{
	/* basic machine hardware */
	M6802(config, m_maincpu, XTAL(3'579'545));  /* Divided by 4 inside the m6802*/
	m_maincpu->set_ram_enable(false); // FIXME: needs standby support
	m_maincpu->set_addrmap(AS_PROGRAM, &eacc_state::mem_map);

	config.set_default_layout(layout_eacc);

	PIA6821(config, m_pia);
	m_pia->readpb_handler().set(FUNC(eacc_state::keyboard_r));
	m_pia->writepa_handler().set(FUNC(eacc_state::segment_w));
	m_pia->writepb_handler().set(FUNC(eacc_state::digit_w));
	m_pia->cb2_handler().set(FUNC(eacc_state::cb2_w));
	m_pia->irqa_handler().set_inputline("maincpu", M6802_IRQ_LINE);
	m_pia->irqb_handler().set_inputline("maincpu", M6802_IRQ_LINE);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	TTL7474(config, m_7474, 0);
	m_7474->output_cb().set(m_pia, FUNC(pia6821_device::cb1_w));

	clock_device &eacc_scan(CLOCK(config, "eacc_scan", 600)); // 74C14 with 100k & 10nF = 1200Hz, but article says 600.
	eacc_scan.signal_handler().set(FUNC(eacc_state::scan_w));

	clock_device &eacc_cb1(CLOCK(config, "eacc_cb1", 15)); // cpu E -> MM5369 -> 15Hz
	eacc_cb1.signal_handler().set(m_7474, FUNC(ttl7474_device::d_w));

	clock_device &eacc_rnd(CLOCK(config, "eacc_rnd", 30)); // random pulse for distance and fuel
	eacc_rnd.signal_handler().set(FUNC(eacc_state::inputs_w));
}



/******************************************************************************
 ROM Definitions
******************************************************************************/

ROM_START(eacc)
	ROM_REGION(0x0800, "maincpu", 0)
	ROM_LOAD("eacc.bin", 0x0000, 0x0800, CRC(287a63c0) SHA1(f61b397d33ea40e5742e34d5f5468572125e8b39) )
ROM_END

} // anonymous namespace


/******************************************************************************
 Drivers
******************************************************************************/

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY                  FULLNAME           FLAGS
COMP( 1982, eacc, 0,      0,      eacc,    eacc,  eacc_state, empty_init, "Electronics Australia", "EA Car Computer", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



eag68k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger, yoyo_chessboard
/*******************************************************************************

Fidelity 68000-based Elite Avant Garde driver
For 6502-based EAG, see elite.cpp

Excel 68000 I/O is very similar to EAG, so it's handled in this driver as well

TODO:
- unemulated waitstates with DTACK
- V10 CPU emulation is too slow, MAME 68040 opcode timing is same as 68030 but in
  reality it is much faster, same goes for V11 of course (see note below)
- V11 CPU should be M68EC060, not yet emulated. Now using M68EC040 in its place
- V11 beeper is too high pitched, related to wrong CPU type too?
  maybe waitstates or clock divider on I/O access.
- Do the opening book modules work? It detects the rom, but then does nothing
  with it. Maybe support is limited since the basic CB9/CB16 modules have no use
  on newer chesscomputers with a large internal opening library.
- does premiere only use half of the nvram? 0x600000-0x603fff seems unused
- premiere unknown reads/writes to 0x500000 with the Vancouver program

Currently(May 2020) when compared to the real chesscomputers, to get closer to the
actual speed, overclock V10 and V11 to 230%. This can be done by starting MAME
with the -cheat option and going to the Slider Controls menu, hold Ctrl and press
Right to overclock maincpu.

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

Excel 68000 (model 6094) overview:
- 16KB RAM(2*SRM2264C-10 @ U8/U9), 64KB ROM(2*AT27C256-15DC @ U6/U7)
- HD68HC000P12 CPU, 12MHz XTAL
- PCB label 510-1129A01
- PCB has edge connector for module, but no external slot

There's room for 2 SIMMs at U22 and U23, unpopulated in Excel 68000 and Mach III.
Mach II has 2*64KB DRAM with a MB1422A DRAM controller @ 25MHz.
Mach III has wire mods from U22/U23 to U8/U9(2*8KB + 2*32KB piggybacked).
Mach IV has 2*256KB DRAM, and a daughterboard(510.1123B01) for the 68020 + 32KB RAM.

All of them have the same 510-1129A01 main PCB. I/O is via TTL, overall very
similar to EAG.

Holding NEW GAME does a quick self-test, on Mach III and Mach IV it will also
display a ROM checksum.

fex68km4a continuously tests RAM at boot and displays "512", this is normal.
To start, hold New Game or Clear.

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

Elite Avant Garde 2265 (EAG, model 6114)
----------------------------------------

There are 5 versions of model 6114(V1 to V5):

V1: 128KB DRAM, no EEPROM
V2: 128KB DRAM
V3: 512KB DRAM
V4: 1MB DRAM
V5: 128KB+16KB DRAM, dual-CPU! (2*68K @ 16MHz)

V2/V3/V4 have the same program, V2/V3 versions can be run by decreasing the
RAM size (-ramsize option). It's not verified if V1 has the same program, but
it probably does.

V6-V11 are on model 6117. Older 1986 model 6081/6088/6089 uses a 6502 CPU.

Hardware info:
--------------
- MC68HC000P12F 16MHz CPU, 16MHz XTAL
- MB1422A DRAM Controller, 25MHz XTAL near, 4 DRAM slots
  (V2: slot 2 & 3 64KB, V3: slot 2 & 3 256KB)
- 2*27C512 64KB EPROM, 2*KM6264AL-10 8KB SRAM, 2*AT28C64X 8KB EEPROM
- OKI M82C51A-2 USART, 4.9152MHz XTAL
- other special: magnet sensors, external module slot, serial port

IRQ source is a 4,9152MHz quartz crystal (Y3) connected to a 74HC4060 (U8,
ripple counter/divider). From Q13 output (counter=8192) we obtain the IRQ signal
applied to IPL1 of 68000 (pin 24) 4,9152 MHz / 8192 = 600 Hz.

The module slot pinout is different from SCC series. The data on those appears
to be compatible with EAG though, and will load fine with an adapter.

The USART allows for a serial connection between the chess computer and another
device, for example the Fidelity Challenger Printer, or a PC. It expects a baud
rate of 600.

Fidelity released a DOS tool called EAGLINK which featured PC printer support,
complete I/O control, detailed information while the program is 'thinking', etc.
It can be enabled with POP3 H3.

The chessboard is the same old wooden Auto Sensory board from EAS / Prestige.
6502-based EAG can easily be upgraded by swapping the PCB. Fidelity also provided
support for converting EAS and Prestige, via optional diode D8 next to the USART.

To play with this EAS / Prestige configuration on MAME, the user needs to provide
external artwork, or copy internal artwork fidel_eas.lay or fidel_pc.lay.

Memory map: (of what is known)
-----------
000000-01FFFF: 128KB ROM
104000-107FFF: 16KB SRAM
200000-2FFFFF: hashtable DRAM (max. 1MB)
300000-30000F W hi d0: NE591: 7seg data
300000-30000F W lo d0: NE591: LED data
300000-30000F R lo d7: 74259: keypad rows 0-7
400000-40000F W lo d0: 74259,74145/7442: led/keypad mux, buzzer out
400000-4????? R hi: external module slot
700002-700003 R lo d7: 74251: keypad row 8
604000-607FFF: 16KB EEPROM

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

Elite Avant Garde 2325 (EAG, model 6117)
----------------------------------------

There are 6 versions of model 6117(V6 to V11). From a programmer's point of view,
the hardware is very similar to model 6114.

V6: 68020, 512KB hashtable RAM
V7: 68020, 1MB h.RAM
V8: 2*68020, 512KB+128KB h.RAM (unreleased?)
V9: 68030, 1MB h.RAM
V10: 68040, 1MB h.RAM
V11: 68060, high speed, 2MB h.RAM (half unused?)

V6/V7/V9 have the same program, V6 can be run by decreasing the RAM size. V11
supposedly has the same program as V10.

V7 Hardware info:
-----------------
- 510.1139A01 daughterboard with MC68020RC20E or MC68020RC25E @ 20MHz,
  and 32KB RAM (4*MCM6264P35)
- rest is same as 6114

V7 Memory map:
--------------
000000-01FFFF: 128KB ROM
104000-107FFF: 16KB SRAM (unused?)
200000-2FFFFF: hashtable SRAM
300000-30000x: see model 6114
400000-40000x: see model 6114
700000-70000x: see model 6114
604000-607FFF: 16KB EEPROM
800000-807FFF: 32KB SRAM

V10 Hardware info:
------------------
- 68040 CPU, 25MHz
- other: assume same or very similar to V11(see below)

The ROM dump came from the V11(see below). Built-in factory test proves
that this program is a V10. Hold TB button immediately after power-on and
press it for a sequence of tests:
1) all LEDs on
2) F40C: V10 ROM checksum 1
3) 38b9: V10 ROM checksum 2
4) xxxx: external module ROM checksum (0000 if no module present)
5) xxxx: user settings (stored in EEPROM)
6) xxxx: "
7) 1024: hashtable RAM size
8) return to game

V11 Hardware info:
------------------
- MC68EC060RC75 CPU, 36MHz XTAL(36MHz bus, 72MHz CPU), CPU cooler required
- 4*CXK5863AP-20 8KB SRAM, 4*K6X4008C1F-DF55 512KB CMOS SRAM
- 4*M27C256B 32KB EPROM, 2*AT28C64 8KB EEPROM, 5*GAL16V8D
- NEC D71051C USART, on quick glance it's same as the OKI USART
- same as 6114: NE555, SN74HC4060, module slot, chessboard, ..

This is a custom overclocked V10, manufactured by Wilfried Bucke. PCB is marked:
"CHESS HW DESIGN COPYRIGHT 22-10-2002: REVA03 510.1136A01/510.1144B01 COMPONENT SIDE"
There are two versions of this, one with a 66MHz CPU, one with a 72MHz CPU.
Maybe other differences too?

V1x Memory map:
---------------
000000-01FFFF: 128KB ROM
280000-37FFFF: hashtable SRAM
B0000x-xxxxxx: see V7, -800000

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

Elite Premiere (model 6131)
---------------------------

This 2-in-1 chesscomputer was manufactured after Fidelity became a H+G subsidiary,
it's still Fidelity branded. It has a switch where the module slot used to be,
for selecting between Vancouver (Richard Lang) and 2265 (Spracklen's) programs.
The 2265 ROM half is almost identical to EAG V2 (the elite_1.6 one), the handful
of bytes difference is due to the changed hash RAM start address.

The switch is not supposed to be flipped while power is on, hence MAME will only
check it at reset (otherwise, the CPU will crash and may overwrite NVRAM). Each
program has its own NVRAM bank, so unfortunately the user can't save the game on
one program and then analyze or continue on the other.

Hardware info:
--------------
- TMP68HC000P-16, 16MHz XTAL
- 256KB ROM (2*M27C1001), 256KB RAM (2*TC551001PL-10)
- 64KB battery-backed RAM (2*KM62256AP-10), Dallas DS1210
- OKI M82C51A-2 USART, 4.9152MHz XTAL

RAM can't be expanded, there are MB1422A and SIMM PCB markings, but unpopulated.
The I/O remains the same as EAG V2.

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

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
#include "cpu/m68000/m68020.h"
#include "cpu/m68000/m68030.h"
#include "cpu/m68000/m68040.h"
#include "machine/clock.h"
#include "machine/gen_latch.h"
#include "machine/i8251.h"
#include "machine/ram.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "fidel_ex_68k.lh"
#include "fidel_eag_68k.lh"


namespace {

// EAG / shared

class eag_state : public driver_device
{
public:
	eag_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ram(*this, "ram"),
		m_usart(*this, "usart"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// machine configs
	void eagv4(machine_config &config);
	void eagv5(machine_config &config);
	void eagv7(machine_config &config);
	void eagv9(machine_config &config);
	void eagv10(machine_config &config);
	void eagv11(machine_config &config);

	void init_eag();

	DECLARE_INPUT_CHANGED_MEMBER(in1_changed) { update_dsr(); }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void eag_base(machine_config &config);

	// devices/pointers
	required_device<m68000_base_device> m_maincpu;
	optional_device<ram_device> m_ram;
	optional_device<i8251_device> m_usart;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	optional_ioport_array<4> m_inputs;

	u8 m_select = 0;
	u8 m_inp_mux = 0;
	u8 m_7seg_data = 0;
	u8 m_led_data = 0;

	// address maps
	void eag_map(address_map &map) ATTR_COLD;
	void eagv7_map(address_map &map) ATTR_COLD;
	void eagv10_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_display();
	void update_dsr();
	void mux_w(offs_t offset, u8 data);
	u8 input_r(offs_t offset);
	void leds_w(offs_t offset, u8 data);
	void digit_w(offs_t offset, u8 data);
};

void eag_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_select));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_7seg_data));
	save_item(NAME(m_led_data));
}

void eag_state::machine_reset()
{
	update_dsr();
}


// EAG V5

class eagv5_state : public eag_state
{
public:
	eagv5_state(const machine_config &mconfig, device_type type, const char *tag) :
		eag_state(mconfig, type, tag),
		m_subcpu(*this, "subcpu"),
		m_mainlatch(*this, "mainlatch"),
		m_sublatch(*this, "sublatch")
	{ }

	// machine configs
	void eagv5(machine_config &config);

private:
	// devices/pointers
	required_device<cpu_device> m_subcpu;
	required_device<generic_latch_8_device> m_mainlatch;
	required_device<generic_latch_8_device> m_sublatch;

	// address maps
	void main_map(address_map &map) ATTR_COLD;
	void sub_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void reset_subcpu_w(u8 data);
	u8 main_ack_r();
	u8 sub_ack_r();
};


// Elite Premiere

class premiere_state : public eag_state
{
public:
	premiere_state(const machine_config &mconfig, device_type type, const char *tag) :
		eag_state(mconfig, type, tag),
		m_nvram(*this, "nvram", 0x10000, ENDIANNESS_BIG),
		m_nvrambank(*this, "nvrambank"),
		m_rombank(*this, "rombank")
	{ }

	// machine configs
	void premiere(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	memory_share_creator<u16> m_nvram;
	required_memory_bank m_nvrambank;
	required_memory_bank m_rombank;

	// address maps
	void main_map(address_map &map) ATTR_COLD;
};

void premiere_state::machine_start()
{
	eag_state::machine_start();

	// init banks
	m_rombank->configure_entries(0, 2, memregion("maincpu")->base(), 0x20000);
	m_nvrambank->configure_entries(0, 2, m_nvram, 0x8000);
}

void premiere_state::machine_reset()
{
	eag_state::machine_reset();

	// program switch directly selects bank
	const u8 bank = m_inputs[3]->read() & 1;
	m_rombank->set_entry(bank);
	m_nvrambank->set_entry(bank);
}


// Excel 68000

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

	// machine configs
	void fex68k(machine_config &config);
	void fex68km2(machine_config &config);
	void fex68km3(machine_config &config);
	void fex68km4(machine_config &config);

private:
	// address maps
	void fex68k_map(address_map &map) ATTR_COLD;
	void fex68km2_map(address_map &map) ATTR_COLD;
	void fex68km3_map(address_map &map) ATTR_COLD;
	void fex68km4_map(address_map &map) ATTR_COLD;
};



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

// TTL/generic

void eag_state::update_display()
{
	// Excel 68000: 4*7seg leds, 8*8 chessboard leds
	// EAG: 8*7seg leds(2 panels), (8+1)*8 chessboard leds
	u8 seg_data = bitswap<8>(m_7seg_data,0,1,3,2,7,5,6,4);
	u8 led_data = bitswap<8>(m_led_data,0,1,2,3,4,5,6,7);
	m_display->matrix(1 << m_inp_mux, led_data << 8 | seg_data);
}

void eag_state::update_dsr()
{
	// USART DSR: 3 more buttons (and optional diode) on EAG
	if (m_usart != nullptr)
		m_usart->write_dsr(BIT(~m_inputs[1]->read(), m_inp_mux) & (BIT(m_select, 4) | m_inputs[2]->read()));
}

void eag_state::mux_w(offs_t offset, u8 data)
{
	// a1-a3,d0: 74259
	u8 mask = 1 << offset;
	m_select = (m_select & ~mask) | ((data & 1) ? mask : 0);

	// 74259 Q0-Q3: 74145 A-D (Q5-Q7 N/C)
	// 74259 Q4: optional diode to USART DSR on EAG
	m_inp_mux = m_select & 0xf;
	update_dsr();

	// 74145 0-8: input mux, digit/led select
	// 74145 9: speaker out
	m_dac->write(BIT(1 << m_inp_mux, 9));
	update_display();
}

u8 eag_state::input_r(offs_t offset)
{
	u8 data = 0;

	// a1-a3,d7: multiplexed inputs (active low)
	// read chessboard sensors
	if (m_inp_mux < 8)
	{
		// EAG chessboard is rotated 90 degrees
		if (m_inputs[2].read_safe(0) & 1)
			data = m_board->read_rank(m_inp_mux);
		else
			data = m_board->read_file(m_inp_mux, true);
	}

	// read button panel
	else if (m_inp_mux == 8)
		data = m_inputs[0]->read();

	return ~data << offset & 0x80;
}

void eag_state::leds_w(offs_t offset, u8 data)
{
	// a1-a3,d0: led data
	m_led_data = (m_led_data & ~(1 << offset)) | ((data & 1) << offset);
	update_display();
}

void eag_state::digit_w(offs_t offset, u8 data)
{
	// a1-a3,d0(d8): digit segment data
	m_7seg_data = (m_7seg_data & ~(1 << offset)) | ((data & 1) << offset);
	update_display();
}


// EAG V5

void eagv5_state::reset_subcpu_w(u8 data)
{
	// reset subcpu, from trigger to monostable 555 (R1=47K, C1=1uF)
	m_subcpu->pulse_input_line(INPUT_LINE_RESET, attotime::from_msec(52));
}

u8 eagv5_state::main_ack_r()
{
	// d8,d9: latches ack state
	return (m_mainlatch->pending_r() << 1 ^ 2) | m_sublatch->pending_r();
}

u8 eagv5_state::sub_ack_r()
{
	// d8,d9: latches ack state
	return (m_sublatch->pending_r() << 1 ^ 2) | m_mainlatch->pending_r();
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

// Excel 68000

void excel68k_state::fex68k_map(address_map &map)
{
	map(0x000000, 0x00ffff).rom();
	map(0x000000, 0x00000f).mirror(0x00fff0).w(FUNC(excel68k_state::leds_w)).umask16(0x00ff);
	map(0x000000, 0x00000f).mirror(0x00fff0).w(FUNC(excel68k_state::digit_w)).umask16(0xff00);
	map(0x044000, 0x047fff).ram();
	map(0x100000, 0x10000f).mirror(0x03fff0).r(FUNC(excel68k_state::input_r)).umask16(0x00ff);
	map(0x140000, 0x14000f).mirror(0x03fff0).w(FUNC(excel68k_state::mux_w)).umask16(0x00ff);
}

void excel68k_state::fex68km2_map(address_map &map)
{
	fex68k_map(map);
	map(0x200000, 0x21ffff).ram();
}

void excel68k_state::fex68km3_map(address_map &map)
{
	fex68k_map(map);
	map(0x200000, 0x20ffff).ram();
}

void excel68k_state::fex68km4_map(address_map &map)
{
	map(0x00000000, 0x0000ffff).rom();
	map(0x00000000, 0x0000000f).mirror(0x00fff0).w(FUNC(excel68k_state::leds_w)).umask32(0x00ff00ff);
	map(0x00000000, 0x0000000f).mirror(0x00fff0).w(FUNC(excel68k_state::digit_w)).umask32(0xff00ff00);
	map(0x00044000, 0x00047fff).ram(); // unused?
	map(0x00100000, 0x0010000f).mirror(0x03fff0).r(FUNC(excel68k_state::input_r)).umask32(0x00ff00ff);
	map(0x00140000, 0x0014000f).mirror(0x03fff0).w(FUNC(excel68k_state::mux_w)).umask32(0x00ff00ff);
	map(0x00200000, 0x0027ffff).ram();
	map(0x00400000, 0x00407fff).ram();
}


// EAG

void eag_state::init_eag()
{
	// eag_map: DRAM slots at $200000-$2fffff - V1/V2/V5: 128K, V3/V6: 512K, V4/V7/V9: 1M
	m_maincpu->space(AS_PROGRAM).install_ram(0x200000, 0x200000 + m_ram->size() - 1, m_ram->pointer());
}

void eag_state::eag_map(address_map &map)
{
	map(0x000000, 0x01ffff).rom();
	map(0x104000, 0x107fff).ram();
	map(0x300000, 0x30000f).mirror(0x000010).w(FUNC(eag_state::digit_w)).umask16(0xff00).nopr();
	map(0x300000, 0x30000f).mirror(0x000010).rw(FUNC(eag_state::input_r), FUNC(eag_state::leds_w)).umask16(0x00ff);
	map(0x400000, 0x40000f).w(FUNC(eag_state::mux_w)).umask16(0x00ff);
	map(0x400000, 0x407fff).r("cartslot", FUNC(generic_slot_device::read_rom)).umask16(0xff00);
	map(0x604000, 0x607fff).ram().share("nvram");
	map(0x700000, 0x700003).rw(m_usart, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
}

void eagv5_state::main_map(address_map &map)
{
	eag_map(map);
	map(0x500000, 0x500000).r(m_sublatch, FUNC(generic_latch_8_device::read)).w(m_mainlatch, FUNC(generic_latch_8_device::write));
	map(0x500002, 0x500002).rw(FUNC(eagv5_state::main_ack_r), FUNC(eagv5_state::reset_subcpu_w));
}

void eagv5_state::sub_map(address_map &map)
{
	map(0x000000, 0x00ffff).rom();
	map(0x000000, 0x000001).mirror(0x00fffe).w(m_sublatch, FUNC(generic_latch_8_device::write)).umask16(0x00ff);
	map(0x044000, 0x047fff).ram();
	map(0x140000, 0x140000).r(FUNC(eagv5_state::sub_ack_r));
	map(0x140001, 0x140001).r(m_mainlatch, FUNC(generic_latch_8_device::read));
}

void eag_state::eagv7_map(address_map &map)
{
	map(0x000000, 0x01ffff).rom();
	map(0x104000, 0x107fff).ram();
	map(0x300000, 0x30000f).mirror(0x000010).w(FUNC(eag_state::digit_w)).umask32(0xff00ff00).nopr();
	map(0x300000, 0x30000f).mirror(0x000010).rw(FUNC(eag_state::input_r), FUNC(eag_state::leds_w)).umask32(0x00ff00ff);
	map(0x400000, 0x40000f).w(FUNC(eag_state::mux_w)).umask32(0x00ff00ff);
	map(0x400000, 0x407fff).r("cartslot", FUNC(generic_slot_device::read_rom)).umask32(0xff00ff00);
	map(0x604000, 0x607fff).ram().share("nvram");
	map(0x700000, 0x700003).rw(m_usart, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask32(0x00ff00ff);
	map(0x800000, 0x807fff).ram();
}

void eag_state::eagv10_map(address_map &map)
{
	map(0x00000000, 0x0001ffff).rom();
	map(0x00280000, 0x0037ffff).ram();
	map(0x00b00000, 0x00b0000f).mirror(0x00000010).w(FUNC(eag_state::digit_w)).umask32(0xff00ff00).nopr();
	map(0x00b00000, 0x00b0000f).mirror(0x00000010).rw(FUNC(eag_state::input_r), FUNC(eag_state::leds_w)).umask32(0x00ff00ff);
	map(0x00c00000, 0x00c0000f).w(FUNC(eag_state::mux_w)).umask32(0x00ff00ff);
	map(0x00c00000, 0x00c07fff).r("cartslot", FUNC(generic_slot_device::read_rom)).umask32(0xff00ff00);
	map(0x00e04000, 0x00e07fff).ram().share("nvram");
	map(0x00f00000, 0x00f00003).rw(m_usart, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask32(0x00ff00ff);
	map(0x01018000, 0x0101ffff).ram();
}

void premiere_state::main_map(address_map &map)
{
	map(0x000000, 0x01ffff).bankr(m_rombank);
	map(0x100000, 0x13ffff).ram();
	map(0x300000, 0x30000f).mirror(0x000010).w(FUNC(premiere_state::digit_w)).umask16(0xff00).nopr();
	map(0x300000, 0x30000f).mirror(0x000010).rw(FUNC(premiere_state::input_r), FUNC(premiere_state::leds_w)).umask16(0x00ff);
	map(0x400000, 0x40000f).w(FUNC(premiere_state::mux_w)).umask16(0x00ff);
	map(0x600000, 0x607fff).mirror(0x008000).bankrw(m_nvrambank);
	map(0x700000, 0x700003).rw(m_usart, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( excel68k )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Verify / King")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Options / Queen")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Level / Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Take Back / Bishop")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Hint / Knight")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Move / Pawn")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Clear")
INPUT_PORTS_END

// EAG or EAS / Prestige button panel
#define HOUSING(x) PORT_CONDITION("IN.2", 0x01, EQUALS, x)

static INPUT_PORTS_START( eag_base )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(1) PORT_CODE(KEYCODE_V) PORT_NAME("RV")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(1) PORT_CODE(KEYCODE_O) PORT_NAME("Option")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(1) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("LV / Pawn")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(1) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("TB / Knight")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(1) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("ST / Bishop")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(1) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("TM / Rook")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(1) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("PV / Queen")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(1) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("PB / King")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(1) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CL") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eag_state::in1_changed), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(1) PORT_CODE(KEYCODE_M) PORT_NAME("DM") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eag_state::in1_changed), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(1) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_N) PORT_NAME("New Game") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eag_state::in1_changed), 0)
INPUT_PORTS_END

static INPUT_PORTS_START( eag )
	PORT_INCLUDE( eag_base )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(0) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(0) PORT_CODE(KEYCODE_O) PORT_NAME("Option")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(0) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("PB / King")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(0) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("PV / Queen")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(0) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("TM / Rook")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(0) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("ST / Bishop")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(0) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("TB / Knight")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(0) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("LV / Pawn")

	PORT_MODIFY("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(0) PORT_CODE(KEYCODE_M) PORT_NAME("DM") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eag_state::in1_changed), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(0) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CL") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eag_state::in1_changed), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) HOUSING(0) PORT_CODE(KEYCODE_V) PORT_NAME("RV") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eag_state::in1_changed), 0)

	PORT_START("IN.2") // factory set (diode)
	PORT_CONFNAME( 0x01, 0x01, "Housing" )
	PORT_CONFSETTING(    0x00, "Elite A/S / Prestige" )
	PORT_CONFSETTING(    0x01, "Elite Avant Garde" )
INPUT_PORTS_END

static INPUT_PORTS_START( premiere )
	PORT_INCLUDE( eag_base )

	PORT_START("IN.2") // does not work on Vancouver
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_CUSTOM)

	PORT_START("IN.3")
	PORT_CONFNAME( 0x01, 0x00, "Program" )
	PORT_CONFSETTING(    0x00, "Vancouver" )
	PORT_CONFSETTING(    0x01, "2265" )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void excel68k_state::fex68k(machine_config &config)
{
	// basic machine hardware
	M68000(config, m_maincpu, 12_MHz_XTAL); // HD68HC000P12
	m_maincpu->set_addrmap(AS_PROGRAM, &excel68k_state::fex68k_map);

	auto &irq_clock(CLOCK(config, "irq_clock", 600)); // 556 timer (22nF, 91K + 20K POT @ 14.8K, 0.1K), ideal is 600Hz (measured 580Hz, 604Hz, 632Hz)
	irq_clock.set_pulse_width(attotime::from_nsec(1525)); // active for 1.525us
	irq_clock.signal_handler().set_inputline(m_maincpu, M68K_IRQ_2);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(200));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 16);
	m_display->set_segmask(0x55, 0x7f);
	config.set_default_layout(layout_fidel_ex_68k);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void excel68k_state::fex68km2(machine_config &config)
{
	fex68k(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &excel68k_state::fex68km2_map);
}

void excel68k_state::fex68km3(machine_config &config)
{
	fex68k(config);

	// basic machine hardware
	m_maincpu->set_clock(16_MHz_XTAL); // factory overclock
	m_maincpu->set_addrmap(AS_PROGRAM, &excel68k_state::fex68km3_map);
}

void excel68k_state::fex68km4(machine_config &config)
{
	fex68k(config);

	// basic machine hardware
	M68020(config.replace(), m_maincpu, 20_MHz_XTAL); // XC68020RC16 or MC68020RC20E
	m_maincpu->set_addrmap(AS_PROGRAM, &excel68k_state::fex68km4_map);

	subdevice<clock_device>("irq_clock")->set_pulse_width(attotime::from_usec(10)); // irq active for 10us
}

void eag_state::eag_base(machine_config &config)
{
	// basic machine hardware
	M68000(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_interrupt_mixer(false);
	m_maincpu->set_addrmap(AS_PROGRAM, &eag_state::eag_map);

	auto &irq_clock(CLOCK(config, "irq_clock", 4.9152_MHz_XTAL / 0x2000)); // 4060 Q13, 600Hz
	irq_clock.set_pulse_width(attotime::from_nsec(8250)); // active for 8.25us
	irq_clock.signal_handler().set_inputline(m_maincpu, M68K_IRQ_IPL1);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	I8251(config, m_usart, 4.9152_MHz_XTAL);
	m_usart->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));

	auto &usart_clock(CLOCK(config, "usart_clock", 4.9152_MHz_XTAL / 128)); // 4060 Q7, 38.4kHz
	usart_clock.signal_handler().set(m_usart, FUNC(i8251_device::write_txc));

	auto &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_usart, FUNC(i8251_device::write_rxd));
	rs232.cts_handler().set(m_usart, FUNC(i8251_device::write_cts));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 16);
	m_display->set_segmask(0x1ef, 0x7f);
	config.set_default_layout(layout_fidel_eag_68k);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);

	// cartridge
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "fidel_scc");
	SOFTWARE_LIST(config, "cart_list").set_original("fidel_scc");
}

void eag_state::eagv4(machine_config &config)
{
	eag_base(config);

	// basic machine hardware
	RAM(config, m_ram).set_extra_options("128K, 512K, 1M");
	m_ram->set_default_size("1M");
	m_ram->set_default_value(0);
}

void eagv5_state::eagv5(machine_config &config)
{
	eagv4(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &eagv5_state::main_map);

	M68000(config, m_subcpu, 16_MHz_XTAL);
	m_subcpu->set_addrmap(AS_PROGRAM, &eagv5_state::sub_map);
	m_subcpu->set_periodic_int(FUNC(eagv5_state::irq2_line_hold), attotime::from_hz(16_MHz_XTAL/0x4000)); // 4060 Q14, ~1kHz

	GENERIC_LATCH_8(config, m_mainlatch);
	GENERIC_LATCH_8(config, m_sublatch);
	m_sublatch->data_pending_callback().set_inputline(m_maincpu, M68K_IRQ_IPL0);

	// gen_latch syncs on write, but this is still needed with tight cpu comms
	// (not that it locks up or anything, but it will calculate moves much slower if timing is off)
	config.set_maximum_quantum(attotime::from_hz(m_maincpu->clock() / 4));

	m_ram->set_default_size("128K");
}

void eag_state::eagv7(machine_config &config)
{
	eagv4(config);

	// basic machine hardware
	M68020(config.replace(), m_maincpu, 20_MHz_XTAL); // MC68020RC20E
	m_maincpu->set_interrupt_mixer(false);
	m_maincpu->set_addrmap(AS_PROGRAM, &eag_state::eagv7_map);

	m_ram->set_extra_options("512K, 1M");
}

void eag_state::eagv9(machine_config &config)
{
	eagv7(config);

	// basic machine hardware
	M68030(config.replace(), m_maincpu, 32_MHz_XTAL); // also seen with 40MHz XTAL
	m_maincpu->set_interrupt_mixer(false);
	m_maincpu->set_addrmap(AS_PROGRAM, &eag_state::eagv7_map);

	m_ram->set_extra_options("1M");
}

void eag_state::eagv10(machine_config &config)
{
	eag_base(config);

	// basic machine hardware
	M68040(config.replace(), m_maincpu, 25_MHz_XTAL);
	m_maincpu->set_interrupt_mixer(false);
	m_maincpu->set_addrmap(AS_PROGRAM, &eag_state::eagv10_map);

	// IRQ pulse should be shorter, guessed here
	subdevice<clock_device>("irq_clock")->set_pulse_width(attotime::from_nsec(1000));
}

void eag_state::eagv11(machine_config &config)
{
	eagv10(config);

	// basic machine hardware
	M68EC040(config.replace(), m_maincpu, 36_MHz_XTAL*2); // wrong! should be M68EC060
	m_maincpu->set_interrupt_mixer(false);
	m_maincpu->set_addrmap(AS_PROGRAM, &eag_state::eagv10_map);
}

void premiere_state::premiere(machine_config &config)
{
	eag_base(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &premiere_state::main_map);

	// no cartridge
	config.device_remove("cartslot");
	config.device_remove("cart_list");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( fex68k ) // model 6094
	ROM_REGION16_BE( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("e3_yellow.u6", 0x00000, 0x08000, CRC(a8a27714) SHA1(bc42a561eb39dd389c7831f1a25ad260510085d8) ) // AT27C256-15
	ROM_LOAD16_BYTE("o4_red.u7",    0x00001, 0x08000, CRC(560a14b7) SHA1(11f2375255bfa229314697f103e891ba1cf0c715) ) // "
ROM_END

ROM_START( fex68ka )
	ROM_REGION16_BE( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("e3_yellow.u6", 0x00000, 0x08000, CRC(7dc60d05) SHA1(e47b4d4e64c4cac6c5a94a900c9f2dd017f849ce) )
	ROM_LOAD16_BYTE("o4_red.u7",    0x00001, 0x08000, CRC(4b738583) SHA1(ff506296ea460c7ed852339d2ab24aaae01730d8) )
ROM_END

ROM_START( fex68kb )
	ROM_REGION16_BE( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("e3_yellow.u6", 0x00000, 0x08000, CRC(d9f252f5) SHA1(205cdbadb58a4cdd486d4e40d2fe6a5209d2f8a4) )
	ROM_LOAD16_BYTE("o4_red.u7",    0x00001, 0x08000, CRC(3bf8b3d7) SHA1(6ce419c63159501d2349abfd1e142e38e5466fbc) )
ROM_END

ROM_START( fex68km2 ) // model 6097
	ROM_REGION16_BE( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("e6_yellow.u6", 0x00000, 0x08000, CRC(2e65e7ad) SHA1(4f3aec12041c9014d5d700909bac66bae1f9eadf) ) // 27c256
	ROM_LOAD16_BYTE("o7_red.u7",    0x00001, 0x08000, CRC(4c20334a) SHA1(2e575b88c41505cc89599d2fc13e1e84fe474469) ) // "
ROM_END

ROM_START( fex68km2a ) // model 6097
	ROM_REGION16_BE( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("e7_yellow.u6", 0x00000, 0x08000, CRC(7c88f53b) SHA1(80fdeed90f1388053110c3bc385bc50c2884b11a) ) // 27c256
	ROM_LOAD16_BYTE("o8_red.u7",    0x00001, 0x08000, CRC(26da3424) SHA1(e0cec467bf1249ce89a27c45f5013859e6581ecd) ) // "
ROM_END

ROM_START( fex68km3 ) // model 6098 - checksum DB53
	ROM_REGION16_BE( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("me_white.u6",  0x00000, 0x08000, CRC(4b14cd9f) SHA1(4d41196900a71bf0699dae50f4726acc0ed3dced) ) // 27c256
	ROM_LOAD16_BYTE("mo_yellow.u7", 0x00001, 0x08000, CRC(b96b0b5f) SHA1(281145be802efb38ed764aecb26b511dcd71cb87) ) // "
ROM_END

ROM_START( fex68km3a ) // model 6098 - checksum F806
	ROM_REGION16_BE( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("me_white.u6",  0x00000, 0x08000, CRC(434b68ec) SHA1(eaf6d2b1db3c0145adfd9d2c5d5d5ec65524b5de) ) // 27c256
	ROM_LOAD16_BYTE("mo_yellow.u7", 0x00001, 0x08000, CRC(5e78ee99) SHA1(70e5e47b6daa35dc4d5675d4d1c6a4093fb47ea6) ) // "
ROM_END

ROM_START( fex68km3b ) // model 6098 - checksum E2DA
	ROM_REGION16_BE( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("me_white.u6",  0x00000, 0x08000, CRC(39e17293) SHA1(61d557f96ae2bb164108f53d7d48239f8f2a7f6b) ) // 27c256
	ROM_LOAD16_BYTE("mo_yellow.u7", 0x00001, 0x08000, CRC(572b208a) SHA1(cd50982d8fa5bde9ac83472fddd14d56fa6ead3b) ) // "
ROM_END

ROM_START( fex68km4 ) // model 6110, serial 9004000x or 9004009x - checksum 1185
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("68020_mach_iv_v1.3a_even_11_30_88.u6", 0x00000, 0x08000, CRC(ea47a712) SHA1(bd853f74c83d760a4f4df0250248cbdba8f1f241) ) // also seen with "Mach 3" label, same SUM16
	ROM_LOAD16_BYTE("68020_mach_iv_v1.3a_odd_11_30_88.u7",  0x00001, 0x08000, CRC(41b5860d) SHA1(935c43ef1ff511426f49e83e29014883c68e99b4) ) // "
ROM_END

ROM_START( fex68km4a ) // no model # on backplate, serial 0009999x but modified hardware - checksum FD9B
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("68020_even_master_2325.u6", 0x00000, 0x08000, CRC(13ea816c) SHA1(98d00fc382ddcbccb0a47c3f8d7fc73f30a15fbd) )
	ROM_LOAD16_BYTE("68020_odd_master_2325.u7",  0x00001, 0x08000, CRC(d24c7b54) SHA1(3204fd600786792a618965715990c44890cc7119) )
ROM_END


ROM_START( feagv4 ) // dumped from a V3 - checksum F66D 4B3E
	ROM_REGION16_BE( 0x20000, "maincpu", 0 )
	ROM_LOAD16_BYTE("elite_1.6_e.u22", 0x00000, 0x10000, CRC(c8b89ccc) SHA1(d62e0a72f54b793ab8853468a81255b62f874658) )
	ROM_LOAD16_BYTE("elite_1.6_o.u19", 0x00001, 0x10000, CRC(904c7061) SHA1(742110576cf673321440bc81a4dae4c949b49e38) )
ROM_END

ROM_START( feagv4a ) // dumped from a V2 - checksum FD5C 49AC
	ROM_REGION16_BE( 0x20000, "maincpu", 0 )
	ROM_LOAD16_BYTE("6114_e5_yellow.u22", 0x00000, 0x10000, CRC(f9c7bada) SHA1(60e545f829121b9a4f1100d9e85ac83797715e80) ) // 27c512
	ROM_LOAD16_BYTE("6114_o5_green.u19",  0x00001, 0x10000, CRC(04f97b22) SHA1(8b2845dd115498f7b385e8948eca6a5893c223d1) ) // "
ROM_END

ROM_START( feagv4b ) // dumped from a V2 - checksum 0DCC 5536
	ROM_REGION16_BE( 0x20000, "maincpu", 0 )
	ROM_LOAD16_BYTE("6114_e_yellow.u22", 0x00000, 0x10000, CRC(70f66709) SHA1(ad78b251d852930b3c79582e913bfc68527e3791) ) // 27c512
	ROM_LOAD16_BYTE("6114_o1_green.u19", 0x00001, 0x10000, CRC(bd9258ac) SHA1(6cdecdf841312cfc9daa25dba27b9005dcfcd679) ) // "
ROM_END


ROM_START( feagv5 )
	ROM_REGION16_BE( 0x20000, "maincpu", 0 ) // PCB label 510.1136A01 - checksum 0140 9CF2
	ROM_LOAD16_BYTE("master_e", 0x00000, 0x10000, CRC(e424bddc) SHA1(ff03656addfe5c47f06df2efb4602f43a9e19d96) )
	ROM_LOAD16_BYTE("master_o", 0x00001, 0x10000, CRC(33a00894) SHA1(849460332b1ac10d452ca3631eb99f5597511b73) )

	ROM_REGION16_BE( 0x10000, "subcpu", 0 ) // PCB label 510.1138B01
	ROM_LOAD16_BYTE("slave_e", 0x00000, 0x08000, CRC(eea4de52) SHA1(a64ca8a44b431e2fa7f00e44cab7e6aa2d4a9403) )
	ROM_LOAD16_BYTE("slave_o", 0x00001, 0x08000, CRC(35fe2fdf) SHA1(731da12ee290bad9bc03cffe281c8cc48e555dfb) )
ROM_END


ROM_START( feagv7 ) // also seen on a V9 - checksum F702 6863
	ROM_REGION( 0x20000, "maincpu", 0 )
	ROM_LOAD16_BYTE("6117_e3_yellow.u22", 0x00000, 0x10000, CRC(60523199) SHA1(a308eb6b782732af1ab2fd0ed8b046de7a8dd24b) )
	ROM_LOAD16_BYTE("6117_o3_red.u19",    0x00001, 0x10000, CRC(44fbb3b0) SHA1(8bf5c7ac5801f5a656ae710c1a61b693f5314b8c) )
ROM_END

ROM_START( feagv7a ) // dumped from a repro pcb - checksum FCA0 6969
	ROM_REGION( 0x20000, "maincpu", 0 )
	ROM_LOAD16_BYTE("eag-v7b", 0x00000, 0x10000, CRC(f2f68b63) SHA1(621e5073e9c5083ac9a9b467f3ef8aa29beac5ac) )
	ROM_LOAD16_BYTE("eag-v7a", 0x00001, 0x10000, CRC(506b688f) SHA1(0a091c35d0f01166b57f964b111cde51c5720d58) )
ROM_END

ROM_START( feagv7b ) // PCB label 510.1136A01, dumped from a V6 - checksum 005D 6AB7
	ROM_REGION( 0x20000, "maincpu", 0 )
	ROM_LOAD16_BYTE("e1_yellow.u22", 0x00000, 0x10000, CRC(2fa692a9) SHA1(357fd47e97f823462e372c7b4d0730c1fa35c364) )
	ROM_LOAD16_BYTE("o1_red.u19",    0x00001, 0x10000, CRC(bceb99f0) SHA1(601869be5fb9724fe75f14d4dac58471eed6e0f4) )
ROM_END

ROM_START( feagv7c ) // checksum 00D5 6939
	ROM_REGION( 0x20000, "maincpu", 0 )
	ROM_LOAD16_BYTE("e1_yellow.u22", 0x00000, 0x10000, CRC(44baefbf) SHA1(dbc24340d7e3013cc8f111ebb2a59169c5dcb8e8) )
	ROM_LOAD16_BYTE("o1_red.u19",    0x00001, 0x10000, CRC(951a7857) SHA1(dad21b049fd4f411a79d4faefb922c1277569c0e) )
ROM_END

// feagv9 has same ROMs as feagv7
#define rom_feagv9 rom_feagv7
#define rom_feagv9a rom_feagv7a
#define rom_feagv9b rom_feagv7b
#define rom_feagv9c rom_feagv7c


ROM_START( feagv10 ) // checksum F40C 38B9
	ROM_REGION( 0x20000, "maincpu", 0 )
	ROM_LOAD32_BYTE("16", 0x00000, 0x08000, CRC(8375d61f) SHA1(e042f6f01480c59ee09a458cf34f135664479824) ) // 27c256
	ROM_LOAD32_BYTE("17", 0x00001, 0x08000, CRC(bfd14916) SHA1(115af6dfd29ddd8ad6d2ce390f8ecc4d60de6fce) ) // "
	ROM_LOAD32_BYTE("18", 0x00002, 0x08000, CRC(9341dcaf) SHA1(686bd4799e89ffaf11a813d4cf5a2aedd4c2d97a) ) // "
	ROM_LOAD32_BYTE("19", 0x00003, 0x08000, CRC(a70c5468) SHA1(7f6b4f46577d5cfdaa84d387c7ce35d941e5bbc7) ) // "
ROM_END

ROM_START( feagv11 ) // checksum F40C 38B9
	ROM_REGION( 0x20000, "maincpu", 0 )
	ROM_LOAD32_BYTE("16", 0x00000, 0x08000, CRC(8375d61f) SHA1(e042f6f01480c59ee09a458cf34f135664479824) ) // 27c256
	ROM_LOAD32_BYTE("17", 0x00001, 0x08000, CRC(bfd14916) SHA1(115af6dfd29ddd8ad6d2ce390f8ecc4d60de6fce) ) // "
	ROM_LOAD32_BYTE("18", 0x00002, 0x08000, CRC(9341dcaf) SHA1(686bd4799e89ffaf11a813d4cf5a2aedd4c2d97a) ) // "
	ROM_LOAD32_BYTE("19", 0x00003, 0x08000, CRC(a70c5468) SHA1(7f6b4f46577d5cfdaa84d387c7ce35d941e5bbc7) ) // "
ROM_END


ROM_START( premiere ) // model 6131, PCB label 510.1157A01 - checksum (2265 only) F667 4B06
	ROM_REGION16_BE( 0x40000, "maincpu", 0 )
	ROM_LOAD16_BYTE("101.1103a01_1meg_even.u22", 0x00000, 0x20000, CRC(0df2d4d8) SHA1(2c6cd8d83768d14aeb9860be76ed2ec0f64f118b) ) // M27C1001
	ROM_LOAD16_BYTE("101.1104a01_1meg_odd.u19",  0x00001, 0x20000, CRC(afae9d5e) SHA1(7ab5fb8b8a2fa30f2fd444a050eae2432c9236d0) ) // "
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT   COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1987, fex68k,    0,       0,      fex68k,   excel68k, excel68k_state, empty_init, "Fidelity International", "Excel 68000 (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1987, fex68ka,   fex68k,  0,      fex68k,   excel68k, excel68k_state, empty_init, "Fidelity International", "Excel 68000 (set 2)", MACHINE_SUPPORTS_SAVE )
SYST( 1987, fex68kb,   fex68k,  0,      fex68k,   excel68k, excel68k_state, empty_init, "Fidelity International", "Excel 68000 (set 3)", MACHINE_SUPPORTS_SAVE )
SYST( 1988, fex68km2,  fex68k,  0,      fex68km2, excel68k, excel68k_state, empty_init, "Fidelity International", "Excel 68000 Mach II (rev. C+, set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1988, fex68km2a, fex68k,  0,      fex68km2, excel68k, excel68k_state, empty_init, "Fidelity International", "Excel 68000 Mach II (rev. C+, set 2)", MACHINE_SUPPORTS_SAVE )
SYST( 1988, fex68km3,  fex68k,  0,      fex68km3, excel68k, excel68k_state, empty_init, "Fidelity International", "Excel 68000 Mach III Master 2265 (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1988, fex68km3a, fex68k,  0,      fex68km3, excel68k, excel68k_state, empty_init, "Fidelity International", "Excel 68000 Mach III Master 2265 (set 2)", MACHINE_SUPPORTS_SAVE )
SYST( 1988, fex68km3b, fex68k,  0,      fex68km3, excel68k, excel68k_state, empty_init, "Fidelity International", "Excel 68000 Mach III Master 2265 (set 3)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, fex68km4,  fex68k,  0,      fex68km4, excel68k, excel68k_state, empty_init, "Fidelity International", "Excel 68000 Mach IV 68020 Master 2325 (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, fex68km4a, fex68k,  0,      fex68km4, excel68k, excel68k_state, empty_init, "Fidelity International", "Excel 68000 Mach IV 68020 Master 2325 (set 2)", MACHINE_SUPPORTS_SAVE )

SYST( 1989, feagv4,    0,       0,      eagv4,    eag,      eag_state,      init_eag,   "Fidelity International", "Elite Avant Garde 2265 (model 6114-2/3/4, set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, feagv4a,   feagv4,  0,      eagv4,    eag,      eag_state,      init_eag,   "Fidelity International", "Elite Avant Garde 2265 (model 6114-2/3/4, set 2)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, feagv4b,   feagv4,  0,      eagv4,    eag,      eag_state,      init_eag,   "Fidelity International", "Elite Avant Garde 2265 (model 6114-2/3/4, set 3)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, feagv5,    feagv4,  0,      eagv5,    eag,      eagv5_state,    init_eag,   "Fidelity International", "Elite Avant Garde 2265 (model 6114-5)", MACHINE_SUPPORTS_SAVE )

SYST( 1989, feagv7,    feagv4,  0,      eagv7,    eag,      eag_state,      init_eag,   "Fidelity International", "Elite Avant Garde 2325 (model 6117-6/7, set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, feagv7a,   feagv4,  0,      eagv7,    eag,      eag_state,      init_eag,   "Fidelity International", "Elite Avant Garde 2325 (model 6117-6/7, set 2)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, feagv7b,   feagv4,  0,      eagv7,    eag,      eag_state,      init_eag,   "Fidelity International", "Elite Avant Garde 2325 (model 6117-6/7, set 3)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, feagv7c,   feagv4,  0,      eagv7,    eag,      eag_state,      init_eag,   "Fidelity International", "Elite Avant Garde 2325 (model 6117-6/7, set 4)", MACHINE_SUPPORTS_SAVE )

SYST( 1989, feagv9,    feagv4,  0,      eagv9,    eag,      eag_state,      init_eag,   "Fidelity International", "Elite Avant Garde 2325 (model 6117-9, set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, feagv9a,   feagv4,  0,      eagv9,    eag,      eag_state,      init_eag,   "Fidelity International", "Elite Avant Garde 2325 (model 6117-9, set 2)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, feagv9b,   feagv4,  0,      eagv9,    eag,      eag_state,      init_eag,   "Fidelity International", "Elite Avant Garde 2325 (model 6117-9, set 3)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, feagv9c,   feagv4,  0,      eagv9,    eag,      eag_state,      init_eag,   "Fidelity International", "Elite Avant Garde 2325 (model 6117-9, set 4)", MACHINE_SUPPORTS_SAVE )

SYST( 1990, feagv10,   feagv4,  0,      eagv10,   eag,      eag_state,      empty_init, "Fidelity International", "Elite Avant Garde 2325 (model 6117-10)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_TIMING )
SYST( 2001, feagv11,   feagv4,  0,      eagv11,   eag,      eag_state,      empty_init, "hack (Wilfried Bucke)", "Elite Avant Garde 2325 (model 6117-11)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_TIMING )

SYST( 1992, premiere,  0,       0,      premiere, premiere, premiere_state, empty_init, "Fidelity Electronics International", "Elite Premiere", MACHINE_SUPPORTS_SAVE )



easy_karaoke.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/******************************************************************************

    IVL Technologies Karaoke systems

    KaraokeTV Star (c) IVL Technologies

    licensed out as

    KaraokeStation (Japan) (c) Bandai
    Easy Karaoke Groove Station (UK) (c) Easy Karaoke
    KaraokeMicro Star (France) (c) Lexibook

    -------------------------------------

    Easy Karaoke uses

    Clarity 4.3 ARM
    SVI1186
    NV0165  0317
    Sound Vision Inc.

    an overview for 4.1 and 4.2 can be found at
    http://web.archive.org/web/20031212120255fw_/http://www.soundvisioninc.com/OEMProducts/C4datasheet072401.pdf
    Amusingly this datasheet advertises 'MAME Game emulation' as one of the capabilities despite the chip
    clocking in at only 72Mhz

    Support chip is

    IVL
    Technologies
    ICS0253R1.0
    UA1068ABK-RD
    0327 A01491F

    RAM chip is

    IC42S16400-7T

    ROM is

    IVL
    Technologies
    ICS0303-B
    (c)1985-1986
    3415BAI THAI

    --------------

    Cartridges contain:

    1x MX 29LV040TC-90 (Flash ROM)

    1x HC573A

    1x ICSI IC89LV52A-24PQ (80C52 MCU with 8KBytes Flash memory, can be read protected)

    presumably manages a serial protocol to send data to the main unit


    -----------------

    Lexibook's KaraokeMicro Star version uses

    Clarity 4.1 ARM
    SV11180
    NV0093  0246
    Sound Vision Inc.

    ------------------

    KaraokeTV Star also uses the Clarity 4.1 but with "JVR043  0225" numbering

    Packaging also shows 'On-Key Karaoke' logo on box, maybe this is the original US / Canada product name?

    "Karaoke TV Star" appears to be a US product using this technology - advertises 50 built in songs, but
    also a downloadable service.  It has a 2002 date on the box / product.  Another version offers 35 songs

    ------------------

    For units which allow downloadable songs the only difference in the bootloader ROMs is a couple of bytes
    at the end.  This may be used as security, to tie the downloads stored in the flash ROM to the units as
    the format of the downloaded data in the flash ROMs appears to be different in each case, starting with
    different unique bytes after the header information for each unit we've seen. (maybe encryption?)
    The bootloader ROM has a unique ID on a sticker in each case too (could be a bytesum, haven't checked)


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

#include "emu.h"

#include "cpu/arm7/arm7.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class ivl_karaoke_state : public driver_device
{
public:
	ivl_karaoke_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
	{ }

	void ivl_karaoke_base(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	uint32_t a000004_r();

	void arm_map(address_map &map) ATTR_COLD;
};

class easy_karaoke_cartslot_state : public ivl_karaoke_state
{
public:
	easy_karaoke_cartslot_state(const machine_config &mconfig, device_type type, const char *tag)
		: ivl_karaoke_state(mconfig, type, tag)
		, m_cart(*this, "cartslot")
		, m_cart_region(nullptr)
	{ }

	void easy_karaoke(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	required_device<generic_slot_device> m_cart;
	memory_region *m_cart_region;
};

uint32_t ivl_karaoke_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void ivl_karaoke_state::machine_start()
{

}

void easy_karaoke_cartslot_state::machine_start()
{
	ivl_karaoke_state::machine_start();

	// if there's a cart, override the standard mapping
	if (m_cart && m_cart->exists())
	{
		m_cart_region = memregion(std::string(m_cart->tag()) + GENERIC_ROM_REGION_TAG);
	}
}

void ivl_karaoke_state::machine_reset()
{
	m_maincpu->set_state_int(arm7_cpu_device::ARM7_R15, 0x04000000);
}

DEVICE_IMAGE_LOAD_MEMBER(easy_karaoke_cartslot_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

static INPUT_PORTS_START( ivl_karaoke )
INPUT_PORTS_END

uint32_t ivl_karaoke_state::a000004_r()
{
	return machine().rand();
}

void ivl_karaoke_state::arm_map(address_map &map)
{
	map(0x00000000, 0x007fffff).ram();
	map(0x04000000, 0x047fffff).rom().region("maincpu", 0);
	map(0x0a000004, 0x0a000007).r(FUNC(ivl_karaoke_state::a000004_r));
}


void ivl_karaoke_state::ivl_karaoke_base(machine_config &config)
{
	ARM9(config, m_maincpu, 72000000); // ARM 720 core
	m_maincpu->set_addrmap(AS_PROGRAM, &ivl_karaoke_state::arm_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320, 262);
	m_screen->set_visarea(0, 320-1, 0, 240-1);
	m_screen->set_screen_update(FUNC(ivl_karaoke_state::screen_update));

	SPEAKER(config, "speaker", 2).front();
}

void easy_karaoke_cartslot_state::easy_karaoke(machine_config &config)
{
	ivl_karaoke_base(config);

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "easy_karaoke_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(easy_karaoke_cartslot_state::cart_load));
	SOFTWARE_LIST(config, "cart_list").set_original("easy_karaoke_cart");
}

/*
The 'karatvst' set has the following 50 songs built in, there don't appear to be any downloaded songs in this NAND dump

ABC                                         Jackson 5
All I Have To Give                          Backstreet Boys
Always On My Mind                           Elvis Presley
America The Beautiful                       Standard
Baby Love                                   Supremes
...Baby One More Time                       Britney Spears
Born To Make You Happy                      Britney Spears
Brick House                                 The Commodores
Bye Bye Bye                                 N'Sync
Dancing Queen                               Abba
Don't Let Me Get Me                         pink
Drive (For Daddy Gene)                      Alan Jackson
Fallin'                                     Alicia Keys
Girlfriend                                  N'Sync
Goodbye Earl                                Dixie Chicks
Hit 'Em Up Style                            Blu Cantrell
I Believe I Can Fly                         R. Kelly
I Heard It Through the Grapevine            Marvin Gaye
I Should Be Sleeping                        Emerson Drive
I Wanna Know                                Joe
I Want It That Way                          Backstreet Boys
If You're Gone                              Matchbox 20
It's A Great Day To Be Alive                Travis Tritt
Lady Marmalade                              Christina Aguilera
Love Shack                                  B52's
Me And Bobby McGee                          Kris Kristofferson
My Girl                                     Temptations
My Guy                                      Mary Wells
New York New York                           Frank Sinatra
No More Drama                               Mary J. Blige
One Fine Day                                Chiffons
Oops...I Did It Again                       Britney Spears
Over The Rainbow                            Judy Garland
Overprotected                               Britney Spears
Stand By Your Man                           Tammy Wynette
Star Spangled Banner                        Public Domain
Stop In The Name Of Love                    Supremes
Stronger                                    Britney Spears
Super Freak                                 Rick James
Superman                                    Five For Fighting
That's The Way (I Like It)                  KC And The Sunshine Band
The Greatest Love Of All                    Whitney Houston
The Loco-Motion                             Kylie Minogue
The One                                     Backstreet Boys
There Is No Arizona                         Jamie O'Neal
We Wish You A Merry Xmas                    Traditional
What's Going On                             Marvin Gaye
Wild Thing                                  The Troggs
Wrapped Around                              Brad Paisley
You Can't Hurry Love                        The Supremes

*/
ROM_START( karatvst )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "karaoke37vf010.bin", 0x000000, 0x20000, CRC(9d3020e4) SHA1(bce5d42ecff88b310a43599c9e47cba920c6b6e1) ) // bootloader

	ROM_REGION( 0x840000, "nand", ROMREGION_ERASEFF ) // NAND with main program, graphics, built in songs (and potentially user downloads)
	ROM_LOAD( "karaoketc58v64bft.bin", 0x000000, 0x840000, CRC(8cf42f20) SHA1(fae09ab08035e8c894fe00dcb23746ea78391d7f) )
ROM_END

/*
The 'karatvsta' set has the following 25 songs built in, there don't appear to be any downloaded songs in this NAND dump
These are a subset of the songs in the 50 song unit, and while the box advertises 35 songs, 10 of those are via a download voucher

Always On My Mind                           Elvis Presley (R)
Brick House                                 Commodores, The
Dancing Queen                               Abba
Don't Let Me Get Me                         Pink
Drive (For Daddy Gene)                      Alan Jackson
Fallin'                                     Alicia Keys
Goodbye Earl                                Dixie Chicks
Heard It Through The Grapevine              Marvin Gaye
I Should Be Sleeping                        Emerson Drive
Lady Marmalade                              Christina Aguilera
Love Shack                                  B52's
Me And Bobby Mcgee                          Janis Joplin
My Girl                                     Temptations
My Guy                                      Mary Wells
New York New York                           Frank Sinatra
No More Drama                               Mary J.Blige
Over The Rainbow                            Judy Garland
Stand By Your Man                           Tammy Wynette
That's The Way (I Like It)                  KC And The Sunshine Band
The Greatest Love Of All                    Whitney Houston
The Loco-Motion                             Kylie Minogue
There Is No Arizona                         Jamie O'neal
What's Going On                             Marvin Gaye
Wild Thing                                  Troggs
Wrapped Around                              Brad Paisley
*/

ROM_START( karatvsta )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "tvstarkaraoke_sst37vf010_bfc5.bin", 0x000000, 0x20000, CRC(fbac56e9) SHA1(bae393ce86de108b6ddc603770863ced885280b6) ) // bootloader, only final bytes differ from karatvsta, serial number?

	ROM_REGION( 0x840000, "nand", ROMREGION_ERASEFF ) // NAND with main program, graphics, built in songs (and potentially user downloads)
	ROM_LOAD( "tvstarkaraoke_tc58v64aft_98e6a59a.bin", 0x000000, 0x840000, CRC(207f2c6b) SHA1(a8853b792ba23bdead913c8d5ad0c75c39a48c99) )
ROM_END

/*

ABC                                         Jackson 5
All I Have To Give                          Backstreet Boys
Always On My Mind                           Elvis Presley
Amazing Grace                               Traditional
America The Beautiful                       Standard
Auld Lang Syne                              Traditional
...Baby One More Time                       Britney Spears
Brick House                                 The Commodores
Bye Bye Bye                                 N'Sync
Dancing Queen                               Abba
 Danny Boy                                   Traditional
Don't Let Me Get Me                         pink
Drive (For Daddy Gene)                      Alan Jackson
Fallin'                                     Alicia Keys
Girlfriend                                  N'Sync
Goodbye Earl                                Dixie Chicks
Hit 'Em Up Style                            Blu Cantrell
I Believe I Can Fly                         R. Kelly
I Heard It Through the Grapevine            Marvin Gaye
I Wanna Know                                Joe
I Want It That Way                          Backstreet Boys
It's A Great Day To Be Alive                Travis Tritt
Jingle Bells                                Traditional
Lady Marmalade                              Christina Aguilera
Love Shack                                  B52's
Me And Bobby McGee                          Kris Kristofferson
My Girl                                     Temptations
My Guy                                      Mary Wells
New York New York                           Frank Sinatra
No More Drama                               Mary J. Blige
O Sole Mio                                  Traditional
One Fine Day                                Chiffons
Oops...I Did It Again                       Britney Spears
Over The Rainbow                            Judy Garland
Overprotected                               Britney Spears
Stand By Your Man                           Tammy Wynette
Star Spangled Banner                        Public Domain
Stop In The Name Of Love                    Supremes
Stronger                                    Britney Spears
Superman                                    Five For Fighting
Swing Low, Sweet Chariot                    Traditional
That's The Way (I Like It)                  KC And The Sunshine Band
The Greatest Love Of All                    Whitney Houston
The One                                     Backstreet Boys
There Is No Arizona                         Jamie O'Neal
We Wish You A Merry Xmas                    Traditional
What's Going On                             Marvin Gaye
When The Saints Go Marching In              Traditional
Wild Thing                                  The Troggs
Wrapped Around                              Brad Paisley

-- downloaded songs?

All The Things You Are                      Frank Sinatra
How Great Thou Art                          Popular
(Let Me Be Your) Teddy Bear                 Elvis Presley
All I Have To Do Is Dream                   Everly Brothers
Ave Maria                                   Christmas
Blue Hawaii                                 Elvis Presley
Blue Velvet                                 Bobby Vinton
Breaking Up Is Hard To Do                   Neil Sedaka
Calendar Girl                               Neil Sedaka
Can't Help Falling In Love                  Elvis Presley
Don't                                       Elvis Presley
Everybody Loves Somebody                    Dean Martin
For Me And My Gal                           Popular
Frosty The Snowman                          Christmas
Georgia On My Mind                          Ray Charles
Give My Regards To Broadway                 Popular
Good Luck Charm                             Elvis Presley
It's Now Or Never                           Elvis Presley
Jingle Bells                                Christmas
Love Me Tender                              Elvis Presley
Moon River                                  Andy Williams
O Holy Night (Cantique De Noel)             Christmas
Put Your Head On My Shoulder                Paul Anka
Rudolph The Red Nosed Reindeer              Christmas
Surfin' U.S.A.                              Beach Boys
That's Life                                 Frank Sinatra
The Beat Goes On                            Sonny & Cher
Twinkle, Twinkle, Little Star               Children
Unchained Melody                            Righteous Brothers
What A Wonderful World                      Louis Armstrong
White Christmas                             Bing Crosby
Yesterday Once More                         Carpenters
zz Free Test Song                           For Testing Download
Crazy                                       Leann Rimes
Nobody Does It Better                       Carly Simon
As Time Goes By                             Frank Sinatra
Smoke Gets In Your Eyes                     Platters
Could I Have This Dance                     Anne Murray
The Christmas Song                          Nat King Cole
I Only Have Eyes For You                    Frank Sinatra
Oh Pretty Woman                             Roy Orbison
When I Fall In Love                         Clive Griffin
Live And Let Die                            Guns N' Roses
You're Sixteen                              Ringo Starr
Puff (The Magic Dragon)                     Peter, Paul & Mary
Crazy                                       Patsy Cline
Hopelessly Devoted to You                   Olivia Newton-John
I Can't Stop Loving You                     Ray Charles
On The Road Again                           Willie Nelson
You Light Up My Life                        Leann Rimes
Happy Birthday Sweet Sixteen                Neil Sedaka
Save The Last Dance For Me                  Drifters
You're Nobody Till Somebody Loves You       Dean Martin
Only You                                    Platters

*/

ROM_START( mks4001 )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "memorex_sst37vf010.bin", 0x000000, 0x20000, CRC(e2eb86f1) SHA1(846734c1e193b268e24b8493334639a4311a8053) ) // bootloader, only final bytes differ from karatvsta, serial number?

	ROM_REGION( 0x840000, "nand", ROMREGION_ERASEFF ) // NAND with main program, graphics, built in songs (and user downloads)
	ROM_LOAD( "memorex_tc58v64bft.bin", 0x000000, 0x840000, CRC(980589e8) SHA1(e3f8d65ba92e7dbdba356a8c565aef9b0e800347) )
ROM_END

/*

the bkarast set contains the following 68 songs, the box indicates there are '3 trial songs' so most of these are probably downloaded
to the unit?

長崎は今日も雨だった                      内山田洋とクール・ファイブ
ＴＳＵＮＡＭＩ                           サザンオールスターズ
北酒場                                   細川たかし
未来へ                                   Kiroro
らいおんハート                            SMAP
ザ☆ピ～ス！                              モーニング娘。
君をのせて                                天空の城ラピュタより
となりのトトロ                           となりのトトロ　井上あずみ
おどるポンポコリン                          Ｂ．Ｂ．クィーンズ
ついて来るかい                              小林旭
あの娘が泣いてる波止場                      三橋美智也
君といつまでも                              加山雄三
いい日旅立ち                              山口百恵
矢切の渡し                                細川たかし
恋の町札幌                               石原裕次郎
またあえる日まで                             ゆ　ず
また逢う日まで                              尾崎紀世彦
中の島ブルース                             内山田洋とクール・ファイブ
燃えてヒーロー                              キャプテン翼　沖田浩之・小粥よう子
アカシアの雨がやむとき                        西田佐知子
霧の摩周湖                               布施明
圭子の夢は夜ひらく                          藤圭子
きよしのズンドコ節                           氷川きよし
人生いろいろ                              島倉千代子
真赤な太陽                               美空ひばり＆ジャッキー吉川とブルー・コメッツ
別れの一本杉                             春日八郎
天城越え                                 石川さゆり
夜霧よ今夜も有難う                         石原裕次郎
命くれない                                 瀬川瑛子
千曲川                                  五木ひろし
川の流れのように                           美空ひばり
北国の春                                千昌夫
瀬戸の花嫁                              小柳ルミ子
愛燦燦＜あいさんさん＞                     美空ひばり
一円玉の旅がらす                          晴山さおり
北の宿から                               都はるみ
孫                                      大泉逸郎
だんな様                                 三船和子
越冬つばめ                               森昌子
津軽海峡・冬景色                         石川さゆり
君は心の妻だから                          鶴岡雅義と東京ロマンチカ
おふくろさん                               森進一
祝い酒                                  坂本冬美
さざんかの宿                              大川栄策
昔の名前で出ています                       小林旭
DAN DAN 心魅かれてく                      FIELD OF VIEW
君こそわが命                              水原弘
くちなしの花                               渡哲也
おもいで酒                                小林幸子
ソーラン節                                北海道民謡
星は何でも知っている                        平尾昌晃
嵐を呼ぶ男                               石原裕次郎
昴－すばる－                             谷村新司
骨まで愛して                             城卓矢
女のみち                                宮史郎とぴんからトリオ
襟裳岬                                 森進一
さそり座の女                             美川憲一
笑って許して                             和田アキ子
長崎から船に乗って                        五木ひろし
星のフラメンコ                             西郷輝彦
今日でお別れ                            菅原洋一
どうにもとまらない                          山本リンダ
いつか逢う日を夢みて                      増位山太志郎・長沢薫
男と女のラブゲーム                         日野美歌／葵司朗
別れても好きな人                         ロス・インディオス＆シルヴィア
もしかして－ＰＡＲＴⅡ                       小林幸子＋美樹克彦
ウナ・セラ・ディ東京                        ザ・ピーナッツ
涙くんさよなら                            坂本九


*/

ROM_START( bkarast )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "sst37vf010.u9", 0x000000, 0x20000, CRC(a7c69fbb) SHA1(28ef698e63e76d9461b71649e4ee9c8f252f82e2) ) // bootloader

	ROM_REGION( 0x840000, "nand", ROMREGION_ERASEFF ) // NAND with main program, graphics, built in songs (and potentially user downloads)
	ROM_LOAD( "tc58v64bft_with_spare.u8", 0x000000, 0x840000, CRC(7770996a) SHA1(cb6df756f88c1f5ff4bd202e7758586c03aff00e) )
ROM_END

ROM_START( bkarasta )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "1ed041.u9", 0x000000, 0x20000, CRC(b6a9c84b) SHA1(062b44dfbbf1dcbc40aa86ab0836fc19c6bddcd5) ) // bootloader

	ROM_REGION( 0x840000, "nand", ROMREGION_ERASEFF ) // NAND with main program, graphics, built in songs (and potentially user downloads)
	ROM_LOAD( "tc58v64bft_with_spare.u8", 0x000000, 0x840000, CRC(50ff920d) SHA1(421850df09d7f407291de31102ebcdfc69da1872) )
ROM_END

ROM_START( bkarastb )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "1f2aea.u9", 0x000000, 0x20000, CRC(4001da51) SHA1(3b3a7cc08d8a30b6b8a422c108d71cb34b0c0b3a) ) // bootloader

	ROM_REGION( 0x840000, "nand", ROMREGION_ERASEFF ) // NAND with main program, graphics, built in songs (and potentially user downloads)
	ROM_LOAD( "tc58v64bft_with_spare.u8", 0x000000, 0x840000, CRC(a7e8ae4f) SHA1(69cc81c574a47c9b1e8c6570b6d3f1144ec99168) )
ROM_END

ROM_START( bkarastc )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "1f260f.u9", 0x000000, 0x20000, CRC(a8ba923f) SHA1(79295b4508b02d9db2dd38b785693146c5be34f2) ) // bootloader

	ROM_REGION( 0x840000, "nand", ROMREGION_ERASEFF ) // NAND with main program, graphics, built in songs (and potentially user downloads)
	ROM_LOAD( "tc58v64bft_with_spare.u8", 0x000000, 0x840000, CRC(34cafa82) SHA1(4c3970a996c06398d11ada88fa412e0e66ebf509) )
ROM_END

ROM_START( bkarastd )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "1f2589.u9", 0x000000, 0x20000, CRC(62cec581) SHA1(c7737be096805565db53ab668089296efb4a8984) ) // bootloader

	ROM_REGION( 0x840000, "nand", ROMREGION_ERASEFF ) // NAND with main program, graphics, built in songs (and potentially user downloads)
	ROM_LOAD( "tc58v64bft_with_spare.u8", 0x000000, 0x840000, CRC(491578a4) SHA1(e20be74daa83fce5dcd5d90822de73f98b7d25d5) )
ROM_END

ROM_START( bkaraste )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "1f4808.u9", 0x000000, 0x20000, CRC(77244cac) SHA1(17b8db2d94a0f83a175d6ad8ce8ffd31ebb8500b) ) // bootloader

	ROM_REGION( 0x840000, "nand", ROMREGION_ERASEFF ) // NAND with main program, graphics, built in songs (and potentially user downloads)
	ROM_LOAD( "tc58v64bft_with_spare.u8", 0x000000, 0x840000, CRC(6b3985f3) SHA1(8dda8ece164d6cbad99f6f6cfde5a714c7271e1f) )
ROM_END

ROM_START( bkarastf )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "200cc8.u9", 0x000000, 0x20000, CRC(2e24ad8a) SHA1(64e4b7756c1a8818f8c445286f63113f6b5c61c6) ) // bootloader

	ROM_REGION( 0x840000, "nand", ROMREGION_ERASEFF ) // NAND with main program, graphics, built in songs (and potentially user downloads)
	ROM_LOAD( "tc58v64bft_with_spare.u8", 0x000000, 0x840000, CRC(4d300ac8) SHA1(afec80f68265e1cd81de6024be4b744acf9a2d3c) )
ROM_END


/*
The 'easykara' set has the following 10 songs built in.

One Step Closer                             S Club Juniors
S Club Party                                S Club 7
Automatic High                              S Club Juniors
Don't Stop Movin'                           S Club 7
Get the Party Started                       Pink
Feel                                        Robbie Williams
Complicated                                 Avril Lavigne
One love                                    Blue
If you're not the one                       Daniel Bedingfield
Sound of the Underground                    Girls Aloud

*/
ROM_START( easykara )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "ics0303-b.bin", 0x000000, 0x400000, CRC(43d86ae8) SHA1(219dcbf72b92d1b7e00f78f237194ab47dc08f1b) )
ROM_END

/*
The 'karams' set has the following 50 songs built in.

...Baby One More Time                       Britney Spears
Alexandrie Alexandra                        Claude François
All By Myself                               Celine Dion
Allumer le feu                              Johnny Hallyday
Alors regarde                               Patrick Bruel
Always On My Mind                           Elvis Presley (R)
Au soleil                                   Jennifer
Auprès de ma blonde                         Enfant
Believe                                     Cher
Bye Bye Bye                                 'N Sync
Can't Get You Out Of My Head                Kylie Minogue
Cette année là                              Claude François
Don't Let Me Get Me                         Pink
Déshabillez-moi                             Juliette Greco
Elle te rend dingue                         Nuttea
Embrasse-moi idiot                          Forban
Fallin'                                     Alicia Keys
Fame                                        Irene Cara
Femmes je vous aime                         Julien Clerc
Frère Jacques                               Enfant
I Love Rock and Roll                        Joan Jett
I Will Survive                              Gloria Gaynor
I'll Be There                               Mariah Carey
Il était un petit navire                    Enfant
It's Raining Men                            Geri Halliwell
Juste quelqu'un de bien                     Enzo Enzo
La Bohème                                   Charles Aznavour
La Cucaracha                                Standard
La Marseillaise                             Popular
La musique                                  Star Academy 1
Lady Marmalade                              Christina Aguilera
Laissons entrer le soleil                   A la recherche de la Nouvelle Star
Le bon roi Dagobert                         Enfant
Le pénitencier                              Johnny Halliday
London Bridge                               Children
Magnolias forever                           Claude François
My Girl                                     Temptations
New York New York                           Frank Sinatra
Noir c'est noir                             Johnny Hallyday
Oops!...I Did It Again                      Britney Spears
Pour le plaisir                             Herbert Léonard
Qui est l'exemple?                          Rohf
Silent Night                                Christmas
That's The Way (I Like It)                  KC And The Sunshine Band
That's The Way It Is                        Celine Dion
The Loco-Motion                             Kylie Minogue
Toute seule                                 Lorie
Vieille canaille                            Gainsbourg
We Wish You A Merry Christmas               Standard
When The Saints Go Marchin' In              Louis Armstrong

*/

ROM_START( karams )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "ics0300-a.u9", 0x000000, 0x800000, CRC(32a7a429) SHA1(ed219bc9201b45f67c5e7dbe3fb3db70823c59f0) )
ROM_END

/*
The 'karamsg' set has the following 42 songs built in.

40 παλικάρια
Alouette                                    Enfant
Always On My Mind                           Elvis Presley (R)
Discogirl                                   Σάκης Ρουβάς
Guantanamera                                Popular
Kumbaya                                     Popular
La Cucaracha                                Standard
Oh, My Darling Clementine                   Children
Silent Night                                Christmas
The Greatest Love Of All                    Whitney Houston
We Wish You A Merry Christmas               Standard
Έλα να με τελειώσεις                        Αντώνης Ρέμος
Ένας αϊτός
Απόψε                                       Αντώνης Ρέμος
Αρχιμηνιά κι αρχιχρονι
Αχ κορίτσι μου                              Γιάννης Πλούταρχος
Αχ κουνελάκι
Βγαίνει η βαρκούλα
Γιάννη μου το μαντήλι σ
Δεν έχω τάσεις αυτοκτον Άννα Βίσση
Εχω τόσα να σου πω                          One
Ιτιά ιτιά
Καλήν εσπέραν άρχοντες
Κόφτην Ελένη την ελιά
Μια βοσκοπούλα αγάπησα
Μια ωραία πεταλούδα
Μου παρήγγειλε τ'αηδόνι
Παιδιά της Σαμαρίνας
Παπάκι πάει στην ποταμι
Παραδοσιακό Ηπείρου
Περνά περνά η μέλισσα
Σήμερα γάμος γίνεται
Σαμιώτισσα
Στου Μανώλη την ταβέρνα
Το φεγγάρι Νατάσσα Θεοδωρίδου
Το φεγγάρι κάνει κύκλο
Τρίγωνα κάλαντα
Χαρωπά τα δυό μου χέρια
Χιόνια στο καμπαναριό
Ω έλατο
Ο Μενούσης
Ομολογώ                                     Βαλάντης

*/
ROM_START( karamsg )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "ics0302-a.u9", 0x000000, 0x800000, CRC(9aaeb556) SHA1(c1f9142c15a15c73f249fad229a65580b7a3ad27) )
ROM_END


/*
The 'dks7000c' set has the following 25 songs built in.

A Spoonful of Sugar                         Mary Poppins
A Whole New World                           Disney
Bare Necessities                            The Jungle Book
Be Our Guest                                Beauty and the Beast
Bibbidi-Bobbidi-Boo                         Disney
Can You Feel The Love Tonight               The Lion King
Chim Chim Cheree                            Disney
Circle of Life                              The Lion King
Colors of the Wind                          Pocahontas
Cruella De Vil                              101 Dalmations
Hakuna Matata                               The Lion King
I Just Can't Wait To Be King                The Lion King
I Wan'na Be Like You (The Monkey Song)      The Jungle Book
I Won't Say (I'm In Love)                   Hercules
I'll Make A Man Out Of You                  Mulan
If I Didn't Have You                        Monsters Inc.
It's A Small World                          New York World's Fair
Kiss The Girl                               The Little Mermaid
Part Of Your World                          The Little Mermaid
Supercalifragilisticexpialidocious          Disney
Yo Ho! (A Pirate's Life For Me)             Pirates of the Caribbean
You Can Fly! You Can Fly! You Can Fly!      Peter Pan
You'll Be In My Heart                       Tarzan
You've Got A Friend In Me                   Toy Story
Zip-A-Dee-Do-Dah                            Song of the South

*/
ROM_START( dks7000c )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "dks7000c.bin", 0x000000, 0x400000, CRC(1c03e59e) SHA1(617d13a9b353fb648f10e136bc07c496a424b953) )
ROM_END

/*
The 'dks7000p' set has the following 20 songs built in.

A Dream Is a Wish Your Heart Makes          Cinderella
A Whole New World                           Disney
Be Our Guest                                Beauty and the Beast
Bibbidi-Bobbidi-Boo                         Disney
Chim Chim Cheree                            Disney
Circle of Life                              The Lion King
Colors of the Wind                          Pocahontas
Forget About Love                           The Return of Jafar
Gaston                                      Beauty and the Beast
I Just Can't Wait To Be King                The Lion King
I Won't Say (I'm In Love)                   Hercules
I'll Make A Man Out Of You                  Mulan
Just Around The River Bend                  Pocahontas
Kiss The Girl                               The Little Mermaid
Once Upon A Dream                           Sleeping Beauty
Out There                                   The Huntchback of Notre Dame
Poor Unfortunate Souls                      The Little Mermaid
Reflection                                  Mulan
Supercalifragilisticexpialidocious          Disney
Under The Sea                               Disney

*/
ROM_START( dks7000p )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "dks7000p.bin", 0x000000, 0x400000, CRC(8ec210fb) SHA1(56337e99f3be57af02e5f7f6e340c3f22deb566d) )
ROM_END


} // anonymous namespace

// This is the original US release, there's no cartridge slot, but it has a NAND Flash inside, and in addition to 50 built-in songs, advertises
// use of a (now defunct) www.onkeysongs.com service for downloading additional songs to the microphone via bundled PC software.
CONS( 2002, karatvst,      0,              0,      ivl_karaoke_base, ivl_karaoke, ivl_karaoke_state, empty_init, "IVL Technologies", "KaraokeTV Star (US, with 50 songs)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2002, karatvsta,     karatvst,       0,      ivl_karaoke_base, ivl_karaoke, ivl_karaoke_state, empty_init, "IVL Technologies", "KaraokeTV Star (US, with 25 songs, 'FREE 35 Hit Songs / $35 value' packaging)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING ) // 25 songs on unit, download code for 10 songs

// The "Memorex Star Singer Karaoke / MKS4001" is also made by IVL and boasts 50 built in songs, the casing is different too.
CONS( 2002, mks4001,       0,              0,      ivl_karaoke_base, ivl_karaoke, ivl_karaoke_state, empty_init, "IVL Technologies (Memorex license)", "Star Singer Karaoke (MKS4001)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING ) // 50 songs built in, appears to have around 54 downloads, including a test download

// Bandai's Japanese release also lacks a cartridge slot, relying on downloads for additional songs. It also comes with a CD containing the PC-side software.  The external microphone design differs slightly.
CONS( 2002, bkarast,       0,              0,      ivl_karaoke_base, ivl_karaoke, ivl_karaoke_state, empty_init, "IVL Technologies (Bandai license)", "Karaoke Station (Japan, set 1)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2002, bkarasta,      bkarast,        0,      ivl_karaoke_base, ivl_karaoke, ivl_karaoke_state, empty_init, "IVL Technologies (Bandai license)", "Karaoke Station (Japan, set 2)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2002, bkarastb,      bkarast,        0,      ivl_karaoke_base, ivl_karaoke, ivl_karaoke_state, empty_init, "IVL Technologies (Bandai license)", "Karaoke Station (Japan, set 3)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2002, bkarastc,      bkarast,        0,      ivl_karaoke_base, ivl_karaoke, ivl_karaoke_state, empty_init, "IVL Technologies (Bandai license)", "Karaoke Station (Japan, set 4)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2002, bkarastd,      bkarast,        0,      ivl_karaoke_base, ivl_karaoke, ivl_karaoke_state, empty_init, "IVL Technologies (Bandai license)", "Karaoke Station (Japan, set 5)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2002, bkaraste,      bkarast,        0,      ivl_karaoke_base, ivl_karaoke, ivl_karaoke_state, empty_init, "IVL Technologies (Bandai license)", "Karaoke Station (Japan, set 6)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
// this set has no downloaded data (or session data) suggesting it hasn't been used, the headers on the 3 songs that are included are different from the above sets however, so these themed units are likely different from factory
CONS( 2002, bkarastf,      bkarast,        0,      ivl_karaoke_base, ivl_karaoke, ivl_karaoke_state, empty_init, "IVL Technologies (Bandai license)", "Karaoke Station 'For Girls' (Japan)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )

// The European releases take cartridges rather than relying on a download service
CONS( 2004, easykara,      0,              0,      easy_karaoke, ivl_karaoke, easy_karaoke_cartslot_state, empty_init, "IVL Technologies (Easy Karaoke license)", "Easy Karaoke Groove Station (UK)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )

CONS( 2003, karams,        0,              0,      easy_karaoke, ivl_karaoke, easy_karaoke_cartslot_state, empty_init, "IVL Technologies (Lexibook license)",             "KaraokeMicro Star (France)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2003, karamsg,       0,              0,      easy_karaoke, ivl_karaoke, easy_karaoke_cartslot_state, empty_init, "IVL Technologies (Lexibook / Imago license)",     "Karaoke Microphone Pro / KaraokeMicro Star (Greece)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING ) // KaraokeMicro Star branding is used on-screen, Karaoke Microphone Pro on the box

CONS( 2003, dks7000c,      0,              0,      easy_karaoke, ivl_karaoke, easy_karaoke_cartslot_state, empty_init, "IVL Technologies (Disney / Memcorp Inc license)", "Disney Classic Handheld Karaoke Player (DKS7000-C)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2003, dks7000p,      0,              0,      easy_karaoke, ivl_karaoke, easy_karaoke_cartslot_state, empty_init, "IVL Technologies (Disney / Memcorp Inc license)", "Disney Princess Handheld Karaoke Player (DKS7000-P)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



ec184x.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************

    drivers/ec184x.c

    Driver file for EC-184x series

    To do:
    - verify ec1840 clocks etc.
    - did 640KB memory board exist or it's 512+128 boards?

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


#include "emu.h"
#include "machine/genpc.h"

#include "bus/isa/xsu_cards.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "cpu/i86/i86.h"
#include "machine/ram.h"

#include "softlist_dev.h"


#define LOG_KEYBOARD  (1U << 1)
#define LOG_DEBUG     (1U << 2)

//#define VERBOSE (LOG_DEBUG)
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"

#define LOGKBD(...) LOGMASKED(LOG_KEYBOARD, __VA_ARGS__)
#define LOGDBG(...) LOGMASKED(LOG_DEBUG, __VA_ARGS__)


namespace {

static constexpr int EC1841_MEMBOARD_SIZE = 512 * 1024;


class ec184x_state : public driver_device
{
public:
	ec184x_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
	{ }

	void ec1840(machine_config &config);
	void ec1841(machine_config &config);

	void init_ec1840();
	void init_ec1841();

private:
	DECLARE_MACHINE_RESET(ec1841);
	uint8_t memboard_r(offs_t offset);
	void memboard_w(offs_t offset, uint8_t data);

	void ec1840_io(address_map &map) ATTR_COLD;
	void ec1840_map(address_map &map) ATTR_COLD;
	void ec1841_io(address_map &map) ATTR_COLD;
	void ec1841_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<ram_device> m_ram;

	struct
	{
		uint8_t enable[4]{};
		int boards = 0;
	} m_memory;
};

/*
 * EC-1841 memory controller.  The machine can hold four memory boards;
 * each board has a control register, its address is set by a DIP switch
 * on the board itself.
 *
 * Only one board should be enabled for read, and one for write.
 * Normally, this is the same board.
 *
 * Each board is divided into 4 banks, internally numbered 0..3.
 * POST tests each board on startup, and an error (indicated by
 * I/O CH CK bus signal) causes it to disable failing bank(s) by writing
 * 'reconfiguration code' (inverted number of failing memory bank) to
 * the register.

 * bit 1-0  'reconfiguration code'
 * bit 2    enable read access
 * bit 3    enable write access
 */

uint8_t ec184x_state::memboard_r(offs_t offset)
{
	uint8_t data;

	data = offset % 4;
	if (data >= m_memory.boards)
		data = 0xff;
	else
		data = m_memory.enable[data];
	LOG("ec1841_memboard R (%d of %d) == %02X\n", offset + 1, m_memory.boards, data);

	return data;
}

void ec184x_state::memboard_w(offs_t offset, uint8_t data)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	uint8_t current = m_memory.enable[offset];

	LOG("ec1841_memboard W (%d of %d) <- %02X (%02X)\n", offset + 1, m_memory.boards, data, current);

	if (offset >= m_memory.boards)
	{
		return;
	}

	if (BIT(current, 2) && !BIT(data, 2))
	{
		// disable read access
		program.unmap_read(0, EC1841_MEMBOARD_SIZE - 1);
		LOG("ec1841_memboard_w unmap_read(%d)\n", offset);
	}

	if (BIT(current, 3) && !BIT(data, 3))
	{
		// disable write access
		program.unmap_write(0, EC1841_MEMBOARD_SIZE - 1);
		LOG("ec1841_memboard_w unmap_write(%d)\n", offset);
	}

	if (!BIT(current, 2) && BIT(data, 2))
	{
		for (int i = 0; i < 4; i++)
			m_memory.enable[i] &= 0xfb;
		// enable read access
		program.install_rom(0, EC1841_MEMBOARD_SIZE - 1, m_ram->pointer() + offset * EC1841_MEMBOARD_SIZE);
		LOG("ec1841_memboard_w map_read(%d)\n", offset);
	}

	if (!BIT(current, 3) && BIT(data, 3))
	{
		for (int i = 0; i < 4; i++)
			m_memory.enable[i] &= 0xf7;
		// enable write access
		program.install_writeonly(0, EC1841_MEMBOARD_SIZE - 1, m_ram->pointer() + offset * EC1841_MEMBOARD_SIZE);
		LOG("ec1841_memboard_w map_write(%d)\n", offset);
	}

	m_memory.enable[offset] = data;
}

void ec184x_state::init_ec1840()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	program.install_ram(0, m_ram->size() - 1, m_ram->pointer());
}

void ec184x_state::init_ec1841()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	m_memory.boards = m_ram->size() / EC1841_MEMBOARD_SIZE;
	if (m_memory.boards > 4) m_memory.boards = 4;

	program.install_ram(0, EC1841_MEMBOARD_SIZE - 1, m_ram->pointer());

	// 640K configuration is special -- 512K board mapped at 0 + 128K board mapped at 512K
	if (m_ram->size() == 640 * 1024)
	{
		program.install_ram(EC1841_MEMBOARD_SIZE, m_ram->size() - 1, m_ram->pointer() + EC1841_MEMBOARD_SIZE);
	}
}

MACHINE_RESET_MEMBER(ec184x_state, ec1841)
{
	memset(m_memory.enable, 0, sizeof(m_memory.enable));
	// mark 1st board enabled
	m_memory.enable[0] = 0xc;
}


void ec184x_state::ec1840_map(address_map &map)
{
	map.unmap_value_high();
	map(0xf0000, 0xfffff).rom().region("bios", 0);
}

void ec184x_state::ec1841_map(address_map &map)
{
	map.unmap_value_high();
	map(0xf0000, 0xfffff).rom().region("bios", 0);
}

void ec184x_state::ec1840_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m("mb", FUNC(ec1840_mb_device::map));
}

void ec184x_state::ec1841_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m("mb", FUNC(ec1841_mb_device::map));
	map(0x02b0, 0x02b3).rw(FUNC(ec184x_state::memboard_r), FUNC(ec184x_state::memboard_w));
}


void ec184x_state::ec1840(machine_config &config)
{
	I8086(config, m_maincpu, 4096000);
	m_maincpu->set_addrmap(AS_PROGRAM, &ec184x_state::ec1840_map);
	m_maincpu->set_addrmap(AS_IO, &ec184x_state::ec1840_io);
	m_maincpu->set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	ec1840_mb_device &mb(EC1840_MOTHERBOARD(config, "mb"));
	mb.set_cputag(m_maincpu);
	mb.int_callback().set_inputline(m_maincpu, 0);
	mb.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	mb.kbdclk_callback().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	mb.kbddata_callback().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));

	// FIXME: determine ISA bus clock
	// 7-slot backplane, at least two slots are always taken by CPU and memory cards
	ISA8_SLOT(config, "isa1", 0, "mb:isa", ec184x_isa8_cards, "ec1840.0002", false);
	ISA8_SLOT(config, "isa2", 0, "mb:isa", ec184x_isa8_cards, "ec1840.0003", false);
	ISA8_SLOT(config, "isa3", 0, "mb:isa", ec184x_isa8_cards, "ec1840.0004", false);
	ISA8_SLOT(config, "isa4", 0, "mb:isa", ec184x_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa5", 0, "mb:isa", ec184x_isa8_cards, nullptr, false);

	SOFTWARE_LIST(config, "flop_list").set_original("ec1841");

	pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_EC_1841));
	kbd.out_clock_cb().set("mb", FUNC(ec1840_mb_device::keyboard_clock_w));
	kbd.out_data_cb().set("mb", FUNC(ec1840_mb_device::keyboard_data_w));

	RAM(config, m_ram).set_default_size("640K").set_extra_options("128K,256K,384K,512K");
}

void ec184x_state::ec1841(machine_config &config)
{
	I8086(config, m_maincpu, XTAL(12'288'000) / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &ec184x_state::ec1841_map);
	m_maincpu->set_addrmap(AS_IO, &ec184x_state::ec1841_io);
	m_maincpu->set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	MCFG_MACHINE_RESET_OVERRIDE(ec184x_state, ec1841)

	ec1841_mb_device &mb(EC1841_MOTHERBOARD(config, "mb"));
	mb.set_cputag(m_maincpu);
	mb.int_callback().set_inputline(m_maincpu, 0);
	mb.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	mb.kbdclk_callback().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	mb.kbddata_callback().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));

	// FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa1", 0, "mb:isa", ec184x_isa8_cards, "ec1841.0002", false); // cga
	ISA8_SLOT(config, "isa2", 0, "mb:isa", ec184x_isa8_cards, "ec1841.0003", false); // fdc (IRQ6) + mouse port (IRQ2..5)
	ISA8_SLOT(config, "isa3", 0, "mb:isa", ec184x_isa8_cards, "ec1840.0004", false); // lpt (IRQ7||5) [+ serial (IRQx)]
	ISA8_SLOT(config, "isa4", 0, "mb:isa", ec184x_isa8_cards, "hdc", false);
	ISA8_SLOT(config, "isa5", 0, "mb:isa", ec184x_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa6", 0, "mb:isa", ec184x_isa8_cards, nullptr, false);

	SOFTWARE_LIST(config, "flop_list").set_original("ec1841");

	pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_EC_1841));
	kbd.out_clock_cb().set("mb", FUNC(ec1841_mb_device::keyboard_clock_w));
	kbd.out_data_cb().set("mb", FUNC(ec1841_mb_device::keyboard_data_w));

	RAM(config, m_ram).set_default_size("640K").set_extra_options("512K,1024K,1576K,2048K");
}

ROM_START( ec1840 )
	ROM_REGION16_LE(0x10000,"bios", 0)
	ROM_DEFAULT_BIOS("v4")
	// supports MDA only
	ROM_SYSTEM_BIOS(0, "v1", "EC-1840.01")
	ROMX_LOAD("000-01.bin", 0xe000, 0x0800, CRC(c3ab1fad) SHA1(8168bdee30698f4f9aa7bbb6dfabe62dd723cec5), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("001-01.bin", 0xf000, 0x0800, CRC(601d1155) SHA1(9684d33b92743749704587a48e679ef7a3b20f9c), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("002-01.bin", 0xe001, 0x0800, CRC(ce4dddb7) SHA1(f9b1da60c848e68ff1c154d695a36a0833de4804), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("003-01.bin", 0xf001, 0x0800, CRC(14b40431) SHA1(ce7fffa41897405ee64fd4e86015e774f8bd108a), ROM_SKIP(1) | ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "v4", "EC-1840.04")
	ROMX_LOAD("000-04-971b.bin", 0xe000, 0x0800, CRC(06aeaee8) SHA1(9f954e4c48156d573a8e0109e7ca652be9e6036a), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("001-04-92b7.bin", 0xf000, 0x0800, CRC(3fae650a) SHA1(c98b777fdeceadd72d6eb9465b3501b9ead55a08), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("002-04-9e17.bin", 0xe001, 0x0800, CRC(d59712df) SHA1(02ea1b3ae9662f5c64c58920a32ca9db0f6fbd12), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("003-04-3ccb.bin", 0xf001, 0x0800, CRC(7fc362c7) SHA1(538e13639ad2b4c30bd72582e323181e63513306), ROM_SKIP(1) | ROM_BIOS(1))

	ROM_REGION(0x2000,"gfx1", ROMREGION_ERASE00)
ROM_END

ROM_START( ec1841 )
	ROM_REGION16_LE(0x10000,"bios", 0)
	ROM_DEFAULT_BIOS("v2")
	ROM_SYSTEM_BIOS(0, "v1", "EC-1841.01")
	ROMX_LOAD("012-01-3107.bin", 0xc000, 0x0800, CRC(77957396) SHA1(785f1dceb6e2b4618f5c5f0af15eb74a8c951448), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("013-01-203f.bin", 0xc001, 0x0800, CRC(768bd3d5) SHA1(2e948f2ad262de306d889b7964c3f1aad45ff5bc), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("014-01-fa40.bin", 0xd000, 0x0800, CRC(47722b58) SHA1(a6339ee8af516f834826b7828a5cf79cb650480c), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("015-01-bf1d.bin", 0xd001, 0x0800, CRC(b585b5ea) SHA1(d0ebed586eb13031477c2e071c50416682f80489), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("016-01-65f7.bin", 0xe000, 0x0800, CRC(28a07db4) SHA1(17fbcd60dacd1d3f8d8355db429f97e4d1d1ac88), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("017-01-5be1.bin", 0xe001, 0x0800, CRC(928bda26) SHA1(ee889184067e2680b29a8ef1c3a76cf5afd4c78d), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("018-01-7090.bin", 0xf000, 0x0800, CRC(75ca7d7e) SHA1(6356426820c5326a7893a437d54b02f250ef8609), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("019-01-0492.bin", 0xf001, 0x0800, CRC(8a9d593e) SHA1(f3936d2cb4e6d130dd732973f126c3aa20612463), ROM_SKIP(1) | ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "v2", "EC-1841.02")
	ROMX_LOAD("012-02-37f6.bin", 0xc000, 0x0800, CRC(8f5c6a20) SHA1(874b62f9cee8d3b974f33732f94eff10fc002c44), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("013-02-2552.bin", 0xc001, 0x0800, CRC(e3c10128) SHA1(d6ed743ebe9c130925c9f17aad1a45db9194c967), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("014-02-0fbe.bin", 0xd000, 0x0800, CRC(f8517e5e) SHA1(8034cd6ff5778365dc9daa494524f1753a74f1ed), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("015-02-d736.bin", 0xd001, 0x0800, CRC(8538c52a) SHA1(ee981ce90870b6546a18f2a2e64d71b0038ce0dd), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("016-02-5b2c.bin", 0xe000, 0x0800, CRC(3d1d1e67) SHA1(c527e29796537787c0f6c329f3c203f6131ca77f), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("017-02-4b9d.bin", 0xe001, 0x0800, CRC(1b985264) SHA1(5ddcb9c13564be208c5068c105444a87159c67ee), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("018-02-7090.bin", 0xf000, 0x0800, CRC(75ca7d7e) SHA1(6356426820c5326a7893a437d54b02f250ef8609), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("019-02-0493.bin", 0xf001, 0x0800, CRC(61aae23d) SHA1(7b3aa24a63ee31b194297eb1e61c3827edfcb95a), ROM_SKIP(1) | ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "v3", "EC-1841.03")
	ROMX_LOAD("012-03-37e7.bin", 0xc000, 0x0800, CRC(49992bd5) SHA1(119121e1b4af1c44b9b8c2edabe7dc1d3019c4a6), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD("013-03-2554.bin", 0xc001, 0x0800, CRC(834bd7d7) SHA1(e37514fc4cb8a5cbe68e7564e0e07d5116c4021a), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD("014-03-0fbe.bin", 0xd000, 0x0800, CRC(f8517e5e) SHA1(8034cd6ff5778365dc9daa494524f1753a74f1ed), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD("015-03-d736.bin", 0xd001, 0x0800, CRC(8538c52a) SHA1(ee981ce90870b6546a18f2a2e64d71b0038ce0dd), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD("016-03-5b2c.bin", 0xe000, 0x0800, CRC(3d1d1e67) SHA1(c527e29796537787c0f6c329f3c203f6131ca77f), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD("017-03-4b9d.bin", 0xe001, 0x0800, CRC(1b985264) SHA1(5ddcb9c13564be208c5068c105444a87159c67ee), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD("018-03-7090.bin", 0xf000, 0x0800, CRC(75ca7d7e) SHA1(6356426820c5326a7893a437d54b02f250ef8609), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD("019-03-0493.bin", 0xf001, 0x0800, CRC(61aae23d) SHA1(7b3aa24a63ee31b194297eb1e61c3827edfcb95a), ROM_SKIP(1) | ROM_BIOS(2))
ROM_END

ROM_START( ec1845 )
	ROM_REGION16_LE(0x10000,"bios", 0)
	ROMX_LOAD("184500.bin", 0xc000, 0x0800, CRC(7c472ef7) SHA1(3af53f27b49bbc731bf51f9300fbada23a1bfcfc), ROM_SKIP(1))
	ROMX_LOAD("184501.bin", 0xc001, 0x0800, CRC(db240dc6) SHA1(d7bb022213d09bbf2a8107fe4f1cd27b23939e18), ROM_SKIP(1))
	ROMX_LOAD("184502.bin", 0xd000, 0x0800, CRC(149e7e29) SHA1(7f2a297588fef1bc750c57e6ae0d5acf3d27c486), ROM_SKIP(1))
	ROMX_LOAD("184503.bin", 0xd001, 0x0800, CRC(e28cbd74) SHA1(cf1fba4e67c8e1dd8cdda547118e84b704029b03), ROM_SKIP(1))
	ROMX_LOAD("184504.bin", 0xe000, 0x0800, CRC(55fa7a1d) SHA1(58f7abab08b9d2f0a1c1636e11bb72af2694c95f), ROM_SKIP(1))
	ROMX_LOAD("184505.bin", 0xe001, 0x0800, CRC(c807e3f5) SHA1(08117e449f0d04f96041cff8d34893f500f3760d), ROM_SKIP(1))
	ROMX_LOAD("184506.bin", 0xf000, 0x0800, CRC(24f5c27c) SHA1(7822dd7f715ef00ccf6d8408be8bbfe01c2eba20), ROM_SKIP(1))
	ROMX_LOAD("184507.bin", 0xf001, 0x0800, CRC(75122203) SHA1(7b0fbdf1315230633e39574ac7360163bc7361e1), ROM_SKIP(1))
ROM_END

} // anonymous namespace


//    YEAR  NAME    PARENT   COMPAT  MACHINE  INPUT  STATE         INIT         COMPANY      FULLNAME   FLAGS
COMP( 1986, ec1840, ibm5150, 0,      ec1840,  0,     ec184x_state, init_ec1840, "<unknown>", "EC-1840", 0 )
COMP( 1987, ec1841, ibm5150, 0,      ec1841,  0,     ec184x_state, init_ec1841, "<unknown>", "EC-1841", 0 )
COMP( 1989, ec1845, ibm5150, 0,      ec1841,  0,     ec184x_state, init_ec1841, "<unknown>", "EC-1845", MACHINE_NOT_WORKING )



ec65.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/********************************************************************************

EC-65 (also known as Octopus)

2009-07-16 Initial driver.

No sound. Storage is floppy disk. No software has been found.
Modules are constructed on separate eurocards and plug into a common backplane.

EC65 -  Disk controller module uses 6821 and 6850. Needs to be emulated.
        When done, .b to boot disk

EC65K - To be developed from scratch. Similar design to EC65, but 6522
        replaced with 6821. Contains extra RTC. Particulars of video and
        universal FDC not known.
      - Extra 256KB card
      - Extra Z80 card with ROM, 2x PIA, 64 or 256KB RAM, for CP/M use.
      - Currently runs into the weeds soon after start, at F070


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

#include "emu.h"
#include "cpu/m6502/m6502.h"
#include "cpu/g65816/g65816.h"
#include "video/mc6845.h"
#include "machine/6821pia.h"
#include "machine/6522via.h"
#include "machine/mos6551.h"
#include "machine/6850acia.h"
#include "machine/keyboard.h"
#include "machine/mc146818.h"
#include "emupal.h"
#include "screen.h"

namespace {

class ec65_common : public driver_device
{
public:
	ec65_common(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_vram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
		, m_maincpu(*this, "maincpu")
		, m_palette(*this, "palette")
	{ }

	MC6845_UPDATE_ROW(crtc_update_row);

protected:
	required_shared_ptr<u8> m_vram;
	required_region_ptr<u8> m_p_chargen;
	required_device<cpu_device> m_maincpu;
	required_device<palette_device> m_palette;

};

class ec65_state : public ec65_common
{
public:
	ec65_state(const machine_config &mconfig, device_type type, const char *tag)
		: ec65_common(mconfig, type, tag)
		, m_via0(*this, "via0")
		, m_via1(*this, "via1")
	{ }

	void ec65(machine_config &config);

private:
	void machine_reset() override ATTR_COLD;
	void ec65_mem(address_map &map) ATTR_COLD;
	void kbd_put(u8 data);
	required_device<via6522_device> m_via0;
	required_device<via6522_device> m_via1;
};

class ec65k_state : public ec65_common
{
public:
	ec65k_state(const machine_config &mconfig, device_type type, const char *tag)
		: ec65_common(mconfig, type, tag)
		, m_rtc(*this, "rtc")
		, m_pia0(*this, "pia0")
		, m_pia1(*this, "pia1")
	{ }

	void ec65k(machine_config &config);

private:
	void ec65k_mem(address_map &map) ATTR_COLD;
	void kbd_put(u8 data);
	required_device<mc146818_device> m_rtc;
	required_device<pia6821_device> m_pia0;
	required_device<pia6821_device> m_pia1;
};

void ec65_state::ec65_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xdfff).ram();
	map(0xe000, 0xe003).rw("pia", FUNC(pia6821_device::read), FUNC(pia6821_device::write)); // FDC card
	map(0xe010, 0xe011).rw("fdc", FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // FDC card
	map(0xe100, 0xe10f).m(m_via0, FUNC(via6522_device::map));
	map(0xe110, 0xe11f).m(m_via1, FUNC(via6522_device::map));
	map(0xe130, 0xe133).rw("uart", FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0xe140, 0xe140).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0xe141, 0xe141).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0xe400, 0xe7ff).ram(); // 1KB on-board RAM
	map(0xe800, 0xefff).ram().share("videoram");
	map(0xf000, 0xffff).rom().region("maincpu",0);
}

void ec65k_state::ec65k_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x00dfff).ram();   // D000-DFFF = System RAM
	map(0x00e000, 0x00e003).rw("pia", FUNC(pia6821_device::read), FUNC(pia6821_device::write)); // old FDC card
	map(0x00e010, 0x00e011).rw("fdc", FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // old FDC card
	//map(0x00e150,             colorator CRTC (no info)
	//map(0x00e280,             basicode interface (no info)
	//map(0x00e300,             Z80 board
	map(0x00e400, 0x00e403).rw(m_pia0, FUNC(pia6821_device::read), FUNC(pia6821_device::write)); //  PIA0 porta=keyboard; portb=parallel port
	map(0x00e410, 0x00e413).rw("uart", FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x00e420, 0x00e423).rw(m_pia1, FUNC(pia6821_device::read), FUNC(pia6821_device::write)); //  PIA1 porta=centronics control; portb=centronics data
	map(0x00e430, 0x00e430).w(m_rtc, FUNC(mc146818_device::address_w));  //  RTC 146818 - has battery backup
	map(0x00e431, 0x00e431).rw(m_rtc, FUNC(mc146818_device::data_r), FUNC(mc146818_device::data_w));
	//map(0x00e500, 0x00e5ff)   universal disk controller (no info)
	map(0x00e800, 0x00efff).ram().share("videoram");
	map(0x00f000, 0x00ffff).rom().region("maincpu",0);
	map(0x010000, 0x0fffff).ram();
}

/* Input ports */
static INPUT_PORTS_START( ec65 )
INPUT_PORTS_END

void ec65_state::kbd_put(u8 data)
{
	if (data)
	{
		m_via0->write_pa(data);
		m_via0->write_ca1(1);
		m_via0->write_ca1(0);
	}
}

void ec65k_state::kbd_put(u8 data)
{
	if (data)
	{
		m_pia0->porta_w(data);
		m_pia0->ca1_w(1);
		m_pia0->ca1_w(0);
	}
}

void ec65_state::machine_reset()
{
	m_via1->write_pb(0xff);
}

MC6845_UPDATE_ROW( ec65_common::crtc_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u32 *p = &bitmap.pix(y);

	for (u16 x = 0; x < x_count; x++)
	{
		u8 const inv = (x == cursor_x) ? 0xff : 0;
		u16 const mem = (ma + x) & 0x7ff;
		u8 const chr = m_vram[mem];

		/* get pattern of pixels for that character scanline */
		u8 const gfx = m_p_chargen[(chr<<4) | (ra & 0x0f)] ^ inv;

		/* Display a scanline of a character */
		*p++ = palette[BIT(gfx, 7)];
		*p++ = palette[BIT(gfx, 6)];
		*p++ = palette[BIT(gfx, 5)];
		*p++ = palette[BIT(gfx, 4)];
		*p++ = palette[BIT(gfx, 3)];
		*p++ = palette[BIT(gfx, 2)];
		*p++ = palette[BIT(gfx, 1)];
		*p++ = palette[BIT(gfx, 0)];
	}
}

/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_ec65 )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END

void ec65_state::ec65(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, XTAL(4'000'000) / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &ec65_state::ec65_mem);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 200);
	screen.set_visarea_full();
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	GFXDECODE(config, "gfxdecode", m_palette, gfx_ec65);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	mc6845_device &crtc(MC6845(config, "crtc", XTAL(16'000'000) / 8));
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(8); /*?*/
	crtc.set_update_row_callback(FUNC(ec65_state::crtc_update_row));

	/* devices */
	ACIA6850(config, "fdc", 0); // used as a FDC on separate card
	PIA6821(config, "pia"); // assists 6850

	MOS6522(config, m_via0, XTAL(4'000'000) / 4);

	MOS6522(config, m_via1, XTAL(4'000'000) / 4);

	mos6551_device &uart(MOS6551(config, "uart", 0));
	uart.set_xtal(XTAL(1'843'200));

	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(ec65_state::kbd_put));
}

void ec65k_state::ec65k(machine_config &config)
{
	/* basic machine hardware */
	g65816_device &maincpu(G65816(config, "maincpu", XTAL(4'000'000))); // can use 4,2 or 1 MHz
	maincpu.set_addrmap(AS_PROGRAM, &ec65k_state::ec65k_mem);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 200);
	screen.set_visarea_full();
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	GFXDECODE(config, "gfxdecode", "palette", gfx_ec65);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	mc6845_device &crtc(MC6845(config, "crtc", XTAL(16'000'000) / 8));
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(8); /*?*/
	crtc.set_update_row_callback(FUNC(ec65k_state::crtc_update_row));

	/* devices */
	ACIA6850(config, "fdc", 0); // used as a FDC on separate card
	PIA6821(config, "pia"); // assists 6850

	MC146818(config, m_rtc, 32.768_kHz_XTAL);
	//m_rtc->irq().set(FUNC(micronic_state::mc146818_irq));   Connects to common irq line used by below PIAs and UART

	PIA6821(config, m_pia0);
	PIA6821(config, m_pia1);

	mos6551_device &uart(MOS6551(config, "uart", 0));
	uart.set_xtal(XTAL(1'843'200));

	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(ec65k_state::kbd_put));
}

/* ROM definition */
ROM_START( ec65 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "ec65.ic6", 0x0000, 0x1000, CRC(acd928ed) SHA1(e02a688a057ff77294717cf7b887425fed0b1153))

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "chargen.ic19", 0x0000, 0x1000, CRC(9b56a28d) SHA1(41c04fd9fb542c50287bc0e366358a61fc4b0cd4)) // Located on VDU card
ROM_END

ROM_START( ec65k )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "ec65k.ic19",  0x0000, 0x1000, CRC(5e5a890a) SHA1(daa006f2179fd156833e11c73b37881cafe5dede))

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "chargen.ic19", 0x0000, 0x1000, CRC(9b56a28d) SHA1(41c04fd9fb542c50287bc0e366358a61fc4b0cd4)) // Located on VDU card
ROM_END

} // Anonymous namespace

/*    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY    FULLNAME  FLAGS */
COMP( 1985, ec65,  0,      0,      ec65,    ec65,  ec65_state,  empty_init, "Elektor", "EC-65",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1986, ec65k, ec65,   0,      ec65k,   ec65,  ec65k_state, empty_init, "Elektor", "EC-65K", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



ec7915.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for Mera-Elzab EC-7915/EC-7950 terminal.

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

#include "emu.h"
#include "cpu/i8085/i8085.h"
//#include "machine/i8214.h"
#include "machine/i8251.h"
#include "machine/pit8253.h"
#include "machine/i8255.h"
//#include "screen.h"


namespace {

class ec7915_state : public driver_device
{
public:
	ec7915_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_chargen(*this, "chargen")
	{
	}

	void ec7915(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_chargen;
};


void ec7915_state::mem_map(address_map &map)
{
	map(0x0000, 0x27ff).rom().region("maincpu", 0);
	map(0x4800, 0x5fff).ram();
	map(0x6000, 0x67ff).ram();
}

void ec7915_state::io_map(address_map &map)
{
	map(0x00, 0x03).rw("ppi1", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x04, 0x07).rw("ppi2", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x08, 0x09).rw("usart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x0c, 0x0f).rw("ppi3", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x10, 0x13).w("pit", FUNC(pit8253_device::write));
	//map(0x14, 0x14).w("picu", FUNC(i8214_device::write));
}


static INPUT_PORTS_START(ec7915)
INPUT_PORTS_END


void ec7915_state::ec7915(machine_config &config)
{
	I8080A(config, m_maincpu, 2000000); // КР580ВМ80А (not Z80) + SAB8224P clock generator
	m_maincpu->set_addrmap(AS_PROGRAM, &ec7915_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &ec7915_state::io_map);

	//I8214(config, "picu", 2000000); // CEMI UCY74S414

	PIT8253(config, "pit", 0); // КР580ВИ53

	I8251(config, "usart", 2000000); // КР580ВВ51А
	// 8251 usage appears to prefer synchronous communications

	// 2x NEC D8255AC-2 on main board + КР580ВВ55А on display board
	I8255A(config, "ppi1");
	I8255A(config, "ppi2");
	I8255A(config, "ppi3");
}


ROM_START( ec7915 ) // 6k ram // amber
	ROM_REGION( 0x2800, "maincpu", 0 )
	ROM_LOAD( "50mp_0c10_30_lupper.bin", 0x0000, 0x0800, CRC(e019690f) SHA1(b0ce837a940ad82d2f39bd9d02e3c441cb9e83ed) )
	ROM_LOAD( "50mp_0810_40.bin",        0x0800, 0x0800, CRC(ed7f12d6) SHA1(b6f1da6a74f77cf1d392eee79f5ea168f3626ee5) )
	ROM_LOAD( "50mp_1010_49.bin",        0x1000, 0x0800, CRC(bfddf0e6) SHA1(dff4be8c0403519530e6c9106ab279a3037e074a) )
	ROM_LOAD( "50mp_1810_60.bin",        0x1800, 0x0800, CRC(759f2dc7) SHA1(515778ea213b9204f75f920ef1fbff6c14f9cf3c) )
	ROM_LOAD( "50mp_2c10_30_lower.bin",  0x2000, 0x0800, CRC(1ff59657) SHA1(777ef82e20a0100c0069ee5e7fbac5b3b86e3529) BAD_DUMP ) // fails checksum test

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "char.bin",                0x0000, 0x0800, CRC(e75a6bc4) SHA1(04b56d1f5ab7f2145699555df5ac44d078804821) )
ROM_END

} // anonymous namespace


COMP(198?, ec7915, 0, 0, ec7915, ec7915, ec7915_state, empty_init, "Mera-Elzab", "EC-7915 (EC-7950)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



edames.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Saitek Electronic Dames

Electronic draughts (checkers) game, international rules apply (10*10 board,
flying kings, backward captures allowed).

NOTE: Before exiting MAME, press the STOP button to turn the power off. Otherwise,
NVRAM won't save properly.

MAME sensorboard handling is similar to Fidelity Dame Sensory Challenger, see
fidelity/dames.cpp. It will give an error beep if the user removes a captured
piece from the board, but it doesn't matter.

Two versions were sold, each should have the same MCU and ROM: a tabletop model
(Electronic Dames, 8MHz or 12MHz, model 660), and a portable model (Compact Dames
Computer, 8MHz, model 661). As with SciSys/Saitek chess computers, they were also
licensed to Tandy. The program engine is DIOS by Eric van Riet Paap.

According to the second hand market, the tabletop French version is much more
common than the English one. The manual and a LED label incorrectly call crowned
men queens instead of kings, perhaps due to a translation from French (dame).

Hardware notes (Compact Dames Computer):
- PCB label: DH1-PE-009 REV.1
- Hitachi HD6301Y0P MCU, 8MHz or 12MHz (LC osc, no XTAL)
- 20+8 LEDs, 5*10 buttons sensor board, piezo

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

#include "emu.h"

#include "cpu/m6800/m6801.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "saitek_edames.lh"


namespace {

class edames_state : public driver_device
{
public:
	edames_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void edames(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(go_button);
	DECLARE_INPUT_CHANGED_MEMBER(change_cpu_freq);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<hd6301y0_cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<4> m_inputs;

	u16 m_inp_mux = 0;
	u8 m_led_select = 0;
	u16 m_led_data[2] = { };
	bool m_enable_reset = false;

	void init_board(u8 data);

	// I/O handlers
	void update_display();

	void p1_w(u8 data);
	void p3_w(u8 data);
	void p4_w(u8 data);
	u8 p5_r();
	void p6_w(u8 data);
	void p7_w(u8 data);
};



/*******************************************************************************
    Initialization
*******************************************************************************/

void edames_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_led_select));
	save_item(NAME(m_led_data));
	save_item(NAME(m_enable_reset));
}

void edames_state::init_board(u8 data)
{
	for (int i = 0; i < 20; i++)
	{
		m_board->write_piece(i % 5, i / 5, 1); // white
		m_board->write_piece(i % 5, i / 5 + 6, 3); // black
	}
}

INPUT_CHANGED_MEMBER(edames_state::change_cpu_freq)
{
	// 6MHz and 10MHz versions don't exist, but the software supports it
	static const u32 freq[4] = { 6'000'000, 8'000'000, 10'000'000, 12'000'000 };
	m_maincpu->set_unscaled_clock(freq[~newval & 3]);
}



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

INPUT_CHANGED_MEMBER(edames_state::go_button)
{
	if (newval && m_enable_reset)
		m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
}

void edames_state::update_display()
{
	m_display->matrix_partial(0, 2, m_led_select, m_led_data[0]);
	m_display->write_row(2, m_led_data[1]);
}

void edames_state::p1_w(u8 data)
{
	// P10-P17: board leds data part
	m_led_data[0] = (m_led_data[0] & 0x300) | (data ^ 0xff);
	update_display();
}

void edames_state::p3_w(u8 data)
{
	// P30-P36: status leds part
	m_led_data[1] = (m_led_data[1] & 0x80) | (~data & 0x7f);
	update_display();

	// P37: enable reset button
	m_enable_reset = bool(BIT(data, 7));
}

void edames_state::p4_w(u8 data)
{
	// P40-P45: input mux part
	m_inp_mux = (m_inp_mux & 0x7f) | (~data << 7);

	// P47: status leds part
	m_led_data[1] = (m_led_data[1] & 0x7f) | (~data & 0x80);
	update_display();
}

u8 edames_state::p5_r()
{
	// P50-P54: multiplexed inputs
	u8 data = 0;

	// read checkerboard
	for (int i = 0; i < 10; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_rank(i, true);

	// read buttons
	for (int i = 0; i < 3; i++)
		if (BIT(m_inp_mux, i + 10))
			data |= m_inputs[i]->read();

	// P55-P57: freq sel and one more button
	return (~data & 0x1f) | (m_inputs[3]->read() << 5);
}

void edames_state::p6_w(u8 data)
{
	// P60-P67: input mux part
	m_inp_mux = (m_inp_mux & ~0x7f) | (~data & 0x7f);
}

void edames_state::p7_w(u8 data)
{
	// P70,P71: board leds select
	m_led_select = ~data & 3;

	// P72-P73: board leds data part
	m_led_data[0] = (m_led_data[0] & 0xff) | (~data << 6 & 0x300);
	update_display();

	// P74: speaker out
	m_dac->write(BIT(~data, 4));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( edames ) // see comments for French version labels
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Set Up")     // Position
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Play")       // Joue
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_N) PORT_NAME("New Game") // Nouvelle Partie
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_L) PORT_NAME("Level")    // Niveau
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Take Back")  // Recule 1 Coup

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Move")       // Coup
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Non Auto")   // Non Auto
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("Sound")      // Son
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Black Man")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("White Man")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Evaluation") // Calcul
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Depth")      // Note
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("White King")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Black King")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Stop")       // Stop

	PORT_START("IN.3")
	PORT_CONFNAME( 0x03, 0x02, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(edames_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x03, "6MHz (unofficial)" )
	PORT_CONFSETTING(    0x02, "8MHz (original version)" )
	PORT_CONFSETTING(    0x01, "10MHz (unofficial)" )
	PORT_CONFSETTING(    0x00, "12MHz (newer version)" )
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Swap Side")   // Tourne Damier

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(edames_state::go_button), 0) PORT_NAME("Go") // Go
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void edames_state::edames(machine_config &config)
{
	// basic machine hardware
	HD6301Y0(config, m_maincpu, 8'000'000); // approximation, no XTAL
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(hd6301y0_cpu_device::nvram_set_battery));
	m_maincpu->standby_cb().append([this](int state) { if (state) m_display->clear(); });
	m_maincpu->out_p1_cb().set(FUNC(edames_state::p1_w));
	m_maincpu->out_p3_cb().set(FUNC(edames_state::p3_w));
	m_maincpu->out_p4_cb().set(FUNC(edames_state::p4_w));
	m_maincpu->in_p5_cb().set(FUNC(edames_state::p5_r));
	m_maincpu->out_p6_cb().set(FUNC(edames_state::p6_w));
	m_maincpu->out_p7_cb().set(FUNC(edames_state::p7_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(FUNC(edames_state::init_board));
	m_board->set_size(5, 10); // 2 columns per x (eg. square 1 & 6 are same x)
	m_board->set_spawnpoints(4);
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(2+1, 10);
	config.set_default_layout(layout_saitek_edames);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( edames )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("1988_d1_saitek_6301y0h04p.u1", 0x0000, 0x4000, CRC(a23e2114) SHA1(85b7685ec612221d2ffdd2df4550ffad22acef81) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT    COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1988, edames, 0,        0,      edames,  edames, edames_state, empty_init, "Saitek", "Electronic Dames", MACHINE_SUPPORTS_SAVE )



einstein.cpp
<---------------------------------------------------------------------->
// license: GPL-2.0+
// copyright-holders: Kevin Thacker, Dirk Best, Phill Harvey-Smith, Nigel Barnes
// thanks-to: Chris Coxall, Andrew Dunipace
/******************************************************************************

    Tatung Einstein

    TODO: Einstein 256
    - interrupt priorities
    - VAMP interface (probably never used)

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

#include "emu.h"

#include "bus/centronics/ctronics.h"
#include "bus/einstein/pipe/pipe.h"
#include "bus/einstein/userport/userport.h"
#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "bus/rs232/rs232.h"
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "imagedev/snapquik.h"
#include "cpu/z80/z80.h"
#include "machine/adc0844.h"
#include "machine/i8251.h"
#include "machine/ram.h"
#include "machine/rescap.h"
#include "machine/timer.h"
#include "machine/wd_fdc.h"
#include "machine/z80ctc.h"
#include "machine/z80daisy.h"
#include "machine/z80daisy_generic.h"
#include "machine/z80pio.h"
#include "sound/ay8910.h"
#include "video/tms9928a.h"
#include "video/v9938.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

/***************************************************************************
    CONSTANTS
***************************************************************************/

#define VERBOSE_KEYBOARD    0
#define VERBOSE_DISK        0

#define XTAL_X001  10.738635_MHz_XTAL
#define XTAL_X002  8_MHz_XTAL

#define IC_I001  "i001"  /* Z8400A */
#define IC_I030  "i030"  /* AY-3-8910 */
#define IC_I038  "i038"  /* TMM9129 */
#define IC_I042  "i042"  /* WD1770-PH */
#define IC_I050  "i050"  /* ADC0844CCN */
#define IC_I058  "i058"  /* Z8430A */
#define IC_I060  "i060"  /* uPD8251A */
#define IC_I063  "i063"  /* Z8420A */


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

class einstein_state : public driver_device
{
public:
	einstein_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, IC_I001),
		m_keyboard_daisy(*this, "keyboard_daisy"),
		m_adc_daisy(*this, "adc_daisy"),
		m_fire_daisy(*this, "fire_daisy"),
		m_vdp_daisy(*this, "vdp_daisy"),
		m_pipe(*this, "pipe"),
		m_fdc(*this, IC_I042),
		m_ram(*this, RAM_TAG),
		m_psg(*this, IC_I030),
		m_centronics(*this, "centronics"),
		m_strobe_timer(*this, "strobe"),
		m_rom2(*this, "rom2"),
		m_bios(*this, "bios"),
		m_bank1(*this, "bank1"),
		m_bank2(*this, "bank2"),
		m_bank3(*this, "bank3"),
		m_floppy(*this, IC_I042 ":%u", 0),
		m_cassette(*this, "cassette"),
		m_line(*this, "LINE%u", 0),
		m_extra(*this, "EXTRA"),
		m_buttons(*this, "BUTTONS"),
		m_porta_joy(*this, "PORTA_JOY"),
		m_portb_joy(*this, "PORTB_JOY"),
		m_dips(*this, "DIPS"),
		m_alpha_lock_led(*this, "alpha_lock_led"),
		m_rom_enabled(0),
		m_keyboard_line(0), m_keyboard_data(0xff),
		m_centronics_ack(0), m_centronics_busy(0), m_centronics_perror(0), m_centronics_fault(0), m_strobe(-1),
		m_int(0)
	{}

	void einstein(machine_config &config);
	void einst256(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(joystick_button);

	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	TIMER_DEVICE_CALLBACK_MEMBER(keyboard_timer_callback);
	void keyboard_line_write(uint8_t data);
	uint8_t keyboard_data_read();
	uint8_t reset_r();
	void reset_w(uint8_t data);
	uint8_t rom_r();
	void rom_w(uint8_t data);
	template <int Src> void int_w(int state);
	uint8_t kybint_msk_r();
	void kybint_msk_w(uint8_t data);
	void adcint_msk_w(uint8_t data);
	void fireint_msk_w(uint8_t data);
	void evdpint_msk_w(uint8_t data);
	void drsel_w(uint8_t data);
	void write_centronics_ack(int state);
	void write_centronics_busy(int state);
	void write_centronics_perror(int state);
	void write_centronics_fault(int state);
	void ardy_w(int state);
	TIMER_DEVICE_CALLBACK_MEMBER(strobe_callback);

	uint8_t system_r();
	uint8_t porta_r();
	void porta_w(uint8_t data);
	void porta_int_w(uint8_t data);
	uint8_t portb_r();
	void portb_w(uint8_t data);
	uint8_t pseudo_adc_r();
	void pseudo_adc_w(uint8_t data);
	uint8_t alpha_lock_r();
	void alpha_lock_w(uint8_t data);

	void einstein_io(address_map &map) ATTR_COLD;
	void einstein_mem(address_map &map) ATTR_COLD;
	void einst256_io(address_map &map) ATTR_COLD;

	void einstein_scan_keyboard();

	required_device<z80_device> m_maincpu;
	required_device<z80daisy_generic_device> m_keyboard_daisy;
	required_device<z80daisy_generic_device> m_adc_daisy;
	required_device<z80daisy_generic_device> m_fire_daisy;
	optional_device<z80daisy_generic_device> m_vdp_daisy;
	optional_device<tatung_pipe_device> m_pipe;
	required_device<wd1770_device> m_fdc;
	required_device<ram_device> m_ram;
	required_device<ay8910_device> m_psg;
	required_device<centronics_device> m_centronics;
	required_device<timer_device> m_strobe_timer;
	optional_device<generic_slot_device> m_rom2;
	required_memory_region m_bios;
	required_memory_bank m_bank1;
	required_memory_bank m_bank2;
	required_memory_bank m_bank3;
	optional_device_array<floppy_connector, 4> m_floppy;
	optional_device<cassette_image_device> m_cassette;
	required_ioport_array<8> m_line;
	required_ioport m_extra;
	optional_ioport m_buttons;
	optional_ioport m_porta_joy;
	optional_ioport m_portb_joy;
	optional_ioport m_dips;
	output_finder<> m_alpha_lock_led;

	int m_rom_enabled;

	uint8_t m_keyboard_line;
	uint8_t m_keyboard_data;
	uint8_t m_porta_int;
	uint8_t m_pseudo_adc;

	int m_centronics_ack;
	int m_centronics_busy;
	int m_centronics_perror;
	int m_centronics_fault;
	int m_strobe;
	int m_alpha_lock;

	int m_int;
};


/***************************************************************************
    KEYBOARD
***************************************************************************/

INPUT_CHANGED_MEMBER( einstein_state::joystick_button )
{
	int button_down;
	if (m_buttons)
		button_down = (m_buttons->read() & 0x03) != 0x03;
	else
		button_down = (m_porta_joy->read() & m_portb_joy->read() & 0x10) != 0x10;

	m_fire_daisy->int_w(button_down ? ASSERT_LINE : CLEAR_LINE);
}

/* refresh keyboard data. It is refreshed when the keyboard line is written */
void einstein_state::einstein_scan_keyboard()
{
	uint8_t data = 0xff;

	if (!BIT(m_keyboard_line, 0)) data &= m_line[0]->read();
	if (!BIT(m_keyboard_line, 1)) data &= m_line[1]->read();
	if (!BIT(m_keyboard_line, 2)) data &= m_line[2]->read();
	if (!BIT(m_keyboard_line, 3)) data &= m_line[3]->read();
	if (!BIT(m_keyboard_line, 4)) data &= m_line[4]->read();
	if (!BIT(m_keyboard_line, 5)) data &= m_line[5]->read();
	if (!BIT(m_keyboard_line, 6)) data &= m_line[6]->read();
	if (!BIT(m_keyboard_line, 7)) data &= m_line[7]->read();

	m_keyboard_data = data;
}

TIMER_DEVICE_CALLBACK_MEMBER( einstein_state::keyboard_timer_callback )
{
	/* re-scan keyboard */
	einstein_scan_keyboard();

	if (m_keyboard_data != 0xff)
		m_keyboard_daisy->int_w(ASSERT_LINE);
}

void einstein_state::keyboard_line_write(uint8_t data)
{
	if (VERBOSE_KEYBOARD)
		logerror("einstein_keyboard_line_write: %02x\n", data);

	m_keyboard_line = data;

	/* re-scan the keyboard */
	einstein_scan_keyboard();
}

uint8_t einstein_state::keyboard_data_read()
{
	/* re-scan the keyboard */
	einstein_scan_keyboard();

	if (VERBOSE_KEYBOARD)
		logerror("einstein_keyboard_data_read: %02x\n", m_keyboard_data);

	return m_keyboard_data;
}


/***************************************************************************
    FLOPPY DRIVES
***************************************************************************/

void einstein_state::drsel_w(uint8_t data)
{
	if (VERBOSE_DISK)
		logerror("%s: drsel_w %02x\n", machine().describe_context(), data);

	floppy_image_device *floppy = nullptr;

	if (BIT(data, 0)) floppy = m_floppy[0] ? m_floppy[0]->get_device() : nullptr;
	if (BIT(data, 1)) floppy = m_floppy[1] ? m_floppy[1]->get_device() : nullptr;
	if (BIT(data, 2)) floppy = m_floppy[2] ? m_floppy[2]->get_device() : nullptr;
	if (BIT(data, 3)) floppy = m_floppy[3] ? m_floppy[3]->get_device() : nullptr;

	if (floppy)
		floppy->ss_w(BIT(data, 4));

	m_fdc->set_floppy(floppy);
}


/***************************************************************************
    CENTRONICS
***************************************************************************/

void einstein_state::write_centronics_ack(int state)
{
	m_centronics_ack = state;
}

void einstein_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

void einstein_state::write_centronics_perror(int state)
{
	m_centronics_perror = state;
}

void einstein_state::write_centronics_fault(int state)
{
	m_centronics_fault = state;
}

void einstein_state::ardy_w(int state)
{
	if (m_strobe == 0 && state == 1)
	{
		m_centronics->write_strobe(1);
		m_strobe_timer->adjust(attotime::from_double(TIME_OF_74LS123(RES_K(10), CAP_N(1))));
	}

	m_strobe = state;
}

TIMER_DEVICE_CALLBACK_MEMBER( einstein_state::strobe_callback )
{
	m_centronics->write_strobe(0);
}


/***************************************************************************
    INTERRUPTS
***************************************************************************/

static const z80_daisy_config einstein_daisy_chain[] =
{
	{ "keyboard_daisy" },
	{ IC_I058 },
	{ "adc_daisy" },
	{ IC_I063 },
	{ "fire_daisy" },
	{ nullptr }
};

static const z80_daisy_config einst256_daisy_chain[] =
{
	{ "keyboard_daisy" },
	{ IC_I058 },
	{ "adc_daisy" },
	{ "vdp_daisy" },
	{ "fire_daisy" },
	{ nullptr }
};

template <int Src>
void einstein_state::int_w(int state)
{
	int old = m_int;

	if (state)
	{
		m_int |= (1 << Src);
		if (!old)
		{
			m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
			if (m_pipe) m_pipe->host_int_w(ASSERT_LINE);
		}
	}
	else
	{
		m_int &= ~(1 << Src);
		if (old && !m_int)
		{
			m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
			if (m_pipe) m_pipe->host_int_w(CLEAR_LINE);
		}
	}
}

uint8_t einstein_state::kybint_msk_r()
{
	uint8_t data = 0;

	// reading this port clears the keyboard interrupt
	m_keyboard_daisy->int_w(CLEAR_LINE);

	/* bit 0 and 1: fire buttons on the joysticks */
	if (m_buttons)
		data |= m_buttons->read() & 0x03;
	else
		data |= (m_portb_joy->read() & 0x10) >> 4 | (m_porta_joy->read() & 0x10) >> 3;

	/* bit 2 to 4: printer status */
	data |= m_centronics_busy << 2;
	data |= m_centronics_perror << 3;
	data |= m_centronics_fault << 4;

	/* bit 5 to 7: graph, control and shift key */
	data |= m_extra->read();

	if(VERBOSE_KEYBOARD)
		logerror("%s: kybint_msk_r %02x\n", machine().describe_context(), data);

	return data;
}

void einstein_state::kybint_msk_w(uint8_t data)
{
	logerror("KEY interrupt %s\n", BIT(data, 0) ? "disabled" : "enabled");
	m_keyboard_daisy->mask_w(BIT(data, 0));
}

void einstein_state::adcint_msk_w(uint8_t data)
{
	logerror("ADC interrupt %s\n", BIT(data, 0) ? "disabled" : "enabled");
	m_adc_daisy->mask_w(BIT(data, 0));
}

void einstein_state::fireint_msk_w(uint8_t data)
{
	logerror("FIRE interrupt %s\n", BIT(data, 0) ? "disabled" : "enabled");
	m_fire_daisy->mask_w(BIT(data, 0));
}

void einstein_state::evdpint_msk_w(uint8_t data)
{
	logerror("EVDP interrupt %s\n", BIT(data, 0) ? "disabled" : "enabled");
	logerror("Printer STROBE %s\n", BIT(data, 1) ? "disabled" : "enabled");
	m_vdp_daisy->mask_w(BIT(data, 0));
}


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

uint8_t einstein_state::rom_r()
{
	if (!machine().side_effects_disabled())
	{
		m_rom_enabled ^= 1;
		m_bank1->set_entry(m_rom_enabled);
	}

	return 0xff;
}

void einstein_state::rom_w(uint8_t data)
{
	m_rom_enabled ^= 1;
	m_bank1->set_entry(m_rom_enabled);
}

uint8_t einstein_state::reset_r()
{
	m_psg->reset();
	m_fdc->reset();

	return 0xff;
}

void einstein_state::reset_w(uint8_t data)
{
	m_psg->reset();
	m_fdc->reset();
}

void einstein_state::machine_start()
{
	m_alpha_lock_led.resolve();

	// initialize memory mapping
	m_bank1->configure_entry(0, m_ram->pointer());
	m_bank1->configure_entry(1, m_bios->base());
	m_bank2->set_base(m_ram->pointer());
	m_bank3->set_base(m_ram->pointer() + 0x8000);

	// register save states
	save_item(NAME(m_rom_enabled));
}

void einstein_state::machine_reset()
{
	// 2nd rom socket
	if (m_rom2.found() && m_rom2->exists())
	{
		memcpy(m_bios->base() + 0x4000, m_rom2->get_rom_base(), m_rom2->get_rom_size());
	}

	// rom enabled on reset
	m_rom_enabled = 1;
	m_bank1->set_entry(m_rom_enabled);

	// interrupt mask enabled
	m_keyboard_daisy->mask_w(1);
	m_adc_daisy->mask_w(1);
	m_fire_daisy->mask_w(1);

	m_strobe = -1;

	// enable Alpha Lock LED
	m_alpha_lock = 1;
	m_alpha_lock_led = 1;
}


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

uint8_t einstein_state::system_r()
{
	uint8_t data = 0;

	// b0 1 = Alpha Lock key pressed
	data |= !BIT(m_line[0]->read(), 4);
	// b1 1 = ROM enabled
	data |= m_rom_enabled << 1;
	// b2 Dipswitch 1
	// b3 Dipswitch 2
	// b4 Dipswitch 3
	// b5 Dipswitch 4
	data |= (m_dips->read() & 0x0f) << 2;
	// b6 0 = mouse connected
	data |= 0x40;
	// b7 cassette input
	data |= (m_cassette->input() > 0.0 ? 1 : 0) << 7;

	return data;
}

uint8_t einstein_state::porta_r()
{
	uint8_t data = 0;

	data |= m_porta_joy->read() & 0x1f;
	data |= m_centronics_perror << 5;
	data |= m_centronics_fault << 6;

	return data;
}

void einstein_state::porta_w(uint8_t data)
{
	m_centronics->write_data0(BIT(data, 0));
	m_centronics->write_data1(BIT(data, 1));
	m_centronics->write_data2(BIT(data, 2));
	m_centronics->write_data3(BIT(data, 3));
	m_centronics->write_strobe(BIT(data, 4));
}

void einstein_state::porta_int_w(uint8_t data)
{
	// TODO: Implement Port A interrupts (not used for printing!)
	logerror("Port A interrupt %s\n", BIT(data, 7) ? "enabled" : "disabled");
	m_porta_int = data;
}

uint8_t einstein_state::portb_r()
{
	uint8_t data = 0;

	data |= m_portb_joy->read() & 0x1f;
	data |= m_centronics_busy << 5;
	data |= m_centronics_ack << 6;

	return data;
}

void einstein_state::portb_w(uint8_t data)
{
	m_centronics->write_data4(BIT(data, 0));
	m_centronics->write_data5(BIT(data, 1));
	m_centronics->write_data6(BIT(data, 2));
	m_centronics->write_data7(BIT(data, 3));
}

uint8_t einstein_state::pseudo_adc_r()
{
	uint8_t data = 0x7f; // centre

	uint8_t port = BIT(m_pseudo_adc, 1) ? m_porta_joy->read() : m_portb_joy->read();

	m_adc_daisy->int_w(CLEAR_LINE);

	switch (m_pseudo_adc & 0x05)
	{
	case 4:
		switch (port & 0x0c)
		{
		case 0x04: data = 0xff; break; // right
		case 0x08: data = 0x00; break; // left
		}
		break;
	case 5:
		switch (port & 0x03)
		{
		case 0x01: data = 0x00; break; // down
		case 0x02: data = 0xff; break; // up
		}
		break;
	}
	return data;
}

void einstein_state::pseudo_adc_w(uint8_t data)
{
	m_pseudo_adc = data;
	m_adc_daisy->int_w(ASSERT_LINE);
}

uint8_t einstein_state::alpha_lock_r()
{
	if (!machine().side_effects_disabled())
	{
		m_alpha_lock ^= 1;
		m_alpha_lock_led = m_alpha_lock;
	}
	return 0xff;
}

void einstein_state::alpha_lock_w(uint8_t data)
{
	m_alpha_lock ^= 1;
	m_alpha_lock_led = m_alpha_lock;
}


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

void einstein_state::einstein_mem(address_map &map)
{
	map(0x0000, 0x07fff).bankr("bank1").bankw("bank2");
	map(0x8000, 0x0ffff).bankrw("bank3");
}

// I/O ports are decoded into 8 blocks using address lines A3 to A7
void einstein_state::einstein_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x01).mirror(0xff04).rw(FUNC(einstein_state::reset_r), FUNC(einstein_state::reset_w));
	map(0x02, 0x02).mirror(0xff04).rw(m_psg, FUNC(ay8910_device::data_r), FUNC(ay8910_device::address_w));
	map(0x03, 0x03).mirror(0xff04).w(m_psg, FUNC(ay8910_device::data_w));
	map(0x08, 0x09).mirror(0xff06).rw("vdp", FUNC(tms9129_device::read), FUNC(tms9129_device::write));
	map(0x10, 0x11).mirror(0xff06).rw(IC_I060, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x18, 0x1b).mirror(0xff04).rw(m_fdc, FUNC(wd1770_device::read), FUNC(wd1770_device::write));
	map(0x20, 0x20).mirror(0xff00).rw(FUNC(einstein_state::kybint_msk_r), FUNC(einstein_state::kybint_msk_w));
	map(0x21, 0x21).mirror(0xff00).w(FUNC(einstein_state::adcint_msk_w));
	map(0x23, 0x23).mirror(0xff00).w(FUNC(einstein_state::drsel_w));
	map(0x24, 0x24).mirror(0xff00).rw(FUNC(einstein_state::rom_r), FUNC(einstein_state::rom_w));
	map(0x25, 0x25).mirror(0xff00).w(FUNC(einstein_state::fireint_msk_w));
	map(0x28, 0x2b).mirror(0xff04).rw(IC_I058, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x30, 0x33).mirror(0xff04).rw(IC_I063, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x38, 0x38).mirror(0xff07).rw("adc", FUNC(adc0844_device::read), FUNC(adc0844_device::write));
}

void einstein_state::einst256_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x01).mirror(0xff04).rw(FUNC(einstein_state::reset_r), FUNC(einstein_state::reset_w));
	map(0x02, 0x02).mirror(0xff04).rw(m_psg, FUNC(ay8910_device::data_r), FUNC(ay8910_device::address_w));
	map(0x03, 0x03).mirror(0xff04).w(m_psg, FUNC(ay8910_device::data_w));
	map(0x08, 0x0b).mirror(0xff04).rw("v9938", FUNC(v9938_device::read), FUNC(v9938_device::write));
	map(0x10, 0x11).mirror(0xff06).rw(IC_I060, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x18, 0x1b).mirror(0xff04).rw(m_fdc, FUNC(wd1770_device::read), FUNC(wd1770_device::write));
	map(0x20, 0x20).mirror(0xff00).rw(FUNC(einstein_state::kybint_msk_r), FUNC(einstein_state::kybint_msk_w));
	map(0x21, 0x21).mirror(0xff00).w(FUNC(einstein_state::adcint_msk_w));
	map(0x22, 0x22).mirror(0xff00).rw(FUNC(einstein_state::alpha_lock_r), FUNC(einstein_state::alpha_lock_w));
	map(0x23, 0x23).mirror(0xff00).w(FUNC(einstein_state::drsel_w));
	map(0x24, 0x24).mirror(0xff00).rw(FUNC(einstein_state::rom_r), FUNC(einstein_state::rom_w));
	map(0x25, 0x25).mirror(0xff00).w(FUNC(einstein_state::fireint_msk_w));
	map(0x26, 0x26).mirror(0xff00).r(FUNC(einstein_state::system_r));
	map(0x28, 0x2b).mirror(0xff04).rw(IC_I058, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x30, 0x30).mirror(0xff00).rw(FUNC(einstein_state::porta_r), FUNC(einstein_state::porta_w));
	map(0x31, 0x31).mirror(0xff00).w(FUNC(einstein_state::porta_int_w));
	map(0x32, 0x32).mirror(0xff00).rw(FUNC(einstein_state::portb_r), FUNC(einstein_state::portb_w));
	map(0x38, 0x38).mirror(0xff07).rw(FUNC(einstein_state::pseudo_adc_r), FUNC(einstein_state::pseudo_adc_w));
	map(0x80, 0x80).mirror(0xff00).w(FUNC(einstein_state::evdpint_msk_w));
}


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

static INPUT_PORTS_START( keyboard )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BREAK") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F0") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F7") PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CAPS LOCK") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LEFT") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR(0xA3)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DOWN") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(0xBA) PORT_CHAR(0xBD)    // is \xBA correct for double vertical bar || ?
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('@')

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RIGHT") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TAB") PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F5))

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DELETE") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('-')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("UP") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F4))

	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F3))

	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F2))

	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_START("LINE7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F6") PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F6))

	PORT_START("EXTRA")
	PORT_BIT(0x1f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("GRPH")    PORT_CODE(KEYCODE_F8)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CONTROL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT")   PORT_CODE(KEYCODE_LSHIFT)   PORT_CODE(KEYCODE_RSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)
INPUT_PORTS_END

static INPUT_PORTS_START( einstein )
	PORT_INCLUDE(keyboard)

	// fire buttons for analogue joysticks
	PORT_START("BUTTONS")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("Joystick 1 Button 1") PORT_PLAYER(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(einstein_state::joystick_button), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("Joystick 2 Button 1") PORT_PLAYER(2) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(einstein_state::joystick_button), 0)
	PORT_BIT(0xfc, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("analogue_1_x")
	PORT_BIT(0xff, 0x80, IPT_AD_STICK_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_CENTERDELTA(100) PORT_MINMAX(0,0xff) PORT_PLAYER(1)
	PORT_CODE_DEC(KEYCODE_4_PAD)         PORT_CODE_INC(KEYCODE_6_PAD)
	PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH) PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH)

	PORT_START("analogue_1_y")
	PORT_BIT(0xff, 0x80, IPT_AD_STICK_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_CENTERDELTA(100) PORT_MINMAX(0,0xff) PORT_PLAYER(1)
	PORT_CODE_DEC(KEYCODE_8_PAD)         PORT_CODE_INC(KEYCODE_2_PAD)
	PORT_CODE_DEC(JOYCODE_Y_UP_SWITCH)   PORT_CODE_INC(JOYCODE_Y_DOWN_SWITCH)
	PORT_REVERSE

	PORT_START("analogue_2_x")
	PORT_BIT(0xff, 0x80, IPT_AD_STICK_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_CENTERDELTA(100) PORT_MINMAX(0,0xff) PORT_PLAYER(2)
	PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH) PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH)

	PORT_START("analogue_2_y")
	PORT_BIT(0xff, 0x80, IPT_AD_STICK_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_CENTERDELTA(100) PORT_MINMAX(0,0xff) PORT_PLAYER(2)
	PORT_CODE_DEC(JOYCODE_Y_UP_SWITCH)   PORT_CODE_INC(JOYCODE_Y_DOWN_SWITCH)
	PORT_REVERSE
INPUT_PORTS_END

static INPUT_PORTS_START( einst256 )
	PORT_INCLUDE(keyboard)

	PORT_MODIFY("LINE0")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ALPHA LOCK") PORT_CODE(KEYCODE_CAPSLOCK)

	PORT_START("PORTA_JOY")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)    PORT_PLAYER(1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)  PORT_PLAYER(1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)  PORT_PLAYER(1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(1)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_BUTTON1)        PORT_PLAYER(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(einstein_state::joystick_button), 0)

	PORT_START("PORTB_JOY")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)    PORT_PLAYER(2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)  PORT_PLAYER(2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)  PORT_PLAYER(2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(2)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_BUTTON1)        PORT_PLAYER(2) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(einstein_state::joystick_button), 0)

	PORT_START("DIPS")
	PORT_DIPNAME(0x01, 0x00, "Line Standard") PORT_DIPLOCATION("S:1")
	PORT_DIPSETTING(0x00, "525 lines 60Hz")
	PORT_DIPSETTING(0x01, "625 lines 50Hz")
	PORT_DIPNAME(0x02, 0x00, "Printer") PORT_DIPLOCATION("S:2")
	PORT_DIPSETTING(0x00, "Parallel")
	PORT_DIPSETTING(0x02, "Serial")
	PORT_DIPNAME(0x0c, 0x00, "Language") PORT_DIPLOCATION("S:3,4")
	PORT_DIPSETTING(0x00, "English (ISO646)")
	PORT_DIPSETTING(0x04, "ASCII")
	PORT_DIPSETTING(0x08, "German")
	PORT_DIPSETTING(0x0c, "Spanish")
INPUT_PORTS_END


/***************************************************************************
    QUICKLOAD
***************************************************************************/

QUICKLOAD_LOAD_MEMBER(einstein_state::quickload_cb)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);

	if (image.length() >= 0xfd00)
		return std::make_pair(image_error::INVALIDLENGTH, std::string());

	// disable ROM
	m_rom_enabled = 0;
	m_bank1->set_entry(m_rom_enabled);

	// load image
	uint16_t const quickload_size = image.length();
	for (uint16_t i = 0; i < quickload_size; i++)
	{
		uint8_t data;

		if (image.fread(&data, 1) != 1)
			return std::make_pair(image_error::UNSPECIFIED, std::string());
		prog_space.write_byte(i + 0x100, data);
	}

	// start program
	m_maincpu->set_pc(0x100);

	return std::make_pair(std::error_condition(), std::string());
}


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

static void einstein_floppies(device_slot_interface &device)
{
	device.option_add("3ss", TEAC_FD_30A);
	device.option_add("3ds", FLOPPY_3_DSDD);
	device.option_add("525ssqd", FLOPPY_525_SSQD);
	device.option_add("525qd", FLOPPY_525_QD);
	device.option_add("35ssdd", FLOPPY_35_SSDD);
	device.option_add("35dd", FLOPPY_35_DD);
}

void einstein_state::einstein(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL_X002 / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &einstein_state::einstein_mem);
	m_maincpu->set_addrmap(AS_IO, &einstein_state::einstein_io);
	m_maincpu->set_daisy_config(einstein_daisy_chain);

	/* this is actually clocked at the system clock 4 MHz, but this would be too fast for our
	driver. So we update at 50Hz and hope this is good enough. */
	TIMER(config, "keyboard").configure_periodic(FUNC(einstein_state::keyboard_timer_callback), attotime::from_hz(50));

	z80pio_device& pio(Z80PIO(config, IC_I063, XTAL_X002 / 2));
	pio.out_int_callback().set(FUNC(einstein_state::int_w<0>));
	pio.out_pa_callback().set("cent_data_out", FUNC(output_latch_device::write));
	pio.out_ardy_callback().set(FUNC(einstein_state::ardy_w));
	pio.in_pb_callback().set("user", FUNC(einstein_userport_device::read));
	pio.out_pb_callback().set("user", FUNC(einstein_userport_device::write));
	pio.out_brdy_callback().set("user", FUNC(einstein_userport_device::brdy_w));

	z80ctc_device& ctc(Z80CTC(config, IC_I058, XTAL_X002 / 2));
	ctc.intr_callback().set(FUNC(einstein_state::int_w<1>));
	ctc.set_clk<0>(XTAL_X002 / 4);
	ctc.set_clk<1>(XTAL_X002 / 4);
	ctc.set_clk<2>(XTAL_X002 / 4);
	ctc.zc_callback<0>().set(IC_I060, FUNC(i8251_device::write_txc));
	ctc.zc_callback<1>().set(IC_I060, FUNC(i8251_device::write_rxc));
	ctc.zc_callback<2>().set(IC_I058, FUNC(z80ctc_device::trg3));

	/* Einstein daisy chain support for non-Z80 devices */
	Z80DAISY_GENERIC(config, m_keyboard_daisy, 0xf7);
	m_keyboard_daisy->int_handler().set(FUNC(einstein_state::int_w<2>));
	Z80DAISY_GENERIC(config, m_adc_daisy, 0xfb);
	m_adc_daisy->int_handler().set(FUNC(einstein_state::int_w<3>));
	Z80DAISY_GENERIC(config, m_fire_daisy, 0xfd);
	m_fire_daisy->int_handler().set(FUNC(einstein_state::int_w<4>));

	/* video hardware */
	tms9129_device &vdp(TMS9129(config, "vdp", 10.738635_MHz_XTAL));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000); // 16k RAM, provided by IC i040 and i041
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	AY8910(config, m_psg, XTAL_X002 / 4);
	m_psg->port_b_read_callback().set(FUNC(einstein_state::keyboard_data_read));
	m_psg->port_a_read_callback().set([]() { return 0xff; });
	m_psg->port_a_write_callback().set(FUNC(einstein_state::keyboard_line_write));
	m_psg->add_route(ALL_OUTPUTS, "mono", 0.20);

	adc0844_device &adc(ADC0844(config, "adc"));
	adc.intr_callback().set(m_adc_daisy, FUNC(z80daisy_generic_device::int_w));
	adc.ch1_callback().set_ioport("analogue_1_x");
	adc.ch2_callback().set_ioport("analogue_1_y");
	adc.ch3_callback().set_ioport("analogue_2_x");
	adc.ch4_callback().set_ioport("analogue_2_y");

	/* printer */
	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set(IC_I063, FUNC(z80pio_device::strobe_a));
	m_centronics->busy_handler().set(FUNC(einstein_state::write_centronics_busy));
	m_centronics->perror_handler().set(FUNC(einstein_state::write_centronics_perror));
	m_centronics->fault_handler().set(FUNC(einstein_state::write_centronics_fault));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	TIMER(config, m_strobe_timer).configure_generic(FUNC(einstein_state::strobe_callback));

	/* uart */
	i8251_device &ic_i060(I8251(config, IC_I060, XTAL_X002 / 4));
	ic_i060.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	ic_i060.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	ic_i060.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));

	/* rs232 port */
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(IC_I060, FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set(IC_I060, FUNC(i8251_device::write_dsr));
	rs232.cts_handler().set(IC_I060, FUNC(i8251_device::write_cts));

	/* floppy */
	WD1770(config, m_fdc, XTAL_X002);

	FLOPPY_CONNECTOR(config, IC_I042 ":0", einstein_floppies, "3ss", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, IC_I042 ":1", einstein_floppies, "3ss", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, IC_I042 ":2", einstein_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, IC_I042 ":3", einstein_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	/* RAM is provided by 8k DRAM ICs i009, i010, i011, i012, i013, i014, i015 and i016 */
	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("64K");

	/* tatung pipe connector */
	TATUNG_PIPE(config, m_pipe, XTAL_X002 / 2, tatung_pipe_cards, nullptr);
	m_pipe->set_program_space(m_maincpu, AS_PROGRAM);
	m_pipe->set_io_space(m_maincpu, AS_IO);
	m_pipe->nmi_handler().set_inputline(IC_I001, INPUT_LINE_NMI);

	/* user port */
	EINSTEIN_USERPORT(config, "user").bstb_handler().set(IC_I063, FUNC(z80pio_device::strobe_b));

	/* 2nd rom socket I024 */
	GENERIC_SOCKET(config, m_rom2, generic_linear_slot, "einstein_rom", "bin,rom");

	/* software lists */
	SOFTWARE_LIST(config, "disk_list").set_original("einstein").set_filter("TC01");
	SOFTWARE_LIST(config, "rom_list").set_original("einstein_rom");

	quickload_image_device &quickload(QUICKLOAD(config, "quickload", "com", attotime::from_seconds(2)));
	quickload.set_load_callback(FUNC(einstein_state::quickload_cb));
	quickload.set_interface("einstein_quik");
}

void einstein_state::einst256(machine_config &config)
{
	einstein(config);

	/* remove components not present in TC256 */
	config.device_remove(IC_I063);
	config.device_remove("vdp");
	config.device_remove("adc");
	config.device_remove(IC_I042 ":2");
	config.device_remove(IC_I042 ":3");
	config.device_remove("pipe");
	config.device_remove("user");
	config.device_remove("rom2");

	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_IO, &einstein_state::einst256_io);
	m_maincpu->set_daisy_config(einst256_daisy_chain);

	Z80DAISY_GENERIC(config, m_vdp_daisy, 0xfe);
	m_vdp_daisy->int_handler().set(FUNC(einstein_state::int_w<5>));

	/* video hardware */
	v9938_device &v9938(V9938(config, "v9938", 21.477272_MHz_XTAL));
	v9938.set_screen("screen");
	v9938.set_vram_size(0x30000);
	v9938.int_cb().set(m_vdp_daisy, FUNC(z80daisy_generic_device::int_w));

	/* printer */
	m_centronics->ack_handler().set(FUNC(einstein_state::write_centronics_ack));

	/* cassette */
	CASSETTE(config, m_cassette);

	/* software lists */
	subdevice<software_list_device>("disk_list")->set_filter("256");
}


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

/* There are two sockets, i023 and i024, each either a 2764 or 27128
 * only i023 is used by default and fitted with the 8k bios (called MOS).
 *
 * We are missing dumps of version MOS 1.1, possibly of 1.0 if it exists.
 */
ROM_START( einstein )
	ROM_REGION(0x8000, "bios", ROMREGION_ERASEFF)
	/* i023 */
	ROM_SYSTEM_BIOS(0, "mos12", "MOS 1.2")
	ROMX_LOAD("mos12.i023", 0x0000, 0x2000, CRC(ec134953) SHA1(a02125d8ebcda48aa784adbb42a8b2d7ef3a4b77), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "mos121", "MOS 1.21")
	ROMX_LOAD("mos121.i023", 0x0000, 0x2000, CRC(a746eeb6) SHA1(f75aaaa777d0fd92225acba291f6bf428b341d3e), ROM_BIOS(1))
	ROM_RELOAD(0x2000, 0x2000)
#if 0
	// diagnostic rom, see https://github.com/fdivitto/TatungEinsteinDiagnosticFirmware
	ROM_LOAD("einstein_diag.bin", 0x4000, 0x065a, CRC(21faec06) SHA1(c9b26b79bfa19178dae8e07e14376d5dc88c46b3))
#endif
ROM_END

ROM_START( einst256 )
	ROM_REGION(0x8000, "bios", ROMREGION_ERASEFF)
	/* i008 */
	ROM_LOAD("mos21.i008", 0x0000, 0x4000, CRC(d1bb5efc) SHA1(9168df70af6746c88748049d1b9d119a29e605de) )
ROM_END

} // anonymous namespace


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

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY   FULLNAME          FLAGS
COMP( 1984, einstein, 0,      0,      einstein, einstein, einstein_state, empty_init, "Tatung", "Einstein TC-01", 0 )
COMP( 1986, einst256, 0,      0,      einst256, einst256, einstein_state, empty_init, "Tatung", "Einstein 256",   0 )



eispc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Joakim Larsson Edström
/***************************************************************************************************
 *
 *   Ericsson Information Systems PC "compatibles"
 *
 * The Ericsson PC was the first original Ericsson design for the office PC market replacing the
 * Step/One which was an OEM:ed clone of the Matsushita Mybrain 3000 (see myb3k.cpp driver).
 *
 **************************************************************
 * Ericsson PC
 *------------
 * Links: https://youtu.be/6uilOdMJc24
 * Form Factor: Desktop
 * CPU: 8088 @ 4.77MHz
 * RAM: 256K
 * Bus: 6x ISA
 * Video: Monchrome or Color 80x25 character mode. 320x200 and 640x400 grahics modes
 * Display: Orange Gas Plasma (GP) display
 * Mass storage: 2 x 5.25" 360K or 1 20Mb HDD
 * On board ports: Beeper,
 * Ports: serial, parallel
 * Internal Options: Up to 640K RAM through add-on RAM card
 * Misc: The hardware was not 100% PC compatible so non BIOS based software would not always run. 50.000+ units sold
 *
 * TODO
 * - Complete the Ericsson 1070 MDA ISA board and test all the graphics modes including 640x400 (aka HR)
 * - Add the Ericsson 1065 HDC and boot from a hard drive
 * - Add softlist
 * - Pass the diagnostics software system test at EPC2.IMD, it currently hangs the keyboard.
 *   A later version of the test on EPC5.IMD works though so need to verify EPC2.IMD on real hardware first.
 *
 * CREDITS  The driver code is inspired from m24.cpp, myb3k.cpp and genpc.cpp. Information about the EPC has
 *          been contributed by many, mainly the people at Dalby Computer museum http://www.datormuseum.se/
 *          A dead pcb was donated by rfka01 and rom dumps by ZnaxQue@sweclockers.com
 *
 ************************************************************************************************************/
/*
 Links
 -----

 */

#include "emu.h"

#include "eispc_kb.h"
#include "epc.lh"

// Devices
#include "cpu/i86/i86.h"
#include "machine/am9517a.h"
#include "machine/i8087.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/pit8253.h"
#include "machine/pic8259.h"
#include "machine/upd765.h"
#include "machine/ins8250.h"

// Expansion cards
//#include "bus/isa/isa.h"
//#include "bus/isa/isa_cards.h"
#include "bus/isa/ega.h"
#include "bus/isa/eis_hgb107x.h"
#include "bus/isa/eis_twib.h"
#include "machine/pc_lpt.h"

#include "machine/ram.h"
#include "machine/timer.h"
#include "sound/spkrdev.h"
#include "speaker.h"
#include "imagedev/floppy.h"
#include "bus/rs232/rs232.h"

#define LOG_PPI     (1U << 1)
#define LOG_PIT     (1U << 2)
#define LOG_PIC     (1U << 3)
#define LOG_KBD     (1U << 4)
#define LOG_DMA     (1U << 5)
#define LOG_IRQ     (1U << 6)
#define LOG_FDC     (1U << 7)
#define LOG_LPT     (1U << 8)
#define LOG_NMI     (1U << 9)
#define LOG_BITS    (1U << 10)
#define LOG_FPU     (1U << 11)
#define LOG_COM     (1U << 12)

//#define VERBOSE (LOG_LPT)
//#define LOG_OUTPUT_STREAM std::cout

#include "logmacro.h"

#define LOGPPI(...)  LOGMASKED(LOG_PPI,  __VA_ARGS__)
#define LOGPIT(...)  LOGMASKED(LOG_PIT,  __VA_ARGS__)
#define LOGPIC(...)  LOGMASKED(LOG_PIC,  __VA_ARGS__)
#define LOGKBD(...)  LOGMASKED(LOG_KBD,  __VA_ARGS__)
#define LOGDMA(...)  LOGMASKED(LOG_DMA,  __VA_ARGS__)
#define LOGIRQ(...)  LOGMASKED(LOG_IRQ,  __VA_ARGS__)
#define LOGFDC(...)  LOGMASKED(LOG_FDC,  __VA_ARGS__)
#define LOGLPT(...)  LOGMASKED(LOG_LPT,  __VA_ARGS__)
#define LOGNMI(...)  LOGMASKED(LOG_NMI,  __VA_ARGS__)
#define LOGBITS(...) LOGMASKED(LOG_BITS, __VA_ARGS__)
#define LOGFPU(...)  LOGMASKED(LOG_FPU,  __VA_ARGS__)
#define LOGCOM(...)  LOGMASKED(LOG_COM,  __VA_ARGS__)


namespace {

class epc_state : public driver_device
{
public:
	epc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_isabus(*this, "isabus")
		, m_dma8237a(*this, "dma8237")
		, m_ppi8255(*this, "ppi8255")
		, m_io_dsw(*this, "DSW")
		, m_io_j10(*this, "J10")
		, m_io_s21(*this, "S21")
		, m_lpt(*this, "lpt")
		, m_kbd8251(*this, "kbd8251")
		, m_keyboard(*this, "keyboard")
		, m_leds(*this, "kbled%u")
		, m_pic8259(*this, "pic8259")
		, m_pit8253(*this, "pit8253")
		, m_speaker(*this, "speaker")
		, m_fdc(*this, "fdc")
		, m_floppy_connectors(*this, "fdc:%u", 0)
		, m_uart(*this, "uart")
	{ }

	void epc(machine_config &config);
	void init_epc();


protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<i8086_cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<isa8_device> m_isabus;

	// DMA
	void dma_tc_w(int state);
	void dreq0_ck_w(int state);
	void epc_dma_hrq_changed(int state);
	void epc_dma8237_out_eop(int state);
	uint8_t epc_dma_read_byte(offs_t offset);
	void epc_dma_write_byte(offs_t offset, uint8_t data);
	template <int Channel> uint8_t epc_dma8237_io_r(offs_t offset);
	template <int Channel> void epc_dma8237_io_w(offs_t offset, uint8_t data);
	template <int Channel> void epc_dack_w(int state);
	required_device<am9517a_device> m_dma8237a;
	uint8_t m_dma_segment[4];
	uint8_t m_dma_active;
	bool m_tc;
	bool m_txd;
	bool m_rxrdy;
	bool m_int;
	bool m_dreq0_ck;

	// PPI
	required_device<i8255_device> m_ppi8255;
	void ppi_portb_w(uint8_t data);
	uint8_t ppi_portc_r();
	uint8_t m_ppi_portb;
	required_ioport m_io_dsw;
	required_ioport m_io_j10;
	required_ioport m_io_s21;

	// Printer port
	optional_device<pc_lpt_device> m_lpt;

	// Keyboard Controller/USART
	required_device<i8251_device> m_kbd8251;
	required_device<eispc_keyboard_device> m_keyboard;
	emu_timer *m_kbdclk_timer;
	TIMER_CALLBACK_MEMBER(rxtxclk_w);
	bool m_8251rxtx_clk_state;
	bool m_kbdclk_state;
	bool m_8251dtr_state;
	int m_kbdclk;
	output_finder<3> m_leds;

	// Interrupt Controller
	required_device<pic8259_device> m_pic8259;
	void int_w(int state);
	uint8_t m_nmi_enabled;
	uint8_t m_8087_int = 0;
	uint8_t m_parer_int = 0;
	uint8_t m_iochck_int = 0;
	void update_nmi();

	// Timer
	required_device<pit8253_device> m_pit8253;

	// Speaker
	void speaker_ck_w(int state);
	required_device<speaker_sound_device> m_speaker;
	bool m_pc4;
	bool m_pc5;

	void epc_map(address_map &map) ATTR_COLD;
	void epc_io(address_map &map) ATTR_COLD;

	// FDC
	void check_fdc_irq();
	void check_fdc_drq();
	required_device<i8272a_device> m_fdc;
	uint8_t m_ocr;
	bool m_irq;     // System signal after glue logic
	bool m_drq;     // System signal after glue logic
	bool m_fdc_irq; // FDC output pin
	bool m_fdc_drq; // FDC output pin

	optional_device_array<floppy_connector, 4> m_floppy_connectors;

	// UART
	required_device<ins8250_device> m_uart;
};

void epc_state::check_fdc_irq()
{
	bool pirq = m_irq;
	m_irq = m_fdc_irq && (m_ocr & 4) && (m_ocr & 8);  // IRQ enabled and not in reset?
	if(m_irq != pirq) // has the state changed?
	{
		LOGIRQ("FDC: IRQ6 request: %d\n", m_irq);
		m_pic8259->ir6_w(m_irq);
	}
}

void epc_state::check_fdc_drq()
{
	bool pdrq = m_drq;
	m_drq = m_fdc_drq && (m_ocr & 4) && (m_ocr & 8); // DREQ enabled and not in reset?
	if(m_drq != pdrq) // has the state changed?
	{
		LOGDMA("FDC: DMA channel 2 request: %d\n", m_drq);
		m_dma8237a->dreq2_w(m_drq);
	}
}

void epc_state::epc_map(address_map &map)
{
	map.unmap_value_high();
	map(0x20000, 0x9ffff).noprw(); // Base RAM - mapped to avoid unmaped errors when BIOS is probing RAM size
	// 0xa0000-0xaffff is reserved
	map(0xb0000, 0xb7fff).noprw(); // Monochrome RAM - mapped to avoid unaped errors when BIOS is probing RAM size
	map(0xb0000, 0xb7fff).noprw(); // Monochrome RAM - mapped to avoid unaped errors when BIOS is probing RAM size
	map(0xb8000, 0xbffff).noprw(); // Color/Graphics RAM - mapped to avoid unaped errors when BIOS is probing RAM size
	map(0xc0000, 0xeffff).noprw(); // Expansion ROM area - Hard Disk BIOS etc
	map(0xf0000, 0xfffff).rom().region("bios", 0);
}

void epc_state::epc_io(address_map &map)
{
	map(0x0000, 0x000f).mirror(0x10).lrw8(
		[this](offs_t offset) -> uint8_t
		{
			uint8_t data = m_dma8237a->read(offset);
			LOGDMA("dma8237_r %04x\n", offset);
			return data;
		},
		"dma8237_r",
		[this](offs_t offset, uint8_t data)
		{
			LOGDMA("dma8237_w %04x: %02x\n", offset, data);
			m_dma8237a->write(offset, data);
		},
		"dma8237_w"
	);

	map(0x0020, 0x0021).mirror(0x1e).lrw8(
		[this](offs_t offset) -> uint8_t
		{
			uint8_t data = m_pic8259->read(offset);
			LOGPIC("pic8259_r %04x: %02x\n", offset, data);
			return data;
		},
		"pic8259_r",
		[this](offs_t offset, uint8_t data)
		{
			LOGPIC("pic8259_w %04x: %02x\n", offset, data);
			m_pic8259->write(offset, data);
		},
		"pic8259_w"
	);

	map(0x0040, 0x0043).mirror(0x1c).lrw8(
		[this](offs_t offset) -> uint8_t
		{
			uint8_t data = m_pit8253->read(offset);
			LOGPIT("pit8253_r %04x\n", offset);
			return data;
		},
		"pit8253_r",
		[this](offs_t offset, uint8_t data)
		{
			LOGPIT("pit8253_w %04x: %02x\n", offset, data);
			m_pit8253->write(offset, data);
		},
		"pit8253_w"
	);

	map(0x0060, 0x0060).mirror(0x1c).lrw8(
		[this]() -> uint8_t
		{
			uint8_t data = m_kbd8251->data_r();
			LOGKBD("kbd8251_r %02x\n", data);
			return data;
		},
		"kbd_8251_data_r",
		[this](offs_t offset, uint8_t data)
		{
			LOGKBD("kbd8251_w 0x60 %02x\n", data);
			m_kbd8251->data_w(data);
		},
		"kbd_8251_data_w"
	);
									// NOTE: PPI Port A is not mapped
	map(0x0061, 0x0061).mirror(0x1c).lrw8(              // PPI Port B
		[this](offs_t offset) -> uint8_t
		{
			uint8_t data = m_ppi8255->read(1);
			LOGPPI("ppi8255_r Port B: %02x\n", data);
			return data;
		},
		"ppi8255_r",
		[this](offs_t offset, uint8_t data)
		{
			LOGPPI("ppi8255_w Port B: %02x\n", data);
			m_ppi8255->write(1, data);
		},
		"ppi8255_w"
	);

	map(0x0062, 0x0062).mirror(0x1c).lrw8(              // PPI Port C
		[this](offs_t offset) -> uint8_t
		{
			uint8_t data = m_ppi8255->read(2);
			LOGPPI("ppi8255_r Port C: %02x\n", data);
			return data;
		},
		"ppi8255_r",
		[this](offs_t offset, uint8_t data)
		{
			LOGPPI("ppi8255_w Port C: %02x\n", data);
			m_ppi8255->write(2, data);
		},
		"ppi8255_w"
	);

	map(0x0063, 0x0063).lrw8(               // PPI Control register
		[this](offs_t offset) -> uint8_t
		{
			uint8_t data = m_ppi8255->read(3);
			LOGPPI("ppi8255_r Control: %02x\n", data);
			return data;
		},
		"ppi8255_r",
		[this](offs_t offset, uint8_t data)
		{
			LOGPPI("ppi8255_w Control: %02x\n", data);
			m_ppi8255->write(3, data);
		},
		"ppi8255_w"
	);

	map(0x0070, 0x0070).mirror(0x0e).lw8(
		[this](offs_t offset, uint8_t data)
		{
			LOGKBD("kbd8251_w 0x70: %02x\n", data);
			m_kbd8251->data_w(data);
		},
		"i8251_data_w"
	);

	map(0x0071, 0x0071).mirror(0x0e).lrw8(
		[this](offs_t offset) -> uint8_t
		{
			uint8_t stat = m_kbd8251->status_r();
			//LOGKBD("kbd8251_status_r %02x\n", stat);
			return stat;
		},
		"kbd_8251_stat_ctrl_r",
		[this](offs_t offset, uint8_t data)
		{
			LOGKBD("kbd8251_control_w 0x71: %02x\n", data);
			m_kbd8251->control_w(data);
		},
		"kbd_8251_stat_ctrl_w"
	);

	map(0x0080, 0x0083).mirror(0xc).lw8(
		[this](offs_t offset, uint8_t data)
		{
			LOGDMA("dma_segment_w %04x: %02x\n", offset, data);
			m_dma_segment[offset] = data & 0x0f;
		},
		"dma_segement_w"
	);

	map(0x00a0, 0x00a1).mirror(0xe).lw8(
		[this](offs_t offset, uint8_t data)
		{
			LOGNMI("nmi_enable_w %04x: %02x\n", offset, data);
			m_nmi_enabled = BIT(data,7);
			update_nmi();
		},
		"nmi_enable_w"
	);

	// FDC Output Control Register (same as PC XT DOR)
	map(0x03f2, 0x03f3).lw8(                // B0-B1 Drive select 0-3
		[this](offs_t offset, uint8_t data) // B2 FDC Reset line
		{                   // B3 Enable FDC DMA/IRQ
			LOGFDC("FDC OCR: %02x\n", data);// B4-B7 Motor on for selected drive
			uint8_t pocr = m_ocr;
			uint8_t fid = m_ocr & 3;
			m_ocr = data;
			if ((m_ocr & 4) && m_floppy_connectors[fid]) // Not in reset and there is a floppy drive attached
			{
				floppy_image_device *floppy = m_floppy_connectors[fid]->get_device(); // try to retrieve the floppy
				if (floppy)
				{
					LOGFDC(" - Motor %s for drive %d\n", (m_ocr & (0x10 << fid)) ? "ON" : "OFF", fid);
					floppy->mon_w(!(m_ocr & (0x10 << fid)));
					LOGFDC(" - Setting a floppy for drive %d\n", fid);
					m_fdc->set_floppy((m_ocr & (0x10 << fid)) ? floppy : nullptr);
				}
			}
			if (((pocr ^ m_ocr) & 4) && (m_ocr & 4) == 0) // If FDC reset state bit has changed to low then reset the FDC
				m_fdc->reset();
			check_fdc_irq();
			check_fdc_drq();
		},
		"ocr_w"
	);

	map(0x03f4, 0x03f5).m(m_fdc, FUNC(i8272a_device::map));

	map(0x03bc, 0x03be).lrw8(
		[this](offs_t offset, uint8_t mem_mask) -> uint8_t
		{
			uint8_t data = m_lpt->read(offset);
			LOGLPT("LPT read offset %02x: %02x\n", offset, data);
			return data;
		},
		"lpt_r",
		[this](offs_t offset, uint8_t data)
		{
			LOGLPT("LPT write offset %02x: %02x\n", offset, data);
			m_lpt->write(offset, data);
		},
		"lpt_w"
	);

	map(0x03f8, 0x03ff).rw(m_uart, FUNC(ins8250_device::ins8250_r), FUNC(ins8250_device::ins8250_w));
}

void epc_state::machine_start()
{
	m_maincpu->space(AS_PROGRAM).install_ram(0, m_ram->size() - 1, m_ram->pointer());

	std::fill_n(&m_dma_segment[0], 4, 0);

	save_item(NAME(m_dma_segment));
	save_item(NAME(m_dma_active));
	save_item(NAME(m_tc));
	save_item(NAME(m_txd));
	save_item(NAME(m_rxrdy));
	save_item(NAME(m_int));
	save_item(NAME(m_dreq0_ck));
	save_item(NAME(m_ppi_portb));
	save_item(NAME(m_8251rxtx_clk_state));
	save_item(NAME(m_kbdclk_state));
	save_item(NAME(m_kbdclk));
	save_item(NAME(m_8251dtr_state));
	save_item(NAME(m_nmi_enabled));
	save_item(NAME(m_8087_int));
	save_item(NAME(m_parer_int));
	save_item(NAME(m_iochck_int));
	save_item(NAME(m_pc4));
	save_item(NAME(m_pc5));
	save_item(NAME(m_ocr));
	save_item(NAME(m_irq));
	save_item(NAME(m_drq));
	save_item(NAME(m_fdc_irq));
	save_item(NAME(m_fdc_drq));

	m_leds.resolve();
}

void epc_state::machine_reset()
{
	m_dma_active = 0;
	m_tc = false;
	m_txd = false;
	m_rxrdy = false;
	m_int = 1;
	m_dreq0_ck = true;
	m_ppi_portb = 0;
	m_8251rxtx_clk_state = 0;
	m_kbdclk_state = 0;
	m_kbdclk = 0;
	m_8251dtr_state = 1;
	m_nmi_enabled = 0;
	m_8087_int = 0;
	m_parer_int = 0;
	m_iochck_int = 0;
	m_pc4 = 0;
	m_pc5 = 0;
	m_ocr = 0;
	m_irq = 0;
	m_drq = 0;
	m_fdc_irq = 0;
	m_fdc_drq = 0;

	m_keyboard->rst_line_w(ASSERT_LINE);
	m_kbd8251->write_cts(0); // Tied to GND
}

void epc_state::init_epc()
{
	/* Keyboard UART Rxc/Txc is 19.2 kHz from x960 divider */
	m_kbdclk_timer = timer_alloc(FUNC(epc_state::rxtxclk_w), this);
	m_kbdclk_timer->adjust(attotime::from_hz(XTAL(18'432'000) / 960) / 2);
}

TIMER_CALLBACK_MEMBER(epc_state::rxtxclk_w)
{
	m_kbd8251->write_rxc(m_8251rxtx_clk_state);
	m_kbd8251->write_txc(m_8251rxtx_clk_state);

	// The EPC PCB has an option to support a custom receive clock for the INS8250 apart from the TX clock through a mux controlled
	// by the DTR pin of the I8251. The ins8250 device doesn't support RCLK as it is considerd implicitly as the same as BAUDOUT
	// First attempt to support this in INS8250 by lifting out the BRG from deserial was reverted due to lots of regressions.
	// We probably need to remove diserial dependencies completely from ins8250 or implement BRG hooks in diserial.cpp.
	// if (!m_8251dtr_state) m_uart->rclk_w(m_8251rxtx_clk_state); // TODO: fix RCLK support in INS8250

	m_8251rxtx_clk_state = !m_8251rxtx_clk_state;

	// If CLK signal is jumpered in instead of reset signal for the keyboard
	if ((m_io_s21->read() & 0x01) == 0x01)
	{
		if (m_kbdclk++ >= 4) // Frequncy is taken out of the same divider as the rxtx clock but 2 steps later
		{
			m_keyboard->rst_line_w(m_kbdclk_state);
			m_kbdclk = 0;
			m_kbdclk_state = !m_kbdclk_state;
		}
	}

	/* Keyboard UART Rxc/Txc is 19.2 kHz from x960 divider ( 15 (74ls161) * 4 (74ls393.1) * 16 (74ls393) ) */
	m_kbdclk_timer->adjust(attotime::from_hz(XTAL(18'432'000) / 960) / 2);
}

template <int Channel>
uint8_t epc_state::epc_dma8237_io_r(offs_t offset)
{
	LOGDMA("epc_dma8237_io_r: %d\n", Channel);
	if (Channel == 2)
		return m_fdc->dma_r();
	else
		return m_isabus->dack_r(Channel);
}

template <int Channel>
void epc_state::epc_dma8237_io_w(offs_t offset, uint8_t data)
{
	LOGDMA("epc_dma8237_io_w: %d - %02x\n", Channel, data);
	if (Channel == 2)
		m_fdc->dma_w(data);
	else
		m_isabus->dack_w(Channel, data);
}

template <int Channel>
void epc_state::epc_dack_w(int state)
{
	LOGDMA("epc_dack_w: %d - %d\n", Channel, state);

	m_isabus->dack_line_w(Channel, state);

	if (!state)
	{
		m_dma_active |= 1 << Channel;
		if (Channel == 0)
			m_dma8237a->dreq0_w(0);
		if (m_tc)
			m_isabus->eop_w(Channel, ASSERT_LINE);
	}
	else
	{
		m_dma_active &= ~(1 << Channel);
		if (m_tc)
			m_isabus->eop_w(Channel, CLEAR_LINE);
	}
}

void epc_state::dma_tc_w(int state)
{
	m_tc = (state == ASSERT_LINE);
	for (int channel = 0; channel < 4; channel++)
	{
		if (BIT(m_dma_active, channel))
		{
			LOGDMA("dma_tc_w ch %d: %d\n", channel, state);
			m_isabus->eop_w(channel, state);
		}
	}

	// Special treatment for on board FDC
	if (BIT(m_dma_active, 2))
	{
		m_fdc->tc_w(0);
	}
	else
	{
		m_fdc->tc_w(1);
	}
}

void epc_state::dreq0_ck_w(int state)
{
	if (state && !m_dreq0_ck && !BIT(m_dma_active, 0))
		m_dma8237a->dreq0_w(1);

	m_dreq0_ck = state;
}

void epc_state::speaker_ck_w(int state)
{
	m_pc5 = state;
	m_pc4 = (m_ppi_portb & 0x02) && state ? 1 : 0;
	m_speaker->level_w(m_pc4);
}

/**********************************************************
 *
 * PPI8255 interface
 *
 *
 * PORT A (not used)
 *
 * Reads of port A is shadowed by UART8251A's read register
 * gaining some compatibility with PC software. The UART8251
 * communicates with the serial keyboard and extends it with
 * write capability enabling keyboard led control as with a
 * PC AT keyboard.
 *
 * PORT B (output)
 * 0 - PB0 -             - Control signal for the sound generator (short beeps)
 * 1 - PB1 -             - Control signal for the sound generator
 * 2 - PB2 -             - Unused
 * 3 - PB3 -             - Data select for the configuration switches 0=SW1-4 1=SW5-8
 * 4 - PB4 - *           - Enable ram parity check
 * 5 - PB5 - *           - Enable expansion I/O check
 * 6 - PB6 - *           - Keyboard reset
 * 7 - PB7 -             - Reset keyboard interrupt
 *
 * PORT C
 * 0 - PC0 -         - Dipswitch SW 1/5 PB3=0/PB3=1
 * 1 - PC1 -         - Dipswitch SW 2/6 PB3=0/PB3=1
 * 2 - PC2 -         - Dipswitch SW 3/7 PB3=0/PB3=1
 * 3 - PC3 -         - Dipswitch SW 4/8 PB3=0/PB3=1
 * 4 - PC4 - SPK     - Speaker/cassette data (spare in PC XT spec)
 * 5 - PC5 - OUT2    - OUT2 from 8253 (ibmpcjr compatible)
 * 6 - PC6 -
 * 7 - PC7 -
 *
 * Ericsson PC SW:
 * 1   - Not used. Must be set to OFF
 * 2   - OFF - 8087 present
 *       ON  - No 8087 present *)
 * 3   - Not Used. Don't care but OFF *)
 * 4   - Not Used. Must be set to ON
 * 5+6 - Used to select display
 *       OFF OFF - Monochrome HR graphics monitor 3111 installed + 1020 color secondary monitor
 *       ON  OFF - Monochrome HR graphics monitor 3111 installed + optional 1020 color main monitor *)
 *       OFF ON  - Not used
 *       ON  ON  - Not used
 * 7+8 - Used to select number of disk drives
 *       OFF OFF - Not used
 *       ON  OFF - Not used
 *       OFF ON  - two disk drives, system units 1030-1 and 1030-2
 *       ON  ON  - one disk drive, system units 1030-3, 1030-4, 1031-1 and 1031-2
 *
 *           *)  - Factory settings
 *
 **********************************************************/

uint8_t epc_state::ppi_portc_r()
{
	uint8_t data;

	// Read 4 configurations dip switches depending on PB3
	data = (m_io_dsw->read() >> ((m_ppi_portb & 0x08) ? 4 : 0) & 0x0f);

	data |= (m_pc4 ? 1U << 4 : 0); // Feedback from gated speaker beep
	data |= (m_pc5 ? 1U << 5 : 0); // Feedback from timer source for speaker beep

	LOGPPI("PPI Port C read: %02x\n", data);

	return data;
}

void epc_state::ppi_portb_w(uint8_t data)
{
	LOGPPI("PPI Port B write: %02x\n", data);
	LOGPPI(" PB0 - Enable beeper             : %d\n", (data & 0x01)  ? 1 : 0);
	LOGPPI(" PB1 - Beeper data               : %d\n", (data & 0x02)  ? 1 : 0);
	LOGPPI(" PB2 - Unused                    : %d\n", (data & 0x04)  ? 1 : 0);
	LOGPPI(" PB3 - Port C dip switch select  : %d\n", (data & 0x08)  ? 1 : 0);
	LOGPPI(" PB4 - RAM parity enable         : %d\n", (data & 0x10)  ? 1 : 0);
	LOGPPI(" PB5 - ISA error checking enable : %d\n", (data & 0x20)  ? 1 : 0);
	LOGPPI(" PB6 - Reset keyboard            : %d\n", (data & 0x40)  ? 1 : 0);
	LOGPPI(" PB7 - Reset keyboard interrupt  : %d\n", (data & 0x80)  ? 1 : 0);

	uint8_t changed = m_ppi_portb ^ data;

	m_ppi_portb = data;

	if (changed & 0x40)
	{
		if ((m_io_s21->read() & 0x01) == 0x00)
		{
			if (m_ppi_portb & 0x40)
			{
				LOGKBD("PB6 set, clearing Keyboard RESET\n");
				m_keyboard->rst_line_w(CLEAR_LINE);
			}
			else
			{
				LOGKBD("PB6 cleared, asserting Keyboard RESET\n");
				m_keyboard->rst_line_w(ASSERT_LINE);
			}
		}
	}

	if (changed & m_ppi_portb & 0x80)
	{
		LOGIRQ("PB7 set, clearing IRQ1 and releasing HOLD\n");
		m_pic8259->ir1_w(CLEAR_LINE);
		m_keyboard->hold_w(ASSERT_LINE);
	}
}

void epc_state::int_w(int state)
{
	if (m_int != state)
	{
		LOGIRQ("int_w: %d\n", state);
		m_int = state;
		m_maincpu->set_input_line(0, m_int);
	}
}

static void epc_isa8_cards(device_slot_interface &device)
{
	device.option_add("epc_mda", ISA8_EPC_MDA);
	device.option_add("ega", ISA8_EGA);
	device.option_add("epc_twib", ISA8_EIS_TWIB);
	// device.option_add("epc_hdc1065", ISA8_EPC_HDC1065);
	// device.option_add("epc_mb1080", ISA8_EPC_MB1080);
}

static void epc_sd_floppies(device_slot_interface &device)
{
	device.option_add("525sd", FLOPPY_525_SD);
}

void epc_state::epc(machine_config &config)
{
	config.set_default_layout(layout_epc);

	// CPU
	I8088(config, m_maincpu, XTAL(14'318'181) / 3.0); // TWE crystal marked X1 verified divided through a 82874
	m_maincpu->set_addrmap(AS_PROGRAM, &epc_state::epc_map);
	m_maincpu->set_addrmap(AS_IO, &epc_state::epc_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259", FUNC(pic8259_device::inta_cb));
	m_maincpu->esc_opcode_handler().set("fpu8087", FUNC(i8087_device::insn_w));
	m_maincpu->esc_data_handler().set("fpu8087", FUNC(i8087_device::addr_w));

	i8087_device &i8087(I8087(config, "fpu8087", XTAL(14'318'181) / 3.0));
	i8087.set_space_88(m_maincpu, AS_PROGRAM);
	i8087.irq().set([this](bool state)
	{
		LOGFPU("8087 INT: %d\n", state);
		m_8087_int = state;
		update_nmi();
	});
	i8087.busy().set_inputline(m_maincpu, INPUT_LINE_TEST);

	// DMA
	AM9517A(config, m_dma8237a, XTAL(14'318'181) / 3.0); // TWE crystal marked X1 verified
	m_dma8237a->out_hreq_callback().set(FUNC(epc_state::epc_dma_hrq_changed));
	m_dma8237a->out_eop_callback().set(FUNC(epc_state::dma_tc_w));
	m_dma8237a->in_memr_callback().set(FUNC(epc_state::epc_dma_read_byte));
	m_dma8237a->out_memw_callback().set(FUNC(epc_state::epc_dma_write_byte));
	m_dma8237a->in_ior_callback<1>().set(FUNC(epc_state::epc_dma8237_io_r<1>));
	m_dma8237a->in_ior_callback<2>().set(FUNC(epc_state::epc_dma8237_io_r<2>));
	m_dma8237a->in_ior_callback<3>().set(FUNC(epc_state::epc_dma8237_io_r<3>));
	m_dma8237a->out_iow_callback<0>().set(FUNC(epc_state::epc_dma8237_io_w<0>));
	m_dma8237a->out_iow_callback<1>().set(FUNC(epc_state::epc_dma8237_io_w<1>));
	m_dma8237a->out_iow_callback<2>().set(FUNC(epc_state::epc_dma8237_io_w<2>));
	m_dma8237a->out_iow_callback<3>().set(FUNC(epc_state::epc_dma8237_io_w<3>));
	m_dma8237a->out_dack_callback<0>().set(FUNC(epc_state::epc_dack_w<0>));
	m_dma8237a->out_dack_callback<1>().set(FUNC(epc_state::epc_dack_w<1>));
	m_dma8237a->out_dack_callback<2>().set(FUNC(epc_state::epc_dack_w<2>));
	m_dma8237a->out_dack_callback<3>().set(FUNC(epc_state::epc_dack_w<3>));

	// TTL-level serial keyboard callback
	EISPC_KB(config, m_keyboard);
	m_keyboard->txd_cb().set([this](bool state)
	{
		LOGBITS("KBD->EPC: %d\n", state);
		m_kbd8251->write_rxd(state);
	});
	m_keyboard->caps_cb().set(  [this](bool state){ m_leds[0] = state; });
	m_keyboard->num_cb().set(   [this](bool state){ m_leds[1] = state; });
	m_keyboard->scroll_cb().set([this](bool state){ m_leds[2] = state; });

	// Keyboard USART
	I8251( config, m_kbd8251, XTAL(14'318'181) / 6.0 ); // TWE crystal marked X1 verified divided through a 82874

	m_kbd8251->txd_handler().set([this](bool state)
	{
		if (m_txd != state)
		{
			LOGBITS("EPC->KBD: %d\n", state);
			m_txd = state;
			m_keyboard->rxd_w(m_txd);
		}
	});

	m_kbd8251->rxrdy_handler().set([this](bool state)
	{
		m_rxrdy = state;
		LOGKBD("KBD RxRdy: %d HOLD: %d\n", m_rxrdy ? 1 : 0, m_rxrdy ? 0 : 1);
		m_keyboard->hold_w(!m_rxrdy);
		if (m_rxrdy)
		{
			LOGIRQ("RxRdy set, asserting IRQ1\n");
			m_pic8259->ir1_w(ASSERT_LINE); // Cleared by setting PB7
		}
	});
	m_kbd8251->dtr_handler().set([this](bool state) // Controls RCLK for INS8250, either 19.2KHz or INS8250 BAUDOUT
	{
		LOGCOM("KBD DTR: %d\n", state ? 1 : 0);
		m_8251dtr_state = state;
	});

	// Interrupt Controller
	PIC8259(config, m_pic8259);
	m_pic8259->out_int_callback().set(FUNC(epc_state::int_w));

	// Parallel port
	I8255A(config, m_ppi8255);
	m_ppi8255->out_pa_callback().set([this] (uint8_t data) { LOGPPI("PPI: write %02x to unused Port A\n", data); } ); // Port A is not used
	m_ppi8255->out_pb_callback().set(FUNC(epc_state::ppi_portb_w));
	m_ppi8255->in_pc_callback().set(FUNC(epc_state::ppi_portc_r));

	// system board Parallel port
	PC_LPT(config, m_lpt);
	m_lpt->irq_handler().set([this](int state)
	{   // Jumper field J10 decides what IRQ to pull
		if ((m_io_j10->read() & 0x03) == 0x01) { LOGIRQ("LPT IRQ2: %d\n", state); m_pic8259->ir2_w(state); }
		if ((m_io_j10->read() & 0x0c) == 0x04) { LOGIRQ("LPT IRQ3: %d\n", state); m_pic8259->ir3_w(state); }
		if ((m_io_j10->read() & 0x30) == 0x10) { LOGIRQ("LPT IRQ4: %d\n", state); m_pic8259->ir4_w(state); }
		if ((m_io_j10->read() & 0xc0) == 0x40) { LOGIRQ("LPT IRQ7: %d\n", state); m_pic8259->ir7_w(state); } // Factory setting
	});

	// Timer
	PIT8253(config, m_pit8253);
	m_pit8253->set_clk<0>((XTAL(14'318'181) / 3.0) / 2.0 );
	m_pit8253->set_clk<1>((XTAL(14'318'181) / 3.0) / 2.0 );
	m_pit8253->set_clk<2>((XTAL(14'318'181) / 3.0) / 2.0 );
	m_pit8253->out_handler<0>().set(m_pic8259, FUNC(pic8259_device::ir0_w));
	m_pit8253->out_handler<1>().set(FUNC(epc_state::dreq0_ck_w));
	m_pit8253->out_handler<2>().set(FUNC(epc_state::speaker_ck_w));

	// Speaker
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);

	// ISA bus
	ISA8(config, m_isabus,  XTAL(14'318'181) / 3.0); // TEW crystal marked X1 verified
	m_isabus->set_memspace(m_maincpu, AS_PROGRAM);
	m_isabus->set_iospace(m_maincpu, AS_IO);
	m_isabus->irq2_callback().set(m_pic8259, FUNC(pic8259_device::ir2_w)); // Reserved in service manual
	m_isabus->irq3_callback().set(m_pic8259, FUNC(pic8259_device::ir3_w));
	m_isabus->irq4_callback().set(m_pic8259, FUNC(pic8259_device::ir4_w));
	m_isabus->irq5_callback().set(m_pic8259, FUNC(pic8259_device::ir5_w));
	m_isabus->irq6_callback().set(m_pic8259, FUNC(pic8259_device::ir6_w));
	m_isabus->irq7_callback().set(m_pic8259, FUNC(pic8259_device::ir7_w));
	m_isabus->drq1_callback().set(m_dma8237a, FUNC(am9517a_device::dreq1_w));
	m_isabus->drq2_callback().set(m_dma8237a, FUNC(am9517a_device::dreq2_w));
	m_isabus->drq3_callback().set(m_dma8237a, FUNC(am9517a_device::dreq3_w));
	m_isabus->iochck_callback().set([this] (int state)
	{
		if (m_nmi_enabled && !state && 0)
		{
			LOGNMI("IOCHCK: NMI Requested\n");
			update_nmi();
		}
	});

	ISA8_SLOT(config, "isa1", 0, m_isabus, epc_isa8_cards, "epc_mda", false);
	ISA8_SLOT(config, "isa2", 0, m_isabus, epc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa3", 0, m_isabus, epc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa4", 0, m_isabus, epc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa5", 0, m_isabus, epc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa6", 0, m_isabus, epc_isa8_cards, nullptr, false);

	// System board has 128kB memory with parity, expansion can be achieved through the
	// 128kB Memory Expansion Board 1090 and/or the 128kB Multifunction Board MB1080-001
	// and/or the 384kB MB1080-002. The MB1080 DRAM might need to be dynamically added as
	// base address and also a video memory hole is configurable.
	RAM(config, m_ram).set_default_size("128K").set_extra_options("256K, 384K, 512K, 640K");

	// FDC
	I8272A(config, m_fdc, XTAL(16'000'000) / 2, false); // TEW crystal marked X3 verified
	m_fdc->intrq_wr_callback().set([this] (int state){ m_fdc_irq = state; check_fdc_irq(); });
	m_fdc->drq_wr_callback().set([this] (int state){ m_fdc_drq = state; check_fdc_drq(); });
	FLOPPY_CONNECTOR(config, m_floppy_connectors[0], epc_sd_floppies, "525sd", floppy_image_device::default_pc_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy_connectors[1], epc_sd_floppies, "525sd", floppy_image_device::default_pc_floppy_formats);
	//SOFTWARE_LIST(config, "epc_flop_list").set_original("epc_flop");

	// system board UART
	INS8250(config, m_uart, XTAL(18'432'000) / 10); // TEW crystal marked X2 verified. TODO: Let 8051 DTR control RCLK (see above)
	m_uart->out_tx_callback().set("com1", FUNC(rs232_port_device::write_txd));
	m_uart->out_dtr_callback().set("com1", FUNC(rs232_port_device::write_dtr));
	m_uart->out_rts_callback().set("com1", FUNC(rs232_port_device::write_rts));
	m_uart->out_int_callback().set([this](int state)
	{   // Jumper field J10 decides what IRQ to pull
		if ((m_io_j10->read() & 0x03) == 0x02) { LOGCOM("UART IRQ2: %d\n", state); m_pic8259->ir2_w(state); }
		if ((m_io_j10->read() & 0x0c) == 0x08) { LOGCOM("UART IRQ3: %d\n", state); m_pic8259->ir3_w(state); }
		if ((m_io_j10->read() & 0x30) == 0x20) { LOGCOM("UART IRQ4: %d\n", state); m_pic8259->ir4_w(state); } // Factory setting
		if ((m_io_j10->read() & 0xc0) == 0x80) { LOGCOM("UART IRQ7: %d\n", state); m_pic8259->ir7_w(state); }
	});
	// m_uart->out_baudout_callback().set([this](int state){ if (m_8251dtr_state) m_uart->rclk_w(state); }); // TODO: Fix INS8250 BAUDOUT pin support

	rs232_port_device &rs232(RS232_PORT(config, "com1", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_uart, FUNC(ins8250_uart_device::rx_w));
	rs232.dcd_handler().set(m_uart, FUNC(ins8250_uart_device::dcd_w));
	rs232.dsr_handler().set(m_uart, FUNC(ins8250_uart_device::dsr_w));
	rs232.ri_handler().set(m_uart, FUNC(ins8250_uart_device::ri_w));
	rs232.cts_handler().set(m_uart, FUNC(ins8250_uart_device::cts_w));
}

void epc_state::update_nmi()
{
	if (m_nmi_enabled &&
		((m_8087_int && (m_io_dsw->read() & 0x02)) || // FPU int only if FPU is enabled by DSW2
		 (m_parer_int != 0) || // Parity error is always false as it is an emulator, at least for now
		 (m_iochck_int != 0))) // Same goes for ISA board errors
	{
		LOGNMI(" NMI Asserted\n");
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
	}
	else
	{
		LOGNMI(" NMI Cleared\n");
		m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
	}
}

void epc_state::epc_dma_hrq_changed(int state)
{
	LOGDMA("epc_dma_hrq_changed %d\n", state);

	m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);

	/* Assert HLDA */
	m_dma8237a->hack_w(state);
}


uint8_t epc_state::epc_dma_read_byte(offs_t offset)
{
	if ((m_dma_active & 0x0f) == 0)
	{
		LOGDMA("epc_dma_read_byte failed\n");
		return 0xff;
	}

	const int seg = (BIT(m_dma_active, 2) ? 0 : 2) | (BIT(m_dma_active, 3) ? 0 : 1);
	return m_maincpu->space(AS_PROGRAM).read_byte(offset | u32(m_dma_segment[seg]) << 16);
}

void epc_state::epc_dma_write_byte(offs_t offset, uint8_t data)
{
	if ((m_dma_active & 0x0f) == 0)
	{
		LOGDMA("epc_dma_write_byte failed\n");
		return;
	}

	const int seg = (BIT(m_dma_active, 2) ? 0 : 2) | (BIT(m_dma_active, 3) ? 0 : 1);
	m_maincpu->space(AS_PROGRAM).write_byte(offset | u32(m_dma_segment[seg]) << 16, data);
}


static INPUT_PORTS_START( epc_ports )
	PORT_START("DSW")
	PORT_DIPNAME( 0x01, 0x01, "Not used")
	PORT_DIPSETTING(    0x00, "ON - Don't use")
	PORT_DIPSETTING(    0x01, "OFF - Factory Setting")
	PORT_DIPNAME( 0x02, 0x00, "8087 installed")
	PORT_DIPSETTING(    0x00, DEF_STR(No) )
	PORT_DIPSETTING(    0x02, DEF_STR(Yes) )
	PORT_DIPNAME( 0x04, 0x04, "Not used")
	PORT_DIPSETTING(    0x00, "ON - Don't care")
	PORT_DIPSETTING(    0x04, "OFF - Factory Setting")
	PORT_DIPNAME( 0x08, 0x00, "Not used")
	PORT_DIPSETTING(    0x00, "ON - Factory Setting")
	PORT_DIPSETTING(    0x08, "OFF - Don't use")
	PORT_DIPNAME( 0x30, 0x30, "Main monitor")
	PORT_DIPSETTING(    0x00, "Not used" )
	PORT_DIPSETTING(    0x10, "Optional 1020 color" )
	PORT_DIPSETTING(    0x20, "Not used" )
	PORT_DIPSETTING(    0x30, "3111 HR Monochrome" )
	PORT_DIPNAME( 0xc0, 0x40, "Number of floppy drives")
	PORT_DIPSETTING(    0x00, "1" )
	PORT_DIPSETTING(    0x40, "2" )
	PORT_DIPSETTING(    0x80, "Not used" )
	PORT_DIPSETTING(    0xc0, "Not used" )

	PORT_START("J10") // Jumper area, field 0=no jumper 1=LPT 2=COM 3=n/a
	PORT_DIPNAME(0x03, 0x00, "IRQ2")
	PORT_DIPSETTING(0x00, "no jumper")
	PORT_DIPSETTING(0x01, "LPT")
	PORT_DIPSETTING(0x02, "COM")
	PORT_DIPNAME(0x0c, 0x00, "IRQ3")
	PORT_DIPSETTING(0x00, "no jumper")
	PORT_DIPSETTING(0x04, "LPT")
	PORT_DIPSETTING(0x08, "COM")
	PORT_DIPNAME(0x30, 0x20, "IRQ4")
	PORT_DIPSETTING(0x00, "no jumper")
	PORT_DIPSETTING(0x10, "LPT")
	PORT_DIPSETTING(0x20, "COM")
	PORT_DIPNAME(0xc0, 0x40, "IRQ7")
	PORT_DIPSETTING(0x00, "no jumper")
	PORT_DIPSETTING(0x40, "LPT")
	PORT_DIPSETTING(0x80, "COM")

	PORT_START("S21") // Jumper 0=PB6 reset, 1=KBCLK 4.8kHz - what to send to keyboard pin 3
	PORT_DIPNAME(0x01, 0x00, "Keyboard Clock/Reset pin")
	PORT_DIPSETTING(0x00, "PB6")
	PORT_DIPSETTING(0x01, "4.8kHz") // This setting is apparantly for another keyboard, currently unknown
INPUT_PORTS_END

ROM_START( epc )
	ROM_REGION(0x10000,"bios", 0)
	ROM_DEFAULT_BIOS("p860110")
	ROM_SYSTEM_BIOS(0, "p840705", "P840705")
	ROMX_LOAD("ericsson_8088.bin", 0xe000, 0x2000, CRC(3953c38d) SHA1(2bfc1f1d11d0da5664c3114994fc7aa3d6dd010d), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "p860110", "P860110")
	ROMX_LOAD("epcbios1.bin",  0xe000, 0x02000, CRC(79a83706) SHA1(33528c46a24d7f65ef5a860fbed05afcf797fc55), ROM_BIOS(1))
	ROMX_LOAD("epcbios2.bin",  0xa000, 0x02000, CRC(3ca764ca) SHA1(02232fedef22d31a641f4b65933b9e269afce19e), ROM_BIOS(1))
	ROMX_LOAD("epcbios3.bin",  0xc000, 0x02000, CRC(70483280) SHA1(b44b09da94d77b0269fc48f07d130b2d74c4bb8f), ROM_BIOS(1))
ROM_END

} // anonymous namespace


COMP( 1985, epc,     0,      0,      epc,     epc_ports, epc_state, init_epc,    "Ericsson Information System",     "Ericsson PC" ,          0)
//COMP( 1985, eppc,   ibm5150, 0,  pccga,         pccga,  pc_state, empty_init,    "Ericsson Information System",     "Ericsson Portable PC",  MACHINE_NOT_WORKING )



elan_ep3a19a.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

// The TV Board Game units have "Programmed by E.I. HK Development LTD." in the graphics

// To perform the hidden ROM check do Up + Button A while booting up, then on the black screen Down + Button B.
// This is probably impossible on the single button units using real hardware as the 'B' input isn't connected
// Currently these checksums fail in MAME due to the interrupt hack mapping over ROM, if you remove that hack they pass

#include "emu.h"
#include "elan_eu3a05_a.h"
#include "elan_eu3a05gpio.h"
#include "elan_ep3a19asys.h"
#include "elan_eu3a05vid.h"

#include "cpu/m6502/m6502.h"
#include "machine/bankdev.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class elan_ep3a19a_state : public driver_device
{
public:
	elan_ep3a19a_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_sys(*this, "sys"),
		m_gpio(*this, "gpio"),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen"),
		m_ram(*this, "ram"),
		m_sound(*this, "eu3a05sound"),
		m_vid(*this, "vid"),
		m_bank(*this, "bank"),
		m_gfxdecode(*this, "gfxdecode"),
		m_palette(*this, "palette")
	{ }

	void elan_ep3a19a(machine_config &config);

	void init_tvbg();

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<elan_ep3a19asys_device> m_sys;
	required_device<elan_eu3a05gpio_device> m_gpio;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;

private:
	// screen updates
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	INTERRUPT_GEN_MEMBER(interrupt);

	// for callback
	uint8_t read_full_space(offs_t offset);

	void elan_ep3a19a_bank_map(address_map &map) ATTR_COLD;
	void elan_ep3a19a_map(address_map &map) ATTR_COLD;

	virtual void video_start() override ATTR_COLD;

	required_shared_ptr<uint8_t> m_ram;
	required_device<elan_eu3a05_sound_device> m_sound;
	required_device<elan_eu3a05vid_device> m_vid;
	required_device<address_map_bank_device> m_bank;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<palette_device> m_palette;

	//void sound_end0(int state) { m_sys->generate_custom_interrupt(2); }
	//void sound_end1(int state) { m_sys->generate_custom_interrupt(3); }
	//void sound_end2(int state) { m_sys->generate_custom_interrupt(4); }
	//void sound_end3(int state) { m_sys->generate_custom_interrupt(5); }
	//void sound_end4(int state) { m_sys->generate_custom_interrupt(6); }
	//void sound_end5(int state) { m_sys->generate_custom_interrupt(7); }

	uint8_t nmi_vector_r(offs_t offset)
	{
		return 0xffd4 >> (offset * 8);
	}

};

void elan_ep3a19a_state::video_start()
{
}

uint32_t elan_ep3a19a_state::screen_update(screen_device& screen, bitmap_ind16& bitmap, const rectangle& cliprect)
{
	return m_vid->screen_update(screen, bitmap, cliprect);
}

// sound callback
uint8_t elan_ep3a19a_state::read_full_space(offs_t offset)
{
	address_space& fullbankspace = m_bank->space(AS_PROGRAM);
	return fullbankspace.read_byte(offset);
}

void elan_ep3a19a_state::elan_ep3a19a_map(address_map &map)
{
	// can the addresses move around?
	map(0x0000, 0x3fff).ram().share("ram");
	map(0x4800, 0x49ff).rw(m_vid, FUNC(elan_eu3a05commonvid_device::palette_r), FUNC(elan_eu3a05commonvid_device::palette_w));

	map(0x5000, 0x5014).m(m_sys, FUNC(elan_ep3a19asys_device::map)); // including DMA controller
	map(0x5020, 0x503f).m(m_vid, FUNC(elan_eu3a05vid_device::map));

	// 504x GPIO area?
	map(0x5040, 0x5046).rw(m_gpio, FUNC(elan_eu3a05gpio_device::gpio_r), FUNC(elan_eu3a05gpio_device::gpio_w));
	// 5047
	//map(0x5048, 0x504a).w(m_gpio, FUNC(elan_eu3a05gpio_device::gpio_unk_w));

	// 506x unknown
	//map(0x5060, 0x506d).ram(); // read/written by tetris (ADC?)

	// 508x sound
	map(0x5080, 0x50bf).m(m_sound, FUNC(elan_eu3a05_sound_device::map));

	//map(0x5000, 0x50ff).ram();
	map(0x6000, 0xdfff).m(m_bank, FUNC(address_map_bank_device::amap8));

	map(0xe000, 0xffff).rom().region("maincpu", 0x0000);
	// not sure how these work, might be a modified 6502 core instead.
	//map(0xfffa, 0xfffb).r(m_sys, FUNC(elan_eu3a05commonsys_device::nmi_vector_r)); // custom vectors handled with NMI for now
	map(0xfffa, 0xfffb).r(FUNC(elan_ep3a19a_state::nmi_vector_r)); // custom vectors handled with NMI for now

	//map(0xfffe, 0xffff).r(m_sys, FUNC(elan_eu3a05commonsys_device::irq_vector_r));  // allow normal IRQ for brk
}

void elan_ep3a19a_state::elan_ep3a19a_bank_map(address_map &map)
{
	map(0x000000, 0x3fffff).mirror(0xc00000).noprw();
	map(0x000000, 0x3fffff).mirror(0xc00000).rom().region("maincpu", 0);
}


static INPUT_PORTS_START( tvbg_1button )
	PORT_START("IN0")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN1")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) // the 6-in-1 units have a single button marked with both A and B (unless it depends which side of the button you press?)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( tvbg_2button )
	PORT_INCLUDE( tvbg_1button )

	PORT_MODIFY("IN2")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) // Boggle uses 2 buttons for gameplay, other units do read this to enter secret test mode, but none of the games need it?
INPUT_PORTS_END


void elan_ep3a19a_state::machine_start()
{
}

void elan_ep3a19a_state::machine_reset()
{
	m_maincpu->set_state_int(M6502_S, 0x1ff);
}

static const gfx_layout helper_4bpp_8_layout =
{
	8,1,
	RGN_FRAC(1,1),
	4,
	{ 0,1,2,3 },
	{ STEP8(0,4) },
	{ 0 },
	8 * 4
};

static const gfx_layout helper_8bpp_8_layout =
{
	8,1,
	RGN_FRAC(1,1),
	8,
	{ 0,1,2,3,4,5,6,7 },
	{ STEP8(0,8) },
	{ 0 },
	8 * 8
};

// these are fake just to make looking at the texture pages easier
static const uint32_t texlayout_xoffset_8bpp[256] = { STEP256(0,8) };
static const uint32_t texlayout_yoffset_8bpp[256] = { STEP256(0,256*8) };
static const gfx_layout texture_helper_8bpp_layout =
{
	256, 256,
	RGN_FRAC(1,1),
	8,
	{ 0,1,2,3,4,5,6,7 },
	EXTENDED_XOFFS,
	EXTENDED_YOFFS,
	256*256*8,
	texlayout_xoffset_8bpp,
	texlayout_yoffset_8bpp
};

static const uint32_t texlayout_xoffset_4bpp[256] = { STEP256(0,4) };
static const uint32_t texlayout_yoffset_4bpp[256] = { STEP256(0,256*4) };
static const gfx_layout texture_helper_4bpp_layout =
{
	256, 256,
	RGN_FRAC(1,1),
	4,
	{ 0,1,2,3 },
	EXTENDED_XOFFS,
	EXTENDED_YOFFS,
	256*256*4,
	texlayout_xoffset_4bpp,
	texlayout_yoffset_4bpp
};

static GFXDECODE_START( gfx_elan_eu3a05_fake )
	GFXDECODE_ENTRY( "maincpu", 0, helper_4bpp_8_layout,  0x0, 1  )
	GFXDECODE_ENTRY( "maincpu", 0, texture_helper_4bpp_layout,  0x0, 1  )
	GFXDECODE_ENTRY( "maincpu", 0, helper_8bpp_8_layout,  0x0, 1  )
	GFXDECODE_ENTRY( "maincpu", 0, texture_helper_8bpp_layout,  0x0, 1  )
GFXDECODE_END

INTERRUPT_GEN_MEMBER(elan_ep3a19a_state::interrupt)
{
	//m_sys->generate_custom_interrupt(9);
	m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}

void elan_ep3a19a_state::elan_ep3a19a(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, XTAL(21'477'272)/8);
	m_maincpu->set_addrmap(AS_PROGRAM, &elan_ep3a19a_state::elan_ep3a19a_map);
	m_maincpu->set_vblank_int("screen", FUNC(elan_ep3a19a_state::interrupt));

	ADDRESS_MAP_BANK(config, "bank").set_map(&elan_ep3a19a_state::elan_ep3a19a_bank_map).set_options(ENDIANNESS_LITTLE, 8, 24, 0x8000);

	PALETTE(config, m_palette).set_entries(256);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	m_screen->set_screen_update(FUNC(elan_ep3a19a_state::screen_update));
	m_screen->set_size(32*8, 32*8);
	m_screen->set_visarea(0*8, 32*8-1, 0*8, 28*8-1);
	m_screen->set_palette(m_palette);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_elan_eu3a05_fake);

	ELAN_EU3A05_GPIO(config, m_gpio, 0);
	m_gpio->read_0_callback().set_ioport("IN0");
	m_gpio->read_1_callback().set_ioport("IN1");
	m_gpio->read_2_callback().set_ioport("IN2");

	ELAN_EP3A19A_SYS(config, m_sys, 0);
	m_sys->set_cpu("maincpu");
	m_sys->set_addrbank("bank");

	ELAN_EU3A05_VID(config, m_vid, 0);
	m_vid->set_cpu("maincpu");
	m_vid->set_addrbank("bank");
	m_vid->set_palette("palette");
	m_vid->set_entries(256);
	m_vid->set_is_pvmilfin();
	m_vid->set_use_spritepages();
	m_vid->set_force_basic_scroll();

	/* sound hardware */
	SPEAKER(config, "mono").front_center();


	ELAN_EU3A05_SOUND(config, m_sound, 8000);
	m_sound->space_read_callback().set(FUNC(elan_ep3a19a_state::read_full_space));
	m_sound->add_route(ALL_OUTPUTS, "mono", 1.0);

	/*
	m_sound->sound_end_cb<0>().set(FUNC(elan_ep3a19a_state::sound_end0));
	m_sound->sound_end_cb<1>().set(FUNC(elan_ep3a19a_state::sound_end1));
	m_sound->sound_end_cb<2>().set(FUNC(elan_ep3a19a_state::sound_end2));
	m_sound->sound_end_cb<3>().set(FUNC(elan_ep3a19a_state::sound_end3));
	m_sound->sound_end_cb<4>().set(FUNC(elan_ep3a19a_state::sound_end4));
	m_sound->sound_end_cb<5>().set(FUNC(elan_ep3a19a_state::sound_end5));
	*/
}

ROM_START( tvbg6a )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "candyland_hhh_silly6.bin", 0x00000, 0x200000, CRC(8b16d725) SHA1(06af509d03df0e5a2ca502743797af9f4a5dc6f1) )
	ROM_RELOAD(0x200000,0x200000)
ROM_END

ROM_START( tvbg6b )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "bship_simon_mousetrap.bin", 0x00000, 0x200000, CRC(b0627a98) SHA1(6157e26916bb415037a4d122d3075cbfb8e61dcf) )
	ROM_RELOAD(0x200000,0x200000)
ROM_END

ROM_START( tvbg3a )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hhhssp.bin", 0x00000, 0x100000, CRC(7e23a5a0) SHA1(2cd0f7572df30d2565b64fa0936715f71312ab1a) )
	ROM_RELOAD(0x100000,0x100000)
	ROM_RELOAD(0x200000,0x100000)
	ROM_RELOAD(0x300000,0x100000)
ROM_END

ROM_START( tvbg3b )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "simonbship.bin", 0x00000, 0x100000, CRC(9b10a87a) SHA1(f2022ac07468d911cfb3d32887d6e59e60d48d51) )
	ROM_RELOAD(0x100000,0x100000)
	ROM_RELOAD(0x200000,0x100000)
	ROM_RELOAD(0x300000,0x100000)
ROM_END

ROM_START( tvbg3c )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "boggle_connect4.bin", 0x00000, 0x100000, CRC(c2374eea) SHA1(c6971cb5108828bc72fd1cf7edeb53915d196db7) )
	ROM_RELOAD(0x100000,0x100000)
	ROM_RELOAD(0x200000,0x100000)
	ROM_RELOAD(0x300000,0x100000)
ROM_END

void elan_ep3a19a_state::init_tvbg()
{
	// is this swapping internal to the ep3a19a type ELAN, or external; ROM glob had standard TSOP pinout pads that were used for dumping.
	uint8_t* ROM = memregion("maincpu")->base();
	for (int i = 0; i < 0x400000; i++)
	{
		ROM[i] = bitswap<8>(ROM[i], 6, 5, 7, 0, 2, 3, 1, 4);
	}
}

} // anonymous namespace


CONS( 2007, tvbg6a, 0, 0, elan_ep3a19a, tvbg_1button, elan_ep3a19a_state, init_tvbg, "NSI International / Mammoth Toys (Licensed by Hasbro)", "TV Board Games 6-in-1: Silly 6 Pins, Candy Land, Hungry Hungry Hippos, Match 'em, Mixin' Pics, Checkers", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // https://www.youtube.com/watch?v=zajzQo47YYA
CONS( 2007, tvbg6b, 0, 0, elan_ep3a19a, tvbg_1button, elan_ep3a19a_state, init_tvbg, "NSI International / Mammoth Toys (Licensed by Hasbro)", "TV Board Games 6-in-1: Simon, Battleship, Mouse Trap, Checkers, Link-a-Line, Roll Over", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // https://www.youtube.com/watch?v=JbrR67kY8MI

CONS( 2007, tvbg3a, 0, 0, elan_ep3a19a, tvbg_2button, elan_ep3a19a_state, init_tvbg, "NSI International / Mammoth Toys (Licensed by Hasbro)", "TV Board Games 3-in-1: Silly 6 Pins, Hungry Hungry Hippos, Match 'em", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 2007, tvbg3b, 0, 0, elan_ep3a19a, tvbg_2button, elan_ep3a19a_state, init_tvbg, "NSI International / Mammoth Toys (Licensed by Hasbro)", "TV Board Games 3-in-1: Simon, Battleship, Checkers", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // https://www.youtube.com/watch?v=Q7nwKJfVavU
CONS( 2007, tvbg3c, 0, 0, elan_ep3a19a, tvbg_2button, elan_ep3a19a_state, init_tvbg, "NSI International / Mammoth Toys (Licensed by Hasbro)", "TV Board Games 3-in-1: Boggle, Connect 4, Roll Over", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // https://www.youtube.com/watch?v=SoKKIKSDGhY

// The back of the Silly 6 Pins 3-in-1 packaging suggests a Monopoly TV Board Game device was planned, but this does not appear to have been released.



elan_eu3a05.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood, R.Belmont

/*
    Radica Games 6502 based 'TV Game' hardware

    These use a 6502 derived CPU under a glob
    The CPU die is marked 'ELAN EU3A05'

    There is a second glob surrounded by TSOP48 pads
    this contains the ROM

    Space Invaders uses a 3rd glob marked
    AMIC (C) (M) 1998-1 AM3122A
    this is presumably for the bitmap layer on Qix

    --
    Known games on this hardware

    Tetris
    Space Invaders
    ABL Air-Blaster Joystick

    ---
    XaviX plug and play units almost always have a XaviX logo on the external packaging
    while the ones for this driver (and SunPlus etc.) don't seem to have any specific
    markings.

    Notes:

    Tetris - RAM 0xa0 and 0xa1 contain the ACD0 and AD1 values and player 2 controls if
    between certain values? probably read via serial (or ADC abuse?)

    Internal Test Menus:

    Tetris - hold P1 Down + P1 Anticlockwise (Button 2) on boot
    Space Invaders - hold P1 Down + P1 Button 1 on boot
    ABL Air-Blaster - none?

    -----------------------------------------------------
    Flaws (NOT emulation bugs, happen on hardware):
    -----------------------------------------------------

    rad_sinv:

    In QIX the sprites lag behind the line drawing, so you see the line infront of your player until you stop moving

    In Space Invaders the UFO can sometimes glitch for a frame when appearing, and wraps around at the edges
      (even if the hardware supports having higher priority tiles to prevent this, as used by Lunar Rescue, it isn't
       used here)

    Colony 7 has a typo in the instructions

    The fake 'colour band' effect does not apply to the thruster (and several other elements) in Lunar Rescue

    Enemies in Phoenix are rendered above the score panel

    The 200pt right facing bird on the Phoenix score table is corrupt

    Space Invaders seems to be using a darker than expected palette, there are lighter colours in the palette but
    they don't seem to be used.  It's difficult to judge from hardware videos, although it definitely isn't as
    white as the menu, so this might also be a non-bug. (Uncertain - to check)

    -------------------------

    airblasjs:

    This game is very buggy.

    The 3D stages are prone to softlocking when the refuel jet is meant to appear.

    2D stages will zap you of your lives and then continues one by one if you die on a boss meaning if you have
    2 continues left you'll be offered the continue screen twice while it drains you of your lives before
    actually presenting you with the Game Over screen.  The manual claims you can't continue on a boss however
    this isn't true for the 3D stages, where the continue feature works as expected.  Either way, this is a very
    crude way of implementing a 'no continue' feature on bosses if it isn't simply a bug in the game code that
    was explained away as a feature.

    Sprites clip on / off the top of the screen in parts - if you move your the player helipcopter to the top
    of the screen the top 8 pixels clip off too (not currently happening in MAME, probably need to take out
    sprite wrapping on y)

    Sprites wrap around on X too, if you move to the left edge you can see your shadow on the right etc.

    Sound sometimes stops working properly / shot changes for no reason.

    There's no indication of damage most of the time on bosses, some parts won't take damage until other parts
    have been destroyed, not always obvious.

    Very heavy sprite flicker (not emulated)

    Very heavy slowdown (MAME speed is approximate)

*/

/*

Buzztime Trivia notes from Tahg (addresses based on base ROM, not cartridges)

300 Cursor Left
301 Cursor Top
302 Cursor Right
303 Cursor Bottom

3E5 Sent Command
3E6 Watchdog Counter
3E9 Active Player
3EA Sent Player Id/Incoming Byte
3EB Sent Player Button/Loop Counter

0-A Name
E-F Score
44x Player 1 Data
...
4Fx Player 12 Data

509 Cursor index
515 Name on Enter name screen

These read a byte, (aaabbbcc) MSB first on IO 5041. In general:
Set 5043 bit 0 high.
Repeat 8 times
  Wait for 5041 bit 0 to go high
  Read 5041 bit 1
Set 5043 bit 0 low

6B29  ReadCommandA  Exit on c=2 or c=1,b=7,a=7  Watchdog resets counters and reads port again
6BE8  ReadCommandB  Exit on c=2 or c=1,b=7,a=7  Watchdog exits with b=FF if 3E7 nonzero
6CB4  ReadCommandC  Exit on c=2                 Watchdog exits with b=FF

  Address         Publics by Value

 00000000:00000000       byte_0
 00000000:0000050A       index
 00000000:00006011       LoadTileSet
 00000000:0000605B       LoadPalette
 00000000:00006090       SetTileBase
 00000000:000060D7       DisableSprites
 00000000:000060F0       SetSpriteBase
 00000000:0000648F       Play0
 00000000:00006494       Play1
 00000000:00006499       Play2
 00000000:0000649E       Play3
 00000000:000064A3       Play4
 00000000:000064A8       Play5
 00000000:000064AD       Play6
 00000000:000064B2       Play7
 00000000:000064B7       Play8
 00000000:000064BC       Play9
 00000000:000064C1       PlayA
 00000000:000064C6       PlayB
 00000000:000064CB       DisableSoundCh0
 00000000:000064EA       DisableSoundCh1
 00000000:00006509       DisableSoundCh2
 00000000:00006528       DisableSoundCh3
 00000000:00006547       DisableSoundCh4
 00000000:00006566       DisableSoundCh5
 00000000:00006585       DisableSoundChAll
 00000000:000065AC       WaitForVBIAndSetSpriteBase
 00000000:000065B5       WaitForVBIAndSetTileBase
 00000000:000065BE       WaitForVBIAndLoadTileSet
 00000000:000065C7       JJLoadTileSet
 00000000:00006933       DrawBackground
 00000000:00006B29       ReadCommandA
 00000000:00006BE8       ReadCommandB
 00000000:00006CB4       ReadCommandC
 00000000:00006D66       DrawText
 00000000:00007E80       WaitXTimesForInput
 00000000:000099B4       NameScreenLoop
 00000000:00009EB0       NameScreenSetChar
 00000000:00009F43       NameScreenDeleteChar
 00000000:00009FC8       NameScreenCalcCharacter
 00000000:0000A02F       NameScreenCalculateCursor
 00000000:0000A051       NameScreenUpdateCursor
 00000000:0000A112       SelectPlayersLoop
 00000000:0000A290       SelectPlayer
 00000000:0000A31E       CreatePlayer
 00000000:0000A769       ReadAAndWaitForSelect
 00000000:0000A821       DrawRoundTitles
 00000000:0000AD8A       ReadAAndSetPlayer
 00000000:0000ADDF       ReadAAndCheckPlayer
 00000000:0000AF1F       ShowQuestion
 00000000:0000AF65       ShowChoices
 00000000:0000AFC5       ShowHint1
 00000000:0000B004       ShowHint2
 00000000:0000B033       ShowHint3
 00000000:0000B061       ShowAnswer
 00000000:0000B0BF       ShowReason
 00000000:0000B1C0       ShowScore
 00000000:0000C848       MenuUpdate
 00000000:0000D10D       WinScreen
 00000000:0000D1F1       DoFireworks
 00000000:0000E9AE       JLoadTileSet
 00000000:0000E9B6       JLoadPalette
 00000000:0000E9C2       JSetTileBase
 00000000:0000E9C8       JSetSpriteBase
 00000000:0000E9CE       JDisableSprites
 00000000:0000E9D4       SetGamePage
 00000000:0000E9DF       MemoryTest
 00000000:0000EA36       SRAMTest
 00000000:0000EBA9       nullsub_1
 00000000:0000EBAA       WaitForVBI
 00000000:0000EBCA       RomTest
 00000000:0000EC24       WaitForTimer
 00000000:0000ECE9       nullsub_2

*/

#include "emu.h"

#include "elan_eu3a05_a.h"

#include "cpu/m6502/m6502.h"
//#include "cpu/m6502/w65c02.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "machine/bankdev.h"
#include "elan_eu3a05gpio.h"
#include "elan_eu3a05sys.h"
#include "elan_eu3a05vid.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"


namespace {

class elan_eu3a05_state : public driver_device
{
public:
	elan_eu3a05_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_sys(*this, "sys"),
		m_gpio(*this, "gpio"),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen"),
		m_ram(*this, "ram"),
		m_sound(*this, "eu3a05sound"),
		m_vid(*this, "vid"),
		m_pixram(*this, "pixram"),
		m_bank(*this, "bank"),
		m_gfxdecode(*this, "gfxdecode"),
		m_palette(*this, "palette")
	{ }

	void elan_eu3a05(machine_config &config);
	void elan_eu3a05_pal(machine_config& config);

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<elan_eu3a05sys_device> m_sys;
	required_device<elan_eu3a05gpio_device> m_gpio;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;

	// screen updates
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	INTERRUPT_GEN_MEMBER(interrupt);

	// for callback
	uint8_t read_full_space(offs_t offset);

	void elan_eu3a05_bank_map(address_map &map) ATTR_COLD;
	void elan_eu3a05_map(address_map &map) ATTR_COLD;

	virtual void video_start() override ATTR_COLD;

	required_shared_ptr<uint8_t> m_ram;
	required_device<elan_eu3a05_sound_device> m_sound;
	required_device<elan_eu3a05vid_device> m_vid;
	required_shared_ptr<uint8_t> m_pixram;
	required_device<address_map_bank_device> m_bank;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<palette_device> m_palette;

	void sound_end0(int state) { m_sys->generate_custom_interrupt(2); }
	void sound_end1(int state) { m_sys->generate_custom_interrupt(3); }
	void sound_end2(int state) { m_sys->generate_custom_interrupt(4); }
	void sound_end3(int state) { m_sys->generate_custom_interrupt(5); }
	void sound_end4(int state) { m_sys->generate_custom_interrupt(6); }
	void sound_end5(int state) { m_sys->generate_custom_interrupt(7); }
};

class elan_eu3a05_buzztime_state : public elan_eu3a05_state
{
public:
	elan_eu3a05_buzztime_state(const machine_config &mconfig, device_type type, const char *tag) :
		elan_eu3a05_state(mconfig, type, tag),
		m_cart(*this, "cartslot")
	{ }

	void elan_buzztime(machine_config& config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	//uint8_t random_r() { return machine().rand(); }
	uint8_t porta_r();
	void portb_w(uint8_t data);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	required_device<generic_slot_device> m_cart;
};


class elan_eu3a05_pvwwcas_state : public elan_eu3a05_state
{
public:
	elan_eu3a05_pvwwcas_state(const machine_config &mconfig, device_type type, const char *tag) :
		elan_eu3a05_state(mconfig, type, tag),
		m_prevport_c(0xff)
	{ }

	void pvwwcas(machine_config& config);

	void init_pvwwcas();

protected:

private:
	uint8_t pvwwc_portc_r();
	void pvwwc_portc_w(uint8_t data);
	uint8_t m_prevport_c;
};

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

	void elan_eu3a13(machine_config &config);
	void elan_eu3a13_pal(machine_config &config);
	void elan_eu3a13_pvmil8(machine_config &config);

	void init_sudelan();
	void init_sudelan3();

private:
	void elan_eu3a13_map(address_map &map) ATTR_COLD;
};


void elan_eu3a05_buzztime_state::machine_start()
{
	elan_eu3a05_state::machine_start();

	// if there's a cart make sure we can see it
	if (m_cart && m_cart->exists())
	{
		uint8_t *rom = memregion("maincpu")->base();
		uint8_t* cart = m_cart->get_rom_base();
		std::copy(&cart[0x000000], &cart[0x200000], &rom[0x200000]);
	}
	else
	{
		uint8_t *rom = memregion("maincpu")->base();
		uint8_t* bios = memregion("bios")->base();
		std::copy(&bios[0x000000], &bios[0x200000], &rom[0x200000]);
	}
}

DEVICE_IMAGE_LOAD_MEMBER(elan_eu3a05_buzztime_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	if (size != 0x20'0000)
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported cartridge size (only 2M supported)");

	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_NATIVE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

void elan_eu3a05_buzztime_state::elan_buzztime(machine_config &config)
{
	elan_eu3a05_state::elan_eu3a05(config);

	m_sys->set_alt_timer();

	m_gpio->read_0_callback().set(FUNC(elan_eu3a05_buzztime_state::porta_r)); // I/O lives in here
//  m_gpio->read_1_callback().set(FUNC(elan_eu3a05_buzztime_state::random_r)); // nothing of note
//  m_gpio->read_2_callback().set(FUNC(elan_eu3a05_buzztime_state::random_r)); // nothing of note
	m_gpio->write_1_callback().set(FUNC(elan_eu3a05_buzztime_state::portb_w)); // control related

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "buzztime_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(elan_eu3a05_buzztime_state::cart_load));

	SOFTWARE_LIST(config, "buzztime_cart").set_original("buzztime_cart");
}

uint8_t elan_eu3a05_buzztime_state::porta_r()
{
	logerror("%s: porta_r\n", machine().describe_context());
	return machine().rand();
}

void elan_eu3a05_buzztime_state::portb_w(uint8_t data)
{
	logerror("%s: portb_w %02x\n", machine().describe_context(), data);
}


void elan_eu3a05_state::video_start()
{
}


uint32_t elan_eu3a05_state::screen_update(screen_device& screen, bitmap_ind16& bitmap, const rectangle& cliprect)
{
	return m_vid->screen_update(screen, bitmap, cliprect);
}

// sound callback
uint8_t elan_eu3a05_state::read_full_space(offs_t offset)
{
	address_space& fullbankspace = m_bank->space(AS_PROGRAM);
	return fullbankspace.read_byte(offset);
}

// code at 8bc6 in Air Blaster makes unwanted reads, why, bug in code, flow issue?
//[:maincpu] ':maincpu' (8BC6): unmapped program memory read from 5972 & FF


void elan_eu3a05_state::elan_eu3a05_map(address_map &map)
{
	// can the addresses move around?
	map(0x0000, 0x3fff).ram().share("ram");
	map(0x4800, 0x49ff).rw(m_vid, FUNC(elan_eu3a05commonvid_device::palette_r), FUNC(elan_eu3a05commonvid_device::palette_w));

	map(0x5000, 0x501f).m(m_sys, FUNC(elan_eu3a05sys_device::map)); // including DMA controller
	map(0x5020, 0x503f).m(m_vid, FUNC(elan_eu3a05vid_device::map));

	// 504x GPIO area?
	map(0x5040, 0x5046).rw(m_gpio, FUNC(elan_eu3a05gpio_device::gpio_r), FUNC(elan_eu3a05gpio_device::gpio_w));
	// 5047
	map(0x5048, 0x504a).w(m_gpio, FUNC(elan_eu3a05gpio_device::gpio_unk_w));

	// 506x unknown
	map(0x5060, 0x506d).ram(); // read/written by tetris (ADC?)

	// 508x sound
	map(0x5080, 0x50bf).m(m_sound, FUNC(elan_eu3a05_sound_device::map));

	//map(0x5000, 0x50ff).ram();
	map(0x6000, 0xdfff).m(m_bank, FUNC(address_map_bank_device::amap8));

	map(0xe000, 0xffff).rom().region("maincpu", 0x3f8000);
	// not sure how these work, might be a modified 6502 core instead.
	map(0xfffa, 0xfffb).r(m_sys, FUNC(elan_eu3a05commonsys_device::nmi_vector_r)); // custom vectors handled with NMI for now
	//map(0xfffe, 0xffff).r(m_sys, FUNC(elan_eu3a05commonsys_device::irq_vector_r));  // allow normal IRQ for brk
}

// default e000 mapping is the same as eu3a14, other registers seem closer to eua05
void elan_eu3a13_state::elan_eu3a13_map(address_map& map)
{
	elan_eu3a05_map(map);
	map(0xe000, 0xffff).rom().region("maincpu", 0x0000);
	// not sure how these work, might be a modified 6502 core instead.
	map(0xfffa, 0xfffb).r(m_sys, FUNC(elan_eu3a05commonsys_device::nmi_vector_r)); // custom vectors handled with NMI for now
	//map(0xfffe, 0xffff).r(m_sys, FUNC(elan_eu3a05commonsys_device::irq_vector_r));  // allow normal IRQ for brk
}


void elan_eu3a05_state::elan_eu3a05_bank_map(address_map &map)
{
	map(0x000000, 0xffffff).noprw(); // shut up any logging when video params are invalid
	map(0x000000, 0x3fffff).rom().region("maincpu", 0);
	map(0x400000, 0x40ffff).ram(); // ?? only ever cleared maybe a mirror of below?
	map(0x800000, 0x80ffff).ram().share("pixram"); // Qix writes here and sets the tile base here instead of ROM so it can have a pixel layer
}

static INPUT_PORTS_START( rad_sinv )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON3 ) // MENU
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN1")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN2")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

INPUT_PORTS_END

static INPUT_PORTS_START( rad_tetr )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON1 ) // Anticlockwise
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON2 ) // Clockwise
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) // and Select
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	/* Player 2 inputs must be read via serial or similar
	   the game doesn't read them directly, or even let
	   you select player 2 mode by default
	*/

	PORT_START("IN1")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN2")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

INPUT_PORTS_END


static INPUT_PORTS_START( airblsjs )
	PORT_START("IN0")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN1")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Pause")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_START1 ) PORT_NAME("Start")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Trigger")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Missile")
INPUT_PORTS_END


static INPUT_PORTS_START( sudoku )
	PORT_START("IN0")
	PORT_DIPNAME( 0x01, 0x01, "IN0" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN1")
	PORT_DIPNAME( 0x01, 0x01, "IN1" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN2")
	PORT_DIPNAME( 0x01, 0x01, "IN2" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( sudoku2p )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)

	PORT_START("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)

	PORT_START("IN2")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( rad_ftet )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("P1 Pause")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("P1 Hold")

	PORT_START("IN1")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("P2 Pause")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("P2 Hold")
INPUT_PORTS_END

static INPUT_PORTS_START( carlecfg )
	PORT_START("IN0")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Start/Pause")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Select")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) // guess, not used?
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) // guess, not used?

	PORT_START("IN2")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END



void elan_eu3a05_state::machine_start()
{
}

void elan_eu3a05_state::machine_reset()
{
	/* the 6502 core sets the default stack value to 0x01bd
	   and Tetris does not initialize it to anything else

	   Tetris stores the playfield data at 0x100 - 0x1c7 and
	   has a clear routine that will erase that range and
	   trash the stack

	   It seems likely this 6502 sets it to 0x1ff by default
	   at least.

	   According to
	   http://mametesters.org/view.php?id=6486
	   this isn't right for known 6502 types either
	*/
	m_maincpu->set_state_int(M6502_S, 0x1ff);
}

static const gfx_layout helper_4bpp_8_layout =
{
	8,1,
	RGN_FRAC(1,1),
	4,
	{ 0,1,2,3 },
	{ STEP8(0,4) },
	{ 0 },
	8 * 4
};

static const gfx_layout helper_8bpp_8_layout =
{
	8,1,
	RGN_FRAC(1,1),
	8,
	{ 0,1,2,3,4,5,6,7 },
	{ STEP8(0,8) },
	{ 0 },
	8 * 8
};


// these are fake just to make looking at the texture pages easier
static const uint32_t texlayout_xoffset_8bpp[256] = { STEP256(0,8) };
static const uint32_t texlayout_yoffset_8bpp[256] = { STEP256(0,256*8) };
static const gfx_layout texture_helper_8bpp_layout =
{
	256, 256,
	RGN_FRAC(1,1),
	8,
	{ 0,1,2,3,4,5,6,7 },
	EXTENDED_XOFFS,
	EXTENDED_YOFFS,
	256*256*8,
	texlayout_xoffset_8bpp,
	texlayout_yoffset_8bpp
};

static const uint32_t texlayout_xoffset_4bpp[256] = { STEP256(0,4) };
static const uint32_t texlayout_yoffset_4bpp[256] = { STEP256(0,256*4) };
static const gfx_layout texture_helper_4bpp_layout =
{
	256, 256,
	RGN_FRAC(1,1),
	4,
	{ 0,1,2,3 },
	EXTENDED_XOFFS,
	EXTENDED_YOFFS,
	256*256*4,
	texlayout_xoffset_4bpp,
	texlayout_yoffset_4bpp
};

static GFXDECODE_START( gfx_elan_eu3a05_fake )
	GFXDECODE_ENTRY( "maincpu", 0, helper_4bpp_8_layout,  0x0, 1  )
	GFXDECODE_ENTRY( "maincpu", 0, texture_helper_4bpp_layout,  0x0, 1  )
	GFXDECODE_ENTRY( "maincpu", 0, helper_8bpp_8_layout,  0x0, 1  )
	GFXDECODE_ENTRY( "maincpu", 0, texture_helper_8bpp_layout,  0x0, 1  )
GFXDECODE_END



INTERRUPT_GEN_MEMBER(elan_eu3a05_state::interrupt)
{
	m_sys->generate_custom_interrupt(9);
}


/* Tetris (PAL version)      has XTAL of 21.281370
   Air Blaster (PAL version) has XTAL of 21.2813
   what are the NTSC clocks?

   not confirmed on Space Invaders, actual CPU clock unknown.
   21281370 is the same value as a PAL SNES

   game logic speed (but not level scroll speed) in Air Blaster appears to be limited entirely by CPU speed (and therefore needs to be around 2-3mhz
   at most to match hardware) - a divider of 8 gives something close to original hardware
   it is unclear exactly what limits the clock speed (maybe video / sound causes waitstates? - dma in progress could also slow / stop the CPU
   and is not going to be 'instant' on hardware)

   using a low clock speed also helps with the badly programmed controls in Tetris as that likewise seems to run the game logic 'as fast as possible'
   there don't appear to be any kind of blanking bits being checked.
*/

void elan_eu3a05_state::elan_eu3a05(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, XTAL(21'281'370)/8); // wrong, this is the PAL clock
	m_maincpu->set_addrmap(AS_PROGRAM, &elan_eu3a05_state::elan_eu3a05_map);
	m_maincpu->set_vblank_int("screen", FUNC(elan_eu3a05_state::interrupt));

	ADDRESS_MAP_BANK(config, "bank").set_map(&elan_eu3a05_state::elan_eu3a05_bank_map).set_options(ENDIANNESS_LITTLE, 8, 24, 0x8000);

	PALETTE(config, m_palette).set_entries(256);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	m_screen->set_screen_update(FUNC(elan_eu3a05_state::screen_update));
	m_screen->set_size(32*8, 32*8);
	m_screen->set_visarea(0*8, 32*8-1, 0*8, 28*8-1);
	m_screen->set_palette(m_palette);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_elan_eu3a05_fake);

	ELAN_EU3A05_GPIO(config, m_gpio, 0);
	m_gpio->read_0_callback().set_ioport("IN0");
	m_gpio->read_1_callback().set_ioport("IN1");
	m_gpio->read_2_callback().set_ioport("IN2");

	ELAN_EU3A05_SYS(config, m_sys, 0);
	m_sys->set_cpu("maincpu");
	m_sys->set_addrbank("bank");

	ELAN_EU3A05_VID(config, m_vid, 0);
	m_vid->set_cpu("maincpu");
	m_vid->set_addrbank("bank");
	m_vid->set_palette("palette");
	m_vid->set_entries(256);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	ELAN_EU3A05_SOUND(config, m_sound, 8000);
	m_sound->space_read_callback().set(FUNC(elan_eu3a05_state::read_full_space));
	m_sound->add_route(ALL_OUTPUTS, "mono", 1.0);
	/* just causes select sound to loop in Tetris for now!
	m_sound->sound_end_cb<0>().set(FUNC(elan_eu3a05_state::sound_end0));
	m_sound->sound_end_cb<1>().set(FUNC(elan_eu3a05_state::sound_end1));
	m_sound->sound_end_cb<2>().set(FUNC(elan_eu3a05_state::sound_end2));
	m_sound->sound_end_cb<3>().set(FUNC(elan_eu3a05_state::sound_end3));
	m_sound->sound_end_cb<4>().set(FUNC(elan_eu3a05_state::sound_end4));
	m_sound->sound_end_cb<5>().set(FUNC(elan_eu3a05_state::sound_end5));
	*/
}

void elan_eu3a05_state::elan_eu3a05_pal(machine_config& config)
{
	elan_eu3a05(config);
	m_screen->set_refresh_hz(50);
	m_sys->set_pal(); // TODO: also set PAL clocks
}


void elan_eu3a13_state::elan_eu3a13(machine_config& config)
{
	elan_eu3a05(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &elan_eu3a13_state::elan_eu3a13_map);
	m_vid->set_is_sudoku();
	m_vid->set_use_spritepages();
	m_sys->set_alt_timer(); // for Carl Edwards'
}

void elan_eu3a13_state::elan_eu3a13_pal(machine_config& config)
{
	elan_eu3a13(config);
	m_sys->set_pal(); // TODO: also set PAL clocks
	m_screen->set_refresh_hz(50);
}

void elan_eu3a13_state::elan_eu3a13_pvmil8(machine_config& config)
{
	elan_eu3a05(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &elan_eu3a13_state::elan_eu3a13_map);
	m_vid->set_is_pvmilfin();
	m_sys->set_alt_timer();
	m_sys->set_pal(); // TODO: also set PAL clocks
	m_screen->set_refresh_hz(50);
}


uint8_t elan_eu3a05_pvwwcas_state::pvwwc_portc_r()
{
	int pc = m_maincpu->pc();

	if ((pc!=0xEBAC) && (pc!= 0xEBB5) && (pc != 0xEBA3) && (pc != 0xEBE1))
		logerror("%s: pvwwc_portc_r\n", machine().describe_context().c_str());

	if (pc == 0xEBE1)
		logerror("%s: pvwwc_portc_r reading input bit\n\n", machine().describe_context());

	return m_prevport_c | 0x4;
}

void elan_eu3a05_pvwwcas_state::pvwwc_portc_w(uint8_t data)
{
	logerror("%s: pvwwc_portc_w %02x\n", machine().describe_context());


	if ((m_prevport_c & 0x01) != (data & 0x01))
	{
		if (data & 0x01)
			logerror("bit 0 going high\n");
		else
			logerror("bit 0 going low\n");
	}

	if ((m_prevport_c & 0x02) != (data & 0x02))
	{
		if (data & 0x02)
			logerror("bit 1 going high\n");
		else
			logerror("bit 1 going low\n");
	}

	m_prevport_c = data;
}

void elan_eu3a05_pvwwcas_state::pvwwcas(machine_config& config)
{
	elan_eu3a05(config);
	m_screen->set_refresh_hz(50);
	m_sys->set_pal(); // TODO: also set PAL clocks

	m_gpio->read_2_callback().set(FUNC(elan_eu3a05_pvwwcas_state::pvwwc_portc_r));
	m_gpio->write_2_callback().set(FUNC(elan_eu3a05_pvwwcas_state::pvwwc_portc_w));
}



ROM_START( rad_tetr )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "tetrisrom.bin", 0x000000, 0x100000, CRC(40538e08) SHA1(1aef9a2c678e39243eab8d910bb7f9f47bae0aee) )
	ROM_RELOAD(0x100000, 0x100000)
	ROM_RELOAD(0x200000, 0x100000)
	ROM_RELOAD(0x300000, 0x100000)
ROM_END

ROM_START( rad_sinv )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "spaceinvadersrom.bin", 0x000000, 0x100000, CRC(5ffb2c8f) SHA1(9bde42ec5c65d9584a802de7d7c8b842ebf8cbd8) )
	ROM_RELOAD(0x100000, 0x100000)
	ROM_RELOAD(0x200000, 0x100000)
	ROM_RELOAD(0x300000, 0x100000)
ROM_END

ROM_START( airblsjs )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "airblsjs.bin", 0x000000, 0x400000, BAD_DUMP CRC(d10a6a84) SHA1(fa65f06e7da229006ddaffb245eef2cc4f90a66d) ) // ROM probably ok, but needs verification pass
ROM_END

ROM_START( sudelan3 )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "sudoku.bin", 0x00000, 0x100000, CRC(c2596173) SHA1(cc74932648b577b735151014e8c04ed778e11704) )
	ROM_RELOAD(0x100000,0x100000)
	ROM_RELOAD(0x200000,0x100000)
	ROM_RELOAD(0x300000,0x100000)
ROM_END

ROM_START( sudelan )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "klaussudoku.bin", 0x00000, 0x200000, CRC(afd2b06a) SHA1(21db956fb40b2e3d61fc2bac89000cf7f61fe99e) )
	ROM_RELOAD(0x200000,0x200000)
ROM_END

ROM_START( sudoku2p )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "1596.bin", 0x00000, 0x100000, CRC(fe8ce5bc) SHA1(8fb1463c6d349e07f6483da2b6cce10a4f25fcec) )
	ROM_RELOAD(0x100000,0x100000)
	ROM_RELOAD(0x200000,0x100000)
	ROM_RELOAD(0x300000,0x100000)
ROM_END

ROM_START( rad_ftet )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "familytetris.u2", 0x00000, 0x100000, CRC(2b65a70d) SHA1(1c9a960ebb4c2c51177b8596c017a04bf816b020) )
	ROM_RELOAD(0x100000,0x100000)
	ROM_RELOAD(0x200000,0x100000)
	ROM_RELOAD(0x300000,0x100000)
ROM_END

ROM_START( carlecfg )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "carledwardsracing.bin", 0x00000, 0x100000, CRC(920f633e) SHA1(8460b77b9635a2484edab1111f35bbda74eb68e4) )
	ROM_RELOAD(0x100000,0x100000)
	ROM_RELOAD(0x200000,0x100000)
	ROM_RELOAD(0x300000,0x100000)
ROM_END

ROM_START( pvmil8 )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "millionare_8bit.bin", 0x000000, 0x200000, CRC(8934a8d6) SHA1(24681e06d02f1567a57b84ec1c6f0a23a5f308ac) )
	ROM_RELOAD(0x200000,0x200000)
ROM_END


ROM_START( pvmilfin )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "fwwtbam.bin", 0x000000, 0x200000, CRC(2cfef9ab) SHA1(b64f55e36b59790a310ae33154774ac613b5d49f) )
	ROM_RELOAD(0x200000,0x200000)
ROM_END



ROM_START( buzztime )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )

	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "buzztimeunit.bin", 0x000000, 0x200000, CRC(8ba3569c) SHA1(3e704338a53daed63da90aba0db4f6adb5bccd21) )
ROM_END


ROM_START( pvwwcas )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "playvisiontaikeecasino.bin", 0x000000, 0x200000, CRC(b5e4a58d) SHA1(0a7809e91023258ecd55386b3e36b1541fb9e7f6) )
	ROM_RELOAD(0x200000, 0x200000)
ROM_END

void elan_eu3a13_state::init_sudelan3()
{
	// skip infinite loop (why is this needed? does it think we've soft shutdown?)
	uint8_t* ROM = memregion("maincpu")->base();
	ROM[0x0fcc] = 0xea;
	ROM[0x0fcd] = 0xea;
	ROM[0x0fce] = 0xea;
}

void elan_eu3a13_state::init_sudelan()
{
	// avoid jump to infinite loop (why is this needed? does it think we've soft shutdown?)
	uint8_t* ROM = memregion("maincpu")->base();
	ROM[0xd0f] = 0xea;
	ROM[0xd10] = 0xea;
	ROM[0xd11] = 0xea;
}

void elan_eu3a05_pvwwcas_state::init_pvwwcas()
{
	// avoid jump to infinite loop (why is this needed? does it think we've soft shutdown? or I/O failure?)
	uint8_t* ROM = memregion("maincpu")->base();
	ROM[0x3f8d92] = 0xea;
	ROM[0x3f8d93] = 0xea;
	ROM[0x3f8d94] = 0xea;
}

} // anonymous namespace


CONS( 2004, rad_sinv, 0, 0, elan_eu3a05, rad_sinv, elan_eu3a05_state, empty_init, "Radica (licensed from Taito)",                      "Space Invaders [Lunar Rescue, Colony 7, Qix, Phoenix] (Radica, Arcade Legends TV Game)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // "5 Taito games in 1"

CONS( 2004, rad_tetr, 0, 0, elan_eu3a05, rad_tetr, elan_eu3a05_state, empty_init, "Radica (licensed from Elorg / The Tetris Company)", "Tetris (Radica, Arcade Legends TV Game)", MACHINE_NOT_WORKING ) // "5 Tetris games in 1"

// ROM contains the string "Credit:XiAn Hummer Software Studio(CHINA) Tel:86-29-84270600 Email:HummerSoft@126.com"  PCB has datecode of "050423" (23rd April 2005)
CONS( 2005, airblsjs, 0, 0, elan_eu3a05_pal, airblsjs, elan_eu3a05_state, empty_init, "Advance Bright Ltd", "Air-Blaster Joystick (AB1500, PAL)", MACHINE_NOT_WORKING )

CONS( 2004, buzztime, 0, 0, elan_buzztime, sudoku, elan_eu3a05_buzztime_state, empty_init, "Cadaco", "Buzztime Home Trivia System", MACHINE_NOT_WORKING )

CONS( 2005, pvwwcas,  0,        0, pvwwcas,    sudoku,   elan_eu3a05_pvwwcas_state, init_pvwwcas, "Play Vision / Taikee / V-Tac", "Worldwide Casino Tour 12-in-1", MACHINE_NOT_WORKING )


// Below seem to be EU3A13, as that was confirmed for the Family Tetris die.  They're like EU3A05, but with a different memory map

CONS( 2006, sudelan3, 0,        0, elan_eu3a13,      sudoku,   elan_eu3a13_state, init_sudelan3,  "All in 1 Products Ltd / Senario",  "Ultimate Sudoku TV Edition 3-in-1 (All in 1 / Senario)", MACHINE_NOT_WORKING )
// Senario also distributed this version in the US without the '3 in 1' text on the box, ROM has not been verified to match
CONS( 2005, sudelan, 0,         0, elan_eu3a13_pal,  sudoku,   elan_eu3a13_state, init_sudelan,  "All in 1 Products Ltd / Play Vision",  "Carol Vorderman's Sudoku Plug & Play TV Game (All in 1 / Play Vision)", MACHINE_NOT_WORKING )

CONS( 2005, sudoku2p, 0,        0, elan_eu3a13_pal,  sudoku2p, elan_eu3a13_state, empty_init,  "<unknown>",  "Sudoku TV Game (PAL, 2 players)", MACHINE_NOT_WORKING ) // a pair of yellow controllers with 'TV Sudoku Awesome Puzzles' on their label

CONS( 2006, rad_ftet, 0,        0, elan_eu3a13,      rad_ftet, elan_eu3a13_state, empty_init,  "Radica",  "Family Tetris", MACHINE_NOT_WORKING )
// rad_ftet shows UK logo if set to PAL

CONS( 200?, carlecfg, 0,        0, elan_eu3a13,      carlecfg, elan_eu3a13_state, empty_init,  "Excalibur Electronics",  "Carl Edwards' Chase For Glory", MACHINE_NOT_WORKING )

// this is in very similar packaging to the 'pvmil' game in tvgames/spg2xx_playvision.cpp, and the casing is identical
// however this is from a year earlier, and there is a subtle difference in the otherwise identical text on the back of the box, mentioning that it uses an 8-bit processor, where the other box states 16-bit
CONS( 2005, pvmil8,   0,        0, elan_eu3a13_pvmil8,  sudoku,   elan_eu3a13_state, empty_init,  "Play Vision", "Who Wants to Be a Millionaire? (Play Vision, Plug and Play, UK, 8-bit version)", MACHINE_NOT_WORKING )
// see https://millionaire.fandom.com/wiki/Haluatko_miljon%C3%A4%C3%A4riksi%3F_(Play_Vision_game)
CONS( 2005, pvmilfin, pvmil8,   0, elan_eu3a13_pvmil8,  sudoku,   elan_eu3a13_state, empty_init,  "Play Vision", u8"Haluatko miljonääriksi? (Finland)", MACHINE_NOT_WORKING )



elan_eu3a14.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood, R.Belmont

/*
    These use a 6502 derived CPU under a glob
    The CPU die is marked 'ELAN EU3A14'

    There is a second glob surrounded by TSOP48 pads
    this contains the ROM

    Known to be on this hardware

    name                          PCB ID      ROM width   TSOP pads   ROM size        SEEPROM         die markings
    Golden Tee Golf Home Edition  ?           x16         48          4MB             no              ELAN EU3A14   (developed by FarSight Studios)
    Real Swing Golf               74037       x16         48          4MB             no              ELAN EU3A14   (developed by FarSight Studios)
    Baseball 3                    ?           x16         48          4MB             no              ELAN EU3A14   (developed by FarSight Studios)
    Connectv Football             ?           x16         48          4MB             no              ELAN EU3A14   (developed by Medialink)
    Huntin’3                      ?           x16         48          4MB             no              Elan ?        (developed by V-Tac Technology Co Ltd.)
    Play TV Basketball            75029       x16         48          4MB             no              ELAN EU3A14

    In many ways this is similar to the rad_eu3a05.cpp hardware
    but the video system has changed, here the sprites are more traditional non-tile based, rather
    than coming from 'pages'

    --

    Compared to the XaviXport games camera hookups, Real Swing Golf just has 6 wires, Its camera PCB is the only one with a ceramic resonator.
    Maybe the CU5502 chip offloads some processing from the CPU?

    The Basketball camera also uses an ETOMS CU5502.  It’s different from the others (XaviXport + Real Swing Golf) in that the sensor is on a small PCB with
    a 3.58MHz resonator with 16 wires going to another small PCB that has a glob and a 4MHz resonator.  6 wires go from that PCB to the main game PCB.

    To access hidden test mode in Football hold enter and right during power on.

    Football test mode tests X pos, Y pos, Z pos, direction and speed.  This data must all be coming from the camera in the unit as the shinpads are simply
    reflective objects, they don't contain any electronics.  It could be a useful test case for better understanding these things.

    To access hidden test mode in Golden Tee Home hold back/backspin and left during power on.

    To access hidden test mode in Basketball hold left and Button 1 during power on.

    To access hidden test mode in Real Swing Golf hold left and down during power on.
     - test mode check
     77B6: lda $5041
     77B9: eor #$ed
     77BB: beq $77be

    To access hidden test mode in Baseball 3 hold down during power on.
    - test mode check
    686E: lda $5041
    6871: eor #$f7
    6873: bne $68c8

    It is not clear how to access Huntin'3 Test Mode (if possible) there do appear to be tiles for it tho

    Huntin'3 makes much more extensive use of the video hardware than the other titles, including
     - Table based Rowscroll (most first person views)
     - RAM based tiles (status bar in "Target Range", text descriptions on menus etc.)
     - Windowing effects (to highlight menu items, timer in "Target Range") NOT YET EMULATED / PROPERLY UNDERSTOOD

*/

#include "emu.h"

#include "elan_eu3a05_a.h"
#include "elan_eu3a14sys.h"
#include "elan_eu3a14vid.h"

#include "cpu/m6502/m6502.h"
#include "machine/bankdev.h"
#include "machine/timer.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class elan_eu3a14_state : public driver_device
{
public:
	elan_eu3a14_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_sys(*this, "sys"),
		m_sound(*this, "eu3a05sound"),
		m_vid(*this, "commonvid"),
		m_mainregion(*this, "maincpu"),
		m_mainram(*this, "mainram"),
		m_bank(*this, "bank"),
		m_palette(*this, "palette"),
		m_gfxdecode(*this, "gfxdecode"),
		m_screen(*this, "screen")
	{ }


	void radica_eu3a14(machine_config &config);
	void radica_eu3a14p(machine_config &config);

	void radica_eu3a14p_altrambase(machine_config &config);
	void radica_eu3a14_altrambase(machine_config& config);
	void radica_eu3a14_altrambase_adc(machine_config &config);

	void radica_eu3a14_altrambase_bb3(machine_config &config);
	void radica_eu3a14p_altrambase_bb3(machine_config &config);

	void radica_eu3a14_altspritebase(machine_config& config);
	void radica_eu3a14_altspritebase_bat(machine_config& config);

	int tsbuzz_inputs_r();

private:
	// screen updates
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	INTERRUPT_GEN_MEMBER(interrupt);


	void porta_dir_w(uint8_t data);
	void portb_dir_w(uint8_t data);
	void portc_dir_w(uint8_t data);

	void porta_dat_w(uint8_t data);
	void portb_dat_w(uint8_t data);
	void portc_dat_w(uint8_t data);


	TIMER_DEVICE_CALLBACK_MEMBER(scanline_cb);

	// for callback
	uint8_t read_full_space(offs_t offset);

	void bank_map(address_map &map) ATTR_COLD;
	void radica_eu3a14_map(address_map &map) ATTR_COLD;

	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual void video_start() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<elan_eu3a14sys_device> m_sys;
	required_device<elan_eu3a05_sound_device> m_sound;
	required_device<elan_eu3a14vid_device> m_vid;
	required_region_ptr<uint8_t> m_mainregion;

	required_shared_ptr<uint8_t> m_mainram;
	required_device<address_map_bank_device> m_bank;
	required_device<palette_device> m_palette;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<screen_device> m_screen;

	uint8_t m_portdir[3];

	void sound_end0(int state) { m_sys->generate_custom_interrupt(2); }
	void sound_end1(int state) { m_sys->generate_custom_interrupt(3); }
	void sound_end2(int state) { m_sys->generate_custom_interrupt(4); }
	void sound_end3(int state) { m_sys->generate_custom_interrupt(5); }
	void sound_end4(int state) { m_sys->generate_custom_interrupt(6); }
	void sound_end5(int state) { m_sys->generate_custom_interrupt(7); }
};


void elan_eu3a14_state::video_start()
{
	m_vid->video_start();
}


uint32_t elan_eu3a14_state::screen_update(screen_device& screen, bitmap_ind16& bitmap, const rectangle& cliprect)
{
	return m_vid->screen_update(screen, bitmap, cliprect);
}

// sound callback
uint8_t elan_eu3a14_state::read_full_space(offs_t offset)
{
	address_space& fullbankspace = m_bank->space(AS_PROGRAM);
	return fullbankspace.read_byte(offset);
}

void elan_eu3a14_state::porta_dir_w(uint8_t data)
{
	m_portdir[0] = data;
	// update state
}

void elan_eu3a14_state::portb_dir_w(uint8_t data)
{
	m_portdir[1] = data;
	// update state
}

void elan_eu3a14_state::portc_dir_w(uint8_t data)
{
	m_portdir[2] = data;
	// update state
}

void elan_eu3a14_state::porta_dat_w(uint8_t data)
{
}

void elan_eu3a14_state::portb_dat_w(uint8_t data)
{
}

void elan_eu3a14_state::portc_dat_w(uint8_t data)
{
}



void elan_eu3a14_state::bank_map(address_map &map)
{
	map(0x000000, 0x7fffff).rom().region("maincpu", 0);
}

void elan_eu3a14_state::radica_eu3a14_map(address_map& map)
{
	map(0x0000, 0x01ff).ram();
	map(0x0200, 0x3fff).ram().share("mainram"); // 200-9ff is sprites? a00 - ??? is tilemap?

	map(0x4800, 0x4bff).rw(m_vid, FUNC(elan_eu3a14vid_device::palette_r), FUNC(elan_eu3a14vid_device::palette_w));

	map(0x5000, 0x501f).m(m_sys, FUNC(elan_eu3a14sys_device::map)); // including DMA controller

	// probably GPIO like eu3a05, although it access 47/48 as unknown instead of 48/49/4a
	map(0x5040, 0x5040).w(FUNC(elan_eu3a14_state::porta_dir_w));
	map(0x5041, 0x5041).portr("IN0").w(FUNC(elan_eu3a14_state::porta_dat_w));
	map(0x5042, 0x5042).w(FUNC(elan_eu3a14_state::portb_dir_w));
	map(0x5043, 0x5043).portr("IN1").w(FUNC(elan_eu3a14_state::portb_dat_w));
	map(0x5044, 0x5044).w(FUNC(elan_eu3a14_state::portc_dir_w));
	map(0x5045, 0x5045).portr("IN2").w(FUNC(elan_eu3a14_state::portc_dat_w));

	map(0x5046, 0x5046).nopw();
	map(0x5047, 0x5047).nopw();
	map(0x5048, 0x5048).nopw();

	// 5060 - 506e  r/w during startup on foot (adc?)

	// 0x5080 - 50bf = SOUND AREA (same as eu5a03?)
	map(0x5080, 0x50bf).m(m_sound, FUNC(elan_eu3a05_sound_device::map));

	// 0x5100 - 517f = VIDEO AREA
	map(0x5100, 0x517f).m(m_vid, FUNC(elan_eu3a14vid_device::map));

	map(0x6000, 0xdfff).m(m_bank, FUNC(address_map_bank_device::amap8));

	map(0xe000, 0xffff).rom().region("maincpu", 0x0000);

	map(0xfffa, 0xfffb).r(m_sys, FUNC(elan_eu3a05commonsys_device::nmi_vector_r)); // custom vectors handled with NMI for now
	//map(0xfffe, 0xffff).r(m_sys, FUNC(elan_eu3a05commonsys_device::irq_vector_r));  // allow normal IRQ for brk
}


static INPUT_PORTS_START( eu3a14 )
	PORT_START("IN0")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("IN1")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("IN2")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

int elan_eu3a14_state::tsbuzz_inputs_r()
{
	// Test mode shows 8 directions, 2 buttons and a trigger
	// presumably the motion detection from the peripherals gets converted
	// into basic input directions before reaching the SoC

	// TODO: figure out protocol
	return machine().rand();
}

static INPUT_PORTS_START( tsbuzz )
	PORT_INCLUDE( eu3a14 )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_START1 ) // pause(?), hold this on startup for test mode
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON1 ) // cheat? skips levels
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(elan_eu3a14_state::tsbuzz_inputs_r)) // all other inputs read through here
INPUT_PORTS_END


static INPUT_PORTS_START( rad_gtg )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1 ) // back / backspin
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) // up and down in the menus should be the trackball
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )

	PORT_START("IN1")
	PORT_DIPNAME( 0x01, 0x01, "IN1" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN2")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_DIPNAME( 0x08, 0x08, "Track Y test" ) // trackball up/down direction bit? (read in interrupt, increases / decreases a counter)
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_DIPNAME( 0x20, 0x20, "Track X test" ) // trackball left / right direction bit? (read in interrupt, increases / decreases a counter)
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END


static INPUT_PORTS_START( rad_rsg ) // base unit just has 4 directions + enter and a sensor to swing the club over
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) // aiming
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) // select in menus?
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) // previous in menus?
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) // next in menus?
	PORT_DIPNAME( 0x20, 0x20, "IN0" )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN1")
	PORT_DIPNAME( 0x01, 0x01, "IN1" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN2")
	PORT_DIPNAME( 0x01, 0x01, "IN2" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END


static INPUT_PORTS_START( radica_foot )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) // enter?
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN1")
	PORT_DIPNAME( 0x01, 0x01, "IN1" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN2")
	PORT_DIPNAME( 0x01, 0x01, "IN2" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( radica_hnt3 )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Menu Previous")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Menu Next")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Menu Select")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON4 ) // pause?

	PORT_START("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Fire Gun") // maybe
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Safety") PORT_TOGGLE
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_NAME("Fire Gun (alt)") // maybe
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN2")
	PORT_DIPNAME( 0x01, 0x01, "IN2" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( radica_bask )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_DIPNAME( 0x08, 0x08, "IN0" )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN1")
	PORT_DIPNAME( 0x01, 0x01, "IN1" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN2")
	PORT_DIPNAME( 0x01, 0x01, "IN2" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( radica_bb3 )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN1")
	PORT_DIPNAME( 0x01, 0x01, "IN1" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN2")
	PORT_DIPNAME( 0x01, 0x01, "IN2" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END


void elan_eu3a14_state::machine_start()
{
}

void elan_eu3a14_state::machine_reset()
{
	// rather be safe
	m_maincpu->set_state_int(M6502_S, 0x1ff);

	m_bank->set_bank(0x01);

	m_portdir[0] = 0x00;
	m_portdir[1] = 0x00;
	m_portdir[2] = 0x00;
}


TIMER_DEVICE_CALLBACK_MEMBER(elan_eu3a14_state::scanline_cb)
{
	// these interrupts need to occur based on how fast the trackball is
	// being moved, the direction is read in a port.
	int scanline = param;

	if (scanline == 20)
	{
		// vertical trackball
		m_sys->generate_custom_interrupt(12);
	}

	if (scanline == 40)
	{
		// horizontal trackball
		m_sys->generate_custom_interrupt(13);

	}
}

INTERRUPT_GEN_MEMBER(elan_eu3a14_state::interrupt)
{
	m_sys->generate_custom_interrupt(9);
}


// background
static const gfx_layout helper16x16x8_layout =
{
	16,16,
	RGN_FRAC(1,1),
	8,
	{ STEP8(0,1) },
	{ STEP16(0,8) },
	{ STEP16(0,16*8) },
	16 * 16 * 8
};

static const gfx_layout helper16x16x4_layout =
{
	16,16,
	RGN_FRAC(1,1),
	4,
	{ STEP4(0,1) },
	{ STEP16(0,4) },
	{ STEP16(0,16*4) },
	16 * 16 * 4
};

static const gfx_layout helper8x8x8_layout =
{
	8,8,
	RGN_FRAC(1,1),
	8,
	{ STEP8(0,1) },
	{ STEP8(0,8) },
	{ STEP8(0,8*8) },
	8 * 8 * 8
};

static GFXDECODE_START( gfx_helper )
	// dummy standard decodes to see background tiles, not used for drawing
	GFXDECODE_ENTRY( "maincpu", 0, helper16x16x8_layout,  0x0, 2  )
	GFXDECODE_ENTRY( "maincpu", 0, helper16x16x4_layout,  0x0, 32  )
	GFXDECODE_ENTRY( "maincpu", 0, helper8x8x8_layout,    0x0, 2  )
	GFXDECODE_ENTRY( "maincpu", 0, gfx_8x8x4_packed_msb,  0x0, 32  )
GFXDECODE_END



void elan_eu3a14_state::radica_eu3a14(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, XTAL(21'477'272)/2); // marked as 21'477'270
	m_maincpu->set_addrmap(AS_PROGRAM, &elan_eu3a14_state::radica_eu3a14_map);
	m_maincpu->set_vblank_int("screen", FUNC(elan_eu3a14_state::interrupt));

	ADDRESS_MAP_BANK(config, "bank").set_map(&elan_eu3a14_state::bank_map).set_options(ENDIANNESS_LITTLE, 8, 24, 0x8000);

	ELAN_EU3A14_SYS(config, m_sys, 0);
	m_sys->set_cpu("maincpu");
	m_sys->set_addrbank("bank");

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_helper);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	m_screen->set_screen_update(FUNC(elan_eu3a14_state::screen_update));
	m_screen->set_size(32*8, 32*8);
	m_screen->set_visarea(0*8, 32*8-1, 0*8, 28*8-1);
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette).set_entries(512);

	ELAN_EU3A14_VID(config, m_vid, 0);
	m_vid->set_cpu("maincpu");
	m_vid->set_addrbank("bank");
	m_vid->set_palette("palette");
	m_vid->set_screen("screen");
	m_vid->set_entries(512);
	m_vid->set_tilerambase(0x0200 - 0x200);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	ELAN_EU3A05_SOUND(config, m_sound, 8000);
	m_sound->space_read_callback().set(FUNC(elan_eu3a14_state::read_full_space));
	m_sound->add_route(ALL_OUTPUTS, "mono", 1.0);
	m_sound->sound_end_cb<0>().set(FUNC(elan_eu3a14_state::sound_end0));
	m_sound->sound_end_cb<1>().set(FUNC(elan_eu3a14_state::sound_end1));
	m_sound->sound_end_cb<2>().set(FUNC(elan_eu3a14_state::sound_end2));
	m_sound->sound_end_cb<3>().set(FUNC(elan_eu3a14_state::sound_end3));
	m_sound->sound_end_cb<4>().set(FUNC(elan_eu3a14_state::sound_end4));
	m_sound->sound_end_cb<5>().set(FUNC(elan_eu3a14_state::sound_end5));
}

void elan_eu3a14_state::radica_eu3a14_altspritebase(machine_config& config)
{
	radica_eu3a14(config);
	m_vid->set_default_spriteramaddr(0x04); // at 0x800
}

void elan_eu3a14_state::radica_eu3a14_altspritebase_bat(machine_config& config)
{
	radica_eu3a14(config);
	m_vid->set_default_spriteramaddr(0x0c); // at 0x1800
}



void elan_eu3a14_state::radica_eu3a14_altrambase(machine_config& config)
{
	radica_eu3a14(config);
	m_vid->set_tilerambase(0x0a00 - 0x200);
}

void elan_eu3a14_state::radica_eu3a14_altrambase_bb3(machine_config& config)
{
	radica_eu3a14_altrambase(config);
	m_sys->disable_timer_irq();
}

void elan_eu3a14_state::radica_eu3a14_altrambase_adc(machine_config &config)
{
	radica_eu3a14_altrambase(config);

	TIMER(config, "scantimer").configure_scanline(FUNC(elan_eu3a14_state::scanline_cb), "screen", 0, 1);
}


void elan_eu3a14_state::radica_eu3a14p(machine_config &config) // TODO, clocks differ too, what are they on PAL?
{
	radica_eu3a14(config);
	m_sys->set_pal(); // TODO: also set PAL clocks
	m_screen->set_refresh_hz(50);
}

void elan_eu3a14_state::radica_eu3a14p_altrambase(machine_config& config)
{
	radica_eu3a14p(config);
	m_vid->set_tilerambase(0x0a00 - 0x200);
}

void elan_eu3a14_state::radica_eu3a14p_altrambase_bb3(machine_config& config)
{
	radica_eu3a14p_altrambase(config);
	m_sys->disable_timer_irq();
}


ROM_START( rad_gtg )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "goldentee.bin", 0x000000, 0x400000, CRC(2d6cdb85) SHA1(ce6ed39d692ff16ea407f39c37b6e731f952b9d5) )
ROM_END

ROM_START( rad_rsg )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "realswinggolf.bin", 0x000000, 0x400000, CRC(89e5b6a6) SHA1(0b14aa84d7e7ae7190cd64e3eb125de2104342bc) )
ROM_END

ROM_START( rad_rsgp )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "realswinggolf.bin", 0x000000, 0x400000, CRC(89e5b6a6) SHA1(0b14aa84d7e7ae7190cd64e3eb125de2104342bc) )
ROM_END

ROM_START( rad_rsgpa )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "realswinggolf_multilanguage.bin", 0x000000, 0x400000, CRC(c03752a6) SHA1(7e9cc804edf0c23a8dedfa0c0a51d1bc811ea5c1) )
ROM_END

ROM_START( rad_foot )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "connectvfootball.bin", 0x000000, 0x400000, CRC(00ac4fc0) SHA1(2b60ae5c6bc7e9ef7cdbd3f6a0a0657ed3ab5afe) )
ROM_END

ROM_START( rad_bb3 )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "baseball3.bin", 0x000000, 0x400000, CRC(af86aab0) SHA1(5fed48a295f045ca839f87b0f9b78ecc51104cdc) )
ROM_END

ROM_START( rad_bb3p )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "baseball3.bin", 0x000000, 0x400000, CRC(af86aab0) SHA1(5fed48a295f045ca839f87b0f9b78ecc51104cdc) )
ROM_END

ROM_START( rad_hnt3 )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "huntin3.bin", 0x000000, 0x400000, CRC(c8e3e40b) SHA1(81eb16ac5ab6d93525fcfadbc6703b2811d7de7f) )
ROM_END

ROM_START( rad_hnt3p )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "huntin3.bin", 0x000000, 0x400000, CRC(c8e3e40b) SHA1(81eb16ac5ab6d93525fcfadbc6703b2811d7de7f) )
ROM_END

ROM_START( rad_bask )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "basketball.bin", 0x000000, 0x400000, CRC(7d6ff53c) SHA1(1c75261d55e0107a3b8e8d4c1eb2854750f2d0e8) )
ROM_END

ROM_START( rad_baskp )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "basketball.bin", 0x000000, 0x400000, CRC(7d6ff53c) SHA1(1c75261d55e0107a3b8e8d4c1eb2854750f2d0e8) )
ROM_END

/*

  The Interactive M.A.G. Motion Activated Gear titles use globtops with an unusual square pinout
  for the main ROM

  10   01
  +------\
11|      |48
  |      |
  |      |
24+------+35
  25    34


01 | A10
02 | A09
03 | A08
04 | A19
05 | A21
06 | A20
07 | A18
08 | A17
09 | A07
10 | A06
11 | A05
12 | A04
13 | A00
14 | A01
15 | A02
16 | A03
17 | /CE
18 | N/C
19 | D08
20 | D00
21 | N/C
22 | N/C
23 | D01
24 | D09
25 | D02
26 | D10
27 | D03
28 | D11
29 | N/C
30 | VCC
31 | VCC
32 | D04
33 | D12
34 | D05
35 | D13
36 | D06
37 | D15
38 | GND
39 | D07
40 | D14
41 | GND
42 | VCC
43 | A13
44 | A14
45 | A16
46 | A15
47 | A12
48 | A11

*/

ROM_START( tsbuzz )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "toystory_buzz.bin", 0x000000, 0x800000, CRC(8d727ed4) SHA1(228e1d788cdbaf251e15dba01b6c71e82197ea28) )
ROM_END

ROM_START( batvgc )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "batvgc.bin", 0x000000, 0x800000, CRC(513a5625) SHA1(d8db60818a4452e665c312b8b93642d8b2b33c8f) )
ROM_END

ROM_START( spidtt )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "mag_spidtt", 0x000000, 0x800000, CRC(05de01de) SHA1(f2891d6e743abdd7bb50d0bb84701b18225a0a7a) )
ROM_END

ROM_START( teentit )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "teentitansmag.bin", 0x000000, 0x800000, CRC(6087c7ca) SHA1(0bf639218c5f25449f4f98d5c3659a2311a28f72) )
ROM_END

} // anonymous namespace


CONS( 2006, rad_gtg,  0,        0, radica_eu3a14_altrambase_adc, rad_gtg,       elan_eu3a14_state, empty_init,  "Radica / FarSight Studios (licensed from Incredible Technologies)", "Golden Tee Golf: Home Edition", MACHINE_NOT_WORKING )

CONS( 2005, rad_rsg,  0,        0, radica_eu3a14_altrambase,     rad_rsg,       elan_eu3a14_state, empty_init,  "Radica / FarSight Studios",                                         "Play TV Real Swing Golf", MACHINE_NOT_WORKING )
CONS( 2005, rad_rsgp, rad_rsg,  0, radica_eu3a14p_altrambase,    rad_rsg,       elan_eu3a14_state, empty_init,  "Radica / FarSight Studios",                                         "Connectv Real Swing Golf (set 1)", MACHINE_NOT_WORKING ) // English only
CONS( 2005, rad_rsgpa,rad_rsg,  0, radica_eu3a14p_altrambase,    rad_rsg,       elan_eu3a14_state, empty_init,  "Radica / FarSight Studios",                                         "Connectv Real Swing Golf (set 2)", MACHINE_NOT_WORKING ) // English, German, French, Spanish, Italian

// also has a Connectv Real Soccer logo in the roms, apparently unused, maybe that was to be the US title (without the logo being changed to Play TV) but Play TV Soccer ended up being a different game licensed from Epoch instead.
CONS( 2006, rad_foot, 0,        0, radica_eu3a14p,               radica_foot,   elan_eu3a14_state, empty_init,  "Radica / Medialink",                                                "Connectv Football", MACHINE_NOT_WORKING )

CONS( 2005, rad_bb3,  0,        0, radica_eu3a14_altrambase_bb3,  radica_bb3,    elan_eu3a14_state, empty_init,  "Radica / FarSight Studios",                                         "Play TV Baseball 3", MACHINE_NOT_WORKING )
CONS( 2005, rad_bb3p, rad_bb3,  0, radica_eu3a14p_altrambase_bb3, radica_bb3,    elan_eu3a14_state, empty_init,  "Radica / FarSight Studios",                                         "Connectv Baseball 3", MACHINE_NOT_WORKING )

CONS( 2005, rad_hnt3, 0,        0, radica_eu3a14,                radica_hnt3,   elan_eu3a14_state, empty_init,  "Radica / V-Tac Technology Co Ltd.",                                 "Play TV Huntin' 3", MACHINE_NOT_WORKING )
CONS( 2005, rad_hnt3p,rad_hnt3, 0, radica_eu3a14p,               radica_hnt3,   elan_eu3a14_state, empty_init,  "Radica / V-Tac Technology Co Ltd.",                                 "Connectv Huntin' 3", MACHINE_NOT_WORKING )

CONS( 2005, rad_bask, 0,        0, radica_eu3a14_altrambase,     radica_bask,   elan_eu3a14_state, empty_init,  "Radica / FarSight Studios",                                         "Play TV Basketball", MACHINE_NOT_WORKING )
CONS( 2005, rad_baskp,rad_bask, 0, radica_eu3a14p_altrambase,    radica_bask,   elan_eu3a14_state, empty_init,  "Radica / FarSight Studios",                                         "Connectv Basketball", MACHINE_NOT_WORKING )

CONS( 200?, tsbuzz,   0,        0, radica_eu3a14_altspritebase,      tsbuzz,    elan_eu3a14_state, empty_init,  "Thinkway Toys",                                                     "Interactive M.A.G. Motion Activated Gear: Toy Story and Beyond! Buzz Lightyear Galactic Adventure", MACHINE_NOT_WORKING )
CONS( 200?, batvgc,   0,        0, radica_eu3a14_altspritebase_bat,  tsbuzz,    elan_eu3a14_state, empty_init,  "Thinkway Toys",                                                     "Interactive M.A.G. Motion Activated Gear: The Batman - Villains of Gotham City", MACHINE_NOT_WORKING )
CONS( 200?, spidtt,   0,        0, radica_eu3a14_altspritebase_bat,  tsbuzz,    elan_eu3a14_state, empty_init,  "Thinkway Toys",                                                     "Interactive M.A.G. Motion Activated Gear: Spider-Man - Triple Threat", MACHINE_NOT_WORKING )
CONS( 200?, teentit,  0,        0, radica_eu3a14_altspritebase_bat,  tsbuzz,    elan_eu3a14_state, empty_init,  "Thinkway Toys",                                                     "Interactive M.A.G. Motion Activated Gear: Teen Titans Arena Showdown", MACHINE_NOT_WORKING )

// the following Thinkway Toys 'MAG' products likely also fit here
// MAG: Superman Fight for Metropolis
// MAG: Disney Pixar Cars I Am Speed



eldorado.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:bataais
/*******************************************************************************

Fidelity Eldorado Chess Challenger (model 6119)

Hardware notes:
- PCB label: CXG262-600-001, CXG262-600-101
- TMP80C49AP6-6744 MCU, 2KB internal ROM, 6MHz XTAL
- buzzer, 16 leds, 8*8 chessboard buttons

The chess engine is by Ron Nelson. The hardware was made by CXG for Fidelity,
as seen on the PCB and also confirmed by Ron Nelson.

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

#include "emu.h"

#include "cpu/mcs48/mcs48.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "fidel_eldorado.lh"


namespace {

class eldorado_state : public driver_device
{
public:
	eldorado_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_board(*this, "board"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.0")
	{ }

	void eldorado(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<mcs48_cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<sensorboard_device> m_board;
	required_device<dac_1bit_device> m_dac;
	required_ioport m_inputs;

	bool m_kp_select = false;
	u8 m_inp_mux = 0;

	// I/O handlers
	void mux_w(u8 data);
	u8 mux_r();
	void control_w(u8 data);
	u8 input_r();
};

void eldorado_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_kp_select));
	save_item(NAME(m_inp_mux));
}



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

void eldorado_state::mux_w(u8 data)
{
	// D0-D7: input mux, led data
	m_inp_mux = ~data;
	m_display->write_mx(m_inp_mux);
}

u8 eldorado_state::mux_r()
{
	return ~m_inp_mux;
}

void eldorado_state::control_w(u8 data)
{
	// P24: speaker out
	m_dac->write(BIT(~data, 4));

	// P25,P26: led select
	m_display->write_my(~data >> 5 & 3);

	// P27: input mux highest bit (also goes to T0)
	m_kp_select = !bool(data & 0x80);
}

u8 eldorado_state::input_r()
{
	// P10-P17: multiplexed inputs
	u8 data = 0;

	// read chessboard buttons
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_file(i);

	// read sidepanel keypad
	if (m_kp_select)
		data |= m_inputs->read();

	return ~data;
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( eldorado )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Reverse / Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Move / Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Sound / Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Level / Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Verify / Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Problem / King")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Clear")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void eldorado_state::eldorado(machine_config &config)
{
	// basic machine hardware
	I8049(config, m_maincpu, 6_MHz_XTAL);
	m_maincpu->p1_in_cb().set(FUNC(eldorado_state::input_r));
	m_maincpu->p2_out_cb().set(FUNC(eldorado_state::control_w));
	m_maincpu->bus_in_cb().set(FUNC(eldorado_state::mux_r));
	m_maincpu->bus_out_cb().set(FUNC(eldorado_state::mux_w));
	m_maincpu->t0_in_cb().set(m_maincpu, FUNC(mcs48_cpu_device::p2_r)).bit(7);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(2, 8);
	config.set_default_layout(layout_fidel_eldorado);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( feldo )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD("tmp80c49ap6-6744_100-1027a01", 0x0000, 0x0800, CRC(3b93b6d2) SHA1(353a741624b4c7fd74a0cf601e2e52f9914b58b8) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME   PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1990, feldo, 0,      0,      eldorado, eldorado, eldorado_state, empty_init, "Fidelity Electronics International / CXG Systems", "Eldorado Chess Challenger", MACHINE_SUPPORTS_SAVE )



electrio.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

SciSys Electronic Trio / Kasparov Pocket Chess family

Developed by Heuristic Software (Kaplan's company). Electronic Trio and Pocket
Chess have the same MCU/ROM, the former adds an option to select 2 other games
(Checkers and Tic-Tac-Toe). Use the New Game button to select a different game.
In MAME, make sure to change to set the applicable view under Video Options.

Note that for the Checkers games, captures are not done automatically. Manually
remove the captured pieces while holding Ctrl.

Hardware notes:

Electronic Trio:
- PCB label: SH2-PE-013 REV 3
- Hitachi HD44868 @ ~600kHz (62K resistor)
- piezo, 16 LEDs, button sensors chessboard

PCB labels for others (base hardware is the same):
- Kasparov Pocket Chess: SHC-PE-005
- Kasparov Pocket Plus: SHD-PE-005
- Pocket Checkers: CH1-PE-013 Rev.0 (also seen with Pocket Plus PCB)
- Kasparov Travel Mate II: TDII-PE-006
- Electronic Chess Mk 10: ST1-PE-002 REV 2
- Kasparov Mk 12: ST2-PE-001 REV 3

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

44868A12 MCU is used in:
- SciSys Kasparov Pocket Chess (model 114)
- SciSys Electronic Trio (model 124)
- SciSys Kasparov Travel Mate II (model 125)
- SciSys Courier V (red version of Travel Mate II, French)
- SciSys Electronic Chess Mk 10 (later sold as Kasparov Mk 10) (model 162)
- Scisys Electronic Chess (red version of Mk 10, French)

Button configuration (New Game to switch between games) is determined by pin R43.
VCC = Electronic Trio, GND = dedicated chess computer.

44868A14 MCU is used in:
- SciSys Kasparov Pocket Plus (model 115)
- SciSys Kasparov Plus (model 128)
- SciSys Kasparov Plus (Computer Plus Coach) (model 129)
- SciSys Kasparov Mk 12 (model 164)
- Tandy (Radio Shack) Pocket Chess Computer 1450 (model 60-2251), Tandy brand
  Pocket Plus

Mk 12 and Plus Coach have 15 buttons, but functionality is exactly the same.
As with Electronic Trio, button configuration is determined by R42/R43 pins.
SciSys did something similar with Chess Companion II and Explorer chess.

Saitek Kasparov Mk 12 Trainer (and maybe Pocket Plus Trainer as well) are on
different hardware, with an ST8108 MCU.

44868A16 MCU is used in:
- Saitek Pocket Checkers (model 630)
- Saitek Electronic Checkers (model 640)
- Tandy (Radio Shack) Sensory Electronic Checkers (model 60-2203), Tandy brand
  Pocket Checkers

Pocket Checkers is nearly the same as Electronic Trio, it's hardcoded to Checkers.
The ROM is only 5 bytes different. The A5 LED still lights up when holding New Game,
this is normal. It's not possible to select Chess or Tic-Tac-Toe.

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

#include "emu.h"

#include "cpu/hmcs40/hmcs40.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "saitek_electrio.lh"
#include "saitek_mk12.lh"
#include "saitek_pcheckers.lh"
#include "saitek_pchess.lh"
#include "saitek_pplus.lh"


namespace {

class electrio_state : public driver_device
{
public:
	electrio_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board%u", 0),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_out_piece(*this, "piece%u_%c%u", 1U, unsigned('a'), 1U),
		m_out_pui(*this, "piece%u_ui%u", 1U, 0U),
		m_out_count(*this, "count%u_ui%u", 1U, 0U)
	{ }

	void electrio(machine_config &config);
	void pchess(machine_config &config);
	void pcheckers(machine_config &config);
	void mk12(machine_config &config);
	void pplus(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<hmcs40_cpu_device> m_maincpu;
	optional_device_array<sensorboard_device, 3> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<3> m_inputs;
	output_finder<2, 0x10, 0x10> m_out_piece;
	output_finder<2, 0x20+1> m_out_pui;
	output_finder<2, 2> m_out_count;

	u8 m_inp_mux = 0;

	template<int N> void init_checkers(u8 data);
	template<int N> void output_board(offs_t offset, u16 data);

	// I/O handlers
	template<int N> void input_w(u8 data);
	template<int N> u8 input1_r();
	void leds_w(u16 data);
	u8 input2_r();
};

void electrio_state::machine_start()
{
	// resolve outputs (electrio)
	m_out_piece.resolve();
	m_out_pui.resolve();
	m_out_count.resolve();

	save_item(NAME(m_inp_mux));
}



/*******************************************************************************
    Sensorboard
*******************************************************************************/

template<int N>
void electrio_state::init_checkers(u8 data)
{
	for (int i = 0; i < 12; i++)
	{
		m_board[N]->write_piece((i % 4) * 2 + ((i / 4) & 1), i / 4, 1); // white
		m_board[N]->write_piece((i % 4) * 2 + (~(i / 4) & 1), i / 4 + 5, 3); // black
	}
}

template<int N>
void electrio_state::output_board(offs_t offset, u16 data)
{
	// forward outputs for electrio extra boards
	const u8 sel = (offset >> 8) % 3;
	offset &= 0xff;

	switch (sel)
	{
		case 0:
		{
			const u8 x = offset & 0xf;
			const u8 y = offset >> 4 & 0xf;
			m_out_piece[N][x][y] = data;
			break;
		}

		case 1:
			m_out_pui[N][offset] = data;
			break;

		case 2:
			m_out_count[N][offset] = data;
			break;
	}
}



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

template<int N>
void electrio_state::input_w(u8 data)
{
	// R0x,R1x: input mux
	const u8 shift = N * 4;
	m_inp_mux = (m_inp_mux & ~(0xf << shift)) | ((data ^ 0xf) << shift);
}

template<int N>
u8 electrio_state::input1_r()
{
	// R2x,R3x: read chessboard
	u8 data = 0;

	// more than one emulated board for electrio
	for (auto & board : m_board)
	{
		for (int i = 0; i < 8; i++)
			if (board && BIT(m_inp_mux, i))
				data |= board->read_file(i, true);
	}

	return ~data >> (N * 4) & 0xf;
}

void electrio_state::leds_w(u16 data)
{
	// D0-D15: LEDs (direct)
	m_display->write_row(0, ~data);
}

u8 electrio_state::input2_r()
{
	u8 data = 0;

	// R40,R41: read buttons
	for (int i = 0; i < 2; i++)
		if (m_inp_mux & m_inputs[i]->read())
			data |= 1 << i;

	// R42,R43: button configuration
	data |= m_inputs[2]->read() << 2 & 0xc;
	return data ^ 3;
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( electrio )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("Play")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Multi-Move")
	PORT_BIT(0xe0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.2") // button config
	PORT_BIT(0x03, 0x02, IPT_CUSTOM)
INPUT_PORTS_END

static INPUT_PORTS_START( pchess )
	PORT_INCLUDE( electrio )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Non Auto")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Sound")

	PORT_MODIFY("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("King")

	PORT_MODIFY("IN.2")
	PORT_BIT(0x03, 0x00, IPT_CUSTOM)
INPUT_PORTS_END

static INPUT_PORTS_START( pcheckers )
	PORT_INCLUDE( pchess )

	PORT_MODIFY("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Man")
	PORT_BIT(0x1e, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_K) PORT_NAME("King")

	PORT_MODIFY("IN.2")
	PORT_BIT(0x03, 0x02, IPT_CUSTOM)
INPUT_PORTS_END

static INPUT_PORTS_START( mk12 )
	PORT_INCLUDE( electrio )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Non Auto")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Help")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Display Move")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Studies")

	PORT_MODIFY("IN.1")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Evaluate")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_MODIFY("IN.2")
	PORT_BIT(0x03, 0x03, IPT_CUSTOM)
INPUT_PORTS_END

static INPUT_PORTS_START( pplus )
	PORT_INCLUDE( pchess )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_CODE(KEYCODE_U) PORT_NAME("New Game / Studies")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_C) PORT_NAME("Sound / Coach Level")

	PORT_MODIFY("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_D) PORT_NAME("Pawn / Display Move")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_E) PORT_NAME("King / Evaluate")

	PORT_MODIFY("IN.2")
	PORT_BIT(0x03, 0x02, IPT_CUSTOM)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void electrio_state::pchess(machine_config &config)
{
	// basic machine hardware
	HD44868(config, m_maincpu, 600'000); // approximation
	m_maincpu->write_r<0>().set(FUNC(electrio_state::input_w<0>));
	m_maincpu->write_r<1>().set(FUNC(electrio_state::input_w<1>));
	m_maincpu->read_r<2>().set(FUNC(electrio_state::input1_r<0>));
	m_maincpu->read_r<3>().set(FUNC(electrio_state::input1_r<1>));
	m_maincpu->read_r<4>().set(FUNC(electrio_state::input2_r));
	m_maincpu->write_r<5>().set("dac", FUNC(dac_1bit_device::write)).rshift(3);
	m_maincpu->write_d().set(FUNC(electrio_state::leds_w));

	SENSORBOARD(config, m_board[0]).set_type(sensorboard_device::BUTTONS);
	m_board[0]->init_cb().set(m_board[0], FUNC(sensorboard_device::preset_chess));
	m_board[0]->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 16);
	config.set_default_layout(layout_saitek_pchess);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void electrio_state::mk12(machine_config &config)
{
	pchess(config);
	config.set_default_layout(layout_saitek_mk12);
}

void electrio_state::pplus(machine_config &config)
{
	pchess(config);
	config.set_default_layout(layout_saitek_pplus);
}

void electrio_state::pcheckers(machine_config &config)
{
	pchess(config);
	config.set_default_layout(layout_saitek_pcheckers);

	m_board[0]->init_cb().set(FUNC(electrio_state::init_checkers<0>));
	m_board[0]->set_spawnpoints(4);
}

void electrio_state::electrio(machine_config &config)
{
	pchess(config);
	config.set_default_layout(layout_saitek_electrio);

	// add boards for checkers and tic-tac-toe
	SENSORBOARD(config, m_board[1]).set_type(sensorboard_device::BUTTONS);
	m_board[1]->init_cb().set(FUNC(electrio_state::init_checkers<1>));
	m_board[1]->output_cb().set(FUNC(electrio_state::output_board<0>));
	m_board[1]->set_spawnpoints(4);
	m_board[1]->set_delay(attotime::from_msec(150));

	SENSORBOARD(config, m_board[2]).set_type(sensorboard_device::BUTTONS);
	m_board[2]->output_cb().set(FUNC(electrio_state::output_board<1>));
	m_board[2]->set_spawnpoints(2);
	m_board[2]->set_delay(attotime::from_msec(150));
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( electrio )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD("1985_sx2b_scisys_44868a12.u1", 0x0000, 0x2000, CRC(3d0cbb25) SHA1(719616f5140789dc9e3d970839b205b92c8e1a41) )
	ROM_IGNORE( 0x2000 ) // ignore factory test banks
ROM_END

ROM_START( pcheckers )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD("1988_cx1_saitek_44868a16.u1", 0x0000, 0x2000, CRC(0b9fd694) SHA1(8f3c13f65786c1d1f414665b459724a674226d06) )
	ROM_IGNORE( 0x2000 ) // ignore factory test banks
ROM_END

ROM_START( mk12 )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD("1986_sx1_scisys_44868a14.u1", 0x0000, 0x2000, CRC(41743fbc) SHA1(fb6f97ef9eddaf781eb4348a7fa956a874914086) )
	ROM_IGNORE( 0x2000 ) // ignore factory test banks
ROM_END

#define rom_kpchess rom_electrio
#define rom_kpplus rom_mk12

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT    COMPAT  MACHINE    INPUT      CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1985, electrio,  0,        0,      electrio,  electrio,  electrio_state, empty_init, "SciSys / Heuristic Software", "Electronic Trio", MACHINE_SUPPORTS_SAVE )
SYST( 1985, kpchess,   electrio, 0,      pchess,    pchess,    electrio_state, empty_init, "SciSys / Heuristic Software", "Kasparov Pocket Chess", MACHINE_SUPPORTS_SAVE )
SYST( 1988, pcheckers, electrio, 0,      pcheckers, pcheckers, electrio_state, empty_init, "Saitek / Heuristic Software", "Pocket Checkers", MACHINE_SUPPORTS_SAVE )

SYST( 1986, mk12,      0,        0,      mk12,      mk12,      electrio_state, empty_init, "SciSys / Heuristic Software", "Kasparov Mk 12", MACHINE_SUPPORTS_SAVE )
SYST( 1986, kpplus,    mk12,     0,      pplus,     pplus,     electrio_state, empty_init, "SciSys / Heuristic Software", "Kasparov Pocket Plus", MACHINE_SUPPORTS_SAVE )



electron.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol, Nigel Barnes
/******************************************************************************

Hardware Overview
-----------------
The Acorn Electron is a budget version of the BBC Micro home computer
Basic specs are:
6502 CPU @ 2MHz or 1MHz
32k RAM and 32k ROM
Text modes: 20x32, 40x25, 40x32, 80x25, 80x32
Graphics modes: 160x256 (4 or 16 colours), 320x256 (2 or 4 colours), 640x256 (2 colours), 320x200 (2 colors), 640x200 (2 colors)
Colours: 8 colours (either solid or flashing)
Sound: 1 channel, 7 octaves; built-in speaker
Internal ports for cassette storage, RGB/CVBS monitors and TV output
Various expansions can be added via the rear expansion port

PCB Layout
----------                           |-------------------|
                            |--------|  EXPANSION PORT   |
|-----|---------------------|                            |---||---------|
|MOD  | SPKR     16MHz                            LS169 18VAC||         []18VAC INPUT
|     |17.7345MHz               ROM                     18VAC|| POWER   |
|-----|       74S04                             |----|       || SUPPLY  |
|          KBD_CONN                    6502     |ULA |       ||         |
|CVBS                                           |----|       ||  +5VDC  |
|RGB                  LS00 LS86                              ||  -5VDC  |
|                                                         PWR||         |
|CASS         LM324   LS00 LS86 S74 LS74 4164 4164 4164 4164 ||         |
|------------------------------------------------------------||---------|
Notes: (all IC's shown. Only 16 ICs are used)
     6502 - 6502 CPU, clock input 2.000MHz [16/8]
      ULA - Custom logic chip 12CO21, also containing most of the BBC Micro circuitry
            Early PCB revisions used a PLCC68 chip in a socket. Later revisions used a
            PGA68 chip soldered directly into the motherboard
     4164 - 4164 64k x4-bit DRAM (4 chips for 32kbytes total)
      ROM - Hitachi HN613256 32k x8-bit mask ROM containing OS & BASIC
    LM324 - Texas Instruments LM324 Operational Amplifier
      MOD - UHF TV modulator UM1233-E36
     CVBS - Composite colour video output socket
      RGB - RGB monitor video output socket
     CASS - Cassette port
      PWR - 3-pin power input from internal power supply
 KBD_CONN - 22-pin keyboard connector
     SPKR - 2-pin internal speaker connector


Master RAM Board
----------------
The Master RAM Board (MRB) is an internal expansion board from Slogger, and could
also be purchased pre-fitted in the form of the Electron 64.
The MRB comes with a new MOS which provides three modes of operation, selected by a
switch, which are Normal, Turbo, and 64K. The 64K mode was the first to provide a
'shadow mode' on the Electron.


Stop Press 64i
--------------
The Stop Press 64i (SP64i) is another internal expansion board from Slogger, which
also requires the MRB to fitted. Being released in the early 90's there are no known
working examples, but bare prototype boards and ROMs were discovered during a Slogger
workshop clearout.
It provides a re-written AMX Stop Press (Desktop Publishing) for the Electron to take
advantage of the extra memory provided by the Master RAM Board, and also offers
ROM/RAM sockets and User Port for a mouse.

******************************************************************************
Emulation notes:

Incomplete:
    - 1 MHz bus is not emulated
    - Bus claiming by ULA is not implemented

Other internal boards to emulate:
    - Slogger Turbo Driver
    - Jafa Mode7 Mk2 Display Unit
    - move MRB and SP64i to an internal slot device?

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

#include "emu.h"
#include "electron_ula.h"

#include "bus/bbc/userport/userport.h"
#include "bus/electron/exp.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/m6502/m6502.h"
#include "formats/uef_cas.h"
#include "formats/csw_cas.h"
#include "imagedev/cassette.h"
#include "machine/6522via.h"
#include "machine/input_merger.h"
#include "machine/ram.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class electron_state : public driver_device
{
public:
	electron_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_irqs(*this, "irqs")
		, m_screen(*this, "screen")
		, m_cassette(*this, "cassette")
		, m_ula(*this, "ula")
		, m_region_mos(*this, "mos")
		, m_keybd(*this, "LINE.%u", 0)
		, m_exp(*this, "exp")
		, m_ram(*this, RAM_TAG)
		, m_mrb(*this, "MRB")
		, m_capslock_led(*this, "capslock_led")
	{ }

	void electron(machine_config &config);
	void btm2105(machine_config &config);

	void electron64(machine_config &config);

	static void plus3_default(device_t* device);

	DECLARE_INPUT_CHANGED_MEMBER( trigger_reset );

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint8_t fetch_r(offs_t offset);
	uint8_t ram_r(offs_t offset);
	void ram_w(offs_t offset, uint8_t data);
	virtual uint8_t rom_r(offs_t offset);
	virtual void rom_w(offs_t offset, uint8_t data);
	virtual uint8_t io_r(offs_t offset);
	virtual void io_w(offs_t offset, uint8_t data);
	uint8_t keyboard_r(offs_t offset);

	void mem_map(address_map &map) ATTR_COLD;
	void opcodes_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<input_merger_device> m_irqs;
	required_device<screen_device> m_screen;
	required_device<cassette_image_device> m_cassette;
	required_device<electron_ula_device> m_ula;
	required_memory_region m_region_mos;
	required_ioport_array<14> m_keybd;
	required_device<electron_expansion_slot_device> m_exp;
	required_device<ram_device> m_ram;
	optional_ioport m_mrb;
	output_finder<> m_capslock_led;

	bool m_mrb_mapped = false;
	bool m_vdu_drivers = false;
};


class electronsp_state : public electron_state
{
public:
	electronsp_state(const machine_config &mconfig, device_type type, const char *tag)
		: electron_state(mconfig, type, tag)
		, m_region_sp64(*this, "sp64")
		, m_via(*this, "via6522")
		, m_userport(*this, "userport")
		, m_romi(*this, "romi%u", 1)
		, m_rompages(*this, "ROMPAGES")
	{ }

	void electronsp(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

	virtual uint8_t rom_r(offs_t offset) override;
	virtual void rom_w(offs_t offset, uint8_t data) override;
	virtual uint8_t io_r(offs_t offset) override;
	virtual void io_w(offs_t offset, uint8_t data) override;

private:
	required_memory_region m_region_sp64;
	required_device<via6522_device> m_via;
	required_device<bbc_userport_slot_device> m_userport;
	required_device_array<generic_slot_device, 2> m_romi;
	required_ioport m_rompages;

	uint8_t m_rompage = 0;
	uint8_t m_sp64_bank = 0;
	std::unique_ptr<uint8_t[]> m_sp64_ram;

	std::pair<std::error_condition, std::string> load_rom(device_image_interface &image, generic_slot_device *slot);
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(rom1_load) { return load_rom(image, m_romi[0]); }
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(rom2_load) { return load_rom(image, m_romi[1]); }
};


void electron_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).rw(FUNC(electron_state::ram_r), FUNC(electron_state::ram_w));          /* 32KB of RAM */
	map(0x8000, 0xffff).rw(FUNC(electron_state::rom_r), FUNC(electron_state::rom_w));          /* 32KB of ROM */
	map(0xfc00, 0xfeff).rw(FUNC(electron_state::io_r), FUNC(electron_state::io_w));            /* IO */
}

void electron_state::opcodes_map(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(electron_state::fetch_r));
}


uint8_t electron_state::keyboard_r(offs_t offset)
{
	uint8_t data = 0;

	for (int i = 0; i < 14; i++)
	{
		if (!BIT(offset, i))
			data |= m_keybd[i]->read() & 0x0f;
	}

	return data;
}


uint8_t electron_state::fetch_r(offs_t offset)
{
	m_vdu_drivers = (offset & 0xe000) == 0xc000 ? true : false;

	return m_maincpu->space(AS_PROGRAM).read_byte(offset);
}


uint8_t electron_state::ram_r(offs_t offset)
{
	uint8_t data = 0xff;

	data &= m_exp->expbus_r(offset);

	switch (m_mrb.read_safe(0))
	{
	case 0x00: /* Normal */
		data &= m_ula->read(offset);
		break;

	case 0x01: /* Turbo */
		if (m_mrb_mapped && offset < 0x3000) offset += 0x8000;
		data &= m_ram->read(offset);
		break;

	case 0x02: /* Shadow */
		if (m_mrb_mapped && (offset < 0x3000 || !m_vdu_drivers)) offset += 0x8000;
		data &= m_ram->read(offset);
		break;
	}

	return data;
}

void electron_state::ram_w(offs_t offset, uint8_t data)
{
	m_exp->expbus_w(offset, data);

	switch (m_mrb.read_safe(0))
	{
	case 0x00: /* Normal */
		m_ula->write(offset, data);
		break;

	case 0x01: /* Turbo */
		if (m_mrb_mapped && offset < 0x3000) offset += 0x8000;
		m_ram->write(offset, data);
		break;

	case 0x02: /* Shadow */
		if (m_mrb_mapped && (offset < 0x3000 || !m_vdu_drivers)) offset += 0x8000;
		m_ram->write(offset, data);
		break;
	}
}


uint8_t electron_state::rom_r(offs_t offset)
{
	/* 0,1   Second external socket on the expansion module (SK2) */
	/* 2,3   First external socket on the expansion module (SK1)  */
	/* 4     Disc                                                 */
	/* 5,6   USER applications                                    */
	/* 7     Modem interface ROM                                  */
	/* 8,9   Keyboard                                             */
	/* 10,11 BASIC                                                */
	/* 12    Expansion module operating system                    */
	/* 13    High priority slot in expansion module               */
	/* 14    ECONET                                               */
	/* 15    Reserved                                             */

	uint8_t data = 0xff;

	offset += 0x8000;

	data &= m_ula->read(offset);
	data &= m_exp->expbus_r(offset);

	return data;
}

void electron_state::rom_w(offs_t offset, uint8_t data)
{
	offset += 0x8000;

	m_ula->write(offset, data);
	m_exp->expbus_w(offset, data);
}

uint8_t electronsp_state::rom_r(offs_t offset)
{
	uint8_t data = electron_state::rom_r(offset);

	switch (offset & 0x4000)
	{
	case 0x0000:
		if ((m_rompage & 0x0e) == m_rompages->read())
		{
			data = m_romi[m_rompage & 0x01]->read_rom(offset);
		}
		else
		{
			switch (m_rompage)
			{
			case 10:
				/* SP64 ROM utilises the spare BASIC ROM page */
				if (BIT(m_sp64_bank, 7) && (offset & 0x2000))
				{
					data = m_sp64_ram[offset & 0x1fff];
				}
				else
				{
					data = m_region_sp64->base()[(!BIT(m_sp64_bank, 0) << 14) | offset];
				}
				break;
			}
		}
		break;
	}

	return data;
}

void electronsp_state::rom_w(offs_t offset, uint8_t data)
{
	electron_state::rom_w(offset, data);

	switch (offset & 0x4000)
	{
	case 0x0000:
		if ((m_rompage & 0x0e) == m_rompages->read())
		{
			/* TODO: sockets are writeable if RAM */
		}
		else
		{
			switch (m_rompage)
			{
			case 10:
				/* SP64 ROM utilises the spare BASIC ROM page */
				if (BIT(m_sp64_bank, 7) && (offset & 0x2000))
				{
					m_sp64_ram[offset & 0x1fff] = data;
				}
				break;
			}
		}
		break;
	}
}


uint8_t electron_state::io_r(offs_t offset)
{
	uint8_t data = 0xff;

	offset += 0xfc00;

	data &= m_ula->read(offset);
	data &= m_exp->expbus_r(offset);

	return data;
}

void electron_state::io_w(offs_t offset, uint8_t data)
{
	m_ula->write(0xfc00 + offset, data);

	offset += 0xfc00;

	/* Master RAM Board */
	if (offset == 0xfc7f)
	{
		m_mrb_mapped = !BIT(data, 7);
	}

	m_exp->expbus_w(offset, data);
}


uint8_t electronsp_state::io_r(offs_t offset)
{
	uint8_t data = electron_state::io_r(offset);

	offset += 0xfc00;

	if ((offset & 0xfff0) == 0xfcb0)
	{
		data = m_via->read(offset & 0x0f);
	}

	return data;
}

void electronsp_state::io_w(offs_t offset, uint8_t data)
{
	electron_state::io_w(offset, data);

	offset += 0xfc00;

	if ((offset & 0xfff0) == 0xfcb0)
	{
		m_via->write(offset & 0x0f, data);
	}
	else if (offset == 0xfcfa)
	{
		m_sp64_bank = data;
	}
	else if ((offset == 0xfe05) && !(data & 0xf0))
	{
		m_rompage = data & 0x0f;
	}
}


/**************************************
   Machine Initialisation functions
***************************************/

void electron_state::machine_start()
{
	m_capslock_led.resolve();

	/* set ULA RAM/ROM pointers */
	m_ula->set_ram(m_ram->pointer());
	m_ula->set_rom(m_region_mos->base());

	/* register save states */
	save_item(NAME(m_mrb_mapped));
	save_item(NAME(m_vdu_drivers));
}

void electron_state::machine_reset()
{
	m_mrb_mapped = true;
	m_vdu_drivers = false;
}

void electronsp_state::machine_start()
{
	electron_state::machine_start();

	m_sp64_ram = std::make_unique<uint8_t[]>(0x2000);

	/* register save states */
	save_item(NAME(m_sp64_bank));
	save_pointer(NAME(m_sp64_ram), 0x2000);
}


std::pair<std::error_condition, std::string> electronsp_state::load_rom(device_image_interface &image, generic_slot_device *slot)
{
	uint32_t size = slot->common_get_size("rom");

	// socket accepts 8K and 16K ROM only
	if (size != 0x2000 && size != 0x4000)
		return std::make_pair(image_error::INVALIDLENGTH, "Invalid size: Only 8K/16K is supported");

	slot->rom_alloc(0x4000, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	slot->common_load_rom(slot->get_rom_base(), size, "rom");

	// mirror 8K ROMs
	uint8_t *crt = slot->get_rom_base();
	if (size <= 0x2000) memcpy(crt + 0x2000, crt, 0x2000);

	return std::make_pair(std::error_condition(), std::string());
}


INPUT_CHANGED_MEMBER(electron_state::trigger_reset)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
	if (newval)
	{
		m_exp->reset();
	}
}

static INPUT_PORTS_START( electron )
	PORT_START("LINE.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"\u2192 | \\") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR('|') PORT_CHAR('\\') // on the real keyboard, this would be on the 1st row, the 3rd key after 0
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("COPY") PORT_CODE(KEYCODE_END)  PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CHAR('[') PORT_CHAR(']')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')

	PORT_START("LINE.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"\u2190 ^ ~") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"\u2193 _ }") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CHAR('_') PORT_CHAR('}')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER)     PORT_CHAR(13)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DELETE") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)

	PORT_START("LINE.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"\u2191 £ {") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CHAR(0xA3) PORT_CHAR('{')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("LINE.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_CHAR('0') PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)          PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("LINE.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)          PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)          PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR('>')

	PORT_START("LINE.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)          PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)          PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("LINE.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)          PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)          PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)          PORT_CHAR('M') PORT_CHAR('m')

	PORT_START("LINE.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)          PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)          PORT_CHAR('N') PORT_CHAR('n')

	PORT_START("LINE.8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)          PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)          PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)          PORT_CHAR('B') PORT_CHAR('b')

	PORT_START("LINE.9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)          PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)          PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)          PORT_CHAR('V') PORT_CHAR('v')

	PORT_START("LINE.10")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)          PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)          PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)          PORT_CHAR('C') PORT_CHAR('c')

	PORT_START("LINE.11")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)          PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)          PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)          PORT_CHAR('X') PORT_CHAR('x')

	PORT_START("LINE.12")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)          PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_CHAR('Z') PORT_CHAR('z')

	PORT_START("LINE.13")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ESCAPE") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("FUNC") PORT_CODE(KEYCODE_TAB) PORT_CHAR(UCHAR_MAMEKEY(LALT))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("BRK")       /* BREAK */
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BREAK") PORT_CODE(KEYCODE_F12) PORT_CHAR(UCHAR_MAMEKEY(F12)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(electron_state::trigger_reset), 0)
INPUT_PORTS_END

static INPUT_PORTS_START( electron64 )
	PORT_INCLUDE(electron)

	PORT_START("MRB")
	PORT_CONFNAME(0x03, 0x02, "MRB Mode")
	PORT_CONFSETTING(0x00, "Normal")
	PORT_CONFSETTING(0x01, "Turbo")
	PORT_CONFSETTING(0x02, "64K")
INPUT_PORTS_END

static INPUT_PORTS_START(electronsp)
	PORT_INCLUDE(electron64)

	PORT_START("ROMPAGES")
	/* TODO: Actual 4bit dip settings are unknown, require a manual */
	PORT_DIPNAME(0x0f, 0x06, "SP64i ROM Pages")
	PORT_DIPSETTING(0x00, "0&1")
	PORT_DIPSETTING(0x02, "2&3")
	PORT_DIPSETTING(0x04, "4&5")
	PORT_DIPSETTING(0x06, "6&7")
	PORT_DIPSETTING(0x08, "8&9 (reserved)")
	PORT_DIPSETTING(0x0a, "10&11 (reserved)")
	PORT_DIPSETTING(0x0c, "12&13")
	PORT_DIPSETTING(0x0e, "14&15")
INPUT_PORTS_END

void electron_state::plus3_default(device_t* device)
{
	device->subdevice<electron_expansion_slot_device>("exp")->set_default_option("plus1");
}

void electron_state::electron(machine_config &config)
{
	M6502(config, m_maincpu, 16_MHz_XTAL / 8);
	m_maincpu->set_addrmap(AS_PROGRAM, &electron_state::mem_map);
	config.set_perfect_quantum(m_maincpu);

	INPUT_MERGER_ANY_HIGH(config, m_irqs).output_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(16_MHz_XTAL, 1024, 0, 640, 312, 0, 256);
	m_screen->set_screen_update(m_ula, FUNC(electron_ula_device::screen_update));
	m_screen->set_video_attributes(VIDEO_UPDATE_SCANLINE);

	PALETTE(config, "palette").set_entries(8);

	SPEAKER(config, "mono").front_center();

	RAM(config, m_ram).set_default_size("32K");

	ELECTRON_ULA(config, m_ula, 16_MHz_XTAL);
	m_ula->set_cpu_tag("maincpu");
	m_ula->kbd_cb().set(FUNC(electron_state::keyboard_r));
	m_ula->caps_lock_cb().set([this](int state) { m_capslock_led = state; });
	m_ula->cas_mo_cb().set([this](int state) { m_cassette->set_motor(state); });
	m_ula->cas_in_cb("cassette", FUNC(cassette_image_device::input));
	m_ula->cas_out_cb("cassette", FUNC(cassette_image_device::output));
	m_ula->add_route(ALL_OUTPUTS, "mono", 0.25);
	m_ula->irq_cb().set(m_irqs, FUNC(input_merger_device::in_w<0>));

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(bbc_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED);
	m_cassette->set_interface("bbc_cass");

	/* expansion port */
	ELECTRON_EXPANSION_SLOT(config, m_exp, 16_MHz_XTAL, electron_expansion_devices, "plus3");
	m_exp->set_option_machine_config("plus3", plus3_default);
	m_exp->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<1>));
	m_exp->nmi_handler().set_inputline(m_maincpu, M6502_NMI_LINE);

	/* software lists */
	SOFTWARE_LIST(config, "cass_list").set_original("electron_cass");
	SOFTWARE_LIST(config, "cass_list_bbc").set_compatible("bbc_cass").set_filter("ELK");
	SOFTWARE_LIST(config, "cart_list").set_original("electron_cart");
	SOFTWARE_LIST(config, "flop_list").set_original("electron_flop");
	SOFTWARE_LIST(config, "rom_list").set_original("electron_rom");
}


void electron_state::btm2105(machine_config &config)
{
	electron(config);

	m_screen->set_color(rgb_t::amber());

	/* expansion port */
	m_exp->set_default_option("m2105");
	m_exp->set_fixed(true);

	/* software lists */
	config.device_remove("cass_list");
	config.device_remove("cart_list");
	config.device_remove("flop_list");
	config.device_remove("rom_list");
}


void electron_state::electron64(machine_config &config)
{
	electron(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &electron_state::mem_map);
	m_maincpu->set_addrmap(AS_OPCODES, &electron_state::opcodes_map);

	m_ram->set_default_size("64K");
}


void electronsp_state::electronsp(machine_config &config)
{
	/* install mrb board */
	electron64(config);

	/* rom sockets */
	GENERIC_SOCKET(config, m_romi[0], generic_plain_slot, "electron_rom", "bin,rom");
	m_romi[0]->set_device_load(FUNC(electronsp_state::rom1_load));
	GENERIC_SOCKET(config, m_romi[1], generic_plain_slot, "electron_rom", "bin,rom");
	m_romi[1]->set_device_load(FUNC(electronsp_state::rom2_load));

	/* via */
	MOS6522(config, m_via, 16_MHz_XTAL / 16);
	m_via->readpb_handler().set(m_userport, FUNC(bbc_userport_slot_device::pb_r));
	m_via->writepb_handler().set(m_userport, FUNC(bbc_userport_slot_device::pb_w));
	m_via->cb1_handler().set(m_userport, FUNC(bbc_userport_slot_device::write_cb1));
	m_via->cb2_handler().set(m_userport, FUNC(bbc_userport_slot_device::write_cb2));
	m_via->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<2>));

	/* user port */
	BBC_USERPORT_SLOT(config, m_userport, bbc_userport_devices, "amxmouse");
	m_userport->cb1_handler().set(m_via, FUNC(via6522_device::write_cb1));
	m_userport->cb2_handler().set(m_via, FUNC(via6522_device::write_cb2));
}


ROM_START(electron)
	ROM_REGION( 0x8000, "mos", 0 )
	ROM_LOAD( "os_basic.ic2", 0x0000, 0x8000, CRC(b997f9cb) SHA1(4a66c83aba07d0a8e76ed8a5545af04e11c19fdc) )
ROM_END

ROM_START(electront)
	ROM_REGION( 0x8000, "mos",  0 )
	/* Serial 06-ALA01-0000087 from Centre for Computing History */
	ROM_LOAD( "basic.rom", 0x0000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281) )
	ROM_LOAD( "elk_036.rom", 0x4000, 0x4000, CRC(dd1a99c3) SHA1(87ee1b14895e476909dd002d5ca2346a3a5f3f57) )
ROM_END

ROM_START(electron64)
	ROM_REGION( 0x8000, "mos", 0 )
	ROM_LOAD( "basic.rom", 0x0000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281) )
	ROM_LOAD( "os_300.rom", 0x4000, 0x4000, CRC(f80a0cea) SHA1(165e42ff4164a842e56f08ebd420d5027af99fdd) )
ROM_END

ROM_START(electronsp)
	ROM_REGION( 0x8000, "mos", 0 )
	ROM_LOAD( "basic.rom", 0x0000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281) )
	ROM_LOAD( "os_310.rom", 0x4000, 0x4000, CRC(8b7a9003) SHA1(6d4e2f8ddc1d829b14206d2747749c4c24789568) )
	ROM_REGION( 0x8000, "sp64", 0 )
	ROM_SYSTEM_BIOS( 0, "101_2", "v1.01 (2x16K)" )
	ROMX_LOAD( "sp64_101_1.rom", 0x0000, 0x4000, CRC(07e2c5d6) SHA1(837e3382c376e3cc1ae42f1ca51158657ef2fd73), ROM_BIOS(0) )
	ROMX_LOAD( "sp64_101_2.rom", 0x4000, 0x4000, CRC(3d0e5dc1) SHA1(89743c43b24950d481c150fd4b4de985600cca2d), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "100", "v1.00 (32K)" )
	ROMX_LOAD( "sp64_100.rom", 0x0000, 0x8000, CRC(4918221c) SHA1(f185873106e7e7225b2e0c718803dc1ec4ebc685), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "100_2", "v1.00 (2x16K)" )
	ROMX_LOAD( "sp64_100_1.rom", 0x0000, 0x4000, CRC(6053e5a0) SHA1(6d79e5494349f157672e7c59949f3941e5a3dbdb), ROM_BIOS(2) )
	ROMX_LOAD( "sp64_100_2.rom", 0x4000, 0x4000, CRC(25d11d8e) SHA1(c1bceeb50fee1e11de7505a3b664b844cfb56289), ROM_BIOS(2) )
ROM_END

#define rom_btm2105 rom_electron

} // anonymous namespace


/*     YEAR  NAME        PARENT    COMPAT  MACHINE     INPUT       CLASS             INIT        COMPANY                             FULLNAME                                 FLAGS */
COMP ( 1983, electron,   0,        0,      electron,   electron,   electron_state,   empty_init, "Acorn Computers",                  "Acorn Electron",                        MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
COMP ( 1983, electront,  electron, 0,      electron,   electron,   electron_state,   empty_init, "Acorn Computers",                  "Acorn Electron (Trial)",                MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
COMP ( 1985, btm2105,    electron, 0,      btm2105,    electron,   electron_state,   empty_init, "British Telecom Business Systems", "BT Merlin M2105",                       MACHINE_NOT_WORKING )
COMP ( 1987, electron64, electron, 0,      electron64, electron64, electron_state,   empty_init, "Acorn Computers / Slogger",        "Acorn Electron (64K Master RAM Board)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
COMP ( 1991, electronsp, electron, 0,      electronsp, electronsp, electronsp_state, empty_init, "Acorn Computers / Slogger",        "Acorn Electron (Stop Press 64i)",       MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )



elegance.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:yoyo_chessboard, Berger
/*******************************************************************************

Fidelity Elegance Chess Challenger (AS12/6085)

Hardware notes (AS12):
- PCB label: 510-1084B01
- R65C02P3 @ 3/3.57MHz (apparently the first few had a 3MHz CPU)
- 2*8KB ROM + 1*4KB ROM, 2*2KB RAM(HM6116+TMM2016)

Hardware notes (6085):
- PCB label: 510-1084B01
- R65C02P4 @ 4MHz
- 3*8KB ROM(TMM2764), 2*2KB RAM(HM6116+TMM2016)

This is on the SC12B board, with enough modifications to support more leds and
magnetic chess board sensors. See sc12.cpp for a more technical description.
Like SC12, the printer expects a baud rate of 600, and 7 data bits.

The first RAM chip is low-power, and battery-backed with a capacitor. This is
also mentioned in the manual. Maybe it does not apply to older PCBs.

Like with EAS, the new game command for AS12 is: RE -> D6 (or D8) -> CL.
The newer model 6085 does not have this issue.

TODO:
- is the initial AS12 3MHz version the same ROM as felega1? When it's configured
  at 3MHz and the CPU divider set to 2, the pace is the same as fscc12a.

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

#include "emu.h"
#include "clockdiv.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "bus/rs232/rs232.h"
#include "cpu/m6502/r65c02.h"
#include "machine/clock.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "fidel_elegance.lh"


namespace {

// note: sub-class of fidel_clockdiv_state (see clockdiv.*)

class elegance_state : public fidel_clockdiv_state
{
public:
	elegance_state(const machine_config &mconfig, device_type type, const char *tag) :
		fidel_clockdiv_state(mconfig, type, tag),
		m_rs232(*this, "rs232"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.0")
	{ }

	// machine configs
	void feleg(machine_config &config);
	void felega(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(change_cpu_freq);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<rs232_port_device> m_rs232;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport m_inputs;

	u16 m_inp_mux = 0;
	u8 m_led_data = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_display();
	void control_w(u8 data);
	void led_w(offs_t offset, u8 data);
	u8 input_r(offs_t offset);
};

void elegance_state::machine_start()
{
	fidel_clockdiv_state::machine_start();

	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_led_data));
}

INPUT_CHANGED_MEMBER(elegance_state::change_cpu_freq)
{
	// known official CPU speeds: 3MHz, 3.57MHz, 4MHz
	static const XTAL xtal[3] = { 3_MHz_XTAL, 3.579545_MHz_XTAL, 4_MHz_XTAL };
	m_maincpu->set_unscaled_clock(xtal[newval % 3]);
}



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

void elegance_state::update_display()
{
	// 8*8(+1) chessboard leds
	m_display->matrix(m_inp_mux, m_led_data);
}

void elegance_state::control_w(u8 data)
{
	// d0-d3: 74245 P0-P3
	// 74245 Q0-Q8: input mux, led select
	u16 sel = 1 << (data & 0xf) & 0x3ff;
	m_inp_mux = bitswap<9>(sel,5,8,7,6,4,3,1,0,2);
	update_display();

	// 74245 Q9: speaker out
	m_dac->write(BIT(sel, 9));

	// d4: set when reading printer busy flag from 0xa000
	// d5: printer port data
	m_rs232->write_txd(BIT(~data, 5));

	// d6,d7: N/C?
}

void elegance_state::led_w(offs_t offset, u8 data)
{
	// a0-a2,d0: led data via NE591N
	m_led_data = (m_led_data & ~(1 << offset)) | ((data & 1) << offset);
	update_display();
}

u8 elegance_state::input_r(offs_t offset)
{
	u8 data = 0;

	// a0-a2,d7: multiplexed inputs (active low)
	// read chessboard sensors
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_file(i);

	// read sidepanel buttons
	if (m_inp_mux & 0x100)
		data |= m_inputs->read();

	data = bitswap<8>(data,4,3,2,1,0,5,6,7);
	return (data >> offset & 1) ? 0 : 0x80;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void elegance_state::main_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).ram().share("nvram");
	map(0x0800, 0x0fff).ram();
	map(0x1800, 0x1807).w(FUNC(elegance_state::led_w)).nopr();
	map(0x2000, 0x5fff).r("cartslot", FUNC(generic_slot_device::read_rom));
	map(0x6000, 0x6000).mirror(0x1fff).w(FUNC(elegance_state::control_w));
	map(0x8000, 0x9fff).rom();
	map(0xa000, 0xa007).mirror(0x1ff8).r(FUNC(elegance_state::input_r));
	map(0xc000, 0xcfff).mirror(0x1000).rom();
	map(0xe000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( feleg )
	PORT_INCLUDE( fidel_clockdiv_4 ) // default for >3MHz

	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("RV / Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("DM / Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("TB / Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("LV / Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("PV / Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("PB / King")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CL")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_N) PORT_NAME("RE")

	PORT_START("CPU")
	PORT_CONFNAME( 0x03, 0x02, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(elegance_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "3MHz (original)" )
	PORT_CONFSETTING(    0x01, "3.57MHz (AS12)" )
	PORT_CONFSETTING(    0x02, "4MHz (6085)" )
INPUT_PORTS_END

static INPUT_PORTS_START( felega )
	PORT_INCLUDE( feleg )

	PORT_MODIFY("CPU") // modify default to 3.57MHz
	PORT_CONFNAME( 0x03, 0x01, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(elegance_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "3MHz (original)" )
	PORT_CONFSETTING(    0x01, "3.57MHz (AS12)" )
	PORT_CONFSETTING(    0x02, "4MHz (6085)" )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void elegance_state::feleg(machine_config &config)
{
	// basic machine hardware
	R65C02(config, m_maincpu, 4_MHz_XTAL); // R65C02P4
	m_maincpu->set_addrmap(AS_PROGRAM, &elegance_state::main_map);

	auto &irq_clock(CLOCK(config, "irq_clock", 600)); // from 556 timer (22nF, 110K, 1K), ideal frequency is 600Hz
	irq_clock.set_pulse_width(attotime::from_usec(17)); // active for 17us
	irq_clock.signal_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	config.set_default_layout(layout_fidel_elegance);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);

	// cartridge
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "fidel_scc");
	SOFTWARE_LIST(config, "cart_list").set_original("fidel_scc");
}

void elegance_state::felega(machine_config &config)
{
	feleg(config);

	// basic machine hardware
	m_maincpu->set_clock(3.579545_MHz_XTAL); // R65C02P3
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( feleg ) // model 6085, serial 613623xx
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("feleg.8000", 0x8000, 0x2000, CRC(e9df31e8) SHA1(31c52bb8f75580c82093eb950959c1bc294189a8) ) // TMM2764, no label
	ROM_LOAD("feleg.c000", 0xc000, 0x1000, CRC(bed9c84b) SHA1(c12f39765b054d2ad81f747e698715ad4246806d) ) // "
	ROM_CONTINUE(          0xc000, 0x1000 ) // 1st half empty
	ROM_LOAD("feleg.e000", 0xe000, 0x2000, CRC(b1fb49aa) SHA1(d8c9687dd564f0fa603e6d684effb1d113ac64b4) ) // "
ROM_END

ROM_START( felega ) // model AS12
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("blue.8000",  0x8000, 0x2000, CRC(e86453ed) SHA1(8279cf9a7f471f893922d53d901dae65fabbd33f) ) // AM2764-25DC
	ROM_LOAD("green.c000", 0xc000, 0x1000, CRC(4a2b6946) SHA1(fd7d11e2589e654f91f7c2f667b927075bd49339) ) // D2732D
	ROM_LOAD("black.e000", 0xe000, 0x2000, CRC(823083ad) SHA1(4ea6a679edc7c149f1467113e9e5736ee0d5f643) ) // AM2764-25DC
ROM_END

ROM_START( felega1 ) // model AS12, only 1 byte difference compared with felega2 (evidently not a bad dump)
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("felega1.8000", 0x8000, 0x2000, CRC(8ddd71fb) SHA1(d856b42706df0a0e6ec7f44c49ec38d84155c1ef) ) // unknown label
	ROM_LOAD("felega1.c000", 0xc000, 0x1000, CRC(fcc48302) SHA1(f60d34229721e8659e9f81c267177daec7723d8f) ) // "
	ROM_LOAD("felega1.e000", 0xe000, 0x2000, CRC(b7c55d19) SHA1(45df961c2c3a1e9c8ec79efb7a1a82500425df2f) ) // "
ROM_END

ROM_START( felega2 ) // model AS12, serial 427921xx
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("blue.8000",  0x8000, 0x2000, CRC(2e07e657) SHA1(3238f21bdbf2277851e5a32e18c043e654123f00) ) // M5L2764K-2
	ROM_LOAD("green.c000", 0xc000, 0x1000, CRC(fcc48302) SHA1(f60d34229721e8659e9f81c267177daec7723d8f) ) // TMS2732AJL-45
	ROM_LOAD("black.e000", 0xe000, 0x2000, CRC(b7c55d19) SHA1(45df961c2c3a1e9c8ec79efb7a1a82500425df2f) ) // TMS2764JL-25
ROM_END

ROM_START( felega3 ) // model AS12, serial 427917xx
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("blue.8000",  0x8000, 0x2000, CRC(2e07e657) SHA1(3238f21bdbf2277851e5a32e18c043e654123f00) ) // M5L2764K-2
	ROM_LOAD("green.c000", 0xc000, 0x1000, CRC(fcc48302) SHA1(f60d34229721e8659e9f81c267177daec7723d8f) ) // TMS2732AJL-45
	ROM_LOAD("black.e000", 0xe000, 0x2000, CRC(9142121b) SHA1(264380e7ad36b7b1867658e1af387624d2a72630) ) // TMS2764JL-25
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1986, feleg,   0,      0,      feleg,   feleg,   elegance_state, empty_init, "Fidelity International", "Elegance Chess Challenger (model 6085)", MACHINE_SUPPORTS_SAVE )
SYST( 1984, felega,  feleg,  0,      felega,  felega,  elegance_state, empty_init, "Fidelity Computer Products", "Elegance Chess Challenger (model AS12, set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1984, felega1, feleg,  0,      felega,  felega,  elegance_state, empty_init, "Fidelity Computer Products", "Elegance Chess Challenger (model AS12, set 2)", MACHINE_SUPPORTS_SAVE )
SYST( 1984, felega2, feleg,  0,      felega,  felega,  elegance_state, empty_init, "Fidelity Computer Products", "Elegance Chess Challenger (model AS12, set 3)", MACHINE_SUPPORTS_SAVE )
SYST( 1984, felega3, feleg,  0,      felega,  felega,  elegance_state, empty_init, "Fidelity Computer Products", "Elegance Chess Challenger (model AS12, set 4)", MACHINE_SUPPORTS_SAVE )



elekscmp.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************

Elektor SC/MP

2009-11-22 Skeleton driver.
2012-05-10 Added keyboard [Robbbert]
2019-07-12 Added cassette [Robbbert]

To Use:
- Press MINUS to enter data input mode
- Press UP or DOWN to cycle through addresses

Paste test:
paste this in:  -0F0011^22^33^44^55^66^77^88^99^N-0F00
Now press UP to verify the data that was entered.

It seems the only way to exit each mode is to press NRST.

ToDo:


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

#include "emu.h"
#include "cpu/scmp/scmp.h"
#include "imagedev/cassette.h"
#include "machine/timer.h"
#include "speaker.h"
#include "elekscmp.lh"


namespace {

class elekscmp_state : public driver_device
{
public:
	elekscmp_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cass(*this, "cassette")
		, m_io_keyboard(*this, "X%u", 0U)
		, m_digit(*this, "digit%u", 0U)
	{ }

	void elekscmp(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(reset_button);

private:
	virtual void machine_start() override ATTR_COLD;

	u8 keyboard_r();
	void hex_display_w(offs_t offset, u8 data);
	int cass_r();
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_w);
	u8 convert_key(u8 data);
	bool m_cassinbit = 0, m_cassoutbit = 0, m_cassold = 0;
	u8 m_cass_data[4]{};

	void mem_map(address_map &map) ATTR_COLD;

	required_device<scmp_device> m_maincpu;
	required_device<cassette_image_device> m_cass;
	required_ioport_array<3> m_io_keyboard;
	output_finder<8> m_digit;
};


void elekscmp_state::machine_start()
{
	m_digit.resolve();

	save_item(NAME(m_cassinbit));
	save_item(NAME(m_cassoutbit));
	save_item(NAME(m_cassold));
	save_item(NAME(m_cass_data));
}

void elekscmp_state::hex_display_w(offs_t offset, u8 data)
{
	m_digit[offset & 0x7] = data;
}

u8 elekscmp_state::convert_key(u8 data)
{
	for (u8 i = 0; i < 8; i++)
		if (BIT(data, i))
			return i;

	return 0xff;
}

u8 elekscmp_state::keyboard_r()
{
	u8 data = m_io_keyboard[0]->read();
	if (data)
		return 0x80 | convert_key(data);

	data = m_io_keyboard[1]->read();
	if (data)
		return 0x88 | convert_key(data);

	data = m_io_keyboard[2]->read();
	if (data)
		return 0x80 | (convert_key(data) << 4);

	return 0;
}

TIMER_DEVICE_CALLBACK_MEMBER( elekscmp_state::kansas_r )
{
	// no tape - set uart to idle
	m_cass_data[1]++;
	if (m_cass_data[1] > 32)
	{
		m_cass_data[1] = 32;
		m_cassinbit = 1;
	}

	/* cassette - turn 1200/2400Hz to a bit */
	u8 cass_ws = (m_cass->input() > +0.04) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		m_cassinbit = (m_cass_data[1] < 12) ? 1 : 0;
		m_cass_data[1] = 0;
	}
}

TIMER_DEVICE_CALLBACK_MEMBER( elekscmp_state::kansas_w )
{
	u8 twobit = m_cass_data[3] & 7;
	m_cass_data[3]++;

	if (twobit == 0)
		m_cassold = m_cassoutbit;

	if (m_cassold)
		m_cass->output(BIT(m_cass_data[3], 0) ? -1.0 : +1.0); // 2400Hz
	else
		m_cass->output(BIT(m_cass_data[3], 1) ? -1.0 : +1.0); // 1200Hz
}

int elekscmp_state::cass_r()
{
	return m_cassinbit;
}

void elekscmp_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x0fff);
	map(0x000, 0x5ff).rom(); // ROM
	map(0x700, 0x707).w(FUNC(elekscmp_state::hex_display_w));
	map(0x708, 0x70f).r(FUNC(elekscmp_state::keyboard_r));
	map(0x800, 0xfff).ram(); // RAM - up to 2K of RAM
}

/* Input ports */
static INPUT_PORTS_START( elekscmp )
	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')

	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR('V')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CPU Reg") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Block Transfer") PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Cassette") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Subtract") PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Modify") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Run") PORT_CODE(KEYCODE_ENTER) PORT_CHAR('X')

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("NRST") PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(elekscmp_state::reset_button), 0) PORT_CHAR('N')
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(elekscmp_state::reset_button)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}

void elekscmp_state::elekscmp(machine_config &config)
{
	/* basic machine hardware */
	INS8060(config, m_maincpu, XTAL(1'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &elekscmp_state::mem_map);
	m_maincpu->s_out().set([this] (bool state) { m_cassoutbit = state; });
	m_maincpu->s_in().set(FUNC(elekscmp_state::cass_r));

	/* video hardware */
	config.set_default_layout(layout_elekscmp);

	SPEAKER(config, "mono").front_center();
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	TIMER(config, "kansas_w").configure_periodic(FUNC(elekscmp_state::kansas_w), attotime::from_hz(4800));
	TIMER(config, "kansas_r").configure_periodic(FUNC(elekscmp_state::kansas_r), attotime::from_hz(40000));
}

/* ROM definition */
ROM_START( elekscmp )
	ROM_REGION( 0x0600, "maincpu", 0 )
	ROM_LOAD( "elbug.001", 0x0000, 0x0200, CRC(f733da28) SHA1(b65d98be03eab80478167964beec26bb327bfdf3))
	ROM_LOAD( "elbug.002", 0x0200, 0x0200, CRC(529c0b88) SHA1(bd72dd890cd974e1744ca70aa3457657374cbf76))
	ROM_LOAD( "elbug.003", 0x0400, 0x0200, CRC(13585ad1) SHA1(93f722b3e84095a1b701b04bf9018c891933b9ff))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY    FULLNAME         FLAGS */
COMP( 1977, elekscmp, 0,      0,      elekscmp, elekscmp, elekscmp_state, empty_init, "Elektor", "Elektor SC/MP", MACHINE_SUPPORTS_SAVE )



elektronmono.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    elektronmono.cpp - Elektron Machinedrum and Monomachine

    Skeleton driver by R. Belmont

    The Machinedrum and Monomachine are basically the same hardware
    platform, only the front panel is different. Both have two major
    hardware revisions with different features (MK 1 and MK 2), and a
    "+Drive" storage option/upgrade.

    There are also "user waveform" (UW) versions of the Machinedrum MK 1
    and MK 2, and a keyboard version of the Monomachine MK 1 only. All
    Monomachine MK 2 units support user waveforms, there's no separate
    UW version. The firmware covers all models.

    The same circuit boards are used in both Machinedrum and
    Monomachine, at least in MK 2. I haven't found photos of the top of MK 1
    circuit boards, so some of the memory configurations are guesses based
    on firmware code, and may not be accurate.

    MCU: Motorola Coldfire 5206e
    - Flash memory: 8 Mbyte for Machinedrum UW models and Monomachine MK 2,
    1 Mbyte for others?
    - Main memory: 1 Mbyte DRAM for MK 1, 1 MByte SRAM for MK 2
    - Battery backed "patch" memory: 512 kByte SRAM for Machinedrum MK 1, 1
    MByte SRAM for others

    DSP: 2x Motorola DSP56303
    - Memory for each DSP: 3x 64 kbyte SRAM + 3x 256 kbyte for MK 1, 3x 512
    kbyte SRAM for MK 2
    - Additional memory for DSP 2 in Machinedrum UW: 3x 512 kbyte DRAM for
    MK 1, 3x 512 kbyte SRAM for MK 2

    +Drive: Hynix H26M21001CAR 2 Gbyte flash?

    The Coldfire memory map is software definable, and differs between the
    bootstrap code and the main operating system.

    The +Drive isn't mapped to memory at all. Instead it's accessed through
    Coldfire port A. It's just a large flash chip connected via a 74 series
    chip with XOR gates, but I don't understand exactly how communication
    works. The only one I've seen in a photo is 2 Gbytes, which is curiously
    large considering it stores 128 snapshots of the patch memory, and 128
    snapshots of the user waveform memory when applicable. Probably even 512
    Mbytes would be wasteful.

    Memory map for Coldfire when bootstrap code runs:
    0-100000: Lower 1 Mbyte of flash memory
    100000-200000: Patch memory

    Memory map for Coldfire when operating system runs:
    700000-800000: Patch memory
    10000000-10800000: Flash memory

    Common memory map for Coldfire:
    200000-300000: Main memory
    300000: Coldfire SIM (system integration module - peripherals, etc)
    500000-500008: DSP 1 Hi08 host registers in order (ICR, CVR, ISR, IVR,
    0/unused, RXH/TXH, RXM/TXM, RXL/TXL)
    600000-600008: DSP 2 Hi08
    1000000-1002000?: Coldfire internal SRAM

    Memory map for flash:
    0-100000: Firmware (identical to previous reconstructed dump)
    100000-200000: User waveforms for the factory presets on Machinedrum UW
    and Monomachine MK 2
    200000-800000: Probably user waveforms on units without +Drive. Both the
    units I dumped have factory installed +Drives, and the area is empty
    except for some markers. Machinedrum UW has a big lookup table near the
    end as well. Side note: I think there's code in the firmware to recreate
    markers and lookup tables, so the upper 6 Mbytes may be superfluous for
    emulation.

    Memory map for MK 1 DSPs:
    100000-140000: DRAM
    140000-150000: SRAM
    180000-200000: Additional DRAM for DSP 2 in Machinedrum UW only

    Memory map for MK 2 DSPs:
    100000-180000: SRAM
    180000-200000: Additional SRAM for DSP 2 in Machinedrum UW only

    The DSPs seem to use the ESSI serial interface to communicate, and to
    interface with the AKM AK4626AVQ audio codec chip. On Machinedrum, DSP 2
    does the main synthesis (that's why it has additional memory in UW
    models), and DSP 1 does effects. I haven't looked much at the
    Monomachine DSP code, but both chips share most of the code, so the work
    is probably shared in a more symmetrical manner than on the Machinedrum.

    Coldfire peripherals:
    - UART 1: MIDI
    - UART 2: Buttons, knobs, LEDs, display
    - Port A: Bit 0 mirrors bit 2 on MK 2, bit 1 checks SRAM battery charge,
    bit 2 is used for +Drive address, bit 3 is something about +Drive, bits
    4-7 is data RX/TX for +Drive.

    Model detection:
    - MK 2 is detected by writing to port A bit 2 and reading the same value
    from bit 0. Machinedrum also checks patch SRAM size.
    - Machinedrum UW is detected by comparing the ID of the flash chip to
    certain 8 Mbyte chip IDs, and by DSP 2 which checks available memory.
    - +Drive is detected by communicating with it on port A.
    - I haven't looked into how the Monomachine keyboard is detected, or how
    it's connected to the Coldfire.

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

#include "emu.h"
#include "cpu/m68000/mcf5206e.h"
#include "machine/mcf5206e.h"
#include "machine/intelfsh.h"
#include "speaker.h"


namespace {

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

	void elektron(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void elektron_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};

void elekmono_state::machine_start()
{
}

void elekmono_state::machine_reset()
{
}

void elekmono_state::elektron_map(address_map &map)
{
	map(0x00000000, 0x000fffff).rom().region("maincpu", 0);
	map(0x00100000, 0x001fffff).ram(); // patch memory
	map(0x00200000, 0x002fffff).ram(); // main RAM
	// 00300000 = Coldfire SIM
	// 00400000 = DSP1 Hi08 host registers (ICR, CVR, ISR, IVR, unused, RXH/TXH, RXM/TXM, RXL/TXL)
	// 00500000 = DSP2 Hi08 host registers (same as DSP1)
	map(0x01000000, 0x01001fff).ram();
	map(0x10000000, 0x107fffff).rom().region("maincpu", 0);
}

void elekmono_state::elektron(machine_config &config)
{
	MCF5206E(config, m_maincpu, XTAL(25'447'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &elekmono_state::elektron_map);

	SPEAKER(config, "speaker", 2).front();
}

static INPUT_PORTS_START( elektron )
INPUT_PORTS_END

ROM_START( monomach )
	ROM_REGION(0x800000, "maincpu", 0)
	ROM_LOAD( "elektron_sfx6-60_os1.32b.bin", 0x000000, 0x800000, CRC(f90a8b0e) SHA1(11a37460a5f47fd1a4d911414288690e6e7da605) )
ROM_END

ROM_START( machdrum )
	ROM_REGION(0x800000, "maincpu", 0)
	ROM_LOAD( "elektron_sps1-1uw_os1.63.bin", 0x000000, 0x800000, CRC(3d552c99) SHA1(a872a2f3527063673d6ea6d3080c4c62ef0cadc1) )
ROM_END

} // anonymous namespace


CONS( 2004, monomach, 0, 0, elektron, elektron, elekmono_state, empty_init, "Elektron", "Monomachine SFX6 MK2",  MACHINE_NOT_WORKING|MACHINE_NO_SOUND )
CONS( 2007, machdrum, 0, 0, elektron, elektron, elekmono_state, empty_init, "Elektron", "Machinedrum SPS-1 MK2", MACHINE_NOT_WORKING|MACHINE_NO_SOUND )



elf.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/***************************************************************************

    Netronics Elf II

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

/*

    TODO:
    - add cassette I/O

*/

#include "emu.h"
#include "elf.h"
#include "screen.h"
#include "speaker.h"
#include "elf2.lh"

#define LOAD \
	BIT(m_special->read(), 1)

#define MEMORY_PROTECT \
	BIT(m_special->read(), 2)

/* Read/Write Handlers */

uint8_t elf2_state::dispon_r()
{
	m_vdc->disp_on_w(1);
	m_vdc->disp_on_w(0);

	return 0xff;
}

uint8_t elf2_state::data_r()
{
	return m_data;
}

void elf2_state::data_w(uint8_t data)
{
	m_led_l->a_w(data & 0x0f);
	m_led_h->a_w(data >> 4);
}

void elf2_state::memory_w(offs_t offset, uint8_t data)
{
	if (LOAD)
	{
		if (MEMORY_PROTECT)
		{
			/* latch data from memory */
			data = m_ram->pointer()[offset];
		}
		else
		{
			/* write latched data to memory */
			m_ram->pointer()[offset] = data;
		}

		/* write data to 7 segment displays */
		m_led_l->a_w(data & 0x0f);
		m_led_h->a_w(data >> 4);
	}
}

/* Memory Maps */

void elf2_state::elf2_mem(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	// Ram is added dynamically
}

void elf2_state::elf2_io(address_map &map)
{
	map.unmap_value_high();
	map(0x01, 0x01).r(FUNC(elf2_state::dispon_r));
	map(0x04, 0x04).rw(FUNC(elf2_state::data_r), FUNC(elf2_state::data_w));
}

/* Input Ports */

INPUT_CHANGED_MEMBER(elf2_state::load_w)
{
	/* DMAIN is reset while LOAD is off */
	if (!newval)
		m_dmain = 0;
}

INPUT_CHANGED_MEMBER(elf2_state::input_w)
{
	/* assert DMAIN */
	if (LOAD && !newval && ~m_sc & 2)
		m_dmain = 1;
}

static INPUT_PORTS_START( elf2 )
	PORT_START("X1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("SPECIAL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RUN") PORT_CODE(KEYCODE_R) PORT_TOGGLE
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("LOAD") PORT_CODE(KEYCODE_L) PORT_TOGGLE PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(elf2_state::load_w), 0)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MP") PORT_CODE(KEYCODE_M) PORT_TOGGLE
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("IN") PORT_CODE(KEYCODE_ENTER) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(elf2_state::input_w), 0)
INPUT_PORTS_END

/* CDP1802 Configuration */

void elf2_state::sc_w(uint8_t data)
{
	/* DMAIN is reset while SC1 is high */
	if (data & 2)
		m_dmain = 0;

	m_sc = data;
}

/* MM74C923 Interface */

void elf2_state::da_w(int state)
{
	if (state)
	{
		/* shift keyboard data to latch */
		m_data <<= 4;
		m_data |= m_kb->read() & 0x0f;

		if (LOAD)
		{
			/* write data to 7 segment displays */
			m_led_l->a_w(m_data & 0x0f);
			m_led_h->a_w(m_data >> 4);
		}
	}
}

/* Machine Initialization */

void elf2_state::machine_start()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	/* setup memory banking */
	program.install_rom(0x0000, 0x00ff, m_ram->pointer());
	program.install_write_handler(0x0000, 0x00ff, write8sm_delegate(*this, FUNC(elf2_state::memory_w)));

	/* register for state saving */
	save_item(NAME(m_data));
	save_item(NAME(m_sc));
	save_item(NAME(m_dmain));
}

/* Machine Driver */

QUICKLOAD_LOAD_MEMBER(elf2_state::quickload_cb)
{
	int const size = image.length();

	if (size > m_ram->size())
	{
		return std::make_pair(image_error::INVALIDLENGTH, std::string());
	}

	image.fread(m_ram->pointer(), size);

	return std::make_pair(std::error_condition(), std::string());
}

void elf2_state::elf2(machine_config &config)
{
	/* basic machine hardware */
	CDP1802(config, m_maincpu, XTAL(3'579'545)/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &elf2_state::elf2_mem);
	m_maincpu->set_addrmap(AS_IO, &elf2_state::elf2_io);
	m_maincpu->wait_cb().set_ioport("SPECIAL").bit(1).invert();
	m_maincpu->clear_cb().set_ioport("SPECIAL").bit(0);
	m_maincpu->dma_in_cb().set([this]() { return m_dmain; });
	m_maincpu->ef4_cb().set_ioport("SPECIAL").bit(3);
	m_maincpu->q_cb().set_output("led0");
	m_maincpu->dma_rd_cb().set(FUNC(elf2_state::data_r));
	m_maincpu->dma_wr_cb().set(m_vdc, FUNC(cdp1861_device::dma_w));
	m_maincpu->sc_cb().set(FUNC(elf2_state::sc_w));

	/* video hardware */
	config.set_default_layout(layout_elf2);

	CDP1861(config, m_vdc, XTAL(3'579'545)/2).set_screen(SCREEN_TAG);
	m_vdc->int_cb().set_inputline(m_maincpu, COSMAC_INPUT_LINE_INT);
	m_vdc->dma_out_cb().set_inputline(m_maincpu, COSMAC_INPUT_LINE_DMAOUT);
	m_vdc->efx_cb().set_inputline(m_maincpu, COSMAC_INPUT_LINE_EF1);
	SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER);

	/* devices */
	MM74C923(config, m_kb, 0);
	m_kb->set_cap_osc(CAP_U(0.15));
	m_kb->set_cap_debounce(CAP_U(1));
	m_kb->da_wr_callback().set(FUNC(elf2_state::da_w));
	m_kb->x1_rd_callback().set_ioport("X1");
	m_kb->x2_rd_callback().set_ioport("X2");
	m_kb->x3_rd_callback().set_ioport("X3");
	m_kb->x4_rd_callback().set_ioport("X4");

	DM9368(config, m_led_h).update_cb().set_output("digit0");
	DM9368(config, m_led_l).update_cb().set_output("digit1");

	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	QUICKLOAD(config, "quickload", "bin").set_load_callback(FUNC(elf2_state::quickload_cb));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("256");
}

/* ROMs */

ROM_START( elf2 )
	ROM_REGION( 0x10000, CDP1802_TAG, ROMREGION_ERASE00 )
ROM_END

/* System Drivers */

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY      FULLNAME  FLAGS
COMP( 1978, elf2, 0,      0,      elf2,    elf2,  elf2_state, empty_init, "Netronics", "Elf II", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )



elite.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Fidelity Elite A/S series hardware (EAS, EAG, PC)
see eag68k.cpp for 68000-based EAG hardware

NOTE: To start a new game in EAS/PC, press Game Control (aka Reset), activate
the D6 square, and then press CL. See below for more info.

TODO:
- Chess Encyclopedia modules don't work properly on fpres. Prestige doesn't
  have nvram, it has a load/play switch for swapping modules without having to
  turn off the chesscomputer. This won't work on MAME, since it forces a hard
  reset when inserting a new ROM cartridge.
- Likewise, these modules don't work properly on feasx. It doesn't correctly
  announce when a new module needs to be inserted. Probably unsupported and
  therefore BTANB?

BTANB:
- feasglab locks up at boot if it was powered off in the middle of the game.
  To resolve this, hold the Game Control button while booting to clear nvram.
  The ROM dump was verified from 2 chesscomputers.

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

Elite A/S Challenger (EAS)
--------------------------
This came out in 1983. 2 program updates were released in 1983 and 1984,
named Budapest and Glasgow, places where Fidelity competed in chess computer
matches (they won it in 1983). A/S stands for Auto Sensory, it's the 1st
Fidelity board with magnet sensors. The magnetic chessboard was licensed from
AVE Micro Systems, in fact, the PC model board is the same one as in AVE's ARB.

Hardware notes:
- PCB label: FIDELITY ELECTRONICS, 510-1071A01, copyright 1983
- R65C02P4 or R6502BP CPU, default frequency 3MHz*
- 4KB RAM (2*HM6116), 24KB ROM
- TSI S14001A + speech ROM
- I/O with 8255 PPI and bunch of TTL
- 8*8 magnet sensors, 11 buttons, 8*(8+1) LEDs + 4*7seg LEDs
- module slot and printer port (600 baud, 7 data bits, 1 stop bit, no parity)

*: In West Germany, some distributors released it with overclocked CPUs,
advertised as 3.2, 3.6, or 4MHz. Unmodified EAS PCB photos show only a 3MHz XTAL.
Though model EWC(improved Budapest) had a 3.57MHz XTAL and EAS-C(Glasgow) had
a 4MHz XTAL. Model E4.0 is also assumed to be 4MHz.

A condensator/battery keeps RAM contents alive for a while when powered off.

Note that EAS/PC doesn't have a "new game" button, it is done through game options:
Press GAME CONTROL, then place/lift a piece on D6 to restart with user settings
intact, or D8 to reset and clear memory, then press CL.

Anecdote from Ron Nelson regarding the new game issue (Sid Samole was the CEO):
"The next year I designed it into a wooden housing, and Kathy said she wanted to
design the User Interface. I said ok go for it. When it was finished & masked,
I was at an internal sales meeting. No one could figure out how to start a new game.
Sid turned to me with a questioning look. I showed him how to do it (multiple key
presses as I recall) and explained about Kathy. He said, don't let them do it again."

Prestige Challenger (model PC, 1982 510-1050A01 PCB) hardware is very similar. It
was released before EAS, it doesn't have the 8255 PPI, but has more RAM (7*TMM2016P).
Some were released at 3.6MHz instead of 4MHz, maybe due to hardware instability?
Opening module PC16 was included by default, this module is the same as CB16 but
at different form factor.

There's also an unreleased prototype on a newer 1985 PCB revision (Fidelity Computer
Products, 510-1071B01 PCB). It has 16KB RAM (2*HM6264LP-15), a 5MHz CPU, IRQ is via
a 556 timer instead of a 38.4kHz resonator. This board came from Peter Reckwitz's
inventory, he was a Fidelity representative in West Germany at the time. Considering
the "EXP" (experimental?) EPROM labels, it can be speculated that this version
competed in one of the chess computer tournaments.

Elite Avant Garde (models 6081,6088,6089, Fidelity International, 510-1071C01 PCB)
is nearly the same as the 510-1071B01 PCB above, with one extra 7seg panel. Level
B8 starts a self-test and displays ROM checksums, press CL to advance.

Fidelity Elite Private Line were EAS/EAG conversions released by Fidelity Deutschland.
The "Elite Privat" was probably for the local market and the "Private Line" for
export. They took out the motherboard and leds and placed them inside a little box
separate from a (ledless) magnetic chessboard. The EAS conversion has unmodified
ROMs, most commonly seen with the feasgla romset. In MAME, it's available as an
alternate view (see video options). The EAG conversion is a cloneset of feag.

PC/EAS/EAG are the only Fidelity chesscomputers that officially support the EOA-EOE
Chess Encyclopedia modules. When it's time to swap modules to a different volume,
the 7seg display will announce it like "GS 0C", where the last digit indicates
which module to insert (in this case: Volume C). On EAS/EAG, the user is supposed
to turn off the chesscomputer before inserting a new module (a hard reset on MAME
is sufficient). On Prestige, modules need to be swapped on the fly. For the 1st
version of Prestige, it only works on level A3 or higher.

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

#include "emu.h"
#include "clockdiv.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "bus/rs232/rs232.h"
#include "cpu/m6502/r65c02.h"
#include "machine/clock.h"
#include "machine/i8255.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "sound/s14001a.h"
#include "video/pwm.h"

#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "fidel_eag.lh"
#include "fidel_eag_priv.lh"
#include "fidel_eas.lh"
#include "fidel_pc.lh"


namespace {

// note: sub-class of fidel_clockdiv_state (see clockdiv.*)

class elite_state : public fidel_clockdiv_state
{
public:
	elite_state(const machine_config &mconfig, device_type type, const char *tag) :
		fidel_clockdiv_state(mconfig, type, tag),
		m_ppi8255(*this, "ppi8255"),
		m_rombank(*this, "rombank"),
		m_rs232(*this, "rs232"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_speech(*this, "speech"),
		m_language(*this, "language"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// machine configs
	void pc(machine_config &config);
	void eas(machine_config &config);
	void ewc(machine_config &config);
	void easc(machine_config &config);
	void easx(machine_config &config);
	void eag(machine_config &config);
	void eagpriv(machine_config &config);
	void eag2100(machine_config &config);

	void init_eag() { m_rotate = true; }

	DECLARE_INPUT_CHANGED_MEMBER(change_cpu_freq);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// devices/pointers
	optional_device<i8255_device> m_ppi8255;
	optional_memory_bank m_rombank;
	required_device<rs232_port_device> m_rs232;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_device<s14001a_device> m_speech;
	required_region_ptr<u8> m_language;
	required_ioport_array<2> m_inputs;

	bool m_rotate = false;
	u8 m_led_data = 0;
	u8 m_7seg_data = 0;
	u8 m_inp_mux = 0;

	// address maps
	void pc_map(address_map &map) ATTR_COLD;
	void eas_map(address_map &map) ATTR_COLD;
	void eag_map(address_map &map) ATTR_COLD;
	void eag2100_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_display();
	void segment_w(offs_t offset, u8 data);
	void led_w(offs_t offset, u8 data);
	u8 input_r();
	void ppi_porta_w(u8 data);
	u8 ppi_portb_r();
	void ppi_portc_w(u8 data);
};



/*******************************************************************************
    Initialization
*******************************************************************************/

void elite_state::machine_start()
{
	fidel_clockdiv_state::machine_start();

	if (m_rombank != nullptr)
		m_rombank->configure_entries(0, 4, memregion("rombank")->base(), 0x2000);

	// register for savestates
	save_item(NAME(m_led_data));
	save_item(NAME(m_7seg_data));
	save_item(NAME(m_inp_mux));
}

void elite_state::machine_reset()
{
	fidel_clockdiv_state::machine_reset();

	if (m_rombank != nullptr)
		m_rombank->set_entry(0);
}

INPUT_CHANGED_MEMBER(elite_state::change_cpu_freq)
{
	// known official CPU speeds: 3MHz(EAS), 3.57MHz(PC/EWC/Privat), 4MHz(PC/EAS-C)
	static const XTAL xtal[3] = { 3_MHz_XTAL, 3.579545_MHz_XTAL, 4_MHz_XTAL };
	m_maincpu->set_unscaled_clock(xtal[newval % 3]);
}



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

// TTL/generic

void elite_state::update_display()
{
	// 4/8 7seg leds+H, 8*8(+1) chessboard leds
	u8 seg_data = bitswap<8>(m_7seg_data,0,1,3,2,7,5,6,4);
	m_display->matrix(1 << m_inp_mux, m_led_data << 8 | seg_data);
}

void elite_state::segment_w(offs_t offset, u8 data)
{
	// a0-a2,d7: digit segment
	u8 mask = 1 << offset;
	m_7seg_data = (m_7seg_data & ~mask) | ((data & 0x80) ? mask : 0);
	update_display();
}

void elite_state::led_w(offs_t offset, u8 data)
{
	// a0-a2,d0: led data
	m_led_data = (m_led_data & ~(1 << offset)) | ((data & 1) << offset);
	update_display();
}

u8 elite_state::input_r()
{
	u8 data = 0;

	// multiplexed inputs (active low)
	// read chessboard sensors
	if (m_inp_mux < 8)
	{
		// EAG chessboard is rotated 90 degrees
		if (m_rotate)
			data = m_board->read_rank(m_inp_mux);
		else
			data = m_board->read_file(m_inp_mux, true);
	}

	// read button panel
	else if (m_inp_mux == 8)
		data = m_inputs[0]->read();

	return ~data;
}


// 8255 PPI (PC: done with TTL instead)

void elite_state::ppi_porta_w(u8 data)
{
	// d0-d5: S14001A C0-C5
	// d6: S14001A start pin
	m_speech->data_w(data & 0x3f);
	m_speech->start_w(BIT(data, 6));

	// d7: printer port data
	m_rs232->write_txd(BIT(~data, 7));
}

void elite_state::ppi_portc_w(u8 data)
{
	// d0-d3: 7442 a0-a3
	// 7442 0-8: led select, input mux
	m_inp_mux = data & 0xf;
	update_display();

	// 7442 9: speaker out
	m_dac->write(BIT(1 << m_inp_mux, 9));

	// d4: speech ROM A12
	m_speech->set_rom_bank(BIT(data, 4));

	// d5: lower S14001A volume
	m_speech->set_output_gain(0, (data & 0x20) ? 0.25 : 1.0);

	// d6,d7: bookrom bankswitch (model EAG)
	if (m_rombank != nullptr)
		m_rombank->set_entry(data >> 6 & 3);
}

u8 elite_state::ppi_portb_r()
{
	// d0: printer busy?
	u8 data = 1;

	// d1: S14001A busy pin
	data |= (m_speech->busy_r()) ? 2 : 0;

	// d2,d3: language jumpers (hardwired)
	data |= *m_language << 2 & 0x0c;

	// d5: 3 more buttons
	data |= (BIT(m_inputs[1]->read(), m_inp_mux)) ? 0 : 0x20;

	// other: ?
	return data | 0xd0;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void elite_state::pc_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x17ff).ram();
	map(0x2000, 0x5fff).r("cartslot", FUNC(generic_slot_device::read_rom));
	map(0x7000, 0x7000).w(FUNC(elite_state::ppi_porta_w));
	map(0x7010, 0x7010).r(FUNC(elite_state::ppi_portb_r));
	map(0x7020, 0x7027).w(FUNC(elite_state::segment_w)).nopr();
	map(0x7030, 0x7037).w(FUNC(elite_state::led_w)).nopr();
	map(0x7040, 0x7040).w(FUNC(elite_state::ppi_portc_w));
	map(0x7050, 0x7050).r(FUNC(elite_state::input_r));
	map(0x8000, 0x9fff).ram();
	map(0xb000, 0xffff).rom();
}

void elite_state::eas_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).ram().share("nvram");
	map(0x0800, 0x0fff).ram();
	map(0x2000, 0x5fff).r("cartslot", FUNC(generic_slot_device::read_rom));
	map(0x7000, 0x7003).rw(m_ppi8255, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x7020, 0x7027).w(FUNC(elite_state::segment_w)).nopr();
	map(0x7030, 0x7037).w(FUNC(elite_state::led_w)).nopr();
	map(0x7050, 0x7050).r(FUNC(elite_state::input_r));
	map(0x8000, 0x9fff).rom();
	map(0xc000, 0xffff).rom();
}

void elite_state::eag_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).ram().share("nvram.ic8");
	map(0x2000, 0x5fff).r("cartslot", FUNC(generic_slot_device::read_rom));
	map(0x7000, 0x7003).rw(m_ppi8255, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x7020, 0x7027).w(FUNC(elite_state::segment_w)).nopr();
	map(0x7030, 0x7037).w(FUNC(elite_state::led_w)).nopr();
	map(0x7050, 0x7050).r(FUNC(elite_state::input_r));
	map(0x8000, 0x9fff).ram().share("nvram.ic6");
	map(0xa000, 0xffff).rom();
}

void elite_state::eag2100_map(address_map &map)
{
	eag_map(map);
	map(0xa000, 0xbfff).bankr(m_rombank);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( eas_base )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("Game Control")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_SPACE) PORT_NAME("Speaker")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("PB / King")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("PV / Queen")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("TM / Rook")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("ST / Bishop")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("TB / Knight")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("LV / Pawn")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("DM")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CL")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("RV")
INPUT_PORTS_END

static INPUT_PORTS_START( eas )
	PORT_INCLUDE( eas_base )
	PORT_INCLUDE( fidel_clockdiv_2 ) // default for 3MHz

	PORT_START("CPU")
	PORT_CONFNAME( 0x03, 0x00, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(elite_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "3MHz (EAS)" )
	PORT_CONFSETTING(    0x01, "3.57MHz (EWC)" )
	PORT_CONFSETTING(    0x02, "4MHz (EAS-C)" )
INPUT_PORTS_END

static INPUT_PORTS_START( ewc )
	PORT_INCLUDE( eas_base )
	PORT_INCLUDE( fidel_clockdiv_4 ) // default for >3MHz

	PORT_START("CPU")
	PORT_CONFNAME( 0x03, 0x01, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(elite_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "3MHz (EAS)" )
	PORT_CONFSETTING(    0x01, "3.57MHz (EWC)" )
	PORT_CONFSETTING(    0x02, "4MHz (EAS-C)" )
INPUT_PORTS_END

static INPUT_PORTS_START( easc )
	PORT_INCLUDE( eas_base )
	PORT_INCLUDE( fidel_clockdiv_4 ) // default for >3MHz

	PORT_START("CPU")
	PORT_CONFNAME( 0x03, 0x02, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(elite_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "3MHz (EAS)" )
	PORT_CONFSETTING(    0x01, "3.57MHz (EWC)" )
	PORT_CONFSETTING(    0x02, "4MHz (EAS-C)" )
INPUT_PORTS_END

static INPUT_PORTS_START( pc )
	PORT_INCLUDE( easc )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Reset") // led display still says - G C -
INPUT_PORTS_END

static INPUT_PORTS_START( easx )
	PORT_INCLUDE( easc )

	PORT_MODIFY("CPU") // 5MHz
	PORT_BIT(0x03, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( eag )
	PORT_INCLUDE( fidel_clockdiv_4 )

	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("RV")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Option")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("LV / Pawn")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("TB / Knight")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("ST / Bishop")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("TM / Rook")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("PV / Queen")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("PB / King")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CL")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("DM")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void elite_state::pc(machine_config &config)
{
	// basic machine hardware
	R65C02(config, m_maincpu, 4_MHz_XTAL); // R65C02P3/4
	m_maincpu->set_addrmap(AS_PROGRAM, &elite_state::pc_map);

	auto &irq_clock(CLOCK(config, "irq_clock", 38.4_kHz_XTAL/64)); // through 4060 IC, 600Hz
	irq_clock.set_pulse_width(attotime::from_nsec(13700)); // active for 13.7us
	irq_clock.signal_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(100));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 16);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_fidel_pc);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	S14001A(config, m_speech, 25000); // R/C circuit, around 25khz
	m_speech->add_route(ALL_OUTPUTS, "speaker", 0.75);

	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);

	// cartridge
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "fidel_scc");
	SOFTWARE_LIST(config, "cart_list").set_original("fidel_scc");
}

void elite_state::eas(machine_config &config)
{
	pc(config);

	// basic machine hardware
	m_maincpu->set_clock(3_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &elite_state::eas_map);

	I8255(config, m_ppi8255); // port B: input, port A & C: output
	m_ppi8255->out_pa_callback().set(FUNC(elite_state::ppi_porta_w));
	m_ppi8255->tri_pa_callback().set_constant(0);
	m_ppi8255->in_pb_callback().set(FUNC(elite_state::ppi_portb_r));
	m_ppi8255->out_pc_callback().set(FUNC(elite_state::ppi_portc_w));
	m_ppi8255->tri_pc_callback().set_constant(0);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);
	m_board->set_nvram_enable(true);

	config.set_default_layout(layout_fidel_eas);
}

void elite_state::ewc(machine_config &config)
{
	eas(config);
	m_maincpu->set_clock(3.579545_MHz_XTAL);
}

void elite_state::easc(machine_config &config)
{
	eas(config);
	m_maincpu->set_clock(4_MHz_XTAL);
}

void elite_state::easx(machine_config &config)
{
	eas(config);

	// basic machine hardware
	m_maincpu->set_clock(5_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &elite_state::eag_map);

	auto &irq_clock(CLOCK(config.replace(), "irq_clock", 600)); // from 556 timer (22nF, 82K+pot, 1K), ideal frequency is 600Hz
	irq_clock.set_pulse_width(attotime::from_nsec(15250)); // active for 15.25us
	irq_clock.signal_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	config.device_remove("nvram");
	NVRAM(config, "nvram.ic8", nvram_device::DEFAULT_ALL_1);
	NVRAM(config, "nvram.ic6", nvram_device::DEFAULT_ALL_1);
}

void elite_state::eag(machine_config &config)
{
	easx(config);

	// video hardware
	m_display->set_segmask(0x1ef, 0x7f);
	config.set_default_layout(layout_fidel_eag);
}

void elite_state::eagpriv(machine_config &config)
{
	eag(config);
	config.set_default_layout(layout_fidel_eag_priv);
}

void elite_state::eag2100(machine_config &config)
{
	eag(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &elite_state::eag2100_map);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( fpres ) // serial 0000082x
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("u09_yellow", 0xb000, 0x1000, CRC(03fac294) SHA1(5a9d72978318c61185efd4bc9e4a868c226465b8) ) // TMS2532JL-45
	ROM_LOAD("u10_green",  0xc000, 0x1000, CRC(97727bd2) SHA1(68cb931db0fc705959d3f59271923602918c3892) ) // "
	ROM_LOAD("u11_black",  0xd000, 0x1000, CRC(98bd01b7) SHA1(48cc560c4ca736f54e30d757990ff403c05c39ae) ) // "
	ROM_LOAD("u12_blue",   0xe000, 0x1000, CRC(f6f1e21c) SHA1(b60a02fee3a9c8f8322da3194170c167a8cca3b7) ) // "
	ROM_LOAD("u13_red",    0xf000, 0x1000, CRC(d5076ebd) SHA1(d2a93256b5ed74301ab4e5150b5b5816eb288be5) ) // "

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

ROM_START( fpresa ) // serial 0000076x
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("u09_yellow", 0xb000, 0x1000, CRC(03fac294) SHA1(5a9d72978318c61185efd4bc9e4a868c226465b8) ) // TMS2532JL-45
	ROM_LOAD("u10_green",  0xc000, 0x1000, CRC(97727bd2) SHA1(68cb931db0fc705959d3f59271923602918c3892) ) // "
	ROM_LOAD("u11_black",  0xd000, 0x1000, CRC(98bd01b7) SHA1(48cc560c4ca736f54e30d757990ff403c05c39ae) ) // "
	ROM_LOAD("u12_blue",   0xe000, 0x1000, CRC(6f18115f) SHA1(a08b3a66bfdc23f3400e03fe253a8b9a4967d14f) ) // "
	ROM_LOAD("u13_red",    0xf000, 0x1000, CRC(1e044d0c) SHA1(2bb7a22ef5c4af7d02d88b250adfc8c313a435fc) ) // "

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

ROM_START( fpresb ) // serial ?
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("u09_yellow", 0xb000, 0x1000, CRC(03fac294) SHA1(5a9d72978318c61185efd4bc9e4a868c226465b8) )
	ROM_LOAD("u10_green",  0xc000, 0x1000, CRC(5d049d5e) SHA1(c7359bead92729e8a92d6cf1789d87ae43d23cbf) )
	ROM_LOAD("u11_black",  0xd000, 0x1000, CRC(98bd01b7) SHA1(48cc560c4ca736f54e30d757990ff403c05c39ae) )
	ROM_LOAD("u12_blue",   0xe000, 0x1000, CRC(6f18115f) SHA1(a08b3a66bfdc23f3400e03fe253a8b9a4967d14f) )
	ROM_LOAD("u13_red",    0xf000, 0x1000, CRC(dea8091d) SHA1(1d94a90ae076215c2c009e78ec4919dbd8467ef8) )

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

ROM_START( fpresc ) // serial 0000011x, but updated EPROMs
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("u09", 0xb000, 0x1000, CRC(0782e41e) SHA1(7d3fd37b00e34aa214b56e591d175ea1b6b334f9) ) // TMS2532JL-45, yellow sticker on all
	ROM_LOAD("u10", 0xc000, 0x1000, CRC(6fe6871e) SHA1(882cd8aee8e2a56684cb64a1faa343432bc190aa) ) // "
	ROM_LOAD("u11", 0xd000, 0x1000, CRC(92eb49b5) SHA1(e4fa506f7991e90afd2184b62c28774af023925c) ) // "
	ROM_LOAD("u12", 0xe000, 0x1000, CRC(4916bdfd) SHA1(5fbc06a93b0edc64b04ad192887e81b03c7d40aa) ) // "
	ROM_LOAD("u13", 0xf000, 0x1000, CRC(87ef02f4) SHA1(ac223927d1c6adb4c2d0a008f34be93e52e522f9) ) // "

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

ROM_START( fpresd ) // serial 0000015x
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("u09_yellow", 0xb000, 0x1000, CRC(4d6084fb) SHA1(2c846b3003f347973197b0837c8791cbdf4e1aa3) ) // TMS2532JL-45
	ROM_LOAD("u10_green",  0xc000, 0x1000, CRC(6fe6871e) SHA1(882cd8aee8e2a56684cb64a1faa343432bc190aa) ) // "
	ROM_LOAD("u11_black",  0xd000, 0x1000, CRC(92eb49b5) SHA1(e4fa506f7991e90afd2184b62c28774af023925c) ) // "
	ROM_LOAD("u12_blue",   0xe000, 0x1000, CRC(84cde5ca) SHA1(d60f49e1e92b44bc2ddb9ddada46155cbad27590) ) // "
	ROM_LOAD("u13_red",    0xf000, 0x1000, CRC(46668eb1) SHA1(434a5b6909571f9fc2080fb0962d94d7e8833a79) ) // "

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

ROM_START( fprese ) // serial 0000003x
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("u09_yellow", 0xb000, 0x1000, CRC(4d6084fb) SHA1(2c846b3003f347973197b0837c8791cbdf4e1aa3) ) // TMS2532JDL-45
	ROM_LOAD("u10_green",  0xc000, 0x1000, CRC(6fe6871e) SHA1(882cd8aee8e2a56684cb64a1faa343432bc190aa) ) // "
	ROM_LOAD("u11_black",  0xd000, 0x1000, CRC(6316adc5) SHA1(a98a0c6cc0a9f823a70bae9a4b3bce538d0f58af) ) // "
	ROM_LOAD("u12_blue",   0xe000, 0x1000, CRC(7c6ed6cc) SHA1(8a39678fc28340c208b3da60a83dbf0b509c6604) ) // "
	ROM_LOAD("u13_red",    0xf000, 0x1000, CRC(46668eb1) SHA1(434a5b6909571f9fc2080fb0962d94d7e8833a79) ) // "

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

ROM_START( fpresbu )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("u09_yellow", 0xb000, 0x1000, CRC(bb1cb486) SHA1(b83f50a3ef361d254b88eefaa5aac657aaa72375) )
	ROM_LOAD("u10_green",  0xc000, 0x1000, CRC(af0aec0e) SHA1(8293d00a12efa1c142b9e37bc7786012250536d9) )
	ROM_LOAD("u11_black",  0xd000, 0x1000, CRC(214a91cc) SHA1(aab07ecdd66ac208874f4053fc4b0b0659b017aa) )
	ROM_LOAD("u12_blue",   0xe000, 0x1000, CRC(dae4d8e4) SHA1(f06dbb643f0324c0bddaaae9537d5829768bda22) )
	ROM_LOAD("u13_red",    0xf000, 0x1000, CRC(5fb67708) SHA1(1e9ee724c2be38daf39d5cf37b0ae587e408777c) )

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

ROM_START( fpresgla )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("u09_yellow", 0xb000, 0x1000, CRC(b0a7e8f1) SHA1(f703d56917ea0e2d1dc553e0a9fd5aaf99a8328b) )
	ROM_LOAD("u10_green",  0xc000, 0x1000, CRC(32606946) SHA1(26333f19ec103111f2df83fda9a5660bcfc8d1ec) )
	ROM_LOAD("u11_black",  0xd000, 0x1000, CRC(cecc2094) SHA1(e7860fbcf5a7ef2aa11f7c1e509b1a5de2acfd33) )
	ROM_LOAD("u12_blue",   0xe000, 0x1000, CRC(fe1f25f1) SHA1(3e6b234cb9f05919fead386c9501b3f320db9f10) )
	ROM_LOAD("u13_red",    0xf000, 0x1000, CRC(64d337d2) SHA1(c7573bc15356ddd6e538730d7f059ffa7195d5bf) )

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END


ROM_START( feas )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("elite", 0x8000, 0x0800, CRC(cc904af9) SHA1(2b1d54c597a445ccc1ec73838e4bd2ac5154d7ad) ) // Intel D2764-2
	ROM_CONTINUE( 0x9000, 0x0800 )
	ROM_CONTINUE( 0x8800, 0x0800 )
	ROM_CONTINUE( 0x9800, 0x0800 )
	ROM_LOAD("101-1052a02", 0xc000, 0x2000, CRC(fee42863) SHA1(1854bb9cddb883d86e0d98594ac0186fb3908a01) ) // Mostek MK36C63N-5
	ROM_LOAD("101-1052a01", 0xe000, 0x2000, CRC(41261e1b) SHA1(43664e30348a708fbe449b47e6eed39d4e9acb4c) ) // "

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) ) // NEC D2332C
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) ) // NEC D2364C
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) ) // "
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) ) // "
ROM_END

ROM_START( feasbu )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("eli_bu3", 0x8000, 0x0800, CRC(93dcc23b) SHA1(2eb8c5a85e566948bc256d6b1804694e6b0ffa6f) )
	ROM_CONTINUE( 0x9000, 0x0800 )
	ROM_CONTINUE( 0x8800, 0x0800 )
	ROM_CONTINUE( 0x9800, 0x0800 )
	ROM_LOAD("eli_bu1", 0xc000, 0x2000, CRC(859d69f1) SHA1(a8b057683369e2387f22fc7e916b6f3c75d44b21) )
	ROM_LOAD("eli_bu2", 0xe000, 0x2000, CRC(571a33a7) SHA1(43b110cf0918caf16643178f401e58b2dc73894f) )

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

ROM_START( feasbua ) // model EWC
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("white_a", 0x8000, 0x0800, CRC(93dcc23b) SHA1(2eb8c5a85e566948bc256d6b1804694e6b0ffa6f) ) // HN482764G-2
	ROM_CONTINUE( 0x9000, 0x0800 )
	ROM_CONTINUE( 0x8800, 0x0800 )
	ROM_CONTINUE( 0x9800, 0x0800 )
	ROM_LOAD("black", 0xc000, 0x0800, CRC(132b7f68) SHA1(a7aaac221387275f3a11f4441dcef73c2bfd0ee6) ) // M5L2764K
	ROM_CONTINUE( 0xd000, 0x0800 )
	ROM_CONTINUE( 0xc800, 0x0800 )
	ROM_CONTINUE( 0xd800, 0x0800 )
	ROM_LOAD("green", 0xe000, 0x0800, CRC(c7bbfbbe) SHA1(63fe13d0e64d1e5c1ea1b4de13ac3e753797a992) ) // M5L2764K - only 4 bytes different from feasbu (after descramble)
	ROM_CONTINUE( 0xf000, 0x0800 )
	ROM_CONTINUE( 0xe800, 0x0800 )
	ROM_CONTINUE( 0xf800, 0x0800 )

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

ROM_START( feasgla )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("4.0_8_6", 0x8000, 0x0800, CRC(32784e2d) SHA1(dae060a5c49cc1993a78db293cd80464adfd892d) )
	ROM_CONTINUE( 0x9000, 0x0800 )
	ROM_CONTINUE( 0x8800, 0x0800 )
	ROM_CONTINUE( 0x9800, 0x0800 )
	ROM_LOAD("4.0_c_5", 0xc000, 0x0800, CRC(ddb80412) SHA1(b1d9435d9a71b8eb241a2169bfbaa0499f510769) )
	ROM_CONTINUE( 0xd000, 0x0800 )
	ROM_CONTINUE( 0xc800, 0x0800 )
	ROM_CONTINUE( 0xd800, 0x0800 )
	ROM_LOAD("4.0_e_4", 0xe000, 0x0800, CRC(62a5305a) SHA1(a361bd9a54b903d7b0fbacabe55ea5ccbbc1dc51) )
	ROM_CONTINUE( 0xf000, 0x0800 )
	ROM_CONTINUE( 0xe800, 0x0800 )
	ROM_CONTINUE( 0xf800, 0x0800 )

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

ROM_START( feasglaa ) // model EAS-C
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("orange", 0x8000, 0x0800, CRC(32784e2d) SHA1(dae060a5c49cc1993a78db293cd80464adfd892d) )
	ROM_CONTINUE( 0x9000, 0x0800 )
	ROM_CONTINUE( 0x8800, 0x0800 )
	ROM_CONTINUE( 0x9800, 0x0800 )
	ROM_LOAD("black", 0xc000, 0x0800, CRC(3f0b01b6) SHA1(fe8d214f1678e000ba945e2f6dc3438af97c6f33) ) // only 2 bytes different from feasgla
	ROM_CONTINUE( 0xd000, 0x0800 )
	ROM_CONTINUE( 0xc800, 0x0800 )
	ROM_CONTINUE( 0xd800, 0x0800 )
	ROM_LOAD("green", 0xe000, 0x0800, CRC(62a5305a) SHA1(a361bd9a54b903d7b0fbacabe55ea5ccbbc1dc51) )
	ROM_CONTINUE( 0xf000, 0x0800 )
	ROM_CONTINUE( 0xe800, 0x0800 )
	ROM_CONTINUE( 0xf800, 0x0800 )

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

ROM_START( feasglab )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("6a", 0x8000, 0x0800, CRC(2fdddb4f) SHA1(6da0a328a45462f285ae6a0756f97c5a43148f97) )
	ROM_CONTINUE( 0x9000, 0x0800 )
	ROM_CONTINUE( 0x8800, 0x0800 )
	ROM_CONTINUE( 0x9800, 0x0800 )
	ROM_LOAD("5a", 0xc000, 0x0800, CRC(f094e625) SHA1(fef84c6a3da504aac15988ec9af94417e5fedfbd) )
	ROM_CONTINUE( 0xd000, 0x0800 )
	ROM_CONTINUE( 0xc800, 0x0800 )
	ROM_CONTINUE( 0xd800, 0x0800 )
	ROM_LOAD("4a", 0xe000, 0x0800, CRC(5f6845d1) SHA1(684eb16faf36a49560e5a73b55fd0022dc090e35) )
	ROM_CONTINUE( 0xf000, 0x0800 )
	ROM_CONTINUE( 0xe800, 0x0800 )
	ROM_CONTINUE( 0xf800, 0x0800 )

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

ROM_START( feasx ) // 510-1071B01 PCB
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("exp_8000_6", 0xa000, 0x2000, CRC(b555c5ab) SHA1(d85ae44f0c13e2bfafbc3cadf829e74c7f4ba2e3) ) // Seeq DQ5133-25
	ROM_LOAD("exp_c000_5", 0xc000, 0x2000, CRC(fd8471e3) SHA1(e684ded8ed4934bc5ef0cc4ae37dc5d12496d39e) ) // "
	ROM_LOAD("exp_e000_4", 0xe000, 0x2000, CRC(19c36d83) SHA1(33438c316284182ebe195a383bb2d96d3524c88d) ) // "

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END


ROM_START( feag ) // model 6081, aka "Mobile Master" - checksum BE41 9B27 E959 42C1
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("eg_orange.ic9", 0xa000, 0x2000, CRC(df9e7e74) SHA1(db76750eba5515213ecce07402c4d974c14e1a23) ) // M5L2764K, orange sticker
	ROM_LOAD("eg_black.ic5",  0xc000, 0x2000, CRC(a5f6f295) SHA1(319f00d4b7a1704a3ca722c40f4096004b4b89d2) ) // M5L2764K, black sticker
	ROM_LOAD("eg_green.ic4",  0xe000, 0x2000, CRC(1dc6508a) SHA1(6f2e730b216bfb900074d1d786124fc3cb038a8d) ) // M5L2764K, green sticker

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107.ic16", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(                 0x1000, 0x1000)
	ROMX_LOAD("101-64101.ic16", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105.ic16", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106.ic16", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

ROM_START( feagpriv ) // based on feag
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("e86_2_umbau", 0x8000, 0x8000, CRC(511c2203) SHA1(78c1aa68c51b8fa45578945ceb79fe0c72f828a1) ) // HN27256G-25

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107.ic16", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(                 0x1000, 0x1000)
	ROMX_LOAD("101-64101.ic16", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105.ic16", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106.ic16", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

ROM_START( feag2100 ) // model 6088 - checksum F361 9D5E 1D31 ADF0
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("2100_c_black.ic5",  0xc000, 0x2000, CRC(454eb839) SHA1(83d206464c194b022d43913b5f4092a8201f36b9) )
	ROM_LOAD("2100_c_green.ic4",  0xe000, 0x2000, CRC(f1f76a63) SHA1(337b4572b743d383c6a12c360875d37682de3647) )

	ROM_REGION( 0x8000, "rombank", 0 )
	ROM_LOAD("2100_c_orange.ic9", 0x0000, 0x8000, CRC(feeff71c) SHA1(87614ca850848581d946193efa317181ef9c7a09) )

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107.ic16", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(                 0x1000, 0x1000)
	ROMX_LOAD("101-64101.ic16", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105.ic16", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106.ic16", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

ROM_START( feag2100a ) // checksum F234 9D4A 2373 B2F1
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("el2100_2.ic5", 0xc000, 0x2000, CRC(76fec42f) SHA1(34660edb8458919fd179e93fdab3fe428a6625d0) )
	ROM_LOAD("el2100_3.ic4", 0xe000, 0x2000, CRC(2079a506) SHA1(a7bb83138c7b6eff6ea96702d453a214697f4890) )

	ROM_REGION( 0x8000, "rombank", 0 )
	ROM_LOAD("el2100_1.ic9", 0x0000, 0x8000, CRC(9b62b7d5) SHA1(cfcaea2e36c2d52fe4a85c77dbc7fa135893860c) )

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 3, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107.ic16", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(                 0x1000, 0x1000)
	ROMX_LOAD("101-64101.ic16", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105.ic16", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106.ic16", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT  COMPAT  MACHINE   INPUT  CLASS        INIT        COMPANY, FULLNAME, FLAGS
SYST( 1982, fpres,     0,      0,      pc,       pc,    elite_state, empty_init, "Fidelity Electronics", "Prestige Challenger (original program, set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1982, fpresa,    fpres,  0,      pc,       pc,    elite_state, empty_init, "Fidelity Electronics", "Prestige Challenger (original program, set 2)", MACHINE_SUPPORTS_SAVE )
SYST( 1982, fpresb,    fpres,  0,      pc,       pc,    elite_state, empty_init, "Fidelity Electronics", "Prestige Challenger (original program, set 3)", MACHINE_SUPPORTS_SAVE )
SYST( 1982, fpresc,    fpres,  0,      pc,       pc,    elite_state, empty_init, "Fidelity Electronics", "Prestige Challenger (original program, set 4)", MACHINE_SUPPORTS_SAVE )
SYST( 1982, fpresd,    fpres,  0,      pc,       pc,    elite_state, empty_init, "Fidelity Electronics", "Prestige Challenger (original program, set 5)", MACHINE_SUPPORTS_SAVE )
SYST( 1982, fprese,    fpres,  0,      pc,       pc,    elite_state, empty_init, "Fidelity Electronics", "Prestige Challenger (original program, set 6)", MACHINE_SUPPORTS_SAVE )
SYST( 1983, fpresbu,   fpres,  0,      pc,       pc,    elite_state, empty_init, "Fidelity Electronics", "Prestige Challenger (Budapest program)", MACHINE_SUPPORTS_SAVE )
SYST( 1984, fpresgla,  fpres,  0,      pc,       pc,    elite_state, empty_init, "Fidelity Electronics", "Prestige Challenger (Glasgow program)", MACHINE_SUPPORTS_SAVE )

SYST( 1983, feas,      0,      0,      eas,      eas,   elite_state, empty_init, "Fidelity Electronics", "Elite A/S Challenger (original program)", MACHINE_SUPPORTS_SAVE )
SYST( 1983, feasbu,    feas,   0,      eas,      eas,   elite_state, empty_init, "Fidelity Electronics", "Elite A/S Challenger (Budapest program, model EAS)", MACHINE_SUPPORTS_SAVE )
SYST( 1983, feasbua,   feas,   0,      ewc,      ewc,   elite_state, empty_init, "Fidelity Electronics", "Elite A/S Challenger (Budapest program, model EWC)", MACHINE_SUPPORTS_SAVE )
SYST( 1984, feasgla,   feas,   0,      easc,     easc,  elite_state, empty_init, "Fidelity Electronics", "Elite A/S Challenger (Glasgow program, set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1984, feasglaa,  feas,   0,      easc,     easc,  elite_state, empty_init, "Fidelity Electronics", "Elite A/S Challenger (Glasgow program, set 2)", MACHINE_SUPPORTS_SAVE )
SYST( 1984, feasglab,  feas,   0,      easc,     easc,  elite_state, empty_init, "Fidelity Electronics", "Elite A/S Challenger (Glasgow program, set 3)", MACHINE_SUPPORTS_SAVE )
SYST( 1985, feasx,     feas,   0,      easx,     easx,  elite_state, empty_init, "Fidelity Computer Products", "Elite A/S Challenger (experimental)", MACHINE_SUPPORTS_SAVE )

SYST( 1986, feag,      0,      0,      eag,      eag,   elite_state, init_eag,   "Fidelity International", "Elite Avant Garde (model 6081)", MACHINE_SUPPORTS_SAVE )
SYST( 1986, feagpriv,  feag,   0,      eagpriv,  easx,  elite_state, empty_init, "Fidelity Deutschland", "Elite Private Line (EAG 6081 conversion)", MACHINE_SUPPORTS_SAVE )
SYST( 1986, feag2100,  feag,   0,      eag2100,  eag,   elite_state, init_eag,   "Fidelity International", "Elite Avant Garde 2100 (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1986, feag2100a, feag,   0,      eag2100,  eag,   elite_state, init_eag,   "Fidelity International", "Elite Avant Garde 2100 (set 2)", MACHINE_SUPPORTS_SAVE )



elwro800.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Mariusz Wojcieszek
/***************************************************************************

        Elwro 800 Junior

        Driver by Mariusz Wojcieszek

        ToDo:
        - 8251 DTR and DTS signals are connected (with some additional logic) to NMI of Z80, this
          is not emulated
        - 8251 is used for JUNET network (a network of Elwro 800 Junior computers, allows sharing
          floppy disc drives and printers) - network is not emulated

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

#include "emu.h"
#include "spectrum.h"

/* Components */
#include "cpu/z80/z80.h"
#include "machine/upd765.h" /* for floppy disc controller */
#include "machine/i8255.h"
#include "bus/centronics/ctronics.h"
#include "machine/i8251.h"

/* Devices */
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "formats/tzx_cas.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class elwro800_state : public spectrum_state
{
public:
	elwro800_state(const machine_config &mconfig, device_type type, const char *tag) :
		spectrum_state(mconfig, type, tag),
		m_i8251(*this, "i8251"),
		m_i8255(*this, "ppi8255"),
		m_centronics(*this, "centronics"),
		m_upd765(*this, "upd765"),
		m_flop(*this, "upd765:%u", 0U),
		m_bank1{{*this, "bank1"}, {*this, "m1bank1"}},
		m_bank2{{*this, "bank2"}, {*this, "m1bank2"}},
		m_io_ports(*this, {"LINE7", "LINE6", "LINE5", "LINE4", "LINE3", "LINE2", "LINE1", "LINE0", "LINE8"}),
		m_io_line9(*this, "LINE9"),
		m_io_network_id(*this, "NETWORK_ID")
	{
	}

	void elwro800(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	/* NR signal */
	uint8_t m_NR;

	uint8_t nmi_r();
	void elwro800jr_fdc_control_w(uint8_t data);
	uint8_t elwro800jr_io_r(offs_t offset);
	void elwro800jr_io_w(offs_t offset, uint8_t data);
	uint8_t i8255_port_c_r();
	void i8255_port_c_w(uint8_t data);
	void write_centronics_ack(int state);

	void elwro800_io(address_map &map) ATTR_COLD;
	void elwro800_m1(address_map &map) ATTR_COLD;
	void elwro800_mem(address_map &map) ATTR_COLD;

	required_device<i8251_device> m_i8251;
	required_device<i8255_device> m_i8255;
	required_device<centronics_device> m_centronics;
	required_device<upd765a_device> m_upd765;
	required_device_array<floppy_connector, 2> m_flop;
	memory_view m_bank1[2];
	memory_view m_bank2[2];
	required_ioport_array<9> m_io_ports;
	required_ioport m_io_line9;
	required_ioport m_io_network_id;

	void elwro800jr_mmu_w(uint8_t data);

	int m_centronics_ack;
};


/*************************************
 *
 * When RAM is mapped at 0x0000 - 0x1fff (in CP/J mode), reading a location 66 with /M1=0
 * (effectively reading NMI vector) is hardwired to return 0xDF (RST #18)
 * (note that in CP/J mode address 66 is used for FCB)
 *
 *************************************/

uint8_t elwro800_state::nmi_r()
{
	return 0xdf;
}

/*************************************
 *
 *  UPD765/Floppy drive
 *
 *************************************/

void elwro800_state::elwro800jr_fdc_control_w(uint8_t data)
{
	for (int i = 0; i < 2; i++)
		if (m_flop[i]->get_device())
			m_flop[i]->get_device()->mon_w(!BIT(data, i));

	m_upd765->tc_w(data & 0x04);

	if(!(data & 8))
		m_upd765->reset();
}

/*************************************
 *
 *  I/O port F7: memory mapping
 *
 *************************************/

void elwro800_state::elwro800jr_mmu_w(uint8_t data)
{
	uint8_t *prom = memregion("proms")->base() + 0x200;
	uint8_t *messram = m_ram->pointer();
	uint8_t cs;
	uint8_t ls175;

	ls175 = bitswap<8>(data, 7, 6, 5, 4, 4, 5, 7, 6) & 0x0f;

	cs = prom[((0x0000 >> 10) | (ls175 << 6)) & 0x1ff];
	if (!BIT(cs,0))
	{
		// rom BAS0
		for (memory_view &bank1 : m_bank1)
			bank1.select(1);
	}
	else if (!BIT(cs,4))
	{
		// rom BOOT
		for (memory_view &bank1 : m_bank1)
			bank1.select(2);
	}
	else
	{
		// RAM
		for (memory_view &bank1 : m_bank1)
			bank1.select(0);
	}

	cs = prom[((0x2000 >> 10) | (ls175 << 6)) & 0x1ff];
	if (!BIT(cs,1))
	{
		for (memory_view &bank2 : m_bank2)
			bank2.select(1); // BAS1 ROM
	}
	else
	{
		for (memory_view &bank2 : m_bank2)
			bank2.select(0); // RAM
	}

	if (BIT(ls175,2))
	{
		// relok
		m_screen_location = messram + 0xe000;
	}
	else
	{
		m_screen_location = messram + 0x4000;
	}

	m_NR = BIT(ls175,3);
	if (BIT(ls175,3))
	{
		logerror("Reading network number\n");
	}
}

/*************************************
 *
 *  8255: joystick and Centronics printer connections
 *
 *************************************/

void elwro800_state::write_centronics_ack(int state)
{
	m_centronics_ack = state;
	m_i8255->pc2_w(state);
}

uint8_t elwro800_state::i8255_port_c_r()
{
	return m_centronics_ack << 2;
}

void elwro800_state::i8255_port_c_w(uint8_t data)
{
	m_centronics->write_strobe((data >> 7) & 0x01);
}

/*************************************
 *
 *  I/O reads and writes
 *
 *  I/O accesses are decoded by prom which uses 8 low address lines (A0-A8) as input
 *  and outputs chip select signals for system components. Standard addresses are:
 *
 *  0x1F: 8255 port A (joystick)
 *  0xBE: 8251 data (Junet network)
 *  0xBF: 8251 control/status (Junet network)
 *  0xDC: 8255 control
 *  0xDD: 8255 port C (centronics 7-strobe, 2-ack)
 *  0xDE: 8255 port B (centronics data)
 *  0xDF: 8255 port A (joystick)
 *  0xEE: FDC 765A status
 *  0xEF: FDC 765A command and data
 *  0xF1: FDC control (motor on/off)
 *  0xF7: memory banking
 *  0xFE (write): border color, speaker and tape (as in Spectrum)
 *  0x??FE, 0x??7F, 0x??7B (read): keyboard reading
 *************************************/

uint8_t elwro800_state::elwro800jr_io_r(offs_t offset)
{
	uint8_t *prom = memregion("proms")->base();
	uint8_t cs = prom[offset & 0x1ff];

	if (!BIT(cs,0))
	{
		// CFE
		int mask = 0x8000;
		int data = 0xff;
		int i;

		if ( !m_NR )
		{
			for (i = 0; i < 9; mask >>= 1, i++)
			{
				if (!(offset & mask))
				{
					data &= m_io_ports[i]->read();
				}
			}

			if ((offset & 0xff) == 0xfb)
			{
				data &= m_io_line9->read();
			}

			/* cassette input from wav */
			if (m_cassette->input() > 0.0038 )
			{
				data |= 0x40;
			}
		}
		else
		{
			data = m_io_network_id->read();
		}

		return data;
	}
	else if (!BIT(cs,1))
	{
		// CF7
	}
	else if (!BIT(cs,2))
	{
		// CS55
		return m_i8255->read((offset & 0x03) ^ 0x03);
	}
	else if (!BIT(cs,3))
	{
		// CSFDC
		if (offset & 1)
		{
			return m_upd765->fifo_r();
		}
		else
		{
			return m_upd765->msr_r();
		}
	}
	else if (!BIT(cs,4))
	{
		// CS51
		return m_i8251->read(offset & 1);
	}
	else if (!BIT(cs,5))
	{
		// CF1
	}
	else
	{
		logerror("Unmapped I/O read: %04x\n", offset);
	}
	return 0x00;
}

void elwro800_state::elwro800jr_io_w(offs_t offset, uint8_t data)
{
	uint8_t *prom = memregion("proms")->base();
	uint8_t cs = prom[offset & 0x1ff];

	if (!BIT(cs,0))
	{
		// CFE
		spectrum_ula_w(offset, data);
	}
	else if (!BIT(cs,1))
	{
		// CF7
		elwro800jr_mmu_w(data);
	}
	else if (!BIT(cs,2))
	{
		// CS55
		m_i8255->write((offset & 0x03) ^ 0x03, data);
	}
	else if (!BIT(cs,3))
	{
		// CSFDC
		if (offset & 1)
		{
			m_upd765->fifo_w(data);
		}
	}
	else if (!BIT(cs,4))
	{
		// CS51
		m_i8251->write(offset & 1, data);
	}
	else if (!BIT(cs,5))
	{
		// CF1
		elwro800jr_fdc_control_w(data);
	}
	else
	{
		logerror("Unmapped I/O write: %04x %02x\n", offset, data);
	}
}

/*************************************
 *
 *  Memory maps
 *
 *************************************/

void elwro800_state::elwro800_mem(address_map &map)
{
	map(0x0000, 0x1fff).view(m_bank1[0]);
	m_bank1[0][0](0x0000, 0x1fff).bankrw("rambank1");
	m_bank1[0][1](0x0000, 0x1fff).rom().region("maincpu", 0x0000).nopw(); // BAS0 ROM
	m_bank1[0][2](0x0000, 0x1fff).rom().region("maincpu", 0x4000).nopw(); // BOOT ROM
	map(0x2000, 0x3fff).view(m_bank2[0]);
	m_bank2[0][0](0x2000, 0x3fff).bankrw("rambank2");
	m_bank2[0][1](0x2000, 0x3fff).rom().region("maincpu", 0x2000).nopw(); // BAS1 ROM
	map(0x4000, 0xffff).bankrw("rambank3");
}

void elwro800_state::elwro800_io(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(elwro800_state::elwro800jr_io_r), FUNC(elwro800_state::elwro800jr_io_w));
}

void elwro800_state::elwro800_m1(address_map &map)
{
	map(0x0000, 0x1fff).view(m_bank1[1]);
	m_bank1[1][0](0x0000, 0x1fff).bankrw("rambank1");
	m_bank1[1][0](0x0066, 0x0066).r(FUNC(elwro800_state::nmi_r));
	m_bank1[1][1](0x0000, 0x1fff).rom().region("maincpu", 0x0000).nopw(); // BAS0 ROM
	m_bank1[1][2](0x0000, 0x1fff).rom().region("maincpu", 0x4000).nopw(); // BOOT ROM
	map(0x2000, 0x3fff).view(m_bank2[1]);
	m_bank2[1][0](0x2000, 0x3fff).bankrw("rambank2");
	m_bank2[1][1](0x2000, 0x3fff).rom().region("maincpu", 0x2000).nopw(); // BAS1 ROM
	map(0x4000, 0xffff).bankrw("rambank3");
}

/*************************************
 *
 *  Input ports
 *
 *************************************/

static INPUT_PORTS_START( elwro800 )
	PORT_START("LINE0") /* 0xFEFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CAPS SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)     PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)     PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)     PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)     PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(":    *") PORT_CODE(KEYCODE_EQUALS)       PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";    +") PORT_CODE(KEYCODE_COLON)        PORT_CHAR(';') PORT_CHAR('+')

	PORT_START("LINE1") /* 0xFDFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)     PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)     PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)     PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)     PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)     PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-    =") PORT_CODE(KEYCODE_MINUS)        PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[    {") PORT_CODE(KEYCODE_OPENBRACE)    PORT_CHAR('[') PORT_CHAR('{')

	PORT_START("LINE2") /* 0xFBFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".    >") PORT_CODE(KEYCODE_STOP)         PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",    <") PORT_CODE(KEYCODE_COMMA)        PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("LINE3") /* 0xF7FE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1    !") PORT_CODE(KEYCODE_1)    PORT_CHAR('1') PORT_CHAR(0xff) PORT_CHAR('!')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2   \"") PORT_CODE(KEYCODE_2)    PORT_CHAR('2') PORT_CHAR(0xff) PORT_CHAR('"')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3    #") PORT_CODE(KEYCODE_3)    PORT_CHAR('3') PORT_CHAR(0xff) PORT_CHAR('#')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4    $") PORT_CODE(KEYCODE_4)    PORT_CHAR('4') PORT_CHAR(0xff) PORT_CHAR('$')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5    %") PORT_CODE(KEYCODE_5)    PORT_CHAR('5') PORT_CHAR(0xff) PORT_CHAR('%')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/    ?") PORT_CODE(KEYCODE_SLASH)        PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@    \\") PORT_CODE(KEYCODE_BACKSLASH)   PORT_CHAR('@') PORT_CHAR('\\')

	PORT_START("LINE4") /* 0xEFFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0    _") PORT_CODE(KEYCODE_0)    PORT_CHAR('0') PORT_CHAR(0xff) PORT_CHAR('_')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9    )") PORT_CODE(KEYCODE_9)    PORT_CHAR('9') PORT_CHAR(0xff) PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8    (") PORT_CODE(KEYCODE_8)    PORT_CHAR('8') PORT_CHAR(0xff) PORT_CHAR('(')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7    '") PORT_CODE(KEYCODE_7)    PORT_CHAR('7') PORT_CHAR(0xff) PORT_CHAR('\'')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6    &") PORT_CODE(KEYCODE_6)    PORT_CHAR('6') PORT_CHAR(0xff) PORT_CHAR('&')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL)     PORT_CHAR(UCHAR_MAMEKEY(DEL),127)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]    }") PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_CHAR(']') PORT_CHAR('}')

	PORT_START("LINE5") /* 0xDFFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC)             PORT_CHAR(UCHAR_MAMEKEY(ESC),27)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BS") PORT_CODE(KEYCODE_BACKSPACE)        PORT_CHAR(UCHAR_MAMEKEY(BACKSPACE),8)

	PORT_START("LINE6") /* 0xBFFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TAB") PORT_CODE(KEYCODE_TAB)                 PORT_CHAR('\t')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT LOCK") PORT_CODE(KEYCODE_CAPSLOCK)     PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))

	PORT_START("LINE7") /* 0x7FFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SYMBOL SHIFT") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^    -") PORT_CODE(KEYCODE_TILDE)    PORT_CHAR('^')

	PORT_START("LINE8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ę") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ó") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ś") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ł") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ź") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ń") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ą") PORT_CODE(KEYCODE_6_PAD)

	PORT_START("LINE9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DIR") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cursor Down") PORT_CODE(KEYCODE_DOWN)    PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cursor Up") PORT_CODE(KEYCODE_UP)        PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cursor Right") PORT_CODE(KEYCODE_RIGHT)  PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cursor Left") PORT_CODE(KEYCODE_LEFT)    PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ć") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ż") PORT_CODE(KEYCODE_8_PAD)

	PORT_START("JOY")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P1 Joystick Right") PORT_CODE(JOYCODE_X_RIGHT_SWITCH)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P1 Joystick Left") PORT_CODE(JOYCODE_X_LEFT_SWITCH)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P1 Joystick Down") PORT_CODE(JOYCODE_Y_DOWN_SWITCH)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P1 Joystick Up") PORT_CODE(JOYCODE_Y_UP_SWITCH)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P1 Joystick Fire") PORT_CODE(JOYCODE_BUTTON1)

	PORT_START("NETWORK_ID")
	PORT_DIPNAME( 0x3f, 0x01, "Computer network ID" )
	PORT_DIPSETTING( 0x01, "1" )
	PORT_DIPSETTING( 0x10, "16" )
	PORT_DIPSETTING( 0x11, "17" )

INPUT_PORTS_END

/*************************************
 *
 *  Machine
 *
 *************************************/

void elwro800_state::machine_reset()
{
	spectrum_state::machine_reset();

	uint8_t *messram = m_ram->pointer();

	memset(messram, 0, 64*1024);

	membank("rambank1")->set_base(messram + 0x0000);
	membank("rambank2")->set_base(messram + 0x2000);
	membank("rambank3")->set_base(messram + 0x4000);

	m_port_7ffd_data = 0;
	m_port_1ffd_data = -1;

	// this is a reset of ls175 in mmu
	elwro800jr_mmu_w(0);
}

static void elwro800jr_floppies(device_slot_interface &device)
{
	device.option_add("525hd", FLOPPY_525_HD);
}

/* F4 Character Displayer */
static const gfx_layout elwro800_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	128,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_elwro800 )
	GFXDECODE_ENTRY( "maincpu", 0x3c00, elwro800_charlayout, 0, 8 )
GFXDECODE_END


void elwro800_state::elwro800(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 14_MHz_XTAL / 4);    /* 3.5 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &elwro800_state::elwro800_mem);
	m_maincpu->set_addrmap(AS_IO, &elwro800_state::elwro800_io);
	m_maincpu->set_addrmap(AS_OPCODES, &elwro800_state::elwro800_m1);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	rectangle visarea = { get_screen_area().left() - SPEC_LEFT_BORDER, get_screen_area().right() + SPEC_RIGHT_BORDER,
		get_screen_area().top() - SPEC_TOP_BORDER, get_screen_area().bottom() + SPEC_BOTTOM_BORDER };
	screen.set_raw(14_MHz_XTAL / 2, SPEC_CYCLES_PER_LINE * 2, SPEC_UNSEEN_LINES + SPEC_SCREEN_HEIGHT, visarea);
	// Sync and interrupt timings determined by 2716 EPROM
	screen.set_screen_update(FUNC(elwro800_state::screen_update_spectrum));
	screen.set_palette("palette");
	screen.screen_vblank().set_inputline(m_maincpu, 0, HOLD_LINE);

	SPECTRUM_ULA_UNCONTENDED(config, m_ula); // dummy for required

	PALETTE(config, "palette", FUNC(elwro800_state::spectrum_palette), 16);
	GFXDECODE(config, "gfxdecode", "palette", gfx_elwro800);

	UPD765A(config, "upd765", 8_MHz_XTAL / 2, true, true);

	I8255A(config, m_i8255);
	m_i8255->in_pa_callback().set_ioport("JOY");
	m_i8255->in_pb_callback().set("cent_data_in", FUNC(input_buffer_device::read));
	m_i8255->out_pb_callback().set("cent_data_out", FUNC(output_latch_device::write));
	m_i8255->in_pc_callback().set(FUNC(elwro800_state::i8255_port_c_r));
	m_i8255->out_pc_callback().set(FUNC(elwro800_state::i8255_port_c_w));

	/* printer */
	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->set_data_input_buffer("cent_data_in");
	m_centronics->ack_handler().set(FUNC(elwro800_state::write_centronics_ack));

	INPUT_BUFFER(config, "cent_data_in");
	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	I8251(config, m_i8251, 14_MHz_XTAL / 4);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(tzx_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("spectrum_cass");

	FLOPPY_CONNECTOR(config, "upd765:0", elwro800jr_floppies, "525hd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "upd765:1", elwro800jr_floppies, "525hd", floppy_image_device::default_mfm_floppy_formats);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("64K");

	SOFTWARE_LIST(config, "cass_list").set_original("spectrum_cass");
}

/*************************************
 *
 *  ROM definition
 *
 *************************************/

ROM_START( elwro800 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "bas04.epr", 0x0000, 0x2000, CRC(6ab16f36) SHA1(49a19b279f311279c7fed3d2b3f207732d674c26) )
	ROM_LOAD( "bas14.epr", 0x2000, 0x2000, CRC(a743eb80) SHA1(3a300550838535b4adfe6d05c05fe0b39c47df16) )
	ROM_LOAD( "bootv.epr", 0x4000, 0x2000, CRC(de5fa37d) SHA1(4f203efe53524d84f69459c54b1a0296faa83fd9) )

	ROM_REGION(0x0c00, "proms", 0 )
	ROM_LOAD( "junior_io_prom.bin",  0x0000, 0x0200, CRC(c6a777c4) SHA1(41debc1b4c3bd4eef7e0e572327c759e0399a49c) )
	ROM_LOAD( "junior_mem_prom.bin", 0x0200, 0x0200, CRC(0f745f42) SHA1(360ec23887fb6d7e19ee85d2bb30d9fa57f4936e) )
	ROM_LOAD( "tv_2716.e11",         0x0400, 0x0800, CRC(6093e80e) SHA1(a4972f336490d15222f4f24369f1f3253cfb9516) )
ROM_END

} // Anonymous namespace

/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY  FULLNAME      FLAGS
COMP( 1986, elwro800, 0,      0,      elwro800, elwro800, elwro800_state, empty_init, "Elwro", "800-3 Junior", 0 )



elzet80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

ELZET/K
ELZET/P
BCS

All documentation is in German.

The ELZET/P is a luggable CP/M computer that from the outside looks very
much like a Kaypro II. The floppy drives could be one under the other like
the Kaypro, or could be vertically-orientated side-by-side. The drives are
80 track quad-density with 800KB capacity.
Behind the drives is a cage that can hold up to 8 slots that plug into a
small motherboard.
CARDS: (some are optional)
- Floppy Disk Controller: choice of FDC 2: (basic) or FDC3 (also has 128K RAM)
    (main chips are PIO, DMA, MB8877A)
- Dynamic RAM: choice of 64k or 256k
- Centronics (contains PIO, CTC)
- EIC (contains PIO, DART, Z80B)
- Experimenter board (contains PIO, CTC)
- Video (contains MC6845, 2k vram, 2k attr-ram, chargen roms, 15MHz xtal)
- CPU (contains Z80, SIO, PIO, 4MHz xtal)

The keyboard plugs into the front by using a stereo audio plug, like you
have on a modern computer's line-out jack. There's no internal information
for it.


The ELZET/K is a similar computer but is a slightly smaller form factor.

The BCS is a box that can be used to convert over 300 floppy-disk formats.

***************************************************************************


To Do: Everything.
Status: Just a closet skeleton


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

#include "emu.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/z80ctc.h"
#include "machine/z80dma.h"
#include "machine/z80sio.h"
#include "machine/z80pio.h"
#include "machine/wd_fdc.h"

namespace {

class elzet80_state : public driver_device
{
public:
	elzet80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ctc(*this, "ctc")
		, m_dma(*this, "dma")
		, m_pio(*this, "pio")
		, m_uart(*this, "uart")
		, m_fdc(*this, "fdc")
		, m_floppy0(*this, "fdc:0")
		, m_floppy1(*this, "fdc:1")
		//, m_io_keyboard(*this, "LINE%d", 0U)
	{ }

	void elzet80(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	floppy_image_device *m_floppy = nullptr;
	required_device<cpu_device> m_maincpu;
	required_device<z80ctc_device> m_ctc;
	required_device<z80dma_device> m_dma;
	required_device<z80pio_device> m_pio;
	required_device<z80sio_device> m_uart;
	required_device<fd1793_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	required_device<floppy_connector> m_floppy1;
	//required_ioport_array<8> m_io_keyboard;
};


void elzet80_state::mem_map(address_map &map)
{
}

void elzet80_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
}

static INPUT_PORTS_START( elzet80 )
INPUT_PORTS_END


void elzet80_state::machine_start()
{
}

void elzet80_state::machine_reset()
{
	m_floppy = nullptr;
}

static void elzet80_floppies(device_slot_interface &device)
{
	device.option_add("fdd", FLOPPY_525_QD);
}


void elzet80_state::elzet80(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &elzet80_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &elzet80_state::io_map);

	// devices
	FD1793(config, m_fdc, 1000000);    // unknown where this is derived
	FLOPPY_CONNECTOR(config, "fdc:0", elzet80_floppies, "fdd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", elzet80_floppies, "fdd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	Z80PIO(config, m_pio, 0);
	Z80SIO(config, m_uart, 0);
	Z80CTC(config, m_ctc, 0);
	Z80DMA(config, m_dma, 0);
}


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

  Game driver(s)

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


ROM_START(elzet80k)
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD( "elzet80-k_cpu.bin",                  0x0000, 0x1000, CRC(e5300137) SHA1(5a9fcb52756a2e9008d53b734b874d33b069efb5) )

	ROM_REGION(0x2000, "chargen", 0) // same roms as P?
	ROM_LOAD( "elzet80-p_video80_ic1_prom3.bin",    0x0000, 0x1000, CRC(70008d71) SHA1(048efed186861050f0e6b47fec57149319de22b6) )
	ROM_LOAD( "elzet80-p_video80_ic2_prom4.bin",    0x1000, 0x1000, CRC(8db78b54) SHA1(7d0ce2811ee1f6c179295411fd8c3c2e67ea2842) )

	ROM_REGION(0x1000, "kbd", 0)
	ROM_LOAD( "elzet80-k_tastatur_d8748.bin",       0x0000, 0x0400, CRC(78bf6f1a) SHA1(95919363e73779899cbd7c143d10c245c35ca789) )

	ROM_REGION(0x1000, "bcs", 0)
	ROM_LOAD( "elzet80-bcs_cpuiec.bin",             0x0000, 0x1000, CRC(724fe45a) SHA1(b4b01c9bf11b35c48b0a4839d7530a18259b1ae9) )
ROM_END

ROM_START(elzet80p)
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD( "elzet80-p_cpu.bin",                  0x0000, 0x1000, CRC(8b876e12) SHA1(0fb4dfe267a3fbc033fbce430d13fb22a4ad16ed) )

	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD( "elzet80-p_video80_ic1_prom3.bin",    0x0000, 0x1000, CRC(70008d71) SHA1(048efed186861050f0e6b47fec57149319de22b6) )
	ROM_LOAD( "elzet80-p_video80_ic2_prom4.bin",    0x1000, 0x1000, CRC(8db78b54) SHA1(7d0ce2811ee1f6c179295411fd8c3c2e67ea2842) )

	ROM_REGION(0x1000, "kbd", 0)
	ROM_LOAD( "elzet80-p_tastatur_jk_ti2732.bin",  0x0000, 0x1000, CRC(88be9b29) SHA1(b83603dadce9d7e7367d1782ee78189156fe9a60) )

	ROM_REGION(0x1000, "bcs", 0)
	ROM_LOAD( "elzet80-bcs_cpuiec.bin",             0x0000, 0x1000, CRC(724fe45a) SHA1(b4b01c9bf11b35c48b0a4839d7530a18259b1ae9) )
ROM_END

} // Anonymous namespace

//    YEAR  NAME        PARENT    COMPAT    MACHINE     INPUT        CLASS           INIT             COMPANY          FULLNAME               FLAGS
COMP( 1982, elzet80k,   elzet80p, 0,        elzet80,   elzet80,    elzet80_state, empty_init,     "Giesler & Danne GmbH & Co. KG",  "Elzet/K 80",        MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1982, elzet80p,   0,        0,        elzet80,   elzet80,    elzet80_state, empty_init,     "Giesler & Danne GmbH & Co. KG",  "Elzet/P 80",        MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



emax.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

    Skeleton driver for E-mu Emax & Emax II samplers.

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

#include "emu.h"
#include "bus/nscsi/devices.h"
#include "cpu/ns32000/ns32000.h"
#include "machine/6850acia.h"
#include "machine/eepromser.h"
#include "machine/ncr5380.h"
#include "machine/pit8253.h"
#include "machine/wd_fdc.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"


namespace {

class emax_state : public driver_device
{
public:
	emax_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ctc(*this, "ctc")
		, m_fdc(*this, "fdc")
		, m_hdc(*this, "scsi:7:hdc")
		, m_lcdc(*this, "lcdc")
	{
	}

	void emax(machine_config &config);
	void emaxp(machine_config &config);
	void emax2(machine_config &config);

private:
	HD44780_PIXEL_UPDATE(pixel_update);

	u8 hdc_r(offs_t offset);
	void hdc_w(offs_t offset, u8 data);
	u8 timer_r(offs_t offset);
	void timer_w(offs_t offset, u8 data);
	void mux_w(u8 data);
	void dac_w(u8 data);

	void palette_init(palette_device &palette);
	void scsihd(machine_config &config);

	void emax_periphs(address_map &map) ATTR_COLD;
	void emax_map(address_map &map) ATTR_COLD;
	void emaxp_map(address_map &map) ATTR_COLD;
	void emax2_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<pit8254_device> m_ctc;
	required_device<wd1772_device> m_fdc;
	optional_device<ncr5380_device> m_hdc;
	required_device<hd44780_device> m_lcdc;
};

HD44780_PIXEL_UPDATE(emax_state::pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 16)
		bitmap.pix(line * 8 + y, pos * 6 + x) = state;
}


u8 emax_state::hdc_r(offs_t offset)
{
	return m_hdc->read(offset >> 1);
}

void emax_state::hdc_w(offs_t offset, u8 data)
{
	m_hdc->write(offset >> 1, data);
}

u8 emax_state::timer_r(offs_t offset)
{
	return m_ctc->read(offset >> 1);
}

void emax_state::timer_w(offs_t offset, u8 data)
{
	m_ctc->write(offset >> 1, data);
}

void emax_state::mux_w(u8 data)
{
}

void emax_state::dac_w(u8 data)
{
}

void emax_state::emax_periphs(address_map &map)
{
	map(0x2c0000, 0x2c0000).select(6).rw(FUNC(emax_state::timer_r), FUNC(emax_state::timer_w));
	map(0x822000, 0x822000).w(m_fdc, FUNC(wd1772_device::cmd_w));
	map(0x822400, 0x822400).r(m_fdc, FUNC(wd1772_device::status_r));
	map(0x822800, 0x822800).w(m_fdc, FUNC(wd1772_device::track_w));
	map(0x822c00, 0x822c00).r(m_fdc, FUNC(wd1772_device::track_r));
	map(0x823000, 0x823000).w(m_fdc, FUNC(wd1772_device::sector_w));
	map(0x823400, 0x823400).r(m_fdc, FUNC(wd1772_device::sector_r));
	map(0x823800, 0x823800).w(m_fdc, FUNC(wd1772_device::data_w));
	map(0x823c00, 0x823c00).r(m_fdc, FUNC(wd1772_device::data_r));
	map(0x824004, 0x824004).w(FUNC(emax_state::mux_w));
	map(0x824006, 0x824006).w(FUNC(emax_state::dac_w));
	map(0x890000, 0x890000).w(m_lcdc, FUNC(hd44780_device::control_w));
	map(0x890002, 0x890002).r(m_lcdc, FUNC(hd44780_device::control_r));
	map(0x890004, 0x890004).w(m_lcdc, FUNC(hd44780_device::data_w));
	map(0x890006, 0x890006).r(m_lcdc, FUNC(hd44780_device::data_r));
	//map(0xaa2000, 0xaa2000).select(0x800).w(FUNC(emax_state::echip_w));
	//map(0xaa2400, 0xaa2400).select(0x800).r(FUNC(emax_state::echip_r));
}

void emax_state::emax_map(address_map &map)
{
	map(0x000000, 0x000fff).rom().region("bootprom", 0);
	map(0x008100, 0x0081ff).ram();
	map(0x010000, 0x017fff).ram();
	map(0x818048, 0x818048).w("acia", FUNC(acia6850_device::control_w));
	map(0x81804a, 0x81804a).r("acia", FUNC(acia6850_device::status_r));
	map(0x81804c, 0x81804c).w("acia", FUNC(acia6850_device::data_w));
	map(0x81804e, 0x81804e).r("acia", FUNC(acia6850_device::data_r));
	emax_periphs(map);
}

void emax_state::emaxp_map(address_map &map)
{
	map(0x000000, 0x001fff).rom().region("bootprom", 0);
	map(0x008100, 0x0081ff).ram();
	map(0x010000, 0x017fff).ram();
	map(0x0f8000, 0x0f8000).select(0xe).rw(FUNC(emax_state::hdc_r), FUNC(emax_state::hdc_w));
	map(0x818028, 0x818028).w("acia", FUNC(acia6850_device::control_w));
	map(0x81802a, 0x81802a).r("acia", FUNC(acia6850_device::status_r));
	map(0x81802c, 0x81802c).w("acia", FUNC(acia6850_device::data_w));
	map(0x81802e, 0x81802e).r("acia", FUNC(acia6850_device::data_r));
	emax_periphs(map);
}

void emax_state::emax2_map(address_map &map)
{
	map(0x000000, 0x003fff).rom().region("bootprom", 0);
	map(0x008000, 0x01ffff).ram();
	map(0x0a8018, 0x0a8018).w("acia1", FUNC(acia6850_device::control_w));
	map(0x0a801a, 0x0a801a).r("acia1", FUNC(acia6850_device::status_r));
	map(0x0a801c, 0x0a801c).w("acia1", FUNC(acia6850_device::data_w));
	map(0x0a801e, 0x0a801e).r("acia1", FUNC(acia6850_device::data_r));
	map(0x0a8028, 0x0a8028).w("acia2", FUNC(acia6850_device::control_w));
	map(0x0a802a, 0x0a802a).r("acia2", FUNC(acia6850_device::status_r));
	map(0x0a802c, 0x0a802c).w("acia2", FUNC(acia6850_device::data_w));
	map(0x0a802e, 0x0a802e).r("acia2", FUNC(acia6850_device::data_r));
	map(0x0b0000, 0x0b0000).w(m_lcdc, FUNC(hd44780_device::control_w));
	map(0x0b0002, 0x0b0002).r(m_lcdc, FUNC(hd44780_device::control_r));
	map(0x0b0004, 0x0b0004).w(m_lcdc, FUNC(hd44780_device::data_w));
	map(0x0b0006, 0x0b0006).r(m_lcdc, FUNC(hd44780_device::data_r));
	map(0x1f8000, 0x1f800f).rw(m_hdc, FUNC(ncr5380_device::read), FUNC(ncr5380_device::write)).umask16(0x00ff);
	map(0x3f8000, 0x3f8007).rw(m_ctc, FUNC(pit8254_device::read), FUNC(pit8254_device::write)).umask16(0x00ff);
	map(0x8e8000, 0x8e82ff).ram();
	map(0xae2000, 0xae2000).w(m_fdc, FUNC(wd1772_device::cmd_w));
	map(0xae2400, 0xae2400).r(m_fdc, FUNC(wd1772_device::status_r));
	map(0xae2800, 0xae2800).w(m_fdc, FUNC(wd1772_device::track_w));
	map(0xae2c00, 0xae2c00).r(m_fdc, FUNC(wd1772_device::track_r));
	map(0xae3000, 0xae3000).w(m_fdc, FUNC(wd1772_device::sector_w));
	map(0xae3400, 0xae3400).r(m_fdc, FUNC(wd1772_device::sector_r));
	map(0xae3800, 0xae3800).w(m_fdc, FUNC(wd1772_device::data_w));
	map(0xae3c00, 0xae3c00).r(m_fdc, FUNC(wd1772_device::data_r));
}


static INPUT_PORTS_START(emax)
INPUT_PORTS_END

static INPUT_PORTS_START(emax2)
INPUT_PORTS_END

void emax_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(131, 136, 139));
	palette.set_pen_color(1, rgb_t( 92,  83,  88));
}

void emax_state::scsihd(machine_config &config)
{
	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", default_scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:1", default_scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:2", default_scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:3", default_scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:4", default_scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:5", default_scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:6", default_scsi_devices, "harddisk", false);
	NSCSI_CONNECTOR(config, "scsi:7").option_set("hdc", NCR5380);
}

void emax_state::emax(machine_config &config)
{
	NS32008(config, m_maincpu, 16_MHz_XTAL / 2); // NS32008D-8 + NS32C201D-10
	m_maincpu->set_addrmap(AS_PROGRAM, &emax_state::emax_map);

	//R6500_11(config, "scannercpu", 16_MHz_XTAL / 4);

	PIT8254(config, m_ctc);
	m_ctc->set_clk<0>(16_MHz_XTAL / 2);
	m_ctc->set_clk<1>(16_MHz_XTAL / 2);
	m_ctc->set_clk<2>(16_MHz_XTAL / 32);

	WD1772(config, m_fdc, 16_MHz_XTAL / 2); // WD1772-PA

	ACIA6850(config, "acia"); // MC68A50P

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_screen_update(m_lcdc, FUNC(hd44780_device::screen_update));
	screen.set_size(16*6, 16);
	screen.set_visarea(0, 16*6-1, 0, 16-1);
	screen.set_palette("palette");

	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 16);
	m_lcdc->set_pixel_update_cb(FUNC(emax_state::pixel_update));

	PALETTE(config, "palette", FUNC(emax_state::palette_init), 2);

	//EMU_IM374(config, "echip", 16_MHz_XTAL / 2);
}

void emax_state::emaxp(machine_config &config)
{
	emax(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &emax_state::emaxp_map);

	scsihd(config);
}

void emax_state::emax2(machine_config &config)
{
	NS32016(config, m_maincpu, 20_MHz_XTAL / 2); // NS32CG16V-10 (FIXME)
	m_maincpu->set_addrmap(AS_PROGRAM, &emax_state::emax2_map);

	EEPROM_93C06_16BIT(config, "eeprom"); // NMC93C06N

	PIT8254(config, m_ctc);

	WD1772(config, m_fdc, 8'000'000);

	ACIA6850(config, "acia1");
	ACIA6850(config, "acia2");

	scsihd(config);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_screen_update(m_lcdc, FUNC(hd44780_device::screen_update));
	screen.set_size(16*6, 16);
	screen.set_visarea(0, 16*6-1, 0, 16-1);
	screen.set_palette("palette");

	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 16);
	m_lcdc->set_pixel_update_cb(FUNC(emax_state::pixel_update));

	PALETTE(config, "palette", FUNC(emax_state::palette_init), 2);

	// TODO: add other unknown peripherals
}

ROM_START(emax)
	ROM_REGION(0x1000, "bootprom", 0) // v2, Rev C mainboard, non-SE/HD version
	ROM_LOAD("emax.bin", 0x0000, 0x1000, CRC(b55210aa) SHA1(9b02dfc28700e07be5e044d53035041a54732927))

	ROM_REGION(0xc00, "scannercpu", 0)
	ROM_LOAD("im368-1_ba__r1129-11.ic7", 0x000, 0xc00, NO_DUMP)

	ROM_REGION(0x104, "cspal", 0)
	ROM_LOAD("ip345c.bin", 0x000, 0x104, CRC(7bae1347) SHA1(a49ab0bae41132e60c113d2117c5a042c2a1e44d)) // PAL16R4
ROM_END

ROM_START(emaxp)
	ROM_REGION(0x2000, "bootprom", 0) // SCSI upgrade
	ROM_LOAD("ip424a3089.bin", 0x0000, 0x2000, CRC(3abd3a16) SHA1(8d7ac39c8147bdc2ead9fedee463d1bbe94332c5))

	ROM_REGION(0xc00, "scannercpu", 0)
	ROM_LOAD("im368-1_ba__r1129-11.ic7", 0x000, 0xc00, NO_DUMP)

	ROM_REGION(0x104, "cspal", 0)
	ROM_LOAD("ip345c.bin", 0x000, 0x104, CRC(7bae1347) SHA1(a49ab0bae41132e60c113d2117c5a042c2a1e44d)) // PAL16R4

	ROM_REGION(0x104, "timpal", 0)
	ROM_LOAD("ip379a.bin", 0x000, 0x104, CRC(fb50f8bd) SHA1(5b8b7904736188c4cf8b36a4bf5ad685422ec760)) // PAL16R4
ROM_END

ROM_START(emax2)
	ROM_REGION16_LE(0x4000, "bootprom", 0)
	ROM_LOAD16_BYTE("ip43aemu_3891.ic20", 0x0000, 0x2000, CRC(51fdccb8) SHA1(0cab6540ed5d03ba202569b8730e0ec6dce1a477)) // Am27C64-250DC
	ROM_LOAD16_BYTE("ip43bemu_4291.ic19", 0x0001, 0x2000, CRC(810160b3) SHA1(6f490f9014bc221e047ccd77428b002d0a3c3168)) // Am27C64-250DC

	ROM_REGION16_LE(0x20, "eeprom", 0)
	ROM_LOAD("93c06n.ic24", 0x00, 0x20, CRC(403ef05b) SHA1(893ef614127ac1898d8ac529521f87ff62207138))
ROM_END

} // anonymous namespace


SYST(1986, emax,  0,    0, emax,  emax,  emax_state, empty_init, "E-mu Systems", "Emax Digital Sampling Keyboard", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(198?, emaxp, emax, 0, emaxp, emax,  emax_state, empty_init, "E-mu Systems", "Emax Plus Digital Sampling Keyboard", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1989, emax2, 0,    0, emax2, emax2, emax_state, empty_init, "E-mu Systems", "Emax II 16-Bit Digital Sound System", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



emerclp.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Novag Emerald Classic Plus (model 38710)

Hardware notes:
- PCB label: 100215 A, EMERALD CLASSIC II
- Hitachi H8/325 MCU, 26.601712MHz XTAL
- LCD with 6 7segs and custom segments (same as Sapphire II)
- piezo, 16 LEDs, 8*8 chessboard buttons

H8/325 C88 MCU is used in:
- Novag Emerald Classic Plus
- Novag Amber (suspected)
- Novag Turquoise (suspected)
- Excalibur Karpov 2294 (Excalibur brand Emerald Classic Plus)

TODO:
- are Emerald and Emerald Classic on similar hardware? and Novag Obsidian?
- it does a cold boot at every reset, so nvram won't work properly unless MAME
  adds some kind of auxillary autosave state feature at power-off

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

#include "emu.h"

#include "cpu/h8/h8325.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "novag_emerclp.lh"


namespace {

class emerclp_state : public driver_device
{
public:
	emerclp_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_led_pwm(*this, "led_pwm"),
		m_lcd_pwm(*this, "lcd_pwm"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_out_lcd(*this, "s%u.%u", 0U, 0U)
	{ }

	void emerclp(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(power_switch);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD { set_power(true); }

private:
	// devices/pointers
	required_device<h8325_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_led_pwm;
	required_device<pwm_display_device> m_lcd_pwm;
	required_device<dac_2bit_ones_complement_device> m_dac;
	required_ioport_array<2> m_inputs;
	output_finder<4, 16> m_out_lcd;

	bool m_power = false;
	u8 m_inp_mux = 0;
	u8 m_lcd_com = 0;
	u32 m_lcd_segs = 0;

	// I/O handlers
	void standby(int state);
	void set_power(bool power);
	u8 power_r();

	void lcd_pwm_w(offs_t offset, u8 data);
	void update_lcd();
	template <int N> void lcd_segs_w(u8 data);
	void lcd_com_w(u8 data);

	void p1_w(u8 data);
	u8 p2_r();
	u8 p4_r();
	void p4_w(u8 data);
};

void emerclp_state::machine_start()
{
	m_out_lcd.resolve();

	// register for savestates
	save_item(NAME(m_power));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_lcd_com));
	save_item(NAME(m_lcd_segs));
}



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

// power

void emerclp_state::standby(int state)
{
	// clear display
	if (state)
	{
		m_lcd_pwm->clear();
		m_led_pwm->clear();
	}
}

void emerclp_state::set_power(bool power)
{
	// power switch is tied to IRQ0
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, power ? ASSERT_LINE : CLEAR_LINE);
	m_power = power;
}

INPUT_CHANGED_MEMBER(emerclp_state::power_switch)
{
	if (newval)
		set_power(bool(param));
}

u8 emerclp_state::power_r()
{
	// P64: power switch (IRQ0)
	return m_power ? 0xef : 0xff;
}


// LCD

void emerclp_state::lcd_pwm_w(offs_t offset, u8 data)
{
	m_out_lcd[offset & 0x3f][offset >> 6] = data;
}

void emerclp_state::update_lcd()
{
	u16 lcd_segs = bitswap<16>(m_lcd_segs, 23,22,21,20,19,18,17,16, 13,12,11,10, 3,2,1,0);

	for (int i = 0; i < 4; i++)
	{
		// LCD common is analog (voltage level)
		const u8 com = population_count_32(m_lcd_com >> (i * 2) & 3);
		const u16 data = (com == 0) ? lcd_segs : (com == 2) ? ~lcd_segs : 0;
		m_lcd_pwm->write_row(i, data);
	}
}

template <int N>
void emerclp_state::lcd_segs_w(u8 data)
{
	// P4x, P5x, P7x: LCD segments
	const u8 shift = 8 * N;
	m_lcd_segs = (m_lcd_segs & ~(0xff << shift)) | (data << shift);
	update_lcd();
}

void emerclp_state::lcd_com_w(u8 data)
{
	// P30-P37: LCD common
	m_lcd_com = data;
	update_lcd();
}


// misc

void emerclp_state::p1_w(u8 data)
{
	// P10-P17: input mux, LED data
	m_inp_mux = ~data;
	m_led_pwm->write_mx(~data);
}

u8 emerclp_state::p2_r()
{
	u8 data = 0;

	// P20-P27: read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_rank(i, true);

	return ~data;
}

u8 emerclp_state::p4_r()
{
	u8 data = 0;

	// P46,P47: read buttons
	for (int i = 0; i < 2; i++)
		if (m_inp_mux & m_inputs[i]->read())
			data |= 0x40 << i;

	return ~data;
}

void emerclp_state::p4_w(u8 data)
{
	// P44,P45: LED select
	m_led_pwm->write_my(~data >> 4 & 3);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( emerclp )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Change Color")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Take Back / Next Best")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("King / Easy")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Queen / Random")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Rook / Restore")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Bishop / Info")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Knight / Sound")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Pawn / Referee")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Go")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Trace Forward / Autoplay")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Verify / Set Up")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Training")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Hint")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Set Level")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Clear")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("New Game")

	PORT_START("POWER")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_ON) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(emerclp_state::power_switch), 1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(emerclp_state::power_switch), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void emerclp_state::emerclp(machine_config &config)
{
	// basic machine hardware
	H8325(config, m_maincpu, 26.601712_MHz_XTAL);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(h8325_device::nvram_set_battery));
	m_maincpu->standby_cb().append(FUNC(emerclp_state::standby));
	m_maincpu->write_port1().set(FUNC(emerclp_state::p1_w));
	m_maincpu->read_port2().set(FUNC(emerclp_state::p2_r));
	m_maincpu->write_port3().set(FUNC(emerclp_state::lcd_com_w));
	m_maincpu->read_port4().set(FUNC(emerclp_state::p4_r));
	m_maincpu->write_port4().set(FUNC(emerclp_state::p4_w));
	m_maincpu->write_port4().append(FUNC(emerclp_state::lcd_segs_w<0>));
	m_maincpu->write_port5().set(FUNC(emerclp_state::lcd_segs_w<1>));
	m_maincpu->read_port6().set(FUNC(emerclp_state::power_r));
	m_maincpu->write_port6().set(m_dac, FUNC(dac_2bit_ones_complement_device::write)).mask(3);
	m_maincpu->write_port7().set(FUNC(emerclp_state::lcd_segs_w<2>));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	//m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_lcd_pwm).set_size(4, 16);
	m_lcd_pwm->output_x().set(FUNC(emerclp_state::lcd_pwm_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/5, 671/5);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_led_pwm).set_size(2, 8);
	config.set_default_layout(layout_novag_emerclp);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_2BIT_ONES_COMPLEMENT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.125);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( emerclp )
	ROM_REGION16_BE( 0x8000, "maincpu", 0 )
	ROM_LOAD("ihp_1700106_6433258c88f.u1", 0x0000, 0x8000, CRC(8638661f) SHA1(de8f434fb5b079ec3137b4cf993822a5a5aafde8) )

	ROM_REGION( 72533, "screen", 0 )
	ROM_LOAD("sapphire2.svg", 0, 72533, CRC(34944b61) SHA1(4a0536ac07790cced9f9bf15522b17ebc375ff8a) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT     CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1997, emerclp, 0,      0,      emerclp,  emerclp, emerclp_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Emerald Classic Plus", MACHINE_SUPPORTS_SAVE )



emma2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Emma and Emma II by L.J.Technical Systems
These were produced by L.J.Electronics Ltd, Norwich in 1979.

2018-09-20 Working driver.

We don't have roms for the Emma, so only the Emma II is emulated.

Keys:
0-9,A-F: hex input
M      : memory mode (modify address, or modify data)
G      : go
R      : single step (not emulated)
P      : insert a forced break
S      : save to cassette
L      : load from cassette
+      : up (use UP-arrow key)
-      : down (use DOWN-arrow key)

Pasting example:
M0020M01^02^03^04^05^06^07^08^09^M000020
Press up arrow to see your input.

To save, press S, enter beginning address, press S, enter end address+1,
press S, start the recorder, press 0 for 300 baud or 1 for 1200 baud. The
data is saved.

To load, press L, press play, while leader is playing press 0 or 1 as above.
When a row of dots appears, the data has been loaded.


There's an expansion unit with its own rom, a 8255, an eprom programmer,
and 16k of static RAM.



To Do:
- Cassette LED doesn't work.
- Code up the expansion unit (rom is there but no schematic exists)
- Need software.


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

#include "emu.h"
#include "cpu/m6502/m6502.h"
#include "machine/6522via.h"
#include "machine/6821pia.h"
#include "imagedev/cassette.h"
#include "speaker.h"
#include "video/pwm.h"
#include "emma2.lh"


namespace {

class emma2_state : public driver_device
{
public:
	emma2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cassette(*this, "cassette")
		, m_via(*this, "via")
		, m_pia(*this, "pia")
		, m_display(*this, "display")
		, m_io_keyboard(*this, "X%u", 0U)
	{ }

	void emma2(machine_config &config);

private:
	void segment_w(uint8_t data);
	void digit_w(uint8_t data);
	uint8_t keyboard_r();
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void mem_map(address_map &map) ATTR_COLD;

	uint8_t m_digit = 0U;
	uint8_t m_seg = 0U;
	required_device<cpu_device> m_maincpu;
	required_device<cassette_image_device> m_cassette;
	required_device<via6522_device> m_via;
	required_device<pia6821_device> m_pia;
	required_device<pwm_display_device> m_display;
	required_ioport_array<8> m_io_keyboard;
};

void emma2_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x03ff).ram();
	map(0x0900, 0x090f).mirror(0x02f0).m(m_via, FUNC(via6522_device::map));
	map(0x0a00, 0x0a03).mirror(0x00fc).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x0c00, 0x0fff).ram();
	map(0x9000, 0x97ff).rom().region("maincpu", 0);
	map(0xd800, 0xdfff).mirror(0x2000).rom().region("maincpu", 0x0800);
}


/* Input ports */
static INPUT_PORTS_START( emma2 )
/*
M   L   0   4   8   C
G   R   1   5   9   D
P   +   2   6   A   E
S   -   3   7   B   F
*/

	PORT_START("X0")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')

	PORT_START("X1")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1')

	PORT_START("X2")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2')

	PORT_START("X3")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3')

	PORT_START("X4")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4')

	PORT_START("X5")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5')

	PORT_START("X6")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6')

	PORT_START("X7")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR('V')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
INPUT_PORTS_END


void emma2_state::digit_w(uint8_t data)
{
	m_cassette->output( BIT(data, 6) ? +1.0 : -1.0);

	m_digit = data & 7;
	m_display->matrix(1 << m_digit, m_seg);
}

void emma2_state::segment_w(uint8_t data)
{
	m_seg = data;
	m_display->matrix(1 << m_digit, m_seg);
}

uint8_t emma2_state::keyboard_r()
{
	u8 data = m_io_keyboard[m_digit]->read();
	data |= ((m_cassette)->input() < 0.0) ? 0x80 : 0;
	return data;
}

void emma2_state::machine_reset()
{
	m_seg = 0;
	m_digit = 0;
}

void emma2_state::machine_start()
{
	save_item(NAME(m_digit));
	save_item(NAME(m_seg));
}

void emma2_state::emma2(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, 1'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &emma2_state::mem_map);

	/* video hardware */
	config.set_default_layout(layout_emma2);
	PWM_DISPLAY(config, m_display).set_size(8, 8);
	m_display->set_segmask(0xff, 0xff);

	/* Devices */
	MOS6522(config, m_via, 1'000'000);  // #2 from cpu
	m_via->irq_handler().set_inputline(m_maincpu, m6502_device::IRQ_LINE);

	PIA6821(config, m_pia);
	m_pia->writepa_handler().set(FUNC(emma2_state::segment_w));
	m_pia->writepb_handler().set(FUNC(emma2_state::digit_w));
	m_pia->readpb_handler().set(FUNC(emma2_state::keyboard_r));
	m_pia->ca2_handler().set_output("led0");
	m_pia->irqa_handler().set_inputline(m_maincpu, m6502_device::IRQ_LINE);
	m_pia->irqb_handler().set_inputline(m_maincpu, m6502_device::IRQ_LINE);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	/* cassette */
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
}


/* ROM definition */
ROM_START( emma2 )
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "se118a_90-97", 0x0000, 0x0800, CRC(32d36938) SHA1(910fd1c18c7deae83933c7c4f397103a35bf574a) ) // 0x9000
	ROM_LOAD( "se116a_d8-dd_fe-ff", 0x0800, 0x0800, CRC(ef0f1513) SHA1(46089ba0402828b4204812a04134b313d9be0f93) ) // 0xd800
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY  FULLNAME           FLAGS
COMP( 1979, emma2,  0,      0,      emma2,   emma2,  emma2_state,  empty_init, "L.J.Technical Systems",   "Emma II trainer", MACHINE_SUPPORTS_SAVE )



emu2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for E-mu Emulator II synthesizer.

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

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/adc0808.h"
#include "machine/bankdev.h"
#include "machine/pit8253.h"
#include "machine/scn_pci.h"
#include "machine/z80ctc.h"
#include "machine/z80dma.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"


namespace {

class emu2_state : public driver_device
{
public:
	emu2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pages(*this, "pages")
		, m_scannercpu(*this, "scannercpu")
		, m_scan0(*this, "SCAN%u", 0U)
		, m_scan32(*this, "SCAN%u", 32U)
		, m_scan40(*this, "SCAN%u", 40U)
		, m_page_select{0, 0, 0, 0}
		, m_page_reset(true)
		, m_scan_select(0)
	{
	}

	void emu2(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u8 main_paged_r(offs_t offset);
	void main_paged_w(offs_t offset, u8 data);
	u8 dma_paged_r(offs_t offset);
	void dma_paged_w(offs_t offset, u8 data);
	void page_select_w(offs_t offset, u8 data);
	void scan_select_w(u8 data);
	u8 scan_buffer_r();

	void mem_map(address_map &map) ATTR_COLD;
	void paged_mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void scanner_mem_map(address_map &map) ATTR_COLD;
	void scanner_io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_device<address_map_bank_device> m_pages;
	required_device<z80_device> m_scannercpu;

	required_ioport_array<16> m_scan0;
	required_ioport_array<2> m_scan32;
	required_ioport_array<2> m_scan40;

	u8 m_page_select[4];
	bool m_page_reset;
	u8 m_scan_select;
};

void emu2_state::machine_start()
{
	save_item(NAME(m_page_select));
	save_item(NAME(m_page_reset));
	save_item(NAME(m_scan_select));
}

void emu2_state::machine_reset()
{
	m_page_reset = true;
}


u8 emu2_state::main_paged_r(offs_t offset)
{
	return m_pages->read8((offset & 0x7fff) | u16(m_page_reset ? 0 : m_page_select[2 | BIT(offset, 15)]) << 15);
}

void emu2_state::main_paged_w(offs_t offset, u8 data)
{
	m_pages->write8((offset & 0x7fff) | u16(m_page_reset ? 0 : m_page_select[2 | BIT(offset, 15)]) << 15, data);
}

u8 emu2_state::dma_paged_r(offs_t offset)
{
	return m_pages->read8((offset & 0x7fff) | u16(m_page_reset ? 0 : m_page_select[BIT(offset, 15)]) << 15);
}

void emu2_state::dma_paged_w(offs_t offset, u8 data)
{
	m_pages->write8((offset & 0x7fff) | u16(m_page_reset ? 0 : m_page_select[BIT(offset, 15)]) << 15, data);
}

void emu2_state::page_select_w(offs_t offset, u8 data)
{
	if (!machine().side_effects_disabled())
		m_page_reset = false;
	m_page_select[offset] = data & 0x0f;
}

void emu2_state::scan_select_w(u8 data)
{
	m_scan_select = data;
}

u8 emu2_state::scan_buffer_r()
{
	if (!BIT(m_scan_select, 5))
		return m_scan0[m_scan_select & 0x0f]->read();
	else if (m_scan_select & 0x06)
		return (BIT(m_scan_select, 3) ? m_scan40 : m_scan32)[m_scan_select & 1]->read();
	else
		return 0xff;
}

void emu2_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(emu2_state::main_paged_r), FUNC(emu2_state::main_paged_w));
}

void emu2_state::paged_mem_map(address_map &map)
{
	map(0x00000, 0x01fff).rom().region("main_program", 0);
	map(0x02000, 0x027ff).ram();
}

void emu2_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).r("usart", FUNC(scn2651_device::read));
	map(0x04, 0x07).w("usart", FUNC(scn2651_device::write));
	map(0x08, 0x0b).mirror(4).w("t6x", FUNC(pit8254_device::write));
	map(0x10, 0x13).mirror(4).w("t35", FUNC(pit8254_device::write));
	map(0x18, 0x1b).mirror(4).w("t02", FUNC(pit8254_device::write));
	map(0x20, 0x20).mirror(7).rw("dma", FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	map(0x28, 0x2b).mirror(4).rw("mainctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x30, 0x33).mirror(4).rw("mainpio", FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x38, 0x3b).mirror(4).rw("disksio", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x68, 0x6b).w(FUNC(emu2_state::page_select_w));
}

void emu2_state::scanner_mem_map(address_map &map)
{
	map(0x0000, 0x1fff).mirror(0x6000).rom().region("scanner_program", 0);
	map(0x8000, 0x87ff).mirror(0x7800).ram();
}

void emu2_state::scanner_io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x34, 0x34).mirror(0x81).r("adc", FUNC(adc0809_device::data_r));
	map(0x36, 0x36).mirror(0x81).w("adc", FUNC(adc0809_device::address_data_start_w));
	map(0x38, 0x38).mirror(0x81).r(FUNC(emu2_state::scan_buffer_r));
	map(0x50, 0x53).mirror(0x8c).rw("scannerpio", FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x60, 0x63).mirror(0x8c).rw("scannerctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
}


static INPUT_PORTS_START(emu2)
	PORT_START("SCAN0")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN1")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN2")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN3")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN4")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN5")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN6")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN7")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN8")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN9")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN10")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN11")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN12")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN13")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN14")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN15")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN32")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN33")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN40")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN41")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)
INPUT_PORTS_END

static const z80_daisy_config main_daisy_chain[] =
{
	{ "disksio" },
	{ "dma" },
	{ "mainctc" },
	{ "mainpio" },
	{ nullptr }
};

static const z80_daisy_config scanner_daisy_chain[] =
{
	{ "scannerctc" },
	{ "scannerpio" },
	{ nullptr }
};

void emu2_state::emu2(machine_config &config)
{
	Z80(config, m_maincpu, 20_MHz_XTAL / 5); // Z80A CPU (IC21)
	m_maincpu->set_addrmap(AS_PROGRAM, &emu2_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &emu2_state::io_map);
	m_maincpu->set_daisy_config(main_daisy_chain);

	ADDRESS_MAP_BANK(config, m_pages);
	m_pages->set_endianness(ENDIANNESS_LITTLE);
	m_pages->set_data_width(8);
	m_pages->set_addr_width(19);
	m_pages->set_addrmap(0, &emu2_state::paged_mem_map);

	z80ctc_device &mainctc(Z80CTC(config, "mainctc", 20_MHz_XTAL / 5)); // Z80A CTC (IC60)
	mainctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80dma_device &dma(Z80DMA(config, "dma", 20_MHz_XTAL / 5)); // Z80A DMA
	dma.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	dma.in_mreq_callback().set(FUNC(emu2_state::dma_paged_r));
	dma.out_mreq_callback().set(FUNC(emu2_state::dma_paged_w));

	z80pio_device &mainpio(Z80PIO(config, "mainpio", 20_MHz_XTAL / 5)); // Z80A PIO (IC24)
	mainpio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	mainpio.out_pa_callback().set("scannerpio", FUNC(z80pio_device::port_a_write));

	z80sio_device &disksio(Z80SIO(config, "disksio", 20_MHz_XTAL / 5)); // Z80A SIO/2 (IC126)
	disksio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	scn2651_device &usart(SCN2651(config, "usart", 20_MHz_XTAL / 4));
	usart.rxrdy_handler().set("mainctc", FUNC(z80ctc_device::trg1)).invert();

	pit8254_device &t02(PIT8254(config, "t02")); // 8254-2 (IC3)
	t02.set_clk<0>(20_MHz_XTAL / 2);
	t02.set_clk<1>(20_MHz_XTAL / 2);
	t02.set_clk<2>(20_MHz_XTAL / 2);

	pit8254_device &t35(PIT8254(config, "t35")); // 8254-2 (IC2)
	t35.set_clk<0>(20_MHz_XTAL / 2);
	t35.set_clk<1>(20_MHz_XTAL / 2);
	t35.set_clk<2>(20_MHz_XTAL / 2);

	pit8254_device &t6x(PIT8254(config, "t6x")); // 8254-2 (IC1)
	t6x.set_clk<0>(20_MHz_XTAL / 2);
	t6x.set_clk<1>(20_MHz_XTAL / 2);
	t6x.set_clk<2>(20_MHz_XTAL / 2);

	Z80(config, m_scannercpu, 20_MHz_XTAL / 5); // Z80A CPU (IC66)
	m_scannercpu->set_addrmap(AS_PROGRAM, &emu2_state::scanner_mem_map);
	m_scannercpu->set_addrmap(AS_IO, &emu2_state::scanner_io_map);
	m_scannercpu->set_daisy_config(scanner_daisy_chain);

	z80ctc_device &scannerctc(Z80CTC(config, "scannerctc", 20_MHz_XTAL / 5)); // Z80A CTC (IC45)
	scannerctc.intr_callback().set_inputline(m_scannercpu, INPUT_LINE_IRQ0);
	scannerctc.set_clk<2>(20_MHz_XTAL / 10);

	adc0809_device &adc(ADC0809(config, "adc", 20_MHz_XTAL / 40));
	adc.eoc_callback().set("scannerctc", FUNC(z80ctc_device::trg3));

	z80pio_device &scannerpio(Z80PIO(config, "scannerpio", 20_MHz_XTAL / 5)); // Z80A PIO (IC46)
	scannerpio.out_int_callback().set_inputline(m_scannercpu, INPUT_LINE_IRQ0);
	scannerpio.out_pa_callback().set("mainpio", FUNC(z80pio_device::port_a_write));
	scannerpio.out_pb_callback().set(FUNC(emu2_state::scan_select_w));
}

ROM_START(emu2)
	ROM_REGION(0x2000, "main_program", 0)
	ROM_SYSTEM_BIOS(0, "v23", "Version 2.3")
	ROMX_LOAD("e2newermain_hdmb2_3_main.ic42", 0x0000, 0x2000, CRC(c8303551) SHA1(4ec4bf6df95638d4503ab8ff7e859c420ec0720e), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v21", "Version 2.1")
	ROMX_LOAD("e2plus_mainboot_2_1_dm_2764.ic42", 0x0000, 0x2000, CRC(91e60278) SHA1(a2332266d6a12eade584b5da85817f3b3ed9037c), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "diag", "Diagnostics")
	ROMX_LOAD("e-mu_systems_1986_e2plus_debug_rom_ip341.ic42", 0x0000, 0x2000, CRC(7cd30c30) SHA1(d572d88c8788e25154a3bb8fd2ba3ff8a300ae40), ROM_BIOS(2)) // 2764 chip

	ROM_REGION(0x2000, "scanner_program", 0) // TODO: make these selectable))
	ROM_LOAD("eiiscanv2-1.ic28", 0x0000, 0x2000, CRC(b7913026) SHA1(4485e5423606014be73f121c2ee1f053a6eb148a))
	ROM_LOAD("eiiscanv3-1.ic28", 0x0000, 0x2000, CRC(1544b11d) SHA1(e354388cd694a1ce55a5b7d89c546308628090c7))

	ROM_REGION(0x400, "ucontroller", 0)
	ROM_LOAD("74s472.ic137", 0x000, 0x200, NO_DUMP)
	ROM_LOAD("74s472.ic135", 0x200, 0x200, NO_DUMP)
ROM_END

} // anonymous namespace


SYST(1984, emu2, 0, 0, emu2, emu2, emu2_state, empty_init, "E-mu Systems", "Emulator II", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



emu3.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

    Skeleton driver for E-mu Emulator Three (EIII) synthesizer.


    WIP
     - scsi scan fails, selection code seems buggy
     - debug port serial parameters aren't correct

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

#include "emu.h"

#include "cpu/ns32000/ns32000.h"

// various hardware
#include "machine/pit8253.h"
#include "machine/wd_fdc.h"
#include "machine/z80scc.h"
#include "machine/ncr5380.h"
#include "machine/6850acia.h"
#include "machine/clock.h"

// busses and connectors
#include "machine/nscsi_bus.h"
#include "bus/nscsi/hd.h"
#include "bus/rs232/rs232.h"
#include "bus/rs232/terminal.h"

#include "imagedev/floppy.h"

// video
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"

#define VERBOSE 0
#include "logmacro.h"


namespace {

class emu3_state : public driver_device
{
public:
	emu3_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "lcdc")
		, m_fdc(*this, "fdc")
		, m_fdd(*this, "fdc:0:35dd")
		, m_hdc(*this, "scsi:0:ncr5380")
		, m_pit(*this, "pit")
		, m_scc(*this, "scc")
		, m_ddt(*this, "ddt")
		, m_ddt_port(*this, "ddt_port")
		, m_led(*this, "led%u", 0U)
	{
	}

	void emu3(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(nmi_button);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void emu3_map(address_map &map) ATTR_COLD;

	void palette_init(palette_device &palette);

	required_device<cpu_device> m_maincpu;
	required_device<hd44780_device> m_lcdc;
	required_device<wd1772_device> m_fdc;
	required_device<floppy_image_device> m_fdd;
	required_device<ncr5380_device> m_hdc;
	required_device<pit8254_device> m_pit;
	required_device<z80scc_device> m_scc;
	optional_device<acia6850_device> m_ddt;
	optional_device<rs232_port_device> m_ddt_port;

	output_finder<17> m_led;

	enum irq_number : unsigned
	{
		SCNINT = 0, // scanner interrupt (active low)
		SCCINT = 1, // serial controller interrupt (active low)
		FDCINT = 2, // flopper disk controller interrupt (active high)
		STINT  = 3, // software timer interrupt (active low)
		TGINT  = 4, // transient generator interrupt (active high)
		HDINT  = 5, // hard disk interrupt (active high)
					// unused (tied high)
					// unused (tied low)
	};

	enum misc_latch : u8
	{
		MISC_MET     = 0, // metronome?
		MISC_MIDIOF  = 1, // MIDI/RS422 select (+MIDIOF)
		MISC_SCNDATA = 2, // scanner cpu serial data (+SCNDATA)
		MISC_SCNCLK  = 3, // scanner cpu serial clock (+SCNCLK)
		MISC_SIDE    = 4, // floppy disk side select (-SIDE)
		MISC_MTR     = 5, // floppy disk motor control (-MTR)
		MISC_SYNCON  = 6, // ext. sync on (+SYNCON)
		MISC_LED     = 7, // front panel Erase LED (CN6 60)
	};

	template <irq_number IRQ> void irq_w(int state)
	{
		if (state)
			m_irq_latch |= 1U << IRQ;
		else
			m_irq_latch &= ~(1U << IRQ);

		bool const irq_state = (m_irq_latch & 0x34) || (~m_irq_latch & 0x0b);
		if (irq_state != m_irq_state)
		{
			m_irq_state = irq_state;
			m_maincpu->set_input_line(INPUT_LINE_IRQ0, m_irq_state);
		}
	}

	u8 m_irq_latch = 0;
	bool m_irq_state = false;
};

void emu3_state::machine_start()
{
	m_led.resolve();

	m_irq_latch = 0x4b;
	m_irq_state = false;
}

void emu3_state::machine_reset()
{
	m_fdc->set_floppy(m_fdd);
	m_fdc->dden_w(0);
}

void emu3_state::emu3_map(address_map &map)
{
	map(0x000000, 0x007fff).rom().region("bootprom", 0);
	map(0x008000, 0x027fff).ram();
	map(0x2c0000, 0x2c0000).rw(m_hdc, FUNC(ncr5380_device::dma_r), FUNC(ncr5380_device::dma_w));
	map(0x300000, 0x30000f).rw(m_hdc, FUNC(ncr5380_device::read), FUNC(ncr5380_device::write)).umask16(0x00ff);
	map(0x390000, 0x390007).rw(m_pit, FUNC(pit8254_device::read), FUNC(pit8254_device::write)).umask16(0x00ff);
	map(0x400000, 0xbfffff).ram();

	map(0xc00000, 0xc00000).lw8([this](u8 data) { irq_w<STINT>(1); }, "stint_w");
	map(0xc40000, 0xc40000).lw8([this](u8 data) { irq_w<TGINT>(0); }, "tgint_w");
	map(0xd70000, 0xd70000).lw8(
		[this](u8 data)
		{
			m_fdd->ss_w(!BIT(data, MISC_SIDE));
			m_fdd->mon_w(BIT(data, MISC_MTR));
			m_led[16] = BIT(data, MISC_LED);
		}, "misc_w");
	map(0xdb0000, 0xdb0001).lw16(
		[this](u16 data)
		{
			for (unsigned i = 0; i < 16; i++)
				m_led[i] = BIT(data, i);
		}, "led_w");

	map(0xde0060, 0xde007f).nopw(); // temporarily mute logging

	map(0xeb0000, 0xeb0000).w(m_lcdc, FUNC(hd44780_device::control_w));
	map(0xeb0002, 0xeb0002).r(m_lcdc, FUNC(hd44780_device::control_r));
	map(0xeb0004, 0xeb0004).w(m_lcdc, FUNC(hd44780_device::data_w));
	map(0xeb0006, 0xeb0006).r(m_lcdc, FUNC(hd44780_device::data_r));

	map(0xef0001, 0xef0001).w(m_ddt, FUNC(acia6850_device::control_w));
	map(0xef0003, 0xef0003).r(m_ddt, FUNC(acia6850_device::status_r));
	map(0xef0005, 0xef0005).w(m_ddt, FUNC(acia6850_device::data_w));
	map(0xef0007, 0xef0007).r(m_ddt, FUNC(acia6850_device::data_r));

	map(0xf50000, 0xf50007).w(m_fdc, FUNC(wd1772_device::write)).umask16(0x00ff);
	map(0xf50008, 0xf5000f).r(m_fdc, FUNC(wd1772_device::read)).umask16(0x00ff);

	map(0xfffe00, 0xfffe00).lr8([this]() { return m_irq_latch; }, "irq_latch_r");
}

void emu3_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t::white());
	palette.set_pen_color(1, rgb_t::black());
}

static void emu_scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
}

static void emu3_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
}

static void emu3_rs232(device_slot_interface &device)
{
	device.option_add("terminal", SERIAL_TERMINAL);
}

void emu3_state::emu3(machine_config &config)
{
	NS32016(config, m_maincpu, 20_MHz_XTAL / 2); // 32016-10 CPU + 32001-10 TCU
	m_maincpu->set_addrmap(AS_PROGRAM, &emu3_state::emu3_map);

	//NS32081(config, "fpu", 20_MHz_XTAL / 2);

	//R6500_11(config, "scannercpu", 16_MHz_XTAL / 4);

	WD1772(config, m_fdc, 16_MHz_XTAL / 2);
	m_fdc->intrq_wr_callback().set(*this, FUNC(emu3_state::irq_w<FDCINT>));
	m_fdc->set_disable_motor_control(true);

	FLOPPY_CONNECTOR(config, "fdc:0", emu3_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	PIT8254(config, m_pit); // 8254-2
	m_pit->set_clk<0>(20_MHz_XTAL / 2);
	m_pit->set_clk<1>(16_MHz_XTAL / 16);
	m_pit->out_handler<0>().set(*this, FUNC(emu3_state::irq_w<TGINT>));
	//pit.out_handler<1>().set(); // -SWTIME
	//pit.out_handler<2>().set(); // -SMPTIME

	// scsi bus and devices
	NSCSI_BUS(config, "scsi");

	// scsi host adapter
	NSCSI_CONNECTOR(config, "scsi:0").option_set("ncr5380", NCR5380).machine_config(
		[this](device_t *device)
		{
			ncr5380_device &adapter = downcast<ncr5380_device &>(*device);

			adapter.irq_handler().set(*this, FUNC(emu3_state::irq_w<HDINT>));
		});

	NSCSI_CONNECTOR(config, "scsi:1", emu_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:2", emu_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", emu_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", emu_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", emu_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", emu_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:7", emu_scsi_devices, nullptr);

	SCC85230(config, m_scc, 16_MHz_XTAL / 4);
	m_scc->out_int_callback().set(*this, FUNC(emu3_state::irq_w<SCCINT>)).invert();

	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(4, 20);

	PALETTE(config, "palette", FUNC(emu3_state::palette_init), 2);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60); // no idea
	screen.set_screen_update(m_lcdc, FUNC(hd44780_device::screen_update));
	screen.set_size(20 * 6, 4 * 9); // 20x4 columns/rows, 5x8 character cells
	screen.set_visarea_full();
	screen.set_palette("palette");

	// debug board
	ACIA6850(config, m_ddt);
	clock_device &ddt_clock(CLOCK(config, "ddt_clock", 10_MHz_XTAL / 64));
	ddt_clock.signal_handler().set(m_ddt, FUNC(acia6850_device::write_txc));
	ddt_clock.signal_handler().append(m_ddt, FUNC(acia6850_device::write_rxc));

	RS232_PORT(config, m_ddt_port, emu3_rs232, nullptr);
	m_ddt_port->rxd_handler().set(m_ddt, FUNC(acia6850_device::write_rxd)).invert();
	m_ddt->rts_handler().set(m_ddt_port, FUNC(rs232_port_device::write_rts)).invert();
	m_ddt->txd_handler().set(m_ddt_port, FUNC(rs232_port_device::write_txd)).invert();
}

INPUT_CHANGED_MEMBER(emu3_state::nmi_button)
{
	if (newval)
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
}

static INPUT_PORTS_START(emu3)
	PORT_START("ddt")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(emu3_state::nmi_button), 0)
INPUT_PORTS_END

ROM_START(emu3)
	ROM_REGION16_LE(0x8000, "bootprom", 0)
	ROM_LOAD16_BYTE("e3-lsboot_ip381a_emu_systems_4088.ic3", 0x0000, 0x4000, CRC(34e5283f) SHA1(902c2a9a2b37b34331fb57d45b88ffabc1f12a53)) // 27128B
	ROM_LOAD16_BYTE("e3-msboot_ip380a_emu_systems_4088.ic4", 0x0001, 0x4000, CRC(1302c054) SHA1(28b7e8991e72cc111ee0067c58bddafca70f3824)) // 27128B

	ROM_REGION(0xc00, "scannercpu", 0)
	ROM_LOAD("im368.ic31", 0x000, 0xc00, NO_DUMP)
ROM_END

} // anonymous namespace


SYST(1987, emu3, 0, 0, emu3, emu3, emu3_state, empty_init, "E-mu Systems", "Emulator Three Digital Sound Production System", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



emu68k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

    Skeleton driver for E-mu Proteus and other MC68000-based synthesizer modules.

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

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "machine/mc68901.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"


namespace {

class emu68k_state : public driver_device
{
public:
	emu68k_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mfp(*this, "mfp")
	{
	}

	void proteus1(machine_config &config);
	void vintkeys(machine_config &config);
	void phatt(machine_config &config);
	void proteusxr(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void add_lcd(machine_config &config);

	HD44780_PIXEL_UPDATE(lcd_pixel_update);
	void palette_init(palette_device &palette);

	void proteus1_map(address_map &map) ATTR_COLD;
	void proteusxr_map(address_map &map) ATTR_COLD;
	void vintkeys_map(address_map &map) ATTR_COLD;
	void phatt_map(address_map &map) ATTR_COLD;
	void fc7_map(address_map &map) ATTR_COLD;

	required_device<m68000_base_device> m_maincpu;
	required_device<mc68901_device> m_mfp;
};

void emu68k_state::machine_start()
{
}

HD44780_PIXEL_UPDATE(emu68k_state::lcd_pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 16)
		bitmap.pix(line * 8 + y, pos * 6 + x) = state;
}


void emu68k_state::proteus1_map(address_map &map)
{
	map(0x000000, 0x01ffff).rom().region("program", 0);
	map(0x600080, 0x600083).rw("lcdc", FUNC(hd44780_device::read), FUNC(hd44780_device::write)).umask16(0x00ff);
	map(0x600100, 0x60012f).rw(m_mfp, FUNC(mc68901_device::read), FUNC(mc68901_device::write)).umask16(0x00ff);
	map(0x800000, 0x8003ff).nopw(); // ???
	map(0xffc000, 0xffffff).ram();
}

void emu68k_state::proteusxr_map(address_map &map)
{
	map(0x000000, 0x01ffff).rom().region("program", 0);
	map(0x400000, 0x400001).nopw(); // ???
	map(0x700000, 0x700003).rw("lcdc", FUNC(hd44780_device::read), FUNC(hd44780_device::write)).umask16(0x00ff);
	map(0x800000, 0x8003ff).nopw(); // ???
	map(0x900000, 0x90002f).rw(m_mfp, FUNC(mc68901_device::read), FUNC(mc68901_device::write)).umask16(0x00ff);
	map(0xa00000, 0xa00001).nopr(); // watchdog?
	map(0xb00000, 0xb00001).nopw(); // ???
	map(0xffc000, 0xffffff).ram();
}

void emu68k_state::vintkeys_map(address_map &map)
{
	map(0x000000, 0x03ffff).rom().region("program", 0);
	map(0x600080, 0x600083).rw("lcdc", FUNC(hd44780_device::read), FUNC(hd44780_device::write)).umask16(0x00ff);
	map(0x600100, 0x60012f).rw(m_mfp, FUNC(mc68901_device::read), FUNC(mc68901_device::write)).umask16(0x00ff);
	map(0x800000, 0x8003ff).nopw(); // ???
	map(0xffc000, 0xffffff).ram();
}

void emu68k_state::phatt_map(address_map &map)
{
	map(0x000000, 0x07ffff).rom().region("program", 0);
	map(0x600080, 0x600083).rw("lcdc", FUNC(hd44780_device::read), FUNC(hd44780_device::write)).umask16(0x00ff);
	map(0x600100, 0x60012f).rw(m_mfp, FUNC(mc68901_device::read), FUNC(mc68901_device::write)).umask16(0x00ff);
	map(0x800000, 0x8003ff).nopw(); // ???
	map(0xffc000, 0xffffff).ram();
}

void emu68k_state::fc7_map(address_map &map)
{
	map(0xfffff3, 0xfffff3).r(m_mfp, FUNC(mc68901_device::get_vector));
}


static INPUT_PORTS_START(proteus1)
INPUT_PORTS_END

static INPUT_PORTS_START(procuss)
INPUT_PORTS_END

static INPUT_PORTS_START(vintkeys)
INPUT_PORTS_END

static INPUT_PORTS_START(orbit9090)
INPUT_PORTS_END

static INPUT_PORTS_START(phatt)
INPUT_PORTS_END

static INPUT_PORTS_START(carnaval)
INPUT_PORTS_END

void emu68k_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(131, 136, 139));
	palette.set_pen_color(1, rgb_t( 92,  83,  88));
}

void emu68k_state::add_lcd(machine_config &config)
{
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6*16, 8*2);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(emu68k_state::palette_init), 2);

	hd44780_device &lcdc(HD44780(config, "lcdc", 270'000)); // TODO: clock not measured, datasheet typical clock used
	lcdc.set_lcd_size(2, 16);
	lcdc.set_pixel_update_cb(FUNC(emu68k_state::lcd_pixel_update));
}

void emu68k_state::proteus1(machine_config &config)
{
	M68000(config, m_maincpu, 10'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &emu68k_state::proteus1_map);
	m_maincpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &emu68k_state::fc7_map);

	MC68901(config, m_mfp, 10'000'000); // FIXME: not the right type at all
	m_mfp->set_timer_clock(2'500'000);
	m_mfp->out_irq_cb().set_inputline(m_maincpu, M68K_IRQ_1); // TODO: verify level

	add_lcd(config);
}

void emu68k_state::vintkeys(machine_config &config)
{
	proteus1(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &emu68k_state::vintkeys_map);
}

void emu68k_state::phatt(machine_config &config)
{
	proteus1(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &emu68k_state::phatt_map);
}

void emu68k_state::proteusxr(machine_config &config)
{
	M68000(config, m_maincpu, 40_MHz_XTAL / 4); // MC68000P10
	m_maincpu->set_addrmap(AS_PROGRAM, &emu68k_state::proteusxr_map);
	m_maincpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &emu68k_state::fc7_map);

	// TODO: NVRAM = 2x CXK5864BP-12L + DS1210 + battery?

	MC68901(config, m_mfp, 40_MHz_XTAL / 4); // MC68901P
	m_mfp->set_timer_clock(40_MHz_XTAL / 16); // TODO: determine divider
	m_mfp->out_irq_cb().set_inputline(m_maincpu, M68K_IRQ_1); // TODO: verify level

	add_lcd(config);
}

// TODO: all of these have sample ROMs as well (none currently dumped)

ROM_START(proteus1)
	ROM_REGION16_BE(0x20000, "program", 0)
	ROM_LOAD16_BYTE("e-mu proteus 1 msb 2.05.bin", 0x00000, 0x10000, CRC(6a84ac60) SHA1(0b0b3689678522c26087919a2387ed184391d3f4)) // 27C512
	ROM_LOAD16_BYTE("e-mu proteus 1 lsb 2.05.bin", 0x00001, 0x10000, CRC(3ae786b4) SHA1(b9ad95a5cab86f2d316af34e6cb1b2873c58a9b5)) // 27C512
ROM_END

ROM_START(proteusxr)
	ROM_REGION16_BE(0x20000, "program", 0)
	ROM_LOAD16_BYTE("ip441c_emu__5089.ic38", 0x00000, 0x10000, CRC(01f11633) SHA1(a0612007b22c8e06c803ee5a5bad98e3f2bc4421)) // NMC27C512AN
	ROM_LOAD16_BYTE("ip442c_emu__5089.ic37", 0x00001, 0x10000, CRC(e8154a65) SHA1(1ec834712dc9ea4f4f01090b2fd5e558c1f08208)) // NMC27C512AN
ROM_END

ROM_START(procuss)
	ROM_REGION16_BE(0x20000, "program", 0) // v1.01
	ROM_LOAD16_BYTE("ip516aemu9115.bin", 0x00000, 0x10000, CRC(8c0be608) SHA1(54d5629e4adebd72d5d5924941be3e6319d20427))
	ROM_LOAD16_BYTE("ip517aemu9115.bin", 0x00001, 0x10000, CRC(cb2e454b) SHA1(b142285b998829ec8b3c6ce98eb94d9d4964aff6))
ROM_END

ROM_START(vintkeys)
	ROM_REGION16_BE(0x40000, "program", 0) // v1.03
	ROM_LOAD16_BYTE("vintage_keys_msb.bin", 0x00000, 0x20000, CRC(8ac3123f) SHA1(29180ea26c3de326af0f789a2d7a85face528d1a))
	ROM_LOAD16_BYTE("vintage_keys_lsb.bin", 0x00001, 0x20000, CRC(032e4064) SHA1(f59841eda06e2f348f50887d5bf10aef3a027346))
ROM_END

ROM_START(orbit9090)
	// These may or may not need to be split into separate sets
	ROM_REGION16_BE(0x80000, "program", 0)
	ROM_SYSTEM_BIOS(0, "v2", "v2.00") // Original chips are STMicro M27C2001-12F1
	ROMX_LOAD("orbit-2.00-msb.bin", 0x00000, 0x40000, CRC(6bda893e) SHA1(0ff01a46ba49fd49b8d85edcc9ac141009f73ebe), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("orbit-2.00-lsb.bin", 0x00001, 0x40000, CRC(95ef87f6) SHA1(c0dde419e9b93aa9f4d5b48c9b9bd039a33d2413), ROM_BIOS(0) | ROM_SKIP(1))
	ROM_SYSTEM_BIOS(1, "v1", "v1.00") // Original chips are TMS27C010A-15; "©EMU'96"
	ROMX_LOAD("mip840a__1496.bin", 0x00000, 0x20000, CRC(f60cde2a) SHA1(6762c1e0aa5044e504799041f960d037a600cb62), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("lip839a__1496.bin", 0x00001, 0x20000, CRC(5f16e6db) SHA1(e820bf6ce61256f191c063d4193ed4602c500065), ROM_BIOS(1) | ROM_SKIP(1))
ROM_END

ROM_START(phatt)
	ROM_REGION16_BE(0x80000, "program", 0)
	ROM_LOAD16_BYTE("e-mu planet phatt msb 1.01.bin", 0x00000, 0x40000, CRC(4db960c2) SHA1(1af53bd85ca7fb06709981ce3915cb46e8785cc2)) // 27C2001
	ROM_LOAD16_BYTE("e-mu planet phatt lsb 1.01.bin", 0x00001, 0x40000, CRC(0b2deb2b) SHA1(7715ac34f5eb9d27337eb77e62b66da8c3e80950)) // 27C2001
ROM_END

ROM_START(carnaval)
	ROM_REGION16_BE(0x80000, "program", 0)
	// Original chips are STMicro M27C2001-12F1
	ROM_LOAD16_BYTE("carnaval-1.00-msb.bin", 0x00000, 0x40000, CRC(962b3d2b) SHA1(e4668306e21fef8b0b696f08bed7c6e66941c8b2))
	ROM_LOAD16_BYTE("carnaval-1.00-lsb.bin", 0x00001, 0x40000, CRC(95506a6f) SHA1(1f6f2cce4fac36f4daa22bded1013f3dd0ca72db))
ROM_END

} // anonymous namespace


SYST(1989, proteus1,  0, 0, proteus1,  proteus1,  emu68k_state, empty_init, "E-mu Systems", "Proteus/1 16-Bit Multi-Timbral Digital Sound Module", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1989, proteusxr, 0, 0, proteusxr, proteus1,  emu68k_state, empty_init, "E-mu Systems", "Proteus/1 XR 16-Bit Multi-Timbral Digital Sound Module", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1991, procuss,   0, 0, proteusxr, procuss,   emu68k_state, empty_init, "E-mu Systems", "Pro/Cussion Maximum Percussion Module", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1993, vintkeys,  0, 0, vintkeys,  vintkeys,  emu68k_state, empty_init, "E-mu Systems", "Vintage Keys Classic Analog Keyboards", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1996, orbit9090, 0, 0, phatt,     orbit9090, emu68k_state, empty_init, "E-mu Systems", "Orbit 9090 - The Dance Planet", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1997, phatt,     0, 0, phatt,     phatt,     emu68k_state, empty_init, "E-mu Systems", "Planet Phatt - The Swing System", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1997, carnaval,  0, 0, phatt,     carnaval,  emu68k_state, empty_init, "E-mu Systems", "Carnaval - Jugando con Fuego", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



enmirage.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont, tim lindner
/***************************************************************************

    drivers/enmirage.c

    Ensoniq Mirage Sampler
    Preliminary driver by R. Belmont
    Fleshed out by tim lindner

    Models:
        DSK-8: Pratt-Reed keyboard (early 1984)
        DSK-8: Fatar keyboard (late 1984)
        DMS-8: Rack mount (1985)
        DSK-1: Unweighted keyboard, stereo output (1986)

    M6809 Map for Mirage:
        0000-7fff: 32k window on 128k of sample RAM
        8000-bfff: main RAM
        c000-dfff: optional expansion RAM
        e100-e101: 6850 UART (for MIDI)
        e200-e2ff: 6522 VIA
        e400-e407: write to both filters
        e408-e40f: filter cut-off frequency
        e410-e417: filter resonance
        e418-e41f: DAC pre-set
        e800-e803: WD1770 FDC
        ec00-ecef: ES5503 "DOC" sound chip
        f000-ffff: boot ROM

    M6809 Interrupts:
        NMI: IRQ from WD1772
        IRQ: wired-ORed: DRQ from WD1772, IRQ from ES5503, IRQ from VIA6522, IRQ from cartridge
        FIRQ: IRQ from 6850 UART

    LED / switch matrix:

            A           B           C             D         E         F         G        DP
    ROW 0:  LOAD UPPER  LOAD LOWER  SAMPLE UPPER  PLAY SEQ  LOAD SEQ  SAVE SEQ  REC SEQ  SAMPLE LOWER
    ROW 1:  3           6           9             5         8         0         2        Enter
    ROW 2:  1           4           7             up arrow  PARAM     dn arrow  VALUE    CANCEL
    L. AN:  SEG A       SEG B       SEG C         SEG D     SEG E     SEG F     SEG G    SEG DP (decimal point)
    R. AN:  SEG A       SEG B       SEG C         SEG D     SEG E     SEG F     SEG G    SEG DP

    Column number in VIA port A bits 0-2 is converted to discrete lines by a 74LS145.
    Port A bit 3 is right anode, bit 4 is left anode
    ROW 0 is read on VIA port A bit 5, ROW 1 in port A bit 6, and ROW 2 in port A bit 7.

    Keyboard models talk to the R6500/11 through the VIA shifter: CA2 is handshake, CB1 is shift clock,
    CB2 is shift data.
    This is unconnected on the rackmount version.

    Unimplemented:
        * Four Pole Low-Pass Voltage Controlled Filter section
        * External sync signal
        * Foot pedal
        * ADC feedback
        * Piano keyboard controller
        * Expansion connector
        * Stereo output

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


#include "emu.h"
#include "bus/midi/midi.h"
#include "cpu/m6809/m6809.h"
#include "formats/esq8_dsk.h"
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "machine/6522via.h"
#include "machine/6850acia.h"
#include "machine/clock.h"
#include "machine/input_merger.h"
#include "machine/wd_fdc.h"
#include "sound/es5503.h"
#include "speaker.h"
#include "video/pwm.h"

#include "enmirage.lh"

#define LOG_ADC_READ        (1U << 1)
#define LOG_FILTER_WRITE    (1U << 2)
#define VERBOSE (0)
//#define VERBOSE (LOG_ADC_READ)
//#define VERBOSE (LOG_ADC_READ|LOG_FILTER_WRITE)

#include "logmacro.h"

#define LOGADCREAD(...)     LOGMASKED(LOG_ADC_READ, __VA_ARGS__)
#define LOGFILTERWRITE(...) LOGMASKED(LOG_FILTER_WRITE, __VA_ARGS__)


namespace {

#define PITCH_TAG "pitch"
#define MOD_TAG "mod"

class enmirage_state : public driver_device
{
public:
	enmirage_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_sample_ram(*this, "sampleram", 1024 * 128, ENDIANNESS_BIG)
		, m_sample_bank(*this, "samplebank")
		, m_display(*this, "display")
		, m_fdc(*this, "wd1772")
		, m_floppy_connector(*this, "wd1772:0")
		, m_via(*this, "via6522")
		, m_irq_merge(*this, "irqmerge")
		, m_cassette(*this, "cassette")
		, m_acia(*this, "acia6850")
		, m_wheel(*this, {PITCH_TAG, MOD_TAG})
		, m_key(*this, {"pb5", "pb6", "pb7"})
	{
	}

	void mirage(machine_config &config);
	void enmirage_es5503_map(address_map &map) ATTR_COLD;

	void init_mirage();
	DECLARE_INPUT_CHANGED_MEMBER(input_changed);
	static void floppy_formats(format_registration &fr);

protected:
	virtual void machine_start() override ATTR_COLD;
	void coefficients_w(offs_t offset, uint8_t data);

private:
	void update_keypad_matrix();

	uint8_t mirage_via_read_portb();
	void mirage_via_write_porta(uint8_t data);
	void mirage_via_write_portb(uint8_t data);
	uint8_t mirage_adc_read();

	void mirage_map(address_map &map) ATTR_COLD;

	virtual void machine_reset() override ATTR_COLD;

	required_device<mc6809e_device> m_maincpu;
	memory_share_creator<uint8_t> m_sample_ram;
	required_memory_bank m_sample_bank;
	required_device<pwm_display_device> m_display;
	required_device<wd1772_device> m_fdc;
	required_device<floppy_connector> m_floppy_connector;
	required_device<via6522_device> m_via;
	required_device<input_merger_device> m_irq_merge;
	required_device<cassette_image_device> m_cassette;
	required_device<acia6850_device> m_acia;

	required_ioport_array<2> m_wheel;
	required_ioport_array<3> m_key;

	int m_mux_value;
	int m_key_col_select;

	/* temporary audio data -- used to get past startup filter calibration -- remove when filters are implemented */
	const uint8_t m_wave[34] = {0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x69};
	int m_wave_index;
};

void enmirage_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_ESQ8IMG_FORMAT);
}

static void ensoniq_floppies(device_slot_interface &device)
{
	device.option_add("35dd", PANA_JU_363);
}

uint8_t enmirage_state::mirage_adc_read()
{
	uint8_t value = 0;
	switch(m_mux_value & 0x03)
	{
		case 0:
//          value = m_cassette->input(); /* compressed and mixed input: audio in and ES 5503 (TODO) */
			value = m_wave[m_wave_index]; /* fake data to get past filter calibration (remove when filter implemented) */
			LOGADCREAD("%s, 5503 sample: channel: compressed input, data: $%02x\n", machine().describe_context(), value);
			if(++m_wave_index == 34) m_wave_index = 0;
			break;
		case 1:
			value = m_cassette->input(); /* line level and mixed input: audio in and ES 5503 (TODO) */
			LOGADCREAD("%s, 5503 sample: channel: line input, data: $%02x\n", machine().describe_context(), value);
			break;
		case 2:
			value = m_wheel[0]->read(); /* pitch wheel */
			LOGADCREAD("%s, 5503 sample: channel: pitch wheel, data: $%02x\n", machine().describe_context(), value);
			break;
		case 3:
			value = m_wheel[1]->read(); /* mod wheel */
			LOGADCREAD("%s, 5503 sample: channel: mod wheel, data: $%02x\n", machine().describe_context(), value);
			break;
	}

	return value;
}

void enmirage_state::machine_start()
{
	save_item(NAME(m_mux_value));
	save_item(NAME(m_key_col_select));
	save_item(NAME(m_wave_index));
	m_sample_bank->configure_entries(0, 4, m_sample_ram, 0x8000);
}

void enmirage_state::machine_reset()
{
	m_sample_bank->set_entry(0);
	m_mux_value = 0;
	m_wave_index = 0;
}

void enmirage_state::mirage_map(address_map &map)
{
	map(0x0000, 0x7fff).bankrw("samplebank"); // 32k window on 128k of sample RAM
	map(0x8000, 0xbfff).ram(); // main RAM
	map(0xc000, 0xdfff).ram(); // expansion RAM
	map(0xe100, 0xe101).rw("acia6850", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xe200, 0xe2ff).m(m_via, FUNC(via6522_device::map));
	map(0xe400, 0xe41f).w(FUNC(enmirage_state::coefficients_w));
	map(0xe800, 0xe803).rw(m_fdc, FUNC(wd1772_device::read), FUNC(wd1772_device::write));
	map(0xec00, 0xecef).rw("es5503", FUNC(es5503_device::read), FUNC(es5503_device::write));
	map(0xf000, 0xffff).rom().region("osrom", 0);
}

void enmirage_state::coefficients_w(offs_t offset, uint8_t data)
{
	uint8_t channel = offset & 0x07;
	uint8_t filter_input = (offset >> 3) & 0x03;

	LOGFILTERWRITE("%s, filter update: channel: %d, data: $%02x (%s%s%s%s)\n",
				machine().describe_context(),
				channel,
				data,
				(filter_input & 0x01) == 0 ? "VF" : "", /* cut-off frequency */
				(filter_input & 0x03) == 0 ? " and " : "",
				(filter_input & 0x02) == 0 ? "VQ" : "", /* filter resonance */
				(filter_input & 0x03) == 0x03 ? "preload dac" : "");
}

// port A:
//  bits 5/6/7 keypad rows 0/1/2 return
INPUT_CHANGED_MEMBER(enmirage_state::input_changed)
{
	update_keypad_matrix();
}

void enmirage_state::update_keypad_matrix()
{
	uint8_t value;

	value  = ((m_key[0]->read() >> m_key_col_select) & 0x01) << 5;
	value |= ((m_key[1]->read() >> m_key_col_select) & 0x01) << 6;
	value |= ((m_key[2]->read() >> m_key_col_select) & 0x01) << 7;

	m_via->write_pa(value);
}

// port B:
//  bit 6: IN disk load
//  bit 5: IN Q Chip sync

uint8_t enmirage_state::mirage_via_read_portb()
{
	uint8_t value = m_via->read_pb();

	floppy_image_device *floppy = m_floppy_connector ? m_floppy_connector->get_device() : nullptr;
	if (floppy)
	{
		if (floppy->dskchg_r())
			value |= 0x40;
		else
			value &= ~0x40;
	}

	return value;
}

// port A: front panel
// bits 0/1/2: dual purpose (0 to 7 lines, though a 74LS145 decoder):
//      keyboard matrix column select
//      7 segment display driver
//  bits 3/4 = right and left 7 segment display enable
//  bits 5/6/7 = Keyboard matrix row sense from 0 to 2
void enmirage_state::mirage_via_write_porta(uint8_t data)
{
	u8 segdata = data & 7;
	m_display->matrix(((data >> 3) & 3) ^ 3, (1<<segdata));

	uint8_t new_select = (data & 0x07);
	if (m_key_col_select != new_select)
	{
		m_key_col_select = new_select;
		update_keypad_matrix();
	}
}

// port B:
//  bit 7: OUT UART clock
//  bit 4: OUT disk select, motor on, and 6500/11 reset
//  bit 3: OUT sample/play
//  bit 2: OUT mic line/in
//  bit 1: OUT upper/lower bank (64k halves)
//  bit 0: OUT bank 0/bank 1 (32k quarters)

void enmirage_state::mirage_via_write_portb(uint8_t data)
{
	int bank = 0;

	// handle sound RAM bank switching
	bank = data & 0x03;
	m_sample_bank->set_entry(bank);

	// handle floppy motor on
	floppy_image_device *floppy = m_floppy_connector->get_device();
	if (floppy)
		floppy->mon_w(data & 0x10 ? 1 : 0);

	// handle 6500/11 reset (TODO)

	// record audio input mixer position
	m_mux_value = (data >> 2) & 0x03;

	// handle acia clock
	// this bit is set by the internal via timer
	int clock = (data >> 7) & 0x01;
	m_acia->write_txc(clock);
	m_acia->write_rxc(clock);
}

void enmirage_state::enmirage_es5503_map(address_map &map)
{
	map(0x00000, 0x1ffff).ram().share("sampleram");
}

void enmirage_state::mirage(machine_config &config)
{
	MC6809E(config, m_maincpu, 2000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &enmirage_state::mirage_map);

	INPUT_MERGER_ANY_HIGH(config, m_irq_merge).output_handler().set_inputline(m_maincpu, M6809_IRQ_LINE);
	// <0> via6522
	// <1> wd1772
	// <2> es5502
	// <3> cartridge connector (TODO)

	SPEAKER(config, "speaker").front_center();

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "speaker", 1.0);

	es5503_device &es5503(ES5503(config, "es5503", 8000000));
	es5503.set_channels(8);
	es5503.set_addrmap(0, &enmirage_state::enmirage_es5503_map);
	es5503.irq_func().set(m_irq_merge, FUNC(input_merger_device::in_w<2>));
	es5503.adc_func().set(FUNC(enmirage_state::mirage_adc_read));
	es5503.add_route(ALL_OUTPUTS, "speaker", 1.0);

	MOS6522(config, m_via, 3000000);
	m_via->writepa_handler().set(FUNC(enmirage_state::mirage_via_write_porta));
	m_via->readpb_handler().set(FUNC(enmirage_state::mirage_via_read_portb));
	m_via->writepb_handler().set(FUNC(enmirage_state::mirage_via_write_portb));
	m_via->irq_handler().set(m_irq_merge, FUNC(input_merger_device::in_w<0>));

	PWM_DISPLAY(config, m_display).set_size(2, 8);
	config.set_default_layout(layout_enmirage);

	ACIA6850(config, m_acia).txd_handler().set("mdout", FUNC(midi_port_device::write_txd));
	m_acia->irq_handler().set_inputline(m_maincpu, M6809_FIRQ_LINE);
	MIDI_PORT(config, "mdin", midiin_slot, "midiin").rxd_handler().set(m_acia, FUNC(acia6850_device::write_rxd));
	MIDI_PORT(config, "mdout", midiout_slot, "midiout");

	WD1772(config, m_fdc, 8000000);
	m_fdc->intrq_wr_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_fdc->drq_wr_callback().set(m_irq_merge, FUNC(input_merger_device::in_w<1>));

	FLOPPY_CONNECTOR(config, "wd1772:0", ensoniq_floppies, "35dd", enmirage_state::floppy_formats).enable_sound(true);

	// This clock allows the CPU to keep in sync with the sound chip - may not be used in the firmware
	clock_device &es5503_ca3_clock(CLOCK(config, "ca3_clock", XTAL(8'000'000) / 16));
	es5503_ca3_clock.signal_handler().set(m_via, FUNC(via6522_device::write_pb5));
}

static INPUT_PORTS_START(mirage)
	PORT_START("pb5") /* KEY ROW 0 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Load Upper")         PORT_CODE(KEYCODE_A) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Load Lower")         PORT_CODE(KEYCODE_B) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Sample Upper")       PORT_CODE(KEYCODE_C) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Play Sequence")      PORT_CODE(KEYCODE_D) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Load Sequence")      PORT_CODE(KEYCODE_E) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Save Sequence")      PORT_CODE(KEYCODE_F) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Record Sequence")    PORT_CODE(KEYCODE_G) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Sample Lower")       PORT_CODE(KEYCODE_H) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_START("pb6") /* KEY ROW 1 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3")      PORT_CODE(KEYCODE_3)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6")      PORT_CODE(KEYCODE_6)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9")      PORT_CODE(KEYCODE_9)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5")      PORT_CODE(KEYCODE_5)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8")      PORT_CODE(KEYCODE_8)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0/Prog") PORT_CODE(KEYCODE_0)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2")      PORT_CODE(KEYCODE_2)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter")  PORT_CODE(KEYCODE_ENTER)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_START("pb7") /* KEY ROW 2 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1")          PORT_CODE(KEYCODE_1)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4")          PORT_CODE(KEYCODE_4)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7")          PORT_CODE(KEYCODE_7)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("On/Up")      PORT_CODE(KEYCODE_UP)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Param")      PORT_CODE(KEYCODE_I)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Off/Down")   PORT_CODE(KEYCODE_DOWN) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Value")      PORT_CODE(KEYCODE_J)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cancel")     PORT_CODE(KEYCODE_K)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(enmirage_state::input_changed), 0)

	PORT_START(PITCH_TAG)
	PORT_BIT(0xff, 0x7f, IPT_PADDLE) PORT_NAME("Pitch Wheel") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00,0xff) PORT_CODE_INC(KEYCODE_4_PAD) PORT_CODE_DEC(KEYCODE_1_PAD) PORT_PLAYER(1)
	PORT_START(MOD_TAG)
	PORT_BIT(0xff, 0x7f, IPT_PADDLE) PORT_NAME("Mod Wheel") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00,0xff) PORT_CODE_INC(KEYCODE_6_PAD) PORT_CODE_DEC(KEYCODE_3_PAD) PORT_PLAYER(1)
INPUT_PORTS_END

ROM_START(enmirage)
	ROM_REGION(0x1000, "osrom", 0)
	ROM_LOAD("mirage.bin", 0x0000, 0x1000, CRC(9fc7553c) SHA1(ec6ea5613eeafd21d8f3a7431a35a6ff16eed56d))

	ROM_REGION(0x20000, "es5503", ROMREGION_ERASE)
ROM_END

void enmirage_state::init_mirage()
{
	floppy_image_device *floppy = m_floppy_connector ? m_floppy_connector->get_device() : nullptr;
	if (floppy)
	{
		m_fdc->set_floppy(floppy);
	}
}

} // anonymous namespace


CONS(1984, enmirage, 0, 0, mirage, mirage, enmirage_state, init_mirage, "Ensoniq", "Mirage DMS-8", MACHINE_NOT_WORKING)



ep64.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/**********************************************************************

    Enterprise Sixty Four / One Two Eight emulation

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

/*

Enterprise Sixty Four / Enterprise One Two Eight
Developed by Intelligent Software, marketed by Enterprise Computers Ltd. 1985

MAIN PCB Layout
---------------
                                        DUAL
|-----------| 9V                        TAPE_IN/OUT                             RES
|HEATSINK   | DC_IN             MON1    AND REMOTE     SR1    PR1   CN2A CN1A   |-|
|           |--||---|--||--|--| |--| |-||-||-||-||-| |----| |-----| |--| |--| |-|-|------|
|-----------|7805   | MOD  |  |-|  |-|             |-|    |-|     |-|  |-|  |-|   EXP2 --|EXTERNAL
        |---+7805   |      |                         74LS06 74LS273 74LS86 74LS32      --|EXPANSION
        |   |       |------|             74LS244                                       --|CONNECTOR
        |   |78L12 POT1  LM386                               |-----|   |-----|   EXP1  --|
   CART |   |          LM1889 LM1886                   74LS04|NICK |   |DAVE |         --|
   CONN |   |   KEYB_8     POT2     KEYB_10                  |     |   |     |         --|
        |   |            4.433619MHz     74LS145 POT3 LED    |-----|   |-----|         --|
        |---+                                                                          --|
            |                            74LS74      74LS244  74LS244  74LS245 |---------|
            |                                                                  |
            | 74LS373                    4164  4164  74F157                    |
            |                                                                  |
            | 16-2-103   LM339    LM324  4164  4164  74F157           EXOS.IC2 |
            |                                                 8MHz             |
            | 74LS373                    4164  4164                            |
            |                                                74LS04    Z80A    |
            | 74LS273                    4164  4164                            |
            |------------------------------------------------------------------|
Notes: (all IC's shown)
           Z80A - Z80A CPU, clock input 4MHz [8/2]
       EXOS.IC2 - 32k x8-bit mask ROM usually 23256 manufactured by GI (DIP24). Contains EXOS operating
                  system and built-in word processor software. A few official revisions were made and
                  there were a few unofficial revision made with additional capabilities and bug-fixes
                  ROM labelling of some official versions found....
                  9256DS-0019 (C)1984 INTELLIGENT SOFTWARE LTD ENTER 08-45-A GI
                  9256DS-0036 (C)1984 INTELLIGENT SOFTWARE LTD ENTER 05-23-A GI
           4164 - 64k x1-bit Dynamic RAM with Page Mode (DIP16)
           NICK - Custom graphics chip (QFP72)
           DAVE - Custom sound chip (QFP72)
          LM386 - National Semiconductor LM386 Low Voltage Audio Power Amplifier (DIP8)
         LM1889 - National Semiconductor LM1889 TV Video Modulator (DIP18)
         LM1886 - National Semiconductor LM1886 TV Video Matrix DAC (DIP20)
          LM339 - SGS LM339 Low Power Low Offset Voltage Quad Comparator (DIP8)
          LM324 - SGS LM324 Quad Operational Amplifier (DIP8)
         74LS04 - Hex Inverter (DIP14)
         74LS06 - Hex Inverter/Buffer with Open-Collector Outputs (DIP14)
         74LS32 - Quad 2-Input Positive OR Gate (DIP14)
         74LS74 - Dual Positive-Edge-Triggered D Flip-Flops with Preset, Clear and Complementary Outputs (DIP14)
         74LS86 - Quad 2-Input Exclusive OR Gate (DIP14)
        74LS145 - BCD to Deccimal Decoder/Driver (DIP16)
         74F157 - Quad 2-Line to 1-Line Data Selector/Multiplexer (DIP16). Early rev mainboards use 74LS158 instead
        74LS244 - Octal 3-State Noninverting Buffer/Line Driver/Line Receiver (DIP20)
        74LS245 - Octal Bus Tranceiver with Tri-State Outputs (DIP20)
        74LS273 - Octal D-Type Flip-Flop With Clear (DIP20)
        74LS373 - Octal D-Type Transparent Latches and Edge-Triggered Flip-Flops (DIP20)
           7805 - Voltage regulator. +9V DC input from DC power pack, +5V DC output
          78L12 - Voltage regulator. Voltage input via small transformer on PCB. +12V DC output
           POT1 - Potentiometer located near modulator and video output circuitry
           POT2 - Potentiometer located near video output circuitry. Probably used to fine-tune the video clock
           POT3 - Potentiometer. Possibly for video/NICK-related adjustments
            LED - LED to show +5V is present
       16-2-103 - Resistor Array (DIP16)
         KEYB_8 - 8 pin keyboard connector
        KEYB_10 - 10 pin keyboard connector
           EXP1 - 28 pin internal expansion connector (solder holes only) used for internal 64k memory expansion card
           EXP2 - 10 pin internal expansion connector (solder holes only) used for internal 64k memory expansion card
            MOD - Astec UM1233 TV modulator
            RES - Reset push button switch
           MON1 - Monitor output connector. Pinout is....

                            Green  A1 B1 NC
                            Ground A2 B2 Ground
                  Mono Comp. Video A3 B3 Blue
                             HSync A4 B4 Red
                             VSync A5 B5 Composite Sync
                                NC A6 B6 Mode Switch
                        Left Audio A7 B7 Right Audio

            SR1 - Serial/Network connector. Pinout is....

                         Reference A1 B1 Ground
                                 - A2 B2 -
                               RTS A3 B3 Data Out
                               CTS A4 B4 Data In

            PR1 - Printer connector. Pinout is....

                            Ground A1 B1 Ground
                            Strobe A2 B2 Ready
                            Data 3 A3 B3 Data 4
                                 - A4 B4 -
                            Data 2 A5 B5 Data 5
                            Data 1 A6 B6 Data 6
                            Data 0 A7 B7 Data 7

           CN2A - Joystick 2 connector
           CN1A - Joystick 1 connector
                  Pinout is....

                          Keyboard J A1 B1 Ground
                          Keyboard L A2 B2 Keyboard K
                                   - A3 B3 -
                               Right A4 B4 +5V
                                Down A5 B5 Left
                                Fire A6 B6 Up


Internal Memory Expansion PCB Layout
------------------------------------

|---------------------|
|  4164 74LS00 PL3 PL2|
|4164 74LS30 74F157   |
|4164 4164            |
|4164 4164   74F157   |
|4164 4164 74LS32  PL1|
|---------------------|
Notes: (All IC's shown)
          PL1 - 28-pin connector (solder pads only) hard-wired to solder pads EXP1 on mainboard
      PL2/PL3 - 5-pin connectors (solder pads only) hard-wired to solder pads EXP2 on mainboard
         4164 - 64k x1-bit Dynamic RAM with Page Mode (DIP16)
       74LS00 - Quad 2-Input NAND Gate (DIP14)
       74LS30 - 8-input NAND Gate (DIP14)
       74LS32 - Quad 2-Input Positive OR Gate (DIP14)
       74F157 - Quad 2-Line to 1-Line Data Selector/Multiplexer (DIP16). Early rev memory boards use 74LS158 instead

*/

/*

    TODO:

    - POST RAM errors
    - rewrite DAVE to output to discrete DAC
    - rewrite NICK
    - cassette
    - external joysticks

    http://ep.homeserver.hu/Dokumentacio/Konyvek/

*/

#include "emu.h"

#include "bus/centronics/ctronics.h"
#include "bus/ep64/exp.h"
#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "machine/ram.h"

#include "dave.h"
#include "nick.h"

#include "softlist_dev.h"
#include "speaker.h"


namespace {

#define Z80_TAG         "u1"
#define DAVE_TAG        "u3"
#define NICK_TAG        "u4"
#define CENTRONICS_TAG  "centronics"
#define RS232_TAG       "rs232"
#define CASSETTE1_TAG   "cassette1"
#define CASSETTE2_TAG   "cassette2"
#define SCREEN_TAG      "screen"
#define EP64_EXPANSION_BUS_TAG  "exp"

class ep64_state : public driver_device
{
public:
	ep64_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, Z80_TAG),
		m_dave(*this, DAVE_TAG),
		m_nick(*this, NICK_TAG),
		m_centronics(*this, CENTRONICS_TAG),
		m_rs232(*this, RS232_TAG),
		m_cassette1(*this, CASSETTE1_TAG),
		m_cassette2(*this, CASSETTE2_TAG),
		m_cart(*this, "cartslot"),
		m_exp(*this, EP64_EXPANSION_BUS_TAG),
		m_ram(*this, RAM_TAG),
		m_rom(*this, Z80_TAG),
		m_y(*this, "Y%u", 0)
	{ }

	void ep128(machine_config &config);
	void ep64(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	required_device<dave_device> m_dave;
	required_device<nick_device> m_nick;
	required_device<centronics_device> m_centronics;
	required_device<rs232_port_device> m_rs232;
	required_device<cassette_image_device> m_cassette1;
	required_device<cassette_image_device> m_cassette2;
	required_device<generic_slot_device> m_cart;
	required_device<ep64_expansion_bus_slot_device> m_exp;
	required_device<ram_device> m_ram;
	required_memory_region m_rom;
	required_ioport_array<10> m_y;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint8_t rd0_r();
	void wr0_w(uint8_t data);
	uint8_t rd1_r();
	void wr2_w(uint8_t data);

	uint8_t m_key;

	void write_centronics_busy(int state);
	int m_centronics_busy;
	void dave_128k_mem(address_map &map) ATTR_COLD;
	void dave_64k_mem(address_map &map) ATTR_COLD;
	void dave_io(address_map &map) ATTR_COLD;
	void ep64_io(address_map &map) ATTR_COLD;
	void ep64_mem(address_map &map) ATTR_COLD;
};



//**************************************************************************
//  READ/WRITE HANDLERS
//**************************************************************************

//-------------------------------------------------
//  rd0_r -
//-------------------------------------------------

uint8_t ep64_state::rd0_r()
{
	uint8_t data = 0xff;

	if (m_key < 10)
	{
		data &= m_y[m_key]->read();
	}

	return data;
}


//-------------------------------------------------
//  rd0_r -
//-------------------------------------------------

void ep64_state::wr0_w(uint8_t data)
{
	/*

	    bit     description

	    0       KEY A
	    1       KEY B
	    2       KEY C
	    3       KEY D
	    4       PRINTER _STB
	    5       CASSETTE OUT
	    6       REMOTE 1
	    7       REMOTE 2

	*/

	// keyboard
	m_key = data & 0x0f;

	// printer
	m_centronics->write_strobe(!BIT(data, 4));

	// cassette
	m_cassette1->output(BIT(data, 5) ? -1.0 : +1.0);
	m_cassette2->output(BIT(data, 5) ? -1.0 : +1.0);

	// cassette
	m_cassette1->change_state(BIT(data, 6) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
	m_cassette2->change_state(BIT(data, 7) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
}

void ep64_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

//-------------------------------------------------
//  rd1_r -
//-------------------------------------------------

uint8_t ep64_state::rd1_r()
{
	/*

	    bit     description

	    0       KBJ
	    1       KBK
	    2       KBL
	    3       PRINTER _RDY
	    4       SERIAL/NET DATA IN
	    5       SERIAL/NET STATUS IN
	    6       CASSETTE IN
	    7       ?

	*/

	uint8_t data = 0;

	// printer
	data |= m_centronics_busy << 3;

	// serial
	data |= m_rs232->rxd_r() << 4;
	data |= m_rs232->cts_r() << 5;

	// cassette
	data |= ((m_cassette1->input() < 0) || (m_cassette2->input() < 0)) << 6;

	return data;
}


//-------------------------------------------------
//  wr2_w -
//-------------------------------------------------

void ep64_state::wr2_w(uint8_t data)
{
	/*

	    bit     description

	    0       SERIAL/NET DATA OUT
	    1       SERIAL/NET STATUS OUT
	    2
	    3
	    4
	    5
	    6
	    7

	*/

	// serial
	m_rs232->write_txd(!BIT(data, 0));
	m_rs232->write_rts(!BIT(data, 1));
}



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

//-------------------------------------------------
//  ADDRESS_MAP( ep64_mem )
//-------------------------------------------------

void ep64_state::ep64_mem(address_map &map)
{
	map(0x0000, 0xffff).m(m_dave, FUNC(dave_device::z80_program_map));
}


//-------------------------------------------------
//  ADDRESS_MAP( ep64_io )
//-------------------------------------------------

void ep64_state::ep64_io(address_map &map)
{
	map(0x0000, 0xffff).m(m_dave, FUNC(dave_device::z80_io_map));
}


//-------------------------------------------------
//  ADDRESS_MAP( dave_64k_mem )
//-------------------------------------------------

void ep64_state::dave_64k_mem(address_map &map)
{
	map(0x000000, 0x007fff).rom().region(Z80_TAG, 0);
	//map(0x010000, 0x01ffff)      // mapped by the cartslot
	map(0x3f0000, 0x3fffff).m(m_nick, FUNC(nick_device::vram_map));
}


//-------------------------------------------------
//  ADDRESS_MAP( dave_128k_mem )
//-------------------------------------------------

void ep64_state::dave_128k_mem(address_map &map)
{
	dave_64k_mem(map);
	map(0x3e0000, 0x3effff).ram();
}


//-------------------------------------------------
//  ADDRESS_MAP( dave_io )
//-------------------------------------------------

void ep64_state::dave_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x80, 0x8f).m(m_nick, FUNC(nick_device::vio_map));
	map(0xb5, 0xb5).rw(FUNC(ep64_state::rd0_r), FUNC(ep64_state::wr0_w));
	map(0xb6, 0xb6).r(FUNC(ep64_state::rd1_r)).w("cent_data_out", FUNC(output_latch_device::write));
	map(0xb7, 0xb7).w(FUNC(ep64_state::wr2_w));
}



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

//-------------------------------------------------
//  INPUT_PORTS( ep64 )
//-------------------------------------------------

static INPUT_PORTS_START( ep64 )
	PORT_START("Y0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Left SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("Y1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL)

	PORT_START("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("TAB") PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')

	PORT_START("Y3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR(U'£')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))

	PORT_START("Y4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("FUNCTION 4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("FUNCTION 8") PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("FUNCTION 3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("FUNCTION 6") PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("FUNCTION 5") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("FUNCTION 7") PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("FUNCTION 2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("FUNCTION 1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_START("Y5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('_')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ERASE") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("STOP") PORT_CODE(KEYCODE_END)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("HOLD") PORT_CODE(KEYCODE_HOME)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR('\r')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ALT") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT))

	PORT_START("Y8")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Right SHIFT") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INS") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START("Y9")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END


//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( ep64 )
//-------------------------------------------------

void ep64_state::machine_start()
{
	if (m_cart->exists())
		m_dave->space(AS_PROGRAM).install_read_handler(0x010000, 0x01ffff, read8sm_delegate(*m_cart, FUNC(generic_slot_device::read_rom)));

	// state saving
	save_item(NAME(m_key));
	save_item(NAME(m_centronics_busy));
}


void ep64_state::machine_reset()
{
	m_dave->reset();
	m_nick->reset();

	wr0_w(0);
	subdevice<output_latch_device>("cent_data_out")->write(0);
	wr2_w(0);
}



//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  machine_config( ep64 )
//-------------------------------------------------

void ep64_state::ep64(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, XTAL(8'000'000)/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &ep64_state::ep64_mem);
	m_maincpu->set_addrmap(AS_IO, &ep64_state::ep64_io);

	// video hardware
	screen_device& screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.set_size(ENTERPRISE_SCREEN_WIDTH, ENTERPRISE_SCREEN_HEIGHT);
	screen.set_visarea(0, ENTERPRISE_SCREEN_WIDTH-1, 0, ENTERPRISE_SCREEN_HEIGHT-1);
	screen.set_screen_update(NICK_TAG, FUNC(nick_device::screen_update));

	NICK(config, m_nick, XTAL(8'000'000), SCREEN_TAG);
	m_nick->virq_wr_callback().set(m_dave, FUNC(dave_device::int1_w));

	// sound hardware
	SPEAKER(config, "speaker", 2).front();

	DAVE(config, m_dave, XTAL(8'000'000));
	m_dave->set_addrmap(AS_PROGRAM, &ep64_state::dave_64k_mem);
	m_dave->set_addrmap(AS_IO, &ep64_state::dave_io);
	m_dave->irq_wr().set_inputline(Z80_TAG, INPUT_LINE_IRQ0);
	m_dave->add_route(0, "speaker", 0.25, 0);
	m_dave->add_route(1, "speaker", 0.25, 1);

	// devices
	EP64_EXPANSION_BUS_SLOT(config, m_exp, nullptr);
	m_exp->set_program_space(m_dave, AS_PROGRAM);
	m_exp->set_io_space(m_dave, AS_IO);
	m_exp->irq_wr().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_exp->nmi_wr().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_exp->wait_wr().set_inputline(m_maincpu, Z80_INPUT_LINE_WAIT);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(ep64_state::write_centronics_busy));
	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set(m_dave, FUNC(dave_device::int2_w));

	CASSETTE(config, m_cassette1);
	m_cassette1->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette1->set_interface("ep64_cass");
	m_cassette1->add_route(ALL_OUTPUTS, "speaker", 0.05, 0);

	CASSETTE(config, m_cassette2);
	m_cassette2->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette2->set_interface("ep64_cass");
	m_cassette2->add_route(ALL_OUTPUTS, "speaker", 0.05, 1);

	// internal RAM
	RAM(config, m_ram).set_default_size("64K");

	// cartridge
	GENERIC_CARTSLOT(config, m_cart, generic_linear_slot, "ep64_cart", "bin,rom");

	// software lists
	SOFTWARE_LIST(config, "cart_list").set_original("ep64_cart");
	SOFTWARE_LIST(config, "cass_list").set_original("ep64_cass");
	SOFTWARE_LIST(config, "flop_list").set_original("ep64_flop");
}


//-------------------------------------------------
//  machine_config( ep128 )
//-------------------------------------------------

void ep64_state::ep128(machine_config &config)
{
	ep64(config);
	m_dave->set_addrmap(AS_PROGRAM, &ep64_state::dave_128k_mem);

	// internal RAM
	m_ram->set_default_size("128K");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( ep64 )
//-------------------------------------------------

ROM_START( ep64 )
	ROM_REGION( 0x8000, Z80_TAG, 0 )
	ROM_LOAD( "9256ds-0038_enter05-23-a.u2", 0x0000, 0x8000, CRC(d421795f) SHA1(6033a0535136c40c47137e4d1cd9273c06d5fdff) )
ROM_END

#define rom_phc64   rom_ep64


//-------------------------------------------------
//  ROM( ep128 )
//-------------------------------------------------

ROM_START( ep128 )
	ROM_REGION( 0x8000, Z80_TAG, 0 )
	ROM_LOAD( "9256ds-0019_enter08-45-a.u2", 0x0000, 0x8000, CRC(982a3b44) SHA1(55315b20fecb4441a07ee4bc5dc7153f396e0a2e) )
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY                                        FULLNAME                     FLAGS
COMP( 1985, ep64,  0,      0,      ep64,    ep64,  ep64_state, empty_init, "Intelligent Software / Enterprise Computers", "Enterprise Sixty Four",     MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
COMP( 1985, phc64, ep64,   0,      ep64,    ep64,  ep64_state, empty_init, "Intelligent Software / Hegener + Glaser",     "Mephisto PHC 64 (Germany)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
COMP( 1986, ep128, ep64,   0,      ep128,   ep64,  ep64_state, empty_init, "Intelligent Software / Enterprise Computers", "Enterprise One Two Eight",  MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )



epic14e.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for Epic 14E video display terminal.

    This green-screen terminal emulates the TeleVideo 925. It was later
    acquired by ADDS and rereleased as the Viewpoint/925+.

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

#include "emu.h"
//#include "bus/rs232/rs232.h"
#include "cpu/m6502/m6502.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/input_merger.h"
#include "machine/6522via.h"
#include "machine/mos6551.h"
#include "sound/spkrdev.h"
#include "video/scn2674.h"
#include "screen.h"
#include "speaker.h"


namespace {

class epic14e_state : public driver_device
{
public:
	epic14e_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_via(*this, "via")
		, m_speaker(*this, "speaker")
		, m_charram(*this, "charram")
		, m_attrram(*this, "attrram")
		, m_chargen(*this, "chargen")
		, m_dsw(*this, "DSW%u", 1U)
		, m_pa_select(0)
		, m_invert_screen(false)
	{
	}

	void epic14e(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	SCN2672_DRAW_CHARACTER_MEMBER(draw_character);

	u8 pa_r();
	void pa_w(u8 data);
	void pb_w(u8 data);
	void ca2_w(int state);

	u8 vram_r(offs_t offset);
	void vram_w(offs_t offset, u8 data);

	void cpu_map(address_map &map) ATTR_COLD;
	void char_map(address_map &map) ATTR_COLD;
	void attr_map(address_map &map) ATTR_COLD;

	required_device<m6502_device> m_maincpu;
	required_device<via6522_device> m_via;
	required_device<speaker_sound_device> m_speaker;
	required_shared_ptr<u8> m_charram;
	required_shared_ptr<u8> m_attrram;
	required_region_ptr<u8> m_chargen;
	required_ioport_array<3> m_dsw;

	u8 m_pa_select;
	bool m_invert_screen;
};


void epic14e_state::machine_start()
{
	save_item(NAME(m_pa_select));
	save_item(NAME(m_invert_screen));
}

SCN2672_DRAW_CHARACTER_MEMBER(epic14e_state::draw_character)
{
	const u8 chardata = m_chargen[charcode << 4 | linecount];
	u16 dots = ((chardata & 0x7f) << 2) | (BIT(chardata, 7) ? 3 : 0);
	if (m_invert_screen)
		dots = ~dots;

	for (int i = 0; i < 9; i++)
	{
		bitmap.pix(y, x++) = BIT(dots, 8) ? rgb_t::white() : rgb_t::black();
		dots <<= 1;
	}
}


u8 epic14e_state::pa_r()
{
	u8 state = 0x8f; // TODO: PA7 = shift data from keyboard?
	for (int n = 0; n < 3; n++)
		if (!BIT(m_dsw[n]->read(), m_pa_select))
			state |= 0x10 << n;
	return state;
}

void epic14e_state::pa_w(u8 data)
{
	m_pa_select = data & 0x0f;
}

void epic14e_state::pb_w(u8 data)
{
	m_speaker->level_w(BIT(data, 7));
}

void epic14e_state::ca2_w(int state)
{
	m_invert_screen = !state;
}


u8 epic14e_state::vram_r(offs_t offset)
{
	return (BIT(offset, 0) ? m_charram : m_attrram)[offset >> 1];
}

void epic14e_state::vram_w(offs_t offset, u8 data)
{
	(BIT(offset, 0) ? m_charram : m_attrram)[offset >> 1] = data;
}

void epic14e_state::cpu_map(address_map &map)
{
	map(0x0000, 0x07ff).ram();
	map(0x2000, 0x3fff).rw(FUNC(epic14e_state::vram_r), FUNC(epic14e_state::vram_w));
	map(0x6000, 0x6007).rw("pvtc", FUNC(scn2672_device::read), FUNC(scn2672_device::write));
	map(0x8000, 0x8003).rw("acia1", FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x9000, 0x9003).rw("acia2", FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0xa010, 0xa01f).m(m_via, FUNC(via6522_device::map));
	map(0xe000, 0xffff).rom().region("program", 0);
}

void epic14e_state::char_map(address_map &map)
{
	map(0x0000, 0x0fff).ram().share("charram");
}

void epic14e_state::attr_map(address_map &map)
{
	map(0x0000, 0x0fff).ram().share("attrram");
}


static INPUT_PORTS_START(epic14e)
	PORT_START("DSW1")
	PORT_DIPNAME(0x0200, 0x0200, "Modem Stop Bits") PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(0x0200, "1")
	PORT_DIPSETTING(0x0000, "2")
	PORT_DIPNAME(0x0100, 0x0100, "Modem Data Bits") PORT_DIPLOCATION("SW1:2")
	PORT_DIPSETTING(0x0000, "7")
	PORT_DIPSETTING(0x0100, "8")
	PORT_DIPNAME(0x0080, 0x0080, "Modem Protocol") PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(0x0080, "X-ON/X-OFF")
	PORT_DIPSETTING(0x0000, "CTS")
	PORT_DIPNAME(0x0078, 0x0008, "Modem Baud Rate") PORT_DIPLOCATION("SW1:7,6,5,4")
	PORT_DIPSETTING(0x0070, "50")
	PORT_DIPSETTING(0x0068, "75")
	PORT_DIPSETTING(0x0060, "110")
	PORT_DIPSETTING(0x0058, "135")
	PORT_DIPSETTING(0x0050, "150")
	PORT_DIPSETTING(0x0048, "300")
	PORT_DIPSETTING(0x0040, "600")
	PORT_DIPSETTING(0x0038, "1200")
	PORT_DIPSETTING(0x0030, "1800")
	PORT_DIPSETTING(0x0028, "2400")
	PORT_DIPSETTING(0x0020, "3600")
	PORT_DIPSETTING(0x0018, "4800")
	PORT_DIPSETTING(0x0010, "7200")
	PORT_DIPSETTING(0x0008, "9600")
	PORT_DIPSETTING(0x0000, "19200")
	PORT_DIPNAME(0x0007, 0x0007, "Modem Parity") PORT_DIPLOCATION("SW1:10,9,8")
	PORT_DIPSETTING(0x0007, "Disable")
	PORT_DIPSETTING(0x0006, "Odd")
	PORT_DIPSETTING(0x0004, "Even")
	PORT_DIPSETTING(0x0002, "Mark")
	PORT_DIPSETTING(0x0000, "Space")
	PORT_BIT(0xfc00, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("DSW2")
	PORT_DIPNAME(0x0200, 0x0200, "Auxiliary Stop Bits") PORT_DIPLOCATION("SW2:1")
	PORT_DIPSETTING(0x0200, "1")
	PORT_DIPSETTING(0x0000, "2")
	PORT_DIPNAME(0x0100, 0x0100, "Auxiliary Data Bits") PORT_DIPLOCATION("SW2:2")
	PORT_DIPSETTING(0x0000, "7")
	PORT_DIPSETTING(0x0100, "8")
	PORT_DIPNAME(0x0080, 0x0080, "Auxiliary Protocol") PORT_DIPLOCATION("SW2:3")
	PORT_DIPSETTING(0x0080, "X-ON/X-OFF")
	PORT_DIPSETTING(0x0000, "DTR")
	PORT_DIPNAME(0x0078, 0x008, "Auxiliary Baud Rate") PORT_DIPLOCATION("SW2:7,6,5,4")
	PORT_DIPSETTING(0x0070, "50")
	PORT_DIPSETTING(0x0068, "75")
	PORT_DIPSETTING(0x0060, "110")
	PORT_DIPSETTING(0x0058, "135")
	PORT_DIPSETTING(0x0050, "150")
	PORT_DIPSETTING(0x0048, "300")
	PORT_DIPSETTING(0x0040, "600")
	PORT_DIPSETTING(0x0038, "1200")
	PORT_DIPSETTING(0x0030, "1800")
	PORT_DIPSETTING(0x0028, "2400")
	PORT_DIPSETTING(0x0020, "3600")
	PORT_DIPSETTING(0x0018, "4800")
	PORT_DIPSETTING(0x0010, "7200")
	PORT_DIPSETTING(0x0008, "9600")
	PORT_DIPSETTING(0x0000, "19200")
	PORT_DIPNAME(0x0007, 0x0007, "Auxiliary Parity") PORT_DIPLOCATION("SW2:10,9,8")
	PORT_DIPSETTING(0x0007, "Disable")
	PORT_DIPSETTING(0x0006, "Odd")
	PORT_DIPSETTING(0x0004, "Even")
	PORT_DIPSETTING(0x0002, "Mark")
	PORT_DIPSETTING(0x0000, "Space")
	PORT_BIT(0xfc00, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("DSW3")
	PORT_DIPNAME(0x0300, 0x0300, "Communications Mode") PORT_DIPLOCATION("SW3:2,1")
	PORT_DIPSETTING(0x0300, "Full Duplex")
	PORT_DIPSETTING(0x0200, "Half Duplex")
	PORT_DIPSETTING(0x0100, "Block")
	PORT_DIPSETTING(0x0000, "Local")
	PORT_DIPNAME(0x0080, 0x0080, "Screen Mode") PORT_DIPLOCATION("SW3:3")
	PORT_DIPSETTING(0x0080, "Green on Black")
	PORT_DIPSETTING(0x0000, "Black on Green")
	PORT_DIPNAME(0x0040, 0x0040, "Key Click") PORT_DIPLOCATION("SW3:4")
	PORT_DIPSETTING(0x0040, DEF_STR(Off))
	PORT_DIPSETTING(0x0000, DEF_STR(On))
	PORT_DIPNAME(0x0020, 0x0020, "Screen Refresh") PORT_DIPLOCATION("SW3:5")
	PORT_DIPSETTING(0x0000, "50 Hz")
	PORT_DIPSETTING(0x0020, "60 Hz")
	PORT_DIPNAME(0x0010, 0x0010, "Terminal Emulation") PORT_DIPLOCATION("SW3:6")
	PORT_DIPSETTING(0x0010, "Epic 14E")
	PORT_DIPSETTING(0x0000, "Other")
	PORT_DIPNAME(0x0008, 0x0008, "Page/Line Attributes") PORT_DIPLOCATION("SW3:7")
	PORT_DIPSETTING(0x0008, "Page")
	PORT_DIPSETTING(0x0000, "Line")
	PORT_DIPNAME(0x0004, 0x0004, "Edit Keys") PORT_DIPLOCATION("SW3:8")
	PORT_DIPSETTING(0x0004, "Transmitted")
	PORT_DIPSETTING(0x0000, "Local")
	PORT_DIPNAME(0x0002, 0x0002, "Return Key") PORT_DIPLOCATION("SW3:9")
	PORT_DIPSETTING(0x0002, "CR")
	PORT_DIPSETTING(0x0000, "CR/LF")
	PORT_DIPNAME(0x0001, 0x0001, "CRT Saver") PORT_DIPLOCATION("SW3:10")
	PORT_DIPSETTING(0x0001, DEF_STR(Off))
	PORT_DIPSETTING(0x0000, DEF_STR(On))
	PORT_BIT(0xfc00, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END


void epic14e_state::epic14e(machine_config &config)
{
	M6502(config, m_maincpu, 17.01_MHz_XTAL / 9); // SY6502A (1.89 MHz confirmed)
	m_maincpu->set_addrmap(AS_PROGRAM, &epic14e_state::cpu_map);

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, m6502_device::IRQ_LINE);

	MOS6522(config, m_via, 17.01_MHz_XTAL / 9); // SY6522A
	m_via->readpa_handler().set(FUNC(epic14e_state::pa_r));
	m_via->writepa_handler().set(FUNC(epic14e_state::pa_w));
	m_via->writepb_handler().set(FUNC(epic14e_state::pb_w));
	m_via->ca2_handler().set(FUNC(epic14e_state::ca2_w));
	m_via->irq_handler().set("mainirq", FUNC(input_merger_device::in_w<0>));

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.5);

	I8748(config, "keybmcu", 4608000).set_disable();

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_raw(17.01_MHz_XTAL, 900, 0, 720, 315, 0, 300);
	screen.set_screen_update("pvtc", FUNC(scn2672_device::screen_update));

	scn2672_device &pvtc(SCN2672(config, "pvtc", 17.01_MHz_XTAL / 9));
	pvtc.intr_callback().set_inputline(m_maincpu, m6502_device::NMI_LINE);
	pvtc.set_character_width(9);
	pvtc.set_display_callback(FUNC(epic14e_state::draw_character));
	pvtc.set_addrmap(0, &epic14e_state::char_map);
	pvtc.set_addrmap(1, &epic14e_state::attr_map);
	pvtc.set_screen("screen");
	// TODO: Serial keyboard clocked at 60 Hz frame rate

	mos6551_device &acia1(MOS6551(config, "acia1", 17.01_MHz_XTAL / 9)); // SY6551A
	acia1.set_xtal(1.8432_MHz_XTAL);
	acia1.irq_handler().set("mainirq", FUNC(input_merger_device::in_w<1>));

	mos6551_device &acia2(MOS6551(config, "acia2", 17.01_MHz_XTAL / 9)); // SY6551A
	acia2.set_xtal(1.8432_MHz_XTAL); // each ACIA has its own XTAL
	acia2.irq_handler().set("mainirq", FUNC(input_merger_device::in_w<2>));

	//RS232_PORT(config, "modem", default_rs232_devices, nullptr);
	//RS232_PORT(config, "aux", default_rs232_devices, nullptr);
}


ROM_START(epic14e)
	ROM_REGION(0x2000, "program", 0)
	ROM_LOAD("u6.bin",  0x0000, 0x1000, CRC(014b5da0) SHA1(190c2d48c6928d143458ba094f785d40ac29f2c0))
	ROM_LOAD("u13.bin", 0x1000, 0x1000, CRC(2b406a88) SHA1(e619cc020ab5eabad99967b27cb969ceb191f5ee))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("u24.bin", 0x0000, 0x1000, CRC(da409f03) SHA1(5a90a6b865dad20dc3f455448670b4f5baa55028))

	ROM_REGION(0x0400, "keybmcu", 0)
	ROM_LOAD("246.bin", 0x0000, 0x0400, NO_DUMP)
ROM_END

} // anonymous namespace


COMP(1982, epic14e, 0, 0, epic14e, epic14e, epic14e_state, empty_init, "Epic Computer Products", "Epic 14E (v1.0)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE)



epoch_tv_globe.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/******************************************************************************

main SoC is marked

PONTO
PONTO-1
QHLL3.03
TAIWAN 0915

main PCB is marked

LCT REV1.9
20-K5410100G2
Tiger-Main
KTG-KZ003-08

is this related to koto_zevio.cpp, Koto Laboratory is credited for the hardware
development of both, and both are 3D capable Plug and Play SoCs

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

#include "emu.h"

#include "cpu/arm7/arm7.h"

#include "screen.h"
#include "speaker.h"


namespace {

class epoch_tv_globe_state : public driver_device
{
public:
	epoch_tv_globe_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
	{ }

	void epoch_tv_globe(machine_config &config);

protected:
	virtual void machine_start() override;
	virtual void machine_reset() override;

private:
	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void arm_map(address_map &map);
};

uint32_t epoch_tv_globe_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void epoch_tv_globe_state::machine_start()
{

}

void epoch_tv_globe_state::machine_reset()
{
}

static INPUT_PORTS_START( epoch_tv_globe )
INPUT_PORTS_END

void epoch_tv_globe_state::arm_map(address_map &map)
{
	map(0x00000000, 0x007fffff).rom().region("maincpu", 0);
}


void epoch_tv_globe_state::epoch_tv_globe(machine_config &config)
{
	ARM9(config, m_maincpu, 24000000 * 4); // unknown ARM core, unknown frequency (24Mhz XTAL)
	m_maincpu->set_addrmap(AS_PROGRAM, &epoch_tv_globe_state::arm_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320, 262);
	m_screen->set_visarea(0, 320-1, 0, 240-1);
	m_screen->set_screen_update(FUNC(epoch_tv_globe_state::screen_update));

	SPEAKER(config, "speaker", 2).front();
}

ROM_START( eptvglob )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "a25l010.ic4", 0x000000, 0x20000, CRC(2e28c7a6) SHA1(ea26f8ccb9882e21f3d415af59fc04bdde36db6a) )

	// was also an ATMLH904 device at IC7, but it was empty

	ROM_REGION( 0x8400000, "nand", ROMREGION_ERASEFF )
	ROM_LOAD( "k9f1g08u0b.ic8", 0x000000, 0x8400000, CRC(f1880c56) SHA1(c50f01f799b3296cda56d05a02a59aa78e0c8422) )
ROM_END

ROM_START( digixar )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "mr27t640.ic6", 0x000000, 0x800000, CRC(f593ac1b) SHA1(58cafab21d690de23b4781800c272bebf6b2b46f) )
ROM_END



} // anonymous namespace

CONS( 201?, eptvglob,       0,              0,      epoch_tv_globe, epoch_tv_globe, epoch_tv_globe_state, empty_init, "Epoch", "TV Globe (Japan)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 201?, digixar,        0,              0,      epoch_tv_globe, epoch_tv_globe, epoch_tv_globe_state, empty_init, "Bandai / Koto", "Digimon X Arena (Japan)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



ergo201.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Micro-Term ERGO 201 terminal.

    The ERGO 201 belongs to one half of a paired series of terminals. Half
    of these terminals emulate the VT100 and support a 132 column mode; the
    other half emulate the VT52, ADM3A and ACT-5A instead but do appear to
    have been influenced by the VT100 in overall design.

    The first members of this series, the MIME 340 (non-VT100 emulation)
    and MIME 740 (VT100 emulation), were single units with non-detachable
    keyboards like Micro-Term's older ACT and MIME terminals. They were
    respectively succeeded by the ERGO 2000 and ERGO 3001 with tiltable
    screens and similar but detached keyboards. The ERGO 201 and ERGO 301
    appear to be low-cost redesigns of these two with keyboards even more
    closely resembling the VT100's, with a Plot 10 graphics board as an
    option. Also introduced around this time was the ERGO 4000, a
    portrait-mode terminal displaying 66 lines of 80 characters.

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

#include "emu.h"
//#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/ay31015.h"
#include "machine/clock.h"
#include "machine/er1400.h"
//#include "machine/ergo201_kbd.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "video/tms9927.h"
#include "screen.h"


namespace {

class ergo201_state : public driver_device
{
public:
	ergo201_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_earom(*this, "earom")
		, m_kbuart(*this, "kbuart")
		, m_vtac(*this, "vtac")
		, m_chargen(*this, "chargen")
	{
	}

	void ergo201(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void earom_latch_w(u8 data);
	u8 status_r();

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_device<er1400_device> m_earom;
	required_device<ay31015_device> m_kbuart;
	required_device<tms9927_device> m_vtac;
	required_region_ptr<u8> m_chargen;
};

void ergo201_state::machine_start()
{
	m_kbuart->write_tsb(0);
	m_kbuart->write_nb1(1);
	m_kbuart->write_nb2(1);
	m_kbuart->write_np(1);
	m_kbuart->write_cs(1);
	m_kbuart->write_swe(0);
}

u32 ergo201_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}


void ergo201_state::earom_latch_w(u8 data)
{
	m_earom->c1_w(BIT(data, 0));
	m_earom->data_w(BIT(data, 1));
	m_earom->c3_w(BIT(data, 2));
	m_earom->c2_w(BIT(data, 3));
	m_earom->clock_w(BIT(data, 4));
}

u8 ergo201_state::status_r()
{
	return (m_kbuart->dav_r() << 1) | (m_earom->data_r() << 5);
}

void ergo201_state::mem_map(address_map &map)
{
	map(0x0000, 0x5fff).rom().region("program", 0);
	map(0x1000, 0x1000).nopw();
	map(0x2000, 0x2000).unmapw(); // screen brightness?
	map(0x3000, 0x300f).w(m_vtac, FUNC(tms9927_device::write));
	map(0x4000, 0x4000).w(FUNC(ergo201_state::earom_latch_w));
	map(0x5000, 0x5000).w(m_kbuart, FUNC(ay31015_device::transmit));
	map(0x6000, 0x6000).r(m_kbuart, FUNC(ay31015_device::receive));
	map(0x6001, 0x6001).r(FUNC(ergo201_state::status_r));
	map(0x7000, 0x77ff).ram();
	map(0x8000, 0x87ff).ram();
	map(0xc000, 0xc7ff).ram();
}

void ergo201_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0xf4, 0xf7).rw("dart", FUNC(z80dart_device::cd_ba_r), FUNC(z80dart_device::cd_ba_w));
	map(0xf8, 0xfb).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
}


static INPUT_PORTS_START(ergo201)
INPUT_PORTS_END

static const z80_daisy_config daisy_chain[] =
{
	{ "ctc" },
	{ "dart" },
	{ nullptr }
};

void ergo201_state::ergo201(machine_config &config)
{
	Z80(config, m_maincpu, 16.313_MHz_XTAL / 4); // SGS Z8400AB1 (divider guessed)
	m_maincpu->set_addrmap(AS_PROGRAM, &ergo201_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &ergo201_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	z80ctc_device &ctc(Z80CTC(config, "ctc", 16.313_MHz_XTAL / 4)); // SGS Z8430AB1
	ctc.set_clk<0>(16.313_MHz_XTAL / 53);
	ctc.set_clk<1>(16.313_MHz_XTAL / 53);
	ctc.set_clk<2>(16.313_MHz_XTAL / 53);
	ctc.zc_callback<0>().set("dart", FUNC(z80dart_device::rxca_w));
	ctc.zc_callback<1>().set("dart", FUNC(z80dart_device::txca_w));
	ctc.zc_callback<2>().set("dart", FUNC(z80dart_device::rxtxcb_w));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80dart_device &dart(Z80DART(config, "dart", 16.313_MHz_XTAL / 4)); // Zilog Z8470A PS
	dart.out_txda_callback().set("dart", FUNC(z80dart_device::rxa_w));
	dart.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	AY31015(config, m_kbuart); // GI AY-3-1015D
	m_kbuart->set_auto_rdav(true);

	clock_device &kbclock(CLOCK(config, "kbclock", 10000));
	kbclock.signal_handler().set(m_kbuart, FUNC(ay31015_device::write_rcp));
	kbclock.signal_handler().append(m_kbuart, FUNC(ay31015_device::write_tcp));

	ER1400(config, m_earom); // GI ER-1400

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(16.313_MHz_XTAL, 855, 0, 720, 318, 0, 300);
	screen.set_screen_update(FUNC(ergo201_state::screen_update));
	screen.set_color(rgb_t::green()); // P31

	CRT5037(config, m_vtac, 16.313_MHz_XTAL / 9); // TI TMS9937NL
	m_vtac->set_char_width(9);
	m_vtac->set_screen("screen");
}


ROM_START(ergo201)
	ROM_REGION(0x6000, "program", 0) // Checksum is off by a few bits, so at least one dump is bad
	ROM_LOAD("201.u18", 0x0000, 0x2000, CRC(fec9fa3c) SHA1(3c1f9de9f62e74fbd1c9b67736d0959c0f6b46b1) BAD_DUMP) // M5L2764K
	ROM_LOAD("r1.0_special_5786.u73", 0x2000, 0x2000, CRC(5a31b6bc) SHA1(6929fa8f6d481790cd43732a9a97a9110ad9fb6c)) // MBM2764-25
	ROM_LOAD("201.u17", 0x4000, 0x2000, CRC(e7197403) SHA1(96fff6fb30eeac616d71da731b0ea79c8cd35096)) // M5L2764K

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("201.u53", 0x0000, 0x1000, CRC(907dac04) SHA1(5bae6680f1ef3f5335a223bdf403e8f3ef272430)) // MBM2732A-35
ROM_END

} // anonymous namespace


COMP(1985, ergo201, 0, 0, ergo201, ergo201, ergo201_state, empty_init, "Micro-Term / Kurzweil Computer Products", "ERGO 201 (Special #9233)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



esp250c.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    Esprit Systems 250C

    16 color serial terminal

    Hardware:
    - Intel N80C188XL25
    - AMIC A290021 256k flash memory
    - 2x W24257AJ 32k SRAM
    - TERMTEK TKA-200 A410A0011 ASIC
    - 32 MHz XTAL, 50 MHz XTAL, XTAL labeled "7.3F5G"
    - Buzzer

    External:
    - PC/PS2 keyboard connector
    - 25-pin RS232 host port
    - 9-pin serial printer
    - Parallel printer
    - VGA monitor (48.4 kHz horizontal, 70 Hz vertical)

    TODO:
    - Almost everything (shows an initial screen)

    Notes:
    - Other models in this line: 350C (with Ethernet)
    - PCB labeled "TKP-635 VER:1.5"
    - 10x16 cell 800x416 in 80 column mode
    - 9x16 cell 1188x416 in 132 column mode

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

#include "emu.h"
#include "cpu/i86/i186.h"

#include "emupal.h"
#include "multibyte.h"
#include "screen.h"


namespace {


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

class esp250c_state : public driver_device
{
public:
	esp250c_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen"),
		m_palette(*this, "palette"),
		m_gfxdecode(*this, "gfxdecode"),
		m_vram(*this, "vram"),
		m_chargen(*this, "chargen")
	{ }

	void esp250c(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<i80188_cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<gfxdecode_device> m_gfxdecode;
	required_shared_ptr<uint8_t> m_vram;
	required_shared_ptr<uint8_t> m_chargen;

	bool m_nmi_enable = false;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	void vblank_w(int state);
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void unk_f0_w(uint8_t data);
};


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

void esp250c_state::mem_map(address_map &map)
{
	map(0x00000, 0x0ffff).ram();
	map(0x10000, 0x1ffff).ram().share("vram");
	map(0x20000, 0x23fff).ram().share("chargen");
	map(0xc0000, 0xfffff).rom().region("maincpu", 0);
}

void esp250c_state::io_map(address_map &map)
{
	map(0x00, 0x00).lr8(NAME([this] () { logerror("read 0x00\n"); return 0x10; }));
	map(0x11, 0x11).lr8(NAME([this] () { logerror("read 0x11\n"); return 0x09; }));
	map(0x19, 0x19).lr8(NAME([this] () { logerror("read 0x19\n"); return 0x09; }));
	map(0xf0, 0xf0).w(FUNC(esp250c_state::unk_f0_w));
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( esp250c )
INPUT_PORTS_END


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

void esp250c_state::vblank_w(int state)
{
	// nmi needs a periodic source, might be vblank
	if (m_nmi_enable)
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}

uint32_t esp250c_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	m_gfxdecode->gfx(0)->mark_all_dirty();

	const pen_t *const pen = m_palette->pens();

	for (unsigned y = 0; y < 26; y++)
	{
		uint32_t addr = get_u24le(&m_vram[y * 3]);

		for (unsigned x = 0; x < 80; x++)
		{
			uint8_t code = m_vram[addr++];

			for (int i = 0; i < 16; i++)
			{
				uint16_t data = m_chargen[(code << 5) + i];

				// fixed for now
				rgb_t fg = pen[7];
				rgb_t bg = pen[0];

				for (int p = 0; p < 10; p++)
					bitmap.pix(y * 16 + i, x * 10 + p) = BIT(data, 9 - p) ? fg : bg;
			}
		}
	}

	return 0;
}

static const gfx_layout char_layout =
{
	8, 16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8 * 16
};

static GFXDECODE_START( chars )
	GFXDECODE_RAM("chargen", 0, char_layout, 0, 1)
GFXDECODE_END


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

void esp250c_state::unk_f0_w(uint8_t data)
{
	logerror("io 0xf0 = %02x\n", data);

	// maybe
	m_nmi_enable = bool(BIT(data, 3));
}

void esp250c_state::machine_start()
{
	// register for save states
	save_item(NAME(m_nmi_enable));
}

void esp250c_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void esp250c_state::esp250c(machine_config &config)
{
	I80188(config, m_maincpu, 50_MHz_XTAL); // guess
	m_maincpu->set_addrmap(AS_PROGRAM, &esp250c_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &esp250c_state::io_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(50_MHz_XTAL, 1034, 0, 800, 690, 0, 416); // wrong
	m_screen->set_screen_update(FUNC(esp250c_state::screen_update));
	m_screen->screen_vblank().set(FUNC(esp250c_state::vblank_w));

	PALETTE(config, m_palette).set_entries(16);

	GFXDECODE(config, m_gfxdecode, m_palette, chars);
}


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

ROM_START( esp250c )
	ROM_REGION(0x40000, "maincpu", 0)
	ROM_LOAD("tk-635_v035.bin", 0x00000, 0x40000, CRC(8e907958) SHA1(e0d5d39656e460933aa4e7d9d0c1e16bef414f6d))
	// character data from 0x20000 to 0x2ffff
	// version at 0x30000 "V0.35 13MAY"
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY           FULLNAME  FLAGS
COMP( 2005, esp250c, 0,      0,      esp250c, esp250c, esp250c_state, empty_init, "Esprit Systems", "250C",   MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



esprit.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************************

2017-11-02 Skeleton

Hazeltine Esprit terminals.

Espirit: R6502P, R6531, HD46505RP, MC6850P, 16.5888

Espirit3: 2x R6551AP, HD46850P (6850), R6502BP, R6545-1AP, R6522AP, RO-3-9333B, 1.8432, 17.9712


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

#include "emu.h"
#include "cpu/m6502/m6502.h"
#include "machine/input_merger.h"
#include "machine/6850acia.h"
#include "machine/6522via.h"
#include "machine/mos6551.h"
#include "video/mc6845.h"
#include "emupal.h"
#include "screen.h"


namespace {

class esprit_state : public driver_device
{
public:
	esprit_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_p_chargen(*this, "chargen")
		, m_p_videoram(*this, "videoram")
		, m_palette(*this, "palette")
	{ }

	void esprit(machine_config &config);
	void esprit3(machine_config &config);

	void init_init();

private:
	MC6845_UPDATE_ROW(crtc_update_row);
	MC6845_ON_UPDATE_ADDR_CHANGED(crtc_update_addr);

	void mem3_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_p_chargen;
	required_shared_ptr<u8> m_p_videoram;
	optional_device<palette_device> m_palette;
};

void esprit_state::mem_map(address_map &map)
{
	map.global_mask(0x7fff);
	map(0x0058, 0x0058).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x0059, 0x0059).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x0078, 0x0079).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0x0080, 0x00ff).mirror(0x100).ram();
	//map(0x0780, 0x078e).rw("rrioc", FUNC(r6531_device::io_r), FUNC(r6531_device::io_w));
	map(0x2000, 0x27ff).ram().share("videoram");
	map(0x7000, 0x7fff).rom().region("roms", 0);
}

void esprit_state::mem3_map(address_map &map)
{
	map(0x0000, 0x202f).ram();
	map(0x2030, 0x3fff).ram().share("videoram"); // it might start at 3000
	map(0x81c0, 0x81c0).rw("crtc", FUNC(r6545_1_device::status_r), FUNC(r6545_1_device::address_w));
	map(0x81c1, 0x81c1).rw("crtc", FUNC(r6545_1_device::register_r), FUNC(r6545_1_device::register_w));
	map(0x93c0, 0x93c1).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0x95c0, 0x95c3).rw("acia1", FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x99c0, 0x99c3).rw("acia2", FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0xb1c0, 0xb1cf).m("via", FUNC(via6522_device::map));
	map(0xe000, 0xffff).rom().region("roms", 0);
}

static INPUT_PORTS_START( esprit )
INPUT_PORTS_END

MC6845_UPDATE_ROW(esprit_state::crtc_update_row)
{
	rgb_t const *const pens = m_palette->palette()->entry_list_raw();
	uint32_t *p = &bitmap.pix(y);

	for (uint16_t x = 0; x < x_count; x++)
	{
		uint16_t mem = (ma + x) & 0x7ff;
		uint8_t chr = m_p_videoram[mem];

		/* get pattern of pixels for that character scanline */
		uint16_t gfx = m_p_chargen[(chr<<4) | ra] ^ ((x == cursor_x) ? 0x1ff : 0);

		/* Display a scanline of a character (9 pixels) */
		*p++ = pens[BIT(gfx, 8)];
		*p++ = pens[BIT(gfx, 7)];
		*p++ = pens[BIT(gfx, 6)];
		*p++ = pens[BIT(gfx, 5)];
		*p++ = pens[BIT(gfx, 4)];
		*p++ = pens[BIT(gfx, 3)];
		*p++ = pens[BIT(gfx, 2)];
		*p++ = pens[BIT(gfx, 1)];
		*p++ = pens[BIT(gfx, 0)];
	}
}

MC6845_ON_UPDATE_ADDR_CHANGED(esprit_state::crtc_update_addr)
{
}

/* F4 Character Displayer */
static const gfx_layout esprit_charlayout =
{
	8, 12,                  /* 8 x 12 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0, 8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_esprit )
	GFXDECODE_ENTRY( "chargen", 0x0000, esprit_charlayout, 0, 1 )
GFXDECODE_END

void esprit_state::init_init()
{
	// chargen is incomplete, copy the first half into the vacant second half
	for (u16 i = 0; i < 0x800; i++)
		m_p_chargen[0x800 | i] = m_p_chargen[i];
}


void esprit_state::esprit(machine_config &config)
{
	M6502(config, m_maincpu, 16.5888_MHz_XTAL / 18); // divider guessed
	m_maincpu->set_addrmap(AS_PROGRAM, &esprit_state::mem_map);

	//R6531(config, "rrioc", 16.5888_MHz_XTAL / 18);

	acia6850_device &acia(ACIA6850(config, "acia"));
	acia.irq_handler().set_inputline(m_maincpu, m6502_device::NMI_LINE);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_raw(16.5888_MHz_XTAL, 900, 0, 720, 307, 0, 288);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	GFXDECODE(config, "gfxdecode", m_palette, gfx_esprit);
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	/* Devices */
	mc6845_device &crtc(MC6845(config, "crtc", 16.5888_MHz_XTAL / 9));
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(9);
	crtc.set_update_row_callback(FUNC(esprit_state::crtc_update_row));
}

void esprit_state::esprit3(machine_config &config)
{
	M6502(config, m_maincpu, 17.9712_MHz_XTAL / 18); // divider guessed
	m_maincpu->set_addrmap(AS_PROGRAM, &esprit_state::mem3_map);

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, m6502_device::IRQ_LINE);

	acia6850_device &acia(ACIA6850(config, "acia"));
	acia.irq_handler().set("mainirq", FUNC(input_merger_device::in_w<2>));

	mos6551_device &acia1(MOS6551(config, "acia1", 17.9712_MHz_XTAL / 18));
	acia1.set_xtal(1.8432_MHz_XTAL);
	acia1.irq_handler().set("mainirq", FUNC(input_merger_device::in_w<0>));

	mos6551_device &acia2(MOS6551(config, "acia2", 17.9712_MHz_XTAL / 18));
	acia2.set_xtal(1.8432_MHz_XTAL);
	acia2.irq_handler().set("mainirq", FUNC(input_merger_device::in_w<1>));

	via6522_device &via(MOS6522(config, "via", 17.9712_MHz_XTAL / 18));
	via.irq_handler().set_inputline(m_maincpu, m6502_device::NMI_LINE);
	via.writepb_handler().set("acia", FUNC(acia6850_device::write_rxc)).bit(7);
	via.writepb_handler().append("acia", FUNC(acia6850_device::write_txc)).bit(7);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_raw(17.9712_MHz_XTAL, 936, 0, 720, 320, 0, 288);
	screen.set_screen_update("crtc", FUNC(r6545_1_device::screen_update));

	GFXDECODE(config, "gfxdecode", m_palette, gfx_esprit);
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	r6545_1_device &crtc(R6545_1(config, "crtc", 17.9712_MHz_XTAL / 9));
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(9);
	crtc.set_update_row_callback(FUNC(esprit_state::crtc_update_row));
	crtc.set_on_update_addr_change_callback(FUNC(esprit_state::crtc_update_addr));
	crtc.out_hsync_callback().set("via", FUNC(via6522_device::write_pb6)).invert();
}

ROM_START( esprit )
	// Esprit
	ROM_REGION( 0x1000, "roms", 0 )
	ROM_LOAD( "hazeltine_esprit.u19",    0x0000, 0x1000, CRC(6fdec792) SHA1(a1d1d68c8793e7e15ab5cd17682c299dff3985cb) )
	ROM_REGION( 0x1000, "chargen", ROMREGION_INVERT )
	ROM_LOAD( "hazeltine_esprit.u26",    0x0000, 0x0804, CRC(93f45f13) SHA1(1f493b44124c348759469e24fdfa8b7c52fe6fac) )
	ROM_REGION( 0x0800, "rrioc", 0 )
	ROM_LOAD( "r3198-11.u20",            0x0000, 0x0800, NO_DUMP ) // internal ROM apparently unused
ROM_END

ROM_START( esprit3 )
	// Esprit III
	ROM_REGION( 0x10000, "roms", 0 )
	ROM_LOAD( "hazeltine_espritiii.u5",  0x0000, 0x2000, CRC(fd63dad1) SHA1(b2a3e7db8480b28cab2b2834ad89fb6257f13cba) )
	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "hazeltine_espritiii.u19", 0x0000, 0x1000, CRC(33e4a8ef) SHA1(e19c84a3c5f94812928ea84bab3ede7970dd5e72) )
ROM_END

} // anonymous namespace


COMP( 1981, esprit,  0,      0, esprit,  esprit, esprit_state, init_init,  "Hazeltine", "Esprit",     MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1981, esprit3, esprit, 0, esprit3, esprit, esprit_state, empty_init, "Hazeltine", "Esprit III", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



esq1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont, Olivier Galibert, Felipe Sanches
/***************************************************************************

    ensoniq/esq1.cpp

    Ensoniq ESQ-1 Digital Wave Synthesizer
    Ensoniq ESQ-M (rack-mount ESQ-1)
    Ensoniq SQ-80 Cross Wave Synthesizer
    Driver by R. Belmont and O. Galibert

    Map for ESQ-1 and ESQ-m:
    0000-1fff: OS RAM
    2000-3fff: Cartridge
    4000-5fff: SEQRAM
    6000-63ff: ES5503 DOC
    6400-67ff: MC2681 DUART
    6800-6fff: AD7524 (CV_MUX)
    7000-7fff: OS ROM low (banked)
    8000-ffff: OS ROM high (fixed)

    Map for SQ-80:
    0000-1fff: OS RAM
    2000-3fff: Cartridge
    4000-5fff: DOSRAM or SEQRAM (banked)
    6000-63ff: ES5503 DOC
    6400-67ff: MC2681 DUART
    6800-6bff: AD7524 (CV_MUX)
    6c00-6dff: Mapper (bit 0 only - determines DOSRAM or SEQRAM at 4000)
    6e00-6fff: WD1772 FDC (not present on ESQ1)
    7000-7fff: OS ROM low (banked)
    8000-ffff: OS ROM high (fixed)

    CV_MUX area:
    write to        output goes to
    $68f8   $00     D/A converter
    $68f0   -$08    Filter Frequency (FF)
    $68e8   -$10    Filter Resonance (Q)
    $68d8   -$20    Final DCA (ENV4)
    $68b8   -$40    Panning (PAN)
    $6878   -$80    Floppy (Motor/LED on - SQ-80 only)

ESQ1: 8x CEM3379 VC Signal Processor Filter/Mix/VCA, 1x CEM3360 Dual VCA, 4x SSM2300
SQ-80: 8x CEM3379 VC Signal Processor - Filter/Mix/VCA, 1x CEM3360 Dual VCA, 4x SSM2300


If SEQRAM is mapped at 4000, DUART port 2 determines the 32KB "master bank" and ports 0 and 1
determine which of the 4 8KB "sub banks" is visible.

Output ports 3 to 1 determine the 4kB page which should be shown at $7000 to $7fff.

IRQ sources are the DUART and the DRQ line from the FDC (SQ-80 only).
NMI is from the IRQ line on the FDC (again, SQ-80 only).

TODO:
     - Analog filters and VCA on the back end of the 5503 (inaccurate)
     - DUART seems to keep interrupting even after MIDI xmit buffer becomes empty

NOTES:
    Commands from KPC are all 2 bytes

    first byte: command code, bit 7 is 1 = press, 0 = release
    second byte is source: 00 = panel  01 = internal keyboard

    04 SEQ
    05 CART A
    06 CART B
    07 INT
    08 1 / SEQ 1
    09 2 / SEQ 2
    0A 3 / SEQ 3
    0B 4 / SONG
    0C COMPARE
    0D DATA UP
    0E DATA DOWN
    0F WRITE
    10 = UPPER 1 (buttons above display)
    11 = UPPER 2
    12 = UPPER 3
    13 = UPPER 4
    14 = UPPER 5
    15 = LOWER 1 (buttons below display)
    16 = LOWER 2
    17 = LOWER 3
    18 = LOWER 4
    19 = LOWER 5
    1a = LFO 1
    1b = ENV 2
    1c = MASTER
    1d = CREATE / ERASE
    1e = SELECT
    1f = RECORD
    20 = STORAGE
    21 = EDIT
    22 = MIX
    23 = STOP / CONT
    24 = MIDI
    25 = CONTROL
    26 = LOCATE
    27 = PLAY
    28 = OSC 1
    29 = OSC 2
    2A = OSC 3
    2B = ENV 1
    2C = DCA 1
    2D = DCA 2
    2E = DCA 3
    2F = LFO 2
    30 = LFO 3
    31 = FILTER
    32 = ENV 4
    33 = ENV 3
    34 = DCA 4
    35 = MODES
    36 = SPLIT / LAYER


    Analog filters (CEM3379):

    The analog part is relatively simple.  The digital part outputs 8
    voices, which are filtered, amplified, panned then summed
    together.

    The filtering stage is a 4-level lowpass filter with a loopback:


             +-[+]-<-[*-1]--------------------------+
             |  |                                   |
             ^ [*r]                                 |
             |  |                                   |
             |  v                                   ^
    input ---+-[+]--[LPF]---[LPF]---[LPF]---[LPF]---+--- output

    All 4 LPFs are identical, with a transconductance G:

    output = 1/(1+s/G)^4 * ( (1+r)*input - r*output)

    or

    output = input * (1+r)/((1+s/G)^4+r)

    to which the usual z-transform can be applied (see votrax.c)

    G is voltage controlled through the Vfreq input, with the formula (Vfreq in mV):

         G = 6060*exp(Vfreq/28.5)

    That gives a cutoff frequency (f=G/(2pi)) of 5Hz at 5mV, 964Hz at
    28.5mV and 22686Hz at 90mV.  The resistor ladder between the DAC
    and the input seem to map 0..255 into a range of -150.4mV to
    +83.6mV.

    The resonance is controlled through the Vq input pin, and is not
    well defined.  Reading between the lines the control seems linear
    and tops when then circuit is self-oscillation, at r=4.

    The amplification is exponential for a control voltage between 0
    to 0.2V from -100dB to -20dB, and then linear up to 5V at 0dB.  Or
    in other words:
         amp(Vca) = Vca < 0.2 ? 10**(-5+20*Vca) : Vca*0.1875 + 0.0625


    Finally the panning is not very described.  What is clear is that
    the control voltage at 2.5V gives a gain of -6dB, the max
    attenuation at 0/5V is -100dB.  The doc also says the gain is
    linear between 1V and 3.5V, which makes no sense since it's not
    symmetrical, and logarithmic afterwards, probably meaning
    exponential, otherwise the change between 0 and 1V would be
    minimal.  So we're going to do some assumptions:
        - 0-1V exponential from -100Db to -30dB
        - 1V-2.5V linear from -30dB to -6dB
        - 2.5V-5V is 1-amp at 2.5V-v

    Note that this may be incorrect, maybe to sum of squares should be
    constant, the half-point should be at -3dB and the linearity in dB
    space.


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

#include "emu.h"

#include "esqpanel.h"

#include "bus/midi/midi.h"
#include "cpu/m6809/m6809.h"
#include "machine/input_merger.h"
#include "machine/mc68681.h"
#include "machine/wd_fdc.h"
#include "sound/es5503.h"

#include "speaker.h"

//#define VERBOSE 1
#include "logmacro.h"

#include "esq1.lh"


#define WD1772_TAG      "wd1772"

class esq1_filters : public device_t,
						public device_sound_interface
{
public:
	// construction/destruction
	esq1_filters(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);

	void set_vca(int channel, uint8_t value);
	void set_vpan(int channel, uint8_t value);
	void set_vq(int channel, uint8_t value);
	void set_vfc(int channel, uint8_t value);

protected:
	// device-level overrides
	virtual void device_start() override ATTR_COLD;

	// device_sound_interface overrides
	virtual void sound_stream_update(sound_stream &stream) override;

private:
	struct filter {
		uint8_t vca = 0, vpan = 0, vq = 0, vfc = 0;
		double amp = 0, lamp = 0, ramp = 0;
		double a[5]{}, b[5]{};
		double x[4]{}, y[4]{};
	};

	filter filters[8]{};

	sound_stream *stream = nullptr;

	void recalc_filter(filter &f);
};

DEFINE_DEVICE_TYPE(ESQ1_FILTERS, esq1_filters, "esq1_filters", "ESQ1 Filters stage")

esq1_filters::esq1_filters(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, ESQ1_FILTERS, tag, owner, clock)
	, device_sound_interface(mconfig, *this)
{
}

void esq1_filters::set_vca(int channel, uint8_t value)
{
	if(filters[channel].vca != value) {
		stream->update();
		filters[channel].vca = value;
		recalc_filter(filters[channel]);
	}
}

void esq1_filters::set_vpan(int channel, uint8_t value)
{
	if(filters[channel].vpan != value) {
		stream->update();
		filters[channel].vpan = value;
		recalc_filter(filters[channel]);
	}
}

void esq1_filters::set_vq(int channel, uint8_t value)
{
	if(filters[channel].vq != value) {
		stream->update();
		filters[channel].vq = value;
		recalc_filter(filters[channel]);
	}
}

void esq1_filters::set_vfc(int channel, uint8_t value)
{
	if(filters[channel].vfc != value) {
		stream->update();
		filters[channel].vfc = value;
		recalc_filter(filters[channel]);
	}
}

void esq1_filters::recalc_filter(filter &f)
{
	// Filtering stage
	//   First let's establish the control values
	//   Some tuning may be required

	double vfc = -150.4 + (83.6+150.4)*f.vfc/255;
	double r = 4.0*f.vq/255;


	double g = 6060*exp(vfc/28.5);
	double zc = g/tan(g/2/44100);

/*  if(f.vfc) {
        double ff = g/(2*M_PI);
        double fzc = 2*M_PI*ff/tan(M_PI*ff/44100);
        fprintf(stderr, "%02x f=%f zc=%f zc1=%f\n", f.vfc, g/(2*M_PI), zc, fzc);
    }*/

	double gzc = zc/g;
	double gzc2 = gzc*gzc;
	double gzc3 = gzc2*gzc;
	double gzc4 = gzc3*gzc;
	double r1 = 1+r;

	f.a[0] = r1;
	f.a[1] = 4*r1;
	f.a[2] = 6*r1;
	f.a[3] = 4*r1;
	f.a[4] = r1;

	f.b[0] =    r1 + 4*gzc + 6*gzc2 + 4*gzc3 + gzc4;
	f.b[1] = 4*(r1 + 2*gzc          - 2*gzc3 - gzc4);
	f.b[2] = 6*(r1         - 2*gzc2          + gzc4);
	f.b[3] = 4*(r1 - 2*gzc          + 2*gzc3 - gzc4);
	f.b[4] =    r1 - 4*gzc + 6*gzc2 - 4*gzc3 + gzc4;

/*  if(f.vfc != 0)
        for(int i=0; i<5; i++)
            printf("a%d=%f\nb%d=%f\n",
                   i, f.a[i], i, f.b[i]);*/

	// Amplification stage
	double vca = f.vca*(5.0/255.0);
	f.amp = vca < 0.2 ? pow(10, -5+20*vca) : vca*0.1875 + 0.0625;

	// Panning stage
	//   Very approximative at best
	//   Left/right unverified
	double vpan = f.vpan*(5.0/255.0);
	double vref = vpan > 2.5 ? 2.5 - vpan : vpan;
	double pan_amp = vref < 1 ? pow(10, -5+3.5*vref) : vref*0.312 - 0.280;
	if(vref < 2.5) {
		f.lamp = pan_amp;
		f.ramp = 1-pan_amp;
	} else {
		f.lamp = 1-pan_amp;
		f.ramp = pan_amp;
	}
}

void esq1_filters::device_start()
{
	stream = stream_alloc(8, 2, 44100);
	for(auto & elem : filters)
		elem = filter();
	for(auto & elem : filters)
		recalc_filter(elem);
}

void esq1_filters::sound_stream_update(sound_stream &stream)
{
/*  if(0) {
        for(int i=0; i<8; i++)
            fprintf(stderr, " [%02x %02x %02x %02x]",
                    filters[i].vca,
                    filters[i].vpan,
                    filters[i].vq,
                    filters[i].vfc);
        fprintf(stderr, "\n");
    }*/

	for(int i=0; i<stream.samples(); i++) {
		double l=0, r=0;
		for(int j=0; j<8; j++) {
			filter &f = filters[j];
			double x = stream.get(j, i);
			double y = (x*f.a[0]
						+ f.x[0]*f.a[1] + f.x[1]*f.a[2] + f.x[2]*f.a[3] + f.x[3]*f.a[4]
						- f.y[0]*f.b[1] - f.y[1]*f.b[2] - f.y[2]*f.b[3] - f.y[3]*f.b[4]) / f.b[0];
			memmove(f.x+1, f.x, 3*sizeof(double));
			memmove(f.y+1, f.y, 3*sizeof(double));
			f.x[0] = x;
			f.y[0] = y;
			y = y * f.amp;
			l += y * f.lamp;
			r += y * f.ramp;
		}
		static double maxl = 0;
		if(l > maxl) {
			maxl = l;
//          fprintf(stderr, "%f\n", maxl);
		}

//      l *= 6553;
//      r *= 6553;
		l *= 2;
		r *= 2;
		stream.put_clamp(0, i, l, 1.0);
		stream.put_clamp(1, i, r, 1.0);
	}
}


namespace {

class esq1_state : public driver_device
{
public:
	esq1_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_duart(*this, "duart"),
		m_filters(*this, "filters"),
		m_fdc(*this, WD1772_TAG),
		m_panel(*this, "panel"),
		m_volume_slider(*this, "volume_slider"),
		m_data_entry_slider(*this, "data_entry_slider"),
		m_pitch_wheel(*this, "pitch_wheel"),
		m_mod_wheel(*this, "mod_wheel"),
		m_mdout(*this, "mdout"),
		m_es5503(*this, "es5503"),
		m_es5503_rom(*this, "es5503")
	{ }

	void sq80(machine_config &config);

	void esq1(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(key_stroke);
	DECLARE_INPUT_CHANGED_MEMBER(internal_kbd_key_stroke);

private:
	required_device<cpu_device> m_maincpu;
	required_device<scn2681_device> m_duart;
	required_device<esq1_filters> m_filters;
	optional_device<wd1772_device> m_fdc;
	optional_device<esqpanel2x40_device> m_panel;
	required_ioport m_volume_slider;
	required_ioport m_data_entry_slider;
	required_ioport m_pitch_wheel;
	required_ioport m_mod_wheel;
	optional_device<midi_port_device> m_mdout;
	required_device<es5503_device> m_es5503;
	required_region_ptr<uint8_t> m_es5503_rom;

	uint8_t wd1772_r(offs_t offset);
	void wd1772_w(offs_t offset, uint8_t data);
	uint8_t seqdosram_r(offs_t offset);
	void seqdosram_w(offs_t offset, uint8_t data);
	void mapper_w(uint8_t data);
	void analog_w(offs_t offset, uint8_t data);

	void duart_output(uint8_t data);

	uint8_t esq1_adc_read();

	uint8_t es5503_sample_r(offs_t offset);

	int m_mapper_state = 0;
	int m_seq_bank = 0;
	uint8_t m_seqram[0x10000]{};
	uint8_t m_dosram[0x2000]{};
	virtual void machine_reset() override ATTR_COLD;

	void send_through_panel(uint8_t data);
	void esq1_map(address_map &map) ATTR_COLD;
	void sq80_map(address_map &map) ATTR_COLD;
	void sq80_es5503_map(address_map &map) ATTR_COLD;

	bool kpc_calibrated = false;  // sq80 requires keyboard calibration acknowledgement
	int m_adc_poll_target = 0;
};

uint8_t esq1_state::es5503_sample_r(offs_t offset)
{
	return m_es5503_rom[offset + (((m_es5503->get_channel_strobe() & 8)>>3) * 0x20000)];
}

void esq1_state::sq80_es5503_map(address_map &map)
{
	map(0x000000, 0x1ffff).r(FUNC(esq1_state::es5503_sample_r));
}

uint8_t esq1_state::esq1_adc_read()
{
	uint8_t value;
	switch(m_adc_poll_target) {
	case 0: // VALV
		value = m_data_entry_slider->read();
		break;

	case 1: // PEDV - Expression Pedal / CV input
		value = 0;
		break;

	case 2: // PITV
		value = m_pitch_wheel->read();
		break;

	case 3: // MODV
		value = m_mod_wheel->read();
		break;

	case 4: // FILV
	case 5: // BATV
	default:
		value = 0;
		break;
	}
	return value * 255.0/100;
}

void esq1_state::machine_reset()
{
	// set default OSROM banking
	membank("osbank")->set_base(memregion("osrom")->base());

	m_mapper_state = 1;
	m_seq_bank = 0;
	kpc_calibrated = false;
}

uint8_t esq1_state::wd1772_r(offs_t offset)
{
	return m_fdc->read(offset&3);
}

void esq1_state::wd1772_w(offs_t offset, uint8_t data)
{
	m_fdc->write(offset&3, data);
}

void esq1_state::mapper_w(uint8_t data)
{
	m_mapper_state = (data & 1);

	LOG("mapper_state = %d\n", data ^ 1);
}

void esq1_state::analog_w(offs_t offset, uint8_t data)
{
	if(!(offset & 8))
		m_filters->set_vfc(offset & 7, data);
	if(!(offset & 16))
		m_filters->set_vq(offset & 7, data);
	if(!(offset & 32))
		m_filters->set_vpan(offset & 7, data);
	if(!(offset & 64))
		m_filters->set_vca(offset & 7, data);
}

uint8_t esq1_state::seqdosram_r(offs_t offset)
{
	if (m_mapper_state) {
		return m_dosram[offset];
	} else {
		return m_seqram[offset + m_seq_bank];
	}
}

void esq1_state::seqdosram_w(offs_t offset, uint8_t data)
{
	if (m_mapper_state) {
		m_dosram[offset] = data;
	} else {
		m_seqram[offset + m_seq_bank] = data;
	}
}

void esq1_state::esq1_map(address_map &map)
{
	map(0x0000, 0x1fff).ram();                 // OSRAM
	map(0x4000, 0x5fff).ram();                 // SEQRAM
	map(0x6000, 0x63ff).rw("es5503", FUNC(es5503_device::read), FUNC(es5503_device::write));
	map(0x6400, 0x640f).rw(m_duart, FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0x6800, 0x68ff).w(FUNC(esq1_state::analog_w));
	map(0x7000, 0x7fff).bankr("osbank");
	map(0x8000, 0xffff).rom().region("osrom", 0x8000);  // OS "high" ROM is always mapped here
}

void esq1_state::sq80_map(address_map &map)
{
	map(0x0000, 0x1fff).ram();                 // OSRAM
	map(0x4000, 0x5fff).rw(FUNC(esq1_state::seqdosram_r), FUNC(esq1_state::seqdosram_w));
	map(0x6000, 0x63ff).rw("es5503", FUNC(es5503_device::read), FUNC(es5503_device::write));
	map(0x6400, 0x640f).rw(m_duart, FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0x6800, 0x68ff).w(FUNC(esq1_state::analog_w));
	map(0x6c00, 0x6dff).w(FUNC(esq1_state::mapper_w));
	map(0x6e00, 0x6fff).rw(FUNC(esq1_state::wd1772_r), FUNC(esq1_state::wd1772_w));
	map(0x7000, 0x7fff).bankr("osbank");
	map(0x8000, 0xffff).rom().region("osrom", 0x8000);  // OS "high" ROM is always mapped here
}

// from the schematics:
//
// DUART channel A is MIDI
// channel B is to the keyboard/display
// IP0 = tape in
// IP1 = sequencer expansion cartridge inserted
// IP2 = patch cartridge inserted
// IP3 & 4 are 0.5 MHz, IP 5 & 6 are 1 MHz (note 0.5 MHz / 16 = MIDI baud rate)
//
// OP0 = to display processor
// OP1/2/3 = bank select 0, 1, and 2
// OP4 = metronome low
// OP5 = metronome hi
// OP6/7 = tape out

void esq1_state::duart_output(uint8_t data)
{
	int bank = m_adc_poll_target = ((data >> 1) & 0x7);
	LOG("DP [%02x]: %d mlo %d mhi %d tape %d\n", data, data&1, (data>>4)&1, (data>>5)&1, (data>>6)&3);
	LOG("%s [%02x] bank %d => offset %x\n", machine().describe_context(), data, bank, bank * 0x1000);
	membank("osbank")->set_base(memregion("osrom")->base() + (bank * 0x1000) );

	m_seq_bank = (data & 0x8) ? 0x8000 : 0x0000;
	m_seq_bank += ((data>>1) & 3) * 0x2000;
	LOG("seqram_bank = %x\n", m_seq_bank);
}

void esq1_state::send_through_panel(uint8_t data)
{
	m_panel->xmit_char(data);
}

INPUT_CHANGED_MEMBER(esq1_state::key_stroke)
{
	u8 offset = 0;
	// FIXME: get rid of short name checks
	if(strncmp(machine().basename().c_str(), "sq80", 4) == 0) {
		if (!kpc_calibrated) {
			// ack SQ80 keyboard calibration
			send_through_panel((u8)0xff);
			kpc_calibrated = true;
		}
		offset = 2; // SQ80 keycodes are offset by -2
	}

	if(oldval == 0 && newval == 1) {
		send_through_panel((u8)param - offset);
		send_through_panel((u8)0x00);
	} else if(oldval == 1 && newval == 0) {
		send_through_panel(((u8)param - offset)&0x7f);
		send_through_panel((u8)0x00);
	}
}

INPUT_CHANGED_MEMBER(esq1_state::internal_kbd_key_stroke)
{
	if(oldval == 0 && newval == 1) {
		send_through_panel((u8)param);
		send_through_panel((u8)0x01);
	} else if(oldval == 1 && newval == 0) {
		send_through_panel(((u8)param)&0x7f);
		send_through_panel((u8)0x01);
	}
}

void esq1_state::esq1(machine_config &config)
{
	MC6809E(config, m_maincpu, 8_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &esq1_state::esq1_map);

	input_merger_device &mainirq(INPUT_MERGER_ANY_HIGH(config, "mainirq")); // open collector
	mainirq.output_handler().set_inputline(m_maincpu, M6809_IRQ_LINE);
	mainirq.output_handler().append_inputline(m_maincpu, M6809_FIRQ_LINE); // IRQ and FIRQ are tied together

	SCN2681(config, m_duart, 8_MHz_XTAL / 2);
	m_duart->set_clocks(8_MHz_XTAL / 16, 8_MHz_XTAL / 16, 8_MHz_XTAL / 8, 8_MHz_XTAL / 8);
	m_duart->irq_cb().set("mainirq", FUNC(input_merger_device::in_w<0>));
	m_duart->a_tx_cb().set(m_mdout, FUNC(midi_port_device::write_txd));
	m_duart->b_tx_cb().set(m_panel, FUNC(esqpanel2x40_device::rx_w));
	m_duart->outport_cb().set(FUNC(esq1_state::duart_output));

	ESQPANEL2X40(config, m_panel);
	m_panel->write_tx().set(m_duart, FUNC(scn2681_device::rx_b_w));

	auto &mdin(MIDI_PORT(config, "mdin"));
	midiin_slot(mdin);
	mdin.rxd_handler().set(m_duart, FUNC(scn2681_device::rx_a_w)); // route MIDI Tx send directly to 68681 channel A Rx

	midiout_slot(MIDI_PORT(config, "mdout"));

	SPEAKER(config, "speaker", 2).front();

	ESQ1_FILTERS(config, m_filters);
	m_filters->add_route(0, "speaker", 1.0, 0);
	m_filters->add_route(1, "speaker", 1.0, 1);

	ES5503(config, m_es5503, 8_MHz_XTAL);
	m_es5503->set_channels(8);
	m_es5503->irq_func().set("mainirq", FUNC(input_merger_device::in_w<1>));
	m_es5503->adc_func().set(FUNC(esq1_state::esq1_adc_read));
	m_es5503->add_route(0, "filters", 1.0, 0);
	m_es5503->add_route(1, "filters", 1.0, 1);
	m_es5503->add_route(2, "filters", 1.0, 2);
	m_es5503->add_route(3, "filters", 1.0, 3);
	m_es5503->add_route(4, "filters", 1.0, 4);
	m_es5503->add_route(5, "filters", 1.0, 5);
	m_es5503->add_route(6, "filters", 1.0, 6);
	m_es5503->add_route(7, "filters", 1.0, 7);

	config.set_default_layout(layout_esq1);
}

void esq1_state::sq80(machine_config &config)
{
	esq1(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &esq1_state::sq80_map);

	m_es5503->set_addrmap(0, &esq1_state::sq80_es5503_map);
	m_es5503->irq_func().set_nop(); // not connected here

	WD1772(config, m_fdc, 8_MHz_XTAL);
	m_fdc->drq_wr_callback().set("mainirq", FUNC(input_merger_device::in_w<1>));
	m_fdc->intrq_wr_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
}

static INPUT_PORTS_START( esq1 )
	PORT_START("volume_slider")
	PORT_ADJUSTER(255, "Volume")

	PORT_START("data_entry_slider")
	PORT_ADJUSTER(255, "Data Entry")

	PORT_START("pitch_wheel")
	PORT_ADJUSTER(255, "Pitch Wheel")

	PORT_START("mod_wheel")
	PORT_ADJUSTER(255, "Mod Wheel")

	PORT_START("COL0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x84) PORT_NAME("SEQ")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x85) PORT_NAME("CART A")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x86) PORT_NAME("CART B")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x87) PORT_NAME("INT")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x88) PORT_NAME("1 / SEQ 1")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x89) PORT_NAME("2 / SEQ 2")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x8a) PORT_NAME("3 / SEQ 3")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x8b) PORT_NAME("4 / SONG")

	PORT_START("COL1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('Z') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x8c) PORT_NAME("COMPARE")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x8d) PORT_NAME("DATA UP")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x8e) PORT_NAME("DATA DOWN")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR('\r') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x8f) PORT_NAME("WRITE")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x90) PORT_NAME("UPPER 1")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x91) PORT_NAME("UPPER 2")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x92) PORT_NAME("UPPER 3")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x93) PORT_NAME("UPPER 4")

	PORT_START("COL2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x94) PORT_NAME("LOWER 1")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x95) PORT_NAME("LOWER 2")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x96) PORT_NAME("LOWER 3")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x97) PORT_NAME("LOWER 4")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x98) PORT_NAME("LOWER 5")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x99) PORT_NAME("UPPER 5")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x9a) PORT_NAME("LFO 1")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x9b) PORT_NAME("ENV 2")

	PORT_START("COL3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x9c) PORT_NAME("Master")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x9d) PORT_NAME("Create/Erase")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x9e) PORT_NAME("Tracks: Select")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0x9f) PORT_NAME("Record")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xa0) PORT_NAME("Storage")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xa1) PORT_NAME("Edit")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xa2) PORT_NAME("Mix")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xa3) PORT_NAME("Stop")

	PORT_START("COL4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xa4) PORT_NAME("MIDI")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xa5) PORT_NAME("Control")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xa6) PORT_NAME("Locate")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xa7) PORT_NAME("Play")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xa8) PORT_NAME("OSC 1")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xa9) PORT_NAME("OSC 2")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xaa) PORT_NAME("OSC 3")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xab) PORT_NAME("ENV 1")

	PORT_START("COL5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xac) PORT_NAME("DCA 1")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xad) PORT_NAME("DCA 2")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xae) PORT_NAME("DCA 3")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xaf) PORT_NAME("LFO 2")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xb0) PORT_NAME("LFO 3")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xb1) PORT_NAME("Filter")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xb2) PORT_NAME("ENV 4")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xb3) PORT_NAME("ENV 3")

	PORT_START("COL6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xb4) PORT_NAME("DCA 4")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xb5) PORT_NAME("Modes")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::key_stroke), 0xb6) PORT_NAME("Split")
	PORT_BIT(0xf8, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("INTERNAL_KBD1")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0x98) PORT_GM_C1
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0x99) PORT_GM_CS1
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0x9a) PORT_GM_D1
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0x9b) PORT_GM_DS1
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0x9c) PORT_GM_E1
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0x9d) PORT_GM_F1
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0x9e) PORT_GM_FS1
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0x9f) PORT_GM_G1
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xa0) PORT_GM_GS1
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xa1) PORT_GM_A1
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xa2) PORT_GM_AS1
	PORT_BIT(0x800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xa3) PORT_GM_B1

	PORT_START("INTERNAL_KBD2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xa4) PORT_GM_C2
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xa5) PORT_GM_CS2
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xa6) PORT_GM_D2
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xa7) PORT_GM_DS2
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xa8) PORT_GM_E2
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xa9) PORT_GM_F2
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xaa) PORT_GM_FS2
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xab) PORT_GM_G2
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xac) PORT_GM_GS2
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xad) PORT_GM_A2
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xae) PORT_GM_AS2
	PORT_BIT(0x800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xaf) PORT_GM_B2

	PORT_START("INTERNAL_KBD3")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xb0) PORT_GM_C3
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xb1) PORT_GM_CS3
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xb2) PORT_GM_D3
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xb3) PORT_GM_DS3
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xb4) PORT_GM_E3
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xb5) PORT_GM_F3
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xb6) PORT_GM_FS3
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xb7) PORT_GM_G3
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xb8) PORT_GM_GS3
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xb9) PORT_GM_A3
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xba) PORT_GM_AS3
	PORT_BIT(0x800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xbb) PORT_GM_B3

	PORT_START("INTERNAL_KBD4")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xbc) PORT_GM_C4
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xbd) PORT_GM_CS4
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xbe) PORT_GM_D4
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xbf) PORT_GM_DS4
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xc0) PORT_GM_E4
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xc1) PORT_GM_F4
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xc2) PORT_GM_FS4
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xc3) PORT_GM_G4
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xc4) PORT_GM_GS4
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xc5) PORT_GM_A4
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xc6) PORT_GM_AS4
	PORT_BIT(0x800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xc7) PORT_GM_B4

	PORT_START("INTERNAL_KBD5")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xc8) PORT_GM_C5
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xc9) PORT_GM_CS5
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xca) PORT_GM_D5
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xcb) PORT_GM_DS5
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xcc) PORT_GM_E5
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xcd) PORT_GM_F5
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xce) PORT_GM_FS5
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xcf) PORT_GM_G5
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xd0) PORT_GM_GS5
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xd1) PORT_GM_A5
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xd2) PORT_GM_AS5
	PORT_BIT(0x800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xd3) PORT_GM_B5

	PORT_START("INTERNAL_KBD6")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq1_state::internal_kbd_key_stroke), 0xd4) PORT_GM_C6
	PORT_BIT(0xffe, IP_ACTIVE_HIGH, IPT_UNUSED)

INPUT_PORTS_END

ROM_START( esq1 )
	ROM_DEFAULT_BIOS("v3.5")

	ROM_REGION(0x10000, "osrom", 0)
	ROM_SYSTEM_BIOS(0, "v3.5", "Version 3.5")
	ROMX_LOAD("3p5lo.bin",    0x0000, 0x8000, CRC(ed001ad8) SHA1(14d1150bccdbc15d90567cf1812aacdb3b6ee882), ROM_BIOS(0))
	ROMX_LOAD( "3p5hi.bin",    0x8000, 0x8000, CRC(332c572f) SHA1(ddb4f62807eb2ab29e5ac6b5d209d2ecc74cf806), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "v2.2", "Version 2.2")
	ROMX_LOAD("2p2lo.bin",    0x0000, 0x8000, CRC(ea11eca4) SHA1(6a1f026f1371514664ac5d7ea87729c3038ac735), ROM_BIOS(1))
	ROMX_LOAD( "2p2hi.bin",    0x8000, 0x8000, CRC(78ed71a5) SHA1(9a6c715fbba0f2d1afce2171f791affb4887a0bc), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "v2.0", "Version 2.0")
	ROMX_LOAD("2p0lo.bin",    0x0000, 0x8000, CRC(3b701b08) SHA1(62f92e50ca6a68cfa87613d637e8c73938e62fd6), ROM_BIOS(2))
	ROMX_LOAD( "2p0hi.bin",    0x8000, 0x8000, CRC(10b0f436) SHA1(f6ae76d4d42056c00577e131ee85527386f04649), ROM_BIOS(2))

	ROM_SYSTEM_BIOS(3, "v1.7", "Version 1.7")
	ROMX_LOAD("1p7lo.bin",    0x0000, 0x8000, CRC(cc67d98a) SHA1(fda181aec18621499171d7090454c6d60625ec85), ROM_BIOS(3))
	ROMX_LOAD( "1p7hi.bin",    0x8000, 0x8000, CRC(59b2f8d1) SHA1(eb82936f01f379c23df6523cddbac4c7c21b2d22), ROM_BIOS(3))

	ROM_REGION(0x20000, "es5503", 0)
	ROM_LOAD( "esq1wavlo.bin", 0x0000, 0x8000, CRC(4d04ac87) SHA1(867b51229b0a82c886bf3b216aa8893748236d8b) )
	ROM_LOAD( "esq1wavhi.bin", 0x8000, 0x8000, CRC(94c554a3) SHA1(ed0318e5253637585559e8cf24c06d6115bd18f6) )
ROM_END

ROM_START( sq80 )
	ROM_REGION(0x10000, "osrom", 0)
	ROM_LOAD( "sq80rom.low",  0x0000, 0x008000, CRC(97ecd9a0) SHA1(cadff16ebbc15b52cf1d3335d22dc930d430a058) )
	ROM_LOAD( "sq80rom.hig",  0x8000, 0x008000, CRC(f83962b1) SHA1(e3e5cf41f15a37f8bf29b88fb1c85c0fca9ea912) )

	ROM_REGION(0x40000, "es5503", 0)
	ROM_LOAD( "2202.bin",     0x00000, 0x010000, CRC(dffd538c) SHA1(e90f6ff3a7804b54c8a3b1b574ec9c223a6c2bf9) )
	ROM_LOAD( "2203.bin",     0x20000, 0x010000, CRC(9be8cceb) SHA1(1ee4d7e6d2171b44e88e464071bdc4b800b69c4a) )
	ROM_LOAD( "2204.bin",     0x10000, 0x010000, CRC(4937c6f7) SHA1(4505efb9b28fe6d4bcc1f79e81a70bb215c399cb) )
	ROM_LOAD( "2205.bin",     0x30000, 0x010000, CRC(0f917d40) SHA1(1cfae9c80088f4c90b3c9e0b284c3b91f7ff61b9) )

	ROM_REGION(0x8000, "kpc", 0)    // 68HC11 keyboard/front panel processor
	ROM_LOAD( "sq80_kpc_150.bin", 0x000000, 0x008000, CRC(8170b728) SHA1(3ad68bb03948e51b20d2e54309baa5c02a468f7c) )
ROM_END

ROM_START( esqm )
	ROM_REGION(0x10000, "osrom", 0)
	ROM_LOAD( "1355500157_d640_esq-m_oshi.u14", 0x8000, 0x008000, CRC(ea6a7bae) SHA1(2830f8c52dc443b4ca469dc190b33e2ff15b78e1) )

	ROM_REGION(0x20000, "es5503", 0)
	ROM_LOAD( "esq1wavlo.bin", 0x0000, 0x8000, CRC(4d04ac87) SHA1(867b51229b0a82c886bf3b216aa8893748236d8b) )
	ROM_LOAD( "esq1wavhi.bin", 0x8000, 0x8000, CRC(94c554a3) SHA1(ed0318e5253637585559e8cf24c06d6115bd18f6) )
ROM_END

} // anonymous namespace


CONS( 1986, esq1, 0   , 0, esq1, esq1, esq1_state, empty_init, "Ensoniq", "ESQ-1 Digital Wave Synthesizer", MACHINE_NOT_WORKING )
CONS( 1986, esqm, esq1, 0, esq1, esq1, esq1_state, empty_init, "Ensoniq", "ESQ-M Digital Wave Synthesizer Module", MACHINE_NOT_WORKING )
CONS( 1988, sq80, 0,    0, sq80, esq1, esq1_state, empty_init, "Ensoniq", "SQ-80 Cross Wave Synthesizer", MACHINE_NOT_WORKING )



esq5505.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont, Parduz
/***************************************************************************

    esq5505.cpp - Ensoniq ES5505 + ES5510 based synthesizers and samplers

    Ensoniq VFX, VFX-SD, EPS, EPS-16 Plus, SD-1, SD-1 32, SQ-1 and SQ-R (SQ-1 Plus,
    SQ-2, and KS-32 are known to also be this architecture).

    The Taito sound system in taito_en.cpp is directly derived from the 32-voice version
    of the SD-1.

    Driver by R. Belmont and Parduz with thanks to Christian Brunschen, and Phil Bennett

    Memory map:

    0x000000-0x007fff   work RAM low (64k for SQ-1 and later)
    0x200000-0x20001f   OTIS (5505) regs
    0x240000-0x2400ff   DMAC (68450) regs (EPS/EPS-16)
    0x260000-0x2601ff   ESP (5510) regs
    0x280000-0x28001f   DUART (68681) regs
    0x2C0000-0x2C0003   Floppy (WD1772) regs (VFX-SD, SD-1, and EPS/EPS-16)
    0x2e0000-0x2fffff   Expansion cartridge (VFX, VFX-SD, SD-1, SD-1 32 voice)
    0x300000-0x300003   EPS/EPS-16 SCSI (WD33C93, register at 300001, data at 300003)
    0x330000-0x37ffff   VFX-SD / SD-1 sequencer RAM
    0x340000-0x3bffff   EPS/EPS-16 sample RAM
    0xc00000-0xc3ffff   OS ROM
    0xff8000-0xffffff   work RAM hi (64k for SQ-1 and later)

    Note from es5700.pdf PLA equations:
    RAM if (A23/22/21 = 000 and FC is not 6) or (A23/22/21 = 111 and FC is not 7)
    ROM if (A23/22/21 = 110) or (A23/22/21 = 000 & FC is 6)

    Interrupts:
    5505 interrupts are on normal autovector IRQ 1
    DMAC interrupts (EPS only) are on autovector IRQ 2
    68681 uses custom vector 0x40 (address 0x100) level 3

    VFX / VFX-SD / SD-1 / SD-1 32 panel button codes:
    2 = PROGRAM CONTROL
    3 = WRITE
    4 = WAVE
    5 = SELECT VOICE
    6 = MIXER/SHAPER
    7 = EFFECT
    8 = COMPARE
    9 = COPY EFFECTS PARAMETERS
    10 = LFO
    11 = PITCH
    12 = ENV1
    13 = PITCH MOD
    14 = ENV2
    15 = FILTER
    16 = ENV3
    17 = OUTPUT
    18 = ERROR 20 (VFX) / SEQ. CONTROL
    19 = RECORD
    20 = MASTER
    21 = STORAGE
    22 = STOP/CONT
    23 = PLAY
    24 = MIDI
    25 = BUTTON 9
    26 = PSEL
    27 = STAT
    28 = EFFECT
    29 = SEQ?  (toggles INT0 / TRAX display)
    30 = TRACKS 1-6
    31 = TRACKS 7-12
    32 = ERROR 20 (VFX) / CLICK-REC
    33 = ERROR 20 (VFX) / LOCATE
    34 = BUTTON 8
    35 = BUTTON 7
    36 = VOLUME
    37 = PAN
    38 = TIMBRE
    39 = KEY ZONE
    40 = TRANSPOSE
    41 = RELEASE
    42 = SOFT TOP CENTER
    43 = SOFT TOP RIGHT
    44 = SOFT BOTTOM CENTER
    45 = SOFT BOTTOM RIGHT
    46 = BUTTON 3
    47 = BUTTON 4
    48 = BUTTON 5
    49 = BUTTON 6
    50 = SOFT BOTTOM LEFT
    51 = ERROR 202 (VFX) / SEQ.
    52 = CART
    53 = SOUNDS
    54 = PRESETS
    55 = BUTTON 0
    56 = BUTTON 1
    57 = BUTTON 2
    58 = SOFT TOP LEFT
    59 = ERROR 20 (VFX) / EDIT SEQUENCE
    60 = ERROR 20 (VFX) / EDIT SONG
    61 = ERROR 20 (VFX) / EDIT TRACK
    62 = DATA INCREMENT
    63 = DATA DECREMENT

    VFX / VFX-SD / SD-1 analog values: all values are 10 bits, left-justified within 16 bits.
    0 = Pitch Bend
    1 = Patch Select
    2 = Mod Wheel
    3 = Value, aka Data Entry Slider
    4 = Pedal / CV
    5 = Volume Slider
    6 = Battery
    7 = Voltage Reference

    SQ-1:
    4 = second digit of patch # becomes 2
    5 = first digit of patch # becomes 2
    6 = second digit of patch # becomes 4
    7 = first digit of patch # becomes 4
    8 = trk07 4volume=99
    12 = patch -1
    13 = patch +1
    14 = second digit of patch # becomes 5
    15 = first digit of patch # becomes 5
    20 = select sound?
    22 = second digit of patch # becomes 6
    23 = first digit of patch # becomes 6

    Ensoniq SQ-2 MIDI keyboard
    Ensoniq 1992
    PCB Layout by Guru


    PART NO.: 4001018001 REV B

       |--|  |--|  |--|    |--|                       |--|     |--|   |--|   |--|
    |--|J1|--|J2|--|J3|----|J4|-----------------------|J7|-----|J8|---|J9|---|J10--|
    | PHONE  RAUD  LAUD    PEDAL      3V_BATT         FOOT     MIDI   MIDI   MIDI  |
    |                      CV   74LS244 74LS244 74LS245        IN     OUT    THRU  |
    |                                                                              |
    |    TL072                  J5                                HP6N138   |---|  |
    |            7912       4051      CARTRIDGE       LOWER.U27  UPPER.U32  | 6 |LM2926
    |                 J15         |-------J6------|       62256       62256 | 8 |  |
    |                             |---------------|   74HC4053              | 0 |7805
    |            7812           7407                          ENSONIQ       | 0 |  |
    |    TL072            TL072               ENSONIQ         GLU           | 0 |  |
    |                                         SUPERGLU                      |---|  |
    |                                                                              |
    |                                            16MHz  30.47618MHz                |
    |                                                                         POWER|
    |TDA1541A        ROM3.U38   ROM1.U26    74HC74                LM339         J12|
    |                                                                              |
    |                    ROM2.U39   ROM0.U25              NCR6500/11  J13          |
    |                                                                              |
    |        OTISR2             74LS373      ESP        68681                      |
    |                                                                              |
    |7805    74HC74 74F139  6264      6264   ADJ-POT  ESPR6       LM339            |
    |                                                                              |
    |7915    74LS174 74HC161    74LS373    LM317T                 J11 J14          |
    |------------------------------------------------------------------------------|
    Note: All parts shown.

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

#include "emu.h"

#include "bus/midi/midi.h"
#include "cpu/es5510/es5510.h"
#include "cpu/m68000/m68000.h"
#include "formats/esq16_dsk.h"
#include "imagedev/floppy.h"
#include "esqlcd.h"
#include "esqpanel.h"
#include "esqvfd.h"
#include "machine/hd63450.h"    // compatible with MC68450, which is what these really have
#include "machine/mc68681.h"
#include "machine/wd_fdc.h"
#include "sound/es5506.h"
#include "sound/esqpump.h"
#include "emupal.h"
#include "speaker.h"

#include <cstdarg>
#include <cstdio>


namespace {

#define GENERIC (0)
#define EPS     (1)
#define SQ1     (2)

#define KEYBOARD_HACK (1)   // turn on to play the SQ-1, SD-1, and SD-1 32-voice: Z and X are program up/down, A/S/D/F/G/H/J/K/L and Q/W/E/R/T/Y/U play notes

#if KEYBOARD_HACK
static int shift = 32;
#endif

#if 0
static void ATTR_PRINTF(1,2) print_to_stderr(const char *format, ...)
{
	va_list arg;
	va_start(arg, format);
	vfprintf(stderr, format, arg);
	va_end(arg);
}
#endif

class esq5505_state : public driver_device
{
public:
	esq5505_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_duart(*this, "duart")
		, m_esp(*this, "esp")
		, m_pump(*this, "pump")
		, m_fdc(*this, "wd1772")
		, m_floppy_connector(*this, "wd1772:0")
		, m_panel(*this, "panel")
		, m_dmac(*this, "mc68450")
		, m_mdout(*this, "mdout")
		, m_rom(*this, "osrom")
		, m_ram(*this, "osram")
	{ }

	void sq1(machine_config &config);
	void vfx(machine_config &config);
	void vfxsd(machine_config &config);
	void eps(machine_config &config);
	void vfx32(machine_config &config);
	void ks32(machine_config &config);

	void init_eps();
	void init_common();
	void init_sq1();
	void init_denib();
	DECLARE_INPUT_CHANGED_MEMBER(key_stroke);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<m68000_device> m_maincpu;
	required_device<mc68681_device> m_duart;
	required_device<es5510_device> m_esp;
	required_device<esq_5505_5510_pump_device> m_pump;
	optional_device<wd1772_device> m_fdc;
	optional_device<floppy_connector> m_floppy_connector;
	required_device<esqpanel_device> m_panel;
	optional_device<hd63450_device> m_dmac;
	required_device<midi_port_device> m_mdout;
	required_region_ptr<uint16_t> m_rom;
	required_shared_ptr<uint16_t> m_ram;

	uint16_t lower_r(offs_t offset);
	void lower_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

	uint16_t analog_r();
	void analog_w(offs_t offset, uint16_t data);

	void duart_output(uint8_t data);

	void es5505_clock_changed(u32 data);

	int m_system_type = 0;
	uint8_t m_duart_io = 0;

	static void floppy_formats(format_registration &fr);

	void eps_map(address_map &map) ATTR_COLD;
	void sq1_map(address_map &map) ATTR_COLD;
	void vfx_map(address_map &map) ATTR_COLD;
	void vfxsd_map(address_map &map) ATTR_COLD;

	void cpu_space_map(address_map &map) ATTR_COLD;
	void eps_cpu_space_map(address_map &map) ATTR_COLD;

	uint16_t m_analog_values[8];
};

void esq5505_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_ESQIMG_FORMAT);
}

void esq5505_state::cpu_space_map(address_map &map)
{
	map(0xfffff0, 0xffffff).m(m_maincpu, FUNC(m68000_base_device::autovectors_map));
	map(0xfffff7, 0xfffff7).r(m_duart, FUNC(mc68681_device::get_irq_vector));
}

void esq5505_state::eps_cpu_space_map(address_map &map)
{
	cpu_space_map(map);
	map(0xfffff5, 0xfffff5).r(m_dmac, FUNC(hd63450_device::iack));
}

void esq5505_state::machine_start()
{
}

void esq5505_state::machine_reset()
{
	floppy_image_device *floppy = m_floppy_connector ? m_floppy_connector->get_device() : nullptr;

	// Default analog values: all values are 10 bits, left-justified within 16 bits.
	m_analog_values[0] = 0x7fc0; // pitch mod: start in the center
	m_analog_values[1] = 0x0000; // patch select: nothing pressed.
	m_analog_values[2] = 0xffc0; // mod wheel: at the bottom, no modulation
	m_analog_values[3] = 0xccc0; // data entry: somewhere in the middle
	m_analog_values[4] = 0xffc0; // control voltage / pedal: full on.
	m_analog_values[5] = 0xffc0; // Volume control: full on.
	m_analog_values[6] = 0x7fc0; // Battery voltage: something reasonable.
	m_analog_values[7] = 0x5540; // vRef to check battery.

	// on VFX, bit 0 is 1 for 'cartridge present'.
	// on VFX-SD and later, bit 0 is2 1 for floppy present, bit 1 is 1 for cartridge present
	if (strcmp(machine().system().name, "vfx") == 0)
	{
		// todo: handle VFX cart-in when we support cartridges
		m_duart->ip0_w(ASSERT_LINE);
	}
	else
	{
		m_duart->ip1_w(CLEAR_LINE);

		if (floppy)
		{
			m_duart->ip0_w(CLEAR_LINE);
		}
		else
		{
			m_duart->ip0_w(ASSERT_LINE);
		}
	}
}

uint16_t esq5505_state::lower_r(offs_t offset)
{
	offset &= 0x7fff;

	if (!machine().side_effects_disabled() && m_maincpu->get_fc() == 0x6)  // supervisor mode = ROM
	{
		return m_rom[offset];
	}
	else
	{
		return m_ram[offset];
	}
}

void esq5505_state::lower_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	offset &= 0x7fff;

	if (offset < 0x4000)
	{
		if (m_maincpu->get_fc() != 0x6)  // if not supervisor mode, RAM
		{
			COMBINE_DATA(&m_ram[offset]);
		}
		else
		{
			logerror("Write to ROM: %x @ %x (fc=%x)\n", data, offset, m_maincpu->get_fc());
		}
	}
	else
	{
		COMBINE_DATA(&m_ram[offset]);
	}
}

void esq5505_state::vfx_map(address_map &map)
{
	map(0x000000, 0x007fff).rw(FUNC(esq5505_state::lower_r), FUNC(esq5505_state::lower_w));
	map(0x200000, 0x20001f).rw("otis", FUNC(es5505_device::read), FUNC(es5505_device::write));
	map(0x280000, 0x28001f).rw(m_duart, FUNC(mc68681_device::read), FUNC(mc68681_device::write)).umask16(0x00ff);
	map(0x260000, 0x2601ff).rw(m_esp, FUNC(es5510_device::host_r), FUNC(es5510_device::host_w)).umask16(0x00ff);
	map(0xc00000, 0xc1ffff).rom().region("osrom", 0);
	map(0xff0000, 0xffffff).ram().share("osram");
}

void esq5505_state::vfxsd_map(address_map &map)
{
	map(0x000000, 0x00ffff).rw(FUNC(esq5505_state::lower_r), FUNC(esq5505_state::lower_w));
	map(0x200000, 0x20001f).rw("otis", FUNC(es5505_device::read), FUNC(es5505_device::write));
	map(0x280000, 0x28001f).rw(m_duart, FUNC(mc68681_device::read), FUNC(mc68681_device::write)).umask16(0x00ff);
	map(0x260000, 0x2601ff).rw(m_esp, FUNC(es5510_device::host_r), FUNC(es5510_device::host_w)).umask16(0x00ff);
	map(0x2c0000, 0x2c0007).rw(m_fdc, FUNC(wd1772_device::read), FUNC(wd1772_device::write)).umask16(0x00ff);
	map(0x330000, 0x3bffff).ram(); // sequencer memory?
	map(0xc00000, 0xc3ffff).rom().region("osrom", 0);
	map(0xff0000, 0xffffff).ram().share("osram");
}

void esq5505_state::eps_map(address_map &map)
{
	map(0x000000, 0x007fff).rw(FUNC(esq5505_state::lower_r), FUNC(esq5505_state::lower_w));
	map(0x200000, 0x20001f).rw("otis", FUNC(es5505_device::read), FUNC(es5505_device::write));
	map(0x240000, 0x2400ff).rw(m_dmac, FUNC(hd63450_device::read), FUNC(hd63450_device::write));
	map(0x280000, 0x28001f).rw(m_duart, FUNC(mc68681_device::read), FUNC(mc68681_device::write)).umask16(0x00ff);
	map(0x2c0000, 0x2c0007).rw(m_fdc, FUNC(wd1772_device::read), FUNC(wd1772_device::write)).umask16(0x00ff);
	map(0x580000, 0x7fffff).ram();         // sample RAM?
	map(0xc00000, 0xc1ffff).rom().region("osrom", 0);
	map(0xff0000, 0xffffff).ram().share("osram");
}

void esq5505_state::sq1_map(address_map &map)
{
	map(0x000000, 0x03ffff).rw(FUNC(esq5505_state::lower_r), FUNC(esq5505_state::lower_w));
	map(0x200000, 0x20001f).rw("otis", FUNC(es5505_device::read), FUNC(es5505_device::write));
	map(0x260000, 0x2601ff).rw(m_esp, FUNC(es5510_device::host_r), FUNC(es5510_device::host_w)).umask16(0x00ff);
	map(0x280000, 0x28001f).rw(m_duart, FUNC(mc68681_device::read), FUNC(mc68681_device::write)).umask16(0x00ff);
	map(0x330000, 0x3bffff).ram(); // sequencer memory?
	map(0xc00000, 0xc3ffff).rom().region("osrom", 0);
	map(0xff0000, 0xffffff).ram().share("osram");
}

void esq5505_state::es5505_clock_changed(u32 data)
{
	m_pump->set_unscaled_clock(data);
}

void esq5505_state::analog_w(offs_t offset, uint16_t data)
{
	offset &= 0x7;
	m_analog_values[offset] = data;
}

uint16_t esq5505_state::analog_r()
{
	return m_analog_values[m_duart_io & 7];
}

void esq5505_state::duart_output(uint8_t data)
{
	floppy_image_device *floppy = m_floppy_connector ? m_floppy_connector->get_device() : nullptr;

	m_duart_io = data;

	/*
	    EPS:
	    bit 2 = SSEL

	    VFX:
	    bits 0/1/2 = analog sel
	    bit 6 = ESPHALT
	    bit 7 = SACK (?)

	    VFX-SD & SD-1 (32):
	    bits 0/1/2 = analog sel
	    bit 3 = SSEL (disk side)
	    bit 4 = DSEL (drive select?)
	    bit 6 = ESPHALT
	    bit 7 = SACK (?)
	*/

	if (data & 0x40)
	{
		if (!m_pump->get_esp_halted())
		{
			logerror("ESQ5505: Asserting ESPHALT\n");
			m_pump->set_esp_halted(true);
		}
	}
	else
	{
		if (m_pump->get_esp_halted())
		{
			logerror("ESQ5505: Clearing ESPHALT\n");
			m_pump->set_esp_halted(false);
		}
	}

	if (floppy)
	{
		if (m_system_type == EPS)
		{
			floppy->ss_w((data & 2)>>1);
		}
		else
		{
			floppy->ss_w(((data & 8)>>3)^1);
		}
	}

//    printf("DUART output: %02x (PC=%x)\n", data, m_maincpu->pc());
}

#if KEYBOARD_HACK
INPUT_CHANGED_MEMBER(esq5505_state::key_stroke)
{
	int val = (uint8_t)param;
	int cmp = 0x60;

	if (m_system_type == SQ1)
	{
		cmp = 10;
	}

	if (val < cmp)
	{
		if (oldval == 0 && newval == 1)
		{
			if (val == 0 && shift > 0)
			{
				shift -= 32;
				printf("New shift %d\n", shift);
			}
			else if (val == 1 && shift < 32)
			{
				shift += 32;
				printf("New shift %d\n", shift);
			}
			else if (val == 0x02)
			{
//              printf("Analog tests!\n");
				m_panel->xmit_char(54 | 0x80); m_panel->xmit_char(0); // Preset down
				m_panel->xmit_char(8 | 0x80);  m_panel->xmit_char(0); // Compare down
				m_panel->xmit_char(8);         m_panel->xmit_char(0); // Compare up
				m_panel->xmit_char(54);        m_panel->xmit_char(0); // Preset up
			}
		}
	}
	else
	{
		if (val < 20) val += shift;
		if (oldval == 0 && newval == 1)
		{
	//      printf("key pressed %d\n", val);
			m_panel->xmit_char(val);
			m_panel->xmit_char(0x00);
		}
		else if (oldval == 1 && newval == 0)
		{
	//        printf("key off %x\n", (uint8_t)param);
			m_panel->xmit_char(val&0x7f);
			m_panel->xmit_char(0x00);
		}
	}
}
#endif

void esq5505_state::vfx(machine_config &config)
{
	M68000(config, m_maincpu, 10_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &esq5505_state::vfx_map);
	m_maincpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &esq5505_state::cpu_space_map);

	ES5510(config, m_esp, 10_MHz_XTAL);
	m_esp->set_disable();

	ESQPANEL2X40_VFX(config, m_panel);
	m_panel->write_tx().set(m_duart, FUNC(mc68681_device::rx_b_w));
	m_panel->write_analog().set(FUNC(esq5505_state::analog_w));

	MC68681(config, m_duart, 4000000);
	m_duart->irq_cb().set_inputline(m_maincpu, M68K_IRQ_3);
	m_duart->a_tx_cb().set(m_mdout, FUNC(midi_port_device::write_txd));
	m_duart->b_tx_cb().set(m_panel, FUNC(esqpanel_device::rx_w));
	m_duart->outport_cb().set(FUNC(esq5505_state::duart_output));
	m_duart->set_clocks(500000, 500000, 1000000, 1000000);

	auto &mdin(MIDI_PORT(config, "mdin"));
	midiin_slot(mdin);
	mdin.rxd_handler().set(m_duart, FUNC(scn2681_device::rx_a_w)); // route MIDI Tx send directly to 68681 channel A Rx

	midiout_slot(MIDI_PORT(config, "mdout"));

	SPEAKER(config, "speaker", 2).front();

	ESQ_5505_5510_PUMP(config, m_pump, 10_MHz_XTAL / (16 * 21));
	m_pump->set_esp(m_esp);
	m_pump->add_route(0, "speaker", 1.0, 0);
	m_pump->add_route(1, "speaker", 1.0, 1);

	auto &es5505(ES5505(config, "otis", 10_MHz_XTAL));
	es5505.sample_rate_changed().set(FUNC(esq5505_state::es5505_clock_changed));
	es5505.set_region0("waverom");  /* Bank 0 */
	es5505.set_region1("waverom2"); /* Bank 1 */
	es5505.set_channels(4);          /* channels */
	es5505.irq_cb().set_inputline(m_maincpu, M68K_IRQ_1);
	es5505.read_port_cb().set(FUNC(esq5505_state::analog_r)); /* ADC */
	es5505.add_route(0, "pump", 1.0, 0);
	es5505.add_route(1, "pump", 1.0, 1);
	es5505.add_route(2, "pump", 1.0, 2);
	es5505.add_route(3, "pump", 1.0, 3);
	es5505.add_route(4, "pump", 1.0, 4);
	es5505.add_route(5, "pump", 1.0, 5);
	es5505.add_route(6, "pump", 1.0, 6);
	es5505.add_route(7, "pump", 1.0, 7);
}

void esq5505_state::eps(machine_config &config)
{
	vfx(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &esq5505_state::eps_map);
	m_maincpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &esq5505_state::eps_cpu_space_map);

	m_duart->set_clock(10_MHz_XTAL / 2);

	ESQPANEL1X22(config.replace(), m_panel);
	m_panel->write_tx().set(m_duart, FUNC(mc68681_device::rx_b_w));
	m_panel->write_analog().set(FUNC(esq5505_state::analog_w));

	WD1772(config, m_fdc, 8_MHz_XTAL);
	FLOPPY_CONNECTOR(config, m_floppy_connector);
	m_floppy_connector->option_add("35dd", FLOPPY_35_DD);
	m_floppy_connector->set_default_option("35dd");
	m_floppy_connector->set_formats(esq5505_state::floppy_formats);

	HD63450(config, m_dmac, 10_MHz_XTAL);   // MC68450 compatible
	m_dmac->set_cpu_tag(m_maincpu);
	m_dmac->set_clocks(attotime::from_usec(32), attotime::from_nsec(450), attotime::from_usec(4), attotime::from_hz(15625/2));
	m_dmac->set_burst_clocks(attotime::from_usec(32), attotime::from_nsec(450), attotime::from_nsec(50), attotime::from_nsec(50));
	m_dmac->irq_callback().set_inputline(m_maincpu, M68K_IRQ_2);
	m_dmac->dma_read<0>().set(m_fdc, FUNC(wd1772_device::data_r));  // ch 0 = fdc, ch 1 = 340001 (ADC?)
	m_dmac->dma_write<0>().set(m_fdc, FUNC(wd1772_device::data_w));
}

void esq5505_state::vfxsd(machine_config &config)
{
	vfx(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &esq5505_state::vfxsd_map);

	WD1772(config, m_fdc, 8000000);
	FLOPPY_CONNECTOR(config, m_floppy_connector);
	m_floppy_connector->option_add("35dd", FLOPPY_35_DD);
	m_floppy_connector->set_default_option("35dd");
	m_floppy_connector->set_formats(esq5505_state::floppy_formats);
}

// 32-voice machines with the VFX-SD type config
void esq5505_state::vfx32(machine_config &config)
{
	M68000(config, m_maincpu, 30.47618_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &esq5505_state::vfxsd_map);
	m_maincpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &esq5505_state::cpu_space_map);

	ES5510(config, m_esp, 10_MHz_XTAL);
	m_esp->set_disable();

	ESQPANEL2X40_VFX(config, m_panel);
	m_panel->write_tx().set(m_duart, FUNC(mc68681_device::rx_b_w));
	m_panel->write_analog().set(FUNC(esq5505_state::analog_w));

	MC68681(config, m_duart,  4000000);
	m_duart->irq_cb().set_inputline(m_maincpu, M68K_IRQ_3);
	m_duart->a_tx_cb().set(m_mdout, FUNC(midi_port_device::write_txd));
	m_duart->b_tx_cb().set(m_panel, FUNC(esqpanel_device::rx_w));
	m_duart->outport_cb().set(FUNC(esq5505_state::duart_output));
	m_duart->set_clocks(500000, 500000, 1000000, 1000000);

	auto &mdin(MIDI_PORT(config, "mdin"));
	midiin_slot(mdin);
	mdin.rxd_handler().set(m_duart, FUNC(scn2681_device::rx_a_w)); // route MIDI Tx send directly to 68681 channel A Rx

	midiout_slot(MIDI_PORT(config, "mdout"));

	SPEAKER(config, "speaker", 2).front();

	ESQ_5505_5510_PUMP(config, m_pump, 30.47618_MHz_XTAL / (2 * 16 * 32));
	m_pump->set_esp(m_esp);
	m_pump->add_route(0, "speaker", 1.0, 0);
	m_pump->add_route(1, "speaker", 1.0, 1);

	auto &es5505(ES5505(config, "otis", 30.47618_MHz_XTAL / 2));
	es5505.sample_rate_changed().set(FUNC(esq5505_state::es5505_clock_changed));
	es5505.set_region0("waverom");  /* Bank 0 */
	es5505.set_region1("waverom2"); /* Bank 1 */
	es5505.set_channels(4);          /* channels */
	es5505.irq_cb().set_inputline(m_maincpu, M68K_IRQ_1);
	es5505.read_port_cb().set(FUNC(esq5505_state::analog_r)); /* ADC */
	es5505.add_route(0, "pump", 1.0, 0);
	es5505.add_route(1, "pump", 1.0, 1);
	es5505.add_route(2, "pump", 1.0, 2);
	es5505.add_route(3, "pump", 1.0, 3);
	es5505.add_route(4, "pump", 1.0, 4);
	es5505.add_route(5, "pump", 1.0, 5);
	es5505.add_route(6, "pump", 1.0, 6);
	es5505.add_route(7, "pump", 1.0, 7);

	WD1772(config, m_fdc, 8000000);
	FLOPPY_CONNECTOR(config, m_floppy_connector, "35dd", FLOPPY_35_DD, true, floppy_formats);
}

void esq5505_state::sq1(machine_config &config)
{
	vfx(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &esq5505_state::sq1_map);

	ESQPANEL2X16_SQ1(config.replace(), m_panel);
	m_panel->write_tx().set(m_duart, FUNC(mc68681_device::rx_b_w));
	m_panel->write_analog().set(FUNC(esq5505_state::analog_w));
}

void esq5505_state::ks32(machine_config &config)
{
	vfx32(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &esq5505_state::sq1_map);

	ESQPANEL2X16_SQ1(config.replace(), m_panel);
	m_panel->write_tx().set(m_duart, FUNC(mc68681_device::rx_b_w));
	m_panel->write_analog().set(FUNC(esq5505_state::analog_w));
}

static INPUT_PORTS_START( vfx )
#if KEYBOARD_HACK
	PORT_START("KEY0")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x80)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x81)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x82)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x83)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x84)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x85)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x86)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x87)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x88)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x89)
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x8a)
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x8b)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x8c)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x8d)
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x8e)
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x8f)

	PORT_START("KEY1")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x90)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x91)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x92)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x93)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x94)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x95)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x96)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x97)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x98)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x99)
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x9a)
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x9b)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x9c)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x9d)
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x9e)
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0x9f)

	PORT_START("KEY2")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 0)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 1)

	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 2)
#endif
INPUT_PORTS_END

static INPUT_PORTS_START( sq1 )
#if KEYBOARD_HACK
	PORT_START("KEY0")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)         PORT_CHAR('q')  PORT_CHAR('Q')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 148) PORT_NAME("PITCH")  // 148=PITCH  (lo 1)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)         PORT_CHAR('1')                  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 149) PORT_NAME("CONTROL")  // 149=CONTROL  (hi 1)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)         PORT_CHAR('w')  PORT_CHAR('W')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 132) PORT_NAME("ENV1")  // 132=ENV1        (lo 2)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)         PORT_CHAR('2')                  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 133) PORT_NAME("CLICK")  // 133=CLICK  (hi 2)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)         PORT_CHAR('e')  PORT_CHAR('E')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 182) PORT_NAME("LFO")  // 182=LFO      (lo 3)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)         PORT_CHAR('3')                  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 183) PORT_NAME("SONG")  // 183=SONG        (hi 3)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)         PORT_CHAR('r')  PORT_CHAR('R')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 134) PORT_NAME("FILTER")  // 134=FILTER    (lo 4)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)         PORT_CHAR('4')                  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 135) PORT_NAME("SEQ")  // 135=SEQ      (hi 4)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)         PORT_CHAR('t')  PORT_CHAR('T')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 142) PORT_NAME("ENV2")  // 142=ENV2        (lo 5)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)         PORT_CHAR('5')                  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 143) PORT_NAME("EVENT")  // 143=EVENT  (hi 5)
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)         PORT_CHAR('y')  PORT_CHAR('Y')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 150) PORT_NAME("AMP")  // 150=AMP      (lo 6)
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)         PORT_CHAR('6')                  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 151) PORT_NAME("PARAM")  // 151=PARAM  (hi 6)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)         PORT_CHAR('u')  PORT_CHAR('U')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 166) PORT_NAME("OUTPUT")  // 166=OUTPUT    (lo 7)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)         PORT_CHAR('7')                  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 167) PORT_NAME("MIX")  // 167=MIX      (hi 7)
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)         PORT_CHAR('i')  PORT_CHAR('I')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 158) PORT_NAME("P. EFFECT")  // 158=P.EFFECT   (lo 8)
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)         PORT_CHAR('8')                  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 159) PORT_NAME("S. EFFECT")  // 159=S.EFFECT   (hi 8)
	PORT_START("KEY1")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)         PORT_CHAR('o')  PORT_CHAR('O')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 174) PORT_NAME("MIDI")  // 174=MIDI        (lo 9)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)         PORT_CHAR('9')                  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 175) PORT_NAME("SYSTEM")  // 175=SYSTEM    (hi 9)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)         PORT_CHAR('p')  PORT_CHAR('P')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 164) PORT_NAME("WAVE")  // 164=WAVE        (lo 0)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)         PORT_CHAR('0')                  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 165) PORT_NAME("LOCATE")  // 165=LOCATE    (hi 0)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)         PORT_CHAR('g')  PORT_CHAR('G')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 144) PORT_NAME("TRACK 1")  // 144=Track 1
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)         PORT_CHAR('h')  PORT_CHAR('H')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 160) PORT_NAME("TRACK 2")  // 160=Track 2
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)         PORT_CHAR('j')  PORT_CHAR('J')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 152) PORT_NAME("TRACK 3")  // 152=Track 3
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)         PORT_CHAR('k')  PORT_CHAR('K')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 168) PORT_NAME("TRACK 4")  // 168=Track 4
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)         PORT_CHAR('v')  PORT_CHAR('V')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 145) PORT_NAME("TRACK 5")  // 145=Track 5
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)         PORT_CHAR('b')  PORT_CHAR('B')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 161) PORT_NAME("TRACK 6")  // 161=Track 6
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)         PORT_CHAR('n')  PORT_CHAR('N')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 136) PORT_NAME("TRACK 7")  // 136=Track 7
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)         PORT_CHAR('m')  PORT_CHAR('M')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 176) PORT_NAME("TRACK 8")  // 176=Track 8
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)                                     PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 172) PORT_NAME("ENTER")  // 172=ENTER
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)                                 PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 173) PORT_NAME("COMPARE")  // 173=COMPARE
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)                                      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 140) PORT_NAME("PROG DN")  // 140=ProgDn
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)                                        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 141) PORT_NAME("PROG UP")  // 141=ProgUp
	PORT_START("KEY2")
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)                                     PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 156) PORT_NAME("ROM/INT SELECT +")  // 156=ROM/INT Select  189=track +
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)                                      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 157) PORT_NAME("ROM/INT SELECT -")  // 157=ROM/INT Select  190=track -
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)         PORT_CHAR('z')  PORT_CHAR('Z')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 180) PORT_NAME("SOUND SELECT")  // 180=SOUND Select
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)         PORT_CHAR('a')  PORT_CHAR('A')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 181) PORT_NAME("SOUND EDIT")  // 181=SOUND Edit
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)         PORT_CHAR('s')  PORT_CHAR('S')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 184) PORT_NAME("SEQ EDIT")  // 184=SEQ Edit
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)         PORT_CHAR('x')  PORT_CHAR('X')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esq5505_state::key_stroke), 153) PORT_NAME("SEQ SELECT")  // 153=SEQ Select
	PORT_START("KEY3")
#endif
INPUT_PORTS_END

#define ROM_LOAD16_BYTE_BIOS(bios,name,offset,length,hash) \
		ROMX_LOAD(name, offset, length, hash, ROM_SKIP(1) | ROM_BIOS(bios))

ROM_START( vfx )
	ROM_REGION16_BE(0x40000, "osrom", 0)
	ROM_LOAD16_BYTE( "vfx210b-low.bin",  0x000001, 0x010000, CRC(c51b19cd) SHA1(2a125b92ffa02ae9d7fb88118d525491d785e87e) )
	ROM_LOAD16_BYTE( "vfx210b-high.bin", 0x000000, 0x010000, CRC(59853be8) SHA1(8e07f69d53f80885d15f624e0b912aeaf3212ee4) )

	ROM_REGION(0x200000, "waverom", ROMREGION_ERASE00)
	ROM_LOAD16_BYTE( "u14.bin", 0x000001, 0x080000, CRC(85592299) SHA1(1aa7cf612f91972baeba15991d9686ccde01599c) )
	ROM_LOAD16_BYTE( "u15.bin", 0x100001, 0x080000, CRC(c0055975) SHA1(5a22f1d5e437c6277eb0cfb1ff1b3f8dcdea1cc6) )

	ROM_REGION(0x80000, "nibbles", ROMREGION_ERASE00)
	ROM_LOAD( "u16.bin", 0x000000, 0x080000, CRC(c3ddaf95) SHA1(44a7bd89cd7e82952cc5100479e110c385246559) )
ROM_END

ROM_START( vfxsd )
	ROM_REGION16_BE(0x40000, "osrom", 0)
	ROM_SYSTEM_BIOS( 0, "v200", "V200" )
	ROMX_LOAD( "vfxsd_200_lower.bin", 0x000001, 0x010000, CRC(7bd31aea) SHA1(812bf73c4861a5d963f128def14a4a98171c93ad), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "vfxsd_200_upper.bin", 0x000000, 0x010000, CRC(9a40efa2) SHA1(e38a2a4514519c1573361cb1526139bfcf94e45a), ROM_SKIP(1) | ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v133", "V133" )
	ROMX_LOAD( "vfxsd_133_lower.bin", 0x000001, 0x010000, CRC(65407fcf) SHA1(83952a19f6f9ae7886ac828d8bd5ea7fee8d0fe3), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "vfxsd_133_upper.bin", 0x000000, 0x010000, CRC(150fcd18) SHA1(e42cf08604b52fab248d15d761de7d835a076940), ROM_SKIP(1) | ROM_BIOS(1) )

	ROM_REGION(0x200000, "waverom", ROMREGION_ERASE00)
	ROM_LOAD16_BYTE( "u57.bin", 0x000001, 0x080000, CRC(85592299) SHA1(1aa7cf612f91972baeba15991d9686ccde01599c) )
	ROM_LOAD16_BYTE( "u58.bin", 0x100001, 0x080000, CRC(c0055975) SHA1(5a22f1d5e437c6277eb0cfb1ff1b3f8dcdea1cc6) )

	ROM_REGION(0x200000, "waverom2", ROMREGION_ERASE00)

	ROM_REGION(0x80000, "nibbles", 0)
	ROM_LOAD( "u60.bin", 0x000000, 0x080000, CRC(c3ddaf95) SHA1(44a7bd89cd7e82952cc5100479e110c385246559) )
ROM_END

ROM_START( sd1 )
	ROM_REGION16_BE(0x40000, "osrom", 0)
	ROM_LOAD16_BYTE( "sd1_21_300b_lower.bin", 0x000001, 0x020000, CRC(a1358a0c) SHA1(64ac5358aa46da37ca4195002cf358554e00878a) )
	ROM_LOAD16_BYTE( "sd1_21_300b_upper.bin", 0x000000, 0x010000, CRC(465ba463) SHA1(899b0e83d0788c8d49c7b09ccf0b4a92b528c6e9) )

	ROM_REGION(0x200000, "waverom", ROMREGION_ERASE00)  // BS=0 region (12-bit)
	ROM_LOAD16_BYTE( "u34.bin", 0x000001, 0x080000, CRC(85592299) SHA1(1aa7cf612f91972baeba15991d9686ccde01599c) )
	ROM_LOAD16_BYTE( "u35.bin", 0x100001, 0x080000, CRC(c0055975) SHA1(5a22f1d5e437c6277eb0cfb1ff1b3f8dcdea1cc6) )

	ROM_REGION(0x200000, "waverom2", ROMREGION_ERASE00) // BS=1 region (16-bit)
	ROM_LOAD16_WORD_SWAP( "u38.bin", 0x000000, 0x100000, CRC(a904190e) SHA1(e4fd4e1130906086fb4182dcb8b51269969e2836) )
	ROM_LOAD16_WORD_SWAP( "u37.bin", 0x100000, 0x100000, CRC(d706cef3) SHA1(24ba35248509e9ca45110e2402b8085006ea0cfc) )

	ROM_REGION(0x80000, "nibbles", 0)
	ROM_LOAD( "u36.bin", 0x000000, 0x080000, CRC(c3ddaf95) SHA1(44a7bd89cd7e82952cc5100479e110c385246559) )
ROM_END

// note: all known 4.xx BIOSes are for the 32-voice SD-1 and play out of tune on 21-voice h/w
ROM_START( sd132 )
	ROM_REGION16_BE(0x40000, "osrom", 0)
	ROM_SYSTEM_BIOS(0, "410", "SD-1 v4.10")
	ROM_LOAD16_BYTE_BIOS(0, "sd1_410_lo.bin", 0x000001, 0x020000, CRC(faa613a6) SHA1(60066765cddfa9d3b5d09057d8f83fb120f4e65e) )
	ROM_LOAD16_BYTE_BIOS(0, "sd1_410_hi.bin", 0x000000, 0x010000, CRC(618c0aa8) SHA1(74acf458aa1d04a0a7a0cd5855c49e6855dbd301) )
	ROM_SYSTEM_BIOS(1, "402", "SD-1 v4.02")
	ROM_LOAD16_BYTE_BIOS(1, "sd1_32_402_lo.bin", 0x000001, 0x020000, CRC(5da2572b) SHA1(cb6ddd637ed13bfeb40a99df56000479e63fc8ec) )
	ROM_LOAD16_BYTE_BIOS(1, "sd1_32_402_hi.bin", 0x000000, 0x010000, CRC(fc45c210) SHA1(23b81ebd9176112e6eae0c7c75b39fcb1656c953) )

	ROM_REGION(0x200000, "waverom", ROMREGION_ERASE00)  // BS=0 region (12-bit)
	ROM_LOAD16_BYTE( "u34.bin", 0x000001, 0x080000, CRC(85592299) SHA1(1aa7cf612f91972baeba15991d9686ccde01599c) )
	ROM_LOAD16_BYTE( "u35.bin", 0x100001, 0x080000, CRC(c0055975) SHA1(5a22f1d5e437c6277eb0cfb1ff1b3f8dcdea1cc6) )

	ROM_REGION(0x200000, "waverom2", ROMREGION_ERASE00) // BS=1 region (16-bit)
	ROM_LOAD16_WORD_SWAP( "u38.bin", 0x000000, 0x100000, CRC(a904190e) SHA1(e4fd4e1130906086fb4182dcb8b51269969e2836) )
	ROM_LOAD16_WORD_SWAP( "u37.bin", 0x100000, 0x100000, CRC(d706cef3) SHA1(24ba35248509e9ca45110e2402b8085006ea0cfc) )

	ROM_REGION(0x80000, "nibbles", ROMREGION_ERASE00)
	ROM_LOAD( "u36.bin", 0x000000, 0x080000, CRC(c3ddaf95) SHA1(44a7bd89cd7e82952cc5100479e110c385246559) )
ROM_END


ROM_START( sq1 )
	ROM_REGION16_BE(0x40000, "osrom", 0)
	ROM_LOAD16_BYTE( "sq1lo.bin",    0x000001, 0x010000, CRC(b004cf05) SHA1(567b0dae2e35b06e39da108f9c041fd9bc38fa35) )
	ROM_LOAD16_BYTE( "sq1up.bin",    0x000000, 0x010000, CRC(2e927873) SHA1(06a948cb71fa254b23f4b9236f29035d10778da1) )

	ROM_REGION(0x200000, "waverom", 0)
	ROM_LOAD16_BYTE( "sq1-u25.bin",  0x000001, 0x080000, CRC(26312451) SHA1(9f947a11592fd8420fc581914bf16e7ade75390c) )
	ROM_LOAD16_BYTE( "sq1-u26.bin",  0x100001, 0x080000, CRC(2edaa9dc) SHA1(72fead505c4f44e5736ff7d545d72dfa37d613e2) )

	ROM_REGION(0x80000, "nibbles", ROMREGION_ERASE00)
ROM_END

ROM_START( sqrack )
	ROM_REGION16_BE(0x40000, "osrom", 0)
	ROM_LOAD16_BYTE( "sqr-102-lower.bin", 0x000001, 0x010000, CRC(186c85ad) SHA1(801c5cf82823ce31a88688fbee4c11ea5ffdbc10) )
	ROM_LOAD16_BYTE( "sqr-102-upper.bin", 0x000000, 0x010000, CRC(088c9d31) SHA1(30627f21d893888b6159c481bea08e3eedd21902) )

	ROM_REGION(0x200000, "waverom", 0)
	ROM_LOAD16_BYTE( "sq1-u25.bin",  0x000001, 0x080000, CRC(26312451) SHA1(9f947a11592fd8420fc581914bf16e7ade75390c) )
	ROM_LOAD16_BYTE( "sq1-u26.bin",  0x100001, 0x080000, CRC(2edaa9dc) SHA1(72fead505c4f44e5736ff7d545d72dfa37d613e2) )

	ROM_REGION(0x80000, "nibbles", ROMREGION_ERASE00)
ROM_END

ROM_START( sq2 )
	ROM_REGION16_BE(0x40000, "osrom", 0)
	ROM_LOAD16_BYTE( "sq232_2.03_9193_lower.u27", 0x000001, 0x010000, CRC(e37fbc2c) SHA1(4a74f3540756745073c8768b384905db03da47c0) )
	ROM_LOAD16_BYTE( "sq232_2.03_cbcd_upper.u32", 0x000000, 0x020000, CRC(5a7dc228) SHA1(d25adecc0dbba93a094c49fae105dcc7aad317f1) )

	ROM_REGION(0x200000, "waverom", 0)
	ROM_LOAD16_BYTE( "sq1-u25.bin",  0x000001, 0x080000, CRC(26312451) SHA1(9f947a11592fd8420fc581914bf16e7ade75390c) )
	ROM_LOAD16_BYTE( "sq1-u26.bin",  0x100001, 0x080000, CRC(2edaa9dc) SHA1(72fead505c4f44e5736ff7d545d72dfa37d613e2) )

	ROM_REGION(0x200000, "waverom2", ROMREGION_ERASE00) // BS=1 region (16-bit)
	ROM_LOAD( "rom2.u39",     0x000000, 0x100000, CRC(8d1b5e91) SHA1(12991083a6c574133a1a799813fa4573a33d2297) )
	ROM_LOAD( "rom3.u38",     0x100000, 0x100000, CRC(cb9875ce) SHA1(82021bdc34953e9be97d45746a813d7882250ae0) )

	ROM_REGION(0x80000, "nibbles", ROMREGION_ERASE00)
ROM_END

ROM_START( eps )
	ROM_REGION16_BE(0x20000, "osrom", 0)
	ROM_LOAD16_BYTE( "eps-l.bin",    0x000001, 0x008000, CRC(382beac1) SHA1(110e31edb03fcf7bbde3e17423b21929e5b32db2) )
	ROM_LOAD16_BYTE( "eps-h.bin",    0x000000, 0x008000, CRC(d8747420) SHA1(460597751386eb5f08465699b61381c4acd78065) )

	ROM_REGION(0x200000, "waverom", ROMREGION_ERASE00)  // EPS-16 has no ROM sounds

	ROM_REGION(0x200000, "waverom2", ROMREGION_ERASE00)
ROM_END

ROM_START( eps16p )
	ROM_REGION16_BE(0x20000, "osrom", 0)
	ROM_LOAD16_BYTE( "eps16plus-100f-lower.u27", 0x000001, 0x010000, CRC(78568d3f) SHA1(ac737e093f422e109e8f06d44548629a12d6418c) )
	ROM_LOAD16_BYTE( "eps16plus-100f-upper.u28", 0x000000, 0x010000, CRC(1264465f) SHA1(71604da091bd90a32f0d93698d70b9e114ec1697) )

	ROM_REGION(0x200000, "waverom", ROMREGION_ERASE00)  // EPS-16 Plus has no ROM sounds

	ROM_REGION(0x200000, "waverom2", ROMREGION_ERASE00)
ROM_END

ROM_START( ks32 )
	ROM_REGION16_BE(0x40000, "osrom", 0)
	ROM_SYSTEM_BIOS(0, "301", "KS-32 v3.01")
	ROM_LOAD16_BYTE_BIOS(0, "ks32v301lower.bin", 0x000001, 0x010000, CRC(de37e49c) SHA1(fd5a25e23ff217a5926daae4f57dd966d392d26d) )
	ROM_LOAD16_BYTE_BIOS(0, "ks32v301upper.bin", 0x000000, 0x020000, CRC(d8249b32) SHA1(8712cf2c63a31c00e98fa42e518093cac51f5214) )

	ROM_SYSTEM_BIOS(1, "30", "KS-32 v3.0")
	ROM_LOAD16_BYTE_BIOS(1, "ks32v3pt00lower.bin", 0x000001, 0x010000, CRC(c347708e) SHA1(637e1a5c0a62f4d5726363bdb782448ca9637afc) )
	ROM_LOAD16_BYTE_BIOS(1, "ks32v3pt00upper.bin", 0x000000, 0x020000, CRC(8c56c88f) SHA1(4424f39f74f067f15030b8d4a90d9ace8ea14677) )

	ROM_REGION(0x200000, "waverom", ROMREGION_ERASE00)
	ROM_LOAD16_BYTE( "sq1-u25.bin",  0x000001, 0x080000, CRC(26312451) SHA1(9f947a11592fd8420fc581914bf16e7ade75390c) )
	ROM_LOAD16_BYTE( "sq1-u26.bin",  0x100001, 0x080000, CRC(2edaa9dc) SHA1(72fead505c4f44e5736ff7d545d72dfa37d613e2) )

	ROM_REGION(0x200000, "waverom2", ROMREGION_ERASE00) // BS=1 region (16-bit)
	ROM_LOAD( "rom2.u39",     0x000000, 0x100000, CRC(8d1b5e91) SHA1(12991083a6c574133a1a799813fa4573a33d2297) )
	ROM_LOAD( "rom3.u38",     0x100000, 0x100000, CRC(cb9875ce) SHA1(82021bdc34953e9be97d45746a813d7882250ae0) )

	ROM_REGION(0x80000, "nibbles", ROMREGION_ERASE00)
ROM_END

void esq5505_state::init_common()
{
	m_system_type = GENERIC;
	m_duart_io = 0;

	floppy_image_device *floppy = m_floppy_connector ? m_floppy_connector->get_device() : nullptr;
	if (floppy)
	{
		m_fdc->set_floppy(floppy);
		floppy->ss_w(0);
	}
}

void esq5505_state::init_eps()
{
	init_common();
	m_system_type = EPS;
}

void esq5505_state::init_sq1()
{
	init_common();
	m_system_type = SQ1;
#if KEYBOARD_HACK
	shift = 60;
#endif
}

void esq5505_state::init_denib()
{
	uint8_t *pNibbles = (uint8_t *)memregion("nibbles")->base();
	uint8_t *pBS0L = (uint8_t *)memregion("waverom")->base();
	uint8_t *pBS0H = pBS0L + 0x100000;

	init_common();

	// create the 12 bit samples by patching in the nibbles from the nibble ROM
	// low nibbles go with the lower ROM, high nibbles with the upper ROM
	for (int i = 0; i < 0x80000; i++)
	{
		*pBS0L = (*pNibbles & 0x0f) << 4;
		*pBS0H = (*pNibbles & 0xf0);
		pBS0L += 2;
		pBS0H += 2;
		pNibbles++;
	}
}

} // Anonymous namespace


CONS( 1988, eps,    0,   0, eps,   vfx, esq5505_state, init_eps,    "Ensoniq", "EPS",             MACHINE_NOT_WORKING )  // custom VFD: one alphanumeric 22-char row, one graphics-capable row (alpha row can also do bar graphs)
CONS( 1989, vfx,    0,   0, vfx,   vfx, esq5505_state, init_denib,  "Ensoniq", "VFX",             MACHINE_NOT_WORKING )  // 2x40 VFD
CONS( 1989, vfxsd,  0,   0, vfxsd, vfx, esq5505_state, init_denib,  "Ensoniq", "VFX-SD",          MACHINE_NOT_WORKING )  // 2x40 VFD
CONS( 1990, eps16p, eps, 0, eps,   vfx, esq5505_state, init_eps,    "Ensoniq", "EPS-16 Plus",     MACHINE_NOT_WORKING )  // custom VFD: one alphanumeric 22-char row, one graphics-capable row (alpha row can also do bar graphs)
CONS( 1990, sd1,    0,   0, vfxsd, vfx, esq5505_state, init_denib,  "Ensoniq", "SD-1 (21 voice)", MACHINE_NOT_WORKING )  // 2x40 VFD
CONS( 1990, sq1,    0,   0, sq1,   sq1, esq5505_state, init_sq1,    "Ensoniq", "SQ-1",            MACHINE_NOT_WORKING )  // 2x16 LCD
CONS( 1990, sqrack, sq1, 0, sq1,   sq1, esq5505_state, init_sq1,    "Ensoniq", "SQ-Rack",         MACHINE_NOT_WORKING )  // 2x16 LCD
CONS( 1991, sq2,    0,   0, ks32,  sq1, esq5505_state, init_sq1,    "Ensoniq", "SQ-2",            MACHINE_NOT_WORKING )  // 2x16 LCD
CONS( 1991, sd132,  sd1, 0, vfx32, vfx, esq5505_state, init_denib,  "Ensoniq", "SD-1 (32 voice)", MACHINE_NOT_WORKING )  // 2x40 VFD
CONS( 1992, ks32,   sq2, 0, ks32,  sq1, esq5505_state, init_sq1,    "Ensoniq", "KS-32",           MACHINE_NOT_WORKING)                       // 2x16 LCD



esqasr.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    esqasr.c - Ensoniq ASR-10 and ASR-X

    Skeleton driver by R. Belmont

    ASR-10 hardware:
        CPU: 68302 MCU
        Sound: ES5506
        Effects: ES5510
        FDC: NEC uPD72069
        DUART: 2681

    Memory map:
    0x000000-0x03ffff   OS ROM
    0xfb0000-0xfcffff   OS RAM


    ASR-X hardware:
        CPU: 68340 MCU
        Sound: ES5506
        Effects: ES5511
        FDC: NEC uPD72069

    http://www.gweep.net/~shifty/music/asrxhack/

    Memory map:
    0x00000000-0x000fffff   OS ROM
    0x00800000-0x008000ff   ESP2 5511?
    0x00f00000-0x00f007ff   Unknown
    0x08000000-0x08200000   RAM
    0x0be00000-0x0befffff   RAM (size unknown)

    These may want to be separated when they run more.

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

#include "emu.h"

#include "cpu/es5510/es5510.h"
#include "cpu/m68000/m68000.h"
#include "machine/68340.h"
#include "esqvfd.h"
#include "machine/upd765.h"
#include "sound/es5506.h"
#include "sound/esqpump.h"

#include "speaker.h"


namespace {

class esqasr_state : public driver_device
{
public:
	esqasr_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_esp(*this, "esp")
		, m_pump(*this, "pump")
		, m_sq1vfd(*this, "sq1vfd")
	{ }

	void asrx(machine_config &config);
	void asr(machine_config &config);

	void init_asr();

private:
	required_device<cpu_device> m_maincpu;
	optional_device<es5510_device> m_esp;
	optional_device<esq_5505_5510_pump_device> m_pump;
	required_device<esq2x40_sq1_device> m_sq1vfd;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void esq5506_otto_irq(int state);
	u16 esq5506_read_adc();
	void es5506_clock_changed(u32 data);

	void asr_map(address_map &map) ATTR_COLD;
	void asrx_map(address_map &map) ATTR_COLD;
};

void esqasr_state::machine_start()
{
}

void esqasr_state::machine_reset()
{
}

void esqasr_state::asr_map(address_map &map)
{
	map(0x000000, 0x0fffff).rom().region("maincpu", 0);
	map(0xf00000, 0xffffff).ram();
}

void esqasr_state::asrx_map(address_map &map)
{
	map(0x00000000, 0x000fffff).rom().region("maincpu", 0);
	map(0x08000000, 0x081fffff).ram();
	map(0x0be00000, 0x0befffff).ram();
}

void esqasr_state::esq5506_otto_irq(int state)
{
}

u16 esqasr_state::esq5506_read_adc()
{
	return 0;
}

void esqasr_state::es5506_clock_changed(u32 data)
{
	m_pump->set_unscaled_clock(data);
}

void esqasr_state::asr(machine_config &config)
{
	M68000(config, m_maincpu, XTAL(16'000'000)); // actually MC68302
	m_maincpu->set_addrmap(AS_PROGRAM, &esqasr_state::asr_map);

	ES5510(config, m_esp, XTAL(10'000'000));
	m_esp->set_disable();

	ESQ2X40_SQ1(config, m_sq1vfd, 60);

	SPEAKER(config, "speaker", 2).front();

	ESQ_5505_5510_PUMP(config, m_pump, XTAL(16'000'000) / (16 * 32));
	m_pump->set_esp(m_esp);
	m_pump->add_route(0, "speaker", 1.0, 0);
	m_pump->add_route(1, "speaker", 1.0, 1);

	es5506_device &ensoniq(ES5506(config, "ensoniq", XTAL(16'000'000)));
	ensoniq.sample_rate_changed().set(FUNC(esqasr_state::es5506_clock_changed));
	ensoniq.set_region0("waverom");  /* Bank 0 */
	ensoniq.set_region1("waverom2"); /* Bank 1 */
	ensoniq.set_region2("waverom3"); /* Bank 0 */
	ensoniq.set_region3("waverom4"); /* Bank 1 */
	ensoniq.set_channels(4);         /* channels, Not verified from real hardware */
	ensoniq.irq_cb().set(FUNC(esqasr_state::esq5506_otto_irq)); /* irq */
	ensoniq.read_port_cb().set(FUNC(esqasr_state::esq5506_read_adc));
	ensoniq.add_route(0, "pump", 1.0, 0);
	ensoniq.add_route(1, "pump", 1.0, 1);
	ensoniq.add_route(2, "pump", 1.0, 2);
	ensoniq.add_route(3, "pump", 1.0, 3);
	ensoniq.add_route(4, "pump", 1.0, 4);
	ensoniq.add_route(5, "pump", 1.0, 5);
	ensoniq.add_route(6, "pump", 1.0, 6);
	ensoniq.add_route(7, "pump", 1.0, 7);
}

void esqasr_state::asrx(machine_config &config)
{
	M68340(config, m_maincpu, XTAL(16'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &esqasr_state::asrx_map);

	ES5510(config, m_esp, XTAL(10'000'000)); // Actually ES5511
	m_esp->set_disable();

	ESQ2X40_SQ1(config, m_sq1vfd, 60);

	SPEAKER(config, "speaker", 2).front();

	ESQ_5505_5510_PUMP(config, m_pump, XTAL(16'000'000) / (16 * 32)); // Actually ES5511
	m_pump->set_esp(m_esp);
	m_pump->add_route(0, "speaker", 1.0, 0);
	m_pump->add_route(1, "speaker", 1.0, 1);

	es5506_device &ensoniq(ES5506(config, "ensoniq", XTAL(16'000'000)));
	ensoniq.sample_rate_changed().set(FUNC(esqasr_state::es5506_clock_changed));
	ensoniq.set_region0("waverom");  /* Bank 0 */
	ensoniq.set_region1("waverom2"); /* Bank 1 */
	ensoniq.set_region2("waverom3"); /* Bank 0 */
	ensoniq.set_region3("waverom4"); /* Bank 1 */
	ensoniq.set_channels(4);         /* channels, Not verified from real hardware */
	ensoniq.irq_cb().set(FUNC(esqasr_state::esq5506_otto_irq)); /* irq */
	ensoniq.read_port_cb().set(FUNC(esqasr_state::esq5506_read_adc));
	ensoniq.add_route(0, "pump", 1.0, 0);
	ensoniq.add_route(1, "pump", 1.0, 1);
	ensoniq.add_route(2, "pump", 1.0, 2);
	ensoniq.add_route(3, "pump", 1.0, 3);
	ensoniq.add_route(4, "pump", 1.0, 4);
	ensoniq.add_route(5, "pump", 1.0, 5);
	ensoniq.add_route(6, "pump", 1.0, 6);
	ensoniq.add_route(7, "pump", 1.0, 7);
}

static INPUT_PORTS_START( asr )
INPUT_PORTS_END

ROM_START( asr10 )
	ROM_REGION(0x100000, "maincpu", 0)
	ROM_LOAD16_BYTE( "asr-648c-lo-1.5b.bin", 0x000001, 0x020000, CRC(8e437843) SHA1(418f042acbc5323f5b59cbbd71fdc8b2d851f7d0) )
	ROM_LOAD16_BYTE( "asr-65e0-hi-1.5b.bin", 0x000000, 0x020000, CRC(b37cd3b6) SHA1(c4371848428a628b5e5a50e99be602d7abfc7904) )

	ROM_REGION(0x200000, "waverom", ROMREGION_ERASE00)
	ROM_REGION(0x200000, "waverom2", ROMREGION_ERASE00)
	ROM_REGION(0x200000, "waverom3", ROMREGION_ERASE00)
	ROM_REGION(0x200000, "waverom4", ROMREGION_ERASE00)
ROM_END

ROM_START( asrx )
	ROM_REGION(0x100000, "maincpu", 0)
	ROM_LOAD16_BYTE( "asr267lo.bin", 0x000001, 0x080000, CRC(7408d441) SHA1(0113f84b6d224bf1423ad62c173f32a0c95ca715) )
	ROM_LOAD16_BYTE( "asr267hi.bin", 0x000000, 0x080000, CRC(7df14ea7) SHA1(895b99013c0f924edb52612eb93c3e6babb9f053) )

	ROM_REGION(0x200000, "waverom", ROMREGION_ERASE00)
	ROM_REGION(0x200000, "waverom2", ROMREGION_ERASE00)
	ROM_REGION(0x200000, "waverom3", ROMREGION_ERASE00)
	ROM_REGION(0x200000, "waverom4", ROMREGION_ERASE00)
ROM_END

void esqasr_state::init_asr()
{
}

} // anonymous namespace


CONS( 1992, asr10, 0, 0, asr, asr, esqasr_state, init_asr, "Ensoniq", "ASR-10", MACHINE_NOT_WORKING )
CONS( 1997, asrx,  0, 0, asrx,asr, esqasr_state, init_asr, "Ensoniq", "ASR-X",  MACHINE_NOT_WORKING )



esqkt.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    esqkt.cpp - Ensoniq KT-76, KT-88, and E-Prime
                Ensoniq TS-10 and TS-12

    Driver by R. Belmont

    Hardware:
        CPU: 68EC020-16 CPU
        Serial/timers: SCN2681
        Sound: 2xES5506
        Effects: ES5510

    Memory map:

    0x000000-0x07FFFF   OS ROM (additional RAM on TS)
    0x200000-0x20003F   Master ES5506
    0x240000-0x24003F   Slave ES5506
    0x280000-0x2801FF   ES5510
    0x300000-0x30000F   68681 DUART
    0x400000-0xAFFFFF   User sample RAM (TS only)
    0xFF0000-0xFFFFFF   OS RAM

    Ensoniq KT-76
    Ensoniq 1994

    This is a wavetable-based synth keyboard made by Ensoniq in 1994

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

    KT-76
    |---------------------------------------------|
    |J12 J3      J11                         J10  |
    |                                  LM393 LM358|
    |                                     74HC4051|
    |ADM691                                       |
    |              62256                          |
    |                                             |
    |3V_BATTERY  KT76_0590_LO.U5          ROM0    |
    |                                             |
    |   68EC020    62256       OTTOR2     ROM1    |
    |                                             |
    |            KT76_690B_HI.U6          ROM2    |
    | PAL1                                        |
    |       HP_6N138                              |
    | PAL2                     OTTOR2           J6|
    |      7407                                   |
    |                                             |
    |          D41464 D41464                      |
    |                                             |
    | SCN2681  D41464 D41464                      |
    |                         137000402           |
    |                                             |
    |                                18.432MHz    |
    |       ESPR6                    16MHz        |
    |    POT                                      |
    |                                  R1136-11   |
    |                                             |
    |                                LM339 LM339  |
    |    J13             J5  J1            J2  J4 |
    |---------------------------------------------|
    Notes:
          J1         - connector for digital jacks
          J2         - connector for keyboard
          J3         - connector for LCD display
          J4         - connector for keyboard
          J5         - connector for power input
          J6         - connector for wave expansion
          J10        - connector for wheels/pressure
          J11        - connector for memory card
          J12        - connector for headphones
          J13        - connector for analog jacks
          68EC020    - Motorola MC68EC020FG16 CPU. Clock input 16MHz
          1370000402 - Unknown PLCC44 IC stamped with the Ensoniq logo. Likely CPLD or gate array.
          ESPR6      - Ensoniq ESPR6 (ES5510) sound chip
          OTTOR2     - Ensoniq OTTOR2 (ES5506) sound chip
          POT        - ESP adjustment pot
          KT76*      - 27C2048/27C210 EPROM
          ROM*       - 2M x8-bit SOP44 mask ROM
          R1136-11   - DIP40 IC manufactured by Rockwell - believed to be some type of MCU.
          D41464     - NEC D41464 64k x4-bit DRAM
          62256      - 32k x8-bit SRAM
          SCN2681    - Philips SCN2681 Dual Universal Asynchronous Receiver/Transmitter (DUART)
          HP_6N138   - HP/Agilent HP 6N138 Low Input Current High Gain Optocoupler
          PAL1       - MMI PAL20L8ACN stamped 'KT-76 MMU 6A0A'. Printing is faint so 0 could be a B or a D.
          PAL2       - MMI PAL20L8ACN stamped 'KT-76 BCU 73D6'

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

#include "emu.h"
#include "esqpanel.h"

#include "bus/midi/midi.h"
#include "cpu/es5510/es5510.h"
#include "cpu/m68000/m68000.h"
#include "cpu/m68000/m68020.h"
#include "machine/mc68681.h"
#include "sound/es5506.h"
#include "sound/esqpump.h"

#include "speaker.h"


namespace {

class esqkt_state : public driver_device
{
public:
	esqkt_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_esp(*this, "esp")
		, m_pump(*this, "pump")
		, m_duart(*this, "duart")
		, m_sq1panel(*this, "sq1panel")
		, m_mdout(*this, "mdout")
	{ }

	void kt(machine_config &config);
	void ts(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<m68000_base_device> m_maincpu;
	required_device<es5510_device> m_esp;
	required_device<esq_5505_5510_pump_device> m_pump;
	required_device<scn2681_device> m_duart;
	required_device<esqpanel_device> m_sq1panel;
	required_device<midi_port_device> m_mdout;

	void duart_irq_handler(int state);
	void duart_tx_a(int state);
	void duart_tx_b(int state);
	void duart_output(u8 data);

	u16 *m_rom = nullptr, *m_ram = nullptr;
	u8 m_duart_io = 0;
	bool m_bCalibSecondByte = false; // only set to false on machine_reset()?

	void esq5506_otto_irq(int state);
	u16 esq5506_read_adc();
	void es5506_clock_changed(u32 data);
	void kt_map(address_map &map) ATTR_COLD;
	void ts_map(address_map &map) ATTR_COLD;
};

void esqkt_state::machine_start()
{
	m_rom = (u16 *)(void *)memregion("osrom")->base();
	m_ram = (u16 *)(void *)memshare("osram")->ptr();
	m_duart_io = 0;

	save_item(NAME(m_duart_io));
}

void esqkt_state::machine_reset()
{
	m_bCalibSecondByte = false;
	memcpy(m_ram, m_rom, 8);
}

void esqkt_state::kt_map(address_map &map)
{
	map(0x000000, 0x07ffff).rom().region("osrom", 0);
	map(0x200000, 0x20003f).rw("ensoniq1", FUNC(es5506_device::read), FUNC(es5506_device::write));
	map(0x240000, 0x24003f).rw("ensoniq2", FUNC(es5506_device::read), FUNC(es5506_device::write));
	map(0x280000, 0x2801ff).rw(m_esp, FUNC(es5510_device::host_r), FUNC(es5510_device::host_w));
	map(0x300000, 0x30001f).rw(m_duart, FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0xff0000, 0xffffff).ram().share("osram");
}

void esqkt_state::ts_map(address_map &map)
{
	map(0x000000, 0x01ffff).ram().share("osram");
	map(0x200000, 0x20001f).rw(m_duart, FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask16(0x00ff);
	// 280001/280003 = FDC uPD72065?
	// 2c0001 = ?
	map(0x300000, 0x30007f).rw("ensoniq", FUNC(es5506_device::read), FUNC(es5506_device::write)).umask16(0x00ff);
	map(0x380000, 0x3801ff).rw(m_esp, FUNC(es5510_device::host_r), FUNC(es5510_device::host_w)).umask16(0x00ff);
	map(0x400000, 0xbfffff).ram();  // user sample RAM
	map(0xc00000, 0xcfffff).rom().region("osrom", 0);
	map(0xff0000, 0xffffff).ram();
}

void esqkt_state::esq5506_otto_irq(int state)
{
	#if 0   // 5505/06 IRQ generation needs (more) work
	m_maincpu->set_input_line(1, state);
	#endif
}

u16 esqkt_state::esq5506_read_adc()
{
	switch ((m_duart_io & 7) ^ 7)
	{
		case 0:     // vRef to check battery
			return 0x5b00;

		case 2:     // battery voltage
			return 0x7f00;

		default: // pedal
			return 0;
	}

#ifdef UNUSED
	// Coverity 315636
	if (m_duart_io & 1)
	{
		return 0x5b00;              // vRef
	}
	else
	{
		return 0x7f00;              // vBattery
	}
#endif
}

void esqkt_state::es5506_clock_changed(u32 data)
{
	m_pump->set_unscaled_clock(data);
}

void esqkt_state::duart_irq_handler(int state)
{
	m_maincpu->set_input_line(M68K_IRQ_3, state);
}

void esqkt_state::duart_output(u8 data)
{
	m_duart_io = data;

//    printf("DUART output: %02x (PC=%x)\n", data, m_maincpu->pc());
}

void esqkt_state::duart_tx_a(int state)
{
	m_mdout->write_txd(state);
}

void esqkt_state::duart_tx_b(int state)
{
	m_sq1panel->rx_w(state);
}

void esqkt_state::kt(machine_config &config)
{
	M68EC020(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &esqkt_state::kt_map);

	ES5510(config, m_esp, 10_MHz_XTAL);
	m_esp->set_disable();

	auto &panel(ESQPANEL2X16_SQ1(config, "sq1panel"));
	panel.write_tx().set(m_duart, FUNC(scn2681_device::rx_b_w));

	SCN2681(config, m_duart, 4000000);
	m_duart->irq_cb().set(FUNC(esqkt_state::duart_irq_handler));
	m_duart->a_tx_cb().set(FUNC(esqkt_state::duart_tx_a));
	m_duart->b_tx_cb().set(FUNC(esqkt_state::duart_tx_b));
	m_duart->outport_cb().set(FUNC(esqkt_state::duart_output));
	m_duart->set_clocks(500000, 500000, 1000000, 1000000);

	auto &mdin(MIDI_PORT(config, "mdin"));
	midiin_slot(mdin);
	mdin.rxd_handler().set(m_duart, FUNC(scn2681_device::rx_a_w)); // route MIDI Tx send directly to 68681 channel A Rx

	midiout_slot(MIDI_PORT(config, "mdout"));

	SPEAKER(config, "speaker", 2).front();

	ESQ_5505_5510_PUMP(config, m_pump, 16_MHz_XTAL / (16 * 32));
	m_pump->set_esp(m_esp);
	m_pump->add_route(0, "speaker", 1.0, 0);
	m_pump->add_route(1, "speaker", 1.0, 1);

	auto &es5506a(ES5506(config, "ensoniq1", 16_MHz_XTAL));
	es5506a.sample_rate_changed().set(FUNC(esqkt_state::es5506_clock_changed)); // TODO : Sync with 2 chips?
	es5506a.set_region0("waverom");  /* Bank 0 */
	es5506a.set_region1("waverom2"); /* Bank 1 */
	es5506a.set_region2("waverom3"); /* Bank 0 */
	es5506a.set_region3("waverom4"); /* Bank 1 */
	es5506a.set_channels(4);          /* channels */
	es5506a.irq_cb().set(FUNC(esqkt_state::esq5506_otto_irq)); /* irq */
	es5506a.read_port_cb().set(FUNC(esqkt_state::esq5506_read_adc)); /* ADC */
	es5506a.add_route(0, "pump", 1.0, 0);
	es5506a.add_route(1, "pump", 1.0, 1);
	es5506a.add_route(2, "pump", 1.0, 2);
	es5506a.add_route(3, "pump", 1.0, 3);
	es5506a.add_route(4, "pump", 1.0, 4);
	es5506a.add_route(5, "pump", 1.0, 5);
	es5506a.add_route(6, "pump", 1.0, 6);
	es5506a.add_route(7, "pump", 1.0, 7);

	auto &es5506b(ES5506(config, "ensoniq2", 16_MHz_XTAL));
	es5506b.set_region0("waverom");  /* Bank 0 */
	es5506b.set_region1("waverom2"); /* Bank 1 */
	es5506b.set_region2("waverom3"); /* Bank 0 */
	es5506b.set_region3("waverom4"); /* Bank 1 */
	es5506b.set_channels(4);          /* channels */
	es5506b.add_route(0, "speaker", 1.0, 0);
	es5506b.add_route(1, "speaker", 1.0, 1);
	es5506b.add_route(2, "speaker", 1.0, 0);
	es5506b.add_route(3, "speaker", 1.0, 1);
	es5506b.add_route(4, "speaker", 1.0, 0);
	es5506b.add_route(5, "speaker", 1.0, 1);
	es5506b.add_route(6, "speaker", 1.0, 0);
	es5506b.add_route(7, "speaker", 1.0, 1);
}

void esqkt_state::ts(machine_config &config)
{
	M68000(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &esqkt_state::ts_map);
	ES5510(config, m_esp, 10_MHz_XTAL);
	m_esp->set_disable();

	auto &panel(ESQPANEL2X40_VFX(config, "sq1panel"));
	panel.write_tx().set(m_duart, FUNC(scn2681_device::rx_b_w));

	SCN2681(config, m_duart, 4000000);
	m_duart->irq_cb().set(FUNC(esqkt_state::duart_irq_handler));
	m_duart->a_tx_cb().set(FUNC(esqkt_state::duart_tx_a));
	m_duart->b_tx_cb().set(FUNC(esqkt_state::duart_tx_b));
	m_duart->outport_cb().set(FUNC(esqkt_state::duart_output));
	m_duart->set_clocks(500000, 500000, 1000000, 1000000);

	auto &mdin(MIDI_PORT(config, "mdin"));
	midiin_slot(mdin);
	mdin.rxd_handler().set(m_duart, FUNC(scn2681_device::rx_a_w)); // route MIDI Tx send directly to 68681 channel A Rx

	midiout_slot(MIDI_PORT(config, "mdout"));

	SPEAKER(config, "speaker", 2).front();

	ESQ_5505_5510_PUMP(config, m_pump, 16_MHz_XTAL / (16 * 32));
	m_pump->set_esp(m_esp);
	m_pump->add_route(0, "speaker", 1.0, 0);
	m_pump->add_route(1, "speaker", 1.0, 1);

	auto &es5506a(ES5506(config, "ensoniq", 16_MHz_XTAL));
	es5506a.sample_rate_changed().set(FUNC(esqkt_state::es5506_clock_changed));
	es5506a.set_region0("waverom");  /* Bank 0 */
	es5506a.set_region1("waverom2"); /* Bank 1 */
	es5506a.set_region2("waverom3"); /* Bank 0 */
	es5506a.set_region3("waverom4"); /* Bank 1 */
	es5506a.set_channels(4);          /* channels */
	es5506a.irq_cb().set(FUNC(esqkt_state::esq5506_otto_irq)); /* irq */
	es5506a.read_port_cb().set(FUNC(esqkt_state::esq5506_read_adc)); /* ADC */
	es5506a.add_route(0, "pump", 1.0, 0);
	es5506a.add_route(1, "pump", 1.0, 1);
	es5506a.add_route(2, "pump", 1.0, 2);
	es5506a.add_route(3, "pump", 1.0, 3);
	es5506a.add_route(4, "pump", 1.0, 4);
	es5506a.add_route(5, "pump", 1.0, 5);
	es5506a.add_route(6, "pump", 1.0, 6);
	es5506a.add_route(7, "pump", 1.0, 7);
}
static INPUT_PORTS_START( kt )
INPUT_PORTS_END

ROM_START( kt76 )
	ROM_REGION32_BE(0x80000, "osrom", 0)
	ROM_LOAD32_WORD_SWAP( "kt76_162_lo.bin", 0x000002, 0x020000, CRC(1a1ab910) SHA1(dcc80db2297fd25993e090c2e5bb7f947319a8bf) )
	ROM_LOAD32_WORD_SWAP( "kt76_162_hi.bin", 0x000000, 0x040000, CRC(de16d236) SHA1(c55fca86453e90e8c34a048bed45817063237370) )

	ROM_REGION(0x400000, "waverom", ROMREGION_ERASE00)
	ROM_LOAD16_BYTE( "1351000401_rom0.u103", 0x000001, 0x200000, CRC(425047af) SHA1(9680d1fc222b29ba24f0fbf6136982bee87a60ef) )

	ROM_REGION(0x400000, "waverom2", ROMREGION_ERASE00)
	ROM_LOAD16_BYTE( "1351000402_rom1.u102", 0x000001, 0x200000, CRC(64459185) SHA1(0fa20b16847fc02a384057fc3d385226eb3e7527) )

	ROM_REGION(0x400000, "waverom3", ROMREGION_ERASE00)
	ROM_LOAD16_BYTE( "1351000403_rom2.u104", 0x000001, 0x200000, CRC(c2aacc5d) SHA1(7fab518ba92ddb23cdc4dcb04751b26d25c298c0) )

	ROM_REGION(0x200000, "waverom4", ROMREGION_ERASE00)
ROM_END

ROM_START( ts10 )
	ROM_REGION16_BE(0x100000, "osrom", ROMREGION_ERASE00)
	ROM_SYSTEM_BIOS( 0, "v310", "V310" )
	ROMX_LOAD( "ts10_310h.bin", 0x000000, 0x040000, CRC(cc04aa4f) SHA1(56761d29680bc99cfd625af3f92db836dfacdf31), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "ts10_310l.bin", 0x000001, 0x080000, CRC(51df8987) SHA1(294cde504a36752041deb0c09741153a797f2f28), ROM_SKIP(1) | ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v120", "V120" )
	ROMX_LOAD( "ts10_120l.bin", 0x000000, 0x040000, CRC(047a0aaa) SHA1(6fd592e1a4e6c50877f4266f90620d5f2bd578aa), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "ts10_120h.bin", 0x000001, 0x040000, CRC(3ae66382) SHA1(ce603931d67735e2a8942b6c0992c138923858a6), ROM_SKIP(1) | ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v202", "V202" )
	ROMX_LOAD( "ts10_202l.bin", 0x000000, 0x040000, CRC(509f1c46) SHA1(4feab8c6c1b2e96cf15ae59c0148b7308fd1ee7f), ROM_SKIP(1) | ROM_BIOS(2) )
	ROMX_LOAD( "ts10_202h.bin", 0x000001, 0x040000, CRC(cacedb2c) SHA1(56fdf842a302b41ae2849c0e05ed1f38f42f2e18), ROM_SKIP(1) | ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "v305", "V305" )
	ROMX_LOAD( "ts10_305l.bin", 0x000000, 0x080000, CRC(3df39031) SHA1(3f14b56c7ea0c28f6173a7caf145d18e6287e33e), ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "ts10_305h.bin", 0x000001, 0x040000, CRC(77d547e7) SHA1(268ae44b07a02686c31639c1344d23179a672f87), ROM_SKIP(1) | ROM_BIOS(3) )

	ROM_REGION(0x400000, "waverom", ROMREGION_ERASE00)
	ROM_REGION(0x400000, "waverom2", ROMREGION_ERASE00)
	ROM_REGION(0x400000, "waverom3", ROMREGION_ERASE00)
	ROM_REGION(0x400000, "waverom4", ROMREGION_ERASE00)
ROM_END

ROM_START( ts12 )
	ROM_REGION16_BE(0x100000, "osrom", 0)
	ROM_SYSTEM_BIOS( 0, "v310", "V310" )
	ROMX_LOAD( "ts12-v310-hig.bin", 0x000000, 0x040000, CRC(99823433) SHA1(bd39a5d27824988cb531d90b91685a2362a98b3e), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "ts12-v310-low.bin", 0x000001, 0x080000, CRC(7e64a659) SHA1(c567c2d349e5a58928a74ac22473896c6814bda8), ROM_SKIP(1) | ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v305", "V305" )
	ROMX_LOAD( "ts12_305h.bin", 0x000000, 0x040000, CRC(7968ca06) SHA1(2460d4a824445425d0d733faad1f9b3829fa19ae), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "ts12_305l.bin", 0x000000, 0x080000, CRC(30b4bece) SHA1(41b2f1df99b8106c96d532702de93badea085ed7), ROM_SKIP(1) | ROM_BIOS(1) )

	ROM_REGION(0x400000, "waverom", ROMREGION_ERASE00)
	ROM_REGION(0x400000, "waverom2", ROMREGION_ERASE00)
	ROM_REGION(0x400000, "waverom3", ROMREGION_ERASE00)
	ROM_REGION(0x400000, "waverom4", ROMREGION_ERASE00)
ROM_END

} // Anonymous namespace


CONS( 1993, ts10, 0, 0, ts, kt, esqkt_state, empty_init, "Ensoniq", "TS-10", MACHINE_NOT_WORKING|MACHINE_IMPERFECT_SOUND )
CONS( 1993, ts12, 0, 0, ts, kt, esqkt_state, empty_init, "Ensoniq", "TS-12", MACHINE_NOT_WORKING|MACHINE_IMPERFECT_SOUND )
CONS( 1996, kt76, 0, 0, kt, kt, esqkt_state, empty_init, "Ensoniq", "KT-76", MACHINE_IMPERFECT_SOUND )



esqmr.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    esqmr.c - Ensoniq MR-61, MR-76, and MR-Rack

    Skeleton driver by R. Belmont

    Hardware:
        CPU: 68340 MCU
        Sound: 2xES5506
        Effects: ES5511

    Memory map:

    0x000000-0x0FFFFF   OS ROM

    MR Rack
    Ensoniq, 1995

    This is a 64-voice expandable synth module made by Ensoniq in 1995.
    It is shipped with 12MB of 16-bit 44.1kHz wave data and can be expanded
    with up to 3 ENSONIQ EXP Series Wave Expansion Boards containing up to a
    maximum of 84MB of additional wave data.


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

    PN: 4001028501 REV C
    |--------------------------------------------------------|
    | J6  J7          J12  J13               J21   J23    J22|
    |                                                      J5|
    |       4565  4565                          HP_6N138     |
    |                                                        |
    |                                      BATTERY   7407    |
    |     4565                                               |
    |                                                        |
    |          OTTOR2  OTTOR2                                |
    |AD1861                                                  |
    |AD1861                                                  |
    |AD1861                                                  |
    |AD1861                                    |--J3-----|   |
    | HC04                                     |EXPANSION|   |
    |                                          |BOARD #3 |   |
    |                                          |         |   |
    |            1370001501                    |         |   |
    |                                          |         |   |
    |                                          |--J8-----|   |
    |     ESP2                         ROM2                  |
    |                                          |--J2-----|   |
    |                                          |EXPANSION|   |
    |                                  ROM0    |BOARD #2 |   |
    |                                          |         |   |
    | D43256               IDT7130             |         |   |
    | D43256  35MHz  16MHz             ROM1    |         |   |
    | D43256               J14                 |--J9-----|   |
    | D43256     22.5792MHz                            ADM691|
    | D43256             MC68340               |--J1-----|   |
    | D43256                                   |EXPANSION|   |
    |                    6MHz                  |BOARD #1 |   |
    |                          EPROM_UP        |         |   |
    |         MC68HC705C4A                KM681000       |   |
    |J4                        EPROM_LO   KM681000       |J11|
    |       J19        J18                     |--J10----|   |
    |--------------------------------------------------------|
    Notes:
          J4/J18/J19   - Connectors to front panel buttons, LCD etc
          J1/J10       - Connectors for expansion board #1
          J2/J9        - Connectors for expansion board #2
          J3/J8        - Connectors for expansion board #3
          J11          - Memory card connector
          J14          - JTAG connector
          J6           - Main left/mono jack
          J7           - Main right jack
          J12          - Aux left/mono jack
          J13          - Aux right jack
          J21          - MIDI in connector
          J23          - MIDI out connector
          J22          - MIDI thru connector
          J5           - Power input connector
          1370001501   - Unknown TQFP144 IC stamped with the Ensoniq logo. Possibly CPLD?
          ESP2         - Ensoniq ESP2 sound chip
          OTTOR2       - Ensoniq OTTOR2 sound chip
          ROM*         - 4M x8-bit SOP44 mask ROM
          D43256       - NEC D43256 32k x8-bit Static RAM
          HP_6N138     - HP/Agilent HP 6N138 Low Input Current High Gain Optocoupler
          4565         - JRC4565 Dual Operational Amplifier
          AD1861       - Analog Devices AD1861 16-bit/18-bit PCM Audio DAC
          MC68HC705C4A - Motorola MC68HC705C4A Microcontroller. Clock input is tied to the TQFP CPLD
          MC68340      - Motorola MC68340PV16E 68000-compatible 32-bit processor with on-board peripherals. Clock input 6.000MHz
          EPROM*       - 27C4001 EPROM
          IDT7130      - IDT7130 High Speed 1k x8-bit Dual Port Static RAM
          KM681000     - Samsung KM681000 128k x8-bit Static RAM
          ADM691       - Analog Devices ADM691 Microprocessor Supervisory Circuit with Automatic Battery Backup Switching


    Additional notes from the manual:
    ---------------------------------

    The MR-Rack can play special demonstration songs to give you an idea of how terrific it sounds.

    To Play the MR-Rack Main Demo
    1. Press the Audition button, and hold it down.
    2. While still holding Audition, press the Save button.
    3. Let go of both buttons.

    In an unexpanded MR-Rack, the display shows:

    Hit ENTER to Play:
    MAINDEMO:MR Internal

    If you've installed any Expansion boards or a ROM card containing MAINDEMO-type
    demonstration songs, your display will differ. Turn the Value knob counter-clockwise
    until the display looks as it does above.

    Note: When MR-Rack demos are being viewed or playing, MIDI In is disabled.

    4. Press Enter to play the demo.
    5. Press Enter again to stop the demo.
    6. When you're done listening to the demo song, press Exit to return to normal MR-Rack


    The Version Number of Your MR-Rack Operating System:
    You can easily find out what operating system (or O.S.) your MR-Rack is currently using.
    To Find the Operating System:
    1. Press the Save button and hold it down.
    2. While still holding the Save button, press the System button.
    The display briefly shows your current Operating System:

    ENSONIQ MR-RACK
    O.S. Version: #.##



    ENSONIQ EXP Series Wave Expansion Boards
    ----------------------------------------

    These are small plug-in boards containing a 256k x8-bit EPROM (27C020) and from 1 to 6
    SOP44 mask ROMs. These add additional digital sound waves. These can be used with the MR
    Rack, the full-size MR Keyboards and a few other models.

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

    PN: 4001033401 REV B
    |------------------|
    |                  |
    |    U6       U7   |
    |-                 |
    ||                 |
    ||            U1   |
    ||    U2          -|
    ||                ||
    ||                ||
    ||    U3          ||
    ||                ||
    ||             U8 ||
    ||    U4          ||
    ||                ||
    ||                ||
    ||    U5          ||
    |-                -|
    |------------------|
    Notes:
          U1       - AC138 logic chip
          U2 to U7 - 4M x 8-bit or 2M x 8-bit SOP44 mask ROM
          U8       - 27C020 EPROM

    The EXP boards dumped so far are....

                             MROM  # of
    Name            Version  Size  MROMs
    -----------------------------------
    Piano           V1.00    4M    4 (multisampled pianos, aka "The Perfect Piano")
    The Real World  V1.01    4M    6 (non-Western percusson and other "world music" sounds)
    Drum            V1.00    2M    1 (additional drum kits that are built in to the MR keyboards)


    Additional notes from the manual:
    ---------------------------------

    The MR-Rack provides three displays which can identify any EXP Wave Expansion boards
    you have installed.

    To Identify an Installed Expansion Board
    1. Press the System button.
    2. Turn the Parameter knob until the display shows:

    System parameters:
    WaveEXP1:xxxxxxxxxxx

    When an Expansion Board is installed, this read-only display will show the name of the
    Expansion Board located in the first slot.

    3. Turning the Parameter knob two more times will reveal two more displays which show
    the names of the Expansion Boards in Wave EXP Slots 2 and 3 (if they're installed).
    If there are no Expansion Boards installed, the display will show "WaveEXP1= **EMPTY**."

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

#include "emu.h"
#include "esqvfd.h"

#include "cpu/m6805/m68hc05.h"
#include "machine/68340.h"
#include "machine/68340ser.h"
#include "sound/es5506.h"
#include "esqpanel.h"
//#include "machine/mb8421.h"

#include "speaker.h"


namespace {

class esqmr_state : public driver_device
{
public:
	esqmr_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_panel(*this, "sq1vfd")
	{ }

	void mr(machine_config &config);

	void init_mr();

private:
	required_device<m68340_cpu_device> m_maincpu;
	required_device<esqpanel2x40_vfx_device> m_panel;

	virtual void machine_reset() override ATTR_COLD;

	void esq5506_otto_irq(int state);
	u16 esq5506_read_adc();
	void duart_tx_a(int state);
	void duart_tx_b(int state);

	void mr_map(address_map &map) ATTR_COLD;
};

void esqmr_state::machine_reset()
{
}

void esqmr_state::mr_map(address_map &map)
{
	map(0x00000000, 0x000fffff).rom().region("maincpu", 0);
	map(0x00300000, 0x0037ffff).rom().region("maincpu", 0x80000);   // MR-61 needs this
	map(0x00c00000, 0x00c7ffff).ram();
	map(0x00dc0000, 0x00dc003f).rw("ensoniq", FUNC(es5506_device::read), FUNC(es5506_device::write));
	map(0x00de0000, 0x00de003f).rw("ensoniq2", FUNC(es5506_device::read), FUNC(es5506_device::write));
}

void esqmr_state::duart_tx_a(int state)
{
	//m_mdout->write_txd(state);
}

void esqmr_state::duart_tx_b(int state)
{
	m_panel->rx_w(state);
}

void esqmr_state::esq5506_otto_irq(int state)
{
}

u16 esqmr_state::esq5506_read_adc()
{
	return 0;
}

void esqmr_state::mr(machine_config &config)
{
	M68340(config, m_maincpu, XTAL(16'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &esqmr_state::mr_map);

	mc68340_serial_module_device &duart(*m_maincpu->subdevice<mc68340_serial_module_device>("serial"));
	duart.set_clocks(500000, 500000, 1000000, 1000000);
	duart.a_tx_cb().set(FUNC(esqmr_state::duart_tx_a));
	duart.b_tx_cb().set(FUNC(esqmr_state::duart_tx_b));

	M68HC705C4A(config, "mcu", 4'000'000);

	//IDT7130(config, "dpram"); // present in PCB, but unknown purpose (mcu communication?)

	ESQPANEL2X40_VFX(config, m_panel);
	m_panel->write_tx().set(duart, FUNC(mc68340_serial_module_device::rx_b_w));

	SPEAKER(config, "speaker", 2).front();

	es5506_device &ensoniq(ES5506(config, "ensoniq", XTAL(16'000'000)));
	ensoniq.set_region0("waverom");  /* Bank 0 */
	ensoniq.set_region1("waverom2"); /* Bank 1 */
	ensoniq.set_region2("waverom3"); /* Bank 0 */
	ensoniq.set_region3("waverom4"); /* Bank 1 */
	ensoniq.set_channels(1);
	ensoniq.irq_cb().set(FUNC(esqmr_state::esq5506_otto_irq)); /* irq */
	ensoniq.read_port_cb().set(FUNC(esqmr_state::esq5506_read_adc));
	ensoniq.add_route(0, "speaker", 0.5, 0);
	ensoniq.add_route(1, "speaker", 0.5, 1);

	es5506_device &ensoniq2(ES5506(config, "ensoniq2", XTAL(16'000'000)));
	ensoniq2.set_region0("waverom");  /* Bank 0 */
	ensoniq2.set_region1("waverom2"); /* Bank 1 */
	ensoniq2.set_region2("waverom3"); /* Bank 0 */
	ensoniq2.set_region3("waverom4"); /* Bank 1 */
	ensoniq2.set_channels(1);
	ensoniq2.add_route(0, "speaker", 0.5, 0);
	ensoniq2.add_route(1, "speaker", 0.5, 1);
}

static INPUT_PORTS_START( mr )
INPUT_PORTS_END

ROM_START( mr61 )
	ROM_REGION(0x100000, "maincpu", 0)
	ROM_LOAD16_WORD_SWAP( "mrw-osf-11af-2.10.bin",  0x000000, 0x080000, CRC(5854314e) SHA1(8fb2e2ee2f5fb12eae8ea33cb18f757efaec6780) )
	ROM_LOAD16_WORD_SWAP( "mrw-romc-32ef-1.20.bin", 0x080000, 0x080000, CRC(68321347) SHA1(56cb96943ba42c35ba2787a49b5f4adf7c8dffb8) )

	ROM_REGION(0x2000, "mcu", ROMREGION_ERASE00)

	ROM_REGION(0x400000, "waverom", ROMREGION_ERASE00)

	ROM_REGION(0x400000, "waverom2", ROMREGION_ERASE00)

	ROM_REGION(0x400000, "waverom3", ROMREGION_ERASE00)

	ROM_REGION(0x200000, "waverom4", ROMREGION_ERASE00)
ROM_END

ROM_START( mrrack )
	// 68340 main MCU
	ROM_REGION(0x100000, "maincpu", 0)
	ROM_SYSTEM_BIOS(0, "v153", "Version 1.53")
	ROMX_LOAD( "ensoniq_mr_rack_1.53_lo_46a3.u36", 0x000001, 0x080000, CRC(0dba5bef) SHA1(6f64ec7547ea1fc72b42e2679379cd63cbbfc25e), ROM_BIOS(0) | ROM_SKIP(1) )
	ROMX_LOAD( "ensoniq_mr_rack_1.53_hi_f320.u35", 0x000000, 0x080000, CRC(cb045660) SHA1(53d0f27e9f897c979c26a365bdfaf8911fb6f4d4), ROM_BIOS(0) | ROM_SKIP(1) )
	ROM_SYSTEM_BIOS(1, "v150", "Version 1.50")
	ROMX_LOAD( "mr_r_ec51_lo_1.50.u36", 0x000001, 0x080000, CRC(b29988a1) SHA1(986c2def11de27fa2b9be55ac32f7fec0c414bca), ROM_BIOS(1) | ROM_SKIP(1) )
	ROMX_LOAD( "mr_r_9dac_up_1.50.u35", 0x000000, 0x080000, CRC(71511692) SHA1(54744f16f1db1ac5abb2f70b6e04aebf1e0e029d), ROM_BIOS(1) | ROM_SKIP(1) )

	// 68705 display/front panel MCU
	ROM_REGION(0x2000, "mcu", 0)
	ROM_LOAD( "68hc705.u40",  0x000000, 0x002000, CRC(7b0291a7) SHA1(c92c19ce9289b7b21dbc915475cdff8930e3c677) )

	ROM_REGION(0x400000, "waverom", ROMREGION_ERASE00)
	ROM_LOAD( "1351000901_h-rom0.u5", 0x000000, 0x400000, CRC(89654b42) SHA1(4bdffd8060eb20cdb01f6178222aeb32fdbfd703) )

	ROM_REGION(0x400000, "waverom2", ROMREGION_ERASE00)
	ROM_LOAD( "1351000902_h-rom1.u23", 0x000000, 0x400000, CRC(4a19e517) SHA1(e819f1e0b50c4911c4855ad95ed505998a2bbe86) )

	ROM_REGION(0x400000, "waverom3", ROMREGION_ERASE00)
	ROM_LOAD( "1351000903_h-rom2.u24", 0x000000, 0x400000, CRC(c9ab1214) SHA1(92f48b068bbe49eacbffd03e428599e3ab21b8ec) )

	ROM_REGION(0x200000, "waverom4", ROMREGION_ERASE00)
ROM_END

void esqmr_state::init_mr()
{
}

} // anonymous namespace


CONS( 1996, mr61,   0, 0, mr, mr, esqmr_state, init_mr, "Ensoniq", "MR-61 Workstation", MACHINE_NOT_WORKING )
CONS( 1996, mrrack, 0, 0, mr, mr, esqmr_state, init_mr, "Ensoniq", "MR-Rack",           MACHINE_NOT_WORKING )



et3400.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

ET-3400

2009-05-12 Skeleton driver.
2016-04-29 Added Accessory.

ETA-3400 Memory I/O Accessory
- Provides Tiny Basic, a Terminal, a Serial Interface, a Cassette
  interface, and 1k to 4k of expansion RAM. All parts are working.
- The roms are U105 (Monitor), U106 (Tiny Basic), both type NMOS2316E,
  and U108 (address decoder PROM).
- Navigating:
    LED to Monitor: D1400
    Monitor to Basic: G 1C00
    Monitor to LED: G FC00
    Basic to Monitor: BYE
- All commands in Basic and Monitor are UPPERCASE only.
- Terminal is defaulted to 9600 baud, 7 bits, 2 stop bits.


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

#include "emu.h"
#include "cpu/m6800/m6800.h"
#include "machine/74259.h"
#include "machine/6821pia.h"
#include "bus/rs232/rs232.h"
#include "imagedev/cassette.h"
#include "speaker.h"

#include "et3400.lh"

namespace {

class et3400_state : public driver_device
{
public:
	et3400_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pia(*this, "pia")
		, m_displatch(*this, "displatch%u", 1)
		, m_rs232(*this, "rs232")
		, m_cass(*this, "cassette")
		, m_x(*this, "X%u", 0U)
		, m_digit(*this, "digit%u", 1U)
	{ }

	void et3400(machine_config &config);

	void reset_key_w(int state);
	void segment_test_w(int state);

private:

	virtual void machine_start() override ATTR_COLD;

	uint8_t keypad_r(offs_t offset);
	void display_w(offs_t offset, uint8_t data);
	template <int Digit> void led_w(uint8_t data);
	uint8_t pia_ar();
	void pia_aw(uint8_t data);
	uint8_t pia_br();
	void pia_bw(uint8_t data);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<pia6821_device> m_pia;
	required_device_array<ls259_device, 6> m_displatch;
	required_device<rs232_port_device> m_rs232;
	required_device<cassette_image_device> m_cass;
	required_ioport_array<3> m_x;
	output_finder<6> m_digit;
};



void et3400_state::machine_start()
{
	m_digit.resolve();
}


uint8_t et3400_state::keypad_r(offs_t offset)
{
	uint8_t data = 0xff;

	if (~offset & 4) data &= m_x[2]->read();
	if (~offset & 2) data &= m_x[1]->read();
	if (~offset & 1) data &= m_x[0]->read();

	return data;
}

void et3400_state::display_w(offs_t offset, uint8_t data)
{
	// A6-A4 decoded by IC22 (74LS42); D0 inverted by one gate of IC21 (74S00)
	uint8_t digit = (offset >> 4) & 7;
	if (digit >= 1 && digit <= 6)
		m_displatch[digit - 1]->write_bit(offset & 7, !BIT(data, 0));
}

template <int Digit>
void et3400_state::led_w(uint8_t data)
{
	// This computer sets each segment, one at a time.
	m_digit[Digit - 1] = bitswap<8>(~data, 7, 0, 1, 2, 3, 4, 5, 6);
}

// d1,2,3 = Baud rate
// d4 = gnd
// d7 = rs232 in
uint8_t et3400_state::pia_ar()
{
	return ioport("BAUD")->read() | (m_rs232->rxd_r() << 7);
}

// d0 = rs232 out
void et3400_state::pia_aw(uint8_t data)
{
	m_rs232->write_txd(BIT(data, 0));
}

// d7 = cass in
uint8_t et3400_state::pia_br()
{
	return (m_cass->input() > +0.0) << 7;
}

// d0 = cass out
void et3400_state::pia_bw(uint8_t data)
{
	m_cass->output(BIT(data, 0) ? -1.0 : +1.0);
}


void et3400_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).ram();
	map(0x1000, 0x1003).mirror(0x03fc).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x1400, 0x23ff).rom().region("roms", 0);
	map(0xc000, 0xc0ff).r(FUNC(et3400_state::keypad_r));
	map(0xc100, 0xc1ff).w(FUNC(et3400_state::display_w));
	map(0xfc00, 0xffff).rom().region("roms", 0x1000);
}

void et3400_state::reset_key_w(int state)
{
	// delivered through MC6875 (or 74LS241 on ET-3400A)
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? CLEAR_LINE : ASSERT_LINE);

	// PIA also uses reset line
	if (!state)
		m_pia->reset();
}

void et3400_state::segment_test_w(int state)
{
	for (int d = 0; d < 6; d++)
		m_displatch[d]->clear_w(state);
}

/* Input ports */
static INPUT_PORTS_START( et3400 )
	PORT_START("X0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("D DO")    PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("A AUTO")  PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("7 RTI")   PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("4 INDEX") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("1 ACCA")  PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("0")       PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0xc0, 0xc0, IPT_UNUSED )

	PORT_START("X1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("E EXAM")  PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("B BACK")  PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("8 SS")    PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("5 CC")    PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("2 ACCB")  PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0xe0, 0xe0, IPT_UNUSED )

	PORT_START("X2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("F FWD")   PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("C CHAN")  PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("9 BR")    PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("6 SP")    PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("3 PC")    PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0xe0, 0xe0, IPT_UNUSED )

	PORT_START("BAUD")
	PORT_DIPNAME( 0x0E, 0x02, "Baud Rate" )
	PORT_DIPSETTING(    0x0E, "110" )
	PORT_DIPSETTING(    0x00, "300" )
	PORT_DIPSETTING(    0x0A, "600" )
	PORT_DIPSETTING(    0x08, "1200" )
	PORT_DIPSETTING(    0x06, "2400" )
	PORT_DIPSETTING(    0x04, "4800" )
	PORT_DIPSETTING(    0x02, "9600" )

	PORT_START("RESET") // RESET is directly next to 0, but electrically separate from key matrix
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("RESET") PORT_CODE(KEYCODE_STOP) PORT_WRITE_LINE_MEMBER(FUNC(et3400_state::reset_key_w))

	PORT_START("TEST") // No input mechanism for "Segment Test" defined other than shorting pins together
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Segment Test") PORT_WRITE_LINE_MEMBER(FUNC(et3400_state::segment_test_w))
INPUT_PORTS_END


static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END

void et3400_state::et3400(machine_config &config)
{
	/* basic machine hardware */
	M6800(config, m_maincpu, XTAL(4'000'000) / 4 ); // 1MHz with memory i/o accessory, or 500khz without it
	m_maincpu->set_addrmap(AS_PROGRAM, &et3400_state::mem_map);

	/* video hardware */
	config.set_default_layout(layout_et3400);

	// Devices
	PIA6821(config, m_pia);
	m_pia->writepa_handler().set(FUNC(et3400_state::pia_aw));
	m_pia->writepb_handler().set(FUNC(et3400_state::pia_bw));
	m_pia->readpa_handler().set(FUNC(et3400_state::pia_ar));
	m_pia->readpb_handler().set(FUNC(et3400_state::pia_br));

	RS232_PORT(config, m_rs232, default_rs232_devices, "terminal").set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	for (std::size_t i = 0; i < 6; i++)
		LS259(config, m_displatch[i]);

	m_displatch[0]->parallel_out_cb().set(FUNC(et3400_state::led_w<1>));
	m_displatch[1]->parallel_out_cb().set(FUNC(et3400_state::led_w<2>));
	m_displatch[2]->parallel_out_cb().set(FUNC(et3400_state::led_w<3>));
	m_displatch[3]->parallel_out_cb().set(FUNC(et3400_state::led_w<4>));
	m_displatch[4]->parallel_out_cb().set(FUNC(et3400_state::led_w<5>));
	m_displatch[5]->parallel_out_cb().set(FUNC(et3400_state::led_w<6>));

	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
}

/* ROM definition */
ROM_START( et3400 )
	ROM_REGION( 0x1420, "roms", 0 )
	ROM_LOAD( "monitor.u105",      0x0000, 0x0800, CRC(e4142682) SHA1(785966018dd6eb097ed9bd5c7def2354ab4347db) )
	ROM_LOAD( "basic.u106",        0x0800, 0x0800, CRC(bbd6a801) SHA1(088da24bd4d923d4f196b993154c538835d10605) )
	ROM_LOAD( "et3400.ic12",       0x1000, 0x0400, CRC(2eff1f58) SHA1(38b655de7393d7a92b08276f7c14a99eaa2a4a9f) )
	ROM_LOAD_OPTIONAL("prom.u108", 0x1400, 0x0020, CRC(273025c3) SHA1(136c1cdce2a4a796c1c46e8ea4f798cdee4b549b) ) // not used
ROM_END

} // Anonymous namespace

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY          FULLNAME                                         FLAGS */
COMP( 1976, et3400, 0,      0,      et3400,  et3400, et3400_state, empty_init, "Heath Company", "Heathkit Model ET-3400 Microprocessor Trainer", MACHINE_SUPPORTS_SAVE )



eti660.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder, Robbbert
/*****************************************************************************************

    ETI project 660, featured in the Australian magazine Electronics Today International.

    Commands:
    R - Reset (use this before any other instruction)
    S - Step
    0 - Enter modify memory mode
    2 - Save memory to cassette
    4 - Load memory from cassette
    6 - start binary program
    8 - start chip-8 program.

    To modify memory, press R, 0, enter 4-digit address, S, enter data, S, continue on.
    R to escape.

    To save a tape, enter the start address into 0400,0401 (big endian), and the end
    address into 0402,0403. Press R. Press record on the tape, press 2.

    To load a tape, enter the start and end addresses as above. Press R, 4. Screen goes
    black. Press play on tape. If the screen is still black after the tape ends, press R.

    All chip-8 programs start at 0600. The manual says the max end address is 7FF (gives
    512 bytes), but you can fill up all of memory (gives 2560 bytes).

    TODO:
    - sometimes there's no sound when started. You may need to hard reset until it beeps.
    - doesn't run programs for other chip-8 computers (this might be normal?)
    - we support BIN files, but have none to test with.
    - possible CPU bugs?:
      - in Invaders, can't shoot them
      - in Maze, the result is rubbish (works in Emma02 emulator v1.21, but not in v1.30)

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

#include "emu.h"
#include "cpu/cosmac/cosmac.h"
#include "machine/ram.h"
#include "machine/6821pia.h"
#include "machine/rescap.h"
#include "sound/cdp1864.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

#define CDP1802_TAG     "ic3"
#define CDP1864_TAG     "ic4"
#define MC6821_TAG      "ic5"

enum
{
	LED_POWER = 0,
	LED_PULSE
};

class eti660_state : public driver_device
{
public:
	eti660_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_p_videoram(*this, "videoram")
		, m_maincpu(*this, CDP1802_TAG)
		, m_cti(*this, CDP1864_TAG)
		, m_pia(*this, MC6821_TAG)
		, m_cassette(*this, "cassette")
		, m_io_keyboard(*this, "X%d", 0U)
		, m_special(*this, "SPECIAL")
		, m_leds(*this, "led%d", 0U)
	{ }

	void eti660(machine_config &config);

private:
	u8 pia_r();
	void pia_w(u8 data);
	void colorram_w(offs_t offset, u8 data);
	int clear_r();
	int ef2_r();
	int ef4_r();
	void q_w(int state);
	void ca2_w(int state);
	void dma_w(offs_t offset, u8 data);
	u8 pia_pa_r();
	void pia_pa_w(u8 data);
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);
	required_shared_ptr<u8> m_p_videoram;

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cosmac_device> m_maincpu;
	required_device<cdp1864_device> m_cti;
	required_device<pia6821_device> m_pia;
	required_device<cassette_image_device> m_cassette;
	required_ioport_array<4> m_io_keyboard;
	required_ioport m_special;
	output_finder<2> m_leds;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	uint16_t m_resetcnt;

	/* keyboard state */
	u8 m_keylatch = 0U;

	/* video state */
	u8 m_color_ram[0xc0]{};
	u8 m_color = 0U;
};


/* Read/Write Handlers */
// Schematic is wrong, PCB layout is correct: D0-7 swapped around on PIA.
u8 eti660_state::pia_r()
{
	u8 pia_offset = m_maincpu->get_memory_address() & 0x03;

	return bitswap<8>(m_pia->read(pia_offset), 0,1,2,3,4,5,6,7);
}

void eti660_state::pia_w(u8 data)
{
	u8 pia_offset = m_maincpu->get_memory_address() & 0x03;
	data = bitswap<8>(data,0,1,2,3,4,5,6,7);
	m_pia->write(pia_offset, data);
}

void eti660_state::ca2_w(int state) // test with Wipeout game - it should start up in colour
{
	m_cti->con_w(state);
}

void eti660_state::colorram_w(offs_t offset, u8 data)
{
	offset = m_maincpu->get_memory_address() - 0xc80;

	u8 colorram_offset = (((offset & 0x1f0) >> 1) | (offset & 0x07));

	if (colorram_offset < 0xc0)
		m_color_ram[colorram_offset] = data;
}

/* Memory Maps */

void eti660_state::mem_map(address_map &map)
{
	map.global_mask(0xfff);
	map(0x0000, 0x03ff).rom();
	map(0x0400, 0x047f).ram();
	map(0x0480, 0x05ff).ram().share("videoram");
	map(0x0600, 0x0fff).ram();
}

void eti660_state::io_map(address_map &map)
{
	map(0x01, 0x01).rw(m_cti, FUNC(cdp1864_device::dispon_r), FUNC(cdp1864_device::step_bgcolor_w));
	map(0x02, 0x02).rw(FUNC(eti660_state::pia_r), FUNC(eti660_state::pia_w));
	map(0x03, 0x03).w(FUNC(eti660_state::colorram_w));
	map(0x04, 0x04).rw(m_cti, FUNC(cdp1864_device::dispoff_r), FUNC(cdp1864_device::tone_latch_w));
}

/* Input Ports */
static INPUT_PORTS_START( eti660 )
	PORT_START("X0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("SPECIAL")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("RESET") PORT_CODE(KEYCODE_R)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("STEP") PORT_CODE(KEYCODE_S)
INPUT_PORTS_END

/* CDP1802 Interface */

int eti660_state::clear_r()
{
	// A hack to make the machine reset itself on
	// boot, like the real one does.
	if (m_resetcnt < 0xffff)
		m_resetcnt++;
	if (m_resetcnt == 0xf000)
		return 0;
	return BIT(m_special->read(), 0); // R key
}

int eti660_state::ef2_r()
{
	return m_cassette->input() < 0;
}

int eti660_state::ef4_r()
{
	return BIT(m_special->read(), 1); // S key
}

void eti660_state::q_w(int state)
{
	/* CDP1864 audio output enable */
	m_cti->aoe_w(state);

	/* PULSE led */
	m_leds[LED_PULSE] = state ? 1 : 0;

	/* tape output */
	m_cassette->output(state ? 1.0 : -1.0);
}

void eti660_state::dma_w(offs_t offset, u8 data)
{
	offset -= 0x480;

	m_color = 7;

	u8 colorram_offset = ((offset & 0x1f0) >> 1) | (offset & 0x07);

	if (colorram_offset < 0xc0)
		m_color = m_color_ram[colorram_offset];

	m_cti->dma_w(data);
}

/* PIA6821 Interface */

u8 eti660_state::pia_pa_r()
{
	/*

	    bit     description

	    PA0     keyboard row 0
	    PA1     keyboard row 1
	    PA2     keyboard row 2
	    PA3     keyboard row 3
	    PA4     keyboard column 0
	    PA5     keyboard column 1
	    PA6     keyboard column 2
	    PA7     keyboard column 3

	*/

	u8 i, data = 0xff;

	for (i = 0; i < 4; i++)
		if (BIT(m_keylatch, i))
			return m_io_keyboard[i]->read();

	return data;
}

void eti660_state::pia_pa_w(u8 data)
{
	/*

	    bit     description

	    PA0     keyboard row 0
	    PA1     keyboard row 1
	    PA2     keyboard row 2
	    PA3     keyboard row 3
	    PA4     keyboard column 0
	    PA5     keyboard column 1
	    PA6     keyboard column 2
	    PA7     keyboard column 3

	*/

	m_keylatch = bitswap<8>(data,0,1,2,3,4,5,6,7) ^ 0xff;
}

void eti660_state::machine_reset()
{
	m_resetcnt = 0;
	m_maincpu->reset();  // needed
}

void eti660_state::machine_start()
{
	m_leds.resolve();

	save_item(NAME(m_color_ram));
	save_item(NAME(m_color));
	save_item(NAME(m_keylatch));
	save_item(NAME(m_resetcnt));
}

QUICKLOAD_LOAD_MEMBER(eti660_state::quickload_cb)
{
	int const quick_length = image.length();
	std::vector<u8> quick_data;
	quick_data.resize(quick_length);
	int const read_ = image.fread( &quick_data[0], quick_length);
	if (read_ != quick_length)
		return std::make_pair(image_error::INVALIDIMAGE, "Cannot read the file");

	constexpr int QUICK_ADDR = 0x600;
	address_space &space = m_maincpu->space(AS_PROGRAM);
	for (int i = 0; i < quick_length; i++)
	{
		if ((QUICK_ADDR + i) < 0x1000)
			space.write_byte(i + QUICK_ADDR, quick_data[i]);
	}

	// display a message about the loaded quickload
	if (image.is_filetype("bin"))
		image.message(" Quickload: size=%04X : start=%04X : end=%04X : Press 6 to start", quick_length, QUICK_ADDR, QUICK_ADDR+quick_length);
	else
		image.message(" Quickload: size=%04X : start=%04X : end=%04X : Press 8 to start", quick_length, QUICK_ADDR, QUICK_ADDR+quick_length);

	return std::make_pair(std::error_condition(), std::string());
}

/* Machine Drivers */

void eti660_state::eti660(machine_config &config)
{
	/* basic machine hardware */
	CDP1802(config, m_maincpu, XTAL(8'867'238)/5);
	m_maincpu->set_addrmap(AS_PROGRAM, &eti660_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &eti660_state::io_map);
	m_maincpu->wait_cb().set_constant(1);
	m_maincpu->clear_cb().set(FUNC(eti660_state::clear_r));
	m_maincpu->ef2_cb().set(FUNC(eti660_state::ef2_r));
	m_maincpu->ef4_cb().set(FUNC(eti660_state::ef4_r));
	m_maincpu->q_cb().set(FUNC(eti660_state::q_w));
	m_maincpu->dma_wr_cb().set(FUNC(eti660_state::dma_w));

	/* video hardware */
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	CDP1864(config, m_cti, XTAL(8'867'238)/5).set_screen("screen");
	m_cti->inlace_cb().set_constant(0);
	m_cti->int_cb().set_inputline(m_maincpu, COSMAC_INPUT_LINE_INT);
	m_cti->dma_out_cb().set_inputline(m_maincpu, COSMAC_INPUT_LINE_DMAOUT);
	m_cti->efx_cb().set_inputline(m_maincpu, COSMAC_INPUT_LINE_EF1);
	m_cti->rdata_cb().set([this] () { return BIT(m_color, 0); });
	m_cti->bdata_cb().set([this] () { return BIT(m_color, 1); });
	m_cti->gdata_cb().set([this] () { return BIT(m_color, 2); });
	m_cti->set_chrominance(RES_K(2.2), RES_K(1), RES_K(4.7), RES_K(4.7)); // R7, R5, R6, R4
	m_cti->add_route(ALL_OUTPUTS, "mono", 0.25);

	/* devices */
	PIA6821(config, m_pia);
	m_pia->readpa_handler().set(FUNC(eti660_state::pia_pa_r));
	m_pia->writepa_handler().set(FUNC(eti660_state::pia_pa_w));
	m_pia->ca2_handler().set(FUNC(eti660_state::ca2_w));  // not working, bug in pia
	m_pia->irqa_handler().set_inputline(m_maincpu, COSMAC_INPUT_LINE_INT).invert(); // FIXME: use an input merger for these lines
	m_pia->irqb_handler().set_inputline(m_maincpu, COSMAC_INPUT_LINE_INT).invert();

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("3K");

	/* quickload */
	quickload_image_device &quickload(QUICKLOAD(config, "quickload", "bin,c8,ch8", attotime::from_seconds(2)));
	quickload.set_load_callback(FUNC(eti660_state::quickload_cb));
	quickload.set_interface("etiquik");
	SOFTWARE_LIST(config, "quik_list").set_original("eti660_quik");
}

/* ROMs */

ROM_START( eti660 )
	ROM_REGION( 0x10000, CDP1802_TAG, 0 )
	ROM_LOAD( "eti660.bin", 0x0000, 0x0400, CRC(811dfa62) SHA1(c0c4951e02f873f15560bdc3f35cdf3f99653922) )
ROM_END

} // anonymous namespace


//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY                            FULLNAME   FLAGS
COMP( 1981, eti660, 0,      0,      eti660,  eti660, eti660_state, empty_init, "Electronics Today International", "ETI-660 Learners' Microcomputer", MACHINE_SUPPORTS_SAVE )



eurit.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Ascom Eurit Euro-ISDN Telefon.

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

#include "emu.h"

#include "cpu/m37710/m37710.h"
#include "machine/am79c30.h"
#include "video/hd44780.h"

#include "emupal.h"
#include "screen.h"


namespace {

class eurit_state : public driver_device
{
public:
	eurit_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_keys(*this, "KEY%c", 'A')
		, m_key_scan(0x1f)
	{
	}

	void eurit30(machine_config &mconfig);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	HD44780_PIXEL_UPDATE(lcd_pixel_update);

	void key_scan_w(u8 data);
	u8 key_matrix_r();

	void mem_map(address_map &map) ATTR_COLD;

	void palette_init(palette_device &palette);

	required_device<m37730s2_device> m_maincpu;
	required_ioport_array<5> m_keys;

	u8 m_key_scan;
};

void eurit_state::machine_start()
{
	save_item(NAME(m_key_scan));
}

HD44780_PIXEL_UPDATE(eurit_state::lcd_pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 20)
		bitmap.pix(line * 8 + y, pos * 6 + x) = state;
}


void eurit_state::key_scan_w(u8 data)
{
	m_key_scan = data >> 3;
}

u8 eurit_state::key_matrix_r()
{
	u8 ret = 0xff;
	for (int i = 0; i < 5; i++)
		if (!BIT(m_key_scan, i))
			ret &= m_keys[i]->read();

	return ret;
}


void eurit_state::mem_map(address_map &map)
{
	map(0x000480, 0x007fff).ram(); // probably NVRAM (0x000480 counts seconds for software RTC)
	map(0x008000, 0x00ffff).rom().region("firmware", 0x8000);
	map(0x040000, 0x05ffff).rom().region("firmware", 0);
	map(0x0c0000, 0x0c0007).rw("dsc", FUNC(am79c30a_device::read), FUNC(am79c30a_device::write));
}


static INPUT_PORTS_START(eurit30)
	PORT_START("KEYA")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Unknown Key A0")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Unknown Key A1")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("#") PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("0  0...9") PORT_CODE(KEYCODE_0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("*") PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("ESC") PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Unknown Key A7") // phone off hook?

	PORT_START("KEYB")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Z2") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Menu") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("9  YZ") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("8  VWX") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("7  STU") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Unknown Key B5")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("MIC") PORT_CODE(KEYCODE_M)

	PORT_START("KEYC")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Z6") PORT_CODE(KEYCODE_N)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Z3") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("6  PQR") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("5  MNO") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("4  JKL") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Unknown Key C5")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("+") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Unknown Key C7")

	PORT_START("KEYD")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Z4") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Z5") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("3  GHI") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("2  DEF") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("1  ABC") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Unknown Key D5")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME(u8"\u2192") PORT_CODE(KEYCODE_RIGHT) // →
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME(u8"\u2190") PORT_CODE(KEYCODE_LEFT) // ←

	PORT_START("KEYE")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Fox Key Center Right") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Fox Key Next to Left") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("-") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Fox Key Next to Right") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Fox Key Right") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Fox Key Center Left") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Fox Key Left") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Z1") PORT_CODE(KEYCODE_Z)
INPUT_PORTS_END

void eurit_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(131, 136, 139));
	palette.set_pen_color(1, rgb_t( 92,  83,  88));
}

void eurit_state::eurit30(machine_config &config)
{
	M37730S2(config, m_maincpu, 4'096'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &eurit_state::mem_map);
	m_maincpu->p4_in_cb().set("lcdc", FUNC(hd44780_device::db_r)); // not actually used for input?
	m_maincpu->p4_out_cb().set("lcdc", FUNC(hd44780_device::db_w));
	m_maincpu->p4_out_cb().append(FUNC(eurit_state::key_scan_w));
	m_maincpu->p5_in_cb().set(FUNC(eurit_state::key_matrix_r));
	m_maincpu->p6_out_cb().set("lcdc", FUNC(hd44780_device::e_w)).bit(6);
	m_maincpu->p6_out_cb().append("lcdc", FUNC(hd44780_device::rw_w)).bit(5); // not actually used for read mode?
	m_maincpu->p6_out_cb().append("lcdc", FUNC(hd44780_device::rs_w)).bit(4);

	am79c30a_device &dsc(AM79C30A(config, "dsc", 12'288'000));
	dsc.int_callback().set_inputline(m_maincpu, M37710_LINE_IRQ0);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6*20, 8*2);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(eurit_state::palette_init), 2);

	hd44780_device &lcdc(SED1278(config, "lcdc", 270'000)); // TODO: clock not measured, datasheet typical clock used
	lcdc.set_default_bios_tag("0b");
	lcdc.set_lcd_size(2, 20);
	lcdc.set_pixel_update_cb(FUNC(eurit_state::lcd_pixel_update));
}


ROM_START(eurit30)
	ROM_REGION16_LE(0x20000, "firmware", 0) // Firmware 2.210 deutsch
	ROM_LOAD("d_2.210", 0x00000, 0x20000, CRC(c77be0ac) SHA1(1eaba66dcb4f64cc33565ca85de25341572ddb2e))
ROM_END

} // anonymous namespace


SYST(1996, eurit30, 0, 0, eurit30, eurit30, eurit_state, empty_init, "Ascom", "Eurit 30", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



eurocom2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************

    Eltec Eurocom II V7 single-board computer.  Used in PPG Waveterm A.

    to do:
    - Eurocom board: timer, interrupts
    - Waveterm: PTM, ADC, DAC, 'end' button
    - autoboot Waveterm software without -debug being necessary
    - support more disk image formats (.dsk, flexemu .flx)

    manuals:
    - http://seib.synth.net/documents/EurocomIIHW.pdf
    - http://seib.synth.net/documents/wt_b_serv.pdf
    - http://seib.synth.net/documents/wt_a_usrd.pdf
    - http://seib.synth.net/documents/wavetermbroc.pdf

    useful links:
    - http://www.ppg.synth.net/waveterm/
    - http://www.hermannseib.com/english/synths/ppg/waveterm.htm
    - http://www.synthmuseum.com/ppg/ppgwterm01.html
    - http://www.theppgs.com/waveterma.html
    - http://machines.hyperreal.org/manufacturers/PPG/info/ppg.waveterm.revisions.txt
    - http://www.flexusergroup.com/flexusergroup/default.htm
    - http://web.archive.org/web/20091026234737/http://geocities.com/flexemu/
    - http://oldcomputer.info/8bit/microtrol/index.htm

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

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/m6809/m6809.h"
#include "formats/ppg_dsk.h"
#include "imagedev/floppy.h"
#include "machine/6821pia.h"
#include "machine/6840ptm.h"
#include "machine/6850acia.h"
#include "machine/keyboard.h"
#include "machine/wd_fdc.h"

#include "emupal.h"
#include "softlist_dev.h"
#include "screen.h"


static constexpr int VC_TOTAL_HORZ = 678;
static constexpr int VC_DISP_HORZ = 512;

static constexpr int VC_TOTAL_VERT = 312;
static constexpr int VC_DISP_VERT = 256;


#define LOG_KEYBOARD  (1U << 1)
#define LOG_DEBUG     (1U << 2)

//#define VERBOSE (LOG_DEBUG)
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"

#define LOGKBD(...) LOGMASKED(LOG_KEYBOARD, __VA_ARGS__)
#define LOGDBG(...) LOGMASKED(LOG_DEBUG, __VA_ARGS__)


namespace {

class eurocom2_state : public driver_device
{
public:
	eurocom2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pia1(*this, "pia1")
		, m_pia2(*this, "pia2")
		, m_acia(*this, "acia")
		, m_fdc(*this, "fdc")
		, m_p_videoram(*this, "videoram")
		, m_screen(*this, "screen")
	{ }

	void eurocom2(machine_config &config);
	void microtrol(machine_config &config);

protected:
	// driver_device overrides
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	uint8_t fdc_aux_r(offs_t offset);
	void fdc_aux_w(offs_t offset, uint8_t data);
	static void floppy_formats(format_registration &fr);

	void vico_w(offs_t offset, uint8_t data);

	uint8_t kbd_get();
	void kbd_put(u8 data);

	void pia1_cb2_w(int state);

	void eurocom2_map(address_map &map) ATTR_COLD;

	TIMER_CALLBACK_MEMBER(toggle_sst);

	emu_timer *m_sst = nullptr;

	floppy_image_device *m_floppy = nullptr;
	bool m_sst_state = false;
	bitmap_ind16 m_tmpbmp;

	uint8_t m_vico[2]{};
	uint8_t m_kbd_data = 0;

	required_device<cpu_device> m_maincpu;
	required_device<pia6821_device> m_pia1;
	required_device<pia6821_device> m_pia2;
	required_device<acia6850_device> m_acia;
	required_device<fd1793_device> m_fdc;
	required_shared_ptr<uint8_t> m_p_videoram;
	required_device<screen_device> m_screen;
};

class waveterm_state : public eurocom2_state
{
public:
	waveterm_state(const machine_config &mconfig, device_type type, const char *tag)
		: eurocom2_state(mconfig, type, tag)
		, m_pia3(*this, "pia3")
		, m_ptm(*this, "ptm")
	{ }

	void waveterm(machine_config &config);

private:
	uint8_t waveterm_kb_r();
	void waveterm_kb_w(uint8_t data);
	void waveterm_kbh_w(int state);

	void pia3_pb_w(uint8_t data);
	void pia3_cb2_w(int state);

	uint8_t waveterm_adc();
	void waveterm_dac(uint8_t data); // declared but not defined, commented in memory map

	void waveterm_map(address_map &map) ATTR_COLD;

	bool m_driveh = false;
	uint8_t m_drive = 0;

	required_device<pia6821_device> m_pia3;
	required_device<ptm6840_device> m_ptm;
};


/*
 * b0 -- timer output
 * b1 -- 1 == two-sided diskette in drive
 * b2..b5 nc
 * b6 -- irq
 * b7 -- drq
 */
uint8_t eurocom2_state::fdc_aux_r(offs_t offset)
{
	uint8_t data = 0;

	data |= (m_floppy ? m_floppy->twosid_r() : 1) << 1;
	data |= (m_fdc->intrq_r() << 6);
	data |= (m_fdc->drq_r() << 7);

	LOGDBG("Floppy %d == %02x\n", offset, data);

	return data;
}

/*
 * b0 -- 1 = select 0
 * ..
 * b3 -- 1 = select 3
 * b4 -- 1 = top head
 * b5 -- 1 = single density
 * b6 -- nc
 * b7 -- 1 = enable timer interrupt
 */
void eurocom2_state::fdc_aux_w(offs_t offset, uint8_t data)
{
	floppy_image_device *floppy0 = m_fdc->subdevice<floppy_connector>("0")->get_device();
	floppy_image_device *floppy1 = m_fdc->subdevice<floppy_connector>("1")->get_device();

	if (BIT(data, 0))
		m_floppy = floppy0;
	else if (BIT(data, 1))
		m_floppy = floppy1;
	else
		m_floppy = nullptr;

	if (m_floppy)
	{
		m_fdc->set_floppy(m_floppy);
		m_floppy->ss_w(BIT(data, 4));
		m_floppy->mon_w(0);
	}
	else
	{
		floppy0->mon_w(1);
		floppy1->mon_w(1);
	}

	m_fdc->dden_w(BIT(data, 5));

	LOGDBG("Floppy %d <- %02x\n", offset, data);
}

void eurocom2_state::vico_w(offs_t offset, uint8_t data)
{
	LOG("VICO %d <- %02x\n", offset, data);

	m_vico[offset & 1] = data;
}


void eurocom2_state::pia1_cb2_w(int state)
{
	LOG("PIA1 CB2 <- %d (SST reset)\n", state);
	// reset single-step timer
}

TIMER_CALLBACK_MEMBER(eurocom2_state::toggle_sst)
{
	m_sst_state = !m_sst_state;
	m_pia1->ca2_w(m_sst_state);
	m_pia1->cb1_w(m_sst_state);
}

/* bit 7 may be connected to something else -- see section 6.2 of Eurocom manual */
uint8_t eurocom2_state::kbd_get()
{
	return m_kbd_data;
}

void eurocom2_state::kbd_put(u8 data)
{
	m_kbd_data = data;
	m_pia1->ca1_w(false);
	m_pia1->ca1_w(true);
}


uint8_t waveterm_state::waveterm_kb_r()
{
	uint8_t data = 0xff;

	if (BIT(m_drive, 0)) data &= ioport("ROW.0")->read();
	if (BIT(m_drive, 1)) data &= ioport("ROW.1")->read();
	if (BIT(m_drive, 2)) data &= ioport("ROW.2")->read();
	if (BIT(m_drive, 3)) data &= ioport("ROW.3")->read();
	if (m_driveh)        data &= ioport("ROW.4")->read();

	return data;
}

void waveterm_state::waveterm_kb_w(uint8_t data)
{
	m_drive = (~data) >> 4;
}

void waveterm_state::waveterm_kbh_w(int state)
{
	m_driveh = !state;
}

void waveterm_state::pia3_pb_w(uint8_t data)
{
}

uint8_t waveterm_state::waveterm_adc()
{
	return m_screen->frame_number() % 255; // FIXME
}


uint32_t eurocom2_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	int const page = (m_vico[0] & 3) << 14;

	for (int y = 0; y < VC_DISP_VERT; y++)
	{
		int const offset = (VC_DISP_HORZ / 8) * ((m_vico[1] + y) % VC_DISP_VERT);
		uint16_t *p = &m_tmpbmp.pix(y);

		for (int x = offset; x < offset + VC_DISP_HORZ / 8; x++)
		{
			uint16_t const gfx = m_p_videoram[page + x];

			for (int i = 7; i >= 0; i--)
			{
				*p++ = BIT(gfx, i);
			}
		}
	}

	copybitmap(bitmap, m_tmpbmp, 0, 0, 0, 0, cliprect);

	return 0;
}

void eurocom2_state::eurocom2_map(address_map &map)
{
	map(0x0000, 0xefff).ram().share("videoram");
	map(0xf000, 0xfcef).rom().region("maincpu", 0);
	map(0xfcf0, 0xfcf3).rw(m_pia1, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xfcf4, 0xfcf5).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xfcf6, 0xfcf7).w(FUNC(eurocom2_state::vico_w));
	map(0xfcf8, 0xfcfb).rw(m_pia2, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xfd30, 0xfd37).rw(m_fdc, FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	map(0xfd38, 0xfd38).rw(FUNC(eurocom2_state::fdc_aux_r), FUNC(eurocom2_state::fdc_aux_w));
	map(0xfd40, 0xffff).rom().region("maincpu", 0xd40).nopw();
}

void waveterm_state::waveterm_map(address_map &map)
{
	eurocom2_map(map);
	map(0xfd00, 0xfd03).rw(m_pia3, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xfd08, 0xfd0f).rw(m_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write));
	map(0xfd10, 0xfd17).unmaprw();
	map(0xfd18, 0xfd18).r(FUNC(waveterm_state::waveterm_adc));  //  AD558 ADC
//  map(0xfd20, 0xfd20).r(FUNC(waveterm_state::waveterm_dac));  //  ZN432 DAC ??
}

static INPUT_PORTS_START(eurocom2)
	PORT_START("S1")
	PORT_DIPNAME(0x0f, 0x01, "Serial baud rate")
	PORT_DIPSETTING(0x01, "9600")

	PORT_DIPNAME(0x40, 0x40, "7 or 8-bit keyboard")
	PORT_DIPSETTING(0x40, "8-bit")
	PORT_DIPSETTING(0x00, "7-bit")

	PORT_DIPNAME(0x80, 0x80, "Periodic timer")
	PORT_DIPSETTING(0x80, DEF_STR(Yes))
	PORT_DIPSETTING(0x00, DEF_STR(No))
INPUT_PORTS_END

static INPUT_PORTS_START(waveterm)
	PORT_INCLUDE(eurocom2)

	PORT_START("FP")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("End") PORT_CODE(KEYCODE_PRTSCR) PORT_CHAR(UCHAR_MAMEKEY(PRTSCR))

	PORT_START("ROW.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))

	PORT_START("ROW.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F5") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F6") PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F7") PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F8") PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))

	PORT_START("ROW.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F9") PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F0") PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Num 1") PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Num 2") PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))

	PORT_START("ROW.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Num 3") PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Num 4") PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Num 5") PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Num 6") PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD))

	PORT_START("ROW.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Num 7") PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Num 8") PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Num 9") PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Num 0") PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
INPUT_PORTS_END


void eurocom2_state::machine_reset()
{
	m_pia1->ca1_w(false);
	m_floppy = nullptr;

	if (ioport("S1")->read() & 0x80)
		m_sst->adjust(attotime::from_usec(12200), 0, attotime::from_usec(12200));
	else
		m_sst->adjust(attotime::never, 0, attotime::never);
}

void eurocom2_state::machine_start()
{
	m_sst = timer_alloc(FUNC(eurocom2_state::toggle_sst), this);
	m_tmpbmp.allocate(VC_DISP_HORZ, VC_DISP_VERT);
	m_kbd_data = 0;
}


void eurocom2_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_PPG_FORMAT);
}

static void eurocom_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
	device.option_add("8dsdd", FLOPPY_8_DSDD);
}

void eurocom2_state::eurocom2(machine_config &config)
{
	MC6809(config, m_maincpu, 10.7172_MHz_XTAL / 2); // EXTAL = CLK/2 = 5.3586 MHz; Q = E = 1.33965 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &eurocom2_state::eurocom2_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER, rgb_t::green());
	m_screen->set_raw(10.7172_MHz_XTAL, VC_TOTAL_HORZ, 0, VC_DISP_HORZ, VC_TOTAL_VERT, 0, VC_DISP_VERT);
	m_screen->set_screen_update(FUNC(eurocom2_state::screen_update));
	m_screen->set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(eurocom2_state::kbd_put));

	PIA6821(config, m_pia1);
	m_pia1->ca2_w(m_sst_state); // SST output Q14
	m_pia1->cb1_w(m_sst_state); // SST output Q6
	m_pia1->cb2_handler().set(FUNC(eurocom2_state::pia1_cb2_w)); // SST reset input
	m_pia1->readpa_handler().set(FUNC(eurocom2_state::kbd_get));
//  m_pia1->readpb_handler().set(FUNC(eurocom2_state::kbd_get));
//  m_pia1->irqa_handler().set_inputline("maincpu", M6809_IRQ_LINE);
//  m_pia1->irqb_handler().set_inputline("maincpu", M6809_IRQ_LINE);

	PIA6821(config, m_pia2);
//  m_pia2->irqa_handler().set_inputline("maincpu", M6809_FIRQ_LINE);
//  m_pia2->irqb_handler().set_inputline("maincpu", M6809_FIRQ_LINE);

	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_acia->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_acia, FUNC(acia6850_device::write_rxd));
	rs232.cts_handler().set(m_acia, FUNC(acia6850_device::write_cts));

	FD1793(config, m_fdc, 2_MHz_XTAL / 2);
//  m_fdc->intrq_wr_callback().set_inputline(m_maincpu, M6809_IRQ_LINE);
	FLOPPY_CONNECTOR(config, "fdc:0", eurocom_floppies, "525qd", eurocom2_state::floppy_formats);// enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", eurocom_floppies, "525qd", eurocom2_state::floppy_formats);// enable_sound(true);
}

void waveterm_state::waveterm(machine_config &config)
{
	eurocom2(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &waveterm_state::waveterm_map);

	m_pia2->cb2_handler().set(FUNC(waveterm_state::waveterm_kbh_w));
	m_pia2->writepb_handler().set(FUNC(waveterm_state::waveterm_kb_w));
	m_pia2->readpb_handler().set(FUNC(waveterm_state::waveterm_kb_r));

	// ports A(in/out), B(out), CA1(in), CA2(in), and CB2(out) = interface to PPG bus via DIL socket on WTI board
	// CB1 -- front panel "End" button
	PIA6821(config, m_pia3);
//  m_pia3->readpa_handler().set(FUNC(waveterm_state::pia3_pa_r));
//  m_pia3->writepa_handler().set(FUNC(waveterm_state::pia3_pa_w));
	m_pia3->writepb_handler().set(FUNC(waveterm_state::pia3_pb_w));
	m_pia3->readcb1_handler().set_ioport("FP");
//  m_pia3->cb2_handler().set(FUNC(waveterm_state::pia3_cb2_w));

	PTM6840(config, m_ptm, 0);

	SOFTWARE_LIST(config, "disk_list").set_original("waveterm");
}

void eurocom2_state::microtrol(machine_config &config)
{
	eurocom2(config);

	// TODO: Second board has WD2793A FDC and what looks like a RAM disk
}


ROM_START(eurocom2)
	ROM_REGION(0x1000, "maincpu", 0)

	ROM_DEFAULT_BIOS("mon54")
	ROM_SYSTEM_BIOS(0, "mon24", "Eurocom Control V2.4")
	ROMX_LOAD("mon24.bin", 0x0000, 0x1000, CRC(abf5e115) SHA1(d056705779e109bb56c82f906e2e5a52efe77ec1), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "mon53", "Eurocom Control V5.3")
	ROMX_LOAD("mon53.bin", 0x0000, 0x1000, CRC(fb39c2ad) SHA1(8ce07c349c56f92503f11bb63e32e32c139c003a), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "mon54", "Eurocom Control V5.4")
	ROMX_LOAD("mon54.bin", 0x0000, 0x1000, CRC(2c5a4ad2) SHA1(67b9deec5a6a71d768e35ac97c16cb8992ae159f), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "monu546", "Eurocom Control U5.4")
	ROMX_LOAD("monu54-6.bin", 0x0000, 0x1000, CRC(80c82fa8) SHA1(7255bc2dd536d3dd08cca3ea46992e5ca59323b1), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "neumon54", "New Monitor 5.4")
	ROMX_LOAD("neumon54.bin", 0x0000, 0x1000, CRC(2b60ca41) SHA1(c7252d2e9b267b046f4f3ea6cd77e40d4744a33e), ROM_BIOS(4))
ROM_END

ROM_START(waveterm)
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("rom.bin", 0x0000, 0x1000, CRC(add3c20f) SHA1(4d47d99231bff2209634e6aac5710e782ee2f6da))
ROM_END

ROM_START(microtrol)
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("mon1.bin", 0x0000, 0x0800, CRC(4e82af0f) SHA1(a708f0c8a4d7ab216bc065e82a4ad42009cc3696)) // "microtrol Control V5.1"
	ROM_LOAD("mon2.bin", 0x0800, 0x0800, CRC(577a2b4c) SHA1(e7097a96417fa249a62c967039f039e637079cb6))
ROM_END

} // Anonymous namespace


//    YEAR  NAME       PARENT    COMPAT  MACHINE    INPUT     CLASS           INIT        COMPANY      FULLNAME                     FLAGS
COMP( 1981, eurocom2,  0,        0,      eurocom2,  eurocom2, eurocom2_state, empty_init, "Eltec",     "Eurocom II V7",             MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1982, waveterm,  eurocom2, 0,      waveterm,  waveterm, waveterm_state, empty_init, "PPG",       "Waveterm A",                MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1985, microtrol, eurocom2, 0,      microtrol, eurocom2, eurocom2_state, empty_init, "Microtrol", "unknown Microtrol portable computer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



eurocube.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/***************************************************************************

    Control Universal EuroCUBE

    http://chrisacorns.computinghistory.org.uk/8bit_Upgrades/CU_EuroBeeb.html

    TODO:
    - add configuration to run at either 1MHz or 2MHz.

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

#include "emu.h"

#include "bus/acorn/bus.h"
#include "bus/rs232/rs232.h"
#include "cpu/m6502/m6502.h"
#include "cpu/m6809/m6809.h"
#include "machine/6522via.h"
#include "machine/input_merger.h"
#include "machine/m3002.h"
#include "machine/mos6551.h"

#include "softlist_dev.h"


namespace {

class eurocube_state : public driver_device
{
public:
	eurocube_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_view(*this, "view")
		, m_bus(*this, "bus")
		, m_map(*this, "map")
	{ }

	void eurocube65(machine_config &config);
	void eurocube09(machine_config &config);
	void eurobeeb2(machine_config &config);
	void eurobeeb3m(machine_config &config);
	void eurobeeb3c(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	memory_view m_view;
	required_device<acorn_bus_device> m_bus;
	required_ioport m_map;

	void eurocube65_mem(address_map &map) ATTR_COLD;
	void eurocube09_mem(address_map &map) ATTR_COLD;
	void eurocube_map(address_map &map) ATTR_COLD;
};


void eurocube_state::eurocube65_mem(address_map &map)
{
	map(0x0000, 0xffff).m(FUNC(eurocube_state::eurocube_map));
	map(0xfe00, 0xfe0f).rw("via", FUNC(via6522_device::read), FUNC(via6522_device::write));
	map(0xfe10, 0xfe17).rw("acia", FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0xfe18, 0xfe1f).rw("rtc", FUNC(m3002_device::read), FUNC(m3002_device::write));
}


void eurocube_state::eurocube09_mem(address_map &map)
{
	map(0x0000, 0xffff).m(FUNC(eurocube_state::eurocube_map));
	map(0xee00, 0xee0f).rw("via", FUNC(via6522_device::read), FUNC(via6522_device::write));
	map(0xee10, 0xee17).rw("acia", FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0xee18, 0xee1f).rw("rtc", FUNC(m3002_device::read), FUNC(m3002_device::write));
}


void eurocube_state::eurocube_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).view(m_view);

	// Map 0 - Development BASIC
	m_view[0](0x0000, 0x1fff).ram();                   // M3
	m_view[0](0x2000, 0x3fff).rom().region("m2", 0);   // M2
	m_view[0](0x8000, 0xbfff).rom().region("m1", 0);   // M1
	m_view[0](0xc000, 0xffff).rom().region("m0", 0);   // M0
	m_view[0](0xd000, 0xdfff).lrw8(                    // I/O Block
		NAME([this](offs_t offset) { return m_bus->read(offset | 0xd000); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0xd000, data); })
	);
	m_view[0](0xfe00, 0xfeff).lrw8(                    // I/O Page
		NAME([this](offs_t offset) { return m_bus->read(offset | 0xfe00); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0xfe00, data); })
	);

	// Map 1 - EPROM BASIC
	m_view[1](0x0000, 0x3fff).ram();                   // M3
	m_view[1](0x4000, 0x7fff).rom().region("m2", 0);   // M2
	m_view[1](0x8000, 0xbfff).rom().region("m1", 0);   // M1
	m_view[1](0xc000, 0xffff).rom().region("m0", 0);   // M0
	m_view[1](0xd000, 0xdfff).lrw8(                    // I/O Block
		NAME([this](offs_t offset) { return m_bus->read(offset | 0xd000); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0xd000, data); })
	);
	m_view[1](0xfe00, 0xfeff).lrw8(                    // I/O Page
		NAME([this](offs_t offset) { return m_bus->read(offset | 0xfe00); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0xfe00, data); })
	);

	// Map 2 - Minimum BASIC
	m_view[2](0x0000, 0x07ff).ram();                   // M3
	m_view[2](0x0800, 0x0fff).ram();                   // M2
	m_view[2](0x8000, 0xbfff).rom().region("m1", 0);   // M1
	m_view[2](0xc000, 0xffff).rom().region("m0", 0);   // M0
	m_view[2](0xd000, 0xdfff).lrw8(                    // I/O Block
		NAME([this](offs_t offset) { return m_bus->read(offset | 0xd000); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0xd000, data); })
	);
	m_view[2](0xfe00, 0xfeff).lrw8(                    // I/O Page
		NAME([this](offs_t offset) { return m_bus->read(offset | 0xfe00); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0xfe00, data); })
	);

	// Map 3 - Reserved for future use by Control Universal Ltd
	m_view[3](0x0000, 0xffff).noprw();

	// Map 4 - FLEX, low cost (6809 only)
	m_view[4](0x0000, 0x1fff).ram();                   // M3
	m_view[4](0x2000, 0x3fff).ram();                   // M2
	m_view[4](0xe000, 0xffff).rom().region("m0", 0);   // M0
	m_view[4](0xe000, 0xe7ff).ram();                   // M1
	//m_view[4](0xe000, 0xefff).lrw8(                    // I/O Block
	//  NAME([this](offs_t offset) { return m_bus->read(offset | 0xe800); }),
	//  NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0xe800, data); })
	//);
	m_view[4](0xee00, 0xeeff).lrw8(                    // I/O Page
		NAME([this](offs_t offset) { return m_bus->read(offset | 0xee00); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0xee00, data); })
	);

	// Map 5 - FLEX (6809 only)
	m_view[5](0x0000, 0x3fff).ram();                   // M3
	m_view[5](0x4000, 0x7fff).ram();                   // M2
	m_view[5](0xe000, 0xffff).rom().region("m0", 0);   // M0
	m_view[5](0xe000, 0xe7ff).ram();                   // M1
	//m_view[5](0xe000, 0xefff).lrw8(                    // I/O Block
	//  NAME([this](offs_t offset) { return m_bus->read(offset | 0xe800); }),
	//  NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0xe800, data); })
	//);
	m_view[5](0xee00, 0xeeff).lrw8(                    // I/O Page
		NAME([this](offs_t offset) { return m_bus->read(offset | 0xee00); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0xee00, data); })
	);

	// Map 6 - Reserved for future use by Control Universal Ltd
	m_view[6](0x0000, 0xffff).noprw();

	// Map 7 - Reserved for future use by Control Universal Ltd
	m_view[7](0x0000, 0xffff).noprw();

	// Map 8 - ATOM BASIC
	m_view[8](0x0000, 0x07ff).ram();                   // M3
	m_view[8](0xa000, 0xbfff).ram();                   // M2
	m_view[8](0xc000, 0xdfff).rom().region("m1", 0);   // M1
	m_view[8](0xe000, 0xffff).rom().region("m0", 0);   // M0
	m_view[8](0x0800, 0x0fff).lrw8(                    // I/O Block
		NAME([this](offs_t offset) { return m_bus->read(offset | 0x0800); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0x0800, data); })
	);
	m_view[8](0xfe00, 0xfeff).lrw8(                    // I/O Page
		NAME([this](offs_t offset) { return m_bus->read(offset | 0xfe00); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0xfe00, data); })
	);

	// Map 9 - Low-cost EPROM
	m_view[9](0x0000, 0x3fff).ram();                   // M3
	m_view[9](0xd000, 0xdfff).ram();                   // M2
	m_view[9](0xe000, 0xefff).rom().region("m1", 0);   // M1
	m_view[9](0xf000, 0xffff).rom().region("m0", 0);   // M0
	m_view[9](0x7000, 0x7fff).lrw8(                    // I/O Block
		NAME([this](offs_t offset) { return m_bus->read(offset | 0x7000); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0x7000, data); })
	);
	m_view[9](0xfe00, 0xfeff).lrw8(                    // I/O Page
		NAME([this](offs_t offset) { return m_bus->read(offset | 0xfe00); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0xfe00, data); })
	);

	// Map 10 - ROM intensive
	m_view[10](0x0000, 0x3fff).ram();                  // M3
	m_view[10](0x4000, 0x7fff).rom().region("m2", 0);  // M2
	m_view[10](0x8000, 0xbfff).rom().region("m1", 0);  // M1
	m_view[10](0xc000, 0xffff).rom().region("m0", 0);  // M0
	m_view[10](0xfe00, 0xfeff).lrw8(                   // I/O Page
		NAME([this](offs_t offset) { return m_bus->read(offset | 0xfe00); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0xfe00, data); })
	);

	// Map 11 - Low-cost RAM
	m_view[11](0x0000, 0x07ff).ram();                  // M3
	m_view[11](0x0800, 0x0fff).ram();                  // M2
	m_view[11](0x1000, 0x17ff).ram();                  // M1
	m_view[11](0x8000, 0xffff).rom().region("m0", 0);  // M0
	m_view[11](0xfe00, 0xfeff).lrw8(                   // I/O Page
		NAME([this](offs_t offset) { return m_bus->read(offset | 0xfe00); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0xfe00, data); })
	);

	// Map 12 - RAM intensive
	m_view[12](0x0000, 0x1fff).ram();                  // M3
	m_view[12](0x2000, 0x3fff).ram();                  // M2
	m_view[12](0x4000, 0x5fff).rom().region("m1", 0);  // M1
	m_view[12](0x8000, 0xffff).rom().region("m0", 0);  // M0
	m_view[12](0x7000, 0x7fff).lrw8(                   // I/O Block
		NAME([this](offs_t offset) { return m_bus->read(offset | 0x7000); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0x7000, data); })
	);
	m_view[12](0xfe00, 0xfeff).lrw8(                   // I/O Page
		NAME([this](offs_t offset) { return m_bus->read(offset | 0xfe00); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0xfe00, data); })
	);

	// Map 13 - Minimum configuration
	m_view[13](0x0000, 0x07ff).ram();                  // M3
	m_view[13](0x0800, 0x0fff).ram();                  // M2
	m_view[13](0xe000, 0xefff).rom().region("m1", 0);  // M1
	m_view[13](0xf000, 0xffff).rom().region("m0", 0);  // M0
	m_view[13](0xd000, 0xdfff).lrw8(                   // I/O Block
		NAME([this](offs_t offset) { return m_bus->read(offset | 0xd000); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0xd000, data); })
	);
	m_view[13](0xfe00, 0xfeff).lrw8(                   // I/O Page
		NAME([this](offs_t offset) { return m_bus->read(offset | 0xfe00); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0xfe00, data); })
	);

	// Map 14 - General purpose
	m_view[14](0x0000, 0x1fff).ram();                  // M3
	m_view[14](0x2000, 0x3fff).ram();                  // M2
	m_view[14](0x8000, 0xbfff).rom().region("m1", 0);  // M1
	m_view[14](0xc000, 0xffff).rom().region("m0", 0);  // M0
	m_view[14](0x7000, 0x7fff).lrw8(                   // I/O Block
		NAME([this](offs_t offset) { return m_bus->read(offset | 0x7000); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0x7000, data); })
	);
	m_view[14](0xfe00, 0xfeff).lrw8(                   // I/O Page
		NAME([this](offs_t offset) { return m_bus->read(offset | 0xfe00); }),
		NAME([this](offs_t offset, uint8_t data) { m_bus->write(offset | 0xfe00, data); })
	);

	// Map 15 - Spare for user purposes
	m_view[15](0x0000, 0xffff).noprw();
}


static INPUT_PORTS_START( eurocube65 )
	PORT_START("map")
	PORT_CONFNAME(0x0f, 0x01, "Memory Map")
	PORT_CONFSETTING(0x00, "Development BASIC")
	PORT_CONFSETTING(0x01, "EPROM BASIC")
	PORT_CONFSETTING(0x02, "Minimum BASIC")
	PORT_CONFSETTING(0x09, "Low-cost EPROM")
	PORT_CONFSETTING(0x0a, "ROM intensive")
	PORT_CONFSETTING(0x0b, "Low-cost RAM")
	PORT_CONFSETTING(0x0c, "RAM intensive")
	PORT_CONFSETTING(0x0d, "Minimum configuration")
	PORT_CONFSETTING(0x0e, "General purpose")
INPUT_PORTS_END

static INPUT_PORTS_START( eurocube09 )
	PORT_START("map")
	PORT_CONFNAME(0x0f, 0x05, "Memory Map")
	PORT_CONFSETTING(0x04, "FLEX, low cost 6809 only")
	PORT_CONFSETTING(0x05, "FLEX 6809 only")
INPUT_PORTS_END


void eurocube_state::machine_start()
{
}


void eurocube_state::machine_reset()
{
	m_view.select(m_map->read());
}


static DEVICE_INPUT_DEFAULTS_START(terminal)
	DEVICE_INPUT_DEFAULTS("RS232_RXBAUD", 0xff, RS232_BAUD_9600)
	DEVICE_INPUT_DEFAULTS("RS232_TXBAUD", 0xff, RS232_BAUD_9600)
	DEVICE_INPUT_DEFAULTS("RS232_DATABITS", 0xff, RS232_DATABITS_8)
	DEVICE_INPUT_DEFAULTS("RS232_PARITY", 0xff, RS232_PARITY_NONE)
	DEVICE_INPUT_DEFAULTS("RS232_STOPBITS", 0xff, RS232_STOPBITS_1)
DEVICE_INPUT_DEFAULTS_END


void eurocube_state::eurocube65(machine_config &config)
{
	M6502(config, m_maincpu, 4_MHz_XTAL / 4); // M6502
	m_maincpu->set_addrmap(AS_PROGRAM, &eurocube_state::eurocube65_mem);

	INPUT_MERGER_ANY_HIGH(config, "irqs").output_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	via6522_device &via(MOS6522(config, "via", 4_MHz_XTAL / 4));
	via.irq_handler().set("irqs", FUNC(input_merger_device::in_w<0>));

	mos6551_device &acia(MOS6551(config, "acia", 0));
	acia.set_xtal(1.8432_MHz_XTAL);
	acia.irq_handler().set("irqs", FUNC(input_merger_device::in_w<1>));
	acia.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	acia.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	//acia.dtr_handler().set(m_rs232, FUNC(rs232_port_device::write_dtr));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("acia", FUNC(mos6551_device::write_rxd));
	rs232.dsr_handler().set("acia", FUNC(mos6551_device::write_dsr));
	rs232.cts_handler().set("acia", FUNC(mos6551_device::write_cts));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	M3002(config, "rtc", 32.768_kHz_XTAL);

	// 4 Slot mini-rack
	ACORN_BUS(config, m_bus, 4_MHz_XTAL / 4);
	m_bus->out_irq_callback().set("irqs", FUNC(input_merger_device::in_w<2>));
	m_bus->out_nmi_callback().set_inputline(m_maincpu, M6502_NMI_LINE);
	ACORN_BUS_SLOT(config, "slot1", m_bus, eurocube_bus_devices, nullptr);
	ACORN_BUS_SLOT(config, "slot2", m_bus, eurocube_bus_devices, nullptr);
	ACORN_BUS_SLOT(config, "slot3", m_bus, eurocube_bus_devices, nullptr);

	//SOFTWARE_LIST(config, "flop_list").set_original("eurocube_flop");
	//SOFTWARE_LIST(config, "rom_list").set_original("eurocube_rom");
}

void eurocube_state::eurobeeb2(machine_config &config)
{
	eurocube65(config);

	subdevice<rs232_port_device>("rs232")->set_default_option("keyboard");

	subdevice<acorn_bus_slot_device>("slot1")->set_default_option("teletext");
}

void eurocube_state::eurobeeb3m(machine_config &config)
{
	eurocube65(config);

	subdevice<rs232_port_device>("rs232")->set_default_option("terminal");

	subdevice<acorn_bus_slot_device>("slot1")->set_default_option("cugraphm");
}

void eurocube_state::eurobeeb3c(machine_config &config)
{
	eurocube65(config);

	subdevice<rs232_port_device>("rs232")->set_default_option("terminal");

	subdevice<acorn_bus_slot_device>("slot1")->set_default_option("cugraphc");
}


void eurocube_state::eurocube09(machine_config &config)
{
	MC6809(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &eurocube_state::eurocube09_mem);

	INPUT_MERGER_ANY_HIGH(config, "irqs").output_handler().set_inputline(m_maincpu, M6809_IRQ_LINE);

	via6522_device &via(MOS6522(config, "via", 4_MHz_XTAL / 4));
	via.irq_handler().set("irqs", FUNC(input_merger_device::in_w<0>));

	mos6551_device &acia(MOS6551(config, "acia", 0));
	acia.set_xtal(1.8432_MHz_XTAL);
	acia.irq_handler().set("irqs", FUNC(input_merger_device::in_w<1>));
	acia.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	acia.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	//acia.dtr_handler().set(m_rs232, FUNC(rs232_port_device::write_dtr));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("acia", FUNC(mos6551_device::write_rxd));
	rs232.dsr_handler().set("acia", FUNC(mos6551_device::write_dsr));
	rs232.cts_handler().set("acia", FUNC(mos6551_device::write_cts));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	M3002(config, "rtc", 32.768_kHz_XTAL); // Issue 5 board

	// 4 Slot mini-rack
	ACORN_BUS(config, m_bus, 4_MHz_XTAL / 4);
	m_bus->out_irq_callback().set("irqs", FUNC(input_merger_device::in_w<2>));
	m_bus->out_nmi_callback().set_inputline(m_maincpu, M6809_FIRQ_LINE);
	ACORN_BUS_SLOT(config, "slot1", m_bus, eurocube_bus_devices, nullptr);
	ACORN_BUS_SLOT(config, "slot2", m_bus, eurocube_bus_devices, nullptr);
	ACORN_BUS_SLOT(config, "slot3", m_bus, eurocube_bus_devices, nullptr);

	//SOFTWARE_LIST(config, "flop_list").set_original("eurocube_flop");
}


ROM_START( eurocube65 )
	ROM_REGION(0x8000, "m0", ROMREGION_ERASE00)
	ROM_SYSTEM_BIOS(0, "mosb361ms", "MOSB.3.6-1MS (Monitor)") // 1MHz
	ROMX_LOAD("mosb36-1ms.m0", 0x0000, 0x4000, CRC(538a7a9d) SHA1(afb13540f290b9ab35f410d6e3be8bd8561a974a), ROM_BIOS(0))

	ROM_REGION(0x4000, "m1", ROMREGION_ERASE00)

	ROM_REGION(0x4000, "m2", ROMREGION_ERASE00)
ROM_END

ROM_START( eurobeeb1 )
	ROM_REGION(0x8000, "m0", ROMREGION_ERASE00)
	ROM_SYSTEM_BIOS(0, "mosb361ms", "MOSB.3.6-1MS (Monitor)") // 1MHz
	ROMX_LOAD("mosb36-1ms.m0", 0x0000, 0x4000, CRC(538a7a9d) SHA1(afb13540f290b9ab35f410d6e3be8bd8561a974a), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "mos4c", "C-MOS 1.00-2MSB (Monitor)") // 2MHz
	ROMX_LOAD("mos4c.m0", 0x0000, 0x4000, CRC(075229e6) SHA1(baa22a9aba43b05fba91ccb870eac519303665b3), ROM_BIOS(1))

	ROM_REGION(0x4000, "m1", ROMREGION_ERASE00)
	ROMX_LOAD("basic2.m1", 0x0000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281), ROM_BIOS(0))
	ROMX_LOAD("basic4.m1", 0x0000, 0x4000, CRC(135cd65a) SHA1(1d433252be8e8133d8eaa5855bed0f0a6786e5c1), ROM_BIOS(1))

	ROM_REGION(0x4000, "m2", ROMREGION_ERASE00)
ROM_END

ROM_START( eurobeeb2 )
	ROM_REGION(0x8000, "m0", ROMREGION_ERASE00)
	ROM_SYSTEM_BIOS(0, "mosb3x", "MOSB.3 Oct 84 issue 1 (Teletext)")
	ROMX_LOAD("mosb3t.m0", 0x0000, 0x4000, CRC(9adc03bc) SHA1(15553d53802fddbfdb92f609a4cf8bb73e78380f), ROM_BIOS(0))

	ROM_REGION(0x4000, "m1", ROMREGION_ERASE00)
	ROM_LOAD("basic2.m1", 0x0000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281))

	ROM_REGION(0x4000, "m2", ROMREGION_ERASE00)
ROM_END

ROM_START( eurobeeb3m )
	ROM_REGION(0x8000, "m0", ROMREGION_ERASE00)
	ROM_SYSTEM_BIOS(0, "mosb361c", "MOSB.3.6-1CK53S (CU-Graph/CU-Key53)") // 1MHz
	ROMX_LOAD("mosb3c.m0", 0x0000, 0x4000, CRC(d16f9a4a) SHA1(f64c9ef16aa09b6a8f018d1666560d0e5a283c9b), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "mosb419c", "MOSB.4.19 (CU-Graph/CU-Key99)")
	ROMX_LOAD("mosb419c.m0", 0x0000, 0x4000, CRC(38a3b7b4) SHA1(269f008fa92345c8c82ff0bbeb41a0f74511757a), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "mosb1", "MOSB.1 Dec 83 CU-graph issue 6 ")
	ROMX_LOAD("mosb1.m0", 0x0000, 0x2000, CRC(d4dc55fb) SHA1(0155b78353ebe481a7f0cf08eecc069cee48cf3b), ROM_BIOS(2))
	ROM_RELOAD(0x2000, 0x2000)
	ROM_SYSTEM_BIOS(3, "mosb400c", "Map1:MOSB.4.00 (CU-Graph/CU-Key99)")
	ROMX_LOAD("mosb400c.m0", 0x0000, 0x4000, CRC(6776dcd4) SHA1(cb620efd02f8a4095f0968ead5b7a625431e1ccb), ROM_BIOS(3))

	ROM_REGION(0x4000, "m1", ROMREGION_ERASE00)
	ROM_LOAD("basic2.m1", 0x0000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281))
	ROMX_LOAD("basic4.m1", 0x0000, 0x4000, CRC(135cd65a) SHA1(1d433252be8e8133d8eaa5855bed0f0a6786e5c1), ROM_BIOS(3))

	ROM_REGION(0x4000, "m2", ROMREGION_ERASE00)
	ROMX_LOAD("2287sl-iss-d.m2", 0x0000, 0x4000, CRC(3db9e748) SHA1(96941aeaac5115363218a6b1fd71e6eddf3e71d0), ROM_BIOS(3))
ROM_END

#define rom_eurobeeb3c rom_eurobeeb3m

ROM_START( eurocube09 )
	ROM_REGION(0x8000, "m0", ROMREGION_ERASE00)
	ROM_SYSTEM_BIOS(0, "mosf31", "MOSF.3:1") // 1MHz
	ROMX_LOAD("mosf31.m0", 0x0000, 0x2000, CRC(c9b5c6b5) SHA1(c520b6271d098ac0c3c5aa36baa985030bb0bc76), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "mosf24", "MOSF.2:4")
	ROMX_LOAD("mosf24.m0", 0x0000, 0x2000, CRC(76b4d5a7) SHA1(61341d37d62b4810896f4cf74561cdc09cd8eba8), ROM_BIOS(1))

	ROM_REGION(0x4000, "m1", ROMREGION_ERASE00)

	ROM_REGION(0x4000, "m2", ROMREGION_ERASE00)
ROM_END

} // anonymous namespace


//    YEAR  NAME         PARENT       COMPAT  MACHINE      INPUT        CLASS            INIT         COMPANY               FULLNAME                             FLAGS
COMP( 1982, eurocube65,  0,           0,      eurocube65,  eurocube65,  eurocube_state,  empty_init,  "Control Universal",  "EuroCUBE-65",                       MACHINE_NO_SOUND_HW )
COMP( 1984, eurobeeb1,   eurocube65,  0,      eurocube65,  eurocube65,  eurocube_state,  empty_init,  "Control Universal",  "EuroBEEB-1",                        MACHINE_NO_SOUND_HW )
COMP( 1984, eurobeeb2,   eurocube65,  0,      eurobeeb2,   eurocube65,  eurocube_state,  empty_init,  "Control Universal",  "EuroBEEB-2 (Teletext)",             MACHINE_NO_SOUND_HW )
COMP( 1984, eurobeeb3m,  eurocube65,  0,      eurobeeb3m,  eurocube65,  eurocube_state,  empty_init,  "Control Universal",  "EuroBEEB-3M (CU-Graph monochrome)", MACHINE_NO_SOUND_HW )
COMP( 1984, eurobeeb3c,  eurocube65,  0,      eurobeeb3c,  eurocube65,  eurocube_state,  empty_init,  "Control Universal",  "EuroBEEB-3C (CU-Graph colour)",     MACHINE_NO_SOUND_HW )
COMP( 1983, eurocube09,  0,           0,      eurocube09,  eurocube09,  eurocube_state,  empty_init,  "Control Universal",  "EuroCUBE-09",                       MACHINE_NO_SOUND_HW )



europa.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Mephisto Europa (aka Schachschule)

Single-chip chess computer, the chess engine is by Frans Morsch.

NOTE: Before exiting MAME, press the STOP button to turn the power off. Otherwise,
NVRAM won't save properly.

Hardware notes:
- PCB label: 957&958-2, europa.H+G
- Hitachi HD63B01Y0F, 8MHz resonator
- 8*8 chessboard buttons, 24 LEDs, piezo

The E62 MCU was used in:
- Mephisto Europa
- Mephisto Europa A
- Mephisto Marco Polo
- Mephisto Manhattan (suspected)

In the early 90s, it was also reproduced in Ukraine as Chess Computer-1 (ШК-1).
It has a typical yellow-brown PCB and Soviet discrete components, and they
sourced the official Mephisto MCU with the same E62 mask ROM serial.

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

#include "emu.h"

#include "cpu/m6800/m6801.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "mephisto_europa.lh"


namespace {

class europa_state : public driver_device
{
public:
	europa_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void europa(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(on_button);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<hd6301y0_cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<3> m_inputs;

	u16 m_inp_mux = 0;

	// I/O handlers
	template <int N> void leds_w(u8 data);
	void control_w(u8 data);
	u8 input_r();
	void board_w(u8 data);
};

void europa_state::machine_start()
{
	save_item(NAME(m_inp_mux));
}



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

INPUT_CHANGED_MEMBER(europa_state::on_button)
{
	if (newval && m_maincpu->standby())
		m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
}

template <int N>
void europa_state::leds_w(u8 data)
{
	// P10-P17, P30-P37, P40-P47: leds (direct)
	m_display->write_row(N, ~data);
}

void europa_state::control_w(u8 data)
{
	// P20,P21,P24: input mux (buttons)
	m_inp_mux = (m_inp_mux & 0xff) | bitswap<3>(~data,4,1,0) << 8;

	// P23: speaker out
	m_dac->write(BIT(data, 3));
}

u8 europa_state::input_r()
{
	// P50-P57: multiplexed inputs
	u8 data = 0;

	// read buttons
	for (int i = 0; i < 3; i++)
		if (BIT(m_inp_mux, i + 8))
			data |= m_inputs[i]->read() & 0x3f;

	// read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_file(i);

	return ~data;
}

void europa_state::board_w(u8 data)
{
	// P60-P67: input mux (chessboard)
	m_inp_mux = (m_inp_mux & 0x700) | (data ^ 0xff);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( europa )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("PLAY")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("POS")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("MEM")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("ENT")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("White / Black")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("LEV")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("King")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_N) PORT_NAME("RES")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("STOP")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("BOOK")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("INFO")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("HELP")

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(europa_state::on_button), 0) PORT_NAME("ON")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void europa_state::europa(machine_config &config)
{
	// basic machine hardware
	HD6301Y0(config, m_maincpu, 8_MHz_XTAL);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(hd6301y0_cpu_device::nvram_set_battery));
	m_maincpu->standby_cb().append([this](int state) { if (state) m_display->clear(); });
	m_maincpu->out_p1_cb().set(FUNC(europa_state::leds_w<0>));
	m_maincpu->out_p3_cb().set(FUNC(europa_state::leds_w<1>));
	m_maincpu->out_p4_cb().set(FUNC(europa_state::leds_w<2>));
	m_maincpu->out_p2_cb().set(FUNC(europa_state::control_w));
	m_maincpu->in_p5_cb().set(FUNC(europa_state::input_r));
	m_maincpu->out_p6_cb().set(FUNC(europa_state::board_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(3, 8);
	config.set_default_layout(layout_mephisto_europa);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( europa )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("mephisto_hg-morsch_63b01y0e62f", 0x0000, 0x4000, CRC(eaa11f82) SHA1(95fae8dc063e4e4730d08fa894bea61d49ad39d4) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1988, europa, 0,      0,      europa,  europa, europa_state, empty_init, "Hegener + Glaser", "Mephisto Europa", MACHINE_SUPPORTS_SAVE )



europc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
// comments and BIOS versions:rfka01
/*****************************************************************************************************
*
* Schneider Rundfunkwerke AG Euro PC and Euro PC II driver
*
* Manuals and BIOS files: ftp://ftp.cpcszene.de/pub/Computer/Schneider_PC/EuroPC_XT/ (down), mirror at https://lanowski.de/mirrors/
*
* Euro PC: Computer and floppy drive integrated into the keyboard, 8088, 512K RAM, there was an upgrade card for the ISA slot that took it to 640K, single ISA slot
           FD360 external 360K 5.25" DS DD floppy, FD720 external 720K 3,5" DS DD floppy, HD-20 external harddisk, internal graphics card is CGA or Hercules, 64KB VRAM
* Euro PC II: like Euro PC, socket for 8087, 768K RAM on board, driver on Schneider DOS disk allowed the portion over 640K to be used as extended memory or ramdisk.
* Euro XT: conventional desktop, specs like Euro PC II, two ISA slots on a riser card, 102 key separate keyboard, internal XTA (XT-IDE) 20MB harddisk, connector for FD360 and FD720 was retained
*
* https://www.forum64.de/index.php?thread/43066-schneider-euro-pc-i-ii-xt-welche-bios-version-habt-ihr/ claims Versions BIOS >=2.06 have a change in memory management.
* Versions 2.04 and 2.05 only show a single dash on the top left of the screen, set slot 1 to from AGA to CGA or Hercules to get them to display.
*
* To get rid of the BIOS error messages when you first start the system, enter the BIOS with Ctrl-Alt-Esc, match the RAM size to your settings in MAME, set the CPU speed to 9.54MHz
* and the graphics adapter to Color/Graphics 80 or Special Adapter, internal graphics off
*
* To-Do: * An external 20MB harddisk (Schneider HD20) can be added to the PC and PC II. This is a XTA (8-bit IDE) drive. The BIOSs contain their own copy of the WD XT IDE BIOS that can be activated from the BIOS setup menu.
*          (load debug, then g=f000:a000 to enter formatter routine)
*        * emulate internal graphics, but AGA is not quite the correct choice for the standard graphics adapter (it's a Commodore standard), as the Schneiders are only capable of switching between Hercules and CGA modes.
*        * The PC 2 and XT have 768K of memory that can be configured from the BIOS setup as 640K, 640K+128K EMS and 512K+256K EMS. The EMS options are not visible in our emulation and loading the EMS driver fails.
*          See http://forum.classic-computing.de/index.php?page=Thread&threadID=8380 for screenshots.
*        * use correct AT style keyboard for XT
*
*
*****************************************************************************************************/

#include "emu.h"
#include "cpu/i86/i86.h"
#include "bus/isa/aga.h"
#include "bus/isa/fdc.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "europc_kbd.h"
#include "machine/genpc.h"
#include "machine/m3002.h"
#include "machine/ram.h"
#include "softlist_dev.h"


class europc_fdc_device : public isa8_fdc_device
{
public:
	europc_fdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

protected:
	virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
	virtual void device_start() override ATTR_COLD;

private:
	void map(address_map &map) ATTR_COLD;
};

DEFINE_DEVICE_TYPE(EUROPC_FDC, europc_fdc_device, "europc_fdc", "EURO PC FDC hookup")

europc_fdc_device::europc_fdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: isa8_fdc_device(mconfig, EUROPC_FDC, tag, owner, clock)
{
}

static void pc_dd_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
	device.option_add("35dd", FLOPPY_35_DD);
}

void europc_fdc_device::device_add_mconfig(machine_config &config)
{
	WD37C65C(config, m_fdc, 16_MHz_XTAL);
	m_fdc->intrq_wr_callback().set(FUNC(europc_fdc_device::irq_w));
	m_fdc->drq_wr_callback().set(FUNC(europc_fdc_device::drq_w));
	// single built-in 3.5" 720K drive, connector for optional external 3.5" or 5.25" drive
	FLOPPY_CONNECTOR(config, "fdc:0", pc_dd_floppies, "35dd", isa8_fdc_device::floppy_formats).set_fixed(true);
	FLOPPY_CONNECTOR(config, "fdc:1", pc_dd_floppies, nullptr, isa8_fdc_device::floppy_formats);
}

void europc_fdc_device::device_start()
{
	set_isa_device();
	m_isa->install_device(0x03f0, 0x03f7, *this, &europc_fdc_device::map);
	m_isa->set_dma_channel(2, this, true);
}

void europc_fdc_device::map(address_map &map)
{
	map(2, 2).w(m_fdc, FUNC(wd37c65c_device::dor_w));
	map(4, 5).m(m_fdc, FUNC(wd37c65c_device::map));
	// TODO: DCR also decoded by JIM/BIGJIM
}

static void europc_fdc(device_slot_interface &device)
{
	device.option_add("fdc", EUROPC_FDC);
}


namespace {

class europc_pc_state : public driver_device
{
public:
	europc_pc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_mb(*this, "mb"),
		m_ram(*this, RAM_TAG),
		m_rtc(*this, "rtc"),
		m_jim_state(0)
	{ }

	void europc(machine_config &config);
	void europc2(machine_config &config);
	void euroxt(machine_config &config);

	void init_europc();

private:
	required_device<cpu_device> m_maincpu;
	required_device<pc_noppi_mb_device> m_mb;
	required_device<ram_device> m_ram;
	required_device<m3002_device> m_rtc;

	uint8_t europc_portc_r();
	void reset_in_w(int state);

	void europc_jim_w(offs_t offset, uint8_t data);
	uint8_t europc_jim_r(offs_t offset);
	uint8_t europc_jim2_r();

	uint8_t m_jim_data[16];
	uint8_t m_jim_state;
	isa8_aga_device::mode_t m_jim_mode{};

	void europc_io(address_map &map) ATTR_COLD;
	void europc_map(address_map &map) ATTR_COLD;
};

/*
  europc
  fe107 bios checksum test
   memory test
  fe145
   irq vector init
  fe156
  fe169 fd774 // test of special europc registers 254 354
  fe16c fe817
  fe16f
   fec08 // test of special europc registers 800a rtc time or date error, rtc corrected
    fef66 0xf
    fdb3e 0x8..0xc
    fd7f8
     fdb5f
  fe172
   fecc5 // 801a video setup error
    fd6c9
   copyright output
  fe1b7
  fe1be di bits set mean output text!!!,
   (801a)
   0x8000 output
        1 rtc error
        2 rtc time or date error
        4 checksum error in setup
        8 rtc status corrected
       10 video setup error
       20 video ram bad
       40 monitor type not recogniced
       80 mouse port enabled
      100 joystick port enabled

  fe1e2 fdc0c cpu speed is 4.77 MHz
  fe1e5 ff9c0 keyboard processor error
  fe1eb fc617 external lpt1 at 0x3bc
  fe1ee fe8ee external coms at

  routines:
  fc92d output text at bp
  fdb3e rtc read reg cl
  fe8ee piep
  fe95e rtc write reg cl
   polls until jim 0xa is zero,
   output cl at jim 0xa
   write ah hinibble as lownibble into jim 0xa
   write ah lownibble into jim 0xa
  fef66 rtc read reg cl
   polls until jim 0xa is zero,
   output cl at jim 0xa
   read low 4 nibble at jim 0xa
   read low 4 nibble at jim 0xa
   return first nibble<<4|second nibble in ah
  ff046 seldom compares ret
  ffe87 0 -> ds

  469:
   bit 0: b0000 memory available
   bit 1: b8000 memory available
  46a: 00 jim 250 01 jim 350
 */


/*
  250..253 write only 00 be 00 10

  252 write 0 b0000 memory activ
  252 write 0x10 b8000 memory activ

  jim 04: 0:4.77 0x40:7.16
  pio 63: 11,19 4.77 51,59 7.16

  63 bit 6,7 clock select
  254 bit 6,7 clock select
  250 bit 0: mouse on
      bit 1: joystick on
  254..257 r/w memory ? JIM asic? ram behaviour

*/

void europc_pc_state::europc_jim_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 2:
		if (!(data & 0x80))
		{
			switch (data)
			{
			case 0x1f:
			case 0x0b: m_jim_mode = isa8_aga_device::AGA_MONO; break;
			case 0xe: //80 columns?
			case 0xd: //40 columns?
			case 0x18:
			case 0x1a: m_jim_mode = isa8_aga_device::AGA_COLOR; break;
			default: m_jim_mode = isa8_aga_device::AGA_OFF; break;
			}
		}
//      mode = (data & 0x10) ? isa8_aga_device::AGA_COLOR : isa8_aga_device::AGA_MONO;
//      mode = (data & 0x10) ? isa8_aga_device::AGA_COLOR : isa8_aga_device::AGA_OFF;
		if (data & 0x80) m_jim_state = 0;
		break;
	case 4:
		switch(data & 0xc0)
		{
		case 0x00: m_maincpu->set_clock_scale(1.0 / 2); break;
		case 0x40: m_maincpu->set_clock_scale(3.0 / 4); break;
		default: m_maincpu->set_clock_scale(1); break;
		}
		break;
	case 0xa:
		m_rtc->write(data);
		return;
	}
	logerror("jim write %.2x %.2x\n", offset, data);
	m_jim_data[offset] = data;
}

uint8_t europc_pc_state::europc_jim_r(offs_t offset)
{
	int data = 0;
	switch(offset)
	{
	case 4: case 5: case 6: case 7: data = m_jim_data[offset]; break;
	case 0: case 1: case 2: case 3: data = 0; break;
	case 0xa: return m_rtc->read();
	}
	return data;
}

uint8_t europc_pc_state::europc_jim2_r()
{
	switch (m_jim_state)
	{
	case 0: m_jim_state++; return 0;
	case 1: m_jim_state++; return 0x80;
	case 2:
		m_jim_state = 0;
		switch (m_jim_mode)
		{
		case isa8_aga_device::AGA_COLOR: return 0x87; // for color;
		case isa8_aga_device::AGA_MONO: return 0x90; //for mono
		case isa8_aga_device::AGA_OFF: return 0x80; // for vram
//      return 0x97; //for error
		}
	}
	return 0;
}

/* realtime clock and nvram  EM M3002

   reg 0: seconds
   reg 1: minutes
   reg 2: hours
   reg 3: day 1 based
   reg 4: month 1 based
   reg 5: year bcd (no century, values bigger 88? are handled as 1900, else 2000)
   reg 6:
   reg 7:
   reg 8:
   reg 9:
   reg a:
   reg b: 0x10 written
    bit 0,1: 0 video startup mode: 0=specialadapter, 1=color40, 2=color80, 3=monochrom
    bit 2: internal video on
    bit 4: color
    bit 6,7: clock
   reg c:
    bit 0,1: language/country
   reg d: xor checksum
   reg e:
   reg 0f: 01 status ok, when not 01 written
*/

void europc_pc_state::init_europc()
{
	uint8_t *rom = &memregion("bios")->base()[0];

	/*
	  fix century rom bios bug !
	  if year <79 month (and not CENTURY) is loaded with 0x20
	*/
	if (rom[0xf93e]==0xb6){ // mov dh,
		rom[0xf93e]=0xb5; // mov ch,
		uint8_t a = 0;
		int i = 0x8000;
		for (; i < 0xffff; i++)
			a += rom[i];
		rom[0xffff] = 256 - a;
	}
}

uint8_t europc_pc_state::europc_portc_r()
{
	int data = 0;
	if (m_mb->pit_out2())
		data |= 0x20;

	return data;
}

void europc_pc_state::reset_in_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? CLEAR_LINE : ASSERT_LINE);
	if (!state)
		m_mb->reset();
}

/*
layout of an uk europc

ESC, [SPACE], F1,F2,F3,F4,[SPACE],F5,F6,F7,F8,[SPACE],F9,F0,F11,F12
[SPACE]
\|, 1,2,3,4,5,6,7,8,9,0 -,+, BACKSPACE,[SPACE], NUM LOCK, SCROLL LOCK, PRINT SCREEN, KEYPAD -
TAB,Q,W,E,R,T,Y,U,I,O,P,[,], RETURN, [SPACE], KEYPAD 7, KEYPAD 8, KEYPAD 9, KEYPAD +
CTRL, A,S,D,F,G,H,J,K,L,;,@,~, RETURN, [SPACE],KEYPAD 4,KEYPAD 5,KEYPAD 6, KEYPAD +
LEFT SHIFT, Z,X,C,V,B,N,M,<,>,?,RIGHT SHIFT,[SPACE],KEYPAD 1, KEYPAD 2, KEYPAD 3, KEYPAD ENTER
ALT,[SPACE], SPACE BAR,[SPACE],CAPS LOCK,[SPACE], KEYPAD 0, KEYPAD ., KEYPAD ENTER

\ and ~ had to be swapped
i am not sure if keypad enter delivers the mf2 keycode
 */

static INPUT_PORTS_START( europc )
	PORT_START("DSW0") /* IN1 */

	PORT_START("DSW1") /* IN2 */
	PORT_DIPNAME( 0x80, 0x80, "COM1: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x40, 0x40, "COM2: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x20, 0x00, "COM3: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x10, 0x00, "COM4: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x08, 0x08, "LPT1: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x04, 0x00, "LPT2: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x02, 0x00, "LPT3: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x01, 0x00, "Game port enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x01, DEF_STR( Yes ) )

	PORT_START("DSW2") /* IN3 */
	PORT_DIPNAME( 0x08, 0x08, "HDC1 (C800:0 port 320-323)")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x04, 0x04, "HDC2 (CA00:0 port 324-327)")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Yes ) )
	PORT_BIT( 0x02, 0x02,   IPT_UNUSED ) /* no turbo switch */
	PORT_BIT( 0x01, 0x01,   IPT_UNUSED )
INPUT_PORTS_END

void europc_pc_state::europc_map(address_map &map)
{
	map.unmap_value_high();
	map(0xf0000, 0xfffff).rom().region("bios", 0);
}

void europc_pc_state::europc_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m(m_mb, FUNC(pc_noppi_mb_device::map));
	map(0x0062, 0x0062).r(FUNC(europc_pc_state::europc_portc_r));
	map(0x0250, 0x025f).rw(FUNC(europc_pc_state::europc_jim_r), FUNC(europc_pc_state::europc_jim_w));
	map(0x02e0, 0x02e0).r(FUNC(europc_pc_state::europc_jim2_r));
}


//Euro PC
void europc_pc_state::europc(machine_config &config)
{
	I8088(config, m_maincpu, 4772720*2);
	m_maincpu->set_addrmap(AS_PROGRAM, &europc_pc_state::europc_map);
	m_maincpu->set_addrmap(AS_IO, &europc_pc_state::europc_io);
	m_maincpu->set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	PCNOPPI_MOTHERBOARD(config, m_mb, 0).set_cputag(m_maincpu);
	m_mb->int_callback().set_inputline(m_maincpu, 0);
	m_mb->nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_mb->kbddata_callback().set("kbd", FUNC(europc_keyboard_device::kbdata_w));
	m_mb->kbdclk_callback().set("kbd", FUNC(europc_keyboard_device::kbclk_w));

	europc_keyboard_device &kbd(EUROPC_KEYBOARD(config, "kbd", 16_MHz_XTAL / 4));
	kbd.kbdata_callback().set(m_mb, FUNC(pc_noppi_mb_device::keyboard_data_w));
	kbd.kbclk_callback().set(m_mb, FUNC(pc_noppi_mb_device::keyboard_clock_w));
	kbd.reset_callback().set(FUNC(europc_pc_state::reset_in_w));

	ISA8_SLOT(config, "isa1", 0, "mb:isa", pc_isa8_cards, "aga", false); // FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa2", 0, "mb:isa", pc_isa8_cards, "lpt", true);
	ISA8_SLOT(config, "isa3", 0, "mb:isa", pc_isa8_cards, "com", true);
	ISA8_SLOT(config, "isa4", 0, "mb:isa", europc_fdc, "fdc", true);

	M3002(config, m_rtc, 32.768_kHz_XTAL);

	/* internal ram */
	// Machine came with 512K standard, 640K via expansion card, but BIOS offers 256K as well
	RAM(config, m_ram).set_default_size("512K").set_extra_options("256K, 640K");

	/* software lists */
	SOFTWARE_LIST(config, "disk_list").set_original("ibm5150");
}

//Euro PC II
void europc_pc_state::europc2(machine_config &config)
{
	europc(config);
	// could be configured by the BIOS as 640K, 640K+128K EMS or 512K+256K EMS
	m_ram->set_default_size("768K");
}

//Euro XT
void europc_pc_state::euroxt(machine_config &config)
{
	europc(config);

	pc_kbdc_device &pc_kbdc(PC_KBDC(config.replace(), "kbd", pc_xt_keyboards, STR_KBD_IBM_PC_XT_83));
	pc_kbdc.out_clock_cb().set(m_mb, FUNC(pc_noppi_mb_device::keyboard_clock_w));
	pc_kbdc.out_data_cb().set(m_mb, FUNC(pc_noppi_mb_device::keyboard_data_w));

	m_mb->kbdclk_callback().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	m_mb->kbddata_callback().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));

	m_ram->set_default_size("768K");

	subdevice<isa8_slot_device>("isa2")->set_default_option(nullptr);
	ISA8_SLOT(config, "isa5", 0, "mb:isa", pc_isa8_cards, "xtide", true); // FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa6", 0, "mb:isa", pc_isa8_cards, "lpt", true);
}

ROM_START( europc )
	ROM_REGION(0x10000,"bios", 0)
	// hdd bios integrated!
	ROM_SYSTEM_BIOS( 0, "v2.06", "EuroPC v2.06" )
	ROMX_LOAD("bios_v2.06.bin", 0x8000, 0x8000, CRC(0a25a2eb) SHA1(d35f2f483d56b1eff558586e1d33d82f7efed639), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v2.06b", "EuroPC v2.06b" )
	ROMX_LOAD("bios_v2.06b.bin", 0x8000, 0x8000, CRC(05d8a4c2) SHA1(52c6fd22fb739e29a1f0aa3c96ede79cdc659f72), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "v2.07", "EuroPC v2.07" )
	ROMX_LOAD("50145", 0x8000, 0x8000, CRC(1775a11d) SHA1(54430d4d0462860860397487c9c109e6f70db8e3), ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "v2.08", "EuroPC v2.08" )
	ROMX_LOAD("bios_v2.08.bin", 0x8000, 0x8000, CRC(a7048349) SHA1(c2a0af7276c2ff6925abe5a5edef09c5a84106f2), ROM_BIOS(3))
	ROM_SYSTEM_BIOS( 4, "v2.08a", "EuroPC v2.08a" )
	ROMX_LOAD("bios_v2.08a.bin", 0x8000, 0x8000, CRC(872520b7) SHA1(9c94d33c0d454fab7bcd0c4516b50f1c3c6a30b8), ROM_BIOS(4))
	ROM_SYSTEM_BIOS( 5, "v2.08b", "EuroPC v2.08b" )
	ROMX_LOAD("bios_v2.08b.bin", 0x8000, 0x8000, CRC(668c0d19) SHA1(69412e58e0ed1d141e633f094af91ec5f7ae064b), ROM_BIOS(5))
	ROM_SYSTEM_BIOS( 6, "v2.04", "EuroPC v2.04" )
	ROMX_LOAD("bios_v2.04.bin", 0x8000, 0x8000, CRC(e623967c) SHA1(5196b14018da1f3198e2950af0e6eab41425f556), ROM_BIOS(6))
	ROM_SYSTEM_BIOS( 7, "v2.05", "EuroPC v2.05" )
	ROMX_LOAD("bios_2.05.bin", 0x8000, 0x8000, CRC(372ceed6) SHA1(bb3d3957a22422f98be2225bdc47705bcab96f56), ROM_BIOS(7)) // v2.04 and v2.05 don't work yet, , see comment section
ROM_END

ROM_START( europc2 )
	ROM_REGION(0x10000,"bios", 0)
	// hdd bios integrated!
	ROM_LOAD("europcii_bios_v3.01_500145.bin", 0x8000, 0x8000, CRC(ecca89c8) SHA1(802b89babdf0ab0a0a9c21d1234e529c8386d6fb))
ROM_END

ROM_START( euroxt )
	ROM_REGION(0x10000,"bios", 0)
	// hdd bios integrated!
	ROM_SYSTEM_BIOS( 0, "v1.01", "EuroXT v1.01" )
	ROMX_LOAD("euroxt_bios_v1.01.bin", 0x8000, 0x8000, CRC(1e1fe931) SHA1(bb7cae224d66ae48045f323ecb9ad59bf49ed0a2), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v1.02", "EuroXT v1.02" )
	ROMX_LOAD("euro_xt_bios_id.nr.51463_v1.02.bin", 0x8000, 0x8000, CRC(c36de60e) SHA1(c668cc9c5f3325233f30eac654678e1b8b7a7847), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "v1.04", "EuroXT v1.04" ) // no display
	ROMX_LOAD("euro_xt_bios_v1.04_cs8b00_5.12.89_21_25.bin", 0x8000, 0x8000, CRC(24033a62) SHA1(9d1d89cb8b99569b6c0aaa7c6aceb355dc20b2fd), ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "v1.05", "EuroXT v1.05" ) // no display
	ROMX_LOAD("euro-xt_bios_id.nr.51463_v1.05.bin", 0x8000, 0x8000, CRC(e3d2591d) SHA1(710cdbafeb913f2e436b64eedd7a1794c589a48a), ROM_BIOS(3))

	// BIOS ROM versions 1.02, 1.04 and 1.05 were accompanied by identical char ROM versions 50146, which in turn match the one used in /bus/isa/aga.cpp
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT  COMPAT   MACHINE  INPUT   CLASS            INIT         COMPANY              FULLNAME      FLAGS
COMP( 1988, europc,  0,      ibm5150, europc,  europc, europc_pc_state, init_europc, "Schneider Rdf. AG", "EURO PC",    MACHINE_NOT_WORKING)
COMP( 198?, europc2, 0,      ibm5150, europc2, europc, europc_pc_state, init_europc, "Schneider Rdf. AG", "EURO PC II", MACHINE_NOT_WORKING)
COMP( 198?, euroxt,  0,      ibm5150, euroxt,  europc, europc_pc_state, init_europc, "Schneider Rdf. AG", "EURO XT",    MACHINE_NOT_WORKING)



eva.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:David Viens, Sean Riddle
/*******************************************************************************

Chrysler Electronic Voice Alert

11-function board "EVA-11"
- TMS1000 MCU (label 4230625-N1LL 32045B, die label: 1000F, M32045B)
- TMS5110A, TMS6125 CM73002
- 2 Nat.Semi. 20-pin SDIP, I/O expanders?

24-function board "EVA-24"
- COP420 MCU (custom label)
- TMS5110A, TMS6100 CM63002
- monitor board (unknown MCU, no dump), VFD panel

TODO:
- add eva11/eva24 sensors
- add eva24 VFD

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

#include "emu.h"

#include "cpu/cop400/cop400.h"
#include "cpu/tms1000/tms1000.h"
#include "machine/tms6100.h"
#include "sound/tms5110.h"

#include "speaker.h"


namespace {

class base_state : public driver_device
{
public:
	base_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_tms5100(*this, "tms5100"),
		m_tms6100(*this, "tms6100")
	{ }

	void eva_sound(machine_config &config);

protected:
	// devices
	required_device<tms5110_device> m_tms5100;
	required_device<tms6100_device> m_tms6100;
};

class eva11_state : public base_state
{
public:
	eva11_state(const machine_config &mconfig, device_type type, const char *tag) :
		base_state(mconfig, type, tag),
		m_maincpu(*this, "maincpu")
	{ }

	void eva(machine_config &config);

private:
	// devices
	required_device<tms1000_cpu_device> m_maincpu;

	u8 read_k();
	void write_o(u16 data);
	void write_r(u32 data);
};

class eva24_state : public base_state
{
public:
	eva24_state(const machine_config &mconfig, device_type type, const char *tag) :
		base_state(mconfig, type, tag),
		m_maincpu(*this, "maincpu")
	{ }

	void eva(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices
	required_device<cop420_cpu_device> m_maincpu;

	u8 m_g = 0;

	u8 read_g();
	void write_g(u8 data);
	void write_d(u8 data);
};

void eva24_state::machine_start()
{
	save_item(NAME(m_g));
}



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

// EVA-24

void eva24_state::write_g(u8 data)
{
	// G3: TMS5100 PDC pin
	m_tms5100->pdc_w(data >> 3 & 1);
	m_g = data;
}

u8 eva24_state::read_g()
{
	return m_g;
}

void eva24_state::write_d(u8 data)
{
	// D3210: TMS5100 CTL8421
	m_tms5100->ctl_w(data & 0xf);
}


// EVA-11

void eva11_state::write_r(u32 data)
{
	// R7: TMS5100 PDC pin
	m_tms5100->pdc_w(data >> 7 & 1);
}

void eva11_state::write_o(u16 data)
{
	// O3210: TMS5100 CTL8421
	m_tms5100->ctl_w(data & 0xf);
}

u8 eva11_state::read_k()
{
	// K84: TMS5100 CTL81(O30)
	u8 ctl = m_tms5100->ctl_r();
	ctl = bitswap<2>(ctl, 3,0) << 2;

	// TODO: sensors

	return ctl;
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( eva24 )
INPUT_PORTS_END

static INPUT_PORTS_START( eva11 )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void base_state::eva_sound(machine_config &config)
{
	// sound hardware
	TMS6100(config, m_tms6100, 640_kHz_XTAL/4);

	SPEAKER(config, "mono").front_center();
	TMS5110A(config, m_tms5100, 640_kHz_XTAL);
	m_tms5100->m0().set(m_tms6100, FUNC(tms6100_device::m0_w));
	m_tms5100->m1().set(m_tms6100, FUNC(tms6100_device::m1_w));
	m_tms5100->addr().set(m_tms6100, FUNC(tms6100_device::add_w));
	m_tms5100->data().set(m_tms6100, FUNC(tms6100_device::data_line_r));
	m_tms5100->romclk().set(m_tms6100, FUNC(tms6100_device::clk_w));
	m_tms5100->add_route(ALL_OUTPUTS, "mono", 0.5);
}

void eva24_state::eva(machine_config &config)
{
	// basic machine hardware
	COP420(config, m_maincpu, 640_kHz_XTAL/2); // guessed
	m_maincpu->set_config(COP400_CKI_DIVISOR_4, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_d().set(FUNC(eva24_state::write_d));
	m_maincpu->write_g().set(FUNC(eva24_state::write_g));
	m_maincpu->read_g().set(FUNC(eva24_state::read_g));

	eva_sound(config);
}

void eva11_state::eva(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 640_kHz_XTAL/2); // from TMS5110A CPU CK
	m_maincpu->read_k().set(FUNC(eva11_state::read_k));
	m_maincpu->write_o().set(FUNC(eva11_state::write_o));
	m_maincpu->write_r().set(FUNC(eva11_state::write_r));

	eva_sound(config);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( eva24 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "4232345", 0x0000, 0x0400, CRC(0326c2fe) SHA1(c4c73badee68f682871b42ba4f5ca115cd68fb8a) )

	ROM_REGION( 0x4000, "tms6100", 0 )
	ROM_LOAD( "cm63002", 0x0000, 0x4000, CRC(cb63c807) SHA1(df7323eebcd2a8a5401c2e0addbbabb700182302) )
ROM_END

ROM_START( eva11 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "32045b", 0x0000, 0x0400, CRC(eea36ebe) SHA1(094755b60965654ddc3e57cbd69f4749abd3b526) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_eva11_micro.pla", 0, 867, CRC(38fcc108) SHA1(be265300e0828b6ac83832b3279985a43817bb64) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_eva11_output.pla", 0, 365, CRC(f0f36970) SHA1(a6ad1f5e804ac98e5e1a1d07466b3db3a8d6c256) )

	ROM_REGION( 0x1000, "tms6100", 0 )
	ROM_LOAD( "cm73002", 0x0000, 0x1000, CRC(d5340bf8) SHA1(81195e8f870275d39a1abe1c8e2a6afdfdb15725) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY     FULLNAME                                FLAGS
SYST( 1984, eva24, 0,      0,      eva,     eva24, eva24_state, empty_init, "Chrysler", "Electronic Voice Alert (24-function)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING )
SYST( 1983, eva11, eva24,  0,      eva,     eva11, eva11_state, empty_init, "Chrysler", "Electronic Voice Alert (11-function)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING )



evmbug.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Stuart's Breadboard Project. TMS9995 evaluation kit TMAM6095.

2013-06-02 Skeleton driver.

http://www.stuartconner.me.uk/tms9995_eval_module/tms9995_eval_module.htm

It uses TMS9902 UART for comms, but our implementation of that chip is
not ready for rs232.h as yet.

Press any key to get it started. All input to be in uppercase. Instructions
for Hello World are on the EVMBUG page:

http://www.stuartconner.me.uk/tibug_evmbug/tibug_evmbug.htm#evmbug_example

Pay close attention to "On lines where no label is to be input,
remember to press <Space> to step over the symbol field before
entering the instruction." e.g. a space is needed before XOP.

2022-03-05 TMS9995 Breadboard System added by Chris Swan.

http://www.stuartconner.me.uk/tms9995_breadboard/tms9995_breadboard.htm

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

#include "emu.h"
#include "cpu/tms9900/tms9995.h"
//#include "machine/tms9902.h"
#include "machine/terminal.h"


namespace {

class evmbug_state : public driver_device
{
public:
	evmbug_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_terminal(*this, "terminal")
	{ }

	void evmbug(machine_config &config);
	void tms9995bb(machine_config &config);

private:
	uint8_t rs232_r(offs_t offset);
	void rs232_w(offs_t offset, uint8_t data);
	void kbd_put(u8 data);

	void io_map(address_map &map) ATTR_COLD;
	void evmbug_mem(address_map &map) ATTR_COLD;
	void tms9995bb_mem(address_map &map) ATTR_COLD;

	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	uint8_t m_term_data = 0U;
	uint8_t m_term_out = 0U;
	bool m_rin = 0;
	bool m_rbrl = 0;
	required_device<tms9995_device> m_maincpu;
	required_device<generic_terminal_device> m_terminal;
};

void evmbug_state::evmbug_mem(address_map &map)
{
	map(0x0000, 0x17ff).rom();
	map(0xec00, 0xefff).ram();
}

// Breadboard system uses 32K ROM and 32K RAM
void evmbug_state::tms9995bb_mem(address_map &map)
{
	map(0x0000, 0x7fff).rom();
	map(0x8000, 0xffff).ram();
}

void evmbug_state::io_map(address_map &map)
{
	map.unmap_value_high();
	//map(0x0000, 0x003f).rw("uart1", FUNC(tms9902_device::cruread), FUNC(tms9902_device::cruwrite));
	map(0x0000, 0x003f).rw(FUNC(evmbug_state::rs232_r), FUNC(evmbug_state::rs232_w));
}

/* Input ports */
static INPUT_PORTS_START( evmbug )
INPUT_PORTS_END

uint8_t evmbug_state::rs232_r(offs_t offset)
{
	if (offset < 8)
		return BIT(m_term_data, offset);
	else if (offset == 21)
		return m_rbrl;
	else if (offset == 22 || offset == 23)
		return 1;
	else if (offset == 15)
	{
		m_rin ^= 1;
		return m_rin;
	}
	else
		return 0;
}

void evmbug_state::rs232_w(offs_t offset, uint8_t data)
{
	if (offset < 8)
	{
		if (offset == 0)
			m_term_out = 0;

		m_term_out |= (data << offset);

		if (offset == 7)
			m_terminal->write(m_term_out & 0x7f);
	}
	else
	if (offset == 18)
		m_rbrl = 0;
}

void evmbug_state::kbd_put(u8 data)
{
	m_term_data = data;
	m_rbrl = data ? 1 : 0;
}

void evmbug_state::machine_reset()
{
	m_rbrl = 0;
	// Disable auto wait state generation by raising the READY line on reset
	m_maincpu->ready_line(ASSERT_LINE);
	m_maincpu->reset_line(ASSERT_LINE);
}

void evmbug_state::machine_start()
{
	save_item(NAME(m_term_data));
	save_item(NAME(m_term_out));
	save_item(NAME(m_rin));
	save_item(NAME(m_rbrl));
}

void evmbug_state::evmbug(machine_config &config)
{
	// basic machine hardware
	// TMS9995 CPU @ 12.0 MHz
	// We have no lines connected yet
	TMS9995(config, m_maincpu, XTAL(12'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &evmbug_state::evmbug_mem);
	m_maincpu->set_addrmap(AS_IO, &evmbug_state::io_map);

	/* video hardware */
	GENERIC_TERMINAL(config, m_terminal, 0);
	m_terminal->set_keyboard_callback(FUNC(evmbug_state::kbd_put));

	//TMS9902(config, "uart1", XTAL(12'000'000) / 4);
}

void evmbug_state::tms9995bb(machine_config &config)
{
	evmbug(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &evmbug_state::tms9995bb_mem);
}

/* ROM definition */
ROM_START( evmbug )
	ROM_REGION( 0x1800, "maincpu", 0 )
	ROM_LOAD( "u8.bin", 0x0000, 0x1000, CRC(ca869a70) SHA1(424d8d61ef15645e3ce3867c64a0cfb69633b5bc) )
	ROM_LOAD( "u9.bin", 0x1000, 0x0800, CRC(7f71c9bf) SHA1(5215892585e5282650209c5ce13a2e4bd6041675) )
ROM_END

/* ROMs from and FF padded to make 32K */
ROM_START( tms9995bb )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "evmbug", "EVMBUG system monitor")
	ROMX_LOAD( "evmbug.bin",   0x0000, 0x8000, CRC(a239ec56) SHA1(65b500d7d0f897ce0c320cf3ec32ff4042774599), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "basicram", "EVMBUG system monitor and BASIC in RAM")
	ROMX_LOAD( "basicram.bin", 0x0000, 0x8000, CRC(6ed5aba3) SHA1(76e0e39c0c0028efca339fb3cebaf42351fadb94), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "basicrom", "EVMBUG system monitor and BASIC in ROM")
	ROMX_LOAD( "basicrom.bin", 0x0000, 0x8000, CRC(ded6350b) SHA1(83cf64834e59e216a91065c01ec91be1e10e7244), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "forth", "EVMBUG system monitor and Forth")
	ROMX_LOAD( "forth.bin",    0x0000, 0x8000, CRC(eee8f390) SHA1(59fc2a23c9ac52dce09a519c784db378782242b1), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "test1", "Test EPROM 1")
	ROMX_LOAD( "test1.bin",    0x0000, 0x8000, CRC(9b110ffb) SHA1(58ee990fb17822a879442b98f1b78ccf86b79f00), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "test2", "Test EPROM 2")
	ROMX_LOAD( "test2.bin",    0x0000, 0x8000, CRC(e7a7832d) SHA1(aa8c29097033804d1a0abf2af7cd846edfbd71a3), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6, "test3", "Test EPROM 3")
	ROMX_LOAD( "test3.bin",    0x0000, 0x8000, CRC(a28579eb) SHA1(477f853970f132592714bcdd048ec932e96c8593), ROM_BIOS(6) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME       PARENT     COMPAT  MACHINE     INPUT   CLASS            INIT        COMPANY              FULLNAME              FLAGS
COMP( 198?, evmbug,    0,         0,      evmbug,     evmbug, evmbug_state,    empty_init, "Texas Instruments", "TMAM 6095",          MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 19??, tms9995bb, evmbug,    0,      tms9995bb,  evmbug, evmbug_state,    empty_init, "Stuart Conner",     "TMS9995 breadboard", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



evolution_handheld.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

// TODO: identify the CPU type! - there are vectors(?) near the start

#include "emu.h"

#include "cpu/evolution/evo.h"

#include "screen.h"
#include "speaker.h"


namespace {

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

	void evolhh(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	required_device<evo_cpu_device> m_maincpu;

	void evolution_map(address_map &map) ATTR_COLD;
};

void evolution_handheldgame_state::machine_start()
{
}

void evolution_handheldgame_state::machine_reset()
{
}

static INPUT_PORTS_START( evolhh )
INPUT_PORTS_END


uint32_t evolution_handheldgame_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void evolution_handheldgame_state::evolution_map(address_map &map)
{
	map(0x000000, 0x01ffff).mirror(0x400000).rom().region("maincpu", 0x00000);
}


void evolution_handheldgame_state::evolhh(machine_config &config)
{
	EVOLUTION_CPU(config, m_maincpu, XTAL(16'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &evolution_handheldgame_state::evolution_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(0));
	screen.set_size(256, 256); // resolution not confirmed
	screen.set_visarea(0, 256-1, 0, 256-1);
	screen.set_screen_update(FUNC(evolution_handheldgame_state::screen_update));

	SPEAKER(config, "speaker").front_center();
}

ROM_START( evolhh )
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_LOAD( "s29gl032m90tfir4.u4", 0x000000, 0x400000, CRC(c647ca01) SHA1(a88f512d3fe8803dadc4eb6a94b5babd40c698de) )
ROM_END

ROM_START( smkatsum )
	ROM_REGION( 0x800000, "maincpu", 0 )
	ROM_LOAD( "gpr25l64.ic4", 0x000000, 0x800000, CRC(85be7517) SHA1(f9b838e09ceff9b99f3e41f010ff12f6adfa9be1) )
ROM_END

ROM_START( buttdtct )
	ROM_REGION( 0x800000, "maincpu", 0 )
	ROM_LOAD( "25l64.ic4", 0x000000, 0x800000, CRC(a70a2b0d) SHA1(fe5517d58297b737f9b6f645f76bea2a5dae1eb6) )
ROM_END


} // anonymous namespace


CONS( 2006, evolhh,      0,       0,      evolhh, evolhh, evolution_handheldgame_state, empty_init, "Kidz Delight", "Evolution Max", MACHINE_NO_SOUND | MACHINE_NOT_WORKING ) // from a pink 'for girls' unit, exists in other colours, software likely the same

CONS( 2018, smkatsum,    0,       0,      evolhh, evolhh, evolution_handheldgame_state, empty_init, "San-X / Tomy", "Sumikko Gurashi - Sumikko Atsume (Japan)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING ) // from a pink 'for girls' unit, exists in other colours, software likely the same

// おしりたんてい ププッとかいけつゲーム
CONS( 2020, buttdtct,    0,       0,      evolhh, evolhh, evolution_handheldgame_state, empty_init, "Tomy", "Oshiri Tantei - Puputto Kaiketsu Game (Japan)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING ) // from a pink 'for girls' unit, exists in other colours, software likely the same



ews4800.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * NEC EWS4800 systems.
 *
 * Sources:
 *  - http://www.jira-net.or.jp/vm/data/1993090101/1993090101knr/4-1-14.pdf
 *  - http://wiki.netbsd.org/ports/ews4800mips/
 *
 * TODO:
 *  - everything
 */

#include "emu.h"

// processors and memory
#include "cpu/mips/r4000.h"
#include "machine/ram.h"

// i/o devices
#include "machine/z80scc.h"
#include "machine/am79c90.h"
#include "machine/timekpr.h"
#include "machine/ncr53c90.h"

// busses and connectors
#include "machine/nscsi_bus.h"
#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"
#include "bus/rs232/rs232.h"

#include "debugger.h"

#define VERBOSE 0
#include "logmacro.h"

namespace {

class ews4800_state : public driver_device
{
public:
	ews4800_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_ram(*this, "ram")
		, m_rtc(*this, "rtc")
		, m_scc(*this, "scc%u", 0U)
		, m_scsibus(*this, "scsi")
		, m_scsi(*this, "scsi:7:ncr53c96")
		, m_net(*this, "net")
	{
	}

	// machine config
	void ews4800_310(machine_config &config);

	void init();

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// address maps
	void cpu_map(address_map &map) ATTR_COLD;

	u16 lance_r(offs_t offset, u16 mem_mask = 0xffff);
	void lance_w(offs_t offset, u16 data, u16 mem_mask = 0xffff);

private:
	// processors and memory
	required_device<r4000_device> m_cpu;
	required_device<ram_device> m_ram;

	// i/o devices
	required_device<m48t02_device> m_rtc;
	required_device_array<z80scc_device, 2> m_scc;
	required_device<nscsi_bus_device> m_scsibus;
	required_device<ncr53c94_device> m_scsi;
	required_device<am7990_device> m_net;
};

void ews4800_state::machine_start()
{
}

void ews4800_state::machine_reset()
{
}

void ews4800_state::init()
{
	// map the configured ram
	m_cpu->space(0).install_ram(0x00000000, m_ram->mask(), m_ram->pointer());
}


void ews4800_state::cpu_map(address_map &map)
{
	map(0x1fc00000, 0x1fcfffff).rom().region("eprom", 0);
}

u16 ews4800_state::lance_r(offs_t offset, u16 mem_mask)
{
	return 0;
}

void ews4800_state::lance_w(offs_t offset, u16 data, u16 mem_mask)
{
}

static void ews4800_scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
	device.option_add("cdrom", NSCSI_CDROM);
}

/*
 * irq  function
 *  1   fdd, printer
 *  2   ethernet, scsi
 *  3   vme?
 *  4   serial
 *  5   clock
 */
void ews4800_state::ews4800_310(machine_config &config)
{
	R4000(config, m_cpu, 40_MHz_XTAL);
	m_cpu->set_addrmap(AS_PROGRAM, &ews4800_state::cpu_map);

	// 8 SIMM slots
	RAM(config, m_ram);
	m_ram->set_default_size("16M");
	m_ram->set_extra_options("80M,144M");
	m_ram->set_default_value(0);

	// scsi bus and devices
	NSCSI_BUS(config, m_scsibus);
	NSCSI_CONNECTOR(config, "scsi:0", ews4800_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:1", ews4800_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", ews4800_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", ews4800_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", ews4800_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", ews4800_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", ews4800_scsi_devices, nullptr);

	// scsi host adapter (NCR53C96)
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr53c96", NCR53C94).clock(24_MHz_XTAL).machine_config(
		[](device_t *device)
		{
			ncr53c94_device &adapter = downcast<ncr53c94_device &>(*device);

			adapter.set_busmd(ncr53c94_device::busmd_t::BUSMD_1);
			//adapter.irq_handler_cb()
			//adapter.drq_handler_cb()
		});

	// ethernet
	AM7990(config, m_net);
	//m_net->intr_out()
	m_net->dma_in().set(FUNC(ews4800_state::lance_r));
	m_net->dma_out().set(FUNC(ews4800_state::lance_w));

	// mouse on channel A, keyboard on channel B?
	SCC85230(config, m_scc[0], 4.915200_MHz_XTAL); // TODO: clock unconfirmed
	SCC85230(config, m_scc[1], 4.915200_MHz_XTAL); // TODO: clock unconfirmed

	M48T02(config, m_rtc);
}

ROM_START(ews4800_310)
	ROM_REGION64_BE(0x100000, "eprom", 0)
	ROM_SYSTEM_BIOS(0, "ews4800_310", "ews4800_310")
	ROMX_LOAD("g8ppg__0100.a01f2", 0x00000, 0x80000, CRC(a1e25ce7) SHA1(cfc5e2b203bf6018b04980deeee43afa202dea7c), ROM_BIOS(0))
	ROMX_LOAD("g8ppg__0200.a01f",  0x80000, 0x80000, CRC(d610f20d) SHA1(f8476bf91111b8023ff7984e5e9a8575e48ed5df), ROM_BIOS(0))
ROM_END

} // anonymous namespace

/*   YEAR   NAME         PARENT  COMPAT  MACHINE      INPUT  CLASS          INIT  COMPANY  FULLNAME       FLAGS */
COMP(1993,  ews4800_310, 0,      0,      ews4800_310, 0,     ews4800_state, init, "NEC",   "EWS4800/310", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



excali64.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Excalibur 64 kit computer, designed and sold in Australia by BGR Computers.
The official schematics have a LOT of errors and omissions.

Skeleton driver created on 2014-12-09.

Chips: Z80A, 8251, 8253, 8255, 6845
We have Basic 1.1. Other known versions are 1.01, 2.1
There are 2 versions of the colour prom, which have different palettes.
We have the later version.

Notes:
- When booted it asks for a disk. Press enter to start ROM BASIC.
- Control W then Enter will switch between 40 and 80 characters per line.
- Control V turns cursor on
- Graphics commands such as LINE, CIRCLE, HGRCLS, HGRSET etc only work with disk basic

ToDo:
- Colours are approximate.
- Hardware supports 20cm and 13cm floppies, but we only support 13cm as this
  is the only software that exists.
- The schematic shows the audio counter connected to 2MHz, but this produces
  sounds that are too high. Connected to 1MHz for now.
- Serial
- Pasting can sometimes drop a character.

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


#include "emu.h"

#include "bus/centronics/ctronics.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "machine/74123.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/pit8253.h"
#include "machine/rescap.h"
#include "machine/wd_fdc.h"
#include "machine/z80dma.h"
#include "sound/spkrdev.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/excali64_dsk.h"


namespace {

class excali64_state : public driver_device
{
public:
	excali64_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_palette(*this, "palette")
		, m_maincpu(*this, "maincpu")
		, m_p_chargen(*this, "chargen")
		, m_bankr(*this, "bankr%u", 1U)
		, m_bankw(*this, "bankw%u", 1U)
		, m_cass(*this, "cassette")
		, m_crtc(*this, "crtc")
		, m_io_keyboard(*this, "KEY.%u", 0)
		, m_dma(*this, "dma")
		, m_u12(*this, "u12")
		, m_centronics(*this, "centronics")
		, m_fdc(*this, "fdc")
		, m_floppy0(*this, "fdc:0")
		, m_floppy1(*this, "fdc:1")
	{ }

	void excali64(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	void excali64_palette(palette_device &palette);
	void ppib_w(u8 data);
	u8 ppic_r();
	void ppic_w(u8 data);
	u8 port00_r();
	u8 port50_r();
	void port70_w(u8 data);
	void porte4_w(u8 data);
	u8 porte8_r();
	void portec_w(u8 data);
	static void floppy_formats(format_registration &fr);
	void cent_busy_w(int state);
	u8 memory_read_byte(offs_t offset);
	void memory_write_byte(offs_t offset, u8 data);
	u8 io_read_byte(offs_t offset);
	void io_write_byte(offs_t offset, u8 data);
	MC6845_UPDATE_ROW(update_row);
	void crtc_hs(int state);
	void crtc_vs(int state);
	void motor_w(int state);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	u8 m_sys_status = 0U;
	u8 m_kbdrow = 0U;
	bool m_crtc_vs = 0;
	bool m_crtc_hs = 0;
	bool m_motor = 0;
	bool m_centronics_busy = 0;
	std::unique_ptr<u8[]> m_vram;
	std::unique_ptr<u8[]> m_hram;
	std::unique_ptr<u8[]> m_ram;
	required_device<palette_device> m_palette;
	required_device<z80_device> m_maincpu;
	required_region_ptr<u8> m_p_chargen;
	required_memory_bank_array<4> m_bankr;
	required_memory_bank_array<4> m_bankw;
	required_device<cassette_image_device> m_cass;
	required_device<mc6845_device> m_crtc;
	required_ioport_array<8> m_io_keyboard;
	required_device<z80dma_device> m_dma;
	required_device<ttl74123_device> m_u12;
	required_device<centronics_device> m_centronics;
	required_device<wd2793_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	required_device<floppy_connector> m_floppy1;
};

void excali64_state::mem_map(address_map &map)
{
	map(0x0000, 0x1fff).bankr(m_bankr[0]).bankw(m_bankw[0]);
	map(0x2000, 0x2fff).bankr(m_bankr[1]).bankw(m_bankw[1]);
	map(0x3000, 0x3fff).bankr(m_bankr[2]).bankw(m_bankw[2]);
	map(0x4000, 0xbfff).bankr(m_bankr[3]).bankw(m_bankw[3]);
	map(0xc000, 0xffff).ram();
}

void excali64_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x0f).r(FUNC(excali64_state::port00_r));
	map(0x10, 0x11).mirror(0x0e).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x20, 0x23).mirror(0x0c).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x30, 0x30).mirror(0x0e).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x31, 0x31).mirror(0x0e).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x50, 0x5f).r(FUNC(excali64_state::port50_r));
	map(0x60, 0x63).mirror(0x0c).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x70, 0x7f).w(FUNC(excali64_state::port70_w));
	map(0xe0, 0xe3).rw(m_dma, FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	map(0xe4, 0xe7).w(FUNC(excali64_state::porte4_w));
	map(0xe8, 0xeb).r(FUNC(excali64_state::porte8_r));
	map(0xec, 0xef).w(FUNC(excali64_state::portec_w));
	map(0xf0, 0xf3).rw(m_fdc, FUNC(wd2793_device::read), FUNC(wd2793_device::write));
}


static INPUT_PORTS_START( excali64 )
	PORT_START("KEY.0")    /* line 0 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR(0x12)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR(0x17)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(0x05)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TAB") PORT_CODE(KEYCODE_TAB) PORT_CHAR(0x09)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CAPSLOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR(0x01)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') PORT_CHAR(0x11)

	PORT_START("KEY.1")    /* line 1 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED) // space
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED) // F1

	PORT_START("KEY.2")    /* line 2 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') PORT_CHAR(0x0d)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR(0x02)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED) //B
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(0x0e)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED) //N

	PORT_START("KEY.3")    /* line 3 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("' \"") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(0x27) PORT_CHAR(0x22) PORT_CHAR(0x27)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR(0x0c)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(0x0d)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("; :") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') PORT_CHAR(0x0a)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR(0x07)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR(0x0b)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') PORT_CHAR(0x08)

	PORT_START("KEY.4")    /* line 4 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 $") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 @") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 %") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 #") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL) PORT_CHAR(0x7f) PORT_CHAR(0x7f) PORT_CHAR(0x1f)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(0x1b)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 !") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("INS") PORT_CODE(KEYCODE_INSERT)

	PORT_START("KEY.5")    /* line 5 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[ {") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{') PORT_CHAR(0x1b)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(0x0f)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("] }") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}') PORT_CHAR(0x1d)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR(0x10)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(0x15)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(0x14)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') PORT_CHAR(0x09)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR(0x19)

	PORT_START("KEY.6")    /* line 6 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR(0x06)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(0x18)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR(0x16)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR(0x03)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR(0x04)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR(0x13)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(0x1a)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED) //Z

	PORT_START("KEY.7")    /* line 7 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("= +") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+') PORT_CHAR('=')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0 )") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(0x08)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("- _") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 *") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 ^") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 (") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 &") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
INPUT_PORTS_END

void excali64_state::cent_busy_w(int state)
{
	m_centronics_busy = state;
}

void excali64_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_EXCALI64_FORMAT);
}

static void excali64_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

// pulses from port E4 bit 5 restart the 74123. After 3.6 secs without a pulse, the motor gets turned off.
void excali64_state::motor_w(int state)
{
	m_motor = state;
	m_floppy1->get_device()->mon_w(!m_motor);
	m_floppy0->get_device()->mon_w(!m_motor);
}

u8 excali64_state::porte8_r()
{
	return 0xfc | (u8)m_motor;
}

void excali64_state::porte4_w(u8 data)
{
	floppy_image_device *floppy = nullptr;
	if (BIT(data, 0))
		floppy = m_floppy0->get_device();

	if (BIT(data, 1))
		floppy = m_floppy1->get_device();

	m_fdc->set_floppy(floppy);
	if (floppy)
		floppy->ss_w(BIT(data, 4));

	m_u12->b_w(BIT(data, 5)); // motor pulse
}

/*
d0 = precomp (selectable by jumper)
d1 = size select
d2 = density select (0 = double)
*/
void excali64_state::portec_w(u8 data)
{
	m_fdc->enmf_w(BIT(data, 1));
	m_fdc->dden_w(BIT(data, 2));
}

u8 excali64_state::memory_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	return prog_space.read_byte(offset);
}

void excali64_state::memory_write_byte(offs_t offset, u8 data)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	prog_space.write_byte(offset, data);
}

u8 excali64_state::io_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_IO);
	return prog_space.read_byte(offset);
}

void excali64_state::io_write_byte(offs_t offset, u8 data)
{
	address_space& prog_space = m_maincpu->space(AS_IO);
	prog_space.write_byte(offset, data);
}

void excali64_state::ppib_w(u8 data)
{
	m_kbdrow = data;
}

u8 excali64_state::ppic_r()
{
	u8 data = 0xf4; // READY line must be low to print
	data |= (u8)m_centronics_busy;
	data |= (m_cass->input() > 0.1) << 3;
	return data;
}

void excali64_state::ppic_w(u8 data)
{
	m_cass->output(BIT(data, 7) ? -1.0 : +1.0);
	m_centronics->write_strobe(BIT(data, 4));
}

u8 excali64_state::port00_r()
{
	u8 data = 0xff;

	for (int i = 0; i < 8; i++)
	{
		if (!BIT(m_kbdrow, i))
			data &= m_io_keyboard[i]->read();
	}

	return data;
}

/*
d0 : /rom ; screen
d1 : ram on
d2 : /low ; high res
d3 : 2nd colour set (previously, dispen, which is a mistake in hardware and schematic)
d4 : vsync
d5 : rombank
*/
u8 excali64_state::port50_r()
{
	u8 data = m_sys_status & 0x2f;
	bool csync = m_crtc_hs | m_crtc_vs;
	data |= (u8)csync << 4;
	return data;
}

/*
d0,1,2,3,5 : same as port50
(schematic wrongly says d7 used for 2nd colour set)
*/
void excali64_state::port70_w(u8 data)
{
	m_sys_status = data;
	m_crtc->set_unscaled_clock(BIT(data, 2) ? 2e6 : 1e6);
	if (BIT(data, 1))
	{
		// select 64k ram
		m_bankr[0]->set_entry(0);
		m_bankr[1]->set_entry(0);
		m_bankr[2]->set_entry(0);
		m_bankr[3]->set_entry(0);

		m_bankw[1]->set_entry(0);
		m_bankw[2]->set_entry(0);
		m_bankw[3]->set_entry(0);
	}
	else if (BIT(data, 0))
	{
		// select videoram and hiresram
		m_bankr[0]->set_entry(1);
		m_bankr[1]->set_entry(2);
		m_bankr[2]->set_entry(2);
		m_bankr[3]->set_entry(2);

		m_bankw[1]->set_entry(2);
		m_bankw[2]->set_entry(2);
		m_bankw[3]->set_entry(2);
	}
	else
	{
		// select rom, videoram, and main ram
		m_bankr[0]->set_entry(1);
		m_bankr[1]->set_entry(1);
		m_bankr[2]->set_entry(1);
		m_bankr[3]->set_entry(0);

		m_bankw[1]->set_entry(2);
		m_bankw[2]->set_entry(2);
		m_bankw[3]->set_entry(0);
	}

	// other half of ROM_1
	if ((data & 0x22) == 0x20)
		m_bankr[0]->set_entry(2);
}

void excali64_state::machine_reset()
{
	m_bankr[0]->set_entry(1); // read from ROM
	m_bankr[1]->set_entry(1); // read from ROM
	m_bankr[2]->set_entry(1); // read from ROM
	m_bankr[3]->set_entry(0); // read from RAM

	m_bankw[0]->set_entry(0); // write to RAM
	m_bankw[1]->set_entry(2); // write to videoram
	m_bankw[2]->set_entry(2); // write to videoram hires pointers
	m_bankw[3]->set_entry(0); // write to RAM

	m_maincpu->reset();
}

void excali64_state::machine_start()
{
	save_pointer(NAME(m_vram), 0x2000);
	save_pointer(NAME(m_hram), 0x8000);
	save_pointer(NAME(m_ram),  0xc000);
	save_item(NAME(m_sys_status));
	save_item(NAME(m_kbdrow));
	save_item(NAME(m_crtc_vs));
	save_item(NAME(m_crtc_hs));
	save_item(NAME(m_motor));
	save_item(NAME(m_centronics_busy));

	m_sys_status = 0;
}
void excali64_state::crtc_hs(int state)
{
	m_crtc_hs = state;
}

void excali64_state::crtc_vs(int state)
{
	m_crtc_vs = state;
}

/* F4 Character Displayer */
static const gfx_layout excali64_charlayout =
{
	8, 12,                  /* 8 x 12 characters */
	256,                    /* 256 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_excali64 )
	GFXDECODE_ENTRY( "chargen", 0x0000, excali64_charlayout, 0, 1 )
GFXDECODE_END

// The prom, the schematic, and the manual all contradict each other,
// so the colours can only be described as wild guesses. Further, the 38
// colour-load resistors are missing labels and values.
void excali64_state::excali64_palette(palette_device &palette)
{
	// do this here because driver_init hasn't run yet
	m_vram = make_unique_clear<u8[]>(0x2000);
	m_hram = make_unique_clear<u8[]>(0x8000);
	m_ram = make_unique_clear<u8[]>(0xc000);
	u8 *v = m_vram.get();
	u8 *h = m_hram.get();
	u8 *r = m_ram.get();
	u8 *main = memregion("roms")->base();

	// main ram (cp/m mode)
	m_bankr[0]->configure_entry(0, r);
	m_bankr[1]->configure_entry(0, r+0x2000);
	m_bankr[2]->configure_entry(0, r+0x3000);
	m_bankr[3]->configure_entry(0, r+0x4000);//boot
	m_bankw[0]->configure_entry(0, r);//boot
	m_bankw[1]->configure_entry(0, r+0x2000);
	m_bankw[2]->configure_entry(0, r+0x3000);
	m_bankw[3]->configure_entry(0, r+0x4000);//boot
	// rom_1
	m_bankr[0]->configure_entry(1, &main[0x0000]);//boot
	m_bankr[0]->configure_entry(2, &main[0x2000]);
	// rom_2
	m_bankr[1]->configure_entry(1, &main[0x4000]);//boot
	m_bankr[2]->configure_entry(1, &main[0x5000]);//boot
	// videoram
	m_bankr[1]->configure_entry(2, v);
	m_bankw[1]->configure_entry(2, v);//boot
	// hiresram
	m_bankr[2]->configure_entry(2, v+0x1000);
	m_bankw[2]->configure_entry(2, v+0x1000);//boot
	m_bankr[3]->configure_entry(2, h);
	m_bankw[3]->configure_entry(2, h);

	// Set up foreground colours
	for (u8 i = 0; i < 32; i++)
	{
		u8 const code = m_p_chargen[0x1000+i];
		u8 const r = (BIT(code, 0) ? 38 : 0) + (BIT(code, 1) ? 73 : 0) + (BIT(code, 2) ? 144 : 0);
		u8 const b = (BIT(code, 3) ? 38 : 0) + (BIT(code, 4) ? 73 : 0) + (BIT(code, 5) ? 144 : 0);
		u8 const g = (BIT(code, 6) ? 85 : 0) + (BIT(code, 7) ? 170 : 0);
		palette.set_pen_color(i, r, g, b);
	}

	// Background
	palette.set_pen_color(32, 0x00, 0x00, 0x00);  //  0 Black
	palette.set_pen_color(33, 0xff, 0x00, 0x00);  //  1 Red
	palette.set_pen_color(34, 0x00, 0x00, 0xff);  //  2 Blue
	palette.set_pen_color(35, 0xff, 0x00, 0xff);  //  3 Magenta
	palette.set_pen_color(36, 0x00, 0xff, 0x00);  //  4 Green
	palette.set_pen_color(37, 0xff, 0xff, 0x00);  //  5 Yellow
	palette.set_pen_color(38, 0x00, 0xff, 0xff);  //  6 Cyan
	palette.set_pen_color(39, 0xff, 0xff, 0xff);  //  7 White
}

MC6845_UPDATE_ROW( excali64_state::update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u8 const col_base = BIT(m_sys_status, 3) ? 16 : 0;
	u32 *p = &bitmap.pix(y);

	for (u16 x = 0; x < x_count; x++)
	{
		u16 const mem = (ma + x) & 0x7ff;
		u8 const chr = m_vram[mem];
		u8 const col = m_vram[mem+0x800];
		u8 const fg = col_base + (col >> 4);
		u8 const bg = 32 + ((col >> 1) & 7);

		u8 gfx;
		if (BIT(col, 0))
		{
			u8 h = m_vram[mem+0x1000] - 4;
			if (h > 5)
				h = 0; // keep us in bounds
			// hires definition - pixels are opposite order to characters
			gfx = bitswap<8>(m_hram[(h << 12) | (chr<<4) | ra], 0, 1, 2, 3, 4, 5, 6, 7);
		}
		else
			gfx = m_p_chargen[(chr<<4) | ra]; // normal character

		gfx ^= (x == cursor_x) ? 0xff : 0;

		/* Display a scanline of a character */
		*p++ = palette[BIT(gfx, 0) ? fg : bg];
		*p++ = palette[BIT(gfx, 1) ? fg : bg];
		*p++ = palette[BIT(gfx, 2) ? fg : bg];
		*p++ = palette[BIT(gfx, 3) ? fg : bg];
		*p++ = palette[BIT(gfx, 4) ? fg : bg];
		*p++ = palette[BIT(gfx, 5) ? fg : bg];
		*p++ = palette[BIT(gfx, 6) ? fg : bg];
		*p++ = palette[BIT(gfx, 7) ? fg : bg];
	}
}

void excali64_state::excali64(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &excali64_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &excali64_state::io_map);
	m_maincpu->busack_cb().set(m_dma, FUNC(z80dma_device::bai_w));

	I8251(config, "uart", 0);
	//uart.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	//uart.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	pit8253_device &pit(PIT8253(config, "pit", 0));
	pit.set_clk<0>(16_MHz_XTAL / 16); /* Timer 0: tone gen for speaker */
	pit.out_handler<0>().set("speaker", FUNC(speaker_sound_device::level_w));
	//pit.set_clk<1>(16_MHz_XTAL / 16); /* Timer 1: baud rate gen for 8251 */
	//pit.out_handler<1>().set(FUNC(excali64_state::write_uart_clock));
	//pit.set_clk<2>(16_MHz_XTAL / 16); /* Timer 2: not used */

	i8255_device &ppi(I8255A(config, "ppi"));
	ppi.out_pa_callback().set("cent_data_out", FUNC(output_latch_device::write)); // parallel port
	ppi.out_pb_callback().set(FUNC(excali64_state::ppib_w));
	ppi.in_pc_callback().set(FUNC(excali64_state::ppic_r));
	ppi.out_pc_callback().set(FUNC(excali64_state::ppic_w));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	/* Video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(80*8, 24*12);
	screen.set_visarea_full();
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	PALETTE(config, m_palette, FUNC(excali64_state::excali64_palette), 40);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_excali64);

	MC6845(config, m_crtc, 16_MHz_XTAL / 16); // 1MHz for lowres; 2MHz for highres
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(excali64_state::update_row));
	m_crtc->out_hsync_callback().set(FUNC(excali64_state::crtc_hs));
	m_crtc->out_vsync_callback().set(FUNC(excali64_state::crtc_vs));

	/* Devices */
	CASSETTE(config, m_cass);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);

	WD2793(config, m_fdc, 16_MHz_XTAL / 8);
	m_fdc->drq_wr_callback().set(m_dma, FUNC(z80dma_device::rdy_w));
	FLOPPY_CONNECTOR(config, "fdc:0", excali64_floppies, "525qd", excali64_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", excali64_floppies, "525qd", excali64_state::floppy_formats).enable_sound(true);

	Z80DMA(config, m_dma, 16_MHz_XTAL / 4);
	m_dma->out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	m_dma->in_mreq_callback().set(FUNC(excali64_state::memory_read_byte));
	m_dma->out_mreq_callback().set(FUNC(excali64_state::memory_write_byte));
	m_dma->in_iorq_callback().set(FUNC(excali64_state::io_read_byte));
	m_dma->out_iorq_callback().set(FUNC(excali64_state::io_write_byte));

	TTL74123(config, m_u12, 0);
	m_u12->set_connection_type(TTL74123_GROUNDED);  /* Hook up type (no idea what this means) */
	m_u12->set_resistor_value(RES_K(100));          /* resistor connected between RCext & 5v */
	m_u12->set_capacitor_value(CAP_U(100));         /* capacitor connected between Cext and RCext */
	m_u12->set_a_pin_value(0);                      /* A pin - grounded */
	m_u12->set_b_pin_value(1);                      /* B pin - driven by port e4 bit 5 */
	m_u12->set_clear_pin_value(1);                  /* Clear pin - pulled high */
	m_u12->out_cb().set(FUNC(excali64_state::motor_w));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(excali64_state::cent_busy_w));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	SOFTWARE_LIST(config, "flop_list").set_original("excalibur64");
}

/* ROM definition */
ROM_START( excali64 )
	ROM_REGION(0x6000, "roms", 0)
	ROM_LOAD( "rom_1.ic17", 0x0000, 0x4000, CRC(e129a305) SHA1(e43ec7d040c2b2e548d22fd6bbc7df8b45a26e5a) )
	ROM_LOAD( "rom_2.ic24", 0x4000, 0x2000, CRC(916d9f5a) SHA1(91c527cce963481b7bebf077e955ca89578bb553) )
	// fix a bug that causes screen to be filled with 'p'
	ROM_FILL(0x4ee, 1, 0)
	ROM_FILL(0x4ef, 1, 8)
	ROM_FILL(0x4f6, 1, 0)
	ROM_FILL(0x4f7, 1, 8)
	// patch out the protection
	ROM_FILL(0x3ce7, 1, 0)

	ROM_REGION(0x1020, "chargen", 0)
	ROM_LOAD( "genex_3.ic43", 0x0000, 0x1000, CRC(b91619a9) SHA1(2ced636cb7b94ba9d329868d7ecf79963cefe9d9) )
	ROM_LOAD( "hm7603.ic55",  0x1000, 0x0020, CRC(c74f47dc) SHA1(331ff3c913846191ddd97cacb80bd19438c1ff71) )
ROM_END

} // Anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY          FULLNAME        FLAGS
COMP( 1984, excali64, 0,      0,      excali64, excali64, excali64_state, empty_init, "BGR Computers", "Excalibur 64", MACHINE_SUPPORTS_SAVE )



excalibur.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Mephisto Excalibur

It's a very rare chess computer, the chess engine was programmed by the same guys
that did Mephisto I/II/III and Mephisto III-S Glasgow.

Hardware notes:

Motherboard:
- PCB label: FORCE COMPUTERS
- HD68000-8 @ 8 MHz (16MHz XTAL)
- 128KB DRAM (16*MB8264-20), half unused
- 4 EPROM sockets are unpopulated
- HD6840 PTM @ 800kHz, HD6821P PIA
- MC14411P (set to 9600 baud), 1.8432MHz XTAL
- 2*HD6850P ACIA looped back to itself, both unused

Interface board:
- PCB label: HGS 15 101 00 B
- edge connector to module with 64KB ROM (4*27128)

Brikett:
- stripped down, only the LCD/keypad PCB
- 4-digit 7seg LCD
- keypad, piezo

The wooden ESB type chessboard is the same as the one for ESB II/Mephisto III,
except the interface is more similar to Mephisto Exclusive.

The motherboard is a 1983 Force Computers 68000 kit with lots of patches and wire
mods. It's an older version of the VME in src/devices/bus/vme/sys68k_cpu1.cpp.

The 68000 is clocked at 8MHz, but due to wait states and bus requests, it runs
much slower, closer to around 5MHz.

There's a 555 timer (R1=11K, R2=22K, C=1nf: ~26kHz) that clocks 68000 BR. Tests
on real hardware showed that the chess computer is about 30% faster if the timer
interval is reduced by a factor 100. It still worked fine, so it's probably a
leftover for VME comms, not the DRAM refresh circuit.

Other possible sources of wait states are periodic DRAM refresh and user/system
area access time.

See glasgow.cpp on how to verify CPU speed. On the real thing after 6 minutes,
number of positions is 2026 for excal, and 2028 for excaltm.

TODO:
- verify CPU speed, see notes above

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

#include "emu.h"

#include "mmboard.h"
#include "mmdisplay1.h"

#include "cpu/m68000/m68000.h"
#include "machine/6821pia.h"
#include "machine/6840ptm.h"
#include "sound/dac.h"

#include "speaker.h"

// internal artwork
#include "mephisto_excalibur.lh"


namespace {

class excal_state : public driver_device
{
public:
	excal_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ptm(*this, "ptm"),
		m_pia(*this, "pia"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(reset_button);

	void excal(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<ptm6840_device> m_ptm;
	required_device<pia6821_device> m_pia;
	required_device<mephisto_board_device> m_board;
	required_device<mephisto_display1_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<4> m_inputs;

	void excal_mem(address_map &map) ATTR_COLD;

	u8 input_r(offs_t offset);
};

void excal_state::machine_start()
{
	// HACK: slow down CPU to account for bus arbiter and wait states
	m_maincpu->set_clock_scale(5.0 / 8.0);
}



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

INPUT_CHANGED_MEMBER(excal_state::reset_button)
{
	// reset system (same function as SW1 on the motherboard)
	if (newval)
	{
		m_ptm->reset();
		m_pia->reset();
		m_maincpu->reset();
	}
}

u8 excal_state::input_r(offs_t offset)
{
	u8 data = 0;

	// read keypad
	for (int i = 0; i < 4; i++)
		if (BIT(~offset, i))
			data |= m_inputs[i]->read();

	return data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void excal_state::excal_mem(address_map &map)
{
	map(0x000000, 0x01ffff).ram();
	map(0x000000, 0x000007).rom();
	map(0x020000, 0x02ffff).rom().region("maincpu", 0);
	map(0x04c000, 0x04c00f).mirror(0x003ff0).rw(m_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write)).umask16(0x00ff);
	map(0x05c000, 0x05c007).mirror(0x003ff8).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write)).umask16(0x00ff);

	map(0x100000, 0x100001).mirror(0x0ffffe).r(m_board, FUNC(mephisto_board_device::input_r)).umask16(0x00ff);
	map(0x200000, 0x200001).mirror(0x0ffffe).w(m_board, FUNC(mephisto_board_device::mux_w)).umask16(0xff00);
	map(0x300000, 0x300001).mirror(0x0ffffe).w(m_board, FUNC(mephisto_board_device::led_w)).umask16(0xff00);
	map(0x400000, 0x40001f).mirror(0x0fffe0).r(FUNC(excal_state::input_r)).umask16(0x00ff);
	map(0x800000, 0x800001).mirror(0x0ffffe).w(m_display, FUNC(mephisto_display1_device::data_w)).umask16(0xff00);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( excal )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_NAME("CL")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("A / 1")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("ENT")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("B / 2 / Pawn")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("INFO")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("C / 3 / Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("LEV")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("D / 4 / Bishop")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("POS")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("E / 5 / Rook")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Left / Black / 9")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("F / 6 / Queen")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Right / White / 0")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("G / 7 / King")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("MEM")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("H / 8")

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("RES") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(excal_state::reset_button), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void excal_state::excal(machine_config &config)
{
	// basic machine hardware
	M68000(config, m_maincpu, 16_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &excal_state::excal_mem);

	PTM6840(config, m_ptm, 16_MHz_XTAL / 20);
	m_ptm->irq_callback().set_inputline(m_maincpu, M68K_IRQ_4);

	PIA6821(config, m_pia);
	m_pia->writepa_handler().set(m_display, FUNC(mephisto_display1_device::common_w)).bit(0);
	m_pia->writepa_handler().append(m_dac, FUNC(dac_1bit_device::write)).bit(1);

	MEPHISTO_SENSORS_BOARD(config, m_board);
	m_board->set_delay(attotime::from_msec(200));

	// video hardware
	MEPHISTO_DISPLAY_MODULE1(config, m_display);
	config.set_default_layout(layout_mephisto_excalibur);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( excal )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("1u_215_22.10.u1", 0x00000, 0x04000, CRC(f4b16b35) SHA1(778b1ece0fc5db88b667d550a55cb757c1280d4d) )
	ROM_LOAD16_BYTE("1l_215_22.10.l1", 0x00001, 0x04000, CRC(372639b0) SHA1(fb56a19689e164175a3db10faf24ab3360264b7c) )
	ROM_LOAD16_BYTE("2u_215_22.10.u2", 0x08000, 0x04000, CRC(ad039672) SHA1(4dc80600bcc7ea450102f2d0eb25be644e5e542c) )
	ROM_LOAD16_BYTE("2l_215_22.10.l2", 0x08001, 0x04000, CRC(08dc7409) SHA1(6f7a336c615ff40dd4018a2150c3213bc7e7e1dc) )
ROM_END

ROM_START( excaltm )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("excalibur_wccc_1983_1u.u1", 0x00000, 0x04000, CRC(70c420c0) SHA1(11130d910dbc062da224ca24d020f34e95e299a9) )
	ROM_LOAD16_BYTE("excalibur_wccc_1983_1l.l1", 0x00001, 0x04000, CRC(5ff3c9f7) SHA1(6d408f9709a7b763215a6bf1c7199d9b54f89d79) )
	ROM_LOAD16_BYTE("excalibur_wccc_1983_2u.u2", 0x08000, 0x04000, CRC(5d899bcc) SHA1(d9dbdc1b05a86664bb22489c812ca347b44c6395) )
	ROM_LOAD16_BYTE("excalibur_wccc_1983_2l.l2", 0x08001, 0x04000, CRC(576d2e6e) SHA1(58b8018996442b4b957d64631c0701be64dfc382) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY, FULLNAME, FLAGS
SYST( 1983, excal,   0,      0,      excal,   excal, excal_state, empty_init, "Hegener + Glaser", "Mephisto Excalibur", MACHINE_SUPPORTS_SAVE )
SYST( 1983, excaltm, excal,  0,      excal,   excal, excal_state, empty_init, "Hegener + Glaser", "Mephisto Excalibur (WCCC 1983 New York TM)", MACHINE_SUPPORTS_SAVE )



excel.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger, yoyo_chessboard
/*******************************************************************************

Fidelity Excellence series hardware
(for Excel 68000, see eag68k.cpp)

TODO:
- verify if Designer 2100 CPU is 5MHz or 6MHz
- granits chessboard buttons seem too sensitive (detects input on falling edge if
  held too long), probably BTANB?

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

The Excellence (model EP12)
---------------------------

Hardware notes:
- PCB label: 510-1099A01
- GTE G65SC102P-3 @ 3MHz (12MHz XTAL)
- 16KB ROM, 2KB RAM
- piezo, 16 LEDs, 8*8 chessboard buttons

Model 6080 is on the same hardware as EP12, and model 6080B (510.1117A02 PCB) has
more memory (32KB ROM, 8KB RAM).

One interesting clone of The Excellence is the Computerchess Playmate-2. It was
produced in 1989 by SPS(Bulgaria) and RRR(Riga Radio Factory). The chess program
ROM is identical to Excellence EP12 (the 101-1072A01 one). All internal circuitry
is the same, the only difference is the capacitor driving the 555 for IRQ is 10nf
instead of 22nf.

What makes it unique is the addition of a chess clock.

connector pinout from main pcb:
1) 5V
2) GND
3) 74HC259.pin9 (Q4) = Row LED driving
4) 74HC259.pin10 (Q5) = Column LED driving
5) 74HC259.pin12 (Q7) = Bat. Low signal
6) 74HC42.pin4 (Q3) = Col-D/Row-4 -> 'White Move' if D-LED blinks

The extra board has a 7474, a К1016ХЛ1 (RTC, or MCU clock driver), a 4-digit VFD,
and some buttons for controlling the clock. IRQ frequency is doubled presumedly
for using the blinking led as seconds counter. It only tracks player time, not
of the opponent. And it obviously doesn't show chessmove coordinates either.

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

Par Excellence family
---------------------

Hardware notes:

The Par Excellence (model 6083):
- PCB label: 510-1099A01
- Rockwell R65C02P4, 5MHz XTAL
- 8KB RAM(HM6264AP-10), 32KB ROM(AMI 101.1077A01)
- piezo, 16 LEDs, 8*8 chessboard buttons

Designer 2000 (model 6102):
- PCB label 510.1129A01
- Ricoh RP65C02G, 3MHz XTAL
- rest is same as Par Excellence

Designer 2000 is basically same as (Par) Excellence hardware, reskinned board.
They removed low-voltage detection from the PCB, but low battery signal still
works in software. Designer 2100 (model 6103) is almost the same, with a faster
CPU (5MHz WDC 65C02, same frequency as Par Excellence).

(Designer 1500 is on 80C50 hardware, same ROM as The Classic, see sc6.cpp)

RCS Granit S:
- PCB label: 510-1084B01 (SC12B PCB)
- Rockwell R65C02P4, 8MHz XTAL, ~6.4V overvoltage
- SC12B module slot is not functional
- rest is same as Par Excellence

Granit S is a heavily modified SC12B (see sc12.cpp) by Peter Reckwitz. The CPU
is overclocked a lot, the dynamic clock divider was removed, and wire mods were
added to make the hardware similar to Par Excellence. The program is based on
Par Excellence.

The SC12B labels don't make much sense here, like the ones on the bottom left,
or the check and illegal status LEDs. The PB button is Verify, and the PV button
is for Options. A strip was added at the bottom for the status LED labels.

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

Voice Excellence (model 6092)
-----------------------------

Hardware notes:

Main PCB:
- PCB label: 510.1117A02, appears to be identical to The Excellence 6080B PCB
- GTE G65SC102P-3 @ 3MHz (12MHz XTAL)
- 32 KB PRG ROM: AMI 101-1080A01(IC5), 8192x8 SRAM SRM2264C10(IC6)
- speaker, 16 LEDs, 8*8 chessboard buttons

Voice PCB:
- PCB label: 510.1117A01
- 32 KB ROM: AMI 101-1081A01(IC2)
- TSI S14001A
- DIP Switches set ROM A13 and ROM A14, on the side of the board

ROM A12 is tied to S14001A's A11 (yuck)
ROM A11 is however tied to the CPU's XYZ

0000_07FF - Spanish 1/4
0800_0FFF - Spanish 3/4
1000_17FF - Spanish 2/4
1800_1FFF - Spanish 4/4

2000_27FF - French 1/4
2800_2FFF - French 3/4
3000_3FFF - French 2/4
3800_3FFF - French 4/4

4000_47FF - German 1/4
4800_4FFF - German 3/4
5000_57FF - German 2/4
5800_5FFF - German 4/4

6000_67FF - English 1/2
6800_6FFF - Bridge Challenger 1/2
7000_77FF - English 2/2
7800_7FFF - Bridge Challenger 2/2

------------------
RE info by hap, based on PCB photos

Memory map:
-----------
0000-3FFF: 8K RAM (SRM2264)
4000-7FFF: control (R/W)
8000-FFFF: 32K ROM (M27256 compatible)

control (W):
------------
CPU A0-A2 to 3*74259, CPU Dx to D (_C unused)

CPU D0:
- Q4,Q5: led commons
- Q6,Q7,Q2,Q1: 7seg panel digit select
- Q0-Q3: 7442 A0-A3
  + 0-7: led data
  + 0-8: keypad mux
  + 9: buzzer out

CPU D1: (model 6093)
- Q0-Q7: 7seg data

CPU D2: (model 6092)
- Q0-Q5: TSI C0-C5
- Q6: TSI START pin
- Q7: TSI ROM A11

A11 from TSI is tied to TSI ROM A12(!)
TSI ROM A13,A14 are hardwired to the 2 language switches.
Sound comes from the Audio out pin, digital out pins are N/C.

control (R):
------------
CPU A0-A2 to 2*74251, CPU Dx to output

CPU D7 to Y:
- D0-D7: keypad row data

CPU D6 to W: (model 6092, tied to VCC otherwise)
- D0,D1: language switches
- D2-D6: VCC
- D7: TSI BUSY

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

#include "emu.h"

#include "cpu/m6502/g65sc02.h"
#include "cpu/m6502/r65c02.h"
#include "machine/clock.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "sound/s14001a.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "fidel_ex.lh"
#include "fidel_exb.lh"
#include "fidel_exd.lh"
#include "fidel_exv.lh"
#include "granits.lh"


namespace {

class excel_state : public driver_device
{
public:
	excel_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_speech(*this, "speech"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void init_fexcelv();
	void init_granits() { m_invert = 0xff; }

	// machine configs
	void fexcel(machine_config &config);
	void fexcelb(machine_config &config);
	void fexcel4(machine_config &config);
	void fexceld(machine_config &config);
	void fexcelv(machine_config &config);
	void fexcelp(machine_config &config);
	void granits(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(speech_bankswitch);
	DECLARE_INPUT_CHANGED_MEMBER(fexcelp_change_cpu_freq);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	optional_device<s14001a_device> m_speech;
	optional_ioport_array<3> m_inputs;

	u8 m_invert = 0;
	u8 m_select = 0;
	u8 m_7seg_data = 0;
	u8 m_speech_data = 0;
	u8 m_speech_bank = 0;

	// address maps
	void fexcel_map(address_map &map) ATTR_COLD;
	void fexcelb_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void ttl_w(offs_t offset, u8 data);
	u8 ttl_r(offs_t offset);
	void speech_w(offs_t offset, u8 data);
};

void excel_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_select));
	save_item(NAME(m_7seg_data));
	save_item(NAME(m_speech_data));
	save_item(NAME(m_speech_bank));
}

INPUT_CHANGED_MEMBER(excel_state::fexcelp_change_cpu_freq)
{
	m_maincpu->set_unscaled_clock((newval & 1) ? 5_MHz_XTAL : 3_MHz_XTAL);
}



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

// TTL

void excel_state::ttl_w(offs_t offset, u8 data)
{
	// a0-a2,d0: 74259(1)
	u8 mask = 1 << offset;
	m_select = (m_select & ~mask) | ((data & 1) ? mask : 0);

	// 74259 Q0-Q3: 7442 a0-a3
	// 7442 0-8: led data, input mux
	u16 sel = 1 << (m_select & 0xf) & 0x3ff;
	u8 led_data = sel & 0xff;

	// 7442 9: speaker out
	m_dac->write(BIT(sel, 9));

	// 74259 Q4-Q7,Q2,Q1: digit/led select (active low)
	u8 led_sel = ~bitswap<8>(m_select,0,3,1,2,7,6,5,4) & 0x3f;

	// a0-a2,d1: digit segment data (model 6093)
	m_7seg_data = (m_7seg_data & ~mask) | ((data & 2) ? mask : 0);
	u8 seg_data = bitswap<8>(m_7seg_data,0,1,3,2,7,5,6,4);

	// update display: 4 7seg leds, 2*8 chessboard leds
	m_display->matrix_partial(0, 2, led_sel, led_data);
	m_display->matrix_partial(2, 4, led_sel >> 2, seg_data); // 6093

	// speech (model 6092)
	if (m_speech != nullptr)
		speech_w(offset, data);
}

u8 excel_state::ttl_r(offs_t offset)
{
	u8 sel = m_select & 0xf;
	u8 d7 = 0x80;
	u8 data = 0;

	// 74259(1) Q7 + 74251 I0: battery status
	if (m_inputs[2] != nullptr && sel == 0 && ~m_select & 0x80)
		d7 = m_inputs[2]->read() & 0x80;

	// a0-a2,d6: from speech board: language switches and S14001A busy pin, otherwise tied to VCC
	u8 d6 = (m_inputs[1].read_safe(0xff) >> offset & 1) ? 0x40 : 0;

	// a0-a2,d7: multiplexed inputs (active low)
	// read chessboard sensors
	if (sel < 8)
		data = m_board->read_file(sel) ^ m_invert;

	// read button panel
	else if (sel == 8)
		data = m_inputs[0]->read();

	return ((data >> offset & 1) ? 0 : d7) | d6 | 0x3f;
}


// speech (fexcelv)

void excel_state::init_fexcelv()
{
	u8 *rom = memregion("speech")->base();
	const u32 len = memregion("speech")->bytes();
	assert(len == 0x8000);

	// program controls A11, user controls A13,A14(language switches)
	std::vector<u8> buf(len);
	memcpy(&buf[0], rom, len);
	for (int i = 0; i < len; i++)
		rom[i] = buf[((i & 0x67ff) | bitswap<2>(i,11,12) << 11) ^ 0x6000];
}

INPUT_CHANGED_MEMBER(excel_state::speech_bankswitch)
{
	// tied to speech ROM highest bits
	m_speech_bank = (m_speech_bank & 1) | (newval << 1 & 6);
	m_speech->set_rom_bank(m_speech_bank);
}

void excel_state::speech_w(offs_t offset, u8 data)
{
	// a0-a2,d2 (from ttl_w): 74259(2) to speech board
	u8 mask = 1 << offset;
	m_speech_data = (m_speech_data & ~mask) | ((data & 4) ? mask : 0);

	// 74259 Q6: speech ROM A11
	m_speech_bank = (m_speech_bank & ~1) | BIT(m_speech_data, 6);
	m_speech->set_rom_bank(m_speech_bank);

	// Q0-Q5: S14001A C0-C5
	// Q7: S14001A start pin
	m_speech->data_w(m_speech_data & 0x3f);
	m_speech->start_w(BIT(m_speech_data, 7));
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void excel_state::fexcel_map(address_map &map)
{
	map(0x0000, 0x07ff).mirror(0x3800).ram();
	map(0x4000, 0x4007).mirror(0x3ff8).rw(FUNC(excel_state::ttl_r), FUNC(excel_state::ttl_w));
	//map(0x8000, 0x8000).nopr(); // checks for opening book module, but hw doesn't have a module slot
	map(0xc000, 0xffff).rom();
}

void excel_state::fexcelb_map(address_map &map)
{
	map(0x0000, 0x1fff).mirror(0x2000).ram();
	map(0x4000, 0x4007).mirror(0x3ff8).rw(FUNC(excel_state::ttl_r), FUNC(excel_state::ttl_w));
	map(0x8000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( fexcelb )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Clear")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Move / Pawn")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Hint / Knight")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Take Back / Bishop")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Level / Rook")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Options / Queen")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Verify / King")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
INPUT_PORTS_END

static INPUT_PORTS_START( fexcelv )
	PORT_INCLUDE( fexcelb )

	PORT_START("IN.1")
	PORT_CONFNAME( 0x03, 0x00, DEF_STR( Language ) ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(excel_state::speech_bankswitch), 0)
	PORT_CONFSETTING(    0x00, DEF_STR( English ) )
	PORT_CONFSETTING(    0x01, DEF_STR( German ) )
	PORT_CONFSETTING(    0x02, DEF_STR( French ) )
	PORT_CONFSETTING(    0x03, DEF_STR( Spanish ) )
	PORT_BIT(0x7c, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("speech", FUNC(s14001a_device::busy_r))
INPUT_PORTS_END

static INPUT_PORTS_START( fexcel )
	PORT_INCLUDE( fexcelb )

	PORT_START("IN.2")
	PORT_CONFNAME( 0x80, 0x00, "Battery Status" )
	PORT_CONFSETTING(    0x80, "Low" )
	PORT_CONFSETTING(    0x00, DEF_STR( Normal ) )
INPUT_PORTS_END

static INPUT_PORTS_START( fexcelp )
	PORT_INCLUDE( fexcel )

	PORT_START("CPU")
	PORT_CONFNAME( 0x01, 0x01, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(excel_state::fexcelp_change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "3MHz (Designer 2000)" )
	PORT_CONFSETTING(    0x01, "5MHz (Par Excellence, Designer 2100)" )
INPUT_PORTS_END

static INPUT_PORTS_START( granits )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CL")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("RV / Pawn")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("DM / Knight")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("TB / Bishop")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("LV / Rook")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("PV / Queen") // options
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("PB / King") // verify
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_N) PORT_NAME("RE")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void excel_state::fexcel(machine_config &config)
{
	// basic machine hardware
	G65SC102(config, m_maincpu, 12_MHz_XTAL); // G65SC102P-3, 12.0M ceramic resonator (divided by 4 internally)
	m_maincpu->set_addrmap(AS_PROGRAM, &excel_state::fexcel_map);

	auto &irq_clock(CLOCK(config, "irq_clock", 600)); // from 556 timer (22nF, 102K, 1K), ideal frequency is 600Hz
	irq_clock.set_pulse_width(attotime::from_nsec(15250)); // active for 15.25us
	irq_clock.signal_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(100));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(2+4, 8);
	m_display->set_segmask(0x3c, 0x7f);
	config.set_default_layout(layout_fidel_ex);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void excel_state::fexcel4(machine_config &config)
{
	fexcel(config);

	// basic machine hardware
	R65C02(config.replace(), m_maincpu, 4_MHz_XTAL); // R65C02P4
	m_maincpu->set_addrmap(AS_PROGRAM, &excel_state::fexcel_map);
}

void excel_state::fexcelb(machine_config &config)
{
	fexcel(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &excel_state::fexcelb_map);
	config.set_default_layout(layout_fidel_exb);
}

void excel_state::fexceld(machine_config &config)
{
	fexcelb(config);

	config.set_default_layout(layout_fidel_exd);
}

void excel_state::fexcelv(machine_config &config)
{
	fexcelb(config);

	config.set_default_layout(layout_fidel_exv);

	// sound hardware
	S14001A(config, m_speech, 25000); // R/C circuit, around 25khz
	m_speech->add_route(ALL_OUTPUTS, "speaker", 0.75);
}

void excel_state::fexcelp(machine_config &config)
{
	fexcel(config);

	// basic machine hardware
	R65C02(config.replace(), m_maincpu, 5_MHz_XTAL); // R65C02P4
	m_maincpu->set_addrmap(AS_PROGRAM, &excel_state::fexcelb_map);
}

void excel_state::granits(machine_config &config)
{
	fexcelp(config);

	// basic machine hardware
	m_maincpu->set_clock(8_MHz_XTAL); // R65C02P4
	config.set_default_layout(layout_granits);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( fexcel ) // model 6080(B), PCB label 510.1117A02
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101-1080a01.ic5", 0x8000, 0x8000, CRC(846f8e40) SHA1(4e1d5b08d5ff3422192b54fa82cb3f505a69a971) )
ROM_END

ROM_START( fexceld ) // model 6093, PCB label 510.1117A02
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101-1080a01.ic5", 0x8000, 0x8000, CRC(846f8e40) SHA1(4e1d5b08d5ff3422192b54fa82cb3f505a69a971) ) // same rom as fexcel
ROM_END

ROM_START( fexcelv ) // model 6092, PCB label 510.1117A02, sound PCB 510.1117A01
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101-1080a01.ic5", 0x8000, 0x8000, CRC(846f8e40) SHA1(4e1d5b08d5ff3422192b54fa82cb3f505a69a971) ) // PCB1, M27256, same rom as fexcel

	ROM_REGION( 0x8000, "speech", 0 )
	ROM_LOAD("101-1081a01.ic2", 0x0000, 0x8000, CRC(c8ae1607) SHA1(6491ce6be60ed77f3dd931c0ca17616f13af943e) ) // PCB2, M27256
ROM_END

ROM_START( fexcel12 ) // model EP12, PCB label 510-1099A01
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101-1073a01.ic5", 0xc000, 0x4000, CRC(3e221534) SHA1(7516bc6a8aab9d8ac30ac1a9317630a6aa9ac1a0) )
ROM_END

ROM_START( fexcel12a ) // model EP12, PCB label 510-1099A01
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101-1072a01.ic5", 0xc000, 0x4000, CRC(212b006d) SHA1(242ff851b0841cbec66bbada6a730da021010e2c) )
ROM_END

ROM_START( fexcela ) // model 6080, PCB label 510-1099A01(manuf.1985) or 510-1099B01(manuf.1986)
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101-1072b01.ic5", 0xc000, 0x4000, CRC(fd2f6064) SHA1(f84bb98bdb9565a04891eb6820597d7aecc90c21) ) // RCA
ROM_END


ROM_START( fexcelp ) // model 6083, PCB label 510-1099A01
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101-1077a01.ic5", 0x8000, 0x8000, CRC(62006320) SHA1(1d6370973dbae42c54639b261cc81e32cdfc1d5d) ) // only 1 byte difference, assume bugfix in bookrom
ROM_END

ROM_START( fexcelpa ) // model 6083, PCB label 510-1099B01
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("white.ic5", 0x8000, 0x8000, CRC(0d17b0f0) SHA1(3a6070fd4718c62b62ff0f08637bb6eb84eb9a1c) ) // GI 27C256, no label
ROM_END


ROM_START( granits ) // modified SC12 board
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("granit_s-4.ic15", 0x8000, 0x8000, CRC(274d6aff) SHA1(c8d943b2f15422ac62f539b568f5509cbce568a3) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT   COMPAT  MACHINE   INPUT    CLASS        INIT          COMPANY, FULLNAME, FLAGS
SYST( 1987, fexcel,    0,       0,      fexcelb,  fexcelb, excel_state, empty_init,   "Fidelity International", "The Excellence (model 6080B)", MACHINE_SUPPORTS_SAVE )
SYST( 1987, fexceld,   fexcel,  0,      fexceld,  fexcelb, excel_state, empty_init,   "Fidelity International", "Excel Display", MACHINE_SUPPORTS_SAVE )
SYST( 1987, fexcelv,   fexcel,  0,      fexcelv,  fexcelv, excel_state, init_fexcelv, "Fidelity International", "Voice Excellence", MACHINE_SUPPORTS_SAVE )
SYST( 1985, fexcel12,  fexcel,  0,      fexcel4,  fexcel,  excel_state, empty_init,   "Fidelity International", "The Excellence (model EP12, set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1985, fexcel12a, fexcel,  0,      fexcel,   fexcel,  excel_state, empty_init,   "Fidelity International", "The Excellence (model EP12, set 2)", MACHINE_SUPPORTS_SAVE ) // 1st version of The Excellence
SYST( 1985, fexcela,   fexcel,  0,      fexcel,   fexcel,  excel_state, empty_init,   "Fidelity International", "The Excellence (model 6080)", MACHINE_SUPPORTS_SAVE )

SYST( 1986, fexcelp,   0,       0,      fexcelp,  fexcelp, excel_state, empty_init,   "Fidelity International", "The Par Excellence (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1986, fexcelpa,  fexcelp, 0,      fexcelp,  fexcelp, excel_state, empty_init,   "Fidelity International", "The Par Excellence (set 2)", MACHINE_SUPPORTS_SAVE )

SYST( 1986, granits,   fscc12,  0,      granits,  granits, excel_state, init_granits, "hack (Remote Control Systems)", "Granit S", MACHINE_SUPPORTS_SAVE )



exechess.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

SciSys Executive Chess, handheld chesscomputer.
Also known as Senator Chess in Germany.

Hardware notes:
- Fairchild 3870 MCU (variant with 4KB internal ROM)
- 1KB RAM (2*TC5514P)
- HLCD0538, HLCD0539, LCD screen

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

#include "emu.h"

#include "cpu/f8/f8.h"
#include "machine/f3853.h"
#include "video/hlcd0538.h"
#include "video/pwm.h"

#include "screen.h"

// internal artwork
#include "saitek_exechess.lh"


namespace {

class exechess_state : public driver_device
{
public:
	exechess_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_lcd1(*this, "lcd1"),
		m_lcd2(*this, "lcd2"),
		m_display(*this, "display"),
		m_battery(*this, "battery"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void exechess(machine_config &config);

	// battery status indicator is not software controlled
	DECLARE_INPUT_CHANGED_MEMBER(battery) { m_battery = newval; }

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<hlcd0538_device> m_lcd1;
	required_device<hlcd0539_device> m_lcd2;
	required_device<pwm_display_device> m_display;
	output_finder<> m_battery;
	required_ioport_array<4> m_inputs;

	std::unique_ptr<u8[]> m_ram;
	u8 m_ram_address[2] = { };
	u64 m_lcd_data[2] = { };

	// address maps
	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	// I/O handlers
	template<int N> void lcd_output_w(u64 data);
	void lcd_data_w(u8 data);

	u16 ram_address() { return (m_ram_address[1] << 8 | m_ram_address[0]) & 0x3ff; }
	template<int N> u8 ram_address_r();
	template<int N> void ram_address_w(u8 data);
	u8 ram_data_r();
	void ram_data_w(u8 data);
};

void exechess_state::machine_start()
{
	m_battery.resolve();
	m_ram = make_unique_clear<u8[]>(0x400);

	// register for savestates
	save_pointer(NAME(m_ram), 0x400);
	save_item(NAME(m_ram_address));
	save_item(NAME(m_lcd_data));
}



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

// LCD

template<int N>
void exechess_state::lcd_output_w(u64 data)
{
	m_lcd_data[N] = data;
	m_display->matrix(m_lcd_data[0] & 0xff, m_lcd_data[1] << 26 | m_lcd_data[0] >> 8);
}

void exechess_state::lcd_data_w(u8 data)
{
	// P40: HLCD0539 data
	// P44: HLCD0538 data
	m_lcd1->data_w(BIT(data, 4));
	m_lcd2->data_w(BIT(data, 0));

	if (~m_ram_address[1] & 4)
	{
		m_lcd1->clk_w(1); m_lcd1->clk_w(0);
		m_lcd2->clk_w(1); m_lcd2->clk_w(0);
	}
}


// 1KB RAM (port-mapped)

template<int N>
void exechess_state::ram_address_w(u8 data)
{
	// P00-P07: RAM A0-A7
	// P10-P11: RAM A8-A9
	// P12: RAM CE
	m_ram_address[N] = data;
}

template<int N>
u8 exechess_state::ram_address_r()
{
	u8 data = m_ram_address[N];

	// P13: Enter button
	return (N) ? data | (m_inputs[0]->read() & 8) : data;
}

void exechess_state::ram_data_w(u8 data)
{
	if (m_ram_address[1] & 4)
		m_ram[ram_address()] = data;
}

u8 exechess_state::ram_data_r()
{
	return (m_ram_address[1] & 4) ? m_ram[ram_address()] : 0;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void exechess_state::main_map(address_map &map)
{
	map.global_mask(0xfff);
	map(0x0000, 0x0fff).rom();
}

void exechess_state::main_io(address_map &map)
{
	map(0x00, 0x00).rw(FUNC(exechess_state::ram_address_r<0>), FUNC(exechess_state::ram_address_w<0>));
	map(0x01, 0x01).rw(FUNC(exechess_state::ram_address_r<1>), FUNC(exechess_state::ram_address_w<1>));
	map(0x04, 0x07).rw("psu", FUNC(f38t56_device::read), FUNC(f38t56_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( exechess )
	PORT_START("IN.0")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")
	PORT_BIT(0xf7, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.1")
	PORT_BIT(0x01, 0x01, IPT_CUSTOM) PORT_CONDITION("IN.2", 0x51, NOTEQUALS, 0x00)
	PORT_BIT(0x02, 0x02, IPT_CUSTOM) PORT_CONDITION("IN.2", 0x32, NOTEQUALS, 0x00)
	PORT_BIT(0x04, 0x04, IPT_CUSTOM) PORT_CONDITION("IN.2", 0xa4, NOTEQUALS, 0x00)
	PORT_BIT(0x08, 0x08, IPT_CUSTOM) PORT_CONDITION("IN.2", 0xc8, NOTEQUALS, 0x00)
	PORT_BIT(0x30, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_F) PORT_NAME("2nd F")

	PORT_START("IN.2") // square 'd-pad' (8-way, so define joystick)
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Cursor Left")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP) PORT_CODE(KEYCODE_UP) PORT_NAME("Cursor Up")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Cursor Right")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Cursor Down")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) // ul
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) // ur
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) // dl
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) // dr

	PORT_START("IN.3")
	PORT_CONFNAME( 0x01, 0x00, "Battery Status" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(exechess_state::battery), 0)
	PORT_CONFSETTING(    0x01, "Low" )
	PORT_CONFSETTING(    0x00, DEF_STR( Normal ) )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void exechess_state::exechess(machine_config &config)
{
	// basic machine hardware
	F8(config, m_maincpu, 4'500'000/2); // measured
	m_maincpu->set_addrmap(AS_PROGRAM, &exechess_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &exechess_state::main_io);
	m_maincpu->set_irq_acknowledge_callback("psu", FUNC(f38t56_device::int_acknowledge));

	f38t56_device &psu(F38T56(config, "psu", 4'500'000/2));
	psu.set_int_vector(0x0020);
	psu.int_req_callback().set_inputline("maincpu", F8_INPUT_LINE_INT_REQ);
	psu.read_a().set(FUNC(exechess_state::ram_data_r));
	psu.write_a().set(FUNC(exechess_state::ram_data_w));
	psu.write_a().append(FUNC(exechess_state::lcd_data_w));
	psu.read_b().set_ioport("IN.1");

	// video hardware
	HLCD0538(config, m_lcd1, 310); // measured
	m_lcd1->write_cols().set(FUNC(exechess_state::lcd_output_w<0>));
	m_lcd1->write_interrupt().set(m_lcd2, FUNC(hlcd0539_device::lcd_w));

	HLCD0539(config, m_lcd2, 0);
	m_lcd2->write_cols().set(FUNC(exechess_state::lcd_output_w<1>));
	m_lcd2->write_interrupt().set("psu", FUNC(f38t56_device::ext_int_w)).invert();

	PWM_DISPLAY(config, m_display).set_size(8, 26+34);
	m_display->set_interpolation(0.2);
	config.set_default_layout(layout_saitek_exechess);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1020/1.5, 1080/1.5);
	screen.set_visarea_full();
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( exechess )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("sl90553", 0x0000, 0x1000, CRC(a61b0c7e) SHA1(a13b11a93f78236223c5c0b9879a93284b7f7525) )

	ROM_REGION( 852610, "screen", 0 )
	ROM_LOAD("exechess.svg", 0, 852610, CRC(cb36f9d3) SHA1(83be9b5d906d185b7cf6895f50992e7eea390c7a) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1981, exechess, 0,      0,      exechess, exechess, exechess_state, empty_init, "SciSys / Philidor Software", "Executive Chess", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



executive10.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    Esprit Systems Executive 10/102

    VT102 compatible terminal

    Hardware:
    - Intel P8088
    - Intel D8284A
    - Intel P8259A-8
    - 2x 2764 EPROM, 1x 2732 EPROM
    - SY2128-2 (2k RAM)
    - X2212D NOVRAM
    - MC68B50P
    - SCN2681A
    - 14.469 MHz XTAL (near CPU), 3.6864 MHz XTAL (near 2681)
    - CRT 9007
    - 2x CRT 9006-135
    - CRT9021A
    - 2764 EPROM
    - 4x SY2128-2 (8k RAM)
    - 21.800 MHz XTAL (near 9007), 16.960 MHz XTAL (near 9007)
    - 9 position DIP switch

    TODO:
    - Improve rendering (132 columns, smooth scrolling, non-line attributes(?))
    - NOVRAM store/recall
    - Printer
    - DIP switch

    Notes:
    - Other models in this line: 10/51 (IBM 5251), 10/78 (IBM 3278)
    - To go online: Enter Set-Up, press 5 for next screen, press 4 to switch
      from local to online (no feedback, would be shown on keyboard LEDs)

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

#include "emu.h"

#include "executive10_102_kbd.h"

#include "bus/rs232/rs232.h"
#include "cpu/i86/i86.h"
#include "machine/6850acia.h"
#include "machine/mc68681.h"
#include "machine/pic8259.h"
#include "machine/x2212.h"
#include "video/crt9007.h"

#include "emupal.h"
#include "multibyte.h"
#include "screen.h"


namespace {


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

class executive10_state : public driver_device
{
public:
	executive10_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_novram(*this, "novram"),
		m_pic(*this, "pic"),
		m_screen(*this, "screen"),
		m_palette(*this, "palette"),
		m_vpac(*this, "vpac"),
		m_duart(*this, "duart"),
		m_acia(*this, "acia"),
		m_cram(*this, "cram"),
		m_aram(*this, "aram"),
		m_chargen(*this, "chargen")
	{ }

	void executive10(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<i8088_cpu_device> m_maincpu;
	required_device<x2212_device> m_novram;
	required_device<pic8259_device> m_pic;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<crt9007_device> m_vpac;
	required_device<scn2681_device> m_duart;
	required_device<acia6850_device> m_acia;
	required_shared_ptr<uint8_t> m_cram;
	required_shared_ptr<uint8_t> m_aram;
	required_region_ptr<uint8_t> m_chargen;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};


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

void executive10_state::mem_map(address_map &map)
{
	map.global_mask(0xffff);
	map(0x0000, 0x07ff).ram();
	map(0x2000, 0x2fff).ram().share("cram");
	map(0x4000, 0x4fff).ram().share("aram");
	map(0x6000, 0x60ff).rw(m_novram, FUNC(x2210_device::read), FUNC(x2210_device::write));
	map(0xb000, 0xffff).rom().region("maincpu", 0);
}

void executive10_state::io_map(address_map &map)
{
	map(0x000, 0x03f).rw(m_vpac, FUNC(crt9007_device::read), FUNC(crt9007_device::write));
	map(0x040, 0x04f).rw(m_duart, FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0x080, 0x080).w(m_acia, FUNC(acia6850_device::control_w));
	map(0x081, 0x081).r(m_acia, FUNC(acia6850_device::status_r));
	map(0x082, 0x082).w(m_acia, FUNC(acia6850_device::data_w));
	map(0x083, 0x083).r(m_acia, FUNC(acia6850_device::data_r));
	map(0x0c0, 0x0c1).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	//map(0x180, 0x180).nopw(); // novram store/recall?
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( executive10 )
INPUT_PORTS_END


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

uint32_t executive10_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const pen_t *const pen = m_palette->pens();

	for (int y = 0; y < 25; y++)
	{
		// f---------------  double height line
		// -e--------------  double width line
		// --dc------------  unknown
		// ----ba9876543210  line address

		uint16_t addr = get_u16le(&m_cram[0xfcc + (y * 2)]);
		bool dw = addr & 0xc000;

		uint8_t line_attr = 0x00;

		for (int x = 0; x < (dw ? 40 : 80); x++)
		{
			uint16_t code = m_cram[(addr & 0x0fff) + x];

			// move bit 7 to 8 (acs characters)
			code = bitswap<9>(code, 7, 8, 6, 5, 4, 3, 2, 1, 0);

			// select 80/132 column character set
			code |= 0x80; // fixed 80 columns mode for now

			// 7-------  active for line attributes
			// -6------  unknown
			// --5-----  blink
			// ---4----  reverse
			// ----3---  0 = blank?
			// -----2--  underline
			// ------1-  unknown
			// -------0  highlight

			uint8_t attr = m_aram[(addr & 0x0fff) + x];

			// new line attribute?
			if (BIT(attr, 7))
				line_attr = attr;

			for (int i = 0; i < 12; i++)
			{
				unsigned char_line = i;

				// adjust rendered line for double height mode
				if (BIT(addr, 15))
					char_line = (i / 2) + (BIT(addr, 14) * (12 / 2));

				uint16_t data = m_chargen[(code << 4) + char_line] << 1;

				// maybe? fixes line drawing characters
				if (BIT(data, 8) == 1)
				{
					if (BIT(data, 1) == 1)
						data |= 0x001;

					if (BIT(data, 7) == 0)
						data &= ~0x100;
				}

				// underline?
				if (BIT(line_attr, 2) && i == 10)
					data = 0x1ff;

				// reverse?
				if (BIT(line_attr, 4))
					data = ~data;

				// blink?
				if (BIT(line_attr, 5) && (m_screen->frame_number() & 0x20)) // wrong timing
					data = 0x000;

				// cursor?
				if (m_vpac->cursor_active(x, y))
					data = ~data; // might be solid instead

				// foreground/background colors
				rgb_t fg = BIT(line_attr, 0) ? pen[2] : pen[1];
				rgb_t bg = pen[0];

				// draw character line
				if (dw)
				{
					for (int p = 0; p < 9; p++)
					{
						bitmap.pix(y * 12 + i, x * 18 + p * 2 + 0) = BIT(data, 8 - p) ? fg : bg;
						bitmap.pix(y * 12 + i, x * 18 + p * 2 + 1) = BIT(data, 8 - p) ? fg : bg;
					}
				}
				else
				{
					for (int p = 0; p < 9; p++)
						bitmap.pix(y * 12 + i, x * 9 + p) = BIT(data, 8 - p) ? fg : bg;
				}
			}
		}
	}

	return 0;
}

static const gfx_layout char_layout =
{
	8, 12,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8 * 16
};

static GFXDECODE_START( chars )
	GFXDECODE_ENTRY("chargen", 0, char_layout, 0, 1)
GFXDECODE_END


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

void executive10_state::machine_start()
{
}

void executive10_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void executive10_state::executive10(machine_config &config)
{
	I8088(config, m_maincpu, 14.469_MHz_XTAL / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &executive10_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &executive10_state::io_map);
	m_maincpu->set_irq_acknowledge_callback(m_pic, FUNC(pic8259_device::inta_cb));

	X2212(config, m_novram);

	PIC8259(config, m_pic);
	m_pic->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_color(rgb_t::green());
	m_screen->set_raw(16.960_MHz_XTAL, 890, 0, 720, 320, 0, 300); // maybe
	m_screen->set_screen_update(FUNC(executive10_state::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	GFXDECODE(config, "gfxdecode", m_palette, chars);

	CRT9007(config, m_vpac, 16.960_MHz_XTAL / 9);
	m_vpac->set_screen("screen");
	m_vpac->set_character_width(9);
	m_vpac->int_callback().set(m_pic, FUNC(pic8259_device::ir2_w));

	SCN2681(config, m_duart, 3.6864_MHz_XTAL);
	m_duart->a_tx_cb().set("serial", FUNC(rs232_port_device::write_txd));
	m_duart->outport_cb().set(m_acia, FUNC(acia6850_device::write_rxc)).bit(3);
	m_duart->outport_cb().append(m_acia, FUNC(acia6850_device::write_txc)).bit(3);
	m_duart->outport_cb().append(m_pic, FUNC(pic8259_device::ir0_w)).bit(4).invert();
	m_duart->outport_cb().append(m_pic, FUNC(pic8259_device::ir1_w)).bit(5).invert();

	rs232_port_device &serial(RS232_PORT(config, "serial", default_rs232_devices, nullptr));
	serial.rxd_handler().set(m_duart, FUNC(scn2681_device::rx_a_w));
	serial.cts_handler().set(m_duart, FUNC(scn2681_device::ip0_w));

	ACIA6850(config, m_acia);
	m_acia->irq_handler().set(m_pic, FUNC(pic8259_device::ir4_w));
	m_acia->txd_handler().set("kbd", FUNC(executive10_102_kbd_device::rxd_w));

	executive10_102_kbd_device &kbd(EXECUTIVE10_102_KBD(config, "kbd"));
	kbd.txd_cb().set(m_acia, FUNC(acia6850_device::write_rxd));
	kbd.cts_cb().set(m_acia, FUNC(acia6850_device::write_cts));
}


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

ROM_START( exe10102 )
	ROM_REGION(0x5000, "maincpu", 0)
	ROM_LOAD("113-03-0.u25", 0x0000, 0x1000, CRC(bf5498d4) SHA1(46a3e832a1ba9a08c1d4938d6c94b2194ef00081))
	ROM_LOAD("113-01-0.u26", 0x1000, 0x2000, CRC(5e6babb3) SHA1(4669107e9cfaba1a697db6e832bc36c8220ee591))
	ROM_LOAD("113-02-0.u27", 0x3000, 0x2000, CRC(05bcd49b) SHA1(fbeac116ded46215644df7db1810f4f9d41e49ca))

	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD("111-01-0.u59", 0x0000, 0x2000, CRC(4e54c69f) SHA1(92640a57863c5eab9db6d07c31deebfa3d1dafa5))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME      PARENT  COMPAT  MACHINE      INPUT        CLASS              INIT        COMPANY           FULLNAME            FLAGS
COMP( 1983, exe10102, 0,      0,      executive10, executive10, executive10_state, empty_init, "Esprit Systems", "Executive 10/102", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



exelv.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Raphael Nabet,Robbbert
/*
    Experimental exelvision driver

    Raphael Nabet, 2004

    Exelvision was a French company that designed and sold two computers:
    * EXL 100 (1984)
    * EXELTEL (1986), which is mostly compatible with EXL 100, but has an
      integrated V23b modem and 5 built-in programs.  Two custom variants of
      the EXELTEL were designed for chemist's shops and car dealers: they were
      bundled with application-specific business software, bar code reader,
      etc.
    These computer were mostly sold in France and in Europe (Spain); there was
    an Arabic version, too.

    Exelvision was founded by former TI employees, which is why their designs
    use TI components and have architectural reminiscences of the primitive
    TI-99/4 design (both computers are built around a microcontroller, have
    little CPU RAM and must therefore store program data in VRAM, and feature
    I/R keyboard and joysticks)

Specs:
    * main CPU is a variant of tms7020 (exl100) or tms7040 (exeltel).  AFAIK,
      the only difference compared to a stock tms7020/7040 is the SWAP register
      instruction is replaced by a custom microcoded LVDP instruction that
      reads a byte from the VDP VRAM read port; it seems that the first 6 bytes
      of internal ROM (0xF000-0xF005 on an exeltel) are missing, too.
    * in addition to the internal 128-byte RAM and 2kb (exl100) or 4kb
      (exeltel) ROM, there are 2kb of CPU RAM and 64(?)kb (exeltel only?) of
      CPU ROM.
    * I/O is controlled by a tms7041 (exl100) or tms7042 (exeltel) or a variant
      thereof.  Communication with the main CPU is done through some custom
      interface (I think), details are still to be worked out.
    * video: tms3556 VDP with 32kb of VRAM (expandable to 64kb), attached to
      the main CPU.
    * sound: tms5220 speech synthesizer with speech ROM, attached to the I/O
      CPU
    * keyboard and joystick: an I/R interface controlled by the I/O CPU enables
      to use a keyboard and two joysticks
    * mass storage: tape interface controlled by the main CPU

More info about the keyboard:
- The EXL100 has an infrared keyboard and 2 infrared joysticks. Each uses a MC14497 chip, which scans the
   inputs and drives the infrared transmitter. We only emulate the keyboard, and it only works in Exel Basic.
- The receiver uses a TEA1009 amplifier feeding a pair of NE567 PLL lock detectors. The first one detects
   28437Hz and is for the keyboard and one joystick. When lock achieved, it causes INT1 on the subcpu. The
   other NE567 detects 20000Hz, is for the other joystick, and causes INT3 on the subcpu.
- Although a range of 8 metres was claimed, users found it was very critical in regard to correct positioning.
- You don't hold a modifier key; you hit and release, then hit the key being modified. This also breaks
   the natural keyboard emulation. Exel Basic will indicate if a modifier key is active.

STATUS:
* EXL 100
  - Most games do something, see table below
* EXELTEL can get to the inbuilt "cart" but stops with a black screen,
    presumably because the I/O processor is not emulated

STATUS OF SOFTWARE:

SWList name      Status
---------------------------------------------------------------------------
exelbas          works
exelbasp         Cyan screen, hangs at start
exelmax          options 1-4 work, 5-7 do nothing
exeldrum         can get to the menu, which seems useless
exelogo          can type into it but the usual commands get error
exeltext         works but weird
exlpaint         works
exlmodem         it might work, need instructions
capmenkr         works, video corruptions
guppy            works
imagix           video corruptions, can't proceed
pindo            video corruptions, can select a game, how to play?
quizzy           works, video corruptions
tennis           the demo works, didn't try playing
virus            works, 2nd screen is corrupt, press 1 there.
wizord           works


Using the cassette:
- You must be in Exel Basic. There's no motor control.
- To save: SAVE"1"  hit enter, put player in Record, hit Esc, it saves.
- To load: LOAD"1"  hit enter, hit esc, put player in Play, it loads.
- The cassette output is connected to the audio section, so games could use it as a 1-bit dac.

TODO:
    * dump exeltel tms7042 I/O CPU ROM
    * exeltel: everything
    * The joysticks. No schematics of them have been found.
    * Keyboard layout is preliminary.
    * Keyboard response is terrible, but this might be normal.
    * Add support for cassette k7 format - there's heaps of software for it.
*/


#include "emu.h"

#include "cpu/tms7000/tms7000.h"
#include "imagedev/cassette.h"
#include "machine/tms6100.h"
#include "machine/timer.h"
#include "sound/tms5220.h"
#include "sound/spkrdev.h"
#include "video/tms3556.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "utf8.h"

#define VERBOSE 0
#include "logmacro.h"


namespace {

class exelv_state : public driver_device
{
public:
	exelv_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_subcpu(*this, "subcpu")
		, m_tms3556(*this, "tms3556")
		, m_tms5220c(*this, "tms5220c")
		, m_cart(*this, "cartslot")
		, m_cass(*this, "cassette")
		, m_speaker(*this, "speaker")
		, m_io_keyboard(*this, "X%d", 0U)
		, m_timer_k(*this, "timer_k")
	{ }

	void exeltel(machine_config &config);
	void exl100(machine_config &config);

private:
	required_device<tms7000_device> m_maincpu;
	optional_device<tms7041_device> m_subcpu;
	required_device<tms3556_device> m_tms3556;
	required_device<tms5220c_device> m_tms5220c;
	optional_device<generic_slot_device> m_cart;
	optional_device<cassette_image_device> m_cass;
	optional_device<speaker_sound_device> m_speaker;
	required_ioport_array<8> m_io_keyboard;
	optional_device<timer_device> m_timer_k;

	uint8_t mailbox_wx319_r();
	void mailbox_wx318_w(uint8_t data);
	uint8_t tms7020_porta_r();
	void tms7020_portb_w(uint8_t data);
	uint8_t tms7041_porta_r();
	void tms7041_portb_w(uint8_t data);
	uint8_t tms7041_portc_r();
	void tms7041_portc_w(uint8_t data);
	uint8_t tms7041_portd_r();
	void tms7041_portd_w(uint8_t data);
	uint8_t rom_r(offs_t offset);

	DECLARE_MACHINE_START(exl100);
	DECLARE_MACHINE_START(exeltel);
	TIMER_DEVICE_CALLBACK_MEMBER(timer_k);
	void machine_reset() override ATTR_COLD;
	void machine_common();

	/* tms7020 i/o ports */
	uint8_t   m_tms7020_portb = 0;

	/* tms7041 i/o ports */
	uint8_t   m_tms7041_portb = 0;
	uint8_t   m_tms7041_portc = 0;
	uint8_t   m_tms7041_portd = 0;
	uint32_t  m_rom_size = 0;

	/* mailbox data */
	uint8_t   m_wx318 = 0;    /* data of 74ls374 labeled wx318 */
	uint8_t   m_wx319 = 0;    /* data of 74sl374 labeled wx319 */

	TIMER_DEVICE_CALLBACK_MEMBER(exelv_hblank_interrupt);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER( exelvision_cartridge );
	void tms7020_mem(address_map &map) ATTR_COLD;
	void tms7040_mem(address_map &map) ATTR_COLD;

	// variables for the keyboard
	u8 k_channels[3] = { 0xff, 0xff, 0x3e }; // [0] = key down, [1] = key being sent; [2] = ch62
	u8 k_ch_byte = 0; // 'k_channels' index; 0 = idly scanning the keyboard; 1 = sending a key; 2 = sending ch62
	u8 k_ch_bit = 0; // bit# in the byte being sent; 0 = AGC, 1 = start bit, 2-7 = bits 0-5 of data, 8 = end of byte
	bool k_bit_bit = 0; // the value of the bit of data being processed
	bool k_bit_num = 0; // each bit gets 2 transmissions, this variable shows which half we are working on;
	// if k_bit_bit is high then do interrupt then none; low has none then interrupt
};


TIMER_DEVICE_CALLBACK_MEMBER(exelv_state::exelv_hblank_interrupt)
{
	m_tms3556->interrupt();
}


/*
    I/O CPU protocol (WIP):

    I do not have a dump of the I/O CPU ROMs.  The I/O CPU CRC command should
    enable to dump them, but don't take my word for it.

    * port B bit >01 is asserted on reset and after a byte is sent to the I/O
      CPU.
    * port B bit >02 is asserted after a byte is read from the I/O CPU.  When
      the I/O  CPU sees this line asserted, it asserts port A bit >01.
    * port A bit >01 is asserted after a byte is sent to CPU (condition
      cleared when port B bit >01 is cleared after being asserted) and when
      port B bit >02 is asserted.
    * I/O CPU pulses the main CPU INT1 line when ready to send data; data can
      be read by the main CPU on the mailbox port (P48).  The data is a
      function code optionally followed by several bytes of data.  Function
      codes are:
        >00: unused
        >01: joystick 0 receive
        >02: joystick 1 receive
        >03: speech buffer start
        >04: speech buffer end
        >05: serial
        >06: unused
        >07: introduction screen (logo) (EXL 100 only?) or character
          definitions
            data byte #1: data length - 1 MSB
            data byte #2: data length - 1 LSB
            data bytes #3 through (data length + 3): graphic data
        >08: I/O cpu initialized
        >09: I/O cpu serial interface ready
        >0a: I/O cpu serial interface not ready
        >0b: screen switched off
        >0c: speech buffer start (EXELTEL only?)
        >0d: speech ROM or I/O cpu CRC check (EXELTEL only?)
            data byte #1: expected CRC MSB
            data byte #2: expected CRC LSB
            data byte #3: data length - 1 MSB
            data byte #4: data length - 1 LSB
            data bytes #5 through (data length + 5): data on which effective
                CRC is computed
        >0e: mailbox test, country code read (EXELTEL only?)
        >0f: speech ROM read (data repeat) (EXELTEL only?)
    * The main CPU sends data to the I/O CPU through the mailbox port (P48).
      The data byte is a function code; some function codes ask for extra data
      bytes, which are sent through the mailbox port as well.  Function codes
      are:
        >00: I/O CPU reset
        >01: NOP (EXELTEL only?)
        >02: read joystick 0 current value
        >03: read joystick 1 current value
        >04: test serial interface availability
        >05: transmit a byte to serial interface
        >06: initialization of serial interface
        >07: read contents of speech ROM (EXELTEL only?)
        >08: reset speech synthesizer
        >09: start speech synthesizer
        >0a: synthesizer data
        >0b: standard generator request
        >0c: I/O CPU CRC (EXELTEL only?)
        >0d: send exelvision logo (EXL 100 only), start speech ROM sound (EXELTEL only?)
        >0e: data for speech on ROM (EXELTEL only?)
        >0f: do not decode joystick 0 keys (EXELTEL only?)
        >10: do not decode joystick 1 keys (EXELTEL only?)
        >11: decode joystick 0 keys (EXELTEL only?)
        >12: decode joystick 1 keys (EXELTEL only?)
        >13: mailbox test: echo sent data (EXELTEL only?)
        >14: enter sleep mode (EXELTEL only?)
        >15: read country code in speech ROM (EXELTEL only?)
        >16: position I/O CPU DSR without initialization (EXELTEL only?)
        >17: handle speech ROM sound with address (EXELTEL only?)
        other values: I/O CPU reset?
*/


uint8_t exelv_state::mailbox_wx319_r()
{
	LOG("[TMS7220] reading mailbox %d\n", m_wx319);
	return m_wx319;
}


void exelv_state::mailbox_wx318_w(uint8_t data)
{
	LOG("wx318 write 0x%02x\n", data);
	m_wx318 = data;
}


/*
    TMS7020 PORT A
    A0 - R - TMS7041 port B bit 7 (REV3)
    A1 -
    A2 -
    A3 -
    A4 - R - cass in
    A5 -
    A6 -
    A7 -
*/
uint8_t exelv_state::tms7020_porta_r()
{
	LOG("tms7020_porta_r\n");
	u8 data = ( m_tms7041_portb & 0x80 ) ? 0x01 : 0x00;
	if (m_cass)
	{
		double level = (m_cass->input());
		if (level < 0.02)
			data |= 0x10;
	}
	return data;
}


/*
    TMS7020 PORT B
    B0 - W - TMS7041 port A bit 2 (REV2)
    B1 - W - TMS7041 port A bit 4 (REV4)
    B2 -
    B3 - W - cass out
    B4 -
    B5 -
    B6 -
    B7 -
*/
void exelv_state::tms7020_portb_w(uint8_t data)
{
	LOG("tms7020_portb_w: data = 0x%02x\n", data);
	m_tms7020_portb = data;
	if (m_cass)
	{
		m_cass->output(BIT(data, 3) ? -1.0 : +1.0);
		m_speaker->level_w(BIT(data, 3) ? -1.0 : +1.0);
	}
}


/*
    TMS7041 PORT A
    A0 - X1 NDSR A8
    A1 - X1 MDTR A9
    A2 - R - TMS7020 port B bit 0 (REV2)
    A3 - TMS5220 IRQ
    A4 - R - TMS7020 port B bit 1 (REV4)
    A5 - X1 RXD A4 (version b) / WX301-14 (version a)
    A6 - X1 SCLK A9
    A7 - TMS5220 RDY
*/
uint8_t exelv_state::tms7041_porta_r()
{
	uint8_t data = 0x00;
	static uint8_t data_last=0;

	// TMS5220 OK
	data |= m_tms5220c->intq_r() ? 0x08 : 0x00; // A3
	data |= m_tms5220c->readyq_r() ? 0x80 : 0x00; // A7

	// TMS7220
	data |= (m_tms7020_portb & 0x01 ) ? 0x04 : 0x00; // A2
	data |= (m_tms7020_portb & 0x02) ? 0x10 : 0x00; // A4

	// SERIAL PORT

	if (data!=data_last) {
		LOG("tms7041_porta_r %x\n",data);
	}
	data_last=data;

	return data;
}


/*
    TMS7041 PORT B
    B0 - W - TMS5220 W
    B1 - W - TMS5220 R
    B2 - W - TMS7020 pin 13 / IRQ1
    B3 - X1 TXD A5 (version b) / WX301-8 (version a)
    B4 - TP3
    B5 - REV5 WX318-1
    B6 - W - REV6 WX319-11
    B7 - W - TMS7020 port A bit 0 (REV3)
*/
void exelv_state::tms7041_portb_w(uint8_t data)
{
	LOG("tms7041_portb_w: data = 0x%02x\n", data);

	// optional code; tms5220 device works in different ways depending on if this exists or not
	m_tms5220c->combined_rsq_wsq_w(data & 3);

	LOG("TMS7020 %s int1\n",((data & 0x04) ? "clear" : "assert"));

	/* Check for high->low transition on B2 */
	// Using hold_line because the pulse is too short and can be missed by the other cpu
	if ((BIT(m_tms7041_portb, 2)) && !BIT(data, 2))
		m_maincpu->set_input_line(TMS7000_INT1_LINE, HOLD_LINE);

	/* Check for low->high transition on B6 */
	if ((!BIT(m_tms7041_portb, 6)) && BIT(data, 6))
	{
		LOG("wx319 write 0x%02x\n", m_tms7041_portc);
		m_wx319 = m_tms7041_portc;
	}

	m_tms7041_portb = data;
}


/*
    TMS7041 PORT C - connected to mailbox WX318 and WX319 data bits
*/
uint8_t exelv_state::tms7041_portc_r()
{
	uint8_t data = 0xff;
	LOG("tms7041_portc_r\n");

	/* Check if wx318 output is enabled */
	if (!(m_tms7041_portb & 0x20))
		data = m_wx318;

	return data;
}


void exelv_state::tms7041_portc_w(uint8_t data)
{
	LOG("tms7041_portc_w: data = 0x%02x\n", data);
	m_tms7041_portc = data;
}


/*
    TMS7041 PORT D
    D0 - TMS5220 D7
    D1 - TMS5220 D6
    D2 - TMS5220 D5
    D3 - TMS5220 D4
    D4 - TMS5220 D3
    D5 - TMS5220 D2
    D6 - TMS5220 D1
    D7 - TMS5220 D0
*/
uint8_t exelv_state::tms7041_portd_r()
{
	uint8_t data = m_tms5220c->status_r();
	LOG("tms7041_portd_r: data = 0x%02x\n", data);
	return data;
}


void exelv_state::tms7041_portd_w(uint8_t data)
{
	LOG("tms7041_portd_w: data = 0x%02x\n", data);

	m_tms5220c->data_w(data);
	m_tms7041_portd = data;
}


/*
    CARTRIDGE ACCESS
*/
uint8_t exelv_state::rom_r(offs_t offset)
{
	if (m_rom_size && m_cart && m_cart->exists())
	{
		if (m_rom_size == 0x7e00)
			return m_cart->read_rom(offset);
		else
			return m_cart->read_rom(offset + 0x200);
	}

	return 0;
}

// INFRARED KEYBOARD
// Note: usec times are from the datasheet and should not be altered, but msec times are just guesswork.
TIMER_DEVICE_CALLBACK_MEMBER(exelv_state::timer_k)
{
	// when a key pressed, a channel 0-61 is sent and repeated every 90ms. When released, send channel 62, then silence.

	if (k_ch_byte < 2)
	{
		k_channels[0] = 0xff;
		for (u8 row = 0; row < 8; row++)
		{
			u8 colin = m_io_keyboard[row]->read();
			if (colin)
			{
				for (u8 j = 0; j < 8; j++)
				{
					if (BIT(colin, j))
					{
						k_channels[0] = row*8+j; // key pressed
						if (k_ch_byte == 0)
						{
							// can accept it for processing
							if (k_channels[1] == 0xff)
								k_channels[1] = k_channels[0];
							// init pointers
							k_ch_bit = 0;
							k_bit_num = 0;
							k_ch_byte = 1;
						}
					}
				}
			}
		}
	}

	// Idling; nothing pressed, nothing to do
	if (k_ch_byte == 0)
	{
		m_timer_k->adjust(attotime::from_msec(25));
		return;
	}

	// AGC bit - a single 540us pulse followed by a large gap
	if (k_ch_bit == 0)
	{
		if (!k_bit_num)
		{
			m_subcpu->set_input_line(TMS7000_INT1_LINE, ASSERT_LINE);
			k_bit_num = 1;
			m_timer_k->adjust(attotime::from_usec(540));
		}
		else
		{
			m_subcpu->set_input_line(TMS7000_INT1_LINE, CLEAR_LINE);
			k_bit_num = 0;
			k_ch_bit = 1;
			m_timer_k->adjust(attotime::from_usec(2840));
		}
		return;
	}

	// start bit - a hardcoded '1' - so send a 540us pulse followed by 590us of silence
	if (k_ch_bit == 1)
	{
		if (!k_bit_num)
		{
			m_subcpu->set_input_line(TMS7000_INT1_LINE, ASSERT_LINE);
			k_bit_num = 1;
			m_timer_k->adjust(attotime::from_usec(540));
		}
		else
		{
			m_subcpu->set_input_line(TMS7000_INT1_LINE, CLEAR_LINE);
			k_bit_num = 0;
			k_ch_bit = 2;
			m_timer_k->adjust(attotime::from_usec(590));
		}
		return;
	}

	// stop bit - not really a 'bit' - need to turn off any interrupt,
	//  and prepare for either 90msec inter-byte gap, or ch62 terminating byte.
	//  If we are already finishing up the terminating byte, then set to idle.
	if (k_ch_bit == 8)
	{
		m_subcpu->set_input_line(TMS7000_INT1_LINE, CLEAR_LINE);
		k_ch_bit = 0;
		// just finished sending key
		if (k_ch_byte == 1)
		{
			// key still down, send again in 90ms
			if (k_channels[0] < 0xff)
			{
				m_timer_k->adjust(attotime::from_usec(90000));
			}
			else
			// key was released, send ch62
			{
				k_ch_byte = 2;
				m_timer_k->adjust(attotime::from_msec(3)); // signal end channel
			}
		}
		else
		// just finished sending ch62
		if (k_ch_byte == 2)
		{
			// clean up and go back to looking at kbd
			k_channels[1] = 0xff;
			k_ch_byte = 0;
			m_timer_k->adjust(attotime::from_msec(20));
		}
		return;
	}

	// data bits, LSB first
	// 1st half of a bit
	if (!k_bit_num)
	{
		k_bit_bit = BIT(k_channels[k_ch_byte], k_ch_bit-2);

		if (k_bit_bit)
			m_subcpu->set_input_line(TMS7000_INT1_LINE, ASSERT_LINE);
		else
			m_subcpu->set_input_line(TMS7000_INT1_LINE, CLEAR_LINE);

		k_bit_num = 1;
		m_timer_k->adjust(attotime::from_usec(590));
		return;
	}

	// 2nd half of a bit
	if (!k_bit_bit)
		m_subcpu->set_input_line(TMS7000_INT1_LINE, ASSERT_LINE);
	else
		m_subcpu->set_input_line(TMS7000_INT1_LINE, CLEAR_LINE);

	k_bit_num = 0;
	k_ch_bit++; // next bit
	m_timer_k->adjust(attotime::from_usec(540));
}


/*
    Main CPU memory map summary:

    @>0000-@>007f: tms7020/tms7040 internal RAM
    @>0080-@>00ff: reserved
    @>0100-@>010b: tms7020/tms7040 internal I/O ports
        @>104 (P4): port A
        @>106 (P6): port B
            bit >04: page select bit 0 (LSBit)
    @>010c-@>01ff: external I/O ports?
        @>012d (P45): tms3556 control write port???
        @>012e (P46): tms3556 VRAM write port???
        @>0130 (P48): I/O CPU communication port R/W ("mailbox")
        @>0138 (P56): read sets page select bit 1, write clears it???
        @>0139 (P57): read sets page select bit 2 (MSBit), write clears it???
        @>0140 (P64)
            bit >40: enable page select bit 1 and 2 (MSBits)
    @>0200-@>7fff: system ROM? (two pages?) + cartridge ROMs? (one or two pages?)
    @>8000-@>bfff: free for expansion?
    @>c000-@>c7ff: CPU RAM?
    @>c800-@>efff: free for expansion?
    @>f000-@>f7ff: tms7040 internal ROM
    @>f800-@>ffff: tms7020/tms7040 internal ROM
*/

void exelv_state::tms7020_mem(address_map &map)
{
	map(0x0080, 0x00ff).noprw();
	map(0x0124, 0x0124).r(m_tms3556, FUNC(tms3556_device::vram_r));
	map(0x0125, 0x0125).r(m_tms3556, FUNC(tms3556_device::reg_r));
	map(0x0128, 0x0128).r(m_tms3556, FUNC(tms3556_device::initptr_r));
	map(0x012d, 0x012d).nopr().w(m_tms3556, FUNC(tms3556_device::reg_w));
	map(0x012e, 0x012e).nopr().w(m_tms3556, FUNC(tms3556_device::vram_w));

	map(0x0130, 0x0130).rw(FUNC(exelv_state::mailbox_wx319_r), FUNC(exelv_state::mailbox_wx318_w));
	map(0x0200, 0x7fff).r(FUNC(exelv_state::rom_r));
	map(0x8000, 0xbfff).noprw();
	map(0xc000, 0xc7ff).ram();                                     /* CPU RAM */
	map(0xc800, 0xf7ff).noprw();
}


void exelv_state::tms7040_mem(address_map &map)
{
	map(0x0080, 0x00ff).noprw();
	map(0x0124, 0x0124).r(m_tms3556, FUNC(tms3556_device::vram_r));
	map(0x0125, 0x0125).r(m_tms3556, FUNC(tms3556_device::reg_r));
	map(0x0128, 0x0128).r(m_tms3556, FUNC(tms3556_device::initptr_r));
	map(0x012d, 0x012d).nopr().w(m_tms3556, FUNC(tms3556_device::reg_w));
	map(0x012e, 0x012e).nopr().w(m_tms3556, FUNC(tms3556_device::vram_w));
	map(0x0130, 0x0130).rw(FUNC(exelv_state::mailbox_wx319_r), FUNC(exelv_state::mailbox_wx318_w));
	map(0x0200, 0x7fff).bankr("bank1");                                /* system ROM */
	map(0x8000, 0xbfff).noprw();
	map(0xc000, 0xc7ff).ram();                                     /* CPU RAM */
	map(0xc800, 0xefff).noprw();
}

static INPUT_PORTS_START(exelv)
	PORT_START("X0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_UP) PORT_NAME(UTF8_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RIGHT) PORT_NAME(UTF8_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DOWN) PORT_NAME(UTF8_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LEFT) PORT_NAME(UTF8_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("_  -") PORT_CHAR('_')

	PORT_START("X1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LCONTROL) PORT_NAME("CTL")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CAPSLOCK) PORT_NAME("LOCK")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("/  :") PORT_CHAR('/')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_NAME("?  ,") PORT_CHAR('?')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LALT) PORT_NAME("FCT") // function key

	PORT_START("X2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_HOME) PORT_NAME("\xe2\x86\x96") PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_NAME("+  =") PORT_CHAR('+')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_NAME("1  &") PORT_CHAR('1')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_NAME("6  $") PORT_CHAR('6')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_NAME("8  !") PORT_CHAR('8')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_NAME(u8"0  à") PORT_CHAR('0')

	PORT_START("X3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("\xe2\x8c\xab") PORT_CHAR(127)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_NAME(".  ;") PORT_CHAR('.')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_NAME(u8"2  é") PORT_CHAR('2')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_NAME("3  \"") PORT_CHAR('3')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_NAME(u8"9  ç") PORT_CHAR('9')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("#  )") PORT_CHAR('#')

	PORT_START("X4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LSHIFT) PORT_NAME("SHIFT")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_NAME("<  [") PORT_CHAR('<')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')

	PORT_START("X5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_NAME(">  ]") PORT_CHAR('>')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')

	PORT_START("X6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_NAME("~  ^") PORT_CHAR('~')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_NAME("4  '") PORT_CHAR('4')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_NAME(u8"7  è") PORT_CHAR('7')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RALT) PORT_NAME("*  \\") PORT_CHAR('*')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("X7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_NAME("5  (") PORT_CHAR('5')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_NAME("%  @") PORT_CHAR('%')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


/* Machine Initialization */

void exelv_state::machine_common()
{
	/* register for state saving */
	save_item(NAME(m_tms7020_portb));
	save_item(NAME(m_tms7041_portb));
	save_item(NAME(m_tms7041_portc));
	save_item(NAME(m_tms7041_portd));
	save_item(NAME(m_wx318));
	save_item(NAME(m_wx319));
	save_item(NAME(k_channels));
	save_item(NAME(k_ch_byte));
	save_item(NAME(k_ch_bit));
	save_item(NAME(k_bit_bit));
	save_item(NAME(k_bit_num));
}

MACHINE_START_MEMBER( exelv_state, exl100)
{
	machine_common();
	save_item(NAME(m_rom_size));

	m_rom_size = 0;
	if (m_cart && m_cart->exists())
		m_rom_size = m_cart->get_rom_size();
}

MACHINE_START_MEMBER( exelv_state, exeltel)
{
	machine_common();

	uint8_t *rom = memregion("user1")->base() + 0x0200;
	membank("bank1")->configure_entry(0, rom);
	membank("bank1")->set_entry(0);
}

void exelv_state::machine_reset()
{
	k_channels[0] = 0xff;
	k_channels[1] = 0xff;
	k_ch_byte = 0;
	k_ch_bit = 0;
	k_bit_bit = 0;
	k_bit_num = 0;

	if (m_timer_k)
		m_timer_k->adjust(attotime::from_seconds(2));

	if (m_subcpu)
	{
		m_subcpu->set_input_line(TMS7000_INT1_LINE, CLEAR_LINE);
		m_subcpu->set_input_line(TMS7000_INT3_LINE, CLEAR_LINE);
	}
}


void exelv_state::exl100(machine_config &config)
{
	/* basic machine hardware */
	TMS7020_EXL(config, m_maincpu, 4.9152_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &exelv_state::tms7020_mem);
	m_maincpu->in_porta().set(FUNC(exelv_state::tms7020_porta_r));
	m_maincpu->out_portb().set(FUNC(exelv_state::tms7020_portb_w));

	TIMER(config, "scantimer").configure_scanline(FUNC(exelv_state::exelv_hblank_interrupt), "screen", 0, 1);
	MCFG_MACHINE_START_OVERRIDE(exelv_state, exl100)

	TMS7041(config, m_subcpu, 4.9152_MHz_XTAL);
	m_subcpu->in_porta().set(FUNC(exelv_state::tms7041_porta_r));
	m_subcpu->out_portb().set(FUNC(exelv_state::tms7041_portb_w));
	m_subcpu->in_portc().set(FUNC(exelv_state::tms7041_portc_r));
	m_subcpu->out_portc().set(FUNC(exelv_state::tms7041_portc_w));
	m_subcpu->in_portd().set(FUNC(exelv_state::tms7041_portd_r));
	m_subcpu->out_portd().set(FUNC(exelv_state::tms7041_portd_w));

	config.set_perfect_quantum(m_maincpu);

	TMS3556(config, m_tms3556, 18_MHz_XTAL);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_video_attributes(VIDEO_UPDATE_BEFORE_VBLANK);
	screen.set_screen_update("tms3556", FUNC(tms3556_device::screen_update));
#if TMS3556_DOUBLE_WIDTH
	screen.set_size(tms3556_device::TOTAL_WIDTH*2, tms3556_device::TOTAL_HEIGHT*2);
	screen.set_visarea(0, tms3556_device::TOTAL_WIDTH*2-1, 0, tms3556_device::TOTAL_HEIGHT*2-1);
#else
	screen.set_size(tms3556_device::TOTAL_WIDTH, tms3556_device::TOTAL_HEIGHT*2);
	screen.set_visarea(0, tms3556_device::TOTAL_WIDTH-1, 0, tms3556_device::TOTAL_HEIGHT-1);
#endif
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::RGB_3BIT);

	TIMER(config, m_timer_k).configure_generic(FUNC(exelv_state::timer_k));

	//SPEECHROM(config, "vsm", 0);

	/* sound */
	SPEAKER(config, "mono").front_center();
	// The cassette output is connected into the audio circuit
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.30);

	TMS5220C(config, m_tms5220c, 640000);
	// m_tms5220c->set_speechrom_tag("vsm");
	m_tms5220c->add_route(ALL_OUTPUTS, "mono", 1.00);

	/* cartridge */
	GENERIC_CARTSLOT(config, "cartslot", generic_linear_slot, "exelvision_cart", "bin,rom");

	CASSETTE(config, m_cass, 0);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	SOFTWARE_LIST(config, "cart_list").set_original("exl100");
}

void exelv_state::exeltel(machine_config &config)
{
	/* basic machine hardware */
	TMS7040(config, m_maincpu, 9.8304_MHz_XTAL);
	m_maincpu->set_divide_by_4();
	m_maincpu->set_addrmap(AS_PROGRAM, &exelv_state::tms7040_mem);
	m_maincpu->in_porta().set(FUNC(exelv_state::tms7020_porta_r));
	m_maincpu->out_portb().set(FUNC(exelv_state::tms7020_portb_w));

	TIMER(config, "scantimer").configure_scanline(FUNC(exelv_state::exelv_hblank_interrupt), "screen", 0, 1);
	MCFG_MACHINE_START_OVERRIDE(exelv_state, exeltel)

	tms7042_device &subcpu(TMS7042(config, "tms7042", 9.8304_MHz_XTAL));
	subcpu.set_divide_by_4();
	subcpu.in_porta().set(FUNC(exelv_state::tms7041_porta_r));
	subcpu.out_portb().set(FUNC(exelv_state::tms7041_portb_w));
	subcpu.in_portc().set(FUNC(exelv_state::tms7041_portc_r));
	subcpu.out_portc().set(FUNC(exelv_state::tms7041_portc_w));
	subcpu.in_portd().set(FUNC(exelv_state::tms7041_portd_r));
	subcpu.out_portd().set(FUNC(exelv_state::tms7041_portd_w));

	config.set_perfect_quantum(m_maincpu);

	TMS3556(config, m_tms3556, 18_MHz_XTAL);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_video_attributes(VIDEO_UPDATE_BEFORE_VBLANK);
	screen.set_screen_update("tms3556", FUNC(tms3556_device::screen_update));
#if TMS3556_DOUBLE_WIDTH
	screen.set_size(tms3556_device::TOTAL_WIDTH*2, tms3556_device::TOTAL_HEIGHT*2);
	screen.set_visarea(0, tms3556_device::TOTAL_WIDTH*2-1, 0, tms3556_device::TOTAL_HEIGHT*2-1);
#else
	screen.set_size(tms3556_device::TOTAL_WIDTH, tms3556_device::TOTAL_HEIGHT*2);
	screen.set_visarea(0, tms3556_device::TOTAL_WIDTH-1, 0, tms3556_device::TOTAL_HEIGHT-1);
#endif
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::RGB_3BIT);

	/* sound */
	SPEAKER(config, "mono").front_center();
	TMS5220C(config, m_tms5220c, 9.8304_MHz_XTAL / 15); // unknown divider for "VSPCLK" (generated by TAHC06 gate array)
	m_tms5220c->add_route(ALL_OUTPUTS, "mono", 1.00);

	TMS6100(config, "vsm", 640_kHz_XTAL/4);
	m_tms5220c->m0_cb().set("vsm", FUNC(tms6100_device::m0_w));
	m_tms5220c->m1_cb().set("vsm", FUNC(tms6100_device::m1_w));
	m_tms5220c->addr_cb().set("vsm", FUNC(tms6100_device::add_w));
	m_tms5220c->data_cb().set("vsm", FUNC(tms6100_device::data_line_r));
	m_tms5220c->romclk_cb().set("vsm", FUNC(tms6100_device::clk_w));
}


/*
  ROM loading
*/
ROM_START(exl100)
	ROM_REGION(0x800, "maincpu", 0)
	ROM_LOAD("exl100in.bin", 0x000, 0x800, CRC(049109a3) SHA1(98a07297dcdacef41c793c197b6496dac1e8e744))      /* TMS7020 ROM, correct */

	ROM_REGION(0x1000, "subcpu", 0)
	ROM_LOAD("exl100_7041.bin", 0x0000, 0x1000, CRC(38f6fc7a) SHA1(b71d545664a974d8ad39bdf600c5b9884c3efab6))           /* TMS7041 internal ROM, correct  */
//  ROM_REGION(0x8000, "vsm", 0)
ROM_END


ROM_START(exeltel)
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("exeltel_7040.bin", 0x0000, 0x1000, CRC(2792f02f) SHA1(442a852eb68ef78974733d169084752a131de23d))      /* TMS7040 internal ROM */

	ROM_REGION(0x1000, "tms7042", 0)
	ROM_LOAD("exeltel_7042.bin", 0x0000, 0x1000, BAD_DUMP CRC(a0163507) SHA1(8452849df7eac8a89cf03ee98e2306047c1c4c38))         /* TMS7042 internal ROM, needs redump */

	ROM_REGION(0x10000,"user1",0)
	ROM_SYSTEM_BIOS( 0, "french", "French v1.4" )
	ROMX_LOAD("exeltel14.bin", 0x0000, 0x10000, CRC(52a80dd4) SHA1(2cb4c784fba3aec52770999bb99a9a303269bf89), ROM_BIOS(0))  /* French system ROM v1.4 */
	ROM_SYSTEM_BIOS( 1, "spanish", "Spanish" )
	ROMX_LOAD("amper.bin", 0x0000, 0x10000, CRC(45af256c) SHA1(3bff16542f8ac55b9841084ea38034132459facb), ROM_BIOS(1)) /* Spanish system rom */

	ROM_REGION(0x8000, "vsm", 0)
	ROM_LOAD("cm62312.bin", 0x0000, 0x4000, CRC(93b817de) SHA1(03863087a071b8f22d36a52d18243f1c33e17ff7)) /* system speech ROM */
ROM_END

} // anonymous namespace


//   YEAR   NAME     PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY       FULLNAME   FLAGS
COMP(1984,  exl100,  0,      0,      exl100,  exelv, exelv_state, empty_init, "Exelvision", "EXL 100", MACHINE_NOT_WORKING)
COMP(1986,  exeltel, exl100, 0,      exeltel, exelv, exelv_state, empty_init, "Exelvision", "Exeltel", MACHINE_NOT_WORKING)



exorciser.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:68bit
/***************************************************************************

Motorola M6800 EXORciser I (M68SDT)

Note The EXORciser II (M68SDT II) was distinctly different, using dual 64k
address maps (a user map and a supervisor map) and other improvements, and
used EXBUG 2.

To boot MDOS using the ROM disk boot press 'X' until the EXBUG prompt is seen,
and then enter "MAID" without a LF or CR, and then at the "*" prompt enter
E800;G without a LF or CR.

EXBUG 1.1 and 1.2 commands:
X
  General escape command, exits to the prompt.
MAID
  Switches to the MAID (Motorola Active Interface Debug) mode.
LOAD
  Load S19 format binary object tapes. At the "SGL/CONT" prompt press S to
  load a single program, or press C to load multiple programs and the abort
  button when finished, or press X to abort.
VERF
  Verifies memory from tape data. At the "SGL/CONT" prompt press S to verify a
  single program, or press C to verify multiple programs and the abort button
  when finished, or press X to abort.
SRCH
  Searches for a S0 header and prints it and prompts CONT/LOAD/VERF and then
  press C to continue searching, or press L to switch to the LOAD function, or
  press V to switch to the VERF function, or press X to abort.
PNCH
  Prompts for a begin and an end address and then for a header of 6 characters
  and then prints "EXEC" and waits for Y and then outputs the memory range in
  S19 format, or press X to abort.
PRNT
  Prints a memory dump. Prompts for a begin and an end address then prints
  "EXEC" and waits for Y and then dumps the memory in a readable format, or
  press X to abort.
TERM (EXBUG 1.2 extension)
  Prompts for a 15 bit hex number which is used as a terminal output delay
  between characters.
S10.
S30.
S120
S240 (EXBUG 1.2 extension)
  Select different communication protocols for the tape drive.


MAID (Motorola Active Interface Debug) mode has a '*' prompt and the
following commands:
X
  General escape command, exits the command or MAID mode.
xxxx/
  Memory examine and change.
    xx LF   - enter new value, and advance to the next address
    LF      - advance to the next address
    ^       - previous address
    xxxx;O  - calculate a branch offset from the current to the given address
              printing INVL if out of range.
$R
  Register display and change. LF advances to the next register.
xxxx;V
  Insert a software breakpoint at the given address, up to 8 breakpoints.
  These are stored starting at address $ff4f.
$V
  Prints the address of the 8 breakpoints.
xxxx;U
  Removes the software breakpoint at the given address.
;U
  Removes all software breakpoints.
$M
  Prints the current search mask and prompts for a new search mask and address
  range for use by the ;W search command.
xx;W
  Searches the memory range set by the $M command for a byte matching the
  given xx masked with the mask set by the $M command, printing all
  matches. The test is (byte^xx) & mask == 0.
$T
  Prompts for an end address, to trace this address when code is next run.
;T
  Deactivates the trace end address set by the $T command.
$S
  Prompts for a stop address, which it sets and activates. This is a hardware
  breakpoint that activates when the address is touched.
;S
  Deactivates the stop address, as set by the $S command.
;G
  Execute the user's program from the auto start memory location.
xxxx;G
  Execute the user's program from the given address.
;P
  Continue executing from the current program counter address.
nn;P
  Continue executing from the current program counter address, skipping the
  given number of software breakpoints. This does not appear to be reliable
  at least not in the emulator?
N
  Trace one instruction.
;N
xxxx;N
  Trace the next instruction or the given number of instructions.
#nnnnn=
  Converts the given decimal number to hex.
#@nnn=
  Converts the given octal number to hex.
#$xxxx=
  Converts the given hex number to decimal.


For all tape formats the PNCH command sends code 0x12, aka DC2, to start tape
recording and code 0x14, aka DC4, to stop tape recording. For tape format
S120, the codes 0x10, 0x30, 0x12 are sent to start tape recording, and the
codes 0x14, 0x10, 0x39 are sent to stop tape recording.

For tape formats S10 and S30 the ACIA RTS line is used for tape motor control
when reading from the tape, but not when writing to the tape, and the code
0x11, aka DC1, is sent to start tape playback, and the code 0x13, aka DC3, is
sent to stop tape playback.

For tape format S120, the ACIA RTS line is not used for tape motor control,
rather the codes 0x10, 0x37 are sent to start playback from the tape, and the
code 0x13 is sent to stop playback.

References:

"M6800 EXORciser User's Guide.", second edition, Motorola, 1975.

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

#include "emu.h"
#include "bus/rs232/exorterm.h"
#include "bus/rs232/null_modem.h"
#include "bus/rs232/pty.h"
#include "bus/rs232/rs232.h"
#include "bus/rs232/terminal.h"
#include "cpu/m6800/m6800.h"
#include "machine/bankdev.h"
#include "machine/input_merger.h"
#include "machine/6821pia.h"
#include "machine/6850acia.h"
#include "machine/mc14411.h"
#include "machine/m68sfdc.h"

#include "imagedev/printer.h"

#include "formats/mdos_dsk.h"


namespace {

class exorciser_state : public driver_device
{
public:
	exorciser_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_bankdev(*this, "bankdev")
		, m_mainirq(*this, "mainirq")
		, m_mainnmi(*this, "mainnmi")
		, m_maincpu_clock(*this, "MAINCPU_CLOCK")
		, m_abort_key(*this, "ABORT_KEY")
		, m_pia_dbg(*this, "pia_dbg")
		, m_acia(*this, "acia")
		, m_brg(*this, "brg")
		, m_rs232_baud(*this, "RS232_BAUD")
		, m_rs232_config(*this, "RS232_CONFIG")
		, m_pia_lpt(*this, "pia_lpt")
		, m_printer(*this, "printer")
		, m_acia_prn(*this, "acia_prn")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "floppy%u", 0U)
	{ }

	void exorciser(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(maincpu_clock_change);
	void abort_key_w(int state);

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	void dbg_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	void irq_line_w(int state);
	u8 m_irq;
	address_space *m_banked_space;
	u8 main_r(offs_t offset);
	void main_w(offs_t offset, u8 data);
	u8 prom_r(offs_t offset);

	required_device<m6800_cpu_device> m_maincpu;
	required_device<address_map_bank_device> m_bankdev;
	required_device<input_merger_device> m_mainirq;
	required_device<input_merger_device> m_mainnmi;
	required_ioport m_maincpu_clock;
	required_ioport m_abort_key;
	required_device<pia6821_device> m_pia_dbg;
	required_device<acia6850_device> m_acia;
	required_device<mc14411_device> m_brg;
	required_ioport m_rs232_baud;
	required_ioport m_rs232_config;
	required_device<pia6821_device> m_pia_lpt;
	required_device<printer_image_device> m_printer;
	required_device<acia6850_device> m_acia_prn;
	required_device<m68sfdc_device> m_fdc;
	required_device_array<floppy_connector, 4> m_floppy;

	// RS232 bit rate generator clocks
	void write_f1_clock(int state);
	void write_f3_clock(int state);
	[[maybe_unused]] void write_f5_clock(int state);
	void write_f7_clock(int state);
	void write_f8_clock(int state);
	void write_f9_clock(int state);
	[[maybe_unused]] void write_f11_clock(int state);
	void write_f13_clock(int state);

	u8 m_restart_count;

	emu_timer *m_trace_timer;
	TIMER_CALLBACK_MEMBER(assert_trace);

	void pia_dbg_pa_w(u8 data);
	int pia_dbg_ca1_r();
	void pia_dbg_pb_w(u8 data);
	void pia_dbg_ca2_w(int state);
	void pia_dbg_cb2_w(int state);
	u16 m_stop_address;
	u8 m_stop_enabled;

	void pia_lpt_pa_w(u8 data);
	void pia_lpt_ca2_w(int state);
	uint8_t pia_lpt_pb_r();
	uint8_t m_printer_data;
	uint8_t m_printer_data_ready;

	static void exorciser_rs232_devices(device_slot_interface &device);

	static void floppy_formats(format_registration &fr);
};

void exorciser_state::dbg_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(exorciser_state::main_r), FUNC(exorciser_state::main_w));
}

void exorciser_state::mem_map(address_map &map)
{
	// User RAM
	map(0x0000, 0xe800).ram();

	// Disk driver code.
	map(0xe800, 0xebff).rom().region("68fdc2", 0);

	// Disk driver unit
	map(0xec00, 0xec07).rw(m_fdc, FUNC(m68sfdc_device::read), FUNC(m68sfdc_device::write));

	// Line printer
	map(0xec10, 0xec13).rw(m_pia_lpt, FUNC(pia6821_device::read), FUNC(pia6821_device::write));

	// Serial printer.
	map(0xec26, 0xec27).rw(m_acia_prn, FUNC(acia6850_device::read), FUNC(acia6850_device::write));

	// EXBUG
	map(0xf000, 0xfbff).rom().region("exbug", 0);

	map(0xfcf4, 0xfcf5).mirror(0x0002).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xfcf8, 0xfcfb).rw(m_pia_dbg, FUNC(pia6821_device::read), FUNC(pia6821_device::write));

	// Small PROM with the restart vector and ACIA settings.
	map(0xfcfc, 0xfcff).r(FUNC(exorciser_state::prom_r));

	// EXBUG RAM
	map(0xff00, 0xffff).ram();
}

/* Input ports */
static INPUT_PORTS_START( exorciser )

	PORT_START("ABORT_KEY")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Abort") PORT_WRITE_LINE_MEMBER(FUNC(exorciser_state::abort_key_w))

	// The EXORciser I supported 1MHz, and the EXORciser II also supported
	// 1.5 and 2.0MHz.
	PORT_START("MAINCPU_CLOCK")
	PORT_CONFNAME(0xffffff, 1000000, "CPU clock") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(exorciser_state::maincpu_clock_change), 0)
	PORT_CONFSETTING(1000000, "1.0 MHz")
	PORT_CONFSETTING(2000000, "1.5 MHz")
	PORT_CONFSETTING(4000000, "2.0 MHz")

	PORT_START("RS232_BAUD")
	PORT_CONFNAME(0xff, 1, "RS232 Baud Rate")
	PORT_CONFSETTING(0x80, "110")
	PORT_CONFSETTING(0x40, "150")
	PORT_CONFSETTING(0x20, "300")
	PORT_CONFSETTING(0x10, "600")
	PORT_CONFSETTING(0x08, "1200")
	PORT_CONFSETTING(0x04, "2400")
	PORT_CONFSETTING(0x02, "4800")
	PORT_CONFSETTING(0x01, "9600")

	PORT_START("RS232_CONFIG")
	PORT_CONFNAME(0x7F, 0x75, "RS232 Config")
	PORT_CONFSETTING(0x61, "7 data bits, 2 stop bits, even parity")
	PORT_CONFSETTING(0x65, "7 data bits, 2 stop bits, odd parity")
	PORT_CONFSETTING(0x69, "7 data bits, 1 stop bits, even parity")
	PORT_CONFSETTING(0x6d, "7 data bits, 1 stop bits, odd parity")
	PORT_CONFSETTING(0x71, "8 data bits, 2 stop bits, no parity")
	PORT_CONFSETTING(0x75, "8 data bits, 1 stop bit, no parity")
	PORT_CONFSETTING(0x79, "8 data bits, 1 stop bit, even parity")
	PORT_CONFSETTING(0x7d, "8 data bits, 1 stop bit, odd parity")

INPUT_PORTS_END

INPUT_CHANGED_MEMBER(exorciser_state::maincpu_clock_change)
{
	m_maincpu->set_clock(newval);
}


void exorciser_state::write_f1_clock(int state)
{
	if (BIT(m_rs232_baud->read(), 0))
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}

	m_acia_prn->write_txc(state);
	m_acia_prn->write_rxc(state);
}

void exorciser_state::write_f3_clock(int state)
{
	if (BIT(m_rs232_baud->read(), 1))
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}

void exorciser_state::write_f5_clock(int state)
{
	if (BIT(m_rs232_baud->read(), 2))
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}

void exorciser_state::write_f7_clock(int state)
{
	if (BIT(m_rs232_baud->read(), 3))
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}

void exorciser_state::write_f8_clock(int state)
{
	if (BIT(m_rs232_baud->read(), 4))
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}

void exorciser_state::write_f9_clock(int state)
{
	if (BIT(m_rs232_baud->read(), 5))
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}

void exorciser_state::write_f11_clock(int state)
{
	if (BIT(m_rs232_baud->read(), 6))
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}

void exorciser_state::write_f13_clock(int state)
{
	if (BIT(m_rs232_baud->read(), 7))
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}

u8 exorciser_state::main_r(offs_t offset)
{
	if (offset == m_stop_address && m_stop_enabled &&
		!machine().side_effects_disabled())
	{
		m_pia_dbg->cb1_w(CLEAR_LINE);
		m_pia_dbg->cb1_w(ASSERT_LINE);
		m_pia_dbg->cb1_w(CLEAR_LINE);
		// The PIA does not connect to the NMI, rather
		// this triggers some logic that triggers the NMI.
		// The stop-on-address works with just this.
		// TODO This logic might have some delay etc?
		m_mainnmi->in_w<0>(1);
		m_mainnmi->in_w<0>(0);
	}
	if (offset < 0xfffc)
		return m_banked_space->read_byte(offset);
	else if (m_restart_count < 2)
	{
		// The MAME debugger appears to read here on reset to
		// initialize the PC, so it is not possible to distinguish a
		// normal and debug read, so disable this path after the first
		// two reads irrespective of side effects being enabled.
		m_restart_count++;
		if (offset == 0xfffe)
			return 0xf0;
		if (offset == 0xffff)
			return 0x00;
		return 0;
	}
	return m_banked_space->read_byte(offset);
}

void exorciser_state::main_w(offs_t offset, u8 data)
{
	m_banked_space->write_byte(offset, data);
}


// The PROM occupies four addresses 0xfcfc to 0xfcff, decoding A0 and A1. It is
// used to supply the restart address, the first two reads which will be from
// 0xfffe and 0xffff are redirected to this PROM.
//
// The EXBUG firmware reads 0xfcfd to obtain some ACIA configuation bits. EXBUG
// 1.1 masks out all bits except 0x75, and EXBUG 1.2 masks out all bits except
// 0x7f.
//
// The A2 input comes from a circuit that selects the number of stop bits,
// however this might have only been effective at 150 baud. These are config
// options here, as if that small PROM was configured to the desired serial
// protocol settings. Since EXBUG 1.1 masks more of these bits is has less
// effective options.
//
// Input A3 is connected to the /IRQ line and has the effect of setting high
// bits in the byte read from 0xfcfd to allow probing of the /IRQ line. This
// feature does not appear to be used by EXBUG.
//
// Input A4 is a static level selectable via jumpers and when clear all reads
// from 0xfcfc to 0xfcff appear to return zero in the default PROM.
u8 exorciser_state::prom_r(offs_t offset)
{
	switch (offset)
	{
	case 0:
		return 0;
	case 1: {
		u8 byte = m_rs232_config->read();
		if (!m_irq)
			byte |= 0xf0;
		return byte;
	}
	case 2:
		// Restart vector
		return 0xf0;
	case 3:
		return 0x00;
	}

	return 0;
}


void exorciser_state::pia_dbg_pa_w(u8 data)
{
	m_stop_address = (m_stop_address & 0xff00) | data;
}

void exorciser_state::abort_key_w(int state)
{
	m_pia_dbg->ca1_w(!state);
	m_mainnmi->input_merger_device::in_w<2>(state);
}

int exorciser_state::pia_dbg_ca1_r()
{
	return !m_abort_key->read();
}

void exorciser_state::pia_dbg_pb_w(u8 data)
{
	m_stop_address = (m_stop_address & 0x00ff) | (data << 8);
}

void exorciser_state::pia_dbg_ca2_w(int state)
{
	m_stop_enabled = !state;
}


TIMER_CALLBACK_MEMBER(exorciser_state::assert_trace)
{
	m_mainnmi->input_merger_device::in_w<1>(ASSERT_LINE);
}

// Note the trace timer delay is actually 11 cycles, but is stretched to 16
// cycles here to get it working. This is necessary because of inaccurate
// cycle timing in the 6800 emulation, so change the delay to 11 cycles when
// the cycle emulation is more accurate.
void exorciser_state::pia_dbg_cb2_w(int state)
{
	if (state)
	{
		// The trace timer needs to scale with the CPU clock.
		uint32_t maincpu_clock = m_maincpu_clock->read();
		if (!maincpu_clock)
			maincpu_clock = 10000000;

		m_trace_timer->adjust(attotime::from_ticks(16, maincpu_clock));
	}
	else
		m_mainnmi->input_merger_device::in_w<1>(CLEAR_LINE);
}



void exorciser_state::pia_lpt_pa_w(u8 data)
{
	// External parallel printer data output.
	m_printer_data = data;
}

void exorciser_state::pia_lpt_ca2_w(int state)
{
	// External parallel printer data ready.

	// Trigger on the falling edge.
	if (m_printer_data_ready == 1 && state == 0)
	{
		m_printer->output(m_printer_data);
		// Toggle the printer busy line as the software waits for a
		// low to high transition.
		m_pia_lpt->ca1_w(CLEAR_LINE);
		m_pia_lpt->ca1_w(ASSERT_LINE);
		m_pia_lpt->ca1_w(CLEAR_LINE);
	}
	m_printer_data_ready = state;
}


uint8_t exorciser_state::pia_lpt_pb_r()
{
	// The printer driver expects the low two bits to be 01 for a printer
	// attempt to succeed.
	return 1;
}


void exorciser_state::floppy_formats(format_registration &fr)
{
	fr.add_fm_containers();
	fr.add(FLOPPY_MDOS_FORMAT);
}


static void mdos_floppies(device_slot_interface &device)
{
	device.option_add("8sssd",  FLOPPY_8_SSSD);       // 77 trks ss sd 8"
	device.option_add("8dssd",  FLOPPY_8_DSSD);       // 77 trks ds sd 8"
}


void exorciser_state::irq_line_w(int state)
{
	m_maincpu->set_input_line(M6800_IRQ_LINE, state);
	m_irq = state;
}

void exorciser_state::machine_reset()
{
	uint32_t maincpu_clock = m_maincpu_clock->read();
	if (maincpu_clock)
		m_maincpu->set_clock(maincpu_clock);

	m_brg->rsa_w(0);
	m_brg->rsb_w(1);

	m_restart_count = 0;

	m_fdc->set_floppies_4(m_floppy[0], m_floppy[1], m_floppy[2], m_floppy[3]);

	m_irq = 1;
	m_stop_address = 0x0000;
	m_stop_enabled = 0;

	m_printer_data = 0;
	m_printer_data_ready = 1;
	m_pia_lpt->ca1_w(CLEAR_LINE);

}

void exorciser_state::machine_start()
{
	m_banked_space = &subdevice<address_map_bank_device>("bankdev")->space(AS_PROGRAM);

	save_item(NAME(m_restart_count));
	save_item(NAME(m_irq));
	save_item(NAME(m_stop_address));
	save_item(NAME(m_stop_enabled));
	save_item(NAME(m_printer_data));
	save_item(NAME(m_printer_data_ready));

	m_trace_timer = timer_alloc(FUNC(exorciser_state::assert_trace), this);
}

static DEVICE_INPUT_DEFAULTS_START(printer)
	DEVICE_INPUT_DEFAULTS("RS232_RXBAUD", 0xff, RS232_BAUD_9600)
	DEVICE_INPUT_DEFAULTS("RS232_DATABITS", 0xff, RS232_DATABITS_8)
	DEVICE_INPUT_DEFAULTS("RS232_PARITY", 0xff, RS232_PARITY_NONE)
	DEVICE_INPUT_DEFAULTS("RS232_STOPBITS", 0xff, RS232_STOPBITS_1)
DEVICE_INPUT_DEFAULTS_END

void exorciser_state::exorciser_rs232_devices(device_slot_interface &device)
{
	device.option_add("exorterm155", SERIAL_TERMINAL_EXORTERM155);
	device.option_add("null_modem", NULL_MODEM);
	device.option_add("terminal", SERIAL_TERMINAL);
	device.option_add("pty", PSEUDO_TERMINAL);
}

void exorciser_state::exorciser(machine_config &config)
{
	/* basic machine hardware */
	M6800(config, m_maincpu, 10000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &exorciser_state::dbg_map);

	ADDRESS_MAP_BANK(config, m_bankdev, 0);
	m_bankdev->set_endianness(ENDIANNESS_BIG);
	m_bankdev->set_data_width(8);
	m_bankdev->set_addr_width(16);
	m_bankdev->set_addrmap(AS_PROGRAM, &exorciser_state::mem_map);

	INPUT_MERGER_ANY_HIGH(config, m_mainirq).output_handler().set(FUNC(exorciser_state::irq_line_w));
	INPUT_MERGER_ANY_HIGH(config, m_mainnmi).output_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);

	MC14411(config, m_brg, XTAL(1'843'200));
	m_brg->out_f<1>().set(FUNC(exorciser_state::write_f1_clock));
	m_brg->out_f<3>().set(FUNC(exorciser_state::write_f3_clock));
	m_brg->out_f<7>().set(FUNC(exorciser_state::write_f7_clock));
	m_brg->out_f<8>().set(FUNC(exorciser_state::write_f8_clock));
	m_brg->out_f<9>().set(FUNC(exorciser_state::write_f9_clock));
	m_brg->out_f<13>().set(FUNC(exorciser_state::write_f13_clock));

	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_acia->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", exorciser_state::exorciser_rs232_devices, "exorterm155"));
	rs232.rxd_handler().set(m_acia, FUNC(acia6850_device::write_rxd));

	PIA6821(config, m_pia_dbg);
	m_pia_dbg->writepa_handler().set(FUNC(exorciser_state::pia_dbg_pa_w));
	m_pia_dbg->readca1_handler().set(FUNC(exorciser_state::pia_dbg_ca1_r));
	m_pia_dbg->writepb_handler().set(FUNC(exorciser_state::pia_dbg_pb_w));
	m_pia_dbg->ca2_handler().set(FUNC(exorciser_state::pia_dbg_ca2_w));
	m_pia_dbg->cb2_handler().set(FUNC(exorciser_state::pia_dbg_cb2_w));

	// MEX68PI Parallel printer port
	PIA6821(config, m_pia_lpt);
	m_pia_lpt->writepa_handler().set(FUNC(exorciser_state::pia_lpt_pa_w));
	m_pia_lpt->ca1_w(0); // External parallel printer busy input.
	m_pia_lpt->ca2_handler().set(FUNC(exorciser_state::pia_lpt_ca2_w));
	m_pia_lpt->readpb_handler().set(FUNC(exorciser_state::pia_lpt_pb_r));

	PRINTER(config, m_printer, 0);

	// MEX6850? Serial printer port
	ACIA6850(config, m_acia_prn, 0);
	m_acia_prn->txd_handler().set("rs232_prn", FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232_prn(RS232_PORT(config, "rs232_prn", default_rs232_devices, "printer"));
	rs232_prn.rxd_handler().set(m_acia_prn, FUNC(acia6850_device::write_rxd));
	rs232_prn.set_option_device_input_defaults("printer", DEVICE_INPUT_DEFAULTS_NAME(printer));

	M68SFDC(config, m_fdc, 0);
	m_fdc->irq_handler().set(m_mainirq, FUNC(input_merger_device::in_w<0>));
	m_fdc->nmi_handler().set(m_mainnmi, FUNC(input_merger_device::in_w<3>));

	for (auto &floppy : m_floppy)
		FLOPPY_CONNECTOR(config, floppy, mdos_floppies, "8dssd", exorciser_state::floppy_formats).enable_sound(true);
}

/* ROM definition */
ROM_START( exorciser )
	ROM_REGION( 0x0400, "68fdc2", 0 )
	// Later MDOS versions support four double sided disk drives, but these
	// ROMs only support two singled sided disk drives. These ROMs are hard
	// coded for 26 sectors per track and up to 2002 sectors fill in the 77
	// tracks. Both these ROM versions appear to have largely compatible
	// entry points. The MDOS equate file calls this the Disk EROM.
	//
	// Various MDOS commands, such a 'format' and 'dosgen' probe this ROM
	// at 0xebfe and 0xebff and use the contents to select between
	// operating modes and features. There are clearly some ROMs supported
	// by MDOS that are not represented here. If 0xebff has 'E' then the
	// MDOS FORMAT command errors out. If 0xebff has 'C' then the 'write
	// enabled' input is interpreted as being an active low write protect
	// input, otherwise it is interpreted as being an active low write
	// enabled input. If 0xebff has 'C' or 0xebfe has 0x11 or 0x12 then
	// disks are formatted single sided otherwise PA5 is consulted.
	//
	// The MDOS code, such as DOSGEN, expects the byte at $000d to have bit
	// 7 set for single sided and clear for double sided disks, yet these
	// ROMs clear this bit. For DOSGEN this can be worked around with these
	// ROMs by locking out the sectors that would have been allocated
	// beyond the single sided disk extent, so lock out 0x7d0 to 0xfa0. The
	// FORMAT command does not look at this bit so is not affected by this
	// issue. Clearly neither of these ROMs was intended to work cleanly
	// with MDOS 3 even using single sided disks.
	//
	// So it would be great if a ROM can be found that supports double
	// sided disks, but if not then there appears to be enough known to
	// write a ROM to work with MDOS. The ROM is tightly written already,
	// and it is not clear how well double sided support was implemented,
	// but the ROM accepts logical sector numbers and to work with both
	// single and double sided disks it may need to firstly try a double
	// sided operation and if that fails then to fall back to try a single
	// sided operation and to set the 0x000d flag appropriately. Note that
	// the FORMAT command does not bother to set the 'side' in the address
	// marks, so the ROM should ignore that or expect it to be
	// zero. Support also needs to be added for four drives which is just a
	// matter of setting the 'select 2' output based on bit 1 of 0x0000 and
	// updating the bounds check.
	//
	// The code compression techniques used in this disk driver code are
	// edifying, generally reusing code well. For example, flags are used
	// to make subtle changes in code paths to reuse many paths. Test or
	// comparison instructions are used to probe I/O addresses without
	// destroying an accumulator to reduce register pressure.
	//
	// Good use is made of instructions with overlapping
	// interpretations. For example, placing small instructions in the
	// operands of other instructions where they become effectively a NOP
	// and this saves on branching. Two byte instructions such as loading
	// an accumulator are placed in the 16 bit immediate argument of a
	// three byte instruction, or one byte instructions such as a shift or
	// rotate are placed in the immediate argument of a two byte
	// instruction.
	//
	// This version supports two single sided disk drives. The use of the
	// 'step' and 'direction' lines is conventional. It interprets a high
	// on the 'write enabled' input as an active low write protected
	// input. It drives a serial printer using a 6850 ACIA at $ec26-ec27,
	// which is configured for 8 bits, no parity, and 1 stop bit, and
	// clocked at 9600 baud here, and it supports XON-XOFF software flow
	// control. The last byte of this ROM has been patched from 0x00 to
	// 0x43 to work with the MDOS feature detection code.
	//
	ROM_LOAD("diskeromv2.bin", 0x0000, 0x0400, CRC(09b1e724) SHA1(59b54ded1f9a7266c1f12da37f700b4b478b84bc))

	// This version supports two single sided disk drives. It toggles the
	// 'step' line low to step towards track 0 and toggles the 'direction'
	// line low to step away from track 0. It interprets a high on the
	// 'write enabled' input as an active low write enabled input rather
	// than an active low write protected input. It drives a parallel
	// printer using a 6821 PIA at $ec10-ex13. The last two bytes of this
	// PROM are 0x07 and 0x50 ('P') - these codes are probed by some MDOS
	// commands to select different operation and features.
	//ROM_LOAD("diskeromv1.bin", 0x0000, 0x0400, CRC(87bf9b0d) SHA1(dbcce885d21b418a08812234d1581ac101f32536))

	ROM_REGION( 0x0c00, "exbug", 0 )
	// EXBUG 1.2 adds support for the 'S240' command another punch tape
	// protocol, and a 'TERM' command which accepts a 15 bit hex number as
	// a terminal output delay between characters.
	ROM_DEFAULT_BIOS("exbug12")
	ROM_SYSTEM_BIOS(0, "exbug12", "EXBUG version 1.2")
	ROMX_LOAD("exbug12.bin", 0x0000, 0x0c00, CRC(c002759b) SHA1(0f63d75148e82fb0b9ce7d64b91464523937e0b7), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "exbug11", "EXBUG version 1.1")
	ROMX_LOAD("exbug11.bin", 0x0000, 0x0c00, CRC(5a5db110) SHA1(14f3e14ed809f9ec30b8189e5506ed911127de34), ROM_BIOS(1))

ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY                                     FULLNAME      FLAGS
COMP( 1975, exorciser,  0,      0,      exorciser,   exorciser, exorciser_state, empty_init, "Motorola", "M6800 EXORciser (M68SDT)", MACHINE_NO_SOUND_HW )



exorterm.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: 68bit
/****************************************************************************

    Stand alone front end for the Motorola EXORterm 155 (M68SDS)

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

#include "emu.h"
#include "machine/exorterm.h"
#include "bus/rs232/rs232.h"


namespace {

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

class exorterm155_state : public driver_device
{
public:
	exorterm155_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_exorterm155(*this, "exorterm155")
		, m_host(*this, "host")
	{ }

	void exorterm155(machine_config &config);

private:
	required_device<exorterm155_device> m_exorterm155;
	required_device<rs232_port_device> m_host;
};

void exorterm155_state::exorterm155(machine_config &config)
{
	EXORTERM155(config, m_exorterm155, 0);

	RS232_PORT(config, m_host, default_rs232_devices, nullptr);
	m_host->dcd_handler().set(m_exorterm155, FUNC(exorterm155_device::rs232_conn_dcd_w));
	m_host->dsr_handler().set(m_exorterm155, FUNC(exorterm155_device::rs232_conn_dsr_w));
	m_host->ri_handler().set(m_exorterm155, FUNC(exorterm155_device::rs232_conn_ri_w));
	m_host->cts_handler().set(m_exorterm155, FUNC(exorterm155_device::rs232_conn_cts_w));
	m_host->rxd_handler().set(m_exorterm155, FUNC(exorterm155_device::rs232_conn_rxd_w));

	m_exorterm155->rs232_conn_txd_handler().set(m_host, FUNC(rs232_port_device::write_txd));
	m_exorterm155->rs232_conn_dtr_handler().set(m_host, FUNC(rs232_port_device::write_dtr));
	m_exorterm155->rs232_conn_rts_handler().set(m_host, FUNC(rs232_port_device::write_rts));
}


ROM_START(exorterm155)
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS         INIT        COMPANY  FULLNAME    FLAGS
COMP( 1979, exorterm155,  0,      0,      exorterm155,  0,  exorterm155_state,  empty_init, "Motorola",  "EXORterm 155",  MACHINE_SUPPORTS_SAVE )



exp85.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/***************************************************************************

    Explorer 85

    12/05/2009 Skeleton driver.
    29/12/2023 Reworked driver to enable extended RAM memory,
               MS BASIC and disable ROM mirror after boot/interrupt.

    Setting Up
    ==========
    The terminal must be set for
    - Baud: 9600
    - Format: 7E1
    If it isn't, adjust the settings, then restart the system.

    Once started, press Space. The system will start up.
    All input must be in upper case.

    Microsoft Basic is executed entering the following 2 commands
    in the monitor prompt:
    .XS nnnn-F87F mmmm-C000 <CR>
    .G <CR>

    where nnnn is the previous value of the stack pointer, and mmmm is the
    previous value of the program counter.

    BASIC will request the amount of RAM memory available, 8192 (bytes) must
    be entered.


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

/*

    TODO:

    - dump of the hexadecimal keyboard monitor ROM
    - 64K RAM expansion
    - Disk drive and CP/M OS support

*/


#include "emu.h"

#include "machine/i8155.h"
#include "machine/i8355.h"
#include "machine/ram.h"
#include "speaker.h"

#include "bus/rs232/rs232.h"
#include "cpu/i8085/i8085.h"
#include "imagedev/cassette.h"
#include "sound/spkrdev.h"

namespace {

static constexpr int LOW_MEMORY_ROM_MIRROR_ENTRY = 0;
static constexpr int LOW_MEMORY_RAM_ENTRY        = 1;

class exp85_state : public driver_device
{
public:
	exp85_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "u100")
		, m_i8355(*this, "u105")
		, m_i8155(*this, "u106")
		, m_rs232(*this, "rs232")
		, m_cassette(*this, "cassette")
		, m_speaker(*this, "speaker")
		, m_rom(*this, "u105") // the 8355 chip contains the monitor ROM
		, m_low_memory_view(*this, "low_memory_view")
		, m_is_preparing_interrupt_call(false)
		, m_ignore_timer_out(true)
		, m_tape_control(0)
		, m_timer(nullptr)
	{ }

	void exp85(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER( trigger_reset );
	DECLARE_INPUT_CHANGED_MEMBER( trigger_rst75 );

private:
	virtual void machine_start() override ATTR_COLD;

	uint8_t i8355_a_r();
	void i8355_a_w(uint8_t data);
	int sid_r();
	void sod_w(int state);

	void status_out(u8 status);

	void exp85_io(address_map &map) ATTR_COLD;
	void exp85_mem(address_map &map) ATTR_COLD;

	void to_change(int to);

	TIMER_CALLBACK_MEMBER(trap_delay);

	// Member variables
	required_device<i8085a_cpu_device> m_maincpu;
	required_device<i8355_device> m_i8355;
	required_device<i8155_device> m_i8155;
	required_device<rs232_port_device> m_rs232;
	required_device<cassette_image_device> m_cassette;
	required_device<speaker_sound_device> m_speaker;
	required_memory_region m_rom;
	memory_view m_low_memory_view;

	bool m_is_preparing_interrupt_call;
	bool m_ignore_timer_out;
	bool m_tape_control;
	emu_timer *m_timer;
};


/* Memory Maps */
void exp85_state::exp85_mem(address_map &map)
{
	map.unmap_value_high();
	// Extended RAM or mapped monitor ROM (only during interrupt and reset)
	map(0x0000, 0x1fff).view(m_low_memory_view);
	// Microsoft BASIC ROM
	map(0xc000, 0xdfff).rom();
	// Monitor ROM in the 8355 chip
	map(0xf000, 0xf7ff).rom().region(m_rom, 0);
	// 256 bytes of RAM of level A in the 8155 chip.
	map(0xf800, 0xf8ff).rw(m_i8155, FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w));

	// monitor ROM mirror
	m_low_memory_view[LOW_MEMORY_ROM_MIRROR_ENTRY](0x0000, 0x07ff).rom().region(m_rom, 0);
	// extended RAM
	m_low_memory_view[LOW_MEMORY_RAM_ENTRY](0x0000, 0x1fff).ram();
}

void exp85_state::exp85_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0xf0, 0xf3).rw(m_i8355, FUNC(i8355_device::io_r), FUNC(i8355_device::io_w));
	map(0xf8, 0xfd).rw(m_i8155, FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
}

/* Input Ports */

INPUT_CHANGED_MEMBER( exp85_state::trigger_reset )
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? CLEAR_LINE : ASSERT_LINE);
	m_low_memory_view.select(LOW_MEMORY_ROM_MIRROR_ENTRY);
}

INPUT_CHANGED_MEMBER( exp85_state::trigger_rst75 )
{
	m_maincpu->set_input_line(I8085_RST75_LINE, newval ? CLEAR_LINE : ASSERT_LINE);
}

static INPUT_PORTS_START( exp85 )
	PORT_START("SPECIAL")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("R") PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(exp85_state::trigger_reset), 0)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("I") PORT_CODE(KEYCODE_F2) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(exp85_state::trigger_rst75), 0)
INPUT_PORTS_END

/* 8355 Interface */

uint8_t exp85_state::i8355_a_r()
{
	/*

	    bit     description

	    PA0     tape control
	    PA1     jumper S17 (open=+5V closed=GND)
	    PA2     J5:13
	    PA3
	    PA4     J2:22
	    PA5
	    PA6
	    PA7     speaker output

	*/

	return 0x02;
}

void exp85_state::i8355_a_w(uint8_t data)
{
	/*

	    bit     description

	    PA0     tape control
	    PA1     jumper S17 (open=+5V closed=GND)
	    PA2     J5:13
	    PA3
	    PA4     J2:22
	    PA5
	    PA6
	    PA7     speaker output

	*/

	/* tape control */
	m_tape_control = BIT(data, 0);

	/* speaker output */
	m_speaker->level_w(!BIT(data, 7));
}

/* I8085A Interface */

int exp85_state::sid_r()
{
	int data = 1;

	if (m_tape_control)
	{
		data = (m_cassette->input() > +0.0);
	}
	else
	{
		data = m_rs232->rxd_r();
	}

	return data;
}

void exp85_state::sod_w(int state)
{
	if (m_tape_control)
	{
		m_cassette->output(state ? -1.0 : +1.0);
	}
	else
	{
		m_rs232->write_txd(state);
	}
}

/* Memory mapping mgmt during reset and interrupt */

void exp85_state::status_out(u8 status)
{
	// In the real hardware this is monitored via the IO/M, S0, S1
	// and ALE output pins of the 8085.
	// Since these pins are not emulated, then we must explicitly get the internal
	// interrupt acknowledge state (0x23 or 0x26) and behave as the monitor expects.
	auto current_pc = m_maincpu->pc();
	if (status == 0x23 || status == 0x26)
	{
		// When an interrupt is triggered the low memory shall be set to the ROM mirror
		m_is_preparing_interrupt_call = true;
		m_low_memory_view.select(LOW_MEMORY_ROM_MIRROR_ENTRY);
	}
	else if (m_is_preparing_interrupt_call && (current_pc & 0xff00) == 0x0000)
	{
		// Avoids setting the lower memory back to RAM until
		// it branches to the interrupt handler.
		m_is_preparing_interrupt_call = false;
	}
	else if (!m_is_preparing_interrupt_call && (current_pc & 0xf000) == 0xf000)
	{
		// When the interrupt handler is executing and the address is >= 0xf000
		// the low memory is mapped to RAM
		m_low_memory_view.select(LOW_MEMORY_RAM_ENTRY);
	}

}

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


/* Terminal Interface */

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

/* Machine Initialization */

void exp85_state::machine_start()
{
	/* clear the trap line that is asserted on start */
	m_maincpu->set_input_line(I8085_TRAP_LINE, CLEAR_LINE);

	/* setup memory mirror switch */
	m_low_memory_view.select(LOW_MEMORY_ROM_MIRROR_ENTRY);

	save_item(NAME(m_tape_control));

	m_timer = timer_alloc(FUNC(exp85_state::trap_delay), this);
}

void exp85_state::to_change(int to)
{
	// In the 8155 implementation the TIMER-OUT line (to) is
	// asserted by default on initialization; this generates a spurious exception
	// avoided here via an ignore flag.
	if (m_ignore_timer_out)
	{
		m_ignore_timer_out = false;
		return;
	}

	// The 8155 has a max TIMER-IN to TIMER-OUT delay of 400ns (datasheet page 3-256). In a real system
	// this delay was measured in ~70ns. This is enough for the next instruction cycle to start
	// and for the interrupt to be acknowledged one instruction later. This effect is fundamental
	// for the ROM monitor stepping functionality, since it depends heavily on interrupting when the
	// next instruction of the user program is being executed.
	// MAME allows to set a timer of 70ns but that is not enough to get the CPU scheduled for at least 1 cycle
	// before the interrupt is serviced, so we program a timer to expire beyond a full clock cycle.
	m_timer->adjust(m_i8155->clocks_to_attotime(2), to);
}

TIMER_CALLBACK_MEMBER(exp85_state::trap_delay)
{
	// The 8155 TIMER-OUT line is connected to the TRAP input line of the 8085;
	// that line is set from the start high, and since there is no change in that line, the
	// interrup is never requested (datasheet, page 6-13).
	m_maincpu->set_input_line(I8085_TRAP_LINE, param == 1 ? ASSERT_LINE : CLEAR_LINE);
}

/* Machine Driver */
void exp85_state::exp85(machine_config &config)
{
	/* basic machine hardware */
	I8085A(config, m_maincpu, 6.144_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &exp85_state::exp85_mem);
	m_maincpu->set_addrmap(AS_IO, &exp85_state::exp85_io);
	m_maincpu->in_sid_func().set(FUNC(exp85_state::sid_r));
	m_maincpu->out_sod_func().set(FUNC(exp85_state::sod_w));
	m_maincpu->out_status_func().set(FUNC(exp85_state::status_out));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	/* devices */

	// The Explorer-85 uses a 6.144MHz clock, but the Intel 8085 divides by 2 this
	// input frequency to give the processor internal operating frequency.
	// The bus is connected to the CLK (out) of the 8085, running also
	// at 6.144MHz divided by 2.
	I8155(config, m_i8155, 6.144_MHz_XTAL/2);
	m_i8155->out_to_callback().set(FUNC(exp85_state::to_change));


	I8355(config, m_i8355, 6.144_MHz_XTAL/2);
	m_i8355->in_pa().set(FUNC(exp85_state::i8355_a_r));
	m_i8355->out_pa().set(FUNC(exp85_state::i8355_a_w));

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	RS232_PORT(config, "rs232", default_rs232_devices, "terminal").set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

}

/* ROMs */

ROM_START( exp85 )
	ROM_REGION( 0x10000, "u100", 0 ) // Microsoft BASIC
	ROM_DEFAULT_BIOS("eia")
	ROM_LOAD( "c000.bin", 0xc000, 0x0800, CRC(73ce4aad) SHA1(2c69cd0b6c4bdc92f4640bce18467e4e99255bab) )
	ROM_LOAD( "c800.bin", 0xc800, 0x0800, CRC(eb3fdedc) SHA1(af92d07f7cb7533841b16e1176401363176857e1) )
	ROM_LOAD( "d000.bin", 0xd000, 0x0800, CRC(c10c4a22) SHA1(30588ba0b27a775d85f8c581ad54400c8521225d) )
	ROM_LOAD( "d800.bin", 0xd800, 0x0800, CRC(dfa43ef4) SHA1(56a7e7a64928bdd1d5f0519023d1594cacef49b3) )

	ROM_REGION( 0x800, "u105", 0 ) // Explorer 85 Monitor
	ROM_SYSTEM_BIOS( 0, "eia", "EIA Terminal" ) // Serial terminal ROM
	ROMX_LOAD( "ex 85.u105", 0x0000, 0x0800, CRC(1a99d0d9) SHA1(57b6d48e71257bc4ef2d3dddc9b30edf6c1db766), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "hex", "Hex Keyboard" ) // Keypad ROM (not available)
	ROMX_LOAD( "1kbd.u105", 0x0000, 0x0800, NO_DUMP, ROM_BIOS(1) )
ROM_END

} // anonymous namespace


/* System Drivers */
//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY      FULLNAME       FLAGS
COMP( 1979, exp85, 0,      0,      exp85,   exp85, exp85_state, empty_init, "Netronics", "Explorer/85", MACHINE_SUPPORTS_SAVE )



ez2d.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/*

TODO:
- Jumps to PC=0xfb000 after the first 2 PCI dword configs, which points to empty 0xff opcodes.
  $3a000 contains an "= Award Decompression BIOS =" header.
  Original ASUS CUBX BIOSes actually have valid opcodes in that range, the dump should be bad.
- ASUS CUBX fails reading RTC, needs virtualizing thru ISA.

- In pcipc ez2d2m HDD boots to a Korean Windows 98SE, it will error out with a
  "Error 7 : Set up the system correctly." after driver installation. Follows dump heuristics:
  \- Disables Windows splash screen, instead it prints "Amuseworld System IV" (edited from autoexec.bat)
  \- Contain a driver for a 3Com 3C90x EtherLink
  \- "C:\Hardware Tools" contains diagnostic DOS .exe files testing ISA range I/Os;
  \- "C:\ez2dancer" is the bulk of game, mostly with *.abm compressed files;
  \- "C:\Drivers" and "C:\Install" contains various BIOSes and install programs,
        is former for earlier HW?
    \-  "C:\Install\Bios1006" contains a "1006cu.awd" file
    \- There are VIA and ALi AGP references (interchangeable?)
    \- "C:\Drivers\BIOSji438" , "fic_437" & "fic_438" all contains FIC PA-2013 BIOSes (original HW?)
    \- "C:\Drivers\Basic" contains Microsoft Tweak UI install files, this looks used by the changes in
       "This PC" icon on bottom right of desktop and for HW cursor when system is in SVGA mode to a dot.
    \- "C:\Drivers\BIOS991026" contains a Riva TNT2 dump (W2137.rom)
    \- "C:\Drivers\monitor" contains a video/monitor test;
  \- Windows reboot and shutdown screens are customized so that former displays the game title, latter
    just goes black screen.

Thanks to Guru for hardware infos and pics for Ez2dancer 2nd Move.
Later games in the series might run on newer, beefier hardware.

ASUS CUBX-103 motherboard
Intel Celeron 533 MHz CPU
128 MB PC100 DIMM RAM
Fujitsu MPD3043AT HD (4.3GB) - The ez2d2m dump in this driver comes from a different, bigger hd
Leadtek Winfast 3D S325 32MB Video Card (might not be the original one)
Sound Blaster Live CT4830 Sound Card
EZ2D-IOCARD-c
Hardlock E-Y-E security dongle

Other games thought to run on this or derived hardware:
Ez2Dancer series:
* Ez2Dancer 1st Move (2000)
* Ez2Dancer UK Move(2002)
* Ez2Dancer UK Move Special Edition (2003)
* Ez2Dancer SuperChina (2004)

Ez2DJ series:
* Ez2Dj The 1st Tracks (1999)
* Ez2DJ The 1st Tracks Special Edition (1999)
* Ez2DJ 2nd TraX: It Rules Once Again (2000)
* Ez2DJ 3rd TraX: Absolute Pitch (2001)
* Ez2DJ 4th TraX: Over Mind (2002)
* Ez2DJ Mini (2003)
* Ez2DJ Dual Platinum (2003)
* Ez2DJ Platinum: Limited Edition (2003)
* Ez2DJ Single (2004)
* Ez2DJ 6th TraX: Self Evolution (2004)
* Ez2DJ 7th TraX: Resistance (2007)
* Ez2DJ 7th TraX Class R Codename: Violet (2009)
* Ez2DJ 7th TraX: Bonus Edition (2011)
* Ez2DJ Azure Expression (2012)
* Ez2DJ Azure Expression: Integral Composition (2012)
*/

#include "emu.h"
#include "cpu/i386/i386.h"
#include "machine/pci.h"
#include "machine/pci-ide.h"
#include "machine/i82443bx_host.h"
#include "machine/i82371eb_isa.h"
#include "machine/i82371eb_ide.h"
#include "machine/i82371eb_acpi.h"
#include "machine/i82371eb_usb.h"
#include "bus/isa/isa_cards.h"
#include "bus/pci/rivatnt.h"
//#include "bus/rs232/hlemouse.h"
//#include "bus/rs232/null_modem.h"
//#include "bus/rs232/rs232.h"
//#include "bus/rs232/sun_kbd.h"
//#include "bus/rs232/terminal.h"
#include "machine/w83977tf.h"


namespace {

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

	void cubx(machine_config &config);
	void ez2d(machine_config &config);

private:
	required_device<pentium2_device> m_maincpu;

	void ez2d_map(address_map &map) ATTR_COLD;
	void ez2d_io(address_map &map) ATTR_COLD;

	static void winbond_superio_config(device_t *device);
};


void ez2d_state::ez2d_map(address_map &map)
{
	map.unmap_value_high();
}

void ez2d_state::ez2d_io(address_map &map)
{
	map.unmap_value_high();
}

static INPUT_PORTS_START( ez2d )
INPUT_PORTS_END

static void isa_internal_devices(device_slot_interface &device)
{
	device.option_add("w83977tf", W83977TF);
}

void ez2d_state::winbond_superio_config(device_t *device)
{
	// TODO: Winbond w83977ef
	w83977tf_device &fdc = *downcast<w83977tf_device *>(device);
//  fdc.set_sysopt_pin(1);
	fdc.gp20_reset().set_inputline(":maincpu", INPUT_LINE_RESET);
	fdc.gp25_gatea20().set_inputline(":maincpu", INPUT_LINE_A20);
	fdc.irq1().set(":pci:07.0", FUNC(i82371eb_isa_device::pc_irq1_w));
	fdc.irq8().set(":pci:07.0", FUNC(i82371eb_isa_device::pc_irq8n_w));
//  fdc.txd1().set(":serport0", FUNC(rs232_port_device::write_txd));
//  fdc.ndtr1().set(":serport0", FUNC(rs232_port_device::write_dtr));
//  fdc.nrts1().set(":serport0", FUNC(rs232_port_device::write_rts));
//  fdc.txd2().set(":serport1", FUNC(rs232_port_device::write_txd));
//  fdc.ndtr2().set(":serport1", FUNC(rs232_port_device::write_dtr));
//  fdc.nrts2().set(":serport1", FUNC(rs232_port_device::write_rts));
}

void ez2d_state::cubx(machine_config &config)
{
	// actually a Celeron at 533 MHz
	PENTIUM2(config, m_maincpu, 90'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &ez2d_state::ez2d_map);
	m_maincpu->set_addrmap(AS_IO, &ez2d_state::ez2d_io);
	m_maincpu->set_irq_acknowledge_callback("pci:07.0:pic8259_master", FUNC(pic8259_device::inta_cb));
	m_maincpu->smiact().set("pci:00.0", FUNC(i82443bx_host_device::smi_act_w));

	PCI_ROOT(config, "pci", 0);
	I82443BX_HOST(config, "pci:00.0", 0, "maincpu", 128*1024*1024);
	I82443BX_BRIDGE(config, "pci:01.0", 0 ); //"pci:01.0:00.0");
	//I82443BX_AGP   (config, "pci:01.0:00.0");

	i82371eb_isa_device &isa(I82371EB_ISA(config, "pci:07.0", 0, "maincpu"));
	isa.boot_state_hook().set([](u8 data) { /* printf("%02x\n", data); */ });
	isa.smi().set_inputline("maincpu", INPUT_LINE_SMI);

	i82371eb_ide_device &ide(I82371EB_IDE(config, "pci:07.1", 0, "maincpu"));
	ide.irq_pri().set("pci:07.0", FUNC(i82371eb_isa_device::pc_irq14_w));
	ide.irq_sec().set("pci:07.0", FUNC(i82371eb_isa_device::pc_mirq0_w));

	I82371EB_USB (config, "pci:07.2", 0);
	I82371EB_ACPI(config, "pci:07.3", 0);
	LPC_ACPI     (config, "pci:07.3:acpi", 0);
	SMBUS        (config, "pci:07.3:smbus", 0);

	ISA16_SLOT(config, "board4", 0, "pci:07.0:isabus", isa_internal_devices, "w83977tf", true).set_option_machine_config("w83977tf", winbond_superio_config);
	ISA16_SLOT(config, "isa1", 0, "pci:07.0:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa2", 0, "pci:07.0:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa3", 0, "pci:07.0:isabus", pc_isa16_cards, nullptr, false);

#if 0
	rs232_port_device& serport0(RS232_PORT(config, "serport0", isa_com, nullptr)); // "microsoft_mouse"));
	serport0.rxd_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::rxd1_w));
	serport0.dcd_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::ndcd1_w));
	serport0.dsr_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::ndsr1_w));
	serport0.ri_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::nri1_w));
	serport0.cts_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::ncts1_w));

	rs232_port_device &serport1(RS232_PORT(config, "serport1", isa_com, nullptr));
	serport1.rxd_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::rxd2_w));
	serport1.dcd_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::ndcd2_w));
	serport1.dsr_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::ndsr2_w));
	serport1.ri_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::nri2_w));
	serport1.cts_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::ncts2_w));
#endif

	PCI_SLOT(config, "pci:01.0:1", agp_cards, 0, 0, 1, 2, 3, "rivatnt").set_fixed(true);
}

void ez2d_state::ez2d(machine_config &config)
{
	ez2d_state::cubx(config);

	PCI_SLOT(config.replace(), "pci:01.0:1", agp_cards, 0, 0, 1, 2, 3, "rivatnt2_m64").set_fixed(true);
	// TODO: Sound Blaster Live CT4830
}

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

  Game drivers

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

ROM_START( asuscubx )
	ROM_REGION32_LE(0x40000, "pci:07.0", 0)
	ROM_LOAD("cubx1007.awd", 0x00000, 0x40000, CRC(42a35507) SHA1(4e428e8419e533424d9564b290e2d7f4931744ff) )
ROM_END

ROM_START( ez2d2m )
	ROM_REGION32_LE(0x40000, "pci:07.0", 0)
	ROM_SYSTEM_BIOS( 0, "1006cu", "OEM" )
	// From HDD "C:\Install\Bios1006"
	ROMX_LOAD("1006cu.awd", 0x00000, 0x40000, CRC(086c320a) SHA1(4b4c07e594602c467e678187f80e3a5c1445bd30), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "award", "Award (unknown rev)" )
	ROMX_LOAD("ez2dancer2ndmove_motherboard_v29c51002t_award_bios", 0x00000, 0x40000, BAD_DUMP CRC(02a5e84b) SHA1(94b341d268ce9d42597c68bc98c3b8b62e137205), ROM_BIOS(1) ) // 29f020

	DISK_REGION( "ide:0:hdd" )
	DISK_IMAGE( "ez2d2m", 0, SHA1(431f0bef3b81f83dad3818bca8994faa8ce9d5b7) )
ROM_END

} // anonymous namespace


COMP( 2000, asuscubx, 0,   0, cubx, 0, ez2d_state, empty_init, "ASUS",        "CUBX", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)

GAME( 2001, ez2d2m, 0, ez2d, ez2d, ez2d_state, empty_init, ROT0,   "Amuse World", "Ez2dancer 2nd Move",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



f387x.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/************************************************************************************************************

    Skeleton driver for Fairchild F387X PEP (Prototyping, Emulating and Programming) System.

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

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/f8/f8.h"
#include "machine/f3853.h"


namespace {

class f387x_state : public driver_device
{
public:
	f387x_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rs232c(*this, "rs232c")
		, m_keypad(*this, "ROW%u", 0U)
	{
	}

	void f387x(machine_config &config);

private:
	u8 p8_r();
	void p8_w(u8 data);
	u8 p9_r();
	void p9_w(u8 data);
	u8 ipor_r();
	void opor_w(u8 data);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<f8_cpu_device> m_maincpu;
	required_device<rs232_port_device> m_rs232c;
	required_ioport_array<6> m_keypad;
};


u8 f387x_state::p8_r()
{
	return 0;
}

void f387x_state::p8_w(u8 data)
{
}

u8 f387x_state::p9_r()
{
	return 0;
}

void f387x_state::p9_w(u8 data)
{
}

u8 f387x_state::ipor_r()
{
	return m_rs232c->rxd_r() ? 0x80 : 0;
}

void f387x_state::opor_w(u8 data)
{
	m_rs232c->write_txd(BIT(data, 0));
}

void f387x_state::mem_map(address_map &map)
{
	map(0x2b80, 0x2bff).ram(); // F6810
	map(0x8000, 0x87ff).rom().region("pepbug", 0);
}

void f387x_state::io_map(address_map &map)
{
	map(0x20, 0x20).rw(FUNC(f387x_state::p8_r), FUNC(f387x_state::p8_w));
	map(0x21, 0x21).rw(FUNC(f387x_state::p9_r), FUNC(f387x_state::p9_w));
	map(0x24, 0x24).rw(FUNC(f387x_state::ipor_r), FUNC(f387x_state::opor_w));
	map(0xeb, 0xeb).nopw(); // code deliberately outputs to nonexistent port here
}


static INPUT_PORTS_START(f387x)
	PORT_START("ROW0")
	PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("C/R") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("DEL") PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("CHG") PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("+") PORT_CODE(KEYCODE_EQUALS)

	PORT_START("ROW1")
	PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("MEM") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("REG") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("PORT") PORT_CODE(KEYCODE_O)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)

	PORT_START("ROW2")
	PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("3  NEXT") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("7  ACC") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("B  FIND") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("F  GO") PORT_CODE(KEYCODE_F)

	PORT_START("ROW3")
	PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("2  PREV") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("6  W") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("A  MOVE") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("E  LOAD") PORT_CODE(KEYCODE_E)

	PORT_START("ROW4")
	PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("1  DC") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("5  ISAR") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("9  HEX") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("D  B CLR") PORT_CODE(KEYCODE_D)

	PORT_START("ROW5")
	PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("0  PC") PORT_CODE(KEYCODE_0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("4  2716") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("8  E70") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("C  BRPT") PORT_CODE(KEYCODE_C)

	PORT_START("SW3")
	PORT_DIPNAME(1, 1, DEF_STR(Unknown)) PORT_DIPLOCATION("SW3:1")
	PORT_DIPSETTING(1, DEF_STR(Off))
	PORT_DIPSETTING(0, DEF_STR(On))
	PORT_DIPNAME(2, 2, DEF_STR(Unknown)) PORT_DIPLOCATION("SW3:2")
	PORT_DIPSETTING(2, DEF_STR(Off))
	PORT_DIPSETTING(0, DEF_STR(On))
	PORT_DIPNAME(4, 4, DEF_STR(Unknown)) PORT_DIPLOCATION("SW3:3")
	PORT_DIPSETTING(4, DEF_STR(Off))
	PORT_DIPSETTING(0, DEF_STR(On))
	PORT_DIPNAME(8, 8, DEF_STR(Unknown)) PORT_DIPLOCATION("SW3:4")
	PORT_DIPSETTING(8, DEF_STR(Off))
	PORT_DIPSETTING(0, DEF_STR(On))
INPUT_PORTS_END

static DEVICE_INPUT_DEFAULTS_START(terminal)
	DEVICE_INPUT_DEFAULTS("RS232_RXBAUD", 0xff, RS232_BAUD_110)
	DEVICE_INPUT_DEFAULTS("RS232_TXBAUD", 0xff, RS232_BAUD_110)
	DEVICE_INPUT_DEFAULTS("RS232_DATABITS", 0xff, RS232_DATABITS_8)
	DEVICE_INPUT_DEFAULTS("RS232_PARITY", 0xff, RS232_PARITY_NONE)
	DEVICE_INPUT_DEFAULTS("RS232_STOPBITS", 0xff, RS232_STOPBITS_2)
DEVICE_INPUT_DEFAULTS_END

void f387x_state::f387x(machine_config &config)
{
	F8(config, m_maincpu, 2_MHz_XTAL); // F3850PC
	m_maincpu->set_addrmap(AS_PROGRAM, &f387x_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &f387x_state::io_map);
	m_maincpu->romc08_callback().set_constant(0x80);

	F3853(config, "smi", 0);

	RS232_PORT(config, m_rs232c, default_rs232_devices, "terminal");
	m_rs232c->set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));
}

ROM_START(f387x)
	ROM_REGION(0x1800, "pepbug", 0)
	ROM_LOAD("pepbug.u16", 0x0000, 0x0800, CRC(de05ac6d) SHA1(220281a7016aae785417bbfe8383c76f72f8fac2))
	// 2716 sockets at U14 and U15 are not populated
ROM_END

} // anonymous namespace


COMP(1979, f387x, 0, 0, f387x, f387x, f387x_state, empty_init, "Fairchild Instrument & Camera Corporation", "F387X PEP System", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)



f4431.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    Facit 4431

    VT100 compatible terminal

    Hardware:
    - Z80
    - TMS9927 CRTC
    - Z80A-DART
    - Z80A-CTC
    - ER1400
    - 6116
    - 6116 x2
    - 2114 x4
    - XTAL 9.828 MHz (B1), 14.976 MHz (B2), 4 MHz (B3)
    - AY-5-1013A UART

    TODO:
    - Character attributes (RAM at 0xc000)
    - Cursor
    - 132 column mode
    - Smooth scrolling
    - Timings
    - Printer
    - Figure out why the EAROM hack is needed
    - Move ergo201 driver here? The hardware is very similar

    Notes:

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

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/ay31015.h"
#include "machine/er1400.h"
#include "machine/ripple_counter.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "video/tms9927.h"
#include "f4431_kbd.h"
#include "emupal.h"
#include "screen.h"

#include "f4431.lh"


namespace {


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

class f4431_state : public driver_device
{
public:
	f4431_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_earom(*this, "earom"),
		m_ctc(*this, "ctc"),
		m_dart(*this, "dart"),
		m_vtc(*this, "vtc"),
		m_screen(*this, "screen"),
		m_uart(*this, "uart"),
		m_ascii(*this, "ascii"),
		m_chargen(*this, "chargen"),
		m_switches(*this, "switches"),
		m_display_enabled(false),
		m_nmi_disabled(true)
	{ }

	void f4431(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;
	required_device<er1400_device> m_earom;
	required_device<z80ctc_device> m_ctc;
	required_device<z80dart_device> m_dart;
	required_device<tms9927_device> m_vtc;
	required_device<screen_device> m_screen;
	required_device<ay31015_device> m_uart;
	required_shared_ptr<uint8_t> m_ascii;
	required_region_ptr<uint8_t> m_chargen;
	required_ioport m_switches;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	uint8_t latch_r();
	void latch_w(uint8_t data);

	void scanline_cb(uint32_t data);
	void vsync_cb(int state);
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void row_w(offs_t offset, uint8_t data);
	void brightness_w(uint8_t data);

	uint8_t m_row_address;
	uint8_t m_row_attr;

	bool m_display_enabled;
	bool m_nmi_disabled;
};


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

void f4431_state::mem_map(address_map &map)
{
	map(0x0000, 0x5fff).rom();
	map(0x1a00, 0x1a00).nopw(); // spurious write
	map(0x2000, 0x2000).w(FUNC(f4431_state::brightness_w));
	map(0x3000, 0x300f).w(m_vtc, FUNC(tms9927_device::write));
	map(0x4000, 0x4000).w(FUNC(f4431_state::latch_w));
	map(0x5000, 0x5000).w(m_uart, FUNC(ay31015_device::transmit));
	map(0x6000, 0x6000).r(m_uart, FUNC(ay31015_device::receive));
	map(0x6000, 0x6fff).w(FUNC(f4431_state::row_w));
	map(0x6001, 0x6001).r(FUNC(f4431_state::latch_r));
	map(0x7000, 0x77ff).ram(); // scratchpad ram
	map(0x8000, 0x8fff).ram().share(m_ascii);
	map(0xc000, 0xcfff).ram(); // attribute ram
}

void f4431_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0xf4, 0xf7).rw(m_dart, FUNC(z80dart_device::cd_ba_r), FUNC(z80dart_device::cd_ba_w));
	map(0xf8, 0xfb).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( f4431 )
	PORT_START("switches")
	PORT_DIPNAME(0x01, 0x01, "Production Test")
	PORT_DIPLOCATION("W:4")
	PORT_DIPSETTING(   0x01, DEF_STR( Off ))
	PORT_DIPSETTING(   0x00, DEF_STR( On ))
	PORT_DIPNAME(0x02, 0x00, "Enable EAROM Save")
	PORT_DIPLOCATION("W:5")
	PORT_DIPSETTING(   0x02, DEF_STR( Off ))
	PORT_DIPSETTING(   0x00, DEF_STR( On ))
INPUT_PORTS_END


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

void f4431_state::scanline_cb(uint32_t data)
{
	// in the actual system this would be generated
	// by the r0 and r3 output of the vtc

	if (!m_nmi_disabled && ((data % 10) == 9))
	{
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
		m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
	}
}

void f4431_state::vsync_cb(int state)
{
	if (!m_nmi_disabled && state)
	{
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
		m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
	}
}

uint32_t f4431_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if (m_display_enabled && BIT(m_row_attr, 6))
	{
		for (int i = cliprect.min_y; i <= cliprect.max_y; i++)
		{
			int line = i % 10;

			// double height, bottom
			if ((m_row_attr & 0x30) == 0x10)
				line = line / 2 + 5;

			// double height, top
			if ((m_row_attr & 0x30) == 0x20)
				line = line / 2;

			if (BIT(m_row_attr, 4) || BIT(m_row_attr, 5) )
			{
				// double width
				for (int x = 0; x < 40; x++)
				{
					uint8_t code = m_ascii[(m_row_address << 4) + x * 2];
					uint8_t data = m_chargen[(line << 7) | code];

					bitmap.pix(i, x * 20 + 0) = BIT(data, 7) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 20 + 1) = BIT(data, 7) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 20 + 2) = (BIT(data, 7) || BIT(data, 6)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 20 + 3) = (BIT(data, 7) || BIT(data, 6)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 20 + 4) = (BIT(data, 6) || BIT(data, 5)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 20 + 5) = (BIT(data, 6) || BIT(data, 5)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 20 + 6) = (BIT(data, 5) || BIT(data, 4)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 20 + 7) = (BIT(data, 5) || BIT(data, 4)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 20 + 8) = (BIT(data, 4) || BIT(data, 3)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 20 + 9) = (BIT(data, 4) || BIT(data, 3)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 20 + 10) = (BIT(data, 3) || BIT(data, 2)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 20 + 11) = (BIT(data, 3) || BIT(data, 2)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 20 + 12) = (BIT(data, 2) || BIT(data, 1)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 20 + 13) = (BIT(data, 2) || BIT(data, 1)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 20 + 14) = (BIT(data, 1) || BIT(data, 0)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 20 + 15) = (BIT(data, 1) || BIT(data, 0)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 20 + 16) = BIT(data, 0) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 20 + 17) = BIT(data, 0) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 20 + 18) = rgb_t::black();
					bitmap.pix(i, x * 20 + 19) = rgb_t::black();
				}
			}
			else
			{
				for (int x = 0; x < 80; x++)
				{
					uint8_t code = m_ascii[(m_row_address << 4) + x];
					uint8_t data = m_chargen[(line << 7) | code];

					bitmap.pix(i, x * 10 + 0) = BIT(data, 7) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 10 + 1) = (BIT(data, 7) || BIT(data, 6)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 10 + 2) = (BIT(data, 6) || BIT(data, 5)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 10 + 3) = (BIT(data, 5) || BIT(data, 4)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 10 + 4) = (BIT(data, 4) || BIT(data, 3)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 10 + 5) = (BIT(data, 3) || BIT(data, 2)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 10 + 6) = (BIT(data, 2) || BIT(data, 1)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 10 + 7) = (BIT(data, 1) || BIT(data, 0)) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 10 + 8) = BIT(data, 0) ? rgb_t::white() : rgb_t::black();
					bitmap.pix(i, x * 10 + 9) = rgb_t::black();
				}
			}
		}
	}
	else
	{
		bitmap.fill(rgb_t::black(), cliprect);
	}

	return 0;
}

void f4431_state::row_w(offs_t offset, uint8_t data)
{
	if (0)
		logerror("row_w: %02x %02x line %d %d\n", offset, data, m_screen->vpos(), m_screen->hpos());

	m_row_address = offset >> 4;
	m_row_attr = data;
}

void f4431_state::brightness_w(uint8_t data)
{
	if (0)
		logerror("brightness_w: %02x\n", data);
}


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

uint8_t f4431_state::latch_r()
{
	// 7-------  vsync
	// -6------  not connected
	// --5-----  earom data
	// ---4----  not connected
	// ----3---  switch w5 (disable earom save)
	// -----2--  switch w4 (production test)
	// ------1-  uart dav
	// -------0  uart tbe/boc

	uint8_t data = 0;

	data |= (m_uart->tbmt_r() | m_uart->eoc_r()) << 0;
	data |= m_uart->dav_r() << 1;
	data |= m_switches->read() << 2;
	data |= m_earom->data_r() << 5;
	data |= (m_screen->vblank() ? 0 : 1) << 7;

	return data;
}

void f4431_state::latch_w(uint8_t data)
{
	// 7-------  80/132 columns
	// -6------  interrupt disable
	// --5-----  screen blank
	// ---4----  earom clock
	// ----3---  earom c2
	// -----2--  earom c3
	// ------1-  earom data
	// -------0  earom c1

	m_earom->c1_w(BIT(data, 0));
	m_earom->data_w(BIT(data, 1));
	m_earom->c3_w(BIT(data, 2));
	m_earom->c2_w(BIT(data, 3));
	m_earom->clock_w(BIT(data, 4));

	m_display_enabled = bool(BIT(data, 5));
	m_nmi_disabled = bool(BIT(data, 6));
}

void f4431_state::machine_start()
{
	// set uart control lines
	m_uart->write_cs(1);
	m_uart->write_np(1);
	m_uart->write_tsb(1);
	m_uart->write_nb2(1);
	m_uart->write_nb1(1);
	m_uart->write_eps(1);
	m_uart->write_swe(0);

	// register for save states
	save_item(NAME(m_row_address));
	save_item(NAME(m_row_attr));
	save_item(NAME(m_display_enabled));
	save_item(NAME(m_nmi_disabled));
}

void f4431_state::machine_reset()
{
	m_display_enabled = false;
	m_nmi_disabled = true;
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

static const z80_daisy_config daisy_chain[] =
{
	{ "dart" },
	{ "ctc" },
	{ nullptr }
};

void f4431_state::f4431(machine_config &config)
{
	Z80(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &f4431_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &f4431_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	ER1400(config, m_earom);

	Z80CTC(config, m_ctc, 4_MHz_XTAL);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->set_clk<0>(4_MHz_XTAL / 13);
	m_ctc->zc_callback<0>().set(m_dart, FUNC(z80dart_device::rxca_w));
	m_ctc->set_clk<1>(4_MHz_XTAL / 13);
	m_ctc->zc_callback<1>().set(m_dart, FUNC(z80dart_device::txca_w));
	m_ctc->set_clk<2>(4_MHz_XTAL / 13);
	m_ctc->zc_callback<2>().set(m_dart, FUNC(z80dart_device::rxtxcb_w));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_color(rgb_t::green());
	m_screen->set_raw(9.828_MHz_XTAL * 2, 1020, 0, 800, 268, 0, 250); // probably wrong
	m_screen->set_screen_update(FUNC(f4431_state::screen_update));
	m_screen->scanline().set(FUNC(f4431_state::scanline_cb));

	PALETTE(config, "palette", palette_device::MONOCHROME);

	TMS9927(config, m_vtc, 9.828_MHz_XTAL / 6); // ?
	m_vtc->set_char_width(10); // renders in half-dots?
	m_vtc->set_screen("screen");
	m_vtc->hsyn_callback().set(m_ctc, FUNC(z80ctc_device::trg3));
	m_vtc->vsyn_callback().set(FUNC(f4431_state::vsync_cb));

	Z80DART(config, m_dart, 4_MHz_XTAL);
	m_dart->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_dart->out_txda_callback().set("comm", FUNC(rs232_port_device::write_txd));
	m_dart->out_rtsa_callback().set("comm", FUNC(rs232_port_device::write_rts));
	// port b: printer

	rs232_port_device &porta(RS232_PORT(config, "comm", default_rs232_devices, nullptr));
	porta.rxd_handler().set(m_dart, FUNC(z80dart_device::rxa_w));
	porta.cts_handler().set(m_dart, FUNC(z80dart_device::ctsa_w));

	AY31015(config, m_uart);
	m_uart->set_auto_rdav(true);
	m_uart->write_so_callback().set("kbd", FUNC(f4431_kbd_device::rx_w));

	ripple_counter_device &uart_clk(RIPPLE_COUNTER(config, "uart_clk", 4_MHz_XTAL / 13));
	uart_clk.set_stages(12);
	uart_clk.count_out_cb().set(m_uart, FUNC(ay31015_device::write_rcp)).bit(4); // Q4
	uart_clk.count_out_cb().append(m_uart, FUNC(ay31015_device::write_tcp)).bit(4); // Q4

	f4431_kbd_device &kbd(F4431_KBD(config, "kbd"));
	kbd.tx_handler().set(m_uart, FUNC(ay31015_device::write_si));

	config.set_default_layout(layout_f4431);
}


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

ROM_START( f4431 )
	ROM_REGION(0x6000, "maincpu", 0)
	ROM_LOAD("11420050-00_4431.d73", 0x0000, 0x1000, CRC(e7a9c982) SHA1(60c2eb769bd6051b4acaf38750ef40b9d02c568c))
	ROM_COPY("maincpu", 0x0000, 0x1000, 0x1000)
	ROM_LOAD("11420060-00_4431.d74", 0x2000, 0x1000, CRC(e9703cf0) SHA1(9eef6d4f0b57a6430a034f9405d4d8279723e2a0))
	ROM_COPY("maincpu", 0x2000, 0x3000, 0x1000)
	ROM_LOAD("11420070-00_4431.d75", 0x4000, 0x1000, CRC(a0d5b59e) SHA1(fca5b25163d942ec688d82004253850a3e08b0af))
	ROM_COPY("maincpu", 0x4000, 0x5000, 0x1000)

	ROM_REGION(0x800, "chargen", 0)
	ROM_LOAD("11419840-00_cg30.d40", 0x000, 0x800, CRC(d2988792) SHA1(bc3b59882c351fb503371e358e396e7d683c9467))
	// National variants (not dumped):
	// 1141 89 50-00/0 UK
	// 1141 98 60-00/9 FRA
	// 1141 90 70-00/8 GER
	// 1141 98 80-00/7 NOR
	// 1141 98 90-00/6 SWE/FIN
	// 1141 99 00-00/3 SPA
	// 1141 99 10-00/2 DEN

	ROM_REGION(0x800, "attr", 0)
	ROM_LOAD("11419950-00_4431.d57", 0x000, 0x800, CRC(066dc6bd) SHA1(1ad17c9ec96544278d9f8494c19b4b3bce8e3a8e))

	ROM_REGION(0x20, "prom", 0)
	ROM_LOAD("11419960-00_4431.d19", 0x00, 0x20, CRC(daae0c28) SHA1(58c55b8b9d4161a9d38259a4375cf19799ea0b7a))

	// factory default settings
	ROM_REGION16_LE(200, "earom", 0)
	ROM_LOAD("earom.d63", 0, 200, CRC(f14db754) SHA1(904e26974fbe7fe9166b731850bf414d8ffbe75d))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME  FLAGS
COMP( 1981, f4431, 0,       0,     f4431,   f4431, f4431_state, empty_init, "Facit", "4431",   MACHINE_NOT_WORKING )



facit4440.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Preliminary driver for Facit 4440 Twist terminal.

    The “Twist” sobriquet refers to the adjustable orientation of the
    monitor, whose normally black-on-white display has the dimensions of a
    sheet of A4 paper. It displays 24 lines of 80 characters in landscape
    mode and 72 lines of rather smaller characters in portrait mode.

    No user manual for this terminal has been found. The layout of the
    keyboard is also unclear, not to mention the serial protocol.

    Nonvolatile settings are configured through VT100-style setup screens.
    Several non-ANSI emulation modes are also available.

    An OEM customized version was sold by Norsk Data AS as the ND 319 Twist.
    Micro-Term apparently distributed this terminal in the US.

    The timing of the display circuit seems a bit uncertain. Promotional
    information claims that it has a 40 MHz bandwidth, but this may be only
    a nominal maximum rating since the only high-frequency oscillator seen
    on the PCB is 32 MHz, and schematics label this as 30 MHz. It is unclear
    how the CRTC parameters and this value can produce the documented 65 Hz
    refresh rate. The actual circuit also generates alarming levels of
    electromagnetic radiation.

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

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "cpu/m6800/m6801.h"
#include "machine/clock.h"
#include "machine/er1400.h"
#include "machine/ripple_counter.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "sound/spkrdev.h"
#include "video/mc6845.h"
#include "screen.h"
#include "speaker.h"


namespace {

class facit4440_state : public driver_device
{
public:
	facit4440_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_earom(*this, "earom%u", 1U)
		, m_crtc(*this, "crtc")
		, m_bellctr(*this, "bellctr")
		, m_rs232(*this, "rs232")
		, m_printer(*this, "printer")
		, m_test(*this, "TEST")
		, m_orientation(*this, "ORIENTATION")
		, m_chargen(*this, "chargen")
		, m_scanprom(*this, "scanprom")
		, m_videoram(*this, "videoram")
	{
	}

	void facit4440(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void earom_latch_w(u8 data);
	void control_2000_w(u8 data);
	u8 misc_status_r();
	void control_6000_w(u8 data);
	void control_a000_w(u8 data);

	void vsync_w(int state);

	MC6845_UPDATE_ROW(update_row);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_device_array<er1400_device, 2> m_earom;
	required_device<mc6845_device> m_crtc;
	required_device<ripple_counter_device> m_bellctr;
	required_device<rs232_port_device> m_rs232;
	required_device<rs232_port_device> m_printer;

	required_ioport m_test;
	required_ioport m_orientation;

	required_region_ptr<u8> m_chargen;
	required_region_ptr<u8> m_scanprom;
	required_shared_ptr<u8> m_videoram;

	u8 m_control_latch[3];
};

void facit4440_state::earom_latch_w(u8 data)
{
	// SN74LS174 latch + SN7406 inverter
	m_earom[0]->data_w(BIT(data, 0));
	m_earom[1]->data_w(BIT(data, 1));

	for (auto &earom : m_earom)
	{
		earom->c2_w(BIT(data, 2));
		earom->c1_w(BIT(data, 3));
		earom->c3_w(BIT(data, 4));
		earom->clock_w(BIT(data, 5));
	}
}

void facit4440_state::control_2000_w(u8 data)
{
	m_control_latch[0] = data;

	m_bellctr->reset_w(BIT(data, 5));
	if (!BIT(m_control_latch[0], 6))
		m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
}

void facit4440_state::control_6000_w(u8 data)
{
	m_control_latch[1] = data;
}

void facit4440_state::control_a000_w(u8 data)
{
	m_control_latch[2] = data;
}

u8 facit4440_state::misc_status_r()
{
	u8 status = m_test->read() << 6;

	status |= m_earom[1]->data_r() << 4;
	status |= m_earom[0]->data_r() << 3;

	status |= m_crtc->hsync_r() << 2;
	status |= m_crtc->vsync_r() << 1;
	status |= m_orientation->read();

	return status;
}

void facit4440_state::vsync_w(int state)
{
	if (state && BIT(m_control_latch[0], 5))
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
}

MC6845_UPDATE_ROW(facit4440_state::update_row)
{
	// FIXME: MA does not determine video addressing at all here
	offs_t base = ma / 5 * 6;
	u32 *px = &bitmap.pix(y);

	for (int i = 0; i < x_count; i++)
	{
		u8 chr = m_videoram[(base + i) & 0x1fff];
		rgb_t fg = rgb_t::white();
		rgb_t bg = rgb_t::black();

		// TODO: double-width scans based on attributes
		u8 scan = m_scanprom[ra | (BIT(m_control_latch[1], 7) ? 0x20 : 0x00)];
		u8 dots = m_chargen[(u16(chr) << 5) | (scan & 0x1f)];

		for (int n = 8; n > 0; n--, dots <<= 1)
			*px++ = BIT(dots, 7) ? fg : bg;
	}
}

void facit4440_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("mainprg", 0);
	map(0x0000, 0x0000).w(FUNC(facit4440_state::earom_latch_w));
	map(0x2000, 0x2000).w(FUNC(facit4440_state::control_2000_w));
	map(0x4000, 0x4000).mirror(0x1ffe).w("crtc", FUNC(mc6845_device::address_w));
	map(0x4001, 0x4001).mirror(0x1ffe).w("crtc", FUNC(mc6845_device::register_w));
	map(0x6000, 0x6000).w(FUNC(facit4440_state::control_6000_w));
	map(0x8000, 0x8000).r(FUNC(facit4440_state::misc_status_r));
	map(0x8000, 0x8050).nopw();
	map(0xa000, 0xbfff).rom().region("testprg", 0);
	map(0xa000, 0xa000).w(FUNC(facit4440_state::control_a000_w));
	map(0xc000, 0xdfff).ram().share("videoram"); // 4x TMM2016AP-90
	map(0xe000, 0xffff).ram(); // 4x TMM2016AP-90
}

void facit4440_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0xec, 0xef).rw("kbdart", FUNC(z80dart_device::cd_ba_r), FUNC(z80dart_device::cd_ba_w));
	map(0xf4, 0xf7).rw("iodart", FUNC(z80dart_device::cd_ba_r), FUNC(z80dart_device::cd_ba_w));
	map(0xf8, 0xfb).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
}

void facit4440_state::machine_start()
{
	subdevice<z80dart_device>("kbdart")->rxb_w(0);

	m_control_latch[0] = m_control_latch[1] = m_control_latch[2] = 0;

	save_item(NAME(m_control_latch));
}

static INPUT_PORTS_START(facit4440)
	PORT_START("TEST")
	PORT_DIPNAME(3, 0, "Test Mode") PORT_DIPLOCATION("W7/W4:1,2")
	PORT_DIPSETTING(0, DEF_STR(Off))
	PORT_DIPSETTING(3, DEF_STR(On))
	PORT_DIPSETTING(2, "Burn In")

	PORT_START("ORIENTATION")
	PORT_CONFNAME(1, 1, "Orientation")
	PORT_CONFSETTING(0, "Portrait")
	PORT_CONFSETTING(1, "Landscape")
INPUT_PORTS_END

static const z80_daisy_config daisy_chain[] =
{
	{ "ctc" },
	{ "iodart" },
	{ "kbdart" },
	{ nullptr }
};

void facit4440_state::facit4440(machine_config &config)
{
	constexpr XTAL DOT_CLOCK = 32_MHz_XTAL; // XTAL is 30 MHz according to schematics

	Z80(config, m_maincpu, 32_MHz_XTAL / 8); // divider confirmed from schematics
	m_maincpu->set_addrmap(AS_PROGRAM, &facit4440_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &facit4440_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	ER1400(config, m_earom[0]); // M5G1400P @ D65
	ER1400(config, m_earom[1]); // M5G1400P @ D64

	z80ctc_device &ctc(Z80CTC(config, "ctc", 32_MHz_XTAL / 8));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	ctc.set_clk<0>(2.4576_MHz_XTAL / 4);
	ctc.set_clk<1>(2.4576_MHz_XTAL / 4);
	ctc.set_clk<2>(2.4576_MHz_XTAL / 4);
	ctc.zc_callback<0>().set("iodart", FUNC(z80dart_device::txca_w));
	ctc.zc_callback<1>().set("iodart", FUNC(z80dart_device::rxca_w));
	ctc.zc_callback<2>().set("iodart", FUNC(z80dart_device::txcb_w));
	ctc.zc_callback<2>().append("iodart", FUNC(z80dart_device::rxcb_w));

	clock_device &keybclk(CLOCK(config, "kbdclk", 2.4576_MHz_XTAL / 32)); // divider confirmed from schematics
	keybclk.signal_handler().set("kbdart", FUNC(z80dart_device::txca_w));
	keybclk.signal_handler().append("kbdart", FUNC(z80dart_device::rxca_w));

	z80dart_device &iodart(Z80DART(config, "iodart", 32_MHz_XTAL / 8)); // Z80ADART @ D83
	iodart.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	iodart.out_txda_callback().set(m_rs232, FUNC(rs232_port_device::write_txd));
	iodart.out_rtsa_callback().set(m_rs232, FUNC(rs232_port_device::write_rts));
	iodart.out_dtra_callback().set(m_printer, FUNC(rs232_port_device::write_dtr)); // not correct according to schematics, but needed to pass burn-in test
	iodart.out_txdb_callback().set(m_printer, FUNC(rs232_port_device::write_txd));

	z80dart_device &kbdart(Z80DART(config, "kbdart", 32_MHz_XTAL / 8)); // Z80ADART @ D82
	kbdart.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	kbdart.out_txda_callback().set("kbdart", FUNC(z80dart_device::rxa_w)); // FIXME: serial keyboard needed here
	// Channel B is not used (though schematics show 307.2 kHz provided for RxTxCB)

	M6801(config, "kbdmcu", 2.4576_MHz_XTAL).set_disable(); // exact type unknown

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(DOT_CLOCK, 103 * 8, 0, 80 * 8, 621, 0, 500);
	//screen.set_raw(DOT_CLOCK, 103 * 8, 0, 80 * 8, 621, 0, 560);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	MC6845(config, m_crtc, DOT_CLOCK / 8); // HD46505SP-2
	m_crtc->set_char_width(8);
	m_crtc->set_show_border_area(false);
	m_crtc->set_update_row_callback(FUNC(facit4440_state::update_row));
	m_crtc->out_hsync_callback().set("ctc", FUNC(z80ctc_device::trg3));
	m_crtc->out_hsync_callback().append(m_bellctr, FUNC(ripple_counter_device::clock_w));
	m_crtc->out_vsync_callback().set(FUNC(facit4440_state::vsync_w));

	RIPPLE_COUNTER(config, m_bellctr).set_stages(12); // HEF4040BP
	m_bellctr->count_out_cb().set("speaker", FUNC(speaker_sound_device::level_w)).bit(5);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.5);

	RS232_PORT(config, m_rs232, default_rs232_devices, "loopback"); // temporary loopback for testing
	m_rs232->rxd_handler().set("iodart", FUNC(z80dart_device::rxa_w));
	m_rs232->cts_handler().set("iodart", FUNC(z80dart_device::ctsa_w));
	m_rs232->dcd_handler().set("iodart", FUNC(z80dart_device::dcda_w));

	RS232_PORT(config, m_printer, default_rs232_devices, "loopback"); // temporary loopback for testing
	m_printer->rxd_handler().set("iodart", FUNC(z80dart_device::rxb_w));
	m_printer->dcd_handler().set("iodart", FUNC(z80dart_device::dcdb_w));
}


ROM_START(facit4440)
	ROM_REGION(0x8000, "mainprg", 0)
	ROM_LOAD("rom7.bin", 0x0000, 0x4000, CRC(a8da2b11) SHA1(4436ef14c29ae299f7bc338748158771c02d02a9))
	ROM_LOAD("rom6.bin", 0x4000, 0x4000, CRC(790b7642) SHA1(688a80cbf011e5c14f501e11fe0e3bf64a85bbd7))

	ROM_REGION(0x1000, "kbdmcu", 0)
	ROM_LOAD("6801.bin", 0x0000, 0x1000, NO_DUMP)

	ROM_REGION(0x2000, "testprg", 0)
	ROM_LOAD("rom5.bin", 0x0000, 0x2000, CRC(715d02b6) SHA1(e304718dbdc8867ac01909fd2d027e5014a8c4f9))

	ROM_REGION(0x3000, "chargen", 0) // order unknown
	ROM_LOAD("rom1.bin", 0x0000, 0x1000, CRC(b503c173) SHA1(209bf59e2e9953179d04c4e768fc41574e039d36))
	ROM_LOAD("rom3.bin", 0x1000, 0x1000, CRC(a55a25d9) SHA1(c0d321e65f214adee01bf5f8c495b2518fa31b7b))
	ROM_LOAD("rom4.bin", 0x2000, 0x1000, CRC(52004ef8) SHA1(50d6e2eb48f60db3a3c9d206fc40d3294b6adc0e))

	ROM_REGION(0x0800, "scanprom", 0)
	ROM_LOAD("rom2.bin", 0x0000, 0x0800, CRC(9e1a190c) SHA1(fb08ee806f1056bcdfb5b08ea85995e1d3d01298))
ROM_END

} // anonymous namespace


COMP(1984, facit4440, 0, 0, facit4440, facit4440, facit4440_state, empty_init, "Facit", "4440 Twist (30M-F1)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS)



falco500.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: AJR, Dirk Best
/****************************************************************************

    Falco 500 series terminals

    Third generation of Falco terminals, after the TS and FAME series. Many
    functions are now handled by a custom ASIC that was updated a few times
    between models.

    List of models (incomplete):
    - 1986: 500, 542, 5220, 5500
    - 1987: 500e, 5220e, 5000, 5600
    - 1988: 5600s, 5220s, 580, 5330

    This drivers contains dumps for the 500e, 5220e and 5220s models. The
    5220e is emulated to an advanced stage and usable, the others are
    skeletons for now.

    Additional notes:
    - The keyboard interface is completely wrong. It should be serially
      connected to the ASIC (and support different models)
    - The ASIC emulation still needs lots of work. Lots of guesses and
      many unknown and unhandled writes.

    Detailed hardware descriptions for the dumped models:

    Falco 5220e:
    - Z0840006PSC Z80 CPU
    - Z0843006PSC Z80 CTC
    - Z0844006PSC Z80 SIO/0
    - L1A3417  041500-001  FALCO  TAE8739Δ
    - PAL labeled F-500E
    - D43256C-10L labeled ARAM1, socket label AROM0 empty
    - D43256C-10L labeled CRAM1, socket label CROM0 empty
    - CXK5864AP-10L (next to ASIC)
    - TMM27256AD-20*2 152321-000 (ROM0) 152321-001 (ROM1)
    - CXK5864AP-10L (next to ROMs)
    - 37.980 MHz XTAL
    - 12.288 MHz XTAL
    - Bell
    - Space for an IC labeled "CLOCK", not fitted

    Falco 500e
    - PCB labeled "PWB MINOTAUR V016 P/N 130165-016 FALCO DATA PRODUCTS COPYRIGHT 4-93"
    - Z0840006PSC Z80 CPU
    - Z0843006PSC Z80 CTC
    - Z0844006PSC Z80 SIO/0
    - L1A3641  041501-001  NAP 9314Δ (SG)  WA39038  HONG KONG
    - PALs labeled 150133 and 150424 (near ASIC/CPU)
    - PAL labeled 150423 (near CPU/CTC/SIO)
    - HY62256ALP-70 and empty socket
    - HY62256ALP-70 and empty socket
    - HY62256ALP-70 (next to ASIC)
    - 27C512-15*2 155710-000 and 155710-001
    - HY62256ALP-10
    - 54.046000 MHz XTAL
    - 12.288 MHz XTAL
    - Bell

    Falco 5220s:
    - Z0840006PSC Z80 CPU
    - Z0843006PSC Z80 CTC
    - Z0844006PSC Z80 SIO/0
    - L1A3641  041501-001  FALCO  TAG 8914 Δ  8276 HONG KONG
    - PAL unlabeled
    - HY6264P-10 labeled ARAM1, socket label AROM0 empty
    - HY6264LP labeled CRAM1, socket label CROM0 empty
    - HY6264P-10 (next to ASIC)
    - 27256*2 168412-000 (ROM0) 168412-001 (ROM1)
    - HY6264LP-12
    - 37.980 MHz XTAL
    - 12.288 MHz XTAL
    - Bell

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

#include "emu.h"
#include "bus/rs232/hlemouse.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/bankdev.h"
#include "machine/clock.h"
#include "f5220_kbd.h"
#include "machine/nvram.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "sound/spkrdev.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {


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

class falco500_state : public driver_device
{
public:
	falco500_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_rambank(*this, "rambank"),
		m_ctc(*this, "ctc"),
		m_screen(*this, "screen"),
		m_palette(*this, "palette"),
		m_rombank(*this, "rombank"),
		m_ram(*this, "ram"),
		m_charram(*this, "charram"),
		m_kbd(*this, "kbd"),
		m_asic_5a(0)
	{ }

	void falco500(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;
	required_device<address_map_bank_device> m_rambank;
	required_device<z80ctc_device> m_ctc;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_memory_bank m_rombank;
	required_shared_ptr<uint8_t> m_ram;
	required_shared_ptr<uint8_t> m_charram;
	required_device<f5220_kbd_device> m_kbd;

	void mem_map(address_map &map) ATTR_COLD;
	void bank_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	uint8_t asic_data_r();
	uint8_t asic_status_r();
	void asic_rambank_w(uint8_t data);
	void asic_settings_w(uint8_t data);
	void asic_cursor_addr_w(offs_t offset, uint8_t data);
	void asic_56_w(uint8_t data);
	void asic_5a_w(uint8_t data);
	void asic_mode_w(uint8_t data);
	void asic_cmd_w(uint8_t data);

	void kbd_int_w(int state);

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void rombank_w(uint8_t data);
	void unk_40_w(uint8_t data);

	uint8_t m_unk_40;
	uint8_t m_asic_status;
	uint8_t m_asic_settings;
	uint16_t m_asic_cursor_addr;
	uint8_t m_asic_56;
	uint8_t m_asic_5a;
	uint8_t m_asic_mode;
};


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

void falco500_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("maincpu", 0);
	map(0x8000, 0xbfff).bankr("rombank");
	map(0xc000, 0xdfff).ram().share("nvram");
	map(0xe000, 0xffff).m(m_rambank, FUNC(address_map_bank_device::amap8));
}

void falco500_state::bank_map(address_map &map)
{
	map(0x00000, 0x0ffff).ram().share("ram"); // 2x NEC D43256C-10L? (5220s only has 2x HY6264P-10)
	map(0x10000, 0x11fff).ram().share("charram"); // CXK5864AP-10L / HY6264LP-12?
}

void falco500_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x00).w(FUNC(falco500_state::rombank_w));
	map(0x40, 0x40).w(FUNC(falco500_state::unk_40_w));
	map(0x50, 0x50).r(FUNC(falco500_state::asic_data_r));
	map(0x51, 0x51).r(FUNC(falco500_state::asic_status_r));
	map(0x52, 0x52).w(FUNC(falco500_state::asic_rambank_w));
	map(0x53, 0x53).w(FUNC(falco500_state::asic_settings_w));
	map(0x54, 0x55).w(FUNC(falco500_state::asic_cursor_addr_w));
	map(0x56, 0x56).w(FUNC(falco500_state::asic_56_w));
	map(0x5a, 0x5a).w(FUNC(falco500_state::asic_5a_w)); // L1A3641 only?
	map(0x5b, 0x5b).w(FUNC(falco500_state::asic_cmd_w));
	map(0x5d, 0x5d).w(FUNC(falco500_state::asic_mode_w));
	map(0x60, 0x63).rw("sio", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x70, 0x73).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x88, 0x8b).nopw(); // second SIO?
	map(0x90, 0x93).nopw(); // second CTC?
}


//**************************************************************************
//  ASIC
//**************************************************************************

void falco500_state::asic_rambank_w(u8 data)
{
	m_rambank->set_bank(data & 0x0f);

	if (data > 8)
		logerror("rambank_w: %02x\n", data);
}

uint8_t falco500_state::asic_data_r()
{
	return m_kbd->read();
}

uint8_t falco500_state::asic_status_r()
{
	// 765-----  unknown (not used?)
	// ---4----  keyboard data available
	// ----3---  unknown
	// -----2--  unknown (needs to toggle)
	// ------1-  unknown
	// -------0  unknown

	m_asic_status &= ~0x04;
	m_asic_status |= m_screen->vblank() ? 0x04 : 0x00; // maybe?

	return m_asic_status;
}

void falco500_state::asic_settings_w(u8 data)
{
	logerror("asic_settings_w: unk %d curblink %d curshape %d curen %d undrline %d\n",
		BIT(data, 7), BIT(data, 6), BIT(data, 5), BIT(data, 4), data & 0x0f);

	// 7-------  unknown
	// -6------  cursor blink enabled
	// --5-----  cursor shape (underline, block)
	// ---4----  cursor enabled
	// ----3210  underline location

	m_asic_settings = data;
}

void falco500_state::asic_cursor_addr_w(offs_t offset, uint8_t data)
{
	if (offset)
	{
		m_asic_cursor_addr &= 0x00ff;
		m_asic_cursor_addr |= ((data & 0xf0) << 8) << 1;
		m_asic_cursor_addr |= (data & 0x0f) << 8;
	}
	else
	{
		m_asic_cursor_addr = (m_asic_cursor_addr & 0xff00) | (data << 0);
	}
}

void falco500_state::asic_56_w(uint8_t data)
{
	if (data != m_asic_56)
		logerror("asic_56_w: %02x\n", data);

	m_asic_56 = data;
}

void falco500_state::asic_5a_w(u8 data)
{
	logerror("asic_5a_w: %02x\n", data);
	m_asic_5a = data;
}

void falco500_state::asic_cmd_w(uint8_t data)
{
	//logerror("asic_cmd_w: %02x\n", data);

	// 0x7f -> reset asic?

	// 7-------  unknown
	// -6------  reset keyboard data available?
	// --543210  unknown

	if (BIT(data, 6))
		m_asic_status &= ~0x10;
}

void falco500_state::asic_mode_w(uint8_t data)
{
	if (data != m_asic_mode)
		logerror("asic_mode_w: %02x\n", data);

	m_asic_mode = data;

	// 7654321-  unknown
	// -------0  80/132 columns

	// timing wrong
	rectangle visarea(0, (BIT(data, 0) ? 1320 : 1120) - 1, 0, 400 - 1);
	m_screen->configure(1500, 422, visarea, HZ_TO_ATTOSECONDS(60));
}

void falco500_state::kbd_int_w(int state)
{
	if (state)
		m_asic_status |= 0x10;
}


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

uint32_t falco500_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const pen_t *const pen = m_palette->pens();

	bitmap.fill(pen[0], cliprect);

	int y = 0;

	for (int line = 0; line < 50; line++)
	{
		// line attributes for each line
		uint8_t la0 = m_ram[0x0000 + line * 2 + 0];
		uint8_t la1 = m_ram[0x0000 + line * 2 + 1];
		uint8_t la2 = m_ram[0x1000 + line * 2 + 0];
		uint8_t la3 = m_ram[0x1000 + line * 2 + 1];

		// la0 layout
		// 76543210  line address offset low bytes

		// la1 layout
		// 7654----  line height
		// ----3210  offset for double-height lines

		// la2 layout
		// 7-------  line address highest bit (only for graphics mode?)
		// -654----  line address bank (multiply by 2 to get the address)
		// ----3210  line address offset high bytes

		// la3 layout
		// 7-------  unknown
		// -6------  set on the line between windows
		// --5-----  80 columns/132 columns
		// ---4----  graphics/alphanumeric
		// ----3---  unknown
		// -----2--  double width
		// ------1-  double height first half
		// -------0  double height second half (but not working for line modes 26 and 44?)

		uint16_t line_addr = (la2 << 8) | la0;

		// move address highest bit to its place, shift bank left
		line_addr = bitswap<16>(line_addr, 14, 13, 12, 15, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);

		//uint8_t unk_code = m_ram[0x0000 + line_addr]; // ?
		//uint8_t unk_attr = m_ram[0x1000 + line_addr]; // ?

		// skip unknown value
		line_addr++;

		uint8_t line_height = la1 >> 4;
		line_height++;

		// safety check to prevent writing out of bounds
		if (y + line_height > (cliprect.max_y + 1))
			return 0;

		if (BIT(la3, 4))
		{
			// horizontal resolution 560 pixels
			for (int col = 0; col < 560; col++)
			{
				uint16_t char_addr = line_addr + col;
				uint8_t code = m_ram[0x0000 + char_addr];

				// foreground/background colors
				rgb_t fg = pen[1];
				rgb_t bg = pen[0];

				for (int x = 0; x < 8; x++)
				{
					if (BIT(la3, 2))
					{
						bitmap.pix(y + (col / 70), (((col % 70) * 8) * 2) + (x * 2) + 0) = BIT(code, x) ? fg : bg;
						bitmap.pix(y + (col / 70), (((col % 70) * 8) * 2) + (x * 2) + 1) = BIT(code, x) ? fg : bg;
					}
					else
					{
						bitmap.pix(y + (col / 70), ((col % 70) * 8) + x) = BIT(code, x) ? fg : bg;
					}
				}
			}
		}
		else
		{
			// figure out number of columns
			int cols = BIT(la3, 5) ? 80 : 132;

			if (BIT(la3, 2))
				cols /= 2;

			for (int col = 0; col < cols; col++)
			{
				uint16_t char_addr = line_addr + col;

				uint8_t code = m_ram[0x0000 + char_addr];
				uint8_t attr = m_ram[0x1000 + char_addr];

				// code layout
				// 7-------  unknown
				// -6543210  chargen address

				// attr layout
				// 7-------  unknown
				// -6------  chargen address high bit
				// --5-----  unknown
				// ---4----  normal/bold
				// ----3---  unknown
				// -----2--  reverse
				// ------1-  blink
				// -------0  underline

				for (int y_char = 0; y_char < line_height; y_char++)
				{
					bool blink = bool(m_screen->frame_number() & 0x10); // timing?
					bool underline = bool(y_char == (m_asic_settings & 0x0f));

					int y_chargen = y_char;

					// double height enabled?
					if (la3 & 0x03)
						y_chargen /= 2;

					// double-height, first half
					if (BIT(la3, 1))
						y_chargen += 0;

					// double height, second half
					if (BIT(la3, 0))
						y_chargen += ((la1 & 0x0f) + 1);

					uint16_t gfx = m_charram[(BIT(attr, 6) << 12) | ((code & 0x7f) << 4) | y_chargen];

					if (BIT(attr, 0) && underline)
						gfx = 0x3ff;

					if (BIT(attr, 1) && blink)
						gfx = 0x000;

					if (BIT(attr, 2))
						gfx ^= 0x3ff;

					// cursor
					if (BIT(m_asic_settings, 4) && (m_asic_cursor_addr == char_addr))
					{
						if ((BIT(m_asic_settings, 5) == 1 && BIT(m_asic_settings, 6) == 0) || // block
							(BIT(m_asic_settings, 5) == 1 && BIT(m_asic_settings, 6) == 1 && blink) || // block-blink
							(BIT(m_asic_settings, 5) == 0 && underline && BIT(m_asic_settings, 6) == 0) || // underline
							(BIT(m_asic_settings, 5) == 0 && underline && BIT(m_asic_settings, 6) == 1 && blink)) // underline-blink
							gfx ^= 0x3ff; // might be solid instead
					}

					// foreground/background colors
					rgb_t fg = BIT(attr, 4) ? pen[1] : pen[2];
					rgb_t bg = pen[0];

					for (int x = 0; x < 10; x++)
					{
						if (BIT(la3, 2))
						{
							// double-width
							bitmap.pix(y + y_char, col * 2 * 10 + x * 2 + 0) = BIT(gfx, x) ? fg : bg;
							bitmap.pix(y + y_char, col * 2 * 10 + x * 2 + 1) = BIT(gfx, x) ? fg : bg;
						}
						else
						{
							bitmap.pix(y + y_char, col * 10 + x) = BIT(gfx, x) ? fg : bg;
						}
					}
				}
			}
		}

		y += line_height;
	}

	return 0;
}

static const gfx_layout char_layout =
{
	8, 16,
	512,
	1,
	{ 0 },
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	{ STEP16(0, 8) },
	8 * 16
};

static GFXDECODE_START(chars)
	GFXDECODE_RAM("charram", 0, char_layout, 0, 1)
GFXDECODE_END


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

void falco500_state::rombank_w(u8 data)
{
	m_rombank->set_entry(data & 3);

	if (data > 3)
		logerror("rombank_w: %02x\n", data);
}

void falco500_state::unk_40_w(uint8_t data)
{
	if (data != m_unk_40)
		logerror("unk_40_w: %02x\n", data);

	m_unk_40 = data;
}

void falco500_state::machine_start()
{
	m_rombank->configure_entries(0, 4, memregion("maincpu")->base(), 0x4000);

	// register for save states
	save_item(NAME(m_unk_40));
	save_item(NAME(m_asic_status));
	save_item(NAME(m_asic_settings));
	save_item(NAME(m_asic_cursor_addr));
	save_item(NAME(m_asic_56));
	save_item(NAME(m_asic_5a));
	save_item(NAME(m_asic_mode));
}

void falco500_state::machine_reset()
{
	m_rombank->set_entry(0);
	m_rambank->set_bank(0);
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

static const z80_daisy_config daisy_chain[] =
{
	{ "ctc" },
	{ "sio" },
	{ nullptr }
};

void falco500_state::falco500(machine_config &config)
{
	Z80(config, m_maincpu, 12.288_MHz_XTAL / 2); // Z0840006PSC
	m_maincpu->set_addrmap(AS_PROGRAM, &falco500_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &falco500_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	ADDRESS_MAP_BANK(config, m_rambank);
	m_rambank->set_map(&falco500_state::bank_map);
	m_rambank->set_data_width(8);
	m_rambank->set_addr_width(17);
	m_rambank->set_stride(0x2000);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // CXK5864AP-10L + battery

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_color(rgb_t::amber());
	m_screen->set_raw(37.98_MHz_XTAL, 1500, 0, 800, 422, 0, 400); // 25.32 kHz/60 Hz confirmed
	m_screen->set_screen_update(FUNC(falco500_state::screen_update));
	m_screen->screen_vblank().set_inputline(m_maincpu, INPUT_LINE_NMI); // maybe?

	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	GFXDECODE(config, "gfxdecode", m_palette, chars);

	Z80CTC(config, m_ctc, 12.288_MHz_XTAL / 2); // Z0843006PSC
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->set_clk<0>(12.288_MHz_XTAL / 10);
	m_ctc->zc_callback<0>().set("sio", FUNC(z80sio_device::rxca_w));
	m_ctc->zc_callback<0>().append("sio", FUNC(z80sio_device::txca_w));
	m_ctc->set_clk<1>(12.288_MHz_XTAL / 10);
	m_ctc->zc_callback<1>().set("sio", FUNC(z80sio_device::rxtxcb_w));
	m_ctc->set_clk<2>(12.288_MHz_XTAL / 10); // ?
	m_ctc->zc_callback<2>().set("bell", FUNC(speaker_sound_device::level_w));
	m_ctc->set_clk<3>(12.288_MHz_XTAL / 10); // ?

	z80sio_device &sio(Z80SIO(config, "sio", 12.288_MHz_XTAL / 2)); // Z0844006PSC
	sio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	sio.out_txda_callback().set("porta", FUNC(rs232_port_device::write_txd));
	sio.out_rtsa_callback().set("porta", FUNC(rs232_port_device::write_rts));
	sio.out_txdb_callback().set("portb", FUNC(rs232_port_device::write_txd));
	sio.out_rtsb_callback().set("portb", FUNC(rs232_port_device::write_rts));

	rs232_port_device &porta(RS232_PORT(config, "porta", default_rs232_devices, nullptr));
	porta.rxd_handler().set("sio", FUNC(z80sio_device::rxa_w));
	porta.cts_handler().set("sio", FUNC(z80sio_device::ctsa_w));
	porta.option_add("microsoft_mouse", MSFT_HLE_SERIAL_MOUSE);

	rs232_port_device &portb(RS232_PORT(config, "portb", default_rs232_devices, nullptr));
	portb.rxd_handler().set("sio", FUNC(z80sio_device::rxb_w));
	portb.cts_handler().set("sio", FUNC(z80sio_device::ctsb_w));
	portb.option_add("microsoft_mouse", MSFT_HLE_SERIAL_MOUSE);

	SPEAKER(config, "mono").front_center();

	SPEAKER_SOUND(config, "bell").add_route(ALL_OUTPUTS, "mono", 1.00);

	F5220_KBD(config, m_kbd);
	m_kbd->int_handler().set(FUNC(falco500_state::kbd_int_w));
}


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

ROM_START( falco5220e )
	ROM_REGION(0x10000, "maincpu", 0) // (c) 1987 FDP, Inc 2321
	ROM_LOAD("152321-000.bin", 0x0000, 0x8000, BAD_DUMP CRC(45ef4a68) SHA1(71e12dce710f9b66290618e299b2382834845057)) // wrong checksum, might be a bit flip
	ROM_LOAD("152321-001.bin", 0x8000, 0x8000, CRC(91056626) SHA1(217ca3de76d5e9861284f5b64f8eff8e541fad3d))
ROM_END

ROM_START( falco500e )
	ROM_REGION(0x20000, "maincpu", 0) //  (c) 1991 FDP, Inc 1710
	ROM_LOAD("155710-000.bin", 0x00000, 0x10000, CRC(cc7c53b8) SHA1(214e7f7b4ce2f0b4106292e4d73125df2c4ddc92))
	ROM_LOAD("155710-001.bin", 0x10000, 0x10000, CRC(d1b51d71) SHA1(fd5aee5da1422d4f19045a98663df4f09472ca6b))
ROM_END

ROM_START( falco5220s )
	ROM_REGION(0x10000, "maincpu", 0) // (c) 1989 FDP, Inc 0412
	ROM_LOAD("168412-00.bin", 0x0000, 0x8000, CRC(de34b149) SHA1(6a4824eb5941f4c6475949011e64b28ab185ba59))
	ROM_LOAD("168412-01.bin", 0x8000, 0x8000, CRC(e6facd5b) SHA1(2b9bf3ca18e3e30032dcb6faf0809b6cf6f467ac))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME        PARENT      COMPAT  MACHINE   INPUT  CLASS            INIT        COMPANY                FULLNAME       FLAGS
COMP( 1987, falco5220e, 0,          0,      falco500, 0,     falco500_state, empty_init, "Falco Data Products", "Falco 5220e", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1991, falco500e,  falco5220e, 0,      falco500, 0,     falco500_state, empty_init, "Falco Data Products", "Falco 500e",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1989, falco5220s, falco5220e, 0,      falco500, 0,     falco500_state, empty_init, "Falco Data Products", "Falco 5220s", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



falcots.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Preliminary driver for Falco TS-series terminals.

***************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/input_merger.h"
#include "machine/nvram.h"
#include "machine/ripple_counter.h"
#include "machine/rstbuf.h"
#include "machine/scn_pci.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "sound/spkrdev.h"
#include "video/mc6845.h"
#include "screen.h"
#include "speaker.h"


namespace {

class falcots_state : public driver_device
{
public:
	falcots_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rstbuf(*this, "rstbuf")
		, m_lineint(*this, "lineint")
		, m_bell(*this, "bell")
		, m_crtc(*this, "crtc")
		, m_blinkcnt(*this, "blinkcnt")
		, m_vram(*this, "vram")
		, m_chargen(*this, "chargen")
		, m_keys(*this, "KEY%X", 0U)
	{
	}

	void ts1(machine_config &config);
	void ts2624(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	MC6845_UPDATE_ROW(ts1_update_row);
	MC6845_UPDATE_ROW(update_row);
	void load_counters();
	void row_clock_w(int state);
	void vsync_w(int state);
	void bell_toggle_w(int state);

	u8 key_status_r();
	void key_scan_w(u8 data);
	void video_reset_w(u8 data);
	void line_addr_upper_w(u8 data);
	void line_addr_lower_w(u8 data);
	void line_attr_w(u8 data);
	void line_int_clear_w(u8 data);
	void brightness_w(u8 data);
	void bell_w(u8 data);
	void control_w(u8 data);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void ts1_mem_map(address_map &map) ATTR_COLD;
	void ts1_io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	optional_device<rst_pos_buffer_device> m_rstbuf;
	required_device<input_merger_device> m_lineint;
	optional_device<speaker_sound_device> m_bell;
	required_device<mc6845_device> m_crtc;
	required_device<ripple_counter_device> m_blinkcnt;
	required_shared_ptr<u8> m_vram;
	required_region_ptr<u8> m_chargen;
	optional_ioport_array<16> m_keys;

	u16 m_line_addr_latch = 0;
	u16 m_line_addr_base = 0;
	u8 m_lines_left = 0;
	u8 m_line_attr_latch = 0;
	u8 m_line_attr = 0;
	u8 m_brightness = 0;
	u8 m_key_scan = 0;
	bool m_bell_toggle = false;
};

void falcots_state::machine_start()
{
	save_item(NAME(m_line_addr_latch));
	save_item(NAME(m_line_addr_base));
	save_item(NAME(m_lines_left));
	save_item(NAME(m_line_attr_latch));
	save_item(NAME(m_line_attr));
	save_item(NAME(m_brightness));
	save_item(NAME(m_key_scan));
	if (!m_rstbuf.found())
		save_item(NAME(m_bell_toggle));
}

void falcots_state::machine_reset()
{
	control_w(0);
}

MC6845_UPDATE_ROW(falcots_state::ts1_update_row)
{
	rgb_t fg(m_brightness, m_brightness, m_brightness);
	u32 *pix = &bitmap.pix(y);

	if (BIT(m_line_attr, 5))
		x_count /= 2;
	for (int x = 0; x < x_count; ++x)
	{
		u8 char_code = BIT(m_line_attr, 4) ? m_vram[(m_line_addr_base + x) & 0x7ff] : 0;
		u8 attr_code = BIT(m_line_attr, 4) ? m_vram[0x800 + ((m_line_addr_base + x) & 0x7ff)] : 0;

		u8 dots = 0;
		if (!BIT(attr_code, 5))
		{
			if (!BIT(attr_code, 1) || !BIT(m_blinkcnt->count(), 4))
				dots = ~m_chargen[u16(char_code & 0x7f) << 4 | (m_line_attr & 0x0f)];
			if (BIT(attr_code, 0) && (m_line_attr & 0x0b) == 0x0b)
				dots = ~dots;
			if (BIT(attr_code, 2))
				dots = ~dots;
		}

		for (int n = 8; n-- != 0; )
		{
			*pix++ = BIT(dots, n) ? fg : rgb_t::black();
			if (BIT(m_line_attr, 5))
				*pix++ = BIT(dots, n) ? fg : rgb_t::black();
		}
	}
}

MC6845_UPDATE_ROW(falcots_state::update_row)
{
	rgb_t fg(m_brightness, m_brightness, m_brightness);
	u32 *pix = &bitmap.pix(y);

	if (!BIT(m_line_attr, 5))
		x_count *= 2;
	for (int x = 0; x < x_count; ++x)
	{
		u8 char_code = BIT(m_line_attr, 4) ? m_vram[(m_line_addr_base + x) & 0x0fff] : 0;
		u8 attr_code = BIT(m_line_attr, 4) ? m_vram[0x1000 + ((m_line_addr_base + x) & 0x0fff)] : 0;

		u8 dots = 0;
		if (!BIT(attr_code, 5))
		{
			if (!BIT(attr_code, 1) || !BIT(m_blinkcnt->count(), 4))
				dots = ~m_chargen[u16(attr_code & 0x18) << 8 | u16(char_code & 0x7f) << 4 | (m_line_attr & 0x0f)];
			if (BIT(attr_code, 0) && (m_line_attr & 0x0b) == 0x0b)
				dots = ~dots;
			if (BIT(attr_code, 2))
				dots = ~dots;
		}

		for (int n = 8; n-- != 0; )
		{
			*pix++ = BIT(dots, n) ? fg : rgb_t::black();
			if (BIT(m_line_attr, 5))
				*pix++ = BIT(dots, n) ? fg : rgb_t::black();
		}
	}
}

void falcots_state::load_counters()
{
	m_line_addr_base = m_line_addr_latch >> 4;
	if (m_rstbuf.found())
		m_lines_left = ((m_line_attr_latch & 0x0f) >= 0x0c ? 0x0f : 0x0b) - (m_line_attr_latch & 0x0f);
	else
		m_lines_left = ~m_line_addr_latch & 0x000f;
	if (m_rstbuf.found() && BIT(m_line_attr_latch, 6))
		m_line_attr = (m_line_attr_latch & 0xf0) | (m_line_attr & 0x0f);
	else
		m_line_attr = m_line_attr_latch;
}

void falcots_state::row_clock_w(int state)
{
	if (state)
	{
		if (m_lines_left == 0)
			m_lineint->in_w<1>(1);
	}
	else
	{
		if (m_lines_left == 0)
			load_counters();
		else
		{
			--m_lines_left;
			if (BIT(m_line_attr, 7) != m_rstbuf.found())
				m_line_attr = (m_line_attr & 0xf0) | ((m_line_attr + 1) & 0x0f);
			if (BIT(m_line_attr, 6))
				m_line_attr ^= 0x80;
		}
		if (!m_rstbuf.found())
			m_lineint->in_w<1>(0);
	}
}

void falcots_state::vsync_w(int state)
{
	if (state)
	{
		m_lineint->in_w<1>(1);
	}
	else
	{
		if (!m_rstbuf.found())
			m_lineint->in_w<1>(0);
		load_counters();
	}
}

void falcots_state::bell_toggle_w(int state)
{
	if (state)
	{
		m_bell_toggle = !m_bell_toggle;
		m_bell->level_w(m_bell_toggle);
	}
}

u8 falcots_state::key_status_r()
{
	u8 status = m_crtc->vsync_r() ? 0x00 : 0x02;

	u8 i = (m_key_scan & 0x70) >> 4;
	u8 j = m_key_scan & 0x0f;
	if (!BIT(m_keys[j].read_safe(0xff), i))
		status |= 0x01;

	return status;
}

void falcots_state::key_scan_w(u8 data)
{
	// TODO: this is a vastly oversimplification of the keyboard interface
	// (actual interface uses various shift registers driven by an unknown clock)
	m_key_scan = data;
}

void falcots_state::video_reset_w(u8 data)
{
	m_line_addr_latch = 0;
	m_line_attr_latch = 0;
}

void falcots_state::line_addr_upper_w(u8 data)
{
	m_line_addr_latch = u16(data) << 8 | (m_line_addr_latch & 0x00ff);
}

void falcots_state::line_addr_lower_w(u8 data)
{
	m_line_addr_latch = (m_line_addr_latch & 0xff00) | data;
}

void falcots_state::line_attr_w(u8 data)
{
	m_line_attr_latch = data;
}

void falcots_state::line_int_clear_w(u8 data)
{
	m_lineint->in_w<1>(0);
}

void falcots_state::brightness_w(u8 data)
{
	m_brightness = data;
}

void falcots_state::bell_w(u8 data)
{
	// TODO
	logerror("%s: Bell triggered\n", machine().describe_context());
}

void falcots_state::control_w(u8 data)
{
	m_lineint->in_w<0>(BIT(data, 0));
}

void falcots_state::ts1_mem_map(address_map &map)
{
	map(0x0000, 0x1fff).rom().region("maincpu", 0);
	map(0x8000, 0xbfff).ram(); // 8x NEC D416C
	map(0xec00, 0xefff).ram().share("nvram");
	map(0xf000, 0xffff).ram().share("vram"); // 6x(!?) AM9114EPC
}

void falcots_state::ts1_io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0xe0, 0xe0).w(FUNC(falcots_state::video_reset_w));
	map(0xe1, 0xe1).w(FUNC(falcots_state::key_scan_w));
	map(0xe2, 0xe2).w(FUNC(falcots_state::line_addr_upper_w));
	map(0xe3, 0xe3).w(FUNC(falcots_state::brightness_w));
	map(0xe4, 0xe4).w(FUNC(falcots_state::line_attr_w));
	map(0xe5, 0xe5).w(FUNC(falcots_state::line_int_clear_w));
	map(0xe6, 0xe6).w(FUNC(falcots_state::bell_w));
	map(0xe7, 0xe7).w(FUNC(falcots_state::control_w));
	map(0xe8, 0xe8).r(FUNC(falcots_state::key_status_r));
	map(0xf0, 0xf0).w(m_crtc, FUNC(mc6845_device::address_w));
	map(0xf1, 0xf1).w(m_crtc, FUNC(mc6845_device::register_w));
	map(0xf8, 0xfb).r("pci", FUNC(scn2651_device::read));
	map(0xfc, 0xff).w("pci", FUNC(scn2651_device::write));
}

void falcots_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("maincpu", 0);
	map(0x8000, 0x87ff).ram().share("nvram");
	map(0xa000, 0xbfff).ram().share("vram"); // 4x HM6116P-3
	map(0xc000, 0xffff).ram(); // 8x AM9016EPC (4116)
}

void falcots_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0xe0, 0xe0).r(FUNC(falcots_state::key_status_r));
	map(0xe0, 0xe0).w(FUNC(falcots_state::video_reset_w));
	map(0xe1, 0xe1).w(FUNC(falcots_state::key_scan_w));
	map(0xe2, 0xe2).w(FUNC(falcots_state::line_addr_upper_w));
	map(0xe3, 0xe3).w(FUNC(falcots_state::brightness_w));
	map(0xe4, 0xe4).w(FUNC(falcots_state::line_attr_w));
	map(0xe5, 0xe5).w(FUNC(falcots_state::line_addr_lower_w));
	map(0xe7, 0xe7).w(FUNC(falcots_state::control_w));
	map(0xe8, 0xe8).w(m_crtc, FUNC(mc6845_device::address_w));
	map(0xe9, 0xe9).w(m_crtc, FUNC(mc6845_device::register_w));
	map(0xeb, 0xeb).r(m_crtc, FUNC(mc6845_device::register_r));
	map(0xf0, 0xf3).rw("dart", FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
	map(0xf8, 0xfb).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
}

static INPUT_PORTS_START(ts1)
	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1  !  F1") PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Escape") PORT_CHAR(0x1b) PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q  Set Tab") PORT_CHAR('q') PORT_CHAR('Q') PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab  Back Tab") PORT_CHAR(0x09) PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A  Block Mode") PORT_CHAR('a') PORT_CHAR('A') PORT_CODE(KEYCODE_A)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Control") PORT_CHAR(UCHAR_SHIFT_2) PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CHAR(UCHAR_SHIFT_1) PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift Caps  Pad") PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_CODE(KEYCODE_CAPSLOCK)

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"3  # £  F3") PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2  \"  F2") PORT_CHAR('2') PORT_CHAR('"') PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W  Clr Tab") PORT_CHAR('w') PORT_CHAR('W') PORT_CODE(KEYCODE_W)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S  Conv Mode") PORT_CHAR('s') PORT_CHAR('S') PORT_CODE(KEYCODE_S)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z  Ins Char") PORT_CHAR('z') PORT_CHAR('Z') PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X  Del Char") PORT_CHAR('x') PORT_CHAR('X') PORT_CODE(KEYCODE_X)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Function") PORT_CHAR(UCHAR_MAMEKEY(LALT)) PORT_CODE(KEYCODE_LALT)

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5  %  F5") PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4  $  F4") PORT_CHAR('4') PORT_CHAR('$') PORT_CODE(KEYCODE_4)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R  Scroll Down") PORT_CHAR('r') PORT_CHAR('R') PORT_CODE(KEYCODE_R)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E  Clr All Tabs") PORT_CHAR('e') PORT_CHAR('E') PORT_CODE(KEYCODE_E)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F  Freeze") PORT_CHAR('f') PORT_CHAR('F') PORT_CODE(KEYCODE_F)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CHAR('d') PORT_CHAR('D') PORT_CODE(KEYCODE_D)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V  Del Line") PORT_CHAR('v') PORT_CHAR('V') PORT_CODE(KEYCODE_V)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C  Ins Line") PORT_CHAR('c') PORT_CHAR('C') PORT_CODE(KEYCODE_C)

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6  &  F6") PORT_CHAR('6') PORT_CHAR('&') PORT_CODE(KEYCODE_6)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T  Scroll Up") PORT_CHAR('t') PORT_CHAR('T') PORT_CODE(KEYCODE_T)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CHAR('g') PORT_CHAR('G') PORT_CODE(KEYCODE_G)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CHAR('b') PORT_CHAR('B') PORT_CODE(KEYCODE_B)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(' ') PORT_CODE(KEYCODE_SPACE)

	PORT_START("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7  '  F7") PORT_CHAR('7') PORT_CHAR('\'') PORT_CODE(KEYCODE_7)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8  (  F8") PORT_CHAR('8') PORT_CHAR('(') PORT_CODE(KEYCODE_8)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CHAR('y') PORT_CHAR('Y') PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CHAR('u') PORT_CHAR('U') PORT_CODE(KEYCODE_U)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CHAR('h') PORT_CHAR('H') PORT_CODE(KEYCODE_H)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CHAR('j') PORT_CHAR('J') PORT_CODE(KEYCODE_J)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M  Send Page") PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_M)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N  Send Line") PORT_CHAR('n') PORT_CHAR('N') PORT_CODE(KEYCODE_N)

	PORT_START("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9  )  F9") PORT_CHAR('9') PORT_CHAR(')') PORT_CODE(KEYCODE_9)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0  _  F10") PORT_CHAR('0') PORT_CHAR('_') PORT_CODE(KEYCODE_0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CHAR('i') PORT_CHAR('I') PORT_CODE(KEYCODE_I)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CHAR('o') PORT_CHAR('O') PORT_CODE(KEYCODE_O)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CHAR('k') PORT_CHAR('K') PORT_CODE(KEYCODE_K)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L  Erase Line") PORT_CHAR('l') PORT_CHAR('L') PORT_CODE(KEYCODE_L)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".  >  Print Line") PORT_CHAR('.') PORT_CHAR('>') PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',') PORT_CHAR('<') PORT_CODE(KEYCODE_COMMA)

	PORT_START("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-  =  F11") PORT_CHAR('-') PORT_CHAR('=') PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^  ~  F12") PORT_CHAR('^') PORT_CHAR('~') PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CHAR('p') PORT_CHAR('P') PORT_CODE(KEYCODE_P)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('[') PORT_CHAR('{') PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";  +  Erase Page") PORT_CHAR(';') PORT_CHAR('+') PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(":  *  Erase") PORT_CHAR(':') PORT_CHAR('*') PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/  ?  Print Page") PORT_CHAR('/') PORT_CHAR('?') PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Back Space") PORT_CHAR(0x08) PORT_CODE(KEYCODE_RALT)

	PORT_START("KEY7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\  |  Examine F") PORT_CHAR('\\') PORT_CHAR('|') PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(']') PORT_CHAR('}') PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@  `  Set F") PORT_CHAR('@') PORT_CHAR('`') PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Rub Out  Clear") PORT_CHAR(0x7f) PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CHAR(0x0d) PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Line Feed") PORT_CHAR(0x0a) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT)

	PORT_START("KEY8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD)) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Keypad 8  \u2191 (Brite)") PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CODE(KEYCODE_8_PAD) // U+2191 = ↑
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Keypad 4  \u2190") PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CODE(KEYCODE_4_PAD) // U+2190 = ←
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad 5  Home") PORT_CHAR(UCHAR_MAMEKEY(5_PAD)) PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Keypad 2  \u2193 (Dim)") PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CODE(KEYCODE_2_PAD) // U+2193 = ↓
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0  Set Up") PORT_CHAR(UCHAR_MAMEKEY(0_PAD)) PORT_CODE(KEYCODE_0_PAD)

	PORT_START("KEY9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD)) PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(TAB_PAD)) PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Keypad 6  \u2192") PORT_CHAR(UCHAR_MAMEKEY(6_PAD)) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CODE(KEYCODE_6_PAD) // U+2192 = →
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD)) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad Enter  +") PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD)) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD)) PORT_CODE(KEYCODE_DEL_PAD)
INPUT_PORTS_END

static INPUT_PORTS_START(ts2624)
	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Aids")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Escape") PORT_CHAR(0x1b) PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x09) PORT_NAME("Tab  Back Tab") PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('q') PORT_CHAR('Q') PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Control") PORT_CHAR(UCHAR_SHIFT_2) PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift Caps") PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_CODE(KEYCODE_CAPSLOCK)

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("User Keys")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Mode")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR('@') PORT_CODE(KEYCODE_2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('w') PORT_CHAR('W') PORT_CODE(KEYCODE_W)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('a') PORT_CHAR('A') PORT_CODE(KEYCODE_A)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('z') PORT_CHAR('Z') PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CHAR(UCHAR_SHIFT_1) PORT_CODE(KEYCODE_LSHIFT)

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR('$') PORT_CODE(KEYCODE_4)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('e') PORT_CHAR('E') PORT_CODE(KEYCODE_E)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('s') PORT_CHAR('S') PORT_CODE(KEYCODE_S)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('d') PORT_CHAR('D') PORT_CODE(KEYCODE_D)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('x') PORT_CHAR('X') PORT_CODE(KEYCODE_X)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Function") PORT_CHAR(UCHAR_MAMEKEY(LALT)) PORT_CODE(KEYCODE_LALT)

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('r') PORT_CHAR('R') PORT_CODE(KEYCODE_R)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('t') PORT_CHAR('T') PORT_CODE(KEYCODE_T)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('f') PORT_CHAR('F') PORT_CODE(KEYCODE_F)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('g') PORT_CHAR('G') PORT_CODE(KEYCODE_G)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('v') PORT_CHAR('V') PORT_CODE(KEYCODE_V)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('c') PORT_CHAR('C') PORT_CODE(KEYCODE_C)

	PORT_START("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR('^') PORT_CODE(KEYCODE_6)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR('&') PORT_CODE(KEYCODE_7)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('y') PORT_CHAR('Y') PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('h') PORT_CHAR('H') PORT_CODE(KEYCODE_H)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('b') PORT_CHAR('B') PORT_CODE(KEYCODE_B)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(' ') PORT_CODE(KEYCODE_SPACE)

	PORT_START("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_CODE(KEYCODE_F6)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_CODE(KEYCODE_F7)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('8') PORT_CHAR('*') PORT_CODE(KEYCODE_8)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('i') PORT_CHAR('I') PORT_CODE(KEYCODE_I)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('u') PORT_CHAR('U') PORT_CODE(KEYCODE_U)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('j') PORT_CHAR('J') PORT_CODE(KEYCODE_J)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_M)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('n') PORT_CHAR('N') PORT_CODE(KEYCODE_N)

	PORT_START("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_CODE(KEYCODE_F8)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0') PORT_CHAR(')') PORT_CODE(KEYCODE_0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('9') PORT_CHAR('(') PORT_CODE(KEYCODE_9)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('o') PORT_CHAR('O') PORT_CODE(KEYCODE_O)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('k') PORT_CHAR('K') PORT_CODE(KEYCODE_K)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('l') PORT_CHAR('L') PORT_CODE(KEYCODE_L)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('.') PORT_CHAR('>') PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',') PORT_CHAR('<') PORT_CODE(KEYCODE_COMMA)

	PORT_START("KEY7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ins/Del Line")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ins/Del Char")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('-') PORT_CHAR('_') PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('[') PORT_CHAR('{') PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('p') PORT_CHAR('P') PORT_CODE(KEYCODE_P)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHAR(':') PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('/') PORT_CHAR('?') PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Back Space") PORT_CHAR(0x08) PORT_CODE(KEYCODE_RALT)

	PORT_START("KEY8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Line/Dsp Clear")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('=') PORT_CHAR('+') PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('`') PORT_CHAR('~') PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Delete") PORT_CHAR(0x7f) PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(']') PORT_CHAR('}') PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\\') PORT_CHAR('|') PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\'') PORT_CHAR('"') PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT)

	PORT_START("KEY9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Back Space (Break)") PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD)) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Keypad 4  \u2190") PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) PORT_CODE(KEYCODE_4_PAD) // U+2190 = ←
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CHAR(0x0d) PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD)) PORT_CODE(KEYCODE_0_PAD)

	PORT_START("KEYA")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2190  Prev Page") PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CODE(KEYCODE_LEFT) // U+2190 = ←
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2191  Scroll Up") PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CODE(KEYCODE_UP) // U+2191 = ↑
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Keypad 8  \u2191 (Brite)") PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) PORT_CODE(KEYCODE_8_PAD) // U+2191 = ↑
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad 5  Home") PORT_CHAR(UCHAR_MAMEKEY(5_PAD)) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Keypad 2  \u2193 (Dim)") PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) PORT_CODE(KEYCODE_2_PAD) // U+2193 = ↓
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD)) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD)) PORT_CODE(KEYCODE_DEL_PAD)

	PORT_START("KEYB")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2193  Scroll Down") PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CODE(KEYCODE_DOWN) // U+2193 = ↓
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2192  Next Page") PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CODE(KEYCODE_RIGHT) // U+2192 = →
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD)) PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Keypad 6  \u2192") PORT_CHAR(UCHAR_MAMEKEY(6_PAD)) PORT_CODE(KEYCODE_6_PAD) // U+2192 = →
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad Back Tab") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad Forward Tab") PORT_CHAR(UCHAR_MAMEKEY(TAB_PAD)) PORT_CODE(KEYCODE_ENTER_PAD)
INPUT_PORTS_END

void falcots_state::ts1(machine_config &config)
{
	Z80(config, m_maincpu, 15.2064_MHz_XTAL / 4); // NEC D780C-1 (divider guessed)
	m_maincpu->set_addrmap(AS_PROGRAM, &falcots_state::ts1_mem_map);
	m_maincpu->set_addrmap(AS_IO, &falcots_state::ts1_io_map);
	m_maincpu->set_irq_acknowledge_callback("rstbuf", FUNC(rst_pos_buffer_device::inta_cb));

	RST_POS_BUFFER(config, m_rstbuf).int_callback().set_inputline(m_maincpu, 0);
	INPUT_MERGER_ALL_HIGH(config, m_lineint).output_handler().set(m_rstbuf, FUNC(rst_pos_buffer_device::rst4_w));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // 2x NEC D444C + battery?

	scn2651_device &pci(SCN2651(config, "pci", 15.2064_MHz_XTAL / 3)); // SCN2651N
	pci.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	pci.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	pci.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	pci.txrdy_handler().set(m_rstbuf, FUNC(rst_pos_buffer_device::rst1_w));
	pci.rxrdy_handler().set(m_rstbuf, FUNC(rst_pos_buffer_device::rst2_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(15.2064_MHz_XTAL, 792, 0, 640, 320, 0, 300);
	screen.set_screen_update(m_crtc, FUNC(mc6845_device::screen_update));
	screen.set_video_attributes(VIDEO_UPDATE_SCANLINE);

	MC6845(config, m_crtc, 15.2064_MHz_XTAL / 8); // MC6845P
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(falcots_state::ts1_update_row));
	m_crtc->out_de_callback().set(FUNC(falcots_state::row_clock_w));
	m_crtc->out_vsync_callback().set(FUNC(falcots_state::vsync_w));
	m_crtc->out_vsync_callback().append(m_blinkcnt, FUNC(ripple_counter_device::clock_w)).invert();

	RIPPLE_COUNTER(config, m_blinkcnt).set_stages(5);

	// TODO: actually has two RS-232C ports (A & B)
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "loopback"));
	rs232.rxd_handler().set("pci", FUNC(scn2651_device::rxd_w));
	rs232.cts_handler().set("pci", FUNC(scn2651_device::cts_w));
	rs232.dcd_handler().set("pci", FUNC(scn2651_device::dcd_w));
	rs232.dsr_handler().set("pci", FUNC(scn2651_device::dsr_w));
}

static const z80_daisy_config daisy_chain[] =
{
	{ "ctc" },
	{ "dart" },
	{ nullptr }
};

void falcots_state::ts2624(machine_config &config)
{
	Z80(config, m_maincpu, 14.7456_MHz_XTAL / 4); // Z8400AB1
	m_maincpu->set_addrmap(AS_PROGRAM, &falcots_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &falcots_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	INPUT_MERGER_ALL_HIGH(config, m_lineint).output_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // HM6116LP-4 + battery

	z80ctc_device &ctc(Z80CTC(config, "ctc", 14.7456_MHz_XTAL / 4)); // Z8430AB1
	ctc.set_clk<0>(14.7456_MHz_XTAL / 16);
	ctc.set_clk<1>(14.7456_MHz_XTAL / 16);
	ctc.zc_callback<0>().set("dart", FUNC(z80dart_device::rxca_w));
	ctc.zc_callback<0>().append("dart", FUNC(z80dart_device::txca_w));
	ctc.zc_callback<1>().set("dart", FUNC(z80dart_device::rxtxcb_w));
	ctc.zc_callback<2>().set(FUNC(falcots_state::bell_toggle_w));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80dart_device &dart(Z80DART(config, "dart", 14.7456_MHz_XTAL / 4)); // Z8470AB1
	dart.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	dart.out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
	dart.out_dtra_callback().set("rs232a", FUNC(rs232_port_device::write_dtr));
	dart.out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts));
	dart.out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
	dart.out_dtrb_callback().set("rs232b", FUNC(rs232_port_device::write_dtr));
	dart.out_rtsb_callback().set("rs232b", FUNC(rs232_port_device::write_rts));

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_bell).add_route(ALL_OUTPUTS, "mono", 0.05);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(14.7456_MHz_XTAL, 768, 0, 640, 320, 0, 286);
	//screen.set_raw(23.9616_MHz_XTAL, 1248, 0, 1056, 320, 0, 286);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	screen.set_video_attributes(VIDEO_UPDATE_SCANLINE);

	MC6845(config, m_crtc, 14.7456_MHz_XTAL / 16);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(16);
	m_crtc->set_update_row_callback(FUNC(falcots_state::update_row));
	m_crtc->out_de_callback().set(FUNC(falcots_state::row_clock_w));
	m_crtc->out_vsync_callback().set(FUNC(falcots_state::vsync_w));
	m_crtc->out_vsync_callback().append(m_blinkcnt, FUNC(ripple_counter_device::clock_w)).invert();

	RIPPLE_COUNTER(config, m_blinkcnt).set_stages(5); // 1/2 74LS393 + 1/2 74LS74

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set("dart", FUNC(z80dart_device::rxa_w));
	rs232a.cts_handler().set("dart", FUNC(z80dart_device::ctsa_w));
	rs232a.dcd_handler().set("dart", FUNC(z80dart_device::dcda_w));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set("dart", FUNC(z80dart_device::rxb_w));
	rs232b.cts_handler().set("dart", FUNC(z80dart_device::ctsb_w));
	rs232b.dcd_handler().set("dart", FUNC(z80dart_device::dcdb_w));
}

ROM_START(ts1)
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("v2_13_x.d9",  0x0000, 0x1000, CRC(420e1ecd) SHA1(748e3733858ba813b9d72dfe018ba4f918d8c0db)) // Chip Type: 2732
	ROM_LOAD("v2_13_0.d10", 0x1000, 0x1000, CRC(228e7321) SHA1(43e0d04c58ee7c71f5603222bf0aaaf7979d67a3)) // Chip Type: 2732

	ROM_REGION(0x2000, "chargen", ROMREGION_ERASEFF)
	ROM_LOAD("crom003.f4", 0x0000, 0x0800, CRC(557c8e0b) SHA1(b028f526bd92f957ee6242a7e0e6e0f16b0880a8)) // Chip Type: EA8316E517
ROM_END

ROM_START(ts2624)
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD("1.bin", 0x0000, 0x2000, CRC(14fb80aa) SHA1(93bf0d39f3e4bf092b6cd850f95ee6cbd322ad13))
	ROM_LOAD("2.bin", 0x2000, 0x2000, CRC(d4c74a06) SHA1(291357a296c45fccdbe8e395ea170d847a3a6f03))
	ROM_LOAD("3.bin", 0x4000, 0x2000, CRC(90d0d04b) SHA1(099d6741091b3abbe4187c8278e2c7ebe151531c))
	ROM_LOAD("4.bin", 0x6000, 0x2000, CRC(b0c59ec8) SHA1(099f6d6a7594e177bc668fd19fa19c3f0f4ab38e))

	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD("chr.bin", 0x0000, 0x2000, CRC(38569fe2) SHA1(c666c596bb6326e4f41ccfd91154bcfd75f5c0a3))

	ROM_REGION(0x60, "proms", 0)
	ROM_LOAD("msel64b.9c", 0x00, 0x20, NO_DUMP) // 74S288 or equivalent
	ROM_LOAD("prom.13d",   0x20, 0x20, NO_DUMP) // 74S288 or equivalent
	ROM_LOAD("prom.12f",   0x20, 0x20, NO_DUMP) // 74S288 or equivalent
ROM_END

} // anonymous namespace


COMP(1980, ts1,    0, 0, ts1,    ts1,    falcots_state, empty_init, "Falco Data Products", "TS-1 (v2.13.0)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND)
COMP(1982, ts2624, 0, 0, ts2624, ts2624, falcots_state, empty_init, "Falco Data Products", "TS-2624", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS)



falcots28.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    Falco Endura TS-28 (aka FM-II?)

    VT100/VT102/VT132 terminal

    Hardware:
    - Z80A (Z8400A PS)
    - 6116
    - SY2128-3 x2 (and two empty sockets)
    - 2x 2764 (and one empty socket)
    - 1x 2732 (chargen)
    - Z80ACTC
    - SCN2672 with SCB2673
    - Z80ADART
    - XTAL 16.537 MHz (hard to read)
    - Bell
    - DAC0832LCN (brightness control)

    TODO:
    - Some scrolling issues (GNU nano for example)
    - Bell is really silent

    Notes:
    - Starts with a relatively dark screen. Push FUNCTION (mapped to LALT)
      and cursor up to increase brightness.
    - Keytek inductric keyboard

***************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/nvram.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "sound/spkrdev.h"
#include "video/scn2674.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class ts28_state : public driver_device
{
public:
	ts28_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen"),
		m_palette(*this, "palette"),
		m_pvtc(*this, "pvtc"),
		m_dart(*this, "dart"),
		m_chargen(*this, "chargen"),
		m_keys(*this, "key_%x", 0U)
	{ }

	void ts28(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<scn2672_device> m_pvtc;
	required_device<z80dart_device> m_dart;
	required_region_ptr<uint8_t> m_chargen;
	required_ioport_array<16> m_keys;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	void key_scan_w(uint8_t data);

	void crt_brightness_w(uint8_t data);
	void char_map(address_map &map) ATTR_COLD;
	void attr_map(address_map &map) ATTR_COLD;
	SCN2672_DRAW_CHARACTER_MEMBER(draw_character);

	uint8_t m_key_scan = 0;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void ts28_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).rom().region("maincpu", 0);
	map(0x8000, 0x87ff).ram().share("nvram");
	map(0xa000, 0xafff).ram().share("charram");
	map(0xb000, 0xbfff).ram().share("attrram");
}

void ts28_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0xe1, 0xe1).w(FUNC(ts28_state::crt_brightness_w));
	map(0xe3, 0xe3).w(FUNC(ts28_state::key_scan_w));
	map(0xe8, 0xef).rw(m_pvtc, FUNC(scn2672_device::read), FUNC(scn2672_device::write));
	map(0xf0, 0xf3).rw(m_dart, FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
	map(0xf8, 0xfb).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( ts28 )
	PORT_START("key_0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11)      PORT_NAME("Set Up")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)      PORT_CHAR(27)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)        PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)      PORT_CHAR(9)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)        PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))

	PORT_START("key_1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)     PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)      PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)      PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)      PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)      PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)      PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)      PORT_NAME("Left Shift")

	PORT_START("key_2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)   PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)   PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)    PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)    PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)    PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)    PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)    PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT) PORT_NAME("Function")

	PORT_START("key_3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)  PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)  PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)  PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)  PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)  PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)  PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)  PORT_CHAR('c') PORT_CHAR('C')

	PORT_START("key_4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)    PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)    PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)     PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)     PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)     PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)     PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)     PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("key_5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)  PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)  PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)  PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)  PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)  PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)  PORT_CHAR('n') PORT_CHAR('N')

	PORT_START("key_6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9)    PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)     PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)     PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)     PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)     PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)     PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)  PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("key_7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F12)       PORT_NAME("Edit  Edit F")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10)       PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)     PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)         PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)     PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)     PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RALT)      PORT_NAME("Correct")

	PORT_START("key_8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PRTSCR)     PORT_NAME("Print  Exten")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('`') PORT_CHAR('~')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)        PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT)     PORT_NAME("Right Shift")

	PORT_START("key_9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME)      PORT_CHAR(UCHAR_MAMEKEY(HOME))  PORT_NAME("Home  Clear")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)                    PORT_NAME("Back Space  Break")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)     PORT_CHAR(UCHAR_MAMEKEY(7_PAD)) PORT_NAME("Keypad 7  Ins Char")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)     PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) PORT_NAME("Keypad 4  Erase Line")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)     PORT_CHAR(13)                   PORT_NAME("Return")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)     PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) PORT_NAME("Keypad 1  Send Line")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RCONTROL)                                  PORT_NAME("Line Feed")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)     PORT_CHAR(UCHAR_MAMEKEY(0_PAD))

	PORT_START("key_a")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)    PORT_CHAR(UCHAR_MAMEKEY(DOWN))  PORT_NAME("\xe2\x86\x93  Dim")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)      PORT_CHAR(UCHAR_MAMEKEY(UP))    PORT_NAME("\xe2\x86\x91  Brite")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)   PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) PORT_NAME("Keypad 8  Del Char")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)   PORT_CHAR(UCHAR_MAMEKEY(5_PAD)) PORT_NAME("Keypad 5  Erase Page")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)   PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) PORT_NAME("Keypad 2  Send Page")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)   PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))

	PORT_START("key_b")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)     PORT_CHAR(UCHAR_MAMEKEY(RIGHT))     PORT_NAME("\xe2\x86\x92")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)      PORT_CHAR(UCHAR_MAMEKEY(LEFT))      PORT_NAME("\xe2\x86\x90")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) PORT_NAME("Keypad -  Del Line")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)     PORT_CHAR(UCHAR_MAMEKEY(9_PAD))     PORT_NAME("Keypad 9  Ins Line")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)     PORT_CHAR(UCHAR_MAMEKEY(6_PAD))     PORT_NAME("Keypad 6  Erase")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD)                                      PORT_NAME("Keypad ,")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))

	PORT_START("key_c")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("key_d")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("key_e")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("key_f")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END


//**************************************************************************
//  KEYBOARD
//**************************************************************************

void ts28_state::key_scan_w(uint8_t data)
{
	int i = BIT(data, 4, 3);
	int j = BIT(data, 0, 4);

	m_dart->rib_w(BIT(m_keys[j]->read(), i));
}


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

void ts28_state::crt_brightness_w(uint8_t data)
{
	m_screen->set_brightness(data);
}

void ts28_state::char_map(address_map &map)
{
	map(0x0000, 0x0fff).ram().share("charram");
}

void ts28_state::attr_map(address_map &map)
{
	map(0x0000, 0x0fff).ram().share("attrram");
}

SCN2672_DRAW_CHARACTER_MEMBER( ts28_state::draw_character )
{
	// 76------  unknown
	// --5-----  normal/bold
	// ---43---  unknown
	// -----2--  invert
	// ------1-  blink
	// -------0  underline

	uint16_t data = m_chargen[charcode << 4 | linecount];
	const pen_t *const pen = m_palette->pens();

	if (ul && (BIT(attrcode, 0)))
		data = 0x1ff;

	if (blink && (BIT(attrcode, 1)))
		data = 0x000;

	if (BIT(attrcode, 2))
		data = ~data;

	if (cursor)
		data = ~data;

	// foreground/background colors
	rgb_t fg = BIT(attrcode, 5) ? pen[1] : pen[2];
	rgb_t bg = pen[0];

	// draw 9 pixels of the character
	for (int i = 0; i < 9; i++)
		bitmap.pix(y, x + i) = BIT(data, i) ? fg : bg;
}

static const gfx_layout char_layout =
{
	8, 16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	{ STEP16(0, 8) },
	8 * 16
};

static GFXDECODE_START(chars)
	GFXDECODE_ENTRY("chargen", 0, char_layout, 0, 1)
GFXDECODE_END


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void ts28_state::machine_start()
{
	// register for save states
	save_item(NAME(m_key_scan));
}

void ts28_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

static const z80_daisy_config daisy_chain[] =
{
	{ "ctc" }, // interrupts not enabled, might not be part of the chain
	{ "dart" },
	{ nullptr }
};

void ts28_state::ts28(machine_config &config)
{
	Z80(config, m_maincpu, 16.537_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &ts28_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &ts28_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_color(rgb_t::amber()); // unknown color
	m_screen->set_raw(16.537_MHz_XTAL, 864, 0, 720, 319, 0, 300);
	m_screen->set_screen_update(m_pvtc, FUNC(scn2672_device::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	GFXDECODE(config, "gfxdecode", m_palette, chars);

	SCN2672(config, m_pvtc, 16.537_MHz_XTAL / 9);
	m_pvtc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_pvtc->set_screen("screen");
	m_pvtc->set_character_width(9);
	m_pvtc->set_display_callback(FUNC(ts28_state::draw_character));
	m_pvtc->set_addrmap(0, &ts28_state::char_map);
	m_pvtc->set_addrmap(1, &ts28_state::attr_map);

	z80ctc_device &ctc(Z80CTC(config, "ctc", 16.537_MHz_XTAL / 4));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	ctc.set_clk<0>(16.537_MHz_XTAL / 9);
	ctc.zc_callback<0>().set(m_dart, FUNC(z80dart_device::rxca_w));
	ctc.zc_callback<0>().append(m_dart, FUNC(z80dart_device::txca_w));
	ctc.set_clk<1>(16.537_MHz_XTAL / 9);
	ctc.zc_callback<1>().set(m_dart, FUNC(z80dart_device::rxtxcb_w));
	ctc.set_clk<2>(16.537_MHz_XTAL / 9);
	ctc.zc_callback<2>().set("bell", FUNC(speaker_sound_device::level_w));

	Z80DART(config, m_dart, 16.537_MHz_XTAL / 4);
	m_dart->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_dart->out_txda_callback().set("porta", FUNC(rs232_port_device::write_txd));
	m_dart->out_rtsa_callback().set("porta", FUNC(rs232_port_device::write_rts));
	m_dart->out_txdb_callback().set("portb", FUNC(rs232_port_device::write_txd));
	m_dart->out_rtsb_callback().set("portb", FUNC(rs232_port_device::write_rts));

	rs232_port_device &porta(RS232_PORT(config, "porta", default_rs232_devices, nullptr));
	porta.rxd_handler().set(m_dart, FUNC(z80dart_device::rxa_w));
	porta.cts_handler().set(m_dart, FUNC(z80dart_device::ctsa_w));

	rs232_port_device &portb(RS232_PORT(config, "portb", default_rs232_devices, nullptr));
	portb.rxd_handler().set(m_dart, FUNC(z80dart_device::rxb_w));
	portb.cts_handler().set(m_dart, FUNC(z80dart_device::ctsb_w));

	// sound hardware
	SPEAKER(config, "mono").front_center();

	SPEAKER_SOUND(config, "bell").add_route(ALL_OUTPUTS, "mono", 1.00);
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( ts28 )
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_LOAD("endura_v100_0.e27", 0x0000, 0x2000, CRC(2fd1ea7f) SHA1(711e09b843ecf2e463bd2b193a66842261512fa4))
	ROM_LOAD("endura_v100_1.e28", 0x2000, 0x2000, CRC(4de83d27) SHA1(a1c0858debfb0c1e82b44c620855a13dc1626302))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("chargen.f3", 0x0000, 0x1000, CRC(f0764798) SHA1(3d4826bfe1f1e533ee2134ad9ae6c2512edbb069))

	ROM_REGION(0x40, "prom", 0)
	ROM_LOAD("82s123.f3", 0x00, 0x20, CRC(862b9680) SHA1(0d19266cb9680e0e5dd92d230b64a5ee86bf6046))
	ROM_LOAD("82s123.h7", 0x00, 0x20, CRC(275a4436) SHA1(e986454f6f0f93f72b1c43a49ff3ded41630e38b))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY  FULLNAME        FLAGS
COMP( 1983, ts28, 0,       0,     ts28,    ts28,  ts28_state, empty_init, "Falco", "Endura TS-28", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )



fanucs15.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    Fanuc System 15

    2014-01-04 Skeleton driver.

    This is a circa-1990 CNC machine with a very custom GUI, made of a
    bunch of boards plugged into a passive backplane.

    Possible boards include:
    A16B-2200-0020/04B : Base 2 Board (contains 12 MHz 68020 "CNC CPU" and 1 or 2 MB of DRAM)
    A16B-2200-0090/07A : Digital Servo 4-Axis Controller Board (no ROMs on this board)
    A16B-2200-0121/06C : Base 0 Board (1 ROM location but position is empty. -0120 has 6081 001A)

    A16B-2200-0131/11B : Base 1 Board (15 ROMs on this board, 4040, 9030, AB02)
        See detailed layout and description below.

    A16B-2200-0150/03A : Conversational Board (4 ROMs on this board, 5A00, probably for Mill)
    or
    A16B-2200-0150/03A : Conversational Board (4 ROMs on this board, 5C30. 5C for 2 axis Lathe)
    or
    A16B-2200-0150/03A : Conversational Board (4 ROMs on this board, 5D30. 5D for 4 axis (twin Turret) lathe)
        Fanuc FBI-A MB605111 (QFP100)
        MB1422A (DIP42 - DRAM Controller)
        51256-10 (x18)
        MB89259A (SOP28)
        Intel 80286-8
        Intel 80287-8
        MBL82288-8 (DIP10)
        uPB8284 (DIP18)
        24MHz & 32MHz OSC's
        4 EPROMs

    A16B-2200-0160/07B : Graphic Board (3 ROMs on this board, 6082 001A, 600B 001D, 600B 002D. 600B is for Mill)
    or
    A16B-2200-0160/07B : Graphic Board (3 ROMs on this board, 6082 001A, 600A 001C, 600A 001D. 600A is for Lathe)
    Fanuc FBI-A MB605111 (QFP100)
        HD68HC000P10 (68000 @10MHz, PLCC68)
        HM62256-10 (x2)
        HM53461-12 (x3)
        HD63484 (PLCC68)
        HD6445 (PLCC44)
        Fanuc GBC MB652147 (PGA135)
        MB81464-12 (x12)
        20MHz OSC
        3 EPROMs. 6082 is common to both mills and lathes.
        600A is a known EPROM version required for lathes with FAPT conversational graphics.
        600B is unknown. Possibly for mills with FAPT.

    A20B-1003-0230/09C : Motherboard (dumb backplane, contains only slots)
    A20B-1003-0500/01A : Additional Graphic Board for 15TTF only (for TT, connects to A16B-2200-0160 Board. No info on this PCB)
    A20B-1003-0240/07B : Connector Assembly Unit / IO Board
    A20B-1003-0580/01A : PMC Cassette C (16000 Step + Pascal 128KB, small PCB in a yellow plastic box, contains just 2 EPROMs)


    Fanuc System 15A Base 1 Board A16B-2200-013

    PCB Layout
    ----------

    A16B-2200-0131/03B (note 1 and 03B have been added/printed later)
           |--------------|     |---|
    |------|     CA34     |-----|CA4|-------------------------------------|
    |      |--------------|75463|---|    LL  LL                   MB81C79A|
    |                       (x6)         LL  LL              |--------|   |
    |                                             HM53461(x5)|MB605117|   |
    |                                   FA8191               |        |   |
    |4040002E.M27 4040001E.M23          FA8191               |        |   |
    |                                                        |--------|   |
    |                                                                     |
    |                                    MC-122-41256A9A-12  |--------|   |
    |AB02142A.K27 AB02141A.K23           MC-122-41256A9A-12  |MB661128|   |
    |                         9030001E.J18                   |        |   |
    |                                                        |        |   |
    |                                                        |--------|   |
    |AB02102A.H27 AB02101A.H23                                            |
    |                                                                     |
    |                                                                     |
    |                                                                     |
    |AB020C2A.F27 AB020C1A.F23              24MHz                         |
    |                                                                     |
    |                                                                     |
    |                                                        |--------|   |
    |AB02082A.E27 AB02081A.E23      |------|                 |MB605111|   |
    |                               |68000 |                 |        |   |
    |                               |      |                 |        |   |
    |   |---------|                 |      |                 |--------|   |
    |   |---------|                 |------|                              |
    |      CNM1                                |-----------------------|  |
    |------------------------------------------|          CNA          |--|
                                               |-----------------------|
    Notes:
          MC-122-41256- NEC MC-122-41256A9A-12 256k RAM SIP module (with parity) containing NEC D41256L-12 32k x8 SRAM
                        2 SIP modules populated, 9 chips per module, 18 total RAMs, total 512k
          68000       - Hitachi HD68HC000-12 68000 CPU. Clock 24/2 (PLCC68)
          MB661128    - Fujitsu Fanuc SLC01 MB661128 (Serial Data Link Controller, PLCC68)
          MB605117    - Fujitsu Fanuc BOC MB605117U (DRAM/SRAM Interface Controller, QFP100)
          MB605111    - Fujitsu Fanuc FBI-A MB605111 (Global/Local Interface Controller, QFP100)
          HM53461     - Hitachi HM53461-12 64k x4-bit multiport CMOS video RAM (x5, SOJ24)
          MB81C79A    - Fujitsu MB81C79A-35 8k x 9-bit (72k) CMOS Static RAM (SOP28)
          FA8191      - Fanuc FA8191 RV07 custom ceramic module (contains resistors and transistors, SIL16)
          75463       - Texas Instruments SN75463 Dual High-Voltage, High-Current Peripheral Driver (DIP8)
          L           - LED
          CA34        - Connector for PMC Cassette C A02B-0094-C103 (holds 2 EPROMs containing the PMC Ladder)
                        The Ladder is specific and unique to each CNC machine, depending on capability and options.
          CA4         - Connector for cable joined to Servo Control Board A16B-2200-0090 or 16B-2200-0091
          CNA         - Connector plugs into motherboard slot CNA2 (BASE 1)
          CNM1        - 40 pin flat cable joined to optional board A16B-1600-0280/02A

          Note about EPROMs:
                            Software Series is denoted by the first 4 digits
                            ROM # (possibly relating to the memory location in hex) is the next 3 digits
                            Software Revision is the last letter
                            Both IC locations and ROM # are printed on this board

          ROM         IC         Memory     EPROM     Used
          Label       Location   Location   Type      For...
          -----------------------------------------------------------------------------------------
          9030001E    J18        381        27256     Digital Servo Control Program Version 9030 Revision E

          4040001E    M23        001        27C1001   PMC Control Program Version 4040 Revision E
          4040002E    M27        002        27C1001

          AB02081A    E23        081        27C1001   Boot Software Version AB02 Revision A
          AB02082A    E27        082        27C1001
          AB020C1A    F23        0C1        27C1001
          AB020C2A    F27        0C2        27C1001
          AB02101A    H23        101        27C1001
          AB02102A    H27        102        27C1001
          AB02141A    K23        141        27C1001
          AB02142A    K27        142        27C1001


    Another identical board from a different machine contains the following EPROMs....
    9030001F (known revisions exist up to at least rev M)
    4040001D, 4040002D (known revisions exist up to at least rev I)
    A202081G, A202082G
    A2020C1G, A2020C2G
    A202101G, A202102G

    The complete boot software series is listed below:

    15TA SOFTWARE SERIES (Single Turret 2 Axis Lathe)
    |------+----+---------+---+---+---+---|
    |S/W   |STEP|   CRT   |I/O|SER|SER|DNC|
    |SERIES|    |         |   |   |   |   |
    |      |1  2| 9M 14 9C|LNK|SPN|FB |1 2|
    +------+----+---------+---+---+---+---+
    |A201  |X   | X       |   |   |   |   |
    |A202  |X   |    X    |   |   |   |   |
    |A211  |   X| X       | X | X |   |X  |
    |A212  |   X|    X    | X | X |   |X  |
    |A219  |   X| X       | X | X | X |   |
    |A220  |   X|    X    | X | X | X |   |
    |A215  |   X| X       | X | X |   |  X|
    |A216  |   X|    X    | X | X |   |  X|
    |A217  |   X| X       | X | X | X |  X|
    |A218  |   X|    X    | X | X | X |  X|
    |A221  |   X|       X | X | X | ? |  ?|
    |------+----+---------+---+---+---+---|
    Notes:
          9M      - 9" Monochrome
          9C      - 9" Color
          14      - 14" Color
          SER SPN - Serial Spindle
          SER FB  - Serial FB ?
          DNC 1/2 - DNC (Direct NC) type 1 or type 2 for Direct PC to CNC program transfer operation


    15TTA SOFTWARE SERIES (Twin Turret 4 Axis Lathe)
    |------+----+---------+---+---+---+---|
    |S/W   |STEP|   CRT   |I/O|SER|SER|DNC|
    |SERIES|    |         |   |   |   |   |
    |      |1  2| 9M 14 9C|LNK|SPN|FB |1 2|
    +------+----+---------+---+---+---+---+
    |A401  |X   | X       |   |   |   |   |
    |A402  |X   |    X    |   |   |   |   |
    |A411  |   X| X       | X | X |   |X  |
    |A412  |   X|    X    | X | X |   |X  |
    |A419  |   X| X       | X | X | X |   |
    |A420  |   X|    X    | X | X | X |   |
    |------+----+---------+---+---+---+---|


    15TTF SOFTWARE SERIES (Twin Turret 4 Axis Lathe with FAPT)
    |------+----+---------+---+---+---+---|
    |S/W   |STEP|   CRT   |I/O|SER|SER|DNC|
    |SERIES|    |         |   |   |   |   |
    |      |1  2| 9M 14 9C|LNK|SPN|FB |1 2|
    +------+----+---------+---+---+---+---+
    |A502  |X   |       X |   |   |   |   |
    |A512  |   X|       X | X | X |   |X  |
    |A520  |   X|       X | X | X | X |   |
    |------+----+---------+---+---+---+---|


    15MA SOFTWARE SERIES (Mill With 3 Axes minimum XYZ)
    |------+----+---------+---+---+---+---|---|
    |S/W   |STEP|   CRT   |I/O|SER|SER|DNC|SUB|
    |SERIES|    |         |   |   |   |   |   |
    |      |1  2| 9M 14 9C|LNK|SPN|FB |1 2|CPU|
    +------+----+---------+---+---+---+---+---|
    |A001  |X   | X       |   |   |   |   |   |
    |A002  |X   |    X    |   |   |   |   |   |
    |A00A  |X   | X       |   |   |   |   |   |
    |A00B  |X   |    X    |   |   |   |   |   |
    |AA01  |X   | X       |   |   |   |   | X |
    |AA02  |X   |    X    |   |   |   |   | X |
    |A011  |   X| X       | X | X |   |X  |   |
    |A012  |   X|    X    | X | X |   |X  |   |
    |AA11  |   X| X       | X | X |   |X  | X |
    |AA12  |   X|    X    | X | X |   |X  | X |
    |A017  |   X| X       | X | X | X |  X|   |
    |A018  |   X|    X    | X | X | X |  X|   |
    |A019  |   X| X       | X | X | X |   |   |
    |A01A  |   X| X       | X | X |   |X  |   |
    |A01B  |   X|    X    | X | X |   |X  |   |
    |A020  |   X|    X    | X | X | X |   |   |
    |A021  |   X|        X| X | X | X |  ?|   |
    |A027 (NEW STANDARD)  |   |   |   |   |   |
    |A028 (NEW STANDARD)  |   |   |   |   |   |
    |A041 (FOR OSI ONLY)  |   |   |   |   |   |
    |A042 (FOR OSI ONLY)  |   |   |   |   |   |
    |AA19  |   X| X       | X | X | X |   | X |
    |AA20  |   X|    X    | X | X | X |   | X |
    |AA13  |   X| X       | X | X | X |  X| X |
    |AA14  |   X|    X    | X | X | X |  X| X |
    |AA23  |   X| X       | X | X | X |   | X |
    |AA24  |   X|    X    | X | X | X |   | X |
    |AA26  |   X|    X    | X | X | X |  ?| X |
    |AA27 (NEW STD)       |   |   |   |   |   |
    |AA28 (NEW STD)       |   |   |   |   |   |
    |AA41 (FOR OSI ONLY)  |   |   |   |   |   |
    |AA42 (FOR OSI ONLY)  |   |   |   |   |   |
    |---------------------+---+---+---+---+---|


    Optional PCB A16B-1600-0280/02A
    -------------------------------

    |---------------------------|
    |    181          182       |
    |                           |
    |                           |
    |                           |
    |    1C1          1C2       |
    |                           |
    |                           |
    |                           |
    |    201          202       |
    |                           |
    |                           |
    |                           |
    |    241          242       |
    |                           |
    |                           |
    |                           |
    |    281          282       |
    |                           |
    |                           |
    |                           |
    |    2C1          2C2       |
    |                           |
    |                           |
    |   |---------|             |
    |   |---------|             |
    |      CNM1                 |
    |---------------------------|
    Notes:
          The A16B-1600-0280/02A board contains only 12 DIP32 sockets and some logic and
          bolts to the back of the BASE 1 board. The board extends the boot software memory
          storage space if additional EPROMS are required. Not all sockets are populated.
          Connector CNM1 joins with a 40 pin flat cable to the BASE 1 board connector CNM1
          IC locations are not printed on this board. Only the ROM # is printed at each
          socket location.

          ROM         IC         Memory     EPROM     Used For
          Label       Location   Location   Type
          ---------------------------------------------------------------------------------
          AB02281A    N/A        281        27C1001   Boot Software Version AB02 Revision A
          AB02282A    N/A        282        27C1001
          AB022C1A    N/A        2C1        27C1001
          AB022C2A    N/A        2C2        27C1001


    15A EPROM population and option type
    ----------------+---------+-------------------------------------------
    PCB NAME        |ROM #    | Option Type (Basic, Option A1/A2/A3 etc)
    ----------------|---------+-------------------------------------------
    BASE 1 main     |081, 082 | B
    BASE 1 main     |0C1, 0C2 | B
    BASE 1 main     |101, 102 | A1 if step1 software, B if step2 software
    BASE 1 main     |141, 142 | A2 (only used for foreign language option)
    BASE 1 daughter |181, 182 | A3 (if used)
    BASE 1 daughter |1C1, 1C2 | not used
    BASE 1 daughter |201, 202 | not used
    BASE 1 daughter |241, 242 | A6 (if used)
    BASE 1 daughter |281, 282 | B when subcpu used
    BASE 1 daughter |2C1, 2C2 | B when subcpu used
    --------------------------+-------------------------------------------
    Note: 2 MEG DRAM is required on BASE2 PCB when A3 or A6 is used

****************************************************************************/

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "cpu/m68000/m68020.h"
#include "cpu/i86/i286.h"


namespace {

class fanucs15_state : public driver_device
{
public:
	fanucs15_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")       // main 68020
		, m_pmccpu(*this, "pmccpu")         // sub 68000-12
		, m_gfxcpu(*this, "gfxcpu")         // gfx 68000-10
		, m_convcpu(*this, "convcpu")       // conversational 80286-8
	{ }

	void fanucs15(machine_config &config);

private:
	required_device<m68020_device> m_maincpu;
	required_device<m68000_device> m_pmccpu;
	required_device<m68000_device> m_gfxcpu;
	required_device<i80286_cpu_device> m_convcpu;

	void convcpu_mem(address_map &map) ATTR_COLD;
	void gfxcpu_mem(address_map &map) ATTR_COLD;
	void maincpu_mem(address_map &map) ATTR_COLD;
	void pmccpu_mem(address_map &map) ATTR_COLD;

	virtual void machine_reset() override ATTR_COLD;
};

void fanucs15_state::maincpu_mem(address_map &map)
{
	map(0x00000000, 0x0017ffff).rom().region("base1b", 0);
	map(0x000f8000, 0x000fffff).ram(); // filled with 0x96 on boot
	map(0xffff0000, 0xffffffff).ram(); // initial stack
}

void fanucs15_state::pmccpu_mem(address_map &map)
{
	map(0x000000, 0x03ffff).rom().region("base1a", 0);
	map(0xfde000, 0xffffff).ram();
}

void fanucs15_state::gfxcpu_mem(address_map &map)
{
	map(0x000000, 0x01ffff).rom().region("gfxboard", 0);
	map(0xfe0000, 0xfeffff).ram();
}

void fanucs15_state::convcpu_mem(address_map &map)
{
	map(0x000000, 0x03ffff).rom().region("conversational", 0x40000);
	map(0x040000, 0x07ffff).rom().region("conversational", 0);
	map(0x800000, 0x87ffff).ram();
	map(0xf80000, 0xffffff).rom().region("conversational", 0);
}

/* Input ports */
static INPUT_PORTS_START( fanucs15 )
INPUT_PORTS_END

void fanucs15_state::machine_reset()
{
}

void fanucs15_state::fanucs15(machine_config &config)
{
	/* basic machine hardware */
	M68020(config, m_maincpu, XTAL(12'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &fanucs15_state::maincpu_mem);
	m_maincpu->set_disable();

	M68000(config, m_pmccpu, XTAL(12'000'000));
	m_pmccpu->set_addrmap(AS_PROGRAM, &fanucs15_state::pmccpu_mem);
	m_pmccpu->set_disable();

	M68000(config, m_gfxcpu, XTAL(10'000'000));      // wants bit 15 of 70500 to be set
	m_gfxcpu->set_addrmap(AS_PROGRAM, &fanucs15_state::gfxcpu_mem);
	m_gfxcpu->set_disable();

	I80286(config, m_convcpu, XTAL(8'000'000));      // wants 70500 to return 0x8000 (same as what gfxcpu looks for, basically)
	m_convcpu->set_addrmap(AS_PROGRAM, &fanucs15_state::convcpu_mem);
}

/* ROM definition */
ROM_START( fanucs15 )
	ROM_REGION16_BE( 0x50000, "base1a", 0 ) // 68000 sub CPU code and data on base 1 board (verified)
	ROM_LOAD16_BYTE( "4040_001e_001.23m", 0x000001, 0x020000, CRC(2e12109f) SHA1(83ed846d3d59ab0d81b2e2e2231d1a444e462590) )
	ROM_LOAD16_BYTE( "4040_002e_002.27m", 0x000000, 0x020000, CRC(a5469692) SHA1(31c44edb36fb69d3d418a97e32e4a2769d1ec9e7) )
	ROM_LOAD16_BYTE( "9030_001e_381.18j", 0x040001, 0x008000, CRC(9f10a022) SHA1(dc4a242f7611143cc2d9564993fd5fa52f0ac13a) )

	ROM_REGION32_BE( 0x180000, "base1b", 0 )    // 68020 main CPU code and data on base 1 board (verified)
	ROM_LOAD16_BYTE( "ab02_081a_081.23e", 0x000001, 0x020000, CRC(5328b023) SHA1(661f2908f3287f7cd2b215cd29962f2789f7d99a) )
	ROM_LOAD16_BYTE( "ab02_082a_082.27e", 0x000000, 0x020000, CRC(ad37740f) SHA1(e65cc0a8b4e515fcf5fcefde99e95d100d310018) )
	ROM_LOAD16_BYTE( "ab02_0c1a_0c1.23f", 0x040001, 0x020000, CRC(62566569) SHA1(dd85b6e7875d996759b833552b00e1b3a0e3696b) )
	ROM_LOAD16_BYTE( "ab02_0c2a_0c2.27f", 0x040000, 0x020000, CRC(a4ade1fe) SHA1(44ef5358b34d3538fb061f235b8a14bac6b5faa8) )
	ROM_LOAD16_BYTE( "ab02_101a_101.23h", 0x080001, 0x020000, CRC(96f5d00e) SHA1(f5de3621df536435d27a0aac1c9d25e69601bd40) )
	ROM_LOAD16_BYTE( "ab02_102a_102.27h", 0x080000, 0x020000, CRC(e23a5414) SHA1(f6aff51dfd6d976b7cd33399c7aa3d06c7c06919) )
	ROM_LOAD16_BYTE( "ab02_141a_141.23k", 0x0c0001, 0x020000, CRC(3ceb6809) SHA1(7e37b18847b35f81c08b7b2ab62e99fa3a737c32) )
	ROM_LOAD16_BYTE( "ab02_142a_142.27k", 0x0c0000, 0x020000, CRC(1d8a4d7d) SHA1(580322e2927742bbcbf0bb2757730e2817b320e1) )
	// this appears to be a different version of the 081a/0c1a ROM program.  it's definitely for a 68020 (32-bit pointers everywhere)
	ROM_LOAD16_BYTE( "ab02_281a_281.8a",  0x100001, 0x020000, CRC(cec79742) SHA1(1233ff920d607206a80c8d187745e3d657a8635d) )
	ROM_LOAD16_BYTE( "ab02_282a_282.8d",  0x100000, 0x020000, CRC(63eacc0e) SHA1(1f25b99280112c720d778219b4610f556f33a7f1) )
	ROM_LOAD16_BYTE( "ab02_2c1a_2c1.10a", 0x140001, 0x020000, CRC(66eb74dd) SHA1(f256763cb15b4524c09bd09b88df46a1498846ef) )
	ROM_LOAD16_BYTE( "ab02_2c2a_2c2.10d", 0x140000, 0x020000, CRC(6edf4ff3) SHA1(9cbf7c6555cc27def3b580f5a7b0ff580984206d) )

	ROM_REGION16_LE( 0x80000, "conversational", 0 ) // 80286 ROMs, 5a00 for Mill
	ROM_LOAD16_BYTE( "5a00_041b.041", 0x000000, 0x020000, CRC(bf22c0a3) SHA1(56ab70bfd5794cb4db1d87c8becf7af522687564) )
	ROM_LOAD16_BYTE( "5a00_042b.042", 0x000001, 0x020000, CRC(7abc9d6b) SHA1(5cb6ad08ce93aa99391d1ce46ac8db8ba2e0f94a) )
	ROM_LOAD16_BYTE( "5a00_001b.001", 0x040000, 0x020000, CRC(cd2a2839) SHA1(bc20fd9ae9d071e1df835244aea85648d1bd1dbc) )
	ROM_LOAD16_BYTE( "5a00_002b.002", 0x040001, 0x020000, CRC(d9ebbb4a) SHA1(9c3d96e9b88848472210beacdf9d300ddd42d16e) )

	ROM_REGION16_BE( 0x50000, "gfxboard", 0 )   // graphics board 68000 code/data.  600a for lathe, 600b for mill, 6082 common to both
	ROM_LOAD16_BYTE( "600b_001d_low.3l",  0x000001, 0x010000, CRC(50566c5c) SHA1(966c8d90d09a9c50c5dedebe9c67f1755846b234) )
	ROM_LOAD16_BYTE( "600b_002d_high.3j", 0x000000, 0x010000, CRC(304e1ecb) SHA1(1e4b149b306550750fc03bd80bd399f239f68657) )
	ROM_LOAD16_BYTE( "600a_001c.bin",     0x020001, 0x010000, CRC(63d9fc2f) SHA1(280e825ba7b79e7c38282a4f4b762d2219fd873b) )
	ROM_LOAD16_BYTE( "600a_002c.bin",     0x020000, 0x010000, CRC(4d78e702) SHA1(a89bd07dc1ae030bdee5a541777825eaadbc2307) )
	// font or tilemap data?
	ROM_LOAD( "6082_001a_cg.12b", 0x040000, 0x010000, CRC(f3d10cf9) SHA1(bc5bc88dcb5f347e1442aa4a0897747958a53413) )

	ROM_REGION16_BE( 0x40000, "cassette", 0 )   // "PMC Cassette C", ROM cartridge that plugs into the Base 1 board
	ROM_LOAD16_BYTE( "pmc_high.a2",  0x000000, 0x020000, CRC(7b8f9a96) SHA1(08d828b612c45bb3f2f7a56df418cd8e34731bf4) )
	ROM_LOAD16_BYTE( "pmc_low.a1",   0x000001, 0x020000, CRC(3ab261f8) SHA1(20b7eef96deb91a3a867f9ac4165b0c188fbcff3) )
ROM_END

} // anonymous namespace


/* Driver */
//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY  FULLNAME     FLAGS
COMP( 1990, fanucs15, 0,      0,      fanucs15, fanucs15, fanucs15_state, empty_init, "Fanuc", "System 15", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



fanucspmg.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

Fanuc System P-Model G
Fanuc 1983

2014-03-22 Skeleton driver.
This is a machine from 1983 in a single case
with a lot of ports and a unique keyboard.

Also known as Fanuc P-G System, this is a dedicated 8085+8086+8087-based computer
system running software for CNC Programming.
The system boots up from on-board EPROM and shows a big ASCII-art boot screen
FANUC SYSTEM P and the ROM software version in the lower right corner.
To initiate booting from the floppy drive hold down the LOAD key for 3-5 seconds.
The system checks for a long LOAD key press so that it doesn't load software
if the LOAD key is accidentally pressed quickly while using the system, which would
erase everything in memory and all data up to that point and re-load the software from
scratch. When loading is activated application software is read from floppies.

The software are not point and click auto-generation type conversational CAD/CAM
applications. The earlier 'non-Symbolic' software requires knowledge of programming in APT
and other languages of the era. The 'Symbolic' software has menus and asks questions and
the blanks must be filled in correctly. The graphics are mostly made of lines but are
sufficient to complete the task easily. Efficient and effective use of this system
requires deep knowledge of CNC Machining techniques (Turning/Milling etc) and a
good understanding of machining processes and procedures. With correct usage this system
can be used to create CNC G-Code programs for any part that can be manufactured on a
CNC Machine. Because the system is made in Japan in the early 80's and the manuals are
very technical it also requires some skill in deciphering Japanese-English translated
technical texts to understand how to use it properly.

The box housing everything is 20" wide by 20" deep by 12" high and weighs
approximately 40 pounds. Power input is 85VAC to 110VAC. For the non-US and
non-Japanese markets a separate dedicated power supply is provided and is 12"
wide by 8" deep by 10" high and weighs approximately 20 pounds.

A number of optional peripherals can connect to it including a Fanuc Printer,
Fanuc PPR Unit (Paper tape Puncher/Reader with built-in printer), Fanuc Program
File (containing a 20MB HDD, two 8" floppy drives and two RS232 ports), Fanuc Cassette
Adapter, XY Plotter (A3 or A1), Fanuc Digitizing Tablet (A3 or A0) and Fanuc I/O Selector Box.

The P-G System has an internal 12" monitor and dual 5 1/4" floppy drives.
The first model had a 12" monochrome green monitor. In 1985 a color version was released.
In 1986 another model was released called the Mark II using dual 3 1/2" floppy drives.

The screen resolution is 512 x 384 pixels. It can display 64 characters x 24 lines.

The floppy format is custom. Floppies are double sided double density and
regular PC DSDD 360k floppies can be used after they are formatted using the
P-G System.
The floppy geometry is 40 tracks, 16 sectors per track, 256 bytes per sector
and 2 sides for a total storage capacity of 327680 bytes.
The floppy drives are typical PC-type 5 1/4" 360k drives and were manufactured
by Y-E DATA, model YD-580.

The 5 1/4" floppy disks can be backed-up and imaged using a DOS program called ImageDisk
which is available here.....
http://www.classiccmp.org/dunfield/img/index.htm
With a 5 1/4" HD floppy drive, in the GUI in settings change the number of
cylinders to 40, translate speed 300 -> 250 (to read a DD disk on a HD drive).
On the main menu press R to Read, type a file-name and press enter, press enter
again to skip the comment. Press enter again and it will read the disk and save
it to the HDD.

The following is a complete list of software titles available.
The info is taken from a glossy sales brochure printed in July 1985.
Other versions probably exist so this list is not final.
* denotes it is dumped. All other titles are not dumped and are needed.

Language Input -

                 Title           Part Number
                 --------------------------------
                 FAPT TURN       A08B-0033-J600#E
                 FAPT CUT        A08B-0033-J620#E
                 FAPT MILL       A08B-0033-J640#E
                 FAPT DIE-II     A08B-0033-J660#E
                 FAPT PUNCH-I    A08B-0033-J520#E
                 FAPT PUNCH-II   A08B-0033-J700#E
                 FAPT HELICAL    A08B-0033-J642#E
                 FAPT POST       A08B-0033-H642#E
                *FAPT POST       A08B-0031-H630   Edition C 85/1/31


Graphic Input -

                 Title                 Part Number
                 --------------------------------------
                *Symbolic FAPT TURN    A08B-0033-J800#E Edition B V02 L03 841116
                 Symbolic FAPT MILL    A08B-0033-J840#E
                 Symbolic FAPT DRILL   A08B-0033-J860#E
                 Symbolic FAPT CUT     A08B-0033-J820#E
                 FAPT DIGITIZER        A08B-0033-J510#E

+ Symbolic FAPT TURN was available in English, German, French, Dutch, Finnish,
and Swedish versions. Currently only the English version is archived.


Support System -

                 Title          Part Number
                 -------------------------------
                *FAPT TRACER    A08B-0033-H620#E  Edition B V02 L02 841108
                *FAPT TEACHER   A08B-0033-J610#E  Edition B L02 V01 841101
                *FAPT DOCTOR    A08B-0033-J600#E  Edition B V01 L03 841108


The software listed above with (*) have been tested on both mono and color versions and works fine.
Note: To initiate booting from the floppy drive hold down the LOAD key for 3-5 seconds.

The software for the Fanuc System P-Model G is extremely rare now and very difficult to find.
If you do have any of these wanted software titles or any manuals listed below and want to help
please contact me (Guru) via http://members.iinet.net.au/~lantra9jp1_nbn/gurudumps/comments.html

The following is a complete list of manuals available for the first edition of the
Fanuc System P-Model G released in 1983. The info is taken from a glossy sales brochure
printed in July 1985. There were other manuals released later for the Mark II and
updated manuals (each with a different part number).
The manuals were available in Japanese and English. The part numbers listed here
are English versions, denoted by the E at the end of the part number.
* denotes these manuals are secured and available in PDF format.

Description -

                 Title                        Part Number
                 ---------------------------------------
                 FAPT TURN/MILL Description   B-54102E
                 FAPT CUT Description         B-54103E
                 FAPT PUNCH-I Description     B-54104E
                 FAPT TRACER Description      B-54106E
                 FAPT DIGITIZER Description   B-54107E
                 FAPT DIE-II Description      B-54121E
                 Symbolic FAPT Description    B-54131E


Operator's Manual -

                 Title                                          Part Number
                 ----------------------------------------------------------
                *System P-Model G Operator's Manual             B-54111E/03
                 System P-Model G Mark II Operator's Manual     B-66014E
                *System P-Model G Operator's Manual Supplement  B-54112E/03-1
                 FAPT TURN/MILL Operator's Manual               B-54112E
                 FAPT CUT Operator's Manual                     B-54113E
                 FAPT PUNCH-I Operator's Manual                 B-54114E
                 FAPT PUNCH-II Operator's Manual                B-54115E
                *FAPT TRACER Operator's Manual                  B-54116E/03
                 FAPT DIGITIZER Operator's Manual               B-54117E
                *FAPT Universal POST Operator's Manual          B-54118E/02
                 FAPT DIE-II Operator's Manual (Volume 1)       B-54122E
                 FAPT DIE-II Operator's Manual (Volume 2)       B-54122E-1
                *FAPT TEACHER Operator's Manual                 B-54126E/01
                 220S FAPT MILL Operator's Manual               B-54127E
                *Symbolic FAPT TURN Operator's Manual           B-54132E/01
                 Symbolic FAPT MILL Operator's Manual           B-54134E
                 Symbolic FAPT CUT Operator's Manual            B-54136E
                 Symbolic FAPT DRILL Operator's Manual          B-54138E
                *Symbolic FAPT TURN Operator's Manual           B-66025E/01 (for System P Mark II)


Others -

                 Title                                     Part Number
                 -----------------------------------------------------
                *Symbolic FAPT TURN Operator's Handbook    B-53034E (for System P Model D)
                 FANUC CASSETTE Operator's Manual          B-53484E
                 FAPT DIE-II Part program examples         B-54123E
                 FAPT TURN/MILL/CUT Part program examples  B-54128E
                 Symbolic FAPT TURN Operator's Handbook    B-54133E
                 System P-Model G Operator's Handbook      B-54158E
                *System P-Model G Maintenance Manual       B-54159E/01
                *FANUC PPR Operator's Manual               B-54584E/01

Note the handbooks are pocket-sized 8" long by 3 1/2" wide and approximately 50 pages.


The unit has it's own dedicated keyboard with many special keys.
The keyboard clips to the main box and is the top cover when the main box is transported.
The keyboard layout is shown below.

|------------------------------------------------------------------------------|
|                                                                              |
| LOAD F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15      R0 R1 R2 R3  |
|                                                                              |
|                                                                              |
|          !  "  #  $  %  &  '  (  )     =                                     |
|  K0      1  2  3  4  5  6  7  8  9  0  -   ^   Y   DEL          7  8  9  +   | (Y is Japanese Yen sign)
|                                                                              |
|                                                                              |
|  K1   CAN Q  W  E  R  T  Y  U  I  O  P   @   [   NL   BS        4  5  6  -   | (NL means NEXT LINE, BS is backspace)
|                                                                              | (NL is equivalent to return or enter and)
|                                        +   *                                 | (forces the cursor to move to the next data entry point)
|  K2     UC  A  S  D  F  G  H  J  K  L  ;   :   ]   UC           1  2  3  x   | (UC is uppercase)
|                                                                              |
|                                   <  >   ?                                   |
|  K3      LC  Z  X  C  V  B  N  M  ,  .   /   -   LC             0  ,  .  /   | (LC is lowercase)
|                                                                              |
|                                                                              |
|                  _S__P__A__C__E__B__A__R_                         _N__L_     |
|                                                                              |
|------------------------------------------------------------------------------|

On the numeric keypad there are directional arrows on numbers 1 2 3 4 6 7 8 9
1 3 7 9 have arrows pointing South West, South East, North West, North East.
2 4 6 8 have arrows pointing down, left, right and up.
5 is the center and has no additional markings on it.
Number 0 has an anti-clockwise 180 degrees arc with an arrow at the end and . has
a clockwise 180 degrees arc with an arrow at the end.
These keys are the 'Symbolic' keys.

The F-keys and R-keys are programmed by the software that is running on the system.
The F-keys act like SPDT switches and can be toggled either off or on.
When they are on, a LED in the center of the key lights.
For Symbolic FAPT TURN these keys are pre-programmed as follows.....

F0 - ON:  Sets the backwards direction when using the R1 key.
     OFF: Sets the forwards direction when using the R1 key. Default is OFF.
F1 - ON:  Makes the whole screen the graphic area.
     OFF: Auto-calc the graphic area so the graphic does not overlap the text. Default is OFF.
F2 - ON:  Shows the parts figure (graphics). Default is ON.
     OFF: Does not show the parts figure.
F3 - ON:  Display the NC G-Code data on screen. Default is ON.
     OFF: Does not display the NC G-Code data on screen.
F4 - ON:  Printer ON.
     OFF: Printer OFF. The printer can be switched on or off any time. When enabled
          everything displayed on the screen will also print on the printer. Default is OFF.
F5 - ON:  Stops execution of the NC G-Code data before each process begins. Keyboard input additions
          can also be done at this time. To continue press NL.
     OFF: Program execution continues to the end. Default is OFF.
F6 - ON:  Outputs the NC G-Code data to a separately selected medium (floppy/cassette or paper tape)
     OFF: No output to additional medium. Default is OFF.
F7 - ON:  Stops each time a line of NC G-Code data is output. This is equivalent to Single Block on a CNC Machine.
          To continue press NL.
     OFF: Program execution continues to the end. Default is OFF.
F8 -
F9 -
F10- ON:  Sends the part figure graphic and NC G-Code data to the XY plotter
     OFF: No output to XY plotter
F11-
F12-
F13-
F14-
F15-

The function of the R-keys changes depending on the application and the menu shown on the screen.
The R-keys are used for tasks within the current screen so the function of the R-keys is always
displayed on screen at all times.
The initial Symbolic FAPT TURN settings for the R-keys are....
R0 - FAPT Execution
R1 - Family Program
R2 - Setting
R3 - Auxiliary Work


Box Layout (top view)
----------

A08B-0033-B001 (Color Version from 1985)
A08B-0031-B001 (Mono Version from 1983)
A08B-0031-B002 (Mono Version from 1984)
|--------------------------------------------|
| ------------MAIN PCB---------------------- |
|   -----------SUB-PCB-----------         |  |
|                                         |  |
| |-----------------------|               |  |
| |                       |               |  |
| |   FANUC 12" COLOR     |               P  |
| |       CRT UNIT        |               O  |
| |    A61L-0001-0078     |               W  |
| |                       |               E  |
| |                       |  |---------|  R  |
| |                       |  |FANUC    |  |  |
| |          OR           |  |FDD UNIT |  P  |
| |                       |  |A87l-0001|  C  |
| |                       |  |-0026    |  B  |
| |   FANUC 12" MONO      |  |         |  |  |
| |       CRT UNIT        |  |5 1/4"   |  |  |
| |   A61L-0001-0073      |  |FLOPPY   |     |
| |                       |  |DRIVES   |     |
| |                       |  |x2       |     |
| |-----------------------|  |---------|     |
|--------------------------------------------|
Notes:
      The CRT tube in the color version is a Matsushita 320DHB22. Input voltage is 110V AC
      The CRT tube in the mono version is a Hitachi 310KEB31. Input voltage is 24V DC and B+ is 11.0V
      The mono version does not have a SUB PCB
      The power PCB is identical for both color and mono versions


Main PCB Layout (for color version)
---------------

A20B-1000-0710/03B
|-------------------------------------------|
| CNF CNE     CND  CNC        CNB      CNA  |
|   VR1     ^                               |
|                JUMPERS           XXXXXXXXX|
|       % MB15541         XXXXXXXXXXXXXXXXXX|
|                         XXXXXXXXXXXXXXXXXX|
| 8087-3           D8253  XXXXXXXXXXXXXXXXXX|
|   8086-2   D765  D8253           XXXXXXXXX|
|             D8257                         |
|15MHz   D8259 D8259     D8251 D8251        |
|D8284  040_001A.13A     D8251 D8251        |
|       040_002A.15A  VR2         CN2   CN1 |
|       CN7     CN6      CN5      CN4   CN3 |
|-------------------------------------------|
Notes:
      D8086   - Intel 8086-2 CPU. Clock input 5.000MHz [15/3]
      D8087   - Intel 8087-3 x87 Floating-Point Co-Processor. Clock input 5.000MHz [15/3]
      XXXXXXX - Fujitsu MB8265-15 65536 x1-bit DRAM (72 chips total)
      MB15541 - Fujitsu MB15541 Custom Chip
      D765    - NEC D765 Single/Double Density Floppy-Disk Controller. Clock input 4.000MHz [16/4]
      D8251   - Intel D8251 Programmable Communications Interface (USART)
      D8253   - NEC D8253 Programmable Interval Timer. Clock input 1.25MHz [15/12]
      D8257   - NEC D8257 Programmable DMA Controller. Clock input 3.000MHz [15/5]
      D8259   - NEC D8259 Programmable Interrupt Controller
      D8284   - Intel D8284 Clock Generator and Driver for 8086/8088 Processors
      A40_00* - Fujitsu MBM2764 8k x8-bit EPROM
      VR1     - Potentiometer to adjust pulse width of floppy disk control unit
      VR2     - Potentiometer to adjust screen brightness
      ^       - 3 chips marked Y-E Data Fujitsu
                MB4393
                MB14324
                MB14323
      %       - Unknown 20-pin Ceramic DIP chip with heat-sink (likely to be Intel 8288 Bus Controller)
      CNA     - 50-pin flat cable joining to Sub PCB
      CNB     - 50-pin flat cable joining to Sub PCB
      CNC     - 6-pin power cable joining to Sub PCB
      CND     - 34-pin flat cable joining to FDD Unit
      CNE     - Fanuc Honda MR-50 50-pin female connector for factory testing (not used)
      CNF     - Power input connector
      CN1     - 25-pin Female D-type connector. (for RS232 external peripherals \  CNC Machine,
      CN2     - 25-pin Female D-type connector. (for RS232 external peripherals  | PPR Unit, X-Y Plotter,
      CN3     - 25-pin Female D-type connector. (for RS232 external peripherals  | Tablet,
      CN4     - 25-pin Female D-type connector. (for RS232 external peripherals /  Cassette Adapter etc (connections in any order)
      CN5     - Fanuc Honda MR-50 50-pin female connector (probably for external connection of the Fanuc Program File Unit)
      CN6     - Fanuc Honda MR-20 20-pin female connector for the keyboard
      CN7     - Fanuc Honda MR-20 20-pin male connector. Specification says 'not used'. Video signals are present on
                the connector so it is probably used for an external monitor
      JUMPERS - 15 2-pin jumpers labelled S1 to S15. S2, S3 & S4 are open and the others are shorted


Sub PCB Layout (for color version)
--------------

A20B-1000-0720/02B
|--------------------------------|
| CNA     CNB      CNC   CND     |
|                                |
|                  MB15542    CNE|
|                                |
|                  HD6845S  D8085|
|            16MHz               |
|                                |
|   X                            |
| XXXXXXXX                       |
| XXXXXXXX                       |
| XXXXXXXX      6264 A41_010B.28B|
|               6264 A41_020A.30B|
|--------------------------------|
Notes:
      D8085   - NEC D8085A-2 CPU. Clock input 8.000MHz [16/2]
                Note 8085 has internal /2 divider so actual clock speed is 4.000MHz
      HD6845S - Hitachi HD6845S / HD46505S CRT Controller. Clock input 2.000MHz [16/8]
      6264    - Hitachi HM6264P-15 8k x 8-bit SRAM
      XXXXXXX - Fujitsu MB8265-15 65536 x1-bit DRAM (25 chips total)
      MB15542 - Fujitsu MB15542 Custom Chip
      A41_010B- Intel D27128 16k x8-bit EPROM
      A42_020A- Hitachi 27256G 32k x8-bit EPROM
      CNA     - 50-pin flat cable joining to Main PCB
      CNB     - 50-pin flat cable joining to Main PCB
      CNC     - 6-pin power cable joining to Main PCB
      CND     - 20-pin flat cable joining to CRT Unit (video output)
      CNE     - Fanuc Honda MR-50 50-pin male connector for factory testing (not used)
      HSync   - 22.7273kHz
      VSync   - 54.6330Hz


Main PCB Layout (for mono version)
---------------

A20B-1000-0140/09F
|-------------------------------------------|
| CN10          CN9  VR1   CN8              |
|  6116         S3 ^                        |
| A22_020B.5G             %  D765  XXXXXXXXX|
|   16MHz   MB15542 MB15541  D8257 XXXXXXXXX|
|             S4 15MHz D8284       XXXXXXXXX|
| 8085-2    HD6845S  8087 D8259    XXXXXXXXX|
| CN12  A21_010F.17D 8086 D8253 A25_001A.33E|
|              CN11  S2    A25_002A.35E     |
|                    S1 D8251 D8251   D39   |
|                       D8251 D8251 S5 S6 S7|
|    YYYYYYYYY       VR2   D8253  CN2   CN1 |
|       CN7     CN6      CN5      CN4   CN3 |
|-------------------------------------------|
Notes:
      D8086   - Intel 8086-2 CPU. Clock input 5.000MHz [15/3]
      D8087   - Intel 8087-3 x87 Floating-Point Co-Processor. Clock input 5.000MHz [15/3]
      D8085   - NEC D8085A-2 CPU. Clock input 8.000MHz [16/2]
                Note 8085 has internal /2 divider so actual clock speed is 4.000MHz
      HD6845S - Hitachi HD6845S / HD46505S CRT Controller. Clock input 2.000MHz [16/8]
      XXXXXXX - Fujitsu MB8265-15 65536 x1-bit DRAM (36 chips total)
      YYYYYYY - Fujitsu MB8265-15 65536 x1-bit DRAM (9 chips total)
      6116    - Hitachi HM6116P-3 2k x 8-bit SRAM
      MB15541 - Fujitsu MB15541 Custom Chip
      MB15542 - Fujitsu MB15542 Custom Chip
      D765    - NEC D765 Single/Double Density Floppy-Disk Controller. Clock input 4.000MHz [16/4]
      D8251   - Intel D8251 Programmable Communications Interface (USART)
      D8253   - NEC D8253 Programmable Interval Timer. Clock input 1.25MHz [15/12]
      D8257   - NEC D8257 Programmable DMA Controller. Clock input 3.000MHz [15/5]
      D8259   - NEC D8259 Programmable Interrupt Controller
      D8284   - Intel D8284 Clock Generator and Driver for 8086/8088 Processors
      A2*     - Hitachi HN482764G 8k x8-bit EPROM
      VR1     - Potentiometer to adjust pulse width of floppy disk control unit
      VR2     - Potentiometer to adjust screen brightness
      ^       - 3 chips marked Y-E Data Fujitsu
                MB4393
                MB14324
                MB14323
      %       - Unknown 20-pin Ceramic DIP chip with heat-sink (likely to be Intel 8288 Bus Controller)
      CN1     - 25-pin Female D-type connector. (for RS232 external peripherals \  CNC Machine,
      CN2     - 25-pin Female D-type connector. (for RS232 external peripherals  | PPR Unit, X-Y Plotter,
      CN3     - 25-pin Female D-type connector. (for RS232 external peripherals  | Tablet,
      CN4     - 25-pin Female D-type connector. (for RS232 external peripherals /  Cassette Adapter etc (connections in any order)
      CN5     - Fanuc Honda MR-50 50-pin female connector (probably for external connection of the Fanuc Program File Unit)
      CN6     - Fanuc Honda MR-20 20-pin female connector for the keyboard
      CN7     - Fanuc Honda MR-20 20-pin male. Specification says 'not used' and no signals are present on the connector
      CN8     - 20-pin flat cable joining to CRT Unit (video output)
      CN9     - 34-pin flat cable joining to FDD Unit
      CN10    - Power input connector
      CN11/12 - Fanuc Honda MR-50 50-pin female connector for factory testing (not used)
      Sx      - 7 2-pin jumpers (S1 to S7). S2, S3 & S4 are open and the others are shorted
      D39     - Bank of 8 2-pin jumpers vertically orientated. 2 and 7 are shorted and the others are open


Block Diagram
-------------
Below is the block diagram shown in the System P-Model G Maintenance Manual, relating to the mono version.
The arrows denote direction of data flow.

          |-------|                                             |--------|      |----------------|
          |Sub CPU|                                             |Main CPU|<---->|Math Coprocessor|
          |-------|                                             |--------|      |----------------|
              /\                                                     /\                 /\
              |                                                      |                  |
              |                                                      \/                 \/
  |-----|     |                                                      |------------------|
  |EPROM|<--->|                                                               /\
  |-----|     |          |---------------|                                    |     |----------------|
              |          | Common memory |<---------------------------------->|<--->|RS232C interface|---CN1
              |<-------->|===============|                                    |     |----------------|
              |      /-->| Graphic memory|------|                             |
              |      |   |---------------|      |                             |     |----------------|
              |      |                          |         |--------|          |<--->|RS232C interface|---CN2
              |      |                          |         | BOOT   |<-------->|     |----------------|
              |      |                          |         | EPROM  |          |
              |      |   |----------------|     |         |--------|          |     |----------------|
              |<-----|-->|Character memory|--|  |                             |<--->|RS232C interface|---CN3
              |      |-->|----------------|  |  |         |--------|          |     |----------------|
              |      |                       |  |         |Main RAM|<-------->|
              |      |                       |  |         |--------|          |     |----------------|
              |      |                       |  |                             |<--->|RS232C interface|---CN4
              |      |                       |  |                             |     |----------------|
|---------|   |      |                       \/ \/                            |
|Keyboard |   \/     \---|---------------------------|                        |     |-----------------|  CN9  |--------|
|interface|<->|<-------->|    CRT control circuit    |                        |<--->|Floppy controller|---O---|FDD UNIT|
|----|----|              |-------------|-------------|                        |     |-----------------|       |--------|
     |                                 |                                      |
     |                                 |                                      \/
     O CN6                             O CN8                                  O CN5
     |                                 |
     |                                 |
 |---|----|                       |----|----|
 |Keyboard|                       | Screen  |
 |--------|                       |---------|

  TODO:
    - Is the VRAM hookup anything like correct?
    - Hookup enough keyboard to get it to boot a floppy, the FAPT DOCTOR
      program will be invaluable to answering many questions.
    - Shared RAM is 8k, but there are 2 6264s on the sub board.  Is shared RAM
       banked?

    To boot a floppy put "bp fc5fa,1,{ip=c682;g}" and "bp fc6d7,1,{ip=c755;g}"
    into the debugger.

    At NMI: f8008 must have bit 7 clear and bit 6 set (e008 on 8085)
            f8009 must not equal 0x01 (e009 on 8085)

            8085 sets f8008 to keyboard row 0 AND 0xf3
             "     "  f8009 to keyboard row 1


Keyboard Matrix (preliminary)
---------------
Row select
|         Columns Key by bit
|         D0   D1   D2   D3   D4   D5   D6   D7
V         V    V    V    V    V    V    V    V
0x??      F0   F1   F2   F3   F4   F5   F6   F7
0x??      F8   F9   F10  F11  F12  F13  F14  F15
0x30      0    1    2    3    4    5    6    7
0x38      8    9    :    ;    <    -    >    ?
0x40      @    A    B    C    D    E    F    G
0x48      H    I    J    K    L    M    N    O
0x50      P    Q    R    S    T    U    V    W
0x58      X    Y    Z    [    Yen  ]    ^    _
0x??      KP0  KP1  KP2  KP3  KP4  KP5  KP6  KP7
0x??      KP8  KP9  KP.  KP,  N/A  N/A  N/A  SPACE
0x??      K+   K-   K*   K/   N/A  N/A  N/A  DEL
0x??      BS  (K)NL CAN  N/A  N/A  N/A  N/A  N/A
0x??      K0   K1   K2   K3   N/A  N/A  N/A  N/A

The following keys I have no idea where they map as they don't show a consistent column bit in the diagram:
LOAD UC LC R0 R1 R2 R3
Also any keys which are N/A may actually have something else mapped there.

In short: this keyboard seems to follow some sort of ASCII-derived row/column pattern
If this is a true ascii keyboard, then UC, LC probably do not connect to the matrix at all,
but instead make it so rows 0x40, 0x48 0x50 and 0x58 produce characters from ascii rows 0x60, 0x68, 0x70 and 0x78 (uppercase becomes lowercase)
likewise 0x30 and 0x38 will produce chars from 0x20 and 0x28 (numbers become symbols)
the keypad symbols seem to use a different matrix pattern from the rest?

****************************************************************************/

#include "emu.h"

#include "cpu/i8085/i8085.h"
#include "cpu/i86/i86.h"
#include "imagedev/floppy.h"
#include "machine/i8251.h"
#include "machine/i8257.h"
#include "machine/i8087.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/ram.h"
#include "machine/upd765.h"
#include "machine/upd765.h"
#include "video/mc6845.h"

#include "screen.h"


namespace {

#define MAINCPU_TAG "maincpu"
#define SUBCPU_TAG  "subcpu"
#define DMAC_TAG    "dmac"
#define CRTC_TAG    "crtc"
#define FDC_TAG     "fdc"
#define SCREEN_TAG  "screen"
#define SHARED_TAG  "shared"
#define CHARGEN_TAG "chargen"

class fanucspmg_state : public driver_device
{
public:
	fanucspmg_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, MAINCPU_TAG)
		, m_subcpu(*this, SUBCPU_TAG)
		, m_usart(*this, "usart%u", 0U)
		, m_pit(*this, "pit%u", 0U)
		, m_pic(*this, "pic%u", 0U)
		, m_dmac(*this, DMAC_TAG)
		, m_crtc(*this, CRTC_TAG)
		, m_fdc(*this, FDC_TAG)
		, m_shared(*this, SHARED_TAG)
		, m_chargen(*this, CHARGEN_TAG)
	{ }

	void fanucspmgm(machine_config &config);
	void fanucspmg(machine_config &config);

	void init_fanucspmg();

private:
	required_device<i8086_cpu_device> m_maincpu;
	required_device<i8085a_cpu_device> m_subcpu;
	required_device_array<i8251_device, 4> m_usart;
	required_device_array<pit8253_device, 2> m_pit;
	required_device_array<pic8259_device, 2> m_pic;
	required_device<i8257_device> m_dmac;
	required_device<mc6845_device> m_crtc;
	required_device<upd765a_device> m_fdc;
	required_shared_ptr<uint8_t> m_shared;
	required_memory_region m_chargen;

	uint8_t memory_read_byte(offs_t offset);
	void memory_write_byte(offs_t offset, uint8_t data);
	uint8_t shared_r(offs_t offset);
	void shared_w(offs_t offset, uint8_t data);
	uint8_t vram1_r(offs_t offset);
	void vram1_w(offs_t offset, uint8_t data);
	uint8_t vram2_r(offs_t offset);
	void vram2_w(offs_t offset, uint8_t data);
	void vram_bank_w(uint8_t data);
	uint8_t vblank_ack_r();
	void vbl_ctrl_w(uint8_t data);
	void keyboard_row_w(uint8_t data);
	uint8_t keyboard_r();
	void video_ctrl_w(uint8_t data);
	uint8_t fdcdma_r();
	void fdcdma_w(uint8_t data);
	uint8_t get_slave_ack(offs_t offset);
	void dma_page_w(uint8_t data);

	uint16_t magic_r();

	void vsync_w(int state);
	void tc_w(int state);
	void hrq_w(int state);

	MC6845_UPDATE_ROW(crtc_update_row);
	MC6845_UPDATE_ROW(crtc_update_row_mono);

	uint8_t m_vram[24576];
	uint8_t m_video_ctrl;

	void maincpu_io(address_map &map) ATTR_COLD;
	void maincpu_mem(address_map &map) ATTR_COLD;
	void subcpu_mem(address_map &map) ATTR_COLD;

	virtual void machine_reset() override ATTR_COLD;
	int32_t m_vram_bank;
	uint8_t m_vbl_ctrl;
	uint8_t m_keyboard_row;
	uint8_t m_vbl_stat;
	uint8_t m_dma_page;
};

void fanucspmg_state::init_fanucspmg()
{
	memset(m_vram, 0, sizeof(m_vram));

	save_item(NAME(m_vram));
	save_item(NAME(m_vram_bank));
	save_item(NAME(m_vbl_ctrl));
	save_item(NAME(m_keyboard_row));
	save_item(NAME(m_video_ctrl));
}

uint8_t fanucspmg_state::shared_r(offs_t offset)
{
	return m_shared[offset];
}

void fanucspmg_state::shared_w(offs_t offset, uint8_t data)
{
	m_shared[offset] = data;
}

uint8_t fanucspmg_state::get_slave_ack(offs_t offset)
{
	if(offset == 7)
		return m_pic[1]->acknowledge();

	return 0x00;
}

void fanucspmg_state::tc_w(int state)
{
	m_fdc->tc_w(state);
}

void fanucspmg_state::hrq_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state);
	m_dmac->hlda_w(state);
}

uint8_t fanucspmg_state::fdcdma_r()
{
	return m_fdc->dma_r();
}

void fanucspmg_state::fdcdma_w(uint8_t data)
{
	m_fdc->dma_w(data);
}

void fanucspmg_state::dma_page_w(uint8_t data)
{
	floppy_image_device *floppy0 = m_fdc->subdevice<floppy_connector>("0")->get_device();
	floppy_image_device *floppy1 = m_fdc->subdevice<floppy_connector>("1")->get_device();
	// verify
	floppy0->mon_w(!(data & 2));
	floppy1->mon_w(!(data & 2));

	m_dma_page = (data >> 2) & 0xf;
}

uint16_t fanucspmg_state::magic_r()
{
	return 0x0041;  // 31 = memory error
}

void fanucspmg_state::maincpu_mem(address_map &map)
{
	map(0x00000, 0x7ffff).ram();   // main RAM

	map(0x80000, 0x81fff).ram();   // believed to be shared RAM with a CPU inside the Program File
	map(0x88000, 0x88001).noprw();   // Program File "ready" bit

	map(0xf0000, 0xf0003).rw(m_pic[0], FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0xf0004, 0xf0007).m(m_fdc, FUNC(upd765a_device::map)).umask16(0x00ff);
	map(0xf0008, 0xf000f).rw(m_pit[0], FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0xf0010, 0xf0013).rw(m_usart[0], FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0xf0014, 0xf0017).rw(m_usart[1], FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0xf0018, 0xf001b).rw(m_usart[2], FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0xf001c, 0xf001f).rw(m_usart[3], FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0xf0020, 0xf0029).rw(m_dmac, FUNC(i8257_device::read), FUNC(i8257_device::write));
	map(0xf0042, 0xf0043).r(FUNC(fanucspmg_state::magic_r));
	map(0xf0046, 0xf0046).w(FUNC(fanucspmg_state::dma_page_w));
	map(0xf0048, 0xf004f).rw(m_pit[1], FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0xf2000, 0xf2003).rw(m_pic[1], FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);

	map(0xf8000, 0xf9fff).rw(FUNC(fanucspmg_state::shared_r), FUNC(fanucspmg_state::shared_w));
	map(0xfc000, 0xfffff).rom().region(MAINCPU_TAG, 0);
}

void fanucspmg_state::maincpu_io(address_map &map)
{
}

void fanucspmg_state::vsync_w(int state)
{
	if ((m_vbl_ctrl & 0x08) == 0x08)
	{
		if (state == ASSERT_LINE)
		{
			m_subcpu->set_input_line(I8085_RST75_LINE, ASSERT_LINE);
		}
	}

	m_vbl_stat = (state == ASSERT_LINE) ? 1 : 0;
}

uint8_t fanucspmg_state::vram1_r(offs_t offset)
{
	return m_vram[m_vram_bank + offset];
}

void fanucspmg_state::vram1_w(offs_t offset, uint8_t data)
{
	m_vram[m_vram_bank + offset] = data;
}

uint8_t fanucspmg_state::vram2_r(offs_t offset)
{
	return m_vram[m_vram_bank + offset + 0x600];
}

void fanucspmg_state::vram2_w(offs_t offset, uint8_t data)
{
	m_vram[m_vram_bank + offset + 0x600] = data;
}

void fanucspmg_state::vram_bank_w(uint8_t data)
{
	m_vram_bank = (data & 7) * 0xc00;
}

uint8_t fanucspmg_state::vblank_ack_r()
{
	m_subcpu->set_input_line(I8085_RST75_LINE, CLEAR_LINE);

	return 0xff;
}

// bit 1 is unknown
// bit 3 appears to enable vblank IRQs
void fanucspmg_state::vbl_ctrl_w(uint8_t data)
{
	m_vbl_ctrl = data;
}

// row 2: raising a bit toggles the corresponding bit at 500a
// row 3: raising a bit toggles the corresponding bit at 500b
void fanucspmg_state::keyboard_row_w(uint8_t data)
{
	m_keyboard_row = data;
}

uint8_t fanucspmg_state::keyboard_r()
{
	return 0;
}

// bit 0 is set when clearing VRAM
// bit 1 is display enable
void fanucspmg_state::video_ctrl_w(uint8_t data)
{
	m_video_ctrl = data;
}

void fanucspmg_state::subcpu_mem(address_map &map)
{
	map(0x0000, 0x3fff).rom().region(SUBCPU_TAG, 0);

	map(0x4000, 0x45ff).rw(FUNC(fanucspmg_state::vram1_r), FUNC(fanucspmg_state::vram1_w));
	map(0x4800, 0x4dff).rw(FUNC(fanucspmg_state::vram2_r), FUNC(fanucspmg_state::vram2_w));

	map(0x5000, 0x5000).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x5001, 0x5001).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x5008, 0x5008).w(FUNC(fanucspmg_state::keyboard_row_w));
	map(0x5009, 0x5009).r(FUNC(fanucspmg_state::keyboard_r));
	map(0x500a, 0x500b).nopw();    // keyboard rows 2 and 3 control what's written here.  dip switches?
	map(0x500c, 0x500c).w(FUNC(fanucspmg_state::vbl_ctrl_w));
	map(0x500d, 0x500d).w(FUNC(fanucspmg_state::vram_bank_w));
	map(0x500e, 0x500e).r(FUNC(fanucspmg_state::vblank_ack_r));
	map(0x5018, 0x5018).w(FUNC(fanucspmg_state::video_ctrl_w));

	map(0xe000, 0xffff).ram().share(SHARED_TAG); // shared RAM
}

/* Input ports */
static INPUT_PORTS_START( fanucspmg )
INPUT_PORTS_END

void fanucspmg_state::machine_reset()
{
	m_vbl_ctrl = 0;
	m_vram_bank = 0;
	m_video_ctrl = 0;
	m_dma_page = 0;
}

uint8_t fanucspmg_state::memory_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	return prog_space.read_byte(offset | (m_dma_page << 16));
}

void fanucspmg_state::memory_write_byte(offs_t offset, uint8_t data)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	return prog_space.write_byte(offset | (m_dma_page << 16), data);
}

MC6845_UPDATE_ROW( fanucspmg_state::crtc_update_row )
{
	uint32_t *p = &bitmap.pix(y);
	uint8_t const *const chargen = m_chargen->base();

	for ( int i = 0; i < x_count; i++ )
	{
		uint16_t offset = ( ma + i );

		if (m_video_ctrl & 0x02)
		{
			if (offset <= 0x5ff)
			{
				uint8_t chr = m_vram[offset + 0x600];
				uint8_t attr = m_vram[offset];
				uint8_t data = chargen[ chr + (ra * 256) ];
				uint32_t fg = 0;
				uint32_t bg = 0;

				if (attr & 0x20) fg |= 0xff0000;
				if (attr & 0x40) fg |= 0x00ff00;
				if (attr & 0x80) fg |= 0x0000ff;

				*p++ = BIT(data, 0) ? fg : bg;
				*p++ = BIT(data, 1) ? fg : bg;
				*p++ = BIT(data, 2) ? fg : bg;
				*p++ = BIT(data, 3) ? fg : bg;
				*p++ = BIT(data, 4) ? fg : bg;
				*p++ = BIT(data, 5) ? fg : bg;
				*p++ = BIT(data, 6) ? fg : bg;
				*p++ = BIT(data, 7) ? fg : bg;
			}
		}
		else
		{
			*p++ = 0;
			*p++ = 0;
			*p++ = 0;
			*p++ = 0;
			*p++ = 0;
			*p++ = 0;
			*p++ = 0;
			*p++ = 0;
		}
	}
}

MC6845_UPDATE_ROW( fanucspmg_state::crtc_update_row_mono )
{
	uint32_t *p = &bitmap.pix(y);
	uint8_t const *const chargen = m_chargen->base();

	for ( int i = 0; i < x_count; i++ )
	{
		uint16_t offset = ( ma + i );

		if (m_video_ctrl & 0x02)
		{
			if (offset <= 0x5ff)
			{
				uint8_t chr = m_vram[offset + 0x600];
//              uint8_t attr = m_vram[offset];
				uint8_t data = chargen[ chr + (ra * 256) ];
				uint32_t fg = 0xff00;
				uint32_t bg = 0;

				*p++ = BIT(data, 0) ? fg : bg;
				*p++ = BIT(data, 1) ? fg : bg;
				*p++ = BIT(data, 2) ? fg : bg;
				*p++ = BIT(data, 3) ? fg : bg;
				*p++ = BIT(data, 4) ? fg : bg;
				*p++ = BIT(data, 5) ? fg : bg;
				*p++ = BIT(data, 6) ? fg : bg;
				*p++ = BIT(data, 7) ? fg : bg;
			}
		}
		else
		{
			*p++ = 0;
			*p++ = 0;
			*p++ = 0;
			*p++ = 0;
			*p++ = 0;
			*p++ = 0;
			*p++ = 0;
			*p++ = 0;
		}
	}
}

static void fanuc_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

void fanucspmg_state::fanucspmg(machine_config &config)
{
	/* basic machine hardware */
	I8086(config, m_maincpu, XTAL(15'000'000)/3);
	m_maincpu->set_addrmap(AS_PROGRAM, &fanucspmg_state::maincpu_mem);
	m_maincpu->set_addrmap(AS_IO, &fanucspmg_state::maincpu_io);
	m_maincpu->set_irq_acknowledge_callback("pic0", FUNC(pic8259_device::inta_cb));
	m_maincpu->esc_opcode_handler().set("i8087", FUNC(i8087_device::insn_w));
	m_maincpu->esc_data_handler().set("i8087", FUNC(i8087_device::addr_w));

	i8087_device &i8087(I8087(config, "i8087", XTAL(15'000'000)/3));
	i8087.set_space_86(m_maincpu, AS_PROGRAM);
	//i8087.irq().set_inputline("maincpu", INPUT_LINE_NMI);  // TODO: presumably this is connected to the pic
	i8087.busy().set_inputline("maincpu", INPUT_LINE_TEST);

	I8085A(config, m_subcpu, XTAL(16'000'000)/2/2);
	m_subcpu->set_addrmap(AS_PROGRAM, &fanucspmg_state::subcpu_mem);

	I8251(config, m_usart[0], 0);
	I8251(config, m_usart[1], 0);
	I8251(config, m_usart[2], 0);
	I8251(config, m_usart[3], 0);

	PIT8253(config, m_pit[0], 0);
	m_pit[0]->set_clk<0>(XTAL(15'000'000)/12);
	m_pit[0]->set_clk<1>(XTAL(15'000'000)/12);
	m_pit[0]->set_clk<2>(XTAL(15'000'000)/12);

	PIT8253(config, m_pit[1], 0);
	m_pit[1]->set_clk<0>(XTAL(15'000'000)/12);
	m_pit[1]->set_clk<1>(XTAL(15'000'000)/12);
	m_pit[1]->set_clk<2>(XTAL(15'000'000)/12);

	I8257(config, m_dmac, XTAL(15'000'000) / 5);
	m_dmac->out_hrq_cb().set(FUNC(fanucspmg_state::hrq_w));
	m_dmac->out_tc_cb().set(FUNC(fanucspmg_state::tc_w));
	m_dmac->in_memr_cb().set(FUNC(fanucspmg_state::memory_read_byte));
	m_dmac->out_memw_cb().set(FUNC(fanucspmg_state::memory_write_byte));
	m_dmac->in_ior_cb<0>().set(FUNC(fanucspmg_state::fdcdma_r));
	m_dmac->out_iow_cb<0>().set(FUNC(fanucspmg_state::fdcdma_w));

	PIC8259(config, m_pic[0], 0);
	m_pic[0]->out_int_callback().set_inputline(m_maincpu, 0);
	m_pic[0]->in_sp_callback().set_constant(1);
	m_pic[0]->read_slave_ack_callback().set(FUNC(fanucspmg_state::get_slave_ack));

	PIC8259(config, m_pic[1], 0);
	m_pic[1]->out_int_callback().set(m_pic[0], FUNC(pic8259_device::ir7_w));
	m_pic[1]->in_sp_callback().set_constant(0);

	UPD765A(config, m_fdc, 8'000'000, true, true);
	m_fdc->intrq_wr_callback().set(m_pic[0], FUNC(pic8259_device::ir3_w));
	m_fdc->drq_wr_callback().set(m_dmac, FUNC(i8257_device::dreq0_w));
	FLOPPY_CONNECTOR(config, FDC_TAG":0", fanuc_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, FDC_TAG":1", fanuc_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);

	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_raw(XTAL(15'000'000), 640, 0, 512, 390, 0, 384);
	screen.set_screen_update(CRTC_TAG, FUNC(mc6845_device::screen_update));

	HD6845S(config, m_crtc, XTAL(8'000'000)/2);
	m_crtc->set_screen(SCREEN_TAG);
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(fanucspmg_state::crtc_update_row));
	m_crtc->out_vsync_callback().set(FUNC(fanucspmg_state::vsync_w));
}

void fanucspmg_state::fanucspmgm(machine_config &config)
{
	fanucspmg(config);

	m_crtc->set_update_row_callback(FUNC(fanucspmg_state::crtc_update_row_mono));
}

/* ROM definition */
ROM_START( fanucspg )
	ROM_REGION(0x4000, MAINCPU_TAG, 0)
	ROM_LOAD16_BYTE( "a40_001a.13a", 0x000000, 0x002000, CRC(1b8ac8ef) SHA1(309c081d25270e082ebf846b4f73cef76b52d991) )
	ROM_LOAD16_BYTE( "a40_002a.15a", 0x000001, 0x002000, CRC(587ae652) SHA1(ebc5a4c3d64ab9d6dd4d5355f85bc894e7294e17) )

	ROM_REGION(0x4000, SUBCPU_TAG, 0)
	ROM_LOAD( "a41_010b.28b", 0x000000, 0x004000, CRC(35a9714f) SHA1(5697b6c4db5adb5702dc1290ecc98758d5fab221) )

	ROM_REGION(0x8000, CHARGEN_TAG, 0)
	ROM_LOAD( "a42_020a.30b", 0x000000, 0x008000, CRC(33eb5962) SHA1(1157a72089ff77e8db9a9a8fcd0f6c32a1374f56) )
ROM_END

ROM_START( fanucspgm )
	ROM_REGION(0x4000, MAINCPU_TAG, 0)
	ROM_LOAD16_BYTE( "a25_001a.33e", 0x000000, 0x002000, CRC(81159267) SHA1(f5d53cc6e929f57e8c3747f80fc74d4b1643222d) )
	ROM_LOAD16_BYTE( "a25_002a.35e", 0x000001, 0x002000, CRC(4fb82c4d) SHA1(eb75e9a2d3c8e4ad56a74624ee8c52c785bd0da6) )

	ROM_REGION(0x4000, SUBCPU_TAG, 0)
	ROM_LOAD( "a21_010f.17d", 0x000000, 0x002000, CRC(ef192717) SHA1(7fb3f7ca290d2437ae5956700f88c801018ce1cc) )

	ROM_REGION(0x8000, CHARGEN_TAG, 0)
	ROM_LOAD( "a22_020b.5g",  0x000000, 0x002000, CRC(7b5f8e20) SHA1(9de607e541d8aad2d1ea56321270bb8466b16e3d) )
ROM_END

} // anonymous namespace


/* Driver */
//    YEAR  NAME       PARENT    COMPAT  MACHINE     INPUT      CLASS            INIT            COMPANY  FULLNAME                         FLAGS
COMP( 1983, fanucspg,  0,        0,      fanucspmg,  fanucspmg, fanucspmg_state, init_fanucspmg, "Fanuc", "System P Model G",              MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
COMP( 1983, fanucspgm, fanucspg, 0,      fanucspmgm, fanucspmg, fanucspmg_state, init_fanucspmg, "Fanuc", "System P Model G (monochrome)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



fatman.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:m1macrophage

/*
The PAiA FatMan is a monophonic analog synthesizer, sold as a kit. It consists
of 2 sawtooth (ramp) oscillators, a resonant 2-pole lowpass VCF (State Variable
topology), and a VCA.

The VCF's cutoff frequency is controlled by a knob, an A(S)R EG, pitch, and
velocity. The VCA is controlled by an ADSR EG and velocity. The depth of these
modulation sources is set using knobs.

The FatMan is controlled by MIDI. It listens to note on and off messages on a
channel specified by dipswitches. It converts those to pitch and velocity
control voltages, and a gate signal. Those are also exposed externally by
corresponding outputs on the unit.

The two control voltages are exponential ("V/Hz"). The 8-bit DAC is configured
to generate voltages in the range 3-6 V, which the firmware uses for the top
octave of pitches. That voltage can be divided by 2, 4 or 8, using resistor
dividers and a MUX, to generate the voltages needed for lower octaves. There is
only a single DAC, and the voltages for pitch and velocity are sampled from it
by two Sample & Hold circuits controlled by the firmware.

This driver is based on the schematics, user manual and documentation provided
by the manufacturer on their website. This driver cannot replace the real
device. Specifically, there is no attempt to emulate audio. This is just an
educational tool.

The driver emulates the digital functionality of FatMan, the analog outputs (CV
and gate), and most of the analog functionality that has an effect on the
firmware (the two envelope generators with all their controls, and the "sustain"
switch. The "punch" switch is not yet emulated).

Usage:

This driver requires supplying a MIDI input to MAME.
Example:
./mame -listmidi  # List MIDI devices, physical or virtual (e.g. DAWs).
./mame -window fatman -midiin "{midi device}"

The layout will display some LEDs, and the value of the functional knob. But
to actually see control voltage and EG control signals, you need to look at
the error logs (run mame with `-log` and look at error.log).

The knobs for the two EGs can be controlled by the Slider Control menu. Other
knobs are not emulated.

Keep in mind that changing the MIDI channel via dipswitches won't take effect
until a restart or reset (F3).
*/

#include "emu.h"

#include "cpu/mcs51/mcs51.h"
#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "machine/rescap.h"
#include "sound/va_eg.h"
#include "video/pwm.h"

#include "attotime.h"

#include "paia_fatman.lh"

#define LOG_EG  (1U << 1)
#define LOG_CV  (1U << 2)
#define LOG_DSW (1U << 3)

#define VERBOSE (LOG_GENERAL | LOG_EG | LOG_CV)
//#define LOG_OUTPUT_FUNC osd_printf_info

#include "logmacro.h"

namespace {

constexpr const char MAINCPU_TAG[] = "8031";

class fatman_state : public driver_device
{
public:
	fatman_state(const machine_config &mconfig, device_type type, const char *tag) ATTR_COLD
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, MAINCPU_TAG)
		, m_vca_adsr(*this, "vca_adsr_eg")
		, m_vcf_ar(*this, "vcf_ar_eg")
		, m_midi_led_pwm(*this, "midi_led_pwm_device")
		, m_gate_led(*this, "gate_led")
		, m_dsw_io(*this, "dsw")
		, m_vca_attack_pot(*this, "VCA_attack")
		, m_vca_decay_pot(*this, "VCA_decay")
		, m_vca_sustain_pot(*this, "VCA_sustain")
		, m_vca_release_pot(*this, "VCA_release")
		, m_vcf_attack_pot(*this, "VCF_attack")
		, m_vcf_sustain_switch(*this, "VCF_sustain")
		, m_vcf_release_pot(*this, "VCF_release")
	{
	}

	void fatman(machine_config &config) ATTR_COLD;

protected:
	void machine_start() override ATTR_COLD;

private:
	u8 dsw_midi_channel_r() const;
	int midi_rxd_r() const;
	void midi_rxd_w(int state);

	int vca_adsr_attack_r() const;
	void vca_adsr_decay_w(int state);
	void vca_adsr_release_w(int state);
	void update_vca_adsr();

	int vcf_ar_attack_r() const;
	void vcf_ar_release_w(int state);

	void cv_dac_w(offs_t offset, u8 data);
	void cv_mux_w(u8 data);
	void pitch_cv_sample_w(int state);
	void velocity_cv_sample_w(int state);

	float compute_cv() const;
	void update_pitch_cv();
	void update_velocity_cv();
	void update_cvs();

	void program_map(address_map &map) ATTR_COLD;
	void external_memory_map(address_map &map) ATTR_COLD;

	required_device<mcs51_cpu_device> m_maincpu;
	required_device<va_rc_eg_device> m_vca_adsr;
	required_device<va_rc_eg_device> m_vcf_ar;
	required_device<pwm_display_device> m_midi_led_pwm;  // D2
	output_finder<> m_gate_led;  // D13
	required_ioport m_dsw_io;
	required_ioport m_vca_attack_pot;
	required_ioport m_vca_decay_pot;
	required_ioport m_vca_sustain_pot;
	required_ioport m_vca_release_pot;
	required_ioport m_vcf_attack_pot;
	required_ioport m_vcf_sustain_switch;
	required_ioport m_vcf_release_pot;

	u8 m_midi_rxd_bit = 1; // Initial value is 1, for serial "idle".
	bool m_vca_adsr_decay = false;
	bool m_vca_adsr_release = false;
	bool m_vcf_ar_release = false;
	u8 m_cv_dac = 0;
	u8 m_cv_mux = 0;
	bool m_sampling_pitch = false;
	bool m_sampling_velocity = false;
	float m_pitch_cv = 0;
	float m_velocity_cv = 0;

	static constexpr const float V_PLUS = 9;
	static constexpr const float V_GND = 0;
	static constexpr const float V_MINUS = -12;

	// Voltage reference for both EG comparators.
	static constexpr const float R85 = RES_R(2200);
	static constexpr const float R86 = RES_K(10);
	static constexpr const float V_EG_COMP = V_PLUS * RES_VOLTAGE_DIVIDER(R85, R86);

	// VCA ADSR components.
	static constexpr const float POT_R90 = RES_K(1);  // Sustain potentiometer.
	static constexpr const float R91 = RES_R(100);
	static constexpr const float POT_R92 = RES_M(1);  // Decay rheostat.
	static constexpr const float R93 = RES_R(10);
	static constexpr const float POT_R94 = RES_M(1);  // Attack rheostat.
	static constexpr const float R95 = RES_R(100);
	static constexpr const float POT_R96 = RES_M(1);  // Release rheostat.
	static constexpr const float C19 = CAP_U(2.2);

	// VCF A(S)R components.
	static constexpr const float R80 = RES_K(1);
	static constexpr const float R81 = RES_R(100);
	static constexpr const float POT_R82 = RES_M(1);  // Release rheostat.
	static constexpr const float R83 = RES_R(100);
	static constexpr const float POT_R84 = RES_M(1);  // Attack rheostat.
	static constexpr const float C22 = CAP_U(2.2);
};

u8 fatman_state::dsw_midi_channel_r() const
{
	// Lower 4 bits in reverse order.
	const u8 channel = bitswap<4>(m_dsw_io->read(), 0, 1, 2, 3);
	LOGMASKED(LOG_DSW, "DSW MIDI channel: %d\n", channel + 1);
	return channel;
}

int fatman_state::midi_rxd_r() const
{
	return m_midi_rxd_bit;
}

void fatman_state::midi_rxd_w(int state)
{
	m_midi_rxd_bit = state ? 1 : 0;

	// MIDI IN state is inverted twice (IC7:A and IC7:C) and connected to the
	// cathode of LED D2. So the LED will be on when MIDI IN is low.
	m_midi_led_pwm->write_element(0, 0, state ? 0 : 1);
}

int fatman_state::vca_adsr_attack_r() const
{
	return (m_vca_adsr->get_v() >= V_EG_COMP) ? 1 : 0;
}

void fatman_state::vca_adsr_decay_w(int state)
{
	const bool decay = state > 0;
	if (m_vca_adsr_decay == decay)
		return;

	m_vca_adsr_decay = decay;
	LOGMASKED(LOG_EG, "vca decay: %d\n", m_vca_adsr_decay);
	update_vca_adsr();
}

void fatman_state::vca_adsr_release_w(int state)
{
	const bool release = state > 0;
	if (m_vca_adsr_release == release)
		return;

	m_vca_adsr_release = release;
	LOGMASKED(LOG_EG, "vca release: %d\n", m_vca_adsr_release);

	// LED cathode connected to ground, anode connected to IC7:F inverter,
	// which inverts the 'release' signal.
	m_gate_led = m_vca_adsr_release ? 0 : 1;
	update_vca_adsr();
}

void fatman_state::update_vca_adsr()
{
	// This function only needs to be called on a state change. For instance, if
	// either m_vca_adsr_decay or m_vca_adsr_release has changed.

	if (m_vca_adsr_decay && m_vca_adsr_release)  // Release.
	{
		const float r96 = m_vca_release_pot->read() * POT_R96 / 100.0F;
		m_vca_adsr->set_r(r96 + R95).set_target_v(V_GND);
	}
	else if (m_vca_adsr_decay)  // Decay.
	{
		const float sustain_v = V_PLUS * m_vca_sustain_pot->read() / 100.0F;
		const float current_v = m_vca_adsr->get_v();
		const float r92 = m_vca_decay_pot->read() * POT_R92 / 100.0F;
		m_vca_adsr->set_r(r92 + R91).set_target_v(std::min(sustain_v, current_v));
	}
	else if (m_vca_adsr_release)  // "Invalid" state.
	{
		// release == 1 and decay == 0 is not a useful EG state. But it is
		// emulated for completeness. The firmware enters it transiently when
		// switching from Attack to Release mode.
		const float r94 = m_vca_attack_pot->read() * POT_R94 / 100.0F;
		const float r_attack = POT_R90 + R93 + r94;
		const float r96 = m_vca_release_pot->read() * POT_R96 / 100.0F;
		const float r_release = r96 + R95;
		const float target_v = V_PLUS * RES_VOLTAGE_DIVIDER(r_attack, r_release);
		const float effective_r = RES_2_PARALLEL(r_attack, r_release);
		m_vca_adsr->set_r(effective_r).set_target_v(target_v);
	}
	else // Attack.
	{
		const float r94 = m_vca_attack_pot->read() * POT_R94 / 100.0F;
		m_vca_adsr->set_r(POT_R90 + R93 + r94).set_target_v(V_PLUS);
	}
}

int fatman_state::vcf_ar_attack_r() const
{
	const bool sustain_enabled = !(m_vcf_sustain_switch->read() & 0x01);
	if (sustain_enabled)
	{
		// When the sustain switch (S3) is "on" (i.e. grounded), the MCU never
		// receives a 1, so the firmware doesn't know that the EG attack
		// completed, and it doesn't start the EG release.
		return 0;
	}
	return (m_vcf_ar->get_v() >= V_EG_COMP) ? 1 : 0;
}

void fatman_state::vcf_ar_release_w(int state)
{
	const bool release = state > 0;
	if (m_vcf_ar_release == release)
		return;

	m_vcf_ar_release = release;
	LOGMASKED(LOG_EG, "vcf release: %d\n", m_vcf_ar_release);

	if (m_vcf_ar_release)  // Start release.
	{
		const float r82 = m_vcf_release_pot->read() * POT_R82 / 100.0;
		m_vcf_ar->set_r(r82 + R81).set_target_v(V_GND);
	}
	else // Start attack.
	{
		const float r84 = m_vcf_attack_pot->read() * POT_R84 / 100.0;
		m_vcf_ar->set_r(R80 + R83 + r84).set_target_v(V_PLUS);
	}
}

void fatman_state::cv_dac_w(offs_t offset, u8 data)
{
	if (m_cv_dac == data)
		return;
	m_cv_dac = data;
	update_cvs();
}

void fatman_state::cv_mux_w(u8 data)
{
	// P3.4 and P3.5 are level-shifted and inverted by Q1 and Q2, and used
	// as the control inputs (high and low order bits, respectively) of the
	// 4052 MUX (IC9).
	const u8 mux = bitswap<2>(~data, 4, 5);
	if (m_cv_mux == mux)
		return;
	m_cv_mux = mux;
	update_cvs();
}

void fatman_state::pitch_cv_sample_w(int state)
{
	const bool old_state = m_sampling_pitch;
	// P1.0 is level-shifted with a comparator (without inverting), and
	// connected to 4016 'control A' (active high).
	m_sampling_pitch = state > 0;
	if (!old_state && state)
		update_pitch_cv();
}

void fatman_state::velocity_cv_sample_w(int state)
{
	const bool old_state = m_sampling_velocity;
	// Same as pitch_cv_sample_w above, but for P1.1 and 4016 'control B'
	m_sampling_velocity = state > 0;
	if (!old_state && state)
		update_velocity_cv();
}

float fatman_state::compute_cv() const
{
	static constexpr float MAX_CV = 6;
	static constexpr float HALF_CV = MAX_CV / 2;
	static constexpr float MUX_DIVISOR[4] = {1, 2, 4, 8};

	assert(m_cv_mux >= 0 && m_cv_mux < 4);
	return (HALF_CV + m_cv_dac * HALF_CV / 255) / MUX_DIVISOR[m_cv_mux];
}

void fatman_state::update_pitch_cv()
{
	if (!m_sampling_pitch)
		return;

	const float new_cv = compute_cv();
	if (new_cv == m_pitch_cv)
		return;

	m_pitch_cv = new_cv;
	LOGMASKED(LOG_CV, "Pitch CV %f\n", m_pitch_cv);
}

void fatman_state::update_velocity_cv()
{
	if (!m_sampling_velocity)
		return;

	const float new_cv = compute_cv();
	if (new_cv == m_velocity_cv)
		return;

	m_velocity_cv = new_cv;
	LOGMASKED(LOG_CV, "Velocity CV %f\n", m_velocity_cv);
}

void fatman_state::update_cvs()
{
	update_pitch_cv();
	update_velocity_cv();
}

void fatman_state::program_map(address_map &map)
{
	// A13-A15 are not connected.
	map(0x0000, 0x1fff).mirror(0xe000).rom().region(MAINCPU_TAG, 0);
}

void fatman_state::external_memory_map(address_map &map)
{
	// Address lines ignored on external memory writes.
	map(0x0000, 0x0000).mirror(0xffff).w(FUNC(fatman_state::cv_dac_w));
}

void fatman_state::machine_start()
{
	m_gate_led.resolve();
	save_item(NAME(m_midi_rxd_bit));
	save_item(NAME(m_vca_adsr_decay));
	save_item(NAME(m_vca_adsr_release));
	save_item(NAME(m_vcf_ar_release));
	save_item(NAME(m_cv_dac));
	save_item(NAME(m_cv_mux));
	save_item(NAME(m_sampling_pitch));
	save_item(NAME(m_sampling_velocity));
	save_item(NAME(m_pitch_cv));
	save_item(NAME(m_velocity_cv));
}

void fatman_state::fatman(machine_config &config)
{
	I8031(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &fatman_state::program_map);
	m_maincpu->set_addrmap(AS_IO, &fatman_state::external_memory_map);

	// The `set_constant()`s on inputs 1 and 3 ensure that those inputs, when
	// read, will return whatever was written to them (the input is
	// essentially ANDed to the value that was written to the port. See mcs51
	// implementation). The firmware depends on that functionality for at least
	// some of these inputs.

	m_maincpu->port_in_cb<1>().set_constant(0x0f).mask(0x0f);
	m_maincpu->port_in_cb<1>().append(FUNC(fatman_state::dsw_midi_channel_r)).lshift(4).mask(0xf0);

	m_maincpu->port_out_cb<1>().set(FUNC(fatman_state::pitch_cv_sample_w)).bit(0);
	m_maincpu->port_out_cb<1>().append(FUNC(fatman_state::velocity_cv_sample_w)).bit(1);
	m_maincpu->port_out_cb<1>().append(FUNC(fatman_state::vca_adsr_decay_w)).bit(2);
	m_maincpu->port_out_cb<1>().append(FUNC(fatman_state::vca_adsr_release_w)).bit(3);

	m_maincpu->port_in_cb<3>().set_constant(0x72).mask(0x72);  // Bits 1, 4, 5, 6.
	m_maincpu->port_in_cb<3>().append(FUNC(fatman_state::midi_rxd_r)).mask(0x01);
	m_maincpu->port_in_cb<3>().append(FUNC(fatman_state::vca_adsr_attack_r)).lshift(2).mask(0x04);
	m_maincpu->port_in_cb<3>().append(FUNC(fatman_state::vcf_ar_attack_r)).lshift(3).mask(0x08);
	m_maincpu->port_in_cb<3>().append_ioport("dsw").mask(0x80);

	m_maincpu->port_out_cb<3>().set(FUNC(fatman_state::vcf_ar_release_w)).bit(1);
	m_maincpu->port_out_cb<3>().append(FUNC(fatman_state::cv_mux_w)).mask(0x30);  // Bits 4, 5.

	VA_RC_EG(config, m_vca_adsr).set_c(C19);
	VA_RC_EG(config, m_vcf_ar).set_c(C22);

	midi_port_device &midi_in(MIDI_PORT(config, "mdin", midiin_slot, "midiin"));
	MIDI_PORT(config, "mdthru", midiout_slot, "midiout");
	midi_in.rxd_handler().set(FUNC(fatman_state::midi_rxd_w));
	midi_in.rxd_handler().append("mdthru", FUNC(midi_port_device::write_txd));

	PWM_DISPLAY(config, m_midi_led_pwm).set_size(1, 1);
	m_midi_led_pwm->output_x().set_output("midi_led");
	m_midi_led_pwm->set_interpolation(0.2);
	m_midi_led_pwm->set_bri_levels(0.0001);
	m_midi_led_pwm->set_refresh(attotime::from_hz(30));

	config.set_default_layout(layout_paia_fatman);
}

INPUT_PORTS_START(fatman)
	PORT_START("dsw")
	PORT_DIPNAME(0x0f, 0x00, "MIDI Channel") PORT_DIPLOCATION("SW1:1,2,3,4")
	PORT_DIPSETTING(   0x00, "1")
	PORT_DIPSETTING(   0x08, "2")
	PORT_DIPSETTING(   0x04, "3")
	PORT_DIPSETTING(   0x0c, "4")
	PORT_DIPSETTING(   0x02, "5")
	PORT_DIPSETTING(   0x0a, "6")
	PORT_DIPSETTING(   0x06, "7")
	PORT_DIPSETTING(   0x0e, "8")
	PORT_DIPSETTING(   0x01, "9")
	PORT_DIPSETTING(   0x09, "10")
	PORT_DIPSETTING(   0x05, "11")
	PORT_DIPSETTING(   0x0d, "12")
	PORT_DIPSETTING(   0x03, "13")
	PORT_DIPSETTING(   0x0b, "14")
	PORT_DIPSETTING(   0x07, "15")
	PORT_DIPSETTING(   0x0f, "16")
	PORT_DIPNAME(0x70, 0x00, "Not Connected") PORT_DIPLOCATION("SW1:5,6,7")
	PORT_DIPNAME(0x80, 0x00, DEF_STR(Unused)) PORT_DIPLOCATION("SW1:8")
	PORT_DIPSETTING(   0x00, DEF_STR(On))
	PORT_DIPSETTING(   0x80, DEF_STR(Off))

	// VCA EG Controls.
	PORT_START("VCA_attack")                // POT_R94
	PORT_ADJUSTER(2, "VCA EG Attack")
	PORT_START("VCA_decay")                 // POT_R92
	PORT_ADJUSTER(1, "VCA EG Decay")
	PORT_START("VCA_sustain")               // POT_R90
	PORT_ADJUSTER(50, "VCA EG Sustain")
	PORT_START("VCA_release")               // POT_R96
	PORT_ADJUSTER(3, "VCA EG Release")

	// VCF EG Controls.
	PORT_START("VCF_attack")                // POT_R82
	PORT_ADJUSTER(1, "VCF EG Attack")
	PORT_START("VCF_release")               // POT_R84
	PORT_ADJUSTER(30, "VCF EG Release")
	PORT_START("VCF_sustain")               // S3
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("VCF EG Sustain") PORT_CODE(KEYCODE_S) PORT_TOGGLE
INPUT_PORTS_END

ROM_START(fatman)
	ROM_REGION(0x2000, MAINCPU_TAG, 0)
	ROM_DEFAULT_BIOS("fatman9")
	ROM_SYSTEM_BIOS(0, "fatman9", "FatMan Operating System 1.9 (C) 1995")
	ROMX_LOAD("fatmopv1.9.ic3", 0x000000, 0x002000, CRC(e2a4b9a9) SHA1(3738f0c9b3b158884fddb103b19314899d7e60c0), ROM_BIOS(0))
ROM_END

}  // anonymous namespace

SYST(1992, fatman, 0, 0, fatman, fatman, fatman_state, empty_init, "PAiA Electronics", "FatMan", MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



fb01.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/***************************************************************************

  Yamaha FB-01

***************************************************************************/

#include "emu.h"

#include "bus/midi/midi.h"
#include "cpu/z80/z80.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/nvram.h"
#include "sound/ymopm.h"
#include "video/hd44780.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "fb01.lh"


namespace {

class fb01_state : public driver_device
{
public:
	fb01_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_upd71051(*this, "upd71051")
		, m_ym2164_irq(CLEAR_LINE)
		, m_upd71051_txrdy(CLEAR_LINE)
		, m_upd71051_rxrdy(CLEAR_LINE)
	{
	}

	void fb01(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void ym2164_irq_w(int state);
	void upd71051_txrdy_w(int state);
	void upd71051_rxrdy_w(int state);

	void fb01_palette(palette_device &palette) const;
	HD44780_PIXEL_UPDATE(fb01_pixel_update);

	void fb01_io(address_map &map) ATTR_COLD;
	void fb01_mem(address_map &map) ATTR_COLD;

	void update_int();

	required_device<z80_device> m_maincpu;
	required_device<i8251_device> m_upd71051;
	int m_ym2164_irq;
	int m_upd71051_txrdy;
	int m_upd71051_rxrdy;
};


void fb01_state::fb01_mem(address_map &map)
{
	map(0x0000, 0x7fff).rom();
	map(0x8000, 0xbfff).mirror(0x4000).ram().share("nvram"); // 2 * 8KB S-RAM
}


void fb01_state::fb01_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	// 00-01  YM2164
	map(0x00, 0x00).w("ym2164", FUNC(ym2164_device::address_w));
	map(0x01, 0x01).rw("ym2164", FUNC(ym2164_device::status_r), FUNC(ym2164_device::data_w));

	// 10-11  USART uPD71051C  4MHz & 4MHz / 8
	map(0x10, 0x11).rw(m_upd71051, FUNC(i8251_device::read), FUNC(i8251_device::write));

	// 20     PANEL SWITCH
	map(0x20, 0x20).portr("PANEL");

	// 30-31  HD44780A
	map(0x30, 0x31).rw("hd44780", FUNC(hd44780_device::read), FUNC(hd44780_device::write));
}


static INPUT_PORTS_START( fb01 )
	PORT_START("PANEL")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("System Set Up")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Inst Select")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Inst Assign")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Inst Function")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Voice Function")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Voice Select")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("-1/No")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("+1/Yes")
INPUT_PORTS_END


void fb01_state::machine_start()
{
	save_item(NAME(m_ym2164_irq));
	save_item(NAME(m_upd71051_txrdy));
	save_item(NAME(m_upd71051_rxrdy));
}


void fb01_state::machine_reset()
{
	m_upd71051->write_cts(0);
	m_upd71051->write_rxd(ASSERT_LINE);
}


void fb01_state::ym2164_irq_w(int state)
{
	m_ym2164_irq = state;
	update_int();
}


void fb01_state::upd71051_txrdy_w(int state)
{
	m_upd71051_txrdy = state;
	update_int();
}


void fb01_state::upd71051_rxrdy_w(int state)
{
	m_upd71051_rxrdy = state;
	update_int();
}


void fb01_state::update_int()
{
	m_maincpu->set_input_line(0, (m_ym2164_irq || m_upd71051_txrdy || m_upd71051_rxrdy) ? ASSERT_LINE : CLEAR_LINE);
}


HD44780_PIXEL_UPDATE(fb01_state::fb01_pixel_update)
{
	if ( pos < 8 && line < 2 )
	{
		bitmap.pix(y, line*6*8 + pos*6 + x) = state;
	}
}


void fb01_state::fb01_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(30, 0, 0));
	palette.set_pen_color(1, rgb_t(150, 0, 0));
}


void fb01_state::fb01(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(12'000'000)/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &fb01_state::fb01_mem);
	m_maincpu->set_addrmap(AS_IO, &fb01_state::fb01_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(6*16, 9);
	screen.set_visarea_full();
	screen.set_screen_update("hd44780", FUNC(hd44780_device::screen_update));
	screen.set_palette("palette");

	config.set_default_layout(layout_fb01);

	PALETTE(config, "palette", FUNC(fb01_state::fb01_palette), 2);

	hd44780_device &hd44780(HD44780(config, "hd44780", 270'000)); // TODO: clock not measured, datasheet typical clock used
	hd44780.set_lcd_size(2, 8); // 2x8 displayed as 1x16
	hd44780.set_pixel_update_cb(FUNC(fb01_state::fb01_pixel_update));

	I8251(config, m_upd71051, XTAL(4'000'000));
	m_upd71051->rxrdy_handler().set(FUNC(fb01_state::upd71051_rxrdy_w));
	m_upd71051->txrdy_handler().set(FUNC(fb01_state::upd71051_txrdy_w));
	m_upd71051->txd_handler().set("mdout", FUNC(midi_port_device::write_txd));

	clock_device &usart_clock(CLOCK(config, "usart_clock", XTAL(4'000'000) / 8)); // 500KHz
	usart_clock.signal_handler().set(m_upd71051, FUNC(i8251_device::write_txc));
	usart_clock.signal_handler().append(m_upd71051, FUNC(i8251_device::write_rxc));

	midi_port_device &mdin(MIDI_PORT(config, "mdin", midiin_slot, "midiin"));
	mdin.rxd_handler().set("mdthru", FUNC(midi_port_device::write_txd));
	mdin.rxd_handler().append(m_upd71051, FUNC(i8251_device::write_rxd));

	MIDI_PORT(config, "mdout", midiout_slot, "midiout");

	MIDI_PORT(config, "mdthru", midiout_slot, "midiout");

	SPEAKER(config, "speaker", 2).front();
	ym2164_device &ym2164(YM2164(config, "ym2164", XTAL(4'000'000)));
	ym2164.irq_handler().set(FUNC(fb01_state::ym2164_irq_w));
	ym2164.add_route(0, "speaker", 1.00, 0);
	ym2164.add_route(1, "speaker", 1.00, 1);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);
}


/* ROM definition */
ROM_START( fb01 )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD("nec__-011_xb712c0__8709ex700__d27c256c-15.ic11", 0, 0x8000, CRC(7357e9a4) SHA1(049c482d6c91b7e2846757dd0f5138e0d8b687f0)) // OTP 27c256 windowless eprom?
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  STATE       INIT        COMPANY   FULLNAME                     FLAGS
CONS( 1986, fb01, 0,      0,      fb01,    fb01,  fb01_state, empty_init, "Yamaha", "FB-01 FM Sound Generator",  MACHINE_SUPPORTS_SAVE )



fc100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Goldstar FC-100 (FC stands for Famicom)

2014/04/20 Skeleton driver.

Known chips: M5C6847P, AY-3-8910, 8251. XTALS 7.15909, 4.9152

No manuals or schematics available.
Shift-Run to BREAK out of CLOAD.
Cassette uses the uart.
There is an inbuilt Monitor. MON to enter. Commands are D,G,M,R,S,X.


Test of semigraphic 6
10 SCREEN 2:CLS
20 FOR I=0 TO 360
30 PSET(128+SIN(I)*90,91-COS(I)*90), 1
40 NEXT
RUN

TODO:
- Cassette can be 600 or 1200 baud, how is 600 baud selected?
- Hookup Graphics modes and colours
- Unknown i/o ports
- Need cart software (current code is just a guess)


****************************************************************************/


#include "emu.h"

#include "bus/centronics/ctronics.h"
#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "machine/buffer.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/timer.h"
#include "sound/ay8910.h"
#include "video/mc6847.h"

#include "emupal.h"
#include "speaker.h"

#include "formats/fc100_cas.h"


namespace {

class fc100_state : public driver_device
{
public:
	fc100_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_vdg(*this, "vdg")
		, m_p_videoram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
		, m_cass(*this, "cassette")
		, m_cart(*this, "cartslot")
		, m_uart(*this, "uart")
		, m_centronics(*this, "centronics")
		, m_keyboard(*this, "KEY.%u", 0U)
	{ }

	void fc100(machine_config &config);

	void init_fc100();

private:
	uint8_t mc6847_videoram_r(offs_t offset);
	uint8_t port00_r(offs_t offset);
	void port31_w(uint8_t data);
	void port33_w(uint8_t data);
	void port43_w(uint8_t data);
	void port60_w(offs_t offset, uint8_t data);
	void port70_w(offs_t offset, uint8_t data);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_w);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);
	TIMER_DEVICE_CALLBACK_MEMBER(timer_k);

	MC6847_GET_CHARROM_MEMBER(get_char_rom)
	{
		return m_p_chargen[(ch * 16 + line) & 0xfff];
	}
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// graphics signals
	uint8_t m_ag = 0U;
	uint8_t m_gm2 = 0U;
	uint8_t m_gm1 = 0U;
	uint8_t m_gm0 = 0U;
	uint8_t m_as = 0U;
	uint8_t m_css = 0U;
	uint8_t m_intext = 0U;
	uint8_t m_inv = 0U;
	uint8_t m_cass_data[4]{};
	bool m_cassbit = 0;
	bool m_cassold = 0;
	uint8_t m_key_pressed = 0U;
	bool m_banksw_unlocked = 0;

	required_device<cpu_device> m_maincpu;
	required_device<mc6847_base_device> m_vdg;
	required_shared_ptr<uint8_t> m_p_videoram;
	required_region_ptr<u8> m_p_chargen;
	required_device<cassette_image_device> m_cass;
	required_device<generic_slot_device> m_cart;
	required_device<i8251_device> m_uart;
	required_device<centronics_device> m_centronics;
	required_ioport_array<16> m_keyboard;
};


void fc100_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x5fff).rom().region("roms", 0);
	//map(0x6000, 0x6fff)      // mapped by the cartslot
	map(0x7800, 0x7fff).bankr("bankr").bankw("bankw"); // Banked RAM/ROM
	map(0x8000, 0xbfff).ram(); // expansion ram pack - if omitted you get a 'Pages?' prompt at boot
	map(0xc000, 0xffff).ram().share("videoram");
}

void fc100_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x0f).r(FUNC(fc100_state::port00_r));
	// map(0x10, 0x10).w(FUNC(fc100_state::port10_w));  // vdg, unknown effects
	map(0x21, 0x21).w("psg", FUNC(ay8910_device::data_w));
	map(0x22, 0x22).r("psg", FUNC(ay8910_device::data_r));
	map(0x23, 0x23).w("psg", FUNC(ay8910_device::address_w));
	map(0x31, 0x31).w(FUNC(fc100_state::port31_w));
	map(0x33, 0x33).w(FUNC(fc100_state::port33_w));
	map(0x40, 0x40).w("cent_data_out", FUNC(output_latch_device::write));
	map(0x42, 0x42).nopw(); // bit 0 could be printer select
	map(0x43, 0x43).w(FUNC(fc100_state::port43_w));
	map(0x44, 0x44).r("cent_status_in", FUNC(input_buffer_device::read));
	map(0x60, 0x61).w(FUNC(fc100_state::port60_w));
	map(0x70, 0x71).w(FUNC(fc100_state::port70_w));
	map(0xb0, 0xb0).rw(m_uart, FUNC(i8251_device::data_r), FUNC(i8251_device::data_w));
	map(0xb8, 0xb8).rw(m_uart, FUNC(i8251_device::status_r), FUNC(i8251_device::control_w));
}

static INPUT_PORTS_START( fc100 )
	PORT_START("KEY.0")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT) PORT_NAME("Graph") // does nothing
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_NAME("Shift") PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_NAME("Ctrl")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_NAME("Caps")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_NAME("[") PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_NAME("]") PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_NAME("P") PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_NAME("O") PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("KEY.1")
	PORT_BIT(0xF0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_NAME("\\") PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Backspace") PORT_CHAR(8)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("=") PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_NAME("Enter") PORT_CHAR(13)

	PORT_START("KEY.2")
	PORT_BIT(0xF0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Left") PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Down") PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Right") PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_NAME("Up") PORT_CHAR(UCHAR_MAMEKEY(UP))

	PORT_START("KEY.3")
	PORT_BIT(0xF0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_NAME("9") PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_NAME(";") PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_NAME("'") PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_NAME("Tab") PORT_CHAR(9)

	PORT_START("KEY.4")
	PORT_BIT(0xF0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_NAME("5") PORT_CHAR('5') PORT_CHAR('^')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_NAME("6") PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_NAME("7") PORT_CHAR('7') PORT_CHAR('%')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_NAME("8") PORT_CHAR('8') PORT_CHAR('*')

	PORT_START("KEY.5")
	PORT_BIT(0xF0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_NAME("1") PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_NAME("2") PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_NAME("3") PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_NAME("4") PORT_CHAR('4') PORT_CHAR('$')

	PORT_START("KEY.6")
	PORT_BIT(0xF0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_NAME("F5") PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_NAME("Space") PORT_CHAR(32)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN) PORT_NAME("Run")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_NAME("0") PORT_CHAR('0') PORT_CHAR(')')

	PORT_START("KEY.7")
	PORT_BIT(0xF0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_NAME("F1") PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_NAME("F2") PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_NAME("F3") PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_NAME("F4") PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_CHAR(UCHAR_MAMEKEY(F9))

	PORT_START("KEY.8")
	PORT_BIT(0xF0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_NAME("-") PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_NAME("/") PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_NAME(".") PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_NAME(",") PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("KEY.9")
	PORT_BIT(0xF0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_NAME("Home")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT) PORT_NAME("Ins")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL) PORT_NAME("Del")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_NAME("Esc") PORT_CHAR(27)

	PORT_START("KEY.10")
	PORT_BIT(0xF0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_NAME("B") PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_NAME("N") PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_NAME("M") PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_NAME("L") PORT_CHAR('l') PORT_CHAR('L')

	PORT_START("KEY.11")
	PORT_BIT(0xF0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_NAME("Z") PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_NAME("X") PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_NAME("C") PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_NAME("V") PORT_CHAR('v') PORT_CHAR('V')

	PORT_START("KEY.12")
	PORT_BIT(0xF0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_NAME("G") PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_NAME("H") PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_NAME("J") PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_NAME("K") PORT_CHAR('k') PORT_CHAR('K')

	PORT_START("KEY.13")
	PORT_BIT(0xF0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_NAME("A") PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_NAME("S") PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_NAME("D") PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_NAME("F") PORT_CHAR('f') PORT_CHAR('F')

	PORT_START("KEY.14")
	PORT_BIT(0xF0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_NAME("T") PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_NAME("Y") PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_NAME("U") PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_NAME("I") PORT_CHAR('i') PORT_CHAR('I')

	PORT_START("KEY.15")
	PORT_BIT(0xF0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_NAME("Q") PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_NAME("W") PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_NAME("E") PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_NAME("R") PORT_CHAR('r') PORT_CHAR('R')

	PORT_START("JOY0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("JOY1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

uint8_t fc100_state::port00_r(offs_t offset)
{
	return m_keyboard[offset]->read();
}

// The timer frequency controls the auto-repeat delay and speed
// m_key_pressed:
// 0 = no key pressed, nothing to do
// 1 = key pressed, generate IRQ.
// 2 = key released, generate IRQ.
TIMER_DEVICE_CALLBACK_MEMBER( fc100_state::timer_k)
{
	/* scan the keyboard */
	uint8_t i;

	for (i = 0; i < 16; i++)
	{
		if (m_keyboard[i]->read() < 255)
		{
			// IRQ if key pressed
			m_key_pressed = 1;
			m_maincpu->set_input_line(0, HOLD_LINE);
			return;
		}
	}

	if (m_key_pressed == 1) // IRQ for key released
	{
		m_key_pressed = 2;
		m_maincpu->set_input_line(0, HOLD_LINE);
		return;
	}
	else
	if (m_key_pressed == 2) // release IRQ
	{
		m_key_pressed = 0;
		m_maincpu->set_input_line(0, CLEAR_LINE);
	}
}


//********************* AUDIO **********************************
#if 0
void fc100_state::ay_port_a_w(uint8_t data)
{
	m_ag = BIT(data, 4);
	m_gm2 = BIT(data, 6);
	m_gm1 = BIT(data, 3);
	m_gm0 = BIT(data, 3);
	m_css = m_ag;

	m_vdg->ag_w( m_ag ? ASSERT_LINE : CLEAR_LINE );
	m_vdg->gm2_w( m_gm2 ? ASSERT_LINE : CLEAR_LINE );
	m_vdg->gm1_w( m_gm1 ? ASSERT_LINE : CLEAR_LINE );
	m_vdg->gm0_w( m_gm0 ? ASSERT_LINE : CLEAR_LINE );
	m_vdg->css_w( m_css ? ASSERT_LINE : CLEAR_LINE );
	m_vdg->hack_black_becomes_blue( BIT(data, 1) );
}
#endif

//******************** VIDEO **********************************

uint8_t fc100_state::mc6847_videoram_r(offs_t offset)
{
	if (offset == ~0) return 0xff;

	if ( m_ag )
	{
		if ( m_gm2 )
		{
			// 256 x 192 / 6KB
			offset = ( ( offset & 0x1fc0 ) >> 1 ) | ( offset & 0x1f );
			return m_p_videoram[offset % 0xc00];
		}
		else
		{
			// 256 x 96 / 3KB
			return m_p_videoram[offset % 0xc00];
		}
	}

	// Standard text
	uint8_t data = m_p_videoram[offset];
	uint8_t attr = m_p_videoram[offset+0x200];

	// unknown bits 1,2,4,7
	m_vdg->inv_w( BIT( attr, 0 ));
	m_vdg->css_w( BIT( attr, 1)); // guess
	m_vdg->as_w( BIT( attr, 6 ));

	return data;
}

/* F4 Character Displayer */
static const gfx_layout u53_charlayout =
{
	7, 15,                   /* 7 x 15 characters */
	256,                  /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_fc100 )
	GFXDECODE_ENTRY( "chargen", 0x0000, u53_charlayout, 0, 1 )
GFXDECODE_END

//********************** CENTRONICS PRINTER ***********************************

void fc100_state::port43_w(uint8_t data)
{
	m_centronics->write_strobe(BIT(data, 2));
	m_centronics->write_init(BIT(data, 3));
}

//********************** UART/CASSETTE ***********************************

void fc100_state::port31_w(uint8_t data)
{
	if (data == 8)
		m_cass->change_state(CASSETTE_MOTOR_ENABLED, CASSETTE_MASK_MOTOR);
}

void fc100_state::port33_w(uint8_t data)
{
	if (data == 0)
		m_cass->change_state(CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
}

TIMER_DEVICE_CALLBACK_MEMBER( fc100_state::kansas_w )
{
	m_cass_data[3]++;

	if (m_cassbit != m_cassold)
	{
		m_cass_data[3] = 0;
		m_cassold = m_cassbit;
	}

	if (m_cassbit)
		m_cass->output(BIT(m_cass_data[3], 0) ? -1.0 : +1.0); // 2400Hz
	else
		m_cass->output(BIT(m_cass_data[3], 1) ? -1.0 : +1.0); // 1200Hz
}

TIMER_DEVICE_CALLBACK_MEMBER( fc100_state::kansas_r)
{
	/* cassette - turn 1200/2400Hz to a bit */
	m_cass_data[1]++;
	uint8_t cass_ws = (m_cass->input() > +0.03) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		m_uart->write_rxd((m_cass_data[1] < 12) ? 1 : 0);
		m_cass_data[1] = 0;
	}
}

//******************** MACHINE ******************************

void fc100_state::machine_start()
{
	if (m_cart->exists())
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x6000, 0x6fff, read8sm_delegate(*m_cart, FUNC(generic_slot_device::read_rom)));

	save_item(NAME(m_ag));
	save_item(NAME(m_gm2));
	save_item(NAME(m_gm1));
	save_item(NAME(m_gm0));
	save_item(NAME(m_as));
	save_item(NAME(m_css));
	save_item(NAME(m_intext));
	save_item(NAME(m_inv));
	save_item(NAME(m_cass_data));
	save_item(NAME(m_cassbit));
	save_item(NAME(m_cassold));
	save_item(NAME(m_key_pressed));
	save_item(NAME(m_banksw_unlocked));
}

void fc100_state::machine_reset()
{
	m_ag = 0;
	m_gm2 = 0;
	m_gm1 = 0;
	m_gm0 = 0;
	m_as = 0;
	m_css = 0;
	m_intext = 0;
	m_inv = 0;

	m_cass_data[0] = m_cass_data[1] = m_cass_data[2] = m_cass_data[3] = 0;
	m_cassbit = 0;
	m_cassold = 0;
	m_key_pressed = 0;
	membank("bankr")->set_entry(0);
	membank("bankw")->set_entry(0);
	m_uart->write_cts(0);
}

void fc100_state::port60_w(offs_t offset, uint8_t data)
{
	if (m_banksw_unlocked)
		membank("bankr")->set_entry(offset);
}

void fc100_state::port70_w(offs_t offset, uint8_t data)
{
	m_banksw_unlocked = (bool)offset;
}

void fc100_state::init_fc100()
{
	uint8_t *ram = memregion("ram")->base();
	uint8_t *cgen = memregion("chargen")->base()+0x800;

	membank("bankr")->configure_entry(0, &cgen[0]);
	membank("bankw")->configure_entry(0, &ram[0]);
	membank("bankr")->configure_entry(1, &ram[0]);
}

void fc100_state::fc100(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(7'159'090)/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &fc100_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &fc100_state::io_map);

	/* video hardware */
	M5C6847P1(config, m_vdg, XTAL(7'159'090)/2);  // Clock not verified
	m_vdg->set_screen("screen");
	m_vdg->input_callback().set(FUNC(fc100_state::mc6847_videoram_r));
	m_vdg->set_get_char_rom(FUNC(fc100_state::get_char_rom));
	m_vdg->set_get_fixed_mode(m5c6847p1_device::MODE_INTEXT);
	// other lines not connected

	SCREEN(config, "screen", SCREEN_TYPE_RASTER);
	GFXDECODE(config, "gfxdecode", "f4palette", gfx_fc100);
	PALETTE(config, "f4palette", palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	ay8910_device &psg(AY8910(config, "psg", XTAL(7'159'090)/3/2));  /* AY-3-8910 - clock not verified */
	psg.port_a_read_callback().set_ioport("JOY0");
	psg.port_b_read_callback().set_ioport("JOY1");
	//psg.port_a_write_callback().set(FUNC(fc100_state::ay_port_a_w));
	//psg.port_b_write_callback().set(FUNC(fc100_state::ay_port_b_w));
	psg.add_route(ALL_OUTPUTS, "mono", 1.50);

	/* Devices */
	CASSETTE(config, m_cass);
	m_cass->set_formats(fc100_cassette_formats);
	m_cass->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	I8251(config, m_uart, 0);
	m_uart->txd_handler().set([this] (bool state) { m_cassbit = state; });
	clock_device &uart_clock(CLOCK(config, "uart_clock", XTAL(4'915'200)/16/16)); // gives 19200
	uart_clock.signal_handler().set(m_uart, FUNC(i8251_device::write_txc));
	uart_clock.signal_handler().append(m_uart, FUNC(i8251_device::write_rxc));

	TIMER(config, "kansas_w").configure_periodic(FUNC(fc100_state::kansas_w), attotime::from_hz(4800)); // cass write
	TIMER(config, "kansas_r").configure_periodic(FUNC(fc100_state::kansas_r), attotime::from_hz(40000)); // cass read
	TIMER(config, "timer_k").configure_periodic(FUNC(fc100_state::timer_k), attotime::from_hz(300)); // keyb scan

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "fc100_cart");

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set("cent_status_in", FUNC(input_buffer_device::write_bit4));
	m_centronics->busy_handler().set("cent_status_in", FUNC(input_buffer_device::write_bit5));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	INPUT_BUFFER(config, "cent_status_in");
}

/* ROM definition */
ROM_START( fc100 )
	ROM_REGION( 0x6000, "roms", 0 )
	ROM_LOAD( "08-01.u48",     0x0000, 0x2000, CRC(24e78e75) SHA1(13121706544256a702635448ed2950a75c13f491) )
	ROM_LOAD( "08-02.u49",     0x2000, 0x2000, CRC(e14fc7e9) SHA1(9c5821e65c1efe698e25668d24c36929ea4c3ad7) )
	ROM_LOAD( "06-03.u50",     0x4000, 0x2000, CRC(d783c84e) SHA1(6d1bf53995e08724d5ecc24198cdda4442eb2eb9) )

	ROM_REGION( 0x800, "mcu", ROMREGION_ERASE00 )
	ROM_LOAD( "mcu.bin", 0x000, 0x800, NO_DUMP )

	ROM_REGION( 0x800, "ram", ROMREGION_ERASE00 )

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "cg-04-01.u53",  0x0000, 0x1000, CRC(2de75b7f) SHA1(464369d98cbae92ffa322ebaa4404cf5b26825f1) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY     FULLNAME  FLAGS
CONS( 1982, fc100, 0,      0,      fc100,   fc100, fc100_state, init_fc100, "Goldstar", "FC-100", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



fk1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

FK-1

2009-05-12 Skeleton driver.

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/i8255.h"
#include "machine/pit8253.h"
#include "machine/i8251.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "emupal.h"
#include "screen.h"


namespace {

class fk1_state : public driver_device
{
public:
	fk1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_bankr(*this, "bankr1")
		, m_bankw(*this, "bankw1")
		, m_bank2(*this, "bank2")
	{ }

	void fk1(machine_config &config);

private:
	void ppi1_a_w(uint8_t data);
	void ppi1_b_w(uint8_t data);
	void ppi1_c_w(uint8_t data);
	uint8_t ppi1_a_r();
	uint8_t ppi1_b_r();
	uint8_t ppi1_c_r();
	void ppi2_a_w(uint8_t data);
	void ppi2_c_w(uint8_t data);
	uint8_t ppi2_b_r();
	uint8_t ppi2_c_r();
	void ppi3_a_w(uint8_t data);
	void ppi3_b_w(uint8_t data);
	void ppi3_c_w(uint8_t data);
	uint8_t ppi3_a_r();
	uint8_t ppi3_b_r();
	uint8_t ppi3_c_r();
	void pit_out0(int state);
	void pit_out1(int state);
	void pit_out2(int state);
	void intr_w(uint8_t data);
	uint8_t bank_ram_r();
	uint8_t bank_rom_r();
	void disk_w(uint8_t data);
	uint8_t mouse_r();
	void reset_int_w(uint8_t data);
	uint8_t m_video_rol;
	uint8_t m_int_vector;
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	TIMER_DEVICE_CALLBACK_MEMBER(keyboard_callback);
	TIMER_DEVICE_CALLBACK_MEMBER(vsync_callback);
	IRQ_CALLBACK_MEMBER(irq_callback);
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_device<ram_device> m_ram;
	required_memory_bank    m_bankr;
	required_memory_bank    m_bankw;
	required_memory_bank    m_bank2;
};


/*
Port A:
        Printer
Port B:
        Keyboard
Port C:
    READING :
        7 - / OBF(buffer overflow for the printer)
        6 - INTE printer
        5 - ERROR printer
        4 - /PEND, lack of paper
        3 - INTR printer
        2 - INTE keyboard
        1 - IBF (data from the keyboard)
        0 - INTR keyboard
    WRITING :
        6 - INTE printer
        2 - INTE keyboard
 */

void fk1_state::ppi1_a_w(uint8_t data)
{
//  logerror("fk1_ppi_1_a_w %02x\n",data);
}

void fk1_state::ppi1_b_w(uint8_t data)
{
//  logerror("fk1_ppi_1_b_w %02x\n",data);
}

void fk1_state::ppi1_c_w(uint8_t data)
{
	//logerror("fk1_ppi_1_c_w %02x\n",data);
}

uint8_t fk1_state::ppi1_a_r()
{
	//logerror("fk1_ppi_1_a_r\n");
	return 0xff;
}

uint8_t fk1_state::ppi1_b_r()
{
//  logerror("fk1_ppi_1_b_r\n");
	return 0;
}

uint8_t fk1_state::ppi1_c_r()
{
//  logerror("fk1_ppi_1_c_r\n");
	return 0;
}

/*
Port A:
    Writing data to disk
Port B:
    Reading data from disk
Port C:
    READING:
        7 - / OF A data write to disk,
        6 - INTE A,
        5 - Select the drive A, B,
        4 - Not connected
        3 - INTR A
        2 - INTE B read data,
        1 - IBF B read data,
        0 - INTR B

    WRITING:
        6 - INTE A - reading data
        2 - INTE B - writing data
*/

void fk1_state::ppi2_a_w(uint8_t data)
{
//  logerror("write to disk %02x\n",data);
}

void fk1_state::ppi2_c_w(uint8_t data)
{
//  logerror("fk1_ppi_2_c_w %02x\n",data);
}

uint8_t fk1_state::ppi2_b_r()
{
//  logerror("read from disk\n");
	return 0;
}

uint8_t fk1_state::ppi2_c_r()
{
//  logerror("fk1_ppi_2_c_r\n");
	return 0;
}


/*

Port A:
    6 - / disk, authorization disk operations,
    3 - INTE MOUSE, permit suspension from mouse,
    2 - INTE RTC from the system clock of 50 Hz,
    1 and 0 - to set the type for hours write to the disk (01 index, 11 address mark, mark the date, 00 other).

Port B:
    Video ROL register
Port C

    READING:
    7 - INDEX ( index mark on the disk)
    6 - I do not know,
    5 - WRITE_PROTECT the protected disk,
    4 - TRAC_00 as a sign of trace

    WRITING:
    3 - HEAD LOAD
    2 - TRACK_43
    1 - DIRC - direction to set the direction of stepping disk
    0 - STEP, move disk (0 1 .. 0.).

*/
void fk1_state::ppi3_a_w(uint8_t data)
{
//  logerror("fk1_ppi_3_a_w %02x\n",data);
}

void fk1_state::ppi3_b_w(uint8_t data)
{
	m_video_rol = data;
}

void fk1_state::ppi3_c_w(uint8_t data)
{
//  logerror("fk1_ppi_3_c_w %02x\n",data);
}

uint8_t fk1_state::ppi3_a_r()
{
//  logerror("fk1_ppi_3_a_r\n");
	return 0;
}

uint8_t fk1_state::ppi3_b_r()
{
	return m_video_rol;
}

uint8_t fk1_state::ppi3_c_r()
{
//  logerror("fk1_ppi_3_c_r\n");
	return 0;
}

void fk1_state::pit_out0(int state)
{
	// System time
	logerror("fk1_pit_out0\n");
}

void fk1_state::pit_out1(int state)
{
	// Timeout for disk operation
	logerror("fk1_pit_out1\n");
}

void fk1_state::pit_out2(int state)
{
	// Overflow for disk operations
	logerror("fk1_pit_out2\n");
}

/*
    0 no interrupt allowed,
    1 allowed INTR-7,
    2 allowed INTR-7 and INTR-6,
    8 any interruption allowed.
*/

void fk1_state::intr_w(uint8_t data)
{
	logerror("fk1_intr_w %02x\n",data);
}

uint8_t fk1_state::bank_ram_r()
{
	m_bankr->set_entry(0);
	m_bank2->set_entry(0);
	return 0;
}

uint8_t fk1_state::bank_rom_r()
{
	m_bankr->set_entry(1);
	m_bank2->set_entry(1);
	return 0;
}

/*
    4 - FORMAT authorization
    3 - READ_ADDRESS_MARK
    2 - READ_DATA_MARK
    1 - WRITE
    0 - READ
    Functions are allowed in one.
*/

void fk1_state::disk_w(uint8_t data)
{
//  logerror("fk1_disk_w %02x\n",data);
}

/*
7 and 6 - 1 to connected mouse and 0 if not connected,
5 - / T2, right-click
4 - / T1, left-click
3 - / BY, Y-axis
2 - / AY, Y-axis
1 - / BX, X-axis
0 - / AX, X-axis
*/

uint8_t fk1_state::mouse_r()
{
//  logerror("fk1_mouse_r\n");
	return 0;
}

/*Write to port 70 resets the interrupt from the system clock of 50 Hz. */

void fk1_state::reset_int_w(uint8_t data)
{
	logerror("fk1_reset_int_w\n");
}

void fk1_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).bankr("bankr1").bankw("bankw1");
	map(0x4000, 0x7fff).bankrw("bank2");
	map(0x8000, 0xbfff).ram();
	map(0xc000, 0xffff).ram();
}

void fk1_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x03).rw("ppi1", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x10, 0x13).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x20, 0x23).rw("ppi2", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x30, 0x30).rw(FUNC(fk1_state::bank_ram_r), FUNC(fk1_state::intr_w));
	map(0x40, 0x41).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x50, 0x50).rw(FUNC(fk1_state::bank_rom_r), FUNC(fk1_state::disk_w));
	map(0x60, 0x63).rw("ppi3", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x70, 0x70).rw(FUNC(fk1_state::mouse_r), FUNC(fk1_state::reset_int_w));
}

/* Input ports */
static INPUT_PORTS_START( fk1 )
	PORT_START("LINE0")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR(0xA4)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
INPUT_PORTS_END

TIMER_DEVICE_CALLBACK_MEMBER(fk1_state::keyboard_callback)
{
	if (ioport("LINE0")->read())
	{
		m_int_vector = 6;
		m_maincpu->set_input_line(0, HOLD_LINE);
	}
}

/*
7 ? DATA_READY
6 ? TIMEOUT
5 ? OVERFLOW
4 ? RTC_50HZ
3 ? MOUSE
2 ? SIO
1 ? KEYBOARD
0 ? PRINTER
*/

IRQ_CALLBACK_MEMBER(fk1_state::irq_callback)
{
	logerror("IRQ %02x\n", m_int_vector*2);
	return m_int_vector * 2;
}

TIMER_DEVICE_CALLBACK_MEMBER(fk1_state::vsync_callback)
{
	m_int_vector = 3;
	m_maincpu->set_input_line(0, HOLD_LINE);
}


void fk1_state::machine_start()
{
	save_item(NAME(m_video_rol));
	save_item(NAME(m_int_vector));
	u8 *r = m_ram->pointer();
	m_bankr->configure_entry(0, r);
	m_bankr->configure_entry(1, m_rom);
	m_bankw->configure_entry(0, r);
	m_bank2->configure_entry(0, r+0x4000);
	m_bank2->configure_entry(1, r+0x8000);
}

void fk1_state::machine_reset()
{
	m_bankr->set_entry(1);
	m_bankw->set_entry(0);
	m_bank2->set_entry(1);
}

uint32_t fk1_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint8_t const *const ram = m_ram->pointer();

	for (int x = 0; x < 64; x++)
	{
		for (int y = 0; y < 256; y++)
		{
			uint8_t code = ram[x * 0x100 + ((y + m_video_rol) & 0xff) + 0x8000];
			for (int b = 0; b < 8; b++)
				bitmap.pix(y, x*8+b) =  ((code << b) & 0x80) ? 1 : 0;
		}
	}
	return 0;
}

void fk1_state::fk1(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(8'000'000) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &fk1_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &fk1_state::io_map);
	m_maincpu->set_irq_acknowledge_callback(FUNC(fk1_state::irq_callback));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(fk1_state::screen_update));
	screen.set_size(512, 256);
	screen.set_visarea(0, 512-1, 0, 256-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	pit8253_device &pit8253(PIT8253(config, "pit", 0));
	pit8253.set_clk<0>(50);
	pit8253.out_handler<0>().set(FUNC(fk1_state::pit_out0));
	pit8253.set_clk<1>(1000000);
	pit8253.out_handler<1>().set(FUNC(fk1_state::pit_out1));
	pit8253.set_clk<2>(0);
	pit8253.out_handler<2>().set(FUNC(fk1_state::pit_out2));

	i8255_device &ppi1(I8255(config, "ppi1"));
	ppi1.in_pa_callback().set(FUNC(fk1_state::ppi1_a_r));
	ppi1.out_pa_callback().set(FUNC(fk1_state::ppi1_a_w));
	ppi1.in_pb_callback().set(FUNC(fk1_state::ppi1_b_r));
	ppi1.out_pb_callback().set(FUNC(fk1_state::ppi1_b_w));
	ppi1.in_pc_callback().set(FUNC(fk1_state::ppi1_c_r));
	ppi1.out_pc_callback().set(FUNC(fk1_state::ppi1_c_w));

	i8255_device &ppi2(I8255(config, "ppi2"));
	ppi2.out_pa_callback().set(FUNC(fk1_state::ppi2_a_w));
	ppi2.in_pb_callback().set(FUNC(fk1_state::ppi2_b_r));
	ppi2.in_pc_callback().set(FUNC(fk1_state::ppi2_c_r));
	ppi2.out_pc_callback().set(FUNC(fk1_state::ppi2_c_w));

	i8255_device &ppi3(I8255(config, "ppi3"));
	ppi3.in_pa_callback().set(FUNC(fk1_state::ppi3_a_r));
	ppi3.out_pa_callback().set(FUNC(fk1_state::ppi3_a_w));
	ppi3.in_pb_callback().set(FUNC(fk1_state::ppi3_b_r));
	ppi3.out_pb_callback().set(FUNC(fk1_state::ppi3_b_w));
	ppi3.in_pc_callback().set(FUNC(fk1_state::ppi3_c_r));
	ppi3.out_pc_callback().set(FUNC(fk1_state::ppi3_c_w));

	/* uart */
	I8251(config, "uart", 0);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("48K");  // 32 for banks1,2 + 16 for vram

	TIMER(config, "keyboard_timer").configure_periodic(FUNC(fk1_state::keyboard_callback), attotime::from_hz(300));
	TIMER(config, "vsync_timer").configure_periodic(FUNC(fk1_state::vsync_callback), attotime::from_hz(50));
}

/* ROM definition */
ROM_START( fk1 )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "orig", "Original BIOS" )
	ROMX_LOAD( "fk1.u65",      0x0000, 0x0800, CRC(145561f8) SHA1(a4eb17d773e51b34620c508b6cebcb4531ae99c2), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "diag", "Diag BIOS" )
	ROMX_LOAD( "fk1-diag.u65", 0x0000, 0x0800, CRC(e0660ae1) SHA1(6ad609049b28f27126af0a8a6224362351073dee), ROM_BIOS(1))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY                  FULLNAME  FLAGS
COMP( 1989, fk1,  0,      0,      fk1,     fk1,   fk1_state, empty_init, "Statni statek Klicany", "FK-1",   MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



fm7.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Barry Rodewald
/***********************************************************************************************

    Fujitsu Micro 7 (FM-7)

    12/05/2009 Skeleton driver.

    Computers in this series:

                 | Release |    Main CPU    |  Sub CPU  |              RAM              |
    =====================================================================================
    FM-8         | 1981-05 | M68A09 @ 1MHz  |  M6809    |    64K (main) + 48K (VRAM)    |
    FM-7         | 1982-11 | M68B09 @ 2MHz  |  M68B09   |    64K (main) + 48K (VRAM)    |
    FM-NEW7      | 1984-05 | M68B09 @ 2MHz  |  M68B09   |    64K (main) + 48K (VRAM)    |
    FM-77        | 1984-05 | M68B09 @ 2MHz  |  M68B09E  |  64/256K (main) + 48K (VRAM)  |
    FM-77AV      | 1985-10 | M68B09E @ 2MHz |  M68B09E  | 128/192K (main) + 96K (VRAM)  |
    FM-77AV20    | 1986-10 | M68B09E @ 2MHz |  M68B09E  | 128/192K (main) + 96K (VRAM)  |
    FM-77AV40    | 1986-10 | M68B09E @ 2MHz |  M68B09E  | 192/448K (main) + 144K (VRAM) |
    FM-77AV20EX  | 1987-11 | M68B09E @ 2MHz |  M68B09E  | 128/192K (main) + 96K (VRAM)  |
    FM-77AV40EX  | 1987-11 | M68B09E @ 2MHz |  M68B09E  | 192/448K (main) + 144K (VRAM) |
    FM-77AV40SX  | 1988-11 | M68B09E @ 2MHz |  M68B09E  | 192/448K (main) + 144K (VRAM) |

    Note: FM-77AV dumps probably come from a FM-77AV40SX. Shall we confirm that both computers
    used the same BIOS components?

    memory map info from http://www.nausicaa.net/~lgreenf/fm7page.htm
    see also http://retropc.net/ryu/xm7/xm7.shtml


    Known issues:
     - Beeper is not implemented
     - Keyboard repeat is not implemented
     - Optional Kanji ROM use is not implemented
     - Other optional hardware is not implemented (RS232, Z80 card...)
     - FM-77AV20 and later aren't working (extra features not yet implemented)

************************************************************************************************/

#include "emu.h"
#include "fm7.h"

#include "cpu/m6809/m6809.h"
#include "cpu/i86/i86.h"
#include "cpu/z80/z80.h"

#include "sound/ay8910.h"
#include "sound/beep.h"
#include "sound/ymopn.h"

#include "bus/centronics/dsjoy.h"

#include "imagedev/cassette.h"

#include "formats/fm7_cas.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


/* key scancode conversion table
 * The FM-7 expects different scancodes when shift,ctrl or graph is held, or
 * when kana is active.
 */
	// TODO: fill in shift,ctrl,graph and kana code
static const uint16_t fm7_key_list[0x60][7] =
{ // norm  shift ctrl  graph kana  sh.kana scan
	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
	{0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x01},  // ESC
	{0x31, 0x21, 0xf9, 0xf9, 0xc7, 0x00, 0x02},  // 1
	{0x32, 0x22, 0xfa, 0xfa, 0xcc, 0x00, 0x03},  // 2
	{0x33, 0x23, 0xfb, 0xfb, 0xb1, 0xa7, 0x04},
	{0x34, 0x24, 0xfc, 0xfc, 0xb3, 0xa9, 0x05},
	{0x35, 0x25, 0xf2, 0xf2, 0xb4, 0xaa, 0x06},
	{0x36, 0x26, 0xf3, 0xf3, 0xb5, 0xab, 0x07},
	{0x37, 0x27, 0xf4, 0xf4, 0xd4, 0xac, 0x08},
	{0x38, 0x28, 0xf5, 0xf5, 0xd5, 0xad, 0x09},
	{0x39, 0x29, 0xf6, 0xf6, 0xd6, 0xae, 0x0a},  // 9
	{0x30, 0x00, 0xf7, 0xf7, 0xdc, 0xa6, 0x0b},  // 0
	{0x2d, 0x3d, 0x1e, 0x8c, 0xce, 0x00, 0x0c},  // -
	{0x5e, 0x7e, 0x1c, 0x8b, 0xcd, 0x00, 0x0d},  // ^
	{0x5c, 0x7c, 0xf1, 0xf1, 0xb0, 0x00, 0x0e},  // Yen
	{0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0f},  // Backspace
	{0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x10},  // Tab
	{0x71, 0x51, 0x11, 0xfd, 0xc0, 0x00, 0x11},  // Q
	{0x77, 0x57, 0x17, 0xf8, 0xc3, 0x00, 0x12},  // W
	{0x65, 0x45, 0x05, 0xe4, 0xb2, 0xa8, 0x13},  // E
	{0x72, 0x52, 0x12, 0xe5, 0xbd, 0x00, 0x14},
	{0x74, 0x54, 0x14, 0x9c, 0xb6, 0x00, 0x15},
	{0x79, 0x59, 0x19, 0x9d, 0xdd, 0x00, 0x16},
	{0x75, 0x55, 0x15, 0xf0, 0xc5, 0x00, 0x17},
	{0x69, 0x49, 0x09, 0xe8, 0xc6, 0x00, 0x18},
	{0x6f, 0x4f, 0x0f, 0xe9, 0xd7, 0x00, 0x19},
	{0x70, 0x50, 0x10, 0x8d, 0xbe, 0x00, 0x1a},  // P
	{0x40, 0x60, 0x00, 0x8a, 0xde, 0x00, 0x1b},  // @
	{0x5b, 0x7b, 0x1b, 0xed, 0xdf, 0xa2, 0x1c},  // [
	{0x0d, 0x0d, 0x00, 0x0d, 0x0d, 0x0d, 0x1d},  // Return
	{0x61, 0x41, 0x01, 0x95, 0xc1, 0x00, 0x1e},  // A
	{0x73, 0x53, 0x13, 0x96, 0xc4, 0x00, 0x1f},  // S

	{0x64, 0x44, 0x04, 0xe6, 0xbc, 0x00, 0x20},  // D
	{0x66, 0x46, 0x06, 0xe7, 0xca, 0x00, 0x21},
	{0x67, 0x47, 0x07, 0x9e, 0xb7, 0x00, 0x22},
	{0x68, 0x48, 0x08, 0x9f, 0xb8, 0x00, 0x23},
	{0x6a, 0x4a, 0x0a, 0xea, 0xcf, 0x00, 0x24},
	{0x6b, 0x4b, 0x0b, 0xeb, 0xc9, 0x00, 0x25},
	{0x6c, 0x4c, 0x0c, 0x8e, 0xd8, 0x00, 0x26},  // L
	{0x3b, 0x2b, 0x00, 0x99, 0xda, 0x00, 0x27},  // ;
	{0x3a, 0x2a, 0x00, 0x94, 0xb9, 0x00, 0x28},  // :
	{0x5d, 0x7d, 0x1d, 0xec, 0xd1, 0xa3, 0x29},  // ]
	{0x7a, 0x5a, 0x1a, 0x80, 0xc2, 0xaf, 0x2a},  // Z
	{0x78, 0x58, 0x18, 0x81, 0xbb, 0x00, 0x2b},  // X
	{0x63, 0x43, 0x03, 0x82, 0xbf, 0x00, 0x2c},  // C
	{0x76, 0x56, 0x16, 0x83, 0xcb, 0x00, 0x2d},
	{0x62, 0x42, 0x02, 0x84, 0xba, 0x00, 0x2e},
	{0x6e, 0x4e, 0x0e, 0x85, 0xd0, 0x00, 0x2f},
	{0x6d, 0x4d, 0x0d, 0x86, 0xd3, 0x00, 0x30},  // M
	{0x2c, 0x3c, 0x00, 0x87, 0xc8, 0xa4, 0x31},  // <
	{0x2e, 0x3e, 0x00, 0x88, 0xd9, 0xa1, 0x32},  // >
	{0x2f, 0x3f, 0x00, 0x97, 0xd2, 0xa5, 0x33},  // /
	{0x22, 0x5f, 0x1f, 0xe0, 0xdb, 0x00, 0x34},  // "
	{0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x35},  // Space
	{0x2a, 0x2a, 0x00, 0x98, 0x2a, 0x2a, 0x36},  // Tenkey
	{0x2f, 0x2f, 0x00, 0x91, 0x2f, 0x2f, 0x37},
	{0x2b, 0x2b, 0x00, 0x99, 0x2b, 0x2b, 0x38},
	{0x2d, 0x2d, 0x00, 0xee, 0x2d, 0x2d, 0x39},
	{0x37, 0x37, 0x00, 0xe1, 0x37, 0x37, 0x3a},
	{0x38, 0x38, 0x00, 0xe2, 0x38, 0x38, 0x3b},
	{0x39, 0x39, 0x00, 0xe3, 0x39, 0x39, 0x3c},
	{0x3d, 0x3d, 0x00, 0xef, 0x3d, 0x3d, 0x3d},  // Tenkey =
	{0x34, 0x34, 0x00, 0x93, 0x34, 0x34, 0x3e},
	{0x35, 0x35, 0x00, 0x8f, 0x35, 0x35, 0x3f},

	{0x36, 0x36, 0x00, 0x92, 0x36, 0x36, 0x40},
	{0x2c, 0x2c, 0x00, 0x00, 0x2c, 0x2c, 0x41},
	{0x31, 0x31, 0x00, 0x9a, 0x31, 0x31, 0x42},
	{0x32, 0x32, 0x00, 0x90, 0x32, 0x32, 0x43},
	{0x33, 0x33, 0x00, 0x9b, 0x33, 0x33, 0x44},
	{0x0d, 0x0d, 0x00, 0x0d, 0x0d, 0x0d, 0x45},
	{0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x46},
	{0x2e, 0x2e, 0x00, 0x2e, 0x2e, 0x2e, 0x47},
	{0x12, 0x12, 0x00, 0x12, 0x12, 0x12, 0x48}, // INS
	{0x05, 0x05, 0x00, 0x05, 0x05, 0x05, 0x49},  // EL
	{0x0c, 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0x4a},  // CLS
	{0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x7f, 0x4b},  // DEL
	{0x11, 0x11, 0x00, 0x11, 0x11, 0x11, 0x4c},  // DUP
	{0x1e, 0x19, 0x00, 0x1e, 0x1e, 0x19, 0x4d},  // Cursor Up
	{0x0b, 0x0b, 0x00, 0x0b, 0x0b, 0x0b, 0x4e},  // HOME
	{0x1d, 0x02, 0x00, 0x1d, 0x1d, 0x02, 0x4f},  // Cursor Left
	{0x1f, 0x1a, 0x00, 0x1f, 0x1f, 0x1a, 0x50},  // Cursor Down
	{0x1c, 0x06, 0x00, 0x1c, 0x1c, 0x16, 0x51},  // Cursor Right
	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c},  // BREAK
	{0x101, 0x00, 0x101, 0x101, 0x101, 0x00, 0x5d},  // PF1
	{0x102, 0x00, 0x102, 0x102, 0x102, 0x00, 0x5e},
	{0x103, 0x00, 0x103, 0x103, 0x103, 0x00, 0x5f},
	{0x104, 0x00, 0x104, 0x104, 0x104, 0x00, 0x60},
	{0x105, 0x00, 0x105, 0x105, 0x105, 0x00, 0x61},
	{0x106, 0x00, 0x106, 0x106, 0x106, 0x00, 0x62},
	{0x107, 0x00, 0x107, 0x107, 0x107, 0x00, 0x63},
	{0x108, 0x00, 0x108, 0x108, 0x108, 0x00, 0x64},
	{0x109, 0x00, 0x109, 0x109, 0x109, 0x00, 0x65},
	{0x10a, 0x00, 0x10a, 0x10a, 0x10a, 0x00, 0x66},  // PF10
	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
};


void fm7_state::main_irq_set_flag(uint8_t flag)
{
	m_irq_flags |= flag;

	if(m_irq_flags != 0)
		m_maincpu->set_input_line(M6809_IRQ_LINE,ASSERT_LINE);
}

void fm7_state::main_irq_clear_flag(uint8_t flag)
{
	m_irq_flags &= ~flag;

	if(m_irq_flags == 0)
		m_maincpu->set_input_line(M6809_IRQ_LINE,CLEAR_LINE);
}


/*
 * Main CPU: I/O port 0xfd02
 *
 * On read: returns cassette data (bit 7) and printer status (bits 0-5)
 * On write: sets IRQ masks
 *   bit 0 - keypress
 *   bit 1 - printer
 *   bit 2 - timer
 *   bit 3 - not used
 *   bit 4 - MFD
 *   bit 5 - TXRDY
 *   bit 6 - RXRDY
 *   bit 7 - SYNDET
 *
 */
void fm7_state::irq_mask_w(uint8_t data)
{
	m_irq_mask = data;
	logerror("IRQ mask set: 0x%02x\n",m_irq_mask);
}

/*
 * Main CPU: I/O port 0xfd03
 *
 * On read: returns which IRQ is currently active (typically read by IRQ handler)
 *   bit 0 - keypress
 *   bit 1 - printer
 *   bit 2 - timer
 *   bit 3 - ???
 * On write: Buzzer/Speaker On/Off
 *   bit 0 - speaker on/off
 *   bit 6 - buzzer on for 205ms
 *   bit 7 - buzzer on/off
 */
uint8_t fm7_state::irq_cause_r()
{
	uint8_t ret = ~m_irq_flags;

	// Timer and Printer IRQ flags are cleared when this port is read
	// Keyboard IRQ flag is cleared when the scancode is read from
	// either keyboard data port (main CPU 0xfd01 or sub CPU 0xd401)
	if(m_irq_flags & 0x04)
		main_irq_clear_flag(IRQ_FLAG_TIMER);
	if(m_irq_flags & 0x02)
		main_irq_clear_flag(IRQ_FLAG_PRINTER);

	logerror("IRQ flags read: 0x%02x\n",ret);
	return ret;
}

TIMER_CALLBACK_MEMBER(fm7_state::beeper_off)
{
	m_beeper->set_state(0);
	logerror("timed beeper off\n");
}

void fm7_state::beeper_w(uint8_t data)
{
	m_speaker_active = data & 0x01;

	if(!m_speaker_active)  // speaker not active, disable all beeper sound
	{
		m_beeper->set_state(0);
		return;
	}

	if(data & 0x80)
	{
		if(m_speaker_active)
			m_beeper->set_state(1);
	}
	else
		m_beeper->set_state(0);

	if(data & 0x40)
	{
		if(m_speaker_active)
		{
			m_beeper->set_state(1);
			logerror("timed beeper on\n");
			m_beeper_off_timer->adjust(attotime::from_msec(205));
		}
	}
	logerror("beeper state: %02x\n",data);
}


/*
 *  Sub CPU: port 0xd403 (read-only)
 *  On read: timed buzzer sound
 */
uint8_t fm7_state::sub_beeper_r()
{
	if(m_speaker_active)
	{
		m_beeper->set_state(1);
		logerror("timed beeper on\n");
		m_beeper_off_timer->adjust(attotime::from_msec(205));
	}
	return 0xff;
}

uint8_t fm77_state::vector_r(offs_t offset)
{
	uint32_t init_size = m_rom_ptr.bytes();

	if (m_init_rom_en)
	{
		return m_rom_ptr[(init_size-0x10)+offset];
	}
	else
	{
		return m_vectors[offset];
	}
}

/*
 * Main CPU: I/O port 0xfd04
 *
 *  bit 0 - attention IRQ active, clears flag when read.
 *  bit 1 - break key active
 */
uint8_t fm7_state::fd04_r()
{
	uint8_t ret = 0xff;

	if(m_video.attn_irq != 0)
	{
		ret &= ~0x01;
		m_video.attn_irq = 0;
	}
	if(m_break_flag != 0)
	{
		ret &= ~0x02;
	}
	return ret;
}

/*
 *  Main CPU: I/O port 0xfd0f
 *
 *  On read, enables BASIC ROM at 0x8000 (default)
 *  On write, disables BASIC ROM, enables RAM (if more than 32kB)
 */
uint8_t fm7_state::rom_en_r(address_space &space)
{
	if(!machine().side_effects_disabled())
	{
		m_basic_rom_en = true;
		if(m_type == SYS_FM7)
		{
			membank("bank1")->set_base(&m_basic_ptr[0]);
		}
		else
			fm7_mmr_refresh(space);
		logerror("BASIC ROM enabled\n");
	}
	return 0x00;
}

void fm7_state::rom_en_w(address_space &space, uint8_t data)
{
	m_basic_rom_en = false;
	if(m_type == SYS_FM7)
	{
		membank("bank1")->set_base(&m_a15_ram[0]);
	}
	else
		fm7_mmr_refresh(space);
	logerror("BASIC ROM disabled\n");
}

/*
 *  Main CPU: port 0xfd10
 *  Initiate ROM enable. (FM-77AV and later only)
 *  Port is write-only.  Initiate ROM is on by default.
 *
 */
void fm77_state::init_en_w(address_space &space, uint8_t data)
{
	if(data & 0x02)
	{
		m_init_rom_en = false;
		fm7_mmr_refresh(space);
	}
	else
	{
		m_init_rom_en = true;
		fm7_mmr_refresh(space);
	}
}

/*
 *  Main CPU: I/O ports 0xfd18 - 0xfd1f
 *  Floppy Disk Controller (MB8877A)
 */
void fm7_state::fdc_intrq_w(int state)
{
	m_fdc_irq_flag = state;
}

void fm7_state::fdc_drq_w(int state)
{
	m_fdc_drq_flag = state;
}

uint8_t fm7_state::fdc_r(offs_t offset)
{
	uint8_t ret = 0;

	switch(offset)
	{
		case 0:
			return m_fdc->status_r();
		case 1:
			return m_fdc->track_r();
		case 2:
			return m_fdc->sector_r();
		case 3:
			return m_fdc->data_r();
		case 4:
			return m_fdc_side | 0xfe;
		case 5:
			return m_fdc_drive;
		case 6:
			// FM-7 always returns 0xff for this register
			return 0xff;
		case 7:
			if(m_fdc_irq_flag != 0)
				ret |= 0x40;
			if(m_fdc_drq_flag != 0)
				ret |= 0x80;
			return ret;
	}
	logerror("FDC: read from 0x%04x\n",offset+0xfd18);

	return 0x00;
}

void fm7_state::fdc_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
		case 0:
			m_fdc->cmd_w(data);
			break;
		case 1:
			m_fdc->track_w(data);
			break;
		case 2:
			m_fdc->sector_w(data);
			break;
		case 3:
			m_fdc->data_w(data);
			break;
		case 4:
			m_fdc_side = data & 0x01;
			if (m_floppy)
				m_floppy->ss_w(data & 0x01);
			logerror("FDC: wrote %02x to 0x%04x (side)\n",data,offset+0xfd18);
			break;
		case 5:
			m_fdc_drive = data;
			if((data & 0x03) > 0x01)
			{
				m_fdc_drive = 0;
			}
			else
			{
				switch (data & 0x01)
				{
				case 0: m_floppy = m_floppy0->get_device(); break;
				case 1: m_floppy = m_floppy1->get_device(); break;
				}

				m_fdc->set_floppy(m_floppy);

				if (m_floppy)
					m_floppy->mon_w(!BIT(data, 7));

				logerror("FDC: wrote %02x to 0x%04x (drive)\n",data,offset+0xfd18);
			}
			break;
		case 6:
			// FM77AV and later only. FM-7 returns 0xff;
			// bit 6 = 320k(1)/640k(0) FDD
			// bits 2,3 = logical drive
			logerror("FDC: mode write - %02x\n",data);
			break;
		default:
			logerror("FDC: wrote %02x to 0x%04x\n",data,offset+0xfd18);
	}
}

/*
 *  Main CPU: I/O ports 0xfd00-0xfd01
 *  Sub CPU: I/O ports 0xd400-0xd401
 *
 *  The scancode of the last key pressed is stored in fd/d401, with the 9th
 *  bit (MSB) in bit 7 of fd/d400.  0xfd00 also holds a flag for the main
 *  CPU clock speed in bit 0 (0 = 1.2MHz, 1 = 2MHz)
 *  Clears keyboard IRQ flag
 */
uint8_t fm7_state::keyboard_r(offs_t offset)
{
	uint8_t ret;
	switch(offset)
	{
		case 0:
			ret = (m_current_scancode >> 1) & 0x80;
			ret |= 0x01; // 1 = 2MHz, 0 = 1.2MHz
			return ret;
		case 1:
			main_irq_clear_flag(IRQ_FLAG_KEY);
			return m_current_scancode & 0xff;
		default:
			return 0x00;
	}
}

uint8_t fm7_state::sub_keyboard_r(offs_t offset)
{
	uint8_t ret;
	switch(offset)
	{
		case 0:
			ret = (m_current_scancode >> 1) & 0x80;
			return ret;
		case 1:
			main_irq_clear_flag(IRQ_FLAG_KEY);
			return m_current_scancode & 0xff;
		default:
			return 0x00;
	}
}

/*
 *  Sub CPU: port 0xd431, 0xd432
 *  Keyboard encoder
 *
 *  d431 (R/W): Data register (8 bit)
 *  d432 (R/O): Status register
 *              bit 0 - ACK
 *              bit 7 - LATCH (0 if ready to receive)
 *
 *  Encoder commands:
 *      00 xx    : Set scancode format (FM-7, FM16B(?), Scan(Make/Break))
 *      01       : Get scancode format
 *      02 xx    : Set LED status
 *      03       : Get LED status
 *      04 xx    : Enable/Disable key repeat
 *      05 xx xx : Set repeat rate and time
 *      80 00    : Get RTC
 *      80 01 xx xx xx xx xx xx xx : Set RTC
 *      81 xx    : Video digitise
 *      82 xx    : Set video mode(?)
 *      83       : Get video mode(?)
 *      84 xx    : Video brightness (monitor?)
 *
 *  ACK is received after 5us.
 */
uint8_t fm77_state::av_key_encoder_r(offs_t offset)
{
	uint8_t ret = 0xff;
	switch(offset)
	{
		case 0x00:  // data register
			if(m_encoder.rx_count > 0)
			{
				ret = m_encoder.buffer[m_encoder.position];
				m_encoder.position++;
				m_encoder.rx_count--;
				m_encoder.latch = 0;
			}
			if(m_encoder.rx_count > 0)
				m_encoder.latch = 1;  // more data to receive
			break;
		case 0x01:  // status register
			if(m_encoder.latch != 0)
				ret &= ~0x80;
			if(m_encoder.ack == 0)
				ret &= ~0x01;
			break;
	}

	return ret;
}

void fm77_state::av_encoder_setup_command()
{
	switch(m_encoder.buffer[0])
	{
		case 0:  // set scancode format
			m_encoder.tx_count = 2;
			break;
		case 1:  // get scancode format
			m_encoder.tx_count = 1;
			break;
		case 2:  // set LED
			m_encoder.tx_count = 2;
			break;
		case 3:  // get LED
			m_encoder.tx_count = 1;
			break;
		case 4:  // enable repeat
			m_encoder.tx_count = 2;
			break;
		case 5:  // set repeat rate
			m_encoder.tx_count = 3;
			break;
		case 0x80:  // get/set RTC (at least two bytes, 9 if byte two = 0x01)
			m_encoder.tx_count = 2;
			break;
		case 0x81:  // digitise
			m_encoder.tx_count = 2;
			break;
		case 0x82:  // set screen mode
			m_encoder.tx_count = 2;
			break;
		case 0x83:  // get screen mode
			m_encoder.tx_count = 1;
			break;
		case 0x84:  // set monitor brightness
			m_encoder.tx_count = 2;
			break;
		default:
			m_encoder.tx_count = 0;
			m_encoder.rx_count = 0;
			m_encoder.position = 0;
			logerror("ENC: Unknown command 0x%02x sent, ignoring\n",m_encoder.buffer[0]);
	}
}

TIMER_CALLBACK_MEMBER(fm77_state::av_encoder_ack)
{
	m_encoder.ack = 1;
}

void fm77_state::av_encoder_handle_command()
{
	switch(m_encoder.buffer[0])
	{
		case 0:  // set keyboard scancode mode
			m_key_scan_mode = m_encoder.buffer[1];
			m_encoder.rx_count = 0;
			logerror("ENC: Keyboard set to mode %i\n",m_encoder.buffer[1]);
			break;
		case 1:  // get keyboard scancode mode
			m_encoder.buffer[0] = m_key_scan_mode;
			m_encoder.rx_count = 1;
			logerror("ENC: Command %02x received\n",m_encoder.buffer[0]);
			break;
		case 2:  // set LEDs
			m_encoder.rx_count = 0;
			logerror("ENC: Command %02x received\n",m_encoder.buffer[0]);
			break;
		case 3:  // get LEDs
			m_encoder.rx_count = 1;
			logerror("ENC: Command %02x received\n",m_encoder.buffer[0]);
			break;
		case 4:  // enable key repeat
			m_encoder.rx_count = 0;
			logerror("ENC: Command %02x received\n",m_encoder.buffer[0]);
			break;
		case 5:  // set key repeat rate
			m_key_repeat = m_encoder.buffer[2] * 10;
			m_key_delay = m_encoder.buffer[1] * 10;
			m_encoder.rx_count = 0;
			logerror("ENC: Keyboard repeat rate set to %i/%i\n",m_encoder.buffer[1],m_encoder.buffer[2]);
			break;
		case 0x80:  // get/set RTC
			if(m_encoder.buffer[1] == 0x01)
				m_encoder.rx_count = 0;
			else
				m_encoder.rx_count = 7;
			logerror("ENC: Command %02x %02x received\n",m_encoder.buffer[0],m_encoder.buffer[1]);
			break;
		case 0x81:  // digitise
			m_encoder.rx_count = 0;
			logerror("ENC: Command %02x received\n",m_encoder.buffer[0]);
			break;
		case 0x82:  // set screen mode
			m_encoder.rx_count = 0;
			logerror("ENC: Command %02x received\n",m_encoder.buffer[0]);
			break;
		case 0x83:  // get screen mode
			m_encoder.rx_count = 1;
			logerror("ENC: Command %02x received\n",m_encoder.buffer[0]);
			break;
		case 0x84:  // set monitor brightness
			m_encoder.rx_count = 0;
			logerror("ENC: Command %02x received\n",m_encoder.buffer[0]);
			break;
	}
	m_encoder.position = 0;
}

void fm77_state::av_key_encoder_w(offs_t offset, uint8_t data)
{
	m_encoder.ack = 0;
	if(offset == 0) // data register
	{
		if(m_encoder.position == 0)  // first byte
		{
			av_encoder_setup_command();
		}
		if(m_encoder.position == 1)  // second byte
		{
			if(m_encoder.buffer[0] == 0x80 || m_encoder.buffer[1] == 0x01)
			{
				m_encoder.tx_count = 8; // 80 01 command is 9 bytes
			}
		}
		m_encoder.buffer[m_encoder.position] = data;
		m_encoder.position++;
		m_encoder.tx_count--;
		if(m_encoder.tx_count == 0)  // last byte
			av_encoder_handle_command();

		// wait 5us to set ACK flag
		m_encoder_ack_timer->adjust(attotime::from_usec(5));

		//logerror("ENC: write 0x%02x to data register, moved to pos %i\n",data,m_encoder.position);
	}
}

void fm7_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

void fm7_state::write_centronics_fault(int state)
{
	m_centronics_fault = state;
}

void fm7_state::write_centronics_ack(int state)
{
	m_centronics_ack = state;
}

void fm7_state::write_centronics_perror(int state)
{
	m_centronics_perror = state;
}

uint8_t fm7_state::cassette_printer_r()
{
	// bit 7: cassette input
	// bit 5: printer DET2
	// bit 4: printer DTT1
	// bit 3: printer PE
	// bit 2: printer acknowledge
	// bit 1: printer error
	// bit 0: printer busy
	uint8_t ret = 0;

	if(m_cassette->input() > 0.03)
		ret |= 0x80;

	if(m_cassette->get_state() & CASSETTE_MOTOR_DISABLED)
		ret |= 0x80;  // cassette input is high when not in use.

	ret |= 0x70;
	ret |= m_centronics_perror << 3;
	ret |= m_centronics_ack << 2;
	ret |= m_centronics_fault << 1;
	ret |= m_centronics_busy;

	return ret;
}

void fm7_state::cassette_printer_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
		case 0:
		// bit 7: SLCTIN (select?)
		// bit 6: printer strobe
		// bit 1: cassette motor
		// bit 0: cassette output
			if((data & 0x01) != (m_cp_prev & 0x01))
				m_cassette->output((data & 0x01) ? +1.0 : -1.0);
			if((data & 0x02) != (m_cp_prev & 0x02))
				m_cassette->change_state((data & 0x02) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR);

			m_centronics->write_strobe(!BIT(data,6));
			m_centronics->write_select_in(!BIT(data,7));
			m_cp_prev = data;
			break;
		case 1:
		// Printer data
			m_cent_data_out->write(data);
			break;
	}
}

/*
 *  Main CPU: 0xfd0b
 *   - bit 0: Boot mode: 0=BASIC, 1=DOS
 */
uint8_t fm77_state::av_boot_mode_r()
{
	uint8_t ret = 0xff;

	if(m_dsw->read() & 0x02)
		ret &= ~0x01;

	return ret;
}

/*
 *  Main CPU: I/O ports 0xfd0d-0xfd0e
 *  PSG AY-3-891x (FM-7), YM2203 - (FM-77AV and later)
 *  0xfd0d - function select (bit 1 = BDIR, bit 0 = BC1)
 *  0xfd0e - data register
 *  AY I/O ports are not connected to anything.
 */
void fm7_state::fm7_update_psg()
{
	switch(m_psg_regsel)
	{
	case 0x00:
		// High impedance
		break;
	case 0x01:
		// Data read
		m_psg_data = m_psg->data_r();
		break;
	case 0x02:
		// Data write
		m_psg->data_w(m_psg_data);
		break;
	case 0x03:
		// Address latch
		m_psg->address_w(m_psg_data);
		break;
	}
}

// FM-77AV and later use a YM2203
void fm77_state::fm7_update_psg()
{
	switch(m_psg_regsel)
	{
	case 0x00:
		// High impedance
		break;
	case 0x01:
		// Data read
		m_psg_data = m_ym->read(1);
		break;
	case 0x02:
		// Data write
		m_ym->write(1,m_psg_data);
		logerror("YM: data write 0x%02x\n",m_psg_data);
		break;
	case 0x03:
		// Address latch
		m_ym->write(0,m_psg_data);
		logerror("YM: address latch 0x%02x\n",m_psg_data);
		break;
	case 0x04:
		// Status register
		m_psg_data = m_ym->read(0);
		break;
	case 0x09:
		// Joystick port read
		m_psg_data = m_joy1->read();
		break;
	}
}

uint8_t fm7_state::psg_select_r()
{
	return 0xff;
}

void fm7_state::psg_select_w(uint8_t data)
{
	m_psg_regsel = data & 0x03;
	fm7_update_psg();
}

void fm77_state::av_ym_select_w(uint8_t data)
{
	m_psg_regsel = data & 0x0f;
	fm7_update_psg();
}

uint8_t fm7_state::psg_data_r()
{
//  fm7_update_psg();
	return m_psg_data;
}

void fm7_state::psg_data_w(uint8_t data)
{
	m_psg_data = data;
//  fm7_update_psg();
}

void fm77_state::av_bootram_w(offs_t offset, uint8_t data)
{
	if(!(m_mmr.mode & 0x01))
		return;
	m_boot_ram[offset] = data;
}

// Shared RAM is only usable on the main CPU if the sub CPU is halted
uint8_t fm7_state::main_shared_r(offs_t offset)
{
	if(m_video.sub_halt != 0)
		return m_shared_ram[offset];
	else
		return 0xff;
}

void fm7_state::main_shared_w(offs_t offset, uint8_t data)
{
	if(m_video.sub_halt != 0)
		m_shared_ram[offset] = data;
}

uint8_t fm77_state::fmirq_r()
{
	uint8_t ret = 0xff;

	if(m_fm77av_ym_irq != 0)
		ret &= ~0x08;

	return ret;
}

uint8_t fm7_state::unknown_r()
{
	// Port 0xFDFC is read by Dig Dug.  Controller port, perhaps?
	// Must return 0xff for it to read the keyboard.
	// Mappy uses ports FD15 and FD16.  On the FM77AV, this is the YM2203,
	// but on the FM-7, this is nothing, so we return 0xff for it to
	// read the keyboard correctly.
	return 0xff;
}

/*
 * Memory Management Register
 * Main CPU: 0xfd80 - 0xfd93  (FM-77L4, FM-77AV and later only)
 *
 * fd80-fd8f (R/W): RAM bank select for current segment (A19-A12)
 * fd90 (W/O): segment select register (3-bit, default = 0)
 * fd92 (W/O): window offset register (OA15-OA8)
 * fd93 (R/W): mode select register
 *              - bit 7: MMR enable/disable
 *              - bit 6: window enable/disable
 *              - bit 0: boot RAM read-write/read-only
 *
 */
uint8_t fm77_state::mmr_r(offs_t offset)
{
	if(offset < 0x10)
	{
		return m_mmr.bank_addr[m_mmr.segment][offset];
	}
	if(offset == 0x13)
		return m_mmr.mode;
	return 0xff;
}

void fm77_state::fm7_update_bank(int bank, uint8_t physical)
{
	m_avbank[bank]->set_bank(physical);
}

void fm77_state::fm7_mmr_refresh(address_space &space)
{
	int x;

	if(m_mmr.enabled)
	{
		for(x=0;x<16;x++)
			fm7_update_bank(x,m_mmr.bank_addr[m_mmr.segment][x]);
	}
	else
	{
		// when MMR is disabled, 0x30000-0x3ffff is banked in
		for(x=0;x<16;x++)
			fm7_update_bank(x,0x30+x);
	}

	if(m_mmr.mode & 0x40)
	{
		// Handle window offset - 0x7c00-0x7fff will show the area of extended
		// memory (0x00000-0x0ffff) defined by the window address register
		// 0x00 = 0x07c00, 0x04 = 0x08000 ... 0xff = 0x07400.
		uint16_t window_addr = ((m_mmr.window_offset << 8) + 0x7c00) & 0xffff;
//      if(window_addr < 0xfc00)
		{
			space.install_ram(0x7c00,0x7fff, &m_extended_ram[window_addr]);
		}
	}
	else
	{
		space.install_readwrite_handler(0x7000,0x7fff,read8sm_delegate(*m_avbank[7], FUNC(address_map_bank_device::read8)),write8sm_delegate(*m_avbank[7], FUNC(address_map_bank_device::write8)));
	}
	if(m_init_rom_en)
	{
		membank("init_bank_r")->set_base(&m_rom_ptr[0]);
	}
	else
	{
		membank("init_bank_r")->set_base(&m_init_bank_ram[0]);
	}

	if (m_basic_rom_en)
	{
		if (m_basic_ptr)
		{
			membank("fbasic_bank_r")->set_base(&m_basic_ptr[0]);
		}
	}
	else
	{
		membank("fbasic_bank_r")->set_base(&m_fbasic_bank_ram[0]);
	}
}

void fm77_state::mmr_w(address_space &space, offs_t offset, uint8_t data)
{
	if(offset < 0x10)
	{
		m_mmr.bank_addr[m_mmr.segment][offset] = data;
		if(m_mmr.enabled)
			fm7_update_bank(offset,data);
		logerror("MMR: Segment %i, bank %i, set to  0x%02x\n",m_mmr.segment,offset,data);
		return;
	}
	switch(offset)
	{
		case 0x10:
			m_mmr.segment = data & 0x07;
			fm7_mmr_refresh(space);
			logerror("MMR: Active segment set to %i\n",m_mmr.segment);
			break;
		case 0x12:
			m_mmr.window_offset = data;
			fm7_mmr_refresh(space);
			logerror("MMR: Window offset set to %02x\n",data);
			break;
		case 0x13:
			m_mmr.mode = data;
			m_mmr.enabled = data & 0x80;
			fm7_mmr_refresh(space);
			logerror("MMR: Mode register set to %02x\n",data);
			break;
	}
}

/*
 *  Main CPU: ports 0xfd20-0xfd23
 *  Kanji ROM read ports
 *  FD20 (W/O): ROM address high
 *  FD21 (W/O): ROM address low
 *  FD22 (R/O): Kanji data high
 *  FD23 (R/O): Kanji data low
 *
 *  Kanji ROM is visible at 0x20000 (first half only?)
 */
uint8_t fm7_state::kanji_r(offs_t offset)
{
	uint8_t* KROM = m_kanji->base();
	uint32_t addr = m_kanji_address << 1;

	switch(offset)
	{
		case 0:
		case 1:
			logerror("KANJI: read from invalid register %i\n",offset);
			return 0xff;  // write-only
		case 2:
			return KROM[addr];
		case 3:
			return KROM[addr+1];
		default:
			logerror("KANJI: read from invalid register %i\n",offset);
			return 0xff;
	}
}

void fm7_state::kanji_w(offs_t offset, uint8_t data)
{
	uint16_t addr;

	switch(offset)
	{
		case 0:
			addr = ((data & 0xff) << 8) | (m_kanji_address & 0x00ff);
			m_kanji_address = addr;
			break;
		case 1:
			addr = (data & 0xff) | (m_kanji_address & 0xff00);
			m_kanji_address = addr;
			break;
		case 2:
		case 3:
		default:
			logerror("KANJI: write to invalid register %i\n",offset);
	}
}

TIMER_CALLBACK_MEMBER(fm7_state::timer_irq)
{
	if(m_irq_mask & IRQ_FLAG_TIMER)
	{
		main_irq_set_flag(IRQ_FLAG_TIMER);
	}
}

TIMER_CALLBACK_MEMBER(fm7_state::subtimer_irq)
{
	if(m_video.nmi_mask == 0 && m_video.sub_halt == 0)
		m_sub->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}

// When a key is pressed or released (in scan mode only), an IRQ is generated on the main CPU,
// or an FIRQ on the sub CPU, if masked.  Both CPUs have ports to read keyboard data.
// Scancodes are 9 bits in FM-7 mode, 8 bits in scan mode.
void fm7_state::key_press(uint16_t scancode)
{
	m_current_scancode = scancode;

	if(scancode == 0)
		return;

	if(m_irq_mask & IRQ_FLAG_KEY)
	{
		main_irq_set_flag(IRQ_FLAG_KEY);
	}
	else
	{
		m_sub->set_input_line(M6809_FIRQ_LINE,ASSERT_LINE);
	}
	logerror("KEY: sent scancode 0x%03x\n",scancode);
}

void fm7_state::keyboard_poll_scan()
{
	int bit = 0;
	int x,y;
	uint32_t keys;
	uint32_t modifiers = m_keymod->read();
	static const uint16_t modscancodes[6] = { 0x52, 0x53, 0x54, 0x55, 0x56, 0x5a };

	for(x=0;x<3;x++)
	{
		keys = m_kb_ports[x]->read();

		for(y=0;y<32;y++)  // loop through each bit in the port
		{
			if((keys & (1<<y)) != 0 && (m_key_data[x] & (1<<y)) == 0)
			{
				key_press(fm7_key_list[bit][6]); // key press
			}
			if((keys & (1<<y)) == 0 && (m_key_data[x] & (1<<y)) != 0)
			{
				key_press(fm7_key_list[bit][6] | 0x80); // key release
			}
			bit++;
		}

		m_key_data[x] = keys;
	}
	// check modifier keys
	bit = 0;
	for(y=0;x<7;x++)
	{
		if((modifiers & (1<<y)) != 0 && (m_mod_data & (1<<y)) == 0)
		{
			key_press(modscancodes[bit]); // key press
		}
		if((modifiers & (1<<y)) == 0 && (m_mod_data & (1<<y)) != 0)
		{
			key_press(modscancodes[bit] | 0x80); // key release
		}
		bit++;
	}
	m_mod_data = modifiers;
}

TIMER_CALLBACK_MEMBER(fm7_state::keyboard_poll)
{
	int x,y;
	int bit = 0;
	int mod = 0;
	uint32_t keys;
	uint32_t modifiers = m_keymod->read();

	if (m_kb_ports[2]->read() & 0x40000)
	{
		m_break_flag = 1;
		m_maincpu->set_input_line(M6809_FIRQ_LINE,ASSERT_LINE);
	}
	else
		m_break_flag = 0;

	if(m_key_scan_mode == KEY_MODE_SCAN)
	{
		// handle scancode mode
		keyboard_poll_scan();
		return;
	}

	// check key modifiers (Shift, Ctrl, Kana, etc...)
	if(modifiers & 0x02 || modifiers & 0x04)
		mod = 1;  // shift
	if(modifiers & 0x10)
		mod = 3;  // Graph  (shift has no effect with graph)
	if(modifiers & 0x01)
		mod = 2;  // ctrl (overrides shift, if also pressed)
	if(modifiers & 0x20)
		mod = 4;  // kana (overrides all)
	if((modifiers & 0x22) == 0x22 || (modifiers & 0x24) == 0x24)
		mod = 5;  // shifted kana

	for(x=0;x<3;x++)
	{
		keys = m_kb_ports[x]->read();

		for(y=0;y<32;y++)  // loop through each bit in the port
		{
			if((keys & (1<<y)) != 0 && (m_key_data[x] & (1<<y)) == 0)
			{
				key_press(fm7_key_list[bit][mod]); // key press
			}
			bit++;
		}

		m_key_data[x] = keys;
	}
}

IRQ_CALLBACK_MEMBER(fm7_state::irq_ack)
{
	if(irqline == M6809_FIRQ_LINE)
		m_maincpu->set_input_line(irqline,CLEAR_LINE);
	return -1;
}

IRQ_CALLBACK_MEMBER(fm7_state::sub_irq_ack)
{
	m_sub->set_input_line(irqline,CLEAR_LINE);
	return -1;
}

void fm77_state::av_fmirq(int state)
{
	if(state == 1)
	{
		// cannot be masked
		main_irq_set_flag(IRQ_FLAG_OTHER);
		m_fm77av_ym_irq = 1;
		logerror("YM: IRQ on\n");
	}
	else
	{
		main_irq_clear_flag(IRQ_FLAG_OTHER);
		m_fm77av_ym_irq = 0;
		logerror("YM: IRQ off\n");
	}
}

/*
   0000 - 7FFF: (RAM) BASIC working area, user's area
   8000 - FBFF: (ROM) F-BASIC ROM, extra user RAM
   FC00 - FC7F: more RAM, if 64kB is installed
   FC80 - FCFF: Shared RAM between main and sub CPU, available only when sub CPU is halted
   FD00 - FDFF: I/O space (6809 uses memory-mapped I/O)
   FE00 - FFEF: Boot rom
   FFF0 - FFFF: Interrupt vector table
*/
// The FM-7 has only 64kB RAM, so we'll worry about banking when we do the later models
void fm7_state::fm7_mem(address_map &map)
{
	map(0x0000, 0x7fff).ram();
	map(0x8000, 0xfbff).bankr("bank1").writeonly().share("a15_ram"); // also F-BASIC ROM, when enabled
	map(0xfc00, 0xfc7f).ram();
	map(0xfc80, 0xfcff).rw(FUNC(fm7_state::main_shared_r), FUNC(fm7_state::main_shared_w));
	// I/O space (FD00-FDFF)
	map(0xfd00, 0xfd01).rw(FUNC(fm7_state::keyboard_r), FUNC(fm7_state::cassette_printer_w));
	map(0xfd02, 0xfd02).rw(FUNC(fm7_state::cassette_printer_r), FUNC(fm7_state::irq_mask_w));  // IRQ mask
	map(0xfd03, 0xfd03).rw(FUNC(fm7_state::irq_cause_r), FUNC(fm7_state::beeper_w));  // IRQ flags
	map(0xfd04, 0xfd04).r(FUNC(fm7_state::fd04_r));
	map(0xfd05, 0xfd05).rw(FUNC(fm7_state::subintf_r), FUNC(fm7_state::subintf_w));
	map(0xfd06, 0xfd0c).r(FUNC(fm7_state::unknown_r));
	map(0xfd0d, 0xfd0d).rw(FUNC(fm7_state::psg_select_r), FUNC(fm7_state::psg_select_w));
	map(0xfd0e, 0xfd0e).rw(FUNC(fm7_state::psg_data_r), FUNC(fm7_state::psg_data_w));
	map(0xfd0f, 0xfd0f).rw(FUNC(fm7_state::rom_en_r), FUNC(fm7_state::rom_en_w));
	map(0xfd10, 0xfd17).r(FUNC(fm7_state::unknown_r));
	map(0xfd18, 0xfd1f).rw(FUNC(fm7_state::fdc_r), FUNC(fm7_state::fdc_w));
	map(0xfd20, 0xfd23).rw(FUNC(fm7_state::kanji_r), FUNC(fm7_state::kanji_w));
	map(0xfd24, 0xfd36).r(FUNC(fm7_state::unknown_r));
	map(0xfd37, 0xfd37).w(FUNC(fm7_state::multipage_w));
	map(0xfd38, 0xfd3f).rw(FUNC(fm7_state::palette_r), FUNC(fm7_state::palette_w));
	map(0xfd40, 0xfdff).r(FUNC(fm7_state::unknown_r));
	// Boot ROM
	map(0xfe00, 0xffdf).bankr("bank17");
	map(0xffe0, 0xffef).ram();
	map(0xfff0, 0xffff).ram().share("vectors");
}

void fm7_state::fm8_mem(address_map &map)
{
	map(0x0000, 0x7fff).ram();
	map(0x8000, 0xfbff).bankr("bank1").writeonly().share("a15_ram"); // also F-BASIC ROM, when enabled
	map(0xfc00, 0xfc7f).ram();
	map(0xfc80, 0xfcff).rw(FUNC(fm7_state::main_shared_r), FUNC(fm7_state::main_shared_w));
	// I/O space (FD00-FDFF)
	map(0xfd00, 0xfd01).rw(FUNC(fm7_state::keyboard_r), FUNC(fm7_state::cassette_printer_w));
	map(0xfd02, 0xfd02).rw(FUNC(fm7_state::cassette_printer_r), FUNC(fm7_state::irq_mask_w));  // IRQ mask
	map(0xfd03, 0xfd03).rw(FUNC(fm7_state::irq_cause_r), FUNC(fm7_state::beeper_w));  // IRQ flags
	map(0xfd04, 0xfd04).r(FUNC(fm7_state::fd04_r));
	map(0xfd05, 0xfd05).rw(FUNC(fm7_state::subintf_r), FUNC(fm7_state::subintf_w));
	map(0xfd06, 0xfd0c).r(FUNC(fm7_state::unknown_r));
	map(0xfd0f, 0xfd0f).rw(FUNC(fm7_state::rom_en_r), FUNC(fm7_state::rom_en_w));
	map(0xfd10, 0xfd17).r(FUNC(fm7_state::unknown_r));
	map(0xfd18, 0xfd1f).rw(FUNC(fm7_state::fdc_r), FUNC(fm7_state::fdc_w));
	map(0xfd20, 0xfd23).rw(FUNC(fm7_state::kanji_r), FUNC(fm7_state::kanji_w));
	map(0xfd24, 0xfd36).r(FUNC(fm7_state::unknown_r));
	map(0xfd37, 0xfd37).w(FUNC(fm7_state::multipage_w));
	map(0xfd38, 0xfd3f).rw(FUNC(fm7_state::palette_r), FUNC(fm7_state::palette_w));
	map(0xfd40, 0xfdff).r(FUNC(fm7_state::unknown_r));
	// Boot ROM
	map(0xfe00, 0xffdf).bankr("bank17");
	map(0xffe0, 0xffef).ram();
	map(0xfff0, 0xffff).ram().share("vectors");
}

/*
   0000 - 3FFF: Video RAM bank 0 (Blue plane)
   4000 - 7FFF: Video RAM bank 1 (Red plane)
   8000 - BFFF: Video RAM bank 2 (Green plane)
   D000 - D37F: (RAM) working area
   D380 - D3FF: Shared RAM between main and sub CPU
   D400 - D4FF: I/O ports
   D800 - FFDF: (ROM) Graphics command code
   FFF0 - FFFF: Interrupt vector table
*/

void fm7_state::fm7_sub_mem(address_map &map)
{
	map(0x0000, 0xbfff).rw(FUNC(fm7_state::vram_r), FUNC(fm7_state::vram_w)); // VRAM
	map(0xc000, 0xcfff).ram(); // Console RAM
	map(0xd000, 0xd37f).ram(); // Work RAM
	map(0xd380, 0xd3ff).ram().share("shared_ram");
	// I/O space (D400-D4FF)
	map(0xd400, 0xd401).r(FUNC(fm7_state::sub_keyboard_r));
	map(0xd402, 0xd402).r(FUNC(fm7_state::cancel_ack));
	map(0xd403, 0xd403).r(FUNC(fm7_state::sub_beeper_r));
	map(0xd404, 0xd404).r(FUNC(fm7_state::attn_irq_r));
	map(0xd408, 0xd408).rw(FUNC(fm7_state::crt_r), FUNC(fm7_state::crt_w));
	map(0xd409, 0xd409).rw(FUNC(fm7_state::vram_access_r), FUNC(fm7_state::vram_access_w));
	map(0xd40a, 0xd40a).rw(FUNC(fm7_state::sub_busyflag_r), FUNC(fm7_state::sub_busyflag_w));
	map(0xd40e, 0xd40f).w(FUNC(fm7_state::vram_offset_w));
	map(0xd800, 0xffff).rom();
}

void fm11_state::fm11_mem(address_map &map)
{
	for (int bank = 0; bank < 16; bank++)
	{
		map(bank << 12, (bank << 12) | 0x0fff).rw(m_avbank[bank], FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	}
	map(0xfc00, 0xfc7f).ram();
	map(0xfc80, 0xfcff).rw(FUNC(fm11_state::main_shared_r), FUNC(fm11_state::main_shared_w));
	// I/O space (FD00-FDFF)
	map(0xfd00, 0xfd01).rw(FUNC(fm11_state::keyboard_r), FUNC(fm11_state::cassette_printer_w));
	map(0xfd02, 0xfd02).rw(FUNC(fm11_state::cassette_printer_r), FUNC(fm11_state::irq_mask_w));  // IRQ mask
	map(0xfd03, 0xfd03).rw(FUNC(fm11_state::irq_cause_r), FUNC(fm11_state::beeper_w));  // IRQ flags
	map(0xfd04, 0xfd04).r(FUNC(fm11_state::fd04_r));
	map(0xfd05, 0xfd05).rw(FUNC(fm11_state::subintf_r), FUNC(fm11_state::subintf_w));
	map(0xfd06, 0xfd0a).r(FUNC(fm11_state::unknown_r));
	map(0xfd0b, 0xfd0b).r(FUNC(fm11_state::av_boot_mode_r));
	map(0xfd0c, 0xfd0c).r(FUNC(fm11_state::unknown_r));
	map(0xfd0f, 0xfd0f).rw(FUNC(fm11_state::rom_en_r), FUNC(fm11_state::rom_en_w));
	map(0xfd10, 0xfd10).w(FUNC(fm11_state::init_en_w));
	map(0xfd11, 0xfd11).r(FUNC(fm11_state::unknown_r));
	map(0xfd12, 0xfd12).rw(FUNC(fm11_state::av_sub_modestatus_r), FUNC(fm11_state::av_sub_modestatus_w));
	map(0xfd13, 0xfd13).w(FUNC(fm11_state::av_sub_bank_w));
	map(0xfd14, 0xfd14).r(FUNC(fm11_state::unknown_r));
	map(0xfd17, 0xfd17).r(FUNC(fm11_state::fmirq_r));
	map(0xfd18, 0xfd1f).rw(FUNC(fm11_state::fdc_r), FUNC(fm11_state::fdc_w));
	map(0xfd20, 0xfd23).rw(FUNC(fm11_state::kanji_r), FUNC(fm11_state::kanji_w));
	map(0xfd24, 0xfd2b).r(FUNC(fm11_state::unknown_r));
	map(0xfd30, 0xfd34).w(FUNC(fm11_state::av_analog_palette_w));
	map(0xfd35, 0xfd36).r(FUNC(fm11_state::unknown_r));
	map(0xfd37, 0xfd37).w(FUNC(fm11_state::multipage_w));
	map(0xfd38, 0xfd3f).rw(FUNC(fm11_state::palette_r), FUNC(fm11_state::palette_w));
	map(0xfd40, 0xfd7f).r(FUNC(fm11_state::unknown_r));
	map(0xfd80, 0xfd93).rw(FUNC(fm11_state::mmr_r), FUNC(fm11_state::mmr_w));
	map(0xfd94, 0xfdff).r(FUNC(fm11_state::unknown_r));
	map(0xfe00, 0xffdf).ram().w(FUNC(fm11_state::av_bootram_w)).share("boot_ram");
	map(0xffe0, 0xffef).ram();
	map(0xfff0, 0xffff).r(FUNC(fm11_state::vector_r)).writeonly().share("vectors");
}

// Much of this is guesswork at the moment
void fm11_state::fm11_sub_mem(address_map &map)
{
	map(0x0000, 0x7fff).rw(FUNC(fm11_state::vram_r), FUNC(fm11_state::vram_w)); // VRAM
	map(0x8000, 0x8fff).ram(); // Console RAM(?)
	map(0x9000, 0x9f7f).ram(); // Work RAM(?)
	map(0x9f80, 0x9fff).ram().share("shared_ram");
	map(0xafe0, 0xafe3).ram();
//  map(0xafe4, 0xafe4).rw(FUNC(fm11_state::sub_busyflag_r), FUNC(fm11_state::sub_busyflag_w));
	map(0xafe6, 0xafe6).rw(FUNC(fm11_state::av_video_flags_r), FUNC(fm11_state::av_video_flags_w));
	map(0xaff0, 0xaff0).rw(FUNC(fm11_state::sub_busyflag_r), FUNC(fm11_state::sub_busyflag_w));
	map(0xc000, 0xffff).rom(); // sybsystem ROM
}

void fm11_state::fm11_x86_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0xfefff).ram();
	map(0xff000, 0xfffff).rom();
}

void fm11_state::fm11_x86_io(address_map &map)
{
	map(0xfd00, 0xfd01).rw(FUNC(fm11_state::keyboard_r), FUNC(fm11_state::cassette_printer_w));
	map(0xfd02, 0xfd02).rw(FUNC(fm11_state::cassette_printer_r), FUNC(fm11_state::irq_mask_w));  // IRQ mask
	map(0xfd03, 0xfd03).rw(FUNC(fm11_state::irq_cause_r), FUNC(fm11_state::beeper_w));  // IRQ flags
	map(0xfd04, 0xfd04).r(FUNC(fm11_state::fd04_r));
	map(0xfd05, 0xfd05).rw(FUNC(fm11_state::subintf_r), FUNC(fm11_state::subintf_w));
	map(0xfd06, 0xfd0c).r(FUNC(fm11_state::unknown_r));
	map(0xfd0f, 0xfd0f).rw(FUNC(fm11_state::rom_en_r), FUNC(fm11_state::rom_en_w));
	map(0xfd10, 0xfd17).r(FUNC(fm11_state::unknown_r));
	map(0xfd18, 0xfd1f).rw(FUNC(fm11_state::fdc_r), FUNC(fm11_state::fdc_w));
	map(0xfd20, 0xfd23).rw(FUNC(fm11_state::kanji_r), FUNC(fm11_state::kanji_w));
	map(0xfd24, 0xfd36).r(FUNC(fm11_state::unknown_r));
	map(0xfd37, 0xfd37).w(FUNC(fm11_state::multipage_w));
	map(0xfd38, 0xfd3f).rw(FUNC(fm11_state::palette_r), FUNC(fm11_state::palette_w));
	map(0xfd40, 0xfdff).r(FUNC(fm11_state::unknown_r));
}

void fm7_state::fm16_mem(address_map &map)
{
	map(0x00000, 0xfbfff).ram();
	map(0xfc000, 0xfffff).rom().region("ipl", 0); // IPL
}

void fm7_state::fm16_io(address_map &map)
{
	map(0xfd00, 0xfd01).rw(FUNC(fm7_state::keyboard_r), FUNC(fm7_state::cassette_printer_w));
	map(0xfd02, 0xfd02).rw(FUNC(fm7_state::cassette_printer_r), FUNC(fm7_state::irq_mask_w));  // IRQ mask
	map(0xfd03, 0xfd03).rw(FUNC(fm7_state::irq_cause_r), FUNC(fm7_state::beeper_w));  // IRQ flags
	map(0xfd04, 0xfd04).r(FUNC(fm7_state::fd04_r));
	map(0xfd05, 0xfd05).rw(FUNC(fm7_state::subintf_r), FUNC(fm7_state::subintf_w));
//  map(0xfd06, 0xfd0c).r(FUNC(fm7_state::unknown_r));
	map(0xfd0f, 0xfd0f).rw(FUNC(fm7_state::rom_en_r), FUNC(fm7_state::rom_en_w));
//  map(0xfd10, 0xfd17).r(FUNC(fm7_state::unknown_r));
	map(0xfd18, 0xfd1f).rw(FUNC(fm7_state::fdc_r), FUNC(fm7_state::fdc_w));
	map(0xfd20, 0xfd23).rw(FUNC(fm7_state::kanji_r), FUNC(fm7_state::kanji_w));
//  map(0xfd24, 0xfd36).r(FUNC(fm7_state::unknown_r));
	map(0xfd37, 0xfd37).w(FUNC(fm7_state::multipage_w));
	map(0xfd38, 0xfd3f).rw(FUNC(fm7_state::palette_r), FUNC(fm7_state::palette_w));
//  map(0xfd40, 0xfdff).r(FUNC(fm7_state::unknown_r));
}

void fm7_state::fm16_sub_mem(address_map &map)
{
	map(0x0000, 0xafff).rw(FUNC(fm7_state::vram_r), FUNC(fm7_state::vram_w)); // VRAM
	map(0xb000, 0xffff).rom(); // subsystem ROM
}

void fm77_state::fm77av_mem(address_map &map)
{
	for (int bank = 0; bank < 16; bank++)
	{
		map(bank << 12, (bank << 12) | 0x0fff).rw(m_avbank[bank], FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	}
	map(0xfc00, 0xfc7f).ram();
	map(0xfc80, 0xfcff).rw(FUNC(fm77_state::main_shared_r), FUNC(fm77_state::main_shared_w));
	// I/O space (FD00-FDFF)
	map(0xfd00, 0xfd01).rw(FUNC(fm77_state::keyboard_r), FUNC(fm77_state::cassette_printer_w));
	map(0xfd02, 0xfd02).rw(FUNC(fm77_state::cassette_printer_r), FUNC(fm77_state::irq_mask_w));  // IRQ mask
	map(0xfd03, 0xfd03).rw(FUNC(fm77_state::irq_cause_r), FUNC(fm77_state::beeper_w));  // IRQ flags
	map(0xfd04, 0xfd04).r(FUNC(fm77_state::fd04_r));
	map(0xfd05, 0xfd05).rw(FUNC(fm77_state::subintf_r), FUNC(fm77_state::subintf_w));
	map(0xfd06, 0xfd0a).r(FUNC(fm77_state::unknown_r));
	map(0xfd0b, 0xfd0b).r(FUNC(fm77_state::av_boot_mode_r));
	map(0xfd0c, 0xfd0c).r(FUNC(fm77_state::unknown_r));
	map(0xfd0d, 0xfd0d).rw(FUNC(fm77_state::psg_select_r), FUNC(fm77_state::psg_select_w));
	map(0xfd0e, 0xfd0e).rw(FUNC(fm77_state::psg_data_r), FUNC(fm77_state::psg_data_w));
	map(0xfd0f, 0xfd0f).rw(FUNC(fm77_state::rom_en_r), FUNC(fm77_state::rom_en_w));
	map(0xfd10, 0xfd10).w(FUNC(fm77_state::init_en_w));
	map(0xfd11, 0xfd11).r(FUNC(fm77_state::unknown_r));
	map(0xfd12, 0xfd12).rw(FUNC(fm77_state::av_sub_modestatus_r), FUNC(fm77_state::av_sub_modestatus_w));
	map(0xfd13, 0xfd13).w(FUNC(fm77_state::av_sub_bank_w));
	map(0xfd14, 0xfd14).r(FUNC(fm77_state::unknown_r));
	map(0xfd15, 0xfd15).rw(FUNC(fm77_state::psg_select_r), FUNC(fm77_state::av_ym_select_w));
	map(0xfd16, 0xfd16).rw(FUNC(fm77_state::psg_data_r), FUNC(fm77_state::psg_data_w));
	map(0xfd17, 0xfd17).r(FUNC(fm77_state::fmirq_r));
	map(0xfd18, 0xfd1f).rw(FUNC(fm77_state::fdc_r), FUNC(fm77_state::fdc_w));
	map(0xfd20, 0xfd23).rw(FUNC(fm77_state::kanji_r), FUNC(fm77_state::kanji_w));
	map(0xfd24, 0xfd2b).r(FUNC(fm77_state::unknown_r));
	map(0xfd30, 0xfd34).w(FUNC(fm77_state::av_analog_palette_w));
	map(0xfd35, 0xfd36).r(FUNC(fm77_state::unknown_r));
	map(0xfd37, 0xfd37).w(FUNC(fm77_state::multipage_w));
	map(0xfd38, 0xfd3f).rw(FUNC(fm77_state::palette_r), FUNC(fm77_state::palette_w));
	map(0xfd40, 0xfd7f).r(FUNC(fm77_state::unknown_r));
	map(0xfd80, 0xfd93).rw(FUNC(fm77_state::mmr_r), FUNC(fm77_state::mmr_w));
	map(0xfd94, 0xfdff).r(FUNC(fm77_state::unknown_r));
	// Boot ROM (RAM on FM77AV and later)
	map(0xfe00, 0xffdf).ram().w(FUNC(fm77_state::av_bootram_w)).share("boot_ram");
	map(0xffe0, 0xffef).ram();
	map(0xfff0, 0xffff).r(FUNC(fm77_state::vector_r)).writeonly().share("vectors");
}

void fm77_state::fm77av_sub_mem(address_map &map)
{
	map(0x0000, 0xbfff).rw(FUNC(fm77_state::vram_r), FUNC(fm77_state::vram_w)); // VRAM
	map(0xc000, 0xcfff).ram().share("console_ram"); // Console RAM
	map(0xd000, 0xd37f).ram().share("work_ram"); // Work RAM
	map(0xd380, 0xd3ff).ram().share("shared_ram");
	// I/O space (D400-D4FF)
	map(0xd400, 0xd401).r(FUNC(fm77_state::sub_keyboard_r));
	map(0xd402, 0xd402).r(FUNC(fm77_state::cancel_ack));
	map(0xd403, 0xd403).r(FUNC(fm77_state::sub_beeper_r));
	map(0xd404, 0xd404).r(FUNC(fm77_state::attn_irq_r));
	map(0xd408, 0xd408).rw(FUNC(fm77_state::crt_r), FUNC(fm77_state::crt_w));
	map(0xd409, 0xd409).rw(FUNC(fm77_state::vram_access_r), FUNC(fm77_state::vram_access_w));
	map(0xd40a, 0xd40a).rw(FUNC(fm77_state::sub_busyflag_r), FUNC(fm77_state::sub_busyflag_w));
	map(0xd40e, 0xd40f).w(FUNC(fm77_state::vram_offset_w));
	map(0xd410, 0xd42b).rw(FUNC(fm77_state::av_alu_r), FUNC(fm77_state::av_alu_w));
	map(0xd430, 0xd430).rw(FUNC(fm77_state::av_video_flags_r), FUNC(fm77_state::av_video_flags_w));
	map(0xd431, 0xd432).rw(FUNC(fm77_state::av_key_encoder_r), FUNC(fm77_state::av_key_encoder_w));
	map(0xd500, 0xd7ff).ram().share("work_ram_d500"); // Work RAM
	map(0xd800, 0xdfff).bankr("bank20");
	map(0xe000, 0xffff).bankr("bank21");
}

void fm77_state::fm7_banked_mem(address_map &map)
{
	// Extended RAM
	map(0x00000, 0x0ffff).ram().share("extended_ram");

	// Sub CPU space
	map(0x10000, 0x1bfff).rw(FUNC(fm77_state::vram_r), FUNC(fm77_state::vram_w)); // VRAM
	map(0x1c000, 0x1cfff).ram().share("console_ram"); // Console RAM
	map(0x1d000, 0x1d37f).ram().share("work_ram"); // Work RAM
	map(0x1d380, 0x1d3ff).ram().share("shared_ram");
	// I/O space (D400-D4FF)
	map(0x1d400, 0x1d401).r(FUNC(fm77_state::sub_keyboard_r));
	map(0x1d402, 0x1d402).r(FUNC(fm77_state::cancel_ack));
	map(0x1d403, 0x1d403).r(FUNC(fm77_state::sub_beeper_r));
	map(0x1d404, 0x1d404).r(FUNC(fm77_state::attn_irq_r));
	map(0x1d408, 0x1d408).rw(FUNC(fm77_state::crt_r), FUNC(fm77_state::crt_w));
	map(0x1d409, 0x1d409).rw(FUNC(fm77_state::vram_access_r), FUNC(fm77_state::vram_access_w));
	map(0x1d40a, 0x1d40a).rw(FUNC(fm77_state::sub_busyflag_r), FUNC(fm77_state::sub_busyflag_w));
	map(0x1d40e, 0x1d40f).w(FUNC(fm77_state::vram_offset_w));
	map(0x1d410, 0x1d42b).rw(FUNC(fm77_state::av_alu_r), FUNC(fm77_state::av_alu_w));
	map(0x1d430, 0x1d430).rw(FUNC(fm77_state::av_video_flags_r), FUNC(fm77_state::av_video_flags_w));
	map(0x1d431, 0x1d432).rw(FUNC(fm77_state::av_key_encoder_r), FUNC(fm77_state::av_key_encoder_w));
	map(0x1d500, 0x1d7ff).ram().share("work_ram_d500"); // Work RAM
	map(0x1d800, 0x1dfff).bankr("bank20");
	map(0x1e000, 0x1ffff).bankr("bank21");

	// more RAM?
	map(0x20000, 0x2ffff).ram().share("main_ram_20000");

	// Main CPU space
	map(0x30000, 0x35fff).ram().share("main_ram_30000");
	map(0x36000, 0x37fff).bankr("init_bank_r").writeonly().share("init_bank_w");
	map(0x38000, 0x3fbff).bankr("fbasic_bank_r").writeonly().share("fbasic_bank_w");
	map(0x3fc00, 0x3ffef).ram().share("main_ram_3fc00");
	map(0x3fff0, 0x3ffff).ram().share("vectors");

}

/* Input ports */
INPUT_PORTS_START( fm7_keyboard )
	PORT_START("key1")
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_UNUSED)
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_TILDE) PORT_CHAR(27)
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')  PORT_CHAR('=')
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^')
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\xEF\xBF\xA5") PORT_CHAR(165) PORT_CHAR('|')
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')

	PORT_START("key2")
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("_") PORT_CHAR('"') PORT_CHAR('_')
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey *") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey /") PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey +") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey -") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey =")
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 5") PORT_CODE(KEYCODE_5_PAD)

	PORT_START("key3")
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey ,")
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey Enter") PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey .") PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("INS") PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("EL") PORT_CODE(KEYCODE_PGUP)
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("CLS") PORT_CODE(KEYCODE_PGDN)
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("DUP") PORT_CODE(KEYCODE_END)
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("HOME") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("BREAK") PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF1") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF2") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF3") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF4") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF5") PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF6") PORT_CODE(KEYCODE_F6)
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF7") PORT_CODE(KEYCODE_F7)
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF8") PORT_CODE(KEYCODE_F8)
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF9") PORT_CODE(KEYCODE_F9)
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF10") PORT_CODE(KEYCODE_F10)

	PORT_START("key_modifiers")
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("CAP") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_TOGGLE
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("GRAPH") PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Kana") PORT_CODE(KEYCODE_RCONTROL) PORT_TOGGLE

	PORT_START("joy1")
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_JOYSTICK_UP) PORT_NAME("1P Joystick Up") PORT_8WAY PORT_PLAYER(1)
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_JOYSTICK_DOWN) PORT_NAME("1P Joystick Down") PORT_8WAY PORT_PLAYER(1)
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_JOYSTICK_LEFT) PORT_NAME("1P Joystick Left") PORT_8WAY PORT_PLAYER(1)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_JOYSTICK_RIGHT) PORT_NAME("1P Joystick Right") PORT_8WAY PORT_PLAYER(1)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_BUTTON2) PORT_NAME("1P Joystick Button 2") PORT_PLAYER(1)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_BUTTON1) PORT_NAME("1P Joystick Button 1") PORT_PLAYER(1)
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_UNUSED)
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_UNUSED)

	PORT_START("joy2")
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_JOYSTICK_UP) PORT_NAME("2P Joystick Up") PORT_8WAY PORT_PLAYER(2)
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_JOYSTICK_DOWN) PORT_NAME("2P Joystick Down") PORT_8WAY PORT_PLAYER(2)
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_JOYSTICK_LEFT) PORT_NAME("2P Joystick Left") PORT_8WAY PORT_PLAYER(2)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_JOYSTICK_RIGHT) PORT_NAME("2P Joystick Right") PORT_8WAY PORT_PLAYER(2)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_BUTTON2) PORT_NAME("2P Joystick Button 2") PORT_PLAYER(2)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_BUTTON1) PORT_NAME("2P Joystick Button 1") PORT_PLAYER(2)
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_UNUSED)
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( fm7 )
	PORT_INCLUDE( fm7_keyboard )

	PORT_START("DSW")
	PORT_DIPNAME(0x01,0x01,"Switch A") PORT_DIPLOCATION("SWA:1")
	PORT_DIPSETTING(0x00,DEF_STR( Off ))
	PORT_DIPSETTING(0x01,DEF_STR( On ))
	PORT_DIPNAME(0x02,0x02,"Boot mode") PORT_DIPLOCATION("SWA:2")
	PORT_DIPSETTING(0x00,"DOS")
	PORT_DIPSETTING(0x02,"BASIC")
	PORT_DIPNAME(0x04,0x00,"Switch C") PORT_DIPLOCATION("SWA:3")
	PORT_DIPSETTING(0x00,DEF_STR( Off ))
	PORT_DIPSETTING(0x04,DEF_STR( On ))
	PORT_DIPNAME(0x08,0x00,"FM-8 Compatibility mode") PORT_DIPLOCATION("SWA:4")
	PORT_DIPSETTING(0x00,DEF_STR( Off ))
	PORT_DIPSETTING(0x08,DEF_STR( On ))
INPUT_PORTS_END

static INPUT_PORTS_START( fm8 )
	PORT_INCLUDE( fm7_keyboard )

	PORT_START("DSW")
	PORT_DIPNAME(0x02,0x02,"Boot mode") PORT_DIPLOCATION("SWA:2")
	PORT_DIPSETTING(0x00,"DOS")
	PORT_DIPSETTING(0x02,"BASIC")
INPUT_PORTS_END

void fm7_state::init_fm7()
{
//  m_shared_ram = std::make_unique<uint8_t[]>(0x80);
	m_video_ram = make_unique_clear<uint8_t[]>(0x18000);  // 2 pages on some systems
	m_beeper_off_timer = timer_alloc(FUNC(fm7_state::beeper_off), this);
	m_timer = timer_alloc(FUNC(fm7_state::timer_irq), this);
	m_subtimer = timer_alloc(FUNC(fm7_state::subtimer_irq), this);
	m_keyboard_timer = timer_alloc(FUNC(fm7_state::keyboard_poll), this);

	m_init_rom_en = false;
}

MACHINE_START_MEMBER(fm7_state,fm7)
{
	// The FM-7 has no initialisation ROM, and no other obvious
	// way to set the reset vector, so for now this will have to do.
	m_vectors[0xe] = 0xfe;
	m_vectors[0xf] = 0x00;

	memset(m_shared_ram,0xff,0x80);
	m_type = SYS_FM7;

	m_beeper->set_state(0);
}

MACHINE_START_MEMBER(fm77_state,fm77av)
{
	m_encoder_ack_timer = timer_alloc(FUNC(fm77_state::av_encoder_ack), this);
	m_alu_task_end_timer = timer_alloc(FUNC(fm77_state::av_alu_task_end), this);
	m_vsync_timer = timer_alloc(FUNC(fm77_state::av_vsync), this);

	memset(m_shared_ram,0xff,0x80);

	// last part of Initiate ROM is visible at the end of RAM too (interrupt vectors)
	memcpy(&m_vectors[0], &m_rom_ptr[0x1ff0], 16);

	m_video.subrom = 0;  // default sub CPU ROM is type C.
	membank("bank20")->set_base(memregion("subsyscg")->base());
	membank("bank21")->set_base(memregion("subsys_c")->base()+0x800);

	m_type = SYS_FM77AV;
	m_beeper->set_state(0);
}

MACHINE_START_MEMBER(fm11_state,fm11)
{
	m_encoder_ack_timer = timer_alloc(FUNC(fm11_state::av_encoder_ack), this);
	m_alu_task_end_timer = timer_alloc(FUNC(fm11_state::av_alu_task_end), this);
	m_vsync_timer = timer_alloc(FUNC(fm11_state::av_vsync), this);

	memset(m_shared_ram,0xff,0x80);
	m_type = SYS_FM11;
	m_beeper->set_state(0);

	// last part of Initiate ROM is visible at the end of RAM too (interrupt vectors)
	memcpy(&m_vectors[0], &m_rom_ptr[0x0ff0], 16);
}

MACHINE_START_MEMBER(fm7_state,fm16)
{
	m_type = SYS_FM16;
	m_beeper->set_state(0);
}

void fm7_state::machine_reset()
{
	m_timer->adjust(attotime::from_nsec(2034500),0,attotime::from_nsec(2034500));
	m_subtimer->adjust(attotime::from_msec(20),0,attotime::from_msec(20));
	m_keyboard_timer->adjust(attotime::zero,0,attotime::from_msec(10));

	m_irq_mask = 0x00;
	m_irq_flags = 0x00;
	m_video.attn_irq = 0;
	m_video.sub_busy = 0x80;  // busy at reset
	m_basic_rom_en = true;  // enabled at reset, if in BASIC mode
	if(m_type == SYS_FM11 || m_type == SYS_FM16)
		m_basic_rom_en = false;  // all FM11/16 systems have no BASIC ROM except for the FM-11 ST
	if(m_type == SYS_FM77AV || m_type == SYS_FM77AV40EX)
	{
		m_init_rom_en = true;
		// last part of Initiate ROM is visible at the end of RAM too (interrupt vectors)
		memcpy(&m_vectors[0], &m_rom_ptr[0x1ff0], 16);
	}
	else if (m_type == SYS_FM11)
	{
		m_init_rom_en = true;
		// last part of Initiate ROM is visible at the end of RAM too (interrupt vectors)
		memcpy(&m_vectors[0], &m_rom_ptr[0x0ff0], 16);
	}
	else
		m_init_rom_en = false;
	if(m_type == SYS_FM7)
	{
		if(!(m_dsw->read() & 0x02))
		{
			m_basic_rom_en = false;  // disabled for DOS mode
			membank("bank1")->set_base(&m_a15_ram[0]);
		}
		else
		{
			membank("bank1")->set_base(&m_basic_ptr[0]);
		}
	}

	m_key_delay = 700;  // 700ms on FM-7
	m_key_repeat = 70;  // 70ms on FM-7
	m_break_flag = 0;
	m_key_scan_mode = KEY_MODE_FM7;
	m_psg_regsel = 0;
	m_psg_data = 0;
	m_fdc_side = 0;
	m_fdc_drive = 0;

	// set boot mode (FM-7 only, AV and later has boot RAM instead)
	if(m_type == SYS_FM7)
	{
		if(!(m_dsw->read() & 0x02))
		{  // DOS mode
			membank("bank17")->set_base(&m_btrom_ptr[0x600]);
		}
		else
		{  // BASIC mode
			membank("bank17")->set_base(&m_btrom_ptr[0x200]);
		}
	}
}

void fm77_state::machine_reset()
{
	fm7_state::machine_reset();

	m_vsync_timer->adjust(m_screen->time_until_vblank_end());

	m_mmr.mode = 0;
	m_mmr.segment = 0;
	m_mmr.enabled = 0;
	fm7_mmr_refresh(m_maincpu->space(AS_PROGRAM));

	m_encoder.latch = 1;
	m_encoder.ack = 1;
	m_fm77av_ym_irq = 0;
}

void fm11_state::machine_reset()
{
	fm77_state::machine_reset();

	// Probably best to halt the 8088, I'm pretty sure it and the main 6809 should not be running at the same time
	m_x86->set_input_line(INPUT_LINE_HALT,ASSERT_LINE);
}


static void fm7_floppies(device_slot_interface &device)
{
	device.option_add("qd", FLOPPY_525_QD);
}


void fm7_state::fm7(machine_config &config)
{
	/* basic machine hardware */
	MC6809(config, m_maincpu, 16.128_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &fm7_state::fm7_mem);
	m_maincpu->set_irq_acknowledge_callback(FUNC(fm7_state::irq_ack));

	MC6809(config, m_sub, 16.128_MHz_XTAL / 2);
	m_sub->set_addrmap(AS_PROGRAM, &fm7_state::fm7_sub_mem);
	m_sub->set_irq_acknowledge_callback(FUNC(fm7_state::sub_irq_ack));
	config.set_perfect_quantum(m_sub);

	SPEAKER(config, "mono").front_center();
	AY8913(config, m_psg, 4.9152_MHz_XTAL / 4).add_route(ALL_OUTPUTS,"mono", 1.00);
	BEEP(config, "beeper", 1200).add_route(ALL_OUTPUTS, "mono", 0.50);

	MCFG_MACHINE_START_OVERRIDE(fm7_state,fm7)

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(16.128_MHz_XTAL, 1024, 0, 640, 262, 0, 200); // H = 15.75 KHz, V = 60.1145 Hz
	m_screen->set_screen_update(FUNC(fm7_state::screen_update_fm7));

	PALETTE(config, m_palette, palette_device::BRG_3BIT);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(fm7_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("fm7_cass");

	SOFTWARE_LIST(config, "fm7_cass_list").set_original("fm7_cass");
	SOFTWARE_LIST(config, "fm8_cass_list").set_compatible("fm8_cass");

	MB8877(config, m_fdc, 8_MHz_XTAL / 8);
	m_fdc->intrq_wr_callback().set(FUNC(fm7_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(fm7_state::fdc_drq_w));

	FLOPPY_CONNECTOR(config, m_floppy0, fm7_floppies, "qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy1, fm7_floppies, "qd", floppy_image_device::default_mfm_floppy_formats);

	SOFTWARE_LIST(config, "flop_list").set_original("fm7_disk");

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->option_add("dsjoy", DEMPA_SHINBUNSHA_JOYSTICK);
	m_centronics->busy_handler().set(FUNC(fm7_state::write_centronics_busy));
	m_centronics->fault_handler().set(FUNC(fm7_state::write_centronics_fault));
	m_centronics->ack_handler().set(FUNC(fm7_state::write_centronics_ack));
	m_centronics->perror_handler().set(FUNC(fm7_state::write_centronics_perror));

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);
}

void fm7_state::fm8(machine_config &config)
{
	/* basic machine hardware */
	MC6809(config, m_maincpu, 4.9152_MHz_XTAL);  // 1.2MHz 68A09
	m_maincpu->set_addrmap(AS_PROGRAM, &fm7_state::fm8_mem);
	m_maincpu->set_irq_acknowledge_callback(FUNC(fm7_state::irq_ack));

	MC6809(config, m_sub, 16.128_MHz_XTAL / 2);
	m_sub->set_addrmap(AS_PROGRAM, &fm7_state::fm7_sub_mem);
	m_sub->set_irq_acknowledge_callback(FUNC(fm7_state::sub_irq_ack));
	config.set_perfect_quantum(m_sub);

	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 1200).add_route(ALL_OUTPUTS, "mono", 0.50);

	MCFG_MACHINE_START_OVERRIDE(fm7_state,fm7)

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(16.128_MHz_XTAL, 1024, 0, 640, 262, 0, 200);
	m_screen->set_screen_update(FUNC(fm7_state::screen_update_fm7));

	PALETTE(config, m_palette, palette_device::BRG_3BIT);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(fm7_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("fm7_cass");

	SOFTWARE_LIST(config, "fm8_cass_list").set_original("fm8_cass");

	MB8877(config, m_fdc, 8_MHz_XTAL / 8);
	m_fdc->intrq_wr_callback().set(FUNC(fm7_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(fm7_state::fdc_drq_w));

	FLOPPY_CONNECTOR(config, m_floppy0, fm7_floppies, "qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy1, fm7_floppies, "qd", floppy_image_device::default_mfm_floppy_formats);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(fm7_state::write_centronics_busy));
	m_centronics->fault_handler().set(FUNC(fm7_state::write_centronics_fault));
	m_centronics->ack_handler().set(FUNC(fm7_state::write_centronics_ack));
	m_centronics->perror_handler().set(FUNC(fm7_state::write_centronics_perror));

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);
}

void fm77_state::fm77av(machine_config &config)
{
	/* basic machine hardware */
	MC6809E(config, m_maincpu, 16.128_MHz_XTAL / 8);
	m_maincpu->set_addrmap(AS_PROGRAM, &fm77_state::fm77av_mem);
	m_maincpu->set_irq_acknowledge_callback(FUNC(fm77_state::irq_ack));

	MC6809E(config, m_sub, 16.128_MHz_XTAL / 8);
	m_sub->set_addrmap(AS_PROGRAM, &fm77_state::fm77av_sub_mem);
	m_sub->set_irq_acknowledge_callback(FUNC(fm77_state::sub_irq_ack));
	config.set_perfect_quantum(m_sub);

	SPEAKER(config, "mono").front_center();
	YM2203(config, m_ym, 4.9152_MHz_XTAL / 4);
	m_ym->irq_handler().set(FUNC(fm77_state::av_fmirq));
	m_ym->port_a_read_callback().set_ioport("joy1");
	m_ym->port_b_read_callback().set_ioport("joy2");
	m_ym->add_route(ALL_OUTPUTS,"mono", 1.00);
	BEEP(config, "beeper", 1200).add_route(ALL_OUTPUTS, "mono", 0.50);

	MCFG_MACHINE_START_OVERRIDE(fm77_state,fm77av)

	for (int bank = 0; bank < 16; bank++)
	{
		ADDRESS_MAP_BANK(config, m_avbank[bank]).set_map(&fm77_state::fm7_banked_mem).set_options(ENDIANNESS_LITTLE, 8, 32, 0x1000);
	}

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(16.128_MHz_XTAL, 1024, 0, 640, 262, 0, 200);
	m_screen->set_screen_update(FUNC(fm77_state::screen_update_fm7));

	PALETTE(config, m_palette, palette_device::BRG_3BIT);
	PALETTE(config, m_av_palette).set_entries(4096);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(fm7_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("fm7_cass");

	SOFTWARE_LIST(config, "fm7_cass_list").set_compatible("fm7_cass");
	SOFTWARE_LIST(config, "fm8_cass_list").set_compatible("fm8_cass");

	MB8877(config, m_fdc, 8_MHz_XTAL / 8);
	m_fdc->intrq_wr_callback().set(FUNC(fm77_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(fm77_state::fdc_drq_w));

	FLOPPY_CONNECTOR(config, m_floppy0, fm7_floppies, "qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy1, fm7_floppies, "qd", floppy_image_device::default_mfm_floppy_formats);

	SOFTWARE_LIST(config, "av_flop_list").set_original("fm77av");
	SOFTWARE_LIST(config, "flop_list").set_compatible("fm7_disk");

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(fm77_state::write_centronics_busy));
	m_centronics->fault_handler().set(FUNC(fm77_state::write_centronics_fault));
	m_centronics->ack_handler().set(FUNC(fm77_state::write_centronics_ack));
	m_centronics->perror_handler().set(FUNC(fm77_state::write_centronics_perror));

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);
}

void fm11_state::fm11(machine_config &config)
{
	/* basic machine hardware */
	MC6809E(config, m_maincpu, 2000000);  // 2MHz 68B09E
	m_maincpu->set_addrmap(AS_PROGRAM, &fm11_state::fm11_mem);
	m_maincpu->set_irq_acknowledge_callback(FUNC(fm11_state::irq_ack));

	MC6809(config, m_sub, 8000000);  // 2MHz 68B09
	m_sub->set_addrmap(AS_PROGRAM, &fm11_state::fm11_sub_mem);
	m_sub->set_irq_acknowledge_callback(FUNC(fm11_state::sub_irq_ack));
	config.set_perfect_quantum(m_sub);

	I8088(config, m_x86, 8000000);  // 8MHz i8088
	m_x86->set_addrmap(AS_PROGRAM, &fm11_state::fm11_x86_mem);
	m_x86->set_addrmap(AS_IO, &fm11_state::fm11_x86_io);

	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 1200).add_route(ALL_OUTPUTS, "mono", 0.50);

	MCFG_MACHINE_START_OVERRIDE(fm11_state,fm11)

	for (int bank = 0; bank < 16; bank++)
	{
		ADDRESS_MAP_BANK(config, m_avbank[bank]).set_map(&fm11_state::fm7_banked_mem).set_options(ENDIANNESS_LITTLE, 8, 32, 0x1000);
	}

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(16128000, 1024, 0, 640, 262, 0, 200);
	m_screen->set_screen_update(FUNC(fm11_state::screen_update_fm7));

	PALETTE(config, m_palette, palette_device::BRG_3BIT);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(fm7_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("fm7_cass");

	MB8877(config, m_fdc, 8_MHz_XTAL / 8);
	m_fdc->intrq_wr_callback().set(FUNC(fm11_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(fm11_state::fdc_drq_w));

	FLOPPY_CONNECTOR(config, m_floppy0, fm7_floppies, "qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy1, fm7_floppies, "qd", floppy_image_device::default_mfm_floppy_formats);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(fm11_state::write_centronics_busy));
	m_centronics->fault_handler().set(FUNC(fm11_state::write_centronics_fault));
	m_centronics->ack_handler().set(FUNC(fm11_state::write_centronics_ack));
	m_centronics->perror_handler().set(FUNC(fm11_state::write_centronics_perror));

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);
}

void fm7_state::fm16beta(machine_config &config)
{
	/* basic machine hardware */
	I8086(config, m_maincpu, 8000000);  // 8MHz i8086
	m_maincpu->set_addrmap(AS_PROGRAM, &fm7_state::fm16_mem);
	m_maincpu->set_addrmap(AS_IO, &fm7_state::fm16_io);

	MC6809(config, m_sub, 8000000);
	m_sub->set_irq_acknowledge_callback(FUNC(fm7_state::sub_irq_ack));
	m_sub->set_addrmap(AS_PROGRAM, &fm7_state::fm16_sub_mem);
	config.set_perfect_quantum(m_sub);

	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 1200).add_route(ALL_OUTPUTS, "mono", 0.50);

	MCFG_MACHINE_START_OVERRIDE(fm7_state,fm16)

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(16128000, 1024, 0, 640, 262, 0, 200);
	m_screen->set_screen_update(FUNC(fm7_state::screen_update_fm7));

	PALETTE(config, m_palette, palette_device::BRG_3BIT);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(fm7_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("fm7_cass");

	MB8877(config, m_fdc, 8_MHz_XTAL / 8);
	m_fdc->intrq_wr_callback().set(FUNC(fm7_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(fm7_state::fdc_drq_w));

	FLOPPY_CONNECTOR(config, m_floppy0, fm7_floppies, "qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy1, fm7_floppies, "qd", floppy_image_device::default_mfm_floppy_formats);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(fm7_state::write_centronics_busy));
	m_centronics->fault_handler().set(FUNC(fm7_state::write_centronics_fault));
	m_centronics->ack_handler().set(FUNC(fm7_state::write_centronics_ack));
	m_centronics->perror_handler().set(FUNC(fm7_state::write_centronics_perror));

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);
}

/* ROM definition */
ROM_START( fm8 )
	ROM_REGION( 0x8000, "fbasic", 0 )
	ROM_LOAD( "fbasic10.rom", 0x0000,  0x7c00, CRC(e80ed96c) SHA1(f3fa8a6adb07224ad2a1def77d5dae9662de0867) BAD_DUMP )

	ROM_REGION( 0x10000, "sub", 0 )
	ROM_LOAD( "subsys_8.rom", 0xd800,  0x2800, CRC(979f9046) SHA1(9c52052087bf3a41b83d437a51d89b9fcfec2515) BAD_DUMP )

	// either one of these boot ROMs are selectable via DIP switch
	ROM_REGION( 0x800, "boot", 0 )
	ROM_LOAD( "bootbas8.rom", 0x0200,  0x0200, CRC(8260267a) SHA1(fee6fb9c52d22dd7108c68d08c74e2f3ebcb9e4d) )
	ROM_LOAD( "bootdos8.rom", 0x0600,  0x0200, CRC(1ed5a506) SHA1(966538fa92c32fc15034576dc480cfa4a339384d) )

	// optional Kanji ROM (same as for the FM-7?)
	ROM_REGION( 0x20000, "kanji1", 0 )
	ROM_LOAD_OPTIONAL( "kanji.rom", 0x0000, 0x20000, NO_DUMP )

ROM_END


ROM_START( fmnew7 )
	ROM_REGION( 0x8000, "fbasic", 0 ) // at 0x7ba5 there is the ID string 0302840301, meaning it's v3.02 from 1984/03/01
	ROM_LOAD( "fbasic302.rom", 0x0000,  0x7c00, CRC(a96d19b6) SHA1(8d5f0cfe7e0d39bf2ab7e4c798a13004769c28b2) BAD_DUMP ) // last 1K is inaccessible

	ROM_REGION( 0x10000, "sub", 0 )
	ROM_LOAD( "subsys_c.rom", 0xd800,  0x2800, CRC(24cec93f) SHA1(50b7283db6fe1342c6063fc94046283f4feddc1c) BAD_DUMP ) // actually one 2764 + one half-used 2732

	// either one of these boot ROMs are selectable via DIP switch
	ROM_REGION( 0x800, "boot", 0 )
	ROM_LOAD( "boot_bas.rom", 0x0200,  0x0200, CRC(c70f0c74) SHA1(53b63a301cba7e3030e79c59a4d4291eab6e64b0) BAD_DUMP ) // actually 0.5K banks of the same ROM
	ROM_LOAD( "boot_dos.rom", 0x0600,  0x0200, CRC(198614ff) SHA1(037e5881bd3fed472a210ee894a6446965a8d2ef) BAD_DUMP )

	// optional Kanji ROM
	ROM_REGION( 0x20000, "kanji1", 0 )
	ROM_LOAD_OPTIONAL( "kanji.rom", 0x0000, 0x20000, CRC(62402ac9) SHA1(bf52d22b119d54410dad4949b0687bb0edf3e143) )

ROM_END

ROM_START( fm7 )
	ROM_REGION( 0x8000, "fbasic", 0 ) // at 0x7ba5 there is the ID string 0300820920, meaning it's v3.00 from 1982/09/20
	ROM_LOAD( "fbasic300.rom", 0x0000,  0x7c00, CRC(87c98494) SHA1(d7e3603b0a2442c7632dad45f9704d9ad71968f5) BAD_DUMP ) // last 1K is inaccessible

	ROM_REGION( 0x10000, "sub", 0 )
	ROM_LOAD( "subsys_c.rom", 0xd800,  0x2800, CRC(24cec93f) SHA1(50b7283db6fe1342c6063fc94046283f4feddc1c) BAD_DUMP ) // actually one 2764 + one half-used 2732

	// either one of these boot ROMs are selectable via DIP switch
	ROM_REGION( 0x800, "boot", 0 )
	ROM_LOAD( "boot_bas.rom",   0x0200,  0x0200, CRC(c70f0c74) SHA1(53b63a301cba7e3030e79c59a4d4291eab6e64b0) BAD_DUMP ) // actually 0.5K banks of the same ROM
	ROM_LOAD( "boot_dos_a.rom", 0x0600,  0x0200, CRC(bf441864) SHA1(616c17155f84fb0e3731a31ef0eb0cbb664a5600) BAD_DUMP )

	ROM_REGION( 0x200, "fc00prom", 0 )
	ROM_LOAD( "mb7053.ic139", 0x000, 0x200, NO_DUMP ) // 512x4 bipolar PROM for address decoding

	ROM_REGION( 0x1000, "kbmcu", 0 )
	ROM_LOAD( "mb88401.ic125", 0x0000, 0x1000, NO_DUMP )

	// optional Kanji ROM
	ROM_REGION( 0x20000, "kanji1", 0 )
	ROM_LOAD_OPTIONAL( "kanji.rom", 0x0000, 0x20000, CRC(62402ac9) SHA1(bf52d22b119d54410dad4949b0687bb0edf3e143) )
ROM_END

ROM_START( fm77av )
	ROM_REGION( 0x2000, "init", 0 )
	ROM_LOAD( "initiate.rom", 0x0000,  0x2000, CRC(785cb06c) SHA1(b65987e98a9564a82c85eadb86f0204eee5a5c93) )

	ROM_REGION( 0x8000, "fbasic", 0 )
	ROM_LOAD( "fbasic30.rom", 0x0000,  0x7c00, CRC(a96d19b6) SHA1(8d5f0cfe7e0d39bf2ab7e4c798a13004769c28b2) BAD_DUMP )

	// sub CPU ROMs
	ROM_REGION( 0x2800, "subsys_c", 0 )
	ROM_LOAD( "subsys_c.rom", 0x0000,  0x2800, CRC(24cec93f) SHA1(50b7283db6fe1342c6063fc94046283f4feddc1c) BAD_DUMP )
	ROM_REGION( 0x2000, "subsys_a", 0 )
	ROM_LOAD( "subsys_a.rom", 0x0000,  0x2000, CRC(e8014fbb) SHA1(038cb0b42aee9e933b20fccd6f19942e2f476c83) )
	ROM_REGION( 0x2000, "subsys_b", 0 )
	ROM_LOAD( "subsys_b.rom", 0x0000,  0x2000, CRC(9be69fac) SHA1(0305bdd44e7d9b7b6a17675aff0a3330a08d21a8) )
	ROM_REGION( 0x2000, "subsyscg", 0 )
	ROM_LOAD( "subsyscg.rom", 0x0000,  0x2000, CRC(e9f16c42) SHA1(8ab466b1546d023ba54987790a79e9815d2b7bb2) )

	ROM_REGION( 0x20000, "kanji1", 0 )
	ROM_LOAD( "kanji.rom", 0x0000, 0x20000, CRC(62402ac9) SHA1(bf52d22b119d54410dad4949b0687bb0edf3e143) )

	// optional dict rom?
ROM_END

ROM_START( fm7740sx )
	ROM_REGION( 0x2000, "init", 0 )
	ROM_LOAD( "initiate.rom", 0x0000,  0x2000, CRC(785cb06c) SHA1(b65987e98a9564a82c85eadb86f0204eee5a5c93) )

	ROM_REGION( 0x8000, "fbasic", 0 )
	ROM_LOAD( "fbasic30.rom", 0x0000,  0x7c00, CRC(a96d19b6) SHA1(8d5f0cfe7e0d39bf2ab7e4c798a13004769c28b2) BAD_DUMP )

	// sub CPU ROMs
	ROM_REGION( 0x2800, "subsys_c", 0 )
	ROM_LOAD( "subsys_c.rom", 0x0000,  0x2800, CRC(24cec93f) SHA1(50b7283db6fe1342c6063fc94046283f4feddc1c) BAD_DUMP )
	ROM_REGION( 0x2000, "subsys_a", 0 )
	ROM_LOAD( "subsys_a.rom", 0x0000,  0x2000, CRC(e8014fbb) SHA1(038cb0b42aee9e933b20fccd6f19942e2f476c83) )
	ROM_REGION( 0x2000, "subsys_b", 0 )
	ROM_LOAD( "subsys_b.rom", 0x0000,  0x2000, CRC(9be69fac) SHA1(0305bdd44e7d9b7b6a17675aff0a3330a08d21a8) )
	ROM_REGION( 0x2000, "subsyscg", 0 )
	ROM_LOAD( "subsyscg.rom", 0x0000,  0x2000, CRC(e9f16c42) SHA1(8ab466b1546d023ba54987790a79e9815d2b7bb2) )

	ROM_REGION( 0x20000, "kanji1", 0 )
	ROM_LOAD( "kanji.rom", 0x0000, 0x20000, CRC(62402ac9) SHA1(bf52d22b119d54410dad4949b0687bb0edf3e143) )
	ROM_LOAD( "kanji2.rom", 0x0000, 0x20000, CRC(38644251) SHA1(ebfdc43c38e1380709ed08575c346b2467ad1592) )

	/* These should be loaded at 2e000-2ffff of maincpu, but I'm not sure if it is correct */
	ROM_REGION( 0x4c000, "additional", 0 )
	ROM_LOAD( "dicrom.rom", 0x00000, 0x40000, CRC(b142acbc) SHA1(fe9f92a8a2750bcba0a1d2895e75e83858e4f97f) )
	ROM_LOAD( "extsub.rom", 0x40000, 0x0c000, CRC(0f7fcce3) SHA1(a1304457eeb400b4edd3c20af948d66a04df255e) )

ROM_END

ROM_START( fm11 )
	ROM_REGION( 0x1000, "init", 0 )
	ROM_LOAD( "boot6809.rom", 0x0000, 0x1000, CRC(447caa6f) SHA1(4aa30314994c256d37ee01d11ec2bf4df3bc8cde) )

	ROM_REGION( 0x10000, "sub", 0 )
	ROM_LOAD( "subsys.rom", 0xc000, 0x4000, CRC(436c0618) SHA1(cd508e3e8f79737afb4384ea4b278eddf0ce935d) )

	ROM_REGION( 0x100000, "x86", 0 )
	ROM_LOAD( "boot8088.rom", 0xff000, 0x1000, CRC(d13096a6) SHA1(f9bd95b3b8184d0e04fba9b50f273dbb8823be77) )

	ROM_REGION( 0x10000, "subsys_e", 0 )
	ROM_LOAD( "subsys_e.rom", 0x00000, 0x1000, CRC(31d838aa) SHA1(86a275c27bc99985bef7a51bdab2e47d68e31c6c) )

	// optional Kanji ROM
	ROM_REGION( 0x20000, "kanji1", 0 )
	ROM_LOAD_OPTIONAL( "kanji.rom", 0x0000, 0x20000, CRC(62402ac9) SHA1(bf52d22b119d54410dad4949b0687bb0edf3e143) )

ROM_END

ROM_START( fm16beta )
	ROM_REGION16_LE( 0x4000, "ipl", 0 )
	ROM_LOAD( "ipl.rom", 0x0000, 0x4000, CRC(25f618ea) SHA1(9c27d6ad283260e071d64a1bfca16f7d3ad61f96) )

//  ROM_REGION( 0x10000, "subsys", 0 )

	ROM_REGION( 0x10000, "sub", 0 )
	ROM_LOAD( "sub_cg.rom", 0xa000, 0x0f80, CRC(e7928bed) SHA1(68cf604aa7a5c2ec7bd0d612cf099302c7f8c442) )
	ROM_CONTINUE(0xff80,0x0080)
	ROM_LOAD( "subsys.rom", 0xb000, 0x4f80, CRC(1d878514) SHA1(4673879a81e39880655b380250c6b81137028727) )
ROM_END


/* Driver */

/*    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT  CLASS       INIT      COMPANY    FULLNAME       FLAGS */
COMP( 1981, fm8,      0,      0,      fm8,      fm8,   fm7_state,  init_fm7, "Fujitsu", "FM-8",        0)
COMP( 1982, fm7,      0,      0,      fm7,      fm7,   fm7_state,  init_fm7, "Fujitsu", "FM-7",        0)
COMP( 1984, fmnew7,   fm7,    0,      fm7,      fm7,   fm7_state,  init_fm7, "Fujitsu", "FM-NEW7",     0)
COMP( 1985, fm77av,   fm7,    0,      fm77av,   fm7,   fm77_state, init_fm7, "Fujitsu", "FM-77AV",     MACHINE_IMPERFECT_GRAPHICS)
COMP( 1985, fm7740sx, fm7,    0,      fm77av,   fm7,   fm77_state, init_fm7, "Fujitsu", "FM-77AV40SX", MACHINE_NOT_WORKING)

// These may be separated into a separate driver, depending on how different they are to the FM-8/FM-7
COMP( 1982, fm11,     0,      0,      fm11,     fm7,   fm11_state, init_fm7, "Fujitsu", "FM-11 EX",    MACHINE_NOT_WORKING)
COMP( 1982, fm16beta, 0,      0,      fm16beta, fm7,   fm7_state,  init_fm7, "Fujitsu", u8"FM-16β",    MACHINE_NOT_WORKING)



fmtowns.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Barry Rodewald
/*

    Fujitsu FM-Towns
    driver by Barry Rodewald

    Japanese computer system released in 1989.

    CPU:  various AMD x86 CPUs, originally 80386DX (80387 available as an add-on).
          later models use 80386SX, 80486 and Pentium CPUs
    Sound:  Yamaha YM3438 (some later models are use YMF276; Low voltage variation of YM3438, needs External DAC)
            Ricoh RF5c68
            CD-DA
    Video:  Custom
            16 or 256 colours from a 24-bit palette, or 15-bit high-colour
            1024 sprites (16x16), rendered direct to VRAM
            16 colour text mode, rendered direct to VRAM

    Later models add an unknown single channel 16-bit PCM/ADPCM (FreshTV, SJ, MX), and CL-GD543x Windows accelerator chipsets (SJ)


    Fujitsu FM-Towns Marty

    Japanese console, based on the FM-Towns computer, using an AMD 80386SX CPU,
    released in 1993


    Issues: Video emulation is far from complete.

*/

/*

Regular models:

             Model              | Form factor |     CPU    |        Standard RAM          | Max RAM |      Standard FDDs     |         Standard HDD
FM Towns 1/2                    | Tower       | 386DX-16   | 1 MB (1) or 2 MB (2)         | 6 MB    | 1 or 2                 | No SCSI controller
FM Towns 1F/2F/1H/2H            | Tower       | 386DX-16   | 1 MB (1F/1H) or 2 MB (2F/2H) | 8 MB    | 1 (1F) or 2 (others)   | 20 or 40 MB (H models only)
FM Towns 10F/20F/40H/80H        | Tower       | 386DX-16   | 2 MB                         | 26 MB   | 1 (10F) or 2 (others)  | 40 or 85 MB (H models only)
FM Towns II CX10/20/40/100      | Tower       | 386DX-16   | 2 MB                         | 26 MB   | 1 (CX10) or 2 (others) | No (CX10/20), 40 or 100 MB
FM Towns II UX10/20/40          | All-in-one  | 386SX-16   | 2 MB                         | 10 MB   | 1 (UX10) or 2 (others) | No (UX10/20) or 40 MB
FM Towns II HG20/40/100         | Desktop     | 386DX-20   | 2 MB                         | 26 MB   | 2                      | No (HG20), 40 or 80 MB
FM Towns II HR20/100/200        | Desktop     | 486SX-20   | 4 MB                         | 28 MB   | 2                      | No (HR20) 100 or 200 MB
FM Towns II UG10/20/40/80       | All-in-one  | 386SX-20   | 2 MB                         | 10 MB   | 1 (UG10) or 2 (others) | No (UG10/20), 40 or 80 MB
FM Towns II UR20/40/80          | All-in-one  | 486SX-20   | 2 MB                         | 10 MB   | 2                      | No (UR20), 40 or 80 MB
FM Towns II ME20/170            | Desktop     | 486SX-25   | 2 MB                         | 66 MB   | 2                      | No (ME20) or 170 MB
FM Towns II MA170/340           | Desktop     | 486SX-33   | 4 MB                         | 100 MB  | 2                      | 170 or 340 MB
FM Towns II MX20/170/340        | Desktop     | 486DX2-66  | 4 MB                         | 100 MB  | 2                      | No (MX20), 170 or 340 MB
FM Towns II Fresh/MF20/MF170W   | Desktop     | 486SX-33   | 4 MB (MF20) or 6 MB (others) | 68 MB   | 2                      | No (MF20), or 170 (others)
FM Towns II MA170W/MA340W       | Desktop     | 486SX-33   | 8 MB                         | 100 MB  | 2                      | 170 or 340 MB
FM Towns II MX170W/MA340W       | Desktop     | 486DX2-66  | 8 MB                         | 100 MB  | 2                      | 170 or 340 MB
FM Towns II Fresh-TV            | Desktop     | 486SX-33   | 6 MB                         | 68 MB   | 2                      | 170 MB
FM Towns II Fresh-E             | Desktop     | 486DX2-66  | 8 MB                         | 72 MB   | 2                      | 260 MB
FM Towns II Fresh-T             | Desktop     | 486SX-33   | 8 MB                         | 72 MB   | 2                      | 260 MB
FM Towns II EA2                 | Desktop     | 486SX-33   | 4 MB                         | 68 MB   | 2                      | No
FM Towns II HA2/HA53            | Desktop     | 486DX2-66  | 4 MB (HA2) or 8 MB (HA53)    | 100 MB  | 2                      | No (HA2) or 530 MB
FM Towns II HB2/HB53/HB53M      | Desktop     | Pentium-60 | 8 MB                         | 136 MB  | 2                      | No (HB2) or 530 MB
FM Towns II Fresh-ES/Fresh-ET   | Desktop     | 486DX2-66  | 8 MB                         | 72 MB   | 1                      | 360 MB
FM Towns II HC53/HC53M          | Desktop     | Pentium-90 | 8 MB                         | 136 MB  | 1                      | 540 MB
FM Towns II Fresh-FS/Fresh-FT   | Desktop     | 486DX4-100 | 8 MB                         | 72 MB   | 1                      | 540 MB
FM Towns Marty/Marty 2/TC Marty | Console     | 386SX-16   | 2 MB                         | 4 MB    | 1                      | No SCSI controller
Car Marty                       | Car-mounted | 386SX-16   | 2 MB                         | ?       | No                     | No SCSI controller

Education models:

         Model          | Form factor |     CPU    |        Standard RAM          | Max RAM |      Standard FDDs     |         Standard HDD
FM Towns S1/S2          | Tower       | 386DX-16   | 1 MB (S1) or 2 MB (S2)       | 6 MB    | 1 or 2                 | No SCSI controller
FM Towns SF/SH          | Tower       | 386DX-16   | 1 MB (1F/1H) or 2 MB (2F/2H) | 8 MB    | 2                      | No (SF) or 40 MB
FM Towns SF2/SH2        | Tower       | 386DX-16   | 2 MB                         | 26 MB   | 2                      | No (SF2) or 40 MB
FM Towns II SG20/40     | Desktop     | 386DX-20   | 2 MB                         | 26 MB   | 2                      | No (SG20) or 40 MB
FM Towns II SR20/100    | Desktop     | 486SX-20   | 4 MB                         | 28 MB   | 2                      | No (SR20) or 100 MB
FM Towns II SE          | Desktop     | 486SX-25   | 2 MB                         | 66 MB   | 2                      | No
FM Towns II SA          | Desktop     | 486SX-33   | 4 MB                         | 100 MB  | 2                      | No
FM Towns II SF20/SF170W | Desktop     | 486SX-33   | 4 MB (SF20) or 6 MB (SF170W) | 100 MB  | 2                      | No (SF20) or 170 MB
FM Towns II SA170W      | Desktop     | 486SX-33   | 8 MB                         | 100 MB  | 2                      | 170 MB
FM Towns II SI2/SI26    | Desktop     | 486SX-33   | 4 MB (SI2) or 8 MB (SI26)    | 68 MB   | 2                      | No (SI2) or 260 MB
FM Towns II SJ2/SJ26    | Desktop     | 486DX2-66  | 4 MB (SJ2) or 8 MB (SJ26)    | 68 MB   | 2                      | No (SJ2) or 260 MB
FM Towns II SK53        | Desktop     | Pentium-60 | 8 MB                         | 136 MB  | 2                      | 530 MB
FM Towns II SN          | Laptop      | 486DX2-66  | 4 MB                         | 36 MB   | 1                      | 340 MB
FM Towns II SJ2A/SJ53   | Desktop     | 486DX2-66  | 4 MB (SJ2A) or 8 MB (SJ53)   | 68 MB   | 2                      | No (SJ2A) or 530 MB
FM Towns II SL53        | Desktop     | Pentium-90 | 8 MB                         | 136 MB  | 2                      | 530 MB

*/

/* I/O port map (incomplete, could well be incorrect too)
 *
 * 0x0000   : Master 8259 PIC
 * 0x0002   : Master 8259 PIC
 * 0x0010   : Slave 8259 PIC
 * 0x0012   : Slave 8259 PIC
 * 0x0020 RW: bit 0 = soft reset (read/write), bit 6 = power off (write), bit 7 = NMI vector protect
 * 0x0022  W: bit 7 = power off (write)
 * 0x0025 R : returns 0x00? (read)
 * 0x0026 R : timer?
 * 0x0028 RW: bit 0 = NMI mask (read/write)
 * 0x0030 R : Machine ID (low)
 * 0x0031 R : Machine ID (high)
 * 0x0032 RW: bit 7 = RESET, bit 6 = CLK, bit 0 = data (serial ROM)
 * 0x0040   : 8253 PIT counter 0
 * 0x0042   : 8253 PIT counter 1
 * 0x0044   : 8253 PIT counter 2
 * 0x0046   : 8253 PIT mode port
 * 0x0060   : 8253 PIT timer control
 * 0x006c RW: returns 0x00? (read) timer? (write)
 * 0x00a0-af: DMA controller 1 (uPD71071)
 * 0x00b0-bf: DMA controller 2 (uPD71071)
 * 0x0200-0f: Floppy controller (MB8877A)
 * 0x0400   : Video / CRTC (unknown)
 * 0x0404   : Disable VRAM, CMOS, memory-mapped I/O (everything in low memory except the BIOS)
 * 0x0440-5f: Video / CRTC
 * 0x0480 RW: bit 1 = disable BIOS ROM
 * 0x048a RW: JEIDA v3/v4(?) IC Memory card status
 * 0x0490 RW: JEIDA v4 IC Memory card page select
 * 0x0491 RW: JEIDA v4 IC Memory card
 * 0x04c0-cf: CD-ROM controller
 * 0x04d5   : Sound mute
 * 0x04d8   : YM3438 control port A / status
 * 0x04da   : YM3438 data port A / status
 * 0x04dc   : YM3438 control port B / status
 * 0x04de   : YM3438 data port B / status
 * 0x04e0-e3: volume ports
 * 0x04e9-ec: IRQ masks
 * 0x04f0-f8: RF5c68 registers
 * 0x05e8 R : RAM size in MB
 * 0x05ec RW: bit 0 = compatibility mode?
 * 0x0600 RW: Keyboard data port (8042)
 * 0x0602   : Keyboard control port (8042)
 * 0x0604   : (8042)
 * 0x0a00-0a: RS-232C interface (i8251)
 * 0x3000 - 0x3fff : CMOS RAM
 * 0xfd90-a0: CRTC / Video
 * 0xff81: CRTC / Video - returns value in RAM location 0xcff81?
 *
 * IRQ list
 *
 *      IRQ0 - PIT Timer IRQ
 *      IRQ1 - Keyboard
 *      IRQ2 - Serial Port
 *      IRQ6 - Floppy Disc Drive
 *      IRQ7 - PIC Cascade IRQ
 *      IRQ8 - SCSI controller
 *      IRQ9 - Built-in CD-ROM controller
 *      IRQ11 - VSync interrupt
 *      IRQ12 - Printer port
 *      IRQ13 - Sound (YM3438/RF5c68), Mouse
 *      IRQ15 - 16-bit PCM (expansion?)
 *
 * Machine ID list (I/O port 0x31)
 *
    1(01h)  FM-TOWNS 1/2
    2(02h)  FM-TOWNS 1F/2F/1H/2H
    3(03h)  FM-TOWNS 10F/20F/40H/80H
    4(04h)  FM-TOWNSII UX
    5(05h)  FM-TOWNSII CX
    6(06h)  FM-TOWNSII UG
    7(07h)  FM-TOWNSII HR
    8(08h)  FM-TOWNSII HG
    9(09h)  FM-TOWNSII UR
    11(0Bh) FM-TOWNSII MA
    12(0Ch) FM-TOWNSII MX
    13(0Dh) FM-TOWNSII ME
    14(0Eh) TOWNS Application Card (PS/V Vision)
    15(0Fh) FM-TOWNSII MF/Fresh/Fresh???TV
    16(10h) FM-TOWNSII SN
    17(11h) FM-TOWNSII HA/HB/HC
    19(13h) FM-TOWNSII EA/Fresh???T/Fresh???ET/Fresh???FT
    20(14h) FM-TOWNSII Fresh???E/Fresh???ES/Fresh???FS
    22(16h) FMV-TOWNS H/Fresh???GS/Fresh???GT/H2
    23(17h) FMV-TOWNS H20
    74(4Ah) FM-TOWNS MARTY
 */

/*

Fujitsu FM Towns Marty

PCB Layout
----------

CA20142-B21X

      CN10                                   CN1
   |-| |-| |-|                    |----------------------|
|--| |-| |-| |----|-|-------------|----------------------|------------|
|14577           CN11  SW2       |--------|  MROM.M37|--------|       |
|14576      TMS48C121DZ-80       |FUJITSU |          |FUJITSU |       |
|           TMS48C121DZ-80       |CG24243 |          |CS10501 |       |
|MB40968    TMS48C121DZ-80       |(QFP208)|  MROM.M36|(QFP160)|       |
||--------| TMS48C121DZ-80       |--------|          |--------| 62256 |
||FUJITSU | HM511664JP8    CN12                    FUJITSU            |
||CE31755 |                                        8451 (DIP8)        |
||(QFP160)|                                                           |
||--------|                                   4.9152MHz               |
|CN13            |--------|  28.63636MHz                            |-|
|                |FUJITSU |                  |----------------------|
|       RTC58323 |CG31553 |                  |                      |
|BATTERY         |(QFP208)|  32MHz   74LS00  |                      |
|                |--------|    |------|      |     CN2              |
|    LC7881            MB84256 |I386SX|      |     PCMCIA SLOT      |
|          TL084    CN4        | -16  |      |                      |
||--------|                    |------|      |                      |
||FUJITSU |    MB814400A-70PZ                |                      |
||CS09501 |    MB814400A-70PZ                |----------------------|
||(QFP120)|    MB814400A-70PZ              |--------|74LS14  74HC08 |-|
||--------|    MB814400A-70PZ              | RICOH  |      3771       |
|             YM3438                       |RU6101MF|                 |
|4560                           MB81C78    |(QFP44) |                 |
|             MB88505 |------|             |--------|                 |
|         CN3         |YM6063|  LED2                     LED1   LED3  |
|CN9  CN8     VOL     |(QFP80|      CN5        CN6    SW1          CN7|
|---------------------|------|----------------------------------------|
Notes:
      All IC's shown.
      Main CPU Intel 80386SX-16 running at 16.000MHz
      CN1   - Multi-pin connector possibly for cartridge or external peripheral connector?
      CN2   - PCMCIA slot
      CN3   - 24 pin connector for ?
      CN4   - CDROM connector? (multi-pin connector with internal slot for a thin cable)
      CN5/6 - DB9 connectors (joystick ports?)
      CN7   - Power input socket
      CN8/9 - Headphone out jacks?
      CN10  - Header with left/right RCA audio jacks and composite video output
      CN11  - S-VIDEO output
      CN12  - 8 pin connector (possibly power related for the CDROM?)
      CN13  - 2 pin connector (fan power?)
      SW1   - Reset Switch?
      SW2   - 2 position slide switch
      MROM* - Hitachi HN624116 16MBit SOP44 maskROM

*/

#include "emu.h"
#include "fmtowns.h"

#include "bus/scsi/scsi.h"
#include "bus/scsi/scsihd.h"

#include "screen.h"
#include "softlist.h"
#include "speaker.h"
#include <math.h>

#define LOG_SYS        (1U << 1)
#define LOG_CD         (1U << 2)
#define LOG_CD_UNKNOWN (1U << 3)

#define VERBOSE (LOG_GENERAL | LOG_CD_UNKNOWN)
#include "logmacro.h"


// CD controller IRQ types
#define TOWNS_CD_IRQ_MPU 1
#define TOWNS_CD_IRQ_DMA 2


enum
{
	MOUSE_START,
	MOUSE_SYNC,
	MOUSE_X_HIGH,
	MOUSE_X_LOW,
	MOUSE_Y_HIGH,
	MOUSE_Y_LOW
};


inline uint8_t towns_state::byte_to_bcd(uint8_t val)
{
	return ((val / 10) << 4) | (val % 10);
}

inline uint8_t towns_state::bcd_to_byte(uint8_t val)
{
	return (((val & 0xf0) >> 4) * 10) + (val & 0x0f);
}

inline uint32_t towns_state::msf_to_lbafm(uint32_t val)  // because the CDROM core doesn't provide this
{
	uint8_t m,s,f;
	f = bcd_to_byte(val & 0x0000ff);
	s = (bcd_to_byte((val & 0x00ff00) >> 8));
	m = (bcd_to_byte((val & 0xff0000) >> 16));
	return ((m * (60 * 75)) + (s * 75) + f) - 150;
}

void towns_state::init_serial_rom()
{
	// TODO: init serial ROM contents
	int x;
	static const uint8_t code[8] = { 0x04,0x65,0x54,0xA4,0x95,0x45,0x35,0x5F };
	uint8_t* srom = nullptr;

	if(m_serial)
		srom = m_serial->base();
	memset(m_towns_serial_rom.get(),0,256/8);

	if(srom)
	{
		memcpy(m_towns_serial_rom.get(),srom,32);
		m_towns_machine_id = (m_towns_serial_rom[0x18] << 8) | m_towns_serial_rom[0x17];
		logerror("Machine ID in serial ROM: %04x\n",m_towns_machine_id);
		return;
	}

	for(x=8;x<=21;x++)
		m_towns_serial_rom[x] = 0xff;

	for(x=0;x<=7;x++)
	{
		m_towns_serial_rom[x] = code[x];
	}

	// add Machine ID
	m_towns_machine_id = 0x0101;
	m_towns_serial_rom[0x17] = 0x01;
	m_towns_serial_rom[0x18] = 0x01;

	// serial number?
	m_towns_serial_rom[29] = 0x10;
	m_towns_serial_rom[28] = 0x6e;
	m_towns_serial_rom[27] = 0x54;
	m_towns_serial_rom[26] = 0x32;
	m_towns_serial_rom[25] = 0x10;
}

uint8_t towns_state::towns_system_r(offs_t offset)
{
	uint8_t ret = 0;

	switch(offset)
	{
		case 0x00:
			LOGMASKED(LOG_SYS, "SYS: port 0x20 read\n");
			return 0x00;
		case 0x05:
			LOGMASKED(LOG_SYS, "SYS: port 0x25 read\n");
			return 0x00;
/*      case 0x06:
            count = (m_towns_freerun_counter->elapsed() * ATTOSECONDS_TO_HZ(ATTOSECONDS_IN_USEC(1))).as_double();
            return count & 0xff;
        case 0x07:
            count = (m_towns_freerun_counter->elapsed() * ATTOSECONDS_TO_HZ(ATTOSECONDS_IN_USEC(1))).as_double();
            return (count >> 8) & 0xff;
*/      case 0x06:
			//LOGMASKED(LOG_SYS, "SYS: (0x26) timer read\n");
			return m_freerun_timer;
		case 0x07:
			return m_freerun_timer >> 8;
		case 0x08:
			//LOGMASKED(LOG_SYS, "SYS: (0x28) NMI mask read\n");
			return m_nmi_mask & 0x01;
		case 0x10:
			LOGMASKED(LOG_SYS, "SYS: (0x30) Machine ID read\n");
			return (m_towns_machine_id >> 8) & 0xff;
		case 0x11:
			LOGMASKED(LOG_SYS, "SYS: (0x31) Machine ID read\n");
			return m_towns_machine_id & 0xff;
		case 0x12:
			/* Bit 0 = data, bit 6 = CLK, bit 7 = RESET, bit 5 is always 1? */
			ret = (m_towns_serial_rom[m_towns_srom_position/8] & (1 << (m_towns_srom_position%8))) ? 1 : 0;
			ret |= m_towns_srom_clk;
			ret |= m_towns_srom_reset;
			//LOGMASKED(LOG_SYS, "SYS: (0x32) Serial ROM read [0x%02x, pos=%i]\n",ret,towns_srom_position);
			return ret;
		default:
			//LOGMASKED(LOG_SYS, "SYS: Unknown system port read (0x%02x)\n",offset+0x20);
			return 0x00;
	}
}

void towns_state::towns_system_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
		case 0x00:  // bit 7 = NMI vector protect, bit 6 = power off, bit 0 = software reset, bit 3 = A20 line?
//          space.m_maincpu->set_input_line(INPUT_LINE_A20,(data & 0x08) ? CLEAR_LINE : ASSERT_LINE);
			LOGMASKED(LOG_SYS, "SYS: port 0x20 write %02x\n",data);
			break;
		case 0x02:
			LOGMASKED(LOG_SYS, "SYS: (0x22) power port write %02x\n",data);
			break;
		case 0x08:
			//LOGMASKED(LOG_SYS, "SYS: (0x28) NMI mask write %02x\n",data);
			m_nmi_mask = data & 0x01;
			break;
		case 0x12:
			//LOGMASKED(LOG_SYS, "SYS: (0x32) Serial ROM write %02x\n",data);
			// clocks on low-to-high transition
			if((data & 0x40) && m_towns_srom_clk == 0) // CLK
			{  // advance to next bit
				m_towns_srom_position++;
			}
			if((data & 0x80) && m_towns_srom_reset == 0) // reset
			{  // reset to beginning
				m_towns_srom_position = 0;
			}
			m_towns_srom_clk = data & 0x40;
			m_towns_srom_reset = data & 0x80;
			break;
		default:
			LOGMASKED(LOG_SYS, "SYS: Unknown system port write 0x%02x (0x%02x)\n",data,offset);
			break;
	}
}

void towns_state::towns_intervaltimer2_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
	case 0x00:
		m_intervaltimer2_irqmask = data & 0x80;
		break;
	case 0x02:
		m_intervaltimer2_period = (m_intervaltimer2_period & 0xff00) | data;
		popmessage("Interval Timer 2 period changed to %04x",m_intervaltimer2_period);
		break;
	case 0x03:
		m_intervaltimer2_period = (data << 8) | (m_intervaltimer2_period & 0x00ff);
		popmessage("Interval Timer 2 period changed to %04x",m_intervaltimer2_period);
		break;
	}
}

uint8_t towns_state::towns_intervaltimer2_r(offs_t offset)
{
	uint8_t ret = 0;

	switch(offset)
	{
	case 0x00:
		if(m_intervaltimer2_timeout_flag != 0)
			ret |= 0x40;
		if(m_intervaltimer2_irqmask != 0)
			ret |= 0x80;
		m_intervaltimer2_timeout_flag = 0;  // flag reset on read
		return ret;
	case 0x02:
		return m_intervaltimer2_period & 0x00ff;
	case 0x03:
		return m_intervaltimer2_period >> 8;
	}
	return 0xff;
}

TIMER_CALLBACK_MEMBER(towns_state::freerun_inc)
{
	m_freerun_timer++;
}

TIMER_CALLBACK_MEMBER(towns_state::intervaltimer2_timeout)
{
	m_intervaltimer2_timeout_flag = 1;
}

TIMER_CALLBACK_MEMBER(towns_state::wait_end)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT,CLEAR_LINE);
}

uint8_t towns_state::towns_sys6c_r()
{
	LOGMASKED(LOG_SYS, "SYS: (0x6c) Timer? read\n");
	return 0x00;
}

void towns_state::towns_sys6c_w(uint8_t data)
{
	// halts the CPU for 1 microsecond
	m_maincpu->set_input_line(INPUT_LINE_HALT,ASSERT_LINE);
	m_towns_wait_timer->adjust(attotime::from_usec(1),0,attotime::never);
}

template<int Chip>
uint8_t towns_state::towns_dma_r(offs_t offset)
{
	logerror("DMA#%01x: read register %i\n",Chip,offset);
	return m_dma[Chip]->read(offset);
}

template<int Chip>
void towns_state::towns_dma_w(offs_t offset, uint8_t data)
{
	logerror("DMA#%01x: wrote 0x%02x to register %i\n",Chip,data,offset);
	m_dma[Chip]->write(offset, data);
}

/*
 *  Floppy Disc Controller (MB8877A)
 */

void towns_state::mb8877a_irq_w(int state)
{
	if(m_towns_fdc_irq6mask == 0)
		state = 0;
	m_pic_master->ir6_w(state);  // IRQ6 = FDC
	if(IRQ_LOG) logerror("PIC: IRQ6 (FDC) set to %i\n",state);
}

void towns_state::mb8877a_drq_w(int state)
{
	m_dma[0]->dmarq(state, 0);
}

uint8_t towns_state::towns_floppy_r(offs_t offset)
{
	uint8_t ret;

	switch(offset)
	{
		case 0x00:
			return m_fdc->status_r();
		case 0x02:
			return m_fdc->track_r();
		case 0x04:
			return m_fdc->sector_r();
		case 0x06:
			return m_fdc->data_r();
		case 0x08:  // selected drive status?
			//logerror("FDC: read from offset 0x08\n");
			ret = 0x80;  // always set
			switch(m_towns_selected_drive)
			{
			case 1:
				ret |= 0x0c;
				if(m_flop[0]->get_device() && m_flop[0]->get_device()->exists())
					ret |= 0x03;
				break;
			case 2:
				ret |= 0x0c;
				if(m_flop[1]->get_device() && m_flop[1]->get_device()->exists())
					ret |= 0x03;
				break;
			case 3:
			case 4:
			case 0:
			default:
				break;
			}
			return ret;
		case 0x0e: // DRVCHG
			logerror("FDC: read from offset 0x0e\n");
			if(m_towns_selected_drive == 1)
				if (m_flop[0]->get_device())
					return m_flop[0]->get_device()->dskchg_r();
			if(m_towns_selected_drive == 2)
				if (m_flop[1]->get_device())
					return m_flop[1]->get_device()->dskchg_r();
			return 0x00;
		default:
			logerror("FDC: read from invalid or unimplemented register %02x\n",offset);
	}
	return 0xff;
}

void towns_state::towns_floppy_w(offs_t offset, uint8_t data)
{
	floppy_image_device* sel[4] = { m_flop[0]->get_device(), m_flop[1]->get_device(), nullptr, nullptr };

	switch(offset)
	{
		case 0x00:
			// Commands 0xd0 and 0xfe (Write Track) are apparently ignored?
			if(data == 0xd0)
				return;
			if(data == 0xfe)
				return;
			m_fdc->cmd_w(data);
			logerror("FDC: Command %02x\n",data);
			break;
		case 0x02:
			m_fdc->track_w(data);
			logerror("FDC: Track %02x\n",data);
			break;
		case 0x04:
			m_fdc->sector_w(data);
			logerror("FDC: Sector %02x\n",data);
			break;
		case 0x06:
			m_fdc->data_w(data);
			logerror("FDC: Data %02x\n",data);
			break;
		case 0x08:
		{
			// bit 5 - CLKSEL
			// docs are unclear about this but there's only one motor control line and turning on only the selected drive doesn't work properly.
			for(int i = 0; i < 4; i++)
			{
				if(sel[i] != nullptr)
				{
					sel[i]->mon_w((~data & 0x10)>>4);
					sel[i]->ss_w((data & 0x04)>>2);
				}
			}
			m_fdc->dden_w(BIT(~data, 1));

			m_towns_fdc_irq6mask = data & 0x01;
			//logerror("FDC: Config drive%i %02x\n",m_towns_selected_drive-1,data);

			break;
		}
		case 0x0c:  // drive select
			switch(data & 0x0f)
			{
				case 0x00:
					m_towns_selected_drive = 0;  // No drive selected
					break;
				case 0x01:
					m_towns_selected_drive = 1;
					if(sel[0] != nullptr)
						m_fdc->set_floppy(sel[0]);
					break;
				case 0x02:
					m_towns_selected_drive = 2;
					if(sel[1] != nullptr)
						m_fdc->set_floppy(sel[1]);
					break;
				case 0x04:
					m_towns_selected_drive = 3;
					if(sel[2] != nullptr)
						m_fdc->set_floppy(sel[2]);
					break;
				case 0x08:
					m_towns_selected_drive = 4;
					if(sel[3] != nullptr)
						m_fdc->set_floppy(sel[3]);
					break;
			}
			//logerror("FDC: drive select %02x\n",data);
			break;
		default:
			logerror("FDC: write %02x to invalid or unimplemented register %02x\n",data,offset);
	}
}

uint16_t towns_state::towns_fdc_dma_r()
{   uint16_t data = m_fdc->data_r();
	return data;
}

void towns_state::towns_fdc_dma_w(uint16_t data)
{
	m_fdc->data_w(data);
}

/*
 *  Port 0x600-0x607 - Keyboard controller (8042 MCU)
 *
 *  Sends two-byte code on each key press and release.
 *  First byte has the MSB set, and contains shift/ctrl/keyboard type flags
 *    Known bits:
 *      bit 7 = always 1
 *      bits 6-5 = keyboard type
 *        00 = thumb shift (NICOLA) keyboard
 *        01 = JIS keyboard
 *        10 = new JIS keyboard (with ALT key?)
 *        11 = extended use (?)
 *      bit 4 = key release
 *      bit 3 = ctrl
 *      bit 2 = shift
 *      bit 1 = left shift (thumb shift only)
 *      bit 0 = right shift (thumb shift only)
 *
 *  Second byte has the MSB reset, and contains the scancode of the key
 *  pressed or released.
 *      bit 7 = always 0
 *      bits 6-0 = key scancode
 */
void towns_state::kb_sendcode(uint8_t scancode, int release)
{
	switch(release)
	{
		case 0:  // key press
			m_towns_kb_output = 0xc0;
			m_towns_kb_extend = scancode & 0x7f;
			if (m_kb_ports[2]->read() & 0x00080000)
				m_towns_kb_output |= 0x04;
			if (m_kb_ports[2]->read() & 0x00040000)
				m_towns_kb_output |= 0x08;
			break;
		case 1:  // key release
			m_towns_kb_output = 0xd0;
			m_towns_kb_extend = scancode & 0x7f;
			if (m_kb_ports[2]->read() & 0x00080000)
				m_towns_kb_output |= 0x04;
			if (m_kb_ports[2]->read() & 0x00040000)
				m_towns_kb_output |= 0x08;
			break;
		case 2:  // extended byte
			m_towns_kb_output = scancode;
			m_towns_kb_extend = 0xff;
			break;
	}
	m_towns_kb_status |= 0x01;
	if(m_towns_kb_irq1_enable)
	{
		m_pic_master->ir1_w(1);
		if(IRQ_LOG) logerror("PIC: IRQ1 (keyboard) set high\n");
	}
	//logerror("KB: sending scancode 0x%02x\n",scancode);
}

TIMER_CALLBACK_MEMBER(towns_state::poll_keyboard)
{
	uint8_t scan = 0;
	for(int port = 0; port < 4; port++)
	{
		uint32_t portval = m_kb_ports[port]->read();
		for(int bit = 0; bit < 32; bit++)
		{
			if(BIT(portval, bit) != BIT(m_kb_prev[port], bit))
			{  // bit changed
				if(BIT(portval, bit) == 0)  // release
					kb_sendcode(scan, 1);
				else
					kb_sendcode(scan, 0);
			}
			scan++;
		}
		m_kb_prev[port] = portval;
	}
}

uint8_t towns_state::towns_keyboard_r(offs_t offset)
{
	uint8_t ret = 0x00;

	switch(offset)
	{
		case 0:  // scancode output
			ret = m_towns_kb_output;
			//logerror("KB: read keyboard output port, returning %02x\n",ret);
			m_pic_master->ir1_w(0);
			if(IRQ_LOG) logerror("PIC: IRQ1 (keyboard) set low\n");
			if(m_towns_kb_extend != 0xff)
			{
				kb_sendcode(m_towns_kb_extend,2);
			}
			else
				m_towns_kb_status &= ~0x01;
			return ret;
		case 1:  // status
			//logerror("KB: read status port, returning %02x\n",m_towns_kb_status);
			return m_towns_kb_status;
		default:
			logerror("KB: read offset %02x\n",offset);
	}
	return 0x00;
}

void towns_state::towns_keyboard_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
		case 0:  // command input
			m_towns_kb_status &= ~0x08;
			m_towns_kb_status |= 0x01;
			break;
		case 1:  // control
			m_towns_kb_status |= 0x08;
			break;
		case 2:  // IRQ1 enable
			m_towns_kb_irq1_enable = data & 0x01;
			break;
		default:
			logerror("KB: wrote 0x%02x to offset %02x\n",data,offset);
	}
}

/*
 *  Port 0x60 - PIT Timer control
 *  On read:    bit 0: Timer 0 output level
 *              bit 1: Timer 1 output level
 *              bits 4-2: Timer masks (timer 2 = beeper)
 *  On write:   bits 2-0: Timer mask set
 *              bit 7: Timer 0 output reset
 */
uint8_t towns_state::speaker_get_spk()
{
	return m_towns_spkrdata & m_pit_out2;
}


void towns_state::speaker_set_spkrdata(uint8_t data)
{
	m_towns_spkrdata = data ? 1 : 0;
	m_speaker->level_w(speaker_get_spk());
}


uint8_t towns_state::towns_port60_r()
{
	uint8_t val = 0x00;

	if (m_pit_out0)
		val |= 0x01;
	if (m_pit_out1)
		val |= 0x02;

	val |= (m_towns_timer_mask & 0x07) << 2;

	//logerror("PIT: port 0x60 read, returning 0x%02x\n",val);
	return val;
}

void towns_state::towns_port60_w(uint8_t data)
{
	if(data & 0x80)
	{
		//towns_pic_irq(dev,0);
		m_timer0 = 0;
		m_pic_master->ir0_w(m_timer0 || m_timer1);
	}
	m_towns_timer_mask = data & 0x07;

	speaker_set_spkrdata(data & 0x04);

	//logerror("PIT: wrote 0x%02x to port 0x60\n",data);
}

uint8_t towns_state::towns_sys5e8_r(offs_t offset)
{
	switch(offset)
	{
		case 0x00:
			LOGMASKED(LOG_SYS, "SYS: read RAM size port (%i)\n",m_ram->size());
			return m_ram->size()/1048576;
		case 0x02:
			LOGMASKED(LOG_SYS, "SYS: read port 5ec\n");
			return m_compat_mode & 0x01;
	}
	return 0x00;
}

void towns_state::towns_sys5e8_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
		case 0x00:
			LOGMASKED(LOG_SYS, "SYS: wrote 0x%02x to port 5e8\n",data);
			break;
		case 0x02:
			LOGMASKED(LOG_SYS, "SYS: wrote 0x%02x to port 5ec\n",data);
			m_compat_mode = data & 0x01;
			break;
	}
}

// Sound/LED control (I/O port 0x4e8-0x4ef)
// R/O  -- (0x4e9) FM IRQ flag (bit 0), PCM IRQ flag (bit 3)
// (0x4ea) PCM IRQ mask
// R/W  -- (0x4eb) PCM IRQ flag
// W/O  -- (0x4ec) LED control
uint8_t towns_state::towns_sound_ctrl_r(offs_t offset)
{
	uint8_t ret = 0;

	switch(offset)
	{
		case 0x00:
			ret = 1;
			break;
		case 0x01:
			if(m_towns_fm_irq_flag)
				ret |= 0x01;
			if(m_towns_pcm_irq_flag)
				ret |= 0x08;
			break;
		case 0x02:
			ret = m_towns_pcm_channel_mask;
			break;
		case 0x03:
			ret = m_towns_pcm_channel_flag;
			m_towns_pcm_channel_flag = 0;
			m_towns_pcm_irq_flag = 0;
			if(m_towns_fm_irq_flag == 0)
			{
				m_pic_slave->ir5_w(0);
				if(IRQ_LOG) logerror("PIC: IRQ13 (PCM) set low\n");
			}
			break;
//      default:
			//logerror("FM: unimplemented port 0x%04x read\n",offset + 0x4e8);
	}
	return ret;
}

void towns_state::towns_sound_ctrl_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
		case 0x02:  // PCM channel interrupt mask
			m_towns_pcm_channel_mask = data;
			break;
		default:
			logerror("FM: unimplemented port 0x%04x write %02x\n",offset + 0x4e8,data);
	}
}

// Controller ports
// Joysticks are multiplexed, with fire buttons available when bits 0 and 1 of port 0x4d6 are high. (bits 2 and 3 for second port)
uint8_t towns_state::towns_padport_r(offs_t offset)
{
	// Documentation indicates bit 7 is unused and should be ignored.
	// Tatsujin Ou expects it to read as zero to navigate menus.
	// Unclear whether it always reads as zero, or it's affected by something undocumented.
	unsigned const pad = BIT(offset, 1);
	return m_pad_ports[pad]->read() & (0x0f | (bitswap<3>(m_towns_pad_mask, pad + 4, (pad * 2) + 1, pad * 2) << 4));
}

void towns_state::towns_pad_mask_w(uint8_t data)
{
	m_towns_pad_mask = data;

	m_pad_ports[0]->pin_6_w(BIT(data, 0));
	m_pad_ports[0]->pin_7_w(BIT(data, 1));
	m_pad_ports[0]->pin_8_w(BIT(data, 4));

	m_pad_ports[1]->pin_6_w(BIT(data, 2));
	m_pad_ports[1]->pin_7_w(BIT(data, 3));
	m_pad_ports[1]->pin_8_w(BIT(data, 5));
}

uint8_t towns_state::towns_cmos_low_r(offs_t offset)
{
	if(m_towns_mainmem_enable != 0)
		return m_ram->pointer()[offset + 0xd8000];

	if(m_nvram)
		return m_nvram[offset >> 2] >> ((offset & 3) << 3);
	else
		return m_nvram16[offset >> 1] >> ((offset & 1) << 3);
}

void towns_state::towns_cmos_low_w(offs_t offset, uint8_t data)
{
	if(m_towns_mainmem_enable != 0)
		m_ram->pointer()[offset+0xd8000] = data;
	else
		if(m_nvram)
		{
			uint8_t shift = (offset & 3) << 3;
			m_nvram[offset >> 2] &= ~(0xff << shift);
			m_nvram[offset >> 2] |= (uint32_t)data << shift;
		}
		else
		{
			uint8_t shift = (offset & 1) << 3;
			m_nvram16[offset >> 1] &= ~(0xff << shift);
			m_nvram16[offset >> 1] |= (uint16_t)data << shift;
		}
}

uint8_t towns_state::towns_cmos_r(offs_t offset)
{
	if(m_nvram)
		return m_nvram[offset >> 2] >> ((offset & 3) << 3);
	else
		return m_nvram16[offset >> 1] >> ((offset & 1) << 3);
}

void towns_state::towns_cmos_w(offs_t offset, uint8_t data)
{
	if(m_nvram)
	{
		uint8_t shift = (offset & 3) << 3;
		m_nvram[offset >> 2] &= ~(0xff << shift);
		m_nvram[offset >> 2] |= (uint32_t)data << shift;
	}
	else
	{
		uint8_t shift = (offset & 1) << 3;
		m_nvram16[offset >> 1] &= ~(0xff << shift);
		m_nvram16[offset >> 1] |= (uint16_t)data << shift;
	}
}

void towns_state::towns_update_video_banks()
{
	uint8_t* ROM = m_user->base();

	if(m_towns_mainmem_enable != 0)  // first MB is RAM
	{
//      membank(1)->set_base(m_ram->pointer()+0xc0000);
//      membank(2)->set_base(m_ram->pointer()+0xc8000);
//      membank(3)->set_base(m_ram->pointer()+0xc9000);
//      membank(4)->set_base(m_ram->pointer()+0xca000);
//      membank(5)->set_base(m_ram->pointer()+0xca000);
//      membank(10)->set_base(m_ram->pointer()+0xca800);
		m_bank_cb000_r->set_base(m_ram->pointer()+0xcb000);
		m_bank_cb000_w->set_base(m_ram->pointer()+0xcb000);
		if(m_towns_system_port & 0x02)
			m_bank_f8000_r->set_base(m_ram->pointer()+0xf8000);
		else
			m_bank_f8000_r->set_base(ROM+0x238000);
		m_bank_f8000_w->set_base(m_ram->pointer()+0xf8000);
		return;
	}
	else  // enable I/O ports and VRAM
	{
//      membank(1)->set_base(towns_gfxvram+(towns_vram_rplane*0x8000));
//      membank(2)->set_base(towns_txtvram);
//      membank(3)->set_base(m_ram->pointer()+0xc9000);
//      if(towns_ankcg_enable != 0)
//          membank(4)->set_base(ROM+0x180000+0x3d000);  // ANK CG 8x8
//      else
//          membank(4)->set_base(towns_txtvram+0x2000);
//      membank(5)->set_base(towns_txtvram+0x2000);
//      membank(10)->set_base(m_ram->pointer()+0xca800);
		if(m_towns_ankcg_enable != 0)
			m_bank_cb000_r->set_base(ROM+0x180000+0x3d800);  // ANK CG 8x16
		else
			m_bank_cb000_r->set_base(m_ram->pointer()+0xcb000);
		m_bank_cb000_w->set_base(m_ram->pointer()+0xcb000);
		if(m_towns_system_port & 0x02)
			m_bank_f8000_r->set_base(m_ram->pointer()+0xf8000);
		else
			m_bank_f8000_r->set_base(ROM+0x238000);
		m_bank_f8000_w->set_base(m_ram->pointer()+0xf8000);
		return;
	}
}

uint8_t towns_state::towns_sys480_r()
{
	if(m_towns_system_port & 0x02)
		return 0x02;
	else
		return 0x00;
}

void towns_state::towns_sys480_w(uint8_t data)
{
	m_towns_system_port = data;
	m_towns_ram_enable = data & 0x02;
	towns_update_video_banks();
}

void towns_state::towns_video_404_w(uint8_t data)
{
	m_towns_mainmem_enable = data & 0x80;
	towns_update_video_banks();
}

uint8_t towns_state::towns_video_404_r()
{
	if(m_towns_mainmem_enable != 0)
		return 0x80;
	else
		return 0x00;
}

/*
 *  I/O ports 0x4c0-0x4cf
 *  CD-ROM driver (custom?)
 *
 *  0x4c0 - Status port (R/W)
 *    bit 7 - IRQ from sub MPU (reset when read)
 *    bit 6 - IRQ from DMA end (reset when read)
 *    bit 5 - Software transfer
 *    bit 4 - DMA transfer
 *    bit 1 - status read request
 *    bit 0 - ready
 *    Note: IRQ bits are only set high if the IRQ bit in the command byte is NOT set.
 *
 *  0x4c2 - Command port (R/W)
 *    On read, returns status byte (4 in total?)
 *    On write, performs specified command:
 *      bit 7 - command type
 *      bit 6 - IRQ
 *      bit 5 - status
 *      bits 4-0 - command
 *        Type=1:
 *          0 = set state
 *          1 = set state (CDDASET)
 *        Type=0:
 *          0 = Seek
 *          2 = Read (MODE1)
 *          5 = TOC Read
 *
 *  0x4c4 - Parameter port (R/W)
 *    Inserts a byte into an array of 8 bytes used for command parameters
 *    Writing to this port puts the byte at the front of the array, and
 *    pushes the other parameters back.
 *
 *  0x4c6 (W/O)
 *    bit 3 - software transfer mode
 *    bit 4 - DMA transfer mode
 *
 */
void towns_state::towns_cdrom_set_irq(int line,int state)
{
	switch(line)
	{
		case TOWNS_CD_IRQ_MPU:
			if(state != 0)
			{
				if(m_towns_cd.command & 0x40)
				{
//                  if(m_towns_cd.mpu_irq_enable)
					{
						m_towns_cd.status |= 0x80;
						m_pic_slave->ir1_w(1);
						if(IRQ_LOG) logerror("PIC: IRQ9 (CD-ROM) set high\n");
					}
				}
				else
					m_towns_cd.status |= 0x80;
			}
			else
			{
				m_towns_cd.status &= ~0x80;
				m_pic_slave->ir1_w(0);
				if(IRQ_LOG) logerror("PIC: IRQ9 (CD-ROM) set low\n");
			}
			break;
		case TOWNS_CD_IRQ_DMA:
			if(state != 0)
			{
				if(m_towns_cd.command & 0x40)
				{
//                  if(m_towns_cd.dma_irq_enable)
					{
						m_towns_cd.status |= 0x40;
						m_pic_slave->ir1_w(1);
						if(IRQ_LOG) logerror("PIC: IRQ9 (CD-ROM DMA) set high\n");
					}
				}
				else
					m_towns_cd.status |= 0x40;
			}
			else
			{
				m_towns_cd.status &= ~0x40;
				m_pic_slave->ir1_w(0);
				if(IRQ_LOG) logerror("PIC: IRQ9 (CD-ROM DMA) set low\n");
			}
			break;
	}
}

TIMER_CALLBACK_MEMBER(towns_state::towns_cd_status_ready)
{
	m_towns_cd.status |= 0x02;  // status read request
	m_towns_cd.status |= 0x01;  // ready
	m_towns_cd.cmd_status_ptr = 0;
	towns_cdrom_set_irq(TOWNS_CD_IRQ_MPU,1);
}

void towns_state::towns_cd_set_status(uint8_t st0, uint8_t st1, uint8_t st2, uint8_t st3)
{
	m_towns_cd.cmd_status[0] = st0;
	m_towns_cd.cmd_status[1] = st1;
	m_towns_cd.cmd_status[2] = st2;
	m_towns_cd.cmd_status[3] = st3;
	// wait a bit
	m_towns_status_timer->adjust(attotime::from_msec(1),0,attotime::never);
}

uint8_t towns_state::towns_cd_get_track()
{
	cdrom_image_device* cdrom = m_cdrom;
	uint32_t lba = m_cdda->get_audio_lba();
	uint8_t track;

	for(track=1;track<99;track++)
	{
		if(cdrom->get_track_start(track) > lba)
			break;
	}
	return track;
}

TIMER_CALLBACK_MEMBER(towns_state::towns_cdrom_read_byte)
{
	upd71071_device* device = m_dma_1.target();
	int masked;
	// TODO: support software transfers, for now DMA is assumed.

	if(m_towns_cd.buffer_ptr < 0) // transfer has ended
		return;

	masked = device->dmarq(param, 3);  // CD-ROM controller uses DMA1 channel 3
//  logerror("DMARQ: param=%i ret=%i bufferptr=%i\n",param,masked,m_towns_cd.buffer_ptr);
	if(param != 0)
	{
		m_towns_cd.read_timer->adjust(attotime::from_hz(300000));
	}
	else
	{
		if(masked != 0)  // check if the DMA channel is masked
		{
			m_towns_cd.read_timer->adjust(attotime::from_hz(300000),1);
			return;
		}
		if(m_towns_cd.buffer_ptr < 2048)
			m_towns_cd.read_timer->adjust(attotime::from_hz(300000),1);
		else
		{  // end of transfer
			m_towns_cd.status &= ~0x10;  // no longer transferring by DMA
			m_towns_cd.status &= ~0x20;  // no longer transferring by software
			LOGMASKED(LOG_CD, "DMA1: end of transfer (LBA=%08x)\n",m_towns_cd.lba_current);
			if(m_towns_cd.lba_current >= m_towns_cd.lba_last)
			{
				m_towns_cd.extra_status = 0;
				towns_cd_set_status(0x06,0x00,0x00,0x00);
				towns_cdrom_set_irq(TOWNS_CD_IRQ_DMA,1);
				m_towns_cd.buffer_ptr = -1;
				m_towns_cd.status |= 0x01;  // ready
			}
			else
			{
				m_towns_cd.extra_status = 0;
				towns_cd_set_status(0x22,0x00,0x00,0x00);
				towns_cdrom_set_irq(TOWNS_CD_IRQ_DMA,1);
				m_cdrom->read_data(++m_towns_cd.lba_current,m_towns_cd.buffer,cdrom_file::CD_TRACK_MODE1);
				m_towns_cd.read_timer->adjust(attotime::from_hz(300000),1);
				m_towns_cd.buffer_ptr = -1;
			}
		}
	}
}

uint8_t towns_state::towns_cdrom_read_byte_software()
{
	uint8_t ret;
	if(m_towns_cd.buffer_ptr < 0) // transfer has ended
		return 0x00;

	ret = m_towns_cd.buffer[m_towns_cd.buffer_ptr++];

	if(m_towns_cd.buffer_ptr >= 2048)
	{  // end of transfer
		m_towns_cd.status &= ~0x10;  // no longer transferring by DMA
		m_towns_cd.status &= ~0x20;  // no longer transferring by software
		LOGMASKED(LOG_CD, "CD: end of software transfer (LBA=%08x)\n",m_towns_cd.lba_current);
		if(m_towns_cd.lba_current >= m_towns_cd.lba_last)
		{
			m_towns_cd.extra_status = 0;
			towns_cd_set_status(0x06,0x00,0x00,0x00);
			towns_cdrom_set_irq(TOWNS_CD_IRQ_DMA,1);
			m_towns_cd.buffer_ptr = -1;
			m_towns_cd.status |= 0x01;  // ready
		}
		else
		{
			m_cdrom->read_data(++m_towns_cd.lba_current,m_towns_cd.buffer,cdrom_file::CD_TRACK_MODE1);
			m_towns_cd.extra_status = 0;
			towns_cd_set_status(0x21,0x00,0x00,0x00);
			towns_cdrom_set_irq(TOWNS_CD_IRQ_DMA,1);
			m_towns_cd.status &= ~0x10;
			m_towns_cd.status |= 0x20;
			m_towns_cd.buffer_ptr = -1;
		}
	}
	return ret;
}

void towns_state::towns_cdrom_read(cdrom_image_device* device)
{
	// MODE 1 read
	// load data into buffer to be sent via DMA1 channel 3
	// A set of status bytes is sent after each sector, and DMA is paused
	// so that the DMA controller than be set up again.
	// parameters:
	//          3 bytes: MSF of first sector to read
	//          3 bytes: MSF of last sector to read
	uint32_t lba1,lba2,track;

	lba1 = m_towns_cd.parameter[7] << 16;
	lba1 += m_towns_cd.parameter[6] << 8;
	lba1 += m_towns_cd.parameter[5];
	lba2 = m_towns_cd.parameter[4] << 16;
	lba2 += m_towns_cd.parameter[3] << 8;
	lba2 += m_towns_cd.parameter[2];
	m_towns_cd.lba_current = msf_to_lbafm(lba1);
	m_towns_cd.lba_last = msf_to_lbafm(lba2);

	track = device->get_track(m_towns_cd.lba_current);

	// parameter 7 = sector count?
	// lemmings 2 sets this to 4 but hates 4 extra sectors being read
//  if(m_towns_cd.parameter[1] != 0)
//      m_towns_cd.lba_last += m_towns_cd.parameter[1];

	LOGMASKED(LOG_CD, "CD: Mode 1 read from LBA next:%i last:%i track:%i\n",m_towns_cd.lba_current,m_towns_cd.lba_last,track);

	if(m_towns_cd.lba_current > m_towns_cd.lba_last)
	{
		m_towns_cd.extra_status = 0;
		towns_cd_set_status(0x01,0x00,0x00,0x00);
	}
	else
	{
		device->read_data(m_towns_cd.lba_current,m_towns_cd.buffer,cdrom_file::CD_TRACK_MODE1);
		if(m_towns_cd.software_tx)
		{
			m_towns_cd.status &= ~0x10;  // not a DMA transfer
			m_towns_cd.status |= 0x20;  // software transfer
		}
		else
		{
			m_towns_cd.status |= 0x10;  // DMA transfer begin
			m_towns_cd.status &= ~0x20;  // not a software transfer
		}
//      m_towns_cd.buffer_ptr = 0;
//      m_towns_cd.read_timer->adjust(attotime::from_hz(300000),1);
		if(m_towns_cd.command & 0x20)
		{
			m_towns_cd.extra_status = 2;
			towns_cd_set_status(0x00,0x00,0x00,0x00);
		}
		else
		{
			m_towns_cd.extra_status = 0;
			if(m_towns_cd.software_tx)
				towns_cd_set_status(0x21,0x00,0x00,0x00);
			else
				towns_cd_set_status(0x22,0x00,0x00,0x00);
		}
	}
}

void towns_state::towns_cdrom_play_cdda(cdrom_image_device* device)
{
	// PLAY AUDIO
	// Plays CD-DA audio from the specified MSF
	// Parameters:
	//          3 bytes: starting MSF of audio to play
	//          3 bytes: ending MSF of audio to play (can span multiple tracks)
	uint32_t lba1,lba2;

	lba1 = m_towns_cd.parameter[7] << 16;
	lba1 += m_towns_cd.parameter[6] << 8;
	lba1 += m_towns_cd.parameter[5];
	lba2 = m_towns_cd.parameter[4] << 16;
	lba2 += m_towns_cd.parameter[3] << 8;
	lba2 += m_towns_cd.parameter[2];
	m_towns_cd.cdda_current = msf_to_lbafm(lba1);
	m_towns_cd.cdda_length = msf_to_lbafm(lba2) - m_towns_cd.cdda_current + 1;

	m_cdda->start_audio(m_towns_cd.cdda_current,m_towns_cd.cdda_length);
	LOGMASKED(LOG_CD, "CD: CD-DA start from LBA:%i length:%i\n",m_towns_cd.cdda_current,m_towns_cd.cdda_length);
	if(m_towns_cd.command & 0x20)
	{
		m_towns_cd.extra_status = 1;
		towns_cd_set_status(0x00,0x03,0x00,0x00);
	}
}

TIMER_CALLBACK_MEMBER(towns_state::towns_delay_cdda)
{
	towns_cdrom_play_cdda(m_cdrom.target());
}

TIMER_CALLBACK_MEMBER(towns_state::towns_delay_seek)
{
	m_towns_cd.extra_status = 0;
	towns_cd_set_status(0x04,0x00,0x00,0x00);
}

void towns_state::towns_cdrom_execute_command(cdrom_image_device* device)
{
	towns_cdrom_set_irq(TOWNS_CD_IRQ_MPU,0); // TODO: this isn't sufficiently tested
	m_towns_seek_timer->adjust(attotime::never);
	if(!device->exists() && (m_towns_cd.command != 0xa0))
	{  // No CD in drive
		if(m_towns_cd.command & 0x20)
		{
			m_towns_cd.extra_status = 0;
			towns_cd_set_status(0x10,0x00,0x00,0x00);
		}
	}
	else
	{
		m_towns_cd.status &= ~0x02;
		switch(m_towns_cd.command & 0x9f)
		{
			case 0x00:  // Seek
				if(m_towns_cd.command & 0x20)
				{
					m_towns_cd.extra_status = 0;
					towns_cd_set_status(0x00,0x00,0x00,0x00);
					m_towns_seek_timer->adjust(attotime::from_msec(500));
				}
				LOGMASKED(LOG_CD, "CD: Command 0x00: SEEK\n");
				break;
			case 0x01:  // unknown
				if(m_towns_cd.command & 0x20)
				{
					m_towns_cd.extra_status = 0;
					towns_cd_set_status(0x00,0xff,0xff,0xff);
				}
				LOGMASKED(LOG_CD, "CD: Command 0x01: unknown\n");
				break;
			case 0x02:  // Read (MODE1)
				LOGMASKED(LOG_CD, "CD: Command 0x02: READ MODE1\n");
				towns_cdrom_read(device);
				break;
			case 0x04:  // Play Audio Track
				LOGMASKED(LOG_CD, "CD: Command 0x04: PLAY CD-DA\n");
				m_towns_cdda_timer->adjust(attotime::from_msec(1),0,attotime::never);
				break;
			case 0x05:  // Read TOC
				LOGMASKED(LOG_CD, "CD: Command 0x05: READ TOC\n");
				if(m_towns_cd.command & 0x20)
				{
					m_towns_cd.extra_status = 1;
					towns_cd_set_status(0x00,0x00,0x00,0x00);
				}
				else
				{
					m_towns_cd.extra_status = 2;
					towns_cd_set_status(0x16,0x00,0xa0,0x00);
				}
				break;
			case 0x06:  // Read CD-DA state?
				LOGMASKED(LOG_CD, "CD: Command 0x06: READ CD-DA STATE\n");
				m_towns_cd.extra_status = 1;
				towns_cd_set_status(0x00,0x00,0x00,0x00);
				break;
			case 0x1f:  // unknown
				LOGMASKED(LOG_CD, "CD: Command 0x1f: unknown\n");
				m_towns_cd.extra_status = 0;
				towns_cd_set_status(0x00,0x00,0x00,0x00);
				break;
			case 0x80:  // set state
				LOGMASKED(LOG_CD, "CD: Command 0x80: set state\n");
				if(m_towns_cd.command & 0x20)
				{
					m_towns_cd.extra_status = 0;
					if(m_cdda->audio_active() && !m_cdda->audio_paused())
						towns_cd_set_status(0x00,0x03,0x00,0x00);
					else
						towns_cd_set_status(0x00,0x01,0x00,0x00);

				}
				break;
			case 0x81:  // set state (CDDASET)
				if(m_towns_cd.command & 0x20)
				{
					m_towns_cd.extra_status = 0;
					towns_cd_set_status(0x00,0x00,0x00,0x00);
				}
				LOGMASKED(LOG_CD, "CD: Command 0x81: set state (CDDASET)\n");
				break;
			case 0x84:   // Stop CD audio track  -- generates no status output?
				if(m_towns_cd.command & 0x20)
				{
					m_towns_cd.extra_status = 1;
					towns_cd_set_status(0x00,0x00,0x00,0x00);
				}
				m_cdda->pause_audio(1);
				LOGMASKED(LOG_CD, "CD: Command 0x84: STOP CD-DA\n");
				break;
			case 0x85:   // Stop CD audio track (difference from 0x84?)
				if(m_towns_cd.command & 0x20)
				{
					m_towns_cd.extra_status = 1;
					towns_cd_set_status(0x00,0x00,0x00,0x00);
				}
				m_cdda->pause_audio(1);
				LOGMASKED(LOG_CD, "CD: Command 0x85: STOP CD-DA\n");
				break;
			case 0x87:  // Resume CD-DA playback
				if(m_towns_cd.command & 0x20)
				{
					m_towns_cd.extra_status = 1;
					towns_cd_set_status(0x00,0x03,0x00,0x00);
				}
				m_cdda->pause_audio(0);
				LOGMASKED(LOG_CD, "CD: Command 0x87: RESUME CD-DA\n");
				break;
			default:
				m_towns_cd.extra_status = 0;
				towns_cd_set_status(0x10,0x00,0x00,0x00);
				LOGMASKED(LOG_CD_UNKNOWN, "CD: Unknown or unimplemented command %02x\n",m_towns_cd.command);
				break;
		}
	}
}

uint16_t towns_state::towns_cdrom_dma_r()
{
	if(m_towns_cd.buffer_ptr >= 2048)
		return 0x00;
	return m_towns_cd.buffer[m_towns_cd.buffer_ptr++];
}

uint8_t towns_state::towns_cdrom_r(offs_t offset)
{
	uint32_t addr = 0;
	uint8_t ret = 0;

	ret = m_towns_cd.cmd_status[m_towns_cd.cmd_status_ptr];

	switch(offset)
	{
		case 0x00:  // status
			//LOGMASKED(LOG_CD, "CD: status read, returning %02x\n",towns_cd.status);
			return m_towns_cd.status;
		case 0x01:  // command status
			if(m_towns_cd.cmd_status_ptr >= 3)
			{
				m_towns_cd.status &= ~2;
				// check for more status bytes
				if(m_towns_cd.extra_status != 0)
				{
					switch(m_towns_cd.command & 0x9f)
					{
						case 0x02:  // read
							if(m_towns_cd.extra_status == 2)
								towns_cd_set_status(0x22,0x00,0x00,0x00);
							m_towns_cd.extra_status = 0;
							break;
						case 0x04:  // play cdda
							if(m_cdda->audio_ended())
								towns_cd_set_status(0x07,0x00,0x00,0x00);
							else
								towns_cd_set_status(0x00,0x00,0x03,0x00);
							m_towns_cd.status &= ~2;
							m_towns_cd.extra_status = 0;
							break;
						case 0x05:  // read toc
							switch(m_towns_cd.extra_status)
							{
								case 1:
									towns_cd_set_status(0x16,0x00,0xa0,0x00);
									m_towns_cd.extra_status++;
									break;
								case 2: // st1 = first track number (BCD)
									towns_cd_set_status(0x17,0x01,0x00,0x00);
									m_towns_cd.extra_status++;
									break;
								case 3:
									towns_cd_set_status(0x16,0x00,0xa1,0x00);
									m_towns_cd.extra_status++;
									break;
								case 4: // st1 = last track number (BCD)
									towns_cd_set_status(0x17,
										byte_to_bcd(m_cdrom->get_last_track()),
										0x00,0x00);
									m_towns_cd.extra_status++;
									break;
								case 5:
									towns_cd_set_status(0x16, 0x00, 0xa2, 0x00);
									m_towns_cd.extra_status++;
									break;
								case 6:  // st1/2/3 = address of track 0xaa? (BCD)
									addr = m_cdrom->get_track_start(0xaa);
									addr = cdrom_file::lba_to_msf(addr + 150);
									towns_cd_set_status(0x17,
										(addr & 0xff0000) >> 16,(addr & 0x00ff00) >> 8,addr & 0x0000ff);
									m_towns_cd.extra_status++;
									break;
								default:
									if(m_towns_cd.extra_status & 0x01)
									{
										towns_cd_set_status(0x16,
											((m_cdrom->get_adr_control((m_towns_cd.extra_status/2)-3) & 0x0f) << 4)
											| ((m_cdrom->get_adr_control((m_towns_cd.extra_status/2)-3) & 0xf0) >> 4),
											byte_to_bcd((m_towns_cd.extra_status/2)-2),0x00);
										m_towns_cd.extra_status++;
									}
									else
									{
										int track = (m_towns_cd.extra_status/2)-4;
										addr = m_cdrom->get_track_start(track);
										addr = cdrom_file::lba_to_msf(addr + 150);
										towns_cd_set_status(0x17,
											(addr & 0xff0000) >> 16,(addr & 0x00ff00) >> 8,addr & 0x0000ff);
										if(track >= m_cdrom->get_last_track())
										{
											m_towns_cd.extra_status = 0;
										}
										else
											m_towns_cd.extra_status++;
									}
									break;
							}
							break;
						case 0x06:  // read CD-DA state
							switch(m_towns_cd.extra_status)
							{
								case 1:  // st2 = track number
									towns_cd_set_status(0x18,
										0x00,towns_cd_get_track(),0x00);
									m_towns_cd.extra_status++;
									break;
								case 2:  // st0/1/2 = MSF from beginning of current track
									addr = m_cdda->get_audio_lba();
									addr = cdrom_file::lba_to_msf(addr - m_towns_cd.cdda_current);
									towns_cd_set_status(0x19,
										(addr & 0xff0000) >> 16,(addr & 0x00ff00) >> 8,addr & 0x0000ff);
									m_towns_cd.extra_status++;
									break;
								case 3:  // st1/2 = current MSF
									addr = m_cdda->get_audio_lba();
									addr = cdrom_file::lba_to_msf(addr);  // this data is incorrect, but will do until exact meaning is found
									towns_cd_set_status(0x19,
										0x00,(addr & 0xff0000) >> 16,(addr & 0x00ff00) >> 8);
									m_towns_cd.extra_status++;
									break;
								case 4:
									addr = m_cdda->get_audio_lba();
									addr = cdrom_file::lba_to_msf(addr);  // this data is incorrect, but will do until exact meaning is found
									towns_cd_set_status(0x20,
										addr & 0x0000ff,0x00,0x00);
									m_towns_cd.extra_status = 0;
									break;
							}
							break;
						case 0x84:
							towns_cd_set_status(0x11,0x00,0x00,0x00);
							m_towns_cd.extra_status = 0;
							break;
						case 0x85:
							towns_cd_set_status(0x12,0x00,0x00,0x00);
							m_towns_cd.extra_status = 0;
							break;
					}
				}
				else
					m_towns_cd.status &= ~0x02;
			}
			LOGMASKED(LOG_CD, "CD: reading command status port (%i), returning %02x\n",m_towns_cd.cmd_status_ptr,ret);
			m_towns_cd.cmd_status_ptr++;
			if(m_towns_cd.cmd_status_ptr > 3)
			{
				m_towns_cd.cmd_status_ptr = 0;
/*              if(m_towns_cd.extra_status != 0)
                {
                    towns_cdrom_set_irq(machine(),TOWNS_CD_IRQ_MPU,1);
                    m_towns_cd.status |= 0x02;
                }*/
			}
			return ret;
		case 0x02:  // data transfer (used in software transfers)
			if(m_towns_cd.software_tx)
			{
				return towns_cdrom_read_byte_software();
			}
			[[fallthrough]];
		default:
			return 0x00;
	}
}

void towns_state::towns_cdrom_w(offs_t offset, uint8_t data)
{
	int x;
	switch(offset)
	{
		case 0x00: // status
			if(data & 0x80)
				towns_cdrom_set_irq(TOWNS_CD_IRQ_MPU,0);
			if(data & 0x40)
				towns_cdrom_set_irq(TOWNS_CD_IRQ_DMA,0);
			if(data & 0x04)
				LOG("CD: sub MPU reset\n");
			m_towns_cd.mpu_irq_enable = data & 0x02;
			m_towns_cd.dma_irq_enable = data & 0x01;
			LOGMASKED(LOG_CD, "CD: status write %02x\n",data);
			break;
		case 0x01: // command
			m_towns_cd.command = data;
			towns_cdrom_execute_command(m_cdrom);
			LOGMASKED(LOG_CD, "CD: command %02x sent\n",data);
			LOGMASKED(LOG_CD, "CD: parameters: %02x %02x %02x %02x %02x %02x %02x %02x\n",
				m_towns_cd.parameter[7],m_towns_cd.parameter[6],m_towns_cd.parameter[5],
				m_towns_cd.parameter[4],m_towns_cd.parameter[3],m_towns_cd.parameter[2],
				m_towns_cd.parameter[1],m_towns_cd.parameter[0]);
			break;
		case 0x02: // parameter
			for(x=7;x>0;x--)
				m_towns_cd.parameter[x] = m_towns_cd.parameter[x-1];
			m_towns_cd.parameter[0] = data;
			LOGMASKED(LOG_CD, "CD: parameter %02x added\n",data);
			break;
		case 0x03:
			if(data & 0x08)  // software transfer
			{
				m_towns_cd.status &= ~0x10;  // no DMA transfer
				m_towns_cd.status |= 0x20;
				m_towns_cd.software_tx = true;
				m_towns_cd.buffer_ptr = 0;
			}
			if(data & 0x10)
			{
				m_towns_cd.status |= 0x10;  // DMA transfer begin
				m_towns_cd.status &= ~0x20;  // not a software transfer
				m_towns_cd.software_tx = false;
				if(m_towns_cd.buffer_ptr < 0)
				{
					m_towns_cd.buffer_ptr = 0;
					m_towns_cd.read_timer->adjust(attotime::from_hz(300000),1);
				}
			}
			LOGMASKED(LOG_CD, "CD: transfer mode write %02x\n",data);
			break;
		default:
			LOGMASKED(LOG_CD, "CD: write %02x to port %02x\n",data,offset*2);
			break;
	}
}


/* CMOS RTC
 * 0x70: Data port
 * 0x80: Register select
 */
uint8_t towns_state::towns_rtc_r()
{
	return (m_rtc_busy ? 0 : 0x80) | m_rtc_d;
}

void towns_state::towns_rtc_w(uint8_t data)
{
	m_rtc->d0_w(BIT(data, 0));
	m_rtc->d1_w(BIT(data, 1));
	m_rtc->d2_w(BIT(data, 2));
	m_rtc->d3_w(BIT(data, 3));
}

void towns_state::towns_rtc_select_w(uint8_t data)
{
	m_rtc->cs1_w(BIT(data, 7));
	m_rtc->cs2_w(BIT(data, 7));
	m_rtc->read_w(BIT(data, 2));
	m_rtc->write_w(BIT(data, 1));
	m_rtc->address_write_w(BIT(data, 0));
}

void towns_state::rtc_d0_w(int state)
{
	m_rtc_d = (m_rtc_d & ~1) | (state ? 1 : 0);
}

void towns_state::rtc_d1_w(int state)
{
	m_rtc_d = (m_rtc_d & ~2) | (state ? 2 : 0);
}

void towns_state::rtc_d2_w(int state)
{
	m_rtc_d = (m_rtc_d & ~4) | (state ? 4 : 0);
}

void towns_state::rtc_d3_w(int state)
{
	m_rtc_d = (m_rtc_d & ~8) | (state ? 8 : 0);
}

void towns_state::rtc_busy_w(int state)
{
	// active low output
	m_rtc_busy = !state;
}

// SCSI controller - I/O ports 0xc30 and 0xc32
void towns_state::towns_scsi_irq(int state)
{
	m_pic_slave->ir0_w(state);
	if(IRQ_LOG)
		logerror("PIC: IRQ8 (SCSI) set to %i\n",state);
}

void towns_state::towns_scsi_drq(int state)
{
	m_dma[0]->dmarq(state, 1);  // SCSI HDs use channel 1
}


// Volume ports - I/O ports 0x4e0-0x4e3
// 0x4e0 = input volume level
// 0x4e1 = input channel select
//         0 = Line in, left channel
//         1 = Line in, right channel
// 0x4e2 = output volume level
// 0x4e3 = output channel select
//         0 = CD-DA left channel
//         1 = CD-DA right channel
//         2 = MIC
//         3 = MODEM
uint8_t towns_state::towns_volume_r(offs_t offset)
{
	switch(offset)
	{
	case 2:
		return(m_towns_volume[m_towns_volume_select & 3]);
	case 3:
		return m_towns_volume_select;
	default:
		return 0;
	}
}

void towns_state::cdda_db_to_gain(float db)
{
	float gain = powf(10, db / 20.0f);
	int port = m_towns_volume_select & 3;
	if(port > 1)
		return;
	if(db > 0)
		gain = 0;
	m_cdda->set_output_gain(port, gain);
}

void towns_state::towns_volume_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
	case 2:
		m_towns_volume[m_towns_volume_select & 3] = data;
		if(!(m_towns_volume_select & 4) || (m_towns_volume_select & 0x18))
			return;
		cdda_db_to_gain((~data & 0x3f) * -0.5f);
		break;
	case 3:  // select channel
		m_towns_volume_select = data;
		if(!(data & 4))
			cdda_db_to_gain(1);
		else if(data & 8)
			cdda_db_to_gain(0);
		else if(data & 0x10)
			cdda_db_to_gain(-32.0f);
		break;
	default:
		logerror("SND: Volume port %i set to %02x\n",offset,data);
	}
}

uint8_t towns_state::unksnd_r()
{
	return 0;
}

// some unknown ports...
uint8_t towns_state::towns_41ff_r()
{
	logerror("I/O port 0x41ff read\n");
	return 0x01;
}

// YM3438 interrupt (IRQ 13)
void towns_state::towns_fm_irq(int state)
{
	if(state)
	{
		m_towns_fm_irq_flag = 1;
		m_pic_slave->ir5_w(1);
		if(IRQ_LOG) logerror("PIC: IRQ13 (FM) set high\n");
	}
	else
	{
		m_towns_fm_irq_flag = 0;
		if(m_towns_pcm_irq_flag == 0)
		{
			m_pic_slave->ir5_w(0);
			if(IRQ_LOG) logerror("PIC: IRQ13 (FM) set low\n");
		}
	}
}

// PCM interrupt (IRQ 13)
RF5C68_SAMPLE_END_CB_MEMBER(towns_state::towns_pcm_irq)
{
	if (m_towns_pcm_channel_mask & (1 << channel))
	{
		m_towns_pcm_irq_flag = 1;
		m_towns_pcm_channel_flag |= (1 << channel);
		m_pic_slave->ir5_w(1);
		if(IRQ_LOG) logerror("PIC: IRQ13 (PCM) set high (channel %i)\n",channel);
	}
}

void towns_state::towns_pit_out0_changed(int state)
{
	m_pit_out0 = state;

	if(m_towns_timer_mask & 0x01)
	{
		m_timer0 = state;
		if(IRQ_LOG) logerror("PIC: IRQ0 (PIT Timer ch0) set to %i\n",state);
	}
	else
		m_timer0 = 0;

	m_pic_master->ir0_w(m_timer0 || m_timer1);
}

void towns_state::towns_pit_out1_changed(int state)
{
	m_pit_out1 = state;

	if(m_towns_timer_mask & 0x02)
	{
		m_timer1 = state;
		if(IRQ_LOG) logerror("PIC: IRQ0 (PIT Timer ch1) set to %i\n",state);
	}
	else
		m_timer1 = 0;

	m_pic_master->ir0_w(m_timer0 || m_timer1);
}

void towns_state::pit_out2_changed(int state)
{
	m_pit_out2 = state ? 1 : 0;
	m_speaker->level_w(speaker_get_spk());
}

void towns_state::pit2_out1_changed(int state)
{
	m_i8251->write_rxc(state);
	m_i8251->write_txc(state);
}

void towns_state::towns_serial_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
		case 0:
		case 1:
			m_i8251->write(offset, data);
			break;
		case 4:
			m_serial_irq_enable = data;
			break;
		default:
			logerror("Invalid or unimplemented serial port write [offset=%02x, data=%02x]\n",offset,data);
	}
}

uint8_t towns_state::towns_serial_r(offs_t offset)
{
	switch(offset)
	{
		case 0:
		case 1:
			return m_i8251->read(offset);
		case 3:
			return m_serial_irq_source;
		default:
			logerror("Invalid or unimplemented serial port read [offset=%02x]\n",offset);
			return 0xff;
	}
}

void towns_state::towns_serial_irq(int state)
{
	m_serial_irq_source = state ? 0x01 : 0x00;
	m_pic_master->ir2_w(state);
	popmessage("Serial IRQ state: %i\n",state);
}

void towns_state::towns_rxrdy_irq(int state)
{
	if(m_serial_irq_enable & RXRDY_IRQ_ENABLE)
		towns_serial_irq(state);
}

void towns_state::towns_txrdy_irq(int state)
{
	if(m_serial_irq_enable & TXRDY_IRQ_ENABLE)
		towns_serial_irq(state);
}

void towns_state::towns_syndet_irq(int state)
{
	if(m_serial_irq_enable & SYNDET_IRQ_ENABLE)
		towns_serial_irq(state);
}


void towns_state::towns_mem(address_map &map)
{
	// memory map based on FM-Towns/Bochs (Bochs modified to emulate the FM-Towns)
	// may not be (and probably is not) correct
	map(0x00000000, 0x000bffff).ram();
	map(0x000c0000, 0x000c7fff).rw(FUNC(towns_state::towns_gfx_r), FUNC(towns_state::towns_gfx_w));
	map(0x000c8000, 0x000cafff).rw(FUNC(towns_state::towns_spriteram_low_r), FUNC(towns_state::towns_spriteram_low_w));
	map(0x000cb000, 0x000cbfff).bankr("bank_cb000_r").bankw("bank_cb000_w");
	map(0x000cc000, 0x000cff7f).ram();
	map(0x000cff80, 0x000cffff).rw(FUNC(towns_state::towns_video_cff80_mem_r), FUNC(towns_state::towns_video_cff80_mem_w));
	map(0x000d0000, 0x000d7fff).ram();
	map(0x000d8000, 0x000d9fff).rw(FUNC(towns_state::towns_cmos_low_r), FUNC(towns_state::towns_cmos_low_w)).share("nvram"); // CMOS? RAM
	map(0x000da000, 0x000effff).ram(); //READWRITE(SMH_BANK(11),SMH_BANK(11))
	map(0x000f0000, 0x000f7fff).ram(); //READWRITE(SMH_BANK(12),SMH_BANK(12))
	map(0x000f8000, 0x000fffff).bankr("bank_f8000_r").bankw("bank_f8000_w");
	map(0x80000000, 0x8007ffff).rw(FUNC(towns_state::towns_gfx_high_r), FUNC(towns_state::towns_gfx_high_w)).mirror(0x80000); // VRAM
	map(0x80100000, 0x8017ffff).rw(FUNC(towns_state::towns_gfx_packed_r), FUNC(towns_state::towns_gfx_packed_w)).mirror(0x80000); // VRAM
	map(0x81000000, 0x8101ffff).rw(FUNC(towns_state::towns_spriteram_r), FUNC(towns_state::towns_spriteram_w)); // Sprite RAM
	map(0xc0000000, 0xc0ffffff).rw(m_icmemcard, FUNC(fmt_icmem_device::static_mem_read), FUNC(fmt_icmem_device::static_mem_write));
	map(0xc1000000, 0xc1ffffff).rw(m_icmemcard, FUNC(fmt_icmem_device::mem_read), FUNC(fmt_icmem_device::mem_write));
	map(0xc2000000, 0xc207ffff).rom().region("user", 0x000000);  // OS ROM
	map(0xc2080000, 0xc20fffff).rom().region("user", 0x100000);  // DIC ROM
	map(0xc2100000, 0xc213ffff).rom().region("user", 0x180000);  // FONT ROM
	map(0xc2140000, 0xc2141fff).rw(FUNC(towns_state::towns_cmos_r), FUNC(towns_state::towns_cmos_w)); // CMOS (mirror?)
	map(0xc2180000, 0xc21fffff).rom().region("user", 0x080000);  // F20 ROM
	map(0xc2200000, 0xc2200fff).rw("pcm", FUNC(rf5c68_device::rf5c68_mem_r), FUNC(rf5c68_device::rf5c68_mem_w));  // WAVE RAM
	map(0xfffc0000, 0xffffffff).rom().region("user", 0x200000);  // SYSTEM ROM
}

void towns_state::marty_mem(address_map &map)
{
	map(0x00000000, 0x000bffff).ram();
	map(0x000c0000, 0x000c7fff).rw(FUNC(towns_state::towns_gfx_r), FUNC(towns_state::towns_gfx_w));
	map(0x000c8000, 0x000cafff).rw(FUNC(towns_state::towns_spriteram_low_r), FUNC(towns_state::towns_spriteram_low_w));
	map(0x000cb000, 0x000cbfff).bankr("bank_cb000_r").bankw("bank_cb000_w");
	map(0x000cc000, 0x000cff7f).ram();
	map(0x000cff80, 0x000cffff).rw(FUNC(towns_state::towns_video_cff80_mem_r), FUNC(towns_state::towns_video_cff80_mem_w));
	map(0x000d0000, 0x000d7fff).ram();
	map(0x000d8000, 0x000d9fff).rw(FUNC(towns_state::towns_cmos_low_r), FUNC(towns_state::towns_cmos_low_w)).share("nvram16"); // CMOS? RAM
	map(0x000da000, 0x000effff).ram(); //READWRITE(SMH_BANK(11),SMH_BANK(11))
	map(0x000f0000, 0x000f7fff).ram(); //READWRITE(SMH_BANK(12),SMH_BANK(12))
	map(0x000f8000, 0x000fffff).bankr("bank_f8000_r").bankw("bank_f8000_w");
	map(0x00600000, 0x0067ffff).rom().region("user", 0x000000);  // OS
	map(0x00680000, 0x0087ffff).rom().region("user", 0x280000);  // EX ROM
	map(0x00a00000, 0x00a7ffff).rw(FUNC(towns_state::towns_gfx_high_r), FUNC(towns_state::towns_gfx_high_w)).mirror(0x180000); // VRAM
	map(0x00b00000, 0x00b7ffff).rw(FUNC(towns_state::towns_gfx_packed_r), FUNC(towns_state::towns_gfx_packed_w)).mirror(0x80000); // VRAM
	map(0x00c00000, 0x00c1ffff).rw(FUNC(towns_state::towns_spriteram_r), FUNC(towns_state::towns_spriteram_w)); // Sprite RAM
	map(0x00d00000, 0x00dfffff).rw(m_icmemcard, FUNC(fmt_icmem_device::mem_read), FUNC(fmt_icmem_device::mem_write));
	map(0x00e80000, 0x00efffff).rom().region("user", 0x100000);  // DIC ROM
	map(0x00f00000, 0x00f7ffff).rom().region("user", 0x180000);  // FONT
	map(0x00f80000, 0x00f80fff).rw("pcm", FUNC(rf5c68_device::rf5c68_mem_r), FUNC(rf5c68_device::rf5c68_mem_w));  // WAVE RAM
	map(0x00fc0000, 0x00ffffff).rom().region("user", 0x200000);  // SYSTEM ROM
}

void towns_state::ux_mem(address_map &map)
{
	map(0x00000000, 0x000bffff).ram();
	map(0x000c0000, 0x000c7fff).rw(FUNC(towns_state::towns_gfx_r), FUNC(towns_state::towns_gfx_w));
	map(0x000c8000, 0x000cafff).rw(FUNC(towns_state::towns_spriteram_low_r), FUNC(towns_state::towns_spriteram_low_w));
	map(0x000cb000, 0x000cbfff).bankr("bank_cb000_r").bankw("bank_cb000_w");
	map(0x000cc000, 0x000cff7f).ram();
	map(0x000cff80, 0x000cffff).rw(FUNC(towns_state::towns_video_cff80_mem_r), FUNC(towns_state::towns_video_cff80_mem_w));
	map(0x000d0000, 0x000d7fff).ram();
	map(0x000d8000, 0x000d9fff).rw(FUNC(towns_state::towns_cmos_low_r), FUNC(towns_state::towns_cmos_low_w)).share("nvram16"); // CMOS? RAM
	map(0x000da000, 0x000effff).ram(); //READWRITE(SMH_BANK(11),SMH_BANK(11))
	map(0x000f0000, 0x000f7fff).ram(); //READWRITE(SMH_BANK(12),SMH_BANK(12))
	map(0x000f8000, 0x000fffff).bankr("bank_f8000_r").bankw("bank_f8000_w");
//  map(0x00680000, 0x0087ffff).rom().region("user",0x280000);  // EX ROM
	map(0x00a00000, 0x00a7ffff).rw(FUNC(towns_state::towns_gfx_high_r), FUNC(towns_state::towns_gfx_high_w)).mirror(0x180000); // VRAM
	map(0x00b00000, 0x00b7ffff).rw(FUNC(towns_state::towns_gfx_packed_r), FUNC(towns_state::towns_gfx_packed_w)).mirror(0x80000); // VRAM
	map(0x00c00000, 0x00c1ffff).rw(FUNC(towns_state::towns_spriteram_r), FUNC(towns_state::towns_spriteram_w)); // Sprite RAM
	map(0x00d00000, 0x00dfffff).rw(m_icmemcard, FUNC(fmt_icmem_device::mem_read), FUNC(fmt_icmem_device::mem_write));
	map(0x00e00000, 0x00e7ffff).rom().region("user", 0x000000);  // OS
	map(0x00e80000, 0x00efffff).rom().region("user", 0x100000);  // DIC ROM
	map(0x00f00000, 0x00f7ffff).rom().region("user", 0x180000);  // FONT
	map(0x00f80000, 0x00f80fff).rw("pcm", FUNC(rf5c68_device::rf5c68_mem_r), FUNC(rf5c68_device::rf5c68_mem_w));  // WAVE RAM
	map(0x00fc0000, 0x00ffffff).rom().region("user", 0x200000);  // SYSTEM ROM
}

void towns_state::towns_io(address_map &map)
{
	// I/O ports derived from FM Towns/Bochs, these are specific to the FM Towns
	// System ports
	map.unmap_value_high();
	map(0x0000, 0x0003).rw(m_pic_master, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask32(0x00ff00ff);
	map(0x0010, 0x0013).rw(m_pic_slave, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask32(0x00ff00ff);
	map(0x0020, 0x0033).rw(FUNC(towns_state::towns_system_r), FUNC(towns_state::towns_system_w));
	map(0x0040, 0x0047).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask32(0x00ff00ff);
	map(0x0050, 0x0057).rw("pit2", FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask32(0x00ff00ff);
	map(0x0060, 0x0060).rw(FUNC(towns_state::towns_port60_r), FUNC(towns_state::towns_port60_w));
	map(0x0068, 0x006b).rw(FUNC(towns_state::towns_intervaltimer2_r), FUNC(towns_state::towns_intervaltimer2_w));
	map(0x006c, 0x006c).rw(FUNC(towns_state::towns_sys6c_r), FUNC(towns_state::towns_sys6c_w));
	// 0x0070/0x0080 - CMOS RTC
	map(0x0070, 0x0070).rw(FUNC(towns_state::towns_rtc_r), FUNC(towns_state::towns_rtc_w));
	map(0x0080, 0x0080).w(FUNC(towns_state::towns_rtc_select_w));
	// DMA controllers (uPD71071)
	map(0x00a0, 0x00af).rw(FUNC(towns_state::towns_dma_r<0>), FUNC(towns_state::towns_dma_w<0>));
	map(0x00b0, 0x00bf).rw(FUNC(towns_state::towns_dma_r<1>), FUNC(towns_state::towns_dma_w<1>));
	// Floppy controller
	map(0x0200, 0x020f).rw(FUNC(towns_state::towns_floppy_r), FUNC(towns_state::towns_floppy_w));
	// CRTC / Video
	map(0x0400, 0x0400).r(FUNC(towns_state::towns_video_unknown_r));  // R/O (0x400)
	map(0x0404, 0x0404).rw(FUNC(towns_state::towns_video_404_r), FUNC(towns_state::towns_video_404_w));  // R/W (0x404)
	map(0x0440, 0x045f).rw(FUNC(towns_state::towns_video_440_r), FUNC(towns_state::towns_video_440_w));
	// System port
	map(0x0480, 0x0480).rw(FUNC(towns_state::towns_sys480_r), FUNC(towns_state::towns_sys480_w));  // R/W (0x480)
	// IC Memory Card
	map(0x048a, 0x048a).r(m_icmemcard, FUNC(fmt_icmem_device::status_r));
	map(0x0490, 0x0491).rw(m_icmemcard, FUNC(fmt_icmem_device::bank_r), FUNC(fmt_icmem_device::bank_w));
	// CD-ROM
	map(0x04c0, 0x04cf).rw(FUNC(towns_state::towns_cdrom_r), FUNC(towns_state::towns_cdrom_w)).umask32(0x00ff00ff);
	// Joystick / Mouse ports
	map(0x04d0, 0x04d3).r(FUNC(towns_state::towns_padport_r));
	map(0x04d6, 0x04d6).w(FUNC(towns_state::towns_pad_mask_w));
	// Sound (YM3438 [FM], RF5c68 [PCM])
	map(0x04d8, 0x04df).rw("fm", FUNC(ym3438_device::read), FUNC(ym3438_device::write)).umask32(0x00ff00ff);
	map(0x04e0, 0x04e3).rw(FUNC(towns_state::towns_volume_r), FUNC(towns_state::towns_volume_w));  // R/W  -- volume ports
	map(0x04e4, 0x04e7).r(FUNC(towns_state::unksnd_r));
	map(0x04e8, 0x04ef).rw(FUNC(towns_state::towns_sound_ctrl_r), FUNC(towns_state::towns_sound_ctrl_w));
	map(0x04f0, 0x04fb).w("pcm", FUNC(rf5c68_device::rf5c68_w));
	// CRTC / Video
	map(0x05c8, 0x05cb).rw(FUNC(towns_state::towns_video_5c8_r), FUNC(towns_state::towns_video_5c8_w));
	// System ports
	map(0x05e8, 0x05ef).rw(FUNC(towns_state::towns_sys5e8_r), FUNC(towns_state::towns_sys5e8_w)).umask32(0x00ff00ff);
	// Keyboard (8042 MCU)
	map(0x0600, 0x0607).rw(FUNC(towns_state::towns_keyboard_r), FUNC(towns_state::towns_keyboard_w)).umask32(0x00ff00ff);
	// RS-232C interface
	map(0x0a00, 0x0a0b).rw(FUNC(towns_state::towns_serial_r), FUNC(towns_state::towns_serial_w)).umask32(0x00ff00ff);
	// CMOS
	map(0x3000, 0x4fff).rw(FUNC(towns_state::towns_cmos_r), FUNC(towns_state::towns_cmos_w)).umask32(0x00ff00ff);
	// Something (MS-DOS wants this 0x41ff to be 1)
	//map(0x41fc,0x41ff).r(FUNC(towns_state::towns_41ff_r)).umask32(0xff000000);
	// CRTC / Video (again)
	map(0xfd90, 0xfda3).rw(FUNC(towns_state::towns_video_fd90_r), FUNC(towns_state::towns_video_fd90_w));
	map(0xff80, 0xffff).rw(FUNC(towns_state::towns_video_cff80_r), FUNC(towns_state::towns_video_cff80_w));
}

void towns_state::towns_1g_io(address_map &map)
{
	// For the first generation FM Towns with a SCSI card slot
	towns_io(map);
	map(0x0c30, 0x0c37).rw(m_scsi_slot, FUNC(fmt_scsi_slot_device::read), FUNC(fmt_scsi_slot_device::write)).umask32(0x00ff00ff);
}

void towns_state::towns2_io(address_map &map)
{
	// For FM Towns II models with integrated SCSI controller
	towns_io(map);
	map(0x0c30, 0x0c37).rw(m_scsi, FUNC(fmscsi_device::fmscsi_r), FUNC(fmscsi_device::fmscsi_w)).umask32(0x00ff00ff);
}

void towns_state::towns16_io(address_map &map)
{  // for the 386SX based systems
	// System ports
	map.unmap_value_high();
	map(0x0000, 0x0003).rw(m_pic_master, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x0010, 0x0013).rw(m_pic_slave, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x0020, 0x0033).rw(FUNC(towns_state::towns_system_r), FUNC(towns_state::towns_system_w));
	map(0x0040, 0x0047).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0x0050, 0x0057).rw("pit2", FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0x0060, 0x0060).rw(FUNC(towns_state::towns_port60_r), FUNC(towns_state::towns_port60_w));
	map(0x0068, 0x006b).rw(FUNC(towns_state::towns_intervaltimer2_r), FUNC(towns_state::towns_intervaltimer2_w));
	map(0x006c, 0x006c).rw(FUNC(towns_state::towns_sys6c_r), FUNC(towns_state::towns_sys6c_w));
	// 0x0070/0x0080 - CMOS RTC
	map(0x0070, 0x0070).rw(FUNC(towns_state::towns_rtc_r), FUNC(towns_state::towns_rtc_w));
	map(0x0080, 0x0080).w(FUNC(towns_state::towns_rtc_select_w));
	// DMA controllers (uPD71071)
	map(0x00a0, 0x00af).rw(FUNC(towns_state::towns_dma_r<0>), FUNC(towns_state::towns_dma_w<0>));
	map(0x00b0, 0x00bf).rw(FUNC(towns_state::towns_dma_r<1>), FUNC(towns_state::towns_dma_w<1>));
	// Floppy controller
	map(0x0200, 0x020f).rw(FUNC(towns_state::towns_floppy_r), FUNC(towns_state::towns_floppy_w));
	// CRTC / Video
	map(0x0400, 0x0400).r(FUNC(towns_state::towns_video_unknown_r));  // R/O (0x400)
	map(0x0404, 0x0407).rw(FUNC(towns_state::towns_video_404_r), FUNC(towns_state::towns_video_404_w));  // R/W (0x404)
	map(0x0440, 0x045f).rw(FUNC(towns_state::towns_video_440_r), FUNC(towns_state::towns_video_440_w));
	// System port
	map(0x0480, 0x0480).rw(FUNC(towns_state::towns_sys480_r), FUNC(towns_state::towns_sys480_w));  // R/W (0x480)
	// IC Memory Card
	map(0x048a, 0x048a).r(m_icmemcard, FUNC(fmt_icmem_device::status_r));
	map(0x0490, 0x0491).rw(m_icmemcard, FUNC(fmt_icmem_device::bank_r), FUNC(fmt_icmem_device::bank_w));
	// CD-ROM
	map(0x04c0, 0x04cf).rw(FUNC(towns_state::towns_cdrom_r), FUNC(towns_state::towns_cdrom_w)).umask16(0x00ff);
	// Joystick / Mouse ports
	map(0x04d0, 0x04d3).r(FUNC(towns_state::towns_padport_r));
	map(0x04d6, 0x04d6).w(FUNC(towns_state::towns_pad_mask_w));
	// Sound (YM3438 [FM], RF5c68 [PCM])
	map(0x04d8, 0x04df).rw("fm", FUNC(ym3438_device::read), FUNC(ym3438_device::write)).umask16(0x00ff);
	map(0x04e0, 0x04e3).rw(FUNC(towns_state::towns_volume_r), FUNC(towns_state::towns_volume_w));  // R/W  -- volume ports
	map(0x04e4, 0x04e7).r(FUNC(towns_state::unksnd_r));
	map(0x04e8, 0x04ef).rw(FUNC(towns_state::towns_sound_ctrl_r), FUNC(towns_state::towns_sound_ctrl_w));
	map(0x04f0, 0x04fb).w("pcm", FUNC(rf5c68_device::rf5c68_w));
	// CRTC / Video
	map(0x05c8, 0x05cb).rw(FUNC(towns_state::towns_video_5c8_r), FUNC(towns_state::towns_video_5c8_w));
	// System ports
	map(0x05e8, 0x05ef).rw(FUNC(towns_state::towns_sys5e8_r), FUNC(towns_state::towns_sys5e8_w)).umask16(0x00ff);
	// Keyboard (8042 MCU)
	map(0x0600, 0x0607).rw(FUNC(towns_state::towns_keyboard_r), FUNC(towns_state::towns_keyboard_w)).umask16(0x00ff);
	// RS-232C interface
	map(0x0a00, 0x0a0b).rw(FUNC(towns_state::towns_serial_r), FUNC(towns_state::towns_serial_w)).umask16(0x00ff);
	// CMOS
	map(0x3000, 0x4fff).rw(FUNC(towns_state::towns_cmos_r), FUNC(towns_state::towns_cmos_w)).umask16(0x00ff);
	// Something (MS-DOS wants this 0x41ff to be 1)
	//map(0x41fc,0x41ff).r(FUNC(towns_state::towns_41ff_r)).umask32(0xff000000);
	// CRTC / Video (again)
	map(0xfd90, 0xfda3).rw(FUNC(towns_state::towns_video_fd90_r), FUNC(towns_state::towns_video_fd90_w));
	map(0xff80, 0xffff).rw(FUNC(towns_state::towns_video_cff80_r), FUNC(towns_state::towns_video_cff80_w));
}

void towns_state::townsux_io(address_map &map)
{
	// For FM Towns II UX
	towns16_io(map);
	map(0x0c30, 0x0c37).rw(m_scsi, FUNC(fmscsi_device::fmscsi_r), FUNC(fmscsi_device::fmscsi_w)).umask16(0x00ff);
}

void towns_state::pcm_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).ram();
}

/* Input ports */
static INPUT_PORTS_START( towns )
	// Keyboard
	PORT_START( "key1" )  // scancodes 0x00-0x1f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_UNUSED)
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_TILDE) PORT_CHAR(27)
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1 ! \xE3\x81\xAC") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2 \x22 \xE3\x81\xB5") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR(34)
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3 # \xE3\x81\x82 \xE3\x81\x81") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("4 $ \xE3\x81\x86 \xE3\x81\x85") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("5 % \xE3\x81\x88 \xE3\x81\x87") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("6 & \xE3\x81\x8A \xE3\x81\x89") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("7 ´ \xE3\x82\x84 \xE3\x82\x83") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR(39)
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("8 ( \xE3\x82\x86 \xE3\x82\x85") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("9 ) \xE3\x82\x88 \xE3\x82\x87") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0 \xE3\x82\x8F \xE3\x82\x92") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("- = \xE3\x81\xBB") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("^ \xE2\x80\xBE \xE3\x81\xB8") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^')
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\xEF\xBF\xA5 | -") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(165) PORT_CHAR('|')
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Q \xE3\x81\x9F") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("W \xE3\x81\xA6") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("E \xE3\x81\x84") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("R \xE3\x81\x99") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("T \xE3\x81\x8B") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Y \xE3\x82\x93") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("U \xE3\x81\xAA") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("I \xE3\x81\xAB") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("O \xE3\x82\x89") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("P \xE3\x81\x9B") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("@ ` \xE2\x80\x9D") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("[ { \xE3\x82\x9C \xE3\x80\x8C") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("A \xE3\x81\xA1") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("S \xE3\x81\xA8") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')

	PORT_START( "key2" )  // scancodes 0x20-0x3f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("D \xE3\x81\x97") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F \xE3\x81\xAF") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("G \xE3\x81\x8D") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("H \xE3\x81\x8F") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("J \xE3\x81\xBE") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("K \xE3\x81\xAE") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("L \xE3\x82\x8A") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("; + \xE3\x82\x8C") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(": * \xE3\x81\x91") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("] } \xE3\x82\x80 \xE3\x80\x8D") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Z \xE3\x81\xA4 \xE3\x81\xA3") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("X \xE3\x81\x95") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("C \xE3\x81\x9D") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("V \xE3\x81\xB2") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("B \xE3\x81\x93") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("N \xE3\x81\xBF") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("M \xE3\x82\x82") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(", < \xE3\x81\xAD \xE3\x80\x81") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(". > \xE3\x82\x8B \xE3\x80\x82") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("/ ? \xE3\x82\x81 \xE3\x83\xBB") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\x22 _ \xE3\x82\x8D") PORT_CHAR('"') PORT_CHAR('_')
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey *") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey /") PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey +") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey -") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey =")
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 5") PORT_CODE(KEYCODE_5_PAD)

	PORT_START("key3")  // scancodes 0x40-0x5f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_UNUSED)
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey Enter") PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey .") PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\xE6\x8C\xBF\xE5\x85\xA5 (Insert) / DUP") PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_UNUSED)
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 000") PORT_CODE(KEYCODE_000_PAD)
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\xE5\x89\x8A\xE9\x99\xA4 (Delete) / EL") PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_UNUSED)
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("HOME") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_UNUSED)
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("CAP") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\xE3\x81\xB2\xE3\x82\x89\xE3\x81\x8C\xE3\x81\xAA (Hiragana) / \xE3\x83\xAD\xE3\x83\xBC\xE3\x83\x9E\xE5\xAD\x97 (Romaji)") PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\xE7\x84\xA1\xE5\xA4\x89\xE6\x8F\x9B (Non-conversion)")
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\xE5\xA4\x89\xE6\x8F\x9B (Conversion)")
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\xE3\x81\x8B\xE3\x81\xAA\xE6\xBC\xA2\xE5\xAD\x97 (Kana Kanji)") PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\xE3\x82\xAB\xE3\x82\xBF\xE3\x82\xAB\xE3\x83\x8A (Katakana)") PORT_CODE(KEYCODE_RWIN)
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF12") PORT_CODE(KEYCODE_F12) PORT_CHAR(UCHAR_MAMEKEY(F12))
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("ALT") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))

	PORT_START("key4")
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF5") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF6") PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF7") PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF8") PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF9") PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF10") PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_UNUSED)
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_UNUSED)
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF11") PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11))
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_UNUSED)
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\xE6\xBC\xA2\xE5\xAD\x97\xE8\xBE\x9E\xE6\x9B\xB8 (Kanji Dictionary)")
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\xE5\x8D\x98\xE8\xAA\x9E\xE6\x8A\xB9\xE6\xB6\x88 (Word Deletion)")
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\xE5\x8D\x98\xE8\xAA\x9E\xE7\x99\xBB\xE9\x8C\xB2 (Word Registration)")
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\xE5\x89\x8D\xE8\xA1\x8C (Previous)")
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_UNUSED)
	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\xE6\xAC\xA1\xE8\xA1\x8C (Next)")
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\xE5\x8D\x8A\xE8\xA7\x92\xEF\xBC\x8F\xE5\x85\xA8\xE8\xA7\x92 (Half-width / Full-width)")
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\xE5\x8F\x96\xE6\xB6\x88 (Cancel)")
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\xE5\xAE\x9F\xE8\xA1\x8C (Execute)")
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF13")
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF14")
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF15")
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF16")
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF17")
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF18")
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF19")
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF20")
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("BREAK")
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("COPY")
INPUT_PORTS_END

static INPUT_PORTS_START( marty )
	PORT_INCLUDE(towns)
	// Consoles don't have keyboards...
	PORT_MODIFY("key1")
	PORT_BIT(0xffffffff,IP_ACTIVE_LOW,IPT_UNUSED)
	PORT_MODIFY("key2")
	PORT_BIT(0xffffffff,IP_ACTIVE_LOW,IPT_UNUSED)
	PORT_MODIFY("key3")
	PORT_BIT(0xffffffff,IP_ACTIVE_LOW,IPT_UNUSED)
	PORT_MODIFY("key4")
	PORT_BIT(0xffffffff,IP_ACTIVE_LOW,IPT_UNUSED)
INPUT_PORTS_END

void towns_state::driver_start()
{
	m_towns_vram = std::make_unique<uint32_t[]>(0x20000);
	m_towns_gfxvram = std::make_unique<uint8_t[]>(0x80000);
	m_towns_txtvram = std::make_unique<uint8_t[]>(0x20000);
	memset(m_towns_txtvram.get(), 0, sizeof(uint8_t)*0x20000);
	//towns_sprram = std::make_unique<uint8_t[]>(0x20000);
	m_towns_serial_rom = std::make_unique<uint8_t[]>(256/8);
	init_serial_rom();
	m_towns_kb_timer = timer_alloc(FUNC(towns_state::poll_keyboard), this);
	m_towns_wait_timer = timer_alloc(FUNC(towns_state::wait_end), this);
	m_towns_freerun_counter = timer_alloc(FUNC(towns_state::freerun_inc), this);
	m_towns_intervaltimer2 = timer_alloc(FUNC(towns_state::intervaltimer2_timeout), this);
	m_towns_status_timer = timer_alloc(FUNC(towns_state::towns_cd_status_ready), this);
	m_towns_cdda_timer = timer_alloc(FUNC(towns_state::towns_delay_cdda), this);
	m_towns_seek_timer = timer_alloc(FUNC(towns_state::towns_delay_seek), this);

	m_video = towns_video_controller();
	m_towns_cd = towns_cdrom_controller();
	m_towns_cd.status = 0x01;  // CDROM controller ready
	m_towns_cd.buffer_ptr = -1;
	m_towns_cd.read_timer = timer_alloc(FUNC(towns_state::towns_cdrom_read_byte), this);

	save_item(NAME(m_ftimer));
	save_item(NAME(m_freerun_timer));
	save_item(NAME(m_intervaltimer2_period));
	save_item(NAME(m_intervaltimer2_irqmask));
	save_item(NAME(m_intervaltimer2_timeout_flag));
	save_item(NAME(m_intervaltimer2_timeout_flag2));
	save_item(NAME(m_nmi_mask));
	save_item(NAME(m_compat_mode));
	save_item(NAME(m_towns_system_port));
	save_item(NAME(m_towns_ankcg_enable));
	save_item(NAME(m_towns_mainmem_enable));
	save_item(NAME(m_towns_ram_enable));
	save_pointer(NAME(m_towns_vram), 0x20000);
	save_pointer(NAME(m_towns_gfxvram), 0x80000);
	save_pointer(NAME(m_towns_txtvram), 0x20000);
	save_item(NAME(m_towns_selected_drive));
	save_item(NAME(m_towns_fdc_irq6mask));
	save_pointer(NAME(m_towns_serial_rom), 256/8);
	save_item(NAME(m_towns_srom_position));
	save_item(NAME(m_towns_srom_clk));
	save_item(NAME(m_towns_srom_reset));
	save_item(NAME(m_towns_rtc_select));
	save_item(NAME(m_towns_rtc_data));
	save_item(NAME(m_towns_timer_mask));
	save_item(NAME(m_towns_kb_status));
	save_item(NAME(m_towns_kb_irq1_enable));
	save_item(NAME(m_towns_kb_output));  // key output
	save_item(NAME(m_towns_kb_extend));  // extended key output
	save_item(NAME(m_towns_fm_irq_flag));
	save_item(NAME(m_towns_pcm_irq_flag));
	save_item(NAME(m_towns_pcm_channel_flag));
	save_item(NAME(m_towns_pcm_channel_mask));
	save_item(NAME(m_towns_pad_mask));
	save_item(NAME(m_towns_volume));  // volume ports
	save_item(NAME(m_towns_volume_select));
	save_item(NAME(m_towns_scsi_control));
	save_item(NAME(m_towns_scsi_status));
	save_item(NAME(m_towns_spkrdata));
	save_item(NAME(m_pit_out0));
	save_item(NAME(m_pit_out1));
	save_item(NAME(m_pit_out2));
	save_item(NAME(m_serial_irq_source));

	save_item(NAME(m_kb_prev));
	save_item(NAME(m_prev_pad_mask));
	save_item(NAME(m_prev_x));
	save_item(NAME(m_prev_y));
	save_item(NAME(m_rtc_d));
	save_item(NAME(m_rtc_busy));
	save_item(NAME(m_vram_mask));
	save_item(NAME(m_vram_mask_addr));

	save_item(STRUCT_MEMBER(m_towns_cd, command));
	save_item(STRUCT_MEMBER(m_towns_cd, status));
	save_item(STRUCT_MEMBER(m_towns_cd, cmd_status));
	save_item(STRUCT_MEMBER(m_towns_cd, cmd_status_ptr));
	save_item(STRUCT_MEMBER(m_towns_cd, extra_status));
	save_item(STRUCT_MEMBER(m_towns_cd, parameter));
	save_item(STRUCT_MEMBER(m_towns_cd, mpu_irq_enable));
	save_item(STRUCT_MEMBER(m_towns_cd, dma_irq_enable));
	save_item(STRUCT_MEMBER(m_towns_cd, buffer));
	save_item(STRUCT_MEMBER(m_towns_cd, buffer_ptr));
	save_item(STRUCT_MEMBER(m_towns_cd, lba_current));
	save_item(STRUCT_MEMBER(m_towns_cd, lba_last));
	save_item(STRUCT_MEMBER(m_towns_cd, cdda_current));
	save_item(STRUCT_MEMBER(m_towns_cd, cdda_length));
	save_item(STRUCT_MEMBER(m_towns_cd, software_tx));

	save_item(STRUCT_MEMBER(m_video, towns_vram_wplane));
	save_item(STRUCT_MEMBER(m_video, towns_vram_rplane));
	save_item(STRUCT_MEMBER(m_video, towns_vram_page_sel));
	save_item(STRUCT_MEMBER(m_video, towns_palette_select));
	save_item(STRUCT_MEMBER(m_video, towns_palette_r));
	save_item(STRUCT_MEMBER(m_video, towns_palette_g));
	save_item(STRUCT_MEMBER(m_video, towns_palette_b));
	save_item(STRUCT_MEMBER(m_video, towns_degipal));
	save_item(STRUCT_MEMBER(m_video, towns_dpmd_flag));
	save_item(STRUCT_MEMBER(m_video, towns_crtc_mix));
	save_item(STRUCT_MEMBER(m_video, towns_crtc_sel));
	save_item(STRUCT_MEMBER(m_video, towns_crtc_reg));
	save_item(STRUCT_MEMBER(m_video, towns_video_sel));
	save_item(STRUCT_MEMBER(m_video, towns_video_reg));
	save_item(STRUCT_MEMBER(m_video, towns_sprite_sel));
	save_item(STRUCT_MEMBER(m_video, towns_sprite_reg));
	save_item(STRUCT_MEMBER(m_video, towns_sprite_flag));
	save_item(STRUCT_MEMBER(m_video, towns_sprite_page));
	save_item(STRUCT_MEMBER(m_video, towns_tvram_enable));
	save_item(STRUCT_MEMBER(m_video, towns_kanji_offset));
	save_item(STRUCT_MEMBER(m_video, towns_kanji_code_h));
	save_item(STRUCT_MEMBER(m_video, towns_kanji_code_l));
	save_item(STRUCT_MEMBER(m_video, towns_display_plane));
	save_item(STRUCT_MEMBER(m_video, towns_display_page_sel));
	save_item(STRUCT_MEMBER(m_video, towns_vblank_flag));
	save_item(STRUCT_MEMBER(m_video, towns_layer_ctrl));
	save_item(NAME(m_video.towns_crtc_layerscr[0].min_x));
	save_item(NAME(m_video.towns_crtc_layerscr[0].max_x));
	save_item(NAME(m_video.towns_crtc_layerscr[0].min_y));
	save_item(NAME(m_video.towns_crtc_layerscr[0].max_y));
	save_item(NAME(m_video.towns_crtc_layerscr[1].min_x));
	save_item(NAME(m_video.towns_crtc_layerscr[1].max_x));
	save_item(NAME(m_video.towns_crtc_layerscr[1].min_y));
	save_item(NAME(m_video.towns_crtc_layerscr[1].max_y));

	save_pointer(m_video.towns_crtc_reg,"CRTC registers",32);
	save_pointer(m_video.towns_video_reg,"Video registers",2);

	if (m_ram->size() > 0x100000)
		m_maincpu->space(AS_PROGRAM).install_ram(0x100000,m_ram->size()-1,m_ram->pointer() + 0x100000);
}

void marty_state::driver_start()
{
	towns_state::driver_start();
	if(m_towns_machine_id == 0x0101) // default if no serial ROM present
		m_towns_machine_id = 0x034a;
}

void towns_state::machine_start()
{
	if (m_flop[0]->get_device())
		m_flop[0]->get_device()->set_rpm(360);
	if (m_flop[1]->get_device())
		m_flop[1]->get_device()->set_rpm(360);

	// uninitialized PCM RAM filled with 0xff (fmtmarty chasehq relies on that)
	address_space &space = subdevice<rf5c68_device>("pcm")->space(0);
	for (int i = 0; i < 0x10000; i++)
		space.write_byte(i, 0xff);

	m_timer0 = 0;
	m_timer1 = 0;
	m_serial_irq_enable = 0;
}

void towns_state::machine_reset()
{
	m_ftimer = 0x00;
	m_freerun_timer = 0x00;
	m_nmi_mask = 0x00;
	m_compat_mode = 0x00;
	m_towns_ankcg_enable = 0x00;
	m_towns_mainmem_enable = 0x00;
	m_towns_system_port = 0x00;
	m_towns_ram_enable = 0x00;
	towns_update_video_banks();
	m_towns_kb_status = 0x18;
	m_towns_kb_irq1_enable = 0;
	m_towns_pad_mask = 0x7f;
	m_towns_volume_select = 0;
	m_intervaltimer2_period = 0;
	m_intervaltimer2_timeout_flag = 0;
	m_intervaltimer2_timeout_flag2 = 0;
	m_intervaltimer2_irqmask = 1;  // masked
	m_towns_kb_timer->adjust(attotime::zero,0,attotime::from_msec(10));
	m_towns_freerun_counter->adjust(attotime::zero,0,attotime::from_usec(1));
	m_serial_irq_source = 0;
	m_rtc_d = 0;
	m_rtc_busy = false;
	m_vram_mask_addr = 0;
	m_towns_pcm_channel_flag = 0;
	m_towns_pcm_channel_mask = 0xff;
	m_towns_pcm_irq_flag = 0;
	m_towns_fm_irq_flag = 0;
}

uint8_t towns_state::get_slave_ack(offs_t offset)
{
	if (offset==7) { // IRQ = 7
		return m_pic_slave->acknowledge();
	}
	return 0x00;
}

void towns_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_FMTOWNS_FORMAT);
}

static void towns_floppies(device_slot_interface &device)
{
	device.option_add("35hd", FLOPPY_35_HD);
}

static const gfx_layout fnt_chars_16x16 =
{
	16,16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP16(0,1) },
	{ STEP16(0,16) },
	16*16
};

static const gfx_layout text_chars =
{
	8,16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP8(0,1), },
	{ STEP16(0,1) },
	8*16
};

static GFXDECODE_START( gfx_towns )
	GFXDECODE_ENTRY( "user",   0x180000 + 0x3d800, text_chars,  0, 16 )
	GFXDECODE_ENTRY( "user",   0x180000, fnt_chars_16x16,  0, 16 )
GFXDECODE_END

void towns_state::towns_base(machine_config &config)
{
	/* basic machine hardware */
	I386(config, m_maincpu, 16000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &towns_state::towns_mem);
	m_maincpu->set_addrmap(AS_IO, &towns_state::towns_1g_io);
	m_maincpu->set_vblank_int("screen", FUNC(towns_state::towns_vsync_irq));
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	/* pad ports */
	MSX_GENERAL_PURPOSE_PORT(config, m_pad_ports[0], msx_general_purpose_port_devices, "townspad");
	MSX_GENERAL_PURPOSE_PORT(config, m_pad_ports[1], msx_general_purpose_port_devices, "mouse");

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_size(768,512);
	m_screen->set_visarea(0, 768-1, 0, 512-1);
	m_screen->set_screen_update(FUNC(towns_state::screen_update));

	GFXDECODE(config, "gfxdecode", m_palette16[0], gfx_towns);
	PALETTE(config, m_palette).set_entries(256);
	PALETTE(config, m_palette16[0]).set_entries(16);
	PALETTE(config, m_palette16[1]).set_entries(16);

	/* sound hardware */
	SPEAKER(config, "speaker", 2).front();

	ym3438_device &fm(YM3438(config, "fm", 16000000 / 2)); // actual clock speed unknown
	fm.irq_handler().set(FUNC(towns_state::towns_fm_irq));
	fm.add_route(0, "speaker", 1.00, 0);
	fm.add_route(1, "speaker", 1.00, 1);

/*
    // Later model uses YMF276 for FM
    ymf276_device &fm(YMF276(config, "fm", 16000000 / 2)); // actual clock speed unknown
    fm.irq_handler().set(FUNC(towns_state::towns_fm_irq));
    fm.add_route(0, "speaker", 1.00);
    fm.add_route(1, "speaker", 1.00);
*/

	rf5c68_device &pcm(RF5C68(config, "pcm", 16000000 / 2));  // actual clock speed unknown
	pcm.set_end_callback(FUNC(towns_state::towns_pcm_irq));
	pcm.set_addrmap(0, &towns_state::pcm_mem);
	pcm.add_route(0, "speaker", 1.00, 0);
	pcm.add_route(1, "speaker", 1.00, 1);

	CDDA(config, m_cdda);
	m_cdda->add_route(0, "speaker", 0.30, 0);
	m_cdda->add_route(1, "speaker", 0.30, 1);
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "speaker", 0.50, 0);
	m_speaker->add_route(ALL_OUTPUTS, "speaker", 0.50, 1);

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(307200);
	m_pit->out_handler<0>().set(FUNC(towns_state::towns_pit_out0_changed));
	m_pit->set_clk<1>(307200);
	m_pit->out_handler<1>().set(FUNC(towns_state::towns_pit_out1_changed));
	m_pit->set_clk<2>(307200);
	m_pit->out_handler<2>().set(FUNC(towns_state::pit_out2_changed));

	pit8253_device &pit2(PIT8253(config, "pit2", 0));
	pit2.set_clk<0>(307200); // reserved
	pit2.set_clk<1>(1228800); // RS-232
	pit2.out_handler<1>().set(FUNC(towns_state::pit2_out1_changed));
	pit2.set_clk<2>(307200); // reserved

	PIC8259(config, m_pic_master, 0);
	m_pic_master->out_int_callback().set_inputline(m_maincpu, 0);
	m_pic_master->in_sp_callback().set_constant(1);
	m_pic_master->read_slave_ack_callback().set(FUNC(towns_state::get_slave_ack));

	PIC8259(config, m_pic_slave, 0);
	m_pic_slave->out_int_callback().set(m_pic_master, FUNC(pic8259_device::ir7_w));
	m_pic_slave->in_sp_callback().set_constant(0);

	MB8877(config, m_fdc, 8'000'000 / 4);  // clock unknown
	m_fdc->intrq_wr_callback().set(FUNC(towns_state::mb8877a_irq_w));
	m_fdc->drq_wr_callback().set(FUNC(towns_state::mb8877a_drq_w));
	FLOPPY_CONNECTOR(config, m_flop[0], towns_floppies, "35hd", towns_state::floppy_formats);
	FLOPPY_CONNECTOR(config, m_flop[1], towns_floppies, "35hd", towns_state::floppy_formats);
	SOFTWARE_LIST(config, "fd_list_orig").set_original("fmtowns_flop_orig");
	SOFTWARE_LIST(config, "fd_list_cracked").set_original("fmtowns_flop_cracked");
	SOFTWARE_LIST(config, "fd_list_misc").set_original("fmtowns_flop_misc");

	CDROM(config, m_cdrom).set_interface("cdrom");
	m_cdda->set_cdrom_tag(m_cdrom);
	SOFTWARE_LIST(config, "cd_list").set_original("fmtowns_cd");

	UPD71071(config, m_dma[0], 0);
	m_dma[0]->set_cpu_tag("maincpu");
	m_dma[0]->set_clock(4000000);
	m_dma[0]->dma_read_callback<0>().set(FUNC(towns_state::towns_fdc_dma_r));
	m_dma[0]->dma_read_callback<3>().set(FUNC(towns_state::towns_state::towns_cdrom_dma_r));
	m_dma[0]->dma_write_callback<0>().set(FUNC(towns_state::towns_fdc_dma_w));
	UPD71071(config, m_dma[1], 0);
	m_dma[1]->set_cpu_tag("maincpu");
	m_dma[1]->set_clock(4000000);
	m_dma[1]->dma_read_callback<0>().set(FUNC(towns_state::towns_fdc_dma_r));
	m_dma[1]->dma_read_callback<3>().set(FUNC(towns_state::towns_state::towns_cdrom_dma_r));
	m_dma[1]->dma_write_callback<0>().set(FUNC(towns_state::towns_fdc_dma_w));

	I8251(config, m_i8251, 0);
	m_i8251->rxrdy_handler().set(FUNC(towns_state::towns_rxrdy_irq));
	m_i8251->txrdy_handler().set(FUNC(towns_state::towns_txrdy_irq));
	m_i8251->syndet_handler().set(FUNC(towns_state::towns_syndet_irq));
	m_i8251->dtr_handler().set("rs232c", FUNC(rs232_port_device::write_dtr));
	m_i8251->rts_handler().set("rs232c", FUNC(rs232_port_device::write_rts));
	m_i8251->txd_handler().set("rs232c", FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232c(RS232_PORT(config, "rs232c", default_rs232_devices, nullptr));
	rs232c.rxd_handler().set(m_i8251, FUNC(i8251_device::write_rxd));
	rs232c.dsr_handler().set(m_i8251, FUNC(i8251_device::write_dsr));
	rs232c.cts_handler().set(m_i8251, FUNC(i8251_device::write_cts));

	FMT_ICMEM(config, m_icmemcard, 0);

	/* First-generation models: 1 MB onboard, 3 SIMM slots with 1 or 2 MB each, except slot 1 (limited to 1 MB).
	   Model 2 comes with a 1 MB SIMM preinstalled on slot 1, Model 1 doesn't. */
	RAM(config, m_ram).set_default_size("2M").set_extra_options("1M,3M,4M,5M,6M");

	MSM58321(config, m_rtc, 32768_Hz_XTAL);
	m_rtc->d0_handler().set(FUNC(towns_state::rtc_d0_w));
	m_rtc->d1_handler().set(FUNC(towns_state::rtc_d1_w));
	m_rtc->d2_handler().set(FUNC(towns_state::rtc_d2_w));
	m_rtc->d3_handler().set(FUNC(towns_state::rtc_d3_w));
	m_rtc->busy_handler().set(FUNC(towns_state::rtc_busy_w));
	m_rtc->set_year0(2000);
	m_rtc->set_default_24h(true);
}

void towns_state::towns(machine_config &config)
{
	towns_base(config);
	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	FMT_SCSI_SLOT(config, m_scsi_slot, fmt_scsi_default_devices, nullptr);
	m_scsi_slot->irq_handler().set(FUNC(towns_state::towns_scsi_irq));
	m_scsi_slot->drq_handler().set(FUNC(towns_state::towns_scsi_drq));

	m_dma[0]->dma_read_callback<1>().set(m_scsi_slot, FUNC(fmt_scsi_slot_device::data_read));
	m_dma[0]->dma_write_callback<1>().set(m_scsi_slot, FUNC(fmt_scsi_slot_device::data_write));
	m_dma[1]->dma_read_callback<1>().set(m_scsi_slot, FUNC(fmt_scsi_slot_device::data_read));
	m_dma[1]->dma_write_callback<1>().set(m_scsi_slot, FUNC(fmt_scsi_slot_device::data_write));
}

void towns16_state::townsux(machine_config &config)
{
	towns_base(config);

	I386SX(config.replace(), m_maincpu, 16000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &towns16_state::ux_mem);
	m_maincpu->set_addrmap(AS_IO, &towns16_state::townsux_io);
	m_maincpu->set_vblank_int("screen", FUNC(towns_state::towns_vsync_irq));
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	scsi_port_device &scsi(SCSI_PORT(config, "scsi", 0));
	scsi.set_slot_device(1, "harddisk", SCSIHD, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_0));
	scsi.set_slot_device(2, "harddisk", SCSIHD, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_1));
	scsi.set_slot_device(3, "harddisk", SCSIHD, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_2));
	scsi.set_slot_device(4, "harddisk", SCSIHD, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_3));
	scsi.set_slot_device(5, "harddisk", SCSIHD, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_4));

	FMSCSI(config, m_scsi, 0);
	m_scsi->set_scsi_port("scsi");
	m_scsi->irq_handler().set(FUNC(towns16_state::towns_scsi_irq));
	m_scsi->drq_handler().set(FUNC(towns16_state::towns_scsi_drq));

	m_dma[0]->dma_read_callback<1>().set(m_scsi, FUNC(fmscsi_device::fmscsi_data_r));
	m_dma[0]->dma_write_callback<1>().set(m_scsi, FUNC(fmscsi_device::fmscsi_data_w));
	m_dma[1]->dma_read_callback<1>().set(m_scsi, FUNC(fmscsi_device::fmscsi_data_r));
	m_dma[1]->dma_write_callback<1>().set(m_scsi, FUNC(fmscsi_device::fmscsi_data_w));

	// 2 MB onboard, one SIMM slot with 2-8 MB
	m_ram->set_default_size("2M").set_extra_options("4M,6M,10M");

	NVRAM(config, "nvram16", nvram_device::DEFAULT_ALL_0);
}

void towns_state::townssj(machine_config &config)
{
	towns_base(config);

	I486(config.replace(), m_maincpu, 66000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &towns_state::towns_mem);
	m_maincpu->set_addrmap(AS_IO, &towns_state::towns2_io);
	m_maincpu->set_vblank_int("screen", FUNC(towns_state::towns_vsync_irq));
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	scsi_port_device &scsi(SCSI_PORT(config, "scsi", 0));
	scsi.set_slot_device(1, "harddisk", SCSIHD, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_0));
	scsi.set_slot_device(2, "harddisk", SCSIHD, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_1));
	scsi.set_slot_device(3, "harddisk", SCSIHD, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_2));
	scsi.set_slot_device(4, "harddisk", SCSIHD, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_3));
	scsi.set_slot_device(5, "harddisk", SCSIHD, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_4));

	FMSCSI(config, m_scsi, 0);
	m_scsi->set_scsi_port("scsi");
	m_scsi->irq_handler().set(FUNC(towns_state::towns_scsi_irq));
	m_scsi->drq_handler().set(FUNC(towns_state::towns_scsi_drq));

	m_dma[0]->dma_read_callback<1>().set(m_scsi, FUNC(fmscsi_device::fmscsi_data_r));
	m_dma[0]->dma_write_callback<1>().set(m_scsi, FUNC(fmscsi_device::fmscsi_data_w));
	m_dma[1]->dma_read_callback<1>().set(m_scsi, FUNC(fmscsi_device::fmscsi_data_r));
	m_dma[1]->dma_write_callback<1>().set(m_scsi, FUNC(fmscsi_device::fmscsi_data_w));

	// 4 MB (SJ2/SJ2A) or 8 MB (SJ26/SJ53) onboard, 2 SIMM slots with 4-32 MB each
	m_ram->set_default_size("8M").set_extra_options("4M,12M,16M,20M,24M,28M,32M,36M,40M,44M,48M,52M,56M,68M,72M");

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);
}

void towns_state::townshr(machine_config &config)
{
	townssj(config);
	I486(config.replace(), m_maincpu, 20000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &towns_state::towns_mem);
	m_maincpu->set_addrmap(AS_IO, &towns_state::towns2_io);
	m_maincpu->set_vblank_int("screen", FUNC(towns_state::towns_vsync_irq));
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	// 4 MB onboard, 3 SIMM slots with 2-8 MB each
	m_ram->set_default_size("4M").set_extra_options("6M,8M,10M,12M,14M,16M,18M,20M,22M,24M,28M");
}

void towns_state::townsmx(machine_config &config)
{
	townssj(config);

	// 4 MB onboard, 3 SIMM slots with 2-32 MB each, MX170W/MX340W models come with a 4 MB SIMM preinstalled
	m_ram->set_default_size("8M").set_extra_options("4M,6M,8M,10M,12M,14M,16M,18M,20M,22M,24M,26M,28M,30M,32M,36M,38M,40M,42M,44M,46M,48M,52M,53M,54M,56M,60M,68M,70M,72M,76M,84M,100M");
}

void towns_state::townsftv(machine_config &config)
{
	townssj(config);
	I486(config.replace(), m_maincpu, 33000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &towns_state::towns_mem);
	m_maincpu->set_addrmap(AS_IO, &towns_state::towns2_io);
	m_maincpu->set_vblank_int("screen", FUNC(towns_state::towns_vsync_irq));
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	// 4 MB onboard, 2 SIMM slots with 2-32 MB each, one of them with a 2 MB SIMM preinstalled
	m_ram->set_default_size("6M").set_extra_options("4M,8M,10M,12M,14M,16M,20M,22M,24M,28M,36M,38M,40M,44M,52M,68M");
}

void marty_state::marty(machine_config &config)
{
	towns_base(config);

	I386SX(config.replace(), m_maincpu, 16000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &marty_state::marty_mem);
	m_maincpu->set_addrmap(AS_IO, &marty_state::towns16_io);
	m_maincpu->set_vblank_int("screen", FUNC(towns_state::towns_vsync_irq));
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	m_pad_ports[0]->set_default_option("martypad");
	m_pad_ports[1]->set_default_option(nullptr);

	FLOPPY_CONNECTOR(config.replace(), m_flop[1], towns_floppies, nullptr, towns_state::floppy_formats);

	// 2 MB onboard, expandable to 4 MB with a Marty-only expansion card
	m_ram->set_default_size("2M").set_extra_options("4M");

	NVRAM(config, "nvram16", nvram_device::DEFAULT_ALL_0);
}

/* ROM definitions */

/* These ROMs were dumped from an FM Towns Model 2. Model 1 is assumed to use the same ROMs, since they were
   released at the same time, and the only differences are the amount of RAM and floppy drives.

   The ROM is physically contained in three 4 Mbit chips: two MB834200-20 (DIP40) and one MB834200-25 (QFP44) */
ROM_START( fmtowns )
	ROM_REGION32_LE( 0x280000, "user", 0)
	ROM_LOAD("fmt_dos.rom",  0x000000, 0x080000, CRC(112872ee) SHA1(57fd146478226f7f215caf63154c763a6d52165e) )
	ROM_LOAD("fmt_dic.rom",  0x100000, 0x080000, CRC(b314c659) SHA1(3959c4c6be540252cabea06847bcd408f1911cfb) )
	ROM_LOAD("fmt_fnt.rom",  0x180000, 0x040000, CRC(955c6b75) SHA1(fa5f7a18060afa35678dcbdc3589a1455aba26dc) )
	ROM_LOAD("fmt_sys.rom",  0x200000, 0x040000, CRC(53319e23) SHA1(15d9cc705f3534fe97a2386e4d4848a1602cc534) )
ROM_END

/* System ROM has a date of 91/07/09 and matches the UX set, but the dictionary ROM is completely different. It could be from an FM Towns II CX.
   Font ROM appears to be corrupt, though. */
ROM_START( fmtownsv03 )
	ROM_REGION32_LE( 0x280000, "user", 0)
	ROM_LOAD("fmt_dos_a.rom",  0x000000, 0x080000, CRC(22270e9f) SHA1(a7e97b25ff72b14121146137db8b45d6c66af2ae) )
	ROM_LOAD("fmt_f20_a.rom",  0x080000, 0x080000, CRC(75660aac) SHA1(6a521e1d2a632c26e53b83d2cc4b0edecfc1e68c) )
	ROM_LOAD("fmt_dic_a.rom",  0x100000, 0x080000, CRC(74b1d152) SHA1(f63602a1bd67c2ad63122bfb4ffdaf483510f6a8) )
	ROM_LOAD("fmt_fnt_a.rom",  0x180000, 0x040000, CRC(0108a090) SHA1(1b5dd9d342a96b8e64070a22c3a158ca419894e1) BAD_DUMP )
	ROM_LOAD("fmt_sys_a.rom",  0x200000, 0x040000, CRC(92f3fa67) SHA1(be21404098b23465d24c4201a81c96ac01aff7ab) )
ROM_END

/* 16MHz 80386SX, 2MB RAM expandable up to 10MB (due to the limited 24-bit address space of the CPU), dumped from a UX10 */
ROM_START( fmtownsux )
	ROM_REGION16_LE( 0x480000, "user", 0)
	ROM_LOAD("fmt_dos_a.rom",  0x000000, 0x080000, CRC(22270e9f) SHA1(a7e97b25ff72b14121146137db8b45d6c66af2ae) )
	// no F20 ROM
	ROM_LOAD("fmt_dic.rom",  0x100000, 0x080000, CRC(82d1daa2) SHA1(7564020dba71deee27184824b84dbbbb7c72aa4e) )
	ROM_LOAD("fmt_fnt.rom",  0x180000, 0x040000, CRC(dd6fd544) SHA1(a216482ea3162f348fcf77fea78e0b2e4288091a) )
	ROM_LOAD("fmt_sys_a.rom",  0x200000, 0x040000, CRC(92f3fa67) SHA1(be21404098b23465d24c4201a81c96ac01aff7ab) )
	ROM_REGION( 0x20, "serial", 0)
	ROM_LOAD("mytownsux.rom",  0x00, 0x20, CRC(5cc7e6bc) SHA1(e245f8086df57ce6e48853f0e13525f738e5c4d8) )
ROM_END

/* 20MHz 80486SX, 4MB RAM expandable up to 28MB, dumped from an HR20 */
ROM_START( fmtownshr )
	ROM_REGION32_LE( 0x280000, "user", 0)
	ROM_LOAD("fmt_dos.rom",  0x000000, 0x080000, CRC(112872ee) SHA1(57fd146478226f7f215caf63154c763a6d52165e) )
	// F20 ROM space appears to be all 0xFF on an HR, so it is assumed to be not present
//  ROM_LOAD("fmt_f20.rom",  0x080000, 0x080000, CRC(9f55a20c) SHA1(1920711cb66340bb741a760de187de2f76040b8c) )
	ROM_LOAD("fmt_dic.rom",  0x100000, 0x080000, CRC(82d1daa2) SHA1(7564020dba71deee27184824b84dbbbb7c72aa4e) )
	ROM_LOAD("fmt_fnt.rom",  0x180000, 0x040000, CRC(dd6fd544) SHA1(a216482ea3162f348fcf77fea78e0b2e4288091a) )
	ROM_LOAD("fmthr_sys.rom",0x200000, 0x040000, CRC(8aeff982) SHA1(a4ebf2e247a8e15a5f1ff003b657bbe3a67203d8) )
	ROM_REGION( 0x20, "serial", 0)
	ROM_LOAD("mytownshr.rom",  0x00, 0x20, CRC(c52f0e89) SHA1(634d3965606b18a99507f0a520553005661c41ff) )
ROM_END

/* 66MHz 80486DX2, 8MB RAM expandable up to 72MB, dumped from an SJ26 */
ROM_START( fmtownssj )
	ROM_REGION32_LE( 0x280000, "user", 0)
	// Assumed for now, only the serial ROM has been dumped successfully so far
	ROM_LOAD("fmt_dos.rom",  0x000000, 0x080000, CRC(112872ee) SHA1(57fd146478226f7f215caf63154c763a6d52165e) BAD_DUMP )
	ROM_LOAD("fmt_f20.rom",  0x080000, 0x080000, CRC(9f55a20c) SHA1(1920711cb66340bb741a760de187de2f76040b8c) BAD_DUMP )
	ROM_LOAD("fmt_dic.rom",  0x100000, 0x080000, CRC(82d1daa2) SHA1(7564020dba71deee27184824b84dbbbb7c72aa4e) BAD_DUMP )
	ROM_LOAD("fmt_fnt.rom",  0x180000, 0x040000, CRC(dd6fd544) SHA1(a216482ea3162f348fcf77fea78e0b2e4288091a) BAD_DUMP )
	ROM_LOAD("fmt_sys.rom",  0x200000, 0x040000, CRC(afe4ebcf) SHA1(4cd51de4fca9bd7a3d91d09ad636fa6b47a41df5) BAD_DUMP )
	ROM_REGION( 0x20, "serial", 0)
	ROM_LOAD("mytownssj.rom",  0x00, 0x20, CRC(d0ed8936) SHA1(bf9eeef25e9a1dc4a9ce1c70f4155ac973cae3f9) )
ROM_END

/* 66MHz 80486DX2, dumped from an FM-Towns II MX */
ROM_START( fmtownsmx )
	ROM_REGION32_LE( 0x280000, "user", 0)
	ROM_LOAD("fmtownsiimxbios.m79",  0x000000, 0x080000, CRC(f3fc636e) SHA1(a35a11ab56b1f11e3f69d70b61648ab83699a2df) )
	ROM_CONTINUE(0x180000,0x40000)
	ROM_CONTINUE(0x200000,0x40000)
	ROM_CONTINUE(0x080000,0x80000)
	ROM_CONTINUE(0x100000,0x80000)
	ROM_REGION( 0x20, "serial", 0)
	ROM_LOAD("mytownsmx.rom",  0x00, 0x20, CRC(16e78766) SHA1(38e8810bee9ee6b54c3999d27f499b89e4a4c33f) )
ROM_END

/* 33MHz 80486SX, 6MB RAM expandable up to 68MB, dumped from an FM Towns II Fresh TV */
ROM_START( fmtownsftv )
	ROM_REGION32_LE( 0x280000, "user", 0)
	ROM_LOAD("fmt_dos.rom",  0x000000, 0x080000, CRC(112872ee) SHA1(57fd146478226f7f215caf63154c763a6d52165e) )
	ROM_LOAD("fmt_f20.rom",  0x080000, 0x080000, CRC(9f55a20c) SHA1(1920711cb66340bb741a760de187de2f76040b8c) )
	ROM_LOAD("fmt_dic.rom",  0x100000, 0x080000, CRC(82d1daa2) SHA1(7564020dba71deee27184824b84dbbbb7c72aa4e) )
	ROM_LOAD("fmt_fnt.rom",  0x180000, 0x040000, CRC(dd6fd544) SHA1(a216482ea3162f348fcf77fea78e0b2e4288091a) )
	ROM_LOAD("fmt_sys.rom",  0x200000, 0x040000, CRC(afe4ebcf) SHA1(4cd51de4fca9bd7a3d91d09ad636fa6b47a41df5) )
	ROM_REGION( 0x20, "serial", 0)
	ROM_LOAD("mytownsftv.rom",  0x00, 0x20, CRC(a961aae7) SHA1(bba02edafdfaa6fb1b8122f623259a87a555c307) )
ROM_END

ROM_START( fmtmarty )
	ROM_REGION16_LE( 0x480000, "user", 0)
	ROM_LOAD("mrom.m36",  0x000000, 0x080000, CRC(9c0c060c) SHA1(5721c5f9657c570638352fa9acac57fa8d0b94bd) )
	ROM_CONTINUE(0x280000,0x180000)
	ROM_LOAD("mrom.m37",  0x400000, 0x080000, CRC(fb66bb56) SHA1(e273b5fa618373bdf7536495cd53c8aac1cce9a5) )
	ROM_CONTINUE(0x80000,0x100000)
	ROM_CONTINUE(0x180000,0x40000)
	ROM_CONTINUE(0x200000,0x40000)
ROM_END

ROM_START( fmtmarty2 )
	ROM_REGION16_LE( 0x480000, "user", 0)
	ROM_LOAD("fmt_dos.rom",  0x000000, 0x080000, CRC(2bc2af96) SHA1(99cd51c5677288ad8ef711b4ac25d981fd586884) )
	ROM_LOAD("fmt_dic.rom",  0x100000, 0x080000, CRC(82d1daa2) SHA1(7564020dba71deee27184824b84dbbbb7c72aa4e) )
	ROM_LOAD("fmt_fnt.rom",  0x180000, 0x040000, CRC(dd6fd544) SHA1(a216482ea3162f348fcf77fea78e0b2e4288091a) )
	ROM_LOAD("fmt_sys.rom",  0x200000, 0x040000, CRC(937311f6) SHA1(7b1c97fa778986134104a6964c8fe13e3654f52e) )
	ROM_LOAD("mar_ex0.rom",  0x280000, 0x080000, CRC(f67540f6) SHA1(1611fc4683dab72e56a5d0ee0f757e5878900b31) )
	ROM_LOAD("mar_ex1.rom",  0x300000, 0x080000, CRC(99938a4b) SHA1(167d8ae47312cdaa30b8597144f60f54ce9f74d3) )
	ROM_LOAD("mar_ex2.rom",  0x380000, 0x080000, CRC(c6783422) SHA1(b2ed2ba42b8132d139480484fe116ba7774e1604) )
	ROM_LOAD("mar_ex3.rom",  0x400000, 0x080000, CRC(4aa43e16) SHA1(19b669aa6488bdaf8569e89b1b1067e51246a768) )
	ROM_REGION( 0x20, "serial", 0)
	ROM_LOAD("mytownsm2.rom",  0x00, 0x20, CRC(44f2f076) SHA1(e4d3be54e66931c947993ded9ddbae716ad51ca5) )
ROM_END

ROM_START( carmarty )
	ROM_REGION16_LE( 0x480000, "user", 0)
	ROM_LOAD("fmt_dos.rom",  0x000000, 0x080000, CRC(2bc2af96) SHA1(99cd51c5677288ad8ef711b4ac25d981fd586884) )
	ROM_LOAD("fmt_dic.rom",  0x100000, 0x080000, CRC(82d1daa2) SHA1(7564020dba71deee27184824b84dbbbb7c72aa4e) )
	ROM_LOAD("fmt_fnt.rom",  0x180000, 0x040000, CRC(dd6fd544) SHA1(a216482ea3162f348fcf77fea78e0b2e4288091a) )
	ROM_LOAD("cmar_sys.rom",  0x200000, 0x040000, CRC(e1ff7ce1) SHA1(e6c359177e4e9fb5bbb7989c6bbf6e95c091fd88) )
	ROM_LOAD("cmar_ex0.rom",  0x280000, 0x080000, CRC(e248bfbd) SHA1(0ce89952a7901dd4d256939a6bc8597f87e51ae7) )
	ROM_LOAD("cmar_ex1.rom",  0x300000, 0x080000, CRC(ab2e94f0) SHA1(4b3378c772302622f8e1139ed0caa7da1ab3c780) )
	ROM_LOAD("cmar_ex2.rom",  0x380000, 0x080000, CRC(ce150ec7) SHA1(1cd8c39f3b940e03f9fe999ebcf7fd693f843d04) )
	ROM_LOAD("cmar_ex3.rom",  0x400000, 0x080000, CRC(582fc7fc) SHA1(a77d8014e41e9ff0f321e156c0fe1a45a0c5e58e) )
	ROM_REGION( 0x20, "serial", 0)
	ROM_LOAD("mytownscm.rom",  0x00, 0x20, CRC(bc58eba6) SHA1(483087d823c3952cc29bd827e5ef36d12c57ad49) )
ROM_END

/* Driver */

/*    YEAR  NAME        PARENT    COMPAT  MACHINE   INPUT  CLASS          INIT        COMPANY    FULLNAME                                   FLAGS */
COMP( 1989, fmtowns,    0,        0,      towns,    towns, towns_state,   empty_init, "Fujitsu", "FM-Towns (Model 1 / 2)",                  MACHINE_NOT_WORKING)
COMP( 1991, fmtownsv03, fmtowns,  0,      towns,    towns, towns_state,   empty_init, "Fujitsu", "FM-Towns (unknown, V03 L01 00 91/07/09)", MACHINE_NOT_WORKING)
COMP( 1991, fmtownsux,  fmtowns,  0,      townsux,  towns, towns16_state, empty_init, "Fujitsu", "FM-Towns II UX",                          MACHINE_NOT_WORKING)
COMP( 1992, fmtownshr,  fmtowns,  0,      townshr,  towns, towns_state,   empty_init, "Fujitsu", "FM-Towns II HR",                          MACHINE_NOT_WORKING)
COMP( 1993, fmtownsmx,  fmtowns,  0,      townsmx,  towns, towns_state,   empty_init, "Fujitsu", "FM-Towns II MX",                          MACHINE_NOT_WORKING)
COMP( 1994, fmtownsftv, fmtowns,  0,      townsftv, towns, towns_state,   empty_init, "Fujitsu", "FM-Towns II FreshTV",                     MACHINE_NOT_WORKING)
COMP( 19??, fmtownssj,  fmtowns,  0,      townssj,  towns, towns_state,   empty_init, "Fujitsu", "FM-Towns II SJ",                          MACHINE_NOT_WORKING)
CONS( 1993, fmtmarty,   0,        0,      marty,    marty, marty_state,   empty_init, "Fujitsu", "FM-Towns Marty",                          MACHINE_NOT_WORKING)
CONS( 1993, fmtmarty2,  fmtmarty, 0,      marty,    marty, marty_state,   empty_init, "Fujitsu", "FM-Towns Marty 2",                        MACHINE_NOT_WORKING)
CONS( 1994, carmarty,   fmtmarty, 0,      marty,    marty, marty_state,   empty_init, "Fujitsu", "FM-Towns Car Marty",                      MACHINE_NOT_WORKING)



fontwriter.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    Sharp FontWriter series

    Skeleton driver by R. Belmont

    Main CPU: ROMless Mitsubishi M37720
    FDC: NEC 72068 (entire PC controller on a chip)
    512k RAM
    Custom gate array
    AT28C16 parallel EPROM
    640x400 dot-matrix LCD

    Things to check
    - Hook up 37720 DMAC, it's used before this dies
    - Check if "stack in bank FF" bit is used
    - Verify timer implementation

****************************************************************************/

#include "emu.h"
#include "cpu/m37710/m37710.h"
#include "machine/nvram.h"
#include "machine/at28c16.h"
#include "screen.h"
#include "speaker.h"


namespace {

class fontwriter_state : public driver_device
{
public:
	fontwriter_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
			m_maincpu(*this, "maincpu")
	{ }

	void fontwriter(machine_config &config);
	void fw600(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	uint8_t vbl_r()
	{
		m_vbl ^= 0xff;
		return m_vbl;
	}
	uint8_t vbl2_r()
	{
		m_vbl2 ^= 0x88;
		return m_vbl;
	}
	void main_map(address_map &map) ATTR_COLD;
	void fw600_map(address_map &map) ATTR_COLD;

	// devices
	required_device<m37720s1_device> m_maincpu;

	// driver_device overrides
	virtual void video_start() override ATTR_COLD;
	uint8_t m_vbl = 0, m_vbl2 = 0;
};

void fontwriter_state::machine_reset()
{
	m_vbl = 0;
}

void fontwriter_state::machine_start()
{
}

void fontwriter_state::video_start()
{
}

uint32_t fontwriter_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void fontwriter_state::main_map(address_map &map)
{
	map(0x002000, 0x007fff).ram();
	map(0x008000, 0x00ffff).rom().region("maincpu", 0x0000);
	map(0x020000, 0x04ffff).ram();
	map(0x100000, 0x1007ff).rw("at28c16", FUNC(at28c16_device::read), FUNC(at28c16_device::write));
	map(0x200000, 0x3fffff).rom().region("maincpu", 0x0000);
}

void fontwriter_state::fw600_map(address_map &map)
{
	map(0x000280, 0x0002ff).ram();
	map(0x000800, 0x000fff).rw("at28c16", FUNC(at28c16_device::read), FUNC(at28c16_device::write));
	map(0x002000, 0x007fff).ram();
	map(0x008000, 0x00ffff).rom().region("maincpu", 0x1f8000);
	map(0x020000, 0x04ffff).ram();
	map(0x200000, 0x3fffff).rom().region("maincpu", 0x0000);
}

static INPUT_PORTS_START( fontwriter )
INPUT_PORTS_END

void fontwriter_state::fontwriter(machine_config &config)
{
	M37720S1(config, m_maincpu, XTAL(16'000'000)); /* M37720S1 @ 16MHz - main CPU */
	m_maincpu->set_addrmap(AS_PROGRAM, &fontwriter_state::main_map);
	m_maincpu->p6_in_cb().set(FUNC(fontwriter_state::vbl_r));
	m_maincpu->p7_in_cb().set(FUNC(fontwriter_state::vbl2_r));

	AT28C16(config, "at28c16", 0);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500) /* not accurate */);
	screen.set_screen_update(FUNC(fontwriter_state::screen_update));
	screen.set_size(640, 400);
	screen.set_visarea_full();
}

void fontwriter_state::fw600(machine_config &config)
{
	M37720S1(config, m_maincpu, XTAL(16'000'000)); /* M37720S1 @ 16MHz - main CPU */
	m_maincpu->set_addrmap(AS_PROGRAM, &fontwriter_state::fw600_map);
	m_maincpu->p6_in_cb().set(FUNC(fontwriter_state::vbl_r));
	m_maincpu->p7_in_cb().set(FUNC(fontwriter_state::vbl2_r));

	AT28C16(config, "at28c16", 0);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500) /* not accurate */);
	screen.set_screen_update(FUNC(fontwriter_state::screen_update));
	screen.set_size(640, 400);
	screen.set_visarea_full();
}

ROM_START(fw600)
	ROM_REGION(0x200000, "maincpu", 0)      /* M37720 program ROM */
	ROM_LOAD( "lh5388n5.bin", 0x000000, 0x100000, CRC(3bcc5c19) SHA1(510e3795faf18e10f2fef69110f96183e7cfee35) )
	ROM_LOAD( "lh5388n9.bin", 0x100000, 0x100000, CRC(be2198df) SHA1(9e42f3a933c6f247c452910af3a2e9196291574a) )

	ROM_REGION(0x800, "at28c16", 0)         /* AT28C16 parallel EPROM */
	ROM_LOAD( "at28c16.bin",  0x000000, 0x000800, CRC(a84eafd9) SHA1(12503a71e98f80819959d41643b1d2773739b923) )
ROM_END

ROM_START(fw700ger)
	ROM_REGION(0x200000, "maincpu", 0)       /* M37720 program ROM */
	ROM_LOAD( "lh5370pd.ic7", 0x000000, 0x200000, CRC(29083e13) SHA1(7e1605f91b53580e75f638f9e6b0917305c35f84) )

	ROM_REGION(0x800, "at28c16", ROMREGION_ERASE00)         /* AT28C16 parallel EPROM */
ROM_END

} // anonymous namespace


SYST( 1994, fw600,    0, 0, fw600, fontwriter, fontwriter_state, empty_init, "Sharp", "FontWriter FW-600", MACHINE_NOT_WORKING|MACHINE_NO_SOUND )
SYST( 1994, fw700ger, 0, 0, fontwriter, fontwriter, fontwriter_state, empty_init, "Sharp", "FontWriter FW-700 (German)", MACHINE_NOT_WORKING|MACHINE_NO_SOUND )



fp1100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
/**************************************************************************************************

Casio FP-1000 / FP-1100 (GX-205)

TODO:
- Keyboard not working, should trigger INTF0 on key pressed (for PF only), main CPU receives
  garbage from sub (command protocol 0x04 -> <scancode> -> 0x00 -> 0x00)
  As a _ugly_ workaround you can intercept ASCII scancodes from main CPU with bp d18 A=<value> with
  any emu::keypost trigger.
- Memory maps and machine configuration for FP-1000 with reduced VRAM;
- Unimplemented instruction PER triggered in sub CPU;
- SCREEN 1 mode has heavy corrupted GFXs and runs at half speed, interlace mode?
- Cassette Load is really not working, uses a complex 6 pin discrete circuitry;
- Sub CPU needs proper WAIT line from uPD7801;
- Main CPU waitstates;
- centronics options (likely):
  - FP-1011PL (plotter)
  - FP-1012PR (OEM Epson MX-80)
  - FP-1014PRK (Kanji printer)
  - FP-1017PR (OEM Epson MX-160)

===================================================================================================

Info found at various sites:

Casio FP-1000 and FP-1100 are "pre-PC" personal computers, with
Cassette, Floppy Disk, Printer and two cartridge/expansion slots.  They
had 32K ROM, 64K main RAM, 80x25 text display, 320x200, 640x200, 640x400
graphics display.  Floppy disk is 2x 5 1/4.

The FP-1000 had 16K videoram and monochrome only.  The monitor had a
switch to invert the display (swap foreground and background colours).

The FP-1100 had 48K videoram and 8 colours.

Processors: Z80 @ 4MHz, uPD7801G @ 2MHz

Came with BASIC built in, and you could run CP/M 2.2 from the floppy
disk.

The keyboard is a separate unit.  It contains a beeper.

**************************************************************************************************/

#include "emu.h"

#include "bus/centronics/ctronics.h"
#include "bus/fp1000/fp1000_exp.h"
#include "cpu/upd7810/upd7810.h"
#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "machine/gen_latch.h"
#include "machine/input_merger.h"
#include "machine/timer.h"
#include "sound/beep.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "softlist_dev.h"

#define VERBOSE 0
#include "logmacro.h"


namespace {

#define MAIN_CLOCK 15.9744_MHz_XTAL

class fp1100_state : public driver_device
{
public:
	fp1100_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_subcpu(*this, "sub")
		, m_crtc(*this, "crtc")
		, m_iplview(*this, "iplview")
		, m_workram(*this, "workram")
		, m_videoram(*this, "videoram.%u", 0)
		, m_palette(*this, "palette")
		, m_keyboard(*this, "KEY.%u", 0)
		, m_beep(*this, "beeper")
		, m_centronics(*this, "centronics")
		, m_cassette(*this, "cassette")
		, m_slot(*this, "slot.%u", 0)
		, m_irqs_int(*this, { "irqs_inta", "irqs_intb", "irqs_intc", "irqs_intd"})
	{ }

	void fp1100(machine_config &config);
	DECLARE_INPUT_CHANGED_MEMBER(fkey_hit_cb);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<upd7801_device> m_subcpu;
	required_device<mc6845_device> m_crtc;
	memory_view m_iplview;
	required_shared_ptr<u8> m_workram;
	required_shared_ptr_array<u8, 3> m_videoram;
	required_device<palette_device> m_palette;
	required_ioport_array<16> m_keyboard;
	required_device<beep_device> m_beep;
	required_device<centronics_device> m_centronics;
	required_device<cassette_image_device> m_cassette;
	required_device_array<fp1000_exp_slot_device, 2> m_slot;
	required_device_array<input_merger_device, 4> m_irqs_int;

	void main_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void sub_map(address_map &map) ATTR_COLD;

	void main_bank_w(u8 data);
	void irq_mask_w(u8 data);
	void colour_control_w(u8 data);
	template <unsigned N> u8 vram_r(offs_t offset);
	template <unsigned N> void vram_w(offs_t offset, u8 data);
	void kbd_row_w(u8 data);
	void porta_w(u8 data);
	u8 portb_r();
	u8 portc_r();
	void portc_w(u8 data);
	void centronics_busy_w(int state);

	MC6845_UPDATE_ROW(crtc_update_row);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_w);

	u8 m_kbd_row = 0;
	u8 m_col_border = 0;
	u8 m_col_cursor = 0;
	u8 m_col_display = 0;
	u8 m_centronics_busy = 0;
	u8 m_cassette_data[4]{};
	bool m_sub_irq_status = false;
	bool m_cassettebit = false;
	bool m_cassetteold = false;

	u8 m_sub_porta = 0;
	u8 m_sub_portc = 0;

	u8 m_pending_interrupts = 0;
	u8 m_active_interrupts = 0;
	u8 m_interrupt_mask = 0;
	int m_caps_led_state = 0;
	int m_shift_led_state = 0;

	template<int Line> void int_w(int state);
	TIMER_CALLBACK_MEMBER(update_interrupts);
	IRQ_CALLBACK_MEMBER(restart_cb);

	int m_hsync_state = 0;
	bool m_sub_wait = false;
	void hsync_cb(int state);
};

MC6845_UPDATE_ROW( fp1100_state::crtc_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u32 *p = &bitmap.pix(y);
	const u8 porta = m_sub_porta;

	if (BIT(porta, 4))
	{ // green screen
		for (u16 x = 0; x < x_count; x++)
		{
			u16 const mem = (((ma + x) << 3) + ra) & 0x3fff;
			// TODO: ORs contents from the other two layers (for FP-1100 only)
			u8 const g = m_videoram[0][mem];
			for (u8 i = 0; i < 8; i++)
			{
				u8 col = BIT(g, i);
				if (x == cursor_x)
					col ^= 1;
				*p++ = palette[col << 1];
			}
		}
	}
	else
	{ // RGB screen
		for (u16 x = 0; x < x_count; x++)
		{
			u16 const mem = (((ma + x) << 3) + ra) & 0x3fff;
			u8 const b = BIT(porta, 2) ? m_videoram[0][mem] : 0;
			u8 const r = BIT(porta, 1) ? m_videoram[1][mem] : 0;
			u8 const g = BIT(porta, 0) ? m_videoram[2][mem] : 0;
			for (u8 i = 0; i < 8; i++)
			{
				u8 col = BIT(r, i) | (BIT(g, i) << 1) | (BIT(b, i) << 2);
				if (x == cursor_x)
					col = m_col_cursor;
				*p++ = palette[col];
			}
		}
	}
}

/*
d0 - Package select
d1 - Bank select (at boot time)
other bits not used
*/
void fp1100_state::main_bank_w(u8 data)
{
	m_iplview.select(BIT(data, 1));
	const u8 slot_select = BIT(data, 0);
	m_slot[slot_select ^ 1]->select_w(false);
	m_slot[slot_select]->select_w(true);
}

/*
 * x--- ---- mask for main to sub (INTF2)
 * ---x ---- INTS (sub to main)
 * ---- x--- INTD
 * ---- -x-- INTC (RS-232C)
 * ---- --x- INTB (FDC bus slot irq)
 * ---- ---x INTA (FDC bus slot drq)
 */
void fp1100_state::irq_mask_w(u8 data)
{
	m_interrupt_mask = data;
	machine().scheduler().synchronize(timer_expired_delegate(FUNC(fp1100_state::update_interrupts), this));

	// TODO: this handling doesn't seem enough
	if (BIT(data, 7) && !m_sub_irq_status)
	{
		m_subcpu->set_input_line(UPD7810_INTF2, ASSERT_LINE);
		LOG("%s: Sub IRQ asserted\n",machine().describe_context());
		m_sub_irq_status = true;
	}
	else if (!BIT(data, 7) && m_sub_irq_status)
	{
		m_subcpu->set_input_line(UPD7810_INTF2, CLEAR_LINE);
		LOG("%s: Sub IRQ cleared\n",machine().describe_context());
		m_sub_irq_status = false;
	}

	LOG("%s: IRQmask=%X\n",machine().describe_context(),data);
}

// NOTE: BASIC is very picky if it doesn't find a configuration akin to this
// Disregard the "NOT USABLE" for page 0 from service manual (definitely wants offset + 0x9000 there)
void fp1100_state::main_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).view(m_iplview);
	m_iplview[0](0x0000, 0x7fff).rom().region("ipl", 0);
	m_iplview[0](0x8000, 0x8fff).rom().region("basic", 0);
	m_iplview[0](0x9000, 0xffff).lr8(NAME([this] (offs_t offset) { return m_workram[offset + 0x9000]; }));
	// writes always goes to work RAM
	m_iplview[0](0x0000, 0xffff).writeonly().share(m_workram);
	m_iplview[1](0x0000, 0xffff).ram().share(m_workram);
}

void fp1100_state::io_map(address_map &map)
{
	map.unmap_value_high();
	//map(0x0000, 0xfeff) slot memory area
//  map(0xff00, 0xff00).mirror(0x7f).rw(FUNC(fp1100_state::slot_id_r), FUNC(fp1100_state::slot_bank_w));
	map(0xff80, 0xff80).mirror(0x7f).r("sub2main", FUNC(generic_latch_8_device::read));
	map(0xff80, 0xff80).mirror(0x1f).w(FUNC(fp1100_state::irq_mask_w));
	map(0xffa0, 0xffa0).mirror(0x1f).w(FUNC(fp1100_state::main_bank_w));
	map(0xffc0, 0xffc0).mirror(0x3f).w("main2sub", FUNC(generic_latch_8_device::write));
}

/*
d0,1,2 - border colour (B,R,G)
d3     - not used
d4,5,6 - colour of cursor; or display area (B,R,G) (see d7)
d7     - 1=display area; 0=cursor
*/
void fp1100_state::colour_control_w(u8 data)
{
	// HACK: change BRG to RGB
	data = bitswap<8>(data, 7, 4, 6, 5, 3, 0, 2, 1);
	m_col_border = data & 7;

	if (BIT(data, 7))
		m_col_display = (data >> 4) & 7;
	else
	{
		m_col_display = 0;
		m_col_cursor = data >> 4;
	}
}

template <unsigned N> u8 fp1100_state::vram_r(offs_t offset)
{
	return m_videoram[N][offset];
}

template <unsigned N> void fp1100_state::vram_w(offs_t offset, u8 data)
{
	// NOTE: POST makes sure to punt in FP-1000 mode if this don't XOR the value
	m_videoram[N][offset] = ~data;

	if (!machine().side_effects_disabled())
	{
		if (!m_sub_wait && m_hsync_state)
		{
			// TODO: actual uPD7801 WAIT line
			m_subcpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
			m_sub_wait = true;
		}
	}
}

/*
d0,1,2,3 - keyboard scan row
         - if 13, turn on shift-lock LED
         - if 14, turn on caps-lock LED
         - if 15, turn off both LEDs
d4       - Beeper
d5       - "3state buffer of key data line (1=open, 0=closed)"
d6,7     - not used
*/
void fp1100_state::kbd_row_w(u8 data)
{
	m_kbd_row = data;
	// guess: expect keyboard in open state for leds
	if (BIT(m_kbd_row, 5))
	{
		switch(m_kbd_row & 0xf)
		{
			case 13: m_shift_led_state = 1; break;
			case 14: m_caps_led_state = 1; break;
			case 15: m_caps_led_state = m_shift_led_state = 0; break;
		}
		// TODO: to output_finders
		//popmessage("%d %d", m_caps_led_state, m_shift_led_state);
	}
	m_beep->set_state(BIT(data, 4));
}

void fp1100_state::sub_map(address_map &map)
{
	map(0x0000, 0x1fff).rom().region("sub_ipl", 0x0000);
	map(0x2000, 0x5fff).rw(FUNC(fp1100_state::vram_r<0>), FUNC(fp1100_state::vram_w<0>)).share(m_videoram[0]);
	map(0x6000, 0x9fff).rw(FUNC(fp1100_state::vram_r<1>), FUNC(fp1100_state::vram_w<1>)).share(m_videoram[1]);
	map(0xa000, 0xdfff).rw(FUNC(fp1100_state::vram_r<2>), FUNC(fp1100_state::vram_w<2>)).share(m_videoram[2]);
	map(0xe000, 0xe000).mirror(0x3fe).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0xe001, 0xe001).mirror(0x3fe).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0xe400, 0xe400).mirror(0x3ff).portr("DSW").w(FUNC(fp1100_state::kbd_row_w));
	map(0xe800, 0xe800).mirror(0x3ff).r("main2sub", FUNC(generic_latch_8_device::read));
	map(0xe800, 0xe800).mirror(0x3ff).w("sub2main", FUNC(generic_latch_8_device::write));
	map(0xec00, 0xec00).mirror(0x3ff).lw8(NAME([this] (u8 data) { m_subcpu->set_input_line(UPD7810_INTF0, CLEAR_LINE); }));
	map(0xf000, 0xf000).mirror(0x3ff).w(FUNC(fp1100_state::colour_control_w));
	map(0xf400, 0xff7f).rom().region("sub_ipl", 0x2400);
//  map(0xff80, 0xffff) internal 7801 RAM
}

/*
d0,1,2 - enable RGB guns (G,R,B)
d3     - CRTC clock (80 or 40 cols)
d4     - RGB (0) or Green (1)
d5     - clear videoram
d6     - CMT baud rate (1=300; 0=1200)
d7     - CMT load clock
The SO pin is Serial Output to CMT (1=2400Hz; 0=1200Hz)
*/
void fp1100_state::porta_w(u8 data)
{
	if (BIT(m_sub_porta, 5) && !BIT(data, 5))
	{
		for (int i = 0; i < 3; i++)
		{
			const u8 fill_value = BIT(m_col_display, i) ? 0xff : 0;
			std::fill(m_videoram[i].begin(), m_videoram[i].end(), fill_value);
		}
	}

	const u8 crtc_divider = BIT(data, 3) ? 16 : 8;
	m_crtc->set_unscaled_clock(MAIN_CLOCK / crtc_divider);

	m_sub_porta = data;
}

u8 fp1100_state::portb_r()
{
	u8 data = m_keyboard[m_kbd_row & 15]->read();
	LOG("%s: PortB:%X:%X\n",machine().describe_context(),m_kbd_row,data);

	if (BIT(m_kbd_row, 5))
		return data;

	return 0;
}

/*
d0 - Centronics busy
d1 - Centronics error
d2 - CMT load input clock
d7 - CMT load serial data
*/
u8 fp1100_state::portc_r()
{
	return (m_sub_portc & 0x78) | m_centronics_busy;
}

/*
d3 - cause INT on main cpu
d4 - Centronics port is used for input or output
d5 - CMT relay
d6 - Centronics strobe
*/
void fp1100_state::portc_w(u8 data)
{
	u8 const bits = data ^ m_sub_portc;
	const int main_int_state = BIT(data, 3);
	if (BIT(m_sub_portc, 3) != main_int_state)
		int_w<4>(main_int_state);
	m_sub_portc = data;

	if (BIT(bits, 5))
		m_cassette->change_state(BIT(data, 5) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
	if (BIT(bits, 6))
		m_centronics->write_strobe(BIT(data, 6));
}

// IRQ section (main)
// TODO: merge with skeleton/cdc721.cpp (reuses SN74LS148N)

template <int Line> void fp1100_state::int_w(int state)
{
	if (BIT(m_pending_interrupts, Line) == state)
		return;

	if (state)
		m_pending_interrupts |= 0x01 << Line;
	else
		m_pending_interrupts &= ~(0x01 << Line);

	machine().scheduler().synchronize(timer_expired_delegate(FUNC(fp1100_state::update_interrupts), this));
}

TIMER_CALLBACK_MEMBER(fp1100_state::update_interrupts)
{
	m_active_interrupts = m_pending_interrupts & m_interrupt_mask;
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, m_active_interrupts != 0 ? ASSERT_LINE : CLEAR_LINE);
}

IRQ_CALLBACK_MEMBER(fp1100_state::restart_cb)
{
	u8 vector = 0xf0;
	// INTS > INTA > INTB > INTC > INTD priority order
	// INTS uses 0xf0, INTA 0xf2 and so on.
	u8 active = bitswap<5>(m_active_interrupts, 3, 2, 1, 0, 4);
	while (vector < 0xfa && !BIT(active, 0))
	{
		active >>= 1;
		vector += 0x02;
	}
	return vector;
}

// IRQ section (sub)

void fp1100_state::hsync_cb(int state)
{
	m_hsync_state = state;
	if (m_sub_wait && !state)
	{
		m_subcpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
		m_sub_wait = false;
	}
}

// KI8 specifically connects to INT0 on hit
// TODO: stub, hangs by banging keys too fast
INPUT_CHANGED_MEMBER(fp1100_state::fkey_hit_cb)
{
	if (!oldval && newval)
	{
		m_subcpu->set_input_line(UPD7810_INTF0, ASSERT_LINE);
	}
}


static INPUT_PORTS_START( fp1100 )
	PORT_START("KEY.0")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("KEY.1")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_END)                                  PORT_NAME("BREAK") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp1100_state::fkey_hit_cb), 1)
	PORT_BIT(0x60, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT)                                 PORT_NAME("\u30ab\u30ca (KANA)")  // カナ
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK)                             PORT_NAME("CAPS")   PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RALT)                                 PORT_NAME("GRAPH")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_NAME("CTRL")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)   PORT_CODE(KEYCODE_RSHIFT)   PORT_NAME("SHIFT")  PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("KEY.2")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10)        PORT_NAME("PF0") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp1100_state::fkey_hit_cb), 2)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD)  PORT_NAME("ENTER")                  PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK)   PORT_NAME("KP*")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_NAME(u8"Z  \u30c4  \u30c3")    PORT_CHAR('Z') PORT_CHAR('z')  // ツ ッ
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)          PORT_NAME(u8"Q  \u30bf")            PORT_CHAR('Q') PORT_CHAR('q')  // タ
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD)  PORT_NAME("KP-")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)        PORT_NAME("ESC")                    PORT_CHAR(27)
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_NAME(u8"A  \u30c1")            PORT_CHAR('A') PORT_CHAR('a')  // チ

	PORT_START("KEY.3")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)         PORT_NAME("PF1")                    PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp1100_state::fkey_hit_cb), 3)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA_PAD)  PORT_NAME("KP,")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD)  PORT_NAME("KP/")                    PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)          PORT_NAME(u8"X  \u30b5")            PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)          PORT_NAME(u8"W  \u30c6")            PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD)   PORT_NAME("KP+")                    PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)          PORT_NAME(u8"1  !  \u30cc")         PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)          PORT_NAME(u8"S  \u30c8")            PORT_CHAR('S') PORT_CHAR('s')

	PORT_START("KEY.4")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)         PORT_NAME("PF2")                    PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp1100_state::fkey_hit_cb), 4)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD)    PORT_NAME("KP.")                    PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)        PORT_NAME("DEL")                    PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)          PORT_NAME(u8"C  \u30bd")            PORT_CHAR('C') PORT_CHAR('c')  // ソ
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)          PORT_NAME(u8"E  \u30a4  \u30a3")    PORT_CHAR('E') PORT_CHAR('e')  // イ ィ
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)      PORT_NAME("KP3")                    PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)          PORT_NAME(u8"2  \"  \u30d5")        PORT_CHAR('2') PORT_CHAR('"')  // フ
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)          PORT_NAME(u8"D  \u30b7")            PORT_CHAR('D') PORT_CHAR('d')  // シ

	PORT_START("KEY.5")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)         PORT_NAME("PF3")                    PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp1100_state::fkey_hit_cb), 5)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_000_PAD)    PORT_NAME("KP000")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)      PORT_NAME("Right")                  PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)          PORT_NAME(u8"V  \u30d2")            PORT_CHAR('V') PORT_CHAR('v')  // ヒ
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)          PORT_NAME(u8"R  \u30b9")            PORT_CHAR('R') PORT_CHAR('r')  // ス
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)      PORT_NAME("KP6")                    PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)          PORT_NAME(u8"3  #  \u30a2  \u30a1") PORT_CHAR('3') PORT_CHAR('#')  // ア ァ
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)          PORT_NAME(u8"F  \u30cf")            PORT_CHAR('F') PORT_CHAR('f')  // ハ

	PORT_START("KEY.6")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)         PORT_NAME("PF4")                    PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp1100_state::fkey_hit_cb), 6)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)      PORT_NAME("Space")                  PORT_CHAR(' ')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN)       PORT_NAME("INS")                    PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)          PORT_NAME(u8"B  \u30b3")            PORT_CHAR('B') PORT_CHAR('b')  // コ
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)          PORT_NAME(u8"T  \u30ab")            PORT_CHAR('T') PORT_CHAR('t')  // カ
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)      PORT_NAME("KP9")                    PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)          PORT_NAME(u8"4  $  \u30a6  \u30a5") PORT_CHAR('4') PORT_CHAR('$')  // ウ ゥ
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)          PORT_NAME(u8"G  \u30ad")            PORT_CHAR('G') PORT_CHAR('g')  // キ

	PORT_START("KEY.7")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)         PORT_NAME("PF5")                    PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp1100_state::fkey_hit_cb), 7)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)      PORT_NAME("KP0")                    PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)       PORT_NAME("Down")                   PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)          PORT_NAME(u8"N  \u30df")            PORT_CHAR('N') PORT_CHAR('n')  // ミ
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_NAME(u8"Y  \u30f3")            PORT_CHAR('Y') PORT_CHAR('y')  // ン
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)      PORT_NAME("KP8")                    PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)          PORT_NAME(u8"5  %  \u30a8  \u30a7") PORT_CHAR('5') PORT_CHAR('%')  // エ ェ
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)          PORT_NAME(u8"H  \u30af")            PORT_CHAR('H') PORT_CHAR('h')  // ク

	PORT_START("KEY.8")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)         PORT_NAME("PF6")                    PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp1100_state::fkey_hit_cb), 8)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)      PORT_NAME("KP2")                    PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)         PORT_NAME("Up")                     PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)          PORT_NAME(u8"M  \u30e2")            PORT_CHAR('M') PORT_CHAR('m')  // モ
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)          PORT_NAME(u8"U  \u30ca")            PORT_CHAR('U') PORT_CHAR('u')  // ナ
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)      PORT_NAME("KP5")                    PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_NAME(u8"6  &  \u30aa  \u30a9") PORT_CHAR('6') PORT_CHAR('&')  // オ ォ
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)          PORT_NAME(u8"J  \u30de")            PORT_CHAR('J') PORT_CHAR('j')  // マ

	PORT_START("KEY.9")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)         PORT_NAME("PF7")                    PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp1100_state::fkey_hit_cb), 9)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)      PORT_NAME("KP1")                    PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP)       PORT_NAME("HOME  CLS")              PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_NAME(u8",  <  \u30cd  \u3001") PORT_CHAR(',') PORT_CHAR('<')  // ネ 、
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)          PORT_NAME(u8"I  \u30cb")            PORT_CHAR('I') PORT_CHAR('i')  // ニ
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)      PORT_NAME("KP4")                    PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_NAME(u8"7  '  \u30e4  \u30e3") PORT_CHAR('7') PORT_CHAR('\'') // ヤ ャ
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)          PORT_NAME(u8"K  \u30ce")            PORT_CHAR('K') PORT_CHAR('k')  // ノ

	PORT_START("KEY.10")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)         PORT_NAME("PF8")                    PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp1100_state::fkey_hit_cb), 10)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_NAME(u8"]  }  \u30e0  \u300d") PORT_CHAR(']') PORT_CHAR('}')  // ム 」
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)       PORT_NAME("Left")                   PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_NAME(u8".  >  \u30eb  \u3002") PORT_CHAR('.') PORT_CHAR('>')  // ル 。
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)          PORT_NAME(u8"O  \u30e9")            PORT_CHAR('O') PORT_CHAR('o')  // ラ
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)      PORT_NAME("KP7")                    PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_NAME(u8"8  (  \u30e6  \u30e5") PORT_CHAR('8') PORT_CHAR('(')  // ユ ュ
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)          PORT_NAME(u8"L  \u30ea")            PORT_CHAR('L') PORT_CHAR('l')  // リ

	PORT_START("KEY.11")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9)         PORT_NAME("PF9")                    PORT_CHAR(UCHAR_MAMEKEY(F9)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp1100_state::fkey_hit_cb), 11)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_NAME(u8"[  {  \u309c  \u300c") PORT_CHAR('[') PORT_CHAR('{')  // ゜ 「
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)  PORT_NAME("BS")                     PORT_CHAR(8)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_NAME(u8"/  ?  \u30e1  \u30fb") PORT_CHAR('/') PORT_CHAR('?')  // メ ・
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)          PORT_NAME(u8"P  \u30bb")            PORT_CHAR('P') PORT_CHAR('p')  // セ
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)      PORT_NAME("RETURN")                 PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_NAME(u8"9  )  \u30e8  \u30e7") PORT_CHAR('9') PORT_CHAR(')')  // ヨ ョ
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_NAME(u8";  +  \u30ec")         PORT_CHAR(';') PORT_CHAR('+')  // レ

	PORT_START("KEY.12")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_NUMLOCK)    PORT_NAME("STOP/CONT") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp1100_state::fkey_hit_cb), 12)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_NAME(u8"-  =  \u30db")         PORT_CHAR('-') PORT_CHAR('=')  // ホ
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_NAME(u8"¥  |  \u30fc")         PORT_CHAR(U'¥') PORT_CHAR('|') // ー
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_NAME(u8"@  `  \u309b")         PORT_CHAR('@') PORT_CHAR('`')  // ゛
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)      PORT_NAME(u8"_  \u30ed")            PORT_CHAR('_')                 // ロ
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_NAME(u8"^  ~  \u30d8")         PORT_CHAR('^') PORT_CHAR('~')  // ヘ
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_NAME(u8"0  \u30ef  \u30f2")    PORT_CHAR('0')                 // ワ ヲ
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)      PORT_NAME(u8":  *  \u30b1")         PORT_CHAR(':') PORT_CHAR('*')  // ケ

	PORT_START("KEY.13")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED) // Capslock LED on

	PORT_START("KEY.14")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED) // Kana LED on

	PORT_START("KEY.15")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED) // LEDs off

	PORT_START("DSW")
	PORT_DIPNAME( 0x01, 0x01, "Text width" ) PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(    0x00, "40 chars/line" )
	PORT_DIPSETTING(    0x01, "80 chars/line" )
	PORT_DIPNAME( 0x02, 0x02, "Screen Mode" ) PORT_DIPLOCATION("SW1:2")
	PORT_DIPSETTING(    0x00, "Screen 0" )
	PORT_DIPSETTING(    0x02, "Screen 1" )
	PORT_DIPNAME( 0x04, 0x04, "FP Mode" ) PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(    0x00, "FP-1000" ) // Green screen
	PORT_DIPSETTING(    0x04, "FP-1100" ) // RGB
	PORT_DIPNAME( 0x08, 0x08, "CMT Baud Rate" ) PORT_DIPLOCATION("SW1:4")
	PORT_DIPSETTING(    0x00, "1200 Baud" )
	PORT_DIPSETTING(    0x08, "300 Baud" )
	PORT_DIPNAME( 0x10, 0x10, "Printer Type" ) PORT_DIPLOCATION("SW1:5")
	PORT_DIPSETTING(    0x00, "<undefined>" )
	PORT_DIPSETTING(    0x10, "FP-1012PR" )
	PORT_DIPNAME( 0x20, 0x20, "Keyboard Type" ) PORT_DIPLOCATION("SW1:6")
	PORT_DIPSETTING(    0x00, "<undefined>" )
	PORT_DIPSETTING(    0x20, "Normal" )
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END


// debugging only
static const gfx_layout chars_8x8 =
{
	8,8,
	256,
	1,
	{ 0 },
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};

static GFXDECODE_START( gfx_fp1100 )
	GFXDECODE_ENTRY( "sub_ipl", 0x2400, chars_8x8, 0, 1 )
GFXDECODE_END

void fp1100_state::centronics_busy_w(int state)
{
	m_centronics_busy = state;
}

TIMER_DEVICE_CALLBACK_MEMBER( fp1100_state::kansas_w )
{
	m_cassette_data[3]++;

	if (m_cassettebit != m_cassetteold)
	{
		m_cassette_data[3] = 0;
		m_cassetteold = m_cassettebit;
	}

	if (m_cassettebit)
		m_cassette->output(BIT(m_cassette_data[3], 0) ? -1.0 : +1.0); // 2400Hz
	else
		m_cassette->output(BIT(m_cassette_data[3], 1) ? -1.0 : +1.0); // 1200Hz
}

void fp1100_state::machine_start()
{
	save_item(NAME(m_sub_wait));
	save_item(NAME(m_sub_irq_status));
	save_item(NAME(m_kbd_row));
	save_item(NAME(m_col_border));
	save_item(NAME(m_col_cursor));
	save_item(NAME(m_col_display));
	save_item(NAME(m_centronics_busy));

	save_item(NAME(m_sub_porta));
	save_item(NAME(m_sub_portc));

	save_item(NAME(m_pending_interrupts));
	save_item(NAME(m_active_interrupts));
	save_item(NAME(m_interrupt_mask));

	save_item(NAME(m_caps_led_state));
	save_item(NAME(m_shift_led_state));

	// FIXME: cassette state intentionally not saved for now
}

void fp1100_state::machine_reset()
{
	m_sub_wait = false;
	m_sub_irq_status = false;

	m_beep->set_state(0);

	m_interrupt_mask = m_active_interrupts = m_pending_interrupts = 0;
	m_kbd_row = 0;
	m_caps_led_state = m_shift_led_state = 0;
	m_col_border = 0;
	m_col_cursor = 0;
	m_col_display = 0;
	m_sub_porta = 0;
	m_sub_portc = 0;

	m_iplview.select(0);
}

void fp1100_state::fp1100(machine_config &config)
{
	Z80(config, m_maincpu, MAIN_CLOCK/4);
	m_maincpu->set_addrmap(AS_PROGRAM, &fp1100_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &fp1100_state::io_map);
	m_maincpu->set_irq_acknowledge_callback(FUNC(fp1100_state::restart_cb));

	UPD7801(config, m_subcpu, MAIN_CLOCK/4);
	m_subcpu->set_addrmap(AS_PROGRAM, &fp1100_state::sub_map);
	m_subcpu->pa_out_cb().set(FUNC(fp1100_state::porta_w));
	m_subcpu->pb_in_cb().set(FUNC(fp1100_state::portb_r));
	m_subcpu->pb_out_cb().set("cent_data_out", FUNC(output_latch_device::write));
	m_subcpu->pc_in_cb().set(FUNC(fp1100_state::portc_r));
	m_subcpu->pc_out_cb().set(FUNC(fp1100_state::portc_w));
	m_subcpu->txd_func().set([this] (bool state) { m_cassettebit = state; });

	GENERIC_LATCH_8(config, "main2sub");
	GENERIC_LATCH_8(config, "sub2main");
	// NOTE: Needs some sync otherwise it outright refuses to boot
	config.set_perfect_quantum("maincpu");
//  config.set_perfect_quantum("sub");

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(fp1100_state::centronics_busy_w));

	output_latch_device &latch(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(latch);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("fp1100_cass");

	TIMER(config, "kansas_w").configure_periodic(FUNC(fp1100_state::kansas_w), attotime::from_hz(4800));

	SOFTWARE_LIST(config, "cass_list").set_original("fp1100_cass");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	// doesn't matter, will be reset by 6845 anyway
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.set_size(640, 480);
	screen.set_visarea_full();
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	PALETTE(config, m_palette).set_entries(8);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_fp1100);

	HD6845S(config, m_crtc, MAIN_CLOCK/8);   // hand tuned to get ~60 fps
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(fp1100_state::crtc_update_row));
	m_crtc->out_hsync_callback().set(FUNC(fp1100_state::hsync_cb));

	SPEAKER(config, "mono").front_center();
	BEEP(config, "beeper", 950) // guess
		.add_route(ALL_OUTPUTS, "mono", 0.50); // inside the keyboard

	INPUT_MERGER_ANY_HIGH(config, m_irqs_int[0]).output_handler().set(FUNC(fp1100_state::int_w<0>));
	INPUT_MERGER_ANY_HIGH(config, m_irqs_int[1]).output_handler().set(FUNC(fp1100_state::int_w<1>));
	INPUT_MERGER_ANY_HIGH(config, m_irqs_int[2]).output_handler().set(FUNC(fp1100_state::int_w<2>));
	INPUT_MERGER_ANY_HIGH(config, m_irqs_int[3]).output_handler().set(FUNC(fp1100_state::int_w<3>));

	FP1000_EXP_SLOT(config, m_slot[0], fp1000_exp_devices, nullptr);
	m_slot[0]->set_iospace(m_maincpu, AS_IO);
	m_slot[0]->inta_callback().set(m_irqs_int[0], FUNC(input_merger_device::in_w<0>));
	m_slot[0]->intb_callback().set(m_irqs_int[1], FUNC(input_merger_device::in_w<0>));
	m_slot[0]->intc_callback().set(m_irqs_int[2], FUNC(input_merger_device::in_w<0>));
	m_slot[0]->intd_callback().set(m_irqs_int[3], FUNC(input_merger_device::in_w<0>));

	FP1000_EXP_SLOT(config, m_slot[1], fp1000_exp_devices, nullptr);
	m_slot[1]->set_iospace(m_maincpu, AS_IO);
	m_slot[1]->inta_callback().set(m_irqs_int[0], FUNC(input_merger_device::in_w<1>));
	m_slot[1]->intb_callback().set(m_irqs_int[1], FUNC(input_merger_device::in_w<1>));
	m_slot[1]->intc_callback().set(m_irqs_int[2], FUNC(input_merger_device::in_w<1>));
	m_slot[1]->intd_callback().set(m_irqs_int[3], FUNC(input_merger_device::in_w<1>));
}

// TODO: chargen, keyboard ROM and key tops can be substituted on actual FP-1000/FP-1100
// HN462732G-JKA to position E8 ("common for all languages")
// HN462532G-G?? to position E21 (chargen)
// - GKA Spain
// - GLA Italy
// - GMA France
// - GNA Germany
// - GPA UK
// - GQA Netherlands
// - GRA Norway/Denmark
// - GSA Sweden/Finland
// - GTA <default chargen, Japan?>

ROM_START( fp1100 )
	ROM_REGION( 0x9000, "bios", ROMREGION_ERASEFF )
	// TODO: split into two roms
	ROM_LOAD( "basic.rom", 0x0000, 0x9000, BAD_DUMP CRC(7c7dd17c) SHA1(985757b9c62abd17b0bd77db751d7782f2710ec3))

	ROM_REGION( 0x8000, "ipl", ROMREGION_ERASEFF )
	ROM_COPY( "bios", 0x0000, 0x0000, 0x8000 )

	ROM_REGION( 0x1000, "basic", ROMREGION_ERASEFF )
	ROM_COPY( "bios", 0x8000, 0x0000, 0x1000 )

	ROM_REGION( 0x3000, "sub_ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "sub1.rom", 0x0000, 0x1000, CRC(8feda489) SHA1(917d5b398b9e7b9a6bfa5e2f88c5b99923c3c2a3))
	ROM_LOAD( "sub2.rom", 0x1000, 0x1000, CRC(359f007e) SHA1(0188d5a7b859075cb156ee55318611bd004128d7))
	// Japan chargen ROM (GTA?)
	ROM_LOAD( "sub3.rom", 0x2000, 0xf80, BAD_DUMP CRC(fb2b577a) SHA1(a9ae6b03e06ea2f5db30dfd51ebf5aede01d9672))
ROM_END

/* FP-1000 has video RAM locations RAM9 to RAM24 unpopulated (only RAM1 to RAM8 are populated) - needs its own machine configuration.
   PCB parts overlay silkscreen for sub-CPU shows "µPD7801G-101", but all examples seen have chips silksreened "D7108G 118". */
ROM_START( fp1000 )
	ROM_REGION( 0x8000, "ipl", ROMREGION_ERASE00 )
	ROM_LOAD( "ipl.rom", 0x0000, 0x8000, NO_DUMP)

	ROM_REGION( 0x1000, "basic", ROMREGION_ERASEFF )
	ROM_LOAD( "2l_a10_kkk_fp1000_basic.c1", 0x0000, 0x1000, CRC(9322dedd) SHA1(40a00684ced2b7ead53ca15a915d98f3fe00d3ba))

	ROM_REGION( 0x3000, "sub_ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "sub1.rom", 0x0000, 0x1000, BAD_DUMP CRC(8feda489) SHA1(917d5b398b9e7b9a6bfa5e2f88c5b99923c3c2a3)) // Not dumped, borrowed from 'fp1100'
	ROM_LOAD( "jka_fp1000.e8",    0x1000, 0x1000, CRC(2aefa4e4) SHA1(b3cc5484426c19a7266d17ea5c4d55441b4e3be8))
	// Spain chargen ROM (GKA really?)
	ROM_LOAD( "jkc_fp1000.e21",   0x2000, 0x1000, CRC(67a668a9) SHA1(37fb9308505b47db36f8c341144ca3fe3fec64af))
ROM_END

} // anonymous namespace


COMP( 1983, fp1100, 0,      0, fp1100, fp1100, fp1100_state, empty_init, "Casio", "FP-1100", MACHINE_NOT_WORKING)
COMP( 1982, fp1000, 0, fp1100, fp1100, fp1100, fp1100_state, empty_init, "Casio", "FP-1000", MACHINE_NOT_WORKING)



fp200.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
// thanks-to: Takeda Toshiya
/**************************************************************************************************

FP-200 (c) 1982 Casio

TODO:
- cassette i/f, glued together by discrete;
- serial i/f;
- FDC (requires test program that Service manual mentions);
- ROM/RAM slot interface;
- mini plotter printer (FP-1011PL)
- graphic printer (FP-1012PR)

Notes:
- on start-up there's a "memory illegal" warning. Issue a "RESET" command to initialize it.

**************************************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/nvram.h"
#include "machine/rp5c01.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

static constexpr XTAL MAIN_CLOCK = 6.144_MHz_XTAL;

class fp200_state : public driver_device
{
public:
	fp200_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rtc(*this, "rtc")
		, m_nvram(*this, "nvram")
		, m_ioview(*this, "ioview")
		, m_key(*this, "KEY%X", 0U)
		, m_gfxrom(*this, "chargen")
	{ }

	void fp200(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(keyb_irq);

private:
	// devices
	required_device<i8085a_cpu_device> m_maincpu;
	required_device<rp5c01_device> m_rtc;
	required_device<nvram_device> m_nvram;
	memory_view m_ioview;
	required_ioport_array<16> m_key;
	required_memory_region m_gfxrom;

	//uint8_t *m_chargen = nullptr;
	uint8_t m_keyb_matrix = 0;

	std::unique_ptr<uint8_t[]> m_lcd_vram[2];
	u16 m_lcd_yoffset[2]{};
	u16 m_lcd_address = 0;
	u8 m_lcd_status = 0;
	bool m_lcd_text_mode = false;

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	template <unsigned N> u8 lcd_data_r(offs_t offset);
	template <unsigned N> void lcd_data_w(offs_t offset, u8 data);

	void lcd_map(address_map &map) ATTR_COLD;

	uint8_t keyb_r(offs_t offset);
	void keyb_w(offs_t offset, uint8_t data);

	void sod_w(int state);
	int sid_r();

	void palette_init(palette_device &palette) const;
	void main_io(address_map &map) ATTR_COLD;
	void main_map(address_map &map) ATTR_COLD;

	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual void video_start() override ATTR_COLD;
};

void fp200_state::video_start()
{
	m_lcd_vram[0] = make_unique_clear<uint8_t[]>(0x400);
	m_lcd_vram[1] = make_unique_clear<uint8_t[]>(0x400);

	save_pointer(NAME(m_lcd_vram[0]), 0x400);
	save_pointer(NAME(m_lcd_vram[1]), 0x400);
}

uint32_t fp200_state::screen_update( screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect )
{
	for(int y = cliprect.min_x; y <= cliprect.max_y; y ++)
	{
		for(int x = cliprect.min_x; x <= cliprect.max_x; x ++)
		{
			const u8 which = x < 80;
			const u8 x_tile = x >> 3;
			const u8 y_tile = y >> 3;
			const u16 base_addr = ((x_tile % 10 + y_tile * 0x80) + m_lcd_yoffset[which]) & 0x3ff;
			const u8 xi = x & 7;
			const u8 yi = y & 7;

			const u8 vram = m_lcd_vram[which][(base_addr + yi * 0x10) & 0x3ff];
			uint8_t const pix = BIT(vram, xi);
			bitmap.pix(y, x) = pix;
		}
	}

	return 0;
}

template <unsigned N> u8 fp200_state::lcd_data_r(offs_t offset)
{
	return m_lcd_vram[N][m_lcd_address & 0x3ff];
}

template <unsigned N> void fp200_state::lcd_data_w(offs_t offset, u8 data)
{
	switch (m_lcd_status)
	{
		// select mode
		case 0xb:
			if (BIT(data, 6))
			{
				if (BIT(data, 4))
					m_lcd_yoffset[N] = m_lcd_address;
				else
					m_lcd_text_mode = bool(BIT(data, 5));
			}

			if (data & 0x8f)
				logerror("Warning: LCD write with unknown mode %02x\n", data);

			break;

		// data write
		case 0x1:
			if (m_lcd_text_mode)
			{
				// The handling doesn't make a whole lot of sense for being practically usable ...
				const u8 tile_address = bitswap<8>(data, 3, 2, 1, 0, 7, 6, 5, 4);
				for (int yi = 0; yi < 8; yi ++)
				{
					u8 tile = 0;
					for (int xi = 0; xi < 8; xi ++)
						tile |= BIT(m_gfxrom->base()[tile_address * 8 + xi], 7 - yi) << xi;
					m_lcd_vram[N][(m_lcd_address + 0x10 * yi) & 0x3ff] = tile;
				}
			}
			else
				m_lcd_vram[N][m_lcd_address & 0x3ff] = data;

			break;
		default:
			logerror("Warning: LCD write with unknown status type %02x\n", m_lcd_status);
			break;
	}
}

/*
 * video section is 2x MSM6216-01GS-1K + 1x MSM6215-01GS-K glued together by the gate array
 *
 * [1] DDDD DDDD vram data/mode select (right half)
 * [2] DDDD DDDD vram data/mode select (left half)
 * [8] SSSS ---- Status code (1=vram type/0xb=attr type)
 *     ---- --YY upper part of Y address
 * [9] YYYY XXXX lower part of Y address / X address
 */
void fp200_state::lcd_map(address_map &map)
{
	map(0x1, 0x1).rw(FUNC(fp200_state::lcd_data_r<1>), FUNC(fp200_state::lcd_data_w<1>));
	map(0x2, 0x2).rw(FUNC(fp200_state::lcd_data_r<0>), FUNC(fp200_state::lcd_data_w<0>));
	map(0x8, 0x8).lrw8(
		NAME([this] (offs_t offset) {
			u8 res =  (m_lcd_status & 0xf) << 4 | (m_lcd_address & 0x300) >> 4;
			return res;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_lcd_status = (data & 0xf0) >> 4;
			if (m_lcd_status == 0xb)
			{
				m_lcd_address &= 0xff;
				m_lcd_address |= (data & 0x3) << 8;
			}
		})
	);
	map(0x9, 0x9).lrw8(
		NAME([this] (offs_t offset) {
			u8 res =  m_lcd_address & 0xff;
			return res;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_lcd_address &= 0x300;
			m_lcd_address |= data;
		})
	);
}

uint8_t fp200_state::keyb_r(offs_t offset)
{
	const uint8_t res = m_key[m_keyb_matrix & 0xf]->read();
	return res;
}

void fp200_state::keyb_w(offs_t offset, uint8_t data)
{
	m_keyb_matrix = data & 0xf;
}

void fp200_state::main_map(address_map &map)
{
	map(0x0000, 0x7fff).rom(); // basic & cetl
	// TODO: 1 waitstate penalty for accessing any RAM
	map(0x8000, 0x9fff).ram().share("nvram"); // internal RAM
//  0xa000, 0xffff exp RAM (FP-201RAM)
	map(0xa000, 0xbfff).ram();
	map(0xc000, 0xdfff).ram();
	map(0xe000, 0xffff).ram(); // or exp ROM (FP-206ROM)
}

void fp200_state::main_io(address_map &map)
{
	map(0x00, 0xff).view(m_ioview);
	m_ioview[0](0x10, 0x1f).rw("rtc", FUNC(rp5c01_device::read), FUNC(rp5c01_device::write));
//  m_ioview[0](0x20, 0x2f) AUTO-POWER OFF
//  m_ioview[0](0x40, 0x4f) FDC Device ID Code (5 for "FP-1021FD")
//  m_ioview[0](0x80, 0xff) FDD (unknown type)
	m_ioview[1](0x00, 0x0f).m(*this, FUNC(fp200_state::lcd_map));
//  m_ioview[1](0x10, 0x10) I/O control (w/o), D1 selects CMT or RS-232C
//  m_ioview[1](0x11, 0x11) I/O control (w/o), uPD65010G gate array control
	// TODO: writes are undocumented, just before reads PC=35C (strobe for update?)
	m_ioview[1](0x20, 0x20).r(FUNC(fp200_state::keyb_r)).nopw();
	m_ioview[1](0x21, 0x21).w(FUNC(fp200_state::keyb_w));
//  m_ioview[1](0x40, 0x4f) CMT & RS-232C control
//  m_ioview[1](0x80, 0x8f) [Centronics] printer
}

INPUT_CHANGED_MEMBER(fp200_state::keyb_irq)
{
	m_maincpu->set_input_line(I8085_RST75_LINE, (newval) ? ASSERT_LINE : CLEAR_LINE);
}

static INPUT_PORTS_START( fp200 )
	PORT_START("KEY0")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("7 / '") PORT_CODE(KEYCODE_7) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)

	PORT_START("KEY1")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("8 / (") PORT_CODE(KEYCODE_8) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)

	PORT_START("KEY2")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("9 / )") PORT_CODE(KEYCODE_9) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(". / >") PORT_CODE(KEYCODE_STOP) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)

	PORT_START("KEY3")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(", / <") PORT_CODE(KEYCODE_COMMA) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("; / +") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)

	PORT_START("KEY4")
//  PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CAPS LOCK") PORT_CODE(KEYCODE_CAPSLOCK) //PORT_TOGGLE PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("INS / DEL") PORT_CODE(KEYCODE_INSERT) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("HOME / CLS") PORT_CODE(KEYCODE_HOME) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PF0 / PF5") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("1 / !") PORT_CODE(KEYCODE_1) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)

	PORT_START("KEY5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("STOP / CONT") PORT_CODE(KEYCODE_RIGHT) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("\\ / |") PORT_CODE(KEYCODE_RIGHT) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RIGHT") PORT_CODE(KEYCODE_RIGHT) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PF1 / PF6") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("2 / \"") PORT_CODE(KEYCODE_2) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)

	PORT_START("KEY6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("^ / ~") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("LEFT") PORT_CODE(KEYCODE_LEFT) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PF2 / PF7") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("3 / #") PORT_CODE(KEYCODE_3) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)

	PORT_START("KEY7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("@ / '") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("- / =") PORT_CODE(KEYCODE_MINUS) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DOWN") PORT_CODE(KEYCODE_DOWN) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PF3 / PF8") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("4 / $") PORT_CODE(KEYCODE_4) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)

	PORT_START("KEY8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("[") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(": / *") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("UP") PORT_CODE(KEYCODE_UP) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PF4 / PF9") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("5 / %") PORT_CODE(KEYCODE_5) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)

	PORT_START("KEY9")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("] / }") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SHIFT LOCK") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("_") PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("6 / &") PORT_CODE(KEYCODE_6) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_IMPULSE(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(fp200_state::keyb_irq), 0)

	PORT_START("KEYA")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("KEYB")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEYC")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEYD")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEYE")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEYF")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEYMOD")
	PORT_BIT( 0x01f, IP_ACTIVE_LOW, IPT_UNUSED )
	// positional switch on keyboard
	PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Basic / CETL Mode") PORT_TOGGLE
	PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BREAK")
	PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GRAPH")
	PORT_BIT( 0x200, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL)

	PORT_START("UNUSED")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

static const gfx_layout charlayout =
{
	8,8,
	RGN_FRAC(1,1),
	1,
	{ RGN_FRAC(0,1) },
	{ STEP8(0, 8) },
	{ STEP8(0, 1) },
	8*8
};

static GFXDECODE_START( gfx_fp200 )
	GFXDECODE_ENTRY( "chargen", 0, charlayout,     0, 1 )
GFXDECODE_END


void fp200_state::machine_start()
{
}

void fp200_state::machine_reset()
{
}


void fp200_state::palette_init(palette_device &palette) const
{
	palette.set_pen_color(0, 0xa0, 0xa8, 0xa0);
	palette.set_pen_color(1, 0x30, 0x38, 0x10);
}

void fp200_state::sod_w(int state)
{
	m_ioview.select(state);
}

int fp200_state::sid_r()
{
	return (ioport("KEYMOD")->read() >> m_keyb_matrix) & 1;
}

void fp200_state::fp200(machine_config &config)
{
	I8085A(config, m_maincpu, MAIN_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &fp200_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &fp200_state::main_io);
	m_maincpu->in_sid_func().set(FUNC(fp200_state::sid_r));
	m_maincpu->out_sod_func().set(FUNC(fp200_state::sod_w));

	RP5C01(config, "rtc", XTAL(32'768));
	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.set_screen_update(FUNC(fp200_state::screen_update));
	screen.set_size(20*8, 8*8);
	screen.set_visarea(0*8, 20*8-1, 0*8, 8*8-1);
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_fp200);

	PALETTE(config, "palette", FUNC(fp200_state::palette_init), 2);

	// No sound HW
}


/***************************************************************************

  ROM definition(s)

***************************************************************************/

ROM_START( fp200 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "fp200rom.bin", 0x0000, 0x8000, CRC(dba6e41b) SHA1(c694fa19172eb56585a9503997655bcf9d369c34) )

	ROM_REGION( 0x800, "chargen", ROMREGION_ERASE00 )
	ROM_LOAD( "chr.bin", 0x0000, 0x800, CRC(2e6501a5) SHA1(6186e25feabe6db851ee7d61dad11e182a6d3a4a) )
ROM_END

} // anonymous namespace


COMP( 1982, fp200, 0, 0, fp200, fp200, fp200_state, empty_init, "Casio", "FP-200 (Japan)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



fp6000.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Angelo Salese, Dirk Best
/***************************************************************************

    Casio FP-6000

    TODO:
    - Fix cassette (SAVE is at 300 baud Kansas City format, loadable
      on the super80, but LOAD throws RW error).
    - Floppy/HDD
    - Printer
    - gvram color pen is a rather crude guess (the layer is monochrome on
      BASIC?);
    - everything else

****************************************************************************/

#include "emu.h"

#include "cpu/i86/i86.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "video/mc6845.h"
#include "sound/spkrdev.h"
#include "bus/centronics/ctronics.h"
#include "fp6000_kbd.h"
#include "imagedev/cassette.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class fp6000_state : public driver_device
{
public:
	fp6000_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_pic(*this, "pic"),
		m_pit(*this, "pit"),
		m_crtc(*this, "crtc"),
		m_gfxdecode(*this, "gfxdecode"),
		m_palette(*this, "palette"),
		m_speaker(*this, "speaker"),
		m_cassette(*this, "cassette"),
		m_centronics(*this, "centronics"),
		m_gvram(*this, "gvram"),
		m_vram(*this, "vram"),
		m_pcg(*this, "pcg")
	{ }

	void fp6000(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<pic8259_device> m_pic;
	required_device<pit8253_device> m_pit;
	required_device<mc6845_device>m_crtc;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<palette_device> m_palette;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cassette;
	required_device<centronics_device> m_centronics;

	required_shared_ptr<uint16_t> m_gvram;
	required_shared_ptr<uint16_t> m_vram;
	required_shared_ptr<uint16_t> m_pcg;

	void fp6000_io(address_map &map) ATTR_COLD;
	void fp6000_map(address_map &map) ATTR_COLD;

	emu_timer *m_pit_timer = nullptr;
	void pit_timer0_w(int state);
	TIMER_CALLBACK_MEMBER(pit_timer0_clear);
	void pit_timer2_w(int state);

	uint8_t port_08_r();
	void port_08_w(uint8_t data);
	uint8_t port_09_r();
	void port_09_w(uint8_t data);
	void port_0a_w(uint8_t data);
	uint8_t port_0b_r();
	void port_0b_w(uint8_t data);
	uint8_t port_0c_r();
	void port_0c_w(uint8_t data);
	uint8_t port_0d_r();
	void port_0d_w(uint8_t data);
	uint8_t port_0e_r();
	uint8_t port_0f_r();
	void port_0f_w(uint8_t data);

	MC6845_UPDATE_ROW(crtc_update_row);
	uint16_t unk_r();

	void centronics_busy_w(int state) { m_centronics_busy = state; };
	void centronics_fault_w(int state) { m_centronics_fault = state; };
	void centronics_perror_w(int state) { m_centronics_perror = state; };

	uint8_t m_port_0a = 0;

	int m_centronics_busy = 0;
	int m_centronics_fault = 0;
	int m_centronics_perror = 0;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void fp6000_state::fp6000_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0xbffff).ram();
	map(0xc0000, 0xdffff).ram().share("gvram");
	map(0xe0000, 0xe0fff).ram().share("vram");
	map(0xe7000, 0xe7fff).ram().share("pcg");
	map(0xf0000, 0xfffff).rom().region("ipl", 0);
}

void fp6000_state::fp6000_io(address_map &map)
{
	map.unmap_value_high();
	map(0x08, 0x08).r(FUNC(fp6000_state::port_08_r));
	map(0x08, 0x08).w(FUNC(fp6000_state::port_08_w));
	map(0x09, 0x09).r(FUNC(fp6000_state::port_09_r));
	map(0x09, 0x09).w(FUNC(fp6000_state::port_09_w));
	map(0x0a, 0x0a).lr8(NAME([this] () { return ioport("cpudsw")->read(); }));
	map(0x0a, 0x0a).w(FUNC(fp6000_state::port_0a_w));
	map(0x0b, 0x0b).r(FUNC(fp6000_state::port_0b_r));
	map(0x0b, 0x0b).w(FUNC(fp6000_state::port_0b_w));
	map(0x0c, 0x0c).r(FUNC(fp6000_state::port_0c_r));
	map(0x0c, 0x0c).w(FUNC(fp6000_state::port_0c_w));
	map(0x0d, 0x0d).r(FUNC(fp6000_state::port_0d_r));
	map(0x0d, 0x0d).w(FUNC(fp6000_state::port_0d_w));
	map(0x0e, 0x0e).r(FUNC(fp6000_state::port_0e_r));
	map(0x0e, 0x0e).w("centronics_data_out", FUNC(output_latch_device::write));
	map(0x0f, 0x0f).r(FUNC(fp6000_state::port_0f_r));
	map(0x0f, 0x0f).w(FUNC(fp6000_state::port_0f_w));
	// 10-17 floppy?
	map(0x14, 0x14).lr8(NAME([this] () { return ioport("floppydsw")->read(); }));
	map(0x20, 0x23).rw("keyboard", FUNC(fp6000_kbd_device::read), FUNC(fp6000_kbd_device::write)).umask16(0x00ff);
	map(0x30, 0x33).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x38, 0x3f).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	// 50-5f dma?
	map(0x70, 0x70).w(m_crtc, FUNC(mc6845_device::address_w));
	map(0x72, 0x72).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x74, 0x75).r(FUNC(fp6000_state::unk_r)); //bit 6 busy flag
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( fp6000 )
	PORT_START("cpudsw")
	PORT_DIPNAME(0x1f, 0x1e, "Printer type")
	PORT_DIPSETTING(   0x1f, "0")
	PORT_DIPSETTING(   0x1e, "1")
	PORT_DIPSETTING(   0x1d, "2")
	PORT_DIPSETTING(   0x1c, "3")
	PORT_DIPSETTING(   0x1b, "4")
	PORT_DIPSETTING(   0x1a, "5")
	PORT_DIPSETTING(   0x19, "6")
	PORT_DIPSETTING(   0x18, "7")
	PORT_DIPSETTING(   0x17, "8")
	PORT_DIPSETTING(   0x16, "9")
	PORT_DIPSETTING(   0x15, "10")
	PORT_DIPSETTING(   0x14, "11")
	PORT_DIPSETTING(   0x13, "12")
	PORT_DIPSETTING(   0x12, "13")
	PORT_DIPSETTING(   0x11, "14")
	PORT_DIPSETTING(   0x10, "15")
	PORT_DIPSETTING(   0x0f, "16")
	PORT_DIPSETTING(   0x0e, "17")
	PORT_DIPSETTING(   0x0d, "18")
	PORT_DIPSETTING(   0x0c, "19")
	PORT_DIPSETTING(   0x0b, "20")
	PORT_DIPSETTING(   0x0a, "21")
	PORT_DIPSETTING(   0x09, "22")
	PORT_DIPSETTING(   0x08, "23")
	PORT_DIPSETTING(   0x07, "24")
	PORT_DIPSETTING(   0x06, "25")
	PORT_DIPSETTING(   0x05, "26")
	PORT_DIPSETTING(   0x04, "27")
	PORT_DIPSETTING(   0x03, "28")
	PORT_DIPSETTING(   0x02, "29")
	PORT_DIPSETTING(   0x01, "30")
	PORT_DIPSETTING(   0x00, "31")
	PORT_DIPNAME(0xe0, 0x40, "Installed RAM banks")
	PORT_DIPSETTING(   0xe0, "0")
	PORT_DIPSETTING(   0xc0, "1")
	PORT_DIPSETTING(   0xa0, "2")
	PORT_DIPSETTING(   0x80, "3")
	PORT_DIPSETTING(   0x60, "4")
	PORT_DIPSETTING(   0x40, "5")
	PORT_DIPSETTING(   0x20, "6 (INVALID)") // exceeds 768KB limit (writes to gvram et al)
	PORT_DIPSETTING(   0x00, "7 (INVALID)")

	PORT_START("floppydsw")
	PORT_DIPNAME(0x07, 0x07, "Floppy type?")
	PORT_DIPSETTING(   0x07, DEF_STR( None ))
	PORT_DIPSETTING(   0x06, "1")
	PORT_DIPSETTING(   0x05, "2")
	PORT_DIPSETTING(   0x04, "3")
	PORT_DIPSETTING(   0x03, "4")
	PORT_DIPSETTING(   0x02, "5")
	PORT_DIPSETTING(   0x01, "6")
	PORT_DIPSETTING(   0x00, "7")
	PORT_DIPUNKNOWN(0x08, 0x08)
	PORT_DIPUNKNOWN(0x10, 0x10)
	PORT_DIPUNKNOWN(0x20, 0x20)
	PORT_DIPUNKNOWN(0x40, 0x40)
	PORT_DIPUNKNOWN(0x80, 0x80)
INPUT_PORTS_END


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

uint16_t fp6000_state::unk_r()
{
	// 7-------
	// -6------  ?
	// --5-----
	// ---4----
	// ----3---
	// -----2--
	// ------1-  screen lines: 0=200, 1=400
	// -------0

	return 0x40;
}

MC6845_UPDATE_ROW( fp6000_state::crtc_update_row )
{
	pen_t const *const pen = m_palette->pens();
	uint8_t const *const pcg = reinterpret_cast<uint8_t *>(m_pcg.target());
	uint32_t const *const vram = reinterpret_cast<uint32_t *>(m_gvram.target());

	for (int x = 0; x < x_count; x++)
	{
		// text mode
		uint8_t const code = (m_vram[ma + x] >> 0) & 0xff;
		uint8_t const color = (m_vram[ma + x] >> 8) & 0x0f;
		uint8_t gfx = pcg[(code << 4) | ra];

		// cursor?
		if (x == cursor_x)
			gfx = 0xff;

		// draw 8 pixels of the character
		for (int i = 0; i < 8; i++)
			bitmap.pix(y, x * 8 + i) = BIT(gfx, 7 - i) ? pen[color] : 0;

		// graphics
		uint32_t const data = vram[(ma << 3) + (ra * x_count) + x];

		// draw 8 gfx pixels
		if ((data >> 12) & 0x0f) bitmap.pix(y, x * 8 + 0) = pen[(data >> 12) & 0x0f];
		if ((data >>  8) & 0x0f) bitmap.pix(y, x * 8 + 1) = pen[(data >>  8) & 0x0f];
		if ((data >>  4) & 0x0f) bitmap.pix(y, x * 8 + 2) = pen[(data >>  4) & 0x0f];
		if ((data >>  0) & 0x0f) bitmap.pix(y, x * 8 + 3) = pen[(data >>  0) & 0x0f];
		if ((data >> 28) & 0x0f) bitmap.pix(y, x * 8 + 4) = pen[(data >> 28) & 0x0f];
		if ((data >> 24) & 0x0f) bitmap.pix(y, x * 8 + 5) = pen[(data >> 24) & 0x0f];
		if ((data >> 20) & 0x0f) bitmap.pix(y, x * 8 + 6) = pen[(data >> 20) & 0x0f];
		if ((data >> 16) & 0x0f) bitmap.pix(y, x * 8 + 7) = pen[(data >> 16) & 0x0f];
	}
}

static const gfx_layout charlayout =
{
	8, 16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP8(0,1) },
	{ STEP16(0,8) },
	8*16
};

static GFXDECODE_START( gfx )
	GFXDECODE_RAM("pcg", 0, charlayout, 0, 1)
GFXDECODE_END


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

// 7-------  option rom available (1=no)
// -654----  unknown
// ----3---  cassette in
// -----21-  unknown
// -------0  cassette motor

uint8_t fp6000_state::port_08_r()
{
	uint8_t data = 0;

	data |= 0x80; // no option rom
	data |= (m_cassette->input() > 0 ? 0x00 : 0x08);

	return data;
}

void fp6000_state::port_08_w(uint8_t data)
{
	logerror("port_08 write %02x\n", data);

	m_cassette->change_state(BIT(data, 0) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
}

uint8_t fp6000_state::port_09_r()
{
	logerror("port_09 read\n");
	return 0xff;
}

void fp6000_state::port_09_w(uint8_t data)
{
	logerror("port_09 write %02x\n", data);
}

void fp6000_state::port_0a_w(uint8_t data)
{
	// 7-------  speaker/cassette output select
	// -6543210  unknown

	logerror("port_0a write %02x\n", data);

	m_port_0a = data;
}

uint8_t fp6000_state::port_0b_r()
{
	logerror("port_0b read\n");
	return 0xff;
}

void fp6000_state::port_0b_w(uint8_t data)
{
	// printer control?
	logerror("port_0b write %02x\n", data);
	m_pic->ir7_w(1); // ?
}

uint8_t fp6000_state::port_0c_r()
{
	logerror("port_0c read\n");
	return 0xff;
}

void fp6000_state::port_0c_w(uint8_t data)
{
	// 7-------  unknown
	// -6------  pit timer2 gate?
	// --543210  unknown

	logerror("port_0c write %02x\n", data);
}

uint8_t fp6000_state::port_0d_r()
{
	logerror("port_0d read\n");
	return 0xff;
}

void fp6000_state::port_0d_w(uint8_t data)
{
	// after writing printer data
	logerror("port_0d write %02x\n", data);

	// ?
	m_centronics->write_strobe(1);
	m_centronics->write_strobe(0);
}

uint8_t fp6000_state::port_0e_r()
{
	uint8_t data = 0;

	// 765-----  unknown
	// ---4321-  printer status lines
	// -------0  printer busy

	logerror("port_0e read\n");

	data |= m_centronics_perror << 2; // guess
	data |= m_centronics_fault << 1; // guess
	data |= m_centronics_busy << 0;

	return data;
}

uint8_t fp6000_state::port_0f_r()
{
	// read at end of timer interrupt routine, result discarded
	return 0xff;
}

void fp6000_state::port_0f_w(uint8_t data)
{
	logerror("port_0f write %02x\n", data);
	m_pic->ir7_w(0); // ?
}

void fp6000_state::pit_timer0_w(int state)
{
	// work around pit issue, it issues set and clear at the same time,
	// leaving the pic no time to react
	if (state)
		m_pic->ir0_w(1);
	else
		m_pit_timer->adjust(attotime::from_hz(100000)); // timing?
}

TIMER_CALLBACK_MEMBER(fp6000_state::pit_timer0_clear)
{
	m_pic->ir0_w(0);
}

void fp6000_state::pit_timer2_w(int state)
{
	if (BIT(m_port_0a, 7))
		m_speaker->level_w(state);
	else
		m_cassette->output(state ? -1.0 : +1.0);
}

void fp6000_state::machine_start()
{
	m_pit_timer = timer_alloc(FUNC(fp6000_state::pit_timer0_clear), this);
}

void fp6000_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void fp6000_state::fp6000(machine_config &config)
{
	I8086(config, m_maincpu, 16000000 / 2); // 8 Mhz?
	m_maincpu->set_addrmap(AS_PROGRAM, &fp6000_state::fp6000_map);
	m_maincpu->set_addrmap(AS_IO, &fp6000_state::fp6000_io);
	m_maincpu->set_irq_acknowledge_callback(m_pic, FUNC(pic8259_device::inta_cb));

	PIC8259(config, m_pic, 0);
	m_pic->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(16000000 / 16); // 1 MHz
	m_pit->out_handler<0>().set(FUNC(fp6000_state::pit_timer0_w)).invert();
	m_pit->set_clk<2>(16000000 / 8); // 2 MHz?
	m_pit->out_handler<2>().set(FUNC(fp6000_state::pit_timer2_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(16000000, 1024, 0, 640, 272, 0, 200); // 16 MHz?
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	MC6845(config, m_crtc, 16000000 / 8); // unknown variant, 2 MHz?
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(fp6000_state::crtc_update_row));

	PALETTE(config, m_palette).set_entries(16);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx);

	// audio hardware
	SPEAKER(config, "mono").front_center();

	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	// keyboard
	fp6000_kbd_device &keyboard(FP6000_KBD(config, "keyboard"));
	keyboard.int_handler().set(m_pic, FUNC(pic8259_device::ir1_w));

	// cassette
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	// centronics printer
	output_latch_device &centronics_data_out(OUTPUT_LATCH(config, "centronics_data_out"));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->set_output_latch(centronics_data_out);
	m_centronics->ack_handler().set(m_pic, FUNC(pic8259_device::ir7_w)).invert();
	m_centronics->busy_handler().set(FUNC(fp6000_state::centronics_busy_w));
	m_centronics->fault_handler().set(FUNC(fp6000_state::centronics_fault_w));
	m_centronics->perror_handler().set(FUNC(fp6000_state::centronics_perror_w));
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( fp6000 )
	ROM_REGION16_LE(0x10000, "ipl", 0)
	ROM_LOAD("ipl.rom", 0x0000, 0x10000, CRC(c72fe40a) SHA1(0e4c60dc27f6c7f461c4bc382b81602b3327a7a4))

	ROM_REGION(0x1000, "mcu", 0)
	ROM_LOAD("mcu", 0x0000, 0x1000, NO_DUMP) // unknown MCU type
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS        INIT         COMPANY  FULLNAME   FLAGS
COMP( 1985, fp6000, 0,      0,      fp6000,  fp6000, fp6000_state, empty_init, "Casio", "FP-6000", MACHINE_NOT_WORKING )
// Reportedly released as FP-6000 Jr in Scandinavia



freedom120.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/****************************************************************************

    Liberty Electronics Freedom 120/Aspect 100

    Serial terminal

    Hardware:
    - Z0840006PSC Z80 CPU
    - XTAL 32 Mhz and 48 MHz
    - MC2674B4P
    - 27C256 labeled "G212011 6A22"
    - M27C512 labeled "M2ASP11 C368"
    - KM6264BL-7L x2 (next to ROM)
    - MS6264L-70PC
    - V61C16P70L x2
    - SCN2681AC1N40
    - XTAL 3.6864 MHz
    - PAL labeled "10A8"
    - PAL labeled "PL10251"
    - PAL labeled "PL10151"
    - Motorola IC "LS38BC712PP01"
    - Battery

    External:
    - Serial port labeled "Main Port"
    - Parallel port labeled "Parallel Port"
    - Keyboard port

    Status: Skeleton driver

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "emupal.h"
#include "screen.h"


namespace {


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class freedom120_state : public driver_device
{
public:
	freedom120_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu")
	{ }

	void freedom120(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void freedom120_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).rom();
}

void freedom120_state::io_map(address_map &map)
{
}


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

static const gfx_layout char_layout =
{
	8, 16,
	RGN_FRAC(1, 1),
	1,
	{ 0 },
	{ STEP8(0, 1) },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8 * 16
};

static GFXDECODE_START(chars)
	GFXDECODE_ENTRY("chargen", 0, char_layout, 0, 1)
GFXDECODE_END


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void freedom120_state::machine_start()
{
}

void freedom120_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void freedom120_state::freedom120(machine_config &config)
{
	Z80(config, m_maincpu, 48_MHz_XTAL / 8); // unknown clock
	m_maincpu->set_addrmap(AS_PROGRAM, &freedom120_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &freedom120_state::io_map);

	PALETTE(config, "palette", palette_device::MONOCHROME);

	GFXDECODE(config, "gfxdecode", "palette", chars);
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( free120 )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD("m2asp11.u241", 0x0000, 0x10000, CRC(c079f471) SHA1(4864660c9a4470a4a444943fe89d91dc44297c39))

	ROM_REGION(0x8000, "chargen", 0)
	ROM_LOAD("g212011.u242", 0x0000, 0x8000, CRC(18e6700f) SHA1(fdf22d11468f978661005be115ec0bcc043519fc))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME     PARENT  COMPAT  MACHINE     INPUT  CLASS             INIT        COMPANY                FULLNAME                  FLAGS
COMP( 1993, free120, 0,      0,      freedom120, 0,     freedom120_state, empty_init, "Liberty Electronics", "Freedom 120/Aspect 100", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



freedom200.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/****************************************************************************

    Liberty Electronics Freedom 200 Video Display Terminal
    Serial terminal

    Liberty Electronics Freedom 220 Video Display Terminal
    VT220 compatible serial terminal

    Hardware:
    - Z8400A Z80A
    - 4 MHz XTAL (next to CPU)
    - 3x 2764 (next to CPU)
    - 6x TMM2016AP-10 or D446C-2 (2k)
    - SCN2674B C4N40
    - SCB2675B C5N40
    - 2x 2732A (next to CRT controller)
    - 2x S68B10P (128 byte SRAM)
    - M5L8253P-5
    - 3x D8251AC
    - 18.432 MHz XTAL

    External:
    - DB25 connector "Main Port"
    - DB25 connector "Auxialiary Port"
    - Keyboard connector
    - Expansion slot

    TODO:
    - Light/dark background
    - Soft scroll
    - Pixel clock, characters should be 9 pixels?
    - I/O write to 0xc0

    Notes:
    - Use Set-Up for status line setup, Shift+Set-Up for fullscreen setup
    - On first boot you will get an "error 8" - this is because
      RAM is uninitialized.

****************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/i8251.h"
#include "machine/input_merger.h"
#include "machine/nvram.h"
#include "machine/pit8253.h"
#include "video/scn2674.h"

#include "freedom220_kbd.h"

#include "emupal.h"
#include "screen.h"


namespace {


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class freedom200_state : public driver_device
{
public:
	freedom200_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_avdc(*this, "avdc"),
		m_screen(*this, "screen"),
		m_palette(*this, "palette"),
		m_usart(*this, "usart%u", 0U),
		m_chargen(*this, "chargen"),
		m_translate(*this, "translate"),
		m_charram(*this, "charram%u", 0U),
		m_attrram(*this, "attrram%u", 0U),
		m_video_ctrl(0x00)
	{ }

	void freedom200(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;
	required_device<scn2674_device> m_avdc;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device_array<i8251_device, 3> m_usart;
	required_region_ptr<uint8_t> m_chargen;
	required_region_ptr<uint8_t> m_translate;
	required_shared_ptr_array<uint8_t, 2> m_charram;
	required_shared_ptr_array<uint8_t, 2> m_attrram;

	uint8_t m_video_ctrl;

	// double width support
	bool m_dw_active;
	uint8_t m_dw_char;
	uint8_t m_dw_attr;
	bool m_dw_ul;
	bool m_dw_blink;
	bool m_dw_cursor;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	void char_map(address_map &map) ATTR_COLD;
	void attr_map(address_map &map) ATTR_COLD;
	void avdc_intr_w(int state);
	void video_ctrl_w(uint8_t data);
	SCN2674_DRAW_CHARACTER_MEMBER(draw_character);
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void freedom200_state::mem_map(address_map &map)
{
	map(0x0000, 0x5fff).rom();
	map(0x8000, 0x87ff).ram().share(m_charram[0]);
	map(0x8800, 0x8fff).ram().share(m_attrram[0]);
	map(0x9000, 0x97ff).ram().share(m_charram[1]);
	map(0x9800, 0x9fff).ram().share(m_attrram[1]);
	map(0xa000, 0xa7ff).ram().share("nvram");
	map(0xa800, 0xafff).ram().share("workram");
}

void freedom200_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x07).rw(m_avdc, FUNC(scn2674_device::read), FUNC(scn2674_device::write));
	map(0x20, 0x23).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x40, 0x41).rw(m_usart[0], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x60, 0x61).rw(m_usart[1], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x80, 0x81).rw(m_usart[2], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xa0, 0xa0).w(FUNC(freedom200_state::video_ctrl_w));
	// c0 - used by free200 only
}


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

void freedom200_state::char_map(address_map &map)
{
	map(0x0000, 0x07ff).ram().share(m_charram[0]);
	map(0x1000, 0x17ff).ram().share(m_charram[1]);
	map(0x2000, 0x27ff).ram().share("nvram");
	map(0x2800, 0x2fff).ram().share("workram");
}

void freedom200_state::attr_map(address_map &map)
{
	map(0x0000, 0x07ff).ram().share(m_attrram[0]);
	map(0x1000, 0x17ff).ram().share(m_attrram[1]);
	map(0x2000, 0x27ff).ram().share("workram");
	map(0x2800, 0x2fff).ram().share("workram");
}

void freedom200_state::avdc_intr_w(int state)
{
	if (state)
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}

void freedom200_state::video_ctrl_w(uint8_t data)
{
	// 7-------  unknown
	// -6------  unknown
	// --5-----  unknown
	// ---432--  translation table bit 210
	// ------1-  normal/reverse video
	// -------0  translation table bit 3

	logerror("video_ctrl_w: %02x\n", data);

	m_video_ctrl = data;
}

SCN2674_DRAW_CHARACTER_MEMBER( freedom200_state::draw_character )
{
	// 765-----  unknown
	// ---4----  normal/bold
	// ----3---  underline
	// -----2--  invert
	// ------1-  blink
	// -------0  invisible

	const pen_t *const pen = m_palette->pens();

	// either save or restore attributes for double-width
	if (dw)
	{
		if (m_dw_active)
		{
			charcode = m_dw_char;
			attrcode = m_dw_attr;
			ul = m_dw_ul;
			blink = m_dw_blink;
			cursor = m_dw_cursor;
		}
		else
		{
			m_dw_char = charcode;
			m_dw_attr = attrcode;
			m_dw_ul = ul;
			m_dw_blink = blink;
			m_dw_cursor = cursor;
		}
	}

	// apply translation table
	const int table = bitswap<4>(m_video_ctrl, 0, 4, 3, 2);
	charcode = m_translate[(table << 8) | charcode];

	uint8_t data = m_chargen[charcode << 4 | linecount];

	if (ul && (BIT(attrcode, 3)))
		data = 0xff;

	if (blink && (BIT(attrcode, 1)))
		data = 0x00;

	if (BIT(attrcode, 0))
		data = 0x00;

	if (BIT(attrcode, 2))
		data = ~data;

	if (cursor)
		data = ~data;

	// foreground/background colors
	rgb_t fg = BIT(attrcode, 4) ? pen[1] : pen[2];
	rgb_t bg = pen[0];

	// reverse video?
	if (BIT(m_video_ctrl, 1))
	{
		using std::swap;
		swap(fg, bg);
	}

	// draw 8 pixels of the character
	if (dw)
	{
		// first or second half of char
		int b = m_dw_active ? 3 : 7;

		for (int i = 0; i < 4; i++)
		{
			bitmap.pix(y, x + i * 2 + 0) = BIT(data, b - i) ? fg : bg;
			bitmap.pix(y, x + i * 2 + 1) = BIT(data, b - i) ? fg : bg;
		}

		m_dw_active = !m_dw_active;
	}
	else
	{
		for (int i = 0; i < 8; i++)
			bitmap.pix(y, x + i) = BIT(data, 7 - i) ? fg : bg;
	}
}

static const gfx_layout char_layout =
{
	8, 12,
	RGN_FRAC(1, 1),
	1,
	{ 0 },
	{ STEP8(0, 1) },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8 * 16
};

static GFXDECODE_START(chars)
	GFXDECODE_ENTRY("chargen", 0, char_layout, 0, 1)
GFXDECODE_END


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void freedom200_state::machine_start()
{
	// register for save states
	save_item(NAME(m_video_ctrl));
	save_item(NAME(m_dw_active));
	save_item(NAME(m_dw_char));
	save_item(NAME(m_dw_attr));
	save_item(NAME(m_dw_ul));
	save_item(NAME(m_dw_blink));
	save_item(NAME(m_dw_cursor));
}

void freedom200_state::machine_reset()
{
	m_dw_active = false;
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void freedom200_state::freedom200(machine_config &config)
{
	Z80(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &freedom200_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &freedom200_state::io_map);

	input_merger_device &irq(INPUT_MERGER_ANY_HIGH(config, "irq"));
	irq.output_handler().set_inputline("maincpu", INPUT_LINE_IRQ0);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_color(rgb_t::green());
	m_screen->set_raw(16000000, 768, 0, 640, 321, 0, 300); // clock unverified
	m_screen->set_screen_update(m_avdc, FUNC(scn2674_device::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	GFXDECODE(config, "gfxdecode", m_palette, chars);

	SCN2674(config, m_avdc, 16000000 / 8); // clock unverified
	m_avdc->intr_callback().set(FUNC(freedom200_state::avdc_intr_w));
	m_avdc->set_screen(m_screen);
	m_avdc->set_character_width(8); // unverified
	m_avdc->set_addrmap(0, &freedom200_state::char_map);
	m_avdc->set_addrmap(1, &freedom200_state::attr_map);
	m_avdc->set_display_callback(FUNC(freedom200_state::draw_character));

	pit8253_device &pit(PIT8253(config, "pit", 0));
	pit.set_clk<0>(18.432_MHz_XTAL / 10);
	pit.set_clk<1>(18.432_MHz_XTAL / 10);
	pit.set_clk<2>(18.432_MHz_XTAL / 10);
	pit.out_handler<0>().set(m_usart[2], FUNC(i8251_device::write_txc));
	pit.out_handler<0>().append(m_usart[2], FUNC(i8251_device::write_rxc));
	pit.out_handler<1>().set(m_usart[1], FUNC(i8251_device::write_txc));
	pit.out_handler<1>().append(m_usart[1], FUNC(i8251_device::write_rxc));
	pit.out_handler<2>().set(m_usart[0], FUNC(i8251_device::write_txc));
	pit.out_handler<2>().append(m_usart[0], FUNC(i8251_device::write_rxc));

	I8251(config, m_usart[0], 0); // unknown clock
	m_usart[0]->rxrdy_handler().set("irq", FUNC(input_merger_device::in_w<0>));
	m_usart[0]->txrdy_handler().set("irq", FUNC(input_merger_device::in_w<1>));
	m_usart[0]->txd_handler().set("mainport", FUNC(rs232_port_device::write_txd));
	m_usart[0]->rts_handler().set("mainport", FUNC(rs232_port_device::write_rts));

	I8251(config, m_usart[1], 0); // unknown clock
	m_usart[1]->rxrdy_handler().set("irq", FUNC(input_merger_device::in_w<2>));
	m_usart[1]->txrdy_handler().set("irq", FUNC(input_merger_device::in_w<3>));
	m_usart[1]->txd_handler().set("auxport", FUNC(rs232_port_device::write_txd));
	m_usart[1]->rts_handler().set("auxport", FUNC(rs232_port_device::write_rts));

	I8251(config, m_usart[2], 0); // unknown clock
	m_usart[2]->rxrdy_handler().set("irq", FUNC(input_merger_device::in_w<4>));
	m_usart[2]->txd_handler().set("kbd", FUNC(freedom220_kbd_device::rxd_w));

	rs232_port_device &mainport(RS232_PORT(config, "mainport", default_rs232_devices, nullptr));
	mainport.rxd_handler().set(m_usart[0], FUNC(i8251_device::write_rxd));
	mainport.cts_handler().set(m_usart[0], FUNC(i8251_device::write_cts));

	rs232_port_device &auxport(RS232_PORT(config, "auxport", default_rs232_devices, nullptr));
	auxport.rxd_handler().set(m_usart[1], FUNC(i8251_device::write_rxd));
	auxport.cts_handler().set(m_usart[1], FUNC(i8251_device::write_cts));

	freedom220_kbd_device &kbd(FREEDOM220_KBD(config, "kbd"));
	kbd.txd_cb().set(m_usart[2], FUNC(i8251_device::write_rxd));
	kbd.cts_cb().set(m_usart[2], FUNC(i8251_device::write_cts));
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( free200 )
	ROM_REGION(0x6000, "maincpu", 0)
	ROM_LOAD("m120020.ic213", 0x0000, 0x2000, CRC(869de37e) SHA1(22f9c847aa8d99c22791df7a40a7c1d67f21516b))
	ROM_LOAD("m220020.ic212", 0x2000, 0x2000, CRC(85095c20) SHA1(e7515c8b188732e391015f20ad38207876850589))
	ROM_LOAD("m320020.ic214", 0x4000, 0x2000, CRC(827cafe6) SHA1(887d78bfbfcdc07efc35a7c560822b1386300632))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("g020010.bin", 0x0000, 0x1000, CRC(b99bf3b1) SHA1(799395694d2f88c68230ce7f6bf5453b799926f4))

	ROM_REGION(0x1000, "translate", 0)
	ROM_LOAD("t020010.bin", 0x0000, 0x1000, CRC(19f9c677) SHA1(903eb82b2a7b63d79c00085c8c7ec2cb9583e2a0))
ROM_END

ROM_START( free220 )
	ROM_REGION(0x6000, "maincpu", 0)
	ROM_LOAD("m122010__8cdd.ic213", 0x0000, 0x2000, CRC(a1181809) SHA1(0ec0fd30c8a55f0bb9e1c6453120ab9a696f9041))
	ROM_LOAD("m222010__04c8.ic212", 0x2000, 0x2000, CRC(ddd1e5eb) SHA1(3e3998035721050cd2019474343f072dade6589d))
	ROM_LOAD("m322010__8121.ic214", 0x4000, 0x2000, CRC(eeaa4b44) SHA1(93402e00205d7220f5e248a902ed92de4bbe6dd8))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("g022010__d64e.bin", 0x0000, 0x1000, CRC(a4482adc) SHA1(98479f6396743da6cf23909ff5a0097e9f021e3b))

	ROM_REGION(0x1000, "translate", 0)
	ROM_LOAD("t022010__61f0.bin", 0x0000, 0x1000, CRC(00461116) SHA1(79a53a557ea4386b3e85a312731c6c0763ab46cc))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME     PARENT  COMPAT  MACHINE     INPUT  CLASS             INIT        COMPANY                FULLNAME       FLAGS
COMP( 1983, free200, 0,      0,      freedom200, 0,     freedom200_state, empty_init, "Liberty Electronics", "Freedom 200", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
COMP( 1984, free220, 0,      0,      freedom200, 0,     freedom200_state, empty_init, "Liberty Electronics", "Freedom 220", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )



fs3216.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Fortune 32:16.

    Also known as the Micromega 32 in France (distributed by Thomson).

****************************************************************************/

#include "emu.h"
//#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
#include "cpu/8x300/8x300.h"
#include "imagedev/floppy.h"
#include "machine/bankdev.h"
//#include "machine/com8116.h"
#include "machine/upd765.h"
#include "machine/x2212.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "video/mc6845.h"
#include "screen.h"


namespace {

class fs3216_state : public driver_device
{
public:
	fs3216_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_clb(*this, "clb")
		, m_ctc(*this, "ctc")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0U)
		, m_earom(*this, "earom")
		, m_vecprom(*this, "vecprom")
		, m_videoram(*this, "videoram")
		, m_chargen(*this, "chargen")
	{
	}

	void fs3216(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	MC6845_UPDATE_ROW(crt_update_row);

	void mmu_reg_w(offs_t offset, u16 data);
	u16 mmu_read(offs_t offset, u16 mem_mask);
	void mmu_write(offs_t offset, u16 data, u16 mem_mask);
	void mmu_reset_w(int state);
	void mmu_init_w(u16 data);

	u16 irq_r();
	u8 intack_r(offs_t offset);

	u8 ctc_r(offs_t offset);
	void ctc_w(offs_t offset, u8 data);
	u16 earom_recall_r();
	u16 earom_store_r();

	void fdc_int_w(int state);
	void fdc_drq_w(int state);
	void fdc_hdl_w(int state);
	void floppy_idx_w(int state);
	void fdc_us_w(u8 data);
	u16 floppy_select_r(offs_t offset);
	void floppy_select_w(offs_t offset, u16 data);
	void floppy_control_w(u8 data);
	u8 floppy_status_r();
	TIMER_CALLBACK_MEMBER(fdc_dma);
	u8 fdc_ram_r(offs_t offset);
	void fdc_ram_w(offs_t offset, u8 data);

	void main_map(address_map &map) ATTR_COLD;
	void clb_map(address_map &map) ATTR_COLD;
	void fc7_map(address_map &map) ATTR_COLD;
	void wdcpu_prog_map(address_map &map) ATTR_COLD;
	void wdcpu_bank_map(address_map &map) ATTR_COLD;

	required_device<m68000_device> m_maincpu;
	required_device<address_map_bank_device> m_clb;
	required_device<z80ctc_device> m_ctc;
	required_device<upd765a_device> m_fdc;
	required_device_array<floppy_connector, 4> m_floppy;
	required_device<x2212_device> m_earom;
	required_region_ptr<u8> m_vecprom;

	required_shared_ptr<u16> m_videoram;
	required_region_ptr<u8> m_chargen;

	u32 m_mmu_reg[4]{};
	bool m_from_reset = false;

	u8 m_floppy_status = 0;
	u8 m_floppy_control = 0;
	u8 m_floppy_select = 0;
	u8 m_fdc_select = 0;
	u16 m_fdc_dma_count = 0;
	emu_timer *m_fdc_dma_timer = nullptr;
	std::unique_ptr<u8[]> m_fdc_ram;
};


void fs3216_state::machine_start()
{
	m_fdc_ram = make_unique_clear<u8[]>(0x800);
	save_pointer(NAME(m_fdc_ram), 0x800);

	std::fill(std::begin(m_mmu_reg), std::end(m_mmu_reg), 0);

	m_floppy_status = 0x80;
	m_floppy_control = 0;
	m_floppy_select = 0;
	m_fdc_select = 0;
	m_fdc_dma_count = 0;

	m_fdc_dma_timer = timer_alloc(FUNC(fs3216_state::fdc_dma), this);

	save_item(NAME(m_mmu_reg));
	save_item(NAME(m_from_reset));
	save_item(NAME(m_floppy_status));
	save_item(NAME(m_floppy_control));
	save_item(NAME(m_floppy_select));
	save_item(NAME(m_fdc_select));
	save_item(NAME(m_fdc_dma_count));
}

void fs3216_state::machine_reset()
{
	m_from_reset = true;

	floppy_control_w(0);
	floppy_select_w(0, 0);
}


MC6845_UPDATE_ROW(fs3216_state::crt_update_row)
{
	u32 *px = &bitmap.pix(y);

	for (int i = 0; i < x_count; i++)
	{
		u16 chr = m_videoram[(ma + i) & 0x7ff];
		rgb_t fg = BIT(chr, 13) ? rgb_t::white() : rgb_t(0xc0, 0xc0, 0xc0);
		rgb_t bg = rgb_t::black();

		u16 dots = m_chargen[(chr & 0xff) << 4 | ra] << 1;
		if (ra == 9 && BIT(chr, 12))
			dots = 0x1ff;

		for (int n = 9; n > 0; n--, dots <<= 1)
			*px++ = BIT(dots, 8) ? fg : bg;
	}
}


void fs3216_state::mmu_reg_w(offs_t offset, u16 data)
{
	// 3x SN74LS374N for each of the four spaces
	u32 &reg = m_mmu_reg[offset >> 1];
	if (BIT(offset, 0))
		reg = (reg & 0xff0000) | data;
	else
		reg = (reg & 0x00ffff) | (data & 0x00ff) << 16;
}

u16 fs3216_state::mmu_read(offs_t offset, u16 mem_mask)
{
	const bool a23 = BIT(offset, 22);
	const bool mmu_disable = !a23 && BIT(m_maincpu->get_fc(), 2);
	const u32 mmu_reg = mmu_disable ? (m_from_reset ? 0xfffe00 : 0xfff000) : m_mmu_reg[(offset >> 20) & 3];

	if (!mmu_disable && !machine().side_effects_disabled())
	{
		// TODO: do limit check and cause BERR on failure
	}

	offs_t clbaddr = offset + ((mmu_reg & 0x000fff) << 9);
	clbaddr = (clbaddr & 0x1fffff) | (clbaddr & 0x100000) << 1;
	return m_clb->read16(clbaddr, mem_mask);
}

void fs3216_state::mmu_write(offs_t offset, u16 data, u16 mem_mask)
{
	const bool a23 = BIT(offset, 22);
	const bool mmu_disable = !a23 && BIT(m_maincpu->get_fc(), 2);
	const u32 mmu_reg = mmu_disable ? (m_from_reset ? 0xfffe00 : 0xfff000) : m_mmu_reg[(offset >> 20) & 3];

	if (!mmu_disable && !machine().side_effects_disabled())
	{
		// TODO: do limit/write protect check and cause BERR on failure
	}

	offs_t clbaddr = offset + ((mmu_reg & 0x000fff) << 9);
	clbaddr = (clbaddr & 0x1fffff) | (clbaddr & 0x100000) << 1;
	m_clb->write16(clbaddr, data, mem_mask);
}

void fs3216_state::mmu_reset_w(int state)
{
	if (state)
		m_from_reset = true;
}

void fs3216_state::mmu_init_w(u16 data)
{
	m_from_reset = BIT(data, 0);
}

u16 fs3216_state::irq_r()
{
	// TODO
	return 0xfff8;
}

u8 fs3216_state::intack_r(offs_t offset)
{
	// FIXME: all interrupts are vectored, but not all levels go through this PROM
	return m_vecprom[offset];
}

u8 fs3216_state::ctc_r(offs_t offset)
{
	return m_ctc->read(offset >> 1);
}

void fs3216_state::ctc_w(offs_t offset, u8 data)
{
	m_ctc->write(offset >> 1, data);
}

u16 fs3216_state::earom_recall_r()
{
	if (!machine().side_effects_disabled())
	{
		m_earom->recall(1);
		m_earom->recall(0);
	}
	return 0xffff;
}

u16 fs3216_state::earom_store_r()
{
	if (!machine().side_effects_disabled())
	{
		m_earom->store(1);
		m_earom->store(0);
	}
	return 0xffff;
}

void fs3216_state::fdc_int_w(int state)
{
	if (state)
		m_floppy_status |= 0x02;
	else
		m_floppy_status &= 0xfd;
}

void fs3216_state::fdc_drq_w(int state)
{
	if (state)
	{
		m_floppy_status |= 0x01;
		if (BIT(m_floppy_control, 3) && !m_fdc_dma_timer->enabled())
		{
			m_fdc_dma_timer->adjust(attotime::from_hz(16_MHz_XTAL / 64));
			m_floppy_status |= 0x04;
		}
	}
	else
	{
		m_floppy_status &= 0xfa;
		m_fdc_dma_timer->adjust(attotime::never);
	}
}

void fs3216_state::fdc_hdl_w(int state)
{
	if (state)
		m_floppy_status |= 0x40;
	else
		m_floppy_status &= 0xbf;
}

void fs3216_state::floppy_idx_w(int state)
{
	if (state)
		m_floppy_status |= 0x20;
	else
		m_floppy_status &= 0xdf;

	if (BIT(m_floppy_select, 2))
		m_fdc->ready_w(state);
}

void fs3216_state::fdc_us_w(u8 data)
{
	m_fdc_select = data;
	if (!BIT(m_floppy_select, 2))
		m_fdc->set_floppy(m_floppy[m_fdc_select]->get_device());
}

u16 fs3216_state::floppy_select_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		floppy_select_w(offset, 0);
	return 0xffff;
}

void fs3216_state::floppy_select_w(offs_t offset, u16 data)
{
	if (m_floppy_select == offset)
		return;

	m_floppy_select = offset;
	if (BIT(offset, 2))
		m_fdc->set_floppy(m_floppy[offset & 3]->get_device());
	else
		m_fdc->set_floppy(m_floppy[m_fdc_select]->get_device());
}

void fs3216_state::floppy_control_w(u8 data)
{
	m_floppy_control = data;

	floppy_image_device *fd = m_floppy[BIT(m_floppy_select, 2) ? (m_floppy_select & 3) : m_fdc_select]->get_device();
	if (BIT(data, 5))
	{
		if (fd != nullptr)
			fd->mon_w(0);
		m_floppy_status |= 0x10;
	}
	else
	{
		if (fd != nullptr)
			fd->mon_w(1);
		m_floppy_status &= 0xef;
	}

	m_fdc->reset_w(!BIT(data, 1));
	if (!BIT(data, 1))
	{
		m_fdc_dma_count = 0;
		m_fdc->tc_w(0);
	}

	if (BIT(data, 3) && BIT(m_floppy_status, 0))
	{
		if (!m_fdc_dma_timer->enabled())
		{
			m_fdc_dma_timer->adjust(attotime::from_hz(16_MHz_XTAL / 64));
			m_floppy_status |= 0x04;
		}
	}
	else
	{
		m_fdc_dma_timer->adjust(attotime::never);
		m_floppy_status &= 0xfb;
	}

	m_fdc->set_unscaled_clock(16_MHz_XTAL / (BIT(data, 0) ? 4 : 2));
}

u8 fs3216_state::floppy_status_r()
{
	return m_floppy_status;
}

TIMER_CALLBACK_MEMBER(fs3216_state::fdc_dma)
{
	if (BIT(m_floppy_control, 4))
		m_fdc_ram[m_fdc_dma_count & 0x7ff] = m_fdc->dma_r();
	else
		m_fdc->dma_w(m_fdc_ram[m_fdc_dma_count & 0x7ff]);
	m_floppy_status &= 0xfb;

	++m_fdc_dma_count;
	if (BIT(m_fdc_dma_count, 11))
		m_fdc->tc_w(1);
}

u8 fs3216_state::fdc_ram_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
	{
		m_fdc_dma_count = offset;
		m_fdc->tc_w(BIT(offset, 11));
	}
	return m_fdc_ram[offset & 0x7ff];
}

void fs3216_state::fdc_ram_w(offs_t offset, u8 data)
{
	if (!machine().side_effects_disabled())
	{
		m_fdc_dma_count = offset;
		m_fdc->tc_w(BIT(offset, 11));
	}
	m_fdc_ram[offset & 0x7ff] = data;
}


void fs3216_state::main_map(address_map &map)
{
	map(0x000000, 0xffffff).rw(FUNC(fs3216_state::mmu_read), FUNC(fs3216_state::mmu_write));
}

void fs3216_state::clb_map(address_map &map)
{
	map(0x000000, 0x017fff).ram();
	map(0x780000, 0x783fff).rom().region("momrom", 0);
	map(0x792000, 0x792003).m(m_fdc, FUNC(upd765a_device::map)).umask16(0x00ff);
	map(0x792010, 0x79201f).rw(FUNC(fs3216_state::floppy_select_r), FUNC(fs3216_state::floppy_select_w));
	map(0x792041, 0x792041).w(FUNC(fs3216_state::floppy_control_w));
	map(0x792051, 0x792051).r(FUNC(fs3216_state::floppy_status_r));
	map(0x794680, 0x79468f).rw(FUNC(fs3216_state::ctc_r), FUNC(fs3216_state::ctc_w)).umask16(0x00ff);
	map(0x794701, 0x794701).rw("dart", FUNC(z80dart_device::da_r), FUNC(z80dart_device::da_w));
	map(0x794709, 0x794709).rw("dart", FUNC(z80dart_device::ca_r), FUNC(z80dart_device::ca_w));
	map(0x794711, 0x794711).rw("dart", FUNC(z80dart_device::db_r), FUNC(z80dart_device::db_w));
	map(0x794719, 0x794719).rw("dart", FUNC(z80dart_device::cb_r), FUNC(z80dart_device::cb_w));
	map(0x796000, 0x797fff).rw(FUNC(fs3216_state::fdc_ram_r), FUNC(fs3216_state::fdc_ram_w)).umask16(0x00ff);
	map(0x7a0000, 0x7a1fff).rom().region("video", 0);
	map(0x7a4001, 0x7a4001).w("crtc", FUNC(mc6845_device::address_w));
	map(0x7a4003, 0x7a4003).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x7a8000, 0x7a8fff).ram().share("videoram"); // 2x M58725P
	map(0x7b0000, 0x7b1fff).rom().region("comm_a", 0);
	map(0x7c0000, 0x7c1fff).rom().region("comm_b", 0);
	//map(0x7e0000, 0x7e1fff).rom().region("wd1001_clb", 0);
	map(0x7f1000, 0x7f1001).r(FUNC(fs3216_state::irq_r));
	map(0x7f3000, 0x7f300f).w(FUNC(fs3216_state::mmu_reg_w));
	map(0x7f5000, 0x7f5001).w(FUNC(fs3216_state::mmu_init_w));
	map(0x7f6000, 0x7f6001).nopw();
	map(0x7f7000, 0x7f7001).r(FUNC(fs3216_state::earom_store_r));
	map(0x7f7200, 0x7f7201).r(FUNC(fs3216_state::earom_recall_r));
	map(0x7f7400, 0x7f75ff).rw(m_earom, FUNC(x2212_device::read), FUNC(x2212_device::write)).umask16(0x00ff);
}

void fs3216_state::fc7_map(address_map &map)
{
	map(0xfffff0, 0xffffff).r(FUNC(fs3216_state::intack_r)).umask16(0x00ff);
}

void fs3216_state::wdcpu_prog_map(address_map &map)
{
	map(0x0000, 0x03ff).rom().region("wdcpu", 0);
}

void fs3216_state::wdcpu_bank_map(address_map &map)
{
	map(0x000, 0x000).nopr();
}


static void fs3216_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

void fs3216_state::fs3216(machine_config &config)
{
	M68000(config, m_maincpu, 44.2368_MHz_XTAL / 8); // 5.5 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &fs3216_state::main_map);
	m_maincpu->set_addrmap(m68000_device::AS_CPU_SPACE, &fs3216_state::fc7_map);
	m_maincpu->reset_cb().set(FUNC(fs3216_state::mmu_reset_w));

	ADDRESS_MAP_BANK(config, m_clb);
	m_clb->set_addrmap(0, &fs3216_state::clb_map);
	m_clb->set_data_width(16);
	m_clb->set_addr_width(24);
	m_clb->set_endianness(ENDIANNESS_BIG);

	Z80CTC(config, m_ctc, 44.2368_MHz_XTAL / 8); // Z8430BPS
	m_ctc->set_clk<0>(44.2368_MHz_XTAL / 16); // CLK0 rate guessed
	m_ctc->set_clk<1>(44.2368_MHz_XTAL / 16); // CLK1 rate guessed
	m_ctc->zc_callback<0>().set("dart", FUNC(z80dart_device::rxca_w));
	m_ctc->zc_callback<0>().append("dart", FUNC(z80dart_device::txca_w));
	m_ctc->zc_callback<1>().set("dart", FUNC(z80dart_device::rxtxcb_w));
	m_ctc->zc_callback<1>().append(m_ctc, FUNC(z80ctc_device::trg2));
	m_ctc->zc_callback<2>().set(m_ctc, FUNC(z80ctc_device::trg3));

	Z80DART(config, "dart", 44.2368_MHz_XTAL / 8); // Z8470BPS

	UPD765A(config, m_fdc, 16_MHz_XTAL / 2, false, false);
	m_fdc->intrq_wr_callback().set(FUNC(fs3216_state::fdc_int_w));
	m_fdc->drq_wr_callback().set(FUNC(fs3216_state::fdc_drq_w));
	m_fdc->hdl_wr_callback().set(FUNC(fs3216_state::fdc_hdl_w));
	m_fdc->idx_wr_callback().set(FUNC(fs3216_state::floppy_idx_w));
	m_fdc->us_wr_callback().set(FUNC(fs3216_state::fdc_us_w));

	for (int i = 0; i < 4; i++)
		FLOPPY_CONNECTOR(config, m_floppy[i], fs3216_floppies, i < 1 ? "525dd" : nullptr, floppy_image_device::default_mfm_floppy_formats);

	X2212(config, m_earom);

	mc6845_device &crtc(MC6845(config, "crtc", 14.58_MHz_XTAL / 9)); // HD46505RP
	crtc.set_char_width(9);
	crtc.set_show_border_area(false);
	crtc.set_update_row_callback(FUNC(fs3216_state::crt_update_row));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_raw(14.58_MHz_XTAL, 900, 0, 720, 270, 0, 250);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	n8x305_cpu_device &wdcpu(N8X305(config, "wdcpu", 8_MHz_XTAL)); // N8X305I
	wdcpu.set_addrmap(AS_PROGRAM, &fs3216_state::wdcpu_prog_map);
	wdcpu.set_addrmap(AS_IO, &fs3216_state::wdcpu_bank_map);
}


static INPUT_PORTS_START(fs3216)
INPUT_PORTS_END


// XTALs on Mother Board (1001176-01 Rev. 5): 44.2368 MHz (22A), 16.000 MHz (2J)
// XTALs on Comm A board (1000014-01 2 Port / 1000171-01 4 Port Rev. 7; 10000065-01 Rev. 3): two K1135CM Dual Baud Rate Generators (7D, 8D)
// XTALs on Comm B board (1001651-01 Rev. G): none
// XTALs on Video Controller board (1000443-1 Rev. I): 14.580 MHz (1H)
// XTALs on WD-1001 CLB Disk Controller board (1473-008): 20.000 (Y1), 8.000 (Y2)
ROM_START(fs3216)
	ROM_REGION16_BE(0x4000, "momrom", 0)
	ROM_LOAD16_BYTE("17k_1260-02_h.bin", 0x0000, 0x2000, CRC(75ed6de8) SHA1(0360548493b778995ae436da475b6356945e1872))
	ROM_LOAD16_BYTE("15k_1260-01_l.bin", 0x0001, 0x2000, CRC(82695233) SHA1(0d69309f41306298bf6a4ba6928c53f908bb3f2c))

	ROM_REGION(0x100, "earom", 0)
	ROM_LOAD("sn1000044-08_x2212.bin", 0x000, 0x100, CRC(2bf1fec8) SHA1(e1bdda558364415131e68443013c608bb9c01451))

	ROM_REGION(0x20, "vecprom", 0)
	ROM_LOAD("12j_74s288.bin", 0x00, 0x20, CRC(8f7bf087) SHA1(de785f7ab79f0e58e411ec5cbc42991d1d8486b1))

	ROM_REGION16_BE(0x2000, "comm_a", 0)
	ROM_LOAD16_BYTE("1896-01_c90c3cb92588a2b4bb28bcf4bb8e2023.bin", 0x0000, 0x1000, CRC(ac4cdbd2) SHA1(e448a01a9809cccfb526ac1d4e97d9be3af1e5eb))
	ROM_LOAD16_BYTE("1895-01_fb20aa682a17028cdae2687fc47daef1.bin", 0x0001, 0x1000, CRC(82ebffb5) SHA1(3888b7ba07d0b25bfb9e0444215d4fa9ecd66273))

	ROM_REGION16_BE(0x2000, "comm_b", 0)
	ROM_LOAD16_BYTE("1658-04_b99b2d6b67222a571cc9879f98f2136f.bin", 0x0000, 0x1000, CRC(18b43218) SHA1(ded1419185693350ed2d5868819b0db0c2917ff3))
	ROM_LOAD16_BYTE("1658-03_8425b4008a0fa9092158eb4683110e0f.bin", 0x0001, 0x1000, CRC(83bcdf34) SHA1(782cc58b179b0a42dfe9e09f465582b1420c0c4a))

	ROM_REGION16_BE(0x2000, "video", 0)
	ROM_LOAD16_BYTE("1148-02_3dda8d9a72db50bfc8c2eab697b14952.bin", 0x0000, 0x1000, CRC(ce8f42a4) SHA1(4cfb967890de069270a068b4d4f1f5cf6a9a4c7b))
	ROM_LOAD16_BYTE("1148-01_da087b69caa08bf7c8433a2e089143ce.bin", 0x0001, 0x1000, CRC(72f4c435) SHA1(679fab926b45c99ab19baa75d0d7002d4a5d9299))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("370-01_baf9a45321f489df3ee41175f29105b6.bin", 0x0000, 0x1000, CRC(4d3f1c2a) SHA1(6b5b03757ea53a39675ef442d090e5608bd659c3))

	ROM_REGION16_BE(0x2000, "wd1001_clb", 0)
	ROM_LOAD16_BYTE("u29_1139-02_d54c89bf6a84505b3b9ae05f06597c8d.bin", 0x0000, 0x1000, CRC(396b709a) SHA1(dc1ddef8a16c0529bf76fcd5933ba52e0409e3f9))
	ROM_LOAD16_BYTE("u28_1139-01_3b99c99f03cb4788cc0142e7a9497cda.bin", 0x0001, 0x1000, CRC(c42b7678) SHA1(bec25327cf0bcc8edcb09605cbb609b5708b89f6))

	ROM_REGION16_LE(0x800, "wdcpu", 0) // 2x N82S181N
	ROM_LOAD16_BYTE("u35_ap2001r4m_a794e8b07817734303ede17f38a91e0b_ms2012.bin", 0x000, 0x400, CRC(833a60c9) SHA1(f6414623fc52d030df8814befda02928e2ac5771))
	ROM_LOAD16_BYTE("u53_ap2000r4l_9b8ee868fd1129000b3168350114b6da.bin", 0x001, 0x400, CRC(91b21c9b) SHA1(87fb5b1d5804f771782ceab74fd8d7c97e189bc7))

	ROM_REGION(0x400, "wd1001_prom", 0) // N82S181N (address decoding?)
	ROM_LOAD("u26_ap2002r4f_b3ae6f8966230689d34c82b3c9d817ac.bin", 0x000, 0x400, CRC(fcd31bff) SHA1(ae34c6eb6659dc992896b388be1badfab0fd7971))
ROM_END

} // anonymous namespace


COMP(1982, fs3216, 0, 0, fs3216, fs3216, fs3216_state, empty_init, "Fortune Systems", "Fortune 32:16", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



ft68m.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Forward Technology FT-68M Multibus card.

2013-09-26 Skeleton driver

Chips: HD68000-10, uPD7201C, AM9513APC. Crystal: 19.6608 MHz

Interrupts: INT6 is output of Timer 2, INT7 is output of Timer 3 (refresh),
            INT5 comes from SIO.

****************************************************************************/

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "bus/rs232/rs232.h"
#include "machine/am9513.h"
#include "machine/z80sio.h"


namespace {

class ft68m_state : public driver_device
{
public:
	ft68m_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_p_base(*this, "rambase")
		, m_maincpu(*this, "maincpu")
	{
	}

	void ft68m(machine_config &config);

private:
	uint16_t switches_r();

	void mem_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_shared_ptr<uint16_t> m_p_base;

	required_device<cpu_device> m_maincpu;
};

uint16_t ft68m_state::switches_r()
{
	return 0x7c00; // bypass self test
}


void ft68m_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xffffff);
	map(0x000000, 0x1fffff).ram().share("rambase");
	map(0x200000, 0x201fff).rom().region("roms", 0x0000);
	map(0x400000, 0x401fff).rom().region("roms", 0x2000);
	map(0x600000, 0x600007).mirror(0x1ffff8).rw("mpsc", FUNC(upd7201_device::ba_cd_r), FUNC(upd7201_device::ba_cd_w)).umask16(0xff00);
	map(0x800000, 0x800003).mirror(0x1ffffc).rw("stc", FUNC(am9513_device::read16), FUNC(am9513_device::write16));
	map(0xa00000, 0xbfffff).ram(); //Page Map
	map(0xc00000, 0xdfffff).ram(); //Segment Map
	map(0xe00000, 0xffffff).r(FUNC(ft68m_state::switches_r)); //Context Register
}


/* Input ports */
static INPUT_PORTS_START( ft68m )
INPUT_PORTS_END


void ft68m_state::machine_start()
{
	// GATE 1 is tied to Vcc; other GATE and SRC pins are all grounded
	subdevice<am9513_device>("stc")->gate1_w(1);
}

void ft68m_state::machine_reset()
{
	uint8_t* ROM = memregion("roms")->base();
	memcpy(m_p_base, ROM, 8);
}

void ft68m_state::ft68m(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, XTAL(19'660'800) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &ft68m_state::mem_map);

	upd7201_device& mpsc(UPD7201(config, "mpsc", 0));
	mpsc.out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
	mpsc.out_dtra_callback().set("rs232a", FUNC(rs232_port_device::write_dtr));
	mpsc.out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts));
	mpsc.out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
	mpsc.out_int_callback().set_inputline(m_maincpu, M68K_IRQ_5);

	am9513_device &stc(AM9513A(config, "stc", XTAL(19'660'800) / 8));
	stc.out2_cb().set_inputline(m_maincpu, M68K_IRQ_6);
	stc.out3_cb().set_inputline(m_maincpu, M68K_IRQ_7);
	stc.out4_cb().set("mpsc", FUNC(upd7201_device::rxca_w));
	stc.out4_cb().append("mpsc", FUNC(upd7201_device::txca_w));
	stc.out5_cb().set("mpsc", FUNC(upd7201_device::rxcb_w));
	stc.out5_cb().append("mpsc", FUNC(upd7201_device::txcb_w));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, "terminal"));
	rs232a.rxd_handler().set("mpsc", FUNC(upd7201_device::rxa_w));
	rs232a.dsr_handler().set("mpsc", FUNC(upd7201_device::dcda_w));
	rs232a.cts_handler().set("mpsc", FUNC(upd7201_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set("mpsc", FUNC(upd7201_device::rxb_w));
}

/* ROM definition */
ROM_START( ft68m )
	ROM_REGION16_BE(0x4000, "roms", 0)
	ROM_LOAD16_BYTE("23-0009-01c.a4", 0x0000, 0x1000, CRC(0d45fc8d) SHA1(59587cb1c151bfd0d69e708716ed3b0a78aa85ea) )
	ROM_LOAD16_BYTE("23-0008-01c.a1", 0x0001, 0x1000, CRC(d1aa1164) SHA1(05e10f1c594e2acd369949b873a524a9cc37829f) )
	ROM_LOAD16_BYTE( "33-01.a6", 0x2000, 0x1000, CRC(53fe3c73) SHA1(ad15c74cd8edef9d9716ad0d16f7a95ff2af901f) )
	ROM_LOAD16_BYTE( "33-00.a3", 0x2001, 0x1000, CRC(06b1cc77) SHA1(12e3314e92f800b3c4ebdf55dcd5351230224788) )

	ROM_REGION(0x700, "proms", 0)
	ROM_LOAD("23-0010-00.a15", 0x000, 0x020, CRC(20eb1183) SHA1(9b268792b28d858d6b6a1b6c4148af88a8d6b735) )
	ROM_LOAD("23-0011-00.a14", 0x100, 0x200, CRC(12d9a6be) SHA1(fca99f9c5afc630ac67cbd4e5ba4e5242b826848) )
	ROM_LOAD("23-0012-00.a16", 0x300, 0x020, CRC(ee1e5a14) SHA1(0d3346cb3b647fa2475bd7b4fa36ea6ecfdaf805) )
	ROM_LOAD("23-0034-00.e4",  0x400, 0x100, CRC(1a573887) SHA1(459bd2d8dc8c4b1c0a529984ae8e38d0c81a084c) )
	ROM_LOAD("23-0037-00.e7",  0x500, 0x100, CRC(9ed4b7f6) SHA1(136a74567094d8462c3a4de1b7e6eb8f30fe71ca) )
	ROM_LOAD("23-0038-00.f1",  0x600, 0x100, CRC(3e56cce5) SHA1(f30a8d5d744bfc25493cd1e92961bbb75f9e0d05) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT   CLASS        INIT        COMPANY               FULLNAME  FLAGS
COMP( 198?, ft68m, 0,      0,      ft68m,   ft68m,  ft68m_state, empty_init, "Forward Technology", "FT-68M", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



future32.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:O. Galibert

// Sord Future 32

// According the the Sord timeline page,
// https://www.sord.co.jp/company/corporate/history.html
// there was:

//   1987: Sord Future 32
//   1988: Sord Future 32 HR
//   1989: Sord Future 32α
//   1991: Sord Future 32α II

// At this point, we only have a dump of the 32α
// Strangely the machine is badged "32α" but the internal strings
// in the prom seem to indicated it dates from 1991.  Maybe a revision,
// maybe a II in disguise...


#include "emu.h"
#include "bus/nscsi/hd.h"
#include "bus/nscsi/cd.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68030.h"
#include "imagedev/floppy.h"
#include "machine/6840ptm.h"
#include "machine/clock.h"
#include "machine/mb87030.h"
#include "machine/upd71071.h"
#include "machine/upd765.h"
#include "machine/z80scc.h"
#include "video/bt45x.h"
#include "video/mc6845.h"
#include "video/upd72120.h"
#include "emupal.h"
#include "screen.h"
#include "tilemap.h"

//  HLE of the keyboard, the MCU is not yet dumped (and may not ever be)
class future32_kbd_device : public device_t
{
public:
	static constexpr u32 serial_clock = 9600;
	static constexpr u32 scan_clock = 100;

	future32_kbd_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0);
	virtual ~future32_kbd_device() = default;

	auto tx_cb() { return m_tx_cb.bind(); }
	void rx_w(int state);

protected:
	virtual void device_start() override ATTR_COLD;
	virtual void device_reset() override ATTR_COLD;
	virtual ioport_constructor device_input_ports() const override ATTR_COLD;

private:
	enum { TX_IDLE, TX_SEND };

	devcb_write_line m_tx_cb;
	required_ioport_array<8> m_keys;
	emu_timer *m_tx_timer, *m_rx_timer, *m_scan_timer;

	std::array<u32, 8> m_scan;
	std::array<u8, 64> m_buffer;
	u32 m_buffer_size;

	u32 m_tx_state;
	u16 m_tx;

	TIMER_CALLBACK_MEMBER(timer_rx);
	TIMER_CALLBACK_MEMBER(timer_tx);
	TIMER_CALLBACK_MEMBER(timer_scan);

	void push(u8 data);
	void tx_start();
};

DEFINE_DEVICE_TYPE(FUTURE32_KBD, future32_kbd_device, "future32_kbd", "Sord Future32 keyboard");

future32_kbd_device::future32_kbd_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: device_t(mconfig, FUTURE32_KBD, tag, owner, clock)
	, m_tx_cb(*this)
	, m_keys(*this, "P%u", 0U)
{
}

void future32_kbd_device::rx_w(int state)
{
	// Doesn't seem to be used, so not implemented
	logerror("rx %d\n", state);
}

void future32_kbd_device::device_start()
{
	m_tx_timer = timer_alloc(FUNC(future32_kbd_device::timer_tx), this);
	m_rx_timer = timer_alloc(FUNC(future32_kbd_device::timer_rx), this);
	m_scan_timer = timer_alloc(FUNC(future32_kbd_device::timer_scan), this);

	save_item(NAME(m_buffer));
	save_item(NAME(m_buffer_size));
	save_item(NAME(m_tx_state));
	save_item(NAME(m_tx));
	save_item(NAME(m_scan));
}

void future32_kbd_device::device_reset()
{
	std::fill(m_buffer.begin(), m_buffer.end(), 0);
	std::fill(m_scan.begin(), m_scan.end(), 0xffffffff);
	m_buffer_size = 0;
	m_tx_state = TX_IDLE;
	m_tx = 0;

	m_scan_timer->adjust(attotime::from_ticks(1, scan_clock), 0, attotime::from_ticks(1, scan_clock));
}

void future32_kbd_device::push(u8 data)
{
	if(m_buffer_size == m_buffer.size())
		return;
	m_buffer[m_buffer_size++] = data;
	if(m_tx_state == TX_IDLE)
		tx_start();
}

void future32_kbd_device::tx_start()
{
	m_tx_state = TX_SEND;
	m_tx = 0x200 | (m_buffer[0] << 1);
	m_tx_timer->adjust(attotime::from_ticks(1, serial_clock), 0, attotime::from_ticks(1, serial_clock));
	memmove(m_buffer.data(), m_buffer.data()+1, m_buffer.size()-1);
	m_buffer_size --;
}

TIMER_CALLBACK_MEMBER(future32_kbd_device::timer_tx)
{
	m_tx_cb(m_tx & 1);
	m_tx >>= 1;
	if(m_tx)
		return;
	m_tx_state = TX_IDLE;
	m_tx_timer->adjust(attotime::never);
	if(m_buffer_size)
		tx_start();
}

TIMER_CALLBACK_MEMBER(future32_kbd_device::timer_rx)
{
}

TIMER_CALLBACK_MEMBER(future32_kbd_device::timer_scan)
{
	for(u32 i=0; i != 8; i++) {
		u32 state = m_keys[i]->read();
		u32 diff = state ^ m_scan[i];
		m_scan[i] = state;
		if(diff)
			for(u32 j=0; j != 32; j++)
				if(BIT(diff, j)) {
					if(BIT(state, j))
						push((i << 5) | j);         // keyoff
					else
						push((i << 5) | j | 0x80);  // keyon
				}
	}
}


static INPUT_PORTS_START(future32_kbd)
	PORT_START("P0")
	PORT_BIT( 0xffffffff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P1")
	PORT_BIT( 0x00010000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x00020000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x00040000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x00080000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x00100000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x00200000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x00400000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x00800000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x01000000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x02000000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x04000000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x08000000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT( 0x10000000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT( 0x20000000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\')
	PORT_BIT( 0x40000000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DEL)) PORT_CODE(KEYCODE_DEL)
	PORT_BIT( 0x8000ffff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0x00000001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT( 0x00000002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x00000004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x00000008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x00000010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x00000020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x00000040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x00000080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x00000100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT( 0x00000200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x00000400, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x00000800, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@ `") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT( 0x00001000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT( 0x00002000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)

	PORT_BIT( 0x00010000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x00020000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x00040000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x00080000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x00100000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x00200000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x00400000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x00800000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x01000000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x02000000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x04000000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("; +") PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x08000000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(": *") PORT_CODE(KEYCODE_COLON) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x10000000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT( 0x20000000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LF") PORT_CHAR(10)
	PORT_BIT( 0xc000c000, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x00000001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x00000002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x00000004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x00000008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x00000010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x00000020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x00000040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x00000080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',')
	PORT_BIT( 0x00000100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.')
	PORT_BIT( 0x00000200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/')

	PORT_BIT( 0xfffffc00, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P4")
	PORT_BIT( 0xffffffff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P5")
	PORT_BIT( 0xffffffff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P6")
	PORT_BIT( 0xffffffff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P7")
	PORT_BIT( 0xffffffff, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END


ioport_constructor future32_kbd_device::device_input_ports() const
{
	return INPUT_PORTS_NAME(future32_kbd);
}



class future32a_state : public driver_device
{
public:
	future32a_state(const machine_config &mconfig, device_type type, const char *tag);
	virtual ~future32a_state();

	void future32a(machine_config &config);

protected:
	required_device<m68030_device> m_maincpu;
	required_device<hd6345_device> m_crtc;
	required_device<upd72120_device> m_agdc;
	required_device<upd71071_device> m_dma;
	required_device<scc8530_device> m_scc1;
	required_device<scc8530_device> m_scc2;
	required_device<ptm6840_device> m_ptm;
	required_device<upd72065_device> m_fdc;
	required_device<floppy_connector> m_floppy;
	required_device<mb89352_device> m_scsi;
	required_device<screen_device> m_screen;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<bt451_device> m_ramdac;
	required_device<future32_kbd_device> m_kbd;
	required_shared_ptr<u32> m_textlayer;
	memory_share_creator<u16> m_fontram;
	required_region_ptr<u8> m_fontascii;
	required_region_ptr<u8> m_fontkanji;

	memory_passthrough_handler m_boot_tap;

	u16 m_font_base;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void mem(address_map &map);
	void cpu_space_map(address_map &map);
	MC6845_UPDATE_ROW(crtc_update_row);
	static void floppy_drives(device_slot_interface &device);
	static void scsi_devices(device_slot_interface &device);
	void mb89352(device_t *device);

	void scsi_irq_w(int state);
	void scc1_irq_w(int state);
	u8 intc_get_vector();
	void font_base_w(u16 base);

	u16 font_r(offs_t offset);
	void font_w(offs_t offset, u16 data);
};

future32a_state::future32a_state(const machine_config &mconfig, device_type type, const char *tag)
	: driver_device(mconfig, type, tag)
	, m_maincpu(*this, "maincpu")
	, m_crtc(*this, "crtc")
	, m_agdc(*this, "agdc")
	, m_dma(*this, "dma")
	, m_scc1(*this, "scc1")
	, m_scc2(*this, "scc2")
	, m_ptm(*this, "ptm")
	, m_fdc(*this, "fdc")
	, m_floppy(*this, "fdc:0")
	, m_scsi(*this, "scsi:7:mb89352")
	, m_screen(*this, "screen")
	, m_gfxdecode(*this, "gfxdecode")
	, m_ramdac(*this, "ramdac")
	, m_kbd(*this, "kbd")
	, m_textlayer(*this, "textlayer")
	, m_fontram(*this, "fontram", 8 * 2 * 0x10000, ENDIANNESS_BIG)
	, m_fontascii(*this, "fontascii")
	, m_fontkanji(*this, "fontkanji")
{
}

future32a_state::~future32a_state()
{
}

void future32a_state::mem(address_map &map)
{
	map(0x00000000, 0x003fffff).ram();  // Some magic at startup to see the rom there
	map(0x00400000, 0x009fffff).lr32(NAME([this]() { if(!machine().side_effects_disabled()) m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE); return 0; }));
	map(0x00a00000, 0x00a7ffff).ram();

	map(0x00e00000, 0x00e07fff).rom().region("maincpu", 0);
	map(0x00e10000, 0x00e1000f).rw(m_dma, FUNC(upd71071_device::read), FUNC(upd71071_device::write));
	map(0x00e20000, 0x00e20000).lw8(NAME([this](u8 data) { logerror("boot state %02x\n", data); }));
	map(0x00e30000, 0x00e31fff).lw16(NAME([this](offs_t offset, u16 data) { if(offset != data) logerror("%06x: %04x\n", 0xe30000+2*offset, data); }));
	map(0x00e40000, 0x00e4007f).m(m_agdc, FUNC(upd72120_device::map));
	map(0x00e50000, 0x00e50007).m(m_ramdac, FUNC(bt451_device::map)).umask16(0x00ff);
	map(0x00e60000, 0x00e61fff).ram().share(m_textlayer);
	map(0x00e62000, 0x00e6203f).rw(FUNC(future32a_state::font_r), FUNC(future32a_state::font_w));
	map(0x00e70000, 0x00e7000f).rw(m_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write)).umask16(0x00ff);
	map(0x00e70201, 0x00e70201).rw(m_crtc, FUNC(hd6345_device::status_r), FUNC(hd6345_device::address_w));
	map(0x00e70203, 0x00e70203).rw(m_crtc, FUNC(hd6345_device::register_r), FUNC(hd6345_device::register_w));
	map(0x00e70301, 0x00e70301).rw(m_scc1, FUNC(scc8530_device::ca_r), FUNC(scc8530_device::ca_w));
	map(0x00e70303, 0x00e70303).rw(m_scc1, FUNC(scc8530_device::da_r), FUNC(scc8530_device::da_w));
	map(0x00e70305, 0x00e70305).rw(m_scc1, FUNC(scc8530_device::cb_r), FUNC(scc8530_device::cb_w));
	map(0x00e70307, 0x00e70307).rw(m_scc1, FUNC(scc8530_device::db_r), FUNC(scc8530_device::db_w));

	map(0x00e70800, 0x00e70801). w(FUNC(future32a_state::font_base_w));
	map(0x00e70901, 0x00e70901).lr8(NAME([this]() { return machine().rand() & 1 ? 0xc0 : 0x80; })); // beeper here, possibly timer too

	map(0x00e80101, 0x00e80101).rw(m_scc2, FUNC(scc8530_device::ca_r), FUNC(scc8530_device::ca_w));
	map(0x00e80103, 0x00e80103).rw(m_scc2, FUNC(scc8530_device::da_r), FUNC(scc8530_device::da_w));
	map(0x00e80105, 0x00e80105).rw(m_scc2, FUNC(scc8530_device::cb_r), FUNC(scc8530_device::cb_w));
	map(0x00e80107, 0x00e80107).rw(m_scc2, FUNC(scc8530_device::db_r), FUNC(scc8530_device::db_w));

	map(0x00ec0000, 0x00ec000f).m(m_scsi, FUNC(mb89352_device::map));
	map(0x00ec0100, 0x00ec0103).m(m_fdc, FUNC(upd72065_device::map));

	map(0x00ec0400, 0x00ec0403).portr("dips");

	map(0x00f00000, 0x00f0ffff).lr32(NAME([this]() { if(!machine().side_effects_disabled()) m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE); return 0; }));
}

void future32a_state::cpu_space_map(address_map &map)
{
	map(0xfffffff3, 0xfffffff3).lr8(NAME([] () -> u8 { return 25; }));
	map(0xfffffff5, 0xfffffff5).lr8(NAME([] () -> u8 { return 26; }));
	map(0xfffffff7, 0xfffffff7).lr8(NAME([] () -> u8 { return 27; }));
	map(0xfffffff9, 0xfffffff9).r(FUNC(future32a_state::intc_get_vector));
	map(0xfffffffb, 0xfffffffb).lr8(NAME([] () -> u8 { return 29; }));
	map(0xfffffffd, 0xfffffffd).lr8(NAME([] () -> u8 { return 30; }));
	map(0xffffffff, 0xffffffff).lr8(NAME([] () -> u8 { return 31; }));
}

static const gfx_layout textascii_layout = {
	16, 32,
	1024,
	1,
	{ 0 },
	{ STEP16(0, 1) },
	{ STEP32(0, 16) },
	16*32
};

static const gfx_layout textkanji_layout1 = {
	24, 24,
	0x2000,
	1,
	{ 0 },
	{ STEP16(0, 1), STEP8(16, 1)},
	{ STEP16(0, 32), STEP8(32*16, 32) },
	32*32
};

static const gfx_layout textkanji_layout2 = {
	16, 16,
	0x2000,
	1,
	{ 32*24 },
	{ STEP16(0, 1) },
	{ STEP16(0, 16) },
	32*32
};

static const gfx_layout textkanji_layout3 = {
	32, 32,
	0x2000,
	1,
	{ 0 },
	{ STEP32(0, 1) },
	{ STEP32(0, 32) },
	32*32
};


static GFXDECODE_START( gfx_textlayer )
	GFXDECODE_ENTRY("fontascii", 0, textascii_layout, 0x100, 1)
	GFXDECODE_ENTRY("fontkanji", 0, textkanji_layout1, 0x100, 1)
	GFXDECODE_ENTRY("fontkanji", 0, textkanji_layout2, 0x100, 1)
	GFXDECODE_ENTRY("fontkanji", 0, textkanji_layout3, 0x100, 1)
GFXDECODE_END

// void name(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint16_t ma, uint8_t ra,
//           uint16_t y, uint8_t x_count, int8_t cursor_x, int de, int hbp, int vbp)

MC6845_UPDATE_ROW(future32a_state::crtc_update_row)
{
	const pen_t *palette = m_ramdac->pens();
	uint32_t *d = &bitmap.pix(y);
	for(u8 x = 0; x != x_count; x++) {
		u32 data = m_textlayer[(ma+x) & 0x7ff];
		u16 code = data & 0xffff;
		u16 attr = data >> 16;
		u16 pixels = 0x5555 << (ra & 1);
		if(code >= 0x8000) {
			// JISx 201/208 code
			// Feels like there's a mapping table hiding somewhere
			code &= 0x7fff;

			if(ra >= 24)
				pixels = 0;
			else if(code >= 0x3000) {
				// JISx 208
				code -= 0x3000;
				u16 code1 = ((code & 0x3f00) >> 1) | (code & 0x7f);
				offs_t base = code1 * 0x80 + ra*4;
				if(code & 0x80)
					pixels = ((m_fontkanji[base+3] << 8) | m_fontkanji[base + 2]) << 4;
				else
					pixels = (m_fontkanji[base] << 8) | m_fontkanji[base + 1];
			} else if(code >= 0x2a00) {
				// Font ram, maybe, but it doesn't really work out

			} else if(code >= 0x2120) {
				// JISx 201
				code -= 0x2120;
				u16 code1 = 0x480 + (((code & 0x700) >> 1) | ((code & 0x60) << 5) | (code & 0x1f));
				offs_t base = code1 * 0x80 + ra*4;
				if(code & 0x80)
					pixels = ((m_fontkanji[base+3] << 8) | m_fontkanji[base + 2]) << 4;
				else
					pixels = (m_fontkanji[base] << 8) | m_fontkanji[base + 1];
			}

			if(ra >= 24)
				pixels = 0;

		} else {
			// ASCII code
			offs_t base = (code & 0x3ff)*64 + ra * 2;
			pixels = (m_fontascii[base] << 8) | m_fontascii[base + 1];
		}
		if(x == cursor_x)
			pixels = pixels ^ 0xffff;
		u32 c0 = palette[0];
		u32 c1 = palette[attr & 15];
		if(attr & 0x0020)
			std::swap(c0, c1);
		for(int xx=0; xx != 12; xx++)
			*d++ = BIT(pixels, 15-xx) ? c1 : c0;
	}
}


void future32a_state::machine_start()
{
}

void future32a_state::machine_reset()
{
	m_boot_tap = m_maincpu->space(AS_PROGRAM)
		.install_read_tap(0x00000000, 0x00007fff, "boot",
						  [this](offs_t address, u32 &data, u32 mask) {
							  data = m_maincpu->space(AS_PROGRAM).read_dword(0xe00000 + address, mask);
							  if(address == 4 && mask == 0x0000ffff && !machine().side_effects_disabled())
								  m_boot_tap.remove();
						  });
}

void future32a_state::scsi_irq_w(int state)
{
	m_maincpu->set_input_line(4, state);
}

void future32a_state::scc1_irq_w(int state)
{
	logerror("kbd irq %d\n", state);
}

u8 future32a_state::intc_get_vector()
{
	return 0x46;
}

void future32a_state::font_base_w(u16 base)
{
	m_font_base = base;
}

u16 future32a_state::font_r(offs_t offset)
{
	return m_fontram[m_font_base * 0x20 + offset];
}

void future32a_state::font_w(offs_t offset, u16 data)
{
	m_fontram[m_font_base * 8 + offset] = data;
}

void future32a_state::floppy_drives(device_slot_interface &device)
{
	device.option_add("35hd", FLOPPY_35_HD);
}

void future32a_state::scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
	device.option_add("cdrom", NSCSI_CDROM);
	device.option_add_internal("mb89352", MB89352);
}

void future32a_state::mb89352(device_t *device)
{
	mb89352_device &adapter = downcast<mb89352_device &>(*device);
	adapter.set_clock(32000000/4);
	adapter.out_dreq_callback().set([this](int state) { m_dma->dmarq(state, 1); });
	adapter.out_irq_callback().set(*this, FUNC(future32a_state::scsi_irq_w));
}

void future32a_state::future32a(machine_config &config)
{
	// xtals are 50, 32 and 47.843
	// all values guessed

	M68030(config, m_maincpu, 50_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &future32a_state::mem);
	m_maincpu->set_addrmap(m68030_device::AS_CPU_SPACE, &future32a_state::cpu_space_map);

	UPD71071(config, m_dma, 0);
	m_dma->set_cpu_tag(m_maincpu->tag());
	m_dma->set_clock((50_MHz_XTAL / 5).value());
	m_dma->dma_read_callback<1>().set(m_scsi, FUNC(mb89352_device::dma_r));
	m_dma->dma_write_callback<1>().set(m_scsi, FUNC(mb89352_device::dma_w));

	UPD72120(config, m_agdc, 32_MHz_XTAL / 2);

	PTM6840(config, m_ptm, 50_MHz_XTAL / 20);
	m_ptm->irq_callback().set_inputline(m_maincpu, 6);

	SCC8530(config, m_scc1, 32_MHz_XTAL / 8);
	m_scc1->configure_channels(9600*16, 9600*16, 0, 0);
	m_scc1->out_int_callback().set(FUNC(future32a_state::scc1_irq_w));

	SCC8530(config, m_scc2, 32_MHz_XTAL / 8);
	m_scc2->configure_channels(9600*16, 9600*16, 9600*16, 9600*16);

	FUTURE32_KBD(config, m_kbd);
	m_kbd->tx_cb().set(m_scc1, FUNC(scc8530_device::rxa_w));
	m_scc1->out_txda_callback().set(m_kbd, FUNC(future32_kbd_device::rx_w));

	rs232_port_device &port(RS232_PORT(config, "serial", default_rs232_devices, nullptr));
	port.cts_handler().set(m_scc1, FUNC(scc8530_device::ctsb_w));
	port.dcd_handler().set(m_scc1, FUNC(scc8530_device::dcdb_w));
	port.rxd_handler().set(m_scc1, FUNC(scc8530_device::rxb_w));
	m_scc1->out_rtsb_callback().set(port, FUNC(rs232_port_device::write_rts));
	m_scc1->out_txdb_callback().set(port, FUNC(rs232_port_device::write_txd));

	HD6345(config, m_crtc, 50_MHz_XTAL / 10);
	m_crtc->set_show_border_area(false);
	m_crtc->set_screen(m_screen);
	m_crtc->set_char_width(12);
	m_crtc->set_update_row_callback(FUNC(future32a_state::crtc_update_row));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_physical_aspect(4, 3);
	m_screen->set_screen_update(m_crtc, FUNC(hd6345_device::screen_update));
	m_screen->set_raw(50_MHz_XTAL / 10 * 16, 1248, 0, 959, 815, 0, 749);

	BT451(config, m_ramdac, 0);
	GFXDECODE(config, m_gfxdecode, m_ramdac, gfx_textlayer); // Only for F4 use

	UPD72065(config, m_fdc, 32_MHz_XTAL / 8);
	FLOPPY_CONNECTOR(config, m_floppy, floppy_drives, "35hd", floppy_image_device::default_mfm_floppy_formats);

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:1", scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:2", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:7", scsi_devices, "mb89352", true).set_option_machine_config("mb89352", [this] (device_t *device) { mb89352(device); });
}

static INPUT_PORTS_START(future32a)
	PORT_START("dips")
	PORT_DIPNAME( 0xf0000000, 0x00000000, "Boot mode")
	PORT_DIPSETTING( 0x00000000, "Normal" )
	PORT_DIPSETTING( 0x40000000, "Alternative" )
	PORT_DIPSETTING( 0xc0000000, "Forth console" )
	PORT_DIPSETTING( 0xe0000000, "Serial" )
INPUT_PORTS_END

ROM_START(future32a)
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD("ft2o1c.bin", 0x0000, 0x8000, CRC(82f9c0b0) SHA1(e30a350cc19edbf623fa37aa60f0215188cc55d6))

	ROM_REGION( 0x10000, "fontascii", 0)
	ROM_LOAD16_BYTE("ft2fe00a.u42", 0x0000, 0x8000, CRC(1fce9667) SHA1(ac4955afd9eb9401079c5e7ca8bf65de5bb826ab))
	ROM_LOAD16_BYTE("ft2fo00a.u43", 0x0001, 0x8000, CRC(26a708e2) SHA1(d53f1a2e368fa1d231b3989577129ffadcfda5aa))

	ROM_REGION(0x100000, "fontkanji", 0)
	ROM_LOAD("051.u44", 0x00000, 0x80000, CRC(6a50162a) SHA1(92383c3ad7aaa7b2f9c8cf781c6dcddffe7b9af8))
	ROM_LOAD("052.u45", 0x80000, 0x80000, CRC(f2886c9b) SHA1(76363bb7ef884bcf51c50ac56963d513fe776c2e))
ROM_END


COMP(1989, future32a, 0, 0, future32a, future32a, future32a_state, empty_init, "Sord", "Future 32α", MACHINE_NOT_WORKING|MACHINE_SUPPORTS_SAVE)




fz1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Devin Acker

/*
    Driver for Casio FZ-1 and FZ-10M/20M samplers

    Custom sound + video hardware consists of:

    - GAA (uPD65081G-012): address generator for sample RAM
    - GAB (uPD65042G-052): timing generator for PCM interrupt and sample/hold signals
    - GAS (uPD65012G-074): bus arbiter & DRAM refresh signal generator
    - GAX (MB653121): demultiplexes sample RAM output to two DACs
    - 4x MB87186 DCF/DCA (two inputs/outputs each)
    - GAL (uPD65012G-046): generates data & strobe signals for LCD controller
    - HD44350 LCD controller + 2x HD44251 segment drivers

    Floppy drive: Panasonic JU-386 @ 360 rpm
    Disk format: 2HD, 80 tracks * 8 sectors * 1024 bytes

    A good deal of hardware and programming info is available courtesy of Rainer Buchty:
    http://www.buchty.net/casio/

    TODO:
    - add mic & line in/cassette, connect to PCM hardware & filter
    - audio input peak detection (connected to AN0)
    - filters
*/

#include "emu.h"

#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "bus/nscsi/hd.h"
#include "cpu/mcs48/mcs48.h"
#include "cpu/nec/v5x.h"
#include "imagedev/floppy.h"
#include "machine/bankdev.h"
#include "machine/mb87030.h"
#include "machine/i8255.h"
#include "machine/msm6200.h"
#include "machine/ram.h"
#include "machine/upd765.h"
#include "sound/flt_biquad.h"
#include "sound/fz_pcm.h"
#include "video/hd44352.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "formats/fz1_dsk.h"
#include "formats/hxchfe_dsk.h"


namespace {

class fz1_state : public driver_device
{
public:
	fz1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_subcpu(*this, "subcpu")
		, m_pcm(*this, "pcm")
		, m_filter(*this, "filter_%u", 1)
		, m_line_out(*this, "line_out_%u", 1)
		, m_ram(*this, "ram")
		, m_ram_bank(*this, "ram_bank")
		, m_io(*this, "io%u", 0u)
		, m_kbd(*this, "kbd")
		, m_lcdc(*this, "lcdc")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:0")
		, m_keys(*this, "SC%u", 0u)
		, m_analog(*this, "AN%u", 0u)
		, m_led(*this, "led%u", 0u)
	{ }

	void fz1(machine_config &config);
	void fz10m(machine_config &config);
	void fz20m(machine_config &config);

	void mem_w(offs_t offset, u8 data) { m_maincpu->space(AS_PROGRAM).write_byte(offset, data); }
	u8 mem_r(offs_t offset) { return m_maincpu->space(AS_PROGRAM).read_byte(offset); }

	u8 fdc_irq_r() { return m_fdc->get_irq() ? 1 : 0; }
	void fdc_control_w(u8 data);

	void keys_w(u8 val) { m_key_sel = val; }
	u8 keys_r();

	void adc_sel_w(u8 val) { m_adc_sel = val; }
	u8 adc_latch_r();
	u8 adc_r() { return m_adc_value; }

	u8 lcd_ready_r() { return m_lcd_ready; }

	int cont49_r();
	int sync49_r() { return m_sync49; }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void maincpu_map(address_map &map) ATTR_COLD;
	void fz10m_io_map(address_map &map) ATTR_COLD;
	void fz20m_io_map(address_map &map) ATTR_COLD;
	void fz1_io_map(address_map &map) ATTR_COLD;
	void subcpu_map(address_map &map) ATTR_COLD;

	void gal_w(u8 data);
	void led_w(u8 data);
	void dca_w(offs_t offset, u8 data);

	// main CPU / key MCU comm methods
	u8 subcpu_r();
	void sub_p2_w(u8 data);

	required_device<v50_device> m_maincpu;
	optional_device<i8049_device> m_subcpu;

	required_device<fz_pcm_device> m_pcm;
	required_device_array<filter_biquad_device, 8> m_filter;
	required_device_array<speaker_device, 8> m_line_out;

	required_device<ram_device> m_ram;
	required_device<address_map_bank_device> m_ram_bank;

	required_device_array<i8255_device, 2> m_io;
	optional_device<msm6200_device> m_kbd;
	required_device<hd44352_device> m_lcdc;

	required_device<upd72065_device> m_fdc;
	required_device<floppy_connector> m_floppy;

	required_ioport_array<4> m_keys;
	optional_ioport_array<8> m_analog;

	output_finder<5> m_led;

	u8 m_key_sel;
	u8 m_adc_sel;
	u8 m_adc_value;

	u8 m_sub_p2, m_sync49;

	u8 m_lcd_ready;
	u8 m_lcd_data;
	u8 m_lcd_data_phase;
	u8 m_lcd_nibble;

	u16 m_dca_level[8];
};

/**************************************************************************/
static INPUT_PORTS_START(fz10m)
	PORT_START("SC0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Keypad 0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Keypad 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Keypad 2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Keypad 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Keypad 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Keypad 5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Keypad 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Keypad 7") PORT_CODE(KEYCODE_7_PAD)

	PORT_START("SC1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Keypad 8")       PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Keypad 9")       PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Keypad + / Yes") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Keypad - / No")  PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("SC2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Cursor Up")    PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Cursor Down")  PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Cursor Right") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Cursor Left")  PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Enter")        PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Escape")       PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Display")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("SC3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Play")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Modify")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Call/Set Menu")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Transpose")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Tune")
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO1_PB")
	PORT_BIT(0x01, IP_ACTIVE_LOW,  IPT_CUSTOM) // GAA ready
	PORT_BIT(0x02, IP_ACTIVE_LOW,  IPT_CUSTOM) // GAB ready
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(fz1_state::lcd_ready_r))
	PORT_BIT(0x08, IP_ACTIVE_LOW,  IPT_CUSTOM) // ADC ready
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(fz1_state::cont49_r))
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(fz1_state::sync49_r))
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_CUSTOM) // parallel port IRQ
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(fz1_state::fdc_irq_r))

	PORT_START("IO2_PA")
	PORT_BIT(0x03, IP_ACTIVE_HIGH, IPT_OUTPUT) PORT_WRITE_LINE_MEMBER(FUNC(fz1_state::keys_w));
	PORT_BIT(0x1c, IP_ACTIVE_HIGH, IPT_OUTPUT) PORT_WRITE_LINE_MEMBER(FUNC(fz1_state::adc_sel_w));
	PORT_BIT(0xe0, IP_ACTIVE_HIGH, IPT_OUTPUT) PORT_WRITE_LINE_MEMBER(FUNC(fz1_state::fdc_control_w));

	PORT_START("IO2_PB")
	PORT_BIT(0x3f, IP_ACTIVE_HIGH, IPT_OUTPUT) PORT_WRITE_LINE_DEVICE_MEMBER("ram_bank", FUNC(address_map_bank_device::set_bank));
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OUTPUT) // sampling gain
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OUTPUT) // input sample rate

	PORT_START("AN0")
	PORT_BIT(0xff, 0x00, IPT_CUSTOM) // audio input peak level

	PORT_START("AN5")
	PORT_BIT(0xff, 0xff, IPT_POSITIONAL_V) PORT_NAME("Master Volume") PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_CENTERDELTA(0) PORT_PLAYER(2) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)

	PORT_START("AN6")
	PORT_BIT(0xff, 0x80, IPT_POSITIONAL_V) PORT_NAME("Value") PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_CENTERDELTA(0) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)

INPUT_PORTS_END

static INPUT_PORTS_START(fz1)
	PORT_INCLUDE(fz10m)

	PORT_START("kbd:KI8")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C6")

	PORT_START("kbd:KI9")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B5")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#5")

	PORT_START("kbd:KI10")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A5")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#5")

	PORT_START("kbd:KI11")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G5")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#5")

	PORT_START("kbd:KI12")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F5")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E5")

	PORT_START("kbd:KI13")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#5")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D5")

	PORT_START("kbd:KI14")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#5")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C5")

	PORT_START("kbd:KI15")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B4")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#4")

	PORT_START("kbd:KI16")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A4")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#4")

	PORT_START("kbd:KI17")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G4")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#4")

	PORT_START("kbd:KI18")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F4")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E4")

	PORT_START("kbd:KI19")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#4")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D4")

	PORT_START("kbd:KI20")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#4")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C4")

	PORT_START("kbd:KI21")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B3")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#3")

	PORT_START("kbd:KI22")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A3")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#3")

	PORT_START("kbd:KI23")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G3")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#3")

	PORT_START("kbd:KI24")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F3")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E3")

	PORT_START("kbd:KI25")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#3")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D3")

	PORT_START("kbd:KI26")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#3")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C3")

	PORT_START("kbd:KI27")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B2")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#2")

	PORT_START("kbd:KI28")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A2")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#2")

	PORT_START("kbd:KI29")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G2")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#2")

	PORT_START("kbd:KI30")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F2")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E2")

	PORT_START("kbd:KI31")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#2")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D2")

	PORT_START("kbd:KI32")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#2")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C2")

	PORT_START("kbd:KI33")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B1")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#1")

	PORT_START("kbd:KI34")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A1")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#1")

	PORT_START("kbd:KI35")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G1")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#1")

	PORT_START("kbd:KI36")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F1")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E1")

	PORT_START("kbd:KI37")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#1")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D1")

	PORT_START("kbd:KI38")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#1")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C1")

	PORT_START("kbd:VELOCITY")
	PORT_BIT(0x3f, 0x3f, IPT_POSITIONAL_V) PORT_NAME("Key Velocity") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_CENTERDELTA(0) PORT_PLAYER(7) PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH) PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH)

	PORT_START("AN1")
	PORT_BIT(0xff, 0xff, IPT_POSITIONAL_V) PORT_NAME("Volume Pedal") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_CENTERDELTA(0) PORT_PLAYER(6) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)

	PORT_START("AN2")
	PORT_BIT(0xff, 0xff, IPT_POSITIONAL_V) PORT_NAME("Aftertouch") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_CENTERDELTA(0) PORT_PLAYER(5) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)

	PORT_START("AN3")
	PORT_BIT(0xff, 0x00, IPT_POSITIONAL_V) PORT_NAME("Modulation Wheel") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_CENTERDELTA(0) PORT_PLAYER(4) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)

	PORT_START("AN4")
	PORT_BIT(0xff, 0x7f, IPT_PADDLE)       PORT_NAME("Pitch Wheel") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00, 0xff) PORT_PLAYER(3) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)

INPUT_PORTS_END

/**************************************************************************/
void fz1_state::maincpu_map(address_map &map)
{
	map(0x00000, 0x0ffff).ram();
	map(0x10000, 0x1ffff).rw(m_ram_bank, FUNC(address_map_bank_device::read16), FUNC(address_map_bank_device::write16));
	map(0x80000, 0x8ffff).mirror(0x70000).rom().region("maincpu", 0);
}

/**************************************************************************/
void fz1_state::fz10m_io_map(address_map &map)
{
	map(0x00, 0x07).rw(m_pcm, FUNC(fz_pcm_device::gaa_r), FUNC(fz_pcm_device::gaa_w));
	map(0x08, 0x0f).rw(m_pcm, FUNC(fz_pcm_device::gab_r), FUNC(fz_pcm_device::gab_w));
	map(0x10, 0x13).mirror(0x04).m(m_fdc, FUNC(upd72065_device::map)).umask16(0x00ff);
	map(0x18, 0x1f).rw(m_io[0], FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
	map(0x20, 0x27).rw(m_io[1], FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
	map(0x60, 0x67).r(FUNC(fz1_state::adc_latch_r));
	map(0x68, 0x6f).r(FUNC(fz1_state::adc_r)).umask16(0x00ff);
	map(0x70, 0x77).w(FUNC(fz1_state::gal_w)).umask16(0x00ff);
	map(0x78, 0x7f).w(FUNC(fz1_state::led_w)).umask16(0x00ff);
	// 0x80-bf: DCF/DCA
	map(0x80, 0x9f).w(FUNC(fz1_state::dca_w)).umask16(0x00ff);
	map(0xa0, 0xbf).ram();
}

/**************************************************************************/
void fz1_state::fz1_io_map(address_map &map)
{
	fz10m_io_map(map);
	map(0x28, 0x2f).r(FUNC(fz1_state::subcpu_r)).umask16(0x00ff);
}

/**************************************************************************/
void fz1_state::fz20m_io_map(address_map &map)
{
	fz10m_io_map(map);
	map(0x30, 0x3f).m("scsi:7:spc", FUNC(mb89352_device::map));
}

/**************************************************************************/
void fz1_state::subcpu_map(address_map &map)
{
	map(0x00, 0xff).rw(m_kbd, FUNC(msm6200_device::read), FUNC(msm6200_device::write));
}

/**************************************************************************/
static void fz1_floppies(device_slot_interface &device)
{
	device.option_add("35hd", PANA_JU_386);
}

/**************************************************************************/
static void floppy_formats(format_registration &fr)
{
	fr.add(FLOPPY_FZ1_FORMAT);
	fr.add(FLOPPY_HFE_FORMAT);
}

/**************************************************************************/
void fz1_state::fz10m(machine_config &config)
{
	V50(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &fz1_state::maincpu_map);
	m_maincpu->set_addrmap(AS_IO, &fz1_state::fz10m_io_map);
	m_maincpu->set_tclk(2_MHz_XTAL);
	m_maincpu->tout2_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ7);

	midi_port_device &mdin(MIDI_PORT(config, "mdin", midiin_slot, "midiin"));
	mdin.rxd_handler().set(m_maincpu, FUNC(v50_device::rxd_w));

	MIDI_PORT(config, "mdout", midiout_slot, "midiout");
	m_maincpu->txd_handler_cb().set("mdout", FUNC(midi_port_device::write_txd));

	MIDI_PORT(config, "mdthru", midiout_slot, "midiout");
	mdin.rxd_handler().append("mdthru", FUNC(midi_port_device::write_txd));

	// RAM is fixed on FZ-10M/20M but expandable on FZ-1
	RAM(config, m_ram).set_default_size("2M");
	ADDRESS_MAP_BANK(config, m_ram_bank).set_options(ENDIANNESS_LITTLE, 16, 21, 0x10000);

	I8255(config, m_io[0]);
	// port A: parallel port data bus
	m_io[0]->in_pb_callback().set_ioport("IO1_PB");
	// port C: parallel port control

	I8255(config, m_io[1]);
	m_io[1]->out_pa_callback().set_ioport("IO2_PA");
	m_io[1]->out_pb_callback().set_ioport("IO2_PB");
	m_io[1]->in_pc_callback().set(FUNC(fz1_state::keys_r));

	UPD72065(config, m_fdc, 16_MHz_XTAL / 4);
	m_fdc->set_select_lines_connected(false);
	// WP/TS pin is only used for write protect signal; firmware requires TS low even for DSHD disks
	m_fdc->set_ts_line_connected(false);

	FLOPPY_CONNECTOR(config, m_floppy, fz1_floppies, "35hd", floppy_formats); // leave sound off because drive motor is almost always running

	HD44352(config, m_lcdc, 2_MHz_XTAL); // actually HD44350 + 2x HD44251

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(m_lcdc, FUNC(hd44352_device::screen_update));
	screen.set_size(96, 64);
	screen.set_visarea(0, 96-1, 0, 64-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	FZ_PCM(config, m_pcm, 13.824_MHz_XTAL);
	m_pcm->irq_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ2);
	for (int i = 0; i < 8; i++)
	{
		SPEAKER(config, m_line_out[i]).front_center();
		m_pcm->add_route(i, m_filter[i], 1.0);

		FILTER_BIQUAD(config, m_filter[i]);
		// TODO: these are switched-capacitor lowpass filters
		m_filter[i]->add_route(0, m_line_out[i], 1.0 / 8);
	}
}

/**************************************************************************/
void fz1_state::fz1(machine_config &config)
{
	fz10m(config);
	m_maincpu->set_addrmap(AS_IO, &fz1_state::fz1_io_map);

	// expansion slot on a stock unit only allows 2MB, but hardware and firmware support up to 4MB
	m_ram->set_extra_options("1M,3M,4M");
	m_ram_bank->set_addr_width(22);

	I8049(config, m_subcpu, 8.96_MHz_XTAL);
	m_subcpu->set_addrmap(AS_IO, &fz1_state::subcpu_map);
	m_subcpu->p2_out_cb().set(FUNC(fz1_state::sub_p2_w));
	m_subcpu->t0_in_cb().set(FUNC(fz1_state::sync49_r));

	MSM6200(config, m_kbd, 2.47_MHz_XTAL);
	m_kbd->irq_cb().set_inputline(m_subcpu, MCS48_INPUT_IRQ);
}

/**************************************************************************/
static void scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
	device.option_add_internal("mb89352", MB89352);
}

/**************************************************************************/
void fz1_state::fz20m(machine_config &config)
{
	fz10m(config);
	m_maincpu->set_addrmap(AS_IO, &fz1_state::fz20m_io_map);
	m_maincpu->out_hreq_cb().set(m_maincpu, FUNC(v50_device::hack_w));
	m_maincpu->in_memr_cb().set(FUNC(fz1_state::mem_r));
	m_maincpu->out_memw_cb().set(FUNC(fz1_state::mem_w));
	m_maincpu->in_ior_cb<1>().set("scsi:7:spc", FUNC(mb89352_device::dma_r));
	m_maincpu->out_iow_cb<1>().set("scsi:7:spc", FUNC(mb89352_device::dma_w));

	// note: loading from HD requires running "HDD Operater" [sic] from FL-D1 program disk
	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:1", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:7").option_set("spc", MB89352).machine_config(
		[this](device_t *device)
		{
			mb89352_device &spc = downcast<mb89352_device &>(*device);

			spc.set_clock(8_MHz_XTAL);
			spc.out_irq_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ3);
			spc.out_dreq_callback().set(m_maincpu, FUNC(v50_device::dreq_w<1>));
		});
}

/**************************************************************************/
void fz1_state::machine_start()
{
	m_led.resolve();

	m_key_sel = 0;
	m_adc_sel = 0;
	m_adc_value = 0;
	m_sub_p2 = 0;

	m_ram_bank->space().install_ram(0, m_ram->mask(), m_ram->pointer());
	m_pcm->space().install_ram(0, m_ram->mask() >> 1, m_ram->pointer());

	m_fdc->set_rate(500000);
	m_fdc->set_floppy(m_floppy->get_device());

	save_item(NAME(m_key_sel));
	save_item(NAME(m_adc_sel));
	save_item(NAME(m_adc_value));
	save_item(NAME(m_sub_p2));

	save_item(NAME(m_lcd_ready));
	save_item(NAME(m_lcd_nibble));
	save_item(NAME(m_lcd_data));
	save_item(NAME(m_lcd_data_phase));

	save_item(NAME(m_dca_level));
}

/**************************************************************************/
void fz1_state::machine_reset()
{
	m_lcd_ready = 1;
	m_lcd_nibble = 0;
	m_lcd_data = 0;
	m_lcd_data_phase = 0;
}

/**************************************************************************/
void fz1_state::fdc_control_w(u8 data)
{
	m_fdc->tc_w(BIT(data, 0));

	auto *dev = m_floppy->get_device();
	if (dev)
	{
		dev->mon_w(BIT(~data, 1));
		dev->inuse_w(BIT(data, 2));
	}
}

/**************************************************************************/
u8 fz1_state::keys_r()
{
	return m_keys[m_key_sel & 3]->read();
}

/**************************************************************************/
u8 fz1_state::adc_latch_r()
{
	if (!machine().side_effects_disabled())
		m_adc_value = m_analog[m_adc_sel & 7].read_safe(0);
	return 0;
}

/**************************************************************************/
u8 fz1_state::subcpu_r()
{
	if (!machine().side_effects_disabled())
	{
		m_sync49 = 0;
		m_maincpu->set_input_line(INPUT_LINE_IRQ6, CLEAR_LINE);
	}

	return m_subcpu->p1_r();
}

/**************************************************************************/
void fz1_state::sub_p2_w(u8 data)
{
	if (BIT(~data & m_sub_p2, 6))
		m_maincpu->set_input_line(INPUT_LINE_IRQ6, ASSERT_LINE);

	if (BIT(~data & m_sub_p2, 7))
		m_sync49 = 1;

	m_sub_p2 = data;
}

/**************************************************************************/
int fz1_state::cont49_r()
{
	return BIT(m_sub_p2, 5);
}

/**************************************************************************/
void fz1_state::gal_w(u8 data)
{
	m_lcd_ready = BIT(data, 7);

	switch (data & 0x7f)
	{
	case 0x1f: // command start
		m_lcdc->control_write(0x87);
		m_lcd_data_phase = 0;
		break;

	case 0x10: // data start
		m_lcdc->control_write(0x86);
		m_lcd_data_phase = 1;
		break;

	default:
		if (!m_lcd_ready)
		{
			if (!m_lcd_nibble)
			{
				if (!m_lcd_data_phase)
					m_lcd_data = (data & 0xf);
				else
					m_lcd_data = (data << 4);
			}
			else
			{
				if (!m_lcd_data_phase)
					m_lcd_data |= (data << 4);
				else
					m_lcd_data |= (data & 0xf);

				m_lcdc->data_write(m_lcd_data);
			}

			m_lcd_nibble ^= 1;
		}
	}
}

/**************************************************************************/
void fz1_state::led_w(u8 data)
{
	for (int i = 0; i < m_led.size(); i++)
		m_led[i] = BIT(~data, i);
}

/**************************************************************************/
void fz1_state::dca_w(offs_t offset, u8 data)
{
	const offs_t num = offset & 7;
	if (BIT(offset, 3))
		m_dca_level[num] = (m_dca_level[num] & 0xff) | ((data & 3) << 8);
	else
		m_dca_level[num] = (m_dca_level[num] & 0x300) | data;
	m_filter[num]->set_output_gain(ALL_OUTPUTS, m_dca_level[num] / 1023.0);
}

/**************************************************************************/
ROM_START( fz1 )
	ROM_REGION(0x10000, "maincpu", 0) // "ROM ver.[B]"
	ROM_LOAD16_BYTE( "fz1s.bin", 0x00000, 0x08000, CRC(b0ba313d) SHA1(45a3660d708f0a584f9f61d04e205a96688f0950) )
	ROM_LOAD16_BYTE( "fz2s.bin", 0x00001, 0x08000, CRC(57098176) SHA1(ceeb4de4a3df35430497bee37ec73f1b12042729) )

	ROM_REGION(0x800, "subcpu", 0) // this dump is actually uPD80C49HC-187 from the HT-6000, though it appears functionally identical
	ROM_LOAD("upd8049hc-672.bin", 0x000, 0x800, BAD_DUMP CRC(47b47af7) SHA1(8f0515f95dcc6e224a8a59e0c2cd7ddb4796e34e))

	ROM_REGION( 0x800, "lcdc", 0 ) // taken from pb1000, may not be completely identical
	ROM_LOAD( "charset.bin", 0x000, 0x800, BAD_DUMP CRC(7f144716) SHA1(a02f1ecc6dc0ac55b94f00931d8f5cb6b9ffb7b4))
ROM_END

ROM_START( fz10m )
	ROM_REGION(0x10000, "maincpu", 0) // "ROM ver.[B]"
	ROM_LOAD16_BYTE( "mz1b.bin", 0x00000, 0x08000, CRC(3e16943a) SHA1(d754b60901f848b43ee256c2d3f1dfeee031d943) )
	ROM_LOAD16_BYTE( "mz2b.bin", 0x00001, 0x08000, CRC(1db8400d) SHA1(01edf16839afdd78820d7a0f8a410882c54889a9) )

	ROM_REGION( 0x800, "lcdc", 0 ) // taken from pb1000, may not be completely identical
	ROM_LOAD( "charset.bin", 0x000, 0x800, BAD_DUMP CRC(7f144716) SHA1(a02f1ecc6dc0ac55b94f00931d8f5cb6b9ffb7b4))
ROM_END

ROM_START( fz20m )
	ROM_REGION(0x10000, "maincpu", 0) // "ROM hdd.[C]"
	ROM_LOAD16_BYTE( "fz20m_l.bin", 0x00000, 0x08000, CRC(e16c9ccf) SHA1(aa2c5fc0465cb6c9b0c7a7ab31606ad1703327ee) )
	ROM_LOAD16_BYTE( "fz20m_h.bin", 0x00001, 0x08000, CRC(be272cad) SHA1(09d5c901e2f8905415789595a69e7d4166c0e2d2) )

	ROM_REGION( 0x800, "lcdc", 0 ) // taken from pb1000, may not be completely identical
	ROM_LOAD( "charset.bin", 0x000, 0x800, BAD_DUMP CRC(7f144716) SHA1(a02f1ecc6dc0ac55b94f00931d8f5cb6b9ffb7b4))
ROM_END

} // anonymous namespace

SYST( 1987, fz1,   0,      0, fz1,   fz1,   fz1_state, empty_init, "Casio", "FZ-1 Digital Sampling Synthesizer",          MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
SYST( 1987, fz10m, fz1,    0, fz10m, fz10m, fz1_state, empty_init, "Casio", "FZ-10M Digital Sampling Synthesizer Module", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
SYST( 1989, fz20m, fz1,    0, fz20m, fz10m, fz1_state, empty_init, "Casio", "FZ-20M Digital Sampling Synthesizer Module", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



galaxy.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Krzysztof Strzecha, Miodrag Milanovic
/***************************************************************************
Galaksija driver by Krzysztof Strzecha and Miodrag Milanovic

22/05/2008 Tape support added (Miodrag Milanovic)
21/05/2008 Galaksija plus initial support (Miodrag Milanovic)
20/05/2008 Added real video implementation (Miodrag Milanovic)
18/04/2005 Possibilty to disable ROM 2. 2k, 22k, 38k and 54k memory
       configurations added.
13/03/2005 Memory mapping improved. Palette corrected. Supprort for newer
           version of snapshots added. Lot of cleanups. Keyboard mapping
           corrected.
19/09/2002 malloc() replaced by image_malloc().
15/09/2002 Snapshot loading fixed. Code cleanup.
31/01/2001 Snapshot loading corrected.
09/01/2001 Fast mode implemented (many thanks to Kevin Thacker).
07/01/2001 Keyboard corrected (still some keys unknown).
           Horizontal screen positioning in video subsystem added.
05/01/2001 Keyboard implemented (some keys unknown).
03/01/2001 Snapshot loading added.
01/01/2001 Preliminary driver.

ToDo:
- pacmanp not showing its hi-res graphics - get black screen
- is the hack in the video still needed? commenting it out made no difference.

***************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "formats/gtp_cas.h"
#include "imagedev/snapquik.h"
#include "imagedev/cassette.h"
#include "machine/ram.h"
#include "sound/ay8910.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

namespace {

class galaxy_state : public driver_device
{
public:
	galaxy_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_cassette(*this, "cassette")
		, m_ram(*this, RAM_TAG)
		, m_p_chargen(*this, "chargen")
		, m_io_keyboard(*this, "LINE%u", 0U)
	{ }

	void galaxy(machine_config &config);
	void galaxyp(machine_config &config);

	void init_galaxy();

private:
	uint8_t keyboard_r(offs_t offset);
	void latch_w(uint8_t data);
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	TIMER_CALLBACK_MEMBER(gal_video);
	IRQ_CALLBACK_MEMBER(irq_callback);
	void set_timer();
	void setup_snapshot (const uint8_t * data, uint32_t size);
	DECLARE_SNAPSHOT_LOAD_MEMBER(snapshot_cb);
	void galaxy_mem(address_map &map) ATTR_COLD;
	void galaxyp_io(address_map &map) ATTR_COLD;
	void galaxyp_mem(address_map &map) ATTR_COLD;

	int m_interrupts_enabled = 0;
	uint8_t m_latch_value = 0U;
	uint32_t m_gal_cnt = 0U;
	uint8_t m_code = 0U;
	uint8_t m_first = 0U;
	uint32_t m_start_addr = 0U;
	emu_timer *m_gal_video_timer = nullptr;
	bitmap_ind16 m_bitmap{};

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<cassette_image_device> m_cassette;
	optional_device<ram_device> m_ram;
	required_region_ptr<u8> m_p_chargen;
	required_ioport_array<8> m_io_keyboard;
};

/***************************************************************************
  I/O devices
***************************************************************************/

uint8_t galaxy_state::keyboard_r(offs_t offset)
{
	if (offset == 0)
	{
		double level = m_cassette->input();
		return (level >  -0.1) ? 0xff : 0xfe;
	}
	else
		return m_io_keyboard[(offset>>3) & 0x07]->read() & (0x01<<(offset & 0x07)) ? 0xfe : 0xff;
}

void galaxy_state::latch_w(uint8_t data)
{
	double val = (BIT(data,6) ^ BIT(data,2)) ? 0 : BIT(data,6) ? -1.0f : +1.0f;
	m_latch_value = data;
	m_cassette->output(val);
}

/***************************************************************************
  Interrupts
***************************************************************************/

IRQ_CALLBACK_MEMBER(galaxy_state::irq_callback)
{
	set_timer();
	m_interrupts_enabled = true;
	return 0xff;
}

/***************************************************************************
  Snapshot files (GAL)
***************************************************************************/

#define GALAXY_SNAPSHOT_V1_SIZE 8268
#define GALAXY_SNAPSHOT_V2_SIZE 8244

void galaxy_state::setup_snapshot(const uint8_t * data, uint32_t size)
{
	switch (size)
	{
		case GALAXY_SNAPSHOT_V1_SIZE:
			m_maincpu->set_state_int(Z80_AF,   data[0x00] | data[0x01] << 8);
			m_maincpu->set_state_int(Z80_BC,   data[0x04] | data[0x05] << 8);
			m_maincpu->set_state_int(Z80_DE,   data[0x08] | data[0x09] << 8);
			m_maincpu->set_state_int(Z80_HL,   data[0x0c] | data[0x0d] << 8);
			m_maincpu->set_state_int(Z80_IX,   data[0x10] | data[0x11] << 8);
			m_maincpu->set_state_int(Z80_IY,   data[0x14] | data[0x15] << 8);
			m_maincpu->set_state_int(Z80_PC,   data[0x18] | data[0x19] << 8);
			m_maincpu->set_state_int(Z80_SP,   data[0x1c] | data[0x1d] << 8);
			m_maincpu->set_state_int(Z80_AF2,  data[0x20] | data[0x21] << 8);
			m_maincpu->set_state_int(Z80_BC2,  data[0x24] | data[0x25] << 8);
			m_maincpu->set_state_int(Z80_DE2,  data[0x28] | data[0x29] << 8);
			m_maincpu->set_state_int(Z80_HL2,  data[0x2c] | data[0x2d] << 8);
			m_maincpu->set_state_int(Z80_IFF1, data[0x30]);
			m_maincpu->set_state_int(Z80_IFF2, data[0x34]);
			m_maincpu->set_state_int(Z80_HALT, data[0x38]);
			m_maincpu->set_state_int(Z80_IM,   data[0x3c]);
			m_maincpu->set_state_int(Z80_I,    data[0x40]);
			m_maincpu->set_state_int(Z80_R,    (data[0x44] & 0x7f) | (data[0x48] & 0x80));

			memcpy (m_ram->pointer(), data + 0x084c, (m_ram->size() < 0x1800) ? m_ram->size() : 0x1800);

			break;
		case GALAXY_SNAPSHOT_V2_SIZE:
			m_maincpu->set_state_int(Z80_AF,   data[0x00] | data[0x01] << 8);
			m_maincpu->set_state_int(Z80_BC,   data[0x02] | data[0x03] << 8);
			m_maincpu->set_state_int(Z80_DE,   data[0x04] | data[0x05] << 8);
			m_maincpu->set_state_int(Z80_HL,   data[0x06] | data[0x07] << 8);
			m_maincpu->set_state_int(Z80_IX,   data[0x08] | data[0x09] << 8);
			m_maincpu->set_state_int(Z80_IY,   data[0x0a] | data[0x0b] << 8);
			m_maincpu->set_state_int(Z80_PC,   data[0x0c] | data[0x0d] << 8);
			m_maincpu->set_state_int(Z80_SP,   data[0x0e] | data[0x0f] << 8);
			m_maincpu->set_state_int(Z80_AF2,  data[0x10] | data[0x11] << 8);
			m_maincpu->set_state_int(Z80_BC2,  data[0x12] | data[0x13] << 8);
			m_maincpu->set_state_int(Z80_DE2,  data[0x14] | data[0x15] << 8);
			m_maincpu->set_state_int(Z80_HL2,  data[0x16] | data[0x17] << 8);

			m_maincpu->set_state_int(Z80_IFF1, data[0x18] & 0x01);
			m_maincpu->set_state_int(Z80_IFF2, (uint64_t)0);

			m_maincpu->set_state_int(Z80_HALT, (uint64_t)0);

			m_maincpu->set_state_int(Z80_IM,   (data[0x18] >> 1) & 0x03);

			m_maincpu->set_state_int(Z80_I,    data[0x19]);
			m_maincpu->set_state_int(Z80_R,    data[0x1a]);

			memcpy (m_ram->pointer(), data + 0x0834, (m_ram->size() < 0x1800) ? m_ram->size() : 0x1800);

			break;
	}

	m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
}

SNAPSHOT_LOAD_MEMBER(galaxy_state::snapshot_cb)
{
	uint32_t const snapshot_size = image.length();
	switch (snapshot_size)
	{
		case GALAXY_SNAPSHOT_V1_SIZE:
		case GALAXY_SNAPSHOT_V2_SIZE:
			break;
		default:
			return std::make_pair(
					image_error::INVALIDLENGTH,
					util::string_format("Unsupported image size (must be %u or %u bytes)", GALAXY_SNAPSHOT_V1_SIZE, GALAXY_SNAPSHOT_V2_SIZE));
	}

	std::vector<uint8_t> snapshot_data(snapshot_size);
	image.fread(&snapshot_data[0], snapshot_size);

	setup_snapshot(&snapshot_data[0], snapshot_size);

	return std::make_pair(std::error_condition(), std::string());
}

/***************************************************************************
  Driver Initialization
***************************************************************************/

void galaxy_state::init_galaxy()
{
	address_space &space = m_maincpu->space(AS_PROGRAM);
	space.install_ram( 0x2800, 0x2800 + m_ram->size() - 1, m_ram->pointer());

	if (m_ram->size() < (6 + 48) * 1024)
		space.nop_readwrite( 0x2800 + m_ram->size(), 0xffff);
}

/***************************************************************************
  Machine Initialization
***************************************************************************/

void galaxy_state::machine_reset()
{
	m_interrupts_enabled = true;
}

TIMER_CALLBACK_MEMBER(galaxy_state::gal_video)
{
	address_space &space = m_maincpu->space(AS_PROGRAM);
	if (m_interrupts_enabled == true)
	{
		uint8_t dat = BIT(m_latch_value, 2, 4);
		if ((m_gal_cnt >= 48 * 2) && (m_gal_cnt < 48 * 210))  // display on screen just m_first 208 lines
		{
			uint16_t addr = (m_maincpu->state_int(Z80_I) << 8) | m_maincpu->state_int(Z80_R) | (~m_latch_value & 0x80);
			if (!BIT(m_latch_value, 1)) // bit 2 latch represents mode
			{
				// Text mode
				if (m_first == 0 && (m_maincpu->state_int(Z80_R) & 0x1f) == 0)
				{
					// Due to a fact that on real processor latch value is set at
					// the end of last cycle we need to skip display of double
					// m_first char in each row
					m_code = 0x00;
					m_first = 1;
				}
				else
				{
					m_code = space.read_byte(addr) & 0xbf;
					m_code += (m_code & 0x80) >> 1;
					m_code = m_p_chargen[(m_code & 0x7f) +(dat << 7 )] ^ 0xff;
					m_first = 0;
				}
				int y = m_gal_cnt / 48 - 2;
				int x = (m_gal_cnt % 48) * 8;

				m_bitmap.pix(y, x++ ) = BIT(m_code, 0);
				m_bitmap.pix(y, x++ ) = BIT(m_code, 1);
				m_bitmap.pix(y, x++ ) = BIT(m_code, 2);
				m_bitmap.pix(y, x++ ) = BIT(m_code, 3);
				m_bitmap.pix(y, x++ ) = BIT(m_code, 4);
				m_bitmap.pix(y, x++ ) = BIT(m_code, 5);
				m_bitmap.pix(y, x++ ) = BIT(m_code, 6);
				m_bitmap.pix(y, x   ) = BIT(m_code, 7);
			}
			else
			{ // Graphics mode
				if (m_first < 4 && (m_maincpu->state_int(Z80_R) & 0x1f) == 0)
				{
					// Due to a fact that on real processor latch value is set at
					// the end of last cycle we need to skip display of 4 times
					// m_first char in each row
					m_code = 0x00;
					m_first++;
				}
				else
				{
					m_code = space.read_byte(addr) ^ 0xff;
					m_first = 0;
				}
				int y = m_gal_cnt / 48 - 2;
				int x = (m_gal_cnt % 48) * 8;

				/* hack - until calc of R is fixed in Z80 */
				if (x == 11 * 8 && y == 0)
					m_start_addr = addr;

				if ((x / 8 >= 11) && (x / 8 < 44))
					m_code = space.read_byte(m_start_addr + y * 32 + (m_gal_cnt % 48) - 11) ^ 0xff;
				else
					m_code = 0x00;
				/* end of hack */

				m_bitmap.pix(y, x++ ) = BIT(m_code, 0);
				m_bitmap.pix(y, x++ ) = BIT(m_code, 1);
				m_bitmap.pix(y, x++ ) = BIT(m_code, 2);
				m_bitmap.pix(y, x++ ) = BIT(m_code, 3);
				m_bitmap.pix(y, x++ ) = BIT(m_code, 4);
				m_bitmap.pix(y, x++ ) = BIT(m_code, 5);
				m_bitmap.pix(y, x++ ) = BIT(m_code, 6);
				m_bitmap.pix(y, x   ) = BIT(m_code, 7);
			}
		}
		m_gal_cnt++;
	}
}

void galaxy_state::set_timer()
{
	m_gal_cnt = 0;
	m_gal_video_timer->adjust(attotime::zero, 0, attotime::from_hz(6144000 / 8));
}

void galaxy_state::machine_start()
{
	m_gal_cnt = 0;

	m_gal_video_timer = timer_alloc(FUNC(galaxy_state::gal_video), this);
	m_gal_video_timer->adjust(attotime::zero, 0, attotime::never);

	m_screen->register_screen_bitmap(m_bitmap);

	save_item(NAME(m_interrupts_enabled));
	save_item(NAME(m_latch_value));
	save_item(NAME(m_gal_cnt));
	save_item(NAME(m_code));
	save_item(NAME(m_first));
	save_item(NAME(m_start_addr));
}

uint32_t galaxy_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	m_gal_video_timer->adjust(attotime::zero, 0, attotime::never);
	if (m_interrupts_enabled == false)
	{
		const rectangle black_area(0, 384 - 1, 0, 208 - 1);
		m_bitmap.fill(0, black_area);
	}
	m_interrupts_enabled = false;
	copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
	return 0;
}

void galaxy_state::galaxyp_io(address_map &map)
{
	map.global_mask(0x01);
	map.unmap_value_high();
	map(0x00, 0x00).w("ay8910", FUNC(ay8910_device::address_w));
	map(0x01, 0x01).w("ay8910", FUNC(ay8910_device::data_w));
}


void galaxy_state::galaxy_mem(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x2000, 0x2037).mirror(0x07c0).r(FUNC(galaxy_state::keyboard_r));
	map(0x2038, 0x203f).mirror(0x07c0).w(FUNC(galaxy_state::latch_w));
	// see init_galaxy for ram placement
}

void galaxy_state::galaxyp_mem(address_map &map)
{
	map(0x0000, 0x0fff).rom(); // ROM A
	map(0x1000, 0x1fff).rom(); // ROM B
	map(0x2000, 0x2037).mirror(0x07c0).r(FUNC(galaxy_state::keyboard_r));
	map(0x2038, 0x203f).mirror(0x07c0).w(FUNC(galaxy_state::latch_w));
	map(0x2800, 0xdfff).ram();
	map(0xe000, 0xefff).rom().region("maincpu",0x2000); // ROM C
	map(0xf000, 0xffff).rom().region("maincpu",0x3000); // ROM D
}

/* 2008-05 FP:
Small note about natural keyboard support. Currently:
- "List" is mapped to 'ESC'
- "Break" is mapped to 'F1'
- "Repeat" is mapped to 'F2'                           */

static INPUT_PORTS_START (galaxy)
	PORT_START("LINE0")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)       PORT_CHAR('A')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)       PORT_CHAR('B')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)       PORT_CHAR('C')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)       PORT_CHAR('D')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)       PORT_CHAR('E')
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)       PORT_CHAR('F')
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)       PORT_CHAR('G')

	PORT_START("LINE1")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)       PORT_CHAR('H')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)       PORT_CHAR('I')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)       PORT_CHAR('J')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)       PORT_CHAR('K')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)       PORT_CHAR('L')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)       PORT_CHAR('M')
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)       PORT_CHAR('N')
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)       PORT_CHAR('O')

	PORT_START("LINE2")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)       PORT_CHAR('P')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)       PORT_CHAR('Q')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)       PORT_CHAR('R')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)       PORT_CHAR('S')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)       PORT_CHAR('T')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)       PORT_CHAR('U')
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)       PORT_CHAR('V')
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)       PORT_CHAR('W')

	PORT_START("LINE3")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)       PORT_CHAR('X')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)       PORT_CHAR('Y')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)       PORT_CHAR('Z')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)      PORT_CHAR(UCHAR_MAMEKEY(UP))
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)    PORT_CHAR(UCHAR_MAMEKEY(DOWN))
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)    PORT_CHAR(UCHAR_MAMEKEY(LEFT))
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)   PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)   PORT_CHAR(' ')

	PORT_START("LINE4")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)       PORT_CHAR('0') PORT_CHAR('_')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)       PORT_CHAR('1') PORT_CHAR('!')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)       PORT_CHAR('2') PORT_CHAR('"')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)       PORT_CHAR('3') PORT_CHAR('#')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)       PORT_CHAR('4') PORT_CHAR('$')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)       PORT_CHAR('5') PORT_CHAR('%')
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)       PORT_CHAR('6') PORT_CHAR('&')
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)       PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("LINE5")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)       PORT_CHAR('8') PORT_CHAR('(')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)       PORT_CHAR('9') PORT_CHAR(')')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)   PORT_CHAR(';') PORT_CHAR('+')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)   PORT_CHAR(':') PORT_CHAR('*')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)   PORT_CHAR(',') PORT_CHAR('<')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)  PORT_CHAR('=') PORT_CHAR('-')
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)    PORT_CHAR('.') PORT_CHAR('>')
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)   PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("LINE6")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)   PORT_CHAR(13)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_PAUSE) PORT_CHAR(UCHAR_MAMEKEY(F1))
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Repeat") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(F2))
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Delete") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("List") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("LINE7")
INPUT_PORTS_END

/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 16,                  /* 8 x 16 characters */
	128,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	/* y offsets */
	{ 0, 1*128*8, 2*128*8, 3*128*8, 4*128*8, 5*128*8, 6*128*8, 7*128*8, 8*128*8, 9*128*8, 10*128*8, 11*128*8, 12*128*8, 13*128*8, 14*128*8, 15*128*8 },
	8                   /* every char takes 1 x 16 bytes */
};

static GFXDECODE_START( gfx_galaxy )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END


void galaxy_state::galaxy(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 6'144'000 / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &galaxy_state::galaxy_mem);
	m_maincpu->set_vblank_int("screen", FUNC(galaxy_state::irq0_line_hold));
	m_maincpu->set_irq_acknowledge_callback(FUNC(galaxy_state::irq_callback));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_palette("palette");
	m_screen->set_size(384, 212);
	m_screen->set_visarea(0, 384-1, 0, 208-1);
	m_screen->set_screen_update(FUNC(galaxy_state::screen_update));

	GFXDECODE(config, "gfxdecode", "palette", gfx_galaxy);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* snapshot */
	SNAPSHOT(config, "snapshot", "gal").set_load_callback(FUNC(galaxy_state::snapshot_cb));

	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(gtp_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("galaxy_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("galaxy");

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("6K").set_extra_options("2K,22K,38K,54K");
}

void galaxy_state::galaxyp(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 6'144'000 / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &galaxy_state::galaxyp_mem);
	m_maincpu->set_addrmap(AS_IO, &galaxy_state::galaxyp_io);
	m_maincpu->set_vblank_int("screen", FUNC(galaxy_state::irq0_line_hold));
	m_maincpu->set_irq_acknowledge_callback(FUNC(galaxy_state::irq_callback));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_palette("palette");
	m_screen->set_size(384, 208);
	m_screen->set_visarea(0, 384-1, 0, 208-1);
	m_screen->set_screen_update(FUNC(galaxy_state::screen_update));

	GFXDECODE(config, "gfxdecode", "palette", gfx_galaxy);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* snapshot */
	SNAPSHOT(config, "snapshot", "gal").set_load_callback(FUNC(galaxy_state::snapshot_cb));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	ay8910_device &ay(AY8910(config, "ay8910", 6'144'000 / 4));
	ay.add_route(ALL_OUTPUTS, "mono", 0.50);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(gtp_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("galaxy_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("galaxy");
}

// Original Galaksija kit came with v28 version of ROM A
// at end of 1984 ROM B appeared and people patched their ROM A v28
// to make it auto boot ROM B
// later official v29 was made to auto boot ROM B
// chargen also include prompt char with logo of Mipro, Voja Antonic company
// Elektronika inzinjering have different chargen, modified to include logo char
ROM_START (galaxy)
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS("v29")
	ROM_SYSTEM_BIOS( 0, "v29",  "ROM A v29 + ROM B" )
	ROMX_LOAD( "rom_a_v29.dd8", 0x0000, 0x1000, CRC(e6853bc1) SHA1(aea7a4c0c7ffe1f212f7b9faecfd728862ac6904), ROM_BIOS(0) )
	ROMX_LOAD( "rom_b_v5.dd9",  0x1000, 0x1000, CRC(5dc5a100) SHA1(5d5ab4313a2d0effe7572bb129193b64cab002c1), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v29ei","ROM A v29 + ROM B Elektronika inzinjering" )
	ROMX_LOAD( "rom_a_v29.dd8", 0x0000, 0x1000, CRC(e6853bc1) SHA1(aea7a4c0c7ffe1f212f7b9faecfd728862ac6904), ROM_BIOS(1) )
	ROMX_LOAD( "rom_b_v5.dd9",  0x1000, 0x1000, CRC(5dc5a100) SHA1(5d5ab4313a2d0effe7572bb129193b64cab002c1), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v28p", "ROM A v28 + ROM B auto" )
	ROMX_LOAD( "rom_a_v28p.dd8",0x0000, 0x1000, CRC(dc970a32) SHA1(dfc92163654a756b70f5a446daf49d7534f4c739), ROM_BIOS(2) )
	ROMX_LOAD( "rom_b_v5.dd9",  0x1000, 0x1000, CRC(5dc5a100) SHA1(5d5ab4313a2d0effe7572bb129193b64cab002c1), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "v28b", "ROM A v28 + ROM B" )
	ROMX_LOAD( "rom_a_v28.dd8", 0x0000, 0x1000, CRC(365f3e24) SHA1(ffc6bf2ec09eabdad76604a63f5dd697c30c4358), ROM_BIOS(3) )
	ROMX_LOAD( "rom_b_v5.dd9",  0x1000, 0x1000, CRC(5dc5a100) SHA1(5d5ab4313a2d0effe7572bb129193b64cab002c1), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "v28a", "ROM A v28 only" )
	ROMX_LOAD( "rom_a_v28.dd8", 0x0000, 0x1000, CRC(365f3e24) SHA1(ffc6bf2ec09eabdad76604a63f5dd697c30c4358), ROM_BIOS(4) )

	ROM_REGION( 0x0800, "chargen", 0 )
	ROMX_LOAD( "chr_mipro.dd3", 0x0000, 0x0800, CRC(fd77b6d2) SHA1(cc73b0386b84383b4841e58a1c328cb67b0121d8), ROM_BIOS(0) )
	ROMX_LOAD( "chr_eling.dd3", 0x0000, 0x0800, CRC(5c3b5bb5) SHA1(19429a61dc5e55ddec3242a8f695e06dd7961f88), ROM_BIOS(1) )
	ROMX_LOAD( "chr_mipro.dd3", 0x0000, 0x0800, CRC(fd77b6d2) SHA1(cc73b0386b84383b4841e58a1c328cb67b0121d8), ROM_BIOS(2) )
	ROMX_LOAD( "chr_mipro.dd3", 0x0000, 0x0800, CRC(fd77b6d2) SHA1(cc73b0386b84383b4841e58a1c328cb67b0121d8), ROM_BIOS(3) )
	ROMX_LOAD( "chr_mipro.dd3", 0x0000, 0x0800, CRC(fd77b6d2) SHA1(cc73b0386b84383b4841e58a1c328cb67b0121d8), ROM_BIOS(4) )
ROM_END

// Galaksija plus was hardware modification of original, and could not be considered extension board since it
// was not using expansion port and was also requiring changes on main computer board in order to work.
// It was on separate board and it also included RAM expansion and AY sound generator.
// Instuctions to build also included how to patch ROM A to make it auto boot ROM C.
ROM_START (galaxyp)
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS("v29c")
	ROM_SYSTEM_BIOS( 0, "v29c", "ROM A v29 boot ROM C" )
	ROMX_LOAD( "rom_a_v29c.dd8", 0x0000, 0x1000, CRC(5cb8fb2a) SHA1(fdddae2b08d0dc81eb6191a92e60ac411d8150e9), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v29",  "ROM A v29" )
	ROMX_LOAD( "rom_a_v29.dd8",  0x0000, 0x1000, CRC(e6853bc1) SHA1(aea7a4c0c7ffe1f212f7b9faecfd728862ac6904), ROM_BIOS(1) )

	ROM_LOAD( "rom_b_v5.dd9",    0x1000, 0x1000, CRC(5dc5a100) SHA1(5d5ab4313a2d0effe7572bb129193b64cab002c1) )
	ROM_LOAD( "rom_c.bin",       0x2000, 0x1000, CRC(d4cfab14) SHA1(b507b9026844eeb757547679907394aa42055eee) )

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "chr_mipro.dd3", 0x0000, 0x0800, CRC(fd77b6d2) SHA1(cc73b0386b84383b4841e58a1c328cb67b0121d8) )
ROM_END

} // Anonymous namespace

/*    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS         INIT          COMPANY                                   FULLNAME */
COMP( 1983, galaxy,  0,      0,      galaxy,  galaxy,  galaxy_state, init_galaxy,  "Voja Antonic / Elektronika inzenjering", "Galaksija",      MACHINE_SUPPORTS_SAVE )
COMP( 1985, galaxyp, galaxy, 0,      galaxyp, galaxy,  galaxy_state, empty_init,   "Nenad Dunjic",                           "Galaksija plus", MACHINE_SUPPORTS_SAVE )



galeb.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

Galeb driver by Miodrag Milanovic

2008-02-22 Preliminary driver.
2008-02-23 Sound support added.
2008-03-01 Updated to work with latest SVN code

Driver is based on work of Josip Perusanec.

Galeb (seagull) based on the OSI UK101. Due to its expense it is quite rare.
It was succeeded by the Orao (eagle).

All commands in Monitor and in Basic must be in UPPERCASE. So, the very first
thing to do is to press ^A to enter caps mode.

Commands:
A nnnn           Mini-assembler
B
C aaaabbbb       show checksum of memory from a to b
E aaaabbbb       Hex dump from a to b
F aaaabbbbcc     Fill memory from a to b with c
L                Load tape
M nnnn           Modify memory starting at byte nnnn (enter to escape)
P
Q ccccaaaabbbb   Copy memory range a to b, over to c
R
S
T
U nnnn           Go to nnnn
X nnnn           disassemble
$
^A               caps lock
^B               Start Basic
^G               keyclick
^L               clear screen
Alt\


Cassette:
- Unable to locate a schematic, but it appears that a 6850 is the uart.
  The UK101 used a 6850 and Kansas City tape encoding, and so it is assumed
  that this computer is the same.

Screen:
- The T command can cause the system to expand the video to 4k
  instead of 1k. Therefore the writing goes off the bottom and doesn't scroll.
  It is not known if this is intentional, or a bug. The video routine has been
  modified to cope, in case it's a real feature. (T 0000 0022 is an example)

ToDo:
- BASIC SAVE command is weird... need instructions
- Therefore, cassette is considered to be not working.

****************************************************************************/


#include "emu.h"
#include "cpu/m6502/m6502.h"
#include "machine/clock.h"
#include "screen.h"
#include "speaker.h"
#include "machine/6850acia.h"
#include "machine/timer.h"
#include "imagedev/cassette.h"
#include "sound/dac.h"
#include "emupal.h"

namespace {

class galeb_state : public driver_device
{
public:
	galeb_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_ram(*this, "mainram")
		, m_vram(*this, "videoram")
		, m_maincpu(*this, "maincpu")
		, m_gfxdecode(*this, "gfxdecode")
		, m_palette(*this, "palette")
		, m_io_keyboard(*this, "LINE%u", 0U)
		, m_cass(*this, "cassette")
		, m_acia(*this, "acia")
		, m_dac(*this, "dac")
	{ }

	void galeb(machine_config &config);

private:
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_w);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);
	void dac_w(u8 data);
	u8 keyboard_r(offs_t offset);
	u32 screen_update_galeb(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	u8 m_cass_data[4]{};
	bool m_cassbit = false;
	bool m_cassold = false;
	required_shared_ptr<u8> m_ram;
	required_shared_ptr<u8> m_vram;
	required_device<cpu_device> m_maincpu;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<palette_device> m_palette;
	required_ioport_array<8> m_io_keyboard;
	required_device<cassette_image_device> m_cass;
	required_device<acia6850_device> m_acia;
	required_device<dac_1bit_device> m_dac;

	void mem_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	bool m_dac_state = false;
};

void galeb_state::machine_start()
{
	save_item(NAME(m_dac_state));
	save_item(NAME(m_cass_data));
	save_item(NAME(m_cassbit));
	save_item(NAME(m_cassold));
}

void galeb_state::machine_reset()
{
	m_dac_state = 0;
	m_dac->write(m_dac_state);
}

void galeb_state::dac_w(uint8_t data)
{
	m_dac_state ^= 1;
	m_dac->write(m_dac_state);
}

u8 galeb_state::keyboard_r(offs_t offset)
{
	return m_io_keyboard[offset]->read();
}

TIMER_DEVICE_CALLBACK_MEMBER( galeb_state::kansas_w )
{
	m_cass_data[3]++;

	if (m_cassbit != m_cassold)
	{
		m_cass_data[3] = 0;
		m_cassold = m_cassbit;
	}

	if (m_cassbit)
		m_cass->output(BIT(m_cass_data[3], 0) ? -1.0 : +1.0); // 2400Hz
	else
		m_cass->output(BIT(m_cass_data[3], 1) ? -1.0 : +1.0); // 1200Hz
}

TIMER_DEVICE_CALLBACK_MEMBER( galeb_state::kansas_r)
{
	/* cassette - turn 1200/2400Hz to a bit */
	m_cass_data[1]++;
	u8 cass_ws = (m_cass->input() > +0.03) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		m_acia->write_rxd((m_cass_data[1] < 12) ? 1 : 0);
		m_cass_data[1] = 0;
	}
}


/* Address maps */
void galeb_state::mem_map(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("mainram");
	map(0xb000, 0xbfdf).ram().share("videoram");
	map(0xbfe0, 0xbfe7).r(FUNC(galeb_state::keyboard_r));
	map(0xbfe0, 0xbfe0).w(FUNC(galeb_state::dac_w));
	map(0xbffe, 0xbfff).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xc000, 0xffff).rom().region("maincpu",0);
}

/* Input ports */
static INPUT_PORTS_START( galeb )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Delete") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("GR") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('^') PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down - Up") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(':')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('y') PORT_CHAR('Y')

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(U'đ') PORT_CHAR(U'Đ')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left - Right") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(U'š') PORT_CHAR(U'Š')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(U'ć') PORT_CHAR(U'Ć')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(U'ž') PORT_CHAR(U'Ž')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')

	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(U'č') PORT_CHAR(U'Č')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')

	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')

	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')

	PORT_START("LINE7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

const gfx_layout charlayout =
{
	8, 8,               /* 8x8 characters */
	256,                /* 256 characters */
	1,                /* 1 bits per pixel */
	{0},                /* no bitplanes; 1 bit per pixel */
	{7, 6, 5, 4, 3, 2, 1, 0},
	{0 * 8, 1 * 8, 2 * 8, 3 * 8, 4 * 8, 5 * 8, 6 * 8, 7 * 8},
	8*8                 /* size of one char */
};

static GFXDECODE_START( gfx_galeb )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END

u32 galeb_state::screen_update_galeb(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 ma = (m_ram[0xff]*256+m_ram[0xfe]) & 0xfff;
	if (ma < 0x400)
		ma = 15;
	else
		ma -= 0x3bb;

	for(u8 y = 0; y < 16; y++ )
	{
		for(u8 x = 0; x < 48; x++ )
		{
			u8 code = m_vram[ma + x + y*64];
			m_gfxdecode->gfx(0)->opaque(bitmap,cliprect,  code , 0, 0, 0, x*8,y*8);
		}
	}
	return 0;
}

/* Machine driver */
void galeb_state::galeb(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, 1000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &galeb_state::mem_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(48*8, 16*8);
	screen.set_visarea(0, 48*8-1, 0, 16*8-1);
	screen.set_screen_update(FUNC(galeb_state::screen_update_galeb));
	screen.set_palette(m_palette);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_galeb);

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	/* audio hardware */
	SPEAKER(config, "mono").front_center();
	DAC_1BIT(config, m_dac, 0).add_route(ALL_OUTPUTS, "mono", 0.0625); // unknown DAC

	clock_device &acia_clock(CLOCK(config, "acia_clock", 4'800)); // 300 baud x 16(divider) = 4800
	acia_clock.signal_handler().set(m_acia, FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append(m_acia, FUNC(acia6850_device::write_rxc));

	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set([this] (bool state) { m_cassbit = state; });

	/* cassette */
	CASSETTE(config, m_cass);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	TIMER(config, "kansas_w").configure_periodic(FUNC(galeb_state::kansas_w), attotime::from_hz(4800)); // cass write
	TIMER(config, "kansas_r").configure_periodic(FUNC(galeb_state::kansas_r), attotime::from_hz(40000)); // cass read
}

/* ROM definition */
ROM_START( galeb )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	// BASIC ROMS
	ROM_LOAD( "bas01.rom",   0x0000, 0x0800, CRC(9b19ed58) SHA1(ebfc27af8dbabfb233f9888e6a0a0dfc87ae1691) )
	ROM_LOAD( "bas02.rom",   0x0800, 0x0800, CRC(3f320a84) SHA1(4ea082b4269dca6152426b1f720c7508122d3cb7) )
	ROM_LOAD( "bas03.rom",   0x1000, 0x0800, CRC(f122ad10) SHA1(3c7c1dd67268230d179a00b0f8b35be80c2b7035) )
	ROM_LOAD( "bas04.rom",   0x1800, 0x0800, CRC(b5372a83) SHA1(f93b73d98b943c6791f46617418fb5e4238d75bd) )
	// MONITOR
	ROM_LOAD( "exmd.rom",    0x3000, 0x0800, CRC(1bcb1375) SHA1(fda3361d238720a3d309644093da9832d5aff661) )
	// SYSTEM
	ROM_LOAD( "makbug.rom",  0x3800, 0x0800, CRC(91e38e79) SHA1(2b6439a09a470cda9c81b9d453c6380b99716989) )

	ROM_REGION(0x0800, "chargen",0)
	ROM_LOAD ("chrgen.bin",  0x0000, 0x0800, CRC(409a800e) SHA1(0efe429dd6c0568032636e691d9865a623afeb55) )
ROM_END

} // Anonymous namespace

/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY         FULLNAME  FLAGS
COMP( 1981, galeb, 0,      0,      galeb,   galeb, galeb_state, empty_init, "PEL Varazdin", "Galeb",  MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



gamate.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Peter Trauner
/******************************************************************************
 PeT mess@utanet.at 2007, 2014
 Peter Wilhelmsen peter.wilhelmsen@gmail.com
 Morten Shearman Kirkegaard morten+gamate@afdelingp.dk
 Juan Felix Mateos vectrex@hackermesh.org

 A complete hardware description can be found at
 http://blog.kevtris.org/blogfiles/Gamate%20Inside.txt

 ******************************************************************************/

#include "emu.h"
#include "gamate_v.h"
#include "sound/ay8910.h"
#include "bus/gamate/slot.h"
#include "cpu/m6502/m6502.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class gamate_state : public driver_device
{
public:
	gamate_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ay(*this, "ay8910")
		, m_cartslot(*this, "cartslot")
		, m_io_joy(*this, "JOY")
		, m_bios(*this, "maincpu")
		, m_ram(*this, "ram")
	{ }

	void gamate(machine_config &config);

	void init_gamate();

private:
	uint8_t card_available_check();
	uint8_t card_available_set();
	void card_reset(uint8_t data);

	uint8_t gamate_nmi_r();
	void sound_w(offs_t offset, uint8_t data);
	uint8_t sound_r(offs_t offset);
	void write_cart(offs_t offset, uint8_t data);
	uint8_t read_cart(offs_t offset);

	TIMER_CALLBACK_MEMBER(gamate_timer);
	TIMER_CALLBACK_MEMBER(gamate_timer2);

	void gamate_mem(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	int m_card_available;

	required_device<cpu_device> m_maincpu;
	required_device<ay8910_device> m_ay;
	required_device<gamate_cart_slot_device> m_cartslot;
	required_ioport m_io_joy;
	required_region_ptr<uint8_t> m_bios;
	required_shared_ptr<uint8_t> m_ram;
	emu_timer *timer1;
	emu_timer *timer2;
};

/* todo: what are these really, do they go to the cartridge slot? */
uint8_t gamate_state::card_available_check()
{
	// bits 0 and 1 checked
	return m_card_available ? 3: 1;
}

void gamate_state::card_reset(uint8_t data)
{
	// might reset the card / protection?
}

uint8_t gamate_state::card_available_set()
{
	if (!machine().side_effects_disabled())
		m_card_available = 1;
	return 0;
}

// serial connection
uint8_t gamate_state::gamate_nmi_r()
{
	uint8_t data=0;
	logerror("nmi/4800 read\n");
	return data;
}

uint8_t gamate_state::sound_r(offs_t offset)
{
	m_ay->address_w(offset);
	return m_ay->data_r();
}

void gamate_state::sound_w(offs_t offset, uint8_t data)
{
	m_ay->address_w(offset);
	m_ay->data_w(data);
}

void gamate_state::write_cart(offs_t offset, uint8_t data)
{
	m_cartslot->write_cart(offset, data);
}

uint8_t gamate_state::read_cart(offs_t offset)
{
	return m_cartslot->read_cart(offset);
}

void gamate_state::gamate_mem(address_map &map)
{
	map(0x0000, 0x03ff).mirror(0x1c00).ram().share("ram");
	map(0x4000, 0x400f).mirror(0x03f0).rw(FUNC(gamate_state::sound_r), FUNC(gamate_state::sound_w));
	map(0x4400, 0x4400).mirror(0x03ff).portr("JOY");
	map(0x4800, 0x4800).mirror(0x03ff).r(FUNC(gamate_state::gamate_nmi_r));
	map(0x5000, 0x5007).mirror(0x03f8).m("video", FUNC(gamate_video_device::regs_map));
	map(0x5800, 0x5800).r(FUNC(gamate_state::card_available_set));
	map(0x5900, 0x5900).w(FUNC(gamate_state::card_reset));
	map(0x5a00, 0x5a00).r(FUNC(gamate_state::card_available_check));
	map(0x6000, 0xdfff).rw(FUNC(gamate_state::read_cart), FUNC(gamate_state::write_cart));

	map(0xe000, 0xefff).mirror(0x1000).rom().region("maincpu", 0);
}


static INPUT_PORTS_START( gamate )
	PORT_START("JOY")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("A")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("B")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_START) PORT_NAME("Start/Pause")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_SELECT) PORT_NAME("Select")
INPUT_PORTS_END

void gamate_state::init_gamate()
{
	timer1 = timer_alloc(FUNC(gamate_state::gamate_timer), this);
	timer2 = timer_alloc(FUNC(gamate_state::gamate_timer2), this);
}

void gamate_state::machine_start()
{
	memset(m_ram, 0xff, m_ram.bytes());  /* memory seems to contain 0xff at power up */
	timer2->enable(true);
	timer2->reset(m_maincpu->cycles_to_attotime(1000));

	save_item(NAME(m_card_available));
}

void gamate_state::machine_reset()
{
	m_card_available = 0;
}

TIMER_CALLBACK_MEMBER(gamate_state::gamate_timer)
{
	m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE);
	timer1->enable(false);
}

TIMER_CALLBACK_MEMBER(gamate_state::gamate_timer2)
{
	m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE);
	timer1->enable(true);
	timer1->reset(m_maincpu->cycles_to_attotime(10/* cycles short enought to clear irq line early enough*/));
	timer2->enable(true);
	timer2->reset(m_maincpu->cycles_to_attotime(32768/2));
}

void gamate_state::gamate(machine_config &config)
{
	M6502(config, m_maincpu, 4433000/2); // NCR 65CX02
	m_maincpu->set_addrmap(AS_PROGRAM, &gamate_state::gamate_mem);

	GAMATE_VIDEO(config, "video", 0);

	/* sound hardware */
	SPEAKER(config, "speaker", 2).front(); // Stereo headphone output

	AY8910(config, m_ay, 4433000 / 4); // AY compatible, no actual AY chip present
	m_ay->add_route(0, "speaker", 0.5, 0);
	m_ay->add_route(1, "speaker", 0.5, 1);
	m_ay->add_route(2, "speaker", 0.25, 0);
	m_ay->add_route(2, "speaker", 0.25, 1);

	GAMATE_CART_SLOT(config, m_cartslot, gamate_cart, nullptr);

	SOFTWARE_LIST(config, "cart_list").set_original("gamate");
}


/* ROM notes:
gamate_bios_umc.bin is called UMC or NCR ICASC00002
gamate_bios_bit.bin is called BIT ICASC00001
So basically the UMC UA6588F and NCR 81489 CPU's contains the ICASC00002 bios
while the BIT branded CPU contains the ICASC00001 bios.
They're compatible, but for completeness its nice to have both.
Note i have 8 gamate consoles (dated 1990 though 1993) which has the gamate_bios_umc.bin in it
and only 1 dated 1994 which has the gamate_bios_bit.bin in it, so the former seems much more common.
We dumped the BIOS from all our Gamate consoles, and all except one were
identical (SHA1:ea449dc607601f9a68d855ad6ab53800d2e99297):
Gamate_BIOS_9027__9002008__UMC_UA6588F_9027S_606700.bin
Gamate_BIOS_9027__9142222__UMC_UA6588F_9027S_606700.bin
Gamate_BIOS_9027__unknown__UMC_UA6588F_9027S_606690.bin
Gamate_BIOS_9031__9009719__NCR_81489_BIT_WS39323F_ICASC00002_F841400_R9031.bin
Gamate_BIOS_9038__9145157__NCR_81489_BIT_WS39323F_ICASC00002_F842247_N9038.bin
One console, with an unknown serial number, has an updated BIOS
(SHA1:4e9dfbfe916ca485530ef4221593ab68738e2217):
This console appears to have been manufactured in 1994, based on the date markings on the RAM chips,
as well as the PCB.
*/
ROM_START(gamate)
	ROM_REGION(0x1000,"maincpu", 0)
	ROM_SYSTEM_BIOS(0, "default", "DEFAULT")
	ROMX_LOAD("gamate_bios_umc.bin", 0x0000, 0x1000, CRC(07090415) SHA1(ea449dc607601f9a68d855ad6ab53800d2e99297), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "newer", "NEWER")
	ROMX_LOAD("gamate_bios_bit.bin", 0x0000, 0x1000, CRC(03a5f3a7) SHA1(4e9dfbfe916ca485530ef4221593ab68738e2217), ROM_BIOS(1))
ROM_END

} // anonymous namespace


//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT         COMPANY     FULLNAME  FLAGS
CONS( 1990, gamate, 0,      0,      gamate,  gamate, gamate_state, init_gamate, "Bit Corp", "Gamate", 0 )



gamecom.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol, Robbbert
/***************************************************************************

Driver file to handle emulation of the Tiger Game.com by Wilbert Pol.
Various improvements by Robbbert.

Todo:
- Fix cpu and system problems that prevent the games from working fully.
- RS232 port
- Sound ports 1,2 do not sound anything like the real thing
- Sound port 3 (noise channel)
- Sound dac port (mostly works but is the wrong speed in some places).
  dac pitch is controlled by how often TIM1_INT occurs. This same
  interrupt also controls the seconds countdown in some games, such as
  Quiz Wiz and Scrabble. Currently this countdown goes twice as fast
  as it should. If the INT is slowed down to compensate, the dac sound
  is so slow as to be unintelligible. Need to find a way to keep both happy.
- System seems slower than it should. Probably wrong cycle count in the CPU.
  What we have there is a guess as the real info has not been found.
  -speed 1.2 makes the sound more natural
  -speed 1.7 if TIM1_INT is slowed to fix the countdown.

Game Status:
- Inbuilt ROM and PDA functions all work
- Due to an irritating message, the NVRAM is commented out in the machine config
- All carts appear to work, from my limited testing.
-- indy500 skips some speech just before the trial race starts.

***************************************************************************/

#include "emu.h"
#include "gamecom.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "gamecom.lh"


void gamecom_state::gamecom_mem_map(address_map &map)
{
	map(0x0000, 0x03ff).ram().share("maincpu");
	map(0x0014, 0x0017).rw(FUNC(gamecom_state::gamecom_pio_r), FUNC(gamecom_state::gamecom_pio_w));        // buttons
	map(0x0020, 0x007F).rw(FUNC(gamecom_state::gamecom_internal_r), FUNC(gamecom_state::gamecom_internal_w));/* CPU internal register file */
	map(0x0400, 0x0FFF).noprw();                                          /* Nothing */
	map(0x1000, 0x1FFF).rom().region("maincpu", 0);                       /* Internal ROM (initially), or External ROM/Flash. Controlled by MMU0 (never swapped out in game.com) */
	map(0x2000, 0x3FFF).bankr("bank1");                                   /* External ROM/Flash. Controlled by MMU1 */
	map(0x4000, 0x5FFF).bankr("bank2");                                   /* External ROM/Flash. Controlled by MMU2 */
	map(0x6000, 0x7FFF).bankr("bank3");                                   /* External ROM/Flash. Controlled by MMU3 */
	map(0x8000, 0x9FFF).bankr("bank4");                                   /* External ROM/Flash. Controlled by MMU4 */
	map(0xA000, 0xDFFF).writeonly().share("videoram").nopr();             /* VRAM - writeonly, returns 0 on read, as expected by lostwrld */
	map(0xE000, 0xFFFF).ram().share("nvram");           /* Extended I/O, Extended RAM */
}

static INPUT_PORTS_START( gamecom )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_NAME( "Up" )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_NAME( "Down" )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_NAME( "Left" )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_NAME( "Right" )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME( "Menu" ) PORT_CODE( KEYCODE_M )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME( DEF_STR(Pause) ) PORT_CODE( KEYCODE_V )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME( "Sound" ) PORT_CODE( KEYCODE_S )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME( "Button A" ) PORT_CODE( KEYCODE_A ) PORT_CODE( KEYCODE_LCONTROL )

	PORT_START("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME( "Button B" ) PORT_CODE( KEYCODE_B ) PORT_CODE( KEYCODE_LALT )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME( "Button C" ) PORT_CODE( KEYCODE_C ) PORT_CODE( KEYCODE_SPACE )

	PORT_START("IN2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME( "Reset" ) PORT_CODE( KEYCODE_N )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME( "Button D" ) PORT_CODE( KEYCODE_D ) PORT_CODE( KEYCODE_LSHIFT )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME( "Stylus press" ) PORT_CODE( KEYCODE_Z ) PORT_CODE( MOUSECODE_BUTTON1 )

	// These are used by the "Default Grid" artwork to detect mouse clicks
	PORT_START("GRID.0")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_OTHER)

	PORT_START("GRID.1")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_OTHER)

	PORT_START("GRID.2")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_OTHER)

	PORT_START("GRID.3")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_OTHER)

	PORT_START("GRID.4")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_OTHER)

	PORT_START("GRID.5")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_OTHER)

	PORT_START("GRID.6")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_OTHER)

	PORT_START("GRID.7")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_OTHER)

	PORT_START("GRID.8")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_OTHER)

	PORT_START("GRID.9")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_OTHER)

	PORT_START("GRID.10")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_OTHER)

	PORT_START("GRID.11")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_OTHER)

	PORT_START("GRID.12")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_OTHER)
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_OTHER)
INPUT_PORTS_END

void gamecom_state::gamecom_palette(palette_device &palette) const
{
	palette.set_pen_color(0, 0x00, 0x00, 0x00); // Black
	palette.set_pen_color(1, 0x0f, 0x4f, 0x2f); // Gray 1
	palette.set_pen_color(2, 0x6f, 0x8f, 0x4f); // Gray 2
	palette.set_pen_color(3, 0x8f, 0xcf, 0x8f); // Grey 3
	palette.set_pen_color(4, 0xdf, 0xff, 0x8f); // White
}

uint32_t gamecom_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
	return 0;
}

INTERRUPT_GEN_MEMBER(gamecom_state::gamecom_interrupt)
{
	m_maincpu->set_input_line(sm8500_cpu_device::LCDC_INT, ASSERT_LINE );
}

void gamecom_state::gamecom(machine_config &config)
{
	/* basic machine hardware */
	SM8500(config, m_maincpu, XTAL(11'059'200)/2);   /* actually it's an sm8521 microcontroller containing an sm8500 cpu */
	m_maincpu->set_addrmap(AS_PROGRAM, &gamecom_state::gamecom_mem_map);
	m_maincpu->dma_cb().set(FUNC(gamecom_state::gamecom_handle_dma));
	m_maincpu->timer_cb().set(FUNC(gamecom_state::gamecom_update_timers));
	m_maincpu->set_vblank_int("screen", FUNC(gamecom_state::gamecom_interrupt));

	config.set_maximum_quantum(attotime::from_hz(60));

	//NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(59.732155);
	m_screen->set_vblank_time(500);
	m_screen->set_screen_update(FUNC(gamecom_state::screen_update));
	m_screen->set_size(200, 160);
	m_screen->set_visarea_full();
	m_screen->set_palette("palette");

	config.set_default_layout(layout_gamecom);
	PALETTE(config, "palette", FUNC(gamecom_state::gamecom_palette), 5);

	/* sound hardware */
	SPEAKER(config, "speaker").front_center();
	/* TODO: much more complex than this */
	DAC_8BIT_R2R(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.5); // unknown DAC (Digital audio)
	DAC_4BIT_R2R(config, m_dac0, 0).add_route(ALL_OUTPUTS, "speaker", 0.25); // unknown DAC (Frequency modulation)
	DAC_4BIT_R2R(config, m_dac1, 0).add_route(ALL_OUTPUTS, "speaker", 0.25); // unknown DAC (Frequency modulation)

	/* cartridge */
	GENERIC_CARTSLOT(config, "cartslot1", generic_linear_slot, "gamecom_cart", "bin,tgc").set_device_load(FUNC(gamecom_state::cart1_load));
	GENERIC_CARTSLOT(config, "cartslot2", generic_linear_slot, "gamecom_cart", "bin,tgc").set_device_load(FUNC(gamecom_state::cart2_load));
	SOFTWARE_LIST(config, "cart_list").set_original("gamecom");
}

ROM_START( gamecom )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "internal.bin", 0x0000,  0x1000, CRC(a0cec361) SHA1(03368237e8fed4a8724f3b4a1596cf4b17c96d33) )

	ROM_REGION( 0x40000, "kernel", 0 )
	ROM_LOAD( "external.bin", 0x00000, 0x40000, CRC(e235a589) SHA1(97f782e72d738f4d7b861363266bf46b438d9b50) )
ROM_END

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT          COMPANY  FULLNAME    FLAGS
CONS( 1997, gamecom, 0,      0,      gamecom, gamecom, gamecom_state, init_gamecom, "Tiger", "Game.com", MACHINE_IMPERFECT_SOUND )



gamecube.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best, Segher Boessenkool
/***************************************************************************

    Nintendo GameCube

    Skeleton driver, just to document the available firmware dumps
    for now.

***************************************************************************/

#include "emu.h"
#include "cpu/powerpc/ppc.h"


namespace {

//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class gamecube_state : public driver_device
{
public:
	gamecube_state(const machine_config &mconfig, device_type type, const char *tag) :
	driver_device(mconfig, type, tag),
	m_cpu(*this, "maincpu")
	{ }

	void gc(machine_config &config);
	void ppc_mem(address_map &map) ATTR_COLD;
protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void decrypt(uint8_t *data, unsigned size);

	required_device<cpu_device> m_cpu;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void gamecube_state::ppc_mem(address_map &map)
{
	map(0x00000000, 0x017fffff).ram(); // 24 MB main memory
	map(0x08000000, 0x081fffff).ram(); //  2 MB embedded framebuffer
	map(0xfff00000, 0xffffffff).bankr("boot");
}


//**************************************************************************
//  INPUTS
//**************************************************************************

INPUT_PORTS_START( gc )
INPUT_PORTS_END


//**************************************************************************
//  MACHINE
//**************************************************************************

// bootrom descrambler reversed by segher
// Copyright 2008-2017 Segher Boessenkool <segher@kernel.crashing.org>
void gamecube_state::decrypt(uint8_t *data, unsigned size)
{
	uint8_t acc = 0;
	uint8_t nacc = 0;

	uint16_t t = 0x2953;
	uint16_t u = 0xd9c2;
	uint16_t v = 0x3ff1;

	uint8_t x = 1;

	for (unsigned it = 0; it < size;)
	{
		int t0 = t & 1;
		int t1 = (t >> 1) & 1;
		int u0 = u & 1;
		int u1 = (u >> 1) & 1;
		int v0 = v & 1;

		x ^= t1 ^ v0;
		x ^= (u0 | u1);
		x ^= (t0 ^ u1 ^ v0) & (t0 ^ u0);

		if (t0 == u0)
		{
			v >>= 1;
			if (v0)
				v ^= 0xb3d0;
		}

		if (t0 == 0)
		{
			u >>= 1;
			if (u0)
				u ^= 0xfb10;
		}

		t >>= 1;
		if (t0)
			t ^= 0xa740;

		nacc++;
		acc = 2 * acc + x;
		if (nacc == 8)
		{
			data[it++] ^= acc;
			nacc = 0;
		}
	}
}

void gamecube_state::machine_start()
{
	decrypt(memregion("ipl")->base() + 0x100, 0x1afe00);

	// swap endianess after decryption
	uint8_t *base;
	int i, j;
	for (i = 0, base = memregion("ipl")->base(); i < memregion("ipl")->bytes(); i += 8)
	{
		uint8_t temp[8];
		memcpy(temp, base, 8);
		for (j = 8 - 1; j >= 0; j--)
			*base++ = temp[j];
	}

	membank("boot")->set_base(memregion("ipl")->base());
}

void gamecube_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE DEFINITIONS
//**************************************************************************

void gamecube_state::gc(machine_config &config)
{
	PPC603(config, m_cpu, 485000000 / 100); // 485 MHz IBM "Gekko" (750CXe/750FX based)
	m_cpu->set_addrmap(AS_PROGRAM, &gamecube_state::ppc_mem);
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

/*  There are a lot of bad dumps of the gamecube IPLs floating around.
    The following IPL dumps are known bad, with RAM/MAC address garbage in the last 0x8000 bytes:
    ROM_LOAD("ipl_bad_ntsc_v10.bin",  0x000000, 0x200000, CRC(6d740ae7) SHA1(015808f637a984acde6a06efa7546e278293c6ee))
    ROM_LOAD("ipl_bad2_ntsc_v10.bin", 0x000000, 0x200000, CRC(8bdabbd4) SHA1(f1b0ef434cd74fd8fe23698e2fc911d945b45bf1))
    ROM_LOAD("ipl_bad_pal_v10.bin",   0x000000, 0x200000, CRC(dd8cab7c) SHA1(6f305c37dc1fbe332883bb8153eee26d3d325629))
    The following rom is flat out unknown and unseen in the wild, except for its checksums:
    ROM_LOAD("ipl_unknown.bin",       0x000000, 0x200000, CRC(d235e3f9) SHA1(96f69a21645de73a5ba61e57951ef303d55788c5))
*/

ROM_START( gcjp ) // DOL-001(JPN) and DOL-101(JPN); NTSC gamecube board, outputs NTSC color, NTSC timings; JPN Region jumper set
	ROM_REGION(0x200000, "ipl", 0)
	ROM_DEFAULT_BIOS("v12")
	ROM_SYSTEM_BIOS(0, "v10", "NTSC Revision 1.0") // Internal version 36  Mar 22 2001 22:38:31
	ROMX_LOAD("ipl_ntsc_v10.bin", 0x000000, 0x200000, CRC(6dac1f2a) SHA1(a1837968288253ed541f2b11440b68f5a9b33875), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v11", "NTSC Revision 1.1") // Internal version 47  Sep 27 2001 15:15:22
	ROMX_LOAD("ipl_ntsc_v11.bin", 0x000000, 0x200000, CRC(d5e6feea) SHA1(239eacd86527ff9a75aeb7282da65797baeef010), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v12", "NTSC Revision 1.2") // Internal version 0x2301  Jun 16 2003 04:27:06
	ROMX_LOAD("ipl_ntsc_v12.bin", 0x000000, 0x200000, CRC(86573808) SHA1(ef9194ab4804aa0aa8540d846caf291b28331165), ROM_BIOS(2)) // not verified from console yet but seems good
	// There may be another IPL with the same "NTSC Revision 1.2" string as above but not the same code. If so, it is undumped.

	ROM_REGION(0x20000, "dvd", 0)
	ROM_LOAD("20010608.bin", 0x00000, 0x20000, CRC(c047465a) SHA1(27872c201e87b06a19bf85d36c796ef383f8d52d))

	ROM_REGION(0x1000, "dsp_coef", 0)
	ROM_LOAD("dsp_coef.bin", 0x0000, 0x1000, CRC(d2777c90) SHA1(c116d867ba001dcd6bf6d399ff4bf38d340f556c))

	ROM_REGION(0x2000, "dsp_rom", 0)
	ROM_LOAD("dsp_rom.bin", 0x0000, 0x2000, CRC(47daaa65) SHA1(3c6cc6e04fdd0b2a392d7a6ed769455444846be7))
ROM_END

ROM_START( gcus ) // DOL-001(USA) and DOL-101(USA); NTSC gamecube board, outputs NTSC color, NTSC timings; USA region jumper set
	ROM_REGION(0x200000, "ipl", 0)
	ROM_DEFAULT_BIOS("v12")
	ROM_SYSTEM_BIOS(0, "v10", "NTSC Revision 1.0") // Internal version 36  Mar 22 2001 22:38:31
	ROMX_LOAD("ipl_ntsc_v10.bin", 0x000000, 0x200000, CRC(6dac1f2a) SHA1(a1837968288253ed541f2b11440b68f5a9b33875), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v11", "NTSC Revision 1.1") // Internal version 47  Sep 27 2001 15:15:22
	ROMX_LOAD("ipl_ntsc_v11.bin", 0x000000, 0x200000, CRC(d5e6feea) SHA1(239eacd86527ff9a75aeb7282da65797baeef010), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v12", "NTSC Revision 1.2") // Internal version 0x2301  Jun 16 2003 04:27:06
	ROMX_LOAD("ipl_ntsc_v12.bin", 0x000000, 0x200000, CRC(86573808) SHA1(ef9194ab4804aa0aa8540d846caf291b28331165), ROM_BIOS(2)) // not verified from console yet but seems good
	// There may be another IPL with the same "NTSC Revision 1.2" string as above but not the same code. If so, it is undumped.

	ROM_REGION(0x20000, "dvd", 0)
	ROM_LOAD("20010608.bin", 0x00000, 0x20000, CRC(c047465a) SHA1(27872c201e87b06a19bf85d36c796ef383f8d52d))

	ROM_REGION(0x1000, "dsp_coef", 0)
	ROM_LOAD("dsp_coef.bin", 0x0000, 0x1000, CRC(d2777c90) SHA1(c116d867ba001dcd6bf6d399ff4bf38d340f556c))

	ROM_REGION(0x2000, "dsp_rom", 0)
	ROM_LOAD("dsp_rom.bin", 0x0000, 0x2000, CRC(47daaa65) SHA1(3c6cc6e04fdd0b2a392d7a6ed769455444846be7))
ROM_END

ROM_START( gceu ) // DOL-001(EUR) and DOL-101(EUR); PAL gamecube board, outputs PAL-E color, PAL-E timings; EUR region (not sure if there is a separate jumper for this?)
	ROM_REGION(0x200000, "ipl", 0)
	ROM_DEFAULT_BIOS("v12")
	ROM_SYSTEM_BIOS(0, "v10", "PAL Revision 1.0") // Internal version 47  Sep 27 2001 15:15:22
	ROMX_LOAD("ipl_pal_v10.bin", 0x000000, 0x200000, CRC(4f319f43) SHA1(f27c63e5394e2fd1606f70df004c4fc2d6027700), ROM_BIOS(0))
	// "PAL Revision 1.1" IPL probably doesn't exist; the internal version of "PAL Revision 1.0" lines up with "NTSC Revision 1.1"
	ROM_SYSTEM_BIOS(1, "v12", "PAL Revision 1.2") // Internal version 0x2301  Jun 16 2003 04:27:06
	ROMX_LOAD("ipl_pal_v12.bin", 0x000000, 0x200000, CRC(ad1b7f16) SHA1(80b8744ff5e43585392f55546bd03a673d11ef5f), ROM_BIOS(1)) // not verified from console yet but seems good

	ROM_REGION(0x20000, "dvd", 0)
	ROM_LOAD("20010608.bin", 0x00000, 0x20000, CRC(c047465a) SHA1(27872c201e87b06a19bf85d36c796ef383f8d52d))

	ROM_REGION(0x1000, "dsp_coef", 0)
	ROM_LOAD("dsp_coef.bin", 0x0000, 0x1000, CRC(d2777c90) SHA1(c116d867ba001dcd6bf6d399ff4bf38d340f556c))

	ROM_REGION(0x2000, "dsp_rom", 0)
	ROM_LOAD("dsp_rom.bin", 0x0000, 0x2000, CRC(47daaa65) SHA1(3c6cc6e04fdd0b2a392d7a6ed769455444846be7))
ROM_END

ROM_START( gcbr ) // DOL-002(BRA); NTSC gamecube board, outputs video with PAL-M color, and either PAL-E (for the IPL) or PAL-M (for games) timings; region jumper is unknown but probably USA
	ROM_REGION(0x200000, "ipl", 0) // "MPAL Revision 1.1", Internal version 47  Sep 27 2001 15:15:22
	ROM_LOAD("ipl_mpal_v11.bin", 0x000000, 0x200000, CRC(667d0b64) SHA1(f3cd0c7c61cbcefa85e7de3aff4cfa50bc508714))

	ROM_REGION(0x20000, "dvd", 0)
	ROM_LOAD("20010608.bin", 0x00000, 0x20000, CRC(c047465a) SHA1(27872c201e87b06a19bf85d36c796ef383f8d52d))

	ROM_REGION(0x1000, "dsp_coef", 0)
	ROM_LOAD("dsp_coef.bin", 0x0000, 0x1000, CRC(d2777c90) SHA1(c116d867ba001dcd6bf6d399ff4bf38d340f556c))

	ROM_REGION(0x2000, "dsp_rom", 0)
	ROM_LOAD("dsp_rom.bin", 0x0000, 0x2000, CRC(47daaa65) SHA1(3c6cc6e04fdd0b2a392d7a6ed769455444846be7))
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS           INIT        COMPANY     FULLNAME             FLAGS
CONS( 2001, gcjp,  0,      0,      gc,      gc,    gamecube_state, empty_init, "Nintendo", "GameCube (Japan)",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2001, gcus,  gcjp,   0,      gc,      gc,    gamecube_state, empty_init, "Nintendo", "GameCube (USA)",    MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2002, gceu,  gcjp,   0,      gc,      gc,    gamecube_state, empty_init, "Nintendo", "GameCube (EUR)",    MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2002, gcbr,  gcjp,   0,      gc,      gc,    gamecube_state, empty_init, "Nintendo", "GameCube (Brazil)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



gameking.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Peter Trauner, AJR
/*
  TimeTop - GameKing

  PeT mess@utanet.at 2015

  Thanks to Deathadder, Judge, Porchy, Klaus Sommer, James Brolly & Brian Provinciano

  hopefully my work (reverse engineering, cartridge+bios backup, emulation) will be honored in future
  and my name will not be removed entirely, especially by simple code rewrites of working emulation

  flashcard, handheld, programmer, assembler ready to do some test on real hardware

  todo:
  !back up gameking3 bios so emulation of gameking3 gets possible; my gameking bios backup solution should work
  (improve emulation)
  (add audio)

  use gameking3 cartridge to get illegal cartridge scroller
*/

#include "emu.h"

#include "cpu/m6502/st2204.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "sound/dac.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

namespace {

class gameking_state : public driver_device
{
public:
	gameking_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_cart(*this, "cartslot"),
		m_io_joy(*this, "JOY"),
		m_screen(*this, "screen"),
		m_palette(*this, "palette")
	{ }

	void gameking(machine_config &config);
	void gameking3(machine_config &config);
	void gameking1(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<st2204_device> m_maincpu;
	required_device<generic_slot_device> m_cart;
	required_ioport m_io_joy;
	required_device<screen_device> m_screen;
	optional_device<palette_device> m_palette;

	void gameking_palette(palette_device &palette) const;
	void timer_w(uint8_t data);
	uint8_t input_r();
	uint8_t input2_r();
	TIMER_CALLBACK_MEMBER(gameking_timer1);
	TIMER_CALLBACK_MEMBER(gameking_timer2);

	uint32_t screen_update_gameking(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	uint32_t screen_update_gameking3(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	void gameking_mem(address_map &map) ATTR_COLD;
	void gameking3_mem(address_map &map) ATTR_COLD;

	emu_timer *m_timer1;
	emu_timer *m_timer2;
	uint8_t m_timer1_val = 0;
};


void gameking_state::timer_w(uint8_t data)
{
	m_timer1_val = data;
	//m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE);
	m_timer1->enable(true);
	m_timer1->reset(m_maincpu->cycles_to_attotime(data * 300/*?*/));
}

uint8_t gameking_state::input_r()
{
	return m_io_joy->read() | ~3;
}

uint8_t gameking_state::input2_r()
{
	return m_io_joy->read() | 3;
}

void gameking_state::gameking_mem(address_map &map)
{
	map(0x000000, 0x07ffff).rom().region("maincpu", 0);
}

void gameking_state::gameking3_mem(address_map &map)
{
	map(0x000000, 0x07ffff).rom().region("maincpu", 0);
	map(0x800000, 0x807fff).ram();
}


static INPUT_PORTS_START( gameking )
	PORT_START("JOY")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_START)
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_SELECT) // ?
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON2) // A
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1) // B
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) // ?
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)
INPUT_PORTS_END

static INPUT_PORTS_START( gameking3 )
	PORT_INCLUDE( gameking )
	PORT_MODIFY("JOY")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_START)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_SELECT) // ?
INPUT_PORTS_END

static constexpr rgb_t gameking_pens[] =
{
	{ 255, 255, 255 },
	{ 127, 127, 127 },
	{  63,  63,  63 },
	{   0,   0,   0 }
};

void gameking_state::gameking_palette(palette_device &palette) const
{
	palette.set_pen_colors(0, gameking_pens);
}


uint32_t gameking_state::screen_update_gameking(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	address_space *maincpu_ram = &m_maincpu->space(AS_PROGRAM);
	offs_t lssa = m_maincpu->state_int(st2xxx_device::ST_LSSA);
	if (lssa < 0x0080)
		return 0;

	for (int y=31, i=0;i<32;i++,y--)
	{
		for (int x=0, j=0;j<48/4;x+=4, j++)
		{
			uint8_t data=maincpu_ram->read_byte(lssa+j+i*12);
			bitmap.pix(y, x+3)=data&3;
			bitmap.pix(y, x+2)=(data>>2)&3;
			bitmap.pix(y, x+1)=(data>>4)&3;
			bitmap.pix(y, x)=(data>>6)&3;
		}
	}
	return 0;
}


static constexpr uint8_t gameking3_intensities[] =
{
	0,
	127,
	191,
	255
};

uint32_t gameking_state::screen_update_gameking3(screen_device& screen, bitmap_rgb32 &bitmap, const rectangle& cliprect)
{
	address_space* maincpu_ram = &m_maincpu->space(AS_PROGRAM);
	offs_t lssa = m_maincpu->state_int(st2xxx_device::ST_LSSA);
	if (lssa < 0x0080)
		return 0;

	for (int y = 0, i = 0; i < 80; y += 2, i++)
	{
		for (int x = 0, j = 0; j < 40; x += 4, j++)
		{
			uint8_t data=maincpu_ram->read_byte(lssa+j+i*40);

			// apply SPRD-C color filter
			switch (i % 3)
			{
			case 0:
				bitmap.pix(y, x + 3) = rgb_t(0, gameking3_intensities[data&3], 0);
				bitmap.pix(y + 1, x + 2) = rgb_t(gameking3_intensities[(data>>2)&3], 0, 0);
				bitmap.pix(y, x + 1) = rgb_t(0, gameking3_intensities[(data>>4)&3], 0);
				bitmap.pix(y + 1, x) = rgb_t(gameking3_intensities[(data>>6)&3], 0, 0);
				break;

			case 1:
				bitmap.pix(y, x + 3) = rgb_t(0, 0, gameking3_intensities[data&3]);
				bitmap.pix(y + 1, x+2) = rgb_t(0, gameking3_intensities[(data>>2)&3], 0);
				bitmap.pix(y, x + 1) = rgb_t(0, 0, gameking3_intensities[(data>>4)&3]);
				bitmap.pix(y + 1, x) = rgb_t(0, gameking3_intensities[(data>>6)&3], 0);
				break;

			case 2:
				bitmap.pix(y, x + 3) = rgb_t(gameking3_intensities[data&3], 0, 0);
				bitmap.pix(y + 1, x+2) = rgb_t(0, 0, gameking3_intensities[(data>>2)&3]);
				bitmap.pix(y, x + 1) = rgb_t(gameking3_intensities[(data>>4)&3], 0, 0);
				bitmap.pix(y + 1, x) = rgb_t(0, 0, gameking3_intensities[(data>>6)&3]);
				break;
			}
		}
	}

	// interpolate values for dots in between
	for (int y = 0; y < 160; y++)
	{
		for (int x = y & 1; x < 160; x += 2)
		{
			rgb_t l = rgb_t(x == 0 ? 0 : bitmap.pix(y, x - 1));
			rgb_t r = rgb_t(x == 159 ? 0 : bitmap.pix(y, x + 1));
			rgb_t u = rgb_t(y == 0 ? 0 : bitmap.pix(y - 1, x));
			rgb_t d = rgb_t(y == 159 ? 0 : bitmap.pix(y + 1, x));

			bitmap.pix(y, x) = rgb_t(
				((u.r() + d.r()) * 2 + l.r() + r.r()) / 3,
				((u.g() + d.g()) * 2 + l.g() + r.g()) / 3,
				((u.b() + d.b()) * 2 + l.b() + r.b()) / 3
			);
		}
	}

	return 0;
}


TIMER_CALLBACK_MEMBER(gameking_state::gameking_timer1)
{
	m_maincpu->set_state_int(st2xxx_device::ST_IREQ,
		m_maincpu->state_int(st2xxx_device::ST_IREQ) | (0x010 & m_maincpu->state_int(st2xxx_device::ST_IENA)));
	m_timer1->enable(false);
	m_timer2->enable(true);
	m_timer2->reset(m_maincpu->cycles_to_attotime(10/*?*/));
}

TIMER_CALLBACK_MEMBER(gameking_state::gameking_timer2)
{
	//m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); // in reality int for vector at fff4
	m_timer2->enable(false);
	m_timer1->enable(true);
	m_timer1->reset(m_maincpu->cycles_to_attotime(m_timer1_val * 300/*?*/));
}

DEVICE_IMAGE_LOAD_MEMBER(gameking_state::cart_load)
{
	uint32_t size = m_cart->common_get_size("rom");

	if (size > 0x10'0000)
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported cartridge size (must be no larger than 1M)");

	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

void gameking_state::machine_start()
{
	std::string region_tag;
	memory_region *cart_rom = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());
	if (cart_rom)
		m_maincpu->space(AS_DATA).install_rom(0x400000, 0x400000 + cart_rom->bytes() - 1, cart_rom->base()); // FIXME: gamekin3 wants Flash cartridges, not plain ROM

	m_timer1 = timer_alloc(FUNC(gameking_state::gameking_timer1), this);
	m_timer2 = timer_alloc(FUNC(gameking_state::gameking_timer2), this);

	save_item(NAME(m_timer1_val));
}


void gameking_state::gameking(machine_config &config)
{
	// basic machine hardware
	ST2204(config, m_maincpu, 6000000);
	m_maincpu->set_addrmap(AS_DATA, &gameking_state::gameking_mem);
	m_maincpu->in_pa_callback().set(FUNC(gameking_state::input_r));
	m_maincpu->in_pb_callback().set(FUNC(gameking_state::input2_r));
	m_maincpu->out_pc_callback().set(FUNC(gameking_state::timer_w)); // wrong
	m_maincpu->in_pl_callback().set_constant(6); // bios protection endless loop
	m_maincpu->dac_callback().set("dac", FUNC(dac_byte_interface::write));

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(48, 32);
	m_screen->set_visarea_full();
	m_screen->set_screen_update(FUNC(gameking_state::screen_update_gameking));
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(gameking_state::gameking_palette), std::size(gameking_pens));

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_8BIT_R2R_TWOS_COMPLEMENT(config, "dac", 0).add_route(0, "speaker", 1.0);

	// cartridge
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "gameking_cart", "bin").set_device_load(FUNC(gameking_state::cart_load));
}

void gameking_state::gameking1(machine_config &config)
{
	gameking(config);
	SOFTWARE_LIST(config, "cart_list").set_original("gameking");
}

void gameking_state::gameking3(machine_config &config)
{
	// basic machine hardware
	gameking(config);
	m_maincpu->set_clock(8000000);
	m_maincpu->set_addrmap(AS_DATA, &gameking_state::gameking3_mem);

	// video hardware
	m_screen->set_size(160, 160);
	m_screen->set_visarea_full();
	m_screen->set_physical_aspect(3, 2);
	m_screen->set_refresh_hz(39.308176); // ?
	m_screen->set_screen_update(FUNC(gameking_state::screen_update_gameking3));
	m_screen->set_no_palette();

	config.device_remove("palette");

	// cartridge
	SOFTWARE_LIST(config, "cart_list").set_original("gameking");
	SOFTWARE_LIST(config, "cart_list_3").set_original("gameking3");
}


ROM_START(gameking)
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_LOAD("gm218.bin", 0x00000, 0x80000, CRC(5a1ade3d) SHA1(e0d056f8ebfdf52ef6796d0375eba7fcc4a6a9d3) )
ROM_END

ROM_START(gamekin3)
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_LOAD("gm220.bin", 0x00000, 0x80000, CRC(1dc43bd5) SHA1(f9dcd3cb76bb7cb10565a1acb070ab375c082b4c) )
ROM_END

} // anonymous namespace


CONS( 2003, gameking, 0, 0, gameking1, gameking,  gameking_state, empty_init, "TimeTop", "GameKing GM-218", MACHINE_IMPERFECT_SOUND )
// the GameKing 2 (GM-219) is probably identical HW

CONS( 2003, gamekin3, 0, 0, gameking3, gameking3, gameking_state, empty_init, "TimeTop", "GameKing 3",      MACHINE_IMPERFECT_SOUND )
// gameking 3: similiar cartridges, accepts gameking cartridges, gameking3 cartridges not working on gameking (illegal cartridge scroller)
// my gameking bios backup solution might work on it



gamemachine.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle, Couriersud
/*******************************************************************************

VTech Game Machine tabletop/handheld
It's an electronic game machine + calculator.

Hardware notes:
- Mostek MK3870 MCU (2KB internal ROM)
- 12 digits 7seg VFD/LED panel (LED handheld version has 4 extra LEDs)
- tabletop: MC1455P(555 timer) + bunch of discrete components for sound
- handheld: simple 1-bit sound

CPU Frequency should be around 4 to 4.5MHz. Sean's measurement and video of
the LED handheld version is around 4.4MHz, and there are other video references
on YouTube with slower speed. Sean's measurement of the bigger Game Machine was
around 2.1MHz, that's way too slow compared to video references and it was perhaps
a case of measuring equipment influencing CPU speed.

The I/O for the tabletop and handheld version is nearly identical, enough to
put them in the same MAME driver. The main difference is the more complex sound
on the tabletop version.

The first version should be the tabletop (lower ROM serial). It was created by
VTech, but they didn't distribute it by themselves* until later in 1980 as the
Computer Game System. VTech Computron/Lesson One has a very similar design.

*: Apparently, VTech (Hong Kong company) first couple of products were published
through foreign companies. It wasn't until around 1980 when they started publishing
under their own brand.

Known releases:

Tabletop version:
- Waddingtons 2001: The Game Machine, by Waddingtons
- Computer Game System, by VTech
- Computer Game System, by Cheryco (Japan)
- Bingo 2000: Der Spiele-Computer, by Cheryco
- Game Machine 2, by VTech (sequel with 5 games)

Handheld LED version (Speedway, Brain Drain, Blackjack, Calculator):
- 4 in 1 Electronic Games (model CGS-2011), by VTech
- Electronic Games (model 60-2143), by Tandy (Radio Shack brand)
- 4-in-1 Electronic Computer Game, by Grandstand
- Enterprise, by Videomaster (this is Waddingtons)
- Micro-Game Centre, by Prinztronic

Handheld VFD version (Code Hunter, Grand Prix, Sub Hunt, Blackjack):
- Mini Game Machine, by VTech
- Mini Game Machine, by House of Games (this is Waddingtons)
- The Game Machine, by Grandstand

BTANB:
- gamemach: some digit segments get stuck after crashing in the GP game

================================================================================

Tabletop version notes:

After boot, press a number to start a game:
0: 4 Function Calculator (not a game)
1: Shooting Gallery
2: Black Jack
3: Code Hunter
4: Grand Prix

Screen and keypad overlays were provided for each game, though the default keypad
labels already show the alternate functions.

keypad reference (mapped to PC keyboard A-row and Z-row by default)

Calculator:
  [RET] [MS ] [MR ] [+/-] [.  ] [+= ] [-= ] [x  ] [/  ] [CL ]
  [0  ] [1  ] [2  ] [3  ] [4  ] [5  ] [6  ] [7  ] [8  ] [9  ]

Shooting Gallery:
  [RET] [Cyc] [Zig] [Rnd] [   ] [   ] [   ] [   ] [   ] [   ] * Cyclic, Zigzag, Random
  [   ] [   ] [   ] [   ] [   ] [   ] [   ] [   ] [   ] [   ] * + any of 20 buttons for shooting target

Black Jack:
  [RET] [Dl ] [   ] [   ] [   ] [   ] [   ] [   ] [Hit] [Stn] * Deal, Hit, Stand
  [   ] [   ] [   ] [   ] [   ] [   ] [   ] [   ] [   ] [   ]

Code Hunter:
  [RET] [Sta] [Dis] [   ] [   ] [Ent] [   ] [Crs] [R< ] [R> ] * Start, Display, Enter, Cursor key, Review back, Review ahead
  [   ] [   ] [   ] [   ] [   ] [   ] [   ] [   ] [   ] [   ]

Grand Prix:
  [RET] [Go ] [   ] [   ] [   ] [   ] [   ] [Up ] [Up ] [Up ]
  [Brk] [Gas] [   ] [   ] [   ] [   ] [   ] [Dwn] [Dwn] [Dwn]

*******************************************************************************/

#include "emu.h"

#include "cpu/f8/f8.h"
#include "machine/f3853.h"
#include "machine/netlist.h"
#include "sound/spkrdev.h"
#include "video/pwm.h"

#include "speaker.h"

#include "nl_gamemachine.h"

// internal artwork
#include "gamemach.lh"
#include "v4in1eg.lh"

namespace {

class gm_state : public driver_device
{
public:
	gm_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_psu(*this, "psu"),
		m_display(*this, "display"),
		m_speaker(*this, "speaker"),
		m_sound_nl(*this, "sound_nl:p%02u", 8U),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void v4in1eg(machine_config &config);
	void gamemach(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<f38t56_device> m_psu;
	required_device<pwm_display_device> m_display;
	optional_device<speaker_sound_device> m_speaker;
	optional_device_array<netlist_mame_logic_input_device, 8> m_sound_nl;
	required_ioport_array<2> m_inputs;

	u16 m_mux = 0;
	u8 m_seg_data = 0;
	u8 m_sound_data = 0;

	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	void update_display();
	void mux1_w(u8 data);
	void mux2_w(u8 data);
	void digit_w(u8 data);
	u8 input_r();
	void sound_w(u8 data);
	void discrete_w(u8 data);
	u8 sound_r();
};

void gm_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_mux));
	save_item(NAME(m_seg_data));
	save_item(NAME(m_sound_data));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void gm_state::update_display()
{
	m_display->matrix(m_mux, m_seg_data);
}

void gm_state::mux1_w(u8 data)
{
	// P00-P07: mux part
	m_mux = (m_mux & ~0xff0) | (data << 4 & 0xff0);
	update_display();
}

void gm_state::mux2_w(u8 data)
{
	// P14-P17: mux part
	m_mux = (m_mux & ~0xf) | (data >> 4 & 0xf);
	update_display();
}

void gm_state::digit_w(u8 data)
{
	// P50-P57: digit 7seg data
	m_seg_data = bitswap<8>(data,0,1,2,3,4,5,6,7);
	update_display();
}

u8 gm_state::input_r()
{
	u8 data = 0;

	// P12,P13: multiplexed inputs
	for (int i = 0; i < 12; i++)
		if (BIT(m_mux, i))
			for (int j = 0; j < 2; j++)
				data |= BIT(m_inputs[j]->read(), i) << j;

	return data << 2;
}

void gm_state::sound_w(u8 data)
{
	m_sound_data = data;

	// P40: speaker out
	m_speaker->level_w(data & 1);

	// P44-P47: 4 extra leds
	m_mux = (m_mux & ~0xf000) | (bitswap<4>(data,7,6,4,5) << 12);
	update_display();
}

void gm_state::discrete_w(u8 data)
{
	m_sound_data = data;

	// P40-P47: 555 to speaker (see nl_gamemachine.cpp)
	for (int i = 0; i < 8; i++)
		m_sound_nl[i]->write_line(BIT(~data, i));
}

u8 gm_state::sound_r()
{
	return m_sound_data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void gm_state::main_map(address_map &map)
{
	map.global_mask(0x07ff);
	map(0x0000, 0x07ff).rom();
}

void gm_state::main_io(address_map &map)
{
	map(0x00, 0x00).w(FUNC(gm_state::mux1_w));
	map(0x01, 0x01).rw(FUNC(gm_state::input_r), FUNC(gm_state::mux2_w));
	map(0x04, 0x07).rw("psu", FUNC(f38t56_device::read), FUNC(f38t56_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( gamemach )
	PORT_START("IN.0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_COLON) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("CL")
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CODE(KEYCODE_LEFT) PORT_NAME(u8"÷")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_K) PORT_CODE(KEYCODE_ASTERISK) PORT_CODE(KEYCODE_UP) PORT_NAME(u8"×")
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_J) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-=")
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("+=")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_MINUS) PORT_NAME("+/-")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_END) PORT_NAME("MR")
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_HOME) PORT_NAME("MS")
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_R) PORT_NAME("Return")
	PORT_BIT(0x800, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.1")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_SLASH) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_COMMA) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_DOWN) PORT_NAME("7")
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT(0x800, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( v4in1eg )
	PORT_START("IN.0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("G2: Code / M+")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("G2: Manual / M-")
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("G2: Exam / MR")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("G2: Enter / MC")
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("G3: Black Jack")
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("G2: Brain Drain")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("G4: Calc. / C/CE")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("G1: Speed Way")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("G3: Total / =")
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_UP) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"G1: Up / ÷")
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DOWN) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"G1: Down / ×")
	PORT_BIT(0x800, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("G3: Insur. / -")

	PORT_START("IN.1")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("G3: Clear / +")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME("G3: Bet / .")
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("G1: Gas / 7")
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("G1: Brake / 4")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("G3: Split / 3")
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("G3: Double / 2")
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("G3: Hit / 1")
	PORT_BIT(0x800, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("G3: Stand / 0")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void gm_state::v4in1eg(machine_config &config)
{
	// basic machine hardware
	F8(config, m_maincpu, 4200000/2); // MK3870, approximation (internal /2 divider)
	m_maincpu->set_addrmap(AS_PROGRAM, &gm_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &gm_state::main_io);

	F38T56(config, m_psu, 4200000/2);
	m_psu->write_a().set(FUNC(gm_state::sound_w));
	m_psu->read_a().set(FUNC(gm_state::sound_r));
	m_psu->write_b().set(FUNC(gm_state::digit_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(12+4, 8);
	m_display->set_segmask(0xfff, 0xff);
	m_display->set_bri_levels(0.01, 0.1); // player led may be brighter
	config.set_default_layout(layout_v4in1eg);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

void gm_state::gamemach(machine_config &config)
{
	v4in1eg(config);

	// basic machine hardware
	m_psu->write_a().set(FUNC(gm_state::discrete_w));

	config.set_default_layout(layout_gamemach);

	// sound hardware
	config.device_remove("speaker");
	NETLIST_SOUND(config, "sound_nl", 48000).set_source(NETLIST_NAME(gamemachine)).add_route(ALL_OUTPUTS, "mono", 1.0);
	NETLIST_STREAM_OUTPUT(config, "sound_nl:cout0", 0, "SPK1.2").set_mult_offset(-10000.0 / 32768.0, 10000.0 * 3.75 / 32768.0);

	NETLIST_LOGIC_INPUT(config, "sound_nl:p08", "P08.IN", 0);
	NETLIST_LOGIC_INPUT(config, "sound_nl:p09", "P09.IN", 0);
	NETLIST_LOGIC_INPUT(config, "sound_nl:p10", "P10.IN", 0);
	NETLIST_LOGIC_INPUT(config, "sound_nl:p11", "P11.IN", 0);
	NETLIST_LOGIC_INPUT(config, "sound_nl:p12", "P12.IN", 0);
	NETLIST_LOGIC_INPUT(config, "sound_nl:p13", "P13.IN", 0);
	NETLIST_LOGIC_INPUT(config, "sound_nl:p14", "P14.IN", 0);
	NETLIST_LOGIC_INPUT(config, "sound_nl:p15", "P15.IN", 0);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( gamemach )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD("mk14154n_2001", 0x0000, 0x0800, CRC(6d524c32) SHA1(73d84e59952b751c76dff8bf259b98e1f9136b41) )
ROM_END

ROM_START( v4in1eg )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD("mk14336n_2011", 0x0000, 0x0800, CRC(1846a033) SHA1(8bceff44d80a5d3c1ef6f80a79d03f4083edc280) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS     INIT        COMPANY, FULLNAME, FLAGS
SYST( 1978, gamemach, 0,      0,      gamemach, gamemach, gm_state, empty_init, "VTech / Waddingtons", "The Game Machine", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )

SYST( 1979, v4in1eg,  0,      0,      v4in1eg,  v4in1eg,  gm_state, empty_init, "VTech", "4 in 1 Electronic Games (VTech)", MACHINE_SUPPORTS_SAVE )



gamepock.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol, hap
/*******************************************************************************

Epoch Game Pocket Computer
Japanese LCD handheld console

Hardware notes:
- NEC uPD78C06AG (4KB internal ROM), 6MHz XTAL
- 2KB external RAM(HM6116P-4), up to 28KB external ROM on cartridge
  (28KB in theory, actually the largest game is 16KB)
- 3*HD44102CH, 75*64 1bpp LCD screen
- 1-bit sound

Not counting the mini games included in the BIOS, only 5 games were released.
It takes around 3 seconds for a cartridge to start up, this is normal.

*******************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/upd7810/upd7810.h"
#include "sound/spkrdev.h"
#include "video/hd44102.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "gamepock.lh"

namespace {

class gamepock_state : public driver_device
{
public:
	gamepock_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_lcd(*this, "lcd%u", 0),
		m_speaker(*this, "speaker"),
		m_inputs(*this, "IN%u", 0)
	{ }

	void gamepock(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<upd78c06_device> m_maincpu;
	required_device_array<hd44102_device, 3> m_lcd;
	required_device<speaker_sound_device> m_speaker;
	required_ioport_array<2> m_inputs;

	u8 m_control = 0;
	u8 m_lcd_data = 0;

	void control_w(u8 data);
	void lcd_data_w(u8 data);
	u8 input_r();
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void main_map(address_map &map) ATTR_COLD;
};

void gamepock_state::machine_start()
{
	save_item(NAME(m_control));
	save_item(NAME(m_lcd_data));
}



/*******************************************************************************
    I/O
*******************************************************************************/

// LCD outputs

u32 gamepock_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (int i = 0; i < 3; i++)
	{
		const u8 *src = m_lcd[i]->render();

		for (int sx = 0; sx < 50; sx++)
		{
			for (int sy = 0; sy < 32; sy++)
			{
				int dx = sx;
				int dy = sy;

				// determine destination coordinate
				switch (i)
				{
					// LCD chip #0: top-left, but reverse-x
					case 0:
						dx = 49 - sx;
						break;

					// LCD chip #1: bottom-left
					case 1:
						dy += 32;
						break;

					// LCD chip #2: top-right + bottom-right
					case 2:
						dx = (sx % 25) + 50;
						dy += (sx / 25) * 32;
						break;

					default:
						break;
				}

				if (cliprect.contains(dx, dy))
					bitmap.pix(dy, dx) = src[sy * 50 + sx];
			}
		}
	}

	return 0;
}


// other I/O

void gamepock_state::control_w(u8 data)
{
	// 76------  input select
	// --543---  LCD CS
	// -----2--  LCD D/I (all 3)
	// ------1-  LCD M (all 3)
	// -------0  unknown

	// write to LCD on falling edge of M
	if (~data & m_control & 2)
	{
		for (int i = 0; i < 3; i++)
			if (BIT(data, i + 3))
				m_lcd[i]->write(BIT(data, 2), m_lcd_data);
	}

	m_control = data;
}

void gamepock_state::lcd_data_w(u8 data)
{
	// LCD DB0-DB7
	m_lcd_data = data;
}

u8 gamepock_state::input_r()
{
	u8 data = 0xff;

	for (int i = 0; i < 2; i++)
		if (BIT(m_control, i ^ 7))
			data &= m_inputs[i]->read();

	return data;
}

void gamepock_state::main_map(address_map &map)
{
	// 0x0000-0x0fff is internal ROM
	map(0x0000, 0x7fff).r("cartslot", FUNC(generic_slot_device::read_rom));
	map(0xc000, 0xc7ff).mirror(0x0800).ram();
	// 0xff80-0xffff is internal RAM
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( gamepock )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_SELECT )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1 ) // top-left
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON3 ) // bottom-left
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_START )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON2 ) // top-right
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON4 ) // bottom-right
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void gamepock_state::gamepock(machine_config &config)
{
	// basic machine hardware
	UPD78C06(config, m_maincpu, 6_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &gamepock_state::main_map);
	m_maincpu->pa_out_cb().set(FUNC(gamepock_state::control_w));
	m_maincpu->pb_out_cb().set(FUNC(gamepock_state::lcd_data_w));
	m_maincpu->pc_in_cb().set(FUNC(gamepock_state::input_r));
	m_maincpu->to_func().set(m_speaker, FUNC(speaker_sound_device::level_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_size(75, 64);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(gamepock_state::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	for (int i = 0; i < 3; i++)
		HD44102(config, m_lcd[i]);

	config.set_default_layout(layout_gamepock);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	// cartridge
	GENERIC_CARTSLOT(config, "cartslot", generic_linear_slot, "gamepock_cart");
	SOFTWARE_LIST(config, "cart_list").set_original("gamepock");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( gamepock )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "egpcboot.bin", 0x0000, 0x1000, CRC(ee1ea65d) SHA1(9c7731b5ead721d2cc7f7e2655c5fed9e56db8b0) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY  FULLNAME                FLAGS
SYST( 1984, gamepock, 0,      0,      gamepock, gamepock, gamepock_state, empty_init, "Epoch", "Game Pocket Computer", MACHINE_SUPPORTS_SAVE )



gammonm.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Tryom Gammon Master series, backgammon computer

Gammon Master (published by Cardinal) (not dumped yet)
Gammonmaster II (standard model and Doubler model)

According to the manual, the two GM II have the same housing. GM II has labels
intended for The Doubler, but they don't apply to this model.

Hardware notes:
- PCB label: AN0178 REV I, JRH
- Motorola MC6800P @ ~0.85MHz (no XTAL, main clock and NMI are from a 74124)
- 512 bytes RAM (4*NEC D2111AL-4), 4KB+2KB ROM
- DL-4507 4-digit 7seg panel + other leds for dice and status
- no sound, non-electronic backgammon board is attached

To start a game, press ST twice, then EN. Read the manual for more information.
For test mode, press BO after boot, followed by a number.

TODO:
- what is IN.5 0x04 for? maybe just for led strobe timing (reads it at PC=E1BB,
  sets a counter in RAM offset E3)
- it locks up when holding the 1 key at boot (happens on real machine too),
  what's the purpose of checking this key at power on?

*******************************************************************************/

#include "emu.h"

#include "cpu/m6800/m6800.h"
#include "video/pwm.h"

#include "gammonm2.lh"


namespace {

class gammonm_state : public driver_device
{
public:
	gammonm_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void gammonm2(machine_config &config);

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_ioport_array<6> m_inputs;

	void main_map(address_map &map) ATTR_COLD;

	void led_w(offs_t offset, u8 data);
	void digit_w(offs_t offset, u8 data);
	u8 input_r(offs_t offset);
};



/*******************************************************************************
    I/O
*******************************************************************************/

void gammonm_state::led_w(offs_t offset, u8 data)
{
	// a0-a3 + data: misc leds
	m_display->matrix_partial(4, 4, offset, ~data);
}

void gammonm_state::digit_w(offs_t offset, u8 data)
{
	// a0-a3 + data: 7seg leds
	m_display->matrix_partial(0, 4, offset, bitswap<8>(~data,4,6,3,5,2,7,1,0));
}

u8 gammonm_state::input_r(offs_t offset)
{
	u8 data = 0x0f;

	// read multiplexed inputs
	for (int i = 0; i < 5; i++)
		if (!BIT(offset, i))
			data &= m_inputs[i]->read();

	return data | m_inputs[5]->read() << 4;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void gammonm_state::main_map(address_map &map)
{
	map(0x0000, 0x01ff).ram();
	map(0x4000, 0x400f).mirror(0x1ff0).w(FUNC(gammonm_state::led_w));
	map(0x6000, 0x601f).mirror(0x1fe0).r(FUNC(gammonm_state::input_r));
	map(0x8000, 0x800f).mirror(0x1ff0).w(FUNC(gammonm_state::digit_w));
	map(0xe000, 0xe7ff).rom();
	map(0xf000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( gammonm2 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("ST")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CL")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("EN")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_UP) PORT_NAME("Up")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("BA")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("BO")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("VR")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Down")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("10")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_MINUS) PORT_NAME("11")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("12")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")

	PORT_START("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_CONFNAME( 0x08, 0x00, "Version" ) // factory-set
	PORT_CONFSETTING(    0x08, "Standard" )
	PORT_CONFSETTING(    0x00, "The Doubler" )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void gammonm_state::gammonm2(machine_config &config)
{
	// basic machine hardware
	M6800(config, m_maincpu, 850000); // measured
	m_maincpu->set_addrmap(AS_PROGRAM, &gammonm_state::main_map);

	const attotime nmi_period = attotime::from_hz(262); // measured
	m_maincpu->set_periodic_int(FUNC(gammonm_state::nmi_line_pulse), nmi_period);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 8);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_gammonm2);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( gammonm2 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("c38083_bc3.0-s3", 0xe000, 0x0800, CRC(5a82df02) SHA1(dbad870b0fc69c1af892441aa5a851366c7b04a6) )
	ROM_LOAD("d2332c_027",      0xf000, 0x1000, CRC(407e015e) SHA1(09900a672e8cc6f280253544511e6e08a1c78a02) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1978, gammonm2, 0,      0,      gammonm2, gammonm2, gammonm_state, empty_init, "Tryom", "Gammonmaster II", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )



gb.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Anthony Kruize,Wilbert Pol
/***************************************************************************

  gb.c

  Driver file to handle emulation of the Nintendo Game Boy.
  By:

  Hans de Goede               1998
  Anthony Kruize              2002
  Wilbert Pol                 2004 (Megaduck/Cougar Boy)

  TODO list:
  - Do correct LCD stat timing
  - Add Game Boy Light (Japan, 1997) - does it differ from gbpocket?
  - SGB should be moved to SNES driver
  - Emulate OAM corruption bug on 16bit inc/dec in $fe** region

***************************************************************************/

#include "emu.h"

#include "bus/gameboy/carts.h"
#include "bus/gameboy/gbslot.h"
#include "bus/gameboy/mdslot.h"
#include "cpu/lr35902/lr35902.h"
#include "machine/ram.h"
#include "sound/gb.h"
#include "video/gb_lcd.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

//#define VERBOSE 1
#include "logmacro.h"


namespace {


#define DMG_FRAMES_PER_SECOND   59.732155
#define SGB_FRAMES_PER_SECOND   61.17


class base_state : public driver_device
{
public:
	base_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_cartslot(*this, "cartslot"),
		m_maincpu(*this, "maincpu"),
		m_apu(*this, "apu"),
		m_inputs(*this, "INPUTS"),
		m_ppu(*this, "ppu"),
		m_palette(*this, "palette")
	{ }

protected:
	enum
	{
		SIO_ENABLED = 0x80,
		SIO_FAST_CLOCK = 0x02,
		SIO_INTERNAL_CLOCK = 0x01
	};

	static constexpr XTAL MASTER_CLOCK = 4.194304_MHz_XTAL;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void gb_io_w(offs_t offset, uint8_t data);
	uint8_t gb_ie_r();
	void gb_ie_w(uint8_t data);
	uint8_t gb_io_r(offs_t offset);

	void gb_timer_callback(uint8_t data);

	void gb_init_regs();

	uint8_t       m_gb_io[0x10]{};

	/* Timer related */
	uint16_t      m_divcount = 0;
	uint8_t       m_shift = 0;
	uint16_t      m_shift_cycles = 0;
	uint8_t       m_triggering_irq = 0;
	uint8_t       m_reloading = 0;

	/* Serial I/O related */
	uint16_t      m_internal_serial_clock = 0;
	uint16_t      m_internal_serial_frequency = 0;
	uint32_t      m_sio_count = 0;             /* Serial I/O counter */

	required_device<gb_cart_slot_device_base> m_cartslot;

	required_device<lr35902_cpu_device> m_maincpu;
	required_device<gameboy_sound_device> m_apu;
	required_ioport m_inputs;
	required_device<dmg_ppu_device> m_ppu;
	required_device<palette_device> m_palette;

private:
	void gb_timer_increment();
	void gb_timer_check_irq();
	void gb_serial_timer_tick();
};


class gb_state : public base_state
{
public:
	gb_state(const machine_config &mconfig, device_type type, const char *tag) :
		base_state(mconfig, type, tag),
		m_region_boot(*this, "maincpu"),
		m_boot_view(*this, "boot"),
		m_bios_hack(*this, "SKIP_CHECK")
	{ }

	void gameboy(machine_config &config);
	void gbpocket(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void disable_boot();

	void gb_io2_w(offs_t offset, uint8_t data);

	required_region_ptr<uint8_t> m_region_boot;
	memory_view m_boot_view;

private:
	u8 boot_r(offs_t offset);

	void gb_palette(palette_device &palette) const;
	void gbp_palette(palette_device &palette) const;

	void gameboy_map(address_map &map) ATTR_COLD;

	required_ioport m_bios_hack;
};


class sgb_state : public gb_state
{
public:
	sgb_state(const machine_config &mconfig, device_type type, const char *tag) :
		gb_state(mconfig, type, tag)
	{ }

	void supergb(machine_config &config);
	void supergb2(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void sgb_palette(palette_device &palette) const;

	void sgb_io_w(offs_t offset, uint8_t data);

	void sgb_map(address_map &map) ATTR_COLD;

	int8_t m_sgb_packets = 0;
	uint8_t m_sgb_bitcount = 0;
	uint8_t m_sgb_bytecount = 0;
	uint8_t m_sgb_start = 0;
	uint8_t m_sgb_rest = 0;
	uint8_t m_sgb_controller_no = 0;
	uint8_t m_sgb_controller_mode = 0;
	uint8_t m_sgb_data[0x100]{};
};


class gbc_state : public gb_state
{
public:
	gbc_state(const machine_config &mconfig, device_type type, const char *tag) :
		gb_state(mconfig, type, tag),
		m_rambank(*this, "cgb_ram"),
		m_bankedram(*this, "banked_ram", 7 * 0x1000, ENDIANNESS_LITTLE)
	{ }

	void gbcolor(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	static constexpr XTAL GBC_CLOCK = 8.388_MHz_XTAL;

	void gbc_io_w(offs_t offset, uint8_t data);
	void gbc_io2_w(offs_t offset, uint8_t data);
	uint8_t gbc_io2_r(offs_t offset);

	void gbc_map(address_map &map) ATTR_COLD;

	required_memory_bank m_rambank;
	memory_share_creator<uint8_t> m_bankedram;

	memory_passthrough_handler m_boot_high_tap;
};


class megaduck_state : public base_state
{
public:
	megaduck_state(const machine_config &mconfig, device_type type, const char *tag) :
		base_state(mconfig, type, tag)
	{ }

	void megaduck(machine_config &config);

private:
	uint8_t megaduck_video_r(offs_t offset);
	void megaduck_video_w(offs_t offset, uint8_t data);
	void megaduck_sound_w1(offs_t offset, uint8_t data);
	uint8_t megaduck_sound_r1(offs_t offset);
	void megaduck_sound_w2(offs_t offset, uint8_t data);
	uint8_t megaduck_sound_r2(offs_t offset);
	void megaduck_palette(palette_device &palette) const;
	void megaduck_map(address_map &map) ATTR_COLD;
};



/* RAM layout defines */

#define JOYPAD      m_gb_io[0x00]   // Joystick: 1.1.P15.P14.P13.P12.P11.P10
#define SIODATA     m_gb_io[0x01]   // Serial IO data buffer
#define SIOCONT     m_gb_io[0x02]   // Serial IO control register
#define TIMECNT     m_gb_io[0x05]   // Timer counter. Gen. int. when it overflows
#define TIMEMOD     m_gb_io[0x06]   // New value of TimeCount after it overflows
#define TIMEFRQ     m_gb_io[0x07]   // Timer frequency and start/stop switch


void base_state::gb_init_regs()
{
	/* Initialize the registers */
	SIODATA = 0x00;
	SIOCONT = 0x7E;

	gb_io_w(0x05, 0x00);       /* TIMECNT */
	gb_io_w(0x06, 0x00);       /* TIMEMOD */
}


void base_state::machine_start()
{
	save_item(NAME(m_gb_io));
	save_item(NAME(m_divcount));
	save_item(NAME(m_shift));
	save_item(NAME(m_shift_cycles));
	save_item(NAME(m_triggering_irq));
	save_item(NAME(m_reloading));
	save_item(NAME(m_sio_count));
}

void gb_state::machine_start()
{
	base_state::machine_start();

	m_maincpu->space(AS_PROGRAM).install_view(0x0000, 0x08ff, m_boot_view);
	m_boot_view[0].install_read_handler(0x0000, 0x00ff, read8sm_delegate(*this, NAME(&gb_state::boot_r)));
}

void gbc_state::machine_start()
{
	gb_state::machine_start();

	m_boot_view[0].install_rom(0x0200, 0x08ff, &m_region_boot[0x100]);

	m_rambank->configure_entry(0, &m_bankedram[0]);
	m_rambank->configure_entries(1, 7, &m_bankedram[0], 0x1000);
}

void sgb_state::machine_start()
{
	gb_state::machine_start();

	m_sgb_packets = -1;

	save_item(NAME(m_sgb_packets));
	save_item(NAME(m_sgb_bitcount));
	save_item(NAME(m_sgb_bytecount));
	save_item(NAME(m_sgb_start));
	save_item(NAME(m_sgb_rest));
	save_item(NAME(m_sgb_controller_no));
	save_item(NAME(m_sgb_controller_mode));
	save_item(NAME(m_sgb_data));
}


void base_state::machine_reset()
{
	m_apu->sound_w(0x16, 0x00); // Initialize sound hardware

	m_divcount = 8;
	m_internal_serial_clock = 0;
	m_internal_serial_frequency = 512 / 2;
	m_triggering_irq = 0;
	m_shift = 10; // slowest timer?
	m_shift_cycles = 1 << m_shift;

	// Set registers to default/startup values
	m_gb_io[0x00] = 0xcf;
	m_gb_io[0x01] = 0x00;
	m_gb_io[0x02] = 0x7e;
	m_gb_io[0x03] = 0xff;
	m_gb_io[0x07] = 0xf8;       // Upper bits of TIMEFRQ register are set to 1
}

void gb_state::machine_reset()
{
	base_state::machine_reset();

	m_boot_view.select(0);
}

void gbc_state::machine_reset()
{
	gb_state::machine_reset();

	gb_init_regs();

	std::fill_n(&m_bankedram[0], m_bankedram.length(), 0);
}

void sgb_state::machine_reset()
{
	gb_state::machine_reset();

	gb_init_regs();
}


void gb_state::disable_boot()
{
	m_boot_view.disable();
}


void base_state::gb_io_w(offs_t offset, uint8_t data)
{
	static const uint8_t timer_shifts[4] = {10, 4, 6, 8};

	switch (offset)
	{
	case 0x00:                      /* JOYP - Joypad */
		JOYPAD = 0xCF | data;
		if (!(data & 0x20))
			JOYPAD &= (m_inputs->read() >> 4) | 0xF0;
		if (!(data & 0x10))
			JOYPAD &= m_inputs->read() | 0xF0;
		return;
	case 0x01:                      /* SB - Serial transfer data */
		break;
	case 0x02:                      /* SC - SIO control */
		switch (data & 0x81)
		{
		case 0x00:
		case 0x01:
			m_sio_count = 0;
			break;
		case 0x80:              /* enabled & external clock */
			m_sio_count = 16;
			break;
		case 0x81:              /* enabled & internal clock */
			m_sio_count = 16;
			break;
		}
logerror("SIOCONT write, serial clock is %04x\n", m_internal_serial_clock);
		data |= 0x7E; // unused bits stay high
		break;
	case 0x03:
		return;
	case 0x04:                      /* DIV - Divider register */
		/* Force increment of TIMECNT register when the 'highest' bit is set */
		if ((m_divcount >> (m_shift - 1)) & 1)
		{
			gb_timer_increment();
		}
		LOG("DIV write\n");
		m_divcount = 0;
		return;
	case 0x05:                      /* TIMA - Timer counter */
		/* Check if the counter is being reloaded in this cycle */
		if ((TIMEFRQ & 0x04) && TIMECNT == TIMEMOD && (m_divcount & (m_shift_cycles - 1)) == 4)
		{
			data = TIMEMOD;
		}
		break;
	case 0x06:                      /* TMA - Timer module */
		/* Check if the counter is being reloaded in this cycle */
		if ((TIMEFRQ & 0x04) && TIMECNT == TIMEMOD && (m_divcount & (m_shift_cycles - 1)) == 4)
		{
			TIMECNT = data;
		}
		break;
	case 0x07:                      /* TAC - Timer control */
		data |= 0xF8;
		/* Check if timer is just disabled or the timer frequency is changing */
		if ((!(data & 0x04) && (TIMEFRQ & 0x04)) || ((data & 0x04) && (TIMEFRQ & 0x04) && (data & 0x03) != (TIMEFRQ & 0x03)))
		{
			/* Check if TIMECNT should be incremented */
			if ((m_divcount & (m_shift_cycles - 1)) >= (m_shift_cycles >> 1))
			{
				gb_timer_increment();
			}
		}
		m_shift = timer_shifts[data & 0x03];
		m_shift_cycles = 1 << m_shift;
		break;
	case 0x0F:                      /* IF - Interrupt flag */
		m_ppu->update_state();
		LOG("write if\n");
		data &= 0x1F;
		m_maincpu->set_if(data);
		break;
	}

	m_gb_io[offset] = data;
}

void gb_state::gb_io2_w(offs_t offset, uint8_t data)
{
	if (offset == 0x10)
		disable_boot(); // disable boot ROM
	else
		m_ppu->video_w(offset, data);
}

u8 gb_state::boot_r(offs_t offset)
{
	if (m_bios_hack->read())
	{
		// patch out logo and checksum checks
		// useful to run some pirate carts until properly emulated, or to test homebrew
		if (offset == 0xe9 || offset == 0xea)
			return 0x00;
		if (offset == 0xfa || offset == 0xfb)
			return 0x00;
	}
	return m_region_boot[offset];
}

#ifdef MAME_DEBUG
static const char *const sgbcmds[32] =
{
	/* 0x00 */ "PAL01   ",
	/* 0x01 */ "PAL23   ",
	/* 0x02 */ "PAL03   ",
	/* 0x03 */ "PAL12   ",
	/* 0x04 */ "ATTR_BLK",
	/* 0x05 */ "ATTR_LIN",
	/* 0x06 */ "ATTR_DIV",
	/* 0x07 */ "ATTR_CHR",
	/* 0x08 */ "SOUND   ",
	/* 0x09 */ "SOU_TRN ",
	/* 0x0A */ "PAL_SET ",
	/* 0x0B */ "PAL_TRN ",
	/* 0x0C */ "ATRC_EN ",
	/* 0x0D */ "TEST_EN ",
	/* 0x0E */ "ICON_EN ",
	/* 0x0F */ "DATA_SND",
	/* 0x10 */ "DATA_TRN",
	/* 0x11 */ "MLT_REG ",
	/* 0x12 */ "JUMP    ",
	/* 0x13 */ "CHR_TRN ",
	/* 0x14 */ "PCT_TRN ",
	/* 0x15 */ "ATTR_TRN",
	/* 0x16 */ "ATTR_SET",
	/* 0x17 */ "MASK_EN ",
	/* 0x18 */ "OBJ_TRN ",
	/* 0x19 */ "PAL_PRI ",
	/* 0x1A */ "????????",
	/* 0x1B */ "????????",
	/* 0x1C */ "????????",
	/* 0x1D */ "????????",
	/* 0x1E */ "????????",
	/* 0x1F */ "????????"
};
#endif

void sgb_state::sgb_io_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
		case 0x00:
			switch (data & 0x30)
			{
			case 0x00:                 /* start condition */
				if (m_sgb_start)
					logerror("SGB: Start condition before end of transfer ??\n");
				m_sgb_bitcount = 0;
				m_sgb_start = 1;
				m_sgb_rest = 0;
				JOYPAD = 0x0F & ((m_inputs->read() >> 4) | m_inputs->read() | 0xF0);
				break;
			case 0x10:                 /* data true */
				if (m_sgb_rest)
				{
					/* We should test for this case , but the code below won't
					   work with the current setup */
#if 0
					if (m_sgb_bytecount == 16)
					{
						logerror("SGB: end of block is not zero!");
						m_sgb_start = 0;
					}
#endif
					m_sgb_data[m_sgb_bytecount] >>= 1;
					m_sgb_data[m_sgb_bytecount] |= 0x80;
					m_sgb_bitcount++;
					if (m_sgb_bitcount == 8)
					{
						m_sgb_bitcount = 0;
						m_sgb_bytecount++;
					}
					m_sgb_rest = 0;
				}
				JOYPAD = 0x1F & ((m_inputs->read() >> 4) | 0xF0);
				break;
			case 0x20:              /* data false */
				if (m_sgb_rest)
				{
					if (m_sgb_bytecount == 16 && m_sgb_packets == -1)
					{
#ifdef MAME_DEBUG
						LOG("SGB: %s (%02X) pkts: %d data: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
								sgbcmds[m_sgb_data[0] >> 3],m_sgb_data[0] >> 3, m_sgb_data[0] & 0x07, m_sgb_data[1], m_sgb_data[2], m_sgb_data[3],
								m_sgb_data[4], m_sgb_data[5], m_sgb_data[6], m_sgb_data[7],
								m_sgb_data[8], m_sgb_data[9], m_sgb_data[10], m_sgb_data[11],
								m_sgb_data[12], m_sgb_data[13], m_sgb_data[14], m_sgb_data[15]);
#endif
						m_sgb_packets = m_sgb_data[0] & 0x07;
						m_sgb_start = 0;
					}
					if (m_sgb_bytecount == (m_sgb_packets << 4))
					{
						switch (m_sgb_data[0] >> 3)
						{
							case 0x11:  /* MLT_REQ - Multi controller request */
								if (m_sgb_data[1] == 0x00)
									m_sgb_controller_mode = 0;
								else if (m_sgb_data[1] == 0x01)
									m_sgb_controller_mode = 2;
								break;
							default:
								dynamic_cast<sgb_ppu_device*>(m_ppu.target())->sgb_io_write_pal(m_sgb_data[0] >> 3, &m_sgb_data[0]);
								break;
						}
						m_sgb_start = 0;
						m_sgb_bytecount = 0;
						m_sgb_packets = -1;
					}
					if (m_sgb_start)
					{
						m_sgb_data[m_sgb_bytecount] >>= 1;
						m_sgb_bitcount++;
						if (m_sgb_bitcount == 8)
						{
							m_sgb_bitcount = 0;
							m_sgb_bytecount++;
						}
					}
					m_sgb_rest = 0;
				}
				JOYPAD = 0x2F & (m_inputs->read() | 0xF0);
				break;
			case 0x30:                 /* rest condition */
				if (m_sgb_start)
					m_sgb_rest = 1;
				if (m_sgb_controller_mode)
				{
					m_sgb_controller_no++;
					if (m_sgb_controller_no == m_sgb_controller_mode)
						m_sgb_controller_no = 0;
					JOYPAD = 0x3F - m_sgb_controller_no;
				}
				else
					JOYPAD = 0x3F;

				/* Hack to let cartridge know it's running on an SGB */
				if ((m_sgb_data[0] >> 3) == 0x1F)
					JOYPAD = 0x3E;
				break;
			}
			return;
		default:
			/* we didn't handle the write, so pass it to the GB handler */
			gb_io_w(offset, data);
			return;
	}

	m_gb_io[offset] = data;
}

/* Interrupt Enable register */
uint8_t base_state::gb_ie_r()
{
	return m_maincpu->get_ie();
}

void base_state::gb_ie_w(uint8_t data)
{
	m_maincpu->set_ie(data);
}

/* IO read */
uint8_t base_state::gb_io_r(offs_t offset)
{
	switch(offset)
	{
		case 0x04:
			LOG("read DIV, divcount = %04x\n", m_divcount);
			return (m_divcount >> 8) & 0xFF;
		case 0x00:
		case 0x01:
		case 0x02:
		case 0x03:
		case 0x05:
		case 0x06:
		case 0x07:
			return m_gb_io[offset];
		case 0x0F:
			/* Make sure the internal states are up to date */
			m_ppu->update_state();
			LOG("read if\n");
logerror("IF read, serial clock is %04x\n", m_internal_serial_clock);
			return 0xE0 | m_maincpu->get_if();
		default:
			/* Unsupported registers return 0xFF */
			return 0xFF;
	}
}


/* Called when 512 internal cycles are passed */
void base_state::gb_serial_timer_tick()
{
	if (SIOCONT & SIO_ENABLED)
	{
		if (m_sio_count & 1)
		{
			/* Shift in a received bit */
			SIODATA = (SIODATA << 1) | 0x01;
		}
		/* Decrement number of handled bits */
		m_sio_count--;

		LOG("%04x - gb_serial_timer_proc: SIODATA = %02x, sio_count = %u\n", m_maincpu->pc(), SIODATA, m_sio_count);
		/* If all bits done, stop timer and trigger interrupt */
		if (m_sio_count == 0)
		{
			SIOCONT &= ~SIO_ENABLED;
			m_maincpu->set_input_line(lr35902_cpu_device::SIO_INT, ASSERT_LINE);
			// Make sure the state is updated during the current timeslice in case it is read.
			m_maincpu->execute_set_input(lr35902_cpu_device::SIO_INT, ASSERT_LINE);
		}
	}
}


void base_state::gb_timer_check_irq()
{
	m_reloading = 0;
	if (m_triggering_irq)
	{
		m_triggering_irq = 0;
		if (TIMECNT == 0)
		{
			TIMECNT = TIMEMOD;
			m_maincpu->set_input_line(lr35902_cpu_device::TIM_INT, ASSERT_LINE);
			// Make sure the state is updated during the current timeslice in case it is read.
			m_maincpu->execute_set_input(lr35902_cpu_device::TIM_INT, ASSERT_LINE);
			m_reloading = 1;
		}
	}
}

void base_state::gb_timer_increment()
{
	gb_timer_check_irq();

	LOG("increment timer\n");
	TIMECNT += 1;
	if (TIMECNT == 0)
	{
		m_triggering_irq = 1;
	}
}

// This gets called while the cpu is executing instructions to keep the timer state in sync
void base_state::gb_timer_callback(uint8_t data)
{
	uint16_t old_gb_divcount = m_divcount;
	uint16_t old_internal_serial_clock = m_internal_serial_clock;
	m_divcount += data;
	m_internal_serial_clock += data;

	if ((old_gb_divcount >> 8) != (m_divcount >> 8))
	{
		//LOG("DIV became %02x\n", m_divcount >> 8);
	}
	gb_timer_check_irq();

	if (TIMEFRQ & 0x04)
	{
		uint16_t old_count = old_gb_divcount >> m_shift;
		uint16_t new_count = m_divcount >> m_shift;
		if (data > m_shift_cycles)
		{
			gb_timer_increment();
			old_count++;
		}
		if (new_count != old_count)
		{
			gb_timer_increment();
			if (new_count << m_shift < m_divcount)
			{
				gb_timer_check_irq();
			}
		}
	}

	if (((m_internal_serial_clock ^ old_internal_serial_clock) & m_internal_serial_frequency) && (SIOCONT & SIO_INTERNAL_CLOCK))
	{
		gb_serial_timer_tick();
	}
}


void gbc_state::gbc_io_w(offs_t offset, uint8_t data)
{
	gb_io_w(offset, data);

	// On CGB the internal serial transfer clock is selectable
	if (offset == 0x02)
	{
		m_internal_serial_frequency = ((data & SIO_FAST_CLOCK) ? 16 : 512) / 2;
		SIOCONT = (SIOCONT & ~SIO_FAST_CLOCK) | (data & SIO_FAST_CLOCK);
	}
}


void gbc_state::gbc_io2_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
		case 0x0D:  // KEY1 - Prepare speed switch
			m_maincpu->set_speed(data);
			return;
		case 0x10:  // BFF - boot ROM disable
			disable_boot();
			return;
		case 0x16:  // RP - Infrared port
			break;
		case 0x30:  // SVBK - RAM bank select
			m_rambank->set_entry(data & 0x07);
			break;
		default:
			break;
	}
	m_ppu->video_w(offset, data);
}

uint8_t gbc_state::gbc_io2_r(offs_t offset)
{
	switch (offset)
	{
	case 0x0D:  // KEY1
		return m_maincpu->get_speed();
	case 0x16:  // RP - Infrared port
		break;
	case 0x30:  // SVBK - RAM bank select
		return m_rambank->entry();
	default:
		break;
	}
	return m_ppu->video_r(offset);
}

/****************************************************************************

  Megaduck routines

 ****************************************************************************/

/*
 Map megaduck video related area on to regular Game Boy video area

 Different locations of the video registers:
 Register      Game Boy   Mega Duck
 LCDC          FF40       FF10  (See different bit order below)
 STAT          FF41       FF11
 SCY           FF42       FF12
 SCX           FF43       FF13
 LY            FF44       FF18
 LYC           FF45       FF19
 DMA           FF46       FF1A
 BGP           FF47       FF1B
 OBP0          FF48       FF14
 OBP1          FF49       FF15
 WY            FF4A       FF16
 WX            FF4B       FF17
 Unused        FF4C       FF4C (?)
 Unused        FF4D       FF4D (?)
 Unused        FF4E       FF4E (?)
 Unused        FF4F       FF4F (?)

 Different LCDC register

 Game Boy       Mega Duck
 0                      6       - BG & Window Display : 0 - Off, 1 - On
 1                      0       - OBJ Display: 0 - Off, 1 - On
 2                      1       - OBJ Size: 0 - 8x8, 1 - 8x16
 3                      2       - BG Tile Map Display: 0 - 9800, 1 - 9C00
 4                      4       - BG & Window Tile Data Select: 0 - 8800, 1 - 8000
 5                      5       - Window Display: 0 - Off, 1 - On
 6                      3       - Window Tile Map Display Select: 0 - 9800, 1 - 9C00
 7                      7       - LCD Operation

 **************/

uint8_t megaduck_state::megaduck_video_r(offs_t offset)
{
	uint8_t data;

	if ((offset & 0x0C) && ((offset & 0x0C) ^ 0x0C))
	{
		offset ^= 0x0C;
	}
	data = m_ppu->video_r(offset);
	if (offset)
		return data;
	return bitswap<8>(data,7,0,5,4,6,3,2,1);
}

void megaduck_state::megaduck_video_w(offs_t offset, uint8_t data)
{
	if (!offset)
	{
		data = bitswap<8>(data,7,3,5,4,2,1,0,6);
	}
	if ((offset & 0x0C) && ((offset & 0x0C) ^ 0x0C))
	{
		offset ^= 0x0C;
	}
	m_ppu->video_w(offset, data);
}

// Map megaduck audio offset to game boy audio offsets
// Envelope and LFSR register nibbles are reversed relative to the game boy

static const uint8_t megaduck_sound_offsets[16] = { 0, 2, 1, 3, 4, 6, 5, 7, 8, 9, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };

void megaduck_state::megaduck_sound_w1(offs_t offset, uint8_t data)
{
	if ((offset == 0x01) || (offset == 0x07))
		m_apu->sound_w(megaduck_sound_offsets[offset], ((data & 0x0f)<<4) | ((data & 0xf0)>>4));
	else
		m_apu->sound_w(megaduck_sound_offsets[offset], data);
}

uint8_t megaduck_state::megaduck_sound_r1(offs_t offset)
{
	uint8_t data = m_apu->sound_r(megaduck_sound_offsets[offset]);
	if ((offset == 0x01) || (offset == 0x07))
		return ((data & 0x0f)<<4) | ((data & 0xf0)>>4);
	else
		return data;
}

void megaduck_state::megaduck_sound_w2(offs_t offset, uint8_t data)
{
	if ((offset == 0x01) || (offset == 0x02))
		m_apu->sound_w(0x10 + megaduck_sound_offsets[offset], ((data & 0x0f)<<4) | ((data & 0xf0)>>4));
	else
		m_apu->sound_w(0x10 + megaduck_sound_offsets[offset], data);
}

uint8_t megaduck_state::megaduck_sound_r2(offs_t offset)
{
	uint8_t data = m_apu->sound_r(0x10 + megaduck_sound_offsets[offset]);
	if ((offset == 0x01) || (offset == 0x02))
		return ((data & 0x0f)<<4) | ((data & 0xf0)>>4);
	else
		return data;
}


void gb_state::gameboy_map(address_map &map)
{
	map.unmap_value_high();
	map(0x8000, 0x9fff).rw(m_ppu, FUNC(dmg_ppu_device::vram_r), FUNC(dmg_ppu_device::vram_w));
	map(0xc000, 0xdfff).mirror(0x2000).ram();
	map(0xfe00, 0xfeff).rw(m_ppu, FUNC(dmg_ppu_device::oam_r), FUNC(dmg_ppu_device::oam_w));
	map(0xff00, 0xff0f).rw(FUNC(gb_state::gb_io_r), FUNC(gb_state::gb_io_w));
	map(0xff10, 0xff26).rw(m_apu, FUNC(gameboy_sound_device::sound_r), FUNC(gameboy_sound_device::sound_w));
	map(0xff27, 0xff2f).noprw();
	map(0xff30, 0xff3f).rw(m_apu, FUNC(gameboy_sound_device::wave_r), FUNC(gameboy_sound_device::wave_w));
	map(0xff40, 0xff7f).r(m_ppu, FUNC(dmg_ppu_device::video_r)).w(FUNC(gb_state::gb_io2_w));
	map(0xff80, 0xfffe).ram();
	map(0xffff, 0xffff).rw(FUNC(gb_state::gb_ie_r), FUNC(gb_state::gb_ie_w));
}

void sgb_state::sgb_map(address_map &map)
{
	map.unmap_value_high();
	map(0x8000, 0x9fff).rw(m_ppu, FUNC(sgb_ppu_device::vram_r), FUNC(sgb_ppu_device::vram_w));
	map(0xc000, 0xdfff).mirror(0x2000).ram();
	map(0xfe00, 0xfeff).rw(m_ppu, FUNC(sgb_ppu_device::oam_r), FUNC(sgb_ppu_device::oam_w));
	map(0xff00, 0xff0f).rw(FUNC(sgb_state::gb_io_r), FUNC(sgb_state::sgb_io_w));
	map(0xff10, 0xff26).rw(m_apu, FUNC(gameboy_sound_device::sound_r), FUNC(gameboy_sound_device::sound_w));
	map(0xff27, 0xff2f).noprw();
	map(0xff30, 0xff3f).rw(m_apu, FUNC(gameboy_sound_device::wave_r), FUNC(gameboy_sound_device::wave_w));
	map(0xff40, 0xff7f).r(m_ppu, FUNC(sgb_ppu_device::video_r)).w(FUNC(sgb_state::gb_io2_w));
	map(0xff80, 0xfffe).ram();
	map(0xffff, 0xffff).rw(FUNC(sgb_state::gb_ie_r), FUNC(sgb_state::gb_ie_w));
}

void gbc_state::gbc_map(address_map &map)
{
	map.unmap_value_high();
	map(0x8000, 0x9fff).rw(m_ppu, FUNC(cgb_ppu_device::vram_r), FUNC(cgb_ppu_device::vram_w));
	map(0xc000, 0xcfff).mirror(0x2000).ram();
	map(0xd000, 0xdfff).mirror(0x2000).bankrw(m_rambank);
	map(0xfe00, 0xfeff).rw(m_ppu, FUNC(cgb_ppu_device::oam_r), FUNC(cgb_ppu_device::oam_w));
	map(0xff00, 0xff0f).rw(FUNC(gbc_state::gb_io_r), FUNC(gbc_state::gbc_io_w));
	map(0xff10, 0xff26).rw(m_apu, FUNC(gameboy_sound_device::sound_r), FUNC(gameboy_sound_device::sound_w));
	map(0xff27, 0xff2f).noprw();
	map(0xff30, 0xff3f).rw(m_apu, FUNC(gameboy_sound_device::wave_r), FUNC(gameboy_sound_device::wave_w));
	map(0xff40, 0xff7f).rw(FUNC(gbc_state::gbc_io2_r), FUNC(gbc_state::gbc_io2_w));
	map(0xff80, 0xfffe).ram();
	map(0xffff, 0xffff).rw(FUNC(gbc_state::gb_ie_r), FUNC(gbc_state::gb_ie_w));
}

void megaduck_state::megaduck_map(address_map &map)
{
	map.unmap_value_high();
	map(0x8000, 0x9fff).rw(m_ppu, FUNC(dmg_ppu_device::vram_r), FUNC(dmg_ppu_device::vram_w));
	map(0xc000, 0xfdff).ram();    // 8k or 16k? RAM
	map(0xfe00, 0xfeff).rw(m_ppu, FUNC(dmg_ppu_device::oam_r), FUNC(dmg_ppu_device::oam_w));
	map(0xff00, 0xff0f).rw(FUNC(megaduck_state::gb_io_r), FUNC(megaduck_state::gb_io_w));
	map(0xff10, 0xff1f).rw(FUNC(megaduck_state::megaduck_video_r), FUNC(megaduck_state::megaduck_video_w));
	map(0xff20, 0xff2f).rw(FUNC(megaduck_state::megaduck_sound_r1), FUNC(megaduck_state::megaduck_sound_w1));
	map(0xff30, 0xff3f).rw(m_apu, FUNC(gameboy_sound_device::wave_r), FUNC(gameboy_sound_device::wave_w));
	map(0xff40, 0xff46).rw(FUNC(megaduck_state::megaduck_sound_r2), FUNC(megaduck_state::megaduck_sound_w2));
	map(0xff47, 0xff7f).noprw();
	map(0xff80, 0xfffe).ram();
	map(0xffff, 0xffff).rw(FUNC(megaduck_state::gb_ie_r), FUNC(megaduck_state::gb_ie_w));
}


static INPUT_PORTS_START( megaduck )
	PORT_START("INPUTS")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)  PORT_NAME("Left")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_NAME("Right")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)    PORT_NAME("Up")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)  PORT_NAME("Down")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON2)        PORT_NAME("Button A")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1)        PORT_NAME("Button B")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START)          PORT_NAME("Start")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SELECT)         PORT_NAME("Select")
INPUT_PORTS_END

static INPUT_PORTS_START( gameboy )
	PORT_INCLUDE(megaduck)

	PORT_START("SKIP_CHECK")
	PORT_CONFNAME( 0x01, 0x00, "[HACK] Skip BIOS Logo check" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
INPUT_PORTS_END



static constexpr rgb_t palette_gb[] =
{
	// Simple black and white palette
	/*  0xff,0xff,0xff,
	 0xb0,0xb0,0xb0,
	 0x60,0x60,0x60,
	 0x00,0x00,0x00 */

	// Possibly needs a little more green in it
	{ 0xff,0xfb,0x87 },     // Background
	{ 0xb1,0xae,0x4e },     // Light
	{ 0x84,0x80,0x4e },     // Medium
	{ 0x4e,0x4e,0x4e },     // Dark

	// Palette for Game Boy Pocket/Light
	{ 0xc4,0xcf,0xa1 },     // Background
	{ 0x8b,0x95,0x6d },     // Light
	{ 0x6b,0x73,0x53 },     // Medium
	{ 0x41,0x41,0x41 },     // Dark
};

static constexpr rgb_t palette_megaduck[] = {
	{ 0x6b, 0xa6, 0x4a }, { 0x43, 0x7a, 0x63 }, { 0x25, 0x59, 0x55 }, { 0x12, 0x42, 0x4c }
};

// Initialise the palettes
void gb_state::gb_palette(palette_device &palette) const
{
	for (int i = 0; i < 4; i++)
		palette.set_pen_color(i, palette_gb[i]);
}

void gb_state::gbp_palette(palette_device &palette) const
{
	for (int i = 0; i < 4; i++)
		palette.set_pen_color(i, palette_gb[i + 4]);
}

void sgb_state::sgb_palette(palette_device &palette) const
{
	for (int i = 0; i < 32768; i++)
	{
		int const r = i & 0x1f;
		int const g = (i >> 5) & 0x1f;
		int const b = (i >> 10) & 0x1f;
		palette.set_pen_color(i, pal5bit(r), pal5bit(g), pal5bit(b));
	}
}


void megaduck_state::megaduck_palette(palette_device &palette) const
{
	for (int i = 0; i < 4; i++)
		palette.set_pen_color(i, palette_megaduck[i]);
}


void gb_state::gameboy(machine_config &config)
{
	// basic machine hardware
	LR35902(config, m_maincpu, MASTER_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &gb_state::gameboy_map);
	m_maincpu->timer_cb().set(FUNC(gb_state::gb_timer_callback));
	m_maincpu->set_halt_bug(true);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_raw(MASTER_CLOCK, 456, 0, 20 * 8, 154, 0, 18 * 8);
	screen.set_screen_update(m_ppu, FUNC(dmg_ppu_device::screen_update));
	screen.set_palette(m_palette);

	GFXDECODE(config, "gfxdecode", m_palette, gfxdecode_device::empty);
	PALETTE(config, m_palette, FUNC(gb_state::gb_palette), 4);

	DMG_PPU(config, m_ppu, m_maincpu);

	// sound hardware
	SPEAKER(config, "speaker", 2).front();

	DMG_APU(config, m_apu, MASTER_CLOCK);
	m_apu->add_route(0, "speaker", 0.50, 0);
	m_apu->add_route(1, "speaker", 0.50, 1);

	// cartslot
	GB_CART_SLOT(config, m_cartslot, gameboy_cartridges, nullptr);
	m_cartslot->set_space(m_maincpu, AS_PROGRAM);

	SOFTWARE_LIST(config, "cart_list").set_original("gameboy");
	SOFTWARE_LIST(config, "gbc_list").set_compatible("gbcolor");
}

void sgb_state::supergb(machine_config &config)
{
	// basic machine hardware
	LR35902(config, m_maincpu, 4'295'454); // derived from SNES xtal
	m_maincpu->set_addrmap(AS_PROGRAM, &sgb_state::sgb_map);
	m_maincpu->timer_cb().set(FUNC(sgb_state::gb_timer_callback));
	m_maincpu->set_halt_bug(true);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_physical_aspect(4, 3); // runs on a TV, not an LCD
	screen.set_refresh_hz(SGB_FRAMES_PER_SECOND);
	screen.set_vblank_time(0);
	screen.set_screen_update(m_ppu, FUNC(dmg_ppu_device::screen_update));
	screen.set_palette(m_palette);
	screen.set_size(32*8, 28*8);
	screen.set_visarea(0*8, 32*8-1, 0*8, 28*8-1);

	GFXDECODE(config, "gfxdecode", m_palette, gfxdecode_device::empty);
	PALETTE(config, m_palette, palette_device::BGR_555);

	SGB_PPU(config, m_ppu, m_maincpu);

	// sound hardware
	SPEAKER(config, "speaker", 2).front();

	DMG_APU(config, m_apu, 4'295'454);
	m_apu->add_route(0, "speaker", 0.50, 0);
	m_apu->add_route(1, "speaker", 0.50, 1);

	// cartslot
	GB_CART_SLOT(config, m_cartslot, gameboy_cartridges, nullptr);
	m_cartslot->set_space(m_maincpu, AS_PROGRAM);

	SOFTWARE_LIST(config, "cart_list").set_original("gameboy");
	SOFTWARE_LIST(config, "gbc_list").set_compatible("gbcolor");
}

void sgb_state::supergb2(machine_config &config)
{
	gameboy(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &sgb_state::sgb_map);

	// video hardware
	screen_device &screen(*subdevice<screen_device>("screen"));
	screen.set_physical_aspect(4, 3); // runs on a TV, not an LCD
	screen.set_size(32*8, 28*8);
	screen.set_visarea(0*8, 32*8-1, 0*8, 28*8-1);

	m_palette->set_entries(32'768);
	m_palette->set_init(FUNC(sgb_state::sgb_palette));

	SGB_PPU(config.replace(), m_ppu, m_maincpu);
}

void gb_state::gbpocket(machine_config &config)
{
	gameboy(config);

	// video hardware
	m_palette->set_init(FUNC(gb_state::gbp_palette));

	MGB_PPU(config.replace(), m_ppu, m_maincpu);
}

void gbc_state::gbcolor(machine_config &config)
{
	// basic machine hardware
	LR35902(config, m_maincpu, GBC_CLOCK / 2); // FIXME: make the CPU device divide rather than multiply the clock frequency
	m_maincpu->set_addrmap(AS_PROGRAM, &gbc_state::gbc_map);
	m_maincpu->timer_cb().set(FUNC(gbc_state::gb_timer_callback));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_raw(GBC_CLOCK / 2, 456, 0, 20 * 8, 154, 0, 18 * 8);
	screen.set_screen_update(m_ppu, FUNC(dmg_ppu_device::screen_update));
	screen.set_palette(m_palette);

	GFXDECODE(config, "gfxdecode", m_palette, gfxdecode_device::empty);
	PALETTE(config, m_palette, palette_device::BGR_555);

	CGB_PPU(config, m_ppu, m_maincpu);

	// sound hardware
	SPEAKER(config, "speaker", 2).front();

	CGB04_APU(config, m_apu, GBC_CLOCK / 2);
	m_apu->add_route(0, "speaker", 0.50, 0);
	m_apu->add_route(1, "speaker", 0.50, 1);

	// cartslot
	GB_CART_SLOT(config, m_cartslot, gameboy_cartridges, nullptr);
	m_cartslot->set_space(m_maincpu, AS_PROGRAM);

	SOFTWARE_LIST(config, "cart_list").set_original("gbcolor");
	SOFTWARE_LIST(config, "gb_list").set_compatible("gameboy");
}

void megaduck_state::megaduck(machine_config &config)
{
	// basic machine hardware
	LR35902(config, m_maincpu, XTAL(4'194'304));
	m_maincpu->set_addrmap(AS_PROGRAM, &megaduck_state::megaduck_map);
	m_maincpu->timer_cb().set(FUNC(megaduck_state::gb_timer_callback));
	m_maincpu->set_halt_bug(true);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(DMG_FRAMES_PER_SECOND);
	screen.set_vblank_time(0);
	screen.set_screen_update(m_ppu, FUNC(dmg_ppu_device::screen_update));
	screen.set_palette(m_palette);
	screen.set_size(20*8, 18*8);
	screen.set_visarea(0*8, 20*8-1, 0*8, 18*8-1);

	GFXDECODE(config, "gfxdecode", m_palette, gfxdecode_device::empty);
	PALETTE(config, m_palette, FUNC(megaduck_state::megaduck_palette), 4);

	DMG_PPU(config, m_ppu, m_maincpu);

	// sound hardware
	SPEAKER(config, "speaker", 2).front();
	DMG_APU(config, m_apu, XTAL(4'194'304));
	m_apu->add_route(0, "speaker", 0.50, 0);
	m_apu->add_route(1, "speaker", 0.50, 1);

	// cartslot
	MEGADUCK_CART_SLOT(config, m_cartslot, megaduck_cartridges, nullptr);
	m_cartslot->set_space(m_maincpu, AS_PROGRAM);

	SOFTWARE_LIST(config, "cart_list").set_original("megaduck");
}

/***************************************************************************

  Game driver(s)

***************************************************************************/

ROM_START(gameboy)
	ROM_REGION(0x0100, "maincpu", 0)
	ROM_SYSTEM_BIOS(0, "vx", "DMG vX")
	ROMX_LOAD("dmg_boot.bin", 0x0000, 0x0100, CRC(59c8598e) SHA1(4ed31ec6b0b175bb109c0eb5fd3d193da823339f), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v0", "DMG v0")
	ROMX_LOAD("dmg_v0.rom", 0x0000, 0x0100, CRC(c2f5cc97) SHA1(8bd501e31921e9601788316dbd3ce9833a97bcbc), ROM_BIOS(1))
ROM_END

ROM_START(supergb)
	ROM_REGION(0x0100, "maincpu", 0)
	ROM_LOAD("sgb_boot.bin", 0x0000, 0x0100, CRC(ec8a83b9) SHA1(aa2f50a77dfb4823da96ba99309085a3c6278515))
ROM_END

ROM_START(supergb2 )
	ROM_REGION(0x0100, "maincpu", 0)
	ROM_LOAD("sgb2_boot.bin", 0x0000, 0x0100, CRC(53d0dd63) SHA1(93407ea10d2f30ab96a314d8eca44fe160aea734))
ROM_END

ROM_START(gbpocket )
	ROM_REGION(0x0100, "maincpu", 0)
	ROM_LOAD("mgb_boot.bin", 0x0000, 0x0100, CRC(e6920754) SHA1(4e68f9da03c310e84c523654b9026e51f26ce7f0))
ROM_END

ROM_START(gbcolor)
	ROM_REGION(0x800, "maincpu", 0)
	ROM_LOAD("gbc_boot.1", 0x0000, 0x0100, CRC(779ea374) SHA1(e4b40c9fd593a97a1618cfb2696f290cf9596a62)) // Bootstrap code part 1
	ROM_LOAD("gbc_boot.2", 0x0100, 0x0700, CRC(f741807d) SHA1(f943b1e0b640cf1d371e1d8f0ada69af03ebb396)) // Bootstrap code part 2
ROM_END

ROM_START(megaduck)
ROM_END

ROM_START(gamefgtr)
	ROM_REGION(0x0100, "maincpu", 0)
	ROM_LOAD("gamefgtr.bin", 0x0000, 0x0100, CRC(908ba8de) SHA1(a4a36f71bf1b3b587df620d48ae940af93a982a5))
ROM_END

/*

Notes from Sean:

The bottom of the ROM PCB says
EW-012 4M Mask ROM
German Version
BB35-E012-0A12
REV.0

The bottom of the 7-pin glob-on-a-board says
EW-012
VOICE CHIP PCB
HT-81300 /
HT-81400
BB35-E012-0A09
REV.1

I couldn't find a data sheet, but I did see

"81300 is described as PCM speech synthesizer 5.6s" and
"81400 as PCM speech synthesizer 8.4s" (which is 50% larger)

I assume it that means it has (undumped) internal ROM.

*/

ROM_START(mduckspa)
	ROM_REGION(0x80000, "maincpu", ROMREGION_ERASEFF)
	ROM_LOAD("megaducksp.bin", 0x0000, 0x80000,  CRC(debd33fd) SHA1(fbf86dffa82f6e469da46623541f6f58f6c8a0d8) )

	ROM_REGION(0x10000, "81x00", ROMREGION_ERASEFF) // unknown size / capacity
	ROM_LOAD("81x00.bin", 0x0000, 0x10000, NO_DUMP )
ROM_END

} // anonymous namespace


//   YEAR  NAME      PARENT   COMPAT   MACHINE   INPUT     STATE           INIT        COMPANY     FULLNAME
CONS(1990, gameboy,  0,       0,       gameboy,  gameboy,  gb_state,       empty_init, "Nintendo", "Game Boy",         MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE)
CONS(1994, supergb,  gameboy, 0,       supergb,  gameboy,  sgb_state,      empty_init, "Nintendo", "Super Game Boy",   MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE)
CONS(1998, supergb2, gameboy, 0,       supergb2, gameboy,  sgb_state,      empty_init, "Nintendo", "Super Game Boy 2", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE)
CONS(1996, gbpocket, gameboy, 0,       gbpocket, gameboy,  gb_state,       empty_init, "Nintendo", "Game Boy Pocket",  MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE)
CONS(1998, gbcolor,  0,       0,       gbcolor,  gameboy,  gbc_state,      empty_init, "Nintendo", "Game Boy Color",   MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE)

// Sound is not 100% yet, it generates some sounds which could be OK. Since we're lacking a real system there's no way to verify.
CONS(1993, megaduck, 0,       0,       megaduck, megaduck, megaduck_state, empty_init, "Welback Holdings (Timlex International) / Creatronic / Videojet / Cougar USA", "Mega Duck / Cougar Boy", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )

CONS(199?, mduckspa, 0,       0,       megaduck, megaduck, megaduck_state, empty_init, "Cefa Toys", "Super Quique / Mega Duck (Spain)", MACHINE_NOT_WORKING ) // versions for other regions exist too


// http://blog.gg8.se/wordpress/2012/11/11/gameboy-clone-game-fighter-teardown/
CONS(1993, gamefgtr, gameboy, 0,       gameboy,  gameboy,  gb_state,       empty_init, "bootleg", "Game Fighter (bootleg)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE)



gba.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont,Ryan Holtz
/***************************************************************************

  gba.cpp

  Driver file to handle emulation of the Nintendo Game Boy Advance.

  By R. Belmont & Ryan Holtz

***************************************************************************/

#include "emu.h"
#include "gba.h"

#include "bus/gba/rom.h"
#include "cpu/arm7/arm7.h"
#include "cpu/arm7/arm7core.h"
#include "sound/gb.h"

#include "softlist_dev.h"
#include "speaker.h"

#include <cstdarg>


/* Sound Registers */
#define SOUNDCNT_L  HWLO(0x080)  /* 0x4000080  2  R/W   Control Stereo/Volume/Enable */
#define SOUNDCNT_H  HWHI(0x080)  /* 0x4000082  2  R/W   Control Mixing/DMA Control */
#define SOUNDCNT_X  HWLO(0x084)  /* 0x4000084  2  R/W   Control Sound on/off */
								 /* 0x4000086  2  -     Unused */
#define SOUNDBIAS   HWLO(0x088)  /* 0x4000088  2  BIOS  Sound PWM Control */
								 /* 0x400008A  2  -     Unused */

/* DMA Registers (4 Transfer Channels) */
#define DMASAD(c)   WORD(0x0B0 + c * 0xC) /* 0x40000B0  4  W     DMA 0 Source Address */
#define DMADAD(c)   WORD(0x0B4 + c * 0xC) /* 0x40000B4  4  W     DMA 0 Destination Address */
#define DMACNT_L(c) HWLO(0x0B8 + c * 0xC) /* 0x40000B8  2  W     DMA 0 Word Count */
#define DMACNT_H(c) HWHI(0x0B8 + c * 0xC) /* 0x40000BA  2  R/W   DMA 0 Control */

/* Serial Communication (1) Registers */
#define SIODATA32   WORD(0x120)  /* 0x4000120  4  R/W   SIO Data (Normal-32bit Mode; shared with below) */
#define SIOMULTI0   HWLO(0x120)  /* 0x4000120  2  R/W   SIO Data 0 (Parent)    (Multi-Player Mode) */
#define SIOMULTI1   HWHI(0x120)  /* 0x4000122  2  R/W   SIO Data 1 (1st Child) (Multi-Player Mode) */
#define SIOMULTI2   HWLO(0x124)  /* 0x4000124  2  R/W   SIO Data 2 (2nd Child) (Multi-Player Mode) */
#define SIOMULTI3   HWHI(0x124)  /* 0x4000126  2  R/W   SIO Data 3 (3rd Child) (Multi-Player Mode) */
#define SIOCNT      HWLO(0x128)  /* 0x4000128  2  R/W   SIO Control Register */
#define SIOMLT_SEND HWHI(0x128)  /* 0x400012A  2  R/W   SIO Data (Local of MultiPlayer; shared below) */
#define SIODATA8    HWHI(0x128)  /* 0x400012A  2  R/W   SIO Data (Normal-8bit and UART Mode) */
								 /* 0x400012C  2  -     Unused */

/* Keypad Input Registers */
#define KEYINPUT    HWLO(0x130)  /* 0x4000130  2  R     Key Status */
#define KEYCNT      HWHI(0x130)  /* 0x4000132  2  R/W   Key Interrupt Control */

/* Serial Communication (2) Registers */
#define RCNT        HWLO(0x134)  /* 0x4000134  2  R/W   SIO Mode Select/General Purpose Data */
#define IR          HWHI(0x134)  /* 0x4000136  2  R/W   Ancient - Infrared Register (Prototypes only) */
								 /* 0x4000138  8  -     Unused */
#define JOYCNT      HWLO(0x140)  /* 0x4000140  2  R/W   SIO JOY Bus Control */
								 /* 0x4000142  2  -     Unused */
#define JOY_RECV    WORD(0x150)  /* 0x4000150  4  R/W   SIO JOY Bus Receive Data */
#define JOY_TRANS   WORD(0x154)  /* 0x4000154  4  R/W   SIO JOY Bus Transmit Data */
#define JOYSTAT     HWLO(0x158)  /* 0x4000158  2  R/?   SIO JOY Bus Receive Status */
								 /* 0x400015A  2  -     Unused */

/* Interrupt, Waitstate, and Power-Down Control Registers */
#define IE          HWLO(0x200)  /* 0x4000200  2  R/W   Interrupt Enable Register */
#define IF          HWHI(0x200)  /* 0x4000202  2  R/W   Interrupt Request Flags / IRQ Acknowledge */
#define WAITCNT     HWLO(0x204)  /* 0x4000204  2  R/W   Game Pak Waitstate Control */
								 /* 0x4000206     -     Unused */
#define IME         HWLO(0x208)  /* 0x4000208  2  R/W   Interrupt Master Enable Register */
								 /* 0x400020A     -     Unused */
								 /* 0x4000300  1  R/W   Undocumented - Post Boot Flag */
								 /* 0x4000301  1  W     Undocumented - Power Down Control */
								 /* 0x4000302     -     Unused */
								 /* 0x4000410  ?  ?     Undocumented - Purpose Unknown / Bug ??? 0FFh */
								 /* 0x4000411     -     Unused */
								 /* 0x4000800  4  R/W   Undocumented - Internal Memory Control (R/W) */
								 /* 0x4000804     -     Unused */
								 /* 0x4xx0800  4  R/W   Mirrors of 4000800h (repeated each 64K) */

#define SOUNDBIAS_SET(val)      HWLO_SET(0x088, val)

#define DMASAD_SET(c, val)      WORD_SET(0x0B0 + (c * 0xC), val)
#define DMADAD_SET(c, val)      WORD_SET(0x0B4 + (c * 0xC), val)
#define DMACNT_L_SET(c, val)    HWLO_SET(0x0B8 + (c * 0xC), val)
#define DMACNT_H_SET(c, val)    HWHI_SET(0x0B8 + (c * 0xC), val)
#define DMACNT_H_RESET(c, val)  HWHI_RESET(0x0B8 + (c * 0xC), val)

#define SIOMULTI0_SET(val)      HWLO_SET(0x120, val)
#define SIOMULTI1_SET(val)      HWHI_SET(0x120, val)
#define SIOMULTI2_SET(val)      HWLO_SET(0x124, val)
#define SIOMULTI3_SET(val)      HWHI_SET(0x124, val)

#define SIOCNT_RESET(val)       HWLO_RESET(0x128, val)

#define KEYCNT_SET(val)         HWHI_SET(0x130, val)

#define RCNT_SET(val)           HWLO_SET(0x134, val)

#define JOYSTAT_SET(val)        HWLO_SET(0x158, val)

#define IF_SET(val)             HWHI_SET(0x200, val)
#define IF_RESET(val)           HWHI_RESET(0x200, val)

#define INT_VBL                 0x0001
#define INT_HBL                 0x0002
#define INT_VCNT                0x0004
#define INT_TM0_OVERFLOW        0x0008
#define INT_TM1_OVERFLOW        0x0010
#define INT_TM2_OVERFLOW        0x0020
#define INT_TM3_OVERFLOW        0x0040
#define INT_SIO                 0x0080
#define INT_DMA0                0x0100
#define INT_DMA1                0x0200
#define INT_DMA2                0x0400
#define INT_DMA3                0x0800
#define INT_KEYPAD              0x1000
#define INT_GAMEPAK             0x2000

#define VERBOSE_LEVEL   (0)

static inline void ATTR_PRINTF(3,4) verboselog(device_t &device, int n_level, const char *s_fmt, ...)
{
	if( VERBOSE_LEVEL >= n_level )
	{
		va_list v;
		char buf[ 32768 ];
		va_start( v, s_fmt );
		vsprintf( buf, s_fmt, v );
		va_end( v );
		device.logerror( "%08x: %s", device.machine().describe_context(), buf );
	}
}

static const XTAL timer_clks[4] = { XTAL(16'777'216), XTAL(16'777'216) / 64, XTAL(16'777'216) / 256, XTAL(16'777'216) / 1024 };


void gba_state::request_irq(uint32_t int_type)
{
	// set flag for later recovery
	IF_SET(int_type);

	// is this specific interrupt enabled?
	int_type &= IE;
	if (int_type != 0)
	{
		// master enable?
		if (IME & 1)
		{
			m_maincpu->set_input_line(arm7_cpu_device::ARM7_IRQ_LINE, ASSERT_LINE);
			m_maincpu->set_input_line(arm7_cpu_device::ARM7_IRQ_LINE, CLEAR_LINE);
		}
	}
}

TIMER_CALLBACK_MEMBER(gba_state::dma_complete)
{
	static const uint32_t ch_int[4] = { INT_DMA0, INT_DMA1, INT_DMA2, INT_DMA3 };

	uintptr_t ch = param;

//  printf("dma complete: ch %d\n", ch);

	m_dma_timer[ch]->adjust(attotime::never);

	int ctrl = DMACNT_H(ch);

	// IRQ
	if (ctrl & 0x4000)
	{
		request_irq(ch_int[ch]);
	}

	// if we're supposed to repeat, don't clear "active" and then the next vbl/hbl will retrigger us
	// always clear active for immediate DMAs though
	if (!((ctrl>>9) & 1) || ((ctrl & 0x3000) == 0))
	{
		DMACNT_H_RESET(ch, 0x8000); // clear "active" bit
	}
	else
	{
		// if repeat, reload the count
		if ((ctrl>>9) & 1)
		{
			m_dma_cnt[ch] = DMACNT_L(ch);

			// if increment & reload mode, reload the destination
			if (((ctrl>>5)&3) == 3)
			{
				m_dma_dst[ch] = DMADAD(ch);
			}
		}
	}
}

void gba_state::dma_exec(int ch)
{
	address_space &space = m_maincpu->space(AS_PROGRAM);
	uint32_t src = m_dma_src[ch];
	uint32_t dst = m_dma_dst[ch];
	uint16_t ctrl = DMACNT_H(ch);
	int srcadd = (ctrl >> 7) & 3;
	int dstadd = (ctrl >> 5) & 3;

	int cnt = m_dma_cnt[ch];
	if (cnt == 0)
	{
		if (ch == 3)
			cnt = 0x10000;
		else
			cnt = 0x4000;
	}

	// override special parameters
	if ((ctrl & 0x3000) == 0x3000)      // special xfer mode
	{
		switch (ch)
		{
			case 1:         // Ch 1&2 are for audio DMA
			case 2:
				dstadd = 2; // don't increment destination
				cnt = 4;    // always transfer 4 32-bit words
				ctrl |= 0x400;  // always 32-bit
				break;

			case 3:
				printf("Unsupported DMA 3 special mode\n");
				break;
		}
	}
	else
	{
//      if (dst >= 0x6000000 && dst <= 0x6017fff)
//      printf("DMA exec: ch %d from %08x to %08x, mode %04x, count %04x (%s)\n", (int)ch, src, dst, ctrl, cnt, ((ctrl>>10) & 1) ? "32" : "16");
	}

	for (int i = 0; i < cnt; i++)
	{
		if ((ctrl>>10) & 1)
		{
			src &= 0xfffffffe;
			dst &= 0xfffffffe;

			// 32-bit
			space.write_dword(dst, space.read_dword(src));
			switch (dstadd)
			{
				case 0: // increment
					dst += 4;
					break;
				case 1: // decrement
					dst -= 4;
					break;
				case 2: // don't move
					break;
				case 3: // increment and reload
					dst += 4;
					break;
			}
			switch (srcadd)
			{
				case 0: // increment
					src += 4;
					break;
				case 1: // decrement
					src -= 4;
					break;
				case 2: // don't move
					break;
				case 3: // not used ("Metal Max 2 Kai" expects no increment/decrement)
					break;
			}
		}
		else
		{
			src &= 0xfffffffe;
			dst &= 0xfffffffe;

			// 16-bit
			space.write_word(dst, space.read_word(src));
			switch (dstadd)
			{
				case 0: // increment
					dst += 2;
					break;
				case 1: // decrement
					dst -= 2;
					break;
				case 2: // don't move
					break;
				case 3: // increment and reload
					dst += 2;
					break;
			}
			switch (srcadd)
			{
				case 0: // increment
					src += 2;
					break;
				case 1: // decrement
					src -= 2;
					break;
				case 2: // don't move
					break;
				case 3: // not used (see note in 32-bit version above)
					break;
			}
		}
	}

	m_dma_src[ch] = src;
	m_dma_dst[ch] = dst;

//  printf("settng DMA timer %d for %d cycs (tmr %x)\n", ch, cnt, (uint32_t)m_dma_timer[ch]);
//  m_dma_timer[ch]->adjust(ATTOTIME_IN_CYCLES(0, cnt), ch);
	dma_complete(ch);
}

void gba_state::audio_tick(int ref)
{
	if (BIT(~SOUNDCNT_X, 7))
		return;

	fifo_t &fifo = m_fifo[ref ? 1 : 0];
	if ((fifo.size > 0) && (fifo.remains == 0))
	{
		fifo.sample = fifo.word[fifo.ptr];
		fifo.ptr = (fifo.ptr + 1) & 7;
		fifo.remains = 4;
		fifo.size--;
	}

	if (!ref)
	{
		if (BIT(SOUNDCNT_H, 9))
		{
			m_ldac[0]->write(uint8_t(fifo.sample));
		}
		if (BIT(SOUNDCNT_H, 8))
		{
			m_rdac[0]->write(uint8_t(fifo.sample));
		}
		// fifo half empty?
		if (fifo.size <= 4)
		{
			// is a DMA set up to feed us?
			if ((DMADAD(1) == 0x40000a0) && ((DMACNT_H(1) & 0x3000) == 0x3000))
			{
				// channel 1 it is
				dma_exec(1);
			}
			if ((DMADAD(2) == 0x40000a0) && ((DMACNT_H(2) & 0x3000) == 0x3000))
			{
				// channel 2 it is
				dma_exec(2);
			}
		}
	}
	else
	{
		if (BIT(SOUNDCNT_H, 13))
		{
			m_ldac[1]->write(uint8_t(fifo.sample));
		}
		if (BIT(SOUNDCNT_H, 12))
		{
			m_rdac[1]->write(uint8_t(fifo.sample));
		}
		// fifo half empty?
		if (fifo.size <= 4)
		{
			// is a DMA set up to feed us?
			if ((DMADAD(1) == 0x40000a4) && ((DMACNT_H(1) & 0x3000) == 0x3000))
			{
				// channel 1 it is
				dma_exec(1);
			}
			if ((DMADAD(2) == 0x40000a4) && ((DMACNT_H(2) & 0x3000) == 0x3000))
			{
				// channel 2 it is
				dma_exec(2);
			}
		}
	}
	if (fifo.remains > 0)
	{
		fifo.sample >>= 8;
		fifo.remains--;
	}
}

TIMER_CALLBACK_MEMBER(gba_state::timer_expire)
{
	static const uint32_t tmr_ints[4] = { INT_TM0_OVERFLOW, INT_TM1_OVERFLOW, INT_TM2_OVERFLOW, INT_TM3_OVERFLOW };
	uintptr_t tmr = (uintptr_t) param;

	// "The reload value is copied into the counter only upon following two situations: Automatically upon timer overflows,"
	// "or when the timer start bit becomes changed from 0 to 1."
	if (m_timer_recalc[tmr] != 0)
	{
		double rate, clocksel, final;
		attotime time;
		m_timer_recalc[tmr] = 0;
		m_timer_regs[tmr] = (m_timer_regs[tmr] & 0xFFFF0000) | (m_timer_reload[tmr] & 0x0000FFFF);
		rate = 0x10000 - (m_timer_regs[tmr] & 0xffff);
		clocksel = timer_clks[(m_timer_regs[tmr] >> 16) & 3].dvalue();
		final = clocksel / rate;
		m_timer_hz[tmr] = final;
		time = attotime::from_hz(final);
		m_tmr_timer[tmr]->adjust(time, tmr, time);
	}

	// check if timers 0 or 1 are feeding directsound
	if (tmr == 0)
	{
		if (BIT(~SOUNDCNT_H, 10))
			audio_tick(0);

		if (BIT(~SOUNDCNT_H, 14))
			audio_tick(1);
	}

	if (tmr == 1)
	{
		if (BIT(SOUNDCNT_H, 10))
			audio_tick(0);

		if (BIT(SOUNDCNT_H, 14))
			audio_tick(1);
	}

	// Handle count-up timing
	switch (tmr)
	{
	case 0:
		if (m_timer_regs[1] & 0x40000)
		{
			m_timer_regs[1] = (( ( m_timer_regs[1] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[1] & 0xffff0000);
			if( ( m_timer_regs[1] & 0x0000ffff ) == 0 )
			{
				m_timer_regs[1] |= m_timer_reload[1];
				if( ( m_timer_regs[1] & 0x400000 ) && ( IME != 0 ) )
				{
					request_irq(tmr_ints[1]);
				}
				if( ( m_timer_regs[2] & 0x40000 ) )
				{
					m_timer_regs[2] = (( ( m_timer_regs[2] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[2] & 0xffff0000);
					if( ( m_timer_regs[2] & 0x0000ffff ) == 0 )
					{
						m_timer_regs[2] |= m_timer_reload[2];
						if( ( m_timer_regs[2] & 0x400000 ) && ( IME != 0 ) )
						{
							request_irq(tmr_ints[2]);
						}
						if( ( m_timer_regs[3] & 0x40000 ) )
						{
							m_timer_regs[3] = (( ( m_timer_regs[3] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[3] & 0xffff0000);
							if( ( m_timer_regs[3] & 0x0000ffff ) == 0 )
							{
								m_timer_regs[3] |= m_timer_reload[3];
								if( ( m_timer_regs[3] & 0x400000 ) && ( IME != 0 ) )
								{
									request_irq(tmr_ints[3]);
								}
							}
						}
					}
				}
			}
		}
		break;
	case 1:
		if (m_timer_regs[2] & 0x40000)
		{
			m_timer_regs[2] = (( ( m_timer_regs[2] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[2] & 0xffff0000);
			if( ( m_timer_regs[2] & 0x0000ffff ) == 0 )
			{
				m_timer_regs[2] |= m_timer_reload[2];
				if( ( m_timer_regs[2] & 0x400000 ) && ( IME != 0 ) )
				{
					request_irq(tmr_ints[2]);
				}
				if( ( m_timer_regs[3] & 0x40000 ) )
				{
					m_timer_regs[3] = (( ( m_timer_regs[3] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[3] & 0xffff0000);
					if( ( m_timer_regs[3] & 0x0000ffff ) == 0 )
					{
						m_timer_regs[3] |= m_timer_reload[3];
						if( ( m_timer_regs[3] & 0x400000 ) && ( IME != 0 ) )
						{
							request_irq(tmr_ints[3]);
						}
					}
				}
			}
		}
		break;
	case 2:
		if (m_timer_regs[3] & 0x40000)
		{
			m_timer_regs[3] = (( ( m_timer_regs[3] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[3] & 0xffff0000);
			if( ( m_timer_regs[3] & 0x0000ffff ) == 0 )
			{
				m_timer_regs[3] |= m_timer_reload[3];
				if( ( m_timer_regs[3] & 0x400000 ) && ( IME != 0 ) )
				{
					request_irq(tmr_ints[3]);
				}
			}
		}
		break;
	}

	// are we supposed to IRQ?
	if ((m_timer_regs[tmr] & 0x400000) && (IME != 0))
	{
		request_irq(tmr_ints[tmr]);
	}
}

TIMER_CALLBACK_MEMBER(gba_state::handle_irq)
{
	request_irq(IF);

	m_irq_timer->adjust(attotime::never);
}

static const char *const reg_names[] = {
	/* Sound Registers */
	"SOUND1CNT_L", "SOUND1CNT_H", "SOUND1CNT_X", "Unused",
	"SOUND2CNT_L", "Unused",      "SOUND2CNT_H", "Unused",
	"SOUND3CNT_L", "SOUND3CNT_H", "SOUND3CNT_X", "Unused",
	"SOUND4CNT_L", "Unused",      "SOUND4CNT_H", "Unused",
	"SOUNDCNT_L",  "SOUNDCNT_H",  "SOUNDCNT_X",  "Unused",
	"SOUNDBIAS",   "Unused",      "WAVE_RAM",    "WAVE_RAM",
	"WAVE_RAM",    "WAVE_RAM",    "WAVE_RAM",    "WAVE_RAM",
	"WAVE_RAM",    "WAVE_RAM",    "WAVE_RAM",    "WAVE_RAM",
	"WAVE_RAM",    "WAVE_RAM",    "WAVE_RAM",    "WAVE_RAM",
	"WAVE_RAM",    "WAVE_RAM",    "FIFO_A_L",    "FIFO_A_H",
	"FIFO_B_L",    "FIFO_B_H"
};

uint32_t gba_state::gba_io_r(offs_t offset, uint32_t mem_mask)
{
	uint32_t retval = 0;

	switch( offset + 0x60/4 )
	{
		case 0x0060/4:
			retval = m_gbsound->sound_r(0) | m_gbsound->sound_r(1)<<16 | m_gbsound->sound_r(2)<<24;
			break;
		case 0x0064/4:
			retval = m_gbsound->sound_r(3) | m_gbsound->sound_r(4)<<8;
			break;
		case 0x0068/4:
			retval = m_gbsound->sound_r(6) | m_gbsound->sound_r(7)<<8;
			break;
		case 0x006c/4:
			retval = m_gbsound->sound_r(8) | m_gbsound->sound_r(9)<<8;
			break;
		case 0x0070/4:
			retval = m_gbsound->sound_r(0xa) | m_gbsound->sound_r(0xb)<<16 | m_gbsound->sound_r(0xc)<<24;
			break;
		case 0x0074/4:
			retval = m_gbsound->sound_r(0xd) | m_gbsound->sound_r(0xe)<<8;
			break;
		case 0x0078/4:
			retval = m_gbsound->sound_r(0x10) | m_gbsound->sound_r(0x11)<<8;
			break;
		case 0x007c/4:
			retval = m_gbsound->sound_r(0x12) | m_gbsound->sound_r(0x13)<<8;
			break;
		case 0x0080/4:
			retval = m_gbsound->sound_r(0x14) | m_gbsound->sound_r(0x15)<<8;
			if( ACCESSING_BITS_16_31 )
			{
				retval |= SOUNDCNT_H << 16;
			}
			break;
		case 0x0084/4:
			retval = m_gbsound->sound_r(0x16);
			break;
		case 0x0090/4:
			retval = m_gbsound->wave_r(0) | m_gbsound->wave_r(1)<<8 | m_gbsound->wave_r(2)<<16 | m_gbsound->wave_r(3)<<24;
			break;
		case 0x0094/4:
			retval = m_gbsound->wave_r(4) | m_gbsound->wave_r(5)<<8 | m_gbsound->wave_r(6)<<16 | m_gbsound->wave_r(7)<<24;
			break;
		case 0x0098/4:
			retval = m_gbsound->wave_r(8) | m_gbsound->wave_r(9)<<8 | m_gbsound->wave_r(10)<<16 | m_gbsound->wave_r(11)<<24;
			break;
		case 0x009c/4:
			retval = m_gbsound->wave_r(12) | m_gbsound->wave_r(13)<<8 | m_gbsound->wave_r(14)<<16 | m_gbsound->wave_r(15)<<24;
			break;
		case 0x00a0/4:
		case 0x00a4/4:
			retval = 0; // (does this actually do anything on real h/w?)
			break;
		case 0x00b0/4:
		case 0x00b4/4:
			// read only
			break;
		case 0x00b8/4:
			if (ACCESSING_BITS_0_15)
			{
				// read only
			}
			if (ACCESSING_BITS_16_31)
			{
				retval |= DMACNT_H(0) << 16;
			}
			break;
		case 0x00bc/4:
		case 0x00c0/4:
			// read only
			break;
		case 0x00c4/4:
			if (ACCESSING_BITS_0_15)
			{
				// read only
			}
			if (ACCESSING_BITS_16_31)
			{
				retval |= DMACNT_H(1) << 16;
			}
			break;
		case 0x00c8/4:
		case 0x00cc/4:
			// read only
			break;
		case 0x00d0/4:
			if (ACCESSING_BITS_0_15)
			{
				// read only
			}
			if (ACCESSING_BITS_16_31)
			{
				retval |= DMACNT_H(2) << 16;
			}
			break;
		case 0x00d4/4:
		case 0x00d8/4:
			// read only
			break;
		case 0x00dc/4:
				// no idea why here, but it matches VBA better
				// note: this suspicious piece of code crashes "Buffy The Vampire Slayer" (08008DB4) and "The Ant Bully", so disable it for now
			if (ACCESSING_BITS_0_15)
			{
				// read only
			}
			if (ACCESSING_BITS_16_31)
			{
				retval |= DMACNT_H(3) << 16;
			}
			break;
		case 0x0100/4:
		case 0x0104/4:
		case 0x0108/4:
		case 0x010c/4:
			{
				uint32_t elapsed;
				double time, ticks;
				int timer = offset + 0x60/4 - 0x100/4;

//              printf("Read timer reg %x (PC=%x)\n", timer, m_maincpu->pc());

				// update times for
				if (m_timer_regs[timer] & 0x800000)
				{
					if (m_timer_regs[timer] & 0x00040000)
					{
						elapsed = m_timer_regs[timer] & 0xffff;
					}
					else
					{
						time = m_tmr_timer[timer]->elapsed().as_double();

						ticks = (double)(0x10000 - (m_timer_regs[timer] & 0xffff));

	//                  printf("time %f ticks %f 1/hz %f\n", time, ticks, 1.0 / m_timer_hz[timer]);

						time *= ticks;
						time /= (1.0 / m_timer_hz[timer]);

						elapsed = (uint32_t)time;
					}

//                  printf("elapsed = %x\n", elapsed);
				}
				else
				{
//                  printf("Reading inactive timer!\n");
					elapsed = 0;
				}

				retval = (m_timer_regs[timer] & 0xffff0000) | (elapsed & 0xffff);
			}
			break;
		case 0x0130/4:
			if( ACCESSING_BITS_0_15 )   // KEYINPUT
			{
				retval = m_io_inputs->read();
			}
			else if( ACCESSING_BITS_16_31 )
			{
				retval |= KEYCNT << 16;
			}
			break;
		case 0x0200/4:
			retval = IE | (IF << 16);
			break;
		case 0x0204/4:
			// TODO: bit 15 is CGB mode (from cart IN35, read only)
			// not being writeable fixes hang in dkkswing later stages
			retval = WAITCNT & 0x5fff;
			break;
		default:
			if( ACCESSING_BITS_0_15 )
			{
				retval |= m_regs[offset] & 0x0000ffff;
			}
			if( ACCESSING_BITS_16_31 )
			{
				retval |= m_regs[offset] & 0xffff0000;
			}
			break;
	}

//  assert_always(offset < std::size(reg_names) / 2, "Not enough register names in gba_state");

	if (ACCESSING_BITS_0_15)
	{
		verboselog(*this, 2, "GBA I/O Read: %s = %04x\n", reg_names[offset * 2], retval & 0x0000ffff);
	}
	if (ACCESSING_BITS_16_31)
	{
		verboselog(*this, 2, "GBA I/O Read: %s = %04x\n", reg_names[offset * 2 + 1], (retval & 0xffff0000) >> 16);
	}

	return retval;
}

void gba_state::gba_io_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	uint8_t soundcnt_x = SOUNDCNT_X;
	uint16_t siocnt = SIOCNT;
	uint16_t dmachcnt[4] = { DMACNT_H(0), DMACNT_H(1), DMACNT_H(2), DMACNT_H(3) };
	static const float dac_gain_table[2] = { 0.5f, 1.0f };
	static const float psg_gain_table[4] = { 0.25f, 0.5f, 1.0f, 1.0f/* prohibited? */ };

	COMBINE_DATA(&m_regs[offset]);

//  assert_always(offset < std::size(reg_names) / 2, "Not enough register names in gba_state");

	if (ACCESSING_BITS_0_15)
	{
		verboselog(*this, 2, "GBA I/O Write: %s = %04x\n", reg_names[offset * 2], data & 0x0000ffff);
	}
	if (ACCESSING_BITS_16_31)
	{
		verboselog(*this, 2, "GBA I/O Write: %s = %04x\n", reg_names[offset * 2 + 1], (data & 0xffff0000) >> 16);
	}

	switch( offset + 0x60/4 )
	{
		case 0x0060/4:
			if( ACCESSING_BITS_0_7 )   // SOUNDCNTL
			{
				m_gbsound->sound_w(0, data);
			}
			if( ACCESSING_BITS_16_23 )
			{
				m_gbsound->sound_w(1, data>>16);  // SOUND1CNT_H
			}
			if( ACCESSING_BITS_24_31 )
			{
				m_gbsound->sound_w(2, data>>24);
			}
			break;
		case 0x0064/4:
			if( ACCESSING_BITS_0_7 )   // SOUNDCNTL
			{
				m_gbsound->sound_w(3, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->sound_w(4, data>>8);   // SOUND1CNT_H
			}
			break;
		case 0x0068/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->sound_w(6, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->sound_w(7, data>>8);
			}
			break;
		case 0x006c/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->sound_w(8, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->sound_w(9, data>>8);
			}
			break;
		case 0x0070/4:  //SND3CNTL and H
			if( ACCESSING_BITS_0_7 )   // SOUNDCNTL
			{
				m_gbsound->sound_w(0xa, data);
			}
			if( ACCESSING_BITS_16_23 )
			{
				m_gbsound->sound_w(0xb, data>>16);    // SOUND1CNT_H
			}
			if( ACCESSING_BITS_24_31 )
			{
				m_gbsound->sound_w(0xc, data>>24);
			}
			break;
		case 0x0074/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->sound_w(0xd, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->sound_w(0xe, data>>8);
			}
			break;
		case 0x0078/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->sound_w(0x10, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->sound_w(0x11, data>>8);
			}
			break;
		case 0x007c/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->sound_w(0x12, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->sound_w(0x13, data>>8);
			}
			break;
		case 0x0080/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->sound_w(0x14, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->sound_w(0x15, data>>8);
			}

			if (ACCESSING_BITS_16_31)
			{
				// master volume
				if (((data >> 16) & 3) == 3)
					logerror("%s: Using prohibited PSG Master volume value\n", machine().describe_context());

				m_gbsound->set_output_gain(ALL_OUTPUTS, psg_gain_table[(data >> 16) & 3]);
				m_ldac[0]->set_output_gain(ALL_OUTPUTS, dac_gain_table[BIT(data, 18)]);
				m_rdac[0]->set_output_gain(ALL_OUTPUTS, dac_gain_table[BIT(data, 18)]);
				m_ldac[1]->set_output_gain(ALL_OUTPUTS, dac_gain_table[BIT(data, 19)]);
				m_rdac[1]->set_output_gain(ALL_OUTPUTS, dac_gain_table[BIT(data, 19)]);
				// DAC A reset?
				if (BIT(data, 27))
				{
					m_fifo[0].ptr = 0;
					m_fifo[0].in = 0;
					m_fifo[0].size = 0;
					m_fifo[0].remains = 0;
					m_fifo[0].sample = 0;
					m_ldac[0]->write(0);
					m_rdac[0]->write(0);
				}

				// DAC B reset?
				if (BIT(data, 31))
				{
					m_fifo[1].ptr = 0;
					m_fifo[1].in = 0;
					m_fifo[1].size = 0;
					m_fifo[1].remains = 0;
					m_fifo[1].sample = 0;
					m_ldac[1]->write(0);
					m_rdac[1]->write(0);
				}
			}
			break;
		case 0x0084/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->sound_w(0x16, data);
				if (BIT(data, 7) && BIT(~soundcnt_x, 7))
				{
					m_fifo[0].ptr = m_fifo[0].in = 0;
					m_fifo[0].size = m_fifo[0].remains = 0;
					m_fifo[0].sample = 0;
					m_fifo[1].ptr = m_fifo[1].in = 0;
					m_fifo[1].size = m_fifo[1].remains = 0;
					m_fifo[1].sample = 0;
					m_ldac[0]->write(0);
					m_rdac[0]->write(0);
					m_ldac[1]->write(0);
					m_rdac[1]->write(0);
				}
			}
			break;
		case 0x0090/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->wave_w(0, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->wave_w(1, data>>8);
			}
			if( ACCESSING_BITS_16_23 )
			{
				m_gbsound->wave_w(2, data>>16);
			}
			if( ACCESSING_BITS_24_31 )
			{
				m_gbsound->wave_w(3, data>>24);
			}
			break;
		case 0x0094/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->wave_w(4, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->wave_w(5, data>>8);
			}
			if( ACCESSING_BITS_16_23 )
			{
				m_gbsound->wave_w(6, data>>16);
			}
			if( ACCESSING_BITS_24_31 )
			{
				m_gbsound->wave_w(7, data>>24);
			}
			break;
		case 0x0098/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->wave_w(8, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->wave_w(9, data>>8);
			}
			if( ACCESSING_BITS_16_23 )
			{
				m_gbsound->wave_w(0xa, data>>16);
			}
			if( ACCESSING_BITS_24_31 )
			{
				m_gbsound->wave_w(0xb, data>>24);
			}
			break;
		case 0x009c/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->wave_w(0xc, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->wave_w(0xd, data>>8);
			}
			if( ACCESSING_BITS_16_23 )
			{
				m_gbsound->wave_w(0xe, data>>16);
			}
			if( ACCESSING_BITS_24_31 )
			{
				m_gbsound->wave_w(0xf, data>>24);
			}
			break;
		case 0x00a0/4:
		case 0x00a4/4:
		{
			fifo_t &fifo = m_fifo[offset & 1];
			if (fifo.size >= 8)
			{
				logerror("%s: Sound FIFO %01x write overflow %04x & %04x\n", machine().describe_context(), offset & 1, data, mem_mask);
				return;
			}
			COMBINE_DATA(&fifo.word[fifo.in]);
			fifo.in = (fifo.in + 1) & 7;
			fifo.size++;
			break;
		}
		case 0x00b8/4:
		case 0x00c4/4:
		case 0x00d0/4:
		case 0x00dc/4:
			if( ACCESSING_BITS_16_31 )
			{
				int ch = (offset + 0x60/4 - 0xb0/4) / 3;

				int ctrl = data>>16;

				// Note: Metroid Fusion fails if we enforce the "rising edge" requirement... (who wrote this note?)

				// Note: Caesar's Palace Advance fails if we DO NOT enforce the "rising edge" requirement
				// (value @ 0x3003F9C is accidentally incremented because DMA completion interrupt is accidentally triggered @ 08002F2A)

				// retrigger/restart on a rising edge.
				// also reload internal regs
				if ((ctrl & 0x8000) && !(dmachcnt[ch] & 0x8000))
				{
					m_dma_src[ch] = DMASAD(ch);
					m_dma_dst[ch] = DMADAD(ch);
					m_dma_cnt[ch] = DMACNT_L(ch);

					// immediate start
					if ((ctrl & 0x3000) == 0)
					{
						dma_exec(ch);
						return;
					}
				}
			}
			break;
		case 0x0100/4:
		case 0x0104/4:
		case 0x0108/4:
		case 0x010c/4:
			{
				double rate, clocksel;
				uint32_t old_timer_regs;

				int timer = offset + 0x60/4 - 0x100/4;

				old_timer_regs = m_timer_regs[timer];

				m_timer_regs[timer] = (m_timer_regs[timer] & ~(mem_mask & 0xFFFF0000)) | (data & (mem_mask & 0xFFFF0000));

//              printf("%x to timer %d (mask %x PC %x)\n", data, timer, ~mem_mask, m_maincpu->pc());

				if (ACCESSING_BITS_0_15)
				{
					m_timer_reload[timer] = ((m_timer_reload[timer] & ~mem_mask) | (data & mem_mask)) & 0x0000FFFF;
					m_timer_recalc[timer] = 1;
				}

				// enabling this timer?
				if ((ACCESSING_BITS_16_31) && (data & 0x800000))
				{
					double final;

					if ((old_timer_regs & 0x00800000) == 0) // start bit 0 -> 1
					{
						m_timer_regs[timer] = (m_timer_regs[timer] & 0xFFFF0000) | (m_timer_reload[timer] & 0x0000FFFF);
					}

					rate = 0x10000 - (m_timer_regs[timer] & 0xffff);

					clocksel = timer_clks[(m_timer_regs[timer] >> 16) & 3].dvalue();

					final = clocksel / rate;

					m_timer_hz[timer] = final;

					m_timer_recalc[timer] = 0;

//                  printf("Enabling timer %d @ %f Hz\n", timer, final);

					// enable the timer
					if( !(data & 0x40000) ) // if we're not in Count-Up mode
					{
						attotime time = attotime::from_hz(final);
						m_tmr_timer[timer]->adjust(time, timer, time);
					}
				}
			}
			break;
		case 0x0128/4:
			if( ACCESSING_BITS_0_15 )
			{
				// normal mode ?
				if (!(RCNT & 0x8000) && !(data & 0x2000))
				{
					// start ?
					if (!(siocnt & 0x0080) && (data & 0x0080))
					{
						SIOCNT_RESET(0x0080);
						// request interrupt ?
						if (data & 0x4000)
						{
							request_irq(INT_SIO);
						}
					}
				}
			}
			break;
		case 0x0200/4:
			if( ACCESSING_BITS_0_15 )
			{
#if 0
				if (IE & IF)
				{
					request_irq(IF);
				}
#endif
			}
			if( ACCESSING_BITS_16_31 )
			{
				IF_RESET(( data & mem_mask ) >> 16);

				// if we still have interrupts, yank the IRQ line again
				if (IF)
				{
					m_irq_timer->adjust(m_maincpu->clocks_to_attotime(120));
				}
			}
			break;
		case 0x0208/4:
			if( ACCESSING_BITS_0_15 )
			{
				if (IF)
				{
					m_irq_timer->adjust(attotime::zero);
				}
			}
			break;
		case 0x0300/4:
			if( ACCESSING_BITS_8_15 )
			{
				// power down commanded, halt the CPU
				m_maincpu->spin_until_interrupt();
			}
			break;
	}
}

uint32_t gba_cons_state::gba_bios_r(offs_t offset, uint32_t mem_mask)
{
	uint32_t *rom = m_region_maincpu;
	if (m_bios_hack->read())
	{
		// partially patch out logo and checksum checks
		// (useful to run some protos + to test homebrew)
		if (ACCESSING_BITS_0_15 && (offset == 0x6fc/4))
			return 0;
	}

	if (m_maincpu->pc() >= 0x4000)
	{
		//printf("GBA protection: blocking PC=%x\n", m_maincpu->pc());
		return 0;
	}

	return rom[offset & 0x3fff];
}

uint32_t gba_state::gba_10000000_r(offs_t offset, uint32_t mem_mask)
{
	auto &mspace = m_maincpu->space(AS_PROGRAM);
	uint32_t data;
	uint32_t pc = m_maincpu->state_int(arm7_cpu_device::ARM7_PC);
	if (pc >= 0x10000000)
	{
		return 0;
	}
	uint32_t cpsr = m_maincpu->state_int(arm7_cpu_device::ARM7_CPSR);
	if (T_IS_SET( cpsr))
	{
		data = mspace.read_dword(pc + 8);
	}
	else
	{
		uint16_t insn = mspace.read_word(pc + 4);
		data = (insn << 16) | (insn << 0);
	}
	logerror("%s: unmapped program memory read from %08X = %08X & %08X\n", machine().describe_context( ), 0x10000000 + (offset << 2), data, mem_mask);
	return data;
}

void gba_state::int_hblank_callback(int state)
{
	request_irq(INT_HBL);
}

void gba_state::int_vblank_callback(int state)
{
	request_irq(INT_VBL);
}

void gba_state::int_vcount_callback(int state)
{
	request_irq(INT_VCNT);
}

void gba_state::dma_hblank_callback(int state)
{
	for (int ch = 0; ch < 4; ch++)
	{
		int ctrl = DMACNT_H(ch);

		if ((ctrl & 0x8000) && ((ctrl & 0x3000) == 0x2000))
			dma_exec(ch);
	}
}

void gba_state::dma_vblank_callback(int state)
{
	for (int ch = 0; ch < 4; ch++)
	{
		int ctrl = DMACNT_H(ch);

		if ((ctrl & 0x8000) && ((ctrl & 0x3000) == 0x1000))
			dma_exec(ch);
	}
}

void gba_state::gba_map(address_map &map)
{
	map.unmap_value_high(); // for "Fruit Mura no Doubutsu Tachi" and "Classic NES Series"
	map(0x02000000, 0x0203ffff).ram().mirror(0xfc0000); // External RAM (16 bit)
	map(0x03000000, 0x03007fff).ram().mirror(0xff8000); // Internal RAM (32 bit)
	map(0x04000000, 0x0400005f).rw("lcd", FUNC(gba_lcd_device::video_r), FUNC(gba_lcd_device::video_w));
	map(0x04000060, 0x040003ff).rw(FUNC(gba_state::gba_io_r), FUNC(gba_state::gba_io_w));
	map(0x04000400, 0x04ffffff).noprw();                                         // Not used
	map(0x05000000, 0x050003ff).mirror(0x00fffc00).rw("lcd", FUNC(gba_lcd_device::gba_pram_r), FUNC(gba_lcd_device::gba_pram_w));  // Palette RAM
	map(0x06000000, 0x06017fff).mirror(0x00fe0000).rw("lcd", FUNC(gba_lcd_device::gba_vram_r), FUNC(gba_lcd_device::gba_vram_w));  // VRAM
	map(0x06018000, 0x0601ffff).mirror(0x00fe0000).rw("lcd", FUNC(gba_lcd_device::gba_vram_r), FUNC(gba_lcd_device::gba_vram_w));  // VRAM
	map(0x07000000, 0x070003ff).mirror(0x00fffc00).rw("lcd", FUNC(gba_lcd_device::gba_oam_r), FUNC(gba_lcd_device::gba_oam_w));    // OAM

	map(0x10000000, 0xffffffff).r(FUNC(gba_state::gba_10000000_r)); // for "Justice League Chronicles" (game bug)
}

void gba_cons_state::gba_cons_map(address_map &map)
{
	gba_map(map);

	map(0x00000000, 0x00003fff).rom().mirror(0x01ffc000).r(FUNC(gba_cons_state::gba_bios_r));
	//map(0x08000000, 0x0cffffff)  // cart ROM + mirrors, mapped here at machine_start if a cart is present
}

void gba_robotech_state::gba_robotech_map(address_map &map)
{
	gba_map(map);

	map(0x00000000, 0x007fffff).rom().region("maincpu", 0x00000000); // first part of the ROM is a BIOS replacement?
	map(0x08000000, 0x087fffff).rom().region("maincpu", 0x00800000); // second part is the game?
}

static INPUT_PORTS_START( gbadv )
	PORT_START("INPUTS")
	PORT_BIT( 0xfc00, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("P1 L") PORT_PLAYER(1) // L
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("P1 R") PORT_PLAYER(1) // R
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_START ) PORT_PLAYER(1) // START
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_SELECT ) PORT_PLAYER(1)    // SELECT
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("B") PORT_PLAYER(1)    // B
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("A") PORT_PLAYER(1)    // A
INPUT_PORTS_END

static INPUT_PORTS_START( gbadv_cons )
	PORT_INCLUDE( gbadv )

	PORT_START("SKIP_CHECK")
	PORT_CONFNAME( 0x01, 0x00, "[HACK] Skip BIOS Logo check" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
INPUT_PORTS_END


void gba_state::machine_reset()
{
	memset(m_regs, 0, sizeof(m_regs));

	SOUNDBIAS_SET(0x0200);
	SIOMULTI0_SET(0xffff);
	SIOMULTI1_SET(0xffff);
	SIOMULTI2_SET(0xffff);
	SIOMULTI3_SET(0xffff);
	KEYCNT_SET(0x03ff);
	RCNT_SET(0x8000);
	JOYSTAT_SET(0x0002);

	m_dma_timer[0]->adjust(attotime::never);
	m_dma_timer[1]->adjust(attotime::never, 1);
	m_dma_timer[2]->adjust(attotime::never, 2);
	m_dma_timer[3]->adjust(attotime::never, 3);

	for (auto &fifo : m_fifo)
	{
		fifo.ptr = fifo.in = 0;   // indicate empty
		fifo.size = 0;
		fifo.remains = 0;
		fifo.sample = 0;
	}

	// and clear the DACs
	m_ldac[0]->write(0);
	m_rdac[0]->write(0);
	m_ldac[1]->write(0);
	m_rdac[1]->write(0);
}

void gba_state::machine_start()
{
	/* and one for each DMA channel */
	m_dma_timer[0] = timer_alloc(FUNC(gba_state::dma_complete), this);
	m_dma_timer[1] = timer_alloc(FUNC(gba_state::dma_complete), this);
	m_dma_timer[2] = timer_alloc(FUNC(gba_state::dma_complete), this);
	m_dma_timer[3] = timer_alloc(FUNC(gba_state::dma_complete), this);
	m_dma_timer[0]->adjust(attotime::never);
	m_dma_timer[1]->adjust(attotime::never, 1);
	m_dma_timer[2]->adjust(attotime::never, 2);
	m_dma_timer[3]->adjust(attotime::never, 3);

	/* also one for each timer (heh) */
	m_tmr_timer[0] = timer_alloc(FUNC(gba_state::timer_expire), this);
	m_tmr_timer[1] = timer_alloc(FUNC(gba_state::timer_expire), this);
	m_tmr_timer[2] = timer_alloc(FUNC(gba_state::timer_expire), this);
	m_tmr_timer[3] = timer_alloc(FUNC(gba_state::timer_expire), this);
	m_tmr_timer[0]->adjust(attotime::never);
	m_tmr_timer[1]->adjust(attotime::never, 1);
	m_tmr_timer[2]->adjust(attotime::never, 2);
	m_tmr_timer[3]->adjust(attotime::never, 3);

	/* and an IRQ handling timer */
	m_irq_timer = timer_alloc(FUNC(gba_state::handle_irq), this);
	m_irq_timer->adjust(attotime::never);

	save_item(NAME(m_regs));
	save_item(NAME(m_dma_src));
	save_item(NAME(m_dma_dst));
	save_item(NAME(m_dma_cnt));
	save_item(NAME(m_timer_regs));
	save_item(NAME(m_timer_reload));
	save_item(NAME(m_timer_recalc));
	save_item(NAME(m_timer_hz));
	save_item(STRUCT_MEMBER(m_fifo, ptr));
	save_item(STRUCT_MEMBER(m_fifo, in));
	save_item(STRUCT_MEMBER(m_fifo, size));
	save_item(STRUCT_MEMBER(m_fifo, remains));
	save_item(STRUCT_MEMBER(m_fifo, sample));
	save_item(STRUCT_MEMBER(m_fifo, word));
}

void gba_cons_state::machine_start()
{
	gba_state::machine_start();

	// install the cart ROM & SRAM into the address map, if present
	if (m_cart->exists())
	{

		std::string region_tag;
		memory_region *cart_rom = memregion(region_tag.assign(m_cart->tag()).append(GBASLOT_ROM_REGION_TAG));

		// install ROM accesses
		m_maincpu->space(AS_PROGRAM).install_rom(0x08000000, 0x09ffffff, cart_rom->base());
		m_maincpu->space(AS_PROGRAM).install_rom(0x0a000000, 0x0bffffff, cart_rom->base());
		m_maincpu->space(AS_PROGRAM).install_rom(0x0c000000, 0x0cffffff, cart_rom->base());

		m_maincpu->space(AS_PROGRAM).install_read_handler(0x80000c4, 0x80000cb, read32s_delegate(*m_cart, FUNC(gba_cart_slot_device::read_gpio)));
		m_maincpu->space(AS_PROGRAM).install_write_handler(0x80000c4, 0x80000cb, write32s_delegate(*m_cart, FUNC(gba_cart_slot_device::write_gpio)));

		// add nvram to save state
		m_cart->save_nvram();

		// install the cart NVRAM handlers if necessary
		if (m_cart->get_type() == GBA_SRAM || m_cart->get_type() == GBA_DRILLDOZ || m_cart->get_type() == GBA_WARIOTWS)
		{
			m_maincpu->space(AS_PROGRAM).install_read_handler(0xe000000, 0xe00ffff, read32s_delegate(*m_cart, FUNC(gba_cart_slot_device::read_ram)));
			m_maincpu->space(AS_PROGRAM).install_write_handler(0xe000000, 0xe00ffff, write32s_delegate(*m_cart, FUNC(gba_cart_slot_device::write_ram)));
			m_maincpu->space(AS_PROGRAM).install_read_handler(0xe010000, 0xe01ffff, read32s_delegate(*m_cart, FUNC(gba_cart_slot_device::read_ram)));
			m_maincpu->space(AS_PROGRAM).install_write_handler(0xe010000, 0xe01ffff, write32s_delegate(*m_cart, FUNC(gba_cart_slot_device::write_ram)));
		}
		if (m_cart->get_type() == GBA_EEPROM || m_cart->get_type() == GBA_EEPROM4 || m_cart->get_type() == GBA_EEPROM64 || m_cart->get_type() == GBA_BOKTAI)
		{
			// for games larger than 16MB the actual range is smaller but read_ram/write_ram handles that!
			m_maincpu->space(AS_PROGRAM).install_read_handler(0xd000000, 0xdffffff, read32s_delegate(*m_cart, FUNC(gba_cart_slot_device::read_ram)));
			m_maincpu->space(AS_PROGRAM).install_write_handler(0xd000000, 0xdffffff, write32s_delegate(*m_cart, FUNC(gba_cart_slot_device::write_ram)));
		}
		if (m_cart->get_type() == GBA_YOSHIUG)
		{
			// EEPROM
			m_maincpu->space(AS_PROGRAM).install_read_handler(0xd000000, 0xdffffff, read32s_delegate(*m_cart, FUNC(gba_cart_slot_device::read_ram)));
			m_maincpu->space(AS_PROGRAM).install_write_handler(0xd000000, 0xdffffff, write32s_delegate(*m_cart, FUNC(gba_cart_slot_device::write_ram)));
			// Tilt Sensor
			m_maincpu->space(AS_PROGRAM).install_read_handler(0xe008000, 0xe0085ff, read32s_delegate(*m_cart, FUNC(gba_cart_slot_device::read_tilt)));
			m_maincpu->space(AS_PROGRAM).install_write_handler(0xe008000, 0xe0085ff, write32sm_delegate(*m_cart, FUNC(gba_cart_slot_device::write_tilt)));
		}
		// merge the two flash and mask accesses in read_ram?!?
		if (m_cart->get_type() == GBA_FLASH || m_cart->get_type() == GBA_FLASH512 || m_cart->get_type() == GBA_FLASH_RTC)
		{
			m_maincpu->space(AS_PROGRAM).install_read_handler(0xe000000, 0xe00ffff, read32s_delegate(*m_cart, FUNC(gba_cart_slot_device::read_ram)));
			m_maincpu->space(AS_PROGRAM).install_write_handler(0xe000000, 0xe00ffff, write32s_delegate(*m_cart, FUNC(gba_cart_slot_device::write_ram)));
		}
		if (m_cart->get_type() == GBA_FLASH1M || m_cart->get_type() == GBA_FLASH1M_RTC)
		{
			m_maincpu->space(AS_PROGRAM).install_read_handler(0xe000000, 0xe01ffff, read32s_delegate(*m_cart, FUNC(gba_cart_slot_device::read_ram)));
			m_maincpu->space(AS_PROGRAM).install_write_handler(0xe000000, 0xe01ffff, write32s_delegate(*m_cart, FUNC(gba_cart_slot_device::write_ram)));
		}
		if (m_cart->get_type() == GBA_3DMATRIX)
		{
			m_maincpu->space(AS_PROGRAM).install_write_handler(0x08800000, 0x088001ff, write32sm_delegate(*m_cart, FUNC(gba_cart_slot_device::write_mapper)));
			memory_region *cart_romhlp = memregion(region_tag.assign(m_cart->tag()).append(GBAHELP_ROM_REGION_TAG));
			m_maincpu->space(AS_PROGRAM).install_rom(0x08000000, 0x09ffffff, cart_romhlp->base());
		}

	}
}

static void gba_cart(device_slot_interface &device)
{
	device.option_add_internal("gba_rom",          GBA_ROM_STD);
	device.option_add_internal("gba_sram",         GBA_ROM_SRAM);
	device.option_add_internal("gba_drilldoz",     GBA_ROM_DRILLDOZ);       // Rumble output unemulated
	device.option_add_internal("gba_wariotws",     GBA_ROM_WARIOTWS);       // Rumble output unemulated
	device.option_add_internal("gba_eeprom",       GBA_ROM_EEPROM);
	device.option_add_internal("gba_eeprom_4k",    GBA_ROM_EEPROM);
	device.option_add_internal("gba_yoshiug",      GBA_ROM_YOSHIUG);
	device.option_add_internal("gba_eeprom_64k",   GBA_ROM_EEPROM64);
	device.option_add_internal("gba_boktai",       GBA_ROM_BOKTAI);
	device.option_add_internal("gba_flash",        GBA_ROM_FLASH);          // Panasonic
	device.option_add_internal("gba_flash_rtc",    GBA_ROM_FLASH_RTC);      // Panasonic
	device.option_add_internal("gba_flash_512",    GBA_ROM_FLASH);          // Panasonic
	device.option_add_internal("gba_flash_1m",     GBA_ROM_FLASH1M);        // Sanyo
	device.option_add_internal("gba_flash_1m_rtc", GBA_ROM_FLASH1M_RTC);    // Sanyo
	device.option_add_internal("gba_3dmatrix",     GBA_ROM_3DMATRIX);
}


void gba_state::gbadv(machine_config &config)
{
	ARM7(config, m_maincpu, 4.194304_MHz_XTAL * 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &gba_state::gba_map);

	gba_lcd_device &lcd(GBA_LCD(config, "lcd", 0));
	lcd.int_hblank_callback().set(FUNC(gba_state::int_hblank_callback));
	lcd.int_vblank_callback().set(FUNC(gba_state::int_vblank_callback));
	lcd.int_vcount_callback().set(FUNC(gba_state::int_vcount_callback));
	lcd.dma_hblank_callback().set(FUNC(gba_state::dma_hblank_callback));
	lcd.dma_vblank_callback().set(FUNC(gba_state::dma_vblank_callback));

	SPEAKER(config, "speaker", 2).front();
	AGB_APU(config, m_gbsound, 4.194304_MHz_XTAL);
	m_gbsound->add_route(0, "speaker", 0.5, 0);
	m_gbsound->add_route(1, "speaker", 0.5, 1);

	DAC_8BIT_R2R_TWOS_COMPLEMENT(config, m_ldac[0], 0).add_route(ALL_OUTPUTS, "speaker", 0.5, 0); // unknown DAC
	DAC_8BIT_R2R_TWOS_COMPLEMENT(config, m_rdac[0], 0).add_route(ALL_OUTPUTS, "speaker", 0.5, 1); // unknown DAC
	DAC_8BIT_R2R_TWOS_COMPLEMENT(config, m_ldac[1], 0).add_route(ALL_OUTPUTS, "speaker", 0.5, 0); // unknown DAC
	DAC_8BIT_R2R_TWOS_COMPLEMENT(config, m_rdac[1], 0).add_route(ALL_OUTPUTS, "speaker", 0.5, 1); // unknown DAC

}

void gba_cons_state::gbadv_cons(machine_config &config)
{
	gbadv(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &gba_cons_state::gba_cons_map);

	GBA_CART_SLOT(config, m_cart, gba_cart, nullptr);
	SOFTWARE_LIST(config, "cart_list").set_original("gba");
}

void gba_robotech_state::gbadv_robotech(machine_config &config)
{
	gbadv(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &gba_robotech_state::gba_robotech_map);
}

ROM_START( gba )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "gba.bin", 0x000000, 0x004000, CRC(81977335) SHA1(300c20df6731a33952ded8c436f7f186d25d3492) )
ROM_END

ROM_START( robotech )
	ROM_REGION( 0x1000100, "maincpu", 0 )
	ROM_LOAD( "coleco_robotech_mx29gl128elt21_00c22273.bin", 0x0000000, 0x1000100, CRC(04beee9c) SHA1(acf07d51c525b055679186cc07c6ac2cd8f45eac) )
ROM_END

//   YEAR  NAME       PARENT  COMPAT  MACHINE           INPUT       CLASS               INIT        COMPANY     FULLNAME            FLAGS
CONS(2001, gba,       0,      0,      gbadv_cons,       gbadv_cons, gba_cons_state,     empty_init, "Nintendo", "Game Boy Advance", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND)

// this is a single game reissue of "Robotech - The Macross Saga (Euro, USA)" on the GBA but with double
// sized ROM (BIOS replacement in first half?) and other mods.  It is unclear how compatible this is with
// standard hardware.
CONS(2018, robotech,  0,      0,      gbadv_robotech,   gbadv,      gba_robotech_state, empty_init, "Coleco",   "Robotech",         MACHINE_NOT_WORKING)



gem_rp.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for GEM/Baldwin Pianovelle RP electric pianos.

****************************************************************************/

#include "emu.h"
//#include "bus/midi/midi.h"
#include "cpu/h8/h83003.h"
#include "machine/intelfsh.h"


namespace {

class gem_rp_state : public driver_device
{
public:
	gem_rp_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void rp200(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<h83003_device> m_maincpu;
};


void gem_rp_state::mem_map(address_map &map)
{
	map(0x000000, 0x0fffff).rw("flash", FUNC(intelfsh16_device::read), FUNC(intelfsh16_device::write));
	map(0x200000, 0x23ffff).ram();
}


static INPUT_PORTS_START(rp200)
INPUT_PORTS_END

void gem_rp_state::rp200(machine_config &config)
{
	H83003(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &gem_rp_state::mem_map);

	AMD_29F800B_16BIT(config, "flash");

	//DISP3(config, "disp3mst", 45.1584_MHz_XTAL);
	//DISP3(config, "disp3slv", 45.1584_MHz_XTAL);
}

ROM_START(rp200)
	ROM_REGION(0x100000, "flash", 0)
	ROM_LOAD("gem_rp200_rp220_grp300_am29f800bb_firmware_v1.02.ic3", 0x000000, 0x100000, CRC(4980d8b6) SHA1(8d8c0310b962d422fa1d494eaaf5fe4fd3d20eb5))

	ROM_REGION16_BE(0x200000, "library", 0)
	ROM_LOAD("104014.ic9", 0x000000, 0x200000, NO_DUMP) // HN624316

	ROM_REGION16_BE(0xc00000, "wave", 0)
	ROM_LOAD("104041_wave98.ic13", 0x000000, 0x800000, NO_DUMP) // MX23C6410
	ROM_LOAD("104023_wave3.ic14", 0x800000, 0x400000, NO_DUMP) // 23C2000G
ROM_END

} // anonymous namespace


SYST(1999, rp200, 0, 0, rp200, rp200, gem_rp_state, empty_init, "Generalmusic", "GEM RealPiano RP200", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



generalplus_gp327902.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

#include "emu.h"

#include "cpu/arm7/arm7.h"
#include "machine/timer.h"

#include "screen.h"
#include "speaker.h"

#include "multibyte.h"


namespace {

class generalplus_gp327902_game_state : public driver_device
{
public:
	generalplus_gp327902_game_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen")
	{ }

	void gp327902(machine_config &config) ATTR_COLD;

	void init_spi() ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void arm_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;

	uint32_t screen_update_gp327902(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

private:
	TIMER_DEVICE_CALLBACK_MEMBER(timer);

	uint32_t c0020070_unk_r() { return machine().rand(); }
	void c0060000_unk_w(uint32_t data);
	uint32_t c008000c_unk_r() { return machine().rand(); }
	uint32_t d000003c_unk_r() { return 0xffffffff; }

	int m_copybase;
	int m_copylength;
	int m_copydest;
};


void generalplus_gp327902_game_state::c0060000_unk_w(uint32_t data)
{
	/*

	this is some kind of debug serial output, it currently outputs (if copy_lowest_block is set to false) the following sequence

	adc_init
	DAC Task Create[0]
	DAC BG Task Create[0]
	Audio Task Create[4]
	Audio BG Task Create[6]
	FileServ Task Create[8]
	Image Task Create[10]
	audio_init()
	watch-dog enable
	power on

	with copy_lowest_block as true you get

	GP DV BootLoader v2.2 Entry @ 0 MHz
	GPDV  chip detect

	*/
	//printf("%c", data & 0xff);
}

void generalplus_gp327902_game_state::arm_map(address_map &map)
{
	map(0x00000000, 0x001fffff).ram(); // 16M-bit internal SDRAM

	map(0xc0020070, 0xc0020073).r(FUNC(generalplus_gp327902_game_state::c0020070_unk_r));
	map(0xc0060000, 0xc0060003).w(FUNC(generalplus_gp327902_game_state::c0060000_unk_w));
	map(0xc008000c, 0xc008000f).r(FUNC(generalplus_gp327902_game_state::c008000c_unk_r));
	map(0xd000003c, 0xd000003f).r(FUNC(generalplus_gp327902_game_state::d000003c_unk_r));

	map(0xf8000000, 0xf80003ff).ram(); // writes pointers used by exceptions (including IRQs) here
}

TIMER_DEVICE_CALLBACK_MEMBER( generalplus_gp327902_game_state::timer )
{
	m_maincpu->set_input_line(arm7_cpu_device::ARM7_FIRQ_LINE, HOLD_LINE);
}

uint32_t generalplus_gp327902_game_state::screen_update_gp327902(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void generalplus_gp327902_game_state::machine_start()
{
}

void generalplus_gp327902_game_state::machine_reset()
{
	// perform some kind of bootstrap likely done by an internal ROM
	uint8_t *spirom = memregion("spi")->base();
	address_space &mem = m_maincpu->space(AS_PROGRAM);

	for (int i = 0; i < m_copylength / 2; i++)
	{
		uint16_t word = get_u16le(&spirom[m_copybase + (i * 2)]);
		mem.write_word(m_copydest + (i * 2), word);
	}

	m_maincpu->set_state_int(arm7_cpu_device::ARM7_R15, m_copydest);
}

static INPUT_PORTS_START( gp327902 )
INPUT_PORTS_END

void generalplus_gp327902_game_state::gp327902(machine_config &config)
{
	ARM9(config, m_maincpu, 240'000'000); // unknown core / frequency, but ARM based
	m_maincpu->set_addrmap(AS_PROGRAM, &generalplus_gp327902_game_state::arm_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320, 262);
	m_screen->set_visarea(0, 320-1, 0, 240-1);
	m_screen->set_screen_update(FUNC(generalplus_gp327902_game_state::screen_update_gp327902));

	SPEAKER(config, "speaker", 2).front();

	TIMER(config, "timer").configure_periodic(FUNC(generalplus_gp327902_game_state::timer), attotime::from_hz(1000));
}

void generalplus_gp327902_game_state::init_spi()
{
	const bool copy_lowest_block = true; // change this to false to copy a later part of the bootstrap sequence on sanpetx?

	// these likely come from the header at the start of the ROM
	if (copy_lowest_block)
	{
		// all dumped sets have a block here
		m_copybase = 0x800;
		m_copydest = 0x1f8000;
		m_copylength = 0x2800;
	}
	else
	{
		// sanxpet has the main block here, which is likely copied to RAM by previous parts of the bootloader above
		m_copybase = 0x16000;
		m_copydest = 0x0;
		m_copylength = 0x200000;
	}
}

ROM_START( sanxpet )
	ROM_REGION(  0x800000, "spi", ROMREGION_ERASE00 )
	ROM_LOAD( "25l64.u1", 0x0000, 0x800000, CRC(f28b9fd3) SHA1(8ed4668f271cbe01065bc0836e49ce70faf10834) )
ROM_END

ROM_START( sanxpeta )
	ROM_REGION(  0x800000, "spi", ROMREGION_ERASE00 )
	ROM_LOAD( "gpr25l6403f.u1", 0x0000, 0x800000, CRC(cb5dc7b6) SHA1(425c4d01b56784278b77824a354d9efa46e1a74e) )
ROM_END

ROM_START( tomyegg )
	ROM_REGION(  0x800000, "spi", ROMREGION_ERASE00 )
	ROM_LOAD( "gpr25l6403f.u1", 0x0000, 0x800000, CRC(2acd6752) SHA1(85e59546a1af4618c75c275cead7ef0f5e3faa44) )
ROM_END

ROM_START( chikawac )
	ROM_REGION(  0x800000, "spi", ROMREGION_ERASE00 )
	ROM_LOAD( "xm25qh64c.u1", 0x0000, 0x800000, CRC(88c984aa) SHA1(6e176960b64fc3576efaa40dfe2ff0a6dcea3c3f) )
ROM_END

} // anonymous namespace

// Tomy / San-X devices

// dates for each of these taken from back of case, are the DX versions different software or just different accessories?

// 2018 version is a square device - Sumikko Gurashi - Sumikko Atsume (すみっコぐらし すみっコあつめ)
// see evolution_handheld.cpp

// 2019 version is house shaped device - すみっコぐらし すみっコさがし
CONS( 2019, sanxpet,         0,        0,      gp327902, gp327902, generalplus_gp327902_game_state, init_spi,  "San-X / Tomy",        "Sumikko Gurashi - Sumikko Sagashi (Japan)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
// or Sumikko Gurashi - Sumikko Sagashi DX (すみっコぐらし すみっコさがしDX "Sumikko Gurashi the movie" alt version)

// 2020 version - Sumikko Gurashi - Sumikko Catch, see generalplus_gpl16250_spi_direct.cpp

// 2021 version is a square device with a tiny 'mole' figure on top - すみっコぐらし すみっコみっけDX
// or Sumikko Gurashi - Sumikko Mikke (すみっコぐらし すみっコみっけ)
CONS( 2021, sanxpeta,        0,        0,      gp327902, gp327902, generalplus_gp327902_game_state, init_spi,  "San-X / Tomy",        "Sumikko Gurashi - Sumikko Mikke DX (Japan)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// other devices on the same Soc

// キラッとプリ☆チャン プリたまGO ミスティパープル
CONS( 2019, tomyegg,         0,        0,      gp327902, gp327902, generalplus_gp327902_game_state, init_spi,  "Tomy",        "Kiratto Pri-Chan - PritamaGO: Misty Purple (Japan)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
// these also exist, are they the same software or different versions?
// Powder Pink (パウダーピンク)
// Mint Blue (ミントブルー).

CONS( 2021, chikawac,        0,        0,      gp327902, gp327902, generalplus_gp327902_game_state, init_spi,  "Tomy",        "Chiikawa Camera De Ya-! (Japan)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



generalplus_gpl_unknown.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

/*
   NOTES:

   Exact size of internal ROM is unknown, but it appears to occupy at least 0x4000 - 0xffff (word addresses)
   in unSP space and is different for each game.  Data is pulled for jump tables, where the addresses are in
   internal ROM and the code in external

   For correctly offset disassembly use
   unidasm xxx.bin -arch unsp12 -xchbytes -basepc 200000 >palace.txt

   Multiple different units appear to share the same ROM with a jumper to select game.
   It should be verified in each case that the external ROM was not changed

   It is confirmed that in some cases the external ROMs contain both versions for Micro Arcade and Tiny Arcade
   units, with different sound, but in some cases the expected game behavior differs too, so the code revisions
   could be different.  (for example the Micro Arcade Pac-Man doesn't display points when you eat a ghost)
*/


#include "emu.h"

#include "cpu/unsp/unsp.h"
#include "machine/timer.h"
#include "sound/dac.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "logmacro.h"


namespace {

class generalplus_gpl_unknown_state : public driver_device
{
public:
	generalplus_gpl_unknown_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		//m_mainrom(*this, "maincpu"),
		//m_mainram(*this, "mainram"),
		m_palette(*this, "palette"),
		m_screen(*this, "screen"),
		m_dac(*this, "dac"),
		m_spirom(*this, "spi"),
		m_testio(*this, "TEST")
	{ }

	void generalplus_gpl_unknown(machine_config &config);

	void init_siddr();

private:
	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);

	required_device<unsp_12_device> m_maincpu;
	//required_region_ptr<uint16_t> m_mainrom;
	//required_shared_ptr<uint16_t> m_mainram;

	required_device<palette_device> m_palette;
	required_device<screen_device> m_screen;
	required_device<dac_word_device_base> m_dac;

	required_region_ptr<uint16_t> m_spirom;
	required_ioport m_testio;

	uint16_t reg3001_r(offs_t offset);
	void reg3001_w(offs_t offset, uint16_t data);
	uint16_t reg3002_r(offs_t offset);
	void reg3002_w(offs_t offset, uint16_t data);
	uint16_t reg3003_r(offs_t offset);
	void reg3003_w(offs_t offset, uint16_t data);
	uint16_t reg3004_r(offs_t offset);
	uint16_t reg3005_r(offs_t offset);
	void reg3005_w(offs_t offset, uint16_t data);
	uint16_t reg3006_r(offs_t offset);
	uint16_t reg3007_r(offs_t offset);

	uint16_t reg300f_r(offs_t offset);

	uint16_t reg3016_r(offs_t offset);

	void reg3034_w(offs_t offset, uint16_t data);
	void reg3041_audiodac_w(offs_t offset, uint16_t data);

	uint16_t reg3050_r(offs_t offset);
	void reg3050_w(offs_t offset, uint16_t data);
	void reg3051_w(offs_t offset, uint16_t data);
	uint16_t reg3052_r(offs_t offset);
	uint16_t reg3053_r(offs_t offset);

	uint16_t reg3090_r(offs_t offset);
	uint16_t reg3091_r(offs_t offset);
	void reg3092_lcd_w(offs_t offset, uint16_t data);
	uint16_t reg3094_r(offs_t offset);
	uint16_t reg3095_r(offs_t offset);

	void reg30e0_w(offs_t offset, uint16_t data);
	void reg30e1_w(offs_t offset, uint16_t data);
	void reg30e2_w(offs_t offset, uint16_t data);
	void reg30e3_w(offs_t offset, uint16_t data);
	uint16_t reg30e4_r(offs_t offset);
	uint16_t reg30e5_r(offs_t offset);

	TIMER_DEVICE_CALLBACK_MEMBER(timer);
	TIMER_DEVICE_CALLBACK_MEMBER(timer2);
	TIMER_DEVICE_CALLBACK_MEMBER(timer3);

	uint16_t m_display[128*2 * 128];
	int m_displayposx;
	int m_displayposy;
	uint16_t m_3001;
	uint16_t m_3003;
	uint16_t m_3005;
	uint16_t m_3050;

	void map(address_map &map) ATTR_COLD;
};

uint32_t generalplus_gpl_unknown_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	int count = 0;
	for (int y = 0; y < 128; y++)
	{
		u16* dst = &bitmap.pix(y);

		for (int x = 0; x < 128; x++)
		{
			uint8_t pix1 = m_display[count++];
			uint8_t pix2 = m_display[count++];

			uint16_t pal = ((pix1<<8) | pix2);

			dst[x] = pal;
		}
	}
	return 0;
}


static INPUT_PORTS_START( generalplus_gpl_unknown )
	PORT_START("TEST")
	PORT_DIPNAME( 0x0001, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
INPUT_PORTS_END

uint16_t generalplus_gpl_unknown_state::reg3001_r(offs_t offset)
{
	return m_3001;
}

void generalplus_gpl_unknown_state::reg3001_w(offs_t offset, uint16_t data)
{
	m_3001 = data;
	logerror("%s: reg3001_w %04x\n", machine().describe_context(), data);
}

uint16_t generalplus_gpl_unknown_state::reg3002_r(offs_t offset)
{
	return 0x0000;//machine().rand();
}

void generalplus_gpl_unknown_state::reg3002_w(offs_t offset, uint16_t data)
{
	//logerror("%s: reg3002_w %04x\n", machine().describe_context(), data);
}

uint16_t generalplus_gpl_unknown_state::reg3003_r(offs_t offset)
{
	return m_3003;
}

void generalplus_gpl_unknown_state::reg3003_w(offs_t offset, uint16_t data)
{
	m_3003 = data;
	logerror("%s: reg3003_w %04x\n", machine().describe_context(), data);
}

uint16_t generalplus_gpl_unknown_state::reg3004_r(offs_t offset)
{
	return m_testio->read();
}

uint16_t generalplus_gpl_unknown_state::reg3005_r(offs_t offset)
{
	return m_3005;
}

void generalplus_gpl_unknown_state::reg3005_w(offs_t offset, uint16_t data)
{
	m_3005 = data;
	//logerror("%s: reg3005_w %04x\n", machine().describe_context(), data);
}


uint16_t generalplus_gpl_unknown_state::reg3006_r(offs_t offset)
{
	return 0x0000;//machine().rand();
}

uint16_t generalplus_gpl_unknown_state::reg3007_r(offs_t offset)
{
	return 0x0000;//machine().rand();
}

uint16_t generalplus_gpl_unknown_state::reg300f_r(offs_t offset)
{
	return 0x0000;//machine().rand();
}

uint16_t generalplus_gpl_unknown_state::reg3016_r(offs_t offset)
{
	return 0x0000;//machine().rand();
}



void generalplus_gpl_unknown_state::reg3034_w(offs_t offset, uint16_t data)
{
	// writes a lot of 0x5555 here in a block
	logerror("%s: reg3034_w %04x\n", machine().describe_context(), data);
}

void generalplus_gpl_unknown_state::reg3041_audiodac_w(offs_t offset, uint16_t data)
{
//  logerror("%s: reg3041_audiodac_w %04x\n", machine().describe_context(), data);

	// mapacman only writes 0000 / 7fff / 8000, but is known to have more limited sound than other units

	m_dac->data_w(data);
}

uint16_t generalplus_gpl_unknown_state::reg3050_r(offs_t offset)
{
	return m_3050;
}

void generalplus_gpl_unknown_state::reg3050_w(offs_t offset, uint16_t data)
{
	uint16_t old = m_3050;
	m_3050 = data;

	// probably not, but does happen at the end of a line
	if ((old & 0x0100) != (m_3050 & 0x0100))
	{
		if ((m_3050 & 0x0100) == 0x0000)
		{
			m_displayposx = 0;
			m_displayposy++;
			if (m_displayposy == 128)
			{
				m_displayposy = 0;
			}
		}
	}

	//m_displaypos = 0;
	logerror("%s: reg3050_w %04x\n", machine().describe_context(), data);
}

uint16_t generalplus_gpl_unknown_state::reg3052_r(offs_t offset)
{
	return 0x0000;//machine().rand();
}

uint16_t generalplus_gpl_unknown_state::reg3053_r(offs_t offset)
{
	return 0x0000;//machine().rand();
}

void generalplus_gpl_unknown_state::reg30e0_w(offs_t offset, uint16_t data)
{
	//logerror("%s: reg30e0_w %04x\n", machine().describe_context(), data);
}

void generalplus_gpl_unknown_state::reg30e1_w(offs_t offset, uint16_t data)
{
	//logerror("%s: reg30e1_w %04x\n", machine().describe_context(), data);
}

void generalplus_gpl_unknown_state::reg30e2_w(offs_t offset, uint16_t data)
{
	/* this appears to be querying the SPI Flash, eg.
	   command 0xab = Read Electonic Signature
	   command 0x9f = Read ID of the SPI Flash

	[:] ':maincpu' (0075DD): reg3001_w 1c00
	[:] ':maincpu' (00769B): reg3001_w 0c00
	[:] ':maincpu' (00769F): reg30e2_w 00ab (WITHOUT m_3001 & 0x1000)
	[:] ':maincpu' (0076A5): reg3001_w 1c00
	[:] ':maincpu' (0076A7): reg30e4_r
	[:] ':maincpu' (0076AF): reg3001_w 0c00
	[:] ':maincpu' (0076B3): reg30e2_w 009f (WITHOUT m_3001 & 0x1000)
	[:] ':maincpu' (0076B6): reg30e2_w 0000 (WITHOUT m_3001 & 0x1000)
	[:] ':maincpu' (0076B8): reg30e2_w 0000 (WITHOUT m_3001 & 0x1000)
	[:] ':maincpu' (0076BA): reg30e2_w 0000 (WITHOUT m_3001 & 0x1000)
	[:] ':maincpu' (0076C0): reg30e4_r
	[:] ':maincpu' (0076C2): reg30e4_r
	[:] ':maincpu' (0076C4): reg30e4_r
	[:] ':maincpu' (0076C6): reg30e4_r
	*/
	if (!(m_3001 & 0x1000))
	{
		logerror("%s: reg30e2_w %04x (WITHOUT m_3001 & 0x1000)\n", machine().describe_context(), data);
	}
	else
	{
		logerror("%s: reg30e2_w %04x (WITH m_3001 & 0x1000)\n", machine().describe_context(), data);
	}
}

void generalplus_gpl_unknown_state::reg30e3_w(offs_t offset, uint16_t data)
{
	//logerror("%s: reg30e3_w %04x\n", machine().describe_context(), data);
}

uint16_t generalplus_gpl_unknown_state::reg30e4_r(offs_t offset)
{
	logerror("%s: reg30e4_r\n", machine().describe_context());
	return machine().rand() & 0x01;
}

uint16_t generalplus_gpl_unknown_state::reg30e5_r(offs_t offset)
{
	// status flag: loops on bit 0x0010 after writes to 0x30e2
	// and before reading from 0x30e4

	// clrb [3001],12 is also usually executed before write operations
	// to 0x30e2 and setb [3001],12 befoe the result is read back

	return 0x0000;
}

/*
mapacman vectors

004015: 4844        - just reti
        4845 fiq    - has code, acks by writing 8000 to 3051, very similar to irq0?)
        4809 boot vector
        4854 irq 0  - has code, acks by writing 8000 to 3051
        4863 irq 1  - just acks by writing 4000 to 3051, no payload
        486A irq 2 (used - needed to pass check on value in RAM changing in boot) acks with 2000 to 3051
        487D irq 3 (used - needed to pass check on value in RAM changing shortly after) acks with 0100 to 3051?
        4886 irq 4 - just acks by writing 10 to 3051, no payload
        488C irq 5 - push/pop/return
        488F irq 6 - push/pop/return
        4892 irq 7 - minimal payload, no 3051 ack?
*/

void generalplus_gpl_unknown_state::reg3051_w(offs_t offset, uint16_t data)
{
	if (data & 0x8000)
		m_maincpu->set_input_line(UNSP_IRQ0_LINE, CLEAR_LINE);

	if (data & 0x2000)
		m_maincpu->set_input_line(UNSP_IRQ2_LINE, CLEAR_LINE);

	if (data & 0x0100)
		m_maincpu->set_input_line(UNSP_IRQ3_LINE, CLEAR_LINE);


	//logerror("%s: reg3051_w %04x (IRQ Ack?)\n", machine().describe_context(), data);
}

uint16_t generalplus_gpl_unknown_state::reg3090_r(offs_t offset)
{
	return 0x0000;//machine().rand();
}

uint16_t generalplus_gpl_unknown_state::reg3091_r(offs_t offset)
{
	return 0x0000;//machine().rand();
}

void generalplus_gpl_unknown_state::reg3092_lcd_w(offs_t offset, uint16_t data)
{
	if (m_3005 & 0x1000)
	{
		//if (m_3005 & 0x0800) // also always set for video writes?
		{
			//logerror("%s: reg3092_lcd_w %04x (Video?)\n", machine().describe_context(), data);
			if ((m_displayposx < 256) && (m_displayposy < 256))
				m_display[(m_displayposy * 256) + m_displayposx] = data;

			if (data & 0xff00)
				fatalerror("upper data bits set?\n");

			m_displayposx++;

			/*
			if (m_displayposx == 256)
			{
			    m_displayposy++;
			    m_displayposx = 0;
			}
			*/
			/*
			if (m_displayposy == 128)
			{
			    m_displayposy = 0;
			    m_displayposx = 0;
			}
			*/
		}
	}
}

uint16_t generalplus_gpl_unknown_state::reg3094_r(offs_t offset)
{
	return 0x0000;
}

uint16_t generalplus_gpl_unknown_state::reg3095_r(offs_t offset)
{
	// loops on bit 0x0010 after writing data to 0x3092

	// clrb [3005],12 is used before writing non-pixel data to 0x3092
	// setb [3005],12 is used after reading from 0x3094
	return 0x0000;
}


void generalplus_gpl_unknown_state::map(address_map &map)
{
	map(0x000000, 0x000fff).ram(); // RAM
	//map(0x001000, 0x0017ff).ram(); // acts like open bus?

	//map(0x003000, 0x003fff).ram(); // system regs
	map(0x003001, 0x003001).rw(FUNC(generalplus_gpl_unknown_state::reg3001_r), FUNC(generalplus_gpl_unknown_state::reg3001_w));
	map(0x003002, 0x003002).rw(FUNC(generalplus_gpl_unknown_state::reg3002_r), FUNC(generalplus_gpl_unknown_state::reg3002_w));
	map(0x003003, 0x003003).rw(FUNC(generalplus_gpl_unknown_state::reg3003_r), FUNC(generalplus_gpl_unknown_state::reg3003_w));
	map(0x003004, 0x003004).r(FUNC(generalplus_gpl_unknown_state::reg3004_r));  // input from here is acted upon
	map(0x003005, 0x003005).rw(FUNC(generalplus_gpl_unknown_state::reg3005_r), FUNC(generalplus_gpl_unknown_state::reg3005_w));
	map(0x003006, 0x003006).r(FUNC(generalplus_gpl_unknown_state::reg3006_r));
	map(0x003007, 0x003007).r(FUNC(generalplus_gpl_unknown_state::reg3007_r));

	map(0x00300f, 0x00300f).r(FUNC(generalplus_gpl_unknown_state::reg300f_r));

	map(0x003016, 0x003016).r(FUNC(generalplus_gpl_unknown_state::reg3016_r));

	map(0x003034, 0x003034).w(FUNC(generalplus_gpl_unknown_state::reg3034_w));

	map(0x003041, 0x003041).w(FUNC(generalplus_gpl_unknown_state::reg3041_audiodac_w));

	map(0x003050, 0x003050).rw(FUNC(generalplus_gpl_unknown_state::reg3050_r), FUNC(generalplus_gpl_unknown_state::reg3050_w));
	map(0x003051, 0x003051).w(FUNC(generalplus_gpl_unknown_state::reg3051_w));
	map(0x003052, 0x003052).r(FUNC(generalplus_gpl_unknown_state::reg3052_r));
	map(0x003053, 0x003053).r(FUNC(generalplus_gpl_unknown_state::reg3053_r));

	map(0x003090, 0x003090).r(FUNC(generalplus_gpl_unknown_state::reg3090_r));
	map(0x003091, 0x003091).r(FUNC(generalplus_gpl_unknown_state::reg3091_r));
	map(0x003092, 0x003092).w(FUNC(generalplus_gpl_unknown_state::reg3092_lcd_w));
	map(0x003094, 0x003094).r(FUNC(generalplus_gpl_unknown_state::reg3094_r));  // potential interesting
	map(0x003095, 0x003095).r(FUNC(generalplus_gpl_unknown_state::reg3095_r));  // mostly a status flag

	map(0x0030e0, 0x0030e0).w(FUNC(generalplus_gpl_unknown_state::reg30e0_w));
	map(0x0030e1, 0x0030e1).w(FUNC(generalplus_gpl_unknown_state::reg30e1_w));
	map(0x0030e2, 0x0030e2).w(FUNC(generalplus_gpl_unknown_state::reg30e2_w));
	map(0x0030e3, 0x0030e3).w(FUNC(generalplus_gpl_unknown_state::reg30e3_w));
	map(0x0030e4, 0x0030e4).r(FUNC(generalplus_gpl_unknown_state::reg30e4_r));  // potentially interesting (must not return 0x0000 or 'flash error')
	map(0x0030e5, 0x0030e5).r(FUNC(generalplus_gpl_unknown_state::reg30e5_r));  // potentially interesting (also status flag)

	map(0x004000, 0x00bfff).rom().region("maincpu", 0x0000);
	map(0x00c000, 0x00ffff).rom().region("maincpu", 0x0000);

	map(0x200000, 0x3fffff).rom().region("spi", 0x0000); // has direct access to SPI ROM
}


void generalplus_gpl_unknown_state::machine_start()
{
	for (int color = 0; color < 0x10000; color++)
	{
		const u8 r = pal5bit(color >> 11);
		const u8 g = pal6bit(color >> 5);
		const u8 b = pal5bit(color & 0x1f);
		m_palette->set_pen_color(color, rgb_t(r, g, b));
	}

	save_item(NAME(m_display));
	save_item(NAME(m_displayposx));
	save_item(NAME(m_displayposy));
	save_item(NAME(m_3001));
	save_item(NAME(m_3003));
	save_item(NAME(m_3005));
	save_item(NAME(m_3050));
}

void generalplus_gpl_unknown_state::machine_reset()
{
	m_displayposx = 0;
	m_displayposy = 0;
	m_3001 = 0;
	m_3003 = 0;
	m_3005 = 0;
	m_3050 = 0;
}

TIMER_DEVICE_CALLBACK_MEMBER( generalplus_gpl_unknown_state::timer )
{
	m_maincpu->set_input_line(UNSP_IRQ3_LINE, ASSERT_LINE);
}

TIMER_DEVICE_CALLBACK_MEMBER( generalplus_gpl_unknown_state::timer2 )
{
	m_maincpu->set_input_line(UNSP_IRQ2_LINE, ASSERT_LINE);
}

TIMER_DEVICE_CALLBACK_MEMBER( generalplus_gpl_unknown_state::timer3 )
{
	m_maincpu->set_input_line(UNSP_IRQ0_LINE, ASSERT_LINE);
}



void generalplus_gpl_unknown_state::generalplus_gpl_unknown(machine_config &config)
{
	UNSP_20(config, m_maincpu, 96000000); // internal ROM uses unsp2.0 opcodes, unknown clock
	m_maincpu->set_addrmap(AS_PROGRAM, &generalplus_gpl_unknown_state::map);
	m_maincpu->set_vectorbase(0x4010); // there is also a set of vectors for what looks to be a burn-in test at 4000, maybe external pin selects?

	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(10));
	m_screen->set_size(128, 128);
	m_screen->set_visarea(0, 128-1, 0, 128-1); // not the correct resolution
	m_screen->set_screen_update(FUNC(generalplus_gpl_unknown_state::screen_update));
	//m_screen->screen_vblank().set(FUNC(generalplus_gpl_unknown_state::screen_vblank));
	m_screen->set_palette(m_palette);

	SPEAKER(config, "speaker").front_center();
	DAC_16BIT_R2R_TWOS_COMPLEMENT(config, "dac", 0).add_route(ALL_OUTPUTS, "speaker", 1.0); // unknown DAC

	TIMER(config, "timer").configure_periodic(FUNC(generalplus_gpl_unknown_state::timer), attotime::from_hz(200000)); // draw timer (pushes pixels to the display in the IRQ)
	TIMER(config, "timer2").configure_periodic(FUNC(generalplus_gpl_unknown_state::timer2), attotime::from_hz(1000)); // game speed?
	TIMER(config, "timer3").configure_periodic(FUNC(generalplus_gpl_unknown_state::timer3), attotime::from_hz(20000)); // audio

	PALETTE(config, m_palette).set_format(palette_device::xBGR_555, 0x10000);
}

void generalplus_gpl_unknown_state::init_siddr()
{
	uint8_t* spirom8 = (uint8_t*)memregion("spi")->base();
	for (int i = 0x3000; i < 0x400000; i++)
	{
		spirom8[i] = bitswap<8>(spirom8[i] ^ 0x68,
			3, 5, 0, 7,    1, 4, 2, 6);
	}
}


ROM_START( mapacman ) // this is the single game (no games hidden behind solder pads) release
	ROM_REGION16_BE( 0x18000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "mapacman_internal.rom", 0x000000, 0x10000, CRC(9ea69d2a) SHA1(17f5001794f4454bf5856cfa170834509d68bed0) )

	ROM_REGION16_BE( 0x800000, "spi", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "fm25q16a.bin", 0x000000, 0x200000, CRC(aeb472ac) SHA1(500c24b725f6d3308ef8cbdf4259f5be556c7c92) )
ROM_END

ROM_START( taspinv )
	ROM_REGION16_BE( 0x18000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x000000, 0x18000, NO_DUMP )

	ROM_REGION16_BE( 0x800000, "spi", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "tinyarcade_spaceinvaders.bin", 0x000000, 0x200000, CRC(11ac4c77) SHA1(398d5eff83a4e94487ed810819085a0e44582908) )
ROM_END

ROM_START( tagalaga )
	ROM_REGION16_BE( 0x18000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x000000, 0x18000, NO_DUMP )

	ROM_REGION16_BE( 0x800000, "spi", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "tinyarcadegalaga_fm25q16a_a14015.bin", 0x000000, 0x200000, CRC(2a91460c) SHA1(ce297642d2d51ce568e93c0c57432446633b2077) )
ROM_END

ROM_START( parcade )
	ROM_REGION16_BE( 0x18000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x000000, 0x18000, NO_DUMP )

	ROM_REGION16_BE( 0x800000, "spi", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "palacearcade_gpr25l3203_c22016.bin", 0x000000, 0x400000, CRC(98fbd2a1) SHA1(ffc19aadd53ead1f9f3472475606941055ca09f9) )
ROM_END

ROM_START( taturtf )
	ROM_REGION16_BE( 0x18000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x000000, 0x18000, NO_DUMP )

	ROM_REGION16_BE( 0x800000, "spi", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "tinyarcadeturtlefighter_25q32bst16_684016.bin", 0x000000, 0x400000, CRC(8e046f2d) SHA1(e48492cf953f22a47fa2b88a8f96a1e459b8c487) )
ROM_END

ROM_START( siddr )
	ROM_REGION16_BE( 0x40000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION16_BE(0x800000, "spi", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "ddr-toy.bin", 0x0000, 0x400000, CRC(873cbcc8) SHA1(bdd3d12adb1284991a3f8aaa8e451e3a55931267) )
ROM_END


} // anonymous namespace


// The 'Micro Arcade' units are credit card sized handheld devices
CONS( 2017, mapacman,      0,       0,      generalplus_gpl_unknown,   generalplus_gpl_unknown, generalplus_gpl_unknown_state, empty_init, "Super Impulse", "Pac-Man (Micro Arcade)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )

// The 'Tiny Arcade' units are arcade cabinets on a keyring.
CONS( 2017, taspinv,       0,       0,      generalplus_gpl_unknown,   generalplus_gpl_unknown, generalplus_gpl_unknown_state, empty_init, "Super Impulse", "Space Invaders (Tiny Arcade)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )

CONS( 2017, tagalaga,      0,       0,      generalplus_gpl_unknown,   generalplus_gpl_unknown, generalplus_gpl_unknown_state, empty_init, "Super Impulse", "Galaga (Tiny Arcade)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )

CONS( 2017, parcade,       0,       0,      generalplus_gpl_unknown,   generalplus_gpl_unknown, generalplus_gpl_unknown_state, empty_init, "Hasbro", "Palace Arcade (Tiny Arcade)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )

CONS( 2019, taturtf,       0,       0,      generalplus_gpl_unknown,   generalplus_gpl_unknown, generalplus_gpl_unknown_state, empty_init, "Super Impulse", "Teenage Mutant Ninja Turtles - Turtle Fighter (Tiny Arcade)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )

// Probably not identical hardware, but still not direct mapped SPI.  External ROM after 0x3000 is encrypted (maybe decrypted in software) seems to have jumps to internal ROM
CONS( 2021, siddr,         0,       0,      generalplus_gpl_unknown,   generalplus_gpl_unknown, generalplus_gpl_unknown_state, init_siddr, "Super Impulse", "Dance Dance Revolution - Broadwalk Arcade", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )




generalplus_gpl16250_mobigo.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

/*
    GPL16250 / GPAC800 / GMC384 / GCM420 related support

    GPL16250 is the GeneralPlus / SunPlus part number
    GPAC800 is the JAKKS Pacific codename
    GMC384 / GCM420 is what is printed on the die

    ----

    GPL16250 Mobigo support
    the original Mobigo is ROM+RAM config
    the Mobigo 2 is NAND+RAM config
    cartridges are compatible
*/

#include "emu.h"
#include "generalplus_gpl16250_nand.h"
#include "generalplus_gpl16250_romram.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "softlist_dev.h"
#include "screen.h"


namespace {

class mobigo2_state : public generalplus_gpac800_game_state
{
public:
	mobigo2_state(const machine_config &mconfig, device_type type, const char *tag) :
		generalplus_gpac800_game_state(mconfig, type, tag),
		m_cart(*this, "cartslot")
	{ }

	void mobigo2(machine_config &config);

protected:

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
	required_device<generic_slot_device> m_cart;
};

class mobigo_state : public wrlshunt_game_state
{
public:
	mobigo_state(const machine_config &mconfig, device_type type, const char *tag) :
		wrlshunt_game_state(mconfig, type, tag),
		m_cart(*this, "cartslot")
	{ }

	void mobigo(machine_config &config);
	void init_mobigo();

protected:

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
	required_device<generic_slot_device> m_cart;
};


static INPUT_PORTS_START( mobigo )
	PORT_START("IN0")
	PORT_START("IN1")
	PORT_START("IN2")
INPUT_PORTS_END



DEVICE_IMAGE_LOAD_MEMBER(mobigo2_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");
	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");
	return std::make_pair(std::error_condition(), std::string());
}

DEVICE_IMAGE_LOAD_MEMBER(mobigo_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");
	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");
	return std::make_pair(std::error_condition(), std::string());
}


void mobigo_state::mobigo(machine_config &config)
{
	gcm394_game_state::base(config);

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "mobigo_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(mobigo_state::cart_load));
	//m_cart->set_must_be_loaded(true);

	SOFTWARE_LIST(config, "cart_list").set_original("mobigo_cart");
}

void mobigo2_state::mobigo2(machine_config &config)
{
	GPAC800(config, m_maincpu, 96000000/2, m_screen);  // Doesn't have GPnandnand header in NAND tho, so non-standard bootloader
	m_maincpu->porta_in().set(FUNC(mobigo2_state::porta_r));
	m_maincpu->portb_in().set(FUNC(mobigo2_state::portb_r));
	m_maincpu->portc_in().set(FUNC(mobigo2_state::portc_r));
	m_maincpu->porta_out().set(FUNC(mobigo2_state::porta_w));
	m_maincpu->space_read_callback().set(FUNC(mobigo2_state::read_external_space));
	m_maincpu->space_write_callback().set(FUNC(mobigo2_state::write_external_space));
	m_maincpu->set_irq_acknowledge_callback(m_maincpu, FUNC(sunplus_gcm394_base_device::irq_vector_cb));
	m_maincpu->add_route(ALL_OUTPUTS, "speaker", 0.5, 0);
	m_maincpu->add_route(ALL_OUTPUTS, "speaker", 0.5, 1);
	m_maincpu->set_bootmode(0); // boot from internal ROM (NAND bootstrap)
	m_maincpu->set_cs_config_callback(FUNC(mobigo2_state::cs_callback));

	m_maincpu->nand_read_callback().set(FUNC(mobigo2_state::read_nand));

	FULL_MEMORY(config, m_memory).set_map(&mobigo2_state::cs_map_base);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320*2, 262*2);
	m_screen->set_visarea(0, (320*2)-1, 0, (240*2)-1);
	m_screen->set_screen_update("maincpu", FUNC(sunplus_gcm394_device::screen_update));
	m_screen->screen_vblank().set(m_maincpu, FUNC(sunplus_gcm394_device::vblank));

	SPEAKER(config, "speaker", 2).front();

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "mobigo_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(mobigo2_state::cart_load));
	//m_cart->set_must_be_loaded(true);

	SOFTWARE_LIST(config, "cart_list").set_original("mobigo_cart");
}

void mobigo_state::init_mobigo()
{
	m_sdram.resize(0x400000); // 0x400000 bytes, 0x800000 words (needs verifying)
}

ROM_START( mobigo )
	//ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 ) // not on this model? (or at least not this size, as CS base is different)
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("mobigo.bin", 0x0000, 0x800000, CRC(49479bad) SHA1(4ee82c7ba13072cf25a34893cf6272f2da5d8928) )
ROM_END

ROM_START( mobigos )
	//ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 ) // not on this model? (or at least not this size, as CS base is different)
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("mobigospanish.bin", 0x0000, 0x200000, CRC(462b4f9d) SHA1(1541152f1a359bc18de4d4f3d5038a954c9a3ad4))
ROM_END


ROM_START( mobigo2 )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // doesn't have GPnandnand header in NAND, so bootstrap is likely custom

	ROM_REGION( 0x8400000, "nandrom", ROMREGION_ERASE00 )
	ROM_LOAD( "mobigo2_bios_ger.bin", 0x00000, 0x8400000, CRC(d5ab613d) SHA1(6fb104057dc3484fa958e2cb20c5dd0c19589f75) ) // SPANSION S34ML01G100TF100
ROM_END

} // anonymous namespace


CONS( 2010, mobigo,  0,      0, mobigo,   mobigo, mobigo_state,  init_mobigo , "VTech", "MobiGo", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2010, mobigos, mobigo, 0, mobigo,   mobigo, mobigo_state,  init_mobigo , "VTech", "MobiGo (Spain)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2012, mobigo2, 0,      0, mobigo2,  mobigo, mobigo2_state, nand_init840, "VTech", "MobiGo 2 (Germany)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



generalplus_gpl16250_nand.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/*
    GPL16250 / GPAC800 / GMC384 / GCM420 related support

    GPL16250 is the GeneralPlus / SunPlus part number
    GPAC800 is the JAKKS Pacific codename
    GMC384 / GCM420 is what is printed on the die

    ----

    GPL16250 games using NAND + RAM configuration
*/

/*
    map info (NAND type)

    map(0x000000, 0x006fff) internal RAM
    map(0x007000, 0x007fff) internal peripherals
    map(0x008000, 0x00ffff) internal ROM (lower 32kwords) - can also be configured to mirror CS0 308000 area with external pin for boot from external ROM
    map(0x010000, 0x027fff) internal ROM (upper 96kwords) - can't be switched
    map(0x028000, 0x02ffff) reserved

    map(0x030000, 0x0.....) view into external spaces (CS0 area starts here. followed by CS1 area, CS2 area etc.)

    map(0x200000, 0x3fffff) continued view into external spaces, but this area is banked with m_membankswitch_7810 (valid bank values 0x00-0x3f)
*/

#include "emu.h"
#include "generalplus_gpl16250.h"
#include "generalplus_gpl16250_nand.h"
#include "softlist_dev.h"

uint16_t generalplus_gpac800_game_state::cs0_r(offs_t offset)
{
	return m_sdram2[offset & 0xffff];
}

void generalplus_gpac800_game_state::cs0_w(offs_t offset, uint16_t data)
{
	m_sdram2[offset & 0xffff] = data;
}

uint16_t generalplus_gpac800_game_state::cs1_r(offs_t offset)
{
	return m_sdram[offset & (m_sdram_kwords-1)];
}

void generalplus_gpac800_game_state::cs1_w(offs_t offset, uint16_t data)
{
	m_sdram[offset & (m_sdram_kwords-1)] = data;
}

uint8_t generalplus_gpac800_game_state::read_nand(offs_t offset)
{
	if (!m_nandregion)
		return 0x0000;

	if (offset < m_size)
	{
		return m_nandregion[offset];
	}
	else
	{
		popmessage("read outside of NAND ROM space (offset %08x) (size %08x)\n", offset, m_size);
		return 0xff;
	}

	return 0x00;
}
void generalplus_gpac800_game_state::dma_complete_hacks(int state)
{
	// HACKS to get into service mode for debugging (needed for testing as many of these require input sequences on the not yet emulated custom controls)

	// note, these patch the code copied to SRAM so the 'PROGRAM ROM' check fails (it passes otherwise)

	address_space& mem = m_maincpu->space(AS_PROGRAM);

	//if (mem.read_word(0x4368c) == 0x4846)
	//  mem.write_word(0x4368c, 0x4840);    // cars 2 force service mode

	//if (mem.read_word(0x34410) == 0x4846)
	//  mem.write_word(0x34410, 0x4840);    // golden tee force service mode

	// what is it waiting for when we need these? (needed on some service mode screens)
	//if (mem.read_word(0x3f368) == 0x4840)
	//  mem.write_word(0x3f368, 0x4841);    // cars 2 IRQ? wait hack

	//if (mem.read_word(0x4d8d4) == 0x4840)
	//  mem.write_word(0x4d8d4, 0x4841);    // golden tee IRQ? wait hack

	//if (mem.read_word(0x3510f) == 0x4845)
	//  mem.write_word(0x3510f, 0x4840);    // camp rock force service mode

	if (mem.read_word(0x4abe7) == 0x4840)
		mem.write_word(0x4abe7, 0x4841);    // camp rock IRQ? wait hack

	//if (mem.read_word(0x37244) == 0x4845)
	//  mem.write_word(0x37244, 0x4840);    // hannah montana guitar force service mode
}

void generalplus_gpac800_game_state::generalplus_gpac800(machine_config &config)
{
	GPAC800(config, m_maincpu, 96000000/2, m_screen);
	m_maincpu->porta_in().set(FUNC(generalplus_gpac800_game_state::porta_r));
	m_maincpu->portb_in().set(FUNC(generalplus_gpac800_game_state::portb_r));
	m_maincpu->portc_in().set(FUNC(generalplus_gpac800_game_state::portc_r));
	m_maincpu->porta_out().set(FUNC(generalplus_gpac800_game_state::porta_w));
	m_maincpu->space_read_callback().set(FUNC(generalplus_gpac800_game_state::read_external_space));
	m_maincpu->space_write_callback().set(FUNC(generalplus_gpac800_game_state::write_external_space));
	m_maincpu->set_irq_acknowledge_callback(m_maincpu, FUNC(sunplus_gcm394_base_device::irq_vector_cb));
	m_maincpu->add_route(ALL_OUTPUTS, "speaker", 0.5, 0);
	m_maincpu->add_route(ALL_OUTPUTS, "speaker", 0.5, 1);
	m_maincpu->set_bootmode(0); // boot from internal ROM (NAND bootstrap)
	m_maincpu->set_cs_config_callback(FUNC(gcm394_game_state::cs_callback));
	m_maincpu->dma_complete_callback().set(FUNC(generalplus_gpac800_game_state::dma_complete_hacks));

	m_maincpu->nand_read_callback().set(FUNC(generalplus_gpac800_game_state::read_nand));

	FULL_MEMORY(config, m_memory).set_map(&generalplus_gpac800_game_state::cs_map_base);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320*2, 262*2);
	m_screen->set_visarea(0, (320*2)-1, 0, (240*2)-1);
	m_screen->set_screen_update("maincpu", FUNC(sunplus_gcm394_device::screen_update));
	m_screen->screen_vblank().set(m_maincpu, FUNC(sunplus_gcm394_device::vblank));

	SPEAKER(config, "speaker", 2).front();
}

DEVICE_IMAGE_LOAD_MEMBER(generalplus_gpac800_vbaby_game_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

void generalplus_gpac800_vbaby_game_state::generalplus_gpac800_vbaby(machine_config &config)
{
	generalplus_gpac800_game_state::generalplus_gpac800(config);

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "vbaby_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(generalplus_gpac800_vbaby_game_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("vbaby_cart");
}

static INPUT_PORTS_START( jak_car2 )
	PORT_START("IN0")
	PORT_DIPNAME( 0x0001, 0x0001, "IN0" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON3 ) // unused
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("IN1")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN2")
	PORT_DIPNAME( 0x0001, 0x0001, "IN2" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( jak_gtg )
	PORT_START("IN0")
	PORT_DIPNAME( 0x0001, 0x0001, "IN0" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("IN1")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN2")
	PORT_DIPNAME( 0x0001, 0x0001, "IN2" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
INPUT_PORTS_END




static INPUT_PORTS_START( jak_hsm )
	PORT_START("IN0")
	PORT_DIPNAME( 0x0001, 0x0001, "IN0" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON4 )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("IN1")
	PORT_DIPNAME( 0x0001, 0x0001, "IN1" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("IN2")
	PORT_DIPNAME( 0x0001, 0x0001, "IN2" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_BUTTON3 )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
INPUT_PORTS_END



ROM_START( wlsair60 )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only

	ROM_REGION( 0x8400000, "nandrom", ROMREGION_ERASE00 )
	ROM_LOAD( "wlsair60.nand", 0x0000, 0x8400000, CRC(eec23b97) SHA1(1bb88290cf54579a5bb51c08a02d793cd4d79f7a) )
ROM_END

ROM_START( kiugames )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only

	ROM_REGION( 0x21000000, "nandrom", ROMREGION_ERASE00 )
	ROM_LOAD( "hy27084g2m.u2", 0x0000, 0x21000000, CRC(65cc3864) SHA1(b759ec9816fe98a33ee7d5e12e5492f0160c5b31) )
ROM_END


ROM_START( jak_gtg )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only

	ROM_REGION( 0x4200000, "nandrom", ROMREGION_ERASE00 )
	ROM_LOAD( "goldentee.bin", 0x0000, 0x4200000, CRC(87d5e815) SHA1(5dc46cd753b791449cc41d5eff4928c0dcaf35c0) )
ROM_END

ROM_START( jak_car2 )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only

	ROM_REGION( 0x4200000, "nandrom", ROMREGION_ERASE00 )
	ROM_LOAD( "cars2.bin", 0x0000, 0x4200000, CRC(4d610e09) SHA1(bc59f5f7f676a8f2a78dfda7fb62c804bbf850b6) )
ROM_END


/*  The following pinout was used when dumping jak_sspop, jak_hmhsm, jak_umdf
    For the 256Mbyte parts the parameters of the programmer had to be overridden to dump the full capacity as there were no equivalent parts.

       Sandisk TSOP32 NAND Flash

       +----------------------------------------------+
    NC-|01                                          32|-NC
   VSS-|02                                          31|-NC
   R/B-|03                  SanDisk                 30|-I/O7
    NC-|04                   NAND                   29|-I/O6
    RE-|05                                          28|-I/O5
    CE-|06                   32PIN                  27|-I/O4
    NC-|07                                          26|-VCC
   VCC-|08                                          25|-VSS
   VSS-|09                                          24|-NC
    NC-|10                                          23|-I/O3
    NC-|11                                          22|-I/O2
   CLE-|12                                          21|-I/O1
   ALE-|13                                          20|-I/O0
    WE-|14                                          19|-NC
    WP-|15                                          18|-NC
    NC-|16                                          17|-NC
       +----------------------------------------------+

One of the games has pin 2 grounded, and the other 2 have it N/C.  I'm not sure what it would be, since all the signals are accounted for.

*/

ROM_START( jak_sspop )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only

	/* TSOP32 NAND ROM

	S976172-1
	SanDisk
	11015-128B
	POC142
	0845
	<obscured #>

	appears to be a 128MByte part (or at least that is how much service mode tests)

	*/

	ROM_REGION( 0x8400000, "nandrom", ROMREGION_ERASE00 )
	ROM_LOAD( "singscenepop_as_hy27us081g1m_4579.bin", 0x0000, 0x8400000, CRC(4c8123fe) SHA1(388fda8ddd90b541a53eac4bcbe66bebe7360724) )
ROM_END

ROM_START( jak_hmhsm )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only

	/* TSOP32 NAND ROM

	5769522.1
	SanDisk
	11354-256B
	P44247.00
	xx20
	01xxxx_HSM

	256Mbyte part, 2nd half is just 0xff filled tho so a 128Mbyte part would have been fine

	*/

	ROM_REGION( 0x10800000, "nandrom", ROMREGION_ERASE00 )
	ROM_LOAD( "hmhsm.bin", 0x0000, 0x10800000, CRC(e63ad24c) SHA1(a7844b14af701914150aa7c06743a410f478ff7b) )
ROM_END

ROM_START( jak_camp )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only

	ROM_REGION( 0x10800000, "nandrom", ROMREGION_ERASE00 )
	ROM_LOAD( "camprockguitar1_sandisk11352-256b_45da.bin", 0x0000, 0x10800000, CRC(f52a4289) SHA1(d027ae274cd4ac97924d2344df1a96456e8e7c55) )
ROM_END

ROM_START( jak_hmpt )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only

	ROM_REGION( 0x10800000, "nandrom", ROMREGION_ERASE00 )
	ROM_LOAD( "hmsecretstar_sandisk11270_9876.bin", 0x0000, 0x10800000, CRC(fbe09633) SHA1(169a1546072f53c2da19ce97396cacd25412c5f2) )
ROM_END

ROM_START( jak_hsmg2 )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only

	ROM_REGION( 0x4200000, "nandrom", ROMREGION_ERASE00 )
	ROM_LOAD( "hsm_as_hy27ys08121a_9876.bin", 0x0000, 0x4200000, CRC(4da61056) SHA1(d6c529a6df2703dd55b864e9d7c655203206f8b6) )
ROM_END

ROM_START( jak_hmg2 )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only

	ROM_REGION( 0x4200000, "nandrom", ROMREGION_ERASE00 )
	ROM_LOAD( "hm_as_hy27us08121a_9876_fixed.bin", 0x0000, 0x4200000, BAD_DUMP CRC(ba97fcd6) SHA1(c02a6878910b1312009b21220d51d7c1c3adb767) ) // 4 blocks had to be fixed using data from jak_hmhsm
ROM_END


ROM_START( jak_umdf )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only

	/* TSOP32 NAND ROM

	S744565-1
	SanDisk
	11352-256B
	PA2777.00
	0834
	61050

	again part number would suggest that this is a 256MByte ROM, although in reality all data fits into 64Mbyte, rest is blank

	*/

	ROM_REGION( 0x10800000, "nandrom", ROMREGION_ERASE00 )
	ROM_LOAD( "jak_umdf.bin", 0x0000, 0x10800000, CRC(05f47aca) SHA1(61b417141ccc22324224b1862ea2f5778453f206) )
ROM_END


ROM_START( jak_tsm )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only

	ROM_REGION( 0x4200000, "nandrom", ROMREGION_ERASE00 )
	ROM_LOAD( "toystorymania.bin", 0x0000, 0x4200000, CRC(183b20a5) SHA1(eb4fa5ee9dfac58f5244d00d4e833b1e461cc52c) )
ROM_END


ROM_START( jak_duck )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only

	ROM_REGION( 0x4200000, "nandrom", ROMREGION_ERASE00 )
	ROM_LOAD( "duckcommander_gpr27p512a_c276_as_hy27us08121a.bin", 0x0000, 0x4200000, CRC(d9356d5b) SHA1(aca05525b4a504f7ad264ae9bbc2f1f8f399c4ca) )
ROM_END

ROM_START( jak_swc )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only

	ROM_REGION( 0x4200000, "nandrom", ROMREGION_ERASE00 )
	ROM_LOAD( "jakksstarwarspistol_gpr27p512a_c276_as_hy27us08121a.bin", 0x0000, 0x4200000, CRC(024d49b8) SHA1(9694f4c7cd083c976ffbbcfa6f626fc6b4bc8d91) )
ROM_END

ROM_START( jak_wdzh )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only

	ROM_REGION( 0x4200000, "nandrom", ROMREGION_ERASE00 )
	ROM_LOAD( "walkingdeadrifle_gpr27p512a_c276_as_hy27us08121a.bin", 0x0000, 0x4200000, CRC(b2c762f0) SHA1(7e10df517cc24924e0ec55e2a263563023d945f8) )
ROM_END

ROM_START( jak_wdbg )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only

	ROM_REGION( 0x4200000, "nandrom", ROMREGION_ERASE00 )
	ROM_LOAD( "amcwalkingdeadcrossbow_gpr27p512a_c276_as_hy27us08121a.bin", 0x0000, 0x4200000, CRC(66510fd4) SHA1(3ad6347c5a7758c035654cb3e96858320875b97a) )
ROM_END


ROM_START( vbaby )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only

	ROM_REGION( 0x8400000, "nandrom", ROMREGION_ERASE00 )
	ROM_LOAD( "vbaby.bin", 0x0000, 0x8400000, CRC(d904441b) SHA1(3742bc4e1e403f061ce2813ecfafc6f30a44d287) )
ROM_END

ROM_START( mgtfit )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only

	ROM_REGION( 0x8400000, "nandrom", ROMREGION_ERASE00 ) // Samsung 937 K9F1G08U0D  Ident: 0xEC 0xF1 Full Ident: 0xECF1001540
	ROM_LOAD( "k9f1g08u0d.bin", 0x0000, 0x8400000, CRC(1ca5ac09) SHA1(c2e123085d2198999c2c0edb1df4895361c00a99) )
ROM_END

ROM_START( beambox )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only

	ROM_REGION( 0x4200000, "nandrom", ROMREGION_ERASE00 )
	ROM_LOAD( "beambox.bin", 0x0000, 0x4200000, CRC(a486f04e) SHA1(73c7d99d8922eba58d94e955e254b9c3baa4443e) )
ROM_END


void generalplus_gpac800_game_state::machine_start()
{
	save_item(NAME(m_sdram));
}

void generalplus_gpac800_game_state::nand_create_stripped_region()
{
	uint8_t* rom = m_nandregion;
	int size = memregion("nandrom")->bytes();
	m_size = size;

	int numblocks = size / m_nandblocksize;
	m_strippedsize = numblocks * m_nandblocksize_stripped;
	m_strippedrom.resize(m_strippedsize);

	for (int i = 0; i < numblocks; i++)
	{
		const int base = i * m_nandblocksize;
		const int basestripped = i * m_nandblocksize_stripped;

		for (int j = 0; j < m_nandblocksize_stripped; j++)
		{
			m_strippedrom[basestripped + j] = rom[(base + j)];
		}
	}

	// debug to allow for easy use of unidasm.exe
	if (0)
	{
		auto filename = "stripped_" + std::string(machine().system().name);
		auto fp = fopen(filename.c_str(), "w+b");
		if (fp)
		{
			fwrite(&m_strippedrom[0], m_nandblocksize_stripped * numblocks, 1, fp);
			fclose(fp);
		}
	}
}

void generalplus_gpac800_game_state::machine_reset()
{
	// configure CS defaults
	address_space& mem = m_maincpu->space(AS_PROGRAM);
	mem.write_word(0x007820, 0x0047);
	mem.write_word(0x007821, 0xff47);
	mem.write_word(0x007822, 0x00c7);
	mem.write_word(0x007823, 0x0047);
	mem.write_word(0x007824, 0x0047);

	m_maincpu->set_cs_space(m_memory->get_program());

	if (m_nandregion)
	{
		nand_create_stripped_region();

		// up to 256 pages (16384kw) for each space

		// (size of cs0 + cs1 + cs2 + cs3 + cs4) <= 81920kwords

		// simulate bootstrap / internal ROM

		address_space& mem = m_maincpu->space(AS_PROGRAM);

		/* Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
		   00000000 (50 47 61 6E 64 6E 61 6E 64 6E)-- -- -- -- -- --  PGandnandn------
		   00000010  -- -- -- -- -- bb -- -- -- -- -- -- -- -- -- --  ----------------

		   bb = where to copy first block

		   The header is GPnandnand (byteswapped) then some params
		   one of the params appears to be for the initial code copy operation done
		   by the bootstrap
		*/

		// probably more bytes are used
		int dest = m_strippedrom[0x15] << 8 | (m_strippedrom[0x16] << 16);

		// copy a block of code from the NAND to RAM
		for (int i = 0; i < m_initial_copy_words; i++)
		{
			uint16_t word = m_strippedrom[(i * 2) + 0] | (m_strippedrom[(i * 2) + 1] << 8);

			mem.write_word(dest + i, word);
		}

		/* these vectors must either directly point to RAM, or at least redirect there after some code

		   kiugames has the startup code copied to 20xxx which is outside the scope of a 16-bit vector
		   so these must trampoline (although 20xxx currently isn't handled as RAM, so that needs more
		   thought anyway
		*/
		uint16_t* internal = (uint16_t*)memregion("maincpu:internal")->base();

		int addr;
		addr = (m_vectorbase + 0x0a) & 0x000fffff;
		internal[0x7f00] = 0xfe80 | (addr >> 16);
		internal[0x7f01] = (addr & 0xffff);

		addr = (m_vectorbase + 0x0c) & 0x000fffff;
		internal[0x7f02] = 0xfe80 | (addr >> 16);
		internal[0x7f03] = (addr & 0xffff);

		addr = (dest + 0x20) & 0x000fffff; // point boot vector at code in RAM (probably in reality points to internal code that copies the first block)
		internal[0x7f04] = 0xfe80 | (addr >> 16);
		internal[0x7f05] = (addr & 0xffff);

		addr = (m_vectorbase + 0x10) & 0x000fffff;
		internal[0x7f06] = 0xfe80 | (addr >> 16);
		internal[0x7f07] = (addr & 0xffff);

		addr = (m_vectorbase + 0x12) & 0x000fffff;
		internal[0x7f08] = 0xfe80 | (addr >> 16);
		internal[0x7f09] = (addr & 0xffff);

		addr = (m_vectorbase + 0x14) & 0x000fffff;
		internal[0x7f0a] = 0xfe80 | (addr >> 16);
		internal[0x7f0b] = (addr & 0xffff);

		addr = (m_vectorbase + 0x16) & 0x000fffff;
		internal[0x7f0c] = 0xfe80 | (addr >> 16);
		internal[0x7f0d] = (addr & 0xffff);

		addr = (m_vectorbase + 0x18) & 0x000fffff;
		internal[0x7f0e] = 0xfe80 | (addr >> 16);
		internal[0x7f0f] = (addr & 0xffff);

		addr = (m_vectorbase + 0x1a) & 0x000fffff;
		internal[0x7f10] = 0xfe80 | (addr >> 16);
		internal[0x7f11] = (addr & 0xffff);

		addr = (m_vectorbase + 0x1c) & 0x000fffff;
		internal[0x7f12] = 0xfe80 | (addr >> 16);
		internal[0x7f13] = (addr & 0xffff);

		addr = (m_vectorbase + 0x1e) & 0x000fffff;
		internal[0x7f14] = 0xfe80 | (addr >> 16);
		internal[0x7f15] = (addr & 0xffff);


		internal[0x7ff5] = 0xff00;

		internal[0x7ff6] = 0xff02;
		internal[0x7ff7] = 0xff04;
		internal[0x7ff8] = 0xff06;
		internal[0x7ff9] = 0xff08;
		internal[0x7ffa] = 0xff0a;
		internal[0x7ffb] = 0xff0c;
		internal[0x7ffc] = 0xff0e;
		internal[0x7ffd] = 0xff10;
		internal[0x7ffe] = 0xff12;
		internal[0x7fff] = 0xff14;
	}

	m_maincpu->reset(); // reset CPU so vector gets read etc.

	//m_maincpu->set_paldisplaybank_high_hack(0);
	m_maincpu->set_alt_tile_addressing_hack(1);
}


void generalplus_gpac800_game_state::nand_init210()
{
	m_sdram.resize(m_sdram_kwords);
	m_sdram2.resize(0x10000);

	m_nandblocksize = 0x210;
	m_nandblocksize_stripped = 0x200;

	m_vectorbase = 0x6fe0;
}

void generalplus_gpac800_game_state::nand_init210_32mb()
{
	m_sdram_kwords = 0x400000 * 4;
	nand_init210();
}

void generalplus_gpac800_game_state::nand_init840()
{
	m_sdram.resize(m_sdram_kwords);
	m_sdram2.resize(0x10000);

	m_nandblocksize = 0x840;
	m_nandblocksize_stripped = 0x800;

	m_vectorbase = 0x6fe0;
}

void generalplus_gpac800_game_state::nand_wlsair60()
{
	nand_init840();
	m_initial_copy_words = 0x2800;
}

void generalplus_gpac800_game_state::nand_kiugames()
{
	nand_init840();
	m_initial_copy_words = 0x10000;
}


void generalplus_gpac800_game_state::nand_vbaby()
{
	nand_init840();
	m_initial_copy_words = 0x1000;
	m_maincpu->set_romtype(2);
}

void generalplus_gpac800_game_state::nand_tsm()
{

	// something odd must be going on with the bootloader?
	// structure has the first 0x4000 block repeated 3 times (must appear in RAM on startup?)
	// then it has a 0x10000 block repeated 4 times (must get copied to 0x30000 by code)
	// then it has the larger, main payload, just the once.

	// the addresses written to the NAND device don't compensate for these data repeats, however dump seems ok as no other data is being repeated?
	// reads after startup still need checking
	nand_init210();
	m_maincpu->set_romtype(1);
}

void generalplus_gpac800_game_state::nand_beambox()
{
	nand_init210();
	m_vectorbase = 0x2fe0;
}

// NAND dumps w/ internal bootstrap (and u'nSP 2.0 extended opcodes)  (have gpnandnand strings)
// the JAKKS ones seem to be known as 'Generalplus GPAC800' hardware
CONS(2010, wlsair60,   0, 0, generalplus_gpac800,       jak_car2, generalplus_gpac800_game_state,       nand_wlsair60,      "Jungle Soft / Kids Station Toys Inc",      "Wireless Air 60",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING) // some of th games seem to be based on ones found in the 'Millennium Arcade' multigames (WinFun related) so might have the same external timer check
CONS(200?, beambox,    0, 0, generalplus_gpac800,       jak_car2, generalplus_gpac800_game_state,       nand_beambox,       "Hasbro",                                   "Playskool Heroes Transformers Rescue Bots Beam Box (Spain)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
CONS(200?, mgtfit,     0, 0, generalplus_gpac800,       jak_car2, generalplus_gpac800_game_state,       nand_wlsair60,      "MGT",                                      "Fitness Konsole (NC1470)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING) // probably has other names in English too? menus don't appear to be in German
CONS(200?, vbaby,      0, 0, generalplus_gpac800_vbaby, jak_car2, generalplus_gpac800_vbaby_game_state, nand_vbaby,         "VTech",                                    "V.Baby", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
CONS(200?, kiugames,   0, 0, generalplus_gpac800,       jak_car2, generalplus_gpac800_game_state,       nand_kiugames,      "VideoJet",                                 "Kiu Games",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING) // probably has other names in English too? menus don't appear to be in German

CONS(2011, jak_gtg,    0, 0, generalplus_gpac800,       jak_gtg,  generalplus_gpac800_game_state,       nand_init210,       "JAKKS Pacific Inc / HotGen Ltd",           "Golden Tee Golf (JAKKS Pacific TV Game)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
CONS(200?, jak_car2,   0, 0, generalplus_gpac800,       jak_car2, generalplus_gpac800_game_state,       nand_init210,       "JAKKS Pacific Inc / HotGen Ltd",           "Cars 2 (JAKKS Pacific TV Game)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
CONS(2010, jak_tsm,    0, 0, generalplus_gpac800,       jak_car2, generalplus_gpac800_game_state,       nand_tsm,           "JAKKS Pacific Inc / Schell Games",         "Toy Story Mania (JAKKS Pacific TV Game)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
CONS(2009, jak_sspop,  0, 0, generalplus_gpac800,       jak_hsm,  generalplus_gpac800_game_state,       nand_init210_32mb,  "JAKKS Pacific Inc / HotGen Ltd",           "Sing Scene Pop (JAKKS Pacific TV Game)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
CONS(2008, jak_hmg2,   0, 0, generalplus_gpac800,       jak_hsm,  generalplus_gpac800_game_state,       nand_init210_32mb,  "JAKKS Pacific Inc / HotGen Ltd",           "Hannah Montana G2 Deluxe - All in One (JAKKS Pacific TV Game)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING) // Jul 9 2008 11:50:08
CONS(2008, jak_hsmg2,  0, 0, generalplus_gpac800,       jak_hsm,  generalplus_gpac800_game_state,       nand_init210_32mb,  "JAKKS Pacific Inc / HotGen Ltd",           "High School Musical G2 Deluxe - All in One (JAKKS Pacific TV Game)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING) // Jun 25 2008 14:53:14
CONS(2008, jak_hmhsm,  0, 0, generalplus_gpac800,       jak_hsm,  generalplus_gpac800_game_state,       nand_init210_32mb,  "JAKKS Pacific Inc / HotGen Ltd",           "Hannah Montana G2 Deluxe / High School Musical G2 Deluxe - Two in One (JAKKS Pacific TV Game)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING) // Sep 12 2008 18:48:14 (Menu/HM) / Sep 12 2008 18:50:45 (HSM)
CONS(2008, jak_umdf,   0, 0, generalplus_gpac800,       jak_hsm,  generalplus_gpac800_game_state,       nand_init210_32mb,  "JAKKS Pacific Inc / Handheld Games",       "Ultimotion - Disney Fairies Sleeping Beauty & TinkerBell (JAKKS Pacific TV Game)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
// Ultimotion Swing Zone is SPG29xx instead
CONS(2008, jak_camp,   0, 0, generalplus_gpac800,       jak_hsm,  generalplus_gpac800_game_state,       nand_init210_32mb,  "JAKKS Pacific Inc / HotGen Ltd",           "Camp Rock - Guitar Video Game (JAKKS Pacific TV Game)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// 2 blocks fail the hidden ROM test in jak_hmpt set below, however this seems to be an error in the test mode, not the dump
// a different set, https://www.youtube.com/watch?v=XiEMtLzcTFw showing a date of May 14 2008 10:05:22 shows exactly the same failures
CONS(2008, jak_hmpt,   0, 0, generalplus_gpac800,       jak_hsm,  generalplus_gpac800_game_state,       nand_init210_32mb,  "JAKKS Pacific Inc / HotGen Ltd",           "Hannah Montana Pop Tour - Guitar Video Game (JAKKS Pacific TV Game) (May 16 2008)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING) // May 16 2008 10:36:59

// There were 1 player and 2 player versions for several of the JAKKS guns.  The 2nd gun appears to be simply a controller (no AV connectors) but as they were separate products with the 2 player versions being released up to a year after the original, the code could differ.
// If they differ, it is currently uncertain which versions these ROMs are from
CONS(2012, jak_wdzh,   0, 0, generalplus_gpac800,       jak_car2, generalplus_gpac800_game_state,       nand_init210,       "JAKKS Pacific Inc / Merge Interactive",    "The Walking Dead: Zombie Hunter (JAKKS Pacific TV Game)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING) // gun games all had Atmel 16CM (24C16).
CONS(2013, jak_duck,   0, 0, generalplus_gpac800,       jak_car2, generalplus_gpac800_game_state,       nand_init210_32mb,  "JAKKS Pacific Inc / Merge Interactive",    "Duck Commander (JAKKS Pacific TV Game)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING) // no 2 Player version was released
CONS(2013, jak_swc,    0, 0, generalplus_gpac800,       jak_car2, generalplus_gpac800_game_state,       nand_init210_32mb,  "JAKKS Pacific Inc / Merge Interactive",    "Star Wars Clone Trooper (JAKKS Pacific TV Game)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
CONS(2014, jak_wdbg,   0, 0, generalplus_gpac800,       jak_car2, generalplus_gpac800_game_state,       nand_init210_32mb,  "JAKKS Pacific Inc / Super Happy Fun Fun",  "The Walking Dead: Battleground (JAKKS Pacific TV Game)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



generalplus_gpl16250_rom.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/*
    GPL16250 / GPAC800 / GMC384 / GCM420 related support

    GPL16250 is the GeneralPlus / SunPlus part number
    GPAC800 is the JAKKS Pacific codename
    GMC384 / GCM420 is what is printed on the die

    ----

    GPL16250 games using ROM (no extra RAM) configuration
*/

#include "emu.h"
#include "generalplus_gpl16250.h"



static INPUT_PORTS_START( base )
	PORT_START("IN0")
	PORT_DIPNAME( 0x0001, 0x0001, "IN0" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("IN1")
	PORT_DIPNAME( 0x0001, 0x0001, "IN1" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("IN2")
	PORT_DIPNAME( 0x0001, 0x0001, "IN2" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( dressmtv )
	PORT_INCLUDE( base )

	PORT_MODIFY("IN0")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
INPUT_PORTS_END


static INPUT_PORTS_START( jak_spmm )
	PORT_INCLUDE( base )

	// are these inputs meant to be split across 3 ports, or is that a unSP2.0 / GPL16250 emulation bug?
	PORT_MODIFY("IN0")
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Pause/Menu")

	PORT_MODIFY("IN1")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON2 )

	PORT_MODIFY("IN2")
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_BUTTON3 )
INPUT_PORTS_END

static INPUT_PORTS_START( jak_bj )
	PORT_INCLUDE( base )

	PORT_MODIFY("IN0")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Hint Button") // test mode calls this B
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Menu") // test mode calls this C
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("A Button") // test mode calls this A
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON4 ) // test mode calls this menu (but that seems to be 0x0010 in reality? this is unused?)
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )

INPUT_PORTS_END



static INPUT_PORTS_START( smartfp )
	PORT_INCLUDE( base )

	PORT_MODIFY("IN0")
	// entirely non-standard mat based controller (0-11 are where your feet are placed normally, row of selection places to step above those)
	// no sensible default mapping unless forced
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_CODE(KEYCODE_Q) PORT_NAME("0")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_CODE(KEYCODE_W) PORT_NAME("1")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON7 ) PORT_CODE(KEYCODE_E) PORT_NAME("2")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON8 ) PORT_CODE(KEYCODE_R) PORT_NAME("3")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON9 ) PORT_CODE(KEYCODE_T) PORT_NAME("4")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON10 ) PORT_CODE(KEYCODE_Y) PORT_NAME("5")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON11 ) PORT_CODE(KEYCODE_U) PORT_NAME("6")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON12 ) PORT_CODE(KEYCODE_I) PORT_NAME("7")
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON13 ) PORT_CODE(KEYCODE_O) PORT_NAME("8")
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON14 ) PORT_CODE(KEYCODE_P) PORT_NAME("9")
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON15 ) PORT_CODE(KEYCODE_OPENBRACE) PORT_NAME("10")
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_BUTTON16 ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_NAME("11")

	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CODE(KEYCODE_A) PORT_NAME("Circle / Red")
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_CODE(KEYCODE_S) PORT_NAME("Square / Orange")
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_CODE(KEYCODE_D) PORT_NAME("Triangle / Yellow")
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_CODE(KEYCODE_F) PORT_NAME("Star / Blue")

	PORT_MODIFY("IN1")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_MODIFY("IN2")
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_SERVICE ) PORT_NAME("HOME")
INPUT_PORTS_END

static INPUT_PORTS_START( gormiti ) // DOWN with A+B+C for test mode?
	PORT_INCLUDE( base )

	PORT_MODIFY("IN0")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON3 )
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_START1 ) // causes long delay in test mode?
INPUT_PORTS_END

static INPUT_PORTS_START( tkmag220 )
	PORT_INCLUDE( base )

	PORT_MODIFY("IN1")
	PORT_DIPNAME( 0x0040, 0x0000, "Important" ) // gets stuck in inf loop if this is wrong
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_MODIFY("IN2")
	PORT_DIPNAME( 0x0001, 0x0001, "IN2" )   // set 0x0001 and 0x0002 on to get a test mode (some of the ROM banks fail their test, but dumps were repeatable, should be verified on another unit)
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Menu")
INPUT_PORTS_END

static INPUT_PORTS_START( gameu )
	PORT_START("IN0")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN1")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN2") // P2 inputs are listed in test mode, but unit has no 2nd set of controls
	PORT_BIT( 0x001f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON3 )
	PORT_BIT( 0xe000, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( beijuehh )
	PORT_START("IN0")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("IN1")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("IN2")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_UNKNOWN ) // battery
	PORT_BIT( 0x001e, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0e00, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // this one must be kept in this state or the machine will freeze after a few seconds?
	PORT_BIT( 0xe000, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("IN3") // is there a 4th button?
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Menu")
	PORT_BIT( 0xfff0, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

ROM_START( jak_spmm )
	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("amazingspiderman.bin", 0x000000, 0x400000, CRC(00d2a62c) SHA1(6a08760dc6dabc2aea32e6eb8b1f98e7edf60791) )

	// has a HT24LC04 to store settings
ROM_END

ROM_START( jak_prr )
	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("power_rangers_to_the_rescue.bin", 0x000000, 0x400000, CRC(96aef5c9) SHA1(d9a7f882932237c50cf5b5dbe4f8168d8916d9f2) )

	// has a ISSI 827 404 (?) to store settings
ROM_END

ROM_START( jak_tpir )
	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("jakkstpir.u5", 0x000000, 0x800000, CRC(1c65fffe) SHA1(32a72c8e7b02d4f17da51c284ab72f6a5264887f) )

	// has a 24LC04 to store settings
ROM_END

ROM_START( jak_bj )
	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("jakkpopcap.bin", 0x000000, 0x200000, CRC(21e59932) SHA1(cb0854674bbaf95d45ba4702b8e9482924e3935b) )

	// has a HT24LC04 to store settings
ROM_END

ROM_START( smartfp )
	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("smartfitparkuk.bin", 0x000000, 0x800000, CRC(2072d7d0) SHA1(eaa4f254d6dee3a7eac64ae2204dd6291e4d27cc) )
ROM_END

ROM_START( smartfps )
	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("smartfitpark.bin", 0x000000, 0x800000, CRC(ada84507) SHA1(a3a80bf71fae62ebcbf939166a51d29c24504428) )
ROM_END

ROM_START( smartfpf )
	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("smartfitpark_fr.bin", 0x000000, 0x800000, CRC(e6d3ba29) SHA1(14e4632997318329be3291f2c4e62f088181f3c8) )
ROM_END

ROM_START( fpsport )
	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("fpsports.bin", 0x000000, 0x800000, CRC(d8c23ccc) SHA1(46cbe0aa180facbc06db771c09d3926b27336ac1) )
ROM_END



ROM_START( gormiti )
	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("gormiti.bin", 0x000000, 0x800000, CRC(71b82d41) SHA1(169b35dc7bdd05b7b32176ddf901ace27736cb86) )
ROM_END

ROM_START( tkmag220 )
	ROM_REGION( 0x8000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "taikee220.bin", 0x0000000, 0x8000000, CRC(02881534) SHA1(a0e0c9cfa3a6b1c6107f06abd3268b82bd663d06) )
ROM_END


ROM_START( imgame )
	ROM_REGION( 0x8000000, "maincpu", ROMREGION_ERASE00 ) // the 2nd half of this ROM required multiple attempts to get a matching dump, so could be suspect, might also be unused as this only has 120 games
	ROM_LOAD16_WORD_SWAP( "imgame.bin", 0x0000000, 0x8000000, CRC(6fba9021) SHA1(852f4c0aaed682aa8ff5b8cd52313ea2d3d920a1))
ROM_END

ROM_START( myac220 )
	ROM_REGION( 0x8000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "myarcadegogamerportable.bin", 0x0000000, 0x8000000, BAD_DUMP CRC(c929a2fa) SHA1(e99007ccc45a268267b4ea0efaf22e3117f5a6bd) ) // several sections seemed to be erased, was repaired with data from tkmag220, likely good but should be verified
ROM_END

ROM_START( beijuehh )
	ROM_REGION( 0x8000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "beijeu.bin", 0x0000000, 0x8000000, CRC(e7b968af) SHA1(a39a3a70e6e0827e4395e09e55983eb9e9348e4a) ) // some address lines might be swapped
ROM_END

ROM_START( bornkidh )
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "sunplus.u3", 0x0000000, 0x0800000, CRC(c4da9d0b) SHA1(5fc644ae26046677e67a01dde3fc6061e73bb60f) )
	ROM_CONTINUE(0x1000000, 0x0800000)
	ROM_CONTINUE(0x0800000, 0x0800000)
	ROM_CONTINUE(0x1800000, 0x0800000)
	ROM_CONTINUE(0x2000000, 0x0800000)
	ROM_CONTINUE(0x3000000, 0x0800000)
	ROM_CONTINUE(0x2800000, 0x0800000)
	ROM_CONTINUE(0x3800000, 0x0800000)
ROM_END

ROM_START( gameu50 )
	ROM_REGION( 0x2000000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "gameu.bin", 0x000000, 0x2000000, CRC(13c42bce) SHA1(f769ceabb8ab4e60c0d663dffd5cca91c6aec206) )
ROM_END

ROM_START( gameu90 )
	ROM_REGION( 0x2000000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "s29gl256.bin", 0x000000, 0x2000000, CRC(4b9e0498) SHA1(7ee387deabdd8dd59e5fa7f4f39d472bd4462ba9) )
ROM_END

ROM_START( gameu108 )
	ROM_REGION( 0x2000000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "s29gl256.u5", 0x000000, 0x2000000, CRC(48e727a4) SHA1(7338f8e46f794ae148adb84146cd2eddf4eba98d) )
ROM_END

ROM_START( dressmtv )
	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	// pinout seems correct, so upper lines are probably being swapped somewhere else
	ROM_LOAD16_WORD_SWAP("dressmaniatv.bin", 0x000000, 0x200000, CRC(66702103) SHA1(1b55e2555b53251962e5246984339ea5718800c4) )
	ROM_CONTINUE(0x400000,0x200000)
	ROM_CONTINUE(0x200000,0x200000)
	ROM_CONTINUE(0x600000,0x200000)
ROM_END


void tkmag220_game_state::tkmag220(machine_config &config)
{
	gcm394_game_state::base(config);

	m_maincpu->porta_in().set_ioport("IN0");
	m_maincpu->portb_in().set_ioport("IN1");
	m_maincpu->portc_in().set_ioport("IN2");

	m_maincpu->portd_out().set(FUNC(tkmag220_game_state::tkmag220_portd_w));
}

void tkmag220_game_state::tkmag220_portd_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (m_maincpu->pc() < 0x10000)
	{
		logerror("%s: port write %04x\n", machine().describe_context().c_str(), data);

		int newbank = 0;

		if (data & 0x8000) newbank |= 1;
		if (data & 0x0002) newbank |= 2;
		if (data & 0x0004) newbank |= 4;

		m_upperbase = newbank * (0x1000000 / 2);
	}

}


uint16_t tkmag220_game_state::cs0_r(offs_t offset)
{
	// [:] installing cs0 handler start_address 00000000 end_address 007fffff
	return m_romregion[(offset & 0x07fffff) + m_upperbase];
}

void tkmag220_game_state::machine_reset()
{
	// as with the Family Sport multi-game versions on spg2xx hardware, there are actually 8 programs in here, externally banked
	// each of those programs is 16MBytes, so is either going to have the upper half banked externally like the older hardware types, os possibly using the CS registers on this hardware type
	m_upperbase = 0 * (0x1000000 / 2);
	gcm394_game_state::machine_reset();

	//m_maincpu->set_paldisplaybank_high_hack(0);
	//m_maincpu->set_pal_sprites_hack(0x000);
	//m_maincpu->set_pal_back_hack(0x000);
	m_maincpu->set_alt_tile_addressing_hack(1);
}




void beijuehh_game_state::beijuehh(machine_config &config)
{
	gcm394_game_state::base(config);

	m_maincpu->porta_in().set_ioport("IN0");
	m_maincpu->portb_in().set_ioport("IN1");
	m_maincpu->portc_in().set_ioport("IN2");
	m_maincpu->portd_in().set_ioport("IN3");

	m_maincpu->portb_out().set(FUNC(beijuehh_game_state::beijuehh_portb_w));
	m_maincpu->portd_out().set(FUNC(beijuehh_game_state::beijuehh_portd_w));
}


void beijuehh_game_state::beijuehh_portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (m_maincpu->pc() < 0xf000)
	{
		// 00a0 bits are banking
		logerror("%s: portb write %04x\n", machine().describe_context(), data);

		if (data & 0x0020)
			m_bank |= 0x04;
		else
			m_bank &= ~0x04;

		if (data & 0x0080)
			m_bank |= 0x08;
		else
			m_bank &= ~0x08;

		m_upperbase = m_bank * (0x400000);
	}
	m_portb_data = data;
}


void beijuehh_game_state::beijuehh_portd_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (m_maincpu->pc() < 0xf000)
	{
		// c000 bits are banking
		logerror("%s: portd write %04x\n", machine().describe_context(), data);

		if (data & 0x4000)
			m_bank |= 0x02;
		else
			m_bank &= ~0x02;

		if (data & 0x8000)
			m_bank |= 0x01;
		else
			m_bank &= ~0x01;

		m_upperbase = m_bank * (0x400000);
	}
	m_portd_data = data;
}


uint16_t beijuehh_game_state::cs0_r(offs_t offset)
{
	// [:] installing cs0 handler start_address 00000000 end_address 003fffff
	return m_romregion[(offset & 0x03fffff) + m_upperbase];
}


void beijuehh_game_state::machine_reset()
{
	// this one seems to operate in a mode much closer to the older spg2xx hardware
	// presumably registers enable this behavior
	//
	// ROM is just banked 8MByte blocks
	// overall very similar to marc101 / marc250 units, seems to have the port based
	// 'timer' checks for protection(?) too

	m_portb_data = 0;
	m_portd_data = 0;
	m_bank = 0;

	m_upperbase = 0 * (0x400000);
	gcm394_game_state::machine_reset();

	//m_maincpu->set_paldisplaybank_high_hack(0);
	//m_maincpu->set_pal_sprites_hack(0x000);
	//m_maincpu->set_pal_back_hack(0x000);
	m_maincpu->set_alt_tile_addressing_hack(1);
	//m_maincpu->set_alt_extrasprite_hack(1);
	m_maincpu->set_legacy_video_mode();
}



void gameu_handheld_game_state::gameu(machine_config &config)
{
	gcm394_game_state::base(config);

	m_maincpu->porta_out().set(FUNC(gameu_handheld_game_state::gameu_porta_w));
	m_maincpu->portb_out().set(FUNC(gameu_handheld_game_state::gameu_portb_w));
	m_maincpu->portc_out().set(FUNC(gameu_handheld_game_state::gameu_portc_w));
	m_maincpu->portd_out().set(FUNC(gameu_handheld_game_state::gameu_portd_w));

	m_screen->set_refresh_hz(30); // too fast at 60, but maybe it's for other reasons?
	m_screen->set_visarea(0, (160)-1, 0, (128)-1); // appears to be the correct resolution for the LCD panel
}

void gormiti_game_state::machine_reset()
{
	gcm394_game_state::machine_reset();
	m_maincpu->set_alt_tile_addressing_hack(1);
}

uint16_t gameu_handheld_game_state::cs0_r(offs_t offset)
{
	return m_romregion[(offset & 0x00fffff) + m_upperbase];
}

void gameu_handheld_game_state::gameu_porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: porta write %04x\n", machine().describe_context(), data);
	m_porta_data = data;
}

void gameu_handheld_game_state::gameu_portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: portb write %04x\n", machine().describe_context(), data);
	m_portb_data = data;
}

void gameu_handheld_game_state::gameu_portc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: portc write %04x\n", machine().describe_context(), data);
	m_portc_data = data;
}

void gameu_handheld_game_state::gameu_portd_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	// hacky, maybe we need better direction/attribute handling on the ports in the core?
	m_portd_data = data;
	//int pc = m_maincpu->pc();
	//if ((pc != 0x2b49) && (pc != 0x2b34) && (pc != 0x2b8b) && (pc != 0x2bc0))
	{
		logerror("%s: portd write %04x %04x\n", machine().describe_context(), data, mem_mask);

		uint8_t bank = (data & 0xfc00) >> 10;
		m_upperbase = bank * 0x40000;
	}

}
void gameu_handheld_game_state::machine_start()
{
	m_upperbase = 0;
	m_porta_data = 0;
	m_portb_data = 0;
	m_portc_data = 0;
	m_portd_data = 0;

	save_item(NAME(m_upperbase));
	save_item(NAME(m_porta_data));
	save_item(NAME(m_portb_data));
	save_item(NAME(m_portc_data));
	save_item(NAME(m_portd_data));
}

void gameu_handheld_game_state::machine_reset()
{
	gcm394_game_state::machine_reset();
	m_maincpu->set_alt_tile_addressing_hack(1);
	m_upperbase = 0;
}

void gameu_handheld_game_state::init_gameu()
{
	uint16_t *ROM = (uint16_t*)memregion("maincpu")->base();
	int size = memregion("maincpu")->bytes();

	for (int i = 0; i < size/2; i++)
	{
		ROM[i] = ROM[i] ^ 0x3b90;

		ROM[i] = bitswap<16>(ROM[i], 3, 1, 11, 9,  6, 14, 0,  2,
									 8, 7, 13, 15, 4, 5,  12, 10);
	}

	m_maincpu->set_alt_tile_addressing_hack(0);
	m_maincpu->set_disallow_resolution_control();


}

void gameu_handheld_game_state::init_gameu50()
{
	init_gameu();

	// why do we need these? it will jump to 0 after the menu selection (prior to fadeout and bank select) otherwise, which can't be correct
	uint16_t *ROM = (uint16_t*)memregion("maincpu")->base();
	int base = 0x19c9a;
	ROM[(base + 0x00) / 2] = 0xf165;
	ROM[(base + 0x02) / 2] = 0xf165;
	ROM[(base + 0x04) / 2] = 0xf165;

	ROM[(base + 0x1e) / 2] = 0xf165;
	ROM[(base + 0x20) / 2] = 0xf165;
	ROM[(base + 0x22) / 2] = 0xf165;

	ROM[(base + 0x3a) / 2] = 0xf165;
	ROM[(base + 0x3c) / 2] = 0xf165;
	ROM[(base + 0x3e) / 2] = 0xf165;
}

void gameu_handheld_game_state::init_gameu108()
{
	init_gameu();

	uint16_t *ROM = (uint16_t*)memregion("maincpu")->base();

	// why do we need these? it will jump to 0 after the menu selection (prior to fadeout and bank select) otherwise, which can't be correct
	ROM[(0x1aa48) / 2] = 0xf165;
	ROM[(0x1aa4a) / 2] = 0xf165;
	ROM[(0x1aa4c) / 2] = 0xf165;

	ROM[(0x1aa82) / 2] = 0xf165;
	ROM[(0x1aa84) / 2] = 0xf165;
	ROM[(0x1aa86) / 2] = 0xf165;
}

// the JAKKS ones of these seem to be known as 'Generalplus GPAC500' hardware?
CONS(2008, jak_spmm,  0,       0, base, jak_spmm,  gormiti_game_state, empty_init, "JAKKS Pacific Inc / Santa Cruz Games", "The Amazing Spider-Man and The Masked Menace (JAKKS Pacific TV Game)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
CONS(2008, jak_prr,   0,       0, base, jak_spmm,  gormiti_game_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd", "Power Rangers to the Rescue (JAKKS Pacific TV Game) (Aug 8 2008 16:46:59)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
CONS(2008, jak_bj,    0,       0, base, jak_bj,    gormiti_game_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd", "Bejeweled Deluxe (JAKKS Pacific TV Game) (Feb 28 2008 22:54:43)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
CONS(2009, jak_tpir,  0,       0, base, jak_spmm,  gormiti_game_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd", "The Price Is Right (JAKKS Pacific TV Game) (Mar 24 2009 17:34:55)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

CONS(2009, smartfp,   0,       0, base, smartfp,  gcm394_game_state, empty_init, "Fisher-Price", "Fun 2 Learn Smart Fit Park (UK)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
CONS(2009, smartfps,  smartfp, 0, base, smartfp,  gcm394_game_state, empty_init, "Fisher-Price", "Fun 2 Learn Smart Fit Park (Spain)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
CONS(2009, smartfpf,  smartfp, 0, base, smartfp,  gcm394_game_state, empty_init, "Fisher-Price", "Fun 2 Learn Smart Fit Park (France)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND) // boxart simply has 'Smart Fit'

// skip the call at 6d47a to get it to show something
CONS(2008, fpsport,   0,       0, base, base,     gcm394_game_state, empty_init, "Fisher-Price", "3-in-1 Smart Sports! (US)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)

// uses a barcode card scanner device with custom cards
CONS(200?, dressmtv,  0,       0, base_alt_irq, dressmtv, gormiti_game_state, empty_init, "Tomy Takara", "Disney Princess Dress Mania TV (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// These are ports of the 'Family Sport' games to GPL16250 type hardware, but they don't seem to use many unSP 2.0 instructions.
// The menu style is close to 'm505neo' but the game selection is closer to 'dnv200fs' (but without the Sports titles removed, and with a few other extras not found on that unit)
CONS(201?, tkmag220,  0,       0, tkmag220, tkmag220, tkmag220_game_state,  empty_init,      "TaiKee / Senca",         "Mini Arcade Games Console (Family Sport 220-in-1)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
// DGUN-2891 or DGUN-2864 ? both look the same, no indication on unboxed unit?
CONS(201?, myac220,   0,       0, tkmag220, tkmag220, tkmag220_game_state,  empty_init,      "dreamGEAR / Senca",      "My Arcade Go Gamer Portable (Family Sport 220-in-1)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// 2012 date from manual
CONS(2012, imgame,    0,       0, tkmag220, tkmag220, tkmag220_game_state,  empty_init,      "I'm Game / Senca",      "I'm Game! GP120 (Family Sport 120-in-1)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
// a 180 game Family Sport I'm Game! also exists (and some Famiclones)

// Also sold as 'BornKid 220 in 1' There are lower capacity versions too, was sold with note that 'X-Racer III crashes in-game'
// Does the 'Helicopter' game work properly on real hardware? The function at 0x0D2BE7 uses RAM address 0x2372 for the upper bits of the tile base offset calculation
// but that RAM address doesn't appear to be written anywhere in the code, resulting in scrolling being entirely broken.
CONS(201?, beijuehh,    0,       0, beijuehh, beijuehh, beijuehh_game_state,  empty_init,      "Beijue",      "Beijue 16 Bit Handheld Games 220-in-1 (Game Boy style case)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS(201?, bornkidh,    0,       0, beijuehh, beijuehh, beijuehh_game_state,  empty_init,      "BornKid",     "BornKid 16 Bit Handheld Games 100-in-1 (model GB-10X)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// die on this one is 'GCM420'
CONS(2013, gormiti,   0, 0, base, gormiti,  gormiti_game_state, empty_init, "Giochi Preziosi", "Gormiti Game Arena (Spain)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)

// unit looks a bit like a knock-off Wii-U tablet, but much smaller
// was also available under other names, with different designs (PSP style)
CONS( 201?, gameu50,       0,              0,      gameu, gameu, gameu_handheld_game_state, init_gameu50,  "YSN", "Play Portable Color GameU+ (50-in-1) (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
CONS( 201?, gameu90,       0,              0,      gameu, gameu, gameu_handheld_game_state, init_gameu,    "YSN", "Play Portable Color GameU+ (90-in-1) (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
CONS( 201?, gameu108,      0,              0,      gameu, gameu, gameu_handheld_game_state, init_gameu108, "YSN", "Play Portable Color GameU+ (108-in-1) (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )



generalplus_gpl16250_romram.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/*
    GPL16250 / GPAC800 / GMC384 / GCM420 related support

    GPL16250 is the GeneralPlus / SunPlus part number
    GPAC800 is the JAKKS Pacific codename
    GMC384 / GCM420 is what is printed on the die

    ----

    GPL16250 games using ROM + RAM configuration
*/

#include "emu.h"
#include "generalplus_gpl16250_romram.h"


static INPUT_PORTS_START( wrlshunt )
	PORT_START("IN0")
	PORT_START("IN1")
	PORT_START("IN2")
INPUT_PORTS_END


static INPUT_PORTS_START( jak_s500 )
	PORT_START("IN0")
	PORT_DIPNAME( 0x0001, 0x0001, "IN0" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_BUTTON3 )

	PORT_START("IN1")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN2")
	PORT_DIPNAME( 0x0001, 0x0001, "IN2" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( paccon ) // for Test Mode hold buttons 1+2 until the screen starts changing colours (happens after the copyright display)
	PORT_START("IN0")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_UNUSED ) // PAL/NTSC flag
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_UNUSED ) // '***'
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_UNUSED ) // '***'
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON1 ) // 'A'
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON3 ) // '*C*  (doesn't exist?) (cheat)
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON2 ) // 'B'
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_UNUSED ) // '***'
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON4 ) // '*MENU*' (doesn't exist?)
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_UNUSED ) // '***'
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_UNUSED ) // '***'
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_UNUSED ) // '***'
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_UNUSED ) // '***'

	PORT_START("IN1")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN2")
	PORT_DIPNAME( 0x0001, 0x0001, "IN2" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( jak_ths )
	PORT_START("IN0")
	PORT_DIPNAME( 0x0001, 0x0001, "IN0" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("A")
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("B")
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_BUTTON3 )

	PORT_START("IN1")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN2")
	PORT_DIPNAME( 0x0001, 0x0001, "IN2" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
INPUT_PORTS_END

uint16_t wrlshunt_game_state::cs0_r(offs_t offset)
{
	return m_romregion[offset & m_romwords_mask];
}

void wrlshunt_game_state::cs0_w(offs_t offset, uint16_t data)
{
	logerror("cs0_w write to ROM?\n");
	//m_romregion[offset & 0x3ffffff] = data;
}

uint16_t wrlshunt_game_state::cs1_r(offs_t offset)
{
	return m_sdram[offset & 0x3fffff];
}

void wrlshunt_game_state::cs1_w(offs_t offset, uint16_t data)
{
	m_sdram[offset & 0x3fffff] = data;
}


void wrlshunt_game_state::machine_start()
{
	m_romwords_mask = (memregion("maincpu")->bytes()/2)-1;
	save_item(NAME(m_sdram));
}

void wrlshunt_game_state::machine_reset()
{
	cs_callback(0x00, 0x00, 0x00, 0x00, 0x00);
	m_maincpu->set_cs_space(m_memory->get_program());
	m_maincpu->reset(); // reset CPU so vector gets read etc.

	//m_maincpu->set_paldisplaybank_high_hack(1);
	m_maincpu->set_alt_tile_addressing_hack(1);
}

void wrlshunt_game_state::init_wrlshunt()
{
	m_sdram.resize(0x400000); // 0x400000 words, 0x800000 bytes
}

void wrlshunt_game_state::init_ths()
{
	m_sdram.resize(0x400000); // 0x400000 words, 0x800000 bytes (verify)
}

void wrlshunt_game_state::gpl16250_romram(machine_config &config)
{
	gcm394_game_state::base(config);
}

uint16_t wrlshunt_game_state::porta_r()
{
	uint16_t data = m_io[0]->read();
	logerror("%s: Port A Read: %04x\n",  machine().describe_context(), data);
	return data;
}

void wrlshunt_game_state::porta_w(uint16_t data)
{
	logerror("%s: Port A:WRITE %04x\n", machine().describe_context(), data);

	// HACK
	address_space& mem = m_maincpu->space(AS_PROGRAM);
	if (mem.read_word(0x5b354) == 0xafd0)   // wrlshubt - skip check (EEPROM?)
		mem.write_word(0x5b354, 0xB403);
}




uint16_t jak_s500_game_state::porta_r()
{
	uint16_t data = m_io[0]->read();
	logerror("%s: Port A Read: %04x\n", machine().describe_context(), data);

	// these are debug helpers to access the test modes while we don't have the
	// secret codes / controls mapped properly

	//address_space& mem = m_maincpu->space(AS_PROGRAM);

	//if (mem.read_word(0x22b408) == 0x4846)
	//  mem.write_word(0x22b408, 0x4840);    // jak_s500 force service mode

	//if (mem.read_word(0x236271) == 0x4846)
	//  mem.write_word(0x236271, 0x4840);    // jak_totm force service mode

	//if (mem.read_word(0x22d6f7) == 0x4846)
	//  mem.write_word(0x22d6f7, 0x4840);    // jak_pf force service mode

	//if (mem.read_word(0x23e295) == 0x4846)
	//  mem.write_word(0x23e295, 0x4840);    // jak_smwm force service mode

	//if (mem.read_word(0x22e92e) == 0x4646)
	//  mem.write_word(0x22e92e, 0x4640);    // jak_swcl force service mode

	return data;
}

uint16_t jak_s500_game_state::portb_r()
{
	uint16_t data = m_io[1]->read();
	logerror("%s: Port B Read: %04x\n", machine().describe_context(), data);
	return data;
}


void jak_s500_game_state::machine_reset()
{
	cs_callback(0x00, 0x00, 0x00, 0x00, 0x00);
	m_maincpu->set_cs_space(m_memory->get_program());
	m_maincpu->reset(); // reset CPU so vector gets read etc.

	//m_maincpu->set_paldisplaybank_high_hack(0);
	m_maincpu->set_alt_tile_addressing_hack(1);
}


void lazertag_game_state::machine_reset()
{
	jak_s500_game_state::machine_reset();
	//m_maincpu->set_pal_sprites_hack(0x800);
}

void paccon_game_state::machine_reset()
{
	jak_s500_game_state::machine_reset();
	m_maincpu->space(AS_PROGRAM).install_read_handler(0x6593, 0x6593, read16smo_delegate(*this, FUNC(paccon_game_state::paccon_speedup_hack_r)));
//  install_speedup_hack(0x6593, 0x30033);
}

void jak_pf_game_state::machine_reset()
{
	jak_s500_game_state::machine_reset();
	m_maincpu->space(AS_PROGRAM).install_read_handler(0x0001, 0x0001, read16smo_delegate(*this, FUNC(jak_pf_game_state::jak_pf_speedup_hack_r)));
	m_maincpu->space(AS_PROGRAM).install_read_handler(0x13dd, 0x13dd, read16smo_delegate(*this, FUNC(jak_pf_game_state::jak_pf_speedup_hack2_r)));
}

void jak_prft_game_state::machine_reset()
{
	jak_s500_game_state::machine_reset();
	//m_maincpu->set_alt_tile_addressing_hack(0);
	m_maincpu->set_alt_extrasprite_hack(1);
}


uint16_t paccon_game_state::paccon_speedup_hack_r()
{
	u32 const pc = m_maincpu->pc();
	if (pc == 0x30033)
		m_maincpu->spin_until_time(m_maincpu->cycles_to_attotime(2000));
	return m_maincpu->get_ram_addr(0x6593);
}

uint16_t jak_pf_game_state::jak_pf_speedup_hack_r()
{
	u32 const pc = m_maincpu->pc();
	if (pc == 0x30010)
		m_maincpu->spin_until_time(m_maincpu->cycles_to_attotime(2000));
	return m_maincpu->get_ram_addr(0x0001);
}

uint16_t jak_pf_game_state::jak_pf_speedup_hack2_r()
{
	u32 const pc = m_maincpu->pc();
	if (pc == 0x2611b4)
		m_maincpu->spin_until_time(m_maincpu->cycles_to_attotime(2000));
	return m_maincpu->get_ram_addr(0x13dd);
}

ROM_START( paccon )
	//ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 ) // not on this model? (or at least not this size, as CS base is different)
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("pacmanconnect.bin", 0x000000, 0x400000, CRC(8567cdc7) SHA1(cef4e003142e479169e4438ab33558436ee9ee68) )
ROM_END



ROM_START(lazertag)
	//ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 ) // not on this model? (or at least not this size, as CS base is different)
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION(0x1000000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("lazertag.bin", 0x000000, 0x1000000, CRC(8bf16a28) SHA1(90d05e1876332324b074e4845e28b90fcb007122) )
ROM_END

ROM_START(jak_sinv)
	//ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 ) // not on this model? (or at least not this size, as CS base is different)
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("jakkspaceinvaders.u3", 0x000000, 0x800000, CRC(2ccb1fc9) SHA1(21d92829de4b03b92894d92853bb5ec360dfad3c) )
ROM_END

ROM_START(jak_s500)
	//ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 ) // not on this model? (or at least not this size, as CS base is different)
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("spbwheel.bin", 0x000000, 0x800000, CRC(6ba1d335) SHA1(1bb3e4d02c7b35dd4d336971c6a9f82071cc6ce1) )
ROM_END

ROM_START(jak_swcl)
	//ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 ) // not on this model? (or at least not this size, as CS base is different)
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("jakksclonewars.bin", 0x000000, 0x800000, CRC(549bb326) SHA1(992a60321580a4e014801d401b3a7ee000d2b465) )
ROM_END

ROM_START(jak_smwm)
	//ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 ) // not on this model? (or at least not this size, as CS base is different)
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("spidersense.bin", 0x000000, 0x800000, CRC(e0676d0e) SHA1(01c01852fe4aea799c09ebbb6870b2f6e92085c4) )
ROM_END


ROM_START(jak_pf)
	//ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 ) // not on this model? (or at least not this size, as CS base is different)
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("phineas.bin", 0x000000, 0x800000, CRC(bb18f70d) SHA1(4e3c204e44efe9186809404521ebeac348c45fac))
ROM_END

ROM_START(jak_prft)
	//ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 ) // not on this model? (or at least not this size, as CS base is different)
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("powerrangerssword.bin", 0x000000, 0x800000, CRC(77bc8aea) SHA1(a2efaa718d8ecece46cebb9f0f13a8fa10fc2826) )
ROM_END

ROM_START(jak_tink)
	//ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 ) // not on this model? (or at least not this size, as CS base is different)
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("disneyfairies.bin", 0x000000, 0x800000, CRC(566dae87) SHA1(3abe1b7d578ed9255101bfec0e4bb4d6dc0aa0b7) )
ROM_END





ROM_START(jak_totm)
	//ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 ) // not on this model? (or at least not this size, as CS base is different)
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("toysonthemove.bin", 0x000000, 0x800000, CRC(d08fb72a) SHA1(1fea98542ef7c65eef31afb70fd50952b4cef1c1) )
ROM_END

ROM_START(jak_ths)
	//ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 ) // not on this model? (or at least not this size, as CS base is different)
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("tripleheadersports.bin", 0x000000, 0x800000, CRC(2b5f8734) SHA1(57bccaa70f0efbf3da3259b74f3082d1a14c9908) )
ROM_END


ROM_START(wrlshunt)
	//ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 ) // not on this model? (or at least not this size, as CS base is different)
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION(0x8000000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP("wireless.bin", 0x0000, 0x8000000, CRC(a6ecc20e) SHA1(3645f23ba2bb218e92d4560a8ae29dddbaabf796))
ROM_END

/*
Wireless Hunting Video Game System
(info provided with dump)

System: Wireless Hunting Video Game System
Publisher: Hamy / Kids Station Toys Inc
Year: 2011
ROM: FDI MSP55LV100G
RAM: Micron Technology 48LC8M16A2

Games:

Secret Mission
Predator
Delta Force
Toy Land
Dream Forest
Trophy Season
Freedom Force
Be Careful
Net Power
Open Training
Super Archer
Ultimate Frisbee
UFO Shooting
Happy Darts
Balloon Shoot
Avatair
Angry Pirate
Penguin War
Ghost Shooter
Duck Hunt


ROM Board:

Package: SO44
Spacing: 1.27 mm
Width: 16.14 mm
Length: 27.78 mm
Voltage: 3V
Pinout:

          A25  A24
            |  |
      +--------------------------+
A21 --|==   #  # `.__.'        ==|-- A20
A18 --|==                      ==|-- A19
A17 --|==                      ==|-- A8
 A7 --|==                      ==|-- A9
 A6 --|==                  o   ==|-- A10
 A5 --|==  +----------------+  ==|-- A11
 A4 --|==  |                |  ==|-- A12
 A3 --|==  |  MSP55LV100G   |  ==|-- A13
 A2 --|==  |  0834 M02H     |  ==|-- A14
 A1 --|==  |  JAPAN         |  ==|-- A15
 A0 --|==  |                |  ==|-- A16
#CE --|==  |                |  ==|-- A23
GND --|==  |                |  ==|-- A22
#OE --|==  |                |  ==|-- Q15
 Q0 --|==  |                |  ==|-- Q7
 Q8 --|==  |                |  ==|-- Q14
 Q1 --|==  +----------------+  ==|-- Q6
 Q9 --|==                      ==|-- Q13
 Q2 --|==       M55L100G       ==|-- Q5
Q10 --|==                      ==|-- Q12
 Q3 --|==                      ==|-- Q4
Q11 --|==                      ==|-- VCC
      +--------------------------+


The only interesting string in this ROM is SPF2ALP,
which is also found in the Wireless Air 60 ROM.

*/

// Bokudora Ver1.4 2014-09-20 on PCB
ROM_START(tomycar)
	//ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 ) // not on this model? (or at least not this size, as CS base is different)
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION(0x2000000, "maincpu", ROMREGION_ERASE00)
	// this loading gives correct sprites (must be swapped somewhere on PCB because this was otherwise a standard pinout?)
	// but backgrounds are still broken (different issue maybe? there are writes to CS0 ROM area)
	ROM_LOAD16_WORD_SWAP( "tomycar.bin", 0x000000, 0x800000, CRC(bd98a198) SHA1(117aba55bf98bf76cbc9ed169e2a968bfdd9ed1a) )
	ROM_CONTINUE(0x1000000, 0x800000)
	ROM_CONTINUE(0x0800000, 0x800000)
	ROM_CONTINUE(0x1800000, 0x800000)
ROM_END



// also sold as "Pac-Man Connect & Play 35th Anniversary" (same ROM?)
CONS(2012, paccon,   0, 0, gpl16250_romram, paccon, paccon_game_state, init_wrlshunt, "Bandai", "Pac-Man Connect & Play (Feb 14 2012 10:46:23)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)

CONS(2008, lazertag, 0, 0, gpl16250_romram, jak_s500, lazertag_game_state, init_wrlshunt, "Tiger Electronics", "Lazer Tag Video Game Module", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)

CONS(2009, jak_swcl, 0, 0, gpl16250_romram, jak_s500, jak_s500_game_state, init_wrlshunt, "JAKKS Pacific Inc / HotGen Ltd",          "Star Wars: The Clone Wars - Republic Squadron (JAKKS Pacific TV Motion Game) (May 6 2009 12:53:31)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
CONS(2009, jak_s500, 0, 0, gpl16250_romram, jak_s500, jak_s500_game_state, init_wrlshunt, "JAKKS Pacific Inc / HotGen Ltd",          "SpongeBob SquarePants Bikini Bottom 500 (JAKKS Pacific TV Motion Game) (Apr 16 2009 15:11:17)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
CONS(2009, jak_smwm, 0, 0, gpl16250_romram, jak_s500, jak_s500_game_state, init_wrlshunt, "JAKKS Pacific Inc / HotGen Ltd",          "Spider-Man Web Master (JAKKS Pacific TV Motion Game) (Apr 23 2009 17:10:04)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
CONS(2010, jak_pf,   0, 0, gpl16250_romram, jak_s500, jak_pf_game_state,   init_wrlshunt, "JAKKS Pacific Inc / HotGen Ltd",          "Phineas and Ferb: Best Game Ever! (JAKKS Pacific TV Motion Game) (Sep 16 2009 17:36:00)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND) // build date is 2009, but onscreen display is 2010
CONS(2009, jak_totm, 0, 0, gpl16250_romram, jak_s500, jak_s500_game_state, init_wrlshunt, "JAKKS Pacific Inc / HotGen Ltd",          "Toy Story - Toys on the Move (JAKKS Pacific TV Motion Game) (Dec 24 2009 17:34:29)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND) // Toys on the Move has ISSI 404A

CONS(2009, jak_prft, 0, 0, gpl16250_romram, jak_s500, jak_prft_game_state, init_wrlshunt, "JAKKS Pacific Inc / Santa Cruz Games",    "Power Rangers Force In Time (JAKKS Pacific TV Motion Game)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
CONS(2009, jak_tink, 0, 0, gpl16250_romram, jak_s500, jak_prft_game_state, init_wrlshunt, "JAKKS Pacific Inc / Santa Cruz Games",    "Tinker Bell and the Lost Treasure (JAKKS Pacific TV Motion Game)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)

CONS(2009, jak_ths,  0, 0, gpl16250_romram, jak_ths,  jak_s500_game_state, init_ths,      "JAKKS Pacific Inc / Super Happy Fun Fun", "Triple Header Sports (JAKKS Pacific TV Motion Game)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)

CONS(2011, jak_sinv, 0, 0, gpl16250_romram, jak_s500, jak_s500_game_state, init_wrlshunt, "JAKKS Pacific Inc / Code Mystics",        "Retro Arcade featuring Space Invaders (JAKKS Pacific TV Game)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)

CONS(2011, wrlshunt, 0, 0, gpl16250_romram, wrlshunt, wrlshunt_game_state, init_wrlshunt, "Hamy / Kids Station Toys Inc", "Wireless Hunting Video Game System", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// ぼくはトミカドライバー はたらくのりもの大集合！
CONS(2014, tomycar,  0, 0, gpl16250_romram, paccon, jak_prft_game_state, init_wrlshunt, "Tomy Takara", "Boku wa Tomica Driver - Hataraku Norimono Daishuugou! (Japan)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)




generalplus_gpl16250_spi.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/*
     GPL16250* games using SPI Flash + RAM configuration

     *part number could be different for these, they've only
      been seen as globtops
*/

#include "emu.h"
#include "generalplus_gpl16250.h"
#include "softlist_dev.h"


namespace {

class generalplus_gpspispi_game_state : public gcm394_game_state
{
public:
	generalplus_gpspispi_game_state(const machine_config& mconfig, device_type type, const char* tag) :
		gcm394_game_state(mconfig, type, tag)
	{
	}

	void generalplus_gpspispi(machine_config &config);

	void init_spi();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
};



class generalplus_gpspispi_bkrankp_game_state : public generalplus_gpspispi_game_state
{
public:
	generalplus_gpspispi_bkrankp_game_state(const machine_config& mconfig, device_type type, const char* tag) :
		generalplus_gpspispi_game_state(mconfig, type, tag),
		m_cart(*this, "cartslot")
	{
	}

	void generalplus_gpspispi_bkrankp(machine_config &config);

protected:
	required_device<generic_slot_device> m_cart;
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

private:
};


void generalplus_gpspispi_game_state::machine_start()
{
}

void generalplus_gpspispi_game_state::machine_reset()
{
	m_maincpu->reset(); // reset CPU so vector gets read etc.

	//m_maincpu->set_paldisplaybank_high_hack(0);
	m_maincpu->set_alt_tile_addressing_hack(1);
}

static INPUT_PORTS_START( gcm394 )
	PORT_START("IN0")
	PORT_START("IN1")
	PORT_START("IN2")
INPUT_PORTS_END


void generalplus_gpspispi_game_state::generalplus_gpspispi(machine_config &config)
{
	GP_SPISPI(config, m_maincpu, 96000000/2, m_screen);
	m_maincpu->porta_in().set(FUNC(generalplus_gpspispi_game_state::porta_r));
	m_maincpu->portb_in().set(FUNC(generalplus_gpspispi_game_state::portb_r));
	m_maincpu->portc_in().set(FUNC(generalplus_gpspispi_game_state::portc_r));
	m_maincpu->porta_out().set(FUNC(generalplus_gpspispi_game_state::porta_w));
	m_maincpu->space_read_callback().set(FUNC(generalplus_gpspispi_game_state::read_external_space));
	m_maincpu->space_write_callback().set(FUNC(generalplus_gpspispi_game_state::write_external_space));
	m_maincpu->set_irq_acknowledge_callback(m_maincpu, FUNC(sunplus_gcm394_base_device::irq_vector_cb));
	m_maincpu->add_route(ALL_OUTPUTS, "speaker", 0.5, 0);
	m_maincpu->add_route(ALL_OUTPUTS, "speaker", 0.5, 1);
	m_maincpu->set_bootmode(0); // boot from internal ROM (SPI bootstrap)
	m_maincpu->set_cs_config_callback(FUNC(gcm394_game_state::cs_callback));

	FULL_MEMORY(config, m_memory).set_map(&generalplus_gpspispi_game_state::cs_map_base);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320*2, 262*2);
	m_screen->set_visarea(0, (320*2)-1, 0, (240*2)-1);
	m_screen->set_screen_update("maincpu", FUNC(sunplus_gcm394_device::screen_update));
	m_screen->screen_vblank().set(m_maincpu, FUNC(sunplus_gcm394_device::vblank));

	SPEAKER(config, "speaker", 2).front();
}

DEVICE_IMAGE_LOAD_MEMBER(generalplus_gpspispi_bkrankp_game_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

void generalplus_gpspispi_bkrankp_game_state::generalplus_gpspispi_bkrankp(machine_config &config)
{
	generalplus_gpspispi_game_state::generalplus_gpspispi(config);

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "bkrankp_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(generalplus_gpspispi_bkrankp_game_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("bkrankp_cart");
}



ROM_START( bkrankp )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only

	ROM_REGION(0x400000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "unit_mx25l3206e_c22016.bin", 0x0000, 0x400000, CRC(7efad116) SHA1(427d707e97586ae6ab5fe08f29ca450ddc7ad36e) )
ROM_END

ROM_START( prailpls )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only (if it exists at all)

	ROM_REGION(0x2000000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "mx25l25635f.u9", 0x0000, 0x2000000, CRC(17faefb0) SHA1(1d31c5aa1a37882f74c08414f69c4285149352b7) )
ROM_END

ROM_START( anpanbd )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only (if it exists at all)

	ROM_REGION(0x1000000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "mx25l12835f.u2", 0x0000, 0x1000000, CRC(c4be09d7) SHA1(c9098d0c1c9db649a010f67469f500b69407372f) )
ROM_END

ROM_START( anpanm15 )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only (if it exists at all)

	ROM_REGION(0x1000000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "mx25l12835f.ic3", 0x0000, 0x1000000, CRC(47c36cbd) SHA1(f1cae506e21c1795401004d79f6bb1b1d982d657) )
ROM_END

ROM_START( pokegach )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only (if it exists at all)

	ROM_REGION(0x1000000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "mx25l12835f.u4", 0x0000, 0x1000000, CRC(85bc9716) SHA1(3de7f0fd92e8f6084eb0b82ec293be3166c800ac) )
ROM_END

ROM_START( pokegac2 )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only (if it exists at all)

	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "red_mx25l6445e.u4", 0x0000, 0x800000, CRC(f20bb213) SHA1(787ae27e36352525e6ffebe25da4329cb156b219) )

	ROM_REGION(0x800, "i2cmem", ROMREGION_ERASE00) // probably just progress / settings
	ROM_LOAD16_WORD_SWAP( "red_ft24c16a.u9", 0x000, 0x800, CRC(2abcf4d4) SHA1(5227e868f93205a069bc49c30792a9b95c8f0efc) )
ROM_END

ROM_START( pokegac2y )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only (if it exists at all)

	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "yellow_mx25l6445e.u4", 0x0000, 0x800000, CRC(587310fa) SHA1(4334b91b7f9f599bc21b354b267b132fd470f53c) )

	ROM_REGION(0x800, "i2cmem", ROMREGION_ERASE00) // probably just progress / settings
	ROM_LOAD16_WORD_SWAP( "yellow_ft24c16a.u9", 0x000, 0x800, CRC(b7662106) SHA1(a75366cbf3f3954a4136c89cc1db0ffb6f7d8c13) )
ROM_END


ROM_START( bk139in1 )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only (if it exists at all)

	ROM_REGION(0x4000000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD( "25q512.bin", 0x0000, 0x4000000, CRC(0cd111a4) SHA1(70553a44c3d946e5d23c09f04e0627a5dbaa3e4d) )
ROM_END

ROM_START( lxcyrace )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only (if it exists at all)

	ROM_REGION(0x1000000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD( "25q128.u2", 0x0000, 0x1000000, CRC(4489c99d) SHA1(792d6d224584fe1f3349c64a59aa79a587dd8c17) )
ROM_END


void generalplus_gpspispi_game_state::init_spi()
{
	int vectorbase = 0x2fe0;
	uint8_t* spirom = memregion("maincpu")->base();

	address_space& mem = m_maincpu->space(AS_PROGRAM);

	/*  Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

	    00000000  50 47 70 73 73 69 69 70 70 73 44 00 44 3F 44 00  PGpssiipps
	    00000010  -- -- -- -- -- bb -- -- -- -- -- -- -- -- -- --
	                             ^^ copy dest, just like with nand type

	    bb = where to copy first block

	    The header is GPspispi (byteswapped) then some params
	    one of the params appears to be for the initial code copy operation done
	    by the bootstrap
	*/

	// probably more bytes are used
	int dest = spirom[0x15] << 8;

	// copy a block of code from the NAND to RAM
	for (int i = 0; i < 0x2000; i++)
	{
		uint16_t word = spirom[(i * 2) + 0] | (spirom[(i * 2) + 1] << 8);

		mem.write_word(dest + i, word);
	}

	// these vectors must either directly point to RAM, or at least redirect there after some code
	uint16_t* internal = (uint16_t*)memregion("maincpu:internal")->base();
	internal[0x7ff5] = vectorbase + 0x0a;
	internal[0x7ff6] = vectorbase + 0x0c;
	internal[0x7ff7] = dest + 0x20; // point boot vector at code in RAM (probably in reality points to internal code that copies the first block)
	internal[0x7ff8] = vectorbase + 0x10;
	internal[0x7ff9] = vectorbase + 0x12;
	internal[0x7ffa] = vectorbase + 0x14;
	internal[0x7ffb] = vectorbase + 0x16;
	internal[0x7ffc] = vectorbase + 0x18;
	internal[0x7ffd] = vectorbase + 0x1a;
	internal[0x7ffe] = vectorbase + 0x1c;
	internal[0x7fff] = vectorbase + 0x1e;
}

} // anonymous namespace

// ぼくはプラレール運転士 新幹線で行こう！プラス  (I am a Plarail driver Let's go by Shinkansen! Plus)
CONS(2015, prailpls, 0, 0, generalplus_gpspispi,         gcm394, generalplus_gpspispi_game_state,         init_spi, "Takara Tomy", "Boku wa Plarail Untenshi Shinkansen de Ikou! Plus (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND) // has built-in screen, but can be connected to a TV

// this is a half-head shaped unit, SHP13017-R1 main PCB  -  アンパンマン レッツゴー！育脳ドライブ きみものれるよ！アンパンマンごう「それいけ！アンパンマン」
CONS(2014, anpanbd,  0, 0, generalplus_gpspispi,         gcm394, generalplus_gpspispi_game_state,         init_spi, "JoyPalette", "Anpanman: Let's Go! Ikunou Drive (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)

//  それいけ！アンパンマン」みんなで！育脳マット
CONS(2015, anpanm15, 0, 0, generalplus_gpspispi,         gcm394, generalplus_gpspispi_game_state,         init_spi, "JoyPalette", "Anpanman: Minnade! Ikunou Mat (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)

CONS(2015, bkrankp,  0, 0, generalplus_gpspispi_bkrankp, gcm394, generalplus_gpspispi_bkrankp_game_state, init_spi, "Bandai", "Karaoke Ranking Party (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)

CONS(2015, pokegach,  0,        0, generalplus_gpspispi_bkrankp, gcm394, generalplus_gpspispi_bkrankp_game_state, init_spi, "Tomy", "Pokegacha (20150902, Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)

// the 2nd release comes in 2 colours and they can communicate?
CONS(2015, pokegac2,  0,        0, generalplus_gpspispi_bkrankp, gcm394, generalplus_gpspispi_bkrankp_game_state, init_spi, "Tomy", "Pokegacha V2 Red (20151230, Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
CONS(2015, pokegac2y, pokegach, 0, generalplus_gpspispi_bkrankp, gcm394, generalplus_gpspispi_bkrankp_game_state, init_spi, "Tomy", "Pokegacha V2 Yellow (20151230, Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)

// This can be found listed as a ZHISHAN / Aojiao / Bornkid 32 Bit Preloaded 139-in-1 Handheld Game Console
// but these just seem to be brands, manufacturer is unknown.
// Various case styles are available, the unit here was styled after a Nintendo Switch
//
// Architecture is unknown, it contains many of the games in beijuehh / bornkidh (generalplus_gpl16250_rom.cpp)
// but is running from SPI flash and has 'Loading' screens between menus and after selecting a game.
//
// While those are GeneralPlus based platforms, it's possible the games were ported to something else, the SPI
// appears to contain a filesystem, but data looks to be compressed / encrypted with no obvious code.
// There is no GPspi header in the SPI ROM.
CONS(202?, bk139in1,  0, 0, generalplus_gpspispi, gcm394, generalplus_gpspispi_game_state, empty_init, "<unknown>", "BornKid 32 Bit Preloaded 139-in-1 Handheld Game Console", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
// same unknown hardware as above, fewer games
CONS(2021, lxcyrace,  0, 0, generalplus_gpspispi, gcm394, generalplus_gpspispi_game_state, empty_init, "Lexibook", "Cyber Arcade Racing (JL3150)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)



generalplus_gpl16250_spi_direct.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/*
    These seem to be GPL16250 related based on video register use
    however the SPI ROM maps directly into CPU space, where you'd
    expect internal ROM to be?!

    It has been confirmed that Pacman/Fix It Felix/Ms Pacman can
    be swapped onto the same PCB, so either there is no internal
    area (runs direct from SPI?) or it's the same between games.
*/

#include "emu.h"
#include "generalplus_gpl16250.h"


namespace {

class generalplus_gpspi_direct_game_state : public gcm394_game_state
{
public:
	generalplus_gpspi_direct_game_state(const machine_config& mconfig, device_type type, const char* tag) :
		gcm394_game_state(mconfig, type, tag)
	{
	}

	void init_fif();

	void generalplus_gpspi_direct(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual uint16_t cs0_r(offs_t offset) override;

private:
};

void generalplus_gpspi_direct_game_state::machine_start()
{
}

void generalplus_gpspi_direct_game_state::machine_reset()
{
	cs_callback(0x00, 0x00, 0x00, 0x00, 0x00);
	m_maincpu->set_cs_space(m_memory->get_program());

	m_maincpu->reset(); // reset CPU so vector gets read etc.

	m_maincpu->set_alt_tile_addressing_hack(1);
}

static INPUT_PORTS_START( bfmpac )
	PORT_START("IN2")
	PORT_DIPNAME( 0x0001, 0x0000, "0" )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0001, DEF_STR( On ) )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_START1 )
	PORT_DIPNAME( 0x0004, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( On ) )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_DIPNAME( 0x0100, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, "Demo Play" ) // the units have a strip of paper that you must rip out to disable display / demo mode, this is probably related to that, have to press start to make it run a demo cycle?
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( On ) )

	PORT_START("IN1")
	PORT_START("IN0")
INPUT_PORTS_END

static INPUT_PORTS_START( bfspyhnt )
	PORT_INCLUDE( bfmpac )

	PORT_MODIFY("IN2")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_BUTTON2 )
INPUT_PORTS_END


uint16_t generalplus_gpspi_direct_game_state::cs0_r(offs_t offset)
{
	// TODO: is cs_space even used by this type?
	return 0x00;
}

void generalplus_gpspi_direct_game_state::generalplus_gpspi_direct(machine_config &config)
{
	GP_SPI_DIRECT(config, m_maincpu, 96000000/2, m_screen);
	m_maincpu->porta_in().set(FUNC(generalplus_gpspi_direct_game_state::porta_r));
	m_maincpu->portb_in().set(FUNC(generalplus_gpspi_direct_game_state::portb_r));
	m_maincpu->portc_in().set(FUNC(generalplus_gpspi_direct_game_state::portc_r));
	m_maincpu->porta_out().set(FUNC(generalplus_gpspi_direct_game_state::porta_w));
	m_maincpu->space_read_callback().set(FUNC(generalplus_gpspi_direct_game_state::read_external_space));
	m_maincpu->space_write_callback().set(FUNC(generalplus_gpspi_direct_game_state::write_external_space));
	m_maincpu->set_irq_acknowledge_callback(m_maincpu, FUNC(sunplus_gcm394_base_device::irq_vector_cb));
	m_maincpu->add_route(ALL_OUTPUTS, "speaker", 0.5, 0);
	m_maincpu->add_route(ALL_OUTPUTS, "speaker", 0.5, 1);
	m_maincpu->set_bootmode(0);
	m_maincpu->set_cs_config_callback(FUNC(gcm394_game_state::cs_callback));

	FULL_MEMORY(config, m_memory).set_map(&generalplus_gpspi_direct_game_state::cs_map_base);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	//m_screen->set_refresh_hz(20); // 20hz update gives more correct speed (and working inputs) in fixitflx and bfdigdug, but speed should probably be limited in some other way
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320*2, 262*2);
	m_screen->set_visarea(0, (320*2)-1, 0, (240*2)-1);
	m_screen->set_screen_update("maincpu", FUNC(sunplus_gcm394_device::screen_update));
	m_screen->screen_vblank().set(m_maincpu, FUNC(sunplus_gcm394_device::vblank));

	SPEAKER(config, "speaker", 2).front();
}

// Is there an internal ROM that gets mapped out or can this type really execute directly from scrambled SPI?
// there doesn't appear to be anywhere to map an internal ROM, nor access to it, so I think they just execute
// from SPI

ROM_START( fixitflx )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION16_BE(0x800000, "maincpu:spidirect", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "fixitfelix_md25q16csig_c84015.bin", 0x0000, 0x200000, CRC(605c6863) SHA1(4f6cc2e8388e20eb90c6b05265273650eeea56eb) )
ROM_END

ROM_START( wiwcs )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION16_BE(0x800000, "maincpu:spidirect", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "wwcs.img", 0x0000, 0x800000, CRC(9b86bc45) SHA1(17721c662642a257d3e0f56e351a9a80d75d9110) )

	ROM_REGION16_BE(0x400, "i2c", ROMREGION_ERASE00)
	ROM_LOAD( "witw_eeprom.bin", 0x0000, 0x400, CRC(9426350b) SHA1(c481ded8b1f61fbf2403532dabb9c0a5c2a33fa2) )
ROM_END

ROM_START( bfpacman )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION16_BE(0x800000, "maincpu:spidirect", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "basicfunpacman_25q80_c84014.bin", 0x0000, 0x100000, CRC(dd39fc64) SHA1(48c0e1eb729f61b7359e1fd52b7faab56817dfe8) )
ROM_END

ROM_START( bfmpac )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION16_BE(0x800000, "maincpu:spidirect", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "mspacman_25q80_c84014.bin", 0x0000, 0x100000, CRC(c0c3f8ce) SHA1(30da9b14f1a2c966167c97da9b8329f2f7f73291) )
ROM_END

ROM_START( bfdigdug )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION16_BE(0x800000, "maincpu:spidirect", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "arcadeclassicsdigdug_25q80csig_c84014.bin", 0x0000, 0x100000, CRC(4030bc46) SHA1(8c086c96b9822e95c1862012786d6d6e59e0387e) )
ROM_END

ROM_START( bfgalaga )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION16_BE(0x800000, "maincpu:spidirect", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "arcadeclassicsgalaga_25q80csig_c84014.bin", 0x0000, 0x100000, CRC(69982c9d) SHA1(0f8f403fefa7d8a9fdfcc04dca5a67919b662c7e) )
ROM_END

ROM_START( bfspyhnt )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION16_BE(0x800000, "maincpu:spidirect", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "arcadeclassicsspyhunter_md25q16csig_c84015.bin", 0x0000, 0x200000, CRC(1f1eaabd) SHA1(1c484e0b0749123cfa1ac6d1959aefa6ed09ab20) )

	// also has a 24C04 (to store high scores?)
ROM_END

ROM_START( bftetris )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	//ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP )

	ROM_REGION16_BE(0x800000, "maincpu:spidirect", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "arcadeclassicstetris_25q16ct_c84015.bin", 0x0000, 0x200000, CRC(a97e1bab) SHA1(400944d310d5d5fccb2c6d048d7bf0cb00da09de) )
ROM_END



ROM_START( punirune )
	ROM_REGION16_BE(0x800000, "maincpu:spidirect", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "25l64.ic103", 0x0000, 0x800000, CRC(0737edc0) SHA1(fce19d91a0522a75e676197fb18645b8c6a273b8) )
ROM_END

ROM_START( punij1m )
	ROM_REGION16_BE(0x800000, "maincpu:spidirect", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "japan_v1pcb_mint_25l6433f.ic103", 0x0000, 0x800000, CRC(76f28b5b) SHA1(be04d60c88df52951dd51eab2f5bf5f1dc2405e8) )
ROM_END

ROM_START( punij1pk ) // this might be the same software revision as punij1m with different save (or default save) data
	ROM_REGION16_BE(0x800000, "maincpu:spidirect", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "japan_v1pcb_pink_25l6433f.ic103", 0x0000, 0x800000, CRC(9268c881) SHA1(10bacfa48b3d02956d804396b652829ff868d947) )
ROM_END

ROM_START( punij1pu ) // different software revision to punij1m / punij1pk but same case style
	ROM_REGION16_BE(0x800000, "maincpu:spidirect", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "japan_v1pcb_purple_25l6433f.ic103", 0x0000, 0x800000, CRC(5b73bcb6) SHA1(109b6fa29693e7622c528d95d2a995d37a1cd8ca) )
ROM_END

ROM_START( punij2pk )
	ROM_REGION16_BE(0x800000, "maincpu:spidirect", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "japan_v2pcb_pink_gpr25l64.ic103", 0x0000, 0x800000, CRC(7ae9f009) SHA1(d762634a0442ff231837f9481a1203933c070df0) )
ROM_END

ROM_START( punifrnd )
	ROM_REGION16_BE(0x800000, "maincpu:spidirect", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "25oh64.ic3", 0x0000, 0x800000, CRC(622ca9b3) SHA1(4206393a4458ffcdb63352e743481865532fe8b5) )
ROM_END

ROM_START( pokgoget )
	ROM_REGION16_BE(0x2000000, "maincpu:spidirect", ROMREGION_ERASE00)
	ROM_LOAD16_WORD_SWAP( "mx25l25645g.u1", 0x0000, 0x2000000, CRC(a76ae22f) SHA1(3fa5eeedb3fe343a7707d76710298377b22b0681) )
ROM_END

ROM_START( smkcatch )
	ROM_REGION16_BE(0x800000, "maincpu:spidirect", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "gpr25l64.u2", 0x0000, 0x800000,  CRC(e2f52c4a) SHA1(f79862d27152cff8f96151c672d9762a3897a593) )
ROM_END

ROM_START( dsgnpal )
	ROM_REGION16_BE(0x800000, "maincpu:spidirect", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "gpr25l64.ic2", 0x0000, 0x800000, CRC(a1017ea8) SHA1(bd4b553ff71e763cd3fd726c49f5408eac3b7984) )
ROM_END


void generalplus_gpspi_direct_game_state::init_fif()
{
	uint16_t* spirom16 = (uint16_t*)memregion("maincpu:spidirect")->base();
	for (int i = 0; i < 0x800000 / 2; i++)
	{
		spirom16[i] = bitswap<16>(spirom16[i] ^ 0xdd0d,
			3, 1, 11, 9, 6, 14, 0, 2, 8, 7, 13, 15, 4, 5, 12, 10);
	}

	// the games upload some self-check code to 0x100 in RAM, it's unclear what it is checking, skip it for now
	// goto mr -> nop
	if (spirom16[0x00d8] == 0xf161) spirom16[0x00d8] = 0xf165; // fixitflx, bfpacman, bfmpac
	if (spirom16[0x00ac] == 0xf161) spirom16[0x00ac] = 0xf165; // wiwcs, bfgalaga, bfdigdug, bfspyhnt
	if (spirom16[0x00a2] == 0xf161) spirom16[0x00a2] = 0xf165; // bftetris
}

} // anonymous namespace

CONS(2017, fixitflx, 0, 0, generalplus_gpspi_direct, bfmpac, generalplus_gpspi_direct_game_state, init_fif, "Basic Fun", "Fix It Felix Jr. (mini arcade)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
CONS(2018, wiwcs,    0, 0, generalplus_gpspi_direct, bfmpac, generalplus_gpspi_direct_game_state, init_fif, "Basic Fun", "Where in the World Is Carmen Sandiego? (handheld)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
CONS(2018, bfpacman, 0, 0, generalplus_gpspi_direct, bfmpac, generalplus_gpspi_direct_game_state, init_fif, "Basic Fun", "Pac-Man (mini arcade)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
CONS(2017, bfmpac,   0, 0, generalplus_gpspi_direct, bfmpac, generalplus_gpspi_direct_game_state, init_fif, "Basic Fun", "Ms. Pac-Man (mini arcade)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
CONS(2017, bfgalaga, 0, 0, generalplus_gpspi_direct, bfmpac, generalplus_gpspi_direct_game_state, init_fif, "Basic Fun", "Galaga (mini arcade)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
CONS(2018, bfdigdug, 0, 0, generalplus_gpspi_direct, bfmpac, generalplus_gpspi_direct_game_state, init_fif, "Basic Fun", "Dig Dug (mini arcade)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
CONS(2019, bfspyhnt, 0, 0, generalplus_gpspi_direct, bfspyhnt, generalplus_gpspi_direct_game_state, init_fif, "Basic Fun", "Spy Hunter (mini arcade)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
CONS(2019, bftetris, 0, 0, generalplus_gpspi_direct, bfspyhnt, generalplus_gpspi_direct_game_state, init_fif, "Basic Fun", "Tetris (mini arcade)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)

// games below use GPL95101 series chips, which might be different but are definitely unSP2.0 chips that run from SPI directly

// unclear if colour matches, but there are multiple generations of these at least
// uses PUNIRUNZU_MAIN_V3 pcb
CONS(2021, punirune, 0, 0, generalplus_gpspi_direct, bfspyhnt, generalplus_gpspi_direct_game_state, empty_init, "Takara Tomy", "Punirunes (PUNIRUNZU_MAIN_V3, pastel blue, Europe)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)

// the case on these looks like the European release, including English title logo.  CPU is a glob, PUNIRUNZU_MAIN_DICE_V1 on PCB
CONS(2021, punij1m,  punirune, 0, generalplus_gpspi_direct, bfspyhnt, generalplus_gpspi_direct_game_state, empty_init, "Takara Tomy", "Punirunes (PUNIRUNZU_MAIN_DICE_V1, mint, Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
CONS(2021, punij1pk, punirune, 0, generalplus_gpspi_direct, bfspyhnt, generalplus_gpspi_direct_game_state, empty_init, "Takara Tomy", "Punirunes (PUNIRUNZU_MAIN_DICE_V1, pink, Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
CONS(2021, punij1pu, punirune, 0, generalplus_gpspi_direct, bfspyhnt, generalplus_gpspi_direct_game_state, empty_init, "Takara Tomy", "Punirunes (PUNIRUNZU_MAIN_DICE_V1, purple, Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)

// the case on these is similar to the above, but the text is in Japanese, uses PUNIRUNZU_MAIN_V2 on pcb
CONS(2021, punij2pk, punirune, 0, generalplus_gpspi_direct, bfspyhnt, generalplus_gpspi_direct_game_state, empty_init, "Takara Tomy", "Punirunes (PUNIRUNZU_MAIN_V2, pink, Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)

// has a link feature
CONS(2021, punifrnd, 0,        0, generalplus_gpspi_direct, bfspyhnt, generalplus_gpspi_direct_game_state, empty_init, "Takara Tomy", "Punirunes Punitomo Tsuushin (hot pink, Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)

// Pocket Monsters ガチッとゲットだぜ! モンスターボールゴー! - Pocket Monsters is printed on the inner shell, but not the box?
CONS(2021, pokgoget, 0,        0, generalplus_gpspi_direct, bfspyhnt, generalplus_gpspi_direct_game_state, empty_init, "Takara Tomy", "Gachitto Get da ze! Monster Ball Go! (210406, Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)


// 2020 (device) / 2021 (box) version of Sumikko Gurashi a cloud shaped device
// Sumikko Gurashi - Sumikko Catch (すみっコぐらし すみっコキャッチ)
CONS( 2021, smkcatch, 0, 0, generalplus_gpspi_direct, bfmpac, generalplus_gpspi_direct_game_state, empty_init,  "San-X / Tomy", "Sumikko Gurashi - Sumikko Catch (Japan)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
// or Sumikko Gurashi - Sumikko Catch DX (すみっコぐらし すみっコキャッチDX) = Sumikko Catch with pouch and strap

// there seem to be different versions of this available, is the software the same?
CONS( 201?, dsgnpal, 0, 0, generalplus_gpspi_direct, bfmpac, generalplus_gpspi_direct_game_state, empty_init,  "Tomy", "Kiratto Pri-Chan Design Palette (Japan)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



generalplus_gpl162xx_lcdtype.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

/* These contain a similar game selection to the devices in unk6502_st2xxx.cpp but on updated hardware

   The hardware appears to be an abuse of the GPL16250 SoC. The palette and sprite banks are used, but as work-ram
   rather than for their intended purpose, and the rest of the GPL16250 video hardware is either entirely bypassed
   or doesn't exist.  All video is software rendered and output directly to the LCD Controller.

   If this is confirmed via a decap then this should be merged with the GPL16250 implementation.

   The coding of these is similar to the unk6502_st2xxx.cpp too, with all game specific function calls being loaded
   on the fly from the SPI to a tiny portion of work RAM, with graphics likewise being loaded and decompressed for
   every draw call.

   pcp8718 / pcp8728 / bkid218
   to access test mode hold up + left on startup.
   (bkid218 cursor in test menu can't be moved in emulation, works on unit, why?)
*/

// TODO: convert to use generic_spi_flash.cpp (but there seems to be some buffering of writes / reads?)

#include "emu.h"

#include "cpu/unsp/unsp.h"
#include "machine/bl_handhelds_menucontrol.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#define LOG_GPL162XX_LCDTYPE            (1U << 1)
#define LOG_GPL162XX_LCDTYPE_SELECT_SIM (1U << 2)
#define LOG_GPL162XX_LCDTYPE_IO_PORT    (1U << 3)

#define LOG_ALL             (LOG_GPL162XX_LCDTYPE | LOG_GPL162XX_LCDTYPE_SELECT_SIM | LOG_GPL162XX_LCDTYPE_IO_PORT)
#define VERBOSE             (0)

#include "logmacro.h"


namespace {

class gpl162xx_lcdtype_state : public driver_device
{
public:
	gpl162xx_lcdtype_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_mainrom(*this, "maincpu"),
		m_mainram(*this, "mainram"),
		m_palette(*this, "palette"),
		m_screen(*this, "screen"),
		m_spirom(*this, "spi"),
		m_io_in0(*this, "IN0"),
		m_io_in1(*this, "IN1"),
		m_menucontrol(*this, "menucontrol")
	{ }

	void gpl162xx_lcdtype(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	required_device<unsp_20_device> m_maincpu;
	required_region_ptr<uint16_t> m_mainrom;
	required_shared_ptr<uint16_t> m_mainram;

	required_device<palette_device> m_palette;
	required_device<screen_device> m_screen;

	void map(address_map &map) ATTR_COLD;

	required_region_ptr<uint8_t> m_spirom;

	uint16_t unk_7abf_r();
	uint16_t io_7860_r();
	uint16_t unk_780f_r();

	void io_7860_w(uint16_t data);
	uint16_t m_7860;

	void unk_7862_w(uint16_t data);
	void unk_7863_w(uint16_t data);

	void unk_7868_w(uint16_t data);
	uint16_t unk_7868_r();
	uint16_t m_7868;

	void bankswitch_707e_w(uint16_t data);
	uint16_t bankswitch_707e_r();
	uint16_t m_707e_bank;

	void bankswitch_703a_w(uint16_t data);
	uint16_t bankswitch_703a_r();
	uint16_t m_703a_bank;

	void bankedram_7300_w(offs_t offset, uint16_t data);
	uint16_t bankedram_7300_r(offs_t offset);
	uint16_t m_bankedram_7300[0x400];

	void bankedram_7400_w(offs_t offset, uint16_t data);
	uint16_t bankedram_7400_r(offs_t offset);
	uint16_t m_bankedram_7400[0x800];

	void system_dma_params_channel0_w(offs_t offset, uint16_t data);
	uint16_t system_dma_params_channel0_r(offs_t offset);

	uint16_t m_dmaregs[8];

	void lcd_w(uint16_t data);
	void lcd_command_w(uint16_t data);

	uint16_t spi_misc_control_r();
	uint16_t spi_rx_fifo_r();
	void spi_tx_fifo_w(uint16_t data);

	void spi_control_w(uint16_t data);

	void spi_process_tx_data(uint8_t data);
	uint8_t spi_process_rx();
	uint8_t spi_rx();
	uint8_t spi_rx_fast();

	uint8_t m_rx_fifo[5]; // actually 8 bytes? or 8 half-bytes?

	uint32_t m_spiaddress;

	uint16_t unk_78a1_r();
	uint16_t m_78a1;
	uint16_t unk_78d8_r();
	void unk_78d8_w(uint16_t data);

	enum spistate : int
	{
	   SPI_STATE_READY = 0,
	   SPI_STATE_WAITING_HIGH_ADDR = 1,
	   SPI_STATE_WAITING_MID_ADDR = 2,
	   SPI_STATE_WAITING_LOW_ADDR = 3,
	   // probably not
	   SPI_STATE_WAITING_DUMMY1_ADDR = 4,
	   SPI_STATE_WAITING_DUMMY2_ADDR = 5,
	   SPI_STATE_READING = 6,

	   SPI_STATE_WAITING_HIGH_ADDR_FAST = 8,
	   SPI_STATE_WAITING_MID_ADDR_FAST = 9,
	   SPI_STATE_WAITING_LOW_ADDR_FAST = 10,
	   SPI_STATE_WAITING_LOW_ADDR_FAST_DUMMY = 11,
	   SPI_STATE_READING_FAST = 12

	};

	spistate m_spistate;

	enum lcdstate : int
	{
		LCD_STATE_READY = 0,
		LCD_STATE_WAITING_FOR_COMMAND = 1,
		LCD_STATE_PROCESSING_COMMAND = 2
	};

	lcdstate m_lcdstate;
	int m_lastlcdcommand;

	uint8_t m_displaybuffer[0x40000];
	int m_lcdaddr;

	uint16_t io_7870_r();
	required_ioport m_io_in0;
	required_ioport m_io_in1;
	required_device<bl_handhelds_menucontrol_device> m_menucontrol;

	void screen_vblank(int state);
};

uint32_t gpl162xx_lcdtype_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	int count = 0;
	for (int y = 0; y < 256; y++)
	{
		uint32_t *const dst = &bitmap.pix(y);

		for (int x = 0; x < 320; x++)
		{
			// 8-bit values get pumped through a 256 word table in internal ROM and converted to words
			uint16_t dat = m_displaybuffer[(count * 2) + 1] | (m_displaybuffer[(count * 2) + 0] << 8);

			int b = ((dat >> 0) & 0x1f) << 3;
			int g = ((dat >> 5) & 0x3f) << 2;
			int r = ((dat >> 11) & 0x1f) << 3;

			dst[x] = (r << 16) | (g << 8) | (b << 0);
			count++;
		}
	}

	return 0;
}


static INPUT_PORTS_START( gpl162xx_lcdtype )
	PORT_START("IN0")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_UNUSED ) // causes lag if state is inverted, investigate
	PORT_DIPNAME( 0x0002, 0x0002, "P0:0002" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0002, "0002" )
	PORT_DIPNAME( 0x0004, 0x0000, "Show Vs in Test Mode" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0004, "0004" )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("menucontrol", FUNC(bl_handhelds_menucontrol_device::status_r))
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("menucontrol", FUNC(bl_handhelds_menucontrol_device::data_r))
	PORT_DIPNAME( 0x0020, 0x0020, "P0:0020" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0020, "0020" )
	PORT_DIPNAME( 0x0040, 0x0040, "P0:0040" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0040, "0040" )
	PORT_DIPNAME( 0x0080, 0x0080, "P0:0080" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0080, "0080" )
	PORT_DIPNAME( 0x0100, 0x0100, "P0:0100" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0100, "0100" )
	PORT_DIPNAME( 0x0200, 0x0000, "Battery Level" )
	PORT_DIPSETTING(      0x0000, "Normal" )
	PORT_DIPSETTING(      0x0200, "Low" )
	PORT_DIPNAME( 0x0400, 0x0400, "P0:0400" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0400, "0400" )
	PORT_DIPNAME( 0x0800, 0x0800, "P0:0800" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0800, "0800" )
	PORT_DIPNAME( 0x1000, 0x1000, "P0:1000" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x1000, "1000" )
	PORT_DIPNAME( 0x2000, 0x2000, "P0:2000" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x2000, "2000" )
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )

	PORT_START("IN1")
	PORT_DIPNAME( 0x0001, 0x0001, "P1:0001" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0001, "0001" )
	PORT_DIPNAME( 0x0002, 0x0002, "P1:0002" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0002, "0002" )
	PORT_DIPNAME( 0x0004, 0x0004, "P1:0004" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0004, "0004" )
	PORT_DIPNAME( 0x0008, 0x0008, "P1:0008" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0008, "0008" )
	PORT_DIPNAME( 0x0010, 0x0010, "P1:0010" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0010, "0010" )
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("SOUND")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("A")
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("B")
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("ON/OFF")
	PORT_DIPNAME( 0x1000, 0x1000, "P1:1000" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x1000, "1000" )
	PORT_DIPNAME( 0x2000, 0x2000, "P1:2000" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x2000, "2000" )
	PORT_DIPNAME( 0x4000, 0x4000, "P1:4000" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x4000, "4000" )
	PORT_DIPNAME( 0x8000, 0x8000, "P1:8000" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x8000, "8000" )
INPUT_PORTS_END

uint16_t gpl162xx_lcdtype_state::unk_7abf_r()
{
	return 0x0001;
}

uint16_t gpl162xx_lcdtype_state::io_7860_r()
{
	uint16_t ret = m_io_in0->read();
	LOGMASKED(LOG_GPL162XX_LCDTYPE_IO_PORT, "%s: io_7860_r %02x\n", machine().describe_context(), ret);
	return ret;
}

void gpl162xx_lcdtype_state::io_7860_w(uint16_t data)
{
	m_menucontrol->data_w((data & 0x10)>>4);
	m_menucontrol->clock_w((data & 0x20)>>5);

	m_7860 = data;
}

void gpl162xx_lcdtype_state::unk_7862_w(uint16_t data)
{
}

void gpl162xx_lcdtype_state::unk_7863_w(uint16_t data)
{
	// probably port direction (or 7862 is?)
	if (data == 0x3cf7)
	{
		m_menucontrol->reset_w(1);
	}
}

uint16_t gpl162xx_lcdtype_state::unk_780f_r()
{
	return 0x0002;
}

uint16_t gpl162xx_lcdtype_state::spi_misc_control_r()
{
	LOGMASKED(LOG_GPL162XX_LCDTYPE,"%s: spi_misc_control_r\n", machine().describe_context());
	return 0x0000;
}

uint16_t gpl162xx_lcdtype_state::spi_rx_fifo_r()
{
	if (m_spistate == SPI_STATE_READING_FAST)
		return spi_rx_fast();

	LOGMASKED(LOG_GPL162XX_LCDTYPE,"%s: spi_rx_fifo_r\n", machine().describe_context());
	return spi_rx();
}

void gpl162xx_lcdtype_state::spi_process_tx_data(uint8_t data)
{
	LOGMASKED(LOG_GPL162XX_LCDTYPE,"transmitting %02x\n", data);

	switch (m_spistate)
	{
	case SPI_STATE_READY:
	{
		if (data == 0x03)
		{
			LOGMASKED(LOG_GPL162XX_LCDTYPE,"set to read mode (need address) %02x\n", data);
			m_spistate = SPI_STATE_WAITING_HIGH_ADDR;
		}
		else if (data == 0x0b)
		{
			LOGMASKED(LOG_GPL162XX_LCDTYPE,"set to fast read mode (need address) %02x\n", data);
			m_spistate = SPI_STATE_WAITING_HIGH_ADDR_FAST;
			//machine().debug_break();
		}
		else
		{
			LOGMASKED(LOG_GPL162XX_LCDTYPE,"invalid state request %02x\n", data);
		}
		break;
	}

	case SPI_STATE_WAITING_HIGH_ADDR:
	{
		m_spiaddress = (m_spiaddress & 0xff00ffff) | data << 16;
		LOGMASKED(LOG_GPL162XX_LCDTYPE,"set to high address %02x address is now %08x\n", data, m_spiaddress);
		m_spistate = SPI_STATE_WAITING_MID_ADDR;
		break;
	}

	case SPI_STATE_WAITING_MID_ADDR:
	{
		m_spiaddress = (m_spiaddress & 0xffff00ff) | data << 8;
		LOGMASKED(LOG_GPL162XX_LCDTYPE,"set to mid address %02x address is now %08x\n", data, m_spiaddress);
		m_spistate = SPI_STATE_WAITING_LOW_ADDR;
		break;
	}

	case SPI_STATE_WAITING_LOW_ADDR:
	{
		m_spiaddress = (m_spiaddress & 0xffffff00) | data;
		LOGMASKED(LOG_GPL162XX_LCDTYPE,"set to low address %02x address is now %08x\n", data, m_spiaddress);
		m_spistate = SPI_STATE_READING;
		break;
	}

	case SPI_STATE_READING:
	{
		// writes when in read mode clock in data?
		LOGMASKED(LOG_GPL162XX_LCDTYPE,"write while in read mode (clock data?)\n", data, m_spiaddress);
		break;
	}

	case SPI_STATE_WAITING_DUMMY1_ADDR:
	{
		m_spistate = SPI_STATE_WAITING_DUMMY2_ADDR;
		break;
	}

	case SPI_STATE_WAITING_DUMMY2_ADDR:
	{
	//  m_spistate = SPI_STATE_READY;
		break;
	}


	case SPI_STATE_WAITING_HIGH_ADDR_FAST:
	{
		m_spiaddress = (m_spiaddress & 0xff00ffff) | data << 16;
		LOGMASKED(LOG_GPL162XX_LCDTYPE,"set to high address %02x address is now %08x\n", data, m_spiaddress);
		m_spistate = SPI_STATE_WAITING_MID_ADDR_FAST;
		break;
	}

	case SPI_STATE_WAITING_MID_ADDR_FAST:
	{
		m_spiaddress = (m_spiaddress & 0xffff00ff) | data << 8;
		LOGMASKED(LOG_GPL162XX_LCDTYPE,"set to mid address %02x address is now %08x\n", data, m_spiaddress);
		m_spistate = SPI_STATE_WAITING_LOW_ADDR_FAST;
		break;
	}

	case SPI_STATE_WAITING_LOW_ADDR_FAST:
	{
		m_spiaddress = (m_spiaddress & 0xffffff00) | data;
		LOGMASKED(LOG_GPL162XX_LCDTYPE,"set to low address %02x address is now %08x\n", data, m_spiaddress);
		m_spistate = SPI_STATE_WAITING_LOW_ADDR_FAST_DUMMY;

		break;
	}

	case SPI_STATE_WAITING_LOW_ADDR_FAST_DUMMY:
	{
		LOGMASKED(LOG_GPL162XX_LCDTYPE,"dummy write %08x\n", data, m_spiaddress);
		m_spistate = SPI_STATE_READING_FAST;
		break;
	}

	case SPI_STATE_READING_FAST:
	{
		// writes when in read mode clock in data?
		LOGMASKED(LOG_GPL162XX_LCDTYPE,"write while in read mode (clock data?)\n", data, m_spiaddress);
		break;
	}

	}
}

uint8_t gpl162xx_lcdtype_state::spi_process_rx()
{

	switch (m_spistate)
	{
	case SPI_STATE_READING:
	case SPI_STATE_READING_FAST:
	{
		uint8_t dat = m_spirom[m_spiaddress & 0x7fffff];

		LOGMASKED(LOG_GPL162XX_LCDTYPE,"reading SPI %02x from SPI Address %08x (adjusted word offset %08x)\n", dat, m_spiaddress, (m_spiaddress/2)+0x20000);
		m_spiaddress++;
		return dat;
	}

	default:
	{
		LOGMASKED(LOG_GPL162XX_LCDTYPE,"reading FIFO in unknown state\n");
		return 0x00;
	}
	}

	return 0x00;
}


uint8_t gpl162xx_lcdtype_state::spi_rx()
{
	uint8_t ret = m_rx_fifo[0];

	m_rx_fifo[0] = m_rx_fifo[1];
	m_rx_fifo[1] = m_rx_fifo[2];
	m_rx_fifo[2] = m_rx_fifo[3];
	m_rx_fifo[3] = spi_process_rx();

	return ret;
}

uint8_t gpl162xx_lcdtype_state::spi_rx_fast()
{
	uint8_t ret = m_rx_fifo[0];

	m_rx_fifo[0] = m_rx_fifo[1];
	m_rx_fifo[1] = m_rx_fifo[2];
	m_rx_fifo[2] = m_rx_fifo[3];
	m_rx_fifo[3] = m_rx_fifo[4];
	m_rx_fifo[4] = spi_process_rx();

	return ret;
}

void gpl162xx_lcdtype_state::spi_tx_fifo_w(uint16_t data)
{
	data &= 0x00ff;
	LOGMASKED(LOG_GPL162XX_LCDTYPE,"%s: spi_tx_fifo_w %04x\n", machine().describe_context(), data);

	spi_process_tx_data(data);
}

// this is probably 'port b' but when SPI is enabled some points of this can become SPI control pins
// it's accessed after each large data transfer, probably to reset the SPI into 'ready for command' state?
void gpl162xx_lcdtype_state::unk_7868_w(uint16_t data)
{
	LOGMASKED(LOG_GPL162XX_LCDTYPE_IO_PORT, "%s: unk_7868_w %04x (Port B + SPI reset?)\n", machine().describe_context(), data);

	if ((m_7868 & 0x0100) != (data & 0x0100))
	{
		if (!(data & 0x0100))
		{
			for (int i = 0; i < 4; i++)
				m_rx_fifo[i] = 0xff;

			m_spistate = SPI_STATE_READY;
		}
	}

	m_7868 = data;
}

uint16_t gpl162xx_lcdtype_state::unk_7868_r()
{
	return m_7868;
}

void gpl162xx_lcdtype_state::bankswitch_707e_w(uint16_t data)
{
	LOGMASKED(LOG_GPL162XX_LCDTYPE,"%s: bankswitch_707e_w %04x\n", machine().describe_context(), data);
	m_707e_bank = data;
}

uint16_t gpl162xx_lcdtype_state::bankswitch_707e_r()
{
	return m_707e_bank;
}


void gpl162xx_lcdtype_state::bankswitch_703a_w(uint16_t data)
{
	LOGMASKED(LOG_GPL162XX_LCDTYPE,"%s: bankswitch_703a_w %04x\n", machine().describe_context(), data);
	m_703a_bank = data;
}

uint16_t gpl162xx_lcdtype_state::bankswitch_703a_r()
{
	return m_703a_bank;
}

void gpl162xx_lcdtype_state::bankedram_7300_w(offs_t offset, uint16_t data)
{
	offset |= (m_703a_bank & 0x000c) << 6;
	m_bankedram_7300[offset] = data;
}

uint16_t gpl162xx_lcdtype_state::bankedram_7300_r(offs_t offset)
{
	offset |= (m_703a_bank & 0x000c) << 6;
	return m_bankedram_7300[offset];
}

void gpl162xx_lcdtype_state::bankedram_7400_w(offs_t offset, uint16_t data)
{
	if (m_707e_bank & 1)
	{
		m_bankedram_7400[offset + 0x400] = data;
	}
	else
	{
		m_bankedram_7400[offset] = data;
	}
}

uint16_t gpl162xx_lcdtype_state::bankedram_7400_r(offs_t offset)
{
	if (m_707e_bank & 1)
	{
		return m_bankedram_7400[offset + 0x400];
	}
	else
	{
		return m_bankedram_7400[offset];
	}
}

void gpl162xx_lcdtype_state::system_dma_params_channel0_w(offs_t offset, uint16_t data)
{
	m_dmaregs[offset] = data;

	switch (offset)
	{
		case 0:
		{
			LOGMASKED(LOG_GPL162XX_LCDTYPE,"%s: system_dma_params_channel0_w %01x %04x (DMA Mode)\n", machine().describe_context(), offset, data);

			uint16_t mode = m_dmaregs[0];
			uint32_t source = m_dmaregs[1] | (m_dmaregs[4] << 16);
			uint32_t dest = m_dmaregs[2] | (m_dmaregs[5] << 16) ;
			uint32_t length = m_dmaregs[3] | (m_dmaregs[6] << 16);

			if ((mode != 0x0200) && (mode != 0x4009) && (mode != 0x6009))
				fatalerror("unknown dma mode write %04x\n", data);

			if ((mode == 0x4009) || (mode == 0x6009))
			{
				address_space& mem = m_maincpu->space(AS_PROGRAM);

				for (int i = 0; i < length; i++)
				{
					uint16_t dat = mem.read_word(source);

					if (mode & 0x2000)
					{
						// Racing Car and Elevator Action need this logic
						mem.write_word(dest, dat & 0xff);
						dest++;
						mem.write_word(dest, dat >> 8);
						dest++;
					}
					else
					{
						mem.write_word(dest, dat);
						dest++;
					}

					source++;
				}
			}

			break;
		}
		case 1:
			LOGMASKED(LOG_GPL162XX_LCDTYPE,"%s: system_dma_params_channel0_w %01x %04x (DMA Source Low)\n", machine().describe_context(), offset, data);
			break;

		case 2:
			LOGMASKED(LOG_GPL162XX_LCDTYPE,"%s: system_dma_params_channel0_w %01x %04x (DMA Dest Low)\n", machine().describe_context(), offset, data);
			break;

		case 3:
			LOGMASKED(LOG_GPL162XX_LCDTYPE,"%s: system_dma_params_channel0_w %01x %04x (DMA Length Low)\n", machine().describe_context(), offset, data);
			break;

		case 4:
			LOGMASKED(LOG_GPL162XX_LCDTYPE,"%s: system_dma_params_channel0_w %01x %04x (DMA Source High)\n", machine().describe_context(), offset, data);
			break;

		case 5:
			LOGMASKED(LOG_GPL162XX_LCDTYPE,"%s: system_dma_params_channel0_w %01x %04x (DMA Dest High)\n", machine().describe_context(), offset, data);
			break;

		case 6:
			LOGMASKED(LOG_GPL162XX_LCDTYPE,"%s: system_dma_params_channel0_w %01x %04x (DMA Length High)\n", machine().describe_context(), offset, data);
			break;

		case 7:
			LOGMASKED(LOG_GPL162XX_LCDTYPE,"%s: system_dma_params_channel0_w %01x %04x (DMA unknown)\n", machine().describe_context(), offset, data);
			break;
	}
}

uint16_t gpl162xx_lcdtype_state::system_dma_params_channel0_r(offs_t offset)
{
	LOGMASKED(LOG_GPL162XX_LCDTYPE,"%s: system_dma_params_channel0_r %01x\n", machine().describe_context(), offset);
	return m_dmaregs[offset];
}

uint16_t gpl162xx_lcdtype_state::io_7870_r()
{
	LOGMASKED(LOG_GPL162XX_LCDTYPE_IO_PORT, "%s: io_7870_r (IO port)\n", machine().describe_context() );
	return m_io_in1->read();
}

void gpl162xx_lcdtype_state::spi_control_w(uint16_t data)
{
	LOGMASKED(LOG_GPL162XX_LCDTYPE,"%s: spi_control_w %04x\n", machine().describe_context(), data);
}

uint16_t gpl162xx_lcdtype_state::unk_78a1_r()
{
	// checked in interrupt, code skipped entirely if this isn't set
	return m_78a1;
}

uint16_t gpl162xx_lcdtype_state::unk_78d8_r()
{
	return 0xffff;
}

void gpl162xx_lcdtype_state::unk_78d8_w(uint16_t data)
{
	// written in IRQ, possible ack
	if (data & 0x8000)
		m_78a1 &= ~0x8000;
}


void gpl162xx_lcdtype_state::map(address_map &map)
{
	// there are calls to 01xxx and 02xxx regions
	// (RAM populated by internal ROM?, TODO: check to make sure code copied there isn't from SPI ROM like the GPL16250 bootstrap
	//  does from NAND, it doesn't seem to have a header in the same format at least)
	map(0x000000, 0x006fff).ram().share("mainram");

	// registers at 7xxx are similar to GPL16250, but not identical? (different video system? or just GPL16250 with the video part unused?)

	map(0x00703a, 0x00703a).rw(FUNC(gpl162xx_lcdtype_state::bankswitch_703a_r), FUNC(gpl162xx_lcdtype_state::bankswitch_703a_w));
	map(0x00707e, 0x00707e).rw(FUNC(gpl162xx_lcdtype_state::bankswitch_707e_r), FUNC(gpl162xx_lcdtype_state::bankswitch_707e_w));

	map(0x007100, 0x0071ff).ram(); // rowscroll on gpl16250
	map(0x007300, 0x0073ff).rw(FUNC(gpl162xx_lcdtype_state::bankedram_7300_r), FUNC(gpl162xx_lcdtype_state::bankedram_7300_w)); // palette on gpl16250
	map(0x007400, 0x0077ff).rw(FUNC(gpl162xx_lcdtype_state::bankedram_7400_r), FUNC(gpl162xx_lcdtype_state::bankedram_7400_w)); // spriteram on gpl16250

	map(0x00780f, 0x00780f).r(FUNC(gpl162xx_lcdtype_state::unk_780f_r));


	map(0x007860, 0x007860).rw(FUNC(gpl162xx_lcdtype_state::io_7860_r),FUNC(gpl162xx_lcdtype_state::io_7860_w)); // Port A?
	map(0x007862, 0x007862).w(FUNC(gpl162xx_lcdtype_state::unk_7862_w));
	map(0x007863, 0x007863).w(FUNC(gpl162xx_lcdtype_state::unk_7863_w));

	map(0x007868, 0x007868).rw(FUNC(gpl162xx_lcdtype_state::unk_7868_r), FUNC(gpl162xx_lcdtype_state::unk_7868_w));  // Port B?

	map(0x007870, 0x007870).r(FUNC(gpl162xx_lcdtype_state::io_7870_r)); // Port C?

	map(0x0078a1, 0x0078a1).r(FUNC(gpl162xx_lcdtype_state::unk_78a1_r));

	map(0x0078d8, 0x0078d8).rw(FUNC(gpl162xx_lcdtype_state::unk_78d8_r), FUNC(gpl162xx_lcdtype_state::unk_78d8_w));


	map(0x007940, 0x007940).w(FUNC(gpl162xx_lcdtype_state::spi_control_w));
	// 7941 SPI Transmit Status
	map(0x007942, 0x007942).w(FUNC(gpl162xx_lcdtype_state::spi_tx_fifo_w));
	// 7943 SPI Receive Status
	map(0x007944, 0x007944).r(FUNC(gpl162xx_lcdtype_state::spi_rx_fifo_r));
	map(0x007945, 0x007945).r(FUNC(gpl162xx_lcdtype_state::spi_misc_control_r));

	map(0x007a80, 0x007a87).rw(FUNC(gpl162xx_lcdtype_state::system_dma_params_channel0_r), FUNC(gpl162xx_lcdtype_state::system_dma_params_channel0_w));

	map(0x007abf, 0x007abf).r(FUNC(gpl162xx_lcdtype_state::unk_7abf_r));

	// there are calls to 0x0f000 (internal ROM?)
	map(0x00f000, 0x00ffff).rom().region("maincpu", 0x00000);

	// external LCD controller
	map(0x200000, 0x200000).w(FUNC(gpl162xx_lcdtype_state::lcd_command_w));
	map(0x20fc00, 0x20fc00).w(FUNC(gpl162xx_lcdtype_state::lcd_w));
}

void gpl162xx_lcdtype_state::lcd_command_w(uint16_t data)
{
	data &= 0xff;

	switch (m_lcdstate)
	{
	case LCD_STATE_READY:
	case LCD_STATE_PROCESSING_COMMAND:
	{
		if (data == 0x0000)
		{
			m_lcdstate = LCD_STATE_WAITING_FOR_COMMAND;
			m_lastlcdcommand = 0;
		}
		break;
	}

	case LCD_STATE_WAITING_FOR_COMMAND:
	{
		m_lastlcdcommand = data;
		m_lcdstate = LCD_STATE_PROCESSING_COMMAND;
		break;
	}

	}
}

void gpl162xx_lcdtype_state::lcd_w(uint16_t data)
{
	data &= 0xff; // definitely looks like 8-bit port as 16-bit values are shifted and rewritten
	LOGMASKED(LOG_GPL162XX_LCDTYPE,"%s: lcd_w %02x\n", machine().describe_context(), data);

	if ((m_lcdstate == LCD_STATE_PROCESSING_COMMAND) && (m_lastlcdcommand == 0x22))
	{
		m_displaybuffer[m_lcdaddr] = data;
		m_lcdaddr++;

		if (m_lcdaddr >= ((320 * 240) * 2))
			m_lcdaddr = 0;

		//m_lcdaddr &= 0x3ffff;
	}
}


void gpl162xx_lcdtype_state::machine_start()
{
}


void gpl162xx_lcdtype_state::machine_reset()
{
	m_spistate = SPI_STATE_READY;
	m_spiaddress = 0;

	for (int i = 0; i < 320*240*2; i++)
		m_displaybuffer[i] = 0x00;

	m_lcdaddr = 0;

	m_lcdstate = LCD_STATE_READY;
	m_lastlcdcommand = 0;

	m_78a1 = 0;

// first menu index is stored here
//  m_spirom[0x16000] = gamenum & 0xff;
//  m_spirom[0x16001] = (gamenum>>8) & 0xff;
}

void gpl162xx_lcdtype_state::screen_vblank(int state)
{
	if (state)
	{
		// probably a timer
		m_maincpu->set_input_line(UNSP_IRQ4_LINE, ASSERT_LINE);
		m_78a1 |= 0x8000;
	}
	else
	{
		m_maincpu->set_input_line(UNSP_IRQ4_LINE, CLEAR_LINE);
	}
}

void gpl162xx_lcdtype_state::gpl162xx_lcdtype(machine_config &config)
{

	UNSP_20(config, m_maincpu, 96000000); // unknown CPU, unsp20 based, 96Mhz is listed as the maximum for most unSP2.0 chips, and appears correct here
	m_maincpu->set_addrmap(AS_PROGRAM, &gpl162xx_lcdtype_state::map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(10));
	m_screen->set_size(64*8, 32*8);
	m_screen->set_visarea(0*8, 320-1, 0*8, 240-1);
	m_screen->set_screen_update(FUNC(gpl162xx_lcdtype_state::screen_update));
	m_screen->screen_vblank().set(FUNC(gpl162xx_lcdtype_state::screen_vblank));

	PALETTE(config, m_palette).set_format(palette_device::xBGR_555, 0x8000);

	BL_HANDHELDS_MENUCONTROL(config, m_menucontrol, 0);
	m_menucontrol->set_is_unsp_type_hack();

}

// pcp8718 and pcp8728 both contain user data (player name?) and will need to be factory defaulted once they work
// the ROM code is slightly different between them

ROM_START( pcp8718 )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "internal.rom", 0x000000, 0x2000, CRC(ea119561) SHA1(a2680577e20fe1155efc40a5781cf1ec80ccec3a) )

	ROM_REGION( 0x800000, "spi", ROMREGION_ERASEFF )
	//ROM_LOAD16_WORD_SWAP( "8718_en25f32.bin", 0x000000, 0x400000, CRC(cc138db4) SHA1(379af3d94ae840f52c06416d6cf32e25923af5ae) ) // bad dump, some blocks are corrupt
	ROM_LOAD( "eyecare_25q32av1g_ef4016.bin", 0x000000, 0x400000, CRC(58415e10) SHA1(b1adcc03f2ad8d741544204671677740e904ce1a) )
ROM_END

ROM_START( pcp8728 )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "internal.rom", 0x000000, 0x2000, CRC(ea119561) SHA1(a2680577e20fe1155efc40a5781cf1ec80ccec3a) )

	ROM_REGION( 0x800000, "spi", ROMREGION_ERASEFF )
	ROM_LOAD( "pcp 8728 788 in 1.bin", 0x000000, 0x400000, CRC(60115f21) SHA1(e15c39f11e442a76fae3823b6d510178f6166926) )
ROM_END

ROM_START( bkid218 )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "internal.rom", 0x000000, 0x2000, CRC(ea119561) SHA1(a2680577e20fe1155efc40a5781cf1ec80ccec3a) )

	ROM_REGION( 0x800000, "spi", ROMREGION_ERASEFF )
	ROM_LOAD( "218n1_25q64csig_c84017.bin", 0x000000, 0x800000, CRC(94f35dbd) SHA1(a1bd6defd2465ae14753cd83be5c31f99e9158ec) )
ROM_END

} // anonymous namespace


CONS( 200?, pcp8718,      0,       0,      gpl162xx_lcdtype,   gpl162xx_lcdtype, gpl162xx_lcdtype_state, empty_init, "PCP", "PCP 8718 - HD 360 Degrees Rocker Palm Eyecare Console - 788 in 1", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
CONS( 200?, pcp8728,      0,       0,      gpl162xx_lcdtype,   gpl162xx_lcdtype, gpl162xx_lcdtype_state, empty_init, "PCP", "PCP 8728 - 788 in 1", MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // what name was this sold under?
CONS( 200?, bkid218,      0,       0,      gpl162xx_lcdtype,   gpl162xx_lcdtype, gpl162xx_lcdtype_state, empty_init, "BornKid", "Handheld Game Console BC-19 - 218 in 1", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



generalplus_gpl32612.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood


/*****************************************************************************

    unlike earlier SunPlus / GeneralPlus based SoCs this one is
    ARM based

    NAND types

    MX30LF1G08AA
    ID = C2F1
    Capacity = (2048+64) x 64 x 512

    Star Wars Blaster - MX30LF1G08AA
    TMNT Hero Portal  - MX30LF1G08AA
    DC Hero Portal    - MX30LF1G08AA

*****************************************************************************/


#include "emu.h"

#include "cpu/arm7/arm7.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "softlist_dev.h"

#include "screen.h"
#include "speaker.h"


namespace {

class generalplus_gpl32612_game_state : public driver_device
{
public:
	generalplus_gpl32612_game_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen")
	{ }

	void gpl32612(machine_config &config);

	void nand_init840();
	void nand_init880();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void arm_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;

	uint32_t screen_update_gpl32612(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

private:

	void nand_init(int blocksize, int blocksize_stripped);
	void copy_block(int i, int blocksize, int blocksize_stripped, uint8_t* nandrom, int dest);
	void bootstrap();

	std::vector<uint8_t> m_strippedrom;

	uint32_t unk_d000003c_r(offs_t offset, uint32_t mem_mask);
	uint32_t unk_d0800018_r(offs_t offset, uint32_t mem_mask);
	uint32_t unk_d0900140_r(offs_t offset, uint32_t mem_mask);
	uint32_t unk_d0900153_r(offs_t offset, uint32_t mem_mask);
};


class generalplus_zippity_game_state : public generalplus_gpl32612_game_state
{
public:
	generalplus_zippity_game_state(const machine_config &mconfig, device_type type, const char *tag) :
		generalplus_gpl32612_game_state(mconfig, type, tag),
		m_cart(*this, "cartslot"),
		m_cart_region(nullptr)
	{ }

	void zippity(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	required_device<generic_slot_device> m_cart;
	memory_region *m_cart_region;


};


uint32_t generalplus_gpl32612_game_state::unk_d000003c_r(offs_t offset, uint32_t mem_mask)
{
	return machine().rand();
}

uint32_t generalplus_gpl32612_game_state::unk_d0800018_r(offs_t offset, uint32_t mem_mask)
{
	return machine().rand();
}

uint32_t generalplus_gpl32612_game_state::unk_d0900140_r(offs_t offset, uint32_t mem_mask)
{
	return machine().rand();
}

uint32_t generalplus_gpl32612_game_state::unk_d0900153_r(offs_t offset, uint32_t mem_mask)
{
	return machine().rand();
}

void generalplus_gpl32612_game_state::arm_map(address_map &map)
{
	map(0x00000000, 0x03ffffff).ram();

	map(0xd000003c, 0xd000003f).r(FUNC(generalplus_gpl32612_game_state::unk_d000003c_r));

	map(0xd0800018, 0xd080001b).r(FUNC(generalplus_gpl32612_game_state::unk_d0800018_r));

	map(0xd0900140, 0xd0900143).r(FUNC(generalplus_gpl32612_game_state::unk_d0900140_r));
	map(0xd0900150, 0xd0900153).r(FUNC(generalplus_gpl32612_game_state::unk_d0900153_r));
}

uint32_t generalplus_gpl32612_game_state::screen_update_gpl32612(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void generalplus_gpl32612_game_state::machine_start()
{
}

void generalplus_gpl32612_game_state::machine_reset()
{
}

static INPUT_PORTS_START( gpl32612 )
INPUT_PORTS_END

void generalplus_gpl32612_game_state::copy_block(int i, int blocksize, int blocksize_stripped, uint8_t* nandrom, int dest)
{
	const int base = i * blocksize;
	address_space& mem = m_maincpu->space(AS_PROGRAM);

	for (int j = 0; j < blocksize_stripped; j++)
	{
		uint8_t data = nandrom[base + j];
		//printf("writing to %08x : %02x", dest + j, data);
		mem.write_byte(dest+j, data);
	}
}

void generalplus_gpl32612_game_state::bootstrap()
{
	uint8_t* rom = memregion("nand")->base();

	//int startblock = 0xe0000 / 0x800;
	int startblock = 0xa0000 / 0x800;
	int endblock = 0x1f0000 / 0x800;

	int j = 0;
	for (int i = startblock; i < endblock; i++) // how much is copied, and where from? as with the unSP NAND ones there appear to be several stages of bootloader, this is not the 1st one
	{
		copy_block(i, 0x840, 0x800, rom, j * 0x800);
		j++;
	}
}

void generalplus_gpl32612_game_state::gpl32612(machine_config &config)
{
	ARM9(config, m_maincpu, 240'000'000); // unknown core / frequency, but ARM based
	m_maincpu->set_addrmap(AS_PROGRAM, &generalplus_gpl32612_game_state::arm_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320, 262);
	m_screen->set_visarea(0, 320-1, 0, 240-1);
	m_screen->set_screen_update(FUNC(generalplus_gpl32612_game_state::screen_update_gpl32612));

	SPEAKER(config, "speaker", 2).front();
}

void generalplus_zippity_game_state::machine_start()
{
	generalplus_gpl32612_game_state::machine_start();

	// if there's a cart, override the standard mapping
	if (m_cart && m_cart->exists())
	{
		m_cart_region = memregion(std::string(m_cart->tag()) + GENERIC_ROM_REGION_TAG);
	}
}

DEVICE_IMAGE_LOAD_MEMBER(generalplus_zippity_game_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}



void generalplus_zippity_game_state::zippity(machine_config &config)
{
	// don't inherit from gpl32612 as in reality this is GPL32300A and could differ

	ARM9(config, m_maincpu, 240'000'000); // unknown core / frequency, but ARM based
	m_maincpu->set_addrmap(AS_PROGRAM, &generalplus_zippity_game_state::arm_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320, 262);
	m_screen->set_visarea(0, 320-1, 0, 240-1);
	m_screen->set_screen_update(FUNC(generalplus_zippity_game_state::screen_update_gpl32612));

	SPEAKER(config, "speaker", 2).front();

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "leapfrog_zippity_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(generalplus_zippity_game_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("leapfrog_zippity_cart");
}



// NAND dumps, so there will be a bootloader / boot strap at least

ROM_START( jak_swbstrik )
	ROM_REGION( 0x8400000, "nand", ROMREGION_ERASEFF )
	ROM_LOAD( "starwarsblaster.bin", 0x000000, 0x8400000, CRC(02c3c4d6) SHA1(a6ae05a7d7b2015023113f6baad25458f3c01102) )
ROM_END

ROM_START( jak_tmnthp )
	ROM_REGION( 0x8400000, "nand", ROMREGION_ERASEFF )
	ROM_LOAD( "tmntheroportal.bin", 0x000000, 0x8400000, CRC(75ec7127) SHA1(cd05f55a1f5a7fd3d1b0658ad6805b8777857a7e) )
ROM_END

ROM_START( jak_ddhp )
	ROM_REGION( 0x8400000, "nand", ROMREGION_ERASEFF )
	ROM_LOAD( "dragonsheroesportal_mx30lf1g08aa_c2f1.bin", 0x000000, 0x8400000, CRC(825cce7b) SHA1(2185137138f2a20e5cfe9c167eeb67a146953b65) )
ROM_END

ROM_START( jak_dchp )
	ROM_REGION( 0x8400000, "nand", ROMREGION_ERASEFF )
	ROM_LOAD( "dcheroportal_mx30lf1g08aa_c2f1.bin", 0x000000, 0x8400000, CRC(576a3005) SHA1(6cd9edc4def707aede3f82a21c87269d2a6bc870) )
ROM_END

ROM_START( jak_prhp )
	ROM_REGION( 0x8400000, "nand", ROMREGION_ERASEFF )
	ROM_LOAD( "mx30lf1g08aa.u2", 0x000000, 0x8400000, CRC(4ccd7e53) SHA1(decbd424f088d180776a817c80b147d6a887e5c1) )
ROM_END

// uncertain hardware type, ARM based, has GPNAND strings
ROM_START( zippity )
	ROM_REGION( 0x10800000, "nand", ROMREGION_ERASE00 )
	ROM_LOAD( "zippity_mt29f2g08aacwp_2cda8015.bin", 0x0000, 0x10800000, CRC(16248b63) SHA1(3607337588a68052ef5c495b496aa3e0449d3eb6) )
ROM_END

ROM_START( zippityuk )
	ROM_REGION( 0x10800000, "nand", ROMREGION_ERASE00 )
	ROM_LOAD( "29f2c08aacwp.u2", 0x0000, 0x10800000, CRC(27d172ae) SHA1(9ade19d7aa28fba13581e6879b39e3a7702260b0) )
ROM_END

ROM_START( kidizmp )
	ROM_REGION( 0x10800000, "nand", ROMREGION_ERASE00 )
	ROM_LOAD( "s34ml02g1_withspare.u13", 0x0000, 0x10800000, CRC(c5d55bdc) SHA1(073fc3fd56c532750b4e2020abe27d3448999d56) )
ROM_END

ROM_START( kidizmb )
	ROM_REGION(  0x8400000, "nand", ROMREGION_ERASE00 )
	ROM_LOAD( "hy27uf081g2a_withspare.bin", 0x0000, 0x8400000, CRC(b87861c4) SHA1(8b5cc2557b54a37928be818430b91c48db98758f) )
ROM_END

ROM_START( pocketmp )
	ROM_REGION(  0x8800000, "nand", ROMREGION_ERASE00 )
	ROM_LOAD( "tc58nvg0s3hta00.u3", 0x0000, 0x8800000, CRC(aabf2deb) SHA1(ee3118377c21b1fb28ff262484c9b587b394bd80) )
ROM_END

ROM_START( pocketmr )
	ROM_REGION(  0x8800000, "nand", ROMREGION_ERASE00 )
	ROM_LOAD( "tc58nvg0s3hta00_withspare.u6", 0x0000, 0x8800000, CRC(ec839dde) SHA1(18b77c7e1cf3c66787ccfde9f450671e3d1b0e36) )
ROM_END

ROM_START( dmnslayg )
	ROM_REGION(  0x8800000, "nand", ROMREGION_ERASE00 )
	ROM_LOAD( "tc58nvg0s3hta00_with_spare.u3", 0x0000, 0x8800000, CRC(a9402fdb) SHA1(0809a8da176f65efc2926131ba0259278d3c644d) )
ROM_END

ROM_START( anpanm19 )
	ROM_REGION(  0x1000000, "spi", ROMREGION_ERASE00 )
	ROM_LOAD( "25l1280.u3", 0x0000, 0x1000000, CRC(7932fb3e) SHA1(a381eeba5357fe71e4d6081b9b91b57e5705f7f1) )
ROM_END

ROM_START( smatomo )
	ROM_REGION(  0x400000, "spi", ROMREGION_ERASE00 )
	ROM_LOAD( "mx25l3206e.ic3", 0x0000, 0x400000, CRC(fb4d1684) SHA1(98cecd7ead52118028cb3a1de71cb3528cd81be5) )
ROM_END

ROM_START( tamameet )
	ROM_REGION(  0x800000, "spi", ROMREGION_ERASE00 )
	ROM_LOAD( "25q64.u3", 0x0000, 0x800000, CRC(f15507f8) SHA1(356cb1bd68169eb747898325eacfd7590dbe9f9c) )
ROM_END

ROM_START( chiikpc )
	ROM_REGION(  0x1000000, "spi", ROMREGION_ERASE00 )
	ROM_LOAD( "25l12833f.u6", 0x0000, 0x1000000, CRC(bde74209) SHA1(8a91554ae653f4ed54fd354049c32b545e4d359d) )
ROM_END

ROM_START( saikyopc )
	ROM_REGION(  0x1000000, "spi", ROMREGION_ERASE00 )
	ROM_LOAD( "25l12833f.u6", 0x0000, 0x1000000, CRC(5b870182) SHA1(909d2834875484f8369cfbce2c51fa27c0a3d973) )
ROM_END

ROM_START( tmydistb )
	ROM_REGION(  0x800000, "spi", ROMREGION_ERASE00 )
	ROM_LOAD( "gpr25l64.u5", 0x0000, 0x800000, CRC(01e5a892) SHA1(b9164173e707eb69cd7d50ce69f3368de7e7390f) )
ROM_END

ROM_START( intrtvg )
	ROM_REGION(  0x100000, "spi", ROMREGION_ERASE00 )
	ROM_LOAD( "25q08.u6", 0x0000, 0x100000, CRC(5aa91972) SHA1(296108e8683063c16951ff326e6ff3d63d9ed5b8) )

	DISK_REGION( "sdcard" ) // 4GB SD Card
	DISK_IMAGE( "interactivetv", 0, SHA1(7061e28c4560b763bda1157036b79c726387e430) )
ROM_END

ROM_START( ardancem )
	ROM_REGION(  0x100000, "spi", ROMREGION_ERASE00 )
	ROM_LOAD( "25q08.u6", 0x0000, 0x100000, CRC(ba2cdacd) SHA1(d47829ee5310140665146262a44e0ba91942f25c) )

	DISK_REGION( "sdcard" ) // 16GB SD Card
	DISK_IMAGE( "ardancemat", 0, SHA1(df8cb065f5ce0ca863b205549ecc4c27647f9954) )
ROM_END


ROM_START( pdcm2 )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only?

	ROM_REGION( 0x84000000, "nand", ROMREGION_ERASE00 )
	ROM_LOAD( "k9gag08u0m.u3", 0x0000, 0x84000000, CRC(88d9c107) SHA1(0b70962ecddf3a8a748b7af5e81cffb365f704e2) )
ROM_END

ROM_START( arcadege )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only?

	ROM_REGION( 0x8400000, "nand", ROMREGION_ERASE00 )
	ROM_LOAD( "tc58nvg0s3eta00.u3", 0x0000, 0x8400000, CRC(9b4db25e) SHA1(7e3d7e15f2592efd98027440c3761179c95e4417) )
ROM_END

ROM_START( airobo )
	ROM_REGION16_BE( 0x40000, "maincpu:internal", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "internal.rom", 0x00000, 0x40000, NO_DUMP ) // used as bootstrap only?

	ROM_REGION( 0x22000000, "nand", ROMREGION_ERASE00 )
	ROM_LOAD( "tc58nvg2s0hta00.bin", 0x0000, 0x22000000, CRC(6cfa4600) SHA1(152c04532ae2587dea590d169e87534924f5ea89) )
ROM_END


void generalplus_gpl32612_game_state::nand_init(int blocksize, int blocksize_stripped)
{
	uint8_t* rom = memregion("nand")->base();
	int size = memregion("nand")->bytes();

	int numblocks = size / blocksize;

	m_strippedrom.resize(numblocks * blocksize_stripped);

	for (int i = 0; i < numblocks; i++)
	{
		const int base = i * blocksize;
		const int basestripped = i * blocksize_stripped;

		for (int j = 0; j < blocksize_stripped; j++)
		{
			m_strippedrom[basestripped + j] = rom[base + j];
		}
	}

	// debug to allow for easy use of unidasm.exe
	if (0)
	{
		auto filename = "stripped_" + std::string(machine().system().name);
		auto fp = fopen(filename.c_str(), "w+b");
		if (fp)
		{
			fwrite(&m_strippedrom[0], blocksize_stripped * numblocks, 1, fp);
			fclose(fp);
		}
	}
}

void generalplus_gpl32612_game_state::nand_init840()
{
	nand_init(0x840, 0x800);
	bootstrap();
}

void generalplus_gpl32612_game_state::nand_init880()
{
	nand_init(0x880, 0x800);
	bootstrap();
}

} // anonymous namespace


//    year, name,         parent,  compat, machine,      input,        class,              init,       company,  fullname,                             flags
CONS( 200?, jak_swbstrik,    0,       0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, nand_init840, "JAKKS Pacific Inc", "Star Wars Blaster Strike", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 200?, jak_tmnthp,      0,       0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, nand_init840, "JAKKS Pacific Inc", "Teenage Mutant Ninja Turtles Hero Portal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 200?, jak_ddhp,        0,       0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, nand_init840, "JAKKS Pacific Inc", "DreamWorks Dragons Hero Portal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 200?, jak_prhp,        0,       0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, nand_init840, "JAKKS Pacific Inc", "Power Rangers Super Megaforce Hero Portal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING ) // from a PAL unit (probably not region specific)
CONS( 200?, jak_dchp,        0,       0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, nand_init840, "JAKKS Pacific Inc", "DC Super Heroes The Watchtower Hero Portal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )

// Might not belong here, SoC is marked GPL32300A instead, but is still ARM based, and has GPNAND strings
CONS( 201?, zippity,         0,       0,      zippity, gpl32612, generalplus_zippity_game_state, empty_init,  "LeapFrog",         "Zippity (US)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
// TODO, check if code differs, or just unused areas of the NAND
CONS( 201?, zippityuk,       zippity, 0,      zippity, gpl32612, generalplus_zippity_game_state, empty_init,  "LeapFrog",         "Zippity (UK)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// GP32C01 (maybe, picture is unclear) - Camera for kids
CONS( 2013, kidizmp,         0,        0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, empty_init,  "VTech",         "Kidizoom Connect (Germany, pink camera)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
// seems to be older tech, just glob + ROM, assuming it's a GP32 series based on above and due to having ARM code
CONS( 201?, kidizmb,         0,        0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, empty_init,  "VTech",         "Kidizoom (Germany, blue camera)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

CONS( 2019, pocketmp,        0,        0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, nand_init880,  "Takara Tomy",        "Pocket Monsters PC",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
CONS( 2019, pocketmr,        0,        0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, nand_init880,  "Takara Tomy",        "Pocket Monsters Rotom Tablet",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// uses GPL32610 - 「それいけ！アンパンマン」スポーツ育脳マット
CONS( 2019, anpanm19,        0,        0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, empty_init,  "JoyPalette",        "Anpanman: Sports Ikunou Mat (Japan)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// unknown (uses a glob) has GPspispi header, ARM based, SPI ROM
CONS( 201?, smatomo,         0,        0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, empty_init,  "Bandai",        "Smatomo (Japan)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// company is called 深圳市飞讯互动科技有限公司
// very generic packaging, boots from SPI, has game data on SD card (mostly NES games)
CONS( 202?, intrtvg,         0,        0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, empty_init,  "Shen Zhen Shi Fei Xun Hu Dong Technology",     "Interactive Game Console (Model B608, YRPRSODF)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
// also very generic packaging, similar SD card content to above, including NES games, but with some extra music/videos for the dance part
CONS( 202?, ardancem,        0,        0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, empty_init,  "Shen Zhen Shi Fei Xun Hu Dong Technology",     "AR Dance Mat (Model DM02, YRPRSODF)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// unknown (uses a glob) but it's GeneralPlus and ARM based, so put in here for now
// ROM has 'GPNandTag2' header rather than the usual
// 鬼滅の刃 全集中パッド（グリーン)
CONS( 2021, dmnslayg,        0,        0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, nand_init880,  "Bandai",        "Demon Slayer: Kimetsu no Yaiba Zenshuuchuu Pad (green ver.) (Japan)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

/* PCB is marked as M2-SPG48-GPG35-V30 2009-08-11

SoC appears to be
CONNY CNT61623P-003A-QL172
MD481P
0917

(could be a rebranded GPL32 series, ROM has GPNand header)

there is also a
GPY0201A

*/

CONS( 2009, pdcm2,           0,        0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, empty_init,  "VideoJet / Conny",        "PDC M2",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// uses a GPL32600A-003A-QL141
CONS( 200?, arcadege,           0,        0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, nand_init840,  "Millennium 2000 GmbH",        "Millennium Arcade Genius SE",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// uses a GP326813
// 学習おうえんAI★ミラクルロボ
CONS( 2020, airobo,             0,        0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, nand_init840,  "Benesse Corporation",        "Gakushuu Ouen AI Miracle Robo",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// uses GPE13101A
// たまごっち みーつ  (there appear to be many units in this series, ROM data could differ, this was from a light blue 'hearts and rainbows' themed unit with no subtitles)
CONS( 2018, tamameet,           0,        0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, empty_init,  "Bandai",        "Tamagotchi Meets (Japan)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// uses a glob CPU
// ちいかわラーニングパソコン
CONS( 2021, chiikpc,            0,        0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, empty_init,  "Bandai",        "Chiikawa Learning PC (Japan)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// uses a glob CPU
// 学びの最強王になれ! 最強王図鑑パソコン
CONS( 2020, saikyopc,           0,        0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, empty_init,  "Bandai",        "Manabi no Sai-Kyo-Oh ni Nare! Sai-Kyo-Oh Zukan PC (Japan)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// ディズニー&ディズニー／ピクサーキャラクターズ できた!がいっぱい ドリームトイパッド
CONS( 2020, tmydistb,           0,        0,      gpl32612, gpl32612, generalplus_gpl32612_game_state, empty_init,  "Tomy",          "Disney & Disney/Pixar Characters Dekita! ga Ippai Dream Toy Pad (Japan)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



generalplus_gpm453x_nand.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood


/*****************************************************************************

    ARM Cortex M4F based GPM453x series chips

    all game data is stored on a NAND

    both games here are on HDMI sticks

    These likely boot from an internal ROM so will need bootstrapping without
    a dump of it

    LeapLand Adventures - GPM4530A
    Paw Patrol - GPM4532C

*****************************************************************************/


#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/arm7/arm7.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class generalplus_gpm453x_game_state : public driver_device
{
public:
	generalplus_gpm453x_game_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen")
	{ }

	void gpm453x(machine_config &config) ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void arm_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

private:

};


void generalplus_gpm453x_game_state::arm_map(address_map &map)
{
	map(0x00000000, 0x03ffffff).ram();
}

uint32_t generalplus_gpm453x_game_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void generalplus_gpm453x_game_state::machine_start()
{
}

void generalplus_gpm453x_game_state::machine_reset()
{
}

static INPUT_PORTS_START( gpm453x )
INPUT_PORTS_END

void generalplus_gpm453x_game_state::gpm453x(machine_config &config)
{
	ARM9(config, m_maincpu, 240'000'000); // unknown core / frequency, but ARM based
	m_maincpu->set_addrmap(AS_PROGRAM, &generalplus_gpm453x_game_state::arm_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320, 262);
	m_screen->set_visarea(0, 320-1, 0, 240-1);
	m_screen->set_screen_update(FUNC(generalplus_gpm453x_game_state::screen_update));

	SPEAKER(config, "speaker", 2).front();
}

ROM_START( leapland )
	ROM_REGION( 0x22000000, "nand", ROMREGION_ERASEFF )
	ROM_LOAD( "tc58nvg2s0hta00_withspare.u2", 0x000000, 0x22000000, CRC(2482c26d) SHA1(2ebaacdcc9188bcf86507ebdc9cea6e13f9b9988) )
ROM_END

ROM_START( leappawp )
	ROM_REGION( 0x11000000, "nand", ROMREGION_ERASEFF )
	ROM_LOAD( "tc58nvg1s3hta00_withspare.u2", 0x000000, 0x11000000, CRC(0d7ff9a1) SHA1(9916c3578595b89e94c0ab64f4356badc5b8a0dd) )
ROM_END

} // anonymous namespace

CONS( 2021, leapland,    0,       0,      gpm453x, gpm453x, generalplus_gpm453x_game_state, empty_init, "LeapFrog", "LeapLand Adventures (UK)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2021, leappawp,    0,       0,      gpm453x, gpm453x, generalplus_gpm453x_game_state, empty_init, "LeapFrog", "PAW Patrol: To The Rescue! (UK)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



generalplus_gpm453x_sdcard.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

// uses a GPM4530A (see https://www.generalplus.com/GPM4530A-j2tLs-1LVbPHkLN4921SVpnSNproduct_detail )

#include "emu.h"

#include "cpu/arm7/arm7.h"
#include "machine/spi_sdcard.h"

#include "screen.h"
#include "speaker.h"

namespace {

class gpm4530a_lexibook_state : public driver_device
{
public:
	gpm4530a_lexibook_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen"),
		m_sdcard(*this, "sdcard")
	{ }

	void gpm4530a_lexibook(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void arm_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<spi_sdcard_device> m_sdcard;

	uint32_t screen_update_gpm4530a_lexibook(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

private:

};

void gpm4530a_lexibook_state::arm_map(address_map &map)
{
}

uint32_t gpm4530a_lexibook_state::screen_update_gpm4530a_lexibook(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void gpm4530a_lexibook_state::machine_start()
{
}

void gpm4530a_lexibook_state::machine_reset()
{
}

static INPUT_PORTS_START( gpm4530a_lexibook )
INPUT_PORTS_END


void gpm4530a_lexibook_state::gpm4530a_lexibook(machine_config &config)
{
	ARM9(config, m_maincpu, 192'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &gpm4530a_lexibook_state::arm_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320, 262);
	m_screen->set_visarea(0, 320-1, 0, 240-1);
	m_screen->set_screen_update(FUNC(gpm4530a_lexibook_state::screen_update_gpm4530a_lexibook));

	SPI_SDCARD(config, m_sdcard, 0);
	m_sdcard->set_prefer_sdhc();

	SPEAKER(config, "speaker", 2).front();
}

ROM_START( lx_jg7420 )
	ROM_REGION( 0x10000, "boot", ROMREGION_ERASEFF )
	ROM_LOAD( "bootrom.bin", 0x00000, 0x10000, NO_DUMP ) // unknown size/capacity/type (internal?)

	DISK_REGION( "sdcard" ) // 4GB SD Card
	DISK_IMAGE( "jg7420", 0, SHA1(214a1686c7eefdb4cb5d723e98957600c8cb138d) )
ROM_END

ROM_START( rizstals )
	ROM_REGION( 0x100000, "spi", ROMREGION_ERASEFF )
	ROM_LOAD( "mx25v8035f.u5", 0x0000, 0x100000, CRC(1ba0c7b8) SHA1(d3f4fdabf07c1d8bbd73da54280e3fab006a72b6) )

	DISK_REGION( "sdcard" ) // 8GB SD Card
	DISK_IMAGE( "sdcard", 0, SHA1(79462dd4a632d9b9710581ed170a696c059e8a2d) )
ROM_END

} // anonymous namespace

// JG7420_24 on sticker
CONS( 201?, lx_jg7420,    0,       0,      gpm4530a_lexibook, gpm4530a_lexibook, gpm4530a_lexibook_state, empty_init, "Lexibook", "Lexibook JG7420 200-in-1", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2021, rizstals,     0,       0,      gpm4530a_lexibook, gpm4530a_lexibook, gpm4530a_lexibook_state, empty_init, "Takara Tomy", "RizSta Live Studio", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



geneve.cpp
<---------------------------------------------------------------------->
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/******************************************************************************

    ======  Original system =========

    Myarc Geneve 9640
    =================

    +--------------------------------------------------------------------+
    |                                   /---\                        +---+
    |  +-----+-----+                    |Bat|                        |Vid()
    |  |     |     |  +-HM62256---+ ____\---/                        +---+
    |  +-----+-----+  | 32K SRAM  ||RTC_|+-------------+             +---+
    |  |   512K    |  +-----------+ ____ |    V9938    |             |Mou[]
    |  +-  DRAM ---+  +-----------+|Snd_||     VDP     |             +---+
    |  | ... | ... |  | 16K EPROM |      +-------------+             +---+
    |  |    16*    |  +-----------+     +----+         +--+   +----+ |Joy[]
    |  +--HM50256--+  +--------------+  |    | +-----+ |  |   |Keyb| +---+
    |  |   256Kx1  |  |   TMS9995    |  |    | |128K | |  +---+(  )+----+
    |  +-----+-----+  |     CPU      |  |TMS | +Video+ |
    |  |     |     |  +--------------+  |9901| |RAM  | |  outside of box
    |  +-----+-----+   +--------+  +-+  |    | +- - -+ |
    |                  |        |  |P|  |    | |4*HM | |
    |                  | Gate   |  |A|  |    | +50464+ |
    |                  | Array  |  |L|  +----+ |64Kx4| |
   O= LED              |        |  +-+         +-----+ |
    |              +-+ +--------+          +-+         |
    +--------------+ |                     | +---------+
    Front            |||||||||||||||||||||||       Back
                     +---------------------+

    The Geneve 9640 is a card for the Peripheral Expansion Box of the TI-99/4A
    system, equipped with a complete computing architecture and thus replaces
    the TI console. It was created by the company Myarc Inc. in 1987, who also
    manufactured several expansion cards for the TI system (like floppy disk
    controllers, hard disk controllers, serial interfaces, memory expansions).

    The Geneve 9640 got its name purportedly from a picture at the wall of
    one of its creators, showing the Swiss city Geneva (Geneve). The number
    9640 should be read as 9-640, with the 9 being a reference to the TI-99
    family (TI did not agree to use the 99 as a reference), and the 640 as
    the amount of built-in RAM (CPU + video).

    It is equipped with a TMS9995 microprocessor, which is downward compatible
    with the TMS9900 used in the TI console. It is clocked at 12 MHz,
    internally divided by 4, which makes it compatible with the expected clock
    rate of external devices, but internally it is much more efficient and
    can execute TMS programs about 2-3 times faster. For more details, see
    tms9995.cpp.

    General architecture
    --------------------
    - CPU: TMS9995, 12 Mhz, with 256 bytes on-chip RAM and decrementer
    - RAM: SRAM (32 KiB, 0 wait states, HM62256 32Kx8),
           DRAM (512 KiB, 1 wait state, 16*HM50256 256Kx1)
    - Video: Yamaha V9938 (compatible to TMS9928, also used in MSX2)
    - Video memory: 128 KiB (4*HM50464 64Kx4)
    - Sound: SN76496 (compat. to TMS9919)
    - System interface: TMS9901
    - Real time clock: MM58274, battery-backed
    - Keyboard: Connector for external XT-compatible keyboard
    - Mouse: Bus mouse connector, going to the color bus of the V9938
    - Joysticks: TI-99/4A joystick connector
    - Video output: RGB or composite via DIN plug

    A Gate Array circuit (labeled Myarc M60014-1004J 715500) contains most of
    logic circuitry for device and memory selection, a memory mapper, and the
    keyboard interface. Unfortunately, the details of its implementation must
    be considered as lost. We can only guess its implementation by its
    behavior.

    Also, a PAL circuit (PAL16R4ACN) is mounted on the board whose task is
    to drive the READY line to the processor and selection lines to the
    peribox. The equations of the PAL are available. The PAL controls
    the creation of wait states according to the device or memory selected
    by the Gate Array.


    Operating modes
    ---------------
    To achieve a maximum degree of compatibility, the Geneve offers two
    operation modes: native and GPL.

    In the GPL mode, memory space layout is largely equal to the TI-99/4A
    layout, thus allowing programs to run without any adaptation.

    The native mode rearranges the memory layout in order to exploit all
    capabilities of the enhanced architecture. However, this mode cannot run
    older TI-99 programs.

    Memory Map
    ----------
    The memory map is described in genboard.cpp where the emulation of the
    Gate Array and the PAL are implemented.

    CRU map
    -------
    The CRU space contains the selectable devices and flags of the system.
    For more details, see genboard.cpp.


    ======  Modifications  ===========

    32K SRAM expansion
    ------------------
    The schematics prove that Myarc already planned for a 32K expansion of
    the stock 32K SRAM memory. One additional chip is soldered on top
    of the base 32K chip, with its CE* line (pin 20) wired to pin 48 of the
    Gate Array (RAMEN-X*).

    The 32K expansion is required for releases of the operating system (MDOS)
    later than version 2.5.


    PFM: Boot EPROM replacement
    ---------------------------
    The Programmable Flash Memory mod replaces the stock EPROM (16K) by a
    flash memory chip (AT29C040(A) for 512K). It is enabled by selecting
    the machine configuration switch BOOTROM. When set to EPROM, the mod is
    inactive.

    There were several versions. Only the third one is emulated, since it
    functionally subsumes the other two:

    PFM: Original version, 128 KiB
    PFM+: Expansion of the original version, piggybacked, adds another 128KiB
    PFM512: Using an AT29C040 (not A), 512 KiB

    The PFM512 is visible as four banks in memory pages 0xF0-0xFF.
    Bank 0 is the boot code, while banks 1-3 can be used as flash drives.

    The lower 13 bits (A3-A15 by TI counting) of its memory space are set by
    the main address bus, provided that the Gate Array enables the access.
    This is true when the EPROM bank area is accessed (pages F0-FF) or in
    direct mode (unmapped mode, used during boot).

    The next four bits (AMA, AB0, AB1, AB2) are set by the Gate Array as the
    lower four bits of the page number (F0-FF, i.e. 0-F). Note that the AMB
    and further lines cannot be used, because the boot ROM area is restricted
    to that range by the Gate Array.

    To set the most significant two bits of the flash memory chip (A17 and A18
    in the usual counting), two dedicated CRU bits are used, set and latched
    by the 9901 chip. Also, the output can be disabled by another CRU bit.

    CRU 0028: LSB of bank number
    CRU 003A: MSB of bank number
    CRU 002A: PFM output enable

    Genmod
    ------
    A special modification was published for the Geneve around 1990. The goal
    was to fill all memory space with physical memory from a memory
    expansion card, and also to make use of 0-wait state SRAM from this card.
    The mod required some few changes on the Geneve card. The Geneve itself
    never had a large user base, compared to the 99/4A, and this modification
    was even much rarer. Nevertheless, its interesting point is that it
    in fact expands the Geneve architecture to its maximum.

    For more details, see genboard.cpp.

    Genmod is treated as a separate system in MAME. Beside the modified
    hardware, it also requires a modified boot ROM.

    ============================================

    2003: Original version by Raphael Nabet
    2012: Rewritten by Michael Zapf (functionally, high level)
    2019: Rewritten by Michael Zapf (closer to the actual hardware)

******************************************************************************/

#include "emu.h"
#include "cpu/tms9900/tms9995.h"
#include "machine/tms9901.h"
#include "machine/mm58274c.h"
#include "sound/sn76496.h"

#include "bus/ti99/internal/genboard.h"
#include "bus/ti99/internal/genkbd.h"

#include "bus/ti99/colorbus/colorbus.h"
#include "bus/ti99/joyport/joyport.h"
#include "bus/ti99/peb/peribox.h"

#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "bus/pc_kbd/pcxt83.h"
#include "bus/pc_kbd/keytro.h"

#include "speaker.h"

#define LOG_WARN     (1U << 1)
#define LOG_CRU      (1U << 2)
#define LOG_CRUKEY   (1U << 3)
#define LOG_READ     (1U << 4)
#define LOG_READG    (1U << 5)
#define LOG_WRITE    (1U << 6)
#define LOG_CONFIG   (1U << 7)
#define LOG_PFM      (1U << 8)

// Minimum log should be settings and warnings
#define VERBOSE ( LOG_GENERAL | LOG_CONFIG | LOG_WARN )

#include "logmacro.h"


namespace {

#define GENEVE_SRAM_TAG  "sram"
#define GENEVE_SRAMX_TAG "sramexp"
#define GENEVE_SRAMU_TAG "sramult"
#define GENEVE_DRAM_TAG  "dram"
#define GENEVE_CLOCK_TAG "mm58274c"
#define GENEVE_SOUNDCHIP_TAG   "soundchip"
#define GENEVE_TMS9901_TAG     "tms9901"
#define GENEVE_SCREEN_TAG      "screen"

enum
{
	AB2 = 1,
	AB1 = 2,
	AB0 = 4,
	AMA = 8,
	FULLGEN = 63,  // AMC,AMB,AMA,AB0,AB1,AB2
	FULLGNM = 255  // AME,AMD,FULLGEN
};

enum
{
	SRAM32 = 0,
	SRAM64 = 1,
	SRAM384 = 2
};

void geneve_xt_keyboards(device_slot_interface &device)
{
	device.option_add(STR_KBD_KEYTRONIC_PC3270, PC_KBD_KEYTRONIC_PC3270);
	device.option_add(STR_KBD_IBM_PC_XT_83, PC_KBD_IBM_PC_XT_83);
	device.option_add(STR_KBD_GENEVE_XT_101_HLE, KBD_GENEVE_XT_101_HLE);
}

class geneve_state : public driver_device
{
public:
	geneve_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_cpu(*this, "maincpu"),
		m_tms9901(*this, GENEVE_TMS9901_TAG),
		m_sound(*this, GENEVE_SOUNDCHIP_TAG),
		m_video(*this, TIGEN_V9938_TAG),
		m_rtc(*this, GENEVE_CLOCK_TAG),
		m_dram(*this, GENEVE_DRAM_TAG),
		m_sram(*this, GENEVE_SRAM_TAG),
		m_sramx(*this, GENEVE_SRAMX_TAG),
		m_sramu(*this, GENEVE_SRAMU_TAG),
		m_gatearray(*this, GENEVE_GATE_ARRAY_TAG),
		m_genmod_decoder(*this, GENMOD_DECODER_TAG),
		m_pal(*this, GENEVE_PAL_TAG),
		m_joyport(*this, TI_JOYPORT_TAG),
		m_colorbus(*this, COLORBUS_TAG),
		m_kbdconn(*this, "kbd"),
		m_peribox(*this, TI_PERIBOX_TAG),
		m_pfm512(*this, GENEVE_PFM512_TAG),
		m_pfm512a(*this, GENEVE_PFM512A_TAG),
		m_left_button(0),
		m_pfm_prefix(0),
		m_pfm_oe(true),
		m_sram_size(SRAM64),
		m_genmod(false)
	{
	}

	void geneve_common(machine_config &config);
	void geneve(machine_config &config);
	void genmod(machine_config &config);
	void init_geneve();
	void init_genmod();

	DECLARE_INPUT_CHANGED_MEMBER( settings_changed );
	DECLARE_INPUT_CHANGED_MEMBER( setgm_changed );

private:
	// CRU (Communication Register Unit) handling
	uint8_t cruread(offs_t offset);
	void cruwrite(offs_t offset, uint8_t data);

	// Connections with the system interface TMS9901
	uint8_t psi_input(offs_t offset);
	void peripheral_bus_reset(int state);
	void VDP_reset(int state);
	void joystick_select(int state);
	void keyboard_reset(int state);
	void video_wait_states(int state);
	void left_mouse_button(int state);

	void keyboard_clock_line(int state);
	void keyboard_data_line(int state);

	void clock_out(int state);

	void external_operation(offs_t offset, uint8_t data);

	void tms9901_interrupt(offs_t offset, uint8_t data);

	void keyboard_interrupt(int state);

	required_device<tms9995_device>     m_cpu;
	required_device<tms9901_device>     m_tms9901;
	required_device<sn76496_device>     m_sound;
	required_device<v9938_device>       m_video;
	required_device<mm58274c_device>    m_rtc;
	required_device<ram_device>         m_dram;
	required_device<ram_device>         m_sram;
	required_device<ram_device>         m_sramx;
	required_device<ram_device>         m_sramu;

	required_device<bus::ti99::internal::geneve_gate_array_device> m_gatearray;
	optional_device<bus::ti99::internal::genmod_decoder_device> m_genmod_decoder;
	required_device<bus::ti99::internal::geneve_pal_device>        m_pal;
	required_device<bus::ti99::joyport::joyport_device>            m_joyport;
	required_device<bus::ti99::colorbus::v9938_colorbus_device>    m_colorbus;
	required_device<pc_kbdc_device>                                m_kbdconn;
	required_device<bus::ti99::peb::peribox_device>                m_peribox;

	uint8_t* m_eprom = nullptr;  // Pointer to the EPROM

	// PFM expansion
	required_device<at29c040_device>     m_pfm512;
	required_device<at29c040a_device>    m_pfm512a;
	void read_eprom_or_pfm(offs_t offset, uint8_t& value);
	void write_pfm(offs_t offset, uint8_t data);

	void pfm_a17(int state);
	void pfm_a18(int state);
	void pfm_oe(int state);

	// Interrupts
	void inta(int state);
	void intb(int state);
	void keyboard_int(int state);
	void int2_from_v9938(int state);

	// READY line contributors
	void extready(int state);
	void sndready(int state);

	// Memory bus
	void setaddress_debug(bool debug, offs_t address, uint8_t busctrl);
	void setaddress(offs_t address, uint8_t busctrl);
	uint8_t memread(offs_t offset);
	void memwrite(offs_t offset, uint8_t data);

	void crumap(address_map &map) ATTR_COLD;
	void memmap(address_map &map) ATTR_COLD;
	void memmap_setaddress(address_map &map) ATTR_COLD;

	// General device lifecycle
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// Members
	int  m_inta = 0;
	int  m_intb = 0;
	int  m_int2 = 0;
	int  m_keyint = 0;

	int     m_left_button;   // Left mouse button, not wired to the 9938
	int     m_pfm_prefix;
	bool    m_pfm_oe;

	// Settings
	int m_boot_rom = 0;     // Kind of boot ROM (EPROM or PFM512 or PFM512A)
	int m_sram_size = SRAM64;

	// Genmod modifications
	bool m_genmod;
};

/*
    Memory map
*/

void geneve_state::memmap(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(geneve_state::memread), FUNC(geneve_state::memwrite));
}

void geneve_state::memmap_setaddress(address_map &map)
{
	map(0x0000, 0xffff).w(FUNC(geneve_state::setaddress));
}

/*
    CRU map
    The TMS9901 is fully decoded, no mirroring, so we have 32 bits for it,
    and the rest goes to the board (and from there to the PEB)
    TMS9995 has a full 15-bit CRU bit address space (attached to A0-A14)

    We cannot use the map because there is a least one card (sidmaster) that
    activates itself when no other device is selected.
*/
void geneve_state::crumap(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(geneve_state::cruread), FUNC(geneve_state::cruwrite));
}

static INPUT_PORTS_START(geneve_common)

	PORT_START( "BOOTROM" )
	PORT_CONFNAME( 0x03, GENEVE_EPROM, "Boot from" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geneve_state::settings_changed), 3)
		PORT_CONFSETTING( GENEVE_EPROM, "EPROM" )
		PORT_CONFSETTING( GENEVE_PFM512, "PFM 512" )
		PORT_CONFSETTING( GENEVE_PFM512A, "PFM 512A" )

	PORT_START( "VRAM" )
	PORT_CONFNAME( 0x01, 0x00, "Video RAM" )
		PORT_CONFSETTING( 0x00, "128 KiB" )
		PORT_CONFSETTING( 0x01, "192 KiB" )

INPUT_PORTS_END

static INPUT_PORTS_START(geneve)
	PORT_INCLUDE(geneve_common)

	PORT_START( "SRAM" )
	PORT_CONFNAME( 0x03, 0x01, "SRAM size" )
		PORT_CONFSETTING( SRAM32, "32 KiB" )
		PORT_CONFSETTING( SRAM64, "64 KiB" )
		PORT_CONFSETTING( SRAM384, "384 KiB" )

INPUT_PORTS_END

static INPUT_PORTS_START(genmod)
	PORT_INCLUDE(geneve_common)

	PORT_START( "GENMODDIPS" )
	PORT_DIPNAME( GENEVE_GM_TURBO, GENEVE_GM_TURBO, "Genmod Turbo mode") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geneve_state::setgm_changed), 1)
		PORT_CONFSETTING( 0x00, DEF_STR( Off ))
		PORT_CONFSETTING( GENEVE_GM_TURBO, DEF_STR( On ))
	PORT_DIPNAME( GENEVE_GM_TIM, 0, "Genmod TI mode") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geneve_state::setgm_changed), 2)
		PORT_CONFSETTING( 0x00, DEF_STR( Off ))
		PORT_CONFSETTING( GENEVE_GM_TIM, DEF_STR( On ))

INPUT_PORTS_END

INPUT_CHANGED_MEMBER( geneve_state::settings_changed )
{
	// Used when switching the boot ROMs during runtime, especially the PFM
	m_boot_rom = newval;
}

INPUT_CHANGED_MEMBER( geneve_state::setgm_changed )
{
	int number = int(param&0x03);
	int value = newval;

	switch (number)
	{
	case 1:
		// Turbo switch. May be changed at any time.
		LOGMASKED(LOG_CONFIG, "Setting turbo flag to %d\n", value);
		m_genmod_decoder->set_turbo(value!=0);
		break;
	case 2:
		// TIMode switch. Causes reset when changed.
		LOGMASKED(LOG_CONFIG, "Setting timode flag to %d\n", value);
		m_genmod_decoder->set_timode(value!=0);
		machine().schedule_hard_reset();
		break;
	case 3:
		// Used when switching the boot ROMs during runtime, especially the PFM
		m_boot_rom = value;
		break;
	default:
		LOGMASKED(LOG_WARN, "Unknown setting %d ignored\n", number);
	}
}

/*
    Propagate the address bus levels to the Gate Array. This will make it
    create waitstates before the read operation and assert the selector lines.
*/
void geneve_state::setaddress(offs_t address, uint8_t busctrl)
{
	setaddress_debug(false, address, busctrl);
}

void geneve_state::setaddress_debug(bool debug, offs_t address, uint8_t busctrl)
{
	m_gatearray->set_debug(debug);
	m_gatearray->setaddress(address, busctrl);

	// Genmod
	if (m_genmod)
	{
		m_genmod_decoder->set_debug(debug);
		m_genmod_decoder->set_function(m_gatearray->get_function(), m_gatearray->get_prefix(FULLGNM)>>13);
	}

	// Insert a wait state except for SRAM (not by debugger)
	if (!debug)
	{
		// Note that the CSR/CSW inputs must not be asserted for more than
		// 2 us; i.e. they cannot remain asserted for the whole 14 wait cycles
		// (which are 4.6 us). Accordingly, we have to assume that the CSW/CSR
		// signals are only asserted for a single clock cycle (333ns, which
		// is more than the minimum pulse width of 186 ns).
		// See V9938 specs
		m_pal->csw_in(m_gatearray->csw_out());
		m_pal->csr_in(m_gatearray->csr_out());

		// Trigger the 9901 clock when A10=1
		if ((address & 0x0020) != 0)
			m_tms9901->update_clock();
	}

	// Going to the box
	int extbus = m_genmod? m_genmod_decoder->dben_out() : m_gatearray->dben_out();

	if (extbus==ASSERT_LINE)
	{
		offs_t addr13 = address & 0x1fff;
		m_peribox->memen_in(ASSERT_LINE);
		m_peribox->setaddress_dbin(m_gatearray->get_prefix(m_genmod? FULLGNM : FULLGEN) | addr13, ((busctrl & TMS99xx_BUS_DBIN)!=0));
	}
}

uint8_t geneve_state::memread(offs_t offset)
{
	uint8_t value = 0;
	offs_t sramadd;
	offs_t dramadd;
	offs_t pboxadd;
	offs_t addr13 = offset & 0x1fff;
	// For debugging
	int page = m_gatearray->get_prefix(FULLGNM) >> 13;

	if (machine().side_effects_disabled())
	{
		if (m_cpu->is_onchip(offset))
			return m_cpu->debug_read_onchip_memory(offset);

		// The debugger does not call setaddress, so we do it here
		// Also, the decode result is replaced by the debugger version
		setaddress_debug(true, offset, TMS99xx_BUS_DBIN);
	}

	// Video read (never by debugger)
	if (m_gatearray->csr_out()==ASSERT_LINE)
	{
		value = m_video->read((offset>>1)&3);   // M1=A13, M0=A14
		LOGMASKED(LOG_READ, "Video %04x -> %02x\n", offset, value);
	}

	// All of the following parts are accessed in parallel and can set the
	// value on the data bus. In reality, if several of them did, this would
	// be a bug and likely damage the machine.

	// Gate array itself (Keyboard, mapper, GROM address register)
	// If not addressed, value remains unchanged
	m_gatearray->readz(value);

	// Clock
	// The clock is connected with only 4 data bits. Tests on the real machine
	// showed that the upper nibble is 0xf (probably because of the location
	// at f130-f13f?)
	// In TI mode, however, the upper nibble is 1, unless we read 801f,
	// in which case the nibble is 2. Here the location is 8010-801f.
	// Needs more investigation. Simply clearing the upper nibble will not work
	// for some software that assumes these bits to be set.
	if (m_gatearray->rtcen_out()==ASSERT_LINE)
	{
		value = m_rtc->read(offset & 0x000f);
		if (m_gatearray->geneve_mode())
			value |= 0xf0;
		else
			value |= ((offset & 0x001f)+1) & 0xf0;

		LOGMASKED(LOG_READ, "Clock %04x -> %02x\n", offset, value);
	}

	// DRAM (also for GROM simulator)
	// Genmod uses the box, but also the DRAM in parallel. Only for
	// TIMODE, the box access is suppressed.
	if (m_gatearray->accessing_dram())
	{
		dramadd = m_gatearray->get_dram_address();
		value = m_dram->pointer()[dramadd];
		int dpage = dramadd >> 13;

		const char* ramtype = (m_gatearray->accessing_grom())? "GROM" : "DRAM";
		LOGMASKED(LOG_READ, "%s %02x:%04x -> %02x\n", ramtype, dpage, addr13, value);
	}

	// Boot ROM or PFM (normal and Genmod)
	if (m_gatearray->romen_out()==ASSERT_LINE)
		read_eprom_or_pfm(offset, value);

	// Stock SRAM 32K (normal and Genmod)
	if (m_gatearray->ramen_out()==ASSERT_LINE)
	{
		sramadd = m_gatearray->get_prefix(AB1 | AB2) | addr13;
		value = m_sram->pointer()[sramadd];
		LOGMASKED(LOG_READ, "SRAM %02x:%04x -> %02x\n", page, addr13, value);
	}

	// Expanded SRAM 32K (not in Genmod)
	if (!m_genmod && m_gatearray->ramenx_out()==ASSERT_LINE)
	{
		if (m_sram_size != SRAM32)
		{
			sramadd = m_gatearray->get_prefix(AB1 | AB2) | addr13;
			value = m_sramx->pointer()[sramadd];
			LOGMASKED(LOG_READ, "SRAMX %02x:%04x -> %02x\n", page, addr13, value);
		}
		else
			LOGMASKED(LOG_WARN, "Access to SRAMX page %02x, but no SRAM expansion available\n", page);
	}

	// Ultimate SRAM expansion (not in Genmod)
	if (!m_genmod && m_gatearray->ramenu_out()==ASSERT_LINE)
	{
		if (m_sram_size == SRAM384)
		{
			sramadd = m_gatearray->get_prefix(FULLGEN) | addr13;
			value = m_sramu->pointer()[sramadd];
			LOGMASKED(LOG_READ, "SRAMU %02x:%04x -> %02x\n", page, addr13, value);
		}
		else
			LOGMASKED(LOG_WARN, "Access to SRAMU page %02x, but no 384K SRAM expansion available\n", page);
	}

	// Peripheral box
	if ((m_genmod && m_genmod_decoder->dben_out())
		|| (!m_genmod && m_gatearray->dben_out()==ASSERT_LINE))
	{
		pboxadd = m_gatearray->get_prefix(m_genmod? FULLGNM : FULLGEN) | addr13;
		m_peribox->readz(pboxadd, &value);
		m_peribox->memen_in(CLEAR_LINE);
		LOGMASKED(LOG_READ, "PEB %02x:%04x -> %02x\n", page, addr13, value);
	}

	// In case we had a debugger read, reset the flag.
	m_gatearray->set_debug(false);
	if (m_genmod) m_genmod_decoder->set_debug(false);

	return value;
}

void geneve_state::memwrite(offs_t offset, uint8_t data)
{
	offs_t sramadd = 0;
	offs_t dramadd = 0;
	offs_t pboxadd = 0;

	offs_t addr13 = offset & 0x1fff;
	// For debugging
	int page = m_gatearray->get_prefix(FULLGNM) >> 13;

	if (machine().side_effects_disabled())
	{
		if (m_cpu->is_onchip(offset))
		{
			m_cpu->debug_write_onchip_memory(offset, data);
			return;
		}

		// The debugger does not call setaddress, so we do it here
		// Also, the decode result is replaced by the debugger version
		setaddress_debug(true, offset, 0);
	}

	// Video write (never by debugger)
	if (m_gatearray->csw_out()==ASSERT_LINE)
	{
		LOGMASKED(LOG_WRITE, "Video %04x <- %02x\n", offset, data);
		m_video->write((offset>>1)&3, data);   // M1=A13, M0=A14
	}

	// Gate array itself (Keyboard, mapper, GROM address (not by debugger))
	// Has no effect when not addressed
	m_gatearray->write(data);

	// Clock
	if (m_gatearray->rtcen_out()==ASSERT_LINE)
	{
		LOGMASKED(LOG_WRITE, "Clock %04x <- %02x\n", offset, data);
		m_rtc->write(offset & 0x000f, data);
	}

	// DRAM (also for GROM simulator)
	if (m_gatearray->accessing_dram())
	{
		// We block the write access to the DRAM for the Genmod when not in TI mode
		// This is not verified to happen on the real machine, but if we do not
		// block, page 3A will be declared as available, which is wrong.
		if (!m_genmod || !m_genmod_decoder->dben_out())
		{
			dramadd = m_gatearray->get_dram_address();
			m_dram->pointer()[dramadd] = data;
			const char* ramtype = (m_gatearray->accessing_grom())? "GROM" : "DRAM";
			LOGMASKED(LOG_WRITE, "%s %02x:%04x <- %02x\n", ramtype, page, addr13, data);
		}
	}

	// Sound
	if (m_gatearray->snden_out()==ASSERT_LINE)
	{
		LOGMASKED(LOG_WRITE, "Sound %04x <- %02x\n", offset, data);
		m_sound->write(data);
	}

	// Boot ROM or PFM (normal and Genmod)
	if (m_gatearray->romen_out()==ASSERT_LINE)
		write_pfm(offset, data);

	// Stock SRAM 32K
	if (m_gatearray->ramen_out()==ASSERT_LINE)
	{
		sramadd = m_gatearray->get_prefix(AB1 | AB2) | addr13;
		LOGMASKED(LOG_WRITE, "SRAM %02x:%04x <- %02x\n", page, addr13, data);
		m_sram->pointer()[sramadd] = data;
	}

	// Expanded SRAM (not in Genmod)
	if (!m_genmod && m_gatearray->ramenx_out()==ASSERT_LINE)
	{
		if (m_sram_size != SRAM32)
		{
			sramadd = m_gatearray->get_prefix(AB1 | AB2) | addr13;
			LOGMASKED(LOG_WRITE, "SRAMX %02x:%04x <- %02x\n", page, addr13, data);
			m_sramx->pointer()[sramadd] = data;
		}
		else
			LOGMASKED(LOG_WARN, "Access to SRAMX page %02x, but no SRAM expansion available\n", page);
	}

	// Ultimate SRAM expansion (not in Genmod)
	if (!m_genmod && m_gatearray->ramenu_out()==ASSERT_LINE)
	{
		if (m_sram_size == SRAM384)
		{
			sramadd = m_gatearray->get_prefix(FULLGEN) | addr13;
			LOGMASKED(LOG_READ, "SRAMU %02x:%04x -> %02x\n", page, addr13, data);
			m_sramu->pointer()[sramadd] = data;
		}
		else
			LOGMASKED(LOG_WARN, "Access to SRAMU page %02x, but no 384K SRAM expansion available\n", page);
	}

	// Peripheral box
	if ((m_genmod && m_genmod_decoder->dben_out())
		|| (!m_genmod && m_gatearray->dben_out()==ASSERT_LINE))
	{
		pboxadd = m_gatearray->get_prefix(m_genmod? FULLGNM : FULLGEN) | addr13;
		LOGMASKED(LOG_WRITE, "PEB %02x:%04x <- %02x\n", page, addr13, data);
		m_peribox->write(pboxadd, data);
		m_peribox->memen_in(CLEAR_LINE);
	}

	// In case we had a debugger write, reset the flag.
	m_gatearray->set_debug(false);
	if (m_genmod) m_genmod_decoder->set_debug(false);
}

/****************************************************************************
    PFM handling
*****************************************************************************/

void geneve_state::pfm_a17(int state)
{
	if (state==ASSERT_LINE) m_pfm_prefix |= 0x20000;
	else m_pfm_prefix &= ~0x20000;
}

void geneve_state::pfm_a18(int state)
{
	if (state==ASSERT_LINE) m_pfm_prefix |= 0x40000;
	else m_pfm_prefix &= ~0x40000;
}

void geneve_state::pfm_oe(int state)
{
	// Negative logic
	LOGMASKED(LOG_PFM, "PFM output %s\n", (state==0)? "enable" : "disable");
	m_pfm_oe = (state==0);
}

/*
    Boot ROM handling, from EPROM or PFM.
*/
void geneve_state::read_eprom_or_pfm(offs_t offset, uint8_t& value)
{
	int pfmaddress;
	offs_t addr13 = offset & 0x1fff;
	int page = m_gatearray->get_prefix(FULLGNM) >> 13;

	switch (m_boot_rom)
	{
	case GENEVE_EPROM:
		// Mirrors at pages F0, F2, F4,... FE, and F1, F3, ... FF.
		value = m_eprom[addr13 | m_gatearray->get_prefix(AB2)];
		LOGMASKED(LOG_READ, "EPROM %02x:%04x -> %02x\n", page, addr13, value);
		break;
	case GENEVE_PFM512:
		pfmaddress = addr13 | m_gatearray->get_prefix(AMA | AB0 | AB1 | AB2) | m_pfm_prefix;
		if (m_pfm_oe)
		{
			value = m_pfm512->read(pfmaddress);
			LOGMASKED(LOG_PFM, "PFM %02x:%04x -> %02x\n", page, addr13, value);
		}
		else LOGMASKED(LOG_PFM, "PFM512 disabled\n");
		break;
	case GENEVE_PFM512A:
		pfmaddress = addr13 | m_gatearray->get_prefix(AMA | AB0 | AB1 | AB2) | m_pfm_prefix;
		if (m_pfm_oe)
		{
			value = m_pfm512a->read(pfmaddress);
			LOGMASKED(LOG_PFM, "PFM %02x:%04x -> %02x\n", page, addr13, value);
		}
		else LOGMASKED(LOG_PFM, "PFM512a disabled\n");
		break;
	default:
		LOGMASKED(LOG_WARN, "Illegal mode for reading boot ROM: %d\n", m_boot_rom);
	}
}

void geneve_state::write_pfm(offs_t offset, uint8_t data)
{
	// Nota bene: The PFM must be write protected on startup, or the RESET
	// of the 9995 will attempt to write the return vector into the flash EEPROM
	offs_t addr13 = offset & 0x1fff;
	int pfmaddress = addr13 | m_gatearray->get_prefix(AMA | AB0 | AB1 | AB2) | m_pfm_prefix;
	int page = m_gatearray->get_prefix(FULLGNM) >> 13;

	switch (m_boot_rom)
	{
	case GENEVE_EPROM:
		LOGMASKED(LOG_WARN, "Write to EPROM at %02x:%04x ignored\n", page, addr13);
		break;
	case GENEVE_PFM512:
		m_pfm512->write(pfmaddress, data);
		LOGMASKED(LOG_PFM, "PFM %02x:%04x <- %02x\n", page, addr13, data);
		break;
	case GENEVE_PFM512A:
		m_pfm512a->write(pfmaddress, data);
		LOGMASKED(LOG_PFM, "PFMa %02x:%04x <- %02x\n", page, addr13, data);
		break;
	default:
		LOGMASKED(LOG_WARN, "Illegal mode for writing to PFM: %d\n", m_boot_rom);
	}
}

/****************************************************************************
    CRU handling
*****************************************************************************/

void geneve_state::cruwrite(offs_t offset, uint8_t data)
{
	offs_t cruaddr = offset << 1;

	// 9901 access: 0000..003e (fully decoded)
	if ((cruaddr & 0xffc0)==0)
		m_tms9901->write(offset & 0x1f, data);

	// Gate array: 13c0..13ce (Single step), write only
	if ((cruaddr & 0xfff0)==0x13c0)
		m_gatearray->cru_sstep_write(offset, data);

	// Gate array: 1ee0..1efe (mirror of 9995-internal flags), write only
	if ((cruaddr & 0xffe0)==0x1ee0)
		m_gatearray->cru_ctrl_write(offset, data);

	// Rest of the system
	m_peribox->cruwrite(cruaddr, data);
}

uint8_t geneve_state::cruread(offs_t offset)
{
	offs_t cruaddr = offset << 1;
	uint8_t value = 0;
	// 9901 access: 0000..003e (fully decoded)
	if ((cruaddr & 0xffc0)==0)
		value = m_tms9901->read(offset & 0x3f);

	// Propagate the CRU access to external devices
	m_peribox->crureadz(cruaddr, &value);
	return value;
}

/***********************************************************************
    CRU callbacks
***********************************************************************/

uint8_t geneve_state::psi_input(offs_t offset)
{
	switch (offset)
	{
	// External interrupt (INTA)
	case tms9901_device::INT1:
		return (m_inta==CLEAR_LINE)? 1 : 0;

	// Video interrupt
	case tms9901_device::INT2:
		return (m_int2==CLEAR_LINE)? 1 : 0;

	// Joystick port
	case tms9901_device::INT3:
	case tms9901_device::INT4:
	case tms9901_device::INT5:
	case tms9901_device::INT6:
	case tms9901_device::INT7_P15:
		return BIT(m_joyport->read_port(), offset-tms9901_device::INT3);

	// Keyboard interrupt
	case tms9901_device::INT8_P14:
		return (m_keyint==CLEAR_LINE)? 1 : 0;

	// Left mouse button
	case tms9901_device::INT10_P12:
		LOGMASKED(LOG_CRU, "Mouse button = %d\n", m_left_button);
		return (m_left_button==CLEAR_LINE)? 1 : 0;

	// TODO: Real time clock interrupt
	case tms9901_device::INT11_P11:
		return 1;

	// INTB interrupt
	case tms9901_device::INT12_P10:
		return (m_intb==CLEAR_LINE)? 1 : 0;

	default:
		// Pin 9 seems to be queried although there is no connection, maybe
		// by CRU multi-bit operation (STCR)
		// LOGMASKED(LOG_WARN, "Unknown pin %d\n", offset);
		return 1;
	}
}

void geneve_state::left_mouse_button(int state)
{
	m_left_button = state;
}

/*
    Write PE bus reset line
*/
void geneve_state::peripheral_bus_reset(int state)
{
	m_peribox->reset_in(state);
}

/*
    Write VDP reset line
*/
void geneve_state::VDP_reset(int state)
{
	m_video->reset_line(state);
}

/*
    Write joystick select line. 1 selects joystick 1 (pin 7), 0 selects joystick 2 (pin 2)
*/
void geneve_state::joystick_select(int state)
{
	m_joyport->write_port((state==ASSERT_LINE)? 1:2);
}

/*
   Keyboard reset (active low). Most keyboards do not use a dedicated reset
   line but trigger a reset when the clock line is held low for some time.
*/
void geneve_state::keyboard_reset(int state)
{
	if (state==CLEAR_LINE)
		LOG("Keyboard reset (line not connected)\n");
}

/*
    Called by the 9901 core whenever the state of INTREQ and IC0-3 changes.
    As with the TI-99/4A, the interrupt level is delivered as the offset,
    but again it is ignored. Anyway, the TMS9995 has only two external inputs
    (INT1 and INT4).
*/
void geneve_state::tms9901_interrupt(offs_t offset, uint8_t data)
{
	/* INTREQ is connected to INT1. */
	m_cpu->set_input_line(INT_9995_INT1, data);
}

/*******************************************************************
    Signal lines
*******************************************************************/

/*
    inta is connected to both tms9901 IRQ1 line and to tms9995 INT4/EC line.
*/
void geneve_state::inta(int state)
{
	m_inta = (state!=0)? ASSERT_LINE : CLEAR_LINE;
	m_tms9901->set_int_line(1, state);
	m_cpu->set_input_line(INT_9995_INT4, state);
}

/*
    intb is connected to tms9901 IRQ12 line.
*/
void geneve_state::intb(int state)
{
	m_intb = (state!=0)? ASSERT_LINE : CLEAR_LINE;
	m_tms9901->set_int_line(12, state);
}

/*
    set the state of int2 (called by the v9938 core)
*/
void geneve_state::int2_from_v9938(int state)
{
	// This method is frequently called without level change, so we only
	// react on changes
	if (state != m_int2)
	{
		m_int2 = (state!=0)? ASSERT_LINE : CLEAR_LINE;
		m_tms9901->set_int_line(2, state);
	}
}

/*
    Interrupt from the keyboard.
*/
void geneve_state::keyboard_interrupt(int state)
{
	m_keyint = (state!=0)? ASSERT_LINE : CLEAR_LINE;
	m_tms9901->set_int_line(8, state);
}

/*
    READY from the box is connected to the Gate Array and the Genmod board.
*/
void geneve_state::extready(int state)
{
	m_gatearray->extready_in(state);
	if (m_genmod)
		m_genmod_decoder->extready_in(state);
}

/*
    READY from the sound chip is connected to the Gate Array and the Genmod board.
*/
void geneve_state::sndready(int state)
{
	m_gatearray->sndready_in(state);
	if (m_genmod)
		m_genmod_decoder->sndready_in(state);
}

void geneve_state::external_operation(offs_t offset, uint8_t data)
{
	static char const *const extop[8] = { "inv1", "inv2", "IDLE", "RSET", "inv3", "CKON", "CKOF", "LREX" };
	if (offset != IDLE_OP)
		LOGMASKED(LOG_WARN, "External operation %s not implemented on Geneve board\n", extop[offset]);
}

/*
    Clock line from the CPU. Used to control wait state generation.
*/
void geneve_state::clock_out(int state)
{
	m_tms9901->phi_line(state);
	m_gatearray->clock_in(state);


	if (state==ASSERT_LINE)
	{
		// Video also has a GA waitstate, in addition to the video wait states
		// obviously, when comparing to the real machine

		int readyin = m_gatearray->gaready_out();
		if (m_genmod)
		{
			m_genmod_decoder->gaready_in(readyin);
			readyin = m_genmod_decoder->gaready_out();
		}
		m_pal->gaready_in(readyin);
	}
	else
	{
		// Stop the pulse after one cycle for video write. Video read
		// will be reset by the next access.
		if (m_gatearray->csw_out()==ASSERT_LINE)
		{
			m_pal->csw_in(CLEAR_LINE);
		}  // see pal_device::set_ready

		// The pulse must be active so that the READY line is asserted after
		// the ext waitstate
	}

	m_pal->clock_in(state);
}

void geneve_state::init_geneve()
{
	m_genmod = false;
}

void geneve_state::init_genmod()
{
	m_genmod = true;
}

void geneve_state::machine_start()
{
	save_item(NAME(m_inta));
	save_item(NAME(m_intb));
	save_item(NAME(m_int2));
	save_item(NAME(m_keyint));
	save_item(NAME(m_left_button));
	save_item(NAME(m_pfm_prefix));
	save_item(NAME(m_pfm_oe));
}

/*
    Reset the machine.
*/
void geneve_state::machine_reset()
{
	m_inta = CLEAR_LINE;    // flag reflecting the INTA line
	m_intb = CLEAR_LINE;    // flag reflecting the INTB line
	m_int2 = CLEAR_LINE;    // flag reflecting the INT2 line
	m_keyint = CLEAR_LINE;

	// READY=ASSERT; RESET -> no additional wait states
	// READY=CLEAR; RESET -> create wait state in every memory cycle
	m_cpu->ready_line(ASSERT_LINE);
	m_cpu->hold_line(CLEAR_LINE);
	m_cpu->reset_line(ASSERT_LINE);

	m_joyport->write_port(0x01);    // select Joystick 1

	// Configuring the VRAM size
	uint32_t videoram = (ioport("VRAM")->read()!=0)? 0x30000 : 0x20000;
	m_video->set_vram_size(videoram);
	LOGMASKED(LOG_CONFIG, "Video RAM set to %d KiB\n", videoram / 1024);

	// Check which boot EPROM we are using (or PFM)
	m_eprom = memregion("maincpu")->base();
	m_boot_rom = ioport("BOOTROM")->read();

	if (m_genmod)
	{
		m_genmod_decoder->set_turbo((ioport("GENMODDIPS")->read() & GENEVE_GM_TURBO)!=0);
		m_genmod_decoder->set_timode((ioport("GENMODDIPS")->read() & GENEVE_GM_TIM)!=0);
	}
	else
	{
		// SRAM expansion
		// Only applies to the standard Geneve; Genmod uses the Memex instead
		m_sram_size = (ioport("SRAM")->read());
	}
}

void geneve_state::geneve(machine_config &config)
{
	geneve_common(config);

	// Gate array
	GENEVE_GATE_ARRAY(config, m_gatearray, 0);
	m_gatearray->kbdint_cb().set(FUNC(geneve_state::keyboard_interrupt));
	m_gatearray->kbdclk_cb().set(m_kbdconn, FUNC(pc_kbdc_device::clock_write_from_mb));
	m_gatearray->kbddata_cb().set(m_kbdconn, FUNC(pc_kbdc_device::data_write_from_mb));

	// Peripheral expansion box (Geneve composition)
	TI99_PERIBOX_GEN(config, m_peribox, 0);
	m_peribox->inta_cb().set(FUNC(geneve_state::inta));
	m_peribox->intb_cb().set(FUNC(geneve_state::intb));
	m_peribox->ready_cb().set(FUNC(geneve_state::extready));
}

void geneve_state::genmod(machine_config &config)
{
	geneve_common(config);
	GENMOD_DECODER(config, m_genmod_decoder, 0);

	// Gate Array
	GENEVE_GATE_ARRAY(config, m_gatearray, 0);
	m_gatearray->kbdint_cb().set(FUNC(geneve_state::keyboard_interrupt));
	m_gatearray->kbdclk_cb().set(m_kbdconn, FUNC(pc_kbdc_device::clock_write_from_mb));
	m_gatearray->kbddata_cb().set(m_kbdconn, FUNC(pc_kbdc_device::data_write_from_mb));

	// Peripheral expansion box (Geneve composition with Genmod and plugged-in Memex)
	TI99_PERIBOX_GENMOD(config, m_peribox, 0);
	m_peribox->inta_cb().set(FUNC(geneve_state::inta));
	m_peribox->intb_cb().set(FUNC(geneve_state::intb));
	m_peribox->ready_cb().set(FUNC(geneve_state::extready));
}

void geneve_state::geneve_common(machine_config &config)
{
	// basic machine hardware
	// TMS9995 CPU @ 12.0 MHz
	TMS9995(config, m_cpu, 12000000);
	m_cpu->set_addrmap(AS_PROGRAM, &geneve_state::memmap);
	m_cpu->set_addrmap(AS_IO, &geneve_state::crumap);
	m_cpu->set_addrmap(tms9995_device::AS_SETADDRESS, &geneve_state::memmap_setaddress);
	m_cpu->extop_cb().set(FUNC(geneve_state::external_operation));
	m_cpu->clkout_cb().set(FUNC(geneve_state::clock_out));

	// Video hardware
	v99x8_device& video(V9938(config, TIGEN_V9938_TAG, XTAL(21'477'272))); // typical 9938 clock, not verified
	video.set_vram_size(0x20000);
	video.int_cb().set(FUNC(geneve_state::int2_from_v9938));
	video.set_screen(GENEVE_SCREEN_TAG);
	screen_device& screen(SCREEN(config, GENEVE_SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_raw(XTAL(21'477'272),
		v99x8_device::HTOTAL,
		0,
		v99x8_device::HVISIBLE - 1,
		v99x8_device::VTOTAL_NTSC * 2,
		v99x8_device::VERTICAL_ADJUST * 2,
		v99x8_device::VVISIBLE_NTSC * 2 - 1 - v99x8_device::VERTICAL_ADJUST * 2);
	screen.set_screen_update(TIGEN_V9938_TAG, FUNC(v99x8_device::screen_update));

	// Main board components
	TMS9901(config, m_tms9901, 0);
	m_tms9901->read_cb().set(FUNC(geneve_state::psi_input));
	m_tms9901->p_out_cb(0).set(FUNC(geneve_state::peripheral_bus_reset));
	m_tms9901->p_out_cb(1).set(FUNC(geneve_state::VDP_reset));
	m_tms9901->p_out_cb(2).set(FUNC(geneve_state::joystick_select));
	m_tms9901->p_out_cb(6).set(FUNC(geneve_state::keyboard_reset));
	m_tms9901->p_out_cb(7).set(GENEVE_PAL_TAG, FUNC(bus::ti99::internal::geneve_pal_device::sysspeed));
	m_tms9901->p_out_cb(9).set(GENEVE_PAL_TAG, FUNC(bus::ti99::internal::geneve_pal_device::vwaiten));
	m_tms9901->intreq_cb().set(FUNC(geneve_state::tms9901_interrupt));

	// PFM expansion: Select the 2^17 and 2^18 bit and the output enable
	m_tms9901->p_out_cb(4).set(FUNC(geneve_state::pfm_a17));
	m_tms9901->p_out_cb(5).set(FUNC(geneve_state::pfm_oe));
	m_tms9901->p_out_cb(13).set(FUNC(geneve_state::pfm_a18));

	// Clock
	MM58274C(config, GENEVE_CLOCK_TAG, 0).set_mode_and_day(1, 0); // 24h, sunday

	// PAL
	GENEVE_PAL(config, m_pal, 0);
	m_pal->ready_cb().set("maincpu", FUNC(tms9995_device::ready_line));

	// Sound hardware
	SPEAKER(config, "sound_out").front_center();
	SN76496(config, m_sound, XTAL(21'477'272)/6); // Delivered by the CLKOUT of the V9938
	m_sound->add_route(ALL_OUTPUTS, "sound_out", 0.75);
	m_sound->ready_cb().set(FUNC(geneve_state::sndready));

	// User interface devices: PC-style keyboard, joystick port, mouse connector
	PC_KBDC(config, m_kbdconn, geneve_xt_keyboards, STR_KBD_GENEVE_XT_101_HLE);
	m_kbdconn->out_clock_cb().set(GENEVE_GATE_ARRAY_TAG, FUNC(bus::ti99::internal::geneve_gate_array_device::kbdclk));
	m_kbdconn->out_data_cb().set(GENEVE_GATE_ARRAY_TAG, FUNC(bus::ti99::internal::geneve_gate_array_device::kbddata));

	TI99_JOYPORT(config, m_joyport, 0, ti99_joyport_options_plain, "twinjoy");
	V9938_COLORBUS(config, m_colorbus, 0, ti99_colorbus_options, nullptr);
	m_colorbus->extra_button_cb().set(FUNC(geneve_state::left_mouse_button));

	// PFM expansion
	AT29C040(config, GENEVE_PFM512_TAG);
	AT29C040A(config, GENEVE_PFM512A_TAG);

	// DRAM 512K
	RAM(config, GENEVE_DRAM_TAG).set_default_size("512K").set_default_value(0);

	// SRAM
	RAM(config, GENEVE_SRAM_TAG).set_default_size("32K").set_default_value(0);
	RAM(config, GENEVE_SRAMX_TAG).set_default_size("32K").set_default_value(0);

	// Ultimate SRAM expansion
	RAM(config, GENEVE_SRAMU_TAG).set_default_size("384K").set_default_value(0);
}

/*
    ROM loading
*/

ROM_START(geneve)
	/*CPU memory space*/
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_DEFAULT_BIOS("0.98")
	ROM_SYSTEM_BIOS(0, "0.98", "Geneve Boot ROM 0.98 (1987)")
	ROMX_LOAD("genbt098.bin", 0x0000, 0x4000, CRC(b2e20df9) SHA1(2d5d09177afe97d63ceb3ad59b498b1c9e2153f7), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "1.00", "Geneve Boot ROM 1.00 (1990)")
	ROMX_LOAD("genbt100.bin", 0x0000, 0x4000, CRC(8001e386) SHA1(b44618b54dabac3882543e18555d482b299e0109), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "2.00", "Geneve Boot ROM 2.00 (2021)")
	ROMX_LOAD("genbt200.bin", 0x0000, 0x4000, CRC(cc159fd6) SHA1(15d3bb48edb301364ecbd42025c4a2539cc3070d), ROM_BIOS(2))
ROM_END

ROM_START(genmod)
	/*CPU memory space*/
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_DEFAULT_BIOS("1.00")
	ROM_SYSTEM_BIOS(0, "1.00", "Geneve Mod Boot ROM 1.00 (1990)")
	ROMX_LOAD("gnmbt100.bin", 0x0000, 0x4000, CRC(19b89479) SHA1(6ef297eda78dc705946f6494e9d7e95e5216ec47), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "2.00", "Geneve Mod Boot ROM 2.00 (2021)")
	ROMX_LOAD("gnmbt200.bin", 0x0000, 0x4000, CRC(0a66c714) SHA1(139ed03d365b21123295cd99c73736ee424dbb74), ROM_BIOS(1))
ROM_END

} // anonymous namespace


//    YEAR  NAME    PARENT  COMPAT  MACHINE      INPUT   CLASS         INIT         COMPANY  FULLNAME       FLAGS
COMP( 1987, geneve, 0,      0,      geneve,      geneve, geneve_state, init_geneve, "Myarc", "Geneve 9640", MACHINE_SUPPORTS_SAVE)
COMP( 1990, genmod, 0,      0,      genmod,      genmod, geneve_state, init_genmod, "Myarc / Ron G. Walters", "Geneve 9640 Mod",  MACHINE_SUPPORTS_SAVE)



geniuscolor.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:


/*************************************************************************************************************

    Skeleton driver for VTech Genius Color Pocket / Super Color Pocket / Genio Color Pocket.

    VTech 35-140500-100-203 PCB with MX25L3206E and N25S10 serial ROMs on one side and two globs on the other.
    Unknown CPU, program ROM seems compressed.

*************************************************************************************************************/


#include "emu.h"

#include "screen.h"
#include "speaker.h"


namespace {


class geniuscolor_state : public driver_device
{
public:
	geniuscolor_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_screen(*this, "screen")
	{ }

	void geniuscolor(machine_config &config) ATTR_COLD;

protected:
	required_device<screen_device> m_screen;

	uint32_t screen_update_geniuscolor(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};

uint32_t geniuscolor_state::screen_update_geniuscolor(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

// 45-keys "slider" keyboard, 8 activity buttons, two direction keys (right, left) and home, OK and power buttons.
INPUT_PORTS_START( geniuscolor )
INPUT_PORTS_END

void geniuscolor_state::geniuscolor(machine_config &config)
{
	// Unknown CPU

	SCREEN(config, m_screen, SCREEN_TYPE_LCD); // 104x48 color LCD screen
	m_screen->set_refresh_hz(60); // Guess
	m_screen->set_size(104, 48);
	m_screen->set_visarea(0, 104-1, 0, 48-1);
	m_screen->set_screen_update(FUNC(geniuscolor_state::screen_update_geniuscolor));

	SPEAKER(config, "mono").front_left();
}

// Spanish machine
ROM_START( geniuscps )
	ROM_REGION( 0x010000, "maincpu", 0 )
	ROM_LOAD( "internal.bin",       0x000000, 0x010000, NO_DUMP ) // Unknown CPU type, unknown internal ROM size

	ROM_REGION( 0x400000, "program", 0 )
	ROM_LOAD( "mx25l3206e.u1",      0x000000, 0x400000, CRC(fcc2e78d) SHA1(7f166256a10acfe854bac3fd2426ec4173d66518) ) // Compressed data?

	ROM_REGION( 0x010000, "soundcpu", 0 )
	ROM_LOAD( "sound_internal.bin", 0x000000, 0x010000, NO_DUMP ) // Unknown CPU type, unknown internal ROM size

	ROM_REGION( 0x20000, "user", 0 ) // Probably user data
	ROM_LOAD( "n25s10.u6",          0x000000, 0x020000, CRC(c5508360) SHA1(87c0855c90af2545a074df82411e5679e7309692) )
ROM_END

} // anonymous namespace


CONS( 2013, geniuscps, 0, 0, geniuscolor, geniuscolor, geniuscolor_state, empty_init, "VTech", "Genio Color Pocket (Spanish)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



geniusiq.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************
Video Technology Genius computers:
    PreComputer Unlimited (USA and Canada)
    VTech Genius PC (France)
    VTech Genius IQ 512 (Germany)
    The French packaging mentions distributions in Switzerland, the Netherlands,
    and UK as well. Looking for more information and ROM dumps.

System driver:

    Adrien Destugues <pulkomandy@gmail.com>, May 2012
      - First attempt

Memory map:
    00000000 System ROM (2MB)
    00200000 RAM (256K)
    00400000 Flash memory (128K)
    00600000 Some memory mapped hardware
    00a00000 Cartridge port

TODO:
    - Sound
    - Flash cartridge
    - Dump the MCU and rewrites everything using low-level emulation
    - Check with different countries ROMs

Not very much is known about this computer released in 1997.


PCB - German Version:
                       +----------------------------------+   +-------------------+
 +-----------------+   |                                  |   |                   +-------+   +--------------+
 |                 +---+                                  +---+                           +---+              |
 |                                                                                                           |
 |                                                                                                           |
 |                                                                                                           |
 |                                                                                                           |
 |                                                                                                           |
 |                                                                                                           |
 |                                                                                                           |
 |                            ###########################   +---------+                                      +-------+
 |                            #                         #   | XTAL    |                           +-------+   -------|
 |    +-------+               # +-------------------+   #   |26.601712|                           | VTECH |   -------|
 |    |LMT324D|               # | PALCE20V8H-25PC   |   #   |KDS17H   |                           |LH537- |   -------|
 |    +-------+               # |                   |   #   +---------+                           |   -NWL|   -------|
 |                            # +-------------------+   ########                   +--------+     |       |   -------|
 |                            #                                #                   |        |     |1997   |   -------|
 |                            # +-------------+     +-------+  #                   |        |     |27-5947|   -------|
 |                            # |MC74HC4060AN |     |MEC/ZTB|  #                   |AM29F010|     |-00    |   -------|
 |                            # |             |     | 307D  |  #                   |-120PC  |     |       |   -------|
 |                            # +-------------+     |       |  #                   |        |     |9741 D |   -------|
 |                            #010                  +-------+  #                   |        |     +-------+  +-------+
 |                            ##################################                   |9731MBM |                |
 |                                         |                  |                    |       A|                |
 |                                         |       VTECH      |                    |        |                |
 |                                         |   27-05793-0-0   |                    |        |                |
 |                                         |     ZKAL9736     |                    |        |                |
 |                                         |                  |                    |        |                |
 |                                         |                  |                    |        |                |
 |                                         |                  |                    |        |                |
 |           +--------+                    |                  |                    |1991 AMD|                |
 |           |BH7236AF|                    |                  |                    +--------+                |
 |           +--------+                    +------------------+                                              |
 |                           +-----+                                                                         |
 |                           |XTAL |                                                                         |
 |                           |     |                                                                         |
 |                           |32.00|                                                                         |
 |                           |0    |                                                                         |
 |                           +-----+                                                            9740         |
 |                                                                +---------+                   GER          |
 |                                     +----------+               |MC68EC000|                   016          |
 |                                     |HY534256AL|               |FU16     |                                |
 |                                     |J-60      |               |         |                                |
 |                                     +----------+               |    0G74K|                                |
 |                                                                | HHIG9728|                                |
 |                                     +----------+               +---------+                                |
 |    +------+                         |HY534256AL|                                                          |
 |    |9727H |                         |J-60      |                                                          |
 |    |C807U-|                         +----------+                                                          |
 |    |1225  |                                                                                               |
 |    +------+                                                                                               |
 |                                                                                           35-13300-28     |
 |                                                                                                           |
 |                                                              35-13300-28 702748-E CS                      |
 |      +----+                                      +----+      E403                         +----+          |
 +------+    +--------------------------------------+    +-----------------------------------+    +----------+



 IQ TV 512 PCB (German version)
                       +----------------------------------+   +-------------------+
 +-----------------+   |                                  |   |                   +-------+   +--------------+
 |                 +---+                                  +---+                           +---+              |
 |                                                                                                           |
 |                                                                                                           |
 |                                                                                                           |
 |                                                                                                           |
 |                                                                                                           |
 |                                                                                                           |
 |                                                                                                           |
 |                            ###########################   +---------+                                      +-------+
 |                            #                         #   | XTAL    |                           +-------+   -------|
 |    +-------+               # +-------------------+   #   |26.601712|                           | VTECH |   -------|
 |    |LM324D |               # | PALCE20V8H-25PC   |   #   |KDS17H   |                           |LH537- |   -------|
 |    +-------+               # |                   |   #   +---------+                           |   -NUA|   -------|
 |                            # +-------------------+   ########                   +--------+     |       |   -------|
 |                            #                                #                   |        |     |1998   |   -------|
 |                            # +-------------+     +-------+  #                   |        |     |27-0617|   -------|
 |                            # |MC74HC4060AN |     |MEC/ZTB|  #                   |AM29F040|     |1-000  |   -------|
 |                            # |             |     | 307D  |  #                   |B-120PC |     |       |   -------|
 |                            # +-------------+     |       |  #                   |        |     |9824 D |   -------|
 |                            #010                  +-------+  #                   |        |     +-------+  +-------+
 |                            ##################################                   |9808MBM |                |
 |                                         |                  |                    |       A|                |
 |                                         |       VTECH      |                    |        |                |
 |                                         |   27-05793-0-0   |                    |        |                |
 |                                         |     ZKAH9805     |                    |        |                |
 |                                         |                  |                    |        |                |
 |                                         |                  |                    |        |                |
 |                                         |                  |                    |        |                |
 |           +--------+                    |                  |                    |1993 AMD|                |
 |           |BH7236AF|                    |                  |                    +--------+                |
 |           +--------+                    +------------------+                                              |
 |                           +-----+                                                                         |
 |                           |XTAL |                                                                         |
 |                           |     |                                                                         |
 |                           |32.00|                                                                         |
 |                           |0    |                                                                         |
 |                           +-----+                                                            9749         |
 |                                                                +---------+                   GER          |
 |                                     +----------+               |MC68EC000|                   023          |
 |                                     |HY534256AL|               |FU16     |                                |
 |                                     |J-60      |               |         |                                |
 |                                     +----------+               |    0G74K|                                |
 |                                                                | HHIG9728|                                |
 |                                     +----------+               +---------+                                |
 |    +------+                         |HY534256AL|                                                          |
 |    |9805HB|                         |J-60      |                                                          |
 |    |C807U-|                         +----------+                                                          |
 |    |1225  |                                                                                               |
 |    +------+                                                                                               |
 |                                                                                           35-13300-28     |
 |                                                                                                           |
 |                                                              35-13300-28 702749-E CS                      |
 |      +----+                                      +----+      E493                         +----+          |
 +------+    +--------------------------------------+    +-----------------------------------+    +----------+



****************************************************************************/

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "machine/intelfsh.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"


namespace {

#define KEYBOARD_QUEUE_SIZE     0x80

class geniusiq_state : public driver_device
{
public:
	geniusiq_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_cart(*this, "cartslot"),
		m_rom(*this, "maincpu"),
		m_vram(*this, "vram"),
		m_mouse_gfx(*this, "mouse_gfx"),
		m_cart_state(IQ128_NO_CART)
	{ }

	void iqtv512(machine_config &config);
	void iq128(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(send_input);
	DECLARE_INPUT_CHANGED_MEMBER(send_mouse_input);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	enum
	{
		IQ128_ROM_CART      = 0x00,
		IQ128_ROMLESS1_CART = 0x01,
		IQ128_ROMLESS2_CART = 0x02,
		IQ128_NO_CART       = 0x03
	};

	required_device<cpu_device> m_maincpu;
	required_device<generic_slot_device> m_cart;
	required_region_ptr<uint16_t> m_rom;
	required_shared_ptr<uint16_t> m_vram;
	required_shared_ptr<uint16_t> m_mouse_gfx;

	void geniusiq_palette(palette_device &palette) const;
	virtual uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	uint16_t input_r();
	void mouse_pos_w(offs_t offset, uint16_t data);
	void gfx_base_w(offs_t offset, uint16_t data);
	void gfx_dest_w(offs_t offset, uint16_t data);
	void gfx_color_w(offs_t offset, uint16_t data);
	void gfx_idx_w(uint16_t data);
	void queue_input(uint16_t data);
	uint16_t cart_state_r();

	uint16_t unk0_r() { return 0; }
	uint16_t unk_r() { return machine().rand(); }
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
	DECLARE_DEVICE_IMAGE_UNLOAD_MEMBER(cart_unload);

	void geniusiq_mem(address_map &map) ATTR_COLD;

	uint16_t      m_gfx_y = 0;
	uint16_t      m_gfx_x = 0;
	uint32_t      m_gfx_base = 0;
	uint8_t       m_gfx_color[2]{};
	uint8_t       m_mouse_posx = 0;
	uint8_t       m_mouse_posy = 0;
	uint16_t      m_mouse_gfx_posx = 0;
	uint16_t      m_mouse_gfx_posy = 0;
	uint8_t       m_cart_state = 0;
	struct
	{
		uint16_t  buffer[KEYBOARD_QUEUE_SIZE]{};
		int     head = 0;
		int     tail = 0;
	} m_keyboard;
};


void geniusiq_state::geniusiq_palette(palette_device &palette) const
{
	// shades need to be verified
	constexpr rgb_t palette_val[] =
	{
		{ 0x00, 0x00, 0x00 },   // Black?? (used in the cursor for transparency)
		{ 0xff, 0xff, 0xff },   // White
		{ 0xa0, 0xa0, 0xa0 },   // Light grey
		{ 0x7f, 0x7f, 0x7f },   // Dark grey
		{ 0x00, 0x00, 0x00 },   // Black
		{ 0x00, 0x60, 0xff },   // Sky blue
		{ 0x00, 0x00, 0xff },   // Blue
		{ 0xff, 0x00, 0x00 },   // Red
		{ 0x00, 0xff, 0x00 },   // Green
		{ 0x00, 0x7f, 0x00 },   // Dark green
		{ 0xff, 0xff, 0x00 },   // Yellow
		{ 0xff, 0x7f, 0x00 },   // Orange
		{ 0x7f, 0x40, 0x00 },   // Brown
		{ 0x60, 0x40, 0x00 },   // Dark brown
		{ 0x60, 0x00, 0xff },   // Mauve
		{ 0xff, 0x00, 0xff }    // Pink
	};

	palette.set_pen_colors(0, palette_val);
}


uint32_t geniusiq_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (int y=0; y<256; y++)
		for (int x=0; x<256; x+=2)
		{
			uint16_t data = m_vram[(y*256 + x)>>1];

			for(int b=0; b<4; b++)
			{
				bitmap.pix(y, x*2 + b) = (data>>12) & 0x0f;
				data <<= 4;
			}
		}

	// mouse cursor
	for (int y=0; y<16; y++)
		for (int x=0; x<6; x+=2)
		{
			uint16_t data = m_mouse_gfx[(y*6 + x)>>1];

			for(int b=0; b<8; b++)
			{
				uint8_t pen = (data>>14) & 0x03;

				// I assume color 0 is transparent
				if(pen != 0 && screen.visible_area().contains(m_mouse_gfx_posx + x*4 + b, m_mouse_gfx_posy + y))
					bitmap.pix(m_mouse_gfx_posy + y, m_mouse_gfx_posx + x*4 + b) = pen;
				data <<= 2;
			}
		}

	return 0;
}

uint16_t geniusiq_state::cart_state_r()
{
	return m_cart_state;
}

void geniusiq_state::mouse_pos_w(offs_t offset, uint16_t data)
{
	if (offset)
		m_mouse_gfx_posy = data;
	else
		m_mouse_gfx_posx = data;
}

void geniusiq_state::gfx_color_w(offs_t offset, uint16_t data)
{
	m_gfx_color[offset & 1] = data & 0x0f;
}

void geniusiq_state::gfx_dest_w(offs_t offset, uint16_t data)
{
	if (offset)
		m_gfx_y = data;
	else
		m_gfx_x = data;
}

void geniusiq_state::gfx_base_w(offs_t offset, uint16_t data)
{
	if (offset)
		m_gfx_base = (m_gfx_base & 0xffff0000) | (data<<0);
	else
		m_gfx_base = (m_gfx_base & 0x0000ffff) | (data<<16);
}

void geniusiq_state::gfx_idx_w(uint16_t data)
{
	uint16_t *gfx = m_rom + ((m_gfx_base + (data & 0xff)*32)>>1);

	// first 16 bits are used to define the character size
	uint8_t gfx_heigh = (gfx[0]>>0) & 0xff;
	uint8_t gfx_width = (gfx[0]>>8) & 0xff;

	for(int y=0; y<gfx_heigh; y++)
		for(int x=0; x<gfx_width; x++)
		{
			uint16_t src = gfx[y + 1];
			uint32_t dst = (m_gfx_y + y)*512 + (m_gfx_x + x);
			uint8_t pen = m_gfx_color[BIT(src,15-x)];
			int bit_pos = (3 - (dst & 3)) << 2;

			m_vram[dst>>2] = (m_vram[dst>>2] & ~(0x0f << bit_pos)) | (pen << bit_pos);
		}
}

uint16_t geniusiq_state::input_r()
{
	/*
	    this is guesswork and may not be correct

	    xxxx xxx- ---- ----     unknown
	    ---- ---x ---- ----     used for indicate if the data read is valid (if not set the other bits are discarded)
	    ---- ---- x--- ----     if set indicates a KeyUp otherwise a KeyDown
	    ---- ---- -xxx xxxx     this is the scan code
	*/

	uint16_t data = 0;

	if(m_keyboard.head != m_keyboard.tail)
	{
		data = m_keyboard.buffer[m_keyboard.head];

		m_keyboard.head = (m_keyboard.head+1) % KEYBOARD_QUEUE_SIZE;

		data |= (1<<8);
	}

	if(m_keyboard.head == m_keyboard.tail)
		m_maincpu->set_input_line(M68K_IRQ_4, CLEAR_LINE);

	return data;
}

void geniusiq_state::queue_input(uint16_t data)
{
	m_keyboard.buffer[m_keyboard.tail] = data;

	m_keyboard.tail = (m_keyboard.tail+1) % KEYBOARD_QUEUE_SIZE;

	// new data in queue
	m_maincpu->set_input_line(M68K_IRQ_4, ASSERT_LINE);
}

INPUT_CHANGED_MEMBER( geniusiq_state::send_mouse_input )
{
	uint8_t new_mouse_x = ioport("MOUSEX")->read();
	uint8_t new_mouse_y = ioport("MOUSEY")->read();
	uint8_t mouse_buttons = ioport("MOUSE")->read();

	uint8_t delta_x = (uint8_t)(new_mouse_x - m_mouse_posx);
	uint8_t delta_y = (uint8_t)(new_mouse_y - m_mouse_posy);
	m_mouse_posx = new_mouse_x;
	m_mouse_posy = new_mouse_y;

	queue_input(0x1000 | 0x40 | mouse_buttons | ((delta_y>>4) & 0x0c) | ((delta_x>>6) & 0x03));
	queue_input(0x1000 | (delta_x & 0x3f));
	queue_input(0x1000 | (delta_y & 0x3f));
}

INPUT_CHANGED_MEMBER( geniusiq_state::send_input )
{
	uint16_t data = (uint16_t)param;

	// set bit 7 if the key is released
	if (!newval)
		data |= 0x80;

	queue_input(data);
}


void geniusiq_state::geniusiq_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x1FFFFF).rom();
	map(0x200000, 0x23FFFF).ram();
	map(0x300000, 0x30FFFF).ram().share("vram");
	map(0x310000, 0x31FFFF).ram();
	map(0x400000, 0x41ffff).mirror(0x0e0000).rw("flash", FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write)).umask16(0x00ff);
	map(0x600300, 0x600301).r(FUNC(geniusiq_state::input_r));
	//map(0x600500, 0x60050f)                      // read during IRQ 5
	//map(0x600600, 0x600605)                      // sound ??
	map(0x600606, 0x600609).w(FUNC(geniusiq_state::gfx_base_w));
	map(0x60060a, 0x60060b).w(FUNC(geniusiq_state::gfx_idx_w));
	map(0x600802, 0x600803).r(FUNC(geniusiq_state::cart_state_r));  // cartridge state
	map(0x600108, 0x600109).r(FUNC(geniusiq_state::unk0_r));        // read before run a BASIC program
	map(0x600918, 0x600919).r(FUNC(geniusiq_state::unk0_r));        // loop at start if bit 0 is set
	map(0x601008, 0x601009).r(FUNC(geniusiq_state::unk_r));         // unknown, read at start and expect that bit 2 changes several times before continue
	map(0x601010, 0x601011).r(FUNC(geniusiq_state::unk0_r));        // loop at start if bit 1 is set
	map(0x601018, 0x60101b).w(FUNC(geniusiq_state::gfx_dest_w));
	map(0x60101c, 0x60101f).w(FUNC(geniusiq_state::gfx_color_w));
	map(0x601060, 0x601063).w(FUNC(geniusiq_state::mouse_pos_w));
	map(0x601100, 0x6011ff).ram().share("mouse_gfx");   // mouse cursor gfx (24x16)
	map(0xa00000, 0xafffff).r(m_cart, FUNC(generic_slot_device::read16_rom));
	// 0x600000 : some memory mapped hardware
}

// Input ports
static INPUT_PORTS_START( geniusiq )
	PORT_START( "IN0" )
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x00)
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x01)
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_LCONTROL )  PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x02)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_LSHIFT )    PORT_CHAR(UCHAR_SHIFT_1)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x03)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_RSHIFT )    PORT_CHAR(UCHAR_MAMEKEY(RSHIFT)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x04)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_LALT )      PORT_CHAR(UCHAR_MAMEKEY(LALT))  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x05)
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F1")                PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x06)
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F2")                PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x07)
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F3")                PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x08)
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F4")                PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x09)
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F5")                PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x0a)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F6")                PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x0b)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F7")                PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x0c)
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F8")                PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x0d)
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F9")                PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x0e)
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F10")               PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x0f)

	PORT_START( "IN1" )
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x10)
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x11)
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x12)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x13)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x14)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x15)
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x16)
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_QUOTE )     PORT_CHAR('\'') PORT_CHAR('~')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x17)
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_BACKSLASH ) PORT_CHAR('$')                  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x18)
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_EQUALS )    PORT_CHAR('=')  PORT_CHAR('+')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x19)
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_CLOSEBRACE ) PORT_CHAR(')') PORT_CHAR(0x00b0)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x1a)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_OPENBRACE )  PORT_CHAR(0x00f9)  PORT_CHAR('%')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x1b)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_SLASH )     PORT_CHAR('^')                  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x1c)
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_M )         PORT_CHAR('m')  PORT_CHAR('M')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x1d)
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_P )         PORT_CHAR('p')  PORT_CHAR('P')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x1e)
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_TILDE )     PORT_CHAR('!')  PORT_CHAR('*')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x1f)

	PORT_START( "IN2" )
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_0 )         PORT_CHAR(0x00e0)   PORT_CHAR('0')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x20)
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Return") PORT_CODE( KEYCODE_ENTER )     PORT_CHAR(13)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x21)
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Backspace") PORT_CODE( KEYCODE_BACKSPACE ) PORT_CHAR(8)  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x22)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_L )         PORT_CHAR('l')  PORT_CHAR('L')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x23)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_O )         PORT_CHAR('o')  PORT_CHAR('O')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x24)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_9 )         PORT_CHAR(0x00e7)   PORT_CHAR('9')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x25)
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x26)
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Mouse Button 2 (keyboard)") PORT_CODE( KEYCODE_F3 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x27)
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x28)
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_PGDN )      PORT_CHAR(UCHAR_MAMEKEY(PGDN))  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x29)
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_PGUP )      PORT_CHAR(UCHAR_MAMEKEY(PGUP))  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x2a)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_STOP )      PORT_CHAR(':')  PORT_CHAR('/')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x2b)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_COLON )     PORT_CHAR(';')  PORT_CHAR('.')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x2c)
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_K )         PORT_CHAR('k')  PORT_CHAR('K')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x2d)
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_I )         PORT_CHAR('i')  PORT_CHAR('I')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x2e)
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Mouse Button 1 (keyboard)") PORT_CODE( KEYCODE_F2 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x2f)

	PORT_START( "IN3" )
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_8 )         PORT_CHAR('_')  PORT_CHAR('8')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x30)
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_ESC )       PORT_CHAR(UCHAR_MAMEKEY(ESC))   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x31)
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_COMMA )     PORT_CHAR(',')  PORT_CHAR('?')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x32)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_J )         PORT_CHAR('j')  PORT_CHAR('J')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x33)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_U )         PORT_CHAR('u')  PORT_CHAR('U')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x34)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_7 )         PORT_CHAR(0x00e8)   PORT_CHAR('7')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x35)
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x36)
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x37)
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x38)
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_PRTSCR )    PORT_CHAR(UCHAR_MAMEKEY(PRTSCR))    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x39)
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_MINUS )     PORT_CHAR('-')  PORT_CHAR(0x00a3)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x3a)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_DEL )       PORT_CHAR(UCHAR_MAMEKEY(DEL))   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x3b)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_N )         PORT_CHAR('n')  PORT_CHAR('N')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x3c)
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_H )         PORT_CHAR('h')  PORT_CHAR('H')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x3d)
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_Y )         PORT_CHAR('y')  PORT_CHAR('Y')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x3e)
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_NUMLOCK )   PORT_CHAR(UCHAR_MAMEKEY(NUMLOCK))   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x3f)

	PORT_START( "IN4" )
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_6 )         PORT_CHAR('-')  PORT_CHAR('6')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x40)
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_TAB )       PORT_CHAR('\t')                 PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x41)
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_B )         PORT_CHAR('b')  PORT_CHAR('B')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x42)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_G )         PORT_CHAR('g')  PORT_CHAR('G')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x43)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_T )         PORT_CHAR('t')  PORT_CHAR('T')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x44)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_5 )         PORT_CHAR('(')  PORT_CHAR('5')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x45)
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x46)
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_CAPSLOCK )  PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x47)
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x48)
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_HOME )      PORT_CHAR(UCHAR_MAMEKEY(HOME))  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x49)
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_BACKSLASH2 ) /*PORT_CHAR('')*/  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x4a)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_SPACE )     PORT_CHAR(' ')                  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x4b)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_V )         PORT_CHAR('v')  PORT_CHAR('V')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x4c)
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_F )         PORT_CHAR('f')  PORT_CHAR('F')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x4d)
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_R )         PORT_CHAR('r')  PORT_CHAR('R')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x4e)
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Mouse Up Button")   PORT_CODE( KEYCODE_UP )         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x4f)

	PORT_START( "IN5" )
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_4 )         PORT_CHAR('\'') PORT_CHAR('4')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x50)
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_C )         PORT_CHAR('c')  PORT_CHAR('C')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x51)
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_D )         PORT_CHAR('d')  PORT_CHAR('D')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x52)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_E )         PORT_CHAR('e')  PORT_CHAR('E')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x53)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_3 )         PORT_CHAR('"')  PORT_CHAR('3')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x54)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x55)
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x56)
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Mouse Down Button") PORT_CODE( KEYCODE_DOWN )       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x57)
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_INSERT )    PORT_CHAR(UCHAR_MAMEKEY(INSERT))    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x58)
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_X )         PORT_CHAR('x')  PORT_CHAR('X')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x59)
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_S )         PORT_CHAR('s')  PORT_CHAR('S')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x5a)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_Z )         PORT_CHAR('z')  PORT_CHAR('Z')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x5b)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_2 )         PORT_CHAR(0x00e9)   PORT_CHAR('2')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x5c)
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x5d)
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x5e)
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Mouse Left Button") PORT_CODE( KEYCODE_LEFT )       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x5f)

	PORT_START( "IN6" )
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Help")              PORT_CODE( KEYCODE_F1 )         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x60)
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x61)
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_END )       PORT_CHAR(UCHAR_MAMEKEY(END))   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x62)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_W )         PORT_CHAR('w')  PORT_CHAR('W')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x63)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_Q )         PORT_CHAR('q')  PORT_CHAR('Q')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x64)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_A )         PORT_CHAR('a')  PORT_CHAR('A')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x65)
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_1 )         PORT_CHAR('&')  PORT_CHAR('1')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x66)
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Mouse Right Button") PORT_CODE( KEYCODE_RIGHT )     PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x67)
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x68)
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_F4 )        PORT_CHAR(0x00a4)               PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x69)
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_DEL_PAD )   PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x6a)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_SLASH_PAD ) PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x6b)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_ASTERISK )  PORT_CHAR('*')                      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x6c)
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_MINUS_PAD ) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x6d)
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_PLUS_PAD )  PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x6e)
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_ENTER_PAD ) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x6f)

	PORT_START( "IN7" )
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_0_PAD )     PORT_CHAR(UCHAR_MAMEKEY(0_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x70)
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_1_PAD )     PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x71)
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_2_PAD )     PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x72)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_3_PAD )     PORT_CHAR(UCHAR_MAMEKEY(3_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x73)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_4_PAD )     PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x74)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_5_PAD )     PORT_CHAR(UCHAR_MAMEKEY(5_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x75)
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_6_PAD )     PORT_CHAR(UCHAR_MAMEKEY(6_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x76)
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_7_PAD )     PORT_CHAR(UCHAR_MAMEKEY(7_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x77)
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_8_PAD )     PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x78)
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_9_PAD )     PORT_CHAR(UCHAR_MAMEKEY(9_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x79)
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x7a)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x7b)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x7c)
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x7d)
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x7e)
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_UNUSED )      //  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x7f)

	PORT_START("MOUSEX")
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_X ) PORT_SENSITIVITY(50) PORT_KEYDELTA(0)       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_mouse_input), 0)

	PORT_START("MOUSEY")
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y ) PORT_SENSITIVITY(50) PORT_KEYDELTA(0)       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_mouse_input), 0)

	PORT_START("MOUSE")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Mouse Button 2")     PORT_CODE(MOUSECODE_BUTTON2)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_mouse_input), 0)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Mouse Button 1")     PORT_CODE(MOUSECODE_BUTTON1)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_mouse_input), 0)
INPUT_PORTS_END


static INPUT_PORTS_START( geniusiq_de )
	PORT_INCLUDE(geniusiq)

	PORT_MODIFY( "IN1" )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_QUOTE )     PORT_CHAR('^')  PORT_CHAR(0x00b0)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x17)
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_BACKSLASH ) PORT_CHAR('+')  PORT_CHAR('*')      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x18)
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_EQUALS )    PORT_CHAR('}')  PORT_CHAR('{')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x19)
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_CLOSEBRACE ) PORT_CHAR(0x00b0)  PORT_CHAR('?')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x1a)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_OPENBRACE )  PORT_CHAR(0x00e4)  PORT_CHAR(0x00c4)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x1b)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_SLASH )     PORT_CHAR(0x00fc)   PORT_CHAR(0x00dc)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x1c)
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_COLON )     PORT_CHAR(0x00f6)   PORT_CHAR(0x00d6)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x1d)
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_TILDE )     PORT_CHAR('-')  PORT_CHAR('_')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x1f)

	PORT_MODIFY( "IN2" )
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_0 )         PORT_CHAR('0')  PORT_CHAR('=')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x20)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_9 )         PORT_CHAR('9')  PORT_CHAR(')')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x25)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_STOP )      PORT_CHAR('.')  PORT_CHAR(':')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x2b)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_COMMA )     PORT_CHAR(',')  PORT_CHAR(';')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x2c)

	PORT_MODIFY( "IN3" )
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_8 )         PORT_CHAR('8')  PORT_CHAR('(')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x30)
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_M )         PORT_CHAR('m')  PORT_CHAR('M')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x32)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_7 )         PORT_CHAR('7')  PORT_CHAR('/')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x35)
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_MINUS )     PORT_CHAR('#')  PORT_CHAR('\'') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x3a)
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_Z )         PORT_CHAR('z')  PORT_CHAR('Z')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x3e)

	PORT_MODIFY( "IN4" )
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_6 )         PORT_CHAR('6')  PORT_CHAR('&')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x40)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_5 )         PORT_CHAR('5')  PORT_CHAR('/')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x45)
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_BACKSLASH2 ) PORT_CHAR('<') PORT_CHAR('>')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x4a)

	PORT_MODIFY( "IN5" )
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_4 )         PORT_CHAR('4')  PORT_CHAR('$')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x50)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_3 )         PORT_CHAR('3')  PORT_CHAR(0x00a7)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x54)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_W )         PORT_CHAR('w')  PORT_CHAR('W')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x5b)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_2 )         PORT_CHAR('2')  PORT_CHAR('"')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x5c)

	PORT_MODIFY( "IN6" )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_Y )         PORT_CHAR('y')  PORT_CHAR('Y')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x63)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_A )         PORT_CHAR('a')  PORT_CHAR('A')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x64)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_Q )         PORT_CHAR('q')  PORT_CHAR('Q')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x65)
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_1 )         PORT_CHAR('1')  PORT_CHAR('!')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(geniusiq_state::send_input), 0x66)
INPUT_PORTS_END


void geniusiq_state::machine_start()
{
}

void geniusiq_state::machine_reset()
{
	m_keyboard.head = m_keyboard.tail = 0;

	m_gfx_y = 0;
	m_gfx_x = 0;
	m_gfx_base = 0;
	m_gfx_color[0] = m_gfx_color[1] = 0;
	m_mouse_posx = 0;
	m_mouse_posy = 0;
	m_mouse_gfx_posx = 0;
	m_mouse_gfx_posy = 0;
}

DEVICE_IMAGE_LOAD_MEMBER(geniusiq_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	// we always a 0x100000 region, for easier mapping in the memory map
	m_cart->rom_alloc(0x100000, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	m_cart_state = IQ128_ROM_CART;

	if (image.loaded_through_softlist())
	{
		const char *pcb_type = image.get_feature("pcb_type");
		if (pcb_type)
		{
			if (!strcmp(pcb_type, "romless1"))
				m_cart_state = IQ128_ROMLESS1_CART;
			if (!strcmp(pcb_type, "romless2"))
				m_cart_state = IQ128_ROMLESS2_CART;
		}
	}

	return std::make_pair(std::error_condition(), std::string());
}

DEVICE_IMAGE_UNLOAD_MEMBER(geniusiq_state::cart_unload)
{
	m_cart_state = IQ128_NO_CART;
}


void geniusiq_state::iq128(machine_config &config)
{
	// Basic machine hardware
	M68000(config, m_maincpu, XTAL(32'000'000)/2); // The main crystal is at 32MHz, not sure whats the CPU freq
	m_maincpu->set_addrmap(AS_PROGRAM, &geniusiq_state::geniusiq_mem);
	m_maincpu->set_periodic_int(FUNC(geniusiq_state::irq6_line_hold), attotime::from_hz(125));  // the internal clock is increased by 1 sec every 125 interrupts

	// Video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // not accurate
	screen.set_size(512, 256);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(geniusiq_state::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(geniusiq_state::geniusiq_palette), 16);

	// Internal flash
	AMD_29F010(config, "flash");

	// Cartridge
	generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "iq128_cart"));
	cartslot.set_device_load(FUNC(geniusiq_state::cart_load));
	cartslot.set_device_unload(FUNC(geniusiq_state::cart_unload));

	// Software lists
	SOFTWARE_LIST(config, "cart_list").set_original("iq128");
}

void geniusiq_state::iqtv512(machine_config &config)
{
	iq128(config);
	// Internal flash
	AMD_29F040(config.replace(), "flash");
}

// ROM definitions

ROM_START( pcunlim )
	ROM_REGION(0x200000, "maincpu", 0)
	ROM_LOAD( "27-5792-03.u3", 0x0000, 0x200000, CRC(944aa3be) SHA1(6005627035d99cfb6c479064808424adf0430df3) )
ROM_END

ROM_START( iq128 )
	ROM_REGION(0x200000, "maincpu", 0)
	ROM_LOAD( "27-5947-00.bin", 0x0000, 0x200000, CRC(a98fc3ff) SHA1(de76a5898182bd0180bd2b3e34c4502f0918a3fa) )
ROM_END

ROM_START( iq128_fr )
	ROM_REGION(0x200000, "maincpu", 0)
	ROM_LOAD( "geniusiq.bin", 0x0000, 0x200000, CRC(9b06cbf1) SHA1(b9438494a9575f78117c0033761f899e3c14e292) )
ROM_END

ROM_START( iqtv512 )
	ROM_REGION(0x200000, "maincpu", 0)
	ROM_LOAD( "27-06171-000.bin", 0x0000, 0x200000, CRC(2597af70) SHA1(9db8151a84517407d380424410b6fa0003ceb1eb) )
ROM_END

ROM_START( itunlim )
	ROM_REGION(0x200000, "maincpu", 0)
	ROM_LOAD( "27-06124-002.u3", 0x000000, 0x200000, CRC(0c0753ce) SHA1(d22504d583ca8d6a9d2f56fbaa3e1d52c442a1e9) )
ROM_END

} // anonymous namespace


// Drivers

//    YEAR  NAME      PARENT   COMPAT  MACHINE  INPUT        CLASS           INIT        COMPANY             FULLNAME                              FLAGS
COMP( 1997, pcunlim,  0,       0,      iq128,   geniusiq_de, geniusiq_state, empty_init, "Video Technology", "PreComputer Unlimited (USA/Canada)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
COMP( 1997, iq128,    pcunlim, 0,      iq128,   geniusiq_de, geniusiq_state, empty_init, "Video Technology", "Genius IQ 128 (Germany)",            MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
COMP( 1997, iq128_fr, pcunlim, 0,      iq128,   geniusiq,    geniusiq_state, empty_init, "Video Technology", "Genius IQ 128 (France)",             MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
COMP( 1998, iqtv512,  pcunlim, 0,      iqtv512, geniusiq_de, geniusiq_state, empty_init, "Video Technology", "Genius IQ TV 512 (Germany)",         MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
COMP( 1998, itunlim,  pcunlim, 0,      iq128,   geniusiq_de, geniusiq_state, empty_init, "Video Technology", "VTech IT Unlimited (UK)",            MACHINE_NO_SOUND)



geniusjr.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR, Roberto Fresca
/*

VTech Genius Junior series

CPU is 68HC05 derived?

Other known undumped international versions:
- Genius PRO (French version of Genius Leader Select)
- Pitagorín Plus (Spanish version of Genius Junior Redstar 3)
- PreComputer Notebook (alternate English version of Genius Leader Notebook)
- Smart Start Animated (alternate English version of Genius Junior Redstar)
- Talande Smart Start Laptop (Swedish version of Genius Junior Movie)
- Talking Whiz-Kid Animated (English version of Genius Junior Redstar)
- Talking Whiz-Kid Genius (English version of Genius Junior Movie)
- Talking Whiz-Kid Notebook (English version of Genius Leader Notebook)
- Talking Whiz-Kid Power Mouse (English version of Genius Leader Select)
- Talking Whiz-Kid Super Animated (English version of Genius Junior Redstar 3)

Undumped VTech laptops possibly on similar hardware:
- Genius Einstein (French version of Genius Leader Action)
- Genius Explorations (French version; German version unknown)
- Genius Junior Profi 2
- Genius Junior Profi 3
- Genius Leader Action
- Genius Leader Notebook Plus
- Genius Master Notebook MM
- Genius Master Power Maus
- Genius Prodige (French version of Genius Master Power Maus)
- Genius Progress (French version of Genius Junior Profi 2)
- Mega Ratón Parlanchín (Spanish version of Genius Explorations)
- PreComputer Notebook II (English version of Genius Leader Notebook Plus)
- Smart Start Future (alternate English version of Genius Junior Profi 2)
- Talking Einstein (English version of Genius Leader Action)
- Talking Whiz-Kid Explorer (alternate English version of Genius Leader Action)
- Talking Whiz-Kid Honors (alternate English version of Genius Junior Notebook Plus)
- Talking Whiz-Kid Laptop (English version of Genius Junior Profi 2)
- Talking Whiz-Kid Lessons (English version of Genius Master Power Maus)
- Talking Whiz-Kid Major Mouse (alternate English version of Genius Explorations)
- Talking Whiz-Kid Notebook 2000 (English version of Genius Master Notebook MM)
- Talking Whiz-Kid Notebook 3000 (alternate English version of Genius Master Notebook MM)
- Talking Whiz-Kid Power Mouse Deluxe (English version of Genius Explorations)

*/

/***************************************************************************************

    Product name:    Pitagorín Junior.
    Brand:           VTech.
    Type:            First steps (4-6 years old) laptop.
    Language:        Spanish.
    Description:     23 didactic games with voice and sounds for 1 or 2 players.
                     (simple maths operations, spell, hangman, letters, numbers, etc)

    Docs by Roberto Fresca.

  ***************************************************************************************

  Games / Activities ...

  ORDER  TITLE                       TRANSLATION
  -----+---------------------------+----------------------
   01  - La letra perdida.           The missing letter.
   02  - Deletrear.                  Spell.
   03  - Plurales.                   Plurals.
   04  - Verbos.                     Verbs.
   05  - El Ahorcado.                Hangman.
   06  - Revoltijo de letras.        Messed letters.
   07  - La palabra escondida.       The hidden word.
   08  - Trueque de letras.          Swapped letters.
   09  - La letra intrusa.           The intruder letter.
   10  - Matemáticas (+,-,x,÷).      Mathematics (+,-,x,÷).
   11  - Aprendiendo los números.    Learning the numbers.
   12  - Redondeando cifras.         Rounding numbers.
   13  - Encuentra el signo.         Find the sign.
   14  - Calculadora.                Calculator.
   15  - Tres en raya.               Three in a row.
   16  - El juego de los puntos.     The dot's game.
   17  - El juego del squash.        The squash game.
   18  - El juego del arquero.       The archer game.
   19  - Dibujos animados.           Animated cartoons.
   20  - El compositor.              The composer.

  ***************************************************************************************

  What's inside....

  PCB silkscreened '9817'
      etched on copper '35-19122-2' & '703013-C'

  1x Unknown CPU inside an epoxy blob (more than 100 connections) @ U? (covered with the blob).
  1x VTech LH532HJT mask ROM (originary from Sharp) also silkscreened '9811D' @ U3.
  1x Texas Instruments TSP50C10 (CSM10150AN) speech synth with 8-bit microprocessor @ U2.
  1x SN74HC00N @ U5.
  1x SN74HC244N @ U4.

  1x Unknown oscillator (XTAL1).
  1x Unknown trimpot on an r/c oscillator (XTAL2).

  1x 32 contacts (single side) expansion port.
  1x 3 contacts (unknown) connector.
  1x 17 contacts Keyboard (KEY1) connector.
  1x 3 contacts (CONT) connector.


  PCB layout:
                         .......CONNECTORS........
  .------------------------------------------------------------------------------------.
  | .-----------------.  ooo ooooooooooooooooo ooo                         9817        |.---.
  | |   THIS SECTOR   |  unk        KEY1       CONT                      .-------.     /   =|
  | |                 |                                                  |       |    /    =|
  | |  IS POPULATED   |                                                  | VTECH |   | E   =|
  | |                 |            .-----------.                         |       |   | X   =|
  | |   WITH A LOT    |            | SN74HC00N |      (84C91)            | LH532 |   | P P =|
  | |                 |            '-----------'    .----------.         |  HJT  |   | A O =|
  | |      OF...      |                 U5          |CSM10150AN|         |       |   | N R =|
  | |                 |                             '----------'.---.    | 9811D |   | S T =|
  | |   RESISTORS,    |                                  U2     | / |    |       |   | I   =|
  | |                 |   U4                                    '---'    |       |   | O   =|
  | |   CAPACITORS,   |  .--.                                   XTAL 2   '-------'   | N   =|
  | |                 |  |SN|                          ____                 U3        \    =|
  | |      AND        |  |74|                         /    \                           \   =|
  | |                 |  |HC|         35-19122-2     | BLOB |                          |'---'
  | |  TRANSISTORS    |  |24|          703013-C       \____/              .----.       |
  | |                 |  |4N|                           U?                '----'       |
  | '-----------------'  '--'                                             XTAL 1       |
  '------------------------------------------------------------------------------------'


  Expansion Port:

  CONNECTOR                     CONNECTOR
  ---------                     ---------
  01 ----> Vcc                  17 ----> LH532HJT (pin 09)
  02 ----> Vcc                  18 ----> LH532HJT (pin 25)
  03 ----> GND                  19 ----> LH532HJT (pin 10)
  04 ----> ???                  20 ----> LH532HJT (pin 23)
  05 ----> LH532HJT (pin 03)    21 ----> LH532HJT (pin 11)
  06 ----> LH532HJT (pin 02)    22 ----> LH532HJT (pin 21)
  07 ----> LH532HJT (pin 04)    23 ----> LH532HJT (pin 12)
  08 ----> LH532HJT (pin 30)    24 ----> LH532HJT (pin 20)
  09 ----> LH532HJT (pin 05)    25 ----> LH532HJT (pin 13)
  10 ----> LH532HJT (pin 29)    26 ----> LH532HJT (pin 19)
  11 ----> LH532HJT (pin 06)    27 ----> LH532HJT (pin 14)
  12 ----> LH532HJT (pin 28)    28 ----> LH532HJT (pin 18)
  13 ----> LH532HJT (pin 07)    29 ----> LH532HJT (pin 15)
  14 ----> LH532HJT (pin 27)    30 ----> LH532HJT (pin 17)
  15 ----> LH532HJT (pin 08)    31 ----> GND
  16 ----> LH532HJT (pin 26)    32 ----> GND


  U3 - VTech LH532HJT (9811D) 2Mb mask ROM.
       Seems to be 27C020 pin compatible.

                .----v----.
          VCC --|01     32|-- VCC
              --|02     31|--
              --|03     30|--
              --|04     29|--
              --|05     28|--
              --|06     27|--
              --|07     26|--
              --|08     25|--
              --|09     24|--
              --|10     23|--
              --|11     22|--
              --|12     21|--
              --|13     20|--
              --|14     19|--
              --|15     18|--
          GND --|16     17|--
                '---------'


  U2 - Texas Instruments TSP50C10 (CSM10150AN).

       Speech Generator with 8-bit microprocessor, 8K ROM, 112 bytes RAM.
       Maximum Clock Frequency = 9.6 MHz.
       Package = DIP16
       Technology = CMOS

                 .---v---.
               --|01   16|--
               --|02   15|--
               --|03   14|--
               --|04   13|--
           GND --|05   12|-- VCC
               --|06   11|--
               --|07   10|-- GND
               --|08   09|--
                 '-------'

  ***************************************************************************************/

#include "emu.h"
#include "cpu/m6805/m68hc05.h"
#include "softlist_dev.h"


namespace {

class geniusjr_state : public driver_device
{
public:
	geniusjr_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rombank(*this, "rombank")
	{
	}

	void gj4000(machine_config &config);
	void gln(machine_config &config);
	void gls(machine_config &config);
	void gj5000(machine_config &config);
	void gjrstar(machine_config &config);
	void gjmovie(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void gj4000_map(address_map &map) ATTR_COLD;
	void gj5000_map(address_map &map) ATTR_COLD;
	void gjrstar_map(address_map &map) ATTR_COLD;

	required_device<m68hc05_device> m_maincpu;
	required_memory_bank m_rombank;

	u16 m_bank_size;
};

void geniusjr_state::gj4000_map(address_map &map)
{
	map(0x8000, 0xffff).bankr("rombank");
}

void geniusjr_state::gj5000_map(address_map &map)
{
	map(0x4000, 0x7fff).bankr("rombank");
}

void geniusjr_state::gjrstar_map(address_map &map)
{
	map(0x2000, 0x3fff).bankr("rombank");
}


INPUT_PORTS_START( geniusjr )
INPUT_PORTS_END


void geniusjr_state::machine_start()
{
	memory_region *extrom = memregion("extrom");

	m_rombank->configure_entries(0, extrom->bytes() / m_bank_size, extrom->base(), m_bank_size);
	m_rombank->set_entry(0);
}

void geniusjr_state::gj4000(machine_config &config)
{
	M68HC05L9(config, m_maincpu, 8'000'000); // unknown clock
	m_maincpu->set_addrmap(AS_PROGRAM, &geniusjr_state::gj4000_map);

	m_bank_size = 0x8000;

	SOFTWARE_LIST(config, "cart_list").set_original("gj4000");
}

void geniusjr_state::gln(machine_config &config)
{
	gj4000(config);

	subdevice<software_list_device>("cart_list")->set_original("gln");
}

void geniusjr_state::gj5000(machine_config &config)
{
	M68HC05L9(config, m_maincpu, 8'000'000); // unknown clock (type also uncertain)
	m_maincpu->set_addrmap(AS_PROGRAM, &geniusjr_state::gj5000_map);

	m_bank_size = 0x4000;

	SOFTWARE_LIST(config, "cart_list").set_original("gj4000");
}

void geniusjr_state::gjrstar(machine_config &config)
{
	M68HC05L9(config, m_maincpu, 8'000'000); // unknown clock (type also uncertain, could be L7 instead of L9)
	m_maincpu->set_addrmap(AS_PROGRAM, &geniusjr_state::gjrstar_map);

	m_bank_size = 0x2000;

	SOFTWARE_LIST(config, "cart_list").set_original("gjrstar");
}

void geniusjr_state::gjmovie(machine_config &config)
{
	gjrstar(config);

	subdevice<software_list_device>("cart_list")->set_original("gjmovie");
}

void geniusjr_state::gls(machine_config &config)
{
	gjrstar(config);

	subdevice<software_list_device>("cart_list")->set_original("gls");
}


ROM_START( gj4000 )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "hc05_internal.bin", 0x0000, 0x2000, NO_DUMP )

	ROM_REGION( 0x40000, "extrom", 0 )
	ROM_LOAD( "27-05886-000-000.u4", 0x000000, 0x40000, CRC(5f6db95b) SHA1(fe683154e33a82ea38696096616d11e850e0c7a3))
ROM_END

// VTech PCB 35-21205. "C.Q.F.D" is a VTech brand, and the Scientus is a straight clone of the "Genius Junior 4000".
ROM_START( scientus )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "hc05_internal.bin", 0x0000, 0x2000, NO_DUMP )

	ROM_REGION( 0x80000, "extrom", 0 )
	ROM_LOAD( "54-6050-00-0.u2",  0x000000, 0x80000, CRC(dbcfebaa) SHA1(863697d144857fab45aad493b812ed607ad7e1d0)) // AMD AM27C010

	ROM_REGION( 0x2000, "speech", 0 )
	ROM_LOAD( "27-05992-0-0.u3", 0x0000, 0x2000, NO_DUMP ) // TI speech chip
ROM_END

ROM_START( gj5000 )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "hc05_internal.bin", 0x0000, 0x2000, NO_DUMP )

	ROM_REGION( 0x80000, "extrom", 0 )
	ROM_LOAD( "27-6019-01.u2", 0x000000, 0x80000, CRC(946e5b7d) SHA1(80963d6ad80d49e54c8996bfc77ac135c4935be5))
ROM_END

ROM_START( gjmovie )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "hc05_internal.bin", 0x0000, 0x2000, NO_DUMP )

	ROM_REGION( 0x40000, "extrom", 0 )
	ROM_LOAD( "lh532hlk.bin", 0x000000, 0x40000, CRC(2e64c296) SHA1(604034f902e20851cb9af60964031a508ceef83e))
ROM_END

// Same main ROM as VTech "El Super-Ordenador Parlanchín", and also same label on the TSP50C10 (probably same ROM too)
ROM_START( pitagjr )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "hc05_internal_sp.bin", 0x0000, 0x2000, NO_DUMP )

	ROM_REGION( 0x40000, "extrom", 0 )
	ROM_LOAD( "lh532hjt_9811d.u3", 0x00000, 0x40000, CRC(23878b45) SHA1(8f3c41c10cfde9d76763c3a8701ec6616db4ab40) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROM_LOAD( "csm10150an.u2", 0x0000, 0x2000, NO_DUMP ) // TSP50C10 (8K bytes of ROM) labeled "CSM10150AN"
ROM_END

ROM_START( gjrstar )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "hc05_internal.bin", 0x0000, 0x2000, NO_DUMP )

	ROM_REGION( 0x40000, "extrom", 0 )
	ROM_LOAD( "27-5740-00.u1", 0x000000, 0x40000, CRC(ff3dc3bb) SHA1(bc16dfc1e12b0008456c700c431c8df6263b671f))
ROM_END

ROM_START( gjrstar2 )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "hc05_internal.bin", 0x0000, 0x2000, NO_DUMP )

	ROM_REGION( 0x40000, "extrom", 0 )
	ROM_LOAD( "27-5740-00.u1", 0x000000, 0x40000, CRC(ff3dc3bb) SHA1(bc16dfc1e12b0008456c700c431c8df6263b671f)) // Identical to 'Genius Junior Redstar'
ROM_END

// VTech PCB 35-10100-01
ROM_START( pcompelr )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "hc05_internal.u8", 0x0000, 0x2000, NO_DUMP )

	ROM_REGION( 0x2000, "speech", 0 )
	ROM_LOAD( "speech.u7", 0x0000, 0x2000, NO_DUMP ) // Labeled "930 0AF21FK / VTECH / ©YY 04044"

	ROM_REGION( 0x40000, "extrom", 0 )
	ROM_LOAD( "27-05944-000-001.u1", 0x000000, 0x40000, CRC(5018763d) SHA1(70e1d8b8e34e0b2ab10d7ac06c2f454d1f377e77)) // Dumped as AM27C020, pin 1 connected to 32 (Vcc), as per 27c020 specs)
ROM_END

ROM_START( gjrstar3 )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "hc05_internal.bin", 0x0000, 0x2000, NO_DUMP )

	ROM_REGION( 0x40000, "extrom", 0 )
	ROM_LOAD( "54-06056-000-000.u3", 0x000000, 0x040000, CRC(72522179) SHA1(ede9491713ad018012cf925a519bcafe126f1ad3))
ROM_END

ROM_START( gln )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "hc05_internal.bin", 0x0000, 0x2000, NO_DUMP )

	ROM_REGION( 0x80000, "extrom", 0 )
	ROM_LOAD( "27-5308-00_9524_d.bin", 0x000000, 0x080000, CRC(d1b994ee) SHA1(b5cf0810df0676712e4f30e279cc46c19b4277dd))
ROM_END

ROM_START( pitagor )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "hc05_internal.bin", 0x0000, 0x2000, NO_DUMP )

	ROM_REGION( 0x80000, "extrom", 0 )
	ROM_LOAD( "27-5374-00.u2", 0x000000, 0x80000, CRC(89a8fe7d) SHA1(dff06f7313af22c6c19b1f00c0651a64cc505fe2))

	ROM_REGION( 0x2000, "speech", 0 )
	ROM_LOAD( "csm10150an.u1", 0x0000, 0x2000, NO_DUMP ) // TSP50C10 (8K bytes of ROM) labeled "64C_4TT VIDEO TECH CSM10150AN"
ROM_END

ROM_START( gls )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "hc05_internal.bin", 0x0000, 0x2000, NO_DUMP ) // As per decap, confirmed to be a Motorola 68HC05 CSIC (Customer Specification Integrated Circuit)

	ROM_REGION( 0x40000, "extrom", 0 )
	ROM_LOAD( "27-5635-00.u2", 0x000000, 0x40000, CRC(bc3c0587) SHA1(fe98f162bd80d96ce3264087b5869f4505955464))
ROM_END

} // anonymous namespace


//    YEAR   NAME      PARENT   COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY    FULLNAME                             FLAGS
COMP( 1996,  gj4000,   0,       0,      gj4000,   geniusjr, geniusjr_state, empty_init, "VTech",   "Genius Junior 4000 (Germany)",      MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1999?, scientus, gj4000,  0,      gj4000,   geniusjr, geniusjr_state, empty_init, "C.Q.F.D", "Scientus (France)",                 MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1993,  gjmovie,  0,       0,      gjmovie,  geniusjr, geniusjr_state, empty_init, "VTech",   "Genius Junior Movie (Germany)",     MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 199?,  pitagjr,  gjmovie, 0,      gjmovie,  geniusjr, geniusjr_state, empty_init, "VTech",   u8"Pitagorín Junior",                MACHINE_NO_SOUND | MACHINE_NOT_WORKING ) // Also sold as "El Super-Ordenador Parlanchín"
COMP( 1996,  gjrstar,  0,       0,      gjrstar,  geniusjr, geniusjr_state, empty_init, "VTech",   "Genius Junior Redstar (Germany)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1996,  gjrstar2, gjrstar, 0,      gjrstar,  geniusjr, geniusjr_state, empty_init, "VTech",   "Genius Junior Redstar 2 (Germany)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1995,  pcompelr, gjrstar, 0,      gjrstar,  geniusjr, geniusjr_state, empty_init, "VTech",   "Precomputer Elektronik (Russia)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING ) // Прекомпьютер Электроник
COMP( 1998,  gjrstar3, 0,       0,      gjrstar,  geniusjr, geniusjr_state, empty_init, "VTech",   "Genius Junior Redstar 3 (Germany)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1998,  gj5000,   0,       0,      gj5000,   geniusjr, geniusjr_state, empty_init, "VTech",   "Genius Junior 5000 (Germany)",      MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1993,  gln,      0,       0,      gln,      geniusjr, geniusjr_state, empty_init, "VTech",   "Genius Leader Notebook",            MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1993,  pitagor,  gln,     0,      gln,      geniusjr, geniusjr_state, empty_init, "VTech",   u8"Pitagorín",                       MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1995,  gls,      0,       0,      gls,      geniusjr, geniusjr_state, empty_init, "VTech",   "Genius Leader Select",              MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



genpc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol, Miodrag Milanovic
/***************************************************************************

    drivers/genpc.c

    Driver file for generic PC machines

***************************************************************************/

#include "emu.h"
#include "machine/genpc.h"
#include "cpu/nec/nec.h"
#include "cpu/i86/i86.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "softlist_dev.h"


namespace {

class genpc_state : public driver_device
{
public:
	genpc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag) ,
		m_maincpu(*this, "maincpu") { }

	void pcega(machine_config &config);
	void pcvga(machine_config &config);
	void pccga(machine_config &config);
	void pcherc(machine_config &config);
	void pcmda(machine_config &config);
	void pcv20(machine_config &config);
	void pc8_io(address_map &map) ATTR_COLD;
	void pc8_map(address_map &map) ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
};

void genpc_state::pc8_map(address_map &map)
{
	map.unmap_value_high();
	map(0xf0000, 0xfffff).rom().region("bios", 0);
}

void genpc_state::pc8_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m("mb", FUNC(ibm5160_mb_device::map));
}

static DEVICE_INPUT_DEFAULTS_START(cga)
	DEVICE_INPUT_DEFAULTS("DSW0",0x30, 0x20)
DEVICE_INPUT_DEFAULTS_END

static DEVICE_INPUT_DEFAULTS_START(vga)
	DEVICE_INPUT_DEFAULTS("DSW0",0x30, 0x00)
DEVICE_INPUT_DEFAULTS_END

void genpc_state::pcmda(machine_config &config)
{
	/* basic machine hardware */
	I8088(config, m_maincpu, 4772720);
	m_maincpu->set_addrmap(AS_PROGRAM, &genpc_state::pc8_map);
	m_maincpu->set_addrmap(AS_IO, &genpc_state::pc8_io);
	m_maincpu->set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	ibm5160_mb_device &mb(IBM5160_MOTHERBOARD(config, "mb"));
	mb.set_cputag(m_maincpu);
	mb.int_callback().set_inputline(m_maincpu, 0);
	mb.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	mb.kbdclk_callback().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	mb.kbddata_callback().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));

	ISA8_SLOT(config, "isa1", 0, "mb:isa", pc_isa8_cards, "mda", false); // FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa2", 0, "mb:isa", pc_isa8_cards, "com", false);
	ISA8_SLOT(config, "isa3", 0, "mb:isa", pc_isa8_cards, "fdc_xt", false);
	ISA8_SLOT(config, "isa4", 0, "mb:isa", pc_isa8_cards, "hdc", false);
	ISA8_SLOT(config, "isa5", 0, "mb:isa", pc_isa8_cards, "adlib", false);
	ISA8_SLOT(config, "isa6", 0, "mb:isa", pc_isa8_cards, nullptr, false);

	/* keyboard */
	pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_IBM_PC_XT_83));
	kbd.out_clock_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_clock_w));
	kbd.out_data_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_data_w));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("640K").set_extra_options("64K, 128K, 256K, 512K");

	/* software lists */
	SOFTWARE_LIST(config, "disk_list").set_original("ibm5150");
	SOFTWARE_LIST(config, "hdd_list").set_original("ibm5150_hdd");
}

void genpc_state::pcv20(machine_config &config)
{
	pccga(config);

	v20_device &maincpu(V20(config.replace(), "maincpu", XTAL(14'318'181)/3)); /* 4.77 MHz */
	maincpu.set_addrmap(AS_PROGRAM, &genpc_state::pc8_map);
	maincpu.set_addrmap(AS_IO, &genpc_state::pc8_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));
}

void genpc_state::pcherc(machine_config &config)
{
	pcmda(config);
	subdevice<isa8_slot_device>("isa1")->set_default_option("hercules");
}


void genpc_state::pccga(machine_config &config)
{
	pcmda(config);
	subdevice<ibm5160_mb_device>("mb")->set_input_default(DEVICE_INPUT_DEFAULTS_NAME(cga));
	subdevice<isa8_slot_device>("isa1")->set_default_option("cga");
}


void genpc_state::pcega(machine_config &config)
{
	pccga(config);
	subdevice<isa8_slot_device>("isa1")->set_default_option("ega");
	subdevice<ibm5160_mb_device>("mb")->set_input_default(DEVICE_INPUT_DEFAULTS_NAME(vga));
}


void genpc_state::pcvga(machine_config &config)
{
	pcega(config);
	subdevice<isa8_slot_device>("isa1")->set_default_option("vga");
}

ROM_START(pc)
	ROM_REGION(0x10000, "bios", 0)
	// 0: Turbo XT BIOS v3.1 - 10/28/2017
	ROM_SYSTEM_BIOS(0, "v31", "Turbo XT BIOS 3.1")
	ROMX_LOAD("pcxtbios31.rom", 0xe000, 0x02000, CRC(8ede74c6) SHA1(5848065f9b40e504c02e6930d3602965b2b1bfad),ROM_BIOS(0))
	// 1: Turbo XT BIOS v3.0 - 11/09/2016
	ROM_SYSTEM_BIOS(1, "v30", "Turbo XT BIOS 3.0")
	ROMX_LOAD("pcxtbios30.rom", 0xe000, 0x02000, CRC(4e1fd77a) SHA1(36873971c47b242db7edad7a0c1ea2f7f8d43b87),ROM_BIOS(1))
	// 2: Turbo XT BIOS v2.6 for 8088/V20
	ROM_SYSTEM_BIOS(2, "v26", "Turbo XT BIOS 2.6")
	ROMX_LOAD("pcxtbios26.rom", 0xe000, 0x02000, CRC(a7505acd) SHA1(1fdd80b09feed0ac59401fd6d8dae6250cb56054),ROM_BIOS(2))
	// 3: Turbo XT BIOS v2.5 for 8088/V20
	ROM_SYSTEM_BIOS(3, "v25", "Turbo XT BIOS 2.5")
	ROMX_LOAD("pcxtbios25.rom", 0xe000, 0x02000, CRC(1ab22db6) SHA1(e681acec93c79b08ec06fd26d3be4cccd28f7a45),ROM_BIOS(3))
	// 4: Turbo XT BIOS v2.4 for 8088/V20
	ROM_SYSTEM_BIOS(4, "v24", "Turbo XT BIOS 2.4")
	ROMX_LOAD("pcxtbios24.rom", 0xe000, 0x02000, CRC(80e3c43f) SHA1(3f623cf12f3375aa0fa59da84b5137b9fc86c0ce),ROM_BIOS(4))
	// 5: Turbo XT BIOS for 8088/V20
	ROM_SYSTEM_BIOS(5, "v23", "Turbo XT BIOS 2.3")
	ROMX_LOAD("pcxtbios23.rom", 0xe000, 0x02000, CRC(f397485a) SHA1(777826be2feadb3a8cf7a28ed2245dddef8e1d23),ROM_BIOS(5))
	// 6: Turbo XT BIOS 2.2 for 8088/V20
	ROM_SYSTEM_BIOS(6, "v22", "Turbo XT BIOS 2.2")
	ROMX_LOAD("pcxtbios22.rom", 0xe000, 0x02000, CRC(00967678) SHA1(2dd7f6c8236673e471dd456be009dcc43e28a09f),ROM_BIOS(6))
	// 7: Turbo XT BIOS 2.1 for 8088/V20
	ROM_SYSTEM_BIOS(7, "v21", "Turbo XT BIOS 2.1")
	ROMX_LOAD("pcxtbios21.rom", 0xe000, 0x02000, CRC(017f8f61) SHA1(d9696ba16b56685eb51612eddf1a75364acae7af),ROM_BIOS(7))
	// 8: Turbo XT BIOS for 8088/V20
	ROM_SYSTEM_BIOS(8, "v20", "Turbo XT BIOS 2.0")
	ROMX_LOAD("xtbios2.rom",    0xe000, 0x02000, CRC(1d7bd86c) SHA1(33a500f599b4dad2fe6d7a5c3e89b13bd5dd2987),ROM_BIOS(8))
	// 9: Generic Turbo XT BIOS 1987 for 8088 or V20 cpu (c)Anonymous
	ROM_SYSTEM_BIOS(9, "v10", "XT Anonymous Generic Turbo BIOS")
	ROMX_LOAD("pcxt.rom",       0xe000, 0x02000, CRC(031aafad) SHA1(a641b505bbac97b8775f91fe9b83d9afdf4d038f),ROM_BIOS(9))

	// List of bioses to go to separate drivers
	// 10: 8088-BIOS (C)AMI, 1985, 1986 / (C)AMI, 3000-100386
	ROM_SYSTEM_BIOS(10, "ami", "XT AMI")
	ROMX_LOAD( "ami.bin", 0xe000, 0x2000, CRC(b381eb22) SHA1(9735193de119270c946a17ed58c3ab9554e0852e),ROM_BIOS(10))
	// 11: XT BIOS V2.05 COPYRIGHT Award Software Inc. 1986 / SUPERWAVE ELECTRONIC CO., LTD.
	ROM_SYSTEM_BIOS(11, "award", "XT Award 2.05 #1")
	ROMX_LOAD( "award2.05.bin", 0xe000, 0x2000, CRC(5b3953e5) SHA1(4a36171aa8d993008187f39f732b9296401b7b6c),ROM_BIOS(11))
	// 12: Phoenix ROM BIOS Ver 2.27
	ROM_SYSTEM_BIOS(12, "pho2271", "XT Phoenix BIOS 2.27 #1")
	ROMX_LOAD( "phoenix2.27.bin", 0xe000, 0x2000, CRC(168ffef0) SHA1(69465db2f9246a614044d1f433d374506a13a07f),ROM_BIOS(12))
	// 13: Phoenix ROM BIOS Ver 2.27
	ROM_SYSTEM_BIOS(13, "pho2272", "XT Phoenix BIOS 2.27 #2") // V20 installed on board, 8 ISA8 slots
	ROMX_LOAD( "compatibility_software_phoenix_technologies_1985_1986_1121277.bin", 0xe000, 0x2000, CRC(33ceb81a) SHA1(7c7db75e61e19025938f30798d9d0f8b4f6ab0ee),ROM_BIOS(13))
	// 14: Phoenix ROM BIOS Ver 2.51 / Micro-Universe ver 1.0B
	ROM_SYSTEM_BIOS(14, "pho251", "XT Phoenix BIOS 2.51")
	ROMX_LOAD( "phoenix2.51.bin", 0xe000, 0x2000, CRC(9b7e9c40) SHA1(c948a8d3d715e469105c6e2acd8b46ec274b25a8),ROM_BIOS(14))
	// 15: T U R B O - XT 1986 / Version 3.10
	ROM_SYSTEM_BIOS(15, "turbo", "XT Turbo BIOS 3.10")
	ROMX_LOAD( "turbo3.10.bin", 0xe000, 0x2000, CRC(8aaca1e3) SHA1(9c03da16713e08c0112a04c8bdfa394e7341c1fc),ROM_BIOS(15))
	// 16: System 100 ! / S.pecial I.ntegrated D.esigns / BIOS For PC,XT-16 Version 4.1 / (C) 1986
	ROM_SYSTEM_BIOS(16, "sid41", "SID BIOS v4.1") // from X'GOLDEN mainboard
	ROMX_LOAD( "sid_bios_version_v4.1.bin", 0xe000, 0x2000, CRC(c58daf4d) SHA1(7066f8f993500383b99103a9fa1e6c125c89581b),ROM_BIOS(16))
	// 17: System Already !
	ROM_SYSTEM_BIOS(17, "scb12", "Super Computer BIOS 1.2" ) // from X'GOLDEN mainboard
	ROMX_LOAD( "super_computer_bios_1.2_1984.bin", 0xe000, 0x2000, CRC(0768a9ba) SHA1(d05c893e9dfc84a3c11c35f87859429f350571c3), ROM_BIOS(17))
	// 18: T U R B O - XT 1986 / Version 3.10
	ROM_SYSTEM_BIOS(18, "txt310", "T U R B O  XT Version 3.10") // from X'GOLDEN Turbo mainboard, computer can operate in 8MHz mode, source mentions possible corruption
	ROMX_LOAD( "turbo_xt_3.10_2764.bin", 0xe000, 0x2000,  BAD_DUMP CRC(8aaca1e3) SHA1(9c03da16713e08c0112a04c8bdfa394e7341c1fc),ROM_BIOS(18))
	// 19: Phoenix ROM BIOS Ver 2.27
	ROM_SYSTEM_BIOS(19, "alco", "ALCO 8MHz") // another Phoenix v2.27 variant, probably overdumped, therefore BAD_DUMP
	ROMX_LOAD( "alco8mhz.bin", 0xe000, 0x2000, BAD_DUMP CRC(96a56814) SHA1(7f752cbe1a25ed6ea5f77fed79cfbf608c667dc3),ROM_BIOS(19))
	// 20: System   Ready / American XT Computer / (C) 1986 For American XT BIOS V.1.32
	ROM_SYSTEM_BIOS(20, "american", "American XT 1.32")
	ROMX_LOAD( "americxt.rom", 0xe000, 0x2000, CRC(4c6e23f3) SHA1(6e16f42da9c3d7bd408cf885caf93de9aa02ebe4),ROM_BIOS(20))
	// 21: EXCEL-TURBO SPEEDY SYSTEM / EXCEL-TURBO Computer 9/20/1985 Version 2.14
	ROM_SYSTEM_BIOS(21, "excel214", "Excel-Turbo Computer Version 2.14")
	ROMX_LOAD( "excelturbobios.bin", 0xe000, 0x2000, CRC(8ef472a6) SHA1(8f3d512e23ecffb6d9a650d126b11270ff5cf175), ROM_BIOS(21))
	// 22: EXCEL-TURBO SPEEDY SYSTEM / Excel-Turbo Computer 9/20/1985 Version 3.1
	ROM_SYSTEM_BIOS(22, "excel31", "Excel-Turbo Computer Version 3.1")
	ROMX_LOAD( "excel-turbo_computer_3.1_2764.bin", 0xe000, 0x2000, CRC(d319fea7) SHA1(5b4b0eb35889602aa7f18de82800599528690e15),ROM_BIOS(22))
	// 23: Phoenix ROM BIOS Ver 2.51
	ROM_SYSTEM_BIOS(23, "s10b1", "Super 10-B1") // another Phoenix 2.51 variant
	ROMX_LOAD( "super_10-b1_27c64.bin", 0xe000, 0x2000, CRC(ba7797db) SHA1(2ee8863640b860a1807cc41e1ac9d94f73a087aa),ROM_BIOS(23))
	// 24: 86(C) TD3.86 ID: 75102637
	ROM_SYSTEM_BIOS(24, "td386", "TD 3.86")
	ROMX_LOAD( "td3.86_id_75102637.bin", 0xe000, 0x2000, CRC(aec96e13) SHA1(6e3143418f439a0373fba626cf69df34e41815e5),ROM_BIOS(24))
	// 25: 86(C) TD3.91 ID:
	ROM_SYSTEM_BIOS(25, "td391", "TD 3.91")
	ROMX_LOAD( "td391-td.rom", 0xe000, 0x2000, CRC(508b1bad) SHA1(ee9f51423f4cccfdc160c565ecd95fabbcb8a4d4),ROM_BIOS(25))
	// 26: 86(C) TD3.93 ID:
	ROM_SYSTEM_BIOS(26, "td393", "TD 3.93")
	ROMX_LOAD( "td3.93.bin", 0xe000, 0x2000, CRC(807620d9) SHA1(3f0ca24e33feb32051de9e819b962df1528a0403),ROM_BIOS(26))
	// 27: Phoenix ROM BIOS Ver 2.27 / YANGTECH.INC
	ROM_SYSTEM_BIOS(27, "yangp227", "YANGTECH.INC Phoenix 2.27")
	ROMX_LOAD( "000p001.bin", 0xe000, 0x2000, CRC(16f4fdc8) SHA1(8e73e9d1456aadd65bb89cc813d1aa1354c90d68),ROM_BIOS(27))
	// 28: ETHOM Associates Inc. Personal Computer Version 1.1F
	ROM_SYSTEM_BIOS(28, "ethom11f", "ETHOM Associates Version 1.1f") // 8 MHz
	ROMX_LOAD( "ethom_associates_version_1.1f.bin", 0xe000, 0x02000, CRC(bbe7dc12) SHA1(195989a43e6701ff247329524622f1d6f41db7b4),ROM_BIOS(28))
	// 29: ARC Turbo Board - X Turbo System
	ROM_SYSTEM_BIOS(29, "arc20", "ARC BIOS 2.0")
	ROMX_LOAD( "ibm-artb.rom", 0xe000, 0x2000, CRC(0ae5bf8e) SHA1(79b043070c92f9b2f6f9ca25fe61b4c1fcdf1bc8),ROM_BIOS(29))
	// 30:  Phoenix ROM BIOS Ver 2.52
	ROM_SYSTEM_BIOS(30, "pho252", "XT Phoenix BIOS 2.52")
	ROMX_LOAD( "ibm-phxt.rom", 0xe000, 0x2000, CRC(c0bc9482) SHA1(a527403c92b6bf4fd876f516c18ca499cb7d4b13),ROM_BIOS(30))
	// 31: System Already ! IBM COMPATIBLE BIOS v3.3  .......1985
	ROM_SYSTEM_BIOS(31, "com33", "IBM Compatible BIOS v3.3")
	ROMX_LOAD( "ibm3-3.rom", 0xe000, 0x02000, CRC(bf6dde1a) SHA1(e63456a888b887b8c0f77f35261ff067f0e2020d),ROM_BIOS(31))
	// 32: TURBO SYSTEM / Compatible Computer TURBO
	ROM_SYSTEM_BIOS(32, "xt16", "Turbo BIOS for PC XT-16")
	ROMX_LOAD( "ibmturb.rom", 0xe000, 0x2000, CRC(ba4a711e) SHA1(82fe2f76fd6668d2b38f8e6552a605d70c822792),ROM_BIOS(32))
	// 33: Z-NIX PC-1600
	ROM_SYSTEM_BIOS(33, "znix", "Z-NIX PC-1600")
	ROMX_LOAD( "ibmzen.rom", 0xe000, 0x2000, CRC(c5468172) SHA1(499a7813f870b04003e246cc90d4a591d043c6bb),ROM_BIOS(33))
	// 34: PC/88 BIOS Ver1.92
	ROM_SYSTEM_BIOS(34, "pcpi", "PC/88 BIOS Ver1.92") // use pcega
	ROMX_LOAD( "pcpi-192.rom", 0xe000, 0x2000, CRC(ef2da5ce) SHA1(95376440be1276e6f1c16fe49c847056bb1e4d5c),ROM_BIOS(34))
	// 35: no POST screen, takes a few seconds to beep, then boots
	ROM_SYSTEM_BIOS(35, "fday17", "Faraday 5 slot PC")
	ROMX_LOAD( "fdaypc17.rom", 0xe000, 0x2000, CRC(26bb29ac) SHA1(5a58680b9193f4323db3e7894f853dc82d17f4ee),ROM_BIOS(35))
	// 36: (c) E C D Computer GmbH 1985 - BIOS for ECD Professional Microcomputer - use pcherc
	ROM_SYSTEM_BIOS(36, "ecd", "ECD-Computer")
	ROMX_LOAD( "ecd_computer.bin", 0xe000, 0x2000, CRC(caab05f5) SHA1(060aa6c17ff9405c256684cec8a5165227c7c522), ROM_BIOS(36))
	// 37: Triple D International TD-20 - 8088/86 Modular BIOS Ver 3.1jk 06/19&/89 15:42 / Copyright Award Software  Inc.
	ROM_SYSTEM_BIOS(37, "td20", "TD-20")
	ROMX_LOAD( "td20bios.bin", 0xc000, 0x4000, CRC(dfce8cd5) SHA1(c4a9624f230ecdeeee606ee1d0bc685226938505), ROM_BIOS(37))
	// 38: B-190-B' P1.830 810.02, Chipset: Faraday FE2010A ICs: UM8272A, INS8250N-BT, MM58167AN-T
	// 86(C) CD3.98 ID:
	ROM_SYSTEM_BIOS(38, "b190b", "B-190-B")
	ROMX_LOAD( "b190bios.bin", 0xc000, 0x4000, CRC(4178d321) SHA1(a6b30c0805beabe3566b7d22984aa683fc62d7dc), ROM_BIOS(38))
	// 39: XT BIOS V2.05 COPYRIGHT Award Software Inc. 1986
	ROM_SYSTEM_BIOS(39, "kt10mb", "KT 10 M/B") // Award XT BIOS 2.05
	ROMX_LOAD( "kt10bios.bin", 0xe000, 0x2000, CRC(94e9836e) SHA1(793a9359ffd6f0964aa25edce31a3f37aa0dadc8), ROM_BIOS(39))
	// 40: // http://www.vcfed.org/forum/showthread.php?68214-Ruud-s-diagnostic-ROM-for-IBM-PC-XT-and-compatibles
	ROM_SYSTEM_BIOS(40, "diag", "Ruud Baltissen's Diagnostics")
	ROMX_LOAD( "diagrom.bin", 0xe000, 0x2000, CRC(747b1853) SHA1(204a484bc83b3607d5e1404a2dbe629f5f3044b1), ROM_BIOS(40))
	// 41:
	ROM_SYSTEM_BIOS(41, "081682", "08/16/1982")
	ROMX_LOAD( "xt_rom_1_081682_clone.bin", 0xe000, 0x2000, CRC(cfce9b2c) SHA1(14145acb0aca2baf8a6f3c7613f4521fdf0cbe92), ROM_BIOS(41))
	// 42: V20 NEC D70108C-8 - OSC: 14.31818, 24.000 MHz
	// XT BIOS V2.05 COPYRIGHT Award Software Inc. 1986
	ROM_SYSTEM_BIOS(42, "awxt205", "XT Award 2.05 #2")
	ROMX_LOAD( "rom7.u35", 0xe000, 0x2000, CRC(aa3def6b) SHA1(9fb88b6b522d939f7080a567f4a24279ca6c0928), ROM_BIOS(42))
	// 43: 8 MHz TURBO BOARD - ISA8: 8 -
	// American Research Corp., Copyright 1985, ARC TURBO BIOS VERSION 1.23 6/27/85
	ROM_SYSTEM_BIOS(43, "arc123", "ARC Turbo BIOS 1.23")
	ROMX_LOAD( "arcturbobios.bin", 0xe000, 0x2000, CRC(07692e7b) SHA1(27aa350dbc0d846cee8f9149bde0ef72d3862254), ROM_BIOS(43))
	// 44: XT-Faraday PAC - Chipset: Faraday FE2010A-ES, Faraday FE2100, MM58167AN, Z0765A08PSC, NS8250N - CPU: SONY CXQ70108P-8 (V20)
	// OSC: 28.6363, 18.4328.000 - ISA8: 6 - BIOS: PCBIOS 05017 / FARADAY'84'87 / 07017007 - on board: Floppy, ... (ser/par?)
	ROM_SYSTEM_BIOS(44, "pac", "XT-Faraday PAC")
	ROMX_LOAD( "xt-faraday_pac_32k.bin", 0x8000, 0x8000, CRC(d1edf110) SHA1(09570ef36dada08a6d3b97d17ad64814fe32d345), ROM_BIOS(44))
	// 45: AMI XT BIOS
	// 8088-BIOS (C) 1985,1986, AMI - (C)AMI, (1255-013189)
	ROM_SYSTEM_BIOS(45, "amixt", "AMI XT BIOS")
	ROMX_LOAD( "ami_8088_bios_31jan89.bin", 0xe000, 0x2000, CRC(0bcafd1f) SHA1(cb30f01c46dad83343999c609d6f82092e2e8f54), ROM_BIOS(45))
	// 46: From a motherboard marked VIP M X M/10
	// Phoenix ROM BIOS Ver 2.52
	ROM_SYSTEM_BIOS(46, "vipmxm10", "VIP M X M/10")
	ROMX_LOAD( "xt-vip-mxm-10.bin", 0x8000, 0x8000, CRC(6fd64a0a) SHA1(43808f758e9e92d8920e8c3590c3050ec68415aa), ROM_BIOS(46))
	// 47: JUKO BABY XT BXM/12
	ROM_SYSTEM_BIOS(47, "bxm12", "JUKO Baby XT BXM/12")
	ROMX_LOAD("juko_baby_xt_bxm_12.bin", 0xe000, 0x2000, CRC(22d29b06) SHA1(75a504f50e4779d7fc0f0e0b0b1c17d3705cab42), ROM_BIOS(47))
ROM_END

// BIOS versions specifically for NEC V20 CPUs, these don't run on plain 8088
ROM_START( pcv20 )
	ROM_REGION(0x10000, "bios", 0)
	// 0: V20-BIOS Version 3.75 c't // (C) Peter Köhlmann 1987
	ROM_SYSTEM_BIOS(0, "v375", "c't v3.75")
	ROMX_LOAD( "peterv203.75.bin", 0xe000, 0x2000, CRC(b053a6a4) SHA1(f53218ad3d725f12d9149b22d8afcf6a8869a3bd), ROM_BIOS(0))
	// 1: V20-BIOS Version 3.72 c't // (C) Peter Köhlmann 1987 => last known version is 3.82
	ROM_SYSTEM_BIOS(1, "v372", "c't v3.72")
	ROMX_LOAD( "v20xtbios.bin", 0xe000, 0x2000, CRC(b2dca2e4) SHA1(18b0cb90084723eae08cf6b27bfb3fec8e9fb11b), ROM_BIOS(1))
	// 2: Chipset: Vopl TM 215 8750KK - M1101 / M78H012A / 7723 - CPU: NEC 8805F5 V20 D70108C-10
	// OSC: 32.000000MHz, 14.31818, 16.000MHz
	ROM_SYSTEM_BIOS(2, "v365", "c't v3.65")
	ROMX_LOAD( "xt_ls-1720_u52.bin", 0xe000, 0x2000, CRC(7082371a) SHA1(9965dbae5fa4355bc6325ac27a9acc176cc454c3), ROM_BIOS(2))
	// ROM_LOAD( "xt_ls-1720_u8.bin", 0x0000, 0x2000, CRC(aa1d3916) SHA1(bb1723fc637d5d8a9af82b2bdd9e3b11689f0cb9)))
	ROM_SYSTEM_BIOS(3, "glabios_0.24", "GLaBIOS 0.24") // Open Source XT clone BIOS under GPL3 https://github.com/640-KB/GLaBIOS
	// Versions of this BIOS exist for 8088, V20, pure emulation, homebrew projects, XT chipsets and genuine IBM 5150/5160 machines
	ROMX_LOAD( "glabios_0.2.4_vt.rom", 0xe000, 0x2000, CRC(7c173fe3) SHA1(4ac6ca07453890e02c203617fbbdeefb53098cdb), ROM_BIOS(3))
ROM_END


#define rom_pcmda    rom_pc

#define rom_pcherc   rom_pc

#define rom_pcega    rom_pc

#define rom_pcvga    rom_pc

} // anonymous namespace


/***************************************************************************

  Game driver(s)

***************************************************************************/

//    YEAR  NAME    PARENT   COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY      FULLNAME
COMP( 1987, pc,     ibm5150, 0,      pccga,   0,     genpc_state, empty_init, "<generic>", "PC (CGA)",        0 )
COMP( 1987, pcega,  ibm5150, 0,      pcega,   0,     genpc_state, empty_init, "<generic>", "PC (EGA)",        0 )
COMP( 1987, pcmda,  ibm5150, 0,      pcmda,   0,     genpc_state, empty_init, "<generic>", "PC (MDA)",        0 )
COMP( 1987, pcherc, ibm5150, 0,      pcherc,  0,     genpc_state, empty_init, "<generic>", "PC (Hercules)",   0 )
COMP( 1987, pcvga,  ibm5150, 0,      pcvga,   0,     genpc_state, empty_init, "<generic>", "PC (VGA)",        0 )
COMP( 1987, pcv20,  ibm5150, 0,      pcv20,   0,     genpc_state, empty_init, "<generic>", "PC with V20 CPU", 0 )



ggm.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:bataais, Berger
/*******************************************************************************

Applied Concepts Great Game Machine (GGM), electronic board game computer,
in cooperation with Chafitz, who marketed it as Modular Game System (MGS).
After a legal dispute with Chafitz, all rights went to Applied Concepts.

Hardware notes:
- 6502A 2MHz, SYP6522 VIA
- 2KB RAM (1*M58725P battery-backed, or 4*2114 / 2*2114 + 2*2114L of which
  only the first 1KB is battery-backed), no ROM on main PCB
- 2*74164 shift register, 3*6118P VFD driver
- 8-digit 14seg VFD panel (same one as in Speak & Spell)
- 5*4 keypad(unlabeled by default), 1-bit sound

Games are on separate cartridges, each came with a keypad overlay.
There were also some standalone machines, eg. Morphy Encore, Odin Encore.
Cartridge pins are A0-A15, D0-D7, external RAM CS and RAM WR.

The opening/endgame cartridges are meant to be ejected/inserted while playing:
Press RANK, switch power switch to MEM (internal RAM gets powered by rechargable
battery), swap cartridge, switch power switch back to ON. In other words, don't
power cycle the machine (or MAME).

Known chess cartridges (*denotes not dumped):
- Chess/Boris 2.5 (aka Sargon 2.5)
- Gruenfeld Edition: Master Chess Openings
- Morphy Edition: Master Chess
- Capablanca Edition: Master Chess Endgame
- Sandy Edition: Master Chess (German language version of Morphy)
- Steinitz Edition-4: Master Chess
- *Monitor Edition: Master Kriegspiel

The newer revisions of Gruenfeld and Capablanca did not have any version label
visible on the module, though the German distributor Sandy Electronic sold them
(and offered an upgrade service) as Gruenfeld-S and Capablanca-S.

Other games:
- *Borchek: Championship Checkers
- Las Vegas 21: Master Blackjack
- Odin Edition: Master Reversi

There are supposedly 2 versions of Odin, one with 7 levels, and one with 8 levels
(level 8 is a tournament level). Odin Encore is said to have 7 levels, but that
can't be right, since the one in MAME was dumped from an Odin Encore. More likely,
there are 2 revisions, used in both the standalone version and separate module.

Advertised games, presumed they were never released: Backgammon, Lunar Lander,
WitsEnd (Mastermind).

TODO:
- confirm display AP segment, is it used anywhere?

*******************************************************************************/

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/m6502/m6502.h"
#include "machine/6522via.h"
#include "machine/nvram.h"
#include "machine/timer.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"
#include "softlist_dev.h"

// internal artwork
#include "aci_ggm.lh"


namespace {

class ggm_state : public driver_device
{
public:
	ggm_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_via(*this, "via"),
		m_extram(*this, "extram", 0x800, ENDIANNESS_LITTLE),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_cart(*this, "cartslot"),
		m_ca1_off(*this, "ca1_off"),
		m_fdigit(*this, "fdigit%u.%u", 0U, 0U),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void ggm(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(reset_switch) { update_reset(newval); }
	DECLARE_INPUT_CHANGED_MEMBER(overlay_switch) { update_overlay(); }
	ioport_value overlay_r();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<via6522_device> m_via;
	memory_share_creator<u8> m_extram;
	required_device<pwm_display_device> m_display;
	required_device<dac_bit_interface> m_dac;
	required_device<generic_slot_device> m_cart;
	required_device<timer_device> m_ca1_off;
	output_finder<2, 6> m_fdigit;
	required_ioport_array<4+3> m_inputs;

	u8 m_inp_mux = 0;
	u16 m_digit_data = 0;
	u8 m_shift_data = 0;
	u8 m_shift_clock = 0;

	bool m_extram_enabled = false;
	u8 m_overlay = 0;

	void main_map(address_map &map) ATTR_COLD;

	void update_reset(ioport_value state);
	void update_overlay();
	void update_display();
	TIMER_DEVICE_CALLBACK_MEMBER(ca1_off) { m_via->write_ca1(0); }

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(load_cart);
	DECLARE_DEVICE_IMAGE_UNLOAD_MEMBER(unload_cart);

	u8 extram_r(offs_t offset);
	void extram_w(offs_t offset, u8 data);
	void select_w(u8 data);
	void control_w(u8 data);
	u8 input_r();

	void shift_clock_w(int state);
	void shift_data_w(int state);
};

void ggm_state::machine_start()
{
	m_fdigit.resolve();

	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_digit_data));
	save_item(NAME(m_shift_data));
	save_item(NAME(m_shift_clock));
}



/*******************************************************************************
    Power
*******************************************************************************/

void ggm_state::machine_reset()
{
	update_overlay();

	// it determines whether it's a cold boot or warm boot ("MEM" switch), with CA1
	if (~m_inputs[4]->read() & 2)
	{
		m_via->write_ca1(1);
		m_ca1_off->adjust(attotime::from_msec(10));
	}
	else
		update_reset(1);
}

void ggm_state::update_reset(ioport_value state)
{
	// assume that the MEM switch puts the system in reset state (just like Boris)
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? ASSERT_LINE : CLEAR_LINE);

	if (state)
	{
		m_via->reset();
		m_display->clear();
	}
}



/*******************************************************************************
    Keypad Overlay
*******************************************************************************/

ioport_value ggm_state::overlay_r()
{
	u8 data = m_inputs[5]->read() & 0xf;
	return (data == 0xf) ? m_overlay : data;
}

void ggm_state::update_overlay()
{
	static const u16 overlay_lut[16] =
	{
		// see input defs for which overlay number is which
		0, 1, 1, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
	};

	static const u16 fdigit_lut[][6] =
	{
		// button labels in same style as MAME digits
		{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 },
		{ 0x01fe, 0x1bdc, 0x1bfe, 0x2408, 0x0cac, 0x383e },
		{ 0x0000, 0x0000, 0x0000, 0x003f, 0x0000, 0x3fff },
	};

	for (int i = 0; i < 6; i++)
	{
		u16 value = fdigit_lut[overlay_lut[overlay_r()]][i];
		m_fdigit[1][i] = value;
		m_fdigit[0][i] = value ^ 0x3fff;
	}
}



/*******************************************************************************
    Cartridge
*******************************************************************************/

DEVICE_IMAGE_LOAD_MEMBER(ggm_state::load_cart)
{
	u32 size = m_cart->common_get_size("rom");
	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	// keypad overlay
	const char *overlay = image.get_feature("overlay");
	m_overlay = overlay ? strtoul(overlay, nullptr, 0) & 0xf : 0;

	// external ram (optional)
	memset(m_extram, 0, m_extram.bytes());
	m_extram_enabled = image.get_feature("ram") != nullptr;

	return std::make_pair(std::error_condition(), std::string());
}

DEVICE_IMAGE_UNLOAD_MEMBER(ggm_state::unload_cart)
{
	// reset external ram
	memset(m_extram, 0, m_extram.bytes());
	m_extram_enabled = false;
}

u8 ggm_state::extram_r(offs_t offset)
{
	// A11 = 1, A14 + A15 = 0
	if (m_extram_enabled)
		return m_extram[offset & 0x7ff];
	else
		return m_cart->read_rom(offset);
}

void ggm_state::extram_w(offs_t offset, u8 data)
{
	if (m_extram_enabled)
		m_extram[offset & 0x7ff] = data;
}



/*******************************************************************************
    I/O
*******************************************************************************/

void ggm_state::update_display()
{
	u16 data = bitswap<16>(m_digit_data,15,7,2,11,10,3,1,9,6,14,12,5,0,4,13,8);
	m_display->matrix(m_inp_mux, data);
}

void ggm_state::shift_clock_w(int state)
{
	// shift display segment data on rising edge
	if (state && !m_shift_clock)
	{
		m_digit_data = m_digit_data << 1 | (m_shift_data & 1);
		update_display();
	}

	m_shift_clock = state;
}

void ggm_state::shift_data_w(int state)
{
	m_shift_data = state;
}

void ggm_state::select_w(u8 data)
{
	// PA0-PA7: input mux, digit select
	m_inp_mux = data;
	update_display();
}

void ggm_state::control_w(u8 data)
{
	// PB0: DC/DC converter, toggles once per IRQ
	// (probably for VFD, not needed for emulation)

	// PB7: speaker out
	m_dac->write(BIT(data, 7));
}

u8 ggm_state::input_r()
{
	u8 data = 0;

	// PB1-PB5: multiplexed inputs
	for (int i = 0; i < 4; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read();

	data = ~data << 1 & 0x3e;

	// PB6: hardware version
	return 0x81 | data | (m_inputs[4]->read() << 6 & 0x40);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void ggm_state::main_map(address_map &map)
{
	// external slot has potential bus conflict with RAM/VIA
	map(0x0000, 0xffff).r(m_cart, FUNC(generic_slot_device::read_rom));
	map(0x0000, 0x3fff).rw(FUNC(ggm_state::extram_r), FUNC(ggm_state::extram_w));
	map(0x0000, 0x07ff).mirror(0x3000).ram().share("nvram");
	map(0x8000, 0x800f).mirror(0x3ff0).m(m_via, FUNC(via6522_device::map));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

#define OVERLAY(val) \
	PORT_CONDITION("IN.6", 0x0f, EQUALS, val)

// overlay numbers are in order they were added to MAME, this prevents complications with (external) artwork

static INPUT_PORTS_START( overlay_boris ) // actually most of the Chess games have a similar overlay
	PORT_MODIFY("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x01) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x01) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("A.1 / Pawn")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x01) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("B.2 / Knight")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x01) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("C.3 / Bishop")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x01) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("D.4 / Rook")

	PORT_MODIFY("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x01) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("E.5 / Queen")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x01) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("F.6 / King")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x01) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("G.7")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x01) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("H.8")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x01) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")

	PORT_MODIFY("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x01) PORT_CODE(KEYCODE_SPACE) PORT_CODE(KEYCODE_MINUS) PORT_NAME("-")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x01) PORT_CODE(KEYCODE_W) PORT_NAME("B/W")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x01) PORT_CODE(KEYCODE_K) PORT_NAME("Rank")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x01) PORT_CODE(KEYCODE_T) PORT_NAME("Time")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x01) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE")

	PORT_MODIFY("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x01) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x01) PORT_CODE(KEYCODE_I) PORT_NAME("Halt / Hint")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x01) PORT_CODE(KEYCODE_S) PORT_NAME("Best")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x01) PORT_CODE(KEYCODE_R) PORT_NAME("Restore")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x01) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")
INPUT_PORTS_END

static INPUT_PORTS_START( overlay_morphy ) // only changed "9" to "Audio"
	PORT_MODIFY("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x02) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x02) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("A.1 / Pawn")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x02) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("B.2 / Knight")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x02) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("C.3 / Bishop")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x02) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("D.4 / Rook")

	PORT_MODIFY("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x02) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("E.5 / Queen")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x02) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("F.6 / King")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x02) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("G.7")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x02) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("H.8")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x02) PORT_CODE(KEYCODE_U) PORT_NAME("Audio")

	PORT_MODIFY("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x02) PORT_CODE(KEYCODE_SPACE) PORT_CODE(KEYCODE_MINUS) PORT_NAME("-")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x02) PORT_CODE(KEYCODE_W) PORT_NAME("B/W")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x02) PORT_CODE(KEYCODE_K) PORT_NAME("Rank")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x02) PORT_CODE(KEYCODE_T) PORT_NAME("Time")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x02) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE")

	PORT_MODIFY("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x02) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x02) PORT_CODE(KEYCODE_I) PORT_NAME("Halt / Hint")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x02) PORT_CODE(KEYCODE_S) PORT_NAME("Best")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x02) PORT_CODE(KEYCODE_R) PORT_NAME("Restore")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x02) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")
INPUT_PORTS_END

static INPUT_PORTS_START( overlay_steinitz )
	PORT_MODIFY("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x03) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Display / 0")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x03) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("A.1 / Pawn")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x03) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("B.2 / Knight")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x03) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("C.3 / Bishop")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x03) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("D.4 / Rook")

	PORT_MODIFY("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x03) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("E.5 / Queen")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x03) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("F.6 / King")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x03) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("G.7")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x03) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("H.8")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x03) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_U) PORT_NAME("Audio / 9")

	PORT_MODIFY("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x03) PORT_CODE(KEYCODE_V) PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Review / Left / Right")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x03) PORT_CODE(KEYCODE_W) PORT_NAME("B/W")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x03) PORT_CODE(KEYCODE_K) PORT_NAME("Rank")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x03) PORT_CODE(KEYCODE_T) PORT_NAME("Time / Change")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x03) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE")

	PORT_MODIFY("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x03) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x03) PORT_CODE(KEYCODE_I) PORT_NAME("Halt / Hint / Look")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x03) PORT_CODE(KEYCODE_S) PORT_NAME("Best / Score")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x03) PORT_CODE(KEYCODE_R) PORT_NAME("Restore / Timing")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x03) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")
INPUT_PORTS_END

static INPUT_PORTS_START( overlay_lasvegas )
	PORT_MODIFY("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x04) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x04) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x04) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x04) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x04) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")

	PORT_MODIFY("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x04) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x04) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x04) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x04) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x04) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")

	PORT_MODIFY("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x04) PORT_CODE(KEYCODE_D) PORT_NAME("Double")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x04) PORT_CODE(KEYCODE_H) PORT_NAME("Hit")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x04) PORT_CODE(KEYCODE_S) PORT_NAME("Stand / Decline")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x04) PORT_CODE(KEYCODE_I) PORT_NAME("Take Insurance")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x04) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE")

	PORT_MODIFY("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x04) PORT_CODE(KEYCODE_L) PORT_NAME("Split")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x04) PORT_CODE(KEYCODE_P) PORT_NAME("Shuffle Point")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x04) PORT_CODE(KEYCODE_A) PORT_NAME("Audio")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x04) PORT_CODE(KEYCODE_T) PORT_NAME("Total")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x04) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")
INPUT_PORTS_END

static INPUT_PORTS_START( overlay_odin )
	PORT_MODIFY("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x05) PORT_CODE(KEYCODE_S) PORT_NAME("Flips")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x05) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("A.1 / Black")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x05) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("B.2 / Erase")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x05) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("C.3 / White")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x05) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("D.4")

	PORT_MODIFY("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x05) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("E.5")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x05) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("F.6")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x05) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("G.7")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x05) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("H.8")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x05) PORT_CODE(KEYCODE_U) PORT_NAME("Audio")

	PORT_MODIFY("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x05) PORT_CODE(KEYCODE_P) PORT_NAME("Play / -")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x05) PORT_CODE(KEYCODE_W) PORT_NAME("B/W")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x05) PORT_CODE(KEYCODE_K) PORT_NAME("Rank")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x05) PORT_CODE(KEYCODE_T) PORT_NAME("Time")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x05) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE")

	PORT_MODIFY("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x05) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x05) PORT_CODE(KEYCODE_I) PORT_NAME("Hint")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x05) PORT_CODE(KEYCODE_X) PORT_NAME("Halt")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x05) PORT_CODE(KEYCODE_R) PORT_NAME("Restore")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x05) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")
INPUT_PORTS_END

static INPUT_PORTS_START( ggm )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x00) PORT_CODE(KEYCODE_X) PORT_NAME("Keypad 4-2")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x00) PORT_CODE(KEYCODE_S) PORT_NAME("Keypad 3-2")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x00) PORT_CODE(KEYCODE_D) PORT_NAME("Keypad 3-3")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x00) PORT_CODE(KEYCODE_F) PORT_NAME("Keypad 3-4")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x00) PORT_CODE(KEYCODE_W) PORT_NAME("Keypad 2-2")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x00) PORT_CODE(KEYCODE_E) PORT_NAME("Keypad 2-3")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x00) PORT_CODE(KEYCODE_R) PORT_NAME("Keypad 2-4")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x00) PORT_CODE(KEYCODE_2) PORT_NAME("Keypad 1-2")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x00) PORT_CODE(KEYCODE_3) PORT_NAME("Keypad 1-3")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x00) PORT_CODE(KEYCODE_4) PORT_NAME("Keypad 1-4")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x00) PORT_CODE(KEYCODE_C) PORT_NAME("Keypad 4-3")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x00) PORT_CODE(KEYCODE_V) PORT_NAME("Keypad 4-4")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x00) PORT_CODE(KEYCODE_G) PORT_NAME("Keypad 3-5")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x00) PORT_CODE(KEYCODE_T) PORT_NAME("Keypad 2-5")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x00) PORT_CODE(KEYCODE_5) PORT_NAME("Keypad 1-5")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x00) PORT_CODE(KEYCODE_Z) PORT_NAME("Keypad 4-1")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x00) PORT_CODE(KEYCODE_Q) PORT_NAME("Keypad 2-1")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x00) PORT_CODE(KEYCODE_A) PORT_NAME("Keypad 3-1")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x00) PORT_CODE(KEYCODE_1) PORT_NAME("Keypad 1-1")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) OVERLAY(0x00) PORT_CODE(KEYCODE_B) PORT_NAME("Keypad 4-5")

	PORT_INCLUDE( overlay_boris )
	PORT_INCLUDE( overlay_morphy )
	PORT_INCLUDE( overlay_steinitz )
	PORT_INCLUDE( overlay_lasvegas )
	PORT_INCLUDE( overlay_odin )

	PORT_START("IN.4")
	PORT_CONFNAME( 0x01, 0x00, "Version" ) // factory-set
	PORT_CONFSETTING(    0x00, "GGM (Applied Concepts)" )
	PORT_CONFSETTING(    0x01, "MGS (Chafitz)" )
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_F1) PORT_TOGGLE PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ggm_state::reset_switch), 0) PORT_NAME("Memory Switch")

	PORT_START("IN.5")
	PORT_CONFNAME( 0x0f, 0x0f, "Keypad Overlay" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ggm_state::overlay_switch), 0)
	PORT_CONFSETTING(    0x00, "None" )
	PORT_CONFSETTING(    0x0f, "Auto" ) // get param from softwarelist
	PORT_CONFSETTING(    0x01, "Boris 2.5" )
	PORT_CONFSETTING(    0x04, "Las Vegas 21" )
	PORT_CONFSETTING(    0x02, "Morphy" )
	PORT_CONFSETTING(    0x05, "Odin" )
	PORT_CONFSETTING(    0x03, "Steinitz" )

	PORT_START("IN.6")
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_CUSTOM_MEMBER(FUNC(ggm_state::overlay_r))
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void ggm_state::ggm(machine_config &config)
{
	// basic machine hardware
	M6502(config, m_maincpu, 2_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &ggm_state::main_map);

	MOS6522(config, m_via, 2_MHz_XTAL); // DDRA = 0xff, DDRB = 0x81
	m_via->writepa_handler().set(FUNC(ggm_state::select_w));
	m_via->writepb_handler().set(FUNC(ggm_state::control_w));
	m_via->readpb_handler().set(FUNC(ggm_state::input_r));
	m_via->irq_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);
	m_via->cb1_handler().set(FUNC(ggm_state::shift_clock_w));
	m_via->cb2_handler().set(FUNC(ggm_state::shift_data_w));
	TIMER(config, m_ca1_off).configure_generic(FUNC(ggm_state::ca1_off));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 16);
	m_display->set_segmask(0xff, 0x3fff);
	m_display->set_bri_levels(0.05);
	config.set_default_layout(layout_aci_ggm);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);

	// cartridge
	GENERIC_CARTSLOT(config, m_cart, generic_linear_slot, "ggm");
	m_cart->set_device_load(FUNC(ggm_state::load_cart));
	m_cart->set_device_unload(FUNC(ggm_state::unload_cart));
	m_cart->set_must_be_loaded(true);

	SOFTWARE_LIST(config, "cart_list").set_original("ggm");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( ggm )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
	// nothing here, ROM is on cartridge
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY, FULLNAME, FLAGS
SYST( 1980, ggm,  0,      0,      ggm,     ggm,   ggm_state, empty_init, "Applied Concepts / Chafitz", "Great Game Machine", MACHINE_SUPPORTS_SAVE )



gigatron.cpp
<---------------------------------------------------------------------->
// license:BSD-2-Clause
// copyright-holders:Sterophonick, Phil Thomas
/***************************************************************************

    Driver for Gigatron TTL Microcomputer by Sterophonick

    Based on Gigatron.js by Phil Thomas
    https://github.com/PhilThomas/gigatron

***************************************************************************/

#include "emu.h"
#include "cpu/gigatron/gigatron.h"
#include "screen.h"
#include "sound/dac.h"
#include "speaker.h"

#include "gigatron.lh"


namespace {

#define MAIN_CLOCK 6250000
#define VSYNC      0x80
#define HSYNC      0x40

/***************************************************************************

    TODO

    Hook up a quikload for loading .gt1 files
    HLE the keyboard and Pluggy McPlugface

***************************************************************************/


//**************************************************************************
//  Driver Definition
//**************************************************************************

class gigatron_state : public driver_device
{
public:
	gigatron_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_dac(*this, "dac")
		, m_screen(*this, "screen")
		, m_io_inputs(*this, "GAMEPAD")
		, m_blinken(*this, "blinken%u", 1U)
	{
	}

	void gigatron(machine_config &config);

private:

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;
	virtual void video_reset() override ATTR_COLD;

	void prog_map(address_map &map) ATTR_COLD;
	void data_map(address_map &map) ATTR_COLD;

	uint8_t m_lights; //blinkenlights

	//Video Generation stuff
	uint8_t m_out;
	int16_t m_row;
	int16_t m_col;
	uint8_t m_pixel;

	uint8_t m_dacoutput;

	void port_outx(uint8_t data);
	void port_out(uint8_t data);

	std::unique_ptr<bitmap_rgb32> m_bitmap_render;

	required_device<gigatron_cpu_device> m_maincpu;
	required_device<dac_byte_interface> m_dac;
	required_device<screen_device> m_screen;
	required_ioport m_io_inputs;

	output_finder<4> m_blinken;
};

//**************************************************************************
//  Video
//**************************************************************************

void gigatron_state::video_start()
{
	m_bitmap_render = std::make_unique<bitmap_rgb32>(640, 480);
}

void gigatron_state::video_reset()
{
	uint32_t *dest = &m_bitmap_render->pix(0, 0);
	for(uint32_t i = 0; i < 640*480; i++)
		*dest++ = 0;
}

void gigatron_state::port_out(uint8_t data)
{
	m_pixel = data;
	uint8_t out = m_pixel;
	uint8_t falling = m_out & ~out;

	if (falling & VSYNC)
	{
		m_row = -36;
		m_pixel = 0;
	}

	if (falling & HSYNC)
	{
		m_col = -4;
		m_row++;
	}

	m_out = out;

	if ((out & (VSYNC | HSYNC)) != (VSYNC | HSYNC))
	{
		return;
	}

	if((m_row >= 0 && m_row < 480) && (m_col >= 0 && m_col < 640))
	{
		uint8_t r = (out << 6) & 0xC0;
		uint8_t g = (out << 4) & 0xC0;
		uint8_t b = (out << 2) & 0xC0;
		uint32_t *dest = &m_bitmap_render->pix(m_row, m_col);
		for(uint8_t i = 0; i < 4; i++)
			*dest++ = b|(g<<8)|(r<<16);
	}
	m_col += 4;
}

//6-bit color, VGA
uint32_t gigatron_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	copybitmap(bitmap, *m_bitmap_render, 0, 0, 0, 0, cliprect);
	video_reset();

	m_blinken[0] = BIT(m_lights, 3);
	m_blinken[1] = BIT(m_lights, 2);
	m_blinken[2] = BIT(m_lights, 1);
	m_blinken[3] = BIT(m_lights, 0);

	return 0;
}

//**************************************************************************
//  Memory Map
//**************************************************************************

void gigatron_state::prog_map(address_map &map)
{
	map(0x0000, 0xffff).rom().region("maincpu", 0);
}

void gigatron_state::data_map(address_map &map)
{
	map(0x0000, 0xffff).ram();
}

//**************************************************************************
//  Machine
//**************************************************************************

static INPUT_PORTS_START(gigatron)
	PORT_START("GAMEPAD")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1) // START
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1)    // SELECT
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("B Button") PORT_PLAYER(1)    // B Button
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("A Button") PORT_PLAYER(1)    // A Button
INPUT_PORTS_END

void gigatron_state::machine_start()
{
	//blinkenlights
	m_blinken.resolve();

	//Savestate stuff
	save_item(NAME(m_lights));
	save_item(NAME(m_out));
	save_item(NAME(m_row));
	save_item(NAME(m_col));
	save_item(NAME(m_pixel));
	save_item(NAME(m_dacoutput));
}

void gigatron_state::machine_reset()
{
	m_dacoutput = 0;
	m_dac->write(0);
	m_lights = 0;
	m_out = 0;
	m_row = 0;
	m_col = 0;
	m_pixel = 0;
}

void gigatron_state::port_outx(uint8_t data)
{
	//Write sound to DAC
	m_dacoutput = (data & 0xF0) >> 4;
	m_dac->write(m_dacoutput);

	//Blinkenlights
	m_lights = data & 0xF;
}

void gigatron_state::gigatron(machine_config &config)
{
	config.set_default_layout(layout_gigatron);

	/* sound hardware */
	SPEAKER(config, "speaker").front_center();
	DAC_4BIT_R2R(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.5);

	GTRON(config, m_maincpu, MAIN_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &gigatron_state::prog_map);
	m_maincpu->set_addrmap(AS_DATA, &gigatron_state::data_map);
	m_maincpu->outx_cb().set(FUNC(gigatron_state::port_outx));
	m_maincpu->out_cb().set(FUNC(gigatron_state::port_out));
	m_maincpu->ir_cb().set_ioport("GAMEPAD").invert();

	/* video hardware */
	screen_device &m_screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	m_screen.set_refresh_hz(59.98);
	m_screen.set_size(640, 480);
	m_screen.set_visarea(0, 640-1, 0, 480-1);
	m_screen.set_screen_update(FUNC(gigatron_state::screen_update));
}

ROM_START( gigatron )
	ROM_REGION( 0x20000, "maincpu", 0 )
	ROM_SYSTEM_BIOS(0, "v5a", "Gigatron ROM v5a")
	ROMX_LOAD( "gigrom5a.rom",  0x0000, 0x20000, CRC(dcc071a6) SHA1(f82059ba0227ff48e4c687b90c8445da30213ee2),ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v4", "Gigatron ROM v4")
	ROMX_LOAD( "gigrom4.rom",  0x0000, 0x20000, CRC(78995109) SHA1(2395fc48e64099836111f5aeca39ddbf4650ea4e),ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v3", "Gigatron ROM v3")
	ROMX_LOAD( "gigrom3.rom",  0x0000, 0x20000, CRC(1536efbe) SHA1(959268069e761a01d620396eedb9abc1ee63c421),ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v2", "Gigatron ROM v2")
	ROMX_LOAD( "gigrom2.rom",  0x0000, 0x20000, CRC(b4a3d936) SHA1(c93f417d589144b912c79f85b9e942d66242c2c3),ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "v1", "Gigatron ROM v1")
	ROMX_LOAD( "gigrom1.rom",  0x0000, 0x20000, CRC(8ea5a2af) SHA1(e5758d5cc467c3476bd8f992fd45dfcdf06d0430),ROM_BIOS(4))
ROM_END

} // anonymous namespace


COMP(2018, gigatron, 0, 0, gigatron, gigatron, gigatron_state, empty_init, "Marcel van Kervinck / Walter Belgers", "Gigatron TTL Microcomputer", MACHINE_SUPPORTS_SAVE)



gimix.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Barry Rodewald, 68bit
/*
    Gimix 6809-Based Computers

    Representative group of GIMIX users included:  Government Research and Scientific Organizations, Universities, Industrial, Computer Mainframe and
    Peripheral Manufacturers and Software Houses.

    This system is most notable for being the development base of the "Vid Kidz", a pair of programmers (Eugene Jarvis and Larry DeMar) who formerly
    worked for Williams Electronics on the game, Defender.  They left Willams and continued work on other games eventually making a deal with Williams
    to continue to design games producing the titles: Stargate, Robotron: 2084 and Blaster.

    Information Link:  http://www.backglass.org/duncan/gimix/

    TODO: Hard disk support

    Usage:
    System boots into GMXBUG-09
    To boot Flex, insert the Flex system disk (3.3 or later, must support the DMA disk controller), type U and press enter.
    To boot OS-9, select ROM version, insert the OS-9 system disk, type O, and press Enter.
*/

#include "emu.h"
#include "cpu/m6809/m6809.h"
#include "imagedev/floppy.h"
#include "machine/input_merger.h"
#include "machine/mm58167.h"
#include "machine/6840ptm.h"
#include "machine/6821pia.h"
#include "machine/6850acia.h"
#include "bus/rs232/rs232.h"
#include "machine/clock.h"
#include "machine/wd_fdc.h"
#include "machine/bankdev.h"
#include "machine/ram.h"
#include "formats/flex_dsk.h"
#include "formats/os9_dsk.h"
#include "softlist_dev.h"


namespace {

#define DMA_DRQ         (m_dma_status & 0x80)
#define DMA_INTRQ       (m_dma_status & 0x40)
#define DMA_MOTOR_DELAY (m_dma_status & 0x20)
#define DMA_ENABLED     (m_dma_status & 0x10)
#define DMA_FAULT       (m_dma_status & 0x08)
#define DMA_DRIVE_SIZE  (m_dma_status & 0x04)
#define DMA_DIP_SENSE   (m_dma_status & 0x01)

#define DMA_CONNECT_SEL (m_dma_drive_select & 0x40)
#define DMA_DENSITY     (m_dma_drive_select & 0x20)
#define DMA_WR_PROTECT  (m_dma_drive_select & 0x10)
#define DMA_SEL_DRV3    (m_dma_drive_select & 0x08)
#define DMA_SEL_DRV2    (m_dma_drive_select & 0x04)
#define DMA_SEL_DRV1    (m_dma_drive_select & 0x02)
#define DMA_SEL_DRV0    (m_dma_drive_select & 0x01)

#define DMA_IRQ_ENABLE  (m_dma_ctrl & 0x80)
#define DMA_SIDE_SEL    (m_dma_ctrl & 0x40)
#define DMA_DIRECTION   (m_dma_ctrl & 0x20)
#define DMA_ENABLE      (m_dma_ctrl & 0x10)
#define DMA_BANK        (m_dma_ctrl & 0x0f)

#define DMA_START_ADDR  (((m_dma_ctrl & 0x0f) << 16) | m_dma_start_addr)

class gimix_state : public driver_device
{
public:
	gimix_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_irqs(*this, "irqs")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0U)
		, m_ram(*this, RAM_TAG)
		, m_rom(*this, "roms")
		, m_acia1(*this, "acia1")
		, m_acia2(*this, "acia2")
		, m_acia3(*this, "acia3")
		, m_acia4(*this, "acia4")
		, m_bank(*this, "bank%u", 1U)
		, m_rombank1(*this, "rombank1")
		, m_rombank2(*this, "rombank2")
		, m_fixedrombank(*this, "fixedrombank")
		, m_lowerram(*this, "lower_ram")
		, m_upperram(*this, "upper_ram")
		, m_dma_dip(*this, "dma_s2")
	{}

	void gimix(machine_config &config);

private:
	void system_w(offs_t offset, uint8_t data);
	void fdc_irq_w(int state);
	void fdc_drq_w(int state);
	uint8_t dma_r(offs_t offset);
	void dma_w(offs_t offset, uint8_t data);
	uint8_t fdc_r(offs_t offset);
	void fdc_w(offs_t offset, uint8_t data);
	uint8_t pia_pa_r();
	void pia_pa_w(uint8_t data);
	uint8_t pia_pb_r();
	void pia_pb_w(uint8_t data);

	static void floppy_formats(format_registration &fr);

	void gimix_banked_mem(address_map &map) ATTR_COLD;
	void gimix_mem(address_map &map) ATTR_COLD;

	// disassembly override
	offs_t os9_dasm_override(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const util::disasm_interface::data_buffer &params);
	offs_t dasm_override(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const util::disasm_interface::data_buffer &params);

	bool m_fpla_sw_latch;
	uint8_t m_term_data;
	uint8_t m_dma_status;
	uint8_t m_dma_ctrl;
	uint8_t m_dma_drive_select;
	uint16_t m_dma_start_addr;
	uint32_t m_dma_current_addr;
	uint8_t m_task;
	uint8_t m_task_banks[16][16];
	uint8_t m_selected_drive;
	bool m_floppy_ready[4];

	uint8_t m_pia1_pa;
	uint8_t m_pia1_pb;

	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	virtual void driver_start() override;

	void refresh_memory();

	required_device<cpu_device> m_maincpu;
	required_device<input_merger_device> m_irqs;
	required_device<fd1797_device> m_fdc;
	required_device_array<floppy_connector, 4> m_floppy;
	required_device<ram_device> m_ram;
	required_memory_region m_rom;
	required_device<acia6850_device> m_acia1;
	required_device<acia6850_device> m_acia2;
	required_device<acia6850_device> m_acia3;
	required_device<acia6850_device> m_acia4;

	required_device_array<address_map_bank_device, 16> m_bank;
	required_memory_bank m_rombank1;
	required_memory_bank m_rombank2;
	required_memory_bank m_fixedrombank;
	required_memory_bank m_lowerram;
	memory_bank_creator m_upperram;

	required_ioport m_dma_dip;
};

void gimix_state::gimix_banked_mem(address_map &map)
{
	map(0x00000, 0x0dfff).bankrw("lower_ram");
	map(0x0e000, 0x0e001).rw(m_acia1, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0x0e004, 0x0e005).rw(m_acia2, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	//map(0x0e018, 0x0e01b).rw(FUNC(gimix_state::fdc_r), FUNC(gimix_state::fdc_w));  // FD1797 FDC (PIO)
	map(0x0e100, 0x0e1ff).ram();
	//map(0x0e200, 0x0e20f) // 9511A / 9512 Arithmetic Processor
	map(0x0e210, 0x0e21f).rw("timer", FUNC(ptm6840_device::read), FUNC(ptm6840_device::write));
	map(0x0e220, 0x0e23f).rw("rtc", FUNC(mm58167_device::read), FUNC(mm58167_device::write));
	map(0x0e240, 0x0e3af).ram();
	map(0x0e3b0, 0x0e3b3).rw(FUNC(gimix_state::dma_r), FUNC(gimix_state::dma_w));  // DMA controller (custom?)
	map(0x0e3b4, 0x0e3b7).rw(FUNC(gimix_state::fdc_r), FUNC(gimix_state::fdc_w));  // FD1797 FDC
	map(0x0e400, 0x0e7ff).ram();  // scratchpad RAM
	map(0x0e800, 0x0efff).ram();
	map(0x0f000, 0x0f7ff).bankr("rombank2");
	map(0x0f800, 0x0ffff).bankr("rombank1");
	//map(0x10000, 0x1ffff).ram();
}

void gimix_state::gimix_mem(address_map &map)
{
	for (int bank = 0; bank < 16; bank++)
	{
		map(bank << 12, (bank << 12) | 0x0fff).rw(m_bank[bank], FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	}
	map(0xff00, 0xffff).bankr("fixedrombank").w(FUNC(gimix_state::system_w));
}

static INPUT_PORTS_START( gimix )
	PORT_START("dma_s2")
	PORT_DIPNAME(0x00000100,0x00000000,"5.25\" / 8\" floppy drive 0") PORT_DIPLOCATION("S2:9")
	PORT_DIPSETTING(0x00000000,"5.25\"")
	PORT_DIPSETTING(0x00000100,"8\"")

INPUT_PORTS_END

void gimix_state::refresh_memory()
{
	for (int bank = 0; bank < 16; bank++)
	{
		m_bank[bank]->set_bank(m_task_banks[m_task][bank]);
	}
}

void gimix_state::system_w(offs_t offset, uint8_t data)
{
	if(offset == 0x7f)  // task register
	{
		if(data & 0x20)  // FPLA software latch
		{
			m_rombank1->set_entry(2);
			m_rombank2->set_entry(3);
			m_fixedrombank->set_entry(2);
			m_fpla_sw_latch = true;
			logerror("SYS: FPLA software latch set\n");
		}
		else
		{
			m_rombank1->set_entry(0);
			m_rombank2->set_entry(1);
			m_fixedrombank->set_entry(0);
			m_fpla_sw_latch = false;
			logerror("SYS: FPLA software latch reset\n");
		}
		m_task = data & 0x0f;
		refresh_memory();
		logerror("SYS: Task set to %02x\n",data & 0x0f);
	}
	if(offset >= 0xf0)  // Dynamic Address Translation RAM (write only)
	{
		m_bank[offset-0xf0]->set_bank(data & 0x0f);
		m_task_banks[m_task][offset-0xf0] = data & 0x0f;
		logerror("SYS: Bank %i set to physical bank %02x\n",offset-0xf0,data);
	}
}

uint8_t gimix_state::dma_r(offs_t offset)
{
	switch(offset)
	{
	case 0:
		if(m_dma_dip->read() & 0x00000100)
			m_dma_status |= 0x01;   // 8"
		else
			m_dma_status &= ~0x01;  // 5.25"
		return m_dma_status;
	case 1:
		return m_dma_ctrl;
	case 2:
		return (m_dma_start_addr & 0xff00) >> 8;
	case 3:
		return (m_dma_start_addr & 0x00ff);
	default:
		logerror("DMA: Unknown or invalid DMA register %02x read\n",offset);
	}
	return 0xff;
}

void gimix_state::dma_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
	case 0:
		logerror("DMA: Drive select %02x\n",data);
		m_dma_drive_select = data;
		m_fdc->dden_w(DMA_DENSITY ? 1 : 0);
		if(data & 0x40)  // 8" / 5.25" connector select
		{
			// 8 inch
			m_dma_status |= 0x04;
			m_fdc->set_unscaled_clock(8_MHz_XTAL / 4); // (2MHz)
		}
		else
		{
			// 5.25 inch
			m_dma_status &= ~0x04;
			m_fdc->set_unscaled_clock(8_MHz_XTAL / 8); // (1MHz)
		}

		if(data & 0x01)
		{
			m_selected_drive = 1;
			m_fdc->set_floppy(m_floppy[0]->get_device());
		}
		else if(data & 0x02)
		{
			m_selected_drive = 2;
			m_fdc->set_floppy(m_floppy[1]->get_device());
		}
		else if(data & 0x04)
		{
			m_selected_drive = 3;
			m_fdc->set_floppy(m_floppy[2]->get_device());
		}
		else if(data & 0x08)
		{
			m_selected_drive = 4;
			m_fdc->set_floppy(m_floppy[3]->get_device());
		}
		else
		{
			m_selected_drive = 0;
			m_fdc->set_floppy(nullptr);
		}

		for(int n = 0; n < 4; n++)
		{
			if(m_selected_drive != n + 1)
			{
				m_floppy[n]->get_device()->mon_w(1);
				m_floppy_ready[n] = false;
				logerror("FDC: Floppy drive %d motor off\n", n);
			}
		}
		break;
	case 1:
		logerror("DMA: DMA control %02x\n",data);
		m_dma_ctrl = data;
		if(data & 0x10)
			m_dma_status |= 0x12;
		else
			m_dma_status &= ~0x12;
		if(m_selected_drive != 0)
			m_floppy[m_selected_drive - 1]->get_device()->ss_w(BIT(data, 6));
		if((data & 0x80) == 0)
			m_irqs->in_w<6>(0);
		break;
	case 2:
		logerror("DMA: DMA start address MSB %02x\n",data);
		m_dma_start_addr = (m_dma_start_addr & 0x00ff) | (data << 8);
		m_dma_current_addr = DMA_START_ADDR;
		break;
	case 3:
		logerror("DMA: DMA start address LSB %02x\n",data);
		m_dma_start_addr = (m_dma_start_addr & 0xff00) | data;
		m_dma_current_addr = DMA_START_ADDR;
		break;
	default:
		logerror("DMA: Unknown or invalid DMA register %02x write %02x\n",offset,data);
	}
}

uint8_t gimix_state::fdc_r(offs_t offset)
{
	// motors are switched on on FDC access
	if(m_selected_drive != 0 && !m_floppy_ready[m_selected_drive - 1] && !machine().side_effects_disabled())
	{
		m_floppy[m_selected_drive - 1]->get_device()->mon_w(0);
		m_floppy_ready[m_selected_drive - 1] = true;
		logerror("FDC: Floppy drive %d motor on\n", m_selected_drive - 1);
	}
	return m_fdc->read(offset);
}

void gimix_state::fdc_w(offs_t offset, uint8_t data)
{
	// motors are switched on on FDC access
	if(m_selected_drive != 0 && !m_floppy_ready[m_selected_drive - 1])
	{
		m_floppy[m_selected_drive - 1]->get_device()->mon_w(0);
		m_floppy_ready[m_selected_drive - 1] = true;
		logerror("FDC: Floppy drive %d motor on\n", m_selected_drive - 1);
	}
	m_fdc->write(offset,data);
}

uint8_t gimix_state::pia_pa_r()
{
	return m_pia1_pa;
}

void gimix_state::pia_pa_w(uint8_t data)
{
	m_pia1_pa = data;
	logerror("PIA: Port A write %02x\n",data);
}

uint8_t gimix_state::pia_pb_r()
{
	return m_pia1_pb;
}

void gimix_state::pia_pb_w(uint8_t data)
{
	m_pia1_pb = data;
	logerror("PIA: Port B write %02x\n",data);
}


void gimix_state::fdc_irq_w(int state)
{
	if(state)
		m_dma_status |= 0x40;
	else
		m_dma_status &= ~0x40;

	if (DMA_IRQ_ENABLE)
		m_irqs->in_w<6>(state);
	else
		m_irqs->in_w<6>(0);
}

void gimix_state::fdc_drq_w(int state)
{
	if(state && DMA_ENABLED)
	{
		m_dma_status |= 0x80;
		// do a DMA transfer
		if(DMA_DIRECTION)
		{
			// write to disk
			m_fdc->data_w(m_ram->read(m_dma_current_addr));
//          logerror("DMA: read from RAM %05x\n",m_dma_current_addr);
		}
		else
		{
			// read from disk
			m_ram->write(m_dma_current_addr,m_fdc->data_r());
//          logerror("DMA: write to RAM %05x\n",m_dma_current_addr);
		}
		m_dma_current_addr++;
	}
	else
		m_dma_status &= ~0x80;
}

void gimix_state::machine_reset()
{
	m_term_data = 0;
	m_rombank1->set_entry(0);  // RAM banks are undefined on startup
	m_rombank2->set_entry(1);
	m_fixedrombank->set_entry(0);
	m_fpla_sw_latch = false;
	m_dma_status = 0x00;
	m_dma_ctrl = 0x00;
	m_irqs->in_w<6>(0);
	m_task = 0x00;
	m_selected_drive = 0;
	std::fill(std::begin(m_floppy_ready), std::end(m_floppy_ready), false);
	m_lowerram->set_base(m_ram->pointer());
	if(m_ram->size() > 65536)
		m_upperram->set_base(m_ram->pointer()+0x10000);

	// initialise FDC clock based on DIP Switch S2-9 (5.25"/8" drive select)
	if(m_dma_dip->read() & 0x00000100)
		m_fdc->set_unscaled_clock(8_MHz_XTAL / 4); // 8 inch (2MHz)
	else
		m_fdc->set_unscaled_clock(8_MHz_XTAL / 8); // 5.25 inch (1MHz)
}

void gimix_state::machine_start()
{
	uint8_t* ROM = m_rom->base();
	m_rombank1->configure_entries(0,4,ROM,0x800);
	m_rombank2->configure_entries(0,4,ROM,0x800);
	m_fixedrombank->configure_entries(0,4,ROM+0x700,0x800);
	m_rombank1->set_entry(0);  // RAM banks are undefined on startup
	m_rombank2->set_entry(1);
	m_fixedrombank->set_entry(0);
	m_fpla_sw_latch = false;
	// install any extra RAM
	if(m_ram->size() > 65536)
	{
		for (int bank = 0; bank < 16; bank++)
		{
			m_bank[bank]->space(AS_PROGRAM).install_readwrite_bank(0x10000,m_ram->size()-1,m_upperram);
		}
	}
	m_floppy[0]->get_device()->set_rpm(300);
	m_floppy[1]->get_device()->set_rpm(300);
}

void gimix_state::driver_start()
{
}

void gimix_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_FLEX_FORMAT);
	fr.add(FLOPPY_OS9_FORMAT);
}

static void gimix_floppies(device_slot_interface &device)
{
	device.option_add("525hd", FLOPPY_525_HD);
	device.option_add("8dd", FLOPPY_8_DSDD);
}

/***************************************************************************
  DISASSEMBLY OVERRIDE (OS9 syscalls)
 ***************************************************************************/

static const char *const os9syscalls[] =
{
	"F$Link",          // Link to Module
	"F$Load",          // Load Module from File
	"F$UnLink",        // Unlink Module
	"F$Fork",          // Start New Process
	"F$Wait",          // Wait for Child Process to Die
	"F$Chain",         // Chain Process to New Module
	"F$Exit",          // Terminate Process
	"F$Mem",           // Set Memory Size
	"F$Send",          // Send Signal to Process
	"F$Icpt",          // Set Signal Intercept
	"F$Sleep",         // Suspend Process
	"F$SSpd",          // Suspend Process
	"F$ID",            // Return Process ID
	"F$SPrior",        // Set Process Priority
	"F$SSWI",          // Set Software Interrupt
	"F$PErr",          // Print Error
	"F$PrsNam",        // Parse Pathlist Name
	"F$CmpNam",        // Compare Two Names
	"F$SchBit",        // Search Bit Map
	"F$AllBit",        // Allocate in Bit Map
	"F$DelBit",        // Deallocate in Bit Map
	"F$Time",          // Get Current Time
	"F$STime",         // Set Current Time
	"F$CRC",           // Generate CRC
	"F$GPrDsc",        // get Process Descriptor copy
	"F$GBlkMp",        // get System Block Map copy
	"F$GModDr",        // get Module Directory copy
	"F$CpyMem",        // Copy External Memory
	"F$SUser",         // Set User ID number
	"F$UnLoad",        // Unlink Module by name
	"F$Alarm",         // Color Computer Alarm Call (system wide)
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	"F$TPS",           // Return System's Ticks Per Second
	nullptr,
	"F$VIRQ",          // Install/Delete Virtual IRQ
	"F$SRqMem",        // System Memory Request
	"F$SRtMem",        // System Memory Return
	"F$IRQ",           // Enter IRQ Polling Table
	"F$IOQu",          // Enter I/O Queue
	"F$AProc",         // Enter Active Process Queue
	"F$NProc",         // Start Next Process
	"F$VModul",        // Validate Module
	"F$Find64",        // Find Process/Path Descriptor
	"F$All64",         // Allocate Process/Path Descriptor
	"F$Ret64",         // Return Process/Path Descriptor
	"F$SSvc",          // Service Request Table Initialization
	"F$IODel",         // Delete I/O Module
	"F$SLink",         // System Link
	"F$Boot",          // Bootstrap System
	"F$BtMem",         // Bootstrap Memory Request
	"F$GProcP",        // Get Process ptr
	"F$Move",          // Move Data (low bound first)
	"F$AllRAM",        // Allocate RAM blocks
	"F$AllImg",        // Allocate Image RAM blocks
	"F$DelImg",        // Deallocate Image RAM blocks
	"F$SetImg",        // Set Process DAT Image
	"F$FreeLB",        // Get Free Low Block
	"F$FreeHB",        // Get Free High Block
	"F$AllTsk",        // Allocate Process Task number
	"F$DelTsk",        // Deallocate Process Task number
	"F$SetTsk",        // Set Process Task DAT registers
	"F$ResTsk",        // Reserve Task number
	"F$RelTsk",        // Release Task number
	"F$DATLog",        // Convert DAT Block/Offset to Logical
	"F$DATTmp",        // Make temporary DAT image (Obsolete)
	"F$LDAXY",         // Load A [X,[Y]]
	"F$LDAXYP",        // Load A [X+,[Y]]
	"F$LDDDXY",        // Load D [D+X,[Y]]
	"F$LDABX",         // Load A from 0,X in task B
	"F$STABX",         // Store A at 0,X in task B
	"F$AllPrc",        // Allocate Process Descriptor
	"F$DelPrc",        // Deallocate Process Descriptor
	"F$ELink",         // Link using Module Directory Entry
	"F$FModul",        // Find Module Directory Entry
	"F$MapBlk",        // Map Specific Block
	"F$ClrBlk",        // Clear Specific Block
	"F$DelRAM",        // Deallocate RAM blocks
	"F$GCMDir",        // Pack module directory
	"F$AlHRam",        // Allocate HIGH RAM Blocks
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	"F$RegDmp",        // Ron Lammardo's debugging register dump call
	"F$NVRAM",         // Non Volatile RAM (RTC battery backed static) read/write
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	"I$Attach",        // Attach I/O Device
	"I$Detach",        // Detach I/O Device
	"I$Dup",           // Duplicate Path
	"I$Create",        // Create New File
	"I$Open",          // Open Existing File
	"I$MakDir",        // Make Directory File
	"I$ChgDir",        // Change Default Directory
	"I$Delete",        // Delete File
	"I$Seek",          // Change Current Position
	"I$Read",          // Read Data
	"I$Write",         // Write Data
	"I$ReadLn",        // Read Line of ASCII Data
	"I$WritLn",        // Write Line of ASCII Data
	"I$GetStt",        // Get Path Status
	"I$SetStt",        // Set Path Status
	"I$Close",         // Close Path
	"I$DeletX"         // Delete from current exec dir
};


//-------------------------------------------------
//  os9_dasm_override
//-------------------------------------------------

offs_t gimix_state::os9_dasm_override(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const util::disasm_interface::data_buffer &params)
{
	unsigned call;
	offs_t result = 0;

	// Microware OS-9 (on the Gimix) and a number of other 6x09 based
	// systems used the SWI2 instruction for syscalls.  This checks for a
	// SWI2 and looks up the syscall as appropriate.
	//
	// But only apply this override if the OS9 ROMs are latched on.
	if (!m_fpla_sw_latch)
		return 0;

	if ((opcodes.r8(pc) == 0x10) && (opcodes.r8(pc+1) == 0x3F))
	{
		call = opcodes.r8(pc+2);
		if ((call < std::size(os9syscalls)) && (os9syscalls[call] != nullptr))
		{
			util::stream_format(stream, "OS9   %s", os9syscalls[call]);
			result = 3;
		}
	}
	return result;
}


offs_t gimix_state::dasm_override(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const util::disasm_interface::data_buffer &params)
{
	return os9_dasm_override(stream, pc, opcodes, params);
}

void gimix_state::gimix(machine_config &config)
{
	// basic machine hardware
	MC6809(config, m_maincpu, 8_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &gimix_state::gimix_mem);
	m_maincpu->set_dasm_override(FUNC(gimix_state::dasm_override));

	INPUT_MERGER_ANY_HIGH(config, m_irqs).output_handler().set_inputline(m_maincpu, M6809_IRQ_LINE);

	/* rtc */
	mm58167_device &rtc(MM58167(config, "rtc", 32.768_kHz_XTAL));
	rtc.irq().set(m_irqs, FUNC(input_merger_device::in_w<0>));

	/* timer */
	ptm6840_device &ptm(PTM6840(config, "timer", 2'000'000));  // clock is a guess
	// PCB pictures show both the RTC and timer set to generate IRQs (are jumper configurable)
	ptm.irq_callback().set(m_irqs, FUNC(input_merger_device::in_w<1>));

	/* floppy disks */
	FD1797(config, m_fdc, 8_MHz_XTAL / 4);
	m_fdc->intrq_wr_callback().set(FUNC(gimix_state::fdc_irq_w));
	m_fdc->drq_wr_callback().set(FUNC(gimix_state::fdc_drq_w));
	m_fdc->set_force_ready(true);
	FLOPPY_CONNECTOR(config, "fdc:0", gimix_floppies, "525hd", gimix_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", gimix_floppies, "525hd", gimix_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:2", gimix_floppies, "525hd", gimix_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:3", gimix_floppies, "525hd", gimix_state::floppy_formats).enable_sound(true);

	/* parallel ports */
	pia6821_device &pia1(PIA6821(config, "pia1", 2'000'000));
	pia1.writepa_handler().set(FUNC(gimix_state::pia_pa_w));
	pia1.writepb_handler().set(FUNC(gimix_state::pia_pb_w));
	pia1.readpa_handler().set(FUNC(gimix_state::pia_pa_r));
	pia1.readpb_handler().set(FUNC(gimix_state::pia_pb_r));

	PIA6821(config, "pia2", 2'000'000);

	/* serial ports */
	ACIA6850(config, m_acia1, 2'000'000);
	m_acia1->txd_handler().set("serial1", FUNC(rs232_port_device::write_txd));
	m_acia1->rts_handler().set("serial1", FUNC(rs232_port_device::write_rts));
	m_acia1->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<2>));

	ACIA6850(config, m_acia2, 2'000'000);
	m_acia2->txd_handler().set("serial2", FUNC(rs232_port_device::write_txd));
	m_acia2->rts_handler().set("serial2", FUNC(rs232_port_device::write_rts));
	m_acia2->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<3>));

	ACIA6850(config, m_acia3, 2'000'000);
	m_acia3->txd_handler().set("serial3", FUNC(rs232_port_device::write_txd));
	m_acia3->rts_handler().set("serial3", FUNC(rs232_port_device::write_rts));
	m_acia3->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<4>));

	ACIA6850(config, m_acia4, 2'000'000);
	m_acia4->txd_handler().set("serial4", FUNC(rs232_port_device::write_txd));
	m_acia4->rts_handler().set("serial4", FUNC(rs232_port_device::write_rts));
	m_acia4->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<5>));

	rs232_port_device &serial1(RS232_PORT(config, "serial1", default_rs232_devices, nullptr));
	serial1.rxd_handler().set(m_acia1, FUNC(acia6850_device::write_rxd));
	serial1.cts_handler().set(m_acia1, FUNC(acia6850_device::write_cts));

	rs232_port_device &serial2(RS232_PORT(config, "serial2", default_rs232_devices, "terminal"));
	serial2.rxd_handler().set(m_acia2, FUNC(acia6850_device::write_rxd));
	serial2.cts_handler().set(m_acia2, FUNC(acia6850_device::write_cts));

	rs232_port_device &serial3(RS232_PORT(config, "serial3", default_rs232_devices, nullptr));
	serial3.rxd_handler().set(m_acia3, FUNC(acia6850_device::write_rxd));
	serial3.cts_handler().set(m_acia3, FUNC(acia6850_device::write_cts));

	rs232_port_device &serial4(RS232_PORT(config, "serial4", default_rs232_devices, nullptr));
	serial4.rxd_handler().set(m_acia4, FUNC(acia6850_device::write_rxd));
	serial4.cts_handler().set(m_acia4, FUNC(acia6850_device::write_cts));

	clock_device &acia_clock(CLOCK(config, "acia_clock", 9600 * 16));
	acia_clock.signal_handler().set(m_acia1, FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append(m_acia1, FUNC(acia6850_device::write_rxc));
	acia_clock.signal_handler().append(m_acia2, FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append(m_acia2, FUNC(acia6850_device::write_rxc));

	/* banking */
	for (int bank = 0; bank < 16; bank++)
	{
		ADDRESS_MAP_BANK(config, m_bank[bank]).set_map(&gimix_state::gimix_banked_mem).set_options(ENDIANNESS_LITTLE, 8, 32, 0x1000);
	}

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("128K").set_extra_options("56K,256K,512K");

	SOFTWARE_LIST(config, "flop_list").set_original("gimix");
}

ROM_START( gimix )
	ROM_REGION( 0x10000, "roms", 0)
/* CPU board U4: gimixf8.bin  - checksum 68DB - 2716 - GMXBUG09 V2.1 | (c)1981 GIMIX | $F800 I2716 */
	ROM_LOAD( "gimixf8.u4",  0x000000, 0x000800, CRC(7d60f838) SHA1(eb7546e8bbf50d33e181f3e86c3e4c5c9032cab2) )
/* CPU board U5: gimixv14.bin - checksum 97E2 - 2716 - GIMIX 6809 | AUTOBOOT | V1.4 I2716 */
	ROM_LOAD( "gimixv14.u5", 0x000800, 0x000800, CRC(f795b8b9) SHA1(eda2de51cc298d94b36605437d900ce971b3b276) )

	ROM_SYSTEM_BIOS(0, "os9l1v11", "OS9 Level 1 version 1.1")
/* CPU board U6: os9p1-l1v11.bin - checksum 2C84 - 2716 - OS-9tmL1 V1 | GIMIX P1 " (c)1982 MSC
   CPU board U7: os9p2-l1v11.bin - checksum 7694 - 2716 - OS-9tmL1 V1 | GIMIX P2-68 | (c)1982 MSC */
	ROMX_LOAD( "os9p1-l1v11.u6", 0x001000, 0x000800, CRC(0d6527a0) SHA1(1435a22581c6e9e0ae338071a72eed646f429530), ROM_BIOS(0))
	ROMX_LOAD( "os9p2-l1v11.u7", 0x001800, 0x000800, CRC(b3c65feb) SHA1(19d1ea1e84473b25c95cbb8449e6b9828567e998), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "os9l1v12", "OS9 Level 1 version 1.2")
	ROMX_LOAD( "os9p1-l1v12.u6", 0x001000, 0x000800, CRC(4de6e313) SHA1(b32cbc07418a147fd33a4404a5c2f68c25616c0d), ROM_BIOS(1))
	ROMX_LOAD( "os9p2-l1v12.u7", 0x001800, 0x000800, CRC(22f5f128) SHA1(8abf5cd2a52c0b8286f717f9ddf7feca61d1f46d), ROM_BIOS(1))

/* Hard drive controller board 2 (XEBEC board) 11H: gimixhd.bin - checksum 2436 - 2732 - 104521D */
	ROM_REGION( 0x10000, "xebec", 0)
	ROM_LOAD( "gimixhd.h11",  0x000000, 0x001000, CRC(35c12201) SHA1(51ac9052f9757d79c7f5bd3aa5d8421e98cfcc37) )
ROM_END

} // anonymous namespace


COMP( 1980, gimix, 0, 0, gimix, gimix, gimix_state, empty_init, "Gimix", "Gimix 6809 System", MACHINE_NO_SOUND_HW )



gizmondo.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Tim Schuerewegen
/*******************************************************************************

    Tiger Telematics Gizmondo

    (c) 2010 Tim Schuerewegen

*******************************************************************************/

/*

== DiskOnChip G3 - Layout ==

00000020 00000020 00000000 00000000 00000000 00000006 00000025 00000006 12A00000 000000FF 00000000 00000000 00000000 00000000 (BOOT & FTST)
00000140 00000140 00000000 00000000 00000000 00000026 00000165 00000026 12100000 000000FF 00000000 00000000 00000000 00000000 (KRNL)
00000001 00000001 00000000 00000000 00000000 00000166 00000166 00000166 12100000 000000FF 00000000 00000000 00000000 00000000
00000299 00000264 0000002F 00000003 00000003 0000016A 000003FF 00000167 1C100000 000000FF 00000000

== Windows CE - Interrupts ==

SYSINTR_KEYBOARD = INT_EINT1, INT_EINT4_7 (EINT4/EINT5/EINT6/EINT7, INT_EINT8_23 (EINT23)
SYSINTR_SERIAL_1 = INT_UART0 (SUBINT_ERR0, SUBINT_TXD0, SUBINT_RXD0), INT_EINT4_7 (EINT13)
SYSINTR_SERIAL_2 = INT_UART2 (SUBINT_ERR2, SUBINT_TXD2, SUBINT_RXD2)
SYSINTR_SERIAL_3 = INT_EINT8_23 (EINT17), ?
SYSINTR_POWER    = INT_EINT8_23 (EINT12)
SYSINTR_SYNTH    = INT_EINT8_23 (EINT16)
SYSINTR_GPS      = INT_EINT3, INT_EINT8_23 (EINT18)

*/

#include "emu.h"
#include "cpu/arm7/arm7.h"
#include "docg3.h"
#include "machine/s3c2440.h"
#include "video/gf4500.h"
#include "screen.h"


namespace {

#define BITS(x,m,n) (((x)>>(n))&(((uint32_t)1<<((m)-(n)+1))-1))

class gizmondo_state : public driver_device
{
public:
	gizmondo_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag) ,
		m_s3c2440(*this, "s3c2440"),
		m_maincpu(*this, "maincpu"),
		m_gf4500(*this, "gf4500")
		{ }

	void gizmondo(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(port_changed);

	void init_gizmondo();

private:
	uint32_t m_port[9];
	required_device<s3c2440_device> m_s3c2440;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_device<gf4500_device> m_gf4500;
	uint32_t s3c2440_gpio_port_r(offs_t offset);
	void s3c2440_gpio_port_w(offs_t offset, uint32_t data);

	bitmap_rgb32 m_bitmap;
	void gizmondo_map(address_map &map) ATTR_COLD;
};


/*******************************************************************************
    ...
*******************************************************************************/

// I/O PORT

uint32_t gizmondo_state::s3c2440_gpio_port_r(offs_t offset)
{
	uint32_t data = m_port[offset];
	switch (offset)
	{
		case S3C2440_GPIO_PORT_D :
		{
			data = data & ~0x00000010;
			data |= (1 << 4); // 1 = "BOOT", 0 = "FTST"
		}
		break;
		case S3C2440_GPIO_PORT_F :
		{
			uint32_t port_c = m_port[S3C2440_GPIO_PORT_C];
			data = data & ~0x000000F2;
			// keys
			data |= 0x00F2;
			if ((port_c & 0x01) == 0) data &= ~ioport("PORTF-01")->read();
			if ((port_c & 0x02) == 0) data &= ~ioport("PORTF-02")->read();
			if ((port_c & 0x04) == 0) data &= ~ioport("PORTF-04")->read();
			if ((port_c & 0x08) == 0) data &= ~ioport("PORTF-08")->read();
			if ((port_c & 0x10) == 0) data &= ~ioport("PORTF-10")->read();
			data &= ~ioport( "PORTF")->read();
		}
		break;
		case S3C2440_GPIO_PORT_G :
		{
			data = data & ~0x00008001;
			// keys
			data = data | 0x8000;
			data &= ~ioport( "PORTG")->read();
			// no sd card inserted
			data = data | 0x0001;
		}
		break;
	}
	return data;
}

void gizmondo_state::s3c2440_gpio_port_w(offs_t offset, uint32_t data)
{
	m_port[offset] = data;
}

INPUT_CHANGED_MEMBER(gizmondo_state::port_changed)
{
	m_s3c2440->s3c2440_request_eint(4);
	//m_s3c2440->s3c2440_request_irq(S3C2440_INT_EINT1);
}

#if 0
QUICKLOAD_LOAD_MEMBER(gizmondo_state::quickload_cb)
{
	return gizmondo_quickload(image, file_type, quickload_size, 0x3000E000); // eboot
	//return gizmondo_quickload(image, file_type, quickload_size, 0x30400000); // wince
}
#endif

/*******************************************************************************
    MACHINE HARDWARE
*******************************************************************************/

void gizmondo_state::machine_start()
{
	m_port[S3C2440_GPIO_PORT_B] = 0x055E;
	m_port[S3C2440_GPIO_PORT_C] = 0x5F20;
	m_port[S3C2440_GPIO_PORT_D] = 0x4F60;
}

void gizmondo_state::machine_reset()
{
	m_maincpu->reset();
}

/*******************************************************************************
    ADDRESS MAPS
*******************************************************************************/

void gizmondo_state::gizmondo_map(address_map &map)
{
	map(0x00000000, 0x000007ff).rom();
	map(0x00000800, 0x00000fff).rw("diskonchip", FUNC(diskonchip_g3_device::sec_1_r), FUNC(diskonchip_g3_device::sec_1_w));
	map(0x00001000, 0x000017ff).rw("diskonchip", FUNC(diskonchip_g3_device::sec_2_r), FUNC(diskonchip_g3_device::sec_2_w));
	map(0x00001800, 0x00001fff).rw("diskonchip", FUNC(diskonchip_g3_device::sec_3_r), FUNC(diskonchip_g3_device::sec_3_w));
	map(0x30000000, 0x33ffffff).ram();
	map(0x34000000, 0x3413ffff).rw(m_gf4500, FUNC(gf4500_device::read), FUNC(gf4500_device::write));
}

/*******************************************************************************
    MACHINE DRIVERS
*******************************************************************************/

void gizmondo_state::init_gizmondo()
{
	// do nothing
}

void gizmondo_state::gizmondo(machine_config &config)
{
	ARM9(config, m_maincpu, 40000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &gizmondo_state::gizmondo_map);

	PALETTE(config, "palette").set_entries(32768);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(320, 240);
	screen.set_visarea_full();
	screen.set_screen_update("gf4500", FUNC(gf4500_device::screen_update));

	GF4500(config, m_gf4500, 0);

	S3C2440(config, m_s3c2440, 12000000);
	m_s3c2440->set_palette_tag("palette");
	m_s3c2440->set_screen_tag("screen");
	m_s3c2440->gpio_port_r_callback().set(FUNC(gizmondo_state::s3c2440_gpio_port_r));
	m_s3c2440->gpio_port_w_callback().set(FUNC(gizmondo_state::s3c2440_gpio_port_w));

	DISKONCHIP_G3(config, "diskonchip").set_size(64);

#if 0
	QUICKLOAD(config, "quickload", "bin", 0).set_load_callback(FUNC(gizmondo_state::quickload_cb), this);
#endif
}

static INPUT_PORTS_START( gizmondo )
	PORT_START( "PORTF-01" )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gizmondo_state::port_changed), 0) PORT_NAME("STOP") PORT_PLAYER(1)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gizmondo_state::port_changed), 0) PORT_PLAYER(1)
	PORT_START( "PORTF-02" )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON8 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gizmondo_state::port_changed), 0) PORT_NAME("F2") PORT_PLAYER(1) PORT_CODE(KEYCODE_F2)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gizmondo_state::port_changed), 0) PORT_NAME("FORWARD") PORT_PLAYER(1)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gizmondo_state::port_changed), 0) PORT_PLAYER(1)
	PORT_START( "PORTF-04" )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON9 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gizmondo_state::port_changed), 0) PORT_NAME("F3") PORT_PLAYER(1) PORT_CODE(KEYCODE_F3)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gizmondo_state::port_changed), 0) PORT_NAME("PLAY") PORT_PLAYER(1)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gizmondo_state::port_changed), 0) PORT_PLAYER(1)
	PORT_START( "PORTF-08" )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON10 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gizmondo_state::port_changed), 0) PORT_NAME("F4") PORT_PLAYER(1) PORT_CODE(KEYCODE_F4)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gizmondo_state::port_changed), 0) PORT_NAME("REWIND") PORT_PLAYER(1)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gizmondo_state::port_changed), 0) PORT_PLAYER(1)
	PORT_START( "PORTF-10" )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gizmondo_state::port_changed), 0) PORT_NAME("L") PORT_PLAYER(1) PORT_CODE(KEYCODE_L)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gizmondo_state::port_changed), 0) PORT_NAME("R") PORT_PLAYER(1) PORT_CODE(KEYCODE_R)
	PORT_START( "PORTF" )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON11 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gizmondo_state::port_changed), 0) PORT_NAME("F5") PORT_PLAYER(1) PORT_CODE(KEYCODE_F5)
	PORT_START( "PORTG" )
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gizmondo_state::port_changed), 0) PORT_NAME("F1") PORT_PLAYER(1) PORT_CODE(KEYCODE_F1)
INPUT_PORTS_END

/*******************************************************************************
    GAME DRIVERS
*******************************************************************************/

ROM_START( gizmondo )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "fboot", "fboot" )
	ROMX_LOAD( "fboot.bin", 0, 0x800, CRC(28887c29) SHA1(e625caaa63b9db74cb6d7499dce12ac758c5fe76), ROM_BIOS(0) )
ROM_END

} // anonymous namespace


CONS(2005, gizmondo, 0, 0, gizmondo, gizmondo, gizmondo_state, init_gizmondo, "Tiger Telematics", "Gizmondo", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



gk2000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle, Berger
/*******************************************************************************

Saitek GK 2000 / GK 2100 / Centurion

These chess computers all have the same I/O and fit in the same driver. The chess
engine is by Frans Morsch.

TODO:
- versions with the A20 ROM that don't officially support the extra options on
  the 2nd row, can still access them when turning the computer on by simultaneously
  pressing the Go/Stop button with the Option button. This doesn't work on MAME,
  something with MCU standby stabilisation maybe?
- it does a cold boot at every reset, so nvram won't work properly unless MAME
  adds some kind of auxillary autosave state feature at power-off

BTANB:
- see note below about so-called H8 bug on the A20 ROM

================================================================================

Saitek GK 2000 family
---------------------

Hardware notes:

GK 2000 (H8/323 version):
- PCB label: ST12-PE-009 REV1
- Hitachi H8/323 MCU, 20MHz XTAL
- LCD with 5 7segs and custom segments
- piezo, 16 LEDs, button sensors chessboard

Barracuda:
- PCB label: ST39B-PE-013, P/N: 51K090-01310
- Hitachi H8/3212 MCU, 10MHz XTAL
- rest is same as GK 2000

Radio Shack Chess Master:
- PCB label: same as Centurion / Cougar (see below)
- rest is same as Barracuda

Mephisto Explorer:
- PCB label: 51CT09-01001L
- Hitachi H8/3212 MCU (QFP), 10MHz XTAL, same ROM contents as Barracuda
- LCD layout is slightly different, symbols are on the right side
- no board edge LEDs, LCD backlight via P51

H8/323 A13 MCU is used in:
- Saitek GK 2000 (1992 version, 86071220X12)
- Saitek Travel Champion 2080 (86071220X12)
- Saitek Mephisto Mythos (86142221X34)
- Tandy (Radio Shack) Mega 2050X (86071221X12)
- Tandy (Radio Shack) Master 2200X (suspected)

Travel Champion 2080 and Tandy Mega 2050X are 14MHz instead of 20MHz.

H8/3212 V03 MCU (DIP) is used in:
- Saitek GK 2000 (1997 version, suspected)
- Saitek Barracuda
- Saitek Mephisto Montana
- Tandy (Radio Shack) Chess Master (aka Master Chess Computer)

GK 2000 was still sold in 1997. Just like the latest version of Turbo Advanced
Trainer was fitted with a H8/3212 (see tatrain.cpp), it is assumed Saitek did
the same with GK 2000.

H8/3212 V04 MCU (QFP) is used in:
- Saitek Mephisto Miami
- Saitek Mephisto Diplomat Advanced Travel Chess (suspected)
- Saitek Mephisto Explorer

The V04 QFP MCU is confirmed to have the same ROM contents as the V03 DIP MCU.

================================================================================

Saitek GK 2100
--------------

Hardware notes:
- PCB label: ST12-PE-009 REV1 (same as GK 2000)
- Hitachi H8/325 MCU, 20MHz XTAL
- rest is same as GK 2000

H8/325 B40 MCU is used in:
- Saitek GK 2100
- Saitek Travel Champion 2100

================================================================================

Saitek Centurion family
-----------------------

This is the program with the infamous H8 bug, not named after the MCU, but after
the H8 square. The piece on H8 is moved immediately, regardless of playing level,
often resulting in blunders.

Hardware notes:

Centurion / Cougar:
- PCB label: ST42C/39A LOGIC PCB, SAITEK LTD, PN 51A125-01113 VER 3.0,
  SCH ST42C-PE-000 REV 1.0, SCH ST39A-PE-002 VER 1.0
- Hitachi H8/3214 MCU, configuration diodes for XTAL a.o.
- same LCD and board hardware as GK 2000

Mephisto Explorer Pro:
- PCB label: 51CT12-01002, REV1.0 (smaller PCB)
- same MCU as Cougar, 16MHz XTAL
- LCD layout is slightly different (same as Mephisto Explorer)

For test mode, hold Enter after cold boot during the LCD test. It will say "TST",
press Enter again to see the diode configuration setting.

S = Studies button (if absent, this input functions as new game)
t = Teach mode (press a Symbol key after pressing Level)
L = LEDs enabled
n = 0/2/4/6 10+n MHz XTAL
b = Playing mode options on the 2nd row

All known chess computers with the A20 ROM have LEDs, and the S/t/b options are
configured as either St, or b. So, none of them that have the Studies button and
teach mode officially support the extra options and vice versa.

H8/3214 A20 MCU is used in:
- Saitek Centurion (config: StL0_)
- Saitek Cosmos (config: __L0b)
- Saitek Cougar (config: __L6b)
- Saitek Mephisto Chess Challenger (config: StL0_)
- Saitek Mephisto Explorer Pro (config: StL6_)
- Saitek Mephisto Expert Travel Chess (config: __L0b)
- Saitek Mephisto Mystery (config: __L2b)

*******************************************************************************/

#include "emu.h"

#include "cpu/h8/h83217.h"
#include "cpu/h8/h8325.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "saitek_centurion.lh"
#include "saitek_cougar.lh"
#include "saitek_gk2000.lh"
#include "saitek_gk2100.lh"


namespace {

class gk2000_state : public driver_device
{
public:
	gk2000_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_led_pwm(*this, "led_pwm"),
		m_lcd_pwm(*this, "lcd_pwm"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_out_lcd(*this, "s%u.%u", 0U, 0U)
	{ }

	template <typename T> void cpu_config(T &maincpu);
	void shared(machine_config &config);
	void gk2000(machine_config &config);
	void gk2000a(machine_config &config);
	void gk2100(machine_config &config);
	void centurion(machine_config &config);
	void cougar(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(go_button);
	DECLARE_INPUT_CHANGED_MEMBER(gk2000a_change_cpu_freq);
	DECLARE_INPUT_CHANGED_MEMBER(centurion_change_cpu_freq);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<h8_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_led_pwm;
	required_device<pwm_display_device> m_lcd_pwm;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<4> m_inputs;
	output_finder<2, 24> m_out_lcd;

	u16 m_inp_mux = 0;
	u32 m_lcd_segs = 0;
	u8 m_lcd_com = 0;

	// I/O handlers
	void standby(int state);

	void lcd_pwm_w(offs_t offset, u8 data);
	void update_lcd();
	template <int N> void lcd_segs_w(u8 data);
	void lcd_com_w(u8 data);

	void p2_w(u8 data);
	u8 p4_r();
	void p5_w(u8 data);
};



/*******************************************************************************
    Initialization
*******************************************************************************/

void gk2000_state::machine_start()
{
	m_out_lcd.resolve();

	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_lcd_segs));
	save_item(NAME(m_lcd_com));
}

INPUT_CHANGED_MEMBER(gk2000_state::gk2000a_change_cpu_freq)
{
	// only 20MHz and 14MHz versions are known to exist, but the software supports others
	static const int xm[9] = { 8, 20, 24, 28, 32, -1, -1, -1, 14 }; // XTAL in MHz (-1 is invalid)
	int mhz = xm[(count_leading_zeros_32(bitswap<8>(newval,0,1,2,3,4,5,6,7)) - 24) % 9];

	if (mhz > 0)
		m_maincpu->set_unscaled_clock(mhz * 1'000'000);
}

INPUT_CHANGED_MEMBER(gk2000_state::centurion_change_cpu_freq)
{
	// 14MHz version doesn't exist, but the software supports it
	static const XTAL freq[4] = { 12_MHz_XTAL, 16_MHz_XTAL, 14_MHz_XTAL, 10_MHz_XTAL };
	m_maincpu->set_unscaled_clock(freq[newval & 3]);
}



/*******************************************************************************
    I/O
*******************************************************************************/

// power

void gk2000_state::standby(int state)
{
	// clear display
	if (state)
	{
		m_lcd_pwm->clear();
		m_led_pwm->clear();
	}
}

INPUT_CHANGED_MEMBER(gk2000_state::go_button)
{
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, newval ? ASSERT_LINE : CLEAR_LINE);
}


// LCD

void gk2000_state::lcd_pwm_w(offs_t offset, u8 data)
{
	m_out_lcd[offset & 0x3f][offset >> 6] = data;
}

void gk2000_state::update_lcd()
{
	for (int i = 0; i < 2; i++)
	{
		// LCD common is analog (voltage level)
		const u8 com = population_count_32(m_lcd_com >> (i * 2) & 3);
		const u32 data = (com == 0) ? m_lcd_segs : (com == 2) ? ~m_lcd_segs : 0;
		m_lcd_pwm->write_row(i, data);
	}
}

template <int N>
void gk2000_state::lcd_segs_w(u8 data)
{
	// P1x, P3x, P7x: LCD segments
	const u8 shift = 8 * N;
	m_lcd_segs = (m_lcd_segs & ~(0xff << shift)) | (data << shift);
	update_lcd();
}

void gk2000_state::lcd_com_w(u8 data)
{
	// P60-P63: LCD common
	m_lcd_com = data & 0xf;
	update_lcd();
}


// misc

void gk2000_state::p2_w(u8 data)
{
	// P20-P27: input mux (chessboard), led data
	m_inp_mux = (m_inp_mux & 0x700) | (data ^ 0xff);
	m_led_pwm->write_mx(~data);
}

u8 gk2000_state::p4_r()
{
	// P40-P47: multiplexed inputs
	u8 data = 0;

	// read buttons
	for (int i = 0; i < 3; i++)
		if (BIT(m_inp_mux, i + 8))
			data |= m_inputs[i]->read();

	// read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_rank(i);

	return ~data;
}

void gk2000_state::p5_w(u8 data)
{
	// P50: speaker out
	m_dac->write(data & 1);

	// P51,P52: led select
	m_led_pwm->write_my(~data >> 1 & 3);

	// P53-P55: input mux (buttons)
	m_inp_mux = (m_inp_mux & 0xff) | (~data << 5 & 0x700);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( gk2000 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("Position")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Option")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Info")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Clear")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_CODE(KEYCODE_LEFT) PORT_NAME("White / Left")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Black / Right")

	PORT_START("IN.2")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.3")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gk2000_state::go_button), 0) PORT_NAME("Go / Stop")
	PORT_BIT(0xef, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( gk2000a )
	PORT_INCLUDE( gk2000 )

	PORT_MODIFY("IN.2")
	PORT_CONFNAME( 0xff, 0x02, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gk2000_state::gk2000a_change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x01, "8MHz (unofficial)" )
	PORT_CONFSETTING(    0x00, "14MHz (Travel Champion 2080)" )
	PORT_CONFSETTING(    0x02, "20MHz (GK 2000, GK 2100)" )
	PORT_CONFSETTING(    0x04, "24MHz (unofficial)" )
	PORT_CONFSETTING(    0x08, "28MHz (unofficial)" )
	PORT_CONFSETTING(    0x10, "32MHz (unofficial)" )
INPUT_PORTS_END

static INPUT_PORTS_START( gk2100 )
	PORT_INCLUDE( gk2000a )

	PORT_MODIFY("IN.1")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("White / -")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_EQUALS) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("Black / +")
INPUT_PORTS_END

static INPUT_PORTS_START( cougar )
	PORT_INCLUDE( gk2000 )

	PORT_MODIFY("IN.2") // configuration diodes
	PORT_BIT(0x27, 0x00, IPT_CUSTOM) // __Lnb
	PORT_CONFNAME( 0x18, 0x08, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gk2000_state::centurion_change_cpu_freq), 0)
	PORT_CONFSETTING(    0x18, "10MHz (Centurion, Cosmos)" )
	PORT_CONFSETTING(    0x00, "12MHz (Mephisto Mystery)" )
	PORT_CONFSETTING(    0x10, "14MHz (unofficial)" )
	PORT_CONFSETTING(    0x08, "16MHz (Cougar, Mephisto Explorer Pro)" )
INPUT_PORTS_END

static INPUT_PORTS_START( centurion )
	PORT_INCLUDE( cougar )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Studies")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_F1) PORT_NAME("Clear")   // combine for NEW GAME
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CODE(KEYCODE_F1) PORT_NAME("Enter") // "

	PORT_MODIFY("IN.2") // change defaults
	PORT_BIT(0x27, 0x23, IPT_CUSTOM) // StLn_
	PORT_CONFNAME( 0x18, 0x18, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gk2000_state::centurion_change_cpu_freq), 0)
	PORT_CONFSETTING(    0x18, "10MHz (Centurion, Cosmos)" )
	PORT_CONFSETTING(    0x00, "12MHz (Mephisto Mystery)" )
	PORT_CONFSETTING(    0x10, "14MHz (unofficial)" )
	PORT_CONFSETTING(    0x08, "16MHz (Cougar, Mephisto Explorer Pro)" )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

template <typename T>
void gk2000_state::cpu_config(T &maincpu)
{
	maincpu.nvram_enable_backup(true);
	maincpu.standby_cb().set(maincpu, FUNC(T::nvram_set_battery));
	maincpu.standby_cb().append(FUNC(gk2000_state::standby));
	maincpu.write_port1().set(FUNC(gk2000_state::lcd_segs_w<0>));
	maincpu.write_port2().set(FUNC(gk2000_state::p2_w));
	maincpu.write_port3().set(FUNC(gk2000_state::lcd_segs_w<1>));
	maincpu.read_port4().set(FUNC(gk2000_state::p4_r));
	maincpu.read_port5().set_constant(0xff);
	maincpu.write_port5().set(FUNC(gk2000_state::p5_w));
	maincpu.read_port6().set_ioport("IN.3").invert();
	maincpu.write_port6().set(FUNC(gk2000_state::lcd_com_w));
	maincpu.write_port7().set(FUNC(gk2000_state::lcd_segs_w<2>));
}

void gk2000_state::shared(machine_config &config)
{
	// basic machine hardware
	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	//m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_lcd_pwm).set_size(2, 24);
	m_lcd_pwm->output_x().set(FUNC(gk2000_state::lcd_pwm_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/5, 804/5);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_led_pwm).set_size(2, 8);
	config.set_default_layout(layout_saitek_gk2000);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void gk2000_state::gk2000(machine_config &config)
{
	H83212(config, m_maincpu, 10_MHz_XTAL);
	cpu_config<h83212_device>(downcast<h83212_device &>(*m_maincpu));

	shared(config);
}

void gk2000_state::gk2000a(machine_config &config)
{
	H8323(config, m_maincpu, 20_MHz_XTAL);
	cpu_config<h8323_device>(downcast<h8323_device &>(*m_maincpu));

	shared(config);
}

void gk2000_state::gk2100(machine_config &config)
{
	H8325(config, m_maincpu, 20_MHz_XTAL);
	cpu_config<h8325_device>(downcast<h8325_device &>(*m_maincpu));

	shared(config);

	config.set_default_layout(layout_saitek_gk2100);
}

void gk2000_state::centurion(machine_config &config)
{
	H83214(config, m_maincpu, 10_MHz_XTAL);
	cpu_config<h83214_device>(downcast<h83214_device &>(*m_maincpu));

	shared(config);

	config.set_default_layout(layout_saitek_centurion);
}

void gk2000_state::cougar(machine_config &config)
{
	centurion(config);
	m_maincpu->set_clock(16_MHz_XTAL);

	config.set_default_layout(layout_saitek_cougar);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( gk2000 )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("97_saitek_86164430826_hd6433212v03p.u1", 0x0000, 0x4000, CRC(220c80d4) SHA1(f1df2d04afeffec0a0a66036dfa2c4dcb85ae8f2) )

	ROM_REGION( 68501, "screen", 0 )
	ROM_LOAD("gk2000.svg", 0, 68501, CRC(80554c49) SHA1(88f06ec8f403eaaf7cbce4cc84807b5742ce7108) )
ROM_END

ROM_START( gk2000a )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("92_saitek_86071220x12_3238a13p.u1", 0x0000, 0x4000, CRC(2059399c) SHA1(d99d5f86b80565e6017b19ef3f330112ac1ce685) )

	ROM_REGION( 68501, "screen", 0 )
	ROM_LOAD("gk2000.svg", 0, 68501, CRC(80554c49) SHA1(88f06ec8f403eaaf7cbce4cc84807b5742ce7108) )
ROM_END

ROM_START( gk2100 )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD("94_saitek_86100150110_3258b40p.u1", 0x0000, 0x8000, CRC(33823df6) SHA1(df528bbbf5eed985d05ced07fcb8f1cfb91a9f1b) )

	ROM_REGION( 68501, "screen", 0 )
	ROM_LOAD("gk2000.svg", 0, 68501, CRC(80554c49) SHA1(88f06ec8f403eaaf7cbce4cc84807b5742ce7108) )
ROM_END

ROM_START( centurion )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD("98_saitek_86171400305_hd6433214a20p.u1", 0x0000, 0x8000, CRC(31e35d22) SHA1(92cc3d90fc4e33f9634c0229fdb339dd0d8c5133) )

	ROM_REGION( 68501, "screen", 0 )
	ROM_LOAD("gk2000.svg", 0, 68501, CRC(80554c49) SHA1(88f06ec8f403eaaf7cbce4cc84807b5742ce7108) )
ROM_END

#define rom_cougar rom_centurion

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT     COMPAT  MACHINE     INPUT      CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1997, gk2000,    0,         0,      gk2000,     gk2000,    gk2000_state, empty_init, "Saitek", "Kasparov GK 2000 (H8/3212 version)", MACHINE_SUPPORTS_SAVE )
SYST( 1992, gk2000a,   gk2000,    0,      gk2000a,    gk2000a,   gk2000_state, empty_init, "Saitek", "Kasparov GK 2000 (H8/323 version)", MACHINE_SUPPORTS_SAVE )

SYST( 1994, gk2100,    0,         0,      gk2100,     gk2100,    gk2000_state, empty_init, "Saitek", "Kasparov GK 2100", MACHINE_SUPPORTS_SAVE )

SYST( 1998, centurion, 0,         0,      centurion,  centurion, gk2000_state, empty_init, "Saitek", "Kasparov Centurion", MACHINE_SUPPORTS_SAVE )
SYST( 1998, cougar,    centurion, 0,      cougar,     cougar,    gk2000_state, empty_init, "Saitek", "Kasparov Cougar", MACHINE_SUPPORTS_SAVE )



gkidabc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************************

    Genius KID ABC Fan
    Mis Primeras Lecciones
    Genius Junior Profi

    Other known undumped international versions:
    - Smart Start Elite (English version of Genius Junior Profi / Mis Primeras Lecciones)

    TODO: identify CPU type (16-bit processor internally, but with 8-bit external bus?)
    It might be that the dumped ROMs contain no actual code, only graphics data and
    sound samples.

***************************************************************************************/

#include "emu.h"


namespace {

class gkidabc_state : public driver_device
{
public:
	gkidabc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
	{
	}

	void gkidabc(machine_config &config);
};


static INPUT_PORTS_START(gkidabc)
INPUT_PORTS_END

void gkidabc_state::gkidabc(machine_config &config)
{
}


ROM_START(gkidabc)
	ROM_REGION(0x20000, "maincpu", 0)
	ROM_LOAD("27-5730-00.bin", 0x00000, 0x20000, CRC(64664708) SHA1(74212c2dec1caa41dbc933b50f857904a8ac623b))
ROM_END

ROM_START(miprimlec)
	ROM_REGION(0x20000, "maincpu", 0)
	ROM_LOAD("27-5482-01.u1", 0x00000, 0x20000, CRC(83aa655b) SHA1(5d7b03f0ff2836e228da77676df03854f87edd26))

	ROM_REGION( 0x2000, "speech", 0 )
	ROM_LOAD( "csm10150an.u3", 0x0000, 0x2000, NO_DUMP ) // TSP50C10 (8K bytes of ROM) labeled "67ACLKT VIDEO TECH CSM10150AN"
ROM_END

ROM_START(gjrprofi)
	ROM_REGION(0x20000, "maincpu", 0)
	ROM_LOAD("27-5476-00.u1", 0x00000, 0x20000, CRC(ad1ec838) SHA1(0cf90c02762ace656191a38ae423a4fa0e7484f7))
ROM_END

} // anonymous namespace


COMP(1996, gkidabc,   0, 0, gkidabc, gkidabc, gkidabc_state, empty_init, "VTech", "Genius KID ABC Fan (Germany)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP(1995, miprimlec, 0, 0, gkidabc, gkidabc, gkidabc_state, empty_init, "VTech", "Mis Primeras Lecciones (Spain)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP(1995, gjrprofi,  0, 0, gkidabc, gkidabc, gkidabc_state, empty_init, "VTech", "Genius Junior Profi (Germany)",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



glasgow.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Dirk Verwiebe, Cowering, hap
// thanks-to:Berger
/*******************************************************************************

Mephisto III-S Glasgow chess computer
Dirk V.
sp_rinter@gmx.de

Mephisto Glasgow is the last chess engine written by Thomas Nitsche & Elmar Henne.
Amsterdam/Dallas/Roma are by Richard Lang.

Hardware notes:
- R68000C10 or MC68000P12 @ 12MHz, IRQ from 50Hz Seiko SG-10 chip "50H", to IPL0+2
- 64KB ROM (4*27128), 16KB RAM (2*6264)
- LCD module same as MMx series, piezo

Other TTL:

3*74LS138 Decoder/Multiplexer
1*74LS74  Dual positive edge triggered D Flip Flop
1*74LS139 1of4 Demultiplexer
1*74LS05  Hex Inverter
1*NE555   R=100K C=10uF
2*74LS04  Hex Inverter
1*74LS164 8 Bit Shift register
1*74121   Monostable Multivibrator with Schmitt Trigger Inputs
1*74LS20  Dual 4 Input NAND GAte
1*74LS367 3 State Hex Buffers

By default, it makes heavy use of DTACK wait states. Overall it runs much slower
than 12MHz. The LDS/UDS wait states can be modified with solder pads on the
backside of the PCB (under the 74LS164).

To verify CPU speed: Set level to 9 and move pawn to F3. At exactly 6 minutes,
a real Glasgow with 1 LDS/UDS wait state will have calculated 3432 positions
(may fluctuate a little due to 74121). To see number of calculated positions,
press INFO, C, then Right 3 times.

*******************************************************************************/

#include "emu.h"

#include "mmboard.h"
#include "mmdisplay1.h"

#include "cpu/m68000/m68000.h"
#include "sound/dac.h"

#include "speaker.h"

// internal artwork
#include "mephisto_glasgow.lh"


namespace {

class glasgow_state : public driver_device
{
public:
	glasgow_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_keys(*this, "KEY.%u", 0),
		m_wait(*this, "WAIT")
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(wait_changed) { install_wait(); }

	void glasgow(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD { install_wait(); }
	virtual void device_post_load() override { install_wait(); }

private:
	required_device<cpu_device> m_maincpu;
	required_device<mephisto_board_device> m_board;
	required_device<mephisto_display1_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<2> m_keys;
	required_ioport m_wait;

	memory_passthrough_handler m_read_tap;
	memory_passthrough_handler m_write_tap;

	u8 m_kp_mux = 0;
	int m_wait_ticks = 0;
	int m_ext_ticks = 0;

	void glasgow_mem(address_map &map) ATTR_COLD;

	void install_wait();

	void control_w(u8 data);
	u8 keys_r();
	void keys_w(u8 data);
};



/*******************************************************************************
    Initialization
*******************************************************************************/

void glasgow_state::machine_start()
{
	save_item(NAME(m_kp_mux));

	// on external access (0x10000-0x17fff), additional wait states via a 74121
	// (R=internal 2K, C=10nf, which is 20us according to datasheet)
	attotime ext_delay = attotime::from_usec(20);
	m_ext_ticks = ext_delay.as_ticks(m_maincpu->clock());

	address_space &space = m_maincpu->space(AS_PROGRAM);

	space.install_read_tap(
			0x10000, 0x17fff,
			"maincpu_ext_r",
			[this] (offs_t offset, u16 &data, u16 mem_mask)
			{
				if (!machine().side_effects_disabled())
					m_maincpu->adjust_icount(-m_ext_ticks);
			});
	space.install_write_tap(
			0x10000, 0x17fff,
			"maincpu_ext_w",
			[this] (offs_t offset, u16 &data, u16 mem_mask)
			{
				if (!machine().side_effects_disabled())
					m_maincpu->adjust_icount(-m_ext_ticks);
			});
}

void glasgow_state::install_wait()
{
	m_read_tap.remove();
	m_write_tap.remove();

	// optional 0-3 wait states via 74LS164 for each LDS/UDS
	m_wait_ticks = m_wait->read() & 3;

	if (m_wait_ticks)
	{
		address_space &program = m_maincpu->space(AS_PROGRAM);

		m_read_tap = program.install_read_tap(
				0x00000, 0x1ffff,
				"maincpu_wait_r",
				[this] (offs_t offset, u16 &data, u16 mem_mask)
				{
					if (!machine().side_effects_disabled())
						m_maincpu->adjust_icount(-m_wait_ticks);
				},
				&m_read_tap);
		m_write_tap = program.install_write_tap(
				0x00000, 0x1ffff,
				"maincpu_wait_w",
				[this] (offs_t offset, u16 &data, u16 mem_mask)
				{
					if (!machine().side_effects_disabled())
						m_maincpu->adjust_icount(-m_wait_ticks);
				},
				&m_write_tap);
	}
}



/*******************************************************************************
    I/O
*******************************************************************************/

void glasgow_state::control_w(u8 data)
{
	// d0: speaker out
	m_dac->write(BIT(data, 0));

	// d7: lcd common
	m_display->common_w(BIT(data, 7));
}

u8 glasgow_state::keys_r()
{
	u8 data = 0;

	// d0,d1: multiplexed inputs
	for (int i = 0; i < 2; i++)
		if (m_kp_mux & m_keys[i]->read())
			data |= 1 << i;

	// reading keypad also clears irq
	if (!machine().side_effects_disabled())
		m_maincpu->set_input_line(M68K_IRQ_5, CLEAR_LINE);

	return ~data;
}

void glasgow_state::keys_w(u8 data)
{
	m_kp_mux = ~data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void glasgow_state::glasgow_mem(address_map &map)
{
	map.global_mask(0x1ffff);
	map(0x000000, 0x00ffff).rom();
	map(0x010000, 0x010001).mirror(0x007ff0).w(m_display, FUNC(mephisto_display1_device::data_w)).umask16(0xff00);
	map(0x010002, 0x010003).mirror(0x007ff0).rw(FUNC(glasgow_state::keys_r), FUNC(glasgow_state::keys_w)).umask16(0xff00);
	map(0x010004, 0x010005).mirror(0x007ff0).w(FUNC(glasgow_state::control_w)).umask16(0xff00);
	map(0x010006, 0x010007).mirror(0x007ff0).rw(m_board, FUNC(mephisto_board_device::input_r), FUNC(mephisto_board_device::led_w)).umask16(0xff00);
	map(0x010008, 0x010009).mirror(0x007ff0).w(m_board, FUNC(mephisto_board_device::mux_w)).umask16(0xff00);
	map(0x01c000, 0x01ffff).ram();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( glasgow )
	PORT_START("KEY.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("E / 5 / Rook") PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("INFO") PORT_CODE(KEYCODE_I)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Right / White / 0") PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("POS") PORT_CODE(KEYCODE_P)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("H / 8") PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("LEV") PORT_CODE(KEYCODE_L)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("G / 7 / King") PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("MEM") PORT_CODE(KEYCODE_M)

	PORT_START("KEY.1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Left / Black / 9") PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_LEFT)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("CL") PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("C / 3 / Knight") PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("ENT") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("D / 4 / Bishop") PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("A / 1") PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("F / 6 / Queen") PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("B / 2 / Pawn") PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)

	PORT_START("WAIT") // hardwired, default to 1
	PORT_CONFNAME( 0x03, 0x01, "LDS/UDS Wait States" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(glasgow_state::wait_changed), 0)
	PORT_CONFSETTING(    0x00, "None (12MHz)" )
	PORT_CONFSETTING(    0x01, "1 (~9.5MHz)" )
	PORT_CONFSETTING(    0x02, "2 (~8MHz)" )
	PORT_CONFSETTING(    0x03, "3 (~7Mhz)" )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void glasgow_state::glasgow(machine_config &config)
{
	// basic machine hardware
	M68000(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_periodic_int(FUNC(glasgow_state::irq5_line_assert), attotime::from_hz(50));
	m_maincpu->set_addrmap(AS_PROGRAM, &glasgow_state::glasgow_mem);

	MEPHISTO_SENSORS_BOARD(config, m_board);
	m_board->set_delay(attotime::from_msec(200));

	// video hardware
	MEPHISTO_DISPLAY_MODULE1(config, m_display);
	config.set_default_layout(layout_mephisto_glasgow);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( glasgow )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("me3_3_1u.410", 0x00000, 0x04000, CRC(bc8053ba) SHA1(57ea2d5652bfdd77b17d52ab1914de974bd6be12) )
	ROM_LOAD16_BYTE("me3_1_1l.410", 0x00001, 0x04000, CRC(d5263c39) SHA1(1bef1cf3fd96221eb19faecb6ec921e26ac10ac4) )
	ROM_LOAD16_BYTE("me3_4_2u.410", 0x08000, 0x04000, CRC(8dba504a) SHA1(6bfab03af835cdb6c98773164d32c76520937efe) )
	ROM_LOAD16_BYTE("me3_2_2l.410", 0x08001, 0x04000, CRC(b3f27827) SHA1(864ba897d24024592d08c4ae090aa70a2cc5f213) )
ROM_END


ROM_START( amsterdg )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("vr_1_1", 0x00000, 0x04000, CRC(a186cc81) SHA1(903f93243536de3c2778ba3d38dcf46ae568862d) )
	ROM_LOAD16_BYTE("vl_2_1", 0x00001, 0x04000, CRC(9b326226) SHA1(1b29319643d63a43ac84c1af08e02a4fc4fc6ffa) )
	ROM_LOAD16_BYTE("br_3_1", 0x08000, 0x04000, CRC(372fd7fe) SHA1(c7c11796450fe202e9641170cd0625461cee24af) )
	ROM_LOAD16_BYTE("bl_4_1", 0x08001, 0x04000, CRC(533e584a) SHA1(0e4510977dc627125c278920492bc137793a9554) )
ROM_END

ROM_START( dallas16g )
	ROM_REGION16_BE( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("dal_g_pr", 0x00000, 0x04000, CRC(66deade9) SHA1(07ec6b923f2f053172737f1fc94aec84f3ea8da1) )
	ROM_LOAD16_BYTE("dal_g_pl", 0x00001, 0x04000, CRC(c5b6171c) SHA1(663167a3839ed7508ecb44fd5a1b2d3d8e466763) )
	ROM_LOAD16_BYTE("dal_g_br", 0x08000, 0x04000, CRC(e24d7ec7) SHA1(a936f6fcbe9bfa49bf455f2d8a8243d1395768c1) )
	ROM_LOAD16_BYTE("dal_g_bl", 0x08001, 0x04000, CRC(144a15e2) SHA1(c4fcc23d55fa5262f5e01dbd000644a7feb78f32) )
ROM_END

ROM_START( roma16g )
	ROM_REGION16_BE( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("roma_r_low",  0x00000, 0x04000, CRC(f2312170) SHA1(82a50ba59f74365aa77478adaadbbace6693dcc1) )
	ROM_LOAD16_BYTE("roma_l_low",  0x00001, 0x04000, CRC(5fbb72cc) SHA1(458473a62f9f7394c9d02a6ad0939d8e19bae78b) )
	ROM_LOAD16_BYTE("roma_r_high", 0x08000, 0x04000, CRC(a55917db) SHA1(df9a9a96cdc1c9a7ed0dc70c4ddbb4278236a15f) )
	ROM_LOAD16_BYTE("roma_l_high", 0x08001, 0x04000, CRC(0b20617b) SHA1(f0296c486ce9009a69de1e50b90b0e1b7555f468) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT    COMPAT  MACHINE   INPUT    CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1984, glasgow,   0,        0,      glasgow,  glasgow, glasgow_state, empty_init, "Hegener + Glaser", "Mephisto III-S Glasgow", MACHINE_SUPPORTS_SAVE )

// newer chesscomputers on 4-ROM hardware (see amsterdam.cpp for parent sets)
SYST( 1985, amsterdg,  amsterd,  0,      glasgow,  glasgow, glasgow_state, empty_init, "Hegener + Glaser", "Mephisto Amsterdam (Glasgow hardware)", MACHINE_SUPPORTS_SAVE )
SYST( 1986, dallas16g, dallas32, 0,      glasgow,  glasgow, glasgow_state, empty_init, "Hegener + Glaser", "Mephisto Dallas 68000 (Glasgow hardware)", MACHINE_SUPPORTS_SAVE )
SYST( 1987, roma16g,   roma32,   0,      glasgow,  glasgow, glasgow_state, empty_init, "Hegener + Glaser", "Mephisto Roma 68000 (Glasgow hardware)", MACHINE_SUPPORTS_SAVE )



glcx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco

// gl6600cx uses a NSC1028 system-on-a-chip designed by National Semiconductor specifically for VTech
// http://web.archive.org/web/19991127134657/http://www.national.com/news/item/0,1735,425,00.html

/*

Other known undumped international versions:
- Genius Pro 2000 (French version of Genius Tabletop Black Magic CX)

Leader 8008 CX (German version)

+---+-----------+-----+-----------------------+-----+-----+-----+
|   |SERIAL PORT|     |PARALLEL PORT (PRINTER)|     |MOUSE|     |
|   +-----------+     +-----------------------+     +-----+     |
|                                                               |
|                                                               |
|                                                               |
|                                                               |
|   +----+                                                      |
|   | A0 |                                                      |
|   +----+                                                      |
|                                                               |
|                                                               |
|                                        +--------+             |
|                                        |        |             |
|                              CPU       | VTECH  |   +------+  |
|                                        |LHMV5GNS|   |      |  |
|                                        |        |   |GM76U8|  |
|                                        |1999    |   |128CLF|  |
|                                        |27-6393-|   |W85   |  |
|       +-----------+                    |11      |   |      |  |
|       |27-6296-0-0|                    |        |   |      |  |
|       |47C241M NH7|                    |        |   +------+  |
|       +-----------+                    +--------+             |
|                                                               |
|                                                               |
|                                                               |
|                                                               |
+---------------------------------------------------------------+

CPU = epoxy blob
GM76U8128CLFW85 = LGS / Hynix 131,072 WORDS x 8 BIT CMOS SRAM
TMP47C241MG = TLCS-47 series 4-bit CPU with 2048x8 internal ROM

*/

#include "emu.h"
#include "cpu/cr16b/cr16b.h"
#include "screen.h"
#include "softlist_dev.h"


namespace {

class glcx_state : public driver_device
{
public:
	glcx_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void glcx(machine_config &config);

private:
	virtual uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cr16b_device> m_maincpu;
};

uint32_t glcx_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void glcx_state::mem_map(address_map &map)
{
	map(0x000000, 0x1fffff).rom().region("maincpu", 0);
}

static INPUT_PORTS_START( glcx )
INPUT_PORTS_END

void glcx_state::glcx(machine_config &config)
{
	/* basic machine hardware */
	CR16B(config, m_maincpu, 10000000); // FIXME: determine exact type and clock
	m_maincpu->set_addrmap(AS_PROGRAM, &glcx_state::mem_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(512, 256);
	screen.set_visarea(0, 512-1, 0, 256-1);
	screen.set_screen_update(FUNC(glcx_state::screen_update));

	SOFTWARE_LIST(config, "cart_list").set_original("glcx");
}

ROM_START( gl6600cx )
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_LOAD( "54-06400-00.u1", 0x000000, 0x200000, CRC(b05cd075) SHA1(b1d9eb02ca56350eb9e89518db89c0a2a845ebd8))
ROM_END

ROM_START( gl8008cx )
	ROM_REGION(0x200000, "maincpu", 0)
	ROM_LOAD( "27-6393-11.u1", 0x0000, 0x200000, CRC(fd49db46) SHA1(fc55bb31f42068f9d6cc8e2c2f419c3c4edb4fe6) )

	ROM_REGION(0x800, "subcpu", 0)
	ROM_LOAD( "27-6296-0-0.u3", 0x000, 0x800, NO_DUMP )
ROM_END

ROM_START( bs9009cx )
	ROM_REGION(0x200000, "maincpu", 0)
	ROM_LOAD( "27-6603-01.u1", 0x0000, 0x200000, CRC(2c299f65) SHA1(44b37007a7c4087d7c2bd8c24907402bfe445ba4) )

	ROM_REGION(0x800, "subcpu", 0)
	ROM_LOAD( "mcu.u5", 0x000, 0x800, NO_DUMP )
ROM_END

ROM_START( gtbmcx )
	ROM_REGION(0x200000, "maincpu", 0)
	ROM_LOAD( "27-6455-00.u5", 0x0000, 0x200000, CRC(166f11b7) SHA1(5d57573f3c97cfd75a07c333833f920ebc417867) )

	// Cartridge "EUROPA" contains no ROM
ROM_END

} // anonymous namespace


COMP( 1999, gl6600cx, 0, 0, glcx, glcx, glcx_state, empty_init, "VTech", "Genius Leader 6600 CX (Germany)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1999, gl8008cx, 0, 0, glcx, glcx, glcx_state, empty_init, "VTech", "Genius Leader 8008 CX (Germany)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP( 1999, bs9009cx, 0, 0, glcx, glcx, glcx_state, empty_init, "VTech", "BrainStation 9009 CXL (Germany)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP( 2000, gtbmcx,   0, 0, glcx, glcx, glcx_state, empty_init, "VTech", "Genius Tabletop Black Magic CX (Germany)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



gm1000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Skeleton driver for Nihon Eniac BH-1000/Hammond GM-1000 sound module.

*******************************************************************************/

#include "emu.h"
//#include "bus/midi/midi.h"
#include "cpu/m37710/m37710.h"
#include "machine/nvram.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"

namespace {

class gm1000_state : public driver_device
{
public:
	gm1000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_nvram(*this, "nvram", 0x800, ENDIANNESS_LITTLE)
	{
	}

	void gm1000(machine_config &config);

private:
	HD44780_PIXEL_UPDATE(lcd_pixel_update);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<m37702s1_device> m_maincpu;
	memory_share_creator<u8> m_nvram;
};

HD44780_PIXEL_UPDATE(gm1000_state::lcd_pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 24)
		bitmap.pix(line * 8 + y, pos * 6 + x) = state;
}


void gm1000_state::mem_map(address_map &map)
{
	map(0x000400, 0x000403).rw("lcdc", FUNC(hd44780_device::read), FUNC(hd44780_device::write)).umask16(0x00ff);
	map(0x004000, 0x007fff).ram();
	map(0x008000, 0x03ffff).rom().region("program", 0x8000);
	map(0x040000, 0x04ffff).rom().region("program", 0);
	map(0x050000, 0x050fff).lrw8(
		[this](offs_t offset) { return m_nvram[offset]; }, "nvram_r",
		[this](offs_t offset, u8 data) { m_nvram[offset] = data; }, "nvram_w").umask16(0x00ff);
	map(0x064000, 0x06efff).ram();
	map(0x070002, 0x070005).nopr();
}


static INPUT_PORTS_START(gm1000)
	// Keycode assignments are rather arbitrary and do not at all reflect the panel layout
	PORT_START("P4")
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_LEFT) PORT_NAME(u8"Parameter \u2190") // ←
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_RIGHT) PORT_NAME(u8"Parameter \u2192") // →
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_DOWN) PORT_NAME(u8"Data \u2190") // ←
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_UP) PORT_NAME(u8"Data \u2192") // →

	PORT_START("P6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("All Mute")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_BACKSLASH) PORT_NAME("P. Mute")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_RSHIFT) PORT_NAME("P. Monitor")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_NAME("Display")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_COMMA) PORT_NAME(u8"Part \u2190") // ←
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_STOP) PORT_NAME(u8"Part \u2192") // →
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_OPENBRACE) PORT_NAME(u8"Instrument \u2190") // ←
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_NAME(u8"Instrument \u2192") // →
INPUT_PORTS_END

void gm1000_state::gm1000(machine_config &config)
{
	M37702S1(config, m_maincpu, 4'000'000); // unknown clock; type guessed
	m_maincpu->set_addrmap(AS_PROGRAM, &gm1000_state::mem_map);
	m_maincpu->p4_in_cb().set_ioport("P4");
	m_maincpu->p6_in_cb().set_ioport("P6");
	m_maincpu->an0_cb().set_constant(0xc0); // battery voltage
	m_maincpu->an2_cb().set_constant(0x80); // ?

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6*24, 8*2);
	screen.set_visarea_full();
	screen.set_palette("palette");
	screen.set_color(rgb_t::green());

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	hd44780_device &lcdc(HD44780(config, "lcdc", 270'000)); // TODO: clock not measured, datasheet typical clock used
	lcdc.set_lcd_size(2, 24);
	lcdc.set_pixel_update_cb(FUNC(gm1000_state::lcd_pixel_update));
	lcdc.set_function_set_at_any_time(true);
}

ROM_START(bh1000)
	ROM_REGION16_LE(0x40000, "program", 0)
	ROM_LOAD16_BYTE("bh1_mgs71a__u6_sysl__v2.1_bf66.u6", 0x00000, 0x20000, CRC(d003880c) SHA1(5cf727c42dd1b903c0048f147b461676e8c35faf)) // MX27C1000-90
	ROM_LOAD16_BYTE("bh1_mgs71a__u7_sysh__v2.1_2fac.u7", 0x00001, 0x20000, CRC(989417a1) SHA1(3de4f10a2e7cde5eb93f04bd75db36e194b1d991)) // MX27C1000-90

	ROM_REGION(0x800000, "waves", 0) // DIP42 mask ROMs "© SUZUKI 1993".
	ROM_LOAD("319-35006_wd06__m531602c-53.u13", 0x000000, 0x200000, CRC(afcea840) SHA1(f003b19b83560191bef03d0d2c1559d77bdaa227))
	ROM_LOAD("319-35007_wd07__m531602c-52.u14", 0x200000, 0x200000, CRC(1f322ddb) SHA1(5f3b1be61782b74e4696d23cd551aa07eb709bb7))
	ROM_LOAD("319-35008_wd08__m531602c-54.u15", 0x200000, 0x200000, CRC(be9c158b) SHA1(878b08ace7b54fa27180c9c45d4b90c04b4bb656))
	ROM_LOAD("319-35009_wd09__m531602c-55.u16", 0x200000, 0x200000, CRC(dee0b84a) SHA1(c528131182d24c42c9d64d3b7f811fd8fe88c3e5))
ROM_END

ROM_START(gm1000)
	ROM_REGION16_LE(0x40000, "program", 0)
	ROM_LOAD16_BYTE("bh1_mgs71a__u6_sysl__v2.1_bf66.u6", 0x00000, 0x20000, CRC(d003880c) SHA1(5cf727c42dd1b903c0048f147b461676e8c35faf)) // MX27C1000-90
	ROM_LOAD16_BYTE("bh1_mgs71a__u7_sysh__v2.1_2fac.u7", 0x00001, 0x20000, CRC(989417a1) SHA1(3de4f10a2e7cde5eb93f04bd75db36e194b1d991)) // MX27C1000-90

	ROM_REGION(0x800000, "waves", 0) // DIP42 mask ROMs "© SUZUKI 1993"
	ROM_LOAD("319-35006_wd06__m531602c-53.u13", 0x000000, 0x200000, NO_DUMP)
	ROM_LOAD("319-35007_wd07__m531602c-52.u14", 0x200000, 0x200000, NO_DUMP)
	ROM_LOAD("319-35008_wd08__m531602c-54.u15", 0x200000, 0x200000, NO_DUMP)
	ROM_LOAD("319-35009_wd09__m531602c-55.u16", 0x200000, 0x200000, NO_DUMP)
ROM_END

} // anonymous namespace

SYST(1994, bh1000, 0, 0, gm1000, gm1000, gm1000_state, empty_init, "Nihon Eniac Co., Ltd.", "Sound Saurus BH-1000", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1994, gm1000, 0, 0, gm1000, gm1000, gm1000_state, empty_init, "Suzuki (Hammond license)", "GM-1000 GM Sound Module", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



gmaster.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Peter Trauner, hap
/*******************************************************************************

Hartung Game Master
Hong Kong LCD handheld console (mainly sold in Europe)
PeT mess@utanet.at march 2002

Hardware notes:
- NEC D78C11AGF (4KB internal ROM), 12.00MHz XTAL
- 2KB external RAM(UM6116-2L), cartridge slot for external ROM
- 2*LCDC hiding under epoxy, appears to be SED1520
- 61*64 1bpp LCD screen (the odd width is correct)
- 1-bit sound

Known releases:
- Hartung Game Master (Germany, gray)
- Impel Game Master (Hong Kong, gray)
- Systema 2000 (UK, gray)
- <unknown> Game Master / Game Tronic / Mega Tronic / Super Game (purple)
- Videojet Game Master (France, gray or white)
- Prodis PDJ-10 (Spain, gray or white)
- Delplay Game Plus (France, vertical orientation)

I presume it's an anonymous Hong Kong production. Most of the games too,
they have no copyright/company info in them. Some of the later games have
a copyright by Bon Treasure (a Hong Kong company that's also involved with
Watara Supervision), so perhaps it's them.

TODO:
- does port B do anything?
- according to one video on Youtube, hspace should have some kind of volume
  filter on the bgm? not sure what controls it, or maybe it's a hardware quirk

BTANB:
- LCD flickers partially, especially bad in finitezn
- fast button retriggers, for example the gear shift in carracing

*******************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/upd7810/upd7810.h"
#include "sound/spkrdev.h"
#include "video/sed1520.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "gmaster.lh"


namespace {

class gmaster_state : public driver_device
{
public:
	gmaster_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ram(*this, "ram"),
		m_lcd(*this, "lcd%u", 0),
		m_screen(*this, "screen"),
		m_speaker(*this, "speaker")
	{ }

	void gmaster(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<upd78c11_device> m_maincpu;
	required_shared_ptr<u8> m_ram;
	required_device_array<sed1520_device, 2> m_lcd;
	required_device<screen_device> m_screen;
	required_device<speaker_sound_device> m_speaker;

	u8 m_chipsel = 0;

	u8 io_r(offs_t offset);
	void io_w(offs_t offset, u8 data);
	void portb_w(u8 data);
	void portc_w(u8 data);
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	template<int N> SED1520_UPDATE_CB(screen_update_cb);

	void main_map(address_map &map) ATTR_COLD;
};

void gmaster_state::machine_start()
{
	save_item(NAME(m_chipsel));
}



/*******************************************************************************
    I/O
*******************************************************************************/

// LCD outputs

u32 gmaster_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u32 s0 = m_lcd[0]->screen_update(screen, bitmap, cliprect);
	u32 s1 = m_lcd[1]->screen_update(screen, bitmap, cliprect);
	return s0 & s1;
}

template<int N>
SED1520_UPDATE_CB(gmaster_state::screen_update_cb)
{
	// LCD #0: top half, LCD #1: bottom half
	rectangle clip = m_screen->visible_area();
	if (N == 1)
		clip.min_y = 32;
	else
		clip.max_y = 32-1;
	clip &= cliprect;

	for (int c = 0; c < 320; c++)
	{
		for (int b = 0; b < 8; b++)
		{
			int pixel = lcd_on ? BIT(dram[c], b) : 0;
			int x = c % 80;
			int y = N << 5 | (c / 80) << 3 | b;

			if (clip.contains(x, y))
				bitmap.pix(y, x) = pixel;
		}
	}
	return 0;
}


// memory mapped I/O

u8 gmaster_state::io_r(offs_t offset)
{
	u8 data = 0;

	// read from external RAM
	if (m_chipsel & 1)
		data |= m_ram[offset];

	// read from LCD
	for (int i = 0; i < 2; i++)
		if (BIT(m_chipsel, i + 1))
			data |= m_lcd[i]->read(offset & 1);

	return data;
}

void gmaster_state::io_w(offs_t offset, u8 data)
{
	// write to external RAM
	if (m_chipsel & 1)
		m_ram[offset] = data;

	// write to LCD
	for (int i = 0; i < 2; i++)
		if (BIT(m_chipsel, i + 1))
			m_lcd[i]->write(offset & 1, data);
}

void gmaster_state::main_map(address_map &map)
{
	// 0x0000-0x0fff is internal ROM
	map(0x4000, 0x47ff).mirror(0x3800).rw(FUNC(gmaster_state::io_r), FUNC(gmaster_state::io_w)).share("ram");
	map(0x8000, 0xffff).r("cartslot", FUNC(generic_slot_device::read_rom));
	// 0xff00-0xffff is internal RAM
}


// MCU ports

void gmaster_state::portb_w(u8 data)
{
	// ?
}

void gmaster_state::portc_w(u8 data)
{
	// d0: RAM CS
	// d1,d2: LCD CS
	m_chipsel = data & 7;

	// d4: speaker out
	m_speaker->level_w(BIT(data, 4));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( gmaster )
	PORT_START("JOY")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON2 ) // B
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1 ) // A
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SELECT )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void gmaster_state::gmaster(machine_config &config)
{
	// basic machine hardware
	UPD78C11(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &gmaster_state::main_map);
	m_maincpu->pa_in_cb().set_ioport("JOY");
	m_maincpu->pb_out_cb().set(FUNC(gmaster_state::portb_w));
	m_maincpu->pc_out_cb().set(FUNC(gmaster_state::portc_w));

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(80, 64);
	m_screen->set_visarea(0, 64-1-3, 0, 64-1);
	m_screen->set_screen_update(FUNC(gmaster_state::screen_update));
	m_screen->set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	SED1520(config, m_lcd[0]).set_screen_update_cb(FUNC(gmaster_state::screen_update_cb<0>));
	SED1520(config, m_lcd[1]).set_screen_update_cb(FUNC(gmaster_state::screen_update_cb<1>));

	config.set_default_layout(layout_gmaster);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(0, "mono", 0.50);

	// cartridge
	GENERIC_CARTSLOT(config, "cartslot", generic_linear_slot, "gmaster_cart").set_must_be_loaded(true);
	SOFTWARE_LIST(config, "cart_list").set_original("gmaster");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START(gmaster)
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "d78c11agf_e19.u1", 0x0000, 0x1000, CRC(05cc45e5) SHA1(05d73638dea9657ccc2791c0202d9074a4782c1e) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY    FULLNAME       FLAGS
SYST( 1990, gmaster, 0,      0,      gmaster, gmaster, gmaster_state, empty_init, "Hartung", "Game Master", MACHINE_SUPPORTS_SAVE )



gnat10.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for CP/M system by GNAT Computers, Inc.

    For more background on this company, visit the following link:
    https://classictech.wordpress.com/computer-companies/gnat-computers-san-diego-calif/

****************************************************************************/

#include "emu.h"
//#include "bus/ieee488/ieee488.h"
//#include "bus/rs232/rs232.h"
//#include "machine/tms9914.h"
#include "cpu/z80/z80.h"
#include "cpu/i8085/i8085.h"
#include "imagedev/floppy.h"
//#include "machine/am9511.h"
//#include "machine/am9517a.h"
//#include "machine/i8155.h"
//#include "machine/i8251.h"
#include "machine/com8116.h"
//#include "machine/mm58167.h"
//#include "machine/tms9914.h"
#include "machine/wd_fdc.h"
//#include "machine/z80ctc.h"
//#include "machine/z80pio.h"
#include "machine/z80sio.h"
//#include "video/mc6845.h"
//#include "screen.h"
//#include "speaker.h"


namespace {

class gnat10_state : public driver_device
{
public:
	gnat10_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0U)
		, m_brg(*this, "brg")
		, m_monitor(*this, "monitor")
		, m_ram(*this, "ram")
		, m_ram_on(false)
		, m_prom_disable(false)
	{
	}

	void gnat10(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u8 mem_r(offs_t offset);
	u8 floppy_status_r();
	void floppy_latch_w(u8 data);
	u8 rtc_r(offs_t offset);
	void rtc_w(offs_t offset, u8 data);
	void baud0_w(u8 data);
	void baud1_w(u8 data);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void video_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<fd1793_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<com8116_device> m_brg;
	required_region_ptr<u8> m_monitor;
	required_shared_ptr<u8> m_ram;

	bool m_ram_on;
	bool m_prom_disable;
};

void gnat10_state::machine_start()
{
	save_item(NAME(m_ram_on));
	save_item(NAME(m_prom_disable));
}

void gnat10_state::machine_reset()
{
	m_ram_on = false;
	floppy_latch_w(0);
}


u8 gnat10_state::mem_r(offs_t offset)
{
	if (m_ram_on && (m_prom_disable || offset < 0xf800))
		return m_ram[offset];
	else
		return m_monitor[offset & 0x07ff];
}

u8 gnat10_state::floppy_status_r()
{
	if (!machine().side_effects_disabled())
	{
		// TODO: this also forces the motor on (it turns off on a timeout)
	}
	return m_fdc->drq_r() << 7 | m_fdc->intrq_r();
}

void gnat10_state::floppy_latch_w(u8 data)
{
	// D0 = DS1, D1 = DS2, D5 = SS
	floppy_image_device *floppy = nullptr;
	for (int i = 0; i < 2 && floppy == nullptr; i++)
		if (BIT(data, i))
			floppy = m_floppy[i]->get_device();
	m_fdc->set_floppy(floppy);
	if (floppy != nullptr)
		floppy->ss_w(BIT(data, 5));

	m_prom_disable = BIT(data, 6);

	m_fdc->mr_w(BIT(data, 7));
}

u8 gnat10_state::rtc_r(offs_t offset)
{
	return 0; //m_rtc->read(offset ^ 0x10);
}

void gnat10_state::rtc_w(offs_t offset, u8 data)
{
	//m_rtc->write(offset ^ 0x10, data);
}

void gnat10_state::baud0_w(u8 data)
{
	m_brg->str_w(data & 0x0f);
	m_ram_on = true;
}

void gnat10_state::baud1_w(u8 data)
{
	m_brg->stt_w((data & 0xf0) >> 4);
}

void gnat10_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(gnat10_state::mem_r)).writeonly().share("ram");
}

void gnat10_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	//map(0x00, 0x0f).rw("dmac", FUNC(am9517a_device::read), FUNC(am9517a_device::write));
	//map(0x10, 0x11).mirror(0xe).rw("apu", FUNC(am9511_device::read), FUNC(am9511_device::write));
	//map(0x20, 0x23).mirror(0xc).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	//map(0x30, 0x3f).rw("488c", FUNC(tms9914_device::read), FUNC(tms9914_device::write));
	//map(0x40, 0x40).mirror(0xf).rw(FUNC(gnat10_state::pdma1_r), FUNC(gnat10_state::pdma1_w));
	//map(0x50, 0x53).mirror(0xc).rw("pio", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x60, 0x63).mirror(0xc).rw("sio1", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x70, 0x73).mirror(0xc).rw("sio0", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x80, 0x83).mirror(0xc).rw(m_fdc, FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	//map(0x90, 0x90).mirror(0xf).rw(FUNC(gnat10_state::pdma0_r), FUNC(gnat10_state::pdma0_w));
	map(0xa0, 0xa0).mirror(0xf).rw(FUNC(gnat10_state::floppy_status_r), FUNC(gnat10_state::floppy_latch_w));
	map(0xb0, 0xcf).rw(FUNC(gnat10_state::rtc_r), FUNC(gnat10_state::rtc_w));
	map(0xd0, 0xd0).mirror(0xf).w(FUNC(gnat10_state::baud0_w));
	map(0xe0, 0xe0).mirror(0xf).w(FUNC(gnat10_state::baud1_w));
	//map(0xf0, 0xf0).r(?); // undocumented switches?
}

void gnat10_state::video_map(address_map &map)
{
	map.global_mask(0x7fff);
	map(0x0000, 0x07ff).mirror(0x800).rom().region("videocpu", 0);
	map(0x1000, 0x13ff).mirror(0xc00).ram();
	map(0x2000, 0x27ff).mirror(0x800).ram().share("vram");
	//map(0x3000, 0x3000).mirror(0xfff).w(FUNC(gnat10_state::reset_out_w));
	//map(0x4000, 0x40ff).mirror(0x700).rw("videopio", FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w));
	//map(0x4800, 0x4807).mirror(0x7f8).rw("videopio", FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
	//map(0x5000, 0x5001).mirror(0xffe).rw("videosio", FUNC(i8251_device::read), FUNC(i8251_device::write));
	//map(0x6000, 0x6000).mirror(0xfff).w(FUNC(gnat10_state::bell_w));
	//map(0x7000, 0x7000).mirror(0xffe).w("crtc", FUNC(mc6845_device::address_w));
	//map(0x7001, 0x7001).mirror(0xffe).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
}


static INPUT_PORTS_START(gnat10)
INPUT_PORTS_END

static void gnat_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

void gnat10_state::gnat10(machine_config &config)
{
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &gnat10_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &gnat10_state::io_map);

	Z80SIO(config, "sio0", 16_MHz_XTAL / 4); // MK3884
	Z80SIO(config, "sio1", 16_MHz_XTAL / 4);

	FD1793(config, m_fdc, 16_MHz_XTAL / 16);
	FLOPPY_CONNECTOR(config, m_floppy[0], gnat_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[1], gnat_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);

	COM5016_5(config, m_brg, 4.9152_MHz_XTAL); // BR1941-5
	m_brg->fr_handler().set("sio0", FUNC(z80sio_device::rxca_w));
	m_brg->fr_handler().append("sio0", FUNC(z80sio_device::txca_w));
	m_brg->fr_handler().append("sio1", FUNC(z80sio_device::rxca_w));
	m_brg->fr_handler().append("sio1", FUNC(z80sio_device::txca_w));
	m_brg->ft_handler().set("sio1", FUNC(z80sio_device::rxtxcb_w));

	// TODO: DMAC, peripheral ports

	i8085a_cpu_device &videocpu(I8085A(config, "videocpu", 18.432_MHz_XTAL / 4));
	videocpu.set_addrmap(AS_PROGRAM, &gnat10_state::video_map);
	videocpu.set_disable();

	// TODO: 8155, 8251A, 6845, video screen
}

ROM_START(gnat10)
	ROM_REGION(0x800, "monitor", 0)
	ROM_LOAD("gnat-507", 0x000, 0x800, CRC(72baa750) SHA1(7b78324b90b8c6f78c88a7dde8d53ea612ea1f7f)) // LF patched back to CR/LF in four instances

	ROM_REGION(0x800, "videocpu", 0)
	ROM_LOAD("videocpu.bin", 0x000, 0x800, NO_DUMP) // TMS2716 or TMS2732

	ROM_REGION(0x800, "chargen", 0)
	ROM_LOAD("chargen.bin", 0x000, 0x800, NO_DUMP) // TMS2716 or TMS2732
ROM_END

} // anonymous namespace


COMP(1980, gnat10, 0, 0, gnat10, gnat10, gnat10_state, empty_init, "GNAT Computers", "GNAT System 10", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



goupil.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Jean-François DEL NERO
/***************************************************************************

    SMT Goupil G1 & G2 driver

    Current state :

    -> CPU / ROM / RAM working
    -> Video output working (G1: ef9364, G2: Visu 24x80 MC6845)
    -> Keyboard support working (need to be polished... )
    -> Floppy FDC not fully implemented.
    -> Sound support missing.

    Software :
    -> The Monitor is working
    -> The internal G1 Basic is working (-> 6800 0xC3 illegal opcode emulation needed).

    02/04/2016
    Jean-François DEL NERO

****************************************************************************/

#include "emu.h"

#include "cpu/m6800/m6800.h"
#include "imagedev/floppy.h"
#include "machine/ram.h"
#include "machine/6522via.h"
#include "machine/6850acia.h"
#include "machine/i8279.h"
#include "machine/timer.h"
#include "machine/wd_fdc.h"
#include "video/ef9364.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "softlist.h"

#include "logmacro.h"


namespace {

#define MAIN_CLOCK           4_MHz_XTAL
#define VIDEO_CLOCK          MAIN_CLOCK / 8     /* 1.75 Mhz */
#define CPU_CLOCK            MAIN_CLOCK / 4     /* 1 Mhz */

class goupil_base_state : public driver_device
{
public:
	goupil_base_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_acia(*this,  "ef6850")
		, m_via_video(*this, "m_via_video")
		, m_via_keyb(*this, "m_via_keyb")
		, m_via_modem(*this, "m_via_modem")
		, m_palette(*this, "palette")
		, m_screen(*this, "screen")
		, m_fdc(*this, "fd1791")
		, m_floppy0(*this, "fd1791:0")
		, m_floppy1(*this, "fd1791:1")
		, m_ctrl(*this, "CTR0")
		, m_floppy(nullptr)
	{ }

	uint8_t kbd1_r();
	uint8_t kbd2_r();
	uint8_t shift_kb1_r();
	uint8_t ctrl_kb1_r();

	void scanlines_kbd1_w(uint8_t data);
	void scanlines_kbd2_w(uint8_t data);

	void base(machine_config &config);
protected:
	virtual void machine_reset() override ATTR_COLD;

	required_device<m6808_cpu_device> m_maincpu;
	required_device<acia6850_device> m_acia;
	required_device<via6522_device> m_via_video;
	required_device<via6522_device> m_via_keyb;
	required_device<via6522_device> m_via_modem;
	required_device<palette_device> m_palette;
	required_device<screen_device> m_screen;
	required_device<fd1791_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	required_device<floppy_connector> m_floppy1;
	required_ioport m_ctrl;
	floppy_image_device *m_floppy;

	uint8_t m_row_kbd1;
	uint8_t m_row_kbd2;
	uint8_t m_cnttim;
	uint8_t m_valkeyb;
};

class goupil_g1_state : public goupil_base_state
{
public:
	goupil_g1_state(const machine_config &mconfig, device_type type, const char *tag)
		: goupil_base_state(mconfig, type, tag)
		, m_ef9364(*this, "ef9364")
		, m_scanline_timer(nullptr)
		, m_old_state_ca2(0)
		, m_via_video_pbb_data(0)
	{ }

	void via_video_pba_w(uint8_t data);
	void via_video_pbb_w(uint8_t data);
	void via_video_ca2_w(int state);

	void goupil_g1(machine_config &config);
protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	TIMER_CALLBACK_MEMBER(scanline_tick);

private:
	void mem(address_map &map) ATTR_COLD;

	required_device<ef9364_device> m_ef9364;

	emu_timer *m_scanline_timer;
	int m_old_state_ca2;
	uint8_t m_via_video_pbb_data;
};

class goupil_g2_state : public goupil_base_state
{
public:
	goupil_g2_state(const machine_config &mconfig, device_type type, const char *tag)
		: goupil_base_state(mconfig, type, tag)
		, m_visu24x80_ram(*this, RAM_TAG)
		, m_visu24x80_rom(*this, "visu_24x80")
	{
	}

	MC6845_UPDATE_ROW(crtc_update_row);
	MC6845_ON_UPDATE_ADDR_CHANGED(crtc_update_addr_changed);

	uint8_t visu24x80_ram_r(offs_t offset);
	void visu24x80_ram_w(offs_t offset, uint8_t data);

	void goupil_g2(machine_config &config);

private:
	void mem(address_map &map) ATTR_COLD;

	required_device<ram_device>     m_visu24x80_ram;
	required_region_ptr<uint8_t>    m_visu24x80_rom;
};

/**********************************
* Floppy controller I/O Handlers  *
***********************************/
// TODO

/**********************************
*      Keyboard I/O Handlers      *
***********************************/

TIMER_CALLBACK_MEMBER(goupil_g1_state::scanline_tick)
{
	m_ef9364->update_scanline((uint16_t)m_screen->vpos());
	m_scanline_timer->adjust(m_screen->time_until_pos(m_screen->vpos() + 10));
}

void goupil_g1_state::mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3FFF).ram();
	map(0x4000, 0x7FFF).ram();
	map(0xC000, 0xE3FF).rom().region("maincpu", 0xC000); // Basic ROM (BASIC 1 up to BASIC 9).

	map(0xE400, 0xE7FF).ram();
	map(0xE800, 0xE80F).rw(m_acia, FUNC(acia6850_device::data_r), FUNC(acia6850_device::data_w));
	map(0xE810, 0xE81F).m(m_via_video, FUNC(via6522_device::map));

	map(0xE820, 0xE820).rw("i8279_kb1", FUNC(i8279_device::data_r), FUNC(i8279_device::data_w));
	map(0xE821, 0xE821).rw("i8279_kb1", FUNC(i8279_device::status_r), FUNC(i8279_device::cmd_w));

	map(0xE830, 0xE830).rw("i8279_kb2", FUNC(i8279_device::data_r), FUNC(i8279_device::data_w));
	map(0xE831, 0xE831).rw("i8279_kb2", FUNC(i8279_device::status_r), FUNC(i8279_device::cmd_w));

	map(0xE840, 0xE84F).m(m_via_keyb, FUNC(via6522_device::map));

	map(0xE860, 0xE86F).m(m_via_modem, FUNC(via6522_device::map));

	map(0xE8F0, 0xE8FF).rw(m_fdc, FUNC(fd1791_device::read), FUNC(fd1791_device::write));

	map(0xF000, 0xF3FF).rom().region("maincpu", 0xF000);
	map(0xF400, 0xF7FF).rom().region("maincpu", 0xF400); // Modem (MOD 3)
	map(0xF800, 0xFFFF).rom().region("maincpu", 0xF800); // Monitor (MON 1 + MON 2)
}

void goupil_g2_state::mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3FFF).ram();
	map(0x4000, 0x7FFF).ram();
	map(0x8000, 0xE3FF).ram();

	map(0xE400, 0xE7FF).ram();

	map(0xE800, 0xE80F).rw(m_acia, FUNC(acia6850_device::data_r), FUNC(acia6850_device::data_w));
	map(0xE810, 0xE81F).m(m_via_video, FUNC(via6522_device::map));

	map(0xE820, 0xE820).rw("i8279_kb1", FUNC(i8279_device::data_r), FUNC(i8279_device::data_w));
	map(0xE821, 0xE821).rw("i8279_kb1", FUNC(i8279_device::status_r), FUNC(i8279_device::cmd_w));

	map(0xE830, 0xE830).rw("i8279_kb2", FUNC(i8279_device::data_r), FUNC(i8279_device::data_w));
	map(0xE831, 0xE831).rw("i8279_kb2", FUNC(i8279_device::status_r), FUNC(i8279_device::cmd_w));

	map(0xE840, 0xE84F).m(m_via_keyb, FUNC(via6522_device::map));

	map(0xE860, 0xE86F).m(m_via_modem, FUNC(via6522_device::map));

	map(0xE870, 0xE870).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0xE871, 0xE871).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));

	map(0xE8F0, 0xE8FF).rw(m_fdc, FUNC(fd1791_device::read), FUNC(fd1791_device::write));
	map(0xEC00, 0xF3FF).rw(FUNC(goupil_g2_state::visu24x80_ram_r), FUNC(goupil_g2_state::visu24x80_ram_w));
	map(0xF400, 0xF7FF).rom().region("maincpu", 0xF400); // Monitor (MON 1)
	map(0xF800, 0xFFFF).rom().region("maincpu", 0xF800); // Monitor (MON 2)
}

void goupil_base_state::scanlines_kbd1_w(uint8_t data)
{
	m_row_kbd1 = data;
}

uint8_t goupil_base_state::ctrl_kb1_r()
{
	return BIT(m_ctrl->read(), 1);
}

uint8_t goupil_base_state::shift_kb1_r()
{
	return BIT(m_ctrl->read(), 0);
}

uint8_t goupil_base_state::kbd1_r()
{
	char kbdrow[6];
	uint8_t data = 0xff;

	kbdrow[0] = 'A';
	kbdrow[1] = 'X';
	kbdrow[2] = '0' + ( m_row_kbd1 & 7 ) ;
	kbdrow[3] = 0;

	data = ioport(kbdrow)->read();

	return data;
}

void goupil_base_state::scanlines_kbd2_w(uint8_t data)
{
	m_row_kbd2 = data & 7;
}

uint8_t goupil_base_state::kbd2_r()
{
	char kbdrow[6];
	uint8_t data = 0xff;

	kbdrow[0] = 'B';
	kbdrow[1] = 'X';
	kbdrow[2] = '0' + ( m_row_kbd2 & 7 ) ;
	kbdrow[3] = 0;

	data = ioport(kbdrow)->read();

	return data;
}

/* Input ports */
static INPUT_PORTS_START( goupil_g1 )
	PORT_START("AX0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_START("AX1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_START("AX2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('?')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_START("AX3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)
	PORT_START("AX4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)  PORT_CHAR(':') PORT_CHAR('/')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('.')
	PORT_START("AX5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TAB") PORT_CODE(KEYCODE_TAB) PORT_CHAR(UCHAR_MAMEKEY(TAB))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BS") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_START("AX6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(0x00F9) PORT_CHAR('%')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_START("AX7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("CTR0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)

	PORT_START("BX0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("BX1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("BX2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("BX3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_START("BX4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("BX5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("BX6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_START("BX7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
INPUT_PORTS_END

static void goupil_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

void goupil_base_state::machine_reset()
{
	m_floppy = nullptr;
	m_valkeyb = 0xFF;
}

void goupil_g1_state::machine_start()
{
	m_scanline_timer = timer_alloc(FUNC(goupil_g1_state::scanline_tick), this);
}

void goupil_g1_state::machine_reset()
{
	goupil_base_state::machine_reset();
	m_scanline_timer->adjust(m_screen->time_until_pos(m_screen->vpos() + 10));
	m_old_state_ca2 = 0;
	m_via_video_pbb_data = 0;
}

void goupil_g1_state::via_video_pba_w(uint8_t data)
{
	LOG("%s: write via_video_pba_w reg : 0x%X\n",machine().describe_context(),data);
	m_ef9364->char_latch_w(data);
}

void goupil_g1_state::via_video_pbb_w(uint8_t data)
{
	LOG("%s: write via_video_pbb_w reg : 0x%X\n",machine().describe_context(),data);
	m_via_video_pbb_data = data;
}

void goupil_g1_state::via_video_ca2_w(int state)
{
	if (!m_old_state_ca2 && state)
	{
		m_ef9364->command_w(m_via_video_pbb_data & 0xF);
	}
	m_old_state_ca2 = state;
}

// Visu 24x80 video card update row.
MC6845_UPDATE_ROW(goupil_g2_state::crtc_update_row)
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	uint32_t *p = &bitmap.pix(y);
	for (uint8_t x = 0; x < x_count; ++x)
	{
		uint16_t const offset = ( 0x400 + ( ma + x ) ) & 0x7FF;
		uint8_t const chr = m_visu24x80_ram->pointer()[offset];
		uint8_t const gfx = m_visu24x80_rom[ ( ( chr & 0x7F ) << 4 ) + ra ] ^ ((x == cursor_x) ? 0xff : 0);

		for (unsigned bit = 0; 8 > bit; ++bit)
		{
			*p++ = palette[BIT(gfx, 7 - bit)];
		}
	}
}

MC6845_ON_UPDATE_ADDR_CHANGED(goupil_g2_state::crtc_update_addr_changed)
{
}

void goupil_g2_state::visu24x80_ram_w(offs_t offset, uint8_t data)
{
	LOG("%s: write visu24x80_ram_w mem : 0x%.4X <- 0x%X\n",machine().describe_context(),offset,data);

	m_visu24x80_ram->pointer()[offset] = data;
}

uint8_t goupil_g2_state::visu24x80_ram_r(offs_t offset)
{
	uint8_t data;

	data = m_visu24x80_ram->pointer()[offset];

	LOG("%s: read visu24x80_ram_r 0x%.4X = 0x%.2X\n",machine().describe_context(),offset,data);

	return data;
}

void goupil_base_state::base(machine_config &config)
{
	M6808(config, m_maincpu, CPU_CLOCK);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);

	// TODO: sound hardware

	ACIA6850(config, m_acia, 0);

	// TODO: Is this specific to the G1?
	MOS6522(config, m_via_video, CPU_CLOCK / 4);

	MOS6522(config, m_via_keyb, CPU_CLOCK / 4);
	m_via_keyb->irq_handler().set_inputline(m_maincpu, M6808_IRQ_LINE);

	MOS6522(config, m_via_modem, CPU_CLOCK / 4);
	m_via_modem->irq_handler().set_inputline(m_maincpu, M6808_IRQ_LINE);

	/* Floppy */
	FD1791(config, m_fdc, 8_MHz_XTAL);
	FLOPPY_CONNECTOR(config, m_floppy0, goupil_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy1, goupil_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);

	i8279_device &i8279_kb1(I8279(config, "i8279_kb1", CPU_CLOCK));
	i8279_kb1.out_sl_callback().set(FUNC(goupil_g1_state::scanlines_kbd1_w));   // scan SL lines
	i8279_kb1.in_rl_callback().set(FUNC(goupil_g1_state::kbd1_r));              // kbd RL lines
	i8279_kb1.in_shift_callback().set(FUNC(goupil_g1_state::shift_kb1_r));
	i8279_kb1.in_ctrl_callback().set(FUNC(goupil_g1_state::ctrl_kb1_r));
	i8279_kb1.out_irq_callback().set(m_via_keyb, FUNC(via6522_device::write_ca1));

	i8279_device &i8279_kb2(I8279(config, "i8279_kb2", CPU_CLOCK));
	i8279_kb2.out_sl_callback().set(FUNC(goupil_g1_state::scanlines_kbd2_w));   // scan SL lines
	i8279_kb2.in_rl_callback().set(FUNC(goupil_g1_state::kbd2_r));              // kbd RL lines
	i8279_kb2.in_shift_callback().set_constant(1);
	i8279_kb2.in_ctrl_callback().set_constant(1);
}

void goupil_g1_state::goupil_g1(machine_config &config)
{
	base(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &goupil_g1_state::mem);

	m_screen->set_screen_update("ef9364", FUNC(ef9364_device::screen_update));
	m_screen->set_size(64*8, 16*(8+4));
	m_screen->set_visarea(0, 64*8-1, 0, 16*(8+4)-1);

	PALETTE(config, m_palette).set_entries(16);

	EF9364(config, m_ef9364, VIDEO_CLOCK);
	m_ef9364->set_palette_tag("palette");
	m_ef9364->set_nb_of_pages(1);
	m_ef9364->set_erase(0x7f);

	m_via_video->writepa_handler().set(FUNC(goupil_g1_state::via_video_pba_w));
	m_via_video->writepb_handler().set(FUNC(goupil_g1_state::via_video_pbb_w));
	m_via_video->ca2_handler().set(FUNC(goupil_g1_state::via_video_ca2_w));
}

void goupil_g2_state::goupil_g2(machine_config &config)
{
	base(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &goupil_g2_state::mem);

	// "visu 24x80" board
	RAM(config, m_visu24x80_ram);
	m_visu24x80_ram->set_default_size("2K");    // visu24x80 2K ram

	m_screen->set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	m_screen->set_size((80*8), (24*(8+4)));
	m_screen->set_visarea(0, (80*8)-1, 0, (24*(8+4))-1);

	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	mc6845_device &crtc(MC6845(config, "crtc", 14.318181_MHz_XTAL / 8));
	crtc.set_show_border_area(false);
	crtc.set_char_width(8);
	crtc.set_update_row_callback(FUNC(goupil_g2_state::crtc_update_row));
	crtc.set_on_update_addr_change_callback(FUNC(goupil_g2_state::crtc_update_addr_changed));

	m_via_video->writepa_handler().set_nop();
	m_via_video->writepb_handler().set_nop();
	m_via_video->ca2_handler().set_nop();
}

/* ROM definition */
ROM_START( goupilg1 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "smt_goupil_g1_basic_1.bin", 0xC000, 0x0400, CRC(ad105b12) SHA1(631cd4b997f76b57bf2509e4bff30b1595c8bd13) )
	ROM_LOAD( "smt_goupil_g1_basic_2.bin", 0xC400, 0x0400, CRC(0c5c309c) SHA1(f1cab4b0f9191e53113790a95f1ab7108f9406a1) )
	ROM_LOAD( "smt_goupil_g1_basic_3.bin", 0xC800, 0x0400, CRC(1f1eb127) SHA1(dbbb880c79d515acbfcb2be9a4c96962f3e4edea) )
	ROM_LOAD( "smt_goupil_g1_basic_4.bin", 0xCC00, 0x0400, CRC(09be48e4) SHA1(86cae0d159583c1d572a5754f3bb6b4a2e479359) )
	ROM_LOAD( "smt_goupil_g1_basic_5.bin", 0xD000, 0x0400, CRC(bdeb395c) SHA1(32a50468f1ca772ee45a1f5c61c66f3ecc774074) )
	ROM_LOAD( "smt_goupil_g1_basic_6.bin", 0xD400, 0x0400, CRC(850a4000) SHA1(720f0bb3e45877835219b7e1d943ef4f19b9977d) )
	ROM_LOAD( "smt_goupil_g1_basic_7.bin", 0xD800, 0x0400, CRC(586c7670) SHA1(13e2e96b9f1a53555ce0d55f657cf3c6b96f10a0) )
	ROM_LOAD( "smt_goupil_g1_basic_8.bin", 0xDC00, 0x0400, CRC(33281300) SHA1(ce631fa8157a3f8869c5fefe24b7f40e06696df9) )
	ROM_LOAD( "smt_goupil_g1_basic_9.bin", 0xE000, 0x0400, CRC(a3911201) SHA1(8623a0a2d83eb3a27a795030643c5c05a4350a9f) )
	ROM_LOAD( "smt_goupil_g1_mod_3.bin",   0xF400, 0x0400, CRC(e662f152) SHA1(11b91c5737e7572a2c18472b66bbd16b485132d5) )
	ROM_LOAD( "smt_goupil_g1_mon_1.bin",   0xF800, 0x0400, CRC(98b7be69) SHA1(69e83fe78a43fcf2b08fb0bcefb0d217a57b1ecb) )
	ROM_LOAD( "smt_goupil_g1_mon_2.bin",   0xFC00, 0x0400, CRC(19386b81) SHA1(e52f63fd29d374319781e9677de6d3fd61a3684c) )

	ROM_REGION( 0x400, "ef9364", 0 )
	ROM_LOAD( "smt_goupil_g1_charset.bin", 0x0000, 0x0400, CRC(8b6da54b) SHA1(ac2204600f45c6dd0df1e759b62ed25928f02a12) )
ROM_END

/* ROM definition */
ROM_START( goupilg2 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "smt_goupil_g2_mod_1.bin",   0xC000, 0x0400, CRC(4d585e40) SHA1(7558f89db52299c4c305755259d5c908b3f66ac7) )
	ROM_LOAD( "smt_goupil_g2_mod_2.bin",   0xC400, 0x0400, CRC(c5531667) SHA1(24b0a1d3b812b95e68f4dc4323581b1fd14eb4fb) )
	ROM_LOAD( "smt_goupil_g2_mon_1.bin",   0xF000, 0x0800, CRC(91a4f256) SHA1(ece3b47a17e47fc87e2262be806ce8015f5f5db6) )
	ROM_LOAD( "smt_goupil_g2_mon_2.bin",   0xF800, 0x0800, CRC(f7783a32) SHA1(7368fc0bd86b48e6727367bd7d1922f219741015) )

	ROM_REGION( 0x400, "ef9364", 0 )
	ROM_LOAD( "smt_goupil_g2_charset.bin", 0x0000, 0x0400, CRC(d3930877) SHA1(7b790fb18f8893cfc753bf622c8b795075741d22) )

	ROM_REGION( 0x800, "visu_24x80", 0 )
	ROM_LOAD( "smt_goupil_g2_charset_24x80.bin", 0x0000, 0x0800, CRC(f0f83b99) SHA1(75a7730aec30280ee4ccf3dcaf587eea4f861196) )

ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME      PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY  FULLNAME     FLAGS */
COMP( 1979, goupilg1, 0,      0,      goupil_g1, goupil_g1, goupil_g1_state, empty_init, "SMT",   "Goupil G1", MACHINE_NO_SOUND )
COMP( 1981, goupilg2, 0,      0,      goupil_g2, goupil_g1, goupil_g2_state, empty_init, "SMT",   "Goupil G2", MACHINE_NO_SOUND )



gp2x.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/**************************************************************************
 *
 * gp2x.c - Game Park Holdings GP2X
 * Skeleton by R. Belmont
 *
 * CPU: MagicEyes MP2520F SoC
 * MP2520F consists of:
 *    ARM920T CPU core + MMU
 *    ARM940T slave CPU w/o MMU
 *    LCD controller
 *    DMA controller
 *    Interrupt controller
 *    NAND flash interface
 *    MMC/SD Card interface
 *    USB controller
 *    and more.
 *
 **************************************************************************/

#include "emu.h"
#include "cpu/arm7/arm7.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class gp2x_state : public driver_device
{
public:
	gp2x_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
			m_maincpu(*this, "maincpu"),
			m_maincpu_region(*this, "maincpu"),
			m_ram(*this, "ram"){ }

	void gp2x(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	required_region_ptr<uint32_t> m_maincpu_region;
	uint32_t gp2x_lcdc_r(offs_t offset);
	void gp2x_lcdc_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t nand_r(offs_t offset, uint32_t mem_mask = ~0);
	void nand_w(offs_t offset, uint32_t data);
	uint32_t tx_status_r();
	void tx_xmit_w(uint32_t data);
	uint32_t timer_r();
	uint32_t nand_ctrl_r();
	void nand_ctrl_w(uint32_t data);
	uint32_t sdcard_r();
	required_shared_ptr<uint32_t> m_ram;
	uint16_t m_vidregs[0x200/2]{};
	uint32_t m_nand_ptr = 0;
	uint32_t m_nand_cmd = 0;
	uint32_t m_nand_subword_stage = 0;
	uint32_t m_nand_stage = 0;
	uint32_t m_nand_ptr_temp = 0;
	uint32_t m_timer = 0;
	uint32_t screen_update_gp2x(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void gp2x_map(address_map &map) ATTR_COLD;
};



#if 0
static const char *const gp2x_regnames[0x200] =
{
	"DPC Control",
	"PAD Control",
	"PAD Polarity Control 1",
	"PAD Polarity Control 2",
	"PAD Polarity Control 3",
	"PAD Enable Control 1",
	"PAD Enable Control 2",
	"PAD Enable Control 3",
	"",
	"",
	"",
	"Active width",
	"Active height",
	"HSYNC width",
	"HSYNC start",
	"HSYNC end",
	"VSYNC control",    // 20
	"VSYNC end",
	"",
	"DE Control",
	"S Control",
	"FG Control",
	"LP Control",
	"CLKV Control",
	"CLKV Control 2",   // 30
	"POL Control",
	"CIS Sync Control",
	"",
	"",
	"Luma blank level",
	"Chroma blank level",
	"Luma pos sync",
	"Luma neg sync",    // 40
	"Chroma pos sync",
	"Chroma neg sync",
	"Interrupt control",
	"Clock control",
	"",
	"",
	"",
	"",         // 50
	"",
	"",
	"",
	"",
	"",
	"",
	"",
	"",         // 60
	"",
	"",
	"",
	"",
	"",
	"",
	"",
	"",         // 70
	"",
	"",
	"",
	"",
	"",
	"",
	"",
	"Overlay control",  // 80
	"Image effect",
	"Image control",
	"Scale factor A top",
	"Scale factor A bottom",
	"Scale factor A top 2 H",
	"Scale factor A top 2 L",
	"Scale factor A bottom 2 H",
	"Scale factor A bottom 2 L",    // 90
	"Width region A top",
	"Width region A bottom",
	"H Offset region A",
	"H Offset region A 2",
	"V Start region A",
	"V End region A top",
	"V End region A bottom",
	"YUV Source region A L",    // a0
	"YUV Source region A H",
};
#endif
uint32_t gp2x_state::screen_update_gp2x(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	// display enabled?
	if (m_vidregs[0] & 1)
	{
		// only support RGB still image layer for now
		if (m_vidregs[0x80/2] & 4)
		{
			uint16_t const *const vram = (uint16_t *)&m_ram[0x2100000/4];

/*          printf("RGB still image 1 enabled, bpp %d, size is %d %d %d %d\n",
                (m_vidregs[(0xda/2)]>>9)&3,
                m_vidregs[(0xe2/2)],
                m_vidregs[(0xe4/2)],
                m_vidregs[(0xe6/2)],
                m_vidregs[(0xe8/2)]);*/


			for (int y = 0; y < 240; y++)
			{
				uint32_t *scanline = &bitmap.pix(y);

				for (int x = 0; x < 320; x++)
				{
					uint16_t const pixel = vram[(320*y)+x];

					*scanline++ = rgb_t(0xff, (pixel>>11)<<3, ((pixel>>5)&0x3f)<<2, (pixel&0x1f)<<3);
				}
			}
		}
	}

	return 0;
}

uint32_t gp2x_state::gp2x_lcdc_r(offs_t offset)
{
	return m_vidregs[offset*2] | m_vidregs[(offset*2)+1]<<16;
}

void gp2x_state::gp2x_lcdc_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	if (mem_mask == 0xffff)
	{
		m_vidregs[offset*2] = data;
//      printf("%x to video reg %x (%s)\n", data, offset*2, gp2x_regnames[offset*2]);
	}
	else if (mem_mask == 0xffff0000)
	{
		m_vidregs[(offset*2)+1] = data>>16;
//      printf("%x to video reg %x (%s)\n", data>>16, (offset*2)+1, gp2x_regnames[(offset*2)+1]);
	}
	else
	{
		logerror("GP2X LCDC: ERROR: write %x to vid reg @ %x mask %08x\n", data, offset, mem_mask);
	}
}

uint32_t gp2x_state::nand_r(offs_t offset, uint32_t mem_mask)
{
	uint32_t *ROM = m_maincpu_region;
	uint32_t ret;

	if (offset == 0)
	{
		switch (m_nand_cmd)
		{
			case 0:
				if (mem_mask == 0xffffffff)
				{
					return ROM[m_nand_ptr++];
				}
				else if (mem_mask == 0x000000ff)    // byte-wide reads?
				{
					switch (m_nand_subword_stage++)
					{
						case 0:
							return ROM[m_nand_ptr];
						case 1:
							return ROM[m_nand_ptr]>>8;
						case 2:
							return ROM[m_nand_ptr]>>16;
						case 3:
							ret = ROM[m_nand_ptr]>>24;
							m_nand_subword_stage = 0;
							m_nand_ptr++;
							return ret;
						default:
							logerror("Bad nand_subword_stage = %d\n", m_nand_subword_stage);
							break;
					}
				}
				break;

			case 0x50:  // read out-of-band data
				return 0xff;

			case 0x90:  // read ID
				switch (m_nand_stage++)
				{
					case 0:
						return 0xec;    // Samsung
					case 1:
						return 0x76;    // 64MB
				}
				break;

			default:
				logerror("NAND: read unk command %x (PC %x)\n", m_nand_cmd, m_maincpu->pc());
				break;
		}
	}

//  printf("Read unknown nand offset %x\n", offset);

	return 0;
}

void gp2x_state::nand_w(offs_t offset, uint32_t data)
{
	switch (offset)
	{
		case 4: // command
			m_nand_cmd = data;
//          printf("NAND: command %x (PC %x0)\n", data, m_maincpu->pc());
			m_nand_stage = 0;
			m_nand_subword_stage = 0;
			break;

		case 6: // address
			if (m_nand_cmd == 0)
			{
				switch (m_nand_stage)
				{
					case 0:
						m_nand_ptr_temp &= ~0xff;
						m_nand_ptr_temp |= data;
						break;
					case 1:
						m_nand_ptr_temp &= ~0xff00;
						m_nand_ptr_temp |= data<<8;
						break;
					case 2:
						m_nand_ptr_temp &= ~0xff0000;
						m_nand_ptr_temp |= data<<16;
						break;
					case 3:
						m_nand_ptr_temp &= ~0xff000000;
						m_nand_ptr_temp |= data<<24;

//                      printf("NAND: ptr now %x, /2 %x, replacing %x\n", m_nand_ptr_temp, m_nand_ptr_temp/2, m_nand_ptr);
						m_nand_ptr = m_nand_ptr_temp/2;
						break;
				}
				m_nand_stage++;
			}
			break;

		default:
			logerror("nand_w: %x to unknown offset %x\n", data, offset);
			break;
	}
}

uint32_t gp2x_state::tx_status_r()
{
	return 0x6; // tx ready, tx empty
}

void gp2x_state::tx_xmit_w(uint32_t data)
{
	printf("%c", data&0xff);
}

uint32_t gp2x_state::timer_r()
{
	return m_timer++;
}

uint32_t gp2x_state::nand_ctrl_r()
{
	return 0x8000<<16;      // timed out
}

void gp2x_state::nand_ctrl_w(uint32_t data)
{
//  printf("%08x to nand_ctrl_w\n", data);
}

uint32_t gp2x_state::sdcard_r()
{
	return 0xffff<<16;  // at 3e146b0 - indicate timeout & CRC error
}

void gp2x_state::gp2x_map(address_map &map)
{
	map(0x00000000, 0x00007fff).rom();
	map(0x01000000, 0x04ffffff).ram().share("ram"); // 64 MB of RAM
	map(0x9c000000, 0x9c00001f).rw(FUNC(gp2x_state::nand_r), FUNC(gp2x_state::nand_w));
	map(0xc0000a00, 0xc0000a03).r(FUNC(gp2x_state::timer_r));
	map(0xc0001208, 0xc000120b).r(FUNC(gp2x_state::tx_status_r));
	map(0xc0001210, 0xc0001213).w(FUNC(gp2x_state::tx_xmit_w));
	map(0xc0001508, 0xc000150b).r(FUNC(gp2x_state::sdcard_r));
	map(0xc0002800, 0xc00029ff).rw(FUNC(gp2x_state::gp2x_lcdc_r), FUNC(gp2x_state::gp2x_lcdc_w));
	map(0xc0003a38, 0xc0003a3b).rw(FUNC(gp2x_state::nand_ctrl_r), FUNC(gp2x_state::nand_ctrl_w));
}

static INPUT_PORTS_START( gp2x )
INPUT_PORTS_END

void gp2x_state::gp2x(machine_config &config)
{
	ARM9(config, m_maincpu, 80000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &gp2x_state::gp2x_map);

	PALETTE(config, "palette").set_entries(32768);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(320, 240);
	screen.set_visarea(0, 319, 0, 239);
	screen.set_screen_update(FUNC(gp2x_state::screen_update_gp2x));

	SPEAKER(config, "speaker", 2).front();
}

} // anonymous namespace


ROM_START(gp2x)
	ROM_REGION( 0x600000, "maincpu", 0 )    // contents of NAND flash
	ROM_SYSTEM_BIOS(0, "v2", "version 2.0")
	ROMX_LOAD( "gp2xboot.img",   0x000000, 0x05b264, CRC(9d82937e) SHA1(9655f04c11f78526b3b8a4613897070df3119ead), ROM_BIOS(0))
	ROMX_LOAD( "gp2xkernel.img", 0x080000, 0x09a899, CRC(e272eac9) SHA1(9d814ad6c27d22812ba3f101a7358698d1b035eb), ROM_BIOS(0))
	ROMX_LOAD( "gp2xsound.wav",  0x1a0000, 0x03a42c, CRC(b6ac0ade) SHA1(32362ebbcd09a3b15e164ec3fbd2a8f11159bcd7), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "v3", "version 3.0")
	ROMX_LOAD( "gp2xboot.v3",    0x000000, 0x05b264, CRC(ab1fc556) SHA1(2ce66fec325b6e8e29f540322aefb435d8e3baf0), ROM_BIOS(1))
	ROMX_LOAD( "gp2xkernel.v3",  0x080000, 0x09f047, CRC(6f8922ae) SHA1(0f44204757f9251c7d68560ff31a1ce72f16698e), ROM_BIOS(1))
	ROMX_LOAD( "gp2xsound.wav",  0x1a0000, 0x03a42c, CRC(b6ac0ade) SHA1(32362ebbcd09a3b15e164ec3fbd2a8f11159bcd7), ROM_BIOS(1))
	ROMX_LOAD( "gp2xyaffs.v3",   0x300000, 0x2ec4d0, CRC(f81a4a57) SHA1(73bb84798ad3a4e281612a47abb2da4772cbe6cd), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "v4", "version 4.0")
	ROMX_LOAD( "gp2xboot.v4",    0x000000, 0x05b264, CRC(160af379) SHA1(0940fc975960fa52f32a9258a9480cc5cb1140e2), ROM_BIOS(2))
	ROMX_LOAD( "gp2xkernel.v4",  0x080000, 0x0a43a8, CRC(77b1cf9c) SHA1(7e759e4581399bfbee982e1b6c3b54a10b0e9c3d), ROM_BIOS(2))
	ROMX_LOAD( "gp2xsound.wav",  0x1a0000, 0x03a42c, CRC(b6ac0ade) SHA1(32362ebbcd09a3b15e164ec3fbd2a8f11159bcd7), ROM_BIOS(2))
	ROMX_LOAD( "gp2xyaffs.v4",   0x300000, 0x2dfed0, CRC(e77efc53) SHA1(21477ff77aacb84005bc465a03066d71031a6098), ROM_BIOS(2))
ROM_END

CONS(2005, gp2x, 0, 0, gp2x, gp2x, gp2x_state, empty_init, "Game Park Holdings", "GP2X", MACHINE_NOT_WORKING|MACHINE_NO_SOUND)



gp32.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Tim Schuerewegen
/**************************************************************************
 *
 * gp32.cpp - Game Park GP32
 * Driver by Tim Schuerewegen
 *
 * CPU: Samsung S3C2400X01 SoC
 * S3C2400X01 consists of:
 *    ARM920T CPU core + MMU
 *    LCD controller
 *    DMA controller
 *    Interrupt controller
 *    USB controller
 *    and more.
 *
 * TODO:
 * - device-ify s3c240x;
 * - console screen is horizontal, but here screen is setted up with
 *   Height < Width and ROT270, in a double negation fashion. Simplify and
 *   eventually update video fns;
 * - Normalize palette to actual TFT color space;
 * - Several games have dubious sound clipping and mixing;
 * - RF and internet comms & netplay (rallypop has both);
 * - Games from SW list doesn't reload after save, is it even supported?
 * - Add slot for USB PC-Link application, add a host machine connection
 *   somehow;
 * - Verify MP3 support, which in turn needs checking out how the filesystem
 *   works here (and eventually a tool for direct injecting);
 * - Verify gp32linux distro;
 *
 **************************************************************************/

#include "emu.h"
#include "gp32.h"

#include "cpu/arm7/arm7.h"

#include "softlist_dev.h"
#include "speaker.h"

#define LOG_STARTSTOP (1U << 1)
#define LOG_TIMER     (1U << 2)
#define LOG_VRAM      (1U << 3)
#define LOG_MISC      (1U << 4)
#define LOG_TRACE     (1U << 5)

#define VERBOSE LOG_GENERAL

#include "logmacro.h"

#define CLOCK_MULTIPLIER 1

#define MPLLCON  1
#define UPLLCON  2

#define BITS(x,m,n) (((x)>>(n))&((1<<((m)-(n)+1))-1))


// LCD CONTROLLER


#define BPPMODE_TFT_01  0x08
#define BPPMODE_TFT_02  0x09
#define BPPMODE_TFT_04  0x0A
#define BPPMODE_TFT_08  0x0B
#define BPPMODE_TFT_16  0x0C

inline rgb_t gp32_state::s3c240x_get_color_5551( uint16_t data)
{
	uint8_t r, g, b, i;
	r = BITS( data, 15, 11) << 3;
	g = BITS( data, 10, 6) << 3;
	b = BITS( data, 5, 1) << 3;
	i = BIT( data, 1) << 2;
	return rgb_t( r | i, g | i, b | i);
}

void gp32_state::s3c240x_lcd_dma_reload()
{
	m_s3c240x_lcd.vramaddr_cur = m_s3c240x_lcd_regs[5] << 1;
	m_s3c240x_lcd.vramaddr_max = ((m_s3c240x_lcd_regs[5] & 0xFFE00000) | m_s3c240x_lcd_regs[6]) << 1;
	m_s3c240x_lcd.offsize = BITS( m_s3c240x_lcd_regs[7], 21, 11);
	m_s3c240x_lcd.pagewidth_cur = 0;
	m_s3c240x_lcd.pagewidth_max = BITS( m_s3c240x_lcd_regs[7], 10, 0);
	LOGMASKED(LOG_VRAM, "LCD - vramaddr %08X %08X offsize %08X pagewidth %08X\n", m_s3c240x_lcd.vramaddr_cur, m_s3c240x_lcd.vramaddr_max, m_s3c240x_lcd.offsize, m_s3c240x_lcd.pagewidth_max);
}

void gp32_state::s3c240x_lcd_dma_init()
{
	s3c240x_lcd_dma_reload();
	m_s3c240x_lcd.bppmode = BITS( m_s3c240x_lcd_regs[0], 4, 1);
	m_s3c240x_lcd.bswp = BIT( m_s3c240x_lcd_regs[4], 1);
	m_s3c240x_lcd.hwswp = BIT( m_s3c240x_lcd_regs[4], 0);
	m_s3c240x_lcd.lineval = BITS( m_s3c240x_lcd_regs[1], 23, 14);
	m_s3c240x_lcd.hozval = BITS( m_s3c240x_lcd_regs[2], 18, 8);
}

uint32_t gp32_state::s3c240x_lcd_dma_read( )
{
	uint8_t *vram, data[4];
	int i;
	for (i = 0; i < 2; i++)
	{
		vram = (uint8_t *)m_s3c240x_ram.target() + m_s3c240x_lcd.vramaddr_cur - 0x0C000000;
		data[i*2+0] = vram[0];
		data[i*2+1] = vram[1];
		m_s3c240x_lcd.vramaddr_cur += 2;
		m_s3c240x_lcd.pagewidth_cur++;
		if (m_s3c240x_lcd.pagewidth_cur >= m_s3c240x_lcd.pagewidth_max)
		{
			m_s3c240x_lcd.vramaddr_cur += m_s3c240x_lcd.offsize << 1;
			m_s3c240x_lcd.pagewidth_cur = 0;
		}
	}
	if (m_s3c240x_lcd.hwswp == 0)
	{
		if (m_s3c240x_lcd.bswp == 0)
		{
			return (data[3] << 24) | (data[2] << 16) | (data[1] << 8) | (data[0] << 0);
		}
		else
		{
			return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3] << 0);
		}
	}
	else
	{
		if (m_s3c240x_lcd.bswp == 0)
		{
			return (data[1] << 24) | (data[0] << 16) | (data[3] << 8) | (data[2] << 0);
		}
		else
		{
			return (data[2] << 24) | (data[3] << 16) | (data[0] << 8) | (data[1] << 0);
		}
	}
}

void gp32_state::s3c240x_lcd_render_01( )
{
	bitmap_rgb32 &bitmap = m_bitmap;
	uint32_t *scanline = &bitmap.pix(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
	for (int i = 0; i < 4; i++)
	{
		uint32_t data = s3c240x_lcd_dma_read();
		for (int j = 0; j < 32; j++)
		{
			*scanline++ = m_palette->pen_color((data >> 31) & 0x01);
			data <<= 1;
			m_s3c240x_lcd.hpos++;
			if (m_s3c240x_lcd.hpos >= (m_s3c240x_lcd.pagewidth_max << 4))
			{
				m_s3c240x_lcd.vpos = (m_s3c240x_lcd.vpos + 1) % (m_s3c240x_lcd.lineval + 1);
				m_s3c240x_lcd.hpos = 0;
				scanline = &bitmap.pix(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
			}
		}
	}
}

void gp32_state::s3c240x_lcd_render_02( )
{
	bitmap_rgb32 &bitmap = m_bitmap;
	uint32_t *scanline = &bitmap.pix(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
	for (int i = 0; i < 4; i++)
	{
		uint32_t data = s3c240x_lcd_dma_read();
		for (int j = 0; j < 16; j++)
		{
			*scanline++ = m_palette->pen_color((data >> 30) & 0x03);
			data <<= 2;
			m_s3c240x_lcd.hpos++;
			if (m_s3c240x_lcd.hpos >= (m_s3c240x_lcd.pagewidth_max << 3))
			{
				m_s3c240x_lcd.vpos = (m_s3c240x_lcd.vpos + 1) % (m_s3c240x_lcd.lineval + 1);
				m_s3c240x_lcd.hpos = 0;
				scanline = &bitmap.pix(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
			}
		}
	}
}

void gp32_state::s3c240x_lcd_render_04( )
{
	bitmap_rgb32 &bitmap = m_bitmap;
	uint32_t *scanline = &bitmap.pix(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
	for (int i = 0; i < 4; i++)
	{
		uint32_t data = s3c240x_lcd_dma_read( );
		for (int j = 0; j < 8; j++)
		{
			*scanline++ = m_palette->pen_color((data >> 28) & 0x0F);
			data <<= 4;
			m_s3c240x_lcd.hpos++;
			if (m_s3c240x_lcd.hpos >= (m_s3c240x_lcd.pagewidth_max << 2))
			{
				m_s3c240x_lcd.vpos = (m_s3c240x_lcd.vpos + 1) % (m_s3c240x_lcd.lineval + 1);
				m_s3c240x_lcd.hpos = 0;
				scanline = &bitmap.pix(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
			}
		}
	}
}

void gp32_state::s3c240x_lcd_render_08( )
{
	bitmap_rgb32 &bitmap = m_bitmap;
	uint32_t *scanline = &bitmap.pix(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
	for (int i = 0; i < 4; i++)
	{
		uint32_t data = s3c240x_lcd_dma_read();
		for (int j = 0; j < 4; j++)
		{
			*scanline++ = m_palette->pen_color((data >> 24) & 0xFF);
			data <<= 8;
			m_s3c240x_lcd.hpos++;
			if (m_s3c240x_lcd.hpos >= (m_s3c240x_lcd.pagewidth_max << 1))
			{
				m_s3c240x_lcd.vpos = (m_s3c240x_lcd.vpos + 1) % (m_s3c240x_lcd.lineval + 1);
				m_s3c240x_lcd.hpos = 0;
				scanline = &bitmap.pix(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
			}
		}
	}
}

void gp32_state::s3c240x_lcd_render_16( )
{
	bitmap_rgb32 &bitmap = m_bitmap;
	uint32_t *scanline = &bitmap.pix(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
	for (int i = 0; i < 4; i++)
	{
		uint32_t data = s3c240x_lcd_dma_read();
		for (int j = 0; j < 2; j++)
		{
			*scanline++ = s3c240x_get_color_5551( (data >> 16) & 0xFFFF);
			data <<= 16;
			m_s3c240x_lcd.hpos++;
			if (m_s3c240x_lcd.hpos >= (m_s3c240x_lcd.pagewidth_max << 0))
			{
				m_s3c240x_lcd.vpos = (m_s3c240x_lcd.vpos + 1) % (m_s3c240x_lcd.lineval + 1);
				m_s3c240x_lcd.hpos = 0;
				scanline = &bitmap.pix(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
			}
		}
	}
}

TIMER_CALLBACK_MEMBER(gp32_state::s3c240x_lcd_timer_exp)
{
	LOGMASKED(LOG_TIMER, "LCD timer callback\n");
	m_s3c240x_lcd.vpos = m_screen->vpos();
	m_s3c240x_lcd.hpos = m_screen->hpos();
	LOGMASKED(LOG_VRAM, "LCD - vpos %d hpos %d\n", m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
	if (m_s3c240x_lcd.vramaddr_cur >= m_s3c240x_lcd.vramaddr_max)
	{
		s3c240x_lcd_dma_reload();
	}
	LOGMASKED(LOG_VRAM, "LCD - vramaddr %08X\n", m_s3c240x_lcd.vramaddr_cur);
	while (m_s3c240x_lcd.vramaddr_cur < m_s3c240x_lcd.vramaddr_max)
	{
		switch (m_s3c240x_lcd.bppmode)
		{
			case BPPMODE_TFT_01 : s3c240x_lcd_render_01(); break;
			case BPPMODE_TFT_02 : s3c240x_lcd_render_02(); break;
			case BPPMODE_TFT_04 : s3c240x_lcd_render_04(); break;
			case BPPMODE_TFT_08 : s3c240x_lcd_render_08(); break;
			case BPPMODE_TFT_16 : s3c240x_lcd_render_16(); break;
			default : LOG("s3c240x_lcd_timer_exp: bppmode %d not supported\n", m_s3c240x_lcd.bppmode); break;
		}
		if ((m_s3c240x_lcd.vpos == 0) && (m_s3c240x_lcd.hpos == 0)) break;
	}
	m_s3c240x_lcd_timer->adjust(m_screen->time_until_pos(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos));
}

void gp32_state::video_start()
{
	m_screen->register_screen_bitmap(m_bitmap);
}

uint32_t gp32_state::screen_update_gp32(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
	s3c240x_lcd_dma_init();
	return 0;
}

uint32_t gp32_state::s3c240x_lcd_r(offs_t offset)
{
	uint32_t data = m_s3c240x_lcd_regs[offset];
	switch (offset)
	{
		// LCDCON1
		case 0x00 / 4 :
		{
			// make sure line counter is going
			uint32_t lineval = BITS( m_s3c240x_lcd_regs[1], 23, 14);
			data = (data & ~0xFFFC0000) | ((lineval - m_screen->vpos()) << 18);
		}
		break;
	}
	LOGMASKED(LOG_TRACE, "(LCD) %08X -> %08X %s\n", 0x14A00000 + (offset << 2), data, machine().describe_context());
	return data;
}

void gp32_state::s3c240x_lcd_configure()
{
	uint32_t vspw, vbpd, lineval, vfpd, hspw, hbpd, hfpd, hozval, clkval, hclk;
	double framerate, vclk;
	rectangle visarea;
	vspw = BITS( m_s3c240x_lcd_regs[1], 5, 0);
	vbpd = BITS( m_s3c240x_lcd_regs[1], 31, 24);
	lineval = BITS( m_s3c240x_lcd_regs[1], 23, 14);
	vfpd = BITS( m_s3c240x_lcd_regs[1], 13, 6);
	hspw = BITS( m_s3c240x_lcd_regs[3], 7, 0);
	hbpd = BITS( m_s3c240x_lcd_regs[2], 25, 19);
	hfpd = BITS( m_s3c240x_lcd_regs[2], 7, 0);
	hozval = BITS( m_s3c240x_lcd_regs[2], 18, 8);
	clkval = BITS( m_s3c240x_lcd_regs[0], 17, 8);
	hclk = s3c240x_get_hclk(MPLLCON);
	LOGMASKED(LOG_VRAM, "LCD - vspw %d vbpd %d lineval %d vfpd %d hspw %d hbpd %d hfpd %d hozval %d clkval %d hclk %d\n", vspw, vbpd, lineval, vfpd, hspw, hbpd, hfpd, hozval, clkval, hclk);
	vclk = (double)(hclk / ((clkval + 1) * 2));
	LOGMASKED(LOG_VRAM, "LCD - vclk %f\n", vclk);
	framerate = vclk / (((vspw + 1) + (vbpd + 1) + (lineval + 1) + (vfpd + 1)) * ((hspw + 1) + (hbpd + 1) + (hfpd + 1) + (hozval + 1)));
	LOGMASKED(LOG_VRAM, "LCD - framerate %f\n", framerate);
	visarea.set(0, hozval, 0, lineval);
	LOGMASKED(LOG_VRAM, "LCD - visarea min_x %d min_y %d max_x %d max_y %d\n", visarea.min_x, visarea.min_y, visarea.max_x, visarea.max_y);
	m_screen->configure(hozval + 1, lineval + 1, visarea, HZ_TO_ATTOSECONDS( framerate));
}

void gp32_state::s3c240x_lcd_start()
{
	LOGMASKED(LOG_STARTSTOP, "LCD start\n");
	s3c240x_lcd_configure();
	s3c240x_lcd_dma_init();
	m_s3c240x_lcd_timer->adjust(m_screen->time_until_pos(0, 0));
}

void gp32_state::s3c240x_lcd_stop()
{
	LOGMASKED(LOG_STARTSTOP, "LCD stop\n");
	m_s3c240x_lcd_timer->adjust( attotime::never);
}

void gp32_state::s3c240x_lcd_recalc()
{
	if (m_s3c240x_lcd_regs[0] & 1)
	{
		s3c240x_lcd_start();
	}
	else
	{
		s3c240x_lcd_stop();
	}
}

void gp32_state::s3c240x_lcd_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	uint32_t old_value = m_s3c240x_lcd_regs[offset];
	LOGMASKED(LOG_TRACE, "(LCD) %08X <- %08X %s\n", 0x14A00000 + (offset << 2), data, machine().describe_context());
	COMBINE_DATA(&m_s3c240x_lcd_regs[offset]);
	switch (offset)
	{
		// LCDCON1
		case 0x00 / 4 :
		{
			if ((old_value & 1) != (data & 1))
			{
				s3c240x_lcd_recalc();
			}
		}
		break;
	}
}

// LCD PALETTE


uint32_t gp32_state::s3c240x_lcd_palette_r(offs_t offset)
{
	uint32_t data = m_s3c240x_lcd_palette[offset];
	LOGMASKED(LOG_TRACE, "(LCD) %08X -> %08X %s\n", 0x14A00400 + (offset << 2), data, machine().describe_context());
	return data;
}

void gp32_state::s3c240x_lcd_palette_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOGMASKED(LOG_TRACE, "(LCD) %08X <- %08X %s\n", 0x14A00400 + (offset << 2), data, machine().describe_context());
	COMBINE_DATA(&m_s3c240x_lcd_palette[offset]);
	if (mem_mask != 0xffffffff)
	{
		LOG("s3c240x_lcd_palette_w: unknown mask %08x\n", mem_mask);
	}
	m_palette->set_pen_color( offset, s3c240x_get_color_5551( data & 0xFFFF));
}

// CLOCK & POWER MANAGEMENT


uint32_t gp32_state::s3c240x_get_fclk(int reg)
{
	uint32_t data, mdiv, pdiv, sdiv;
	data = m_s3c240x_clkpow_regs[reg]; // MPLLCON or UPLLCON
	mdiv = BITS( data, 19, 12);
	pdiv = BITS( data, 9, 4);
	sdiv = BITS( data, 1, 0);
	return (uint32_t)((double)((mdiv + 8) * 12000000) / (double)((pdiv + 2) * (1 << sdiv)));
}

uint32_t gp32_state::s3c240x_get_hclk(int reg)
{
	switch (m_s3c240x_clkpow_regs[5] & 0x3) // CLKDIVN
	{
		case 0 : return s3c240x_get_fclk(reg) / 1;
		case 1 : return s3c240x_get_fclk(reg) / 1;
		case 2 : return s3c240x_get_fclk(reg) / 2;
		case 3 : return s3c240x_get_fclk(reg) / 2;
	}
	return 0;
}

uint32_t gp32_state::s3c240x_get_pclk(int reg)
{
	switch (m_s3c240x_clkpow_regs[5] & 0x3) // CLKDIVN
	{
		case 0 : return s3c240x_get_fclk(reg) / 1;
		case 1 : return s3c240x_get_fclk(reg) / 2;
		case 2 : return s3c240x_get_fclk(reg) / 2;
		case 3 : return s3c240x_get_fclk(reg) / 4;
	}
	return 0;
}

uint32_t gp32_state::s3c240x_clkpow_r(offs_t offset)
{
	uint32_t data = m_s3c240x_clkpow_regs[offset];
	LOGMASKED(LOG_TRACE, "(CLKPOW) %08X -> %08X %s\n", 0x14800000 + (offset << 2), data, machine().describe_context());
	return data;
}

void gp32_state::s3c240x_clkpow_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOGMASKED(LOG_TRACE, "(CLKPOW) %08X <- %08X %s\n", 0x14800000 + (offset << 2), data, machine().describe_context());
	COMBINE_DATA(&m_s3c240x_clkpow_regs[offset]);
	switch (offset)
	{
		// MPLLCON
		case 0x04 / 4 :
		{
			m_maincpu->set_unscaled_clock(s3c240x_get_fclk(MPLLCON) * CLOCK_MULTIPLIER);
		}
		break;
	}
}

// INTERRUPT CONTROLLER


void gp32_state::s3c240x_check_pending_irq()
{
	if (m_s3c240x_irq_regs[0] != 0)
	{
		uint32_t int_type = 0, temp;
		temp = m_s3c240x_irq_regs[0];
		while (!(temp & 1))
		{
			int_type++;
			temp = temp >> 1;
		}
		m_s3c240x_irq_regs[4] |= (1 << int_type); // INTPND
		m_s3c240x_irq_regs[5] = int_type; // INTOFFSET
		m_maincpu->set_input_line(arm7_cpu_device::ARM7_IRQ_LINE, ASSERT_LINE);
	}
	else
	{
		m_maincpu->set_input_line(arm7_cpu_device::ARM7_IRQ_LINE, CLEAR_LINE);
	}
}

void gp32_state::s3c240x_request_irq(uint32_t int_type)
{
	LOGMASKED(LOG_MISC, "request irq %d\n", int_type);
	if (m_s3c240x_irq_regs[0] == 0)
	{
		m_s3c240x_irq_regs[0] |= (1 << int_type); // SRCPND
		m_s3c240x_irq_regs[4] |= (1 << int_type); // INTPND
		m_s3c240x_irq_regs[5] = int_type; // INTOFFSET
		m_maincpu->set_input_line(arm7_cpu_device::ARM7_IRQ_LINE, ASSERT_LINE);
	}
	else
	{
		m_s3c240x_irq_regs[0] |= (1 << int_type); // SRCPND
		s3c240x_check_pending_irq();
	}
}


uint32_t gp32_state::s3c240x_irq_r(offs_t offset)
{
	uint32_t data = m_s3c240x_irq_regs[offset];
	LOGMASKED(LOG_TRACE, "(IRQ) %08X -> %08X %s\n", 0x14400000 + (offset << 2), data, machine().describe_context());
	return data;
}

void gp32_state::s3c240x_irq_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	uint32_t old_value = m_s3c240x_irq_regs[offset];
	LOGMASKED(LOG_TRACE, "(IRQ) %08X <- %08X %s\n", 0x14400000 + (offset << 2), data, machine().describe_context());
	COMBINE_DATA(&m_s3c240x_irq_regs[offset]);
	switch (offset)
	{
		// SRCPND
		case 0x00 / 4 :
		{
			m_s3c240x_irq_regs[0] = (old_value & ~data); // clear only the bit positions of SRCPND corresponding to those set to one in the data
			s3c240x_check_pending_irq();
		}
		break;
		// INTPND
		case 0x10 / 4 :
		{
			m_s3c240x_irq_regs[4] = (old_value & ~data); // clear only the bit positions of INTPND corresponding to those set to one in the data
		}
		break;
	}
}

// PWM TIMER

#if 0
static const char *const timer_reg_names[] =
{
	"Timer config 0",
	"Timer config 1",
	"Timer control",
	"Timer count buffer 0",
	"Timer compare buffer 0",
	"Timer count observation 0",
	"Timer count buffer 1",
	"Timer compare buffer 1",
	"Timer count observation 1",
	"Timer count buffer 2",
	"Timer compare buffer 2",
	"Timer count observation 2",
	"Timer count buffer 3",
	"Timer compare buffer 3",
	"Timer count observation 3",
	"Timer count buffer 4",
	"Timer compare buffer 4",
	"Timer count observation 4",
};
#endif


uint32_t gp32_state::s3c240x_pwm_r(offs_t offset)
{
	uint32_t data = m_s3c240x_pwm_regs[offset];
	LOGMASKED(LOG_TRACE, "(PWM) %08X -> %08X %s\n", 0x15100000 + (offset << 2), data, machine().describe_context());
	return data;
}

void gp32_state::s3c240x_pwm_start(int timer)
{
	static const int mux_table[] = { 2, 4, 8, 16 };
	static const int prescaler_shift[] = { 0, 0, 8, 8, 8 };
	static const int mux_shift[] = { 0, 4, 8, 12, 16 };
	static const int tcon_shift[] = { 0, 8, 12, 16, 20 };
	const uint32_t *regs = &m_s3c240x_pwm_regs[3+timer*3];
	uint32_t prescaler, mux, cnt, cmp, auto_reload;
	double freq, hz;
	LOGMASKED(LOG_STARTSTOP, "PWM %d start\n", timer);
	prescaler = (m_s3c240x_pwm_regs[0] >> prescaler_shift[timer]) & 0xFF;
	mux = (m_s3c240x_pwm_regs[1] >> mux_shift[timer]) & 0x0F;
	freq = s3c240x_get_pclk(MPLLCON) / (prescaler + 1) / mux_table[mux];
	cnt = BITS( regs[0], 15, 0);
	if (timer != 4)
	{
		cmp = BITS( regs[1], 15, 0);
		auto_reload = BIT( m_s3c240x_pwm_regs[2], tcon_shift[timer] + 3);
	}
	else
	{
		cmp = 0;
		auto_reload = BIT( m_s3c240x_pwm_regs[2], tcon_shift[timer] + 2);
	}
	hz = freq / (cnt - cmp + 1);
	LOGMASKED(LOG_MISC, "PWM %d - FCLK=%d HCLK=%d PCLK=%d prescaler=%d div=%d freq=%f cnt=%d cmp=%d auto_reload=%d hz=%f\n", timer, s3c240x_get_fclk(MPLLCON), s3c240x_get_hclk(MPLLCON), s3c240x_get_pclk(MPLLCON), prescaler, mux_table[mux], freq, cnt, cmp, auto_reload, hz);
	if (auto_reload)
	{
		m_s3c240x_pwm_timer[timer]->adjust( attotime::from_hz( hz), timer, attotime::from_hz( hz));
	}
	else
	{
		m_s3c240x_pwm_timer[timer]->adjust( attotime::from_hz( hz), timer);
	}
}

void gp32_state::s3c240x_pwm_stop(int timer)
{
	LOGMASKED(LOG_STARTSTOP, "PWM %d stop\n", timer);
	m_s3c240x_pwm_timer[timer]->adjust( attotime::never);
}

void gp32_state::s3c240x_pwm_recalc(int timer)
{
	static const int tcon_shift[] = { 0, 8, 12, 16, 20 };
	if (m_s3c240x_pwm_regs[2] & (1 << tcon_shift[timer]))
	{
		s3c240x_pwm_start(timer);
	}
	else
	{
		s3c240x_pwm_stop(timer);
	}
}

void gp32_state::s3c240x_pwm_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	uint32_t old_value = m_s3c240x_pwm_regs[offset];
	LOGMASKED(LOG_TRACE, "(PWM) %08X <- %08X %s\n", 0x15100000 + (offset << 2), data, machine().describe_context());
	COMBINE_DATA(&m_s3c240x_pwm_regs[offset]);
	switch (offset)
	{
		// TCON
		case 0x08 / 4 :
		{
			if ((data & 1) != (old_value & 1))
			{
				s3c240x_pwm_recalc(0);
			}
			if ((data & 0x100) != (old_value & 0x100))
			{
				s3c240x_pwm_recalc(1);
			}
			if ((data & 0x1000) != (old_value & 0x1000))
			{
				s3c240x_pwm_recalc(2);
			}
			if ((data & 0x10000) != (old_value & 0x10000))
			{
				s3c240x_pwm_recalc(3);
			}
			if ((data & 0x100000) != (old_value & 0x100000))
			{
				s3c240x_pwm_recalc(4);
			}
		}
	}
}

TIMER_CALLBACK_MEMBER(gp32_state::s3c240x_pwm_timer_exp)
{
	int ch = param;
	static const int ch_int[] = { INT_TIMER0, INT_TIMER1, INT_TIMER2, INT_TIMER3, INT_TIMER4 };
	LOGMASKED(LOG_TIMER, "PWM %d timer callback\n", ch);
	if (BITS( m_s3c240x_pwm_regs[1], 23, 20) == (ch + 1))
	{
		s3c240x_dma_request_pwm();
	}
	else
	{
		s3c240x_request_irq(ch_int[ch]);
	}
}

// DMA


void gp32_state::s3c240x_dma_reload(int dma)
{
	uint32_t *regs = &m_s3c240x_dma_regs[dma<<3];
	regs[3] = (regs[3] & ~0x000FFFFF) | BITS( regs[2], 19, 0);
	regs[4] = (regs[4] & ~0x1FFFFFFF) | BITS( regs[0], 28, 0);
	regs[5] = (regs[5] & ~0x1FFFFFFF) | BITS( regs[1], 28, 0);
}

void gp32_state::s3c240x_dma_trigger(int dma)
{
	uint32_t *regs = &m_s3c240x_dma_regs[dma<<3];
	uint32_t curr_tc, curr_src, curr_dst;
	address_space &space = m_maincpu->space( AS_PROGRAM);
	int dsz, inc_src, inc_dst, servmode;
	static const uint32_t ch_int[] = { INT_DMA0, INT_DMA1, INT_DMA2, INT_DMA3 };
	LOGMASKED(LOG_MISC, "DMA %d trigger\n", dma);
	curr_tc = BITS( regs[3], 19, 0);
	curr_src = BITS( regs[4], 28, 0);
	curr_dst = BITS( regs[5], 28, 0);
	dsz = BITS( regs[2], 21, 20);
	servmode = BIT( regs[2], 26);
	inc_src = BIT( regs[0], 29);
	inc_dst = BIT( regs[1], 29);
	LOGMASKED(LOG_MISC, "DMA %d - curr_src %08X curr_dst %08X curr_tc %d dsz %d\n", dma, curr_src, curr_dst, curr_tc, dsz);
	while (curr_tc > 0)
	{
		curr_tc--;
		switch (dsz)
		{
			case 0 : space.write_byte( curr_dst, space.read_byte( curr_src)); break;
			case 1 : space.write_word( curr_dst, space.read_word( curr_src)); break;
			case 2 : space.write_dword( curr_dst, space.read_dword( curr_src)); break;
		}
		if (inc_src == 0) curr_src += (1 << dsz);
		if (inc_dst == 0) curr_dst += (1 << dsz);
		if (servmode == 0) break;
	}
	// update curr_src
	regs[4] = (regs[4] & ~0x1FFFFFFF) | curr_src;
	// update curr_dst
	regs[5] = (regs[5] & ~0x1FFFFFFF) | curr_dst;
	// update curr_tc
	regs[3] = (regs[3] & ~0x000FFFFF) | curr_tc;
	// ...
	if (curr_tc == 0)
	{
		int _int, reload;
		reload = BIT( regs[2], 22);
		if (!reload)
		{
			s3c240x_dma_reload(dma);
		}
		else
		{
			regs[6] &= ~(1 << 1); // clear on/off
		}
		_int = BIT( regs[2], 28);
		if (_int)
		{
			s3c240x_request_irq(ch_int[dma]);
		}
	}
}

void gp32_state::s3c240x_dma_request_iis()
{
	uint32_t *regs = &m_s3c240x_dma_regs[2<<3];
	LOGMASKED(LOG_MISC, "s3c240x_dma_request_iis\n");
	if ((BIT( regs[6], 1) != 0) && (BIT( regs[2], 23) != 0) && (BITS( regs[2], 25, 24) == 0))
	{
		s3c240x_dma_trigger(2);
	}
}

void gp32_state::s3c240x_dma_request_pwm()
{
	int i;
	LOGMASKED(LOG_MISC, "s3c240x_dma_request_pwm\n");
	for (i = 0; i < 4; i++)
	{
		if (i != 1)
		{
			uint32_t *regs = &m_s3c240x_dma_regs[i<<3];
			if ((BIT( regs[6], 1) != 0) && (BIT( regs[2], 23) != 0) && (BITS( regs[2], 25, 24) == 3))
			{
				s3c240x_dma_trigger(i);
			}
		}
	}
}

void gp32_state::s3c240x_dma_start(int dma)
{
	uint32_t addr_src, addr_dst, tc;
	uint32_t *regs = &m_s3c240x_dma_regs[dma<<3];
	uint32_t dsz, tsz, reload;
	int inc_src, inc_dst, _int, servmode, swhwsel, hwsrcsel;
	LOGMASKED(LOG_STARTSTOP, "DMA %d start\n", dma);
	addr_src = BITS( regs[0], 28, 0);
	addr_dst = BITS( regs[1], 28, 0);
	tc = BITS( regs[2], 19, 0);
	inc_src = BIT( regs[0], 29);
	inc_dst = BIT( regs[1], 29);
	tsz = BIT( regs[2], 27);
	_int = BIT( regs[2], 28);
	servmode = BIT( regs[2], 26);
	hwsrcsel = BITS( regs[2], 25, 24);
	swhwsel = BIT( regs[2], 23);
	reload = BIT( regs[2], 22);
	dsz = BITS( regs[2], 21, 20);
	LOGMASKED(LOG_MISC, "DMA %d - addr_src %08X inc_src %d addr_dst %08X inc_dst %d int %d tsz %d servmode %d hwsrcsel %d swhwsel %d reload %d dsz %d tc %d\n", dma, addr_src, inc_src, addr_dst, inc_dst, _int, tsz, servmode, hwsrcsel, swhwsel, reload, dsz, tc);
	LOGMASKED(LOG_MISC, "DMA %d - copy %08X bytes from %08X (%s) to %08X (%s)\n", dma, tc << dsz, addr_src, inc_src ? "fix" : "inc", addr_dst, inc_dst ? "fix" : "inc");
	s3c240x_dma_reload(dma);
	if (swhwsel == 0)
	{
		s3c240x_dma_trigger(dma);
	}
}

void gp32_state::s3c240x_dma_stop(int dma)
{
	LOGMASKED(LOG_STARTSTOP, "DMA %d stop\n", dma);
}

void gp32_state::s3c240x_dma_recalc(int dma)
{
	if (m_s3c240x_dma_regs[(dma<<3)+6] & 2)
	{
		s3c240x_dma_start(dma);
	}
	else
	{
		s3c240x_dma_stop(dma);
	}
}

uint32_t gp32_state::s3c240x_dma_r(offs_t offset)
{
	uint32_t data = m_s3c240x_dma_regs[offset];
	LOGMASKED(LOG_TRACE, "(DMA) %08X -> %08X %s\n", 0x14600000 + (offset << 2), data, machine().describe_context());
	return data;
}

void gp32_state::s3c240x_dma_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	uint32_t old_value = m_s3c240x_dma_regs[offset];
	LOGMASKED(LOG_TRACE, "(DMA) %08X <- %08X %s\n", 0x14600000 + (offset << 2), data, machine().describe_context());
	COMBINE_DATA(&m_s3c240x_dma_regs[offset]);
	switch (offset)
	{
		// DCON0
		case 0x08 / 4 :
		{
			if (((data >> 22) & 1) != 0) // reload
			{
				m_s3c240x_dma_regs[0x18/4] &= ~(1 << 1); // clear on/off
			}
		}
		break;
		// DMASKTRIG0
		case 0x18 / 4 :
		{
			if ((old_value & 2) != (data & 2)) s3c240x_dma_recalc(0);
		}
		break;
		// DCON1
		case 0x28 / 4 :
		{
			if (((data >> 22) & 1) != 0) // reload
			{
				m_s3c240x_dma_regs[0x38/4] &= ~(1 << 1); // clear on/off
			}
		}
		break;
		// DMASKTRIG1
		case 0x38 / 4 :
		{
			if ((old_value & 2) != (data & 2)) s3c240x_dma_recalc(1);
		}
		break;
		// DCON2
		case 0x48 / 4 :
		{
			if (((data >> 22) & 1) != 0) // reload
			{
				m_s3c240x_dma_regs[0x58/4] &= ~(1 << 1); // clear on/off
			}
		}
		break;
		// DMASKTRIG2
		case 0x58 / 4 :
		{
			if ((old_value & 2) != (data & 2)) s3c240x_dma_recalc(2);
		}
		break;
		// DCON3
		case 0x68 / 4 :
		{
			if (((data >> 22) & 1) != 0) // reload
			{
				m_s3c240x_dma_regs[0x78/4] &= ~(1 << 1); // clear on/off
			}
		}
		break;
		// DMASKTRIG3
		case 0x78 / 4 :
		{
			if ((old_value & 2) != (data & 2)) s3c240x_dma_recalc(3);
		}
		break;
	}
}

TIMER_CALLBACK_MEMBER(gp32_state::s3c240x_dma_timer_exp)
{
	int ch = param;
	LOGMASKED(LOG_TIMER, "DMA %d timer callback\n", ch);
}

// SMARTMEDIA

void gp32_state::smc_reset()
{
	LOGMASKED(LOG_MISC, "smc_reset\n");
	m_smc.add_latch = 0;
	m_smc.chip = 0;
	m_smc.cmd_latch = 0;
	m_smc.do_read = 0;
	m_smc.do_write = 0;
	m_smc.read = 0;
	m_smc.wp = 0;
	m_smc.busy = 0;
}

void gp32_state::smc_init()
{
	LOGMASKED(LOG_MISC, "smc_init\n");
	smc_reset();
}

uint8_t gp32_state::smc_read()
{
	uint8_t data;
	data = m_smartmedia->data_r();
	LOGMASKED(LOG_MISC, "smc_read %08X\n", data);
	return data;
}

void gp32_state::smc_write(uint8_t data)
{
	LOGMASKED(LOG_MISC, "smc_write %08X\n", data);
	if ((m_smc.chip) && (!m_smc.read))
	{
		if (m_smc.cmd_latch)
		{
			LOGMASKED(LOG_MISC, "smartmedia_command_w %08X\n", data);
			m_smartmedia->command_w(data);
		}
		else if (m_smc.add_latch)
		{
			LOGMASKED(LOG_MISC, "smartmedia_address_w %08X\n", data);
			m_smartmedia->address_w(data);
		}
		else
		{
			LOGMASKED(LOG_MISC, "smartmedia_data_w %08X\n", data);
			m_smartmedia->data_w(data);
		}
	}
}

void gp32_state::smc_update()
{
	if (!m_smc.chip)
	{
		smc_reset();
	}
	else
	{
		if ((m_smc.do_write) && (!m_smc.read))
		{
			smc_write(m_smc.datatx);
		}
		else if ((!m_smc.do_write) && (m_smc.do_read) && (m_smc.read) && (!m_smc.cmd_latch) && (!m_smc.add_latch))
		{
			m_smc.datarx = smc_read();
		}
	}
}

// I2S

#define I2S_L3C ( 1 )
#define I2S_L3M ( 2 )
#define I2S_L3D ( 3 )

void gp32_state::i2s_reset()
{
	LOGMASKED(LOG_MISC, "i2s_reset\n");
	m_i2s.l3d = 0;
	m_i2s.l3m = 0;
	m_i2s.l3c = 0;
}

void gp32_state::i2s_init()
{
	LOGMASKED(LOG_MISC, "i2s_init\n");
	i2s_reset();
}

void gp32_state::i2s_write(int line, int data)
{
	switch (line)
	{
		case I2S_L3C :
		{
			if (data != m_i2s.l3c)
			{
				LOGMASKED(LOG_MISC, "I2S L3C %d\n", data);
				m_i2s.l3c = data;
			}
		}
		break;
		case I2S_L3M :
		{
			if (data != m_i2s.l3m)
			{
				LOGMASKED(LOG_MISC, "I2S L3M %d\n", data);
				m_i2s.l3m = data;
			}
		}
		break;
		case I2S_L3D :
		{
			if (data != m_i2s.l3d)
			{
				LOGMASKED(LOG_MISC, "I2S L3D %d\n", data);
				m_i2s.l3d = data;
			}
		}
		break;
	}
}

// I/O PORT


uint32_t gp32_state::s3c240x_gpio_r(offs_t offset)
{
	uint32_t data = m_s3c240x_gpio[offset];
	switch (offset)
	{
		// PBCON
		case 0x08 / 4 :
		{
			// smartmedia
			data = (data & ~0x00000001);
			if (!m_smc.read) data = data | 0x00000001;
		}
		break;
		// PBDAT
		case 0x0C / 4 :
		{
			// smartmedia
			data = (data & ~0x000000FF) | (m_smc.datarx & 0xFF);
			// buttons
			data = (data & ~0x0000FF00) | (m_io_in0->read() & 0x0000FF00);
		}
		break;
		// PDDAT
		case 0x24 / 4 :
		{
			// smartmedia
			data = (data & ~0x000003C0);
			if (!m_smc.busy) data = data | 0x00000200;
			if (!m_smc.do_read) data = data | 0x00000100;
			if (!m_smc.chip) data = data | 0x00000080;
			if (!m_smartmedia->is_protected()) data = data | 0x00000040;
		}
		break;
		// PEDAT
		case 0x30 / 4 :
		{
			// smartmedia
			data = (data & ~0x0000003C);
			if (m_smc.cmd_latch) data = data | 0x00000020;
			if (m_smc.add_latch) data = data | 0x00000010;
			if (!m_smc.do_write) data = data | 0x00000008;
			if (!m_smartmedia->is_present()) data = data | 0x00000004;
			// buttons
			data = (data & ~0x000000C0) | (m_io_in1->read() & 0x000000C0);
		}
		break;
	}
	LOGMASKED(LOG_TRACE, "(GPIO) %08X -> %08X %s\n", 0x15600000 + (offset << 2), data, machine().describe_context());
	return data;
}

void gp32_state::s3c240x_gpio_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	COMBINE_DATA(&m_s3c240x_gpio[offset]);
	LOGMASKED(LOG_TRACE, "(GPIO) %08X <- %08X %s\n", 0x15600000 + (offset << 2), data, machine().describe_context());
	switch (offset)
	{
		// PBCON
		case 0x08 / 4 :
		{
			// smartmedia
			m_smc.read = ((data & 0x00000001) == 0);
			smc_update();
		}
		break;
		// PBDAT
		case 0x0C / 4 :
		{
			// smartmedia
			m_smc.datatx = data & 0xFF;
		}
		break;
		// PDDAT
		case 0x24 / 4 :
		{
			// smartmedia
			m_smc.do_read = ((data & 0x00000100) == 0);
			m_smc.chip = ((data & 0x00000080) == 0);
			m_smc.wp = ((data & 0x00000040) == 0);
			smc_update();
		}
		break;
		// PEDAT
		case 0x30 / 4 :
		{
			// smartmedia
			m_smc.cmd_latch = ((data & 0x00000020) != 0);
			m_smc.add_latch = ((data & 0x00000010) != 0);
			m_smc.do_write = ((data & 0x00000008) == 0);
			smc_update();
			// sound
			i2s_write(I2S_L3D, (data & 0x00000800) ? 1 : 0);
			i2s_write(I2S_L3M, (data & 0x00000400) ? 1 : 0);
			i2s_write(I2S_L3C, (data & 0x00000200) ? 1 : 0);
		}
		break;
#if 0
		// PGDAT
		case 0x48 / 4 :
		{
			int i2ssdo;
			i2ssdo = BIT( data, 3);
		}
		break;
#endif
	}
}

// MEMORY CONTROLLER


uint32_t gp32_state::s3c240x_memcon_r(offs_t offset)
{
	uint32_t data = m_s3c240x_memcon_regs[offset];
	LOGMASKED(LOG_TRACE, "(MEMCON) %08X -> %08X %s\n", 0x14000000 + (offset << 2), data, machine().describe_context());
	return data;
}

void gp32_state::s3c240x_memcon_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOGMASKED(LOG_TRACE, "(MEMCON) %08X <- %08X %s\n", 0x14000000 + (offset << 2), data, machine().describe_context());
	COMBINE_DATA(&m_s3c240x_memcon_regs[offset]);
}

// USB HOST CONTROLLER


uint32_t gp32_state::s3c240x_usb_host_r(offs_t offset)
{
	uint32_t data = m_s3c240x_usb_host_regs[offset];
	LOGMASKED(LOG_TRACE, "(USB H) %08X -> %08X %s\n", 0x14200000 + (offset << 2), data, machine().describe_context());
	return data;
}

void gp32_state::s3c240x_usb_host_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOGMASKED(LOG_TRACE, "(USB H) %08X <- %08X %s\n", 0x14200000 + (offset << 2), data, machine().describe_context());
	COMBINE_DATA(&m_s3c240x_usb_host_regs[offset]);
}

// UART 0


uint32_t gp32_state::s3c240x_uart_0_r(offs_t offset)
{
	uint32_t data = m_s3c240x_uart_0_regs[offset];
	switch (offset)
	{
		// UTRSTAT0
		case 0x10 / 4 :
		{
			data = (data & ~0x00000006) | 0x00000004 | 0x00000002; // [bit 2] Transmitter empty / [bit 1] Transmit buffer empty
		}
		break;
	}
	LOGMASKED(LOG_TRACE, "(UART 0) %08X -> %08X %s\n", 0x15000000 + (offset << 2), data, machine().describe_context());
	return data;
}

void gp32_state::s3c240x_uart_0_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOGMASKED(LOG_TRACE, "(UART 0) %08X <- %08X %s\n", 0x15000000 + (offset << 2), data, machine().describe_context());
	COMBINE_DATA(&m_s3c240x_uart_0_regs[offset]);
}

// UART 1


uint32_t gp32_state::s3c240x_uart_1_r(offs_t offset)
{
	uint32_t data = m_s3c240x_uart_1_regs[offset];
	switch (offset)
	{
		// UTRSTAT1
		case 0x10 / 4 :
		{
			data = (data & ~0x00000006) | 0x00000004 | 0x00000002; // [bit 2] Transmitter empty / [bit 1] Transmit buffer empty
		}
		break;
	}
	LOGMASKED(LOG_TRACE, "(UART 1) %08X -> %08X %s\n", 0x15004000 + (offset << 2), data, machine().describe_context());
	return data;
}

void gp32_state::s3c240x_uart_1_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOGMASKED(LOG_TRACE, "(UART 1) %08X <- %08X %s\n", 0x15004000 + (offset << 2), data, machine().describe_context());
	COMBINE_DATA(&m_s3c240x_uart_1_regs[offset]);
}

// USB DEVICE


uint32_t gp32_state::s3c240x_usb_device_r(offs_t offset)
{
	uint32_t data = m_s3c240x_usb_device_regs[offset];
	LOGMASKED(LOG_TRACE, "(USB D) %08X -> %08X %s\n", 0x15200140 + (offset << 2), data, machine().describe_context());
	return data;
}

void gp32_state::s3c240x_usb_device_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOGMASKED(LOG_TRACE, "(USB D) %08X <- %08X %s\n", 0x15200140 + (offset << 2), data, machine().describe_context());
	COMBINE_DATA(&m_s3c240x_usb_device_regs[offset]);
}

// WATCHDOG TIMER


uint32_t gp32_state::s3c240x_watchdog_r(offs_t offset)
{
	uint32_t data = m_s3c240x_watchdog_regs[offset];
	LOGMASKED(LOG_TRACE, "(WDOG) %08X -> %08X %s\n", 0x15300000 + (offset << 2), data, machine().describe_context());
	return data;
}

void gp32_state::s3c240x_watchdog_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOGMASKED(LOG_TRACE, "(WDOG) %08X <- %08X %s\n", 0x15300000 + (offset << 2), data, machine().describe_context());
	COMBINE_DATA(&m_s3c240x_watchdog_regs[offset]);
}

// EEPROM

uint8_t gp32_state::eeprom_read(uint16_t address)
{
	uint8_t data;
	data = m_eeprom_data[address];
	LOGMASKED(LOG_MISC, "EEPROM %04X -> %02X\n", address, data);
	return data;
}

void gp32_state::eeprom_write(uint16_t address, uint8_t data)
{
	LOGMASKED(LOG_MISC, "EEPROM %04X <- %02X\n", address, data);
	m_eeprom_data[address] = data;
}

// IIC

#if 0
uint8_t gp32_state::i2cmem_read_byte( int last)
{
	uint8_t data = 0;
	int i;
	i2cmem_write( machine, 0, I2CMEM_SDA, 1);
	for (i = 0; i < 8; i++)
	{
		i2cmem_write( machine, 0, I2CMEM_SCL, 1);
		data = (data << 1) + (i2cmem_read( machine, 0, I2CMEM_SDA) ? 1 : 0);
		i2cmem_write( machine, 0, I2CMEM_SCL, 0);
	}
	i2cmem_write( machine, 0, I2CMEM_SDA, last);
	i2cmem_write( machine, 0, I2CMEM_SCL, 1);
	i2cmem_write( machine, 0, I2CMEM_SCL, 0);
	return data;
}
#endif

#if 0
void gp32_state::i2cmem_write_byte( uint8_t data)
{
	int i;
	for (i = 0; i < 8; i++)
	{
		i2cmem_write( machine, 0, I2CMEM_SDA, (data & 0x80) ? 1 : 0);
		data = data << 1;
		i2cmem_write( machine, 0, I2CMEM_SCL, 1);
		i2cmem_write( machine, 0, I2CMEM_SCL, 0);
	}
	i2cmem_write( machine, 0, I2CMEM_SDA, 1); // ack bit
	i2cmem_write( machine, 0, I2CMEM_SCL, 1);
	i2cmem_write( machine, 0, I2CMEM_SCL, 0);
}
#endif

#if 0
void gp32_state::i2cmem_start( )
{
	i2cmem_write( machine, 0, I2CMEM_SDA, 1);
	i2cmem_write( machine, 0, I2CMEM_SCL, 1);
	i2cmem_write( machine, 0, I2CMEM_SDA, 0);
	i2cmem_write( machine, 0, I2CMEM_SCL, 0);
}
#endif

#if 0
void gp32_state::i2cmem_stop( )
{
	i2cmem_write( machine, 0, I2CMEM_SDA, 0);
	i2cmem_write( machine, 0, I2CMEM_SCL, 1);
	i2cmem_write( machine, 0, I2CMEM_SDA, 1);
	i2cmem_write( machine, 0, I2CMEM_SCL, 0);
}
#endif

void gp32_state::iic_start()
{
	LOGMASKED(LOG_STARTSTOP, "IIC start\n");
	m_s3c240x_iic.data_index = 0;
	m_s3c240x_iic_timer->adjust( attotime::from_msec( 1));
}

void gp32_state::iic_stop()
{
	LOGMASKED(LOG_STARTSTOP, "IIC stop\n");
	m_s3c240x_iic_timer->adjust( attotime::never);
}

void gp32_state::iic_resume()
{
	LOGMASKED(LOG_STARTSTOP, "IIC resume\n");
	m_s3c240x_iic_timer->adjust( attotime::from_msec( 1));
}

uint32_t gp32_state::s3c240x_iic_r(offs_t offset)
{
	uint32_t data = m_s3c240x_iic_regs[offset];
	switch (offset)
	{
		// IICSTAT
		case 0x04 / 4 :
		{
			data = data & ~0x0000000F;
		}
		break;
	}
	LOGMASKED(LOG_TRACE, "(IIC) %08X -> %08X %s\n", 0x15400000 + (offset << 2), data, machine().describe_context());
	return data;
}

void gp32_state::s3c240x_iic_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOGMASKED(LOG_TRACE, "(IIC) %08X <- %08X %s\n", 0x15400000 + (offset << 2), data, machine().describe_context());
	COMBINE_DATA(&m_s3c240x_iic_regs[offset]);
	switch (offset)
	{
		// ADDR_IICCON
		case 0x00 / 4 :
		{
			int interrupt_pending_flag;
#if 0
			static const int div_table[] = { 16, 512 };
			int enable_interrupt, transmit_clock_value, tx_clock_source_selection
			double clock;
			transmit_clock_value = (data >> 0) & 0xF;
			tx_clock_source_selection = (data >> 6) & 1;
			enable_interrupt = (data >> 5) & 1;
			clock = (double)(s3c240x_get_pclk(MPLLCON) / div_table[tx_clock_source_selection] / (transmit_clock_value + 1));
#endif
			interrupt_pending_flag = BIT( data, 4);
			if (interrupt_pending_flag == 0)
			{
				int start_stop_condition;
				start_stop_condition = BIT( m_s3c240x_iic_regs[1], 5);
				if (start_stop_condition != 0)
				{
					iic_resume();
				}
			}
		}
		break;
		// IICSTAT
		case 0x04 / 4 :
		{
			int start_stop_condition;
			start_stop_condition = BIT( data, 5);
			if (start_stop_condition != 0)
			{
				iic_start();
			}
			else
			{
				iic_stop();
			}
		}
		break;
	}
}

TIMER_CALLBACK_MEMBER(gp32_state::s3c240x_iic_timer_exp)
{
	int enable_interrupt, mode_selection;
	LOGMASKED(LOG_TIMER, "IIC timer callback\n");
	mode_selection = BITS( m_s3c240x_iic_regs[1], 7, 6);
	switch (mode_selection)
	{
		// master receive mode
		case 2 :
		{
			if (m_s3c240x_iic.data_index == 0)
			{
				uint8_t data_shift = m_s3c240x_iic_regs[3] & 0xFF;
				LOGMASKED(LOG_MISC, "IIC write %02X\n", data_shift);
			}
			else
			{
				uint8_t data_shift = eeprom_read(m_s3c240x_iic.address);
				LOGMASKED(LOG_MISC, "IIC read %02X\n", data_shift);
				m_s3c240x_iic_regs[3] = (m_s3c240x_iic_regs[3] & ~0xFF) | data_shift;
			}
			m_s3c240x_iic.data_index++;
		}
		break;
		// master transmit mode
		case 3 :
		{
			uint8_t data_shift = m_s3c240x_iic_regs[3] & 0xFF;
			LOGMASKED(LOG_MISC, "IIC write %02X\n", data_shift);
			m_s3c240x_iic.data[m_s3c240x_iic.data_index++] = data_shift;
			if (m_s3c240x_iic.data_index == 3)
			{
				m_s3c240x_iic.address = (m_s3c240x_iic.data[1] << 8) | m_s3c240x_iic.data[2];
			}
			if ((m_s3c240x_iic.data_index == 4) && (m_s3c240x_iic.data[0] == 0xA0))
			{
				eeprom_write(m_s3c240x_iic.address, data_shift);
			}
		}
		break;
	}
	enable_interrupt = BIT( m_s3c240x_iic_regs[0], 5);
	if (enable_interrupt)
	{
		s3c240x_request_irq(INT_IIC);
	}
}

// IIS

void gp32_state::s3c240x_iis_start()
{
	static const uint32_t codeclk_table[] = { 256, 384 };
	double freq;
	int prescaler_enable, prescaler_control_a, prescaler_control_b, codeclk;
	LOGMASKED(LOG_STARTSTOP, "IIS start\n");
	prescaler_enable = BIT( m_s3c240x_iis_regs[0], 1);
	prescaler_control_a = BITS( m_s3c240x_iis_regs[2], 9, 5);
	prescaler_control_b = BITS( m_s3c240x_iis_regs[2], 4, 0);
	codeclk = BIT( m_s3c240x_iis_regs[1], 2);
	freq = (double)(s3c240x_get_pclk(MPLLCON) / (prescaler_control_a + 1) / codeclk_table[codeclk]) * 2; // why do I have to multiply by two?
	LOGMASKED(LOG_MISC, "IIS - pclk %d psc_enable %d psc_a %d psc_b %d codeclk %d freq %f\n", s3c240x_get_pclk(MPLLCON), prescaler_enable, prescaler_control_a, prescaler_control_b, codeclk_table[codeclk], freq);
	m_s3c240x_iis_timer->adjust( attotime::from_hz( freq), 0, attotime::from_hz( freq));
}

void gp32_state::s3c240x_iis_stop()
{
	LOGMASKED(LOG_STARTSTOP, "IIS stop\n");
	m_s3c240x_iis_timer->adjust( attotime::never);
}

void gp32_state::s3c240x_iis_recalc()
{
	if (m_s3c240x_iis_regs[0] & 1)
	{
		s3c240x_iis_start();
	}
	else
	{
		s3c240x_iis_stop();
	}
}

uint32_t gp32_state::s3c240x_iis_r(offs_t offset)
{
	uint32_t data = m_s3c240x_iis_regs[offset];
#if 0
	switch (offset)
	{
		// IISCON
		case 0x00 / 4 :
		{
			data = data & ~1; // for mp3 player
		}
		break;
	}
#endif
	LOGMASKED(LOG_TRACE, "(IIS) %08X -> %08X %s\n", 0x15508000 + (offset << 2), data, machine().describe_context());
	return data;
}

void gp32_state::s3c240x_iis_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	uint32_t old_value = m_s3c240x_iis_regs[offset];
	LOGMASKED(LOG_TRACE, "(IIS) %08X <- %08X %s\n", 0x15508000 + (offset << 2), data, machine().describe_context());
	COMBINE_DATA(&m_s3c240x_iis_regs[offset]);
	switch (offset)
	{
		// IISCON
		case 0x00 / 4 :
		{
			if ((old_value & 1) != (data & 1)) s3c240x_iis_recalc();
		}
		break;
		// IISFIF
		case 0x10 / 4 :
		{
			if (ACCESSING_BITS_16_31)
			{
				m_s3c240x_iis.fifo[m_s3c240x_iis.fifo_index++] = BITS( data, 31, 16);
			}
			if (ACCESSING_BITS_0_15)
			{
				m_s3c240x_iis.fifo[m_s3c240x_iis.fifo_index++] = BITS( data, 15, 0);
			}
			if (m_s3c240x_iis.fifo_index == 2)
			{
				m_s3c240x_iis.fifo_index = 0;
				m_ldac->write(m_s3c240x_iis.fifo[0]);
				m_rdac->write(m_s3c240x_iis.fifo[1]);
			}
		}
		break;
	}
}

TIMER_CALLBACK_MEMBER(gp32_state::s3c240x_iis_timer_exp)
{
	LOGMASKED(LOG_TIMER, "IIS timer callback\n");
	s3c240x_dma_request_iis();
}

// RTC


uint32_t gp32_state::s3c240x_rtc_r(offs_t offset)
{
	uint32_t data = m_s3c240x_rtc_regs[offset];
	LOGMASKED(LOG_TRACE, "(RTC) %08X -> %08X %s\n", 0x15700040 + (offset << 2), data, machine().describe_context());
	return data;
}

void gp32_state::s3c240x_rtc_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOGMASKED(LOG_TRACE, "(RTC) %08X <- %08X %s\n", 0x15700040 + (offset << 2), data, machine().describe_context());
	COMBINE_DATA(&m_s3c240x_rtc_regs[offset]);
}

// A/D CONVERTER


uint32_t gp32_state::s3c240x_adc_r(offs_t offset)
{
	uint32_t data = m_s3c240x_adc_regs[offset];
	LOGMASKED(LOG_TRACE, "(ADC) %08X -> %08X %s\n", 0x15800000 + (offset << 2), data, machine().describe_context());
	return data;
}

void gp32_state::s3c240x_adc_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOGMASKED(LOG_TRACE, "(ADC) %08X <- %08X %s\n", 0x15800000 + (offset << 2), data, machine().describe_context());
	COMBINE_DATA(&m_s3c240x_adc_regs[offset]);
}

// SPI


uint32_t gp32_state::s3c240x_spi_r(offs_t offset)
{
	uint32_t data = m_s3c240x_spi_regs[offset];
	LOGMASKED(LOG_TRACE, "(SPI) %08X -> %08X %s\n", 0x15900000 + (offset << 2), data, machine().describe_context());
	return data;
}

void gp32_state::s3c240x_spi_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOGMASKED(LOG_TRACE, "(SPI) %08X <- %08X %s\n", 0x15900000 + (offset << 2), data, machine().describe_context());
	COMBINE_DATA(&m_s3c240x_spi_regs[offset]);
}

// MMC INTERFACE


uint32_t gp32_state::s3c240x_mmc_r(offs_t offset)
{
	uint32_t data = m_s3c240x_mmc_regs[offset];
	LOGMASKED(LOG_TRACE, "(MMC) %08X -> %08X %s\n", 0x15A00000 + (offset << 2), data, machine().describe_context());
	return data;
}

void gp32_state::s3c240x_mmc_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOGMASKED(LOG_TRACE, "(MMC) %08X <- %08X %s\n", 0x15A00000 + (offset << 2), data, machine().describe_context());
	COMBINE_DATA(&m_s3c240x_mmc_regs[offset]);
}

// ...

void gp32_state::s3c240x_machine_start()
{
	m_s3c240x_pwm_timer[0] = timer_alloc(FUNC(gp32_state::s3c240x_pwm_timer_exp), this);
	m_s3c240x_pwm_timer[1] = timer_alloc(FUNC(gp32_state::s3c240x_pwm_timer_exp), this);
	m_s3c240x_pwm_timer[2] = timer_alloc(FUNC(gp32_state::s3c240x_pwm_timer_exp), this);
	m_s3c240x_pwm_timer[3] = timer_alloc(FUNC(gp32_state::s3c240x_pwm_timer_exp), this);
	m_s3c240x_pwm_timer[4] = timer_alloc(FUNC(gp32_state::s3c240x_pwm_timer_exp), this);
	m_s3c240x_dma_timer[0] = timer_alloc(FUNC(gp32_state::s3c240x_dma_timer_exp), this);
	m_s3c240x_dma_timer[1] = timer_alloc(FUNC(gp32_state::s3c240x_dma_timer_exp), this);
	m_s3c240x_dma_timer[2] = timer_alloc(FUNC(gp32_state::s3c240x_dma_timer_exp), this);
	m_s3c240x_dma_timer[3] = timer_alloc(FUNC(gp32_state::s3c240x_dma_timer_exp), this);
	m_s3c240x_iic_timer = timer_alloc(FUNC(gp32_state::s3c240x_iic_timer_exp), this);
	m_s3c240x_iis_timer = timer_alloc(FUNC(gp32_state::s3c240x_iis_timer_exp), this);
	m_s3c240x_lcd_timer = timer_alloc(FUNC(gp32_state::s3c240x_lcd_timer_exp), this);
	m_eeprom_data = std::make_unique<uint8_t[]>(0x2000); // a dump of the EEPROM (S524AB0X91) resulted to be 0x1000
	m_nvram->set_base(m_eeprom_data.get(), 0x2000);
	smc_init();
	i2s_init();
}

void gp32_state::s3c240x_machine_reset()
{
	smc_reset();
	i2s_reset();
	m_s3c240x_iis.fifo_index = 0;
	m_s3c240x_iic.data_index = 0;
}

void gp32_state::gp32_map(address_map &map)
{
	map(0x00000000, 0x0007ffff).rom();
	map(0x0c000000, 0x0c7fffff).ram().share("s3c240x_ram");
	map(0x14000000, 0x1400003b).rw(FUNC(gp32_state::s3c240x_memcon_r), FUNC(gp32_state::s3c240x_memcon_w));
	map(0x14200000, 0x1420005b).rw(FUNC(gp32_state::s3c240x_usb_host_r), FUNC(gp32_state::s3c240x_usb_host_w));
	map(0x14400000, 0x14400017).rw(FUNC(gp32_state::s3c240x_irq_r), FUNC(gp32_state::s3c240x_irq_w));
	map(0x14600000, 0x1460007b).rw(FUNC(gp32_state::s3c240x_dma_r), FUNC(gp32_state::s3c240x_dma_w));
	map(0x14800000, 0x14800017).rw(FUNC(gp32_state::s3c240x_clkpow_r), FUNC(gp32_state::s3c240x_clkpow_w));
	map(0x14a00000, 0x14a003ff).rw(FUNC(gp32_state::s3c240x_lcd_r), FUNC(gp32_state::s3c240x_lcd_w));
	map(0x14a00400, 0x14a007ff).rw(FUNC(gp32_state::s3c240x_lcd_palette_r), FUNC(gp32_state::s3c240x_lcd_palette_w));
	map(0x15000000, 0x1500002b).rw(FUNC(gp32_state::s3c240x_uart_0_r), FUNC(gp32_state::s3c240x_uart_0_w));
	map(0x15004000, 0x1500402b).rw(FUNC(gp32_state::s3c240x_uart_1_r), FUNC(gp32_state::s3c240x_uart_1_w));
	map(0x15100000, 0x15100043).rw(FUNC(gp32_state::s3c240x_pwm_r), FUNC(gp32_state::s3c240x_pwm_w));
	map(0x15200140, 0x152001fb).rw(FUNC(gp32_state::s3c240x_usb_device_r), FUNC(gp32_state::s3c240x_usb_device_w));
	map(0x15300000, 0x1530000b).rw(FUNC(gp32_state::s3c240x_watchdog_r), FUNC(gp32_state::s3c240x_watchdog_w));
	map(0x15400000, 0x1540000f).rw(FUNC(gp32_state::s3c240x_iic_r), FUNC(gp32_state::s3c240x_iic_w));
	map(0x15508000, 0x15508013).rw(FUNC(gp32_state::s3c240x_iis_r), FUNC(gp32_state::s3c240x_iis_w));
	map(0x15600000, 0x1560005b).rw(FUNC(gp32_state::s3c240x_gpio_r), FUNC(gp32_state::s3c240x_gpio_w));
	map(0x15700040, 0x1570008b).rw(FUNC(gp32_state::s3c240x_rtc_r), FUNC(gp32_state::s3c240x_rtc_w));
	map(0x15800000, 0x15800007).rw(FUNC(gp32_state::s3c240x_adc_r), FUNC(gp32_state::s3c240x_adc_w));
	map(0x15900000, 0x15900017).rw(FUNC(gp32_state::s3c240x_spi_r), FUNC(gp32_state::s3c240x_spi_w));
	map(0x15a00000, 0x15a0003f).rw(FUNC(gp32_state::s3c240x_mmc_r), FUNC(gp32_state::s3c240x_mmc_w));
}

static INPUT_PORTS_START( gp32 )
	PORT_START("IN0")
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("R") PORT_PLAYER(1)
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("L") PORT_PLAYER(1)
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("B") PORT_PLAYER(1)
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("A") PORT_PLAYER(1)
	PORT_START("IN1")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_SELECT ) PORT_NAME("SELECT") PORT_PLAYER(1)
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_START ) PORT_NAME("START") PORT_PLAYER(1)
INPUT_PORTS_END

void gp32_state::machine_start()
{
	s3c240x_machine_start();
}

void gp32_state::machine_reset()
{
	s3c240x_machine_reset();
}

void gp32_state::gp32(machine_config &config)
{
	ARM9(config, m_maincpu, 40000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &gp32_state::gp32_map);

	PALETTE(config, m_palette).set_entries(32768);

	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	// TODO: bad setup that theoretically should fail a validation check plus console doesn't have vertical screen anyway
	// TODO: retrieve actual defaults from BIOS
	m_screen->set_size(240, 320);
	m_screen->set_visarea(0, 239, 0, 319);
	m_screen->set_screen_update(FUNC(gp32_state::screen_update_gp32));

	SPEAKER(config, "speaker", 2).front();
	DAC_16BIT_R2R_TWOS_COMPLEMENT(config, m_ldac, 0).add_route(ALL_OUTPUTS, "speaker", 1.0, 0); // unknown DAC
	DAC_16BIT_R2R_TWOS_COMPLEMENT(config, m_rdac, 0).add_route(ALL_OUTPUTS, "speaker", 1.0, 1); // unknown DAC

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	SMARTMEDIA(config, m_smartmedia, 0);

	SOFTWARE_LIST(config, "memc_list").set_original("gp32");
}

ROM_START( gp32 )
	ROM_REGION( 0x80000, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "157e", "Firmware 1.5.7 (English)" )
	ROMX_LOAD( "gp32157e.bin", 0x000000, 0x080000, CRC(b1e35643) SHA1(1566bc2a27980602e9eb501cf8b2d62939bfd1e5), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "100k", "Firmware 1.0.0 (Korean)" )
	ROMX_LOAD( "gp32100k.bin", 0x000000, 0x080000, CRC(d9925ac9) SHA1(3604d0d7210ed72eddd3e3e0c108f1102508423c), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "156k", "Firmware 1.5.6 (Korean)" )
	ROMX_LOAD( "gp32156k.bin", 0x000000, 0x080000, CRC(667fb1c8) SHA1(d179ab8e96411272b6a1d683e59da752067f9da8), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "166m", "Firmware 1.6.6 (European)" )
	ROMX_LOAD( "gp32166m.bin", 0x000000, 0x080000, CRC(4548a840) SHA1(1ad0cab0af28fb45c182e5e8c87ead2aaa4fffe1), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "mfv2", "Mr. Spiv Multi Firmware V2" )
	ROMX_LOAD( "gp32mfv2.bin", 0x000000, 0x080000, CRC(7ddaaaeb) SHA1(5a85278f721beb3b00125db5c912d1dc552c5897), ROM_BIOS(4) )

	ROM_REGION( 0x4000, "plds", ROMREGION_ERASEFF )
	ROM_LOAD( "x2c32.jed", 0, 0x3bbb, CRC(eeec10d8) SHA1(34c4b1b865511517a5de1fa352228d95cda387c5) ) // JEDEC format for the time being. X2C32: 32 Macrocell CoolRunner-II CPLD
ROM_END

CONS(2001, gp32, 0, 0, gp32, gp32, gp32_state, empty_init, "Game Park Holdings", "GP32", ROT270 | MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_COLORS )



grfd2301.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/************************************************************************************************************

Genrad Futuredata 2301 Network Control Processor

2013-08-11 Skeleton

Has a number of plug-in daughter boards, some of which are:
- CPU board: Mostek Z80 CPU, 24MHz crystal, 2 eproms (U2.PB72.2300.4039 1.0 ; U23.N.C.P.2300.4023 3.0)
- IO board: 5.0688MHz crystal, D8253C, 2x S2651
- Memory board: 16k static ram consisting of 32x TMS2147H-7 chips, 1 prom? with sticker U36.2300.4035 1.0
- 2301 board: 2x D8253C, 4x S2651

Back panel has a number of DB25 sockets, labelled thus:
- Station 1-4
- Station 5-8
- EIA 1
- EIA 2
- Printer
- 3 unlabelled ones

A sticker on the back panel says: GenRad, Culver City CA, Model 2301-9001


- No schematic or documents are available. Everything in this driver is a guess.
- Only one rom has been dumped. The best guess would be U23, as this is adjacent to the Z80.
- Although there's no display device, it has display ram. This has been hooked up with a chargen rom
  from another system.

*************************************************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/pit8253.h"
#include "machine/scn_pci.h"
#include "video/i8275.h"
#include "screen.h"


namespace {

class grfd2301_state : public driver_device
{
public:
	grfd2301_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_p_videoram(*this, "videoram")
		, m_maincpu(*this, "maincpu")
		, m_crtc(*this, "crtc")
		, m_p_chargen(*this, "chargen")
		, m_ma(0)
	{ }

	void grfd2301(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void drq_w(int state);
	void vrtc_w(int state);

	I8275_DRAW_CHARACTER_MEMBER(draw_character);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_shared_ptr<uint8_t> m_p_videoram;
	required_device<cpu_device> m_maincpu;
	required_device<i8275_device> m_crtc;
	required_region_ptr<u8> m_p_chargen;

	uint16_t m_ma;
};

void grfd2301_state::drq_w(int state)
{
	if (state)
	{
		m_crtc->dack_w(m_p_videoram[m_ma++]);
		m_ma &= 0x7ff;
	}
}

void grfd2301_state::vrtc_w(int state)
{
	if (state)
		m_ma = 0;
}

I8275_DRAW_CHARACTER_MEMBER(grfd2301_state::draw_character)
{
	using namespace i8275_attributes;

	// HACK: adjust for incorrect character generator
	u8 lc = (linecount - 1) & 0x0f;
	u8 gfx = BIT(attrcode, LTEN) ? 0xff : (BIT(attrcode, VSP) || lc > 8) ? 0 : m_p_chargen[(charcode << 4) | lc];
	if (BIT(attrcode, RVV))
		gfx ^= 0xff;
	for (int i = 8; --i >= 0; )
		bitmap.pix(y, x++) = BIT(gfx, i) ? rgb_t::white() : rgb_t::black();
}

void grfd2301_state::mem_map(address_map &map)
{
	map(0xe000, 0xefff).rom().region("maincpu", 0);
	map(0xf000, 0xf7ff).ram();
	map(0xf800, 0xffff).ram().share("videoram");
}

void grfd2301_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0xc4, 0xc7).w("pci", FUNC(scn2651_device::write));
	map(0xc8, 0xcb).w("pit", FUNC(pit8253_device::write));
	map(0xce, 0xce).nopw(); // ?
	map(0xd8, 0xd8).nopr(); // ?
	map(0xf1, 0xf1).portr("CONFIG");
	map(0xf2, 0xf3).w("crtc", FUNC(i8275_device::write));
}

static INPUT_PORTS_START( grfd2301 )
	PORT_START("CONFIG")
	PORT_CONFNAME(0x04, 0x00, "Refresh rate")
	PORT_CONFSETTING(0x04, "50 Hz")
	PORT_CONFSETTING(0x00, "60 Hz")
INPUT_PORTS_END

void grfd2301_state::machine_start()
{
	save_item(NAME(m_ma));
}

void grfd2301_state::machine_reset()
{
	m_maincpu->set_pc(0xe000);
}

void grfd2301_state::grfd2301(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 4000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &grfd2301_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &grfd2301_state::io_map);

	SCN2651(config, "pci", 5.0688_MHz_XTAL);

	PIT8253(config, "pit");

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(15874560, 848, 0, 640, 312, 0, 288); // parameters guessed
	screen.set_screen_update(m_crtc, FUNC(i8275_device::screen_update));

	I8275(config, m_crtc, 15874560 / 8); // type and clock unknown
	m_crtc->set_screen("screen");
	m_crtc->set_character_width(8); // also guessed
	m_crtc->drq_wr_callback().set(FUNC(grfd2301_state::drq_w));
	m_crtc->vrtc_wr_callback().set(FUNC(grfd2301_state::vrtc_w));
	m_crtc->set_display_callback(FUNC(grfd2301_state::draw_character));
}

ROM_START( grfd2301 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "boot2301", 0x000, 0x1000, CRC(feec0cbd) SHA1(ec8138aca7ed489d86aaf2e07225c8d715440db7) )

	// Using the chargen from 'c10' for now.
	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "c10_char.bin", 0x0000, 0x2000, BAD_DUMP CRC(cb530b6f) SHA1(95590bbb433db9c4317f535723b29516b9b9fcbf))
ROM_END

} // anonymous namespace


COMP( 198?, grfd2301, 0, 0, grfd2301, grfd2301, grfd2301_state, empty_init, "Genrad", "Futuredata 2301 Network Processor", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



gridcomp.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************

    drivers/gridcomp.cpp

    Driver file for GRiD Compass series

    US patent 4,571,456 describes model 1101:

    - 15 MHz XTAL, produces
        - 5 MHz system clock for CPU, FPU, OSP
        - 7.5 MHz pixel clock
    - Intel 8086 - CPU
    - Intel 8087 - FPU
    - Intel 80130 - Operating System Processor, equivalent of:
        - 8259 PIC
        - 8254 PIT
    - Texas Instruments TMS9914 GPIB controller
    - Intel 7220 Bubble Memory Controller
        - 7110 Magnetic Bubble Memory modules and support chips
    - X2210D - EAROM for machine ID
    - MM58174AN - Real-Time Clock
    - (custom DMA logic)
    - Intel 8741 - keyboard MCU
    - Intel 8274 - UART
    - Intel 8255 - modem interface
        - 2x DAC0832LCN - DAC
        - MK5089N - DTMF generator
        - ...

    high-resolution motherboard photo (enough to read chip numbers): http://deltacxx.insomnia247.nl/gridcompass/motherboard.jpg

    differences between models:
    - Compass 110x do not have GRiDROM slots.
    - Compass II (112x, 113x) have 4 of them.
    - Compass II 113x have 512x256 screen size
    - Compass 11x9 have 512K ram
    - Compass II have DMA addresses different from Compass 110x

    to do:

    - keyboard: decode and add the rest of keycodes
        keycode table can be found here on page A-2:
        http://deltacxx.insomnia247.nl/gridcompass/large_files/Yahoo%20group%20backup/RuGRiD-Laptop/files/6_GRiD-OS-Programming/3_GRiD-OS-Reference.pdf
    - EAROM, RTC
    - serial port (incomplete), modem (incl. DTMF generator)
    - proper custom DMA logic timing
    - implement units other than 1101

    missing dumps:

    - BIOS from models other than 1139 and late 1101 revision (the latter one is detected as 1108 in VERIFYPROM utility)
    - GRiDROM's
    - keyboard MCU
    - external floppy and hard disk (2101, 2102)

    to boot CCOS 3.0.1:
    - convert GRIDOS.IMD to IMG format
    - create zero-filled 384K bubble memory image and attach it as -memcard
    - attach floppy with `-ieee_grid grid2102 -flop GRIDOS.IMG`
    - use grid1101 with 'ccos' ROM

***************************************************************************/

#include "emu.h"

#include "bus/ieee488/ieee488.h"
#include "bus/rs232/rs232.h"
#include "cpu/i86/i86.h"
#include "gridkeyb.h"
#include "machine/i7220.h"
#include "machine/i80130.h"
#include "machine/i8255.h"
#include "machine/mm58174.h"
#include "machine/ram.h"
#include "machine/tms9914.h"
#include "machine/z80sio.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "screen.h"
#include "softlist.h"
#include "speaker.h"

#define LOG_KEYBOARD  (1U << 1)
#define LOG_DEBUG     (1U << 2)

#define VERBOSE (LOG_GENERAL)
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"

#define LOGKBD(...) LOGMASKED(LOG_KEYBOARD, __VA_ARGS__)
#define LOGDBG(...) LOGMASKED(LOG_DEBUG, __VA_ARGS__)


namespace {

#define I80130_TAG      "osp"

class gridcomp_state : public driver_device
{
public:
	gridcomp_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_osp(*this, I80130_TAG)
		, m_rtc(*this, "rtc")
		, m_modem(*this, "modem")
		, m_uart8274(*this, "uart8274")
		, m_speaker(*this, "speaker")
		, m_ram(*this, RAM_TAG)
		, m_tms9914(*this, "hpib")
	{ }

	static constexpr feature_type unemulated_features() { return feature::WAN; }

	void grid1129(machine_config &config);
	void grid1131(machine_config &config);
	void grid1121(machine_config &config);
	void grid1139(machine_config &config);
	void grid1109(machine_config &config);
	void grid1101(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<i80130_device> m_osp;
	required_device<mm58174_device> m_rtc;
	required_device<i8255_device> m_modem;
	optional_device<i8274_device> m_uart8274;
	required_device<speaker_sound_device> m_speaker;
	required_device<ram_device> m_ram;
	required_device<tms9914_device> m_tms9914;

	IRQ_CALLBACK_MEMBER(irq_callback);

	uint16_t grid_9ff0_r(offs_t offset);
	uint16_t grid_keyb_r(offs_t offset);
	uint8_t grid_modem_r(offs_t offset);
	void grid_keyb_w(offs_t offset, uint16_t data);
	void grid_modem_w(offs_t offset, uint8_t data);

	void grid_dma_w(offs_t offset, uint8_t data);
	uint8_t grid_dma_r(offs_t offset);

	uint32_t screen_update_110x(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	uint32_t screen_update_113x(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	uint32_t screen_update_generic(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int px);

	void kbd_put(u16 data);

	void grid1101_io(address_map &map) ATTR_COLD;
	void grid1101_map(address_map &map) ATTR_COLD;
	void grid1121_map(address_map &map) ATTR_COLD;

	bool m_kbd_ready = false;
	uint16_t m_kbd_data = 0;

	uint16_t *m_videoram = nullptr;
};


[[maybe_unused]] uint16_t gridcomp_state::grid_9ff0_r(offs_t offset)
{
	uint16_t data = 0;

	switch (offset)
	{
	case 0:
		data = 0xbb66;
		break;
	}

	LOGDBG("9FF0: %02x == %02x\n", 0x9ff00 + (offset << 1), data);

	return data;
}

uint16_t gridcomp_state::grid_keyb_r(offs_t offset)
{
	uint16_t data = 0;

	switch (offset)
	{
	case 0:
		data = m_kbd_data;
		m_kbd_data = 0xff;
		m_kbd_ready = false;
		m_osp->ir4_w(CLEAR_LINE);
		break;

	case 1:
		data = m_kbd_ready ? 2 : 0;
		break;
	}

	LOGKBD("%02x == %02x\n", 0xdffc0 + (offset << 1), data);

	return data;
}

void gridcomp_state::grid_keyb_w(offs_t offset, uint16_t data)
{
	LOGKBD("%02x <- %02x\n", 0xdffc0 + (offset << 1), data);
}

void gridcomp_state::kbd_put(u16 data)
{
	m_kbd_data = data;
	m_kbd_ready = true;
	m_osp->ir4_w(ASSERT_LINE);
}


// reject all commands
uint8_t gridcomp_state::grid_modem_r(offs_t offset)
{
	uint8_t data = 0;
	LOG("MDM %02x == %02x\n", 0xdfec0 + (offset << 1), data);

	return data;
}

void gridcomp_state::grid_modem_w(offs_t offset, uint8_t data)
{
	LOG("MDM %02x <- %02x\n", 0xdfec0 + (offset << 1), data);
}

void gridcomp_state::grid_dma_w(offs_t offset, uint8_t data)
{
	m_tms9914->write(7, data);
	// LOG("DMA %02x <- %02x\n", offset, data);
}

uint8_t gridcomp_state::grid_dma_r(offs_t offset)
{
	int ret = m_tms9914->read(7);
	// LOG("DMA %02x == %02x\n", offset, ret);
	return ret;
}

uint32_t gridcomp_state::screen_update_generic(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int px)
{
	for (int y = 0; y < 240; y++)
	{
		uint16_t *p = &bitmap.pix(y);

		int const offset = y * (px / 16);

		for (int x = offset; x < offset + px / 16; x++)
		{
			uint16_t const gfx = m_videoram[x];

			for (int i = 15; i >= 0; i--)
			{
				*p++ = BIT(gfx, i);
			}
		}
	}

	return 0;
}

uint32_t gridcomp_state::screen_update_110x(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return screen_update_generic(screen, bitmap, cliprect, 320);
}

uint32_t gridcomp_state::screen_update_113x(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return screen_update_generic(screen, bitmap, cliprect, 512);
}

void gridcomp_state::machine_start()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	program.install_ram(0, m_ram->size() - 1, m_ram->pointer());

	m_videoram = (uint16_t *)m_maincpu->space(AS_PROGRAM).get_write_ptr(0x400);
}

void gridcomp_state::machine_reset()
{
	m_kbd_ready = false;
}

IRQ_CALLBACK_MEMBER(gridcomp_state::irq_callback)
{
	return m_osp->inta_r();
}


void gridcomp_state::grid1101_map(address_map &map)
{
	map.unmap_value_high();
	map(0xdfe80, 0xdfe83).rw("i7220", FUNC(i7220_device::read), FUNC(i7220_device::write)).umask16(0x00ff);
	map(0xdfea0, 0xdfeaf).unmaprw(); // ??
	map(0xdfec0, 0xdfecf).rw(FUNC(gridcomp_state::grid_modem_r), FUNC(gridcomp_state::grid_modem_w)).umask16(0x00ff); // incl. DTMF generator
	map(0xdff00, 0xdff1f).rw("uart8274", FUNC(i8274_device::ba_cd_r), FUNC(i8274_device::ba_cd_w)).umask16(0x00ff);
	map(0xdff40, 0xdff5f).rw(m_rtc, FUNC(mm58174_device::read), FUNC(mm58174_device::write)).umask16(0xff00);
	map(0xdff80, 0xdff8f).rw("hpib", FUNC(tms9914_device::read), FUNC(tms9914_device::write)).umask16(0x00ff);
	map(0xdffc0, 0xdffcf).rw(FUNC(gridcomp_state::grid_keyb_r), FUNC(gridcomp_state::grid_keyb_w)); // Intel 8741 MCU
	map(0xe0000, 0xeffff).rw(FUNC(gridcomp_state::grid_dma_r), FUNC(gridcomp_state::grid_dma_w)); // DMA
	map(0xfc000, 0xfffff).rom().region("user1", 0);
}

void gridcomp_state::grid1121_map(address_map &map)
{
	map.unmap_value_high();
	map(0x90000, 0x97fff).unmaprw(); // ?? ROM slot
	map(0x9ff00, 0x9ff0f).unmaprw(); // .r(FUNC(gridcomp_state::grid_9ff0_r)); // ?? ROM?
	map(0xc0000, 0xcffff).unmaprw(); // ?? ROM slot -- signature expected: 0x4554, 0x5048
	map(0xdfa00, 0xdfdff).rw(FUNC(gridcomp_state::grid_dma_r), FUNC(gridcomp_state::grid_dma_w)); // DMA
	map(0xdfe00, 0xdfe1f).unmaprw(); // .rw("uart8274", FUNC(i8274_device::ba_cd_r), FUNC(i8274_device::ba_cd_w)).umask16(0x00ff);
	map(0xdfe40, 0xdfe4f).unmaprw(); // ?? diagnostic 8274
	map(0xdfe80, 0xdfe83).rw("i7220", FUNC(i7220_device::read), FUNC(i7220_device::write)).umask16(0x00ff);
	map(0xdfea0, 0xdfeaf).unmaprw(); // ??
	map(0xdfec0, 0xdfecf).rw(FUNC(gridcomp_state::grid_modem_r), FUNC(gridcomp_state::grid_modem_w)).umask16(0x00ff); // incl. DTMF generator
	map(0xdff40, 0xdff5f).rw(m_rtc, FUNC(mm58174_device::read), FUNC(mm58174_device::write)).umask16(0xff00);
	map(0xdff80, 0xdff8f).rw("hpib", FUNC(tms9914_device::read), FUNC(tms9914_device::write)).umask16(0x00ff);
	map(0xdffc0, 0xdffcf).rw(FUNC(gridcomp_state::grid_keyb_r), FUNC(gridcomp_state::grid_keyb_w)); // Intel 8741 MCU
	map(0xfc000, 0xfffff).rom().region("user1", 0);
}

void gridcomp_state::grid1101_io(address_map &map)
{
	map(0x0000, 0x000f).m(m_osp, FUNC(i80130_device::io_map));
}

static INPUT_PORTS_START( gridcomp )
INPUT_PORTS_END

/*
 * IRQ0 serial
 * IRQ1 bubble
 * IRQ2 modem
 * IRQ3 system tick || vert sync
 * IRQ4 keyboard
 * IRQ5 gpib
 * IRQ6 8087
 * IRQ7 ring
 */
void gridcomp_state::grid1101(machine_config &config)
{
	I8086(config, m_maincpu, XTAL(15'000'000) / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &gridcomp_state::grid1101_map);
	m_maincpu->set_addrmap(AS_IO, &gridcomp_state::grid1101_io);
	m_maincpu->set_irq_acknowledge_callback(FUNC(gridcomp_state::irq_callback));

	I80130(config, m_osp, XTAL(15'000'000)/3);
	m_osp->irq().set_inputline("maincpu", 0);

	MM58174(config, m_rtc, 32.768_kHz_XTAL);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD)); // actually a kind of EL display
	screen.set_color(rgb_t::amber());
	screen.set_screen_update(FUNC(gridcomp_state::screen_update_110x));
	screen.set_raw(XTAL(15'000'000)/2, 424, 0, 320, 262, 0, 240); // XXX 66 Hz refresh
	screen.screen_vblank().set(m_osp, FUNC(i80130_device::ir3_w));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	grid_keyboard_device &keyboard(GRID_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(gridcomp_state::kbd_put));

	i7220_device &i7220(I7220(config, "i7220", XTAL(4'000'000)));
	i7220.set_data_size(3); // 3 1-Mbit MBM's
	i7220.set_must_be_loaded(true);
	i7220.irq_callback().set(I80130_TAG, FUNC(i80130_device::ir1_w));
	i7220.drq_callback().set(I80130_TAG, FUNC(i80130_device::ir1_w));

	tms9914_device &hpib(TMS9914(config, m_tms9914, XTAL(4'000'000)));
	hpib.int_write_cb().set(I80130_TAG, FUNC(i80130_device::ir5_w));
	hpib.dio_read_cb().set(IEEE488_TAG, FUNC(ieee488_device::dio_r));
	hpib.dio_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_dio_w));
	hpib.eoi_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_eoi_w));
	hpib.dav_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_dav_w));
	hpib.nrfd_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_nrfd_w));
	hpib.ndac_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ndac_w));
	hpib.ifc_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ifc_w));
	hpib.srq_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_srq_w));
	hpib.atn_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_atn_w));
	hpib.ren_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ren_w));

	ieee488_device &ieee(IEEE488(config, IEEE488_TAG));
	ieee.eoi_callback().set("hpib", FUNC(tms9914_device::eoi_w));
	ieee.dav_callback().set("hpib", FUNC(tms9914_device::dav_w));
	ieee.nrfd_callback().set("hpib", FUNC(tms9914_device::nrfd_w));
	ieee.ndac_callback().set("hpib", FUNC(tms9914_device::ndac_w));
	ieee.ifc_callback().set("hpib", FUNC(tms9914_device::ifc_w));
	ieee.srq_callback().set("hpib", FUNC(tms9914_device::srq_w));
	ieee.atn_callback().set("hpib", FUNC(tms9914_device::atn_w));
	ieee.ren_callback().set("hpib", FUNC(tms9914_device::ren_w));
	IEEE488_SLOT(config, "ieee_grid", 0, grid_ieee488_devices, nullptr);
	IEEE488_SLOT(config, "ieee_grid2", 0, grid_ieee488_devices, nullptr);
	IEEE488_SLOT(config, "ieee_grid3", 0, grid_ieee488_devices, nullptr);
	IEEE488_SLOT(config, "ieee_grid4", 0, grid_ieee488_devices, nullptr);
	IEEE488_SLOT(config, "ieee_rem", 0, remote488_devices, nullptr);

	I8274(config, m_uart8274, XTAL(4'032'000));
	m_uart8274->out_txda_callback().set("rs232_port", FUNC(rs232_port_device::write_txd));
	m_uart8274->out_dtra_callback().set("rs232_port", FUNC(rs232_port_device::write_dtr));
	m_uart8274->out_rtsa_callback().set("rs232_port", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232_port(RS232_PORT(config, "rs232_port", default_rs232_devices, nullptr));
	rs232_port.rxd_handler().set("uart8274", FUNC(i8274_device::rxa_w));
	rs232_port.dcd_handler().set("uart8274", FUNC(i8274_device::dcda_w));
	rs232_port.cts_handler().set("uart8274", FUNC(i8274_device::ctsa_w));

	I8255(config, "modem", 0);

	RAM(config, m_ram).set_default_size("256K").set_default_value(0);
}

void gridcomp_state::grid1109(machine_config &config)
{
	grid1101(config);
	m_ram->set_default_size("512K");
}

void gridcomp_state::grid1121(machine_config &config)
{
	grid1101(config);
	// m_maincpu->set_clock(XTAL(24'000'000) / 3); // XXX
	m_maincpu->set_addrmap(AS_PROGRAM, &gridcomp_state::grid1121_map);
}

void gridcomp_state::grid1129(machine_config &config)
{
	grid1121(config);
	m_ram->set_default_size("512K");
}

void gridcomp_state::grid1131(machine_config &config)
{
	grid1121(config);
	subdevice<screen_device>("screen")->set_screen_update(FUNC(gridcomp_state::screen_update_113x));
	subdevice<screen_device>("screen")->set_raw(XTAL(15'000'000)/2, 720, 0, 512, 262, 0, 240); // XXX
}

void gridcomp_state::grid1139(machine_config &config)
{
	grid1131(config);
	m_ram->set_default_size("512K");
}


ROM_START( grid1101 )
	ROM_REGION16_LE(0x10000, "user1", 0)

	ROM_SYSTEM_BIOS(0, "ccos", "ccos bios")
	ROMX_LOAD("bios1101_0_25.bin", 0x0000, 0x4000, CRC(625388cb) SHA1(4c52c62fa9bc2f9a9a0a1e7f3beddef6809b9eed), ROM_BIOS(0))
ROM_END

ROM_START( grid1109 )
	ROM_REGION16_LE(0x10000, "user1", 0)

	ROM_SYSTEM_BIOS(0, "ccos", "ccos bios")
	ROMX_LOAD("1109even.bin", 0x0000, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("1109odd.bin",  0x0001, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(0))
ROM_END

ROM_START( grid1121 )
	ROM_REGION16_LE(0x10000, "user1", 0)

	ROM_SYSTEM_BIOS(0, "ccos", "ccos bios")
	ROMX_LOAD("1121even.bin", 0x0000, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("1121odd.bin",  0x0001, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(0))
ROM_END

ROM_START( grid1129 )
	ROM_REGION16_LE(0x10000, "user1", 0)
	ROM_DEFAULT_BIOS("ccos")

	ROM_SYSTEM_BIOS(0, "ccos", "ccos bios")
	ROMX_LOAD("1129even.bin", 0x0000, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("1129odd.bin",  0x0001, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "patched", "patched 1139 bios")
	ROMX_LOAD("1139even.bin", 0x0000, 0x2000, CRC(67071849) SHA1(782239c155fa5821f8dbd2607cee9152d175e90e), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("1139odd.bin",  0x0001, 0x2000, CRC(13ed4bf0) SHA1(f7087f86dbbc911bee985125bccd2417e0374e8e), ROM_SKIP(1) | ROM_BIOS(1))

	// change bubble driver setup to read floppy images with 512-byte sectors
	ROM_FILL(0x3114,1,0x00)
	ROM_FILL(0x3115,1,0x02)
	ROM_FILL(0x3116,1,0xf8)
	ROM_FILL(0x3117,1,0x01)

	// move work area from 0440h:XXXX to 0298h:XXXX
	ROM_FILL(0x23,1,0x98)
	ROM_FILL(0x24,1,0x2)
	ROM_FILL(0xbc,1,0x98)
	ROM_FILL(0xbd,1,0x2)
	ROM_FILL(0x14e,1,0xc1)  //
	ROM_FILL(0x14f,1,0x2)   //
	ROM_FILL(0x15a,1,0xc2)  //
	ROM_FILL(0x15b,1,0x2)   //
	ROM_FILL(0x17b,1,0x45)  //
	ROM_FILL(0x17c,1,0x3)   //
	ROM_FILL(0x28c,1,0x98)
	ROM_FILL(0x28d,1,0x2)
	ROM_FILL(0x28f,1,0x98)
	ROM_FILL(0x290,1,0x2)
	ROM_FILL(0x2b9,1,0x98)
	ROM_FILL(0x2ba,1,0x2)
	ROM_FILL(0x2d0,1,0x98)
	ROM_FILL(0x2d1,1,0x2)
	ROM_FILL(0x31a,1,0x98)
	ROM_FILL(0x31b,1,0x2)
	ROM_FILL(0x3a0,1,0x98)
	ROM_FILL(0x3a1,1,0x2)
	ROM_FILL(0x3a3,1,0x98)
	ROM_FILL(0x3a4,1,0x2)
	ROM_FILL(0x3e2,1,0x98)
	ROM_FILL(0x3e3,1,0x2)
	ROM_FILL(0x43e,1,0x98)
	ROM_FILL(0x43f,1,0x2)
	ROM_FILL(0x46d,1,0x98)
	ROM_FILL(0x46e,1,0x2)
	ROM_FILL(0x4fe,1,0x98)
	ROM_FILL(0x4ff,1,0x2)
	ROM_FILL(0x512,1,0x98)
	ROM_FILL(0x513,1,0x2)
	ROM_FILL(0x768,1,0x98)
	ROM_FILL(0x769,1,0x2)
	ROM_FILL(0x79e,1,0x98)
	ROM_FILL(0x79f,1,0x2)
	ROM_FILL(0x7f5,1,0x98)
	ROM_FILL(0x7f6,1,0x2)
	ROM_FILL(0x92a,1,0x98)
	ROM_FILL(0x92b,1,0x2)
	ROM_FILL(0xe50,1,0x98)
	ROM_FILL(0xe51,1,0x2)
	ROM_FILL(0xfa6,1,0x98)
	ROM_FILL(0xfa7,1,0x2)
	ROM_FILL(0x15fe,1,0xce) //
	ROM_FILL(0x15ff,1,0x2)  //
	ROM_FILL(0x1628,1,0xd0) //
	ROM_FILL(0x1629,1,0x2)  //
	ROM_FILL(0x1700,1,0x98)
	ROM_FILL(0x1701,1,0x2)
	ROM_FILL(0x1833,1,0xd6) //
	ROM_FILL(0x1834,1,0x2)  //
	ROM_FILL(0x184a,1,0xd6) //
	ROM_FILL(0x184b,1,0x2)  //
	ROM_FILL(0x1a2e,1,0xd6) //
	ROM_FILL(0x1a2f,1,0x2)  //
	ROM_FILL(0x19c2,1,0x98)
	ROM_FILL(0x19c3,1,0x2)
	ROM_FILL(0x1ee0,1,0x98)
	ROM_FILL(0x1ee1,1,0x2)
	ROM_FILL(0x1f1d,1,0x98)
	ROM_FILL(0x1f1e,1,0x2)
	ROM_FILL(0x1f40,1,0x98)
	ROM_FILL(0x1f41,1,0x2)
	ROM_FILL(0x2253,1,0x98)
	ROM_FILL(0x2254,1,0x2)
	ROM_FILL(0x2437,1,0x98)
	ROM_FILL(0x2438,1,0x2)
	ROM_FILL(0x283a,1,0x98)
	ROM_FILL(0x283b,1,0x2)
	ROM_FILL(0x2868,1,0x98)
	ROM_FILL(0x2869,1,0x2)
	ROM_FILL(0x288f,1,0x98)
	ROM_FILL(0x2890,1,0x2)
	ROM_FILL(0x2942,1,0x98)
	ROM_FILL(0x2943,1,0x2)
	ROM_FILL(0x295c,1,0x98)
	ROM_FILL(0x295d,1,0x2)
	ROM_FILL(0x2a5e,1,0x98)
	ROM_FILL(0x2a5f,1,0x2)
	ROM_FILL(0x315c,1,0xc9) //
	ROM_FILL(0x315d,1,0x2)  //
	ROM_FILL(0x3160,1,0xce) //
	ROM_FILL(0x3161,1,0x2)  //
	ROM_FILL(0x3164,1,0xcf) //
	ROM_FILL(0x3165,1,0x2)  //
ROM_END

ROM_START( grid1131 )
	ROM_REGION16_LE(0x10000, "user1", 0)

	ROM_SYSTEM_BIOS(0, "ccos", "ccos bios")
	ROMX_LOAD("1131even.bin", 0x0000, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("1131odd.bin",  0x0001, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(0))
ROM_END

ROM_START( grid1139 )
	ROM_REGION16_LE(0x10000, "user1", 0)

	ROM_SYSTEM_BIOS(0, "normal", "normal bios")
	ROMX_LOAD("1139even.bin", 0x0000, 0x2000, CRC(67071849) SHA1(782239c155fa5821f8dbd2607cee9152d175e90e), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("1139odd.bin",  0x0001, 0x2000, CRC(13ed4bf0) SHA1(f7087f86dbbc911bee985125bccd2417e0374e8e), ROM_SKIP(1) | ROM_BIOS(0))
ROM_END

} // Anonymous namespace


/***************************************************************************

  Game driver(s)

***************************************************************************/

//    YEAR  NAME      PARENT    COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY           FULLNAME           FLAGS
COMP( 1982, grid1101, 0,        0,      grid1101, gridcomp, gridcomp_state, empty_init, "GRiD Computers", "Compass 1101",    MACHINE_NO_SOUND_HW | MACHINE_IMPERFECT_CONTROLS )
COMP( 1982, grid1109, grid1101, 0,      grid1109, gridcomp, gridcomp_state, empty_init, "GRiD Computers", "Compass 1109",    MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1984, grid1121, 0,        0,      grid1121, gridcomp, gridcomp_state, empty_init, "GRiD Computers", "Compass II 1121", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1984, grid1129, grid1121, 0,      grid1129, gridcomp, gridcomp_state, empty_init, "GRiD Computers", "Compass II 1129", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1984, grid1131, grid1121, 0,      grid1131, gridcomp, gridcomp_state, empty_init, "GRiD Computers", "Compass II 1131", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1984, grid1139, grid1121, 0,      grid1139, gridcomp, gridcomp_state, empty_init, "GRiD Computers", "Compass II 1139", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



gs6502.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Frank Palazzolo

// MAME driver for Grant Searle's Simple 6502 Computer
// http://www.searle.wales/

#include "emu.h"

#include "cpu/m6502/m6502.h"
#include "machine/6850acia.h"

#include "machine/clock.h"
#include "bus/rs232/rs232.h"


namespace {

class gs6502_state : public driver_device
{
public:
	gs6502_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_acia(*this, "acia")
	{ }

	void gs6502(machine_config &config);

private:
	void gs6502_mem(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<acia6850_device> m_acia;
};

void gs6502_state::gs6502_mem(address_map &map)
{
	map(0x0000, 0x7fff).ram();
	map(0xa000, 0xbfff).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xc000, 0xffff).rom();
}

// This is here only to configure our terminal for interactive use
static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_115200 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_115200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void gs6502_state::gs6502(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, XTAL(1'843'200));
	m_maincpu->set_addrmap(AS_PROGRAM, &gs6502_state::gs6502_mem);

	// Configure UART (via m_acia)
	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	// should this be reverse polarity?
	m_acia->irq_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	clock_device &acia_clock(CLOCK(config, "acia_clock", 1'843'200));
	acia_clock.signal_handler().set("acia", FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append("acia", FUNC(acia6850_device::write_rxc));

	// Configure a "default terminal" to connect to the 6850, so we have a console
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_acia, FUNC(acia6850_device::write_rxd));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal)); // must be below the DEVICE_INPUT_DEFAULTS_START block
}

ROM_START(gs6502)
	ROM_REGION(0x10000, "maincpu",0)
	ROM_LOAD("gs6502.bin",   0xc000, 0x4000, CRC(0b1d8348) SHA1(482451aa8cc0c470ce9706b43bfa093df47c8ab1))
ROM_END

} // anonymous namespace


//    YEAR  NAME         PARENT    COMPAT  MACHINE   INPUT    CLASS         INIT           COMPANY           FULLNAME                FLAGS
COMP( 2013, gs6502,      0,        0,      gs6502,   0,       gs6502_state, empty_init,    "Grant Searle",   "Simple 6502 Machine",  MACHINE_NO_SOUND_HW ) // schematics are dated 2009-2013



gs6809.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Frank Palazzolo

// MAME driver for Grant Searle's Simple 6809 Computer
// http://www.searle.wales/

#include "emu.h"

#include "cpu/m6809/m6809.h"
#include "machine/6850acia.h"

#include "machine/clock.h"
#include "bus/rs232/rs232.h"


namespace {

class gs6809_state : public driver_device
{
public:
	gs6809_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_acia(*this, "acia")
	{ }

	void gs6809(machine_config &config);

private:
	void gs6809_mem(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<acia6850_device> m_acia;
};

void gs6809_state::gs6809_mem(address_map &map)
{
	map(0x0000, 0x7fff).ram();
	map(0xa000, 0xbfff).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xc000, 0xffff).rom();
}

// This is here only to configure our terminal for interactive use
static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_115200 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_115200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void gs6809_state::gs6809(machine_config &config)
{
	/* basic machine hardware */
	MC6809(config, m_maincpu, XTAL(7'372'800));
	m_maincpu->set_addrmap(AS_PROGRAM, &gs6809_state::gs6809_mem);

	// Configure UART (via m_acia)
	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	// should this be reverse polarity?
	m_acia->irq_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	clock_device &acia_clock(CLOCK(config, "acia_clock", 7'372'800/4)); // E Clock from M6809
	acia_clock.signal_handler().set("acia", FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append("acia", FUNC(acia6850_device::write_rxc));

	// Configure a "default terminal" to connect to the 6850, so we have a console
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_acia, FUNC(acia6850_device::write_rxd));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal)); // must be below the DEVICE_INPUT_DEFAULTS_START block
}

ROM_START(gs6809)
	ROM_REGION(0x10000, "maincpu",0)
	ROM_LOAD("gs6809.bin",   0xc000, 0x4000, CRC(f997a378) SHA1(1e0d5997b1b286aa328bdbff776bcddbb68d1c34))
ROM_END

} // anonymous namespace


//    YEAR  NAME         PARENT    COMPAT  MACHINE   INPUT    CLASS         INIT           COMPANY           FULLNAME                FLAGS
COMP( 2011, gs6809,      0,        0,      gs6809,   0,       gs6809_state, empty_init,    "Grant Searle",   "Simple 6809 Machine",  MACHINE_NO_SOUND_HW )



gscpm.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Frank Palazzolo

// MAME driver for Grant Searle's Simple CP/M Computer
// http://www.searle.wales/

// This driver uses a compact flash card as a hard disk device.
// To create a virtual disk file, use the following (for a 128MB card):
//     chdman createhd -s 134217728 -o filename.chd
// (or use -s 67108864 for 64MB card)
//
// Then, run MAME with -hard filename.chd, or mount it using the GUI
// and restart the driver
//

#include "emu.h"

#include "cpu/z80/z80.h"
#include "bus/ata/ataintf.h"
#include "machine/z80sio.h"
#include "machine/ram.h"
#include "machine/clock.h"
#include "bus/rs232/rs232.h"


namespace {

class gscpm_state : public driver_device
{
public:
	gscpm_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, "ram")
		, m_ide(*this, "ide")
		, m_sio(*this, "sio")
	{ }

	void gscpm(machine_config &config);

protected:
	void machine_reset() override ATTR_COLD;

	void gscpm_mem(address_map &map) ATTR_COLD;
	void gscpm_io(address_map &map) ATTR_COLD;

	uint8_t cflash_r(offs_t offset);
	void cflash_w(offs_t offset, uint8_t data);
	uint8_t sio_r(offs_t offset);
	void sio_w(offs_t offset, uint8_t data);

	void switch_to_ram_w(uint8_t data);

	required_device<z80_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<ata_interface_device> m_ide;
	required_device<z80sio_device> m_sio;
};

void gscpm_state::gscpm_mem(address_map &map)
{
	//map(0x0000, 0x3fff).rom("maincpu");  // This is ROM after reset, and RAM is switched in when CP/M is booted
										   // (will install handlers dynamically)
	map(0x4000, 0xffff).ram();
}

void gscpm_state::gscpm_io(address_map &map)
{
	map.global_mask(0xff);  // use 8-bit ports
	map.unmap_value_high(); // unmapped addresses return 0xff
	map(0x00, 0x07).rw(FUNC(gscpm_state::sio_r), FUNC(gscpm_state::sio_w));
	map(0x10, 0x17).rw(FUNC(gscpm_state::cflash_r), FUNC(gscpm_state::cflash_w)); // compact flash
	map(0x38, 0x3f).w(FUNC(gscpm_state::switch_to_ram_w));
}

uint8_t gscpm_state::cflash_r(offs_t offset)
{
	return m_ide->cs0_r(offset, 0xff);
}

void gscpm_state::cflash_w(offs_t offset, uint8_t data)
{
	m_ide->cs0_w(offset, data, 0xff);
}

uint8_t gscpm_state::sio_r(offs_t offset)
{
	switch (offset & 3)
	{
	case 0x00:
		return m_sio->da_r();
	case 0x01:
		return m_sio->db_r();
	case 0x02:
		return m_sio->ca_r();
	case 0x03:
		return m_sio->cb_r();
	}
	return 0x00; // can't happen
}

void gscpm_state::sio_w(offs_t offset, uint8_t data)
{
	switch (offset & 3)
	{
	case 0x00:
		m_sio->da_w(data);
		break;
	case 0x01:
		m_sio->db_w(data);
		break;
	case 0x02:
		m_sio->ca_w(data);
		break;
	case 0x03:
		m_sio->cb_w(data);
		break;
	}
}

void gscpm_state::switch_to_ram_w(uint8_t data)
{
	// Install the RAM handler here
	m_maincpu->space(AS_PROGRAM).unmap_readwrite(0x0000, 0x3fff); // Unmap the ROM handler
	m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x3fff, m_ram->pointer());
}

void gscpm_state::machine_reset()
{
	// Install the ROM handler here
	m_maincpu->space(AS_PROGRAM).unmap_readwrite(0x0000, 0x3fff);  // Unmap RAM handler if being rebooted
	m_maincpu->space(AS_PROGRAM).install_rom(0x0000, 0x3fff, memregion("maincpu")->base());
}

// This is here only to configure our terminal for interactive use
static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_115200 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_115200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

static const z80_daisy_config gscpm_daisy_chain[] =
{
	{ "sio" },
	{ nullptr }
};

void gscpm_state::gscpm(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(7'372'800));
	m_maincpu->set_addrmap(AS_PROGRAM, &gscpm_state::gscpm_mem);
	m_maincpu->set_addrmap(AS_IO, &gscpm_state::gscpm_io);
	m_maincpu->set_daisy_config(gscpm_daisy_chain);

	/* compact flash hard drive */
	ATA_INTERFACE(config, m_ide).options(ata_devices, "hdd", nullptr, false);

	RAM(config, m_ram).set_default_size("16K"); // This shadows the ROM

	Z80SIO(config, m_sio, 0);
	m_sio->out_txdb_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	m_sio->out_rtsb_callback().set("rs232", FUNC(rs232_port_device::write_rts));
	m_sio->out_int_callback().set_inputline("maincpu", INPUT_LINE_IRQ0); // Connect interrupt pin to our Z80 INT line

	clock_device &sio_clock(CLOCK(config, "sio_clock", 7'372'800));
	sio_clock.signal_handler().set("sio", FUNC(z80sio_device::txca_w));
	sio_clock.signal_handler().append("sio", FUNC(z80sio_device::rxca_w));
	sio_clock.signal_handler().append("sio", FUNC(z80sio_device::txcb_w));
	sio_clock.signal_handler().append("sio", FUNC(z80sio_device::rxcb_w));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("sio", FUNC(z80sio_device::rxb_w));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal)); // must be below the DEVICE_INPUT_DEFAULTS_START block
}

// ROM mapping is trivial, this binary was created from the HEX file on Grant's website
ROM_START(gscpm)
	ROM_REGION(0x4000, "maincpu",0)
	ROM_LOAD("gscpm.bin",   0x0000, 0x4000, CRC(35ae0d43) SHA1(7fae4df419d38a1787a4a97cbef558f402109959))
ROM_END

} // anonymous namespace


//    YEAR  NAME         PARENT    COMPAT  MACHINE   INPUT    CLASS        INIT           COMPANY           FULLNAME                FLAGS
COMP( 201?, gscpm,       0,        0,      gscpm,    0,       gscpm_state, empty_init,    "Grant Searle",   "Simple CP/M Machine",  MACHINE_NO_SOUND_HW )



gsz80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Frank Palazzolo

// MAME Reference driver for Grant Searle's Simple Z80 Computer
// http://www.searle.wales/

// All the common emulator stuff is here
#include "emu.h"

// Two member devices referenced in state class, a Z80 and a UART
#include "cpu/z80/z80.h"
#include "machine/6850acia.h"

// Two more devices needed, a clock device for the UART, and RS-232 devices
#include "machine/clock.h"
#include "bus/rs232/rs232.h"


namespace {

// State class - derives from driver_device
class gsz80_state : public driver_device
{
public:
	gsz80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")   // Tag name for Z80 is "maincpu"
		, m_acia(*this, "acia")         // Tag name for UART is "acia"
	{ }

	// This function sets up the machine configuration
	void gsz80(machine_config &config);

protected:
	// address maps for program memory and io memory
	void gsz80_mem(address_map &map) ATTR_COLD;
	void gsz80_io(address_map &map) ATTR_COLD;

	// two member devices required here
	required_device<cpu_device> m_maincpu;
	required_device<acia6850_device> m_acia;
};

// Trivial memory map for program memory
void gsz80_state::gsz80_mem(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x2000, 0xffff).ram();
}

void gsz80_state::gsz80_io(address_map &map)
{
	map.global_mask(0xff);  // use 8-bit ports
	map.unmap_value_high(); // unmapped addresses return 0xff
	map(0x80, 0xbf).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
}

// This is here only to configure our terminal for interactive use
static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_115200 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_115200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void gsz80_state::gsz80(machine_config &config)
{
	/* basic machine hardware */

	// Configure member Z80 (via m_maincpu)
	Z80(config, m_maincpu, XTAL(7'372'800));
	m_maincpu->set_addrmap(AS_PROGRAM, &gsz80_state::gsz80_mem);
	m_maincpu->set_addrmap(AS_IO, &gsz80_state::gsz80_io);

	// Configure UART (via m_acia)
	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_acia->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_acia->irq_handler().set_inputline("maincpu", INPUT_LINE_IRQ0); // Connect interrupt pin to our Z80 INT line

	// Create a clock device to connect to the transmit and receive clock on the 6850
	clock_device &acia_clock(CLOCK(config, "acia_clock", 7'372'800));
	acia_clock.signal_handler().set("acia", FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append("acia", FUNC(acia6850_device::write_rxc));

	// Configure a "default terminal" to connect to the 6850, so we have a console
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_acia, FUNC(acia6850_device::write_rxd));
	rs232.dcd_handler().set(m_acia, FUNC(acia6850_device::write_dcd));
	rs232.cts_handler().set(m_acia, FUNC(acia6850_device::write_cts));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal)); // must be below the DEVICE_INPUT_DEFAULTS_START block
}

// ROM mapping is trivial, this binary was created from the HEX file on Grant's website
ROM_START(gsz80)
	ROM_REGION(0x2000, "maincpu",0)
	ROM_LOAD("gsz80.bin",   0x0000, 0x2000, CRC(6f4bc7e5) SHA1(9008fe3b9754ec5537b3ad90f748096602ba008e))
ROM_END

} // anonymous namespace


// This ties everything together
//    YEAR  NAME            PARENT    COMPAT    MACHINE        INPUT          CLASS             INIT           COMPANY           FULLNAME                FLAGS
COMP( 2007, gsz80,          0,        0,        gsz80,         0,             gsz80_state,      empty_init,    "Grant Searle",   "Simple Z-80 Machine",  MACHINE_NO_SOUND_HW )



h01x.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:zzemu-cn, Robbbert
/***************************************************************************
NF500A (TRS80 Level II Basic)
        09/01/2019

H-01B (TRS80 Level II Basic)
        10/05/2019

Despite the references to the TRS-80, the machines are entirely incompatible.

TODO:
- Need schematics and technical info.
- Need confirmation of clock speeds for each machine.
  (Cassettes made on machines of different clocks are not shareable)
- JCE's 16KB extended ROM functionality is not understood, functionality is
  unemulated
- Need software.

****************************************************************************/

#include "emu.h"
#include "screen.h"
#include "speaker.h"
#include "emupal.h"
#include "cpu/z80/z80.h"
#include "video/mc6845.h"
#include "machine/ram.h"
#include "sound/spkrdev.h"
#include "imagedev/cassette.h"


namespace {

class h01x_state : public driver_device
{
public:
	h01x_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_vram(*this, "vram")
		, m_rom(*this, "maincpu")
		, m_hzrom(*this, "hzrom")
		, m_exrom(*this, "exrom")
		, m_palette(*this, "palette")
		, m_crtc(*this, "crtc")
		, m_speaker(*this, "speaker")
		, m_cassette(*this, "cassette")
		, m_io_keyboard(*this, "LINE%u", 0U)
	{ }

	void h01x(machine_config &config);
	void h01b(machine_config &config);
	void nf500a(machine_config &config);
	void h01jce(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void h01x_mem_map(address_map &map) ATTR_COLD;
	void h01x_io_map(address_map &map) ATTR_COLD;

	uint8_t mem_0000_r(offs_t offset);
	void mem_0000_w(uint8_t data);
	uint8_t mem_4000_r(offs_t offset);
	void mem_4000_w(offs_t offset, uint8_t data);
	uint8_t mem_8000_r(offs_t offset);
	void mem_8000_w(offs_t offset, uint8_t data);
	uint8_t mem_c000_r(offs_t offset);
	void mem_c000_w(offs_t offset, uint8_t data);

	void port_70_w(uint8_t data);
	uint8_t port_50_r();

	required_device<cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<ram_device> m_vram;
	required_region_ptr<u8> m_rom;
	required_region_ptr<u8> m_hzrom;
	optional_region_ptr<u8> m_exrom;
	required_device<palette_device> m_palette;
	required_device<mc6845_device> m_crtc;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cassette;
	required_ioport_array<11> m_io_keyboard;

	uint8_t m_bank = 0U;
	MC6845_UPDATE_ROW(crtc_update_row);

	uint8_t *m_ram_ptr = nullptr;
	uint8_t *m_vram_ptr = nullptr;

	TIMER_CALLBACK_MEMBER(cassette_data_callback);
	bool m_cassette_data = false;
	emu_timer *m_cassette_data_timer = nullptr;
};


void h01x_state::h01x(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 16_MHz_XTAL / 8);
	m_maincpu->set_addrmap(AS_PROGRAM, &h01x_state::h01x_mem_map);
	m_maincpu->set_addrmap(AS_IO, &h01x_state::h01x_io_map);

	// RAM
	RAM(config, m_ram).set_default_size("32K").set_default_value(0x00);
	// VRAM 16K 4bit
	RAM(config, m_vram).set_default_size("16K").set_default_value(0xf0);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(10.6445_MHz_XTAL, 336, 0, 336, 192, 0, 192);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	MC6845(config, m_crtc, 10.6445_MHz_XTAL / 8);   // freq guess
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(4);
	m_crtc->set_update_row_callback(FUNC(h01x_state::crtc_update_row));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.50);

	/* devices */
	CASSETTE(config, m_cassette);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_default_state(CASSETTE_STOPPED);
}

void h01x_state::h01b(machine_config &config)
{
	// H-01B CPU: 2MHz ROM: 16KB + 32KB
	h01x(config);
	m_maincpu->set_clock(16_MHz_XTAL/8);  // to confirm
}

void h01x_state::nf500a(machine_config &config)
{
	// NF-500A CPU: 4MHz ROM: 16KB + 32KB
	h01x(config);
	m_maincpu->set_clock(16_MHz_XTAL/4);  // to confirm
}

void h01x_state::h01jce(machine_config &config)
{
	// JCE CPU: 4MHz ROM: 16KB + 32KB + 16KB
	h01x(config);
	m_maincpu->set_clock(16_MHz_XTAL/4);  // to confirm
}

void h01x_state::h01x_mem_map(address_map &map)
{
	map(0x0000, 0x3fff).rw(FUNC(h01x_state::mem_0000_r), FUNC(h01x_state::mem_0000_w));
	map(0x4000, 0x7fff).rw(FUNC(h01x_state::mem_4000_r), FUNC(h01x_state::mem_4000_w));
	map(0x8000, 0xbfff).rw(FUNC(h01x_state::mem_8000_r), FUNC(h01x_state::mem_8000_w));
	map(0xc000, 0xffff).rw(FUNC(h01x_state::mem_c000_r), FUNC(h01x_state::mem_c000_w));
}

void h01x_state::h01x_io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();

	map(0x60, 0x60).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x64, 0x64).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x50, 0x50).r(FUNC(h01x_state::port_50_r));
	map(0x70, 0x70).w(FUNC(h01x_state::port_70_w));
}

/*
H-01B
   KD7 KD6 KD5 KD4 KD3 KD2 KD1 KD0  scan address
A0            space Z   A   Q   1    BFFEH
A1             BRK  X   S   W   2    BFFDH
A2             (16) C   D   E   3    BFFBH
A3             E/C  V   F   R   4    BFF7H
A4              -   B   G   T   5    BFEFH
A5             右   N   H   Y   6    BFDFH
A6            RETN  M   J   U   7    BFBFH
A7              [   ,   K   I   8    BF7FH
A8            BS 左 .   L   O   9    BEFFH
A9              :   /   ;   P   0    BDFFH
A10           SHIFT            下    BBFFH

need to determine keys:
Ctrl ESC
*/

static INPUT_PORTS_START( h01b )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)          PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)          PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)          PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)  PORT_CHAR(' ')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)          PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)          PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)          PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)          PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_END)    PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)          PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)          PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)          PORT_CHAR('C') PORT_CHAR('c')
//  PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Clear") PORT_CODE(KEYCODE_HOME)   PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC)      PORT_CHAR(27)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)          PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)          PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)          PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E/C") PORT_CODE(KEYCODE_TILDE)    PORT_CHAR('`') PORT_CHAR('~')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)          PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)          PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)          PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("- =") PORT_CODE(KEYCODE_EQUALS)   PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)          PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)          PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)          PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)  PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)          PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)          PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)          PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)          PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)      PORT_CHAR(13)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)          PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)          PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA)    PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)          PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)          PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP)     PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_BACKSPACE)   PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD)          PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)          PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("; +") PORT_CODE(KEYCODE_COLON)    PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH)    PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(": *") PORT_CODE(KEYCODE_MINUS)    PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)    PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END


/*

模拟器中的按键位置安排参照 TRS-80

NF-500A

   KD7 KD6 KD5 KD4 KD3 KD2 KD1 KD0  scan address
A0      R   E   5 CTRL? 6   T   W    BFFEH
A1      3   2   Y  E/C  U   4   1    BFFDH
A2      9   :   8   -   7   0   下   BFFBH
A3      D   S   G  ESC  H   F   Q    BFF7H
A4      X   A   V   Z       C  BRK   BFEFH
A5      L BS 左 K space J   ;   右   BFDFH
A6      M   .   N       B   ,   /    BFBFH
A7      P  RETN O SHIFT I   [        BF7FH

3 56 34 51 key functions not verified, temporarily mapped to ] TAB \ '

E/C E汉 = ~  translates to ASCII 20
ESC translates to ASCII 31
BS translates to ASCII 8
61 translates to ASCII 13 enter
16 translates to ASCII 10 down
56 translates to ASCII 16
27 3 34 51  no action

Program to test key ASCII codes
10 A$=INKEY$
20 IF LEN(A$)>0 THEN PRINT ASC(A$)
30 GOTO 10

TRS-80
left 8 right 9 up 91 down 10
Clear 31
@ 64
backspace 8

*/

/* Input ports */
static INPUT_PORTS_START( h01x )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)          PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)          PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)          PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)          PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)          PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E/C") PORT_CODE(KEYCODE_TILDE)    PORT_CHAR('`') PORT_CHAR('~')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)          PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)          PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)        PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD)          PORT_CHAR('0')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)          PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("- =") PORT_CODE(KEYCODE_EQUALS)   PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(": *") PORT_CODE(KEYCODE_MINUS)    PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)          PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)          PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)          PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC)      PORT_CHAR(27)
	//PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Clear") PORT_CODE(KEYCODE_HOME)   PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)          PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)          PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)          PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_END)    PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)          PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)          PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)          PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)          PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)          PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("; +") PORT_CODE(KEYCODE_COLON)    PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)          PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)          PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_BACKSPACE)   PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)          PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH)    PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA)    PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)          PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)          PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP)     PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)          PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)          PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)          PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)      PORT_CHAR(13)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)          PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE8")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE9")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE10")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END


/*  Machine     */

void h01x_state::machine_start()
{
	save_item(NAME(m_bank));
	save_item(NAME(m_cassette_data));

	m_cassette_data_timer = timer_alloc(FUNC(h01x_state::cassette_data_callback), this);
	m_cassette_data_timer->adjust(attotime::zero, 0, attotime::from_hz(48000));
}

void h01x_state::machine_reset()
{
	m_bank = 0x00;

	m_ram_ptr = m_ram->pointer();
	m_vram_ptr = m_vram->pointer();
}

MC6845_UPDATE_ROW( h01x_state::crtc_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u32 *p = &bitmap.pix(y);

	for (u16 x = 0; x < x_count; x++)
	{
		u16 mem = ((ma+x)*16 + ra) & 0x3fff;
		u8 gfx = 0;
		if (ra < 16)
			gfx = m_vram_ptr[mem] ^ ((x == cursor_x) ? 15 : 0);

		*p++ = palette[BIT(gfx, 3)];
		*p++ = palette[BIT(gfx, 2)];
		*p++ = palette[BIT(gfx, 1)];
		*p++ = palette[BIT(gfx, 0)];
	}
}

void h01x_state::port_70_w(uint8_t data)
{
	m_bank = data & 0xc0;

	// bit5, speaker
	m_speaker->level_w(BIT(data, 5));

	// bit4, cassette
	m_cassette->output(BIT(data, 4) ? 1.0 : -1.0);
}

uint8_t h01x_state::port_50_r()
{
	// bit 7, cassette input
	return m_cassette_data ? 0xff : 0x7f;
}


// 0x0000 --- 0x3FFF
uint8_t h01x_state::mem_0000_r(offs_t offset)
{
	return m_rom[offset];
}

void h01x_state::mem_0000_w(uint8_t data)
{
}

// 0x4000 --- 0x7FFF
uint8_t h01x_state::mem_4000_r(offs_t offset)
{
	return m_ram_ptr[offset];
}

void h01x_state::mem_4000_w(offs_t offset, uint8_t data)
{
	m_ram_ptr[offset] = data;
}

// 0x8000 --- 0xBFFF
uint8_t h01x_state::mem_8000_r(offs_t offset)
{
	switch (m_bank) {
	case 0xc0:
		return m_hzrom[offset];
	case 0x40:
		if ((offset & 0xf000) == 0x3000) {
			u8 result = 0xff;
			for (int i = 0; i < 11; i++) {
				if (!BIT(offset, i))
					result &= m_io_keyboard[i]->read();
			}
			return result;
		} else {
			return 0xff;
		}
	case 0x00:
		return m_ram_ptr[offset + 0x4000];
	default:
		return 0xff;
	}
}

void h01x_state::mem_8000_w(offs_t offset, uint8_t data)
{
	if (m_bank == 0x00)
		m_ram_ptr[offset + 0x4000] = data;
}


// 0xC000 --- 0xFFFF
uint8_t h01x_state::mem_c000_r(offs_t offset)
{
	if (m_bank == 0xc0)
		return m_hzrom[offset + 0x4000];
	else if (m_bank == 0x40)
		return m_vram_ptr[offset];
	else
		return 0xff;
}

void h01x_state::mem_c000_w(offs_t offset, uint8_t data)
{
	if (m_bank == 0x40)
		m_vram_ptr[offset] = (data & 0x0f) | 0xf0;
}


TIMER_CALLBACK_MEMBER(h01x_state::cassette_data_callback)
{
	m_cassette_data = false;

	if (m_cassette->input() > 0.2)
		m_cassette_data = true;
}


/* ROM definition */
ROM_START(h01b)
	ROM_REGION(0x4000, "maincpu",0)
	ROM_LOAD("h-01b_sysrom.bin",   0x0000, 0x4000, CRC(b52093a7) SHA1(8c874765033444688c906b1a987a73f2c3ec83fb))

	ROM_REGION(0x8000,"hzrom",0)
	ROM_LOAD("h-01b_hzrom.bin",   0x0000, 0x8000, CRC(f0d6a7ac) SHA1(72151d3215bc8b26f983466221fe5f4009727ce8))
ROM_END

ROM_START(nf500a)
	ROM_REGION(0x4000, "maincpu",0)
	ROM_LOAD("u2-hn27128-9adc.bin",   0x0000, 0x4000, CRC(147dae83) SHA1(856b0970c603e88707ce8638be5dbd8ab1c42a1b))

	ROM_REGION(0x8000,"hzrom",0)
	ROM_LOAD("u4-hn27256-32aa.bin",   0x0000, 0x8000, CRC(9ecfddaa) SHA1(54b6e1b43f79b7705e95edda845b21d7326d48e2))
ROM_END

ROM_START(h01jce)
	ROM_REGION(0x4000, "maincpu",0)
	ROM_LOAD("m5l27128k_9b99.bin",   0x0000, 0x4000, CRC(59be30df) SHA1(21ccc765d13992753ec0457e09ac97cea82888a9))

	ROM_REGION(0x8000,"hzrom",0)
	ROM_LOAD("u4-hn27256-32aa.bin",   0x0000, 0x8000, CRC(9ecfddaa) SHA1(54b6e1b43f79b7705e95edda845b21d7326d48e2))

	ROM_REGION(0x4000, "exrom",0)
	ROM_LOAD("hn4827128g_f0f9.bin",   0x0000, 0x4000, CRC(36bffec0) SHA1(5b4b24c54eba0a8b69f291ca656ea27a3685f42e))
ROM_END

} // anonymous namespace


/* Driver */

// H-01B中文教育电脑
// 普乐电器公司
// cpu      Z-80A 2MHz

// NF500A教学电脑
// 国营八三〇厂制造
// cpu      Z-80A 4MHz
// video    MC6845P
// sysrom   16KB EPROM
// hzrom    32KB EPROM
// ram      32KB SRAM
// vram     16Kx4bit DRAM

// JCE
// 广东江门计算机应用设备厂
// video    HD6845SP
// sysrom   16KB EPROM
// hzrom    32KB EPROM
// extrom   16KB EPROM

// Startup screens
// H-01B  : H-01型中文教育电脑 普乐电器公司制造
// NF500A : H-01型汉字微电脑   中国科学院H电脑公司
// JCE    : H-01型中文普及电脑 北岳电子有限公司制造

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS         INIT          COMPANY                                      FULLNAME     FLAGS
COMP( 1985, h01b,    0,      0,      h01b,    h01b,    h01x_state,   empty_init,   "China H Computer Company",                  "H-01B",     MACHINE_SUPPORTS_SAVE )
COMP( 1985, nf500a,  0,      0,      nf500a,  h01x,    h01x_state,   empty_init,   "China State-owned 830 Factory",             "NF500A",    MACHINE_SUPPORTS_SAVE )
COMP( 1987, h01jce,  0,      0,      h01jce,  h01x,    h01x_state,   empty_init,   "China Jiangmen Computer Equipment Factory", "H-01 JCE",  MACHINE_SUPPORTS_SAVE )



h19.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Mark Garlanger
/***************************************************************************

  Heathkit H19
  Zenith Data Systems Z-19

    A smart terminal designed and manufactured by Heath Company. This
    is identical to the Zenith Data Systems Z-19.

****************************************************************************/

#include "emu.h"

#include "bus/heathzenith/h19/tlb.h"
#include "bus/rs232/rs232.h"

#include "h19.lh"


namespace {

class h19_state : public driver_device
{
public:
	h19_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_tlbc(*this, "tlbc")
	{
	}

	void h19(machine_config &config);

private:
	required_device<heath_tlb_connector> m_tlbc;

};

static void tlb_options(device_slot_interface &device)
{
	device.option_add("heath",      HEATH_TLB);
	device.option_add("gp19",       HEATH_GP19);
	device.option_add("imaginator", HEATH_IMAGINATOR);
	device.option_add("super19",    HEATH_SUPER19);
	device.option_add("superset",   HEATH_SUPERSET);
	device.option_add("ultrarom",   HEATH_ULTRA);
	device.option_add("watzman",    HEATH_WATZ);
}

void h19_state::h19(machine_config &config)
{
	config.set_default_layout(layout_h19);

	HEATH_TLB_CONNECTOR(config, m_tlbc, tlb_options, "heath");
	m_tlbc->serial_data_callback().set("dte", FUNC(rs232_port_device::write_txd));
	m_tlbc->dtr_callback().set("dte", FUNC(rs232_port_device::write_dtr));
	m_tlbc->rts_callback().set("dte", FUNC(rs232_port_device::write_rts));

	rs232_port_device &dte(RS232_PORT(config, "dte", default_rs232_devices, "loopback"));
	dte.rxd_handler().set(m_tlbc, FUNC(heath_tlb_connector::serial_in_w));
	dte.dcd_handler().set(m_tlbc, FUNC(heath_tlb_connector::rlsd_in_w));
	dte.dsr_handler().set(m_tlbc, FUNC(heath_tlb_connector::dsr_in_w));
	dte.cts_handler().set(m_tlbc, FUNC(heath_tlb_connector::cts_in_w));
}

// ROM definition
ROM_START( h19 )
ROM_END

} // anonymous namespace

//    year  name  parent  compat  machine input  class       init         company                fullname          flags
COMP( 1979, h19,  0,      0,      h19,    0,     h19_state,  empty_init,  "Heath Company",       "H-19 Terminal",  MACHINE_SUPPORTS_SAVE )



h8.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Mark Garlanger
/***************************************************************************

    Heathkit H8

    This system uses Octal and Split-Octal rather than the usual hexadecimal.

    STATUS:
        It runs, keyboard works, you can enter data.
        Serial console works. You can make it visible by setting Video
        Options in settings.

    Meaning of LEDs:
        PWR = Power is turned on (+5V is present at on front panel)
        MON = The front panel is being serviced by the cpu (controls should work)
        RUN = CPU is running (not halted)
        ION = Interrupts are enabled

    Pasting:
        H8    | mame key
    -----------------------
        0-F   | as is
        +     |   ^
        -     |   V
        MEM   |   -
        ALTER |   =

        Addresses must have all 6 digits entered. Data must have all 3 digits entered.
        System has a short beep for each key, and a slightly longer beep for each
        group of 3 digits. The largest number allowed is octal 377 (=256/0xFF).

    Test Paste:
        -041000=123 245 333 144 255 366 077=-041000
        Now press up-arrow to confirm the data has been entered.

    Official test program from pages 4 to 8 of the operator's manual:
        -040100=076 002 062 010 040 006 004 041 170 040 021 013 040 016 011 176
                022 043 023 015 302 117 040 016 003 076 377 315 053 000 015 302
                131 040 005 302 112 040 076 062 315 140 002 076 062 315 053 000
                076 062 315 140 002 303 105 040 377 262 270 272 275 377 222 200
                377 237 244 377 272 230 377 220 326 302 377 275 272 271 271 373
                271 240 377 236 376 362 236 376 362 236 376 362 R6=040100=4

****************************************************************************/

#include "emu.h"

#include "bus/heathzenith/h8/cards.h"
#include "bus/heathzenith/h8/h8bus.h"

namespace {

class h8_state : public driver_device
{
public:
	h8_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_h8bus(*this, "h8bus")
		, m_p1(*this, "p1")
		, m_p2(*this, "p2")
		{}

	void h8(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void device_config_complete() override  ATTR_COLD;

private:
	required_device<h8bus_device> m_h8bus;
	required_device<h8bus_slot_device> m_p1;
	required_device<h8bus_slot_device> m_p2;
};

// Input ports
static INPUT_PORTS_START( h8 )
INPUT_PORTS_END

void h8_state::machine_start()
{
}

void h8_state::device_config_complete()
{
	// Connect up the p201 cable between p1 and p2 boards. This is separate from the h8bus.
	auto p1_lookup = m_p1.lookup()->get_card_device();
	auto p2_lookup = m_p2.lookup()->get_card_device();

	// avoid crash when there isn't a card installed in either slot.
	if (p1_lookup && p2_lookup)
	{
		device_p201_p1_card_interface *p1 = dynamic_cast<device_p201_p1_card_interface *>(p1_lookup);
		device_p201_p2_card_interface *p2 = dynamic_cast<device_p201_p2_card_interface *>(p2_lookup);

		p1->p201_reset_cb().set(*p2, FUNC(device_p201_p2_card_interface::p201_reset_w));
		p1->p201_int1_cb().set(*p2, FUNC(device_p201_p2_card_interface::p201_int1_w));
		p1->p201_int2_cb().set(*p2, FUNC(device_p201_p2_card_interface::p201_int2_w));

		p2->p201_inte_cb().set(*p1, FUNC(device_p201_p1_card_interface::p201_inte_w));
	}
}

void h8_state::h8(machine_config &config)
{
	H8BUS(config, m_h8bus, 0);

	H8BUS_SLOT(config,  m_p1, "h8bus", h8_p1_cards,  "fp");
	H8BUS_SLOT(config,  m_p2, "h8bus", h8_p2_cards,  "cpu8080");
	H8BUS_SLOT(config,  "p3", "h8bus", h8_cards,     "wh_8_64");
	H8BUS_SLOT(config,  "p4", "h8bus", h8_cards,     nullptr);
	H8BUS_SLOT(config,  "p5", "h8bus", h8_cards,     nullptr);
	H8BUS_SLOT(config,  "p6", "h8bus", h8_cards,     nullptr);
	H8BUS_SLOT(config,  "p7", "h8bus", h8_cards,     nullptr);
	H8BUS_SLOT(config,  "p8", "h8bus", h8_cards,     nullptr);
	H8BUS_SLOT(config,  "p9", "h8bus", h8_cards,     "h_8_5");
	H8BUS_SLOT(config, "p10", "h8bus", h8_p10_cards, "ha_8_8");
}

// ROM definition
ROM_START( h8 )
ROM_END

} // anonymous namespace

// Driver

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS,    INIT        COMPANY          FULLNAME                        FLAGS
COMP( 1977, h8,   0,      0,      h8,      h8,    h8_state, empty_init, "Heath Company", "Heathkit H8 Digital Computer", MACHINE_SUPPORTS_SAVE )



h89.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco, Mark Garlanger
/***************************************************************************

  Heathkit H89

    Heath Company made several very similar systems, including
      - H88 - kit, came with a cassette interface board instead of floppy controller
      - H89 - kit, came with a hard-sectored floppy disk controller
      - WH89 - was factory assembled

    Heath's parent company Zenith, also sold systems under the Zenith Data
    Systems brand. These were all factory assembled
      - Z-89 - same as Heath's H89, but assembled
      - Z-90 - came with a soft-sectored floppy disk controller

    Monitor Commands (for MTR-90):
      B Boot
      C Convert (number)
      G Go (address)
      I In (address)
      O Out (address,data)
      R Radix (H/O)
      S Substitute (address)
      T Test Memory
      V View

    Monitor Commands (for MTR-88)
      B Boot
      D Dump - dump a program to cassette
      G Go (address)
      L Load - load a program from cassette
      P Program Counter (address) - select an address in the PC
      S Substitute - inspect or change memory

    Monitor Commands (for MTR-89)
      B Boot
      G Go (address)
      P Program Counter (address) - select an address in the PC
      S Substitute - inspect or change memory

****************************************************************************/

#include "emu.h"

#include "bus/heathzenith/h19/tlb.h"
#include "bus/heathzenith/h89/h89bus.h"
#include "bus/heathzenith/h89/cdr_fdc_880h.h"
#include "bus/heathzenith/h89/h_88_3.h"
#include "bus/heathzenith/h89/h_88_5.h"
#include "bus/heathzenith/h89/mms77316_fdc.h"
#include "bus/heathzenith/h89/sigmasoft_parallel_port.h"
#include "bus/heathzenith/h89/sigmasoft_sound.h"
#include "bus/heathzenith/h89/we_pullup.h"
#include "bus/heathzenith/h89/z_89_11.h"
#include "bus/heathzenith/h89/z37_fdc.h"
#include "bus/heathzenith/intr_cntrl/intr_cntrl.h"

#include "cpu/z80/z80.h"
#include "machine/ins8250.h"
#include "machine/ram.h"
#include "machine/timer.h"

#include "softlist_dev.h"

#include "h89.lh"


// Single Step
#define LOG_SS    (1U << 1)
#define LOG_SETUP (1U << 2)

#define VERBOSE (LOG_SETUP)
#include "logmacro.h"

#define LOGSS(...)    LOGMASKED(LOG_SS,    __VA_ARGS__)
#define LOGSETUP(...) LOGMASKED(LOG_SETUP, __VA_ARGS__)

#ifdef _MSC_VER
#define FUNCNAME __func__
#else
#define FUNCNAME __PRETTY_FUNCTION__
#endif


namespace {

/**
 * Base Heathkit H89 functionality
 */
class h89_base_state : public driver_device
{
protected:
	h89_base_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_maincpu_region(*this, "maincpu")
		, m_mem_view(*this, "rom_bank")
		, m_ram(*this, RAM_TAG)
		, m_floppy_ram(*this, "floppyram")
		, m_tlbc(*this, "tlbc")
		, m_intr_socket(*this, "intr_socket")
		, m_h89bus(*this, "h89bus")
		, m_console(*this, "console")
		, m_config(*this, "CONFIG")
		, m_sw501(*this, "SW501")
	{
	}

	void h89_base(machine_config &config);

	required_device<z80_device>                          m_maincpu;
	required_memory_region                               m_maincpu_region;
	memory_view                                          m_mem_view;
	required_device<ram_device>                          m_ram;
	required_shared_ptr<u8>                              m_floppy_ram;
	required_device<heath_tlb_connector>                 m_tlbc;
	required_device<heath_intr_socket>                   m_intr_socket;
	required_device<h89bus_device>                       m_h89bus;
	required_device<ins8250_device>                      m_console;
	required_ioport                                      m_config, m_sw501;
	memory_access<16, 0, 0, ENDIANNESS_LITTLE>::specific m_program;

	// General Purpose Port (GPP)
	u8   m_gpp;

	bool m_rom_enabled;
	bool m_timer_intr_enabled;
	bool m_single_step_enabled;
	bool m_floppy_ram_we;

	// single step flags
	bool m_555a_latch;
	bool m_555b_latch;
	bool m_556b_latch;

	u32  m_cpu_speed_multiplier;

	bool m_installed;

	// Clocks
	static constexpr XTAL H89_CLOCK                      = XTAL(12'288'000) / 6;
	static constexpr XTAL INS8250_CLOCK                  = XTAL(1'843'200);

	static constexpr u8 GPP_SINGLE_STEP_BIT             = 0;
	static constexpr u8 GPP_ENABLE_TIMER_INTERRUPT_BIT  = 1;
	static constexpr u8 GPP_MEM0_BIT                    = 4;
	static constexpr u8 GPP_MEM1_BIT                    = 5;
	static constexpr u8 GPP_IO0_BIT                     = 6;
	static constexpr u8 GPP_IO1_BIT                     = 7;

	void update_mem_view();

	void update_gpp(u8 gpp);
	void port_f2_w(u8 data);
	u8 read_sw();

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	TIMER_DEVICE_CALLBACK_MEMBER(h89_irq_timer);

	void h89_mem(address_map &map) ATTR_COLD;
	void map_fetch(address_map &map) ATTR_COLD;
	u8 m1_r(offs_t offset);

	void h89_base_io(address_map &map) ATTR_COLD;

	void set_wait_state(int data);

	void set_fmwe(int data);

	u8 raise_NMI_r();
	void raise_NMI_w(u8 data);
	void console_intr(int data);
	void reset_line(int data);
	void reset_single_step_state();

	template <int line> void slot_irq(int state);

	void h89_left_cards(device_slot_interface &device);
	void h89_right_cards(device_slot_interface &device);
	void h89_right_cards_mms(device_slot_interface &device);
	void h89_right_p506_cards(device_slot_interface &device);
};

/**
 * Heathkit H88
 *  - ROM MTR-88
 *  - H-88-5 Cassette Interface Board
 *
 */
class h88_state : public h89_base_state
{
public:
	h88_state(const machine_config &mconfig, device_type type, const char *tag)
		: h89_base_state(mconfig, type, tag)
	{
	}

	void h88(machine_config &config);
};


/**
 * Heathkit H89
 *  - Z-89-37 Soft-sectored Floppy Controller
 *
 */
class h89_state : public h89_base_state
{
public:
	h89_state(const machine_config &mconfig, device_type type, const char *tag)
		: h89_base_state(mconfig, type, tag)
	{
	}

	void h89(machine_config &config);
};

class h89_cdr_state : public h89_base_state
{
public:
	h89_cdr_state(const machine_config &mconfig, device_type type, const char *tag)
		: h89_base_state(mconfig, type, tag)
	{
	}

	void h89_cdr(machine_config &config);
};

/**
 * Heathkit H89 with MMS hardware
 *  - MMS 77316 - DD controller
 *
 * Functionality already implemented/Same as Heath options
 *  - MMS 77311 - 16k RAM
 *  - MMS 77312 - ORG-0 CP/M Mod
 *
 * Hardware currently planned to be implemented:
 *  - MMS 77318 - 128k RAM board
 *  - MMS 77319 - Video Output
 *  - MMS 77320 - SASI board
 *  - MMS 77322 - Network Controller
 *
 * Other hardware MMS offered
 *  - MMS 77314 - Remex H47 / IMI(Corvus) / 3 serial port
 *  - MMS 77315 - Cameo I/o
 *  - MMS 77317 - ACT/XCOMP I/O
 *
 */
class h89_mms_state : public h89_base_state
{
public:
	h89_mms_state(const machine_config &mconfig, device_type type, const char *tag)
		: h89_base_state(mconfig, type, tag)
	{
	}

	void h89_mms(machine_config &config);

};


/*
  The H89 supported 16K, 32K, 48K, or 64K of RAM. The first 8K of address space
  is reserved for the monitor ROM, floppy ROM, and scratch pad RAM. For 16k-48K
  sizes, the upper 8k of memory is remapped to the first 8K when the ROM is disabled.
  For systems with 64K of RAM, the upper half of the expansion board is permanently
  mapped to the lower 8K. Even when ROM is mapped, any writes will still occur
  to the RAM.

  H89 Lower 8K address map

        HDOS Mode                       CP/M Mode
  ------------------- 0x2000 (8k) ----------------
  |   Floppy ROM   |                |            |
  ------------------- 0x1800 (6k)   |            |
  |   Floppy RAM   |                |            |
  ------------------- 0x1400 (5k)   |    RAM     |
  |      Open      |                |            |
  ------------------- 0x1000 (4k)   |            |
  |   MTR-90 ROM   |                |            |
  -................-- 0x0800 (2k)   |            |
  | MTR(88/89) ROM |                |            |
  ------------------- 0x0000 (0k) ----------------


        16K RAM Example

      HDOS                           CP/M
  ------------- 24k
  |    RAM    |  ------+
  ------------- 16k    |         ------------- 16k
  |    RAM    |  ------------->  |    RAM    |
  -------------  8k    |         -------------  8k
  |    ROM    |        +------>  |    RAM    |
  -------------  0k              -------------  0k

*/
void h89_base_state::h89_mem(address_map &map)
{
	map.unmap_value_high();

	map(0x0000, 0xffff).view(m_mem_view);

	// View 0 - ROM / Floppy RAM R/O
	// View 1 - ROM / Floppy RAM R/W
	// monitor ROM
	m_mem_view[0](0x0000, 0x0fff).rom().region(m_maincpu_region, 0).unmapw();
	m_mem_view[1](0x0000, 0x0fff).rom().region(m_maincpu_region, 0).unmapw();

	// Floppy RAM
	m_mem_view[0](0x1400, 0x17ff).readonly().share(m_floppy_ram);
	m_mem_view[1](0x1400, 0x17ff).ram().share(m_floppy_ram);

	// Floppy ROM
	m_mem_view[0](0x1800, 0x1fff).rom().region(m_maincpu_region, 0x1800).unmapw();
	m_mem_view[1](0x1800, 0x1fff).rom().region(m_maincpu_region, 0x1800).unmapw();
}

void h89_base_state::map_fetch(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(h89_base_state::m1_r));
}

u8 h89_base_state::m1_r(offs_t offset)
{
	u8 data = m_program.read_byte(offset);

	if (!machine().side_effects_disabled() && m_single_step_enabled && !m_556b_latch)
	{
		LOGSS("single step m1_r - data: 0x%02x, 555a: %d, 555b: %d, 556b: %d\n", data, m_555a_latch, m_555b_latch, m_556b_latch);

		if (!m_555a_latch)
		{
			// Wait for EI instruction
			if (data == 0xfb)
			{
				m_555a_latch = true;
			}
		}
		else if (!m_555b_latch)
		{
			m_555b_latch = true;
		}
		else if (!m_556b_latch)
		{
			m_556b_latch = true;

			m_intr_socket->set_irq_level(2, ASSERT_LINE);
		}
	}

	return data;
}

void h89_base_state::h89_base_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
}

// Input ports
static INPUT_PORTS_START( h88 )

	PORT_START("SW501")
	// MTR-88  (444-40)
	PORT_DIPUNUSED_DIPLOC(0x01, 0x00, "SW501:1")
	PORT_DIPUNUSED_DIPLOC(0x02, 0x00, "SW501:2")
	PORT_DIPUNUSED_DIPLOC(0x04, 0x00, "SW501:3")
	PORT_DIPUNUSED_DIPLOC(0x08, 0x00, "SW501:4")
	PORT_DIPUNUSED_DIPLOC(0x10, 0x00, "SW501:5")
	PORT_DIPNAME( 0x20, 0x20, "Perform memory test at start" )       PORT_DIPLOCATION("SW501:6")
	PORT_DIPSETTING(    0x20, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0xc0, 0x00, "Console Baud rate" )                  PORT_DIPLOCATION("SW501:7,8")
	PORT_DIPSETTING(    0x00, "9600" )
	PORT_DIPSETTING(    0x40, "19200" )
	PORT_DIPSETTING(    0x80, "38400" )
	PORT_DIPSETTING(    0xc0, "57600" )

	PORT_START("CONFIG")
	PORT_CONFNAME( 0x03, 0x00, "CPU Clock Speed Upgrade")
	PORT_CONFSETTING(    0x00, DEF_STR( None ) )
	PORT_CONFSETTING(    0x01, "2 / 4 MHz")
	PORT_CONFSETTING(    0x02, "2 / 6 MHz")

INPUT_PORTS_END


static INPUT_PORTS_START( h89 )

	PORT_START("SW501")
	// Generic definition
	PORT_DIPNAME( 0x01, 0x00, "Switch 0" )                           PORT_DIPLOCATION("SW501:1")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x00)
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, "Switch 1" )                           PORT_DIPLOCATION("SW501:2")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x00)
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, "Switch 2" )                           PORT_DIPLOCATION("SW501:3")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x00)
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, "Switch 3" )                           PORT_DIPLOCATION("SW501:4")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x00)
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, "Switch 4" )                           PORT_DIPLOCATION("SW501:5")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x00)
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, "Switch 5" )                           PORT_DIPLOCATION("SW501:6")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x00)
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, "Switch 6" )                           PORT_DIPLOCATION("SW501:7")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x00)
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, "Switch 7" )                           PORT_DIPLOCATION("SW501:8")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x00)
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )

	// MTR-90 (444-84 or 444-142)
	PORT_DIPNAME( 0x03, 0x00, "Disk I/O #2" )                        PORT_DIPLOCATION("SW501:1,2")     PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x04)
	PORT_DIPSETTING(    0x00, "H-88-1 (Not yet implemented)" )
	PORT_DIPSETTING(    0x01, "H/Z-47 (Not yet implemented)" )
	PORT_DIPSETTING(    0x02, "Z-67 (Not yet implemented)" )
	PORT_DIPSETTING(    0x03, "Undefined" )
	PORT_DIPNAME( 0x0c, 0x00, "Disk I/O #1" )                        PORT_DIPLOCATION("SW501:3,4")     PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x04)
	PORT_DIPSETTING(    0x00, "H-89-37" )
	PORT_DIPSETTING(    0x04, "H/Z-47 (Not yet implemented)" )
	PORT_DIPSETTING(    0x08, "Z-67 (Not yet implemented)" )
	PORT_DIPSETTING(    0x0c, "Undefined" )
	PORT_DIPNAME( 0x10, 0x00, "Primary Boot from" )                  PORT_DIPLOCATION("SW501:5")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x04)
	PORT_DIPSETTING(    0x00, "Disk I/O #2" )
	PORT_DIPSETTING(    0x10, "Disk I/O #1" )
	PORT_DIPNAME( 0x20, 0x20, "Perform memory test at start" )       PORT_DIPLOCATION("SW501:6")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x04)
	PORT_DIPSETTING(    0x20, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x40, 0x00, "Console Baud rate" )                  PORT_DIPLOCATION("SW501:7")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x04)
	PORT_DIPSETTING(    0x00, "9600" )
	PORT_DIPSETTING(    0x40, "19200" )
	PORT_DIPNAME( 0x80, 0x00, "Boot mode" )                          PORT_DIPLOCATION("SW501:8")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x04)
	PORT_DIPSETTING(    0x00, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x80, "Auto" )

	// MTR-89 (444-62)
	PORT_DIPNAME( 0x03, 0x00, "Disk I/O #2" )                        PORT_DIPLOCATION("SW501:1,2")     PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x08)
	PORT_DIPSETTING(    0x00, "H-88-1" )
	PORT_DIPSETTING(    0x01, "H/Z-47" )
	PORT_DIPSETTING(    0x02, "Undefined" )
	PORT_DIPSETTING(    0x03, "Undefined" )
	PORT_DIPNAME( 0x0c, 0x00, "Disk I/O #1" )                        PORT_DIPLOCATION("SW501:3,4")     PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x08)
	PORT_DIPSETTING(    0x00, "Unused" )
	PORT_DIPSETTING(    0x04, "H/Z-47" )
	PORT_DIPSETTING(    0x08, "Undefined" )
	PORT_DIPSETTING(    0x0c, "Undefined" )
	PORT_DIPNAME( 0x10, 0x00, "Primary Boot from" )                  PORT_DIPLOCATION("SW501:5")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x08)
	PORT_DIPSETTING(    0x00, "Disk I/O #2" )
	PORT_DIPSETTING(    0x10, "Disk I/O #1" )
	PORT_DIPNAME( 0x20, 0x20, "Perform memory test at start" )       PORT_DIPLOCATION("SW501:6")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x08)
	PORT_DIPSETTING(    0x20, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x40, 0x00, "Console Baud rate" )                  PORT_DIPLOCATION("SW501:7")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x08)
	PORT_DIPSETTING(    0x00, "9600" )
	PORT_DIPSETTING(    0x40, "19200" )
	PORT_DIPNAME( 0x80, 0x00, "Boot mode" )                          PORT_DIPLOCATION("SW501:8")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x08)
	PORT_DIPSETTING(    0x00, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x80, "Auto" )

	// MMS 444-84B (and possibly 444-84A)
	PORT_DIPNAME( 0x03, 0x00, "Disk I/O #2" )                        PORT_DIPLOCATION("SW501:1,2")     PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x0c)
	PORT_DIPSETTING(    0x00, "H-88-1" )
	PORT_DIPSETTING(    0x01, "H/Z-47 (Not yet implemented)" )
	PORT_DIPSETTING(    0x02, "MMS 77320 SASI or Z-67 (Not yet implemented)" )
	PORT_DIPSETTING(    0x03, "MMS 77422 Network Controller" )
	PORT_DIPNAME( 0x0c, 0x00, "Disk I/O #1" )                        PORT_DIPLOCATION("SW501:3,4")     PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x0c)
	PORT_DIPSETTING(    0x00, "H-89-37" )
	PORT_DIPSETTING(    0x04, "H/Z-47 (Not yet implemented)" )
	PORT_DIPSETTING(    0x08, "MMS 77320 SASI or Z-67 (Not yet implemented)" )
	PORT_DIPSETTING(    0x0c, "MMS 77422 Network Controller" )
	PORT_DIPNAME( 0x70, 0x00, "Default Boot Device" )                PORT_DIPLOCATION("SW501:5,6,7")   PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x0c)
	PORT_DIPSETTING(    0x00, "MMS 77316 Dbl Den 5\"" )
	PORT_DIPSETTING(    0x10, "MMS 77316 Dbl Den 8\"" )
	PORT_DIPSETTING(    0x20, "Disk Device at 0x7C" )
	PORT_DIPSETTING(    0x30, "Disk Device at 0x78" )
	PORT_DIPSETTING(    0x40, "reserved for future use" )
	PORT_DIPSETTING(    0x50, "reserved for future use" )
	PORT_DIPSETTING(    0x60, "MMS Network (77422)" )
	PORT_DIPSETTING(    0x70, "Use MMS I/O board Config Port" )
	PORT_DIPNAME( 0x80, 0x00, "Boot mode" )                          PORT_DIPLOCATION("SW501:8")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x0c)
	PORT_DIPSETTING(    0x00, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x80, "Auto" )

	// Kres KMR-100
	PORT_DIPNAME( 0x0f, 0x00, "Default Boot Device" )                PORT_DIPLOCATION("SW501:1,2,3,4") PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x10)
	PORT_DIPSETTING(    0x00, "H-17 hard-sectored 5\" floppy units 0-2" )
	PORT_DIPSETTING(    0x01, "H-37 soft-sectored 5\" floppy units 0-3" )
	PORT_DIPSETTING(    0x02, "Corvus hard disk/Magnolia interface, partitions 0-8" )
	PORT_DIPSETTING(    0x03, "CDR 5\"/8\" double density floppy, units 0-3" )
	PORT_DIPSETTING(    0x04, "H-47 8\" floppy at port 0x78/0170, units 0-3" )
	PORT_DIPSETTING(    0x05, "H-47 8\" floppy at port 0x7c/0174, units 0-3" )
	PORT_DIPSETTING(    0x06, "Reserved" )
	PORT_DIPSETTING(    0x07, "Help - lists boot devices" )
	PORT_DIPSETTING(    0x08, "Reserved" )
	PORT_DIPSETTING(    0x09, "SASI controller or Z-67 at port 0x78/0170, units 0-7" )
	PORT_DIPSETTING(    0x0a, "SASI controller or Z-67 at port 0x7c/0174, units 0-7" )
	PORT_DIPSETTING(    0x0b, "Livingston 8\" single density floppy, units 0-3" )
	PORT_DIPSETTING(    0x0c, "Magnolia 5\"/8\" double density floppy, units 0-3 (8\"), 4-7 (5\")" )
	PORT_DIPSETTING(    0x0d, "Reserved" )
	PORT_DIPSETTING(    0x0e, "Reserved" )
	PORT_DIPSETTING(    0x0f, "Magnolia 128K pseudo disk, banks 0-1" )
	PORT_DIPNAME( 0x10, 0x00, "Map ROM into RAM on boot" )           PORT_DIPLOCATION("SW501:5")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x10)
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPSETTING(    0x10, DEF_STR( No ) )
	PORT_DIPNAME( 0x20, 0x20, "Perform memory test at start" )       PORT_DIPLOCATION("SW501:6")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x10)
	PORT_DIPSETTING(    0x20, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x40, 0x00, "Have a LLL controller installed" )    PORT_DIPLOCATION("SW501:7")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x10)
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x80, 0x00, "Boot mode" )                          PORT_DIPLOCATION("SW501:8")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x10)
	PORT_DIPSETTING(    0x00, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x80, "Auto" )

	// Ultimeth MTRHEX-4k
	// (values based on testing and comparison with the Kres KMR-100 ROM which was also written by Ultimeth)
	PORT_DIPNAME( 0x0f, 0x00, "Default Boot Device" )                PORT_DIPLOCATION("SW501:1,2,3,4") PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x14)
	PORT_DIPSETTING(    0x00, "H-17 hard-sectored 5\" floppy" )
	PORT_DIPSETTING(    0x01, "H-37 soft-sectored 5\" floppy" )
	PORT_DIPSETTING(    0x02, "? Corvus hard disk/Magnolia interface" )
	PORT_DIPSETTING(    0x03, "? CDR 5\"/8\" double density floppy" )
	PORT_DIPSETTING(    0x04, "? H-47 8\" floppy at port 0x78/0170" )
	PORT_DIPSETTING(    0x05, "? H-47 8\" floppy at port 0x7c/0174" )
	PORT_DIPSETTING(    0x06, "? Reserved" )
	PORT_DIPSETTING(    0x07, "? Reserved" )
	PORT_DIPSETTING(    0x08, "? Reserved" )
	PORT_DIPSETTING(    0x09, "? SASI controller or Z-67 at port 0x78/0170" )
	PORT_DIPSETTING(    0x0a, "? SASI controller or Z-67 at port 0x7c/0174" )
	PORT_DIPSETTING(    0x0b, "? Livingston 8\" single density floppy" )
	PORT_DIPSETTING(    0x0c, "? Magnolia 5\"/8\" double density floppy" )
	PORT_DIPSETTING(    0x0d, "? Reserved" )
	PORT_DIPSETTING(    0x0e, "? Reserved" )
	PORT_DIPSETTING(    0x0f, "? Magnolia 128K pseudo disk" )
	PORT_DIPNAME( 0x10, 0x00, "? Map ROM into RAM on boot" )         PORT_DIPLOCATION("SW501:5")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x14)
	PORT_DIPSETTING(    0x00, "? Yes" )
	PORT_DIPSETTING(    0x10, "? No" )
	PORT_DIPNAME( 0x20, 0x20, "Perform memory test at start" )       PORT_DIPLOCATION("SW501:6")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x14)
	PORT_DIPSETTING(    0x20, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x40, 0x00, "? Have a LLL controller installed" )  PORT_DIPLOCATION("SW501:7")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x14)
	PORT_DIPSETTING(    0x00, "? No" )
	PORT_DIPSETTING(    0x40, "? Yes" )
	PORT_DIPNAME( 0x80, 0x00, "Boot mode" )                          PORT_DIPLOCATION("SW501:8")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x14)
	PORT_DIPSETTING(    0x00, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x80, "Auto" )

	// Ultimeth MTRHEX-2k
	PORT_DIPNAME( 0x03, 0x00, "Default Boot Device" )                PORT_DIPLOCATION("SW501:1,2")     PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x18)
	PORT_DIPSETTING(    0x00, "H-17 hard-sectored 5\" floppy" )
	PORT_DIPSETTING(    0x01, "H-47 8\" floppy at port 0x78/0170" )
	PORT_DIPSETTING(    0x02, "H-47 8\" floppy at port 0x7c/0174" )
	PORT_DIPSETTING(    0x03, "Magnolia 5\"/8\" double density floppy" )
	PORT_DIPUNUSED_DIPLOC(0x04, 0x00, "SW501:3")                                                       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x18)
	PORT_DIPUNUSED_DIPLOC(0x08, 0x00, "SW501:4")                                                       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x18)
	PORT_DIPUNUSED_DIPLOC(0x10, 0x00, "SW501:5")                                                       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x18)
	PORT_DIPNAME( 0x20, 0x20, "Perform memory test at start" )       PORT_DIPLOCATION("SW501:6")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x18)
	PORT_DIPSETTING(    0x20, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x40, 0x00, "Boot mode" )                          PORT_DIPLOCATION("SW501:7")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x18)
	PORT_DIPSETTING(    0x00, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x40, "Auto" )
	PORT_DIPNAME( 0x80, 0x00, "Baud Rate" )                          PORT_DIPLOCATION("SW501:8")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x18)
	PORT_DIPSETTING(    0x00, "9600" )
	PORT_DIPSETTING(    0x80, "19200" )

	// SigmaSoft's SigmaROM
	PORT_DIPNAME( 0x03, 0x00, "Disk I/O #2" )                        PORT_DIPLOCATION("SW501:1,2")     PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x1c)
	PORT_DIPSETTING(    0x00, "H-88-1" )
	PORT_DIPSETTING(    0x01, "H/Z-47" )
	PORT_DIPSETTING(    0x02, "WD1002 Hard Disk" )
	PORT_DIPSETTING(    0x03, "WD1002 Floppy Disk" )
	PORT_DIPNAME( 0x0c, 0x00, "Disk I/O #1" )                        PORT_DIPLOCATION("SW501:3,4")     PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x1c)
	PORT_DIPSETTING(    0x00, "H-89-37" )
	PORT_DIPSETTING(    0x04, "H/Z-47" )
	PORT_DIPSETTING(    0x08, "WD1002 Hard Disk" )
	PORT_DIPSETTING(    0x0c, "WD1002 Floppy Disk" )
	PORT_DIPNAME( 0x10, 0x00, "Primary Boot from" )                  PORT_DIPLOCATION("SW501:5")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x1c)
	PORT_DIPSETTING(    0x00, "Disk I/O #2" )
	PORT_DIPSETTING(    0x10, "Disk I/O #1" )
	PORT_DIPNAME( 0x20, 0x20, "Reserved" )                           PORT_DIPLOCATION("SW501:6")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x1c)
	PORT_DIPSETTING(    0x20, "Must be selected" )
	PORT_DIPSETTING(    0x00, "Must not be selected" )
	PORT_DIPNAME( 0x40, 0x00, "Console Baud rate" )                  PORT_DIPLOCATION("SW501:7")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x1c)
	PORT_DIPSETTING(    0x00, "9600" )
	PORT_DIPSETTING(    0x40, "19200" )
	PORT_DIPNAME( 0x80, 0x00, "Boot mode" )                          PORT_DIPLOCATION("SW501:8")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x1c)
	PORT_DIPSETTING(    0x00, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x80, "Auto" )

	// CDR8390 ROM
	PORT_DIPNAME( 0x03, 0x00, "Disk I/O #2" )                        PORT_DIPLOCATION("SW501:1,2")     PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x20)
	PORT_DIPSETTING(    0x00, "H-88-1" )
	PORT_DIPSETTING(    0x01, "undefined" )
	PORT_DIPSETTING(    0x02, "undefined" )
	PORT_DIPSETTING(    0x03, "undefined" )
	PORT_DIPNAME( 0x0c, 0x00, "Disk I/O #1" )                        PORT_DIPLOCATION("SW501:3,4")     PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x20)
	PORT_DIPSETTING(    0x00, "undefined" )
	PORT_DIPSETTING(    0x04, "CDR FDC-880H" )
	PORT_DIPSETTING(    0x08, "undefined" )
	PORT_DIPSETTING(    0x0c, "undefined" )
	PORT_DIPNAME( 0x10, 0x00, "Primary Boot from" )                  PORT_DIPLOCATION("SW501:5")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x20)
	PORT_DIPSETTING(    0x00, "Disk I/O #2" )
	PORT_DIPSETTING(    0x10, "Disk I/O #1" )
	PORT_DIPNAME( 0x20, 0x20, "Perform memory test at start" )       PORT_DIPLOCATION("SW501:6")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x20)
	PORT_DIPSETTING(    0x20, DEF_STR( No ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x40, 0x00, "Console Baud rate" )                  PORT_DIPLOCATION("SW501:7")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x20)
	PORT_DIPSETTING(    0x00, "9600" )
	PORT_DIPSETTING(    0x40, "19200" )
	PORT_DIPNAME( 0x80, 0x00, "Boot mode" )                          PORT_DIPLOCATION("SW501:8")       PORT_CONDITION("CONFIG", 0x3c, EQUALS, 0x20)
	PORT_DIPSETTING(    0x00, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x80, "Auto" )


	PORT_START("CONFIG")
	PORT_CONFNAME(0x03, 0x00, "CPU Clock Speed Upgrade")
	PORT_CONFSETTING(   0x00, DEF_STR( None ) )
	PORT_CONFSETTING(   0x01, "2 / 4 MHz")
	PORT_CONFSETTING(   0x02, "2 / 6 MHz")
	PORT_CONFNAME(0x3c, 0x04, "Switch SW501 Definitions")
	PORT_CONFSETTING(   0x00, "Generic" )
	PORT_CONFSETTING(   0x04, "Heath MTR-90")
	PORT_CONFSETTING(   0x08, "Heath MTR-89")
	PORT_CONFSETTING(   0x0c, "MMS 444-84B/444-84A")
	PORT_CONFSETTING(   0x10, "Kres KMR-100")
	PORT_CONFSETTING(   0x14, "Ultimeth MTRHEX-4k")
	PORT_CONFSETTING(   0x18, "Ultimeth MTRHEX-2k")
	PORT_CONFSETTING(   0x1c, "SigmaROM")
	PORT_CONFSETTING(   0x20, "CDR8390 ROM")

INPUT_PORTS_END


void h89_base_state::machine_start()
{
	save_item(NAME(m_gpp));
	save_item(NAME(m_rom_enabled));
	save_item(NAME(m_timer_intr_enabled));
	save_item(NAME(m_single_step_enabled));
	save_item(NAME(m_floppy_ram_we));
	save_item(NAME(m_cpu_speed_multiplier));
	save_item(NAME(m_555a_latch));
	save_item(NAME(m_555b_latch));
	save_item(NAME(m_556b_latch));
	save_item(NAME(m_installed));

	m_installed = false;

	m_maincpu->space(AS_PROGRAM).specific(m_program);

	// update RAM mappings based on RAM size
	u8 *ram_ptr  = m_ram->pointer();
	u32 ram_size = m_ram->size();

	if (ram_size == 0x10000)
	{
		// system has a full 64k
		m_maincpu->space(AS_PROGRAM).install_ram(0x2000, 0xffff, ram_ptr);

		// install shadow writing to RAM when in ROM mode and Floppy RAM is write-protected.
		m_mem_view[0].install_writeonly(0x0000, 0x1fff, ram_ptr + 0xe000);
		// when Floppy RAM is in write enable mode, must use write_tap so writes occur to both RAMs
		// NOTE: the H89 had space reserved for additional RAM (without write protection) in the ROM space, but not aware
		// it was ever used. If that was added to this emulation, m_mem_view[0] would also need to be a write_tap.
		m_mem_view[1].install_write_tap(0x0000, 0x1fff, "shadow_w", [ram_ptr](offs_t offset, u8 &data, u8 mem_mask)
		{
			ram_ptr[0xe000 + offset] = data;
		});

		// The Org-0 (often used for CP/M) view has RAM at the lower 8k
		m_mem_view[2].install_ram(0x0000, 0x1fff, ram_ptr + 0xe000);
	}
	else
	{
		// less than 64k

		// for views with ROM visible, the top of memory is 8k higher than
		// the memory size, since the base starts at 8k.
		u32 ram_top = ram_size + 0x1fff;

		m_mem_view[0].install_ram(0x2000, ram_top, ram_ptr);
		m_mem_view[1].install_ram(0x2000, ram_top, ram_ptr);

		// when ROM is not active, memory still starts at 8k, but is 8k smaller so the last 8k can be mapped to addr 0.
		m_mem_view[2].install_ram(0x2000, ram_size - 1, ram_ptr);

		// remap the top 8k down to addr 0
		m_mem_view[2].install_ram(0x0000, 0x1fff, ram_ptr + ram_size - 0x2000);
	}

	m_floppy_ram_we       = false;
}

u8 h89_base_state::read_sw()
{
	return m_sw501->read();
}

void h89_base_state::machine_reset()
{
	if (!m_installed)
	{
		// Console/Terminal address
		h89bus::addr_ranges term_ranges = m_h89bus->get_address_ranges(h89bus::IO_TERM);
		if (term_ranges.size() == 1)
		{
			h89bus::addr_range range = term_ranges.front();

			m_h89bus->install_io_device(range.first, range.second,
				read8sm_delegate(*m_console, FUNC(ins8250_device::ins8250_r)),
				write8sm_delegate(*m_console, FUNC(ins8250_device::ins8250_w)));
		}

		h89bus::addr_ranges nmi_ranges = m_h89bus->get_address_ranges(h89bus::IO_NMI);

		// Multiple ranges cause a NMI interrupt to occur, loop through all.
		for (h89bus::addr_range range : nmi_ranges)
		{
			m_h89bus->install_io_device(range.first, range.second,
				read8smo_delegate(*this, FUNC(h89_base_state::raise_NMI_r)),
				write8smo_delegate(*this, FUNC(h89_base_state::raise_NMI_w)));
		}

		h89bus::addr_ranges gpp_ranges = m_h89bus->get_address_ranges(h89bus::IO_GPP);

		for (h89bus::addr_range range : gpp_ranges)
		{
			// check for the first single address, MMS piggy-backed on this select
			// line, the proper one for the CPU board GPP port is a single address
			if (range.first == range.second)
			{
				m_h89bus->install_io_device(range.first, range.second,
					read8smo_delegate(*this, FUNC(h89_base_state::read_sw)),
					write8smo_delegate(*this, FUNC(h89_base_state::port_f2_w)));

				break;
			}
		}

		m_installed = true;
	}

	m_rom_enabled         = true;
	m_timer_intr_enabled  = true;
	m_single_step_enabled = false;
	reset_single_step_state();

	ioport_value const cfg(m_config->read());

	// CPU clock speed
	const u8 selected_clock_upgrade = cfg & 0x3;

	switch (selected_clock_upgrade)
	{
	case 0x01:
		// 4 MHz was offered by several companies including Kres, ANAPRO, and an article
		// in REMark magazine.
		m_cpu_speed_multiplier = 2;
		break;
	case 0x02:
		// 6 MHz was offered by at least ANAPRO, and a how to article in CHUG newsletter
		m_cpu_speed_multiplier = 3;
		break;
	case 0x00:
	default:
		// No speed upgrade installed - Standard Clock
		m_cpu_speed_multiplier = 1;
		break;
	}

	update_gpp(0);
	update_mem_view();
}

void h89_base_state::set_wait_state(int data)
{
	m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, data);
	if (data)
	{
		machine().scheduler().synchronize();
		m_maincpu->defer_access();
	}
}

void h89_base_state::set_fmwe(int data)
{
	m_floppy_ram_we = bool(data);

	update_mem_view();
}

u8 h89_base_state::raise_NMI_r()
{
	m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::from_usec(2));

	return 0x00;
}

void h89_base_state::raise_NMI_w(u8)
{
	m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::from_usec(2));
}

void h89_base_state::console_intr(int data)
{
	m_intr_socket->set_irq_level(3, data);
}

template <int line> void h89_base_state::slot_irq(int state)
{
	m_intr_socket->set_irq_level(line, state);
}

template void h89_base_state::slot_irq<3>(int state);
template void h89_base_state::slot_irq<4>(int state);
template void h89_base_state::slot_irq<5>(int state);

void h89_base_state::reset_line(int data)
{
	if (bool(data))
	{
		reset();
	}
	m_maincpu->set_input_line(INPUT_LINE_RESET, data);
}

TIMER_DEVICE_CALLBACK_MEMBER(h89_base_state::h89_irq_timer)
{
	if (m_timer_intr_enabled)
	{
		m_intr_socket->set_irq_level(1, ASSERT_LINE);
	}
}

void h89_base_state::update_mem_view()
{
	m_mem_view.select(m_rom_enabled ? (m_floppy_ram_we ? 1 : 0) : 2);
}

void h89_base_state::reset_single_step_state()
{
	LOGSS("reset_single_step_state\n");
	m_555a_latch = false;
	m_555b_latch = false;
	m_556b_latch = false;
	m_intr_socket->set_irq_level(2, CLEAR_LINE);
}

// General Purpose Port
//
// Bit     OUTPUT
// ---------------------
//  0    Single-step enable
//  1    2 mSec interrupt enable
//  2    Not used (on original Heath CPU Board)
//  3    Not used (on original Heath CPU Board)
//  4    Latched bit MEM 0 H on memory expansion connector (Commonly used for Speed upgrades)
//  5    Latched bit MEM 1 H on memory expansion connector - ORG-0 (CP/M map)
//  6    Latched bit I/O 0 on I/O exp connector
//  7    Latched bit I/O 1 on I/O exp connector
//
void h89_base_state::update_gpp(u8 gpp)
{
	u8 changed_gpp = gpp ^ m_gpp;

	m_gpp = gpp;

	m_timer_intr_enabled = bool(BIT(m_gpp, GPP_ENABLE_TIMER_INTERRUPT_BIT));

	m_h89bus->set_mem0(BIT(m_gpp, GPP_MEM0_BIT));
	m_h89bus->set_mem1(BIT(m_gpp, GPP_MEM1_BIT));
	m_h89bus->set_io0(BIT(m_gpp, GPP_IO0_BIT));
	m_h89bus->set_io1(BIT(m_gpp, GPP_IO1_BIT));

	if (BIT(changed_gpp, GPP_SINGLE_STEP_BIT))
	{
		LOGSS("single step enable: %d\n", BIT(m_gpp, GPP_SINGLE_STEP_BIT));
		m_single_step_enabled = bool(BIT(m_gpp, GPP_SINGLE_STEP_BIT));

		if (!m_single_step_enabled)
		{
			reset_single_step_state();
		}
	}

	if (BIT(changed_gpp, GPP_MEM1_BIT))
	{
		m_rom_enabled = BIT(m_gpp, GPP_MEM1_BIT) == 0;

		update_mem_view();
	}

	if (BIT(changed_gpp, GPP_MEM0_BIT))
	{
		m_maincpu->set_clock(BIT(m_gpp, GPP_MEM0_BIT) ?
			H89_CLOCK * m_cpu_speed_multiplier : H89_CLOCK);
	}
}

// General Purpose Port
void h89_base_state::port_f2_w(u8 data)
{
	update_gpp(data);

	m_intr_socket->set_irq_level(1, CLEAR_LINE);
}

static void tlb_options(device_slot_interface &device)
{
	device.option_add("heath",        HEATH_TLB);
	device.option_add("gp19",         HEATH_GP19);
	device.option_add("imaginator",   HEATH_IMAGINATOR);
	device.option_add("super19",      HEATH_SUPER19);
	device.option_add("superset",     HEATH_SUPERSET);
	device.option_add("ultrarom",     HEATH_ULTRA);
	device.option_add("watzman",      HEATH_WATZ);
	device.option_add("igc",          HEATH_IGC);
	device.option_add("igc_super19",  HEATH_IGC_SUPER19);
	device.option_add("igc_ultrarom", HEATH_IGC_ULTRA);
	device.option_add("igc_watzman",  HEATH_IGC_WATZ);
}

static void intr_ctrl_options(device_slot_interface &device)
{
	device.option_add("original", HEATH_INTR_CNTRL);
	device.option_add("h37",      HEATH_Z37_INTR_CNTRL);
	device.option_add("mms",      HEATH_MMS_INTR_CNTRL);
}


void h89_base_state::h89_left_cards(device_slot_interface &device)
{
	device.option_add("ss_parallel",     H89BUS_SIGMASOFT_PARALLEL);
	device.option_add("ss_parallel_igc", H89BUS_SIGMASOFT_PARALLEL_IGC).machine_config(
		[this](device_t *device)
		{
			downcast<sigmasoft_parallel_port_igc &>(*device).set_tlbc(m_tlbc);
		});
}

void h89_base_state::h89_right_cards(device_slot_interface &device)
{
	device.option_add("cdr_fdc", H89BUS_CDR_FDC_880H);
	device.option_add("h_88_3",  H89BUS_H_88_3);
	device.option_add("ha_88_3", H89BUS_HA_88_3);
	device.option_add("h_88_5",  H89BUS_H_88_5);
	device.option_add("ss_snd",  H89BUS_SIGMASOFT_SND);
	device.option_add("z_89_11", H89BUS_Z_89_11);
	device.option_add("z37fdc",  H89BUS_Z37).machine_config(
		[this](device_t *device)
		{
			downcast<h89bus_z37_device &>(*device).set_intr_cntrl(m_intr_socket);
		});
}

void h89_base_state::h89_right_cards_mms(device_slot_interface &device)
{
	h89_right_cards(device);
	device.option_add("mms77316", H89BUS_MMS77316).machine_config(
		[this](device_t *device)
		{
			downcast<mms77316_fdc_device &>(*device).set_intr_cntrl(m_intr_socket);
		});
}

void h89_base_state::h89_right_p506_cards(device_slot_interface &device)
{
	device.option_add("h_88_3",    H89BUS_H_88_3);
	device.option_add("ha_88_3",   H89BUS_HA_88_3);
	device.option_add("ss_snd",    H89BUS_SIGMASOFT_SND);
	device.option_add("we_pullup", H89BUS_WE_PULLUP);
}

static void io_decoder_options(device_slot_interface &device)
{
	device.option_add("444_43",  H89BUS_IO_DECODER_444_43);
	device.option_add("444_61",  H89BUS_IO_DECODER_444_61);
	device.option_add("mms_61c", H89BUS_IO_DECODER_MMS_61C);
	device.option_add("cdr86",   H89BUS_IO_DECODER_CDR_86);
}

void h89_base_state::h89_base(machine_config &config)
{
	config.set_default_layout(layout_h89);

	// basic machine hardware
	Z80(config, m_maincpu, H89_CLOCK);
	m_maincpu->set_m1_map(&h89_base_state::map_fetch);
	m_maincpu->set_memory_map(&h89_base_state::h89_mem);
	m_maincpu->set_io_map(&h89_base_state::h89_base_io);
	m_maincpu->set_irq_acknowledge_callback("intr_socket", FUNC(heath_intr_socket::irq_callback));

	RAM(config, m_ram).set_default_size("64K").set_extra_options("16K,32K,48K").set_default_value(0x00);

	HEATH_INTR_SOCKET(config, m_intr_socket, intr_ctrl_options, nullptr);
	m_intr_socket->irq_line_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	INS8250(config, m_console, INS8250_CLOCK);
	m_console->out_int_callback().set(FUNC(h89_base_state::console_intr));

	HEATH_TLB_CONNECTOR(config, m_tlbc, tlb_options, "heath");

	// Connect the console port on CPU board to TLB connector
	m_console->out_tx_callback().set(m_tlbc, FUNC(heath_tlb_connector::serial_in_w));
	m_console->out_rts_callback().set(m_tlbc, FUNC(heath_tlb_connector::cts_in_w));
	m_console->out_dtr_callback().set(m_tlbc, FUNC(heath_tlb_connector::dsr_in_w));
	m_tlbc->serial_data_callback().set(m_console, FUNC(ins8250_uart_device::rx_w));
	m_tlbc->rts_callback().set(m_console, FUNC(ins8250_uart_device::cts_w));
	m_tlbc->dtr_callback().set(m_console, FUNC(ins8250_uart_device::dsr_w));

	m_tlbc->reset_cb().set(FUNC(h89_base_state::reset_line));

	H89BUS(config, m_h89bus, 0);
	m_h89bus->set_program_space(m_maincpu, AS_PROGRAM);
	m_h89bus->set_io_space(m_maincpu, AS_IO);
	m_h89bus->out_int3_callback().set(FUNC(h89_base_state::slot_irq<3>));
	m_h89bus->out_int4_callback().set(FUNC(h89_base_state::slot_irq<4>));
	m_h89bus->out_int5_callback().set(FUNC(h89_base_state::slot_irq<5>));
	m_h89bus->out_wait_callback().set(FUNC(h89_base_state::set_wait_state));
	m_h89bus->out_fmwe_callback().set(FUNC(h89_base_state::set_fmwe));
	H89BUS_LEFT_SLOT(config, "p501", "h89bus", [this](device_slot_interface &device) { h89_left_cards(device); }, nullptr);
	H89BUS_LEFT_SLOT(config, "p502", "h89bus", [this](device_slot_interface &device) { h89_left_cards(device); }, nullptr);
	H89BUS_LEFT_SLOT(config, "p503", "h89bus", [this](device_slot_interface &device) { h89_left_cards(device); }, nullptr);
	H89BUS_RIGHT_SLOT(config, "p504", "h89bus", [this](device_slot_interface &device) { h89_right_cards(device); }, nullptr);
	H89BUS_RIGHT_SLOT(config, "p505", "h89bus", [this](device_slot_interface &device) { h89_right_cards(device); }, "ha_88_3");
	H89BUS_RIGHT_SLOT(config, "p506", "h89bus", [this](device_slot_interface &device) { h89_right_p506_cards(device); }, "we_pullup").set_p506_signalling(true);

	// H89 interrupt interval is 2mSec
	TIMER(config, "irq_timer", 0).configure_periodic(FUNC(h89_base_state::h89_irq_timer), attotime::from_msec(2));
}

void h88_state::h88(machine_config &config)
{
	h89_base(config);

	m_intr_socket->set_default_option("original");
	m_intr_socket->set_fixed(true);

	H89BUS_RIGHT_SLOT(config.replace(), "p504", "h89bus", [this](device_slot_interface &device) { h89_right_cards(device); }, "h_88_5");

	LOGSETUP("%s: about to call set_io_prom_tag\n", FUNCNAME);
	H89BUS_IO_DECODER_SOCKET(config, "h89bus:io_decoder", io_decoder_options, "444_43");
}

void h89_state::h89(machine_config &config)
{
	h89_base(config);

	m_intr_socket->set_default_option("h37");
	m_intr_socket->set_fixed(true);

	H89BUS_RIGHT_SLOT(config.replace(), "p504", "h89bus", [this](device_slot_interface &device) { h89_right_cards(device); }, "z37fdc");

	LOGSETUP("%s: about to call set_io_prom_tag\n", FUNCNAME);
	H89BUS_IO_DECODER_SOCKET(config, "h89bus:io_decoder", io_decoder_options, "444_61");
}

void h89_cdr_state::h89_cdr(machine_config &config)
{
	h89_base(config);

	m_intr_socket->set_default_option("original");
	m_intr_socket->set_fixed(true);

	H89BUS_RIGHT_SLOT(config.replace(), "p504", "h89bus", [this](device_slot_interface &device) { h89_right_cards(device); }, "cdr_fdc");

	LOGSETUP("%s: about to call set_io_prom_tag\n", FUNCNAME);
	H89BUS_IO_DECODER_SOCKET(config, "h89bus:io_decoder", io_decoder_options, "cdr86");
}

void h89_mms_state::h89_mms(machine_config &config)
{
	h89_base(config);

	// the card selection is different with the MMS mapping PROM
	H89BUS_RIGHT_SLOT(config.replace(), "p504", "h89bus", [this](device_slot_interface &device) { h89_right_cards_mms(device); }, "mms77316");
	H89BUS_RIGHT_SLOT(config.replace(), "p505", "h89bus", [this](device_slot_interface &device) { h89_right_cards_mms(device); }, "ha_88_3");

	m_intr_socket->set_default_option("mms");
	m_intr_socket->set_fixed(true);

	LOGSETUP("%s: about to call set_io_prom_tag\n", FUNCNAME);
	H89BUS_IO_DECODER_SOCKET(config, "h89bus:io_decoder", io_decoder_options, "mms_61c");
}

#define ROM_H17 \
		ROM_LOAD( "2716_444-19_h17.u520",     0x1800, 0x0800, CRC(26e80ae3) SHA1(0c0ee95d7cb1a760f924769e10c0db1678f2435c))

#define ROM_MTR90_444_142(x) \
		ROM_SYSTEM_BIOS(x, "mtr90", "Zenith Data Systems MTR-90 (444-142)") \
		ROMX_LOAD("2732_444-142_mtr90.u518",  0x0000, 0x1000, CRC(c4ff47c5) SHA1(d6f3d71ff270a663003ec18a3ed1fa49f627123a), ROM_BIOS(x))

#define ROM_MTR89(x) \
		ROM_SYSTEM_BIOS(x, "mtr89", "Heath MTR-89 (444-62)") \
		ROMX_LOAD("2716_444-62_mtr89.u518",   0x0000, 0x0800, CRC(8f507972) SHA1(ac6c6c1344ee4e09fb60d53c85c9b761217fe9dc), ROM_BIOS(x))

#define ROM_MMS_444_84B(x) \
		ROM_SYSTEM_BIOS(x, "mms84b", "Magnolia MicroSystems 444-84B") \
		ROMX_LOAD("2732_444_84b_mms.u518",    0x0000, 0x1000, CRC(7e75d6f4) SHA1(baf34e036388d1a191197e31f8a93209f04fc58b), ROM_BIOS(x))

#define ROM_KMR_100(x) \
		ROM_SYSTEM_BIOS(x, "kmr-100", "Kres KMR-100 V3.a.02") \
		ROMX_LOAD("2732_kmr100_v3_a_02.u518", 0x0000, 0x1000, CRC(fd491592) SHA1(3d5803f95c38b237b07cd230353cd9ddc9858c13), ROM_BIOS(x))

#define ROM_ULTIMETH_4K(x) \
		ROM_SYSTEM_BIOS(x, "mtrhex_4k", "Ultimeth 4k ROM") \
		ROMX_LOAD("2732_mtrhex_4k.u518",      0x0000, 0x1000, CRC(e26b29a9) SHA1(ba13d6c9deef682a9a8262bc910d46b577929a13), ROM_BIOS(x))

#define ROM_MTR90_444_84(x) \
		ROM_SYSTEM_BIOS(x, "mtr90-84", "Zenith Data Systems MTR-90 (444-84 - Superseded by 444-142)") \
		ROMX_LOAD("2732_444-84_mtr90.u518",   0x0000, 0x1000, CRC(f10fca03) SHA1(c4a978153af0f2dfcc9ba05be4c1033d33fee30b), ROM_BIOS(x))

#define ROM_MMS_444_84A(x) \
		ROM_SYSTEM_BIOS(x, "mms84a", "Magnolia MicroSystems 444-84A (Superseded by MMS 444-84B)") \
		ROMX_LOAD("2732_444_84a_mms.u518",    0x0000, 0x1000, CRC(0e541a7e) SHA1(b1deb620fc89c1068e2e663e14be69d1f337a4b9), ROM_BIOS(x))

#define ROM_ULTIMETH_2K(x) \
		ROM_SYSTEM_BIOS(x, "mtrhex", "Ultimeth 2k ROM") \
		ROMX_LOAD("2716_mtrhex.u518",         0x0000, 0x0800, CRC(842a306a) SHA1(ddbc2b8bb127464af9eda8e7c56e6be7c8b43a16), ROM_BIOS(x))

#define ROM_SIGMA_V_1_3(x) \
		ROM_SYSTEM_BIOS(x, "sigmarom", "SigmaROM v1.3") \
		ROMX_LOAD("2732_sigma_rom_v_1.3.bin", 0x0000, 0x1000, CRC(c5c6b799) SHA1(f55e141a63cde8e1481480b8da9ba50569e08546), ROM_BIOS(x))

#define ROM_SIGMA_V_1_2(x) \
		ROM_SYSTEM_BIOS(x, "sigmarom_v1_2", "SigmaROM v1.2") \
		ROMX_LOAD("2732_sigma_rom_v_1.2.bin", 0x0000, 0x1000, CRC(c4ff47c5) SHA1(d6f3d71ff270a663003ec18a3ed1fa49f627123a), ROM_BIOS(x))

#define ROM_CDR_8390(x) \
		ROM_SYSTEM_BIOS(x, "cdr8390", "CDR 8390") \
		ROMX_LOAD("2732_cdr8390.u518",        0x0000, 0x1000, CRC(1d30fe43) SHA1(170092d1b62cf88edd29338b474e799c249a0dd7), ROM_BIOS(x))

// NOTE: this rom is not currently working
#define ROM_CDR_80B2(x) \
		ROM_SYSTEM_BIOS(x, "cdr80b2", "CDR 80B2") \
		ROMX_LOAD("2732_cdr80b2.u518",        0x0000, 0x1000, CRC(804a6898) SHA1(a58daca0baf7b5d7c1485531680bd63168eb2d7e), ROM_BIOS(x))


ROM_START( h88 )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )

	ROM_H17

	ROM_LOAD("2716_444-40_mtr88.u518",    0x0000, 0x0800, CRC(093afb79) SHA1(bcc1569ad9da7babf0a4199cab96d8cd59b2dd78))
ROM_END

ROM_START( h89 )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS("mtr90")

	ROM_H17

	ROM_MTR90_444_142(0)

	ROM_MTR89(1)

	ROM_MMS_444_84B(2)

	ROM_KMR_100(3)

	ROM_ULTIMETH_4K(4)

	ROM_MTR90_444_84(5)

	ROM_MMS_444_84A(6)

	ROM_ULTIMETH_2K(7)

	ROM_SIGMA_V_1_3(8)

	ROM_SIGMA_V_1_2(9)

	ROM_CDR_8390(10)

	ROM_CDR_80B2(11)
ROM_END

ROM_START( h89_cdr )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS("cdr8390")

	ROM_H17

	ROM_CDR_8390(0)

	ROM_CDR_80B2(1)

	ROM_KMR_100(2)

	ROM_ULTIMETH_4K(3)
ROM_END

ROM_START( h89_mms )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS("mms84b")

	ROM_H17

	ROM_MMS_444_84B(0)

	ROM_KMR_100(1)

	ROM_ULTIMETH_4K(2)

	ROM_MMS_444_84A(3)

	ROM_ULTIMETH_2K(4)
ROM_END

ROM_START( z90 )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS("mtr90")

	ROM_H17

	ROM_MTR90_444_142(0)

	ROM_MMS_444_84B(1)

	ROM_KMR_100(2)

	ROM_ULTIMETH_4K(3)

	ROM_MTR90_444_84(4)

	ROM_MMS_444_84A(5)

	ROM_SIGMA_V_1_3(6)

	ROM_SIGMA_V_1_2(7)

	ROM_CDR_8390(8)

	ROM_CDR_80B2(9)
ROM_END

} // anonymous namespace


//    year  name           parent compat machine        input class                init        company                fullname                   flags
COMP( 1979, h88,           h89,   0,     h88,           h88,  h88_state,           empty_init, "Heath Company",       "H-88",                    MACHINE_SUPPORTS_SAVE)
COMP( 1979, h89,           0,     0,     h89,           h89,  h89_state,           empty_init, "Heath Company",       "H-89",                    MACHINE_SUPPORTS_SAVE)
COMP( 1981, h89_cdr,       h89,   0,     h89_cdr,       h89,  h89_cdr_state,       empty_init, "Heath Company",       "H-89 with CDR Equipment", MACHINE_SUPPORTS_SAVE)
COMP( 1981, h89_mms,       h89,   0,     h89_mms,       h89,  h89_mms_state,       empty_init, "Heath Company",       "H-89 with MMS Equipment", MACHINE_SUPPORTS_SAVE)
COMP( 1981, z90,           h89,   0,     h89,           h89,  h89_state,           empty_init, "Zenith Data Systems", "Z-90",                    MACHINE_SUPPORTS_SAVE)



harriet.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Harriet (c) 1990 Quantel

***************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68010.h"
#include "machine/hd63450.h"
//#include "machine/imsc012.h"
#include "machine/mc68681.h"
#include "machine/mc68901.h"
#include "machine/nscsi_bus.h"
#include "machine/nvram.h"
#include "machine/timekpr.h"
#include "machine/wd33c9x.h"


namespace {

class harriet_state : public driver_device
{
public:
	harriet_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void harriet(machine_config &config);

private:
	uint8_t zpram_r(offs_t offset);
	void zpram_w(offs_t offset, uint8_t data);

	void harriet_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	std::unique_ptr<u8[]> m_zpram_data;
};

uint8_t harriet_state::zpram_r(offs_t offset)
{
	return m_zpram_data[offset];
}

void harriet_state::zpram_w(offs_t offset, uint8_t data)
{
	m_zpram_data[offset] = data;
}

void harriet_state::harriet_map(address_map &map)
{
	map(0x000000, 0x007fff).rom().region("monitor", 0);
	map(0x040000, 0x040fff).rw(FUNC(harriet_state::zpram_r), FUNC(harriet_state::zpram_w)).umask16(0xff00);
	map(0x040000, 0x040fff).rw("timekpr", FUNC(timekeeper_device::read), FUNC(timekeeper_device::write)).umask16(0x00ff);
	map(0x7f0000, 0x7fffff).ram();
	map(0xf10000, 0xf1001f).rw("duart", FUNC(mc68681_device::read), FUNC(mc68681_device::write)).umask16(0x00ff);
	map(0xf20000, 0xf2002f).rw("mfp", FUNC(mc68901_device::read), FUNC(mc68901_device::write)).umask16(0x00ff);
	map(0xf30000, 0xf301ff).rw("dmac", FUNC(hd63450_device::read), FUNC(hd63450_device::write));
	map(0xf40000, 0xf4003f).rw("scsia:7:wdc", FUNC(wd33c93_device::dir_r), FUNC(wd33c93_device::dir_w)).umask16(0x00ff);
	//map(0xf60000, 0xf60007).rw("c012", FUNC(imsc012_device::read), FUNC(imsc012_device::write)).umask16(0x00ff);
	map(0xf60006, 0xf60007).nopr();
	map(0xfa0000, 0xfa0001).nopr();
	map(0xfb0000, 0xfb0001).noprw();
}

static INPUT_PORTS_START( harriet )
INPUT_PORTS_END

void harriet_state::machine_start()
{
	m_zpram_data = std::make_unique<u8[]>(0x800);
	subdevice<nvram_device>("zpram")->set_base(m_zpram_data.get(), 0x800);
	save_pointer(NAME(m_zpram_data), 0x800);
}

void harriet_state::machine_reset()
{
}


static const input_device_default terminal_defaults[] =
{
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_19200 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_19200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
	{ nullptr, 0, 0 }
};

void harriet_state::harriet(machine_config &config)
{
	M68010(config, m_maincpu, 40_MHz_XTAL / 4); // MC68010FN10
	m_maincpu->set_addrmap(AS_PROGRAM, &harriet_state::harriet_map);

	MC68681(config, "duart", 3.6864_MHz_XTAL);

	mc68901_device &mfp(MC68901(config, "mfp", 40_MHz_XTAL / 16));
	mfp.set_timer_clock(2.4576_MHz_XTAL);
	mfp.out_so_cb().set("rs232", FUNC(rs232_port_device::write_txd));
	mfp.out_tco_cb().set("mfp", FUNC(mc68901_device::rc_w));
	mfp.out_tdo_cb().set("mfp", FUNC(mc68901_device::tc_w));

	HD63450(config, "dmac", 40_MHz_XTAL / 4, "maincpu"); // MC68450R10 (or HD68450Y-10)

	M48T02(config, "timekpr");
	NVRAM(config, "zpram", nvram_device::DEFAULT_ALL_0); // MK48Z02

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("mfp", FUNC(mc68901_device::si_w));
	rs232.rxd_handler().append("mfp", FUNC(mc68901_device::tbi_w));
	rs232.set_option_device_input_defaults("terminal", terminal_defaults);

	NSCSI_BUS(config, "scsia");
	NSCSI_CONNECTOR(config, "scsia:7").option_set("wdc", WD33C93A).clock(40_MHz_XTAL / 4);

	//WD33C93(config, "wdcb", 40_MHz_XTAL / 4);
	//IMSC012(config, "c012", 40_MHz_XTAL / 8); // INMOS IMSC012-P20S link adaptor
}


ROM_START( harriet )
	ROM_REGION16_BE(0x8000, "monitor", 0)
	ROM_LOAD16_BYTE("harriet 36-74c.tfb v5.01 lobyte 533f.bin", 0x0001, 0x4000, CRC(f07fff76) SHA1(8288f7eaa8f4155e0e4746635f63ca2cc3da25d1))
	ROM_LOAD16_BYTE("harriet 36-74c.tdb v5.01 hibyte 2a0c.bin", 0x0000, 0x4000, CRC(a61f441d) SHA1(76af6eddd5c042f1b2eef590eb822379944b9b28))
ROM_END

} // anonymous namespace


COMP( 1990, harriet, 0, 0, harriet, harriet, harriet_state, empty_init, "Quantel", "Harriet", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



hawk.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/***************************************************************************

    Husky Hawk

    No schematics or manuals available.

    Known RAM configurations:
    Hawk - 352K

****************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/z180/z180.h"
#include "machine/msm6242.h"
#include "machine/ram.h"
#include "machine/nvram.h"
#include "sound/spkrdev.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class hawk_state : public driver_device
{
public:
	hawk_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_palette(*this, "palette")
		, m_speaker(*this, "speaker")
		, m_rs232(*this, "serial")
		, m_ram(*this, RAM_TAG)
		, m_nvram(*this, "nvram")
	{ }

	void hawk(machine_config &config);

	void init_hawk();

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	void hawk_palette(palette_device &palette) const;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void hawk_mem(address_map &map) ATTR_COLD;
	void hawk_io(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<palette_device> m_palette;
	required_device<speaker_sound_device> m_speaker;
	required_device<rs232_port_device> m_rs232;
	required_device<ram_device> m_ram;
	required_device<nvram_device> m_nvram;
};

void hawk_state::hawk_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0010, 0x0010).mirror(0xff00).nopw();
	map(0x0018, 0x0018).nopw();
	map(0x003f, 0x003f).nopw();
	map(0x0080, 0x00bf).noprw(); // Z180 internal registers
	map(0x00c0, 0x00cf).mirror(0xff00).rw("rtc", FUNC(msm6242_device::read), FUNC(msm6242_device::write));
}

void hawk_state::hawk_mem(address_map &map)
{
	map(0x00000, 0x1ffff).rom();
	map(0x20000, 0x7ffff).ram().share(RAM_TAG);
}

static INPUT_PORTS_START( hawk )
INPUT_PORTS_END

void hawk_state::machine_reset()
{
}

void hawk_state::init_hawk()
{
	m_nvram->set_base(m_ram->pointer(), m_ram->size());
}

uint32_t hawk_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	// video RAM at 78000 ??
	return 0;
}

void hawk_state::hawk_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}


void hawk_state::hawk(machine_config &config)
{
	/* basic machine hardware */
	Z80180(config, m_maincpu, 12.288_MHz_XTAL); /* HD64B180R0F */
	m_maincpu->set_addrmap(AS_PROGRAM, &hawk_state::hawk_mem);
	m_maincpu->set_addrmap(AS_IO, &hawk_state::hawk_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_screen_update(FUNC(hawk_state::screen_update));
	screen.set_size(240, 64);
	screen.set_visarea(0, 239, 0, 63);
	PALETTE(config, m_palette, FUNC(hawk_state::hawk_palette), 2);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* rtc */
	MSM6242(config, "rtc", 32.768_kHz_XTAL); // M6242B

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);

	/* internal ram */
	RAM(config, m_ram).set_default_size("352K");
	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);
}


ROM_START(hawk)
	ROM_REGION(0x20000, "maincpu", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "120", "DEMOS 2.21 V1.20")
	ROMX_LOAD("hawk-v1.20.rom",   0x00000, 0x20000, CRC(186b1974) SHA1(eb46041715266cc263d71aad4f09cdf0c913cd16), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "110", "DEMOS 2.21 V1.10")
	ROMX_LOAD("hawk-v1.10-0.rom", 0x00000, 0x08000, CRC(c933ed5c) SHA1(f7b8375099ade54e3573467bf8e7f62624958e4c), ROM_BIOS(1))
	ROMX_LOAD("hawk-v1.10-1.rom", 0x08000, 0x08000, CRC(121b5ce0) SHA1(baf06bc0d16501b50cbf97b686612b00098c73ab), ROM_BIOS(1))
	ROMX_LOAD("hawk-v1.10-2.rom", 0x10000, 0x08000, CRC(cd5d94c7) SHA1(44c996b4bf00185ccb303cf9ef9bbd705018390c), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "110a", "DEMOS 2.21 V1.10 (alt)")
	ROMX_LOAD("v1.10_0.rom",      0x00000, 0x08000, CRC(7ad48dcf) SHA1(0e2b82dec0a082f4a8032b7a64cc9afdbf421521), ROM_BIOS(2))
	ROMX_LOAD("v1.10_1.rom",      0x08000, 0x08000, CRC(121b5ce0) SHA1(baf06bc0d16501b50cbf97b686612b00098c73ab), ROM_BIOS(2))
	ROMX_LOAD("v1.10_2.rom",      0x10000, 0x08000, CRC(cd5d94c7) SHA1(44c996b4bf00185ccb303cf9ef9bbd705018390c), ROM_BIOS(2))
	ROMX_LOAD("56189.rom",        0x18000, 0x08000, CRC(1b2db82b) SHA1(2185d27816bde263c62db1a2441d8a6d2cb6d193), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "101", "DEMOS 2.21 V1.01")
	ROMX_LOAD("hawk-v1.01-0.rom", 0x00000, 0x08000, CRC(96404435) SHA1(3b108d6906ecc7d7a36c36b993e79ec7480668fa), ROM_BIOS(3))
	ROMX_LOAD("hawk-v1.01-1.rom", 0x08000, 0x08000, CRC(4f99c76c) SHA1(45ff638277cff7b1fb2e21c4c348dad2b2e779b7), ROM_BIOS(3))
	ROMX_LOAD("hawk-v1.01-2.rom", 0x10000, 0x08000, CRC(982ed053) SHA1(ab0a860f1204f36f490fdfadfefe2ee4a82ed3be), ROM_BIOS(3))
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT  CLASS       INIT       COMPANY                FULLNAME      FLAGS
COMP( 1987, hawk,    0,       0,      hawk,    hawk,  hawk_state, init_hawk, "Husky Computers Ltd", "Husky Hawk", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



hazeltin.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/***************************************************************************

    Hazeltine 1500
    original machine (c) 1977 Hazeltine Corporation

    preliminary driver by Ryan Holtz

TODO (roughly in order of importance):
    - Figure out the correct keyboard decoding.
    - Proper RS232 hookup.
    - Iron out proper horizontal and vertical start/end values.
    - Hook up /FGBIT, REV FLD FRAME, and REV VIDEO SELECT lines on video
      board.
    - Reimplement logic probe (since removed) as a netlist device so other
      devs can use it.
    - Implement /BRESET line in netlist to possibly smooth out some sync
      issues.

References:
    [1]: Hazeltine_1500_Series_Maintenance_Manual_Dec77.pdf, on Bitsavers

****************************************************************************/

#include "emu.h"

#include "cpu/i8085/i8085.h"
#include "machine/ay31015.h"
#include "machine/clock.h"
#include "machine/com8116.h"
#include "machine/input_merger.h"
#include "machine/kb3600.h"
#include "machine/netlist.h"
#include "nl_hazelvid.h"
#include "netlist/devices/net_lib.h"

#include "emupal.h"
#include "screen.h"


namespace {

#define CPU_TAG         "maincpu"
#define NETLIST_TAG     "videobrd"
#define UART_TAG        "uart"
#define BAUDGEN_TAG     "baudgen"
#define KBDC_TAG        "ay53600"
#define CHARROM_TAG     "chargen"
#define BAUDPORT_TAG    "baud"
#define MISCPORT_TAG    "misc"
#define MISCKEYS_TAG    "misc_keys"
#define SCREEN_TAG      "screen"
#define BAUD_PROM_TAG   "u39"
//#define NL_PROM_TAG     "videobrd:u71"
//#define NL_EPROM_TAG    "videobrd:u78"
// VIDEO_PROM at u71
#define VIDEO_PROM_TAG  NETLIST_TAG ":u90_702128_82s129.bin"
// CHAR_EPROM at u78
#define CHAR_EPROM_TAG  NETLIST_TAG ":u83_chr.bin"
#define VIDEO_OUT_TAG   "videobrd:video_out"
#define VBLANK_OUT_TAG  "videobrd:vblank"
#define TVINTERQ_OUT_TAG "videobrd:tvinterq"

#define VIDEO_CLOCK     (XTAL(33'264'000)/2)
#define VIDEOBRD_CLOCK  (XTAL(33'264'000)*30)

#define SR2_FULL_DUPLEX (0x01)
#define SR2_UPPER_ONLY  (0x08)

#define SR3_PB_RESET    (0x04)

#define KBD_STATUS_KBDR     (0x01)
#define KBD_STATUS_TV_UB    (0x40)
#define KBD_STATUS_TV_INT   (0x80)

#define SCREEN_HTOTAL   (9*100)
#define SCREEN_HDISP    (9*80)
#define SCREEN_HSTART   (9*5)

#define SCREEN_VTOTAL   (28*11)
#define SCREEN_VDISP    (24*11)
#define SCREEN_VSTART   (3*11)

class hazl1500_state : public driver_device
{
public:
	hazl1500_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, CPU_TAG)
		, m_video_board(*this, NETLIST_TAG)
		, m_u9(*this, "videobrd:u9")
		, m_u10(*this, "videobrd:u10")
		, m_u11(*this, "videobrd:u11")
		, m_u12(*this, "videobrd:u12")
		, m_u13(*this, "videobrd:u13")
		, m_u14(*this, "videobrd:u14")
		, m_u15(*this, "videobrd:u15")
		, m_u16(*this, "videobrd:u16")
		, m_u22(*this, "videobrd:u22")
		, m_u23(*this, "videobrd:u23")
		, m_u24(*this, "videobrd:u24")
		, m_u25(*this, "videobrd:u25")
		, m_u26(*this, "videobrd:u26")
		, m_u27(*this, "videobrd:u27")
		, m_u28(*this, "videobrd:u28")
		, m_u29(*this, "videobrd:u29")
		, m_cpu_db0(*this, "videobrd:cpu_db0")
		, m_cpu_db1(*this, "videobrd:cpu_db1")
		, m_cpu_db2(*this, "videobrd:cpu_db2")
		, m_cpu_db3(*this, "videobrd:cpu_db3")
		, m_cpu_db4(*this, "videobrd:cpu_db4")
		, m_cpu_db5(*this, "videobrd:cpu_db5")
		, m_cpu_db6(*this, "videobrd:cpu_db6")
		, m_cpu_db7(*this, "videobrd:cpu_db7")
		, m_cpu_ba4(*this, "videobrd:cpu_ba4")
		, m_cpu_iowq(*this, "videobrd:cpu_iowq")
		, m_video_out(*this, VIDEO_OUT_TAG)
		, m_vblank_out(*this, VBLANK_OUT_TAG)
		, m_tvinterq_out(*this, TVINTERQ_OUT_TAG)
		, m_uart(*this, UART_TAG)
		, m_kbdc(*this, KBDC_TAG)
		, m_baud_dips(*this, BAUDPORT_TAG)
		, m_baud_prom(*this, BAUD_PROM_TAG)
		, m_misc_dips(*this, MISCPORT_TAG)
		, m_kbd_misc_keys(*this, MISCKEYS_TAG)
		, m_screen(*this, SCREEN_TAG)
		, m_mainint(*this, "mainint")
		, m_iowq_timer(nullptr)
		, m_status_reg_3(0)
		, m_kbd_status_latch(0)
		, m_refresh_address(0)
		, m_screen_buf(nullptr)
		, m_last_beam(0.0)
		, m_last_hpos(0)
		, m_last_vpos(0)
		, m_last_fraction(0.0)
	{
	}

	void hazl1500(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint32_t screen_update_hazl1500(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	uint8_t ram_r(offs_t offset);
	void ram_w(offs_t offset, uint8_t data);

	uint8_t system_test_r(); // noted as "for use with auto test equip" in flowchart on pg. 30, ref[1], jumps to 0x8000 if bit 0 is unset
	uint8_t status_reg_2_r();
	void status_reg_3_w(uint8_t data);

	uint8_t uart_r();
	void uart_w(uint8_t data);

	uint8_t kbd_status_latch_r();
	uint8_t kbd_encoder_r();
	int ay3600_shift_r();
	int ay3600_control_r();
	void ay3600_data_ready_w(int state);

	void refresh_address_w(uint8_t data);

	NETDEV_ANALOG_CALLBACK_MEMBER(video_out_cb);
	NETDEV_ANALOG_CALLBACK_MEMBER(vblank_cb);
	NETDEV_ANALOG_CALLBACK_MEMBER(tvinterq_cb);

	void hazl1500_io(address_map &map) ATTR_COLD;
	void hazl1500_mem(address_map &map) ATTR_COLD;

	TIMER_CALLBACK_MEMBER(update_iowq);

	required_device<cpu_device> m_maincpu;
	required_device<netlist_mame_device> m_video_board;
	required_device<netlist_mame_ram_pointer_device> m_u9;
	required_device<netlist_mame_ram_pointer_device> m_u10;
	required_device<netlist_mame_ram_pointer_device> m_u11;
	required_device<netlist_mame_ram_pointer_device> m_u12;
	required_device<netlist_mame_ram_pointer_device> m_u13;
	required_device<netlist_mame_ram_pointer_device> m_u14;
	required_device<netlist_mame_ram_pointer_device> m_u15;
	required_device<netlist_mame_ram_pointer_device> m_u16;
	required_device<netlist_mame_ram_pointer_device> m_u22;
	required_device<netlist_mame_ram_pointer_device> m_u23;
	required_device<netlist_mame_ram_pointer_device> m_u24;
	required_device<netlist_mame_ram_pointer_device> m_u25;
	required_device<netlist_mame_ram_pointer_device> m_u26;
	required_device<netlist_mame_ram_pointer_device> m_u27;
	required_device<netlist_mame_ram_pointer_device> m_u28;
	required_device<netlist_mame_ram_pointer_device> m_u29;
	required_device<netlist_mame_logic_input_device> m_cpu_db0;
	required_device<netlist_mame_logic_input_device> m_cpu_db1;
	required_device<netlist_mame_logic_input_device> m_cpu_db2;
	required_device<netlist_mame_logic_input_device> m_cpu_db3;
	required_device<netlist_mame_logic_input_device> m_cpu_db4;
	required_device<netlist_mame_logic_input_device> m_cpu_db5;
	required_device<netlist_mame_logic_input_device> m_cpu_db6;
	required_device<netlist_mame_logic_input_device> m_cpu_db7;
	required_device<netlist_mame_logic_input_device> m_cpu_ba4;
	required_device<netlist_mame_logic_input_device> m_cpu_iowq;
	required_device<netlist_mame_analog_output_device> m_video_out;
	required_device<netlist_mame_analog_output_device> m_vblank_out;
	required_device<netlist_mame_analog_output_device> m_tvinterq_out;
	required_device<ay31015_device> m_uart;
	required_device<ay3600_device> m_kbdc;
	required_ioport m_baud_dips;
	required_region_ptr<uint8_t> m_baud_prom;
	required_ioport m_misc_dips;
	required_ioport m_kbd_misc_keys;

	required_device<screen_device> m_screen;

	required_device<input_merger_device> m_mainint;

	emu_timer* m_iowq_timer;

	uint8_t m_status_reg_3;
	uint8_t m_kbd_status_latch;

	uint8_t m_refresh_address;

	std::unique_ptr<float[]> m_screen_buf;

	double m_last_beam;
	int m_last_hpos;
	int m_last_vpos;
	double m_last_fraction;
};

void hazl1500_state::machine_start()
{
	m_screen_buf = std::make_unique<float[]>(SCREEN_HTOTAL * SCREEN_VTOTAL);

	m_iowq_timer = timer_alloc(FUNC(hazl1500_state::update_iowq), this);
	m_iowq_timer->adjust(attotime::never);

	m_uart->write_swe(0);

	save_item(NAME(m_status_reg_3));
	save_item(NAME(m_kbd_status_latch));
	save_item(NAME(m_refresh_address));
	save_item(NAME(m_last_beam));
	save_item(NAME(m_last_hpos));
	save_item(NAME(m_last_vpos));
	save_item(NAME(m_last_fraction));
}

void hazl1500_state::machine_reset()
{
	m_status_reg_3 = 0;
	m_kbd_status_latch = 0;
}

TIMER_CALLBACK_MEMBER(hazl1500_state::update_iowq)
{
	m_cpu_iowq->write(1);
	m_cpu_ba4->write(1);
}

uint32_t hazl1500_state::screen_update_hazl1500(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	int last_index = m_last_vpos * SCREEN_HTOTAL + m_last_hpos;
	while (last_index < SCREEN_HTOTAL * SCREEN_VTOTAL)
	{
		m_screen_buf[last_index++] = m_last_beam;
	}
	m_last_hpos = 0;
	m_last_vpos = 0;

	for (int y = 0; y < SCREEN_VTOTAL; y++)
	{
		uint32_t *scanline = &bitmap.pix(y);
		uint32_t pixindex = y * SCREEN_HTOTAL;
		for (int x = 0; x < SCREEN_HTOTAL; x++)
			//*scanline++ = 0xff000000 | (uint8_t(m_screen_buf[pixindex++] * 0.5) * 0x010101);
			*scanline++ = 0xff000000 | (uint8_t(m_screen_buf[pixindex++] * 63.0) * 0x010101);
	}

	return 0;
}

uint8_t hazl1500_state::ram_r(offs_t offset)
{
	const uint8_t* chips[2][8] =
	{
		{ m_u29->ptr(), m_u28->ptr(), m_u27->ptr(), m_u26->ptr(), m_u25->ptr(), m_u24->ptr(), m_u23->ptr(), m_u22->ptr() },
		{ m_u16->ptr(), m_u15->ptr(), m_u14->ptr(), m_u13->ptr(), m_u12->ptr(), m_u11->ptr(), m_u10->ptr(), m_u9->ptr() }
	};

	int bank = ((offset & 0x400) != 0 ? 1 : 0);
	const int byte_pos = (offset >> 3) & 0x7f;
	const int bit_pos = offset & 7;

	uint8_t ret = 0;
	for (std::size_t bit = 0; bit < 8; bit++)
		ret |= ((chips[bank][bit][byte_pos] >> bit_pos) & 1) << bit;

	return ret;
}

void hazl1500_state::ram_w(offs_t offset, uint8_t data)
{
	uint8_t* chips[2][8] =
	{
		{ m_u29->ptr(), m_u28->ptr(), m_u27->ptr(), m_u26->ptr(), m_u25->ptr(), m_u24->ptr(), m_u23->ptr(), m_u22->ptr() },
		{ m_u16->ptr(), m_u15->ptr(), m_u14->ptr(), m_u13->ptr(), m_u12->ptr(), m_u11->ptr(), m_u10->ptr(), m_u9->ptr() }
	};

	int bank = ((offset & 0x400) != 0 ? 1 : 0);
	const int byte_pos = (offset >> 3) & 0x7f;
	const int bit_pos = offset & 7;

	for (std::size_t bit = 0; bit < 8; bit++)
	{
		chips[bank][bit][byte_pos] &= ~(1 << bit_pos);
		chips[bank][bit][byte_pos] |= ((data >> bit) & 1) << bit_pos;
	}
}

uint8_t hazl1500_state::system_test_r()
{
	return 0xff;
}

uint8_t hazl1500_state::status_reg_2_r()
{
	uint8_t misc_dips = m_misc_dips->read();
	uint8_t status = 0;

	if (misc_dips & 0x10)
		status |= SR2_FULL_DUPLEX;
	if (misc_dips & 0x40)
		status |= SR2_UPPER_ONLY;

	return status ^ 0xff;
}

void hazl1500_state::status_reg_3_w(uint8_t data)
{
	m_status_reg_3 = data;
	m_uart->write_rdav(BIT(data, 2));
}

uint8_t hazl1500_state::uart_r()
{
	return (m_uart->receive() & 0x7f) | (m_uart->pe_r() << 7);
}

void hazl1500_state::uart_w(uint8_t data)
{
	m_uart->transmit((data & 0x7f) | (BIT(m_misc_dips->read(), 3) ? 0x00 : 0x80));
}

uint8_t hazl1500_state::kbd_status_latch_r()
{
	return m_kbd_status_latch;
}

uint8_t hazl1500_state::kbd_encoder_r()
{
	return m_kbdc->b_r() & 0xff; // TODO: This should go through an 8048, but we have no dump of it currently.
}

int hazl1500_state::ay3600_shift_r()
{
	// either shift key
	if (m_kbd_misc_keys->read() & 0x06)
	{
		return 1;
	}

	return 0;
}

int hazl1500_state::ay3600_control_r()
{
	if (m_kbd_misc_keys->read() & 0x08)
	{
		return 1;
	}

	return 0;
}

void hazl1500_state::ay3600_data_ready_w(int state)
{
	if (state)
		m_kbd_status_latch |= KBD_STATUS_KBDR;
	else
		m_kbd_status_latch &= ~KBD_STATUS_KBDR;
}

NETDEV_ANALOG_CALLBACK_MEMBER(hazl1500_state::vblank_cb)
{
	if (int(data) > 1)
	{
		m_kbd_status_latch &= ~KBD_STATUS_TV_UB;
	}
	else
	{
		m_kbd_status_latch |= KBD_STATUS_TV_UB;
	}
}

NETDEV_ANALOG_CALLBACK_MEMBER(hazl1500_state::tvinterq_cb)
{
	if (int(data) > 1)
	{
		m_kbd_status_latch &= ~KBD_STATUS_TV_INT;
		m_mainint->in_w<1>(0);
	}
	else
	{
		m_kbd_status_latch |= KBD_STATUS_TV_INT;
		m_mainint->in_w<1>(1);
	}
}

NETDEV_ANALOG_CALLBACK_MEMBER(hazl1500_state::video_out_cb)
{
	attotime second_fraction(0, time.attoseconds());
	attotime frame_fraction(0, (second_fraction * 60).attoseconds());
	attotime pixel_time = frame_fraction * (SCREEN_HTOTAL * SCREEN_VTOTAL);
	int32_t pixel_index = (frame_fraction * (SCREEN_HTOTAL * SCREEN_VTOTAL)).seconds();
	double pixel_fraction = ATTOSECONDS_TO_DOUBLE(pixel_time.attoseconds());

	pixel_index -= 16; // take back 16 clock cycles to honor the circuitry god whose ark this is
	if (pixel_index < 0)
	{
		m_last_beam = float(data);
		m_last_hpos = 0;
		m_last_vpos = 0;
		m_last_fraction = 0.0;
		return;
	}

	const int hpos = pixel_index % SCREEN_HTOTAL;//m_screen->hpos();
	const int vpos = pixel_index / SCREEN_HTOTAL;//m_screen->vpos();
	const int curr_index = vpos * SCREEN_HTOTAL + hpos;

	int last_index = m_last_vpos * SCREEN_HTOTAL + m_last_hpos;
	if (last_index != curr_index)
	{
		m_screen_buf[last_index] *= m_last_fraction;
		m_screen_buf[last_index] += float(m_last_beam * (1.0 - m_last_fraction));
		last_index++;
		while (last_index <= curr_index)
			m_screen_buf[last_index++] = float(m_last_beam);
	}

	m_last_beam = float(data);
	m_last_hpos = hpos;
	m_last_vpos = vpos;
	m_last_fraction = pixel_fraction;
}

void hazl1500_state::refresh_address_w(uint8_t data)
{
	//printf("refresh: %02x, %d, %d\n", data, m_screen->hpos(), m_screen->vpos());
	m_iowq_timer->adjust(attotime::from_hz(XTAL(18'000'000)/9));
	m_cpu_iowq->write(0);
	m_cpu_ba4->write(0);
	m_cpu_db0->write((data >> 0) & 1);
	m_cpu_db1->write((data >> 1) & 1);
	m_cpu_db2->write((data >> 2) & 1);
	m_cpu_db3->write((data >> 3) & 1);
	m_cpu_db4->write((data >> 4) & 1);
	m_cpu_db5->write((data >> 5) & 1);
	m_cpu_db6->write((data >> 6) & 1);
	m_cpu_db7->write((data >> 7) & 1);
}

void hazl1500_state::hazl1500_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).rom().region(CPU_TAG, 0);
	map(0x3000, 0x377f).rw(FUNC(hazl1500_state::ram_r), FUNC(hazl1500_state::ram_w));
	map(0x3780, 0x37ff).ram();
}

void hazl1500_state::hazl1500_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x7f, 0x7f).rw(FUNC(hazl1500_state::status_reg_2_r), FUNC(hazl1500_state::status_reg_3_w));
	map(0xbf, 0xbf).rw(FUNC(hazl1500_state::uart_r), FUNC(hazl1500_state::uart_w));
	map(0xdf, 0xdf).r(FUNC(hazl1500_state::kbd_encoder_r));
	map(0xef, 0xef).rw(FUNC(hazl1500_state::system_test_r), FUNC(hazl1500_state::refresh_address_w));
	map(0xf7, 0xf7).r(FUNC(hazl1500_state::kbd_status_latch_r));
}

	/*
	  Hazeltine 1500 key matrix (from ref[1])

	      | Y0  | Y1  | Y2  | Y3  | Y4  | Y5  | Y6  | Y7  | Y8  | Y9  |
	      |     |     |     |     |     |     |     |     |     |     |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X1  | TAB |  1  |  3  |  5  |  7  |BREAK| ^~  |     |     |     |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X2  |BAKSP|  2  |  4  |  6  |  8  |  9  | -=  |     |     |     |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X3  |     | ESC |  W  |  R  |  Y  |  0  | `@  | CLR |NP , |NP 9 |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X4  |     |  Q  |  E  |  T  |  U  |  O  | {[  | |\  |NP 7 |NP 6 |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X5  |HOME |  A  |  D  |  G  |  I  |  P  | *:  | LF  |NP 8 |NP 5 |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X6  |     |  S  |  F  |  H  |  J  | +;  | }]  | DEL |NP 4 |NP 2 |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X7  |     |  Z  |  C  |  B  |  K  |  L  | <,  | CR  |NP 1 |NP 3 |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	  X8  |     |  X  |  V  |  N  |SPACE|  M  | >.  | ?/  |NP 0 |NP . |
	  ----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
	*/

static INPUT_PORTS_START( hazl1500 )
	PORT_START("X0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	// X1  | TAB |  1  |  3  |  5  |  7  |BREAK| ^~  |     |     |     |
	PORT_START("X1")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t') PORT_NAME("Tab")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)  PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)  PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)  PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)  PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PAUSE) PORT_NAME("Break")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	// X2  |BAKSP|  2  |  4  |  6  |  8  |  9  | -=  |     |     |     |
	PORT_START("X2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)  PORT_NAME("Backspace")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	// X3  |     | ESC |  W  |  R  |  Y  |  0  | `@  | CLR |NP , |NP 9 |
	PORT_START("X3")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)        PORT_NAME("Esc")
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)          PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)          PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_CHAR('0')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)        PORT_NAME("Clr")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)      PORT_CHAR(UCHAR_MAMEKEY(9_PAD))

	// X4  |     |  Q  |  E  |  T  |  U  |  O  | {[  | |\  |NP 7 |NP 6 |
	PORT_START("X4")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)          PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)          PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)          PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)          PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)          PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)      PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)      PORT_CHAR(UCHAR_MAMEKEY(6_PAD))

	// X5  |HOME |  A  |  D  |  G  |  I  |  P  | *:  | LF  |NP 8 |NP 5 |
	PORT_START("X5")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME)   PORT_NAME("Home")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)      PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)      PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)      PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)      PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)      PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)  PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN)   PORT_NAME("Line Feed")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)  PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)  PORT_CHAR(UCHAR_MAMEKEY(5_PAD))

	// X6  |     |  S  |  F  |  H  |  J  | +;  | }]  | DEL |NP 4 |NP 2 |
	PORT_START("X6")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)      PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)      PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)      PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)      PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)  PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)    PORT_NAME("Del")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)  PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)  PORT_CHAR(UCHAR_MAMEKEY(2_PAD))

	// X7  |     |  Z  |  C  |  B  |  K  |  L  | <,  | CR  |NP 1 |NP 3 |
	PORT_START("X7")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)      PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)      PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)      PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)      PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)      PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)  PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)  PORT_CHAR(13)  PORT_NAME("Return")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)  PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)  PORT_CHAR(UCHAR_MAMEKEY(3_PAD))

	// X8  |     |  X  |  V  |  N  |SPACE|  M  | >.  | ?/  |NP 0 |NP . |
	PORT_START("X8")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)      PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)      PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)      PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)  PORT_CHAR(' ')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)      PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)   PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)  PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)  PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))

	PORT_START(MISCKEYS_TAG)
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Shift")   PORT_CODE(KEYCODE_LSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Shift")  PORT_CODE(KEYCODE_RSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Control")      PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START(BAUDPORT_TAG)
	PORT_DIPNAME( 0xff, 0x08, "Baud Rate" )
	PORT_DIPSETTING( 0x01, "110" )
	PORT_DIPSETTING( 0x02, "300" )
	PORT_DIPSETTING( 0x04, "1200" )
	PORT_DIPSETTING( 0x08, "1800" )
	PORT_DIPSETTING( 0x10, "2400" )
	PORT_DIPSETTING( 0x20, "4800" )
	PORT_DIPSETTING( 0x40, "9600" )
	PORT_DIPSETTING( 0x80, "19.2K" )

	PORT_START(MISCPORT_TAG)
	PORT_DIPNAME( 0x0f, 0x01, "Parity" )
	PORT_DIPSETTING( 0x01, "Even" )
	PORT_DIPSETTING( 0x02, "Odd" )
	PORT_DIPSETTING( 0x04, "1" )
	PORT_DIPSETTING( 0x08, "0" )
	PORT_DIPNAME( 0x10, 0x10, "Duplex" )
	PORT_DIPSETTING( 0x00, "Half" )
	PORT_DIPSETTING( 0x10, "Full" )
	PORT_DIPNAME( 0x20, 0x20, "Auto" )
	PORT_DIPSETTING( 0x00, "LF" )
	PORT_DIPSETTING( 0x20, "CR" )
	PORT_DIPNAME( 0x40, 0x40, "Case" )
	PORT_DIPSETTING( 0x00, "Upper and Lower" )
	PORT_DIPSETTING( 0x40, "Upper" )
	PORT_DIPNAME( 0x80, 0x80, "Video" )
	PORT_DIPSETTING( 0x00, "Standard" )
	PORT_DIPSETTING( 0x80, "Reverse" )
INPUT_PORTS_END

/* F4 Character Displayer */
static const gfx_layout hazl1500_charlayout =
{
	8, 16,
	128,
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16
};

static GFXDECODE_START( gfx_hazl1500 )
	GFXDECODE_ENTRY( CHAR_EPROM_TAG, 0x0000, hazl1500_charlayout, 0, 1 )
GFXDECODE_END

void hazl1500_state::hazl1500(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, XTAL(18'000'000)/9); // 18MHz crystal on schematics, using an i8224 clock gen/driver IC
	m_maincpu->set_addrmap(AS_PROGRAM, &hazl1500_state::hazl1500_mem);
	m_maincpu->set_addrmap(AS_IO, &hazl1500_state::hazl1500_io);
	config.set_perfect_quantum(m_maincpu);

	INPUT_MERGER_ANY_HIGH(config, "mainint").output_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_screen_update(FUNC(hazl1500_state::screen_update_hazl1500));
	//m_screen->set_raw(XTAL(33'264'000) / 2,
	//    SCREEN_HTOTAL, SCREEN_HSTART, SCREEN_HSTART + SCREEN_HDISP,
	//    SCREEN_VTOTAL, SCREEN_VSTART, SCREEN_VSTART + SCREEN_VDISP); // TODO: Figure out exact visibility
	m_screen->set_raw(XTAL(33'264'000) / 2,
		SCREEN_HTOTAL, 0, SCREEN_HTOTAL,
		SCREEN_VTOTAL, 0, SCREEN_VTOTAL);

	PALETTE(config, "palette", palette_device::MONOCHROME);
	GFXDECODE(config, "gfxdecode", "palette", gfx_hazl1500);

	com8116_device &baudgen(COM8116(config, BAUDGEN_TAG, XTAL(5'068'800)));
	baudgen.fr_handler().set(m_uart, FUNC(ay51013_device::write_tcp));
	baudgen.fr_handler().append(m_uart, FUNC(ay51013_device::write_rcp));

	AY51013(config, m_uart);
	m_uart->write_dav_callback().set("mainint", FUNC(input_merger_device::in_w<0>));

	NETLIST_CPU(config, NETLIST_TAG, VIDEOBRD_CLOCK).set_source(NETLIST_NAME(hazelvid));

	// First 1K
	NETLIST_RAM_POINTER(config, NETLIST_TAG ":u22", "u22.m_RAM");
	NETLIST_RAM_POINTER(config, NETLIST_TAG ":u23", "u23.m_RAM");
	NETLIST_RAM_POINTER(config, NETLIST_TAG ":u24", "u24.m_RAM");
	NETLIST_RAM_POINTER(config, NETLIST_TAG ":u25", "u25.m_RAM");
	NETLIST_RAM_POINTER(config, NETLIST_TAG ":u26", "u26.m_RAM");
	NETLIST_RAM_POINTER(config, NETLIST_TAG ":u27", "u27.m_RAM");
	NETLIST_RAM_POINTER(config, NETLIST_TAG ":u28", "u28.m_RAM");
	NETLIST_RAM_POINTER(config, NETLIST_TAG ":u29", "u29.m_RAM");

	// Second 1K
	NETLIST_RAM_POINTER(config, NETLIST_TAG ":u9",  "u9.m_RAM");
	NETLIST_RAM_POINTER(config, NETLIST_TAG ":u10", "u10.m_RAM");
	NETLIST_RAM_POINTER(config, NETLIST_TAG ":u11", "u11.m_RAM");
	NETLIST_RAM_POINTER(config, NETLIST_TAG ":u12", "u12.m_RAM");
	NETLIST_RAM_POINTER(config, NETLIST_TAG ":u13", "u13.m_RAM");
	NETLIST_RAM_POINTER(config, NETLIST_TAG ":u14", "u14.m_RAM");
	NETLIST_RAM_POINTER(config, NETLIST_TAG ":u15", "u15.m_RAM");
	NETLIST_RAM_POINTER(config, NETLIST_TAG ":u16", "u16.m_RAM");

	NETLIST_LOGIC_INPUT(config, NETLIST_TAG ":cpu_iowq", "cpu_iowq.IN", 0);
	NETLIST_LOGIC_INPUT(config, NETLIST_TAG ":cpu_ba4", "cpu_ba4.IN", 0);
	NETLIST_LOGIC_INPUT(config, NETLIST_TAG ":cpu_db0", "cpu_db0.IN", 0);
	NETLIST_LOGIC_INPUT(config, NETLIST_TAG ":cpu_db1", "cpu_db1.IN", 0);
	NETLIST_LOGIC_INPUT(config, NETLIST_TAG ":cpu_db2", "cpu_db2.IN", 0);
	NETLIST_LOGIC_INPUT(config, NETLIST_TAG ":cpu_db3", "cpu_db3.IN", 0);
	NETLIST_LOGIC_INPUT(config, NETLIST_TAG ":cpu_db4", "cpu_db4.IN", 0);
	NETLIST_LOGIC_INPUT(config, NETLIST_TAG ":cpu_db5", "cpu_db5.IN", 0);
	NETLIST_LOGIC_INPUT(config, NETLIST_TAG ":cpu_db6", "cpu_db6.IN", 0);
	NETLIST_LOGIC_INPUT(config, NETLIST_TAG ":cpu_db7", "cpu_db7.IN", 0);

	NETLIST_ANALOG_OUTPUT(config, NETLIST_TAG ":video_out", 0).set_params("video_out", FUNC(hazl1500_state::video_out_cb));
	NETLIST_ANALOG_OUTPUT(config, NETLIST_TAG ":vblank", 0).set_params("vblank", FUNC(hazl1500_state::vblank_cb));
	NETLIST_ANALOG_OUTPUT(config, NETLIST_TAG ":tvinterq", 0).set_params("tvinterq", FUNC(hazl1500_state::tvinterq_cb));

	/* keyboard controller */
	AY3600(config, m_kbdc, 0);
	m_kbdc->x0().set_ioport("X0");
	m_kbdc->x1().set_ioport("X1");
	m_kbdc->x2().set_ioport("X2");
	m_kbdc->x3().set_ioport("X3");
	m_kbdc->x4().set_ioport("X4");
	m_kbdc->x5().set_ioport("X5");
	m_kbdc->x6().set_ioport("X6");
	m_kbdc->x7().set_ioport("X7");
	m_kbdc->x8().set_ioport("X8");
	m_kbdc->shift().set(FUNC(hazl1500_state::ay3600_shift_r));
	m_kbdc->control().set(FUNC(hazl1500_state::ay3600_control_r));
	m_kbdc->data_ready().set(FUNC(hazl1500_state::ay3600_data_ready_w));
}


ROM_START( hazl1500 )
	ROM_REGION( 0x10000, NETLIST_TAG, ROMREGION_ERASE00 )

	ROM_REGION( 0x1000, CPU_TAG, ROMREGION_ERASEFF )
	ROM_LOAD( "h15s-00i-10-3.bin", 0x0000, 0x0800, CRC(a2015f72) SHA1(357cde517c3dcf693de580881add058c7b26dfaa))

	ROM_REGION( 0x800, CHAR_EPROM_TAG, ROMREGION_ERASEFF )
	ROM_LOAD( "u83_chr.bin", 0x0000, 0x0800, CRC(e0c6b734) SHA1(7c42947235c66c41059fd4384e09f4f3a17c9857))

	ROM_REGION( 0x100, BAUD_PROM_TAG, ROMREGION_ERASEFF )
	ROM_LOAD( "u43_702129_82s129.bin", 0x0000, 0x0100, CRC(b35aea2b) SHA1(4702620cdef72b32a397580c22b75df36e24ac74))

	ROM_REGION( 0x100, VIDEO_PROM_TAG, ROMREGION_ERASEFF )
	ROM_LOAD( "u90_702128_82s129.bin", 0x0000, 0x0100, CRC(277bc424) SHA1(528a0de3b54d159bc14411961961706bf9ec41bf))
ROM_END

ROM_START( hazl1552 )
	ROM_REGION( 0x10000, NETLIST_TAG, ROMREGION_ERASE00 )

	ROM_REGION( 0x1000, CPU_TAG, ROMREGION_ERASEFF )
	ROM_LOAD( "h200-009-11-0_vt52_u22.bin", 0x0000, 0x0800, CRC(622e3fe1) SHA1(886cea7315c4945f4dced7ee45b695ae0bd004aa))
	ROM_LOAD( "h200-009-21-0_vt52_u23.bin", 0x0800, 0x0800, CRC(25494fea) SHA1(89059ec76b386114208f2e589046f230502577f4))

	ROM_REGION( 0x800, CHAR_EPROM_TAG, ROMREGION_ERASEFF )
	ROM_LOAD( "h200-010-0.bin", 0x0000, 0x0800, CRC(80a58198) SHA1(ae37adbceedd7de22770f5831e32d8749c6ef3b8))

	ROM_REGION( 0x100, BAUD_PROM_TAG, ROMREGION_ERASEFF )
	ROM_LOAD( "u49.bin", 0x0000, 0x0100, CRC(b35aea2b) SHA1(4702620cdef72b32a397580c22b75df36e24ac74))

	ROM_REGION( 0x100, VIDEO_PROM_TAG, ROMREGION_ERASEFF )
	ROM_LOAD( "u90.bin", 0x0000, 0x0100, CRC(277bc424) SHA1(528a0de3b54d159bc14411961961706bf9ec41bf))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY                  FULLNAME          FLAGS
COMP( 1977, hazl1500, 0,      0,      hazl1500, hazl1500, hazl1500_state, empty_init, "Hazeltine Corporation", "Hazeltine 1500", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)
COMP( 1979, hazl1552, 0,      0,      hazl1500, hazl1500, hazl1500_state, empty_init, "Hazeltine Corporation", "Hazeltine 1552", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)



hazl1420.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Hazeltine 1420 terminal.

****************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/bankdev.h"
#include "machine/i8243.h"
#include "machine/input_merger.h"
#include "machine/ins8250.h"
#include "sound/beep.h"
#include "video/dp8350.h"
#include "screen.h"
#include "speaker.h"


namespace {

class hazl1420_state : public driver_device
{
public:
	hazl1420_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_bankdev(*this, "bankdev")
		, m_ioexp(*this, "ioexp%u", 0U)
		, m_mainint(*this, "mainint")
		, m_crtc(*this, "crtc")
		, m_beeper(*this, "beep")
		, m_videoram(*this, "videoram")
		, m_keys(*this, "KEY%u", 0U)
	{
	}

	void hazl1420(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void p1_w(u8 data);
	u8 p2_r();
	void p2_w(u8 data);
	void videoram_w(offs_t offset, u8 data);
	void crtc_w(offs_t offset, u8 data);
	void p6_w(u8 data);
	void p7_w(u8 data);

	u8 key_r();

	void crtc_lbre_w(int state);
	void crtc_vblank_w(int state);

	void prog_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void bank_map(address_map &map) ATTR_COLD;

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	required_device<mcs48_cpu_device> m_maincpu;
	required_device<address_map_bank_device> m_bankdev;
	required_device_array<i8243_device, 2> m_ioexp;
	required_device<input_merger_device> m_mainint;
	required_device<dp8350_device> m_crtc;
	required_device<beep_device> m_beeper;
	required_shared_ptr<u8> m_videoram;
	required_ioport_array<10> m_keys;
};

void hazl1420_state::p1_w(u8 data)
{
	m_ioexp[0]->cs_w((data & 0xc0) == 0x80 ? 0 : 1);
	m_ioexp[1]->cs_w((data & 0xc0) == 0xc0 ? 0 : 1);

	// acknowledge CRTC interrupts
	if (BIT(data, 4))
		m_mainint->in_w<0>(0);
}

u8 hazl1420_state::p2_r()
{
	u8 result = 0xe0 | (!m_crtc->lbre_r() << 4);
	result |= m_ioexp[0]->p2_r() & m_ioexp[1]->p2_r();
	return result;
}

void hazl1420_state::p2_w(u8 data)
{
	m_bankdev->set_bank((data & 0xe0) >> 1 | (data & 0x0f));
}

void hazl1420_state::videoram_w(offs_t offset, u8 data)
{
	m_videoram[offset] = data;
	if (BIT(m_maincpu->p1_r(), 5))
		m_videoram[offset ^ 1] = data;
}

void hazl1420_state::crtc_w(offs_t offset, u8 data)
{
	// CRTC registers are loaded only during vertical blanking period
	m_crtc->register_load(bitswap<2>(offset >> 12, 0, 1), offset & 0xfff);
}

void hazl1420_state::p6_w(u8 data)
{
	m_beeper->set_state(!BIT(data, 1));
}

void hazl1420_state::p7_w(u8 data)
{
}

u8 hazl1420_state::key_r()
{
	u8 row = m_maincpu->p1_r() & 0x0f;
	return (row < 10) ? m_keys[row]->read() : 0xff;
}

void hazl1420_state::prog_map(address_map &map)
{
	map(0x000, 0xfff).rom().region("maincpu", 0);
}

void hazl1420_state::io_map(address_map &map)
{
	map(0x00, 0xff).m(m_bankdev, FUNC(address_map_bank_device::amap8));
}

void hazl1420_state::bank_map(address_map &map)
{
	map(0x0000, 0x07ff).ram().share("videoram").w(FUNC(hazl1420_state::videoram_w));
	map(0x0800, 0x0807).mirror(0x10).rw("ace", FUNC(ins8250_device::ins8250_r), FUNC(ins8250_device::ins8250_w));
	map(0x0c00, 0x0cff).ram(); // optional input buffer?
	map(0x4000, 0x7fff).w(FUNC(hazl1420_state::crtc_w));
}

void hazl1420_state::machine_start()
{
}

void hazl1420_state::crtc_lbre_w(int state)
{
	if (!state && !m_crtc->vblank_r() && !BIT(m_maincpu->p1_r(), 4))
		m_mainint->in_w<0>(1);
}

void hazl1420_state::crtc_vblank_w(int state)
{
	if (state && !BIT(m_maincpu->p1_r(), 4))
		m_mainint->in_w<0>(1);
}

u32 hazl1420_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

static INPUT_PORTS_START(hazl1420)
	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD)) PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD)) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD)) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) PORT_CODE(KEYCODE_MINUS_PAD)

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD)) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD)) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD)) PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD)) PORT_CODE(KEYCODE_COMMA_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD)) PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_CODE(KEYCODE_ENTER_PAD)

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR('@') PORT_CODE(KEYCODE_2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR('$') PORT_CODE(KEYCODE_4)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR('^') PORT_CODE(KEYCODE_6)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR('&') PORT_CODE(KEYCODE_7)

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('q') PORT_CHAR('Q') PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('w') PORT_CHAR('W') PORT_CODE(KEYCODE_W)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('e') PORT_CHAR('E') PORT_CODE(KEYCODE_E)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('r') PORT_CHAR('R') PORT_CODE(KEYCODE_R)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('t') PORT_CHAR('T') PORT_CODE(KEYCODE_T)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('y') PORT_CHAR('Y') PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('u') PORT_CHAR('U') PORT_CODE(KEYCODE_U)

	PORT_START("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('a') PORT_CHAR('A') PORT_CODE(KEYCODE_A)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('s') PORT_CHAR('S') PORT_CODE(KEYCODE_S)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('d') PORT_CHAR('D') PORT_CODE(KEYCODE_D)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('f') PORT_CHAR('F') PORT_CODE(KEYCODE_F)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('g') PORT_CHAR('G') PORT_CODE(KEYCODE_G)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('h') PORT_CHAR('H') PORT_CODE(KEYCODE_H)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('j') PORT_CHAR('J') PORT_CODE(KEYCODE_J)

	PORT_START("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('z') PORT_CHAR('Z') PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('x') PORT_CHAR('X') PORT_CODE(KEYCODE_X)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('c') PORT_CHAR('C') PORT_CODE(KEYCODE_C)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('v') PORT_CHAR('V') PORT_CODE(KEYCODE_V)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('b') PORT_CHAR('B') PORT_CODE(KEYCODE_B)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('n') PORT_CHAR('N') PORT_CODE(KEYCODE_N)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_M)

	PORT_START("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('=') PORT_CHAR('+') PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('8') PORT_CHAR('*') PORT_CODE(KEYCODE_8)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('9') PORT_CHAR('(') PORT_CODE(KEYCODE_9)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0') PORT_CHAR(')') PORT_CODE(KEYCODE_0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('-') PORT_CHAR('_') PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x08) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('`') PORT_CHAR('~') PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(']') PORT_CHAR('}') PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('i') PORT_CHAR('I') PORT_CODE(KEYCODE_I)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('o') PORT_CHAR('O') PORT_CODE(KEYCODE_O)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('p') PORT_CHAR('P') PORT_CODE(KEYCODE_P)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('[') PORT_CHAR('{') PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\\') PORT_CHAR('|') PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KEY8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CHAR(0x0d) PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('k') PORT_CHAR('K') PORT_CODE(KEYCODE_K)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('l') PORT_CHAR('L') PORT_CODE(KEYCODE_L)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHAR(':') PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\'') PORT_CHAR('"') PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(' ') PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x1b) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KEY9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LF") PORT_CHAR(0x0a) PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',') PORT_CHAR('<') PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('.') PORT_CHAR('>') PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('/') PORT_CHAR('/') PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del") PORT_CHAR(0x7f) PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	// DIP switches are on access panel above keyboard
	// "SW1" and "SW2" are not actual names

	PORT_START("INP4")
	PORT_DIPNAME(0x7, 0x6, "Baud Rate") PORT_DIPLOCATION("SW1:3,4,5")
	PORT_DIPSETTING(0x0, "110")
	PORT_DIPSETTING(0x1, "300")
	PORT_DIPSETTING(0x2, "600")
	PORT_DIPSETTING(0x3, "1200")
	PORT_DIPSETTING(0x4, "1800")
	PORT_DIPSETTING(0x5, "2400")
	PORT_DIPSETTING(0x6, "4800")
	PORT_DIPSETTING(0x7, "9600")
	PORT_DIPNAME(0x8, 0x0, "Lead-In") PORT_DIPLOCATION("SW1:6") // not verified
	PORT_DIPSETTING(0x0, "ESC")
	PORT_DIPSETTING(0x8, "~")

	PORT_START("INP5")
	PORT_DIPNAME(0x3, 0x3, "Parity") PORT_DIPLOCATION("SW1:7,8")
	PORT_DIPSETTING(0x0, "Odd")
	PORT_DIPSETTING(0x1, "Even")
	PORT_DIPSETTING(0x2, "1")
	PORT_DIPSETTING(0x3, "0")
	PORT_DIPNAME(0x4, 0x0, DEF_STR(Unused)) PORT_DIPLOCATION("SW2:1")
	PORT_DIPSETTING(0x4, DEF_STR(Off))
	PORT_DIPSETTING(0x0, DEF_STR(On))
	PORT_BIT(0x8, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("INP6")
	PORT_DIPNAME(0x1, 0x0, "Cursor") PORT_DIPLOCATION("SW2:2")
	PORT_DIPSETTING(0x0, "Wraparound")
	PORT_DIPSETTING(0x1, "No Wrap")
	PORT_DIPNAME(0x2, 0x2, "On Line") PORT_DIPLOCATION("SW2:7")
	PORT_DIPSETTING(0x2, DEF_STR(Off))
	PORT_DIPSETTING(0x0, DEF_STR(On))
	PORT_DIPNAME(0x4, 0x4, "Automatic LF/CR") PORT_DIPLOCATION("SW2:6")
	PORT_DIPSETTING(0x4, "Auto LF")
	PORT_DIPSETTING(0x0, "Carriage Return")
	PORT_DIPNAME(0x8, 0x8, "Communication Mode") PORT_DIPLOCATION("SW2:5")
	PORT_DIPSETTING(0x0, "Half Duplex")
	PORT_DIPSETTING(0x8, "Full Duplex")

	PORT_START("INP7")
	PORT_DIPNAME(0x1, 0x1, "Font") PORT_DIPLOCATION("SW2:4")
	PORT_DIPSETTING(0x1, "Upper/Lower Case")
	PORT_DIPSETTING(0x0, "Upper Case Only")
	PORT_BIT(0x2, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("All Caps") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT(0x4, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x8, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL)

	PORT_START("UNUSED")
	PORT_DIPNAME(0x1, 0x0, DEF_STR(Unused)) PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(0x1, DEF_STR(Off))
	PORT_DIPSETTING(0x0, DEF_STR(On))
	PORT_DIPNAME(0x2, 0x0, DEF_STR(Unused)) PORT_DIPLOCATION("SW1:2")
	PORT_DIPSETTING(0x2, DEF_STR(Off))
	PORT_DIPSETTING(0x0, DEF_STR(On))
	PORT_DIPNAME(0x4, 0x0, DEF_STR(Unused)) PORT_DIPLOCATION("SW2:3")
	PORT_DIPSETTING(0x4, DEF_STR(Off))
	PORT_DIPSETTING(0x0, DEF_STR(On))
	PORT_DIPNAME(0x8, 0x0, DEF_STR(Unused)) PORT_DIPLOCATION("SW2:8")
	PORT_DIPSETTING(0x8, DEF_STR(Off))
	PORT_DIPSETTING(0x0, DEF_STR(On))
INPUT_PORTS_END

void hazl1420_state::hazl1420(machine_config &config)
{
	I8049(config, m_maincpu, 11_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &hazl1420_state::prog_map);
	m_maincpu->set_addrmap(AS_IO, &hazl1420_state::io_map);
	m_maincpu->p1_out_cb().set(FUNC(hazl1420_state::p1_w));
	m_maincpu->p2_in_cb().set(FUNC(hazl1420_state::p2_r));
	m_maincpu->p2_out_cb().set(FUNC(hazl1420_state::p2_w));
	m_maincpu->p2_out_cb().append(m_ioexp[0], FUNC(i8243_device::p2_w));
	m_maincpu->p2_out_cb().append(m_ioexp[1], FUNC(i8243_device::p2_w));
	m_maincpu->prog_out_cb().set(m_ioexp[0], FUNC(i8243_device::prog_w));
	m_maincpu->prog_out_cb().append(m_ioexp[1], FUNC(i8243_device::prog_w));
	m_maincpu->t0_in_cb().set(m_crtc, FUNC(dp8350_device::vblank_r));
	m_maincpu->t1_in_cb().set("ace", FUNC(ins8250_device::intrpt_r));

	INPUT_MERGER_ANY_HIGH(config, m_mainint);
	m_mainint->output_handler().set_inputline(m_maincpu, MCS48_INPUT_IRQ);

	ADDRESS_MAP_BANK(config, m_bankdev);
	m_bankdev->set_addrmap(0, &hazl1420_state::bank_map);
	m_bankdev->set_data_width(8);
	m_bankdev->set_addr_width(15);
	m_bankdev->set_stride(0x100);

	I8243(config, m_ioexp[0]);
	m_ioexp[0]->p4_in_cb().set_ioport("INP4");
	m_ioexp[0]->p5_in_cb().set_ioport("INP5");
	m_ioexp[0]->p6_out_cb().set(FUNC(hazl1420_state::p6_w));
	m_ioexp[0]->p7_out_cb().set(FUNC(hazl1420_state::p7_w));

	I8243(config, m_ioexp[1]);
	m_ioexp[1]->p4_in_cb().set(FUNC(hazl1420_state::key_r));
	m_ioexp[1]->p5_in_cb().set(FUNC(hazl1420_state::key_r)).rshift(4);
	m_ioexp[1]->p6_in_cb().set_ioport("INP6");
	m_ioexp[1]->p7_in_cb().set_ioport("INP7");

	ins8250_device &ace(INS8250(config, "ace", 2'764'800));
	ace.out_int_callback().set(m_mainint, FUNC(input_merger_device::in_w<1>));
	ace.out_tx_callback().set("eia", FUNC(rs232_port_device::write_txd));
	ace.out_rts_callback().set("eia", FUNC(rs232_port_device::write_rts));
	ace.out_dtr_callback().set("eia", FUNC(rs232_port_device::write_dtr));
	//ace.baudout_callback().set("eia", FUNC(rs232_port_device::write_etc)); // 16x rate output (unemulated)

	DP8350(config, m_crtc, 10.92_MHz_XTAL).set_screen("screen");
	m_crtc->lbre_callback().set(FUNC(hazl1420_state::crtc_lbre_w));
	m_crtc->vblank_callback().set(FUNC(hazl1420_state::crtc_vblank_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_screen_update(FUNC(hazl1420_state::screen_update));

	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 1000).add_route(ALL_OUTPUTS, "mono", 1.00);

	rs232_port_device &eia(RS232_PORT(config, "eia", default_rs232_devices, nullptr));
	eia.rxd_handler().set("ace", FUNC(ins8250_device::rx_w));
	eia.cts_handler().set("ace", FUNC(ins8250_device::cts_w));
	eia.dsr_handler().set("ace", FUNC(ins8250_device::dsr_w));
	eia.dcd_handler().set("ace", FUNC(ins8250_device::dcd_w));
}

ROM_START(hazl1420)
	ROM_REGION(0x0800, "maincpu_internal", 0)
	// This internal ROM seems to belong to some earlier program revision
	ROM_LOAD("8049h.u19", 0x0000, 0x0800, CRC(81beb6de) SHA1(f272d1277f100af92384a4c4cec2c9db9424b603))

	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("2716.u10", 0x0000, 0x0800, CRC(7c40ba24) SHA1(7575225adf1a06d66b079efcf0f4f9ee77fbddd4))
	ROM_LOAD("8316.u11", 0x0800, 0x0800, CRC(1c112f09) SHA1(fa4973e99c6d66809cffef009c4869787089a774))

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD("8316.u23", 0x0000, 0x0800, NO_DUMP)
ROM_END

} // anonymous namespace


COMP(1979, hazl1420, 0, 0, hazl1420, hazl1420, hazl1420_state, empty_init, "Hazeltine", "1420 Video Display Terminal", MACHINE_NOT_WORKING)



hds200.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    Human Designed Systems HDS200

    ANSI/DEC-compatible terminal

    Hardware:
    - Z80A (Z8400APS)
    - Z80A DMA (Z8410APS)
    - 2x SCN2681A
    - SCN2674B
    - 2x MB81416-12 DRAM (and two empty sockets)
    - 2x TMM2016BP-90 (2k)
    - 1x TMM2016AP-10 (2k)
    - MK48Z02B-25 (2k)
    - XTAL 3.6864 MHz (next to DUARTs)
    - XTAL 8 MHz (CPU)
    - XTAL 22.680 MHz and 35.640 MHz (video)

    TODO:
    - Fix SCN2674/Z80DMA hookup
    - Missing keyboard keys
    - RS232 control lines

    Notes:
    - The PCB has a large unpopulated area. Possibly this is used for the
      200G variant.

***************************************************************************/

#include "emu.h"

#include "hds200_kbd.h"

#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/input_merger.h"
#include "machine/mc68681.h"
#include "machine/nvram.h"
#include "machine/z80dma.h"
#include "video/scn2674.h"

#include "emupal.h"
#include "screen.h"


namespace {


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class hds200_state : public driver_device
{
public:
	hds200_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_dma(*this, "dma"),
		m_screen(*this, "screen"),
		m_palette(*this, "palette"),
		m_avdc(*this, "avdc"),
		m_duart(*this, "duart%u", 0U),
		m_chargen(*this, "chargen"),
		m_vram(*this, "vram"),
		m_rombank(*this, "rombank"),
		m_nmi_enabled(false)
	{ }

	void hds200(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_device<z80dma_device> m_dma;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<scn2674_device> m_avdc;
	required_device_array<scn2681_device, 2> m_duart;
	required_region_ptr<uint8_t> m_chargen;
	required_shared_ptr<uint8_t> m_vram;
	required_memory_bank m_rombank;

	uint8_t attr_r(offs_t offset);
	void attrram_map(address_map &map) ATTR_COLD;
	uint8_t char_r(offs_t offset);
	void charram_map(address_map &map) ATTR_COLD;

	SCN2674_DRAW_CHARACTER_MEMBER(draw_character);

	uint8_t dma_mreq_r(offs_t offset);
	void dma_mreq_w(offs_t offset, uint8_t data);

	void nmi_w(int state);

	void duart0_out_w(uint8_t data);
	void duart1_out_w(uint8_t data);

	bool m_reverse_video;
	bool m_nmi_enabled;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void hds200_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).rom().region("maincpu", 0);
	map(0x4000, 0x5fff).bankr(m_rombank);
//  map(0x6000, 0x6001) // unknown device here
	map(0x6800, 0x6fff).ram().share("nvram");
	map(0x7000, 0x77ff).ram().share("vram");
	map(0x8000, 0xbfff).ram();
	map(0xc000, 0xffff).noprw(); // expansion ram
}

void hds200_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x00).rw(m_dma, FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	map(0x20, 0x2f).rw(m_duart[0], FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0x40, 0x4f).rw(m_duart[1], FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0x60, 0x67).rw(m_avdc, FUNC(scn2674_device::read), FUNC(scn2674_device::write));
}


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

uint8_t hds200_state::attr_r(offs_t offset)
{
	offs_t addr = ((offset << 1) + 0) & 0x7ff;
	return m_vram[addr];
}

void hds200_state::attrram_map(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(hds200_state::attr_r));
}

uint8_t hds200_state::char_r(offs_t offset)
{
	offs_t addr = 0;

	// there should be a better way
	if (((offset << 1) & 0x7000) == 0x7000)
	{
		// start address for row table
		addr = (offset << 1) & 0x7ff;
	}
	else
	{
		// char data
		addr = ((offset << 1) + 1) & 0x7ff;
	}

	return m_vram[addr];
}

void hds200_state::charram_map(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(hds200_state::char_r));
}

SCN2674_DRAW_CHARACTER_MEMBER( hds200_state::draw_character )
{
	uint16_t data = m_chargen[charcode << 4 | linecount];
	const pen_t *const pen = m_palette->pens();

	// 7-------  invert
	// -6------  bold
	// --5-----  unknown
	// ---4----  underline
	// ----3---  blink
	// -----2--  conceal
	// ------1-  unknown
	// -------0  unknown

	if (ul && BIT(attrcode, 4))
		data = 0x1ff;

	if (blink && BIT(attrcode, 3))
		data = 0x000;

	// invert
	if (BIT(attrcode, 7))
		data = ~data;

	if (cursor)
		data = ~data;

	// foreground/background colors
	rgb_t fg = BIT(attrcode, 6) ? pen[2] : pen[1];
	rgb_t bg = pen[0];

	if (m_reverse_video)
		std::swap(fg, bg);

	// draw 9 pixels of the character
	for (int i = 0; i < 9; i++)
		bitmap.pix(y, x + i) = BIT(data, 8 - i) ? fg : bg;
}

static const gfx_layout char_layout =
{
	8,16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP8(0,1) },
	{ STEP16(0,8) },
	8*16
};

static GFXDECODE_START(chars)
	GFXDECODE_ENTRY("chargen", 0, char_layout, 0, 1)
GFXDECODE_END


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

uint8_t hds200_state::dma_mreq_r(offs_t offset)
{
	return m_maincpu->space(AS_PROGRAM).read_byte(offset);
}

void hds200_state::dma_mreq_w(offs_t offset, uint8_t data)
{
	m_maincpu->space(AS_PROGRAM).write_byte(offset, data);
}

void hds200_state::nmi_w(int state)
{
	if (state && m_nmi_enabled)
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}

void hds200_state::duart0_out_w(uint8_t data)
{
	// 765-----  unknown
	// ---4----  80/132 column switch (1 = 80)
	// ----3---  reverse video
	// -----210  unknown

	//logerror("duart0_out_w: %02x\n", data);

	m_reverse_video = bool(BIT(data, 3));
}

void hds200_state::duart1_out_w(uint8_t data)
{
	// 765-----  unknown
	// ---4----  nmi enable
	// ----3---  unknown
	// -----2--  rombank
	// ------10  unknown

	//logerror("duart1_out_w: %02x\n", data);

	m_nmi_enabled = bool(BIT(data, 4));
	m_rombank->set_entry(!BIT(data, 2));
}

void hds200_state::machine_start()
{
	m_rombank->configure_entries(0, 2, memregion("maincpu")->base() + 0x4000, 0x2000);

	// register for save states
	save_item(NAME(m_reverse_video));
	save_item(NAME(m_nmi_enabled));
}

void hds200_state::machine_reset()
{
	m_reverse_video = false;
	m_nmi_enabled = false;

	m_rombank->set_entry(0);

	// no duart irq active
	m_duart[0]->ip4_w(1);
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void hds200_state::hds200(machine_config &config)
{
	Z80(config, m_maincpu, 8_MHz_XTAL / 2); // divider not verified
	m_maincpu->set_addrmap(AS_PROGRAM, &hds200_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &hds200_state::io_map);
	m_maincpu->busack_cb().set(m_dma, FUNC(z80dma_device::bai_w));

	input_merger_device &z80_irq(INPUT_MERGER_ANY_HIGH(config, "z80_irq"));
	z80_irq.output_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	Z80DMA(config, m_dma, 8_MHz_XTAL / 2); // divider not verified
	m_dma->out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	m_dma->in_mreq_callback().set(FUNC(hds200_state::dma_mreq_r));
	m_dma->out_mreq_callback().set(FUNC(hds200_state::dma_mreq_w));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_color(rgb_t::amber());
	m_screen->set_raw(22.680_MHz_XTAL, 1008, 0, 720, 375, 0, 350); // 80-column mode
	m_screen->set_screen_update(m_avdc, FUNC(scn2674_device::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	GFXDECODE(config, "gfxdecode", m_palette, chars);

	SCN2674(config, m_avdc, 22.680_MHz_XTAL / 9);
	m_avdc->intr_callback().set("z80_irq", FUNC(input_merger_device::in_w<0>));
	m_avdc->mbc_callback().set(FUNC(hds200_state::nmi_w));
	m_avdc->set_addrmap(0, &hds200_state::charram_map);
	m_avdc->set_addrmap(1, &hds200_state::attrram_map);
	m_avdc->set_character_width(9);
	m_avdc->set_screen("screen");
	m_avdc->set_display_callback(FUNC(hds200_state::draw_character));

	input_merger_device &duart_irq(INPUT_MERGER_ANY_HIGH(config, "duart_irq"));
	duart_irq.output_handler().set("z80_irq", FUNC(input_merger_device::in_w<1>));
	duart_irq.output_handler().append(m_duart[0], FUNC(scn2681_device::ip4_w)).invert();

	SCN2681(config, m_duart[0], 3.6864_MHz_XTAL);
	m_duart[0]->irq_cb().set("duart_irq", FUNC(input_merger_device::in_w<0>));
	m_duart[0]->outport_cb().set(FUNC(hds200_state::duart0_out_w));
	m_duart[0]->a_tx_cb().set("line1", FUNC(rs232_port_device::write_txd));
	m_duart[0]->b_tx_cb().set("line2", FUNC(rs232_port_device::write_txd));

	SCN2681(config, m_duart[1], 3.6864_MHz_XTAL);
	m_duart[1]->irq_cb().set("duart_irq", FUNC(input_merger_device::in_w<1>));
	m_duart[1]->outport_cb().set(FUNC(hds200_state::duart1_out_w));
	m_duart[1]->a_tx_cb().set("line3", FUNC(rs232_port_device::write_txd));
	m_duart[1]->b_tx_cb().set("kbd", FUNC(hds200_kbd_hle_device::rx_w));

	rs232_port_device &rs232_line1(RS232_PORT(config, "line1", default_rs232_devices, nullptr));
	rs232_line1.rxd_handler().set(m_duart[0], FUNC(scn2681_device::rx_a_w));
	rs232_line1.cts_handler().set(m_duart[0], FUNC(scn2681_device::ip0_w));

	rs232_port_device &rs232_line2(RS232_PORT(config, "line2", default_rs232_devices, nullptr));
	rs232_line2.rxd_handler().set(m_duart[0], FUNC(scn2681_device::rx_b_w));
	rs232_line2.cts_handler().set(m_duart[0], FUNC(scn2681_device::ip1_w));

	rs232_port_device &rs232_line3(RS232_PORT(config, "line3", default_rs232_devices, nullptr));
	rs232_line3.rxd_handler().set(m_duart[1], FUNC(scn2681_device::rx_a_w));
	rs232_line3.cts_handler().set(m_duart[1], FUNC(scn2681_device::ip0_w));

	hds200_kbd_hle_device &kbd(HDS200_KBD_HLE(config, "kbd"));
	kbd.tx_handler().set(m_duart[1], FUNC(scn2681_device::rx_b_w));
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( hds200 )
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD("u78.bin", 0x0000, 0x2000, CRC(518cfeb7) SHA1(3c214dede545a2a991fdd77311b3474b01b2123f))
	ROM_LOAD("u79.bin", 0x2000, 0x2000, CRC(3a765c8a) SHA1(8ffb5fb07b086ac725f22c2643ecd2e061130b57))
	ROM_LOAD("u80.bin", 0x4000, 0x2000, CRC(f72dfeeb) SHA1(7e09b8f0df8384f6b5c4d29cd59fa31f743de8b8))
	ROM_LOAD("u81.bin", 0x6000, 0x2000, CRC(b3f430be) SHA1(dd5503de46c7f00f2e376104dff13224026f5870))

	ROM_REGION(0x2000, "chargen", ROMREGION_INVERT)
	ROM_LOAD("u56.bin", 0x0000, 0x2000, CRC(cd268bff) SHA1(42f2aa3f51ae53e5cbcb57f974e99b24bca5f56f))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME    PARENT   COMPAT  MACHINE  INPUT  CLASS         INIT        COMPANY         FULLNAME            FLAGS
COMP( 1985, hds200, 0,       0,      hds200,  0,     hds200_state, empty_init, "Human Designed Systems", "HDS200", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



hec2hrp.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:JJ Stacino
/***************************************************************************

        Hector 2HR+
        Victor
        Hector 2HR
        Hector HRX
        Hector MX40c
        Hector MX80c
        Hector 1
        Interact

The first model (Interact)was made by Interact Electronics Inc of Ann Arbor,
Michigan. However, just after launch, the company collapsed. The liquidator,
Protecto, sold some and MicroVideo sold the rest. MicroVideo continued to
develop but went under 2 years later. Meanwhile, the French company
Lambda Systems sold a clone called the Victor Lambda. But, like the
Americans, Lambda Systems also collapsed. Another French company,
Micronique, purchased all remaining stock and intellectual rights
from Lambda Systems, Microvideo and Interact, and the computer becomes
wholly French. The computer has a name change, becoming the Hector.
This in turn gets upgraded (2HR, HRX, MX). The line is finally
retired in about 1985.

These machines can load and run cassettes for the interact / hector1.
    hec2hr - press 2 then 1
    hec2hrp - press 2 then 1
    victor - press R then L

These machines will load the cassette but the keys don't work
    hec2hrx, hec2mx40, hec2mdhrx - press 5 then 1

This machine not compatible
    hec2mx80

2009-05-12 Skeleton driver - Micko : mmicko@gmail.com
2009-10-29 Update skeleton to functional machine by yo_fr (jj.stac @ aliceadsl.fr)
                => add Keyboard,
                => add color,
                => add cassette,
                => add sn76477 sound and 1bit sound,
                => add joysticks (stick, pot, fire)
                => add BR/HR switching
                => add bank switch for HRX
                => add device MX80c and bank switching for the ROM
    Important note : Keyboard emulation code obtained from
                    DChector project : http://dchector.free.fr/ made by DanielCoulom
                    (thanks Daniel)
2010-01-03 Update and cleanup  by yo_fr       (jj.stac@aliceadsl.fr)
                => add the port mapping for keyboard
2010-11-20 : synchronization between uPD765 and Z80 are now OK, CP/M running! JJStacino
2011-11-11 : add minidisk (3.5") support  JJStacino

        For more information about these machines, see the DChector project : http://dchector.free.fr/ made by DanielCoulom
        (thanks to Daniel) and Yves site : http://hectorvictor.free.fr/ (thanks too Yves!)

TODO :  Add cartridge functionality
        Adjust the one-shot and A/D timing (sn76477)

****************************************************************************/
/* Joystick 1 :
 Numpad :
                (UP)5
  (left)1                     (right)3
               (down)2

 Fire <+>
 Pot =>  home/end */

	/* Joystick 0 :
	arrows
	            (UP)^
	(left)<-                     (right)->
	           (down)v

	Fire <?> near <1>
	Pot => INS /SUPPR

	Cassette : wav file (1 way, 16 bits, 44100hz)
	        K7 file  (For data and games)
	        FOR file (for forth screen data)
*/

#include "emu.h"
#include "hec2hrp.h"

#include "cpu/z80/z80.h"
#include "cpu/i8085/i8085.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "formats/hect_dsk.h"
#include "formats/hect_tap.h"
#include "formats/hector_minidisc.h"


void hec2hrp_state::interact_mem(address_map &map)
{
	map.unmap_value_high();
	/* Main ROM page*/
	map(0x0000, 0x3fff).rom();
	map(0x1000, 0x1000).w(FUNC(hec2hrp_state::color_a_w));  /* Color c0/c1*/
	map(0x1800, 0x1800).w(FUNC(hec2hrp_state::color_b_w));  /* Color c2/c3*/
	map(0x2000, 0x2003).w(FUNC(hec2hrp_state::sn_2000_w));  /* Sound*/
	map(0x2800, 0x2803).w(FUNC(hec2hrp_state::sn_2800_w));  /* Sound*/
	map(0x3000, 0x3000).rw(FUNC(hec2hrp_state::cassette_r), FUNC(hec2hrp_state::sn_3000_w));/* Write necessary*/
	map(0x3800, 0x3807).rw(FUNC(hec2hrp_state::keyboard_r), FUNC(hec2hrp_state::keyboard_w));  /* Keyboard*/
	map(0x4000, 0x49ff).ram().share("videoram");
	map(0x4A00, 0xffff).ram();
}

void hec2hrp_state::hec2hrp_mem(address_map &map)
{
	map.unmap_value_high();
	interact_mem(map);
	map(0x0800, 0x0808).w(FUNC(hec2hrp_state::switch_bank_w));   // bank management
	map(0xc000, 0xffff).ram().share("hector_videoram");    /* => Bank Ram for video and data */
}

void hec2hrp_state::hec2hrx_mem(address_map &map)
{
	map.unmap_value_high();
	hec2hrp_mem(map);
	map(0x0000, 0x3fff).bankr("bank2"); /* Main ROM page*/
	map(0x3000, 0x3000).rw(FUNC(hec2hrp_state::cassette_r), FUNC(hec2hrp_state::sn_3000_w));/* Write necessary*/
	map(0x3800, 0x3807).rw(FUNC(hec2hrp_state::keyboard_r), FUNC(hec2hrp_state::keyboard_w));  /* Keyboard*/
	map(0xc000, 0xffff).bankrw("bank1").share("hector_videoram");  /* => Bank Ram for video and data */
}

void hec2hrp_state::hec2hrp_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0xff).rw(FUNC(hec2hrp_state::io_8255_r), FUNC(hec2hrp_state::io_8255_w));
}

void hec2hrp_state::hec2hrx_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0xf0, 0xff).rw(FUNC(hec2hrp_state::io_8255_r), FUNC(hec2hrp_state::io_8255_w));
}

void hec2hrp_state::hec2mdhrx_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);

	// Minidisc commands and changing the rom page */
	map(0x04, 0x07).rw(m_minidisc_fdc, FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	map(0x08, 0x08).w(FUNC(hec2hrp_state::minidisc_control_w));
	map(0xf0, 0xff).rw(FUNC(hec2hrp_state::io_8255_r), FUNC(hec2hrp_state::io_8255_w));
}

void hec2hrp_state::hec2mx40_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0xef).w(FUNC(hec2hrp_state::mx40_io_port_w));
	map(0xf0, 0xf3).rw(FUNC(hec2hrp_state::io_8255_r), FUNC(hec2hrp_state::io_8255_w));
}

void hec2hrp_state::hec2mx80_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0xef).w(FUNC(hec2hrp_state::mx80_io_port_w));
	map(0xf0, 0xf3).rw(FUNC(hec2hrp_state::io_8255_r), FUNC(hec2hrp_state::io_8255_w));


}

// 2nd cpu
void hec2hrp_state::hecdisc2_mem(address_map &map)
{
	map.unmap_value_high();

	map(0x0000, 0x3fff).bankrw("bank3"); /* ROM at start up, RAM later */
	map(0x4000, 0xffff).ram();
}

void hec2hrp_state::hecdisc2_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	// ROM page handling
	map(0x00, 0x0f).rw(FUNC(hec2hrp_state::disc2_io00_port_r), FUNC(hec2hrp_state::disc2_io00_port_w));
	// RS232 - 8251 comms handling
	map(0x20, 0x2f).rw(FUNC(hec2hrp_state::disc2_io20_port_r), FUNC(hec2hrp_state::disc2_io20_port_w));
	// Hector comms handling
	map(0x30, 0x3f).rw(FUNC(hec2hrp_state::disc2_io30_port_r), FUNC(hec2hrp_state::disc2_io30_port_w));
	map(0x40, 0x4f).rw(FUNC(hec2hrp_state::disc2_io40_port_r), FUNC(hec2hrp_state::disc2_io40_port_w));
	map(0x50, 0x5f).rw(FUNC(hec2hrp_state::disc2_io50_port_r), FUNC(hec2hrp_state::disc2_io50_port_w));
	// uPD765 link
	map(0x60, 0x61).m(m_upd_fdc, FUNC(upd765a_device::map));
	map(0x70, 0x70).mirror(0x0f).rw(m_upd_fdc, FUNC(upd765a_device::dma_r), FUNC(upd765a_device::dma_w));
}


/* Input ports */
static INPUT_PORTS_START( hec2hrp )
	/* keyboard input */
	PORT_START("KEY.0") /* [0] - port 3000 @ 0 */
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)    PORT_CHAR('*')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space")          PORT_CODE(KEYCODE_SPACE) PORT_CHAR(32)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return")         PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab")            PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("<--")            PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Caps Lock")      PORT_CODE(KEYCODE_CAPSLOCK)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl")           PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift")          PORT_CODE(KEYCODE_LSHIFT)     PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_START("KEY.1") /* [1] - port 3000 @ 1 */    /* buttons => 2  1  0  /  .  -  ,  +     */
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 \"")           PORT_CODE(KEYCODE_2)    PORT_CHAR('2') PORT_CHAR('"')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 >")            PORT_CODE(KEYCODE_1)    PORT_CHAR('1') PORT_CHAR('>')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0 <")            PORT_CODE(KEYCODE_0)    PORT_CHAR('0') PORT_CHAR('<')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)     PORT_CHAR('/') PORT_CHAR('@')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)    PORT_CHAR('.') PORT_CHAR('&')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)    PORT_CHAR('-') PORT_CHAR('_')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)        PORT_CHAR(',') PORT_CHAR('#')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)   PORT_CHAR('+') PORT_CHAR('^')

	PORT_START("KEY.2") /* [1] - port 3000 @ 2 */     /* buttons => .. 9  8  7  6  5  4  3  */
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 )")            PORT_CODE(KEYCODE_9)    PORT_CHAR('9') PORT_CHAR(')')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 (")            PORT_CODE(KEYCODE_8)    PORT_CHAR('8') PORT_CHAR('(')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 :")            PORT_CODE(KEYCODE_7)    PORT_CHAR('7') PORT_CHAR(':')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 !")            PORT_CODE(KEYCODE_6)    PORT_CHAR('6') PORT_CHAR('!')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 %")            PORT_CODE(KEYCODE_5)    PORT_CHAR('5') PORT_CHAR('%')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 $")            PORT_CODE(KEYCODE_4)    PORT_CHAR('4') PORT_CHAR('$')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 /")            PORT_CODE(KEYCODE_3)    PORT_CHAR('3') PORT_CHAR(39)
	PORT_START("KEY.3") /* [1] - port 3000 @ 3 */    /* buttons =>  B  A  ..  ? .. =   ..  ;       */
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)      PORT_CHAR('b') PORT_CHAR('B')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A")           PORT_CODE(KEYCODE_Q)    PORT_CHAR('a') PORT_CHAR('A')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)    PORT_CHAR('?')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_CHAR('=')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)        PORT_CHAR(';')
	PORT_START("KEY.4") /* [1] - port 3000 @ 4 */
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)            PORT_CHAR('j') PORT_CHAR('J')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)            PORT_CHAR('i') PORT_CHAR('I')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)            PORT_CHAR('h') PORT_CHAR('H')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)            PORT_CHAR('g') PORT_CHAR('G')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)            PORT_CHAR('f') PORT_CHAR('F')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)            PORT_CHAR('e') PORT_CHAR('E')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)            PORT_CHAR('c') PORT_CHAR('C')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)            PORT_CHAR('d') PORT_CHAR('D')

	PORT_START("KEY.5") /* [1] - port 3000 @ 5 */
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)            PORT_CHAR('r') PORT_CHAR('R')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q")         PORT_CODE(KEYCODE_A)    PORT_CHAR('q') PORT_CHAR('Q')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)            PORT_CHAR('p') PORT_CHAR('P')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)            PORT_CHAR('o') PORT_CHAR('O')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)            PORT_CHAR('n') PORT_CHAR('N')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)        PORT_CHAR('m') PORT_CHAR('M')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)            PORT_CHAR('l') PORT_CHAR('L')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)            PORT_CHAR('k') PORT_CHAR('K')

	PORT_START("KEY.6") /* [1] - port 3000 @ 6 */
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z")          PORT_CODE(KEYCODE_W)            PORT_CHAR('z') PORT_CHAR('Z')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)            PORT_CHAR('y') PORT_CHAR('Y')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X")          PORT_CODE(KEYCODE_X)             PORT_CHAR('x') PORT_CHAR('X')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W")          PORT_CODE(KEYCODE_Z)             PORT_CHAR('w') PORT_CHAR('W')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)            PORT_CHAR('v') PORT_CHAR('V')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)            PORT_CHAR('u') PORT_CHAR('U')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)            PORT_CHAR('t') PORT_CHAR('T')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)            PORT_CHAR('s') PORT_CHAR('S')

	PORT_START("KEY.7") /* [1] - port 3000 @ 7  JOYSTICK */
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Joy(0) LEFT")        PORT_CODE(KEYCODE_LEFT)     PORT_CHAR(UCHAR_MAMEKEY(LEFT))
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Joy(0) RIGHT")       PORT_CODE(KEYCODE_RIGHT)    PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Joy(0) UP")          PORT_CODE(KEYCODE_UP)       PORT_CHAR(UCHAR_MAMEKEY(UP))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Joy(0) DOWN")        PORT_CODE(KEYCODE_DOWN)     PORT_CHAR(UCHAR_MAMEKEY(DOWN))
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Joy(1) LEFT")        PORT_CODE(KEYCODE_1_PAD)     // Joy(1) on numpad
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Joy(1) RIGHT")       PORT_CODE(KEYCODE_3_PAD)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Joy(1) UP")          PORT_CODE(KEYCODE_5_PAD)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Joy(1) DOWN")        PORT_CODE(KEYCODE_2_PAD)

	PORT_START("KEY.8") /* [1] - port 3000 @ 8  not for the real machine, but to emulate the analog signal of the joystick */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RESET")             PORT_CODE(KEYCODE_ESC)      PORT_CHAR(27) // crashes the machine
		PORT_BIT(0x02, IP_ACTIVE_LOW,  IPT_KEYBOARD) PORT_NAME("Joy(0) FIRE")       PORT_CODE(KEYCODE_TILDE)
		PORT_BIT(0x04, IP_ACTIVE_LOW,  IPT_KEYBOARD) PORT_NAME("Joy(1) FIRE")       PORT_CODE(KEYCODE_PLUS_PAD)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Pot(0)+") PORT_CODE(KEYCODE_INSERT)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Pot(0)-") PORT_CODE(KEYCODE_DEL)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Pot(1)+") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Pot(1)-") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END))
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( interact )
	PORT_INCLUDE( hec2hrp )
	PORT_MODIFY("KEY.1")
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)     PORT_CHAR('/')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)    PORT_CHAR('.')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)        PORT_CHAR(',')
INPUT_PORTS_END

MACHINE_RESET_MEMBER(hec2hrp_state,interact)
{
	hector_reset(0, 0);
}

MACHINE_START_MEMBER(hec2hrp_state,hec2hrp)
{
	hector_init();
}

MACHINE_RESET_MEMBER(hec2hrp_state,hec2hrp)
{
	// Machines init
	hector_reset(1, 0);
}

MACHINE_START_MEMBER(hec2hrp_state,hec2hrx)
{
	uint8_t *r = m_ram->pointer();
	//Patch rom possible !
	//RAMD2[0xff6b] = 0xff; // force verbose mode

	// Memory install for bank switching
	m_bank[1]->configure_entry(HECTOR_BANK_PROG, r+0x10000);
	m_bank[1]->configure_entry(HECTOR_BANK_VIDEO, m_hector_vram); // Video RAM

	// Set bank HECTOR_BANK_PROG as basic bank
	m_bank[1]->set_entry(HECTOR_BANK_PROG);

	// MX-specific
	m_bank[2]->configure_entry(HECTORMX_BANK_PAGE0, m_rom);
	m_bank[2]->configure_entry(HECTORMX_BANK_PAGE1, memregion("page1")->base() ); // ROM page 1
	m_bank[2]->configure_entry(HECTORMX_BANK_PAGE2, memregion("page2")->base() ); // ROM page 2
	m_bank[2]->set_entry(HECTORMX_BANK_PAGE0);

	// Disk II-specific
	m_bank[3]->configure_entry(DISCII_BANK_ROM, memregion("rom_disc2")->base() ); // ROM
	m_bank[3]->configure_entry(DISCII_BANK_RAM, r); // RAM
	m_bank[3]->set_entry(DISCII_BANK_ROM);

	hector_init();
}
/*****************************************************************************/
MACHINE_START_MEMBER(hec2hrp_state,hec2mdhrx)
/*****************************************************************************/
//minidisc
{
	uint8_t *r = m_ram->pointer();

	// Memory install for bank switching
	m_bank[1]->configure_entry(HECTOR_BANK_PROG, r+0x10000);
	m_bank[1]->configure_entry(HECTOR_BANK_VIDEO, m_hector_vram); // Video RAM

	// Set HECTOR_BANK_PROG as basic bank
	m_bank[1]->set_entry(HECTOR_BANK_PROG);
	//Here, bank 5 is not used for the language switch but for the floppy ROM

	// Mini disk-specific
	m_bank[2]->configure_entry(HECTOR_BANK_BASE, m_rom); // ROM base page
	m_bank[2]->configure_entry(HECTOR_BANK_DISC, memregion("page2")->base() ); // ROM mini disc page
	m_bank[2]->set_entry(HECTOR_BANK_BASE);

	hector_init();
}

MACHINE_RESET_MEMBER(hec2hrp_state,hec2hrx)
{
	// Hector Memory
	m_bank[1]->set_entry(HECTOR_BANK_PROG);
	m_bank[2]->set_entry(HECTORMX_BANK_PAGE0);

	// DISK II Memory
	m_bank[3]->set_entry(DISCII_BANK_ROM);

	hector_reset(1, 1);
	hector_disc2_reset();
}

// Mini disk
MACHINE_RESET_MEMBER(hec2hrp_state,hec2mdhrx)
{
	// Hector Memory
	m_bank[1]->set_entry(HECTOR_BANK_PROG);
	m_bank[2]->set_entry(HECTORMX_BANK_PAGE0);

	hector_reset(1, 0);
}

void hec2hrp_state::interact_common(machine_config &config)
{
	MCFG_MACHINE_RESET_OVERRIDE(hec2hrp_state,interact)
	MCFG_MACHINE_START_OVERRIDE(hec2hrp_state,hec2hrp)

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(256, 79);
	screen.set_visarea(0, 112, 0, 77);
	screen.set_screen_update(FUNC(hec2hrp_state::screen_update_interact));
	screen.set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(hec2hrp_state::init_palette), 16);  /* 8 colours, but only 4 at a time*/

	hector_audio(config);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(hector_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("interact_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("interact");

	/* printer */
	PRINTER(config, m_printer, 0);
}

void hec2hrp_state::interact(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, XTAL(2'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &hec2hrp_state::interact_mem);
	m_maincpu->set_periodic_int(FUNC(hec2hrp_state::irq0_line_hold), attotime::from_hz(50));

	interact_common(config);
}

void hec2hrp_state::hector1(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(1'750'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &hec2hrp_state::interact_mem);
	m_maincpu->set_periodic_int(FUNC(hec2hrp_state::irq0_line_hold), attotime::from_hz(50));

	interact_common(config);
}


// mini disk interface

void hec2hrp_state::minidisc_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_HMD_FORMAT);
}

static void minidisc_floppies(device_slot_interface &device)
{
	device.option_add("dd", FLOPPY_35_DD);
}

static void hector_floppies(device_slot_interface &device)
{
	device.option_add("525hd", FLOPPY_525_HD);
}


void hec2hrp_state::hec2hr(machine_config &config)
{
	Z80(config, m_maincpu, 5_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &hec2hrp_state::hec2hrp_mem);
	m_maincpu->set_addrmap(AS_IO, &hec2hrp_state::hec2hrp_io);
	m_maincpu->set_periodic_int(FUNC(hec2hrp_state::irq0_line_hold), attotime::from_hz(50)); /*  put on the Z80 irq in Hz*/

	MCFG_MACHINE_RESET_OVERRIDE(hec2hrp_state,hec2hrp)
	MCFG_MACHINE_START_OVERRIDE(hec2hrp_state,hec2hrp)

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(400)); /* 2500 not accurate */
	screen.set_size(512, 230);
	screen.set_visarea(0, 243, 0, 227);
	screen.set_screen_update(FUNC(hec2hrp_state::screen_update_hec2hrp));
	screen.set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(hec2hrp_state::init_palette), 16);  /* 8 colours, but only 4 at a time*/

	hector_audio(config);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(hector_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("interact_cass");

	PRINTER(config, m_printer, 0);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("80K").set_default_value(0x00);

	SOFTWARE_LIST(config, "cass_list").set_original("interact");
}

void hec2hrp_state::hec2hrx(machine_config &config)
{
	hec2hr(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &hec2hrp_state::hec2hrx_mem);
	m_maincpu->set_addrmap(AS_IO, &hec2hrp_state::hec2hrx_io);

	Z80(config, m_disc2cpu, 4_MHz_XTAL);
	m_disc2cpu->set_addrmap(AS_PROGRAM, &hec2hrp_state::hecdisc2_mem);
	m_disc2cpu->set_addrmap(AS_IO, &hec2hrp_state::hecdisc2_io);

	UPD765A(config, m_upd_fdc, 8'000'000, false, true);
	m_upd_fdc->intrq_wr_callback().set(FUNC(hec2hrp_state::disc2_fdc_interrupt));
	m_upd_fdc->drq_wr_callback().set(FUNC(hec2hrp_state::disc2_fdc_dma_irq));

	FLOPPY_CONNECTOR(config, m_upd_connector[0], hector_floppies, "525hd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_upd_connector[1], hector_floppies, "525hd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	MCFG_MACHINE_RESET_OVERRIDE(hec2hrp_state,hec2hrx)
	MCFG_MACHINE_START_OVERRIDE(hec2hrp_state,hec2hrx)
}

void hec2hrp_state::hec2mx40(machine_config &config)
{
	hec2hrx(config);
	m_maincpu->set_addrmap(AS_IO, &hec2hrp_state::hec2mx40_io);
}

void hec2hrp_state::hec2mx80(machine_config &config)
{
	hec2hrx(config);
	m_maincpu->set_addrmap(AS_IO, &hec2hrp_state::hec2mx80_io);
}

void hec2hrp_state::hec2mdhrx(machine_config &config)
{
	hec2hr(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &hec2hrp_state::hec2hrx_mem);
	m_maincpu->set_addrmap(AS_IO, &hec2hrp_state::hec2mdhrx_io);

	MCFG_MACHINE_RESET_OVERRIDE(hec2hrp_state,hec2mdhrx)
	MCFG_MACHINE_START_OVERRIDE(hec2hrp_state,hec2mdhrx)

	/* 3.5" ("mini") disc */
	FD1793(config, m_minidisc_fdc, 1_MHz_XTAL);
	FLOPPY_CONNECTOR(config, "wd179x:0", minidisc_floppies, "dd", hec2hrp_state::minidisc_formats).enable_sound(true);
}


ROM_START( interact )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "interact.rom", 0x0000, 0x0800, CRC(1aa50444) SHA1(405806c97378abcf7c7b0d549430c78c7fc60ba2))
	// cartridge space 0800-0FFF, first byte must be 00.
ROM_END

ROM_START( hector1 )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "hector1.rom",  0x0000, 0x1000, CRC(3be6628b) SHA1(1c106d6732bed743d8283d39e5b8248271f18c42))
ROM_END

ROM_START( hec2hr )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "2hr.bin", 0x0000, 0x1000, CRC(84b9e672) SHA1(8c8b089166122eee565addaed10f84c5ce6d849b))
	// option roms
	ROM_REGION( 0x4000, "page1", ROMREGION_ERASEFF )
	ROM_REGION( 0x4000, "page2", ROMREGION_ERASEFF )
ROM_END

ROM_START( victor )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "victor.rom",  0x0000, 0x1000, CRC(d1e9508f) SHA1(d0f1bdcd39917fafc8859223ab38eee2a7dc85ff))
	// option roms
	ROM_REGION( 0x4000, "page1", ROMREGION_ERASEFF )
	ROM_REGION( 0x4000, "page2", ROMREGION_ERASEFF )
ROM_END

ROM_START( hec2hrp )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "hector2hrp.rom", 0x0000, 0x4000, CRC(983f52e4) SHA1(71695941d689827356042ee52ffe55ce7e6b8ecd))
	// option roms
	ROM_REGION( 0x4000, "page1", ROMREGION_ERASEFF )
	ROM_REGION( 0x4000, "page2", ROMREGION_ERASEFF )
	// 2nd cpu
	ROM_REGION( 0x04000, "rom_disc2", ROMREGION_ERASEFF )
ROM_END

ROM_START( hec2hrx )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "hector2hrx.rom", 0x0000, 0x4000, CRC(f047c521) SHA1(744336b2acc76acd7c245b562bdc96dca155b066))
	// option roms
	ROM_REGION( 0x4000, "page1", ROMREGION_ERASEFF )
	ROM_REGION( 0x4000, "page2", ROMREGION_ERASEFF )
	// 2nd cpu
	ROM_REGION( 0x04000, "rom_disc2", ROMREGION_ERASEFF )
	ROM_LOAD( "d800k.bin" , 0x0000,0x1000, CRC(831bd584) SHA1(9782ee58f570042608d9d568b2c3fc4c6d87d8b9))
ROM_END

// minidisc
ROM_START( hec2mdhrx )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "mdic1.bin" , 0x0000,0x2000, CRC(ddda1065) SHA1(e7bba14a72605238d2f8299da029b8320a563254))
	ROM_LOAD( "mdicmb.bin" , 0x2000,0x2000, CRC(d8090747) SHA1(f2925b68002307562e2ea5e36b740e5458f0f0eb))

	ROM_REGION( 0x4000, "page2", ROMREGION_ERASEFF )  // Page 2 = minidisc page
	ROM_LOAD( "mdic3.bin"  , 0x0000,0x2000, CRC(87801816) SHA1(ddf441f40df014b237cdf17430d1989f3a452d04))
	ROM_LOAD( "mdicmb.bin" , 0x2000,0x2000, CRC(d8090747) SHA1(f2925b68002307562e2ea5e36b740e5458f0f0eb))
ROM_END

ROM_START( hec2mx80 )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "mx80c_page0.rom" , 0x0000,0x4000, CRC(a75945cf) SHA1(542391e482271be0997b069cf13c8b5dae28feec))
	// option roms
	ROM_REGION( 0x4000, "page1", ROMREGION_ERASEFF )
	ROM_LOAD( "mx80c_page1.rom", 0x0000, 0x4000, CRC(4615f57c) SHA1(5de291bf3ae0320915133b99f1a088cb56c41658))

	ROM_REGION( 0x4000, "page2", ROMREGION_ERASEFF )
	ROM_LOAD( "mx80c_page2.rom" , 0x0000,0x4000, CRC(2d5d975e) SHA1(48307132e0f3fad0262859bb8142d108f694a436))
	// 2nd cpu
	ROM_REGION( 0x04000, "rom_disc2", ROMREGION_ERASEFF )
	ROM_LOAD( "d800k.bin" , 0x0000,0x1000, CRC(831bd584) SHA1(9782ee58f570042608d9d568b2c3fc4c6d87d8b9))
ROM_END

ROM_START( hec2mx40 )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "mx40c_page0.rom" , 0x0000,0x4000, CRC(9bb5566d) SHA1(0c8c2e396ec8eb995d2b621abe06b6968ca5d0aa))
	// option roms
	ROM_REGION( 0x4000, "page1", ROMREGION_ERASEFF )
	ROM_LOAD( "mx40c_page1.rom", 0x0000, 0x4000, CRC(192a76fa) SHA1(062aa6df0b554b85774d4b5edeea8496a4baca35))

	ROM_REGION( 0x4000, "page2", ROMREGION_ERASEFF )
	ROM_LOAD( "mx40c_page2.rom" , 0x0000,0x4000, CRC(ef1b2654) SHA1(66624ea040cb7ede4720ad2eca0738d0d3bad89a))
	// 2nd cpu
	ROM_REGION( 0x04000, "rom_disc2", ROMREGION_ERASEFF )
//  ROM_LOAD( "d360k.bin" , 0x0000,0x4000, CRC(2454eacb) SHA1(dc0d5a7d5891a7e422d9d142a2419527bb15dfd5))
	ROM_LOAD( "d800k.bin" , 0x0000,0x1000, CRC(831bd584) SHA1(9782ee58f570042608d9d568b2c3fc4c6d87d8b9))
//  ROM_LOAD( "d200k.bin" , 0x0000,0x4000, CRC(e2801377) SHA1(0926df5b417ecd8013e35c71b76780c5a25c1cbf))
ROM_END

/* Driver */

/*  YEAR   NAME       PARENT    COMPAT    MACHINE    INPUT    CLASS           INIT            COMPANY        FULLNAME                  FLAGS */
COMP(1979, interact,  0,        0,        interact,  interact,hec2hrp_state,  init_interact, "Interact Electronics",   "Interact Family Computer", MACHINE_IMPERFECT_SOUND)
COMP(1983, hector1,   interact, 0,        hector1,   hec2hrp, hec2hrp_state,  init_interact, "Micronique", "Hector 1",               MACHINE_IMPERFECT_SOUND)
COMP(1983, hec2hrp,   0,        interact, hec2hr,    hec2hrp, hec2hrp_state,  init_interact, "Micronique", "Hector 2HR+",            MACHINE_IMPERFECT_SOUND)
COMP(1980, victor,    hec2hrp,  0,        hec2hr,    hec2hrp, hec2hrp_state,  init_victor,   "Micronique", "Victor",                 MACHINE_IMPERFECT_SOUND)
COMP(1983, hec2hr,    hec2hrp,  0,        hec2hr,    hec2hrp, hec2hrp_state,  init_victor,   "Micronique", "Hector 2HR",             MACHINE_IMPERFECT_SOUND)
COMP(1984, hec2hrx,   hec2hrp,  0,        hec2hrx,   hec2hrp, hec2hrp_state,  init_hrx,      "Micronique", "Hector HRX + Disc2",     MACHINE_IMPERFECT_SOUND)
COMP(1985, hec2mdhrx, hec2hrp,  0,        hec2mdhrx, hec2hrp, hec2hrp_state,  init_mdhrx,    "Micronique", "Hector HRX + mini Disc", MACHINE_IMPERFECT_SOUND)
COMP(1985, hec2mx80,  hec2hrp,  0,        hec2mx80,  hec2hrp, hec2hrp_state,  init_mx40,     "Micronique", "Hector MX 80c + Disc2",  MACHINE_IMPERFECT_SOUND)
COMP(1985, hec2mx40,  hec2hrp,  0,        hec2mx40,  hec2hrp, hec2hrp_state,  init_mx40,     "Micronique", "Hector MX 40c + Disc2",  MACHINE_IMPERFECT_SOUND)



hektor.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/**********************************************************************

    Open University Hektor

    Hektor usage notes:
    - G197B - tune machine (requires PT502 Peripheral Board)

    Hektor 2 usage notes:
    - G1B38 - tune machine (via TV speaker and PT502 Peripheral Board)

    Hektor/Hektor 2 monitor commands:
    - E - execute editor
    - G - (go to) execute from addr
    - T - test subsystems
    - H - (high-level language) enter BASIC
    - W - (warm start) re-enter BASIC

    Hektor III usage notes:
    - t  - system test
    - a1 - audio test (plays The Entertainer)
    - a3 - graphic test
    - ay - run analyser software in ROM1
    - az - run analyser software in ROM2


    Known PT502 Modules:

        ME83A - Memory Expansion Module:

            RAM: 6166/400ns
            IC6 4000 - 47FF
            IC5 4800 - 4FFF
            IC4 5000 - 57FF
            IC3 5800 - 5FFF

            EPROM: 2732/400ns
            IC2 C000
            IC1 D000

        EP83 - Eprom Programmer:

            Hektor 1:
            Read 2716 into RAM  ... G1BBC
            Write 2716 from RAM ... G1BBF
            Read 2732 into RAM  ... G1ACD
            Write 2732 from RAM ... G1AD0

            Hektor 1 RAM area 3000-3FFF

            Hektor 2:
            Read 2732 into RAM  ... G1EA7
            Write 2732 from RAM ... G1EAA

            Hektor 2 RAM area 3000-3FFF

        IO83 - Input/Output Prototyping Module:

            8155 Register Addresses:
            1. CS Register.........40H
            2. PA Register.........41H
            3. PB Register.........42H
            4. PC Register.........43H
            5. Timer Low Byte......44H
            6. Timer High Byte.....45H

            Notes:
            1. The CS register sets the
               port configuration:
               Starts and stops the timer.
            2. Bits 0 to 13 in timer register
               set count length; bits 14,15
               set timer mode.


    TODO:
    - Fix Hektor cassette interface, test with T.
    - Add Expansion Bus and Peripheral Boards (probably not useful).

**********************************************************************/


#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8155.h"
#include "machine/i8255.h"
#include "sound/spkrdev.h"
#include "video/ef9364.h"
#include "video/mc6845.h"
#include "imagedev/cassette.h"
#include "bus/rs232/rs232.h"
#include "speaker.h"
#include "emupal.h"
#include "screen.h"


namespace {

class hektor_base_state : public driver_device
{
public:
	hektor_base_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_i8155(*this, "i8155")
		, m_kbd(*this, "KEY%u", 0)
		, m_screen(*this, "screen")
		, m_palette(*this, "palette")
		, m_speaker(*this, "speaker")
		, m_cassette(*this, "cassette")
		, m_rs232(*this, "rs232")
		, m_kbd_row(0)
		, m_motor(0)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(trigger_reset);
	DECLARE_INPUT_CHANGED_MEMBER(trigger_rst65);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void i8155_porta_w(uint8_t data);
	uint8_t i8155_portb_r();

	int sid_r();
	void sod_w(int state);

	required_device<i8085a_cpu_device> m_maincpu;
	required_device<i8155_device> m_i8155;
	required_ioport_array<8> m_kbd;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cassette;
	required_device<rs232_port_device> m_rs232;

	uint8_t m_kbd_row;
	bool m_motor;
};


class hektor_state : public hektor_base_state
{
public:
	hektor_state(const machine_config &mconfig, device_type type, const char *tag)
		: hektor_base_state(mconfig, type, tag)
		, m_ef9364(*this, "ef9364")
		, m_pt_i8155(*this, "pt_i8155")
		, m_ef9364_st(0)
	{ }

	void hektor(machine_config &config);
	void hektor2(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void hektor_mem(address_map &map) ATTR_COLD;
	void hektor_io(address_map &map) ATTR_COLD;
	void hektor2_mem(address_map &map) ATTR_COLD;

	void i8155_portc_w(uint8_t data);

	required_device<ef9364_device> m_ef9364;
	required_device<i8155_device> m_pt_i8155;

	int m_ef9364_st;
};


class hektor3_state : public hektor_base_state
{
public:
	hektor3_state(const machine_config &mconfig, device_type type, const char *tag)
		: hektor_base_state(mconfig, type, tag)
		, m_rom(*this, "maincpu")
		, m_ram(*this, "ram")
		, m_bank(*this, "bank")
		, m_hd6845(*this, "hd6845")
		, m_i8255(*this, "i8255")
	{ }

	void hektor3(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void hektor3_mem(address_map &map) ATTR_COLD;
	void hektor3_io(address_map &map) ATTR_COLD;

	void i8155_portc_w(uint8_t data);
	MC6845_UPDATE_ROW(crtc_update_row);

	required_memory_region m_rom;
	required_shared_ptr<uint8_t> m_ram;
	required_memory_bank m_bank;
	required_device<hd6845s_device> m_hd6845;
	required_device<i8255_device> m_i8255;
};


void hektor_base_state::i8155_porta_w(uint8_t data)
{
	m_kbd_row = data;
}

uint8_t hektor_base_state::i8155_portb_r()
{
	for (int col = 0; col < 8; col++)
	{
		if (!BIT(m_kbd_row, col)) return m_kbd[col]->read();
	}

	return 0xff;
}

void hektor_state::i8155_portc_w(uint8_t data)
{
	/* bit 0: EF9364 strobe ST */
	/* bit 1: EF9364 command C0 */
	/* bit 2: EF9364 command C1 */
	/* bit 3: EF9364 command C2 */
	if (!m_ef9364_st && BIT(data, 0))
		m_ef9364->command_w(data >> 1);
	m_ef9364_st = BIT(data, 0);

	/* bit 4: enable latch output */

	/* bit 5: cassette motor */
	m_motor = BIT(data, 5);
	m_cassette->change_state(m_motor ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
}

void hektor3_state::i8155_portc_w(uint8_t data)
{
	/* bit 2: audio out */
	m_speaker->level_w(BIT(data, 2));

	/* bit 3: cassette motor */
	m_motor = BIT(data, 3);
	m_cassette->change_state(m_motor ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);

	/* bit 5: ROM enable */
	if (BIT(data, 5))
		m_bank->set_base(m_rom->base());
	else
		m_bank->set_base(m_ram);
}


int hektor_base_state::sid_r()
{
	if (m_motor)
		return (m_cassette->input() > 0.0 ? 1 : 0);
	else
		return m_rs232->rxd_r();
}

void hektor_base_state::sod_w(int state)
{
	/* tv out (confirmed for Hektor 2 only) */
	m_speaker->level_w(state);

	if (m_motor)
		m_cassette->output(state ? -1.0 : +1.0);
	else
		m_rs232->write_txd(state);
}


MC6845_UPDATE_ROW(hektor3_state::crtc_update_row)
{
	const pen_t *pen = m_palette->pens();

	for (int x = 0; x < x_count; x++)
	{
		uint16_t offset = (ra << 11) | ((ma + x) & 0x7ff);
		uint8_t data = m_ram[~offset & 0xffff];
		if (x == cursor_x) data ^= 0xff;

		bitmap.pix(y, x * 8 + 0) = pen[BIT(data, 7)];
		bitmap.pix(y, x * 8 + 1) = pen[BIT(data, 6)];
		bitmap.pix(y, x * 8 + 2) = pen[BIT(data, 5)];
		bitmap.pix(y, x * 8 + 3) = pen[BIT(data, 4)];
		bitmap.pix(y, x * 8 + 4) = pen[BIT(data, 3)];
		bitmap.pix(y, x * 8 + 5) = pen[BIT(data, 2)];
		bitmap.pix(y, x * 8 + 6) = pen[BIT(data, 1)];
		bitmap.pix(y, x * 8 + 7) = pen[BIT(data, 0)];
	}
}


void hektor_state::hektor_mem(address_map &map)
{
	map(0x0000, 0x0fff).mirror(0x8000).rom().region("maincpu", 0x0000); // skt0
	map(0x1000, 0x1fff).mirror(0x8000).rom().region("maincpu", 0x1000); // skt1
	map(0x2800, 0x2bff).mirror(0x8000).w(m_ef9364, FUNC(ef9364_device::char_latch_w));
	map(0x2c00, 0x2cff).mirror(0x0300).rw(m_i8155, FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w));
	map(0x3000, 0x33ff).mirror(0x8000).ram(); // skt2
	map(0x3400, 0x37ff).mirror(0x8000).ram(); // skt3
	map(0x3800, 0x3bff).mirror(0x8000).ram(); // skt4
	map(0x3c00, 0x3fff).mirror(0x8000).ram(); // skt5
	map(0x4000, 0x40ff).mirror(0x8000).rw(m_pt_i8155, FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w)); // pt502 peripheral board
	map(0xac00, 0xac07).mirror(0x03f8).rw(m_i8155, FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
}

void hektor_state::hektor_io(address_map &map)
{
	// pt502 peripheral board - PA = unknown, PB = b4 sound, PC = unknown
	map(0x40, 0x47).rw(m_pt_i8155, FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));

	// tm222 peripheral board - PA = board switches, PB = board leds, PC = board pushbuttons
	//map(0x80, 0x83).rw(m_tm_i8255, FUNC(i8255_device::read), FUNC(i8255_device::write));
}


void hektor_state::hektor2_mem(address_map &map)
{
	map(0x0000, 0x0fff).mirror(0x8000).rom().region("maincpu", 0x0000); // skt0
	map(0x1000, 0x1fff).mirror(0x8000).rom().region("maincpu", 0x1000); // skt1
	map(0x2000, 0x23ff).mirror(0x8000).w(m_ef9364, FUNC(ef9364_device::char_latch_w));
	map(0x2400, 0x24ff).mirror(0x0300).rw(m_i8155, FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w));
	map(0x2800, 0x2fff).mirror(0x8000).ram(); // skt3
	map(0x3000, 0x37ff).mirror(0x8000).ram(); // skt4
	map(0x3800, 0x3fff).mirror(0x8000).ram(); // skt5
	map(0x4000, 0x40ff).mirror(0x8000).rw(m_pt_i8155, FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w)); // pt502 peripheral board
	map(0x7000, 0x7fff).mirror(0x8000).rom().region("maincpu", 0x2000); // skt2
	map(0xa400, 0xa407).mirror(0x03f8).rw(m_i8155, FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
}


void hektor3_state::hektor3_mem(address_map &map)
{
	map(0x0000, 0xffff).bankr("bank").writeonly().share("ram");
}

void hektor3_state::hektor3_io(address_map &map)
{
	map(0x00, 0x00).rw(m_hd6845, FUNC(hd6845s_device::status_r), FUNC(hd6845s_device::address_w));
	map(0x01, 0x01).rw(m_hd6845, FUNC(hd6845s_device::register_r), FUNC(hd6845s_device::register_w));
	map(0x08, 0x0f).rw(m_i8155, FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
	map(0x10, 0x13).rw(m_i8255, FUNC(i8255_device::read), FUNC(i8255_device::write));
}


static INPUT_PORTS_START(hektor)
	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Home") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(": *") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("; +") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')

	PORT_START("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')

	PORT_START("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("] }") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[ {") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\ |") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^ ~") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("- =") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 )") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 (") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')

	PORT_START("KEY7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 '") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 &") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 %") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 $") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 #") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 \"") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 !") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_ESC) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hektor_base_state::trigger_reset), 0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_TAB) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hektor_base_state::trigger_rst65), 0)
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(hektor_base_state::trigger_reset)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? CLEAR_LINE : ASSERT_LINE);
}

INPUT_CHANGED_MEMBER(hektor_base_state::trigger_rst65)
{
	m_maincpu->set_input_line(I8085_RST65_LINE, newval ? CLEAR_LINE : ASSERT_LINE);
}

static INPUT_PORTS_START(hektor3)
	PORT_INCLUDE(hektor)

	PORT_MODIFY("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Caps Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\ |") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))

	PORT_MODIFY("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@ `") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@') PORT_CHAR('`')

	PORT_MODIFY("KEY6")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Delete") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0 _") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('_')

	PORT_MODIFY("RESET")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hektor_base_state::trigger_reset), 0)
INPUT_PORTS_END


void hektor_base_state::machine_start()
{
	save_item(NAME(m_kbd_row));
	save_item(NAME(m_motor));
}

void hektor_state::machine_start()
{
	hektor_base_state::machine_start();

	save_item(NAME(m_ef9364_st));
}

void hektor3_state::machine_start()
{
	hektor_base_state::machine_start();

	m_bank->set_base(m_rom->base());
}

void hektor_base_state::machine_reset()
{
	m_maincpu->set_input_line(I8085_TRAP_LINE, CLEAR_LINE);
}


void hektor_state::hektor(machine_config &config)
{
	I8085A(config, m_maincpu, 6.144_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &hektor_state::hektor_mem);
	m_maincpu->set_addrmap(AS_IO, &hektor_state::hektor_io);
	m_maincpu->in_sid_func().set(FUNC(hektor_state::sid_r));
	m_maincpu->out_sod_func().set(FUNC(hektor_state::sod_w));
	m_maincpu->set_clk_out("i8155", FUNC(i8155_device::set_unscaled_clock_int));

	I8155(config, m_i8155, 6.144_MHz_XTAL / 2);
	m_i8155->out_pa_callback().set(FUNC(hektor_state::i8155_porta_w));
	m_i8155->in_pb_callback().set(FUNC(hektor_state::i8155_portb_r));
	m_i8155->out_pc_callback().set(FUNC(hektor_state::i8155_portc_w));
	m_i8155->out_to_callback().set_inputline(m_maincpu, I8085_TRAP_LINE);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_DISABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->cts_handler().set_inputline(m_maincpu, I8085_RST55_LINE);

	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);

	SPEAKER(config, "mono").front_center();

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_screen_update("ef9364", FUNC(ef9364_device::screen_update));
	m_screen->set_size(64 * 8, 16 * (8 + 4));
	m_screen->set_visarea(0, 64 * 8 - 1, 0, 16 * (8 + 4) - 1);

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	EF9364(config, m_ef9364, 6.144_MHz_XTAL / 6);
	m_ef9364->set_screen(m_screen);
	m_ef9364->set_palette_tag("palette");
	m_ef9364->set_nb_of_pages(1);
	m_ef9364->set_erase(0x20);

	// pt502 peripheral board
	I8155(config, m_pt_i8155, 6.144_MHz_XTAL / 2);
	m_pt_i8155->out_pb_callback().set(m_speaker, FUNC(speaker_sound_device::level_w)).bit(4);
}


void hektor_state::hektor2(machine_config &config)
{
	hektor(config);

	m_maincpu->set_clock(6.048_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &hektor_state::hektor2_mem);
	m_maincpu->set_addrmap(AS_IO, &hektor_state::hektor_io);

	m_ef9364->set_clock(6.048_MHz_XTAL / 6);
}


void hektor3_state::hektor3(machine_config &config)
{
	I8085A(config, m_maincpu, 16_MHz_XTAL / 2); // TODO: divider guessed
	m_maincpu->set_addrmap(AS_PROGRAM, &hektor3_state::hektor3_mem);
	m_maincpu->set_addrmap(AS_IO, &hektor3_state::hektor3_io);
	m_maincpu->in_sid_func().set(FUNC(hektor3_state::sid_r));
	m_maincpu->out_sod_func().set(FUNC(hektor3_state::sod_w));
	m_maincpu->set_clk_out("i8155", FUNC(i8155_device::set_unscaled_clock_int));

	I8155(config, m_i8155, 16_MHz_XTAL / 4);
	m_i8155->out_pa_callback().set(FUNC(hektor3_state::i8155_porta_w));
	m_i8155->in_pb_callback().set(FUNC(hektor3_state::i8155_portb_r));
	m_i8155->out_pc_callback().set(FUNC(hektor3_state::i8155_portc_w));
	m_i8155->out_to_callback().set_inputline(m_maincpu, I8085_TRAP_LINE);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_DISABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->cts_handler().set_inputline(m_maincpu, I8085_RST55_LINE);

	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);

	SPEAKER(config, "mono").front_center();

	I8255(config, m_i8255); // I/O Port

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(16_MHz_XTAL, 1024, 0, 640, 312, 0, 240);
	m_screen->set_screen_update("hd6845", FUNC(hd6845s_device::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	HD6845S(config, m_hd6845, 16_MHz_XTAL / 8);
	m_hd6845->set_screen(m_screen);
	m_hd6845->set_show_border_area(false);
	m_hd6845->set_char_width(8);
	m_hd6845->set_update_row_callback(FUNC(hektor3_state::crtc_update_row));
}


ROM_START(hektor)
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("hek1a_skt0.rom", 0x0000, 0x1000, CRC(cadf4d57) SHA1(ffcecac16cc8cf12e65acb359c3dfe26b06e43d1))
	ROM_LOAD("hek1a_skt1.rom", 0x1000, 0x1000, CRC(dbcaa026) SHA1(fa83e65f6125a3ef2a219ef69a3dd939888cb545))

	ROM_REGION(0x800, "ef9364", 0)
	ROM_LOAD("hek1a.ic5", 0x0000, 0x0800, CRC(a04e2061) SHA1(01d95d7e2f62d9c1b952888133e6b0dedf971987))
ROM_END

ROM_START(hektor2)
	ROM_REGION(0x3000, "maincpu", 0)
	ROM_LOAD("hek2_skt0.rom", 0x0000, 0x1000, CRC(2cc5cf07) SHA1(ce40a594be17532e636a7cbcc75c7d18377f3dca))
	ROM_LOAD("hek2_skt1.rom", 0x1000, 0x1000, CRC(b2388185) SHA1(d084df17aa5e333831fa94e02699673b088ee5ca))
	ROM_LOAD("hek2_skt2.rom", 0x2000, 0x1000, CRC(805c62a4) SHA1(f698d78274133124ee9f5420a5b5142b84a22922))

	ROM_REGION(0x800, "ef9364", 0)
	ROM_LOAD("hek2.ic5", 0x0000, 0x0800, CRC(1e9ec285) SHA1(f1cbbff8460d69b231e41e0779f2c3572982051a))
ROM_END

ROM_START(hektor3)
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD("system.rom0",   0x0000, 0x4000, CRC(bfc28204) SHA1(506dd25cbd83a95f53d61d611eb82aea0d80900b))
	ROM_LOAD("fra17_ay.rom1", 0x4000, 0x2000, CRC(e1e0d26e) SHA1(8b31c08896b00c018911b20b6ba7375befcaf320))
	ROM_RELOAD(               0x6000, 0x2000)
	ROM_LOAD("fra24_az.rom2", 0x8000, 0x4000, CRC(5cbf89d6) SHA1(b4a94eb0ba548e281c24ff118ddaca4fe66802fa))
ROM_END

} // anonymous namespace


/*    YEAR  NAME     PARENT   COMPAT   MACHINE   INPUT     CLASS           INIT        COMPANY                 FULLNAME             FLAGS */
COMP( 1981, hektor,  hektor2, 0,       hektor,   hektor,   hektor_state,   empty_init, "The Open University",  "Hektor",            0 )
COMP( 1982, hektor2, 0,       0,       hektor2,  hektor,   hektor_state,   empty_init, "The Open University",  "Hektor II",         0 )
COMP( 1984, hektor3, 0,       0,       hektor3,  hektor3,  hektor3_state,  empty_init, "The Open University",  "Hektor III",        0 )



hh_cop400.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

National Semiconductor COPS(COP400 MCU series) handhelds or other simple
devices, mostly LED electronic games/toys.

TODO:
- minspace: Add graphics overlay mask? There's a commercial with B&W footage
  of what's probably an older prototype, and there's an advertisement with a
  mock-up picture for the display.
- vidchal: Add screen and gun cursor with brightness detection callback,
  and softwarelist for the video tapes. We'd also need a VHS player device.
  The emulated lightgun itself appears to be working fine(eg. add a 30hz
  timer to IN.3 to score +100)
- solution release year, most chips on the PCB were from 1984, but this one
  was dumped from a licensed product branded for the VWR company, so it
  could be later than the initial release

*******************************************************************************/

#include "emu.h"

#include "cpu/cop400/cop400.h"
#include "machine/timer.h"
#include "sound/spkrdev.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "bshipg.lh"
#include "comparca.lh"
#include "ctstein.lh"
#include "einvaderc.lh"
#include "funjacks.lh"
#include "funrlgl.lh"
#include "funtag.lh"
#include "h2hbaskbc.lh"
#include "h2hhockeyc.lh"
#include "h2hsoccerc.lh"
#include "lafootb.lh"
#include "lchicken.lh"
#include "lightfgt.lh"
#include "lilcomp.lh"
#include "mbaskb2.lh"
#include "mdallas.lh"
#include "minspace.lh"
#include "msoccer2.lh"
#include "qkracera.lh"
#include "scat.lh"
#include "vidchal.lh"

//#include "hh_cop400_test.lh" // common test-layout - use external artwork


namespace {

class hh_cop400_state : public driver_device
{
public:
	hh_cop400_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_speaker(*this, "speaker"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(reset_button);
	DECLARE_INPUT_CHANGED_MEMBER(power_button);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// devices
	required_device<cop400_cpu_device> m_maincpu;
	optional_device<pwm_display_device> m_display;
	optional_device<speaker_sound_device> m_speaker;
	optional_ioport_array<6> m_inputs; // max 6

	u16 m_inp_mux = ~0; // multiplexed inputs mask

	// MCU output pin state
	u8 m_l = 0;         // port L
	u8 m_g = 0;         // port G
	u8 m_d = 0;         // port D
	int m_so = 0;       // SO line
	int m_sk = 0;       // SK line

	u16 read_inputs(int columns, u16 colmask = ~0);
	void set_power(bool state);
};


// machine start/reset

void hh_cop400_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_l));
	save_item(NAME(m_g));
	save_item(NAME(m_d));
	save_item(NAME(m_so));
	save_item(NAME(m_sk));
}

void hh_cop400_state::machine_reset()
{
	set_power(true);
}



/*******************************************************************************

  Helper Functions

*******************************************************************************/

// generic input handlers

u16 hh_cop400_state::read_inputs(int columns, u16 colmask)
{
	// active low
	u16 ret = ~0 & colmask;

	// read selected input rows
	for (int i = 0; i < columns; i++)
		if (!BIT(m_inp_mux, i))
			ret &= m_inputs[i]->read();

	return ret;
}

INPUT_CHANGED_MEMBER(hh_cop400_state::reset_button)
{
	// when an input is directly wired to MCU reset pin
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}

INPUT_CHANGED_MEMBER(hh_cop400_state::power_button)
{
	if (newval != field.defvalue())
		set_power((bool)param);
}

void hh_cop400_state::set_power(bool state)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? CLEAR_LINE : ASSERT_LINE);

	if (m_display && !state)
		m_display->clear();
}



/*******************************************************************************

  Minidrivers (subclass, I/O, Inputs, Machine Config, ROM Defs)

*******************************************************************************/

/*******************************************************************************

  Castle Toy Einstein
  * COP421 MCU label ~/927 COP421-NEZ/N
  * 4 lamps, 1-bit sound

  This is a Simon clone, the tones are not harmonic. Two models exist, each
  with a different batteries setup, assume they're same otherwise.

*******************************************************************************/

class ctstein_state : public hh_cop400_state
{
public:
	ctstein_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void ctstein(machine_config &config);

private:
	void write_g(u8 data);
	void write_l(u8 data);
	u8 read_l();
};

// handlers

void ctstein_state::write_g(u8 data)
{
	// G0-G2: input mux
	m_inp_mux = data & 7;
}

void ctstein_state::write_l(u8 data)
{
	// L0-L3: button lamps
	m_display->matrix(1, data & 0xf);
}

u8 ctstein_state::read_l()
{
	// L4-L7: multiplexed inputs
	return read_inputs(3, 0xf) << 4 | 0xf;
}

// inputs

static INPUT_PORTS_START( ctstein )
	PORT_START("IN.0") // G0 port L
	PORT_CONFNAME( 0x0f, 0x01^0x0f, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x01^0x0f, "1" )
	PORT_CONFSETTING(    0x02^0x0f, "2" )
	PORT_CONFSETTING(    0x04^0x0f, "3" )
	PORT_CONFSETTING(    0x08^0x0f, "4" )

	PORT_START("IN.1") // G1 port L
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_START )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_SELECT ) PORT_NAME("Best Score")
	PORT_BIT( 0x0c, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN.2") // G2 port L
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Red Button")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Yellow Button")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Green Button")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Blue Button")
INPUT_PORTS_END

// config

void ctstein_state::ctstein(machine_config &config)
{
	// basic machine hardware
	COP421(config, m_maincpu, 850000); // approximation - RC osc. R=12K, C=100pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_4, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_g().set(FUNC(ctstein_state::write_g));
	m_maincpu->write_l().set(FUNC(ctstein_state::write_l));
	m_maincpu->write_sk().set(m_speaker, FUNC(speaker_sound_device::level_w));
	m_maincpu->read_l().set(FUNC(ctstein_state::read_l));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 4);
	config.set_default_layout(layout_ctstein);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( ctstein )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "cop421-nez_n", 0x0000, 0x0400, CRC(16148e03) SHA1(b2b74891d36813d9a1eefd56a925054997c4b7f7) ) // 2nd half empty
ROM_END





/*******************************************************************************

  Coleco Head to Head: Electronic Basketball/Hockey/Soccer (model 2150/2160/2170)
  * COP420L MCU label COP420L-NEZ/N
  * 2-digit 7seg display, 41 other leds, 1-bit sound

  3 Head to Head games were released using this MCU/ROM. They play very much
  the same, only differing on game time. The PCB is pre-configured on G1+IN2
  and IN3 to select the game.

  An earlier revision of this runs on TMS1000, see hh_tms1k.cpp driver. Model
  numbers are the same. From the outside, an easy way to spot the difference is
  the Start/Display button: TMS1000 version button label is D, COP420 one is a *.
  The COP420 version also plays much slower.

*******************************************************************************/

class h2hbaskbc_state : public hh_cop400_state
{
public:
	h2hbaskbc_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void h2hsoccerc(machine_config &config);
	void h2hbaskbc(machine_config &config);
	void h2hhockeyc(machine_config &config);

private:
	void update_display();
	void write_d(u8 data);
	void write_g(u8 data);
	void write_l(u8 data);
	u8 read_in();
};

// handlers

void h2hbaskbc_state::update_display()
{
	// D2,D3 double as multiplexer
	u16 mask = ((~m_d >> 3 & 1) * 0x00ff) | ((~m_d >> 2 & 1) * 0xff00);
	u16 sel = m_g | m_d << 4;

	m_display->matrix((sel << 8 | sel) & mask, m_l);
}

void h2hbaskbc_state::write_d(u8 data)
{
	// D: led select
	m_d = data;
	update_display();
}

void h2hbaskbc_state::write_g(u8 data)
{
	// G: led select, input mux
	m_g = m_inp_mux = data;
	update_display();
}

void h2hbaskbc_state::write_l(u8 data)
{
	// L0-L6: digit segments A-G
	// L0-L4: led data
	m_l = data;
	update_display();
}

u8 h2hbaskbc_state::read_in()
{
	// IN: multiplexed inputs
	return read_inputs(4, 7) | (m_inputs[4]->read() & 8);
}

// inputs

static INPUT_PORTS_START( h2hbaskbc )
	PORT_START("IN.0") // G0 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_16WAY PORT_NAME("P1 Pass CW") // clockwise
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_16WAY PORT_NAME("P1 Pass CCW") // counter-clockwise
	PORT_CONFNAME( 0x04, 0x04, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x04, "1" )
	PORT_CONFSETTING(    0x00, "2" )

	PORT_START("IN.1") // G1 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("P1 Shoot")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_START ) PORT_NAME("Start/Display")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_CUSTOM ) // factory set

	PORT_START("IN.2") // G2 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL PORT_16WAY PORT_NAME("P2 Defense Right")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL PORT_16WAY PORT_NAME("P2 Defense Left")
	PORT_CONFNAME( 0x04, 0x04, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x04, "1" )
	PORT_CONFSETTING(    0x00, "2" )

	PORT_START("IN.3") // G3 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_CONFNAME( 0x04, 0x04, "Factory Test" )
	PORT_CONFSETTING(    0x04, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN.4") // IN3 (factory set)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM )
INPUT_PORTS_END

static INPUT_PORTS_START( h2hhockeyc )
	PORT_INCLUDE( h2hbaskbc )

	PORT_MODIFY("IN.3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_COCKTAIL PORT_NAME("P2 Goalie Right")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Goalie Left")

	PORT_MODIFY("IN.4")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_CUSTOM )
INPUT_PORTS_END

static INPUT_PORTS_START( h2hsoccerc )
	PORT_INCLUDE( h2hhockeyc )

	PORT_MODIFY("IN.1")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_CUSTOM )
INPUT_PORTS_END

// config

void h2hbaskbc_state::h2hbaskbc(machine_config &config)
{
	// basic machine hardware
	COP420(config, m_maincpu, 1000000); // approximation - RC osc. R=43K, C=100pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_d().set(FUNC(h2hbaskbc_state::write_d));
	m_maincpu->write_g().set(FUNC(h2hbaskbc_state::write_g));
	m_maincpu->write_l().set(FUNC(h2hbaskbc_state::write_l));
	m_maincpu->read_in().set(FUNC(h2hbaskbc_state::read_in));
	m_maincpu->write_so().set(m_speaker, FUNC(speaker_sound_device::level_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(16, 7);
	m_display->set_segmask(3, 0x7f);
	config.set_default_layout(layout_h2hbaskbc);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

void h2hbaskbc_state::h2hhockeyc(machine_config &config)
{
	h2hbaskbc(config);
	config.set_default_layout(layout_h2hhockeyc);
}

void h2hbaskbc_state::h2hsoccerc(machine_config &config)
{
	h2hbaskbc(config);
	config.set_default_layout(layout_h2hsoccerc);
}

// roms

ROM_START( h2hbaskbc )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "cop420l-nmy", 0x0000, 0x0400, CRC(87152509) SHA1(acdb869b65d49b3b9855a557ed671cbbb0f61e2c) )
ROM_END

#define rom_h2hhockeyc rom_h2hbaskbc // dumped from Basketball
#define rom_h2hsoccerc rom_h2hbaskbc // "





/*******************************************************************************

  Entex Space Invader
  * COP444L MCU label /B138 COPL444-HRZ/N INV II (die label HRZ COP 444L/A)
  * 3 7seg LEDs, LED matrix and overlay mask, 1-bit sound

  The first version was on TMS1100 (see hh_tms1k.cpp), this is the reprogrammed
  second release with a gray case instead of black.

*******************************************************************************/

class einvaderc_state : public hh_cop400_state
{
public:
	einvaderc_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void einvaderc(machine_config &config);

private:
	void update_display();
	void write_d(u8 data);
	void write_g(u8 data);
	void write_sk(int state);
	void write_so(int state);
	void write_l(u8 data);
};

// handlers

void einvaderc_state::update_display()
{
	u8 l = bitswap<8>(m_l,7,6,0,1,2,3,4,5);
	u16 grid = (m_d | m_g << 4 | m_sk << 8 | m_so << 9) ^ 0x0ff;

	m_display->matrix(grid, l);
}

void einvaderc_state::write_d(u8 data)
{
	// D: led grid 0-3 (D0-D2 are 7segs)
	m_d = data;
	update_display();
}

void einvaderc_state::write_g(u8 data)
{
	// G: led grid 4-7
	m_g = data;
	update_display();
}

void einvaderc_state::write_sk(int state)
{
	// SK: speaker out + led grid 8
	m_speaker->level_w(state);
	m_sk = state;
	update_display();
}

void einvaderc_state::write_so(int state)
{
	// SO: led grid 9
	m_so = state;
	update_display();
}

void einvaderc_state::write_l(u8 data)
{
	// L: led state/segment
	m_l = data;
	update_display();
}

// inputs

static INPUT_PORTS_START( einvaderc )
	PORT_START("IN.0") // port IN
	PORT_CONFNAME( 0x01, 0x01, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x01, "Amateur" )
	PORT_CONFSETTING(    0x00, "Professional" )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1 )
INPUT_PORTS_END

// config

void einvaderc_state::einvaderc(machine_config &config)
{
	// basic machine hardware
	COP444L(config, m_maincpu, 850000); // approximation - RC osc. R=47K, C=100pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->read_in().set_ioport("IN.0");
	m_maincpu->write_d().set(FUNC(einvaderc_state::write_d));
	m_maincpu->write_g().set(FUNC(einvaderc_state::write_g));
	m_maincpu->write_sk().set(FUNC(einvaderc_state::write_sk));
	m_maincpu->write_so().set(FUNC(einvaderc_state::write_so));
	m_maincpu->write_l().set(FUNC(einvaderc_state::write_l));

	// video hardware
	screen_device &mask(SCREEN(config, "mask", SCREEN_TYPE_SVG));
	mask.set_refresh_hz(60);
	mask.set_size(919, 1080);
	mask.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(10, 8);
	m_display->set_segmask(7, 0x7f);
	config.set_default_layout(layout_einvaderc);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( einvaderc )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "copl444-hrz_n_inv_ii", 0x0000, 0x0800, CRC(76400f38) SHA1(0e92ab0517f7b7687293b189d30d57110df20fe0) )

	ROM_REGION( 82104, "mask", 0)
	ROM_LOAD( "einvaderc.svg", 0, 82104, CRC(0013227f) SHA1(44a3ac48c947369231f010559331ad16fcbef7be) )
ROM_END





/*******************************************************************************

  LJN I Took a Lickin' From a Chicken
  * COP421 MCU label ~/005 COP421-NJC/N
  * 11 leds, 1-bit sound, motor to a chicken on a spring

  This toy includes 4 games: Tic Tac Toe, Chicken Sez, and Total Recall I/II.

  known releases:
  - USA: I Took a Lickin' From a Chicken, published by LJN
  - Japan: Professor Chicken's Genius Classroom 「にわとり博士の天才教室」, published by Bandai
  - Netherlands: Kip ik heb je, published by Smith Family Toys

*******************************************************************************/

class lchicken_state : public hh_cop400_state
{
public:
	lchicken_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag),
		m_motor_pos_out(*this, "motor_pos"),
		m_motor_on_out(*this, "motor_on")
	{ }

	void lchicken(machine_config &config);

	int motor_switch_r();

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	output_finder<> m_motor_pos_out;
	output_finder<> m_motor_on_out;

	u8 m_motor_pos = 0;
	TIMER_DEVICE_CALLBACK_MEMBER(motor_sim_tick);

	void write_l(u8 data);
	void write_d(u8 data);
	void write_g(u8 data);
	u8 read_g();
};

void lchicken_state::machine_start()
{
	hh_cop400_state::machine_start();

	m_motor_pos_out.resolve();
	m_motor_on_out.resolve();

	// register for savestates
	save_item(NAME(m_motor_pos));
}

// handlers

int lchicken_state::motor_switch_r()
{
	return m_motor_pos > 0xe8; // approximation
}

TIMER_DEVICE_CALLBACK_MEMBER(lchicken_state::motor_sim_tick)
{
	if (~m_inp_mux & 8)
	{
		m_motor_pos++;
		m_motor_pos_out = 100 * (m_motor_pos / (float)0x100);
	}
}

void lchicken_state::write_l(u8 data)
{
	// L0-L3: led data
	// L4-L6: led select
	// L7: N/C
	m_display->matrix(data >> 4 & 7, ~data & 0xf);
}

void lchicken_state::write_d(u8 data)
{
	// D0-D3: input mux
	// D3: motor on
	m_inp_mux = data;
	m_motor_on_out = ~data >> 3 & 1;
}

void lchicken_state::write_g(u8 data)
{
	m_g = data;
}

u8 lchicken_state::read_g()
{
	// G0-G3: multiplexed inputs
	return read_inputs(4, m_g);
}

// inputs

static INPUT_PORTS_START( lchicken )
	PORT_START("IN.0") // D0 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN.1") // D1 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN.2") // D2 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN.3") // D3 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(lchicken_state::motor_switch_r))
INPUT_PORTS_END

// config

void lchicken_state::lchicken(machine_config &config)
{
	// basic machine hardware
	COP421(config, m_maincpu, 850000); // approximation - RC osc. R=12K, C=100pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_4, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_l().set(FUNC(lchicken_state::write_l));
	m_maincpu->write_d().set(FUNC(lchicken_state::write_d));
	m_maincpu->write_g().set(FUNC(lchicken_state::write_g));
	m_maincpu->read_g().set(FUNC(lchicken_state::read_g));
	m_maincpu->write_so().set(m_speaker, FUNC(speaker_sound_device::level_w));
	m_maincpu->read_si().set(m_maincpu, FUNC(cop400_cpu_device::so_r));

	TIMER(config, "chicken_motor").configure_periodic(FUNC(lchicken_state::motor_sim_tick), attotime::from_msec(6000/0x100)); // ~6sec for a full rotation

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(3, 4);
	config.set_default_layout(layout_lchicken);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( lchicken )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "cop421-njc_n", 0x0000, 0x0400, CRC(319e7985) SHA1(9714327518f65ebefe38ac7911bed2b9b9c77307) )
ROM_END





/*******************************************************************************

  Mattel Funtronics: Jacks (model 1603)
  * COP410L MCU die bonded directly to PCB (die label COP410L/B NGS)
  * 8 LEDs, 1-bit sound

*******************************************************************************/

class funjacks_state : public hh_cop400_state
{
public:
	funjacks_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void funjacks(machine_config &config);

private:
	void update_display();
	void write_d(u8 data);
	void write_l(u8 data);
	void write_g(u8 data);
	u8 read_l();
	u8 read_g();
};

// handlers

void funjacks_state::update_display()
{
	m_display->matrix(m_d, m_l);
}

void funjacks_state::write_d(u8 data)
{
	// D: led grid + input mux
	m_inp_mux = data;
	m_d = ~data & 0xf;
	update_display();
}

void funjacks_state::write_l(u8 data)
{
	// L0,L1: led state
	m_l = data & 3;
	update_display();
}

void funjacks_state::write_g(u8 data)
{
	// G1: speaker out
	m_speaker->level_w(data >> 1 & 1);
	m_g = data;
}

u8 funjacks_state::read_l()
{
	// L4,L5: multiplexed inputs
	return read_inputs(3, 0x30) | m_l;
}

u8 funjacks_state::read_g()
{
	// G1: speaker out state
	// G2,G3: inputs
	return m_inputs[3]->read() | (m_g & 2);
}

// inputs

static INPUT_PORTS_START( funjacks )
	PORT_START("IN.0") // D0 port L
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON3 )

	PORT_START("IN.1") // D1 port L
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON5 )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON4 )

	PORT_START("IN.2") // D2 port L
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) // positioned at 1 o'clock on panel, increment clockwise
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON6 )

	PORT_START("IN.3") // port G
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_CUSTOM ) // speaker
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_START )
	PORT_CONFNAME( 0x08, 0x08, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x08, "1" )
	PORT_CONFSETTING(    0x00, "2" )
INPUT_PORTS_END

// config

void funjacks_state::funjacks(machine_config &config)
{
	// basic machine hardware
	COP410(config, m_maincpu, 850000); // approximation - RC osc. R=47K, C=56pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_d().set(FUNC(funjacks_state::write_d));
	m_maincpu->write_l().set(FUNC(funjacks_state::write_l));
	m_maincpu->write_g().set(FUNC(funjacks_state::write_g));
	m_maincpu->read_l().set(FUNC(funjacks_state::read_l));
	m_maincpu->read_g().set(FUNC(funjacks_state::read_g));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 2);
	config.set_default_layout(layout_funjacks);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( funjacks )
	ROM_REGION( 0x0200, "maincpu", 0 )
	ROM_LOAD( "cop410l_b_ngs", 0x0000, 0x0200, CRC(863368ea) SHA1(f116cc27ae721b3a3e178fa13765808bdc275663) )
ROM_END





/*******************************************************************************

  Mattel Funtronics: Red Light Green Light (model 1604)
  * COP410L MCU die bonded directly to PCB (die label COP410L/B NHZ)
  * 14 LEDs, 1-bit sound

  known releases:
  - USA: Funtronics: Red Light Green Light, published by Mattel
  - USA(rerelease): Funtronics: Hot Wheels Drag Race, published by Mattel

*******************************************************************************/

class funrlgl_state : public hh_cop400_state
{
public:
	funrlgl_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void funrlgl(machine_config &config);

private:
	void update_display();
	void write_d(u8 data);
	void write_l(u8 data);
	void write_g(u8 data);
};

// handlers

void funrlgl_state::update_display()
{
	m_display->matrix(m_d, m_l);
}

void funrlgl_state::write_d(u8 data)
{
	// D: led grid
	m_d = ~data & 0xf;
	update_display();
}

void funrlgl_state::write_l(u8 data)
{
	// L0-L3: led state
	// L4-L7: N/C
	m_l = ~data & 0xf;
	update_display();
}

void funrlgl_state::write_g(u8 data)
{
	// G3: speaker out
	m_speaker->level_w(data >> 3 & 1);
}

// inputs

static INPUT_PORTS_START( funrlgl )
	PORT_START("IN.0") // port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_CONFNAME( 0x04, 0x04, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x04, "1" )
	PORT_CONFSETTING(    0x00, "2" )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("RESET")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_cop400_state::reset_button), 0)
INPUT_PORTS_END

// config

void funrlgl_state::funrlgl(machine_config &config)
{
	// basic machine hardware
	COP410(config, m_maincpu, 800000); // approximation - RC osc. R=51K, C=91pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_d().set(FUNC(funrlgl_state::write_d));
	m_maincpu->write_l().set(FUNC(funrlgl_state::write_l));
	m_maincpu->read_l_tristate().set_constant(0xff);
	m_maincpu->write_g().set(FUNC(funrlgl_state::write_g));
	m_maincpu->read_g().set_ioport("IN.0");

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 4);
	m_display->set_bri_levels(0.005, 0.1); // top led is brighter
	config.set_default_layout(layout_funrlgl);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( funrlgl )
	ROM_REGION( 0x0200, "maincpu", 0 )
	ROM_LOAD( "cop410l_b_nhz", 0x0000, 0x0200, CRC(4065c3ce) SHA1(f0bc8125d922949e0d7ab1ba89c805a836d20e09) )
ROM_END





/*******************************************************************************

  Mattel Funtronics: Tag (model 1497)
  * COP410L MCU die bonded directly to PCB (die label COP410L/B GTJ)
  * 7 LEDs, 7 buttons, 1-bit sound

*******************************************************************************/

class funtag_state : public hh_cop400_state
{
public:
	funtag_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void funtag(machine_config &config);

private:
	void update_display();
	void write_d(u8 data);
	void write_l(u8 data);
	void write_g(u8 data);
	u8 read_l();
	u8 read_g();
};

// handlers

void funtag_state::update_display()
{
	m_display->matrix(m_d, m_l);
}

void funtag_state::write_d(u8 data)
{
	// D: led grid + input mux
	m_inp_mux = data;
	m_d = ~data & 0xf;
	update_display();
}

void funtag_state::write_l(u8 data)
{
	// L0,L1: led state
	m_l = data & 3;
	update_display();
}

void funtag_state::write_g(u8 data)
{
	// G2: speaker out
	m_speaker->level_w(data >> 2 & 1);
}

u8 funtag_state::read_l()
{
	// L2: difficulty switch
	return m_inputs[4]->read() | 8;
}

u8 funtag_state::read_g()
{
	// G0,G1: multiplexed inputs
	// G3: start button
	return read_inputs(3, 3) | m_inputs[3]->read() | 4;
}

// inputs

static INPUT_PORTS_START( funtag )
	PORT_START("IN.0") // D0 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON4 )

	PORT_START("IN.1") // D1 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON5 )

	PORT_START("IN.2") // D2 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON3 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON6 )

	PORT_START("IN.3") // port G
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START )

	PORT_START("IN.4") // port L
	PORT_CONFNAME( 0x04, 0x04, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x04, "1" )
	PORT_CONFSETTING(    0x00, "2" )
INPUT_PORTS_END

// config

void funtag_state::funtag(machine_config &config)
{
	// basic machine hardware
	COP410(config, m_maincpu, 1000000); // approximation - RC osc. R=47K, C=91pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_d().set(FUNC(funtag_state::write_d));
	m_maincpu->write_l().set(FUNC(funtag_state::write_l));
	m_maincpu->write_g().set(FUNC(funtag_state::write_g));
	m_maincpu->read_l().set(FUNC(funtag_state::read_l));
	m_maincpu->read_g().set(FUNC(funtag_state::read_g));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 2);
	config.set_default_layout(layout_funtag);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( funtag )
	ROM_REGION( 0x0200, "maincpu", 0 )
	ROM_LOAD( "cop410l_b_gtj", 0x0000, 0x0200, CRC(ce565da6) SHA1(34e5f39e32f220007d353c93787c1a6d117592c1) )
ROM_END





/*******************************************************************************

  Mattel Basketball 2 (model 1645), Soccer 2 (model 1642)
  * PCB label: MA6037/38
  * dual COP420L MCUs, dies bonded to PCB (see romdefs for rom serials)
  * 4001, die also bonded to PCB
  * 2-digit 7seg display, 36 other leds, 1-bit sound

  The clock generator was measured ~527kHz for mbaskb2, ~483kHz for msoccer2,
  meaning that the internal divider is 8. Main MCU SK connects to the other
  MCU CKO pin, probably for syncing serial I/O.

  These two are on the same hardware, see patents US4341383 and US4372556
  for detailed descriptions of the games.

*******************************************************************************/

class mbaskb2_state : public hh_cop400_state
{
public:
	mbaskb2_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag),
		m_subcpu(*this, "subcpu")
	{ }

	void mbaskb2(machine_config &config);
	void msoccer2(machine_config &config);

	ioport_value switch_r();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cop400_cpu_device> m_subcpu;

	attotime m_on_time;

	void update_display();
	void shared_write_l(u8 data);
	void main_write_g(u8 data);
	void sub_write_g(u8 data);
	void sub_write_d(u8 data);
	u8 sub_read_in();
};

void mbaskb2_state::machine_start()
{
	hh_cop400_state::machine_start();
	save_item(NAME(m_on_time));
}

void mbaskb2_state::machine_reset()
{
	hh_cop400_state::machine_reset();
	m_on_time = machine().time() + attotime::from_msec(5);
}

// handlers

void mbaskb2_state::update_display()
{
	m_display->matrix(~(m_d << 4 | m_g), m_l);
}

void mbaskb2_state::shared_write_l(u8 data)
{
	// L: led data (though it's unlikely that maincpu will write valid led data to it)
	m_l = m_maincpu->l_r() | m_subcpu->l_r();
	update_display();
}

void mbaskb2_state::main_write_g(u8 data)
{
	// G1: speaker out
	m_speaker->level_w(data >> 1 & 1);
}

void mbaskb2_state::sub_write_g(u8 data)
{
	// G: led select (low), input mux
	m_g = m_inp_mux = data & 0xf;
	update_display();
}

void mbaskb2_state::sub_write_d(u8 data)
{
	// D: led select (high)
	m_d = data;
	update_display();
}

u8 mbaskb2_state::sub_read_in()
{
	// IN: multiplexed inputs
	return read_inputs(3, 0xf);
}

// inputs

ioport_value mbaskb2_state::switch_r()
{
	// The power switch is off-1-2, and the game relies on power-on starting at 1,
	// otherwise msoccer2 boots up to what looks like a factory test mode.
	return (machine().time() < m_on_time && ~m_inputs[4]->read() & 1) ? 1 : (m_inputs[3]->read() & 1);
}

static INPUT_PORTS_START( mbaskb2 )
	PORT_START("IN.0") // G0 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_16WAY

	PORT_START("IN.1") // G1 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Pass")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Shoot")
	PORT_BIT( 0x0c, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN.2") // G2 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("Defense: Man")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("Defense: Zone")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("Defense: Press")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(mbaskb2_state::switch_r))

	PORT_START("IN.3")
	PORT_CONFNAME( 0x01, 0x01, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x00, "2" )

	PORT_START("IN.4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( msoccer2 )
	PORT_INCLUDE( mbaskb2 )

	PORT_MODIFY("IN.2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("Low/High Kick")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("Score")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("Teammate")

	PORT_MODIFY("IN.4")
	PORT_CONFNAME( 0x01, 0x00, "Factory Test" ) PORT_CONDITION("IN.3", 0x01, EQUALS, 0x00)
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
INPUT_PORTS_END

// config

void mbaskb2_state::mbaskb2(machine_config &config)
{
	// basic machine hardware
	COP420(config, m_maincpu, 500000); // approximation
	m_maincpu->set_config(COP400_CKI_DIVISOR_8, COP400_CKO_SYNC_INPUT, false); // guessed
	m_maincpu->write_g().set(FUNC(mbaskb2_state::main_write_g));
	m_maincpu->write_l().set(FUNC(mbaskb2_state::shared_write_l));
	m_maincpu->read_l().set(m_subcpu, FUNC(cop400_cpu_device::l_r));
	m_maincpu->read_l_tristate().set_constant(0x80);
	m_maincpu->read_si().set(m_subcpu, FUNC(cop400_cpu_device::so_r));

	COP420(config, m_subcpu, 500000); // same as maincpu
	m_subcpu->set_config(COP400_CKI_DIVISOR_8, COP400_CKO_SYNC_INPUT, false); // guessed
	m_subcpu->write_d().set(FUNC(mbaskb2_state::sub_write_d));
	m_subcpu->write_g().set(FUNC(mbaskb2_state::sub_write_g));
	m_subcpu->write_l().set(FUNC(mbaskb2_state::shared_write_l));
	m_subcpu->read_l().set(m_maincpu, FUNC(cop400_cpu_device::l_r));
	m_subcpu->read_l_tristate().set_constant(0x80);
	m_subcpu->read_in().set(FUNC(mbaskb2_state::sub_read_in));
	m_subcpu->read_si().set(m_maincpu, FUNC(cop400_cpu_device::so_r));
	m_subcpu->read_cko().set(m_maincpu, FUNC(cop400_cpu_device::sk_r));

	config.set_perfect_quantum(m_maincpu);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 7);
	m_display->set_segmask(0xc0, 0x7f);
	m_display->set_bri_levels(0.008, 0.04); // offense is brighter
	config.set_default_layout(layout_mbaskb2);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

void mbaskb2_state::msoccer2(machine_config &config)
{
	mbaskb2(config);
	config.set_default_layout(layout_msoccer2);
}

// roms

ROM_START( mbaskb2 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "cop420l_nmp", 0x0000, 0x0400, CRC(afc44378) SHA1(e96435bd1d0b2bea5140efdfe21f4684f2525075) )

	ROM_REGION( 0x0400, "subcpu", 0 )
	ROM_LOAD( "cop420l_nmq", 0x0000, 0x0400, CRC(70943f6f) SHA1(3f711d8b7c7c5dd13c68bf1ef980d2f784b748f4) )
ROM_END

ROM_START( msoccer2 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "cop420l_nnm", 0x0000, 0x0400, CRC(c9169aca) SHA1(525486e8a18ec6132e53f9be582b2667172230a9) )

	ROM_REGION( 0x0400, "subcpu", 0 )
	ROM_LOAD( "cop420l_nnk", 0x0000, 0x0400, CRC(a84dd5f4) SHA1(5d269816248319a2bca1708d5022af455d52682d) )
ROM_END





/*******************************************************************************

  Mattel Look Alive! Football (model 1998)
  * COP421L MCU die bonded directly to PCB (rom serial HCJ)
  * 2 7seg LEDs, LED matrix and overlay mask, 1-bit sound

  For a detailed description, see patent US4582323. 1st-person view versions
  for Baseball and Basketball were also announced, but not released.

*******************************************************************************/

class lafootb_state : public hh_cop400_state
{
public:
	lafootb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void lafootb(machine_config &config);

private:
	void update_display();
	void write_l(u8 data);
	void write_d(u8 data);
	u8 read_g();
};

// handlers

void lafootb_state::update_display()
{
	m_display->matrix(~m_d, m_l);
}

void lafootb_state::write_l(u8 data)
{
	// L: led data
	m_l = data;
	update_display();
}

void lafootb_state::write_d(u8 data)
{
	// D: led select, D2,D3: input mux
	m_d = data;
	m_inp_mux = data >> 2 & 3;
	update_display();
}

u8 lafootb_state::read_g()
{
	// G: multiplexed inputs
	return read_inputs(2, 7) | (m_inputs[2]->read() & 8);
}

// inputs

static INPUT_PORTS_START( lafootb )
	PORT_START("IN.0") // D2 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_16WAY PORT_NAME("Right / Home")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Kick / Yards to go")

	PORT_START("IN.1") // D3 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_16WAY PORT_NAME("Left / Visitors")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_16WAY PORT_NAME("Up / Time")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Pass / Status")

	PORT_START("IN.2") // G3
	PORT_CONFNAME( 0x08, 0x08, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x08, "1" )
	PORT_CONFSETTING(    0x00, "2" )
INPUT_PORTS_END

// config

void lafootb_state::lafootb(machine_config &config)
{
	// basic machine hardware
	COP421(config, m_maincpu, 900000); // approximation - RC osc. R=51K, C=100pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_l().set(FUNC(lafootb_state::write_l));
	m_maincpu->write_d().set(FUNC(lafootb_state::write_d));
	m_maincpu->read_g().set(FUNC(lafootb_state::read_g));
	m_maincpu->write_sk().set(m_speaker, FUNC(speaker_sound_device::level_w));

	// video hardware
	screen_device &mask(SCREEN(config, "mask", SCREEN_TYPE_SVG));
	mask.set_refresh_hz(60);
	mask.set_size(1920, 864);
	mask.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(4, 8);
	m_display->set_segmask(0x4, 0x7f);
	m_display->set_segmask(0x8, 0xff); // right digit has dp
	m_display->set_bri_levels(0.005);
	config.set_default_layout(layout_lafootb);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( lafootb )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "cop421l_hcj", 0x0000, 0x0400, CRC(a9cc1e94) SHA1(7a39f5a5f10b8a2bd72da3ff3f3fcfaad35ead5f) )

	ROM_REGION( 38608, "mask", 0)
	ROM_LOAD( "lafootb.svg", 0, 38608, CRC(35387445) SHA1(7cd9db170820fc84d47545c3db8d991b2c5f4f7f) )
ROM_END





/*******************************************************************************

  Mattel Dalla$ (J.R. handheld)
  * COP444L MCU label COP444L-HYN/N
  * 8-digit 7seg display, 1-bit sound

  This is a board game, only the handheld device is emulated here.

*******************************************************************************/

class mdallas_state : public hh_cop400_state
{
public:
	mdallas_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void mdallas(machine_config &config);

private:
	void update_display();
	void write_l(u8 data);
	void write_d(u8 data);
	void write_g(u8 data);
	u8 read_in();
};

// handlers

void mdallas_state::update_display()
{
	m_display->matrix(~(m_d << 4 | m_g), m_l);
}

void mdallas_state::write_l(u8 data)
{
	// L: digit segment data
	m_l = data;
	update_display();
}

void mdallas_state::write_d(u8 data)
{
	// D: select digit, input mux high
	m_inp_mux = (m_inp_mux & 0xf) | (data << 4 & 0x30);
	m_d = data;
	update_display();
}

void mdallas_state::write_g(u8 data)
{
	// G: select digit, input mux low
	m_inp_mux = (m_inp_mux & 0x30) | (data & 0xf);
	m_g = data;
	update_display();
}

u8 mdallas_state::read_in()
{
	// IN: multiplexed inputs
	return read_inputs(6, 0xf);
}

// inputs

/* physical button layout and labels are like this:

    <  ON>  [YES]   [NO]   [NEXT]
    [<W]    [^N]    [Sv]   [E>]
    [7]     [8]     [9]    [STATUS]
    [4]     [5]     [6]    [ASSETS]
    [1]     [2]     [3]    [START]
    [CLEAR] [0]     [MOVE] [ENTER]
*/

static INPUT_PORTS_START( mdallas )
	PORT_START("IN.0") // G0 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_DEL) PORT_NAME("Clear")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_M) PORT_NAME("Move")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")

	PORT_START("IN.1") // G1 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Start")

	PORT_START("IN.2") // G2 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Assets")

	PORT_START("IN.3") // G3 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Status")

	PORT_START("IN.4") // D0 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("West") // W
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME("Next")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("East") // E

	PORT_START("IN.5") // D1 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_N) PORT_NAME("No")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_Y) PORT_NAME("Yes")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("South") // S
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("North") // N
INPUT_PORTS_END

// config

void mdallas_state::mdallas(machine_config &config)
{
	// basic machine hardware
	COP444L(config, m_maincpu, 900000); // approximation - RC osc. R=57K, C=100pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_l().set(FUNC(mdallas_state::write_l));
	m_maincpu->write_d().set(FUNC(mdallas_state::write_d));
	m_maincpu->write_g().set(FUNC(mdallas_state::write_g));
	m_maincpu->read_in().set(FUNC(mdallas_state::read_in));
	m_maincpu->write_so().set(m_speaker, FUNC(speaker_sound_device::level_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 8);
	m_display->set_segmask(0xff, 0xff);
	config.set_default_layout(layout_mdallas);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( mdallas )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "cop444l-hyn_n", 0x0000, 0x0800, CRC(7848b78c) SHA1(778d24512180892f58c49df3c72ca77b2618d63b) )
ROM_END





/*******************************************************************************

  Mego Invasion From Space (unreleased)
  * COP421 (likely a development chip)
  * 36+9 LEDs with overlay mask (for enemies and player ship), 1-bit sound

  This game is presumedly unreleased. The design is very complex. Player ship
  and bullets are on a moving "wand", a 2-way mirror makes it appear on the same
  plane as the enemies and barriers.

  It is described in patent US4345764, the ROM data is included.

*******************************************************************************/

class minspace_state : public hh_cop400_state
{
public:
	minspace_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void minspace(machine_config &config);

private:
	void update_display();
	void write_g(u8 data);
	void write_d(u8 data);
	void write_l(u8 data);
	u8 read_l();
};

// handlers

void minspace_state::update_display()
{
	m_display->matrix(m_g << 4 | m_d, m_l);
}

void minspace_state::write_g(u8 data)
{
	// G0,G1: led select part
	// G2,G3: input mux
	m_g = ~data & 0xf;
	update_display();
}

void minspace_state::write_d(u8 data)
{
	// D0-D3: led select part
	m_d = ~data & 0xf;
	update_display();
}

void minspace_state::write_l(u8 data)
{
	// L0-L7: led data
	m_l = ~data & 0xff;
	update_display();
}

u8 minspace_state::read_l()
{
	u8 ret = 0xff;

	// L0-L5+G2: positional odd
	// L0-L5+G3: positional even
	u8 pos = m_inputs[1]->read() >> 8;
	if (m_g & 4 && pos & 1)
		ret ^= (1 << (pos >> 1));
	if (m_g & 8 && ~pos & 1)
		ret ^= (1 << (pos >> 1));

	// L7+G3: fire button
	if (m_g & 8 && m_inputs[0]->read())
		ret ^= 0x80;

	return ret & ~m_l;
}

// inputs

static INPUT_PORTS_START( minspace )
	PORT_START("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 )

	PORT_START("IN.1")
	PORT_BIT( 0xfff, 0x600, IPT_PADDLE ) PORT_MINMAX(0x040, 0xbc0) PORT_SENSITIVITY(25) PORT_KEYDELTA(100) PORT_CENTERDELTA(0)
INPUT_PORTS_END

// config

void minspace_state::minspace(machine_config &config)
{
	// basic machine hardware
	COP421(config, m_maincpu, 850000); // frequency guessed
	m_maincpu->set_config(COP400_CKI_DIVISOR_4, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_g().set(FUNC(minspace_state::write_g));
	m_maincpu->write_d().set(FUNC(minspace_state::write_d));
	m_maincpu->write_l().set(FUNC(minspace_state::write_l));
	m_maincpu->read_l().set(FUNC(minspace_state::read_l));
	m_maincpu->read_l_tristate().set_constant(0xff);
	m_maincpu->write_so().set(m_speaker, FUNC(speaker_sound_device::level_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(6, 8);
	config.set_default_layout(layout_minspace);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( minspace )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "cop421_us4345764", 0x0000, 0x0400, CRC(0068c3a3) SHA1(4e5fd566a5a26c066cc14623a9bd01e109ebf797) ) // typed in from patent US4345764, good print quality
ROM_END





/*******************************************************************************

  Milton Bradley Plus One
  * COP410L MCU in 8-pin DIP, label ~/029 MM 57405 (die label COP410L/B NNE)
  * orientation sensor(4 directions), 1-bit sound

  This is a board game, each player needs to rotate a triangular pyramid
  shaped piece the same as the previous player, plus 1.

*******************************************************************************/

class plus1_state : public hh_cop400_state
{
public:
	plus1_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void plus1(machine_config &config);

private:
	void write_d(u8 data);
	void write_l(u8 data);
	u8 read_l();
};

// handlers

void plus1_state::write_d(u8 data)
{
	// D0?: speaker out
	m_speaker->level_w(data & 1);
}

void plus1_state::write_l(u8 data)
{
	m_l = data;
}

u8 plus1_state::read_l()
{
	// L: IN.1, mask with output
	return m_inputs[1]->read() & m_l;
}

// inputs

static INPUT_PORTS_START( plus1 )
	PORT_START("IN.0") // port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Sensor Position Green")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Sensor Position Red")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN.1") // port L
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Sensor Position Blue")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Sensor Position Yellow")
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

// config

void plus1_state::plus1(machine_config &config)
{
	// basic machine hardware
	COP410(config, m_maincpu, 850000); // approximation - RC osc. R=51K, C=100pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_d().set(FUNC(plus1_state::write_d));
	m_maincpu->read_g().set_ioport("IN.0");
	m_maincpu->write_l().set(FUNC(plus1_state::write_l));
	m_maincpu->read_l().set(FUNC(plus1_state::read_l));

	// no visual feedback!

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( plus1 )
	ROM_REGION( 0x0200, "maincpu", 0 )
	ROM_LOAD( "cop410l_b_nne", 0x0000, 0x0200, CRC(dbde3864) SHA1(8e7284b526dc6b99b9d921c9ad608972a761dc36) )
ROM_END





/*******************************************************************************

  Milton Bradley Electronic Lightfight
  * COP421L MCU label /B119 COP421L-HLA/N
  * 5*5 leds, 1-bit sound

  Xbox-shaped electronic game for 2 or more players, with long diagonal buttons
  next to each outer LED. The main object of the game is to pinpoint a light
  by pressing 2 buttons. To start, press a skill-level button(P2 button 7/8/9)
  after selecting a game mode(P1 button 6-10).

  The game variations are:
  1: LightFight
  2: NightFight
  3: RiteSite
  4: QuiteBrite
  5: RightLight

*******************************************************************************/

class lightfgt_state : public hh_cop400_state
{
public:
	lightfgt_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void lightfgt(machine_config &config);

private:
	void update_display();
	void write_so(int state);
	void write_d(u8 data);
	void write_l(u8 data);
	u8 read_g();
};

// handlers

void lightfgt_state::update_display()
{
	u8 grid = (m_so | m_d << 1) ^ 0x1f;
	m_display->matrix(grid, m_l);
}

void lightfgt_state::write_so(int state)
{
	// SO: led grid 0 (and input mux)
	m_so = state;
	update_display();
}

void lightfgt_state::write_d(u8 data)
{
	// D: led grid 1-4 (and input mux)
	m_d = data;
	update_display();
}

void lightfgt_state::write_l(u8 data)
{
	// L0-L4: led state
	// L5-L7: N/C
	m_l = data & 0x1f;
	update_display();
}

u8 lightfgt_state::read_g()
{
	// G: multiplexed inputs
	m_inp_mux = m_d << 1 | m_so;
	return read_inputs(5, 0xf);
}

// inputs

static INPUT_PORTS_START( lightfgt )
	PORT_START("IN.0") // SO port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON6 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON1 ) // note: button 1 is on the left side from player perspective
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_COCKTAIL
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON10 ) PORT_COCKTAIL

	PORT_START("IN.1") // D0 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON7 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_COCKTAIL
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON9 ) PORT_COCKTAIL

	PORT_START("IN.2") // D1 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON8 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON3 )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_COCKTAIL
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON8 ) PORT_COCKTAIL

	PORT_START("IN.3") // D2 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON9 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON4 )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_COCKTAIL
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON7 ) PORT_COCKTAIL

	PORT_START("IN.4") // D3 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON10 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON5 )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_COCKTAIL
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_COCKTAIL
INPUT_PORTS_END

// config

void lightfgt_state::lightfgt(machine_config &config)
{
	// basic machine hardware
	COP421(config, m_maincpu, 950000); // approximation - RC osc. R=82K, C=56pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_so().set(FUNC(lightfgt_state::write_so));
	m_maincpu->write_d().set(FUNC(lightfgt_state::write_d));
	m_maincpu->write_l().set(FUNC(lightfgt_state::write_l));
	m_maincpu->write_sk().set(m_speaker, FUNC(speaker_sound_device::level_w));
	m_maincpu->read_g().set(FUNC(lightfgt_state::read_g));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(5, 5);
	config.set_default_layout(layout_lightfgt);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( lightfgt )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "cop421l-hla_n", 0x0000, 0x0400, CRC(aceb2d65) SHA1(2328cbb195faf93c575f3afa3a1fe0079180edd7) )
ROM_END





/*******************************************************************************

  Milton Bradley Electronic Battleship (model 4750G)
  * PCB label: 7924750G02 REV A
  * COP420 MCU label COP420-JWE/N

  This is the COP420 version, see hh_tms1k.cpp bship driver for more information.

*******************************************************************************/

class bshipg_state : public hh_cop400_state
{
public:
	bshipg_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag),
		m_dac(*this, "dac")
	{ }

	void bshipg(machine_config &config);

private:
	required_device<dac_3bit_r2r_device> m_dac;

	void write_d(u8 data);
	void write_g(u8 data);
	u8 read_l();
	u8 read_in();
	void write_so(int state);
};

// handlers

void bshipg_state::write_d(u8 data)
{
	// D: input mux
	m_inp_mux = data;
}

void bshipg_state::write_g(u8 data)
{
	// G0-G2: speaker out via 3.9K, 2.2K, 1.0K resistors
	// G3: enable speaker
	m_dac->write((data & 8) ? (data & 7) : 0);
}

u8 bshipg_state::read_l()
{
	// L: multiplexed inputs
	return read_inputs(4, 0xff);
}

u8 bshipg_state::read_in()
{
	// IN: multiplexed inputs
	return read_inputs(4, 0xf00) >> 8;
}

void bshipg_state::write_so(int state)
{
	// SO: led
	m_display->matrix(1, state);
}

// inputs

static INPUT_PORTS_START( bshipg )
	PORT_START("IN.0") // D0 ports L,IN
	PORT_BIT( 0x001, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("P1 Clear Last Entry") // CLE
	PORT_BIT( 0x002, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("P1 A")
	PORT_BIT( 0x004, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_NAME("P1 B")
	PORT_BIT( 0x008, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("P1 C")
	PORT_BIT( 0x010, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("P1 D")
	PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("P1 E")
	PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("P1 F")
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_G) PORT_NAME("P1 G")
	PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_H) PORT_NAME("P1 H")
	PORT_BIT( 0x200, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_I) PORT_NAME("P1 I")
	PORT_BIT( 0x400, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_J) PORT_NAME("P1 J")
	PORT_BIT( 0x800, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN.1") // D1 ports L,IN
	PORT_BIT( 0x001, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_NAME("P1 Clear Memory") // CM
	PORT_BIT( 0x002, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("P1 1")
	PORT_BIT( 0x004, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("P1 2")
	PORT_BIT( 0x008, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("P1 3")
	PORT_BIT( 0x010, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("P1 4")
	PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("P1 5")
	PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("P1 6")
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("P1 7")
	PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("P1 8")
	PORT_BIT( 0x200, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("P1 9")
	PORT_BIT( 0x400, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("P1 10")
	PORT_BIT( 0x800, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("P1 Fire")

	PORT_START("IN.2") // D2 ports L,IN
	PORT_BIT( 0x001, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 Clear Last Entry") // CLE
	PORT_BIT( 0x002, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 A")
	PORT_BIT( 0x004, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 B")
	PORT_BIT( 0x008, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 C")
	PORT_BIT( 0x010, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 D")
	PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 E")
	PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 F")
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 G")
	PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 H")
	PORT_BIT( 0x200, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 I")
	PORT_BIT( 0x400, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 J")
	PORT_BIT( 0x800, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN.3") // D3 ports L,IN
	PORT_BIT( 0x001, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 Clear Memory") // CM
	PORT_BIT( 0x002, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 1")
	PORT_BIT( 0x004, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 2")
	PORT_BIT( 0x008, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 3")
	PORT_BIT( 0x010, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 4")
	PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 5")
	PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 6")
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 7")
	PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 8")
	PORT_BIT( 0x200, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 9")
	PORT_BIT( 0x400, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 10")
	PORT_BIT( 0x800, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 Fire")

	PORT_START("IN.4") // SI
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_TOGGLE PORT_CODE(KEYCODE_F1) PORT_NAME("Load/Go") // switch
INPUT_PORTS_END

// config

void bshipg_state::bshipg(machine_config &config)
{
	// basic machine hardware
	COP420(config, m_maincpu, 800000); // approximation - RC osc. R=14K, C=100pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_4, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_d().set(FUNC(bshipg_state::write_d));
	m_maincpu->write_g().set(FUNC(bshipg_state::write_g));
	m_maincpu->read_l().set(FUNC(bshipg_state::read_l));
	m_maincpu->read_in().set(FUNC(bshipg_state::read_in));
	m_maincpu->write_so().set(FUNC(bshipg_state::write_so));
	m_maincpu->read_si().set_ioport("IN.4");

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 1);
	config.set_default_layout(layout_bshipg);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	DAC_3BIT_R2R(config, m_dac).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( bshipg )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "cop420-jwe_n", 0x0000, 0x0400, CRC(5ea8111a) SHA1(34931463b806b48dce4f8ae2361512510bae0ebf) )
ROM_END





/*******************************************************************************

  National Semiconductor QuizKid Racer (COP420 version)
  * COP420 MCU label COP420-NPG/N
  * 8-digit 7seg led display(1 custom digit), 1 green led, no sound

  This is the COP420 version, they removed support for the link cable.
  The first release was on a MM5799 MCU, see hh_cops1.cpp.

*******************************************************************************/

class qkracera_state : public hh_cop400_state
{
public:
	qkracera_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void qkracera(machine_config &config);

private:
	void update_display();
	void write_d(u8 data);
	void write_g(u8 data);
	void write_l(u8 data);
	u8 read_in();
	void write_sk(int state);
};

// handlers

void qkracera_state::update_display()
{
	m_display->matrix(~(m_d | m_g << 4 | m_sk << 8), m_l);
}

void qkracera_state::write_d(u8 data)
{
	// D: select digit, D3: input mux low bit
	m_inp_mux = (m_inp_mux & ~1) | (data >> 3 & 1);
	m_d = data;
	update_display();
}

void qkracera_state::write_g(u8 data)
{
	// G: select digit, input mux
	m_inp_mux = (m_inp_mux & 1) | (data << 1 & 0x1e);
	m_g = data;
	update_display();
}

void qkracera_state::write_l(u8 data)
{
	// L0-L6: digit segment data
	m_l = data & 0x7f;
	update_display();
}

u8 qkracera_state::read_in()
{
	// IN: multiplexed inputs
	return read_inputs(5, 0xf);
}

void qkracera_state::write_sk(int state)
{
	// SK: green led
	m_sk = state;
	update_display();
}

// inputs

static INPUT_PORTS_START( qkracera )
	PORT_START("IN.0") // D3 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Amateur")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("Pro")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("Complex")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("Tables")

	PORT_START("IN.1") // G0 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")

	PORT_START("IN.2") // G1 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")

	PORT_START("IN.3") // G2 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")

	PORT_START("IN.4") // G3 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("Slow")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("Fast")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
INPUT_PORTS_END

// config

void qkracera_state::qkracera(machine_config &config)
{
	// basic machine hardware
	COP420(config, m_maincpu, 700000); // approximation - RC osc. R=47K, C=100pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_d().set(FUNC(qkracera_state::write_d));
	m_maincpu->write_g().set(FUNC(qkracera_state::write_g));
	m_maincpu->write_l().set(FUNC(qkracera_state::write_l));
	m_maincpu->read_in().set(FUNC(qkracera_state::read_in));
	m_maincpu->write_sk().set(FUNC(qkracera_state::write_sk));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 7);
	m_display->set_segmask(0xdf, 0x7f);
	m_display->set_segmask(0x20, 0x41); // equals sign
	config.set_default_layout(layout_qkracera);

	// no sound!
}

// roms

ROM_START( qkracera )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "cop420-npg_n", 0x0000, 0x0400, CRC(17f8e538) SHA1(23d1a1819e6ba552d8da83da2948af1cf5b13d5b) )
ROM_END





/*******************************************************************************

  National Semiconductor COPS Pocket Assistant (CPA)
  * COP444L MCU label COP444L-JXY/N
  * 8-digit 7seg display, 1-bit sound

  It's a programmable COP400 series MCU simulator, on a COP400 series MCU.
  Note that this MCU doesn't have executable RAM, so it truly is a simulator.
  The hardware/PCB and the green clamshell are identical to Mattel Dalla$.

*******************************************************************************/

// handlers: see mdallas_state

// inputs

static INPUT_PORTS_START( copspa )
	PORT_START("IN.0") // G0 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0') PORT_NAME("0 / CLRA")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1') PORT_NAME("1 / LQID")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2') PORT_NAME("2 / COMP")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3') PORT_NAME("3 / CAB")

	PORT_START("IN.1") // G1 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4') PORT_NAME("4 / RC")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5') PORT_NAME("5 / SC")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6') PORT_NAME("6 / ASC")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7') PORT_NAME("7 / CBA")

	PORT_START("IN.2") // G2 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8') PORT_NAME("8 / SKC")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9') PORT_NAME("9 / SKE")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_NAME("A / JP")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_NAME("B / JSR")

	PORT_START("IN.3") // G3 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_NAME("C / LBI")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_NAME("D / SMB")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_NAME("E / RMB")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_NAME("F / SKMB")

	PORT_START("IN.4") // D0 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_NAME("Prog")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_NAME("SS / AISC")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_NAME("Run / STII")

	PORT_START("IN.5") // D1 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_NAME("Exec / LD")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_NAME("Reset / X")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_NAME("Modify / XIS")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_NAME("Disp / XDS")
INPUT_PORTS_END

// roms

ROM_START( copspa )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "cop444l-jxy_n", 0x0000, 0x0800, CRC(8e5da5d2) SHA1(d557a5ede206fa0dff7b549acef9e0ef48e48c8a) )
ROM_END





/*******************************************************************************

  SCAT specialist calculators
  * COP404LSN-5 MCU (no internal ROM)
  * 2KB EPROM (ETC2716Q)
  * 8-digit 7seg led display

  SCAT = aka South Carolina(SC) Applied Technology, Inc.

  Known products, assumed to be all on the same hardware:
  - The Dimension (aka Feet & Inch Calculator)
  - The Solution
  - Metalmate

  CKI was measured ~1.469MHz, but D0 was measured ~77.44Hz so that means real
  clock speed is a bit higher than CKI measurement, and the clock divider is 32.

*******************************************************************************/

class scat_state : public hh_cop400_state
{
public:
	scat_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void scat(machine_config &config);

private:
	void main_map(address_map &map) ATTR_COLD;

	void update_display();
	void write_d(u8 data);
	void write_g(u8 data);
	void write_l(u8 data);
	u8 read_in();
};

// handlers

void scat_state::update_display()
{
	m_display->matrix(~(m_d | m_g << 4), bitswap<8>(m_l,0,1,2,3,4,5,6,7));
}

void scat_state::write_d(u8 data)
{
	// D: select digit, input mux (low)
	m_inp_mux = (m_inp_mux & 0x30) | (data & 0xf);
	m_d = data;
	update_display();
}

void scat_state::write_g(u8 data)
{
	// G: select digit, input mux (high)
	m_inp_mux = (m_inp_mux & 0xf) | (data << 4 & 0x30);
	m_g = data;
	update_display();
}

void scat_state::write_l(u8 data)
{
	// L: digit segment data
	m_l = data;
	update_display();
}

u8 scat_state::read_in()
{
	// IN: multiplexed inputs
	return read_inputs(6, 0xf);
}

void scat_state::main_map(address_map &map)
{
	map(0x0000, 0x07ff).rom();
}

// inputs

static INPUT_PORTS_START( solution )
	PORT_START("IN.0") // D0 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("= / Enter")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+ / ppm")

	PORT_START("IN.1") // D1 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2 / Atoms / Mole")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3 / Atomic Wt.")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("- / %")

	PORT_START("IN.2") // D2 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4 / Density Wntd.")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5 / Orig. Density")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6 / Eq. Wt.")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"× / Normal")

	PORT_START("IN.3") // D3 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7 / Conc. Wntd.")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8 / Orig. Conc.")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9 / Fmla. Wt.")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷ / Molar")

	PORT_START("IN.4") // G0 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("Milli.")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("Micro.")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_G) PORT_NAME("Gram")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_L) PORT_NAME("Liter")

	PORT_START("IN.5") // G1 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_V) PORT_NAME("Known Vol.")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("Known Wt.")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("Dil. Wntd.")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_NAME("CE/C")
INPUT_PORTS_END

// config

void scat_state::scat(machine_config &config)
{
	// basic machine hardware
	COP404L(config, m_maincpu, 1500000); // R/C OSC via MM74C14N
	m_maincpu->set_config(COP400_CKI_DIVISOR_32, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->set_addrmap(AS_PROGRAM, &scat_state::main_map);
	m_maincpu->write_d().set(FUNC(scat_state::write_d));
	m_maincpu->write_g().set(FUNC(scat_state::write_g));
	m_maincpu->write_l().set(FUNC(scat_state::write_l));
	m_maincpu->read_in().set(FUNC(scat_state::read_in));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 8);
	m_display->set_segmask(0xff, 0xff);
	config.set_default_layout(layout_scat);

	// no sound!
}

// roms

ROM_START( solution )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "etc2716q", 0x0000, 0x0800, CRC(cf990f88) SHA1(6d505fdc94028cbdf6445df9e9451156a9d5f372) ) // no custom label
ROM_END





/*******************************************************************************

  Select Merchandise Video Challenger
  * COP420 MCU label COP420-TDX/N
  * 6-digit 7seg led display, 3 other leds, 4-bit sound

  This is a lightgun with scorekeeping. The "games" themselves were released
  on VHS tapes. To determine scoring, the lightgun detects strobe lighting
  from objects in the video.

  known releases:
  - Japan: Video Challenger, published by Takara
  - UK: Video Challenger, published by Bandai
  - Canada: Video Challenger, published by Irwin

*******************************************************************************/

class vidchal_state : public hh_cop400_state
{
public:
	vidchal_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void vidchal(machine_config &config);

private:
	void update_display();
	void write_d(u8 data);
	void write_l(u8 data);
	void write_sk(int state);
};

// handlers

void vidchal_state::update_display()
{
	m_display->matrix(m_d | m_sk << 6, m_l);
}

void vidchal_state::write_d(u8 data)
{
	// D: CD4028BE to digit select
	m_d = 1 << data & 0x3f;
	update_display();
}

void vidchal_state::write_l(u8 data)
{
	// L: digit segment data
	m_l = bitswap<8>(data,0,3,1,5,4,7,2,6);
	update_display();
}

void vidchal_state::write_sk(int state)
{
	// SK: hit led
	m_sk = state;
	update_display();
}

// inputs

static INPUT_PORTS_START( vidchal )
	PORT_START("IN.0") // port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_F1) PORT_NAME("Light Sensor")
INPUT_PORTS_END

// config

void vidchal_state::vidchal(machine_config &config)
{
	// basic machine hardware
	COP420(config, m_maincpu, 900000); // approximation
	m_maincpu->set_config(COP400_CKI_DIVISOR_4, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_d().set(FUNC(vidchal_state::write_d));
	m_maincpu->write_g().set("dac", FUNC(dac_byte_interface::data_w));
	m_maincpu->write_l().set(FUNC(vidchal_state::write_l));
	m_maincpu->read_in().set_ioport("IN.0");
	m_maincpu->write_sk().set(FUNC(vidchal_state::write_sk));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(6+1, 8);
	m_display->set_segmask(0x3f, 0xff);
	config.set_default_layout(layout_vidchal);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	DAC_4BIT_R2R(config, "dac").add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( vidchal )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "cop420-tdx_n", 0x0000, 0x0400, CRC(c9bd041c) SHA1(ab0dcaf4741620fa4c28ab75337a23d646af7626) )
ROM_END





/*******************************************************************************

  Tandy Computerized Arcade (model 60-2159A)
  * PCB label: 60-2159A
  * COP421 MCU label -B9112 COP421-UPG/N
  * 12 lamps behind buttons, 1-bit sound

  This is the COP421 version, see hh_tms1k.cpp comparc driver for more information.

*******************************************************************************/

class comparca_state : public hh_cop400_state
{
public:
	comparca_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void comparca(machine_config &config);

private:
	void write_d(u8 data);
	void write_l(u8 data);
	u8 read_l();
	u8 read_g();
	int read_si();
};

// handlers

void comparca_state::write_d(u8 data)
{
	// D: input mux
	m_d = m_inp_mux = data;
}

void comparca_state::write_l(u8 data)
{
	// L0-L3: lamp data
	// L4-L6: lamp select
	m_l = data;
	m_display->matrix(~m_l >> 4 & 7, m_l);
}

u8 comparca_state::read_l()
{
	// L7: Repeat-2 button
	return m_inputs[4]->read() << 7 | m_l;
}

u8 comparca_state::read_g()
{
	// G: multiplexed inputs
	return read_inputs(4, 0xf);
}

int comparca_state::read_si()
{
	// SI: D3
	return BIT(m_d, 3);
}

// inputs

static INPUT_PORTS_START( comparca )
	PORT_START("IN.0") // D0 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Button 1")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Button 2")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Button 3")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Button 4")

	PORT_START("IN.1") // D1 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Button 5")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Button 6")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Button 7")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Button 8")

	PORT_START("IN.2") // D2 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("Button 9")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("Button 10")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("Button 11")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_EQUALS) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("Button 12")

	PORT_START("IN.3") // D3 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Space-2")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("Select")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("Play-2/Hit-7")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Start")

	PORT_START("IN.4") // L7
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("Repeat-2")
INPUT_PORTS_END

// config

void comparca_state::comparca(machine_config &config)
{
	// basic machine hardware
	COP421(config, m_maincpu, 550000); // approximation - RC osc. R=33K, C=56pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_4, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_d().set(FUNC(comparca_state::write_d));
	m_maincpu->write_l().set(FUNC(comparca_state::write_l));
	m_maincpu->read_l().set(FUNC(comparca_state::read_l));
	m_maincpu->read_g().set(FUNC(comparca_state::read_g));
	m_maincpu->write_so().set(m_speaker, FUNC(speaker_sound_device::level_w));
	m_maincpu->read_si().set(FUNC(comparca_state::read_si));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(3, 4);
	config.set_default_layout(layout_comparca);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( comparca )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "cop421-upg_n.ic1", 0x0000, 0x0400, CRC(dcaf8655) SHA1(68bc84a108476c41f91b882d24cb516ba72a8d99) )
ROM_END





/*******************************************************************************

  Texas Instruments My Little Computer
  * PCB label: 1066659-4, 17-92-81
  * COP444L MCU label COP444L 1066666
  * 4*4 leds, 1-bit sound

  It's an educational toy for young children. Overlays were included for the
  mini games, MAME external artwork is required for these.

  Strangely, TI didn't use their own brand MCU for this toy.

*******************************************************************************/

class lilcomp_state : public hh_cop400_state
{
public:
	lilcomp_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag),
		m_power_timer(*this, "power")
	{ }

	void lilcomp(machine_config &config);

private:
	required_device<timer_device> m_power_timer;

	void update_display();
	void write_d(u8 data);
	void write_l(u8 data);
	void write_g(u8 data);
	u8 read_g();
	void write_sk(int state);

	TIMER_DEVICE_CALLBACK_MEMBER(power_off) { set_power(false); }
};

// handlers

void lilcomp_state::update_display()
{
	m_display->matrix(m_l >> 4, ~m_d);
}

void lilcomp_state::write_d(u8 data)
{
	// D: led data
	m_d = data;
	update_display();
}

void lilcomp_state::write_l(u8 data)
{
	// L0-L2: input mux
	// L3: N/C
	m_inp_mux = data & 7;

	// L4-L7: led select
	m_l = data;
	update_display();
}

void lilcomp_state::write_g(u8 data)
{
	m_g = data;
}

u8 lilcomp_state::read_g()
{
	// G: multiplexed inputs
	return read_inputs(3, m_g);
}

void lilcomp_state::write_sk(int state)
{
	// SK: trigger power off after a short delay (since it also toggles at boot)
	if (state != m_sk)
		m_power_timer->adjust(state ? attotime::from_msec(100) : attotime::never);

	m_sk = state;
}

// inputs

static INPUT_PORTS_START( lilcomp )
	PORT_START("IN.0") // L0 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Cursor Down")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Cursor Right")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_UP) PORT_NAME("Cursor Up")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Cursor Left")

	PORT_START("IN.1") // L1 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Go")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Code 5")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN.2") // L2 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Code 1")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Code 4")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Code 3")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Code 2")

	PORT_START("IN.3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_ON ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_cop400_state::power_button), true)
INPUT_PORTS_END

// config

void lilcomp_state::lilcomp(machine_config &config)
{
	// basic machine hardware
	COP444L(config, m_maincpu, 800000); // approximation - RC osc. R=46.4K, C=100pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_d().set(FUNC(lilcomp_state::write_d));
	m_maincpu->write_l().set(FUNC(lilcomp_state::write_l));
	m_maincpu->write_g().set(FUNC(lilcomp_state::write_g));
	m_maincpu->read_g().set(FUNC(lilcomp_state::read_g));
	m_maincpu->write_sk().set(FUNC(lilcomp_state::write_sk));
	m_maincpu->write_so().set(m_speaker, FUNC(speaker_sound_device::level_w));

	TIMER(config, "power").configure_generic(FUNC(lilcomp_state::power_off));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 4);
	config.set_default_layout(layout_lilcomp);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( lilcomp )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "cop444l_1066666", 0x0000, 0x0800, CRC(fb4674d2) SHA1(2c38c2f0bd4222166298a50dec88339b06362005) )
ROM_END



} // anonymous namespace

/*******************************************************************************

  Game driver(s)

*******************************************************************************/

//    YEAR  NAME        PARENT     COMPAT  MACHINE     INPUT       CLASS            INIT        COMPANY, FULLNAME, FLAGS
SYST( 1979, ctstein,    0,         0,      ctstein,    ctstein,    ctstein_state,   empty_init, "Castle Toy", "Einstein (Castle Toy)", MACHINE_SUPPORTS_SAVE )

SYST( 1980, h2hbaskbc,  h2hbaskb,  0,      h2hbaskbc,  h2hbaskbc,  h2hbaskbc_state, empty_init, "Coleco", "Head to Head: Electronic Basketball (COP420L version)", MACHINE_SUPPORTS_SAVE )
SYST( 1980, h2hhockeyc, h2hhockey, 0,      h2hhockeyc, h2hhockeyc, h2hbaskbc_state, empty_init, "Coleco", "Head to Head: Electronic Hockey (COP420L version)", MACHINE_SUPPORTS_SAVE )
SYST( 1980, h2hsoccerc, 0,         0,      h2hsoccerc, h2hsoccerc, h2hbaskbc_state, empty_init, "Coleco", "Head to Head: Electronic Soccer (COP420L version)", MACHINE_SUPPORTS_SAVE )

SYST( 1981, einvaderc,  einvader,  0,      einvaderc,  einvaderc,  einvaderc_state, empty_init, "Entex", "Space Invader (Entex, COP444L version)", MACHINE_SUPPORTS_SAVE )

SYST( 1980, lchicken,   0,         0,      lchicken,   lchicken,   lchicken_state,  empty_init, "LJN Toys", "I Took a Lickin' From a Chicken", MACHINE_SUPPORTS_SAVE | MACHINE_MECHANICAL )

SYST( 1979, funjacks,   0,         0,      funjacks,   funjacks,   funjacks_state,  empty_init, "Mattel Electronics", "Funtronics: Jacks", MACHINE_SUPPORTS_SAVE )
SYST( 1979, funrlgl,    0,         0,      funrlgl,    funrlgl,    funrlgl_state,   empty_init, "Mattel Electronics", "Funtronics: Red Light Green Light", MACHINE_SUPPORTS_SAVE )
SYST( 1980, funtag,     0,         0,      funtag,     funtag,     funtag_state,    empty_init, "Mattel Electronics", "Funtronics: Tag", MACHINE_SUPPORTS_SAVE )
SYST( 1979, mbaskb2,    0,         0,      mbaskb2,    mbaskb2,    mbaskb2_state,   empty_init, "Mattel Electronics", "Basketball 2 (Mattel)", MACHINE_SUPPORTS_SAVE )
SYST( 1979, msoccer2,   0,         0,      msoccer2,   msoccer2,   mbaskb2_state,   empty_init, "Mattel Electronics", "Soccer 2 (Mattel)", MACHINE_SUPPORTS_SAVE )
SYST( 1980, lafootb,    0,         0,      lafootb,    lafootb,    lafootb_state,   empty_init, "Mattel Electronics", "Look Alive! Football", MACHINE_SUPPORTS_SAVE )
SYST( 1981, mdallas,    0,         0,      mdallas,    mdallas,    mdallas_state,   empty_init, "Mattel Electronics", "Dalla$ (J.R. handheld)", MACHINE_SUPPORTS_SAVE ) // ***

SYST( 1980, minspace,   0,         0,      minspace,   minspace,   minspace_state,  empty_init, "Mego", "Invasion From Space (patent)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )

SYST( 1980, plus1,      0,         0,      plus1,      plus1,      plus1_state,     empty_init, "Milton Bradley", "Plus One", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_CONTROLS ) // ***
SYST( 1981, lightfgt,   0,         0,      lightfgt,   lightfgt,   lightfgt_state,  empty_init, "Milton Bradley", "Electronic Lightfight: The Games of Dueling Lights", MACHINE_SUPPORTS_SAVE )
SYST( 1982, bshipg,     bship,     0,      bshipg,     bshipg,     bshipg_state,    empty_init, "Milton Bradley", "Electronic Battleship (COP420 version, rev. G)", MACHINE_SUPPORTS_SAVE ) // ***

SYST( 1979, qkracera,   qkracer,   0,      qkracera,   qkracera,   qkracera_state,  empty_init, "National Semiconductor", "QuizKid Racer (COP420 version)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1982, copspa,     0,         0,      mdallas,    copspa,     mdallas_state,   empty_init, "National Semiconductor", "COPS Pocket Assistant", MACHINE_SUPPORTS_SAVE )

SYST( 1984, solution,   0,         0,      scat,       solution,   scat_state,      empty_init, "SCAT", "The Solution", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )

SYST( 1987, vidchal,    0,         0,      vidchal,    vidchal,    vidchal_state,   empty_init, "Select Merchandise", "Video Challenger", MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING )

SYST( 1981, comparca,   comparc,   0,      comparca,   comparca,   comparca_state,  empty_init, "Tandy Corporation", "Computerized Arcade (COP421 version, model 60-2159A)", MACHINE_SUPPORTS_SAVE ) // some of the games: ***

SYST( 1989, lilcomp,    0,         0,      lilcomp,    lilcomp,    lilcomp_state,   empty_init, "Texas Instruments", "My Little Computer", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )

// ***: As far as MAME is concerned, the game is emulated fine. But for it to be playable, it requires interaction
// with other, unemulatable, things eg. game board/pieces, book, playing cards, pen & paper, etc.



hh_cops1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

National Semiconductor COPS(MM57 MCU series) handhelds

MCU die label for MM5799 games says MM4799, but they are in fact MM5799.

ROM source notes when dumped from another title, but confident it's the same:
- cambrp: Radio Shack EC-4001 Programmable

TODO:
- qkracer link cable (already tested locally and it works, so driver notes
  and MCU serial emulation are good enough)

*******************************************************************************/

#include "emu.h"

#include "cpu/cops1/mm5799.h"
#include "sound/spkrdev.h"
#include "video/ds8874.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "cambrp.lh"
#include "mbaskb.lh"
#include "mhockey.lh"
#include "mhockeya.lh"
#include "msoccer.lh"
#include "qkracer.lh"
#include "qkspeller.lh"

//#include "hh_cops1_test.lh" // common test-layout - use external artwork


namespace {

class hh_cops1_state : public driver_device
{
public:
	hh_cops1_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_speaker(*this, "speaker"),
		m_inputs(*this, "IN.%u", 0)
	{ }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// devices
	required_device<cops1_base_device> m_maincpu;
	optional_device<pwm_display_device> m_display;
	optional_device<speaker_sound_device> m_speaker;
	optional_ioport_array<9> m_inputs; // max 9

	u16 m_inp_mux = 0;
	u16 m_grid = 0;

	// MCU output pin state
	u8 m_s = 0;
	u8 m_do = 0;
	u8 m_f = 0;
	int m_blk = false;

	u8 read_inputs(int columns);
};


// machine start/reset

void hh_cops1_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_grid));
	save_item(NAME(m_s));
	save_item(NAME(m_do));
	save_item(NAME(m_f));
	save_item(NAME(m_blk));
}

void hh_cops1_state::machine_reset()
{
}



/*******************************************************************************

  Helper Functions

*******************************************************************************/

// generic input handlers

u8 hh_cops1_state::read_inputs(int columns)
{
	u8 ret = 0;

	// read selected input rows
	for (int i = 0; i < columns; i++)
		if (BIT(m_inp_mux, i))
			ret |= m_inputs[i]->read();

	return ret;
}



/*******************************************************************************

  Minidrivers (subclass, I/O, Inputs, Machine Config, ROM Defs)

*******************************************************************************/

/*******************************************************************************

  Mattel Basketball (model 2437)
  * PCB label: MA 6017/18/19
  * MM5799 MCU die bonded directly to PCB (die label MM4799 C NCX)
  * 4001 and 74154, also bonded to PCB
  * 2-digit 7seg led display, 21 leds, 2-bit sound

  Mattel Soccer (model 2678)
  * MCU die label MM4799 C NDC
  * same hardware as Basketball

  Mattel Hockey (model 2946)
  * MCU die label MM4799 C NFR
  * same hardware as Basketball

  Judging from videos online, there are two versions of Basketball. One where
  the display shows "12" at power-on(as on MAME), and one that shows "15".

  There's also an older version of Hockey, it has the same ROM as Soccer.
  This version wasn't sold in the USA. It is commonly known as the Canadian
  version, though it was also released in Europe and Japan.

*******************************************************************************/

class mbaskb_state : public hh_cops1_state
{
public:
	mbaskb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cops1_state(mconfig, type, tag)
	{ }

	void mbaskb(machine_config &config);
	void msoccer(machine_config &config);
	void mhockey(machine_config &config);
	void mhockeya(machine_config &config);

private:
	void update_display();
	void write_do(u8 data);
	void write_blk(int state);
	void write_s(u8 data);
	void write_f(u8 data);
	u8 read_f();
};

// handlers

void mbaskb_state::update_display()
{
	// DO4321: 74154 CBAD
	u8 d = (m_do >> 1 & 7) | (m_do << 3 & 8);
	u16 sel = m_blk ? (1 << d) : 0;

	// 74154 output 2,6: speaker out
	u8 spk = bitswap<2>(sel, 6,2);
	m_speaker->level_w(spk);

	// 74154 output 3-5,10-13: digit/led select
	u8 dsp = bitswap<7>(sel, 11,4,12,5,13,3,10);
	m_display->matrix((m_f << 5 & 0x80) | dsp, m_s);
}

void mbaskb_state::write_do(u8 data)
{
	// DO: 74154 inputs
	m_do = data;
	update_display();
}

void mbaskb_state::write_blk(int state)
{
	// BLK: 74154 enable
	m_blk = state;
	update_display();
}

void mbaskb_state::write_s(u8 data)
{
	// Sa-Sg: digit segment/led data
	m_s = data;
	update_display();
}

void mbaskb_state::write_f(u8 data)
{
	// F3: led data
	m_f = data;
	update_display();
}

u8 mbaskb_state::read_f()
{
	// F1: difficulty switch
	// F2: N/C or tied high
	return m_inputs[2]->read() | (m_f & 2);
}

// inputs

static INPUT_PORTS_START( mbaskb )
	PORT_START("IN.0") // port K
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY

	PORT_START("IN.1") // INB
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) // both buttons

	PORT_START("IN.2") // F1
	PORT_CONFNAME( 0x01, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x01, "2" )
INPUT_PORTS_END

static INPUT_PORTS_START( mhockeya )
	PORT_INCLUDE( mbaskb )

	PORT_MODIFY("IN.2") // F2
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_CUSTOM ) // tied high to select Hockey
INPUT_PORTS_END

// config

void mbaskb_state::mbaskb(machine_config &config)
{
	// basic machine hardware
	MM5799(config, m_maincpu, 370000); // approximation
	m_maincpu->write_do().set(FUNC(mbaskb_state::write_do));
	m_maincpu->write_blk().set(FUNC(mbaskb_state::write_blk));
	m_maincpu->write_s().set(FUNC(mbaskb_state::write_s));
	m_maincpu->write_f().set(FUNC(mbaskb_state::write_f));
	m_maincpu->read_f().set(FUNC(mbaskb_state::read_f));
	m_maincpu->read_k().set_ioport("IN.0");
	m_maincpu->read_inb().set_ioport("IN.1");

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 7);
	m_display->set_segmask(3, 0x7f);
	m_display->set_bri_levels(0.015, 0.2); // ball led is brighter
	config.set_default_layout(layout_mbaskb);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[4] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

void mbaskb_state::msoccer(machine_config &config)
{
	mbaskb(config);
	config.set_default_layout(layout_msoccer);

	m_display->set_bri_levels(0.005, 0.05, 0.2); // goalie is darker
}

void mbaskb_state::mhockey(machine_config &config)
{
	mbaskb(config);
	config.set_default_layout(layout_mhockey);
}

void mbaskb_state::mhockeya(machine_config &config)
{
	msoccer(config);
	config.set_default_layout(layout_mhockeya);
}

// roms

ROM_START( mbaskb )
	ROM_REGION( 0x0800, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "mm4799_c_ncx", 0x0000, 0x0200, CRC(d0e5fce1) SHA1(04708c043a763bf92d765095e9551388a1d967e8) )
	ROM_CONTINUE(             0x0400, 0x0400 )

	ROM_REGION( 254, "maincpu:opla", 0 )
	ROM_LOAD( "mm5799_common1_output.pla", 0, 254, CRC(c8d225f1) SHA1(4f1e1977e96e53d1d716b7785c4c3971ed9ff65b) )
ROM_END

ROM_START( msoccer )
	ROM_REGION( 0x0800, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "mm4799_c_ndc", 0x0000, 0x0200, CRC(4b5ce604) SHA1(6b3d58f633b4b36f533e9a3b3ca091b2e5ea5018) )
	ROM_CONTINUE(             0x0400, 0x0400 )

	ROM_REGION( 254, "maincpu:opla", 0 )
	ROM_LOAD( "mm5799_common1_output.pla", 0, 254, CRC(c8d225f1) SHA1(4f1e1977e96e53d1d716b7785c4c3971ed9ff65b) )
ROM_END

ROM_START( mhockeya )
	ROM_REGION( 0x0800, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "mm4799_c_ndc", 0x0000, 0x0200, CRC(4b5ce604) SHA1(6b3d58f633b4b36f533e9a3b3ca091b2e5ea5018) )
	ROM_CONTINUE(             0x0400, 0x0400 )

	ROM_REGION( 254, "maincpu:opla", 0 )
	ROM_LOAD( "mm5799_common1_output.pla", 0, 254, CRC(c8d225f1) SHA1(4f1e1977e96e53d1d716b7785c4c3971ed9ff65b) )
ROM_END

ROM_START( mhockey )
	ROM_REGION( 0x0800, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "mm4799_c_nfr", 0x0000, 0x0200, CRC(979e5c5b) SHA1(e6c81572f47d93f4c13472f477aac67e05841976) )
	ROM_CONTINUE(             0x0400, 0x0400 )

	ROM_REGION( 254, "maincpu:opla", 0 )
	ROM_LOAD( "mm5799_common1_output.pla", 0, 254, CRC(c8d225f1) SHA1(4f1e1977e96e53d1d716b7785c4c3971ed9ff65b) )
ROM_END





/*******************************************************************************

  National Semiconductor QuizKid Racer (MM5799 version)
  * MM5799 MCU die bonded directly to PCB (die label MM4799 C DUZ)
  * DS8874 LED driver, die bonded to PCB as well
  * 8-digit 7seg led display(1 custom digit), 1 green led, no sound
  * optional link cable to compete with another player (see patent US4051605)

  This is the first version of QuizKid Racer, the 2nd release is on a
  COP420 MCU, see hh_cop400.cpp.

*******************************************************************************/

class qkracer_state : public hh_cops1_state
{
public:
	qkracer_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cops1_state(mconfig, type, tag),
		m_ds8874(*this, "ds8874")
	{ }

	void qkracer(machine_config &config);

private:
	required_device<ds8874_device> m_ds8874;

	void ds8874_output_w(u16 data);

	void update_display();
	void write_do(u8 data);
	void write_s(u8 data);
	u8 read_f();
	u8 read_k();
	int read_si();
};

// handlers

void qkracer_state::update_display()
{
	m_display->matrix(m_grid, m_s);
}

void qkracer_state::ds8874_output_w(u16 data)
{
	// DS8874 outputs: digit select, input mux
	m_grid = ~data;
	m_inp_mux = m_grid >> 3;
	update_display();
}

void qkracer_state::write_do(u8 data)
{
	// DO1: DS8874 CP
	// DO4: DS8874 _DATA
	m_ds8874->cp_w(BIT(data, 0));
	m_ds8874->data_w(BIT(data, 3));
}

void qkracer_state::write_s(u8 data)
{
	// Sa-Sg: digit segment data
	// Sp: link data out
	m_s = data;
	update_display();
}

u8 qkracer_state::read_f()
{
	// F1: N/C
	// F2: link cable detected
	// F3: link data in
	return m_maincpu->f_output_r() & 1;
}

u8 qkracer_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(5);
}

int qkracer_state::read_si()
{
	// SI: link master(1)/slave(0)
	return 0;
}

// inputs

static INPUT_PORTS_START( qkracer )
	PORT_START("IN.0") // DS8874 OUT 4 port K
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Amateur")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("Pro")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("Complex")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("Tables")

	PORT_START("IN.1") // DS8874 OUT 5 port K
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")

	PORT_START("IN.2") // DS8874 OUT 6 port K
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")

	PORT_START("IN.3") // DS8874 OUT 7 port K
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")

	PORT_START("IN.4") // DS8874 OUT 8 port K
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("Slow")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("Fast")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
INPUT_PORTS_END

// config

void qkracer_state::qkracer(machine_config &config)
{
	// basic machine hardware
	MM5799(config, m_maincpu, 220000); // approximation
	m_maincpu->set_option_ram_d12(true);
	m_maincpu->set_option_lb_10(5);
	m_maincpu->write_do().set(FUNC(qkracer_state::write_do));
	m_maincpu->write_s().set(FUNC(qkracer_state::write_s));
	m_maincpu->read_f().set(FUNC(qkracer_state::read_f));
	m_maincpu->read_k().set(FUNC(qkracer_state::read_k));
	m_maincpu->read_si().set(FUNC(qkracer_state::read_si));

	// video hardware
	DS8874(config, m_ds8874).write_output().set(FUNC(qkracer_state::ds8874_output_w));
	PWM_DISPLAY(config, m_display).set_size(9, 7);
	m_display->set_segmask(0xdf, 0x7f);
	m_display->set_segmask(0x20, 0x41); // equals sign
	config.set_default_layout(layout_qkracer);

	// no sound!
}

// roms

ROM_START( qkracer )
	ROM_REGION( 0x0800, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "mm4799_c_duz", 0x0000, 0x0200, CRC(8b484d2a) SHA1(809e902a11e23bed010ac795ab8dc50e5c1869dc) )
	ROM_CONTINUE(             0x0400, 0x0400 )

	ROM_REGION( 254, "maincpu:opla", 0 )
	ROM_LOAD( "mm5799_qkracer_output.pla", 0, 254, CRC(f095fc51) SHA1(e3321d5cc4ecef363df7ef94f47e860c7a6d8c7e) )
ROM_END





/*******************************************************************************

  National Semiconductor QuizKid Speller
  * MM5799 MCU die bonded directly to PCB (die label MM4799 C NDF)
  * 2-digit 7seg led display, green led, red led, no sound

  The manual included 99 pictures for matching the words, with increased
  difficulty. For example 10 = owl, 90 = kangaroo.

  Modes:
  - Spell: match numbered picture with word
  - Learn: same as Spell, but only the 1st letter
  - Game: player 1 enters word, player 2 needs to guess it

*******************************************************************************/

class qkspeller_state : public hh_cops1_state
{
public:
	qkspeller_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cops1_state(mconfig, type, tag)
	{ }

	void qkspeller(machine_config &config);

private:
	void update_display();
	void write_do(u8 data);
	void write_s(u8 data);
	void write_f(u8 data);
	u8 read_f();
	u8 read_k();
};

// handlers

void qkspeller_state::update_display()
{
	m_display->matrix((m_f << 1 & 0xc) | (m_do & 3), m_inp_mux);
}

void qkspeller_state::write_do(u8 data)
{
	// DO1,DO2: digit select
	m_do = data;
	update_display();
}

void qkspeller_state::write_s(u8 data)
{
	// S: digit segment data, input mux
	m_inp_mux = data;
	update_display();
}

void qkspeller_state::write_f(u8 data)
{
	// F2,F3: led data
	m_f = data;
	update_display();
}

u8 qkspeller_state::read_f()
{
	// F1: 3-pos switch (GND, floating, Vcc)
	if (m_inputs[8]->read() == 2)
		return m_f;
	else
		return (m_inputs[8]->read() & 1) | (m_f & ~1);
}

u8 qkspeller_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(8);
}

// inputs

static INPUT_PORTS_START( qkspeller )
	PORT_START("IN.0") // Sa port K
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')

	PORT_START("IN.1") // Sb port K
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')

	PORT_START("IN.2") // Sc port K
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')

	PORT_START("IN.3") // Sd port K
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')

	PORT_START("IN.4") // Se port K
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_CHAR(8) PORT_NAME("Erase")

	PORT_START("IN.5") // Sf port K
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')

	PORT_START("IN.6") // Sg port K
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')

	PORT_START("IN.7") // Sp port K
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_NAME("Start")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13) PORT_NAME("Next")

	PORT_START("IN.8") // F1
	PORT_CONFNAME( 0x03, 0x00, "Mode" )
	PORT_CONFSETTING(    0x00, "Spell" )
	PORT_CONFSETTING(    0x02, "Learn" )
	PORT_CONFSETTING(    0x01, "Game" )

	PORT_START("TEST.0") // INB test pad
	PORT_CONFNAME( 0x01, 0x00, "Factory Test 1" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )

	PORT_START("TEST.1") // DO3 test pad
	PORT_CONFNAME( 0x01, 0x00, "Factory Test 2" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
INPUT_PORTS_END

// config

void qkspeller_state::qkspeller(machine_config &config)
{
	// basic machine hardware
	MM5799(config, m_maincpu, 220000); // approximation
	m_maincpu->write_do().set(FUNC(qkspeller_state::write_do));
	m_maincpu->write_s().set(FUNC(qkspeller_state::write_s));
	m_maincpu->write_f().set(FUNC(qkspeller_state::write_f));
	m_maincpu->read_f().set(FUNC(qkspeller_state::read_f));
	m_maincpu->read_k().set(FUNC(qkspeller_state::read_k));
	m_maincpu->read_inb().set_ioport("TEST.0");
	m_maincpu->read_do3().set_ioport("TEST.1");

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 7);
	m_display->set_segmask(3, 0x7f);
	config.set_default_layout(layout_qkspeller);

	// no sound!
}

// roms

ROM_START( qkspeller )
	ROM_REGION( 0x0800, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "mm4799_c_ndf", 0x0000, 0x0200, CRC(0f497a12) SHA1(78eb79dcc414ed2a955450ffb3be431ecc2edb0c) )
	ROM_CONTINUE(             0x0400, 0x0400 )

	ROM_REGION( 254, "maincpu:opla", 0 )
	ROM_LOAD( "mm5799_common1_output.pla", 0, 254, CRC(c8d225f1) SHA1(4f1e1977e96e53d1d716b7785c4c3971ed9ff65b) )
ROM_END





/*******************************************************************************

  Sinclair Radionics Cambridge Programmable
  * MM5799 MCU (label MM5799NBP/N, die label MM4799 C NBP)
  * DS8874 LED driver, 9-digit 7seg led display

  It's a programmable pocket calculator, up to 36 steps. 2 MCU revisions
  are known: MM5799EHY and MM5799NBP.

  4 program libraries were available:
  - Vol. 1: General / Finance / Statistics
  - Vol. 2: Mathematics
  - Vol. 3: Physics & Engineering
  - Vol. 4: Electronics

  Paste example: CCSS200SR 2-32+.12131*.5=.5.201=50.200 CSS200C
  Now enter a value under 70, followed by RUN, to calculate its factorial.

  known releases:
  - World: Cambridge Programmable, published by Sinclair Radionics
  - USA: EC-4001 Programmable, published by Tandy Corporation, Radio Shack brand

*******************************************************************************/

class cambrp_state : public hh_cops1_state
{
public:
	cambrp_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cops1_state(mconfig, type, tag),
		m_ds8874(*this, "ds8874")
	{ }

	void cambrp(machine_config &config);

private:
	required_device<ds8874_device> m_ds8874;

	void ds8874_output_w(u16 data);

	void update_display();
	void write_do(u8 data);
	void write_s(u8 data);
	u8 read_f();
	u8 read_k();
};

// handlers

void cambrp_state::update_display()
{
	m_display->matrix(m_grid, m_s);
}

void cambrp_state::ds8874_output_w(u16 data)
{
	// DS8874 outputs: digit select, input mux
	m_grid = ~data;
	m_inp_mux = m_grid >> 2;
	update_display();
}

void cambrp_state::write_do(u8 data)
{
	// DO1: DS8874 CP
	// DO4: DS8874 _DATA
	m_ds8874->cp_w(BIT(data, 0));
	m_ds8874->data_w(BIT(data, 3));
}

void cambrp_state::write_s(u8 data)
{
	// S: digit segment data
	// (DS8874 low battery out also connects to Sp)
	m_s = data;
	update_display();
}

u8 cambrp_state::read_f()
{
	// F2: K3, other: N/C
	return (~read_inputs(6) >> 1 & 2) | (m_maincpu->f_output_r() & ~2);
}

u8 cambrp_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(6);
}

// inputs

static INPUT_PORTS_START( cambrp )
	PORT_START("IN.0") // DS8874 OUT 3 port K
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0') PORT_NAME("0 / stop / +/-")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6') PORT_NAME("6 / () / R>D")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR('=') PORT_NAME("= / -")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_NAME("Up/Downshift")

	PORT_START("IN.1") // DS8874 OUT 4 port K
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1') PORT_NAME(u8"1 / \u221ax / go if neg") // U+221A = √
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7') PORT_NAME("7 / sin / arcsin")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR('-') PORT_NAME("- / -x / F")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR('C') PORT_NAME("C/CE / step")

	PORT_START("IN.2") // DS8874 OUT 5 port K
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2') PORT_NAME("2 / sto / go to")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8') PORT_NAME("8 / cos / arccos")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR('/') PORT_NAME(u8"÷ / 1/x / G")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_NAME("RUN / learn")

	PORT_START("IN.3") // DS8874 OUT 6 port K
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3') PORT_NAME("3 / ChN/# / D>R")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9') PORT_NAME("9 / tan / arctan")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR('+') PORT_NAME("+ / 2x / E")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // DS8874 OUT 7 port K
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4') PORT_NAME(u8"4 / ln x / eˣ")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR('.') PORT_NAME("./EE/_ / Downshift / A")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR('*') PORT_NAME(u8"× / x² / .")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // DS8874 OUT 8 port K
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5') PORT_NAME("5 / rcl / MEx")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void cambrp_state::cambrp(machine_config &config)
{
	// basic machine hardware
	MM5799(config, m_maincpu, 200000); // approximation
	m_maincpu->set_option_ram_d12(true);
	m_maincpu->set_option_lb_10(4);
	m_maincpu->write_do().set(FUNC(cambrp_state::write_do));
	m_maincpu->write_s().set(FUNC(cambrp_state::write_s));
	m_maincpu->read_f().set(FUNC(cambrp_state::read_f));
	m_maincpu->read_k().set(FUNC(cambrp_state::read_k));

	// video hardware
	DS8874(config, m_ds8874).write_output().set(FUNC(cambrp_state::ds8874_output_w));
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	m_display->set_segmask(0x1ff, 0xff);
	config.set_default_layout(layout_cambrp);

	// no sound!
}

// roms

ROM_START( cambrp )
	ROM_REGION( 0x0800, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "mm5799nbp_n", 0x0000, 0x0200, CRC(f4e9063b) SHA1(04b7bf24e994cd453584d233405621f8110feded) )
	ROM_CONTINUE(            0x0400, 0x0400 )

	ROM_REGION( 254, "maincpu:opla", 0 )
	ROM_LOAD( "mm5799_cambrp_output.pla", 0, 254, CRC(eb882256) SHA1(acd77c066a7b7d18c3ea10f137a45ab83d1c53e1) )
ROM_END



} // anonymous namespace

/*******************************************************************************

  Game driver(s)

*******************************************************************************/

//    YEAR  NAME       PARENT   COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY, FULLNAME, FLAGS
SYST( 1978, mbaskb,    0,       0,      mbaskb,    mbaskb,    mbaskb_state,    empty_init, "Mattel Electronics", "Basketball (Mattel)", MACHINE_SUPPORTS_SAVE )
SYST( 1978, msoccer,   0,       0,      msoccer,   mbaskb,    mbaskb_state,    empty_init, "Mattel Electronics", "Soccer (Mattel)", MACHINE_SUPPORTS_SAVE )
SYST( 1978, mhockey,   0,       0,      mhockey,   mbaskb,    mbaskb_state,    empty_init, "Mattel Electronics", "Hockey (Mattel, US version)", MACHINE_SUPPORTS_SAVE )
SYST( 1978, mhockeya,  mhockey, 0,      mhockeya,  mhockeya,  mbaskb_state,    empty_init, "Mattel Electronics", "Hockey (Mattel, export version)", MACHINE_SUPPORTS_SAVE )

SYST( 1977, qkracer,   0,       0,      qkracer,   qkracer,   qkracer_state,   empty_init, "National Semiconductor", "QuizKid Racer (MM5799 version)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW | MACHINE_NODEVICE_LAN )
SYST( 1978, qkspeller, 0,       0,      qkspeller, qkspeller, qkspeller_state, empty_init, "National Semiconductor", "QuizKid Speller", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW ) // ***

SYST( 1977, cambrp,    0,       0,      cambrp,    cambrp,    cambrp_state,    empty_init, "Sinclair Radionics", "Cambridge Programmable", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )

// ***: As far as MAME is concerned, the game is emulated fine. But for it to be playable, it requires interaction
// with other, unemulatable, things eg. game board/pieces, book, playing cards, pen & paper, etc.



hh_e0c6200.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:digshadow, Segher, azya
/*******************************************************************************

Seiko Epson E0C6200-based handhelds, mostly electronic keychain toys from the
late-1990s. The first Tamagotchi games are on this MCU.

These were meant to stay on 24/7, so make sure to use save states if you want
to play the games for a longer time or remember high scores.

For most of the games, external artwork is required for the background inlays.
For the drivers that don't have an SVG screen, use -prescale or -nofilter to
disable bilinear filtering.

TODO:
- in stackch, you can still move around when the game is paused, maybe BTANB?
- digimon external port is unemulated
- alienfev unmapped reads/writes, or are they harmless?
- SVGs could be more accurate? Instead of 1:1 scans like with Game & Watch,
  they were created by tracing segments by hand from macro photos
- redo tamamot SVG, LCD was not available and azya redrew it from online photos
- add LCD deflicker like hh_sm510? see venusdm for example
- hook up LCD contrast, stackch and alienfev support user-defined contrast,
  but it doesn't look like any game uses it for eg. fade-out

*******************************************************************************/

#include "emu.h"

#include "cpu/e0c6200/e0c6s46.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

// internal artwork
#include "hh_e0c6200_lcd.lh"


namespace {

class hh_e0c6200_state : public driver_device
{
public:
	hh_e0c6200_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_out_x(*this, "%u.%u", 0U, 0U)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(input_changed);
	DECLARE_INPUT_CHANGED_MEMBER(reset_button);

protected:
	virtual void machine_start() override ATTR_COLD;

	void lcd_segment_w(offs_t offset, u8 data) { m_out_x[offset & 0xf][offset >> 4] = data; }

	required_device<e0c6s46_device> m_maincpu;
	output_finder<16, 51> m_out_x; // max 16 * 51
};

void hh_e0c6200_state::machine_start()
{
	m_out_x.resolve();
}



/*******************************************************************************

  Helper Functions

*******************************************************************************/

// generic input handlers

#define PORT_CHANGED_CB(x) \
	PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_e0c6200_state::input_changed), E0C6S46_LINE_K00 + x)

INPUT_CHANGED_MEMBER(hh_e0c6200_state::input_changed)
{
	// inputs are hooked up backwards here, because MCU input ports are all tied to its interrupt controller
	m_maincpu->set_input_line(param, newval ? ASSERT_LINE : CLEAR_LINE);
}

INPUT_CHANGED_MEMBER(hh_e0c6200_state::reset_button)
{
	// when an input is directly wired to MCU RESET pin
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}



/*******************************************************************************

  Minidrivers (subclass, I/O, Inputs, Machine Config, ROM Defs)

*******************************************************************************/

/*******************************************************************************

  Bandai Tamagotchi (Generation 1)
  * PCB label: TMG-M1
  * Seiko Epson E0C6S46 MCU under epoxy
  * 32*16 LCD screen + 8 custom segments, 1-bit sound

  Generation 2 is on the exact same hardware. It's nearly the same game, they
  only changed the graphics.

*******************************************************************************/

class tama_state : public hh_e0c6200_state
{
public:
	tama_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_e0c6200_state(mconfig, type, tag)
	{ }

	void tama(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tama )
	PORT_START("K0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_CHANGED_CB(0)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_CHANGED_CB(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CHANGED_CB(2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

// config

void tama_state::tama(machine_config &config)
{
	// basic machine hardware
	E0C6S46(config, m_maincpu, 32.768_kHz_XTAL);
	m_maincpu->write_r<4>().set("speaker", FUNC(speaker_sound_device::level_w)).bit(3);
	m_maincpu->write_segs().set(FUNC(tama_state::lcd_segment_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1119, 1080);
	screen.set_visarea_full();

	config.set_default_layout(layout_hh_e0c6200_lcd);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( tama )
	ROM_REGION( 0x3000, "maincpu", 0 )
	ROM_LOAD( "tama.bin", 0x0000, 0x3000, CRC(5c864cb1) SHA1(4b4979cf92dc9d2fb6d7295a38f209f3da144f72) )

	ROM_REGION( 0x3000, "maincpu:test", 0 )
	ROM_LOAD( "test.bin", 0x0000, 0x3000, CRC(4372220e) SHA1(6e13d015113e16198c0059b9d0c38d7027ae7324) ) // this rom is on the die too, test pin enables it?

	ROM_REGION( 139072, "screen", 0)
	ROM_LOAD( "tama.svg", 0, 139072, CRC(9468b964) SHA1(ab49471db21a00a3b3a68da39c40da69da5d7e1b) )
ROM_END

ROM_START( tamag2 )
	ROM_REGION( 0x3000, "maincpu", 0 )
	ROM_LOAD( "tamag2.bin", 0x0000, 0x3000, CRC(9f97539e) SHA1(09e5101b37636a314fc599d5d69b4846721b3c88) )

	ROM_REGION( 139072, "screen", 0)
	ROM_LOAD( "tama.svg", 0, 139072, CRC(9468b964) SHA1(ab49471db21a00a3b3a68da39c40da69da5d7e1b) )
ROM_END





/*******************************************************************************

  Bandai Tamagotchi Angel (Tenshitchi no Tamagotchi (aka Angel Gotch) in Japan)
  * PCB label: TAL-1, 00-83520-001
  * Seiko Epson E0C6S48 under epoxy
  * 32*16 LCD screen + 8 custom segments, 1-bit sound

  Mothra no Tamagotchi is on similar hardware.

*******************************************************************************/

class tamaang_state : public hh_e0c6200_state
{
public:
	tamaang_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_e0c6200_state(mconfig, type, tag)
	{ }

	void tamaang(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tamaang )
	PORT_INCLUDE( tama )

	PORT_MODIFY("K0")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_CHANGED_CB(3) PORT_NAME("Vibration Sensor")
INPUT_PORTS_END

// config

void tamaang_state::tamaang(machine_config &config)
{
	// basic machine hardware
	E0C6S48(config, m_maincpu, 32.768_kHz_XTAL);
	m_maincpu->set_osc3(1'000'000);
	m_maincpu->write_r<4>().set("speaker", FUNC(speaker_sound_device::level_w)).bit(3);
	m_maincpu->write_segs().set(FUNC(tamaang_state::lcd_segment_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1119, 1080);
	screen.set_visarea_full();

	config.set_default_layout(layout_hh_e0c6200_lcd);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( tamaang )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "tamaang.bin", 0x0000, 0x4000, CRC(87bcb59f) SHA1(f5899bb7717756ac581451cf16cf97d909961c5c) )

	ROM_REGION( 139978, "screen", 0)
	ROM_LOAD( "tamaang.svg", 0, 139978, CRC(76f27f06) SHA1(b416275a12173316e053fa994c5fd68a4d5c1a5c) )
ROM_END

ROM_START( tamamot )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "tamamot.bin", 0x0000, 0x4000, CRC(85e4bee9) SHA1(74c1f6761724b7cbda8bca3113db78586b786d2d) )

	ROM_REGION( 138289, "screen", 0)
	ROM_LOAD( "tamamot.svg", 0, 138289, CRC(4e8210c2) SHA1(522536ae5bf744889c0d028c3a292bdf649f81e3) )
ROM_END





/*******************************************************************************

  Bandai Digital Monster (retroactively called Ver. 1)
  * PCB label: TDM-1, 00-83830-001
  * Seiko Epson E0C6S48 MCU under epoxy
  * external port, for connecting to another Digimon handheld
  * 32*16 LCD screen + 8 custom segments, 1-bit sound

  The sequels (Ver. 2 to Ver. 4) are on the same hardware, and are compatible
  with each other for battle mode.

*******************************************************************************/

class digimon_state : public hh_e0c6200_state
{
public:
	digimon_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_e0c6200_state(mconfig, type, tag)
	{ }

	void digimon(machine_config &config);

private:
	u8 extport_r();
	void extport_w(u8 data);
};

// handlers

u8 digimon_state::extport_r()
{
	// P20: external port data from linked digimon
	return 0xf;
}

void digimon_state::extport_w(u8 data)
{
	// P20: external port data to linked digimon
}

// inputs

static INPUT_PORTS_START( digimon )
	PORT_INCLUDE( tama )

	PORT_START("RESET")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_ON ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(digimon_state::reset_button), 0) PORT_NAME("Reset")
INPUT_PORTS_END

// config

void digimon_state::digimon(machine_config &config)
{
	// basic machine hardware
	E0C6S48(config, m_maincpu, 32.768_kHz_XTAL);
	m_maincpu->set_osc3(1'000'000);
	m_maincpu->read_p<2>().set(FUNC(digimon_state::extport_r));
	m_maincpu->write_p<2>().set(FUNC(digimon_state::extport_w));
	m_maincpu->write_r<4>().set("speaker", FUNC(speaker_sound_device::level_w)).bit(3);
	m_maincpu->write_segs().set(FUNC(digimon_state::lcd_segment_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1113, 1080);
	screen.set_visarea_full();

	config.set_default_layout(layout_hh_e0c6200_lcd);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( digimon )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "digimon.bin", 0x0000, 0x4000, CRC(08ffac1b) SHA1(1dde9b0aa81c8f4a1e22d3a79d4743833fc6cba7) )

	ROM_REGION( 158040, "screen", 0)
	ROM_LOAD( "digimon.svg", 0, 158040, CRC(0a6ad374) SHA1(e0bafc2c907dbe49e366ff76f2aef622e058f915) )
ROM_END

ROM_START( digimonv2 )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "digimonv2.bin", 0x0000, 0x4000, CRC(19a9e54e) SHA1(ab860ca9f31f478532122cb9d20f59964a080a27) )

	ROM_REGION( 158040, "screen", 0)
	ROM_LOAD( "digimon.svg", 0, 158040, CRC(0a6ad374) SHA1(e0bafc2c907dbe49e366ff76f2aef622e058f915) )
ROM_END

ROM_START( digimonv3 )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "digimonv3.bin", 0x0000, 0x4000, CRC(3000cf30) SHA1(0acd50e623e20d857e13bae150ac03405896cf2b) )

	ROM_REGION( 158040, "screen", 0)
	ROM_LOAD( "digimon.svg", 0, 158040, CRC(0a6ad374) SHA1(e0bafc2c907dbe49e366ff76f2aef622e058f915) )
ROM_END





/*******************************************************************************

  Epoch Chibi Pachi: Alien Fever
  * Seiko Epson E0C6S46 MCU
  * 39*16 LCD screen, 1-bit sound

  It's a Pachislot keychain game, the MCU constantly runs on the higher-speed OSC3.

*******************************************************************************/

class alienfev_state : public hh_e0c6200_state
{
public:
	alienfev_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_e0c6200_state(mconfig, type, tag)
	{ }

	void alienfev(machine_config &config);
};

// inputs

static INPUT_PORTS_START( alienfev )
	PORT_START("K0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_CHANGED_CB(0) PORT_NAME("Mode")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_CHANGED_CB(1) PORT_NAME("Select")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(2) PORT_NAME("Sound")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CHANGED_CB(3) PORT_NAME("Handle")
INPUT_PORTS_END

// config

void alienfev_state::alienfev(machine_config &config)
{
	// basic machine hardware
	E0C6S46(config, m_maincpu, 32.768_kHz_XTAL);
	m_maincpu->set_osc3(1'000'000);
	m_maincpu->write_r<4>().set("speaker", FUNC(speaker_sound_device::level_w)).bit(3);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(0));
	screen.set_size(39, 16);
	screen.set_visarea_full();
	screen.set_screen_update(m_maincpu, FUNC(e0c6s46_device::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	config.set_default_layout(layout_hh_e0c6200_lcd);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( alienfev )
	ROM_REGION( 0x3000, "maincpu", 0 )
	ROM_LOAD( "alienfev.bin", 0x0000, 0x3000, CRC(e561599c) SHA1(7927e198f8989861ba057150e59d1f4ad403c1d2) )
ROM_END





/*******************************************************************************

  Nikko Beans Collection: Venus Diet Monogatari
  * Seiko Epson E0C6S46 MCU
  * 32*20 LCD screen, 1-bit sound

*******************************************************************************/

class venusdm_state : public hh_e0c6200_state
{
public:
	venusdm_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_e0c6200_state(mconfig, type, tag)
	{ }

	void venusdm(machine_config &config);

private:
	// reorder pixel coordinates
	void pixel_callback(int &dx, int &dy) { int x = dx; dx = dy | (dx / 20) << 4; dy = x % 20; }
};

// inputs

static INPUT_PORTS_START( venusdm )
	PORT_START("K1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_CHANGED_CB(4)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_CHANGED_CB(5)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CHANGED_CB(6)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

// config

void venusdm_state::venusdm(machine_config &config)
{
	// basic machine hardware
	E0C6S46(config, m_maincpu, 32.768_kHz_XTAL);
	m_maincpu->write_r<4>().set("speaker", FUNC(speaker_sound_device::level_w)).bit(3);
	m_maincpu->set_pixel_callback(FUNC(venusdm_state::pixel_callback));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(0));
	screen.set_size(32, 20);
	screen.set_visarea_full();
	screen.set_screen_update(m_maincpu, FUNC(e0c6s46_device::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	config.set_default_layout(layout_hh_e0c6200_lcd);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( venusdm )
	ROM_REGION( 0x3000, "maincpu", 0 )
	ROM_LOAD( "venusdm.bin", 0x0000, 0x3000, CRC(2228b081) SHA1(22f6a2ede6259e76f1c8b9b50171c54d8a7de502) )
ROM_END





/*******************************************************************************

  Tandy (Radio Shack division) Stack Challenge (model 60-2247)
  * Seiko Epson E0C6S46 MCU
  * 10*21 LCD screen + custom segments, 1-bit sound

  It's a brick game clone. The game is supposedly from 1991 (it's included in
  the 1992 Radio Shack catalog). Did E0C6S46 exist already, or is this a newer
  revision?

  BTANB:
  - it doesn't allow 2 button presses at the same time, making fast gameplay
    impossible (eg. left or right + rotate button)

*******************************************************************************/

class stackch_state : public hh_e0c6200_state
{
public:
	stackch_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_e0c6200_state(mconfig, type, tag)
	{ }

	void stackch(machine_config &config);
};

// inputs

static INPUT_PORTS_START( stackch )
	PORT_START("K0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_16WAY PORT_CHANGED_CB(0) PORT_NAME("Left / Level")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_16WAY PORT_CHANGED_CB(1) PORT_NAME("Down / Start")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_16WAY PORT_CHANGED_CB(2) PORT_NAME("Right / Height")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CHANGED_CB(3) PORT_NAME("Rotate / Contrast")

	PORT_START("K1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_ON ) PORT_CHANGED_CB(4) PORT_NAME("On / Off")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_SELECT ) PORT_CHANGED_CB(5) PORT_NAME("Pause")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(6) PORT_NAME("Sound")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

// config

void stackch_state::stackch(machine_config &config)
{
	// basic machine hardware
	E0C6S46(config, m_maincpu, 32.768_kHz_XTAL);
	m_maincpu->set_osc3(1'000'000);
	m_maincpu->write_r<4>().set("speaker", FUNC(speaker_sound_device::level_w)).bit(3);
	m_maincpu->write_segs().set(FUNC(stackch_state::lcd_segment_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(856, 1080);
	screen.set_visarea_full();

	config.set_default_layout(layout_hh_e0c6200_lcd);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( stackch )
	ROM_REGION( 0x3000, "maincpu", 0 )
	ROM_LOAD( "stackch.bin", 0x0000, 0x3000, CRC(28b9310a) SHA1(52b80d70aa7fc3b6323799403b3aba0e3d957f3b) )

	ROM_REGION( 133022, "screen", 0)
	ROM_LOAD( "stackch.svg", 0, 133022, CRC(caf74ad4) SHA1(2f2e836b0efe377305bb113a550f1cb4ec939273) )
ROM_END

} // anonymous namespace



/*******************************************************************************

  Game driver(s)

*******************************************************************************/

//    YEAR  NAME       PARENT   COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1997, tama,      0,       0,      tama,     tama,     tama_state,     empty_init, "Bandai", "Tamagotchi (Gen. 1, World)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1997, tamag2,    0,       0,      tama,     tama,     tama_state,     empty_init, "Bandai", "Tamagotchi (Gen. 2, Japan)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1997, tamaang,   0,       0,      tamaang,  tamaang,  tamaang_state,  empty_init, "Bandai", "Tenshitchi no Tamagotchi (Japan)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1997, tamamot,   0,       0,      tamaang,  tama,     tamaang_state,  empty_init, "Bandai", "Mothra no Tamagotchi (Japan)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1997, digimon,   0,       0,      digimon,  digimon,  digimon_state,  empty_init, "Bandai", "Digital Monster (Japan)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK | MACHINE_NODEVICE_LAN )
SYST( 1998, digimonv2, 0,       0,      digimon,  digimon,  digimon_state,  empty_init, "Bandai", "Digital Monster Ver. 2 (Japan)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK | MACHINE_NODEVICE_LAN )
SYST( 1998, digimonv3, 0,       0,      digimon,  digimon,  digimon_state,  empty_init, "Bandai", "Digital Monster Ver. 3 (Japan)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK | MACHINE_NODEVICE_LAN )

SYST( 1997, alienfev,  0,       0,      alienfev, alienfev, alienfev_state, empty_init, "Epoch", "Chibi Pachi: Alien Fever", MACHINE_SUPPORTS_SAVE )

SYST( 1997, venusdm,   0,       0,      venusdm,  venusdm,  venusdm_state,  empty_init, "Nikko", "Beans Collection: Venus Diet Monogatari", MACHINE_SUPPORTS_SAVE )

SYST( 1991, stackch,   0,       0,      stackch,  stackch,  stackch_state,  empty_init, "Tandy Corporation", "Stack Challenge", MACHINE_SUPPORTS_SAVE )



hh_hmcs40.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Kevin Horton
/*******************************************************************************

Hitachi HMCS40 MCU tabletops/handhelds or other simple devices, most of them
are VFD electronic games/toys.

known chips:

  serial  device   etc.
----------------------------------------------------------------
 @A07     HD38750  1979, Bambino Knock-Em Out Boxing (ET-06B)
 @A08     HD38750  1979, Bambino Dribble Away Basketball (ET-05)
 @A45     HD38750  1981, VTech Invaders
 *A56     HD38750  1981, Actronics(Hanzawa) Twinvader (small brown version)
 *A58     HD38750  1981, Actronics(Hanzawa) Challenge Racer/Ludotronic(Hanzawa) Grand Prix Turbo
 *A62     HD38750  1982, Actronics(Hanzawa) Pack'n Maze
 @A67     HD38750  1982, Romtec Pucki & Monsters (ET-803)

 @A04     HD38800  1980, Gakken Heiankyo Alien
  A16     HD38800  1981, Entex Select-A-Game cartridge: Basketball 3 -> entex/sag.cpp
 *A20     HD38800  1981, Entex Super Space Invader 2
 @A25     HD38800  1981, Coleco Alien Attack
 @A27     HD38800  1981, Bandai Packri Monster
 @A31     HD38800  1981, Entex Select-A-Game cartridge: Space Invader 2 -> entex/sag.cpp - also used in 2nd version of Super Space Invader 2!
  A36     HD38800  1981, Entex Select-A-Game cartridge: Pac-Man 2       -> "
  A37     HD38800  1981, Entex Select-A-Game cartridge: Baseball 4      -> "
  A38     HD38800  1981, Entex Select-A-Game cartridge: Pinball         -> "
 *A41     HD38800  1982, Gakken Puck Monster
 *A42     HD38800  1981, Akai GX-77
 *A51     HD38800  1981, Actronics(Hanzawa) Twinvader (larger white version)
 @A70     HD38800  1982, Coleco Galaxian
 @A73     HD38800  1982, Mattel Star Hawk (PT-317B)
 @A77     HD38800  1982, Bandai Frisky Tom (PT-327A)
 *A87     HD38800  1982, Gakken Paint Roller
 @A88     HD38800  1982, Tomy Tron (THN-02)
 @B01     HD38800  1982, Gakken Crazy Kong
 @B19     HD38800  1982, Bandai Zaxxon
 @B23     HD38800  1982, Tomy Kingman (THF-01II)
 @B24     HD38800  1982, Actronics(Hanzawa) Wanted G-Man
 @B29     HD38800  1984, Tomy Bombman
 *B31     HD38800  1983, Romtec Frog Prince (ET-806)
 *B35     HD38800  1983, Bandai Gundam vs Gelgoog Zaku
 @B42     HD38800  1983, Bandai Kiteyo Parman
 @B43     HD38800  1983, Bandai Dokodemo Dorayaki Doraemon (PT-412)
 @B48     HD38800  1983, Bandai Toukon Juohmaru
 @B52     HD38800  1983, Bandai Ultraman Monster Battle (PT-424)

 @A09     HD38820  1980, Mattel World Championship Baseball
 @A13     HD38820  1981, Entex Galaxian 2
 @A23     HD38820  1981, Entex Pac Man 2
 @A28     HD38820  1981, Coleco Pac-Man (ver 1)
 @A29     HD38820  1981, Coleco Pac-Man (ver 2)
 @A32     HD38820  1982, Gakken Super Cobra
 *A38     HD38820  1982, Entex Crazy Climber
 @A42     HD38820  1982, Entex Stargate
 @A43     HD38820  1982, Entex Turtles
 @A45     HD38820  1982, Coleco Donkey Kong
 @A49     HD38820  1983, Bandai Zackman
 @L53     HD38820  1983, Gakken Defender
 @A61     HD38820  1983, Coleco Ms. Pac-Man
 *A62     HD38820  1983, Coleco Zaxxon
 @A63     HD38820  1983, Bandai Pengo
 @A65     HD38820  1983, Bandai Burger Time (PT-389)
 @A69     HD38820  1983, Gakken Dig Dug
 @A70     HD38820  1983, Parker Brothers Q*Bert
 @A75     HD38820  1983, Bandai Go Go Dynaman
 @A85     HD38820  1984, Bandai Machine Man (PT-438)
 @A88     HD38820  1984, Bandai Pair Match (PT-460) (1/2)
 @A89     HD38820  1984, Bandai Pair Match (PT-460) (2/2)

  A34     HD44801  1981, SciSys Mini Chess -> saitek/minichess.cpp
  A50     HD44801  1981, CXG Sensor Computachess -> cxg/computachess.cpp
  A75     HD44801  1982, Alpha 8201 protection MCU -> alpha/alpha8201.*
  A85     HD44801  1982, SciSys Travel Sensor Chess -> saitek/tschess.cpp
 *A92     HD44801  1982, SciSys Play Bridge Computer (have dump)
  B35     HD44801  1983, Alpha 8302 protection MCU (see 8201)
  B42     HD44801  1983, Alpha 8303 protection MCU (see 8201)
 *B43     HD44801  1983, Alpha 8304 protection MCU (see 8201)
  C57     HD44801  1985, Alpha 8505 protection MCU (see 8201)
  C89     HD44801  1985, CXG Sensor Computachess (1985 version) -> cxg/computachess.cpp

 *A86     HD44820  1983, Chess King Pocket Micro / Mighty Midget
 *B46     HD44820  1984, Chess King Pocket Micro / Mighty Midget
  B63     HD44820  1986, CXG Pocketchess -> cxg/pchess.cpp

 *A13     HD44840  1982, CXG Computachess II
  A14     HD44840  1982, CXG Computachess II -> cxg/computachess2.cpp

  B29     HD44860  1987, Micro-Concepts Diamond Bridge Computer -> handheld/dbridgec.cpp
 *B55     HD44860  1987, Saitek Pro Bridge 100 (have dump)

 *A04     HD44868  1984, SciSys Rapier
  A07     HD44868  1984, Chess King Pocket Micro De-Luxe -> chessking/pmicrodx.cpp
  A12     HD44868  1985, SciSys Electronic Trio / Kasparov Pocket Chess -> saitek/electrio.cpp
  A14     HD44868  1986, SciSys Kasparov Mk 12 / Kasparov Pocket Plus -> saitek/electrio.cpp
  A16     HD44868  1988, Saitek Pocket Checkers -> saitek/electrio.cpp

  (* means undumped unless noted, @ denotes it's in this driver)

================================================================================

ROM source notes when dumped from another title, but confident it's the same:
- gckong: CGL Super Kong
- ggdman: Bandai Kampf der Monster
- ghalien: CGL Earth Invaders
- kingman: Tandy Kingman
- wantgman: Ludotronic Operation 'Z'
- zackman: Tandy Zackman

TODO:
- cgalaxn netlist sound for alien attack sweep sound (MAME doesn't support N13T1?)
- epacman2 booting the game in demo mode, pacman should take the shortest route
  to the upper-left power pill, followed by going to the top-right power pill:
  mcu cycle/interrupt timing related
- kevtris's HMCS40 ROM dumps are incomplete, missing MCU factory test code from
  the 2nd half of the ROM, none of the games access it though and it's impossible
  to execute unless the chip is in testmode.
- Though very uncommon when compared to games with LED/lamp display, some games
  may deliberately change VFD plate brightness by strobing it longer/shorter,
  eg. cgalaxn when a ship explodes.
- bzaxxon 3D effect is difficult to simulate
- improve/redo SVG for: bzaxxon, bbtime
- add SVG for: ggdman, ktparman, tkjmaru, gdefender, bombman, wantgman, puckimon

*******************************************************************************/

#include "emu.h"

#include "cpu/hmcs40/hmcs40.h"
#include "cpu/cop400/cop400.h"
#include "machine/gen_latch.h"
#include "machine/timer.h"
#include "sound/flt_vol.h"
#include "sound/spkrdev.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "bambball.lh"
#include "bmboxing.lh"
#include "gckong.lh"
#include "mwcbaseb.lh"
#include "msthawk.lh"
#include "packmon.lh"
#include "pairmtch.lh"

#include "hh_hmcs40_test.lh" // common test-layout - no svg artwork(yet), use external artwork


namespace {

class hh_hmcs40_state : public driver_device
{
public:
	hh_hmcs40_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_speaker(*this, "speaker"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(single_interrupt_line);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// devices
	required_device<hmcs40_cpu_device> m_maincpu;
	optional_device<pwm_display_device> m_display;
	optional_device<speaker_sound_device> m_speaker;
	optional_ioport_array<7> m_inputs; // max 7

	// misc common
	u8 m_r[8] = { };   // MCU R ports write data (optional)
	u16 m_d = 0;       // MCU D port write data (optional)
	u8 m_int[2] = { }; // MCU INT0/1 pins state
	u16 m_inp_mux = 0; // multiplexed inputs mask

	u32 m_grid = 0;    // VFD current row data
	u64 m_plate = 0;   // VFD current column data

	u16 read_inputs(int columns);
	void refresh_interrupts(void);
	void set_interrupt(int line, int state);
};


// machine start/reset

void hh_hmcs40_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_r));
	save_item(NAME(m_int));
	save_item(NAME(m_d));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_grid));
	save_item(NAME(m_plate));
}

void hh_hmcs40_state::machine_reset()
{
	refresh_interrupts();
}



/*******************************************************************************

  Helper Functions

*******************************************************************************/

// generic input handlers

u16 hh_hmcs40_state::read_inputs(int columns)
{
	u16 ret = 0;

	// read selected input rows
	for (int i = 0; i < columns; i++)
		if (BIT(m_inp_mux, i))
			ret |= m_inputs[i]->read();

	return ret;
}


// interrupt handling

void hh_hmcs40_state::refresh_interrupts()
{
	for (int i = 0; i < 2; i++)
		m_maincpu->set_input_line(i, m_int[i] ? ASSERT_LINE : CLEAR_LINE);
}

void hh_hmcs40_state::set_interrupt(int line, int state)
{
	line = line ? 1 : 0;
	state = state ? 1 : 0;

	if (state != m_int[line])
	{
		if (machine().phase() >= machine_phase::RESET)
			m_maincpu->set_input_line(line, state ? ASSERT_LINE : CLEAR_LINE);
		m_int[line] = state;
	}
}

INPUT_CHANGED_MEMBER(hh_hmcs40_state::single_interrupt_line)
{
	set_interrupt((int)param, newval);
}



/*******************************************************************************

  Minidrivers (subclass, I/O, Inputs, Machine Config, ROM Defs)

*******************************************************************************/

/*******************************************************************************

  Actronics / Hanzawa Wanted G-Man
  * Hitachi HD38800B24 MCU, 1-bit sound
  * cyan/red VFD

  known releases:
  - World: Wanted G-Man, published by Actronics
  - France: Operation 'Z', published by Ludotronic

*******************************************************************************/

class wantgman_state : public hh_hmcs40_state
{
public:
	wantgman_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void wantgman(machine_config &config);

private:
	void update_display();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
	u16 input_r();
};

// handlers

void wantgman_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void wantgman_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R3x: vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void wantgman_state::grid_w(u16 data)
{
	// D0: speaker out
	m_speaker->level_w(data & 1);

	// D1-D6: input mux
	m_inp_mux = data >> 1 & 0x3f;

	// D1-D9,D13,D14: vfd grid
	m_grid = (data >> 1 & 0x1ff) | (data >> 4 & 0x600);
	update_display();
}

u16 wantgman_state::input_r()
{
	// D15: multiplexed inputs
	return read_inputs(6) & 0x8000;
}

// inputs

static INPUT_PORTS_START( wantgman )
	PORT_START("IN.0") // D1 line D15
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )

	PORT_START("IN.1") // D2 line D15
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )

	PORT_START("IN.2") // D3 line D15
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )

	PORT_START("IN.3") // D4 line D15
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )

	PORT_START("IN.4") // D5 line D15
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Skill / Start / Jump")

	PORT_START("IN.5") // D6 line D15
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Demo")
INPUT_PORTS_END

// config

void wantgman_state::wantgman(machine_config &config)
{
	// basic machine hardware
	HD38800(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(wantgman_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(wantgman_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(wantgman_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(wantgman_state::plate_w));
	m_maincpu->write_d().set(FUNC(wantgman_state::grid_w));
	m_maincpu->read_d().set(FUNC(wantgman_state::input_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(11, 16);
	config.set_default_layout(layout_hh_hmcs40_test);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( wantgman )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38800b24", 0x0000, 0x1000, CRC(ad1dfb26) SHA1(f2ef0ec98116a8a38e97299e3e3b2b0129d78c04) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 100000, "screen", 0)
	ROM_LOAD( "wantgman.svg", 0, 100000, NO_DUMP )
ROM_END





/*******************************************************************************

  Bambino Dribble Away Basketball (manufactured in Japan)
  * PCB label: Emix Corp. ET-05
  * Hitachi HD38750A08 MCU, 1-bit sound
  * cyan VFD Emix-106, with bezel overlay
  * color overlay: green (optional)

*******************************************************************************/

class bambball_state : public hh_hmcs40_state
{
public:
	bambball_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void bambball(machine_config &config);

private:
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
	u8 input_r();
};

// handlers

void bambball_state::plate_w(offs_t offset, u8 data)
{
	// R1x-R3x(,D0-D3): vfd plate
	int shift = (offset - 1) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	// update display
	m_display->matrix(m_grid, m_plate);
}

void bambball_state::grid_w(u16 data)
{
	// D4: speaker out
	m_speaker->level_w(data >> 4 & 1);

	// D7-D10: input mux
	m_inp_mux = data >> 7 & 0xf;

	// D7-D15: vfd grid
	m_grid = data >> 7 & 0x1ff;

	// D0-D3: vfd plate (update display there)
	plate_w(3 + 1, data & 0xf);
}

u8 bambball_state::input_r()
{
	// R0x: multiplexed inputs
	return read_inputs(4);
}

// inputs

static INPUT_PORTS_START( bambball )
	PORT_START("IN.0") // D7 port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Dribble Low")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Dribble Medium")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Dribble High")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_NAME("Shoot")

	PORT_START("IN.1") // D8 port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // D9 port R0x
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_NAME("Display")

	PORT_START("IN.3") // D10 port R0x
	PORT_CONFNAME( 0x07, 0x01, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_CONFSETTING(    0x04, "3" )
	PORT_CONFNAME( 0x08, 0x08, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x08, "1" )
	PORT_CONFSETTING(    0x00, "2" )
INPUT_PORTS_END

// config

void bambball_state::bambball(machine_config &config)
{
	// basic machine hardware
	HD38750(config, m_maincpu, 400000); // approximation
	m_maincpu->read_r<0>().set(FUNC(bambball_state::input_r));
	m_maincpu->write_r<1>().set(FUNC(bambball_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(bambball_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(bambball_state::plate_w));
	m_maincpu->write_d().set(FUNC(bambball_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 478);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(9, 16);
	config.set_default_layout(layout_bambball);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( bambball )
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38750a08", 0x0000, 0x0800, CRC(907fef18) SHA1(73fe7ca7c6332268a3a9abc5ac88ada2991012fb) )
	ROM_CONTINUE(           0x0f00, 0x0080 )

	ROM_REGION( 281982, "screen", 0)
	ROM_LOAD( "bambball.svg", 0, 281982, CRC(df113d8a) SHA1(cc7bd74f805cced9c14faae3cf0e3f2c85a8a001) )
ROM_END





/*******************************************************************************

  Bambino Knock-Em Out Boxing
  * PCB label: Emix Corp. ET-06B
  * Hitachi HD38750A07 MCU, 1-bit sound
  * cyan VFD Emix-103, with blue or transparent window

*******************************************************************************/

class bmboxing_state : public hh_hmcs40_state
{
public:
	bmboxing_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void bmboxing(machine_config &config);

private:
	void update_display();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
	u8 input_r();
};

// handlers

void bmboxing_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void bmboxing_state::plate_w(offs_t offset, u8 data)
{
	// R1x-R3x: vfd plate
	int shift = (offset - 1) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void bmboxing_state::grid_w(u16 data)
{
	// D13: speaker out
	m_speaker->level_w(data >> 13 & 1);

	// D9-D12: input mux
	m_inp_mux = data >> 9 & 0xf;

	// D4-D12: vfd grid
	m_grid = data >> 4 & 0x1ff;
	update_display();
}

u8 bmboxing_state::input_r()
{
	// R0x: multiplexed inputs
	return read_inputs(4);
}

// inputs

/* physical button layout and labels are like this:

    * left = P2 side *                                       * right = P1 side *

    [ BACK ]  [ HIGH ]        (players sw)                   [ HIGH ]  [ BACK ]
                              1<--->2         [START/
    [NORMAL]  [MEDIUM]                         RESET]        [MEDIUM]  [NORMAL]
                              1<---OFF--->2
    [ DUCK ]  [ LOW  ]        (skill lvl sw)                 [ LOW  ]  [ DUCK ]
*/

static INPUT_PORTS_START( bmboxing )
	PORT_START("IN.0") // D9 port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_U) PORT_NAME("P1 Punch High")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_J) PORT_NAME("P1 Punch Medium")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_M) PORT_NAME("P1 Punch Low")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // D10 port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_K) PORT_NAME("P1 Position Normal")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_I) PORT_NAME("P1 Position Back")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_COMMA) PORT_NAME("P1 Position Ducking")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // D11 port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("P2 Punch High")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("P2 Punch Medium")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME("P2 Punch Low")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // D12 port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("P2 Position Normal")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("P2 Position Back")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Z) PORT_NAME("P2 Position Ducking")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // port D
	PORT_CONFNAME( 0x0001, 0x0000, DEF_STR( Players ) )
	PORT_CONFSETTING(      0x0000, "1" )
	PORT_CONFSETTING(      0x0001, "2" )
	PORT_CONFNAME( 0x0002, 0x0000, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(      0x0000, "1" )
	PORT_CONFSETTING(      0x0002, "2" )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_START )
	PORT_BIT( 0xfff8, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void bmboxing_state::bmboxing(machine_config &config)
{
	// basic machine hardware
	HD38750(config, m_maincpu, 400000); // approximation
	m_maincpu->read_r<0>().set(FUNC(bmboxing_state::input_r));
	m_maincpu->write_r<1>().set(FUNC(bmboxing_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(bmboxing_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(bmboxing_state::plate_w));
	m_maincpu->write_d().set(FUNC(bmboxing_state::grid_w));
	m_maincpu->read_d().set_ioport("IN.4");

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 529);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(9, 12);
	config.set_default_layout(layout_bmboxing);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( bmboxing )
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38750a07", 0x0000, 0x0800, CRC(7f33e259) SHA1(c5fcdd6bf060c96666354f09f0570c754f6ed4e0) )
	ROM_CONTINUE(           0x0f00, 0x0080 )

	ROM_REGION( 257149, "screen", 0)
	ROM_LOAD( "bmboxing.svg", 0, 257149, CRC(f8d38287) SHA1(bfbd791c237a89d4021dd81b7f89326cac19ab03) )
ROM_END





/*******************************************************************************

  Bandai Frisky Tom (manufactured in Japan)
  * PCB label: Kaken Corp., PT-327A
  * Hitachi HD38800A77 MCU, 1-bit sound
  * cyan/red/green VFD Futaba DM-43ZK 2E

*******************************************************************************/

class bfriskyt_state : public hh_hmcs40_state
{
public:
	bfriskyt_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void bfriskyt(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(input_changed) { update_int1(); }

private:
	void update_int1();
	void update_display();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
};

// handlers

void bfriskyt_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void bfriskyt_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R3x: vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void bfriskyt_state::grid_w(u16 data)
{
	// D6: speaker out
	m_speaker->level_w(data >> 6 & 1);

	// D11-D15: input mux
	u8 inp_mux = data >> 11 & 0x1f;
	if (inp_mux != m_inp_mux)
	{
		m_inp_mux = inp_mux;
		update_int1();
	}

	// D8-D15: vfd grid
	m_grid = data >> 8 & 0xff;

	// D0-D5: vfd plate
	m_plate = (m_plate & 0x00ffff) | (data << 16 & 0x3f0000);
	update_display();
}

void bfriskyt_state::update_int1()
{
	// INT1 on multiplexed inputs
	set_interrupt(1, read_inputs(5));
}

// inputs

static INPUT_PORTS_START( bfriskyt )
	PORT_START("IN.0") // D11 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bfriskyt_state::input_changed), 0)

	PORT_START("IN.1") // D12 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bfriskyt_state::input_changed), 0)

	PORT_START("IN.2") // D13 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bfriskyt_state::input_changed), 0)

	PORT_START("IN.3") // D14 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bfriskyt_state::input_changed), 0)

	PORT_START("IN.4") // D15 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bfriskyt_state::input_changed), 0)

	PORT_START("IN.5") // INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_hmcs40_state::single_interrupt_line), 0)
INPUT_PORTS_END

// config

void bfriskyt_state::bfriskyt(machine_config &config)
{
	// basic machine hardware
	HD38800(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(bfriskyt_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(bfriskyt_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(bfriskyt_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(bfriskyt_state::plate_w));
	m_maincpu->write_d().set(FUNC(bfriskyt_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 675);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(8, 22);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( bfriskyt )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38800a77", 0x0000, 0x1000, CRC(a2445c4f) SHA1(0aaccfec90b66d27dae194d4462d88e654c41578) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 413553, "screen", 0)
	ROM_LOAD( "bfriskyt.svg", 0, 413553, CRC(8c7e96f9) SHA1(8aeb5266401850e5defd69ce08071f0536c043b7) )
ROM_END





/*******************************************************************************

  Bandai Packri Monster (manufactured in Japan)
  * PCB label: DM-21ZA2
  * Hitachi HD38800A27 MCU, 1-bit sound
  * cyan/red/green VFD Futaba DM-21ZK 2B, with bezel overlay

  known releases:
  - Japan: FL Packri Monster, published by Bandai
  - USA(World?): Packri Monster, published by Bandai
  - USA/Canada: Hungry Monster, published by Tandy
  - other: Gobble Man/Ogre Monster, published by Tandy

*******************************************************************************/

class packmon_state : public hh_hmcs40_state
{
public:
	packmon_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void packmon(machine_config &config);

private:
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
	u16 input_r();
};

// handlers

void packmon_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R3x(,D0-D3): vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	// update display
	m_display->matrix(m_grid, m_plate);
}

void packmon_state::grid_w(u16 data)
{
	// D4: speaker out
	m_speaker->level_w(data >> 4 & 1);

	// D11-D15: input mux
	m_inp_mux = data >> 11 & 0x1f;

	// D6-D15: vfd grid
	m_grid = data >> 6 & 0x3ff;

	// D0-D3: vfd plate (update display there)
	plate_w(4, data & 0xf);
}

u16 packmon_state::input_r()
{
	// D5: multiplexed inputs
	return read_inputs(5) & 0x20;
}

// inputs

static INPUT_PORTS_START( packmon )
	PORT_START("IN.0") // D11 line D5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_START )

	PORT_START("IN.1") // D12 line D5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )

	PORT_START("IN.2") // D13 line D5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )

	PORT_START("IN.3") // D14 line D5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )

	PORT_START("IN.4") // D15 line D5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
INPUT_PORTS_END

// config

void packmon_state::packmon(machine_config &config)
{
	// basic machine hardware
	HD38800(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(packmon_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(packmon_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(packmon_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(packmon_state::plate_w));
	m_maincpu->write_d().set(FUNC(packmon_state::grid_w));
	m_maincpu->read_d().set(FUNC(packmon_state::input_r));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 680);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(10, 20);
	config.set_default_layout(layout_packmon);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( packmon )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38800a27", 0x0000, 0x1000, CRC(86e09e84) SHA1(ac7d3c43667d5720ca513f8ff51d146d9f2af124) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 224385, "screen", 0)
	ROM_LOAD( "packmon.svg", 0, 224385, CRC(4fe7f59e) SHA1(d31a9ed5bde174df5256a77c9c1cdd6dd3d89c37) )
ROM_END





/*******************************************************************************

  Bandai Zaxxon (manufactured in Japan, licensed from Sega)
  * PCB label: FL Zaxxon
  * Hitachi HD38800B19 MCU, 1-bit sound
  * cyan/red/blue VFD NEC FIP11BM24T no. 4-8, half of it reflected with a
    one-way mirror to give the illusion of a 3D display

*******************************************************************************/

class bzaxxon_state : public hh_hmcs40_state
{
public:
	bzaxxon_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void bzaxxon(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(input_changed) { update_int1(); }

private:
	void update_int1();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
};

// handlers

void bzaxxon_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R3x(,D0-D2): vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	// update display
	m_display->matrix(m_grid, m_plate);
}

void bzaxxon_state::grid_w(u16 data)
{
	// D4: speaker out
	m_speaker->level_w(data >> 4 & 1);

	// D7-D10: input mux
	u8 inp_mux = data >> 7 & 0xf;
	if (inp_mux != m_inp_mux)
	{
		m_inp_mux = inp_mux;
		update_int1();
	}

	// D5-D15: vfd grid
	m_grid = data >> 5 & 0x7ff;

	// D0-D2: vfd plate (update display there)
	plate_w(4, data & 7);
}

void bzaxxon_state::update_int1()
{
	// INT1 on multiplexed inputs
	set_interrupt(1, read_inputs(4));
}

// inputs

static INPUT_PORTS_START( bzaxxon )
	PORT_START("IN.0") // D7 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bzaxxon_state::input_changed), 0)

	PORT_START("IN.1") // D8 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bzaxxon_state::input_changed), 0)

	PORT_START("IN.2") // D9 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bzaxxon_state::input_changed), 0)

	PORT_START("IN.3") // D10 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bzaxxon_state::input_changed), 0)

	PORT_START("IN.4") // INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_hmcs40_state::single_interrupt_line), 0)

	PORT_START("IN.5") // port D
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_SELECT )
	PORT_BIT( 0xfff7, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void bzaxxon_state::bzaxxon(machine_config &config)
{
	// basic machine hardware
	HD38800(config, m_maincpu, 450000); // approximation
	m_maincpu->write_r<0>().set(FUNC(bzaxxon_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(bzaxxon_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(bzaxxon_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(bzaxxon_state::plate_w));
	m_maincpu->write_d().set(FUNC(bzaxxon_state::grid_w));
	m_maincpu->read_d().set_ioport("IN.5");

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(613, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(11, 19);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( bzaxxon )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38800b19", 0x0000, 0x1000, CRC(4fecb80d) SHA1(7adf079480ffd3825ad5ae1eaa4d892eecbcc42d) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 521091, "screen", 0)
	ROM_LOAD( "bzaxxon.svg", 0, 521091, BAD_DUMP CRC(3df4c10b) SHA1(804cabe09d11bf79592e25615fd6914ef0d337d8) )
ROM_END





/*******************************************************************************

  Bandai Zackman (manufactured in Japan)
  * Hitachi QFP HD38820A49 MCU, 1-bit sound
  * cyan/red/yellow VFD Futaba DM-53Z 3E
  * color overlay: score/lives: blue, game row 3,4: pink1, row 5,6: pink2

  It is licensed from/based on Zilec's The Pit, an arcade game.

  known releases:
  - World: Zackman, published by Bandai
  - USA: Zackman, published by Tandy

*******************************************************************************/

class zackman_state : public hh_hmcs40_state
{
public:
	zackman_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void zackman(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(input_changed) { update_int0(); }

private:
	void update_int0();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
};

// handlers

void zackman_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R6x(,D0,D1): vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	// update display
	m_display->matrix(m_grid, m_plate);
}

void zackman_state::grid_w(u16 data)
{
	// D2: speaker out
	m_speaker->level_w(data >> 2 & 1);

	// D11-D14: input mux
	u8 inp_mux = data >> 11 & 0xf;
	if (inp_mux != m_inp_mux)
	{
		m_inp_mux = inp_mux;
		update_int0();
	}

	// D8-D15: vfd grid
	m_grid = data >> 8 & 0xff;

	// D0,D1: vfd plate (update display there)
	plate_w(7, data & 3);
}

void zackman_state::update_int0()
{
	// INT0 on multiplexed inputs
	set_interrupt(0, read_inputs(4));
}

// inputs

static INPUT_PORTS_START( zackman )
	PORT_START("IN.0") // D11 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(zackman_state::input_changed), 0)

	PORT_START("IN.1") // D12 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(zackman_state::input_changed), 0)

	PORT_START("IN.2") // D13 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(zackman_state::input_changed), 0)

	PORT_START("IN.3") // D14 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(zackman_state::input_changed), 0)

	PORT_START("IN.4") // INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_hmcs40_state::single_interrupt_line), 1)
INPUT_PORTS_END

// config

void zackman_state::zackman(machine_config &config)
{
	// basic machine hardware
	HD38820(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(zackman_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(zackman_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(zackman_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(zackman_state::plate_w));
	m_maincpu->write_r<4>().set(FUNC(zackman_state::plate_w));
	m_maincpu->write_r<5>().set(FUNC(zackman_state::plate_w));
	m_maincpu->write_r<6>().set(FUNC(zackman_state::plate_w));
	m_maincpu->write_d().set(FUNC(zackman_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(487, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(8, 30);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( zackman )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820a49", 0x0000, 0x1000, CRC(b97f5ef6) SHA1(7fe20e8107361caf9ea657e504be1f8b10b8b03f) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 910695, "screen", 0)
	ROM_LOAD( "zackman.svg", 0, 910695, CRC(8385497b) SHA1(eec68a9f677e3ae849414278f6461929d77f3169) )
ROM_END





/*******************************************************************************

  Bandai Pengo (manufactured in Japan, licensed from Sega)
  * PCB label: FL Pengo(in katakana)
  * Hitachi QFP HD38820A63 MCU, 1-bit sound
  * cyan/red/blue VFD Futaba DM-68ZK 3D DM-63

*******************************************************************************/

class bpengo_state : public hh_hmcs40_state
{
public:
	bpengo_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void bpengo(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(input_changed) { update_int0(); }

private:
	void update_int0();
	void update_display();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
};

// handlers

void bpengo_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void bpengo_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R6x: vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void bpengo_state::grid_w(u16 data)
{
	// D10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// D12-D15: input mux
	u8 inp_mux = data >> 12 & 0xf;
	if (inp_mux != m_inp_mux)
	{
		m_inp_mux = inp_mux;
		update_int0();
	}

	// D0-D7: vfd grid
	m_grid = data & 0xff;
	update_display();
}

void bpengo_state::update_int0()
{
	// INT0 on multiplexed inputs
	set_interrupt(0, read_inputs(4));
}

// inputs

static INPUT_PORTS_START( bpengo )
	PORT_START("IN.0") // D12 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bpengo_state::input_changed), 0)

	PORT_START("IN.1") // D13 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bpengo_state::input_changed), 0)

	PORT_START("IN.2") // D14 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bpengo_state::input_changed), 0)

	PORT_START("IN.3") // D15 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bpengo_state::input_changed), 0)

	PORT_START("IN.4") // INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_hmcs40_state::single_interrupt_line), 1)

	PORT_START("IN.5") // port D
	PORT_CONFNAME( 0x0800, 0x0000, "Factory Test" )
	PORT_CONFSETTING(      0x0000, DEF_STR( Off ) )
	PORT_CONFSETTING(      0x0800, DEF_STR( On ) )
	PORT_BIT( 0xf7ff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void bpengo_state::bpengo(machine_config &config)
{
	// basic machine hardware
	HD38820(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(bpengo_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(bpengo_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(bpengo_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(bpengo_state::plate_w));
	m_maincpu->write_r<4>().set(FUNC(bpengo_state::plate_w));
	m_maincpu->write_r<5>().set(FUNC(bpengo_state::plate_w));
	m_maincpu->write_r<6>().set(FUNC(bpengo_state::plate_w));
	m_maincpu->write_d().set(FUNC(bpengo_state::grid_w));
	m_maincpu->read_d().set_ioport("IN.5");

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 759);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(8, 28);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( bpengo )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820a63", 0x0000, 0x1000, CRC(ebd6bc64) SHA1(0a322c47b9553a2739a85908ce64b9650cf93d49) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 565069, "screen", 0)
	ROM_LOAD( "bpengo.svg", 0, 565069, CRC(29ad1525) SHA1(9ed56ff7bfbf70ead9bd7921f46d86f4b96ee9df) )
ROM_END





/*******************************************************************************

  Bandai Burger Time (manufactured in Japan, licensed from Data East)
  * PCB label: Kaken Corp. PT-389 Burger Time
  * Hitachi QFP HD38820A65 MCU, 1-bit sound
  * cyan/red/green VFD NEC FIP6AM25T no. 21-21

*******************************************************************************/

class bbtime_state : public hh_hmcs40_state
{
public:
	bbtime_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void bbtime(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(input_changed) { update_int0(); }

private:
	void update_int0();
	void update_display();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
};

// handlers

void bbtime_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void bbtime_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R6x: vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void bbtime_state::grid_w(u16 data)
{
	// D3: speaker out
	m_speaker->level_w(data >> 3 & 1);

	// D10-D14: input mux
	u8 inp_mux = data >> 10 & 0x1f;
	if (inp_mux != m_inp_mux)
	{
		m_inp_mux = inp_mux;
		update_int0();
	}

	// D4-D9: vfd grid
	m_grid = data >> 4 & 0x3f;
	update_display();
}

void bbtime_state::update_int0()
{
	// INT0 on multiplexed inputs
	set_interrupt(0, read_inputs(5));
}

// inputs

static INPUT_PORTS_START( bbtime )
	PORT_START("IN.0") // D10 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bbtime_state::input_changed), 0)

	PORT_START("IN.1") // D11 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bbtime_state::input_changed), 0)

	PORT_START("IN.2") // D12 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bbtime_state::input_changed), 0)

	PORT_START("IN.3") // D13 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bbtime_state::input_changed), 0)

	PORT_START("IN.4") // D14 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bbtime_state::input_changed), 0)

	PORT_START("IN.5") // INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_hmcs40_state::single_interrupt_line), 1)
INPUT_PORTS_END

// config

void bbtime_state::bbtime(machine_config &config)
{
	// basic machine hardware
	HD38820(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(bbtime_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(bbtime_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(bbtime_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(bbtime_state::plate_w));
	m_maincpu->write_r<4>().set(FUNC(bbtime_state::plate_w));
	m_maincpu->write_r<5>().set(FUNC(bbtime_state::plate_w));
	m_maincpu->write_r<6>().set(FUNC(bbtime_state::plate_w));
	m_maincpu->write_d().set(FUNC(bbtime_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(379, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(6, 28);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( bbtime )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820a65", 0x0000, 0x1000, CRC(33611faf) SHA1(29b6a30ed543688d31ec2aa18f7938fa4eef30b0) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 461598, "screen", 0)
	ROM_LOAD( "bbtime.svg", 0, 461598, BAD_DUMP CRC(297f30de) SHA1(a5f38cd9c5d5ba9392c5d57ac85ecc2782b6ae7a) )
ROM_END





/*******************************************************************************

  Bandai Kiteyo Parman
  * Hitachi HD38800B42 MCU, 1-bit sound
  * cyan/red/blue VFD

*******************************************************************************/

class ktparman_state : public hh_hmcs40_state
{
public:
	ktparman_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void ktparman(machine_config &config);

private:
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
};

// handlers

void ktparman_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R3x(,D0-D2): vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	// update display
	m_display->matrix(m_grid, m_plate);
}

void ktparman_state::grid_w(u16 data)
{
	// D3: speaker out
	m_speaker->level_w(data >> 3 & 1);

	// D8-D15: vfd grid
	m_grid = data >> 8 & 0xff;

	// D0-D2: vfd plate (update display there)
	plate_w(4, data & 7);
}

// inputs

static INPUT_PORTS_START( ktparman )
	PORT_START("IN.0") // port D
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )

	PORT_START("IN.1") // INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_hmcs40_state::single_interrupt_line), 0)
INPUT_PORTS_END

// config

void ktparman_state::ktparman(machine_config &config)
{
	// basic machine hardware
	HD38800(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(ktparman_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(ktparman_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(ktparman_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(ktparman_state::plate_w));
	m_maincpu->write_d().set(FUNC(ktparman_state::grid_w));
	m_maincpu->read_d().set_ioport("IN.0");

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 19);
	config.set_default_layout(layout_hh_hmcs40_test);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( ktparman )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38800b42", 0x0000, 0x1000, CRC(ba1f7939) SHA1(1038d33923fcc87b5dd95954d5f964064a10ff9d) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 100000, "screen", 0)
	ROM_LOAD( "ktparman.svg", 0, 100000, NO_DUMP )
ROM_END





/*******************************************************************************

  Bandai Dokodemo Dorayaki Doraemon (FL LSI Game Push Up) (manufactured in Japan)
  * PCB label: Kaken Corp PT-412 FL-Doreamon(in katakana)
  * Hitachi HD38800B43 MCU, 1-bit sound
  * cyan/red/blue VFD Futaba DM-71

*******************************************************************************/

class bdoramon_state : public hh_hmcs40_state
{
public:
	bdoramon_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void bdoramon(machine_config &config);

private:
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
};

// handlers

void bdoramon_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R3x(,D0-D3): vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	// update display
	m_display->matrix(m_grid, m_plate);
}

void bdoramon_state::grid_w(u16 data)
{
	// D7: speaker out
	m_speaker->level_w(data >> 7 & 1);

	// D8-D15: vfd grid
	m_grid = data >> 8 & 0xff;

	// D0-D3: vfd plate (update display there)
	plate_w(4, data & 0xf);
}

// inputs

static INPUT_PORTS_START( bdoramon )
	PORT_START("IN.0") // INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_hmcs40_state::single_interrupt_line), 0)

	PORT_START("IN.1") // INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_hmcs40_state::single_interrupt_line), 1)

	PORT_START("IN.2") // port D
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0xff8f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // port R2x
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x08, 0x00, "Factory Test" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x08, DEF_STR( On ) )
INPUT_PORTS_END

// config

void bdoramon_state::bdoramon(machine_config &config)
{
	// basic machine hardware
	HD38800(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(bdoramon_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(bdoramon_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(bdoramon_state::plate_w));
	m_maincpu->read_r<2>().set_ioport("IN.3");
	m_maincpu->write_r<3>().set(FUNC(bdoramon_state::plate_w));
	m_maincpu->write_d().set(FUNC(bdoramon_state::grid_w));
	m_maincpu->read_d().set_ioport("IN.2");

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 668);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(8, 20);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( bdoramon )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38800b43", 0x0000, 0x1000, CRC(9387ca42) SHA1(8937e208934b34bd9f49700aa50287dfc8bda76c) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 624751, "screen", 0)
	ROM_LOAD( "bdoramon.svg", 0, 624751, CRC(fc6ae4e4) SHA1(a9bd544a8753435bdfb8f06285aa799c47c9ff24) )
ROM_END





/*******************************************************************************

  Bandai Toukon Juohmaru
  * Hitachi HD38800B48 MCU, 1-bit sound
  * cyan/red/blue VFD

*******************************************************************************/

class tkjmaru_state : public hh_hmcs40_state
{
public:
	tkjmaru_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void tkjmaru(machine_config &config);

private:
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
	u16 input_r();
};

// handlers

void tkjmaru_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R3x(,D0): vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	// update display
	m_display->matrix(m_grid, m_plate);
}

void tkjmaru_state::grid_w(u16 data)
{
	// D4-D7: input mux
	m_inp_mux = data >> 4 & 0xf;

	// D10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// D11-D15: vfd grid
	m_grid = data >> 11 & 0x1f;

	// D0: vfd plate (update display there)
	plate_w(4, data & 1);
}

u16 tkjmaru_state::input_r()
{
	// D3: multiplexed inputs, D8,D9: fixed inputs
	return read_inputs(4) | m_inputs[4]->read();
}

// inputs

static INPUT_PORTS_START( tkjmaru )
	PORT_START("IN.0") // D4 line D3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )

	PORT_START("IN.1") // D5 line D3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )

	PORT_START("IN.2") // D6 line D3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )

	PORT_START("IN.3") // D7 line D3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )

	PORT_START("IN.4") // D8,D9
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_CONFNAME( 0x200, 0x000, "Factory Test" )
	PORT_CONFSETTING(     0x000, DEF_STR( Off ) )
	PORT_CONFSETTING(     0x200, DEF_STR( On ) )
INPUT_PORTS_END

// config

void tkjmaru_state::tkjmaru(machine_config &config)
{
	// basic machine hardware
	HD38800(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(tkjmaru_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(tkjmaru_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(tkjmaru_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(tkjmaru_state::plate_w));
	m_maincpu->write_d().set(FUNC(tkjmaru_state::grid_w));
	m_maincpu->read_d().set(FUNC(tkjmaru_state::input_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(5, 17);
	config.set_default_layout(layout_hh_hmcs40_test);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( tkjmaru )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38800b48", 0x0000, 0x1000, CRC(249f357d) SHA1(0b5dad187a035db622790eb092b7be1ca7c57d97) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 100000, "screen", 0)
	ROM_LOAD( "tkjmaru.svg", 0, 100000, NO_DUMP )
ROM_END





/*******************************************************************************

  Bandai Ultraman Monster Battle (FL LSI Game Push Up) (manufactured in Japan)
  * PCB label: Kaken Corp. PT-424 FL Ultra Man
  * Hitachi HD38800B52 MCU, 1-bit sound
  * cyan/red/blue VFD NEC FIP8BM25T no. 21-8 2

*******************************************************************************/

class bultrman_state : public hh_hmcs40_state
{
public:
	bultrman_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void bultrman(machine_config &config);

private:
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
};

// handlers

void bultrman_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R3x(,D0-D2): vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	// update display
	m_display->matrix(m_grid, m_plate);
}

void bultrman_state::grid_w(u16 data)
{
	// D7: speaker out
	m_speaker->level_w(data >> 7 & 1);

	// D8-D15: vfd grid
	m_grid = data >> 8 & 0xff;

	// D0-D2: vfd plate (update display there)
	plate_w(4, data & 7);
}

// inputs

static INPUT_PORTS_START( bultrman )
	PORT_START("IN.0") // INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_hmcs40_state::single_interrupt_line), 0)

	PORT_START("IN.1") // port D
	PORT_CONFNAME( 0x0010, 0x0000, "Factory Test" )
	PORT_CONFSETTING(      0x0000, DEF_STR( Off ) )
	PORT_CONFSETTING(      0x0010, DEF_STR( On ) )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0xff8f, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void bultrman_state::bultrman(machine_config &config)
{
	// basic machine hardware
	HD38800(config, m_maincpu, 350000); // approximation
	m_maincpu->write_r<0>().set(FUNC(bultrman_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(bultrman_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(bultrman_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(bultrman_state::plate_w));
	m_maincpu->write_d().set(FUNC(bultrman_state::grid_w));
	m_maincpu->read_d().set_ioport("IN.1");

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 673);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(8, 19);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( bultrman )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38800b52", 0x0000, 0x1000, CRC(88d372dc) SHA1(f2ac3b89be8afe6fb65914ccebe1a56316b9472a) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 405725, "screen", 0)
	ROM_LOAD( "bultrman.svg", 0, 405725, CRC(dfbe1dd2) SHA1(193ad1138bc0b8596c42517ade3dd128bacd587e) )
ROM_END





/*******************************************************************************

  Bandai Go Go Dynaman
  * Hitachi HD38820A75 MCU, 1-bit sound
  * cyan/red/blue VFD

  known releases:
  - Japan: Go Go Dynaman, published by Bandai
  - Germany: Kampf der Monster, published by Bandai

*******************************************************************************/

class ggdman_state : public hh_hmcs40_state
{
public:
	ggdman_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void ggdman(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(input_changed) { update_int0(); }

private:
	void update_int0();
	void update_display();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
};

// handlers

void ggdman_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void ggdman_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R5x: vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void ggdman_state::grid_w(u16 data)
{
	// D0: speaker out
	m_speaker->level_w(data & 1);

	// D6-D9: input mux
	u8 inp_mux = data >> 6 & 0xf;
	if (inp_mux != m_inp_mux)
	{
		m_inp_mux = inp_mux;
		update_int0();
	}

	// D11-D15: vfd grid
	m_grid = data >> 11 & 0x1f;
	update_display();
}

void ggdman_state::update_int0()
{
	// INT0 on multiplexed inputs
	set_interrupt(0, read_inputs(4));
}

// inputs

static INPUT_PORTS_START( ggdman )
	PORT_START("IN.0") // D6 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ggdman_state::input_changed), 0)

	PORT_START("IN.1") // D7 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ggdman_state::input_changed), 0)

	PORT_START("IN.2") // D8 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ggdman_state::input_changed), 0)

	PORT_START("IN.3") // D9 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ggdman_state::input_changed), 0)

	PORT_START("IN.4") // INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_hmcs40_state::single_interrupt_line), 1)
INPUT_PORTS_END

// config

void ggdman_state::ggdman(machine_config &config)
{
	// basic machine hardware
	HD38820(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(ggdman_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(ggdman_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(ggdman_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(ggdman_state::plate_w));
	m_maincpu->write_r<4>().set(FUNC(ggdman_state::plate_w));
	m_maincpu->write_r<5>().set(FUNC(ggdman_state::plate_w));
	m_maincpu->write_d().set(FUNC(ggdman_state::grid_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(5, 24);
	config.set_default_layout(layout_hh_hmcs40_test);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( ggdman )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820a75", 0x0000, 0x1000, CRC(14a9c064) SHA1(253a5decbf4219c1b457280e301b82388694e49f) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 100000, "screen", 0)
	ROM_LOAD( "ggdman.svg", 0, 100000, NO_DUMP )
ROM_END





/*******************************************************************************

  Bandai Machine Man (FL Flat Type) (manufactured in Japan)
  * PCB label: Kaken PT-438
  * Hitachi QFP HD38820A85 MCU, 1-bit sound
  * cyan/red/green VFD NEC FIP5CM33T no. 4 21

*******************************************************************************/

class machiman_state : public hh_hmcs40_state
{
public:
	machiman_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void machiman(machine_config &config);

private:
	void update_display();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
};

// handlers

void machiman_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void machiman_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R3x,R6012: vfd plate
	int shift = (offset == 6) ? 16 : offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void machiman_state::grid_w(u16 data)
{
	// D13: speaker out
	m_speaker->level_w(data >> 13 & 1);

	// D0-D4: vfd grid
	m_grid = data & 0x1f;
	update_display();
}

// inputs

static INPUT_PORTS_START( machiman )
	PORT_START("IN.0") // INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_hmcs40_state::single_interrupt_line), 0)

	PORT_START("IN.1") // port D
	PORT_BIT( 0x3fff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_2WAY
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_2WAY
INPUT_PORTS_END

// config

void machiman_state::machiman(machine_config &config)
{
	// basic machine hardware
	HD38820(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(machiman_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(machiman_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(machiman_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(machiman_state::plate_w));
	m_maincpu->write_r<6>().set(FUNC(machiman_state::plate_w));
	m_maincpu->write_d().set(FUNC(machiman_state::grid_w));
	m_maincpu->read_d().set_ioport("IN.1");

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1534, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(5, 19);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( machiman )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820a85", 0x0000, 0x1000, CRC(894b4954) SHA1(cab49638a326b031aa548301beb16f818759ef62) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 374093, "screen", 0)
	ROM_LOAD( "machiman.svg", 0, 374093, CRC(41436ebc) SHA1(beca9bc7fb7ef31cfecdb87b258ff91cee6a05c4) )
ROM_END





/*******************************************************************************

  Bandai Pair Match (manufactured in Japan)
  * PCB label: Kaken Corp. PT-460
  * Hitachi QFP HD38820A88 MCU(main), HD38820A89(audio), 1-bit sound
  * cyan/red VFD

  This is a memory game, the difference is instead of pictures, the player
  needs to match sound effects. It has an extra MCU for sound. The case is
  shaped like a glossy black pyramid. Star Trek fans will recognize it as
  a prop used in TNG Ten Forward.

*******************************************************************************/

class pairmtch_state : public hh_hmcs40_state
{
public:
	pairmtch_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag),
		m_audiocpu(*this, "audiocpu"),
		m_soundlatch(*this, "soundlatch%u", 0)
	{ }

	void pairmtch(machine_config &config);

private:
	required_device<hmcs40_cpu_device> m_audiocpu;
	required_device_array<generic_latch_8_device, 2> m_soundlatch;

	void update_display();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
	u8 input_r();

	void sound_w(u8 data);
	void sound2_w(u8 data);
	void speaker_w(u16 data);
};

// handlers: maincpu side

void pairmtch_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void pairmtch_state::plate_w(offs_t offset, u8 data)
{
	// R2x,R3x,R6x: vfd plate
	int shift = (offset == 6) ? 8 : (offset-2) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void pairmtch_state::grid_w(u16 data)
{
	// D7: sound reset (to audiocpu reset line)
	m_audiocpu->set_input_line(INPUT_LINE_RESET, (data & 0x80) ? ASSERT_LINE : CLEAR_LINE);

	// D9: sound start (to audiocpu INT0)
	m_audiocpu->set_input_line(0, (data & 0x200) ? ASSERT_LINE : CLEAR_LINE);

	// D10,D15: input mux
	m_inp_mux = (data >> 10 & 1) | (data >> 14 & 2);

	// D0-D5: vfd grid
	m_grid = data & 0x3f;
	update_display();
}

u8 pairmtch_state::input_r()
{
	// R4x: multiplexed inputs
	return read_inputs(2);
}

void pairmtch_state::sound_w(u8 data)
{
	// R5x: soundlatch (to audiocpu R2x)
	m_soundlatch[0]->write(bitswap<4>(data,0,1,2,3));
}

// handlers: audiocpu side

void pairmtch_state::sound2_w(u8 data)
{
	// R2x: soundlatch (to maincpu R5x)
	m_soundlatch[1]->write(bitswap<4>(data,0,1,2,3));
}

void pairmtch_state::speaker_w(u16 data)
{
	// D0: speaker out
	m_speaker->level_w(data & 1);

	// D1: sound ack (to maincpu INT0)
	m_maincpu->set_input_line(0, (data & 2) ? ASSERT_LINE : CLEAR_LINE);
}

// inputs

static INPUT_PORTS_START( pairmtch )
	PORT_START("IN.0") // D10 port R4x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL PORT_16WAY

	PORT_START("IN.1") // D15 port R4x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY

	PORT_START("IN.2") // port D
	PORT_CONFNAME( 0x0040, 0x0000, "Factory Test" )
	PORT_CONFSETTING(      0x0000, DEF_STR( Off ) )
	PORT_CONFSETTING(      0x0040, DEF_STR( On ) )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL
	PORT_CONFNAME( 0x0800, 0x0800, DEF_STR( Players ) )
	PORT_CONFSETTING(      0x0800, "1" )
	PORT_CONFSETTING(      0x0000, "2" )
	PORT_CONFNAME( 0x3000, 0x2000, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(      0x2000, "1" )
	PORT_CONFSETTING(      0x1000, "2" )
	PORT_CONFSETTING(      0x0000, "3" )
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x86bf, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void pairmtch_state::pairmtch(machine_config &config)
{
	// basic machine hardware
	HD38820(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<2>().set(FUNC(pairmtch_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(pairmtch_state::plate_w));
	m_maincpu->read_r<4>().set(FUNC(pairmtch_state::input_r));
	m_maincpu->write_r<5>().set(FUNC(pairmtch_state::sound_w));
	m_maincpu->read_r<5>().set(m_soundlatch[1], FUNC(generic_latch_8_device::read));
	m_maincpu->write_r<6>().set(FUNC(pairmtch_state::plate_w));
	m_maincpu->write_d().set(FUNC(pairmtch_state::grid_w));
	m_maincpu->read_d().set_ioport("IN.2");

	HD38820(config, m_audiocpu, 400000); // approximation
	m_audiocpu->write_r<2>().set(FUNC(pairmtch_state::sound2_w));
	m_audiocpu->read_r<2>().set(m_soundlatch[0], FUNC(generic_latch_8_device::read));
	m_audiocpu->write_d().set(FUNC(pairmtch_state::speaker_w));

	config.set_perfect_quantum(m_maincpu);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(6, 12);
	config.set_default_layout(layout_pairmtch);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	GENERIC_LATCH_8(config, m_soundlatch[0]);
	GENERIC_LATCH_8(config, m_soundlatch[1]);
}

// roms

ROM_START( pairmtch )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820a88", 0x0000, 0x1000, CRC(ffa35730) SHA1(5a80b9025aaad2ac0ab0b1436a1355ae8cd3f868) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 0x2000, "audiocpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820a89", 0x0000, 0x1000, CRC(3533ec56) SHA1(556d69e78a0ee1bf766fce16ed58992d7272d57f) )
	ROM_CONTINUE(           0x1e80, 0x0100 )
ROM_END





/*******************************************************************************

  Coleco Alien Attack (manufactured in Taiwan)
  * Hitachi HD38800A25 MCU, 1-bit sound
  * cyan/red VFD Futaba DM-19Z 1J

  It looks like Coleco took Gakken's Heiankyo Alien and turned it into a more
  action-oriented game.

*******************************************************************************/

class alnattck_state : public hh_hmcs40_state
{
public:
	alnattck_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void alnattck(machine_config &config);

private:
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
	u16 input_r();
};

// handlers

void alnattck_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R3x(,D0-D3): vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	// update display
	m_display->matrix(m_grid, m_plate);
}

void alnattck_state::grid_w(u16 data)
{
	// D4: speaker out
	m_speaker->level_w(data >> 4 & 1);

	// D7-D13: input mux
	m_inp_mux = data >> 7 & 0x7f;

	// D6-D15: vfd grid
	m_grid = data >> 6 & 0x3ff;

	// D0-D3: vfd plate (update display there)
	plate_w(4, data & 0xf);
}

u16 alnattck_state::input_r()
{
	// D5: multiplexed inputs
	return read_inputs(7) & 0x20;
}

// inputs

static INPUT_PORTS_START( alnattck )
	PORT_START("IN.0") // D7 line D5
	PORT_CONFNAME( 0x20, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x20, "2" )

	PORT_START("IN.1") // D8 line D5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )

	PORT_START("IN.2") // D9 line D5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )

	PORT_START("IN.3") // D10 line D5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )

	PORT_START("IN.4") // D11 line D5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )

	PORT_START("IN.5") // D12 line D5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Move")

	PORT_START("IN.6") // D13 line D5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Fire")
INPUT_PORTS_END

// config

void alnattck_state::alnattck(machine_config &config)
{
	// basic machine hardware
	HD38800(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(alnattck_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(alnattck_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(alnattck_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(alnattck_state::plate_w));
	m_maincpu->write_d().set(FUNC(alnattck_state::grid_w));
	m_maincpu->read_d().set(FUNC(alnattck_state::input_r));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 700);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(10, 20);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( alnattck )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38800a25", 0x0000, 0x1000, CRC(18b50869) SHA1(11e9d5f7b4ae818b077b0ee14a3b43190e20bff3) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 564266, "screen", 0)
	ROM_LOAD( "alnattck.svg", 0, 564266, CRC(386bf327) SHA1(d8931a9e4faa31de292a57dfae0834da8dd8fc15) )
ROM_END





/*******************************************************************************

  Coleco Donkey Kong (manufactured in Taiwan, licensed from Nintendo)
  * PCB label: Coleco Rev C 75790 DK
  * Hitachi QFP HD38820A45 MCU
  * 1-bit sound with RC circuit for speaker volume decay
  * cyan/red VFD Futaba DM-47ZK 2K
  * color overlay: playfield: red1, donkey kong/princess: red2

*******************************************************************************/

class cdkong_state : public hh_hmcs40_state
{
public:
	cdkong_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag),
		m_volume(*this, "volume")
	{ }

	void cdkong(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<filter_volume_device> m_volume;

	double m_speaker_volume = 0.0;

	void update_display();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);

	void speaker_update();
	TIMER_DEVICE_CALLBACK_MEMBER(speaker_decay_sim);
};

void cdkong_state::machine_start()
{
	hh_hmcs40_state::machine_start();
	save_item(NAME(m_speaker_volume));
}

// handlers

void cdkong_state::speaker_update()
{
	if (m_r[1] & 8)
		m_speaker_volume = 1.0;

	m_volume->set_gain(m_speaker_volume);
}

TIMER_DEVICE_CALLBACK_MEMBER(cdkong_state::speaker_decay_sim)
{
	// volume decays when speaker is off (divisor and timer period determine duration)
	speaker_update();
	m_speaker_volume /= 1.02;
}

void cdkong_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void cdkong_state::plate_w(offs_t offset, u8 data)
{
	// R13: speaker on
	m_r[offset] = data;
	speaker_update();

	// R0x-R6x: vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void cdkong_state::grid_w(u16 data)
{
	// D3: speaker out
	m_speaker->level_w(BIT(data, 3));

	// D4-D14: vfd grid
	m_grid = data >> 4 & 0x7ff;
	update_display();
}

// inputs

static INPUT_PORTS_START( cdkong )
	PORT_START("IN.0") // INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_hmcs40_state::single_interrupt_line), 0)

	PORT_START("IN.1") // port D
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x7ff8, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void cdkong_state::cdkong(machine_config &config)
{
	// basic machine hardware
	HD38820(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(cdkong_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(cdkong_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(cdkong_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(cdkong_state::plate_w));
	m_maincpu->write_r<4>().set(FUNC(cdkong_state::plate_w));
	m_maincpu->write_r<5>().set(FUNC(cdkong_state::plate_w));
	m_maincpu->write_r<6>().set(FUNC(cdkong_state::plate_w));
	m_maincpu->write_d().set(FUNC(cdkong_state::grid_w));
	m_maincpu->read_d().set_ioport("IN.1");

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(605, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(11, 28);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "volume", 0.25);
	FILTER_VOLUME(config, m_volume).add_route(ALL_OUTPUTS, "mono", 1.0);

	TIMER(config, "speaker_decay").configure_periodic(FUNC(cdkong_state::speaker_decay_sim), attotime::from_msec(1));
}

// roms

ROM_START( cdkong )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820a45", 0x0000, 0x1000, CRC(196b8070) SHA1(da85d1eb4b048b77f3168630662ab94ec9baa262) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 359187, "screen", 0)
	ROM_LOAD( "cdkong.svg", 0, 359187, CRC(cc9f4e32) SHA1(32d77aa05620127e346f07a64f1fe102ecdd960e) )
ROM_END





/*******************************************************************************

  Coleco Galaxian (manufactured in Taiwan)
  * PCB label: Coleco Rev A 75718
  * Hitachi HD38800A70 MCU
  * 1-bit sound + discrete sound (when alien attacks)
  * cyan/red VFD Futaba DM-36Z 2H
  * color overlay: gameover+top row: orange, aliens: pink

  Select game mode on start:
  - P1 Left:  Galaxian (default game)
  - P1 Right: Midway's Attackers
  - P2 Left:  Head-to-Head Galaxian (2-player mode, short)
  - P2 Right: Head-to-Head Galaxian (2-player mode, long)

*******************************************************************************/

class cgalaxn_state : public hh_hmcs40_state
{
public:
	cgalaxn_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void cgalaxn(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(player_switch);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(u16 data);
	u8 input_r();
};

// handlers

void cgalaxn_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

INPUT_CHANGED_MEMBER(cgalaxn_state::player_switch)
{
	// 2-player switch directly enables corner segments
	m_plate = (m_plate & 0x3fff) | (newval ? 0 : 0x4000);
	update_display();
}

void cgalaxn_state::grid_w(offs_t offset, u8 data)
{
	// R10,R11: input mux
	if (offset == 1)
		m_inp_mux = data & 3;

	// R1x-R3x: vfd grid
	int shift = (offset - 1) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);
	update_display();
}

void cgalaxn_state::plate_w(u16 data)
{
	// D0: speaker out
	m_speaker->level_w(data & 1);

	// D1: start alien attack whine sound effect (edge triggered)

	// D2-D15: vfd plate
	m_plate = (m_plate & 0x4000) | (data >> 2 & 0x3fff);
	update_display();
}

u8 cgalaxn_state::input_r()
{
	// R0x: multiplexed inputs
	return read_inputs(2);
}

// inputs

static INPUT_PORTS_START( cgalaxn )
	PORT_START("IN.0") // R10 port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )

	PORT_START("IN.1") // R11 port R0x
	PORT_CONFNAME( 0x01, 0x01, DEF_STR( Players ) ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(cgalaxn_state::player_switch), 0)
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x00, "2" )
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_hmcs40_state::single_interrupt_line), 0)

	PORT_START("IN.3") // INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_hmcs40_state::single_interrupt_line), 1)
INPUT_PORTS_END

// config

void cgalaxn_state::cgalaxn(machine_config &config)
{
	// basic machine hardware
	HD38800(config, m_maincpu, 400000); // approximation
	m_maincpu->read_r<0>().set(FUNC(cgalaxn_state::input_r));
	m_maincpu->write_r<1>().set(FUNC(cgalaxn_state::grid_w));
	m_maincpu->write_r<2>().set(FUNC(cgalaxn_state::grid_w));
	m_maincpu->write_r<3>().set(FUNC(cgalaxn_state::grid_w));
	m_maincpu->write_d().set(FUNC(cgalaxn_state::plate_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(526, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(12, 15);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( cgalaxn )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38800a70", 0x0000, 0x1000, CRC(a4c5ed1d) SHA1(0f647cb78437d7e62411febf7c9ce3c5b6753a80) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 712225, "screen", 0)
	ROM_LOAD( "cgalaxn.svg", 0, 712225, CRC(c351da8e) SHA1(de55011338deca7fb7f1a06040492b897ce2cadb) )
ROM_END





/*******************************************************************************

  Coleco Pac-Man (manufactured in Taiwan, licensed from Midway)
  * PCB label: Coleco 75690
  * Hitachi QFP HD38820A28/29 MCU, 1-bit sound
  * cyan/red VFD Futaba DM-34Z 2A
  * color overlay: playfield: orange, lives/status: pink

  known releases:
  - USA: Pac-Man, by Coleco (name-license from Midway)
  - Japan: Super Puck Monster, published by Gakken

  Select game mode on start:
  - P1 Right: Pac-Man (default game)
  - P1 Left:  Head-to-Head Pac-Man (2-player mode)
  - P1 Up:    Eat & Run
  - P1 Down:  Demo

  BTANB: 1st version doesn't show the whole maze on power-on

*******************************************************************************/

class cpacman_state : public hh_hmcs40_state
{
public:
	cpacman_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void cpacman(machine_config &config);

private:
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
	u8 input_r();
};

// handlers

void cpacman_state::plate_w(offs_t offset, u8 data)
{
	// R1x-R6x(,D1,D2): vfd plate
	int shift = (offset - 1) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	// update display
	m_display->matrix(m_grid, m_plate);
}

void cpacman_state::grid_w(u16 data)
{
	// D0: speaker out
	m_speaker->level_w(data & 1);

	// D13-D15: input mux
	m_inp_mux = data >> 13 & 7;

	// D5-D15: vfd grid
	m_grid = data >> 5 & 0x7ff;

	// D1,D2: vfd plate (update display there)
	plate_w(6 + 1, data >> 1 & 3);
}

u8 cpacman_state::input_r()
{
	// R0x: multiplexed inputs
	return read_inputs(3);
}

// inputs

static INPUT_PORTS_START( cpacman )
	PORT_START("IN.0") // D13 port R0x
	PORT_CONFNAME( 0x01, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x01, "2" )
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // D14 port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )

	PORT_START("IN.2") // D15 port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
INPUT_PORTS_END

// config

void cpacman_state::cpacman(machine_config &config)
{
	// basic machine hardware
	HD38820(config, m_maincpu, 400000); // approximation
	m_maincpu->read_r<0>().set(FUNC(cpacman_state::input_r));
	m_maincpu->write_r<1>().set(FUNC(cpacman_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(cpacman_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(cpacman_state::plate_w));
	m_maincpu->write_r<4>().set(FUNC(cpacman_state::plate_w));
	m_maincpu->write_r<5>().set(FUNC(cpacman_state::plate_w));
	m_maincpu->write_r<6>().set(FUNC(cpacman_state::plate_w));
	m_maincpu->write_d().set(FUNC(cpacman_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(484, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(11, 26);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( cpacman )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820a29", 0x0000, 0x1000, CRC(1082d577) SHA1(0ef73132bd41f6ca1e4c001ae19f7f7c97eaa8d1) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 359763, "screen", 0)
	ROM_LOAD( "cpacman.svg", 0, 359763, CRC(2e078065) SHA1(3ddb04f8d0671e1696f0a048c4064d4d9858db59) )
ROM_END

ROM_START( cpacmanr1 )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820a28", 0x0000, 0x1000, CRC(d2ed57e5) SHA1(f56f1341485ac28ea9e6cc4d162fab18d8a4c977) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 359763, "screen", 0)
	ROM_LOAD( "cpacman.svg", 0, 359763, CRC(2e078065) SHA1(3ddb04f8d0671e1696f0a048c4064d4d9858db59) )
ROM_END





/*******************************************************************************

  Coleco Ms. Pac-Man (manufactured in Taiwan, licensed from Midway)
  * PCB label: Coleco 911171
  * Hitachi QFP HD38820A61 MCU, 1-bit sound
  * cyan/red VFD Futaba DM-60Z 3I
  * color overlay: lives/playfield: orange, best: pink, fruit: yellow

  Select game mode on start:
  - P1 Left:  Ms. Pac-Man (default game)
  - P1 Down:  Head-to-Head Ms. Pac-Man (2-player mode)
  - P1 Up:    Demo

  BTANB: in demo-mode, she hardly ever walks to the upper two rows

*******************************************************************************/

class cmspacmn_state : public hh_hmcs40_state
{
public:
	cmspacmn_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void cmspacmn(machine_config &config);

private:
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
	u8 input_r();
};

// handlers

void cmspacmn_state::plate_w(offs_t offset, u8 data)
{
	// R1x-R6x(,D0,D1): vfd plate
	int shift = (offset - 1) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	// update display
	m_display->matrix(m_grid, m_plate);
}

void cmspacmn_state::grid_w(u16 data)
{
	// D2: speaker out
	m_speaker->level_w(data >> 2 & 1);

	// D13-D15: input mux
	m_inp_mux = data >> 13 & 7;

	// D5-D15: vfd grid
	m_grid = data >> 5 & 0x7ff;

	// D0,D1: vfd plate (update display there)
	plate_w(6 + 1, data & 3);
}

u8 cmspacmn_state::input_r()
{
	// R0x: multiplexed inputs
	return read_inputs(3);
}

// inputs

static INPUT_PORTS_START( cmspacmn )
	PORT_START("IN.0") // D13 port R0x
	PORT_CONFNAME( 0x01, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x01, "2" )
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // D14 port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)

	PORT_START("IN.2") // D15 port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
INPUT_PORTS_END

// config

void cmspacmn_state::cmspacmn(machine_config &config)
{
	// basic machine hardware
	HD38820(config, m_maincpu, 400000); // approximation
	m_maincpu->read_r<0>().set(FUNC(cmspacmn_state::input_r));
	m_maincpu->write_r<1>().set(FUNC(cmspacmn_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(cmspacmn_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(cmspacmn_state::plate_w));
	m_maincpu->write_r<4>().set(FUNC(cmspacmn_state::plate_w));
	m_maincpu->write_r<5>().set(FUNC(cmspacmn_state::plate_w));
	m_maincpu->write_r<6>().set(FUNC(cmspacmn_state::plate_w));
	m_maincpu->write_d().set(FUNC(cmspacmn_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(481, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(11, 26);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( cmspacmn )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820a61", 0x0000, 0x1000, CRC(76276318) SHA1(9d6ff3f49b4cdaee5c9e238c1ed638bfb9b99aa7) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 849283, "screen", 0)
	ROM_LOAD( "cmspacmn.svg", 0, 849283, CRC(73e192cb) SHA1(8bb27f23e3dbecbb4c6d31553732cfdc850da067) )
ROM_END





/*******************************************************************************

  Entex Galaxian 2 (manufactured in Japan)
  * PCB labels: ENTEX GALAXIAN PB-118/116/097 80-210137/135/114
  * Hitachi QFP HD38820A13 MCU, 1-bit sound
  * cyan/red/green VFD Futaba DM-20

  known releases:
  - USA: Galaxian 2, published by Entex
  - UK: Astro Invader, published by Hales/Entex

*******************************************************************************/

class egalaxn2_state : public hh_hmcs40_state
{
public:
	egalaxn2_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void egalaxn2(machine_config &config);

private:
	void update_display();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
	u8 input_r();
};

// handlers

void egalaxn2_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void egalaxn2_state::grid_w(u16 data)
{
	// D0: speaker out
	m_speaker->level_w(data & 1);

	// D1-D4: input mux
	m_inp_mux = data >> 1 & 0xf;

	// D1-D15: vfd grid
	m_grid = data >> 1 & 0x7fff;
	update_display();
}

void egalaxn2_state::plate_w(offs_t offset, u8 data)
{
	// R1x-R6x: vfd plate
	int shift = (offset - 1) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

u8 egalaxn2_state::input_r()
{
	// R0x: multiplexed inputs
	return read_inputs(4);
}

// inputs

static INPUT_PORTS_START( egalaxn2 )
	PORT_START("IN.0") // D1 port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY

	PORT_START("IN.1") // D2 port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL PORT_16WAY

	PORT_START("IN.2") // D3 port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // D4 port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x02, 0x02, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x02, "1" )
	PORT_CONFSETTING(    0x00, "2" )
	PORT_CONFNAME( 0x0c, 0x00, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x08, "0 (Demo)" ) // for Demo mode: need to hold down Fire button at power-on
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x04, "2" )
INPUT_PORTS_END

// config

void egalaxn2_state::egalaxn2(machine_config &config)
{
	// basic machine hardware
	HD38820(config, m_maincpu, 400000); // approximation
	m_maincpu->read_r<0>().set(FUNC(egalaxn2_state::input_r));
	m_maincpu->write_r<1>().set(FUNC(egalaxn2_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(egalaxn2_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(egalaxn2_state::plate_w));
	m_maincpu->write_r<4>().set(FUNC(egalaxn2_state::plate_w));
	m_maincpu->write_r<5>().set(FUNC(egalaxn2_state::plate_w));
	m_maincpu->write_r<6>().set(FUNC(egalaxn2_state::plate_w));
	m_maincpu->write_d().set(FUNC(egalaxn2_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(505, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(15, 24);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( egalaxn2 )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820a13", 0x0000, 0x1000, CRC(112b721b) SHA1(4a185bc57ea03fe64f61f7db4da37b16eeb0cb54) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 507935, "screen", 0)
	ROM_LOAD( "egalaxn2.svg", 0, 507935, CRC(e6df6664) SHA1(0009ea1a4c82041eec8168dff6181670edcac581) )
ROM_END





/*******************************************************************************

  Entex Pac Man 2 (manufactured in Japan)
  * PCB labels: ENTEX PAC-MAN PB-093/094 80-210149/50/51
  * Hitachi QFP HD38820A23 MCU, 1-bit sound
  * cyan/red VFD Futaba DM-28Z 1G(cyan Pac-Man) or DM-28 1K(orange Pac-Man)

  2 VFD revisions are known, the difference is Pac-Man's color: cyan or red.

*******************************************************************************/

class epacman2_state : public egalaxn2_state
{
public:
	epacman2_state(const machine_config &mconfig, device_type type, const char *tag) :
		egalaxn2_state(mconfig, type, tag)
	{ }

	void epacman2(machine_config &config);
};

// handlers are identical to Galaxian 2, so we can use those

// inputs

static INPUT_PORTS_START( epacman2 )
	PORT_START("IN.0") // D1 port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY

	PORT_START("IN.1") // D2 port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL PORT_16WAY

	PORT_START("IN.2") // D3 port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START ) PORT_NAME("P1 Skill Control")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_NAME("Demo Light Test")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // D4 port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x02, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_CONFNAME( 0x0c, 0x04, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x08, "0 (Demo)" )
	PORT_CONFSETTING(    0x04, "1" )
	PORT_CONFSETTING(    0x00, "2" )
INPUT_PORTS_END

// config

void epacman2_state::epacman2(machine_config &config)
{
	egalaxn2(config);

	// video hardware
	screen_device *screen = subdevice<screen_device>("screen");
	screen->set_size(505, 1080);
	screen->set_visarea_full();
}

// roms

ROM_START( epacman2 )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820a23", 0x0000, 0x1000, CRC(6eab640f) SHA1(509bdd02be915089e13769f22a08e03509f03af4) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 262468, "screen", 0)
	ROM_LOAD( "epacman2.svg", 0, 262468, CRC(95aeee41) SHA1(636eed1ff8fd0371a720c31bbf24b27117f620ac) )
ROM_END

ROM_START( epacman2r )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820a23", 0x0000, 0x1000, CRC(6eab640f) SHA1(509bdd02be915089e13769f22a08e03509f03af4) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 262471, "screen", 0)
	ROM_LOAD( "epacman2r.svg", 0, 262471, CRC(3bf12392) SHA1(7de4f2836ad03c99fc36b59ada08aff44322beb3) )
ROM_END





/*******************************************************************************

  Entex Super Space Invader 2 (black version)
  * Hitachi HD38800A31 MCU, 1-bit sound
  * cyan/red VFD

  This version has the same MCU as the Select-A-Game cartridge. Maybe from
  surplus inventory after that console was discontinued?. It was also sold
  as "Super Alien Invader 2".

  Hold down the fire button at boot for demo mode to work.

*******************************************************************************/

class einvader2_state : public hh_hmcs40_state
{
public:
	einvader2_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void einvader2(machine_config &config);

private:
	void update_display();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
	u16 input_r();
};

// handlers

void einvader2_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void einvader2_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R3x: vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void einvader2_state::grid_w(u16 data)
{
	// D0: speaker out
	m_speaker->level_w(data & 1);

	// D3,D5,D6: input mux
	m_inp_mux = (data >> 3 & 1) | (data >> 4 & 6);

	// D1-D12: vfd grid
	m_grid = data >> 1 & 0xfff;
	update_display();
}

u16 einvader2_state::input_r()
{
	// D13-D15: multiplexed inputs
	return read_inputs(3) << 13;
}

// inputs

static INPUT_PORTS_START( einvader2 )
	PORT_START("IN.0") // D3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x04, 0x04, IPT_CUSTOM ) PORT_CONDITION("FAKE", 0x03, EQUALS, 0x01) // 1 player

	PORT_START("IN.1") // D5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x04, 0x04, IPT_CUSTOM ) PORT_CONDITION("FAKE", 0x03, EQUALS, 0x00) // demo

	PORT_START("IN.2") // D6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL

	PORT_START("FAKE") // shared D3/D5
	PORT_CONFNAME( 0x03, 0x01, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x00, "Demo" )
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x02, "2" )
INPUT_PORTS_END

// config

void einvader2_state::einvader2(machine_config &config)
{
	// basic machine hardware
	HD38800(config, m_maincpu, 450000); // approximation
	m_maincpu->write_r<0>().set(FUNC(einvader2_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(einvader2_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(einvader2_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(einvader2_state::plate_w));
	m_maincpu->write_d().set(FUNC(einvader2_state::grid_w));
	m_maincpu->read_d().set(FUNC(einvader2_state::input_r));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(469, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(12, 14);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( einvader2 )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "inv2_hd38800a31", 0x0000, 0x1000, CRC(10e39521) SHA1(41d86696e518ea071e75ed37d5dc63c0408c262e) )
	ROM_CONTINUE(                0x1e80, 0x0100 )

	ROM_REGION( 217430, "screen", 0)
	ROM_LOAD( "einvader2.svg", 0, 217430, CRC(31badc0c) SHA1(4af77438e1e00e344966a289dab633903e9c6843) )
ROM_END





/*******************************************************************************

  Entex Turtles (manufactured in Japan)
  * PCB label: 560359
  * Hitachi QFP HD38820A43 MCU, speed adjustable by knob
  * COP411L sub MCU for sound, label COP411L-KED/N, 1-bit sound
  * cyan/red/green VFD NEC FIP15BM32T

*******************************************************************************/

class eturtles_state : public hh_hmcs40_state
{
public:
	eturtles_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag),
		m_audiocpu(*this, "audiocpu")
	{ }

	void eturtles(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(input_changed) { update_int(); }
	DECLARE_INPUT_CHANGED_MEMBER(game_speed);

protected:
	virtual void machine_start() override ATTR_COLD;

	required_device<cop411_cpu_device> m_audiocpu;

	u8 m_cop_irq = 0;

	void update_int();
	virtual void update_display();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);

	void speaker_w(int state);
	void cop_irq_w(u8 data);
	u8 cop_latch_r();
	u8 cop_ack_r();
};

void eturtles_state::machine_start()
{
	hh_hmcs40_state::machine_start();
	save_item(NAME(m_cop_irq));
}

// handlers: maincpu side

INPUT_CHANGED_MEMBER(eturtles_state::game_speed)
{
	// maincpu clock is controlled by game speed knob, range is around 150kHz
	m_maincpu->set_unscaled_clock(newval * 1500 + 325000);
}

void eturtles_state::update_display()
{
	// D10 also goes to the same plate as R13
	u32 plate = m_plate | BIT(m_d, 10) << 7;
	m_display->matrix(m_grid, plate);
}

void eturtles_state::plate_w(offs_t offset, u8 data)
{
	m_r[offset] = data;

	// R0x-R6x: vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void eturtles_state::grid_w(u16 data)
{
	m_d = data;

	// D1-D6: input mux
	u8 inp_mux = data >> 1 & 0x3f;
	if (inp_mux != m_inp_mux)
	{
		m_inp_mux = inp_mux;
		update_int();
	}

	// D1-D15: vfd grid
	m_grid = data >> 1 & 0x7fff;
	update_display();
}

void eturtles_state::update_int()
{
	// INT0/1 on multiplexed inputs, and from COP D0
	u8 inp = read_inputs(6);
	set_interrupt(0, (inp & 1) | m_cop_irq);
	set_interrupt(1, inp & 2);
}

// handlers: COP side

void eturtles_state::speaker_w(int state)
{
	// SK: speaker out
	m_speaker->level_w(!state);
}

void eturtles_state::cop_irq_w(u8 data)
{
	// D0: maincpu INT0 (active low)
	m_cop_irq = ~data & 1;
	update_int();
}

u8 eturtles_state::cop_latch_r()
{
	// L0-L3: soundlatch from maincpu R0x
	return m_r[0];
}

u8 eturtles_state::cop_ack_r()
{
	// G0: ack from maincpu D0
	return m_d & 1;
}

// inputs

static INPUT_PORTS_START( eturtles )
	PORT_START("IN.0") // D1 INT0/1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::input_changed), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::input_changed), 0)

	PORT_START("IN.1") // D2 INT0/1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::input_changed), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::input_changed), 0)

	PORT_START("IN.2") // D3 INT0/1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::input_changed), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::input_changed), 0)

	PORT_START("IN.3") // D4 INT0/1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::input_changed), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::input_changed), 0)

	PORT_START("IN.4") // D5 INT0/1
	PORT_CONFNAME( 0x01, 0x01, DEF_STR( Difficulty ) ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::input_changed), 0)
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x00, "2" )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::input_changed), 0)

	PORT_START("IN.5") // D6 INT0/1
	PORT_CONFNAME( 0x03, 0x00, DEF_STR( Players ) ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::input_changed), 0)
	PORT_CONFSETTING(    0x02, "0 (Demo)" )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x01, "2" )

	PORT_START("CPU")
	PORT_ADJUSTER(50, "Game Speed") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::game_speed), 0)
INPUT_PORTS_END

// config

void eturtles_state::eturtles(machine_config &config)
{
	// basic machine hardware
	HD38820(config, m_maincpu, 400000); // see game_speed
	m_maincpu->write_r<0>().set(FUNC(eturtles_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(eturtles_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(eturtles_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(eturtles_state::plate_w));
	m_maincpu->write_r<4>().set(FUNC(eturtles_state::plate_w));
	m_maincpu->write_r<5>().set(FUNC(eturtles_state::plate_w));
	m_maincpu->write_r<6>().set(FUNC(eturtles_state::plate_w));
	m_maincpu->write_d().set(FUNC(eturtles_state::grid_w));

	COP411(config, m_audiocpu, 215000); // approximation
	m_audiocpu->set_config(COP400_CKI_DIVISOR_4, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_audiocpu->write_sk().set(FUNC(eturtles_state::speaker_w));
	m_audiocpu->write_d().set(FUNC(eturtles_state::cop_irq_w));
	m_audiocpu->read_l().set(FUNC(eturtles_state::cop_latch_r));
	m_audiocpu->read_g().set(FUNC(eturtles_state::cop_ack_r));

	config.set_perfect_quantum(m_maincpu);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(484, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(15, 28);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( eturtles )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820a43", 0x0000, 0x1000, CRC(446aa4e2) SHA1(d1c0fb14ea7081def53b1174964b39eed1e5d5e6) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 0x0200, "audiocpu", 0 )
	ROM_LOAD( "cop411l-ked_n", 0x0000, 0x0200, CRC(503d26e9) SHA1(a53d24d62195bfbceff2e4a43199846e0950aef6) )

	ROM_REGION( 1027549, "screen", 0)
	ROM_LOAD( "eturtles.svg", 0, 1027549, CRC(34c16de6) SHA1(039be17b2df4beecab637cebbe48c3e3b3c2797e) )
ROM_END





/*******************************************************************************

  Entex Stargate (manufactured in Japan)
  * PCB label: 5603521/31
  * Hitachi QFP HD38820A42 MCU, speed adjustable by knob
  * COP411L sub MCU for sound, label ~/B8236 COP411L-KEC/N
  * 1-bit sound with volume control
  * cyan/red/green VFD NEC FIP15AM32T (EL628-003) no. 2-421
  * color overlay: bottom row: red

  BTANB: when changing direction, player bullets remain and become obstacles

*******************************************************************************/

class estargte_state : public eturtles_state
{
public:
	estargte_state(const machine_config &mconfig, device_type type, const char *tag) :
		eturtles_state(mconfig, type, tag),
		m_volume(*this, "volume")
	{ }

	void estargte(machine_config &config);

private:
	required_device<filter_volume_device> m_volume;

	virtual void update_display() override;
	u8 cop_latch_ack_r();
	void cop_vol_w(u8 data);
};

// handlers (most of it is in eturtles_state above)

void estargte_state::update_display()
{
	m_display->matrix(m_grid >> 1, m_plate);
}

u8 estargte_state::cop_latch_ack_r()
{
	// L0-L3: soundlatch from maincpu R0x
	// L7: ack from maincpu D0
	return cop_latch_r() | cop_ack_r() << 7;
}

void estargte_state::cop_vol_w(u8 data)
{
	// G0-G2: speaker volume (not mute when 0)
	m_volume->set_gain(((data & 7) | 8) / 15.0);
}

// inputs

static INPUT_PORTS_START( estargte )
	PORT_INCLUDE( eturtles )

	PORT_MODIFY("IN.0") // D1 INT0/1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::input_changed), 0) PORT_NAME("Inviso")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::input_changed), 0) PORT_NAME("Smart Bomb")

	PORT_MODIFY("IN.1") // D2 INT0/1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::input_changed), 0) PORT_NAME("Fire")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::input_changed), 0) PORT_NAME("Change Direction")

	PORT_MODIFY("IN.2") // D3 INT0/1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::input_changed), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::input_changed), 0)

	PORT_MODIFY("IN.3") // D4 INT0/1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::input_changed), 0) PORT_NAME("Thrust")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_MODIFY("IN.4") // D5 INT0/1
	PORT_CONFNAME( 0x11, 0x00, DEF_STR( Players ) ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::input_changed), 0)
	PORT_CONFSETTING(    0x10, "0 (Demo)" ) // yes, same value as 1-player, hold the Inviso button at boot to enter demo mode
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x01, "2" )
	PORT_CONFNAME( 0x02, 0x00, DEF_STR( Difficulty ) ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(eturtles_state::input_changed), 0)
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x02, "2" )

	PORT_MODIFY("IN.5") // D6 INT0/1
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void estargte_state::estargte(machine_config &config)
{
	// basic machine hardware
	HD38820(config, m_maincpu, 400000); // see game_speed
	m_maincpu->write_r<0>().set(FUNC(estargte_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(estargte_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(estargte_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(estargte_state::plate_w));
	m_maincpu->write_r<4>().set(FUNC(estargte_state::plate_w));
	m_maincpu->write_r<5>().set(FUNC(estargte_state::plate_w));
	m_maincpu->write_r<6>().set(FUNC(estargte_state::plate_w));
	m_maincpu->write_d().set(FUNC(estargte_state::grid_w));

	COP411(config, m_audiocpu, 195000); // approximation
	m_audiocpu->set_config(COP400_CKI_DIVISOR_4, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_audiocpu->write_sk().set(FUNC(estargte_state::speaker_w));
	m_audiocpu->write_d().set(FUNC(estargte_state::cop_irq_w));
	m_audiocpu->read_l().set(FUNC(estargte_state::cop_latch_ack_r));
	m_audiocpu->write_g().set(FUNC(estargte_state::cop_vol_w));

	config.set_perfect_quantum(m_maincpu);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 854);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(14, 28);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "volume", 0.25);
	FILTER_VOLUME(config, m_volume).add_route(ALL_OUTPUTS, "mono", 1.0);
}

// roms

ROM_START( estargte )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820a42", 0x0000, 0x1000, CRC(5f6d55a6) SHA1(0da32149790fa5f16097338fc80536b462169e0c) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 0x0200, "audiocpu", 0 )
	ROM_LOAD( "cop411l-kec_n", 0x0000, 0x0200, CRC(fbd3c2d3) SHA1(65b8b24d38678c3fa970bfd639e9449a75a28927) )

	ROM_REGION( 462205, "screen", 0)
	ROM_LOAD( "estargte.svg", 0, 462205, CRC(e67defb1) SHA1(905ae1285eb7d789f9887054192be6e954de3eab) )
ROM_END





/*******************************************************************************

  Gakken Heiankyo Alien (manufactured in Japan)
  * Hitachi HD38800A04 MCU, 1-bit sound
  * cyan/red VFD Futaba DM-11Z 1H

  known releases:
  - Japan: Heiankyo Alien, published by Gakken
  - USA: Earth Invaders, published by CGL

*******************************************************************************/

class ghalien_state : public hh_hmcs40_state
{
public:
	ghalien_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void ghalien(machine_config &config);

private:
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
	u16 input_r();
};

// handlers

void ghalien_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R3x(,D10-D13): vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	// update display
	m_display->matrix(m_grid, m_plate);
}

void ghalien_state::grid_w(u16 data)
{
	// D14: speaker out
	m_speaker->level_w(data >> 14 & 1);

	// D0-D6: input mux
	m_inp_mux = data & 0x7f;

	// D0-D9: vfd grid
	m_grid = data & 0x3ff;

	// D10-D13: vfd plate (update display there)
	plate_w(4, data >> 10 & 0xf);
}

u16 ghalien_state::input_r()
{
	// D15: multiplexed inputs
	return read_inputs(7) & 0x8000;
}

// inputs

static INPUT_PORTS_START( ghalien )
	PORT_START("IN.0") // D0 line D15
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY

	PORT_START("IN.1") // D1 line D15
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY

	PORT_START("IN.2") // D2 line D15
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY

	PORT_START("IN.3") // D3 line D15
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY

	PORT_START("IN.4") // D4 line D15
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Dig")

	PORT_START("IN.5") // D5 line D15
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Bury")

	PORT_START("IN.6") // D6 line D15
	PORT_CONFNAME( 0x8000, 0x0000, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(      0x0000, "1" ) // AMA
	PORT_CONFSETTING(      0x8000, "2" ) // PRO
INPUT_PORTS_END

// config

void ghalien_state::ghalien(machine_config &config)
{
	// basic machine hardware
	HD38800(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(ghalien_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(ghalien_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(ghalien_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(ghalien_state::plate_w));
	m_maincpu->write_d().set(FUNC(ghalien_state::grid_w));
	m_maincpu->read_d().set(FUNC(ghalien_state::input_r));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 699);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(10, 20);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( ghalien )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38800a04", 0x0000, 0x1000, CRC(019c3328) SHA1(9f1029c5c479f78350952c4f18747341ba5ea7a0) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 462751, "screen", 0)
	ROM_LOAD( "ghalien.svg", 0, 462751, CRC(d5f2c87d) SHA1(f3c2fa9ab9df9af038400a4fcfb2e9901eea6437) )
ROM_END





/*******************************************************************************

  Gakken Crazy Kong (manufactured in Japan)
  * PCB label: ZENY 5603601
  * Hitachi HD38800B01 MCU, 1-bit sound
  * cyan/red/blue VFD Futaba DM-54Z 2H, with bezel overlay

  known releases:
  - Japan: Crazy Kong, published by Gakken
  - USA: Super Kong, published by CGL

*******************************************************************************/

class gckong_state : public hh_hmcs40_state
{
public:
	gckong_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void gckong(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(input_changed) { update_int1(); }

private:
	void update_int1();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
};

// handlers

void gckong_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R3x(,D0,D1): vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	// update display
	m_display->matrix(m_grid, m_plate);
}

void gckong_state::grid_w(u16 data)
{
	// D2: speaker out
	m_speaker->level_w(data >> 2 & 1);

	// D5-D8: input mux
	u8 inp_mux = data >> 5 & 0xf;
	if (inp_mux != m_inp_mux)
	{
		m_inp_mux = inp_mux;
		update_int1();
	}

	// D5-D15: vfd grid
	m_grid = data >> 5 & 0x7ff;

	// D0,D1: vfd plate (update display there)
	plate_w(4, data & 3);
}

void gckong_state::update_int1()
{
	// INT1 on multiplexed inputs
	set_interrupt(1, read_inputs(4));
}

// inputs

static INPUT_PORTS_START( gckong )
	PORT_START("IN.0") // D5 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gckong_state::input_changed), 0)

	PORT_START("IN.1") // D6 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gckong_state::input_changed), 0)

	PORT_START("IN.2") // D7 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gckong_state::input_changed), 0)

	PORT_START("IN.3") // D8 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gckong_state::input_changed), 0)

	PORT_START("IN.4") // INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_hmcs40_state::single_interrupt_line), 0)

	PORT_START("IN.5") // port D
	PORT_CONFNAME( 0x0010, 0x0000, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(      0x0000, "A" )
	PORT_CONFSETTING(      0x0010, "B" )
	PORT_BIT( 0xffef, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void gckong_state::gckong(machine_config &config)
{
	// basic machine hardware
	HD38800(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(gckong_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(gckong_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(gckong_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(gckong_state::plate_w));
	m_maincpu->write_d().set(FUNC(gckong_state::grid_w));
	m_maincpu->read_d().set_ioport("IN.5");

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(479, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(11, 18);
	config.set_default_layout(layout_gckong);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( gckong )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38800b01", 0x0000, 0x1000, CRC(d5a2cca3) SHA1(37bb5784383daab672ed1e0e2362c7a40d8d9b3f) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 346544, "screen", 0)
	ROM_LOAD( "gckong.svg", 0, 346544, CRC(a0885a76) SHA1(654b170f276e58f9a13d3a873efc12b574e377cf) )
ROM_END





/*******************************************************************************

  Gakken Super Cobra
  * PCB label: SUPER COBRA 3000N
  * Hitachi QFP HD38820A32 MCU, 1-bit sound
  * cyan/red/green VFD

  known releases:
  - World: Super Cobra, published by Gakken
  - USA: Cobra Super Copter, published by Tandy

  There are 2 versions, a green one and a white one. They have the same MCU,
  though the VFD has color differences and is more compact.

  BTANB(green version): 1 rocket seems out of place at the top-right area

*******************************************************************************/

class gscobra_state : public hh_hmcs40_state
{
public:
	gscobra_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void gscobra(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(input_changed) { update_int0(); }

private:
	void update_int0();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
};

// handlers

void gscobra_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R6x(,D1-D3): vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	// update display
	m_display->matrix(m_grid, m_plate);
}

void gscobra_state::grid_w(u16 data)
{
	// D0: speaker out
	m_speaker->level_w(data & 1);

	// D10-D15: input mux
	u8 inp_mux = data >> 10 & 0x3f;
	if (inp_mux != m_inp_mux)
	{
		m_inp_mux = inp_mux;
		update_int0();
	}

	// D7-D15: vfd grid
	m_grid = data >> 7 & 0x1ff;

	// D1-D3: vfd plate (update display there)
	plate_w(7, data >> 1 & 7);
}

void gscobra_state::update_int0()
{
	// INT0 on multiplexed inputs
	set_interrupt(0, read_inputs(6));
}

// inputs

static INPUT_PORTS_START( gscobra )
	PORT_START("IN.0") // D10 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gscobra_state::input_changed), 0)

	PORT_START("IN.1") // D11 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gscobra_state::input_changed), 0)

	PORT_START("IN.2") // D12 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gscobra_state::input_changed), 0)

	PORT_START("IN.3") // D13 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gscobra_state::input_changed), 0)

	PORT_START("IN.4") // D14 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gscobra_state::input_changed), 0)

	PORT_START("IN.5") // D15 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gscobra_state::input_changed), 0)
INPUT_PORTS_END

// config

void gscobra_state::gscobra(machine_config &config)
{
	// basic machine hardware
	HD38820(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(gscobra_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(gscobra_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(gscobra_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(gscobra_state::plate_w));
	m_maincpu->write_r<4>().set(FUNC(gscobra_state::plate_w));
	m_maincpu->write_r<5>().set(FUNC(gscobra_state::plate_w));
	m_maincpu->write_r<6>().set(FUNC(gscobra_state::plate_w));
	m_maincpu->write_d().set(FUNC(gscobra_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 852);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(9, 31);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( gscobra )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820a32", 0x0000, 0x1000, CRC(7bbd130f) SHA1(91dd280e4108fad7ba99191355364bd3217b9d17) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 232919, "screen", 0)
	ROM_LOAD( "gscobra.svg", 0, 232919, CRC(c0fb4e67) SHA1(042d5490f6fa26e3c7071f05ac88c53163b9ffca) )
ROM_END





/*******************************************************************************

  Gakken Defender
  * Hitachi HD38820L53 MCU (SDIP), 1-bit sound
  * cyan/red/green VFD

  Entex Defender is possibly the same game, but with a cyan/red VFD.

*******************************************************************************/

class gdefender_state : public hh_hmcs40_state
{
public:
	gdefender_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void gdefender(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(input_changed) { update_int1(); }

private:
	void update_display();
	void update_int1();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
	u16 input_r();
};

// handlers

void gdefender_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void gdefender_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R6x: vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void gdefender_state::grid_w(u16 data)
{
	// D0: speaker out
	m_speaker->level_w(data & 1);

	// D12-D15: input mux
	u8 inp_mux = data >> 12 & 0xf;
	if (inp_mux != m_inp_mux)
	{
		m_inp_mux = inp_mux;
		update_int1();
	}

	// D3-D15: vfd grid
	m_grid = data >> 3 & 0x1fff;
	update_display();
}

u16 gdefender_state::input_r()
{
	// D1: multiplexed inputs
	return read_inputs(4) & 2;
}

void gdefender_state::update_int1()
{
	// INT1 on multiplexed inputs
	set_interrupt(1, read_inputs(4) & 1);
}

// inputs

static INPUT_PORTS_START( gdefender )
	PORT_START("IN.0") // D11 INT1/D1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gdefender_state::input_changed), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )

	PORT_START("IN.1") // D12 INT1/D1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gdefender_state::input_changed), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Warp")

	PORT_START("IN.2") // D13 INT1/D1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gdefender_state::input_changed), 0) PORT_NAME("Missile / Game")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Bomb")

	PORT_START("IN.3") // D14 INT1/D1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gdefender_state::input_changed), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void gdefender_state::gdefender(machine_config &config)
{
	// basic machine hardware
	HD38820(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(gdefender_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(gdefender_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(gdefender_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(gdefender_state::plate_w));
	m_maincpu->write_r<4>().set(FUNC(gdefender_state::plate_w));
	m_maincpu->write_r<5>().set(FUNC(gdefender_state::plate_w));
	m_maincpu->write_r<6>().set(FUNC(gdefender_state::plate_w));
	m_maincpu->write_d().set(FUNC(gdefender_state::grid_w));
	m_maincpu->read_d().set(FUNC(gdefender_state::input_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(13, 28);
	config.set_default_layout(layout_hh_hmcs40_test);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( gdefender )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820l53", 0x0000, 0x1000, CRC(fe52bbb4) SHA1(e0250954c3801af9841306c2e1fdf57f3b9edffa) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 100000, "screen", 0)
	ROM_LOAD( "gdefender.svg", 0, 100000, NO_DUMP )
ROM_END





/*******************************************************************************

  Gakken Dig Dug (manufactured in Japan)
  * PCB label: Gakken DIG-DAG KS-004283(A/B)
  * Hitachi QFP HD38820A69 MCU, 1-bit sound
  * cyan/red/yellow VFD Futaba DM-69Z 3F
  * color overlay: game row 1,2: orange1: row 3,4: o2, row 5,6: o3, row 7: o4

*******************************************************************************/

class gdigdug_state : public hh_hmcs40_state
{
public:
	gdigdug_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void gdigdug(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(input_changed) { update_int1(); }

private:
	void update_int1();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
};

// handlers

void gdigdug_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R6x(,D0-D3): vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	// update display
	m_display->matrix(m_grid, m_plate);
}

void gdigdug_state::grid_w(u16 data)
{
	// D6: speaker out
	m_speaker->level_w(data >> 6 & 1);

	// D11-D15: input mux
	u8 inp_mux = data >> 11 & 0x1f;
	if (inp_mux != m_inp_mux)
	{
		m_inp_mux = inp_mux;
		update_int1();
	}

	// D7-D15: vfd grid
	m_grid = data >> 7 & 0x1ff;

	// D0-D3: vfd plate (update display there)
	plate_w(7, data & 0xf);
}

void gdigdug_state::update_int1()
{
	// INT1 on multiplexed inputs
	set_interrupt(1, read_inputs(5));
}

// inputs

static INPUT_PORTS_START( gdigdug )
	PORT_START("IN.0") // D11 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gdigdug_state::input_changed), 0)

	PORT_START("IN.1") // D12 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gdigdug_state::input_changed), 0)

	PORT_START("IN.2") // D13 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gdigdug_state::input_changed), 0)

	PORT_START("IN.3") // D14 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gdigdug_state::input_changed), 0)

	PORT_START("IN.4") // D15 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(gdigdug_state::input_changed), 0)

	PORT_START("IN.5") // INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_hmcs40_state::single_interrupt_line), 0)
INPUT_PORTS_END

// config

void gdigdug_state::gdigdug(machine_config &config)
{
	// basic machine hardware
	HD38820(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(gdigdug_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(gdigdug_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(gdigdug_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(gdigdug_state::plate_w));
	m_maincpu->write_r<4>().set(FUNC(gdigdug_state::plate_w));
	m_maincpu->write_r<5>().set(FUNC(gdigdug_state::plate_w));
	m_maincpu->write_r<6>().set(FUNC(gdigdug_state::plate_w));
	m_maincpu->write_d().set(FUNC(gdigdug_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(476, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(9, 32);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( gdigdug )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820a69", 0x0000, 0x1000, CRC(501165a9) SHA1(8a15d00c4aa66e870cadde33148426463560d2e6) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 809074, "screen", 0)
	ROM_LOAD( "gdigdug.svg", 0, 809074, CRC(0a311f63) SHA1(4af095cd61351348ab4b2358f134fe9161e9d626) )
ROM_END





/*******************************************************************************

  Mattel World Championship Baseball (model 3201)
  * PCB label: MEL-001 Baseball Rev. B
  * Hitachi QFP HD38820A09 MCU, 3-bit sound
  * cyan/red/green VFD Futaba DM-24ZK 1G, with etched overlay

  It was patented under US4372557. To start the game in 2-player mode, simply
  turn the game on. For 1-player, turn the game on while holding the 1-key
  and use the visitor's side keypad to play offsense.

*******************************************************************************/

class mwcbaseb_state : public hh_hmcs40_state
{
public:
	mwcbaseb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void mwcbaseb(machine_config &config);

private:
	void update_display();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
	void speaker_w(u8 data);
	u8 input_r();
};

// handlers

void mwcbaseb_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void mwcbaseb_state::plate_w(offs_t offset, u8 data)
{
	// R1x-R3x,R6x: vfd plate
	int shift = (offset == 6) ? 12 : (offset - 1) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void mwcbaseb_state::grid_w(u16 data)
{
	// D9-D15: input mux
	m_inp_mux = data >> 9 & 0x7f;

	// D0-D7: vfd grid
	m_grid = data & 0xff;
	update_display();
}

void mwcbaseb_state::speaker_w(u8 data)
{
	// R50: speaker lead 1
	// R51+R52(tied together): speaker lead 2
	m_speaker->level_w(data & 7);
}

u8 mwcbaseb_state::input_r()
{
	// R4x: multiplexed inputs
	return read_inputs(7);
}

// inputs

/* physical button layout and labels are like this:

        (visitor team side)                                       (home team side)
    COMP PITCH                     [SCORE]       [INNING]
    [1]      [2]      [3]                                     [1]      [2]      [3]
    NEW PITCHER       PINCH HITTER                            NEW PITCHER       PINCH HITTER

    [4]      [5]      [6]                                     [4]      [5]      [6]
    BACKWARD (pitch)  FORWARD                                 BACKWARD (pitch)  FORWARD

    [7]      [8]      [9]                                     [7]      [8]      [9]

    BUNT     NORMAL   HR SWING                                BUNT     NORMAL   HR SWING
    [CLEAR]  [0]      [ENTER]                                 [CLEAR]  [0]      [ENTER]
    SLOW     CURVE    FAST                                    SLOW     CURVE    FAST
*/

static INPUT_PORTS_START( mwcbaseb ) // P1 = left/visitor, P2 = right/home
	PORT_START("IN.0") // D9 port R4x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Y) PORT_NAME("P2 4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_NAME("P2 3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_NAME("P2 2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_NAME("P2 1")

	PORT_START("IN.1") // D10 port R4x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_J) PORT_NAME("P2 8")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_H) PORT_NAME("P2 7")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_I) PORT_NAME("P2 6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_U) PORT_NAME("P2 5")

	PORT_START("IN.2") // D11 port R4x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_COMMA) PORT_NAME("P2 Enter")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_N) PORT_NAME("P2 Clear")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_M) PORT_NAME("P2 0")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_K) PORT_NAME("P2 9")

	PORT_START("IN.3") // D12 port R4x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Inning")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("Score")

	PORT_START("IN.4") // D13 port R4x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("P1 Enter")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Z) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME("P1 Clear")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("P1 0")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("P1 9")

	PORT_START("IN.5") // D14 port R4x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("P1 8")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("P1 7")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("P1 6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("P1 5")

	PORT_START("IN.6") // D15 port R4x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("P1 4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("P1 3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("P1 2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("P1 1")
INPUT_PORTS_END

// config

void mwcbaseb_state::mwcbaseb(machine_config &config)
{
	// basic machine hardware
	HD38820(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<1>().set(FUNC(mwcbaseb_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(mwcbaseb_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(mwcbaseb_state::plate_w));
	m_maincpu->read_r<4>().set(FUNC(mwcbaseb_state::input_r));
	m_maincpu->write_r<5>().set(FUNC(mwcbaseb_state::speaker_w));
	m_maincpu->write_r<6>().set(FUNC(mwcbaseb_state::plate_w));
	m_maincpu->write_d().set(FUNC(mwcbaseb_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 478);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(8, 16);
	m_display->set_bri_levels(0.001); // cyan elements strobed very briefly?
	config.set_default_layout(layout_mwcbaseb);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
	static const double speaker_levels[] = { 0.0, 0.5, -0.5, 0.0, -0.5, 0.0, -1.0, -0.5 };
	m_speaker->set_levels(8, speaker_levels);
}

// roms

ROM_START( mwcbaseb )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820a09", 0x0000, 0x1000, CRC(25ba7dc0) SHA1(69e0a867fdcf07b454b1faf835e576ae782432c0) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 178441, "screen", 0)
	ROM_LOAD( "mwcbaseb.svg", 0, 178441, CRC(5522a567) SHA1(2de0f75d06714fe5e223f3221cfeaac213c41ca7) )
ROM_END





/*******************************************************************************

  Mattel Star Hawk (manufactured in Japan)
  * PCB label: Kaken, PT-317B
  * Hitachi HD38800A73 MCU, 1-bit sound
  * cyan/red VFD Futaba DM-41ZK, with cross hatch on bezel
  * color overlay: score panel: red

  Before release, it was advertised as "Space Battle"(a Mattel Intellivision game).
  Kaken was a subsidiary of Bandai. Star Hawk shell design is the same as Bandai's
  games from the same era. It's likely that this was made under contract exclusively
  for Mattel. There is no indication that this game was released in Japan by Bandai.

*******************************************************************************/

class msthawk_state : public hh_hmcs40_state
{
public:
	msthawk_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void msthawk(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(input_changed) { update_int0(); }

private:
	void update_int0();
	void update_display();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
};

// handlers

void msthawk_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void msthawk_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R3x: vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void msthawk_state::grid_w(u16 data)
{
	// D5: speaker out
	m_speaker->level_w(data >> 5 & 1);

	// D10-D15: input mux
	u8 inp_mux = data >> 10 & 0x3f;
	if (inp_mux != m_inp_mux)
	{
		m_inp_mux = inp_mux;
		update_int0();
	}

	// D6-D15: vfd grid
	m_grid = data >> 6 & 0x3ff;

	// D0-D4: vfd plate
	m_plate = (m_plate & 0x00ffff) | (data << 16 & 0x1f0000);
	update_display();
}

void msthawk_state::update_int0()
{
	// INT0 on multiplexed inputs
	set_interrupt(0, read_inputs(6));
}

// inputs

static INPUT_PORTS_START( msthawk )
	PORT_START("IN.0") // D10 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(msthawk_state::input_changed), 0) PORT_NAME("Score")

	PORT_START("IN.1") // D11 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(msthawk_state::input_changed), 0) PORT_NAME("Land")

	PORT_START("IN.2") // D12 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(msthawk_state::input_changed), 0)

	PORT_START("IN.3") // D13 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(msthawk_state::input_changed), 0)

	PORT_START("IN.4") // D14 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(msthawk_state::input_changed), 0)

	PORT_START("IN.5") // D15 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(msthawk_state::input_changed), 0)

	PORT_START("IN.6") // INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_hmcs40_state::single_interrupt_line), 1) PORT_NAME("Fire")
INPUT_PORTS_END

// config

void msthawk_state::msthawk(machine_config &config)
{
	// basic machine hardware
	HD38800(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(msthawk_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(msthawk_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(msthawk_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(msthawk_state::plate_w));
	m_maincpu->write_d().set(FUNC(msthawk_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 696);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(10, 21);
	config.set_default_layout(layout_msthawk);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( msthawk )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38800a73", 0x0000, 0x1000, CRC(a4f9a523) SHA1(465f06b02e2e7d2277218fd447830725790a816c) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 191888, "screen", 0)
	ROM_LOAD( "msthawk.svg", 0, 191888, CRC(67ab8dec) SHA1(23afe1a7413ce552fb74cee61dce90f7bc92a8f3) )
ROM_END





/*******************************************************************************

  Parker Brothers Q*Bert
  * PCB label: 13662 REV-4
  * Hitachi QFP HD38820A70 MCU, 1-bit sound
  * cyan/red/green/darkgreen VFD Itron CP5137

*******************************************************************************/

class pbqbert_state : public hh_hmcs40_state
{
public:
	pbqbert_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void pbqbert(machine_config &config);

private:
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
};

// handlers

void pbqbert_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R6x(,D8): vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	// update display
	m_display->matrix(m_grid, m_plate);
}

void pbqbert_state::grid_w(u16 data)
{
	// D14: speaker out
	m_speaker->level_w(data >> 14 & 1);

	// D0-D7: vfd grid
	m_grid = data & 0xff;

	// D8: vfd plate (update display there)
	plate_w(7, data >> 8 & 1);
}

// inputs

static INPUT_PORTS_START( pbqbert )
	PORT_START("IN.0") // port D
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) // up-left
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) // up-right
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) // down-right
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) // down-left
	PORT_BIT( 0xe1ff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void pbqbert_state::pbqbert(machine_config &config)
{
	// basic machine hardware
	HD38820(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(pbqbert_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(pbqbert_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(pbqbert_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(pbqbert_state::plate_w));
	m_maincpu->write_r<4>().set(FUNC(pbqbert_state::plate_w));
	m_maincpu->write_r<5>().set(FUNC(pbqbert_state::plate_w));
	m_maincpu->write_r<6>().set(FUNC(pbqbert_state::plate_w));
	m_maincpu->write_d().set(FUNC(pbqbert_state::grid_w));
	m_maincpu->read_d().set_ioport("IN.0");

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(603, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(8, 29);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( pbqbert )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38820a70", 0x0000, 0x1000, CRC(be7c80b4) SHA1(0617a80ef7fe188ea221de32e760d45fd4318c67) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 456572, "screen", 0)
	ROM_LOAD( "pbqbert.svg", 0, 456572, CRC(87845a8b) SHA1(26030c05b9b8c1f62050a77a26be84681ff5bdf7) )
ROM_END





/*******************************************************************************

  Romtec Pucki & Monsters
  * Hitachi HD38750A67 MCU, 1-bit sound
  * cyan/red/green VFD

*******************************************************************************/

class puckimon_state : public hh_hmcs40_state
{
public:
	puckimon_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void puckimon(machine_config &config);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(u16 data);
};

// handlers

void puckimon_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void puckimon_state::grid_w(offs_t offset, u8 data)
{
	// R1x-R3x: vfd grid
	int shift = (offset - 1) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);

	// R13: vfd plate
	if (offset == 1)
		m_plate = (m_plate & 0x7fff) | (data << 12 & 0x8000);
	update_display();
}

void puckimon_state::plate_w(u16 data)
{
	// D0: speaker out
	m_speaker->level_w(data & 1);

	// D1-D15: vfd plate
	m_plate = (m_plate & 0x8000) | (data >> 1 & 0x7fff);
	update_display();

}

// inputs

static INPUT_PORTS_START( puckimon )
	PORT_START("IN.0") // port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
INPUT_PORTS_END

// config

void puckimon_state::puckimon(machine_config &config)
{
	// basic machine hardware
	HD38750(config, m_maincpu, 400000); // approximation
	m_maincpu->read_r<0>().set_ioport("IN.0").invert();
	m_maincpu->write_r<1>().set(FUNC(puckimon_state::grid_w));
	m_maincpu->write_r<2>().set(FUNC(puckimon_state::grid_w));
	m_maincpu->write_r<3>().set(FUNC(puckimon_state::grid_w));
	m_maincpu->write_d().set(FUNC(puckimon_state::plate_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(12, 16);
	config.set_default_layout(layout_hh_hmcs40_test);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( puckimon )
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38750a67", 0x0000, 0x0800, CRC(8b493783) SHA1(6c37e67de5d59889abf8a155654f130218cfc12e) )
	ROM_CONTINUE(           0x0f00, 0x0080 )

	ROM_REGION( 100000, "screen", 0)
	ROM_LOAD( "puckimon.svg", 0, 100000, NO_DUMP )
ROM_END





/*******************************************************************************

  Tomy Tron (manufactured in Japan)
  * PCB label: THN-02 2E114E07
  * Hitachi HD38800A88 MCU, 1-bit sound
  * cyan/red/green VFD NEC FIP10AM24T no. 2-8 1

*******************************************************************************/

class tmtron_state : public hh_hmcs40_state
{
public:
	tmtron_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void tmtron(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(input_changed) { update_int1(); }

private:
	void update_int1();
	void update_display();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
};

// handlers

void tmtron_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void tmtron_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R3x: vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void tmtron_state::grid_w(u16 data)
{
	// D4: speaker out
	m_speaker->level_w(data >> 4 & 1);

	// D12-D15: input mux
	u8 inp_mux = data >> 12 & 0xf;
	if (inp_mux != m_inp_mux)
	{
		m_inp_mux = inp_mux;
		update_int1();
	}

	// D6-D15: vfd grid
	m_grid = data >> 6 & 0x3ff;

	// D0-D3,D5: vfd plate
	m_plate = (m_plate & 0x00ffff) | (data << 16 & 0x2f0000);
	update_display();
}

void tmtron_state::update_int1()
{
	// INT1 on multiplexed inputs
	set_interrupt(1, read_inputs(4));
}

// inputs

static INPUT_PORTS_START( tmtron )
	PORT_START("IN.0") // D12 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tmtron_state::input_changed), 0)

	PORT_START("IN.1") // D13 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tmtron_state::input_changed), 0)

	PORT_START("IN.2") // D14 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tmtron_state::input_changed), 0)

	PORT_START("IN.3") // D15 INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tmtron_state::input_changed), 0)

	PORT_START("IN.4") // INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_hmcs40_state::single_interrupt_line), 0)
INPUT_PORTS_END

// config

void tmtron_state::tmtron(machine_config &config)
{
	// basic machine hardware
	HD38800(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(tmtron_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(tmtron_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(tmtron_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(tmtron_state::plate_w));
	m_maincpu->write_d().set(FUNC(tmtron_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 662);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(10, 22);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( tmtron )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38800a88", 0x0000, 0x1000, CRC(33db9670) SHA1(d6f747a59356526698784047bcfdbb59e79b9a23) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 384163, "screen", 0)
	ROM_LOAD( "tmtron.svg", 0, 384163, CRC(16325e35) SHA1(87769420e8597fe5109f4011353334c57967084a) )
ROM_END





/*******************************************************************************

  Tomy Kingman (manufactured in Japan)
  * PCB label: THF-01II 2E138E01/2E128E02
  * Hitachi HD38800B23 MCU, 1-bit sound
  * cyan/red/blue VFD Futaba DM-65ZK 3A

  known releases:
  - World: Kingman, published by Tomy
  - USA: Kingman, published by Tandy

*******************************************************************************/

class kingman_state : public hh_hmcs40_state
{
public:
	kingman_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void kingman(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(input_changed) { update_int0(); }

private:
	void update_int0();
	void update_display();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
};

// handlers

void kingman_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void kingman_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R3x: vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void kingman_state::grid_w(u16 data)
{
	// D6: speaker out
	m_speaker->level_w(data >> 6 & 1);

	// D12-D15: input mux
	u8 inp_mux = data >> 12 & 0xf;
	if (inp_mux != m_inp_mux)
	{
		m_inp_mux = inp_mux;
		update_int0();
	}

	// D7-D15: vfd grid
	m_grid = data >> 7 & 0x1ff;

	// D0-D4: vfd plate
	m_plate = (m_plate & 0x00ffff) | (data << 16 & 0x1f0000);
	update_display();
}

void kingman_state::update_int0()
{
	// INT0 on multiplexed inputs
	set_interrupt(0, read_inputs(4));
}

// inputs

static INPUT_PORTS_START( kingman )
	PORT_START("IN.0") // D12 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(kingman_state::input_changed), 0)

	PORT_START("IN.1") // D13 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(kingman_state::input_changed), 0)

	PORT_START("IN.2") // D14 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(kingman_state::input_changed), 0)

	PORT_START("IN.3") // D15 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(kingman_state::input_changed), 0)

	PORT_START("IN.4") // INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_hmcs40_state::single_interrupt_line), 1)
INPUT_PORTS_END

// config

void kingman_state::kingman(machine_config &config)
{
	// basic machine hardware
	HD38800(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(kingman_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(kingman_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(kingman_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(kingman_state::plate_w));
	m_maincpu->write_d().set(FUNC(kingman_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(374, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(9, 21);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( kingman )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38800b23", 0x0000, 0x1000, CRC(f8dfe14f) SHA1(660610d92ae7e5f92bddf5a3bcc2296b2ec3946b) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 396312, "screen", 0)
	ROM_LOAD( "kingman.svg", 0, 396312, CRC(025f8b94) SHA1(af679a5d487248a17f2d2d2a8953d2165eca346e) )
ROM_END





/*******************************************************************************

  Tomy Bombman (Portable 6000 series)
  * Hitachi HD38800B29 MCU, 1-bit sound
  * cyan/red/blue VFD

*******************************************************************************/

class bombman_state : public hh_hmcs40_state
{
public:
	bombman_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void bombman(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(input_changed) { update_int0(); }

private:
	void update_int0();
	void update_display();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
};

// handlers

void bombman_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void bombman_state::plate_w(offs_t offset, u8 data)
{
	// R0x-R3x: vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void bombman_state::grid_w(u16 data)
{
	// D7: speaker out
	m_speaker->level_w(data >> 7 & 1);

	// D11-D15: input mux
	u8 inp_mux = data >> 11 & 0x1f;
	if (inp_mux != m_inp_mux)
	{
		m_inp_mux = inp_mux;
		update_int0();
	}

	// D8-D15: vfd grid
	m_grid = data >> 8 & 0xff;

	// D0-D4: vfd plate
	m_plate = (m_plate & 0x00ffff) | (data << 16 & 0x1f0000);
	update_display();
}

void bombman_state::update_int0()
{
	// INT0 on multiplexed inputs
	set_interrupt(0, read_inputs(5));
}

// inputs

static INPUT_PORTS_START( bombman )
	PORT_START("IN.0") // D11 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bombman_state::input_changed), 0)

	PORT_START("IN.1") // D12 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bombman_state::input_changed), 0)

	PORT_START("IN.2") // D13 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bombman_state::input_changed), 0)

	PORT_START("IN.3") // D14 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bombman_state::input_changed), 0)

	PORT_START("IN.4") // D15 INT0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bombman_state::input_changed), 0)
INPUT_PORTS_END

// config

void bombman_state::bombman(machine_config &config)
{
	// basic machine hardware
	HD38800(config, m_maincpu, 400000); // approximation
	m_maincpu->write_r<0>().set(FUNC(bombman_state::plate_w));
	m_maincpu->write_r<1>().set(FUNC(bombman_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(bombman_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(bombman_state::plate_w));
	m_maincpu->write_d().set(FUNC(bombman_state::grid_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 21);
	config.set_default_layout(layout_hh_hmcs40_test);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( bombman )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38800b29", 0x0000, 0x1000, CRC(f99ebd3f) SHA1(5016d4e35efa1353d26e60f98d8a027773d571a0) )
	ROM_CONTINUE(           0x1e80, 0x0100 )

	ROM_REGION( 100000, "screen", 0)
	ROM_LOAD( "bombman.svg", 0, 100000, NO_DUMP )
ROM_END





/*******************************************************************************

  VTech Invaders (manufactured in Taiwan)
  * Hitachi HD38750A45 MCU, 1-bit sound
  * cyan/red VFD Futaba DM-26Z 1G
  * color overlay: alien row 2,4: yellow, row 2,5: red

  known releases:
  - USA: Invaders/Sonic Invader, published by VTech
  - UK: Cosmic Invader, published by Grandstand
  - UK: Galactic Invaders, published by Prinztronic

*******************************************************************************/

class vinvader_state : public hh_hmcs40_state
{
public:
	vinvader_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_hmcs40_state(mconfig, type, tag)
	{ }

	void vinvader(machine_config &config);

private:
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
};

// handlers

void vinvader_state::plate_w(offs_t offset, u8 data)
{
	// R1x-R3x(,D4-D6): vfd plate
	int shift = (offset - 1) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	// update display
	m_display->matrix(m_grid, m_plate);
}

void vinvader_state::grid_w(u16 data)
{
	// D0: speaker out
	m_speaker->level_w(data & 1);

	// D7-D15: vfd grid
	m_grid = data >> 7 & 0x1ff;

	// D4-D6: vfd plate (update display there)
	plate_w(3 + 1, data >> 4 & 7);
}

// inputs

static INPUT_PORTS_START( vinvader )
	PORT_START("IN.0") // port R0x
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // port D
	PORT_CONFNAME( 0x0002, 0x0000, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(      0x0000, "1" )
	PORT_CONFSETTING(      0x0002, "2" )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0xfff5, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void vinvader_state::vinvader(machine_config &config)
{
	// basic machine hardware
	HD38750(config, m_maincpu, 300000); // approximation
	m_maincpu->read_r<0>().set_ioport("IN.0");
	m_maincpu->write_r<1>().set(FUNC(vinvader_state::plate_w));
	m_maincpu->write_r<2>().set(FUNC(vinvader_state::plate_w));
	m_maincpu->write_r<3>().set(FUNC(vinvader_state::plate_w));
	m_maincpu->write_d().set(FUNC(vinvader_state::grid_w));
	m_maincpu->read_d().set_ioport("IN.1");

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(233, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(9, 15);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( vinvader )
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "hd38750a45", 0x0000, 0x0800, CRC(32de6056) SHA1(70238c6c40c3d513f8eced1cb81bdd4dbe12f16c) )
	ROM_CONTINUE(           0x0f00, 0x0080 )

	ROM_REGION( 166391, "screen", 0)
	ROM_LOAD( "vinvader.svg", 0, 166391, CRC(baa5595a) SHA1(323efa378cd47297b55be5d93b130bb8f955fbe9) )
ROM_END



} // anonymous namespace

/*******************************************************************************

  Game driver(s)

*******************************************************************************/

//    YEAR  NAME       PARENT    COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY, FULLNAME, FLAGS
SYST( 1982, wantgman,  0,        0,      wantgman,  wantgman,  wantgman_state,  empty_init, "Actronics / Hanzawa", "Wanted G-Man", MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING )

SYST( 1979, bambball,  0,        0,      bambball,  bambball,  bambball_state,  empty_init, "Bambino", "Dribble Away Basketball", MACHINE_SUPPORTS_SAVE )
SYST( 1979, bmboxing,  0,        0,      bmboxing,  bmboxing,  bmboxing_state,  empty_init, "Bambino", "Knock-Em Out Boxing", MACHINE_SUPPORTS_SAVE )

SYST( 1982, bfriskyt,  0,        0,      bfriskyt,  bfriskyt,  bfriskyt_state,  empty_init, "Bandai", "Frisky Tom (Bandai)", MACHINE_SUPPORTS_SAVE )
SYST( 1981, packmon,   0,        0,      packmon,   packmon,   packmon_state,   empty_init, "Bandai", "Packri Monster", MACHINE_SUPPORTS_SAVE )
SYST( 1982, bzaxxon,   0,        0,      bzaxxon,   bzaxxon,   bzaxxon_state,   empty_init, "Bandai", "Zaxxon (Bandai)", MACHINE_SUPPORTS_SAVE )
SYST( 1983, zackman,   0,        0,      zackman,   zackman,   zackman_state,   empty_init, "Bandai", "Zackman", MACHINE_SUPPORTS_SAVE )
SYST( 1983, bpengo,    0,        0,      bpengo,    bpengo,    bpengo_state,    empty_init, "Bandai", "Pengo (Bandai)", MACHINE_SUPPORTS_SAVE )
SYST( 1983, bbtime,    0,        0,      bbtime,    bbtime,    bbtime_state,    empty_init, "Bandai", "Burger Time (Bandai)", MACHINE_SUPPORTS_SAVE )
SYST( 1983, ktparman,  0,        0,      ktparman,  ktparman,  ktparman_state,  empty_init, "Bandai", "Kiteyo Parman", MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING )
SYST( 1983, bdoramon,  0,        0,      bdoramon,  bdoramon,  bdoramon_state,  empty_init, "Bandai", "Dokodemo Dorayaki Doraemon", MACHINE_SUPPORTS_SAVE )
SYST( 1983, tkjmaru,   0,        0,      tkjmaru,   tkjmaru,   tkjmaru_state,   empty_init, "Bandai", "Toukon Juohmaru", MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING )
SYST( 1983, bultrman,  0,        0,      bultrman,  bultrman,  bultrman_state,  empty_init, "Bandai", "Ultraman Monster Battle", MACHINE_SUPPORTS_SAVE )
SYST( 1983, ggdman,    0,        0,      ggdman,    ggdman,    ggdman_state,    empty_init, "Bandai", "Go Go Dynaman", MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING )
SYST( 1984, machiman,  0,        0,      machiman,  machiman,  machiman_state,  empty_init, "Bandai", "Machine Man", MACHINE_SUPPORTS_SAVE )
SYST( 1984, pairmtch,  0,        0,      pairmtch,  pairmtch,  pairmtch_state,  empty_init, "Bandai", "Pair Match", MACHINE_SUPPORTS_SAVE )

SYST( 1981, alnattck,  0,        0,      alnattck,  alnattck,  alnattck_state,  empty_init, "Coleco", "Alien Attack", MACHINE_SUPPORTS_SAVE )
SYST( 1982, cdkong,    0,        0,      cdkong,    cdkong,    cdkong_state,    empty_init, "Coleco", "Donkey Kong (Coleco)", MACHINE_SUPPORTS_SAVE )
SYST( 1982, cgalaxn,   0,        0,      cgalaxn,   cgalaxn,   cgalaxn_state,   empty_init, "Coleco", "Galaxian (Coleco)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )
SYST( 1981, cpacman,   0,        0,      cpacman,   cpacman,   cpacman_state,   empty_init, "Coleco", "Pac-Man (Coleco, rev. 29)", MACHINE_SUPPORTS_SAVE )
SYST( 1981, cpacmanr1, cpacman,  0,      cpacman,   cpacman,   cpacman_state,   empty_init, "Coleco", "Pac-Man (Coleco, rev. 28)", MACHINE_SUPPORTS_SAVE )
SYST( 1983, cmspacmn,  0,        0,      cmspacmn,  cmspacmn,  cmspacmn_state,  empty_init, "Coleco", "Ms. Pac-Man (Coleco)", MACHINE_SUPPORTS_SAVE )

SYST( 1981, egalaxn2,  0,        0,      egalaxn2,  egalaxn2,  egalaxn2_state,  empty_init, "Entex", "Galaxian 2 (Entex)", MACHINE_SUPPORTS_SAVE )
SYST( 1981, epacman2,  0,        0,      epacman2,  epacman2,  epacman2_state,  empty_init, "Entex", "Pac Man 2 (Entex, cyan Pacman)", MACHINE_SUPPORTS_SAVE )
SYST( 1981, epacman2r, epacman2, 0,      epacman2,  epacman2,  epacman2_state,  empty_init, "Entex", "Pac Man 2 (Entex, red Pacman)", MACHINE_SUPPORTS_SAVE )
SYST( 1982, einvader2, 0,        0,      einvader2, einvader2, einvader2_state, empty_init, "Entex", "Super Space Invader 2 (Entex, black version)", MACHINE_SUPPORTS_SAVE )
SYST( 1982, eturtles,  0,        0,      eturtles,  eturtles,  eturtles_state,  empty_init, "Entex", "Turtles (Entex)", MACHINE_SUPPORTS_SAVE )
SYST( 1982, estargte,  0,        0,      estargte,  estargte,  estargte_state,  empty_init, "Entex", "Stargate (Entex)", MACHINE_SUPPORTS_SAVE )

SYST( 1980, ghalien,   0,        0,      ghalien,   ghalien,   ghalien_state,   empty_init, "Gakken", "Heiankyo Alien (Gakken)", MACHINE_SUPPORTS_SAVE )
SYST( 1982, gckong,    0,        0,      gckong,    gckong,    gckong_state,    empty_init, "Gakken", "Crazy Kong (Gakken)", MACHINE_SUPPORTS_SAVE )
SYST( 1982, gscobra,   0,        0,      gscobra,   gscobra,   gscobra_state,   empty_init, "Gakken", "Super Cobra (Gakken, green version)", MACHINE_SUPPORTS_SAVE )
SYST( 1983, gdefender, 0,        0,      gdefender, gdefender, gdefender_state, empty_init, "Gakken", "Defender (Gakken)", MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING )
SYST( 1983, gdigdug,   0,        0,      gdigdug,   gdigdug,   gdigdug_state,   empty_init, "Gakken", "Dig Dug (Gakken)", MACHINE_SUPPORTS_SAVE )

SYST( 1980, mwcbaseb,  0,        0,      mwcbaseb,  mwcbaseb,  mwcbaseb_state,  empty_init, "Mattel Electronics", "World Championship Baseball", MACHINE_SUPPORTS_SAVE )
SYST( 1982, msthawk,   0,        0,      msthawk,   msthawk,   msthawk_state,   empty_init, "Mattel Electronics", "Star Hawk (Mattel)", MACHINE_SUPPORTS_SAVE )

SYST( 1983, pbqbert,   0,        0,      pbqbert,   pbqbert,   pbqbert_state,   empty_init, "Parker Brothers", "Q*Bert (Parker Brothers)", MACHINE_SUPPORTS_SAVE )

SYST( 1982, puckimon,  0,        0,      puckimon,  puckimon,  puckimon_state,  empty_init, "Romtec", "Pucki & Monsters", MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING )

SYST( 1982, tmtron,    0,        0,      tmtron,    tmtron,    tmtron_state,    empty_init, "Tomy", "Tron (Tomy)", MACHINE_SUPPORTS_SAVE )
SYST( 1982, kingman,   0,        0,      kingman,   kingman,   kingman_state,   empty_init, "Tomy", "Kingman", MACHINE_SUPPORTS_SAVE )
SYST( 1984, bombman,   0,        0,      bombman,   bombman,   bombman_state,   empty_init, "Tomy", "Bombman", MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING )

SYST( 1981, vinvader,  0,        0,      vinvader,  vinvader,  vinvader_state,  empty_init, "VTech", "Invaders (VTech)", MACHINE_SUPPORTS_SAVE )



hh_ht11xx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
// thanks-to:azya

/*

The following should fit here

HT1132A Space War
HT1134A Pin Ball
HT1136A Football
HT1137A Motorcycle
HT113AA Streetfighters
HT113FA Submarine War
HT113JA Baseball
HT113RA Poker and Black Jack
HT113SA Casino Game 5-in-1
HT113LA Original "Tea" Brick Game
HTG1395 3-in-1 (Car racing, Soccer, The eagle preys on the chicken)

(and likely many more)

TODO:
- add LCD deflicker like hh_sm510?

*/

#include "emu.h"

#include "cpu/ht1130/ht1130.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "hh_ht11xx_lcd.lh"


namespace {

// base class

class hh_ht11xx_state : public driver_device
{
public:
	virtual DECLARE_INPUT_CHANGED_MEMBER(input_wakeup);

protected:
	hh_ht11xx_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu")
	{ }

	void mcfg_svg_screen(machine_config &config, u16 width, u16 height, const char *tag = "screen");

	required_device<ht1130_device> m_maincpu;
};

INPUT_CHANGED_MEMBER(hh_ht11xx_state::input_wakeup)
{
	m_maincpu->set_input_line(HT1130_EXT_WAKEUP_LINE, newval ? CLEAR_LINE : ASSERT_LINE);
}

void hh_ht11xx_state::mcfg_svg_screen(machine_config &config, u16 width, u16 height, const char *tag)
{
	screen_device &screen(SCREEN(config, tag, SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(width, height);
	screen.set_visarea_full();

	config.set_default_layout(layout_hh_ht11xx_lcd);
}


// HT1130 class

class hh_ht1130_state : public hh_ht11xx_state
{
public:
	hh_ht1130_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ht11xx_state(mconfig, type, tag),
		m_out_x(*this, "%u.%u", 0U, 0U),
		m_in(*this, "IN%u", 1)
	{ }

	void ga888(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

	void segment_w(offs_t offset, u64 data);

private:
	output_finder<4, 32> m_out_x;
	required_ioport_array<3> m_in;
};

void hh_ht1130_state::machine_start()
{
	m_out_x.resolve();
}

void hh_ht1130_state::segment_w(offs_t offset, u64 data)
{
	// output to x.y where x = COM# and y = SEG#
	for (int i = 0; i < 32; i++)
		m_out_x[offset][i] = BIT(data, i);
}


// HT1190 class

class hh_ht1190_state : public hh_ht11xx_state
{
public:
	hh_ht1190_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ht11xx_state(mconfig, type, tag),
		m_out_x(*this, "%u.%u", 0U, 0U),
		m_in(*this, "IN%u", 1)
	{ }

	void brke23p2(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

	void segment_w(offs_t offset, u64 data);

private:
	output_finder<8, 40> m_out_x;
	required_ioport_array<2> m_in;
};

void hh_ht1190_state::machine_start()
{
	m_out_x.resolve();
}

void hh_ht1190_state::segment_w(offs_t offset, u64 data)
{
	// output to x.y where x = COM# and y = SEG#
	for (int i = 0; i < 40; i++)
		m_out_x[offset][i] = BIT(data, i);
}



/*******************************************************************************

  Minidrivers (optional subclass, I/O, Inputs, Machine Config, ROM Defs)

*******************************************************************************/

/*******************************************************************************

  E-Star Brick Game 96 in 1 (E-23 Plus Mark II)
  * Holtek HT1190
  * 10*20 LCD screen + custom segments, 1-bit sound

*******************************************************************************/

// inputs

static INPUT_PORTS_START( brke23p2 )
	PORT_START("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_START )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Mute")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_POWER_ON ) PORT_NAME("Power") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_ht11xx_state::input_wakeup), 0)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN2") // not a joystick, but buttons are used for directional inputs in the snake game etc.
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Up / Rotate")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Down / Drop")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Right")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Left")
INPUT_PORTS_END

// config

void hh_ht1190_state::brke23p2(machine_config &config)
{
	HT1190(config, m_maincpu, 1000000/8); // frequency?
	m_maincpu->segment_out_cb().set(FUNC(hh_ht1190_state::segment_w));

	m_maincpu->ps_in_cb().set_ioport(m_in[0]);
	m_maincpu->pp_in_cb().set_ioport(m_in[1]);

	SPEAKER(config, "speaker").front_center();

	mcfg_svg_screen(config, 755, 1080);
}

// roms

ROM_START( brke23p2 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "brke23p2.bin", 0x0000, 0x1000, CRC(8045fac4) SHA1(a36213309e6add31f31e4248f02f17de9914a5c1) ) // visual decap

	ROM_REGION( 0x280, "melody", 0 )
	ROM_LOAD( "e23plusmarkii96in1.srom", 0x000, 0x280, CRC(591a8a21) SHA1(f039359e8e1d1bf75581a4c852b263c8c140e072) )

	ROM_REGION( 160500, "screen", 0)
	ROM_LOAD( "brke23p2.svg", 0, 160500, CRC(9edf8aab) SHA1(f2ab907d23517612196648f1b5b0cb9b4a1ab3bd) )
ROM_END





/*******************************************************************************

  Block Game & Echo Key GA888
  * Holtek HT1130
  * 8*12 LCD screen + 8 custom segments, 1-bit sound

*******************************************************************************/

// inputs

static INPUT_PORTS_START( ga888 ) // the unit also has an up button, and a reset button, is 'up' connected to anything?
	PORT_START("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Pause / Power") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_ht11xx_state::input_wakeup), 0)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_START ) PORT_NAME("Start / Rotate")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Down / Drop")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Right / Sound")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Left")
INPUT_PORTS_END

// config

void hh_ht1130_state::ga888(machine_config &config)
{
	HT1130(config, m_maincpu, 1000000/8); // frequency?
	m_maincpu->segment_out_cb().set(FUNC(hh_ht1130_state::segment_w));

	m_maincpu->ps_in_cb().set_ioport(m_in[0]);
	m_maincpu->pp_in_cb().set_ioport(m_in[1]);
	m_maincpu->pm_in_cb().set_ioport(m_in[2]);

	SPEAKER(config, "speaker").front_center();

	mcfg_svg_screen(config, 698, 1080);
}

// roms

ROM_START( ga888 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "ga888.bin", 0x0000, 0x1000, CRC(cb3f8ff4) SHA1(00b08773e8ee7577377f4d52cd1e6bb657b6f242) ) // visual decap

	ROM_REGION( 0x280, "melody", 0 )
	ROM_LOAD( "ga888.srom", 0x000, 0x280, CRC(a8495ac7) SHA1(f6c24fc9622bff73ab09b5ee77eb338f27f7a6b1) )

	ROM_REGION( 85508, "screen", 0)
	ROM_LOAD( "ga888.svg", 0, 85508, CRC(9ab6dd67) SHA1(a4365a00204bf4e376f28600c0b87289bda0cbb0) )
ROM_END

} // anonymous namespace



/*******************************************************************************

  Game driver(s)

*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS            INIT        COMPANY, FULLNAME, FLAGS
CONS( 1993, brke23p2, 0,      0,      brke23p2, brke23p2, hh_ht1190_state, empty_init, "E-Star", "Brick Game 96 in 1 (E-23 Plus Mark II)", MACHINE_IMPERFECT_TIMING | MACHINE_NO_SOUND ) // some other dieshots have 1996 on them, it is also possible the software is from Holtek

CONS( 199?, ga888,    0,      0,      ga888,    ga888,    hh_ht1130_state, empty_init, "<unknown>", "Block Game & Echo Key GA888", MACHINE_IMPERFECT_TIMING | MACHINE_NO_SOUND ) // clone of Tetris Jr?



hh_melps4.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Kevin Horton
/*******************************************************************************

Mitsubishi MELPS 4 MCU tabletops/handhelds or other simple devices, most of them
are VFD electronic games/toys.

TODO:
- dump/add Gakken version of Frogger

*******************************************************************************/

#include "emu.h"

#include "cpu/melps4/m58846.h"
#include "sound/spkrdev.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

//#include "hh_melps4_test.lh" // common test-layout - no svg artwork(yet), use external artwork


namespace {

class hh_melps4_state : public driver_device
{
public:
	hh_melps4_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_speaker(*this, "speaker"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	virtual DECLARE_INPUT_CHANGED_MEMBER(reset_button);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// devices
	required_device<m58846_device> m_maincpu;
	optional_device<pwm_display_device> m_display;
	optional_device<speaker_sound_device> m_speaker;
	optional_ioport_array<4> m_inputs; // max 4

	// misc common
	u16 m_inp_mux = 0; // multiplexed inputs mask

	u32 m_grid = 0;    // VFD current row data
	u32 m_plate = 0;   // VFD current column data

	u8 read_inputs(int columns);
};


// machine start/reset

void hh_melps4_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_grid));
	save_item(NAME(m_plate));
}

void hh_melps4_state::machine_reset()
{
}



/*******************************************************************************

  Helper Functions

*******************************************************************************/

// generic input handlers

u8 hh_melps4_state::read_inputs(int columns)
{
	u8 ret = 0;

	// read selected input rows
	for (int i = 0; i < columns; i++)
		if (BIT(m_inp_mux, i))
			ret |= m_inputs[i]->read();

	return ret;
}

INPUT_CHANGED_MEMBER(hh_melps4_state::reset_button)
{
	// for when reset button is directly tied to MCU reset pin
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}



/*******************************************************************************

  Minidrivers (subclass, I/O, Inputs, Machine Config, ROM Defs)

*******************************************************************************/

/*******************************************************************************

  Coleco Frogger (manufactured in Japan, licensed from Sega)
  * PCB label: Coleco Frogger Code No. 01-81543, KS-003282 Japan
  * Mitsubishi M58846-701P MCU, 1-bit sound
  * cyan/red/green VFD Itron CP5090GLR R1B
  * color overlay: row 2(goal): blue, row 3-6: yellow

  Gakken / Konami Frogger
  * PCB label: Konami Gakken KH-8201D
  * Mitsubishi M58846-700P MCU (Konami logo on it), 1-bit sound
  * cyan/red/green VFD
  * color overlay: row 2(goal): blue, row 3-6: yellow, row 8-10(cars): red

*******************************************************************************/

class cfrogger_state : public hh_melps4_state
{
public:
	cfrogger_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_melps4_state(mconfig, type, tag)
	{ }

	void cfrogger(machine_config &config);

private:
	void update_display();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
	u16 input_r();
};

// handlers

void cfrogger_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void cfrogger_state::plate_w(offs_t offset, u8 data)
{
	// F0,F1: input mux
	if (offset == MELPS4_PORTF)
		m_inp_mux = data & 3;

	// Sx,Fx,Gx: vfd plate
	int mask = (offset == MELPS4_PORTS) ? 0xff : 0xf; // port S is 8-bit
	int shift = (offset == MELPS4_PORTS) ? 0 : (offset + 1) * 4;
	m_plate = (m_plate & ~(mask << shift)) | (data << shift);
	update_display();
}

void cfrogger_state::grid_w(u16 data)
{
	// D0-D11: vfd grid
	m_grid = data;
	update_display();
}

u16 cfrogger_state::input_r()
{
	// K0,K1: multiplexed inputs
	// K2: N/C
	// K3: fixed input
	return (m_inputs[2]->read() & 8) | (read_inputs(2) & 3);
}

// inputs

static INPUT_PORTS_START( cfrogger )
	PORT_START("IN.0") // F0 port K0,K1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )

	PORT_START("IN.1") // F1 port K0,K1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )

	PORT_START("IN.2") // K3
	PORT_CONFNAME( 0x08, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x08, "2" )

	PORT_START("RESET")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_melps4_state::reset_button), 0)
INPUT_PORTS_END

// config

void cfrogger_state::cfrogger(machine_config &config)
{
	// basic machine hardware
	M58846(config, m_maincpu, 600_kHz_XTAL);
	m_maincpu->read_k().set(FUNC(cfrogger_state::input_r));
	m_maincpu->write_s().set(FUNC(cfrogger_state::plate_w));
	m_maincpu->write_f().set(FUNC(cfrogger_state::plate_w));
	m_maincpu->write_g().set(FUNC(cfrogger_state::plate_w));
	m_maincpu->write_d().set(FUNC(cfrogger_state::grid_w));
	m_maincpu->write_t().set(m_speaker, FUNC(speaker_sound_device::level_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(500, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(12, 16);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( cfrogger )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "m58846-701p", 0x0000, 0x1000, CRC(ba52a242) SHA1(7fa53b617f4bb54be32eb209e9b88131e11cb518) )

	ROM_REGION( 786254, "screen", 0)
	ROM_LOAD( "cfrogger.svg", 0, 786254, CRC(1d63f0ad) SHA1(d1b3f504a649c29b2f47ee1715d47dfd0f3eca05) )
ROM_END





/*******************************************************************************

  Gakken / Konami Jungler (manufactured in Japan)
  * PCB label: Konami Gakken GR503
  * Mitsubishi M58846-702P MCU, 1-bit sound
  * cyan/red/green VFD Itron CP5143GLR SGA
  * color overlay: all yellow

*******************************************************************************/

class gjungler_state : public hh_melps4_state
{
public:
	gjungler_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_melps4_state(mconfig, type, tag)
	{ }

	void gjungler(machine_config &config);

private:
	void update_display();
	void plate_w(offs_t offset, u8 data);
	void grid_w(u16 data);
	u16 input_r();
};

// handlers

void gjungler_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void gjungler_state::plate_w(offs_t offset, u8 data)
{
	// G0,G1: input mux
	if (offset == MELPS4_PORTG)
		m_inp_mux = data & 3;

	// Sx,Fx,Gx,U: vfd plate
	int mask = (offset == MELPS4_PORTS) ? 0xff : 0xf; // port S is 8-bit
	int shift = (offset == MELPS4_PORTS) ? 0 : (offset + 1) * 4;
	m_plate = (m_plate & ~(mask << shift)) | (data << shift);
	update_display();
}

void gjungler_state::grid_w(u16 data)
{
	// D0-D11: vfd grid
	m_grid = data;
	update_display();
}

u16 gjungler_state::input_r()
{
	// K0,K1: multiplexed inputs
	// K2,K3: fixed inputs
	return (m_inputs[2]->read() & 0xc) | (read_inputs(2) & 3);
}

// inputs

static INPUT_PORTS_START( gjungler )
	PORT_START("IN.0") // G0 port K0,K1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )

	PORT_START("IN.1") // G1 port K0,K1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )

	PORT_START("IN.2") // K2,K3
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_CONFNAME( 0x08, 0x08, "Game Mode" )
	PORT_CONFSETTING(    0x08, "A" )
	PORT_CONFSETTING(    0x00, "B" )

	PORT_START("RESET")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_melps4_state::reset_button), 0)
INPUT_PORTS_END

// config

void gjungler_state::gjungler(machine_config &config)
{
	// basic machine hardware
	M58846(config, m_maincpu, 600_kHz_XTAL);
	m_maincpu->read_k().set(FUNC(gjungler_state::input_r));
	m_maincpu->write_s().set(FUNC(gjungler_state::plate_w));
	m_maincpu->write_f().set(FUNC(gjungler_state::plate_w));
	m_maincpu->write_g().set(FUNC(gjungler_state::plate_w));
	m_maincpu->write_u().set(FUNC(gjungler_state::plate_w));
	m_maincpu->write_d().set(FUNC(gjungler_state::grid_w));
	m_maincpu->write_t().set(m_speaker, FUNC(speaker_sound_device::level_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(481, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(12, 17);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( gjungler )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "m58846-702p", 0x0000, 0x1000, CRC(94ab7060) SHA1(3389bc115d1df8d01a30611fa9e95a900d32b29b) )

	ROM_REGION( 419707, "screen", 0)
	ROM_LOAD( "gjungler.svg", 0, 419707, CRC(c43d55d7) SHA1(e25002377a6eab25607949b6cc49894fbdaa44a9) )
ROM_END



} // anonymous namespace

/*******************************************************************************

  Game driver(s)

*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1982, cfrogger, 0,      0,      cfrogger, cfrogger, cfrogger_state, empty_init, "Coleco / Konami", "Frogger (Coleco)", MACHINE_SUPPORTS_SAVE )

SYST( 1982, gjungler, 0,      0,      gjungler, gjungler, gjungler_state, empty_init, "Gakken / Konami", "Jungler (Gakken)", MACHINE_SUPPORTS_SAVE )



hh_mn1400.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Matsushita (Panasonic) MN1400 handhelds. Matsushita used this MCU in their
audio/video equipment, and it's used in some handheld toys too.

*******************************************************************************/

#include "emu.h"

#include "cpu/mn1400/mn1400.h"
#include "sound/spkrdev.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "compperf.lh"
#include "scrablexa.lh"
#include "tmbaskb.lh"

//#include "hh_mn1400_test.lh" // common test-layout - use external artwork


namespace {

class hh_mn1400_state : public driver_device
{
public:
	hh_mn1400_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_speaker(*this, "speaker"),
		m_inputs(*this, "IN.%u", 0)
	{ }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// devices
	required_device<mn1400_base_device> m_maincpu;
	optional_device<pwm_display_device> m_display;
	optional_device<speaker_sound_device> m_speaker;
	optional_ioport_array<5> m_inputs; // max 5

	u16 m_inp_mux = 0; // multiplexed inputs mask

	// MCU output pins state
	u16 m_c = 0;       // C pins
	u8 m_d = 0;        // D pins
	u8 m_e = 0;        // E pins

	u16 read_inputs(int columns);
};


// machine start/reset

void hh_mn1400_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_c));
	save_item(NAME(m_d));
	save_item(NAME(m_e));
}

void hh_mn1400_state::machine_reset()
{
}



/*******************************************************************************

  Helper Functions

*******************************************************************************/

// generic input handlers

u16 hh_mn1400_state::read_inputs(int columns)
{
	u16 ret = 0;

	// read selected input rows
	for (int i = 0; i < columns; i++)
		if (BIT(m_inp_mux, i))
			ret |= m_inputs[i]->read();

	return ret;
}



/*******************************************************************************

  Minidrivers (subclass, I/O, Inputs, Machine Config, ROM Defs)

*******************************************************************************/

/*******************************************************************************

  Lakeside Computer Perfection
  * PCB label: Lakeside, PANASONIC, TCI-A4H94HB
  * MN1400ML (28 pins, die label: 1400 ML-0)
  * 10 LEDs, 2-bit sound

  known releases:
  - USA: Computer Perfection, published by Lakeside
  - UK: Computer Perfection, published by Action GT

*******************************************************************************/

class compperf_state : public hh_mn1400_state
{
public:
	compperf_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_mn1400_state(mconfig, type, tag)
	{ }

	void compperf(machine_config &config);

private:
	void update_display();
	void write_c(u16 data);
	void write_d(u8 data);
	void write_e(u8 data);
	u8 read_sns();
};

// handlers

void compperf_state::update_display()
{
	m_display->matrix(1, ~m_inp_mux);
}

void compperf_state::write_c(u16 data)
{
	// CO5-CO9: leds/input mux part
	m_inp_mux = (m_inp_mux & ~0x3e0) | (data & 0x3e0);
	update_display();
}

void compperf_state::write_d(u8 data)
{
	// DO0-DO3: leds/input mux part
	m_inp_mux = (m_inp_mux & ~0x1e) | (data << 1 & 0x1e);
	update_display();
}

void compperf_state::write_e(u8 data)
{
	// EO0: leds/input mux part
	m_inp_mux = (m_inp_mux & ~1) | (data & 1);
	update_display();

	// EO2,EO3: speaker out
	m_speaker->level_w(data >> 2 & 3);
}

u8 compperf_state::read_sns()
{
	// SNS0: multiplexed inputs, SNS1: set button
	u8 sns0 = (m_inputs[2]->read() & m_inp_mux) ? 1 : 0;
	return (m_inputs[3]->read() & 2) | sns0;
}

// inputs

static INPUT_PORTS_START( compperf )
	PORT_START("IN.0") // AI
	PORT_CONFNAME( 0x07, 0x01, "Game" )
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_CONFSETTING(    0x04, "3" )
	PORT_CONFSETTING(    0x00, "4" )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_NAME("Score")

	PORT_START("IN.1") // BI
	PORT_CONFNAME( 0x03, 0x00, "Mode" )
	PORT_CONFSETTING(    0x01, "T" ) // Test
	PORT_CONFSETTING(    0x00, "N" ) // New
	PORT_CONFSETTING(    0x02, "R" ) // Repeat
	PORT_CONFNAME( 0x0c, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x08, "2" )
	PORT_CONFSETTING(    0x04, "3" )

	PORT_START("IN.2") // EO/DO/CO SNS0
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_BUTTON3 )
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_BUTTON4 )
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_BUTTON5 )
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_BUTTON6 )
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_BUTTON7 )
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_BUTTON8 )
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_BUTTON9 )
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_BUTTON10 ) // 0

	PORT_START("IN.3") // SNS1
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START ) PORT_NAME("Set")
INPUT_PORTS_END

// config

void compperf_state::compperf(machine_config &config)
{
	// basic machine hardware
	MN1400_28PINS(config, m_maincpu, 290000); // approximation - RC osc. R=18K, C=100pF
	m_maincpu->write_c().set(FUNC(compperf_state::write_c));
	m_maincpu->write_d().set(FUNC(compperf_state::write_d));
	m_maincpu->write_e().set(FUNC(compperf_state::write_e));
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->read_b().set_ioport("IN.1");
	m_maincpu->read_sns().set(FUNC(compperf_state::read_sns));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 10);
	m_display->set_bri_levels(0.25);
	config.set_default_layout(layout_compperf);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[4] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.125);
}

// roms

ROM_START( compperf )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mn1400ml", 0x0000, 0x0400, CRC(8d5ab2af) SHA1(f6281bb5a5a7ffeead107681d68b66a4844e93ad) )

	ROM_REGION( 428, "maincpu:opla", 0 ) // 4-bit
	ROM_LOAD( "mn1400_common1_output.pla", 0, 428, CRC(07489d8b) SHA1(4fe65af8ee798490ed0bbe6a77d61713a2fb28b4) )
ROM_END





/*******************************************************************************

  Selchow & Righter Scrabble Lexor
  * PCB label: 2294HB
  * MN1405MS (die label: 1405 MS-0)
  * 8-digit 14-seg LEDs, 2-bit sound

  This is the MN1405 version, see scrablex.cpp for the MB8841 version.

*******************************************************************************/

class scrablexa_state : public hh_mn1400_state
{
public:
	scrablexa_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_mn1400_state(mconfig, type, tag)
	{ }

	void scrablexa(machine_config &config);

private:
	void update_display();
	void write_c(u16 data);
	void write_d(u8 data);
	void write_e(u8 data);
	template<int N> u8 read_abs();
};

// handlers

void scrablexa_state::update_display()
{
	u16 data = bitswap<14>(m_e << 12 | m_c,10,8,6,12,11,7,9,13,5,4,3,2,1,0);
	m_display->matrix(m_d, ~data);
}

void scrablexa_state::write_c(u16 data)
{
	// CO0-CO11: digit segments
	m_c = data;
	update_display();
}

void scrablexa_state::write_d(u8 data)
{
	// DO0-DO4: input mux
	// DO0-DO7: digit select
	m_inp_mux = data & 0x1f;
	m_d = data;
	update_display();
}

void scrablexa_state::write_e(u8 data)
{
	// EO0,EO1: digit segments
	m_e = data;
	update_display();

	// EO2,EO3: speaker out
	m_speaker->level_w(data >> 2 & 3);
}

template<int N>
u8 scrablexa_state::read_abs()
{
	// AI/BI/SNS: multiplexed inputs
	return read_inputs(5) >> (N * 4);
}

// inputs

static INPUT_PORTS_START( scrablexa )
	PORT_START("IN.0") // DO0
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')

	PORT_START("IN.1") // DO1
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')

	PORT_START("IN.2") // DO2
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR('*')

	PORT_START("IN.3") // DO4
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_NAME("Double")
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_NAME("Triple")
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("Word")
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("Bonus")
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_NAME("Minus")
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_NAME("Clear")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("Enter")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // DO5
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1) PORT_NAME("Flash")
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_NAME("Solo")
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3) PORT_NAME("Score")
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_NAME("Timer")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_NAME("Review")
INPUT_PORTS_END

// config

void scrablexa_state::scrablexa(machine_config &config)
{
	// basic machine hardware
	MN1405(config, m_maincpu, 310000); // approximation - RC osc. R=15K, C=100pF
	m_maincpu->write_c().set(FUNC(scrablexa_state::write_c));
	m_maincpu->write_d().set(FUNC(scrablexa_state::write_d));
	m_maincpu->write_e().set(FUNC(scrablexa_state::write_e));
	m_maincpu->read_a().set(FUNC(scrablexa_state::read_abs<0>));
	m_maincpu->read_b().set(FUNC(scrablexa_state::read_abs<1>));
	m_maincpu->read_sns().set(FUNC(scrablexa_state::read_abs<2>));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 14);
	m_display->set_segmask(0xff, 0x3fff);
	config.set_default_layout(layout_scrablexa);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[4] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.125);
}

// roms

ROM_START( scrablexa )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mn1405ms", 0x0000, 0x0800, CRC(d6d61d03) SHA1(b319cb0f0539e7516bb28f1182665b05b7dd16b1) )

	ROM_REGION( 428, "maincpu:opla", 0 )
	ROM_LOAD( "mn1400_scrablexa_output.pla", 0, 428, CRC(555fe168) SHA1(cf19be34391dae0a8aacc7be53f2a3415fed3108) )
ROM_END





/*******************************************************************************

  Tomy Basketball (model 7600)
  * PCB label: TOMY, BASKET, 2E018E01
  * MN9008 (28-pin MN1400, AI0-AI3 replaced with CO0-CO3, die label: 1400 BM-0)
  * 2 7seg LEDs, 29 other LEDs, 1-bit sound

  Two versions are known: one with a black bezel and one with a brown bezel,
  the internal hardware is the same. The other 2 games in this series (Soccer,
  Volleyball) use a TMS1000 MCU instead.

*******************************************************************************/

class tmbaskb_state : public hh_mn1400_state
{
public:
	tmbaskb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_mn1400_state(mconfig, type, tag)
	{ }

	void tmbaskb(machine_config &config);

private:
	void update_display();
	void write_c(u16 data);
	void write_d(u8 data);
	void write_e(u8 data);
	u8 read_b();
};

// handlers

void tmbaskb_state::update_display()
{
	u8 data = bitswap<8>(m_e << 4 | m_d,7,5,1,4,3,2,6,0);
	m_display->matrix((m_c >> 1 & 0x30) | (m_c & 0xf), data);
}

void tmbaskb_state::write_c(u16 data)
{
	// CO0,CO1: digit select
	// CO2,CO3,CO5,CO6: led select
	m_c = data;
	update_display();

	// CO3,CO5: input mux
	m_inp_mux = bitswap<2>(data,5,3);

	// CO7: speaker out
	m_speaker->level_w(BIT(data, 7));
}

void tmbaskb_state::write_d(u8 data)
{
	// DO0-DO3: led data
	m_d = data;
	update_display();
}

void tmbaskb_state::write_e(u8 data)
{
	// EO0-EO3: led data
	m_e = data;
	update_display();
}

u8 tmbaskb_state::read_b()
{
	// BI1-BI3: multiplexed inputs, BI0: score button
	return (read_inputs(2) & 0xe) | (m_inputs[2]->read() & 1);
}

// inputs

static INPUT_PORTS_START( tmbaskb )
	PORT_START("IN.0") // CO3 BI (left)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Offense P")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P1 Offense S")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Defense")

	PORT_START("IN.1") // CO5 BI (right)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_COCKTAIL PORT_NAME("P2 Offense P")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_COCKTAIL PORT_NAME("P2 Offense S")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Defense")

	PORT_START("IN.2") // BI0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_NAME("Score")

	PORT_START("IN.3") // SNS
	PORT_CONFNAME( 0x01, 0x01, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x00, "2" )
	PORT_CONFNAME( 0x02, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" ) // PRO1
	PORT_CONFSETTING(    0x02, "2" ) // PRO2
INPUT_PORTS_END

// config

void tmbaskb_state::tmbaskb(machine_config &config)
{
	// basic machine hardware
	MN1400_28PINS(config, m_maincpu, 290000); // approximation - RC osc. R=18K, C=100pF
	m_maincpu->write_c().set(FUNC(tmbaskb_state::write_c));
	m_maincpu->set_c_mask(0x3ef);
	m_maincpu->write_d().set(FUNC(tmbaskb_state::write_d));
	m_maincpu->write_e().set(FUNC(tmbaskb_state::write_e));
	m_maincpu->read_b().set(FUNC(tmbaskb_state::read_b));
	m_maincpu->read_sns().set_ioport("IN.3");

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(6, 8);
	m_display->set_segmask(3, 0x7f);
	config.set_default_layout(layout_tmbaskb);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( tmbaskb )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tomy_basket_mn9008", 0x0000, 0x0400, CRC(25be3560) SHA1(17855397cf05963c1381191cd4731860b8e180a8) )

	ROM_REGION( 428, "maincpu:opla", 0 ) // 4-bit
	ROM_LOAD( "mn1400_common1_output.pla", 0, 428, CRC(07489d8b) SHA1(4fe65af8ee798490ed0bbe6a77d61713a2fb28b4) )
ROM_END



} // anonymous namespace

/*******************************************************************************

  Game driver(s)

*******************************************************************************/

//    YEAR  NAME       PARENT    COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY, FULLNAME, FLAGS
SYST( 1979, compperf,  0,        0,      compperf,  compperf,  compperf_state,  empty_init, "Lakeside", "Computer Perfection", MACHINE_SUPPORTS_SAVE )

SYST( 1980, scrablexa, scrablex, 0,      scrablexa, scrablexa, scrablexa_state, empty_init, "Selchow & Righter", "Scrabble Lexor: Computer Word Game (MN1405 version)", MACHINE_SUPPORTS_SAVE )

SYST( 1980, tmbaskb,   0,        0,      tmbaskb,   tmbaskb,   tmbaskb_state,   empty_init, "Tomy", "Basketball (Tomy)", MACHINE_SUPPORTS_SAVE )



hh_pic16.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle, Kevin Horton
/*******************************************************************************

GI PIC 16xx-driven dedicated handhelds or other simple devices.

known chips:

  serial  device  etc.
-----------------------------------------------------------
 *020     1650    19??, GI Economega IV TV PPL Tuning System Control
 *021     1650    1978, GI AY-3-8910 demo board
 @024     1655    1979, Toytronic? Football
 @033     1655A   1979, Toytronic Football (newer)
 *034     1655A   1979, Cardinal Electronic Football
 @036     1655A   1979, Ideal Maniac
 @043     1655A   1979, Caprice Pro-Action Baseball
 @049     1655A   1980, Kingsford Match Me(?)/Mini Match Me
 @051     1655A   1979, Kmart Dr. Dunk/Tandy Electronic Basketball
 @053     1655A   1979, Atari Touch Me
 @0??     1655A   1979, Tiger Half Court Computer Basketball/Sears Electronic Basketball (custom label)
 @061     1655A   1980, Lakeside Le Boom
 @078     1655A   1980, Ideal Flash
 *081     1655A   1981, Ramtex Space Invaders/Block Buster
 *085     1655A   1980, VTech Soccer 2/Grandstand Match of the Day Soccer
 @094     1655A   1980, GAF Melody Madness
 @110     1650A   1979, Tiger/Tandy Rocket Pinball
 *123     1655A?  1980, Kingsford Match Me/Mini Match Me
 @133     1650A   1981, U.S. Games Programmable Baseball/Tandy 2-Player Baseball
 @144     1650A   1981, U.S. Games/Tandy 2-Player Football
 *192     1650    19??, <unknown> phone dialer (have dump)
 *255     1655    19??, <unknown> talking clock (have dump)
 *518     1650A   19??, GI Teleview Control Chip (features differ per program)
 *519     1650A   19??, GI Teleview Control Chip
 @522     1655A   1981, Electroplay Sound FX Phasor
 *532     1650A   19??, GI Teleview Control Chip
 *533     1650A   19??, GI Teleview Control Chip
 *536     1650    1982, GI Teleview Autodialer/Terminal Identifier

  (* means undumped unless noted, @ denotes it's in this driver)

ROM source notes when dumped from another title, but confident it's the same:
- drdunk: Tandy Electronic Basketball
- flash: Radio Shack Sound Effects Chassis
- hccbaskb: Sears Electronic Basketball
- ttfballa: (no brand) Football
- us2pfball: Tandy 2-Player Football
- uspbball: Tandy 2-Player Baseball

TODO:
- tweak MCU frequency for games when video/audio recording surfaces(YouTube etc.)
- what's the relation between drdunk and hccbaskb? Probably made by the same
  Hong Kong subcontractor? I presume Toytronic.
- uspbball and pabball internal artwork

*******************************************************************************/

#include "emu.h"

#include "cpu/pic16c5x/pic16c5x.h"
#include "machine/clock.h"
#include "machine/input_merger.h"
#include "machine/netlist.h"
#include "machine/timer.h"
#include "sound/dac.h"
#include "sound/flt_vol.h"
#include "sound/spkrdev.h"
#include "video/pwm.h"

#include "speaker.h"

// netlist
#include "nl_sfxphasor.h"

// internal artwork
#include "drdunk.lh"
#include "flash.lh"
#include "hccbaskb.lh"
#include "leboom.lh"
#include "maniac.lh"
#include "melodym.lh"
#include "matchme.lh"
#include "rockpin.lh"
#include "touchme.lh"
#include "ttfball.lh"
#include "us2pfball.lh"

#include "hh_pic16_test.lh" // common test-layout - use external artwork


namespace {

class hh_pic16_state : public driver_device
{
public:
	hh_pic16_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_speaker(*this, "speaker"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(reset_button);
	DECLARE_INPUT_CHANGED_MEMBER(power_button);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// devices
	required_device<pic16c5x_device> m_maincpu;
	optional_device<pwm_display_device> m_display;
	optional_device<speaker_sound_device> m_speaker;
	optional_ioport_array<6> m_inputs; // max 6

	u16 m_inp_mux = ~0; // multiplexed inputs mask

	// MCU output pin state
	u8 m_a = 0;         // port A
	u8 m_b = 0;         // port B
	u8 m_c = 0;         // port C
	u8 m_d = 0;         // port D

	u16 read_inputs(int columns, u16 colmask = ~0);
	u8 read_rotated_inputs(int columns, u8 rowmask = ~0);
	void set_power(bool state);
};


// machine start/reset

void hh_pic16_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_a));
	save_item(NAME(m_b));
	save_item(NAME(m_c));
	save_item(NAME(m_d));
}

void hh_pic16_state::machine_reset()
{
	set_power(true);
}



/*******************************************************************************

  Helper Functions

*******************************************************************************/

// generic input handlers

u16 hh_pic16_state::read_inputs(int columns, u16 colmask)
{
	// active low
	u16 ret = ~0 & colmask;

	// read selected input rows
	for (int i = 0; i < columns; i++)
		if (!BIT(m_inp_mux, i))
			ret &= m_inputs[i]->read();

	return ret;
}

u8 hh_pic16_state::read_rotated_inputs(int columns, u8 rowmask)
{
	u8 ret = 0;
	u16 colmask = (1 << columns) - 1;

	// read selected input columns
	for (int i = 0; i < 8; i++)
		if (1 << i & rowmask && ~m_inputs[i]->read() & ~m_inp_mux & colmask)
			ret |= 1 << i;

	// active low
	return ~ret & rowmask;
}

INPUT_CHANGED_MEMBER(hh_pic16_state::reset_button)
{
	// when an input is directly wired to MCU MCLR pin
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}

INPUT_CHANGED_MEMBER(hh_pic16_state::power_button)
{
	if (newval != field.defvalue())
		set_power((bool)param);
}

void hh_pic16_state::set_power(bool state)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? CLEAR_LINE : ASSERT_LINE);

	if (m_display && !state)
		m_display->clear();
}



/*******************************************************************************

  Minidrivers (subclass, I/O, Inputs, Machine Config, ROM Defs)

*******************************************************************************/

/*******************************************************************************

  Atari Touch Me
  * PIC 1655A-053
  * 2 7seg LEDs + 4 other LEDs, 1-bit sound

  This is the handheld version of the 1974 arcade game.

  known revisions:
  - Model BH-100 GI C013233 Rev 2 Atari W 1979: PIC 1655A-053
  - Model BH-100 C013150 Rev 6 Atari 1979: AMI C10745 (custom ASIC)

*******************************************************************************/

class touchme_state : public hh_pic16_state
{
public:
	touchme_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pic16_state(mconfig, type, tag)
	{ }

	void touchme(machine_config &config);

private:
	void update_display();
	void update_speaker();
	u8 read_a();
	void write_b(u8 data);
	void write_c(u8 data);
};

// handlers

void touchme_state::update_display()
{
	m_display->matrix(~m_b & 0x7b, m_c);
}

void touchme_state::update_speaker()
{
	m_speaker->level_w((m_b >> 7 & 1) | (m_c >> 6 & 2));
}

u8 touchme_state::read_a()
{
	// A: multiplexed inputs
	return read_inputs(3, 0xf);
}

void touchme_state::write_b(u8 data)
{
	// B0-B2: input mux
	m_inp_mux = data & 7;

	// B0,B1: digit select
	// B3-B6: leds
	m_b = data;
	update_display();

	// B7: speaker lead 1
	update_speaker();
}

void touchme_state::write_c(u8 data)
{
	// C0-C6: digit segments
	m_c = data;
	update_display();

	// C7: speaker lead 2
	update_speaker();
}

// inputs

static INPUT_PORTS_START( touchme )
	PORT_START("IN.0") // B0 port A
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_START )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("Last")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("High")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON7 ) PORT_NAME("Skill")

	PORT_START("IN.1") // B1 port A
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Blue Button")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Yellow Button")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Red Button")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Green Button")

	PORT_START("IN.2") // B2 port A
	PORT_CONFNAME( 0x07, 0x01^0x07, "Game Select" )
	PORT_CONFSETTING(    0x01^0x07, "1" )
	PORT_CONFSETTING(    0x02^0x07, "2" )
	PORT_CONFSETTING(    0x04^0x07, "3" )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

// config

void touchme_state::touchme(machine_config &config)
{
	// basic machine hardware
	PIC1655(config, m_maincpu, 250000); // approximation - RC osc. R=100K, C=47pF
	m_maincpu->read_a().set(FUNC(touchme_state::read_a));
	m_maincpu->write_b().set(FUNC(touchme_state::write_b));
	m_maincpu->read_c().set_constant(0xff);
	m_maincpu->write_c().set(FUNC(touchme_state::write_c));

	// PIC CLKOUT, tied to RTCC
	CLOCK(config, "clock", 250000/4).signal_handler().set_inputline("maincpu", PIC16C5x_RTCC);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(7, 7);
	m_display->set_segmask(3, 0x7f);
	config.set_default_layout(layout_touchme);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( touchme )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "pic_1655a-053", 0x0000, 0x0400, CRC(f0858f0a) SHA1(53ffe111d43db1c110847590350ef62f02ed5e0e) )
ROM_END





/*******************************************************************************

  Caprice Pro-Action Baseball (manufactured by Calfax)
  * PIC 1655A-043
  * 1 7seg LED + 36 other LEDs, CD4028, 1-bit sound

  The box says (C) Calfax, Inc. 1979. Manufactured in Hong Kong for Caprice
  Electronics, exclusively for Kmart Corporation. Calfax / Caprice is basically
  the same company.

*******************************************************************************/

class pabball_state : public hh_pic16_state
{
public:
	pabball_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pic16_state(mconfig, type, tag)
	{ }

	void pabball(machine_config &config);

private:
	void update_display();
	void write_b(u8 data);
	void write_c(u8 data);
};

// handlers

void pabball_state::update_display()
{
	// CD4028 BCD to decimal decoder
	// CD4028 0-8: led select, 9: 7seg
	u16 sel = m_c & 0xf;
	if (sel & 8) sel &= 9;
	sel = 1 << sel;

	m_display->matrix(sel, m_b);
}

void pabball_state::write_b(u8 data)
{
	// B: led data
	m_b = ~data;
	update_display();
}

void pabball_state::write_c(u8 data)
{
	// C2: RTCC pin
	m_maincpu->set_input_line(PIC16C5x_RTCC, data >> 2 & 1);

	// C7: speaker out
	m_speaker->level_w(data >> 7 & 1);

	// C0-C3: CD4028 A-D
	m_c = data;
	update_display();
}

// inputs

static INPUT_PORTS_START( pabball )
	PORT_START("IN.0") // port A
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Curve Left")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_COCKTAIL PORT_NAME("P2 Curve Right")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_COCKTAIL PORT_NAME("P2 Straight")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN.1") // port C
	PORT_BIT( 0xcf, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("P1 Hit")
	PORT_CONFNAME( 0x20, 0x00, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x20, "2" )

	PORT_START("RESET")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pabball_state::reset_button), 0) PORT_NAME("P1 Reset")
INPUT_PORTS_END

// config

void pabball_state::pabball(machine_config &config)
{
	// basic machine hardware
	PIC1655(config, m_maincpu, 1200000); // approximation - RC osc. R=18K, C=27pF
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->write_b().set(FUNC(pabball_state::write_b));
	m_maincpu->read_c().set_ioport("IN.1");
	m_maincpu->write_c().set(FUNC(pabball_state::write_c));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 8);
	m_display->set_segmask(0x200, 0xff);
	config.set_default_layout(layout_hh_pic16_test);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( pabball )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "pic_1655a-043", 0x0000, 0x0400, CRC(43c9b765) SHA1(888a431bab9bcb241c14f33f70863fa2ad89c96b) )
ROM_END





/*******************************************************************************

  Electroplay Sound FX Phasor
  * PIC 1655A-522
  * 3-bit sound with volume envelope

  It's a toy synthesizer. It included keypad overlays with nursery rhymes.

  When in music mode, the user can create custom sounds with the F key, other
  keys are music notes. To put it briefly, commands A,B are for vibrato,
  C is for volume decay, and D,E,F change the timbre.

  Paste example (must be in music mode): F11A F3B F4C F3D F3E F6F

*******************************************************************************/

class sfxphasor_state : public hh_pic16_state
{
public:
	sfxphasor_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pic16_state(mconfig, type, tag),
		m_sound_nl(*this, "sound_nl:p%02u", 10U)
	{ }

	void sfxphasor(machine_config &config);

private:
	optional_device_array<netlist_mame_logic_input_device, 8> m_sound_nl;

	void write_b(u8 data);
	void write_c(u8 data);
	u8 read_c();
};

// handlers

void sfxphasor_state::write_b(u8 data)
{
	// B2: trigger power off
	if (~m_b & data & 4)
		set_power(false);

	// B0,B3: envelope param
	m_sound_nl[0]->write_line(BIT(data, 0));
	m_sound_nl[3]->write_line(BIT(data, 3));

	// B5-B7: sound out
	for (int i = 5; i < 8; i++)
		m_sound_nl[i]->write_line(BIT(data, i));

	m_b = data;
}

void sfxphasor_state::write_c(u8 data)
{
	m_c = data;
}

u8 sfxphasor_state::read_c()
{
	// C0-C3: multiplexed inputs from C4-C7
	m_inp_mux = m_c >> 4 & 0xf;
	u8 lo = read_inputs(4, 0xf);

	// C4-C7: multiplexed inputs from C0-C3
	m_inp_mux = m_c & 0xf;
	u8 hi = read_rotated_inputs(4, 0xf);

	return lo | hi << 4;
}

// inputs

static INPUT_PORTS_START( sfxphasor )
	PORT_START("IN.0") // C4 port C
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4') PORT_NAME("4 / Locomotive")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0') PORT_NAME("0 / Helicopter")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')

	PORT_START("IN.1") // C5 port C
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5') PORT_NAME("5 / Bee")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1') PORT_NAME("1 / Telephone")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')

	PORT_START("IN.3") // C7 port C
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6') PORT_NAME("6 / Boat")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2') PORT_NAME("2 / Race Car")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')

	PORT_START("IN.2") // C6 port C
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7') PORT_NAME("7 / Police Car")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3') PORT_NAME("3 / UFO")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR('F') PORT_CHAR(13) PORT_NAME("F / Enter")

	PORT_START("IN.4") // port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x02, 0x02, "Auto Power Off" ) // MCU pin, not a switch
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x02, DEF_STR( On ) )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1) PORT_NAME("On / Sounds") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sfxphasor_state::power_button), true)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_NAME("On / Music") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sfxphasor_state::power_button), true)
INPUT_PORTS_END

// config

void sfxphasor_state::sfxphasor(machine_config &config)
{
	// basic machine hardware
	PIC1655(config, m_maincpu, 950000); // approximation - RC osc. R=10K+VR, C=47pF
	m_maincpu->read_a().set_ioport("IN.4");
	m_maincpu->write_b().set(FUNC(sfxphasor_state::write_b));
	m_maincpu->write_c().set(FUNC(sfxphasor_state::write_c));
	m_maincpu->read_c().set(FUNC(sfxphasor_state::read_c));

	// no visual feedback!

	// sound hardware
	SPEAKER(config, "mono").front_center();
	NETLIST_SOUND(config, "sound_nl", 48000).set_source(NETLIST_NAME(sfxphasor)).add_route(ALL_OUTPUTS, "mono", 0.25);
	NETLIST_STREAM_OUTPUT(config, "sound_nl:cout0", 0, "SPK1.1").set_mult_offset(1.0, 0.0);

	NETLIST_LOGIC_INPUT(config, "sound_nl:p10", "P10.IN", 0);
	NETLIST_LOGIC_INPUT(config, "sound_nl:p13", "P13.IN", 0);
	NETLIST_LOGIC_INPUT(config, "sound_nl:p15", "P15.IN", 0);
	NETLIST_LOGIC_INPUT(config, "sound_nl:p16", "P16.IN", 0);
	NETLIST_LOGIC_INPUT(config, "sound_nl:p17", "P17.IN", 0);
}

// roms

ROM_START( sfxphasor )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "pic_1655a-522", 0x0000, 0x0400, CRC(0af77e83) SHA1(49b089681149254041c14a740cad19a619725c3c) )
ROM_END





/*******************************************************************************

  GAF Melody Madness
  * PIC 1655A-094
  * 2 lamps under tube, 1-bit sound

  Melody Madness is a tabletop music memory game, shaped like a jukebox.
  It can also be played as a simple electronic piano.

*******************************************************************************/

class melodym_state : public hh_pic16_state
{
public:
	melodym_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pic16_state(mconfig, type, tag)
	{ }

	void melodym(machine_config &config);

private:
	void write_b(u8 data);
	u8 read_c();
	void write_c(u8 data);
};

// handlers

void melodym_state::write_b(u8 data)
{
	// B2-B6: input mux
	m_inp_mux = data >> 2 & 0x1f;
}

u8 melodym_state::read_c()
{
	// C0-C4: multiplexed inputs
	return read_inputs(5, 0x1f) | 0xe0;
}

void melodym_state::write_c(u8 data)
{
	// C6: both lamps
	m_display->matrix(1, ~data >> 6 & 1);

	// C7: speaker out
	m_speaker->level_w(~data >> 7 & 1);
}

// inputs

static INPUT_PORTS_START( melodym )
	PORT_START("IN.0") // B2 port C
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("Button 1")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("Button 2")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("Button 3")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_NAME("Button 4")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_NAME("Button 5")

	PORT_START("IN.1") // B3 port C
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Button 6")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("Button 7")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("Button 8")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("Button 9")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Button 10")

	PORT_START("IN.2") // B4 port C
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Button 11")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Button 12")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED ) // there is no button 13
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("Button 14")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_G) PORT_NAME("Button 15")

	PORT_START("IN.3") // B5 port C
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_Z) PORT_NAME("Button 16")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME("Button 17")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("Button 18")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_V) PORT_NAME("Button 19")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_NAME("Button 20")

	PORT_START("IN.4") // B6 port C
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_N) PORT_NAME("Button 21")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_M) PORT_NAME("Button 22")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_COMMA) PORT_NAME("Button 23")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_NAME("Button 24")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("Button 25")

	PORT_START("IN.5") // port A
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_NAME("Novice")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_NAME("Whiz")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_NAME("Pro")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_NAME("Note")
INPUT_PORTS_END

// config

void melodym_state::melodym(machine_config &config)
{
	// basic machine hardware
	PIC1655(config, m_maincpu, 1000000); // approximation
	m_maincpu->read_a().set_ioport("IN.5");
	m_maincpu->write_b().set(FUNC(melodym_state::write_b));
	m_maincpu->read_c().set(FUNC(melodym_state::read_c));
	m_maincpu->write_c().set(FUNC(melodym_state::write_c));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 1);
	m_display->set_bri_levels(0.9);
	config.set_default_layout(layout_melodym);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( melodym )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "pic_1655a-094", 0x0000, 0x0400, CRC(6d35bd7b) SHA1(20e326085878f69a9d4ef1651ef4443f27188567) )
ROM_END





/*******************************************************************************

  Ideal Maniac, by Ralph Baer
  * PIC 1655A-036
  * 2 7seg LEDs, 1-bit sound

  Maniac is a reflex game for 2-4 players. There are 4 challenges:
  1: Musical Maniac: Press the button as soon as the music stops.
  2: Sounds Abound: Count the number of tones in the song, then press the button
     after the same amount of beeps.
  3: Look Twice: Press the button after the game repeats the first pattern.
  4: Your Time Is Up: Press the button after estimating the duration of the tone.

*******************************************************************************/

class maniac_state : public hh_pic16_state
{
public:
	maniac_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pic16_state(mconfig, type, tag)
	{ }

	void maniac(machine_config &config);

private:
	void update_display();
	void update_speaker();
	void write_b(u8 data);
	void write_c(u8 data);
};

// handlers

void maniac_state::update_display()
{
	m_display->write_row(0, ~m_b & 0x7f);
	m_display->write_row(1, ~m_c & 0x7f);
}

void maniac_state::update_speaker()
{
	m_speaker->level_w((m_b >> 7 & 1) | (m_c >> 6 & 2));
}

void maniac_state::write_b(u8 data)
{
	// B0-B6: left 7seg
	m_b = data;
	update_display();

	// B7: speaker lead 1
	update_speaker();
}

void maniac_state::write_c(u8 data)
{
	// C0-C6: right 7seg
	m_c = data;
	update_display();

	// C7: speaker lead 2
	update_speaker();
}

// inputs

static INPUT_PORTS_START( maniac )
	PORT_START("IN.0") // port A
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) // top button, increment clockwise
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(3)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(4)
INPUT_PORTS_END

// config

void maniac_state::maniac(machine_config &config)
{
	// basic machine hardware
	PIC1655(config, m_maincpu, 1000000); // approximation - RC osc. R=~13.4K, C=470pF
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->write_b().set(FUNC(maniac_state::write_b));
	m_maincpu->write_c().set(FUNC(maniac_state::write_c));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(2, 7);
	m_display->set_segmask(3, 0x7f);
	config.set_default_layout(layout_maniac);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( maniac )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "pic_1655a-036", 0x0000, 0x0400, CRC(a96f7011) SHA1(e97ae44d3c1e74c7e1024bb0bdab03eecdc9f827) )
ROM_END





/*******************************************************************************

  Ideal Flash
  * PCB label: 25-600321, REV C, TCI-A3H / 94HB
  * PIC 1655A-078
  * 2 7seg LEDs + 8 other LEDs, 1-bit sound with volume decay

  Flash is a wall-mounted game, players throw beanbags to activate the buttons.
  It's described in patent US4333657 as an electronic dart game.

  BTANB: In games 4 and 5 it's easy to lock up the program by pressing the
  buttons repeatedly and causing a score overflow. Although that wouldn't be
  possible by properly throwing beanbags at it. This bug is warned about in
  the manual.

  This could also be purchased as a bare PCB from Radio Shack under the Archer
  brand, catalog number 277-1013. It was named "Sound Effects Chassis" but
  clearly it's nothing like that. The instruction leaflet that came with the
  PCB says to attach a speaker and a 9V power source. It actually takes 5V,
  9V would break it. The only thing it has to say about the game itself is
  "Your module will produce blinking lights and several different sounds."

*******************************************************************************/

class flash_state : public hh_pic16_state
{
public:
	flash_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pic16_state(mconfig, type, tag),
		m_volume(*this, "volume")
	{ }

	void flash(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<filter_volume_device> m_volume;

	double m_speaker_volume = 0.0;

	void update_display();
	void write_b(u8 data);
	u8 read_c();
	void write_c(u8 data);

	void speaker_update();
	TIMER_DEVICE_CALLBACK_MEMBER(speaker_decay_sim);
};

void flash_state::machine_start()
{
	hh_pic16_state::machine_start();
	save_item(NAME(m_speaker_volume));
}

// handlers

void flash_state::speaker_update()
{
	if (~m_b & 0x40)
		m_speaker_volume = 50.0;

	// it takes a bit before it actually starts fading
	m_volume->set_gain(std::min(m_speaker_volume, 1.0));
}

TIMER_DEVICE_CALLBACK_MEMBER(flash_state::speaker_decay_sim)
{
	// volume decays when speaker is off (divisor and timer period determine duration)
	speaker_update();
	m_speaker_volume /= 1.0075;
}

void flash_state::update_display()
{
	m_display->matrix(~m_b >> 4 & 3, (~m_c >> 1 & 0x7f) | (~m_b << 7 & 0x780));
}

void flash_state::write_b(u8 data)
{
	// B0-B3: led data
	// B4,B5: led select
	m_b = data;
	update_display();

	// B7: speaker out
	m_speaker->level_w(BIT(data, 7));

	// B6: speaker on
	speaker_update();
}

u8 flash_state::read_c()
{
	// C1-C7: buttons
	return (m_c & 1) ? 0xff : m_inputs[1]->read();
}

void flash_state::write_c(u8 data)
{
	// C0: enable buttons
	// C1-C7: digit segments
	m_c = data;
	update_display();
}

// inputs

static INPUT_PORTS_START( flash )
	PORT_START("IN.0") // port A
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) // top button, increment clockwise
	PORT_BIT( 0x0e, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN.1") // port C
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON4 )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON5 )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON6 )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON7 )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON8 )
INPUT_PORTS_END

// config

void flash_state::flash(machine_config &config)
{
	// basic machine hardware
	PIC1655(config, m_maincpu, 1050000); // approximation
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->write_b().set(FUNC(flash_state::write_b));
	m_maincpu->read_c().set(FUNC(flash_state::read_c));
	m_maincpu->write_c().set(FUNC(flash_state::write_c));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(2, 7+4);
	m_display->set_segmask(3, 0x7f);
	config.set_default_layout(layout_flash);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "volume", 0.25);
	FILTER_VOLUME(config, m_volume).add_route(ALL_OUTPUTS, "mono", 1.0);

	TIMER(config, "speaker_decay").configure_periodic(FUNC(flash_state::speaker_decay_sim), attotime::from_msec(1));
}

// roms

ROM_START( flash )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "pic_1655a-078", 0x0000, 0x0400, CRC(bf780733) SHA1(57ac4620d87492280ab8cf69c148f98e38ecedc4) )
ROM_END





/*******************************************************************************

  Kingsford Match Me
  * PIC 1655A-049
  * 8 lamps, 1-bit sound

  Known releases:
  - USA(1): Match Me/Mini Match Me, published by Kingsford
  - USA(2): Me Too, published by Talbot
  - Hong Kong: Gotcha!/Encore/Follow Me, published by Toytronic

  Match Me is the tabletop version, Mini Match Me is the handheld.
  The original is probably by Toytronic, Kingsford's version being licensed from them.

  Known revisions:
  - PIC 1655A-049 (this one, dumped from a Mini Match Me)
  - PIC 1655A-123 (seen in Match Me and Mini Match Me)

*******************************************************************************/

class matchme_state : public hh_pic16_state
{
public:
	matchme_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pic16_state(mconfig, type, tag)
	{ }

	void matchme(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(speed_switch);

private:
	void write_b(u8 data);
	void write_c(u8 data);
	u8 read_c();
};

// handlers

INPUT_CHANGED_MEMBER(matchme_state::speed_switch)
{
	// MCU clock is ~1.2MHz by default (R=18K, C=15pF), high speed setting adds a
	// 10pF cap to speed it up by about 7.5%.
	m_maincpu->set_unscaled_clock((newval & 1) ? 1300000 : 1200000);
}

void matchme_state::write_b(u8 data)
{
	// B0-B7: lamps
	m_display->matrix(1, data);
}

u8 matchme_state::read_c()
{
	// C0-C3: multiplexed inputs from C4-C6
	m_inp_mux = m_c >> 4 & 7;
	u8 lo = read_inputs(3, 0xf);

	// C4-C6: multiplexed inputs from C0-C3
	m_inp_mux = m_c & 0xf;
	u8 hi = read_rotated_inputs(4, 7);

	return lo | hi << 4 | 0x80;
}

void matchme_state::write_c(u8 data)
{
	// C0-C6: input mux
	m_c = data;

	// C7: speaker out + RTCC pin
	m_speaker->level_w(data >> 7 & 1);
	m_maincpu->set_input_line(PIC16C5x_RTCC, data >> 7 & 1);
}

// inputs

static INPUT_PORTS_START( matchme )
	PORT_START("IN.0") // C4 port C
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) // purple
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) // pink
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) // yellow
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON4 ) // blue

	PORT_START("IN.1") // C5 port C
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON5 ) // red
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON6 ) // cyan
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON7 ) // orange
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON8 ) // green

	PORT_START("IN.2") // C6 port C
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_START )
	PORT_BIT( 0x02, 0x02, IPT_CUSTOM ) PORT_CONDITION("FAKE", 0x03, EQUALS, 0x03) // Last/Auto
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON10 ) PORT_NAME("Long")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN.3") // port A
	PORT_CONFNAME( 0x07, 0x00^0x07, "Game" )
	PORT_CONFSETTING(    0x00^0x07, "1" )
	PORT_CONFSETTING(    0x01^0x07, "2" )
	PORT_CONFSETTING(    0x02^0x07, "3" )
	PORT_CONFSETTING(    0x04^0x07, "4" )
	PORT_CONFNAME( 0x08, 0x08, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x08, "Amateur" ) // AM
	PORT_CONFSETTING(    0x00, "Professional" ) // PRO

	PORT_START("CPU") // another fake
	PORT_CONFNAME( 0x01, 0x00, "Speed" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(matchme_state::speed_switch), 0)
	PORT_CONFSETTING(    0x00, DEF_STR( Low ) )
	PORT_CONFSETTING(    0x01, DEF_STR( High ) )

	PORT_START("FAKE") // Last/Auto are electronically the same button
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON9 ) PORT_NAME("Last")
	PORT_CONFNAME( 0x02, 0x02, "Music" )
	PORT_CONFSETTING(    0x02, "Manual" )
	PORT_CONFSETTING(    0x00, "Auto" )
INPUT_PORTS_END

// config

void matchme_state::matchme(machine_config &config)
{
	// basic machine hardware
	PIC1655(config, m_maincpu, 1200000); // see speed_switch
	m_maincpu->read_a().set_ioport("IN.3");
	m_maincpu->write_b().set(FUNC(matchme_state::write_b));
	m_maincpu->read_c().set(FUNC(matchme_state::read_c));
	m_maincpu->write_c().set(FUNC(matchme_state::write_c));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 8);
	config.set_default_layout(layout_matchme);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( matchme )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "pic_1655a-049", 0x0000, 0x0400, CRC(fa3f4805) SHA1(57cbac18baa201927e99cd69cc2ffda4d2e642bb) )
ROM_END





/*******************************************************************************

  Kmart Dr. Dunk (manufactured in Hong Kong)
  * PIC 1655A-51
  * 2 7seg LEDs + 21 other LEDs, 1-bit sound

  It is a clone of Mattel Basketball, but at lower speed.
  The ROM is nearly identical to hccbaskb, the housing/overlay is similar to
  U.S. Games/Tandy Trick Shot Basketball.

  known releases:
  - USA(1): Dr. Dunk, published by Kmart
  - USA(2): Electronic Basketball (model 60-2146), published by Tandy

*******************************************************************************/

class drdunk_state : public hh_pic16_state
{
public:
	drdunk_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pic16_state(mconfig, type, tag)
	{ }

	void drdunk(machine_config &config);

private:
	void update_display();
	u8 read_a();
	void write_b(u8 data);
	void write_c(u8 data);
};

// handlers

void drdunk_state::update_display()
{
	m_display->matrix(m_b, m_c);
}

u8 drdunk_state::read_a()
{
	// A2: skill switch, A3: multiplexed inputs
	return m_inputs[5]->read() | read_inputs(5, 8) | 3;
}

void drdunk_state::write_b(u8 data)
{
	// B0: RTCC pin
	m_maincpu->set_input_line(PIC16C5x_RTCC, data & 1);

	// B0-B4: input mux
	m_inp_mux = ~data & 0x1f;

	// B0-B3: led select
	// B4,B5: digit select
	m_b = data;
	update_display();
}

void drdunk_state::write_c(u8 data)
{
	// C7: speaker out
	m_speaker->level_w(data >> 7 & 1);

	// C0-C6: led data
	m_c = ~data;
	update_display();
}

// inputs

static INPUT_PORTS_START( drdunk )
	PORT_START("IN.0") // B0 port A3
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_16WAY

	PORT_START("IN.1") // B1 port A3
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_16WAY

	PORT_START("IN.2") // B2 port A3
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_16WAY

	PORT_START("IN.3") // B3 port A3
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_16WAY

	PORT_START("IN.4") // B4 port A3
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1 )

	PORT_START("IN.5") // port A2
	PORT_CONFNAME( 0x04, 0x04, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x04, "1" )
	PORT_CONFSETTING(    0x00, "2" )
INPUT_PORTS_END

// config

void drdunk_state::drdunk(machine_config &config)
{
	// basic machine hardware
	PIC1655(config, m_maincpu, 800000); // approximation - RC osc. R=18K, C=47pF
	m_maincpu->read_a().set(FUNC(drdunk_state::read_a));
	m_maincpu->write_b().set(FUNC(drdunk_state::write_b));
	m_maincpu->read_c().set_constant(0xff);
	m_maincpu->write_c().set(FUNC(drdunk_state::write_c));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(6, 7);
	m_display->set_segmask(0x30, 0x7f);
	m_display->set_bri_levels(0.01, 0.2); // player led is brighter
	config.set_default_layout(layout_drdunk);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( drdunk )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "pic_1655a-051", 0x0000, 0x0400, CRC(92534b40) SHA1(7055e32846c913e68f7d35f279cd537f6325f4f2) )
ROM_END





/*******************************************************************************

  Lakeside Le Boom
  * PIC 1655A-061
  * 1 led, 1-bit sound with RC circuit for volume decay

  This is a tabletop timebomb defusion game. It's shaped like an aerial bomb,
  colored black on USA version, yellow on dual-language Canadian version.
  The game starts 'ticking' when the player opens the keypad door. To begin,
  select the game mode, rows(keypad size), and fuse duration.

  Game modes as described on the box:
  1: Eliminate the buttons one by one in the order set out by the computer. Press
     one twice and you'll be sorry!
  2: For 2 or more players. Take turns pressing the buttons, remember which ones.
     Press a button a second time and watch out, it's all over.
  3: The computer picks one secret button that stops the fuse. You must press it
     on your 5th turn. Listen to the clues and you'll do fine.
  4: The computer picks a secret combination. Find it first by listening to the
     clues. Find the right order and you'll get it to fizzle out.

*******************************************************************************/

class leboom_state : public hh_pic16_state
{
public:
	leboom_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pic16_state(mconfig, type, tag),
		m_volume(*this, "volume")
	{ }

	void leboom(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<filter_volume_device> m_volume;

	double m_speaker_volume = 0.0;

	u8 read_a();
	void write_b(u8 data);
	void write_c(u8 data);

	void speaker_update();
	TIMER_DEVICE_CALLBACK_MEMBER(speaker_decay_sim);
};

void leboom_state::machine_start()
{
	hh_pic16_state::machine_start();
	save_item(NAME(m_speaker_volume));
}

// handlers

void leboom_state::speaker_update()
{
	if (~m_c & 0x80)
		m_speaker_volume = 1.0;

	m_volume->set_gain(m_speaker_volume);
}

TIMER_DEVICE_CALLBACK_MEMBER(leboom_state::speaker_decay_sim)
{
	// volume decays when speaker is off (divisor and timer period determine duration)
	speaker_update();
	m_speaker_volume /= 1.005;
}

u8 leboom_state::read_a()
{
	// A: multiplexed inputs
	return read_inputs(6, 0xf);
}

void leboom_state::write_b(u8 data)
{
	// B0-B5: input mux
	m_inp_mux = data & 0x3f;
}

void leboom_state::write_c(u8 data)
{
	// C4: single led
	m_display->matrix(1, BIT(data, 4));

	// C6: speaker out
	m_speaker->level_w(BIT(data, 6));

	// C7: speaker on
	m_c = data;
	speaker_update();
}

// inputs

static INPUT_PORTS_START( leboom )
	PORT_START("IN.0") // B0 port A
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("Red Button 1")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Red Button 2")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Red Button 3")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_Z) PORT_NAME("Red Button 4")

	PORT_START("IN.1") // B1 port A
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("Red-Red Button")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("Red-Green Button")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Red-Yellow Button")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME("Red-Blue Button")

	PORT_START("IN.2") // B2 port A
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("Shortest")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("Short")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("Long")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("Longest")

	PORT_START("IN.3") // B3 port A
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_NAME("Yellow Button 1")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("Yellow Button 2")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("Yellow Button 3")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_V) PORT_NAME("Yellow Button 4")

	PORT_START("IN.4") // B4 port A
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_NAME("Blue Button 1")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Blue Button 2")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_G) PORT_NAME("Blue Button 3")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_NAME("Blue Button 4")

	PORT_START("IN.5") // B5 port A
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_NAME("Blue Button 5")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_Y) PORT_NAME("Blue Button 6")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_H) PORT_NAME("Blue Button 7")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_N) PORT_NAME("Blue Button 8")
INPUT_PORTS_END

// config

void leboom_state::leboom(machine_config &config)
{
	// basic machine hardware
	PIC1655(config, m_maincpu, 1000000); // approximation
	m_maincpu->read_a().set(FUNC(leboom_state::read_a));
	m_maincpu->write_b().set(FUNC(leboom_state::write_b));
	m_maincpu->read_c().set_constant(0xff);
	m_maincpu->write_c().set(FUNC(leboom_state::write_c));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 1);
	config.set_default_layout(layout_leboom);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "volume", 0.25);
	FILTER_VOLUME(config, m_volume).add_route(ALL_OUTPUTS, "mono", 1.0);

	TIMER(config, "speaker_decay").configure_periodic(FUNC(leboom_state::speaker_decay_sim), attotime::from_msec(5));
}

// roms

ROM_START( leboom )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "pic_1655a-061", 0x0000, 0x0400, CRC(5880eea1) SHA1(e3795b347fd5df9de084da36e33f6b70fbc0b0ae) )
ROM_END





/*******************************************************************************

  Tiger Electronics Rocket Pinball (model 7-460)
  * PIC 1650A-110, 69-11397
  * 3 7seg LEDs + 44 other LEDs, 1-bit sound

  known releases:
  - Hong Kong(1): Rocket Pinball, published by Tiger
  - Hong Kong(2): Spaceship Pinball, published by Toytronic
  - USA(1): Rocket Pinball (model 60-2140), published by Tandy
  - USA(2): Cosmic Pinball (model 49-65456), published by Sears

*******************************************************************************/

class rockpin_state : public hh_pic16_state
{
public:
	rockpin_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pic16_state(mconfig, type, tag)
	{ }

	void rockpin(machine_config &config);

private:
	void update_display();
	void write_a(u8 data);
	void write_b(u8 data);
	void write_c(u8 data);
	void write_d(u8 data);
};

// handlers

void rockpin_state::update_display()
{
	// 3 7seg leds from ports A and B
	m_display->matrix_partial(0, 3, m_a, m_b);

	// 44 leds from ports C and D
	m_display->matrix_partial(3, 6, m_d, m_c);
}

void rockpin_state::write_a(u8 data)
{
	// A3,A4: speaker out
	m_speaker->level_w(data >> 3 & 3);

	// A0-A2: select digit
	m_a = ~data & 7;
	update_display();
}

void rockpin_state::write_b(u8 data)
{
	// B0-B6: digit segments
	m_b = data & 0x7f;
	update_display();
}

void rockpin_state::write_c(u8 data)
{
	// C0-C7: led data
	m_c = ~data;
	update_display();
}

void rockpin_state::write_d(u8 data)
{
	// D0-D5: led select
	m_d = ~data;
	update_display();
}

// inputs

static INPUT_PORTS_START( rockpin )
	PORT_START("IN.0") // port A
	PORT_BIT( 0x1f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Right Flipper")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Left Flipper")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Ball")
INPUT_PORTS_END

// config

void rockpin_state::rockpin(machine_config &config)
{
	// basic machine hardware
	PIC1650(config, m_maincpu, 450000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->write_a().set(FUNC(rockpin_state::write_a));
	m_maincpu->read_b().set_constant(0xff);
	m_maincpu->write_b().set(FUNC(rockpin_state::write_b));
	m_maincpu->read_c().set_constant(0xff);
	m_maincpu->write_c().set(FUNC(rockpin_state::write_c));
	m_maincpu->read_d().set_constant(0xff);
	m_maincpu->write_d().set(FUNC(rockpin_state::write_d));

	// PIC CLKOUT, tied to RTCC
	CLOCK(config, "clock", 450000/4).signal_handler().set_inputline(m_maincpu, PIC16C5x_RTCC);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(3+6, 8);
	m_display->set_segmask(7, 0x7f);
	config.set_default_layout(layout_rockpin);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( rockpin )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "pic_1650a-110_69-11397", 0x0000, 0x0400, CRC(d5396e77) SHA1(952feaff70fde53a9eda84c54704520d50749e78) )
ROM_END





/*******************************************************************************

  Tiger Electronics Half Court Computer Basketball (model 7-470)
  * PIC 1655A(no serial), 69-11557
  * 2 7seg LEDs + 26 other LEDs, 1-bit sound

  known releases:
  - Hong Kong: Half Court Computer Basketball, published by Tiger
  - USA: Electronic Basketball (model 49-65453), published by Sears

*******************************************************************************/

class hccbaskb_state : public hh_pic16_state
{
public:
	hccbaskb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pic16_state(mconfig, type, tag)
	{ }

	void hccbaskb(machine_config &config);

private:
	void update_display();
	u8 read_a();
	void write_b(u8 data);
	void write_c(u8 data);
};

// handlers

void hccbaskb_state::update_display()
{
	m_display->matrix(m_b, m_c);
}

u8 hccbaskb_state::read_a()
{
	// A2: skill switch, A3: multiplexed inputs
	return m_inputs[5]->read() | read_inputs(5, 8) | 3;
}

void hccbaskb_state::write_b(u8 data)
{
	// B0: RTCC pin
	m_maincpu->set_input_line(PIC16C5x_RTCC, data & 1);

	// B0-B4: input mux
	m_inp_mux = ~data & 0x1f;

	// B7: speaker out
	m_speaker->level_w(data >> 7 & 1);

	// B0-B4: led select
	// B5,B6: digit select
	m_b = data;
	update_display();
}

void hccbaskb_state::write_c(u8 data)
{
	// C0-C6: led data
	m_c = ~data;
	update_display();
}

// inputs

static INPUT_PORTS_START( hccbaskb )
	PORT_START("IN.0") // B0 port A3
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_16WAY

	PORT_START("IN.1") // B1 port A3
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_16WAY

	PORT_START("IN.2") // B2 port A3
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_16WAY

	PORT_START("IN.3") // B3 port A3
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_16WAY

	PORT_START("IN.4") // B4 port A3
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1 )

	PORT_START("IN.5") // port A2
	PORT_CONFNAME( 0x04, 0x04, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x04, "1" )
	PORT_CONFSETTING(    0x00, "2" )
INPUT_PORTS_END

// config

void hccbaskb_state::hccbaskb(machine_config &config)
{
	// basic machine hardware
	PIC1655(config, m_maincpu, 800000); // approximation - RC osc. R=15K, C=47pF
	m_maincpu->read_a().set(FUNC(hccbaskb_state::read_a));
	m_maincpu->write_b().set(FUNC(hccbaskb_state::write_b));
	m_maincpu->read_c().set_constant(0xff);
	m_maincpu->write_c().set(FUNC(hccbaskb_state::write_c));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(7, 7);
	m_display->set_segmask(0x60, 0x7f);
	m_display->set_bri_levels(0.01, 0.2); // player led is brighter
	config.set_default_layout(layout_hccbaskb);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( hccbaskb )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "69-11557", 0x0000, 0x0400, CRC(56e81079) SHA1(1933f87f82c4c53f953534dba7757c9afc52d5bc) )
ROM_END





/*******************************************************************************

  Toytronic Football
  * PIC 1655A-033
  * 4511 7seg BCD decoder, 7 7seg LEDs + 27 other LEDs
  * 1-bit sound through volume gate

  Hello and welcome to another Mattel Football clone, there are so many of these.
  Comparison suggests that this is the 'sequel' to 1655A-024.

*******************************************************************************/

class ttfball_state : public hh_pic16_state
{
public:
	ttfball_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pic16_state(mconfig, type, tag)
	{ }

	void ttfball(machine_config &config);

protected:
	void update_display();
	u8 read_a();
	void write_b(u8 data);
	virtual void write_c(u8 data);
};

// handlers

void ttfball_state::update_display()
{
	// C0-C2: led data
	// C0-C3: 4511 A-D, C4: digit segment DP
	// C5: select digits or led matrix
	const u8 cd4511_map[0x10] = { 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7c,0x07,0x7f,0x67 };
	u16 led_data = (m_c & 0x20) ? (cd4511_map[m_c & 0xf] | (~m_c << 3 & 0x80)) : (~m_c << 8 & 0x700);

	m_display->matrix(m_b | (m_c << 1 & 0x100), led_data);
}

u8 ttfball_state::read_a()
{
	// A3: multiplexed inputs, A0-A2: other inputs
	return m_inputs[5]->read() | read_inputs(5, 8);
}

void ttfball_state::write_b(u8 data)
{
	// B0: RTCC pin
	m_maincpu->set_input_line(PIC16C5x_RTCC, data & 1);

	// B0,B1,B3,B7: input mux low
	m_inp_mux = (m_inp_mux & 0x10) | (~data & 3) | (~data >> 1 & 4) | (~data >> 4 & 8);

	// B0-B7: led select (see above)
	m_b = data;
	update_display();
}

void ttfball_state::write_c(u8 data)
{
	// C7: input mux high
	m_inp_mux = (m_inp_mux & 0xf) | (data >> 3 & 0x10);

	// C0-C7: led data/select (see above)
	m_c = data;
	update_display();
}

// inputs

static INPUT_PORTS_START( ttfball )
	PORT_START("IN.0") // B0 port A3
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_16WAY

	PORT_START("IN.1") // B1 port A3
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_16WAY

	PORT_START("IN.2") // B3 port A3
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_16WAY

	PORT_START("IN.3") // B7 port A3
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_16WAY

	PORT_START("IN.4") // C7 port A3
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Kick")

	PORT_START("IN.5") // port A
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_START1 ) PORT_NAME("Status")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_START2 ) PORT_NAME("Score")
	PORT_CONFNAME( 0x04, 0x04, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x04, "1" )
	PORT_CONFSETTING(    0x00, "2" )
INPUT_PORTS_END

// config

void ttfball_state::ttfball(machine_config &config)
{
	// basic machine hardware
	PIC1655(config, m_maincpu, 550000); // approximation - RC osc. 27K, C=68pF
	m_maincpu->read_a().set(FUNC(ttfball_state::read_a));
	m_maincpu->write_b().set(FUNC(ttfball_state::write_b));
	m_maincpu->read_c().set_constant(0xff);
	m_maincpu->write_c().set(FUNC(ttfball_state::write_c));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 11);
	m_display->set_segmask(0x7f, 0xff);
	m_display->set_bri_levels(0.003, 0.03); // player led is brighter
	config.set_default_layout(layout_ttfball);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	auto &gate(CLOCK(config, "gate", 3500)); // approximation
	gate.signal_handler().set("merge", FUNC(input_merger_all_high_device::in_w<0>));
	m_maincpu->write_c().append("merge", FUNC(input_merger_all_high_device::in_w<1>)).bit(6);

	auto &merge(INPUT_MERGER_ALL_HIGH(config, "merge"));
	merge.output_handler().set(m_speaker, FUNC(speaker_sound_device::level_w));
}

// roms

ROM_START( ttfball )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "pic_1655a-033", 0x0000, 0x0400, CRC(2b500501) SHA1(f7fe464663c56e2181a31a1dc5f1f5239df57bed) )
ROM_END





/*******************************************************************************

  Toytronic Football (model 003201)
  * PIC 1655-024
  * 4511 7seg BCD decoder, 7 7seg LEDs + 27 other LEDs, 1-bit sound

  The 1655-024 version looks and sounds the same as Conic "Electronic Football".
  The reason it went through several brands (and even a no brand one) was
  probably due to legal pursuit from Mattel.

  known releases:
  - Hong Kong(1): Football, published by Toytronic
  - Hong Kong(2): Football, published by (no brand)

*******************************************************************************/

class ttfballa_state : public ttfball_state
{
public:
	ttfballa_state(const machine_config &mconfig, device_type type, const char *tag) :
		ttfball_state(mconfig, type, tag)
	{ }

	void ttfballa(machine_config &config);

protected:
	virtual void write_c(u8 data) override;
};

// handlers

void ttfballa_state::write_c(u8 data)
{
	// C6: speaker out
	m_speaker->level_w(BIT(data, 6));

	// same as ttfball
	m_c = data;
	update_display();
}

// inputs

static INPUT_PORTS_START( ttfballa )
	PORT_START("IN.0") // B0 port A3
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Kick")

	PORT_START("IN.1") // B1 port A3
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Forward")

	PORT_START("IN.2") // B3 port A3
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_16WAY

	PORT_START("IN.3") // B7 port A3
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_16WAY

	PORT_START("IN.4") // C7 port A3 (not used)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN.5") // port A
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_START1 ) PORT_NAME("Status")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_START2 ) PORT_NAME("Score")
	PORT_CONFNAME( 0x04, 0x04, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x04, "1" )
	PORT_CONFSETTING(    0x00, "2" )
INPUT_PORTS_END

// config

void ttfballa_state::ttfballa(machine_config &config)
{
	ttfball(config);

	m_maincpu->set_clock(500000); // approximation - RC osc. 33K, C=68pF
	m_display->set_bri_levels(0.002, 0.02);

	// no volume gate
	m_maincpu->write_c().set(FUNC(ttfballa_state::write_c));
	config.device_remove("gate");
	config.device_remove("merge");
}

// roms

ROM_START( ttfballa )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "pic_1655-024", 0x0000, 0x0400, CRC(9091102f) SHA1(ef72759f20b5a99e0366863caad1e26be114263f) )
ROM_END





/*******************************************************************************

  U.S. Games Programmable Baseball
  * PIC 1650A-133
  * 3 7seg LEDs + 36 other LEDs, 1-bit sound

  known releases:
  - USA(1): Programmable Baseball, published by U.S. Games
  - USA(2): Electronic 2-Player Baseball (model 60-2157), published by Tandy

*******************************************************************************/

class uspbball_state : public hh_pic16_state
{
public:
	uspbball_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pic16_state(mconfig, type, tag)
	{ }

	void uspbball(machine_config &config);

private:
	void update_display();
	void write_a(u8 data);
	void write_b(u8 data);
	void write_c(u8 data);
	void write_d(u8 data);
};

// handlers

void uspbball_state::update_display()
{
	m_display->matrix(m_d, m_c << 8 | m_b);
}

void uspbball_state::write_a(u8 data)
{
	// A0: speaker out
	m_speaker->level_w(data & 1);
}

void uspbball_state::write_b(u8 data)
{
	// B: digit segment data
	m_b = bitswap<8>(data,0,1,2,3,4,5,6,7);
	update_display();
}

void uspbball_state::write_c(u8 data)
{
	// C: led data
	m_c = ~data;
	update_display();
}

void uspbball_state::write_d(u8 data)
{
	// D0-D2: digit select
	// D3-D5: led select
	m_d = ~data;
	update_display();
}

// inputs

static INPUT_PORTS_START( uspbball )
	PORT_START("IN.0") // port A
	PORT_BIT( 0x03, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Curve Right")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_COCKTAIL PORT_NAME("P2 Slow")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_COCKTAIL PORT_NAME("P2 Fast")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_COCKTAIL PORT_NAME("P2 Curve Left")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_COCKTAIL PORT_NAME("P2 Change Up/Fielder")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("P1 Batter")

	PORT_START("IN.1") // port D
	PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_CONFNAME( 0x80, 0x80, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x80, "1" )
	PORT_CONFSETTING(    0x00, "2" )
INPUT_PORTS_END

// config

void uspbball_state::uspbball(machine_config &config)
{
	// basic machine hardware
	PIC1650(config, m_maincpu, 900000); // approximation - RC osc. R=22K, C=47pF
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->write_a().set(FUNC(uspbball_state::write_a));
	m_maincpu->read_b().set_constant(0xff);
	m_maincpu->write_b().set(FUNC(uspbball_state::write_b));
	m_maincpu->read_c().set_constant(0xff);
	m_maincpu->write_c().set(FUNC(uspbball_state::write_c));
	m_maincpu->read_d().set_constant(0xff);
	m_maincpu->write_d().set(FUNC(uspbball_state::write_d));

	// PIC CLKOUT, tied to RTCC
	CLOCK(config, "clock", 900000/4).signal_handler().set_inputline("maincpu", PIC16C5x_RTCC);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(6, 16);
	m_display->set_segmask(7, 0x7f);
	config.set_default_layout(layout_hh_pic16_test);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( uspbball )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "pic_1650a-133", 0x0000, 0x0400, CRC(479e98be) SHA1(67437177b059dfa6e01940da26daf997cec96ead) )
ROM_END





/*******************************************************************************

  U.S. Games Electronic 2-Player Football
  * PIC 1650A-144
  * 8 7seg LEDs + 2 other LEDs, 1-bit sound

  known releases:
  - USA(1): Electronic 2-Player Football, published by U.S. Games
  - USA(2): Electronic 2-Player Football (model 60-2156), published by Tandy

*******************************************************************************/

class us2pfball_state : public hh_pic16_state
{
public:
	us2pfball_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pic16_state(mconfig, type, tag)
	{ }

	void us2pfball(machine_config &config);

private:
	void update_display();
	u8 read_a();
	void write_a(u8 data);
	void write_b(u8 data);
	void write_c(u8 data);
	void write_d(u8 data);
};

// handlers

void us2pfball_state::update_display()
{
	m_display->matrix(m_d | (m_a << 6 & 0x300), m_c);
}

u8 us2pfball_state::read_a()
{
	// A0,A1: multiplexed inputs, A4-A7: other inputs
	return read_inputs(4, 3) | (m_inputs[4]->read() & 0xf0) | 0x0c;
}

void us2pfball_state::write_a(u8 data)
{
	// A2,A3: leds
	m_a = data;
	update_display();
}

void us2pfball_state::write_b(u8 data)
{
	// B0-B3: input mux
	m_inp_mux = data & 0xf;
}

void us2pfball_state::write_c(u8 data)
{
	// C7: speaker out
	m_speaker->level_w(data >> 7 & 1);

	// C0-C6: digit segments
	m_c = data;
	update_display();
}

void us2pfball_state::write_d(u8 data)
{
	// D0-D7: digit select
	m_d = ~data;
	update_display();
}

// inputs

static INPUT_PORTS_START( us2pfball )
	PORT_START("IN.0") // B0 port A low
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_16WAY

	PORT_START("IN.1") // B1 port A low
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_16WAY

	PORT_START("IN.2") // B2 port A low
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2) PORT_16WAY

	PORT_START("IN.3") // B3 port A low
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_16WAY

	PORT_START("IN.4") // port A high
	PORT_CONFNAME( 0x10, 0x10, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x10, "1" )
	PORT_CONFSETTING(    0x00, "2" )
	PORT_CONFNAME( 0x20, 0x20, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x20, "1" ) // college
	PORT_CONFSETTING(    0x00, "2" ) // pro
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SELECT ) PORT_TOGGLE PORT_NAME("Play Selector") // pass
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Kick/Pass") // K/P

	PORT_START("IN.5") // port B
	PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START ) PORT_NAME("Status/Score") // S
INPUT_PORTS_END

// config

void us2pfball_state::us2pfball(machine_config &config)
{
	// basic machine hardware
	PIC1650(config, m_maincpu, 800000); // approximation - RC osc. R=39K, C=75pF
	m_maincpu->read_a().set(FUNC(us2pfball_state::read_a));
	m_maincpu->write_a().set(FUNC(us2pfball_state::write_a));
	m_maincpu->read_b().set_ioport("IN.5");
	m_maincpu->write_b().set(FUNC(us2pfball_state::write_b));
	m_maincpu->read_c().set_constant(0xff);
	m_maincpu->write_c().set(FUNC(us2pfball_state::write_c));
	m_maincpu->read_d().set_constant(0xff);
	m_maincpu->write_d().set(FUNC(us2pfball_state::write_d));

	// PIC CLKOUT, tied to RTCC
	CLOCK(config, "clock", 800000/4).signal_handler().set_inputline("maincpu", PIC16C5x_RTCC);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 7);
	m_display->set_segmask(0xff, 0x7f);
	m_display->set_bri_levels(0.01, 0.17); // player led is brighter
	config.set_default_layout(layout_us2pfball);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( us2pfball )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "pic_1650a-144", 0x0000, 0x0400, CRC(ef3677c9) SHA1(33f89c79e7e090710681dffe09eddaf66b5cb794) )
ROM_END



} // anonymous namespace

/*******************************************************************************

  Game driver(s)

*******************************************************************************/

//    YEAR  NAME       PARENT   COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY, FULLNAME, FLAGS
SYST( 1979, touchme,   0,       0,      touchme,   touchme,   touchme_state,   empty_init, "Atari", "Touch Me (handheld, rev. 2)", MACHINE_SUPPORTS_SAVE )

SYST( 1979, pabball,   0,       0,      pabball,   pabball,   pabball_state,   empty_init, "Calfax / Caprice Electronics", "Pro-Action Electronic-Computerized Baseball", MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING )

SYST( 1981, sfxphasor, 0,       0,      sfxphasor, sfxphasor, sfxphasor_state, empty_init, "Electroplay", "Sound FX Phasor", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )

SYST( 1980, melodym,   0,       0,      melodym,   melodym,   melodym_state,   empty_init, "GAF", "Melody Madness", MACHINE_SUPPORTS_SAVE )

SYST( 1979, maniac,    0,       0,      maniac,    maniac,    maniac_state,    empty_init, "Ideal Toy Corporation", "Maniac", MACHINE_SUPPORTS_SAVE )
SYST( 1980, flash,     0,       0,      flash,     flash,     flash_state,     empty_init, "Ideal Toy Corporation", "Flash (Ideal)", MACHINE_SUPPORTS_SAVE )

SYST( 1980, matchme,   0,       0,      matchme,   matchme,   matchme_state,   empty_init, "Kingsford", "Match Me", MACHINE_SUPPORTS_SAVE )

SYST( 1979, drdunk,    0,       0,      drdunk,    drdunk,    drdunk_state,    empty_init, "Kmart Corporation", "Dr. Dunk", MACHINE_SUPPORTS_SAVE )

SYST( 1980, leboom,    0,       0,      leboom,    leboom,    leboom_state,    empty_init, "Lakeside", "Le Boom", MACHINE_SUPPORTS_SAVE )

SYST( 1979, rockpin,   0,       0,      rockpin,   rockpin,   rockpin_state,   empty_init, "Tiger Electronics", "Rocket Pinball", MACHINE_SUPPORTS_SAVE )
SYST( 1979, hccbaskb,  0,       0,      hccbaskb,  hccbaskb,  hccbaskb_state,  empty_init, "Tiger Electronics", "Half Court Computer Basketball", MACHINE_SUPPORTS_SAVE )

SYST( 1979, ttfball,   0,       0,      ttfball,   ttfball,   ttfball_state,   empty_init, "Toytronic", "Football (Toytronic, set 1)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )
SYST( 1979, ttfballa,  ttfball, 0,      ttfballa,  ttfballa,  ttfballa_state,  empty_init, "Toytronic", "Football (Toytronic, set 2)", MACHINE_SUPPORTS_SAVE )

SYST( 1981, uspbball,  0,       0,      uspbball,  uspbball,  uspbball_state,  empty_init, "U.S. Games Corporation", "Programmable Baseball", MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING )
SYST( 1981, us2pfball, 0,       0,      us2pfball, us2pfball, us2pfball_state, empty_init, "U.S. Games Corporation", "Electronic 2-Player Football", MACHINE_SUPPORTS_SAVE )



hh_pps41.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle, Kevin Horton
/*******************************************************************************

Rockwell PPS-4/1 MCU series handhelds

ROM source notes when dumped from another title, but confident it's the same:
- memoquiz: Mattel Mind Boggler

*******************************************************************************/

#include "emu.h"

#include "cpu/pps41/mm75.h"
#include "cpu/pps41/mm76.h"
#include "cpu/pps41/mm78.h"
#include "cpu/pps41/mm78la.h"
#include "sound/beep.h"
#include "sound/spkrdev.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "addocalc.lh"
#include "brainbaf.lh"
#include "dunksunk.lh"
#include "ftri1.lh"
#include "horocomp.lh"
#include "mastmind.lh"
#include "memoquiz.lh"
#include "mfootb2.lh"
#include "mwcfootb.lh"
#include "rdqa.lh"
#include "scrabsen.lh"
#include "smastmind.lh"

//#include "hh_pps41_test.lh" // common test-layout - use external artwork


namespace {

class hh_pps41_state : public driver_device
{
public:
	hh_pps41_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_speaker(*this, "speaker"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	virtual DECLARE_INPUT_CHANGED_MEMBER(reset_button);
	virtual DECLARE_INPUT_CHANGED_MEMBER(power_button);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override { update_int(); }
	virtual void device_post_load() override { update_int(); }

	// devices
	required_device<pps41_base_device> m_maincpu;
	optional_device<pwm_display_device> m_display;
	optional_device<speaker_sound_device> m_speaker;
	optional_ioport_array<11> m_inputs; // max 11

	u16 m_inp_mux = 0;
	u32 m_grid = 0;
	u32 m_plate = 0;

	// MCU output pin state
	u16 m_d = 0;
	u16 m_r = 0;
	u8 m_ssc = 0;
	u8 m_sdo = 0;

	u8 read_inputs(int columns);
	void set_power(bool state);
	virtual void update_int() { ; }
};


// machine start

void hh_pps41_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_grid));
	save_item(NAME(m_plate));
	save_item(NAME(m_d));
	save_item(NAME(m_r));
	save_item(NAME(m_ssc));
	save_item(NAME(m_sdo));
}



/*******************************************************************************

  Helper Functions

*******************************************************************************/

// generic input handlers

u8 hh_pps41_state::read_inputs(int columns)
{
	u8 ret = 0;

	// read selected input rows
	for (int i = 0; i < columns; i++)
		if (BIT(m_inp_mux, i))
			ret |= m_inputs[i]->read();

	return ret;
}

INPUT_CHANGED_MEMBER(hh_pps41_state::reset_button)
{
	// when an input is directly wired to MCU PO pin
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}

INPUT_CHANGED_MEMBER(hh_pps41_state::power_button)
{
	if (newval != field.defvalue())
		set_power((bool)param);
}

void hh_pps41_state::set_power(bool state)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? CLEAR_LINE : ASSERT_LINE);

	if (m_display && !state)
		m_display->clear();
}



/*******************************************************************************

  Minidrivers (subclass, I/O, Inputs, Machine Config, ROM Defs)

*******************************************************************************/

/*******************************************************************************

  Addometer Company Addometer Calculator
  * MM78 MCU (label MM78 A7872-11, die label A7872)
  * 12-digit 7seg VFD, 1 digit unused (NEC LD8197A/FIP12A4A 0B)

  This is presumably the only pocket calculator by Addometer Company (previously
  known as Reliable Typewriter and Adding Machine Corporation). It's a feet
  and inches/metric calculator.

*******************************************************************************/

class addocalc_state : public hh_pps41_state
{
public:
	addocalc_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pps41_state(mconfig, type, tag)
	{ }

	void addocalc(machine_config &config);

	virtual DECLARE_INPUT_CHANGED_MEMBER(power_button) override;

private:
	virtual void update_int() override;
	void update_display();
	void write_d(u16 data);
	void write_r(u16 data);
	u8 read_p();
	void write_ssc(int state);
	void write_sdo(int state);
};

// handlers

INPUT_CHANGED_MEMBER(addocalc_state::power_button)
{
	hh_pps41_state::power_button(field, param, oldval, newval);

	// ON/OFF button is also tied to INT1 (see below)
	update_int();
}

void addocalc_state::update_int()
{
	m_maincpu->set_input_line(1, (m_inputs[7]->read() & 1) ? ASSERT_LINE : CLEAR_LINE);
}

void addocalc_state::update_display()
{
	m_display->matrix(m_d | m_ssc << 10, bitswap<8>(~m_r, 0,1,2,3,4,5,6,7));
}

void addocalc_state::write_d(u16 data)
{
	// DIO0-DIO9: digit select
	// DIO0-DIO6: input mux
	m_d = m_inp_mux = data;
	update_display();
}

void addocalc_state::write_r(u16 data)
{
	// RIO1-RIO8: digit segment data
	m_r = data;
	update_display();
}

u8 addocalc_state::read_p()
{
	// PI1-PI4: multiplexed inputs
	return ~read_inputs(7);
}

void addocalc_state::write_ssc(int state)
{
	// CLOCK: one more digit
	m_ssc = state;
	update_display();
}

void addocalc_state::write_sdo(int state)
{
	// DATAO: power off on rising edge
	if (state && !m_sdo)
		set_power(false);
	m_sdo = state;
}

// inputs

static INPUT_PORTS_START( addocalc )
	PORT_START("IN.0") // DIO0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CL")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // DIO1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")

	PORT_START("IN.2") // DIO2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("STO")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("REC")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("EX")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")

	PORT_START("IN.3") // DIO3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("FT")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_I) PORT_NAME("IN")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("FRA")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_M) PORT_NAME("MET")

	PORT_START("IN.4") // DIO4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")

	PORT_START("IN.5") // DIO5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")

	PORT_START("IN.6") // DIO6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.7") // INT1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_ON ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(addocalc_state::power_button), true) PORT_NAME("ON/OFF")
INPUT_PORTS_END

// config

void addocalc_state::addocalc(machine_config &config)
{
	// basic machine hardware
	MM78(config, m_maincpu, 430000); // approximation - VC osc. R=47K
	m_maincpu->write_d().set(FUNC(addocalc_state::write_d));
	m_maincpu->write_r().set(FUNC(addocalc_state::write_r));
	m_maincpu->read_p().set(FUNC(addocalc_state::read_p));
	m_maincpu->write_ssc().set(FUNC(addocalc_state::write_ssc));
	m_maincpu->write_sdo().set(FUNC(addocalc_state::write_sdo));
	m_maincpu->read_sdi().set_constant(0);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(11, 8);
	m_display->set_segmask(0x7ff, 0xff);
	config.set_default_layout(layout_addocalc);

	// no sound!
}

// roms

ROM_START( addocalc )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mm78_a7872-11", 0x0000, 0x0800, CRC(1ff26da7) SHA1(b4b9b5886d60dcd634661604f9ef5346a6c8bd1b) )
ROM_END





/*******************************************************************************

  Fonas Tri-1
  * PCB label: CASSIA CA010-F
  * MM78 MCU variant with 40 pins (no label, die label A7859)
  * 4 7seg leds, 41 other leds, 1-bit sound

  The game only uses 1.5KB ROM and seems it doesn't use all the RAM either,
  as if it was programmed for MM77L.

  Hold all 4 buttons at boot (not counting RESET) for a led test.
  Cassia was Eric White/Ken Cohen's company, later named CXG, known for
  their chess computers.

*******************************************************************************/

class ftri1_state : public hh_pps41_state
{
public:
	ftri1_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pps41_state(mconfig, type, tag)
	{ }

	void ftri1(machine_config &config);

private:
	void update_display();
	void write_d(u16 data);
	void write_r(u16 data);
};

// handlers

void ftri1_state::update_display()
{
	m_display->matrix(m_d, bitswap<8>(~m_r, 0,7,6,5,4,3,2,1));
}

void ftri1_state::write_d(u16 data)
{
	// DIO0-DIO8: digit/led select
	m_d = data;
	update_display();

	// DIO9: speaker out
	m_speaker->level_w(BIT(data, 9));
}

void ftri1_state::write_r(u16 data)
{
	// RIO1-RIO8: digit/led data
	m_r = data;
	update_display();
}

// inputs

static INPUT_PORTS_START( ftri1 )
	PORT_START("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x0c, 0x04, "Game Select" )
	PORT_CONFSETTING(    0x08, "Star Chase" )
	PORT_CONFSETTING(    0x04, "All Star Baseball" )
	PORT_CONFSETTING(    0x00, "Batting Champs" )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Score / S1 H")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Steal / S1 V")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Pitch / S2 H")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Swing / S2 V")

	PORT_START("RESET")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_ON ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ftri1_state::reset_button), 0) PORT_NAME("Game Reset")
INPUT_PORTS_END

// config

void ftri1_state::ftri1(machine_config &config)
{
	// basic machine hardware
	MM78(config, m_maincpu, 300000); // approximation - VC osc. R=68K
	m_maincpu->write_d().set(FUNC(ftri1_state::write_d));
	m_maincpu->write_r().set(FUNC(ftri1_state::write_r));
	m_maincpu->read_p().set_ioport("IN.0");

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	m_display->set_segmask(0x1e0, 0x7f);
	config.set_default_layout(layout_ftri1);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( ftri1 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "a7859", 0x0000, 0x0800, CRC(3c957f1d) SHA1(42db81a78bbef971a84e61a26d91f7411980d79c) )
ROM_END





/*******************************************************************************

  Invicta Electronic Master Mind
  * MM75 MCU (label MM75 A7525-11, die label A7525)
  * 9-digit 7seg VFD (Futaba 9-ST)

  Invicta Super-Sonic Electronic Master Mind
  * MM75 MCU (label A7539-12, die label A7539)
  * same base hardware, added beeper

  Invicta Plastics is the owner of the Mastermind game rights. The back of the
  Master Mind unit says (C) 1977, but this electronic handheld version came
  out in 1979. Or maybe there's an older revision.

*******************************************************************************/

class mastmind_state : public hh_pps41_state
{
public:
	mastmind_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pps41_state(mconfig, type, tag),
		m_beeper(*this, "beeper")
	{ }

	void mastmind(machine_config &config);
	void smastmind(machine_config &config);

private:
	optional_device<beep_device> m_beeper;

	void update_display();
	void write_d(u16 data);
	void write_r(u16 data);
	u8 read_p();
};

// handlers

void mastmind_state::update_display()
{
	m_display->matrix(m_inp_mux, ~m_r);
}

void mastmind_state::write_d(u16 data)
{
	// DIO0-DIO7: digit select (DIO7 N/C on mastmind)
	// DIO0-DIO3: input mux
	m_inp_mux = data;
	update_display();

	// DIO8: beeper on smastmind
	if (m_beeper)
		m_beeper->set_state(BIT(data, 8));
}

void mastmind_state::write_r(u16 data)
{
	// RIO1-RIO7: digit segment data
	m_r = data;
	update_display();
}

u8 mastmind_state::read_p()
{
	// PI1-PI4: multiplexed inputs
	return ~read_inputs(4);
}

// inputs

static INPUT_PORTS_START( mastmind )
	PORT_START("IN.0") // DIO0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Try")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("Fail")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED ) // display test on mastmind?
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // DIO1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Set")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Clear")

	PORT_START("IN.2") // DIO2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")

	PORT_START("IN.3") // DIO3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
INPUT_PORTS_END

// config

void mastmind_state::mastmind(machine_config &config)
{
	// basic machine hardware
	MM75(config, m_maincpu, 360000); // approximation - VC osc. R=56K
	m_maincpu->write_d().set(FUNC(mastmind_state::write_d));
	m_maincpu->write_r().set(FUNC(mastmind_state::write_r));
	m_maincpu->read_p().set(FUNC(mastmind_state::read_p));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 7);
	m_display->set_segmask(0xff, 0x7f);
	config.set_default_layout(layout_mastmind);

	// no sound!
}

void mastmind_state::smastmind(machine_config &config)
{
	mastmind(config);

	config.set_default_layout(layout_smastmind);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 2400); // approximation
	m_beeper->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( mastmind )
	ROM_REGION( 0x0400, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "mm75_a7525-11", 0x0000, 0x0200, CRC(39dbdd50) SHA1(72fa5781e9df62d91d57437ded2931fab8253c3c) )
	ROM_CONTINUE(              0x0380, 0x0080 )

	ROM_REGION( 314, "maincpu:opla", 0 )
	ROM_LOAD( "mm76_mastmind_output.pla", 0, 314, CRC(c936aee7) SHA1(e9ec08a82493d6b63e936f82deeab3e4449b54c3) )
ROM_END

ROM_START( smastmind )
	ROM_REGION( 0x0400, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "a7539-12", 0x0000, 0x0200, CRC(b63c453f) SHA1(f47a540fd90eed7514ed03864be2121f641c1154) )
	ROM_CONTINUE(         0x0380, 0x0080 )

	ROM_REGION( 314, "maincpu:opla", 0 )
	ROM_LOAD( "mm76_smastmind_output.pla", 0, 314, CRC(c936aee7) SHA1(e9ec08a82493d6b63e936f82deeab3e4449b54c3) )
ROM_END





/*******************************************************************************

  Kmart Dunk 'n Sunk (manufactured in Hong Kong)
  * MM76EL MCU (label GE-E 1V2280, die label B8617)
  * 4 7seg leds, 31 other leds, 1-bit sound

  It's by the same Hong Kong company that did Kmart Dr. Dunk/Tandy Electronic
  Basketball (PIC16 MCU), Grandstand Pocket Match of the Day (MOS MPS765x MCU)
  among others.

  known releases:
  - USA(1): Dunk 'n Sunk, published by Kmart
  - USA(2): Electronic Basketball / Submarine Warfare, published by U.S. Games

*******************************************************************************/

class dunksunk_state : public hh_pps41_state
{
public:
	dunksunk_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pps41_state(mconfig, type, tag)
	{ }

	void dunksunk(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(difficulty_switch) { update_int(); }
	DECLARE_INPUT_CHANGED_MEMBER(game_switch) { update_int(); }

private:
	virtual void update_int() override;
	void update_display();
	void write_d(u16 data);
	void write_r(u16 data);
};

// handlers

void dunksunk_state::update_int()
{
	// 2 of the switches are tied to MCU interrupt pins
	m_maincpu->set_input_line(0, (m_inputs[1]->read() & 1) ? ASSERT_LINE : CLEAR_LINE);
	m_maincpu->set_input_line(1, (m_inputs[2]->read() & 1) ? ASSERT_LINE : CLEAR_LINE);
}

void dunksunk_state::update_display()
{
	m_display->matrix(m_d >> 1, ~m_r);
}

void dunksunk_state::write_d(u16 data)
{
	// DIO0: speaker out
	m_speaker->level_w(data & 1);

	// DIO1-DIO5: led select
	// DIO6-DIO9: digit select
	m_d = data;
	update_display();
}

void dunksunk_state::write_r(u16 data)
{
	// RIO1-RIO7: led data
	m_r = data;
	update_display();
}

// inputs

static INPUT_PORTS_START( dunksunk )
	PORT_START("IN.0") // PI
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_CONDITION("IN.1", 0x03, EQUALS, 0x01)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )

	PORT_START("IN.1")
	PORT_CONFNAME( 0x03, 0x00, DEF_STR( Difficulty ) ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dunksunk_state::difficulty_switch), 0)
	PORT_CONFSETTING(    0x00, "1" ) // INT0
	PORT_CONFSETTING(    0x01, "2" ) // PI4
	PORT_CONFSETTING(    0x03, "3" )

	PORT_START("IN.2") // INT1
	PORT_CONFNAME( 0x01, 0x01, "Game Select" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(dunksunk_state::game_switch), 0)
	PORT_CONFSETTING(    0x01, "Basketball" )
	PORT_CONFSETTING(    0x00, "Submarine Chase" )
INPUT_PORTS_END

// config

void dunksunk_state::dunksunk(machine_config &config)
{
	// basic machine hardware
	MM76EL(config, m_maincpu, 390000); // approximation - VC osc. R=56K
	m_maincpu->write_d().set(FUNC(dunksunk_state::write_d));
	m_maincpu->write_r().set(FUNC(dunksunk_state::write_r));
	m_maincpu->read_p().set_ioport("IN.0");

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 7);
	m_display->set_segmask(0x1e0, 0x7f);
	m_display->set_bri_levels(0.015, 0.2); // player led is brighter
	config.set_default_layout(layout_dunksunk);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( dunksunk )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "ge-e_1v2280", 0x0000, 0x0400, CRC(90f17191) SHA1(80c3708af99c9db7afe17254fa4df2080aa9f145) )

	ROM_REGION( 314, "maincpu:opla", 0 )
	ROM_LOAD( "mm76_dunksunk_output.pla", 0, 314, CRC(410fa6d7) SHA1(d46aaf1ec2c942083cba7dbd59d4261dc238d4c8) )
ROM_END





/*******************************************************************************

  M.E.M. Belgium Memoquiz
  * PCB label: MEMOQUIZ MO3
  * MM75 MCU (label M7505 A7505-12, die label A7505)
  * 9-digit 7seg VFD, no sound

  It's a Mastermind game, not as straightforward as Invicta's version.
  To start, press the "?" button to generate a new code, then try to guess it,
  confirming with the "=" button. CD reveals the answer, PE is for player entry.

  known releases:
  - Europe: Memoquiz, published by M.E.M. Belgium
  - UK: Memoquiz, published by Polymark
  - USA: Mind Boggler (model 2626), published by Mattel

*******************************************************************************/

class memoquiz_state : public hh_pps41_state
{
public:
	memoquiz_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pps41_state(mconfig, type, tag)
	{ }

	void memoquiz(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(digits_switch) { update_int(); }

private:
	virtual void update_int() override;
	void update_display();
	void write_d(u16 data);
	void write_r(u16 data);
	u8 read_p();
};

// handlers

void memoquiz_state::update_int()
{
	// digits switch is tied to MCU interrupt pins
	u8 inp = m_inputs[4]->read();
	m_maincpu->set_input_line(0, (inp & 1) ? CLEAR_LINE : ASSERT_LINE);
	m_maincpu->set_input_line(1, (inp & 2) ? ASSERT_LINE : CLEAR_LINE);
}

void memoquiz_state::update_display()
{
	m_display->matrix(m_inp_mux, (m_inp_mux << 2 & 0x80) | (~m_r & 0x7f));
}

void memoquiz_state::write_d(u16 data)
{
	// DIO0-DIO7: digit select, DIO5 is also DP segment
	// DIO0-DIO3: input mux
	m_inp_mux = data;
	update_display();

	// DIO8: N/C, looks like they planned to add sound, but didn't
}

void memoquiz_state::write_r(u16 data)
{
	// RIO1-RIO7: digit segment data
	m_r = data;
	update_display();
}

u8 memoquiz_state::read_p()
{
	// PI1-PI4: multiplexed inputs
	return ~read_inputs(4);
}

// inputs

static INPUT_PORTS_START( memoquiz )
	PORT_START("IN.0") // DIO0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")

	PORT_START("IN.1") // DIO1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")

	PORT_START("IN.2") // DIO2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("AC")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")

	PORT_START("IN.3") // DIO3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("?")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_P) PORT_NAME("PE")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("CD")

	PORT_START("IN.4")
	PORT_CONFNAME( 0x03, 0x01, "Digits" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(memoquiz_state::digits_switch), 0)
	PORT_CONFSETTING(    0x01, "3" ) // INT0, Vdd when closed, pulled to GND when open
	PORT_CONFSETTING(    0x02, "4" ) // INT1, GND when closed, pulled to Vdd when open
	PORT_CONFSETTING(    0x00, "5" )
INPUT_PORTS_END

// config

void memoquiz_state::memoquiz(machine_config &config)
{
	// basic machine hardware
	MM75(config, m_maincpu, 360000); // approximation - VC osc. R=56K
	m_maincpu->write_d().set(FUNC(memoquiz_state::write_d));
	m_maincpu->write_r().set(FUNC(memoquiz_state::write_r));
	m_maincpu->read_p().set(FUNC(memoquiz_state::read_p));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 8);
	m_display->set_segmask(0xff, 0xff);
	config.set_default_layout(layout_memoquiz);

	// no sound!
}

// roms

ROM_START( memoquiz )
	ROM_REGION( 0x0400, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "m7505_a7505-12", 0x0000, 0x0200, CRC(47223508) SHA1(97b62e0c453ae2e65d48e039ad65857dae2d4d76) )
	ROM_CONTINUE(               0x0380, 0x0080 )

	ROM_REGION( 314, "maincpu:opla", 0 )
	ROM_LOAD( "mm76_memoquiz_output.pla", 0, 314, CRC(a5799b50) SHA1(9b4923b37c9ba8221ecece5a3370c605a880a453) )
ROM_END





/*******************************************************************************

  Mattel Football 2 (model 1050)
  * PCB label: MATTEL, 1050-4369D
  * MM77LA MCU (label B8000-12, die label B8000)
  * 7 7seg leds, 30 other leds, 2-bit sound

  Through its production run, it was released as "Football 2" and "Football II".

*******************************************************************************/

class mfootb2_state : public hh_pps41_state
{
public:
	mfootb2_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pps41_state(mconfig, type, tag)
	{ }

	void mfootb2(machine_config &config);

private:
	void update_display();
	void write_d(u16 data);
	void write_r(u16 data);
	void write_spk(u8 data);
};

// handlers

void mfootb2_state::update_display()
{
	m_display->matrix(m_d, (m_r << 1 & 0x700) | (m_d >> 4 & 0x80) | (m_r & 0x7f));
}

void mfootb2_state::write_d(u16 data)
{
	// DIO0-DIO2, DIO6-DIO9: digit select
	// DIO3-DIO5: led select
	// DIO10: 4th digit DP
	m_d = data;
	update_display();
}

void mfootb2_state::write_r(u16 data)
{
	// RO01-RO10: led data
	m_r = data;
	update_display();
}

void mfootb2_state::write_spk(u8 data)
{
	// SPK: speaker out
	m_speaker->level_w(data);
}

// inputs

static INPUT_PORTS_START( mfootb2 )
	PORT_START("IN.0") // PI
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START2 ) PORT_NAME("Score")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Status")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Kick")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Pass")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY

	PORT_START("IN.1") // DIO11
	PORT_CONFNAME( 0x400, 0x000, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(     0x000, "1" ) // PRO 1
	PORT_CONFSETTING(     0x400, "2" ) // PRO 2
INPUT_PORTS_END

// config

void mfootb2_state::mfootb2(machine_config &config)
{
	// basic machine hardware
	MM77LA(config, m_maincpu, 380000); // approximation - VC osc. R=56K
	m_maincpu->write_d().set(FUNC(mfootb2_state::write_d));
	m_maincpu->read_d().set_ioport("IN.1");
	m_maincpu->write_r().set(FUNC(mfootb2_state::write_r));
	m_maincpu->read_p().set_ioport("IN.0");
	m_maincpu->write_spk().set(FUNC(mfootb2_state::write_spk));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 11);
	m_display->set_segmask(0x3c7, 0x7f);
	m_display->set_segmask(0x002, 0xff); // only one digit has DP
	m_display->set_bri_levels(0.015, 0.2); // ball led is brighter
	config.set_default_layout(layout_mfootb2);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[4] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.125);
}

// roms

ROM_START( mfootb2 )
	ROM_REGION( 0x0800, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "b8000-12", 0x0000, 0x0600, CRC(5b65fc38) SHA1(4fafc9deb5609b16f09b18b7346ea96ffe8bf9e0) )

	ROM_REGION( 317, "maincpu:opla", 0 )
	ROM_LOAD( "mm77la_mfootb2_output.pla", 0, 317, CRC(11c0bbfa) SHA1(939a0a6adeace8ca0f9e17290306a2e7ced21db3) )
ROM_END





/*******************************************************************************

  Mattel Brain Baffler (model 1080)
  * PCB label: OLYMPOS KOREA, CM04-D102-001 REV D
  * MM78LA MCU (label MM95 B9000-12, die label B9000)
  * 8-digit 14seg LED display, 2-bit sound

  It includes 8 word games, most of them are meant for 2 players.

*******************************************************************************/

class brainbaf_state : public hh_pps41_state
{
public:
	brainbaf_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pps41_state(mconfig, type, tag)
	{ }

	void brainbaf(machine_config &config);

private:
	void update_display();
	void write_d(u16 data);
	void write_r(u16 data);
	u8 read_p();
	void write_spk(u8 data);
};

// handlers

void brainbaf_state::update_display()
{
	m_display->matrix(m_inp_mux, bitswap<14>(m_r, 6,5,13,12,11,4,3,10,9,8,7,2,1,0));
}

void brainbaf_state::write_d(u16 data)
{
	// DIO0-DIO9: input mux
	// DIO0-DIO7: digit select
	m_inp_mux = data;
	update_display();
}

void brainbaf_state::write_r(u16 data)
{
	// RO01-RO14: digit segment data
	m_r = data;
	update_display();
}

u8 brainbaf_state::read_p()
{
	// PI5-PI8: multiplexed inputs
	return read_inputs(10) << 4;
}

void brainbaf_state::write_spk(u8 data)
{
	// SPK: speaker out
	m_speaker->level_w(data);
}

// inputs

static INPUT_PORTS_START( brainbaf )
	PORT_START("IN.0") // DIO0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('J') PORT_NAME("J-0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F12) PORT_NAME("Player / Bonus R")

	PORT_START("IN.1") // DIO1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('A') PORT_NAME("A-1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1) PORT_NAME("Player / Bonus L")

	PORT_START("IN.2") // DIO2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('B') PORT_NAME("B-2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3) PORT_NAME("Game")

	PORT_START("IN.3") // DIO3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('C') PORT_NAME("C-3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F4) PORT_NAME("Go")

	PORT_START("IN.4") // DIO4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('D') PORT_NAME("D-4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F5) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13) PORT_NAME("Entry")

	PORT_START("IN.5") // DIO5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('E') PORT_NAME("E-5")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F6) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_NAME("Clear")

	PORT_START("IN.6") // DIO6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('F') PORT_NAME("F-6")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F7) PORT_NAME("Refresh")

	PORT_START("IN.7") // DIO7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('G') PORT_NAME("G-7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F8) PORT_NAME("Buy")

	PORT_START("IN.8") // DIO8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('H') PORT_NAME("H-8")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F9) PORT_NAME("Give-Up")

	PORT_START("IN.9") // DIO9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('I') PORT_NAME("I-9")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_CODE(KEYCODE_F11) PORT_NAME("Stop") // both STOP keys
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F10) PORT_NAME("Score")
INPUT_PORTS_END

// config

void brainbaf_state::brainbaf(machine_config &config)
{
	// basic machine hardware
	MM78LA(config, m_maincpu, 440000); // approximation - VC osc. R=10K
	m_maincpu->write_d().set(FUNC(brainbaf_state::write_d));
	m_maincpu->write_r().set(FUNC(brainbaf_state::write_r));
	m_maincpu->read_p().set(FUNC(brainbaf_state::read_p));
	m_maincpu->write_spk().set(FUNC(brainbaf_state::write_spk));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 14);
	m_display->set_segmask(0xff, 0x3fff);
	config.set_default_layout(layout_brainbaf);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[4] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.125);
}

// roms

ROM_START( brainbaf )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mm95_b9000-12", 0x0000, 0x0800, CRC(f7a4829c) SHA1(12f789d8264a969777764d31ea67067cc330a73c) )

	ROM_REGION( 605, "maincpu:opla", 0 )
	ROM_LOAD( "mm78la_brainbaf_output.pla", 0, 605, CRC(4fae532f) SHA1(3a08c0fa3ce476c014280b3cfeb6aa37824ae503) )
ROM_END





/*******************************************************************************

  Mattel Horoscope Computer (model 1081)
  * PCB label: DET. CM05-D102-001 REV D
  * MM78LA MCU (label MM95 B9001-13, die label B9001)
  * 8-digit 14seg LED display, 2-bit sound

  This is not a toy, but a fortune forecast. Date format is mm-dd-yy, it is
  valid only from June 1 1979 until December 31 1987.

*******************************************************************************/

class horocomp_state : public hh_pps41_state
{
public:
	horocomp_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pps41_state(mconfig, type, tag)
	{ }

	void horocomp(machine_config &config);

private:
	void update_display();
	void write_d(u16 data);
	void write_r(u16 data);
	u8 read_p();
	void write_spk(u8 data);
};

// handlers

void horocomp_state::update_display()
{
	// 14seg display is upside-down
	u16 flip = m_r << 7 | m_r >> 7;
	m_display->matrix(m_inp_mux, bitswap<14>(flip, 6,5,13,12,11,4,3,10,9,8,7,2,1,0));
}

void horocomp_state::write_d(u16 data)
{
	// DIO0-DIO9: input mux
	// DIO0-DIO7: digit select
	m_inp_mux = data;
	update_display();
}

void horocomp_state::write_r(u16 data)
{
	// RO01-RO14: digit segment data
	m_r = data;
	update_display();
}

u8 horocomp_state::read_p()
{
	// PI4: mode switch
	// PI5-PI8: multiplexed inputs
	return read_inputs(10) << 4 | (m_inputs[10]->read() & 8);
}

void horocomp_state::write_spk(u8 data)
{
	// SPK: speaker out
	m_speaker->level_w(data);
}

// inputs

/* physical button layout and labels are like this:

              CAP.
       AQU.   [ ]   SAG.
         [ ]   9   [ ]
  PIS.             8     SCO.
     [ ]              7[ ]

ARI.[ ]0               6[ ]LIB.

     [ ]1             5[ ]          [   ]   [   ]   [   ]   [   ]
  TAU.     2       4     VIR.       LOVE    CAREER  TRAVEL  MONEY
         [ ]   3   [ ]
       GEM.   [ ]   LEO             [   ]   [   ]   [   ]   [   ]
              CAN.                  FRIENDS SPIRIT  FAMILY  CREATIVE
*/

static INPUT_PORTS_START( horocomp )
	PORT_START("IN.0") // DIO0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_L) PORT_NAME("Love")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("Friends")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0 / Aries")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // DIO1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("Career")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Spirit")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1 / Taurus")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // DIO2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Travel")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Family")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2 / Gemini")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // DIO3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_M) PORT_NAME("Money")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("Creative")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3 / Cancer")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // DIO4
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4 / Leo")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("Aquarius / Reset")

	PORT_START("IN.5") // DIO5
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5 / Virgo")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_EQUALS) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("Pisces")

	PORT_START("IN.6") // DIO6
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6 / Libra")

	PORT_START("IN.7") // DIO7
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7 / Scorpio")

	PORT_START("IN.8") // DIO8
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8 / Sagittarius")

	PORT_START("IN.9") // DIO9
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9 / Capricorn")

	PORT_START("IN.10") // PI4
	PORT_CONFNAME( 0x08, 0x00, "Mode" )
	PORT_CONFSETTING(    0x08, "C" ) // compatibility
	PORT_CONFSETTING(    0x00, "P" ) // personal
INPUT_PORTS_END

// config

void horocomp_state::horocomp(machine_config &config)
{
	// basic machine hardware
	MM78LA(config, m_maincpu, 440000); // approximation - VC osc. R=10K
	m_maincpu->write_d().set(FUNC(horocomp_state::write_d));
	m_maincpu->write_r().set(FUNC(horocomp_state::write_r));
	m_maincpu->read_p().set(FUNC(horocomp_state::read_p));
	m_maincpu->write_spk().set(FUNC(horocomp_state::write_spk));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 14);
	m_display->set_segmask(0xff, 0x3fff);
	config.set_default_layout(layout_horocomp);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[4] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.125);
}

// roms

ROM_START( horocomp )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mm95_b9001-13", 0x0000, 0x0800, CRC(d284a837) SHA1(02092db2a29b4bb7e9f86286601b67b2e9556476) )

	ROM_REGION( 605, "maincpu:opla", 0 )
	ROM_LOAD( "mm78la_horocomp_output.pla", 0, 605, CRC(4fae532f) SHA1(3a08c0fa3ce476c014280b3cfeb6aa37824ae503) )
ROM_END





/*******************************************************************************

  Mattel World Championship Football (model 3202)
  * MM78 MCU (MM78L pinout) (label MM78 A78C6-12, die label A78C6)
  * MM78 MCU (MM78L pinout) (label MM78 A78C7-12, die label A78C7)
  * 8-digit 7seg VFD, cyan/red/green VFD Itron CP5023, 1-bit sound

  It was patented under US4422639. Like the Baseball counterpart (mwcbaseb in
  hh_hmcs40.cpp), this handheld is a complex game.

*******************************************************************************/

class mwcfootb_state : public hh_pps41_state
{
public:
	mwcfootb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pps41_state(mconfig, type, tag),
		m_subcpu(*this, "subcpu")
	{ }

	void mwcfootb(machine_config &config);

private:
	required_device<pps41_base_device> m_subcpu;

	void update_display();

	void main_write_d(u16 data);
	u16 main_read_d();
	void main_write_r(u16 data);
	u8 main_read_p();

	void sub_write_d(u16 data);
	void sub_write_r(u16 data);
};

// handlers

void mwcfootb_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

// maincpu side

void mwcfootb_state::main_write_d(u16 data)
{
	// DIO0-DIO7: vfd grid
	// DIO0-DIO2: input mux
	m_grid = m_inp_mux = data;
	update_display();

	// DIO8: subcpu INT0
	m_subcpu->set_input_line(0, (data & 0x100) ? ASSERT_LINE : CLEAR_LINE);
}

u16 mwcfootb_state::main_read_d()
{
	// DIO9: subcpu DIO9
	return m_subcpu->d_output_r() & 0x200;
}

void mwcfootb_state::main_write_r(u16 data)
{
	// RIO1-RIO7: vfd plate (7segs)
	m_plate = (m_plate & 0xfff00) | bitswap<7>(~data,2,3,1,0,6,5,4);
	update_display();

	// RIO8: speaker out
	m_speaker->level_w(BIT(~data, 7));
}

u8 mwcfootb_state::main_read_p()
{
	// PI1-PI8: multiplexed inputs
	return ~read_inputs(3);
}

// subcpu side

void mwcfootb_state::sub_write_d(u16 data)
{
	// DIO0-DIO3: vfd plate
	m_plate = (m_plate & 0x0ffff) | (data << 16 & 0xf0000);
	update_display();

	// DIO9: maincpu INT0 (+DIO9)
	m_maincpu->set_input_line(0, (data & 0x200) ? ASSERT_LINE : CLEAR_LINE);
}

void mwcfootb_state::sub_write_r(u16 data)
{
	// RIO1-RIO8: vfd plate
	m_plate = (m_plate & 0xf00ff) | (~data << 8 & 0xff00);
	update_display();
}

// inputs

/* physical button layout and labels are like this:

     (home team side)                                                      (visitor team side)
    [1] RECEIVERS [2]                                                       [1] RECEIVERS [2]

           [1]                                                                     [1]
    [4]  [PAUSE]  [2]                                                       [4]  [PAUSE]  [2]
           [3]                                                                     [3]
                           DOWN      QUARTER
         [ENTER]        YDS. TO GO  TIME LEFT        POSITION    SCORE           [ENTER]
    [KICK]     [TIME]     [    ]     [    ]           [    ]     [    ]     [TIME]     [KICK]

           [^]                                                                     [^]
    [<]   [P/C]   [>]                                                       [<]   [P/C]   [>]
           [v]                                                                     [v]
*/

static INPUT_PORTS_START( mwcfootb ) // P1 = left/home, P2 = right/visitor
	PORT_START("IN.0") // DIO0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START4 ) PORT_NAME("Score")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START3 ) PORT_NAME("Position")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 Kick")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 P/C/Pause")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_START2 ) PORT_NAME("Quarter Time Left")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Down / Yards To Go")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("P1 Kick")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("P1 P/C/Pause")

	PORT_START("IN.1") // DIO1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_PLAYER(2) PORT_NAME("P2 Receiver 2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("P2 Time")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("P2 Enter")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_PLAYER(2) PORT_NAME("P2 Receiver 1")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_PLAYER(1) PORT_NAME("P1 Receiver 2")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("P1 Time")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("P1 Enter")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_PLAYER(1) PORT_NAME("P1 Receiver 1")

	PORT_START("IN.2") // DIO2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_16WAY PORT_NAME("P2 Left/4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_16WAY PORT_NAME("P2 Right/2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_16WAY PORT_NAME("P2 Down/3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2) PORT_16WAY PORT_NAME("P2 Up/1")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_16WAY PORT_NAME("P1 Left/4")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_16WAY PORT_NAME("P1 Right/2")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_16WAY PORT_NAME("P1 Down/3")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_16WAY PORT_NAME("P1 Up/1")
INPUT_PORTS_END

// config

void mwcfootb_state::mwcfootb(machine_config &config)
{
	// basic machine hardware
	MM78(config, m_maincpu, 360000); // approximation - VC osc. R=56K
	m_maincpu->write_d().set(FUNC(mwcfootb_state::main_write_d));
	m_maincpu->read_d().set(FUNC(mwcfootb_state::main_read_d));
	m_maincpu->write_r().set(FUNC(mwcfootb_state::main_write_r));
	m_maincpu->read_p().set(FUNC(mwcfootb_state::main_read_p));
	m_maincpu->read_sdi().set(m_subcpu, FUNC(pps41_base_device::sdo_r));
	m_maincpu->write_ssc().set(m_subcpu, FUNC(pps41_base_device::ssc_w));

	MM78(config, m_subcpu, 360000); // osc. from maincpu
	m_subcpu->write_d().set(FUNC(mwcfootb_state::sub_write_d));
	m_subcpu->write_r().set(FUNC(mwcfootb_state::sub_write_r));
	m_subcpu->read_sdi().set(m_maincpu, FUNC(pps41_base_device::sdo_r));

	config.set_perfect_quantum(m_maincpu);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 571);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(8, 20);
	m_display->set_segmask(0x7f, 0x7f);
	config.set_default_layout(layout_mwcfootb);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( mwcfootb )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mm78_a78c6-12", 0x0000, 0x0800, CRC(91cf0d9b) SHA1(8d778b441eb26fcff50e8532c142f368c0dd5818) )

	ROM_REGION( 0x0800, "subcpu", 0 )
	ROM_LOAD( "mm78_a78c7-12", 0x0000, 0x0800, CRC(b991d06e) SHA1(1f801b5cd7214f7378ae3f19799b84a9dc5bba4e) )

	ROM_REGION( 248494, "screen", 0)
	ROM_LOAD( "mwcfootb.svg", 0, 248494, CRC(1000d65a) SHA1(22fddd8f5c1c75da90a6a911db5b2c967aeaa19e) )
ROM_END





/*******************************************************************************

  Selchow & Righter Scrabble Sensor
  * MM76EL MCU (label B8610-11, die label B8610)
  * 16 leds, 1-bit sound

  The game concept is similar to Mastermind. Enter a word (or press AUTO.)
  to start the game, then try to guess it.

*******************************************************************************/

class scrabsen_state : public hh_pps41_state
{
public:
	scrabsen_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pps41_state(mconfig, type, tag)
	{ }

	void scrabsen(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(players_switch) { update_int(); }

private:
	virtual void update_int() override;
	void update_display();
	void write_d(u16 data);
	void write_r(u16 data);
	u8 read_p();
};

// handlers

void scrabsen_state::update_int()
{
	// players switch is tied to MCU INT0
	m_maincpu->set_input_line(0, (m_inputs[5]->read() & 1) ? ASSERT_LINE : CLEAR_LINE);
}

void scrabsen_state::update_display()
{
	m_display->matrix(m_inp_mux >> 6 & 3, ~m_r);
}

void scrabsen_state::write_d(u16 data)
{
	// DIO0-DIO4: input mux
	// DIO6,DIO7: led select
	m_inp_mux = data;
	update_display();

	// DIO8: speaker out
	m_speaker->level_w(BIT(data, 8));
}

void scrabsen_state::write_r(u16 data)
{
	// RIO1-RIO8: led data
	m_r = data;
	update_display();
}

u8 scrabsen_state::read_p()
{
	// PI1-PI7: multiplexed inputs
	return ~read_inputs(5);
}

// inputs

static INPUT_PORTS_START( scrabsen )
	PORT_START("IN.0") // DIO0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_NAME("Clear")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')

	PORT_START("IN.1") // DIO1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') PORT_NAME("Space")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')

	PORT_START("IN.2") // DIO2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13) PORT_NAME("Enter")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')

	PORT_START("IN.3") // DIO3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_NAME("Auto.")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')

	PORT_START("IN.4") // DIO4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')

	PORT_START("IN.5") // INT0
	PORT_CONFNAME( 0x01, 0x01, "Players" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(scrabsen_state::players_switch), 0)
	PORT_CONFSETTING(    0x01, "1" ) // single
	PORT_CONFSETTING(    0x00, "2" ) // double
INPUT_PORTS_END

// config

void scrabsen_state::scrabsen(machine_config &config)
{
	// basic machine hardware
	MM76EL(config, m_maincpu, 380000); // approximation - VC osc. R=56K
	m_maincpu->write_d().set(FUNC(scrabsen_state::write_d));
	m_maincpu->write_r().set(FUNC(scrabsen_state::write_r));
	m_maincpu->read_p().set(FUNC(scrabsen_state::read_p));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(2, 8);
	config.set_default_layout(layout_scrabsen);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( scrabsen )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "b8610-11", 0x0000, 0x0400, CRC(97c8a466) SHA1(ed5d2cddd2761ed6e3ddc47d97b2ed19a2aaeee9) )

	ROM_REGION( 314, "maincpu:opla", 0 ) // unused
	ROM_LOAD( "mm76_scrabsen_output.pla", 0, 314, CRC(410fa6d7) SHA1(d46aaf1ec2c942083cba7dbd59d4261dc238d4c8) )
ROM_END





/*******************************************************************************

  Selchow & Righter Reader's Digest Q&A
  * MM76EL MCU (label MM76EL B8654-11, die label B8654)
  * 9-digit 7seg LED display(4 unused), 2-bit sound

  The game requires question books. The player inputs a 3-digit code and
  answers 20 multiple-choice questions from the page.

*******************************************************************************/

class rdqa_state : public hh_pps41_state
{
public:
	rdqa_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_pps41_state(mconfig, type, tag)
	{ }

	void rdqa(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(players_switch) { update_int(); }

private:
	virtual void update_int() override;
	void update_display();
	void write_d(u16 data);
	void write_r(u16 data);
	u8 read_p();
};

// handlers

void rdqa_state::update_int()
{
	// players switch is tied to MCU INT0
	m_maincpu->set_input_line(0, (m_inputs[4]->read() & 1) ? ASSERT_LINE : CLEAR_LINE);
}

void rdqa_state::update_display()
{
	m_display->matrix(m_inp_mux, ~m_r);
}

void rdqa_state::write_d(u16 data)
{
	// DIO0-DIO4: digit select
	// DIO0-DIO3: input mux
	m_inp_mux = data;
	update_display();

	// DIO8,DIO9: speaker out
	m_speaker->level_w(data >> 8 & 3);
}

void rdqa_state::write_r(u16 data)
{
	// RIO1-RIO7: digit segment data
	m_r = data;
	update_display();
}

u8 rdqa_state::read_p()
{
	// PI1-PI5: multiplexed inputs
	return ~read_inputs(4);
}

// inputs

static INPUT_PORTS_START( rdqa )
	PORT_START("IN.0") // DIO0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_NAME("4")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_NAME("5")

	PORT_START("IN.1") // DIO1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_NAME("6")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_NAME("7")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_NAME("8")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_NAME("9")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_NAME("0")

	PORT_START("IN.2") // DIO2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_NAME("B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("C")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("D")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Score")

	PORT_START("IN.3") // DIO3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("Right")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("Wrong")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_P) PORT_NAME("Pass")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Clear")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")

	PORT_START("IN.4") // INT0
	PORT_CONFNAME( 0x01, 0x01, "Players" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(rdqa_state::players_switch), 0)
	PORT_CONFSETTING(    0x01, "1" ) // single
	PORT_CONFSETTING(    0x00, "2" ) // double
INPUT_PORTS_END

// config

void rdqa_state::rdqa(machine_config &config)
{
	// basic machine hardware
	MM76EL(config, m_maincpu, 400000); // approximation - VC osc. R=56K
	m_maincpu->write_d().set(FUNC(rdqa_state::write_d));
	m_maincpu->write_r().set(FUNC(rdqa_state::write_r));
	m_maincpu->read_p().set(FUNC(rdqa_state::read_p));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(5, 7);
	m_display->set_segmask(0x1f, 0x7f);
	config.set_default_layout(layout_rdqa);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[4] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.125);
}

// roms

ROM_START( rdqa )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mm76el_b8654-11", 0x0000, 0x0400, CRC(95c00eee) SHA1(1537626df03a7131d83a555e557a4528e093a22a) )

	ROM_REGION( 314, "maincpu:opla", 0 )
	ROM_LOAD( "mm76_rdqa_output.pla", 0, 314, CRC(e024b2d3) SHA1(fc3121e70f22151cf8f3411f9fcbac88002ae330) )
ROM_END



} // anonymous namespace

/*******************************************************************************

  Game driver(s)

*******************************************************************************/

//    YEAR  NAME       PARENT   COMPAT  MACHINE    INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1980, addocalc,  0,       0,      addocalc,  addocalc, addocalc_state, empty_init, "Addometer Company", "Addometer Calculator", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )

SYST( 1979, ftri1,     0,       0,      ftri1,     ftri1,    ftri1_state,    empty_init, "Fonas", "Tri-1 (Fonas)", MACHINE_SUPPORTS_SAVE )

SYST( 1979, mastmind,  0,       0,      mastmind,  mastmind, mastmind_state, empty_init, "Invicta", "Electronic Master Mind (Invicta)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1979, smastmind, 0,       0,      smastmind, mastmind, mastmind_state, empty_init, "Invicta", "Super-Sonic Electronic Master Mind", MACHINE_SUPPORTS_SAVE )

SYST( 1979, dunksunk,  0,       0,      dunksunk,  dunksunk, dunksunk_state, empty_init, "Kmart Corporation", "Dunk 'n Sunk", MACHINE_SUPPORTS_SAVE )

SYST( 1978, memoquiz,  0,       0,      memoquiz,  memoquiz, memoquiz_state, empty_init, "M.E.M. Belgium", "Memoquiz", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )

SYST( 1978, mfootb2,   0,       0,      mfootb2,   mfootb2,  mfootb2_state,  empty_init, "Mattel Electronics", "Football 2 (Mattel)", MACHINE_SUPPORTS_SAVE )
SYST( 1979, brainbaf,  0,       0,      brainbaf,  brainbaf, brainbaf_state, empty_init, "Mattel Electronics", "Brain Baffler", MACHINE_SUPPORTS_SAVE )
SYST( 1979, horocomp,  0,       0,      horocomp,  horocomp, horocomp_state, empty_init, "Mattel Electronics", "Horoscope Computer", MACHINE_SUPPORTS_SAVE )
SYST( 1980, mwcfootb,  0,       0,      mwcfootb,  mwcfootb, mwcfootb_state, empty_init, "Mattel Electronics", "World Championship Football", MACHINE_SUPPORTS_SAVE )

SYST( 1978, scrabsen,  0,       0,      scrabsen,  scrabsen, scrabsen_state, empty_init, "Selchow & Righter", "Scrabble Sensor: Electronic Word Game", MACHINE_SUPPORTS_SAVE )
SYST( 1980, rdqa,      0,       0,      rdqa,      rdqa,     rdqa_state,     empty_init, "Selchow & Righter", "Reader's Digest Q&A: Computer Question & Answer Game", MACHINE_SUPPORTS_SAVE ) // ***

// ***: As far as MAME is concerned, the game is emulated fine. But for it to be playable, it requires interaction
// with other, unemulatable, things eg. game board/pieces, book, playing cards, pen & paper, etc.



hh_rw5000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Rockwell A/B5000 MCU series handhelds (before PPS-4/1)
Mostly calculators on these MCUs, but also Mattel's first couple of handhelds.

ROM source notes when dumped from another title, but confident it's the same:
- rw18r: Rockwell 8R
- rw24k: Rockwell 14RD-II
- misatk: Mattel Space Alert

*******************************************************************************/

#include "emu.h"

#include "cpu/rw5000/a5000.h"
#include "cpu/rw5000/a5500.h"
#include "cpu/rw5000/a5900.h"
#include "cpu/rw5000/b5000.h"
#include "cpu/rw5000/b5500.h"
#include "cpu/rw5000/b6000.h"
#include "cpu/rw5000/b6100.h"
#include "sound/spkrdev.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "autorace.lh"
#include "gravity.lh"
#include "mbaseb.lh"
#include "mfootb.lh"
#include "misatk.lh"
#include "rw10r.lh"
#include "rw24k.lh"
#include "rw30r.lh"

//#include "hh_rw5000_test.lh" // common test-layout - use external artwork


namespace {

class hh_rw5000_state : public driver_device
{
public:
	hh_rw5000_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_speaker(*this, "speaker"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(power_button);
	template<int Sel> DECLARE_INPUT_CHANGED_MEMBER(switch_next) { if (newval) switch_change(Sel, param, true); }
	template<int Sel> DECLARE_INPUT_CHANGED_MEMBER(switch_prev) { if (newval) switch_change(Sel, param, false); }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// devices
	required_device<rw5000_base_device> m_maincpu;
	optional_device<pwm_display_device> m_display;
	optional_device<speaker_sound_device> m_speaker;
	optional_ioport_array<9> m_inputs; // max 9

	u16 m_inp_mux = 0;

	// MCU output pin state
	u16 m_str = 0;
	u16 m_seg = 0;

	u8 read_inputs(int columns);
	void switch_change(int sel, u32 mask, bool next);
};


// machine start/reset

void hh_rw5000_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_str));
	save_item(NAME(m_seg));
}

void hh_rw5000_state::machine_reset()
{
}



/*******************************************************************************

  Helper Functions

*******************************************************************************/

// generic input handlers

u8 hh_rw5000_state::read_inputs(int columns)
{
	u8 ret = 0;

	// read selected input rows
	for (int i = 0; i < columns; i++)
		if (BIT(m_inp_mux, i))
			ret |= m_inputs[i]->read();

	return ret;
}

void hh_rw5000_state::switch_change(int sel, u32 mask, bool next)
{
	// config switches (for direct control)
	ioport_field *inp = m_inputs[sel]->field(mask);

	if (next && inp->has_next_setting())
		inp->select_next_setting();
	else if (!next && inp->has_previous_setting())
		inp->select_previous_setting();
}

INPUT_CHANGED_MEMBER(hh_rw5000_state::power_button)
{
	// power button or switch
	bool power = (param) ? (bool(param - 1)) : !newval;

	if (!power && m_display != nullptr)
		m_display->clear();
	m_maincpu->set_input_line(INPUT_LINE_RESET, power ? CLEAR_LINE : ASSERT_LINE);
}



/*******************************************************************************

  Minidrivers (subclass, I/O, Inputs, Machine Config, ROM Defs)

*******************************************************************************/

/*******************************************************************************

  Mattel Auto Race (model 9879)
  * B6000 MCU (label B6000CA, die label B6000-B)
  * 2-digit 7seg display, 21 other leds, 1-bit sound

  This is Mattel's first electronic handheld game, also the first CPU-based
  handheld game overall. Hardware design (even the MCU) and programming
  was done at Rockwell.

  A European version was released as "Ski Slalom" (model 8290), except it's
  upside-down.

*******************************************************************************/

class autorace_state : public hh_rw5000_state
{
public:
	autorace_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_rw5000_state(mconfig, type, tag)
	{ }

	void autorace(machine_config &config);

private:
	void write_str(u16 data);
	void write_seg(u16 data);
};

// handlers

void autorace_state::write_str(u16 data)
{
	m_display->write_my(data);
}

void autorace_state::write_seg(u16 data)
{
	m_display->write_mx(data);
}

// inputs

static INPUT_PORTS_START( autorace )
	PORT_START("IN.0") // KB
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) // does not auto-center on real device
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) // "
	PORT_CONFNAME( 0x0c, 0x0c, "Gear" )
	PORT_CONFSETTING(    0x0c, "1" )
	PORT_CONFSETTING(    0x04, "2" )
	PORT_CONFSETTING(    0x00, "3" )
	PORT_CONFSETTING(    0x08, "4" )

	PORT_START("IN.1") // DIN
	PORT_CONFNAME( 0x01, 0x00, "Factory Test" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )

	PORT_START("POWER") // power switch
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_rw5000_state::power_button), 0) PORT_NAME("Start / Reset")

	PORT_START("SWITCH") // fake
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_rw5000_state::switch_prev<0>), 0x0c) PORT_NAME("Gear Switch Down")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_rw5000_state::switch_next<0>), 0x0c) PORT_NAME("Gear Switch Up")
INPUT_PORTS_END

// config

void autorace_state::autorace(machine_config &config)
{
	// basic machine hardware
	B6000(config, m_maincpu, 160000); // approximation
	m_maincpu->write_str().set(FUNC(autorace_state::write_str));
	m_maincpu->write_seg().set(FUNC(autorace_state::write_seg));
	m_maincpu->read_kb().set_ioport("IN.0");
	m_maincpu->read_din().set_ioport("IN.1");
	m_maincpu->write_spk().set(m_speaker, FUNC(speaker_sound_device::level_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	m_display->set_segmask(0x180, 0x7f);
	m_display->set_bri_levels(0.02, 0.2); // player led is brighter
	config.set_default_layout(layout_autorace);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( autorace )
	ROM_REGION( 0x200, "maincpu", 0 )
	ROM_LOAD( "b6000ca", 0x000, 0x200, CRC(a112c928) SHA1(aa5d0b46a08e2460081d4148cf254dc5ec53817e) )
ROM_END





/*******************************************************************************

  Mattel Missile Attack (model 2048) / Space Alert (model 2448)
  * B6000 MCU (label B6001CA/EA, die label B6001)
  * 2-digit 7seg display, 21 other leds, 1-bit sound

  The initial release was called Missile Attack, it didn't sell well (Mattel
  blamed it on NBC for refusing to air their commercial). They changed the
  title/setting and rereleased it as "Space Alert" (aka "Battlestar Galactica:
  Space Alert"). In 1980, they advertised another rerelease, this time as
  "Flash Gordon", but that didn't come out.

*******************************************************************************/

class misatk_state : public hh_rw5000_state
{
public:
	misatk_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_rw5000_state(mconfig, type, tag)
	{ }

	void misatk(machine_config &config);

private:
	void write_str(u16 data);
	void write_seg(u16 data);
};

// handlers

void misatk_state::write_str(u16 data)
{
	m_display->write_my(data);
}

void misatk_state::write_seg(u16 data)
{
	m_display->write_mx(data);
}

// inputs

static INPUT_PORTS_START( misatk )
	PORT_START("IN.0") // KB
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) // does not auto-center on real device
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) // "
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 )

	PORT_START("IN.1") // DIN
	PORT_CONFNAME( 0x01, 0x00, "Factory Test" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )

	PORT_START("POWER") // power switch
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_rw5000_state::power_button), 0) PORT_NAME("Arm / Off")
INPUT_PORTS_END

// config

void misatk_state::misatk(machine_config &config)
{
	// basic machine hardware
	B6000(config, m_maincpu, 250000); // approximation
	m_maincpu->write_str().set(FUNC(misatk_state::write_str));
	m_maincpu->write_seg().set(FUNC(misatk_state::write_seg));
	m_maincpu->read_kb().set_ioport("IN.0");
	m_maincpu->read_din().set_ioport("IN.1");
	m_maincpu->write_spk().set(m_speaker, FUNC(speaker_sound_device::level_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	m_display->set_segmask(0x180, 0x7f);
	m_display->set_bri_levels(0.015, 0.15); // player led is brighter
	config.set_default_layout(layout_misatk);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( misatk )
	ROM_REGION( 0x200, "maincpu", 0 )
	ROM_LOAD( "b6001ea", 0x000, 0x200, CRC(56564b79) SHA1(6f33f57ea312cb2018fb59f72eaff3a9642e74a2) )
ROM_END





/*******************************************************************************

  Mattel Football (model 2024)
  * B6100 MCU (label B6100EB/-15, die label B6100 A)
  * 7-digit 7seg display, 27 other leds, 1-bit sound

  When Football II came out, they renamed this one to Football I.

*******************************************************************************/

class mfootb_state : public hh_rw5000_state
{
public:
	mfootb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_rw5000_state(mconfig, type, tag)
	{ }

	void mfootb(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(score_button) { update_display(); }

private:
	void update_display();
	void write_str(u16 data);
	void write_seg(u16 data);
};

// handlers

void mfootb_state::update_display()
{
	// 4th digit DP is from the SCORE button
	u8 dp = (m_inputs[1]->read() & 2) ? 0x80 : 0;
	m_display->matrix(m_str, (m_seg << 1 & 0x700) | dp | (m_seg & 0x7f));
}

void mfootb_state::write_str(u16 data)
{
	m_str = data;
	update_display();
}

void mfootb_state::write_seg(u16 data)
{
	m_seg = data;
	update_display();
}

// inputs

static INPUT_PORTS_START( mfootb )
	PORT_START("IN.0") // KB
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Forward")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Kick")

	PORT_START("IN.1") // DIN
	PORT_CONFNAME( 0x01, 0x01, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x01, "1" ) // PRO 1
	PORT_CONFSETTING(    0x00, "2" ) // PRO 2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_NAME("Score") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mfootb_state::score_button), 0)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Status")
	PORT_CONFNAME( 0x08, 0x00, "Factory Test" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x08, DEF_STR( On ) )
INPUT_PORTS_END

// config

void mfootb_state::mfootb(machine_config &config)
{
	// basic machine hardware
	B6100(config, m_maincpu, 280000); // approximation
	m_maincpu->write_str().set(FUNC(mfootb_state::write_str));
	m_maincpu->write_seg().set(FUNC(mfootb_state::write_seg));
	m_maincpu->read_kb().set_ioport("IN.0");
	m_maincpu->read_din().set_ioport("IN.1");
	m_maincpu->write_spk().set(m_speaker, FUNC(speaker_sound_device::level_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 11);
	m_display->set_segmask(0x7f, 0x7f);
	m_display->set_segmask(0x08, 0xff); // only one digit has DP
	m_display->set_bri_levels(0.02, 0.2); // player led is brighter
	config.set_default_layout(layout_mfootb);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( mfootb )
	ROM_REGION( 0x400, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "b6100eb", 0x000, 0x300, CRC(5b27620f) SHA1(667ff6cabced89ef4ad848b73d66a06526edc5e6) )
	ROM_CONTINUE(        0x380, 0x080 )
ROM_END





/*******************************************************************************

  Mattel Baseball (model 2942)
  * B6100 MCU (label B6101-12, die label B6101 A)
  * 4-digit 7seg display, 28 other leds, 1-bit sound

*******************************************************************************/

class mbaseb_state : public hh_rw5000_state
{
public:
	mbaseb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_rw5000_state(mconfig, type, tag)
	{ }

	void mbaseb(machine_config &config);

private:
	void write_str(u16 data);
	void write_seg(u16 data);
};

// handlers

void mbaseb_state::write_str(u16 data)
{
	m_display->write_my(data);
}

void mbaseb_state::write_seg(u16 data)
{
	m_display->write_mx(bitswap<10>(data,7,8,9,6,5,4,3,2,1,0));
}

// inputs

static INPUT_PORTS_START( mbaseb )
	PORT_START("IN.0") // KB
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Pitch")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Hit")

	PORT_START("IN.1") // DIN
	PORT_CONFNAME( 0x01, 0x01, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x01, "1" ) // PRO 1
	PORT_CONFSETTING(    0x00, "2" ) // PRO 2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Score")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Run")
	PORT_CONFNAME( 0x08, 0x00, "Factory Test" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x08, DEF_STR( On ) )
INPUT_PORTS_END

// config

void mbaseb_state::mbaseb(machine_config &config)
{
	// basic machine hardware
	B6100(config, m_maincpu, 280000); // approximation
	m_maincpu->write_str().set(FUNC(mbaseb_state::write_str));
	m_maincpu->write_seg().set(FUNC(mbaseb_state::write_seg));
	m_maincpu->read_kb().set_ioport("IN.0");
	m_maincpu->read_din().set_ioport("IN.1");
	m_maincpu->write_spk().set(m_speaker, FUNC(speaker_sound_device::level_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 10);
	m_display->set_segmask(0x170, 0x7f);
	m_display->set_segmask(0x110, 0xff); // 2 digits have DP
	config.set_default_layout(layout_mbaseb);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( mbaseb )
	ROM_REGION( 0x400, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "b6101-12", 0x000, 0x300, CRC(7720ddcc) SHA1(cd43126db7c6262f659f325aa58420e1a8a1a659) )
	ROM_CONTINUE(         0x380, 0x080 )
ROM_END





/*******************************************************************************

  Mattel Gravity (model 8291)
  * B6100 MCU (label B6102-11, die label B6102 A)
  * 3-digit 7seg display, 27 other leds, 1-bit sound

  It was advertised as "Catastrophe", but went unreleased. It got sold later
  as "Gravity", with a less catastrophic setting.

  The game is basically 3 mini games in 1:
  - Juggling (Rumbling Rocks in Catastrophe)
  - Coin Drop (Quake Shock in Catastrophe)
  - Docking (Meteorite Shower in Catstrophe)

*******************************************************************************/

class gravity_state : public hh_rw5000_state
{
public:
	gravity_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_rw5000_state(mconfig, type, tag)
	{ }

	void gravity(machine_config &config);

private:
	void write_str(u16 data);
	void write_seg(u16 data);
};

// handlers

void gravity_state::write_str(u16 data)
{
	m_display->write_my(data);
}

void gravity_state::write_seg(u16 data)
{
	m_display->write_mx(data);
}

// inputs

static INPUT_PORTS_START( gravity )
	PORT_START("IN.0") // KB
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // DIN
	PORT_CONFNAME( 0x08, 0x00, "Factory Test" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x08, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gravity_state::gravity(machine_config &config)
{
	// basic machine hardware
	B6100(config, m_maincpu, 250000); // approximation
	m_maincpu->write_str().set(FUNC(gravity_state::write_str));
	m_maincpu->write_seg().set(FUNC(gravity_state::write_seg));
	m_maincpu->read_kb().set_ioport("IN.0");
	m_maincpu->read_din().set_ioport("IN.1");
	m_maincpu->write_spk().set(m_speaker, FUNC(speaker_sound_device::level_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 10);
	m_display->set_segmask(0x1c0, 0x7f);
	config.set_default_layout(layout_gravity);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( gravity )
	ROM_REGION( 0x400, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "b6102-11", 0x000, 0x300, CRC(532f332e) SHA1(593224d3a2a5b6022f0d73b6e6fb9093d2d54f68) )
	ROM_CONTINUE(         0x380, 0x080 )
ROM_END





/*******************************************************************************

  Rockwell 10R
  * A5000 MCU (label A5000PA, die label A5000)
  * 8-digit 7seg LED display

  Rockwell 12R "Square Root"
  * A5000 MCU (label A5001, die label A5001)
  * rest is same as 10R

  12R supports square root by pressing × after ÷ (or the other way around).

*******************************************************************************/

class rw10r_state : public hh_rw5000_state
{
public:
	rw10r_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_rw5000_state(mconfig, type, tag)
	{ }

	void rw10r(machine_config &config);

private:
	void write_str(u16 data);
	void write_seg(u16 data);
	u8 read_kb();
};

// handlers

void rw10r_state::write_str(u16 data)
{
	// STR0-STR7: digit select
	// STR4-STR7: input mux
	m_display->write_my(data);
	m_inp_mux = data >> 4;
}

void rw10r_state::write_seg(u16 data)
{
	// SEG0-SEG7: digit segment data
	m_display->write_mx(bitswap<8>(data,0,7,6,5,4,3,2,1));
}

u8 rw10r_state::read_kb()
{
	// KB: multiplexed inputs
	return read_inputs(4);
}

// inputs

static INPUT_PORTS_START( rw10r )
	PORT_START("IN.0") // STR4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE/C")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("+=")

	PORT_START("IN.1") // STR5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")

	PORT_START("IN.2") // STR6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")

	PORT_START("IN.3") // STR7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")
INPUT_PORTS_END

// config

void rw10r_state::rw10r(machine_config &config)
{
	// basic machine hardware
	A5000(config, m_maincpu, 250000); // approximation
	m_maincpu->write_str().set(FUNC(rw10r_state::write_str));
	m_maincpu->write_seg().set(FUNC(rw10r_state::write_seg));
	m_maincpu->read_kb().set(FUNC(rw10r_state::read_kb));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 8);
	m_display->set_segmask(0xff, 0xff);
	config.set_default_layout(layout_rw10r);
}

// roms

ROM_START( rw10r )
	ROM_REGION( 0x200, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "a5000pa", 0x000, 0x0c0, CRC(db6cbbea) SHA1(3757f303a84e412e8763c4ec2170c9badc172454) )
	ROM_CONTINUE(        0x100, 0x100 )
ROM_END

ROM_START( rw12r )
	ROM_REGION( 0x200, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "a5001", 0x000, 0x0c0, CRC(4337a46b) SHA1(70f4683eac0afa31a6cd9dd44fdf1b6680575f67) )
	ROM_CONTINUE(      0x100, 0x100 )
ROM_END





/*******************************************************************************

  Rockwell 8R "Automatic Percent", Rockwell 18R "Memory"
  * B5000 MCU (label B5000CB/CC, die label B5000)
  * 8-digit 7seg LED display

  This MCU was used in Rockwell 8R, 18R, and 9TR. It was also sold by
  Tandy (Radio Shack) as EC-220.

  8R/9TR doesn't have the memory store/recall buttons.

*******************************************************************************/

class rw18r_state : public hh_rw5000_state
{
public:
	rw18r_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_rw5000_state(mconfig, type, tag)
	{ }

	void rw18r(machine_config &config);

private:
	void write_str(u16 data);
	void write_seg(u16 data);
	u8 read_kb();
};

// handlers

void rw18r_state::write_str(u16 data)
{
	// STR0-STR7: digit select
	// STR4-STR8: input mux
	m_display->write_my(data);
	m_inp_mux = data >> 4;
}

void rw18r_state::write_seg(u16 data)
{
	// SEG0-SEG7: digit segment data
	m_display->write_mx(bitswap<8>(data,0,7,6,5,4,3,2,1));
}

u8 rw18r_state::read_kb()
{
	// KB: multiplexed inputs
	return read_inputs(5);
}

// inputs

static INPUT_PORTS_START( rw18r )
	PORT_START("IN.0") // STR4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE/C")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")

	PORT_START("IN.1") // STR5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")

	PORT_START("IN.2") // STR6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")

	PORT_START("IN.3") // STR7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")

	PORT_START("IN.4") // STR8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("STO") // unpopulated on 8R
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("RCL") // "
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("%")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")
INPUT_PORTS_END

// config

void rw18r_state::rw18r(machine_config &config)
{
	// basic machine hardware
	B5000(config, m_maincpu, 250000); // approximation
	m_maincpu->write_str().set(FUNC(rw18r_state::write_str));
	m_maincpu->write_seg().set(FUNC(rw18r_state::write_seg));
	m_maincpu->read_kb().set(FUNC(rw18r_state::read_kb));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 8);
	m_display->set_segmask(0xff, 0xff);
	config.set_default_layout(layout_rw10r);
}

// roms

ROM_START( rw18r )
	ROM_REGION( 0x200, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "b5000cc", 0x000, 0x0c0, CRC(ace32614) SHA1(23cf11acf2e73ce2dfc165cb87f86fab15f69ff7) )
	ROM_CONTINUE(        0x100, 0x100 )
ROM_END





/*******************************************************************************

  Rockwell 30R "Slide Rule Memory"
  * B5500 MCU (label B5500PA, die label B5500)
  * 9-digit 7seg LED display

  Rockwell 31R "Slide Rule Memory"
  * A5500 MCU (label A5502PA, die label A5500)
  * rest is same as 30R

  30R and 31R have the exact same functionality, even though they are on
  different MCUs. There's also a 30R version on an A4600 MCU.

*******************************************************************************/

class rw30r_state : public hh_rw5000_state
{
public:
	rw30r_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_rw5000_state(mconfig, type, tag)
	{ }

	void rw30r(machine_config &config);
	void rw31r(machine_config &config);

private:
	void write_str(u16 data);
	void write_seg(u16 data);
	u8 read_kb();
};

// handlers

void rw30r_state::write_str(u16 data)
{
	// STR0-STR8: digit select
	// STR4-STR8: input mux
	m_display->write_my(data);
	m_inp_mux = data >> 4;
}

void rw30r_state::write_seg(u16 data)
{
	// SEG0-SEG7: digit segment data
	m_display->write_mx(bitswap<8>(data,0,7,6,5,4,3,2,1));
}

u8 rw30r_state::read_kb()
{
	// KB: multiplexed inputs
	return read_inputs(5);
}

// inputs

static INPUT_PORTS_START( rw30r )
	PORT_START("IN.0") // STR4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(". / +/-")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("F / CF") // function
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+ / M+")

	PORT_START("IN.1") // STR5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("- / M-")

	PORT_START("IN.2") // STR6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"× / x²")

	PORT_START("IN.3") // STR7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷ / 1/x")

	PORT_START("IN.4") // STR8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("C / MC")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME(u8"X\u2194Y / X\u2194M") // U+2194 = ↔
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME(u8"% / u221ax") // U+221A = √
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("= / MR")
INPUT_PORTS_END

// config

void rw30r_state::rw30r(machine_config &config)
{
	// basic machine hardware
	B5500(config, m_maincpu, 250000); // approximation
	m_maincpu->write_str().set(FUNC(rw30r_state::write_str));
	m_maincpu->write_seg().set(FUNC(rw30r_state::write_seg));
	m_maincpu->read_kb().set(FUNC(rw30r_state::read_kb));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	m_display->set_segmask(0x1ff, 0xff);
	config.set_default_layout(layout_rw30r);
}

void rw30r_state::rw31r(machine_config &config)
{
	rw30r(config);

	// basic machine hardware
	A5500(config.replace(), m_maincpu, 250000); // approximation
	m_maincpu->write_str().set(FUNC(rw30r_state::write_str));
	m_maincpu->write_seg().set(FUNC(rw30r_state::write_seg));
	m_maincpu->read_kb().set(FUNC(rw30r_state::read_kb));
}

// roms

ROM_START( rw30r )
	ROM_REGION( 0x400, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "b5500pa", 0x000, 0x280, CRC(937dd695) SHA1(bc286209943f49e9acccbf319b87c6ec24386894) )
	ROM_CONTINUE(        0x380, 0x080 )
ROM_END

ROM_START( rw31r )
	ROM_REGION( 0x400, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "a5502pa", 0x000, 0x280, CRC(6ee0b30a) SHA1(42e5d1e29bdef4b4faaafee3592cacc0e66b982f) )
	ROM_CONTINUE(        0x380, 0x080 )
ROM_END





/*******************************************************************************

  Rockwell 24K aka "the 24K" (see below for more)
  * A5900 MCU (label A5901CA/A5903CB, die label A59__)
  * 9-digit 7seg VFD

  This MCU was used in Rockwell 14RD-II, 24RD-II, 24K, 24K II, 24MS, and
  probably also in 14RD, 24RD, 22K.

  14RD has 6 less buttons than 24RD, the non-II versions have LED(s) instead
  of a 9th digit for minus sign and memory status. 24K has an extra button
  for register exchange. The difference between 24K and 24K II is unknown.

*******************************************************************************/

class rw24k_state : public hh_rw5000_state
{
public:
	rw24k_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_rw5000_state(mconfig, type, tag)
	{ }

	void rw24k(machine_config &config);

private:
	void write_str(u16 data);
	void write_seg(u16 data);
	u8 read_kb();
};

// handlers

void rw24k_state::write_str(u16 data)
{
	// STR0-STR8: digit select, input mux
	m_display->write_my(data);
	m_inp_mux = data;
}

void rw24k_state::write_seg(u16 data)
{
	// SEG0-SEG7: digit segment data
	m_display->write_mx(bitswap<8>(data,0,7,6,5,4,3,2,1));
}

u8 rw24k_state::read_kb()
{
	// KB: multiplexed inputs
	return read_inputs(9);
}

// inputs

static INPUT_PORTS_START( rw24k )
	PORT_START("IN.0") // STR0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("+/-") // unpopulated on 14RD
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_V) PORT_NAME("M+") // "

	PORT_START("IN.1") // STR1
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("M-") // "
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // STR2
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // STR3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Z) PORT_NAME("MC") // "
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME(u8"\u221a") // " - U+221A = √
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME("MR") // "

	PORT_START("IN.4") // STR4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")

	PORT_START("IN.5") // STR5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")

	PORT_START("IN.6") // STR6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")

	PORT_START("IN.7") // STR7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")

	PORT_START("IN.8") // STR8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE/C")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME(u8"\u2194") // register exchange - unpopulated on 14RD/24RD - U+2194 = ↔
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("%")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")
INPUT_PORTS_END

// config

void rw24k_state::rw24k(machine_config &config)
{
	// basic machine hardware
	A5900(config, m_maincpu, 250000); // approximation
	m_maincpu->write_str().set(FUNC(rw24k_state::write_str));
	m_maincpu->write_seg().set(FUNC(rw24k_state::write_seg));
	m_maincpu->read_kb().set(FUNC(rw24k_state::read_kb));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	m_display->set_segmask(0x1ff, 0xff);
	config.set_default_layout(layout_rw24k);
}

// roms

ROM_START( rw24k )
	ROM_REGION( 0x200, "maincpu", 0 )
	ROM_LOAD( "a5901ca", 0x000, 0x200, CRC(00de7764) SHA1(0f24add4b6d2660aad63ddd4d0003d59a0e39df6) )
ROM_END



} // anonymous namespace

/*******************************************************************************

  Game driver(s)

*******************************************************************************/

//    YEAR  NAME       PARENT   COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY, FULLNAME, FLAGS
SYST( 1976, autorace,  0,       0,      autorace,  autorace,  autorace_state,  empty_init, "Mattel Electronics", "Auto Race", MACHINE_SUPPORTS_SAVE )
SYST( 1977, misatk,    0,       0,      misatk,    misatk,    misatk_state,    empty_init, "Mattel Electronics", "Missile Attack / Space Alert", MACHINE_SUPPORTS_SAVE )
SYST( 1977, mfootb,    0,       0,      mfootb,    mfootb,    mfootb_state,    empty_init, "Mattel Electronics", "Football (Mattel)", MACHINE_SUPPORTS_SAVE )
SYST( 1978, mbaseb,    0,       0,      mbaseb,    mbaseb,    mbaseb_state,    empty_init, "Mattel Electronics", "Baseball (Mattel)", MACHINE_SUPPORTS_SAVE )
SYST( 1980, gravity,   0,       0,      gravity,   gravity,   gravity_state,   empty_init, "Mattel Electronics", "Gravity (Mattel)", MACHINE_SUPPORTS_SAVE )

SYST( 1974, rw10r,     0,       0,      rw10r,     rw10r,     rw10r_state,     empty_init, "Rockwell", "10R (Rockwell)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1975, rw12r,     0,       0,      rw10r,     rw10r,     rw10r_state,     empty_init, "Rockwell", "12R: Square Root", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1975, rw18r,     0,       0,      rw18r,     rw18r,     rw18r_state,     empty_init, "Rockwell", "18R: Memory", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1974, rw30r,     0,       0,      rw30r,     rw30r,     rw30r_state,     empty_init, "Rockwell", "30R: Slide Rule Memory (B5500 version)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1975, rw31r,     rw30r,   0,      rw31r,     rw30r,     rw30r_state,     empty_init, "Rockwell", "31R: Slide Rule Memory", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1976, rw24k,     0,       0,      rw24k,     rw24k,     rw24k_state,     empty_init, "Rockwell", "24K (Rockwell)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )



hh_sm510.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap, Henrik Algestam
// thanks-to:Sean Riddle, Igor, Lee Robson, Milan Galcik
/*******************************************************************************

Sharp SM5xx family handhelds.
List of child drivers:
- rzone.cpp: Tiger R-Zone

The LCD screen graphics are provided internally with an SVG file.
MAME external artwork is recommended for the backgrounds inlays.

Most of these LCD games are meant to stay powered on 24/7. There is no
RTC or NVRAM. Quitting MAME is akin to removing the handheld's battery.
Use -autosave to at least make them remember the highscores.

ROM source notes when dumped from another title, but confident it's the same:
- gnw_ball: Mego Toss-Up
- gnw_bfight: New Wide Screen model
- gnw_climber: New Wide Screen model
- gnw_smb: New Wide Screen model
- kbottom9: Ganbare Baseball
- misc. Soviet Egg clones (not the LCD): G&W Mickey Mouse / Nu, pogodi!

TODO:
- improve display decay simulation? but SVG doesn't support setting brightness
  per segment, adding pwm_display_device right now has no added value
- add nstarfox sound effect chip emulation
- naltair IPT_DIAL should be 1-way, it's not supposed to rotate left
- Currently there is no accurate way to dump the SM511/SM512 melody ROM
  electronically. For the ones that weren't decapped, they were read by
  playing back all melody data and reconstructing it to ROM. Visual(decap)
  verification is wanted for: bassmate, gnw_bfightn, gnw_bjack, gnw_bsweep,
  gnw_climbern, gnw_dkcirc, gnw_dkhockey, gnw_dkjrp, gnw_dkong3, gnw_gcliff,
  gnw_mariocmt, gnw_mariocmta, gnw_mariotj, gnw_mbaway, gnw_mmousep,
  gnw_pinball, gnw_popeyep, gnw_sbuster, gnw_snoopyp, gnw_zelda, trtreisl,
  trspacadv, vesarif, uchitari

================================================================================

Misc Nintendo Game & Watch notes:

Trivia: Most of the Nintendo G&W have built-in cheats, likely kept in by
Nintendo to test the game. These were not accessible to users of course,
but for the sake of fun they're available on MAME.

BTANB: On some of the earlier G&W games, eg. gnw_fire, gnw_mmouse, gnw_pchute,
gnw_popeye, the controls still work after game over, this happens on the real
thing too.

Game list (* denotes not emulated yet)

Serial  Series MCU     Title
---------------------------------------------
AC-01     s    SM5A    Ball (aka Toss-Up)
FL-02     s    SM5A    Flagman (aka Flag Man)
MT-03     s    SM5A    Vermin (aka The Exterminator)
RC-04     s    SM5A    Fire (aka Fireman Fireman)
IP-05     s    SM5A    Judge
MH-06     g    SM5A    Manhole
CN-07     g    SM5A    Helmet (aka Headache)
LN-08     g    SM5A    Lion
PR-21     ws   SM5A    Parachute
OC-22     ws   SM5A    Octopus
PP-23     ws   SM5A    Popeye
FP-24     ws   SM5A    Chef
MC-25     ws   SM5A    Mickey Mouse
EG-26     ws   SM5A    Egg (same ROM as MC-25, but LCD differs)
FR-27     ws   SM5A    Fire
TL-28     ws   SM510   Turtle Bridge
ID-29     ws   SM510   Fire Attack
SP-30     ws   SM510   Snoopy Tennis
OP-51     ms   SM510   Oil Panic
DK-52     ms   SM510   Donkey Kong
DM-53     ms   SM510   Mickey & Donald
GH-54     ms   SM510   Green House
JR-55     ms   SM510   Donkey Kong II
MW-56     ms   SM510   Mario Bros.
LP-57     ms   SM510   Rain Shower
TC-58     ms   SM510   Life Boat
PB-59     ms   SM511   Pinball
BJ-60     ms   SM512   Black Jack
MG-61     ms   SM510   Squish
BD-62     ms   SM512   Bomb Sweeper
JB-63     ms   SM511   Safe Buster
MV-64     ms   SM512   Gold Cliff
ZL-65     ms   SM512   Zelda
TR-66*    ms   SM512?  Tetris Jr. (prototype)
CJ-71*    tt   SM511?  Donkey Kong Jr.
CM-72     tt   SM511   Mario's Cement Factory
SM-73*    tt   SM511?  Snoopy
PG-74*    tt   SM511?  Popeye
SM-91     p    SM511   Snoopy (assume same ROM & LCD as tabletop version)
PG-92     p    SM511   Popeye          "
CJ-93     p    SM511   Donkey Kong Jr. "
TB-94     p    SM511   Mario's Bombs Away
DC-95     p    SM511   Mickey Mouse
MK-96     p    SM511   Donkey Kong Circus (same ROM as DC-95, LCD is different)
DJ-101    nws  SM510   Donkey Kong Jr.
ML-102    nws  SM510   Mario's Cement Factory
NH-103    nws  SM510   Manhole
TF-104    nws  SM510   Tropical Fish
YM-105    nws  SM511   Super Mario Bros.
DR-106    nws  SM511   Climber
BF-107    nws  SM511   Balloon Fight
MB-108    nws  SM511   Mario The Juggler
BU-201    sc   SM510   Spitball Sparky
UD-202    sc   SM510   Crab Grab
BX-301    mvs  SM511   Boxing (aka Punch Out)
AK-302    mvs  SM511   Donkey Kong 3
HK-303    mvs  SM511   Donkey Kong Hockey
YM-801    cs   SM511   Super Mario Bros. (assume same ROM as nws version)
DR-802    cs   SM511   Climber            "
BF-803    cs   SM511   Balloon Fight      "
YM-901-S* x    SM511   Super Mario Bros.  "

RGW-001 (2010 Ball remake) is on different hardware, ATmega169PV MCU.
The "Mini Classics" keychains are by Stadlbauer, not Nintendo.
The "Game Watch" wristwatches are by Nelsonic, not Nintendo.

Bassmate Computer (BM-501) is on identical hardware as G&W Multi Screen,
but it's not part of the game series.

================================================================================

Regarding Электроника (Elektronika, translated: Electronics): It is not
actually a company. It was a USSR brand name for consumer electronics,
produced by factories belonging to the Ministry of Electronic Industry
(Minelektronprom, МЭП).

The LCD games were produced by: Angstrem, Mikron, Voschod (Russia), Billur
(Azerbaijan), Kamerton, Evistor (Belarus), Severodonetsk Instrument-Making
Plant (Ukraine), PO Proton and more. Their most popular LCD game (Nu, pogodi!),
is known to be initially produced by Evistor.

Most of the games are marked "bootleg" in MAME, because the ROM contents are
a 1:1 copy of Nintendo Game & Watch games. Known G&W cloned by Elektronika:
Fire(FR-27), Octopus, Chef, Egg/Mickey Mouse, Donkey Kong Jr.(CJ-93),
Spitball Sparky.

The MCUs used were not imported from Sharp, but cloned by USSR, renamed to
КБ1013ВК1-2 for SM5A, КБ1013ВК4-2 for SM510 and КБ1013ВК7-2 for SM511.

*******************************************************************************/

#include "emu.h"
#include "hh_sm510.h"

#include "sound/samples.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "hh_sm510_single.lh"
#include "hh_sm510_dualv.lh"
#include "hh_sm510_dualh.lh"
#include "hh_sm510_tripleh.lh"

#include "elbaskb.lh"

//#include "hh_sm510_test.lh" // common test-layout - use external artwork
#include "hh_sm500_test.lh" // "


// machine start/reset

void hh_sm510_state::machine_start()
{
	// resolve outputs
	m_out_x.resolve();

	// determine number of input lines (set it in the subclass constructor if different)
	if (m_inp_lines == 0 && m_inp_fixed < 0)
	{
		for (; m_inputs[m_inp_lines] != nullptr; m_inp_lines++) { ; }

		// when last input line is fixed(GND)
		if (m_inp_fixed == -2)
		{
			m_inp_lines--;
			m_inp_fixed = m_inp_lines;
		}
	}

	// 1kHz display decay ticks
	m_display_decay_timer = timer_alloc(FUNC(hh_sm510_state::display_decay_tick), this);
	m_display_decay_timer->adjust(attotime::from_hz(1024), 0, attotime::from_hz(1024));

	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_inp_lines));
	save_item(NAME(m_inp_fixed));
	save_item(NAME(m_speaker_data));
	save_item(NAME(m_s));
	save_item(NAME(m_r));

	save_item(NAME(m_display_x_len));
	save_item(NAME(m_display_y_len));
	save_item(NAME(m_display_z_len));
	save_item(NAME(m_display_state));
	save_item(NAME(m_display_decay));
	save_item(NAME(m_decay_pivot));
	save_item(NAME(m_decay_len));
}

void hh_sm510_state::machine_reset()
{
}



/*******************************************************************************

  Helper Functions

*******************************************************************************/

// lcd panel - on lcd handhelds, usually not a generic x/y screen device
// deflicker here, especially needed for SM500/SM5A with the active shift register

void hh_sm510_state::update_display()
{
	u8 z_mask = (1 << m_display_z_len) - 1;
	u8 zx_len = 1 << (m_display_x_len + m_display_z_len);

	for (int zx = 0; zx < zx_len; zx++)
	{
		for (int y = 0; y < m_display_y_len; y++)
		{
			// delay lcd segment on/off state
			if (m_display_state[zx] >> y & 1)
			{
				if (m_display_decay[y][zx] < (m_decay_pivot + m_decay_len))
					m_display_decay[y][zx]++;
			}
			else if (m_display_decay[y][zx] > 0)
				m_display_decay[y][zx]--;
			u8 active_state = (m_display_decay[y][zx] < m_decay_pivot) ? 0 : 1;

			// SM510 series: output to x.y.z, where:
			// x = group a/b/bs/c (0/1/2/3)
			// y = segment 1-16 (0-15)
			// z = common H1-H4 (0-3)

			// SM500/SM530 series: output to x.y.z, where:
			// x = O group (0-*)
			// y = O segment 1-4 (0-3)
			// z = common H1/H2 (0/1)
			m_out_x[zx >> m_display_z_len][y][zx & z_mask] = active_state;
		}
	}
}

void hh_sm510_state::set_display_size(u8 x, u8 y, u8 z)
{
	// x = groups(in bits)
	// y = number of segments per group
	// z = commons(in bits)
	m_display_x_len = x;
	m_display_y_len = y;
	m_display_z_len = z;
}

void hh_sm510_state::sm510_lcd_segment_w(offs_t offset, u16 data)
{
	set_display_size(2, 16, 2);
	m_display_state[offset] = data;
}

void hh_sm510_state::sm500_lcd_segment_w(offs_t offset, u16 data)
{
	set_display_size(4, 4, 1);
	m_display_state[offset] = data;
}


// generic input handlers - usually S output is input mux, and K input for buttons

u8 hh_sm510_state::read_inputs(int columns, int fixed)
{
	u8 ret = 0;

	// read selected input rows
	for (int i = 0; i < columns; i++)
		if (BIT(m_inp_mux, i))
			ret |= m_inputs[i]->read();

	if (fixed >= 0)
		ret |= m_inputs[fixed]->read();

	return ret;
}

void hh_sm510_state::update_k_line()
{
	// this is necessary because the MCU can wake up on K input activity
	m_maincpu->set_input_line(0, input_r() ? ASSERT_LINE : CLEAR_LINE);
}

INPUT_CHANGED_MEMBER(hh_sm510_state::input_changed)
{
	update_k_line();
}

void hh_sm510_state::input_w(u8 data)
{
	m_inp_mux = data;
	update_k_line();
}

u8 hh_sm510_state::input_r()
{
	return read_inputs(m_inp_lines, m_inp_fixed);
}

INPUT_CHANGED_MEMBER(hh_sm510_state::acl_button)
{
	// ACL button is directly tied to MCU ACL pin
	m_maincpu->set_input_line(SM510_INPUT_LINE_ACL, newval ? ASSERT_LINE : CLEAR_LINE);
}


// other generic output handlers

void hh_sm510_state::piezo_r1_w(u8 data)
{
	// R1 to piezo (SM511 R pin is melody output)
	m_speaker->level_w(data & 1);
}

void hh_sm510_state::piezo_r2_w(u8 data)
{
	// R2 to piezo
	m_speaker->level_w(data >> 1 & 1);
}

void hh_sm510_state::piezo_input_w(u8 data)
{
	// R1 to piezo, other to input mux
	piezo_r1_w(data & 1);
	input_w(data >> 1);
}

void hh_sm510_state::piezo2bit_r1_w(u8 data)
{
	// R1(+S1) to piezo
	m_speaker_data = (m_speaker_data & ~1) | (data & 1);
	m_speaker->level_w(m_speaker_data);
}

void hh_sm510_state::piezo2bit_input_w(u8 data)
{
	// S1(+R1) to piezo, other to input mux
	m_speaker_data = (m_speaker_data & ~2) | (data << 1 & 2);
	m_speaker->level_w(m_speaker_data);
	input_w(data >> 1);
}



/*******************************************************************************

  Common Machine Configurations

*******************************************************************************/

// building blocks

void hh_sm510_state::mcfg_cpu_common(machine_config &config)
{
	m_maincpu->read_k().set(FUNC(hh_sm510_state::input_r));
	m_maincpu->read_ba().set([this]() { return m_io_ba.read_safe(1); });
	m_maincpu->read_b().set([this]() { return m_io_b.read_safe(1); });
}

void hh_sm510_state::mcfg_cpu_sm5a(machine_config &config)
{
	SM5A(config, m_maincpu);
	mcfg_cpu_common(config);
	m_maincpu->set_r_mask_option(sm510_base_device::RMASK_DIRECT);
	m_maincpu->write_segs().set(FUNC(hh_sm510_state::sm500_lcd_segment_w));
}

void hh_sm510_state::mcfg_cpu_sm510(machine_config &config)
{
	SM510(config, m_maincpu);
	mcfg_cpu_common(config);
	m_maincpu->set_r_mask_option(2);
	m_maincpu->write_segs().set(FUNC(hh_sm510_state::sm510_lcd_segment_w));
	m_maincpu->write_s().set(FUNC(hh_sm510_state::input_w));
}

void hh_sm510_state::mcfg_cpu_sm511(machine_config &config)
{
	SM511(config, m_maincpu);
	mcfg_cpu_common(config);
	m_maincpu->write_segs().set(FUNC(hh_sm510_state::sm510_lcd_segment_w));
	m_maincpu->write_s().set(FUNC(hh_sm510_state::input_w));
}

void hh_sm510_state::mcfg_cpu_sm512(machine_config &config)
{
	SM512(config, m_maincpu);
	mcfg_cpu_common(config);
	m_maincpu->write_segs().set(FUNC(hh_sm510_state::sm510_lcd_segment_w));
	m_maincpu->write_s().set(FUNC(hh_sm510_state::input_w));
}

void hh_sm510_state::mcfg_cpu_sm530(machine_config &config)
{
	SM530(config, m_maincpu);
	mcfg_cpu_common(config);
	m_maincpu->write_segs().set(FUNC(hh_sm510_state::sm500_lcd_segment_w));
	m_maincpu->write_s().set(FUNC(hh_sm510_state::input_w));
}

void hh_sm510_state::mcfg_svg_screen(machine_config &config, u16 width, u16 height, const char *tag)
{
	if (width == 0 || height == 0)
		return;

	screen_device &screen(SCREEN(config, tag, SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(width, height);
	screen.set_visarea_full();

	config.set_default_layout(layout_hh_sm510_single);
}

void hh_sm510_state::mcfg_sound_r1(machine_config &config)
{
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);

	m_maincpu->write_r().set(FUNC(hh_sm510_state::piezo_r1_w));
}


// common presets

void hh_sm510_state::sm5a_common(machine_config &config, u16 width, u16 height)
{
	mcfg_cpu_sm5a(config);
	mcfg_sound_r1(config);
	m_maincpu->write_r().set(FUNC(hh_sm510_state::piezo_input_w));
	mcfg_svg_screen(config, width, height);
}

void hh_sm510_state::sm510_common(machine_config &config, u16 width, u16 height)
{
	mcfg_cpu_sm510(config);
	mcfg_sound_r1(config);
	mcfg_svg_screen(config, width, height);
}

void hh_sm510_state::sm511_common(machine_config &config, u16 width, u16 height)
{
	mcfg_cpu_sm511(config);
	mcfg_sound_r1(config);
	mcfg_svg_screen(config, width, height);
}

void hh_sm510_state::sm530_common(machine_config &config, u16 width, u16 height)
{
	mcfg_cpu_sm530(config);
	mcfg_sound_r1(config);
	mcfg_svg_screen(config, width, height);
}


// deviations

// multi-screen

void hh_sm510_state::sm510_dualh(machine_config &config, u16 leftwidth, u16 leftheight, u16 rightwidth, u16 rightheight)
{
	mcfg_cpu_sm510(config);
	mcfg_sound_r1(config);
	mcfg_svg_screen(config, leftwidth, leftheight, "screen_left");
	mcfg_svg_screen(config, rightwidth, rightheight, "screen_right");

	config.set_default_layout(layout_hh_sm510_dualh);
}

void hh_sm510_state::sm511_tripleh(machine_config &config, u16 leftwidth, u16 leftheight, u16 middlewidth, u16 middleheight, u16 rightwidth, u16 rightheight)
{
	mcfg_cpu_sm511(config);
	mcfg_sound_r1(config);
	mcfg_svg_screen(config, leftwidth, leftheight, "screen_left");
	mcfg_svg_screen(config, middlewidth, middleheight, "screen_middle");
	mcfg_svg_screen(config, rightwidth, rightheight, "screen_right");

	config.set_default_layout(layout_hh_sm510_tripleh);
}

void hh_sm510_state::dualv_common(machine_config &config, u16 topwidth, u16 topheight, u16 botwidth, u16 botheight)
{
	mcfg_sound_r1(config);
	mcfg_svg_screen(config, topwidth, topheight, "screen_top");
	mcfg_svg_screen(config, botwidth, botheight, "screen_bottom");

	config.set_default_layout(layout_hh_sm510_dualv);
}

void hh_sm510_state::sm510_dualv(machine_config &config, u16 topwidth, u16 topheight, u16 botwidth, u16 botheight)
{
	mcfg_cpu_sm510(config);
	dualv_common(config, topwidth, topheight, botwidth, botheight);
}

void hh_sm510_state::sm511_dualv(machine_config &config, u16 topwidth, u16 topheight, u16 botwidth, u16 botheight)
{
	mcfg_cpu_sm511(config);
	dualv_common(config, topwidth, topheight, botwidth, botheight);
}

void hh_sm510_state::sm512_dualv(machine_config &config, u16 topwidth, u16 topheight, u16 botwidth, u16 botheight)
{
	mcfg_cpu_sm512(config);
	dualv_common(config, topwidth, topheight, botwidth, botheight);
}


// Tiger (SM510 R mask is direct, BA/B pins always connected)

void hh_sm510_state::sm510_tiger(machine_config &config, u16 width, u16 height)
{
	sm510_common(config, width, height);

	m_maincpu->set_r_mask_option(sm510_base_device::RMASK_DIRECT);
	m_maincpu->read_ba().set_ioport("BA");
	m_maincpu->read_b().set_ioport("B");
}

void hh_sm510_state::sm511_tiger1bit(machine_config &config, u16 width, u16 height)
{
	sm511_common(config, width, height);

	m_maincpu->read_ba().set_ioport("BA");
	m_maincpu->read_b().set_ioport("B");
}

void hh_sm510_state::sm511_tiger2bit(machine_config &config, u16 width, u16 height)
{
	sm511_tiger1bit(config, width, height);

	m_maincpu->write_s().set(FUNC(hh_sm510_state::piezo2bit_input_w));
	m_maincpu->write_r().set(FUNC(hh_sm510_state::piezo2bit_r1_w));

	// R via 120K resistor, S1 via 39K resistor (eg. tsonic, tsonic2, tbatmana)
	static const double speaker_levels[] = { 0.0, 1.0/3.0, 2.0/3.0, 1.0 };
	m_speaker->set_levels(4, speaker_levels);
}



/*******************************************************************************

  Minidrivers (subclass, I/O, Inputs, Machine Config, ROM Defs)

*******************************************************************************/

namespace {

/*******************************************************************************

  Nintendo Game & Watch: Ball (model AC-01)
  * PCB label: AC-01
  * Sharp SM5A label AC-01 5009 (no decap)
  * lcd screen with custom segments, 1-bit sound

  In the USA, it was distributed as Toss-Up by Mego under their Time-Out series.

*******************************************************************************/

class gnw_ball_state : public hh_sm510_state
{
public:
	gnw_ball_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void gnw_ball(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_ball )
	PORT_START("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_CONFNAME( 0x08, 0x00, "Invincibility (Cheat)" ) // factory test, unpopulated on PCB -- disable after boot
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x08, DEF_STR( On ) )

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void gnw_ball_state::gnw_ball(machine_config &config)
{
	sm5a_common(config, 1671, 1080); // R mask option confirmed
}

// roms

ROM_START( gnw_ball )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "ac-01", 0x0000, 0x0740, CRC(ac94e6e4) SHA1(8270cb61f9fbff252eafec411b4c67f0171f8687) )

	ROM_REGION( 71748, "screen", 0)
	ROM_LOAD( "gnw_ball.svg", 0, 71748, CRC(7c116eaf) SHA1(578882af492b8a9f1eb72e06a547c8b574255fb9) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Flagman (model FL-02)
  * PCB label: FL-02
  * Sharp SM5A label FL-02 2005 (no decap)
  * lcd screen with custom segments, 1-bit sound

  In the USA, it was distributed as Flag Man by Mego under their Time-Out series.

*******************************************************************************/

class gnw_flagman_state : public hh_sm510_state
{
public:
	gnw_flagman_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_flagman(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_flagman )
	PORT_START("IN.0") // R2
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY PORT_NAME("4")

	PORT_START("IN.2") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB -- only works after power-on
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void gnw_flagman_state::gnw_flagman(machine_config &config)
{
	sm5a_common(config, 1511, 1080); // R mask option confirmed
}

// roms

ROM_START( gnw_flagman )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "fl-02", 0x0000, 0x0740, CRC(cc7a99e4) SHA1(d03d9a6b278bc11df7839708831241b5fa805f69) )

	ROM_REGION( 56163, "screen", 0)
	ROM_LOAD( "gnw_flagman.svg", 0, 56163, CRC(3aa97c65) SHA1(a363e71d371e5c85835cb3a0679760d0aedc75d5) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Vermin (model MT-03)
  * PCB label: MT-03
  * Sharp SM5A label MT-03 5012 (no decap)
  * lcd screen with custom segments, 1-bit sound

  In the USA, it was distributed as The Exterminator by Mego under their Time-Out series.

*******************************************************************************/

class gnw_vermin_state : public hh_sm510_state
{
public:
	gnw_vermin_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void gnw_vermin(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_vermin )
	PORT_START("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_CONFNAME( 0x08, 0x00, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB -- disable after boot
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x08, DEF_STR( On ) )

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void gnw_vermin_state::gnw_vermin(machine_config &config)
{
	sm5a_common(config, 1650, 1080); // R mask option confirmed
}

// roms

ROM_START( gnw_vermin )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "mt-03", 0x0000, 0x0740, CRC(f8493177) SHA1(d629432ef8e9fbd7bbdc3fbeb45d9bd70d9d571b) )

	ROM_REGION( 105603, "screen", 0)
	ROM_LOAD( "gnw_vermin.svg", 0, 105603, CRC(1bd59ef4) SHA1(099120105e80d4753838ea513ffa784c4690cf5f) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Fire (model RC-04)
  * PCB label: RC-04
  * Sharp SM5A label RC-04 5103 (no decap)
  * lcd screen with custom segments, 1-bit sound

  This is the silver version, there's also a wide screen version.

  In the USA, it was distributed as Fireman Fireman by Mego under their Time-Out series.

*******************************************************************************/

class gnw_fires_state : public hh_sm510_state
{
public:
	gnw_fires_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void gnw_fires(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_fires )
	PORT_START("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_CONFNAME( 0x08, 0x00, "Invincibility (Cheat)" ) // factory test, unpopulated on PCB -- disable after boot
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x08, DEF_STR( On ) )

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void gnw_fires_state::gnw_fires(machine_config &config)
{
	sm5a_common(config, 1646, 1080); // R mask option confirmed
}

// roms

ROM_START( gnw_fires )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "rc-04", 0x0000, 0x0740, CRC(154ef27d) SHA1(fb65826dfd405ad05fe0f5f947c213214bbd61c0) )

	ROM_REGION( 102678, "screen", 0)
	ROM_LOAD( "gnw_fires.svg", 0, 102678, CRC(4f61f2f8) SHA1(2873629f0e36d3170bc284fa031a9c6181021495) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Judge (model IP-05)
  * PCB label: IP-05
  * Sharp SM5A label IP-05 5010, or IP-15 5012 (no decap)
  * lcd screen with custom segments, 1-bit sound

  The first (green) issue of the game contains a bug where the players are
  scored differently when wrongly dodging a win. This issue is fixed in the
  second (purple) issue.

*******************************************************************************/

class gnw_judge_state : public hh_sm510_state
{
public:
	gnw_judge_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_judge(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_judge )
	PORT_START("IN.0") // R2
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY PORT_NAME("P2 Dodge")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY PORT_NAME("P2 Hit")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY PORT_NAME("P1 Dodge")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY PORT_NAME("P1 Hit")

	PORT_START("IN.2") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B") // 2-Player
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A") // 1-Player
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Increase Computer's Operation Time (Cheat)" ) // factory test, unpopulated on PCB -- only works after power-on
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void gnw_judge_state::gnw_judge(machine_config &config)
{
	sm5a_common(config, 1647, 1080); // R mask option confirmed
}

// roms

ROM_START( gnw_judge )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "ip-15", 0x0000, 0x0740, CRC(f6ed6f62) SHA1(97bc1b5c383fb4077d982cfdc5a7d7603a0b5e2f) )

	ROM_REGION( 105108, "screen", 0)
	ROM_LOAD( "gnw_judge.svg", 0, 105108, CRC(7760e82e) SHA1(cfc1f08465ecc8ac3385bcb078268cbbfca9fc41) )
ROM_END

ROM_START( gnw_judgeo )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "ip-05", 0x0000, 0x0740, CRC(1b28a834) SHA1(cb8dbbf678ba22c4484d18cc1a6b99c1d34d1951) )

	ROM_REGION( 105108, "screen", 0)
	ROM_LOAD( "gnw_judge.svg", 0, 105108, CRC(7760e82e) SHA1(cfc1f08465ecc8ac3385bcb078268cbbfca9fc41) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Manhole (model MH-06)
  * PCB label: MH-06
  * Sharp SM5A label MH-06 5104 (no decap)
  * lcd screen with custom segments, 1-bit sound

  This is the Gold Series version, there's also a new wide screen version (NH-103)

*******************************************************************************/

class gnw_manholeg_state : public hh_sm510_state
{
public:
	gnw_manholeg_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_manholeg(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_manholeg )
	PORT_START("IN.0") // R2
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("IN.2") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) // display test?

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Invincibility (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void gnw_manholeg_state::gnw_manholeg(machine_config &config)
{
	sm5a_common(config, 1667, 1080); // R mask option confirmed
}

// roms

ROM_START( gnw_manholeg )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "mh-06", 0x0000, 0x0740, CRC(ae52c425) SHA1(8da8a714ecbdde7d0f257b52a5014993675a5f3f) )

	ROM_REGION( 125607, "screen", 0)
	ROM_LOAD( "gnw_manholeg.svg", 0, 125607, CRC(4b281ff2) SHA1(18f212ab5738756e0841d6afa401a03f7aaddf7b) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Helmet (model CN-07)
  * PCB label: CN-07
  * Sharp SM5A label CN-07 5102, or CN-17 21ZA (no decap)
  * lcd screen with custom segments, 1-bit sound

  In the UK, it was distributed as Headache by CGL.

  MCU label CN-07 is the first version, CN-17 is a bugfix release.

*******************************************************************************/

class gnw_helmet_state : public hh_sm510_state
{
public:
	gnw_helmet_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_helmet(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_helmet )
	PORT_START("IN.0") // R2
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R3
	PORT_CONFNAME( 0x01, 0x00, "Invincibility (Cheat)" ) // factory test, unpopulated on PCB -- disable after boot
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) // same as 0x01?
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED ) // "
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) // alarm test?

	PORT_START("IN.2") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void gnw_helmet_state::gnw_helmet(machine_config &config)
{
	sm5a_common(config, 1657, 1080); // R mask option confirmed
}

// roms

ROM_START( gnw_helmet )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "cn-17", 0x0000, 0x0740, CRC(6d251e2e) SHA1(c61f591514de36fb2270038a6505945564c9f90e) )

	ROM_REGION( 109404, "screen", 0)
	ROM_LOAD( "gnw_helmet.svg", 0, 109404, CRC(0dce1694) SHA1(412e69054b95f17fe08545f3c303c11abbe26304) )
ROM_END

ROM_START( gnw_helmeto )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "cn-07", 0x0000, 0x0740, CRC(30c8bc90) SHA1(f5ac0fe7a09ee1ad6f6e4bd096b4be20f65d73db) )

	ROM_REGION( 109404, "screen", 0)
	ROM_LOAD( "gnw_helmet.svg", 0, 109404, CRC(0dce1694) SHA1(412e69054b95f17fe08545f3c303c11abbe26304) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Lion (model LN-08)
  * PCB label: LN-08
  * Sharp SM5A label LN-08 519A (no decap)
  * lcd screen with custom segments, 1-bit sound

  BTANB: The game doesn't support simultaneous button presses for the controls,
  it's the same as in eg. gnw_mmouse but in this game it doesn't make much sense
  with the 2 separate guys. More likely a bad game design choice than bug.

*******************************************************************************/

class gnw_lion_state : public hh_sm510_state
{
public:
	gnw_lion_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_lion(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_lion )
	PORT_START("IN.0") // R2
	PORT_CONFNAME( 0x01, 0x00, "Increase Speed (Cheat)" ) // factory test, unpopulated on PCB -- disable after boot
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) // same as 0x01?
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED ) // "
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) // "

	PORT_START("IN.1") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("IN.2") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) // display test?

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void gnw_lion_state::gnw_lion(machine_config &config)
{
	sm5a_common(config, 1646, 1080); // R mask option confirmed
}

// roms

ROM_START( gnw_lion )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "ln-08", 0x0000, 0x0740, CRC(9677681d) SHA1(6f7c960e04b63f1b7d926b598413f4c818b8fe53) )

	ROM_REGION( 155863, "screen", 0)
	ROM_LOAD( "gnw_lion.svg", 0, 155863, CRC(b5a5a4dc) SHA1(49d894d6e1d1fb35cd11f08c7ce30518be89dd0f) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Parachute (model PR-21)
  * PCB label: PR-21Y
  * Sharp SM5A label PR-21 52XC (no decap)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class gnw_pchute_state : public hh_sm510_state
{
public:
	gnw_pchute_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_pchute(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_pchute )
	PORT_START("IN.0") // R2
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R3
	PORT_CONFNAME( 0x01, 0x00, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB -- disable after boot
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) // same as 0x01?
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED ) // "
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) // alarm test?

	PORT_START("IN.2") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void gnw_pchute_state::gnw_pchute(machine_config &config)
{
	sm5a_common(config, 1602, 1080); // R mask option confirmed
}

// roms

ROM_START( gnw_pchute )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "pr-21", 0x0000, 0x0740, CRC(392b545e) SHA1(e71940cd4cee07ba1e62c1c7d9e9b19410e7232d) )

	ROM_REGION( 169640, "screen", 0)
	ROM_LOAD( "gnw_pchute.svg", 0, 169640, CRC(f30a0b31) SHA1(676989a418ae0dfe6bb1b097640422219c930453) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Octopus (model OC-22)
  * PCB label: OC-22Y A
  * Sharp SM5A label OC-22 204A (no decap)
  * lcd screen with custom segments, 1-bit sound

  Also cloned in 1989 by Elektronika(USSR) as Тайны океана (Tayny okeana, export
  version: Mysteries of the Ocean), ROM is identical, graphics as well except
  for the AM/PM/GAME segments.

*******************************************************************************/

class gnw_octopus_state : public hh_sm510_state
{
public:
	gnw_octopus_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_octopus(machine_config &config);
	void taynyoke(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_octopus )
	PORT_START("IN.0") // R2
	PORT_CONFNAME( 0x01, 0x00, "Invincibility (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) // same as 0x01?
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED ) // "
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) // "

	PORT_START("IN.1") // R3
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED ) // alarm test?

	PORT_START("IN.2") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void gnw_octopus_state::gnw_octopus(machine_config &config)
{
	sm5a_common(config, 1586, 1080); // R mask option confirmed
}

void gnw_octopus_state::taynyoke(machine_config &config)
{
	sm5a_common(config, 1647, 1080); // КБ1013ВК1-2, R mask option confirmed
}

// roms

ROM_START( gnw_octopus )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "oc-22", 0x0000, 0x0740, CRC(bd27781d) SHA1(07b4feb9265c83b159f96c7e8ee1c61a2cc17dc5) )

	ROM_REGION( 119827, "screen", 0)
	ROM_LOAD( "gnw_octopus.svg", 0, 119827, CRC(efbdaa65) SHA1(42c746bef282176d59f57ddf7328f8d034f4ca02) )
ROM_END

ROM_START( taynyoke )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "im-03.bin", 0x0000, 0x0740, CRC(bd27781d) SHA1(07b4feb9265c83b159f96c7e8ee1c61a2cc17dc5) )

	ROM_REGION( 93910, "screen", 0)
	ROM_LOAD( "taynyoke.svg", 0, 93910, CRC(da7a835e) SHA1(1fe427a60bbf78fdf29ea401ec86b225098b68bb) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Popeye (model PP-23)
  * PCB label: PP-23 Y
  * Sharp SM5A label PP-23 52YD (no decap)
  * lcd screen with custom segments, 1-bit sound

  This is the wide screen version, there's also tabletop and panorama versions.

*******************************************************************************/

class gnw_popeye_state : public hh_sm510_state
{
public:
	gnw_popeye_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_popeye(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_popeye )
	PORT_START("IN.0") // R2
	PORT_CONFNAME( 0x01, 0x00, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) // same as 0x01?
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED ) // reset?
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) // alarm test?

	PORT_START("IN.1") // R3
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void gnw_popeye_state::gnw_popeye(machine_config &config)
{
	sm5a_common(config, 1604, 1080); // R mask option confirmed
}

// roms

ROM_START( gnw_popeye )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "pp-23", 0x0000, 0x0740, CRC(49987769) SHA1(ad90659a3ce7169a4df16367c5307435d9f9d956) )

	ROM_REGION( 218587, "screen", 0)
	ROM_LOAD( "gnw_popeye.svg", 0, 218587, CRC(4740bcd5) SHA1(a46ab455f2dd41caabd6c85cfa7dfde70805f157) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Chef (model FP-24)
  * PCB label: FP-24
  * Sharp SM5A label FP-24 51YB (die label CMS646, ROM ID 74)
  * lcd screen with custom segments, 1-bit sound

  In 1989, Elektronika(USSR) released a clone: Весёлый повар (Vesyolyy povar,
  export version: Merry Cook). This game shares the same ROM, though the graphics
  are slightly different.

*******************************************************************************/

class gnw_chef_state : public hh_sm510_state
{
public:
	gnw_chef_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void vespovar(machine_config &config);
	void gnw_chef(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_chef )
	PORT_START("IN.0") // R2
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("IN.2") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB -- only works after power-on
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_chef_state::gnw_chef(machine_config &config)
{
	sm5a_common(config, 1666, 1080); // assuming same R mask option as merry cook
}

void gnw_chef_state::vespovar(machine_config & config)
{
	sm5a_common(config, 1679, 1080); // КБ1013ВК1-2, R mask option confirmed
}

// roms

ROM_START( gnw_chef )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "fp-24", 0x0000, 0x0740, CRC(2806ab39) SHA1(18261a80eec5bf768bb88b803c598f80e078c71f) )

	ROM_REGION( 199518, "screen", 0)
	ROM_LOAD( "gnw_chef.svg", 0, 199518, CRC(ecc18d28) SHA1(1c0b7dfff71faa4d4395c19a84454870e403f927) )
ROM_END

ROM_START( vespovar )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "im-04.bin", 0x0000, 0x0740, CRC(2806ab39) SHA1(18261a80eec5bf768bb88b803c598f80e078c71f) )

	ROM_REGION( 144128, "screen", 0)
	ROM_LOAD( "vespovar.svg", 0, 144128, CRC(dcd1c073) SHA1(e15bf643f17b7ead37407c985e053e6434683d7c) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Mickey Mouse (model MC-25), Egg (model EG-26)
  * PCB label: MC-25 EG-26 (yes, both listed)
  * Sharp SM5A label MC-25 51YD (Mickey Mouse), MC-25 519D (Egg) (no decap)
  * lcd screen with custom segments, 1-bit sound

  MC-25 and EG-26 are the same game, it's assumed that the latter was for
  regions where Nintendo wasn't able to license from Disney.

  This game was also cloned (a lot) by Elektronika, see nupogodi_state.

*******************************************************************************/

class gnw_mmouse_state : public hh_sm510_state
{
public:
	gnw_mmouse_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_mmouse(machine_config &config);
	void gnw_egg(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_mmouse )
	PORT_START("IN.0") // R2
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("IN.2") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB -- only works after power-on
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_mmouse_state::gnw_mmouse(machine_config &config)
{
	sm5a_common(config, 1684, 1080); // R mask option confirmed
}

void gnw_mmouse_state::gnw_egg(machine_config &config)
{
	sm5a_common(config, 1690, 1080); // R mask option confirmed
}

// roms

ROM_START( gnw_mmouse )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "mc-25", 0x0000, 0x0740, CRC(cb820c32) SHA1(7e94fc255f32db725d5aa9e196088e490c1a1443) )

	ROM_REGION( 181706, "screen", 0)
	ROM_LOAD( "gnw_mmouse.svg", 0, 181706, CRC(60cdc76a) SHA1(09755abd16222c1a0fe6c7ebb902706440d3e369) )
ROM_END

ROM_START( gnw_egg )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "mc-25", 0x0000, 0x0740, CRC(cb820c32) SHA1(7e94fc255f32db725d5aa9e196088e490c1a1443) )

	ROM_REGION( 193119, "screen", 0)
	ROM_LOAD( "gnw_egg.svg", 0, 193119, CRC(1e469fe5) SHA1(bc80114337feefca590e48c823e8488f6b63f896) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Fire (model FR-27)
  * PCB label: FR-27
  * Sharp SM5A label FR-27 523B (no decap)
  * lcd screen with custom segments, 1-bit sound

  This is the wide screen version, there's also a silver version. Doing a
  hex-compare between the two, this one seems to be a complete rewrite.
  FR-27 is the last G&W on SM5A, they were followed with SM51x.

  In 1989 Elektronika(USSR) released a clone: Космический мост (Kosmicheskiy most,
  export version: Space Bridge). This game shares the same ROM, though the
  graphics are different.

*******************************************************************************/

class gnw_fire_state : public hh_sm510_state
{
public:
	gnw_fire_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void kosmicmt(machine_config &config);
	void gnw_fire(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_fire )
	PORT_START("IN.0") // R2
	PORT_CONFNAME( 0x01, 0x00, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) // same as 0x01?
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED ) // reset?
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) // alarm test?

	PORT_START("IN.1") // R3
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED ) // lcd test?

	PORT_START("IN.2") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void gnw_fire_state::gnw_fire(machine_config &config)
{
	sm5a_common(config, 1624, 1080); // R mask option confirmed
}

void gnw_fire_state::kosmicmt(machine_config & config)
{
	sm5a_common(config, 1673, 1080); // КБ1013ВК1-2, R mask option confirmed
}

// roms

ROM_START( gnw_fire )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "fr-27", 0x0000, 0x0740, CRC(f4c53ef0) SHA1(6b57120a0f9d2fd4dcd65ad57a5f32def71d905f) )

	ROM_REGION( 163920, "screen", 0)
	ROM_LOAD( "gnw_fire.svg", 0, 163920, CRC(be8a9f05) SHA1(644d8bed6228fa7e2f541b60fcfc1a0d97df0df6) )
ROM_END

ROM_START( kosmicmt )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "im-09.bin", 0x0000, 0x0740, CRC(f4c53ef0) SHA1(6b57120a0f9d2fd4dcd65ad57a5f32def71d905f) )

	ROM_REGION( 124578, "screen", 0)
	ROM_LOAD( "kosmicmt.svg", 0, 124578, CRC(913324ef) SHA1(6e72f7f517da754075af11283d71fc8d24ac0529) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Turtle Bridge (model TL-28)
  * PCB label: TL-28
  * Sharp SM510 label TL-28 523C (no decap)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class gnw_tbridge_state : public hh_sm510_state
{
public:
	gnw_tbridge_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		// increase lcd decay: unwanted segments light up
		m_decay_pivot = 25;
		m_decay_len = 25;
	}

	void gnw_tbridge(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_tbridge )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_tbridge_state::gnw_tbridge(machine_config &config)
{
	sm510_common(config, 1587, 1080); // R mask option confirmed
}

// roms

ROM_START( gnw_tbridge )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "tl-28", 0x0000, 0x1000, CRC(284e7224) SHA1(b50d7f3a527ffe50771ef55fdf8214929bfa2253) )

	ROM_REGION( 242944, "screen", 0)
	ROM_LOAD( "gnw_tbridge.svg", 0, 242944, CRC(bf66cb38) SHA1(3f19d1e6584062944e56107d47ebe26335d50f42) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Fire Attack (model ID-29)
  * PCB label: ID-29
  * Sharp SM510 label ID-29 524B (no decap)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class gnw_fireatk_state : public hh_sm510_state
{
public:
	gnw_fireatk_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_fireatk(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_fireatk )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_fireatk_state::gnw_fireatk(machine_config &config)
{
	sm510_common(config, 1655, 1080); // R mask option confirmed
}

// roms

ROM_START( gnw_fireatk )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "id-29", 0x0000, 0x1000, CRC(5f6e8042) SHA1(63afc3acd8a2a996095fa8ba2dfccd48e5214478) )

	ROM_REGION( 267914, "screen", 0)
	ROM_LOAD( "gnw_fireatk.svg", 0, 267914, CRC(f9eea340) SHA1(1fbc224dac447fe3902920ee3f1afc11150b5962) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Snoopy Tennis (model SP-30)
  * PCB label: SP-30
  * Sharp SM510 label SP-30 525B (no decap)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class gnw_stennis_state : public hh_sm510_state
{
public:
	gnw_stennis_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_stennis(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_stennis )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Hit

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_stennis_state::gnw_stennis(machine_config &config)
{
	sm510_common(config, 1581, 1080); // R mask option confirmed
}

// roms

ROM_START( gnw_stennis )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "sp-30", 0x0000, 0x1000, CRC(ba1d9504) SHA1(ff601765d88564b1570a59f5b1a4005c7b0fd66c) )

	ROM_REGION( 228125, "screen", 0)
	ROM_LOAD( "gnw_stennis.svg", 0, 228125, CRC(1134ef9a) SHA1(6f35a4d610c952663761f7ccb74c6650752cac77) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Oil Panic (model OP-51)
  * PCB label: OP-51A
  * Sharp SM510 label OP-51 28ZB (no decap)
  * vertical dual lcd screens with custom segments, 1-bit sound

*******************************************************************************/

class gnw_opanic_state : public hh_sm510_state
{
public:
	gnw_opanic_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_opanic(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_opanic )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_opanic_state::gnw_opanic(machine_config &config)
{
	sm510_dualv(config, 1920/2, 1292/2, 1920/2, 1230/2); // R mask option confirmed
}

// roms

ROM_START( gnw_opanic )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "op-51", 0x0000, 0x1000, CRC(31c288c9) SHA1(4bfd0fba94a9927cefc925db8196b063c5dd9b19) )

	ROM_REGION( 79771, "screen_top", 0)
	ROM_LOAD( "gnw_opanic_top.svg", 0, 79771, CRC(0e1e6485) SHA1(15d5ec48cad65759a50ed624e4161a8f2513f704) )

	ROM_REGION( 112962, "screen_bottom", 0)
	ROM_LOAD( "gnw_opanic_bottom.svg", 0, 112962, CRC(ae4f4f1f) SHA1(97907bea3ca92759a0ea889e80d60d25a701027a) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Donkey Kong (model DK-52)
  * PCB label: DK-52C
  * Sharp SM510 label DK-52 52ZD (no decap)
  * vertical dual lcd screens with custom segments, 1-bit sound

*******************************************************************************/

class gnw_dkong_state : public hh_sm510_state
{
public:
	gnw_dkong_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_dkong(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_dkong )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Jump

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_dkong_state::gnw_dkong(machine_config &config)
{
	sm510_dualv(config, 1920/2, 1266/2, 1920/2, 1266/2); // R mask option confirmed
}

// roms

ROM_START( gnw_dkong )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "dk-52", 0x0000, 0x1000, CRC(5180cbf8) SHA1(5174570a8d6a601226f51e972bac6735535fe11d) )

	ROM_REGION( 176843, "screen_top", 0)
	ROM_LOAD( "gnw_dkong_top.svg", 0, 176843, CRC(16c16b84) SHA1(fa2e54c04366a30b51de024296b9f94c1cb76d68) )

	ROM_REGION( 145516, "screen_bottom", 0)
	ROM_LOAD( "gnw_dkong_bottom.svg", 0, 145516, CRC(2b711e9d) SHA1(0e263020cbe0e8b88bb68e3176630639b518935e) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Mickey & Donald (model DM-53)
  * PCB label: DM-53
  * Sharp SM510 label DM-53 52ZC (die label CMS54C, CMS565)
  * vertical dual lcd screens with custom segments, 1-bit sound

*******************************************************************************/

class gnw_mickdon_state : public hh_sm510_state
{
public:
	gnw_mickdon_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_mickdon(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_mickdon )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_mickdon_state::gnw_mickdon(machine_config &config)
{
	sm510_dualv(config, 1920/2, 1281/2, 1920/2, 1236/2); // R mask option confirmed

	m_maincpu->write_r().set(FUNC(gnw_mickdon_state::piezo_r2_w));
}

// roms

ROM_START( gnw_mickdon )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "dm-53_565", 0x0000, 0x1000, CRC(e21fc0f5) SHA1(3b65ccf9f98813319410414e11a3231b787cdee6) )

	ROM_REGION( 126477, "screen_top", 0)
	ROM_LOAD( "gnw_mickdon_top.svg", 0, 126477, CRC(11e02fce) SHA1(fe2700711c73940a9488a6d223db4c4e92df4188) )

	ROM_REGION( 122915, "screen_bottom", 0)
	ROM_LOAD( "gnw_mickdon_bottom.svg", 0, 122915, CRC(b8cf63c2) SHA1(2406c9826f94a345ca9641e51fb26088f434960c) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Green House (model GH-54)
  * PCB label: GH-54
  * Sharp SM510 label GH-54 52ZD (no decap)
  * vertical dual lcd screens with custom segments, 1-bit sound

  After the 20,000,000th G&W, Nintendo made a special edition of Green House
  (still model GH-54), with the box art showing all the released games so far.

*******************************************************************************/

class gnw_ghouse_state : public hh_sm510_state
{
public:
	gnw_ghouse_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_ghouse(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_ghouse )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Spray

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Invincibility (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_ghouse_state::gnw_ghouse(machine_config &config)
{
	sm510_dualv(config, 1920/2, 1303/2, 1920/2, 1274/2); // R mask option confirmed
}

// roms

ROM_START( gnw_ghouse )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "gh-54", 0x0000, 0x1000, CRC(4df12b4d) SHA1(708be5fef8dbd9337f5ab35baaca5bdf21e1f36c) )

	ROM_REGION( 159258, "screen_top", 0)
	ROM_LOAD( "gnw_ghouse_top.svg", 0, 159258, CRC(308c9c86) SHA1(e83d114e702b6da3cba4e45bd48edfe9882afac1) )

	ROM_REGION( 149922, "screen_bottom", 0)
	ROM_LOAD( "gnw_ghouse_bottom.svg", 0, 149922, CRC(c07c6bb8) SHA1(7a0d6f38ecdbfcd09ab967417fa9d06b5c5c21e4) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Donkey Kong II (model JR-55)
  * PCB label: JR-55
  * Sharp SM510 label JR-55 53YC (die label CMS54C, KMS560)
  * vertical dual lcd screens with custom segments, 1-bit sound

*******************************************************************************/

class gnw_dkong2_state : public hh_sm510_state
{
public:
	gnw_dkong2_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_dkong2(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_dkong2 )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Jump

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Invincibility (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_dkong2_state::gnw_dkong2(machine_config &config)
{
	sm510_dualv(config, 1920/2, 1241/2, 1920/2, 1237/2); // R mask option confirmed
}

// roms

ROM_START( gnw_dkong2 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "jr-55_560", 0x0000, 0x1000, CRC(46aed0ae) SHA1(72f75ccbd84aea094148c872fc7cc1683619a18a) )

	ROM_REGION( 267462, "screen_top", 0)
	ROM_LOAD( "gnw_dkong2_top.svg", 0, 267462, CRC(41bb5414) SHA1(20c7af7c64e12273320029eecc5a33ec65d15bc5) )

	ROM_REGION( 390601, "screen_bottom", 0)
	ROM_LOAD( "gnw_dkong2_bottom.svg", 0, 390601, CRC(3f85bb01) SHA1(8964f02e8372f5d8dd5e8edfe0b79dae31b59b3a) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Mario Bros. (model MW-56)
  * PCB label: MW-56-M-I (left), MW-56-S (right)
  * Sharp SM510 label MW-56 533C (no decap)
  * horizontal dual lcd screens with custom segments, 1-bit sound

*******************************************************************************/

class gnw_mario_state : public hh_sm510_state
{
public:
	gnw_mario_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_mario(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_mario )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_mario_state::gnw_mario(machine_config &config)
{
	sm510_dualh(config, 2258/2, 1440/2, 2261/2, 1440/2); // R mask option confirmed
}

// roms

ROM_START( gnw_mario )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mw-56", 0x0000, 0x1000, CRC(385e59da) SHA1(2f79281bdf2f2afca2fb5bd7b9a3beeffc9c4eb7) )

	ROM_REGION( 154916, "screen_left", 0)
	ROM_LOAD( "gnw_mario_left.svg", 0, 154916, CRC(8ea82355) SHA1(ad286039a215dfa0f02bb1caf875d55dedb9b71e) )

	ROM_REGION( 202902, "screen_right", 0)
	ROM_LOAD( "gnw_mario_right.svg", 0, 202902, CRC(cfe8c0ba) SHA1(87cd54a8104e9bb4f266b137b043e32a0c1d9772) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Rain Shower (model LP-57)
  * PCB labels: LP-57-M-I (left), LP-57-S (right)
  * Sharp SM510 label LP-57 538A (no decap)
  * horizontal dual lcd screens with custom segments, 1-bit sound

*******************************************************************************/

class gnw_rshower_state : public hh_sm510_state
{
public:
	gnw_rshower_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_rshower(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_rshower )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // L/R
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_rshower_state::gnw_rshower(machine_config &config)
{
	sm510_dualh(config, 2126/2, 1440/2, 2146/2, 1440/2); // R mask option confirmed
}

// roms

ROM_START( gnw_rshower )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "lp-57", 0x0000, 0x1000, CRC(51a2c5c4) SHA1(d60542e6785ba7b6a44153a66c739787cf670816) )

	ROM_REGION( 135868, "screen_left", 0)
	ROM_LOAD( "gnw_rshower_left.svg", 0, 135868, CRC(806493f1) SHA1(0287fba2c2962aced8156c2ebc4f299c4703acf2) )

	ROM_REGION( 140445, "screen_right", 0)
	ROM_LOAD( "gnw_rshower_right.svg", 0, 140445, CRC(bead097a) SHA1(a3929e0043ff5132fb4cf7a41edece96926f50d2) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Life Boat (model TC-58)
  * PCB labels: TC-58-M (left), TC-58-S (right)
  * Sharp SM510 label TC-58 281D (no decap)
  * horizontal dual lcd screens with custom segments, 1-bit sound

*******************************************************************************/

class gnw_lboat_state : public hh_sm510_state
{
public:
	gnw_lboat_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_lboat(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_lboat )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Invincibility (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_lboat_state::gnw_lboat(machine_config &config)
{
	sm510_dualh(config, 2116/2, 1440/2, 2057/2, 1440/2); // R mask option confirmed
}

// roms

ROM_START( gnw_lboat )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "tc-58", 0x0000, 0x1000, CRC(1f88f6a2) SHA1(22fd62127dda43a0ada2fe89b0518eec8cbe2a25) )

	ROM_REGION( 156441, "screen_left", 0)
	ROM_LOAD( "gnw_lboat_left.svg", 0, 156441, CRC(a1727890) SHA1(b1dd24f99496d215a3083a138fc3fff923303d34) )

	ROM_REGION( 155258, "screen_right", 0)
	ROM_LOAD( "gnw_lboat_right.svg", 0, 155258, CRC(76619ad3) SHA1(b44d57e2f4a2cecf98e402adf802d16c5934d301) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Pinball (model PB-59)
  * PCB label: PB-59
  * Sharp SM511 label PB-59 53ZD (no decap)
  * vertical dual lcd screens with custom segments, 1-bit sound

*******************************************************************************/

class gnw_pinball_state : public hh_sm510_state
{
public:
	gnw_pinball_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_pinball(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_pinball )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB -- this one multiplies scoring factor
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_pinball_state::gnw_pinball(machine_config &config)
{
	sm511_dualv(config, 1920/2, 1271/2, 1920/2, 1286/2);
}

// roms

ROM_START( gnw_pinball )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "pb-59.program", 0x0000, 0x1000, CRC(d29dab34) SHA1(69ac9ee63eda67360c21627b7d625093709b5cd9) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "pb-59.melody", 0x000, 0x100, BAD_DUMP CRC(5c9ccb55) SHA1(1bada6caf3609f969421087219e6635f4c135282) ) // decap needed for verification

	ROM_REGION( 83191, "screen_top", 0)
	ROM_LOAD( "gnw_pinball_top.svg", 0, 83191, CRC(abe3edd9) SHA1(b32327b81b788896150e709ab8dc4a2155ae0995) )

	ROM_REGION( 63618, "screen_bottom", 0)
	ROM_LOAD( "gnw_pinball_bottom.svg", 0, 63618, CRC(1db44191) SHA1(73f73d246630d0b9efeb8dc72f37a2b88f735ceb) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Black Jack (model BJ-60)
  * PCB label: BJ-60
  * Sharp SM512 label BJ-60 564D (no decap)
  * vertical dual lcd screens with custom segments, 1-bit sound

*******************************************************************************/

class gnw_bjack_state : public hh_sm510_state
{
public:
	gnw_bjack_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_bjack(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_bjack )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY PORT_NAME("Double Down")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY PORT_NAME("Bet x10 / Hit")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY PORT_NAME("Bet x1 / Stand")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY PORT_NAME("Enter")

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void gnw_bjack_state::gnw_bjack(machine_config &config)
{
	sm512_dualv(config, 1920/2, 1290/2, 1920/2, 1297/2);
}

// roms

ROM_START( gnw_bjack )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "bj-60.program", 0x0000, 0x1000, CRC(8e74f633) SHA1(54b0f65ee716d2820a9ed9c743755d2a2d99ce4d) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "bj-60.melody", 0x000, 0x100, BAD_DUMP CRC(2619224e) SHA1(b65dc590b6eb1de793e980af236ccf8360b3cfee) ) // decap needed for verification

	ROM_REGION( 75366, "screen_top", 0)
	ROM_LOAD( "gnw_bjack_top.svg", 0, 75366, CRC(d36fb4e4) SHA1(7f2a0256d78eb01e757208ead0fd52ee63ce8efa) )

	ROM_REGION( 112599, "screen_bottom", 0)
	ROM_LOAD( "gnw_bjack_bottom.svg", 0, 112599, CRC(04880ae1) SHA1(60f3723f81965fe4891f25a3522351872f338389) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Squish (model MG-61)
  * PCB label: MG-61
  * Sharp SM510 label MG-61 8841B (no decap)
  * vertical dual lcd screens with custom segments, 1-bit sound

*******************************************************************************/

class gnw_squish_state : public hh_sm510_state
{
public:
	gnw_squish_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		// increase lcd decay: unwanted segments light up
		m_decay_pivot = 17;
	}

	void gnw_squish(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_squish )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Bonus Life (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_squish_state::gnw_squish(machine_config &config)
{
	sm510_dualv(config, 1920/2, 1285/2, 1920/2, 1287/2); // R mask option confirmed
}

// roms

ROM_START( gnw_squish )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mg-61", 0x0000, 0x1000, CRC(79cd509c) SHA1(969e5425984ba9e5183c68b38b3588f53d1e8e5d) )

	ROM_REGION( 70456, "screen_top", 0)
	ROM_LOAD( "gnw_squish_top.svg", 0, 70456, CRC(8d10b94e) SHA1(33854e7ea8f02adceb597c9ba259aa553953e698) )

	ROM_REGION( 279739, "screen_bottom", 0)
	ROM_LOAD( "gnw_squish_bottom.svg", 0, 279739, CRC(7f4bd704) SHA1(e625910101896cf3a6d41e28ccda77f902f71c7a) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Bomb Sweeper (model BD-62)
  * PCB label: BD-62
  * Sharp SM512 label BD-62 8727 A (no decap)
  * vertical dual lcd screens with custom segments, 1-bit sound

*******************************************************************************/

class gnw_bsweep_state : public hh_sm510_state
{
public:
	gnw_bsweep_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_bsweep(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_bsweep )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Level Skip (Cheat)" ) // " -- Controller keys skips level when activated
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_bsweep_state::gnw_bsweep(machine_config &config)
{
	sm512_dualv(config, 1920/2, 1291/2, 1920/2, 1239/2);
}

// roms

ROM_START( gnw_bsweep )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "bd-62.program", 0x0000, 0x1000, CRC(f3ac66ea) SHA1(3fbf444ade5bc96cf0073ca72f1d583cb0f48fc5) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "bd-62.melody", 0x000, 0x100, BAD_DUMP CRC(addc0368) SHA1(fc488bdf1c2ea5ca84cc66762126bb5874659d8f) ) // decap needed for verification

	ROM_REGION( 218174, "screen_top", 0)
	ROM_LOAD( "gnw_bsweep_top.svg", 0, 218174, CRC(b2c8e895) SHA1(9f7d5973a5f920845c83d30f7ebbbec93232c41e) )

	ROM_REGION( 277420, "screen_bottom", 0)
	ROM_LOAD( "gnw_bsweep_bottom.svg", 0, 277420, CRC(8a9786cb) SHA1(48390a77b0e436ec7d7e8835923faef787e163d4) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Safe Buster (model JB-63)
  * PCB label: JB-63
  * Sharp SM511 label JB-63 8841 B (no decap)
  * vertical dual lcd screens with custom segments, 1-bit sound

*******************************************************************************/

class gnw_sbuster_state : public hh_sm510_state
{
public:
	gnw_sbuster_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_sbuster(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_sbuster )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Invincibility (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_sbuster_state::gnw_sbuster(machine_config &config)
{
	sm511_dualv(config, 1920/2, 1246/2, 1920/2, 1269/2);
}

// roms

ROM_START( gnw_sbuster )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "jb-63.program", 0x0000, 0x1000, CRC(231d358d) SHA1(c748788da125e77b9d0fe1228f64de71f41af42b) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "jb-63.melody", 0x000, 0x100, BAD_DUMP CRC(28cb2914) SHA1(52d34265611f786b597653193752d16563dd5e82) ) // decap needed for verification

	ROM_REGION( 221903, "screen_top", 0)
	ROM_LOAD( "gnw_sbuster_top.svg", 0, 221903, CRC(adb9b67f) SHA1(902998ead1a13d3c26854393283ab622e1fd3f70) )

	ROM_REGION( 282593, "screen_bottom", 0)
	ROM_LOAD( "gnw_sbuster_bottom.svg", 0, 282593, CRC(12542c5e) SHA1(fb05b8f4a2cbeeb566ae111cd27ff486c1478d7b) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Gold Cliff (model MV-64)
  * PCB label: MV-64
  * Sharp SM512 label MV-64 9027 A (no decap)
  * vertical dual lcd screens with custom segments, 1-bit sound

*******************************************************************************/

class gnw_gcliff_state : public hh_sm510_state
{
public:
	gnw_gcliff_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_gcliff(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_gcliff )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Jump
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Continue")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Invincibility (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Level Skip (Cheat)" ) // " -- Left or right skips level when activated
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_gcliff_state::gnw_gcliff(machine_config &config)
{
	sm512_dualv(config, 1920/2, 1257/2, 1920/2, 1239/2);
}

// roms

ROM_START( gnw_gcliff )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mv-64.program", 0x0000, 0x1000, CRC(2448a3bf) SHA1(bfb1a1b500321f8ee0b6f07ef8503e64fe6d37c0) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "mv-64.melody", 0x000, 0x100, BAD_DUMP CRC(cb938709) SHA1(516dcc8a1edffe02f50d349389caac0676de1eba) ) // decap needed for verification

	ROM_REGION( 530731, "screen_top", 0)
	ROM_LOAD( "gnw_gcliff_top.svg", 0, 530731, CRC(3bb60d8f) SHA1(e7dac1fcbe7b682c9d988443c1446e5ad28d3baa) )

	ROM_REGION( 519321, "screen_bottom", 0)
	ROM_LOAD( "gnw_gcliff_bottom.svg", 0, 519321, CRC(1117041e) SHA1(0f87167614c1ba65915fa7205a6bb44778e443a8) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Zelda (model ZL-65)
  * PCB label: ZL-65
  * Sharp SM512 label ZL-65 8935 A (no decap)
  * vertical dual lcd screens with custom segments, 1-bit sound

*******************************************************************************/

class gnw_zelda_state : public hh_sm510_state
{
public:
	gnw_zelda_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_zelda(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_zelda )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) // Water of Life

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Attack
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Continue")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Invincibility (Cheat)" ) // " -- Invincibility when playing on bottom screen only
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_zelda_state::gnw_zelda(machine_config &config)
{
	sm512_dualv(config, 1920/2, 1346/2, 1920/2, 1291/2);
}

// roms

ROM_START( gnw_zelda )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "zl-65.program", 0x0000, 0x1000, CRC(b96aa64e) SHA1(d1f0c64104eb3ecbf370674d5078a3a85b2b7227) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "zl-65.melody", 0x000, 0x100, BAD_DUMP CRC(3a281b0f) SHA1(7a236775557939050bbcd6f9d0a598d219a032f2) ) // decap needed for verification

	ROM_REGION( 283029, "screen_top", 0)
	ROM_LOAD( "gnw_zelda_top.svg", 0, 283029, CRC(aaab1d7e) SHA1(fe01e8a92e6dcf457da87afe6bf39fcf511da9db) )

	ROM_REGION( 424886, "screen_bottom", 0)
	ROM_LOAD( "gnw_zelda_bottom.svg", 0, 424886, CRC(09f00d09) SHA1(33045028bd7e0df4e976e79dc180028c6886359a) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Mario's Cement Factory (model CM-72)
  * PCB labels: CM-72 M (main board), CM-72 C (joystick controller board),
    CM-72 S (buttons controller board)
  * Sharp SM511 label CM-72 534A, or CM-72A 536C (no decap)
  * inverted lcd screen with custom segments, 1-bit sound

  This is the tabletop version. There's also a new wide screen version which is
  a different game. Unlike the other tabletop games, there is no panorama version.
  There are two known versions, distinguished by the startup jingle. The first
  version sounds like Queen's "Another One Bites the Dust".

*******************************************************************************/

class gnw_mariocmt_state : public hh_sm510_state
{
public:
	gnw_mariocmt_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_mariocmt(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_mariocmt )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Open
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_mariocmt_state::gnw_mariocmt(machine_config &config)
{
	sm511_common(config, 1920, 1046);
}

// roms

ROM_START( gnw_mariocmt )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "cm-72.program", 0x0000, 0x1000, CRC(b2ae4596) SHA1(f64bf11e18c9fbd4de4134f685bb2d7bda3d7487) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "cm-72.melody", 0x000, 0x100, BAD_DUMP CRC(db4f0fc1) SHA1(e386df3e3e88fa36a73bcd0649feb904180493c8) ) // decap needed for verification

	ROM_REGION( 293317, "screen", 0)
	ROM_LOAD( "gnw_mariocmt.svg", 0, 293317, CRC(4f969dc7) SHA1(fec72c4a8600c0753f81bfb296b53cca6aee14cc) )
ROM_END

ROM_START( gnw_mariocmta )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "cm-72a.program", 0x0000, 0x1000, CRC(b2ae4596) SHA1(f64bf11e18c9fbd4de4134f685bb2d7bda3d7487) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "cm-72a.melody", 0x000, 0x100, BAD_DUMP CRC(b6d72560) SHA1(9d7c23f94b7f894ba1b7881f68824949702a37f2) ) // decap needed for verification

	ROM_REGION( 293317, "screen", 0)
	ROM_LOAD( "gnw_mariocmt.svg", 0, 293317, CRC(4f969dc7) SHA1(fec72c4a8600c0753f81bfb296b53cca6aee14cc) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Snoopy (model SM-91)
  * PCB labels: SM-91 M (main board), SM-91C (controller board)
  * Sharp SM511 label SM-91 538A (no decap)
  * inverted lcd screen with custom segments, 1-bit sound

  This is the panorama version. There's also a tabletop version which is
  assumed to use the same ROM/LCD.

*******************************************************************************/

class gnw_snoopyp_state : public hh_sm510_state
{
public:
	gnw_snoopyp_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_snoopyp(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_snoopyp )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Hit
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_snoopyp_state::gnw_snoopyp(machine_config &config)
{
	sm511_common(config, 1920, 1020);
}

// roms

ROM_START( gnw_snoopyp )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "sm-91.program", 0x0000, 0x1000, CRC(893bd7e3) SHA1(94e218f464b2ec8c81bd4c0f13f3a3049c4effe9) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "sm-91.melody", 0x000, 0x100, BAD_DUMP CRC(09360aaf) SHA1(906eff1d2eaf7ff040d833b4513a995e7026279b) ) // decap needed for verification

	ROM_REGION( 353488, "screen", 0)
	ROM_LOAD( "gnw_snoopyp.svg", 0, 353488, CRC(30cfa42e) SHA1(2abe74299db7241c66f9631b01d0ea336ec411ad) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Popeye (model PG-92)
  * PCB labels: PG-92 M (main board), SM-91C (controller board)
  * Sharp SM511 label PG-92 538A (no decap)
  * inverted lcd screen with custom segments, 1-bit sound

  This is the panorama version. There's also a tabletop version which is
  assumed to use the same ROM/LCD, and a new wide screen version which is
  a different game.

  The PCB design for the controller board is shared with the panorama version
  of Snoopy (SM-91).

*******************************************************************************/

class gnw_popeyep_state : public hh_sm510_state
{
public:
	gnw_popeyep_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_popeyep(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_popeyep )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Punch
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives and Stronger Punch (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_popeyep_state::gnw_popeyep(machine_config &config)
{
	sm511_common(config, 1920, 1043);
}

// roms

ROM_START( gnw_popeyep )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "pg-92.program", 0x0000, 0x1000, CRC(f9a2f181) SHA1(f97969abe63285964ef9585660e82590014bbece) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "pg-92.melody", 0x000, 0x100, BAD_DUMP CRC(ce2a0e03) SHA1(cb7e4c64639579349aa944e4bfff7b05cf49ce0e) ) // decap needed for verification

	ROM_REGION( 541218, "screen", 0)
	ROM_LOAD( "gnw_popeyep.svg", 0, 541218, CRC(ad93aa24) SHA1(b02c1fec1d8388878b5f21887f19aa5007b8ae43) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Donkey Kong Jr. (model CJ-93)
  * PCB labels: CJ-93 M (main board), CJ-93C (controller board)
  * Sharp SM511 label CJ-93 539D (no decap)
  * inverted lcd screen with custom segments, 1-bit sound

  This is the panorama version. There's also a tabletop version which is
  assumed to use the same ROM/LCD, and a new wide screen version which is
  a different game.

  The tabletop version was also licensed to Coleco.

  Also cloned by Elektronika(USSR) as Винни-Пух (Vinni-Pukh, i.e. Winnie the
  Pooh) (model ИМ-12), with different LCD graphics.
  * КБ1515ХМ3-2 9009 (no decap); seems to be compatible with КБ1013ВК7-2
    which in turn is compatible with Sharp SM511 (same ROM contents as CJ-93)

*******************************************************************************/

class gnw_dkjrp_state : public hh_sm510_state
{
public:
	gnw_dkjrp_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_dkjrp(machine_config &config);
	void vinnpukh(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_dkjrp )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Jump

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_dkjrp_state::gnw_dkjrp(machine_config &config)
{
	sm511_common(config, 1920, 1049);
}

void gnw_dkjrp_state::vinnpukh(machine_config &config)
{
	sm511_common(config, 1890, 1080); // 1638 x 936
}

// roms

ROM_START( gnw_dkjrp )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "cj-93.program", 0x0000, 0x1000, CRC(a2cd5a91) SHA1(33f6fd1530e5522491851f16d7c9f928b2dbdc3b) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "cj-93.melody", 0x000, 0x100, BAD_DUMP CRC(38946be7) SHA1(affdb8514d32dc9dbf2ccc1c4a0394e68ebc61cb) ) // decap needed for verification

	ROM_REGION( 340751, "screen", 0)
	ROM_LOAD( "gnw_dkjrp.svg", 0, 340751, CRC(eb3cb98b) SHA1(5b148557d3ade2e2050ddde879a6cc05e119b446) )
ROM_END

ROM_START( vinnpukh )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "vinnpukh.program", 0x0000, 0x1000, CRC(a2cd5a91) SHA1(33f6fd1530e5522491851f16d7c9f928b2dbdc3b) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "vinnpukh.melody", 0x000, 0x100, BAD_DUMP CRC(38946be7) SHA1(affdb8514d32dc9dbf2ccc1c4a0394e68ebc61cb) ) // decap needed for verification

	ROM_REGION( 193376, "screen", 0)
	ROM_LOAD( "vinnpukh.svg", 0, 193376, CRC(69c4e5c5) SHA1(313f164706c3d24e2fc1363ff9730a21e5b1ca6c) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Mario's Bombs Away (model TB-94)
  * PCB labels: TB-94 M (main board), SM-91C (controller board)
  * Sharp SM511 label TB-94 537C (no decap)
  * inverted lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class gnw_mbaway_state : public hh_sm510_state
{
public:
	gnw_mbaway_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_mbaway(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_mbaway )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Up/Down
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Invincibility (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_mbaway_state::gnw_mbaway(machine_config &config)
{
	sm511_common(config, 1920, 1031);
}

// roms

ROM_START( gnw_mbaway )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "tb-94.program", 0x0000, 0x1000, CRC(11d18a48) SHA1(afccfa19dace7c4fcc15a84ecfcfb9d7ae3861e4) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "tb-94.melody", 0x000, 0x100, BAD_DUMP CRC(60d98353) SHA1(8789d7cd39111fe01848a89748ab91731de5caef) ) // decap needed for verification

	ROM_REGION( 514643, "screen", 0)
	ROM_LOAD( "gnw_mbaway.svg", 0, 514643, CRC(2ec2f18b) SHA1(8e2fd20615d867aac97e443fb977513ff98138b4) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Mickey Mouse (model DC-95),
  Nintendo Game & Watch: Donkey Kong Circus (model MK-96)
  * PCB labels: DC-95M (main board), DC-95C (controller board)
  * Sharp SM511:
    label DC-95 284C (Mickey Mouse) (no decap)
    label DC-95 541D (Donkey Kong Circus) (no decap)
  * inverted lcd screen with custom segments, 1-bit sound

  This is the panorama version of Mickey Mouse. There's also a wide screen
  version which is a different game.

  DC-95 and MK-96 are the same game, it's assumed that the latter was for
  regions where Nintendo wasn't able to license from Disney.

*******************************************************************************/

class gnw_mmousep_state : public hh_sm510_state
{
public:
	gnw_mmousep_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_mmousep(machine_config &config);
	void gnw_dkcirc(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_mmousep )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_mmousep_state::gnw_mmousep(machine_config &config)
{
	sm511_common(config, 1920, 1122);
}

void gnw_mmousep_state::gnw_dkcirc(machine_config &config)
{
	sm511_common(config, 1920, 1107);
}

// roms

ROM_START( gnw_mmousep )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "dc-95.program", 0x0000, 0x1000, CRC(39dd864a) SHA1(25c67dac7320fe00990989cd42438461950a68ec) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "dc-95.melody", 0x000, 0x100, BAD_DUMP CRC(6ccde8e3) SHA1(4e704a1d61126465b14e3889b4a0179c5568b90b) ) // decap needed for verification

	ROM_REGION( 275609, "screen", 0)
	ROM_LOAD( "gnw_mmousep.svg", 0, 275609, CRC(bac13689) SHA1(3ddcb4416bc5b8615b2854434ef78acac204a583) )
ROM_END

ROM_START( gnw_dkcirc )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mk-96.program", 0x0000, 0x1000, CRC(39dd864a) SHA1(25c67dac7320fe00990989cd42438461950a68ec) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "mk-96.melody", 0x000, 0x100, BAD_DUMP CRC(6ccde8e3) SHA1(4e704a1d61126465b14e3889b4a0179c5568b90b) ) // decap needed for verification

	ROM_REGION( 367718, "screen", 0)
	ROM_LOAD( "gnw_dkcirc.svg", 0, 367718, CRC(f8571437) SHA1(bc000267deab83dfd460aea5c4102a23ac51f169) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Donkey Kong Jr. (model DJ-101)
  * Sharp SM510 label DJ-101 52ZA (no decap)
  * lcd screen with custom segments, 1-bit sound

  This is the new wide screen version, there's also a tabletop version that
  plays more like the arcade game.

*******************************************************************************/

class gnw_dkjr_state : public hh_sm510_state
{
public:
	gnw_dkjr_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_dkjr(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_dkjr )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Jump

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Invincibility (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_dkjr_state::gnw_dkjr(machine_config &config)
{
	sm510_common(config, 1647, 1080); // R mask option confirmed
}

// roms

ROM_START( gnw_dkjr )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "dj-101", 0x0000, 0x1000, CRC(8dcfb5d1) SHA1(e0ef578e9362eb9a3cab631376df3cf55978f2de) )

	ROM_REGION( 281202, "screen", 0)
	ROM_LOAD( "gnw_dkjr.svg", 0, 281202, CRC(f8b18d58) SHA1(fa8321b3d8f81685da763d66fc148d339e6bcd55) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Mario's Cement Factory (model ML-102)
  * Sharp SM510 label ML-102 298D (die label CMS54C, KMS577)
  * lcd screen with custom segments, 1-bit sound

  This is the new wide screen version, there's also a tabletop version.

*******************************************************************************/

class gnw_mariocm_state : public hh_sm510_state
{
public:
	gnw_mariocm_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_mariocm(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_mariocm )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Open
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_mariocm_state::gnw_mariocm(machine_config &config)
{
	sm510_common(config, 1647, 1080); // R mask option confirmed
}

// roms

ROM_START( gnw_mariocm )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "ml-102_577", 0x0000, 0x1000, CRC(c1128dea) SHA1(8647e36f43a0e37756a3c7b6a3f08d4c8243f1cc) )

	ROM_REGION( 302983, "screen", 0)
	ROM_LOAD( "gnw_mariocm.svg", 0, 302983, CRC(32ed7941) SHA1(ce7c5ae7a179ec9bcd17db7d7a27780801f7c1cb) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Manhole (model NH-103)
  * PCB label: NH-103
  * Sharp SM510 label NH-103 538A (no decap)
  * lcd screen with custom segments, 1-bit sound

  This is the new wide screen version, there's also a Gold Series version
  (MH-06). The two games are using different MCU types so this version seems
  to be a complete rewrite.

*******************************************************************************/

class gnw_manhole_state : public hh_sm510_state
{
public:
	gnw_manhole_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_manhole(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_manhole )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Invincibility (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_manhole_state::gnw_manhole(machine_config &config)
{
	sm510_common(config, 1560, 1080); // R mask option confirmed
}

// roms

ROM_START( gnw_manhole )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "nh-103", 0x0000, 0x1000, CRC(ec03acf7) SHA1(b74ae672d8f8a155b2ea4ecee9afbaed95ec0ceb) )

	ROM_REGION( 223414, "screen", 0)
	ROM_LOAD( "gnw_manhole.svg", 0, 223414, CRC(774d806b) SHA1(acb730d8e397eb29988a353e0a9db8ae69913117) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Tropical Fish (model TF-104)
  * PCB label: TF-104
  * Sharp SM510 label TF-104 8739A (no decap)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class gnw_tfish_state : public hh_sm510_state
{
public:
	gnw_tfish_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_tfish(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_tfish )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_tfish_state::gnw_tfish(machine_config &config)
{
	sm510_common(config, 1572, 1080); // R mask option confirmed
}

// roms

ROM_START( gnw_tfish )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "tf-104", 0x0000, 0x1000, CRC(53cde918) SHA1(bc1e1b8f8b282bb886bb076c1c7ce35d00eca6fc) )

	ROM_REGION( 257396, "screen", 0)
	ROM_LOAD( "gnw_tfish.svg", 0, 257396, CRC(6f457a30) SHA1(0b748c9573ff96b99f4fa0adb17d218e89b56d3f) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Super Mario Bros. (model: see below)
  * PCB label: YM-801 (Crystal Screen), YM-105 (New Wide Screen)
  * Sharp SM511:
    label YM-801 8034A (Crystal Screen) (not dumped yet)
    label YM-105 9024B (New Wide Screen) (die label ?)
  * lcd screen with custom segments, 1-bit sound

  First released in 1986 on Crystal Screen (model YM-801), rereleased on
  New Wide Screen in 1988 (model YM-105). It was also a prize in a Nintendo
  game contest in 1987 (model YM-901-S). In YM-801, Mario looks like the
  ones in ML-102 and MW-56. In YM-901-S and YM-105 he looks more detailed.
  Until further proof, it's assumed that the ROM is the same for each model.

*******************************************************************************/

class gnw_smb_state : public hh_sm510_state
{
public:
	gnw_smb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_smb(machine_config &config);
	void gnw_smbn(machine_config & config);
};

// inputs

static INPUT_PORTS_START( gnw_smb )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Jump
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_smb_state::gnw_smb(machine_config &config)
{
	sm511_common(config, 1768, 1080);
}

void gnw_smb_state::gnw_smbn(machine_config &config)
{
	sm511_common(config, 1677, 1080);
}

// roms

ROM_START( gnw_smb )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "ym-801.program", 0x0000, 0x1000, BAD_DUMP CRC(0dff3b12) SHA1(3fa83f88e49ea9d7080fe935ec90ce69acbe8850) ) // dumped from NWS version

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "ym-801.melody", 0x000, 0x100, BAD_DUMP CRC(b48c6d90) SHA1(a1ce1e52627767752974ab0d49bec48ead36663e) ) // dumped from NWS version

	ROM_REGION( 342106, "screen", 0)
	ROM_LOAD( "gnw_smb.svg", 0, 342106, CRC(243224ac) SHA1(9b7f41abe4e340e32893ff1ef6e4d696deadc637) )
ROM_END

ROM_START( gnw_smbn )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "ym-105.program", 0x0000, 0x1000, CRC(0dff3b12) SHA1(3fa83f88e49ea9d7080fe935ec90ce69acbe8850) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "ym-105.melody", 0x000, 0x100, CRC(b48c6d90) SHA1(a1ce1e52627767752974ab0d49bec48ead36663e) )

	ROM_REGION( 648313, "screen", 0)
	ROM_LOAD( "gnw_smbn.svg", 0, 648313, CRC(5808c793) SHA1(06b90993eb9db2a1909509f99ebf00e27c20dcad) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Climber Crystal Screen (model DR-802),
  Nintendo Game & Watch: Climber New Wide Screen (model DR-106)
  * PCB label: DR-802 (Crystal Screen), DR-106 (New Wide Screen)
  * Sharp SM511:
    label DR-802 8626A (Crystal Screen) (not dumped yet)
    label DR-106 9038B (New Wide Screen) (no decap)
  * lcd screen with custom segments, 1-bit sound

  First released in 1986 on Crystal Screen (model DR-802), rereleased on
  New Wide Screen in 1988 (model DR-106). The graphic LCD elements look the same
  in both versions but the display aspect ratio and the graphical background is
  slightly different. Until further proof, it's assumed that the ROM is the same
  for both models.

*******************************************************************************/

class gnw_climber_state : public hh_sm510_state
{
public:
	gnw_climber_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_climber(machine_config &config);
	void gnw_climbern(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_climber )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Jump
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_climber_state::gnw_climber(machine_config &config)
{
	sm511_common(config, 1756, 1080);
}

void gnw_climber_state::gnw_climbern(machine_config &config)
{
	sm511_common(config, 1677, 1080);
}

// roms

ROM_START( gnw_climber )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "dr-802.program", 0x0000, 0x1000, BAD_DUMP CRC(2adcbd6d) SHA1(110dc08c65120ab2c76ee647e89aa2726e24ac1a) ) // dumped from NWS version

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "dr-802.melody", 0x000, 0x100, BAD_DUMP CRC(7c49a3a3) SHA1(fad00d650b4864135c7d50f6fae735b7fffe720f) ) // dumped from NWS version

	ROM_REGION( 564868, "screen", 0)
	ROM_LOAD( "gnw_climber.svg", 0, 564868, CRC(a50ebd1c) SHA1(51047db960c8f110c1b681347cf8efd1d6263b85) )
ROM_END

ROM_START( gnw_climbern )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "dr-106.program", 0x0000, 0x1000, CRC(2adcbd6d) SHA1(110dc08c65120ab2c76ee647e89aa2726e24ac1a) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "dr-106.melody", 0x000, 0x100, BAD_DUMP CRC(7c49a3a3) SHA1(fad00d650b4864135c7d50f6fae735b7fffe720f) ) // decap needed for verification

	ROM_REGION( 542453, "screen", 0)
	ROM_LOAD( "gnw_climbern.svg", 0, 542453, CRC(2ded966e) SHA1(7e9c99d372b6e547b9b3e789dca9dee60455a427) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Balloon Fight Crystal Screen (model BF-803),
  Nintendo Game & Watch: Balloon Fight New Wide Screen (model BF-107)
  * PCB label: DR-802-2 (Crystal Screen), DR-106 (New Wide Screen)
  * Sharp SM511:
    label BF-803 8646A (Crystal Screen) (not dumped yet)
    label BF-107 9031B (New Wide Screen) (no decap)
  * lcd screen with custom segments, 1-bit sound

  First released in 1986 on Crystal Screen (model BF-803), rereleased on
  New Wide Screen in 1988 (model BF-107). The graphic LCD elements look the same
  in both versions but the graphical background is slightly different.
  Until further proof, it's assumed that the ROM is the same for both models.

  The PCB design for the different editions seems to be shared with the
  corresponding editions of Climber.

*******************************************************************************/

class gnw_bfight_state : public hh_sm510_state
{
public:
	gnw_bfight_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_bfight(machine_config &config);
	void gnw_bfightn(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_bfight )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Eject
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_bfight_state::gnw_bfight(machine_config &config)
{
	sm511_common(config, 1771, 1080);
}

void gnw_bfight_state::gnw_bfightn(machine_config &config)
{
	sm511_common(config, 1549, 1080);
}

// roms

ROM_START( gnw_bfight )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "bf-803.program", 0x0000, 0x1000, BAD_DUMP CRC(4c8d07ed) SHA1(a8974dff85d5f3bacaadb71b86e9b30994b6d129) ) // dumped from NWS version

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "bf-803.melody", 0x000, 0x100, BAD_DUMP CRC(ffddf9ed) SHA1(e9cb3a340924363eeef5ab453c452b9cc69207b9) ) // dumped from NWS version

	ROM_REGION( 586453, "screen", 0)
	ROM_LOAD( "gnw_bfight.svg", 0, 586453, CRC(40d81b65) SHA1(96ed909647229cfde6d733ba10d54ace29e5618a) )
ROM_END

ROM_START( gnw_bfightn )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "bf-107.program", 0x0000, 0x1000, CRC(4c8d07ed) SHA1(a8974dff85d5f3bacaadb71b86e9b30994b6d129) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "bf-107.melody", 0x000, 0x100, BAD_DUMP CRC(ffddf9ed) SHA1(e9cb3a340924363eeef5ab453c452b9cc69207b9) ) // decap needed for verification

	ROM_REGION( 558496, "screen", 0)
	ROM_LOAD( "gnw_bfightn.svg", 0, 558496, CRC(c488000e) SHA1(f9a042799a1489f83b07a91827b8b421238a67e8) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Mario The Juggler (model MB-108)
  * PCB label: MB-108
  * Sharp SM511 label MB-108 9209B (no decap)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class gnw_mariotj_state : public hh_sm510_state
{
public:
	gnw_mariotj_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_mariotj(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_mariotj )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_mariotj_state::gnw_mariotj(machine_config &config)
{
	sm511_common(config, 1630, 1080);
}

// roms

ROM_START( gnw_mariotj )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mb-108.program", 0x0000, 0x1000, CRC(f7118bb4) SHA1(c3117fd009e4686a149f85fb65786ddffc091eeb) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "mb-108.melody", 0x000, 0x100, BAD_DUMP CRC(d8cc1f74) SHA1(4bbb470ef01777b0c1dbd7b84dc560da6d3b87e7) ) // decap needed for verification

	ROM_REGION( 210391, "screen", 0)
	ROM_LOAD( "gnw_mariotj.svg", 0, 210391, CRC(8f1e6118) SHA1(4ecad443142330470384659af1e8dd59bca519e4) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Spitball Sparky (model BU-201)
  * PCB label: BU-201
  * Sharp SM510 label BU-201 542A (no decap)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class gnw_ssparky_state : public hh_sm510_state
{
public:
	gnw_ssparky_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_ssparky(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_ssparky )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Shooter
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_ssparky_state::gnw_ssparky(machine_config &config)
{
	sm510_common(config, 627, 1080); // R mask option confirmed
}

// roms

ROM_START( gnw_ssparky )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "bu-201", 0x0000, 0x1000, CRC(ae0d28e7) SHA1(1427cca1f3aaf3ef6fc3499171a5220428d9894f) )

	ROM_REGION( 136929, "screen", 0)
	ROM_LOAD( "gnw_ssparky.svg", 0, 136929, CRC(66e5d586) SHA1(b666f675abb8edef65ff402e8bc9a5213b630851) )
ROM_END





/*******************************************************************************

  Nintendo Game & Watch: Crab Grab (model UD-202)
  * PCB label: UD-202
  * Sharp SM510 label UD-202 542B (no decap)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class gnw_cgrab_state : public hh_sm510_state
{
public:
	gnw_cgrab_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_cgrab(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_cgrab )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Do not release crabs (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_cgrab_state::gnw_cgrab(machine_config &config)
{
	sm510_common(config, 609, 1080); // R mask option confirmed
}

// roms

ROM_START( gnw_cgrab )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "ud-202", 0x0000, 0x1000, CRC(65e97963) SHA1(f6d589fac337e2c4acdaa8f1281912feabc54198) )

	ROM_REGION( 354770, "screen", 0)
	ROM_LOAD( "gnw_cgrab.svg", 0, 354770, CRC(d61478aa) SHA1(8dd44cfb3720740150defdfbebe0bd52a3b3a377) )
ROM_END





/*******************************************************************************

  Nintendo Micro Vs. System: Boxing (model BX-301)
  * Sharp SM511 label BX-301 287C (die label KMS73B, KMS744)
  * wide lcd screen with custom segments, 1-bit sound

  Also known as Punch-Out!! in the USA.

*******************************************************************************/

class gnw_boxing_state : public hh_sm510_state
{
public:
	gnw_boxing_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_boxing(machine_config &config);
};

// inputs

static INPUT_PORTS_START( microvs_shared )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_PLAYER(2)

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_PLAYER(2)

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_PLAYER(2)

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // S7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

static INPUT_PORTS_START( gnw_boxing )
	PORT_INCLUDE( microvs_shared )

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "P2 Decrease Health (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "P1 Infinite Health (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_boxing_state::gnw_boxing(machine_config &config)
{
	sm511_common(config, 1920, 524);
}

// roms

ROM_START( gnw_boxing )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "bx-301_744.program", 0x0000, 0x1000, CRC(0fdf0303) SHA1(0b791c9d4874e9534d0a9b7a8968ce02fe4bee96) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "bx-301_744.melody", 0x000, 0x100, CRC(439d943d) SHA1(52880df15ec7513f96482f455ef3d9778aa24750) )

	ROM_REGION( 265217, "screen", 0)
	ROM_LOAD( "gnw_boxing.svg", 0, 265217, CRC(306c733e) SHA1(8c80df1295ff0889e16ef9a14e45b27a6ebaa9a2) )
ROM_END





/*******************************************************************************

  Nintendo Micro Vs. System: Donkey Kong 3 (model AK-302)
  * PCB label: AK-302M
  * Sharp SM511 label AK-302 299D (no decap)
  * wide lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class gnw_dkong3_state : public hh_sm510_state
{
public:
	gnw_dkong3_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_dkong3(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_dkong3 )
	PORT_INCLUDE( microvs_shared )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "P1 Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_dkong3_state::gnw_dkong3(machine_config &config)
{
	sm511_common(config, 1920, 563);
}

// roms

ROM_START( gnw_dkong3 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "ak-302.program", 0x0000, 0x1000, CRC(ed59c15e) SHA1(94f6ce23677d2150c9f86c4b1954f5f531693b21) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "ak-302.melody", 0x000, 0x100, BAD_DUMP CRC(8b8f3d55) SHA1(54ebdeff4dd56a8bc2cd39bca1deada14bb90cce) ) // decap needed for verification

	ROM_REGION( 292480, "screen", 0)
	ROM_LOAD( "gnw_dkong3.svg", 0, 292480, CRC(980d2486) SHA1(8578bdf4a3814401d9a79867252ee09ed7df253c) )
ROM_END





/*******************************************************************************

  Nintendo Micro Vs. System: Donkey Kong Hockey (model HK-303)
  * PCB label: HK-303M
  * Sharp SM511 label HK-303 57XD (no decap)
  * wide lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class gnw_dkhockey_state : public hh_sm510_state
{
public:
	gnw_dkhockey_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void gnw_dkhockey(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gnw_dkhockey )
	PORT_INCLUDE( microvs_shared )

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Any Goal Scores 10 Points (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "P2 Goals Scores No Points (Cheat)" ) // "
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void gnw_dkhockey_state::gnw_dkhockey(machine_config &config)
{
	sm511_common(config, 1920, 579);
}

// roms

ROM_START( gnw_dkhockey )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "hk-303.program", 0x0000, 0x1000, CRC(dc73eec7) SHA1(daaca286de326321335fd26d9b435b444787f609) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "hk-303.melody", 0x000, 0x100, BAD_DUMP CRC(3f61032c) SHA1(b0fd9077fb5e59ca2787c828c78c35116c48c245) ) // decap needed for verification

	ROM_REGION( 263239, "screen", 0)
	ROM_LOAD( "gnw_dkhockey.svg", 0, 263239, CRC(3a576c12) SHA1(9a7ca67c35fcfb5858227f3ef2a6027c877c64d3) )
ROM_END





/*******************************************************************************

  Telko Bassmate Computer (model BM-501)
  * PCB label: BM-501
  * Sharp SM511 label BM-501 556AA (no decap)
  * vertical dual lcd screens with custom segments, 1-bit sound

  The Bassmate Computer was produced for Telko by Nintendo as an OEM product
  and sold under different brands, i.e. Telko, KMV and Probe 2000.

  The hardware is identical as G&W Multi Screen, but it's not part of the game
  series.

*******************************************************************************/

class bassmate_state : public hh_sm510_state
{
public:
	bassmate_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void bassmate(machine_config &config);
};

// inputs

static INPUT_PORTS_START( bassmate )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Compute")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON8 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Wind")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Cover")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time of Day")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Water Temp F")

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Water Clarity")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Structure")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Water Depth(ft)")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Season")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Skip Compute Animation (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

// config

void bassmate_state::bassmate(machine_config &config)
{
	sm511_dualv(config, 1920/2, 1253/2, 1920/2, 1273/2);
}

// roms

ROM_START( bassmate )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "bm-501.program", 0x0000, 0x1000, CRC(9bdd0501) SHA1(986b3b84184a987ae383c325700df21d8915f0e2) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "bm-501.melody", 0x000, 0x100, BAD_DUMP CRC(fbe15600) SHA1(8be64792fffe5b8913a55b9b2624dd57dc238be7) ) // decap needed for verification

	ROM_REGION( 42305, "screen_top", 0)
	ROM_LOAD( "bassmate_top.svg", 0, 42305, CRC(0cc056fe) SHA1(4d0e5b115adf513f5b3148ca7e39e0acbafd925c) )

	ROM_REGION( 19775, "screen_bottom", 0)
	ROM_LOAD( "bassmate_bottom.svg", 0, 19775, CRC(9561d52d) SHA1(903ef3944810c0efdc02f46a619891c1ef17c483) )
ROM_END





/*******************************************************************************

  Elektronika Nu, pogodi! family (Egg game clones)
  * KB1013VK1-2 MCU
  * lcd screen with custom segments, 1-bit sound

  In 1983, Электроника (Elektronika, USSR) released an unlicensed clone of
  Nintendo G&W Egg: Ну, погоди! (Nu, pogodi!). This was followed by several other
  titles that were the same under the hood, only differing in graphics. They also
  made a slightly modified version, adding a new game mode (by pressing A+B)
  where the player/CPU roles are reversed. This version is known as Разведчики
  космоса (Razvedchiki kosmosa, export version: Explorers of Space).

  Another variant of the game which also included a radiation scintillation
  counter was released by Научприбор (Nauchpribor, USSR) in 1991. This unit was
  named Альтаир (Altair). This unit uses the same screen as ИМ-22 (Весёлые
  футболисты, export version: Monkey Goalkeeper). The ROM has been modified to
  include showing radiation exposure ("Dosimeter Mode"). The dosimeter mode can
  be entered by pressing the dosimeter mode button when the unit is showing time.
  Radiation readings are shown in µSv/h. The dosimeter mode ends automatically
  after 40 seconds. A gas-discharge counter (SBM-20-1) collects radiation exposure
  and feeds info to the game board via the D0-D3 input lines.

  The following Mickey Mouse Elektronika clones are emulated in MAME:

  Model    Title               Transliteration      Export version      Note
  --------------------------------------------------------------------------------
  ИМ-02    Ну, погоди!         Nu, pogodi!          -                   -
  ИМ-10    Хоккей              Hockey (Khokkey)     Ice Hockey          Export version manufactured by PO Proton
  ИМ-13    Разведчики космоса  Razvedchiki kosmosa  Explorers of Space  Modified ROM (see note above)
  ИМ-16    Охота               Okhota               Fowling             -
  ИМ-19    Биатлон             Biathlon (Biatlon)   -                   -
  ИМ-22    Весёлые футболисты  Vesyolye futbolisty  Monkey Goalkeeper   -
  ИМ-32    Кот-рыболов         Kot-rybolov          -                   -
  ИМ-33    Квака-задавака      Kvaka-zadavaka       Frogling            -
  ИМ-49    Ночные воришки      Nochnye vorishki     Night Burglars      -
  ИМ-50    Космический полёт   Kosmicheskiy polyot  Space Flight        Same Model ID as Весёлая арифметика (Amusing Arithmetic)
  ИМ-51    Морская атака       Morskaya ataka       -                   -
  ИМ-53    Атака астероидов    Ataka asteroidov     -                   Graphics are very similar to ИМ-50
  -        Цирк                Circus (Tsirk)       -                   Unknown Model ID
  ДБГБ-06И Альтаир             Altair               -                   Modified ROM (see note above)

*******************************************************************************/

class nupogodi_state : public hh_sm510_state
{
public:
	nupogodi_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void nupogodi(machine_config &config);
	void ehockey(machine_config &config);
	void rkosmosa(machine_config &config);
	void okhota(machine_config &config);
	void biathlon(machine_config &config);
	void vfutbol(machine_config &config);
	void krybolov(machine_config &config);
	void kvakazad(machine_config &config);
	void nochnyev(machine_config &config);
	void kosmicpt(machine_config &config);
	void morataka(machine_config &config);
	void atakaast(machine_config &config);
	void ecircus(machine_config &config);
	void naltair(machine_config &config);
};

// inputs

static INPUT_PORTS_START( rkosmosa )
	PORT_INCLUDE( gnw_mmouse )

	PORT_MODIFY("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( naltair )
	PORT_INCLUDE( gnw_mmouse )

	PORT_MODIFY("IN.0") // R2
	PORT_BIT( 0x0f, 0x00, IPT_DIAL ) PORT_CHANGED_CB(input_changed) PORT_SENSITIVITY(10) PORT_KEYDELTA(2) PORT_CODE_DEC(INPUT_CODE_INVALID) PORT_NAME("Dosimeter Reading")

	PORT_MODIFY("IN.1") // R3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY PORT_NAME("P1 Right Stick/Up / Dosimeter Mode")
INPUT_PORTS_END

// config

void nupogodi_state::nupogodi(machine_config &config)
{
	sm5a_common(config, 1715, 1080); // КБ1013ВК1-2, R mask option ?
}

void nupogodi_state::ehockey(machine_config &config)
{
	sm5a_common(config, 1782, 1080); // КБ1013ВК1-2, R mask option ?
}

void nupogodi_state::rkosmosa(machine_config &config)
{
	sm5a_common(config, 1646, 1080); // КБ1013ВК1-2, R mask option ?
}

void nupogodi_state::okhota(machine_config &config)
{
	sm5a_common(config, 1632, 1080); // КБ1013ВК1-2, R mask option ?
}

void nupogodi_state::biathlon(machine_config &config)
{
	sm5a_common(config, 1633, 1080); // КБ1013ВК1-2, R mask option ?
}

void nupogodi_state::vfutbol(machine_config &config)
{
	sm5a_common(config, 1655, 1080); // КБ1013ВК1-2, R mask option ?
}

void nupogodi_state::krybolov(machine_config &config)
{
	sm5a_common(config, 1638, 1080); // КБ1013ВК1-2, R mask option ?
}

void nupogodi_state::kvakazad(machine_config &config)
{
	sm5a_common(config, 1660, 1080); // КБ1013ВК1-2, R mask option ?
}

void nupogodi_state::nochnyev(machine_config &config)
{
	sm5a_common(config, 1641, 1080); // КБ1013ВК1-2, R mask option ?
}

void nupogodi_state::kosmicpt(machine_config &config)
{
	sm5a_common(config, 1658, 1080); // КБ1013ВК1-2, R mask option ?
}

void nupogodi_state::morataka(machine_config &config)
{
	sm5a_common(config, 1648, 1080); // КБ1013ВК1-2, R mask option ?
}

void nupogodi_state::atakaast(machine_config &config)
{
	sm5a_common(config, 1620, 1080); // КБ1013ВК1-2, R mask option ?
}

void nupogodi_state::ecircus(machine_config &config)
{
	sm5a_common(config, 1657, 1080); // КБ1013ВК1-2, R mask option ?
}

// roms

ROM_START( nupogodi )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "im-02.bin", 0x0000, 0x0740, CRC(cb820c32) SHA1(7e94fc255f32db725d5aa9e196088e490c1a1443) )

	ROM_REGION( 154233, "screen", 0)
	ROM_LOAD( "nupogodi.svg", 0, 154233, CRC(42cfb84a) SHA1(249ca7ec78066b57f9a18e48ada64712c944e461) )
ROM_END

ROM_START( ehockey )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "im-10.bin", 0x0000, 0x0740, CRC(cb820c32) SHA1(7e94fc255f32db725d5aa9e196088e490c1a1443) )

	ROM_REGION( 94977, "screen", 0)
	ROM_LOAD( "ehockey.svg", 0, 94977, CRC(98cf43b0) SHA1(4353505709612344cd3b597c3b4e9f6b441ddb66) )
ROM_END

ROM_START( rkosmosa )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "im-13.bin", 0x0000, 0x0740, CRC(553e2b09) SHA1(2b74f8437b881fbb62b61f25435a5bfc66872a9a) )

	ROM_REGION( 81420, "screen", 0)
	ROM_LOAD( "rkosmosa.svg", 0, 81420, CRC(dc6632be) SHA1(0906d933f4cda39ee1e57b502651a821e61e95ef) )
ROM_END

ROM_START( okhota )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "im-16.bin", 0x0000, 0x0740, CRC(cb820c32) SHA1(7e94fc255f32db725d5aa9e196088e490c1a1443) )

	ROM_REGION( 117838, "screen", 0)
	ROM_LOAD( "okhota.svg", 0, 117838, CRC(7de707c6) SHA1(c876ea16bd8af033086e2e20860d2e1d09296d59) )
ROM_END

ROM_START( biathlon )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "im-19.bin", 0x0000, 0x0740, CRC(cb820c32) SHA1(7e94fc255f32db725d5aa9e196088e490c1a1443) )

	ROM_REGION( 116377, "screen", 0)
	ROM_LOAD( "biathlon.svg", 0, 116377, CRC(fadf729e) SHA1(671f9496e2bfe7b4800ee7bad039485e19958428) )
ROM_END

ROM_START( vfutbol )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "im-22.bin", 0x0000, 0x0740, CRC(cb820c32) SHA1(7e94fc255f32db725d5aa9e196088e490c1a1443) )

	ROM_REGION( 131901, "screen", 0)
	ROM_LOAD( "vfutbol.svg", 0, 131901, CRC(85811308) SHA1(288aa41bade08c61e0d346b9c1109179564e34ed) )
ROM_END

ROM_START( krybolov )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "im-32.bin", 0x0000, 0x0740, CRC(cb820c32) SHA1(7e94fc255f32db725d5aa9e196088e490c1a1443) )

	ROM_REGION( 132804, "screen", 0)
	ROM_LOAD( "krybolov.svg", 0, 132804, CRC(4e3e70d3) SHA1(18f1300afa601deb6ac01dcf7dca88187b7940a3) )
ROM_END

ROM_START( kvakazad )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "im-33.bin", 0x0000, 0x0740, CRC(cb820c32) SHA1(7e94fc255f32db725d5aa9e196088e490c1a1443) )

	ROM_REGION( 131961, "screen", 0)
	ROM_LOAD( "kvakazad.svg", 0, 131961, CRC(37b27420) SHA1(25d9e273f056c10e3a5bc4476ce980bfdb8095e1) )
ROM_END

ROM_START( nochnyev )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "im-49.bin", 0x0000, 0x0740, CRC(cb820c32) SHA1(7e94fc255f32db725d5aa9e196088e490c1a1443) )

	ROM_REGION( 136498, "screen", 0)
	ROM_LOAD( "nochnyev.svg", 0, 136498, CRC(24a287cd) SHA1(2d14aa9b55b42c634df141fe4037ae286549b17b) )
ROM_END

ROM_START( kosmicpt )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "im-50.bin", 0x0000, 0x0740, CRC(cb820c32) SHA1(7e94fc255f32db725d5aa9e196088e490c1a1443) )

	ROM_REGION( 110214, "screen", 0)
	ROM_LOAD( "kosmicpt.svg", 0, 110214, CRC(ccef6d27) SHA1(71f3cf49a5797ed9296f1e86ec4575ffefab67dd) )
ROM_END

ROM_START( morataka )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "im-51.bin", 0x0000, 0x0740, CRC(cb820c32) SHA1(7e94fc255f32db725d5aa9e196088e490c1a1443) )

	ROM_REGION( 105057, "screen", 0)
	ROM_LOAD( "morataka.svg", 0, 105057, CRC(c235c56c) SHA1(b6ef74ba7826221683243e23513270d0f0f2cfda) )
ROM_END

ROM_START( atakaast )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "im-53.bin", 0x0000, 0x0740, CRC(cb820c32) SHA1(7e94fc255f32db725d5aa9e196088e490c1a1443) )

	ROM_REGION( 105570, "screen", 0)
	ROM_LOAD( "atakaast.svg", 0, 105570, CRC(3d79aacc) SHA1(bc25969f4d6fa75b320130c920ac0bdc8fb44cbd) )
ROM_END

ROM_START( ecircus )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "ecircus.bin", 0x0000, 0x0740, CRC(cb820c32) SHA1(7e94fc255f32db725d5aa9e196088e490c1a1443) )

	ROM_REGION( 124643, "screen", 0)
	ROM_LOAD( "ecircus.svg", 0, 124643, CRC(079f25db) SHA1(defa784c80e01ce6affbb424930674114275bea1) )
ROM_END

ROM_START( naltair )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "dbgb-06i.bin", 0x0000, 0x0740, CRC(7e5bf42b) SHA1(588db84d8c9a1abaae77534321dec8466967eb5f) )

	ROM_REGION( 131901, "screen", 0)
	ROM_LOAD( "naltair.svg", 0, 131901, CRC(85811308) SHA1(288aa41bade08c61e0d346b9c1109179564e34ed) )
ROM_END





/*******************************************************************************

  Elektronika Автослалом (Autoslalom) (model ИМ-23)
  * KB1013VK1-2 MCU
  * lcd screen with custom segments, 1-bit sound

  This is not an unlicensed clone, but doing a hex compare with MC-25 still
  shows around 30% similarity so clearly they used that as a base.

*******************************************************************************/

class auslalom_state : public hh_sm510_state
{
public:
	auslalom_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void auslalom(machine_config &config);
};

// inputs

static INPUT_PORTS_START( auslalom )
	PORT_START("IN.0") // R2
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME(u8"Запуск (Start)")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME(u8"Скорость (Speed)")

	PORT_START("IN.2") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void auslalom_state::auslalom(machine_config &config)
{
	sm5a_common(config, 1732, 1080); // КБ1013ВК1-2, R mask option ?
}

// roms

ROM_START( auslalom )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "im-23.bin", 0x0000, 0x0740, CRC(3b6e726f) SHA1(eabd04722811d1cc6519db9386b14a535f5aa865) )

	ROM_REGION( 117520, "screen", 0)
	ROM_LOAD( "auslalom.svg", 0, 117520, CRC(2f90fd4c) SHA1(f0de58b1fe2f7c18fc219f9f9a94c227ca1245e4) )
ROM_END





/*******************************************************************************

  Elektronika Баскетбол (Basketbol) (model ИМ-55)
  * PCB label: ЕНСК.758726.002/3
  * KB1013VK1-2 MCU
  * 26 LEDs + 4 7seg LEDs, 1-bit sound

  It's a LED game with an LCD driver MCU, that's unconventional.

*******************************************************************************/

class elbaskb_state : public hh_sm510_state
{
public:
	elbaskb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag),
		m_digits(*this, "digit%u", 0U)
	{ }

	void elbaskb(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void update_display() override;

private:
	output_finder<4> m_digits;
};

void elbaskb_state::machine_start()
{
	hh_sm510_state::machine_start();
	m_digits.resolve();
}

// handlers

void elbaskb_state::update_display()
{
	hh_sm510_state::update_display();

	// convert to digit segments
	for (int i = 0; i < 4; i++)
	{
		u8 data = 0;
		for (int seg = 0; seg < 7; seg++)
		{
			int x = ((i << 1) | (seg >> 2 ^ 1)) & 3;
			int y = seg & 3;
			int z = i >> 1 ^ 1;

			data = data >> 1 | (m_out_x[x][y][z] ? 0x40 : 0);
		}

		m_digits[i] = data;
	}
}

// inputs

static INPUT_PORTS_START( elbaskb )
	PORT_START("IN.0") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_COCKTAIL
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_COCKTAIL
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_COCKTAIL
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_COCKTAIL // Отбор

	PORT_START("IN.1") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Отбор

	PORT_START("IN.2") // R4
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x06, 0x02, DEF_STR( Difficulty ) ) PORT_CHANGED_CB(input_changed)
	PORT_CONFSETTING(    0x02, "1" )
	PORT_CONFSETTING(    0x04, "2" )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x00, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x01, "2" )
INPUT_PORTS_END

// config

void elbaskb_state::elbaskb(machine_config &config)
{
	sm5a_common(config, 0, 0); // КБ1013ВК1-2, R mask option ?
	config.set_default_layout(layout_elbaskb);
}

// roms

ROM_START( elbaskb )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "im-55.bin", 0x0000, 0x0740, CRC(006f82d0) SHA1(aca582dcb387345cd09a08e42a954c43430772fc) )
ROM_END





/*******************************************************************************

  Elektronika Весёлая арифметика (model ИМ-50) or Amusing Arithmetic (export
  version, model MG-50)
  * КБ1515ХМ3-2 9202 006 (no decap); seems to be compatible with КБ1013ВК7-2
    which in turn is compatible with Sharp SM511
  * lcd screen with custom segments, 1-bit sound
  * ROM signature: ALEXANDER GAGANOV, USSR, MOSCOW, 1990

*******************************************************************************/

class vesarif_state : public hh_sm510_state
{
public:
	vesarif_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void vesarif(machine_config &config);
};

// inputs

static INPUT_PORTS_START( vesarif )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Start")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void vesarif_state::vesarif(machine_config &config)
{
	sm511_common(config, 1672, 1080); // 1326 x 856
}

// roms

ROM_START( vesarif )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "vesarif.program", 0x0000, 0x1000, CRC(ae9053ef) SHA1(40dcda3616c9f430e04e20aef22e7db6b2b94f37) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "vesarif.melody", 0x000, 0x100, BAD_DUMP CRC(28fb8872) SHA1(18db11d27d8af2fddf8bba8080e05e2fa50b8215) ) // decap needed for verification

	ROM_REGION( 81240, "screen", 0)
	ROM_LOAD( "vesarif.svg", 0, 81240, CRC(27121109) SHA1(63d53e5718b1a014a5cfd7dffa5cb17469fa3182) )
ROM_END





/*******************************************************************************

  Elektronika Учитель арифметики(?) (Uchitel' arifmetiki(?), model ???)
  * КБ1515ХМ3-2 9108 001 (no decap); seems to be compatible with КБ1013ВК7-2
    which in turn is compatible with Sharp SM511
  * lcd screen with custom segments, 1-bit sound
  * ROM signature: ALEXANDER GAGANOV USSR, MOSCOW

  There is no evidence of a real unit yet. Only two hardware parts are
  available at the moment: PCB with chip, and LCD (in fact, there must have been
  two PCBs; the other one was for the keypad, battery compartment and speaker).
  Both parts were found in ruins of the factory in Vinnytsia (Ukraine) formerly
  producing LCD games.

  As the device did not come into mass production, it's considered to be
  just a prototype. The game title is not fully determined. There is a reference
  to a game called "Учитель арифметики" in the Микропроцессорные средства и
  системы magazine (year 1990, number 4, page 38), and this reference was
  taken as the most probable name for this game.

*******************************************************************************/

class uchitari_state : public hh_sm510_state
{
public:
	uchitari_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void uchitari(machine_config &config);
};

// inputs

static INPUT_PORTS_START( uchitari )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("Evaluate Entry")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_CHANGED_CB(input_changed) PORT_NAME("Start Exam")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_CHANGED_CB(input_changed) PORT_NAME("Evaluate Division Entries")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_M) PORT_CHANGED_CB(input_changed) PORT_NAME("Mode")
	PORT_BIT( 0x04, 0x04, IPT_CUSTOM ) PORT_CONDITION("FAKE", 0x30, NOTEQUALS, 0x00) // Alarm / Exam Level
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, 0x01, IPT_CUSTOM ) PORT_CONDITION("FAKE", 0x03, NOTEQUALS, 0x00) // "0"/Minute
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHANGED_CB(input_changed) PORT_NAME("Clear entry / Correction")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("5")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("6")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("+")
	PORT_BIT( 0x04, 0x04, IPT_CUSTOM ) PORT_CONDITION("FAKE", 0x0c, NOTEQUALS, 0x00) // "1"/Hour
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("-")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("8")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_CHANGED_CB(input_changed) PORT_NAME(u8"×")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // S7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("9")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME(u8"÷")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("4")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_ON ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("FAKE") // some of the buttons are presumed to be physically separate but electronically the same
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_RIGHT) PORT_CHANGED_CB(input_changed) PORT_NAME("Minute")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_LEFT) PORT_CHANGED_CB(input_changed) PORT_NAME("Hour")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_L) PORT_CHANGED_CB(input_changed) PORT_NAME("Exam Level")
INPUT_PORTS_END

// config

void uchitari_state::uchitari(machine_config &config)
{
	sm511_common(config, 1520, 1080); // 1341 x 953
}

// roms

ROM_START( uchitari )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "uchitari.program", 0x0000, 0x1000, CRC(1d2d7abb) SHA1(10d90f63813cddc5c986ed3942c64ee6d67f545e) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "uchitari.melody", 0x000, 0x100, BAD_DUMP CRC(28041942) SHA1(a36e246b215499b7392c28fddf1aad499016a480) ) // decap needed for verification

	ROM_REGION( 98294, "screen", 0)
	ROM_LOAD( "uchitari.svg", 0, 98294, CRC(f84b9d59) SHA1(a05713d571824d11f037b7592c0d280e418dc8b3) )
ROM_END





/*******************************************************************************

  Konami Double Dribble (model BH001)
  * PCB label: BH001
  * Sharp SM510 under epoxy (die label CMS54C, KMS584)
  * lcd screen with custom segments, 1-bit sound

  BTANB:
  - At the basket, the ball goes missing sometimes for 1 frame, or may show 2 balls
    at the same time. It's the same on the real handheld.
  - players flicker (increasing LCD delay won't improve it much)
  - If a period is over at the same time a defender on the 2nd column grabs the ball,
    his arm won't be erased until it's redrawn (BTANB not verified).

*******************************************************************************/

class kdribble_state : public hh_sm510_state
{
public:
	kdribble_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void kdribble(machine_config &config);
};

// inputs

static INPUT_PORTS_START( kdribble )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Level Select")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("All Clear")
INPUT_PORTS_END

// config

void kdribble_state::kdribble(machine_config &config)
{
	sm510_common(config, 1524, 1080); // R mask option confirmed
}

// roms

ROM_START( kdribble )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "584", 0x0000, 0x1000, CRC(1d9022c8) SHA1(64567f9f161e830a0634d5c89917ab866c26c0f8) )

	ROM_REGION( 450349, "screen", 0)
	ROM_LOAD( "kdribble.svg", 0, 450349, CRC(0ea4153e) SHA1(b5deb398bb9f5e56e5bbcbe477d54528fb989487) )
ROM_END





/*******************************************************************************

  Konami Contra (model BH002)
  * PCB label: BH002
  * Sharp SM511 under epoxy (die label KMS73B, 773)
  * lcd screen with custom segments, 1-bit sound

  Contra handheld is titled simply "C" in the USA.

*******************************************************************************/

class kcontra_state : public hh_sm510_state
{
public:
	kcontra_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		// increase lcd decay: score digit flickers
		m_decay_len = 20;
	}

	void kcontra(machine_config &config);
};

// inputs

static INPUT_PORTS_START( kcontra )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x06, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("All Clear")
INPUT_PORTS_END

// config

void kcontra_state::kcontra(machine_config &config)
{
	sm511_common(config, 1505, 1080);
}

// roms

ROM_START( kcontra ) // except for filler/unused bytes, ROM listing in patent US5120057 "BH002 C (Contra)" program/melody is same
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "773.program", 0x0000, 0x1000, CRC(bf834877) SHA1(055dd56ec16d63afba61ab866481fd9c029fb54d) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "773.melody", 0x000, 0x100, CRC(23d02b99) SHA1(703938e496db0eeacd14fe7605d4b5c39e0a5bc8) )

	ROM_REGION( 721055, "screen", 0)
	ROM_LOAD( "kcontra.svg", 0, 721055, CRC(f1ce8d19) SHA1(7d8f2fac40605a3fd6f1386c945a53412b2f2b15) )
ROM_END





/*******************************************************************************

  Konami Top Gun (model BH003)
  * PCB label: BH003
  * Sharp SM510 under epoxy (die label CMS54C, KMS598)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class ktopgun_state : public hh_sm510_state
{
public:
	ktopgun_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void ktopgun(machine_config &config);
};

// inputs

static INPUT_PORTS_START( ktopgun )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("All Clear")
INPUT_PORTS_END

// config

void ktopgun_state::ktopgun(machine_config &config)
{
	sm510_common(config, 1515, 1080); // R mask option confirmed
}

// roms

ROM_START( ktopgun ) // except for filler/unused bytes, ROM listing in patent US5137277 "BH003 Top Gun" is same
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "598", 0x0000, 0x1000, CRC(50870b35) SHA1(cda1260c2e1c180995eced04b7d7ff51616dcef5) )

	ROM_REGION( 425839, "screen", 0)
	ROM_LOAD( "ktopgun.svg", 0, 425839, CRC(f0eb200f) SHA1(cbdc7cfaf1785b393c806dabd1a355d325bddc3f) )
ROM_END





/*******************************************************************************

  Konami Gradius (model BH004)
  * PCB label: BH004
  * Sharp SM511 under epoxy (die label KMS73B, 771)
  * lcd screen with custom segments, 1-bit sound

  known releases:
  - World: Gradius
  - Japan: Nemesis

*******************************************************************************/

class kgradius_state : public hh_sm510_state
{
public:
	kgradius_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void kgradius(machine_config &config);
};

// inputs

static INPUT_PORTS_START( kgradius )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("All Clear")
INPUT_PORTS_END

// config

void kgradius_state::kgradius(machine_config &config)
{
	sm511_common(config, 1420, 1080);
}

// roms

ROM_START( kgradius )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "771.program", 0x0000, 0x1000, CRC(830c2afc) SHA1(bb9ebd4e52831cc02cd92dd4b37675f34cf37b8c) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "771.melody", 0x000, 0x100, CRC(4c586b73) SHA1(14c5ab2898013a577f678970a648c374749cc66d) )

	ROM_REGION( 638136, "screen", 0)
	ROM_LOAD( "kgradius.svg", 0, 638136, CRC(85dd296e) SHA1(bd75d0c08387a69bbcf4fd100252846499a261b3) )
ROM_END





/*******************************************************************************

  Konami Teenage Mutant Ninja Turtles
  * PCB label: BH005
  * Sharp SM511 under epoxy (die label KMS73B, 774)
  * lcd screen with custom segments, 1-bit sound

  known releases:
  - World: Teenage Mutant Ninja Turtles
  - UK: Teenage Mutant Hero Turtles

  がんばれゴエモン えびす丸危機一髪 (Ganbare Goemon: Ebisumaru Kiki Ippatsu) (model BH101)
  is presumed to have the same MCU ROM.

*******************************************************************************/

class ktmnt_state : public hh_sm510_state
{
public:
	ktmnt_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void ktmnt(machine_config &config);
};

// inputs

static INPUT_PORTS_START( ktmnt )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_RIGHT ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game Select")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("All Clear")
INPUT_PORTS_END

// config

void ktmnt_state::ktmnt(machine_config &config)
{
	sm511_common(config, 1505, 1080);
}

// roms

ROM_START( ktmnt ) // except for filler/unused bytes, ROM listing in patent US5150899 "BH005 TMNT" program/melody is same
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "774.program", 0x0000, 0x1000, CRC(a1064f87) SHA1(92156c35fbbb414007ee6804fe635128a741d5f1) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "774.melody", 0x000, 0x100, CRC(8270d626) SHA1(bd91ca1d5cd7e2a62eef05c0033b19dcdbe441ca) )

	ROM_REGION( 610309, "screen", 0)
	ROM_LOAD( "ktmnt.svg", 0, 610309, CRC(9f48c50d) SHA1(917c0ed8e83d949e5115c897cacda8d60e42d74d) )
ROM_END





/*******************************************************************************

  Konami Skate or Die (licensed from Electronic Arts)
  * PCB label: BH006
  * Sharp SM511 under epoxy (die label KMS73B, 775)
  * lcd screen with custom segments, 1-bit sound

  けっきょく 南極大冒険 (Kekkyoku Nankyoku Daibouken, aka Antarctic Adventure)
  (model BH100) is presumed to have the same MCU ROM.

*******************************************************************************/

class kskatedie_state : public hh_sm510_state
{
public:
	kskatedie_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void kskatedie(machine_config &config);
};

// inputs

static INPUT_PORTS_START( kskatedie )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_RIGHT ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Continue")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("IN.2") // S3
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("All Clear")
INPUT_PORTS_END

// config

void kskatedie_state::kskatedie(machine_config &config)
{
	sm511_common(config, 1496, 1080);
}

// roms

ROM_START( kskatedie )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "775.program", 0x0000, 0x1000, CRC(067b11db) SHA1(1fe0795515c6787c2af1d38f18a5c4c5c9d87408) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "775.melody", 0x000, 0x100, CRC(d8c59670) SHA1(c33da2415bbd1a414e5dda3b05e139c1d22d267a) )

	ROM_REGION( 395160, "screen", 0)
	ROM_LOAD( "kskatedie.svg", 0, 395160, CRC(34fbb7b8) SHA1(402dfba32947aac75bc9386079b95e7223f78e6d) )
ROM_END





/*******************************************************************************

  Konami The Adventures of Bayou Billy
  * PCB label: BH007
  * Sharp SM511 under epoxy (die label KMS73B, 780)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class kbilly_state : public hh_sm510_state
{
public:
	kbilly_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void kbilly(machine_config &config);
};

// inputs

static INPUT_PORTS_START( kbilly )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_RIGHT ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("All Clear")
INPUT_PORTS_END

// config

void kbilly_state::kbilly(machine_config &config)
{
	sm511_common(config, 1490, 1080);
}

// roms

ROM_START( kbilly )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "780.program", 0x0000, 0x1000, CRC(b8b1f734) SHA1(619dd527187b43276d081cdb1b13e0a9a81f2c6a) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "780.melody", 0x000, 0x100, CRC(cd488bea) SHA1(8fc60081f46e392978d6950c74711fb7ebd154de) )

	ROM_REGION( 598317, "screen", 0)
	ROM_LOAD( "kbilly.svg", 0, 598317, CRC(fec67ddf) SHA1(3e5f520733e8b720966028ed6a72062be5381f27) )
ROM_END





/*******************************************************************************

  Konami Bottom of the Ninth (model BH008)
  * PCB label: BH008
  * Sharp SM511 under epoxy (die label KMS73B, 779)
  * lcd screen with custom segments, 1-bit sound

  known releases:
  - World: Bottom of the Ninth
  - USA: Major League Baseball
  - Japan: がんばれ ベースボール (Ganbare Baseball)

*******************************************************************************/

class kbottom9_state : public hh_sm510_state
{
public:
	kbottom9_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void kbottom9(machine_config &config);
};

// inputs

static INPUT_PORTS_START( kbottom9 )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Level/Time")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("IN.2") // S3
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("All Clear")
INPUT_PORTS_END

// config

void kbottom9_state::kbottom9(machine_config &config)
{
	sm511_common(config, 1480, 1080);
}

// roms

ROM_START( kbottom9 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "779.program", 0x0000, 0x1000, CRC(2f566534) SHA1(01f67fad87648e00278a5aa707a3cd827e6d68cf) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "779.melody", 0x000, 0x100, CRC(eaa2aa04) SHA1(24f1d9b381c3f6a3f9ee1b336593d0ca46744978) )

	ROM_REGION( 411107, "screen", 0)
	ROM_LOAD( "kbottom9.svg", 0, 411107, CRC(04a50349) SHA1(eba3bee0dba7c2d37405fea6156adc0e2fe6cb07) )
ROM_END





/*******************************************************************************

  Konami The Lone Ranger
  * PCB label: BH009
  * Sharp SM511 under epoxy (die label KMS73B, 781)
  * lcd screen with custom segments, 1-bit sound

  There's also a rare version from 1992 called "Bull's-Eye: Barbecue Sauce",
  assumed to be a promotional item licensed to Heinz.

*******************************************************************************/

class kloneran_state : public hh_sm510_state
{
public:
	kloneran_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void kloneran(machine_config &config);
};

// inputs

static INPUT_PORTS_START( kloneran )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("All Clear")
INPUT_PORTS_END

// config

void kloneran_state::kloneran(machine_config &config)
{
	sm511_common(config, 1497, 1080);
}

// roms

ROM_START( kloneran )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "781.program", 0x0000, 0x1000, CRC(52b9735f) SHA1(06c5ef6e7e781b1176d4c1f2445f765ccf18b3f7) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "781.melody", 0x000, 0x100, CRC(a393de36) SHA1(55089f04833ccb318524ab2b584c4817505f4019) )

	ROM_REGION( 633161, "screen", 0)
	ROM_LOAD( "kloneran.svg", 0, 633161, CRC(1fb937ff) SHA1(9e5841dc67e50b789f0161693ebbd75f79915980) )
ROM_END





/*******************************************************************************

  Konami Bill Elliott's NASCAR Racing (model 13010), Chequered Flag
  * PCB label:
    BH010 (Bill Elliott's NASCAR Racing)
    BH016 (Chequered Flag)
  * Sharp SM511 under epoxy
    die label KMS73B, 783 (Bill Elliott's NASCAR Racing)
  * lcd screen with custom segments, 1-bit sound

  Chequered Flag is presumed to have the same MCU ROM as Bill Elliott's NASCAR
  Racing. The MCU was not decapped, so it's not 100% certain.

*******************************************************************************/

class knascar_state : public hh_sm510_state
{
public:
	knascar_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void knascar(machine_config &config);
	void kchqflag(machine_config &config);
};

// inputs

static INPUT_PORTS_START( knascar )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("All Clear")
INPUT_PORTS_END

// config

void knascar_state::knascar(machine_config &config)
{
	sm511_common(config, 1491, 1080);
}

void knascar_state::kchqflag(machine_config &config)
{
	sm511_common(config, 1482, 1080);
}

// roms

ROM_START( knascar )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "783.program", 0x0000, 0x1000, CRC(0a08536a) SHA1(199203fad96e0d2b173b876b9746064b0c30dc7b) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "783.melody", 0x000, 0x100, CRC(ffeef4bc) SHA1(a3b21eefb170aa54eb53cf56f88b0c00dd29703f) )

	ROM_REGION( 474061, "screen", 0)
	ROM_LOAD( "knascar.svg", 0, 474061, CRC(d30f639a) SHA1(6fd061eda61f925a9f85cf5fb4b7024f15e1e1fe) )
ROM_END

ROM_START( kchqflag )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "783.program", 0x0000, 0x1000, CRC(0a08536a) SHA1(199203fad96e0d2b173b876b9746064b0c30dc7b) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "783.melody", 0x000, 0x100, CRC(ffeef4bc) SHA1(a3b21eefb170aa54eb53cf56f88b0c00dd29703f) )

	ROM_REGION( 439251, "screen", 0)
	ROM_LOAD( "kchqflag.svg", 0, 439251, CRC(8d477ab1) SHA1(eb0fe02a4f285081f0fd422df17bca48498112de) )
ROM_END




/*******************************************************************************

  Konami Blades of Steel (model 13011)
  * PCB label: BH011
  * Sharp SM511 under epoxy (die label KMS73B, 782)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class kblades_state : public hh_sm510_state
{
public:
	kblades_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		// increase lcd decay: too much overall flicker
		m_decay_len = 25;
	}

	void kblades(machine_config &config);
};

// inputs

static INPUT_PORTS_START( kblades )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("All Clear")
INPUT_PORTS_END

// config

void kblades_state::kblades(machine_config &config)
{
	sm511_common(config, 1516, 1080);
}

// roms

ROM_START( kblades )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "782.program", 0x0000, 0x1000, CRC(3351a35d) SHA1(84c64b65d3cabfa20c18f4649c9ede2578b82523) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "782.melody", 0x000, 0x100, CRC(e8bf48ba) SHA1(3852c014dc9136566322b4f9e2aab0e3ec3a7387) )

	ROM_REGION( 455154, "screen", 0)
	ROM_LOAD( "kblades.svg", 0, 455154, CRC(f17ec8ba) SHA1(ed999ef4b3f0ae94c243219ea8ea1eedd7179c26) )
ROM_END





/*******************************************************************************

  Konami Teenage Mutant Ninja Turtles II: Splinter Speaks (model 13012)
  * PCB label: BH012
  * Sharp SM511 under epoxy (die label KMS73B, 785)
  * OKI MSM6373 ADPCM under epoxy + 1-bit sound
  * lcd screen with custom segments

*******************************************************************************/

class ktmnt2_state : public hh_sm510_state
{
public:
	ktmnt2_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag),
		m_samples(*this, "samples")
	{ }

	void ktmnt2(machine_config &config);

protected:
	required_device<samples_device> m_samples;

	void sound_w(u8 data);
	int sound_busy_r();
};

// handlers

void ktmnt2_state::sound_w(u8 data)
{
	// S8: ADPCM reset
	if (~data & 0x80)
		m_samples->stop(0);

	// S7: ADPCM ST
	else if (~data & m_inp_mux & 0x40)
	{
		// latch from S1-S5
		u8 sample = data & 0x1f;

		if (m_samples->playing(0))
		{
			// stop command
			if (sample == 0)
				m_samples->stop(0);
		}
		else if (sample != 0)
		{
			sample--;
			if (sample < m_samples->samples() && strncmp(m_samples->names()[sample + 1], "none", 4))
				m_samples->start(0, sample);
		}
	}

	// other: input mux
	input_w(data);
}

int ktmnt2_state::sound_busy_r()
{
	// B: ADPCM busy
	return m_samples->playing(0) ? 0 : 1;
}

// inputs

static INPUT_PORTS_START( ktmnt2 )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("All Clear")
INPUT_PORTS_END

// config

static const char *const ktmnt2_sample_names[] =
{
	"*ktmnt2",
	"cowabunga",
	"pizzapower",
	"lookoutforshredder",
	"bebop",
	"rocksteady",
	"radicaldude",
	"gameover",
	"yeow",
	"what",
	"oof",
	"yeah",
	nullptr
};

void ktmnt2_state::ktmnt2(machine_config &config)
{
	sm511_common(config, 1513, 1080);
	m_maincpu->write_s().set(FUNC(ktmnt2_state::sound_w));
	m_maincpu->read_b().set(FUNC(ktmnt2_state::sound_busy_r));

	// sound hardware
	SAMPLES(config, m_samples);
	m_samples->set_channels(1);
	m_samples->set_samples_names(ktmnt2_sample_names);
	m_samples->add_route(ALL_OUTPUTS, "mono", 0.5);
}

// roms

ROM_START( ktmnt2 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "785.program", 0x0000, 0x1000, CRC(de10cfbc) SHA1(10251abae89317258d3fa45f9378ec458128b080) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "785.melody", 0x000, 0x100, CRC(cdcecef2) SHA1(f29e3dd268b2f2c6d5ed6ffb68051ac462bcac8a) )

	ROM_REGION( 520607, "screen", 0)
	ROM_LOAD( "ktmnt2.svg", 0, 520607, CRC(cdbdd320) SHA1(cb2569426eec18c3fb4cedfc4d1f95a92e818e9a) )

	ROM_REGION( 0x8000, "adpcm", 0)
	ROM_LOAD( "msm6373", 0, 0x8000, NO_DUMP )
ROM_END





/*******************************************************************************

  Konami NFL Football (model 13013)
  * PCB label: BH013
  * Sharp SM511 under epoxy (die label KMS73B, 786)
  * lcd screen with custom segments, 1-bit sound

  This is the 1990 version. It was rereleased in 1992, assumed to be the same
  game underneath.

*******************************************************************************/

class knfl_state : public hh_sm510_state
{
public:
	knfl_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		// increase lcd decay: too much overall flicker
		m_decay_len = 35;
	}

	void knfl(machine_config &config);
};

// inputs

static INPUT_PORTS_START( knfl )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("All Clear")
INPUT_PORTS_END

// config

void knfl_state::knfl(machine_config &config)
{
	sm511_common(config, 1449, 1080);
}

// roms

ROM_START( knfl )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "786.program", 0x0000, 0x1000, CRC(0535c565) SHA1(44cdcd284713ff0b194b24beff9f1b94c8bc63b2) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "786.melody", 0x000, 0x100, CRC(6c80263b) SHA1(d3c21e2f8491fef101907b8e0871b1e1c1ed58f5) )

	ROM_REGION( 571173, "screen", 0)
	ROM_LOAD( "knfl.svg", 0, 571173, CRC(406c5bed) SHA1(1f3a704f091b78c89c06108ba11310f4072cc178) )
ROM_END





/*******************************************************************************

  Konami Star Trek: 25th Anniversary (model 13015)
  * PCB label: BH015
  * Sharp SM511 under epoxy (die label KMS73B, 787)
  * OKI MSM6373 ADPCM under epoxy + 1-bit sound
  * lcd screen with custom segments

*******************************************************************************/

class kst25_state : public ktmnt2_state
{
public:
	kst25_state(const machine_config &mconfig, device_type type, const char *tag) :
		ktmnt2_state(mconfig, type, tag)
	{
		// increase lcd decay: bullets flicker
		m_decay_len = 30;
	}

	void kst25(machine_config &config);
};

// inputs

static INPUT_PORTS_START( kst25 )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, 0x02, IPT_CUSTOM ) PORT_CONDITION("FAKE", 0x03, NOTEQUALS, 0x00) // Up/Down
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("IN.2") // S3
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("FAKE") // Up/Down are electronically the same button
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
INPUT_PORTS_END

// config

static const char *const kst25_sample_names[] =
{
	"*kst25",
	"beammeupscotty",
	"warpspeed",
	"thatsnotlogical",
	"shieldsup",
	"firethephotontorpedos",
	"orbittheplanet",
	"engage",
	"boom",
	"teleporter",
	"klaxon",
	"photontorpedo",
	"hum",
	nullptr
};

void kst25_state::kst25(machine_config &config)
{
	ktmnt2(config);

	config.device_remove("screen");
	mcfg_svg_screen(config, 1464, 1080);
	m_samples->set_samples_names(kst25_sample_names);
}

// roms

ROM_START( kst25 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "787.program", 0x0000, 0x1000, CRC(05abdeec) SHA1(b947fcf4c6c9696b606eabdc74594076013ff73c) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "787.melody", 0x000, 0x100, CRC(3f1c63d4) SHA1(efdfa49c4fd89f2ca5d5cfc6bf9fa741ac227338) )

	ROM_REGION( 570121, "screen", 0)
	ROM_LOAD( "kst25.svg", 0, 570121, CRC(6529d2de) SHA1(993c84ae46277d0d8e0af66473f6ce324b697638) )

	ROM_REGION( 0x8000, "adpcm", 0)
	ROM_LOAD( "msm6373", 0, 0x8000, NO_DUMP )
ROM_END





/*******************************************************************************

  Konami Top Gun: Second Mission
  * PCB label: BH017
  * Sharp SM511 under epoxy (die label KMS73B, 792)
  * OKI MSM6373 ADPCM under epoxy + 1-bit sound
  * lcd screen with custom segments

*******************************************************************************/

class ktopgun2_state : public ktmnt2_state
{
public:
	ktopgun2_state(const machine_config &mconfig, device_type type, const char *tag) :
		ktmnt2_state(mconfig, type, tag)
	{ }

	void ktopgun2(machine_config &config);
};

// inputs

static INPUT_PORTS_START( ktopgun2 )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("All Clear")
INPUT_PORTS_END

// config

static const char *const ktopgun2_sample_names[] =
{
	"*ktopgun2",
	"explosion",
	"crash",
	"none",
	"machinegun",
	"boom",
	"lockon",
	"none",
	"danger",
	"missioncomplete",
	"targetinrange",
	"none",
	"bailout",
	"none",
	"gameover",
	nullptr
};

void ktopgun2_state::ktopgun2(machine_config &config)
{
	ktmnt2(config);

	config.device_remove("screen");
	mcfg_svg_screen(config, 1496, 1080);
	m_samples->set_samples_names(ktopgun2_sample_names);
}

// roms

ROM_START( ktopgun2 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "792.program", 0x0000, 0x1000, CRC(bb6eeeb6) SHA1(7bb21d1736ef7fdfc58ffc0e9dc633ec3c491117) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "792.melody", 0x000, 0x100, CRC(dce276ae) SHA1(27ce9c02e69c38120fd3608577176cdce29f6f06) )

	ROM_REGION( 756991, "screen", 0)
	ROM_LOAD( "ktopgun2.svg", 0, 756991, CRC(d65dbf75) SHA1(f2b55bfba784919f6601a7adaeeb7e5951a03367) )

	ROM_REGION( 0x8000, "adpcm", 0)
	ROM_LOAD( "msm6373", 0, 0x8000, NO_DUMP )
ROM_END





/*******************************************************************************

  Konami Teenage Mutant Ninja Turtles 3: Shredder's Last Stand (model 13017)
  * PCB label: BH018
  * Sharp SM511 under epoxy (die label KMS73B, 794)
  * lcd screen with custom segments, 1-bit sound

  This game was also embedded in a PDA sold by Takara, called Teenage Mutant
  Ninja Turtles: Electrical Note.

*******************************************************************************/

class ktmnt3_state : public hh_sm510_state
{
public:
	ktmnt3_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void ktmnt3(machine_config &config);
};

// inputs

static INPUT_PORTS_START( ktmnt3 )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")

	PORT_START("IN.2") // S3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("All Clear")
INPUT_PORTS_END

// config

void ktmnt3_state::ktmnt3(machine_config &config)
{
	sm511_common(config, 1593, 1080);
}

// roms

ROM_START( ktmnt3 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "794.program", 0x0000, 0x1000, CRC(fcbd6f79) SHA1(45badb94fb3e32350efb7e46e2e271c18135e2aa) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "794.melody", 0x000, 0x100, CRC(9731c180) SHA1(443c4b9c2564e0901a0777d90ab8c138b24788ea) )

	ROM_REGION( 563465, "screen", 0)
	ROM_LOAD( "ktmnt3.svg", 0, 563465, CRC(7877c17e) SHA1(276e42a7dce9d57647b9168a0843dd988d043d88) )
ROM_END





/*******************************************************************************

  Konami Teenage Mutant Ninja Turtles: Basketball (model 13018)
  * PCB label: BH019
  * Sharp SM511 under epoxy (die label KMS73B, 793)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class ktmntbb_state : public hh_sm510_state
{
public:
	ktmntbb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void ktmntbb(machine_config &config);
};

// inputs

static INPUT_PORTS_START( ktmntbb )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("IN.2") // S3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("All Clear")
INPUT_PORTS_END

// config

void ktmntbb_state::ktmntbb(machine_config &config)
{
	sm511_common(config, 1466, 1080);
}

// roms

ROM_START( ktmntbb )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "793.program", 0x0000, 0x1000, CRC(0973b329) SHA1(1c4161e5c53f6c6dd9752a228f361cf053f181f4) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "793.melody", 0x000, 0x100, CRC(cddefb96) SHA1(e9b6d3947c415a3ed520a3dab66a381159dfd79b) )

	ROM_REGION( 873508, "screen", 0)
	ROM_LOAD( "ktmntbb.svg", 0, 873508, CRC(cc4bbfbd) SHA1(1b300e24a8890ad36a7f6e65ba8d5e41e2de2858) )
ROM_END





/*******************************************************************************

  Konami Bucky O'Hare (model 13019)
  * PCB label: BH020
  * Sharp SM511 under epoxy (die label KMS73B, N58)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class kbucky_state : public hh_sm510_state
{
public:
	kbucky_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void kbucky(machine_config &config);
};

// inputs

static INPUT_PORTS_START( kbucky )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("All Clear")
INPUT_PORTS_END

// config

void kbucky_state::kbucky(machine_config &config)
{
	sm511_common(config, 1490, 1080);
}

// roms

ROM_START( kbucky )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "n58.program", 0x0000, 0x1000, CRC(7c36a0c4) SHA1(1b55ac64a71af746fd0a0f44266fcc92cca77482) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "n58.melody", 0x000, 0x100, CRC(7e99e469) SHA1(3e9a3843c6ab392f5989f3366df87a2d26cb8620) )

	ROM_REGION( 727879, "screen", 0)
	ROM_LOAD( "kbucky.svg", 0, 727879, CRC(64cde7e6) SHA1(60c0120c7955a694bb07eb013e42c7a71757ab9f) )
ROM_END





/*******************************************************************************

  Konami Garfield
  * PCB label: BH021
  * Sharp SM511 under epoxy (die label KMS73B, N62)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class kgarfld_state : public hh_sm510_state
{
public:
	kgarfld_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		// increase lcd decay: too much overall flicker
		m_decay_len = 30;
	}

	void kgarfld(machine_config &config);
};

// inputs

static INPUT_PORTS_START( kgarfld )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("IN.2") // S3
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed)

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("All Clear")
INPUT_PORTS_END

// config

void kgarfld_state::kgarfld(machine_config &config)
{
	sm511_common(config, 1500, 1080);
}

// roms

ROM_START( kgarfld )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "n62.program", 0x0000, 0x1000, CRC(5a762049) SHA1(26d4d891160d254dfd752734e1047126243f88dd) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "n62.melody", 0x000, 0x100, CRC(232b7d55) SHA1(76f6a19e8182ee3f00c9f4ef007b5dde75a9c00d) )

	ROM_REGION( 581147, "screen", 0)
	ROM_LOAD( "kgarfld.svg", 0, 581147, CRC(ef2e5a61) SHA1(fbf0236cd0d4228403823d2623c6fd2d68349f7a) )
ROM_END





/*******************************************************************************

  Nelsonic Game Watches on SM530*, wristwatch with an LCD game on it.
  *: Older games are on a different MCU, several seen with OKI MSM5055.
  Newer ones: to be investigated, maybe SM5x.

  Hardware notes:
  * Sharp SM530 under epoxy (die label KAS600 + ROM serial, see romdefs)
  * lcd screen with custom segments, 1-bit sound

  Currently emulated in generic driver:
  - Super Mario Bros. 3
  - Super Mario World (aka Super Mario Bros. 4)

  Different hardware:
  - Star Fox (extra sound effect chip)

*******************************************************************************/

class gamewatch_state : public hh_sm510_state
{
public:
	gamewatch_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void nsmb3(machine_config &config);
	void nsmw(machine_config &config);
};

// inputs

static INPUT_PORTS_START( gamewatch )
	PORT_START("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // mode
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) // set
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
INPUT_PORTS_END

// config

void gamewatch_state::nsmb3(machine_config &config)
{
	sm530_common(config, 1203, 1080);
	config.set_default_layout(layout_hh_sm500_test);
}

void gamewatch_state::nsmw(machine_config &config)
{
	sm530_common(config, 1190, 1080);
	config.set_default_layout(layout_hh_sm500_test);
}

// roms

ROM_START( nsmb3 )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "633.program", 0x000, 0x800, CRC(a981b540) SHA1(1271d15e6168d73852f8ade9ade4d5f3b1838bf5) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "633.melody", 0x000, 0x100, CRC(98340c46) SHA1(94b5865fc669b7f6487845647866c06f4f581f63) )

	ROM_REGION( 373935, "screen", 0)
	ROM_LOAD( "nsmb3.svg", 0, 373935, CRC(d3dd4ef9) SHA1(2e95bdf55b8ba050efbd8fd27442a1872d93685f) )
ROM_END

ROM_START( nsmw )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "636.program", 0x000, 0x800, CRC(56a705fa) SHA1(384ba68e597c7901cc9db2eb991cb80049113503) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "636.melody", 0x000, 0x100, CRC(5df99e13) SHA1(98d5f78b5bcf59a232ccb5c61210568948c4501b) )

	ROM_REGION( 401273, "screen", 0)
	ROM_LOAD( "nsmw.svg", 0, 401273, CRC(72cb618d) SHA1(cecf9bb7ad21896c9a68ca606359f2b6f9c08d5d) )
ROM_END





/*******************************************************************************

  Nelsonic Star Fox (Game Watch)
  * Sharp SM530 under epoxy (die label KAS600, 643)
  * sound effect chip under epoxy (die label HMC, HA1152_001A)
  * lcd screen with custom segments, 1-bit sound

  Two versions are known, with different shape/button layout, but assumed
  to be the exact same game.

  The HMC sound effect chip is very similar (possibly the same one) as found
  in electronic toys such as the Executor keychain. There are 8 sound effects
  in the ROM, only 3 are used in Star Fox.

*******************************************************************************/

class nstarfox_state : public gamewatch_state
{
public:
	nstarfox_state(const machine_config &mconfig, device_type type, const char *tag) :
		gamewatch_state(mconfig, type, tag)
	{ }

	void nstarfox(machine_config &config);

private:
	void sound_w(u8 data);
};

// handlers

void nstarfox_state::sound_w(u8 data)
{
	// S2-S4: falling edge starts sound effect
}

// inputs

static INPUT_PORTS_START( nstarfox )
	PORT_INCLUDE( gamewatch )

	PORT_MODIFY("IN.0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // laser
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) // bomb
INPUT_PORTS_END

// config

void nstarfox_state::nstarfox(machine_config &config)
{
	sm530_common(config, 1176, 1080);
	m_maincpu->write_s().set(FUNC(nstarfox_state::sound_w));
}

// roms

ROM_START( nstarfox )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "643.program", 0x000, 0x800, CRC(ac1f5c68) SHA1(165cefeba9abc8725e55d15fdc218b07857d4cfe) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "643.melody", 0x000, 0x100, CRC(6684f6b7) SHA1(32056467f796cb2e3c9f05c364419e7935fd1361) )

	ROM_REGION( 0x80, "sfx", 0)
	ROM_LOAD( "ha1152_001a", 0x00, 0x80, CRC(fba00b7c) SHA1(5dfb15eee3c57bbca80f485f68442e5f7c6bc5e4) )

	ROM_REGION( 155284, "screen", 0)
	ROM_LOAD( "nstarfox.svg", 0, 155284, CRC(843c0fa2) SHA1(bed08fcc8e9fd20624052d91db90923897a1cf7c) )
ROM_END





/*******************************************************************************

  Tiger Gauntlet (model 7-778) (licensed from Tengen)
  * Sharp SM510 under epoxy (die label CMS54C, KMS583)
  * lcd screen with custom segments, 1-bit sound

  known releases:
  - World: Gauntlet, published by Tiger
  - Japan: Gauntlet, published by Sega
  - UK: Gauntlet, published by Grandstand

  MCU ROM is the same for Gauntlet, Robin Hood.

*******************************************************************************/

class tgaunt_state : public hh_sm510_state
{
public:
	tgaunt_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tgaunt(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tgaunt )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pick")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0a, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Bomb")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Attack")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Key")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tgaunt_state::tgaunt(machine_config &config)
{
	sm510_tiger(config, 1425, 1080);
}

// roms

ROM_START( tgaunt )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "583", 0x0000, 0x1000, CRC(598d8156) SHA1(9f776e8b9b4321e8118481e6b1304f8a38f9932e) )

	ROM_REGION( 713071, "screen", 0)
	ROM_LOAD( "tgaunt.svg", 0, 713071, CRC(b2dfb31b) SHA1(3e57c6aaa665e2874e6e7e051245a81ab7a917b3) )
ROM_END





/*******************************************************************************

  Tiger Double Dragon (model 7-780) (licensed from Technos / Tradewest)
  * Sharp SM510 under epoxy (die label CMS54C, KMS570, 593)
  * lcd screen with custom segments, 1-bit sound

  BTANB: On the baddie in the background throwing dynamite, the sparks
  above his head are the same segment as the body, not the arm.

*******************************************************************************/

class tddragon_state : public hh_sm510_state
{
public:
	tddragon_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tddragon(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tddragon )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_NAME("Jump")
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sway")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch/Pick")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Kick")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Status")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tddragon_state::tddragon(machine_config &config)
{
	sm510_tiger(config, 1467, 1080); // R mask option confirmed
}

// roms

ROM_START( tddragon )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "593", 0x0000, 0x1000, CRC(2642f778) SHA1(fee77acf93e057a8b4627389dfd481c6d9cbd02b) )

	ROM_REGION( 511477, "screen", 0)
	ROM_LOAD( "tddragon.svg", 0, 511477, CRC(d3046671) SHA1(15fd328e28362402eab1094851dddd8e20a0bcec) )
ROM_END





/*******************************************************************************

  Tiger Castlevania II: Simon's Quest (model 7-781) (licensed from Konami)
  * Sharp SM510 under epoxy (die label 581)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tsimquest_state : public hh_sm510_state
{
public:
	tsimquest_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tsimquest(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tsimquest )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_NAME("Whip Up")
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Whip Down")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sword")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Jump Up")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Jump Forward")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Max Score")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tsimquest_state::tsimquest(machine_config &config)
{
	sm510_tiger(config, 1434, 1080); // R mask option confirmed
}

// roms

ROM_START( tsimquest )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "581", 0x0000, 0x1000, CRC(f6bd105f) SHA1(86255bf9fea63f27d10552fbbfc5f8764846f0ab) )

	ROM_REGION( 413414, "screen", 0)
	ROM_LOAD( "tsimquest.svg", 0, 413414, CRC(fe59b341) SHA1(42cff257f28a1796a66fffd142c95644e0130e3b) )
ROM_END





/*******************************************************************************

  Tiger Karnov (model 7-783) (licensed from Data East)
  * Sharp SM510 under epoxy (die label CMS54C, KMS582)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tkarnov_state : public hh_sm510_state
{
public:
	tkarnov_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tkarnov(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tkarnov )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Fire-Ball")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Boomerang")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Shield")
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Max Score")
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tkarnov_state::tkarnov(machine_config &config)
{
	sm510_tiger(config, 1477, 1080);
}

// roms

ROM_START( tkarnov )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "582", 0x0000, 0x1000, CRC(cee85bdd) SHA1(143e39524f1dea523e0575f327ed189343cc87f5) )

	ROM_REGION( 527432, "screen", 0)
	ROM_LOAD( "tkarnov.svg", 0, 527432, CRC(9317066c) SHA1(087cfde97e106c6fd8c52d3a1138e6bde2ad9289) )
ROM_END





/*******************************************************************************

  Tiger Vindicators (model 7-786) (licensed from Tengen)
  * Sharp SM510 under epoxy (die label CMS54C, KMS595)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tvindictr_state : public hh_sm510_state
{
public:
	tvindictr_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tvindictr(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tvindictr )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Gun Right")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Fire")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Gun Left")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Max Score")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tvindictr_state::tvindictr(machine_config &config)
{
	sm510_tiger(config, 1459, 1080);
}

// roms

ROM_START( tvindictr )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "595", 0x0000, 0x1000, CRC(b574d16f) SHA1(d2cb0f2e21ca2defe49a4b45f4c8e169ae9979ab) )

	ROM_REGION( 314205, "screen", 0)
	ROM_LOAD( "tvindictr.svg", 0, 314205, CRC(fefe9f31) SHA1(3c8e7ab2cd81de72740b2948def07a2fc000a78a) )
ROM_END





/*******************************************************************************

  Tiger Ninja Gaiden (model 7-787) (licensed from Tecmo)
  * Sharp SM510 under epoxy (die label M82)
  * lcd screen with custom segments, 1 led, 1-bit sound

*******************************************************************************/

class tgaiden_state : public hh_sm510_state
{
public:
	tgaiden_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag),
		m_led_out(*this, "led")
	{
		inp_fixed_last();
	}

	void tgaiden(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// R2 connects to a single LED behind the screen
	output_finder<> m_led_out;
	void led_w(u8 data) { m_led_out = data >> 1 & 1; }
};

void tgaiden_state::machine_start()
{
	hh_sm510_state::machine_start();
	m_led_out.resolve();
}

// inputs

static INPUT_PORTS_START( tgaiden )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Jump")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Attack/Pick")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tgaiden_state::tgaiden(machine_config &config)
{
	sm510_tiger(config, 1476, 1080);
	m_maincpu->write_r().append(FUNC(tgaiden_state::led_w));
}

// roms

ROM_START( tgaiden )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "m82", 0x0000, 0x1000, CRC(278eafb0) SHA1(14396a0010bade0fde705969151200ed432321e7) )

	ROM_REGION( 588916, "screen", 0)
	ROM_LOAD( "tgaiden.svg", 0, 588916, CRC(5845c630) SHA1(c4b0d4d85e4b58a051920b6b34668847049c57a7) )
ROM_END





/*******************************************************************************

  Tiger Double Dragon II: The Revenge (model 7-798) (licensed from Technos)
  * Sharp SM510 under epoxy (die label M84)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tddragon2_state : public hh_sm510_state
{
public:
	tddragon2_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tddragon2(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tddragon2 )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_NAME("Jump")
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) // Down
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Kick Right")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Kick Left")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tddragon2_state::tddragon2(machine_config &config)
{
	sm510_tiger(config, 1451, 1080);
}

// roms

ROM_START( tddragon2 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "m84", 0x0000, 0x1000, CRC(fe4d3618) SHA1(44c6ad512a01126799cfbc87634fa04edf81b6af) )

	ROM_REGION( 435894, "screen", 0)
	ROM_LOAD( "tddragon2.svg", 0, 435894, CRC(6244f507) SHA1(578f1b3bf7fe757162d231cd0f93d05bbb120ed1) )
ROM_END





/*******************************************************************************

  Tiger Batman (model 7-799) (licensed from DC Comics)
  * Sharp SM510 under epoxy (die label CMS54C, KMS597, 597)
  * lcd screen with custom segments, 1-bit sound

  MCU ROM is the same for Shinobi, Batman.

*******************************************************************************/

class tbatman_state : public hh_sm510_state
{
public:
	tbatman_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tbatman(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tbatman )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pick")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Attack")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Max Score")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tbatman_state::tbatman(machine_config &config)
{
	sm510_tiger(config, 1442, 1080);
}

// roms

ROM_START( tbatman )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "597", 0x0000, 0x1000, CRC(8b7acc97) SHA1(fe811675dc5c5ef9f6f969685c933926c8b9e868) )

	ROM_REGION( 551931, "screen", 0)
	ROM_LOAD( "tbatman.svg", 0, 551931, CRC(95ae104b) SHA1(0508f925f29b2152b41c478447e63c74fce718ad) )
ROM_END





/*******************************************************************************

  Tiger Space Harrier II (model 7-814) (licensed from Sega)
  * Sharp SM510 under epoxy (die label M91)
  * lcd screen with custom segments, 1-bit sound

  known releases:
  - World: Space Harrier II, published by Tiger
  - Japan: Space Harrier, published by Sega

*******************************************************************************/

class tsharr2_state : public hh_sm510_state
{
public:
	tsharr2_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tsharr2(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tsharr2 )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Attack
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Max Score")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tsharr2_state::tsharr2(machine_config &config)
{
	sm510_tiger(config, 1493, 1080); // R mask option confirmed
}

// roms

ROM_START( tsharr2 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "m91", 0x0000, 0x1000, CRC(b207ac79) SHA1(9889dfec26089313ba2bdac845a75a26742d09e1) )

	ROM_REGION( 555177, "screen", 0)
	ROM_LOAD( "tsharr2.svg", 0, 555177, CRC(842f8f7e) SHA1(2c523531059acdfa3a0cebac9d8f84f1971e1086) )
ROM_END





/*******************************************************************************

  Tiger Strider (model 7-815) (licensed from Capcom)
  * Sharp SM510 under epoxy (die label M92)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tstrider_state : public hh_sm510_state
{
public:
	tstrider_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tstrider(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tstrider )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Attack/Pick")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Weapon")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Select")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tstrider_state::tstrider(machine_config &config)
{
	sm510_tiger(config, 1479, 1080);
}

// roms

ROM_START( tstrider )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "m92", 0x0000, 0x1000, CRC(4b488e8f) SHA1(b037c220c4a456f0dac67d759736f202a7609ee5) )

	ROM_REGION( 554858, "screen", 0)
	ROM_LOAD( "tstrider.svg", 0, 554858, CRC(12767799) SHA1(9d67a96affee18dacaf3d49d89f60940c33492aa) )
ROM_END





/*******************************************************************************

  Tiger Golden Axe (model 7-817) (licensed from Sega)
  * Sharp SM510 under epoxy (die label M94)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tgoldnaxe_state : public hh_sm510_state
{
public:
	tgoldnaxe_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tgoldnaxe(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tgoldnaxe )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Magic")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Attack Left")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Attack Right")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tgoldnaxe_state::tgoldnaxe(machine_config &config)
{
	sm510_tiger(config, 1456, 1080);
}

// roms

ROM_START( tgoldnaxe )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "m94", 0x0000, 0x1000, CRC(af183fbf) SHA1(23716e2a7c4bb4842b2af1a43fe88db44e18dc17) )

	ROM_REGION( 605525, "screen", 0)
	ROM_LOAD( "tgoldnaxe.svg", 0, 605525, CRC(9c1097c5) SHA1(bf9c2a1f4ae98ebe5eb9d381b0729588a150fa27) )
ROM_END





/*******************************************************************************

  Tiger Robocop 2 (model 7-830) (licensed from Orion Pictures)
  * Sharp SM510 under epoxy (die label M96)
  * lcd screen with custom segments, 1-bit sound

  MCU ROM is the same for Robocop 2, The Rocketeer.

*******************************************************************************/

class trobocop2_state : public hh_sm510_state
{
public:
	trobocop2_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void trobocop2(machine_config &config);
};

// inputs

static INPUT_PORTS_START( trobocop2 )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Rescue")
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("P1 Down/Pick")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Shoot Right")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Shoot Up")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Shoot Left")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void trobocop2_state::trobocop2(machine_config &config)
{
	sm510_tiger(config, 1487, 1080);
}

// roms

ROM_START( trobocop2 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "m96", 0x0000, 0x1000, CRC(3704b60c) SHA1(04275833e1a79fd33226faf060890b66ae54e1d3) )

	ROM_REGION( 463572, "screen", 0)
	ROM_LOAD( "trobocop2.svg", 0, 463572, CRC(0218c1d9) SHA1(2932825ca03e008e5c2993882d363ae00df43f26) )
ROM_END





/*******************************************************************************

  Tiger Altered Beast (model 7-831) (licensed from Sega)
  * Sharp SM510 under epoxy (die label M88)
  * lcd screen with custom segments, 1-bit sound

  known releases:
  - World: Altered Beast, published by Tiger
  - Japan: Juuouki, published by Sega

*******************************************************************************/

class taltbeast_state : public hh_sm510_state
{
public:
	taltbeast_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void taltbeast(machine_config &config);
};

// inputs

static INPUT_PORTS_START( taltbeast )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch Right")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Kick/Attack")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch Left")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void taltbeast_state::taltbeast(machine_config &config)
{
	sm510_tiger(config, 1455, 1080); // R mask option confirmed
}

// roms

ROM_START( taltbeast )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "m88", 0x0000, 0x1000, CRC(1b3d15e7) SHA1(78371230dff872d6c07eefdbc4856c2a3336eb61) )

	ROM_REGION( 667931, "screen", 0)
	ROM_LOAD( "taltbeast.svg", 0, 667931, CRC(a642d5f7) SHA1(ad005deaa35189e59317a07d860403adeef51aad) )
ROM_END





/*******************************************************************************

  Tiger Mega Man 3 (model 7-834) (licensed from Capcom)
  * Sharp SM510 under epoxy (die label MA4)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tmegaman3_state : public hh_sm510_state
{
public:
	tmegaman3_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tmegaman3(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tmegaman3 )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pick")
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Attack")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Weapon")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Select")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tmegaman3_state::tmegaman3(machine_config &config)
{
	sm510_tiger(config, 1457, 1080);
}

// roms

ROM_START( tmegaman3 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "ma4", 0x0000, 0x1000, CRC(851ef37a) SHA1(0b67db3fe6ffcdee0427d9fea895de6943a28abc) )

	ROM_REGION( 320824, "screen", 0)
	ROM_LOAD( "tmegaman3.svg", 0, 320824, CRC(770e9497) SHA1(0fc68b999aa4d00eef3fa1d1226dfdf8ee2aeb7b) )
ROM_END





/*******************************************************************************

  Tiger Street Fighter 2010: The Final Fight (model 7-837) (licensed from Capcom)
  * Sharp SM510 under epoxy (die label MA2)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tsf2010_state : public hh_sm510_state
{
public:
	tsf2010_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tsf2010(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tsf2010 )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch Right")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Kick")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch Left")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Select Planet")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tsf2010_state::tsf2010(machine_config &config)
{
	sm510_tiger(config, 1465, 1080);
}

// roms

ROM_START( tsf2010 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "ma2", 0x0000, 0x1000, CRC(764b3757) SHA1(c5f90b860128658576bb837e9cabbb3045ad2756) )

	ROM_REGION( 595191, "screen", 0)
	ROM_LOAD( "tsf2010.svg", 0, 595191, CRC(78f96bad) SHA1(4389580e3d47dda0243b01625427013eb7ec4336) )
ROM_END





/*******************************************************************************

  Tiger Swamp Thing (model 7-851) (licensed from DC Comics)
  * Sharp SM510 under epoxy (die label MB0)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tswampt_state : public hh_sm510_state
{
public:
	tswampt_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tswampt(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tswampt )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch/Fire")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Throw")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tswampt_state::tswampt(machine_config &config)
{
	sm510_tiger(config, 1450, 1080);
}

// roms

ROM_START( tswampt )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mb0", 0x0000, 0x1000, CRC(8433530c) SHA1(60716d3bba92dc8ac3f1ee29c5734c9e894a1aff) )

	ROM_REGION( 578544, "screen", 0)
	ROM_LOAD( "tswampt.svg", 0, 578544, CRC(921e2b43) SHA1(176426fe9bf4855256e0ff7804ca48e619fa0cf3) )
ROM_END





/*******************************************************************************

  Tiger Spider-Man (model 7-853) (licensed from Marvel Entertainment Group)
  * Sharp SM510 under epoxy (die label MA5)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tspidman_state : public hh_sm510_state
{
public:
	tspidman_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tspidman(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tspidman )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Shoot Right/Kick")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Shoot Up/Punch")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Shoot Left/Rescue")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Status")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tspidman_state::tspidman(machine_config &config)
{
	sm510_tiger(config, 1440, 1080);
}

// roms

ROM_START( tspidman )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "ma5", 0x0000, 0x1000, CRC(2624daed) SHA1(7c10434ae899637264de706045d48e3fce1d30a7) )

	ROM_REGION( 605375, "screen", 0)
	ROM_LOAD( "tspidman.svg", 0, 605375, CRC(6c032ce4) SHA1(69043096e28709821ddc26ef05f60f353fc3e35d) )
ROM_END





/*******************************************************************************

  Tiger X-Men (model 7-854) (licensed from Marvel Entertainment Group)
  * Sharp SM510 under epoxy (die label MA7)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class txmen_state : public hh_sm510_state
{
public:
	txmen_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void txmen(machine_config &config);
};

// inputs

static INPUT_PORTS_START( txmen )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Attack/Pick Right")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch/Claws")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Attack/Pick Left")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void txmen_state::txmen(machine_config &config)
{
	sm510_tiger(config, 1467, 1080);
}

// roms

ROM_START( txmen )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "ma7", 0x0000, 0x1000, CRC(6f3ff34f) SHA1(aa24fbc3a4117ea51ebf951ee343a36c77692b72) )

	ROM_REGION( 543273, "screen", 0)
	ROM_LOAD( "txmen.svg", 0, 543273, CRC(039b37bb) SHA1(bd57cba8f380185beda2eb5ea7b5d1f25c8d447b) )
ROM_END





/*******************************************************************************

  Tiger Double Dragon 3: The Rosetta Stone (model 7-858) (licensed from Technos)
  * Sharp SM510 under epoxy (die label MA6)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tddragon3_state : public hh_sm510_state
{
public:
	tddragon3_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tddragon3(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tddragon3 )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_NAME("Jump")
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("P1 Down/Pick")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Throw")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Kick")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Select")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tddragon3_state::tddragon3(machine_config &config)
{
	sm510_tiger(config, 1514, 1080);
}

// roms

ROM_START( tddragon3 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "ma6", 0x0000, 0x1000, CRC(8e2da0d9) SHA1(54dd05124b4c605975b0cb1eadd7456ff4a94d68) )

	ROM_REGION( 615734, "screen", 0)
	ROM_LOAD( "tddragon3.svg", 0, 615734, CRC(4c94d574) SHA1(e2717db6c0279da4813550f0035a23bdaaa8b7bb) )
ROM_END





/*******************************************************************************

  Tiger The Flash (model 7-859) (licensed from DC Comics)
  * Sharp SM510 under epoxy (die label MB5)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tflash_state : public hh_sm510_state
{
public:
	tflash_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tflash(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tflash )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch/Brake")
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Run")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Tie")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pick")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // S7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.7") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tflash_state::tflash(machine_config &config)
{
	sm510_tiger(config, 1444, 1080);
}

// roms

ROM_START( tflash )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mb5", 0x0000, 0x1000, CRC(f7f1d082) SHA1(49a7a931450cf27fe69076c4e15ffb34814e25d4) )

	ROM_REGION( 587863, "screen", 0)
	ROM_LOAD( "tflash.svg", 0, 587863, CRC(8ddb9391) SHA1(fa8b4610a914de8ae95123c4273a12cdb4353a39) )
ROM_END





/*******************************************************************************

  Tiger Robin Hood (model 7-861) (not licensed)
  * Sharp SM510 under epoxy (die label CMS54C, KMS583)
  * lcd screen with custom segments, 1-bit sound

  MCU ROM is the same for Gauntlet, Robin Hood.

*******************************************************************************/

class trobhood_state : public hh_sm510_state
{
public:
	trobhood_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void trobhood(machine_config &config);
};

// inputs

static INPUT_PORTS_START( trobhood )
	PORT_INCLUDE( tgaunt )

	PORT_MODIFY("IN.3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Money")
INPUT_PORTS_END

// config

void trobhood_state::trobhood(machine_config &config)
{
	sm510_tiger(config, 1468, 1080);
}

// roms

ROM_START( trobhood )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "583", 0x0000, 0x1000, CRC(598d8156) SHA1(9f776e8b9b4321e8118481e6b1304f8a38f9932e) )

	ROM_REGION( 704816, "screen", 0)
	ROM_LOAD( "trobhood.svg", 0, 704816, CRC(f4b94f32) SHA1(8f68a7f4240489d42934d3875f82456aceabfb48) )
ROM_END





/*******************************************************************************

  Tiger Pit-Fighter (model 7-863) (licensed from Atari Games / Tengen)
  * Sharp SM510 under epoxy (die label MA8)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tpitfight_state : public hh_sm510_state
{
public:
	tpitfight_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tpitfight(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tpitfight )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_CHANGED_CB(input_changed) PORT_NAME("Left Attack High")
	PORT_BIT( 0x0a, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Right Attack Low")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_CHANGED_CB(input_changed) PORT_NAME("Right Attack High")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pick On Right")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_LEFT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Left Attack Low")
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pick On Left")
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Select")
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tpitfight_state::tpitfight(machine_config &config)
{
	sm510_tiger(config, 1447, 1080);
}

// roms

ROM_START( tpitfight )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "ma8", 0x0000, 0x1000, CRC(8470a539) SHA1(f857da9a498dfbae10307c55f3c4e49c94fe4ea8) )

	ROM_REGION( 330535, "screen", 0)
	ROM_LOAD( "tpitfight.svg", 0, 330535, CRC(5407b415) SHA1(e489c63a4bbbf9f146a25a451227f1916df7e868) )
ROM_END





/*******************************************************************************

  Tiger The Rocketeer (model 7-864) (licensed from Walt Disney)
  * Sharp SM510 under epoxy (die label M96)
  * lcd screen with custom segments, 1-bit sound

  MCU ROM is the same for Robocop 2, The Rocketeer.

*******************************************************************************/

class trockteer_state : public hh_sm510_state
{
public:
	trockteer_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void trockteer(machine_config &config);
};

// inputs

static INPUT_PORTS_START( trockteer )
	PORT_INCLUDE( trobocop2 )

	PORT_MODIFY("IN.0")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_NAME("P1 Up/Rocket Pack")

	PORT_MODIFY("IN.3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Fire Right")

	PORT_MODIFY("IN.4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Fire Up")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Fire Left")
INPUT_PORTS_END

// config

void trockteer_state::trockteer(machine_config &config)
{
	sm510_tiger(config, 1463, 1080);
}

// roms

ROM_START( trockteer )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "m96", 0x0000, 0x1000, CRC(3704b60c) SHA1(04275833e1a79fd33226faf060890b66ae54e1d3) )

	ROM_REGION( 558128, "screen", 0)
	ROM_LOAD( "trockteer.svg", 0, 558128, CRC(70ff1f46) SHA1(5cd94655654614206ed11844ba31650edb51eb22) )
ROM_END





/*******************************************************************************

  Tiger MC Hammer: U Can't Touch This (model 7-865) (licensed from Winterland
  Productions)
  * Sharp SM511 under epoxy (die label N63)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tmchammer_state : public hh_sm510_state
{
public:
	tmchammer_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tmchammer(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tmchammer )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_LEFT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Leg Footwork Left")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_CHANGED_CB(input_changed) PORT_NAME("Arm Up")
	PORT_BIT( 0x0a, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Leg Footwork Right")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_CHANGED_CB(input_changed) PORT_NAME("Leg Leaps Up/Jump")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Leg Leaps Down")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Arm Splits Right")
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_LEFT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Arm Splits Left")
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Arm Down")
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Mode")
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tmchammer_state::tmchammer(machine_config &config)
{
	sm511_tiger1bit(config, 1471, 1080);
}

// roms

ROM_START( tmchammer )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "n63.program", 0x0000, 0x1000, CRC(303aa6f7) SHA1(296689be1ee05238e52e9882812868b2ea96202c) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "n63.melody", 0x000, 0x100, CRC(77c1a5a3) SHA1(c00ae3b7c64dd9db96eab520fe674a40571fc15f) )

	ROM_REGION( 456487, "screen", 0)
	ROM_LOAD( "tmchammer.svg", 0, 456487, CRC(1cd10ff3) SHA1(092396c56adae7f872c3b5916ef3ecf67ab30161) )
ROM_END





/*******************************************************************************

  Tiger Battletoads (model 7-868) (licensed from Rare / Tradewest)
  * Sharp SM510 under epoxy (die label MB3)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tbtoads_state : public hh_sm510_state
{
public:
	tbtoads_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tbtoads(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tbtoads )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Attack Right")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Select")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Attack Left")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Score")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tbtoads_state::tbtoads(machine_config &config)
{
	sm510_tiger(config, 1454, 1080);
}

// roms

ROM_START( tbtoads )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mb3", 0x0000, 0x1000, CRC(8fa4c55a) SHA1(2be97e63dfed51313e180d7388dd431058db5a51) )

	ROM_REGION( 694417, "screen", 0)
	ROM_LOAD( "tbtoads.svg", 0, 694417, CRC(c0fbc25d) SHA1(def54ab2f10123246f7809fc3caf3e9d26800f87) )
ROM_END





/*******************************************************************************

  Tiger Hook (model 7-869) (licensed from Tri-Star Pictures)
  * Sharp SM510 under epoxy (die label MB7)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class thook_state : public hh_sm510_state
{
public:
	thook_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void thook(machine_config &config);
};

// inputs

static INPUT_PORTS_START( thook )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sword Up")
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sword Down/Dodge")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Swing Right")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Attack/Pick")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Swing Left")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void thook_state::thook(machine_config &config)
{
	sm510_tiger(config, 1489, 1080);
}

// roms

ROM_START( thook )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mb7", 0x0000, 0x1000, CRC(7eb1a6e2) SHA1(f4a09ab95c968b0ddbe56cd7bb2667881c145731) )

	ROM_REGION( 680544, "screen", 0)
	ROM_LOAD( "thook.svg", 0, 680544, CRC(bddefb9b) SHA1(438b0fe6f4c0196952bd179075498d6bd19c3e48) )
ROM_END





/*******************************************************************************

  Tiger Back to the Future (model 7-809) (licensed from UCS & Amblin)
  * Sharp SM510 under epoxy (die label MC3)
  * lcd screen with custom segments, 1-bit sound

  This game is from 1992, even though the model number suggests otherwise.
  Perhaps Tiger filled unused model numbers before switching to 78-xxx.

*******************************************************************************/

class tbttf_state : public hh_sm510_state
{
public:
	tbttf_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tbttf(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tbttf )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_NAME("Repair")
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Brake")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Accelerate")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Rescue")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tbttf_state::tbttf(machine_config &config)
{
	sm510_tiger(config, 1466, 1080);
}

// roms

ROM_START( tbttf )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mc3", 0x0000, 0x1000, CRC(9c37a23c) SHA1(c09fa5caac8b574f8460265b98c0bea1d5e78c6a) )

	ROM_REGION( 667741, "screen", 0)
	ROM_LOAD( "tbttf.svg", 0, 667741, CRC(1a57e35a) SHA1(9b622e08cc44e3d48b71a283cd07b89fbcc6faa4) )
ROM_END





/*******************************************************************************

  Tiger The Addams Family (model 7-829) (licensed from Paramount Pictures)
  * Sharp SM510 under epoxy (die label MC2)
  * lcd screen with custom segments, 1-bit sound

  Like Back to the Future, this game is newer than the model number suggests.

*******************************************************************************/

class taddams_state : public hh_sm510_state
{
public:
	taddams_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void taddams(machine_config &config);
};

// inputs

static INPUT_PORTS_START( taddams )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Take")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Throw")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Select")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void taddams_state::taddams(machine_config &config)
{
	sm510_tiger(config, 1464, 1080);
}

// roms

ROM_START( taddams )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mc2", 0x0000, 0x1000, CRC(af33d432) SHA1(676ada238c389d1dd02dcb29731d69624f60b342) )

	ROM_REGION( 554703, "screen", 0)
	ROM_LOAD( "taddams.svg", 0, 554703, CRC(85f15123) SHA1(088dbfbe760b782988bbfe6d29d89b8427844992) )
ROM_END





/*******************************************************************************

  Tiger Home Alone (model 78-502) (licensed from Twentieth Century Fox)
  * Sharp SM510 under epoxy (die label MC7)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class thalone_state : public hh_sm510_state
{
public:
	thalone_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void thalone(machine_config &config);
};

// inputs

static INPUT_PORTS_START( thalone )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Trap Right")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Climb")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Trap Left")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void thalone_state::thalone(machine_config &config)
{
	sm510_tiger(config, 1448, 1080);
}

// roms

ROM_START( thalone )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mc7", 0x0000, 0x1000, CRC(eceda335) SHA1(20c9ffcf914db61aba03716fe146bac42873ac82) )

	ROM_REGION( 494279, "screen", 0)
	ROM_LOAD( "thalone.svg", 0, 494279, CRC(2479d88d) SHA1(061413d6e0893106b335ca42cf0260f45c7dacc7) )
ROM_END





/*******************************************************************************

  Tiger Ninja Gaiden III (model 78-503) (licensed from Tecmo)
  * Sharp SM510 under epoxy (die label MD6)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tgaiden3_state : public hh_sm510_state
{
public:
	tgaiden3_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tgaiden3(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tgaiden3 )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Jump")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Attack/Pick")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tgaiden3_state::tgaiden3(machine_config &config)
{
	sm510_tiger(config, 1474, 1080);
}

// roms

ROM_START( tgaiden3 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "md6", 0x0000, 0x1000, CRC(00037018) SHA1(ca1e9f87706e44fb14c302626bdb4d1b274ffe03) )

	ROM_REGION( 512535, "screen", 0)
	ROM_LOAD( "tgaiden3.svg", 0, 512535, CRC(c9006c7b) SHA1(c50279d8133f5fb4d1e3da5c6ebefbc9bbd9dbcd) )
ROM_END





/*******************************************************************************

  Tiger X-Men: Project X (model 78-504) (licensed from Marvel Entertainment Group)
  * Sharp SM510 under epoxy (die label MD3)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class txmenpx_state : public hh_sm510_state
{
public:
	txmenpx_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void txmenpx(machine_config &config);
};

// inputs

static INPUT_PORTS_START( txmenpx )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pick")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Jump")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Move")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void txmenpx_state::txmenpx(machine_config &config)
{
	sm510_tiger(config, 1464, 1080);
}

// roms

ROM_START( txmenpx )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "md3", 0x0000, 0x1000, CRC(11c2b09a) SHA1(f94b1e3e60f002398b39c98946469dd1a6aa8e77) )

	ROM_REGION( 572583, "screen", 0)
	ROM_LOAD( "txmenpx.svg", 0, 572583, CRC(b98f3134) SHA1(78ac7cd32b36f214b5e6eada725378ccbca91987) )
ROM_END





/*******************************************************************************

  Tiger Home Alone 2: Lost in New York (model 78-506) (licensed from Twentieth
  Century Fox)
  * Sharp SM510 under epoxy (die label MD7)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class thalone2_state : public hh_sm510_state
{
public:
	thalone2_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void thalone2(machine_config &config);
};

// inputs

static INPUT_PORTS_START( thalone2 )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Trap/Flash Right")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Climb/Pick")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Trap/Flash Left")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void thalone2_state::thalone2(machine_config &config)
{
	sm510_tiger(config, 1454, 1080);
}

// roms

ROM_START( thalone2 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "md7", 0x0000, 0x1000, CRC(ac8a21e9) SHA1(9024f74e34056f90b7dbf439300797183f74eb00) )

	ROM_REGION( 748928, "screen", 0)
	ROM_LOAD( "thalone2.svg", 0, 748928, CRC(d42ec743) SHA1(7df9654b7f700662f29ca7cabe25ac78b2c4b04b) )
ROM_END





/*******************************************************************************

  Tiger Sonic The Hedgehog (model 78-513) (licensed from Sega)
  * Sharp SM511 under epoxy (die label KMS73B, N71)
  * lcd screen with custom segments, 2-bit sound

*******************************************************************************/

class tsonic_state : public hh_sm510_state
{
public:
	tsonic_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tsonic(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tsonic )
	PORT_START("IN.0") // S2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) // Jump Up
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) // Down
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Super Sonic Spin
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tsonic_state::tsonic(machine_config &config)
{
	sm511_tiger2bit(config, 1517, 1080);
}

// roms

ROM_START( tsonic )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "n71.program", 0x0000, 0x1000, CRC(44cafd68) SHA1(bf8d0ab88d153fabc688ffec19959209ca79c3db) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "n71.melody", 0x000, 0x100, CRC(bae258c8) SHA1(81cb75d73fab4479cd92fcb13d9cb03cec2afdd5) )

	ROM_REGION( 541491, "screen", 0)
	ROM_LOAD( "tsonic.svg", 0, 541491, CRC(ac6bff26) SHA1(fa944958eb64e8283d35951ff105cead28e6ab8a) )
ROM_END





/*******************************************************************************

  Tiger Robocop 3 (model 78-514) (licensed from Orion Pictures)
  * Sharp SM510 under epoxy (die label MC6)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class trobocop3_state : public hh_sm510_state
{
public:
	trobocop3_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void trobocop3(machine_config &config);
};

// inputs

static INPUT_PORTS_START( trobocop3 )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_NAME("Jump/Fly")
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Rescue")
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("P1 Down/Pick")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Shoot Right")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Shoot Up")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Shoot Left")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void trobocop3_state::trobocop3(machine_config &config)
{
	sm510_tiger(config, 1464, 1080);
}

// roms

ROM_START( trobocop3 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mc6", 0x0000, 0x1000, CRC(07b44e4c) SHA1(3165c85e16c062d2d9d0c0f1b1f6bd6079b4de15) )

	ROM_REGION( 612142, "screen", 0)
	ROM_LOAD( "trobocop3.svg", 0, 612142, CRC(b55c1440) SHA1(e8f692deecf489be22be570510175b750d65d5c5) )
ROM_END





/*******************************************************************************

  Tiger The Incredible Crash Dummies (model 78-516) (licensed from Tyco / LCI)
  * Sharp SM510 under epoxy (die label ME0)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tdummies_state : public hh_sm510_state
{
public:
	tdummies_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tdummies(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tdummies )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Crash")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Brake")
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Accelerate")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pick")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tdummies_state::tdummies(machine_config &config)
{
	sm510_tiger(config, 1441, 1080);
}

// roms

ROM_START( tdummies )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "me0", 0x0000, 0x1000, CRC(29efae4a) SHA1(0b26913a3fd2fde2b39549f0f7cbc3daaa41eb50) )

	ROM_REGION( 525535, "screen", 0)
	ROM_LOAD( "tdummies.svg", 0, 525535, CRC(b0db8655) SHA1(937b3edd9c0949b9f7d01ef8920ac63b61e64909) )
ROM_END





/*******************************************************************************

  Tiger Street Fighter II (model 78-522) (licensed from Capcom)
  * Sharp SM510 under epoxy (die label ME1)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tsfight2_state : public hh_sm510_state
{
public:
	tsfight2_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tsfight2(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tsfight2 )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) // Jump
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) // Down
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Kick Right")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch/Special Move")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Kick Left")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tsfight2_state::tsfight2(machine_config &config)
{
	sm510_tiger(config, 1444, 1080);
}

// roms

ROM_START( tsfight2 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "me1", 0x0000, 0x1000, CRC(73384e94) SHA1(350417d101ce034b3974b4a1d2e04bcb3bf70605) )

	ROM_REGION( 630444, "screen", 0)
	ROM_LOAD( "tsfight2.svg", 0, 630444, CRC(42b82c9b) SHA1(8e18d0cfd629478973f2c857105daad70eae46d9) )
ROM_END





/*******************************************************************************

  Tiger Wayne's World (model 78-523) (licensed from Broadway Video / NBC)
  * Sharp SM510 under epoxy (die label ME7)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class twworld_state : public hh_sm510_state
{
public:
	twworld_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void twworld(machine_config &config);
};

// inputs

static INPUT_PORTS_START( twworld )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_NAME("High Five")
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Hockey Right")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Shoot/Cassandra")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Hockey Left")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void twworld_state::twworld(machine_config &config)
{
	sm510_tiger(config, 1429, 1080);
}

// roms

ROM_START( twworld )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "me7", 0x0000, 0x1000, CRC(dcb16d98) SHA1(539989e12bbc4a719818546c5edcfda02b98210e) )

	ROM_REGION( 527901, "screen", 0)
	ROM_LOAD( "twworld.svg", 0, 527901, CRC(515fede8) SHA1(e9c5adfb02b860fb97968957f282e685b1a4e3bc) )
ROM_END





/*******************************************************************************

  Tiger Jurassic Park (model 78-524) (licensed from Universal City Studios & Amblin)
  * Sharp SM510 under epoxy (die label MF4)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tjpark_state : public hh_sm510_state
{
public:
	tjpark_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tjpark(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tjpark )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_NAME("Jump/Climb")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Swing/Power")
	PORT_BIT( 0x0a, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Forward")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Call")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tjpark_state::tjpark(machine_config &config)
{
	sm510_tiger(config, 1454, 1080);
}

// roms

ROM_START( tjpark )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mf4", 0x0000, 0x1000, CRC(f66faf73) SHA1(4cfa743dcd6e44a3c1f56206d5824fddba16df01) )

	ROM_REGION( 812619, "screen", 0)
	ROM_LOAD( "tjpark.svg", 0, 812619, CRC(04d85ec6) SHA1(97f67bb496d985b62dc094f6f1d6b5597a4df895) )
ROM_END





/*******************************************************************************

  Tiger Sonic The Hedgehog 2 (model 78-527) (licensed from Sega)
  * Sharp SM511 under epoxy (die label N86)
  * lcd screen with custom segments, 2-bit sound

*******************************************************************************/

class tsonic2_state : public hh_sm510_state
{
public:
	tsonic2_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tsonic2(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tsonic2 )
	PORT_START("IN.0") // S2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Action
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tsonic2_state::tsonic2(machine_config &config)
{
	sm511_tiger2bit(config, 1475, 1080);
}

// roms

ROM_START( tsonic2 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "n86.program", 0x0000, 0x1000, CRC(782874c5) SHA1(b7eb1f56cbc781ba0b90f6b4b5b51944120733cc) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "n86.melody", 0x000, 0x100, CRC(c16fa2b2) SHA1(222772d311fd3b3b05d80cfd539c2c862bed0be5) )

	ROM_REGION( 667927, "screen", 0)
	ROM_LOAD( "tsonic2.svg", 0, 667927, CRC(d2c52e67) SHA1(24ffbc8fae606dcd2f60a4d95fe1dcfd261c3576) )
ROM_END





/*******************************************************************************

  Tiger Super Double Dragon (model 78-528) (licensed from Technos)
  * Sharp SM510 under epoxy (die label MF5)
  * lcd screen with custom segments, 1-bit sound

  BTANB: The player char right arm muscle is part of the right kick segment.
  They probably meant to use it for the right punch segment, but this is
  how it shows on the LCD.

*******************************************************************************/

class tsddragon_state : public hh_sm510_state
{
public:
	tsddragon_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tsddragon(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tsddragon )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Kick Left")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_NAME("Jump")
	PORT_BIT( 0x0a, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("P1 Down/Pick")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Special Technique")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Kick Right")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Select")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tsddragon_state::tsddragon(machine_config &config)
{
	sm510_tiger(config, 1503, 1080);
}

// roms

ROM_START( tsddragon )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mf5", 0x0000, 0x1000, CRC(264c8e82) SHA1(470eb2f09a58ef05eb0b7c8e11380ad1d8ce4e1a) )

	ROM_REGION( 753572, "screen", 0)
	ROM_LOAD( "tsddragon.svg", 0, 753572, CRC(0759388b) SHA1(4e4acb1b97845e529522ba21de846ce1dc74357d) )
ROM_END





/*******************************************************************************

  Tiger Dennis the Menace (model 78-532) (licensed from Warner Bros. / HKE)
  * Sharp SM510 under epoxy (die label MF9)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tdennis_state : public hh_sm510_state
{
public:
	tdennis_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tdennis(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tdennis )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Slingshot Left")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0a, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Brake")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pick Left")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Slingshot Right")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pick Right")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tdennis_state::tdennis(machine_config &config)
{
	sm510_tiger(config, 1467, 1080);
}

// roms

ROM_START( tdennis )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mf9", 0x0000, 0x1000, CRC(d95f54d5) SHA1(1b3a170f32deec98e54ad09c04b404f5ae03dcea) )

	ROM_REGION( 754896, "screen", 0)
	ROM_LOAD( "tdennis.svg", 0, 754896, CRC(6e7512c3) SHA1(b84ef988051a6a883f3435a779ea67e544d50dae) )
ROM_END





/*******************************************************************************

  Tiger Nightmare Before Christmas (model 78-537) (licensed from Touchstone Pictures)
  * Sharp SM510 under epoxy (die label MG0)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tnmarebc_state : public hh_sm510_state
{
public:
	tnmarebc_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tnmarebc(machine_config &config);

private:
	virtual void input_w(u8 data) override;
};

// handlers

void tnmarebc_state::input_w(u8 data)
{
	// S5 and S6 tied together
	hh_sm510_state::input_w((data & 0x1f) | (data >> 1 & 0x10));
}

// inputs

static INPUT_PORTS_START( tnmarebc )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) // Jump
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Action
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5/S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tnmarebc_state::tnmarebc(machine_config &config)
{
	sm510_tiger(config, 1456, 1080);
}

// roms

ROM_START( tnmarebc )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mg0", 0x0000, 0x1000, CRC(5ef21421) SHA1(8fd458575111b89d7c33c969e76703bde5ad2c36) )

	ROM_REGION( 631351, "screen", 0)
	ROM_LOAD( "tnmarebc.svg", 0, 631351, CRC(140e278b) SHA1(f0d2b6e6ee2328f54255532ad03d5d505ebbb23b) )
ROM_END





/*******************************************************************************

  Tiger Transformers: Generation 2 (model 78-541) (not licensed: Transformers
  was owned by parent company Hasbro)
  * Sharp SM510 under epoxy (die label MG2)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class ttransf2_state : public hh_sm510_state
{
public:
	ttransf2_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void ttransf2(machine_config &config);
};

// inputs

static INPUT_PORTS_START( ttransf2 )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Attack")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Call")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Transform")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void ttransf2_state::ttransf2(machine_config &config)
{
	sm510_tiger(config, 1476, 1080);
}

// roms

ROM_START( ttransf2 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mg2", 0x0000, 0x1000, CRC(65c0f456) SHA1(b1bc3887c5088b3fe359585658e5c5236c09af9e) )

	ROM_REGION( 727708, "screen", 0)
	ROM_LOAD( "ttransf2.svg", 0, 727708, CRC(bd527f23) SHA1(9ce35bbfe1ea61c431eccaf32274faa4181587da) )
ROM_END





/*******************************************************************************

  Tiger Operation: Aliens (model 78-552) (licensed from Twentieth Century Fox)
  * Sharp SM510 under epoxy (die label MJ1)
  * lcd screen with custom segments, 1-bit sound

  BTANB: Energy and weapon power bars are the wrong way around. The manual does
  show them correctly, but it appears to be a doctored picture.

  There's also a newer version called Project Alien, presumed to be the same ROM.
  The LCD is different (no Xenomorph aliens). The background inlay is different
  too, and this time the weapon/energy labels are placed correctly.

*******************************************************************************/

class topaliens_state : public hh_sm510_state
{
public:
	topaliens_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void topaliens(machine_config &config);
};

// inputs

static INPUT_PORTS_START( topaliens )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Attack")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pick")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void topaliens_state::topaliens(machine_config &config)
{
	sm510_tiger(config, 1450, 1080);
}

// roms

ROM_START( topaliens )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mj1", 0x0000, 0x1000, CRC(ccc196cf) SHA1(f18f7cf842cddecf90d05ab0f90257bb76514f54) )

	ROM_REGION( 1214917, "screen", 0)
	ROM_LOAD( "topaliens.svg", 0, 1214917, CRC(cbd57ab4) SHA1(efac0833f421212a81a8fc9b35c97369375aa1a9) )
ROM_END





/*******************************************************************************

  Tiger Mortal Kombat (model 78-553) (licensed from Midway)
  * Sharp SM510 under epoxy (die label MG6)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tmkombat_state : public hh_sm510_state
{
public:
	tmkombat_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tmkombat(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tmkombat )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch High")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0a, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Kick High")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch Low")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Kick Low")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Select")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tmkombat_state::tmkombat(machine_config &config)
{
	sm510_tiger(config, 1468, 1080);
}

// roms

ROM_START( tmkombat )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mg6", 0x0000, 0x1000, CRC(f6375dc7) SHA1(a711199c2623979f19c11067ebfff9355256c2c3) )

	ROM_REGION( 841871, "screen", 0)
	ROM_LOAD( "tmkombat.svg", 0, 841871, CRC(3bd5a963) SHA1(4d093f34c64caf60233e156fe160d8fceb15b6c6) )
ROM_END





/*******************************************************************************

  Tiger The Shadow (model 78-559) (licensed from The Conde Nast Publications)
  * Sharp SM510 under epoxy (die label MJ5)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tshadow_state : public hh_sm510_state
{
public:
	tshadow_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tshadow(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tshadow )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Shadow")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Kick")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tshadow_state::tshadow(machine_config &config)
{
	sm510_tiger(config, 1484, 1080);
}

// roms

ROM_START( tshadow )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mj5", 0x0000, 0x1000, CRC(09822d73) SHA1(30cae8b783a4f388193aee248fa18c6c1042e0ec) )

	ROM_REGION( 946494, "screen", 0)
	ROM_LOAD( "tshadow.svg", 0, 946494, CRC(e62de82e) SHA1(94fcef33066e97266efcd0d91b60229859088b36) )
ROM_END





/*******************************************************************************

  Tiger Skeleton Warriors: The Dark Crusade (model 78-569) (licensed from Landmark
  Entertainment Group)
  * Sharp SM510 under epoxy (die label MK0)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tskelwarr_state : public hh_sm510_state
{
public:
	tskelwarr_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tskelwarr(machine_config &);
};

// inputs

static INPUT_PORTS_START( tskelwarr )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Attack Right")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pick/Call")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Attack Left")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tskelwarr_state::tskelwarr(machine_config &config)
{
	sm510_tiger(config, 1444, 1080);
}

// roms

ROM_START( tskelwarr )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mk0", 0x0000, 0x1000, CRC(dc7827a1) SHA1(74ff143605684df0c70db604a5f22dbf512044d7) )

	ROM_REGION( 1125043, "screen", 0)
	ROM_LOAD( "tskelwarr.svg", 0, 1125043, CRC(ee086073) SHA1(70e06a7fe8a1a8b8fee483c08522996da9917501) )
ROM_END





/*******************************************************************************

  Tiger Batman Forever: Double Dose of Doom (model 78-572) (licensed from DC Comics)
  * Sharp SM510 under epoxy (die label MK3)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tbatfor_state : public hh_sm510_state
{
public:
	tbatfor_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tbatfor(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tbatfor )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Grappling Gun")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0a, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Help")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Batarang")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Thruster")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tbatfor_state::tbatfor(machine_config &config)
{
	sm510_tiger(config, 1493, 1080);
}

// roms

ROM_START( tbatfor )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mk3", 0x0000, 0x1000, CRC(9993c382) SHA1(0c89e21024315ce7c086af5390c60f5766028c4f) )

	ROM_REGION( 902412, "screen", 0)
	ROM_LOAD( "tbatfor.svg", 0, 902412, CRC(d92d799e) SHA1(fa8b4198099748570a9cc9772c81aa583ac7ea72) )
ROM_END





/*******************************************************************************

  Tiger Judge Dredd (model 78-581) (licensed from Cinergi / Egmont Foundation /
  CPI / Surge Comic Prop.)
  * Sharp SM510 under epoxy (die label MK5)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tjdredd_state : public hh_sm510_state
{
public:
	tjdredd_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tjdredd(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tjdredd )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Kick")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0a, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pick/Call")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Fire")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tjdredd_state::tjdredd(machine_config &config)
{
	sm510_tiger(config, 1444, 1080);
}

// roms

ROM_START( tjdredd )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mk5", 0x0000, 0x1000, CRC(7beee5a7) SHA1(9a190197c5751b43a9ab2dc8c536934dc5fc5e83) )

	ROM_REGION( 1051637, "screen", 0)
	ROM_LOAD( "tjdredd.svg", 0, 1051637, CRC(0f3541c6) SHA1(9838e2ae5806a48be595f53e6096b7c150b91651) )
ROM_END





/*******************************************************************************

  Tiger Apollo 13 (model 78-591) (licensed from UCS)
  * Sharp SM510 under epoxy (die label 10 07)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tapollo13_state : public hh_sm510_state
{
public:
	tapollo13_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tapollo13(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tapollo13 )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Thruster Left")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0a, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Parachute")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Thruster Right")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Capture")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tapollo13_state::tapollo13(machine_config &config)
{
	sm510_tiger(config, 1467, 1080);
}

// roms

ROM_START( tapollo13 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "10_07", 0x0000, 0x1000, CRC(63d0deaa) SHA1(d5de99d5e0ee08ec2ebeef7189ebac1c008d2e7d) )

	ROM_REGION( 643219, "screen", 0)
	ROM_LOAD( "tapollo13.svg", 0, 643219, CRC(f4b94141) SHA1(d67367212f2be1685de0f3acaebaae0dc67734c4) )
ROM_END





/*******************************************************************************

  Tiger 007: GoldenEye (model 78-594) (licensed from Danjaq / United Artists)
  * Sharp SM510 under epoxy (die label 10 06)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tgoldeye_state : public hh_sm510_state
{
public:
	tgoldeye_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tgoldeye(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tgoldeye )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Kick")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0a, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Q")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Fire")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tgoldeye_state::tgoldeye(machine_config &config)
{
	sm510_tiger(config, 1461, 1080);
}

// roms

ROM_START( tgoldeye )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "10_06", 0x0000, 0x1000, CRC(fe053efb) SHA1(3c90c0fa43e6e5e1f76b306e402f902d19175c96) )

	ROM_REGION( 938956, "screen", 0)
	ROM_LOAD( "tgoldeye.svg", 0, 938956, CRC(c4ad9836) SHA1(23555bd5fdaebed190ce02054a8ee681c88a8afb) )
ROM_END





/*******************************************************************************

  Tiger Kazaam (model 78-613) (licensed from Interscope Communications)
  * Sharp SM510 under epoxy (die label KMS10, 18)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tkazaam_state : public hh_sm510_state
{
public:
	tkazaam_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tkazaam(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tkazaam )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Kick")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0a, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pick/Rescue")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch Forward")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch Back")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tkazaam_state::tkazaam(machine_config &config)
{
	sm510_tiger(config, 1452, 1080); // no external XTAL
}

// roms

ROM_START( tkazaam )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "10_18", 0x0000, 0x1000, CRC(aa796372) SHA1(fdaea736e1df46c19fca08ed981e9659e038d15a) )

	ROM_REGION( 929109, "screen", 0)
	ROM_LOAD( "tkazaam.svg", 0, 929109, CRC(5eb178d4) SHA1(bcf64df4f342d16d5fd3d4cb503fceb1e5485591) )
ROM_END





/*******************************************************************************

  Tiger Space Jam (model 78-621) (licensed from Warner Bros.)
  * Sharp SM510 under epoxy (die label KMS10, 23)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tsjam_state : public hh_sm510_state
{
public:
	tsjam_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tsjam(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tsjam )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Shoot/Block")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Tune/Steal")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tsjam_state::tsjam(machine_config &config)
{
	sm510_tiger(config, 1421, 1080); // no external XTAL
}

// roms

ROM_START( tsjam )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "10_23", 0x0000, 0x1000, CRC(6eaabfbd) SHA1(f0ecbd6f65fe72ce2d8a452685be2e77a63fc9f0) )

	ROM_REGION( 1046158, "screen", 0)
	ROM_LOAD( "tsjam.svg", 0, 1046158, CRC(29187365) SHA1(f277a064e6ecd6219c930736a0bdf56196279b42) )
ROM_END





/*******************************************************************************

  Tiger Independence Day (aka ID4) (model 78-624) (licensed from Fox)
  * Sharp SM510 under epoxy (die label 10 16)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class tinday_state : public hh_sm510_state
{
public:
	tinday_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tinday(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tinday )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_NAME("Shield")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Fire")
	PORT_BIT( 0x0a, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alert")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Velocity")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tinday_state::tinday(machine_config &config)
{
	sm510_tiger(config, 1463, 1080);
}

// roms

ROM_START( tinday )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "10_16", 0x0000, 0x1000, CRC(77c2c2f7) SHA1(06326b26d0f6757180724ba0bdeb4110cc7e29d6) )

	ROM_REGION( 1162716, "screen", 0)
	ROM_LOAD( "tinday.svg", 0, 1162716, CRC(97056e62) SHA1(0348f0aca5d9b8e24f83c7e73b5e31f419fe60df) )
ROM_END





/*******************************************************************************

  Tiger Batman: The Animated Series (model 72-505) (licensed from DC Comics)
  * Sharp SM511 under epoxy (die label N81)
  * lcd screen with custom segments, 2-bit sound

*******************************************************************************/

class tbatmana_state : public hh_sm510_state
{
public:
	tbatmana_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tbatmana(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tbatmana )
	PORT_START("IN.0") // S2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY PORT_NAME("Jump")
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S3
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY PORT_NAME("Fast")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S5
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Throw/Attack")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S6
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Max Score")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tbatmana_state::tbatmana(machine_config &config)
{
	sm511_tiger2bit(config, 1478, 1080);
}

// roms

ROM_START( tbatmana )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "n81.program", 0x0000, 0x1000, CRC(efb3f122) SHA1(d55c2fb92fb9bd41d6001f42143691b84f3f389a) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "n81.melody", 0x000, 0x100, CRC(56ba8fe5) SHA1(5c286ae1bfc943bbe8c8f4cdc9c8b73d9b3c186e) )

	ROM_REGION( 618872, "screen", 0)
	ROM_LOAD( "tbatmana.svg", 0, 618872, CRC(8c721273) SHA1(d8c52441466943254bffcd6449af47a9fad6296b) )
ROM_END





/*******************************************************************************

  Tiger Mighty Max (model 72-544) (licensed from Film Roman / Bluebird Toys / Canal+)
  * Sharp SM511 under epoxy (die label KMS73B, ND1)
  * lcd screen with custom segments, 2-bit sound

*******************************************************************************/

class tmigmax_state : public hh_sm510_state
{
public:
	tmigmax_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tmigmax(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tmigmax )
	PORT_START("IN.0") // S2
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S4
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S5
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pick")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Call")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Max Score")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tmigmax_state::tmigmax(machine_config &config)
{
	sm511_tiger2bit(config, 1474, 1080);
}

// roms

ROM_START( tmigmax )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "nd1.program", 0x0000, 0x1000, CRC(0950a05e) SHA1(829c722bf2c9554cb22baa6e987034c417196cc4) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "nd1.melody", 0x000, 0x100, CRC(9f8e719a) SHA1(6bf2e3e8cecf9ec659ca587d53a2428521b702d8) )

	ROM_REGION( 735451, "screen", 0)
	ROM_LOAD( "tmigmax.svg", 0, 735451, CRC(4d83d754) SHA1(ade145888aee60aac1a184d5b35754711dab22a0) )
ROM_END





/*******************************************************************************

  Tiger Gargoyles: Night Flight (Tiger) (model 72-816) (licensed from BVTV)
  * Sharp SM511 under epoxy (die label KMS73B, NE1)
  * lcd screen with custom segments, 2-bit sound

  MCU ROM is the same for Gargoyles: Night Flight, Superman.

*******************************************************************************/

class tgargnf_state : public hh_sm510_state
{
public:
	tgargnf_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tgargnf(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tgargnf )
	PORT_START("IN.0") // S2
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S4
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S5
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch/Claw")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Call")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Max Score")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // GND!
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed) PORT_NAME("Power On/Start")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_VOLUME_DOWN ) PORT_NAME("Sound")

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_POWER_OFF )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tgargnf_state::tgargnf(machine_config &config)
{
	sm511_tiger2bit(config, 1479, 1080);
}

// roms

ROM_START( tgargnf )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "ne1.program", 0x0000, 0x1000, CRC(187b1f47) SHA1(dc01a89ac856133f948453aca5367b49d1b4b42b) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "ne1.melody", 0x000, 0x100, CRC(840cfed6) SHA1(81590f6a6c2b1aa53a9e9260a0e79eb4ce2f2d62) )

	ROM_REGION( 1110760, "screen", 0)
	ROM_LOAD( "tgargnf.svg", 0, 1110760, CRC(7883d3a7) SHA1(2c382b483683f1b9ea9ba2dd7d27479c0524a511) )
ROM_END





/*******************************************************************************

  Tiger Superman (model 72-???) (licensed from DC Comics)
  * Sharp SM511 under epoxy (no decap, dumped electronically)
  * lcd screen with custom segments, 2-bit sound

  MCU ROM is the same for Gargoyles: Night Flight, Superman.

  In the same year, Tiger also sold a red & blue Superman handheld (model 71-093),
  it's not the same game as this.

*******************************************************************************/

class tsuperman_state : public hh_sm510_state
{
public:
	tsuperman_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{
		inp_fixed_last();
	}

	void tsuperman(machine_config &config);
};

// inputs

static INPUT_PORTS_START( tsuperman )
	PORT_INCLUDE( tgargnf )

	PORT_MODIFY("IN.3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Punch")
INPUT_PORTS_END

// config

void tsuperman_state::tsuperman(machine_config &config)
{
	sm511_tiger2bit(config, 1487, 1080);
}

// roms

ROM_START( tsuperman )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "ne1.program", 0x0000, 0x1000, CRC(187b1f47) SHA1(dc01a89ac856133f948453aca5367b49d1b4b42b) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "ne1.melody", 0x000, 0x100, CRC(840cfed6) SHA1(81590f6a6c2b1aa53a9e9260a0e79eb4ce2f2d62) )

	ROM_REGION( 853009, "screen", 0)
	ROM_LOAD( "tsuperman.svg", 0, 853009, CRC(d9a4a681) SHA1(d25a0198a705ed84ef00a9020819d047f10192a6) )
ROM_END





/*******************************************************************************

  Tronica Shuttle Voyage (MG-8)
  * Sharp SM510 label 0019 238E TRONICA (no decap)
  * lcd screen with custom segments, 1-bit sound

  Even though the serial is MG-8, the back of the game says 1983, newer than MG-9?
  Thief in Garden (model TG-18) is the exact same MCU, but different graphics.

*******************************************************************************/

class trshutvoy_state : public hh_sm510_state
{
public:
	trshutvoy_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void trshutvoy(machine_config &config);
	void tigarden(machine_config &config);
};

// inputs

static INPUT_PORTS_START( trshutvoy )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("3")

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("7")

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("8")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("9")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME(".")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("=")

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("+")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("-")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_CHANGED_CB(input_changed) PORT_NAME(u8"×")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME(u8"÷")

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_CHANGED_CB(input_changed) PORT_NAME("ALM")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH) PORT_CHANGED_CB(input_changed) PORT_NAME("%")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_CHANGED_CB(input_changed) PORT_NAME("C")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // S6
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // S7
	PORT_BIT( 0x01, 0x01, IPT_CUSTOM ) PORT_CONDITION("FAKE", 0x03, NOTEQUALS, 0x00) // Up/Sound
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // F

	PORT_START("IN.7") // S8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Z) PORT_CHANGED_CB(input_changed) PORT_NAME("Mode")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("FAKE") // Up/Sound are electronically the same button
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")
INPUT_PORTS_END

// config

void trshutvoy_state::trshutvoy(machine_config &config)
{
	sm510_common(config, 1496, 1080); // R mask option confirmed
}

void trshutvoy_state::tigarden(machine_config &config)
{
	sm510_common(config, 1515, 1080);
}

// roms

ROM_START( trshutvoy )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "0019_238e", 0x0000, 0x1000, CRC(8bd0eadd) SHA1(7bb5eb30d569901dce52d777bc01c0979e4afa06) )

	ROM_REGION( 221748, "screen", 0)
	ROM_LOAD( "trshutvoy.svg", 0, 221748, CRC(9e630b4e) SHA1(7e2a3a82519f29f7b1b92930604e010b5b9fdb06) )
ROM_END

ROM_START( tigarden )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "0019_238e", 0x0000, 0x1000, CRC(8bd0eadd) SHA1(7bb5eb30d569901dce52d777bc01c0979e4afa06) )

	ROM_REGION( 409134, "screen", 0)
	ROM_LOAD( "tigarden.svg", 0, 409134, CRC(628f4aee) SHA1(5fa4843be61b52660a932e0d1efad403cf12de88) )
ROM_END





/*******************************************************************************

  Tronica: Space Rescue (model MG-9), Thunder Ball (model FR-23)
  * PCB labels: SPACE RESCUE MG-9 080492 (MG-9)
                SPACE RESCUE MG-9 210982 (FR-23)
  * Sharp SM510 labels (no decap): 0015 224B TRONICA (MG-9)
                                   0015 236D TRONICA (FR-23)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class trsrescue_state : public hh_sm510_state
{
public:
	trsrescue_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void trsrescue(machine_config &config);
	void trthuball(machine_config &config);
};

// inputs

static INPUT_PORTS_START( trsrescue )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void trsrescue_state::trsrescue(machine_config &config)
{
	sm510_common(config, 1533, 1080); // R mask option confirmed
}

void trsrescue_state::trthuball(machine_config &config)
{
	sm510_common(config, 1599, 1080); // R mask option confirmed
}

// roms

ROM_START( trsrescue )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "0015_224b", 0x0000, 0x1000, CRC(f58a3832) SHA1(2d843b3520de66463e628cea9344a04015d1f5f1) )

	ROM_REGION( 178760, "screen", 0)
	ROM_LOAD( "trsrescue.svg", 0, 178760, CRC(40756fd3) SHA1(9762ebbe4753a3194d7f0844c91addb8e1f8930b) )
ROM_END

ROM_START( trthuball )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "0015_236d", 0x0000, 0x1000, CRC(f58a3832) SHA1(2d843b3520de66463e628cea9344a04015d1f5f1) )

	ROM_REGION( 175018, "screen", 0)
	ROM_LOAD( "trthuball.svg", 0, 175018, CRC(3404cc1d) SHA1(4cebe3c742c6947c6fddb8a84ae2f7d0cea1b527) )
ROM_END





/*******************************************************************************

  Tronica: Super Goal Keeper (model SK-10)
  * PCB labels: SK-10 280683 32-647-1
  * Sharp SM5A labels (no decap): 0132 238A TRONICA
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class trsgkeep_state : public hh_sm510_state
{
public:
	trsgkeep_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void trsgkeep(machine_config &config);
};

// inputs

static INPUT_PORTS_START( trsgkeep )
	PORT_START("IN.0") // R2
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.2") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Increase Score (Cheat)" ) // factory test, unpopulated on PCB -- this one multiplies scoring factor with 10
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void trsgkeep_state::trsgkeep(machine_config &config)
{
	sm5a_common(config, 1465, 1080); // R mask option confirmed
}

// roms

ROM_START( trsgkeep )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "0132_238a", 0x0000, 0x0740, CRC(db80f1ed) SHA1(030e18ec05bbc98a474105d8d6f503082ab638cc) )

	ROM_REGION( 119358, "screen", 0)
	ROM_LOAD( "trsgkeep.svg", 0, 119358, CRC(4108349e) SHA1(359fb4eee1cfd85965efd1308f0002ebf38d231a) )
ROM_END





/*******************************************************************************

  Tronica: Space Mission (model SM-11), Spider (model SG-21)
  * PCB labels: SPACE MISSION SM-11 250582 (SM-11)
                SPACE MISSION SM-11 220982 (SG-21)
  * Sharp SM5A labels (no decap): 0126 228B TRONICA (SM-11)
                                  0126 22YC TRONICA (SG-21)
  * lcd screen with custom segments, 1-bit sound

  SM-11 and SG-21 are the exact same MCU, but with different graphics.

  In 1983, Tronica released a second revision of SG-21 with different graphic
  overlays. This version can be distinguished by having the year 1983 labeled
  on the backside of the unit.

*******************************************************************************/

class trspacmis_state : public hh_sm510_state
{
public:
	trspacmis_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void trspacmis(machine_config &config);
	void trspider(machine_config & config);
};

// inputs

static INPUT_PORTS_START( trspacmis )
	PORT_START("IN.0") // R2
	PORT_CONFNAME( 0x01, 0x00, "Invincibility (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) // same as 0x01?
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED ) // display test?
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) // alarm test?

	PORT_START("IN.1") // R3
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("BA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("B")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void trspacmis_state::trspacmis(machine_config &config)
{
	sm5a_common(config, 1601, 1080); // R mask option confirmed
}

void trspacmis_state::trspider(machine_config &config)
{
	sm5a_common(config, 1597, 1080); // R mask option confirmed
}

// roms

ROM_START( trspacmis )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "0126_228b", 0x0000, 0x0740, CRC(3d319537) SHA1(007d376cfd29574554caa59ed9163178179ae9c5) )

	ROM_REGION( 106675, "screen", 0)
	ROM_LOAD( "trspacmis.svg", 0, 106675, CRC(45d7b798) SHA1(db08fef21462507a115547ecf8eac38260a0c868) )
ROM_END

ROM_START( trspider )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD( "0126_22yc", 0x0000, 0x0740, CRC(3d319537) SHA1(007d376cfd29574554caa59ed9163178179ae9c5) )

	ROM_REGION( 130534, "screen", 0)
	ROM_LOAD( "trspider.svg", 0, 130534, CRC(d3f00245) SHA1(348c47dbe87ce8537c9b1c91d0d8bbf0809546e8) )
ROM_END





/*******************************************************************************

  Tronica: Space Adventure (model SA-12)
  * PCB labels: SA-12 111182 32-523-1
  * Sharp SM511 label 1112 237C TRONICA (no decap)
  * lcd screen with custom segments, 1-bit sound

*******************************************************************************/

class trspacadv_state : public hh_sm510_state
{
public:
	trspacadv_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void trspacadv(machine_config &config);
};

// inputs

static INPUT_PORTS_START( trspacadv )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("3")

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("7")

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("8")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("9")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME(".")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("=")

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("+")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("-")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_CHANGED_CB(input_changed) PORT_NAME(u8"×")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME(u8"÷")

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_CHANGED_CB(input_changed) PORT_NAME("ALM")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH) PORT_CHANGED_CB(input_changed) PORT_NAME("%")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_CHANGED_CB(input_changed) PORT_NAME("C")

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, 0x01, IPT_CUSTOM ) PORT_CONDITION("FAKE", 0x03, NOTEQUALS, 0x00) // Left/Sound
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Fire") // F
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // S7
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.7") // S8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Z) PORT_CHANGED_CB(input_changed) PORT_NAME("Mode")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("FAKE") // Left/Sound are electronically the same button
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")
INPUT_PORTS_END

// config

void trspacadv_state::trspacadv(machine_config &config)
{
	sm511_common(config, 1532, 1080);
}

// roms

ROM_START( trspacadv )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "1112_237c.program", 0x0000, 0x1000, CRC(7c863fbc) SHA1(1c65f9f8166162c2cd203967ae3d0d49cc14baf7) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "1112_237c.melody", 0x000, 0x100, BAD_DUMP CRC(685a3305) SHA1(31b948dbd0fbae3517db505bc58904092c2b077f) ) // decap needed for verification

	ROM_REGION( 117824, "screen", 0)
	ROM_LOAD( "trspacadv.svg", 0, 117824, CRC(5b7c7f28) SHA1(3851eb2221660a33c521bbd3b01d7a76a558d07c) )
ROM_END





/*******************************************************************************

  Tronica Treasure Island (model TI-31)
  * PCB labels: TI-31 160383 32-537-1
  * Sharp SM511 label 1113 239B TRONICA (no decap)
  * horizontal triple lcd screens with custom segments, 1-bit sound

*******************************************************************************/

class trtreisl_state : public hh_sm510_state
{
public:
	trtreisl_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void trtreisl(machine_config &config);
};

// inputs

static INPUT_PORTS_START( trtreisl )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, 0x01, IPT_CUSTOM ) PORT_CONDITION("FAKE", 0x03, NOTEQUALS, 0x00) // Button/Hour
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE4 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Min")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x00, "Infinite Lives (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )

	PORT_START("BA")
	PORT_CONFNAME( 0x01, 0x00, "Score Booster (Cheat)" ) // factory test, unpopulated on PCB -- this one multiplies scoring factor with 10
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )

	PORT_START("FAKE") // SJP and Hour are electronically the same button
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Start/Jump/Pick")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_SERVICE3 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Hour")
INPUT_PORTS_END

// config

void trtreisl_state::trtreisl(machine_config &config)
{
	sm511_tripleh(config, 525, 675, 870, 675, 525, 675); // 1555 x 547
}

// roms

ROM_START( trtreisl )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "1113_239b.program", 0x0000, 0x1000, CRC(fec16266) SHA1(f2725a077cff9dbed9cd324ff802d10dcbdb4451) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "1113_239b.melody", 0x000, 0x100, BAD_DUMP CRC(2de3f974) SHA1(17e7a3947a457580efefa544c3132e93e20cbc71) ) // decap needed for verification

	ROM_REGION( 59803, "screen_left", 0)
	ROM_LOAD( "trtreisl_left.svg", 0, 59803, CRC(92f5f11e) SHA1(fdf8edf7742d2e0d2893e2226872c897e966f64e) )

	ROM_REGION( 94316, "screen_middle", 0)
	ROM_LOAD( "trtreisl_middle.svg", 0, 94316, CRC(adcf5308) SHA1(0c7932e4ba7a3daba24f0ff385c21035b86f4e23) )

	ROM_REGION( 46905, "screen_right", 0)
	ROM_LOAD( "trtreisl_right.svg", 0, 46905, CRC(26e6b4bd) SHA1(7469f2d5291f20621dc3f2b7f260cf4b4681f95b) )
ROM_END





/*******************************************************************************

  Tronica: Diver's Adventure (model DA-37), Clever Chicken (model CC-38V)
  * PCB labels: DA-37 260383 32-541-1 (DA-37)
                CC38V 210483 32-545-1 (CC-38V)
  * Sharp SM510 labels (no decap): 0029 235D TRONICA (DA-37)
                                   0029 238C TRONICA (CC-38V)
  * lcd screen with custom segments, 1-bit sound

  DA-37 and CC-38V are the exact same MCU, but with different graphics. The
  player moves horizontally in DA-37 and vertically in CC-38V.

*******************************************************************************/

class trdivadv_state : public hh_sm510_state
{
public:
	trdivadv_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void trdivadv(machine_config &config);
	void trclchick(machine_config &config);
};

// inputs

static INPUT_PORTS_START( trdivadv )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")

	PORT_START("B")
	PORT_CONFNAME( 0x01, 0x01, "Invincibility (Cheat)" ) // factory test, unpopulated on PCB
	PORT_CONFSETTING(    0x01, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
INPUT_PORTS_END

static INPUT_PORTS_START( trclchick )
	PORT_INCLUDE( trdivadv )

	PORT_MODIFY("IN.0") // S1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed) PORT_16WAY
INPUT_PORTS_END

// config

void trdivadv_state::trdivadv(machine_config &config)
{
	sm510_common(config, 1520, 1080);
}

void trdivadv_state::trclchick(machine_config &config)
{
	sm510_common(config, 811, 1080);
}

// roms

ROM_START( trdivadv )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "0029_235d", 0x0000, 0x1000, CRC(8977a1cf) SHA1(9ac413efedcff8b53b859420c0575c66e7be6e73) )

	ROM_REGION( 165418, "screen", 0)
	ROM_LOAD( "trdivadv.svg", 0, 165418, CRC(727040f1) SHA1(2318d6973a165eedcd369bd11342eca7efd24c39) )
ROM_END

ROM_START( trclchick )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "0029_238c", 0x0000, 0x1000, CRC(8977a1cf) SHA1(9ac413efedcff8b53b859420c0575c66e7be6e73) )

	ROM_REGION( 122284, "screen", 0)
	ROM_LOAD( "trclchick.svg", 0, 122284, CRC(c8e67d54) SHA1(d3d113c7bcb597fafddb0fab5410808360ad9a4b) )
ROM_END





/*******************************************************************************

  VTech Electronic Number Muncher
  * Sharp SM511 under epoxy (die label 772)
  * lcd screen with custom segments(no background), 1-bit sound

*******************************************************************************/

class nummunch_state : public hh_sm510_state
{
public:
	nummunch_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag)
	{ }

	void nummunch(machine_config &config);
};

// inputs

static INPUT_PORTS_START( nummunch )
	PORT_START("IN.0") // S1
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // S2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_CHANGED_CB(input_changed) PORT_NAME("Calc. / Clear")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("=")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // S3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_CHANGED_CB(input_changed) PORT_NAME("Game")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_CHANGED_CB(input_changed) PORT_NAME("Count")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_CHANGED_CB(input_changed) PORT_NAME("Quiz")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // S4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_CHANGED_CB(input_changed) PORT_NAME("Choose +")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_CHANGED_CB(input_changed) PORT_NAME("Choose -")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_CHANGED_CB(input_changed) PORT_NAME(u8"Choose ×")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_CHANGED_CB(input_changed) PORT_NAME(u8"Choose ÷")

	PORT_START("IN.4") // S5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("0")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_LEFT) PORT_CHANGED_CB(input_changed) PORT_NAME("Left")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_RIGHT) PORT_CHANGED_CB(input_changed) PORT_NAME("Right")

	PORT_START("IN.5") // S6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("5")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("4")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("2")

	PORT_START("IN.6") // S7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("9")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("7")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("6")

	PORT_START("IN.7") // S8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("+")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME("-")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_CHANGED_CB(input_changed) PORT_NAME(u8"×")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHANGED_CB(input_changed) PORT_NAME(u8"÷")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_ON ) PORT_CHANGED_CB(acl_button)
INPUT_PORTS_END

// config

void nummunch_state::nummunch(machine_config &config)
{
	sm511_common(config, 1920, 875);
}

// roms

ROM_START( nummunch )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "772.program", 0x0000, 0x1000, CRC(2f7ff516) SHA1(132e7c5c4d69170953b2e51731992d6d6ba829f9) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "772.melody", 0x000, 0x100, CRC(96fe463a) SHA1(dcef5eee15a3f6d21e0db1b8ae3fbddc81633fc8) )

	ROM_REGION( 140704, "screen", 0)
	ROM_LOAD( "nummunch.svg", 0, 140704, CRC(050301d7) SHA1(3671d0e1b0cc788d74df0c6adb57a01729f66d7c) )
ROM_END



} // anonymous namespace

/*******************************************************************************

  Game driver(s)

*******************************************************************************/

//    YEAR  NAME          PARENT       COMPAT  MACHINE       INPUT         CLASS               INIT        COMPANY, FULLNAME, FLAGS

// Nintendo G&W: Silver/Gold (initial series is uncategorized, "Silver" was made up later)
SYST( 1980, gnw_ball,     0,           0,      gnw_ball,     gnw_ball,     gnw_ball_state,     empty_init, "Nintendo", "Game & Watch: Ball", MACHINE_SUPPORTS_SAVE )
SYST( 1980, gnw_flagman,  0,           0,      gnw_flagman,  gnw_flagman,  gnw_flagman_state,  empty_init, "Nintendo", "Game & Watch: Flagman", MACHINE_SUPPORTS_SAVE )
SYST( 1980, gnw_vermin,   0,           0,      gnw_vermin,   gnw_vermin,   gnw_vermin_state,   empty_init, "Nintendo", "Game & Watch: Vermin", MACHINE_SUPPORTS_SAVE )
SYST( 1980, gnw_fires,    0,           0,      gnw_fires,    gnw_fires,    gnw_fires_state,    empty_init, "Nintendo", "Game & Watch: Fire (Silver)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1980, gnw_judge,    0,           0,      gnw_judge,    gnw_judge,    gnw_judge_state,    empty_init, "Nintendo", "Game & Watch: Judge (purple version)", MACHINE_SUPPORTS_SAVE )
SYST( 1980, gnw_judgeo,   gnw_judge,   0,      gnw_judge,    gnw_judge,    gnw_judge_state,    empty_init, "Nintendo", "Game & Watch: Judge (green version)", MACHINE_SUPPORTS_SAVE )
SYST( 1981, gnw_manholeg, 0,           0,      gnw_manholeg, gnw_manholeg, gnw_manholeg_state, empty_init, "Nintendo", "Game & Watch: Manhole (Gold)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1981, gnw_helmet,   0,           0,      gnw_helmet,   gnw_helmet,   gnw_helmet_state,   empty_init, "Nintendo", "Game & Watch: Helmet (version CN-17)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1981, gnw_helmeto,  gnw_helmet,  0,      gnw_helmet,   gnw_helmet,   gnw_helmet_state,   empty_init, "Nintendo", "Game & Watch: Helmet (version CN-07)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1981, gnw_lion,     0,           0,      gnw_lion,     gnw_lion,     gnw_lion_state,     empty_init, "Nintendo", "Game & Watch: Lion", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )

// Nintendo G&W: Wide Screen
SYST( 1981, gnw_pchute,   0,           0,      gnw_pchute,   gnw_pchute,   gnw_pchute_state,   empty_init, "Nintendo", "Game & Watch: Parachute", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1981, gnw_octopus,  0,           0,      gnw_octopus,  gnw_octopus,  gnw_octopus_state,  empty_init, "Nintendo", "Game & Watch: Octopus", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1981, gnw_popeye,   0,           0,      gnw_popeye,   gnw_popeye,   gnw_popeye_state,   empty_init, "Nintendo", "Game & Watch: Popeye (Wide Screen)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1981, gnw_chef,     0,           0,      gnw_chef,     gnw_chef,     gnw_chef_state,     empty_init, "Nintendo", "Game & Watch: Chef", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1981, gnw_mmouse,   0,           0,      gnw_mmouse,   gnw_mmouse,   gnw_mmouse_state,   empty_init, "Nintendo", "Game & Watch: Mickey Mouse (Wide Screen)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1981, gnw_egg,      gnw_mmouse,  0,      gnw_egg,      gnw_mmouse,   gnw_mmouse_state,   empty_init, "Nintendo", "Game & Watch: Egg", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1981, gnw_fire,     0,           0,      gnw_fire,     gnw_fire,     gnw_fire_state,     empty_init, "Nintendo", "Game & Watch: Fire (Wide Screen)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1982, gnw_tbridge,  0,           0,      gnw_tbridge,  gnw_tbridge,  gnw_tbridge_state,  empty_init, "Nintendo", "Game & Watch: Turtle Bridge", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1982, gnw_fireatk,  0,           0,      gnw_fireatk,  gnw_fireatk,  gnw_fireatk_state,  empty_init, "Nintendo", "Game & Watch: Fire Attack", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1982, gnw_stennis,  0,           0,      gnw_stennis,  gnw_stennis,  gnw_stennis_state,  empty_init, "Nintendo", "Game & Watch: Snoopy Tennis", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )

// Nintendo G&W: Multi Screen
SYST( 1982, gnw_opanic,   0,           0,      gnw_opanic,   gnw_opanic,   gnw_opanic_state,   empty_init, "Nintendo", "Game & Watch: Oil Panic", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1982, gnw_dkong,    0,           0,      gnw_dkong,    gnw_dkong,    gnw_dkong_state,    empty_init, "Nintendo", "Game & Watch: Donkey Kong", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1982, gnw_mickdon,  0,           0,      gnw_mickdon,  gnw_mickdon,  gnw_mickdon_state,  empty_init, "Nintendo", "Game & Watch: Mickey & Donald", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1982, gnw_ghouse,   0,           0,      gnw_ghouse,   gnw_ghouse,   gnw_ghouse_state,   empty_init, "Nintendo", "Game & Watch: Green House", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1983, gnw_dkong2,   0,           0,      gnw_dkong2,   gnw_dkong2,   gnw_dkong2_state,   empty_init, "Nintendo", "Game & Watch: Donkey Kong II", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1983, gnw_mario,    0,           0,      gnw_mario,    gnw_mario,    gnw_mario_state,    empty_init, "Nintendo", "Game & Watch: Mario Bros.", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1983, gnw_rshower,  0,           0,      gnw_rshower,  gnw_rshower,  gnw_rshower_state,  empty_init, "Nintendo", "Game & Watch: Rain Shower", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1983, gnw_lboat,    0,           0,      gnw_lboat,    gnw_lboat,    gnw_lboat_state,    empty_init, "Nintendo", "Game & Watch: Life Boat", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1983, gnw_pinball,  0,           0,      gnw_pinball,  gnw_pinball,  gnw_pinball_state,  empty_init, "Nintendo", "Game & Watch: Pinball", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1985, gnw_bjack,    0,           0,      gnw_bjack,    gnw_bjack,    gnw_bjack_state,    empty_init, "Nintendo", "Game & Watch: Black Jack", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1986, gnw_squish,   0,           0,      gnw_squish,   gnw_squish,   gnw_squish_state,   empty_init, "Nintendo", "Game & Watch: Squish", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1987, gnw_bsweep,   0,           0,      gnw_bsweep,   gnw_bsweep,   gnw_bsweep_state,   empty_init, "Nintendo", "Game & Watch: Bomb Sweeper", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1988, gnw_sbuster,  0,           0,      gnw_sbuster,  gnw_sbuster,  gnw_sbuster_state,  empty_init, "Nintendo", "Game & Watch: Safe Buster", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1988, gnw_gcliff,   0,           0,      gnw_gcliff,   gnw_gcliff,   gnw_gcliff_state,   empty_init, "Nintendo", "Game & Watch: Gold Cliff", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1989, gnw_zelda,    0,           0,      gnw_zelda,    gnw_zelda,    gnw_zelda_state,    empty_init, "Nintendo", "Game & Watch: Zelda", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )

// Nintendo G&W: New Wide Screen / Crystal Screen
SYST( 1982, gnw_dkjr,     0,           0,      gnw_dkjr,     gnw_dkjr,     gnw_dkjr_state,     empty_init, "Nintendo", "Game & Watch: Donkey Kong Jr. (New Wide Screen)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1983, gnw_mariocm,  0,           0,      gnw_mariocm,  gnw_mariocm,  gnw_mariocm_state,  empty_init, "Nintendo", "Game & Watch: Mario's Cement Factory (New Wide Screen)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1983, gnw_manhole,  0,           0,      gnw_manhole,  gnw_manhole,  gnw_manhole_state,  empty_init, "Nintendo", "Game & Watch: Manhole (New Wide Screen)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1985, gnw_tfish,    0,           0,      gnw_tfish,    gnw_tfish,    gnw_tfish_state,    empty_init, "Nintendo", "Game & Watch: Tropical Fish", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1986, gnw_smb,      0,           0,      gnw_smb,      gnw_smb,      gnw_smb_state,      empty_init, "Nintendo", "Game & Watch: Super Mario Bros. (Crystal Screen)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1988, gnw_smbn,     gnw_smb,     0,      gnw_smbn,     gnw_smb,      gnw_smb_state,      empty_init, "Nintendo", "Game & Watch: Super Mario Bros. (New Wide Screen)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1986, gnw_climber,  0,           0,      gnw_climber,  gnw_climber,  gnw_climber_state,  empty_init, "Nintendo", "Game & Watch: Climber (Crystal Screen)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1988, gnw_climbern, gnw_climber, 0,      gnw_climbern, gnw_climber,  gnw_climber_state,  empty_init, "Nintendo", "Game & Watch: Climber (New Wide Screen)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1986, gnw_bfight,   0,           0,      gnw_bfight,   gnw_bfight,   gnw_bfight_state,   empty_init, "Nintendo", "Game & Watch: Balloon Fight (Crystal Screen)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1988, gnw_bfightn,  gnw_bfight,  0,      gnw_bfightn,  gnw_bfight,   gnw_bfight_state,   empty_init, "Nintendo", "Game & Watch: Balloon Fight (New Wide Screen)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, gnw_mariotj,  0,           0,      gnw_mariotj,  gnw_mariotj,  gnw_mariotj_state,  empty_init, "Nintendo", "Game & Watch: Mario The Juggler", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )

// Nintendo G&W: Table Top / Panorama Screen (the first Table Top releases in Japan were called "Color Screen")
SYST( 1983, gnw_mariocmt, 0,           0,      gnw_mariocmt, gnw_mariocmt, gnw_mariocmt_state, empty_init, "Nintendo", "Game & Watch: Mario's Cement Factory (Table Top, version CM-72)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK ) // "Another one bites the dust" startup jingle
SYST( 1983, gnw_mariocmta,gnw_mariocmt,0,      gnw_mariocmt, gnw_mariocmt, gnw_mariocmt_state, empty_init, "Nintendo", "Game & Watch: Mario's Cement Factory (Table Top, version CM-72A)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK ) // Plays an alternate jingle when starting a game
SYST( 1983, gnw_snoopyp,  0,           0,      gnw_snoopyp,  gnw_snoopyp,  gnw_snoopyp_state,  empty_init, "Nintendo", "Game & Watch: Snoopy (Panorama Screen)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1983, gnw_popeyep,  0,           0,      gnw_popeyep,  gnw_popeyep,  gnw_popeyep_state,  empty_init, "Nintendo", "Game & Watch: Popeye (Panorama Screen)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1983, gnw_dkjrp,    0,           0,      gnw_dkjrp,    gnw_dkjrp,    gnw_dkjrp_state,    empty_init, "Nintendo", "Game & Watch: Donkey Kong Jr. (Panorama Screen)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1983, gnw_mbaway,   0,           0,      gnw_mbaway,   gnw_mbaway,   gnw_mbaway_state,   empty_init, "Nintendo", "Game & Watch: Mario's Bombs Away", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1984, gnw_mmousep,  0,           0,      gnw_mmousep,  gnw_mmousep,  gnw_mmousep_state,  empty_init, "Nintendo", "Game & Watch: Mickey Mouse (Panorama Screen)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1984, gnw_dkcirc,   gnw_mmousep, 0,      gnw_dkcirc,   gnw_mmousep,  gnw_mmousep_state,  empty_init, "Nintendo", "Game & Watch: Donkey Kong Circus", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )

// Nintendo G&W: Super Color
SYST( 1984, gnw_ssparky,  0,           0,      gnw_ssparky,  gnw_ssparky,  gnw_ssparky_state,  empty_init, "Nintendo", "Game & Watch: Spitball Sparky", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1984, gnw_cgrab,    0,           0,      gnw_cgrab,    gnw_cgrab,    gnw_cgrab_state,    empty_init, "Nintendo", "Game & Watch: Crab Grab", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )

// Nintendo G&W: Micro Vs. System (actually, no official Game & Watch logo anywhere)
SYST( 1984, gnw_boxing,   0,           0,      gnw_boxing,   gnw_boxing,   gnw_boxing_state,   empty_init, "Nintendo", "Micro Vs. System: Boxing", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1984, gnw_dkong3,   0,           0,      gnw_dkong3,   gnw_dkong3,   gnw_dkong3_state,   empty_init, "Nintendo", "Micro Vs. System: Donkey Kong 3", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1984, gnw_dkhockey, 0,           0,      gnw_dkhockey, gnw_dkhockey, gnw_dkhockey_state, empty_init, "Nintendo", "Micro Vs. System: Donkey Kong Hockey", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )

// Nintendo G&W hardware licensed to other companies (not part of G&W series)
SYST( 1984, bassmate,     0,           0,      bassmate,     bassmate,     bassmate_state,     empty_init, "Telko / Nintendo", "Bassmate Computer", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )

// Elektronika (G&W clones)
SYST( 1988, taynyoke,     gnw_octopus, 0,      taynyoke,     gnw_octopus,  gnw_octopus_state,  empty_init, "bootleg (Elektronika)", "Tayny okeana", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1989, vespovar,     gnw_chef,    0,      vespovar,     gnw_chef,     gnw_chef_state,     empty_init, "bootleg (Elektronika)", "Vesyolyy povar", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1983, nupogodi,     gnw_mmouse,  0,      nupogodi,     gnw_mmouse,   nupogodi_state,     empty_init, "bootleg (Elektronika)", "Nu, pogodi!", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1988, ehockey,      gnw_mmouse,  0,      ehockey,      gnw_mmouse,   nupogodi_state,     empty_init, "bootleg (Elektronika)", "Hockey (Elektronika)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1989, rkosmosa,     gnw_mmouse,  0,      rkosmosa,     rkosmosa,     nupogodi_state,     empty_init, "bootleg (Elektronika)", "Razvedchiki kosmosa", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1989, okhota,       gnw_mmouse,  0,      okhota,       gnw_mmouse,   nupogodi_state,     empty_init, "bootleg (Elektronika)", "Okhota", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1989, biathlon,     gnw_mmouse,  0,      biathlon,     gnw_mmouse,   nupogodi_state,     empty_init, "bootleg (Elektronika)", "Biathlon", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1989, vfutbol,      gnw_mmouse,  0,      vfutbol,      gnw_mmouse,   nupogodi_state,     empty_init, "bootleg (Elektronika)", "Vesyolye futbolisty", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1989, krybolov,     gnw_mmouse,  0,      krybolov,     gnw_mmouse,   nupogodi_state,     empty_init, "bootleg (Elektronika)", "Kot-rybolov (Elektronika)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1989, kvakazad,     gnw_mmouse,  0,      kvakazad,     gnw_mmouse,   nupogodi_state,     empty_init, "bootleg (Elektronika)", "Kvaka-zadavaka", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 19??, nochnyev,     gnw_mmouse,  0,      nochnyev,     gnw_mmouse,   nupogodi_state,     empty_init, "bootleg (Elektronika)", "Nochnye vorishki", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 19??, kosmicpt,     gnw_mmouse,  0,      kosmicpt,     gnw_mmouse,   nupogodi_state,     empty_init, "bootleg (Elektronika)", "Kosmicheskiy polyot", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 19??, morataka,     gnw_mmouse,  0,      morataka,     gnw_mmouse,   nupogodi_state,     empty_init, "bootleg (Elektronika)", "Morskaja ataka", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1992, atakaast,     gnw_mmouse,  0,      atakaast,     gnw_mmouse,   nupogodi_state,     empty_init, "bootleg (Elektronika)", "Ataka asteroidov", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 19??, ecircus,      gnw_mmouse,  0,      ecircus,      gnw_mmouse,   nupogodi_state,     empty_init, "bootleg (Elektronika)", "Circus (Elektronika)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, naltair,      gnw_mmouse,  0,      vfutbol,      naltair,      nupogodi_state,     empty_init, "bootleg (Nauchpribor)", "Altair (Nauchpribor)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK | MACHINE_IMPERFECT_CONTROLS )
SYST( 1989, kosmicmt,     gnw_fire,    0,      kosmicmt,     gnw_fire,     gnw_fire_state,     empty_init, "bootleg (Elektronika)", "Kosmicheskiy most", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 199?, vinnpukh,     gnw_dkjrp,   0,      vinnpukh,     gnw_dkjrp,    gnw_dkjrp_state,    empty_init, "bootleg (Elektronika)", "Vinni-Pukh", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )

// Elektronika (original)
SYST( 1990, auslalom,     0,           0,      auslalom,     auslalom,     auslalom_state,     empty_init, "Elektronika", "Autoslalom", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 199?, elbaskb,      0,           0,      elbaskb,      elbaskb,      elbaskb_state,      empty_init, "Elektronika", "Basketbol (Elektronika)", MACHINE_SUPPORTS_SAVE )
SYST( 1992, vesarif,      0,           0,      vesarif,      vesarif,      vesarif_state,      empty_init, "Elektronika", "Vesolaya arifmetika", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, uchitari,     0,           0,      uchitari,     uchitari,     uchitari_state,     empty_init, "Elektronika", "Uchitel' arifmetiki (prototype?)", MACHINE_SUPPORTS_SAVE )

// Konami
SYST( 1989, kdribble,     0,           0,      kdribble,     kdribble,     kdribble_state,     empty_init, "Konami", "Double Dribble (handheld)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1989, kcontra,      0,           0,      kcontra,      kcontra,      kcontra_state,      empty_init, "Konami", "Contra (handheld)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1989, ktopgun,      0,           0,      ktopgun,      ktopgun,      ktopgun_state,      empty_init, "Konami", "Top Gun (handheld)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1989, kgradius,     0,           0,      kgradius,     kgradius,     kgradius_state,     empty_init, "Konami", "Gradius (handheld)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1989, ktmnt,        0,           0,      ktmnt,        ktmnt,        ktmnt_state,        empty_init, "Konami", "Teenage Mutant Ninja Turtles (handheld)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1989, kskatedie,    0,           0,      kskatedie,    kskatedie,    kskatedie_state,    empty_init, "Konami", "Skate or Die (Konami, handheld)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1990, kbilly,       0,           0,      kbilly,       kbilly,       kbilly_state,       empty_init, "Konami", "The Adventures of Bayou Billy (handheld)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1990, kbottom9,     0,           0,      kbottom9,     kbottom9,     kbottom9_state,     empty_init, "Konami", "Bottom of the Ninth (handheld)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1990, kloneran,     0,           0,      kloneran,     kloneran,     kloneran_state,     empty_init, "Konami", "The Lone Ranger (handheld)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1990, knascar,      0,           0,      knascar,      knascar,      knascar_state,      empty_init, "Konami", "Bill Elliott's NASCAR Racing (handheld)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1990, kchqflag,     knascar,     0,      kchqflag,     knascar,      knascar_state,      empty_init, "Konami", "Chequered Flag (handheld)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1990, kblades,      0,           0,      kblades,      kblades,      kblades_state,      empty_init, "Konami", "Blades of Steel (handheld)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1990, ktmnt2,       0,           0,      ktmnt2,       ktmnt2,       ktmnt2_state,       empty_init, "Konami", "Teenage Mutant Ninja Turtles II: Splinter Speaks (handheld)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK | MACHINE_IMPERFECT_SOUND )
SYST( 1990, knfl,         0,           0,      knfl,         knfl,         knfl_state,         empty_init, "Konami", "NFL Football (Konami, handheld)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, kst25,        0,           0,      kst25,        kst25,        kst25_state,        empty_init, "Konami", "Star Trek: 25th Anniversary (handheld)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK | MACHINE_IMPERFECT_SOUND )
SYST( 1991, ktopgun2,     0,           0,      ktopgun2,     ktopgun2,     ktopgun2_state,     empty_init, "Konami", "Top Gun: Second Mission (handheld)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK | MACHINE_IMPERFECT_SOUND )
SYST( 1991, ktmnt3,       0,           0,      ktmnt3,       ktmnt3,       ktmnt3_state,       empty_init, "Konami", "Teenage Mutant Ninja Turtles 3: Shredder's Last Stand (handheld)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, ktmntbb,      0,           0,      ktmntbb,      ktmntbb,      ktmntbb_state,      empty_init, "Konami", "Teenage Mutant Ninja Turtles: Basketball", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, kbucky,       0,           0,      kbucky,       kbucky,       kbucky_state,       empty_init, "Konami", "Bucky O'Hare (handheld)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, kgarfld,      0,           0,      kgarfld,      kgarfld,      kgarfld_state,      empty_init, "Konami", "Garfield (Konami)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )

// Nelsonic Game Watch series
SYST( 1990, nsmb3,        0,           0,      nsmb3,        gamewatch,    gamewatch_state,    empty_init, "Nelsonic", "Super Mario Bros. 3 (Nelsonic)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, nsmw,         0,           0,      nsmw,         gamewatch,    gamewatch_state,    empty_init, "Nelsonic", "Super Mario World (Nelsonic)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1993, nstarfox,     0,           0,      nstarfox,     nstarfox,     nstarfox_state,     empty_init, "Nelsonic", "Star Fox (Nelsonic)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK | MACHINE_IMPERFECT_SOUND )

// Tiger 7-xxx/78-xxx models
SYST( 1988, tgaunt,       0,           0,      tgaunt,       tgaunt,       tgaunt_state,       empty_init, "Tiger Electronics", "Gauntlet (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1988, tddragon,     0,           0,      tddragon,     tddragon,     tddragon_state,     empty_init, "Tiger Electronics", "Double Dragon (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1988, tsimquest,    0,           0,      tsimquest,    tsimquest,    tsimquest_state,    empty_init, "Tiger Electronics", "Castlevania II: Simon's Quest (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1988, tkarnov,      0,           0,      tkarnov,      tkarnov,      tkarnov_state,      empty_init, "Tiger Electronics", "Karnov (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1989, tvindictr,    0,           0,      tvindictr,    tvindictr,    tvindictr_state,    empty_init, "Tiger Electronics", "Vindicators (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1989, tgaiden,      0,           0,      tgaiden,      tgaiden,      tgaiden_state,      empty_init, "Tiger Electronics", "Ninja Gaiden (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1990, tddragon2,    0,           0,      tddragon2,    tddragon2,    tddragon2_state,    empty_init, "Tiger Electronics", "Double Dragon II: The Revenge (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1989, tbatman,      0,           0,      tbatman,      tbatman,      tbatman_state,      empty_init, "Tiger Electronics", "Batman (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1990, tsharr2,      0,           0,      tsharr2,      tsharr2,      tsharr2_state,      empty_init, "Tiger Electronics", "Space Harrier II (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1990, tstrider,     0,           0,      tstrider,     tstrider,     tstrider_state,     empty_init, "Tiger Electronics", "Strider (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1990, tgoldnaxe,    0,           0,      tgoldnaxe,    tgoldnaxe,    tgoldnaxe_state,    empty_init, "Tiger Electronics", "Golden Axe (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1990, trobocop2,    0,           0,      trobocop2,    trobocop2,    trobocop2_state,    empty_init, "Tiger Electronics", "Robocop 2 (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1990, taltbeast,    0,           0,      taltbeast,    taltbeast,    taltbeast_state,    empty_init, "Tiger Electronics", "Altered Beast (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, tmegaman3,    0,           0,      tmegaman3,    tmegaman3,    tmegaman3_state,    empty_init, "Tiger Electronics", "Mega Man 3 (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, tsf2010,      0,           0,      tsf2010,      tsf2010,      tsf2010_state,      empty_init, "Tiger Electronics", "Street Fighter 2010: The Final Fight (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, tswampt,      0,           0,      tswampt,      tswampt,      tswampt_state,      empty_init, "Tiger Electronics", "Swamp Thing (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, tspidman,     0,           0,      tspidman,     tspidman,     tspidman_state,     empty_init, "Tiger Electronics", "Spider-Man (Tiger, 1991 version)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, txmen,        0,           0,      txmen,        txmen,        txmen_state,        empty_init, "Tiger Electronics", "X-Men (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, tddragon3,    0,           0,      tddragon3,    tddragon3,    tddragon3_state,    empty_init, "Tiger Electronics", "Double Dragon 3: The Rosetta Stone (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, tflash,       0,           0,      tflash,       tflash,       tflash_state,       empty_init, "Tiger Electronics", "The Flash (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, trobhood,     tgaunt,      0,      trobhood,     trobhood,     trobhood_state,     empty_init, "Tiger Electronics", "Robin Hood (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, tpitfight,    0,           0,      tpitfight,    tpitfight,    tpitfight_state,    empty_init, "Tiger Electronics", "Pit-Fighter (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, trockteer,    trobocop2,   0,      trockteer,    trockteer,    trockteer_state,    empty_init, "Tiger Electronics", "The Rocketeer (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, tmchammer,    0,           0,      tmchammer,    tmchammer,    tmchammer_state,    empty_init, "Tiger Electronics", "MC Hammer: U Can't Touch This (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, tbtoads,      0,           0,      tbtoads,      tbtoads,      tbtoads_state,      empty_init, "Tiger Electronics", "Battletoads (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1991, thook,        0,           0,      thook,        thook,        thook_state,        empty_init, "Tiger Electronics", "Hook (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1992, tbttf,        0,           0,      tbttf,        tbttf,        tbttf_state,        empty_init, "Tiger Electronics", "Back to the Future (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1992, taddams,      0,           0,      taddams,      taddams,      taddams_state,      empty_init, "Tiger Electronics", "The Addams Family (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1992, thalone,      0,           0,      thalone,      thalone,      thalone_state,      empty_init, "Tiger Electronics", "Home Alone (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1992, tgaiden3,     0,           0,      tgaiden3,     tgaiden3,     tgaiden3_state,     empty_init, "Tiger Electronics", "Ninja Gaiden III (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1993, txmenpx,      0,           0,      txmenpx,      txmenpx,      txmenpx_state,      empty_init, "Tiger Electronics", "X-Men: Project X (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1992, thalone2,     0,           0,      thalone2,     thalone2,     thalone2_state,     empty_init, "Tiger Electronics", "Home Alone 2: Lost in New York (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1992, tsonic,       0,           0,      tsonic,       tsonic,       tsonic_state,       empty_init, "Tiger Electronics", "Sonic The Hedgehog (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1992, trobocop3,    0,           0,      trobocop3,    trobocop3,    trobocop3_state,    empty_init, "Tiger Electronics", "Robocop 3 (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1993, tdummies,     0,           0,      tdummies,     tdummies,     tdummies_state,     empty_init, "Tiger Electronics", "The Incredible Crash Dummies (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1993, tsfight2,     0,           0,      tsfight2,     tsfight2,     tsfight2_state,     empty_init, "Tiger Electronics", "Street Fighter II (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1992, twworld,      0,           0,      twworld,      twworld,      twworld_state,      empty_init, "Tiger Electronics", "Wayne's World (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1993, tjpark,       0,           0,      tjpark,       tjpark,       tjpark_state,       empty_init, "Tiger Electronics", "Jurassic Park (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1993, tsonic2,      0,           0,      tsonic2,      tsonic2,      tsonic2_state,      empty_init, "Tiger Electronics", "Sonic The Hedgehog 2 (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1993, tsddragon,    0,           0,      tsddragon,    tsddragon,    tsddragon_state,    empty_init, "Tiger Electronics", "Super Double Dragon (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1993, tdennis,      0,           0,      tdennis,      tdennis,      tdennis_state,      empty_init, "Tiger Electronics", "Dennis the Menace (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1993, tnmarebc,     0,           0,      tnmarebc,     tnmarebc,     tnmarebc_state,     empty_init, "Tiger Electronics", "Nightmare Before Christmas (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK ) // note: title has no "The"
SYST( 1993, ttransf2,     0,           0,      ttransf2,     ttransf2,     ttransf2_state,     empty_init, "Tiger Electronics", "Transformers: Generation 2 (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1994, topaliens,    0,           0,      topaliens,    topaliens,    topaliens_state,    empty_init, "Tiger Electronics", "Operation: Aliens (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1993, tmkombat,     0,           0,      tmkombat,     tmkombat,     tmkombat_state,     empty_init, "Tiger Electronics", "Mortal Kombat (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1994, tshadow,      0,           0,      tshadow,      tshadow,      tshadow_state,      empty_init, "Tiger Electronics", "The Shadow (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1994, tskelwarr,    0,           0,      tskelwarr,    tskelwarr,    tskelwarr_state,    empty_init, "Tiger Electronics", "Skeleton Warriors: The Dark Crusade (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1995, tbatfor,      0,           0,      tbatfor,      tbatfor,      tbatfor_state,      empty_init, "Tiger Electronics", "Batman Forever: Double Dose of Doom (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1995, tjdredd,      0,           0,      tjdredd,      tjdredd,      tjdredd_state,      empty_init, "Tiger Electronics", "Judge Dredd (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1995, tapollo13,    0,           0,      tapollo13,    tapollo13,    tapollo13_state,    empty_init, "Tiger Electronics", "Apollo 13 (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1996, tgoldeye,     0,           0,      tgoldeye,     tgoldeye,     tgoldeye_state,     empty_init, "Tiger Electronics", "007: GoldenEye (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1996, tkazaam,      0,           0,      tkazaam,      tkazaam,      tkazaam_state,      empty_init, "Tiger Electronics", "Kazaam (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1996, tsjam,        0,           0,      tsjam,        tsjam,        tsjam_state,        empty_init, "Tiger Electronics", "Space Jam (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1996, tinday,       0,           0,      tinday,       tinday,       tinday_state,       empty_init, "Tiger Electronics", "Independence Day (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )

// Tiger 72-xxx models
SYST( 1992, tbatmana,     0,           0,      tbatmana,     tbatmana,     tbatmana_state,     empty_init, "Tiger Electronics", "Batman: The Animated Series (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1994, tmigmax,      0,           0,      tmigmax,      tmigmax,      tmigmax_state,      empty_init, "Tiger Electronics", "Mighty Max (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1995, tgargnf,      0,           0,      tgargnf,      tgargnf,      tgargnf_state,      empty_init, "Tiger Electronics", "Gargoyles: Night Flight (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1996, tsuperman,    tgargnf,     0,      tsuperman,    tsuperman,    tsuperman_state,    empty_init, "Tiger Electronics", "Superman (Tiger)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )

// Tronica Game Clock series
SYST( 1983, trshutvoy,    0,           0,      trshutvoy,    trshutvoy,    trshutvoy_state,    empty_init, "Tronica", "Shuttle Voyage", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1983, tigarden,     trshutvoy,   0,      tigarden,     trshutvoy,    trshutvoy_state,    empty_init, "Tronica", "Thief in Garden", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1982, trsrescue,    0,           0,      trsrescue,    trsrescue,    trsrescue_state,    empty_init, "Tronica", "Space Rescue", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1983, trthuball,    trsrescue,   0,      trthuball,    trsrescue,    trsrescue_state,    empty_init, "Tronica", "Thunder Ball (Tronica)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1983, trsgkeep,     0,           0,      trsgkeep,     trsgkeep,     trsgkeep_state,     empty_init, "Tronica", "Super Goal Keeper", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1982, trspacmis,    0,           0,      trspacmis,    trspacmis,    trspacmis_state,    empty_init, "Tronica", "Space Mission (Tronica)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1982, trspider,     trspacmis,   0,      trspider,     trspacmis,    trspacmis_state,    empty_init, "Tronica", "Spider (Tronica)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1982, trspacadv,    0,           0,      trspacadv,    trspacadv,    trspacadv_state,    empty_init, "Tronica", "Space Adventure", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1983, trtreisl,     0,           0,      trtreisl,     trtreisl,     trtreisl_state,     empty_init, "Tronica", "Treasure Island (Tronica)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1983, trdivadv,     0,           0,      trdivadv,     trdivadv,     trdivadv_state,     empty_init, "Tronica", "Diver's Adventure", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1983, trclchick,    trdivadv,    0,      trclchick,    trclchick,    trdivadv_state,     empty_init, "Tronica", "Clever Chicken", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )

// misc
SYST( 1989, nummunch,     0,           0,      nummunch,     nummunch,     nummunch_state,     empty_init, "VTech", "Electronic Number Muncher", MACHINE_SUPPORTS_SAVE )



hh_smc1k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Suwa Seikosha (S-MOS) SMC11xx handhelds
Some LCD games with this MCU, and perhaps early-80s Seiko wristwatches too.

In Europe, Tiger didn't release their pre-78-xxx LCD games (hh_sm510.cpp)
themselves, but through different publishers, eg. Orlitronic in France,
Grandstand in the UK, Virca in Italy.

TODO:
- add common mcfg (like hh_sm510) when more games are added?
- add svg for: tkkongq, tlluke2

*******************************************************************************/

#include "emu.h"

#include "cpu/tms1000/smc1102.h"
#include "sound/spkrdev.h"

#include "screen.h"
#include "speaker.h"

#include "hh_smc1k_test.lh" // common test-layout - no svg artwork(yet), use external artwork


namespace {

class hh_smc1k_state : public driver_device
{
public:
	hh_smc1k_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_speaker(*this, "speaker"),
		m_inputs(*this, "IN.%u", 0),
		m_out_x(*this, "%u.%u", 0U, 0U)
	{ }

	virtual DECLARE_INPUT_CHANGED_MEMBER(acl_button);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// devices
	required_device<smc1102_cpu_device> m_maincpu;
	optional_device<speaker_sound_device> m_speaker;
	optional_ioport_array<4> m_inputs; // max 4
	output_finder<4, 32> m_out_x;

	u8 m_inp_mux = 0;

	virtual void write_segs(offs_t offset, u32 data);
	u8 read_inputs(int columns);
};


// machine start/reset

void hh_smc1k_state::machine_start()
{
	// resolve outputs
	m_out_x.resolve();

	// register for savestates
	save_item(NAME(m_inp_mux));
}

void hh_smc1k_state::machine_reset()
{
}



/*******************************************************************************

  Helper Functions

*******************************************************************************/

// LCD screen

void hh_smc1k_state::write_segs(offs_t offset, u32 data)
{
	for (int i = 0; i < 32; i++)
	{
		// 4*32 segments
		m_out_x[offset & 3][i] = data & 1;
		data >>= 1;
	}
}


// generic input handlers

u8 hh_smc1k_state::read_inputs(int columns)
{
	u8 ret = 0;

	// read selected input rows
	for (int i = 0; i < columns; i++)
		if (BIT(m_inp_mux, i))
			ret |= m_inputs[i]->read();

	return ret;
}

INPUT_CHANGED_MEMBER(hh_smc1k_state::acl_button)
{
	// ACL button is directly tied to MCU INIT pin
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}



/*******************************************************************************

  Minidrivers (subclass, I/O, Inputs, Machine Config, ROM Defs)

*******************************************************************************/

/*******************************************************************************

  Tiger King Kong (model 7-721)
  * PCB label: TG-LC003
  * SMC1112 under epoxy (die label: SMC1112 D0W0)
  * lcd screen with custom segments, 1-bit sound

  Pyramid is suspected to have the same ROM as this.

  This is the "Quartz Game Clock" version. Tiger also released King Kong on
  several older LCD series.

*******************************************************************************/

class tkkongq_state : public hh_smc1k_state
{
public:
	tkkongq_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_smc1k_state(mconfig, type, tag)
	{ }

	void tkkongq(machine_config &config);

private:
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void tkkongq_state::write_r(u32 data)
{
	// R0,R1: input mux
	m_inp_mux = data & 3;

	// R7: speaker out
	m_speaker->level_w(BIT(data, 7));
}

u8 tkkongq_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(2);
}

// inputs

static INPUT_PORTS_START( tkkongq )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_16WAY

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_NAME("Set")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_NAME("Mode")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START2 ) PORT_NAME("Game B")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Game A")

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tkkongq_state::acl_button), 0) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tkkongq_state::tkkongq(machine_config &config)
{
	// basic machine hardware
	SMC1112(config, m_maincpu, 32.768_kHz_XTAL);
	m_maincpu->write_r().set(FUNC(tkkongq_state::write_r));
	m_maincpu->read_k().set(FUNC(tkkongq_state::read_k));
	m_maincpu->write_segs().set(FUNC(tkkongq_state::write_segs));

	// video hardware
	config.set_default_layout(layout_hh_smc1k_test);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( tkkongq )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "smc1112_d0w0", 0x0000, 0x0800, CRC(77ff7a01) SHA1(ed5dc860e400dc11518b0a32f13879e143c17953) )
ROM_END





/*******************************************************************************

  Tiger Lucky Luke (model 7-???)
  * PCB label: REV 0, ET820, MM
  * SMC1112 under epoxy (die label: SMC1112 D2A0)
  * lcd screen with custom segments, 1-bit sound

  Mickey Mouse is suspected to have the same ROM as this.

  This is the "Double Wide Screen" version, there's also a "Large Screen" one.

*******************************************************************************/

class tlluke2_state : public hh_smc1k_state
{
public:
	tlluke2_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_smc1k_state(mconfig, type, tag)
	{ }

	void tlluke2(machine_config &config);

private:
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void tlluke2_state::write_r(u32 data)
{
	// R0-R2: input mux
	m_inp_mux = data & 7;

	// R7: speaker out
	m_speaker->level_w(BIT(data, 7));
}

u8 tlluke2_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(3);
}

// inputs

static INPUT_PORTS_START( tlluke2 )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_NAME("Mode")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Game A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START2 ) PORT_NAME("Game B")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) // Jump/Open

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )

	PORT_START("ACL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tlluke2_state::acl_button), 0) PORT_NAME("ACL")
INPUT_PORTS_END

// config

void tlluke2_state::tlluke2(machine_config &config)
{
	// basic machine hardware
	SMC1112(config, m_maincpu, 32.768_kHz_XTAL);
	m_maincpu->write_r().set(FUNC(tlluke2_state::write_r));
	m_maincpu->read_k().set(FUNC(tlluke2_state::read_k));
	m_maincpu->write_segs().set(FUNC(tlluke2_state::write_segs));

	// video hardware
	config.set_default_layout(layout_hh_smc1k_test);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( tlluke2 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "smc1112_d2a0", 0x0000, 0x0800, CRC(73df08ff) SHA1(8ddf058e74e07364ab702e01f1cecb5fdb22133f) )
ROM_END



} // anonymous namespace

/*******************************************************************************

  Game driver(s)

*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY, FULLNAME, FLAGS

// Tiger Quartz Game Clock
SYST( 1982, tkkongq, 0,      0,      tkkongq, tkkongq, tkkongq_state, empty_init, "Tiger Electronics", "King Kong (Tiger, Quartz Game Clock)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK | MACHINE_NOT_WORKING )

// Tiger Double Wide Screen
SYST( 1984, tlluke2, 0,      0,      tlluke2, tlluke2, tlluke2_state, empty_init, "Tiger Electronics", "Lucky Luke (Tiger, Double Wide Screen)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK | MACHINE_NOT_WORKING )



hh_tms1k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle, Kevin Horton
/*******************************************************************************

This driver is a collection of simple dedicated handheld and tabletop
toys based around the TMS1000 MCU series. Anything more complex or clearly
part of a series is (or will be) in its own driver, see:
- atari/hitparade.cpp: Atari Europe Hit Parade jukebox(es)
- entex/sag.cpp: Entex Select-A-Game Machine (actually most games are on HMCS40)
- miltonbradley/microvsn.cpp: Milton Bradley Microvision
- misc/eva.cpp: Chrysler EVA-11 (and EVA-24)
- ti/snspell.cpp: TI Speak & Spell series gen. 1
- ti/snspellc.cpp: TI Speak & Spell Compact / Touch & Tell
- ti/spellb.cpp: TI Spelling B series gen. 1
- tiger/bingobear.cpp: Hasbro/Tiger Bingo Bear / Monkgomery Monkey
- tiger/k28.cpp: Tiger K-2-8: Talking Learning Computer (model 7-232)

About the approximated MCU frequency everywhere: The RC osc. is not that
stable on most of these handhelds. When comparing multiple video recordings
of the same game, it shows(and sounds) that the frequency range can differ
up to 50kHz. This is probably exaggerated due to components getting worn out
after many decades. TMS1000 RC curve is documented in the data manual, but
not for newer ones (rev. E or TMS1400 MCUs). TMS0970/0980 osc. is on-die.

ROM source notes when dumped from another title, but confident it's the same:
- arrball: Tandy Zingo
- bcheetah: Fundimensions Incredible Brain Buggy
- cchime: Videomaster Door Tunes
- cmsport: Conic Basketball
- cnbaskb: Cardinal Electronic Basketball
- cnfball: Elecsonic Football
- copycat: Sears Follow Me
- ditto: Tandy Electronic Pocket Repeat
- fxmcr165: Tandy Science Fair Microcomputer Trainer
- ginv1000: Tandy Cosmic 1000 Fire Away
- gjackpot: Entex Electronic Jackpot: Gin Rummy & Black Jack
- gpoker: Entex Electronic Poker
- matchnum: LJN Electronic Concentration
- palmf31: Toshiba BC-8018B
- racetime: Bandai FL Grand Prix Champion
- ti1250: Texas Instruments TI-1200
- ti25503: Texas Instruments TI-1265
- ti5100: loose 1979 TMS1073NL chip

TODO:
- Verify output PLA and microinstructions PLA for MCUs that have been dumped
  electronically (mpla is usually the default, opla is often custom).
- unknown MCU clocks for some, especially if no YouTube videos are found
- Fake-press ON button when emulation starts for machines that have it on the
  button matrix (doesn't look like any relies on it though).
- t7in1ss: in 2-player mode, game select and skill select can be configured after
  selecting a game? Possibly BTANB, players are expected to quickly press the
  "First Up" button after the alarm sound.
- finish bshipb SN76477 sound (incomplete output PLA)
- redo internal artwork for the baseball games (embedded SVG for diamond shapes)
- improve elecbowl driver
- tithermos temperature sensor comparator (right now just the digital clock works)
- is alphie(patent) the same as the final version?
- is starwbcp the same as MP3438? (starwbc is MP3438A)

================================================================================

Let's use this driver for a list of known devices and their serials,
excluding most of TI's own products(they normally didn't use "MP" codes).
For TI's calculators, a comprehensive list of MCU serials is available
on Joerg Woerner's datamath.org: http://www.datamath.org/IC_List.htm

  serial   device    etc.
--------------------------------------------------------------------
 @CP0904A  TMS0970   1977, Milton Bradley Comp IV
 @MP0905B  TMS0970   1977, Parker Brothers Codename Sector
 @MP0027A  TMS1000   1977, Texas Instruments OEM melody chip (used in eg. Chromatronics Chroma-Chime)
 *MP0057   TMS1000   1978, APH Student Speech+ (same ROM contents as TSI Speech+?)
 *MP0121   TMS1000   1979, Waddingtons Compute-A-Tune
 @MP0154   TMS1000   1979, Fonas 2 Player Baseball
 @MP0158   TMS1000   1979, Entex Soccer (6003)
 @MP0159   TMS1000   1979, Tomy Volleyball
 @MP0163   TMS1000   1979, A-One LSI Match Number/LJN Electronic Concentration
 @MP0166   TMS1000   1980, A-One Arrange Ball/LJN Computer Impulse/Tandy Zingo (model 60-2123)
 @MP0168   TMS1000   1979, Conic Multisport/Tandy Sports Arena (model 60-2158)
 *MP0169   TMS1000   1979, Conic Electronic Baseball
 @MP0170   TMS1000   1979, Conic Football
 *MP0171   TMS1000   1979, Tomy Soccer
 *MP0186   TMS1000   1979, Hornby Railways Zero 1: R944 Master Control Unit
 *MP0220   TMS1000   1980, Tomy Teacher
 @MP0230   TMS1000   1980, Entex Blast It (6015)
 @MP0271   TMS1000   1982, Tandy (Radio Shack) Monkey See
 @MP0907   TMS1000   1979, Conic Basketball (101-006)
 @MP0908   TMS1000   1979, Conic Electronic I.Q.
 *MP0910   TMS1000   1979, Conic Basketball (101-003)
 @MP0914   TMS1000   1979, Entex Baseball 1
 @MP0915   TMS1000   1979, Bandai System Control Car: Cheetah/The Incredible Brain Buggy
 @MP0919   TMS1000   1979, Tiger Copy Cat (model 7-520)
 @MP0920   TMS1000   1979, Entex Space Battle (6004)
 @MP0923   TMS1000   1979, Entex Baseball 2 (6002)
 *MP1022   TMS1100   1979, Texas Instruments unknown thermostat
 @MP1030   TMS1100   1980, APF Mathemagician
 *MP1072   TMS1100   198?, unknown device, Germany (have decap)
 @MP1133   TMS1470   1979, Kosmos Astro
 @MP1180   TMS1100   1980, Tomy Power House Pinball
 @MP1181   TMS1100   1979, Conic Football 2
 @MP1183   TMS1100   1980, E.R.S. Superbowl XV Football/Tandy Championship Football (model 60-2151)
 @MP1185   TMS1100   1979, Fonas 3 in 1: Football, Basketball, Soccer
 @MP1193   TMS1100   1980, Tandy Championship Football (model 60-2150)
 @MP1204   TMS1100   1980, Entex Baseball 3 (6007)
 @MP1209   TMS1100   1980, U.S. Games Space Cruiser
 @MP1211   TMS1100   1980, Entex Space Invader (6012)
 @MP1215   TMS1100   1980, Tiger Playmaker
 @MP1218   TMS1100   1980, Entex Basketball 2 (6010)
 @MP1219   TMS1100   1980, U.S. Games Super Sports-4
 @MP1221   TMS1100   1980, Entex Raise The Devil (6011)
 @MP1228   TMS1100   1980, Entex Musical Marvin (6014)
 @MP1231   TMS1100   1984, Tandy 3 in 1 Sports Arena (model 60-2178)
 *MP1272   TMS1100   1981, Tandy Computerized Arcade (assumed same as CD7282, not confirmed)
 @MP1288   TMS1100   1981, Tiger Finger Bowl
 @MP1296   TMS1100   1982, Entex Black Knight Pinball (6081)
 @MP1311   TMS1100   1981, Bandai TC7: Air Traffic Control
 @MP1312   TMS1100   1981, Gakken FX-Micom R-165
 @MP1312A  TMS1100   1981, Gakken FX-Micom R-165/Radio Shack Science Fair Microcomputer Trainer
 *MP1342   TMS1100   1985, Tiger Micro-Bot
 @MP1343   TMS1100   1984, Tandy (Micronta) VoxClock 3
 *MP1359   TMS1100   1985, Play-Jour Capsela CRC2000
 @MP1362   TMS1100   1985, Technasonic Weight Talker
 @MP1525   TMS1170   1980, Coleco Head to Head: Electronic Baseball
 @MP1604   TMS1370   1982, Gakken Invader 2000/Tandy Cosmic Fire Away 3000
 @MP1801   TMS1700   1981, Tiger Ditto/Tandy Pocket Repeat (model 60-2152)
  MP2012   TMS1300   1977, Atari Europe Hit Parade 144 (jukebox) -> atari/hitparade.cpp
 *MP2032   TMS1300   1980, Atari Europe unknown (jukebox, same PCB as the other one)
 @MP2105   TMS1370   1979, Gakken/Entex Poker (6005)
 @MP2110   TMS1370   1980, Gakken Invader/Tandy Fire Away
 @MP2139   TMS1370   1981, Gakken Galaxy Invader 1000/Tandy Cosmic 1000 Fire Away
 *MP2721   TMS1070   1978, Sanyo VTC 9300 Beta VCR timer unit
 @MP2726   TMS1040   1979, Tomy Break Up
 @MP2787   TMS1070   1980, Bandai Race Time
 *MP2788   TMS1070   1980, Bandai Flight Time
 @MP3005   TMS1730   1989, Tiger Copy Cat (model 7-522)
 @MP3200   TMS1000   1978, Parker Brothers Electronic Master Mind
 @MP3201   TMS1000   1977, Milton Bradley Electronic Battleship (1977, model 4750A)
 @MP3206   TMS1000   1978, Concept 2000 Mr. Mus-I-Cal (model 560)
 @MP3207   TMS1000   1978, Concept 2000 Lite 'n Learn: Electronic Organ (model 554)
 @MP3208   TMS1000   1978, Milton Bradley Electronic Battleship (1977, model 4750B)
 @MP3209   TMS1000   1978, Kenner Star Wars: Electronic Laser Battle Game
 @MP3226   TMS1000   1978, Milton Bradley Simon (Rev A)
 *MP3228   TMS1000   1979, Texas Instruments OEM melody chip
 *MP3232   TMS1000   1979, Fonas 2 Player Baseball (no "MP" on chip label)
 @MP3260   TMS1000   1979, Electroplay Quickfire
 *MP3261   TMS1000   1979, Hornby Railways Zero 1: R947 Locomotive Module
 @MP3300   TMS1000   1979, Milton Bradley Simon (Rev F)
 @MP3301A  TMS1000   1979, Milton Bradley Big Trak
 *MP3310   TMS1000   1979, Texas Instruments OEM melody chip
 @MP3312   TMS1000   1980, Nathan Mega 10000
 *MP3318   TMS1000   1979, Texas Instruments OEM melody chip
 @MP3320A  TMS1000   1979, Coleco Head to Head: Electronic Basketball
 @MP3321A  TMS1000   1979, Coleco Head to Head: Electronic Hockey
 @MP3352   TMS1200   1979, Tiger Sub Wars (model 7-490)
 @M32001   TMS1000   1981, Coleco Quiz Wiz Challenger (note: MP3398, MP3399, M3200x?)
 *M32018   TMS1000   1990, unknown device (have decap/dump)
  M32045B  TMS1000   1983, Chrysler Electronic Voice Alert (11-function) -> misc/eva.cpp
 @MP3403   TMS1100   1978, Marx Electronic Bowling
 @MP3404   TMS1100   1978, Parker Brothers Merlin
 @MP3405   TMS1100   1979, Coleco Amaze-A-Tron
 *MP3407   TMS1100   1979, General Electric The Great Awakening (model 7-4880)
 @MP3415   TMS1100   1978, Coleco Electronic Quarterback
 @MP3435   TMS1100   1979, Coleco Zodiac
 @MP3438A  TMS1100   1979, Kenner Star Wars: Electronic Battle Command Game
  MP3447   TMS1100   1982, Texas Instruments Les Maths Magiques -> ti/snspellc.cpp
  MP3450A  TMS1100   1979, Microvision cartridge: Block Buster
  MP3454   TMS1100   1979, Microvision cartridge: Star Trek Phaser Strike
  MP3455   TMS1100   1980, Microvision cartridge: Pinball
  MP3457   TMS1100   1979, Microvision cartridge: Mindbuster
 @MP3460   TMS1100   1979, Coleco Head to Head: Electronic Football
  MP3474   TMS1100   1979, Microvision cartridge: Vegas Slots
  MP3475   TMS1100   1979, Microvision cartridge: Bowling
 @MP3476   TMS1100   1979, Milton Bradley Super Simon
  MP3479   TMS1100   1980, Microvision cartridge: Baseball
  MP3481   TMS1100   1979, Microvision cartridge: Connect Four
 @MP3487   TMS1100   1980, Lakeside Strobe
 @MP3489   TMS1100   1980, Kenner Live Action Football
 @MP3491   TMS1100   1979, Mattel Thoroughbred Horse Race Analyzer
 *MP3493   TMS1100   1980, Milton Bradley OMNI Entertainment System (1/2)
 *MP3494   TMS1100   1980, Milton Bradley OMNI Entertainment System (2/2)
  MP3496   TMS1100   1980, Microvision cartridge: Sea Duel
 @M34004A  TMS1100   1981, Ideal Sky-Writer (note: MP3498, MP3499, M3400x..)
  M34009   TMS1100   1981, Microvision cartridge: Alien Raiders
 @M34012   TMS1100   1980, Mattel Dungeons & Dragons: Computer Labyrinth Game
 *M34014   TMS1100   1981, Coleco Bowlatronic
  M34017   TMS1100   1981, Microvision cartridge: Cosmic Hunter
 @M34018   TMS1100   1981, Coleco Head to Head: Electronic Boxing
 *M34033   TMS1100   1982, Spartus Electronic Talking Clock (1411-61)
 @M34038   TMS1100   1982, Parker Brothers Lost Treasure
  M34047   TMS1100   1982, Microvision cartridge: Super Blockbuster
 @M34078A  TMS1100   1983, Milton Bradley Electronic Arcade Mania
 *M34137   TMS1100   1985, Technasonic Weight Talker
 @MP4486A  TMS1000C  1983, Vulcan XL 25
 *MP6061   TMS0970   1979, Texas Instruments Electronic Digital Thermostat (from patent, the one in MAME didn't have a label)
 @MP6100A  TMS0980   1979, Ideal Electronic Detective
 @MP6101B  TMS0980   1979, Parker Brothers Stop Thief
 *MP6262A  TMS1170   1981, Bearcat BC-210XL
 @MP6354   TMS1475   1982, Tsukuda The Dracula
 @MP6358   TMS1475   1982, Bandai U-Boat
 *MP6361   TMS1475   1983, <unknown> Defender Strikes
 @MP7302   TMS1400   1980, Tiger Deluxe Football with Instant Replay
 *MP7303   TMS1400   1980, Entex Football 3 (6018)
 @MP7304   TMS1400   1982, Tiger 7 in 1 Sports Stadium (model 7-555)
 @MP7313   TMS1400   1980, Parker Brothers Bank Shot
 @MP7314   TMS1400   1980, Parker Brothers Split Second
  MP7324   TMS1400   1985, Tiger K-2-8/Coleco Talking Teacher -> tiger/k28.cpp
 @MP7332   TMS1400   1981, Milton Bradley Dark Tower
 @MP7334   TMS1400   1981, Coleco Total Control 4
 @MP7351   TMS1400   1982, Parker Brothers Master Merlin
 @MP7551   TMS1670   1980, Entex Color Football 4 (6009)
 *MP7574   TMS1600   1981, Busch Microtronic 2090 (actually, label is TMS1600NLL7574, no MP label)
 @MPF553   TMS1670   1980, Gakken/Entex Jackpot: Gin Rummy & Black Jack (6008) (note: assume F to be a misprint)
  MP7573   TMS1670   1981, Entex Select-A-Game cartridge: Football 4 -> entex/sag.cpp
 *M30026   TMS2370   1983, Yaesu FT-757 Display Unit part
 @M95041   TMS2670   1983, Tsukuda Slot Elepachi

  inconsistent:

 @TMS1007  TMS1000   1976, TSI Speech+ (S14002-A)
 @CD7282SL TMS1100   1981, Tandy Computerized Arcade (serial is similar to TI Speak & Spell series?)

  (* means undumped unless noted, @ denotes it's in this driver)

*******************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/tms1000/tms1000.h"
#include "cpu/tms1000/tms1000c.h"
#include "cpu/tms1000/tms1100.h"
#include "cpu/tms1000/tms1400.h"
#include "cpu/tms1000/tms2400.h"
#include "cpu/tms1000/tms0970.h"
#include "cpu/tms1000/tms0980.h"
#include "machine/clock.h"
#include "machine/netlist.h"
#include "machine/nvram.h"
#include "machine/timer.h"
#include "machine/tmc0999.h"
#include "machine/tms1024.h"
#include "machine/tms6100.h"
#include "sound/beep.h"
#include "sound/flt_vol.h"
#include "sound/s14001a.h"
#include "sound/sn76477.h"
#include "sound/spkrdev.h"
#include "sound/tms5110.h"
#include "video/ds8874.h"
#include "video/hlcd0515.h"
#include "video/pwm.h"

#include "softlist_dev.h"
#include "screen.h"
#include "speaker.h"

// netlist
#include "nl_bship.h"

// internal artwork
#include "t7in1ss.lh"
#include "alphie.lh"
#include "amaztron.lh"
#include "arcmania.lh"
#include "arrball.lh"
#include "astro.lh"
#include "bankshot.lh"
#include "bcheetah.lh"
#include "bigtrak.lh"
#include "blastit.lh"
#include "bship.lh"
#include "cmsport.lh"
#include "cnbaskb.lh"
#include "cnfball.lh"
#include "cnfball2.lh"
#include "cnsector.lh"
#include "comp4.lh"
#include "comparc.lh"
#include "copycat.lh"
#include "copycata.lh"
#include "cqback.lh"
#include "dataman.lh"
#include "ditto.lh"
#include "dxfootb.lh"
#include "ebball.lh"
#include "ebball2.lh"
#include "ebball3.lh"
#include "ebaskb2.lh"
#include "ebknight.lh"
#include "efootb4.lh"
#include "einvader.lh"
#include "elecbowl.lh"
#include "elecdet.lh"
#include "eleciq.lh"
#include "esbattle.lh"
#include "esoccer.lh"
#include "f2pbball.lh"
#include "f3in1.lh"
#include "fingbowl.lh"
#include "fxmcr165.lh"
#include "gjackpot.lh"
#include "gpoker.lh"
#include "h2hbaseb.lh"
#include "h2hbaskb.lh"
#include "h2hboxing.lh"
#include "h2hfootb.lh"
#include "h2hhockey.lh"
#include "lilprof.lh"
#include "litelrn.lh"
#include "liveafb.lh"
#include "lostreas.lh"
#include "matchnum.lh"
#include "mathmagi.lh"
#include "mathmarv.lh"
#include "mbdtower.lh"
#include "mdndclab.lh"
#include "mega10k.lh"
#include "merlin.lh"
#include "mmarvin.lh"
#include "mmerlin.lh"
#include "monkeysee.lh"
#include "mrmusical.lh"
#include "palmf31.lh"
#include "palmmd8.lh"
#include "pbmastm.lh"
#include "phpball.lh"
#include "playmaker.lh"
#include "qfire.lh"
#include "quizwizc.lh"
#include "raisedvl.lh"
#include "scruiser.lh"
#include "simon.lh"
#include "speechp.lh"
#include "splitsec.lh"
#include "ssimon.lh"
#include "ssports4.lh"
#include "starwbc.lh"
#include "starwlb.lh"
#include "stopthief.lh"
#include "strobe.lh"
#include "subwars.lh"
#include "t3in1sa.lh"
#include "tbreakup.lh"
#include "tc4.lh"
#include "tc7atc.lh"
#include "tcfball.lh"
#include "tcfballa.lh"
#include "tdracula.lh"
#include "ti1250.lh"
#include "ti1270.lh"
#include "ti1680.lh"
#include "ti25502.lh"
#include "ti30.lh"
#include "ti5100.lh"
#include "ti5200.lh"
#include "timaze.lh"
#include "tisr16.lh"
#include "tithermos.lh"
#include "tmvolleyb.lh"
#include "uboat.lh"
#include "vclock3.lh"
#include "xl25.lh"
#include "zodiac.lh"

//#include "hh_tms1k_test.lh" // common test-layout - use external artwork


namespace {

class hh_tms1k_state : public driver_device
{
public:
	hh_tms1k_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_speaker(*this, "speaker"),
		m_inputs(*this, "IN.%u", 0),
		m_out_power(*this, "power")
	{ }

	virtual DECLARE_INPUT_CHANGED_MEMBER(reset_button);
	virtual DECLARE_INPUT_CHANGED_MEMBER(power_button);

	template<int Sel> DECLARE_INPUT_CHANGED_MEMBER(switch_next) { if (newval) switch_change(Sel, param, true); }
	template<int Sel> DECLARE_INPUT_CHANGED_MEMBER(switch_prev) { if (newval) switch_change(Sel, param, false); }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// devices
	required_device<tms1k_base_device> m_maincpu;
	optional_device<pwm_display_device> m_display;
	optional_device<speaker_sound_device> m_speaker;
	optional_ioport_array<18> m_inputs; // max 18
	output_finder<> m_out_power; // power state, eg. led

	// misc common
	u32 m_r = 0U;            // MCU R-pins data
	u16 m_o = 0U;            // MCU O-pins data
	u32 m_inp_mux = 0U;      // multiplexed inputs mask
	bool m_power_on = false;

	u32 m_grid = 0U;         // VFD/LED current row data
	u32 m_plate = 0U;        // VFD/LED current column data

	u8 read_inputs(int columns);
	u8 read_rotated_inputs(int columns, u8 rowmask = 0xf);
	virtual void auto_power_off(int state);
	virtual void power_off();
	virtual void set_power(bool state);
	void switch_change(int sel, u32 mask, bool next);
};


// machine_start/reset

void hh_tms1k_state::machine_start()
{
	// resolve outputs
	m_out_power.resolve();

	// register for savestates
	save_item(NAME(m_o));
	save_item(NAME(m_r));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_power_on));
	save_item(NAME(m_grid));
	save_item(NAME(m_plate));
}

void hh_tms1k_state::machine_reset()
{
	set_power(true);
}



/*******************************************************************************

  Helper Functions

*******************************************************************************/

// generic input handlers

u8 hh_tms1k_state::read_inputs(int columns)
{
	u8 ret = 0;

	// read selected input rows
	for (int i = 0; i < columns; i++)
		if (BIT(m_inp_mux, i))
			ret |= m_inputs[i]->read();

	return ret;
}

u8 hh_tms1k_state::read_rotated_inputs(int columns, u8 rowmask)
{
	u8 ret = 0;
	u16 colmask = (1 << columns) - 1;

	// read selected input columns
	for (int i = 0; i < 8; i++)
		if (1 << i & rowmask && m_inputs[i]->read() & m_inp_mux & colmask)
			ret |= 1 << i;

	return ret;
}

void hh_tms1k_state::switch_change(int sel, u32 mask, bool next)
{
	// config switches (for direct control)
	ioport_field *inp = m_inputs[sel]->field(mask);

	if (next && inp->has_next_setting())
		inp->select_next_setting();
	else if (!next && inp->has_previous_setting())
		inp->select_previous_setting();
}

INPUT_CHANGED_MEMBER(hh_tms1k_state::reset_button)
{
	// when an input is directly wired to MCU INIT pin
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}

INPUT_CHANGED_MEMBER(hh_tms1k_state::power_button)
{
	if (newval != field.defvalue())
		set_power((bool)param);
}

void hh_tms1k_state::auto_power_off(int state)
{
	// devices with a TMS0980 can auto power-off
	if (state)
		power_off();
}

void hh_tms1k_state::power_off()
{
	set_power(false);
}

void hh_tms1k_state::set_power(bool state)
{
	m_power_on = state;
	m_maincpu->set_input_line(INPUT_LINE_RESET, m_power_on ? CLEAR_LINE : ASSERT_LINE);

	m_out_power = state ? 1 : 0;

	if (m_display && !m_power_on)
		m_display->clear();
}



/*******************************************************************************

  Minidrivers (subclass, I/O, Inputs, Machine Config, ROM Defs)

*******************************************************************************/

/*******************************************************************************

  A-One LSI Match Number
  * PCB label: PT-204 "Pair Card"
  * TMS1000NLL MP0163 (die label: 1000B, MP0163)
  * 2x2-digit 7seg LED displays + 3 LEDs, 1-bit sound

  A-One was a subsidiary of Bandai? The PCB serial PT-xxx is same, and the font
  used on the boxes for "A-One LSI" is same as "Bandai Electronics" from early-80s.

  known releases:
  - Japan: Match Number, published by A-One (white case, Queen playing card bezel)
  - USA: Electronic Concentration, published by LJN (black case, rainbow pattern bezel)
  - UK: Electronic Concentration, published by Peter Pan Playthings (same as USA version)

*******************************************************************************/

class matchnum_state : public hh_tms1k_state
{
public:
	matchnum_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void matchnum(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void matchnum_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void matchnum_state::write_r(u32 data)
{
	// R3-R5,R8-R10: input mux
	m_inp_mux = (data >> 3 & 7) | (data >> 5 & 0x38);

	// R6,R7: speaker out
	m_speaker->level_w(data >> 6 & 3);

	// R0-R3: digit/led select
	m_r = data;
	update_display();
}

void matchnum_state::write_o(u16 data)
{
	// O0-O6: digit segments A-G
	// O7: led data
	m_o = data;
	update_display();
}

u8 matchnum_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(6);
}

// inputs

static INPUT_PORTS_START( matchnum )
	PORT_START("IN.0") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 16")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 15")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 14")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 13")

	PORT_START("IN.1") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 20")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 19")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 18")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 17")

	PORT_START("IN.2") // R5
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_NAME("Change")
	PORT_CONFNAME( 0x08, 0x08, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x08, "1" )
	PORT_CONFSETTING(    0x00, "2" )

	PORT_START("IN.3") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 12")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 11")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 10")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 9")

	PORT_START("IN.4") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 8")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 7")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 5")

	PORT_START("IN.5") // R10
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 1")
INPUT_PORTS_END

// config

void matchnum_state::matchnum(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 325000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_k().set(FUNC(matchnum_state::read_k));
	m_maincpu->write_r().set(FUNC(matchnum_state::write_r));
	m_maincpu->write_o().set(FUNC(matchnum_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 8);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_matchnum);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[4] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( matchnum )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp0163", 0x0000, 0x0400, CRC(37507600) SHA1(b1d4d8ea563e97ef378b42c44cb3ea4eb6abe0d2) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_matchnum_output.pla", 0, 365, CRC(da29670c) SHA1(bcec28bf25dc8c81d08851ad8a3f4e89f413017a) )
ROM_END





/*******************************************************************************

  A-One LSI Arrange Ball
  * PCB label: Kaken, PT-249
  * TMS1000NLL MP0166 (die label: 1000B, MP0166)
  * 2-digit 7seg LED display + 22 LEDs, 1-bit sound

  known releases:
  - Japan/World: Arrange Ball, published by A-One (black case)
  - USA(1): Zingo (model 60-2123), published by Tandy (red case)
  - USA(2): Computer Impulse, published by LJN (white case)
  - Germany: Fixball, unknown publisher, same as LJN version

*******************************************************************************/

class arrball_state : public hh_tms1k_state
{
public:
	arrball_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void arrball(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void arrball_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void arrball_state::write_r(u32 data)
{
	// R8: input mux (always set)
	m_inp_mux = data >> 8 & 1;

	// R9,R10: speaker out
	m_speaker->level_w(data >> 9 & 3);

	// R0-R6: digit/led select
	m_r = data;
	update_display();
}

void arrball_state::write_o(u16 data)
{
	// O0-O6: digit segments/led data
	m_o = data;
	update_display();
}

u8 arrball_state::read_k()
{
	// K: multiplexed inputs (actually just 1)
	return read_inputs(1);
}

// inputs

static INPUT_PORTS_START( arrball )
	PORT_START("IN.0") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Shot") // pressed when START lights up
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Stop")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x08, 0x00, "Speed" )
	PORT_CONFSETTING(    0x00, "Slow" )
	PORT_CONFSETTING(    0x08, "Fast" )
INPUT_PORTS_END

// config

void arrball_state::arrball(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 325000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_k().set(FUNC(arrball_state::read_k));
	m_maincpu->write_r().set(FUNC(arrball_state::write_r));
	m_maincpu->write_o().set(FUNC(arrball_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(7, 7);
	m_display->set_segmask(0x10, 0x7f);
	m_display->set_segmask(0x20, 0x06); // left digit only segments B and C
	config.set_default_layout(layout_arrball);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[4] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( arrball )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp0166", 0x0000, 0x0400, CRC(a78694db) SHA1(362aa6e356288e8df7da610246bd01fe72985d57) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_arrball_output.pla", 0, 365, CRC(ffc206fb) SHA1(339be3f066fb2f075211c554e81260b49cd83d15) )
ROM_END





/*******************************************************************************

  APF Mathemagician
  * TMS1100 MCU, label MP1030 (no decap)
  * 2 x DS8870N - Hex LED Digit Driver
  * 2 x DS8861N - MOS-to-LED 5-Segment Driver
  * 10-digit 7seg LED display(2 custom ones) + 4 LEDs, no sound

  This is a tabletop educational calculator. It came with plastic overlays
  for playing different kind of games. Refer to the manual on how to use it.
  In short, to start from scratch, press [SEL]. By default the device is in
  calculator teaching mode. If [SEL] is followed with 1-6 and then [NXT],
  one of the games is started.

  1) Number Machine
  2) Countin' On
  3) Walk The Plank
  4) Gooey Gumdrop
  5) Football
  6) Lunar Lander

*******************************************************************************/

class mathmagi_state : public hh_tms1k_state
{
public:
	mathmagi_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void mathmagi(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void mathmagi_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void mathmagi_state::write_r(u32 data)
{
	// R3,R5-R7,R9,R10: input mux
	m_inp_mux = (data >> 3 & 1) | (data >> 4 & 0xe) | (data >> 5 & 0x30);

	// R0-R7: 7seg leds
	// R8: custom math symbols digit
	// R9: custom equals digit
	// R10: misc lamps
	m_r = data;
	update_display();
}

void mathmagi_state::write_o(u16 data)
{
	// O1-O7: led/digit segment data
	// O0: N/C
	m_o = data;
	update_display();
}

u8 mathmagi_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(6);
}

// inputs

/* physical button layout and labels are like this:

    ON     ONE       [SEL] [NXT] [?]   [/]
     |      |        [7]   [8]   [9]   [x]
    OFF    TWO       [4]   [5]   [6]   [-]
         PLAYERS     [1]   [2]   [3]   [+]
                     [0]   [_]   [r]   [=]
*/

static INPUT_PORTS_START( mathmagi )
	PORT_START("IN.0") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")

	PORT_START("IN.1") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SPACE) PORT_NAME("_") // blank
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("r")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")

	PORT_START("IN.2") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")

	PORT_START("IN.3") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("SEL")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_N) PORT_NAME("NXT")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("?") // check
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")

	PORT_START("IN.4") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")

	PORT_START("IN.5") // R10
	PORT_CONFNAME( 0x01, 0x00, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x01, "2" )
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

// output PLA is not decapped, this was made by hand
static const u16 mathmagi_output_pla[0x20] =
{
	0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, // 0, 1, 2, 3, 4, 5, 6, 7
	0x7f, 0x6f, 0x53, 0x50, 0x08, 0x79, 0x40, 0x00, // 8, 9, questionmark, r, underscore?, E, -(negative), empty
	0x00, 0x40, 0x08, 0x7c, 0x02, 0x42, 0x06, 0x6e, // empty, led4/-, led3, b, led2, +, ×, y
	0x01, 0x41, 0x09, 0,    0,    0x5c, 0,    0x3d  // led1, ÷, =, ?, ?, o, ?, G
};

void mathmagi_state::mathmagi(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 175000); // approximation - RC osc. R=68K, C=82pF
	m_maincpu->set_output_pla(mathmagi_output_pla);
	m_maincpu->read_k().set(FUNC(mathmagi_state::read_k));
	m_maincpu->write_r().set(FUNC(mathmagi_state::write_r));
	m_maincpu->write_o().set(FUNC(mathmagi_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(11, 7);
	m_display->set_segmask(0xff, 0x7f);
	config.set_default_layout(layout_mathmagi);

	// no sound!
}

// roms

ROM_START( mathmagi )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1030", 0x0000, 0x0800, CRC(a81d7ccb) SHA1(4756ce42f1ea28ce5fe6498312f8306f10370969) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, BAD_DUMP CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) ) // not verified
	ROM_REGION( 365, "maincpu:opla", ROMREGION_ERASE00 )
	ROM_LOAD( "tms1100_mathmagi_output.pla", 0, 365, NO_DUMP )
ROM_END





/*******************************************************************************

  Bandai System Control Car: Cheetah 「システムコントロールカー チーター」
  * TMS1000NLL MP0915 (die label: 1000B, MP0915)
  * 2 motors (one for back axis, one for steering), no sound

  It's a programmable buggy modeled after the Lamborghini Cheetah, it's like
  Big Trak but much simpler. To add a command step in program-mode, press a
  direction key and one of the time delay number keys at the same time. To run
  the program(max 24 steps), switch to run-mode and press the go key.

  known releases:
  - Japan: System Control Car: Cheetah, published by Bandai
  - USA: The Incredible Brain Buggy, published by Fundimensions
  - UK: The Incredible Brain Buggy, published by Palitoy (same as USA version)

  Bandai also made an RC car version of the Cheetah with the same mould.

*******************************************************************************/

class bcheetah_state : public hh_tms1k_state
{
public:
	bcheetah_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag),
		m_motor1(*this, "motor1"),
		m_motor2_left(*this, "motor2_left"),
		m_motor2_right(*this, "motor2_right")
	{ }

	void bcheetah(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	output_finder<> m_motor1;
	output_finder<> m_motor2_left;
	output_finder<> m_motor2_right;

	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

void bcheetah_state::machine_start()
{
	hh_tms1k_state::machine_start();

	// resolve outputs
	m_motor1.resolve();
	m_motor2_left.resolve();
	m_motor2_right.resolve();
}

// handlers

void bcheetah_state::write_r(u32 data)
{
	// R0-R4: input mux
	m_inp_mux = data & 0x1f;
	m_r = data;
}

void bcheetah_state::write_o(u16 data)
{
	// O1: back motor (drive)
	// O0: front motor steer left
	// O2: front motor steer right
	// O3: GND, other: N/C
	m_motor1 = BIT(data, 1);
	m_motor2_left = BIT(data, 0);
	m_motor2_right = BIT(data, 2);
}

u8 bcheetah_state::read_k()
{
	// K: multiplexed inputs, K4 is also tied to R5+R6
	return read_inputs(5) | ((m_r & 0x60) ? 4 : 0);
}

// inputs

static INPUT_PORTS_START( bcheetah )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x02, 0x02, "Mode")
	PORT_CONFSETTING(    0x02, "Program" )
	PORT_CONFSETTING(    0x00, "Run" )

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Left")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_UP) PORT_NAME("Forward")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Right")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Go")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Stop")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x05, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")

	PORT_START("IN.4") // R4
	PORT_BIT( 0x05, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
INPUT_PORTS_END

// config

void bcheetah_state::bcheetah(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 350000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_k().set(FUNC(bcheetah_state::read_k));
	m_maincpu->write_r().set(FUNC(bcheetah_state::write_r));
	m_maincpu->write_o().set(FUNC(bcheetah_state::write_o));

	config.set_default_layout(layout_bcheetah);

	// no sound!
}

// roms

ROM_START( bcheetah )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp0915", 0x0000, 0x0400, CRC(2968c81e) SHA1(d1e6691952600e88ccf626cb3d683419a1e8468c) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_bcheetah_output.pla", 0, 365, CRC(cc6d1ecd) SHA1(b0635a841d8850c36c1f414abe0571b81884b972) )
ROM_END





/*******************************************************************************

  Bandai Race Time
  * PCB label: SM-007
  * TMS1070 MP2787 (die label: 1070B, MP2787)
  * cyan/red VFD Futaba DM-8Z, 1-bit sound

  known releases:
  - World: Race Time (model 8007), published by Bandai
  - Japan: FL Grand Prix Champion (model 16162), published by Bandai

*******************************************************************************/

class racetime_state : public hh_tms1k_state
{
public:
	racetime_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void racetime(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void racetime_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void racetime_state::write_r(u32 data)
{
	// R7: input mux
	m_inp_mux = BIT(data, 7);

	// R10: speaker out
	m_speaker->level_w(BIT(data, 10));

	// R0-R8: VFD grid
	// R9: VFD plate
	m_grid = data & 0x1ff;
	m_plate = (m_plate & 0xff) | (data >> 1 & 0x100);
	update_display();
}

void racetime_state::write_o(u16 data)
{
	// O0-O7: VFD plate
	m_plate = (m_plate & ~0xff) | data;
	update_display();
}

u8 racetime_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(1);
}

// inputs

static INPUT_PORTS_START( racetime )
	PORT_START("IN.0") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
INPUT_PORTS_END

// config

void racetime_state::racetime(machine_config &config)
{
	// basic machine hardware
	TMS1070(config, m_maincpu, 350000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_k().set(FUNC(racetime_state::read_k));
	m_maincpu->write_r().set(FUNC(racetime_state::write_r));
	m_maincpu->write_o().set(FUNC(racetime_state::write_o));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(229, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(9, 9);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( racetime )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp2787", 0x0000, 0x0400, CRC(38c668b9) SHA1(2181647d4f2385a81355eaf99a40ad82f57ecdc6) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 406, "maincpu:opla", 0 )
	ROM_LOAD( "tms1070_racetime_output.pla", 0, 406, CRC(21e966b0) SHA1(ce918302d2b462df81b2d0cf2145e2351c98a0c0) )

	ROM_REGION( 221716, "screen", 0)
	ROM_LOAD( "racetime.svg", 0, 221716, CRC(d3934aed) SHA1(40b1fde191506c884b16b2ee3daaee2c6a4f4f08) )
ROM_END





/*******************************************************************************

  Bandai TC7: Air Traffic Control
  * TMS1100 MCU, label MP1311 (die label: 1100E, MP1311)
  * 4-digit 7seg LED display, 40 other LEDs, 1-bit sound

  It is a very complicated game, refer to the manual on how to play.

*******************************************************************************/

class tc7atc_state : public hh_tms1k_state
{
public:
	tc7atc_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void tc7atc(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void tc7atc_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void tc7atc_state::write_r(u32 data)
{
	// R5: speaker out
	m_speaker->level_w(BIT(data, 5));

	// R0-R4: input mux, led select
	// R6-R9: digit select
	m_inp_mux = m_r = data;
	update_display();
}

void tc7atc_state::write_o(u16 data)
{
	// O0-O7: led data
	m_o = data;
	update_display();
}

u8 tc7atc_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(5);
}

// inputs

static INPUT_PORTS_START( tc7atc )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("High Score")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_H) PORT_NAME("Hazard")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Clear")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("West")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("East")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("Arrive")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Depart")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Flight Path A / Level 1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_NAME("Flight Path B / Level 2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("Flight Path C / Level 3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("Flight Path D / Level 4")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_NAME("Altitude Descend")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_NAME("Altitude Ascend")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("Air Speed Decrease")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("Air Speed Increase")

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("10%")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("20%")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("40%")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_NAME("80%")
INPUT_PORTS_END

// config

void tc7atc_state::tc7atc(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 350000); // approximation - RC osc. R=68K, C=47pF
	m_maincpu->read_k().set(FUNC(tc7atc_state::read_k));
	m_maincpu->write_r().set(FUNC(tc7atc_state::write_r));
	m_maincpu->write_o().set(FUNC(tc7atc_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 8);
	m_display->set_segmask(0x3c0, 0x7f);
	config.set_default_layout(layout_tc7atc);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( tc7atc )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1311", 0x0000, 0x0800, CRC(704f5e1b) SHA1(765dce31798640480eab7576550c5378d6351b65) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_tc7atc_output.pla", 0, 365, CRC(0e6e3096) SHA1(375beb43657af0cc3070e581b42e501878c0eaaa) )
ROM_END





/*******************************************************************************

  Bandai U-Boat (model 16300)
  * TMS1475 MP6358 (die label: TMS1175 /TMS1475, MP6358, 40H-PASS-0900-R300-0)
  * cyan/red VFD NEC FIP10DM24AT no. 2-8 21 (2-sided), 1-bit sound
  * color overlay: red/yellow/blue, and black

  The VFD is 2-sided, the destroyer side views it from the back and basically
  sees a mirror image. Each side has its own color overlay, and a black panel
  that obscures part of the screen.

*******************************************************************************/

class uboat_state : public hh_tms1k_state
{
public:
	uboat_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void uboat(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void uboat_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void uboat_state::write_r(u32 data)
{
	// R14: speaker out
	m_speaker->level_w(BIT(data, 14));

	// R15,R19-R21: input mux
	m_inp_mux = (data >> 15 & 1) | (data >> 18 & 0xe);

	// R0-R9: VFD grid
	// R10-R13, R16-R18: VFD plate
	m_grid = data & 0x3ff;
	m_plate = (m_plate & 0xff) | (data >> 2 & 0xf00) | (data >> 4 & 0x7000);
	update_display();
}

void uboat_state::write_o(u16 data)
{
	// O0-O7: VFD plate
	m_plate = (m_plate & ~0xff) | data;
	update_display();
}

u8 uboat_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(4);
}

// inputs

/* physical button layout and labels are like this:

    player 1 side:                                     player 2 side (opposite):

    OFF ON  OFF ON     ---- SELECT ----           s
    [---o]  [---o]  [    ]  [    ]  [    ]        c
    POWER   SOUND   COMvsD  MANUAL  COMvsS        r
                                                  e                SUBMARINE SIDE
     [  ]                                         e      \     |     /
     FIRE  DISTROYER SIDE (sic)     [joystick]    n    [  ]  [  ]  [  ]    [joystick]
     [  ]                             ACTION             --- FIRE ---        ACTION
*/

static INPUT_PORTS_START( uboat )
	PORT_START("IN.0") // R15
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Fire Shallow")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Fire Left")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL

	PORT_START("IN.1") // R19
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START2 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START3 ) PORT_NAME("1 Player Start (Submarine)")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL

	PORT_START("IN.2") // R20
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("1 Player Start (Destroyer)")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_COCKTAIL PORT_NAME("P2 Fire Right")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL

	PORT_START("IN.3") // R21
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Fire Deep")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_COCKTAIL PORT_NAME("P2 Fire Middle")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL
INPUT_PORTS_END

// config

void uboat_state::uboat(machine_config &config)
{
	// basic machine hardware
	TMS1475(config, m_maincpu, 425000); // approximation - RC osc. R=43K, C=47pF
	m_maincpu->read_k().set(FUNC(uboat_state::read_k));
	m_maincpu->write_r().set(FUNC(uboat_state::write_r));
	m_maincpu->write_o().set(FUNC(uboat_state::write_o));

	// video hardware
	screen_device &screen1(SCREEN(config, "screen1", SCREEN_TYPE_SVG));
	screen1.set_refresh_hz(60);
	screen1.set_size(372, 1080);
	screen1.set_visarea_full();

	screen_device &screen2(SCREEN(config, "screen2", SCREEN_TYPE_SVG));
	screen2.set_refresh_hz(60);
	screen2.set_size(372, 1080);
	screen2.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(10, 15);
	config.set_default_layout(layout_uboat);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( uboat )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mp6358", 0x0000, 0x1000, CRC(2e238df8) SHA1(e60f061b1f449946e1a68fb8789a381961b088ad) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 557, "maincpu:opla", 0 )
	ROM_LOAD( "tms1400_uboat_output.pla", 0, 557, CRC(fdb07ac9) SHA1(beb2d8f8de39b795cb95aef2b0f02abc5e4d79dc) )

	ROM_REGION( 305576, "screen1", 0) // destroyer side
	ROM_LOAD( "uboat1.svg", 0, 305576, CRC(5d51fa01) SHA1(a42ccbc821050f5719232130f115eeb4888ed452) )

	ROM_REGION( 310743, "screen2", 0) // submarine side
	ROM_LOAD( "uboat2.svg", 0, 310743, CRC(72c44272) SHA1(a43a57b8038b00f9d63c6e5d1a43c4a45a6c6075) )
ROM_END





/*******************************************************************************

  Canon Palmtronic F-31, Canon Canola L813, Toshiba BC-8111B, Toshiba BC-8018B,
  Triumph-Adler 81 SN, Silver-Reed 8J, more
  * TMS1040 MCU label TMS1045NL (die label: 1040A, 1045)
  * 9-digit cyan VFD (leftmost may be custom)

  TMS1045NL is a versatile calculator chip for 3rd party manufacturers, used
  by Canon, Toshiba, and several smaller companies. It doesn't look like it
  was used in any TI calculator. It was up to the manufacturer to choose which
  functions(keys) to leave out.

*******************************************************************************/

class palmf31_state : public hh_tms1k_state
{
public:
	palmf31_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void palmf31(machine_config &config);

private:
	void update_display();
	void write_o(u16 data);
	void write_r(u32 data);
	u8 read_k();
};

// handlers

void palmf31_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void palmf31_state::write_r(u32 data)
{
	// R0-R8: select digit, input mux
	m_r = m_inp_mux = data;
	update_display();
}

void palmf31_state::write_o(u16 data)
{
	// O0-O7: digit segments
	m_o = data;
	update_display();
}

u8 palmf31_state::read_k()
{
	// K: multiplexed inputs, one of the columns is K2+K8
	u8 data = read_inputs(9);
	if (data & 1)
		data = (data & 0xe) | 0xa;

	// switches are on K1
	if (m_inp_mux & m_inputs[9]->read())
		data |= 1;

	return data;
}

// inputs

static INPUT_PORTS_START( palmf31 )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_M) PORT_NAME("M+") // add to memory
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("SC") // sign change

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_N) PORT_NAME("M-") // subtract from memory
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("RV") // reverse

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) // same as M+
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) // same as sqrt

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_NAME("(")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("1/x")

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_NAME(")")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME(u8"\u221a") // U+221A = √

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_P) PORT_NAME(u8"π")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME(u8"x²")

	PORT_START("IN.6") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("RM/CM") // combined function (press twice)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("% +/-")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Z) PORT_NAME("CM") // clear memory
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME("RM") // recall memory

	PORT_START("IN.7") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")

	PORT_START("IN.8") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_V) PORT_NAME("C") // clear (all)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME(u8"\u2190") // U+2190 = ←
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_NAME("CI") // clear indicator
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_NAME("CI/C") // combined function (press twice)

	PORT_START("IN.9") // K1
	PORT_CONFNAME( 0x1f, 0x00, "DP" ) // display point
	PORT_CONFSETTING(    0x02, "+" )
	PORT_CONFSETTING(    0x01, "0" )
	PORT_CONFSETTING(    0x04, "2" )
	PORT_CONFSETTING(    0x08, "3" )
	PORT_CONFSETTING(    0x10, "4" )
	PORT_CONFSETTING(    0x00, "F" )
	PORT_BIT( 0xe0, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_CONFNAME( 0x100, 0x000, "AM" ) // accumulate memory
	PORT_CONFSETTING(     0x000, DEF_STR( Off ) )
	PORT_CONFSETTING(     0x100, DEF_STR( On ) )
INPUT_PORTS_END

// config

void palmf31_state::palmf31(machine_config &config)
{
	// basic machine hardware
	TMS1040(config, m_maincpu, 250000); // approximation
	m_maincpu->read_k().set(FUNC(palmf31_state::read_k));
	m_maincpu->write_o().set(FUNC(palmf31_state::write_o));
	m_maincpu->write_r().set(FUNC(palmf31_state::write_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	m_display->set_segmask(0x1ff, 0xff);
	config.set_default_layout(layout_palmf31);

	// no sound!
}

// roms

ROM_START( palmf31 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tms1045nl", 0x0000, 0x0400, CRC(0c42d43e) SHA1(b76d404623e3abfd0b237ee1c0b46e83f96ceb78) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_palmf31_micro.pla", 0, 867, CRC(639cbc13) SHA1(a96152406881bdfc7ddc542cf4b478525c8b0e23) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_palmf31_output.pla", 0, 365, CRC(bc295ea6) SHA1(7e7c9ed0c1e5e37173dd8f297473516c410bca8c) )
ROM_END





/*******************************************************************************

  Canon Palmtronic MD-8 (Multi 8) / Canon Canola MD 810
  * PCB label: Canon EHI-0115-03
  * TMS1070 MCU label TMC1079 (die label: 1070B, 1079A)
  * cyan VFD Futaba 20-ST-22, 2-line 9-digit 7seg + 1 custom

  The only difference between MD-8 and MD 810 is the form factor. The latter
  is a tabletop calculator.

*******************************************************************************/

class palmmd8_state : public hh_tms1k_state
{
public:
	palmmd8_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void palmmd8(machine_config &config);

private:
	void update_display();
	void write_o(u16 data);
	void write_r(u32 data);
	u8 read_k();
};

// handlers

void palmmd8_state::update_display()
{
	// M-digit is on in memory mode, upper row is off in single mode
	u32 m = (m_inputs[10]->read() & 0x10) ? 0x100000 : 0;
	u32 mask = (m_inputs[10]->read() & 0x20) ? 0xfffff : 0xffc00;

	// R10 selects display row
	u32 sel = (m_r & 0x400) ? (m_r & 0x3ff) : (m_r << 10 & 0xffc00);
	m_display->matrix((sel & mask) | m, m_o);
}

void palmmd8_state::write_r(u32 data)
{
	// R0-R10: input mux, select digit
	m_r = m_inp_mux = data;
	update_display();
}

void palmmd8_state::write_o(u16 data)
{
	// O0-O7: digit segments
	m_o = bitswap<8>(data,0,4,5,6,7,1,2,3);
	update_display();
}

u8 palmmd8_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(11);
}

// inputs

static INPUT_PORTS_START( palmmd8 )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("% +/-")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME("RM") // recall memory
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("SC") // sign change
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Z) PORT_NAME("CM") // clear memory
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME(u8"\u221a") // U+221A = √
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("M+") // add to memory
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")

	PORT_START("IN.6") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_V) PORT_NAME("RV") // reverse
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")

	PORT_START("IN.7") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")

	PORT_START("IN.8") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")

	PORT_START("IN.9") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CI/C")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")

	PORT_START("IN.10") // R10
	PORT_CONFNAME( 0x31, 0x20, "Mode" ) // bit 4 indicates M-digit on/off, bit 5 indicates upper row filament on/off
	PORT_CONFSETTING(    0x31, "Memory" )
	PORT_CONFSETTING(    0x01, "Single" )
	PORT_CONFSETTING(    0x20, "Process" )
	PORT_CONFNAME( 0x02, 0x00, "AM" ) // accumulate memory
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x02, DEF_STR( On ) )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void palmmd8_state::palmmd8(machine_config &config)
{
	// basic machine hardware
	TMS1070(config, m_maincpu, 250000); // approximation - RC osc. R=56K, C=68pf
	m_maincpu->read_k().set(FUNC(palmmd8_state::read_k));
	m_maincpu->write_o().set(FUNC(palmmd8_state::write_o));
	m_maincpu->write_r().set(FUNC(palmmd8_state::write_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(21, 8);
	m_display->set_segmask(0xfffff, 0xff);
	config.set_default_layout(layout_palmmd8);

	// no sound!
}

// roms

ROM_START( palmmd8 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tmc1079nl", 0x0000, 0x0400, CRC(202c5ed8) SHA1(0143975cac20cb4a4e9f659ca0535e8a9056f5bb) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 406, "maincpu:opla", 0 )
	ROM_LOAD( "tms1070_palmmd8_output.pla", 0, 406, CRC(55117610) SHA1(fd06060ae5f04b802ae26697935d4a9765e1a7d7) )
ROM_END





/*******************************************************************************

  Chromatronics Chroma-Chime
  * PCB label: DESIGNED BY CHROMATRONICS, 118-2-003
  * TMS1000NL MP0027A (die label: 1000B, MP0027A)
  * 1-bit sound with volume decay

  It was sold as a kit (so, the buyer had to assemble the PCB themselves).
  Chromatronics claims it's the first ever processor-based musical doorbell.
  It's likely they asked TI for a generic melody chip. It would be odd for
  a UK product to play the US anthem (B2) much longer than the UK one (B1).

  known releases:
  - UK(1): Chroma-Chime, published by Chromatronics
  - UK(2): Door Tunes, published by Videomaster (Chromatronics PCB, not a kit)

  The MCU was also used in Bell-Fruit Oranges and Lemons (slot machine).

*******************************************************************************/

class cchime_state : public hh_tms1k_state
{
public:
	cchime_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag),
		m_volume(*this, "volume"),
		m_tempo_timer(*this, "tempo")
	{ }

	void cchime(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<filter_volume_device> m_volume;
	required_device<timer_device> m_tempo_timer;

	double m_speaker_volume = 0.0;

	void write_o(u16 data);
	void write_r(u32 data);
	u8 read_k();

	TIMER_DEVICE_CALLBACK_MEMBER(speaker_decay_sim);
};

void cchime_state::machine_start()
{
	hh_tms1k_state::machine_start();
	save_item(NAME(m_speaker_volume));
}

// handlers

TIMER_DEVICE_CALLBACK_MEMBER(cchime_state::speaker_decay_sim)
{
	m_volume->set_gain(m_speaker_volume);

	// volume decays when speaker is off, decay scale is determined by tone knob
	const double step = (1.005 - 1.0005) / 100.0; // approximation
	m_speaker_volume /= 1.005 - (double)(u8)m_inputs[4]->read() * step;
}

void cchime_state::write_r(u32 data)
{
	// R9: trigger tempo knob timer
	if (m_r & ~data & 0x200)
	{
		const double step = 50 / 100.0; // approximation
		m_tempo_timer->adjust(attotime::from_msec(50 + (u8)m_inputs[3]->read() * step));
	}

	// R10: power off when low (combined with doorbell)
	if (~data & 0x400 && !m_inputs[2]->read())
		power_off();

	m_r = data;
}

void cchime_state::write_o(u16 data)
{
	// O6+O7: speaker out
	m_speaker->level_w(BIT(~data, 6) & BIT(~data, 7));

	// O2: trigger speaker on
	if (~m_o & data & 4)
		m_volume->set_gain(m_speaker_volume = 1.0);

	m_o = data;
}

u8 cchime_state::read_k()
{
	u8 inp = 0;

	// R0-R7 + K1-K4: selected tune
	u8 rs = m_inputs[0]->read();
	u8 ks = m_inputs[1]->read();
	inp |= (m_r & rs) ? ks : 0;

	// K4: rear doorbell
	inp |= m_inputs[2]->read() << 1 & 4;

	// K8: tempo knob state
	if (m_tempo_timer->enabled())
		inp |= 8;

	return inp;
}

// inputs

static INPUT_PORTS_START( cchime )
	PORT_START("IN.0") // R0-R7
	PORT_CONFNAME( 0xff, 0x01, "Switch 1")
	PORT_CONFSETTING(    0x01, "A" )
	PORT_CONFSETTING(    0x02, "B" )
	PORT_CONFSETTING(    0x04, "C" )
	PORT_CONFSETTING(    0x08, "D" )
	PORT_CONFSETTING(    0x10, "E" )
	PORT_CONFSETTING(    0x20, "F" )
	PORT_CONFSETTING(    0x40, "G" )
	PORT_CONFSETTING(    0x80, "H" )

	PORT_START("IN.1") // K1-K4
	PORT_CONFNAME( 0x07, 0x01, "Switch 2")
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_CONFSETTING(    0x04, "3" )

	PORT_START("IN.2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Front Doorbell") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), true)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Rear Doorbell") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), true)

	PORT_START("IN.3")
	PORT_ADJUSTER(50, "Tempo Knob")

	PORT_START("IN.4")
	PORT_ADJUSTER(50, "Tone Knob")
INPUT_PORTS_END

// config

void cchime_state::cchime(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 400000); // approximation - RC osc. R=39K, C=47pF
	m_maincpu->read_k().set(FUNC(cchime_state::read_k));
	m_maincpu->write_o().set(FUNC(cchime_state::write_o));
	m_maincpu->write_r().set(FUNC(cchime_state::write_r));

	TIMER(config, "tempo").configure_generic(nullptr);

	// no visual feedback!

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "volume", 0.25);
	FILTER_VOLUME(config, m_volume).add_route(ALL_OUTPUTS, "mono", 1.0);

	TIMER(config, "speaker_decay").configure_periodic(FUNC(cchime_state::speaker_decay_sim), attotime::from_msec(1));
}

// roms

ROM_START( cchime )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp0027a.n1", 0x0000, 0x0400, CRC(8b5e2c4d) SHA1(5364d2836e9daefee2529a20c022e811bb3c7d89) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_cchime_output.pla", 0, 365, CRC(75d68c56) SHA1(85abde0ca0bcc605720551bea360498db350a7af) )
ROM_END





/*******************************************************************************

  Coleco Amaze-A-Tron, by Ralph Baer
  * TMS1100 MCU, label MP3405 (die label same)
  * 2-digit 7seg LED display + 2 LEDs(one red, one green), 1-bit sound
  * 5*5 pressure-sensitive playing board(buttons), 4 game pieces

  This is an electronic board game with a selection of 8 maze games,
  most of them for 2 players.

*******************************************************************************/

class amaztron_state : public hh_tms1k_state
{
public:
	amaztron_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void amaztron(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void amaztron_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void amaztron_state::write_r(u32 data)
{
	// R0-R5: input mux
	m_inp_mux = data & 0x3f;

	// R10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// R6,R7: leds
	// R8,R9: select digit
	m_r = data >> 6 & 0xf;
	update_display();
}

void amaztron_state::write_o(u16 data)
{
	// O0-O6: digit segments A-G
	// O7: N/C
	m_o = data & 0x7f;
	update_display();
}

u8 amaztron_state::read_k()
{
	// K: multiplexed inputs
	u8 k = read_inputs(6);

	// the 5th column is tied to K4+K8
	if (k & 0x10) k |= 0xc;
	return k & 0xf;
}

// inputs

static INPUT_PORTS_START( amaztron )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 6")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 11")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 16")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 21")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 7")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 12")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 17")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 22")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 13")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 18")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 23")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 9")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 14")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 19")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 24")

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 5")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 10")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 15")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 20")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 25")

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_NAME("Game Select")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START ) PORT_NAME("Game Start")
	PORT_BIT( 0x1c, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void amaztron_state::amaztron(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 300000); // approximation - RC osc. R=33K?, C=100pF
	m_maincpu->read_k().set(FUNC(amaztron_state::read_k));
	m_maincpu->write_r().set(FUNC(amaztron_state::write_r));
	m_maincpu->write_o().set(FUNC(amaztron_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 7);
	m_display->set_segmask(0xc, 0x7f);
	config.set_default_layout(layout_amaztron);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( amaztron )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp3405", 0x0000, 0x0800, CRC(9cbc0009) SHA1(17772681271b59280687492f37fa0859998f041d) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common3_micro.pla", 0, 867, CRC(03574895) SHA1(04407cabfb3adee2ee5e4218612cb06c12c540f4) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_amaztron_output.pla", 0, 365, CRC(f3875384) SHA1(3c256a3db4f0aa9d93cf78124db39f4cbdc57e4a) )
ROM_END





/*******************************************************************************

  Coleco Zodiac: The Astrology Computer (model 2110)
  * TMS1100 MP3435 (no decap)
  * 8-digit 7seg display, 12 other LEDs, 1-bit sound

  As the name suggests, this is an astrologic calculator. Refer to the
  (very extensive) manual on how to use it.

*******************************************************************************/

class zodiac_state : public hh_tms1k_state
{
public:
	zodiac_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void zodiac(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void zodiac_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void zodiac_state::write_r(u32 data)
{
	// R10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// R0-R4,R8: input mux
	m_inp_mux = (data & 0x1f) | (data >> 3 & 0x20);

	// R0-R7: digit select
	// R8,R9: led select
	m_r = data & 0x3ff;
	update_display();
}

void zodiac_state::write_o(u16 data)
{
	// O0-O7: digit segment/led data
	m_o = bitswap<8>(data,0,7,6,5,4,3,2,1);
	update_display();
}

u8 zodiac_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(6);
}

// inputs

/* physical button layout and labels are like this:

      [P]                                [A]
                [ 1 ] [ 2 ] [ 3 ]
    [L]         [ 4 ] [ 5 ] [ 6 ]          [D]
                [ 7 ] [ 8 ] [ 9 ]
      [J]       [ 0 ] [CLR] [ENT]        [E]

                  [.__.__.__.]
                  OFF H  P  A
*/

static INPUT_PORTS_START( zodiac )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("D")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("A")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_P) PORT_NAME("P")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_L) PORT_NAME("L")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_J) PORT_NAME("J")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("E")

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME("Clear")

	PORT_START("IN.5") // R8
	PORT_CONFNAME( 0x03, 0x01, "Mode")
	PORT_CONFSETTING(    0x01, "Horoscope" )
	PORT_CONFSETTING(    0x02, "Preview" )
	PORT_CONFSETTING(    0x00, "Answer" )
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void zodiac_state::zodiac(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 500000); // approximation - RC osc. R=18K, C=100pF
	m_maincpu->read_k().set(FUNC(zodiac_state::read_k));
	m_maincpu->write_r().set(FUNC(zodiac_state::write_r));
	m_maincpu->write_o().set(FUNC(zodiac_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 8);
	m_display->set_segmask(0xff, 0x7f);
	config.set_default_layout(layout_zodiac);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( zodiac )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp3435", 0x0000, 0x0800, CRC(ecdc3160) SHA1(a7e82d66314a039fcffeddf99919d9f9ad42d61d) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common3_micro.pla", 0, 867, BAD_DUMP CRC(03574895) SHA1(04407cabfb3adee2ee5e4218612cb06c12c540f4) ) // not verified
	ROM_REGION( 365, "maincpu:opla", ROMREGION_ERASE00 )
	ROM_LOAD( "tms1100_zodiac_output.pla", 0, 365, NO_DUMP )

	ROM_REGION16_LE( 0x40, "maincpu:opla_b", ROMREGION_ERASE00 ) // verified, electronic dump
	ROM_LOAD16_BYTE( "tms1100_zodiac_output.bin", 0, 0x20, CRC(d35d64cb) SHA1(315e7ca9a18a06ff7dc0b95c6f119e093b15a41f) )
ROM_END





/*******************************************************************************

  Coleco Electronic Quarterback (model 2120)
  * TMS1100NLL MP3415 (die label: 1100B, MP3415)
  * 9-digit LED grid, 1-bit sound

  known releases:
  - USA(1): Electronic Quarterback, published by Coleco
  - USA(2): Electronic Touchdown, published by Sears

*******************************************************************************/

class cqback_state : public hh_tms1k_state
{
public:
	cqback_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void cqback(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void cqback_state::update_display()
{
	// R9 selects between segments B/C or A'/D'
	u16 seg = m_o;
	if (m_r & 0x200)
		seg = (m_o << 7 & 0x300) | (m_o & 0xf9);

	m_display->matrix(m_r & 0x1ff, seg);
}

void cqback_state::write_r(u32 data)
{
	// R10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// R0-R4: input mux
	m_inp_mux = data & 0x1f;

	// R0-R9: select digit/segment
	m_r = data;
	update_display();
}

void cqback_state::write_o(u16 data)
{
	// O0-O7: digit segments
	m_o = data;
	update_display();
}

u8 cqback_state::read_k()
{
	// K: multiplexed inputs, rotated matrix
	return read_rotated_inputs(5);
}

// inputs

static INPUT_PORTS_START( cqback )
	PORT_START("IN.0") // K1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Forward")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Kick/Pass")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_START ) PORT_NAME("Display")

	PORT_START("IN.1") // K2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_TOGGLE PORT_NAME("Play Selector") // pass
	PORT_BIT( 0x02, 0x02, IPT_CUSTOM ) PORT_CONDITION("IN.1", 0x01, EQUALS, 0x00) // run/kick

	PORT_START("IN.2") // K4
	PORT_CONFNAME( 0x03, 0x01, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x02, "2" )

	PORT_START("IN.3") // K8
	PORT_CONFNAME( 0x01, 0x00, "Factory Test" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) ) // TP1-TP2
INPUT_PORTS_END

// config

void cqback_state::cqback(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 310000); // approximation - RC osc. R=33K, C=100pF
	m_maincpu->read_k().set(FUNC(cqback_state::read_k));
	m_maincpu->write_r().set(FUNC(cqback_state::write_r));
	m_maincpu->write_o().set(FUNC(cqback_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 11);
	m_display->set_segmask(0x1ff, 0xff);
	m_display->set_bri_levels(0.003, 0.03); // offense leds are brighter
	config.set_default_layout(layout_cqback);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( cqback )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp3415.u4", 0x0000, 0x0800, CRC(65ebdabf) SHA1(9b5cf5adaf9132ced87f611ae8c3148b9b62ba89) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common3_micro.pla", 0, 867, CRC(03574895) SHA1(04407cabfb3adee2ee5e4218612cb06c12c540f4) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_cqback_output.pla", 0, 365, CRC(c6dcbfd0) SHA1(593b6b7de981a28d1b4a33336b39df92d02ed4f4) )
ROM_END





/*******************************************************************************

  Coleco Head to Head: Electronic Football (model 2140)
  * TMS1100NLLE (rev. E!) MP3460 (die label: 1100E, MP3460)
  * 2*SN75492N LED display drivers, 9-digit LED grid, 1-bit sound

  LED electronic football game. To distinguish between offense and defense,
  offense blips appear brighter. The hardware is similar to cqback.

  known releases:
  - USA(1): Head to Head: Electronic Football, published by Coleco
  - USA(2): Team Play Football, published by Sears

*******************************************************************************/

class h2hfootb_state : public hh_tms1k_state
{
public:
	h2hfootb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void h2hfootb(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void h2hfootb_state::update_display()
{
	m_display->matrix(m_r & 0x1ff, m_o | (m_r >> 1 & 0x100));
}

void h2hfootb_state::write_r(u32 data)
{
	// R10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// R0-R8: input mux
	m_inp_mux = data & 0x1ff;

	// R0-R8: select led
	// R9: led between digits
	m_r = data;
	update_display();
}

void h2hfootb_state::write_o(u16 data)
{
	// O0-O7: digit segments A-G,A'
	m_o = data;
	update_display();
}

u8 h2hfootb_state::read_k()
{
	// K: multiplexed inputs, rotated matrix
	return read_rotated_inputs(9);
}

// inputs

static INPUT_PORTS_START( h2hfootb )
	PORT_START("IN.0") // K1
	PORT_CONFNAME( 0x03, 0x01, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x02, "2" )

	PORT_START("IN.1") // K2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_TOGGLE PORT_NAME("P1 Play Selector") // pass
	PORT_BIT( 0x02, 0x02, IPT_CUSTOM ) PORT_CONDITION("IN.1", 0x01, EQUALS, 0x00) // run/kick

	PORT_START("IN.2") // K4
	PORT_CONFNAME( 0x03, 0x01, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x02, "2" )

	PORT_START("IN.3") // K8
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Forward")
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Kick/Pass")
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_START ) PORT_NAME("Display")
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL PORT_16WAY
INPUT_PORTS_END

// config

void h2hfootb_state::h2hfootb(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 310000); // approximation - RC osc. R=39K, C=100pF
	m_maincpu->read_k().set(FUNC(h2hfootb_state::read_k));
	m_maincpu->write_r().set(FUNC(h2hfootb_state::write_r));
	m_maincpu->write_o().set(FUNC(h2hfootb_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 9);
	m_display->set_segmask(0x1ff, 0x7f);
	m_display->set_bri_levels(0.003, 0.03); // offense leds are brighter
	config.set_default_layout(layout_h2hfootb);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( h2hfootb )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp3460.u3", 0x0000, 0x0800, CRC(3a4e53a8) SHA1(5052e706f992c6c4bada1fa7769589eec3df6471) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_h2hfootb_output.pla", 0, 365, CRC(c8d85873) SHA1(16bd6fc8e3cd16d5f8fd32d0c74e67de77f5487e) )
ROM_END





/*******************************************************************************

  Coleco Head to Head: Electronic Basketball (model 2150)
  * TMS1000NLL MP3320A (die label: 1000E, MP3320A)
  * 2-digit 7seg LED display, LED grid display, 1-bit sound

  Coleco Head to Head: Electronic Hockey (model 2160)
  * TMS1000NLL E MP3321A (die label: 1000E, MP3321A)
  * same PCB/hardware as above

  Unlike the COP420 version(see hh_cop400.cpp driver), each game has its own MCU.
  To begin play, press start while holding left/right.

*******************************************************************************/

class h2hbaskb_state : public hh_tms1k_state
{
public:
	h2hbaskb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag),
		m_cap_discharge(*this, "cap_discharge")
	{ }

	void h2hbaskb(machine_config &config);
	void h2hhockey(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<timer_device> m_cap_discharge;
	attotime m_cap_charge = attotime::zero;

	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

void h2hbaskb_state::machine_start()
{
	hh_tms1k_state::machine_start();

	// register for savestates
	save_item(NAME(m_cap_charge));
}

// handlers

void h2hbaskb_state::update_display()
{
	// R6,R7 are commons for R0-R5
	u16 sel = 0;
	if (m_r & 0x40) sel |= (m_r & 0x3f);
	if (m_r & 0x80) sel |= (m_r & 0x3f) << 6;

	m_display->matrix(sel, m_o);
}

void h2hbaskb_state::write_r(u32 data)
{
	// R0-R3: input mux
	m_inp_mux = (data & 0xf);

	// R8: speaker out
	m_speaker->level_w(data >> 8 & 1);

	// R9: K8 and 15uF cap to V- (used as timer)
	// rising edge, remember the time
	if (data & ~m_r & 0x200)
		m_cap_charge = machine().time();

	// falling edge, determine how long K8 should stay up
	else if (~data & m_r & 0x200)
	{
		const attotime full = attotime::from_usec(1300); // approx. charge time
		const int factor = 27; // approx. factor for charge/discharge to logic 0

		attotime charge = machine().time() - m_cap_charge;
		if (charge > full)
			charge = full;

		m_cap_discharge->adjust(charge * factor);
	}

	// R0-R7: led select
	m_r = data;
	update_display();
}

void h2hbaskb_state::write_o(u16 data)
{
	// O1-O7: led data
	m_o = data >> 1 & 0x7f;
	update_display();
}

u8 h2hbaskb_state::read_k()
{
	// K1-K4: multiplexed inputs, K8: R9 and capacitor
	u8 cap_state = (m_r & 0x200 || m_cap_discharge->enabled()) ? 8 : 0;
	return (read_inputs(4) & 7) | cap_state;
}

// inputs

static INPUT_PORTS_START( h2hbaskb )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY PORT_NAME("P1 Pass CW") // clockwise
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY PORT_NAME("P1 Pass CCW") // counter-clockwise
	PORT_CONFNAME( 0x04, 0x00, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x04, "2" )

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Shoot")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START ) PORT_NAME("Start/Display")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL PORT_16WAY PORT_NAME("P2 Defense Right")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL PORT_16WAY PORT_NAME("P2 Defense Left")
	PORT_CONFNAME( 0x04, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x04, "2" )

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( h2hhockey )
	PORT_INCLUDE( h2hbaskb )

	PORT_MODIFY("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_COCKTAIL PORT_NAME("P2 Goalie Right")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Goalie Left")
INPUT_PORTS_END

// config

void h2hbaskb_state::h2hbaskb(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 375000); // approximation - RC osc. R=43K, C=100pF
	m_maincpu->read_k().set(FUNC(h2hbaskb_state::read_k));
	m_maincpu->write_r().set(FUNC(h2hbaskb_state::write_r));
	m_maincpu->write_o().set(FUNC(h2hbaskb_state::write_o));

	TIMER(config, "cap_discharge").configure_generic(nullptr);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(6+6, 7);
	m_display->set_segmask(0xc0, 0x7f);
	config.set_default_layout(layout_h2hbaskb);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

void h2hbaskb_state::h2hhockey(machine_config &config)
{
	h2hbaskb(config);
	config.set_default_layout(layout_h2hhockey);
}

// roms

ROM_START( h2hbaskb )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp3320a", 0x0000, 0x0400, CRC(39a63f43) SHA1(14a765e42a39f8d3a465c990e09dd651e595a1c5) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common1_micro.pla", 0, 867, CRC(4becec19) SHA1(3c8a9be0f00c88c81f378b76886c39b10304f330) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_h2hbaskb_output.pla", 0, 365, CRC(9d1a91e1) SHA1(96303eb22375129b0dfbfcd823c8ca5b919511bc) )
ROM_END

ROM_START( h2hhockey )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp3321a", 0x0000, 0x0400, CRC(e974e604) SHA1(ed740c98ce96ad70ee5237eccae1f54a75ad8100) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common1_micro.pla", 0, 867, CRC(4becec19) SHA1(3c8a9be0f00c88c81f378b76886c39b10304f330) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_h2hhockey_output.pla", 0, 365, CRC(9d1a91e1) SHA1(96303eb22375129b0dfbfcd823c8ca5b919511bc) )
ROM_END





/*******************************************************************************

  Coleco Head to Head: Electronic Baseball (model 2180)
  * PCB labels: Coleco rev C 73891/2
  * TMS1170NLN MP1525-N2 (die label: 1170A, MP1525)
  * 9-digit cyan VFD, and other LEDs behind bezel, 1-bit sound

  known releases:
  - USA: Head to Head: Electronic Baseball, published by Coleco
  - Japan: Computer Baseball, published by Tsukuda

*******************************************************************************/

class h2hbaseb_state : public hh_tms1k_state
{
public:
	h2hbaseb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void h2hbaseb(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(skill_switch);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

INPUT_CHANGED_MEMBER(h2hbaseb_state::skill_switch)
{
	// MCU clock is from an RC circuit with C=47pF, and R value is depending on
	// skill switch: R=51K(1) or 43K(2)
	m_maincpu->set_unscaled_clock((newval & 1) ? 400000 : 350000);
}

void h2hbaseb_state::update_display()
{
	m_display->matrix((m_r & 0xff) | (m_r >> 1 & 0x100), (m_r & 0x100) | m_o);
}

void h2hbaseb_state::write_r(u32 data)
{
	// R10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// R4-R7: input mux
	m_inp_mux = data >> 4 & 0xf;

	// R0-R7,R9: select vfd digit/led
	// R8: led state
	m_r = data;
	update_display();
}

void h2hbaseb_state::write_o(u16 data)
{
	// O0-O6: digit segments A-G
	// O7: N/C
	m_o = data;
	update_display();
}

u8 h2hbaseb_state::read_k()
{
	// K: multiplexed inputs (note: K8(Vss row) is always on)
	return m_inputs[4]->read() | read_inputs(4);
}

// inputs

static INPUT_PORTS_START( h2hbaseb )
	PORT_START("IN.0") // R4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("N")
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("B")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("S")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_NAME("Curve") // these two buttons appear twice on the board
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Fast Pitch") // "
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // Vss
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Swing")

	PORT_START("CPU") // fake
	PORT_CONFNAME( 0x01, 0x00, DEF_STR( Difficulty ) ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(h2hbaseb_state::skill_switch), 0)
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x01, "2" )
INPUT_PORTS_END

// config

void h2hbaseb_state::h2hbaseb(machine_config &config)
{
	// basic machine hardware
	TMS1170(config, m_maincpu, 350000); // see skill_switch
	m_maincpu->read_k().set(FUNC(h2hbaseb_state::read_k));
	m_maincpu->write_r().set(FUNC(h2hbaseb_state::write_r));
	m_maincpu->write_o().set(FUNC(h2hbaseb_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 9);
	m_display->set_segmask(0x1ff, 0x7f);
	config.set_default_layout(layout_h2hbaseb);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( h2hbaseb )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1525", 0x0000, 0x0800, CRC(b5d6bf9b) SHA1(2cc9f35f077c1209c46d16ec853af87e4725c2fd) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_h2hbaseb_output.pla", 0, 365, CRC(cb3d7e38) SHA1(6ab4a7c52e6010b7c7158463cb499973e52ff556) )
ROM_END





/*******************************************************************************

  Coleco Head to Head: Electronic Boxing (model 2190)
  * TMS1100NLL M34018-N2 (die label: 1100E, M34018)
  * 2-digit 7seg LED display, LED grid display, 1-bit sound

  This appears to be the last game of Coleco's Head to Head series.

*******************************************************************************/

class h2hboxing_state : public hh_tms1k_state
{
public:
	h2hboxing_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void h2hboxing(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void h2hboxing_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void h2hboxing_state::write_r(u32 data)
{
	// R0-R4: input mux
	m_inp_mux = data & 0x1f;

	// R8: speaker out
	m_speaker->level_w(data >> 8 & 1);

	// R0-R7: select led
	// R9,R10: select digit
	m_r = data & ~0x100;
	update_display();
}

void h2hboxing_state::write_o(u16 data)
{
	// O0-O7: digit segments/led data
	m_o = data;
	update_display();
}

u8 h2hboxing_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(5);
}

// inputs

static INPUT_PORTS_START( h2hboxing )
	PORT_START("IN.0") // R0
	PORT_CONFNAME( 0x01, 0x00, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x01, "2" )
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Punch / Pro")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Block / Amateur")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // R4
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_COCKTAIL PORT_NAME("P2 Punch")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Block")
INPUT_PORTS_END

// config

void h2hboxing_state::h2hboxing(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 350000); // approximation - RC osc. R=39K, C=100pF
	m_maincpu->read_k().set(FUNC(h2hboxing_state::read_k));
	m_maincpu->write_r().set(FUNC(h2hboxing_state::write_r));
	m_maincpu->write_o().set(FUNC(h2hboxing_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(11, 8);
	m_display->set_segmask(0x600, 0x7f);
	config.set_default_layout(layout_h2hboxing);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( h2hboxing )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "m34018", 0x0000, 0x0800, CRC(e26a11a3) SHA1(aa2735088d709fa8d9188c4fb7982a53e3a8c1bc) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_h2hboxing_output.pla", 0, 365, CRC(ffb0e63d) SHA1(31ee3f779270a23f05f9ad508283d2569ef069f1) )
ROM_END





/*******************************************************************************

  Coleco Quiz Wiz Challenger
  * TMS1000NLL M32001-N2 (die label: 1000E, M32001)
  * 4 7seg LEDs, 17 other LEDs, 1-bit sound

  This is a 4-player version of Quiz Wiz, a multiple choice quiz game.
  According to the manual, Quiz Wiz cartridges are compatible with it.
  The question books are needed to play, as well as optional game pieces.

  Cartridge pinout:
  1 R4
  2 R6
  3 R7
  4 K1
  5 N/C
  6 R8
  7 R5
  8 R9

  The cartridge connects one or more of the R pins to K1. Together with the
  question numbers, the game generates a pseudo-random answerlist.

*******************************************************************************/

class quizwizc_state : public hh_tms1k_state
{
public:
	quizwizc_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void quizwizc(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	u16 m_pinout = 0x07; // cartridge R pins
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

void quizwizc_state::machine_start()
{
	hh_tms1k_state::machine_start();

	// register for savestates
	save_item(NAME(m_pinout));
}

// handlers

DEVICE_IMAGE_LOAD_MEMBER(quizwizc_state::cart_load)
{
	if (!image.loaded_through_softlist())
		return std::make_pair(image_error::UNSUPPORTED, "Can only load through software list");

	// get cartridge pinout K1 to R connections
	const char *pinout = image.get_feature("pinout");
	m_pinout = pinout ? strtoul(pinout, nullptr, 2) & 0xe7 : 0;
	m_pinout = bitswap<8>(m_pinout,4,3,7,5,2,1,6,0) << 4;

	if (m_pinout == 0)
		return std::make_pair(image_error::BADSOFTWARE, "Invalid cartridge pinout");

	return std::make_pair(std::error_condition(), std::string());
}

void quizwizc_state::update_display()
{
	// note: O7 is on VSS
	m_display->matrix(m_r | 0x400, m_o);
}

void quizwizc_state::write_r(u32 data)
{
	// R10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// R0-R5: input mux
	// R4-R9: to cartridge slot
	m_inp_mux = data & 0x3f;

	// R0-R3: led select
	// R6-R9: digit select
	m_r = data;
	update_display();
}

void quizwizc_state::write_o(u16 data)
{
	// O0-O7: led/digit segment data
	m_o = bitswap<8>(data,7,0,1,2,3,4,5,6);
	update_display();
}

u8 quizwizc_state::read_k()
{
	// K: multiplexed inputs
	// K1: cartridge pin 4 (pin 5 N/C)
	return read_inputs(6) | ((m_r & m_pinout) ? 1 : 0);
}

// inputs

static INPUT_PORTS_START( quizwizc )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) // A (player 1 at bottom, increment counter-clockwise)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) // B
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) // C
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(1) // D

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(4)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(4)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(4)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(4)

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(3)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(3)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(3)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(3)

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(2)

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START ) PORT_NAME("Go")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Fast Forward")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Slow Forward")

	PORT_START("IN.5") // R5
	PORT_CONFNAME( 0x02, 0x00, "Game Select" )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void quizwizc_state::quizwizc(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 300000); // approximation - RC osc. R=43K, C=100pF
	m_maincpu->read_k().set(FUNC(quizwizc_state::read_k));
	m_maincpu->write_r().set(FUNC(quizwizc_state::write_r));
	m_maincpu->write_o().set(FUNC(quizwizc_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10+1, 8);
	m_display->set_segmask(0x3c0, 0x7f);
	config.set_default_layout(layout_quizwizc);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);

	// cartridge
	generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "quizwiz_cart"));
	cartslot.set_must_be_loaded(true);
	cartslot.set_device_load(FUNC(quizwizc_state::cart_load));
	SOFTWARE_LIST(config, "cart_list").set_original("quizwiz");
}

// roms

ROM_START( quizwizc )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "m32001", 0x0000, 0x0400, CRC(053657eb) SHA1(38c84f7416f79aa679f434a3d35df54cd9aa528a) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common4_micro.pla", 0, 867, CRC(80912d0a) SHA1(7ae5293ed4d93f5b7a64d43fe30c3639f39fbe5a) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_quizwizc_output.pla", 0, 365, CRC(475b7053) SHA1(8f61bf736eb41d7029a6b165cc0a184ba0a70a2a) )
ROM_END





/*******************************************************************************

  Coleco Total Control 4
  * TMS1400NLL MP7334-N2 (die label: TMS1400, MP7334, 28L 01D D000 R000)
  * 2x2-digit 7seg LED display + 4 LEDs, LED grid display, 1-bit sound

  This is a head to head electronic tabletop LED-display sports console.
  One cartridge(Football) was included with the console, the other three were
  sold in a pack. Gameplay has emphasis on strategy, read the official manual
  on how to play. MAME external artwork is needed for the switchable overlays.

  Cartridge pinout:
  1 N/C
  2 9V+
  3 power switch
  4 K8
  5 K4
  6 K2
  7 K1
  8 R9

  The cartridge connects pin 8 with one of the K-pins.

*******************************************************************************/

class tc4_state : public hh_tms1k_state
{
public:
	tc4_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void tc4(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	u8 m_pinout = 0xf; // cartridge K pins
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

void tc4_state::machine_start()
{
	hh_tms1k_state::machine_start();

	// register for savestates
	save_item(NAME(m_pinout));
}

// handlers

DEVICE_IMAGE_LOAD_MEMBER(tc4_state::cart_load)
{
	if (!image.loaded_through_softlist())
		return std::make_pair(image_error::UNSUPPORTED, "Can only load through software list");

	// get cartridge pinout R9 to K connections
	const char *pinout = image.get_feature("pinout");
	m_pinout = pinout ? strtoul(pinout, nullptr, 0) & 0xf : 0xf;

	return std::make_pair(std::error_condition(), std::string());
}

void tc4_state::update_display()
{
	// note: R6 is an extra column
	m_display->matrix(m_r, (m_o | (m_r << 2 & 0x100)));
}

void tc4_state::write_r(u32 data)
{
	// R10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// R0-R5: input mux
	// R9: to cartridge slot
	m_inp_mux = data & 0x3f;

	// R0-R4: select led
	// R6: led 8 state
	// R5,R7-R9: select digit
	m_r = data;
	update_display();
}

void tc4_state::write_o(u16 data)
{
	// O0-O7: led state
	m_o = data;
	update_display();
}

u8 tc4_state::read_k()
{
	// K: multiplexed inputs, cartridge pins from R9
	return read_inputs(6) | ((m_r & 0x200) ? m_pinout : 0);
}

// inputs

static INPUT_PORTS_START( tc4 )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P1 Pass/Shoot Button 3") // right
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Pass/Shoot Button 1") // left
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Pass/Shoot Button 2") // middle
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("P1 D/K Button")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_RIGHT )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_LEFT )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN )

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_RIGHT )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_LEFT )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN )

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_PLAYER(2)

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_PLAYER(2)

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("P2 Pass/Shoot Button 3") // right
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 Pass/Shoot Button 1") // left
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 Pass/Shoot Button 2") // middle
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("P2 D/K Button")
INPUT_PORTS_END

// config

void tc4_state::tc4(machine_config &config)
{
	// basic machine hardware
	TMS1400(config, m_maincpu, 450000); // approximation - RC osc. R=27.3K, C=100pF
	m_maincpu->read_k().set(FUNC(tc4_state::read_k));
	m_maincpu->write_r().set(FUNC(tc4_state::write_r));
	m_maincpu->write_o().set(FUNC(tc4_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 9);
	m_display->set_segmask(0x3a0, 0x7f);
	m_display->set_bri_levels(0.005, 0.05); // offense leds are brighter
	config.set_default_layout(layout_tc4);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);

	// cartridge
	generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "tc4_cart"));
	cartslot.set_must_be_loaded(true); // system won't power on without cartridge
	cartslot.set_device_load(FUNC(tc4_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("tc4");
}

// roms

ROM_START( tc4 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mp7334", 0x0000, 0x1000, CRC(923f3821) SHA1(a9ae342d7ff8dae1dedcd1e4984bcfae68586581) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 557, "maincpu:opla", 0 )
	ROM_LOAD( "tms1400_tc4_output.pla", 0, 557, CRC(3b908725) SHA1(f83bf5faa5b3cb51f87adc1639b00d6f9a71ad19) )
ROM_END





/*******************************************************************************

  Concept 2000 Lite 'n Learn (model 554)
  * PCB label: CONCEPT 2000, SEC 554
  * TMS1000NLL MP3207 (die label: 1000C, MP3207)
  * 10 LEDs, 1-bit sound with volume decay

  Two versions are known, one with the tone knob, and one without. The MCU
  is the same for both.

  Electronic Jukebox (model 552) has the exact same MCU as well, it's on a
  much smaller PCB. It doesn't have the tone knob either. They removed the
  LEDs and learn mode.

*******************************************************************************/

class litelrn_state : public hh_tms1k_state
{
public:
	litelrn_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag),
		m_volume(*this, "volume")
	{ }

	void litelrn(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<filter_volume_device> m_volume;

	double m_speaker_volume = 0.0;

	void write_o(u16 data);
	void write_r(u32 data);
	u8 read_k();

	TIMER_DEVICE_CALLBACK_MEMBER(speaker_decay_sim);
};

void litelrn_state::machine_start()
{
	hh_tms1k_state::machine_start();
	save_item(NAME(m_speaker_volume));
}

// handlers

TIMER_DEVICE_CALLBACK_MEMBER(litelrn_state::speaker_decay_sim)
{
	m_volume->set_gain(m_speaker_volume);

	// volume decays when speaker is off, rate is determined by tone knob
	const double div[3] = { 1.002, 1.004, 1.008 }; // approximation
	m_speaker_volume /= div[m_inputs[2]->read() % 3];
}

void litelrn_state::write_r(u32 data)
{
	// R0: speaker out
	m_speaker->level_w(data & 1);

	// R1-R10: input mux, leds
	m_inp_mux = data >> 1;
	m_display->matrix(1, data >> 1);
}

void litelrn_state::write_o(u16 data)
{
	// O0: trigger speaker on
	if (~data & m_o & 1)
		m_volume->set_gain(m_speaker_volume = 1.0);

	// O2: select mode switch
	// other: N/C
	m_o = data;
}

u8 litelrn_state::read_k()
{
	// K1: multiplexed inputs
	u8 data = read_rotated_inputs(10, 1);

	// K4,K8: mode switch
	if (m_o & 4)
		data |= m_inputs[1]->read() & 0xc;

	return data;
}

// inputs

static INPUT_PORTS_START( litelrn )
	PORT_START("IN.0") // K1
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("10")

	PORT_START("IN.1") // O2
	PORT_CONFNAME( 0x0c, 0x00, "Mode" )
	PORT_CONFSETTING(    0x00, "Learn" )
	PORT_CONFSETTING(    0x0c, "Auto" )
	PORT_CONFSETTING(    0x04, "Manual" )

	PORT_START("IN.2")
	PORT_CONFNAME( 0x03, 0x00, "Tone" )
	PORT_CONFSETTING(    0x00, "Organ" )
	PORT_CONFSETTING(    0x01, "Harpsichord" )
	PORT_CONFSETTING(    0x02, "Banjo" )
INPUT_PORTS_END

// config

void litelrn_state::litelrn(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 325000); // approximation - RC osc. R=33K, C=100pF
	m_maincpu->read_k().set(FUNC(litelrn_state::read_k));
	m_maincpu->write_o().set(FUNC(litelrn_state::write_o));
	m_maincpu->write_r().set(FUNC(litelrn_state::write_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 10);
	m_display->set_bri_levels(0.25);
	config.set_default_layout(layout_litelrn);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "volume", 0.25);
	FILTER_VOLUME(config, m_volume).add_route(ALL_OUTPUTS, "mono", 1.0);

	TIMER(config, "speaker_decay").configure_periodic(FUNC(litelrn_state::speaker_decay_sim), attotime::from_msec(1));
}

// roms

ROM_START( litelrn )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp3207", 0x0000, 0x0400, CRC(9fd7bc0b) SHA1(0dd2903bb33833e85103a1d012fda449d92722af) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common1_micro.pla", 0, 867, CRC(4becec19) SHA1(3c8a9be0f00c88c81f378b76886c39b10304f330) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_litelrn_output.pla", 0, 365, CRC(393d859e) SHA1(2d224a8dee621ee598001ebdac3e57cae52c93e1) )
ROM_END





/*******************************************************************************

  Concept 2000 Mr. Mus-I-Cal (model 560)
  * PCB label: CONCEPT 2000, ITE 556
  * TMS1000NLL MP3206 (die label: 1000C, MP3206)
  * 9-digit 7seg LED display(one custom digit), 1-bit sound

  It's a simple 4-function calculator, and plays music tones too.

*******************************************************************************/

class mrmusical_state : public hh_tms1k_state
{
public:
	mrmusical_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void mrmusical(machine_config &config);

private:
	void update_display();
	void write_o(u16 data);
	void write_r(u32 data);
	u8 read_k();
};

// handlers

void mrmusical_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void mrmusical_state::write_r(u32 data)
{
	// R10: speaker out (there's a speaker off-on switch)
	m_speaker->level_w(m_inputs[6]->read() & BIT(data, 10));

	// R0-R5: input mux
	// R0-R8: digit select (R6 is a math symbol like with lilprof)
	m_inp_mux = m_r = data;
	update_display();
}

void mrmusical_state::write_o(u16 data)
{
	// O0-O7: digit segment data
	m_o = data;
	update_display();
}

u8 mrmusical_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(6);
}

// inputs

static INPUT_PORTS_START( mrmusical )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("A=")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_NAME("C")

	PORT_START("IN.5") // R5
	PORT_CONFNAME( 0x01, 0x00, "Mode" )
	PORT_CONFSETTING(    0x01, "A" )
	PORT_CONFSETTING(    0x00, "=" )

	PORT_START("IN.6") // no read
	PORT_CONFNAME( 0x01, 0x01, "Speaker" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
INPUT_PORTS_END

// config

void mrmusical_state::mrmusical(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 325000); // approximation - RC osc. R=33K, C=100pF
	m_maincpu->read_k().set(FUNC(mrmusical_state::read_k));
	m_maincpu->write_o().set(FUNC(mrmusical_state::write_o));
	m_maincpu->write_r().set(FUNC(mrmusical_state::write_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	m_display->set_segmask(0x1ff, 0xff);
	m_display->set_segmask(0x180, 0x7f); // no DP for leftmost 2 digits
	config.set_default_layout(layout_mrmusical);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( mrmusical )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp3206", 0x0000, 0x0400, CRC(15013d4d) SHA1(2bbc5599183381ad050b7518bca1babc579ee79d) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common1_micro.pla", 0, 867, CRC(4becec19) SHA1(3c8a9be0f00c88c81f378b76886c39b10304f330) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_mrmusical_output.pla", 0, 365, CRC(9652aa3b) SHA1(be41e1588561875b362ba878098ede8299792166) )
ROM_END





/*******************************************************************************

  Conic Electronic Basketball
  * PCB label: CONIC 101-006
  * TMS1000NLL MP0907 (die label: 1000B, MP0907)
  * DS8871N, 2 7seg LEDs, 30 other LEDs, 1-bit sound

  There are 3 known versions of Conic Basketball: MP0910(101-003) and
  MP0907(101-006) are nearly identical. MP0168 is found in Conic Multisport.

  known releases:
  - Hong Kong: Electronic Basketball, published by Conic
  - USA: Electronic Basketball, published by Cardinal

*******************************************************************************/

class cnbaskb_state : public hh_tms1k_state
{
public:
	cnbaskb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void cnbaskb(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void cnbaskb_state::update_display()
{
	m_display->matrix(m_r & 0x1fc, m_o);
}

void cnbaskb_state::write_r(u32 data)
{
	// R9: speaker out
	m_speaker->level_w(data >> 9 & 1);

	// R0,R1: input mux
	// R10 is also tied to K1 (locks up at boot if it's not handled)
	m_inp_mux = (data >> 8 & 4) | (data & 3);

	// R2-R6: led select
	// R7,R8: digit select
	m_r = data;
	update_display();
}

void cnbaskb_state::write_o(u16 data)
{
	// O0-O6: led/digit segment data
	// O7: N/C
	m_o = data;
	update_display();
}

u8 cnbaskb_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(3);
}

// inputs

static INPUT_PORTS_START( cnbaskb )
	PORT_START("IN.0") // R0
	PORT_CONFNAME( 0x01, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" ) // amateur
	PORT_CONFSETTING(    0x01, "2" ) // professional
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY

	PORT_START("IN.2") // R10
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_CUSTOM )
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void cnbaskb_state::cnbaskb(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 375000); // approximation - RC osc. R=39K, C=47pF
	m_maincpu->read_k().set(FUNC(cnbaskb_state::read_k));
	m_maincpu->write_r().set(FUNC(cnbaskb_state::write_r));
	m_maincpu->write_o().set(FUNC(cnbaskb_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 7);
	m_display->set_segmask(0x180, 0x7f);
	m_display->set_bri_levels(0.01, 0.1); // player led is brighter
	config.set_default_layout(layout_cnbaskb);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( cnbaskb )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp0907", 0x0000, 0x0400, CRC(35f84f0f) SHA1(744ca60bb853a2785184042e747530a9e02488f8) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_cnbaskb_output.pla", 0, 365, CRC(b4e28956) SHA1(8356112da71b351420a88d7e394e7d03e429368c) )
ROM_END





/*******************************************************************************

  Conic Electronic Multisport
  * PCB label: CONIC 101-027(1979), or CONIC 101-021 REV A(1980, with DS8871N)
  * TMS1000 MP0168 (die label: 1000B, MP0168)
  * 2 7seg LEDs, 33 other LEDs, 1-bit sound

  This handheld includes 3 games: Basketball, Ice Hockey, Soccer.
  MAME external artwork is needed for the switchable overlays.

  known releases:
  - Hong Kong: Electronic Multisport, published by Conic
  - Hong Kong: Basketball/Ice Hockey/Soccer, published by Conic (3 separate handhelds)
  - USA(1): Electronic Multisport, published by Innocron
  - USA(2): Sports Arena, published by Tandy (model 60-2158)

*******************************************************************************/

class cmsport_state : public hh_tms1k_state
{
public:
	cmsport_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void cmsport(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void cmsport_state::update_display()
{
	m_display->matrix(m_r & ~0x80, m_o);
}

void cmsport_state::write_r(u32 data)
{
	// R7: speaker out
	m_speaker->level_w(data >> 7 & 1);

	// R0,R9,R10: input mux
	m_inp_mux = (data & 1) | (data >> 8 & 6);

	// R0-R4,R8: led select
	// R5,R6: digit select
	m_r = data;
	update_display();
}

void cmsport_state::write_o(u16 data)
{
	// O0-O7: led/digit segment data
	m_o = data;
	update_display();
}

u8 cmsport_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(3);
}

// inputs

static INPUT_PORTS_START( cmsport )
	PORT_START("IN.0") // R0
	PORT_CONFNAME( 0x05, 0x01, "Game Select" )
	PORT_CONFSETTING(    0x01, "Basketball" )
	PORT_CONFSETTING(    0x00, "Soccer" )
	PORT_CONFSETTING(    0x04, "Ice Hockey" )
	PORT_BIT( 0x0a, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY

	PORT_START("IN.2") // R10
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Shoot")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Score")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x08, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" ) // amateur
	PORT_CONFSETTING(    0x08, "2" ) // professional
INPUT_PORTS_END

// config

void cmsport_state::cmsport(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 350000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_k().set(FUNC(cmsport_state::read_k));
	m_maincpu->write_r().set(FUNC(cmsport_state::write_r));
	m_maincpu->write_o().set(FUNC(cmsport_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	m_display->set_segmask(0x60, 0x7f);
	m_display->set_bri_levels(0.01, 0.1); // player led is brighter
	config.set_default_layout(layout_cmsport);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( cmsport )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp0168.u1", 0x0000, 0x0400, CRC(0712a268) SHA1(bd4e23e5c17b28c52e7e769e44773cc9c8839bed) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_cmsport_output.pla", 0, 365, CRC(7defa140) SHA1(477e3cb55e79938d6acaa911e410f6dcb974c218) )
ROM_END





/*******************************************************************************

  Conic Electronic Football
  * TMS1000 MP0170 (die label: 1000B, MP0170)
  * DS8874N, 3*9 LED array, 7 7seg LEDs, 1-bit sound

  This is a clone of Mattel Football. Apparently Mattel tried to keep imports
  of infringing games from going through customs. Conic (Hong Kong) answered
  by distributing the game under subsidiary brands - see list below.

  known releases:
  - Hong Kong: Electronic Football, published by Conic
  - USA(1): Football, published by E.R.S.(Electronic Readout Systems)
  - USA(2): Football, published by ELECsonic
  - USA(3): Football, no brand!

  Another hardware revision of this game uses a PIC16 MCU.

*******************************************************************************/

class cnfball_state : public hh_tms1k_state
{
public:
	cnfball_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag),
		m_ds8874(*this, "ds8874")
	{ }

	void cnfball(machine_config &config);

private:
	required_device<ds8874_device> m_ds8874;

	void ds8874_output_w(u16 data);

	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void cnfball_state::update_display()
{
	m_display->matrix(~m_grid, m_o | (m_r << 6 & 0x700));
}

void cnfball_state::ds8874_output_w(u16 data)
{
	m_grid = data;
	update_display();
}

void cnfball_state::write_r(u32 data)
{
	// R5,R8: N/C
	// R6,R7: speaker out
	m_speaker->level_w(data >> 6 & 3);

	// R9,R10: input mux
	m_inp_mux = data >> 9 & 3;

	// R0: DS8874N CP (note: it goes back to K8 too, game relies on it)
	// R1: DS8874N _DATA
	m_ds8874->cp_w(BIT(data, 0));
	m_ds8874->data_w(BIT(data, 1));

	// R2-R4: led data
	m_r = data;
	update_display();
}

void cnfball_state::write_o(u16 data)
{
	// O0-O7: digit segments
	m_o = data;
	update_display();
}

u8 cnfball_state::read_k()
{
	// K: multiplexed inputs, K8 also tied to DS8874N CP(R0)
	return read_inputs(2) | (m_r << 3 & 8);
}

// inputs

static INPUT_PORTS_START( cnfball )
	PORT_START("IN.0") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Forward")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R10
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Kick")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_NAME("Score")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Status")
	PORT_CONFNAME( 0x08, 0x08, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x08, "1" ) // college
	PORT_CONFSETTING(    0x00, "2" ) // professional
INPUT_PORTS_END

// config

void cnfball_state::cnfball(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 350000); // approximation - RC osc. R=39K, C=47pF
	m_maincpu->read_k().set(FUNC(cnfball_state::read_k));
	m_maincpu->write_r().set(FUNC(cnfball_state::write_r));
	m_maincpu->write_o().set(FUNC(cnfball_state::write_o));

	// video hardware
	DS8874(config, m_ds8874).write_output().set(FUNC(cnfball_state::ds8874_output_w));
	PWM_DISPLAY(config, m_display).set_size(9, 8+3);
	m_display->set_segmask(0xc3, 0x7f);
	m_display->set_segmask(0x38, 0xff); // only the middle 3 7segs have DP
	m_display->set_bri_levels(0.01, 0.1); // player led is brighter
	config.set_default_layout(layout_cnfball);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[4] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( cnfball )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp0170", 0x0000, 0x0400, CRC(50e8a44f) SHA1(fea6ae03c4ef329d825f8688e6854df15023d47e) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_cnfball_output.pla", 0, 365, CRC(0af52f64) SHA1(b4cf450e4d895eddb67448aa69e4f18a5a84e033) )
ROM_END





/*******************************************************************************

  Conic Electronic Football II
  * TMS1100 MP1181 (no decap)
  * 9-digit LED grid, 1-bit sound

  This is a clone of Coleco's Quarterback, similar at hardware-level too.
  Unlike the other LED Football games, this one looks like it doesn't make
  the offense(player) leds brighter.

  known releases:
  - Hong Kong: Electronic Football II, published by Conic
  - USA: Electronic Football II (model 60-2169), published by Tandy

*******************************************************************************/

class cnfball2_state : public hh_tms1k_state
{
public:
	cnfball2_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void cnfball2(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void cnfball2_state::update_display()
{
	// R1 selects between segments B/C or A'/D'
	u16 seg = m_o;
	if (~m_r & 2)
		seg = (m_o << 7 & 0x300) | (m_o & 0xf9);

	m_display->matrix(m_r >> 2 & 0x1ff, seg);
}

void cnfball2_state::write_r(u32 data)
{
	// R0: speaker out
	m_speaker->level_w(data & 1);

	// R8-R10: input mux
	m_inp_mux = data >> 8 & 7;

	// R1-R10: select digit/segment
	m_r = data;
	update_display();
}

void cnfball2_state::write_o(u16 data)
{
	// O0-O7: digit segments
	m_o = data;
	update_display();
}

u8 cnfball2_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(3);
}

// inputs

static INPUT_PORTS_START( cnfball2 )
	PORT_START("IN.0") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x02, 0x00, "Factory Test" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x02, DEF_STR( On ) )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_TOGGLE PORT_NAME("Play Selector") // pass/run
	PORT_CONFNAME( 0x08, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" ) // college
	PORT_CONFSETTING(    0x08, "2" ) // professional

	PORT_START("IN.1") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY

	PORT_START("IN.2") // R10
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Kick/Pass")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_NAME("Score")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Status")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void cnfball2_state::cnfball2(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 325000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_k().set(FUNC(cnfball2_state::read_k));
	m_maincpu->write_r().set(FUNC(cnfball2_state::write_r));
	m_maincpu->write_o().set(FUNC(cnfball2_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 11);
	m_display->set_segmask(0x1ff, 0xff);
	config.set_default_layout(layout_cnfball2);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( cnfball2 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1181", 0x0000, 0x0800, CRC(4553a840) SHA1(2e1132c9bc51641f77ba7f2430b5a3b2766b3a3d) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, BAD_DUMP CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) ) // not verified
	ROM_REGION( 365, "maincpu:opla", ROMREGION_ERASE00 )
	ROM_LOAD( "tms1100_cnfball2_output.pla", 0, 365, NO_DUMP )

	ROM_REGION16_LE( 0x40, "maincpu:opla_b", ROMREGION_ERASE00 ) // verified, electronic dump, 2nd half unused
	ROM_LOAD16_BYTE( "tms1100_cnfball2_output.bin", 0, 0x20, CRC(c51d7404) SHA1(3a00c69b52b7d0dd12ccd66428130592c7e06240) )
ROM_END





/*******************************************************************************

  Conic Electronic I.Q.
  * PCB labels: main: CONIC 101-037 (other side: HG-15, 11*00198*00),
    button PCB: CONIC 102-001, led PCB: CONIC 100-003 REV A itac
  * TMS1000NLL MP0908 (die label: 1000B, MP0908)
  * 2 7seg LEDs, 30 other LEDs, 1-bit sound

  This is a peg solitaire game, with random start position.

  known releases:
  - Hong Kong: Electronic I.Q., published by Conic
  - UK: Solitaire, published by Grandstand

*******************************************************************************/

class eleciq_state : public hh_tms1k_state
{
public:
	eleciq_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void eleciq(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void eleciq_state::update_display()
{
	m_display->matrix(m_r & ~1, m_o);
}

void eleciq_state::write_r(u32 data)
{
	// R0: speaker out
	m_speaker->level_w(data & 1);

	// R1-R6,R9: input mux
	m_inp_mux = (data >> 1 & 0x3f) | (data >> 3 & 0x40);

	// R1-R6: led select
	// R7,R8: digit select
	m_r = data;
	update_display();
}

void eleciq_state::write_o(u16 data)
{
	// O0-O6: led/digit segment data
	// O7: N/C
	m_o = data;
	update_display();
}

u8 eleciq_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(7);
}

// inputs

static INPUT_PORTS_START( eleciq )
	PORT_START("IN.0") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Button A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("Button 1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_HOME) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Up-Left")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_UP) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Up")

	PORT_START("IN.1") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_NAME("Button B")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("Button 2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PGUP) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("Up-Right")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DOWN) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Down")

	PORT_START("IN.2") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("Button C")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("Button 3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_END) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Down-Left")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Left")

	PORT_START("IN.3") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("Button D")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_NAME("Button 4")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PGDN) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Down-Right")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_RIGHT) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Right")

	PORT_START("IN.4") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("Button E")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_NAME("Button 5")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("Button F")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // R9
	PORT_CONFNAME( 0x01, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" ) // amateur
	PORT_CONFSETTING(    0x01, "2" ) // professional
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("RESET")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("Reset") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::reset_button), 0)
INPUT_PORTS_END

// config

void eleciq_state::eleciq(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 325000); // approximation - RC osc. R=47K, C=50pF
	m_maincpu->read_k().set(FUNC(eleciq_state::read_k));
	m_maincpu->write_r().set(FUNC(eleciq_state::write_r));
	m_maincpu->write_o().set(FUNC(eleciq_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 7);
	m_display->set_segmask(0x180, 0x7f);
	config.set_default_layout(layout_eleciq);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( eleciq )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp0908", 0x0000, 0x0400, CRC(db59b82c) SHA1(c9a6bcba208969560495ad9f8775f53de16a69c3) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_eleciq_output.pla", 0, 365, CRC(b8e04232) SHA1(22eed6d9b1fb1e5c9974ea3df16cda71a39aad57) )
ROM_END





/*******************************************************************************

  Electroplay Quickfire
  * TMS1000NLL MP3260 (die label: 1000C, MP3260)
  * 2 7seg LEDs, 5 lamps, 1-bit sound
  * 3 lightsensors, lightgun

  To play it in MAME, either use the clickable artwork with -mouse, or set
  button 1 to "Z or X or C" and each lightsensor to one of those keys.
  Although the game seems mostly playable without having to use the gun trigger

*******************************************************************************/

class qfire_state : public hh_tms1k_state
{
public:
	qfire_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void qfire(machine_config &config);

private:
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void qfire_state::write_r(u32 data)
{
	// R1,R2,R5: input mux
	m_inp_mux = (data >> 1 & 3) | (data >> 3 & 4);

	// R3,R4,R6-R8: leds (direct)
	m_display->write_row(2, (data >> 3 & 3) | (data >> 4 & 0x1c));

	// R9: speaker out
	m_speaker->level_w(BIT(data, 9));
}

void qfire_state::write_o(u16 data)
{
	// O0: 1st digit "1"
	// O1-O7: 2nd digit segments
	m_display->write_row(0, (data & 1) ? 6 : 0);
	m_display->write_row(1, data >> 1 & 0x7f);
}

u8 qfire_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(3);
}

// inputs

static INPUT_PORTS_START( qfire )
	PORT_START("IN.0") // R1
	PORT_CONFNAME( 0x0f, 0x00, "Game" )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x08, "2" )
	PORT_CONFSETTING(    0x04, "3" )
	PORT_CONFSETTING(    0x02, "4" )
	PORT_CONFSETTING(    0x01, "5" )
	PORT_CONFSETTING(    0x06, "6" )

	PORT_START("IN.1") // R2
	PORT_CONFNAME( 0x07, 0x04, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x04, "A" )
	PORT_CONFSETTING(    0x02, "B" )
	PORT_CONFSETTING(    0x01, "C" )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Lightsensor 1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Lightsensor 2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Lightsensor 3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) // lightgun trigger, also turns on lightgun lamp
INPUT_PORTS_END

// config

void qfire_state::qfire(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 375000); // approximation - RC osc. R=39K, C=47pF
	m_maincpu->read_k().set(FUNC(qfire_state::read_k));
	m_maincpu->write_r().set(FUNC(qfire_state::write_r));
	m_maincpu->write_o().set(FUNC(qfire_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(3, 7);
	m_display->set_segmask(3, 0x7f);
	config.set_default_layout(layout_qfire);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( qfire )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp3260", 0x0000, 0x0400, CRC(f6e28376) SHA1(6129584c55a1629b458694cdc97edccb77ab00ba) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common3_micro.pla", 0, 867, CRC(52f7c1f1) SHA1(dbc2634dcb98eac173ad0209df487cad413d08a5) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_qfire_output.pla", 0, 365, CRC(8f7668a9) SHA1(c8faeff0f88bfea8f032ce5bc583f049e8930c11) )
ROM_END





/*******************************************************************************

  Entex (Electronic) Soccer
  * TMS1000NL MP0158 (die label: 1000B, MP0158)
  * 2 7seg LEDs, 30 other LEDs, 1-bit sound

  known releases:
  - USA: Electronic Soccer, 2 versions (leds on green bezel, or leds under bezel)
  - Germany: Fussball, with skill switch

*******************************************************************************/

class esoccer_state : public hh_tms1k_state
{
public:
	esoccer_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void esoccer(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void esoccer_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void esoccer_state::write_r(u32 data)
{
	// R0-R2: input mux
	m_inp_mux = data & 7;

	// R10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// R0-R7: led select
	// R8,R9: digit select
	m_r = data;
	update_display();
}

void esoccer_state::write_o(u16 data)
{
	// O0-O6: led state
	m_o = data;
	update_display();
}

u8 esoccer_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(3);
}

// inputs

static INPUT_PORTS_START( esoccer )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL PORT_16WAY

	PORT_START("IN.2") // R2
	PORT_CONFNAME( 0x03, 0x01, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x01, "1" ) // Auto
	PORT_CONFSETTING(    0x02, "2" ) // Manual
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL
INPUT_PORTS_END

// config

void esoccer_state::esoccer(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 475000); // approximation - RC osc. R=47K, C=33pF
	m_maincpu->read_k().set(FUNC(esoccer_state::read_k));
	m_maincpu->write_r().set(FUNC(esoccer_state::write_r));
	m_maincpu->write_o().set(FUNC(esoccer_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 7);
	m_display->set_segmask(0x300, 0x7f);
	m_display->set_bri_levels(0.008, 0.08); // player led is brighter
	config.set_default_layout(layout_esoccer);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( esoccer )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp0158.ic1", 0x0000, 0x0400, CRC(ae4581ea) SHA1(5f6881f8247094abf8cffb17f6e6586e94cff38c) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_esoccer_output.pla", 0, 365, CRC(c6eeabbd) SHA1(99d07902126b5a1c1abf43340f30d3390da5fa92) )
ROM_END





/*******************************************************************************

  Entex (Electronic) Baseball (1)
  * TMS1000NLP MP0914 (die label: 1000B, MP0914A)
  * 1 7seg LED, and other LEDs behind bezel, 1-bit sound

  This is a handheld LED baseball game. One player controls the batter, the CPU
  or other player controls the pitcher. Pitcher throw buttons are on a 'joypad'
  obtained from a compartment in the back. Player scores are supposed to be
  written down manually, the game doesn't save scores or innings (this annoyance
  was resolved in the sequel). For more information, refer to the official manual.

  The overlay graphic is known to have 2 versions: one where the field players
  are denoted by words ("left", "center", "short", etc), and an alternate one
  with little guys drawn next to the LEDs.

  led translation table: led LDzz from game PCB = MAME y.x:

    0 = -     10 = 1.2   20 = 4.2   30 = 6.0
    1 = 2.3   11 = 0.4   21 = 4.1   31 = 6.1
    2 = 0.0   12 = 1.5   22 = 4.0   32 = 6.2
    3 = 0.1   13 = 2.2   23 = 4.3   33 = 7.0
    4 = 0.2   14 = 3.3   24 = 5.3   34 = 7.1
    5 = 1.0   15 = 3.2   25 = 5.2
    6 = 1.3   16 = 2.1   26 = 5.1
    7 = 1.1   17 = 3.1   27 = 5.0
    8 = 0.3   18 = 3.0   28 = 7.2
    9 = 1.4   19 = 2.0   29 = 7.3

*******************************************************************************/

class ebball_state : public hh_tms1k_state
{
public:
	ebball_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void ebball(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void ebball_state::update_display()
{
	m_display->matrix(m_r, ~m_o);
}

void ebball_state::write_r(u32 data)
{
	// R1-R5: input mux
	m_inp_mux = data >> 1 & 0x1f;

	// R9: speaker out
	m_speaker->level_w(data >> 9 & 1);

	// R0-R7: led select
	// R8: digit select
	m_r = data;
	update_display();
}

void ebball_state::write_o(u16 data)
{
	// O0-O6: led state
	// O7: N/C
	m_o = data & 0x7f;
	update_display();
}

u8 ebball_state::read_k()
{
	// K: multiplexed inputs (note: K8(Vss row) is always on)
	return m_inputs[5]->read() | read_inputs(5);
}

// inputs

static INPUT_PORTS_START( ebball )
	PORT_START("IN.0") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 Change Up")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START ) PORT_NAME("Change Sides")
	PORT_CONFNAME( 0x04, 0x04, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x04, "1" ) // Auto
	PORT_CONFSETTING(    0x00, "2" ) // Manual
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 Fast Ball")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_PLAYER(2) PORT_NAME("P2 Knuckler")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("P2 Curve")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("P2 Slider")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // Vss
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Batter")
INPUT_PORTS_END

// config

void ebball_state::ebball(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 375000); // approximation - RC osc. R=43K, C=47pF
	m_maincpu->read_k().set(FUNC(ebball_state::read_k));
	m_maincpu->write_r().set(FUNC(ebball_state::write_r));
	m_maincpu->write_o().set(FUNC(ebball_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 7);
	m_display->set_segmask(0x100, 0x7f);
	config.set_default_layout(layout_ebball);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( ebball )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp0914", 0x0000, 0x0400, CRC(3c6fb05b) SHA1(b2fe4b3ca72d6b4c9bfa84d67f64afdc215e7178) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_ebball_output.pla", 0, 365, CRC(062bf5bb) SHA1(8d73ee35444299595961225528b153e3a5fe66bf) )
ROM_END





/*******************************************************************************

  Entex (Electronic) Baseball 2
  * PCB label: ZENY
  * TMS1000 MCU, MP0923 (die label same)
  * 3 7seg LEDs, and other LEDs behind bezel, 1-bit sound

  The Japanese version was published by Gakken, black case instead of white.

  The sequel to Entex Baseball, this version keeps up with score and innings.
  As its predecessor, the pitcher controls are on a separate joypad.

  led translation table: led zz from game PCB = MAME y.x:

    00 = -     10 = 9.4   20 = 7.4   30 = 5.0
    01 = 5.3   11 = 9.3   21 = 7.5   31 = 5.1
    02 = 0.7   12 = 9.2   22 = 8.0   32 = 5.2
    03 = 1.7   13 = 6.2   23 = 8.1   33 = 4.0
    04 = 2.7   14 = 7.0   24 = 8.2   34 = 4.1
    05 = 9.7   15 = 7.1   25 = 8.3   35 = 3.1
    06 = 9.0   16 = 6.1   26 = 8.4   36 = 3.0
    07 = 9.5   17 = 7.2   27 = 8.5   37 = 3.3
    08 = 6.3   18 = 7.3   28 = 4.2   38 = 3.2
    09 = 9.1   19 = 6.0   29 = 4.3

*******************************************************************************/

class ebball2_state : public hh_tms1k_state
{
public:
	ebball2_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void ebball2(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void ebball2_state::update_display()
{
	m_display->matrix(m_r ^ 0x7f, ~m_o);
}

void ebball2_state::write_r(u32 data)
{
	// R3-R6: input mux
	m_inp_mux = data >> 3 & 0xf;

	// R10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// R0-R2: digit select
	// R3-R9: led select
	m_r = data;
	update_display();
}

void ebball2_state::write_o(u16 data)
{
	// O0-O7: led state
	m_o = data;
	update_display();
}

u8 ebball2_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(4);
}

// inputs

static INPUT_PORTS_START( ebball2 )
	PORT_START("IN.0") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x02, 0x02, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x02, "1" ) // Auto
	PORT_CONFSETTING(    0x00, "2" ) // Manual
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 Fast Ball")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Batter")

	PORT_START("IN.1") // R4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Steal")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 Change Up")
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // R5
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("P2 Slider")
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_PLAYER(2) PORT_NAME("P2 Knuckler")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("P2 Curve")
	PORT_BIT( 0x0a, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void ebball2_state::ebball2(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 350000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_k().set(FUNC(ebball2_state::read_k));
	m_maincpu->write_r().set(FUNC(ebball2_state::write_r));
	m_maincpu->write_o().set(FUNC(ebball2_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 8);
	m_display->set_segmask(7, 0x7f);
	config.set_default_layout(layout_ebball2);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( ebball2 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp0923", 0x0000, 0x0400, CRC(077acfe2) SHA1(a294ce7614b2cdb01c754a7a50d60d807e3f0939) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_ebball2_output.pla", 0, 365, CRC(adcd73d1) SHA1(d69e590d288ef99293d86716498f3971528e30de) )
ROM_END





/*******************************************************************************

  Entex (Electronic) Baseball 3
  * PCB label: ZENY
  * TMS1100NLL 6007 MP1204 (rev. E!) (die label: MP1204)
  * 2*SN75492N LED display driver
  * 4 7seg LEDs, and other LEDs behind bezel, 1-bit sound

  This is another improvement over Entex Baseball, where gameplay is a bit more
  varied. Like the others, the pitcher controls are on a separate joypad.

  led translation table: led zz from game PCB = MAME y.x:
  note: unlabeled panel leds are listed here as Sz, Bz, Oz, Iz, z left-to-right

    00 = -     10 = 7.5   20 = 7.2
    01 = 6.0   11 = 6.5   21 = 8.4
    02 = 6.1   12 = 5.5   22 = 8.5
    03 = 6.2   13 = 5.2   23 = 9.0
    04 = 6.3   14 = 8.0   24 = 9.2
    05 = 7.3   15 = 8.1   25 = 9.1
    06 = 5.3   16 = 5.1   26 = 9.3
    07 = 7.4   17 = 8.2   27 = 9.5
    08 = 6.4   18 = 8.3   28 = 9.4
    09 = 5.4   19 = 5.0

    S1,S2: 4.0,4.1
    B1-B3: 3.0-3.2
    O1,O2: 4.2,4.3
    I1-I6: 2.0-2.5, I7-I9: 3.3-3.5

*******************************************************************************/

class ebball3_state : public hh_tms1k_state
{
public:
	ebball3_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void ebball3(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(skill_switch);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

INPUT_CHANGED_MEMBER(ebball3_state::skill_switch)
{
	// MCU clock is from an RC circuit(R=47K, C=33pF) oscillating by default at ~340kHz,
	// but on PRO, the difficulty switch adds an extra 150K resistor to Vdd to speed
	// it up to around ~440kHz.
	m_maincpu->set_unscaled_clock((newval & 1) ? 440000 : 340000);
}

void ebball3_state::update_display()
{
	m_display->matrix_partial(0, 10, m_r, m_o);

	// R0,R1 are normal 7segs
	// R4,R7 contain segments(only F and B) for the two other digits
	m_display->write_row(10, (m_display->read_row(4) & 0x20) | (m_display->read_row(7) & 0x02));
	m_display->write_row(11, ((m_display->read_row(4) & 0x10) | (m_display->read_row(7) & 0x01)) << 1);
}

void ebball3_state::write_r(u32 data)
{
	// R0-R2: input mux
	m_inp_mux = data & 7;

	// R10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// R0,R1, digit select
	// R2-R9: led select
	m_r = data;
	update_display();
}

void ebball3_state::write_o(u16 data)
{
	// O0-O6: led state
	// O7: N/C
	m_o = data & 0x7f;
	update_display();
}

u8 ebball3_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(3);
}

// inputs

/* physical button layout and labels are like this:

    main device (batter side):            remote pitcher:

                          MAN
    PRO                    |              [FAST BALL]  [CHANGE UP]    [CURVE]  [SLIDER]
     |                  OFF|
     o                     o                   [STEAL DEFENSE]           [KNUCKLER]
    AM                    AUTO

    [BUNT]  [BATTER]  [STEAL]
*/

static INPUT_PORTS_START( ebball3 )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 Fast Ball")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 Change Up")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("P2 Slider")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("P2 Curve")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_PLAYER(2) PORT_NAME("P2 Knuckler")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P1 Steal")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Batter")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_PLAYER(2) PORT_NAME("P2 Steal Defense")

	PORT_START("IN.2") // R2
	PORT_CONFNAME( 0x01, 0x01, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x01, "1" ) // AUTO
	PORT_CONFSETTING(    0x00, "2" ) // MAN
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Bunt")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("CPU") // fake
	PORT_CONFNAME( 0x01, 0x00, DEF_STR( Difficulty ) ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ebball3_state::skill_switch), 0)
	PORT_CONFSETTING(    0x00, "1" ) // AM
	PORT_CONFSETTING(    0x01, "2" ) // PRO
INPUT_PORTS_END

// config

void ebball3_state::ebball3(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 340000); // see skill_switch
	m_maincpu->read_k().set(FUNC(ebball3_state::read_k));
	m_maincpu->write_r().set(FUNC(ebball3_state::write_r));
	m_maincpu->write_o().set(FUNC(ebball3_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10+2, 7);
	m_display->set_segmask(3, 0x7f);
	m_display->set_segmask(0xc00, 0x22);
	config.set_default_layout(layout_ebball3);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( ebball3 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "6007_mp1204", 0x0000, 0x0800, CRC(987a29ba) SHA1(9481ae244152187d85349d1a08e439e798182938) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_ebball3_output.pla", 0, 365, CRC(00db663b) SHA1(6eae12503364cfb1f863df0e57970d3e766ec165) )
ROM_END





/*******************************************************************************

  Entex Space Battle
  * TMS1000 EN-6004 MP0920 (die label: 1000B, MP0920)
  * 2 7seg LEDs, and other LEDs behind bezel, 1-bit sound

  The Japanese version was published by Gakken, same name.

  led translation table: led zz from game PCB = MAME y.x:

    0 = -     10 = 1.1   20 = 2.3   30 = 5.2
    1 = 0.0   11 = 1.2   21 = 3.0   31 = 5.3
    2 = 0.1   12 = 1.3   22 = 3.1   32 = 6.0
    3 = 0.2   13 = 1.4   23 = 3.2   33 = 6.1
    4 = 0.3   14 = 1.5   24 = 3.3   34 = 6.2
    5 = 0.4   15 = 1.6   25 = 4.0   35 = 7.0
    6 = 0.5   16 = 1.7   26 = 4.1   36 = 7.1
    7 = 0.6   17 = 2.0   27 = 4.2
    8 = 0.7   18 = 2.1   28 = 5.0
    9 = 1.0   19 = 2.2   29 = 5.1

*******************************************************************************/

class esbattle_state : public hh_tms1k_state
{
public:
	esbattle_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void esbattle(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void esbattle_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void esbattle_state::write_r(u32 data)
{
	// R0,R1: input mux
	m_inp_mux = data & 3;

	// R10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// R0-R7: led select
	// R8,R9: digit select
	m_r = data;
	update_display();
}

void esbattle_state::write_o(u16 data)
{
	// O0-O7: led state
	m_o = data;
	update_display();
}

u8 esbattle_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(2);
}

// inputs

static INPUT_PORTS_START( esbattle )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Fire 1") // F1
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Fire 2") // F2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P1 Launch")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Fire 1") // F1
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_COCKTAIL PORT_NAME("P2 Fire 2") // F2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_COCKTAIL PORT_NAME("P2 Launch")
	PORT_CONFNAME( 0x08, 0x08, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x08, "1" ) // Auto
	PORT_CONFSETTING(    0x00, "2" ) // Manual
INPUT_PORTS_END

// config

void esbattle_state::esbattle(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 450000); // approximation - RC osc. R=47K, C=33pF
	m_maincpu->read_k().set(FUNC(esbattle_state::read_k));
	m_maincpu->write_r().set(FUNC(esbattle_state::write_r));
	m_maincpu->write_o().set(FUNC(esbattle_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 8);
	m_display->set_segmask(0x300, 0x7f);
	config.set_default_layout(layout_esbattle);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( esbattle )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "en-6004_mp0920", 0x0000, 0x0400, CRC(7460c179) SHA1(be855054b4a98b05b34fd931d5c247c5c0f9b036) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_esbattle_output.pla", 0, 365, CRC(861b45a2) SHA1(a5a9dc9bef8adb761845ad548058b55e970517d3) )
ROM_END





/*******************************************************************************

  Entex Blast It
  * TMS1000 MP0230 (die label: 1000B, MP0230)
  * 3 7seg LEDs, 49 other LEDs (both under an overlay mask), 1-bit sound

*******************************************************************************/

class blastit_state : public hh_tms1k_state
{
public:
	blastit_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void blastit(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void blastit_state::update_display()
{
	m_display->matrix(m_r >> 1, m_o);
}

void blastit_state::write_r(u32 data)
{
	// R3: input mux
	m_inp_mux = data >> 3 & 1;

	// R0: speaker out
	m_speaker->level_w(data & 1);

	// R1-R7: led select
	// R8-R10: digit select
	m_r = data;
	update_display();
}

void blastit_state::write_o(u16 data)
{
	// O0-O6: led state
	m_o = data;
	update_display();
}

u8 blastit_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(1);
}

// inputs

static INPUT_PORTS_START( blastit )
	PORT_START("IN.0") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_CONFNAME( 0x08, 0x08, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x08, "1" ) // AM
	PORT_CONFSETTING(    0x00, "2" ) // PRO
INPUT_PORTS_END

// config

void blastit_state::blastit(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 425000); // approximation - RC osc. R=47K, C=33pF
	m_maincpu->read_k().set(FUNC(blastit_state::read_k));
	m_maincpu->write_r().set(FUNC(blastit_state::write_r));
	m_maincpu->write_o().set(FUNC(blastit_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 7);
	m_display->set_segmask(0x380, 0x7f);
	m_display->set_bri_levels(0.01, 0.115); // ball/paddle is slightly brighter
	config.set_default_layout(layout_blastit);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( blastit )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp0230", 0x0000, 0x0400, CRC(1eb5f473) SHA1(76cd8c0e04368aa2150d428018643e1d0b9adda0) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_blastit_output.pla", 0, 365, CRC(fa8081df) SHA1(99706d5ad58a76d47446576fac18964e602171c8) )
ROM_END





/*******************************************************************************

  Entex Space Invader
  * TMS1100 MP1211 (die label same)
  * 3 7seg LEDs, LED matrix and overlay mask, 1-bit sound

  There are two versions of this game: the first release(this one) is on
  TMS1100, the second more widespread release runs on a COP400. There are
  also differences with the overlay mask.

*******************************************************************************/

class einvader_state : public hh_tms1k_state
{
public:
	einvader_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(skill_switch);
	void einvader(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
};

// handlers

INPUT_CHANGED_MEMBER(einvader_state::skill_switch)
{
	// MCU clock is from an RC circuit(R=47K, C=56pF) oscillating by default at ~320kHz,
	// but on PRO, the difficulty switch adds an extra 180K resistor to Vdd to speed
	// it up to around ~400kHz.
	m_maincpu->set_unscaled_clock((newval & 1) ? 400000 : 320000);
}

void einvader_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void einvader_state::write_r(u32 data)
{
	// R10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// R0-R6: led select
	// R7-R9: digit select
	m_r = data;
	update_display();
}

void einvader_state::write_o(u16 data)
{
	// O0-O7: led state
	m_o = data;
	update_display();
}

// inputs

static INPUT_PORTS_START( einvader )
	PORT_START("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_CONFNAME( 0x08, 0x00, DEF_STR( Difficulty ) ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(einvader_state::skill_switch), 0)
	PORT_CONFSETTING(    0x00, "1" ) // amateur
	PORT_CONFSETTING(    0x08, "2" ) // professional
INPUT_PORTS_END

// config

void einvader_state::einvader(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 320000); // see skill_switch
	m_maincpu->read_k().set_ioport("IN.0");
	m_maincpu->write_r().set(FUNC(einvader_state::write_r));
	m_maincpu->write_o().set(FUNC(einvader_state::write_o));

	// video hardware
	screen_device &mask(SCREEN(config, "mask", SCREEN_TYPE_SVG));
	mask.set_refresh_hz(60);
	mask.set_size(945, 1080);
	mask.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(10, 8);
	m_display->set_segmask(0x380, 0x7f);
	m_display->set_bri_levels(0.01, 0.1); // ufo/player explosion is brighter
	config.set_default_layout(layout_einvader);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( einvader )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1211", 0x0000, 0x0800, CRC(b6efbe8e) SHA1(d7d54921dab22bb0c2956c896a5d5b56b6f64969) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_einvader_output.pla", 0, 365, CRC(490158e1) SHA1(61cace1eb09244663de98d8fb04d9459b19668fd) )

	ROM_REGION( 45196, "mask", 0)
	ROM_LOAD( "einvader.svg", 0, 45196, CRC(35a1c744) SHA1(6beb9767454b9bc8f2ccf9fee25e7be209eefd22) )
ROM_END





/*******************************************************************************

  Entex Color Football 4
  * TMS1670 6009 MP7551 (die label: TMS1400, MP7551, 40H 01D D000 R000)
  * 9-digit cyan VFD, 60 red and green LEDs behind mask, 1-bit sound

  Another version exist, one with a LED(red) 7seg display.

*******************************************************************************/

class efootb4_state : public hh_tms1k_state
{
public:
	efootb4_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void efootb4(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void efootb4_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void efootb4_state::write_r(u32 data)
{
	// R0-R4: input mux
	m_inp_mux = data & 0x1f;

	// R0-R9: led select
	// R10-R15: digit select
	m_r = data;
	update_display();
}

void efootb4_state::write_o(u16 data)
{
	// O7: speaker out
	m_speaker->level_w(data >> 7 & 1);

	// O0-O6: led state
	m_o = data;
	update_display();
}

u8 efootb4_state::read_k()
{
	return read_inputs(5);
}

// inputs

static INPUT_PORTS_START( efootb4 )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY // 1
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY // 2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY // 3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY // 4

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL PORT_16WAY // 1
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL PORT_16WAY // 2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL PORT_16WAY // 3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL PORT_16WAY // 4

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Run")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Pass")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P1 Kick")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Run")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_COCKTAIL PORT_NAME("P2 Pass")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_COCKTAIL PORT_NAME("P2 Kick")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // R4
	PORT_CONFNAME( 0x01, 0x01, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x01, "1" ) // Auto
	PORT_CONFSETTING(    0x00, "2" ) // Manual
	PORT_CONFNAME( 0x02, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" ) // amateur
	PORT_CONFSETTING(    0x02, "2" ) // professional
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START ) PORT_NAME("Status")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void efootb4_state::efootb4(machine_config &config)
{
	// basic machine hardware
	TMS1670(config, m_maincpu, 400000); // approximation - RC osc. R=42K, C=47pF
	m_maincpu->read_k().set(FUNC(efootb4_state::read_k));
	m_maincpu->write_r().set(FUNC(efootb4_state::write_r));
	m_maincpu->write_o().set(FUNC(efootb4_state::write_o));

	// video hardware
	screen_device &mask(SCREEN(config, "mask", SCREEN_TYPE_SVG));
	mask.set_refresh_hz(60);
	mask.set_size(1920, 904);
	mask.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(16, 7);
	m_display->set_segmask(0xfc00, 0x7f);
	config.set_default_layout(layout_efootb4);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( efootb4 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "6009_mp7551", 0x0000, 0x1000, CRC(54fa7244) SHA1(4d16bd825c4a2db76ca8a263c373ade15c20e270) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 557, "maincpu:opla", 0 )
	ROM_LOAD( "tms1400_efootb4_output.pla", 0, 557, CRC(5c87c753) SHA1(bde9d4aa1e57a718affd969475c0a1edcf60f444) )

	ROM_REGION( 67472, "mask", 0)
	ROM_LOAD( "efootb4.svg", 0, 67472, CRC(ba0abcda) SHA1(6066e4cbae5404e4db17fae0153808b044adc823) )
ROM_END





/*******************************************************************************

  Entex Basketball 2
  * TMS1100 6010 MP1218 (die label: 1100B, MP1218)
  * 4 7seg LEDs, and other LEDs behind bezel, 1-bit sound

  led translation table: led zz from game PCB = MAME y.x:

    11 = 9.0   21 = 9.1   31 = 9.2   41 = 9.3   51 = 9.5
    12 = 8.0   22 = 8.1   32 = 8.2   42 = 8.3   52 = 8.5
    13 = 7.0   23 = 7.1   33 = 7.2   43 = 7.3   53 = 8.4
    14 = 6.0   24 = 6.1   34 = 6.2   44 = 6.3   54 = 7.5
    15 = 5.0   25 = 5.1   35 = 5.2   45 = 5.3   55 = 7.4
    16 = 4.0   26 = 4.1   36 = 4.2   46 = 4.3   56 = 6.5

    A  = 9.4
    B  = 6.4

*******************************************************************************/

class ebaskb2_state : public hh_tms1k_state
{
public:
	ebaskb2_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void ebaskb2(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void ebaskb2_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void ebaskb2_state::write_r(u32 data)
{
	// R10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// R6-R9: input mux
	m_inp_mux = data >> 6 & 0xf;

	// R0-R3: digit select
	// R4-R9: led select
	m_r = data;
	update_display();
}

void ebaskb2_state::write_o(u16 data)
{
	// O0-O6: led state
	// O7: N/C
	m_o = data;
	update_display();
}

u8 ebaskb2_state::read_k()
{
	return read_inputs(4);
}

// inputs

static INPUT_PORTS_START( ebaskb2 )
	PORT_START("IN.0") // R6
	PORT_CONFNAME( 0x01, 0x01, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x01, "1" ) // amateur
	PORT_CONFSETTING(    0x00, "2" ) // professional
	PORT_CONFNAME( 0x02, 0x02, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x02, "1" ) // Auto
	PORT_CONFSETTING(    0x00, "2" ) // Manual
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_COCKTAIL PORT_NAME("P2 Shoot")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Pass")

	PORT_START("IN.1") // R7
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Pass")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Shoot")

	PORT_START("IN.2") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL PORT_16WAY

	PORT_START("IN.3") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
INPUT_PORTS_END

// config

void ebaskb2_state::ebaskb2(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 360000); // approximation - RC osc. R=33K, C=82pF
	m_maincpu->read_k().set(FUNC(ebaskb2_state::read_k));
	m_maincpu->write_r().set(FUNC(ebaskb2_state::write_r));
	m_maincpu->write_o().set(FUNC(ebaskb2_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 7);
	m_display->set_segmask(0xf, 0x7f);
	m_display->set_bri_levels(0.01, 0.1); // ball carrier led is brighter
	config.set_default_layout(layout_ebaskb2);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( ebaskb2 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "6010_mp1218", 0x0000, 0x0800, CRC(0089ede8) SHA1(c8a79d5aca7e37b637a4d152150acba9f41aad96) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_ebaskb2_output.pla", 0, 365, CRC(c18103ae) SHA1(5a9bb8e1d95a9f6919b05ff9471fa0a8014b8b81) )
ROM_END





/*******************************************************************************

  Entex Raise The Devil
  * TMS1100 MP1221 (die label: 1100B, MP1221)
  * 4 7seg LEDs(rightmost one unused), and other LEDs behind bezel, 1-bit sound

  Entex Black Knight (licensed handheld version of Williams' pinball game)
  * TMS1100 MP1296 (no decap)
  * same hardware as Raise The Devil

  raisedvl led translation table: led zz from game PCB = MAME y.x:
  (no led labels on ebknight PCB)

    0 = -     10 = 4.4   20 = 5.3   30 = 9.5   40 = 9.2
    1 = 3.0   11 = 4.5   21 = 5.4   31 = 8.5   41 = 9.3
    2 = 3.1   12 = -     22 = -     32 = 6.5   42 = 9.0
    3 = 3.2   13 = 5.0   23 = 6.0   33 = 7.4   43 = 9.1
    4 = 3.3   14 = 5.1   24 = 6.1   34 = 7.0
    5 = 3.4   15 = 5.2   25 = 6.2   35 = 7.1
    6 = 4.0   16 = -     26 = 6.3   36 = 8.0
    7 = 4.1   17 = 7.2   27 = 6.4   37 = 8.1
    8 = 4.2   18 = 7.3   28 = 8.4   38 = 8.2
    9 = 4.3   19 = -     29 = 9.4   39 = 8.3

  NOTE!: MAME external artwork is required

*******************************************************************************/

class raisedvl_state : public hh_tms1k_state
{
public:
	raisedvl_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void raisedvl(machine_config &config);
	void ebknight(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(skill_switch);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

INPUT_CHANGED_MEMBER(raisedvl_state::skill_switch)
{
	// MCU clock is from an RC circuit with C=47pF, R=47K by default. Skills
	// 2 and 3 add a 150K resistor in parallel, and skill 4 adds a 100K one.
	// 0:   R=47K  -> ~350kHz
	// 2,3: R=35K8 -> ~425kHz (combined)
	// 4:   R=32K  -> ~465kHz (combined)
	static const u32 freq[3] = { 350000, 425000, 465000 };
	m_maincpu->set_unscaled_clock(freq[(newval >> 4) % 3]);
}

void raisedvl_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void raisedvl_state::write_r(u32 data)
{
	// R10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// R0,R1: input mux
	m_inp_mux = data & 3;

	// R0-R2: digit select
	// R3-R9: led select
	m_r = data;
	update_display();
}

void raisedvl_state::write_o(u16 data)
{
	// O0-O6: led state
	// O7: N/C
	m_o = data & 0x7f;
	update_display();
}

u8 raisedvl_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(2) & 0xf;
}

// inputs

static INPUT_PORTS_START( raisedvl )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Shoot")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Left Flipper")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Right Flipper")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R1 (only bit 0)
	PORT_CONFNAME( 0x31, 0x00, DEF_STR( Difficulty ) ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(raisedvl_state::skill_switch), 0)
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x10, "2" )
	PORT_CONFSETTING(    0x11, "3" )
	PORT_CONFSETTING(    0x21, "4" )
INPUT_PORTS_END

// config

void raisedvl_state::raisedvl(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 350000); // see skill_switch
	m_maincpu->read_k().set(FUNC(raisedvl_state::read_k));
	m_maincpu->write_r().set(FUNC(raisedvl_state::write_r));
	m_maincpu->write_o().set(FUNC(raisedvl_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 7);
	m_display->set_segmask(7, 0x7f);
	config.set_default_layout(layout_raisedvl);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

void raisedvl_state::ebknight(machine_config &config)
{
	raisedvl(config);
	config.set_default_layout(layout_ebknight);
}

// roms

ROM_START( raisedvl )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1221", 0x0000, 0x0800, CRC(782791cc) SHA1(214249406fcaf44efc6350022bd534e59ec69c88) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_raisedvl_output.pla", 0, 365, CRC(00db663b) SHA1(6eae12503364cfb1f863df0e57970d3e766ec165) )
ROM_END

ROM_START( ebknight )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1296", 0x0000, 0x0800, CRC(bc57a46a) SHA1(f60843779f49a8bd28291df3390086e54b9e3f40) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, BAD_DUMP CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) ) // not verified, taken from raisedvl
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_ebknight_output.pla", 0, 365, BAD_DUMP CRC(00db663b) SHA1(6eae12503364cfb1f863df0e57970d3e766ec165) ) // "
ROM_END





/*******************************************************************************

  Entex Musical Marvin
  * TMS1100 MP1228 (no decap)
  * 1 7seg LED, 8 other leds, 1-bit sound with volume decay
  * knobs for speed, tone (volume decay), volume (also off/on switch)

  The design patent was assigned to Hanzawa (Japan), so it's probably
  manufactured by them.

*******************************************************************************/

class mmarvin_state : public hh_tms1k_state
{
public:
	mmarvin_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag),
		m_volume(*this, "volume"),
		m_speed_timer(*this, "speed")
	{ }

	void mmarvin(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<filter_volume_device> m_volume;
	required_device<timer_device> m_speed_timer;

	double m_speaker_volume = 0.0;

	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();

	TIMER_DEVICE_CALLBACK_MEMBER(speaker_decay_sim);
};

void mmarvin_state::machine_start()
{
	hh_tms1k_state::machine_start();
	save_item(NAME(m_speaker_volume));
}

// handlers

TIMER_DEVICE_CALLBACK_MEMBER(mmarvin_state::speaker_decay_sim)
{
	m_volume->set_gain(m_speaker_volume);

	// volume decays when speaker is off, decay scale is determined by tone knob
	const double step = (1.01 - 1.003) / 100.0; // approximation
	m_speaker_volume /= 1.01 - (double)(u8)m_inputs[5]->read() * step;
}

void mmarvin_state::update_display()
{
	u8 digit = bitswap<7>(m_o,6,2,1,0,5,4,3); // 7seg is upside-down
	m_display->matrix(m_r, (m_o << 1 & 0x100) | (m_r & 0x80) | digit);
}

void mmarvin_state::write_r(u32 data)
{
	// R2-R5: input mux
	m_inp_mux = data >> 2 & 0xf;

	// R6: trigger speed knob timer
	if (m_r & ~data & 0x40)
	{
		const double step = (2100 - 130) / 100.0; // duration range is around 0.13s to 2.1s
		m_speed_timer->adjust(attotime::from_msec(2100 - (u8)m_inputs[4]->read() * step));
	}

	// R10: speaker out
	m_speaker->level_w(BIT(m_r, 10));

	// R9: trigger speaker on
	if (m_r & ~data & 0x200)
		m_volume->set_gain(m_speaker_volume = 1.0);

	// R0,R1: digit/led select
	// R7: digit DP
	m_r = data;
	update_display();
}

void mmarvin_state::write_o(u16 data)
{
	// O0-O7: led data
	m_o = data;
	update_display();
}

u8 mmarvin_state::read_k()
{
	// K1-K4: multiplexed inputs
	// K8: speed knob state
	return (read_inputs(4) & 7) | (m_speed_timer->enabled() ? 0 : 8);
}

// inputs

static INPUT_PORTS_START( mmarvin )
	PORT_START("IN.0") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_M) PORT_NAME("Mode")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("Button Do 1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("Button Re")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("Button Mi")

	PORT_START("IN.2") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_NAME("Button Fa")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_NAME("Button Sol")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_NAME("Button La")

	PORT_START("IN.3") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_NAME("Button Ti")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_NAME("Button Do 2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Space")

	PORT_START("IN.4")
	PORT_ADJUSTER(50, "Speed Knob")

	PORT_START("IN.5")
	PORT_ADJUSTER(50, "Tone Knob")
INPUT_PORTS_END

// config

void mmarvin_state::mmarvin(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 300000); // approximation - RC osc. R=51K, C=47pF
	m_maincpu->read_k().set(FUNC(mmarvin_state::read_k));
	m_maincpu->write_r().set(FUNC(mmarvin_state::write_r));
	m_maincpu->write_o().set(FUNC(mmarvin_state::write_o));

	TIMER(config, "speed").configure_generic(nullptr);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(2, 9);
	m_display->set_segmask(1, 0xff);
	config.set_default_layout(layout_mmarvin);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "volume", 0.25);
	FILTER_VOLUME(config, m_volume).add_route(ALL_OUTPUTS, "mono", 1.0);

	TIMER(config, "speaker_decay").configure_periodic(FUNC(mmarvin_state::speaker_decay_sim), attotime::from_msec(1));
}

// roms

ROM_START( mmarvin )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1228", 0x0000, 0x0800, CRC(68ce3ab6) SHA1(15f7fb3f438116bf6128f11c981bdc0852d5a4ec) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, BAD_DUMP CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) ) // not verified
	ROM_REGION( 365, "maincpu:opla", ROMREGION_ERASE00 )
	ROM_LOAD( "tms1100_mmarvin_output.pla", 0, 365, NO_DUMP )

	ROM_REGION16_LE( 0x40, "maincpu:opla_b", ROMREGION_ERASE00 ) // verified, electronic dump
	ROM_LOAD16_BYTE( "tms1100_mmarvin_output.bin", 0, 0x20, CRC(a274951f) SHA1(7e91ddd42c132942a32351649eaf1107b5b0285c) )
ROM_END





/*******************************************************************************

  Fonas 2 Player Baseball
  * PCB label: CA-014 (probably Cassia)
  * TMS1000NLL MP0154 (die label: 1000B, MP0154)
  * 4 7seg LEDs, 37 other LEDs, 1-bit sound

  known releases:
  - World: 2 Player Baseball, published by Fonas
  - USA: 2 Player Baseball, published by Sears
  - Canada: 2 Player Baseball, published by Talbot Electronics

  Calfax / Caprice Pro-Action Strategy Baseball is also suspected to be the
  same game, even though it looks a bit different.

  led translation table: led zz from game PCB = MAME y.x:

    0 = -     10 = 2.2   20 = 4.0   30 = 4.4
    1 = 2.3   11 = 3.3   21 = 2.7   31 = 3.7
    2 = 0.4   12 = 1.2   22 = 0.0   32 = 4.3
    3 = 3.2   13 = 2.4   23 = 4.1   33 = 4.6
    4 = 0.5   14 = 1.0   24 = 3.1   34 = 3.5
    5 = 0.3   15 = 2.1   25 = 0.2   35 = 4.5
    6 = 3.4   16 = 1.1   26 = 0.1
    7 = 1.3   17 = 4.7   27 = 4.2
    8 = 1.4   18 = 2.0   28 = 3.0
    9 = 1.7   19 = 0.7   29 = 1.5

*******************************************************************************/

class f2pbball_state : public hh_tms1k_state
{
public:
	f2pbball_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void f2pbball(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void f2pbball_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void f2pbball_state::write_r(u32 data)
{
	// R4,R9,R10: input mux
	m_inp_mux = (data >> 4 & 1) | (data >> 8 & 6);

	// R9,R10(ANDed together): speaker out
	m_speaker->level_w(data >> 10 & data >> 9 & 1);

	// R0-R4: led select
	// R5-R8: digit select
	m_r = data;
	update_display();
}

void f2pbball_state::write_o(u16 data)
{
	// O0-O7: led state
	m_o = bitswap<8>(data,0,7,6,5,4,3,2,1);
	update_display();
}

u8 f2pbball_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(3);
}

// inputs

static INPUT_PORTS_START( f2pbball )
	PORT_START("IN.0") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_COCKTAIL PORT_NAME("P2 Pick Off")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x0c, 0x04, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x04, "1" )
	PORT_CONFSETTING(    0x00, "Practice" ) // middle switch
	PORT_CONFSETTING(    0x08, "2" )

	PORT_START("IN.1") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("P1 Score")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P1 Steal")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Pitch")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Swing")

	PORT_START("IN.2") // R10
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_COCKTAIL PORT_NAME("P2 Curve Left")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_COCKTAIL PORT_NAME("P2 Slow")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Curve Right")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_COCKTAIL PORT_NAME("P2 Fast")

	PORT_START("RESET")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("P1 Reset") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::reset_button), 0)
INPUT_PORTS_END

// config

void f2pbball_state::f2pbball(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 325000); // approximation - RC osc. R=51K, C=39pF
	m_maincpu->read_k().set(FUNC(f2pbball_state::read_k));
	m_maincpu->write_r().set(FUNC(f2pbball_state::write_r));
	m_maincpu->write_o().set(FUNC(f2pbball_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	m_display->set_segmask(0x1e0, 0x7f);
	config.set_default_layout(layout_f2pbball);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( f2pbball )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp0154", 0x0000, 0x0400, CRC(c5b45ace) SHA1(b2de32e83ab447b22d6828f0081843f364040b01) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_f2pbball_output.pla", 0, 365, CRC(30c2f28f) SHA1(db969b22475f37f083c3594f5e4f5759048377b8) )
ROM_END





/*******************************************************************************

  Fonas 3 in 1: Football, Basketball, Soccer
  * PCB label: HP-801
  * TMS1100NLL MP1185
  * 4 7seg LEDs, 40 other LEDs, 1-bit sound

  It's not known if this game has an official title. The current one is
  taken from the handheld front side.

  Calfax / Caprice separate Football / Basketball / Soccer handhelds are
  suspected to be the same ROM.

  MAME external artwork is needed for the switchable overlays.

*******************************************************************************/

class f3in1_state : public hh_tms1k_state
{
public:
	f3in1_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void f3in1(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(skill_switch);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

INPUT_CHANGED_MEMBER(f3in1_state::skill_switch)
{
	// MCU clock is from an RC circuit where C=47pF, R=39K(PROF) or 56K(REG)
	m_maincpu->set_unscaled_clock((newval & 1) ? 400000 : 300000);
}

void f3in1_state::update_display()
{
	m_display->matrix(m_r & ~0x20, m_o);
}

void f3in1_state::write_r(u32 data)
{
	// R0-R2,R4: input mux
	m_inp_mux = (data & 7) | (data >> 1 & 8);

	// R10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// R0-R4: led select
	// R6-R9: digit select
	m_r = data;
	update_display();
}

void f3in1_state::write_o(u16 data)
{
	// O0-O7: led state
	m_o = data;
	update_display();
}

u8 f3in1_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(4);
}

// inputs

static INPUT_PORTS_START( f3in1 )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P Button")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("K Button")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("D Button")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x0e, 0x02, "Game Select" )
	PORT_CONFSETTING(    0x02, "Football" )
	PORT_CONFSETTING(    0x04, "Basketball" )
	PORT_CONFSETTING(    0x08, "Soccer" )

	PORT_START("CPU") // fake
	PORT_CONFNAME( 0x01, 0x00, DEF_STR( Difficulty ) ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(f3in1_state::skill_switch), 0)
	PORT_CONFSETTING(    0x00, "1" ) // REG
	PORT_CONFSETTING(    0x01, "2" ) // PROF
INPUT_PORTS_END

// config

void f3in1_state::f3in1(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 300000); // see skill_switch
	m_maincpu->read_k().set(FUNC(f3in1_state::read_k));
	m_maincpu->write_r().set(FUNC(f3in1_state::write_r));
	m_maincpu->write_o().set(FUNC(f3in1_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 8);
	m_display->set_segmask(0x3c0, 0x7f);
	m_display->set_bri_levels(0.003, 0.05); // player led is brighter
	config.set_default_layout(layout_f3in1);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( f3in1 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1185", 0x0000, 0x0800, CRC(53f7b28d) SHA1(2249890e3a259095193b4331ca88c29ccd81eefe) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_f3in1_output.pla", 0, 365, CRC(51d947bc) SHA1(f766397d84f038be96e83d40989195c98ddcb1d9) )
ROM_END





/*******************************************************************************

  Gakken Poker
  * PCB label: POKER. gakken
  * TMS1370 MP2105 (die label: 1170, MP2105)
  * 11-digit cyan VFD Itron FG1114B, oscillator sound

  known releases:
  - Japan: Poker, published by Gakken
  - USA: Electronic Poker, published by Entex

*******************************************************************************/

class gpoker_state : public hh_tms1k_state
{
public:
	gpoker_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag),
		m_beeper(*this, "beeper")
	{ }

	void gpoker(machine_config &config);

protected:
	required_device<beep_device> m_beeper;

	void update_display();
	virtual void write_r(u32 data);
	virtual void write_o(u16 data);
	virtual u8 read_k();
};

// handlers

void gpoker_state::update_display()
{
	u16 segs = bitswap<16>(m_o, 15,14,7,12,11,10,9,8,6,6,5,4,3,2,1,0) & 0x20ff;
	m_display->matrix(m_r & 0x7ff, segs | (m_r >> 3 & 0xf00));
}

void gpoker_state::write_r(u32 data)
{
	// R15: enable beeper
	m_beeper->set_state(data >> 15 & 1);

	// R0-R6: input mux
	m_inp_mux = data & 0x7f;

	// R0-R10: select digit
	// R11-R14: card symbols
	m_r = data;
	update_display();
}

void gpoker_state::write_o(u16 data)
{
	// O0-O7: digit segments A-G,H
	m_o = data;
	update_display();
}

u8 gpoker_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(7);
}

// inputs

/* physical button layout and labels are like this:

    [7]   [8]   [9]   [DL]   | (on/off switch)
    [4]   [5]   [6]   [BT]
    [1]   [2]   [3]   [CA]  [CE]
    [0]         [GO]  [T]   [AC]
*/

static INPUT_PORTS_START( gpoker )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Go")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x02, 0x02, IPT_CUSTOM ) PORT_CONDITION("FAKE", 0x03, NOTEQUALS, 0x00) // 9/DL
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Clear Entry") // CE

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x06, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_NAME("Clear All") // AC

	PORT_START("IN.6") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("Call") // CA
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Total") // T
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_NAME("Bet") // BT

	PORT_START("FAKE") // 9/DL are electronically the same button
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("Deal") // DL
INPUT_PORTS_END

// config

void gpoker_state::gpoker(machine_config &config)
{
	// basic machine hardware
	TMS1370(config, m_maincpu, 375000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_k().set(FUNC(gpoker_state::read_k));
	m_maincpu->write_r().set(FUNC(gpoker_state::write_r));
	m_maincpu->write_o().set(FUNC(gpoker_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(11, 14);
	m_display->set_segmask(0x7ff, 0x20ff); // 7seg + bottom-right diagonal
	config.set_default_layout(layout_gpoker);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 2400); // astable multivibrator - C1 and C2 are 0.003uF, R1 and R4 are 1K, R2 and R3 are 100K
	m_beeper->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( gpoker )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp2105", 0x0000, 0x0800, CRC(95a8f5b4) SHA1(d14f00ba9f57e437264d972baa14a14a28ff8719) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_gpoker_output.pla", 0, 365, CRC(f7e2d812) SHA1(cc3abd89afb1d2145dc47636553ccd0ba7de70d9) )
ROM_END





/*******************************************************************************

  Gakken Jackpot: Gin Rummy & Black Jack
  * PCB label: gakken
  * TMS1670 MPF553 (die label: TMS1400, MPF553, 40H 01D D000 R000)
  * 11-digit cyan VFD Itron FG1114B, oscillator sound

  known releases:
  - Japan: Jackpot(?), published by Gakken
  - USA: Electronic Jackpot: Gin Rummy & Black Jack, published by Entex

*******************************************************************************/

class gjackpot_state : public gpoker_state
{
public:
	gjackpot_state(const machine_config &mconfig, device_type type, const char *tag) :
		gpoker_state(mconfig, type, tag)
	{ }

	void gjackpot(machine_config &config);

private:
	virtual void write_r(u32 data) override;
};

// handlers

void gjackpot_state::write_r(u32 data)
{
	// same as gpoker, only input mux msb is R10 instead of R6
	gpoker_state::write_r(data);
	m_inp_mux = (data & 0x3f) | (data >> 4 & 0x40);
}

// inputs

/* physical button layout and labels are like this:
  (note: on dual-function buttons, upper label=Gin, lower label=Black Jack)

                       BJ --o GIN
                          OFF
    [7]    [8]    [9]    [DL]

    [4]    [5]    [6]    [      [KN
                          DB]    SP]
    [1]    [2]    [3]    [DS]   [DR]
                          BT]    HT]
    [10/0] [T]    [MD    [CH    [AC]
                   GO]    ST]
*/

static INPUT_PORTS_START( gjackpot )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("10/0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_H) PORT_NAME("Draw/Hit") // DR/HT
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("Deal") // DL

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_K) PORT_NAME("Knock/Split") // KN/SP
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Total") // T

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_NAME("Display/Bet") // DS/BT
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_M) PORT_NAME("Meld/Go") // MD/GO
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME("Double") // DB

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("Change/Stand") // CH/ST
	PORT_BIT( 0x0a, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Clear Entry") // CE
	PORT_BIT( 0x0a, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // R10
	PORT_CONFNAME( 0x06, 0x04, "Game Select" )
	PORT_CONFSETTING(    0x04, "Gin Rummy" )
	PORT_CONFSETTING(    0x02, "Black Jack" )
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void gjackpot_state::gjackpot(machine_config &config)
{
	gpoker(config);

	// basic machine hardware
	TMS1670(config.replace(), m_maincpu, 375000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_k().set(FUNC(gjackpot_state::read_k));
	m_maincpu->write_r().set(FUNC(gjackpot_state::write_r));
	m_maincpu->write_o().set(FUNC(gjackpot_state::write_o));

	config.set_default_layout(layout_gjackpot);
}

// roms

ROM_START( gjackpot )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mpf553", 0x0000, 0x1000, CRC(f45fd008) SHA1(8d5d6407a8a031a833ceedfb931f5c9d2725ecd0) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 557, "maincpu:opla", 0 )
	ROM_LOAD( "tms1400_gjackpot_output.pla", 0, 557, CRC(50e471a7) SHA1(9d862cb9f51a563882b62662c5bfe61b52e3df00) )
ROM_END





/*******************************************************************************

  Gakken Invader
  * PCB label: GAKKEN, INVADER, KS-00779
  * TMS1370 MP2110 (die label: 1370, MP2110)
  * cyan VFD Itron? CP5008A, 1-bit sound

  known releases:
  - World: Invader, published by Gakken
  - USA(1): Galaxy Invader, published by CGL
  - USA(2): Fire Away, published by Tandy
  - USA(3): Electron Blaster, published by Vanity Fair

  On the real thing, the joystick is "sticky"(it doesn't autocenter when you let go).
  There's also a version with a cyan/red VFD, possibly the same ROM.

*******************************************************************************/

class ginv_state : public hh_tms1k_state
{
public:
	ginv_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void ginv(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void ginv_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void ginv_state::write_r(u32 data)
{
	// R9,R10: input mux
	m_inp_mux = data >> 9 & 3;

	// R15: speaker out
	m_speaker->level_w(data >> 15 & 1);

	// R0-R8: VFD grid
	// R11-R14: VFD plate
	m_grid = data & 0x1ff;
	m_plate = (m_plate & 0xff) | (data >> 3 & 0xf00);
	update_display();
}

void ginv_state::write_o(u16 data)
{
	// O0-O7: VFD plate
	m_plate = (m_plate & ~0xff) | data;
	update_display();
}

u8 ginv_state::read_k()
{
	// K1-K4: multiplexed inputs (K8 is fire button)
	return m_inputs[2]->read() | read_inputs(2);
}

// inputs

static INPUT_PORTS_START( ginv )
	PORT_START("IN.0") // R9
	PORT_CONFNAME( 0x07, 0x02, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_CONFSETTING(    0x04, "3" )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R10
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x02, 0x02, IPT_CUSTOM ) PORT_CONDITION("IN.1", 0x05, EQUALS, 0x00) // joystick centered
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // K8
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 )
INPUT_PORTS_END

// config

void ginv_state::ginv(machine_config &config)
{
	// basic machine hardware
	TMS1370(config, m_maincpu, 350000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_k().set(FUNC(ginv_state::read_k));
	m_maincpu->write_r().set(FUNC(ginv_state::write_r));
	m_maincpu->write_o().set(FUNC(ginv_state::write_o));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(236, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(9, 12);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( ginv )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp2110", 0x0000, 0x0800, CRC(f09c5588) SHA1(06eb8ed512eaf5367ea30c2b633219e105ddfd14) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_ginv_output.pla", 0, 365, CRC(6e33a24e) SHA1(cdf7ecf12ddd3863e6301e20fe80f9737db429e5) )

	ROM_REGION( 142959, "screen", 0)
	ROM_LOAD( "ginv.svg", 0, 142959, CRC(36a04f56) SHA1(a81c80e51d0a9d2855bf026236a257cf771be35c) )
ROM_END





/*******************************************************************************

  Gakken Invader 1000
  * TMS1370 MP2139 (die label: 1170, MP2139)
  * cyan/red VFD Futaba DM-25Z 2D, 1-bit sound

  known releases:
  - World: Galaxy Invader 1000, published by Gakken
  - Japan: Invader 1000, published by Gakken
  - USA(1): Galaxy Invader 1000, published by CGL
  - USA(2): Cosmic 1000 Fire Away, published by Tandy

*******************************************************************************/

class ginv1000_state : public hh_tms1k_state
{
public:
	ginv1000_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void ginv1000(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void ginv1000_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void ginv1000_state::write_r(u32 data)
{
	// R0: speaker out
	m_speaker->level_w(data & 1);

	// R8,R15: input mux
	m_inp_mux = (data >> 8 & 1) | (data >> 14 & 2);

	// R1-R10: VFD grid
	// R11-R14: VFD plate
	m_grid = data >> 1 & 0x3ff;
	m_plate = (m_plate & 0xff) | (data >> 3 & 0xf00);
	update_display();
}

void ginv1000_state::write_o(u16 data)
{
	// O0-O7: VFD plate
	m_plate = (m_plate & ~0xff) | data;
	update_display();
}

u8 ginv1000_state::read_k()
{
	// K1,K2: multiplexed inputs (K8 is fire button)
	return m_inputs[2]->read() | read_inputs(2);
}

// inputs

static INPUT_PORTS_START( ginv1000 )
	PORT_START("IN.0") // R8
	PORT_CONFNAME( 0x03, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x00, "2" )
	PORT_CONFSETTING(    0x02, "3" )
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R15
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // K8
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 )
INPUT_PORTS_END

// config

void ginv1000_state::ginv1000(machine_config &config)
{
	// basic machine hardware
	TMS1370(config, m_maincpu, 350000); // approximation
	m_maincpu->read_k().set(FUNC(ginv1000_state::read_k));
	m_maincpu->write_r().set(FUNC(ginv1000_state::write_r));
	m_maincpu->write_o().set(FUNC(ginv1000_state::write_o));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(226, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(10, 12);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( ginv1000 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp2139", 0x0000, 0x0800, CRC(036eab37) SHA1(0795878ad89296f7a6a0314c6e4db23c1cc3673e) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_ginv1000_output.pla", 0, 365, CRC(b0a5dc41) SHA1(d94746ec48661998173e7f60ccc7c96e56b3484e) )

	ROM_REGION( 227219, "screen", 0)
	ROM_LOAD( "ginv1000.svg", 0, 227219, CRC(4b21599a) SHA1(fbb728e44e504ab2169e0ee491a9ff3a231a717c) )
ROM_END





/*******************************************************************************

  Gakken Invader 2000
  * TMS1370(28 pins) MP1604 (die label: 1370A, MP1604)
  * TMS1024 I/O expander
  * cyan/red/green VFD, 1-bit sound

  known releases:
  - World: Invader 2000, published by Gakken
  - USA(1): Galaxy Invader 10000, published by CGL
  - USA(2): Cosmic 3000 Fire Away, published by Tandy

*******************************************************************************/

class ginv2000_state : public hh_tms1k_state
{
public:
	ginv2000_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag),
		m_expander(*this, "expander")
	{ }

	void ginv2000(machine_config &config);

private:
	required_device<tms1024_device> m_expander;

	void expander_w(offs_t offset, u8 data);

	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void ginv2000_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void ginv2000_state::expander_w(offs_t offset, u8 data)
{
	// TMS1024 port 4-7: VFD plate
	int shift = (offset - tms1024_device::PORT4) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void ginv2000_state::write_r(u32 data)
{
	// R0: speaker out
	m_speaker->level_w(data & 1);

	// R11,R12: input mux
	m_inp_mux = data >> 11 & 3;

	// R11,R12: TMS1024 S1,S0 (S2 forced high)
	// R13: TMS1024 STD
	m_expander->write_s((data >> 12 & 1) | (data >> 10 & 2) | 4);
	m_expander->write_std(data >> 13 & 1);

	// R1-R10: VFD grid
	m_grid = data >> 1 & 0x3ff;
	update_display();
}

void ginv2000_state::write_o(u16 data)
{
	// O4-O7: TMS1024 H1-H4
	m_expander->write_h(data >> 4 & 0xf);
}

u8 ginv2000_state::read_k()
{
	// K1,K2: multiplexed inputs (K8 is fire button)
	return m_inputs[2]->read() | read_inputs(2);
}

// inputs

static INPUT_PORTS_START( ginv2000 )
	PORT_START("IN.0") // R11
	PORT_CONFNAME( 0x03, 0x01, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R12
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // K8
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 )
INPUT_PORTS_END

// config

void ginv2000_state::ginv2000(machine_config &config)
{
	// basic machine hardware
	TMS1370(config, m_maincpu, 425000); // approximation - RC osc. R=36K, C=47pF
	m_maincpu->read_k().set(FUNC(ginv2000_state::read_k));
	m_maincpu->write_r().set(FUNC(ginv2000_state::write_r));
	m_maincpu->write_o().set(FUNC(ginv2000_state::write_o));

	TMS1024(config, m_expander).set_ms(1); // MS tied high
	m_expander->write_port4_callback().set(FUNC(ginv2000_state::expander_w));
	m_expander->write_port5_callback().set(FUNC(ginv2000_state::expander_w));
	m_expander->write_port6_callback().set(FUNC(ginv2000_state::expander_w));
	m_expander->write_port7_callback().set(FUNC(ginv2000_state::expander_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(364, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(10, 16);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( ginv2000 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1604", 0x0000, 0x0800, CRC(f1646d0b) SHA1(65601931d81e3eef7bf22a08de5a146910ce8137) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_ginv2000_output.pla", 0, 365, CRC(520bb003) SHA1(1640ae54f8dcc257e0ad0cbe0281b38fcbd8da35) )

	ROM_REGION( 374443, "screen", 0)
	ROM_LOAD( "ginv2000.svg", 0, 374443, CRC(7fb7ac44) SHA1(3cb0cd453433fd65afcdf0e206c79c83b9687913) )
ROM_END





/*******************************************************************************

  Gakken FX-Micom R-165
  * TMS1100 MCU, label MP1312 (die label: 1100E, MP1312A)
  * 1 7seg led, 6 other leds, 1-bit sound

  This is a simple educational home computer. Refer to the extensive manual
  for more information. It was published later in the USA by Tandy (Radio Shack),
  under their Science Fair series. Another 25 years later, Gakken re-released
  the R-165 as GMC-4, obviously on modern hardware, but fully compatible.

  known releases:
  - Japan: FX-Micom R-165, published by Gakken
  - USA: Science Fair Microcomputer Trainer, published by Tandy. Of note is
    the complete redesign of the case, adding more adjustable wiring

  ROM Revision A was used in Science Fair Microcomputer Trainer, and probably
  also in newer batches of FX-Micom R-165. It improves RNG and key debouncing.

*******************************************************************************/

class fxmcr165_state : public hh_tms1k_state
{
public:
	fxmcr165_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void fxmcr165(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void fxmcr165_state::update_display()
{
	// 7seg digit from O0-O6
	m_display->write_row(0, bitswap<8>(m_o,7,2,6,5,4,3,1,0) & 0x7f);

	// leds from R4-R10
	m_display->write_row(1, m_r >> 4 & 0x7f);
}

void fxmcr165_state::write_r(u32 data)
{
	// R0-R3: input mux low
	m_inp_mux = (m_inp_mux & 0x10) | (data & 0xf);

	// R7: speaker out
	m_speaker->level_w(data >> 7 & 1);

	// R4-R10: led data (direct)
	m_r = data;
	update_display();
}

void fxmcr165_state::write_o(u16 data)
{
	// O7: input mux high
	m_inp_mux = (m_inp_mux & 0xf) | (data >> 3 & 0x10);

	// O0-O6: digit segments (direct)
	m_o = data;
	update_display();
}

u8 fxmcr165_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(5);
}

// inputs

/* physical button layout and labels are like this:

    [C]   [D]   [E]   [F]   [ADR SET]
    [8]   [9]   [A]   [B]   [INCR]
    [4]   [5]   [6]   [7]   [RUN]
    [0]   [1]   [2]   [3]   [RESET]
*/

static INPUT_PORTS_START( fxmcr165 )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("C")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("D")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("E")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_NAME("B")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("F")

	PORT_START("IN.4") // O7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Reset")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Run")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_EQUALS) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("Increment")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("Address Set")
INPUT_PORTS_END

// config

void fxmcr165_state::fxmcr165(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 400_kHz_XTAL);
	m_maincpu->read_k().set(FUNC(fxmcr165_state::read_k));
	m_maincpu->write_r().set(FUNC(fxmcr165_state::write_r));
	m_maincpu->write_o().set(FUNC(fxmcr165_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1+1, 7);
	m_display->set_segmask(1, 0x7f);
	config.set_default_layout(layout_fxmcr165);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( fxmcr165 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1312a", 0x0000, 0x0800, CRC(6efc8bcc) SHA1(ced8a02b472a3178073691d3dccc0f19f57428fd) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_fxmcr165_output.pla", 0, 365, CRC(ce656866) SHA1(40e1614f5afcc7572fda596e1be453d54e95af0c) )
ROM_END

ROM_START( fxmcr165a )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1312", 0x0000, 0x0800, CRC(a76e1644) SHA1(981b6723ac899a2cf325041c4a96cb4304ee1c1d) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_fxmcr165_output.pla", 0, 365, CRC(ce656866) SHA1(40e1614f5afcc7572fda596e1be453d54e95af0c) )
ROM_END





/*******************************************************************************

  Ideal Electronic Detective
  * TMS0980NLL MP6100A (die label: 0980B-00)
  * 9-digit 7seg LED display (2 unused), 2-level sound

  hardware (and concept) is very similar to Parker Brothers Stop Thief

  This is an electronic board game. It requires game cards with suspect info,
  and good old pen and paper to record game progress. To start the game, enter
  difficulty(1-3), then number of players(1-4), then [ENTER]. Refer to the
  manual for more information.

*******************************************************************************/

class elecdet_state : public hh_tms1k_state
{
public:
	elecdet_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void elecdet(machine_config &config);

private:
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void elecdet_state::write_r(u32 data)
{
	// R7,R8(tied together): speaker out
	m_speaker->level_w((m_o & 0x80) ? population_count_32(data >> 7 & 3) : 0);

	// R0-R6: select digit
	m_display->matrix(data, bitswap<8>(m_o,7,5,2,1,4,0,6,3));
}

void elecdet_state::write_o(u16 data)
{
	// O0,O1,O4,O6: input mux
	m_inp_mux = (data & 3) | (data >> 2 & 4) | (data >> 3 & 8);

	// O0-O6: digit segments A-G
	// O7: speaker on -> write_r
	m_o = data;
}

u8 elecdet_state::read_k()
{
	// K: multiplexed inputs (note: the Vss row is always on)
	return m_inputs[4]->read() | read_inputs(4);
}

// inputs

/* physical button layout and labels are like this:

    [1]   [2]   [3]   [SUSPECT]
    [4]   [5]   [6]   [PRIVATE QUESTION]
    [7]   [8]   [9]   [I ACCUSE]
                [0]   [ENTER]
    [ON]  [OFF]       [END TURN]
*/

static INPUT_PORTS_START( elecdet )
	PORT_START("IN.0") // O0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Private Question")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")

	PORT_START("IN.1") // O1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // O4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("I Accuse")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")

	PORT_START("IN.3") // O6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Suspect")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")

	// note: even though power buttons are on the matrix, they are not CPU-controlled
	PORT_START("IN.4") // Vss
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_ON ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), true)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("End Turn")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), false)
INPUT_PORTS_END

// config

void elecdet_state::elecdet(machine_config &config)
{
	// basic machine hardware
	TMS0980(config, m_maincpu, 450000); // approximation
	m_maincpu->read_k().set(FUNC(elecdet_state::read_k));
	m_maincpu->write_r().set(FUNC(elecdet_state::write_r));
	m_maincpu->write_o().set(FUNC(elecdet_state::write_o));
	m_maincpu->power_off().set(FUNC(elecdet_state::auto_power_off));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(7, 7);
	m_display->set_segmask(0x7f, 0x7f);
	config.set_default_layout(layout_elecdet);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[3] = { 0.0, 0.5, 1.0};
	m_speaker->set_levels(3, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( elecdet )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mp6100a", 0x0000, 0x1000, CRC(9522fb2d) SHA1(240bdb44b7d67d3b13ebf75851635ac4b4ca2bfd) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 1982, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0980_common1_micro.pla", 0, 1982, CRC(3709014f) SHA1(d28ee59ded7f3b9dc3f0594a32a98391b6e9c961) )
	ROM_REGION( 352, "maincpu:opla", 0 )
	ROM_LOAD( "tms0980_elecdet_output.pla", 0, 352, CRC(5d12c24a) SHA1(e486802151a704c6273d4a8682c9c374d27d1e6d) )
	ROM_REGION( 157, "maincpu:spla", 0 )
	ROM_LOAD( "tms0980_common1_segment.pla", 0, 157, CRC(399aa481) SHA1(72c56c58fde3fbb657d69647a9543b5f8fc74279) )
ROM_END





/*******************************************************************************

  Ideal Sky-Writer (model 6070-7)
  * PCB label: ITC 5A-3917-02, 201101
  * TMS1100 M34004A (die label: 1100E, M34004A)
  * 7 LEDs, no sound

  It's a toy wand that the user waves around to display a message in the air,
  relying on human persistence of vision. A keyboard is included, making the
  whole thing quite tiring to wave around for a kid.

  The display is simulated in MAME with a screen and slowly fading pixels.

*******************************************************************************/

static constexpr u32 SKYWRITER_WIDTH = 0x300; // screen width (wave range)

class skywriter_state : public hh_tms1k_state
{
public:
	skywriter_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void skywriter(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	std::unique_ptr<u16[]> m_pixbuf;
	u8 m_led_data[2] = { };
	u16 m_wand_pos[2] = { };
	attotime m_shake;

	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	TIMER_DEVICE_CALLBACK_MEMBER(check_pos);
};

void skywriter_state::machine_start()
{
	hh_tms1k_state::machine_start();

	m_pixbuf = make_unique_clear<u16[]>(7 * SKYWRITER_WIDTH);
	save_pointer(NAME(m_pixbuf), 7 * SKYWRITER_WIDTH);

	save_item(NAME(m_led_data));
	save_item(NAME(m_wand_pos));
	save_item(NAME(m_shake));
}

// handlers

u32 skywriter_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	for (int i = 0; i < 7 * SKYWRITER_WIDTH; i++)
	{
		int dx = i % SKYWRITER_WIDTH;
		int dy = (i / SKYWRITER_WIDTH) * 4 + 1;

		// write current led state to pixels
		u8 red = std::clamp<u16>(m_pixbuf[i], 0, 0xff);
		u8 green = red / 16;
		u8 blue = red / 12;

		for (int j = 0; j < 3; j++)
		{
			if (cliprect.contains(dx, dy + j))
				bitmap.pix(dy + j, dx) = red << 16 | green << 8 | blue;
		}

		// decay led brightness
		const double rate = 1.15;
		m_pixbuf[i] /= rate;
	}

	return 0;
}

TIMER_DEVICE_CALLBACK_MEMBER(skywriter_state::check_pos)
{
	u16 pos = m_inputs[11]->read() % SKYWRITER_WIDTH;

	if (pos != m_wand_pos[0])
	{
		// set shake sensor if wand changed direction from left to right
		if (pos > m_wand_pos[0] && m_wand_pos[0] < m_wand_pos[1])
			m_shake = machine().time() + attotime::from_msec(10);

		m_wand_pos[1] = m_wand_pos[0];
		m_wand_pos[0] = pos;
	}

	// write lit leds to pixel buffer
	for (int i = 0; i < 7; i++)
	{
		if (BIT(m_led_data[0] | m_led_data[1], i))
			m_pixbuf[i * SKYWRITER_WIDTH + pos] = 0x180;
	}

	m_led_data[1] = m_led_data[0];
}

void skywriter_state::write_r(u32 data)
{
	// R0-R10: input mux
	m_inp_mux = data;
}

void skywriter_state::write_o(u16 data)
{
	// O0-O3, O5-O7: leds
	m_led_data[0] = bitswap<7>(data,1,5,6,7,3,2,0);

	// O4: enable shake sensor
	m_o = data;
}

u8 skywriter_state::read_k()
{
	// K: multiplexed inputs + shake sensor
	u8 shake = (m_o & 0x10 && m_shake > machine().time()) ? 4 : 0;
	return read_inputs(11) | shake;
}

// inputs

static INPUT_PORTS_START( skywriter )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_CHAR(127) PORT_NAME("CL")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13) PORT_NAME("Start")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED ) // factory test?
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_NAME("BS")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') PORT_NAME("SP")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('?')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8')

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6')

	PORT_START("IN.6") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')

	PORT_START("IN.7") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4')

	PORT_START("IN.8") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')

	PORT_START("IN.9") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2')

	PORT_START("IN.10") // R10
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')

	PORT_START("IN.11") // fake
	PORT_BIT( 0xfff, SKYWRITER_WIDTH / 2, IPT_PADDLE ) PORT_MINMAX(0, SKYWRITER_WIDTH - 1) PORT_SENSITIVITY(25) PORT_KEYDELTA(300) PORT_CENTERDELTA(0) PORT_NAME("Wave Wand")
INPUT_PORTS_END

// config

void skywriter_state::skywriter(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 475000); // approximation - RC osc. R=27K, C=100pF
	m_maincpu->read_k().set(FUNC(skywriter_state::read_k));
	m_maincpu->write_r().set(FUNC(skywriter_state::write_r));
	m_maincpu->write_o().set(FUNC(skywriter_state::write_o));

	TIMER(config, "check_pos").configure_periodic(FUNC(skywriter_state::check_pos), attotime::from_usec(1));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_video_attributes(VIDEO_ALWAYS_UPDATE); // for led decay
	screen.set_physical_aspect(16, 1);
	screen.set_refresh_hz(60);
	screen.set_size(SKYWRITER_WIDTH, 4 * 7 + 1);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(skywriter_state::screen_update));

	// no sound!
}

// roms

ROM_START( skywriter )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "m34004a.ic1", 0x0000, 0x0800, CRC(8e218dcc) SHA1(d3d5e0fa02c947d49d5bba1297edb7cecc0356da) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_skywriter_output.pla", 0, 365, CRC(407cb3b5) SHA1(f5830d25ec0ff8682993a2d03001606f1f30b46f) )
ROM_END





/*******************************************************************************

  Kenner Star Wars: Electronic Laser Battle Game (model 40090)
  * TMS1000NLL MP3209 (die label: 1000C, MP3209)
  * 12 LEDs, 1 lamp, 1-bit sound

  known releases:
  - USA: Star Wars: Electronic Laser Battle Game, published by Kenner
  - France: La Guerre des Etoiles: Bataille Spatiale Électronique, published by Meccano

*******************************************************************************/

class starwlb_state : public hh_tms1k_state
{
public:
	starwlb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void starwlb(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void starwlb_state::update_display()
{
	m_display->matrix(1, (m_r & 0x7ff) | (m_o << 4 & 0x800) | (m_o << 10 & 0x1000));
}

void starwlb_state::write_r(u32 data)
{
	// R0-R10: leds
	m_r = data;
	update_display();
}

void starwlb_state::write_o(u16 data)
{
	// O0,O1: input mux
	m_inp_mux = data & 3;

	// O3-O6(tied together): speaker out (actually only writes 0x0 or 0xf)
	m_speaker->level_w(population_count_32(data >> 3 & 0xf));

	// O2: lamp
	// O7: one more led
	m_o = data;
	update_display();
}

u8 starwlb_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(2);
}

// inputs

static INPUT_PORTS_START( starwlb )
	PORT_START("IN.0") // O0
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Button B")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Button F")

	PORT_START("IN.1") // O1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_COCKTAIL PORT_NAME("P2 Button B")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Button F")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void starwlb_state::starwlb(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 350000); // approximation - RC osc. R=33K, C=100pF
	m_maincpu->read_k().set(FUNC(starwlb_state::read_k));
	m_maincpu->write_r().set(FUNC(starwlb_state::write_r));
	m_maincpu->write_o().set(FUNC(starwlb_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 13);
	config.set_default_layout(layout_starwlb);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[5] = { 0.0, 1.0/4.0, 2.0/4.0, 3.0/4.0, 1.0 };
	m_speaker->set_levels(5, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( starwlb )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp3209", 0x0000, 0x0400, CRC(99628f9c) SHA1(5db191462521c4ae0b6955b31d5e8c6de65dd6a0) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common1_micro.pla", 0, 867, CRC(4becec19) SHA1(3c8a9be0f00c88c81f378b76886c39b10304f330) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_starwlb_output.pla", 0, 365, CRC(80a2d158) SHA1(8c74ca6af5d76425ff451bb5140a8dd2cfde850a) )
ROM_END





/*******************************************************************************

  Kenner Star Wars: Electronic Battle Command Game (model 40370)
  * TMS1100 MCU, label MP3438A (die label: 1100B, MP3438A)
  * 4x4 LED grid display + 2 separate LEDs and 2-digit 7segs, 1-bit sound

  This is a small tabletop space-dogfighting game. To start the game,
  press BASIC/INTER/ADV and enter P#(number of players), then
  START TURN. Refer to the official manual for more information.

*******************************************************************************/

class starwbc_state : public hh_tms1k_state
{
public:
	starwbc_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void starwbc(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void starwbc_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void starwbc_state::write_r(u32 data)
{
	// R0,R1,R3,R5,R7: input mux
	m_inp_mux = (data & 3) | (data >> 1 & 4) | (data >> 2 & 8) | (data >> 3 & 0x10);

	// R9: speaker out
	m_speaker->level_w(data >> 9 & 1);

	// R0,R2,R4: led select
	// R6,R8: digit select
	m_r = data & 0x155;
	update_display();
}

void starwbc_state::write_o(u16 data)
{
	// O0-O7: led state
	m_o = (data << 4 & 0xf0) | (data >> 4 & 0x0f);
	update_display();
}

u8 starwbc_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(5);
}

// inputs

/* physical button layout and labels are like this:

    (reconnnaissance=yellow)        (tactical reaction=green)
    [MAGNA] [ENEMY]                 [EM]       [BS]   [SCR]

    [BASIC] [INTER]    [START TURN] [END TURN] [MOVE] [FIRE]
    [ADV]   [P#]       [<]          [^]        [>]    [v]
    (game=blue)        (maneuvers=red)
*/

static INPUT_PORTS_START( starwbc )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("Basic Game")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("Intermediate Game")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("Advanced Game")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_P) PORT_NAME("Player Number")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_NAME("Start Turn")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_NAME("End Turn")

	PORT_START("IN.2") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Magna Scan") // only used in adv. game
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("Enemy Scan") // only used in adv. game
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Screen Up")

	PORT_START("IN.3") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("Evasive Maneuvers")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_M) PORT_NAME("Move")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("Fire")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_NAME("Battle Stations")

	PORT_START("IN.4") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Left")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_UP) PORT_NAME("Up")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Down")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Right")
INPUT_PORTS_END

// config

void starwbc_state::starwbc(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 350000); // approximation - RC osc. R=51K, C=47pF
	m_maincpu->read_k().set(FUNC(starwbc_state::read_k));
	m_maincpu->write_r().set(FUNC(starwbc_state::write_r));
	m_maincpu->write_o().set(FUNC(starwbc_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 8);
	m_display->set_segmask(0x140, 0x7f);
	config.set_default_layout(layout_starwbc);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( starwbc )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp3438a", 0x0000, 0x0800, CRC(c12b7069) SHA1(d1f39c69a543c128023ba11cc6228bacdfab04de) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common3_micro.pla", 0, 867, CRC(03574895) SHA1(04407cabfb3adee2ee5e4218612cb06c12c540f4) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_starwbc_output.pla", 0, 365, CRC(d358a76d) SHA1(06b60b207540e9b726439141acadea9aba718013) )
ROM_END

ROM_START( starwbcp )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "us4270755", 0x0000, 0x0800, BAD_DUMP CRC(fb3332f2) SHA1(a79ac81e239983cd699b7cfcc55f89b203b2c9ec) ) // from patent US4270755, may have errors

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common3_micro.pla", 0, 867, CRC(03574895) SHA1(04407cabfb3adee2ee5e4218612cb06c12c540f4) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_starwbc_output.pla", 0, 365, CRC(d358a76d) SHA1(06b60b207540e9b726439141acadea9aba718013) )
ROM_END





/*******************************************************************************

  Kenner Live Action Football
  * TMS1100NLL MCU, label MP3489-N2 (die label: 1100E, MP3489)
  * 6-digit 7seg LED display, other LEDs under overlay, 1-bit sound

  The LEDs are inside reflective domes, with an overlay mask on top of that.
  It is done with an SVG screen on MAME. In reality, the display is not as
  sharp or as evenly lit as MAME suggests it to be.

  It has a 1-bit roller controller. Half of the axis connects to the input
  (eg. 1 rising edge per full rotation), so there's no difference between
  rotating left or right.

*******************************************************************************/

class liveafb_state : public hh_tms1k_state
{
public:
	liveafb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void liveafb(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void liveafb_state::update_display()
{
	u8 d = (~m_r & 0x100) ? (m_r & 0x3f) : 0; // digit select
	u8 l = (m_r & 0x100) ? (m_r & 0xf) : 0; // led select
	m_display->matrix(d | l << 6 | BIT(m_r, 6) << 10 | BIT(m_r, 8, 3) << 11, m_o | (m_r << 4 & 0x300));
}

void liveafb_state::write_r(u32 data)
{
	// R0-R3: input mux
	m_inp_mux = data & 0xf;

	// R7(+R8): speaker out
	m_speaker->level_w(BIT(data, 7) & BIT(data, 8));

	// R8: enable digit or led select
	// R0-R3: led select
	// R0-R5: digit select
	// R4,R5: led data high
	// R6,R8-R10: direct leds
	m_r = data & ~0x80;
	update_display();
}

void liveafb_state::write_o(u16 data)
{
	// O0-O7: led data low
	m_o = data;
	update_display();
}

u8 liveafb_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(4);
}

// inputs

static INPUT_PORTS_START( liveafb )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x04, 0x04, IPT_CUSTOM ) PORT_CONDITION("ROLLER", 0x7f, LESSTHAN, 0x40)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Tackle")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Pass")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Punt")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P1 Field Goal")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START2 ) PORT_NAME("Skill / Score")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Action")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x08, 0x08, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x08, "1" )
	PORT_CONFSETTING(    0x00, "2" )

	PORT_START("ROLLER")
	PORT_BIT( 0x7f, 0x00, IPT_DIAL ) PORT_SENSITIVITY(100) PORT_KEYDELTA(10)
INPUT_PORTS_END

// config

void liveafb_state::liveafb(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 350000); // approximation - RC osc. R=33K, C=100pF
	m_maincpu->read_k().set(FUNC(liveafb_state::read_k));
	m_maincpu->write_r().set(FUNC(liveafb_state::write_r));
	m_maincpu->write_o().set(FUNC(liveafb_state::write_o));

	// video hardware
	screen_device &mask(SCREEN(config, "mask", SCREEN_TYPE_SVG));
	mask.set_refresh_hz(60);
	mask.set_size(1834, 1080);
	mask.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(6+4+4, 8+2);
	m_display->set_segmask(0x3f, 0x7f);
	m_display->set_segmask(0x20, 0xff); // only one digit has DP
	config.set_default_layout(layout_liveafb);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( liveafb )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp3489", 0x0000, 0x0800, CRC(1fe05ab3) SHA1(a8d7dfed61a6397b7af1d3fcf17b26d5d917b4f0) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_liveafb_output.pla", 0, 365, CRC(a7bc9384) SHA1(fab458de394eeddbf5ba0830853a915e51f909c6) )

	ROM_REGION( 162058, "mask", 0)
	ROM_LOAD( "liveafb.svg", 0, 162058, CRC(046078d0) SHA1(68a5775f4f9a1258c06b76839e1cfdab69b61920) )
ROM_END





/*******************************************************************************

  Jeux Nathan Mega 10000
  * PCB label: MADE IN FRANCE, OD 1112A
  * TMS1000ENLL MP3312 (die label: 1000E, MP3312)
  * 9-digit 7seg LED display, 1-bit sound

  It's a quiz game/scorekeeper, it needs question books to play. It's a bit
  cumbersome to play. To start, enter the book code, press R, then enter number
  of players (0 or 1-4). Press Q and manually input a question number, press N
  and enter the player number that will answer, press R, followed by the answer.

*******************************************************************************/

class mega10k_state : public hh_tms1k_state
{
public:
	mega10k_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void mega10k(machine_config &config);

private:
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void mega10k_state::write_r(u32 data)
{
	// R0-R6: digit segments
	m_display->write_mx(data);

	// R7-R10: input mux
	m_inp_mux = data >> 7 & 0xf;
}

void mega10k_state::write_o(u16 data)
{
	// O0-O3: digit select
	m_display->write_my(~data);

	// O5: speaker out
	m_speaker->level_w(BIT(data, 5));

	// O6: power off on falling edge
	if (~data & m_o & 0x40)
		power_off();
	m_o = data;
}

u8 mega10k_state::read_k()
{
	// K: multiplexed inputs (note: the Vdd row is always on)
	return m_inputs[4]->read() | read_inputs(4);
}

// inputs

static INPUT_PORTS_START( mega10k )
	PORT_START("IN.0") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("S") // score

	PORT_START("IN.1") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("R") // reponse (answer)

	PORT_START("IN.2") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Q") // question

	PORT_START("IN.3") // R10
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_N) PORT_NAME("N") // nombre (player#)

	PORT_START("IN.4") // Vdd
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_POWER_OFF )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_POWER_ON ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), true)
INPUT_PORTS_END

// config

void mega10k_state::mega10k(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 375000); // approximation - RC osc. R=33K, C=100pF
	m_maincpu->read_k().set(FUNC(mega10k_state::read_k));
	m_maincpu->write_r().set(FUNC(mega10k_state::write_r));
	m_maincpu->write_o().set(FUNC(mega10k_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 7);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_mega10k);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( mega10k )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp3312", 0x0000, 0x0400, CRC(2949f6a7) SHA1(0ac5560213ed681150b8f198be09a8d4ec3a41c4) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_mega10k_output.pla", 0, 365, CRC(dbacff74) SHA1(30ba16c0ea955f1d1264157a26c3a7cf169a6983) )
ROM_END





/*******************************************************************************

  Kosmos Astro
  * TMS1470NLHL MP1133 (die label: TMS1400, MP1133, 28H 01D D000 R000)
  * 9-digit 7seg VFD + 8 LEDs(4 green, 4 yellow), no sound

  This is an astrological calculator, and also supports 4-function calculations.
  Refer to the official manual on how to use this device.

  known releases:
  - World: Astro, published by Kosmos
  - USA: Astro (model EC-312), published by Tandy (Radio Shack)
  - Japan: Astrostar, published by Bandai

*******************************************************************************/

class astro_state : public hh_tms1k_state
{
public:
	astro_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void astro(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void astro_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void astro_state::write_r(u32 data)
{
	// R0-R7: input mux
	m_inp_mux = data & 0xff;

	// R0-R9: led select
	m_r = data;
	update_display();
}

void astro_state::write_o(u16 data)
{
	// O0-O7: led state
	m_o = data;
	update_display();
}

u8 astro_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(8);
}

// inputs

static INPUT_PORTS_START( astro )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷/Sun")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×/Mercury")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-/Venus")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+/Mars")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=/Astro")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Z) PORT_NAME("B1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME("B2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_NAME("C")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.7") // R7
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x08, 0x08, "Mode" )
	PORT_CONFSETTING(    0x00, "Calculator" )
	PORT_CONFSETTING(    0x08, "Astro" )
INPUT_PORTS_END

// config

void astro_state::astro(machine_config &config)
{
	// basic machine hardware
	TMS1470(config, m_maincpu, 450000); // approximation - RC osc. R=47K, C=33pF
	m_maincpu->read_k().set(FUNC(astro_state::read_k));
	m_maincpu->write_r().set(FUNC(astro_state::write_r));
	m_maincpu->write_o().set(FUNC(astro_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 8);
	m_display->set_segmask(0x3ff, 0xff);
	config.set_default_layout(layout_astro);

	// no sound!
}

// roms

ROM_START( astro )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mp1133", 0x0000, 0x1000, CRC(bc21109c) SHA1(05a433cce587d5c0c2d28b5fda5f0853ea6726bf) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 557, "maincpu:opla", 0 )
	ROM_LOAD( "tms1400_astro_output.pla", 0, 557, CRC(eb08957e) SHA1(62ae0d13a1eaafb34f1b27d7df51441b400ccd56) )
ROM_END





/*******************************************************************************

  Lakeside Strobe
  * PCB label: Lakeside, REVN 3, MODEL 9020-1099, PART 9504-3007, TCI-A4H 94HB
  * TMS1100NLLE MCU, label MP3487-N1 (die label: 1100E, MP3487)
  * SN75492, 4 lamps, 1-bit sound

  If patent US4309030 does not just describe a prototype, there should be an
  older version on a Matsushita MN1400.

  known releases:
  - USA: Strobe, published by Lakeside
  - UK: Strobe, published by Action GT

*******************************************************************************/

class strobe_state : public hh_tms1k_state
{
public:
	strobe_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void strobe(machine_config &config);

private:
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void strobe_state::write_r(u32 data)
{
	// R1-R7,R9: input mux
	m_inp_mux = (data >> 1 & 0x7f) | (data >> 2 & 0x80);

	// R8: speaker out
	m_speaker->level_w(BIT(data, 8));
}

void strobe_state::write_o(u16 data)
{
	// O0-O3: lamps
	// O4-O7: N/C
	m_display->matrix(1, data & 0xf);
}

u8 strobe_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(8) | (m_inputs[8]->read() & 8);
}

// inputs

static INPUT_PORTS_START( strobe )
	PORT_START("IN.0") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(3) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(3) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(3) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(4) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(4) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(4) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // R5
	PORT_CONFNAME( 0x05, 0x00, "Game" )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x01, "2" )
	PORT_CONFSETTING(    0x04, "3" )
	PORT_BIT( 0x0a, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // R6
	PORT_CONFNAME( 0x07, 0x00, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x01, "2" )
	PORT_CONFSETTING(    0x02, "3" )
	PORT_CONFSETTING(    0x04, "4" )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // R7
	PORT_CONFNAME( 0x06, 0x00, "Speed" )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_CONFSETTING(    0x04, "3" )
	PORT_BIT( 0x09, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.7") // R9
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE ) // beneath a small hole between "S" and "T" on the faceplate

	PORT_START("IN.8") // Vss
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_NAME("Set")
INPUT_PORTS_END

// config

void strobe_state::strobe(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 500000); // approximation - RC osc. R=39K, C=47pF
	m_maincpu->read_k().set(FUNC(strobe_state::read_k));
	m_maincpu->write_r().set(FUNC(strobe_state::write_r));
	m_maincpu->write_o().set(FUNC(strobe_state::write_o));

	PWM_DISPLAY(config, m_display).set_size(1, 4);
	config.set_default_layout(layout_strobe);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( strobe )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp3487", 0x0000, 0x0800, CRC(aaa999d3) SHA1(69bba74bafc60573ed29451c3939c479851fe867) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_strobe_output.pla", 0, 365, CRC(021bc65b) SHA1(1360e9f05bac922a27379058de16c92aca3df663) )
ROM_END





/*******************************************************************************

  Marx Series 300 Electronic Bowling Game
  * TMS1100NLL MP3403 DBS 7836 SINGAPORE (no decap)
  * 4*SN75492 quad segment driver, 2*SN74259 8-line demultiplexer,
    2*CD4043 quad r/s input latch
  * 5 7seg LEDs, 15 lamps(10 lamps projected to bowling pins reflection),
    1bit-sound with crude volume control
  * edge connector to sensors(switches trigger when ball rolls over)
    and other inputs

  lamp translation table: SN74259.u5(mux 1) goes to MAME output 5.x,
  SN74259.u6(mux 2) goes to MAME output 6.*. u1-u3 are SN75492 ICs,
  where other: u1 A2 is N/C, u3 A1 is from O2 and goes to digits seg C.

    u5 Q0 -> u1 A4 -> L2 (pin #2)       u6 Q0 -> u3 A4 -> L1 (pin #1)
    u5 Q1 -> u1 A5 -> L4 (pin #4)       u6 Q1 -> u3 A5 -> L5 (pin #5)
    u5 Q2 -> u1 A6 -> L7 (pin #7)       u6 Q2 -> u2 A3 -> L11 (player 1)
    u5 Q3 -> u1 A1 -> L8 (pin #8)       u6 Q3 -> u2 A2 -> L12 (player 2)
    u5 Q4 -> u3 A2 -> L3 (pin #3)       u6 Q4 -> u2 A1 -> L15 (?)
    u5 Q5 -> u2 A6 -> L6 (pin #6)       u6 Q5 -> u3 A6 -> L14 (?)
    u5 Q6 -> u2 A5 -> L10 (pin #10)     u6 Q6 -> u1 A3 -> L13 (spare)
    u5 Q7 -> u2 A4 -> L9 (pin #9)       u6 Q7 -> u3 A3 -> digit 4 B+C

*******************************************************************************/

class elecbowl_state : public hh_tms1k_state
{
public:
	elecbowl_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void elecbowl(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void elecbowl_state::update_display()
{
	// standard 7segs
	m_display->matrix_partial(0, 4, m_r >> 4, m_o);

	// lamp muxes
	u8 sel = m_o & 7;
	u8 state = m_r >> 1 & 1;
	if (~m_r & 1)
		m_display->write_element(5, sel, state);
	if (~m_r & 4)
		m_display->write_element(6, sel, state);

	// digit 4 is from mux2 Q7
	m_display->write_row(4, m_display->read_element(6, 7) ? 6 : 0);
}

void elecbowl_state::write_r(u32 data)
{
	// R5-R7,R10: input mux
	m_inp_mux = (data >> 5 & 7) | (data >> 7 & 8);

	// R9: speaker out
	// R3,R8: speaker volume..
	m_speaker->level_w(data >> 9 & 1);

	// R4-R7: select digit
	// R0,R2: lamp mux1,2 _enable
	// R1: lamp muxes state
	m_r = data;
	update_display();
}

void elecbowl_state::write_o(u16 data)
{
	//if (data & 0x80) printf("%X ",data&0x7f);

	// O0-O2: lamp muxes select
	// O0-O6: digit segments A-G
	// O7: N/C
	m_o = data & 0x7f;
	update_display();
}

u8 elecbowl_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(4);
}

// inputs

static INPUT_PORTS_START( elecbowl )
	PORT_START("IN.0") // R5
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4)

	PORT_START("IN.1") // R6
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) // reset/test?
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) // reset/test?

	PORT_START("IN.2") // R7
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F)

	PORT_START("IN.3") // R10
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) // 2 players sw?
INPUT_PORTS_END

// config

// output PLA is not decapped, this was made by hand
static const u16 elecbowl_output_pla[0x20] =
{
	0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, // 0-9
	0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, // ?
	0, 1, 2, 3, 4, 5, 6, 7, // lamp muxes select
	0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f // ?
};

void elecbowl_state::elecbowl(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 350000); // approximation - RC osc. R=33K, C=100pF
	m_maincpu->set_output_pla(elecbowl_output_pla);
	m_maincpu->read_k().set(FUNC(elecbowl_state::read_k));
	m_maincpu->write_r().set(FUNC(elecbowl_state::write_r));
	m_maincpu->write_o().set(FUNC(elecbowl_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(7, 8);
	m_display->set_segmask(0xf, 0x7f);
	m_display->set_segmask(0x10, 0x06); // 1
	config.set_default_layout(layout_elecbowl);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( elecbowl )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp3403.u9", 0x0000, 0x0800, CRC(9eabaa7d) SHA1(b1f54587ed7f2bbf3a5d49075c807296384c2b06) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common3_micro.pla", 0, 867, BAD_DUMP CRC(03574895) SHA1(04407cabfb3adee2ee5e4218612cb06c12c540f4) ) // not verified
	ROM_REGION( 365, "maincpu:opla", ROMREGION_ERASE00 )
	ROM_LOAD( "tms1100_elecbowl_output.pla", 0, 365, NO_DUMP )
ROM_END





/*******************************************************************************

  Mattel Thoroughbred Horse Race Analyzer
  * PCB label: 1670-4619D
  * TMS1100NLL MP3491-N2 (die label: 1100E, MP3491)
  * HLCD0569, 67-segment LCD panel, no sound

  This handheld is not a toy, read the manual for more information, and patent
  US4382280 for more in-depth information. In short, it is a device for predicting
  the winning chance of a gambling horserace.

  It was rereleased in 1994 in China/Canada by AHTI (Advanced Handicapping
  Technologies, Inc.), on a Hitachi HD613901 (LCD-IV).

*******************************************************************************/

class horseran_state : public hh_tms1k_state
{
public:
	horseran_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag),
		m_lcd(*this, "lcd")
	{ }

	void horseran(machine_config &config);

private:
	required_device<hlcd0569_device> m_lcd;

	void lcd_output_w(offs_t offset, u32 data);
	void write_r(u32 data);
	u8 read_k();
};

// handlers

void horseran_state::lcd_output_w(offs_t offset, u32 data)
{
	// only 3 rows used
	m_display->matrix(1 << offset, data);
}

void horseran_state::write_r(u32 data)
{
	// R0: HLCD0569 clock
	// R1: HLCD0569 data in
	// R2: HLCD0569 _CS
	m_lcd->cs_w(data >> 2 & 1);
	m_lcd->data_w(data >> 1 & 1);
	m_lcd->clock_w(data & 1);

	// R3-R10: input mux
	m_inp_mux = data >> 3 & 0xff;
}

u8 horseran_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(8);
}

// inputs

/* physical button layout and labels are like this:

    [PURSE]      [DIST.]      [P. POSN.]   [DAYS]       [R.S.L.]     [7]     [8]     [9]
    [RACES]      [WINS]       [PLACES]     [SHOWS]      [EARNINGS]   [4]     [5]     [6]
    [DISTANCE]   [L.E.B. 1st] [L.E.B. 2nd] [L.E.B. 3rd] [FIN. POSN.] [1]     [2]     [3]
    [L.E.B. Fin] [SPD. RTG.]  [SPD. RTG.]  [ANALYZE]    [NEXT]       [C/H]   [C/E]   [0]

    R.S.L. = Races Since (Last) Layoff
    L.E.B. = Last Race / Lengths Back
*/

static INPUT_PORTS_START( horseran )
	PORT_START("IN.0") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_NAME("Next")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_G) PORT_NAME("Final Position")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Earnings")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_NAME("R.S.L.")

	PORT_START("IN.1") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_V) PORT_NAME("Analyze")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("L.E.B. 3rd")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("Shows")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_NAME("Days")

	PORT_START("IN.2") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("Speed Rating (right)")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("L.E.B. 2nd")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("Places")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("Post Position")

	PORT_START("IN.3") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME("Speed Rating (left)")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("L.E.B. 1st")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("Wins")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("Distance (L.E.B.)")

	PORT_START("IN.4") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Z) PORT_NAME("L.E.B. Finish")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Distance")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Races")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("Purse")

	PORT_START("IN.5") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_COMMA) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_K) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_I) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")

	PORT_START("IN.6") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_M) PORT_NAME("C/E") // Clear Entry
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_J) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_U) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")

	PORT_START("IN.7") // R10
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_N) PORT_NAME("C/H") // Clear Horse Information
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Y) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
INPUT_PORTS_END

// config

void horseran_state::horseran(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 300000); // approximation - RC osc. R=56K, C=47pF
	m_maincpu->read_k().set(FUNC(horseran_state::read_k));
	m_maincpu->write_r().set(FUNC(horseran_state::write_r));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 978);
	screen.set_visarea_full();

	HLCD0569(config, m_lcd, 1100); // C=0.022uF
	m_lcd->write_cols().set(FUNC(horseran_state::lcd_output_w));

	PWM_DISPLAY(config, m_display).set_size(3, 24);

	// no sound!
}

// roms

ROM_START( horseran )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp3491", 0x0000, 0x0800, CRC(a0081671) SHA1(a5a07b502c69d429e5bcd1d313e86b6ee057cda6) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 365, "maincpu:opla", 0 ) // unused
	ROM_LOAD( "tms1100_horseran_output.pla", 0, 365, CRC(0fea09b0) SHA1(27a56fcf2b490e9a7dbbc6ad48cc8aaca4cada94) )

	ROM_REGION( 285857, "screen", 0)
	ROM_LOAD( "horseran.svg", 0, 285857, CRC(2835acc5) SHA1(4fe19b4f902249ea9fb3e3baea704d4a7b9c897a) )
ROM_END





/*******************************************************************************

  Mattel Dungeons & Dragons: Computer Labyrinth Game
  * TMS1100 M34012-N2LL (die label: 1100E, M34012)
  * 72 buttons, no LEDs, 1-bit sound

  This is an electronic board game. It requires markers and wall pieces to play.
  Refer to the official manual for more information.

*******************************************************************************/

class mdndclab_state : public hh_tms1k_state
{
public:
	mdndclab_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void mdndclab(machine_config &config);

private:
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void mdndclab_state::write_r(u32 data)
{
	// R10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// R0-R9: input mux part
	m_inp_mux = (m_inp_mux & 0xff) | (data << 8 & 0x3ff00);
}

void mdndclab_state::write_o(u16 data)
{
	// O0-O7: input mux part
	m_inp_mux = (m_inp_mux & ~0xff) | data;
}

u8 mdndclab_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(18);
}

// inputs

static INPUT_PORTS_START( mdndclab )
	PORT_START("IN.0") // O0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")

	PORT_START("IN.1") // O1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")

	PORT_START("IN.2") // O2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")

	PORT_START("IN.3") // O3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")

	PORT_START("IN.4") // O4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")

	PORT_START("IN.5") // O5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")

	PORT_START("IN.6") // O6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")

	PORT_START("IN.7") // O7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")

	PORT_START("IN.8") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Wall / Door")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Illegal Move / Warrior Moves")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Warrior 1 / Winner")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Warrior 2 / Treasure")

	PORT_START("IN.9") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")

	PORT_START("IN.10") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")

	PORT_START("IN.11") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")

	PORT_START("IN.12") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")

	PORT_START("IN.13") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")

	PORT_START("IN.14") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")

	PORT_START("IN.15") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")

	PORT_START("IN.16") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Board Sensor")

	PORT_START("IN.17") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_NAME("Switch Key")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Next Turn / Level 1/2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Dragon Flying / Defeat Tune")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Dragon Attacks / Dragon Wakes")
INPUT_PORTS_END

// config

void mdndclab_state::mdndclab(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 475000); // approximation - RC osc. R=27K, C=100pF
	m_maincpu->read_k().set(FUNC(mdndclab_state::read_k));
	m_maincpu->write_r().set(FUNC(mdndclab_state::write_r));
	m_maincpu->write_o().set(FUNC(mdndclab_state::write_o));

	config.set_default_layout(layout_mdndclab); // playing board

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( mdndclab )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "m34012", 0x0000, 0x0800, CRC(e851fccd) SHA1(158362c2821678a51554e02dbb2f9ef5aaf5f59f) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_mdndclab_output.pla", 0, 365, CRC(592b40ba) SHA1(63a2531278a665ace54c541101e052eb84413511) )
ROM_END





/*******************************************************************************

  Milton Bradley Comp IV
  * TMC0904NL CP0904A (die label: 0970D-04A)
  * 10 LEDs behind bezel, no sound

  This is small tabletop Mastermind game; a code-breaking game where the player
  needs to find out the correct sequence of colours (numbers in our case).
  Press the R key to start, followed by a set of unique numbers and E.
  Refer to the official manual for more information.

  known releases:
  - USA: Comp IV (two versions, different case), published by MB
  - Europe: Logic 5, published by MB
  - Japan: Pythaligoras, published by Takara

*******************************************************************************/

class comp4_state : public hh_tms1k_state
{
public:
	comp4_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void comp4(machine_config &config);

private:
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void comp4_state::write_r(u32 data)
{
	// leds:
	// R4    R9
	// R10!  R8
	// R2    R7
	// R1    R6
	// R0    R5
	m_display->matrix(m_o, data);
}

void comp4_state::write_o(u16 data)
{
	// O1-O3: input mux
	m_inp_mux = data >> 1 & 7;

	// O0: leds common
	// other bits: N/C
	m_o = data;
}

u8 comp4_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(3);
}

// inputs

static INPUT_PORTS_START( comp4 )
	PORT_START("IN.0") // O1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME("R")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")

	PORT_START("IN.1") // O2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")

	PORT_START("IN.2") // O3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("E")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
INPUT_PORTS_END

// config

void comp4_state::comp4(machine_config &config)
{
	// basic machine hardware
	TMS0970(config, m_maincpu, 250000); // approximation
	m_maincpu->read_k().set(FUNC(comp4_state::read_k));
	m_maincpu->write_r().set(FUNC(comp4_state::write_r));
	m_maincpu->write_o().set(FUNC(comp4_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 11);
	config.set_default_layout(layout_comp4);

	// no sound!
}

// roms

ROM_START( comp4 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tmc0904nl_cp0904a", 0x0000, 0x0400, CRC(6233ee1b) SHA1(738e109b38c97804b4ec52bed80b00a8634ad453) )

	ROM_REGION( 782, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0970_common2_instr.pla", 0, 782, CRC(e038fc44) SHA1(dfc280f6d0a5828d1bb14fcd59ac29caf2c2d981) )
	ROM_REGION( 860, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0970_comp4_micro.pla", 0, 860, CRC(ee9d7d9e) SHA1(25484e18f6a07f7cdb21a07220e2f2a82fadfe7b) )
	ROM_REGION( 352, "maincpu:opla", 0 )
	ROM_LOAD( "tms0980_comp4_output.pla", 0, 352, CRC(144ce2d5) SHA1(459b92ad62421932df61b7e3965f1821f9636a2c) )
	ROM_REGION( 157, "maincpu:spla", 0 )
	ROM_LOAD( "tms0980_comp4_segment.pla", 0, 157, CRC(73426b07) SHA1(311be3f95a97936b6d1a4dcfa7746da26318ce54) )
ROM_END





/*******************************************************************************

  Milton Bradley Electronic Battleship (model 4750A)
  * PCB label: 4750A
  * TMS1000NL MP3201 (die label: 1000C, MP3201)
  * LM324N, MC14016CP/TP4016AN, NE555P, discrete sound
  * 4 sliding buttons, light bulb

  This is a 2-player electronic board game. It still needs game pieces like the
  original Battleship board game.

  It went through at least 6 hardware revisions (not counting Talking Battleship):
  1977: model 4750A, TMS1000 discrete sound, see notes above
  1978: model 4750B, TMS1000 SN76477 sound, see notes at bshipb (driver below this)
  1979: model 4750C: cost-reduced single-chip design, lesser quality game board.
        The chip is assumed to be custom, no MCU: 28-pin DIP, label 4750, SCUS 0462
  1982: model 4750E: similar custom single-chip hardware, chip label MB4750 SCUS 0562
  1982: model 4750G: back to MCU, COP420 instead of TI, emulated in hh_cop400.cpp
  1982: model 4750H: unknown hardware, probably also COP420

*******************************************************************************/

class bship_state : public hh_tms1k_state
{
public:
	bship_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag),
		m_sound_nl(*this, "sound_nl:o%u", 0)
	{ }

	void bship(machine_config &config);

private:
	optional_device_array<netlist_mame_logic_input_device, 8> m_sound_nl;

	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void bship_state::write_r(u32 data)
{
	// R0-R10: input mux
	m_inp_mux = data;
}

void bship_state::write_o(u16 data)
{
	// O4: explosion light bulb
	m_display->matrix(1, BIT(data, 4));

	// other: sound
	for (int i = 0; i < 8; i++)
		if (i != 4) m_sound_nl[i]->write_line(BIT(data, i));
}

u8 bship_state::read_k()
{
	// K: multiplexed inputs (note: the Vss row is always on)
	return m_inputs[11]->read() | read_inputs(11);
}

// inputs

static INPUT_PORTS_START( bship )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("P1 1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("P1 A")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 A")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("P1 2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_NAME("P1 B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 B")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("P1 3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("P1 C")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 C")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("P1 4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("P1 D")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 4")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 D")

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("P1 5")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("P1 E")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 5")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 E")

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("P1 6")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("P1 F")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 F")

	PORT_START("IN.6") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("P1 7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_G) PORT_NAME("P1 G")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 7")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 G")

	PORT_START("IN.7") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("P1 8")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_H) PORT_NAME("P1 H")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 8")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 H")

	PORT_START("IN.8") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("P1 9")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_I) PORT_NAME("P1 I")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 I")

	PORT_START("IN.9") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("P1 10")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_J) PORT_NAME("P1 J")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 10")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 J")

	PORT_START("IN.10") // R10
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("P1 Fire")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Fire")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_TOGGLE PORT_CODE(KEYCODE_F1) PORT_NAME("Load/Go") // switch

	PORT_START("IN.11") // Vss
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_NAME("P1 Clear Memory") // CM
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("P1 Clear Last Entry") // CLE
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Clear Memory")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Clear Last Entry")
INPUT_PORTS_END

// config

void bship_state::bship(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 200000); // approximation - RC osc. R=100K, C=47pF
	m_maincpu->read_k().set(FUNC(bship_state::read_k));
	m_maincpu->write_r().set(FUNC(bship_state::write_r));
	m_maincpu->write_o().set(FUNC(bship_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 1);
	config.set_default_layout(layout_bship);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	NETLIST_SOUND(config, "sound_nl", 48000).set_source(NETLIST_NAME(bship)).add_route(ALL_OUTPUTS, "mono", 0.125);
	NETLIST_STREAM_OUTPUT(config, "sound_nl:cout0", 0, "SPK1.1").set_mult_offset(1.0, 0.0);

	NETLIST_LOGIC_INPUT(config, "sound_nl:o0", "O0.IN", 0);
	NETLIST_LOGIC_INPUT(config, "sound_nl:o1", "O1.IN", 0);
	NETLIST_LOGIC_INPUT(config, "sound_nl:o2", "O2.IN", 0);
	NETLIST_LOGIC_INPUT(config, "sound_nl:o3", "O3.IN", 0);
	NETLIST_LOGIC_INPUT(config, "sound_nl:o5", "O5.IN", 0);
	NETLIST_LOGIC_INPUT(config, "sound_nl:o6", "O6.IN", 0);
	NETLIST_LOGIC_INPUT(config, "sound_nl:o7", "O7.IN", 0);
}

// roms

ROM_START( bship )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp3201", 0x0000, 0x0400, CRC(bf6104a6) SHA1(8d28b43a2aa39dcbbe71f669cdafc518715812c9) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common1_micro.pla", 0, 867, CRC(4becec19) SHA1(3c8a9be0f00c88c81f378b76886c39b10304f330) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_bship_output.pla", 0, 365, CRC(ea0570b0) SHA1(6eb803b40717486d7b24939985f245327ac8a7e9) )
ROM_END





/*******************************************************************************

  Milton Bradley Electronic Battleship (model 4750B)
  * PCB label: MB 4750B
  * TMS1000NLL MP3208 (die label: 1000C, MP3208)
  * SN75494N (acting as inverters), SN76477 sound
  * 4 sliding buttons, light bulb

  This is the 2nd version. The sound hardware was changed to a SN76477.

*******************************************************************************/

class bshipb_state : public hh_tms1k_state
{
public:
	bshipb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag),
		m_sn(*this, "sn76477")
	{ }

	void bshipb(machine_config &config);

private:
	required_device<sn76477_device> m_sn;

	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void bshipb_state::write_r(u32 data)
{
	// R0-R10: input mux
	m_inp_mux = data;

	// R4: 75494 to R12 33K to SN76477 pin 20
	m_sn->slf_res_w((data & 0x10) ? RES_INF : RES_K(33));
}

void bshipb_state::write_o(u16 data)
{
	//printf("%X ", m_maincpu->debug_peek_o_index() & 0xf);

	// O0: SN76477 pin 9
	m_sn->enable_w(data & 1);

	// O1: 75494 to R4 100K to SN76477 pin 18
	// O2: 75494 to R3 150K to SN76477 pin 18
	const double o12[4] = { RES_INF, RES_K(100), RES_K(150), RES_2_PARALLEL(RES_K(100), RES_K(150)) };
	m_sn->vco_res_w(o12[~data >> 1 & 3]);

	// O2,O6: (TODO) to SN76477 pin 21
	//m_sn->slf_cap_w(x);

	// O4: SN76477 pin 22
	m_sn->vco_w(BIT(data, 4));

	// O5: R11 27K to SN76477 pin 23
	m_sn->one_shot_cap_w((data & 0x20) ? RES_K(27) : 0);

	// O6: SN76477 pin 25
	m_sn->mixer_b_w(BIT(data, 6));

	// O7: 75494 to light bulb
	m_display->matrix(1, BIT(data, 7));
}

u8 bshipb_state::read_k()
{
	// K: multiplexed inputs (note: the Vss row is always on)
	return m_inputs[11]->read() | read_inputs(11);
}

// config

void bshipb_state::bshipb(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 200000); // approximation - RC osc. R=100K, C=47pF
	m_maincpu->read_k().set(FUNC(bshipb_state::read_k));
	m_maincpu->write_r().set(FUNC(bshipb_state::write_r));
	m_maincpu->write_o().set(FUNC(bshipb_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 1);
	config.set_default_layout(layout_bship);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SN76477(config, m_sn);
	m_sn->set_noise_params(RES_K(47), RES_K(100), CAP_P(47));
	m_sn->set_decay_res(RES_M(3.3));
	m_sn->set_attack_params(CAP_U(0.47), RES_K(15));
	m_sn->set_amp_res(RES_K(100));
	m_sn->set_feedback_res(RES_K(39));
	m_sn->set_vco_params(5.0 * RES_VOLTAGE_DIVIDER(RES_K(47), RES_K(33)), CAP_U(0.01), RES_K(270));
	m_sn->set_pitch_voltage(5.0);
	m_sn->set_slf_params(CAP_U(22), RES_K(750));
	m_sn->set_oneshot_params(0, RES_INF);
	m_sn->set_vco_mode(0);
	m_sn->set_mixer_params(0, 0, 0);
	m_sn->set_envelope_params(1, 0);
	m_sn->set_enable(0);
	m_sn->add_route(ALL_OUTPUTS, "mono", 0.35);
}

// roms

ROM_START( bshipb )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp3208", 0x0000, 0x0400, CRC(982fa720) SHA1(1c6dbbe7b9e55d62a510225a88cd2de55fe9b181) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common1_micro.pla", 0, 867, CRC(4becec19) SHA1(3c8a9be0f00c88c81f378b76886c39b10304f330) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_bshipb_output.pla", 0, 365, BAD_DUMP CRC(74a9a244) SHA1(479c1f1e37cf8f75352e10226b20322906bee813) ) // a corner of the die broke off
ROM_END





/*******************************************************************************

  Milton Bradley Simon (model 4850), created by Ralph Baer
  * TMS1000 (die label: 1000C, MP3226), or MP3300 (die label: 1000C, MP3300)
  * DS75494 Hex digit LED driver, 4 big lamps, 1-bit sound

  known revisions:
  - 1978: Rev A: TMS1000(no label)
  - 198?: Rev B: MB4850 SCUS0640(16-pin custom ASIC), PCB label REV.B,
    cost-reduced, same hardware as Pocket Simon
  - 1979: Rev F: TMS1000(MP3300), PCB label 4850 Rev F

  The semi-sequel Super Simon uses a TMS1100 (see next minidriver).

*******************************************************************************/

class simon_state : public hh_tms1k_state
{
public:
	simon_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void simon(machine_config &config);

private:
	void write_r(u32 data);
	u8 read_k();
};

// handlers

void simon_state::write_r(u32 data)
{
	// R4-R8 go through an 75494 IC first:
	// R4 -> 75494 IN6 -> green lamp
	// R5 -> 75494 IN3 -> red lamp
	// R6 -> 75494 IN5 -> yellow lamp
	// R7 -> 75494 IN2 -> blue lamp
	m_display->matrix(1, data >> 4);

	// R8 -> 75494 IN0 -> speaker out
	m_speaker->level_w(data >> 8 & 1);

	// R0-R2,R9: input mux
	// R3: GND
	// other bits: N/C
	m_inp_mux = (data & 7) | (data >> 6 & 8);
}

u8 simon_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(4);
}

// inputs

static INPUT_PORTS_START( simon )
	PORT_START("IN.0") // R0
	PORT_CONFNAME( 0x07, 0x02, "Game Select")
	PORT_CONFSETTING(    0x02, "1" )
	PORT_CONFSETTING(    0x01, "2" )
	PORT_CONFSETTING(    0x04, "3" )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Green Button")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Red Button")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Yellow Button")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Blue Button")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Last")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Longest")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // R9
	PORT_CONFNAME( 0x0f, 0x02, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x02, "1" )
	PORT_CONFSETTING(    0x04, "2" )
	PORT_CONFSETTING(    0x08, "3" )
	PORT_CONFSETTING(    0x01, "4" )

	PORT_START("SWITCH") // fake
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::switch_prev<0>), 0x07)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::switch_next<0>), 0x07)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::switch_prev<3>), 0x0f)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::switch_next<3>), 0x0f)
INPUT_PORTS_END

// config

void simon_state::simon(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 325000); // approximation - RC osc. R=33K, C=100pF
	m_maincpu->read_k().set(FUNC(simon_state::read_k));
	m_maincpu->write_r().set(FUNC(simon_state::write_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 4);
	config.set_default_layout(layout_simon);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( simon )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tms1000.u1", 0x0000, 0x0400, CRC(9961719d) SHA1(35dddb018a8a2b31f377ab49c1f0cb76951b81c0) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common3_micro.pla", 0, 867, CRC(52f7c1f1) SHA1(dbc2634dcb98eac173ad0209df487cad413d08a5) )
	ROM_REGION( 365, "maincpu:opla", 0 ) // unused
	ROM_LOAD( "tms1000_simon_output.pla", 0, 365, CRC(2943c71b) SHA1(bd5bb55c57e7ba27e49c645937ec1d4e67506601) )
ROM_END

ROM_START( simonf )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp3300", 0x0000, 0x0400, CRC(b9fcf93a) SHA1(45960e4242a08495f2a99fc5d44728eabd93cd9f) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common3_micro.pla", 0, 867, CRC(52f7c1f1) SHA1(dbc2634dcb98eac173ad0209df487cad413d08a5) )
	ROM_REGION( 365, "maincpu:opla", 0 ) // unused
	ROM_LOAD( "tms1000_simon_output.pla", 0, 365, CRC(2943c71b) SHA1(bd5bb55c57e7ba27e49c645937ec1d4e67506601) )
ROM_END





/*******************************************************************************

  Milton Bradley Super Simon
  * TMS1100 MP3476NLL (die label: 1100E, MP3476)
  * 8 big lamps(2 turn on at same time), 1-bit sound

  The semi-squel to Simon, not as popular. It includes more game variations
  and a 2-player head-to-head mode.

*******************************************************************************/

class ssimon_state : public hh_tms1k_state
{
public:
	ssimon_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void ssimon(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(speed_switch);

private:
	void write_r(u32 data);
	u8 read_k();
};

// handlers

INPUT_CHANGED_MEMBER(ssimon_state::speed_switch)
{
	// MCU clock is from an RC circuit with C=100pF, R=x depending on speed switch:
	// 0 Simple: R=51K -> ~200kHz
	// 1 Normal: R=37K -> ~275kHz
	// 2 Super:  R=22K -> ~400kHz
	static const u32 freq[3] = { 200000, 275000, 400000 };
	m_maincpu->set_unscaled_clock(freq[newval % 3]);
}

void ssimon_state::write_r(u32 data)
{
	// R0-R3,R9,R10: input mux
	m_inp_mux = (data & 0xf) | (data >> 5 & 0x30);

	// R4: yellow lamps
	// R5: green lamps
	// R6: blue lamps
	// R7: red lamps
	m_display->matrix(1, data >> 4);

	// R8: speaker out
	m_speaker->level_w(data >> 8 & 1);
}

u8 ssimon_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(6);
}

// inputs

static INPUT_PORTS_START( ssimon )
	PORT_START("IN.0") // R0
	PORT_CONFNAME( 0x0f, 0x01, "Game Select")
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_CONFSETTING(    0x04, "3" )
	PORT_CONFSETTING(    0x08, "4" )
	PORT_CONFSETTING(    0x00, "5" )

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("P2 Yellow Button")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("P2 Green Button")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 Blue Button")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 Red Button")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Last")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Longest")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_NAME("Decision")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("P1 Yellow Button")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P1 Green Button")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Blue Button")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Red Button")

	PORT_START("IN.4") // R9
	PORT_CONFNAME( 0x0f, 0x02, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "Head-to-Head" ) // this sets R10 K2, see below
	PORT_CONFSETTING(    0x02, "1" )
	PORT_CONFSETTING(    0x04, "2" )
	PORT_CONFSETTING(    0x08, "3" )
	PORT_CONFSETTING(    0x01, "4" )

	PORT_START("IN.5") // R10
	PORT_BIT( 0x02, 0x02, IPT_CUSTOM ) PORT_CONDITION("IN.4", 0x0f, EQUALS, 0x00)
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // fake
	PORT_CONFNAME( 0x03, 0x01, "Speed" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssimon_state::speed_switch), 0)
	PORT_CONFSETTING(    0x00, "Simple" )
	PORT_CONFSETTING(    0x01, "Normal" )
	PORT_CONFSETTING(    0x02, "Super" )

	PORT_START("SWITCH") // fake
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::switch_prev<0>), 0x0f)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::switch_next<0>), 0x0f)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::switch_prev<4>), 0x0f)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::switch_next<4>), 0x0f)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::switch_prev<6>), 0x03)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::switch_next<6>), 0x03)
INPUT_PORTS_END

// config

void ssimon_state::ssimon(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 275000); // see speed_switch
	m_maincpu->read_k().set(FUNC(ssimon_state::read_k));
	m_maincpu->write_r().set(FUNC(ssimon_state::write_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 4);
	config.set_default_layout(layout_ssimon);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( ssimon )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp3476", 0x0000, 0x0800, CRC(98200571) SHA1(cbd0bcfc11a534aa0be5d011584cdcac58ff437a) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 365, "maincpu:opla", 0 ) // unused
	ROM_LOAD( "tms1100_ssimon_output.pla", 0, 365, CRC(0fea09b0) SHA1(27a56fcf2b490e9a7dbbc6ad48cc8aaca4cada94) )
ROM_END





/*******************************************************************************

  Milton Bradley Big Trak
  * TMS1000NLL MP3301A or MP3301ANLL E (rev. E!) (die label: 1000E, MP3301)
  * SN75494N Hex digit LED driver, 1 lamp, 3-level sound
  * gearbox with magnetic clutch, 1 IR led+sensor, 2 motors(middle wheels)
  * 24-button keypad, ext in/out ports

  Big Trak is a programmable toy car, up to 16 steps. Supported commands include
  driving and turning, firing a photon cannon(hey, have some imagination!),
  and I/O ports. The output port was used for powering the dump truck accessory.

  The In command was canceled midst production, it is basically a nop. Newer
  releases and the European version removed the button completely.

  There's also an unofficial Soviet Version: Elektronika IM-11 (УУ-1 MCU and
  КМ1010КТ1), Lunokhod and later Planetokhod. The software is presumed to be
  identical to Big Trak, УУ-1 is very likely a TMS1000 clone.

*******************************************************************************/

class bigtrak_state : public hh_tms1k_state
{
public:
	bigtrak_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag),
		m_left_motor_forward(*this, "left_motor_forward"),
		m_left_motor_reverse(*this, "left_motor_reverse"),
		m_right_motor_forward(*this, "right_motor_forward"),
		m_right_motor_reverse(*this, "right_motor_reverse"),
		m_ext_out(*this, "ext_out")
	{ }

	void bigtrak(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	output_finder<> m_left_motor_forward;
	output_finder<> m_left_motor_reverse;
	output_finder<> m_right_motor_forward;
	output_finder<> m_right_motor_reverse;
	output_finder<> m_ext_out;

	int m_gearbox_pos = 0;

	TIMER_DEVICE_CALLBACK_MEMBER(gearbox_sim_tick);
	bool sensor_state() { return m_gearbox_pos < 0 && m_display->element_on(0, 0); }

	void update_speaker();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

void bigtrak_state::machine_start()
{
	hh_tms1k_state::machine_start();

	// resolve outputs
	m_left_motor_forward.resolve();
	m_left_motor_reverse.resolve();
	m_right_motor_forward.resolve();
	m_right_motor_reverse.resolve();
	m_ext_out.resolve();

	// register for savestates
	save_item(NAME(m_gearbox_pos));
}

// handlers

TIMER_DEVICE_CALLBACK_MEMBER(bigtrak_state::gearbox_sim_tick)
{
	// the last gear in the gearbox has 12 evenly spaced holes, it is located
	// between an IR emitter and receiver
	const int speed = 17;
	if (m_gearbox_pos >= speed)
		m_gearbox_pos = -speed;

	if (m_o & 0x1e)
		m_gearbox_pos++;
}

void bigtrak_state::update_speaker()
{
	int data = (m_o & 1) | (m_o >> 6 & 2) | (m_r >> 8 & 4);
	m_speaker->level_w(population_count_32(data));
}

void bigtrak_state::write_r(u32 data)
{
	// R0-R5,R8: input mux (keypad, ext in enable)
	m_inp_mux = (data & 0x3f) | (data >> 2 & 0x40);

	// R6: N/C
	// R7: IR led on
	// R9: lamp on
	m_display->matrix(1, (data >> 7 & 1) | (data >> 8 & 2));

	// (O0,O7,)R10(tied together): speaker out
	m_r = data;
	update_speaker();
}

void bigtrak_state::write_o(u16 data)
{
	// O1: left motor forward
	// O2: left motor reverse
	// O3: right motor reverse
	// O4: right motor reverse
	// O5: ext out
	// O6: N/C
	m_left_motor_forward = data >> 1 & 1;
	m_left_motor_reverse = data >> 2 & 1;
	m_right_motor_forward = data >> 3 & 1;
	m_right_motor_reverse = data >> 4 & 1;
	m_ext_out = data >> 5 & 1;

	// O0,O7(,R10)(tied together): speaker out
	m_o = data;
	update_speaker();
}

u8 bigtrak_state::read_k()
{
	// K: multiplexed inputs
	// K8: IR sensor
	return read_inputs(7) | (sensor_state() ? 8 : 0);
}

// inputs

/* physical button layout and labels are like this:

        USA version:                          UK version:

           [^]           [CLR]                   [^]           [CM]
    [<]    [HOLD] [>]    [FIRE]           [<]    [P]    [>]    [\|/]
           [v]           [CLS]                   [v]           [CE]
    [7]    [8]    [9]    [RPT]            [7]    [8]    [9]    [x2]
    [4]    [5]    [6]    [TEST]           [4]    [5]    [6]    [TEST]
    [1]    [2]    [3]    [CK]             [1]    [2]    [3]    [checkmark]
    [IN]   [0]    [OUT]  [GO]                    [0]    [OUT]  [GO]
*/

static INPUT_PORTS_START( bigtrak )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_H) PORT_NAME("Hold")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_UP) PORT_NAME("Forward")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("Fire")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_NAME("Clear Memory")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Left")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Backward")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Right")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Clear Last Step")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("Repeat")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Test")

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("Check")

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_I) PORT_NAME("In")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_O) PORT_NAME("Out")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Go")

	PORT_START("IN.6") // R8
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_F1) PORT_NAME("Input Port")
	PORT_BIT( 0x0b, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void bigtrak_state::bigtrak(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 200000); // approximation - RC osc. R=83K, C=100pF
	m_maincpu->read_k().set(FUNC(bigtrak_state::read_k));
	m_maincpu->write_r().set(FUNC(bigtrak_state::write_r));
	m_maincpu->write_o().set(FUNC(bigtrak_state::write_o));

	TIMER(config, "gearbox").configure_periodic(FUNC(bigtrak_state::gearbox_sim_tick), attotime::from_msec(1));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 2);
	config.set_default_layout(layout_bigtrak);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[4] = { 0.0, 1.0/3.0, 2.0/3.0, 1.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( bigtrak )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp3301a", 0x0000, 0x0400, CRC(1351bcdd) SHA1(68865389c25b541c09a742be61f8fb6488134d4e) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common4_micro.pla", 0, 867, CRC(80912d0a) SHA1(7ae5293ed4d93f5b7a64d43fe30c3639f39fbe5a) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_bigtrak_output.pla", 0, 365, CRC(63be45f6) SHA1(918e38a223152db883c1a6f7acf56e87d7074734) )
ROM_END





/*******************************************************************************

  Milton Bradley Dark Tower
  * TMS1400NLL MP7332-N1.U1(rev. B) or MP7332-N2LL(rev. C) (die label:
    TMS1400, MP7332, 28L 01D D100 R100) (assume same ROM between revisions)
  * SN75494N MOS-to-LED digit driver
  * motorized rotating reel + lightsensor, 1bit-sound

  This is a board game, it obviously requires game pieces and the board.
  The emulated part is the centerpiece, a black tower with a rotating card
  panel and LED digits for displaying health, amount of gold, etc. As far
  as MAME is concerned, the game works fine.

  To start up the game, first press [MOVE], the machine now does a self-test.
  Then select level and number of players and the game will start. Read the
  official manual on how to play the game.

*******************************************************************************/

class mbdtower_state : public hh_tms1k_state
{
public:
	mbdtower_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag),
		m_motor_pos_out(*this, "motor_pos"),
		m_card_pos_out(*this, "card_pos"),
		m_motor_on_out(*this, "motor_on")
	{ }

	void mbdtower(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	output_finder<> m_motor_pos_out;
	output_finder<> m_card_pos_out;
	output_finder<> m_motor_on_out;

	int m_motor_pos = 0;
	int m_motor_pos_prev = -1;
	int m_motor_decay = 0;
	bool m_motor_on = false;
	bool m_sensor_blind = false;

	void update_display();
	bool sensor_led_on() { return m_display->element_on(0, 0); }

	TIMER_DEVICE_CALLBACK_MEMBER(motor_sim_tick);

	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

void mbdtower_state::machine_start()
{
	hh_tms1k_state::machine_start();

	// resolve outputs
	m_motor_pos_out.resolve();
	m_card_pos_out.resolve();
	m_motor_on_out.resolve();

	// register for savestates
	save_item(NAME(m_motor_pos));
	/* save_item(NAME(m_motor_pos_prev)); */ // don't save!
	save_item(NAME(m_motor_decay));
	save_item(NAME(m_motor_on));
	save_item(NAME(m_sensor_blind));
}

// handlers

TIMER_DEVICE_CALLBACK_MEMBER(mbdtower_state::motor_sim_tick)
{
	// it rotates counter-clockwise (when viewed from above)
	if (m_motor_on)
	{
		m_motor_pos = (m_motor_pos - 1) & 0x7f;

		// give it some time to spin out when it's turned off
		if (m_r & 0x200)
			m_motor_decay += (m_motor_decay < 4);
		else if (m_motor_decay > 0)
			m_motor_decay--;
		else
			m_motor_on = false;
	}

	// 8 evenly spaced holes in the rotation disc for the sensor to 'see' through.
	// The first hole is much bigger, enabling the game to determine the position.
	if ((m_motor_pos & 0xf) < 4 || m_motor_pos < 0xc)
		m_sensor_blind = false;
	else
		m_sensor_blind = true;

	// on change, output info
	if (m_motor_pos != m_motor_pos_prev)
		m_motor_pos_out = 100 * (m_motor_pos / (float)0x80);

	/* 3 display cards per hole, like this:

	    (0)                <---- display increments this way <----                   (7)

	    CURSED   VICTORY    WIZARD         DRAGON    GOLD KEY     SCOUT    WARRIOR   (void)
	    LOST     WARRIORS   BAZAAR CLOSED  SWORD     SILVER KEY   HEALER   FOOD      (void)
	    PLAGUE   BRIGANDS   KEY MISSING    PEGASUS   BRASS KEY    GOLD     BEAST     (void)
	*/
	int card_pos = m_motor_pos >> 4 & 7;
	if (card_pos != (m_motor_pos_prev >> 4 & 7))
		m_card_pos_out = card_pos;

	m_motor_pos_prev = m_motor_pos;
}

void mbdtower_state::update_display()
{
	// update current state
	if (~m_r & 0x10)
	{
		u8 o = bitswap<8>(m_o,7,0,4,3,2,1,6,5) & 0x7f;
		m_display->write_row(2, (m_o & 0x80) ? o : 0);
		m_display->write_row(1, (m_o & 0x80) ? 0 : o);
		m_display->write_row(0, (m_r >> 8 & 1) | (m_r >> 4 & 0xe));
	}
	else
	{
		// display items turned off
		m_display->clear();
	}
}

void mbdtower_state::write_r(u32 data)
{
	// R0-R2: input mux
	m_inp_mux = data & 7;

	// R9: motor on
	if ((m_r ^ data) & 0x200)
		m_motor_on_out = data >> 9 & 1;
	if (data & 0x200)
		m_motor_on = true;

	// R3: N/C
	// R4: 75494 /EN (speaker, lamps, digit select go through that IC)
	// R5-R7: tower lamps
	// R8: rotation sensor led
	m_r = data;
	update_display();

	// R10: speaker out
	m_speaker->level_w(~data >> 4 & data >> 10 & 1);
}

void mbdtower_state::write_o(u16 data)
{
	// O0-O6: led segments A-G
	// O7: digit select
	m_o = data;
	update_display();
}

u8 mbdtower_state::read_k()
{
	// K: multiplexed inputs
	// K8: rotation sensor
	return read_inputs(3) | ((!m_sensor_blind && sensor_led_on()) ? 8 : 0);
}

// inputs

/* physical button layout and labels are like this:

    (green)     (l.blue)    (red)
    [YES/       [REPEAT]    [NO/
     BUY]                    END]

    (yellow)    (blue)      (white)
    [HAGGLE]    [BAZAAR]    [CLEAR]

    (blue)      (blue)      (blue)
    [TOMB/      [MOVE]      [SANCTUARY/
     RUIN]                   CITADEL]

    (orange)    (blue)      (d.yellow)
    [DARK       [FRONTIER]  [INVENTORY]
     TOWER]
*/

static INPUT_PORTS_START( mbdtower )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("Inventory")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("No/End")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("Clear")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("Sanctuary/Citadel")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME("Frontier")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("Repeat")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("Bazaar")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Move")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Z) PORT_NAME("Dark Tower")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("Yes/Buy")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Haggle")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Tomb/Ruin")
INPUT_PORTS_END

// config

void mbdtower_state::mbdtower(machine_config &config)
{
	// basic machine hardware
	TMS1400(config, m_maincpu, 425000); // approximation - RC osc. R=43K, C=56pF
	m_maincpu->read_k().set(FUNC(mbdtower_state::read_k));
	m_maincpu->write_r().set(FUNC(mbdtower_state::write_r));
	m_maincpu->write_o().set(FUNC(mbdtower_state::write_o));

	TIMER(config, "tower_motor").configure_periodic(FUNC(mbdtower_state::motor_sim_tick), attotime::from_msec(3500/0x80)); // ~3.5sec for a full rotation

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(3, 7);
	m_display->set_segmask(6, 0x7f);
	config.set_default_layout(layout_mbdtower);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( mbdtower )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mp7332", 0x0000, 0x1000, CRC(ebeab91a) SHA1(7edbff437da371390fa8f28b3d183f833eaa9be9) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 557, "maincpu:opla", 0 )
	ROM_LOAD( "tms1400_mbdtower_output.pla", 0, 557, CRC(64c84697) SHA1(72ce6d24cedf9c606f1742cd5620f75907246e87) )
ROM_END





/*******************************************************************************

  Milton Bradley Electronic Arcade Mania
  * TMS1100 M34078A-N2LL (die label: 1100G M34078A)
  * 9 LEDs, 3-bit sound

  This is a board game. The mini arcade machine is the emulated part here.
  External artwork is needed for the game overlays.

*******************************************************************************/

class arcmania_state : public hh_tms1k_state
{
public:
	arcmania_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void arcmania(machine_config &config);

private:
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void arcmania_state::write_r(u32 data)
{
	// R1-R9: leds
	m_display->matrix(1, data >> 1);
}

void arcmania_state::write_o(u16 data)
{
	// O0-O2(tied together): speaker out
	m_speaker->level_w(population_count_32(data & 7));

	// O3,O4,O6: input mux
	m_inp_mux = (data >> 3 & 3) | (data >> 4 & 4);

	// O5: power off when low (turn back on with button row 1)
	if (~data & 0x20)
		power_off();
}

u8 arcmania_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(3);
}

// inputs

/* physical button layout and labels are like this:

    (orange)    (orange)    (orange)
    [1]         [2]         [3]

    (red)       (yellow)    (blue)
    [RUN]                   [SNEAK
     AMUK]                   ATTACK]

    (green)     (yellow)    (purple)
    [Alien                  [Rattler]
     Raiders]
*/

static INPUT_PORTS_START( arcmania )
	PORT_START("IN.0") // O3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Alien Raiders")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Yellow Button 2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("Rattler")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // O4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Run Amuk")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("Yellow Button 1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("Sneak Attack")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // O6 (also O5 to power)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("Orange Button 1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), true)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("Orange Button 2") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), true)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("Orange Button 3") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), true)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void arcmania_state::arcmania(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 250000); // approximation - RC osc. R=56K, C=100pF
	m_maincpu->read_k().set(FUNC(arcmania_state::read_k));
	m_maincpu->write_r().set(FUNC(arcmania_state::write_r));
	m_maincpu->write_o().set(FUNC(arcmania_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 9);
	config.set_default_layout(layout_arcmania);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[4] = { 0.0, 1.0/3.0, 2.0/3.0, 1.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( arcmania )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "m34078a", 0x0000, 0x0800, CRC(90ea0087) SHA1(9780c9c1ba89300b1bbe72c47e5fec68d8bb6a77) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_arcmania_output.pla", 0, 365, CRC(a1517b15) SHA1(72eedd7fd41de9c9102219f325fe8668a7c02663) )
ROM_END





/*******************************************************************************

  Parker Brothers Code Name: Sector, by Bob Doyle
  * TMS0970 MCU, MP0905BNL ZA0379 (die label: 0970F-05B)
  * 6-digit 7seg LED display + 4 LEDs for compass, no sound

  This is a tabletop submarine pursuit game. A grid board and small toy
  boats are used to remember your locations (a Paint app should be ok too).
  Refer to the official manual for more information, it is not a simple game.

*******************************************************************************/

class cnsector_state : public hh_tms1k_state
{
public:
	cnsector_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void cnsector(machine_config &config);

private:
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void cnsector_state::write_r(u32 data)
{
	// R0-R5: select digit
	// R6-R9: direction leds
	m_display->matrix(data, m_o);
}

void cnsector_state::write_o(u16 data)
{
	// O0-O4: input mux
	m_inp_mux = data & 0x1f;

	// O0-O7: digit segments
	m_o = data;
}

u8 cnsector_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(5);
}

// inputs

/* physical button layout and labels are like this:

             COMBAT INFORMATION CENTER
    [NEXT SHIP]       [RECALL]    [MOVE SHIP]

    [LEFT]   [RIGHT]      o       [SLOWER] [FASTER]
        STEERING     EVASIVE SUB        SPEED            o (on/off switch)
              NAVIGATIONAL PROGRAMMING                   |

    [RANGE]  [AIM]    [FIRE]        o      [TEACH MODE]
      SONAR CONTROL            SUB FINDER
*/

static INPUT_PORTS_START( cnsector )
	PORT_START("IN.0") // O0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Next Ship")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Left")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Z) PORT_NAME("Range")

	PORT_START("IN.1") // O1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Right")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME("Aim")

	PORT_START("IN.2") // O2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("Recall")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("Evasive Sub") // expert button
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("Fire")

	PORT_START("IN.3") // O3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("Slower")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_V) PORT_NAME("Sub Finder") // expert button

	PORT_START("IN.4") // O4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Move Ship")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_G) PORT_NAME("Faster")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_NAME("Teach Mode")
INPUT_PORTS_END

// config

void cnsector_state::cnsector(machine_config &config)
{
	// basic machine hardware
	TMS0970(config, m_maincpu, 250000); // approximation
	m_maincpu->read_k().set(FUNC(cnsector_state::read_k));
	m_maincpu->write_r().set(FUNC(cnsector_state::write_r));
	m_maincpu->write_o().set(FUNC(cnsector_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 8);
	m_display->set_segmask(0x3f, 0xff);
	config.set_default_layout(layout_cnsector);

	// no sound!
}

// roms

ROM_START( cnsector )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp0905bnl_za0379", 0x0000, 0x0400, CRC(201036e9) SHA1(b37fef86bb2bceaf0ac8bb3745b4702d17366914) )

	ROM_REGION( 782, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0970_common2_instr.pla", 0, 782, CRC(e038fc44) SHA1(dfc280f6d0a5828d1bb14fcd59ac29caf2c2d981) )
	ROM_REGION( 860, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0970_common3_micro.pla", 0, 860, CRC(059f5bb4) SHA1(2653766f9fd74d41d44013bb6f54c0973a6080c9) )
	ROM_REGION( 352, "maincpu:opla", 0 )
	ROM_LOAD( "tms0980_cnsector_output.pla", 0, 352, CRC(c8bfb9d2) SHA1(30c3c73cec194debdcb1dd01b4adfefaeddf9516) )
	ROM_REGION( 157, "maincpu:spla", 0 )
	ROM_LOAD( "tms0980_common2_segment.pla", 0, 157, CRC(c03cccd8) SHA1(08bc4b597686a7aa8b2c9f43b85b62747ffd455b) )
ROM_END





/*******************************************************************************

  Parker Brothers Merlin handheld game, by Bob Doyle
  * TMS1100NLL MP3404 or MP3404A-N2
  * 11 LEDs behind buttons, 3-level sound

  Also published in Japan by Tomy as "Dr. Smith", white case instead of red.
  The one with dark-blue case is the rare sequel Master Merlin, see below.
  More sequels followed too, but on other hardware.

  To start a game, press NEW GAME, followed by a number:
  1: Tic-Tac-Toe
  2: Music Machine
  3: Echo
  4: Blackjack 13
  5: Magic Square
  6: Mindbender

  Refer to the official manual for more information on the games.

*******************************************************************************/

class merlin_state : public hh_tms1k_state
{
public:
	merlin_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void merlin(machine_config &config);

protected:
	virtual void write_r(u32 data);
	virtual void write_o(u16 data);
	virtual u8 read_k();
};

// handlers

void merlin_state::write_r(u32 data)
{
	/* leds:

	     R0
	R1   R2   R3
	R4   R5   R6
	R7   R8   R9
	     R10
	*/
	m_display->matrix(1, data);
}

void merlin_state::write_o(u16 data)
{
	// O4-O6(tied together): speaker out
	m_speaker->level_w(population_count_32(data >> 4 & 7));

	// O0-O3: input mux
	// O7: N/C
	m_inp_mux = data & 0xf;
}

u8 merlin_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(4);
}

// inputs

static INPUT_PORTS_START( merlin )
	PORT_START("IN.0") // O0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME("Button 0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Button 1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("Button 3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Button 2")

	PORT_START("IN.1") // O1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Button 4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Button 5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Button 7")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Button 6")

	PORT_START("IN.2") // O2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Button 8")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Button 9")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_G) PORT_NAME("Same Game")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("Button 10")

	PORT_START("IN.3") // O3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("Comp Turn")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_H) PORT_NAME("Hit Me")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_N) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("New Game")
INPUT_PORTS_END

// config

void merlin_state::merlin(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 350000); // approximation - RC osc. R=33K, C=100pF
	m_maincpu->read_k().set(FUNC(merlin_state::read_k));
	m_maincpu->write_r().set(FUNC(merlin_state::write_r));
	m_maincpu->write_o().set(FUNC(merlin_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 11);
	config.set_default_layout(layout_merlin);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[4] = { 0.0, 1.0/3.0, 2.0/3.0, 1.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( merlin )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp3404a", 0x0000, 0x0800, CRC(7515a75d) SHA1(76ca3605d3fde1df62f79b9bb1f534c2a2ae0229) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common3_micro.pla", 0, 867, CRC(03574895) SHA1(04407cabfb3adee2ee5e4218612cb06c12c540f4) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_merlin_output.pla", 0, 365, CRC(3921b074) SHA1(12bd58e4d6676eb8c7059ef53598279e4f1a32ea) )
ROM_END





/*******************************************************************************

  Parker Brothers Master Merlin
  * TMS1400 MP7351-N2LL (die label: 1400CR, MP7351)
  * 11 LEDs behind buttons, 3-level sound

  The TMS1400CR MCU has the same pinout as a standard TMS1100. The hardware
  outside of the MCU is exactly the same as Merlin.

  The included minigames are:
  1: Three Shells
  2: Hi/Lo
  3: Match It
  4: Hit or Miss
  5: Pair Off
  6: Tempo
  7: Musical Ladder
  8: Patterns
  9: Hot Potato

*******************************************************************************/

class mmerlin_state : public merlin_state
{
public:
	mmerlin_state(const machine_config &mconfig, device_type type, const char *tag) :
		merlin_state(mconfig, type, tag)
	{ }

	void mmerlin(machine_config &config);
};

// handlers: uses the ones in merlin_state

// inputs

static INPUT_PORTS_START( mmerlin )
	PORT_INCLUDE( merlin )

	PORT_MODIFY("IN.3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Score") // instead of Hit Me
INPUT_PORTS_END

// config

void mmerlin_state::mmerlin(machine_config &config)
{
	merlin(config);

	// basic machine hardware
	TMS1400(config.replace(), m_maincpu, 425000); // approximation - RC osc. R=30K, C=100pF
	m_maincpu->read_k().set(FUNC(mmerlin_state::read_k));
	m_maincpu->write_r().set(FUNC(mmerlin_state::write_r));
	m_maincpu->write_o().set(FUNC(mmerlin_state::write_o));

	config.set_default_layout(layout_mmerlin);
}

// roms

ROM_START( mmerlin )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mp7351", 0x0000, 0x1000, CRC(0f7a4c83) SHA1(242c1278ddfe92c28fd7cd87300e48e7a4827831) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 557, "maincpu:opla", 0 )
	ROM_LOAD( "tms1400_mmerlin_output.pla", 0, 557, CRC(fd3dcd93) SHA1(f2afc52df700daa0eb7356c7876af9b2966f971b) )
ROM_END





/*******************************************************************************

  Parker Brothers Electronic Master Mind
  * TMS1000NLL MP3200 (die label: 1000E, MP3200)
  * 5 red leds, 5 green leds

  This is a board game, it came with 4 plug boards and a lot of colored pegs.
  It's not related to the equally named Electronic Master Mind by Invicta,
  that one is on a Rockwell MM75.

*******************************************************************************/

class pbmastm_state : public hh_tms1k_state
{
public:
	pbmastm_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void pbmastm(machine_config &config);

private:
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void pbmastm_state::write_r(u32 data)
{
	// R1-R10: leds (direct)
	m_display->matrix(1, data >> 1);
}

void pbmastm_state::write_o(u16 data)
{
	// O0-O5: input mux
	m_inp_mux = data & 0x3f;
}

u8 pbmastm_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(6);
}

// inputs

static INPUT_PORTS_START( pbmastm )
	PORT_START("IN.0") // O0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")

	PORT_START("IN.1") // O1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("C")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("D")

	PORT_START("IN.2") // O2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Red")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Blue")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_J) PORT_NAME("Brown")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_K) PORT_NAME("Black")

	PORT_START("IN.3") // O3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("Yellow")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("White")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_L) PORT_NAME("Pink")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_COLON) PORT_NAME("Gray")

	PORT_START("IN.4") // O4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_G) PORT_NAME("Green")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_H) PORT_NAME("Orange")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_QUOTE) PORT_NAME("Blank")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Delete")

	PORT_START("IN.5") // O5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Z) PORT_NAME("Battery Test")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME("Code")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Check")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void pbmastm_state::pbmastm(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 300000); // approximation - RC osc. R=56K, C=47pF
	m_maincpu->read_k().set(FUNC(pbmastm_state::read_k));
	m_maincpu->write_r().set(FUNC(pbmastm_state::write_r));
	m_maincpu->write_o().set(FUNC(pbmastm_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 10);
	config.set_default_layout(layout_pbmastm);

	// no sound!
}

// roms

ROM_START( pbmastm )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp3200", 0x0000, 0x0400, CRC(059dbb88) SHA1(20e3f6aeecce167371bda914f263daf53c50c839) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_pbmastm_output.pla", 0, 365, CRC(b355c4d9) SHA1(43cc971a4feacfddfe810c8983c65d9eef3ed57b) )
ROM_END





/*******************************************************************************

  Parker Brothers Stop Thief, by Bob Doyle
  * TMS0980NLL MP6101B (die label: 0980B-01A)
  * 3-digit 7seg LED display, 6-level sound

  Stop Thief is actually a board game, the electronic device emulated here
  (called Electronic Crime Scanner) is an accessory. To start a game, press
  the ON button. Otherwise, it is in test-mode where you can hear all sounds.
  For the patent romset, it needs to be turned off and on to exit test mode.

*******************************************************************************/

class stopthief_state : public hh_tms1k_state
{
public:
	stopthief_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void stopthief(machine_config &config);

private:
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void stopthief_state::write_r(u32 data)
{
	// R0-R2: select digit
	m_display->matrix(data & 7, bitswap<8>(m_o,3,5,2,1,4,0,6,7) & 0x7f);

	// R3-R8(tied together): speaker out
	m_speaker->level_w((m_o & 8) ? population_count_32(data >> 3 & 0x3f) : 0);
}

void stopthief_state::write_o(u16 data)
{
	// O0,O6: input mux
	m_inp_mux = (data & 1) | (data >> 5 & 2);

	// O3: speaker on
	// O0-O2,O4-O7: led segments A-G
	m_o = data;
}

u8 stopthief_state::read_k()
{
	// K: multiplexed inputs (note: the Vss row is always on)
	return m_inputs[2]->read() | read_inputs(2);
}

// inputs

/* physical button layout and labels are like this:

    [1] [2] [OFF]
    [3] [4] [ON]
    [5] [6] [T, TIP]
    [7] [8] [A, ARREST]
    [9] [0] [C, CLUE]
*/

static INPUT_PORTS_START( stopthief )
	PORT_START("IN.0") // O0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")

	PORT_START("IN.1") // O6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")

	// note: even though power buttons are on the matrix, they are not CPU-controlled
	PORT_START("IN.2") // Vss
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_ON ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), true)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Tip")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Arrest")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("Clue")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), false)
INPUT_PORTS_END

// config

void stopthief_state::stopthief(machine_config &config)
{
	// basic machine hardware
	TMS0980(config, m_maincpu, 425000); // approximation
	m_maincpu->read_k().set(FUNC(stopthief_state::read_k));
	m_maincpu->write_r().set(FUNC(stopthief_state::write_r));
	m_maincpu->write_o().set(FUNC(stopthief_state::write_o));
	m_maincpu->power_off().set(FUNC(stopthief_state::auto_power_off));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(3, 7);
	m_display->set_segmask(7, 0x7f);
	config.set_default_layout(layout_stopthief);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[7] = { 0.0, 1.0/6.0, 2.0/6.0, 3.0/6.0, 4.0/6.0, 5.0/6.0, 1.0 };
	m_speaker->set_levels(7, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( stopthief )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mp6101b", 0x0000, 0x1000, CRC(b9c9d64a) SHA1(481f8653064c142fe5d9314b750bcd73797b92b2) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 1982, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0980_common1_micro.pla", 0, 1982, CRC(3709014f) SHA1(d28ee59ded7f3b9dc3f0594a32a98391b6e9c961) )
	ROM_REGION( 352, "maincpu:opla", 0 )
	ROM_LOAD( "tms0980_stopthief_output.pla", 0, 352, CRC(680ca1c1) SHA1(dea6365f2e6b50a52f1a8f1d8417176b905d2bc9) )
	ROM_REGION( 157, "maincpu:spla", 0 )
	ROM_LOAD( "tms0980_common1_segment.pla", 0, 157, CRC(399aa481) SHA1(72c56c58fde3fbb657d69647a9543b5f8fc74279) )
ROM_END

ROM_START( stopthiefp )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD16_WORD( "us4341385", 0x0000, 0x1000, CRC(07aec38a) SHA1(0a3d0956495c0d6d9ea771feae6c14a473a800dc) ) // from patent US4341385, data should be correct (it included checksums)

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 1982, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0980_common1_micro.pla", 0, 1982, CRC(3709014f) SHA1(d28ee59ded7f3b9dc3f0594a32a98391b6e9c961) )
	ROM_REGION( 352, "maincpu:opla", 0 )
	ROM_LOAD( "tms0980_stopthief_output.pla", 0, 352, CRC(680ca1c1) SHA1(dea6365f2e6b50a52f1a8f1d8417176b905d2bc9) )
	ROM_REGION( 157, "maincpu:spla", 0 )
	ROM_LOAD( "tms0980_common1_segment.pla", 0, 157, CRC(399aa481) SHA1(72c56c58fde3fbb657d69647a9543b5f8fc74279) )
ROM_END





/*******************************************************************************

  Parker Brothers Bank Shot (known as Cue Ball in the UK), by Garry Kitchen
  * TMS1400NLL MP7313-N2 (die label: TMS1400, MP7313, 28L 01D D000 R000)
  * LED grid display, 1-bit sound

  Bank Shot is an electronic pool game. To select a game, repeatedly press
  the [SELECT] button, then press [CUE UP] to start. Refer to the official
  manual for more information. The game selections are:
  1: Straight Pool (1 player)
  2: Straight Pool (2 players)
  3: Poison Pool
  4: Trick Shots

  BTANB: Some of the other (not cue) balls temporarily flash brighter sometimes,
  eg. the bottom one when they're placed. This happens on the real device.

*******************************************************************************/

class bankshot_state : public hh_tms1k_state
{
public:
	bankshot_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void bankshot(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void bankshot_state::update_display()
{
	m_display->matrix(m_r & ~3, m_o);
}

void bankshot_state::write_r(u32 data)
{
	// R0: speaker out
	m_speaker->level_w(data & 1);

	// R2,R3: input mux
	m_inp_mux = data >> 2 & 3;

	// R2-R10: led select
	m_r = data;
	update_display();
}

void bankshot_state::write_o(u16 data)
{
	// O0-O7: led state
	m_o = data;
	update_display();
}

u8 bankshot_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(2);
}

// inputs

/* physical button layout and labels are like this:
  (note: remember that you can rotate the display in MAME)

    [SELECT  [BALL UP] [BALL OVER]
     SCORE]

    ------  led display  ------

    [ANGLE]  [AIM]     [CUE UP
                        SHOOT]
*/

static INPUT_PORTS_START( bankshot )
	PORT_START("IN.0") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Angle")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Aim")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Cue Up/Shoot")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Select/Score")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Ball Up")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Ball Over")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void bankshot_state::bankshot(machine_config &config)
{
	// basic machine hardware
	TMS1400(config, m_maincpu, 475000); // approximation - RC osc. R=24K, C=100pF
	m_maincpu->read_k().set(FUNC(bankshot_state::read_k));
	m_maincpu->write_r().set(FUNC(bankshot_state::write_r));
	m_maincpu->write_o().set(FUNC(bankshot_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(11, 8);
	m_display->set_bri_levels(0.01, 0.08); // cue ball is brigher
	config.set_default_layout(layout_bankshot);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( bankshot )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mp7313", 0x0000, 0x1000, CRC(7a5016a9) SHA1(a8730dc8a282ffaa3d89e675f371d43eb39f39b4) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 557, "maincpu:opla", 0 )
	ROM_LOAD( "tms1400_bankshot_output.pla", 0, 557, CRC(7539283b) SHA1(f791fa98259fc10c393ff1961d4c93040f1a2932) )
ROM_END





/*******************************************************************************

  Parker Brothers Split Second
  * TMS1400NLL MP7314-N2 (die label: TMS1400, MP7314, 28L 01D D000 R000)
  * LED grid display(default round LEDs, and rectangular shape ones), 1-bit sound

  This is an electronic handheld reflex gaming device, it's straightforward
  to use. The included mini-games are:
  1, 2, 3: Mad Maze*
  4, 5: Space Attack*
  6: Auto Cross
  7: Stomp
  8: Speedball

  *: higher number indicates higher difficulty

  display layout, where number yx is lamp R(y).O(x)

       00    02    04
    10 01 12 03 14 05 16
       11    13    15
    20 21 22 23 24 25 26
       31    33    35
    30 41 32 43 34 45 36
       51    53    55
    40 61 42 63 44 65 46
       71    73    75
    50 60 52 62 54 64 56
       70    72    74

*******************************************************************************/

class splitsec_state : public hh_tms1k_state
{
public:
	splitsec_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void splitsec(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void splitsec_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void splitsec_state::write_r(u32 data)
{
	// R8: speaker out
	m_speaker->level_w(data >> 8 & 1);

	// R9,R10: input mux
	m_inp_mux = data >> 9 & 3;

	// R0-R7: led select
	m_r = data;
	update_display();
}

void splitsec_state::write_o(u16 data)
{
	// O0-O6: led state
	// O7: N/C
	m_o = data & 0x7f;
	update_display();
}

u8 splitsec_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(2);
}

// inputs

static INPUT_PORTS_START( splitsec )
	PORT_START("IN.0") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R10
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_SELECT )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void splitsec_state::splitsec(machine_config &config)
{
	// basic machine hardware
	TMS1400(config, m_maincpu, 475000); // approximation - RC osc. R=24K, C=100pF
	m_maincpu->read_k().set(FUNC(splitsec_state::read_k));
	m_maincpu->write_r().set(FUNC(splitsec_state::write_r));
	m_maincpu->write_o().set(FUNC(splitsec_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 7);
	config.set_default_layout(layout_splitsec);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( splitsec )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mp7314", 0x0000, 0x1000, CRC(e94b2098) SHA1(f0fc1f56a829252185592a2508740354c50bedf8) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 557, "maincpu:opla", 0 )
	ROM_LOAD( "tms1400_splitsec_output.pla", 0, 557, CRC(7539283b) SHA1(f791fa98259fc10c393ff1961d4c93040f1a2932) )
ROM_END





/*******************************************************************************

  Parker Brothers Lost Treasure: The Electronic Deep-Sea Diving Game,
  Featuring The Electronic Dive-Control Center
  * TMS1100 M34038-NLL (die label: 1100E, M34038)
  * 11 LEDs, 4-bit sound

  This is a board game. The electronic accessory is the emulated part here.

*******************************************************************************/

class lostreas_state : public hh_tms1k_state
{
public:
	lostreas_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void lostreas(machine_config &config);

private:
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void lostreas_state::write_r(u32 data)
{
	// R0-R10: leds
	m_display->matrix(1, data);
}

void lostreas_state::write_o(u16 data)
{
	// O0-O3: input mux
	m_inp_mux = data & 0xf;

	// O4: 330 ohm resistor - speaker out
	// O5: 220 ohm resistor - speaker out
	// O6: 180 ohm resistor - speaker out
	// O7:  68 ohm resistor - speaker out
	m_speaker->level_w(data >> 4 & 0xf);
}

u8 lostreas_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(4);
}

// inputs

/* physical button layout and labels are like this:
  (note: Canadian version differs slightly to accommodate dual-language)

    [N-S(gold)]    [1] [2] [3]    [AIR]
    [E-W(gold)]    [4] [5] [6]    [UP]
    [N-S(silv)]    [7] [8] [9]    [$ VALUE]
    [E-W(silv)]                   [CLEAR]
*/

static INPUT_PORTS_START( lostreas )
	PORT_START("IN.0") // O0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")

	PORT_START("IN.1") // O1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")

	PORT_START("IN.2") // O2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_DEL) PORT_NAME("Clear")
	PORT_BIT( 0x04, 0x04, IPT_CUSTOM ) PORT_CONDITION("FAKE", 0x03, NOTEQUALS, 0x00) // air/up
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_V) PORT_NAME("$ Value")

	PORT_START("IN.3") // O3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("E-W (silver)")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("N-S (silver)")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("E-W (gold)")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("N-S (gold)")

	PORT_START("FAKE") // Air/Up buttons share the same position on the matrix
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Air")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_U) PORT_NAME("Up")
INPUT_PORTS_END

// config

void lostreas_state::lostreas(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 425000); // approximation - RC osc. R=39K, C=47pF
	m_maincpu->read_k().set(FUNC(lostreas_state::read_k));
	m_maincpu->write_r().set(FUNC(lostreas_state::write_r));
	m_maincpu->write_o().set(FUNC(lostreas_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 11);
	config.set_default_layout(layout_lostreas);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);

	// set volume levels
	static const double speaker_levels[0x10] =
		{ 1.0/16.0, 1.0/15.0, 1.0/14.0, 1.0/13.0, 1.0/12.0, 1.0/11.0, 1.0/10.0, 1.0/9.0, 1.0/8.0, 1.0/7.0, 1.0/6.0, 1.0/5.0, 1.0/4.0, 1.0/3.0, 1.0/2.0, 1.0 };
	m_speaker->set_levels(16, speaker_levels);
}

// roms

ROM_START( lostreas )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "m34038", 0x0000, 0x0800, CRC(4c996f63) SHA1(ebbaa8b2f909f4300887aa2dbdb7185eedc75d3f) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_lostreas_output.pla", 0, 365, CRC(c62d850f) SHA1(d25974e6901eb10c52cdda12e6d4a13e26745e6f) )
ROM_END





/*******************************************************************************

  Playskool Alphie
  * TMS1000 (label not known yet)
  * 5 LEDs, 1-bit sound

  This is an educational toy robot for young kids. It has 2 sliding arms:
  the left arm(Alphie's right arm) is for questions, the other for answers.
  Cardboard inlays are used for arm position labels.

  There are 4 play modes:
  - S symbol: Alphie answers questions. The answers are always the same,
    no matter the inlay: Q1=A3, Q2=A5, Q3=A4, Q4=A1, Q5=A2.
  - * symbol: used with Lunar Landing board game
  - X symbol: used with Robot Land board game
  - music note: play a selection of 5 tunes

*******************************************************************************/

class alphie_state : public hh_tms1k_state
{
public:
	alphie_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void alphie(machine_config &config);

private:
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void alphie_state::write_r(u32 data)
{
	// R1-R5, input mux (using d5 for Vss)
	m_inp_mux = (data >> 1 & 0x1f) | 0x20;

	// R6-R10: leds
	m_display->matrix(1, data >> 6);

	// R0: power off on falling edge (turn back on with button)
	if (~data & m_r & 1)
		power_off();

	m_r = data;
}

void alphie_state::write_o(u16 data)
{
	// O?: speaker out
	m_speaker->level_w(data & 1);
}

u8 alphie_state::read_k()
{
	// K: multiplexed inputs, rotated matrix
	return read_rotated_inputs(6);
}

// inputs

static INPUT_PORTS_START( alphie )
	PORT_START("IN.0") // K1
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), true)

	PORT_START("IN.1") // K2
	PORT_CONFNAME( 0x1f, 0x01, "Question" )
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_CONFSETTING(    0x04, "3" )
	PORT_CONFSETTING(    0x08, "4" )
	PORT_CONFSETTING(    0x10, "5" )

	PORT_START("IN.2") // K4
	PORT_CONFNAME( 0x1f, 0x01, "Answer" )
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_CONFSETTING(    0x04, "3" )
	PORT_CONFSETTING(    0x08, "4" )
	PORT_CONFSETTING(    0x10, "5" )

	PORT_START("IN.3") // K8
	PORT_CONFNAME( 0x0f, 0x01, "Activity" )
	PORT_CONFSETTING(    0x01, "Questions" )
	PORT_CONFSETTING(    0x02, "Lunar Landing" )
	PORT_CONFSETTING(    0x04, "Robot Land" )
	PORT_CONFSETTING(    0x08, "Tunes" )

	PORT_START("SWITCH") // fake
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::switch_prev<1>), 0x1f) PORT_NAME("Question Arm Up")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::switch_next<1>), 0x1f) PORT_NAME("Question Arm Down")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::switch_prev<2>), 0x1f) PORT_NAME("Answer Arm Up")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::switch_next<2>), 0x1f) PORT_NAME("Answer Arm Down")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::switch_prev<3>), 0x0f) PORT_NAME("Activity Selector Left")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::switch_next<3>), 0x0f) PORT_NAME("Activity Selector Right")
INPUT_PORTS_END

// config

// output PLA is guessed
static const u16 alphie_output_pla[0x20] =
{
	0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
	0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
};

void alphie_state::alphie(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 350000); // approximation
	m_maincpu->set_output_pla(alphie_output_pla);
	m_maincpu->read_k().set(FUNC(alphie_state::read_k));
	m_maincpu->write_r().set(FUNC(alphie_state::write_r));
	m_maincpu->write_o().set(FUNC(alphie_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 5);
	config.set_default_layout(layout_alphie);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( alphie )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "us4280809", 0x0000, 0x0400, CRC(f8f14013) SHA1(bf31b929fcbcb189bbe4623104e1da0a639b5954) ) // from patent US4280809, should be good

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, BAD_DUMP CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) ) // not in patent description
	ROM_REGION( 365, "maincpu:opla", ROMREGION_ERASE00 )
	ROM_LOAD( "tms1000_alphie_output.pla", 0, 365, NO_DUMP ) // "
ROM_END





/*******************************************************************************

  Tandy Championship Football (model 60-2150)
  * PCB label: CYG-316
  * TMS1100NLL MP1193 (die label: 1100B, MP1193)
  * 7-digit 7seg LED display + LED grid, 1-bit sound

  Another clone of Mattel Football II. The original manufacturer is unknown, but
  suspected to be Conic/E.R.S.(Electronic Readout Systems).

*******************************************************************************/

class tcfball_state : public hh_tms1k_state
{
public:
	tcfball_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void tcfball(machine_config &config);

protected:
	void update_display();
	virtual void write_r(u32 data);
	virtual void write_o(u16 data);
	virtual u8 read_k();
};

// handlers

void tcfball_state::update_display()
{
	// R8 enables leds, R9 enables digits
	u16 mask = ((m_r >> 9 & 1) * 0x7f) | ((m_r >> 8 & 1) * 0x780);
	u16 sel = ((m_r & 0x7f) | (m_r << 7 & 0x780)) & mask;

	m_display->matrix(sel, m_o);
}

void tcfball_state::write_r(u32 data)
{
	// R10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// R5-R7: input mux
	m_inp_mux = data >> 5 & 7;

	// R8+R0-R3: select led
	// R9+R0-R6: select digit
	m_r = data;
	update_display();
}

void tcfball_state::write_o(u16 data)
{
	// O0-O7: digit segments/led data
	m_o = data;
	update_display();
}

u8 tcfball_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(3);
}

// inputs

static INPUT_PORTS_START( tcfball )
	PORT_START("IN.0") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY

	PORT_START("IN.1") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START2 ) PORT_NAME("Score")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Status")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Pass")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Kick")

	PORT_START("IN.2") // R7
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x08, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" ) // college
	PORT_CONFSETTING(    0x08, "2" ) // professional
INPUT_PORTS_END

// config

void tcfball_state::tcfball(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 375000); // approximation - RC osc. R=56K, C=24pF
	m_maincpu->read_k().set(FUNC(tcfball_state::read_k));
	m_maincpu->write_r().set(FUNC(tcfball_state::write_r));
	m_maincpu->write_o().set(FUNC(tcfball_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(11, 8);
	m_display->set_segmask(0x77, 0x7f);
	m_display->set_segmask(0x08, 0xff); // R3 has DP
	m_display->set_bri_levels(0.003, 0.03); // offense leds are brighter
	config.set_default_layout(layout_tcfball);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( tcfball )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1193", 0x0000, 0x0800, CRC(7d9f446f) SHA1(bb6af47b42d989494f21475a73f072cddf58c99f) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_tcfball_output.pla", 0, 365, CRC(26b2996e) SHA1(df0e706c552bf74123aa65e71b0c9b4d33cddb2b) )
ROM_END





/*******************************************************************************

  Tandy Championship Football (model 60-2151)
  * TMS1100NLL MP1183 (no decap)
  * 7-digit 7seg LED display + LED grid, 1-bit sound

  The hardware is almost the same as the MP1193 one, they added an extra row of leds.

  known releases:
  - World(1): Superbowl XV Football, published by E.R.S.(Electronic Readout Systems)
  - World(2): Super-Pro Football, no brand
  - USA: Championship Football (model 60-2151), published by Tandy

*******************************************************************************/

class tcfballa_state : public tcfball_state
{
public:
	tcfballa_state(const machine_config &mconfig, device_type type, const char *tag) :
		tcfball_state(mconfig, type, tag)
	{ }

	void tcfballa(machine_config &config);
};

// handlers: uses the ones in tcfball_state

// inputs

static INPUT_PORTS_START( tcfballa )
	PORT_INCLUDE( tcfball )

	PORT_MODIFY("IN.1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Display")
INPUT_PORTS_END

// config

void tcfballa_state::tcfballa(machine_config &config)
{
	tcfball(config);

	// basic machine hardware
	m_maincpu->set_clock(375000); // approximation - RC osc. R=47K, C=50pF

	config.set_default_layout(layout_tcfballa);
}

// roms

ROM_START( tcfballa )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1183", 0x0000, 0x0800, CRC(2a4db1d5) SHA1(5df15d1115bb425578ad522d607a582dd478f35c) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, BAD_DUMP CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) ) // not verified
	ROM_REGION( 365, "maincpu:opla", ROMREGION_ERASE00 )
	ROM_LOAD( "tms1100_tcfballa_output.pla", 0, 365, NO_DUMP )

	ROM_REGION16_LE( 0x40, "maincpu:opla_b", ROMREGION_ERASE00 ) // verified, electronic dump
	ROM_LOAD16_BYTE( "tms1100_tcfballa_output.bin", 0, 0x20, CRC(887ad9a5) SHA1(a2bac48f555df098c1f1f0fa663e5bcb989b8987) )
ROM_END





/*******************************************************************************

  Tandy Computerized Arcade (model 60-2159)
  * PCB label: HP-805 or CDC-805
  * TMS1100 MCU, label MP1272 or CD7282SL
  * 12 lamps behind buttons, 1-bit sound

  known releases:
  - World: Tandy-12: Computerized Arcade, published by Tandy, model 60-2159.
  - World: Computerized Arcade, Radio Shack brand, also model 60-2159 and same
    hardware as above. "Tandy-12" on the side of the handheld was changed to
    "Radio Shack-12", but there's no title prefix on the box.
  - World: Computerized Arcade, Radio Shack brand, model 60-2159A, COP421 MCU,
    see hh_cop400.cpp driver
  - World: Computerized Arcade, Radio Shack brand, model 60-2495, 28-pin Motorola
    LSC417570DW, should be 68HC05 family
  - Mexico: Fabuloso Fred, published by Ensueño Toys (also released as
    9-button version, a clone of Mego Fabulous Fred)

  This handheld contains 12 minigames. It looks and plays like Takatoku Toys'
  Game Robot 9 from 1980 (aka Mego's Fabulous Fred).

  Some of the games require accessories included with the toy (eg. the Baseball
  game is played with a board representing the playing field). To start a game,
  hold the [SELECT] button, then press [START] when the game button lights up.
  As always, refer to the official manual for more information.

  See below at the input defs for a list of the games.

*******************************************************************************/

class comparc_state : public hh_tms1k_state
{
public:
	comparc_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void comparc(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void comparc_state::update_display()
{
	m_display->matrix(1, (m_o << 1 & 0x1fe) | (m_r << 9 & 0x1e00));
}

void comparc_state::write_r(u32 data)
{
	// R10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// R5-R9: input mux
	m_inp_mux = data >> 5 & 0x1f;

	// R0-R3: button lamps 9-12
	m_r = data;
	update_display();
}

void comparc_state::write_o(u16 data)
{
	// O0-O7: button lamps 1-8
	m_o = data;
	update_display();
}

u8 comparc_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(5);
}

// inputs

/* physical button layout and labels are like this:

        REPEAT-2              SPACE-2
          [O]     OFF--ON       [O]

    [purple]1     [blue]5       [l-green]9
    ORGAN         TAG-IT        TREASURE HUNT

    [l-orange]2   [turquoise]6  [red]10
    SONG WRITER   ROULETTE      COMPETE

    [pink]3       [yellow]7     [violet]11
    REPEAT        BASEBALL      FIRE AWAY

    [green]4      [orange]8     [brown]12
    TORPEDO       REPEAT PLUS   HIDE 'N SEEK

          [O]        [O]        [O]
         START      SELECT    PLAY-2/HIT-7
*/

static INPUT_PORTS_START( comparc )
	PORT_START("IN.0") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_EQUALS) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("Button 12")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("Button 11")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("Button 10")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("Button 9")

	PORT_START("IN.1") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Space-2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("Play-2/Hit-7")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("Select")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Start")

	PORT_START("IN.2") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("Repeat-2")
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Button 4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Button 3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Button 2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Button 1")

	PORT_START("IN.4") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Button 8")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Button 7")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Button 6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Button 5")
INPUT_PORTS_END

// config

// output PLA is not decapped, this was made by hand
static const u16 comparc_output_pla[0x20] =
{
	// these are certain
	0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40,
	0x80, 0x00, 0x00, 0x00, 0x00,

	// rest is unused?
	0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

void comparc_state::comparc(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 375000); // approximation - RC osc. R=39K, C=47pF
	m_maincpu->set_output_pla(comparc_output_pla);
	m_maincpu->read_k().set(FUNC(comparc_state::read_k));
	m_maincpu->write_r().set(FUNC(comparc_state::write_r));
	m_maincpu->write_o().set(FUNC(comparc_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 13);
	config.set_default_layout(layout_comparc);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( comparc )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "cd7282sl", 0x0000, 0x0800, CRC(a10013dd) SHA1(42ebd3de3449f371b99937f9df39c240d15ac686) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, BAD_DUMP CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) ) // not verified
	ROM_REGION( 365, "maincpu:opla", ROMREGION_ERASE00 )
	ROM_LOAD( "tms1100_comparc_output.pla", 0, 365, NO_DUMP )
ROM_END





/*******************************************************************************

  Tandy (Radio Shack division) Monkey See (1982 version)
  * TMS1000 MP0271 (die label: 1000E, MP0271), only half of ROM space used
  * 2 LEDs(one red, one green), 1-bit sound

  This is the TMS1000 version, the one from 1977 has a MM5780.
  To play, enter an equation followed by the ?-key, and the calculator will
  tell you if it was right(green) or wrong(red). For example 1+2=3?

  known releases:
  - USA(1): Monkey See, published by Tandy
  - USA(2): Heathcliff, published by McNaught Syndicate in 1983

*******************************************************************************/

class monkeysee_state : public hh_tms1k_state
{
public:
	monkeysee_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void monkeysee(machine_config &config);

private:
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void monkeysee_state::write_r(u32 data)
{
	// R0-R4: input mux
	m_inp_mux = data & 0x1f;

	// R5: speaker out
	m_speaker->level_w(data >> 5 & 1);
}

void monkeysee_state::write_o(u16 data)
{
	// O6,O7: leds
	// other: N/C
	m_display->matrix(1, data >> 6);
}

u8 monkeysee_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(5);
}

// inputs

static INPUT_PORTS_START( monkeysee )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("?")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_NAME("C")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
INPUT_PORTS_END

// config

void monkeysee_state::monkeysee(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 250000); // approximation - RC osc. R=68K, C=47pF
	m_maincpu->read_k().set(FUNC(monkeysee_state::read_k));
	m_maincpu->write_r().set(FUNC(monkeysee_state::write_r));
	m_maincpu->write_o().set(FUNC(monkeysee_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 2);
	config.set_default_layout(layout_monkeysee);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( monkeysee )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp0271", 0x0000, 0x0400, CRC(acab0f05) SHA1(226f7688caf4a94a88241d3b61ddc4254e4a918c) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_monkeysee_micro.pla", 0, 867, CRC(368d878f) SHA1(956e700a04f453c1610cfdb974fce898ba4cf01f) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_monkeysee_output.pla", 0, 365, CRC(8a010e89) SHA1(3ffbabc5d6c9b34cc06d290817d15b2be42d8b17) )
ROM_END





/*******************************************************************************

  Tandy 3 in 1 Sports Arena (model 60-2178)
  * PCB label: HP-804
  * TMS1100 (just a datestamp label (8331), die label: 1100B, MP1231)
  * 2x2-digit 7seg LED display + 45+2 other LEDs, 1-bit sound

  For Tandy Sports Arena (model 60-2158), see cmsport, this is a different game.
  This version is very similar to ssports4 released a few years earlier.

  3 overlays were included for the games (Tandy calls them graphic sheets),
  MAME external artwork is needed for those.

  It is always in 2-player head-to-head mode, the Player switch is just meant
  for allowing the other player to have full control over 2 defense spots.

*******************************************************************************/

class t3in1sa_state : public hh_tms1k_state
{
public:
	t3in1sa_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void t3in1sa(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void t3in1sa_state::update_display()
{
	m_display->matrix(m_r, m_o | (m_r << 6 & 0x100));
}

void t3in1sa_state::write_r(u32 data)
{
	// R2: led data high
	// R3-R7: led select
	// R0,R1,R8,R9: digit select
	m_r = data;
	update_display();

	// R0,R1,R5,R7-R9: input mux
	m_inp_mux = (data & 3) | (data >> 3 & 4) | (data >> 4 & 0x38);

	// R10: speaker out
	m_speaker->level_w(BIT(data, 10));
}

void t3in1sa_state::write_o(u16 data)
{
	// O0-O7: led data low
	m_o = data;
	update_display();
}

u8 t3in1sa_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(6);
}

// inputs

/* physical button layout and labels are like this:

                  ↑
     [ ]         [ ]         [ ]
  TEAM-MATE                  PASS
          ←[ ]         [ ]→  shoot
        ↗                   ↖
     [ ]         [ ]         [ ]
    STATUS        ↓          KICK
                             pass
                             shoot

Game and difficulty switches are under the yellow buttons,
player switch is under the red buttons. P1 is yellow, P2 is red.

*/

static INPUT_PORTS_START( t3in1sa )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL PORT_16WAY

	PORT_START("IN.2") // R5
	PORT_CONFNAME( 0x01, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" ) // AMATEUR
	PORT_CONFSETTING(    0x01, "2" ) // PRO
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x08, 0x08, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x08, "1" )
	PORT_CONFSETTING(    0x00, "2" )

	PORT_START("IN.3") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x0e, 0x08, "Game Select" )
	PORT_CONFSETTING(    0x08, "Football" ) // F
	PORT_CONFSETTING(    0x04, "Basketball" ) // B
	PORT_CONFSETTING(    0x02, "Soccer" ) // S

	PORT_START("IN.4") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("P1 Up-Left / Kick")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P1 Up-Right / Status")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Pass / Shoot")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Team-Mate")

	PORT_START("IN.5") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_COCKTAIL PORT_NAME("P2 Up-Left / Kick")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_COCKTAIL PORT_NAME("P2 Up-Right / Status")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_COCKTAIL PORT_NAME("P2 Pass / Shoot")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Team-Mate")
INPUT_PORTS_END

// config

void t3in1sa_state::t3in1sa(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 350000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_k().set(FUNC(t3in1sa_state::read_k));
	m_maincpu->write_r().set(FUNC(t3in1sa_state::write_r));
	m_maincpu->write_o().set(FUNC(t3in1sa_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 9);
	m_display->set_segmask(0x303, 0x7f);
	m_display->set_bri_levels(0.005, 0.05); // offense leds are brighter
	config.set_default_layout(layout_t3in1sa);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( t3in1sa )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1231", 0x0000, 0x0800, CRC(1c24e5c2) SHA1(0b6c2edea27eba15e890d82475b91a5e9ef6c4b9) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_t3in1sa_output.pla", 0, 365, CRC(de82a294) SHA1(7187666a510919b90798b92b9104ac5d6820d559) )
ROM_END





/*******************************************************************************

  Tandy (Micronta) VoxClock 3 (sold by Radio Shack, model 63-906)
  * PCB label: VOXCLOCK 3
  * TMS1100 MP1343 (die label: 1100E, MP1343)
  * TMS5110AN2L-1 speech chip, 4KB VSM CM72010NL (die label: T0355C, 72010U)
  * 4-digit 7seg LED display, 6 other LEDs

  Even though it has a 60 Hz inputline, it doesn't use it to sync the clock.
  Instead, it relies on the MCU frequency, which is not very accurate when
  using a simple R/C osc. In 60 Hz mode, it expects a CPU clock of around
  375 kHz, and in 50 Hz mode around 369 kHz.

  Micronta is not a company, but one of the Radio Shack house brands.
  Schematics are included in the manual, they also mention a CM72005 VSM.

  Spartus AVT from 1982 is nearly the same as VoxClock 3.

*******************************************************************************/

class vclock3_state : public hh_tms1k_state
{
public:
	vclock3_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag),
		m_tms5100(*this, "tms5100"),
		m_tms6100(*this, "tms6100"),
		m_ac_power(*this, "ac_power")
	{ }

	void vclock3(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(switch_hz) { m_ac_power->set_clock(newval ? 50 : 60); }

private:
	required_device<tms5110_device> m_tms5100;
	required_device<tms6100_device> m_tms6100;
	required_device<clock_device> m_ac_power;

	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void vclock3_state::update_display()
{
	m_display->matrix(m_r, m_o | (m_r << 2 & 0x180));
}

void vclock3_state::write_r(u32 data)
{
	// R0-R4,R8: input mux
	m_inp_mux = (data & 0x1f) | (data >> 3 & 0x20);

	// R0-R3: select digit
	// R5,R6: led data
	m_r = data;
	update_display();

	// R7: TMS5100 PDC
	m_tms5100->pdc_w(BIT(data, 7));

	// R10: speaker out
	m_speaker->level_w(BIT(data, 10));
}

void vclock3_state::write_o(u16 data)
{
	// O0-O3: TMS5100 CTL
	m_tms5100->ctl_w(data & 0xf);

	// O0-O6: digit segments
	m_o = data & 0x7f;
	update_display();
}

u8 vclock3_state::read_k()
{
	// K1-K4: multiplexed inputs
	u8 data = read_inputs(6) & 7;

	// K4: TMS5100 CTL1
	// K8: AC power osc
	data |= m_tms5100->ctl_r() << 2 & 4;
	data |= (m_inputs[6]->read() & m_ac_power->signal_r()) << 3;
	return data;
}

// inputs

/* physical button layout and labels are like this:

    [  ]       [  ]       [  ]       [  ]
  CALENDAR-SET-TIME       CHIME    ANNOUNCE

    [  ]       [  ]       [  ]       [  ]
  HOUR FWD↑   MIN FWD↑   SET ALARM 1-ON/OFF

    [  ]       [  ]       [  ]       [  ]
  HOUR REV↓   MIN REV↓   SET ALARM 2-ON/OFF

    [  ]       ---------------    LOW[  ]HIGH
  SENTINEL     LOW VOLUME HIGH      DIMMER

     CALENDAR
  [             ]                [      ]
    SNOOZE/DATE                    TIME
*/

static INPUT_PORTS_START( vclock3 )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Time")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("Snooze / Date")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F2) PORT_NAME("Alarm 2 On/Off")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F1) PORT_NAME("Alarm 1 On/Off")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Announce")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("Set Alarm 2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("Set Alarm 1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("Chime")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("Minute Reverse")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("Minute Forward")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Set Time")

	PORT_START("IN.4") // R4 (factory-set jumpers)
	PORT_CONFNAME( 0x01, 0x00, "AC Frequency") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vclock3_state::switch_hz), 0)
	PORT_CONFSETTING(    0x01, "50 Hz" )
	PORT_CONFSETTING(    0x00, "60 Hz" )
	PORT_CONFNAME( 0x02, 0x00, "Chime / Announce" )
	PORT_CONFSETTING(    0x02, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )
	PORT_CONFNAME( 0x04, 0x00, "Recall" )
	PORT_CONFSETTING(    0x04, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN.5") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_NAME("Hour Reverse")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_NAME("Hour Forward")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("Set Calendar")

	PORT_START("IN.6")
	PORT_CONFNAME( 0x01, 0x01, "Power Source" )
	PORT_CONFSETTING(    0x00, "Battery" )
	PORT_CONFSETTING(    0x01, "Mains" )
	PORT_CONFNAME( 0x02, 0x00, "Battery Status" )
	PORT_CONFSETTING(    0x02, "Low" )
	PORT_CONFSETTING(    0x00, DEF_STR( Normal ) )
INPUT_PORTS_END

// config

void vclock3_state::vclock3(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 375000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_k().set(FUNC(vclock3_state::read_k));
	m_maincpu->write_r().set(FUNC(vclock3_state::write_r));
	m_maincpu->write_o().set(FUNC(vclock3_state::write_o));

	CLOCK(config, m_ac_power, 60); // from mains power

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 9);
	m_display->set_segmask(0xf, 0x7f);
	m_display->set_segmask(0x1, 0x06); // 1st digit only has segments B,C
	config.set_default_layout(layout_vclock3);

	// sound hardware
	SPEAKER(config, "mono").front_center();

	TMS5110A(config, m_tms5100, 640000); // approximation - RC osc. R=47K, C=47pF
	m_tms5100->m0().set(m_tms6100, FUNC(tms6100_device::m0_w));
	m_tms5100->m1().set(m_tms6100, FUNC(tms6100_device::m1_w));
	m_tms5100->addr().set(m_tms6100, FUNC(tms6100_device::add_w));
	m_tms5100->data().set(m_tms6100, FUNC(tms6100_device::data_line_r));
	m_tms5100->romclk().set(m_tms6100, FUNC(tms6100_device::clk_w));
	m_tms5100->add_route(ALL_OUTPUTS, "mono", 0.25);

	TMS6100(config, m_tms6100, 640000/4);

	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( vclock3 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1343.u1", 0x0000, 0x0800, CRC(d8fac397) SHA1(65003ec70ae3d45296c08b10aff85ca29c0f573e) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_vclock3_output.pla", 0, 365, CRC(e5b0e95b) SHA1(6d624bfefd302fd04c02177081cea9416ba344ee) )

	ROM_REGION( 0x1000, "tms6100", 0 )
	ROM_LOAD( "cm72010.u3", 0x0000, 0x1000, CRC(2054847c) SHA1(716592f4cd01a9edf16b1061431bd6dc934d9053) )
ROM_END





/*******************************************************************************

  Technasonic Weight Talker
  * PCB label: BHP_8338A8
  * TMS1100 MP1362 (no decap)
  * TMS5110AN2L-1 speech chip, 16KB VSM CM62088N2L
  * CD40114BE (4x16 RAM, battery-backed)

  There's also an older version (BHP_8338A2 PCB, M34137N2, CM62074). It can be
  identified by the missing (unpopulated) language switch.

  It has a normal mechanical scale hidden from view, with a quadrature encoder.
  In MAME, it's simulated with a dial control. Turn it left to increase pressure,
  right to decrease. After around 0.5s of inactivity, it will tell your 'weight'.

*******************************************************************************/

class wtalker_state : public hh_tms1k_state
{
public:
	wtalker_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag),
		m_nvram(*this, "nvram", 0x10, ENDIANNESS_BIG),
		m_tms5100(*this, "tms5100"),
		m_tms6100(*this, "tms6100")
	{ }

	void wtalker(machine_config &config);

	ioport_value sensor_r() { return m_dial & 1; }
	ioport_value pulse_r() { return (m_pulse > machine().time()) ? 1 : 0; }

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	memory_share_creator<u8> m_nvram;
	required_device<tms5110_device> m_tms5100;
	required_device<tms6100_device> m_tms6100;

	attotime m_pulse;
	u8 m_dial = 0;
	u8 m_ram_address = 0;

	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();

	TIMER_DEVICE_CALLBACK_MEMBER(sense_weight);
};

void wtalker_state::machine_start()
{
	hh_tms1k_state::machine_start();

	// register for savestates
	save_item(NAME(m_pulse));
	save_item(NAME(m_dial));
	save_item(NAME(m_ram_address));
}

// handlers

TIMER_DEVICE_CALLBACK_MEMBER(wtalker_state::sense_weight)
{
	const u8 mask = 3;
	u8 inp = m_inputs[7]->read() & mask;

	// short pulse on rising edge or falling edge, depending on direction
	if (inp != m_dial && BIT(mask + inp - m_dial, 1) != BIT(inp, 0))
		m_pulse = machine().time() + attotime::from_usec(250);

	m_dial = inp;
}

void wtalker_state::write_r(u32 data)
{
	// R2,R3,R9: input mux part
	m_inp_mux = (m_inp_mux & 7) | (data << 1 & 0x18) | (data >> 4 & 0x20);

	// R8: TMS5100 PDC
	m_tms5100->pdc_w(BIT(data, 8));

	// R10: power off on rising edge
	if (data & ~m_r & 0x400)
		power_off();

	// R0: RAM ME
	// R1: RAM WE
	// R4-R7: RAM D-A

	// latch RAM address
	if (data & ~m_r & 1)
		m_ram_address = bitswap<4>(data,4,5,6,7);

	// write RAM
	if ((data & 3) == 3 && (m_r & 3) != 3)
		m_nvram[m_ram_address] = m_o & 0xf;

	m_r = data;
}

void wtalker_state::write_o(u16 data)
{
	// O4-O6: input mux part
	// O7: N/C
	m_inp_mux = (m_inp_mux & ~7) | (data >> 4 & 7);

	// O0-O3: TMS5100 CTL, RAM data
	m_tms5100->ctl_w(data & 0xf);
	m_o = data;
}

u8 wtalker_state::read_k()
{
	// K2-K8: multiplexed inputs
	u8 data = read_inputs(6) << 1 & 0xe;

	// read RAM (complemented)
	if ((m_r & 3) == 1)
		data |= ~m_nvram[m_ram_address] & 0xf;

	// K1: TMS5100 CTL1
	data |= m_tms5100->ctl_r() & 1;
	return data;
}

// inputs

static INPUT_PORTS_START( wtalker )
	PORT_START("IN.0") // O4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")

	PORT_START("IN.1") // O5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("Guest")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")

	PORT_START("IN.2") // O6
	PORT_CONFNAME( 0x01, 0x01, "Memory")
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
	PORT_CONFNAME( 0x02, 0x00, "Weight Unit")
	PORT_CONFSETTING(    0x02, "Kilogram" )
	PORT_CONFSETTING(    0x00, "Pound" )
	PORT_CONFNAME( 0x04, 0x00, "Shutdown Message")
	PORT_CONFSETTING(    0x04, "Good Bye" ) PORT_CONDITION("IN.5", 0x02, NOTEQUALS, 0x02)
	PORT_CONFSETTING(    0x00, "Have a Nice Day" ) PORT_CONDITION("IN.5", 0x02, NOTEQUALS, 0x02)
	PORT_CONFSETTING(    0x04, "Auf Wiedersehen" ) PORT_CONDITION("IN.5", 0x02, EQUALS, 0x02)
	PORT_CONFSETTING(    0x00, "Guten Tag" ) PORT_CONDITION("IN.5", 0x02, EQUALS, 0x02)

	PORT_START("IN.3") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(wtalker_state::sensor_r))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(wtalker_state::pulse_r))

	PORT_START("IN.4") // R3
	PORT_CONFNAME( 0x01, 0x00, "Battery Status" )
	PORT_CONFSETTING(    0x01, "Low" )
	PORT_CONFSETTING(    0x00, DEF_STR( Normal ) )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_CONFNAME( 0x02, 0x00, DEF_STR( Language ) ) // unpopulated switch
	PORT_CONFSETTING(    0x00, DEF_STR( English ) )
	PORT_CONFSETTING(    0x02, DEF_STR( German ) )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_ON ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), true)

	PORT_START("IN.7")
	PORT_BIT( 0x03, 0x00, IPT_DIAL ) PORT_SENSITIVITY(25) PORT_KEYDELTA(10)
INPUT_PORTS_END

// config

void wtalker_state::wtalker(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 550000); // approximation - RC osc. R=36K, C=33pF
	m_maincpu->read_k().set(FUNC(wtalker_state::read_k));
	m_maincpu->write_r().set(FUNC(wtalker_state::write_r));
	m_maincpu->write_o().set(FUNC(wtalker_state::write_o));

	TIMER(config, "sense_weight").configure_periodic(FUNC(wtalker_state::sense_weight), attotime::from_usec(1));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	// no visual feedback!

	// sound hardware
	SPEAKER(config, "mono").front_center();

	TMS5110A(config, m_tms5100, 640000); // approximation - trimpot
	m_tms5100->m0().set(m_tms6100, FUNC(tms6100_device::m0_w));
	m_tms5100->m1().set(m_tms6100, FUNC(tms6100_device::m1_w));
	m_tms5100->addr().set(m_tms6100, FUNC(tms6100_device::add_w));
	m_tms5100->data().set(m_tms6100, FUNC(tms6100_device::data_line_r));
	m_tms5100->romclk().set(m_tms6100, FUNC(tms6100_device::clk_w));
	m_tms5100->add_route(ALL_OUTPUTS, "mono", 0.25);

	TMS6100(config, m_tms6100, 640000/4);
}

// roms

ROM_START( wtalker )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1362", 0x0000, 0x0800, CRC(99247fad) SHA1(30e48f235f821491643554c9e58a72459cf1d834) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, BAD_DUMP CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) ) // not verified
	ROM_REGION( 365, "maincpu:opla", ROMREGION_ERASE00 )
	ROM_LOAD( "tms1100_wtalker_output.pla", 0, 365, NO_DUMP )

	ROM_REGION16_LE( 0x40, "maincpu:opla_b", ROMREGION_ERASE00 ) // verified, electronic dump
	ROM_LOAD16_BYTE( "tms1100_wtalker_output.bin", 0, 0x20, CRC(fb51ad7c) SHA1(5972665fbc154ebb18e4eb2663c6088643651489) )

	ROM_REGION( 0x4000, "tms6100", 0 )
	ROM_LOAD( "cm62088", 0x0000, 0x4000, CRC(0c7a6d26) SHA1(2dbdc54019d02531adbd3c7515a8995710a4267c) )
ROM_END





/*******************************************************************************

  Telesensory Systems, Inc.(TSI) Speech+
  * TMS1000 MCU, label TMS1007NL (die label: 1000B, 1007A)
  * TSI S14001A speech chip, GI S14007-A 2KB maskrom for samples
  * 9-digit 7seg LED display

  This is a speaking calculator for the blind, the instructions that came
  with it were on audio cassette. It was also released in 1978 by APH
  (American Printing House for the Blind).

*******************************************************************************/

class speechp_state : public hh_tms1k_state
{
public:
	speechp_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag),
		m_speech(*this, "speech")
	{ }

	void speechp(machine_config &config);

private:
	required_device<s14001a_device> m_speech;

	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void speechp_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void speechp_state::write_r(u32 data)
{
	// R5-R9: TSI C0-C5
	m_speech->data_w(data >> 5 & 0x3f);

	// R10: TSI START line
	m_speech->start_w(data >> 10 & 1);

	// R0-R9: input mux
	m_inp_mux = data & 0x3ff;

	// R0-R8: select digit
	m_r = data;
	update_display();
}

void speechp_state::write_o(u16 data)
{
	// O0-O7: digit segments
	m_o = data;
	update_display();
}

u8 speechp_state::read_k()
{
	// K: multiplexed inputs
	return m_inputs[10]->read() | (read_inputs(10) & 7);
}

// inputs

static INPUT_PORTS_START( speechp )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("S") // Swap
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_NAME("C") // Clear
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("+/-")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("%")

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SPACE) PORT_NAME("Speak")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")

	PORT_START("IN.7") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_M) PORT_NAME("M") // Memory
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.8") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME(u8"\u221a") // U+221A = √
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.9") // R9
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x04, 0x00, "Verbose" )
	PORT_CONFSETTING(    0x04, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN.10") // K8
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x08, 0x00, "Battery Status" )
	PORT_CONFSETTING(    0x08, "Low" )
	PORT_CONFSETTING(    0x00, DEF_STR( Normal ) )
INPUT_PORTS_END

// config

void speechp_state::speechp(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 400000); // approximation - RC osc. R=39K, C=47pF
	m_maincpu->read_k().set(FUNC(speechp_state::read_k));
	m_maincpu->write_r().set(FUNC(speechp_state::write_r));
	m_maincpu->write_o().set(FUNC(speechp_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	m_display->set_segmask(0x1ff, 0xff);
	config.set_default_layout(layout_speechp);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	S14001A(config, m_speech, 25000); // approximation
	m_speech->add_route(ALL_OUTPUTS, "mono", 0.75);
}

// roms

ROM_START( speechp )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tms1007nl", 0x0000, 0x0400, CRC(c2669d5c) SHA1(7943d6f39508a9a82bc21e4fe34a5b9f86e3add2) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common1_micro.pla", 0, 867, CRC(4becec19) SHA1(3c8a9be0f00c88c81f378b76886c39b10304f330) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_speechp_output.pla", 0, 365, CRC(e1b4197f) SHA1(258f4276a9f15c9bfbfa58df2f7202aed1542fdc) )

	ROM_REGION( 0x0800, "speech", 0 )
	ROM_LOAD("s14007-a", 0x0000, 0x0800, CRC(543b46d4) SHA1(99daf7fe3354c378b4bd883840c9bbd22b22ebe7) )
ROM_END





/*******************************************************************************

  Texas Instruments SR-16 (1974, first consumer product with TMS1000 series MCU)
  * TMS1000 MCU label TMS1001NL (die label: 1000, 1001A)
  * 12-digit 7seg LED display

  Texas Instruments SR-16 II (1975 version)
  * TMS1000 MCU label TMS1016NL (die label: 1000B, 1016A)
  * notes: cost-reduced 'sequel', [10^x] was removed, and [pi] was added.

*******************************************************************************/

class tisr16_state : public hh_tms1k_state
{
public:
	tisr16_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void tisr16(machine_config &config);

private:
	void update_display();
	void write_o(u16 data);
	void write_r(u32 data);
	u8 read_k();
};

// handlers

void tisr16_state::update_display()
{
	m_display->matrix(m_r, m_o);

	// exponent sign is from R10 O1, and R10 itself only uses segment G
	u8 r10 = m_display->read_row(10);
	m_display->write_row(11, r10 << 5 & 0x40);
	m_display->write_row(10, r10 & 0x40);
}

void tisr16_state::write_r(u32 data)
{
	// R0-R10: input mux, select digit
	m_r = m_inp_mux = data;
	update_display();
}

void tisr16_state::write_o(u16 data)
{
	// O0-O7: digit segments
	m_o = data;
	update_display();
}

u8 tisr16_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(11);
}

// inputs

static INPUT_PORTS_START( tisr16 )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_END) PORT_NAME("RCL")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("+/-")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_NAME("EE")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME(u8"Σ")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_HOME) PORT_NAME("STO")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")

	PORT_START("IN.6") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_NAME("1/x")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_NAME(u8"yˣ")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")

	PORT_START("IN.7") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_NAME(u8"x²")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")

	PORT_START("IN.8") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_NAME(u8"10ˣ")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_NAME(u8"eˣ")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")

	PORT_START("IN.9") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_NAME(u8"\u221ax") // U+221A = √
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")

	PORT_START("IN.10") // R10
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_NAME("C")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_NAME("log")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_NAME("ln(x)")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( tisr16ii )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_NAME("C")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CD")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("+/-")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_NAME(u8"x²")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_NAME("log")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_NAME("EE")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")

	PORT_START("IN.6") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_NAME(u8"\u221ax") // U+221A = √
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")

	PORT_START("IN.7") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_NAME("ln(x)")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_HOME) PORT_NAME("STO")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")

	PORT_START("IN.8") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_NAME("1/x")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_END) PORT_NAME("RCL")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")

	PORT_START("IN.9") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_NAME(u8"eˣ")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME(u8"Σ")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")

	PORT_START("IN.10") // R10
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_NAME(u8"yˣ")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_NAME(u8"π")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
INPUT_PORTS_END

// config

void tisr16_state::tisr16(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 350000); // approximation - RC osc. R=43K, C=68pf (note: tisr16ii MCU RC osc. is different: R=30K, C=100pf, same freq)
	m_maincpu->read_k().set(FUNC(tisr16_state::read_k));
	m_maincpu->write_o().set(FUNC(tisr16_state::write_o));
	m_maincpu->write_r().set(FUNC(tisr16_state::write_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(12, 8);
	m_display->set_segmask(0xfff, 0xff);
	config.set_default_layout(layout_tisr16);

	// no sound!
}

// roms

ROM_START( tisr16 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tms1001nl", 0x0000, 0x0400, CRC(b7ce3c1d) SHA1(95cdb0c6be31043f4fe06314ed41c0ca1337bc46) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_sr16_micro.pla", 0, 867, CRC(5b35019c) SHA1(730d3b9041ed76d57fbedd73b009477fe432b386) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_sr16_output.pla", 0, 365, CRC(29b08739) SHA1(d55f01e40a2d493d45ea422f12e63b01bcde08fb) )
ROM_END

ROM_START( tisr16ii )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tms1016nl", 0x0000, 0x0400, CRC(c07a7b27) SHA1(34ea4d3b59871e08db74f8c5bfb7ff00d1f0adc7) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_sr16ii_micro.pla", 0, 867, CRC(31b43e95) SHA1(6864e4c20f3affffcd3810dcefbc9484dd781547) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_sr16ii_output.pla", 0, 365, CRC(c45dfbd0) SHA1(5d588c1abc317134b51eb08ac3953f1009d80056) )
ROM_END





/*******************************************************************************

  Texas Instruments TI-1250/TI-1200 (1975 version), Spirit of '76
  * TMS0950 MCU label TMC0952NL, K0952 (die label: 0950A 0952)
  * 9-digit 7seg LED display

  TI-1250/TI-1200 (1976 version), TI-1400, TI-1450, TI-1205, TI-1255, LADY 1200, ABLE
  * TMS0970 MCU label TMS0972NL ZA0348, JP0972A (die label: 0970D-72A)
  * 8-digit 7seg LED display, or 9 digits with leftmost unused

  As seen listed above, the basic 4-function TMS0972 calculator MCU was used
  in many calculators. It was licensed to other manufacturers too, one funny
  example being a Concept 2000 Barbie handheld calculator.

  Some cheaper models lacked the memory buttons (the function itself still works).
  The ABLE series was for educational purposes, with each having a small subset of
  available buttons.

  TI-1270
  * TMS0970 MCU label TMC0974NL ZA0355, DP0974A (die label: 0970D-74A)
  * 8-digit 7seg LED display
  * notes: almost same hardware as TMS0972 TI-1250, minor scientific functions

*******************************************************************************/

class ti1250_state : public hh_tms1k_state
{
public:
	ti1250_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void ti1270(machine_config &config);
	void ti1250(machine_config &config);

private:
	void write_o(u16 data);
	void write_r(u32 data);
	u8 read_k();
};

// handlers

void ti1250_state::write_r(u32 data)
{
	// R0-R8: select digit
	m_display->matrix(data, m_o);
}

void ti1250_state::write_o(u16 data)
{
	// O1-O5,O7: input mux
	// O0-O7: digit segments
	m_inp_mux = (data >> 1 & 0x1f) | (data >> 2 & 0x20);
	m_o = data;
}

u8 ti1250_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(6);
}

// inputs

static INPUT_PORTS_START( ti1250 )
	PORT_START("IN.0") // O1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")

	PORT_START("IN.1") // O2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")

	PORT_START("IN.2") // O3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")

	PORT_START("IN.3") // O4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")

	PORT_START("IN.4") // O5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_NAME("C")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("CS") // named either CS(Change Sign) or +/-
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("%")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")

	PORT_START("IN.5") // O7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_NAME("MC")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_END) PORT_NAME("MR")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_INSERT) PORT_NAME("M-")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_HOME) PORT_NAME("M+")
INPUT_PORTS_END

static INPUT_PORTS_START( ti1270 )
	PORT_INCLUDE( ti1250 )

	PORT_MODIFY("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_NAME("CE/C")

	PORT_MODIFY("IN.4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_HOME) PORT_NAME("STO")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_END) PORT_NAME("RCL")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_NAME(u8"π")

	PORT_MODIFY("IN.5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_NAME("1/x")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_NAME(u8"x²")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_NAME(u8"\u221ax") // U+221A = √
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("+/-")
INPUT_PORTS_END

// config

void ti1250_state::ti1250(machine_config &config)
{
	// basic machine hardware
	TMS0950(config, m_maincpu, 200000); // approximation - RC osc. R=68K, C=68pf
	m_maincpu->read_k().set(FUNC(ti1250_state::read_k));
	m_maincpu->write_o().set(FUNC(ti1250_state::write_o));
	m_maincpu->write_r().set(FUNC(ti1250_state::write_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	m_display->set_segmask(0xff, 0xff);
	m_display->set_segmask(0x100, 0x40); // R8 only has segment G connected
	config.set_default_layout(layout_ti1250);

	// no sound!
}

void ti1250_state::ti1270(machine_config &config)
{
	ti1250(config);

	// basic machine hardware
	TMS0970(config.replace(), m_maincpu, 250000); // approximation
	m_maincpu->read_k().set(FUNC(ti1250_state::read_k));
	m_maincpu->write_o().set(FUNC(ti1250_state::write_o));
	m_maincpu->write_r().set(FUNC(ti1250_state::write_r));

	config.set_default_layout(layout_ti1270);
}

// roms

ROM_START( ti1250 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tmc0952nl", 0x0000, 0x0400, CRC(fc0cee65) SHA1(1480e4553181f081281d3b78457721b9ecb20173) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_ti1250_micro.pla", 0, 867, CRC(cb3fd2d6) SHA1(82cf36a65dfc3ccb9dd08e48f45ac4d90f693238) )
	ROM_REGION( 195, "maincpu:opla", 0 )
	ROM_LOAD( "tms0950_ti1250_output.pla", 0, 195, CRC(31570eb8) SHA1(c1cb17c31367b65aa777925459515c3d5c565508) )
	ROM_REGION( 157, "maincpu:spla", 0 )
	ROM_LOAD( "tms0980_common2_segment.pla", 0, 157, CRC(c03cccd8) SHA1(08bc4b597686a7aa8b2c9f43b85b62747ffd455b) )
ROM_END

ROM_START( ti1250a )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tms0972nl_za0348", 0x0000, 0x0400, CRC(6e3f8add) SHA1(a249209e2a92f5016e33b7ad2c6c2660df1df959) )

	ROM_REGION( 782, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0970_common1_instr.pla", 0, 782, CRC(05306ef8) SHA1(60a0a3c49ce330bce0c27f15f81d61461d0432ce) )
	ROM_REGION( 860, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0970_common1_micro.pla", 0, 860, CRC(6ff5d51d) SHA1(59d3e5de290ba57694068ddba78d21a0c1edf427) )
	ROM_REGION( 352, "maincpu:opla", 0 )
	ROM_LOAD( "tms0980_ti1270_output.pla", 0, 352, CRC(472f95a0) SHA1(32adb17285f2f3f93a4b027a3dd2156ec48000ec) )
	ROM_REGION( 157, "maincpu:spla", 0 )
	ROM_LOAD( "tms0980_common2_segment.pla", 0, 157, CRC(c03cccd8) SHA1(08bc4b597686a7aa8b2c9f43b85b62747ffd455b) )
ROM_END

ROM_START( ti1270 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tmc0974nl_za0355", 0x0000, 0x0400, CRC(48e09b4b) SHA1(17f27167164df223f9f06082ece4c3fc3900eda3) )

	ROM_REGION( 782, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0970_common1_instr.pla", 0, 782, CRC(05306ef8) SHA1(60a0a3c49ce330bce0c27f15f81d61461d0432ce) )
	ROM_REGION( 860, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0970_common1_micro.pla", 0, 860, CRC(6ff5d51d) SHA1(59d3e5de290ba57694068ddba78d21a0c1edf427) )
	ROM_REGION( 352, "maincpu:opla", 0 )
	ROM_LOAD( "tms0980_ti1270_output.pla", 0, 352, CRC(472f95a0) SHA1(32adb17285f2f3f93a4b027a3dd2156ec48000ec) )
	ROM_REGION( 157, "maincpu:spla", 0 )
	ROM_LOAD( "tms0980_common2_segment.pla", 0, 157, CRC(c03cccd8) SHA1(08bc4b597686a7aa8b2c9f43b85b62747ffd455b) )
ROM_END





/*******************************************************************************

  Texas Instruments TI-2550 II, more (see below)
  * TMS1070 TMS1071NL (die label: 1070A, 1071A)
  * 9-digit cyan VFD

  This chip was also used in 3rd-party calculators, like Citizen 831RD,
  Prinztronic M800, Lloyd's E311, Privileg 861MD.

*******************************************************************************/

class ti25502_state : public hh_tms1k_state
{
public:
	ti25502_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void ti25502(machine_config &config);

private:
	void update_display();
	void write_o(u16 data);
	void write_r(u32 data);
	u8 read_k();
};

// handlers

void ti25502_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void ti25502_state::write_r(u32 data)
{
	// R0-R6,R9: input mux
	m_inp_mux = (data & 0x7f) | (data >> 2 & 0x80);

	// R0-R8: select digit
	m_r = data;
	update_display();
}

void ti25502_state::write_o(u16 data)
{
	// O0-O7: digit segments
	m_o = data;
	update_display();
}

u8 ti25502_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(8);
}

// inputs

static INPUT_PORTS_START( ti25502 )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_V) PORT_NAME("RV")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_NAME("C")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("%")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("CM")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_END) PORT_NAME("MR")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_INSERT) PORT_NAME("M-")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_HOME) PORT_NAME("M+")

	PORT_START("IN.6") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("+/-") // N/A on TI-2550 II
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME(u8"\u221ax") // U+221A = √
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME(u8"x²")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME("1/x")

	PORT_START("IN.7") // R9
	PORT_CONFNAME( 0x0f, 0x00, "Decimal" )
	PORT_CONFSETTING(    0x00, "F" )
	PORT_CONFSETTING(    0x01, "1" ) // N/A on TI-2550 II
	PORT_CONFSETTING(    0x02, "2" )
	PORT_CONFSETTING(    0x04, "4" ) // "
INPUT_PORTS_END

// config

void ti25502_state::ti25502(machine_config &config)
{
	// basic machine hardware
	TMS1070(config, m_maincpu, 350000); // approximation - RC osc. R=43K, C=68pF
	m_maincpu->read_k().set(FUNC(ti25502_state::read_k));
	m_maincpu->write_o().set(FUNC(ti25502_state::write_o));
	m_maincpu->write_r().set(FUNC(ti25502_state::write_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	m_display->set_segmask(0x1ff, 0xff);
	config.set_default_layout(layout_ti25502);

	// no sound!
}

// roms

ROM_START( ti25502 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tms1071nl", 0x0000, 0x0400, CRC(0f5640c9) SHA1(6e8b54d8ceed8850d1186204ea26b6657add3d48) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_ti25502_micro.pla", 0, 867, CRC(639cbc13) SHA1(a96152406881bdfc7ddc542cf4b478525c8b0e23) )
	ROM_REGION( 406, "maincpu:opla", 0 )
	ROM_LOAD( "tms1070_ti25502_output.pla", 0, 406, CRC(c1df3ae6) SHA1(f106caaea1ac4787d8b579f16177dbf2f35b094d) )
ROM_END





/*******************************************************************************

  Texas Instruments TI-2550 III, TI-1650/TI-1600, TI-1265 (they have the same chip)
  * TMS1040 MCU label TMS1043NL ZA0352 (die label: 1040A, 1043A)
  * 9-digit cyan VFD

  Only the TI-2550 III has the top button row (RV, SQRT, etc).
  TI-1600 doesn't have the memory buttons.

*******************************************************************************/

class ti25503_state : public hh_tms1k_state
{
public:
	ti25503_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void ti25503(machine_config &config);

private:
	void update_display();
	void write_o(u16 data);
	void write_r(u32 data);
	u8 read_k();
};

// handlers

void ti25503_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void ti25503_state::write_r(u32 data)
{
	// R0-R6: input mux
	// R0-R8: select digit
	m_r = m_inp_mux = data;
	update_display();
}

void ti25503_state::write_o(u16 data)
{
	// O0-O7: digit segments
	m_o = data;
	update_display();
}

u8 ti25503_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(7);
}

// inputs

static INPUT_PORTS_START( ti25503 )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_NAME("C")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("+/-")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("%")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("CM")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_END) PORT_NAME("MR")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_INSERT) PORT_NAME("M-")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_HOME) PORT_NAME("M+")

	PORT_START("IN.6") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_V) PORT_NAME("RV")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME(u8"\u221ax") // U+221A = √
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME(u8"x²")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME("1/x")
INPUT_PORTS_END

// config

void ti25503_state::ti25503(machine_config &config)
{
	// basic machine hardware
	TMS1040(config, m_maincpu, 350000); // approximation
	m_maincpu->read_k().set(FUNC(ti25503_state::read_k));
	m_maincpu->write_o().set(FUNC(ti25503_state::write_o));
	m_maincpu->write_r().set(FUNC(ti25503_state::write_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	m_display->set_segmask(0x1ff, 0xff);
	config.set_default_layout(layout_ti25502);

	// no sound!
}

// roms

ROM_START( ti25503 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tms1043nl_za0352", 0x0000, 0x0400, CRC(434c2684) SHA1(ff566f1991f63cfe057879674e6bc7ccd580a919) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_ti25503_micro.pla", 0, 867, CRC(65d274ae) SHA1(33d77efe38f8b067096c643d71263bb5adde0ca9) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_ti25503_output.pla", 0, 365, CRC(ac43b768) SHA1(5eb19b493328c73edab73e44591afda0fbe4965f) )
ROM_END





/*******************************************************************************

  Texas Instruments TI-5100, more (see below)
  * TMS1070 MCU label TMS1073NL or TMC1073NL (die label: 1070B, 1073)
  * 11-digit 7seg VFD (1 custom digit)

  This chip was also used in 3rd-party calculators, such as Toshiba BC-1015,
  Panasonic JE1601U, Radio Shack EC2001, Triumph-Adler D100. The original
  TI version did not have the 3 buttons at R4.

*******************************************************************************/

class ti5100_state : public hh_tms1k_state
{
public:
	ti5100_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void ti5100(machine_config &config);

private:
	void update_display();
	void write_o(u16 data);
	void write_r(u32 data);
	u8 read_k();
};

// handlers

void ti5100_state::update_display()
{
	// extra segment on R10
	m_display->matrix(m_r, m_o | ((m_r & 0x400) ? 0x180 : 0));
}

void ti5100_state::write_r(u32 data)
{
	// R0-R10: input mux, select digit
	m_r = m_inp_mux = data;
	update_display();
}

void ti5100_state::write_o(u16 data)
{
	// O0-O7: digit segments
	m_o = data;
	update_display();
}

u8 ti5100_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(11);
}

// inputs

static INPUT_PORTS_START( ti5100 )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x08, 0x00, "Mode" )
	PORT_CONFSETTING(    0x08, "K" ) // constant
	PORT_CONFSETTING(    0x00, "C" ) // chain

	PORT_START("IN.1") // R1
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // R2
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x08, 0x00, "Decimal" )
	PORT_CONFSETTING(    0x00, "F" ) // floating
	PORT_CONFSETTING(    0x08, "2" )

	PORT_START("IN.3") // R3
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) // clear all?
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME("EX")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_G) PORT_NAME("GPM")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME(u8"Δ%")

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_NAME("C/CE")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("CM")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_N) PORT_NAME("N")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("%")

	PORT_START("IN.6") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")

	PORT_START("IN.7") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) // duplicate of R8 0x01
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")

	PORT_START("IN.8") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")

	PORT_START("IN.9") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) // duplicate of R9 0x02
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("+=")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-=")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")

	PORT_START("IN.10") // R10
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_HOME) PORT_NAME("M+=")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_INSERT) PORT_NAME("M-=")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_END) PORT_NAME("RM")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")
INPUT_PORTS_END

// config

void ti5100_state::ti5100(machine_config &config)
{
	// basic machine hardware
	TMS1070(config, m_maincpu, 350000); // approximation
	m_maincpu->read_k().set(FUNC(ti5100_state::read_k));
	m_maincpu->write_o().set(FUNC(ti5100_state::write_o));
	m_maincpu->write_r().set(FUNC(ti5100_state::write_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(11, 9);
	m_display->set_segmask(0x3ff, 0xff);
	config.set_default_layout(layout_ti5100);

	// no sound!
}

// roms

ROM_START( ti5100 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tms1073nl", 0x0000, 0x0400, CRC(94185933) SHA1(d2d9432f857b8530ac399f5097c6d412f06d8814) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_ti5100_micro.pla", 0, 867, CRC(31b43e95) SHA1(6864e4c20f3affffcd3810dcefbc9484dd781547) )
	ROM_REGION( 406, "maincpu:opla", 0 )
	ROM_LOAD( "tms1070_ti5100_output.pla", 0, 406, CRC(b4a3aa6e) SHA1(812b5483a26ae3aa05661c86841d2d3d163e6c46) )
ROM_END





/*******************************************************************************

  Texas Instruments TI-5200
  * TMS1270 MCU label TMS1278NL or TMC1278NL (die label: 1070B, 1278A)
  * 13-digit 7seg VFD Itron FG139A1 (1 custom digit)

*******************************************************************************/

class ti5200_state : public hh_tms1k_state
{
public:
	ti5200_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void ti5200(machine_config &config);

private:
	void update_display();
	void write_o(u16 data);
	void write_r(u32 data);
	u8 read_k();
};

// handlers

void ti5200_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void ti5200_state::write_r(u32 data)
{
	// R0-R5,R9,R12: input mux
	m_inp_mux = (data & 0x3f) | (data >> 3 & 0x40) | (data >> 5 & 0x80);

	// R0-R12: select digit
	m_r = data;
	update_display();
}

void ti5200_state::write_o(u16 data)
{
	// O1-O9: digit segments
	m_o = bitswap<9>(data,7,8,3,6,5,4,9,2,1);
	update_display();
}

u8 ti5200_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(8);
}

// inputs

static INPUT_PORTS_START( ti5200 )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_NAME("C/CE")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("CM")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_V) PORT_NAME("RV")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("%")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("+=")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-=")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_HOME) PORT_NAME("M+=")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_INSERT) PORT_NAME("M-=")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_END) PORT_NAME("RM")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")

	PORT_START("IN.6") // R9
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x08, 0x00, "Mode" )
	PORT_CONFSETTING(    0x08, "K" ) // constant
	PORT_CONFSETTING(    0x00, "C" ) // chain

	PORT_START("IN.7") // R12
	PORT_CONFNAME( 0x01, 0x00, "Decimal" )
	PORT_CONFSETTING(    0x01, "S" ) // set DP with number key
	PORT_CONFSETTING(    0x00, "R" )
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void ti5200_state::ti5200(machine_config &config)
{
	// basic machine hardware
	TMS1270(config, m_maincpu, 350000); // approximation - RC osc. R=43K, C=68pF
	m_maincpu->read_k().set(FUNC(ti5200_state::read_k));
	m_maincpu->write_o().set(FUNC(ti5200_state::write_o));
	m_maincpu->write_r().set(FUNC(ti5200_state::write_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(13, 9);
	m_display->set_segmask(0xfff, 0xff);
	config.set_default_layout(layout_ti5200);

	// no sound!
}

// roms

ROM_START( ti5200 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tms1278nl", 0x0000, 0x0400, CRC(6829f24d) SHA1(d7cf358a26a347d6a2ca4313cb7ffd5082e19885) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_ti5200_micro.pla", 0, 867, CRC(61bd20fc) SHA1(fbdc87138975e2e10f92f75dcae5ca900f78475a) )
	ROM_REGION( 406, "maincpu:opla", 0 )
	ROM_LOAD( "tms1070_ti5200_output.pla", 0, 406, CRC(6f37c393) SHA1(5252a5ac05c65326ee189f859904007e11b0009d) )
ROM_END





/*******************************************************************************

  Texas Instruments TMC098x series Majestic-line calculators

  TI-30, SR-40, TI-15(less buttons) and several by Koh-I-Noor
  * TMS0980 MCU label TMC0981NL (die label: 0980B-81F)
  * 9-digit 7seg LED display

  Of note is a peripheral by Schoenherr, called the Braillotron. It acts as
  a docking station to the TI-30, with an additional display made of magnetic
  refreshable Braille cells. The TI-30 itself is slightly modified to wire
  the original LED display to a 25-pin D-Sub connector.

  See further below for TI Business Analyst and TI Programmer.

*******************************************************************************/

class ti30_state : public hh_tms1k_state
{
public:
	ti30_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void ti30(machine_config &config);

private:
	void write_o(u16 data);
	void write_r(u32 data);
	u8 read_k();
};

// handlers

void ti30_state::write_r(u32 data)
{
	// R0-R8: select digit
	m_display->matrix(data, m_o);
}

void ti30_state::write_o(u16 data)
{
	// O0-O2,O4-O7: input mux
	// O0-O7: digit segments
	m_inp_mux = (data & 7) | (data >> 1 & 0x78);
	m_o = bitswap<8>(data,7,5,2,1,4,0,6,3);
}

u8 ti30_state::read_k()
{
	// K: multiplexed inputs (note: the Vss row is always on)
	return m_inputs[7]->read() | read_inputs(7);
}

// inputs

static INPUT_PORTS_START( ti30 )
	PORT_START("IN.0") // O0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_NAME(u8"yˣ")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_NAME("K")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_NAME("log")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_NAME(u8"EE\u2193") // U+2193 = ↓
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_NAME("ln(x)")

	PORT_START("IN.1") // O1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_HOME) PORT_NAME("STO")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")

	PORT_START("IN.2") // O2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_END) PORT_NAME("RCL")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")

	PORT_START("IN.3") // O4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_NAME(u8"π")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_NAME("(")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("%")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_NAME(")")

	PORT_START("IN.4") // O5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("SUM")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")

	PORT_START("IN.5") // O6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_NAME("DRG")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_NAME("INV")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_NAME("cos")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_NAME("sin")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_NAME("tan")

	PORT_START("IN.6") // O7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_NAME("EXC")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("+/-")

	// note: even though power buttons are on the matrix, they are not CPU-controlled
	PORT_START("IN.7") // Vss
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1) PORT_CODE(KEYCODE_DEL) PORT_NAME("On/C") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), true)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_NAME("1/x")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_NAME(u8"\u221ax") // U+221A = √
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_NAME(u8"x²")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_NAME("Off") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), false)
INPUT_PORTS_END

// config

void ti30_state::ti30(machine_config &config)
{
	// basic machine hardware
	TMS0980(config, m_maincpu, 400000); // guessed
	m_maincpu->read_k().set(FUNC(ti30_state::read_k));
	m_maincpu->write_o().set(FUNC(ti30_state::write_o));
	m_maincpu->write_r().set(FUNC(ti30_state::write_r));
	m_maincpu->power_off().set(FUNC(ti30_state::auto_power_off));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	m_display->set_segmask(0x1fe, 0xff);
	m_display->set_segmask(0x001, 0xe2); // 1st digit only has segments B,F,G,DP
	config.set_default_layout(layout_ti30);

	// no sound!
}

// roms

ROM_START( ti30 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD16_WORD( "tmc0981nl", 0x0000, 0x1000, CRC(41298a14) SHA1(06f654c70add4044a612d3a38b0c2831c188fd0c) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 1982, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0980_common1_micro.pla", 0, 1982, CRC(3709014f) SHA1(d28ee59ded7f3b9dc3f0594a32a98391b6e9c961) )
	ROM_REGION( 352, "maincpu:opla", 0 )
	ROM_LOAD( "tms0980_ti30_output.pla", 0, 352, CRC(00475f99) SHA1(70e04c1472639bd35d4adaab0b9f1ae4a0e394be) )
	ROM_REGION( 157, "maincpu:spla", 0 )
	ROM_LOAD( "tms0980_common1_segment.pla", 0, 157, CRC(399aa481) SHA1(72c56c58fde3fbb657d69647a9543b5f8fc74279) )
ROM_END





/*******************************************************************************

  TI Business Analyst, TI Business Analyst-I, TI Money Manager, TI-31, TI-41
  * TMS0980 MCU label TMC0982NL (die label: 0980B-82F)
  * 9-digit 7seg LED display

  It's on the same hardware as TI-30.

*******************************************************************************/

// class/handlers: uses the ones in ti30_state

// inputs

static INPUT_PORTS_START( tibusan )
	// PORT_NAME lists functions under [2nd] as secondaries.
	PORT_START("IN.0") // O0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_NAME(u8"yˣ  ˣ\u221ay") // U+221A = √ - 2nd one implies xth root of y
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME(u8"%  Δ%")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_NAME("SEL")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_NAME("CST")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_NAME("MAR")

	PORT_START("IN.1") // O1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_HOME) PORT_NAME("STO  m")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")

	PORT_START("IN.2") // O2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_END) PORT_NAME("RCL  b")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")

	PORT_START("IN.3") // O4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME(u8"Σ+  Σ-")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_NAME("(  AN-CI\"")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_NAME(u8"x\u2194y  L.R.") // U+2194 = ↔
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_NAME(")  1/x")

	PORT_START("IN.4") // O5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_NAME(u8"SUM  xʹ")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")

	PORT_START("IN.5") // O6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_NAME("FV")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_NAME("N")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_NAME("PMT")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_NAME("%i")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_NAME("PV")

	PORT_START("IN.6") // O7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_NAME(u8"EXC  xʹ")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("+/-")

	// note: even though power buttons are on the matrix, they are not CPU-controlled
	PORT_START("IN.7") // Vss
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1) PORT_CODE(KEYCODE_DEL) PORT_NAME("On/C") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), true)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_NAME("2nd")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_NAME(u8"x²  \u221ax") // U+221A = √
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_NAME(u8"ln(x)  eˣ")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_NAME("Off") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), false)
INPUT_PORTS_END

// roms

ROM_START( tibusan )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD16_WORD( "tmc0982nl", 0x0000, 0x1000, CRC(6954560a) SHA1(6c153a0c9239a811e3514a43d809964c06f8f88e) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 1982, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0980_common1_micro.pla", 0, 1982, CRC(3709014f) SHA1(d28ee59ded7f3b9dc3f0594a32a98391b6e9c961) )
	ROM_REGION( 352, "maincpu:opla", 0 )
	ROM_LOAD( "tms0980_ti30_output.pla", 0, 352, CRC(00475f99) SHA1(70e04c1472639bd35d4adaab0b9f1ae4a0e394be) )
	ROM_REGION( 157, "maincpu:spla", 0 )
	ROM_LOAD( "tms0980_common1_segment.pla", 0, 157, CRC(399aa481) SHA1(72c56c58fde3fbb657d69647a9543b5f8fc74279) )
ROM_END





/*******************************************************************************

  TI Programmer
  * TMS0980 MCU label ZA0675NL, JP0983AT (die label: 0980B-83)
  * 9-digit 7seg LED display

  Like TI Business Analyst, it's on the same hardware as TI-30.

*******************************************************************************/

// class/handlers: uses the ones in ti30_state

// inputs

static INPUT_PORTS_START( tiprog )
	PORT_START("IN.0") // O0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_NAME("K")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_NAME("SHF")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_NAME("E")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_NAME("d")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_NAME("F")

	PORT_START("IN.1") // O1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_NAME("OR")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")

	PORT_START("IN.2") // O2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_NAME("AND")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")

	PORT_START("IN.3") // O4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_NAME("1'sC")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_NAME("b")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_NAME("A")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_NAME("C")

	PORT_START("IN.4") // O5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_NAME("XOR")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")

	PORT_START("IN.5") // O6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_NAME(")")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_HOME) PORT_NAME("STO")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("SUM")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_END) PORT_NAME("RCL")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_NAME("(")

	PORT_START("IN.6") // O7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("+/-")

	// note: even though power buttons are on the matrix, they are not CPU-controlled
	PORT_START("IN.7") // Vss
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1) PORT_CODE(KEYCODE_DEL) PORT_NAME("C/ON") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), true)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_NAME("DEC")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_NAME("OCT")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_NAME("HEX")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_NAME("Off") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), false)
INPUT_PORTS_END

// roms

ROM_START( tiprog )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD16_WORD( "za0675nl", 0x0000, 0x1000, CRC(82355854) SHA1(03fab373bce04df8ea3fe25352525e8539213626) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 1982, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0980_tiprog_micro.pla", 0, 1982, CRC(57043284) SHA1(0fa06d5865830ecdb3d870271cb92ac917bed3ca) )
	ROM_REGION( 352, "maincpu:opla", 0 )
	ROM_LOAD( "tms0980_tiprog_output.pla", 0, 352, CRC(125c4ee6) SHA1(b8d865c42fd37c3d9b92c5edbecfc831be558597) )
	ROM_REGION( 157, "maincpu:spla", 0 )
	ROM_LOAD( "tms0980_common1_segment.pla", 0, 157, CRC(399aa481) SHA1(72c56c58fde3fbb657d69647a9543b5f8fc74279) )
ROM_END





/*******************************************************************************

  Texas Instruments TI-1000 (1977 version)
  * TMS1990 MCU label TMC1991NL (die label: 1991-91A)
  * 8-digit 7seg LED display

  Texas Instruments TI-1000 (1978 version)
  * TMS1990 MCU label TMC1992-4NL **not dumped yet

*******************************************************************************/

class ti1000_state : public hh_tms1k_state
{
public:
	ti1000_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void ti1000(machine_config &config);

private:
	void write_o(u16 data);
	void write_r(u32 data);
	u8 read_k();
};

// handlers

void ti1000_state::write_r(u32 data)
{
	// R0-R7: select digit
	m_display->matrix(data, m_o);
}

void ti1000_state::write_o(u16 data)
{
	// O0-O2,O4,O5: input mux
	// O0-O7: digit segments
	m_inp_mux = (data & 7) | (data >> 1 & 0x18);
	m_o = bitswap<8>(data,7,4,3,2,1,0,6,5);
}

u8 ti1000_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(5);
}

// inputs

static INPUT_PORTS_START( ti1000 )
	PORT_START("IN.0") // O0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")

	PORT_START("IN.1") // O1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")

	PORT_START("IN.2") // O2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")

	// note: even though power buttons are on the matrix, they are not CPU-controlled
	PORT_START("IN.3") // O4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_NAME("Off") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), false)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("+/-")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("%")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")

	PORT_START("IN.4") // O5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1) PORT_CODE(KEYCODE_DEL) PORT_NAME("On/C") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), true)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")
INPUT_PORTS_END

// config

void ti1000_state::ti1000(machine_config &config)
{
	// basic machine hardware
	TMS1990(config, m_maincpu, 250000); // approximation
	m_maincpu->read_k().set(FUNC(ti1000_state::read_k));
	m_maincpu->write_o().set(FUNC(ti1000_state::write_o));
	m_maincpu->write_r().set(FUNC(ti1000_state::write_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 8);
	m_display->set_segmask(0xff, 0xff);
	config.set_default_layout(layout_ti1270);

	// no sound!
}

// roms

ROM_START( ti1000 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tmc1991nl", 0x0000, 0x0400, CRC(2da5381d) SHA1(b5dc14553db2068ed48e130e5ec9109930d8cda9) )

	ROM_REGION( 782, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0970_common1_instr.pla", 0, 782, CRC(05306ef8) SHA1(60a0a3c49ce330bce0c27f15f81d61461d0432ce) )
	ROM_REGION( 860, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0970_common2_micro.pla", 0, 860, CRC(7f50ab2e) SHA1(bff3be9af0e322986f6e545b567c97d70e135c93) )
	ROM_REGION( 352, "maincpu:opla", 0 )
	ROM_LOAD( "tms0980_ti1000_output.pla", 0, 352, CRC(a936631e) SHA1(1f900b12a41419d6e1ffbddd5cf72be3adaa4435) )
	ROM_REGION( 157, "maincpu:spla", 0 )
	ROM_LOAD( "tms0980_common3_segment.pla", 0, 157, CRC(b5b3153f) SHA1(e0145c729695a4f962970aee0571d42cee6f5a9e) )
ROM_END





/*******************************************************************************

  Texas Instruments Little Professor (1976 version, rev. A)
  * TMS0970 MCU label TMS0975NL ZA0356, AP (die label: 0970D-75A)
  * 9-digit 7seg LED display(one custom digit)

  Texas Instruments Little Professor (1976 version, rev. B)
  * TMS0970 MCU label TMS0975NL ZA0356, BP (die label: 0970D-75B)

  Texas Instruments Little Professor (1976 version, rev. C)
  * TMS0970 MCU label TMS0975NL ZA0356, CSP/GP0975CS (die label: 0970D-75C)

*******************************************************************************/

class lilprofo_state : public hh_tms1k_state
{
public:
	lilprofo_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void lilprofo(machine_config &config);
	void lilprofoc(machine_config &config);

protected:
	void write_o(u16 data);
	void write_o7(u16 data);
	void write_r(u32 data);
	u8 read_k();
};

// handlers

void lilprofo_state::write_r(u32 data)
{
	// R0-R8: select digit

	// 3rd digit only has A and G for =, though some newer hardware revisions
	// (goes for both lilprof and wizatron) use a custom equals-sign digit here

	// 6th digit is custom(not 7seg), for math symbols, like this:
	//   \./    GAB
	//   ---     F
	//   /.\    EDC
	m_display->matrix(data, m_o);
}

void lilprofo_state::write_o(u16 data)
{
	// O1-O5: input mux
	// O0-O6: digit segments A-G
	m_inp_mux = (data >> 1 & 0x1f);
	m_o = data & 0x7f;
}

void lilprofo_state::write_o7(u16 data)
{
	// level switch is on O7
	write_o(data);
	m_inp_mux = (m_inp_mux & 0xf) | (data >> 3 & 0x10);
}

u8 lilprofo_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(5);
}

// inputs

static INPUT_PORTS_START( lilprofo )
	PORT_START("IN.0") // O1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_DEL) PORT_NAME("Set")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Go")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")

	PORT_START("IN.1") // O2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")

	PORT_START("IN.2") // O3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")

	PORT_START("IN.3") // O4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")

	PORT_START("IN.4") // O5/O7
	PORT_CONFNAME( 0x0f, 0x01, "Level")
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_CONFSETTING(    0x04, "3" )
	PORT_CONFSETTING(    0x08, "4" )
INPUT_PORTS_END

// config

void lilprofo_state::lilprofo(machine_config &config)
{
	// basic machine hardware
	TMS0970(config, m_maincpu, 250000); // approximation
	m_maincpu->read_k().set(FUNC(lilprofo_state::read_k));
	m_maincpu->write_o().set(FUNC(lilprofo_state::write_o));
	m_maincpu->write_r().set(FUNC(lilprofo_state::write_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 7);
	m_display->set_segmask(0x1f7, 0x7f);
	m_display->set_segmask(8, 0x41); // equals sign
	config.set_default_layout(layout_lilprof);

	// no sound!
}

void lilprofo_state::lilprofoc(machine_config &config)
{
	lilprofo(config);
	m_maincpu->write_o().set(FUNC(lilprofo_state::write_o7));
}

// roms

ROM_START( lilprofo )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tms0975nl_za0356_csp", 0x0000, 0x0400, CRC(fef9dd39) SHA1(5c9614c9c5092d55dabeee2d6e0387d50d6ad4d5) )

	ROM_REGION( 782, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0970_common1_instr.pla", 0, 782, CRC(05306ef8) SHA1(60a0a3c49ce330bce0c27f15f81d61461d0432ce) )
	ROM_REGION( 860, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0970_common1_micro.pla", 0, 860, CRC(6ff5d51d) SHA1(59d3e5de290ba57694068ddba78d21a0c1edf427) )
	ROM_REGION( 352, "maincpu:opla", 0 )
	ROM_LOAD( "tms0980_lilprofo_output.pla", 0, 352, CRC(73f9ca93) SHA1(9d6c559e2c1886c62bcd57e3c3aa897e8829b4d2) )
	ROM_REGION( 157, "maincpu:spla", 0 )
	ROM_LOAD( "tms0980_common2_segment.pla", 0, 157, CRC(c03cccd8) SHA1(08bc4b597686a7aa8b2c9f43b85b62747ffd455b) )
ROM_END

ROM_START( lilprofob )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tms0975nl_za0356_bp", 0x0000, 0x0400, CRC(00c8357f) SHA1(32ab47e979e117bda889ab1c72c1c239cfa0f386) )

	ROM_REGION( 782, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0970_common1_instr.pla", 0, 782, CRC(05306ef8) SHA1(60a0a3c49ce330bce0c27f15f81d61461d0432ce) )
	ROM_REGION( 860, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0970_common1_micro.pla", 0, 860, CRC(6ff5d51d) SHA1(59d3e5de290ba57694068ddba78d21a0c1edf427) )
	ROM_REGION( 352, "maincpu:opla", 0 )
	ROM_LOAD( "tms0980_lilprofo_output.pla", 0, 352, CRC(73f9ca93) SHA1(9d6c559e2c1886c62bcd57e3c3aa897e8829b4d2) )
	ROM_REGION( 157, "maincpu:spla", 0 )
	ROM_LOAD( "tms0980_common2_segment.pla", 0, 157, CRC(c03cccd8) SHA1(08bc4b597686a7aa8b2c9f43b85b62747ffd455b) )
ROM_END

ROM_START( lilprofoa )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tms0975nl_za0356_ap", 0x0000, 0x0400, CRC(ab77b595) SHA1(8ec5a00d9eb0780b9bb2937c4c023efb2470b287) )

	ROM_REGION( 782, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0970_common1_instr.pla", 0, 782, CRC(05306ef8) SHA1(60a0a3c49ce330bce0c27f15f81d61461d0432ce) )
	ROM_REGION( 860, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0970_common1_micro.pla", 0, 860, CRC(6ff5d51d) SHA1(59d3e5de290ba57694068ddba78d21a0c1edf427) )
	ROM_REGION( 352, "maincpu:opla", 0 )
	ROM_LOAD( "tms0980_lilprofo_output.pla", 0, 352, CRC(73f9ca93) SHA1(9d6c559e2c1886c62bcd57e3c3aa897e8829b4d2) )
	ROM_REGION( 157, "maincpu:spla", 0 )
	ROM_LOAD( "tms0980_common2_segment.pla", 0, 157, CRC(c03cccd8) SHA1(08bc4b597686a7aa8b2c9f43b85b62747ffd455b) )
ROM_END





/*******************************************************************************

  Texas Instruments WIZ-A-TRON
  * TMS0970 MCU label TMC0907NL ZA0379 BSP, DP0907BS (die label: 0970F-07B)
  * 9-digit 7seg LED display(one custom digit)

  The hardware is nearly identical to Little Professor (1976 version). The same
  ZA0379 BSP MCU (should be same ROM) was also used in TI Math Magic.

*******************************************************************************/

// class/handlers: uses the ones in lilprofo_state

// inputs

static INPUT_PORTS_START( wizatron )
	PORT_INCLUDE( lilprofo )

	PORT_MODIFY("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_NAME("Clear")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")

	PORT_MODIFY("IN.4")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// roms

ROM_START( wizatron )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tmc0907nl_za0379_bsp", 0x0000, 0x0400, CRC(5a6af094) SHA1(b1f27e1f13f4db3b052dd50fb08dbf9c4d8db26e) )

	ROM_REGION( 782, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0970_common1_instr.pla", 0, 782, CRC(05306ef8) SHA1(60a0a3c49ce330bce0c27f15f81d61461d0432ce) )
	ROM_REGION( 860, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0970_common2_micro.pla", 0, 860, CRC(7f50ab2e) SHA1(bff3be9af0e322986f6e545b567c97d70e135c93) )
	ROM_REGION( 352, "maincpu:opla", 0 )
	ROM_LOAD( "tms0980_wizatron_output.pla", 0, 352, CRC(c0ee5c04) SHA1(e9fadcef688309adbe6c1c0545aac6883ce4a1ac) )
	ROM_REGION( 157, "maincpu:spla", 0 )
	ROM_LOAD( "tms0980_common2_segment.pla", 0, 157, CRC(c03cccd8) SHA1(08bc4b597686a7aa8b2c9f43b85b62747ffd455b) )
ROM_END





/*******************************************************************************

  Texas Instruments Little Professor (1978 version)
  * TMS1990 MCU label TMC1993NL (die label: 1990C-c3C)
  * 9-digit 7seg LED display(one custom digit)

  1978 re-release, with on/off and level select on buttons instead of
  switches. The casing was slightly revised in 1980 again, but same rom.

*******************************************************************************/

class lilprof_state : public hh_tms1k_state
{
public:
	lilprof_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void lilprof(machine_config &config);

private:
	void write_o(u16 data);
	void write_r(u32 data);
	u8 read_k();
};

// handlers

void lilprof_state::write_r(u32 data)
{
	// update leds state
	u8 seg = bitswap<8>(m_o,7,4,3,2,1,0,6,5) & 0x7f;
	u16 r = (data & 7) | (data << 1 & 0x1f0);
	m_display->matrix(r, seg);

	// 3rd digit A/G(equals sign) is from O7
	m_display->write_row(3, (r != 0 && m_o & 0x80) ? 0x41 : 0);

	// 6th digit is a custom 7seg for math symbols (see lilprofo_state write_r)
	m_display->write_row(6, bitswap<8>(m_display->read_row(6),7,6,1,4,2,3,5,0));
}

void lilprof_state::write_o(u16 data)
{
	// O0-O2,O4,O5: input mux
	// O0-O6: digit segments A-G
	// O7: 6th digit
	m_inp_mux = (data & 7) | (data >> 1 & 0x18);
	m_o = data;
}

u8 lilprof_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(5);
}

// inputs

static INPUT_PORTS_START( lilprof )
	PORT_START("IN.0") // O0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")

	PORT_START("IN.1") // O1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")

	PORT_START("IN.2") // O2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")

	// note: even though power buttons are on the matrix, they are not CPU-controlled
	PORT_START("IN.3") // O4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F2) PORT_NAME("Off") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), false)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Set")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_L) PORT_NAME("Level")

	PORT_START("IN.4") // O5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F1) PORT_CODE(KEYCODE_DEL) PORT_NAME("On") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), true)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Go")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
INPUT_PORTS_END

// config

void lilprof_state::lilprof(machine_config &config)
{
	// basic machine hardware
	TMS1990(config, m_maincpu, 250000); // approximation
	m_maincpu->read_k().set(FUNC(lilprof_state::read_k));
	m_maincpu->write_o().set(FUNC(lilprof_state::write_o));
	m_maincpu->write_r().set(FUNC(lilprof_state::write_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 7);
	m_display->set_segmask(0x1ff, 0x7f);
	config.set_default_layout(layout_lilprof);

	// no sound!
}

// roms

ROM_START( lilprof )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tmc1993nl", 0x0000, 0x0400, CRC(e941316b) SHA1(7e1542045d1e731cea81a639c9ac9e91bb233b15) )

	ROM_REGION( 782, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0970_common1_instr.pla", 0, 782, CRC(05306ef8) SHA1(60a0a3c49ce330bce0c27f15f81d61461d0432ce) )
	ROM_REGION( 860, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0970_common2_micro.pla", 0, 860, CRC(7f50ab2e) SHA1(bff3be9af0e322986f6e545b567c97d70e135c93) )
	ROM_REGION( 352, "maincpu:opla", 0 )
	ROM_LOAD( "tms0980_lilprof_output.pla", 0, 352, CRC(b7416cc0) SHA1(57891ffeaf34aafe8a915086c6d2feb78f5113af) )
	ROM_REGION( 157, "maincpu:spla", 0 )
	ROM_LOAD( "tms0980_common3_segment.pla", 0, 157, CRC(b5b3153f) SHA1(e0145c729695a4f962970aee0571d42cee6f5a9e) )
ROM_END





/*******************************************************************************

  Texas Instruments TI-1680, TI-2550-IV
  * TMS1980 MCU label TMC1981NL (die label: 1980A 81F)
  * TMC0999NL 256x4 RAM (die label: 0999B)
  * 9-digit cyan VFD(leftmost digit is custom)

  The extra RAM is for scrolling back through calculations. For some reason,
  TI-2550-IV has the same hardware, this makes it very different from II and III.

*******************************************************************************/

class ti1680_state : public hh_tms1k_state
{
public:
	ti1680_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag),
		m_ram(*this, "ram")
	{ }

	void ti1680(machine_config &config);

private:
	required_device<tmc0999_device> m_ram;

	void update_display();
	void write_o(u16 data);
	void write_r(u32 data);
	u8 read_k();
};

// handlers

void ti1680_state::update_display()
{
	// note the extra segments on R9 and R3
	m_display->matrix(m_r & 0x1ff, m_o | (m_r >> 2 & 0x80) | (m_r << 5 & 0x100));
}

void ti1680_state::write_r(u32 data)
{
	// R8,R0,R5,R6: TMC0999 data inputs
	// R2,R4+R1/R7: TMC0999 control pins
	m_ram->di_w(bitswap<4>(data,8,0,5,6));
	m_ram->adr_w(BIT(data, 2));
	m_ram->wr_w(BIT(data, 4) & BIT(data, 7));
	m_ram->rd_w(BIT(data, 4) & BIT(data, 1));

	// R3-R8: input mux
	m_inp_mux = data >> 3 & 0x3f;

	// R0-R8: select digit
	// R9: digit DP segment
	m_r = data;
	update_display();
}

void ti1680_state::write_o(u16 data)
{
	// O0-O6: digit segments A-G
	m_o = bitswap<8>(data,7,1,6,5,4,3,2,0) & 0x7f;
	update_display();
}

u8 ti1680_state::read_k()
{
	// K: multiplexed inputs, RAM data
	return read_inputs(6) | m_ram->do_r();
}

// inputs

static INPUT_PORTS_START( ti1680 )
	PORT_START("IN.0") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_HOME) PORT_NAME("M")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_END) PORT_NAME("MR")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PGUP) PORT_NAME("BST")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PGDN) PORT_NAME("RPL")

	PORT_START("IN.1") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F1) PORT_CODE(KEYCODE_DEL) PORT_NAME("On/C") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), true)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F2) PORT_NAME("Off") // -> auto_power_off
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("%")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")

	PORT_START("IN.2") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")

	PORT_START("IN.3") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")

	PORT_START("IN.4") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")

	PORT_START("IN.5") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("+/-")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")
INPUT_PORTS_END

// config

void ti1680_state::ti1680(machine_config &config)
{
	// basic machine hardware
	TMS1980(config, m_maincpu, 300000); // approximation
	m_maincpu->read_k().set(FUNC(ti1680_state::read_k));
	m_maincpu->write_o().set(FUNC(ti1680_state::write_o));
	m_maincpu->write_r().set(FUNC(ti1680_state::write_r));
	m_maincpu->power_off().set(FUNC(ti1680_state::auto_power_off));

	TMC0999(config, m_ram);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 9);
	m_display->set_segmask(0x1fe, 0xff);
	config.set_default_layout(layout_ti1680);

	// no sound!
}

// roms

ROM_START( ti1680 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD16_WORD( "tmc1981nl", 0x0000, 0x1000, CRC(8467c5a1) SHA1(d3a48f6c9dd41e2dc0e0cfafa84dac8d21aacaa3) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 525, "maincpu:opla", 0 )
	ROM_LOAD( "tms1980_ti1680_output.pla", 0, 525, CRC(188af340) SHA1(eaf707266dde0d708870ef5d8f985ce88d35b43e) )
ROM_END





/*******************************************************************************

  Texas Instruments DataMan
  * TMS1980 MCU label TMC1982NL (die label: 1980A 82B)
  * 10-digit cyan VFD(3 digits are custom)

*******************************************************************************/

class dataman_state : public hh_tms1k_state
{
public:
	dataman_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void dataman(machine_config &config);

protected:
	void update_display();
	void write_o(u16 data);
	void write_r(u32 data);
	u8 read_k();
};

// handlers

void dataman_state::update_display()
{
	// note the extra segment on R9
	m_display->matrix(m_r & 0x1ff, m_o | (m_r >> 2 & 0x80));
}

void dataman_state::write_r(u32 data)
{
	// R0-R4: input mux
	// R0-R8: select digit
	// R9: =(equals sign) segment
	m_r = m_inp_mux = data;
	update_display();
}

void dataman_state::write_o(u16 data)
{
	// O0-O6: digit segments A-G
	m_o = bitswap<8>(data,7,1,6,5,4,3,2,0) & 0x7f;
	update_display();
}

u8 dataman_state::read_k()
{
	// K: multiplexed inputs (note: the Vss row is always on)
	return m_inputs[5]->read() | read_inputs(5);
}

// inputs

static INPUT_PORTS_START( dataman )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_M) PORT_NAME("Memory Bank")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_G) PORT_NAME("Go")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("Force Out")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_N) PORT_NAME("Number Guesser")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("Wipe Out")

	// note: even though power buttons are on the matrix, they are not CPU-controlled
	PORT_START("IN.5") // Vss
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F1) PORT_CODE(KEYCODE_U) PORT_NAME("On/User Entry") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), true)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F2) PORT_NAME("Off") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), false)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("?")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("Electro Flash")
INPUT_PORTS_END

// config

void dataman_state::dataman(machine_config &config)
{
	// basic machine hardware
	TMS1980(config, m_maincpu, 300000); // patent says 300kHz
	m_maincpu->read_k().set(FUNC(dataman_state::read_k));
	m_maincpu->write_o().set(FUNC(dataman_state::write_o));
	m_maincpu->write_r().set(FUNC(dataman_state::write_r));
	m_maincpu->power_off().set(FUNC(dataman_state::auto_power_off));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	m_display->set_segmask(0x1ff, 0x7f);
	config.set_default_layout(layout_dataman);

	// no sound!
}

// roms

ROM_START( dataman )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD16_WORD( "tmc1982nl", 0x0000, 0x1000, CRC(3521f53f) SHA1(c46fe7fe20715fdf5aed65833fb867cfd3938062) ) // matches patent US4340374

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 525, "maincpu:opla", 0 )
	ROM_LOAD( "tms1980_dataman_output.pla", 0, 525, CRC(5fc6f451) SHA1(11475c785c34eab5b13c5dc67f413c709cd4bd4d) )
ROM_END





/*******************************************************************************

  Texas Instruments Math Marvel
  * TMS1980 MCU label TMC1986A-NL (die label: 1980A 86A)
  * 9-digit cyan VFD(2 digits are custom), 1-bit sound

  This is the same hardware as DataMan, with R8 connected to a piezo.

*******************************************************************************/

class mathmarv_state : public dataman_state
{
public:
	mathmarv_state(const machine_config &mconfig, device_type type, const char *tag) :
		dataman_state(mconfig, type, tag)
	{ }

	void mathmarv(machine_config &config);
};

// handlers: uses the ones in dataman_state

// inputs

static INPUT_PORTS_START( mathmarv )
	PORT_INCLUDE( dataman )

	PORT_MODIFY("IN.4") // R4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Quest")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("Checker")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("Review")

	PORT_MODIFY("IN.5") // Vss
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F1) PORT_CODE(KEYCODE_N) PORT_NAME("On/Numberific") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_tms1k_state::power_button), true)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Z) PORT_NAME("Zap")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("Flash")
INPUT_PORTS_END

// config

void mathmarv_state::mathmarv(machine_config &config)
{
	dataman(config);

	// basic machine hardware
	m_maincpu->write_r().append(m_speaker, FUNC(speaker_sound_device::level_w)).bit(8);

	config.set_default_layout(layout_mathmarv);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( mathmarv )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD16_WORD( "tmc1986anl", 0x0000, 0x1000, CRC(79fda72d) SHA1(137852b29d9136459f78e29e7810195a956a5903) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 525, "maincpu:opla", 0 )
	ROM_LOAD( "tms1980_mathmarv_output.pla", 0, 525, CRC(5fc6f451) SHA1(11475c785c34eab5b13c5dc67f413c709cd4bd4d) )
ROM_END





/*******************************************************************************

  Texas Instruments maze game (unreleased, from patent GB2040172A)
  * TMS1000 (development version)
  * 1 7seg LED digit, no sound

  The title of this game is unknown, the patent describes it simply as a maze game.
  Several electronic maze game concepts are listed in the patent. The PCB schematic
  and program source code is included for one of them: A predefined 12*8 maze,
  walls close to the player are displayed on a 7seg digit.

  In the end Texas Instruments didn't release any electronic maze game. This version
  is too simple and obviously unfinished, start and goal positions are always the same
  and there is a lot of ROM space left for more levels.

*******************************************************************************/

class timaze_state : public hh_tms1k_state
{
public:
	timaze_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void timaze(machine_config &config);

private:
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void timaze_state::write_r(u32 data)
{
	// R0: input mux
	m_inp_mux = data & 1;
}

void timaze_state::write_o(u16 data)
{
	// O3210: 7seg EGCD?
	m_display->matrix(1, bitswap<8>(data, 7,1,6,0,3,2,5,4));
}

u8 timaze_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(1);
}

// inputs

static INPUT_PORTS_START( timaze )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
INPUT_PORTS_END

// config

void timaze_state::timaze(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 200000); // approximation - RC osc. R=80K, C=27pF
	m_maincpu->read_k().set(FUNC(timaze_state::read_k));
	m_maincpu->write_r().set(FUNC(timaze_state::write_r));
	m_maincpu->write_o().set(FUNC(timaze_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 8);
	m_display->set_segmask(1, 0x5c);
	config.set_default_layout(layout_timaze);

	// no sound!
}

// roms

ROM_START( timaze )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "gb2040172a", 0x0000, 0x0400, CRC(0bab4dc6) SHA1(c9d40649fbb27a8b7cf7460d66c7e217b63376f0) ) // from patent GB2040172A, verified with source code

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, BAD_DUMP CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) ) // not in patent, use default one
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_timaze_output.pla", 0, 365, BAD_DUMP CRC(f0f36970) SHA1(a6ad1f5e804ac98e5e1a1d07466b3db3a8d6c256) ) // described in patent, but unsure about pin order
ROM_END





/*******************************************************************************

  Texas Instruments Electronic Digital Thermostat
  * TMS0970 MCU, label TMS0970NLL TMC0910B (die label: 0970F-10E)
  * 9-digit 7seg LED display, only 4 used
  * temperature sensor, heat/cool/fan outputs

  This is a thermostat and digital clock. It's the 2nd one described in
  patents US4388692 and US4298946.

*******************************************************************************/

class tithermos_state : public hh_tms1k_state
{
public:
	tithermos_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag),
		m_ac_power(*this, "ac_power")
	{ }

	void tithermos(machine_config &config);

private:
	required_device<clock_device> m_ac_power;

	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void tithermos_state::write_r(u32 data)
{
	// D1-D4: select digit
	m_display->matrix(data, m_o);

	// D6: heat/cool
	// D8: A/D reset
	m_r = data;
}

void tithermos_state::write_o(u16 data)
{
	// SA-SP: input mux
	// SA-SG: digit segments
	m_o = m_inp_mux = data;
}

u8 tithermos_state::read_k()
{
	// K: multiplexed inputs
	u8 data = read_inputs(8);

	// when SB/SD/SP is high:
	if (m_inp_mux & 0x8a)
	{
		// K1: 60hz from AC power
		// K2: battery low?
		// K8: A/D output (TODO)
		data |= m_ac_power->signal_r() ? 1 : 0;
	}

	return data;
}

// inputs

static INPUT_PORTS_START( tithermos )
	PORT_START("IN.0") // SA
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("Temp Row PM 2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("Time Row PM 2")

	PORT_START("IN.1") // SB
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // SC
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("Temp Row PM 1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("Time Row PM 1")

	PORT_START("IN.3") // SD
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_CUSTOM ) // AC power osc
	PORT_CONFNAME( 0x06, 0x04, "System")
	PORT_CONFSETTING(    0x04, "Heat" )
	PORT_CONFSETTING(    0x00, "Off" )
	PORT_CONFSETTING(    0x02, "Cool" )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) // A/D output

	PORT_START("IN.4") // SE
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Temp Row AM 2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("Time Row AM 2")

	PORT_START("IN.5") // SF
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Temp Row AM 1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Time Row AM 1")

	PORT_START("IN.6") // SG
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Temp / Actual")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("Time / Clock")

	PORT_START("IN.7") // SP
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_CUSTOM ) // AC power osc
	PORT_CONFNAME( 0x06, 0x04, "Mode")
	PORT_CONFSETTING(    0x04, "Constant" )
	PORT_CONFSETTING(    0x00, "Day/Night" )
	PORT_CONFSETTING(    0x02, "Night" )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) // A/D output

	PORT_START("IN.8")
	PORT_CONFNAME( 0x01, 0x00, "Fan")
	PORT_CONFSETTING(    0x00, "On" )
	PORT_CONFSETTING(    0x01, "Auto" ) // same output as heat/cool
INPUT_PORTS_END

// config

void tithermos_state::tithermos(machine_config &config)
{
	// basic machine hardware
	TMS0970(config, m_maincpu, 250000); // approximation
	m_maincpu->read_k().set(FUNC(tithermos_state::read_k));
	m_maincpu->write_r().set(FUNC(tithermos_state::write_r));
	m_maincpu->write_o().set(FUNC(tithermos_state::write_o));

	CLOCK(config, m_ac_power, 60); // from mains power

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 7);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_tithermos);

	// no sound!
}

// roms

ROM_START( tithermos )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "tmc0910b", 0x0000, 0x0400, CRC(232011cf) SHA1(598ae8cecd98226fb5056c99ce9f47fd23785fd7) )

	ROM_REGION( 782, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0970_common2_instr.pla", 0, 782, CRC(e038fc44) SHA1(dfc280f6d0a5828d1bb14fcd59ac29caf2c2d981) )
	ROM_REGION( 860, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0970_tithermos_micro.pla", 0, 860, CRC(a3bb8ca5) SHA1(006ea733440001c37a77d4ffbc4bd2a8fee212ac) )
	ROM_REGION( 352, "maincpu:opla", 0 )
	ROM_LOAD( "tms0980_tithermos_output.pla", 0, 352, CRC(eb3598fd) SHA1(2372ae17fb982cae2710bf8c348bf02b767daabd) )
	ROM_REGION( 157, "maincpu:spla", 0 )
	ROM_LOAD( "tms0980_common2_segment.pla", 0, 157, CRC(c03cccd8) SHA1(08bc4b597686a7aa8b2c9f43b85b62747ffd455b) )
ROM_END





/*******************************************************************************

  Tiger Sub Wars (model 7-490)
  * PCB label: CSG201A(main), CSG201B(leds)
  * TMS1200N2LL MP3352 (die label: 1000C, MP3352)
  * 4-digit 7seg LED display + 55 other LEDs, 1-bit sound

  Tiger/Yeno also published an LCD handheld called Sub Wars, it's not related.

  This handheld was modified and used as a prop in the 1981 movie Escape from
  New York, Snake Plissken uses it as a homing device.

*******************************************************************************/

class subwars_state : public hh_tms1k_state
{
public:
	subwars_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void subwars(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
};

// handlers

void subwars_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void subwars_state::write_r(u32 data)
{
	// R0-R3: digit select
	// R4-R12: led select
	m_r = data;
	update_display();
}

void subwars_state::write_o(u16 data)
{
	// O0-O6: led data
	m_o = data;
	update_display();

	// O7: speaker out
	m_speaker->level_w(BIT(data, 7));
}

// inputs

static INPUT_PORTS_START( subwars )
	PORT_START("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void subwars_state::subwars(machine_config &config)
{
	// basic machine hardware
	TMS1200(config, m_maincpu, 550000); // approximation - RC osc. R=24K, C=47pF
	m_maincpu->read_k().set_ioport("IN.0");
	m_maincpu->write_r().set(FUNC(subwars_state::write_r));
	m_maincpu->write_o().set(FUNC(subwars_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(13, 7);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_subwars);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( subwars )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp3352", 0x0000, 0x0400, CRC(5dece1e4) SHA1(65ef77b063c94ff4b6c83dace54ea2f75bd3d6a9) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common1_micro.pla", 0, 867, CRC(4becec19) SHA1(3c8a9be0f00c88c81f378b76886c39b10304f330) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_subwars_output.pla", 0, 365, CRC(372b9bbc) SHA1(06a875e114b7757c6f4f1727416d1739ebe60931) )
ROM_END





/*******************************************************************************

  Tiger Playmaker: Hockey, Soccer, Basketball (model 7-540 or 7-540A)
  * TMS1100 MP1215 (die label: 1100B, MP1215)
  * 2-digit 7seg LED display + 40 other LEDs, 1-bit sound

  The games are on playcards(Tiger calls them that), the hardware detects which
  game is inserted from a notch at the lower-right. The playcards also function
  as an overlay. MAME external artwork is needed for those.

  Booting the handheld with no playcard inserted will initiate a halftime show.

  "Playmaker" is actually Tiger's trademark for the d-pad controller, this
  controller term was also used in for example Deluxe Football, and 7 in 1 Sports
  Stadium. The d-pad has a ball shape at the bottom that sits on a concave base.
  It is patented under US4256931 (mid-1979, a couple of years before Nintendo's
  Game & Watch d-pad with US4687200).

*******************************************************************************/

class playmaker_state : public hh_tms1k_state
{
public:
	playmaker_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void playmaker(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	u8 m_notch = 0; // cartridge K1/K2
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

void playmaker_state::machine_start()
{
	hh_tms1k_state::machine_start();

	// register for savestates
	save_item(NAME(m_notch));
}

// handlers

DEVICE_IMAGE_LOAD_MEMBER(playmaker_state::cart_load)
{
	if (!image.loaded_through_softlist())
		return std::make_pair(image_error::UNSUPPORTED, "Can only load through software list");

	// get cartridge notch
	const char *notch = image.get_feature("notch");
	m_notch = notch ? strtoul(notch, nullptr, 0) & 3 : 0;

	return std::make_pair(std::error_condition(), std::string());
}

void playmaker_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void playmaker_state::write_r(u32 data)
{
	// R10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// R0-R3: input mux
	m_inp_mux = data & 0xf;

	// R0-R7: led select
	// R8,R9: digit select
	m_r = data;
	update_display();
}

void playmaker_state::write_o(u16 data)
{
	// O0-O6: led data
	m_o = data;
	update_display();
}

u8 playmaker_state::read_k()
{
	// K: multiplexed inputs, cartridge notch from R3
	return read_inputs(3) | ((m_inp_mux & 8) ? m_notch : 0);
}

// inputs

static INPUT_PORTS_START( playmaker )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Shoot")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_COCKTAIL PORT_NAME("P2 Pass")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Shoot / P1 Skill")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Pass / P2 Skill")
INPUT_PORTS_END

// config

void playmaker_state::playmaker(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 375000); // approximation - RC osc. R=20K, C=250pF
	m_maincpu->read_k().set(FUNC(playmaker_state::read_k));
	m_maincpu->write_r().set(FUNC(playmaker_state::write_r));
	m_maincpu->write_o().set(FUNC(playmaker_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 7);
	m_display->set_segmask(0x300, 0x7f);
	m_display->set_bri_levels(0.004, 0.04); // player 1 leds are brighter
	config.set_default_layout(layout_playmaker);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);

	// cartridge
	generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "playmaker_cart"));
	cartslot.set_must_be_loaded(false);
	cartslot.set_device_load(FUNC(playmaker_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("playmaker");
}

// roms

ROM_START( playmaker )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1215", 0x0000, 0x0800, CRC(bfc7b6c8) SHA1(33f6e2b86fae2fd9e4b0a4b8dc842c257ca3047d) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_playmaker_output.pla", 0, 365, CRC(0cd484d6) SHA1(4a9af9f3d18af504145690cb0f6444ff1aef26ca) )
ROM_END





/*******************************************************************************

  Tiger Deluxe Football with Instant Replay (model 7-550)
  * TMS1400NLL MP7302 (die label: TMS1400, MP7302, 28L 01D D000 R000)
  * 4-digit 7seg LED display, 80 red/green LEDs, 1-bit sound

  According to the manual, player 1 is green, player 2 is red. But when
  playing a 1-player game, the CPU controls green, so on MAME, player 1
  is the red side.

  Booting the handheld with the Score and Replay buttons held down will
  initiate a halftime show.

*******************************************************************************/

class dxfootb_state : public hh_tms1k_state
{
public:
	dxfootb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void dxfootb(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void dxfootb_state::update_display()
{
	// 2 led groups (double multiplexed)
	u16 g1 = (m_r & 0x100) ? 0x7f : 0;
	u16 g2 = (m_r & 0x80) ? 0x7f << 7 : 0;
	m_display->matrix((m_r & g1) | (m_r << 7 & g2), m_o);
}

void dxfootb_state::write_r(u32 data)
{
	// R9,R10: speaker out
	m_speaker->level_w(data >> 9 & 3);

	// R3-R6: input mux
	m_inp_mux = data >> 3 & 0xf;

	// R0-R6: led select
	// R7,R8: group select
	m_r = data;
	update_display();
}

void dxfootb_state::write_o(u16 data)
{
	// O0-O7: led data
	m_o = data;
	update_display();
}

u8 dxfootb_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(4);
}

// inputs

static INPUT_PORTS_START( dxfootb )
	PORT_START("IN.0") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START2 ) PORT_NAME("Replay / Skill (Green)")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Score / Skill (Red)")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_COCKTAIL PORT_NAME("P2 Kick")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Pass")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Kick")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Pass")

	PORT_START("IN.2") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL

	PORT_START("IN.3") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
INPUT_PORTS_END

// config

void dxfootb_state::dxfootb(machine_config &config)
{
	// basic machine hardware
	TMS1400(config, m_maincpu, 425000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_k().set(FUNC(dxfootb_state::read_k));
	m_maincpu->write_r().set(FUNC(dxfootb_state::write_r));
	m_maincpu->write_o().set(FUNC(dxfootb_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(7+7, 8);
	m_display->set_segmask(0x3c00, 0x7f);
	config.set_default_layout(layout_dxfootb);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[4] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( dxfootb )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mp7302", 0x0000, 0x1000, CRC(a8077062) SHA1(c1318fe5c8f2db021d7d1264fc70158944045fa3) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 557, "maincpu:opla", 0 )
	ROM_LOAD( "tms1400_dxfootb_output.pla", 0, 557, CRC(a1b3d2c0) SHA1(8030e6dcd3878b58668c98cff36d93b764e1d67f) )
ROM_END





/*******************************************************************************

  Tiger Electronics Copy Cat (model 7-520)
  * PCB label: CC REV B
  * TMS1000 MCU, label 69-11513 MP0919 (die label: 1000B, MP0919)
  * 4 LEDs, 1-bit sound

  known releases:
  - World: Copy Cat, published by Tiger
  - USA(1): Follow Me, published by Sears
  - USA(2): Electronic Repeat, published by Tandy

*******************************************************************************/

class copycat_state : public hh_tms1k_state
{
public:
	copycat_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void copycat(machine_config &config);

private:
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void copycat_state::write_r(u32 data)
{
	// R0-R3: leds
	m_display->matrix(1, data & 0xf);

	// R4-R7: input mux
	// R8-R10: N/C
	m_inp_mux = data >> 4 & 0xf;
}

void copycat_state::write_o(u16 data)
{
	// O0,O1: speaker out
	// O2,O7: N/C, O3-O6: tied together but unused
	m_speaker->level_w(data & 3);
}

u8 copycat_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(4);
}

// inputs

static INPUT_PORTS_START( copycat )
	PORT_START("IN.0") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Green Button")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Red Button")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Orange Button")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Yellow Button")

	PORT_START("IN.1") // R5
	PORT_CONFNAME( 0x0f, 0x01, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_CONFSETTING(    0x04, "3" )
	PORT_CONFSETTING(    0x08, "4" )

	PORT_START("IN.2") // R6
	PORT_CONFNAME( 0x07, 0x01, "Game Select")
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_CONFSETTING(    0x04, "3" )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Best Play")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START ) PORT_NAME("Play")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Replay")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void copycat_state::copycat(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 320000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_k().set(FUNC(copycat_state::read_k));
	m_maincpu->write_r().set(FUNC(copycat_state::write_r));
	m_maincpu->write_o().set(FUNC(copycat_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 4);
	config.set_default_layout(layout_copycat);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[4] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( copycat )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp0919", 0x0000, 0x0400, CRC(92a21299) SHA1(16daadb8dbf53aaab8a71833017b4a578d035d6d) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_copycat_output.pla", 0, 365, CRC(b1d0c96d) SHA1(ac1a003eab3f69e09e9050cb24ea17211e0523fe) )
ROM_END





/*******************************************************************************

  Tiger Electronics Copy Cat (model 7-522)
  * PCB label: WS 8107-1
  * TMS1730 MCU, label MP3005N (die label: 1700, MP3005)
  * 4 LEDs, 1-bit sound

  This is a simplified rerelease of Copy Cat, 10(!) years later. The gameplay
  is identical to Ditto.

  3 variations exist, each with a different colored case. Let's assume that
  they're on the same hardware.
  - white case, yellow orange green red buttons and leds (same as model 7-520)
  - yellow case, purple orange blue pink buttons, leds are same as older version
  - transparent case, transparent purple orange blue red buttons, leds same as before

*******************************************************************************/

class copycata_state : public hh_tms1k_state
{
public:
	copycata_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void copycata(machine_config &config);

private:
	void write_r(u32 data);
	void write_o(u16 data);
};

// handlers

void copycata_state::write_r(u32 data)
{
	// R1-R4: leds
	m_display->matrix(1, data >> 1 & 0xf);
}

void copycata_state::write_o(u16 data)
{
	// O0,O6: speaker out
	m_speaker->level_w((data & 1) | (data >> 5 & 2));
}

// inputs

static INPUT_PORTS_START( copycata )
	PORT_START("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Orange Button")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Red Button")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Yellow Button")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Green Button")
INPUT_PORTS_END

// config

void copycata_state::copycata(machine_config &config)
{
	// basic machine hardware
	TMS1730(config, m_maincpu, 275000); // approximation - RC osc. R=100K, C=47pF
	m_maincpu->read_k().set_ioport("IN.0");
	m_maincpu->write_r().set(FUNC(copycata_state::write_r));
	m_maincpu->write_o().set(FUNC(copycata_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 4);
	config.set_default_layout(layout_copycata);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[4] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( copycata )
	ROM_REGION( 0x0200, "maincpu", 0 )
	ROM_LOAD( "mp3005n", 0x0000, 0x0200, CRC(a87649cb) SHA1(14ef7967a80578885f0b905772c3bb417b5b3255) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_copycata_micro.pla", 0, 867, CRC(2710d8ef) SHA1(cb7a13bfabedad43790de753844707fe829baed0) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_copycata_output.pla", 0, 365, CRC(d1999aaf) SHA1(0c27789b349e491d5230f9c75c4741e621f5a14e) )
ROM_END





/*******************************************************************************

  Tiger Ditto (model 7-530)
  * TMS1700 MCU, label MP1801-N2LL (die label: 1700, MP1801)
  * 4 LEDs, 1-bit sound

  known releases:
  - World: Ditto, published by Tiger
  - USA: Electronic Pocket Repeat (model 60-2152/60-2468A), published by Tandy
    note: 1996 model 60-2482 MCU is a Z8, and is assumed to be a clone of Tiger Copycat Jr.

*******************************************************************************/

class ditto_state : public hh_tms1k_state
{
public:
	ditto_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void ditto(machine_config &config);

private:
	void write_r(u32 data);
	void write_o(u16 data);
};

// handlers

void ditto_state::write_r(u32 data)
{
	// R0-R3: leds
	m_display->matrix(1, data & 0xf);
}

void ditto_state::write_o(u16 data)
{
	// O5,O6: speaker out
	m_speaker->level_w(data >> 5 & 3);
}

// inputs

static INPUT_PORTS_START( ditto )
	PORT_START("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Yellow Button")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Blue Button")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Orange Button")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Red Button")
INPUT_PORTS_END

// config

void ditto_state::ditto(machine_config &config)
{
	// basic machine hardware
	TMS1700(config, m_maincpu, 275000); // approximation - RC osc. R=100K, C=47pF
	m_maincpu->read_k().set_ioport("IN.0");
	m_maincpu->write_r().set(FUNC(ditto_state::write_r));
	m_maincpu->write_o().set(FUNC(ditto_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 4);
	config.set_default_layout(layout_ditto);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[4] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( ditto )
	ROM_REGION( 0x0200, "maincpu", 0 )
	ROM_LOAD( "mp1801", 0x0000, 0x0200, CRC(cee6043b) SHA1(4ec334be6835688413637ff9d9d7a5f0d61eba27) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_ditto_micro.pla", 0, 867, CRC(2710d8ef) SHA1(cb7a13bfabedad43790de753844707fe829baed0) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_ditto_output.pla", 0, 365, CRC(2b708a27) SHA1(e95415e51ffbe5da3bde1484fcd20467dde9f09a) )
ROM_END





/*******************************************************************************

  Tiger Finger Bowl (model 7-545)
  * TMS1100 MP1288 (no decap)
  * 3 7seg LEDs, 1-bit sound

  It's a track & field electronic board game, played by sliding or tapping
  your finger. Instructions on how to play the events are written on the
  device itself. 5 hurdles were included, though they're not essential.

*******************************************************************************/

class fingbowl_state : public hh_tms1k_state
{
public:
	fingbowl_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void fingbowl(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void fingbowl_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void fingbowl_state::write_r(u32 data)
{
	// R8: speaker out
	m_speaker->level_w(BIT(data, 8));

	// R4-R7: input mux
	m_inp_mux = data >> 4 & 0xf;

	// R0-R2: digit select
	m_r = data;
	update_display();
}

void fingbowl_state::write_o(u16 data)
{
	// O0-O6: led data
	m_o = data;
	update_display();
}

u8 fingbowl_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(4);
}

// inputs

static INPUT_PORTS_START( fingbowl )
	PORT_START("IN.0") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON4 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 )

	PORT_START("IN.1") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON8 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON7 )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON6 )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON5 )

	PORT_START("IN.2") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON12 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON11 )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON10 )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON9 )

	PORT_START("IN.3") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON16 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON15 )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON14 )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON13 )
INPUT_PORTS_END

// config

void fingbowl_state::fingbowl(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 250000); // approximation - RC osc. R=68K, C=47pF
	m_maincpu->read_k().set(FUNC(fingbowl_state::read_k));
	m_maincpu->write_r().set(FUNC(fingbowl_state::write_r));
	m_maincpu->write_o().set(FUNC(fingbowl_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(3, 7);
	m_display->set_segmask(7, 0x7f);
	config.set_default_layout(layout_fingbowl);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( fingbowl )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1288", 0x0000, 0x0800, CRC(8eb489ad) SHA1(65efe9fb25f6a5e0a1319558388d84053e003e93) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, BAD_DUMP CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) ) // not verified
	ROM_REGION( 365, "maincpu:opla", ROMREGION_ERASE00 )
	ROM_LOAD( "tms1100_fingbowl_output.pla", 0, 365, NO_DUMP )

	ROM_REGION16_LE( 0x40, "maincpu:opla_b", ROMREGION_ERASE00 ) // verified, electronic dump, 2nd half unused
	ROM_LOAD16_BYTE( "tms1100_fingbowl_output.bin", 0, 0x20, CRC(b84a8afb) SHA1(777c06c1ebb7884a268385b0f9d6d064400ff757) )
ROM_END





/*******************************************************************************

  Tiger 7 in 1 Sports Stadium (model 7-555)
  * TMS1400 MP7304 (die label: TMS1400, MP7304A, 28L 01D D000 R300)
  * 2x2-digit 7seg LED display + 39 other LEDs, 1-bit sound

  This handheld includes 7 games: 1: Basketball, 2: Hockey, 3: Soccer,
  4: Maze, 5: Baseball, 6: Football, 7: Raquetball.
  MAME external artwork is needed for the switchable overlays.

  known releases:
  - World: 7 in 1 Sports Stadium, published by Tiger
  - USA: 7 in 1 Sports, published by Sears

*******************************************************************************/

class t7in1ss_state : public hh_tms1k_state
{
public:
	t7in1ss_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void t7in1ss(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void t7in1ss_state::update_display()
{
	m_display->matrix(m_r, m_o);
}

void t7in1ss_state::write_r(u32 data)
{
	// R9: speaker out
	m_speaker->level_w(data >> 9 & 1);

	// R0-R2,R10: input mux
	m_inp_mux = (data & 7) | (data >> 7 & 8);

	// R0-R3: digit select
	// R4-R8: led select
	m_r = data;
	update_display();
}

void t7in1ss_state::write_o(u16 data)
{
	// O0-O7: led data
	m_o = data;
	update_display();
}

u8 t7in1ss_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(4);
}

// inputs

static INPUT_PORTS_START( t7in1ss )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_COCKTAIL
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2 )

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )

	PORT_START("IN.3") // R10
	PORT_CONFNAME( 0x01, 0x00, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x01, "2" )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0c, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

// config

void t7in1ss_state::t7in1ss(machine_config &config)
{
	// basic machine hardware
	TMS1400(config, m_maincpu, 425000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_k().set(FUNC(t7in1ss_state::read_k));
	m_maincpu->write_r().set(FUNC(t7in1ss_state::write_r));
	m_maincpu->write_o().set(FUNC(t7in1ss_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	m_display->set_segmask(0xf, 0x7f);
	m_display->set_bri_levels(0.004, 0.04); // player led is brighter
	config.set_default_layout(layout_t7in1ss);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( t7in1ss )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mp7304", 0x0000, 0x1000, CRC(2a1c8390) SHA1(fa10e60686af6828a61f05046abc3854ab49af95) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 557, "maincpu:opla", 0 )
	ROM_LOAD( "tms1400_t7in1ss_output.pla", 0, 557, CRC(6b7660f7) SHA1(bb7d58fa04e7606ccdf5b209e1b089948bdd1e7c) )
ROM_END





/*******************************************************************************

  Tomy Volleyball
  * TMS1000 MP0159 TOMY VOLLEY (die label: 1000B, MP0159)
  * 2 7seg LEDs, 14 other LEDs, 1-bit sound

*******************************************************************************/

class tmvolleyb_state : public hh_tms1k_state
{
public:
	tmvolleyb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void tmvolleyb(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void tmvolleyb_state::update_display()
{
	// O7 goes to left digit segments B/C
	m_display->matrix_partial(0, 1, BIT(m_r, 4), BIT(m_o, 7) * 6);
	m_display->matrix_partial(1, 3, m_r >> 4, m_o);
}

void tmvolleyb_state::write_r(u32 data)
{
	// R0-R3,R7-R9: input mux
	m_inp_mux = (data & 0xf) | (data >> 3 & 0x70);

	// R4: digit select
	// R5,R6: led select
	m_r = data;
	update_display();

	// R10: speaker out
	m_speaker->level_w(BIT(data, 10));
}

void tmvolleyb_state::write_o(u16 data)
{
	// O0-O7: digit segment/led data
	m_o = data;
	update_display();
}

u8 tmvolleyb_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(7);
}

// inputs

static INPUT_PORTS_START( tmvolleyb )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_COCKTAIL
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_COCKTAIL
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_COCKTAIL
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_COCKTAIL
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_COCKTAIL
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_NAME("Score")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START2 ) PORT_NAME("P2 Serve")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON6 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON5 )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON4 )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("P1 Serve")

	PORT_START("IN.4") // R7
	PORT_BIT( 0x01, 0x01, IPT_CUSTOM ) PORT_CONDITION("IN.7", 0x01, EQUALS, 0x01) // Game 2
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // R8
	PORT_BIT( 0x02, 0x02, IPT_CUSTOM ) PORT_CONDITION("IN.7", 0x01, EQUALS, 0x00) // Game 1
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // R9
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x08, 0x08, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x08, "1" )
	PORT_CONFSETTING(    0x00, "2" )

	PORT_START("IN.7") // fake
	PORT_CONFNAME( 0x01, 0x00, "Game" )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x01, "2" )
INPUT_PORTS_END

// config

void tmvolleyb_state::tmvolleyb(machine_config &config)
{
	// basic machine hardware
	TMS1000(config, m_maincpu, 325000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_k().set(FUNC(tmvolleyb_state::read_k));
	m_maincpu->write_r().set(FUNC(tmvolleyb_state::write_r));
	m_maincpu->write_o().set(FUNC(tmvolleyb_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 8);
	m_display->set_segmask(3, 0x7f);
	config.set_default_layout(layout_tmvolleyb);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( tmvolleyb )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp0159_tomy_volley", 0x0000, 0x0400, CRC(0088b0cc) SHA1(90f222d6a3bef7b27709ef2816c15867921a8d4c) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_tmvolleyb_output.pla", 0, 365, CRC(01af7efd) SHA1(d90656a7884b37c7fd49989f8d38dd376b74557b) )
ROM_END





/*******************************************************************************

  Tomy Break Up (manufactured in Japan)
  * PCB label: TOMY B.O.
  * TMS1040 MP2726 TOMY WIPE (die label: 1040B, MP2726A)
  * TMS1025N2LL I/O expander
  * 2-digit 7seg display, 46 other leds, 1-bit sound

  known releases:
  - USA: Break Up, published by Tomy
  - Japan: Block Attack, published by Tomy
  - UK: Break-In, published by Tomy

  led translation table: led zz from game PCB = MAME y.x:

    00 = -     10 = 5.0   20 = 4.2
    01 = 7.0   11 = 5.1   21 = 3.3
    02 = 7.1   12 = 5.2   22 = 2.2
    03 = 7.2   13 = 5.3
    04 = 7.3   14 = 4.3
    05 = 6.0   15 = 3.1
    06 = 6.1   16 = 3.2
    07 = 6.2   17 = 3.0
    08 = 6.3   18 = 4.1
    09 = 4.0   19 = 2.1

  the 7seg panel is 0.* and 1.*(aka digit0/1),
  and the 8(2*4) * 3 rectangular leds panel, where x=0,1,2,3:

    9.*        11.*
    8.*        13.*
    10.*       12.*

*******************************************************************************/

class tbreakup_state : public hh_tms1k_state
{
public:
	tbreakup_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag),
		m_expander(*this, "expander")
	{ }

	void tbreakup(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(skill_switch);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<tms1025_device> m_expander;
	u8 m_exp_port[7] = { };

	void expander_w(offs_t offset, u8 data);

	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

void tbreakup_state::machine_start()
{
	hh_tms1k_state::machine_start();
	save_item(NAME(m_exp_port));
}

// handlers

INPUT_CHANGED_MEMBER(tbreakup_state::skill_switch)
{
	// MCU clock is from an analog circuit with resistor of 73K, PRO2 adds 100K
	m_maincpu->set_unscaled_clock((newval & 1) ? 500000 : 325000);
}

void tbreakup_state::update_display()
{
	// 7seg leds from R0,R1 and O0-O6
	m_display->matrix_partial(0, 2, m_r, m_o & 0x7f);

	// 22 round leds from O2-O7 and expander port 7
	m_display->matrix_partial(2, 6, m_o >> 2, m_exp_port[6]);

	// 24 rectangular leds from expander ports 1-6 (not strobed)
	for (int y = 0; y < 6; y++)
		m_display->write_row(y+8, m_exp_port[y]);
}

void tbreakup_state::expander_w(offs_t offset, u8 data)
{
	// TMS1025 port 1-7 data
	m_exp_port[offset] = data;
}

void tbreakup_state::write_r(u32 data)
{
	// R6: speaker out
	m_speaker->level_w(data >> 6 & 1);

	// R7,R8: input mux
	m_inp_mux = data >> 7 & 3;

	// R3-R5: TMS1025 port S
	// R2: TMS1025 STD pin
	m_expander->write_s(data >> 3 & 7);
	m_expander->write_std(data >> 2 & 1);

	// R0,R1: select digit
	m_r = ~data;
	update_display();
}

void tbreakup_state::write_o(u16 data)
{
	// O0-O3: TMS1025 port H
	m_expander->write_h(data & 0xf);

	// O0-O7: led state
	m_o = data;
	update_display();
}

u8 tbreakup_state::read_k()
{
	// K4: fixed input
	// K8: multiplexed inputs
	return (m_inputs[2]->read() & 4) | (read_inputs(2) & 8);
}

// inputs

static INPUT_PORTS_START( tbreakup )
	PORT_START("IN.0") // R7 K8
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Ball")

	PORT_START("IN.1") // R8 K8
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Hit")

	PORT_START("IN.2") // K4
	PORT_CONFNAME( 0x04, 0x00, DEF_STR( Lives ) )
	PORT_CONFSETTING(    0x00, "3" )
	PORT_CONFSETTING(    0x04, "5" )

	PORT_START("CPU") // fake
	PORT_CONFNAME( 0x01, 0x00, DEF_STR( Difficulty ) ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tbreakup_state::skill_switch), 0)
	PORT_CONFSETTING(    0x00, "1" ) // PRO 1
	PORT_CONFSETTING(    0x01, "2" ) // PRO 2
INPUT_PORTS_END

// config

void tbreakup_state::tbreakup(machine_config &config)
{
	// basic machine hardware
	TMS1040(config, m_maincpu, 325000); // see skill_switch
	m_maincpu->read_k().set(FUNC(tbreakup_state::read_k));
	m_maincpu->write_r().set(FUNC(tbreakup_state::write_r));
	m_maincpu->write_o().set(FUNC(tbreakup_state::write_o));

	TMS1025(config, m_expander).set_ms(1); // MS tied high
	m_expander->write_port1_callback().set(FUNC(tbreakup_state::expander_w));
	m_expander->write_port2_callback().set(FUNC(tbreakup_state::expander_w));
	m_expander->write_port3_callback().set(FUNC(tbreakup_state::expander_w));
	m_expander->write_port4_callback().set(FUNC(tbreakup_state::expander_w));
	m_expander->write_port5_callback().set(FUNC(tbreakup_state::expander_w));
	m_expander->write_port6_callback().set(FUNC(tbreakup_state::expander_w));
	m_expander->write_port7_callback().set(FUNC(tbreakup_state::expander_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(2+6+6, 8);
	m_display->set_segmask(3, 0x7f);
	config.set_default_layout(layout_tbreakup);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( tbreakup )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp2726_tomy_wipe", 0x0000, 0x0400, CRC(1f7c28e2) SHA1(164cda4eb3f0b1d20955212a197c9aadf8d18a06) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000_common2_micro.pla", 0, 867, CRC(d33da3cf) SHA1(13c4ebbca227818db75e6db0d45b66ba5e207776) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000_tbreakup_output.pla", 0, 365, CRC(a1ea035e) SHA1(fcf0b57ed90b41441a8974223a697f530daac0ab) )
ROM_END





/*******************************************************************************

  Tomy Power House Pinball
  * PCB label: TOMY P-B
  * TMS1100 MP1180 TOMY PINB (die label: 1100B, MP1180)
  * 3 7seg LEDs, and other LEDs behind bezel, 1-bit sound

  known releases:
  - USA: Power House Pinball, published by Tomy
  - Japan: Pinball, published by Tomy
  - Europe: Flipper, published by Tomy

  led translation table: led zz from game PCB = MAME y.x:

    0 = -     10 = 5.0   20 = 6.4   A = 3.0
    1 = 3.3   11 = 5.5   21 = 6.5   B = 3.4
    2 = 3.1   12 = 5.1   22 = 7.0   C = 3.5
    3 = 3.2   13 = 5.2   23 = 7.1   D = 8.0
    4 = 4.0   14 = 5.3   24 = 7.2   E = 8.1
    5 = 4.1   15 = 6.0   25 = 7.3   F = 8.2
    6 = 4.2   16 = 5.4   26 = 7.4   G = 8.3
    7 = 4.3   17 = 6.1
    8 = 4.4   18 = 6.2
    9 = 4.5   19 = 6.3

  NOTE!: MAME external artwork is required

*******************************************************************************/

class phpball_state : public hh_tms1k_state
{
public:
	phpball_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void phpball(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(flipper_button) { update_display(); }

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void phpball_state::update_display()
{
	// rectangular LEDs under LEDs D,F and E,G are directly connected
	// to the left and right flipper buttons - output them to 10.a and 9.a
	u16 in1 = m_inputs[1]->read() << 7 & 0x600;
	m_display->matrix((m_r & 0x1ff) | in1, m_o);
}

void phpball_state::write_r(u32 data)
{
	// R10: speaker out
	m_speaker->level_w(data >> 10 & 1);

	// R9: input mux
	m_inp_mux = data >> 9 & 1;

	// R0-R2: digit select
	// R0-R8: led select
	m_r = data;
	update_display();
}

void phpball_state::write_o(u16 data)
{
	// O0-O6: digit segment/led data
	// O7: N/C
	m_o = data & 0x7f;
	update_display();
}

u8 phpball_state::read_k()
{
	// K: multiplexed inputs (note: the Vss row is always on)
	return m_inputs[1]->read() | read_inputs(1);
}

// inputs

static INPUT_PORTS_START( phpball )
	PORT_START("IN.0") // R9
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Plunger")
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // Vss
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Right Flipper") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(phpball_state::flipper_button), 0)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Left Flipper") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(phpball_state::flipper_button), 0)
INPUT_PORTS_END

// config

void phpball_state::phpball(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 375000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_k().set(FUNC(phpball_state::read_k));
	m_maincpu->write_r().set(FUNC(phpball_state::write_r));
	m_maincpu->write_o().set(FUNC(phpball_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(11, 7);
	m_display->set_segmask(7, 0x7f);
	config.set_default_layout(layout_phpball);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( phpball )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1180_tomy_pinb", 0x0000, 0x0800, CRC(2163b92d) SHA1(bc53d1911e88b4e89d951c6f769703105c13389c) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_phpball_output.pla", 0, 365, CRC(87e67aaf) SHA1(ebc7bae1352f39173f1bf0dc10cdc6f635dedab4) )
ROM_END





/*******************************************************************************

  Tsukuda The Dracula
  * PCB label: TOFL-001A / B
  * TMS1475 MP6354 (die label: TMS1175 /TMS1475, MP6354, 40H-PASS-0900-R300-0)
    note: no TMS1xxx label on MCU, PCB says "TMS1375L" but that can't be right
  * cyan/red/green VFD NEC FIP11AM32T no. 2-8, 1-bit sound

  known releases:
  - Japan: The Dracula, published by Tsukuda
  - France: Dracula/The Dracula, published by Euro Toy

*******************************************************************************/

class tdracula_state : public hh_tms1k_state
{
public:
	tdracula_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void tdracula(machine_config &config);

private:
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void tdracula_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void tdracula_state::write_r(u32 data)
{
	// R15: speaker out
	m_speaker->level_w(BIT(data, 15));

	// R20,R21: input mux
	m_inp_mux = data >> 20 & 3;

	// R0-R10: VFD grid
	// R11-R14, R16-R20: VFD plate
	m_grid = data & 0x7ff;
	m_plate = (m_plate & 0xff) | (data >> 3 & 0xf00) | (data >> 4 & 0x1f000);
	update_display();
}

void tdracula_state::write_o(u16 data)
{
	// O0-O7: VFD plate
	m_plate = (m_plate & ~0xff) | data;
	update_display();
}

u8 tdracula_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(2);
}

// inputs

static INPUT_PORTS_START( tdracula )
	PORT_START("IN.0") // R20
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START )
	PORT_CONFNAME( 0x02, 0x00, "Factory Test" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x02, DEF_STR( On ) )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x08, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x08, "2" )

	PORT_START("IN.1") // R21
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
INPUT_PORTS_END

// config

void tdracula_state::tdracula(machine_config &config)
{
	// basic machine hardware
	TMS1475(config, m_maincpu, 500000); // approximation - RC osc. R=43K, C=47pF
	m_maincpu->read_k().set(FUNC(tdracula_state::read_k));
	m_maincpu->write_r().set(FUNC(tdracula_state::write_r));
	m_maincpu->write_o().set(FUNC(tdracula_state::write_o));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(478, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(11, 17);
	config.set_default_layout(layout_tdracula);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( tdracula )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mp6354", 0x0000, 0x1000, CRC(e69299e0) SHA1(e73e674a5e659b4c3df390d271847761fffb5874) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) )
	ROM_REGION( 557, "maincpu:opla", 0 )
	ROM_LOAD( "tms1400_tdracula_output.pla", 0, 557, CRC(52e2258e) SHA1(3dcbef72d2309aeb2375041522acd1a879b9e881) )

	ROM_REGION( 417508, "screen", 0)
	ROM_LOAD( "tdracula.svg", 0, 417508, CRC(4f093a2f) SHA1(2c0f9a18720ff1e694b267274967a60115fa2593) )
ROM_END





/*******************************************************************************

  Tsukuda Slot Elepachi 「スロットエレパチ」
  * PCB label: TOFL003
  * TMS2670 M95041 (die label: TMS2400, M95041, 40H-01D-ND02-PHI0032-TTL O300-R300)
  * TMS1024 I/O expander
  * cyan/red/green VFD NEC FIP9AM31T no. 21-84, 1-bit sound

  Two versions are known, one with a red trigger button and blue slot button,
  and one with a blue trigger button and red slot button. The game itself is
  assumed to be the same.

*******************************************************************************/

class slepachi_state : public hh_tms1k_state
{
public:
	slepachi_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag),
		m_expander(*this, "expander")
	{ }

	void slepachi(machine_config &config);

private:
	required_device<tms1024_device> m_expander;

	void expander_w(offs_t offset, u8 data);

	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
};

// handlers

void slepachi_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void slepachi_state::expander_w(offs_t offset, u8 data)
{
	// TMS1024 port 4-7: VFD plate
	int shift = (offset - tms1024_device::PORT4) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void slepachi_state::write_r(u32 data)
{
	// R13: speaker out
	m_speaker->level_w(BIT(data, 13));

	// R9,R10: TMS1024 S0,S1 (S2 forced high)
	// R8: TMS1024 STD
	m_expander->write_s((data >> 9 & 3) | 4);
	m_expander->write_std(BIT(data, 8));

	// R0-R7: VFD grid
	// R11: 1 more VFD plate
	m_grid = data & 0xff;
	m_plate = (m_plate & 0xfffff) | (BIT(data, 11) << 20);
	update_display();
}

void slepachi_state::write_o(u16 data)
{
	// O0-O3: TMS1024 H1-H4 + VFD plate
	m_expander->write_h(data & 0xf);
	m_plate = (m_plate & ~0xf0000) | (data << 16 & 0xf0000);
	update_display();
}

// inputs

static INPUT_PORTS_START( slepachi )
	PORT_START("IN.0") // K
	PORT_CONFNAME( 0x01, 0x00, "Factory Test" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2 ) // Slot

	PORT_START("IN.1") // J
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x08, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x08, "2" )
INPUT_PORTS_END

// config

void slepachi_state::slepachi(machine_config &config)
{
	// basic machine hardware
	TMS2670(config, m_maincpu, 450000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_k().set_ioport("IN.0");
	m_maincpu->read_j().set_ioport("IN.1");
	m_maincpu->write_r().set(FUNC(slepachi_state::write_r));
	m_maincpu->write_o().set(FUNC(slepachi_state::write_o));

	TMS1024(config, m_expander).set_ms(1); // MS tied high
	m_expander->write_port4_callback().set(FUNC(slepachi_state::expander_w));
	m_expander->write_port5_callback().set(FUNC(slepachi_state::expander_w));
	m_expander->write_port6_callback().set(FUNC(slepachi_state::expander_w));
	m_expander->write_port7_callback().set(FUNC(slepachi_state::expander_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(503, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(8, 21);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( slepachi )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "m95041", 0x0000, 0x1000, CRC(18b39629) SHA1(46bbe2028717ab4a1ca92f45496f7636e1c81fcf) )

	ROM_REGION( 759, "maincpu:mpla", 0 )
	ROM_LOAD( "tms2100_common1_micro.pla", 0, 759, CRC(4a60382f) SHA1(f66ed530ca3869367fc7afacd0b985d555781ba2) )
	ROM_REGION( 557, "maincpu:opla", 0 )
	ROM_LOAD( "tms2100_slepachi_output.pla", 0, 557, CRC(90849b91) SHA1(ed19444b655c48bbf2a662478d46c045d900080d) )

	ROM_REGION( 140540, "screen", 0)
	ROM_LOAD( "slepachi.svg", 0, 140540, CRC(95ad7b9f) SHA1(f2f5550ff6a406bcf8310674e55f1ab8ec21f5d2) )
ROM_END





/*******************************************************************************

  U.S. Games Space Cruiser (model TF2)
  * PCB label: TF2
  * TMS1100 MP1209 (no decap)
  * 4 7seg LEDs, 40+2 other LEDs, 1-bit sound

  It's a 2-in-1 handheld, Strategy Football and Space Game. MAME external
  artwork is recommended for the switchable overlays.

  known releases:
  - USA(1): Space Cruiser, published by U.S. Games
  - USA(2): Pro-Action Strategy Football, published by Calfax / Caprice

*******************************************************************************/

class scruiser_state : public hh_tms1k_state
{
public:
	scruiser_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void scruiser(machine_config &config);

private:
	void update_display();
	void update_input();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void scruiser_state::update_display()
{
	m_display->matrix(m_r, m_o | (m_r & 0x300));
}

void scruiser_state::update_input()
{
	// switches from O7+R7
	m_inp_mux = (m_r & 0xf) | (BIT(m_o, 7) << ((BIT(m_r, 7)) + 4));
}

void scruiser_state::write_r(u32 data)
{
	// R0-R3: 7seg select
	// R4-R7: led select
	// R8,R9: led data
	m_r = data;
	update_display();

	// R0-R3,R7: input mux
	update_input();

	// R10: speaker out
	m_speaker->level_w(BIT(data, 10));
}

void scruiser_state::write_o(u16 data)
{
	// O0-O7: led data
	m_o = data;
	update_display();

	// O7: select switches
	update_input();
}

u8 scruiser_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(6);
}

// inputs

static INPUT_PORTS_START( scruiser )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL PORT_16WAY

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_COCKTAIL PORT_NAME("P2 Info")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_COCKTAIL PORT_NAME("P2 Kick")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_COCKTAIL PORT_NAME("P2 QB / WB")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Pass")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Info")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P1 Kick")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("P1 QB / WB")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Pass")

	PORT_START("IN.4") // O7+!R7
	PORT_CONFNAME( 0x02, 0x02, "Game Select" )
	PORT_CONFSETTING(    0x02, "Space" )
	PORT_CONFSETTING(    0x00, "Football" )
	PORT_BIT( 0x0d, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // O7+R7
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x04, 0x04, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x04, "1" )
	PORT_CONFSETTING(    0x00, "2" )
	PORT_CONFNAME( 0x08, 0x00, "Speed" )
	PORT_CONFSETTING(    0x08, "Fast" ) // F
	PORT_CONFSETTING(    0x00, "Slow" ) // S
INPUT_PORTS_END

// config

void scruiser_state::scruiser(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 350000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_k().set(FUNC(scruiser_state::read_k));
	m_maincpu->write_r().set(FUNC(scruiser_state::write_r));
	m_maincpu->write_o().set(FUNC(scruiser_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 10);
	m_display->set_segmask(0xf, 0x7f);
	m_display->set_bri_levels(0.0075, 0.075); // offense leds are brighter
	config.set_default_layout(layout_scruiser);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( scruiser )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1209", 0x0000, 0x0800, CRC(a0280de4) SHA1(52beb30d8a5e4c85433db5722024f4011a07345b) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, BAD_DUMP CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) ) // not verified
	ROM_REGION( 365, "maincpu:opla", ROMREGION_ERASE00 )
	ROM_LOAD( "tms1100_scruiser_output.pla", 0, 365, NO_DUMP )

	ROM_REGION16_LE( 0x40, "maincpu:opla_b", ROMREGION_ERASE00 ) // verified, electronic dump
	ROM_LOAD16_BYTE( "tms1100_scruiser_output.bin", 0, 0x20, CRC(cd6f162f) SHA1(3348edb697e996b5ab82be54076b9cd444e0f2b1) )
ROM_END





/*******************************************************************************

  U.S. Games Super Sports-4 (model TH42)
  * TMS1100 MP1219 (no decap)
  * 4 7seg LEDs, 47+2 other LEDs, 1-bit sound

  The game is very similar to t3in1sa, even parts of the ROM match. But by
  the time that one was released (in 1983 or 1984), U.S. Games did not exist
  anymore. I suspect t3in1sa was programmed by the same (Hong Kong) company.

  This handheld includes 4 games: Basketball, Football, Soccer, Hockey.
  MAME external artwork is needed for the switchable overlays.

*******************************************************************************/

class ssports4_state : public hh_tms1k_state
{
public:
	ssports4_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void ssports4(machine_config &config);

private:
	void update_display();
	void update_input();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

// handlers

void ssports4_state::update_display()
{
	m_display->matrix(m_r, m_o | (m_r << 6 & 0x100));
}

void ssports4_state::update_input()
{
	// switches from O7+R5
	m_inp_mux = (m_r & 3) | (m_r >> 6 & 0xc) | (BIT(m_o, 7) << ((BIT(m_r, 5)) + 4));
}

void ssports4_state::write_r(u32 data)
{
	// R0,R1,R8,R9: 7seg select
	// R3-R7: led select
	// R2: led data
	m_r = data;
	update_display();

	// R0,R1,R5,R8,R9: input mux
	update_input();

	// R10: speaker out
	m_speaker->level_w(BIT(data, 10));
}

void ssports4_state::write_o(u16 data)
{
	// O0-O7: led data
	m_o = data;
	update_display();

	// O7: select switches
	update_input();
}

u8 ssports4_state::read_k()
{
	// K: multiplexed inputs
	return read_inputs(6);
}

// inputs

static INPUT_PORTS_START( ssports4 )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL PORT_16WAY

	PORT_START("IN.2") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Up-Left / Kick")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Up-Right / Info")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P1 Pass")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("P1 O.P.") // offensive player (modifier button)

	PORT_START("IN.3") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Up-Left / Kick")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_COCKTAIL PORT_NAME("P2 Up-Right / Info")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_COCKTAIL PORT_NAME("P2 Pass")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_COCKTAIL PORT_NAME("P2 O.P.")

	PORT_START("IN.4") // O7+!R5
	PORT_CONFNAME( 0x03, 0x00, "Game Select" )
	PORT_CONFSETTING(    0x02, "Basketball" )
	PORT_CONFSETTING(    0x00, "Football" )
	PORT_CONFSETTING(    0x01, "Soccer" )
	PORT_CONFSETTING(    0x03, "Hockey" )
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // O7+R5
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x04, 0x00, "Speed" )
	PORT_CONFSETTING(    0x04, "High" ) // HI
	PORT_CONFSETTING(    0x00, "Low" )  // LO
	PORT_CONFNAME( 0x08, 0x08, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x08, "1" )
	PORT_CONFSETTING(    0x00, "2" )
INPUT_PORTS_END

// config

void ssports4_state::ssports4(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, 350000); // approximation - RC osc. R=47K, C=47pF
	m_maincpu->read_k().set(FUNC(ssports4_state::read_k));
	m_maincpu->write_r().set(FUNC(ssports4_state::write_r));
	m_maincpu->write_o().set(FUNC(ssports4_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 9);
	m_display->set_segmask(0x303, 0x7f);
	m_display->set_bri_levels(0.005, 0.05); // offense leds are brighter
	config.set_default_layout(layout_ssports4);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( ssports4 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mp1219", 0x0000, 0x0800, CRC(865c06d6) SHA1(12a625a13bdb57b82b35c42b175d38756a1e2e04) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common2_micro.pla", 0, 867, BAD_DUMP CRC(7cc90264) SHA1(c6e1cf1ffb178061da9e31858514f7cd94e86990) ) // not verified
	ROM_REGION( 365, "maincpu:opla", ROMREGION_ERASE00 )
	ROM_LOAD( "tms1100_ssports4_output.pla", 0, 365, NO_DUMP )

	ROM_REGION16_LE( 0x40, "maincpu:opla_b", ROMREGION_ERASE00 ) // verified, electronic dump
	ROM_LOAD16_BYTE( "tms1100_ssports4_output.bin", 0, 0x20, CRC(cd6f162f) SHA1(3348edb697e996b5ab82be54076b9cd444e0f2b1) )
ROM_END





/*******************************************************************************

  Vulcan XL 25
  * TMS1000SLC MP4486A (die label: 1000C/, MP4486A)
  * 28 LEDs, 1-bit sound

  This game is the same logic puzzle as Tiger's Lights Out, except that
  all 25 lights need to be turned on instead of off.

*******************************************************************************/

class xl25_state : public hh_tms1k_state
{
public:
	xl25_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_tms1k_state(mconfig, type, tag)
	{ }

	void xl25(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(k4_button) { update_halt(); }

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	void update_halt();
	void update_display();
	void write_r(u32 data);
	void write_o(u16 data);
	u8 read_k();
};

void xl25_state::machine_reset()
{
	hh_tms1k_state::machine_reset();
	update_halt();
}

// handlers

void xl25_state::update_halt()
{
	// O5+K4 go to HALT pin (used when pressing store/recall button)
	bool halt = !((m_o & 0x20) || (read_k() & 4));
	m_maincpu->set_input_line(INPUT_LINE_HALT, halt ? ASSERT_LINE : CLEAR_LINE);
}

void xl25_state::update_display()
{
	m_display->matrix(m_r, m_o >> 1);
}

void xl25_state::write_r(u32 data)
{
	// R0-R9: input mux, led select
	m_inp_mux = data;
	m_r = data;
	update_display();
}

void xl25_state::write_o(u16 data)
{
	// O1-O3: led data
	m_o = data;
	update_display();

	// O6: speaker out
	m_speaker->level_w(data >> 6 & 1);

	// O5(+K4): MCU halt
	update_halt();
}

u8 xl25_state::read_k()
{
	// K: multiplexed inputs
	// K4 also goes to MCU halt
	return read_inputs(10);
}

// inputs

static INPUT_PORTS_START( xl25 )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 6")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 11") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(xl25_state::k4_button), 0)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 7")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 12") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(xl25_state::k4_button), 0)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 13") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(xl25_state::k4_button), 0)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 9")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 14") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(xl25_state::k4_button), 0)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 5")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 10")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 15") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(xl25_state::k4_button), 0)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 16")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 21")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("Store / Recall") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(xl25_state::k4_button), 0)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 17")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 22")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("Cross / Knight / Score") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(xl25_state::k4_button), 0)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.7") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 18")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 23")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("Clear") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(xl25_state::k4_button), 0)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.8") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 19")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 24")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_NAME("Random") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(xl25_state::k4_button), 0)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.9") // R9
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 20")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Square 25")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_NAME("Sound") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(xl25_state::k4_button), 0)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void xl25_state::xl25(machine_config &config)
{
	// basic machine hardware
	TMS1000C(config, m_maincpu, 300000); // approximation - RC osc. R=56K, C=47pF
	m_maincpu->read_k().set(FUNC(xl25_state::read_k));
	m_maincpu->write_r().set(FUNC(xl25_state::write_r));
	m_maincpu->write_o().set(FUNC(xl25_state::write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 3);
	config.set_default_layout(layout_xl25);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( xl25 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "mp4486a", 0x0000, 0x0400, CRC(bd84b515) SHA1(377fcc68a517260acd51eb9746cd62914a75d739) )

	ROM_REGION( 922, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1000c_xl25_micro.pla", 0, 922, CRC(8823e7f2) SHA1(676b0eace9d8730f2caa9087e8c51e540c7fabf8) )
	ROM_REGION( 558, "maincpu:opla", 0 )
	ROM_LOAD( "tms1000c_xl25_output.pla", 0, 558, CRC(06ecc6e0) SHA1(e0fa1b9388948197b4de2edd3cd02fbde1dbabbb) )
ROM_END



} // anonymous namespace

/*******************************************************************************

  Game driver(s)

*******************************************************************************/

//    YEAR  NAME        PARENT     COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY, FULLNAME, FLAGS
SYST( 1979, matchnum,   0,         0,      matchnum,  matchnum,  matchnum_state,  empty_init, "A-One LSI", "Match Number", MACHINE_SUPPORTS_SAVE )
SYST( 1980, arrball,    0,         0,      arrball,   arrball,   arrball_state,   empty_init, "A-One LSI", "Arrange Ball", MACHINE_SUPPORTS_SAVE )

SYST( 1980, mathmagi,   0,         0,      mathmagi,  mathmagi,  mathmagi_state,  empty_init, "APF Electronics Inc.", "Mathemagician", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )

SYST( 1979, bcheetah,   0,         0,      bcheetah,  bcheetah,  bcheetah_state,  empty_init, "Bandai", "System Control Car: Cheetah", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW | MACHINE_MECHANICAL ) // ***
SYST( 1980, racetime,   0,         0,      racetime,  racetime,  racetime_state,  empty_init, "Bandai", "Race Time", MACHINE_SUPPORTS_SAVE )
SYST( 1981, tc7atc,     0,         0,      tc7atc,    tc7atc,    tc7atc_state,    empty_init, "Bandai", "TC7: Air Traffic Control", MACHINE_SUPPORTS_SAVE )
SYST( 1982, uboat,      0,         0,      uboat,     uboat,     uboat_state,     empty_init, "Bandai", "U-Boat", MACHINE_SUPPORTS_SAVE )

SYST( 1977, palmf31,    0,         0,      palmf31,   palmf31,   palmf31_state,   empty_init, "Canon", "Palmtronic F-31", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1977, palmmd8,    0,         0,      palmmd8,   palmmd8,   palmmd8_state,   empty_init, "Canon", "Palmtronic MD-8 (Multi 8)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )

SYST( 1977, cchime,     0,         0,      cchime,    cchime,    cchime_state,    empty_init, "Chromatronics", "Chroma-Chime", MACHINE_SUPPORTS_SAVE )

SYST( 1978, amaztron,   0,         0,      amaztron,  amaztron,  amaztron_state,  empty_init, "Coleco", "Amaze-A-Tron", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_CONTROLS ) // ***
SYST( 1979, zodiac,     0,         0,      zodiac,    zodiac,    zodiac_state,    empty_init, "Coleco", "Zodiac: The Astrology Computer", MACHINE_SUPPORTS_SAVE )
SYST( 1978, cqback,     0,         0,      cqback,    cqback,    cqback_state,    empty_init, "Coleco", "Electronic Quarterback", MACHINE_SUPPORTS_SAVE )
SYST( 1979, h2hfootb,   0,         0,      h2hfootb,  h2hfootb,  h2hfootb_state,  empty_init, "Coleco", "Head to Head: Electronic Football", MACHINE_SUPPORTS_SAVE )
SYST( 1979, h2hbaskb,   0,         0,      h2hbaskb,  h2hbaskb,  h2hbaskb_state,  empty_init, "Coleco", "Head to Head: Electronic Basketball (TMS1000 version)", MACHINE_SUPPORTS_SAVE )
SYST( 1979, h2hhockey,  0,         0,      h2hhockey, h2hhockey, h2hbaskb_state,  empty_init, "Coleco", "Head to Head: Electronic Hockey (TMS1000 version)", MACHINE_SUPPORTS_SAVE )
SYST( 1980, h2hbaseb,   0,         0,      h2hbaseb,  h2hbaseb,  h2hbaseb_state,  empty_init, "Coleco", "Head to Head: Electronic Baseball", MACHINE_SUPPORTS_SAVE )
SYST( 1981, h2hboxing,  0,         0,      h2hboxing, h2hboxing, h2hboxing_state, empty_init, "Coleco", "Head to Head: Electronic Boxing", MACHINE_SUPPORTS_SAVE )
SYST( 1981, quizwizc,   0,         0,      quizwizc,  quizwizc,  quizwizc_state,  empty_init, "Coleco", "Quiz Wiz Challenger", MACHINE_SUPPORTS_SAVE ) // ***
SYST( 1981, tc4,        0,         0,      tc4,       tc4,       tc4_state,       empty_init, "Coleco", "Total Control 4", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )

SYST( 1978, litelrn,    0,         0,      litelrn,   litelrn,   litelrn_state,   empty_init, "Concept 2000", "Lite 'n Learn: Electronic Organ", MACHINE_SUPPORTS_SAVE )
SYST( 1978, mrmusical,  0,         0,      mrmusical, mrmusical, mrmusical_state, empty_init, "Concept 2000", "Mr. Mus-I-Cal", MACHINE_SUPPORTS_SAVE )

SYST( 1979, cnbaskb,    0,         0,      cnbaskb,   cnbaskb,   cnbaskb_state,   empty_init, "Conic", "Electronic Basketball (Conic)", MACHINE_SUPPORTS_SAVE )
SYST( 1979, cmsport,    0,         0,      cmsport,   cmsport,   cmsport_state,   empty_init, "Conic", "Electronic Multisport", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1979, cnfball,    0,         0,      cnfball,   cnfball,   cnfball_state,   empty_init, "Conic", "Electronic Football (Conic, TMS1000 version)", MACHINE_SUPPORTS_SAVE )
SYST( 1979, cnfball2,   0,         0,      cnfball2,  cnfball2,  cnfball2_state,  empty_init, "Conic", "Electronic Football II (Conic)", MACHINE_SUPPORTS_SAVE )
SYST( 1979, eleciq,     0,         0,      eleciq,    eleciq,    eleciq_state,    empty_init, "Conic", "Electronic I.Q.", MACHINE_SUPPORTS_SAVE )

SYST( 1979, qfire,      0,         0,      qfire,     qfire,     qfire_state,     empty_init, "Electroplay", "Quickfire", MACHINE_SUPPORTS_SAVE )

SYST( 1979, esoccer,    0,         0,      esoccer,   esoccer,   esoccer_state,   empty_init, "Entex", "Electronic Soccer (Entex)", MACHINE_SUPPORTS_SAVE )
SYST( 1979, ebball,     0,         0,      ebball,    ebball,    ebball_state,    empty_init, "Entex", "Electronic Baseball (Entex)", MACHINE_SUPPORTS_SAVE )
SYST( 1979, ebball2,    0,         0,      ebball2,   ebball2,   ebball2_state,   empty_init, "Entex", "Electronic Baseball 2 (Entex)", MACHINE_SUPPORTS_SAVE )
SYST( 1980, ebball3,    0,         0,      ebball3,   ebball3,   ebball3_state,   empty_init, "Entex", "Electronic Baseball 3 (Entex)", MACHINE_SUPPORTS_SAVE )
SYST( 1979, esbattle,   0,         0,      esbattle,  esbattle,  esbattle_state,  empty_init, "Entex", "Space Battle (Entex)", MACHINE_SUPPORTS_SAVE )
SYST( 1980, blastit,    0,         0,      blastit,   blastit,   blastit_state,   empty_init, "Entex", "Blast It", MACHINE_SUPPORTS_SAVE )
SYST( 1980, einvader,   0,         0,      einvader,  einvader,  einvader_state,  empty_init, "Entex", "Space Invader (Entex, TMS1100 version)", MACHINE_SUPPORTS_SAVE )
SYST( 1980, efootb4 ,   0,         0,      efootb4,   efootb4,   efootb4_state,   empty_init, "Entex", "Color Football 4 (Entex)", MACHINE_SUPPORTS_SAVE )
SYST( 1980, ebaskb2 ,   0,         0,      ebaskb2,   ebaskb2,   ebaskb2_state,   empty_init, "Entex", "Basketball 2 (Entex)", MACHINE_SUPPORTS_SAVE )
SYST( 1980, raisedvl,   0,         0,      raisedvl,  raisedvl,  raisedvl_state,  empty_init, "Entex", "Raise The Devil Pinball", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1982, ebknight,   0,         0,      ebknight,  raisedvl,  raisedvl_state,  empty_init, "Entex", "Black Knight Pinball (Entex)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1980, mmarvin,    0,         0,      mmarvin,   mmarvin,   mmarvin_state,   empty_init, "Entex", "Musical Marvin", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )

SYST( 1979, f2pbball,   0,         0,      f2pbball,  f2pbball,  f2pbball_state,  empty_init, "Fonas", "2 Player Baseball (Fonas)", MACHINE_SUPPORTS_SAVE )
SYST( 1979, f3in1,      0,         0,      f3in1,     f3in1,     f3in1_state,     empty_init, "Fonas", "3 in 1: Football, Basketball, Soccer", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )

SYST( 1979, gpoker,     0,         0,      gpoker,    gpoker,    gpoker_state,    empty_init, "Gakken", "Poker (Gakken, 1979 version)", MACHINE_SUPPORTS_SAVE )
SYST( 1980, gjackpot,   0,         0,      gjackpot,  gjackpot,  gjackpot_state,  empty_init, "Gakken", "Jackpot: Gin Rummy & Black Jack", MACHINE_SUPPORTS_SAVE )
SYST( 1980, ginv,       0,         0,      ginv,      ginv,      ginv_state,      empty_init, "Gakken", "Invader (Gakken, cyan version)", MACHINE_SUPPORTS_SAVE )
SYST( 1981, ginv1000,   0,         0,      ginv1000,  ginv1000,  ginv1000_state,  empty_init, "Gakken", "Galaxy Invader 1000", MACHINE_SUPPORTS_SAVE )
SYST( 1982, ginv2000,   0,         0,      ginv2000,  ginv2000,  ginv2000_state,  empty_init, "Gakken", "Invader 2000", MACHINE_SUPPORTS_SAVE )
SYST( 1981, fxmcr165,   0,         0,      fxmcr165,  fxmcr165,  fxmcr165_state,  empty_init, "Gakken", "FX-Micom R-165 (rev. A)", MACHINE_SUPPORTS_SAVE )
SYST( 1981, fxmcr165a,  fxmcr165,  0,      fxmcr165,  fxmcr165,  fxmcr165_state,  empty_init, "Gakken", "FX-Micom R-165 (older)", MACHINE_SUPPORTS_SAVE )

SYST( 1979, elecdet,    0,         0,      elecdet,   elecdet,   elecdet_state,   empty_init, "Ideal Toy Corporation", "Electronic Detective", MACHINE_SUPPORTS_SAVE ) // ***
SYST( 1981, skywriter,  0,         0,      skywriter, skywriter, skywriter_state, empty_init, "Ideal Toy Corporation", "Sky-Writer: The Electronic Message Sender", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_CONTROLS )

SYST( 1978, starwlb,    0,         0,      starwlb,   starwlb,   starwlb_state,   empty_init, "Kenner", "Star Wars: Electronic Laser Battle Game", MACHINE_SUPPORTS_SAVE )
SYST( 1979, starwbc,    0,         0,      starwbc,   starwbc,   starwbc_state,   empty_init, "Kenner", "Star Wars: Electronic Battle Command Game", MACHINE_SUPPORTS_SAVE )
SYST( 1979, starwbcp,   starwbc,   0,      starwbc,   starwbc,   starwbc_state,   empty_init, "Kenner", "Star Wars: Electronic Battle Command Game (patent)", MACHINE_SUPPORTS_SAVE )
SYST( 1980, liveafb,    0,         0,      liveafb,   liveafb,   liveafb_state,   empty_init, "Kenner", "Live Action Football", MACHINE_SUPPORTS_SAVE )

SYST( 1980, mega10k,    0,         0,      mega10k,   mega10k,   mega10k_state,   empty_init, "Jeux Nathan", u8"Mega 10.000: L'Encyclopédie Électronique", MACHINE_SUPPORTS_SAVE ) // ***

SYST( 1979, astro,      0,         0,      astro,     astro,     astro_state,     empty_init, "Kosmos", "Astro", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )

SYST( 1980, strobe,     0,         0,      strobe,    strobe,    strobe_state,    empty_init, "Lakeside", "Strobe", MACHINE_SUPPORTS_SAVE )

SYST( 1978, elecbowl,   0,         0,      elecbowl,  elecbowl,  elecbowl_state,  empty_init, "Marx", "Electronic Bowling (Marx)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_CONTROLS | MACHINE_MECHANICAL | MACHINE_NOT_WORKING ) // ***

SYST( 1979, horseran,   0,         0,      horseran,  horseran,  horseran_state,  empty_init, "Mattel Electronics", "Thoroughbred Horse Race Analyzer", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1980, mdndclab,   0,         0,      mdndclab,  mdndclab,  mdndclab_state,  empty_init, "Mattel Electronics", "Dungeons & Dragons: Computer Labyrinth Game", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_CONTROLS ) // ***

SYST( 1977, comp4,      0,         0,      comp4,     comp4,     comp4_state,     empty_init, "Milton Bradley", "Comp IV", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1977, bship,      0,         0,      bship,     bship,     bship_state,     empty_init, "Milton Bradley", "Electronic Battleship (TMS1000 version, rev. A)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND ) // ***
SYST( 1978, bshipb,     bship,     0,      bshipb,    bship,     bshipb_state,    empty_init, "Milton Bradley", "Electronic Battleship (TMS1000 version, rev. B)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) // ***
SYST( 1978, simon,      0,         0,      simon,     simon,     simon_state,     empty_init, "Milton Bradley", "Simon (rev. A)", MACHINE_SUPPORTS_SAVE )
SYST( 1979, simonf,     simon,     0,      simon,     simon,     simon_state,     empty_init, "Milton Bradley", "Simon (rev. F)", MACHINE_SUPPORTS_SAVE )
SYST( 1979, ssimon,     0,         0,      ssimon,    ssimon,    ssimon_state,    empty_init, "Milton Bradley", "Super Simon", MACHINE_SUPPORTS_SAVE )
SYST( 1979, bigtrak,    0,         0,      bigtrak,   bigtrak,   bigtrak_state,   empty_init, "Milton Bradley", "Big Trak", MACHINE_SUPPORTS_SAVE | MACHINE_MECHANICAL ) // ***
SYST( 1981, mbdtower,   0,         0,      mbdtower,  mbdtower,  mbdtower_state,  empty_init, "Milton Bradley", "Dark Tower (Milton Bradley)", MACHINE_SUPPORTS_SAVE | MACHINE_MECHANICAL ) // ***
SYST( 1983, arcmania,   0,         0,      arcmania,  arcmania,  arcmania_state,  empty_init, "Milton Bradley", "Electronic Arcade Mania (Arcade Machine)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK ) // ***

SYST( 1977, cnsector,   0,         0,      cnsector,  cnsector,  cnsector_state,  empty_init, "Parker Brothers", "Code Name: Sector", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW ) // ***
SYST( 1978, merlin,     0,         0,      merlin,    merlin,    merlin_state,    empty_init, "Parker Brothers", "Merlin: The Electronic Wizard", MACHINE_SUPPORTS_SAVE )
SYST( 1978, pbmastm,    0,         0,      pbmastm,   pbmastm,   pbmastm_state,   empty_init, "Parker Brothers", "Electronic Master Mind (Parker Brothers)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW ) // ***
SYST( 1979, stopthief,  0,         0,      stopthief, stopthief, stopthief_state, empty_init, "Parker Brothers", "Stop Thief: Electronic Cops and Robbers (Electronic Crime Scanner)", MACHINE_SUPPORTS_SAVE ) // ***
SYST( 1979, stopthiefp, stopthief, 0,      stopthief, stopthief, stopthief_state, empty_init, "Parker Brothers", "Stop Thief: Electronic Cops and Robbers (Electronic Crime Scanner) (patent)", MACHINE_SUPPORTS_SAVE ) // ***
SYST( 1980, bankshot,   0,         0,      bankshot,  bankshot,  bankshot_state,  empty_init, "Parker Brothers", "Bank Shot: Electronic Pool", MACHINE_SUPPORTS_SAVE )
SYST( 1980, splitsec,   0,         0,      splitsec,  splitsec,  splitsec_state,  empty_init, "Parker Brothers", "Split Second", MACHINE_SUPPORTS_SAVE )
SYST( 1982, mmerlin,    0,         0,      mmerlin,   mmerlin,   mmerlin_state,   empty_init, "Parker Brothers", "Master Merlin", MACHINE_SUPPORTS_SAVE )
SYST( 1982, lostreas,   0,         0,      lostreas,  lostreas,  lostreas_state,  empty_init, "Parker Brothers", "Lost Treasure: The Electronic Deep-Sea Diving Game (Electronic Dive-Control Center)", MACHINE_SUPPORTS_SAVE ) // ***

SYST( 1978, alphie,     0,         0,      alphie,    alphie,    alphie_state,    empty_init, "Playskool", "Alphie: The Electronic Robot (patent)", MACHINE_SUPPORTS_SAVE ) // ***

SYST( 1980, tcfball,    0,         0,      tcfball,   tcfball,   tcfball_state,   empty_init, "Tandy Corporation", "Championship Football (model 60-2150)", MACHINE_SUPPORTS_SAVE )
SYST( 1980, tcfballa,   tcfball,   0,      tcfballa,  tcfballa,  tcfballa_state,  empty_init, "Tandy Corporation", "Championship Football (model 60-2151)", MACHINE_SUPPORTS_SAVE )
SYST( 1981, comparc,    0,         0,      comparc,   comparc,   comparc_state,   empty_init, "Tandy Corporation", "Computerized Arcade (TMS1100 version, model 60-2159)", MACHINE_SUPPORTS_SAVE ) // some of the games: ***
SYST( 1982, monkeysee,  0,         0,      monkeysee, monkeysee, monkeysee_state, empty_init, "Tandy Corporation", "Monkey See (1982 version)", MACHINE_SUPPORTS_SAVE )
SYST( 1984, t3in1sa,    0,         0,      t3in1sa,   t3in1sa,   t3in1sa_state,   empty_init, "Tandy Corporation", "3 in 1 Sports Arena", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1984, vclock3,    0,         0,      vclock3,   vclock3,   vclock3_state,   empty_init, "Tandy Corporation", "VoxClock 3", MACHINE_SUPPORTS_SAVE )

SYST( 1985, wtalker,    0,         0,      wtalker,   wtalker,   wtalker_state,   empty_init, "Technasonic", "Weight Talker", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_CONTROLS )

SYST( 1976, speechp,    0,         0,      speechp,   speechp,   speechp_state,   empty_init, "Telesensory Systems, Inc.", "Speech+", MACHINE_SUPPORTS_SAVE )

SYST( 1974, tisr16,     0,         0,      tisr16,    tisr16,    tisr16_state,    empty_init, "Texas Instruments", "SR-16", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1975, tisr16ii,   0,         0,      tisr16,    tisr16ii,  tisr16_state,    empty_init, "Texas Instruments", "SR-16 II", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1975, ti1250,     0,         0,      ti1250,    ti1250,    ti1250_state,    empty_init, "Texas Instruments", "TI-1250 (1975 version)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1976, ti1250a,    ti1250,    0,      ti1270,    ti1250,    ti1250_state,    empty_init, "Texas Instruments", "TI-1250 (1976 version)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1976, ti1270,     0,         0,      ti1270,    ti1270,    ti1250_state,    empty_init, "Texas Instruments", "TI-1270", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1975, ti25502,    0,         0,      ti25502,   ti25502,   ti25502_state,   empty_init, "Texas Instruments", "TI-2550 II", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1976, ti25503,    0,         0,      ti25503,   ti25503,   ti25503_state,   empty_init, "Texas Instruments", "TI-2550 III", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1976, ti5100,     0,         0,      ti5100,    ti5100,    ti5100_state,    empty_init, "Texas Instruments", "TI-5100", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1977, ti5200,     0,         0,      ti5200,    ti5200,    ti5200_state,    empty_init, "Texas Instruments", "TI-5200", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1976, ti30,       0,         0,      ti30,      ti30,      ti30_state,      empty_init, "Texas Instruments", "TI-30", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1976, tibusan,    0,         0,      ti30,      tibusan,   ti30_state,      empty_init, "Texas Instruments", "TI Business Analyst", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1977, tiprog,     0,         0,      ti30,      tiprog,    ti30_state,      empty_init, "Texas Instruments", "TI Programmer", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1977, ti1000,     0,         0,      ti1000,    ti1000,    ti1000_state,    empty_init, "Texas Instruments", "TI-1000 (1977 version)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1978, lilprof,    0,         0,      lilprof,   lilprof,   lilprof_state,   empty_init, "Texas Instruments", "Little Professor (1978 version)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1976, lilprofoa,  lilprof,   0,      lilprofo,  lilprofo,  lilprofo_state,  empty_init, "Texas Instruments", "Little Professor (1976 version, rev. A)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1976, lilprofob,  lilprof,   0,      lilprofo,  lilprofo,  lilprofo_state,  empty_init, "Texas Instruments", "Little Professor (1976 version, rev. B)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1976, lilprofo,   lilprof,   0,      lilprofoc, lilprofo,  lilprofo_state,  empty_init, "Texas Instruments", "Little Professor (1976 version, rev. C)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1977, wizatron,   0,         0,      lilprofo,  wizatron,  lilprofo_state,  empty_init, "Texas Instruments", "Wiz-A-Tron", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1977, ti1680,     0,         0,      ti1680,    ti1680,    ti1680_state,    empty_init, "Texas Instruments", "TI-1680", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1977, dataman,    0,         0,      dataman,   dataman,   dataman_state,   empty_init, "Texas Instruments", "DataMan", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1980, mathmarv,   0,         0,      mathmarv,  mathmarv,  mathmarv_state,  empty_init, "Texas Instruments", "Math Marvel", MACHINE_SUPPORTS_SAVE )
SYST( 1979, timaze,     0,         0,      timaze,    timaze,    timaze_state,    empty_init, "Texas Instruments", "unknown electronic maze game (patent)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1979, tithermos,  0,         0,      tithermos, tithermos, tithermos_state, empty_init, "Texas Instruments", "Electronic Digital Thermostat", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )

SYST( 1979, subwars,    0,         0,      subwars,   subwars,   subwars_state,   empty_init, "Tiger Electronics", "Sub Wars (LED version)", MACHINE_SUPPORTS_SAVE )
SYST( 1980, playmaker,  0,         0,      playmaker, playmaker, playmaker_state, empty_init, "Tiger Electronics", "Playmaker: Hockey, Soccer, Basketball", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1980, dxfootb,    0,         0,      dxfootb,   dxfootb,   dxfootb_state,   empty_init, "Tiger Electronics", "Deluxe Football with Instant Replay", MACHINE_SUPPORTS_SAVE )
SYST( 1979, copycat,    0,         0,      copycat,   copycat,   copycat_state,   empty_init, "Tiger Electronics", "Copy Cat (model 7-520)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, copycata,   copycat,   0,      copycata,  copycata,  copycata_state,  empty_init, "Tiger Electronics", "Copy Cat (model 7-522)", MACHINE_SUPPORTS_SAVE )
SYST( 1981, ditto,      0,         0,      ditto,     ditto,     ditto_state,     empty_init, "Tiger Electronics", "Ditto", MACHINE_SUPPORTS_SAVE )
SYST( 1981, fingbowl,   0,         0,      fingbowl,  fingbowl,  fingbowl_state,  empty_init, "Tiger Electronics", "Finger Bowl", MACHINE_SUPPORTS_SAVE ) // some of the games: ***
SYST( 1982, t7in1ss,    0,         0,      t7in1ss,   t7in1ss,   t7in1ss_state,   empty_init, "Tiger Electronics", "7 in 1 Sports Stadium", MACHINE_SUPPORTS_SAVE )

SYST( 1979, tmvolleyb,  0,         0,      tmvolleyb, tmvolleyb, tmvolleyb_state, empty_init, "Tomy", "Volleyball (Tomy)", MACHINE_SUPPORTS_SAVE )
SYST( 1979, tbreakup,   0,         0,      tbreakup,  tbreakup,  tbreakup_state,  empty_init, "Tomy", "Break Up (Tomy)", MACHINE_SUPPORTS_SAVE )
SYST( 1980, phpball,    0,         0,      phpball,   phpball,   phpball_state,   empty_init, "Tomy", "Power House Pinball", MACHINE_SUPPORTS_SAVE )

SYST( 1982, tdracula,   0,         0,      tdracula,  tdracula,  tdracula_state,  empty_init, "Tsukuda", "The Dracula (Tsukuda)", MACHINE_SUPPORTS_SAVE )
SYST( 1983, slepachi,   0,         0,      slepachi,  slepachi,  slepachi_state,  empty_init, "Tsukuda", "Slot Elepachi", MACHINE_SUPPORTS_SAVE )

SYST( 1980, scruiser,   0,         0,      scruiser,  scruiser,  scruiser_state,  empty_init, "U.S. Games Corporation", "Space Cruiser (U.S. Games)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1980, ssports4,   0,         0,      ssports4,  ssports4,  ssports4_state,  empty_init, "U.S. Games Corporation", "Super Sports-4", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )

SYST( 1983, xl25,       0,         0,      xl25,      xl25,      xl25_state,      empty_init, "Vulcan Electronics", "XL 25", MACHINE_SUPPORTS_SAVE )

// ***: As far as MAME is concerned, the game is emulated fine. But for it to be playable, it requires interaction
// with other, unemulatable, things eg. game board/pieces, book, playing cards, pen & paper, etc.



hh_ucom4.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Kevin Horton, Sean Riddle
/*******************************************************************************

NEC uCOM4 MCU tabletops/handhelds or other simple devices, most of them
(emulated ones) are VFD electronic games/toys.

known chips:

  serial  device   etc.
----------------------------------------------------------------
  055     uPD546C  1978, Fidelity Checker Challenger (CR) -> fidelity/checkc2.cpp

 @017     uPD552C  1979, Bambino UFO Master-Blaster Station (ET-02)
 @042     uPD552C  1980, Tomy Cosmic Combat (TN-??)
 @043     uPD552C  1979, Bambino Kick The Goal Soccer (ET-10)
 *044     uPD552C  1979, Bambino Lucky Puck Ice Hockey (ET-08)
 @048     uPD552C  1980, Tomy Tennis (TN-04)
 @049     uPD552C  1981, Bambino Safari (ET-11)
 @054     uPD552C  1980, Epoch Invader From Space

 *024     uPD553C  1981, JIL SX-200 (radio scanner)
 @031     uPD553C  1979, Bambino Super Star Football (ET-03)
 @049     uPD553C  1979, Mego Mini-Vid: Break Free
 @055     uPD553C  1980, Bambino Space Laser Fight (ET-12)
 *073     uPD553C  1980, Sony ST-J75 FM Stereo Tuner
 @080     uPD553C  1980, Epoch Electronic Football
 @084     uPD553C  1980, Bandai Gunfighter
 *102     uPD553C  1981, Bandai Block Out
 @103     uPD553C  1981, Bandai Galaxian
 @153     uPD553C  1981, Epoch Galaxy II
 @160     uPD553C  1982, Tomy Pac Man (TN-08)
 *167     uPD553C  1982, Sony SL models (betamax) (have dump)
 @170     uPD553C  1982, Bandai Crazy Climber
 @192     uPD553C  1982, Tomy Scramble (TN-10)
 @202     uPD553C  1982, Epoch Astro Command
 @206     uPD553C  1982, Epoch Dracula
 *207     uPD553C  1982, Sony SL-J30 (tape/cd deck)
 @209     uPD553C  1982, Tomy Caveman (TN-12)
 @258     uPD553C  1984, Tomy Alien Chase (TN-16)
 *296     uPD553C  1984, Epoch Computer Beam Gun Professional

 @511     uPD557LC 1980, Takatoku Toys Game Robot 9/Mego Fabulous Fred
 @512     uPD557LC 1980, Castle Toy Tactix
 @513     uPD557LC 1980, Castle Toy Name That Tune

 @060     uPD650C  1979, Mattel Computer Gin
  085     uPD650C  1980, Roland TR-808 -> roland/roland_tr808.cpp
 *127     uPD650C  198?, Sony OA-S1100 Typecorder (subcpu, have dump)
  128     uPD650C  1981, Roland TR-606 -> roland/roland_tr606.cpp
  133     uPD650C  1982, Roland TB-303 -> roland/roland_tb303.cpp

  (* means undumped unless noted, @ denotes it's in this driver)

================================================================================

Commonly used VFD(vacuum fluorescent display) are by NEC or Futaba.

NEC FIP9AM20T (example, Epoch Astro Command)
       grcss

FIP = fluorescent indicator panel
g = number of grids
r = revision of the VFD
c = custom display
s = unique display part number

VFD colors used in MAME SVGs:
- #20ffe0: cyan (alone)
- #80fff8: cyan (multi color VFD, relatively the brightest)
- #ff4820: red
- #50ff30: green
- #4840ff: blue
- #ffff48: yellow

It's hard to determine if a game is supposed to have yellow instead of green,
since after decades passed, all the colors fade. It probably has a larger
effect when a color is a mixture of 2 phosphors.

Color overlays are mostly handled in the SVG, since it's often not possible
getting the right color when doing it in a .lay file (eg. changing cpacman
cyan to yellow)

================================================================================

ROM source notes when dumped from another title, but confident it's the same:
- astrocmd: Tandy Astro Command
- caveman: Tandy Caveman
- grobot9: Mego Fabulous Fred

*******************************************************************************/

#include "emu.h"

#include "cpu/ucom4/ucom4.h"
#include "sound/spkrdev.h"
#include "video/hlcd0515.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "alnchase.lh"
#include "bmsafari.lh"
#include "ctntune.lh"
#include "efball.lh"
#include "grobot9.lh"
#include "mcompgin.lh"
#include "mvbfree.lh"
#include "splasfgt.lh"
#include "tactix.lh"
#include "tccombat.lh"
#include "tmtennis.lh"
#include "ufombs.lh"

//#include "hh_ucom4_test.lh" // common test-layout - no svg artwork(yet), use external artwork


namespace {

class hh_ucom4_state : public driver_device
{
public:
	hh_ucom4_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_speaker(*this, "speaker"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(single_interrupt_line);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// devices
	required_device<ucom4_cpu_device> m_maincpu;
	optional_device<pwm_display_device> m_display;
	optional_device<speaker_sound_device> m_speaker;
	optional_ioport_array<6> m_inputs; // max 6

	// misc common
	u8 m_port[9] = { }; // MCU port A-I write data (optional)
	u8 m_int = 0;       // MCU INT pin state
	u16 m_inp_mux = 0;  // multiplexed inputs mask

	u32 m_grid = 0;     // VFD current row data
	u32 m_plate = 0;    // VFD current column data

	u8 read_inputs(int columns);
	void refresh_interrupts(void);
	void set_interrupt(int state);

	enum
	{
		PORTA = 0,
		PORTB,
		PORTC,
		PORTD,
		PORTE,
		PORTF,
		PORTG,
		PORTH,
		PORTI
	};
};


// machine start/reset

void hh_ucom4_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_port));
	save_item(NAME(m_int));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_grid));
	save_item(NAME(m_plate));
}

void hh_ucom4_state::machine_reset()
{
	refresh_interrupts();
}



/*******************************************************************************

  Helper Functions

*******************************************************************************/

// generic input handlers

u8 hh_ucom4_state::read_inputs(int columns)
{
	u8 ret = 0;

	// read selected input rows
	for (int i = 0; i < columns; i++)
		if (BIT(m_inp_mux, i))
			ret |= m_inputs[i]->read();

	return ret;
}


// interrupt handling

void hh_ucom4_state::refresh_interrupts()
{
	m_maincpu->set_input_line(0, m_int ? ASSERT_LINE : CLEAR_LINE);
}

void hh_ucom4_state::set_interrupt(int state)
{
	state = state ? 1 : 0;

	if (state != m_int)
	{
		if (machine().phase() >= machine_phase::RESET)
			m_maincpu->set_input_line(0, state ? ASSERT_LINE : CLEAR_LINE);
		m_int = state;
	}
}

INPUT_CHANGED_MEMBER(hh_ucom4_state::single_interrupt_line)
{
	set_interrupt(newval);
}



/*******************************************************************************

  Minidrivers (subclass, I/O, Inputs, Machine Config, ROM Defs)

*******************************************************************************/

/*******************************************************************************

  Bambino UFO Master-Blaster Station (manufactured in Japan)
  * PCB label: Emix Corp. ET-02
  * NEC uCOM-44 MCU, label EMIX D552C 017, 2-bit sound
  * cyan VFD Emix-101, with blue window

  This is Bambino's first game, it is not known if ET-01 exists. Emix Corp.
  wasn't initially a toy company, the first release was through Tomy. Emix
  created the Bambino brand afterwards. It is claimed to be the first
  computerized VFD game (true, unless TI Speak & Spell or M.E.M. Memoquiz
  from 1978 are considered VFD games)

  known releases:
  - Japan: "Missile Guerilla Warfare Maneuvers", published by Tomy
  - World: UFO Master-Blaster Station, published by Bambino

*******************************************************************************/

class ufombs_state : public hh_ucom4_state
{
public:
	ufombs_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void ufombs(machine_config &config);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(offs_t offset, u8 data);
	void speaker_w(u8 data);
};

// handlers

void ufombs_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void ufombs_state::grid_w(offs_t offset, u8 data)
{
	// F,G,H0: vfd grid
	int shift = (offset - PORTF) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);
	update_display();
}

void ufombs_state::plate_w(offs_t offset, u8 data)
{
	// C,D012,I: vfd plate
	int shift = (offset == PORTI) ? 8 : (offset - PORTC) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void ufombs_state::speaker_w(u8 data)
{
	// E01: speaker out
	m_speaker->level_w(data & 3);
}

// inputs

static INPUT_PORTS_START( ufombs )
	PORT_START("IN.0") // port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_2WAY
	PORT_BIT( 0x04, 0x04, IPT_CUSTOM ) PORT_CONDITION("IN.0", 0x0a, EQUALS, 0x00) // pad in the middle, pressed when joystick is centered
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_2WAY

	PORT_START("IN.1") // port B
	PORT_CONFNAME( 0x07, 0x01, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_CONFSETTING(    0x04, "3" )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void ufombs_state::ufombs(machine_config &config)
{
	// basic machine hardware
	NEC_D552(config, m_maincpu, 400000); // approximation
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->read_b().set_ioport("IN.1");
	m_maincpu->write_c().set(FUNC(ufombs_state::plate_w));
	m_maincpu->write_d().set(FUNC(ufombs_state::plate_w));
	m_maincpu->write_e().set(FUNC(ufombs_state::speaker_w));
	m_maincpu->write_f().set(FUNC(ufombs_state::grid_w));
	m_maincpu->write_g().set(FUNC(ufombs_state::grid_w));
	m_maincpu->write_h().set(FUNC(ufombs_state::grid_w));
	m_maincpu->write_i().set(FUNC(ufombs_state::plate_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(243, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(9, 11);
	config.set_default_layout(layout_ufombs);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( ufombs )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "d552c_017", 0x0000, 0x0400, CRC(0e208cb3) SHA1(57db6566916c94325e2b67ccb94b4ea3b233487d) )

	ROM_REGION( 222402, "screen", 0)
	ROM_LOAD( "ufombs.svg", 0, 222402, CRC(322c12d5) SHA1(221ea9f95af8ff30b0b83440a9f1b5302e25bce6) )
ROM_END





/*******************************************************************************

  Bambino Super Star Football (manufactured in Japan)
  * PCB label: Emix Corp. ET-03
  * NEC uCOM-43 MCU, label D553C 031, 2-bit sound
  * cyan VFD Emix-102

  The game was rereleased in 1982 as Football Classic (ET-0351), with an
  improved cyan/green/red VFD.

  Press the Kick button to start the game, an automatic sequence follows.
  Then choose a formation(A,B,C) and either pass the ball, and/or start
  running. For more information, refer to the official manual.

*******************************************************************************/

class ssfball_state : public hh_ucom4_state
{
public:
	ssfball_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void ssfball(machine_config &config);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(offs_t offset, u8 data);
	u8 input_b_r();
};

// handlers

void ssfball_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void ssfball_state::grid_w(offs_t offset, u8 data)
{
	// C,D(,E3): vfd grid
	int shift = (offset - PORTC) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);
	update_display();
}

void ssfball_state::plate_w(offs_t offset, u8 data)
{
	m_port[offset] = data;

	// E,F,G,H,I(not all!): vfd plate
	int shift = (offset - PORTE) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	// F3,G3: input mux + speaker
	m_inp_mux = (m_port[PORTF] >> 3 & 1) | (m_port[PORTG] >> 2 & 2);
	m_speaker->level_w(m_inp_mux);

	// E3: vfd grid
	if (offset == PORTE)
		grid_w(offset, data >> 3 & 1);
	else
		update_display();
}

u8 ssfball_state::input_b_r()
{
	// B: input port 2, where B3 is multiplexed
	return m_inputs[2]->read() | read_inputs(2);
}

// inputs

/* physical button layout and labels are like this:

    [A]    [B]    [C]    [PASS]  [KICK/
       ^FORMATION^                DISPLAY]

                                 [^]
                         [<>]
    (game lvl sw)                [v]
    1<---OFF--->2
*/

static INPUT_PORTS_START( ssfball )
	PORT_START("IN.0") // F3 port B3
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Formation A")

	PORT_START("IN.1") // G3 port B3
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x08, 0x00, "Game Level" )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x08, "2" )

	PORT_START("IN.2") // port B
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START ) PORT_NAME("Kick/Display")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("Formation C")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_NAME("Formation B")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) // multiplexed, handled in input_b_r

	PORT_START("IN.3") // port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Forward")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Pass")
INPUT_PORTS_END

// config

void ssfball_state::ssfball(machine_config &config)
{
	// basic machine hardware
	NEC_D553(config, m_maincpu, 400000); // approximation
	m_maincpu->read_a().set_ioport("IN.3");
	m_maincpu->read_b().set(FUNC(ssfball_state::input_b_r));
	m_maincpu->write_c().set(FUNC(ssfball_state::grid_w));
	m_maincpu->write_d().set(FUNC(ssfball_state::grid_w));
	m_maincpu->write_e().set(FUNC(ssfball_state::plate_w));
	m_maincpu->write_f().set(FUNC(ssfball_state::plate_w));
	m_maincpu->write_g().set(FUNC(ssfball_state::plate_w));
	m_maincpu->write_h().set(FUNC(ssfball_state::plate_w));
	m_maincpu->write_i().set(FUNC(ssfball_state::plate_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 482);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(9, 19);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( ssfball )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d553c_031", 0x0000, 0x0800, CRC(ff5d91d0) SHA1(9b2c0ae45f1e3535108ee5fef8a9010e00c8d5c3) )

	ROM_REGION( 331365, "screen", 0)
	ROM_LOAD( "ssfball.svg", 0, 331365, CRC(25685f1f) SHA1(61ff517c2d766dae49170c807e75f99333cf5c88) )
ROM_END

ROM_START( bmcfball )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d553c_031", 0x0000, 0x0800, CRC(ff5d91d0) SHA1(9b2c0ae45f1e3535108ee5fef8a9010e00c8d5c3) )

	ROM_REGION( 331365, "screen", 0)
	ROM_LOAD( "bmcfball.svg", 0, 331365, CRC(15cbfc6e) SHA1(588078824e9ce27c397a536a2c6cd2b80142b50c) )
ROM_END





/*******************************************************************************

  Bambino Kick The Goal Soccer
  * PCB label: Emix Corp. ET-10/08 (PCB is for 2 possible games)
  * NEC uCOM-44 MCU, label D552C 043, 1-bit sound
  * cyan VFD Emix-105, with bezel overlay

  Press the Display button twice to start the game. Action won't start until
  player 1 presses one of the directional keys. In 2-player mode, player 2
  controls the goalkeeper, defensive players are still controlled by the CPU.

*******************************************************************************/

class bmsoccer_state : public hh_ucom4_state
{
public:
	bmsoccer_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void bmsoccer(machine_config &config);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(offs_t offset, u8 data);
	u8 input_a_r();
};

// handlers

void bmsoccer_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void bmsoccer_state::grid_w(offs_t offset, u8 data)
{
	// C01: input mux
	if (offset == PORTC)
		m_inp_mux = data & 3;

	// C,D(,E3): vfd grid
	int shift = (offset - PORTC) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);
	update_display();
}

void bmsoccer_state::plate_w(offs_t offset, u8 data)
{
	// G3: speaker out
	if (offset == PORTG)
		m_speaker->level_w(data >> 3 & 1);

	// E012,F012,G012,H,I: vfd plate
	int shift = (offset - PORTE) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	// E3: vfd grid
	if (offset == PORTE)
		grid_w(offset, data >> 3 & 1);
	else
		update_display();
}

u8 bmsoccer_state::input_a_r()
{
	// port A: multiplexed inputs
	return read_inputs(2);
}

// inputs

static INPUT_PORTS_START( bmsoccer )
	PORT_START("IN.0") // C0 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY PORT_NAME("Ball-carrier Right")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY PORT_NAME("Ball-carrier Down")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY PORT_NAME("Ball-carrier Left")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY PORT_NAME("Ball-carrier Up")

	PORT_START("IN.1") // C1 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL PORT_16WAY PORT_NAME("Goalkeeper Left") // note: swap buttons if viewed from the same angle as player 1
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL PORT_16WAY PORT_NAME("Goalkeeper Right")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // port B
	PORT_CONFNAME( 0x01, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x01, "2" )
	PORT_CONFNAME( 0x02, 0x00, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Display/Banana Shoot")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Shoot")
INPUT_PORTS_END

// config

void bmsoccer_state::bmsoccer(machine_config &config)
{
	// basic machine hardware
	NEC_D552(config, m_maincpu, 400000); // approximation
	m_maincpu->read_a().set(FUNC(bmsoccer_state::input_a_r));
	m_maincpu->read_b().set_ioport("IN.2");
	m_maincpu->write_c().set(FUNC(bmsoccer_state::grid_w));
	m_maincpu->write_d().set(FUNC(bmsoccer_state::grid_w));
	m_maincpu->write_e().set(FUNC(bmsoccer_state::plate_w));
	m_maincpu->write_f().set(FUNC(bmsoccer_state::plate_w));
	m_maincpu->write_g().set(FUNC(bmsoccer_state::plate_w));
	m_maincpu->write_h().set(FUNC(bmsoccer_state::plate_w));
	m_maincpu->write_i().set(FUNC(bmsoccer_state::plate_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(271, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(9, 19);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( bmsoccer )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "d552c_043", 0x0000, 0x0400, CRC(10c2a4ea) SHA1(6ebca7d406e22ff7a8cd529579b55a700da487b4) )

	ROM_REGION( 273809, "screen", 0)
	ROM_LOAD( "bmsoccer.svg", 0, 273809, CRC(e9e29724) SHA1(58a505ed72952ba8e37fa15b493742f6af458eee) )
ROM_END





/*******************************************************************************

  Bambino Safari (manufactured in Japan)
  * PCB label: Emix Corp. ET-11
  * NEC uCOM-44 MCU, label EMIX D552C 049, 1-bit sound
  * cyan VFD Emix-108
  * color overlay: green (optional)

*******************************************************************************/

class bmsafari_state : public hh_ucom4_state
{
public:
	bmsafari_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void bmsafari(machine_config &config);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(offs_t offset, u8 data);
	void speaker_w(u8 data);
};

// handlers

void bmsafari_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void bmsafari_state::grid_w(offs_t offset, u8 data)
{
	// C,D(,E3): vfd grid
	int shift = (offset - PORTC) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);
	update_display();
}

void bmsafari_state::plate_w(offs_t offset, u8 data)
{
	// E012,H,I: vfd plate
	int shift = (offset == PORTE) ? 8 : (offset - PORTH) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	// E3: vfd grid
	if (offset == PORTE)
		grid_w(offset, data >> 3 & 1);
	else
		update_display();
}

void bmsafari_state::speaker_w(u8 data)
{
	// G0: speaker out
	m_speaker->level_w(data & 1);
}

// inputs

static INPUT_PORTS_START( bmsafari )
	PORT_START("IN.0") // port A
	PORT_CONFNAME( 0x07, 0x04, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x04, "1" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_CONFSETTING(    0x01, "3" )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 )

	PORT_START("IN.1") // port B
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
INPUT_PORTS_END

// config

void bmsafari_state::bmsafari(machine_config &config)
{
	// basic machine hardware
	NEC_D552(config, m_maincpu, 400000); // approximation
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->read_b().set_ioport("IN.1");
	m_maincpu->write_c().set(FUNC(bmsafari_state::grid_w));
	m_maincpu->write_d().set(FUNC(bmsafari_state::grid_w));
	m_maincpu->write_e().set(FUNC(bmsafari_state::plate_w));
	m_maincpu->write_g().set(FUNC(bmsafari_state::speaker_w));
	m_maincpu->write_h().set(FUNC(bmsafari_state::plate_w));
	m_maincpu->write_i().set(FUNC(bmsafari_state::plate_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(248, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(9, 11);
	config.set_default_layout(layout_bmsafari);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( bmsafari )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "d552c_049", 0x0000, 0x0400, CRC(82fa3cbe) SHA1(019e7ec784e977eba09997fc46af253054fb222c) )

	ROM_REGION( 275393, "screen", 0)
	ROM_LOAD( "bmsafari.svg", 0, 275393, CRC(a6a91b41) SHA1(11e5db6d57e2206a18eca11894d91d7ae2d41544) )
ROM_END





/*******************************************************************************

  Bambino Space Laser Fight (manufactured in Japan)
  * PCB label: Emix Corp. ET-12
  * NEC uCOM-43 MCU, label D553C 055, 2-bit sound
  * cyan VFD Emix-104, with blue or transparent window

  This is basically a revamp of their earlier Boxing game (ET-06), case and
  buttons are exactly the same.

*******************************************************************************/

class splasfgt_state : public hh_ucom4_state
{
public:
	splasfgt_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void splasfgt(machine_config &config);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(offs_t offset, u8 data);
	u8 input_b_r();
};

// handlers

void splasfgt_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void splasfgt_state::grid_w(offs_t offset, u8 data)
{
	// G,H,I0: vfd grid
	int shift = (offset - PORTG) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);

	// G(grid 0-3): input mux
	m_inp_mux = m_grid & 0xf;

	// I2: vfd plate
	if (offset == PORTI)
		plate_w(4 + PORTC, data >> 2 & 1);
	else
		update_display();
}

void splasfgt_state::plate_w(offs_t offset, u8 data)
{
	// F01: speaker out
	if (offset == PORTF)
		m_speaker->level_w(data & 3);

	// C,D,E,F23(,I2): vfd plate
	int shift = (offset - PORTC) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

u8 splasfgt_state::input_b_r()
{
	// B: multiplexed buttons
	return read_inputs(4);
}

// inputs

/* physical button layout and labels are like this:

    * left = P1 side *                                         * right = P2 side * (note: in 1P mode, switch sides between turns)

    [  JUMP  ]  [ HIGH ]        (players sw)                   [ HIGH ]  [  JUMP  ]
                                1<--->2         [START/
    [STRAIGHT]  [MEDIUM]                         RESET]        [MEDIUM]  [STRAIGHT]
                                1<---OFF--->2
    [ STOOP  ]  [ LOW  ]        (skill lvl sw)                 [ LOW  ]  [ STOOP  ]
*/

static INPUT_PORTS_START( splasfgt )
	PORT_START("IN.0") // G0 port B
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("P1 Position Straight")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("P1 Position Jump")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Z) PORT_NAME("P1 Position Stoop")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // G1 port B
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("P1 Beam High")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("P1 Beam Medium")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME("P1 Beam Low")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // G2 port B
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_K) PORT_NAME("P2 Position Straight")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_I) PORT_NAME("P2 Position Jump")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_COMMA) PORT_NAME("P2 Position Stoop")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // G3 port B
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_U) PORT_NAME("P2 Beam High")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_J) PORT_NAME("P2 Beam Medium")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_M) PORT_NAME("P2 Beam Low")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // port A
	PORT_CONFNAME( 0x01, 0x00, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x01, "2" )
	PORT_CONFNAME( 0x02, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void splasfgt_state::splasfgt(machine_config &config)
{
	// basic machine hardware
	NEC_D553(config, m_maincpu, 400000); // approximation
	m_maincpu->read_a().set_ioport("IN.4");
	m_maincpu->read_b().set(FUNC(splasfgt_state::input_b_r));
	m_maincpu->write_c().set(FUNC(splasfgt_state::plate_w));
	m_maincpu->write_d().set(FUNC(splasfgt_state::plate_w));
	m_maincpu->write_e().set(FUNC(splasfgt_state::plate_w));
	m_maincpu->write_f().set(FUNC(splasfgt_state::plate_w));
	m_maincpu->write_g().set(FUNC(splasfgt_state::grid_w));
	m_maincpu->write_h().set(FUNC(splasfgt_state::grid_w));
	m_maincpu->write_i().set(FUNC(splasfgt_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 476);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(9, 17);
	config.set_default_layout(layout_splasfgt);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( splasfgt )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d553c_055", 0x0000, 0x0800, CRC(eb471fbd) SHA1(f06cfe567bf6f9ed4dcdc88acdcfad50cd370a02) )

	ROM_REGION( 246609, "screen", 0)
	ROM_LOAD( "splasfgt.svg", 0, 246609, CRC(df3270cc) SHA1(3bf059397728f6aca211ae55074fc7bc76fb9058) )
ROM_END





/*******************************************************************************

  Bandai Gunfighter
  * PCB label: KAKEN CORP., PT-256, PT-256B/PT-258 for the button PCBs
  * NEC uCOM-43 MCU, label D553C 084, 1-bit sound
  * cyan VFD NEC FIP9BM18T

  This is presumedly Bandai's first VFD handheld game. Japanese versions of
  Bandai VFD games often had an "FL" prefix. Early games made it appear as if
  it was part of the game title (maybe initially it really was). But newer
  games indicate that "FL" is meant to be a category or series, it probably
  simply means "Fluorescent Light". The prefix was removed for export versions.

  known releases:
  - Japan: FL Gun Professional (model 16150), published by Bandai
  - World: Gunfighter (model 8006), published by Bandai
  - Canada: Duel, published by Bandai ("FL Gun Professional" on handheld itself)

*******************************************************************************/

class bgunf_state : public hh_ucom4_state
{
public:
	bgunf_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void bgunf(machine_config &config);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(offs_t offset, u8 data);
	u8 input_r();
};

// handlers

void bgunf_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void bgunf_state::grid_w(offs_t offset, u8 data)
{
	m_port[offset] = data;

	// G01: input mux
	m_inp_mux = m_port[PORTG] & 3;

	// I1: speaker out
	m_speaker->level_w(m_port[PORTI] >> 1 & 1);

	// G,H,I0: vfd grid
	int shift = (offset - PORTG) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);
	update_display();
}

void bgunf_state::plate_w(offs_t offset, u8 data)
{
	// C,D,E,F: vfd plate
	int shift = (offset - PORTC) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

u8 bgunf_state::input_r()
{
	// A: multiplexed inputs
	return read_inputs(2);
}

// inputs

static INPUT_PORTS_START( bgunf )
	PORT_START("IN.0") // G0 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // G1 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // port B
	PORT_CONFNAME( 0x0f, 0x01, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x01, "Auto 1" )
	PORT_CONFSETTING(    0x02, "Auto 2" )
	PORT_CONFSETTING(    0x04, "Auto 3" )
	PORT_CONFSETTING(    0x08, "Manual" )
INPUT_PORTS_END

// config

void bgunf_state::bgunf(machine_config &config)
{
	// basic machine hardware
	NEC_D553(config, m_maincpu, 400000); // approximation
	m_maincpu->read_a().set(FUNC(bgunf_state::input_r));
	m_maincpu->read_b().set_ioport("IN.2");
	m_maincpu->write_c().set(FUNC(bgunf_state::plate_w));
	m_maincpu->write_d().set(FUNC(bgunf_state::plate_w));
	m_maincpu->write_e().set(FUNC(bgunf_state::plate_w));
	m_maincpu->write_f().set(FUNC(bgunf_state::plate_w));
	m_maincpu->write_g().set(FUNC(bgunf_state::grid_w));
	m_maincpu->write_h().set(FUNC(bgunf_state::grid_w));
	m_maincpu->write_i().set(FUNC(bgunf_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 528);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(9, 16);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( bgunf )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d553c_084", 0x0000, 0x0800, CRC(a924ba9b) SHA1(31a0beaf31b530ec1a7e25b316acdf22f78e4423) )

	ROM_REGION( 140908, "screen", 0)
	ROM_LOAD( "bgunf.svg", 0, 140908, CRC(6bbeb80b) SHA1(b30d701bbc89a0c32f640ea734b89070bbfe4820) )
ROM_END





/*******************************************************************************

  Bandai Galaxian
  * PCB label: SM-008
  * NEC uCOM-43 MCU, label D553C 103, 2-bit sound
  * cyan/red VFD Futaba DM-12Z
  * color overlay: score/bottom rows: yellow, 2nd/3rd rows: pink

  known releases:
  - Japan: FL Beam Galaxian (model 16167), published by Bandai
  - World: Galaxian, published by Bandai ("FL Beam Galaxian" on handheld itself)

  There's also a 2nd version on a NEC D7502G MCU, this one doesn't have
  the Star Wars jingle.

*******************************************************************************/

class bgalaxn_state : public hh_ucom4_state
{
public:
	bgalaxn_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void bgalaxn(machine_config &config);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(offs_t offset, u8 data);
};

// handlers

void bgalaxn_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void bgalaxn_state::grid_w(offs_t offset, u8 data)
{
	// I12: speaker out
	if (offset == PORTI)
		m_speaker->level_w(data >> 1 & 3);

	// C,D,I0: vfd grid
	int shift = (offset == PORTI) ? 8 : (offset - PORTC) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);
	update_display();
}

void bgalaxn_state::plate_w(offs_t offset, u8 data)
{
	// E,F,G,H: vfd plate
	int shift = (offset - PORTE) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

// inputs

static INPUT_PORTS_START( bgalaxn )
	PORT_START("IN.0") // port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_CONFNAME( 0x04, 0x00, "Factory Test" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x04, DEF_STR( On ) )
	PORT_CONFNAME( 0x08, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x08, "2" )

	PORT_START("IN.1") // INT
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_ucom4_state::single_interrupt_line), 0)
INPUT_PORTS_END

// config

void bgalaxn_state::bgalaxn(machine_config &config)
{
	// basic machine hardware
	NEC_D553(config, m_maincpu, 400_kHz_XTAL);
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->write_c().set(FUNC(bgalaxn_state::grid_w));
	m_maincpu->write_d().set(FUNC(bgalaxn_state::grid_w));
	m_maincpu->write_e().set(FUNC(bgalaxn_state::plate_w));
	m_maincpu->write_f().set(FUNC(bgalaxn_state::plate_w));
	m_maincpu->write_g().set(FUNC(bgalaxn_state::plate_w));
	m_maincpu->write_h().set(FUNC(bgalaxn_state::plate_w));
	m_maincpu->write_i().set(FUNC(bgalaxn_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(301, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(9, 16);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( bgalaxn )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d553c_103", 0x0000, 0x0800, CRC(a6330519) SHA1(23498346aee1de93f11045a30aa331271052c1f4) )

	ROM_REGION( 153620, "screen", 0)
	ROM_LOAD( "bgalaxn.svg", 0, 153620, CRC(6ef7249d) SHA1(1aea3e4c8e17ceee377340e2798799158d90280d) )
ROM_END





/*******************************************************************************

  Bandai Crazy Climber (manufactured in Japan)
  * PCB labels: SM-020/SM-021
  * NEC uCOM-43 MCU, label D553C 170, 1-bit sound
  * cyan/red/green VFD NEC FIP6AM2-T no. 1-8 2
  * color overlay: score/bird rows: pink

  known releases:
  - Japan: FL Crazy Climbing, published by Bandai
  - USA: Crazy Climber, published by Bandai

*******************************************************************************/

class bcclimbr_state : public hh_ucom4_state
{
public:
	bcclimbr_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void bcclimbr(machine_config &config);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(offs_t offset, u8 data);
};

// handlers

void bcclimbr_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void bcclimbr_state::grid_w(offs_t offset, u8 data)
{
	// I2: speaker out
	if (offset == PORTI)
		m_speaker->level_w(data >> 2 & 1);

	// H,I01: vfd grid
	int shift = (offset - PORTH) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);
	update_display();
}

void bcclimbr_state::plate_w(offs_t offset, u8 data)
{
	// C,D,E,F,G: vfd plate
	int shift = (offset - PORTC) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

// inputs

static INPUT_PORTS_START( bcclimbr )
	PORT_START("IN.0") // port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_LEFT )

	PORT_START("IN.1") // port B
	PORT_CONFNAME( 0x01, 0x00, "Factory Test" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_RIGHT )
INPUT_PORTS_END

// config

void bcclimbr_state::bcclimbr(machine_config &config)
{
	// basic machine hardware
	NEC_D553(config, m_maincpu, 400_kHz_XTAL);
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->read_b().set_ioport("IN.1");
	m_maincpu->write_c().set(FUNC(bcclimbr_state::plate_w));
	m_maincpu->write_d().set(FUNC(bcclimbr_state::plate_w));
	m_maincpu->write_e().set(FUNC(bcclimbr_state::plate_w));
	m_maincpu->write_f().set(FUNC(bcclimbr_state::plate_w));
	m_maincpu->write_g().set(FUNC(bcclimbr_state::plate_w));
	m_maincpu->write_h().set(FUNC(bcclimbr_state::grid_w));
	m_maincpu->write_i().set(FUNC(bcclimbr_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(310, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(6, 20);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( bcclimbr )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d553c_170", 0x0000, 0x0800, CRC(fc2eabdb) SHA1(0f5cc854be7fdf105d9bd2114659d40c65f9d782) )

	ROM_REGION( 219983, "screen", 0)
	ROM_LOAD( "bcclimbr.svg", 0, 219983, CRC(776a80ad) SHA1(61495edf276b517118280f6c8bdbf0c812338ba5) )
ROM_END





/*******************************************************************************

  Castle Toy Tactix
  * NEC uCOM-43 MCU, label D557LC 512, 1-bit sound
  * 16 LEDs behind buttons

  Tactix is similar to Merlin, for 1 or 2 players. In 2-player mode, simply
  don't press the Comp Turn button. The four included minigames are:
  1: Capture (reversi)
  2: Jump-Off (peg solitaire)
  3: Triple Play (3 in a row)
  4: Concentration (memory)

*******************************************************************************/

class tactix_state : public hh_ucom4_state
{
public:
	tactix_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void tactix(machine_config &config);

private:
	void leds_w(offs_t offset, u8 data);
	void speaker_w(u8 data);
	void input_w(offs_t offset, u8 data);
	u8 input_r();
};

// handlers

void tactix_state::leds_w(offs_t offset, u8 data)
{
	// D,F: 4*4 led matrix
	m_port[offset] = data;
	m_display->matrix(m_port[PORTF], m_port[PORTD]);
}

void tactix_state::speaker_w(u8 data)
{
	// G0: speaker out
	m_speaker->level_w(data & 1);
}

void tactix_state::input_w(offs_t offset, u8 data)
{
	// C,E0: input mux
	m_port[offset] = data;
	m_inp_mux = (m_port[PORTE] << 4 & 0x10) | m_port[PORTC];
}

u8 tactix_state::input_r()
{
	// A: multiplexed inputs
	return read_inputs(5);
}

// inputs

static INPUT_PORTS_START( tactix )
	PORT_START("IN.0") // C0 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("Button 1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Button 5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Button 9")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Z) PORT_NAME("Button 13")

	PORT_START("IN.1") // C1 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("Button 2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("Button 6")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Button 10")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME("Button 14")

	PORT_START("IN.2") // C2 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("Button 3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("Button 7")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("Button 11")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("Button 15")

	PORT_START("IN.3") // C3 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_NAME("Button 4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("Button 8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("Button 12")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_V) PORT_NAME("Button 16")

	PORT_START("IN.4") // E0 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_NAME("New Game")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Comp Turn")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void tactix_state::tactix(machine_config &config)
{
	// basic machine hardware
	NEC_D557L(config, m_maincpu, 200000); // approximation
	m_maincpu->read_a().set(FUNC(tactix_state::input_r));
	m_maincpu->write_c().set(FUNC(tactix_state::input_w));
	m_maincpu->write_d().set(FUNC(tactix_state::leds_w));
	m_maincpu->write_e().set(FUNC(tactix_state::input_w));
	m_maincpu->write_f().set(FUNC(tactix_state::leds_w));
	m_maincpu->write_g().set(FUNC(tactix_state::speaker_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 4);
	config.set_default_layout(layout_tactix);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( tactix )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d557lc_512", 0x0000, 0x0800, CRC(1df738cb) SHA1(15a5de28a3c03e6894d29c56b5b424983569ccf2) )
ROM_END





/*******************************************************************************

  Castle Toy Name That Tune
  * NEC uCOM-43 MCU, label D557LC 513, 1-bit sound
  * 2 lamps, 1 7seg(+2 fake 7segs above a power-on lamp, showing "0")

  This is a tabletop multiplayer game. Players are meant to place a bid,
  and guess the song (by announcing it to everyone).

*******************************************************************************/

class ctntune_state : public hh_ucom4_state
{
public:
	ctntune_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void ctntune(machine_config &config);

	// start button powers unit back on
	DECLARE_INPUT_CHANGED_MEMBER(start_button) { m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE); }

private:
	void update_display();
	void _7seg_w(offs_t offset, u8 data);
	void speaker_w(u8 data);
	void input_w(offs_t offset, u8 data);
	u8 input_r();
};

// handlers

void ctntune_state::update_display()
{
	u8 sel = m_port[PORTD] >> 3 & 1; // turn off display when power is off
	u8 lamps = m_port[PORTD] & 3;
	u8 digit = (m_port[PORTF] << 4 | m_port[PORTE]) & 0x7f;

	m_display->matrix(sel, lamps << 7 | digit);
}

void ctntune_state::_7seg_w(offs_t offset, u8 data)
{
	// E,F012: 7seg data, F3: N/C
	m_port[offset] = data;
	update_display();
}

void ctntune_state::speaker_w(u8 data)
{
	// G0: speaker out
	m_speaker->level_w(data & 1);
}

void ctntune_state::input_w(offs_t offset, u8 data)
{
	// D3: trigger power-off on falling edge
	if (offset == PORTD && ~data & m_port[PORTD] & 8)
		m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);

	// C,D23: input mux
	// D0,D1: yellow, red lamp
	m_port[offset] = data;
	m_inp_mux = (m_port[PORTD] << 2 & 0x30) | m_port[PORTC];
	update_display();
}

u8 ctntune_state::input_r()
{
	// A: multiplexed inputs
	return read_inputs(6);
}

// inputs

static INPUT_PORTS_START( ctntune )
	PORT_START("IN.0") // C0 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Button 1") // defaults to keyboard Z row
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON9 ) PORT_NAME("Button 5")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON13 ) PORT_NAME("Button 9")

	PORT_START("IN.1") // C1 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Button 2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON10 ) PORT_NAME("Button 6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON14 ) PORT_NAME("Button 10")

	PORT_START("IN.2") // C2 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_NAME("Button 3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON11 ) PORT_NAME("Button 7")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Yellow Button")

	PORT_START("IN.3") // C3 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON8 ) PORT_NAME("Button 4")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON12 ) PORT_NAME("Button 8")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Red Button")

	PORT_START("IN.4") // D2 port A
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_NAME("Play Button")

	PORT_START("IN.5") // D3 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ctntune_state::start_button), 0)
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void ctntune_state::ctntune(machine_config &config)
{
	// basic machine hardware
	NEC_D557L(config, m_maincpu, 200000); // approximation
	m_maincpu->read_a().set(FUNC(ctntune_state::input_r));
	m_maincpu->write_c().set(FUNC(ctntune_state::input_w));
	m_maincpu->write_d().set(FUNC(ctntune_state::input_w));
	m_maincpu->write_e().set(FUNC(ctntune_state::_7seg_w));
	m_maincpu->write_f().set(FUNC(ctntune_state::_7seg_w));
	m_maincpu->write_g().set(FUNC(ctntune_state::speaker_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 7+2);
	m_display->set_segmask(1, 0x7f);
	config.set_default_layout(layout_ctntune);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( ctntune )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d557lc_513", 0x0000, 0x0800, CRC(cd85ee23) SHA1(32b8fc8cb92fc1fd27da9148788a09d3bcd46a92) )
ROM_END





/*******************************************************************************

  Epoch Invader From Space (manufactured in Japan)
  * PCB labels: 36010(A/B)
  * NEC uCOM-44 MCU, label D552C 054, 1-bit sound
  * cyan VFD NEC FIP9AM18T tube no. 0D
  * color overlay: alien rows 1,2: blue, 3,4: yellow, 5: red

  known releases:
  - USA: Invader From Space, published by Epoch
  - UK: Invader From Space, published by Grandstand

*******************************************************************************/

class einspace_state : public hh_ucom4_state
{
public:
	einspace_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void einspace(machine_config &config);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(offs_t offset, u8 data);
};

// handlers

void einspace_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void einspace_state::grid_w(offs_t offset, u8 data)
{
	// I0: speaker out
	if (offset == PORTI)
		m_speaker->level_w(data & 1);

	// C,D,I1: vfd grid
	int shift = (offset == PORTI) ? 8 : (offset - PORTC) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);
	update_display();
}

void einspace_state::plate_w(offs_t offset, u8 data)
{
	// E,F,G,H123: vfd plate
	int shift = (offset - PORTE) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

// inputs

static INPUT_PORTS_START( einspace )
	PORT_START("IN.0") // port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // port B
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_2WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_2WAY
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void einspace_state::einspace(machine_config &config)
{
	// basic machine hardware
	NEC_D552(config, m_maincpu, 400_kHz_XTAL);
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->read_b().set_ioport("IN.1");
	m_maincpu->write_c().set(FUNC(einspace_state::grid_w));
	m_maincpu->write_d().set(FUNC(einspace_state::grid_w));
	m_maincpu->write_e().set(FUNC(einspace_state::plate_w));
	m_maincpu->write_f().set(FUNC(einspace_state::plate_w));
	m_maincpu->write_g().set(FUNC(einspace_state::plate_w));
	m_maincpu->write_h().set(FUNC(einspace_state::plate_w));
	m_maincpu->write_i().set(FUNC(einspace_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(289, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(10, 16);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( einspace )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "d552c_054", 0x0000, 0x0400, CRC(913d9c13) SHA1(f20edb5458e54d2f6d4e45e5d59efd87e05a6f3f) )

	ROM_REGION( 110894, "screen", 0)
	ROM_LOAD( "einspace.svg", 0, 110894, CRC(1ec324b8) SHA1(847621c7d6c10b254b715642d63efc9c30a701c1) )
ROM_END





/*******************************************************************************

  Epoch Electronic Football (manufactured in Japan)
  * PCB labels: 36020(A/B/C)
  * NEC uCOM-43 MCU, label D553C 080, 1-bit sound
  * cyan VFD NEC FIP10AM15T tube no. 0F, with bezel overlay

  known releases:
  - USA: Electronic Football (aka Pro-Bowl Football), published by Epoch
  - Japan: American Football, published by Epoch

*******************************************************************************/

class efball_state : public hh_ucom4_state
{
public:
	efball_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void efball(machine_config &config);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(offs_t offset, u8 data);
};

// handlers

void efball_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void efball_state::grid_w(offs_t offset, u8 data)
{
	// H2: speaker out
	if (offset == PORTH)
		m_speaker->level_w(data >> 2 & 1);

	// F,G,H01: vfd grid
	int shift = (offset - PORTF) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);
	update_display();
}

void efball_state::plate_w(offs_t offset, u8 data)
{
	// D,E,I: vfd plate
	int shift = (offset == PORTI) ? 8 : (offset - PORTD) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

// inputs

static INPUT_PORTS_START( efball )
	PORT_START("IN.0") // port A
	PORT_CONFNAME( 0x01, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" ) // AMA
	PORT_CONFSETTING(    0x01, "2" ) // PRO
	PORT_CONFNAME( 0x02, 0x02, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x02, "1" )
	PORT_CONFSETTING(    0x00, "2" )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("P1 Down-Field")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START2 ) PORT_NAME("P1 Score-Time")

	PORT_START("IN.1") // port B
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Forward")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Pass")

	PORT_START("IN.2") // port C
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Kick Return")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P1 Kick")
INPUT_PORTS_END

// config

void efball_state::efball(machine_config &config)
{
	// basic machine hardware
	NEC_D553(config, m_maincpu, 400_kHz_XTAL);
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->read_b().set_ioport("IN.1");
	m_maincpu->read_c().set_ioport("IN.2");
	m_maincpu->write_d().set(FUNC(efball_state::plate_w));
	m_maincpu->write_e().set(FUNC(efball_state::plate_w));
	m_maincpu->write_f().set(FUNC(efball_state::grid_w));
	m_maincpu->write_g().set(FUNC(efball_state::grid_w));
	m_maincpu->write_h().set(FUNC(efball_state::grid_w));
	m_maincpu->write_i().set(FUNC(efball_state::plate_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 11);
	config.set_default_layout(layout_efball);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( efball )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d553c_080", 0x0000, 0x0800, CRC(54c1027f) SHA1(6cc98074dae9361fa8c0ed6501b6a57ad325ccbd) )
ROM_END





/*******************************************************************************

  Epoch Galaxy II (manufactured in Japan)
  * PCB labels: 19096/96062
  * NEC uCOM-43 MCU, label D553C 153, 1-bit sound
  * cyan/red VFD NEC FIP10xM20T. x = multiple VFD revisions exist, with different
    graphics: rev B no. 1-8, rev. D no. 2-21.
  * color overlay: score: blue, top/bottom rows: yellow, row 2,3: red

  known releases:
  - USA: Galaxy II, published by Epoch
  - Japan: Astro Wars, published by Epoch
  - UK: Astro Wars, published by Grandstand

*******************************************************************************/

class galaxy2_state : public hh_ucom4_state
{
public:
	galaxy2_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void galaxy2b(machine_config &config);
	void galaxy2(machine_config &config);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(offs_t offset, u8 data);
};

// handlers

void galaxy2_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void galaxy2_state::grid_w(offs_t offset, u8 data)
{
	// E3: speaker out
	if (offset == PORTE)
		m_speaker->level_w(data >> 3 & 1);

	// C,D,E01: vfd grid
	int shift = (offset - PORTC) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);
	update_display();
}

void galaxy2_state::plate_w(offs_t offset, u8 data)
{
	// F,G,H,I: vfd plate
	int shift = (offset - PORTF) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

// inputs

static INPUT_PORTS_START( galaxy2 )
	PORT_START("IN.0") // port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_2WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_2WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // port B
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START )
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void galaxy2_state::galaxy2(machine_config &config)
{
	// basic machine hardware
	NEC_D553(config, m_maincpu, 400_kHz_XTAL);
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->read_b().set_ioport("IN.1");
	m_maincpu->write_c().set(FUNC(galaxy2_state::grid_w));
	m_maincpu->write_d().set(FUNC(galaxy2_state::grid_w));
	m_maincpu->write_e().set(FUNC(galaxy2_state::grid_w));
	m_maincpu->write_f().set(FUNC(galaxy2_state::plate_w));
	m_maincpu->write_g().set(FUNC(galaxy2_state::plate_w));
	m_maincpu->write_h().set(FUNC(galaxy2_state::plate_w));
	m_maincpu->write_i().set(FUNC(galaxy2_state::plate_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(304, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(10, 15);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

void galaxy2_state::galaxy2b(machine_config &config)
{
	galaxy2(config);

	// video hardware
	screen_device *screen = subdevice<screen_device>("screen");
	screen->set_size(306, 1080);
	screen->set_visarea_full();
}

// roms

ROM_START( galaxy2 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d553c_153.s01", 0x0000, 0x0800, CRC(70d552b3) SHA1(72d50647701cb4bf85ea947a149a317aaec0f52c) )

	ROM_REGION( 325057, "screen", 0)
	ROM_LOAD( "galaxy2d.svg", 0, 325057, CRC(c5e5a09d) SHA1(526d90762fc27e441d872e03d07d80dd727777a6) )
ROM_END

ROM_START( galaxy2b )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d553c_153.s01", 0x0000, 0x0800, CRC(70d552b3) SHA1(72d50647701cb4bf85ea947a149a317aaec0f52c) )

	ROM_REGION( 266375, "screen", 0)
	ROM_LOAD( "galaxy2b.svg", 0, 266375, CRC(6c758744) SHA1(f6f08f2988c3069c447727841e75cc0b11e9e4f8) )
ROM_END





/*******************************************************************************

  Epoch Astro Command (manufactured in Japan)
  * PCB labels: 96111/96112
  * NEC uCOM-43 MCU, label D553C 202, 1-bit sound
  * cyan/red VFD NEC FIP9AM20T no. 42-42
  * color overlay: right 3 columns: yellow, bottom row 6 columns: pink

  known releases:
  - Japan: Astro Command, published by Epoch
  - USA: Astro Command, published by Tandy
  - UK: Scramble, published by Grandstand

*******************************************************************************/

class astrocmd_state : public hh_ucom4_state
{
public:
	astrocmd_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void astrocmd(machine_config &config);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(offs_t offset, u8 data);
};

// handlers

void astrocmd_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void astrocmd_state::grid_w(offs_t offset, u8 data)
{
	// C,D(,E3): vfd grid
	int shift = (offset - PORTC) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);
	update_display();
}

void astrocmd_state::plate_w(offs_t offset, u8 data)
{
	// E01,F,G,H,I: vfd plate
	int shift = (offset - PORTE) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);

	if (offset == PORTE)
	{
		// E2: speaker out
		m_speaker->level_w(data >> 2 & 1);

		// E3: vfd grid
		grid_w(offset, data >> 3 & 1);
	}
	else
		update_display();
}

// inputs

static INPUT_PORTS_START( astrocmd )
	PORT_START("IN.0") // port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Missile")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Bomb")

	PORT_START("IN.1") // port B
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
INPUT_PORTS_END

// config

void astrocmd_state::astrocmd(machine_config &config)
{
	// basic machine hardware
	NEC_D553(config, m_maincpu, 400_kHz_XTAL);
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->read_b().set_ioport("IN.1");
	m_maincpu->write_c().set(FUNC(astrocmd_state::grid_w));
	m_maincpu->write_d().set(FUNC(astrocmd_state::grid_w));
	m_maincpu->write_e().set(FUNC(astrocmd_state::plate_w));
	m_maincpu->write_f().set(FUNC(astrocmd_state::plate_w));
	m_maincpu->write_g().set(FUNC(astrocmd_state::plate_w));
	m_maincpu->write_h().set(FUNC(astrocmd_state::plate_w));
	m_maincpu->write_i().set(FUNC(astrocmd_state::plate_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 525);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(9, 19);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( astrocmd )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d553c_202.s01", 0x0000, 0x0800, CRC(b4b34883) SHA1(6246d561c2df1f2124575d2ca671ef85b1819edd) )

	ROM_REGION( 335380, "screen", 0)
	ROM_LOAD( "astrocmd.svg", 0, 335380, CRC(950af2c4) SHA1(8a8c27a4442c94f3fff82206aa48fe76e2952064) )
ROM_END





/*******************************************************************************

  Epoch Dracula (manufactured in Japan)
  * PCB label: 96121
  * NEC uCOM-43 MCU, label D553C 206, 1-bit sound
  * cyan/red/green VFD NEC FIP8BM20T no. 2-42

  known releases:
  - Japan: Dracula House, yellow case, published by Epoch
  - USA: Dracula, red case, published by Epoch
  - Other: Dracula, yellow case, published by Hales

*******************************************************************************/

class edracula_state : public hh_ucom4_state
{
public:
	edracula_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void edracula(machine_config &config);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(offs_t offset, u8 data);
};

// handlers

void edracula_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void edracula_state::grid_w(offs_t offset, u8 data)
{
	// C,D: vfd grid
	int shift = (offset - PORTC) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);
	update_display();
}

void edracula_state::plate_w(offs_t offset, u8 data)
{
	// I2: speaker out
	if (offset == PORTI)
		m_speaker->level_w(data >> 2 & 1);

	// E,F,G,H,I01: vfd plate
	int shift = (offset - PORTE) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

// inputs

static INPUT_PORTS_START( edracula )
	PORT_START("IN.0") // port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.1") // port B
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
INPUT_PORTS_END

// config

void edracula_state::edracula(machine_config &config)
{
	// basic machine hardware
	NEC_D553(config, m_maincpu, 400_kHz_XTAL);
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->read_b().set_ioport("IN.1");
	m_maincpu->write_c().set(FUNC(edracula_state::grid_w));
	m_maincpu->write_d().set(FUNC(edracula_state::grid_w));
	m_maincpu->write_e().set(FUNC(edracula_state::plate_w));
	m_maincpu->write_f().set(FUNC(edracula_state::plate_w));
	m_maincpu->write_g().set(FUNC(edracula_state::plate_w));
	m_maincpu->write_h().set(FUNC(edracula_state::plate_w));
	m_maincpu->write_i().set(FUNC(edracula_state::plate_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 526);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(8, 18);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( edracula )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d553c_206.s01", 0x0000, 0x0800, CRC(b524857b) SHA1(c1c89ed5dd4bb1e6e98462dc8fa5af2aa48d8ede) )

	ROM_REGION( 793604, "screen", 0)
	ROM_LOAD( "edracula.svg", 0, 793604, CRC(062bd9b0) SHA1(8c2194824cd537073c9757207f671c35bc8614f3) )
ROM_END





/*******************************************************************************

  Mattel Computer Gin
  * NEC uCOM-43 MCU, label D650C 060 (die label same), no sound
  * Hughes HLCD0530 LCD driver, 5 by 14 segments LCD panel

*******************************************************************************/

class mcompgin_state : public hh_ucom4_state
{
public:
	mcompgin_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag),
		m_lcd(*this, "lcd")
	{ }

	void mcompgin(machine_config &config);

private:
	required_device<hlcd0530_device> m_lcd;

	void lcd_output_w(offs_t offset, u32 data);
	void lcd_w(u8 data);
};

// handlers

void mcompgin_state::lcd_output_w(offs_t offset, u32 data)
{
	// uses ROW0-4, COL11-24
	m_display->matrix(1 << offset, data);
}

void mcompgin_state::lcd_w(u8 data)
{
	// E0: HLCD0530 _CS
	// E1: HLCD0530 clock
	// E2: HLCD0530 data in
	m_lcd->cs_w(data & 1);
	m_lcd->data_w(data >> 2 & 1);
	m_lcd->clock_w(data >> 1 & 1);
}

// inputs

static INPUT_PORTS_START( mcompgin )
	PORT_START("IN.0") // port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Select")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("Deal / Gin")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_I) PORT_NAME("Discard")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("Draw")

	PORT_START("IN.1") // port B
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("Compare")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_O) PORT_NAME("Score")
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void mcompgin_state::mcompgin(machine_config &config)
{
	// basic machine hardware
	NEC_D650(config, m_maincpu, 400_kHz_XTAL); // TDK FCR400K
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->read_b().set_ioport("IN.1");
	m_maincpu->write_e().set(FUNC(mcompgin_state::lcd_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 743);
	screen.set_visarea_full();

	HLCD0530(config, m_lcd, 500); // C=0.01uF
	m_lcd->write_cols().set(FUNC(mcompgin_state::lcd_output_w));

	PWM_DISPLAY(config, m_display).set_size(8, 24);
	config.set_default_layout(layout_mcompgin);

	// no sound!
}

// roms

ROM_START( mcompgin )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d650c_060", 0x0000, 0x0800, CRC(985e6da6) SHA1(ea4102a10a5741f06297c5426156e4b2f0d85a68) )

	ROM_REGION( 331033, "screen", 0)
	ROM_LOAD( "mcompgin.svg", 0, 331033, CRC(b432b13c) SHA1(6117e30897fcc92de09c69a8d7ede7068e26e43f) )
ROM_END





/*******************************************************************************

  Mego Mini-Vid: Break Free (manufactured in Japan)
  * PCB label: Mego 79 rev F
  * NEC uCOM-43 MCU, label D553C 049, 1-bit sound
  * cyan VFD Futaba DM-4.5 91

*******************************************************************************/

class mvbfree_state : public hh_ucom4_state
{
public:
	mvbfree_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void mvbfree(machine_config &config);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(offs_t offset, u8 data);
	void speaker_w(u8 data);
};

// handlers

void mvbfree_state::update_display()
{
	m_display->matrix(m_grid >> 2, m_plate);
}

void mvbfree_state::grid_w(offs_t offset, u8 data)
{
	// E23,F,G,H: vfd grid
	int shift = (offset - PORTE) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);

	// E01: vfd plate
	if (offset == PORTE)
		plate_w(2 + PORTC, data & 3);
	else
		update_display();
}

void mvbfree_state::plate_w(offs_t offset, u8 data)
{
	// C,D(,E01): vfd plate
	int shift = (offset - PORTC) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void mvbfree_state::speaker_w(u8 data)
{
	// I0: speaker out
	m_speaker->level_w(data & 1);
}

// inputs

static INPUT_PORTS_START( mvbfree )
	PORT_START("IN.0") // port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY

	PORT_START("IN.1") // port B
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED ) // unimplemented p1/p2 buttons
	PORT_CONFNAME( 0x0c, 0x04, "Game Select")
	PORT_CONFSETTING(    0x04, "1" )
	PORT_CONFSETTING(    0x00, "2" )
	PORT_CONFSETTING(    0x08, "3" )
INPUT_PORTS_END

// config

void mvbfree_state::mvbfree(machine_config &config)
{
	// basic machine hardware
	NEC_D553(config, m_maincpu, 400000); // approximation
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->read_b().set_ioport("IN.1");
	m_maincpu->write_c().set(FUNC(mvbfree_state::plate_w));
	m_maincpu->write_d().set(FUNC(mvbfree_state::plate_w));
	m_maincpu->write_e().set(FUNC(mvbfree_state::grid_w));
	m_maincpu->write_f().set(FUNC(mvbfree_state::grid_w));
	m_maincpu->write_g().set(FUNC(mvbfree_state::grid_w));
	m_maincpu->write_h().set(FUNC(mvbfree_state::grid_w));
	m_maincpu->write_i().set(FUNC(mvbfree_state::speaker_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(16, 10);
	config.set_default_layout(layout_mvbfree);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( mvbfree )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d553c_049", 0x0000, 0x0800, CRC(d64a8399) SHA1(97887e486fa29b1fc4a5a40cacf3c960f67aacbf) )
ROM_END





/*******************************************************************************

  Takatoku Toys(T.T) Game Robot 9 「ゲームロボット九」
  * PCB label: GAME ROBOT 7520
  * NEC uCOM-43 MCU, label TTGR-512 (die label NEC D557 511), 1-bit sound
  * 9 lamps behind buttons

  known releases:
  - Japan: Game Robot 9, published by Takatoku Toys
  - USA: Fabulous Fred - The Ultimate Electronic Game, published by Mego
  - Mexico: Fabuloso Fred, published by Ensueño Toys (also released as
    12-button version, a clone of Tandy-12)

  Accessories were included for some of the minigames.

*******************************************************************************/

class grobot9_state : public hh_ucom4_state
{
public:
	grobot9_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void grobot9(machine_config &config);

private:
	void lamps_w(offs_t offset, u8 data);
	void speaker_w(u8 data);
	void input_w(u8 data);
	u8 input_r();
};

// handlers

void grobot9_state::lamps_w(offs_t offset, u8 data)
{
	if (offset == PORTE)
	{
		// E1: speaker out
		m_speaker->level_w(data >> 1 & 1);

		// E3: input mux high bit
		m_inp_mux = (m_inp_mux & 7) | (data & 8);
	}

	// D,F,E0: lamps
	m_port[offset] = data;
	m_display->matrix(1, m_port[PORTD] | m_port[PORTF] << 4 | m_port[PORTE] << 8);
}

void grobot9_state::input_w(u8 data)
{
	// C012: input mux low
	m_inp_mux = (m_inp_mux & 8) | (data & 7);
}

u8 grobot9_state::input_r()
{
	// A: multiplexed inputs
	return read_inputs(5);
}

// inputs

static INPUT_PORTS_START( grobot9 )
	PORT_START("IN.0") // C0 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_Q) PORT_NAME("Button 1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_W) PORT_NAME("Button 2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_E) PORT_NAME("Button 3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_D) PORT_NAME("Button 4")

	PORT_START("IN.1") // C1 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_C) PORT_NAME("Button 5")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_X) PORT_NAME("Button 6")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_Z) PORT_NAME("Button 7")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_A) PORT_NAME("Button 8")

	PORT_START("IN.2") // C2 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_S) PORT_NAME("Button 9")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Rest")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Y) PORT_NAME("Eighth Note")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // E3 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_NAME("Select")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_N) PORT_NAME("Hit")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("Repeat")

	PORT_START("IN.4") // INT
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_V) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hh_ucom4_state::single_interrupt_line), 0) PORT_NAME("Start-Pitch")
INPUT_PORTS_END

// config

void grobot9_state::grobot9(machine_config &config)
{
	// basic machine hardware
	NEC_D557L(config, m_maincpu, 160000); // approximation
	m_maincpu->read_a().set(FUNC(grobot9_state::input_r));
	m_maincpu->write_c().set(FUNC(grobot9_state::input_w));
	m_maincpu->write_d().set(FUNC(grobot9_state::lamps_w));
	m_maincpu->write_e().set(FUNC(grobot9_state::lamps_w));
	m_maincpu->write_f().set(FUNC(grobot9_state::lamps_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 9);
	config.set_default_layout(layout_grobot9);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( grobot9 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "ttgr-511", 0x0000, 0x0800, CRC(1f25b2bb) SHA1(55ae7e23f6dd46cc6e1a65839327726678410c3a) )
ROM_END





/*******************************************************************************

  Tomy Cosmic Combat (manufactured in Japan)
  * PCB label: 2E1019-E01
  * NEC uCOM-44 MCU, label D552C 042, 1-bit sound
  * cyan VFD NEC FIP32AM18Y tube no. 0E, with blue window
  * color overlay: score/ufo/player: yellow (optional)

  There's also a version with a different looking cyan/red VFD.

  known releases:
  - USA: Cosmic Combat, published by Tomy
  - USA: UFO Attack, published by Tomy
  - Japan: Space Attack, published by Tomy

*******************************************************************************/

class tccombat_state : public hh_ucom4_state
{
public:
	tccombat_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void tccombat(machine_config &config);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(offs_t offset, u8 data);
};

// handlers

void tccombat_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void tccombat_state::grid_w(offs_t offset, u8 data)
{
	// I1: speaker out
	if (offset == PORTI)
		m_speaker->level_w(data >> 1 & 1);

	// C,D,I0: vfd grid
	int shift = (offset == PORTI) ? 8 : (offset - PORTC) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);
	update_display();
}

void tccombat_state::plate_w(offs_t offset, u8 data)
{
	// E,F123,G,H: vfd plate
	int shift = (offset - PORTE) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

// inputs

static INPUT_PORTS_START( tccombat )
	PORT_START("IN.0") // port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_CONFNAME( 0x02, 0x02, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x02, "1" )
	PORT_CONFSETTING(    0x00, "2" )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_2WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_2WAY
INPUT_PORTS_END

// config

void tccombat_state::tccombat(machine_config &config)
{
	// basic machine hardware
	NEC_D552(config, m_maincpu, 400000); // approximation
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->write_c().set(FUNC(tccombat_state::grid_w));
	m_maincpu->write_d().set(FUNC(tccombat_state::grid_w));
	m_maincpu->write_e().set(FUNC(tccombat_state::plate_w));
	m_maincpu->write_f().set(FUNC(tccombat_state::plate_w));
	m_maincpu->write_g().set(FUNC(tccombat_state::plate_w));
	m_maincpu->write_h().set(FUNC(tccombat_state::plate_w));
	m_maincpu->write_i().set(FUNC(tccombat_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(300, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(9, 16);
	config.set_default_layout(layout_tccombat);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( tccombat )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "d552c_042", 0x0000, 0x0400, CRC(d7b5cfeb) SHA1(a267be8e43b7740758eb0881b655b1cc8aec43da) )

	ROM_REGION( 210933, "screen", 0)
	ROM_LOAD( "tccombat.svg", 0, 210933, CRC(73b4e4da) SHA1(f479b9667d0169e383a8513cff6be948fe87cc13) )
ROM_END





/*******************************************************************************

  Tomy Tennis (manufactured in Japan)
  * PCB label: TOMY TN-04 TENNIS
  * NEC uCOM-44 MCU, label D552C 048, 1-bit sound
  * cyan VFD NEC FIP11AM15T tube no. 0F, with overlay

  The initial release of this game was in 1979, known as Pro-Tennis,
  it has a D553 instead of D552, with just a little over 50% ROM used.

  Press the Serve button to start, then hit the ball by pressing one of the
  positional buttons when the ball flies over it.

*******************************************************************************/

class tmtennis_state : public hh_ucom4_state
{
public:
	tmtennis_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void tmtennis(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(skill_switch);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(offs_t offset, u8 data);
	void port_e_w(u8 data);
	u8 input_r(offs_t offset);
};

// handlers

INPUT_CHANGED_MEMBER(tmtennis_state::skill_switch)
{
	// MCU clock is from an LC circuit oscillating by default at ~360kHz,
	// but on PRO1, the difficulty switch puts a capacitor across the LC circuit
	// to slow it down to ~260kHz.
	m_maincpu->set_unscaled_clock((newval & 0x100) ? 260000 : 360000);
}

void tmtennis_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void tmtennis_state::grid_w(offs_t offset, u8 data)
{
	// G,H,I: vfd grid
	int shift = (offset - PORTG) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);
	update_display();
}

void tmtennis_state::plate_w(offs_t offset, u8 data)
{
	// C,D,F: vfd plate
	int shift = (offset == PORTF) ? 8 : (offset - PORTC) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void tmtennis_state::port_e_w(u8 data)
{
	// E01: input mux
	// E2: speaker out
	// E3: N/C
	m_inp_mux = data & 3;
	m_speaker->level_w(data >> 2 & 1);
}

u8 tmtennis_state::input_r(offs_t offset)
{
	// A,B: multiplexed buttons
	return ~read_inputs(2) >> (offset*4);
}

// inputs

/* Pro-Tennis physical button layout and labels are like this:

    * left = P2/CPU side *    * right = P1 side *

    [SERVE] [1] [2] [3]       [3] [2] [1] [SERVE]
            [4] [5] [6]       [6] [5] [4]

    PRACTICE<--PRO1-->PRO2    1PLAYER<--OFF-->2PLAYER
*/

static INPUT_PORTS_START( tmtennis )
	PORT_START("IN.0") // E0 port A/B
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("P1 Serve")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_NAME("P2 Serve")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_O) PORT_NAME("P1 Button 1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_L) PORT_NAME("P1 Button 4")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_I) PORT_NAME("P1 Button 2")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_K) PORT_NAME("P1 Button 5")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_U) PORT_NAME("P1 Button 3")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_J) PORT_NAME("P1 Button 6")

	PORT_START("IN.1") // E1 port A/B
	PORT_CONFNAME( 0x101, 0x100, DEF_STR( Difficulty ) ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tmtennis_state::skill_switch), 0)
	PORT_CONFSETTING(     0x001, "Practice" )
	PORT_CONFSETTING(     0x100, "Pro 1" ) // -> skill_switch
	PORT_CONFSETTING(     0x000, "Pro 2" )
	PORT_CONFNAME( 0x02, 0x00, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("P2 Button 1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("P2 Button 4")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("P2 Button 2")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("P2 Button 5")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("P2 Button 3")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("P2 Button 6")
INPUT_PORTS_END

// config

void tmtennis_state::tmtennis(machine_config &config)
{
	// basic machine hardware
	NEC_D552(config, m_maincpu, 260000); // see skill_switch
	m_maincpu->read_a().set(FUNC(tmtennis_state::input_r));
	m_maincpu->read_b().set(FUNC(tmtennis_state::input_r));
	m_maincpu->write_c().set(FUNC(tmtennis_state::plate_w));
	m_maincpu->write_d().set(FUNC(tmtennis_state::plate_w));
	m_maincpu->write_e().set(FUNC(tmtennis_state::port_e_w));
	m_maincpu->write_f().set(FUNC(tmtennis_state::plate_w));
	m_maincpu->write_g().set(FUNC(tmtennis_state::grid_w));
	m_maincpu->write_h().set(FUNC(tmtennis_state::grid_w));
	m_maincpu->write_i().set(FUNC(tmtennis_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 417);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(12, 12);
	config.set_default_layout(layout_tmtennis);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( tmtennis )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "d552c_048", 0x0000, 0x0400, CRC(78702003) SHA1(4d427d4dbeed901770c682338867f58c7b54eee3) )

	ROM_REGION( 204490, "screen", 0)
	ROM_LOAD( "tmtennis.svg", 0, 204490, CRC(b000e7cb) SHA1(d59962245107da28047d6e1dfab00fe9b398c3d0) )
ROM_END





/*******************************************************************************

  Tomy Pac-Man (manufactured in Japan)
  * PCB label: TN-08 2E108E01
  * NEC uCOM-43 MCU, label D553C 160, 1-bit sound
  * cyan/red/green VFD NEC FIP8AM18T no. 2-21
  * bright yellow round casing

  known releases:
  - Japan: Puck Man, published by Tomy
  - USA: Pac Man, published by Tomy
  - UK: Puckman (Tomy), and also published by Grandstand as Munchman
  - Australia: Pac Man-1, published by Futuretronics

  The game will start automatically after turning it on. This Pac Man refuses
  to eat dots with his butt, you can only eat them going right-to-left.

*******************************************************************************/

class tmpacman_state : public hh_ucom4_state
{
public:
	tmpacman_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void tmpacman(machine_config &config);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(offs_t offset, u8 data);
};

// handlers

void tmpacman_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void tmpacman_state::grid_w(offs_t offset, u8 data)
{
	// C,D: vfd grid
	int shift = (offset - PORTC) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);
	update_display();
}

void tmpacman_state::plate_w(offs_t offset, u8 data)
{
	// E1: speaker out
	if (offset == PORTE)
		m_speaker->level_w(data >> 1 & 1);

	// E023,F,G,H,I: vfd plate
	int shift = (offset - PORTE) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

// inputs

static INPUT_PORTS_START( tmpacman )
	PORT_START("IN.0") // port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY

	PORT_START("IN.1") // port B
	PORT_CONFNAME( 0x01, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" ) // AMA
	PORT_CONFSETTING(    0x01, "2" ) // PRO
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void tmpacman_state::tmpacman(machine_config &config)
{
	// basic machine hardware
	NEC_D553(config, m_maincpu, 430_kHz_XTAL);
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->read_b().set_ioport("IN.1");
	m_maincpu->write_c().set(FUNC(tmpacman_state::grid_w));
	m_maincpu->write_d().set(FUNC(tmpacman_state::grid_w));
	m_maincpu->write_e().set(FUNC(tmpacman_state::plate_w));
	m_maincpu->write_f().set(FUNC(tmpacman_state::plate_w));
	m_maincpu->write_g().set(FUNC(tmpacman_state::plate_w));
	m_maincpu->write_h().set(FUNC(tmpacman_state::plate_w));
	m_maincpu->write_i().set(FUNC(tmpacman_state::plate_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 508);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(8, 19);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( tmpacman )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d553c_160", 0x0000, 0x0800, CRC(b21a8af7) SHA1(e3122be1873ce76a4067386bf250802776f0c2f9) )

	ROM_REGION( 230222, "screen", 0)
	ROM_LOAD( "tmpacman.svg", 0, 230222, CRC(824160e5) SHA1(c3c88cb4a01a70b450fbef7e51eaeb2ffed7dc66) )
ROM_END





/*******************************************************************************

  Tomy Scramble (manufactured in Japan)
  * PCB label: TN-10 2E114E01
  * NEC uCOM-43 MCU, label D553C 192, 1-bit sound
  * cyan/red/green VFD NEC FIP10CM20T no. 2-41

  known releases:
  - World: Scramble, published by Tomy
  - USA: Scramble, published by Tandy
  - UK: Astro Blaster, published by Hales (Epoch Astro Command was named Scramble)
  - Germany: Rambler, published by Tomy

*******************************************************************************/

class tmscramb_state : public hh_ucom4_state
{
public:
	tmscramb_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void tmscramb(machine_config &config);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(offs_t offset, u8 data);
};

// handlers

void tmscramb_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void tmscramb_state::grid_w(offs_t offset, u8 data)
{
	// I2: speaker out
	if (offset == PORTI)
		m_speaker->level_w(data >> 2 & 1);

	// C,D,I01: vfd grid
	int shift = (offset == PORTI) ? 8 : (offset - PORTC) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);
	update_display();
}

void tmscramb_state::plate_w(offs_t offset, u8 data)
{
	// E,F,G,H: vfd plate
	int shift = (offset - PORTE) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

// inputs

static INPUT_PORTS_START( tmscramb )
	PORT_START("IN.0") // port A
	PORT_CONFNAME( 0x01, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" ) // AMA
	PORT_CONFSETTING(    0x01, "2" ) // PRO
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_BUTTON1 )

	PORT_START("IN.1") // port B
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_2WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_2WAY
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void tmscramb_state::tmscramb(machine_config &config)
{
	// basic machine hardware
	NEC_D553(config, m_maincpu, 400_kHz_XTAL);
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->read_b().set_ioport("IN.1");
	m_maincpu->write_c().set(FUNC(tmscramb_state::grid_w));
	m_maincpu->write_d().set(FUNC(tmscramb_state::grid_w));
	m_maincpu->write_e().set(FUNC(tmscramb_state::plate_w));
	m_maincpu->write_f().set(FUNC(tmscramb_state::plate_w));
	m_maincpu->write_g().set(FUNC(tmscramb_state::plate_w));
	m_maincpu->write_h().set(FUNC(tmscramb_state::plate_w));
	m_maincpu->write_i().set(FUNC(tmscramb_state::grid_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 556);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(10, 16);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( tmscramb )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d553c_192", 0x0000, 0x0800, CRC(00fcc501) SHA1(a7771e934bf8268c83f38c7ec0acc668836e0939) )

	ROM_REGION( 243853, "screen", 0)
	ROM_LOAD( "tmscramb.svg", 0, 243853, CRC(0c506407) SHA1(045a661dd5f8af1833fd17210fba398ec2805b41) )
ROM_END





/*******************************************************************************

  Tomy Caveman (manufactured in Japan)
  * PCB label: TN-12 2E114E03
  * NEC uCOM-43 MCU, label D553C 209, 1-bit sound
  * cyan/red/green VFD NEC FIP8AM20T no. 2-42

  known releases:
  - World: Caveman, published by Tomy
  - USA: Caveman, published by Tandy
  - UK: Cave Man - Jr. Caveman vs Dinosaur, published by Grandstand

*******************************************************************************/

class tcaveman_state : public hh_ucom4_state
{
public:
	tcaveman_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void tcaveman(machine_config &config);

private:
	void update_display();
	void grid_w(offs_t offset, u8 data);
	void plate_w(offs_t offset, u8 data);
};

// handlers

void tcaveman_state::update_display()
{
	m_display->matrix(m_grid, m_plate);
}

void tcaveman_state::grid_w(offs_t offset, u8 data)
{
	// C,D: vfd grid
	int shift = (offset - PORTC) * 4;
	m_grid = (m_grid & ~(0xf << shift)) | (data << shift);
	update_display();
}

void tcaveman_state::plate_w(offs_t offset, u8 data)
{
	// E3: speaker out
	if (offset == PORTE)
		m_speaker->level_w(data >> 3 & 1);

	// E012,F,G,H,I: vfd plate
	int shift = (offset - PORTE) * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

// inputs

static INPUT_PORTS_START( tcaveman )
	PORT_START("IN.0") // port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_CONFNAME( 0x08, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" ) // AMA
	PORT_CONFSETTING(    0x08, "2" ) // PRO
INPUT_PORTS_END

// config

void tcaveman_state::tcaveman(machine_config &config)
{
	// basic machine hardware
	NEC_D553(config, m_maincpu, 400_kHz_XTAL);
	m_maincpu->read_a().set_ioport("IN.0");
	m_maincpu->write_c().set(FUNC(tcaveman_state::grid_w));
	m_maincpu->write_d().set(FUNC(tcaveman_state::grid_w));
	m_maincpu->write_e().set(FUNC(tcaveman_state::plate_w));
	m_maincpu->write_f().set(FUNC(tcaveman_state::plate_w));
	m_maincpu->write_g().set(FUNC(tcaveman_state::plate_w));
	m_maincpu->write_h().set(FUNC(tcaveman_state::plate_w));
	m_maincpu->write_i().set(FUNC(tcaveman_state::plate_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920, 559);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(8, 19);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( tcaveman )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d553c_209", 0x0000, 0x0800, CRC(d230d4b7) SHA1(2fb12b60410f5567c5e3afab7b8f5aa855d283be) )

	ROM_REGION( 306924, "screen", 0)
	ROM_LOAD( "tcaveman.svg", 0, 306924, CRC(4e214216) SHA1(a42506d8f82ccad7598f91603e3b264ce700ca1e) )
ROM_END





/*******************************************************************************

  Tomy Alien Chase (manufactured in Japan)
  * PCB label: TN-16 2E121B01
  * NEC uCOM-43 MCU, label D553C 258, 1-bit sound
  * red/green VFD NEC FIP9AM24T (2-sided)
  * color overlay: top row: green, bottom: blue, middle: pink, other: yellow

  Player one views the VFD from the front (grid+filament side), while the
  opposite player views it from the back side (through the conductive traces),
  basically a mirror-image.

  To start the game, simply press [UP]. Hold a joystick direction to move around.

*******************************************************************************/

class alnchase_state : public hh_ucom4_state
{
public:
	alnchase_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_ucom4_state(mconfig, type, tag)
	{ }

	void alnchase(machine_config &config);

private:
	void output_w(offs_t offset, u8 data);
	u8 input_r();
};

// handlers

void alnchase_state::output_w(offs_t offset, u8 data)
{
	if (offset <= PORTE)
	{
		// C,D,E0: vfd grid
		int shift = (offset - PORTC) * 4;
		m_grid = (m_grid & ~(0xf << shift)) | (data << shift);

		// C0(grid 0): input enable PL1
		// D0(grid 4): input enable PL2
		m_inp_mux = (m_grid & 1) | (m_grid >> 3 & 2);

		// E1: speaker out
		if (offset == PORTE)
			m_speaker->level_w(data >> 1 & 1);
	}

	if (offset >= PORTE)
	{
		// E23,F,G,H,I: vfd plate
		int shift = (offset - PORTE) * 4;
		m_plate = ((m_plate << 2 & ~(0xf << shift)) | (data << shift)) >> 2;
	}

	m_display->matrix(m_grid, m_plate);
}

u8 alnchase_state::input_r()
{
	// A: multiplexed buttons
	return read_inputs(2);
}

// inputs

/* physical button layout and labels are like this:

    POWER SOUND LEVEL PLAYER
     ON    ON    PRO   TWO        START
      o     o     |     |
      |     |     |     |       [joystick]
      |     |     o     o
     OFF   OFF   AMA   ONE     GAME 0,1,2,3

    1 PLAYER SIDE

    other player side only has a joystick
*/

static INPUT_PORTS_START( alnchase )
	PORT_START("IN.0") // C0 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )

	PORT_START("IN.1") // D0 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL // on non-mirrored view, swap P2 left/right
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL // "
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL

	PORT_START("IN.2") // port B
	PORT_CONFNAME( 0x01, 0x01, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x00, "2" )
	PORT_CONFNAME( 0x02, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" ) // AMA
	PORT_CONFSETTING(    0x02, "2" ) // PRO
	PORT_BIT( 0x0c, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

// config

void alnchase_state::alnchase(machine_config &config)
{
	// basic machine hardware
	NEC_D553(config, m_maincpu, 400_kHz_XTAL);
	m_maincpu->read_a().set(FUNC(alnchase_state::input_r));
	m_maincpu->read_b().set_ioport("IN.2");
	m_maincpu->write_c().set(FUNC(alnchase_state::output_w));
	m_maincpu->write_d().set(FUNC(alnchase_state::output_w));
	m_maincpu->write_e().set(FUNC(alnchase_state::output_w));
	m_maincpu->write_f().set(FUNC(alnchase_state::output_w));
	m_maincpu->write_g().set(FUNC(alnchase_state::output_w));
	m_maincpu->write_h().set(FUNC(alnchase_state::output_w));
	m_maincpu->write_i().set(FUNC(alnchase_state::output_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(362, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(9, 17);
	config.set_default_layout(layout_alnchase);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( alnchase )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d553c_258", 0x0000, 0x0800, CRC(c5284ff5) SHA1(6a20aaacc9748f0e0335958f3cea482e36153704) )

	ROM_REGION( 576555, "screen", 0)
	ROM_LOAD( "alnchase.svg", 0, 576555, CRC(b2c1734b) SHA1(087e2fd66e978f9b3b10401dd0935b4b28a7823f) )
ROM_END



} // anonymous namespace

/*******************************************************************************

  Game driver(s)

*******************************************************************************/

//    YEAR  NAME      PARENT    COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1979, ufombs,   0,        0,      ufombs,   ufombs,   ufombs_state,   empty_init, "Bambino", "UFO Master-Blaster Station", MACHINE_SUPPORTS_SAVE )
SYST( 1979, ssfball,  0,        0,      ssfball,  ssfball,  ssfball_state,  empty_init, "Bambino", "Super Star Football (Bambino)", MACHINE_SUPPORTS_SAVE )
SYST( 1982, bmcfball, ssfball,  0,      ssfball,  ssfball,  ssfball_state,  empty_init, "Bambino", "Football Classic (Bambino)", MACHINE_SUPPORTS_SAVE )
SYST( 1979, bmsoccer, 0,        0,      bmsoccer, bmsoccer, bmsoccer_state, empty_init, "Bambino", "Kick The Goal Soccer", MACHINE_SUPPORTS_SAVE )
SYST( 1981, bmsafari, 0,        0,      bmsafari, bmsafari, bmsafari_state, empty_init, "Bambino", "Safari (Bambino)", MACHINE_SUPPORTS_SAVE )
SYST( 1980, splasfgt, 0,        0,      splasfgt, splasfgt, splasfgt_state, empty_init, "Bambino", "Space Laser Fight", MACHINE_SUPPORTS_SAVE )

SYST( 1980, bgunf,    0,        0,      bgunf,    bgunf,    bgunf_state,    empty_init, "Bandai", "Gunfighter", MACHINE_SUPPORTS_SAVE )
SYST( 1981, bgalaxn,  0,        0,      bgalaxn,  bgalaxn,  bgalaxn_state,  empty_init, "Bandai", "Galaxian (Bandai)", MACHINE_SUPPORTS_SAVE )
SYST( 1982, bcclimbr, 0,        0,      bcclimbr, bcclimbr, bcclimbr_state, empty_init, "Bandai", "Crazy Climber (Bandai)", MACHINE_SUPPORTS_SAVE )

SYST( 1980, tactix,   0,        0,      tactix,   tactix,   tactix_state,   empty_init, "Castle Toy", "Tactix (Castle Toy)", MACHINE_SUPPORTS_SAVE )
SYST( 1980, ctntune,  0,        0,      ctntune,  ctntune,  ctntune_state,  empty_init, "Castle Toy", "Name That Tune (Castle Toy)", MACHINE_SUPPORTS_SAVE ) // ***

SYST( 1980, einspace, 0,        0,      einspace, einspace, einspace_state, empty_init, "Epoch", "Invader From Space", MACHINE_SUPPORTS_SAVE )
SYST( 1980, efball,   0,        0,      efball,   efball,   efball_state,   empty_init, "Epoch", "Electronic Football (Epoch)", MACHINE_SUPPORTS_SAVE )
SYST( 1981, galaxy2,  0,        0,      galaxy2,  galaxy2,  galaxy2_state,  empty_init, "Epoch", "Galaxy II (VFD rev. D)", MACHINE_SUPPORTS_SAVE )
SYST( 1981, galaxy2b, galaxy2,  0,      galaxy2b, galaxy2,  galaxy2_state,  empty_init, "Epoch", "Galaxy II (VFD rev. B)", MACHINE_SUPPORTS_SAVE )
SYST( 1982, astrocmd, 0,        0,      astrocmd, astrocmd, astrocmd_state, empty_init, "Epoch", "Astro Command", MACHINE_SUPPORTS_SAVE )
SYST( 1982, edracula, 0,        0,      edracula, edracula, edracula_state, empty_init, "Epoch", "Dracula (Epoch)", MACHINE_SUPPORTS_SAVE )

SYST( 1979, mcompgin, 0,        0,      mcompgin, mcompgin, mcompgin_state, empty_init, "Mattel Electronics", "Computer Gin", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )

SYST( 1979, mvbfree,  0,        0,      mvbfree,  mvbfree,  mvbfree_state,  empty_init, "Mego", "Mini-Vid: Break Free", MACHINE_SUPPORTS_SAVE )

SYST( 1980, grobot9,  0,        0,      grobot9,  grobot9,  grobot9_state,  empty_init, "Takatoku Toys", "Game Robot 9", MACHINE_SUPPORTS_SAVE ) // some of the games: ***

SYST( 1980, tccombat, 0,        0,      tccombat, tccombat, tccombat_state, empty_init, "Tomy", "Cosmic Combat", MACHINE_SUPPORTS_SAVE )
SYST( 1980, tmtennis, 0,        0,      tmtennis, tmtennis, tmtennis_state, empty_init, "Tomy", "Tennis (Tomy)", MACHINE_SUPPORTS_SAVE )
SYST( 1982, tmpacman, 0,        0,      tmpacman, tmpacman, tmpacman_state, empty_init, "Tomy", "Pac Man (Tomy)", MACHINE_SUPPORTS_SAVE )
SYST( 1982, tmscramb, 0,        0,      tmscramb, tmscramb, tmscramb_state, empty_init, "Tomy", "Scramble (Tomy)", MACHINE_SUPPORTS_SAVE )
SYST( 1982, tcaveman, 0,        0,      tcaveman, tcaveman, tcaveman_state, empty_init, "Tomy", "Caveman (Tomy)", MACHINE_SUPPORTS_SAVE )
SYST( 1984, alnchase, 0,        0,      alnchase, alnchase, alnchase_state, empty_init, "Tomy", "Alien Chase", MACHINE_SUPPORTS_SAVE )

// ***: As far as MAME is concerned, the game is emulated fine. But for it to be playable, it requires interaction
// with other, unemulatable, things eg. game board/pieces, book, playing cards, pen & paper, etc.



hhtiger.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/**********************************************************************

    http://www.computinghistory.org.uk/det/52264/HH-Tiger/

   SPECIFICATION
      Processors: Z80A (4MHz) plus 6809 I/O processor (2MHz) plus 7220
                  graphics processor.
             RAM: 64K with Z80, 2K with 6809, 96K with 7220, plus 0.5K CMOS
                  RAM with battery back-up. 62K available to CP/M.
             ROM: 2K with Z80, 16K with 6809.
     Text screen: 80 x 24 or teletext-type 40 x 24, 8 colours.
  Graphic screen: 512 x 512 pixels, 8 colours.
        Keyboard: 88 keys in unit with CPU, numeric/cursor pad. 10 function
                  keys.
         Storage: 2 x 1 MB (unformatted) floppies
      Interfaces: RS232, Centronics parallel, IEEE488, networking, light pen,
                  cassette, disk expansion (5.25" or 8"), optional UHF or
                  additional video monitor
Operating system: CP/M

    TODO:
    - nothing is known so emulation is very incomplete.

**********************************************************************/


#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/z80dma.h"
#include "machine/z80pio.h"
#include "machine/z80daisy.h"
#include "machine/ram.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "cpu/m6809/m6809.h"
#include "machine/input_merger.h"
#include "machine/nvram.h"
#include "machine/6522via.h"
#include "machine/mc6854.h"
#include "machine/mos6551.h"
#include "machine/tms9914.h"
#include "sound/spkrdev.h"
#include "video/upd7220.h"
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "bus/ieee488/ieee488.h"
#include "bus/rs232/rs232.h"
#include "bus/centronics/ctronics.h"

// Debugging
#define VERBOSE 1
#include "logmacro.h"


namespace {

class hhtiger_state : public driver_device
{
public:
	hhtiger_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "z80")
		, m_rom_z80(*this, "rom_z80")
		, m_ram(*this, RAM_TAG)
		, m_dma(*this, "dma")
		, m_pio(*this, "pio")
		, m_subcpu(*this, "m6809")
		, m_rom_m6809(*this, "rom_m6809")
		, m_screen(*this, "screen")
		, m_gdc(*this, "upd7220")
		, m_palette(*this, "palette")
		, m_video_ram(*this, "video_ram")
		, m_nvram(*this, "nvram")
		, m_via(*this, "via%u", 0)
		, m_adlc(*this, "adlc")
		, m_acia(*this, "acia")
		, m_ieee(*this, IEEE488_TAG)
		, m_tms9914(*this, "hpib")
		, m_speaker(*this, "speaker")
		, m_cassette(*this, "cassette")
		, m_irqs(*this, "irqs")
		, m_rom_mirror(true)
	{ }

	void hhtiger(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

	uint8_t disable_rom_r();
	uint8_t read(offs_t offset);
	void write(offs_t offset, uint8_t data);
	uint8_t memory_read_byte(offs_t offset);
	void memory_write_byte(offs_t offset, uint8_t data);
	uint8_t io_read_byte(offs_t offset);
	void io_write_byte(offs_t offset, uint8_t data);

	UPD7220_DISPLAY_PIXELS_MEMBER(display_pixels);
	UPD7220_DRAW_TEXT_LINE_MEMBER(draw_text);

	/* handlers for logging only, can be removed when more is known */
	uint8_t pio_pa_r();
	void pio_pa_w(uint8_t data);
	void pio_pb_w(uint8_t data);
	void ardy_w(int state);
	void brdy_w(int state);

	uint8_t via_0_in_a();
	void via_0_out_a(uint8_t data);
	void via_0_out_b(uint8_t data);
	void via_0_out_ca2(int state);
	void via_0_out_cb2(int state);

	void via_1_out_a(uint8_t data);
	void via_1_out_b(uint8_t data);
	void via_1_out_ca2(int state);
	void via_1_out_cb2(int state);

	void z80_mem(address_map &map) ATTR_COLD;
	void z80_io(address_map &map) ATTR_COLD;
	void m6809_mem(address_map &map) ATTR_COLD;
	void upd7220_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_memory_region m_rom_z80;
	required_device<ram_device> m_ram;
	required_device<z80dma_device> m_dma;
	required_device<z80pio_device> m_pio;

	required_device<mc6809_device> m_subcpu;
	required_memory_region m_rom_m6809;
	required_device<screen_device> m_screen;
	required_device<upd7220_device> m_gdc;
	required_device<palette_device> m_palette;
	required_shared_ptr<uint16_t> m_video_ram;
	required_device<nvram_device> m_nvram;
	required_device_array<via6522_device, 2> m_via;
	required_device<mc6854_device> m_adlc;
	required_device<mos6551_device> m_acia;
	required_device<ieee488_device> m_ieee;
	required_device<tms9914_device> m_tms9914;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cassette;
	required_device<input_merger_device> m_irqs;

	bool m_rom_mirror;
};


void hhtiger_state::machine_start()
{
	save_item(NAME(m_rom_mirror));
}


void hhtiger_state::machine_reset()
{
	m_rom_mirror = true;
}


static const gfx_layout hhtiger_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	128,                    /* 128 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                    /* every char takes 8 bytes */
};

static GFXDECODE_START(gfx_hhtiger)
	GFXDECODE_ENTRY("rom_m6809", 0x2000, hhtiger_charlayout, 0, 1)
GFXDECODE_END


UPD7220_DISPLAY_PIXELS_MEMBER(hhtiger_state::display_pixels)
{
	/* 96KB video RAM (32KB green + 32KB red + 32KB blue) */
	uint16_t const green = m_video_ram[(0x00000 + (address & 0x3fff))];
	uint16_t const red   = m_video_ram[(0x04000 + (address & 0x3fff))];
	uint16_t const blue  = m_video_ram[(0x08000 + (address & 0x3fff))];

	for (int xi = 0; xi<16; xi++)
	{
		int const r = BIT(red,   xi) ? 255 : 0;
		int const g = BIT(green, xi) ? 255 : 0;
		int const b = BIT(blue,  xi) ? 255 : 0;

		if (bitmap.cliprect().contains(x + xi, y))
			bitmap.pix(y, x + xi) = rgb_t(r, g, b);
	}
}

UPD7220_DRAW_TEXT_LINE_MEMBER(hhtiger_state::draw_text)
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();

	for (int x = 0; x < pitch; x++)
	{
		uint8_t tile = m_video_ram[(((addr + x) * 2) & 0x1ffff) >> 1] & 0xff;
		uint8_t attr = m_video_ram[(((addr + x) * 2) & 0x1ffff) >> 1] >> 8;

		for (int yi = 0; yi < lr; yi++)
		{
			uint8_t tile_data = m_rom_m6809->base()[0x1000 | ((tile * 8 + yi) & 0x7ff)];

			if (cursor_on && cursor_addr == addr + x) //TODO
				tile_data ^= 0xff;

			for (int xi = 0; xi < 8; xi++)
			{
				int pen = (tile_data >> xi) & 1 ? (attr & 0x07) : 0;

				int const res_x = x * 8 + xi;
				int const res_y = y + yi;

				if (yi >= 8)
					pen = 0;

				if (!m_screen->visible_area().contains(res_x, res_y))
					bitmap.pix(res_y, res_x) = palette[pen];
			}
		}
	}
}


uint8_t hhtiger_state::disable_rom_r()
{
	if (!machine().side_effects_disabled())
		m_rom_mirror = false;

	return 0xff;
}

uint8_t hhtiger_state::read(offs_t offset)
{
	uint8_t data;

	if (m_rom_mirror)
	{
		data = m_rom_z80->base()[offset & 0x0fff];
	}
	else
	{
		switch (offset & 0xf800)
		{
		case 0xf800:
			data = m_rom_z80->base()[offset & 0x0fff];
			break;
		default:
			data = m_ram->pointer()[offset];
			break;
		}
	}

	return data;
}

void hhtiger_state::write(offs_t offset, uint8_t data)
{
	m_ram->pointer()[offset] = data;
}


uint8_t hhtiger_state::memory_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	return prog_space.read_byte(offset);
}

void hhtiger_state::memory_write_byte(offs_t offset, uint8_t data)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	prog_space.write_byte(offset, data);
}

uint8_t hhtiger_state::io_read_byte(offs_t offset)
{
	address_space& io_space = m_maincpu->space(AS_IO);
	return io_space.read_byte(offset);
}

void hhtiger_state::io_write_byte(offs_t offset, uint8_t data)
{
	address_space& io_space = m_maincpu->space(AS_IO);
	io_space.write_byte(offset, data);
}


//-------------------------------------------------
//  ADDRESS_MAP( z80_mem )
//-------------------------------------------------

void hhtiger_state::z80_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(hhtiger_state::read), FUNC(hhtiger_state::write));
}

void hhtiger_state::z80_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).rw(m_dma, FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	map(0x04, 0x07).rw(m_pio, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x10, 0x17).rw(m_tms9914, FUNC(tms9914_device::read), FUNC(tms9914_device::write)); // ??
	map(0x1e, 0x1e).r(FUNC(hhtiger_state::disable_rom_r));
}


//-------------------------------------------------
//  ADDRESS_MAP( m6809_mem )
//-------------------------------------------------

void hhtiger_state::m6809_mem(address_map &map)
{
	map(0x0000, 0x07ff).ram();
	map(0xb800, 0xb9ff).ram().share("nvram");
	map(0xbfa0, 0xbfa3).rw(m_gdc, FUNC(upd7220_device::read), FUNC(upd7220_device::write));
	map(0xbfb0, 0xbfb3).rw(m_adlc, FUNC(mc6854_device::read), FUNC(mc6854_device::write)); // ??
	map(0xbfc0, 0xbfcf).m(m_via[1], FUNC(via6522_device::map));
	map(0xbfd0, 0xbfd3).rw(m_acia, FUNC(mos6551_device::read), FUNC(mos6551_device::write)); // ??
	map(0xbfe0, 0xbfef).m(m_via[0], FUNC(via6522_device::map));
	map(0xc000, 0xffff).rom().region("rom_m6809", 0);
}


//-------------------------------------------------
//  ADDRESS_MAP( upd7220_map )
//-------------------------------------------------

void hhtiger_state::upd7220_map(address_map &map)
{
	map.global_mask(0x17fff);
	map(0x00000, 0x17fff).ram().share("video_ram");
}


static const z80_daisy_config daisy_chain_intf[] =
{
	{ "dma" },
	{ "pio" },
	{ nullptr }
};


static INPUT_PORTS_START(hhtiger)
INPUT_PORTS_END


uint8_t hhtiger_state::pio_pa_r()
{
	uint8_t data = 0xff;

	LOG("pio_pa_r %02X\n", data);
	return data;
}

void hhtiger_state::pio_pa_w(uint8_t data)
{
	LOG("pio_pa_w %02X\n", data);
	m_via[0]->write_pa(data);
}

void hhtiger_state::pio_pb_w(uint8_t data)
{
	LOG("pio_pb_w %02X\n", data);
	m_via[0]->write_pb(data);
}

void hhtiger_state::ardy_w(int state)
{
	LOG("ardy_w %d\n", state);
	m_via[0]->write_ca1(state);
}

void hhtiger_state::brdy_w(int state)
{
	LOG("brdy_w %d\n", state);
	m_via[0]->write_cb1(state);
}


uint8_t hhtiger_state::via_0_in_a()
{
	uint8_t data = 0xff;

	LOG("via0_in_a %02X\n", data);
	data = m_pio->port_a_read();
	return data;
}

void hhtiger_state::via_0_out_a(uint8_t data)
{
	LOG("via0_out_a %02X\n", data);
	m_pio->port_a_write(data);
}

void hhtiger_state::via_0_out_b(uint8_t data)
{
	LOG("via0_out_b %02X\n", data);
}

void hhtiger_state::via_0_out_ca2(int state)
{
	LOG("via0_out_ca2 %d\n", state);
	m_pio->strobe_a(state);
}

void hhtiger_state::via_0_out_cb2(int state)
{
	LOG("via0_out_cb2 %d\n", state);
}


void hhtiger_state::via_1_out_a(uint8_t data)
{
	LOG("via1_out_a %02X\n", data);
}

void hhtiger_state::via_1_out_b(uint8_t data)
{
	LOG("via1_out_b %02X\n", data);
}

void hhtiger_state::via_1_out_ca2(int state)
{
	LOG("via1_out_ca2 %d\n", state);
}

void hhtiger_state::via_1_out_cb2(int state)
{
	LOG("via1_out_cb2 %d\n", state);
}


static void hhtiger_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}


void hhtiger_state::hhtiger(machine_config &config)
{
	/* main cpu z80a */
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &hhtiger_state::z80_mem);
	m_maincpu->set_addrmap(AS_IO, &hhtiger_state::z80_io);
	m_maincpu->set_daisy_config(daisy_chain_intf);
	m_maincpu->busack_cb().set(m_dma, FUNC(z80dma_device::bai_w));

	Z80DMA(config, m_dma, 16_MHz_XTAL / 4);
	m_dma->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_dma->out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	m_dma->in_mreq_callback().set(FUNC(hhtiger_state::memory_read_byte));
	m_dma->out_mreq_callback().set(FUNC(hhtiger_state::memory_write_byte));
	m_dma->in_iorq_callback().set(FUNC(hhtiger_state::io_read_byte));
	m_dma->out_iorq_callback().set(FUNC(hhtiger_state::io_write_byte));

	Z80PIO(config, m_pio, 16_MHz_XTAL / 4);
	m_pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio->in_pa_callback().set(FUNC(hhtiger_state::pio_pa_r));
	m_pio->out_pa_callback().set(FUNC(hhtiger_state::pio_pa_w));
	m_pio->out_ardy_callback().set(FUNC(hhtiger_state::ardy_w));
	m_pio->out_pb_callback().set(FUNC(hhtiger_state::pio_pb_w));
	m_pio->out_brdy_callback().set(FUNC(hhtiger_state::brdy_w));

	RAM(config, m_ram).set_default_size("64K");

	/* unknown fdc - floppy drives are housed with monitor so maybe fdc is external */
	FLOPPY_CONNECTOR(config, "0", hhtiger_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "1", hhtiger_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "2", hhtiger_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	/* unknown sound hardware - maybe connected to square-wave timer output from via */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* io cpu 6809 */
	MC6809(config, m_subcpu, 16_MHz_XTAL / 4);
	m_subcpu->set_addrmap(AS_PROGRAM, &hhtiger_state::m6809_mem);

	INPUT_MERGER_ANY_HIGH(config, m_irqs);
	m_irqs->output_handler().set_inputline(m_maincpu, M6809_IRQ_LINE);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_video_attributes(VIDEO_UPDATE_BEFORE_VBLANK);
	m_screen->set_raw(16_MHz_XTAL, 520, 0, 320, 308, 0, 240);
	m_screen->set_screen_update("upd7220", FUNC(upd7220_device::screen_update));

	GFXDECODE(config, "gfxdecode", m_palette, gfx_hhtiger);
	PALETTE(config, m_palette, palette_device::RGB_3BIT);

	UPD7220(config, m_gdc, 16_MHz_XTAL / 16);
	m_gdc->set_addrmap(0, &hhtiger_state::upd7220_map);
	m_gdc->set_display_pixels(FUNC(hhtiger_state::display_pixels));
	m_gdc->set_draw_text(FUNC(hhtiger_state::draw_text));
	m_gdc->set_screen(m_screen);

	MOS6522(config, m_via[0], 16_MHz_XTAL / 16);
	m_via[0]->readpa_handler().set(FUNC(hhtiger_state::via_0_in_a));
	m_via[0]->writepa_handler().set(FUNC(hhtiger_state::via_0_out_a));
	m_via[0]->writepb_handler().set(FUNC(hhtiger_state::via_0_out_b));
	m_via[0]->ca2_handler().set(FUNC(hhtiger_state::via_0_out_ca2));
	m_via[0]->cb2_handler().set(FUNC(hhtiger_state::via_0_out_cb2));
	m_via[0]->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<0>));

	MOS6522(config, m_via[1], 16_MHz_XTAL / 16);
	m_via[1]->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<1>));
	m_via[1]->writepa_handler().set(FUNC(hhtiger_state::via_1_out_a));
	m_via[1]->writepb_handler().set(FUNC(hhtiger_state::via_1_out_b));
	m_via[1]->ca2_handler().set(FUNC(hhtiger_state::via_1_out_ca2));
	m_via[1]->cb2_handler().set(FUNC(hhtiger_state::via_1_out_cb2));

	centronics_device &centronics(CENTRONICS(config, "centronics", centronics_devices, "printer"));
	centronics.ack_handler().set(m_via[1], FUNC(via6522_device::write_ca1));
	output_latch_device &latch(OUTPUT_LATCH(config, "cent_data_out"));
	centronics.set_output_latch(latch);

	MC6854(config, m_adlc);
	m_adlc->out_irq_cb().set("irqs", FUNC(input_merger_device::in_w<2>));

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	MOS6551(config, m_acia, 0);
	m_acia->set_xtal(1.8432_MHz_XTAL);
	m_acia->irq_handler().set("irqs", FUNC(input_merger_device::in_w<3>));
	m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_acia->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_acia->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_acia, FUNC(mos6551_device::write_rxd));
	rs232.dcd_handler().set(m_acia, FUNC(mos6551_device::write_dcd));
	rs232.dsr_handler().set(m_acia, FUNC(mos6551_device::write_dsr));
	rs232.cts_handler().set(m_acia, FUNC(mos6551_device::write_cts));

	TMS9914(config, m_tms9914, 16_MHz_XTAL / 4);
	m_tms9914->int_write_cb().set("irqs", FUNC(input_merger_device::in_w<4>));
	m_tms9914->dio_read_cb().set(IEEE488_TAG, FUNC(ieee488_device::dio_r));
	m_tms9914->dio_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_dio_w));
	m_tms9914->eoi_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_eoi_w));
	m_tms9914->dav_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_dav_w));
	m_tms9914->nrfd_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_nrfd_w));
	m_tms9914->ndac_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ndac_w));
	m_tms9914->ifc_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ifc_w));
	m_tms9914->srq_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_srq_w));
	m_tms9914->atn_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_atn_w));
	m_tms9914->ren_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ren_w));

	IEEE488(config, m_ieee);
	m_ieee->eoi_callback().set(m_tms9914, FUNC(tms9914_device::eoi_w));
	m_ieee->dav_callback().set(m_tms9914, FUNC(tms9914_device::dav_w));
	m_ieee->nrfd_callback().set(m_tms9914, FUNC(tms9914_device::nrfd_w));
	m_ieee->ndac_callback().set(m_tms9914, FUNC(tms9914_device::ndac_w));
	m_ieee->ifc_callback().set(m_tms9914, FUNC(tms9914_device::ifc_w));
	m_ieee->srq_callback().set(m_tms9914, FUNC(tms9914_device::srq_w));
	m_ieee->atn_callback().set(m_tms9914, FUNC(tms9914_device::atn_w));
	m_ieee->ren_callback().set(m_tms9914, FUNC(tms9914_device::ren_w));
	IEEE488_SLOT(config, "ieee_dev", 0, cbm_ieee488_devices, nullptr);
}


ROM_START(hhtiger)
	ROM_REGION(0x1000, "rom_z80", 0)
	ROM_DEFAULT_BIOS("rel13")
	ROM_SYSTEM_BIOS(0, "rel12", "Rel1.2")
	ROMX_LOAD("rel1.2-0ea0.ic79", 0x0000, 0x1000, CRC(2f81e48c) SHA1(a4a1d7fde9f92abd6d8f8a1c24e35d713a5cbcb2), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "rel13", "Rel1.3")
	ROMX_LOAD("rel1.3-0ea0.ic79", 0x0000, 0x1000, CRC(2f81e48c) SHA1(a4a1d7fde9f92abd6d8f8a1c24e35d713a5cbcb2), ROM_BIOS(1))
	/* Rel1.4 also known to exist */
	ROM_REGION(0x4000, "rom_m6809", 0)
	ROMX_LOAD("rel1.2-cfd3.ic16", 0x0000, 0x2000, CRC(1ae4d2e0) SHA1(c379c5f1be24835ae4b4bd7bed35800faa3a9af6), ROM_BIOS(0))
	ROMX_LOAD("rel1.2-8fd2.ic15", 0x2000, 0x2000, CRC(0ef23968) SHA1(d46ce3b965ce51d0c90a10121661199b51e33d8b), ROM_BIOS(0))
	ROMX_LOAD("rel1.3-789a.ic16", 0x0000, 0x2000, CRC(7fa6fd33) SHA1(0b2768c170ca7077ef5164bfa13d9bf033528115), ROM_BIOS(1))
	ROMX_LOAD("rel1.3-77c1.ic15", 0x2000, 0x2000, CRC(dd2f15d5) SHA1(139a2b97cb8c27a50e3bfa3f42a9572203e453e0), ROM_BIOS(1))
ROM_END

} // anonymous namespace


/*    YEAR  NAME     PARENT  COMPAT   MACHINE  INPUT    CLASS          INIT        COMPANY               FULLNAME     FLAGS */
COMP( 1983, hhtiger, 0,      0,       hhtiger, hhtiger, hhtiger_state, empty_init, "H/H Microcomputers", "H/H Tiger", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )




hk1000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for Orla HK1000 music keyboard.

***************************************************************************/

#include "emu.h"
#include "cpu/m6502/m6502.h"
#include "cpu/tms7000/tms7000.h"
#include "cpu/upd7810/upd7810.h"
#include "machine/6850acia.h"
#include "machine/i8255.h"
#include "sound/ymopl.h"


namespace {

class hk1000_state : public driver_device
{
public:
	hk1000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_soundcpu(*this, "soundcpu")
		, m_slotcpu(*this, "slotcpu")
	{
	}

	void hk1000(machine_config &config);

private:
	void main_map(address_map &map) ATTR_COLD;
	void sound_map(address_map &map) ATTR_COLD;
	void slot_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<cpu_device> m_soundcpu;
	required_device<cpu_device> m_slotcpu;
};


void hk1000_state::main_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("mainpcb", 0);
	map(0x2000, 0x2000).nopw(); // ?
	map(0x8000, 0x9fff).ram();
	map(0xa000, 0xa003).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xa300, 0xa300).nopr(); // ?
	map(0xc000, 0xc000).nopr(); // ?
}

void hk1000_state::sound_map(address_map &map)
{
	map(0xc000, 0xffff).rom().region("soundpcb", 0);
}

void hk1000_state::slot_map(address_map &map)
{
	map(0x0000, 0x0fff).ram();
	map(0x1000, 0x1001).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0x2000, 0x2001).rw("ymsnd", FUNC(ym3812_device::read), FUNC(ym3812_device::write));
	map(0x8000, 0xffff).rom().region("slotpcb", 0);
}


static INPUT_PORTS_START(hk1000)
INPUT_PORTS_END

void hk1000_state::hk1000(machine_config &config)
{
	UPD7810(config, m_maincpu, 12'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &hk1000_state::main_map);

	I8255(config, "ppi");

	TMS7002(config, m_soundcpu, 4'000'000);
	m_soundcpu->set_addrmap(AS_PROGRAM, &hk1000_state::sound_map);

	M6502(config, m_slotcpu, 2'000'000);
	m_slotcpu->set_addrmap(AS_PROGRAM, &hk1000_state::slot_map);

	acia6850_device &acia(ACIA6850(config, "acia"));
	acia.irq_handler().set_inputline(m_slotcpu, INPUT_LINE_NMI);

	YM3812(config, "ymsnd", 4'000'000);
}


/*
Dumper's notes:

This EPROM set stems from a working specimen of the Orla HK1000 music keyboard.

layout
------
The Orla HK1000 contains many small PCBs. Except PCB3 (analog) they all contain each one eprom. From left to right I number these PCB1 to PCB5. The slot PCB is in the case top to the right.


PCB1 (FM main voice)
--------------------
Suono
HK

PCB2 (main CPU)
---------------
HK 1000
V2

PCB4 (reverb)
-------------
STD.OEM 4/2/87
ALESIS 4D49 A

PCB5 (pcm percussion)
---------------------
TIMBRI
PCM

slot PCB (accompaniment)
------------------------
AKK
HK
V2
*/

ROM_START(hk1000)
	ROM_REGION(0x8000, "mainpcb", 0)
	ROM_LOAD("hk1000 v2_s 27c256-20.bin", 0x0000, 0x8000, CRC(b5d005e9) SHA1(5b1b6e45e84494254c5937ba726ea0f15162bdc9))

	ROM_REGION(0x4000, "soundpcb", 0)
	ROM_LOAD("suono hk_ti tms 27c128-2jl.bin", 0x0000, 0x4000, CRC(02f99a30) SHA1(fec3884150a68b6d3ed29cbcb1ce84ebf2c90dd9))

	ROM_REGION(0x2000, "reverb", 0)
	ROM_LOAD("std.oem 4-2-87 alesis 4d49 a_nmc27c64q 200.bin", 0x0000, 0x2000, CRC(44319276) SHA1(b916d8e88ec28f6b49eb814c5328a35dc8ed857f))

	ROM_REGION(0x10000, "pcm", 0)
	ROM_LOAD("timbri pcm_ti tms 27c512-20jl.bin", 0x00000, 0x10000, CRC(dd573584) SHA1(1d554e11dbcbab390d3fc995976daf122908bad0))

	ROM_REGION(0x8000, "slotpcb", 0)
	ROM_LOAD("akk hk v2_ti tms 27c512-20jl.bin", 0x0000, 0x8000, CRC(74f6461b) SHA1(b2c1b44842b8825123beaad4c0e6692a57e1a9ed))
	ROM_CONTINUE(0x0000, 0x8000) // 0xxxxxxxxxxxxxxx = 0xFF
ROM_END

} // anonymous namespace


SYST(198?, hk1000, 0, 0, hk1000, hk1000, hk1000_state, empty_init, "Orla", "HK1000", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



hohnadam.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Hohner ADAM keyboard synthesizer.

****************************************************************************/

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/nvram.h"

namespace {

class hohnadam_state : public driver_device
{
public:
	hohnadam_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_panelcpu(*this, "panelcpu")
	{
	}

	void hohnadam(machine_config &config);

private:
	u8 c005fb_r();
	void d00000_w(offs_t offset, u8 data);
	void d40000_w(offs_t offset, u8 data);
	void d80000_w(offs_t offset, u8 data);

	void main_map(address_map &map) ATTR_COLD;
	void panel_map(address_map &map) ATTR_COLD;
	void panel_ext_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<mcs51_cpu_device> m_panelcpu;
};

u8 hohnadam_state::c005fb_r()
{
	return 0x20;
}

void hohnadam_state::d00000_w(offs_t offset, u8 data)
{
}

void hohnadam_state::d40000_w(offs_t offset, u8 data)
{
}

void hohnadam_state::d80000_w(offs_t offset, u8 data)
{
}

void hohnadam_state::main_map(address_map &map)
{
	map(0x000000, 0x0fffff).rom().region("program", 0);
	map(0x400000, 0x5fffff).ram();
	map(0x800000, 0x80ffff).ram().share("nvram");
	map(0xc004f2, 0xc004f3).nopr();
	map(0xc005fb, 0xc005fb).r(FUNC(hohnadam_state::c005fb_r));
	map(0xc007e4, 0xc007e5).nopw();
	map(0xc007e8, 0xc007e9).nopr();
	map(0xc007f2, 0xc007f3).noprw();
	map(0xd00000, 0xd00007).w(FUNC(hohnadam_state::d00000_w)).umask16(0x00ff);
	map(0xd40000, 0xd40009).w(FUNC(hohnadam_state::d40000_w)).umask16(0x00ff);
	map(0xd80000, 0xd80009).w(FUNC(hohnadam_state::d80000_w)).umask16(0x00ff);
}

void hohnadam_state::panel_map(address_map &map)
{
	map(0x0000, 0xffff).rom().region("panel", 0);
}

void hohnadam_state::panel_ext_map(address_map &map)
{
	map(0x001d, 0x001d).noprw();
}

static INPUT_PORTS_START(hohnadam)
INPUT_PORTS_END

void hohnadam_state::hohnadam(machine_config &config)
{
	M68000(config, m_maincpu, 12000000); // MC68000FN12, unknown clock
	m_maincpu->set_addrmap(AS_PROGRAM, &hohnadam_state::main_map);

	I8032(config, m_panelcpu, 12000000); // unknown type and clock
	m_panelcpu->set_addrmap(AS_PROGRAM, &hohnadam_state::panel_map);
	m_panelcpu->set_addrmap(AS_IO, &hohnadam_state::panel_ext_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // 2x HY62256ALJ-70 + CR2032 battery

	//F82C721(config, "io", 24000000);

	// TODO: LCD, everything else
}

ROM_START(hohnadam)
	ROM_REGION16_BE(0x100000, "program", 0)
	ROM_LOAD16_BYTE("adam u33_2 version 1.1.bin", 0x00000, 0x80000, CRC(e5d60622) SHA1(9e105ed6d403ecf6713cd4c6643fab9586f25217)) // TMS27C040-10
	ROM_LOAD16_BYTE("adam u32_2 version 1.1.bin", 0x00001, 0x80000, CRC(7c52d112) SHA1(34b1f88ae4a2a575a41e542ab41159b25cb6fc6e)) // TMS27C040-10

	ROM_REGION(0x20000, "panel", 0)
	ROM_LOAD("adam panel version 1.1.bin", 0x00000, 0x20000, CRC(69c45992) SHA1(91c278defdf790ecdd3ff6df90e29d0bf508ba44)) // TMS27C010A-10

	ROM_REGION16_BE(0x300000, "waveroms", 0)
	ROM_LOAD16_BYTE("adam u36 1.bin", 0x000001, 0x080000, CRC(879b0acb) SHA1(7b86069e0939608ea2863fc56a91c344c8360add)) // Am27C040-150DC
	ROM_LOAD16_BYTE("adam u37_2.bin", 0x100001, 0x080000, CRC(dadc8167) SHA1(e37c46651868bb89dc9c768d46de34f1aedfbac9)) // Am27C040-150DC
	ROM_LOAD16_BYTE("adam u38_2.bin", 0x200001, 0x080000, CRC(d522acb3) SHA1(e4941163d4232a7ed119848e900c4848bef181f9)) // Am27C040-150DC
	ROM_LOAD16_BYTE("adam u39_2.bin", 0x000000, 0x080000, CRC(70990980) SHA1(7773d2e184b11cd83ac9682699bb7790d4455e6b)) // Am27C040-150DC
	ROM_LOAD16_BYTE("adam u40 1.bin", 0x100000, 0x080000, CRC(6cfa9a26) SHA1(3d07b3d3c2934dd8c917da6115a9786da64ba98f)) // Am27C040-150DC
	ROM_LOAD16_BYTE("adam u41_2.bin", 0x200000, 0x080000, CRC(46af2b27) SHA1(bb8d75afd65d25678797ee4b34cce3bcb7f13e5b)) // Am27C040-150DC

	ROM_REGION(0x400000, "maskroms", 0)
	ROM_LOAD("dream-gms931600n.u47", 0x000000, 0x200000, NO_DUMP)
	ROM_LOAD("dream-gms931601n.u48", 0x200000, 0x200000, NO_DUMP)
ROM_END

} // anonymous namespace

SYST(1994, hohnadam, 0, 0, hohnadam, hohnadam, hohnadam_state, empty_init, "Hohner", "ADAM Advanced Digital/Analog Musical Instrument", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



homelab.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:linuxforum5, Miodrag Milanovic, Robbbert
/***************************************************************************

    Homelab driver by Miodrag Milanovic

    31/08/2008 Preliminary driver.
    15/06/2012 Various updates [Robbbert]
    27/01/2024 homelab2 now works, reorganized to better match current standards. [linuxforum5, R. Belmont]

    The emulator called HoLa! works fine, but it is closed source.
    You can get HoLa! at: http://gaia.atilia.eu

    ToDO:
    - HTP files should be a cassette format, not a quickload.
    - Quickloads cause the emulated machine to hang or reboot.
    - homelab2 - cassette to fix.
                 Note that rom code 0x40-48 is meaningless garbage,
                 had to patch to stop it crashing. Need a new dump.
               - The speaker code is wrong, but it's the only way to
                 get any sound. Needs to be looked at again.
    - homelab3/4 - Need a dump of the TM188 prom.
                 cassette to fix.
                 up to 64k ram can be fitted. schematic only shows 16k.
                 Z80PIO - expansion only, doesn't do anything in the
                 machine. /CE connects to A6, A/B = A0, C/D = A1.
                 The bios never talks to it. Official port numbers
                 are 3C-3F.
    - Brailab4 - Same as homelab3.


TM188 is (it seems) equivalent to 27S19, TBP18S030N, 6331-1, 74S288, 82S123,
MB7051 - fuse programmed prom.

****************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "machine/timer.h"
#include "sound/mea8000.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

namespace {

class homelab_state : public driver_device
{
public:
	homelab_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_bank1(*this, "bank1"),
		m_p_chargen(*this, "chargen"),
		m_speaker(*this, "speaker"),
		m_cass(*this, "cassette"),
		m_io_keyboard(*this, "X%d", 0),
		m_rows(0), m_cols(0)
	{
	}

protected:
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	required_device<cpu_device> m_maincpu;
	required_memory_bank m_bank1;
	required_region_ptr<u8> m_p_chargen;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cass;
	required_ioport_array<16> m_io_keyboard;

	std::unique_ptr<u8[]> m_vram;
	u8 m_rows; // Character rows in screen
	u8 m_cols;
};

class homelab2_state : public homelab_state
{
public:
	homelab2_state(const machine_config &mconfig, device_type type, const char *tag) :
		homelab_state(mconfig, type, tag),
		m_nmi(0),
		m_screenshadow_is_text_mode(true),
		m_screenshadowY0(0),
		m_screenshadowX0(0),
		m_spr_bit(0)
	{
		std::fill(std::begin(m_4000shadow), std::end(m_4000shadow), 0);
		std::fill(std::begin(m_screenshadow), std::end(m_screenshadow), 0);
	}

	void homelab2(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	u32 screen2_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

private:
	INTERRUPT_GEN_MEMBER(homelab_frame);
	void homelab2_mem(address_map &map) ATTR_COLD;
	u8 cass2_r();
	u8 mem3800_r();
	void mem3800_w(offs_t offset, u8 data);
	u8 mem3a00_r(offs_t offset);
	void mem3c00_w(offs_t, u8 data);
	u8 mem3e00_r(offs_t offset);
	void mem3e00_w(offs_t, u8 data);
	u8 mem4000_r(offs_t offset);
	void mem4000_w(offs_t, u8 data);
	u8 memE000_r(offs_t offset);
	void memE000_w(offs_t, u8 data);

	bool m_nmi;

	u8 m_4000shadow[0x4000];            // Shadow for 0x4000-0x7FFF
	u8 m_screenshadow[40 * 265];        // Maximum screen size is 320x255 (1 bit = 1 pixel). CPU controls hardware to generate video signal.

	bool m_screenshadow_is_text_mode;   // If true, the data source for video generator is from C000. If false, the data source is from 0x6000. This memory contetn addressable from 0xE000
	int32_t m_screenshadowY0;           // The current generated screen x byte position
	int32_t m_screenshadowX0;           // Screen row position
	bool m_spr_bit;
};

class homelab3_state : public homelab_state
{
public:
	using homelab_state::homelab_state;

	void homelab3(machine_config &config);
	void brailab4(machine_config &config);

	int cass3_r();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u8 exxx_r(offs_t offset);
	void port7f_w(u8 data);
	void portff_w(u8 data);
	void homelab3_io(address_map &map) ATTR_COLD;
	void homelab3_mem(address_map &map) ATTR_COLD;
	void brailab4_io(address_map &map) ATTR_COLD;
	void brailab4_mem(address_map &map) ATTR_COLD;

	std::unique_ptr<u8[]> m_ram;
};

INTERRUPT_GEN_MEMBER(homelab2_state::homelab_frame)
{
	if (m_nmi)
	{
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
	}
}

u32 homelab2_state::screen2_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	if (!m_cols)
	{
		return 1;
	}

	int screenHeight = (m_screenshadowY0 > 200) ? m_screenshadowY0 : 200;
	screen.set_visarea(0, 319, 0, screenHeight - 1);
	for (int y = 0; y < screenHeight; y++)
	{
		u16 *p = &bitmap.pix(y); // Row first pixel pointer in viewable screen in screen_y. line
		for (int x = 0; x < 40; x++)
		{
			if (y < m_screenshadowY0)
			{
				u8 const gfx = m_screenshadow[y * 40 + x];
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
			else
			{
				*p++ = 0;
				*p++ = 0;
				*p++ = 0;
				*p++ = 0;
				*p++ = 0;
				*p++ = 0;
				*p++ = 0;
				*p++ = 0;
			}
		}
	}
	m_screenshadowY0 = 0;
	m_screenshadowX0 = 0;
	return 0;
}

u8 homelab2_state::mem3800_r()
{
	return m_io_keyboard[15]->read(); // reset key
}

void homelab2_state::mem3800_w(offs_t offset, u8 data)
{ // Set screen generator hardware to graphics mode
	if (offset == 0x3939 - 0x3800)
	{
		m_screenshadow_is_text_mode = false;
	}
}

u8 homelab2_state::mem3a00_r(offs_t offset)
{
	u8 data = 0xff;

	for (u8 i = 0; i < 8; i++)
	{
		if (!BIT(offset, i))
		{
			data &= m_io_keyboard[i]->read();
		}
	}
	return data;
}

u8 homelab2_state::cass2_r()
{
	return (m_cass->input() > 0.03) ? 0xff : 0;
}

void homelab2_state::mem3c00_w(offs_t offset, u8 data)
{
	m_spr_bit ^= 1;
	m_speaker->level_w(m_spr_bit ? -1.0 : +1.0);
	m_cass->output(m_spr_bit ? -1.0 : +1.0);
}

u8 homelab2_state::memE000_r(offs_t offset)
{
	if (m_nmi)
	{ // NMI enabled, screen generator
		u8 gfx;
		if (m_screenshadow_is_text_mode)
		{
			const int vramRelIndex0 = offset & 0x3ff; // Character address in video ram First character in 0x001
			const int row8_index0 = ((offset - 1) & 0x1c00) >> 10; // Row index in char [0-7]
			u8 const chr = m_vram[vramRelIndex0]; // get char in videoram
			gfx = m_p_chargen[chr | (row8_index0 << 8)]; // get dot pattern in chargen
		}
		else
		{
			gfx = m_4000shadow[0x2000 + offset]; // get dot pixels in GRAPH RAM
		}
		m_screenshadow[40 * m_screenshadowY0 + m_screenshadowX0++] = gfx;

		if (m_screenshadowX0 == 40)
		{
			m_screenshadowX0 = 0;
			m_screenshadowY0++;
			return (m_screenshadow_is_text_mode) ? 0xff : 0xf7; // RST38 : RST 30
		}
		else
		{
			return 0x7f; // LD A,A
		}
	}
	else
	{ // NMI disable, serial input
		return cass2_r();
	}
}

u8 homelab2_state::mem4000_r(offs_t offset)
{
	return m_4000shadow[offset];
}

void homelab2_state::mem4000_w(offs_t offset, u8 data)
{
	m_4000shadow[offset] = data;
}

u8 homelab2_state::mem3e00_r(offs_t offset)
{
	return 0;
}

/**
 * NMI on: LD (3E3E), 0
 * NMI off: LD (3F3F), AL értéke
 */
void homelab2_state::mem3e00_w(offs_t offset, u8 data)
{
	if (offset == 0x3f3f - 0x3e00)
	{ // 3F3F
		m_screenshadow_is_text_mode = true;
		m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
		m_nmi = true;
	}
	if (offset == 0x003e)
	{ // 3E3E
		m_nmi = false;
	}
}

void homelab3_state::machine_reset()
{
	m_bank1->set_entry(1);
}

void homelab3_state::port7f_w(u8 data)
{
	m_bank1->set_entry(1);
}

void homelab3_state::portff_w(u8 data)
{
	m_bank1->set_entry(0);
}

int homelab3_state::cass3_r()
{
	return (m_cass->input() > 0.03);
}

u8 homelab3_state::exxx_r(offs_t offset)
{
	// keys E800-E813 but E810-E813 are not connected
	// cassin E883
	// speaker/cass toggle E880, E802
	if (offset == 0x83)
	{
		return (m_cass->input() > 0.03);
	}
	else
	{
		if (offset == 0x80)
		{
			m_speaker->level_w(0);
			m_cass->output(-1.0);
		}
		else
		{
			if (offset == 0x02)
			{
				m_speaker->level_w(1);
				m_cass->output(+1.0);
			}
		}
	}

	u8 data = 0xff;
	if (offset < 0x10)
	{
		data = m_io_keyboard[offset]->read();
	}
	return data;
}

/* Address maps */
void homelab2_state::homelab2_mem(address_map &map)
{
	map(0x0000, 0x07ff).rom(); // ROM 1
	map(0x0800, 0x0fff).rom(); // ROM 2
	map(0x1000, 0x17ff).rom(); // ROM 3
	map(0x1800, 0x1fff).rom(); // ROM 4
	map(0x2000, 0x27ff).rom(); // ROM 5
	map(0x2800, 0x2fff).rom(); // ROM 6
	map(0x3000, 0x37ff).rom(); // Empty
	map(0x3800, 0x39ff).rw(FUNC(homelab2_state::mem3800_r), FUNC(homelab2_state::mem3800_w));
	map(0x3a00, 0x3bff).r(FUNC(homelab2_state::mem3a00_r));
	map(0x3c00, 0x3dff).w(FUNC(homelab2_state::mem3c00_w));
	map(0x3e00, 0x3fff).rw(FUNC(homelab2_state::mem3e00_r), FUNC(homelab2_state::mem3e00_w));

	map(0x4000, 0x7fff).rw(FUNC(homelab2_state::mem4000_r), FUNC(homelab2_state::mem4000_w));

	map(0xc000, 0xc3ff).mirror(0xc00).bankrw(m_bank1);

	map(0xe000, 0xffff).r(FUNC(homelab2_state::memE000_r));
}

void homelab3_state::homelab3_mem(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	map(0x4000, 0xcfff).ram();
	map(0xe800, 0xefff).r(FUNC(homelab3_state::exxx_r));
	map(0xf800, 0xffff).bankrw(m_bank1);
}

void homelab3_state::homelab3_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x7f, 0x7f).w(FUNC(homelab3_state::port7f_w));
	map(0xff, 0xff).w(FUNC(homelab3_state::portff_w));
}

void homelab3_state::brailab4_mem(address_map &map)
{
	homelab3_mem(map);
	map(0xd000, 0xdfff).rom().region("maincpu", 0x4000);
}

void homelab3_state::brailab4_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	homelab3_io(map);
	map(0xf8, 0xf9).rw("mea8000", FUNC(mea8000_device::read), FUNC(mea8000_device::write));
}

/* Input ports */
static INPUT_PORTS_START(homelab2)
	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0xDE, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Home") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Run/Brk") PORT_CODE(KEYCODE_RCONTROL)

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('<')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR('<') PORT_CHAR(',')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('=') PORT_CHAR('-')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('>') PORT_CHAR('.')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('?') PORT_CHAR('/')

	PORT_START("X4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')

	PORT_START("X5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')

	PORT_START("X6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')

	PORT_START("X7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('^')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RALT) PORT_CODE(KEYCODE_LALT) PORT_CHAR('_')

	PORT_START("X8")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X9")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X10")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X11")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X12")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X13")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X14")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X15")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0xfe, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

	static INPUT_PORTS_START(homelab3) // F4 to F8 are foreign characters
	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED) // A
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)                                     // D
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("screen", FUNC(screen_device::vblank))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ALT") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(homelab3_state::cass3_r))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('=') PORT_CHAR('-')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('?') PORT_CHAR('/')
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X11")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X12")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X13")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X14")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X15")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START(brailab4) // F4 to F8 are foreign characters
	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED) // A
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)                                     // D
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("screen", FUNC(screen_device::vblank))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ALT") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(homelab3_state::cass3_r))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('=') PORT_CHAR('-')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('?') PORT_CHAR('/')
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X11")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X12")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X13")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X14")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X15")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

void homelab2_state::machine_start()
{
	save_item(NAME(m_nmi));
	save_item(NAME(m_rows));
	save_item(NAME(m_cols));
	save_item(NAME(m_4000shadow));
	save_item(NAME(m_screenshadow));
	save_item(NAME(m_screenshadow_is_text_mode));
	save_item(NAME(m_screenshadowX0));
	save_item(NAME(m_screenshadowY0));
	save_item(NAME(m_spr_bit));

	m_vram = make_unique_clear<u8[]>(0x800);
	save_pointer(NAME(m_vram), 0x800);
	m_bank1->configure_entry(0, m_vram.get());
	m_bank1->set_entry(0);

	m_rows = 25;
	m_cols = 40;
	m_nmi = 0;
	m_spr_bit = 0;
}

void homelab3_state::machine_start()
{
	save_item(NAME(m_rows));
	save_item(NAME(m_cols));
	m_vram = make_unique_clear<u8[]>(0x800);
	save_pointer(NAME(m_vram), 0x800);
	m_ram = make_unique_clear<u8[]>(0x800);
	save_pointer(NAME(m_ram), 0x800);
	m_bank1->configure_entry(0, m_vram.get());
	m_bank1->configure_entry(1, m_ram.get());
	m_rows = 32;
	m_cols = 64;
}

u32 homelab_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	if (!m_cols)
	{
		return 1;
	}

	u16 sy = 0, ma = 0;
	for (u8 y = 0; y < m_rows; y++)
	{
		for (u8 ra = 0; ra < 8; ra++)
		{
			u16 *p = &bitmap.pix(sy++);
			for (u16 x = ma; x < ma + m_cols; x++)
			{
				u8 const chr = m_vram[x];                    // get char in videoram
				u8 const gfx = m_p_chargen[chr | (ra << 8)]; // get dot pattern in chargen

				/* Display a scanline of a character */
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma += m_cols;
	}
	return 0;
}

/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 8, /* 8 x 8 characters */
	256,  /* 256 characters */
	1,    /* 1 bits per pixel */
	{0},  /* no bitplanes */
	/* x offsets */
	{0, 1, 2, 3, 4, 5, 6, 7},
	/* y offsets */
	{0, 1 * 256 * 8, 2 * 256 * 8, 3 * 256 * 8, 4 * 256 * 8, 5 * 256 * 8, 6 * 256 * 8, 7 * 256 * 8},
	8 /* every char takes 8 x 1 bytes */
};

static GFXDECODE_START(gfx_homelab)
	GFXDECODE_ENTRY("chargen", 0x0000, charlayout, 0, 1)
GFXDECODE_END

QUICKLOAD_LOAD_MEMBER(homelab_state::quickload_cb)
{
	address_space &space = m_maincpu->space(AS_PROGRAM);
	int block_counter = 0;
	char block_last_character = 1;
	char pgmname[256];
	u16 args[2];

	image.fseek(0, SEEK_SET);

	while (block_last_character != 0)
	{
		u8 ch = 0;
		u32 bytes = 0;

		block_counter++;
		// Read leading zeros
		while (((bytes = image.fread(&ch, 1)) != 0) && (ch == 0))
		{
		}

		if (bytes != 1 || ch != 0xa5)
			return std::make_pair(image_error::INVALIDIMAGE, "Invalid header");

		int i = 0;
		while ((bytes = image.fread(&ch, 1)) != 0 && (ch != 0))
		{
			if (i >= (std::size(pgmname) - 1))
			{
				return std::make_pair(image_error::INVALIDIMAGE, "File name too long");
			}
			// image.message treats characters with bit 7 as nulls, so replace with question mark
			pgmname[i] = BIT(ch, 7) ? 0x3f : ch; // build program description
			i++;
		}
		pgmname[i] = '\0'; /* terminate string with a null */

		if (image.fread(args, sizeof(args)) != sizeof(args))
		{
			return std::make_pair(image_error::UNSPECIFIED, "Unexpected EOF while getting file size");
		}

		const u16 quick_addr = little_endianize_int16(args[0]);
		const u16 quick_length = little_endianize_int16(args[1]);
		const u16 quick_end = quick_addr + quick_length - 1;

		if (quick_end > 0x7fff)
		{
			return std::make_pair(image_error::INVALIDLENGTH, "File too large");
		}

		for (int i = 0; i < quick_length; i++)
		{
			unsigned j = (quick_addr + i);
			if (image.fread(&ch, 1) != 1)
			{
				return std::make_pair(image_error::UNSPECIFIED, util::string_format("%s: Unexpected EOF while writing byte to %04X", pgmname, j));
			}
			space.write_byte(j, ch);
		}
		image.fread(&ch, 1); // Read crc
		image.fread(&block_last_character, 1);
		/* display a message about the loaded quickload */
		image.message(" %s\nsize=%04X : start=%04X : end=%04X : block_counter=%d", pgmname, quick_length, quick_addr, quick_end, block_counter);
	}
	return std::make_pair(std::error_condition(), std::string());
}

/* Machine driver */
void homelab2_state::homelab2(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(8'000'000) / 2); // 4mhz
	m_maincpu->set_addrmap(AS_PROGRAM, &homelab2_state::homelab2_mem);
	m_maincpu->set_vblank_int("screen", FUNC(homelab2_state::homelab_frame));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::white())); // green
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.set_size(320, 255);
	screen.set_visarea(0, 40 * 8 - 1, 0, 25 * 8 - 1);
	screen.set_screen_update(FUNC(homelab2_state::screen2_update));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_homelab);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	CASSETTE(config, m_cass);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	QUICKLOAD(config, "quickload", "htp", attotime::from_seconds(2)).set_load_callback(FUNC(homelab2_state::quickload_cb));
}

void homelab3_state::homelab3(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(12'000'000) / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &homelab3_state::homelab3_mem);
	m_maincpu->set_addrmap(AS_IO, &homelab3_state::homelab3_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.set_size(64 * 8, 32 * 8);
	screen.set_visarea(0, 64 * 8 - 1, 0, 32 * 8 - 1);
	screen.set_screen_update(FUNC(homelab3_state::screen_update));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_homelab);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	CASSETTE(config, m_cass);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	QUICKLOAD(config, "quickload", "htp", attotime::from_seconds(2)).set_load_callback(FUNC(homelab3_state::quickload_cb));
}

void homelab3_state::brailab4(machine_config &config)
{
	homelab3(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &homelab3_state::brailab4_mem);
	m_maincpu->set_addrmap(AS_IO, &homelab3_state::brailab4_io);
	MEA8000(config, "mea8000", 3'840'000).add_route(ALL_OUTPUTS, "mono", 1.0);
}

/* ROM definition */

ROM_START(homelab2)
	ROM_REGION(0x3800, "maincpu", ROMREGION_ERASEFF)
	ROM_LOAD("hl2_1.ic2", 0x0000, 0x0800, CRC(205365f7) SHA1(da93b65befd83513dc762663b234227ba804124d))
	ROM_LOAD("hl2_2.ic3", 0x0800, 0x0800, CRC(696af3c1) SHA1(b53bc6ae2b75975618fc90e7181fa5d21409fce1))
	ROM_LOAD("hl2_3.ic4", 0x1000, 0x0800, CRC(69e57e8c) SHA1(e98510abb715dbf513e1b29fb6b09ab54e9483b7))
	ROM_LOAD("hl2_4.ic5", 0x1800, 0x0800, CRC(97cbbe74) SHA1(34f0bad41302b059322018abc3d1c2336ecfbea8))
	ROM_LOAD("hl2_m.ic6", 0x2000, 0x0800, CRC(10040235) SHA1(e121dfb97cc8ea99193a9396a9f7af08585e0ff0))

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD("hl2.ic33", 0x0000, 0x0800, CRC(2e669d40) SHA1(639dd82ed29985dc69830aca3b904b6acc8fe54a))
ROM_END

ROM_START(homelab3)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_LOAD("hl3_1.ic1", 0x0000, 0x1000, CRC(6b90a8ea) SHA1(8ac40ca889b8c26cdf74ca309fbafd70dcfdfbec))
	ROM_LOAD("hl3_2.ic2", 0x1000, 0x1000, CRC(bcac3c24) SHA1(aff371d17f61cb60c464998e092f04d5d85c4d52))
	ROM_LOAD("hl3_3.ic3", 0x2000, 0x1000, CRC(ab1b4ab0) SHA1(ad74c7793f5dc22061a88ef31d3407267ad08719))
	ROM_LOAD("hl3_4.ic4", 0x3000, 0x1000, CRC(bf67eff9) SHA1(2ef5d46f359616e7d0e5a124df528de44f0e850b))

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD("hl3.ic21", 0x0000, 0x0800, CRC(f58ee39b) SHA1(49399c42d60a11b218a225856da86a9f3975a78a))

	ROM_REGION(0x0040, "proms", 0)
	ROM_LOAD("tm188.ic7", 0x0000, 0x0040, NO_DUMP)
ROM_END

ROM_START(homelab4)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_LOAD("hl4_1.ic1", 0x0000, 0x1000, CRC(a549b2d4) SHA1(90fc5595da8431616aee56eb5143b9f04281e798))
	ROM_LOAD("hl4_2.ic2", 0x1000, 0x1000, CRC(151d33e8) SHA1(d32004bc1553f802b9d3266709552f7d5315fe44))
	ROM_LOAD("hl4_3.ic3", 0x2000, 0x1000, CRC(39571ab1) SHA1(8470cff2e3442101e6a0bc655358b3a6fc1ef944))
	ROM_LOAD("hl4_4.ic4", 0x3000, 0x1000, CRC(f4b77ca2) SHA1(ffbdb3c1819c7357e2a0fc6317c111a8a7ecfcd5))

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD("hl4.ic21", 0x0000, 0x0800, CRC(f58ee39b) SHA1(49399c42d60a11b218a225856da86a9f3975a78a))

	ROM_REGION(0x0040, "proms", 0)
	ROM_LOAD("tm188.ic7", 0x0000, 0x0040, NO_DUMP)
ROM_END

ROM_START(brailab4)
	ROM_REGION(0x5000, "maincpu", 0)
	ROM_LOAD("brl1.ic1", 0x0000, 0x1000, CRC(02323403) SHA1(3a2e853e0a39e05a04a8db58e1a76de1eda579c9))
	ROM_LOAD("brl2.ic2", 0x1000, 0x1000, CRC(36173fbc) SHA1(1c01398e16a1cbe4103e1be769347ceae873e090))
	ROM_LOAD("brl3.ic3", 0x2000, 0x1000, CRC(d3cdd108) SHA1(1a24e6c5f9c370ff6cb25045cb9d95e664467eb5))
	ROM_LOAD("brl4.ic4", 0x3000, 0x1000, CRC(d4047885) SHA1(00fe40c4c2c64a49bb429fb2b27cc7e0d0025a85))
	ROM_LOAD("brl5.rom", 0x4000, 0x1000, CRC(8a76be04) SHA1(4b683b9be23b47117901fe874072eb7aa481e4ff))

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD("hl4.ic21", 0x0000, 0x0800, CRC(f58ee39b) SHA1(49399c42d60a11b218a225856da86a9f3975a78a))

	ROM_REGION(0x0040, "proms", 0)
	ROM_LOAD("tm188.ic7", 0x0000, 0x0040, NO_DUMP)

	// these roms were found on the net, to be investigated
	ROM_REGION(0x5020, "user1", 0)
	// brl1 to 5 merged, with small changes
	// 00BF: 28 18 87 -> 30 30 0c
	// 0138: 07 0a 06 0b -> 0c 06 07 0a (keyboard assignments)
	ROM_LOAD_OPTIONAL("brl.rom", 0x0000, 0x5000, CRC(54af5d30) SHA1(d1e7b7f5866acba0503d47f610456f396526240b))
	// a small prom
	ROM_LOAD_OPTIONAL("brlcpm.rom", 0x5000, 0x0020, CRC(b936d568) SHA1(150330eccbc4b664eba4103f051d6e932038e9e8))
ROM_END

} // anonymous namespace

/* Driver */
/*   YEAR  NAME      PARENT    COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY                    FULLNAME                  FLAGS */
COMP(1982, homelab2, 0,        0,      homelab2, homelab2, homelab2_state, empty_init, "Jozsef and Endre Lukacs", "Homelab 2 / Aircomp 16", MACHINE_SUPPORTS_SAVE)
COMP(1983, homelab3, homelab2, 0,      homelab3, homelab3, homelab3_state, empty_init, "Jozsef and Endre Lukacs", "Homelab 3", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)
COMP(1984, homelab4, homelab2, 0,      homelab3, homelab3, homelab3_state, empty_init, "Jozsef and Endre Lukacs", "Homelab 4", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)
COMP(1984, brailab4, homelab2, 0,      brailab4, brailab4, homelab3_state, empty_init, "Jozsef and Endre Lukacs", "Brailab 4", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)



homez80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

Homebrew Z80 Computer by Kun-Szabo Marton

http://digitarworld.uw.hu/z80.htm

2010-10-31 Initial driver by Miodrag Milanovic

All commands must be entered in uppercase, and since the capslock
 doesn't work, you need to hold the shift key down.

There is next to no error checking, for example the T command
 is to set the time. Entering T by itself will set the time to
 99:99:99, while G will cause a jump to 9999 and so forth. Also
 the parameter must be right next to the command, spaces will cause
 invalid input. Example M1234 will display a byte of memory at 1234,
 while M 1234 will display memory at 9123.

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "emupal.h"
#include "screen.h"


namespace {

class homez80_state : public driver_device
{
public:
	homez80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_vram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
		, m_io_keyboard(*this, "X%u", 0U)
	{ }

	void homez80(machine_config &config);

private:

	u8 homez80_keyboard_r(offs_t offset);
	INTERRUPT_GEN_MEMBER(homez80_interrupt);
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void mem_map(address_map &map) ATTR_COLD;

	bool m_irq = 0;
	void machine_reset() override ATTR_COLD;
	void machine_start() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_shared_ptr<u8> m_vram;
	required_region_ptr<u8> m_p_chargen;
	required_ioport_array<16> m_io_keyboard;
};


u8 homez80_state::homez80_keyboard_r(offs_t offset)
{
	return m_io_keyboard[offset]->read();
}

void homez80_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).rom();    // 27C256 (A12,13,14 tied high)
	map(0x2000, 0x23ff).mirror(0x0c00).ram().share("videoram");   // IC13, UM61256 (A10-14 tied low)
	map(0x7000, 0x700f).mirror(0x0ff0).r(FUNC(homez80_state::homez80_keyboard_r));
	map(0x8000, 0xffff).ram();    // 61256 (32K)
}


/* Input ports */
INPUT_PORTS_START( homez80 )
	PORT_START("X0")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_START("X1") // anything on this row is treated as Shift
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE // crude, just another shift key
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("X2")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) // not working
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("X3")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_START("X4")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_START("X5")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_START("X6")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_START("X7")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_START("X8")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SCRLOCK) // nothing
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PRTSCR)  // nothing
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT) // nothing
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("X9")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PAUSE) // nothing
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("X10")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)
	PORT_START("X11")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)
	PORT_START("X12")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)
	PORT_START("X13")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') // '_' not supported
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(39) PORT_CHAR(34)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') // '|' not supported
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_START("X14")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_START("X15")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9))
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(32)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END


void homez80_state::machine_reset()
{
	m_irq = 0;
}

void homez80_state::machine_start()
{
	save_item(NAME(m_irq));
}

u32 homez80_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 sy=0,ma=0;

	for (u8 y = 0; y < 32; y++)
	{
		for (u8 ra = 0; ra < 8; ra++)
		{
			uint16_t *p = &bitmap.pix(sy++, 44);

			for (u16 x = ma; x < ma+32; x++)
			{
				u8 const chr = m_vram[x];

				/* get pattern of pixels for that character scanline */
				u8 const gfx = m_p_chargen[ (chr<<3) | ra];

				/* Display a scanline of a character (8 pixels) */
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma+=32;
	}
	return 0;
}


const gfx_layout charlayout =
{
	8, 8,               /* 8x8 characters */
	256,                /* 256 characters */
	1,                  /* 1 bits per pixel */
	{0},                /* no bitplanes; 1 bit per pixel */
	{0, 1, 2, 3, 4, 5, 6, 7},
	{0 * 8, 1 * 8, 2 * 8, 3 * 8, 4 * 8, 5 * 8, 6 * 8, 7 * 8},
	8*8                 /* size of one char */
};

static GFXDECODE_START( gfx_homez80 )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END


INTERRUPT_GEN_MEMBER(homez80_state::homez80_interrupt)
{
	device.execute().set_input_line(0, (m_irq) ? HOLD_LINE : CLEAR_LINE);
	m_irq ^= 1;
}

void homez80_state::homez80(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(8'000'000) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &homez80_state::mem_map);
	m_maincpu->set_periodic_int(FUNC(homez80_state::homez80_interrupt), attotime::from_hz(50));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(homez80_state::screen_update));
	screen.set_size(344, 32*8);
	screen.set_visarea(0, 344-1, 0, 32*8-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);
	GFXDECODE(config, "gfxdecode", "palette", gfx_homez80);
}

/* ROM definition */
ROM_START( homez80 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "sysrom.bin",  0x0000, 0x1000, CRC(37ca7545) SHA1(3f597d7e45b1ab211d5bd4a99abb21915723c357) )

	ROM_REGION(0x0800, "chargen",0)
	ROM_LOAD( "chargen.ic12", 0x0000, 0x0800, CRC(93243be3) SHA1(718efc06c131843c15383e50af23f3a5cf44dd9b) ) // 27C256, A11/12/13/14 tied low.
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY             FULLNAME                 FLAGS */
COMP( 2008, homez80, 0,      0,      homez80, homez80, homez80_state, empty_init, "Kun-Szabo Marton", "Homebrew Z80 Computer", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



horizon.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

NorthStar Horizon

2009-12-07 Skeleton driver.

http://www.hartetechnologies.com/manuals/Northstar/

The tiny bios (only about 200 bytes) initialises nothing, but only loads the
initial sector from the disk and transfers control to it. All the used memory
locations in the EA00-EB40 range are listed in the memory map. It does not
use the IO map, and has no text.

The 2MHz downgrade is suggested in the manual for the CPU board (ZPB-A). It
involves replacing the XTAL and reconnecting one jumper.

****************************************************************************/

/*

    TODO:

    - connect to S-100 bus
    - USARTs
    - parallel I/O
    - motherboard ports
    - RTC
    - RAM boards
    - floppy boards
    - floating point board
    - SOROC IQ 120 CRT terminal
    - NEC 5530-2 SPINWRITER printer
    - Anadex DP-8000 printer

*/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "bus/s100/s100.h"
#include "bus/s100/am310.h"
//#include "bus/s100/dj2db.h"
//#include "bus/s100/djdma.h"
//#include "bus/s100/mm65k16s.h"
#include "bus/s100/nsmdsa.h"
#include "bus/s100/nsmdsad.h"
#include "bus/s100/seals8k.h"
//#include "bus/s100/wunderbus.h"
#include "cpu/z80/z80.h"
#include "machine/i8251.h"
#include "softlist_dev.h"


namespace {

#define Z80_TAG         "z80"
#define I8251_L_TAG     "3a"
#define I8251_R_TAG     "4a"
#define RS232_A_TAG     "rs232a"
#define RS232_B_TAG     "rs232b"
#define S100_TAG        "s100"

class horizon_state : public driver_device
{
public:
	horizon_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, Z80_TAG)
		, m_usart_l(*this, I8251_L_TAG)
		, m_usart_r(*this, I8251_R_TAG)
		, m_s100(*this, S100_TAG)
		{ }

	void horizon(machine_config &config);
	void horizon2mhz(machine_config &config);

private:
	uint8_t ff_r();

	void horizon_io(address_map &map) ATTR_COLD;
	void horizon_mem(address_map &map) ATTR_COLD;

	virtual void machine_reset() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_device<i8251_device> m_usart_l;
	required_device<i8251_device> m_usart_r;
	required_device<s100_bus_device> m_s100;
};



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( horizon_mem )
//-------------------------------------------------

void horizon_state::horizon_mem(address_map &map)
{
	map(0x0000, 0xe7ff).ram();
	map(0xe800, 0xe9ff).rom().region("roms", 0);
	map(0xea01, 0xea01);
	map(0xea11, 0xea11);
	map(0xea21, 0xea21);
	map(0xea31, 0xea31);
	map(0xeb10, 0xeb17).r(FUNC(horizon_state::ff_r));
	map(0xeb20, 0xeb20);
	map(0xeb35, 0xeb35);
	map(0xeb40, 0xeb40);
}


//-------------------------------------------------
//  ADDRESS_MAP( horizon_io )
//-------------------------------------------------

void horizon_state::horizon_io(address_map &map)
{
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS
//-------------------------------------------------

static INPUT_PORTS_START( horizon )
INPUT_PORTS_END

void horizon_state::machine_reset()
{
	m_maincpu->set_pc(0xe800);
}

uint8_t horizon_state::ff_r()
{
	return 0xff;
}

//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END


//-------------------------------------------------
//  S100_INTERFACE( s100_intf )
//-------------------------------------------------

static void horizon_s100_cards(device_slot_interface &device)
{
	device.option_add("mdsa", S100_MDS_A);
	device.option_add("mdsad", S100_MDS_AD);
	//device.option_add("hram", S100_HRAM);
	//device.option_add("ram32a", S100_RAM32A);
	//device.option_add("ram16a", S100_RAM16A);
	//device.option_add("fpb", S100_FPB);
	device.option_add("8ksc", S100_8K_SC);
	device.option_add("8kscbb", S100_8K_SC_BB);
	device.option_add("am310", S100_AM310);
}



//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

//-------------------------------------------------
//  machine_config( horizon )
//-------------------------------------------------

void horizon_state::horizon(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, XTAL(8'000'000) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &horizon_state::horizon_mem);
	m_maincpu->set_addrmap(AS_IO, &horizon_state::horizon_io);

	// devices
	I8251(config, m_usart_l, 0);
	m_usart_l->txd_handler().set(RS232_A_TAG, FUNC(rs232_port_device::write_txd));
	m_usart_l->dtr_handler().set(RS232_A_TAG, FUNC(rs232_port_device::write_dtr));
	m_usart_l->rts_handler().set(RS232_A_TAG, FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232a(RS232_PORT(config, RS232_A_TAG, default_rs232_devices, "terminal"));
	rs232a.rxd_handler().set(m_usart_l, FUNC(i8251_device::write_rxd));
	rs232a.dsr_handler().set(m_usart_l, FUNC(i8251_device::write_dsr));
	rs232a.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	I8251(config, m_usart_r, 0);
	m_usart_r->txd_handler().set(RS232_B_TAG, FUNC(rs232_port_device::write_txd));
	m_usart_r->dtr_handler().set(RS232_B_TAG, FUNC(rs232_port_device::write_dtr));
	m_usart_r->rts_handler().set(RS232_B_TAG, FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232b(RS232_PORT(config, RS232_B_TAG, default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_usart_r, FUNC(i8251_device::write_rxd));
	rs232b.dsr_handler().set(m_usart_r, FUNC(i8251_device::write_dsr));

	// S-100
	S100_BUS(config, m_s100, XTAL(8'000'000) / 4);
	//m_s100->rdy().set_inputline(m_maincpu, Z80_INPUT_LINE_WAIT);
	//S100_SLOT(config, S100_TAG":1", horizon_s100_cards, nullptr, nullptr); // CPU
	S100_SLOT(config, "s100:2", horizon_s100_cards, nullptr); // RAM
	S100_SLOT(config, "s100:3", horizon_s100_cards, "mdsad"); // MDS
	S100_SLOT(config, "s100:4", horizon_s100_cards, nullptr); // FPB
	S100_SLOT(config, "s100:5", horizon_s100_cards, nullptr);
	S100_SLOT(config, "s100:6", horizon_s100_cards, nullptr);
	S100_SLOT(config, "s100:7", horizon_s100_cards, nullptr);
	S100_SLOT(config, "s100:8", horizon_s100_cards, nullptr);
	S100_SLOT(config, "s100:9", horizon_s100_cards, nullptr);
	S100_SLOT(config, "s100:10", horizon_s100_cards, nullptr);
	S100_SLOT(config, "s100:11", horizon_s100_cards, nullptr);
	S100_SLOT(config, "s100:12", horizon_s100_cards, nullptr);

	// software list
	SOFTWARE_LIST(config, "flop_list").set_original("horizon");
}

void horizon_state::horizon2mhz(machine_config &config)
{
	horizon(config);
	m_maincpu->set_clock(XTAL(4'000'000) / 2);
	m_s100->set_clock(XTAL(4'000'000) / 2);
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( nshrz )
//-------------------------------------------------

ROM_START( nshrz )
	ROM_REGION( 0x400, "roms", 0 )
	ROM_LOAD( "option.prom", 0x000, 0x400, NO_DUMP )
ROM_END

#define rom_nshrz2mhz rom_nshrz


//-------------------------------------------------
//  ROM( vector1 )
//-------------------------------------------------

ROM_START( vector1 ) // This one have different I/O
	ROM_REGION( 0x400, "roms", ROMREGION_ERASEFF )
	ROM_LOAD( "horizon.bin", 0x0000, 0x0100, CRC(7aafa134) SHA1(bf1552c4818f30473798af4f54e65e1957e0db48))
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME       PARENT  COMPAT  MACHINE      INPUT    CLASS          INIT        COMPANY                 FULLNAME                                FLAGS
COMP( 1976, nshrz,     0,      0,      horizon,     horizon, horizon_state, empty_init, "North Star Computers", "Horizon (North Star Computers, 4MHz)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )
COMP( 1976, nshrz2mhz, nshrz,  0,      horizon2mhz, horizon, horizon_state, empty_init, "North Star Computers", "Horizon (North Star Computers, 2MHz)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )

// This really should be in its own driver
COMP( 1979, vector1,   0,      0,      horizon,     horizon, horizon_state, empty_init, "Vector Graphic",       "Vector 1+ (DD drive)",                 MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



hp_ipc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/******************************************************************************

Integral Personal Computer (HP9807A)
Hewlett-Packard, 1985

Driver to-do list
=================

- keyboard: NMI generation
- RTC chip: proper month, day (possibly a different chip, 82167)
- HP-IL printer

QA
+ diagnstc.td0: display test
- diagnstc.td0: complete keyboard test [second connector not implemented]
+ diagnstc.td0: speaker test
- diagnstc.td0: printer test
+ diagnstc.td0: auto: floppy disc test
+ diagnstc.td0: auto: ram test
- diagnstc.td0: auto: rtc test [cannot execute]
+ diagnstc.td0: auto: short keyboard test

maybe
- drive AP line of MLC from a timer
- RTC standby interrupt?
- what does _desktop do except setting 640x400 mode?
- non-HLE keyboard and mouse? (need dumps of COP4xx)

slot devices
- 82915A -- 300/1200 bps modem; http://www.hpmuseum.net/display_item.php?hw=920
- 82919A -- serial; http://www.hpmuseum.net/display_item.php?hw=445
- 82920A -- current loop; http://www.hpmuseum.net/display_item.php?hw=975
- 82922A -- BCD interface; http://www.hpmuseum.net/display_item.php?hw=921
- 82923A -- GPIO; http://www.hpmuseum.net/display_item.php?hw=976
- 82924A -- HP-IL; http://www.hpmuseum.net/display_item.php?hw=922
- 82968A -- up to 256 KB of ROM on top of operating system PCA
- 82971A -- up to 1 MB of EPROM or 2 MB or masked ROM
- 82998A -- HP-IB; http://www.hpmuseum.net/display_item.php?hw=933
- 98071A -- 640x400 composite Video + Serial; http://www.hpmuseum.net/display_item.php?hw=935


This is a portable mains-powered UNIX workstation computer system produced by Hewlett-Packard and launched in 1985
Basic hardware specs are....
- 68000 CPU at 7.96MHz
- 9" amber electro-luminescent display with a resolution of 255*512 pixels or up to 85 characters x 31 lines (default=80*24) with
  dedicated 32Kb display memory
- Internal 3.5" floppy disk drive with the following specification....
  Encoding: Double Density HP MFM Format
  Rotational speed: 600 RPM
  Transfer speed: 62.5kB/second
  Capacity: 709Kb (709632 bytes)
  Bytes per sector: 512
  Sectors per track: 9
  Total tracks per surface: 80, Available: 77, Spare: 2, Wear: 1
  Surfaces: 2
  Interleave: 1
- HP ThinkJet ink-jet printer integrated into the top of the case. This is a modified HP2225B Thinkjet printer
- 90-key detachable keyboard
- ROM: up to 512Kb standard and an additional 512Kb of option ROM
- RAM: 512Kb, arranged as 256Kbx16. RAM is expandable externally to 7Mb
- Real Time Clock
- Speaker
- External I/O bus with two I/O ports for interfaces and memory modules. Expandable to maximum 10 ports with two 5-port Bus Expander Modules
- HP-IB (IEEE-488) bus
- Runs the HP-UX Operating System III or System V (in ROM)


PCB Layouts
===========

CPU/Memory Board (LOGIC A PCA)
----------------

HP Part# 00095-60953
                     |---------------------------------------------------------------------------------|
                     |  15.92MHz     J1        J2                                                      |
                     |LS74                                                                             |
                     |     |---|                                                                       |
                     |     |T  |                                                                       |
                     |     |M  |                                                                       |
                     |     |S  |                                                                       |
                     |     |4  |         |-------------------------|                                   |
|--------------------|     |5  |         |          68000          |                                   |
|                          |0  |         |                         |                                   |
| MB81256   MB81256        |0  |         |-------------------------|                                   |
|                          |A  |                                                                       |
| MB81256   MB81256        |---|                                                                       |
|                      |----------------J3-----------------|                                           |
| MB81256   MB81256    |                                   |                                           |
|                      |                                   |                                           |
| MB81256   MB81256    |                                   |                                           |
|                      |                                   |                                           |
| MB81256   MB81256    |                                   |                                           |
|                      |                                   |                                           |
| MB81256   MB81256    |                                   |                                           |
|                      |----------------J4-----------------|                                           |
| MB81256   MB81256                                                                                    |
|                                                                                U58            U60    |
| MB81256   MB81256                                                                      555           |
|                                                                                                      |
|                                                                                                      |
|                                                                                                      |
|      J5                                                           J6                         J7      |
|------------------------------------------------------------------------------------------------------|
Notes:
        68000 - 68000 CPU at U7. Clock input 7.96MHz [15.92/2] (DIP64)
         LS74 - 74LS74 at U2 provides system clock dividers /4 /2
      MB81256 - Fujitsu MB81256 256Kx1 DRAM. Total RAM 512Kx8/256Kx16. HP part# 1818-3308. U20-U35 (DIP16)
      TMS4500 - Texas Instruments TMS4500A DRAM Controller at U4. Clock input 3.98MHz [15.92/4] (DIP40)
          U58 - 1RD2-6001, HP-HIL Master Link Controller 'Cerberus'. Clock input on pin 24 is unknown (DIP24)
          U60 - Unknown IC used as an interrupt encoder. Possibly a logic chip? (DIP20)
          555 - 555 Timer
        J1/J2 - Connectors joining to LOGIC B PCA (logic A to logic B bus)
        J3/J4 - Connectors for ROM board
           J5 - Power input connector from power supply PCB
           J6 - 64-pin external I/O bus. This is connected to the I/O backplane PCB mounted at the rear of the case using a ribbon cable
           J7 - 10 pin connector. Labelled on schematics as LOOP0 In/Out and LOOP1 In/Out. Connected to 'Cerberus' IC. Possibly for more input devices?


Interface & Control Board (LOGIC B PCA)
-------------------------

HP Part# 00095-60952
00095-60107 BD REV A
00095-60149 ASSY LOGIC B
|---------------------------------------------------------------------------------|
|SPEAKER                        NS58167A               J11       J10              |
|            LM358   32.768kHz                                                    |
|                                                                                 |
|                                                      16Kx4  16Kx4  16Kx4  16Kx4 |
|                 ROM                                                             |
|                                                                                 |
|   BT1                       HP-IL(2)       GPU                                  |
|                                                                                 |--------------------|
|                 1Kb                                                                                  |
|  COP452                                                                                              |
|                                                                                                      |
|                                                                                                      |
|                                                                              WD2797                  |
| LM393          HP-IL(1)                                                                              |
|                                                                                                      |
|                                           LS193                                                      |
|                                                                                                      |
|                                                                                                      |
|                                                                                                      |
|                                                  LS74    24MHz      LS161                            |
|                                                                                                    J2|
|                                                                                                      |
|                                                            TMS9914                                   |
|                                                                                                      |
|                                                                                                      |
|                                                              75162   75160                           |
|                                                                                                      |
|  J14  J6     J5     J4     J3                  J7             J1         J8        J9        J12     |
|------------------------------------------------------------------------------------------------------|
Notes:
         GPU - 1LL3-0005 GPU (Graphics Processor) at U1. Clock input 3MHz [24/8]. VSync on pin 45, HSync on pin 46 (DIP48)
       16Kx4 - 16Kx4 DRAM, organised as 32Kx8bit/16Kx16bit at U3, U4, U5 & U6. Chip type unknown, likely Fujitsu MB81416 (DIP18)
      WD2797 - Western Digital WD2797 Floppy Disk Controller at U18. Clock input 2MHz [24/2/3/2] (DIP40)
    HP-IL(1) - HP-IL 1LJ7-0015 'Saturn' Thinkjet Printer Controller IC at U28. Clock input 3MHz on pin 9 [24/8] (DIP48)
    HP-IL(2) - HP-IL 1LB3 Interface IC at U31. Clock input 2MHz on pin 22 [24/2/3/2] (DIP28)
         1Kb - 1Kb RAM at U27 for printer buffer (DIP28, type unknown, very old, with only DATA0,1,2,3, C/D and DIN,DOUT)
         ROM - 16Kb (32Kx4) Some kind of very early DIP28 PROM/ROM? Same pinout as 1Kb RAM above. Holds the character font table for the printer
               Four versions of this ROM exist, one each for Japan/Arabic/Hebrew and one for all other regions
    NS58167A - National Semiconductor NS58167A Clock Controller RTC at U44. Clock input 32.768kHz (DIP24)
               (Tony Duell's schematics show a 82167 instead)
       LM358 - National Semiconductor LM358 Operational Amplifier at U40 (DIP8)
       LM393 - Texas Instruments LM393 Dual Comparator at U34 (DIP8)
         BT1 - 3v lithium battery
      COP452 - National Semiconductor COP452 Speaker Controller at U39. Clock input 2MHz [24/2/3/2]. This IC provides programmable square wave output from 100Hz to 5000Hz (DIP14)
     TMS9914 - Texas Instruments TMS9914 General Purpose Interface Bus Adapter at U41. Clock input 4MHz [24/2/3] (DIP40)
       75160 - National Semiconductor DS75160A IEEE-488 General Purpose Interface Bus Transceiver at U42 (DIP20)
       75162 - National Semiconductor DS75162A IEEE-488 General Purpose Interface Bus Transceiver at U43 (DIP22)
       LS193 - 74LS193 at U9 provides the main system clock dividers /8 /4 /2 (24MHz -> 12MHz, 6MHz, 3MHz). This is also the source of the 6MHz video clock on connector J1
       LS161 - 74LS161 at U46. Takes 12MHz clock input on pin 2 and outputs 4MHz on pin 13 (i.e. /3). This is the clock source for the TMS9914
        LS74 - LS74 at U16. Takes output of 4MHz from LS161 at U46 and outputs 2MHz on pin 5 (i.e. /2). This is the source of the 2MHz clocks
          J1 - Connector joining to video display assembly
          J2 - Connector joining to floppy drive
          J3 - Connector joining to printer front switch panel assembly (ADV/CONT/FF/ATTN)
          J4 - Connector joining to printer out of paper switch connected to ATTN
          J5 - Connector joining to printer carriage motor
          J6 - Connector joining to printer mechanism assembly including paper motor
          J7 - Connector joining to printer printhead
          J8 - Connector joining to HO?E switch (? ~ M, text unreadable on schems)
          J9 - Connector for ?
     J10/J11 - Connectors joining to LOGIC A PCA (logic A to logic B bus)
         J12 - Power input connector from power supply PCB
         J14 - Fan connector


ROM board (Operating System ROM PCA. Assembly# HP82991A or HP82995A)
---------

|----------------J3-----------------|
|                                   |
|                                   |
|                                   |
|J1  U1      U2      U3     U4    J2|
|                                   |
|                                   |
|                                   |
|----------------J4-----------------|
Notes:
      J1/J2 - 20 pin connector joining to 'Option ROM PCA'
      J3/J4 - 20 pin connector joining to 'LOGIC A PCA'
      U1-U4 - 28 pin EPROM/mask ROM 0L/1L/0H/1H (note 1Mbit: 128Kx8 28 pin)


ROM board (Option ROM PCA)
---------
Note this PCB plugs in upside-down on top of the Operating System ROM PCB

|----------------J4-----------------|
|                                   |
|                                   |
|                                   |
|J1  U1      U2      U3     U4    J2|
|                                   |
|                                   |
|                                   |
|----------------J3-----------------|
Notes:
      J1/J2 - 20 pin connector joining to 'Operating System ROM PCA'
      J3/J4 - 20 pin connector joining to 'LOGIC A PCA'
      U1-U4 - 28 pin EPROM/mask ROM 0L/1L/0H/1H (note 1Mbit: 128Kx8 28 pin)


Physical Memory Map
===================
000000-07FFFF - Internal ROM (Operating System PCA 512Kb)
080000-0FFFFF - Internal ROM (Option ROM PCA 512Kb)
100000-4FFFFF - External ROM modules (up to 4Mb)
500000-5FFFFF - Reserved 1Mb
600000-6FFFFF - Internal I/O 1Mb

                Address        Port  Use
                -------------------------
                600000-60FFFF   0    MMU
                610000-61FFFF   1    Disk Drive
                620000-62FFFF   2    Display
                630000-63FFFF   3    HP-IB
                640000-64FFFF   4    RTC
                650000-65FFFF   5    Printer
                660000-66FFFF   6    Keyboard
                670000-67FFFF   7    Speaker
                680000-68FFFF   8    Reserved
                690000-69FFFF   9    Reserved
                6A0000-6AFFFF   10   Reserved
                6B0000-6BFFFF   11   Reserved
                6C0000-6CFFFF   12   Reserved
                6D0000-6DFFFF   13   Reserved
                6E0000-6EFFFF   14   Reserved
                6F0000-6FFFFF   15   Reserved

700000-7FFFFF - External I/O 1Mb

                Address        Port  Use
                -------------------------
                700000-70FFFF   16   Mainframe Port A
                710000-71FFFF   17   Mainframe Port B
                720000-72FFFF   18   Bus Expander Port A1
                730000-73FFFF   19   Bus Expander Port A2
                740000-74FFFF   20   Bus Expander Port A3
                750000-75FFFF   21   Bus Expander Port A4
                760000-76FFFF   22   Bus Expander Port A5
                770000-77FFFF   23   Reserved
                780000-78FFFF   24   Reserved
                790000-79FFFF   25   Reserved
                7A0000-7AFFFF   26   Bus Expander Port B1
                7B0000-7BFFFF   27   Bus Expander Port B2
                7C0000-7CFFFF   28   Bus Expander Port B3
                7D0000-7DFFFF   29   Bus Expander Port B4
                7E0000-7EFFFF   30   Bus Expander Port B5
                7F0000-7FFFFF   31   Reserved

800000-EFFFFF - External RAM modules (up to 7Mb)
F00000-F7FFFF - Internal RAM 512Kb
F80000-FFFFFF - Reserved 512Kb

Access to 800000-FFFFFF can be remapped by the MMU registers.


Interrupts (all autovectored)
----------
High Priority 7 - Soft reset from keyboard (NMI)
     /\       6 - RTC or NBIR3 (external I/O)
     ||       5 - Disc Drive or NBIR2 (external I/O)
     ||       4 - GPU or NBIR1 (external I/O)
     ||       3 - HP-IB, printer or NBIR0 (external I/O)
     \/       2 - HP-HIL devices (keyboard/mouse)
Low Priority  1 - RTC

Note external interrupt lines NBIR0 to NBIR3 can be asserted by an interface connected to the external I/O port


Useful links etc.
-----------------

bitsavers://pdf/hp/integral/00095-90126_Integral_Personal_Computer_Service_Jan86.pdf

bitsavers://pdf/hp/hp-hil/45918A-90001_HP-HIL_Technical_Reference_Manual_Jan86.pdf
    HP-HIL MLC, SLC datasheets

bitsavers://pdf/sony/floppy/Sony_OA-D32_Microfloppy_Service_Nov83.pdf
    OA-D32W

http://www.hpl.hp.com/hpjournal/pdfs/IssuePDFs/1983-01.pdf
    HP-IL issue

http://www.hpl.hp.com/hpjournal/pdfs/IssuePDFs/1985-10.pdf
    IPC issue

http://www.hpl.hp.com/hpjournal/pdfs/IssuePDFs/1987-06.pdf
    HP-HIL article

http://www.hpmuseum.net/pdf/ComputerNews_1985_Jan15_37pages_OCR.pdf
    introducing the IPC

http://www.hpmuseum.net/pdf/ComputerFocus_1985_Nov_25pages_OCR.pdf
    SysV upgrade

http://www.hpmuseum.net/pdf/InformationSystemsAndManufacturingNews_81pages_Jun1-86_OCR.pdf
    EPROM/ROM modules

http://www.hpmuseum.net/pdf/HPChannels_1986_11_37pages_Nov86_OCR.pdf
    SW Eng ROM and serial option for it

http://www.coho.org/~pete/downloads/IPC/burst/Freeware/IPC_Driver_Writers_Disc/hp-ux.5.0.0
    kernel namelist

http://www.hpmuseum.net/display_item.php?hw=122
    overview, manuals, software

http://www.ambry.com/hp-computer-model/9807A.html
    replacement parts

http://www.brouhaha.com/~eric/hpcalc/chips/
    chip part numbers


Software to look for
--------------------

00095-60978 "Service ROM - Used in troubleshooting the integral PC" via ambry
00095-60925 "Service ROM" via service manual
00095-60969 "Service Diagnostic Disc" via service manual
00095-60950 "I/O Component-Level Diagnostic Disc" via serial interface service manual

00095-60006 (original System III-based HP-UX 1.0 ROM)
82995A (same bits as 82991A; the latter is an upgrade kit, former was pre-installed)
82989J Technical Basic ROM (Jan'1986)
82987A Software Engineering ROM (Nov'1986)

******************************************************************************/

#include "emu.h"
#include "hp_ipc_optrom.h"

#include "bus/hp_hil/hil_devices.h"
#include "bus/hp_hil/hp_hil.h"
#include "bus/hp_ipc_io/hp_ipc_io.h"
#include "bus/ieee488/ieee488.h"
#include "cpu/m68000/m68000.h"
#include "imagedev/floppy.h"
#include "machine/bankdev.h"
#include "machine/cop452.h"
#include "machine/input_merger.h"
#include "machine/mm58167.h"
#include "machine/ram.h"
#include "machine/tms9914.h"
#include "machine/wd_fdc.h"
#include "softlist_dev.h"
#include "sound/dac.h"
#include "video/hp1ll3.h"

#include "formats/hp_ipc_dsk.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class hp_ipc_state : public driver_device
{
public:
	hp_ipc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_bankdev(*this, "bankdev")
		, m_fdc(*this, "fdc")
		, m_ram(*this, RAM_TAG)
		, m_gpu(*this , "gpu")
		, m_screen(*this, "screen")
		, m_spkr(*this , "spkr")
		, m_dac(*this , "dac")
		, m_irq3_merger(*this , "merge_irq3")
		, m_irq4_merger(*this , "merge_irq4")
		, m_irq5_merger(*this , "merge_irq5")
		, m_irq6_merger(*this , "merge_irq6")
		, m_io_slot_a(*this , "slot_a")
		, m_io_slot_b(*this , "slot_b")
		, m_rom_slots(*this , "rom%u" , 0)
	{ }

	void hp_ipc_base(machine_config &config);
	void hp_ipc(machine_config &config);
	void hp9808a(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	uint16_t mem_r(offs_t offset, uint16_t mem_mask);
	void mem_w(offs_t offset, uint16_t data, uint16_t mem_mask);
	uint16_t mmu_r(offs_t offset);
	void mmu_w(offs_t offset, uint16_t data);
	uint16_t ram_r(offs_t offset, uint16_t mem_mask);
	void ram_w(offs_t offset, uint16_t data, uint16_t mem_mask);
	void spkr_w(offs_t offset, uint16_t data);

	uint8_t floppy_id_r();
	void floppy_id_w(uint8_t data);
	static void floppy_formats(format_registration &fr);

	void irq_1(int state);
	void irq_2(int state);
	void irq_3(int state);
	[[maybe_unused]] void irq_4(int state);
	void irq_5(int state);
	[[maybe_unused]] void irq_6(int state);
	void irq_7(int state);

	void hp_ipc_mem_inner_base(address_map &map) ATTR_COLD;
	void hp_ipc_mem_inner_9807a(address_map &map) ATTR_COLD;
	void hp_ipc_mem_outer(address_map &map) ATTR_COLD;

	void hp_ipc_mem_inner_9808a(address_map &map) ATTR_COLD;

	required_device<m68000_device> m_maincpu;
	required_device<address_map_bank_device> m_bankdev;
	required_device<wd2797_device> m_fdc;
	required_device<ram_device> m_ram;
	required_device<hp1ll3_device> m_gpu;
	required_device<screen_device> m_screen;
	required_device<cop452_device> m_spkr;
	required_device<dac_1bit_device> m_dac;
	required_device<input_merger_any_high_device> m_irq3_merger;
	required_device<input_merger_any_high_device> m_irq4_merger;
	required_device<input_merger_any_high_device> m_irq5_merger;
	required_device<input_merger_any_high_device> m_irq6_merger;
	required_device<hp_ipc_io_slot_device> m_io_slot_a;
	required_device<hp_ipc_io_slot_device> m_io_slot_b;
	required_device_array<hp_ipc_optrom_device , 2> m_rom_slots;

	uint32_t m_mmu[4]{}, m_lowest_ram_addr = 0;
	uint16_t *m_internal_ram = nullptr;
	int m_fc = 0;

	floppy_image_device *m_floppy = nullptr;

	inline uint32_t get_ram_address(offs_t offset)
	{
		return (m_mmu[(m_maincpu->get_fc() >> 1) & 3] + offset) & 0x3FFFFF;
	}
};

void hp_ipc_state::hp_ipc_mem_outer(address_map &map)
{
	map(0x000000, 0xFFFFFF).rw(FUNC(hp_ipc_state::mem_r), FUNC(hp_ipc_state::mem_w));
}

void hp_ipc_state::hp_ipc_mem_inner_base(address_map &map)
{
// bus error handler
	map(0x0000000, 0x1FFFFFF).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));

// user mode
	map(0x1000000, 0x17FFFFF).rw(FUNC(hp_ipc_state::ram_r), FUNC(hp_ipc_state::ram_w));
	map(0x1800000, 0x187FFFF).rom().region("maincpu", 0);
	map(0x1E00000, 0x1E0FFFF).rw(FUNC(hp_ipc_state::mmu_r), FUNC(hp_ipc_state::mmu_w));
	map(0x1E20000, 0x1E2000F).rw(m_gpu, FUNC(hp1ll3_device::read), FUNC(hp1ll3_device::write)).umask16(0x00ff);
	map(0x1E40000, 0x1E4002F).rw("rtc", FUNC(mm58167_device::read), FUNC(mm58167_device::write)).umask16(0x00ff);

// supervisor mode
	map(0x0000000, 0x007FFFF).rom().region("maincpu", 0);       // Internal ROM (operating system PCA)
	map(0x0080000, 0x00FFFFF).unmaprw();     // Internal ROM (option ROM PCA)
	map(0x0100000, 0x04FFFFF).unmaprw();     // External ROM modules
	map(0x0600000, 0x060FFFF).rw(FUNC(hp_ipc_state::mmu_r), FUNC(hp_ipc_state::mmu_w));
	map(0x0610000, 0x0610007).rw(FUNC(hp_ipc_state::floppy_id_r), FUNC(hp_ipc_state::floppy_id_w)).umask16(0x00ff);
	map(0x0610008, 0x061000F).rw(m_fdc, FUNC(wd2797_device::read), FUNC(wd2797_device::write)).umask16(0x00ff);
	map(0x0620000, 0x0620007).rw(m_gpu, FUNC(hp1ll3_device::read), FUNC(hp1ll3_device::write)).umask16(0x00ff);
	map(0x0630000, 0x063FFFF).mask(0xf).rw("hpib" , FUNC(tms9914_device::read) , FUNC(tms9914_device::write)).umask16(0x00ff);
	map(0x0640000, 0x064002F).rw("rtc", FUNC(mm58167_device::read), FUNC(mm58167_device::write)).umask16(0x00ff);
	map(0x0660000, 0x06600FF).rw("mlc", FUNC(hp_hil_mlc_device::read), FUNC(hp_hil_mlc_device::write)).umask16(0x00ff);  // 'caravan', scrn/caravan.h
	map(0x0670000, 0x067FFFF).w(FUNC(hp_ipc_state::spkr_w));    // Speaker (NatSemi COP 452)
	map(0x0670000, 0x067FFFF).nopr();   // Speaker (NatSemi COP 452)
	map(0x0680000, 0x068FFFF).noprw();       // 'SIMON (98628) fast HP-IB card' -- sys/simon.h
	map(0x0700000, 0x07FFFFF).unmaprw();     // External I/O
	map(0x0800000, 0x0FFFFFF).rw(FUNC(hp_ipc_state::ram_r), FUNC(hp_ipc_state::ram_w));
}

void hp_ipc_state::hp_ipc_mem_inner_9807a(address_map &map)
{
	hp_ipc_mem_inner_base(map);
	map(0x0650000, 0x065FFFF).noprw();       // HP-IL Printer (optional; ROM sets _desktop to 0 if not mapped) -- sys/lpint.h
}

void hp_ipc_state::hp_ipc_mem_inner_9808a(address_map &map)
{
	hp_ipc_mem_inner_base(map);
}

static INPUT_PORTS_START(hp_ipc)
INPUT_PORTS_END


uint16_t hp_ipc_state::mmu_r(offs_t offset)
{
	uint16_t data = (m_mmu[offset & 3] >> 10);

	return data;
}

void hp_ipc_state::mmu_w(offs_t offset, uint16_t data)
{
	m_mmu[offset & 3] = (data & 0xFFF) << 10;
}

uint16_t hp_ipc_state::mem_r(offs_t offset, uint16_t mem_mask)
{
	int fc = m_maincpu->get_fc() & 4;

	if (fc != m_fc)
	{
		m_fc = fc;
		m_bankdev->set_bank(m_fc ? 0 : 1);
	}

	return m_bankdev->read16(offset, mem_mask);
}

void hp_ipc_state::mem_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	int fc = m_maincpu->get_fc() & 4;

	if (fc != m_fc)
	{
		m_fc = fc;
		m_bankdev->set_bank(m_fc ? 0 : 1);
	}

	m_bankdev->write16(offset, data, mem_mask);
}

void hp_ipc_state::spkr_w(offs_t offset, uint16_t data)
{
	m_spkr->cs_w(!BIT(data , 0));
	m_spkr->sk_w(BIT(data , 1));
	m_spkr->di_w(BIT(data , 2));
}

uint16_t hp_ipc_state::ram_r(offs_t offset, uint16_t mem_mask)
{
	uint32_t ram_address = get_ram_address(offset);
	uint16_t data = 0xffff;

	if (ram_address < m_lowest_ram_addr)
	{
		if (!machine().side_effects_disabled())
			m_maincpu->trigger_bus_error();
	}
	else if (ram_address < 0x3c0000)
	{
		// Internal RAM
		data = m_internal_ram[ram_address - m_lowest_ram_addr];
	}

	return data;
}

void hp_ipc_state::ram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	uint32_t ram_address = get_ram_address(offset);

	if (ram_address < m_lowest_ram_addr)
	{
		m_maincpu->trigger_bus_error();
	}
	else if (ram_address < 0x3c0000)
	{
		// Internal RAM
		COMBINE_DATA(&m_internal_ram[ram_address - m_lowest_ram_addr]);
	}
}


/*
 * bit 6 -- INTRQ
 * bit 1 -- disk changed (from drive)
 * bit 0 -- write protect (from drive)
 */
uint8_t hp_ipc_state::floppy_id_r()
{
	uint8_t data = 0;

	// TODO: fix sys controller switch
	data = (m_fdc->intrq_r() << 6) | 0x80;

	if (m_floppy)
	{
		data |= (m_fdc->intrq_r() << 6) | (m_floppy->dskchg_r() << 1) | m_floppy->wpt_r();
	}

	return data;
}

/*
 * bit 7 -- 1: motor on (via inverter to drive's /MTorOn)
 * bit 3 -- 1: head 0, 0: head 1 (via inverter to drive's /SSO)
 * bit 1 -- 1: drive select (via inverter to drive's /DRIVE SEL 1)
 * bit 0 -- 1: reset disc_changed (via inverter to drive's /DSKRST)
 */
void hp_ipc_state::floppy_id_w(uint8_t data)
{
	floppy_image_device *floppy0 = m_fdc->subdevice<floppy_connector>("0")->get_device();

	if (!BIT(data, 1))
	{
		if (m_floppy == nullptr)
		{
			m_floppy = floppy0;
			m_fdc->set_floppy(m_floppy);
		}
	}
	else
	{
		m_floppy = nullptr;
	}

	if (m_floppy)
	{
		m_floppy->ss_w(BIT(data, 3));
		m_floppy->mon_w(!BIT(data, 7));
		if (BIT(data, 0))
			m_floppy->dskchg_w(0);
	}
	else
	{
		floppy0->mon_w(1);
	}
}


void hp_ipc_state::irq_1(int state)
{
	m_maincpu->set_input_line(M68K_IRQ_1, state);
}

void hp_ipc_state::irq_2(int state)
{
	m_maincpu->set_input_line(M68K_IRQ_2, state);
}

void hp_ipc_state::irq_3(int state)
{
	m_irq3_merger->in_w<0>(state);
}

void hp_ipc_state::irq_4(int state)
{
	m_irq4_merger->in_w<0>(state);
}

void hp_ipc_state::irq_5(int state)
{
	m_irq5_merger->in_w<0>(state);
}

void hp_ipc_state::irq_6(int state)
{
	m_irq6_merger->in_w<0>(state);
}

void hp_ipc_state::irq_7(int state)
{
	m_maincpu->set_input_line(M68K_IRQ_7, state);
}


void hp_ipc_state::machine_start()
{
	m_bankdev->set_bank(1);

	m_lowest_ram_addr = 0x3c0000 - (m_ram->size() >> 1);
	m_internal_ram = (uint16_t *)m_ram->pointer();

	m_io_slot_a->install_read_write_handlers(m_bankdev->space());
	m_io_slot_b->install_read_write_handlers(m_bankdev->space());
}

void hp_ipc_state::machine_reset()
{
	m_floppy = nullptr;
	m_spkr->cs_w(1);
	m_spkr->sk_w(0);
	m_spkr->di_w(0);

	// Load optional ROMs (if any)
	// But first unload anything in opt ROM range
	m_bankdev->space().unmap_read(0x100000 , 0x4fffff);
	m_bankdev->space().unmap_read(0x100000 + 0x1800000, 0x4fffff + 0x1800000);

	for (auto& slot : m_rom_slots) {
		slot->install_read_handler(m_bankdev->space());
	}
}


void hp_ipc_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_HP_IPC_FORMAT);
}

static void hp_ipc_floppies(device_slot_interface &device)
{
	device.option_add("35dd", SONY_OA_D32W);
}

/*
 *  IRQ levels (page 5-4)
 *
 *  7   Soft reset from keyboard, non-maskable
 *  6   Real-time clock or NBIR3 (ext. I/O)
 *  5   Disc Drive or NBIR2
 *  4   GPU or NBIR1
 *  3   HP-IB, printer, or NBIR0
 *  2   HP-HIL devices (keyboard, mouse)
 *  1   Real-time clock
 */
void hp_ipc_state::hp_ipc_base(machine_config &config)
{
	M68000(config, m_maincpu, 15.92_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &hp_ipc_state::hp_ipc_mem_outer);

	HP1LL3(config , m_gpu , 24_MHz_XTAL / 8).set_screen("screen");

	// FIXME: actual clock is 1MHz; remove this workaround (and change 2000 to 1000 in hp_ipc_dsk.cpp)
	// when floppy code correctly handles 600 rpm drives.
	WD2797(config, m_fdc, 2_MHz_XTAL);
	m_fdc->intrq_wr_callback().set(FUNC(hp_ipc_state::irq_5));
	FLOPPY_CONNECTOR(config, "fdc:0", hp_ipc_floppies, "35dd", hp_ipc_state::floppy_formats);

	SOFTWARE_LIST(config, "flop_list").set_original("hp_ipc");
	SOFTWARE_LIST(config, "rom_list").set_original("hp_ipc_rom");

	mm58167_device &rtc(MM58167(config, "rtc", 32.768_kHz_XTAL));
	rtc.irq().set(FUNC(hp_ipc_state::irq_1));
//  rtc.standby_irq().set(FUNC(hp_ipc_state::irq_6));

	hp_hil_mlc_device &mlc(HP_HIL_MLC(config, "mlc", XTAL(15'920'000)/2));
	mlc.int_callback().set(FUNC(hp_ipc_state::irq_2));
	mlc.nmi_callback().set(FUNC(hp_ipc_state::irq_7));

	HP_HIL_SLOT(config, "hil1", "mlc", hp_hil_devices, "hp_ipc_kbd");
	HP_HIL_SLOT(config, "hil2", "mlc", hp_hil_devices, "hp_46060b");

	tms9914_device &hpib(TMS9914(config, "hpib", 4_MHz_XTAL));
	hpib.int_write_cb().set(FUNC(hp_ipc_state::irq_3));
	hpib.dio_read_cb().set(IEEE488_TAG, FUNC(ieee488_device::dio_r));
	hpib.dio_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_dio_w));
	hpib.eoi_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_eoi_w));
	hpib.dav_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_dav_w));
	hpib.nrfd_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_nrfd_w));
	hpib.ndac_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ndac_w));
	hpib.ifc_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ifc_w));
	hpib.srq_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_srq_w));
	hpib.atn_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_atn_w));
	hpib.ren_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ren_w));

	ieee488_device &ieee(IEEE488(config, IEEE488_TAG));
	ieee.eoi_callback().set("hpib" , FUNC(tms9914_device::eoi_w));
	ieee.dav_callback().set("hpib" , FUNC(tms9914_device::dav_w));
	ieee.nrfd_callback().set("hpib" , FUNC(tms9914_device::nrfd_w));
	ieee.ndac_callback().set("hpib" , FUNC(tms9914_device::ndac_w));
	ieee.ifc_callback().set("hpib" , FUNC(tms9914_device::ifc_w));
	ieee.srq_callback().set("hpib" , FUNC(tms9914_device::srq_w));
	ieee.atn_callback().set("hpib" , FUNC(tms9914_device::atn_w));
	ieee.ren_callback().set("hpib" , FUNC(tms9914_device::ren_w));
	IEEE488_SLOT(config , "ieee_rem" , 0 , remote488_devices , nullptr);

	// Beeper
	COP452(config , m_spkr , 2_MHz_XTAL);
	SPEAKER(config, "mono").front_center();
	DAC_1BIT(config, m_dac , 0).add_route(ALL_OUTPUTS, "mono", 0.5, 0);
	m_spkr->oa_w().set(m_dac , FUNC(dac_1bit_device::write));

	// IO slots
	HP_IPC_IO_SLOT(config , m_io_slot_a).set_slot_idx(0);
	HP_IPC_IO_SLOT(config , m_io_slot_b).set_slot_idx(1);

	// IRQ3/4/5/6 mergers
	INPUT_MERGER_ANY_HIGH(config , m_irq3_merger).output_handler().set_inputline(m_maincpu , M68K_IRQ_3);
	INPUT_MERGER_ANY_HIGH(config , m_irq4_merger).output_handler().set_inputline(m_maincpu , M68K_IRQ_4);
	INPUT_MERGER_ANY_HIGH(config , m_irq5_merger).output_handler().set_inputline(m_maincpu , M68K_IRQ_5);
	INPUT_MERGER_ANY_HIGH(config , m_irq6_merger).output_handler().set_inputline(m_maincpu , M68K_IRQ_6);
	m_io_slot_a->irq3_cb().set(m_irq3_merger , FUNC(input_merger_any_high_device::in_w<1>));
	m_io_slot_a->irq4_cb().set(m_irq4_merger , FUNC(input_merger_any_high_device::in_w<1>));
	m_io_slot_a->irq5_cb().set(m_irq5_merger , FUNC(input_merger_any_high_device::in_w<1>));
	m_io_slot_a->irq6_cb().set(m_irq6_merger , FUNC(input_merger_any_high_device::in_w<1>));
	m_io_slot_b->irq3_cb().set(m_irq3_merger , FUNC(input_merger_any_high_device::in_w<2>));
	m_io_slot_b->irq4_cb().set(m_irq4_merger , FUNC(input_merger_any_high_device::in_w<2>));
	m_io_slot_b->irq5_cb().set(m_irq5_merger , FUNC(input_merger_any_high_device::in_w<2>));
	m_io_slot_b->irq6_cb().set(m_irq6_merger , FUNC(input_merger_any_high_device::in_w<2>));

	// Optional ROMs
	for (auto& finder : m_rom_slots) {
		HP_IPC_OPTROM(config, finder);
	}

	RAM(config, RAM_TAG).set_default_size("512K").set_extra_options("768K,1M,1576K,2M,3M,4M,5M,6M,7M,7680K");
}

void hp_ipc_state::hp_ipc(machine_config &config)
{
	hp_ipc_base(config);

	ADDRESS_MAP_BANK(config, "bankdev").set_map(&hp_ipc_state::hp_ipc_mem_inner_9807a).set_options(ENDIANNESS_BIG, 16, 25, 0x1000000);

	// 16kw/32kb video RAM
	m_gpu->set_vram_size(16);

	// horizontal time = 60 us (min)
	// ver.refresh period = ~300 us
	// ver.period = 16.7ms (~60 hz)
	SCREEN(config , m_screen , SCREEN_TYPE_LCD); // actually an EL display
	m_screen->set_color(rgb_t::amber());
	m_screen->set_screen_update("gpu" , FUNC(hp1ll3_device::screen_update));
	m_screen->set_raw(6_MHz_XTAL * 2 , 720 , 0 , 512 , 261 , 0 , 255);
	m_screen->screen_vblank().set("mlc", FUNC(hp_hil_mlc_device::ap_w)); // this signal is actually driven by a 555 (U59)
	m_screen->set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);
}

void hp_ipc_state::hp9808a(machine_config &config)
{
	hp_ipc_base(config);

	ADDRESS_MAP_BANK(config, "bankdev").set_map(&hp_ipc_state::hp_ipc_mem_inner_9808a).set_options(ENDIANNESS_BIG, 16, 25, 0x1000000);

	// 64kw/128kb video RAM
	m_gpu->set_vram_size(64);

	SCREEN(config , m_screen , SCREEN_TYPE_LCD);
	m_screen->set_color(rgb_t::amber()); // actually a kind of EL display
	m_screen->set_screen_update("gpu" , FUNC(hp1ll3_device::screen_update));
	m_screen->set_raw(6_MHz_XTAL * 2 , 880 , 0 , 640 , 425 , 0 , 400);
	m_screen->screen_vblank().set("mlc", FUNC(hp_hil_mlc_device::ap_w));
	m_screen->set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);
}


ROM_START(hp_ipc)
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_DEFAULT_BIOS("v50")

	ROM_SYSTEM_BIOS(0, "v10", "HP-UX 1.0")
	ROMX_LOAD("00095-60006.bin", 0x00000, 0x80000, NO_DUMP, ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "v50", "HP-UX 5.0")
	// Should be spread across 4 x 128K ROMs
	ROMX_LOAD("hp ipc os 82991a.bin", 0x00000, 0x80000, BAD_DUMP CRC(df45a37b) SHA1(476af9923bca0d2d0f40aeb81be5145ca76fddf5), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "diaga", "Diag ROM A")
	ROMX_LOAD("boarda_u1.bin", 0x00001, 0x4000, CRC(f40e1434) SHA1(a4f633f3e0971ba3ff218c6d6f777b8253bade8c), ROM_BIOS(2) | ROM_SKIP(1))
	ROMX_LOAD("boarda_u2.bin", 0x00000, 0x4000, CRC(80f6eb08) SHA1(ea943f72d37b43b5ba06b8e6f11824601109497c), ROM_BIOS(2) | ROM_SKIP(1))
	ROMX_LOAD("boarda_u3.bin", 0x40001, 0x4000, CRC(0f2fd4d5) SHA1(9ed41fca947d58d7f4159336421601963e3cae17), ROM_BIOS(2) | ROM_SKIP(1))
	ROMX_LOAD("boarda_u4.bin", 0x40000, 0x4000, CRC(d6741772) SHA1(1574a52d6658f9a7beace6572bea11ee923fc1bf), ROM_BIOS(2) | ROM_SKIP(1))

	ROM_SYSTEM_BIOS(3, "diagb", "Diag ROM B")
	ROMX_LOAD("boardb_u1.bin", 0x00001, 0x4000, CRC(597777d9) SHA1(24671499e4685f306b1b37a071adc0634f9f1e4e), ROM_BIOS(3) | ROM_SKIP(1))
	ROMX_LOAD("boardb_u2.bin", 0x00000, 0x4000, CRC(bc2ada19) SHA1(efebab335ca8a57e77b1076050e861a3a0eb09fc), ROM_BIOS(3) | ROM_SKIP(1))
	ROMX_LOAD("boardb_u3.bin", 0x40001, 0x2000, CRC(c227622a) SHA1(99747945dc6efaa45ce8d7d3c58830dbe46b651a), ROM_BIOS(3) | ROM_SKIP(1))
	ROMX_LOAD("boardb_u4.bin", 0x40000, 0x2000, CRC(d99efe90) SHA1(345437007f8d728ceafc5cd97414ef94ce38e363), ROM_BIOS(3) | ROM_SKIP(1))
ROM_END

#define rom_hp9808a rom_hp_ipc

} // Anonymous namespace


//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY            FULLNAME                            FLAGS
COMP( 1985, hp_ipc,  0,      0,      hp_ipc,  hp_ipc, hp_ipc_state, empty_init, "Hewlett-Packard", "Integral Personal Computer 9807A", 0)
COMP( 1985, hp9808a, 0,      0,      hp9808a, hp_ipc, hp_ipc_state, empty_init, "Hewlett-Packard", "Integral Personal Computer 9808A", MACHINE_NOT_WORKING)



hp16500.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

  Hewlett-Packard HP16500a/b/c Logic Analyzer

  These are weird, the "a" has more in common with the older 1650/1651
  than the 16500b.

  16500a rev 00.00:
    MC68000 @ 10 MHz
    MC68A45 CRTC
    FDC9793 floppy controller (WD1793 type)
    TMS9914A GPIB bus interface
    SCN2661 EPCI

  IRQ1 = VBL, IRQ2 = 20b007, IRQ3 = ?, IRQ4 = 20d000, IRQ5 = 20d007,
  IRQ6 = ?, IRQ7 = ?

  16500a rev 00.02:
    MC68000 @ 10 MHz
    MC68A45 CRTC
    Z0765A08PSC floppy controller (NEC765 type)
    TMS9914A GPIB bus interface
    SCN2661 EPCI

  16500b:
    MC68EC030 @ 25 MHz

    WD37C65C floppy controller (NEC765 type)
    Bt475 video RAMDAC
    TMS9914A GPIB bus interface
    Dallas DS1286 RTC/CMOS RAM

    IRQ 1 = VBL
    IRQ 2 = 35b8
    IRQ 3 = 35ce (jump 840120)
    IRQ 4 = 17768
    IRQ 5 = 814a
    IRQ 6 = 35c8 (jump 840120)
    IRQ 7 = 35d4 (jump 840120)

  16500c:
    Same as 16500b, but add:
    53C720 SCSI controller
    DP83934 SONIC ethernet

****************************************************************************/

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "cpu/m68000/m68030.h"
#include "machine/ds1386.h"
#include "machine/scn_pci.h"
#include "bus/hp_hil/hp_hil.h"
#include "bus/hp_hil/hil_devices.h"
#include "video/mc6845.h"
#include "screen.h"
#include "speaker.h"


namespace {

class hp16500_state : public driver_device
{
public:
	hp16500_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_mlc(*this, "mlc")
	{ }

	void hp16500b(machine_config &config);
	void hp16500a(machine_config &config);
	void hp1651(machine_config &config);
	void hp1650(machine_config &config);

private:
	virtual void video_start() override ATTR_COLD;
	uint32_t screen_update_hp16500(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	[[maybe_unused]] uint32_t screen_update_hp16500a(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	required_device<cpu_device> m_maincpu;
	optional_device<hp_hil_mlc_device> m_mlc;
	std::vector<uint8_t> m_vram;

	uint8_t m_mask = 0, m_val = 0;

	void palette_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);

	void vram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint8_t vram_r(offs_t offset);
	void mask_w(uint8_t data);
	void val_w(uint8_t data);
	uint32_t vbl_state_r();
	void vbl_ack_w(uint32_t data);
	uint16_t vbl_ack16_r();
	void vbl_ack16_w(uint16_t data);

	void pal_ctrl_w(uint8_t data);
	void pal_r_w(uint8_t data);
	void pal_g_w(uint8_t data);
	void pal_b_w(uint8_t data);

	void maskval_w(uint16_t data);
	void irq_2(int state);
	void vsync_changed(int state);
	MC6845_UPDATE_ROW(crtc_update_row);
	MC6845_UPDATE_ROW(crtc_update_row_1650);

	void hp16500_map(address_map &map) ATTR_COLD;
	void hp16500a_map(address_map &map) ATTR_COLD;
	void hp1650_map(address_map &map) ATTR_COLD;
	void hp1651_map(address_map &map) ATTR_COLD;

	uint32_t m_palette[256]{}, m_colors[3]{}, m_count = 0, m_clutoffs = 0;
};

uint32_t hp16500_state::vbl_state_r()
{
	return 0x03000000;  // bit 0 set means the interrupt handler advances the pSOS tick counter.
}

void hp16500_state::vbl_ack_w(uint32_t data)
{
	m_maincpu->set_input_line(M68K_IRQ_1, CLEAR_LINE);
}

uint16_t hp16500_state::vbl_ack16_r()
{
	m_maincpu->set_input_line(M68K_IRQ_1, CLEAR_LINE);
	return 0;
}

void hp16500_state::vbl_ack16_w(uint16_t data)
{
	m_maincpu->set_input_line(M68K_IRQ_1, CLEAR_LINE);
}

void hp16500_state::vsync_changed(int state)
{
	if (state)
	{
		m_maincpu->set_input_line(M68K_IRQ_1, ASSERT_LINE);
	}
}

MC6845_UPDATE_ROW( hp16500_state::crtc_update_row )
{
	uint32_t *p = &bitmap.pix(y);

	int pos =  y * 144;

	for (int i = 0; i < x_count; i++)
	{
		*p++  = m_palette[m_vram[pos+0x00000]];
		*p++  = m_palette[m_vram[pos+0x10000]];
		*p++  = m_palette[m_vram[pos+0x20000]];
		*p++  = m_palette[m_vram[pos+0x30000]];
		pos++;
		*p++  = m_palette[m_vram[pos+0x00000]];
		*p++  = m_palette[m_vram[pos+0x10000]];
		*p++  = m_palette[m_vram[pos+0x20000]];
		*p++  = m_palette[m_vram[pos+0x30000]];
		pos++;
	}
}

MC6845_UPDATE_ROW( hp16500_state::crtc_update_row_1650 )
{
	uint32_t *p = &bitmap.pix(y);

	int pos =  y * 148;

	for (int i = 0; i < x_count; i++)
	{
		*p++  = m_palette[m_vram[pos+0x00000]];
		*p++  = m_palette[m_vram[pos+0x10000]];
		*p++  = m_palette[m_vram[pos+0x20000]];
		*p++  = m_palette[m_vram[pos+0x30000]];
		pos++;
		*p++  = m_palette[m_vram[pos+0x00000]];
		*p++  = m_palette[m_vram[pos+0x10000]];
		*p++  = m_palette[m_vram[pos+0x20000]];
		*p++  = m_palette[m_vram[pos+0x30000]];
		pos++;
	}
}

void hp16500_state::pal_ctrl_w(uint8_t data)
{
	m_clutoffs = data & 0xf;
}


void hp16500_state::pal_r_w(uint8_t data)
{
	m_colors[0] = (data<<4);
	m_palette[m_clutoffs] = rgb_t(m_colors[0], m_colors[1], m_colors[2]);
}

void hp16500_state::pal_g_w(uint8_t data)
{
	m_colors[1] = (data<<4);
	m_palette[m_clutoffs] = rgb_t(m_colors[0], m_colors[1], m_colors[2]);
}

void hp16500_state::pal_b_w(uint8_t data)
{
	m_colors[2] = (data<<4);
	m_palette[m_clutoffs] = rgb_t(m_colors[0], m_colors[1], m_colors[2]);
}

void hp16500_state::maskval_w(uint16_t data)
{
	// by analogy with the string printer code from the 16500b, which
	// appears to be a direct port...
	m_val =  ((data>>8) & 0xff) ^ 0xff;
	m_mask = (data & 0xff) ^ 0xff;
}

void hp16500_state::irq_2(int state)
{
	m_maincpu->set_input_line(M68K_IRQ_2, state);
}

void hp16500_state::hp1650_map(address_map &map)
{
	map(0x000000, 0x00ffff).rom().region("bios", 0);

	map(0x201000, 0x201001).w(FUNC(hp16500_state::maskval_w));

	map(0x204001, 0x204001).w(FUNC(hp16500_state::pal_ctrl_w));
	map(0x205001, 0x205001).w(FUNC(hp16500_state::pal_r_w));
	map(0x2050fe, 0x2050ff).noprw();
	map(0x206000, 0x206001).nopr();
	map(0x206001, 0x206001).w(FUNC(hp16500_state::pal_g_w));
	map(0x207001, 0x207001).w(FUNC(hp16500_state::pal_b_w));

	map(0x20a000, 0x20a007).rw("epci", FUNC(scn_pci_device::read), FUNC(scn_pci_device::write)).umask16(0x00ff);

	map(0x20c001, 0x20c001).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x20c003, 0x20c003).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));

	map(0x20e000, 0x20e001).rw(FUNC(hp16500_state::vbl_ack16_r), FUNC(hp16500_state::vbl_ack16_w));

	map(0x20f000, 0x20f001).noprw();

	map(0x600000, 0x61ffff).w(FUNC(hp16500_state::vram_w));
	map(0x600000, 0x67ffff).r(FUNC(hp16500_state::vram_r)).umask16(0x00ff);

	map(0x900000, 0x9fffff).ram();
}

// like 1650 but moves main RAM to match 16500a
void hp16500_state::hp1651_map(address_map &map)
{
	map(0x000000, 0x00ffff).rom().region("bios", 0);

	map(0x201000, 0x201001).w(FUNC(hp16500_state::maskval_w));

	map(0x204001, 0x204001).w(FUNC(hp16500_state::pal_ctrl_w));
	map(0x205001, 0x205001).w(FUNC(hp16500_state::pal_r_w));
	map(0x2050fe, 0x2050ff).noprw();
	map(0x206000, 0x206001).nopr();
	map(0x206001, 0x206001).w(FUNC(hp16500_state::pal_g_w));
	map(0x207001, 0x207001).w(FUNC(hp16500_state::pal_b_w));

	map(0x20a000, 0x20a007).rw("epci", FUNC(scn_pci_device::read), FUNC(scn_pci_device::write)).umask16(0x00ff);

	map(0x20c001, 0x20c001).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x20c003, 0x20c003).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));

	map(0x20e000, 0x20e001).rw(FUNC(hp16500_state::vbl_ack16_r), FUNC(hp16500_state::vbl_ack16_w));

	map(0x20f000, 0x20f001).noprw();

	map(0x600000, 0x61ffff).w(FUNC(hp16500_state::vram_w));
	map(0x600000, 0x67ffff).r(FUNC(hp16500_state::vram_r)).umask16(0x00ff);

	map(0x980000, 0xa7ffff).ram();
}

void hp16500_state::hp16500a_map(address_map &map)
{
	map(0x000000, 0x00ffff).rom().region("bios", 0);

	map(0x201000, 0x201001).w(FUNC(hp16500_state::maskval_w));

	map(0x204001, 0x204001).w(FUNC(hp16500_state::pal_ctrl_w));
	map(0x205001, 0x205001).w(FUNC(hp16500_state::pal_r_w));
	map(0x206001, 0x206001).w(FUNC(hp16500_state::pal_g_w));
	map(0x207001, 0x207001).w(FUNC(hp16500_state::pal_b_w));

	map(0x20c001, 0x20c001).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x20c003, 0x20c003).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));

	map(0x20e000, 0x20e001).rw(FUNC(hp16500_state::vbl_ack16_r), FUNC(hp16500_state::vbl_ack16_w));

	map(0x600000, 0x61ffff).w(FUNC(hp16500_state::vram_w));
	map(0x600000, 0x67ffff).r(FUNC(hp16500_state::vram_r)).umask16(0x00ff);

	map(0x980000, 0xa7ffff).ram();
}

uint32_t hp16500_state::screen_update_hp16500a(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void hp16500_state::hp16500_map(address_map &map)
{
	map(0x00000000, 0x0001ffff).rom().region("bios", 0);
	map(0x0020f000, 0x0020f003).w(FUNC(hp16500_state::palette_w));

	map(0x00202800, 0x00202803).w(FUNC(hp16500_state::vbl_ack_w));
	map(0x00203000, 0x00203003).w(FUNC(hp16500_state::vbl_ack_w));
	map(0x00209800, 0x00209803).r(FUNC(hp16500_state::vbl_state_r));

	map(0x0020b800, 0x0020b83f).rw("rtc", FUNC(ds1286_device::data_r), FUNC(ds1286_device::data_w));
	map(0x0020b840, 0x0020b843).noprw(); // system ram test is really strange.

	map(0x0020f800, 0x0020f80f).rw(m_mlc, FUNC(hp_hil_mlc_device::read), FUNC(hp_hil_mlc_device::write));
	map(0x00600000, 0x0061ffff).w(FUNC(hp16500_state::vram_w));
	map(0x00600000, 0x0067ffff).r(FUNC(hp16500_state::vram_r)).umask32(0x00ff00ff);
	map(0x00700000, 0x00700000).w(FUNC(hp16500_state::mask_w));
	map(0x00740000, 0x00740000).w(FUNC(hp16500_state::val_w));
	map(0x00800000, 0x009fffff).ram();
}

void hp16500_state::video_start()
{
	m_count = 0;
	m_clutoffs = 0;
	memset(m_palette, 0, sizeof(m_palette));
	m_vram.resize(0x40000);
	m_mask = 0;
	m_val = 0;
}

// The test code is buggy, it writes a byte in the wrong position
// (even instead of odd).  It still works because the 68k replicates
// the byte on the word and the hardware doesn't decode UDS/LDS.  But
// that is why the handler needs to be 16 bits, or it won't be called
// in the first place.

void hp16500_state::vram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if(!ACCESSING_BITS_0_7)
		data = data | (data >> 8);
	for(int i=0; i<4; i++) {
		int off = offset + i * 0x10000;
		if(data & (8 >> i))
			m_vram[off] = (m_vram[off] & ~m_mask) | (m_val & m_mask);
		else
			m_vram[off] = (m_vram[off] & ~m_mask);
	}
}

uint8_t hp16500_state::vram_r(offs_t offset)
{
	return m_vram[offset];
}

void hp16500_state::mask_w(uint8_t data)
{
	m_mask = data;
}

void hp16500_state::val_w(uint8_t data)
{
	m_val = data;
}

void hp16500_state::palette_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	if (mem_mask == 0xff000000)
	{
		m_clutoffs = (data>>24) & 0xff;
	}
	else if (mem_mask == 0x00ff0000)
	{
		m_colors[m_count++] = (data>>14) & 0xfc;

		if (m_count == 3)
		{
			m_palette[m_clutoffs] = rgb_t(m_colors[0], m_colors[1], m_colors[2]);
			m_clutoffs++;
			if (m_clutoffs > 255)
			{
				m_clutoffs = 0;
			}
			m_count = 0;
		}
	}
}

// 4 bpp
// addr = ((Y * 0xfc0) + 0x360) + (X * 4)
uint32_t hp16500_state::screen_update_hp16500(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	int pos = 0;
	for (int y = 0; y < 384; y++)
	{
		uint32_t *scanline = &bitmap.pix(y);

		for (int x = 0; x < 576; x+=4)
		{
			*scanline++ = m_palette[m_vram[pos+0x00000]];
			*scanline++ = m_palette[m_vram[pos+0x10000]];
			*scanline++ = m_palette[m_vram[pos+0x20000]];
			*scanline++ = m_palette[m_vram[pos+0x30000]];
			pos++;
		}
	}

	return 0;
}

void hp16500_state::hp1650(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, 10000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &hp16500_state::hp1650_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(25000000, 0x330, 0, 0x250, 0x198, 0, 0x180);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	mc6845_device &crtc(MC6845(config, "crtc", 25000000/9));
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(8);
	crtc.set_update_row_callback(FUNC(hp16500_state::crtc_update_row_1650));
	crtc.out_vsync_callback().set(FUNC(hp16500_state::vsync_changed));

	SCN2661A(config, "epci", 5000000);

	SPEAKER(config, "speaker", 2).front();
}

void hp16500_state::hp1651(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, 10000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &hp16500_state::hp1651_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(25000000, 0x330, 0, 0x250, 0x198, 0, 0x180);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	mc6845_device &crtc(MC6845(config, "crtc", 25000000/9));
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(8);
	crtc.set_update_row_callback(FUNC(hp16500_state::crtc_update_row_1650));
	crtc.out_vsync_callback().set(FUNC(hp16500_state::vsync_changed));

	SCN2661A(config, "epci", 5000000);

	SPEAKER(config, "speaker", 2).front();
}

void hp16500_state::hp16500a(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, 10000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &hp16500_state::hp16500a_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(25000000, 0x320, 0, 0x240, 0x19c, 0, 0x170);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	mc6845_device &crtc(MC6845(config, "crtc", 25000000/9));
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(8);
	crtc.set_update_row_callback(FUNC(hp16500_state::crtc_update_row));
	crtc.out_vsync_callback().set(FUNC(hp16500_state::vsync_changed));

	SPEAKER(config, "speaker", 2).front();
}

void hp16500_state::hp16500b(machine_config &config)
{
	/* basic machine hardware */
	M68EC030(config, m_maincpu, 50_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &hp16500_state::hp16500_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_screen_update(FUNC(hp16500_state::screen_update_hp16500));
	screen.set_size(576,384);
	screen.set_visarea(0, 576-1, 0, 384-1);
	screen.set_refresh_hz(60);
	screen.screen_vblank().set(FUNC(hp16500_state::vsync_changed));
	// FIXME: Where is the AP line connected to? The MLC documentation recommends connecting it to VBLANK
	screen.screen_vblank().append(m_mlc, FUNC(hp_hil_mlc_device::ap_w));

	HP_HIL_MLC(config, m_mlc, XTAL(15'920'000)/2);
	m_mlc->int_callback().set(FUNC(hp16500_state::irq_2));

	// TODO: for now hook up the ipc hil keyboard - this might be replaced
	// later with a 16500b specific keyboard implementation
	HP_HIL_SLOT(config, "hil1", "mlc", hp_hil_devices, "hp_ipc_kbd");

	DS1286(config, "rtc", 32768);
	//WD37C65C(config, "fdc", 16_MHz_XTAL);

	SPEAKER(config, "speaker", 2).front();
}

static INPUT_PORTS_START( hp16500 )
INPUT_PORTS_END

ROM_START( hp1650b )
	ROM_REGION16_BE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "la1.bin",      0x000000, 0x008000, CRC(7c60e334) SHA1(c3661e4bb58e0951e9c13208b4991f5d9dda633b) )
	ROM_LOAD16_BYTE( "la2.bin",      0x000001, 0x008000, CRC(b0c6fe60) SHA1(a77b2e7098f26af93b0946abe6e89711b3332210) )
ROM_END

ROM_START( hp1651b )
	ROM_REGION16_BE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "u102.bin",     0x000000, 0x008000, CRC(d7e1b091) SHA1(af813cc7ae748aedf1b1d9d522ee2154961315c2) )
	ROM_LOAD16_BYTE( "u103.bin",     0x000001, 0x008000, CRC(f32a37c7) SHA1(8addda59923f64fd9b7e909eb0e3a17cc02bb70e) )
ROM_END

ROM_START( hp165ka0 )
	ROM_REGION16_BE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "16500-80002.bin", 0x000000, 0x008000, CRC(0324b75a) SHA1(837855fce9288139226c914cc0c43061e25b57d2) )
	ROM_LOAD16_BYTE( "16500-80001.bin", 0x000001, 0x008000, CRC(362c8cbf) SHA1(812b79d1a31d09ec632a6842b11548168d82b5e7) )
ROM_END

ROM_START( hp16500b )
	ROM_REGION32_BE(0x20000, "bios", 0)
	ROM_LOAD32_BYTE( "16500-80014.bin", 0x000000, 0x008000, CRC(35187716) SHA1(82067737892ecd356a5454a43d9ce9b38ac2397f) )
	ROM_LOAD32_BYTE( "16500-80015.bin", 0x000001, 0x008000, CRC(d8d26c1c) SHA1(b5b956c17c9d6e54519a686b5e4aa733b266bf6f) )
	ROM_LOAD32_BYTE( "16500-80016.bin", 0x000002, 0x008000, CRC(61457b39) SHA1(f209315ec22a8ee9d44a0ec009b1afb47794bece) )
	ROM_LOAD32_BYTE( "16500-80017.bin", 0x000003, 0x008000, CRC(e0b1096b) SHA1(426bb9a4756d8087bded4f6b61365d733ffbb09a) )
ROM_END

} // anonymous namespace


COMP( 1989, hp1650b,  0, 0, hp1650,   hp16500, hp16500_state, empty_init, "Hewlett Packard", "HP 1650b",  MACHINE_NOT_WORKING|MACHINE_NO_SOUND)
COMP( 1989, hp1651b,  0, 0, hp1651,   hp16500, hp16500_state, empty_init, "Hewlett Packard", "HP 1651b",  MACHINE_NOT_WORKING|MACHINE_NO_SOUND)
COMP( 1991, hp165ka0, 0, 0, hp16500a, hp16500, hp16500_state, empty_init, "Hewlett Packard", "HP 16500a", MACHINE_NOT_WORKING|MACHINE_NO_SOUND)
COMP( 1991, hp16500b, 0, 0, hp16500b, hp16500, hp16500_state, empty_init, "Hewlett Packard", "HP 16500b", MACHINE_NOT_WORKING|MACHINE_NO_SOUND)



hp2100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************************

2017-11-06 Skeleton

HP 2100 and HP 1000 minicomputers with a front panel. Produced between 1966 and 1970. The CPU is a 2116A, a hard-wired design.
The computer can have 32K of 16-bit words of magnetic core. It had 16 I/O slots for various accessories.
See https://en.wikipedia.org/wiki/HP_2100 for more info.

**************** Contents of readme files.
**HP 1000 boot proms:
12992-80002     059F                                  7905/6/20/25
12992-80004     0672                                  ICD
12992-80005     0673                                  CS80
12992-80006     073A     1816-0962                    magtape
12992-80007     06C6     1816-1051                    9885 floppy
12992-80009     0749                                  cartridge tape
                053A     1816-0420                    paper tape
                07FB     1816-0862
                077F     5081-2361

**HP 1000 firmware:
 13207-60001


            1816-
            0545.u2  0539.u3   0540.u4   0541.u5   0542.u6   0543.u7
            0538.u9  0544.u10  0549.u11  0548.u12  0547.u13  0546.u14


 * *-*
 *-* *
 *-* *

HP1000 E/F Firmware
                    PN            CKSUM
                    ===========   =====
2113 Base Set       02113-80006   BCDB
                    02113-80007   8741
                    02113-80008   0889

2117 Base Set       02117-80016   C08F
                    02117-80017   84E4
                    02117-80018   2C45

E-FPP               5090-0589     0530
                    5090-0590     C6B6
                    5090-0591     DE78

E-DMI               13307-80036   EA76      13307-80033 (same cksum)
                    13307-80037   64CA      13307-80034 (same cksum)
                    13307-80038   D7B8      13307-80035 (same cksum)

F-DMI/FFP           5180-0141     FCD1
                    5180-0142     8C42
                    5180-0143     E820

F-VIS               12824-80007   FF2C
                    12824-80008   F4B8
                    12825-80009   BF83

F-SIS               12823-80019   B7E9
                    12823-80020   7EAE
                    12823-80021   2CE9

SIGNAL/1000         92835-80001   1EEC
                    92835-80002   D419
                    92835-80003   C034

DS/1000             91740-80067   3F44
                    91740-80068   340A
                    91740-80069   FF62

RTE-IV EMA          92067-80001   F054
                    92067-80002   B744
                    92067-80003   AAAA

RTE6/VM OS          92084-80001   E946
                    92084-80102   E116      92084-80002   E10E (earlier rev)
                    92084-80103   C670      92084-80003   C654 (earlier rev)

RTE6/VM EMA/VMA     92084-80004   D166
                    92084-80005   8C02
                    92084-80006   B412

2000TSB IOP Firmware (from page 81-11 of M/E/F CE manual)

1816-0532 ... 1816-0537   (6 proms)     2100
1816-0538 ... 1816-0549   (12 proms)    21MX   13207-60001 firmware board
1816-0996 ... 1816-1001   (6 proms)     21MX-E 13304A firmware acc board needed


E-Series Control Memory Map
===========================
Control Memory Module Allocation      Module No    Octal        Software Entry Point

HP Base Set                          -+ 0            00000-00377  yes      1k
                                      | 1            00400-00777  yes
                                      | 2            01000-01377  yes
                                      + 3            01400-01777  yes
Available For User Microprogramming  -+ 4            02000-02377  no       2k
                                      | 5            02400-02777  no
                                      | 6            03000-03377  no
                                      | 7            03400-03777  no
                                      | 8            04000-04377  no       3k
                                      | 9            04400-04777  no
                                      | 10           05000-05377  no
                                      | 11           05400-05777  no
                                      | 12           06000-06377  no       4k
                                      | 13           06400-06777  no
                                      | 14           07000-07377  no
                                      | 15           07400-07777  no
                                      | 16           10000-10377  no       5k
                                      | 17           10400-10777  no
                                      | 18           11000-11377  no
                                      | 19           11400-11777  no
                                      | 20           12000-12377  no       6k
                                      | 21           12400-12777  no
                                      | 22           13000-13377  no
                                      | 23           13400-13777  no
                                      | 24           14000-14377  no       7k
                                      | 25           14400-14777  no
                                      | 26           15000-15377  no
                                      | 27           15400-15777  no
                                      | 28           16000-16377  no       8k
                                      | 29           16400-16777  no
                                      | 30           17000-17377  no
                                      + 31           17400-17777  no       8k
HP Dynamic Mapping System            -- 32           20000-20377  yes      9k
HP Fast Fortran Processor            -+ 33           20400-20777  no
                                      | 34           21000-21377  yes
                                      + 35           21400-21777  yes
RTE-IV EMA or RTE-6/VM EMA/VMA       -+ 36           22000-22377  yes      10k
                                      + 37           22400-22777  yes
DS/1000                              -+ 38           23000-23377  yes
                                      + 39           23400-23777  yes
HP Reserved                          -+ 40           24000-24377  yes      11k
                                      | 41           24400-24777  no
                                      | 42           25000-25377  no
                                      + 43           25400-25777  no
RTE-6/VM Operating System            -+ 44           26000-26377  yes      12k
                                      + 45           26400-26777  yes
Recommended For User Microprogramming + 46           27000-27377  yes
                                      | 47           27400-27777  yes
                                      | 48           30000-30377  yes      13k
                                      | 49           30400-30777  yes
                                      | 50           31000-31377  yes
                                      | 51           31400-31777  no
                                      | 52           32000-32377  no       14k
                                      | 53           32400-32777  no
                                      | 54           33000-33377  no
                                      | 55           33400-33777  no
                                      | 56           34000-34377  yes      15k
                                      | 57           34400-34777  yes
                                      | 58           35000-35377  yes
                                      | 59           35400-35777  yes
                                      | 60           36000-36377  yes      16k
                                      | 61           36400-36777  no
                                      | 62           37000-37377  yes
                                      | 63           37400-37777  no

F-Series Control Memory Map
===========================
Control Memory Module Allocation      Module No    Octal        Software Entry Point

HP Base Set                          -+ 0            00000-00377  yes      1k
                                      | 1            00400-00777  yes
                                      | 2            01000-01377  yes
                                      + 3            01400-01777  yes
HP Reserved                          -+ 4            02000-02377  yes      2k
                                      | 5            02400-02777  no
                                      | 6            03000-03377  no
                                      | 7            03400-03777  no
                                      | 8            04000-04377  yes      3k
                                      | 9            04400-04777  no
                                      | 10           05000-05377  no
                                      + 11           05400-05777  no
Vector Instruction Set               -+ 12           06000-06377  yes      4k
                                      | 13           06400-06777  no
                                      | 14           07000-07377  no
                                      + 15           07400-07777  no
RTE-6/VM Operating System            -+ 16           10000-10377  yes      5k
                                      | 17           10400-10777  no
HP Reserved                          -+ 18           11000-11377  yes
                                      | 19           11400-11777  no
                                      | 20           12000-12377  yes      6k
                                      | 21           12400-12777  no
                                      | 22           13000-13377  no
                                      | 23           13400-13777  no
                                      | 24           14000-14377  no       7k
                                      | 25           14400-14777  no
                                      | 26           15000-15377  no
                                      + 27           15400-15777  no
Available For User Microprogramming  -+ 28           16000-16377  no       8k
                                      | 29           16400-16777  no
                                      | 30           17000-17377  no
                                      + 31           17400-17777  no
HP Dynamic Mapping System            -- 32           20000-20377  yes      9k
HP Fast Fortran Processor            -+ 33           20400-20777  no
                                      | 34           21000-21377  yes
                                      + 35           21400-21777  yes
RTE-IV EMA or RTE-6/VM EMA/VMA       -+ 36           22000-22377  yes      10k
                                      + 37           22400-22777  yes
DS/1000                              -+ 38           23000-23377  yes
                                      + 39           23400-23777  yes
Scientific Instruction Set           -+ 40           24000-24377  yes      11k
                                      | 41           24400-24777  no
                                      | 42           25000-25377  no
                                      + 43           25400-25777  no
HP Reserved                          -+ 44           26000-26377  no       12k
                                      + 45           26400-26777  no
Recommended For User Microprogramming + 46           27000-27377  yes
                                      | 47           27400-27777  yes
                                      | 48           30000-30377  yes      13k
                                      | 49           30400-30777  yes
                                      | 50           31000-31377  yes
                                      | 51           31400-31777  no
                                      | 52           32000-32377  no       14k
                                      | 53           32400-32777  no
                                      | 54           33000-33377  no
                                      | 55           33400-33777  no
                                      | 56           34000-34377  yes      15k
                                      | 57           34400-34777  yes
                                      | 58           35000-35377  yes
                                      | 59           35400-35777  yes
                                      | 60           36000-36377  yes      16k
                                      | 61           36400-36777  no
                                      | 62           37000-37377  yes
                                      | 63           37400-37777  no

************************************************************************************************************************************/

#include "emu.h"


namespace {

class hp2100_state : public driver_device
{
public:
	hp2100_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
//      , m_maincpu(*this, "maincpu")
	{ }

void hp2100(machine_config &config);
private:
//  required_device<cpu_device> m_maincpu;
};

static INPUT_PORTS_START( hp2100 )
INPUT_PORTS_END

void hp2100_state::hp2100(machine_config &config)
{
}

ROM_START( hp2100 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	// HP 2100 firmware
	ROM_LOAD( "1816-0054.bin", 0x000000, 0x000100, CRC(7c4192a2) SHA1(1f06e392258dd41f1f7ebd8b744a18ba03514e9d) )
	ROM_LOAD( "1816-0055.bin", 0x000000, 0x000100, CRC(6f0a671d) SHA1(febbbd6672ae1e95141ae4dddaea545b253ad18b) )
	ROM_LOAD( "1816-0056.bin", 0x000000, 0x000100, CRC(2a675e62) SHA1(d044911edc40bee386a8b96b9281250f4d9a4ac0) )
	ROM_LOAD( "1816-0057.bin", 0x000000, 0x000100, CRC(c1b3e302) SHA1(9ab7375ad80bc95efc0d744d23c5a45d42deb75a) )
	ROM_LOAD( "1816-0058.bin", 0x000000, 0x000100, CRC(a03f29e6) SHA1(d2bb3beb947e68bd51783b8730a80dd568b23aef) )
	ROM_LOAD( "1816-0059.bin", 0x000000, 0x000100, CRC(4753cd42) SHA1(bb801c842b82a316e92b6bdee196dc0d2e910590) )
	ROM_LOAD( "1816-0368.bin", 0x000000, 0x000100, CRC(6e4f94e6) SHA1(abac74ff4d57a75e33d35f9f2d1007c953c0d89b) )
	ROM_LOAD( "1816-0369.bin", 0x000000, 0x000100, CRC(be27972a) SHA1(c1dc90614f68adbc6870dd4169d296366f32f335) )
	ROM_LOAD( "1816-0370.bin", 0x000000, 0x000100, CRC(bf9d609b) SHA1(e03467d293b0ce7c3a2f99ac3c72f9d70e1f49ab) )
	ROM_LOAD( "1816-0371.bin", 0x000000, 0x000100, CRC(9292ce4a) SHA1(03ee62752bf1924127eef1decfa974395ccf2d7c) )
	ROM_LOAD( "1816-0372.bin", 0x000000, 0x000100, CRC(e38ada24) SHA1(b5e6ba0fcf49e45d38fd273bb078cc82946cfe5b) )
	ROM_LOAD( "1816-0373.bin", 0x000000, 0x000100, CRC(45e03c99) SHA1(509fe9fec58caf3a5983ad2bdf47c22a6e5376d5) )
	ROM_LOAD( "1816-0490.bin", 0x000000, 0x000100, CRC(61e272b0) SHA1(5d35c135653bf1a1891b7672a5a65958e7e52189) )
	ROM_LOAD( "1816-0491.bin", 0x000000, 0x000100, CRC(9e1504bd) SHA1(6258fa775f1979f0031ad930bf23938ea61afad8) )
	ROM_LOAD( "1816-0492.bin", 0x000000, 0x000100, CRC(0c4e7f1f) SHA1(951c69053de4a0d368d1d3cc851e48645cb69ed0) )
	ROM_LOAD( "1816-0493.bin", 0x000000, 0x000100, CRC(fc3f785e) SHA1(7398ef7bf0fe4f689a1c836cc4295ee6a7ef628b) )
	ROM_LOAD( "1816-0494.bin", 0x000000, 0x000100, CRC(a2424034) SHA1(e99f577dc3c23c82a12b6689c65b2d9e20e7f591) )
	ROM_LOAD( "1816-0495.bin", 0x000000, 0x000100, CRC(e79f1401) SHA1(13812f3acd3129e9ab2e2d790bdb25ed966061af) )
	ROM_LOAD( "1816-0862.bin", 0x000000, 0x000100, CRC(780e180b) SHA1(db4bedf30d347b618101efc5da5b15a822e10f6f) )
	ROM_LOAD( "1816-0869.bin", 0x000000, 0x000100, CRC(897e94a9) SHA1(805a4ebc837daeb0b84b44d2eddbde8862ff9a32) )
	// HP 1000 boot proms
	ROM_LOAD( "12992-80002.bin", 0x000000, 0x000100, CRC(62a32a93) SHA1(fadc7b3c8d9d983ba8834ae3be87b3fef4da5a2c) )
	ROM_LOAD( "12992-80004.bin", 0x000000, 0x000100, CRC(9d699b3b) SHA1(dea16198bc959056782333b87893ec88a8fc8e6d) )
	ROM_LOAD( "12992-80005.bin", 0x000000, 0x000100, CRC(f4fb0410) SHA1(c93c47f3e5c41eb5fae60f5bbe81586e62bc278f) )
	ROM_LOAD( "12992-80006.bin", 0x000000, 0x000100, CRC(a18c008e) SHA1(c5b4237f256732f8e3c044dc728e65510e190056) )
	ROM_LOAD( "12992-80007.bin", 0x000000, 0x000100, CRC(505bb24b) SHA1(3579aaf97534768bfb59b376a685a1104330ebd8) )
	ROM_LOAD( "12992-80009.bin", 0x000000, 0x000100, CRC(38904247) SHA1(e41abd203e918d966ace66f198c53953f7c8547d) )
	ROM_LOAD( "1816-0420.bin", 0x000000, 0x000100, CRC(f3d7c188) SHA1(f63287cbb0aa6d20dd179a7f2d15755c8f34f027) )
	ROM_LOAD( "1816-0796.bin", 0x000000, 0x000100, CRC(d3a675a7) SHA1(1f64f40e46d80f770cc00c2c2c9f251f96ff47b0) )
	ROM_LOAD( "5081-2361.bin", 0x000000, 0x000100, CRC(29f71b0c) SHA1(767cc9d57b5921f755dbd3e376a30dd21cfa33cd) )
	// HP 1000 firmware
	ROM_LOAD( "02113-80006.bin", 0x000000, 0x000400, CRC(8cd6f92a) SHA1(fa0048897362b2151bb417b36712f33a6a346113) )
	ROM_LOAD( "02113-80007.bin", 0x000000, 0x000400, CRC(5f92b933) SHA1(461a24aabc631dec2d980c595fbb36207fa08d40) )
	ROM_LOAD( "02113-80008.bin", 0x000000, 0x000400, CRC(52382fef) SHA1(bc05f8860b495c8e46cda7308858730738f76e4f) )
	ROM_LOAD( "02117-80016.bin", 0x000000, 0x000400, CRC(b43e2864) SHA1(b4753042d8750e169f24333545309838d9540a9a) )
	ROM_LOAD( "02117-80017.bin", 0x000000, 0x000400, CRC(03485b73) SHA1(8c5b6c5aed2351aa6e2455205a8bd6d32e69f978) )
	ROM_LOAD( "02117-80018.bin", 0x000000, 0x000400, CRC(dfeffc0c) SHA1(b062faa150bc2df6c92dc312e302e632def8fb00) )
	ROM_LOAD( "12823-80019.bin", 0x000000, 0x000400, CRC(219412d1) SHA1(71175efb74eaaebbcd80ff57bada53b9ac1fc31a) )
	ROM_LOAD( "12823-80020.bin", 0x000000, 0x000400, CRC(797932e2) SHA1(64f722e5893b397f87a94a9d0e06015658d3687d) )
	ROM_LOAD( "12823-80021.bin", 0x000000, 0x000400, CRC(7b79a419) SHA1(06ed29f9f728d16aed4de348aa8de5508ec65eb7) )
	ROM_LOAD( "12824-80007.bin", 0x000000, 0x000400, CRC(11617b11) SHA1(792f9867ced452fc33ed00e44f12e77e7d59de1c) )
	ROM_LOAD( "12824-80008.bin", 0x000000, 0x000400, CRC(72deee30) SHA1(85fb0c3d513c996b25864e1707bbd29a8d9bb50a) )
	ROM_LOAD( "12824-80009.bin", 0x000000, 0x000400, CRC(349fdb59) SHA1(c3de054e7d03428a1c7afc77fcae4e9b29b2d681) )
	ROM_LOAD( "13307-80036.bin", 0x000000, 0x000400, CRC(85cef205) SHA1(e0192c1dcc2d89c8862ba3c28ec173138c1a44ee) )
	ROM_LOAD( "13307-80033.bin", 0x000000, 0x000400, CRC(85cef205) SHA1(e0192c1dcc2d89c8862ba3c28ec173138c1a44ee) )
	ROM_LOAD( "13307-80034.bin", 0x000000, 0x000400, CRC(c726dd35) SHA1(fafd35727b0b29817ccd9278bd7e642aa7163082) )
	ROM_LOAD( "13307-80037.bin", 0x000000, 0x000400, CRC(c726dd35) SHA1(fafd35727b0b29817ccd9278bd7e642aa7163082) )
	ROM_LOAD( "13307-80035.bin", 0x000000, 0x000400, CRC(26c33e75) SHA1(cf40f18dd10b4c43bb93fecc0ca0fd5b0c5425c7) )
	ROM_LOAD( "13307-80038.bin", 0x000000, 0x000400, CRC(26c33e75) SHA1(cf40f18dd10b4c43bb93fecc0ca0fd5b0c5425c7) )
	ROM_LOAD( "1816-0538.u9.bin", 0x000000, 0x000100, CRC(9cf6fe69) SHA1(7fe6c4d890bdc9eb91fa5c0a7218b8994a62fa6c) )
	ROM_LOAD( "1816-0539.u3.bin", 0x000000, 0x000100, CRC(519a08bf) SHA1(fac12d2bb9ebc289fc7da5ffb3b784459fd75285) )
	ROM_LOAD( "1816-0540.u4.bin", 0x000000, 0x000100, CRC(fccb14f8) SHA1(9f71317303d9e0e9d5e32022d78fb0851e614ab5) )
	ROM_LOAD( "1816-0541.u5.bin", 0x000000, 0x000100, CRC(afeb0709) SHA1(6f9c4ab0975b3364940c60f3085824209e40f018) )
	ROM_LOAD( "1816-0542.u6.bin", 0x000000, 0x000100, CRC(bbafb8d8) SHA1(d3e8e0b0b04d79d55f1931585ba82310166f8614) )
	ROM_LOAD( "1816-0543.u7.bin", 0x000000, 0x000100, CRC(6df287d4) SHA1(5a2cd835ecd2082a418e6a672af9ca4d236eb394) )
	ROM_LOAD( "1816-0544.u10.bin", 0x000000, 0x000100, CRC(363c4c2b) SHA1(3a39a871c2833d16b16d74ca43f772d9b7196729) )
	ROM_LOAD( "1816-0545.u2.bin", 0x000000, 0x000100, CRC(11d99e86) SHA1(ccef7b22840ee8030dbac8e4d712cb488c403e35) )
	ROM_LOAD( "1816-0546.u14.bin", 0x000000, 0x000100, CRC(88c78dc0) SHA1(dae14d4fcf86d80c1a1df0fbd8f9aaa4bb5257d8) )
	ROM_LOAD( "1816-0547.u13.bin", 0x000000, 0x000100, CRC(db1f149a) SHA1(279099b2c438114e0ab722b16c956f3be8871f96) )
	ROM_LOAD( "1816-0548.u12.bin", 0x000000, 0x000100, CRC(fb0ad4ad) SHA1(97c4e55a3983181782ba41685b99d7c5a4a692e8) )
	ROM_LOAD( "1816-0549.u11.bin", 0x000000, 0x000100, CRC(8bf77f30) SHA1(6aa73d95b954ff33c1a69a19dd47b30712a574ad) )
	ROM_LOAD( "5090-0589.bin", 0x000000, 0x000400, CRC(e33752ca) SHA1(5956190e9bf4126065992de46a59ebd83757a9b1) )
	ROM_LOAD( "5090-0590.bin", 0x000000, 0x000400, CRC(b423b55c) SHA1(7931f155666da04d09e34d9c9e323c6a6a3e7621) )
	ROM_LOAD( "5090-0591.bin", 0x000000, 0x000400, CRC(f8653fc6) SHA1(3341fc65cd73c1adb0c56f1e0518041206d8b4de) )
	ROM_LOAD( "5180-0141.bin", 0x000000, 0x000400, CRC(215c5f5c) SHA1(1ec17050e63a447f07291f5de479dedf6ff6bdc7) )
	ROM_LOAD( "5180-0142.bin", 0x000000, 0x000400, CRC(919119f3) SHA1(18ebcaf355af1c6920fb90688f692e73d2ca9abf) )
	ROM_LOAD( "5180-0143.bin", 0x000000, 0x000400, CRC(82bf297a) SHA1(34ff0ac268b06072533fdf90d2365a4cf049e9f3) )
	ROM_LOAD( "91740-80067.bin", 0x000000, 0x000400, CRC(d65f5c3f) SHA1(bc3ef3623f0b475499f708647e779c3f82c9998b) )
	ROM_LOAD( "91740-80068.bin", 0x000000, 0x000400, CRC(5c62a4db) SHA1(6064c9d1179ea115c84e7f9133f93c9d0a85d1ab) )
	ROM_LOAD( "91740-80069.bin", 0x000000, 0x000400, CRC(7e8b5ebe) SHA1(2a2c56ab55bc32d5b60c29166969653297354e16) )
	ROM_LOAD( "92067-80001.bin", 0x000000, 0x000400, CRC(4366dafe) SHA1(559b5494cb57f1dc862717b881df51dde22b9ea1) )
	ROM_LOAD( "92067-80002.bin", 0x000000, 0x000400, CRC(b31eed07) SHA1(43004a14080176707bd492335be84ee9e776f97f) )
	ROM_LOAD( "92067-80003.bin", 0x000000, 0x000400, CRC(9f97bbe6) SHA1(e37e6c48786bc3ad6eb1ea9307c8b6e188524276) )
	ROM_LOAD( "92084-80001.bin", 0x000000, 0x000400, CRC(8ebbf4a8) SHA1(f770da090f1df7a9e8c9e69621b5690264b623d7) )
	ROM_LOAD( "92084-80002.bin", 0x000000, 0x000400, CRC(7b02d33a) SHA1(1a0f7f7fee60f26eec9c9aa06b19944ff5de1b1f) )
	ROM_LOAD( "92084-80003.bin", 0x000000, 0x000400, CRC(9e338250) SHA1(6758fe50931ec9b623247f51b5b63d9cbca47061) )
	ROM_LOAD( "92084-80004.bin", 0x000000, 0x000400, CRC(524ee313) SHA1(d9194d566b59974bf17d290b7f9efbbf0874b410) )
	ROM_LOAD( "92084-80005.bin", 0x000000, 0x000400, CRC(9a62d961) SHA1(f1c1faa0760df0e0406a5a2cb7b4fc6bb12f349f) )
	ROM_LOAD( "92084-80006.bin", 0x000000, 0x000400, CRC(26af135b) SHA1(82dbd9be22a6ffa90cfbfad25f80daa281aa3a14) )
	ROM_LOAD( "92084-80102.bin", 0x000000, 0x000400, CRC(644f8bb2) SHA1(10bef2af538c543bf42c5df109506834fbfbcaa8) )
	ROM_LOAD( "92084-80103.bin", 0x000000, 0x000400, CRC(77b7b66c) SHA1(b8a4afc9b48696dd8691918c4d8b89fba4d2526b) )
	ROM_LOAD( "92835-80001.bin", 0x000000, 0x000400, CRC(f6b71580) SHA1(f0e529f1f81ddf9c3e028d666b65f10289e088bb) )
	ROM_LOAD( "92835-80002.bin", 0x000000, 0x000400, CRC(e417c13a) SHA1(bcbe0421079fb18a1c3989e863b1f9a763f4d548) )
	ROM_LOAD( "92835-80003.bin", 0x000000, 0x000400, CRC(1df52f66) SHA1(84fd048d50f6fde9a79e8df618b5a3435a087f85) )
ROM_END

} // anonymous namespace


COMP( 1966, hp2100, 0, 0, hp2100, hp2100, hp2100_state, empty_init, "Hewlett-Packard", "HP 2100", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



hp2620.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

Skeleton driver for HP-2620 series display terminals.

************************************************************************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/input_merger.h"
#include "machine/mos6551.h"
#include "machine/nvram.h"
#include "machine/ripple_counter.h"
#include "sound/spkrdev.h"
#include "video/dp8350.h"
#include "screen.h"
#include "speaker.h"


namespace {

class hp2620_state : public driver_device
{
public:
	hp2620_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_nmigate(*this, "nmigate")
		, m_crtc(*this, "crtc")
		, m_datacomm(*this, "datacomm")
		, m_bell(*this, "bell")
		, m_bellctr(*this, "bellctr")
		, m_p_chargen(*this, "chargen")
		, m_nvram(*this, "nvram")
	{ }

	void hp2622(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u8 nvram_r(offs_t offset);
	void nvram_w(offs_t offset, u8 data);
	u8 keystat_r();
	void keydisp_w(u8 data);
	u8 sysstat_r();
	void modem_w(u8 data);
	void crtc_w(offs_t offset, u8 data);
	void ennmi_w(offs_t offset, u8 data);

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void nlrc_w(int state);
	void bell_w(int state);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<input_merger_device> m_nmigate;
	required_device<dp8367_device> m_crtc;
	required_device<rs232_port_device> m_datacomm;
	required_device<speaker_sound_device> m_bell;
	required_device<ripple_counter_device> m_bellctr;
	required_region_ptr<u8> m_p_chargen;
	required_shared_ptr<u8> m_nvram;

	u16 m_display_page = 0;
	u8 m_key_status = 0;
};


u32 hp2620_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

u8 hp2620_state::nvram_r(offs_t offset)
{
	// TODO: wait states
	return 0xf0 | m_nvram[offset];
}

void hp2620_state::nvram_w(offs_t offset, u8 data)
{
	// TODO: wait states
	m_nvram[offset] = data & 0x0f;
}

u8 hp2620_state::keystat_r()
{
	// LS299 at U25
	return m_key_status;
}

void hp2620_state::keydisp_w(u8 data)
{
	// LS374 at U26

	// TODO: D0-D3 = KEY3-KEY6
	m_bellctr->reset_w(BIT(data, 4));
	// TODO: D6 = BLINK RATE
	// TODO: D7 = ENHOFF
}

u8 hp2620_state::sysstat_r()
{
	// LS244 at U36
	u8 status = 0xe0;

	status |= m_crtc->vblank_r() << 0;
	// TODO: D1 = PINT
	status |= m_datacomm->dsr_r() << 2; // DM (J6-12)
	status |= m_datacomm->cts_r() << 3; // CS (J6-11)
	status |= m_datacomm->ri_r() << 4; // OCR1 (J6-18)

	return status;
}

void hp2620_state::modem_w(u8 data)
{
	// LS174 at U35 (TODO: D0 = DISPOFF)
	m_datacomm->write_spds(BIT(data, 1)); // OCD1 (J6-7)
	m_crtc->refresh_control(BIT(data, 5));
}

void hp2620_state::crtc_w(offs_t offset, u8 data)
{
	m_crtc->register_load(data & 3, offset & 0xfff);
	m_display_page = offset & 0x3000;
}

void hp2620_state::ennmi_w(offs_t offset, u8 data)
{
	m_nmigate->in_w<0>(BIT(offset, 0));
}

void hp2620_state::nlrc_w(int state)
{
	// clock input for LS175 at U59
	if (state)
		m_nmigate->in_w<1>((m_crtc->lc_r() & 7) == 3);

	// TODO: shift keyboard response into m_key_status
}

void hp2620_state::bell_w(int state)
{
	m_bell->level_w(state);
}

void hp2620_state::mem_map(address_map &map)
{
	map(0x0000, 0xbfff).rom().region("maincpu", 0);
	map(0x8000, 0xbfff).w(FUNC(hp2620_state::crtc_w));
	map(0xc000, 0xffff).ram();
}

void hp2620_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x7f).rw(FUNC(hp2620_state::nvram_r), FUNC(hp2620_state::nvram_w)).share("nvram");
	map(0x80, 0x80).r(FUNC(hp2620_state::keystat_r));
	map(0x88, 0x89).w(FUNC(hp2620_state::ennmi_w));
	map(0x90, 0x90).r(FUNC(hp2620_state::sysstat_r));
	map(0xa0, 0xa3).w("acia", FUNC(mos6551_device::write));
	map(0xa4, 0xa7).r("acia", FUNC(mos6551_device::read));
	map(0xa8, 0xa8).w(FUNC(hp2620_state::modem_w));
	map(0xb8, 0xb8).w(FUNC(hp2620_state::keydisp_w));
	map(0xe1, 0xe1).nopw(); // program bug, undocumented expansion or ???
}

void hp2620_state::machine_start()
{
	save_item(NAME(m_display_page));
	save_item(NAME(m_key_status));
}

void hp2620_state::machine_reset()
{
	m_nmigate->in_w<1>(0);
	m_display_page = 0;
	m_key_status = 0;
}

static INPUT_PORTS_START( hp2622 )
INPUT_PORTS_END

void hp2620_state::hp2622(machine_config &config)
{
	Z80(config, m_maincpu, 25.7715_MHz_XTAL / 7); // 3.68 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &hp2620_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &hp2620_state::io_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // 5101 (A7 tied to GND) + battery (+ wait states)

	INPUT_MERGER_ALL_HIGH(config, m_nmigate).output_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_screen_update(FUNC(hp2620_state::screen_update));

	DP8367(config, m_crtc, 25.7715_MHz_XTAL).set_screen("screen");
	m_crtc->set_half_shift(true);
	m_crtc->hsync_callback().set(m_bellctr, FUNC(ripple_counter_device::clock_w)).invert();
	m_crtc->lrc_callback().set(FUNC(hp2620_state::nlrc_w)).invert();

	mos6551_device &acia(MOS6551(config, "acia", 0)); // SY6551
	acia.set_xtal(25.7715_MHz_XTAL / 14); // 1.84 MHz
	acia.irq_handler().set_inputline("maincpu", INPUT_LINE_IRQ0);
	acia.rts_handler().set(m_datacomm, FUNC(rs232_port_device::write_rts)); // RS (J6-22)
	acia.dtr_handler().set(m_datacomm, FUNC(rs232_port_device::write_dtr)); // TR (J6-23)
	acia.txd_handler().set(m_datacomm, FUNC(rs232_port_device::write_txd)); // SD (J6-21)

	RS232_PORT(config, m_datacomm, default_rs232_devices, nullptr);
	m_datacomm->rxd_handler().set("acia", FUNC(mos6551_device::write_rxd)); // RD (J6-9)

	RIPPLE_COUNTER(config, m_bellctr); // LS393 at U114
	m_bellctr->set_stages(8);
	m_bellctr->count_out_cb().set(FUNC(hp2620_state::bell_w)).bit(4);

	SPEAKER(config, "mono").front_center(); // on keyboard
	SPEAKER_SOUND(config, m_bell).add_route(ALL_OUTPUTS, "mono", 0.50);
}

/**************************************************************************************************************

Hewlett-Packard HP-2622A.
Chips: National 8367 CRTC (labeled B8250), SY6551 (labeled 8251), Z8400A (Z80)
Crystal: 25.7715

***************************************************************************************************************/

ROM_START( hp2622a )
	ROM_REGION(0xc000, "maincpu", 0)
	ROM_LOAD( "1818-1685.xu63", 0x0000, 0x2000, CRC(a57ffe5e) SHA1(4d7844320deba916d9ec289927af987fea025b02) )
	ROM_LOAD( "1818-1686.xu64", 0x2000, 0x2000, CRC(bee9274c) SHA1(20796c559031a91cb2666776fcf7ffdb52a0a318) )
	ROM_LOAD( "1818-1687.xu65", 0x4000, 0x2000, CRC(e9ecd489) SHA1(9b249b8d066d256069ccdb8809bb808c414f106a) )
	// XU66-XU68 are empty sockets

	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD( "1818-1489.xu311", 0x0000, 0x2000, CRC(9879b153) SHA1(fc1705d6de38eb6d3a67f1ae439e359e5124d028) )
ROM_END

} // anonymous namespace


COMP(1982, hp2622a, 0, 0, hp2622, hp2622, hp2620_state, empty_init, "HP", "HP-2622A", MACHINE_NOT_WORKING)



hp2640.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:F. Ulivi, Gavin Scott
//
// **********************************
// Driver for HP2640-series terminals
// **********************************
//
// HP264x terminals are built around a few common elements:
// - Chassis with a passive card cage and power supply. Cards share a common bus
//   with a daisy-chained priority order (i.e. cards closer to power supply have
//   higher priority in accessing the bus). The bus also distributes a 4.9152 MHz
//   system clock.
// - White phosphor CRT (with its unusual 2:1 ratio) and the related drive board
// - Keyboard
// The "personality" in each version is in the set of cards installed in
// the card cage (and in a few minor differences of the common elements).
//
// *********************************************
// Driver for HP2641A APL terminal (Gavin Scott)
// *********************************************
//
// This driver implements the HP2641A APL Terminal which
// was used with APL\3000 on the HP 3000 Series II/III computers from 1976
// into the 1980s. I recently got APL\3000 running under simulation so this
// is an attempt to recreate the whole experience with the APL characters
// on the terminal device designed for it. The HP2641A is basically a 2645
// with a few altered ROMs (many are identical to the 2645) and one
// additional 2K ROM for which a second CTL PCA was required that was not
// needed in the 2645.
//
// This driver emulates the HP2641A model, as composed of the following cards:
// - 02640-60123    Keyboard interface
// - 02640-60112    Display control
// - 02640-60088    Display timing
// - 02640-60124    Display extended DMA
// - 02640-60024    Display enhancement
// - 02640-60209    CPU
// - 02640-60192    Control storage (firmware ROMs & 256-byte SRAM)
// - 02640-60192    Second CTL PCA with one additional 2K ROM and 256b SRAM.
// - 02640-60065    4k DRAM (4 of these for a 16k total)
// - 02640-60086    Asynchronous data comm
// - 02640-60137    CTU interface
// - 02640-60032    Read/write PCA
//
// The following table summarizes the emulated character sets. The 2641A only
// had room for a single optional character set (line drawing here) after the
// required 192 APL characters were accounted for.
//
// | Char. set | Description    | No. of chars |      ROMs | Type              |
// |-----------+----------------+--------------+-----------+-------------------|
// |         0 | Std ASCII set  |          128 | 1816-0612 | Alphanumeric      |
// |           |                |              | 1816-0613 | Alphanumeric      |
// |         1 | APL Symbols    |          128 | 1816-0984 | Alphanumeric      |
// |           | APL UC and sym |              | 1816-0985 | Alphanumeric      |
// |         2 | APL Overstruck |           64 | 1816-0986 | Alphanumeric      |
// |         3 | Line drawing   |           64 | 1816-1417 | 8-bit microvector |
//
// *************************************
// Driver for HP2645A terminal (F.Ulivi)
// *************************************
//
// This driver emulates the HP2645A model, as composed of the following cards:
// - 02640-60123    Keyboard interface
// - 02640-60112    Display control
// - 02640-60088    Display timing
// - 02640-60124    Display extended DMA
// - 02640-60024    Display enhancement
// - 02640-60209    CPU
// - 02640-60192    Control storage (firmware ROMs & 256-byte SRAM)
// - 02640-60065    4k DRAM (4 of these for a 16k total)
// - 02640-60086    Asynchronous data comm
// - 02640-60137    CTU interface
// - 02640-60032    Read/write PCA
//
// The following table summarizes the emulated character sets.
//
// | Char. set | Description    | No. of chars |      ROMs | Type              |
// |-----------+----------------+--------------+-----------+-------------------|
// |         0 | Std ASCII set  |          128 | 1816-0612 | Alphanumeric      |
// |           |                |              | 1816-0613 |                   |
// |         1 | Math symbols   |           64 | 1816-0642 | Alphanumeric      |
// |         2 | Line drawing   |           64 | 1816-1417 | 8-bit microvector |
// |         3 | Big characters |           64 | 1816-1425 | 8-bit microvector |
//
// All characters are rendered in a 9x15 cell.
// Fonts are rendered in different ways according to type: alphanumeric fonts
// take advantage of half-pixel shifting for aesthetic improvement, microvector fonts
// are rendered by mapping 8 bits to 9 pixels (bit 0 is displayed twice).
// Earlier versions of display hw used weird 9-bit PROMs for microvector fonts.
// Then, luckily, HP wised up and reverted to 8-bit memories.
//
// All non-volatile settings of this terminal are "stored" in mechanical switches
// and not in non-volatile memories (they were probably too expensive/limited/unreliable
// back then).
// There are 3 switches in a corner of the keyboard (data comm switches) and 24 option
// switches (named A to Z) on the keyboard interface PCB.
//
// Notes:
// - Speed of the CPU is not accurately emulated. The effects of bus sharing by
//   video DMA and CPU are not taken into account.
// - LEDs are not output.
// - RESET key is not implemented.
// - A few TODOs here & there.

#include "emu.h"
#include "screen.h"
#include "cpu/i8085/i8085.h"
#include "machine/timer.h"
#include "bus/rs232/rs232.h"
#include "machine/ay31015.h"
#include "machine/clock.h"
#include "hp2640_tape.h"
#include "sound/beep.h"
#include "emupal.h"
#include "speaker.h"

#include "hp2640.lh"

// Debugging
#define VERBOSE 0
#include "logmacro.h"

namespace {

// Bit manipulation
template<typename T> constexpr T BIT_MASK(unsigned n)
{
	return (T)1U << n;
}

template<typename T> void BIT_CLR(T& w , unsigned n)
{
	w &= ~BIT_MASK<T>(n);
}

template<typename T> void BIT_SET(T& w , unsigned n)
{
	w |= BIT_MASK<T>(n);
}

template<typename T> void COPY_BIT(bool bit , T& w , unsigned n)
{
	if (bit) {
		BIT_SET(w , n);
	} else {
		BIT_CLR(w , n);
	}
}

// **** Constants ****
constexpr auto SYS_CLOCK = XTAL(4'915'200);
constexpr auto VIDEO_DOT_CLOCK   = XTAL(21'060'000);
constexpr unsigned VIDEO_VIS_ROWS    = 24;
constexpr unsigned VIDEO_TOT_ROWS    = 25;
constexpr unsigned VIDEO_VIS_COLS    = 80;
constexpr unsigned VIDEO_TOT_COLS    = 104;
constexpr unsigned VIDEO_CHAR_WIDTH  = 9;
constexpr unsigned VIDEO_CHAR_HEIGHT = 15;
constexpr uint16_t START_DMA_ADDR   = 0xffff;
constexpr unsigned MAX_DMA_CYCLES   = 450;
constexpr unsigned CURSOR_BLINK_INH_MS  = 110;
constexpr unsigned BEEP_FREQUENCY   = 650;
constexpr unsigned BEEP_DURATION_MS = 100;

// ************
// hp2640_base
// ************
class hp2640_base_state : public driver_device
{
protected:
	hp2640_base_state(const machine_config &mconfig, device_type type, const char *tag , uint8_t m_cg_0 , uint8_t m_cg_1 , uint8_t m_cg_2 , uint8_t m_cg_3);

	void hp2640_base(machine_config &config);

	// Character generator settings
	enum : uint8_t {
		// OR-ENABLE (0) or AND-ENABLE (1)
		// *Ignored*
		CHARGEN_A = 0x01,
		// Alphanumeric (0) or Microvector (1)
		CHARGEN_B = 0x02,
		// 128 characters (0) or 64 characters (1)
		CHARGEN_C = 0x04,
		// D0 copy disabled (0) or enabled (1)
		// *Ignored as D0 copy is always enabled*
		CHARGEN_D = 0x08,
		// D0 copy on UC/LC (0) or LC only (1)
		// *Ignored, see CHARGEN_D*
		CHARGEN_E = 0x10
	};

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	IRQ_CALLBACK_MEMBER(irq_callback);

	void mode_byte_w(uint8_t data);
	TIMER_DEVICE_CALLBACK_MEMBER(timer_10ms_exp);

	uint8_t kb_r(offs_t offset);
	void kb_prev_w(uint8_t data);
	void kb_reset_w(uint8_t data);
	uint8_t switches_ah_r();
	uint8_t switches_jr_r();
	uint8_t switches_sz_r();
	uint8_t datacomm_sw_r();
	void kb_led_w(uint8_t data);

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	TIMER_DEVICE_CALLBACK_MEMBER(scanline_timer);
	void cx_w(uint8_t data);
	void cy_w(uint8_t data);
	TIMER_DEVICE_CALLBACK_MEMBER(timer_cursor_blink_inh);

	uint8_t async_status_r();
	void async_control_w(uint8_t data);
	void async_dav_w(int state);
	void async_txd_w(int state);

	TIMER_DEVICE_CALLBACK_MEMBER(timer_beep_exp);

	void tape_irq_w(int state);

	uint8_t poll_r();

	void cpu_mem_map(address_map &map) ATTR_COLD;
	void cpu_io_map(address_map &map) ATTR_COLD;

	required_device<i8080a_cpu_device> m_cpu;
	required_device<timer_device> m_timer_10ms;
	required_ioport_array<4> m_io_key;
	required_ioport m_io_comm;
	required_ioport m_io_sw_ah;
	required_ioport m_io_sw_jr;
	required_ioport m_io_sw_sz;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<timer_device> m_timer_cursor_blink_inh;
	required_device<rs232_port_device> m_rs232;
	required_device<ay51013_device> m_uart;
	required_device<clock_device> m_uart_clock;
	required_device<beep_device> m_beep;
	required_device<timer_device> m_timer_beep;
	required_device<hp2640_tape_device> m_tapes;
	memory_view m_io_view;

	uint8_t m_mode_byte;
	bool m_timer_irq;
	bool m_datacom_irq;
	bool m_tape_irq;

	// Character generators
	required_region_ptr_array<uint8_t , 4> m_chargen;

	const uint8_t m_chargen_set[ 4 ];

	// Video DMA
	struct line_buffer {
		uint8_t m_chars[ VIDEO_VIS_COLS ];
		uint8_t m_attrs[ VIDEO_VIS_COLS ];
	};
	struct line_buffer m_buffers[ 2 ];
	bool m_even;
	bool m_dma_on;  // U310-9
	bool m_line_done;
	uint16_t m_dma_addr;
	uint8_t m_row_counter;
	bool m_row_clock;   // U21-5
	bool m_row_reset;   // U11-1
	bool m_eop; // U311-9
	bool m_eol; // U211-9
	bool m_skipeol; // U311-5
	bool m_en_skipeol;  // U211-5

	// Video
	bitmap_rgb32 m_bitmap;
	uint8_t m_cursor_x;
	uint8_t m_cursor_y;
	bool m_cursor_blink_inh;
	bool m_blanking;

	// Async line interface
	uint8_t m_async_control;

	void update_irq();
	uint8_t video_dma_get();
	void video_load_buffer(bool buff_idx , unsigned& idx , uint8_t ch , bool iv , uint8_t attrs);
	void video_fill_buffer(bool buff_idx , unsigned max_cycles);
	void video_render_buffer(unsigned video_scanline , unsigned line_in_row , bool buff_idx , bool cyen);
	void update_async_control(uint8_t new_control);
	void update_async_irq();
};

hp2640_base_state::hp2640_base_state(const machine_config &mconfig, device_type type, const char *tag , uint8_t m_cg_0 , uint8_t m_cg_1 , uint8_t m_cg_2 , uint8_t m_cg_3)
	: driver_device(mconfig , type , tag),
	  m_cpu(*this , "cpu"),
	  m_timer_10ms(*this , "timer_10ms"),
	  m_io_key(*this , "KEY%u" , 0),
	  m_io_comm(*this , "comm"),
	  m_io_sw_ah(*this , "sw_ah"),
	  m_io_sw_jr(*this , "sw_jr"),
	  m_io_sw_sz(*this , "sw_sz"),
	  m_screen(*this , "screen"),
	  m_palette(*this , "palette"),
	  m_timer_cursor_blink_inh(*this , "timer_cursor_blink_inh"),
	  m_rs232(*this , "rs232"),
	  m_uart(*this , "uart"),
	  m_uart_clock(*this , "uart_clock"),
	  m_beep(*this , "beep"),
	  m_timer_beep(*this , "timer_beep"),
	  m_tapes(*this , "tapes"),
	  m_io_view(*this , "io_view"),
	  m_chargen(*this , "chargen%u" , 0),
	  m_chargen_set{ m_cg_0 , m_cg_1 , m_cg_2 , m_cg_3}
{
}

void hp2640_base_state::machine_start()
{
	m_screen->register_screen_bitmap(m_bitmap);

	// these are set in machine_reset, but device reset callbacks end up reading them before then
	// (machine_reset is called after all child devices are reset)
	m_mode_byte = false;
	m_timer_irq = m_datacom_irq = m_tape_irq = false;
	m_async_control = 0;

	m_even = true;
	m_line_done = false;
	m_dma_addr = 0;
	m_row_counter = 0;
	m_row_reset = false;

	// TODO: save more state
	save_item(NAME(m_mode_byte));
	save_item(NAME(m_timer_irq));
	save_item(NAME(m_datacom_irq));
	save_item(NAME(m_tape_irq));
}

void hp2640_base_state::machine_reset()
{
	m_mode_byte = 0;
	m_timer_irq = false;
	m_datacom_irq = false;
	m_tape_irq = false;
	m_timer_10ms->reset();
	update_irq();
	m_blanking = true;
	m_dma_on = true;
	m_eop = true;
	m_uart->write_xr(1);
	m_uart->write_swe(0);
	m_uart->write_cs(1);
	m_uart->write_nb2(1);
	m_async_control = 0;
	m_uart_clock->set_unscaled_clock(0);
	update_async_control(0x00);
	m_rs232->write_dtr(0);
	async_txd_w(0);
	m_beep->set_state(0);
}

IRQ_CALLBACK_MEMBER(hp2640_base_state::irq_callback)
{
	uint8_t res;

	// Encode interrupts in restart instruction (in order of decreasing priority)
	if (m_tape_irq) {
		// RST 5
		res = 0xef;
	} else if (m_datacom_irq && !BIT(m_mode_byte , 4)) {
		// RST 4
		res = 0xe7;
	} else if (m_timer_irq && !BIT(m_mode_byte , 5)) {
		// RST 3
		res = 0xdf;
	} else {
		// RST 0: should never happen (TM)
		res = 0xc7;
	}
	LOG("IRQ ACK %02x\n" , res);
	return res;
}

void hp2640_base_state::mode_byte_w(uint8_t data)
{
	if (BIT(m_mode_byte , 0) && !BIT(data , 0)) {
		m_timer_10ms->reset();
	} else if (!BIT(m_mode_byte , 0) && BIT(data , 0)) {
		m_timer_10ms->adjust(attotime::from_msec(10) , 0 , attotime::from_msec(10));
	}
	m_mode_byte = data;

	if (!BIT(m_mode_byte , 1)) {
		m_timer_irq = false;
	}
	update_irq();

	m_io_view.select(BIT(m_mode_byte , 6));
}

TIMER_DEVICE_CALLBACK_MEMBER(hp2640_base_state::timer_10ms_exp)
{
	if (BIT(m_mode_byte , 1)) {
		m_timer_irq = true;
		update_irq();
	}
}

uint8_t hp2640_base_state::kb_r(offs_t offset)
{
	ioport_value k = m_io_key[ offset / 4 ]->read();

	return uint8_t(k >> (8 * (offset % 4)));
}

void hp2640_base_state::kb_prev_w(uint8_t data)
{
	// This port is used to set the threshold in key sense circuit for hysteresis.
	// We can safely ignore all writes.
}

void hp2640_base_state::kb_reset_w(uint8_t data)
{
	// TODO: enabled/disable CPU reset
}

uint8_t hp2640_base_state::switches_ah_r()
{
	uint8_t res = m_io_sw_ah->read();
	LOG("SW AH=%02x\n" , res);
	return res;
}

uint8_t hp2640_base_state::switches_jr_r()
{
	uint8_t res = m_io_sw_jr->read();
	LOG("SW JR=%02x\n" , res);
	return res;
}

uint8_t hp2640_base_state::switches_sz_r()
{
	uint8_t res = m_io_sw_sz->read();
	LOG("SW SZ=%02x\n" , res);
	return res;
}

uint8_t hp2640_base_state::datacomm_sw_r()
{
	uint8_t res = m_io_comm->read();
	LOG("COM SW=%02x\n" , res);
	return res;
}

void hp2640_base_state::kb_led_w(uint8_t data)
{
	if (BIT(data , 7)) {
		m_beep->set_state(1);
		m_timer_beep->adjust(attotime::from_msec(BEEP_DURATION_MS));
	}
	// TODO:
	LOG("LED = %02x\n" , data);
}

uint32_t hp2640_base_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
	return 0;
}

TIMER_DEVICE_CALLBACK_MEMBER(hp2640_base_state::scanline_timer)
{
	unsigned video_scanline = param;
	if (video_scanline < VIDEO_VIS_ROWS * VIDEO_CHAR_HEIGHT) {
		unsigned row = video_scanline / VIDEO_CHAR_HEIGHT;
		unsigned line_in_row = video_scanline - row * VIDEO_CHAR_HEIGHT;

		if (line_in_row == 0) {
			if (row == VIDEO_VIS_ROWS - 1) {
				// Start of V blank (GVS)
				m_even = true;
				m_row_reset = true;
				m_row_counter = 0;
				m_dma_addr = START_DMA_ADDR;
				m_eop = m_blanking;
				m_skipeol = false;
			} else {
				m_even = !m_even;
			}
			m_line_done = false;
			video_fill_buffer(m_even , MAX_DMA_CYCLES);
		};
		video_render_buffer(video_scanline , line_in_row , !m_even , row == m_cursor_y);
	}
}

void hp2640_base_state::cx_w(uint8_t data)
{
	m_cursor_x = data & 0x7f;
	m_cursor_blink_inh = true;
	m_timer_cursor_blink_inh->adjust(attotime::from_msec(CURSOR_BLINK_INH_MS));
}

void hp2640_base_state::cy_w(uint8_t data)
{
	m_blanking = BIT(data , 7);
	m_cursor_y = data & 0x1f;
	m_dma_on = m_blanking || !BIT(data , 6);
	if (m_cursor_y == m_row_counter) {
		LOG("ROW MATCH %02x\n" , data);
		if (!BIT(data , 6) && BIT(data , 5)) {
			LOG("EOP on cy\n");
			m_eop = true;
		}
		if (BIT(data , 6) && !BIT(data , 5) && m_en_skipeol) {
			LOG("SKIPEOL on cy\n");
			m_skipeol = true;
		}
	}
	if (m_dma_on) {
		// Fill buffer if DMA is enabled for the 1st time on the current line
		// TODO: compute correct max. no. of cycles
		video_fill_buffer(m_even , MAX_DMA_CYCLES);
	}
	m_cursor_blink_inh = true;
	m_timer_cursor_blink_inh->adjust(attotime::from_msec(CURSOR_BLINK_INH_MS));
}

TIMER_DEVICE_CALLBACK_MEMBER(hp2640_base_state::timer_cursor_blink_inh)
{
	m_cursor_blink_inh = false;
}

uint8_t hp2640_base_state::async_status_r()
{
	uint8_t res = 0;

	if (m_uart->dav_r()) {
		BIT_SET(res, 0);
	}
	if (m_uart->tbmt_r()) {
		BIT_SET(res, 1);
	}
	if (m_uart->or_r()) {
		BIT_SET(res, 2);
	}
	if (m_uart->pe_r()) {
		BIT_SET(res, 3);
	}
	if (m_rs232->dcd_r()) {
		BIT_SET(res, 4);
	}
	if (m_rs232->cts_r()) {
		BIT_SET(res, 5);
	}
	// Secondary ch. receive always 1
	BIT_SET(res, 6);
	LOG("ASYNC ST=%02x\n" , res);
	return res;
}

void hp2640_base_state::async_control_w(uint8_t data)
{
	update_async_control(data);
}

void hp2640_base_state::async_dav_w(int state)
{
	update_async_irq();
}

void hp2640_base_state::async_txd_w(int state)
{
	m_rs232->write_txd(!BIT(m_async_control , 6) && m_uart->so_r());
}

TIMER_DEVICE_CALLBACK_MEMBER(hp2640_base_state::timer_beep_exp)
{
	m_beep->set_state(0);
}

void hp2640_base_state::tape_irq_w(int state)
{
	m_tape_irq = state;
	update_irq();
}

uint8_t hp2640_base_state::poll_r()
{
	uint8_t res = m_tapes->poll_r();
	LOG("POLL %02x\n" , res);
	return res;
}

void hp2640_base_state::update_irq()
{
	bool state = m_tape_irq ||
		(m_datacom_irq && !BIT(m_mode_byte , 4)) ||
		(m_timer_irq && !BIT(m_mode_byte , 5));
	m_cpu->set_input_line(I8085_INTR_LINE , state);
}

uint8_t hp2640_base_state::video_dma_get()
{
	uint8_t b = m_cpu->space(AS_PROGRAM).read_byte(m_dma_addr | 0xc000);
	m_dma_addr--;
	return b;
}

void hp2640_base_state::video_load_buffer(bool buff_idx , unsigned& idx , uint8_t ch , bool iv , uint8_t attrs)
{
	COPY_BIT(iv, ch, 7);
	m_buffers[ buff_idx ].m_chars[ idx ] = ch;
	// Real hw only stores bits 5,4,3,2,0
	m_buffers[ buff_idx ].m_attrs[ idx ] = attrs;
	idx++;
	m_en_skipeol = false;
	m_row_reset = false;
}

void hp2640_base_state::video_fill_buffer(bool buff_idx , unsigned max_cycles)
{
	if (m_line_done || !m_dma_on) {
		return;
	}
	m_line_done = true;
	m_row_clock = true;
	m_eol = false;
	bool curr_iv = false;   // U310-5
	uint8_t curr_attrs = 0;
	bool link_lsb = false;
	uint8_t dma_byte = 0;

	for (unsigned i = 0 , mem_cycles = 0; i < VIDEO_VIS_COLS && mem_cycles < max_cycles; mem_cycles++) {
		if (link_lsb) {
			uint8_t lsb = video_dma_get();
			if ((lsb & 0xf) != 0xf) {
				// This link is a pointer to a new row
				if (m_row_clock) {
					m_row_clock = false;
					if (!m_row_reset) {
						m_row_counter++;
					}
				}
				m_skipeol = false;
			}
			m_dma_addr = (uint16_t(dma_byte) << 8) | lsb;
			link_lsb = false;
		} else if (m_eop || m_eol) {
			curr_iv = false;
			curr_attrs = 0;
			// Load spaces
			video_load_buffer(buff_idx, i, 0x20, false, 0);
		} else {
			dma_byte = video_dma_get();
			// Decode byte type
			if (!BIT(dma_byte , 7)) {
				// 0xxx-xxxx
				// DATA byte (ordinary character)
				if (!m_skipeol) {
					video_load_buffer(buff_idx, i, dma_byte, curr_iv, curr_attrs);
				}
			} else if (!BIT(dma_byte , 6)) {
				// 10xx-xxxx
				// CONTROL byte
				// Bit(s)   Content
				// ================
				// 5..4     Character set selection
				// 3        Half-brightness
				// 2        Underline
				// 1        Inverse video
				// 0        Blink
				curr_iv = BIT(dma_byte , 1);
				curr_attrs = dma_byte;
			} else if (BIT(dma_byte , 5) || BIT(dma_byte , 4)) {
				// 111x-xxxx or 1101-xxxx
				// Link MSB
				link_lsb = true;
			} else if (!BIT(dma_byte , 3) || !BIT(dma_byte , 2)) {
				// 1100-0xxx or 1100-10xx
				// Flags: ignored
			} else if (!BIT(dma_byte , 1)) {
				// 1100-110x
				// EOL
				if (!m_skipeol) {
					m_eol = true;
					m_en_skipeol = true;
				}
				m_skipeol = false;
			} else if (!BIT(dma_byte , 0)) {
				// 1100-1110
				// EOP
				m_eop = true;
			} else {
				// 1100-1111
				// "O" data byte
				if (!m_skipeol) {
					video_load_buffer(buff_idx, i, dma_byte, curr_iv, curr_attrs);
				}
			}
		}
	}
}

void hp2640_base_state::video_render_buffer(unsigned video_scanline , unsigned line_in_row , bool buff_idx , bool cyen)
{
	if (m_blanking) {
		m_bitmap.fill(rgb_t::black() , rectangle(0 , VIDEO_VIS_COLS * VIDEO_CHAR_WIDTH * 2 , video_scanline , video_scanline));
		return;
	}

	bool cursor_blink;
	unsigned fn = (unsigned)m_screen->frame_number();
	unsigned fn_12 = fn / 12;
	if (m_cursor_blink_inh) {
		cursor_blink = true;
		if (BIT(fn , 0)) {
			cyen = false;
		}
	} else {
		cursor_blink = BIT(fn_12 , 0);
	}

	for (unsigned i = 0 , x_left = 0; i < VIDEO_VIS_COLS; i++ , x_left += VIDEO_CHAR_WIDTH * 2) {
		uint8_t ch = m_buffers[ buff_idx ].m_chars[ i ];
		bool iv = BIT(ch , 7);
		BIT_CLR(ch, 7);
		uint8_t attrs = m_buffers[ buff_idx ].m_attrs[ i ];
		uint8_t char_set = (attrs >> 4) & 3;
		uint8_t chargen_set = m_chargen_set[ char_set ];
		uint16_t ch_addr = ch & 0x1f;
		if (BIT(ch , 6)) {
			BIT_SET(ch_addr, 5);
		}
		uint8_t byte;
		if ((ch <= 0x1f || ch >= 0x60) && (chargen_set & CHARGEN_C) == 0) {
			if (m_chargen[ char_set ].bytes() >= 0x800) {
				// Read from LC ROM
				byte = ~m_chargen[ char_set ][ (ch_addr << 4) + line_in_row + 0x400 ];
			} else {
				// LC ROM not installed
				byte = 0;
			}
		} else if (ch >= 0x20) {
			// Read from UC ROM
			byte = ~m_chargen[ char_set ][ (ch_addr << 4) + line_in_row ];
		} else {
			byte = 0;
		}
		uint16_t pixels_e;
		uint16_t pixels_o;
		bool microvector = chargen_set & CHARGEN_B;

		if (cyen && (line_in_row == 11 || line_in_row == 12) && i == m_cursor_x) {
			pixels_e = pixels_o = cursor_blink ? ~0 : 0;
		} else if (BIT(fn_12 , 1) && BIT(attrs , 0)) {
			pixels_e = pixels_o = 0;
		} else if (line_in_row == 11 && BIT(attrs , 2)) {
			pixels_e = pixels_o = ~0;
		} else if (microvector) {
			pixels_e = pixels_o = uint16_t(byte) << 1;
			// Copy b0
			if (BIT(pixels_e , 1)) {
				BIT_SET(pixels_e, 0);
				BIT_SET(pixels_o, 0);
			}
		} else {
			bool half_shift = BIT(byte , 0);
			pixels_e = pixels_o = uint16_t(byte & 0xfe);
			if (half_shift) {
				pixels_e <<= 1;
				if (BIT(pixels_o , 7)) {
					BIT_SET(pixels_o, 8);
				}
			}
		}
		if (iv) {
			pixels_e = ~pixels_e;
			pixels_o = ~pixels_o;
		}
		unsigned on_pen = BIT(attrs , 3) ? 1 : 2;
		for (unsigned x = 0; x < VIDEO_CHAR_WIDTH * 2; x += 2) {
			m_bitmap.pix(video_scanline , x_left + x) = m_palette->pen(BIT(pixels_e , 0) ? on_pen : 0);
			m_bitmap.pix(video_scanline , x_left + x + 1) = m_palette->pen(BIT(pixels_o , 0) ? on_pen : 0);
			pixels_e >>= 1;
			pixels_o >>= 1;
		}
	}
}

static const unsigned baud_rate_divisors[] = {
	0,      // 0: external clock (here: no clock)
	2800,   // 1: 110 baud
	2048,   // 2: 150 baud
	1024,   // 3: 300 baud
	256,    // 4: 1200 baud
	128,    // 5: 2400 baud
	64,     // 6: 4800 baud
	32      // 7: 9600 baud
};

void hp2640_base_state::update_async_control(uint8_t new_control)
{
	LOG("ASYNC CT=%02x\n" , new_control);
	uint8_t diff = m_async_control ^ new_control;
	m_async_control = new_control;
	m_rs232->write_rts(BIT(m_async_control , 0));
	if (diff & 0x0e) {
		unsigned new_rate_idx = (m_async_control >> 1) & 0x07;
		// Set baud rate
		double rxc_txc_freq;
		if (new_rate_idx == 0) {
			rxc_txc_freq = 0.0;
		} else {
			rxc_txc_freq = SYS_CLOCK.dvalue() / baud_rate_divisors[ new_rate_idx ];
		}
		m_uart_clock->set_unscaled_clock(rxc_txc_freq);
		m_uart->write_tsb(new_rate_idx == 1);
		LOG("ASYNC freq=%f\n" , rxc_txc_freq);
	}
	if (diff & 0x30) {
		m_uart->write_np(BIT(new_control , 5));
		m_uart->write_nb1(BIT(new_control , 5));
		m_uart->write_eps(BIT(new_control , 4));
	}
	// Update TxD
	async_txd_w(0);
}

void hp2640_base_state::update_async_irq()
{
	m_datacom_irq = m_uart->dav_r();
	LOG("ASYNC IRQ=%d\n" , m_datacom_irq);
	update_irq();
}

#define IOP_MASK(x) BIT_MASK<ioport_value>((x))

static INPUT_PORTS_START(hp2640_base)
	PORT_START("KEY0")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)        // 000 CONTROL
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))        // 001 ESC
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')                      // 002 TAB
	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)          // 003 LSHIFT
	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_MAMEKEY(RSHIFT))  // 004 RSHIFT
	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))    // 005 PAD1
	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))    // 006 PAD4
	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)                   // 007 BS
	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_TOGGLE PORT_NAME("Remote")                             // 010 REMOTE
	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')          // 011 1
	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')          // 012 Q
	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')          // 013 Z
	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("RETURN")  // 014 RETURN
	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))    // 015 PAD2
	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))    // 016 PAD5
	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_TILDE) PORT_CHAR('\\') PORT_CHAR('|')     // 017 BACKSLASH
	PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_NAME("Caps lock")  // 020 CAPS LOCK
	PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')          // 021 2
	PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')          // 022 W
	PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')          // 023 X
	PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(']') PORT_CHAR('}')  // 024 ]
	PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))    // 025 PAD3
	PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD))    // 026 PAD6
	PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_NAME("f5")  // 027 F5
	PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_TOGGLE PORT_NAME("Memory lock")                        // 030 MEMORY LOCK
	PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')          // 031 3
	PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')          // 032 E
	PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')          // 033 C
	PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')      // 034 :
	PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))      // 035 LEFT
	PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Roll up")                                        // 036 ROLL UP
	PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_NAME("f6")  // 037 F6
	PORT_START("KEY1")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_TOGGLE PORT_NAME("Auto LF")                            // 040 AUTO LF
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')          // 041 4
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')          // 042 R
	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')          // 043 V
	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')      // 044 ;
	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))      // 045 HOME
	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))          // 046 UP
	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_NAME("f7")  // 047 F7
	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_NAME("Test")                                           // 050 TEST
	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')          // 051 5
	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')          // 052 T
	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')          // 053 B
	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')          // 054 L
	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))    // 055 RIGHT
	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_PGDN) PORT_NAME("Next page")              // 056 NEXT PAGE
	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_NAME("f8")  // 057 F8
	PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                // 060
	PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')          // 061 6
	PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')          // 062 Y
	PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')                     // 063 SPACE
	PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')          // 064 K
	PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_PGUP) PORT_NAME("Prev page")              // 065 PREV PAGE
	PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Clear dsply")                                    // 066 CLEAR DSPLY
	PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_INSERT) PORT_NAME("Insert char")          // 067 INSERT CHAR
	PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Display functions")                              // 070 DISPLAY FUNCTIONS
	PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')         // 071 7
	PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')          // 072 U
	PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')          // 073 N
	PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')          // 074 J
	PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))      // 075 DOWN
	PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Set tab")                                        // 076 SET TAB
	PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_DEL) PORT_NAME("Delete char")             // 077 DELETE CHAR
	PORT_START("KEY2")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_TOGGLE PORT_NAME("Block mode")                         // 100 BLOCK MODE
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')          // 101 8
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')          // 102 I
	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')          // 103 M
	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')          // 104 H
	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_NAME("Roll down")                                      // 105 ROLL DOWN
	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_NAME("Clear tab")                                      // 106 CLEAR TAB
	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_NAME("Delete line")                                    // 107 DELETE LINE
	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_NAME("Green")                                          // 110 GREEN
	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_NAME("Enter")                                          // 111 ENTER
	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')          // 112 O
	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')      // 113 ,
	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')          // 114 G
	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR('.')                   // 115 PAD.
	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))    // 116 PAD9
	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Insert line")                                    // 117 INSERT LINE
	PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Read")                                           // 120 READ
	PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')          // 121 9
	PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')          // 122 P
	PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')       // 123 .
	PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')          // 124 F
	PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))    // 125 PAD0
	PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))    // 126 PAD8
	PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_NAME("f4")  // 127 F4
	PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Record")                                         // 130 RECORD
	PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_0) PORT_CHAR('0')                         // 131 0
	PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@') PORT_CHAR('`')  // 132 @
	PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')      // 133 /
	PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')          // 134 D
	PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                // 135
	PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))    // 136 PAD7
	PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_NAME("f3")  // 137 F3
	PORT_START("KEY3")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_NAME("Gold")                                           // 140 GOLD
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')      // 141 -
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[') PORT_CHAR('{') // 142 [
	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                 // 143
	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')          // 144 S
	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                 // 145
	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                 // 146
	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_NAME("f2")  // 147 F2
	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_NAME("Transmit")                                       // 150 TRANSMIT
	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') PORT_CHAR('~')     // 151 ^
	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('_')                // 152 _
	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                // 153
	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')          // 154 A
	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                // 155
	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                // 156
	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_NAME("f1")  // 157 F1

	// Default: Full duplex, no parity, 9600 bps
	PORT_START("comm")
	PORT_CONFNAME(0x80, 0x80, "Duplex")
	PORT_CONFSETTING(0x80, "Full")
	PORT_CONFSETTING(0x00, "Half")
	PORT_CONFNAME(0x30, 0x20, "Parity")
	PORT_CONFSETTING(0x00, "Odd")
	PORT_CONFSETTING(0x10, "Even")
	PORT_CONFSETTING(0x20, "None")
	PORT_CONFNAME(0x0e, 0x0e, "Baud rate")
	PORT_CONFSETTING(0x00, "Ext")
	PORT_CONFSETTING(0x02, "110")
	PORT_CONFSETTING(0x04, "150")
	PORT_CONFSETTING(0x06, "300")
	PORT_CONFSETTING(0x08, "1200")
	PORT_CONFSETTING(0x0a, "2400")
	PORT_CONFSETTING(0x0c, "4800")
	PORT_CONFSETTING(0x0e, "9600")

	// Default: all switches closed except R, U & V
	// This setting should work for standard character-oriented I/O
	PORT_START("sw_ah")
	PORT_CONFNAME(0x01, 0x00, "Switch A")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x01, DEF_STR(Off))
	PORT_CONFNAME(0x02, 0x00, "Switch B")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x02, DEF_STR(Off))
	PORT_CONFNAME(0x04, 0x00, "Switch C")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x04, DEF_STR(Off))
	PORT_CONFNAME(0x08, 0x00, "Switch D")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x08, DEF_STR(Off))
	PORT_CONFNAME(0x10, 0x00, "Switch E")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x10, DEF_STR(Off))
	PORT_CONFNAME(0x20, 0x00, "Switch F")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x20, DEF_STR(Off))
	PORT_CONFNAME(0x40, 0x00, "Switch G")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x40, DEF_STR(Off))
	PORT_CONFNAME(0x80, 0x00, "Switch H")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x80, DEF_STR(Off))
	PORT_START("sw_jr")
	PORT_CONFNAME(0x01, 0x00, "Switch J")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x01, DEF_STR(Off))
	PORT_CONFNAME(0x02, 0x00, "Switch K")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x02, DEF_STR(Off))
	PORT_CONFNAME(0x04, 0x00, "Switch L")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x04, DEF_STR(Off))
	PORT_CONFNAME(0x08, 0x00, "Switch M")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x08, DEF_STR(Off))
	PORT_CONFNAME(0x10, 0x00, "Switch N")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x10, DEF_STR(Off))
	PORT_CONFNAME(0x20, 0x00, "Switch P")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x20, DEF_STR(Off))
	PORT_CONFNAME(0x40, 0x00, "Switch Q")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x40, DEF_STR(Off))
	PORT_CONFNAME(0x80, 0x80, "Switch R")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x80, DEF_STR(Off))
	PORT_START("sw_sz")
	PORT_CONFNAME(0x01, 0x00, "Switch S")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x01, DEF_STR(Off))
	PORT_CONFNAME(0x02, 0x00, "Switch T")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x02, DEF_STR(Off))
	PORT_CONFNAME(0x04, 0x04, "Switch U")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x04, DEF_STR(Off))
	PORT_CONFNAME(0x08, 0x08, "Switch V")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x08, DEF_STR(Off))
	PORT_CONFNAME(0x10, 0x00, "Switch W")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x10, DEF_STR(Off))
	PORT_CONFNAME(0x20, 0x00, "Switch X")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x20, DEF_STR(Off))
	PORT_CONFNAME(0x40, 0x00, "Switch Y")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x40, DEF_STR(Off))
	PORT_CONFNAME(0x80, 0x00, "Switch Z")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x80, DEF_STR(Off))
INPUT_PORTS_END

void hp2640_base_state::cpu_mem_map(address_map &map)
{
	map.unmap_value_low();
	map(0x0000, 0x57ff).rom();
	map(0x8000, 0x8fff).view(m_io_view);

	// View 0 is for normal I/O
	// View 1 is for poll read
	// Writing is independent of poll state
	m_io_view[ 0 ](0x0100, 0x0100).r(m_uart, FUNC(ay51013_device::receive));
	m_io_view[ 0 ](0x0120, 0x0120).r(FUNC(hp2640_base_state::async_status_r));

	map(0x8140, 0x8140).w(FUNC(hp2640_base_state::async_control_w));
	map(0x8160, 0x8160).w(m_uart, FUNC(ay51013_device::transmit));
	map(0x8300, 0x8300).w(FUNC(hp2640_base_state::kb_led_w));

	m_io_view[ 0 ](0x0300, 0x030d).r(FUNC(hp2640_base_state::kb_r));
	m_io_view[ 0 ](0x030e, 0x030e).r(FUNC(hp2640_base_state::switches_ah_r));
	m_io_view[ 0 ](0x030f, 0x030f).r(FUNC(hp2640_base_state::datacomm_sw_r));

	map(0x8320, 0x8320).w(FUNC(hp2640_base_state::kb_prev_w));
	map(0x8380, 0x8380).w(FUNC(hp2640_base_state::kb_reset_w));

	m_io_view[ 0 ](0x0380, 0x0380).r(FUNC(hp2640_base_state::switches_jr_r));
	m_io_view[ 0 ](0x03a0, 0x03a0).r(FUNC(hp2640_base_state::switches_sz_r));

	map(0x8700, 0x8700).w(FUNC(hp2640_base_state::cx_w));
	map(0x8720, 0x8720).w(FUNC(hp2640_base_state::cy_w));
	map(0x8b00, 0x8b00).w(m_tapes, FUNC(hp2640_tape_device::command_w));

	m_io_view[ 0 ](0x0b00, 0x0b00).r(m_tapes, FUNC(hp2640_tape_device::status_r));

	map(0x8b20, 0x8b20).w(m_tapes, FUNC(hp2640_tape_device::data_w));

	m_io_view[ 0 ](0x0b20, 0x0b20).r(m_tapes, FUNC(hp2640_tape_device::data_r));

	m_io_view[ 1 ](0x0000, 0x0fff).r(FUNC(hp2640_base_state::poll_r));

	map(0x9100, 0x91ff).ram();
	map(0xc000, 0xffff).ram();
}

void hp2640_base_state::cpu_io_map(address_map &map)
{
	map.unmap_value_low();
	map(0x00, 0xff).w(FUNC(hp2640_base_state::mode_byte_w));
}

void hp2640_base_state::hp2640_base(machine_config &config)
{
	I8080A(config, m_cpu, SYS_CLOCK / 2);
	m_cpu->set_addrmap(AS_PROGRAM, &hp2640_base_state::cpu_mem_map);
	m_cpu->set_addrmap(AS_IO, &hp2640_base_state::cpu_io_map);
	m_cpu->set_irq_acknowledge_callback(FUNC(hp2640_base_state::irq_callback));

	TIMER(config, m_timer_10ms).configure_generic(FUNC(hp2640_base_state::timer_10ms_exp));
	TIMER(config, m_timer_cursor_blink_inh).configure_generic(FUNC(hp2640_base_state::timer_cursor_blink_inh));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER, rgb_t::white());
	// Actual pixel clock is half this value: 21.06 MHz
	// We use the doubled value to be able to emulate the half-pixel shifting of the real hw
	// Each real-world half pixel is a whole MAME pixel
	m_screen->set_raw(VIDEO_DOT_CLOCK * 2 ,
						   VIDEO_TOT_COLS * VIDEO_CHAR_WIDTH * 2 , 0 , VIDEO_VIS_COLS * VIDEO_CHAR_WIDTH * 2 ,
						   VIDEO_TOT_ROWS * VIDEO_CHAR_HEIGHT , 0 , VIDEO_VIS_ROWS * VIDEO_CHAR_HEIGHT);
	m_screen->set_screen_update(FUNC(hp2640_base_state::screen_update));
	TIMER(config, "scantimer").configure_scanline(FUNC(hp2640_base_state::scanline_timer), "screen", 0, 1);
	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);
	config.set_default_layout(layout_hp2640);

	// RS232
	RS232_PORT(config, m_rs232, default_rs232_devices , nullptr);

	// UART (TR1602B)
	AY51013(config, m_uart);
	m_uart->read_si_callback().set(m_rs232, FUNC(rs232_port_device::rxd_r));
	m_uart->write_so_callback().set(FUNC(hp2640_base_state::async_txd_w));
	m_uart->write_dav_callback().set(FUNC(hp2640_base_state::async_dav_w));
	m_uart->set_auto_rdav(true);

	CLOCK(config, m_uart_clock, 19200 * 16);
	m_uart_clock->signal_handler().set(m_uart, FUNC(ay51013_device::write_rcp));
	m_uart_clock->signal_handler().append(m_uart, FUNC(ay51013_device::write_tcp));

	// Beep
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, BEEP_FREQUENCY).add_route(ALL_OUTPUTS, "mono", 1.00);
	TIMER(config, m_timer_beep).configure_generic(FUNC(hp2640_base_state::timer_beep_exp));

	// Tape drives
	HP2640_TAPE(config , m_tapes , SYS_CLOCK);
	m_tapes->irq().set(FUNC(hp2640_base_state::tape_irq_w));
}

// ************
// hp2641_state
// ************
class hp2641_state : public hp2640_base_state
{
public:
	hp2641_state(const machine_config &mconfig, device_type type, const char *tag);

	void hp2641(machine_config &config);

protected:
	void cpu_mem_map(address_map &map) ATTR_COLD;
};

hp2641_state::hp2641_state(const machine_config &mconfig, device_type type, const char *tag)
	: hp2640_base_state(mconfig, type, tag, 0, 0, 0, CHARGEN_B | CHARGEN_C)
{
}

void hp2641_state::hp2641(machine_config &config)
{
	hp2640_base(config);
	m_cpu->set_addrmap(AS_PROGRAM, &hp2641_state::cpu_mem_map);
}

void hp2641_state::cpu_mem_map(address_map &map)
{
	hp2640_base_state::cpu_mem_map(map);
	map(0x7000, 0x77ff).rom(); // The extra APL code ROM
	map(0x9000, 0x90ff).ram(); // 256 bytes of RAM on second CTL PCA board
}

static INPUT_PORTS_START(hp2641)
	PORT_INCLUDE(hp2640_base)

	PORT_MODIFY("KEY0")
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_NAME("LINE FEED") PORT_CHAR(0x0a)                      // 001 LINE FEED
	PORT_BIT(IOP_MASK(24), IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_NAME("APL")                                            // 030 APL
	PORT_MODIFY("KEY3")
	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD)   PORT_NAME("BREAK")                                          // 150 BREAK
INPUT_PORTS_END

ROM_START(hp2641)
	ROM_REGION(0x7800, "cpu", 0) // ROMs on first CTL PCA
	ROM_LOAD("1818-0512.bin", 0x0000, 0x800, CRC(1796F4FC) SHA1(1BDEC49B3A937F9D52E143319E4EA3B42DD34FF2))
	ROM_LOAD("1818-0287.bin", 0x0800, 0x800, CRC(717C01A6) SHA1(2BA2362B658A095126A5BFD533A0B35B0EEFC6EC))
	ROM_LOAD("1818-0448.bin", 0x1000, 0x800, CRC(15B09E97) SHA1(7975d04fcf24490b3c16b2ac261754d2ad848017))
	ROM_LOAD("1818-0206.bin", 0x1800, 0x800, CRC(788f8464) SHA1(94c55390801c193bb395855d3a0f186c1d1fd498))
	ROM_LOAD("1818-0273.bin", 0x2000, 0x800, CRC(0DA3F328) SHA1(1FDD7391CBB64BAEE0D69DA147EE2AF123971A61))
	ROM_LOAD("1818-0208.bin", 0x2800, 0x800, CRC(1eca0ff8) SHA1(630a533efe53d3643f652a5d9d6503ab7f47d4e5))
	ROM_LOAD("1818-0209.bin", 0x3000, 0x800, CRC(82e02695) SHA1(eaa7010f4672320116a1f319f96aeb078ce79609))
	ROM_LOAD("1818-0210.bin", 0x3800, 0x800, CRC(A1EBB6AB) SHA1(71B2BEEFF3817D9C87870983BF1653F682B15D21))
	ROM_LOAD("1818-0426.bin", 0x4000, 0x800, CRC(9C49FB37) SHA1(15F2F236F567A6234F9DE8841FE9A6C246022127))
	ROM_LOAD("1818-0275.bin", 0x4800, 0x800, CRC(462175D1) SHA1(DD6974876177453D420201398F88C8389CA8240D))
	ROM_LOAD("1818-0513.bin", 0x5000, 0x800, CRC(416d1a4d) SHA1(6ce7136f36f06fc44716da1970b96a3ef29e5b13))
	// Second CTL PCA. Is this gap in the region a problem? Not so far!
	ROM_LOAD("1818-0276.bin", 0x7000, 0x800, CRC(D061D1B8) SHA1(91FA0541640DF6A0EB8A3DC2C7CAB0EE5967EAAE))

	ROM_REGION(0x800, "chargen0", 0) // HP Roman UC and LC ROMs
	ROM_LOAD("1816-0612.bin", 0x0000, 0x400, CRC(5d7befd6) SHA1(31357e7b8630f52698f1b6825e79c7a51ff3f245))
	ROM_LOAD("1816-0613.bin", 0x0400, 0x400, CRC(b6bac431) SHA1(42a557ecff769425d295ebbd1b73b26ddbfd3a09))

	ROM_REGION(0x800, "chargen1", 0) // APL 1 and APL 2 Character ROMs
	ROM_LOAD("1816-0984.bin", 0x0000, 0x400, CRC(0CEAE759) SHA1(04634EE6271C69D5AC399E547A8738B8AB22EA4A))
	ROM_LOAD("1816-0985.bin", 0x0400, 0x400, CRC(25FCC682) SHA1(E2160ACB34E972ECF59590550327EE4471982AAF))

	ROM_REGION(0x400, "chargen2", 0) // APL 3 Overstruck character ROM
	ROM_LOAD("1816-0986.bin", 0x0000, 0x400, CRC(21D68D26) SHA1(EC76FB234B97106AA2DB3BE163C75864C785C08F))

	ROM_REGION(0x400, "chargen3", 0) // Line Drawing (microvector format)
	ROM_LOAD("1816-1417.bin", 0x0000, 0x400, CRC(E91343A4) SHA1(B37BE2F3699BC8766435B5EE3775D36510DF8D1E))
ROM_END

// ************
// hp2645_state
// ************
class hp2645_state : public hp2640_base_state
{
public:
	hp2645_state(const machine_config &mconfig, device_type type, const char *tag);

	void hp2645(machine_config &config);
};

hp2645_state::hp2645_state(const machine_config &mconfig, device_type type, const char *tag)
	: hp2640_base_state(mconfig, type, tag, 0, CHARGEN_C, CHARGEN_B | CHARGEN_C, CHARGEN_B | CHARGEN_C)
{
}

void hp2645_state::hp2645(machine_config &config)
{
	hp2640_base(config);
}

ROM_START(hp2645)
	ROM_REGION(0x5800 , "cpu" , 0)
	ROM_LOAD("1818-0512.bin", 0x0000, 0x800, CRC(1796f4fc) SHA1(1bdec49b3a937f9d52e143319e4ea3b42dd34ff2))
	ROM_LOAD("1818-0287.bin", 0x0800, 0x800, CRC(717c01a6) SHA1(2ba2362b658a095126a5bfd533a0b35b0eefc6ec))
	ROM_LOAD("1818-0448.bin", 0x1000, 0x800, CRC(15b09e97) SHA1(7975d04fcf24490b3c16b2ac261754d2ad848017))
	ROM_LOAD("1818-0206.bin", 0x1800, 0x800, CRC(788f8464) SHA1(94c55390801c193bb395855d3a0f186c1d1fd498))
	ROM_LOAD("1818-0207.bin", 0x2000, 0x800, CRC(de7186a9) SHA1(aaf5d29f95e5417320e3af573fdcc7ab03922e3d))
	ROM_LOAD("1818-0208.bin", 0x2800, 0x800, CRC(1eca0ff8) SHA1(630a533efe53d3643f652a5d9d6503ab7f47d4e5))
	ROM_LOAD("1818-0209.bin", 0x3000, 0x800, CRC(82e02695) SHA1(eaa7010f4672320116a1f319f96aeb078ce79609))
	ROM_LOAD("1818-0210.bin", 0x3800, 0x800, CRC(a1ebb6ab) SHA1(71b2beeff3817d9c87870983bf1653f682b15d21))
	ROM_LOAD("1818-0426.bin", 0x4000, 0x800, CRC(9c49fb37) SHA1(15f2f236f567a6234f9de8841fe9a6c246022127))
	ROM_LOAD("1818-0212.bin", 0x4800, 0x800, CRC(50221ec0) SHA1(b3fb76da1210ed6eefec9c3fbd731970dd50f962))
	ROM_LOAD("1818-0513.bin", 0x5000, 0x800, CRC(416d1a4d) SHA1(6ce7136f36f06fc44716da1970b96a3ef29e5b13))

	ROM_REGION(0x800, "chargen0", 0)
	ROM_LOAD("1816-0612.bin", 0x0000, 0x400, CRC(5d7befd6) SHA1(31357e7b8630f52698f1b6825e79c7a51ff3f245))
	ROM_LOAD("1816-0613.bin", 0x0400, 0x400, CRC(b6bac431) SHA1(42a557ecff769425d295ebbd1b73b26ddbfd3a09))

	ROM_REGION(0x400, "chargen1", 0)
	ROM_LOAD("1816-0642.bin", 0x0000, 0x400, CRC(2b8d151d) SHA1(208ae3ec780eb8bbbe6ac39cc61141730eda7fdd))

	ROM_REGION(0x400, "chargen2", 0)
	ROM_LOAD("1816-1417.bin", 0x0000, 0x400, CRC(e91343a4) SHA1(b37be2f3699bc8766435b5ee3775d36510df8d1e))

	ROM_REGION(0x400, "chargen3", 0)
	ROM_LOAD("1816-1425.bin", 0x0000, 0x400, CRC(69a34fef) SHA1(816929cadd53c2fe42b3ca561c029cb1ccd4ca24))
ROM_END

} // anonymous namespace

COMP( 1976, hp2641, 0, 0, hp2641, hp2641     , hp2641_state, empty_init, "Hewlett-Packard", "HP 2641A", 0)
COMP( 1976, hp2645, 0, 0, hp2645, hp2640_base, hp2645_state, empty_init, "Hewlett-Packard", "HP 2645A", 0)



hp3478a.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:fenugrec
/******************************************************************************
* HP 3478A Digital Multimeter
*
* Emulating test equipment is not very meaningful except for developping ROM patches.
* This aims to be the minimal emulation sufficient to run the UI (keypad and display).
* Ideally, faking ADC readings could be useful too.
*
* Some of this will probably be applicable to HP 3468A units too.
*
* Current status : runs, AD LINK ERROR on stock ROM due to unimplemented AD link
* - patching the AD comms, we get to a mostly functional state (for patch examples,
*   see https://github.com/fenugrec/hp3478a_rompatch )
*
* TODO
* - split out LCD driver code. It seems common to other HP equipment of the
*   era, such as the 3468, 3457, 3488?, 4263?, 6623?, and probably others.
*
* TODO next level
* * do something for analog CPU serial link (not quite uart), or emulate CPU
* * better display render and layout - actual photo ?
*
* TODO level 9000
* * Connect this with the existing i8291.cpp driver
* * add analog CPU (8049)
* * validate one single chipselect active when doing external access (movx)


**** Hardware details (refer to service manual for schematics)
Main CPU : i8039 , no internal ROM
Analog (floating) CPU : i8049, internal ROM (, dump available at ko4bb.com)
ROM : 2764 (64kbit, org 8kB)
RAM : 5101 , 256 * 4bit (!), battery-backed calibration data
GPIB:  i8291
Display : unknown; similar protocol for HP 3457A documented on
    http://www.eevblog.com/forum/projects/led-display-for-hp-3457a-multimeter-i-did-it-)/25/


Main cpu I/O ports:
Port1
P14-P17 : keypad out (cols)
P10-P13 : keypad in (rows)

P20 : disp.clk1
P21 : !CS for GPIB, and disp.IWA
P22 : !CS for DIPswitch; disp.ISA (for instructions)
P23 = !OE for RAM ; disp.sync (enable instruction)
P24 = disp.PWO  (enable)
P25 = disp.clk2
P26 : address bit12 ! (0x1000) => hardware banking
P27 : data out thru isol, to analog CPU

T1 : data in thru isol, from analog CPU (opcodes jt1 / jnt1)
*/

#include "emu.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/bankdev.h"
#include "machine/nvram.h"
#include "machine/watchdog.h"
#include "hp3478a.lh"

#define CPU_CLOCK       XTAL(5'856'000)

/* port pin/bit defs. Would be nice if mcs48.h had these */
#define P20 (1 << 0)
#define P21 (1 << 1)
#define P22 (1 << 2)
#define P23 (1 << 3)
#define P24 (1 << 4)
#define P25 (1 << 5)
#define P26 (1 << 6)
#define P27 (1 << 7)

#define A12_PIN P26
#define CALRAM_CS P23
#define DIPSWITCH_CS P22
#define GPIB_CS P21

#define DISP_PWO P24
#define DISP_SYNC P23
#define DISP_ISA P22
#define DISP_IWA P21
#define DISP_CK1 P20 //don't care about CK2 since it's supposed to be a delayed copy of CK1
#define DISP_MASK (DISP_PWO | DISP_SYNC | DISP_ISA | DISP_IWA | DISP_CK1)   //used for edge detection

// IO banking : indexes of m_iobank maps
#define CALRAM_ENTRY 0
#define GPIB_ENTRY 1
#define DIP_ENTRY 2

/**** optional debug outputs, must be before #include logmacro.*/
#define DEBUG_PORTS (LOG_GENERAL << 1)
#define DEBUG_BANKING (LOG_GENERAL << 2)
#define DEBUG_BUS (LOG_GENERAL << 3)    //not used after all
#define DEBUG_KEYPAD (LOG_GENERAL << 4)
#define DEBUG_LCD (LOG_GENERAL << 5)    //low level
#define DEBUG_LCD2 (LOG_GENERAL << 6)
#define DEBUG_CAL (LOG_GENERAL << 7)

#define VERBOSE (DEBUG_BUS) //can be combined, like (DEBUG_CAL | DEBUG_KEYPAD)

#include "logmacro.h"


namespace {

/**** HP 3478A class **/

class hp3478a_state : public driver_device
{
public:
	hp3478a_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_nvram(*this, "nvram")
		, m_nvram_raw(*this, "nvram")
		, m_bank0(*this, "bank0")
		, m_iobank(*this, "iobank")
		, m_keypad(*this, "COL.%u", 0)
		, m_calenable(*this, "CAL_EN")
	{
	}

	void hp3478a(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	//virtual void machine_reset() override ATTR_COLD;    //not needed?

private:
	uint8_t p1read();
	void p2write(uint8_t data);
	void nvwrite(offs_t offset, uint8_t data);

	void io_bank(address_map &map) ATTR_COLD;
	void i8039_io(address_map &map) ATTR_COLD;
	void i8039_map(address_map &map) ATTR_COLD;

	required_device<i8039_device> m_maincpu;
	required_device<nvram_device> m_nvram;
	required_shared_ptr<uint8_t> m_nvram_raw;
	required_memory_bank m_bank0;
	required_device<address_map_bank_device> m_iobank;
	required_ioport_array<4> m_keypad;
	required_ioport m_calenable;

	/////////////// stuff for internal LCD emulation
	// shoud be split to a separate driver
	std::unique_ptr<output_finder<16> > m_outputs;
	std::unique_ptr<output_finder<12> > m_annuns;

	void lcd_interface(uint8_t p2new);
	void lcd_update_hinib(uint64_t shiftreg);
	void lcd_update_lonib(uint64_t shiftreg);
	void lcd_update_annuns(uint64_t shiftreg);
	void lcd_map_chars();
	static uint32_t lcd_set_display(uint32_t segin);

	uint8_t m_lcd_bitcount;
	uint8_t m_lcd_want;
	uint64_t m_lcd_bitbuf;
	enum class lcd_state : uint8_t {
		IDLE,
		SYNC_SKIP,
		SELECTED_ISA,
		SELECTED_IWA
	} m_lcdstate;
	enum class lcd_iwatype : uint8_t {
		ANNUNS,
		REG_A,
		REG_B,
		REG_C,
		DISCARD
	} m_lcdiwa;
	uint8_t m_lcd_chrbuf[12];   //raw digits (not ASCII)
	uint8_t m_lcd_text[13]; //mapped to ASCII, only for debug output
	uint32_t m_lcd_segdata[12];
	bool m_lcd_annuns[12];  //local copy of annunciators
	///////////////////////////

	uint8_t m_p2_oldstate;  //used to detect edges on Port2 IO pins. Should be saveable ?
};



/***** callbacks */
/* port1 manages the keypad matrix */

uint8_t hp3478a_state::p1read()
{
	unsigned i;
	uint8_t data = m_maincpu->p1_r() | 0x0F; //P10-P13 "pull-up"

	// for each column, set Px=0 for pressed buttons (active low)
	for (i = 0; i < 4; i++) {
		if (!(data & (0x10 << i))) {
			data &= (0xF0 | m_keypad[i]->read());   //not sure if the undefined upper bits will read as 1 ?
		}
	}
	LOGMASKED(DEBUG_KEYPAD, "port1 read: 0x%02X\n", data);
	return data;
}

/** a lot of stuff multiplexed on the P2 pins.
 * parse the chipselect lines, A12 line, and LCD interface.
 */
void hp3478a_state::p2write(uint8_t data)
{
	LOGMASKED(DEBUG_PORTS, "port2 write: %02X\n", data);

	// check which CS line is active. No collision checking is done here
	// because the LCD interface reuses those pins and we'd get spurious errors.
	// So the last evaluated condition will be kept.

	if (!(data & CALRAM_CS)) {
		//will read lower 4 bits from calram
		m_iobank->set_bank(CALRAM_ENTRY);
	}
	if (!(data & DIPSWITCH_CS)) {
		m_iobank->set_bank(DIP_ENTRY);
	}
	if (!(data & GPIB_CS)) {
		m_iobank->set_bank(GPIB_ENTRY);
	}

	if ((m_p2_oldstate ^ data) & A12_PIN) {
		/* A12 pin state changed */
		if (data & A12_PIN) {
			m_bank0->set_entry(1);
			LOGMASKED(DEBUG_BANKING, "changed to bank1\n");
		} else {
			m_bank0->set_entry(0);
			LOGMASKED(DEBUG_BANKING, "changed to bank0\n");
		}
	}

	if ((m_p2_oldstate ^ data) & DISP_MASK) {
		/* display signals changed */
		lcd_interface(data);
	}

	m_p2_oldstate = data;
}


/* CAL RAM write handler, to implement "CAL enable" front panel switch
*/
void hp3478a_state::nvwrite(offs_t offset, uint8_t data) {
	if (m_calenable->read()) {
		m_nvram_raw[offset] = data;
		LOGMASKED(DEBUG_CAL, "write %02X to cal[%02X]\n", data, offset);
	} else {
		LOGMASKED(DEBUG_CAL, "write %02X to cal[%02X]:dropped\n", data, offset);
	}
}


/**** LCD emulation
 *
 * Yuck. Emulate serial LCD module interface. don't really want to make a separate driver for this...
 * The protocol is common to many HP products of the era. Some sources have the instruction words written as 10-bit
 * words, but it would appear more consistent (and matches the intent guessed from the disassembled functions)
 * that they are actually 8-bit bytes. The 2-bit difference is a "bogus" 2 clock cycles for when SYNC or PWO changes ?
 *
*/

/** charset copied from roc10937 driver. Some special chars are wrong.
 * Interestingly, the 3478a usually doesn't use "0x30" for the number 0, but instead
 * maps it to the character 'O' ! It does use 0x30 when printing the GPIB address however.
 */
static const uint16_t hpcharset[]=
{           // FEDC BA98 7654 3210
	0x507F, // 0101 0000 0111 1111 @.
	0x44CF, // 0100 0100 1100 1111 A.
	0x153F, // 0001 0101 0011 1111 B.
	0x00F3, // 0000 0000 1111 0011 C.
	0x113F, // 0001 0001 0011 1111 D.
	0x40F3, // 0100 0000 1111 0011 E.
	0x40C3, // 0100 0000 1100 0011 F.
	0x04FB, // 0000 0100 1111 1011 G.
	0x44CC, // 0100 0100 1100 1100 H.
	0x1133, // 0001 0001 0011 0011 I.
	0x007C, // 0000 0000 0111 1100 J.
	0x4AC0, // 0100 1010 1100 0000 K.
	0x00F0, // 0000 0000 1111 0000 L.
	0x82CC, // 1000 0010 1100 1100 M.
	0x88CC, // 1000 1000 1100 1100 N.
	0x00FF, // 0000 0000 1111 1111 O.
	0x44C7, // 0100 0100 1100 0111 P.
	0x08FF, // 0000 1000 1111 1111 Q.
	0x4CC7, // 0100 1100 1100 0111 R.
	0x44BB, // 0100 0100 1011 1011 S.
	0x1103, // 0001 0001 0000 0011 T.
	0x00FC, // 0000 0000 1111 1100 U.
	0x22C0, // 0010 0010 1100 0000 V.
	0x28CC, // 0010 1000 1100 1100 W.
	0xAA00, // 1010 1010 0000 0000 X.
	0x9200, // 1001 0010 0000 0000 Y.
	0x2233, // 0010 0010 0011 0011 Z.
	0x00E1, // 0000 0000 1110 0001 [.
	0x8800, // 1000 1000 0000 0000 \.
	0x001E, // 0000 0000 0001 1110 ].
	0x2800, // 0010 1000 0000 0000 ^.
	0x0030, // 0000 0000 0011 0000 _.
	0x0000, // 0000 0000 0000 0000 [space] , 0x20
	0x8121, // 1000 0001 0010 0001 !.
	0x0180, // 0000 0001 1000 0000 ".
	0x553C, // 0101 0101 0011 1100 #.
	0x55BB, // 0101 0101 1011 1011 $.
	0x7799, // 0111 0111 1001 1001 %.
	0xC979, // 1100 1001 0111 1001 &.
	0x0200, // 0000 0010 0000 0000 '.
	0x0A00, // 0000 1010 0000 0000 (.
	0xA050, // 1010 0000 0000 0000 ).
	0xFF00, // 1111 1111 0000 0000 *.
	0x5500, // 0101 0101 0000 0000 +.
	0x0000, // 0000 0000 0000 0000  //XXX (0x2C)
	0x4400, // 0100 0100 0000 0000 --.
	0x0000, // 0000 0000 0000 0000  //XXX (0x2E)
	0x2200, // 0010 0010 0000 0000 /.
	0x22FF, // 0010 0010 1111 1111 0. (0x30)
	0x1100, // 0001 0001 0000 0000 1.
	0x4477, // 0100 0100 0111 0111 2.
	0x443F, // 0100 0100 0011 1111 3.
	0x448C, // 0100 0100 1000 1100 4.
	0x44BB, // 0100 0100 1011 1011 5.
	0x44FB, // 0100 0100 1111 1011 6.
	0x000F, // 0000 0000 0000 1111 7.
	0x44FF, // 0100 0100 1111 1111 8.
	0x44BF, // 0100 0100 1011 1111 9.
	0xFFFF, // 1111 1111 1111 1111 [all segs] (0x3A)
	0x2001, // 0010 0000 0000 0001  //XXX
	0x2230, // 0010 0010 0011 0000 <.
	0x4430, // 0100 0100 0011 0000 =.
	0x8830, // 1000 1000 0011 0000 >.
	0x1407, // 0001 0100 0000 0111 ?.
};

/** copy data in shiftreg to the high nibble of each digit in m_lcd_chrbuf */
void hp3478a_state::lcd_update_hinib(uint64_t shiftreg)
{
	int i;
	for (i=11; i >= 0; i--) {
		m_lcd_chrbuf[i] &= 0x0F;
		m_lcd_chrbuf[i] |= (shiftreg & 0x0F) << 4;
		shiftreg >>= 4;
	}
}

/** copy data in shiftreg to the low nibble of each digit in m_lcd_chrbuf */
void hp3478a_state::lcd_update_lonib(uint64_t shiftreg)
{
	int i;
	for (i=11; i >= 0; i--) {
		m_lcd_chrbuf[i] &= 0xF0;
		m_lcd_chrbuf[i] |= (shiftreg & 0x0F);
		shiftreg >>= 4;
	}
}


/** update annunciators : 12 bits */
void hp3478a_state::lcd_update_annuns(uint64_t shiftreg)
{
	int i;
	for (i=11; i >= 0; i--) {
		m_lcd_annuns[i] = (shiftreg & 0x01);
		shiftreg >>=1;
	}
	std::copy(std::begin(m_lcd_annuns), std::end(m_lcd_annuns), std::begin(*m_annuns));
}

/** map LCD char to ASCII and segment data + update
 *
 * discards extra bits
 */
void hp3478a_state::lcd_map_chars()
{
	int i;
	LOGMASKED(DEBUG_LCD2, "LCD : map ");
	for (i=0; i < 12; i++) {
		bool dp = m_lcd_chrbuf[i] & 0x40;   //check decimal point. Needs to be mapped to seg_bit16
		bool comma = m_lcd_chrbuf[i] & 0x80;    //check comma, maps to seg17
		m_lcd_text[i] = (m_lcd_chrbuf[i] & 0x3F) + 0x40;
		m_lcd_segdata[i] = hpcharset[m_lcd_chrbuf[i] & 0x3F] | (dp << 16) | (comma << 17);
		LOGMASKED(DEBUG_LCD2, "[%02X>%04X] ", m_lcd_chrbuf[i] & 0x3F, m_lcd_segdata[i]);
	}
	LOGMASKED(DEBUG_LCD2, "\n");
}

/** ?? from roc10937 */
uint32_t hp3478a_state::lcd_set_display(uint32_t segin)
{
	return bitswap<32>(segin, 31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,11,9,15,13,12,8,10,14,7,6,5,4,3,2,1,0);
}

// ISA command bytes
#define DISP_ISA_WANNUN 0xBC    //annunciators
#define DISP_ISA_WA 0x0A    //low nibbles
#define DISP_ISA_WB 0x1A    //hi nib
#define DISP_ISA_WC 0x2A    // "extended bit" ?

/** LCD serial interface state machine. I cheat and don't implement all commands.
 * Also, it's not clear when exactly the display should be updated. After each regA/regB write
 * seems to generate some glitches. After PWO deselect causes some half-written text to appear sometimes.
 */
void hp3478a_state::lcd_interface(uint8_t p2new)
{
	bool pwo_state, sync_state, isa_state, iwa_state;

	pwo_state = p2new & DISP_PWO;
	sync_state = p2new   & DISP_SYNC;
	isa_state = p2new & DISP_ISA;
	iwa_state = p2new & DISP_IWA;

	if (!((p2new ^ m_p2_oldstate) & DISP_CK1)) {
		// no clock edge : boring.
		//LOGMASKED(DEBUG_LCD, "LCD : pwo(%d), sync(%d), isa(%d), iwa(%d)\n",
		//      pwo_state, sync_state, isa_state, iwa_state);
		return;
	}

	if (!(p2new & DISP_CK1)) {
		//neg edge
		return;
	}

	// CK1 clock positive edge
	if (!pwo_state) {
		//not selected, reset everything
		LOGMASKED(DEBUG_LCD, "LCD : state=IDLE, PWO deselected, %d stray bits(0x...%02X)\n",m_lcd_bitcount, m_lcd_bitbuf & 0xFF);
		m_lcdstate = lcd_state::IDLE;
		m_lcdiwa = lcd_iwatype::DISCARD;
		std::transform(std::begin(m_lcd_segdata), std::end(m_lcd_segdata), std::begin(*m_outputs), lcd_set_display);
		m_lcd_bitcount = 0;
		m_lcd_bitbuf = 0;
		return;
	}
	switch (m_lcdstate) {
		case lcd_state::IDLE:
			m_lcd_want = 8;
			m_lcdstate = lcd_state::SYNC_SKIP;
			break;
		case lcd_state::SYNC_SKIP:
			// if SYNC changed, we need to ignore two clock pulses.
			m_lcd_bitcount++;
			if (m_lcd_bitcount < 1) {
				break;
			}
			m_lcd_bitcount = 0;
			m_lcd_bitbuf = 0;
			if (sync_state) {
				m_lcdstate = lcd_state::SELECTED_ISA;
				m_lcd_want = 8;
				LOGMASKED(DEBUG_LCD, "LCD : state=SELECTED_ISA\n");
			} else {
				//don't touch m_lcd_want since it was possibly set in the ISA stage
				m_lcdstate = lcd_state::SELECTED_IWA;
				LOGMASKED(DEBUG_LCD, "LCD : state=SELECTED_IWA, want %d\n", m_lcd_want);
			}
			break;
		case lcd_state::SELECTED_ISA:
			if (!sync_state) {
				//changing to SELECTED_IWA
				m_lcdstate = lcd_state::SYNC_SKIP;
				if (m_lcd_bitcount) {
					LOGMASKED(DEBUG_LCD, "LCD : ISA->IWA, %d stray bits (0x%0X)\n", m_lcd_bitcount, m_lcd_bitbuf);
				} else {
					LOGMASKED(DEBUG_LCD, "LCD : ISA->IWA\n");
				}
				m_lcd_bitcount = 0;
				m_lcd_bitbuf = 0;
				break;
			}
			m_lcd_bitbuf |= (isa_state << m_lcd_bitcount);
			m_lcd_bitcount++;
			if (m_lcd_bitcount != m_lcd_want) {
				break;
			}
			LOGMASKED(DEBUG_LCD, "LCD : Instruction 0x%02X\n", m_lcd_bitbuf & 0xFF);
			//shouldn't get extra bits, but we have nothing better to do so just reset the shiftreg.
			m_lcd_bitcount = 0;
			switch (m_lcd_bitbuf & 0xFF) {
			case DISP_ISA_WANNUN:
				m_lcd_want = 44;
				m_lcdiwa = lcd_iwatype::ANNUNS;
				break;
			case DISP_ISA_WA:
				m_lcd_want = 100;   //no, doesn't fit in a uint64, but only the first 36 bits are significant.
				m_lcdiwa = lcd_iwatype::REG_A;
				break;
			case DISP_ISA_WB:
				m_lcd_want = 100;
				m_lcdiwa = lcd_iwatype::REG_B;
				break;
			case DISP_ISA_WC:
				m_lcd_want = 44;
				m_lcdiwa = lcd_iwatype::REG_C;
				break;
			default:
				m_lcd_want = 44;
				m_lcdiwa = lcd_iwatype::DISCARD;
				break;
			}
			m_lcd_bitbuf = 0;
			break;
		case lcd_state::SELECTED_IWA:
			if (sync_state) {
				//changing to SELECTED_ISA
				m_lcdstate = lcd_state::SYNC_SKIP;
				if (m_lcd_bitcount) {
					LOGMASKED(DEBUG_LCD, "LCD : IWA->ISA, %d stray bits (0x%X)\n", m_lcd_bitcount, m_lcd_bitbuf);
				} else {
					LOGMASKED(DEBUG_LCD, "LCD : IWA->ISA\n");
				}
				m_lcd_bitcount = 0;
				m_lcd_bitbuf = 0;
				break;
			}
			if (m_lcd_bitcount <= 0x3F) {
				//clamp to bit 63;
				m_lcd_bitbuf |= ((uint64_t) iwa_state << m_lcd_bitcount);
			}
			m_lcd_bitcount++;
			if (m_lcd_bitcount != m_lcd_want) {
				break;
			}
			LOGMASKED(DEBUG_LCD, "LCD : data 0x%X\n", m_lcd_bitbuf);
			switch (m_lcdiwa) {
			case lcd_iwatype::ANNUNS:
				lcd_update_annuns(m_lcd_bitbuf);
				LOGMASKED(DEBUG_LCD2, "LCD : write annuns 0x%02X\n", m_lcd_bitbuf & 0xFF);
				break;
			case lcd_iwatype::REG_A:
				lcd_update_lonib(m_lcd_bitbuf);
				lcd_map_chars();
				LOGMASKED(DEBUG_LCD2, "LCD : write reg A (lonib) %X, text=%s\n", m_lcd_bitbuf, (char *) m_lcd_text);
				break;
			case lcd_iwatype::REG_B:
				lcd_update_hinib(m_lcd_bitbuf);
				lcd_map_chars();
				LOGMASKED(DEBUG_LCD2, "LCD : write reg B (lonib) %X, text=%s\n", m_lcd_bitbuf, (char *) m_lcd_text);
				break;
			default:
				//discard
				break;
			}
			//shouldn't get extra bits, but we have nothing better to do so just reset the shiftreg.
			m_lcd_bitcount = 0;
			m_lcd_bitbuf = 0;
			break;  //case SELECTED_IWA
	}

	return;
}



void hp3478a_state::machine_start()
{
	m_bank0->configure_entries(0, 2, memregion("maincpu")->base(), 0x1000);

	m_outputs = std::make_unique<output_finder<16> >(*this, "vfd%u", (unsigned) 0);
	m_outputs->resolve();
	m_annuns = std::make_unique<output_finder<12> >(*this, "ann%u", (unsigned) 0);
	m_annuns->resolve();

	m_p2_oldstate = 0;
}


/******************************************************************************
 Address Maps
******************************************************************************/

void hp3478a_state::i8039_map(address_map &map)
{
	map(0x0000, 0x0fff).bankr("bank0"); // CPU address space (4kB), banked according to P26 pin
}

void hp3478a_state::i8039_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0xff).m(m_iobank, FUNC(address_map_bank_device::amap8));
}

/* depending on the P2 port state, different chipselect lines are activated, which
 * affect the subsequent external accesses (movx)
 * The addresses in here have nothing to do with the mcs48 address space.
 */
void hp3478a_state::io_bank(address_map &map)
{
	map.unmap_value_high();
	map(0x000, 0x0ff).ram().share("nvram").w(FUNC(hp3478a_state::nvwrite));
	map(0x100, 0x107).ram().share("gpibregs");  //XXX TODO : connect to i8291.cpp
	map(0x200, 0x2ff).portr("DIP");
}


/******************************************************************************
 Input Ports
******************************************************************************/

static INPUT_PORTS_START( hp3478a )
/* keypad bit matrix:
            0x08|0x04|0x02|0x01
    col.0 : (nc)|shift|ACA|DCA
    col.1 : 4W|2W|ACV|DCV
    col.2 : int|dn|up|auto
    col.3 : (nc)|loc|srq|sgl
*/
	PORT_START("COL.0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("DCA")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("ACA")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("SHIFT")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED ) //nothing on 0x08
	PORT_START("COL.1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("DCV")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("ACV")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("2W")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON7 ) PORT_NAME("4W")
	PORT_START("COL.2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON8 ) PORT_NAME("AUTO")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON9 ) PORT_NAME("UP")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON10 ) PORT_NAME("DN")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON11 ) PORT_NAME("INT")
	PORT_START("COL.3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON12 ) PORT_NAME("SGL")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON13 ) PORT_NAME("SRQ")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON14 ) PORT_NAME("LOC")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED ) //nothing on 0x08

	PORT_START("CAL_EN")
	PORT_CONFNAME(1, 0, "CAL")
	PORT_CONFSETTING(0x00, "disabled")
	PORT_CONFSETTING(0x01, "enabled")

	PORT_START("DIP")
	PORT_DIPNAME( 0x1f, 0x17, "HP-IB Bus Address" ) PORT_DIPLOCATION("DIP:1,2,3,4,5")
	PORT_DIPSETTING(    0x00, "0" )
	PORT_DIPSETTING(    0x01, "1" )
	PORT_DIPSETTING(    0x02, "2" )
	PORT_DIPSETTING(    0x03, "3" )
	PORT_DIPSETTING(    0x04, "4" )
	PORT_DIPSETTING(    0x05, "5" )
	PORT_DIPSETTING(    0x06, "6" )
	PORT_DIPSETTING(    0x07, "7" )
	PORT_DIPSETTING(    0x08, "8" )
	PORT_DIPSETTING(    0x09, "9" )
	PORT_DIPSETTING(    0x0a, "10" )
	PORT_DIPSETTING(    0x0b, "11" )
	PORT_DIPSETTING(    0x0c, "12" )
	PORT_DIPSETTING(    0x0d, "13" )
	PORT_DIPSETTING(    0x0e, "14" )
	PORT_DIPSETTING(    0x0f, "15" )
	PORT_DIPSETTING(    0x10, "16" )
	PORT_DIPSETTING(    0x11, "17" )
	PORT_DIPSETTING(    0x12, "18" )
	PORT_DIPSETTING(    0x13, "19" )
	PORT_DIPSETTING(    0x14, "20" )
	PORT_DIPSETTING(    0x15, "21" )
	PORT_DIPSETTING(    0x16, "22" )
	PORT_DIPSETTING(    0x17, "23" )
	PORT_DIPSETTING(    0x18, "24" )
	PORT_DIPSETTING(    0x19, "25" )
	PORT_DIPSETTING(    0x1a, "26" )
	PORT_DIPSETTING(    0x1b, "27" )
	PORT_DIPSETTING(    0x1c, "28" )
	PORT_DIPSETTING(    0x1d, "29" )
	PORT_DIPSETTING(    0x1e, "30" )
	PORT_DIPSETTING(    0x1f, "31" )
	PORT_DIPNAME( 0x20, 0x00, "PWR ON SRQ" ) PORT_DIPLOCATION("DIP:6")
	PORT_DIPSETTING(    0x00, "Disabled" )
	PORT_DIPSETTING(    0x20, "Enabled" )
	//0x40 unused
	PORT_DIPNAME( 0x80, 0x00, "50/60Hz AC" ) PORT_DIPLOCATION("DIP:8")
	PORT_DIPSETTING(    0x00, "60Hz" )
	PORT_DIPSETTING(    0x80, "50Hz" )
INPUT_PORTS_END


/******************************************************************************
 Machine Drivers
******************************************************************************/

void hp3478a_state::hp3478a(machine_config &config)
{
	auto &mcu(I8039(config, "maincpu", CPU_CLOCK));
	mcu.set_addrmap(AS_PROGRAM, &hp3478a_state::i8039_map);
	mcu.set_addrmap(AS_IO, &hp3478a_state::i8039_io);
	mcu.p1_in_cb().set(FUNC(hp3478a_state::p1read));
	mcu.p1_out_cb().set("watchdog", FUNC(watchdog_timer_device::reset_line_w)).bit(7);
	mcu.p2_out_cb().set(FUNC(hp3478a_state::p2write));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	ADDRESS_MAP_BANK(config, m_iobank, 0);
	m_iobank->set_map(&hp3478a_state::io_bank);
	m_iobank->set_data_width(8);
	m_iobank->set_addr_width(18);
	m_iobank->set_stride(0x100);

	WATCHDOG_TIMER(config, "watchdog").set_time(attotime::from_ticks(3*5*(1<<19),CPU_CLOCK));

	// video
	config.set_default_layout(layout_hp3478a);
}


/******************************************************************************
 ROM Definitions
******************************************************************************/

ROM_START( hp3478a )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD("rom_dc118.bin", 0, 0x2000, CRC(10097ced) SHA1(bd665cf7e07e63f825b2353c8322ed8a4376b3bd))  // main CPU ROM, can match other datecodes too

	ROM_REGION( 0x100, "nvram", 0 ) // default data for battery-backed Calibration RAM
	ROM_LOAD( "calram.bin", 0, 0x100, NO_DUMP)
ROM_END

} // Anonymous namespace


/******************************************************************************
 Drivers
******************************************************************************/

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY                        FULLNAME             FLAGS
SYST( 1983, hp3478a,  0,      0,  hp3478a, hp3478a,hp3478a_state, empty_init, "HP", "HP 3478A Multimeter", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



hp48.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Antoine Mine
/**********************************************************************

  Copyright (C) Antoine Mine' 2008

   Hewlett Packard HP48 S/SX & G/GX/G+ and HP49 G

**********************************************************************/

#include "emu.h"
#include "hp48.h"

#include "machine/nvram.h"
#include "screen.h"
#include "speaker.h"


/* TODO:
   - IR I/O port
   - HP48 to HP48 serial connection
   - set card size when creating a new card image
   - some bits I/O RAM not implemented
   - printer
   - more accurate timing (accurate SATURN cycles, speedup when screen is off)
   - save-state: figure what RAM to store & where (currently, when exiting,
   base RAM is saved as nvram, RAM in expansion ports is saved as image,
   IO RAM is & CPU state are discarded; save-state saves base & IO RAM,
   CPU state, but not expansion port RAM)
   - more accurate IRQ, NMI, sleep and wake-up handling
   - HP49 G flash ROM write
   - HP49 xmodem
*/


/*
  HP48: graphical calculators by Hewlett Packard.

  Two main series: S series & G series.
  Each series is subdivised into an 'expandable' model (SX,GX) and a
  cheaper 'non-expandable' model (S,G).

  Timeline:
  - 1990-03-06 HP48SX (discontinued in ????)
  - 1991-04-02 HP48S  (discontinued in 1993)
  - 1993-06-01 HP48G  (discontinued in 1999)
               HP48GX (discontinued in 2003)
  - 1998-03-30 HP48G+ (discontinued in 2003)

  Common characteristics:
  - HP family name: Charlemagne
  - CPU: Saturn, 4-bit processor, chip version: 1LT8
  - BUS:
      . 20-bit address bus
      . nibble (4-bit) addressable
      => 512 KB address space (i.e., 1 Mnibbles)
      . daisy-chained modules
  - Screen: 131x64 LCD, 2-color, 64 Hz refresh
  - Keyboard: 49 keys
  - IR I/O port
  - 4-wire serial I/O port
  - 1-bit beeper
  - Power-source: 3 AAA cells
  - Continuous memory


  S series specific:
  - Clarke IC
  - clock speed: 2 MHz
  - 256 KB ROM (various revisions, identical in S and SX)

  - S model specific:
    + Codename: Shorty
    + 32 KB RAM base, not expandable (in theory)
    + no expansion port

  - SX model specific:
    + Codename: Charlemagne
    + 32 KB base RAM, expandable to 288 KB
    + 2 expansion ports (port 1 and port 2)
      up to 128 KB RAM per port, can be merged with base RAM

  G series specific:
  - Yorke IC
  - clock speed: ~4 MHz
  - 512 KB ROM (various revisions, identical in G and GX)

  - G model specific:
    + Codename: Alcuin
    + 32 KB RAM, not expandable (in theory!)
    + no expansion port

  - GX model specific:
    + Codename: Hammer
    + 128 KB base RAM, expandable up to 4 MB (banked)
    + 2 expansion ports
      . port 1: up to 128 KB RAM, can be merged with base RAM
      . port 2: up to   4 MB RAM, in 32 banks of 128 KB, cannot be merged

  - G+ model specific:
    + Codename: Plus
    + 128 KB RAM, not expandable
    + no expansion port



  HP49 G:
  similar to HP48 G with the following differences:
  - 2048 KB flash ROM (re-writtable)
  - 512 KB RAM (not expandable)
  - Keyboard: 51 keys
  - no IR I/O port
  - rewritten optimized OS makes the HP49 G much faster than HP48 despite
  having the same CPU and clock rate (4 MHz York Saturn)

*/


/*
   More about memory.

   The base RAM is treated as NVRAM: stored in a .nv file in your nvram
   directory.
   When first starting the emulator, or after nvram file has been deleted,
   you'll get a "Try to recover memory?". This is expected.
   Answer NO (F key): there is nothing to recover, but it will setup the
   system-reserved RAM part correctly.
   If the emulator is shut down properly (i.e., when the calculator is
   not busy), the message should not appear next time it is run.
   All the variables and port 0 should be conserved (in the .nv file),
   but not the stack.
   Stopping and restarting the emulator loosely corresponds to hitting the
   reset button.

   Expansion RAMs are treated as images, not NVRAM.
   The -p1 (resp. -p2) image option allows mapping some file to port 1
   (resp. port 2).
   The file size should be between 32 KB and 128 KB (or 4 GB for port 2 on GX)
   and be a power of two.
*/


/*
  More about serial connection.

  Normally, files are exchanged by serial connection to a computer running a
  kermit communication program (xmodem is also allowed for the G/GX).
  We emulate both the serial link and the communication program.
  Thus, the emulated HP48 can directly upload/download image files.
  The -k image option corresponds to the kermit protocol (SEND and RECV
  commands on the HP48).
  The -x image option corresponds to the xmodem protocol (XSEND and XRECV
  commands on the HP48).
*/



/*

  References:

  - Voyage au centre de la HP48 S/SX, by Paul Courbis & S?bastien Lalande
    (English version: HP48 machine language - a journey to the center
     of the HP48 s/sx)
    available at http://www.courbis.com

  - Voyage au centre de la HP48 G/GX, by Paul Courbis
    available at http://www.courbis.com

  - Guide to the Saturn Processor (With HP48 Applications)
    by Matthew Mastracci

  - Introduction to Saturn Assembly Language
    by Gilbert Fernandes

  - http://www.hpcalc.org/

*/




/**************************** inputs *******************************/

/*  KEYBOARD
    --------

    keyboard layout for 48 models

   -------------------------------------------------
   |   A   |   B   |   C   |   D   |   E   |   F   |
   |-------+-------+-------+-------+-------+-------|
   |  MTH  |  PRG  |  CST  |  VAR  |   up  |  NXT  |
   |-------+-------+-------+-------+-------+-------|
   |   '   |  STO  |  EVAL |  left | down  | right |
   |-------+-------+-------+-------+-------+-------|
   |  SIN  |  COS  |  TAN  |  sqrt |  y^x  |  1/x  |
   |---------------+-------+-------+-------+-------|
   |     ENTER     |  +/-  |  EEX  |  DEL  |  <=   |
   |-----------------------------------------------|
   |  alpha  |   7    |    8   |    9    |    /    |
   |---------+--------+--------+---------+---------|
   |   red   |   4    |    5   |    6    |    *    |
   |---------+--------+--------+---------+---------|
   |  green  |   1    |    2   |    3    |    -    |
   |---------+--------+--------+---------+---------|
   |   ON    |   0    |    .   |   SPC   |    +    |
   -------------------------------------------------

   * 49 keys
   * including 3 modifier keys:
     - a  blue/green shift (upper right)
     - an orange/red shift (upper left)
     - a  white alpha key (lower right)

   Difference between G and S series: a few red and blue shifted keys.

*/

/* S/SX */
static INPUT_PORTS_START( hp48sx_kbd )

		PORT_START( "LINE0" ) /* OUT = 0x001 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "+      { }  : :" )
	PORT_CODE ( KEYCODE_EQUALS )
	PORT_CODE ( KEYCODE_PLUS_PAD )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "SPC    \xCF\x80 \xE2\x88\xA1" /* pi, angle */ )
	PORT_CODE ( KEYCODE_SPACE )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( ".      ,  \xE2\x86\xb5" /* return arrow */ )
	PORT_CODE ( KEYCODE_STOP )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "0      =  \xE2\x86\x92" /* right arrow */ )
	PORT_CODE ( KEYCODE_0 )
	PORT_CODE ( KEYCODE_0_PAD )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "'  M   UP  HOME" )
	PORT_CODE ( KEYCODE_QUOTE )
	PORT_CODE ( KEYCODE_M )

		PORT_BIT ( 0xffe0, 0, IPT_UNUSED )


		PORT_START( "LINE1" ) /* OUT = 0x002 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "-      << >>  \" \"" )
	PORT_CODE ( KEYCODE_MINUS )
	PORT_CODE ( KEYCODE_MINUS_PAD )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "3      CMD  MENU" )
	PORT_CODE ( KEYCODE_3 )
	PORT_CODE ( KEYCODE_3_PAD )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "2      STACK ARG" )
	PORT_CODE ( KEYCODE_2 )
	PORT_CODE ( KEYCODE_2_PAD )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "1       RAD POLAR" )
	PORT_CODE ( KEYCODE_1 )
	PORT_CODE ( KEYCODE_1_PAD )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "A" )
	PORT_CODE ( KEYCODE_A )

	PORT_BIT  ( 32, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "right shift" )
	PORT_CODE ( KEYCODE_RSHIFT )

		PORT_BIT ( 0xffc0, 0, IPT_UNUSED )


		PORT_START( "LINE2" ) /* OUT = 0x004 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "*      [ ]     _" )
	PORT_CODE ( KEYCODE_ASTERISK )
	PORT_CODE ( KEYCODE_CLOSEBRACE )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "6              UNITS" )
	PORT_CODE ( KEYCODE_6_PAD )
	PORT_CODE ( KEYCODE_6 )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "5              STAT" )
	PORT_CODE ( KEYCODE_5_PAD )
	PORT_CODE ( KEYCODE_5 )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "4              TIME" )
	PORT_CODE ( KEYCODE_4_PAD )
	PORT_CODE ( KEYCODE_4 )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "MTH   G  PRINT")
	PORT_CODE ( KEYCODE_G )

	PORT_BIT  ( 32, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "left shift" )
	PORT_CODE ( KEYCODE_LSHIFT )

	PORT_BIT ( 0xffc0, 0, IPT_UNUSED )


		PORT_START( "LINE3" ) /* OUT = 0x008 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "/      ( )     #" )
	PORT_CODE ( KEYCODE_SLASH )
	PORT_CODE ( KEYCODE_SLASH_PAD )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "9              ALGEBRA" )
	PORT_CODE ( KEYCODE_9_PAD )
	PORT_CODE ( KEYCODE_9 )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "8              PLOT" )
	PORT_CODE ( KEYCODE_8_PAD )
	PORT_CODE ( KEYCODE_8 )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "7              SOLVE" )
	PORT_CODE ( KEYCODE_7_PAD )
	PORT_CODE ( KEYCODE_7 )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "SIN   S  ASIN  \xE2\x88\x82" /* delta */ )
	PORT_CODE ( KEYCODE_S )

	PORT_BIT  ( 32, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "\xCE\xB1       USER ENTRY" /* alpha */ )
	PORT_CODE ( KEYCODE_LALT )

	PORT_BIT ( 0xffc0, 0, IPT_UNUSED )


		PORT_START( "LINE4" ) /* OUT = 0x010 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME (" \xE2\x87\x90       DROP  CLR" /* double left arrow */ )
	PORT_CODE ( KEYCODE_BACKSPACE )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "DEL        PURGE" )
	PORT_CODE ( KEYCODE_DEL )
	PORT_CODE ( KEYCODE_DEL_PAD )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "EEX  Z   2D   3D" )
	PORT_CODE ( KEYCODE_Z )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "+/-  Y   EDIT  VISIT" )
	PORT_CODE ( KEYCODE_Y )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "ENTER     EQUATION MATRIX")
	PORT_CODE ( KEYCODE_ENTER )
	PORT_CODE ( KEYCODE_ENTER_PAD )

	PORT_BIT ( 0xffe0, 0, IPT_UNUSED )


		PORT_START( "LINE5" ) /* OUT = 0x020 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "1/x  X   e^x   LN" )
	PORT_CODE ( KEYCODE_X )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "y^x  W   10^x  LOG" )
	PORT_CODE ( KEYCODE_W )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "\xE2\x88\x9A  V   x^2  sqrt(x,y)" /* square root */ )
	PORT_CODE ( KEYCODE_V )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "TAN  U   ATAN  \xE2\x88\x91" /* sum */ )
	PORT_CODE ( KEYCODE_U )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "COS  T   ACOS  \xE2\x88\xAB" /* integral */ )
	PORT_CODE ( KEYCODE_T )

		PORT_BIT ( 0xffe0, 0, IPT_UNUSED )


		PORT_START( "LINE6" ) /* OUT = 0x040 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "\xE2\x86\x92  R        SWAP" /* right arrow */ )
	PORT_CODE ( KEYCODE_RIGHT )
	PORT_CODE ( KEYCODE_R )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "\xE2\x86\x93  Q        REVIEW" /* down arrow */ )
	PORT_CODE ( KEYCODE_DOWN )
	PORT_CODE ( KEYCODE_Q)

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "\xE2\x86\x90  P        GRAPH" /* left arrow */ )
	PORT_CODE ( KEYCODE_LEFT )
	PORT_CODE ( KEYCODE_P )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "EVAL O   \xE2\x86\x92NUM UNDO" )
	PORT_CODE ( KEYCODE_O )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "STO  N   DEF   RCL" )
	PORT_CODE ( KEYCODE_N )

		PORT_BIT ( 0xffe0, 0, IPT_UNUSED )


		PORT_START( "LINE7" ) /* OUT = 0x080 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "NXT  L   PREV" )
	PORT_CODE ( KEYCODE_L )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "\xE2\x86\x91  K        LIBRARY" /* up arrow */ )
	PORT_CODE ( KEYCODE_UP )
	PORT_CODE ( KEYCODE_K )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "VAR  J         MEMORY" )
	PORT_CODE ( KEYCODE_J )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "CST  I         MODES" )
	PORT_CODE ( KEYCODE_I )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "PRG  H         I/O" )
	PORT_CODE ( KEYCODE_H )

		PORT_BIT ( 0xffe0, 0, IPT_UNUSED )


		PORT_START( "LINE8" ) /* OUT = 0x100 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "F" )
	PORT_CODE ( KEYCODE_F )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "E" )
	PORT_CODE ( KEYCODE_E )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "D" )
	PORT_CODE ( KEYCODE_D )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "C" )
	PORT_CODE ( KEYCODE_C )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "B" )
	PORT_CODE ( KEYCODE_B )

		PORT_BIT ( 0xffe0, 0, IPT_UNUSED )


		PORT_START( "ON" ) /* ON key, appears on all OUT lines */

	PORT_BIT  ( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "ON  CANCEL CONT OFF" )
	PORT_CODE ( KEYCODE_ESC )
	PORT_CODE ( KEYCODE_HOME )

		PORT_BIT ( 0x7fff, 0, IPT_UNUSED )

INPUT_PORTS_END



/* G/GX/G+ */
static INPUT_PORTS_START( hp48gx_kbd )

		PORT_START( "LINE0" ) /* OUT = 0x001 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "+      { }  : :" )
	PORT_CODE ( KEYCODE_EQUALS )
	PORT_CODE ( KEYCODE_PLUS_PAD )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "SPC    \xCF\x80 \xE2\x88\xA1" /* pi, angle */ )
	PORT_CODE ( KEYCODE_SPACE )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( ".      ,  \xE2\x86\xb5" /* return arrow */ )
	PORT_CODE ( KEYCODE_STOP )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "0      =  \xE2\x86\x92" /* right arrow */ )
	PORT_CODE ( KEYCODE_0 )
	PORT_CODE ( KEYCODE_0_PAD )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "'  M   UP  HOME" )
	PORT_CODE ( KEYCODE_QUOTE )
	PORT_CODE ( KEYCODE_M )

		PORT_BIT ( 0xffe0, 0, IPT_UNUSED )


		PORT_START( "LINE1" ) /* OUT = 0x002 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "-      << >>  \" \"" )
	PORT_CODE ( KEYCODE_MINUS )
	PORT_CODE ( KEYCODE_MINUS_PAD )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "3             EQ LIB" )
	PORT_CODE ( KEYCODE_3 )
	PORT_CODE ( KEYCODE_3_PAD )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "2             LIBRARY" )
	PORT_CODE ( KEYCODE_2 )
	PORT_CODE ( KEYCODE_2_PAD )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "1             I/O" )
	PORT_CODE ( KEYCODE_1 )
	PORT_CODE ( KEYCODE_1_PAD )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "A" )
	PORT_CODE ( KEYCODE_A )

	PORT_BIT  ( 32, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "right shift" )
	PORT_CODE ( KEYCODE_RSHIFT )

		PORT_BIT ( 0xffc0, 0, IPT_UNUSED )


		PORT_START( "LINE2" ) /* OUT = 0x004 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "*      [ ]     _" )
	PORT_CODE ( KEYCODE_ASTERISK )
	PORT_CODE ( KEYCODE_CLOSEBRACE )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "6              UNITS" )
	PORT_CODE ( KEYCODE_6_PAD )
	PORT_CODE ( KEYCODE_6 )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "5              STAT" )
	PORT_CODE ( KEYCODE_5_PAD )
	PORT_CODE ( KEYCODE_5 )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "4              TIME" )
	PORT_CODE ( KEYCODE_4_PAD )
	PORT_CODE ( KEYCODE_4 )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "MTH   G  RAD   POLAR")
	PORT_CODE ( KEYCODE_G )

	PORT_BIT  ( 32, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "left shift" )
	PORT_CODE ( KEYCODE_LSHIFT )

	PORT_BIT ( 0xffc0, 0, IPT_UNUSED )


		PORT_START( "LINE3" ) /* OUT = 0x008 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "/      ( )     #" )
	PORT_CODE ( KEYCODE_SLASH )
	PORT_CODE ( KEYCODE_SLASH_PAD )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "9              SYMBOLIC" )
	PORT_CODE ( KEYCODE_9_PAD )
	PORT_CODE ( KEYCODE_9 )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "8              PLOT" )
	PORT_CODE ( KEYCODE_8_PAD )
	PORT_CODE ( KEYCODE_8 )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "7              SOLVE" )
	PORT_CODE ( KEYCODE_7_PAD )
	PORT_CODE ( KEYCODE_7 )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "SIN   S  ASIN  \xE2\x88\x82" /* delta */ )
	PORT_CODE ( KEYCODE_S )

	PORT_BIT  ( 32, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "\xCE\xB1       USER ENTRY" /* alpha */ )
	PORT_CODE ( KEYCODE_LALT )

	PORT_BIT ( 0xffc0, 0, IPT_UNUSED )


		PORT_START( "LINE4" ) /* OUT = 0x010 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME (" \xE2\x87\x90       DROP" /* double left arrow */ )
	PORT_CODE ( KEYCODE_BACKSPACE )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "DEL            CLEAR" )
	PORT_CODE ( KEYCODE_DEL )
	PORT_CODE ( KEYCODE_DEL_PAD )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "EEX  Z   PURG  ARG" )
	PORT_CODE ( KEYCODE_Z )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "+/-  Y   EDIT  CMD" )
	PORT_CODE ( KEYCODE_Y )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "ENTER     EQUATION MATRIX")
	PORT_CODE ( KEYCODE_ENTER )
	PORT_CODE ( KEYCODE_ENTER_PAD )

	PORT_BIT ( 0xffe0, 0, IPT_UNUSED )


		PORT_START( "LINE5" ) /* OUT = 0x020 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "1/x  X   e^x   LN" )
	PORT_CODE ( KEYCODE_X )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "y^x  W   10^x  LOG" )
	PORT_CODE ( KEYCODE_W )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "\xE2\x88\x9A  V   x^2  sqrt(x,y)" /* square root */ )
	PORT_CODE ( KEYCODE_V )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "TAN  U   ATAN  \xE2\x88\x91" /* sum */ )
	PORT_CODE ( KEYCODE_U )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "COS  T   ACOS  \xE2\x88\xAB" /* integral */ )
	PORT_CODE ( KEYCODE_T )

		PORT_BIT ( 0xffe0, 0, IPT_UNUSED )


		PORT_START( "LINE6" ) /* OUT = 0x040 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "\xE2\x86\x92  R        SWAP" /* right arrow */ )
	PORT_CODE ( KEYCODE_RIGHT )
	PORT_CODE ( KEYCODE_R )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "\xE2\x86\x93  Q        VIEW" /* down arrow */ )
	PORT_CODE ( KEYCODE_DOWN )
	PORT_CODE ( KEYCODE_Q)

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "\xE2\x86\x90  P        PICTURE" /* left arrow */ )
	PORT_CODE ( KEYCODE_LEFT )
	PORT_CODE ( KEYCODE_P )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "EVAL O   \xE2\x86\x92NUM UNDO" )
	PORT_CODE ( KEYCODE_O )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "STO  N   DEF   RCL" )
	PORT_CODE ( KEYCODE_N )

		PORT_BIT ( 0xffe0, 0, IPT_UNUSED )


		PORT_START( "LINE7" ) /* OUT = 0x080 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "NXT  L   PREV  MENU" )
	PORT_CODE ( KEYCODE_L )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "\xE2\x86\x91  K          STACK" /* up arrow */ )
	PORT_CODE ( KEYCODE_UP )
	PORT_CODE ( KEYCODE_K )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "VAR  J         MEMORY" )
	PORT_CODE ( KEYCODE_J )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "CST  I         MODES" )
	PORT_CODE ( KEYCODE_I )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "PRG  H         CHARS" )
	PORT_CODE ( KEYCODE_H )

		PORT_BIT ( 0xffe0, 0, IPT_UNUSED )


		PORT_START( "LINE8" ) /* OUT = 0x100 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "F" )
	PORT_CODE ( KEYCODE_F )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "E" )
	PORT_CODE ( KEYCODE_E )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "D" )
	PORT_CODE ( KEYCODE_D )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "C" )
	PORT_CODE ( KEYCODE_C )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "B" )
	PORT_CODE ( KEYCODE_B )

		PORT_BIT ( 0xffe0, 0, IPT_UNUSED )


		PORT_START( "ON" ) /* ON key, appears on all OUT lines */

	PORT_BIT  ( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "ON  CANCEL CONT OFF" )
	PORT_CODE ( KEYCODE_ESC )
	PORT_CODE ( KEYCODE_HOME )

		PORT_BIT ( 0x7fff, 0, IPT_UNUSED )

INPUT_PORTS_END




/*
   keyboard layout for 49 G model

   -------------------------------------------------
   |   F1  |   F2  |   F3  |   F4  |   F5  |  F6   |
   |-----------------------------------------------|
   |   APPS  |  MODE  |  TOOL  |        up         |
   |---------+--------+--------| left        right |
   |   VAR   |  STO   |  NXT   |       down        |
   |---------+--------+--------+-------------------|
   |   HIST  |  CAT   |  EQW   |  SYMB   |   <=    |
   |---------+--------+--------+---------+---------|
   |   y^x   |  sqrt  |  SIN   |  COS    |   TAN   |
   |---------+--------+--------+---------+---------|
   |   EEX   |  +/-   |   X    |  1/x    |    /    |
   |---------+--------+--------+---------+---------|
   |  alpha  |   7    |   8    |   9     |    *    |
   |---------+--------+--------+---------+---------|
   |   blue  |   4    |   5    |   6     |    -    |
   |---------+--------+--------+---------+---------|
   |   red   |   1    |   2    |   3     |    +    |
   |---------+--------+--------+---------+---------|
   |   ON    |   0    |    .   |  SPC    |  ENTER  |
   -------------------------------------------------

   * 51 keys
   * including 3 modifier keys:
     - a red shift (upper right)
     - a blue shift (upper left)
     - a cyan alpha key (lower right)
*/


static INPUT_PORTS_START( hp49g_kbd )

		PORT_START( "LINE0" ) /* OUT = 0x001 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "ENTER   ANS  NUM")
	PORT_CODE ( KEYCODE_ENTER )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "+  { }  \xC2\xAB \xC2\xBB")  /* << >> */
	PORT_CODE ( KEYCODE_EQUALS )
	PORT_CODE ( KEYCODE_PLUS_PAD )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "-  ( )  _")
	PORT_CODE ( KEYCODE_MINUS )
	PORT_CODE ( KEYCODE_MINUS_PAD )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "*  [ ]  \" \"")
	PORT_CODE ( KEYCODE_ASTERISK )
	PORT_CODE ( KEYCODE_CLOSEBRACE )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "/  ABS  ARG  Z")
	PORT_CODE ( KEYCODE_SLASH )
	PORT_CODE ( KEYCODE_SLASH_PAD )
	PORT_CODE ( KEYCODE_Z)

	PORT_BIT  ( 32, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "TAN  ATAN  \xE2\x88\xAB  U") /* integral */
	PORT_CODE ( KEYCODE_U )

	PORT_BIT  ( 64, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "\xE2\x87\x90  DEL  CLEAR") /* double left arrow */
	PORT_CODE ( KEYCODE_BACKSPACE )
	PORT_CODE ( KEYCODE_DEL )
	PORT_CODE ( KEYCODE_DEL_PAD )

	PORT_BIT  ( 128, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "NXT  PREV  PASTE  L")
	PORT_CODE ( KEYCODE_L )

		PORT_BIT ( 0x7f00, 0, IPT_UNUSED )


		PORT_START( "LINE1" ) /* OUT = 0x002 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "SPC  \xCF\x80  ,")  /* pi */
	PORT_CODE ( KEYCODE_SPACE )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "3  #  BASE")
	PORT_CODE ( KEYCODE_3 )
	PORT_CODE ( KEYCODE_3_PAD )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "6  CONVERT  UNITS")
	PORT_CODE ( KEYCODE_6 )
	PORT_CODE ( KEYCODE_6_PAD )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "9  FINANCE  TIME")
	PORT_CODE ( KEYCODE_9 )
	PORT_CODE ( KEYCODE_9_PAD )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "1/x  \xE2\x89\xA5  >  Y") /* >= */
	PORT_CODE ( KEYCODE_Y )

	PORT_BIT  ( 32, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "COS  ACOS  \xE2\x88\x82  T") /* delta */
	PORT_CODE ( KEYCODE_T )

	PORT_BIT  ( 64, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "SYMB  MTH  EVAL  P")
	PORT_CODE ( KEYCODE_P )

	PORT_BIT  ( 128, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "STO\xE2\x8A\xB3  RCL  CUT  K")
	PORT_CODE ( KEYCODE_K )

		PORT_BIT ( 0x7f00, 0, IPT_UNUSED )


		PORT_START( "LINE2" ) /* OUT = 0x004 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( ".  : :  \xE2\x86\xb5") /* return arrow */
	PORT_CODE ( KEYCODE_STOP )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "2  DEF  LIB")
	PORT_CODE ( KEYCODE_2 )
	PORT_CODE ( KEYCODE_2_PAD )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "5  MATRICES  STAT")
	PORT_CODE ( KEYCODE_5 )
	PORT_CODE ( KEYCODE_5_PAD )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "8  EXP&LN  TRIG")
	PORT_CODE ( KEYCODE_8 )
	PORT_CODE ( KEYCODE_8_PAD )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "X  \xE2\x89\xA4  <  X") /* <= */
	PORT_CODE ( KEYCODE_X )

	PORT_BIT  ( 32, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "SIN  ASIN  \xE2\x88\x91  S") /* sum */
	PORT_CODE ( KEYCODE_S )

	PORT_BIT  ( 64, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "EQW  MTRW  '  O")
	PORT_CODE ( KEYCODE_O )

	PORT_BIT  ( 128, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "VAR  UPDIR  COPY  J")
	PORT_CODE ( KEYCODE_J )

		PORT_BIT ( 0x7f00, 0, IPT_UNUSED )


		PORT_START( "LINE3" ) /* OUT = 0x008 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "0  \xE2\x88\x9E  \xE2\x86\x92") /* infinity, right arrow */
	PORT_CODE ( KEYCODE_0 )
	PORT_CODE ( KEYCODE_0_PAD )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "1  ARITH  CMPLX")
	PORT_CODE ( KEYCODE_1 )
	PORT_CODE ( KEYCODE_1_PAD )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "4  CALC  ALG")
	PORT_CODE ( KEYCODE_4 )
	PORT_CODE ( KEYCODE_4_PAD )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "7  SSLV  NUMSLV")
	PORT_CODE ( KEYCODE_7 )
	PORT_CODE ( KEYCODE_7_PAD )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "+/-  \xE2\x89\xA0  =  W") /* =/ */
	PORT_CODE ( KEYCODE_W )

	PORT_BIT  ( 32, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "\xE2\x88\x9A  x^2  sqrt(x,y)  R") /* square root */
	PORT_CODE ( KEYCODE_R )

	PORT_BIT  ( 64, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "CAT  PRG  CHARS  N")
	PORT_CODE ( KEYCODE_N )

	PORT_BIT  ( 128, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "TOOL i  |  I")
	PORT_CODE ( KEYCODE_I )

		PORT_BIT ( 0x7f00, 0, IPT_UNUSED )


		PORT_START( "LINE4" ) /* OUT = 0x010 */

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "EEX  10^x  LOG  V")
	PORT_CODE ( KEYCODE_V )

	PORT_BIT  ( 32, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "y^x  e^x  LN  Q")
	PORT_CODE ( KEYCODE_Q )

	PORT_BIT  ( 64, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "HIST  CMD  UNDO  M")
	PORT_CODE ( KEYCODE_M )

	PORT_BIT  ( 128, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "MODE  CUSTOM  END  H")
	PORT_CODE ( KEYCODE_H )

		PORT_BIT ( 0x7f0f, 0, IPT_UNUSED )


		PORT_START( "LINE5" ) /* OUT = 0x020 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "F1  Y=  A")
	PORT_CODE ( KEYCODE_A )
	PORT_CODE ( KEYCODE_F1 )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "F2  WIN  B")
	PORT_CODE ( KEYCODE_B )
	PORT_CODE ( KEYCODE_F2 )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "F3  GRAPH  C")
	PORT_CODE ( KEYCODE_C )
	PORT_CODE ( KEYCODE_F3 )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "F4  2D/3D  D")
	PORT_CODE ( KEYCODE_D )
	PORT_CODE ( KEYCODE_F4 )

	PORT_BIT  ( 16, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "F5  TBLSET  E")
	PORT_CODE ( KEYCODE_E )
	PORT_CODE ( KEYCODE_F5 )

	PORT_BIT  ( 32, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "F6  TABLE  F")
	PORT_CODE ( KEYCODE_F )
	PORT_CODE ( KEYCODE_F6 )

	PORT_BIT  ( 128, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "APPS  FILES  BEGIN  G")
	PORT_CODE ( KEYCODE_G )

		PORT_BIT ( 0x7f40, 0, IPT_UNUSED )


		PORT_START( "LINE6" ) /* OUT = 0x040 */

	PORT_BIT  ( 1, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "\xE2\x86\x92") /* right arrow */
	PORT_CODE ( KEYCODE_RIGHT )

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "\xE2\x86\x93") /* down arrow */
	PORT_CODE ( KEYCODE_DOWN )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "\xE2\x86\x90") /* left arrow */
	PORT_CODE ( KEYCODE_LEFT )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "\xE2\x86\x91") /* up arrow */
	PORT_CODE ( KEYCODE_UP )

		PORT_BIT ( 0x7ff0, 0, IPT_UNUSED )


		PORT_START( "LINE7" ) /* OUT = 0x080 */

	PORT_BIT  ( 2, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "right shift")
	PORT_CODE ( KEYCODE_RSHIFT )

	PORT_BIT  ( 4, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "left shift")
	PORT_CODE ( KEYCODE_LSHIFT )

	PORT_BIT  ( 8, IP_ACTIVE_HIGH, IPT_KEYBOARD )
		PORT_NAME ( "\xCE\xB1  USER  ENTRY") /* alpha */
	PORT_CODE ( KEYCODE_LALT )

		PORT_BIT ( 0x7ff1, 0, IPT_UNUSED )


		PORT_START( "LINE8" ) /* OUT = 0x100 */

		PORT_BIT ( 0xffff, 0, IPT_UNUSED )


		PORT_START( "ON" ) /* ON key, appears on all OUT lines */

	PORT_BIT  ( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD )
	PORT_NAME ( "ON  CONT  OFF  CANCEL" )
	PORT_CODE ( KEYCODE_ESC )
	PORT_CODE ( KEYCODE_HOME )

		PORT_BIT ( 0x7fff, 0, IPT_UNUSED )

INPUT_PORTS_END




/*  BATTERY
    -------
 */

static INPUT_PORTS_START( hp48_battery )
		PORT_START( "BATTERY" )
		PORT_CONFNAME ( 0x03, 0, "Battery status" )
		PORT_CONFSETTING ( 0x00, DEF_STR( Normal ) )
		PORT_CONFSETTING ( 0x01, DEF_STR( Low ) )
		PORT_CONFSETTING ( 0x02, "Very low" )
INPUT_PORTS_END



/*  MAIN
    -----
 */

static INPUT_PORTS_START( hp48sx )
		PORT_INCLUDE( hp48sx_kbd )
		PORT_INCLUDE( hp48_battery )
INPUT_PORTS_END

static INPUT_PORTS_START( hp48gx )
		PORT_INCLUDE( hp48gx_kbd )
		PORT_INCLUDE( hp48_battery )
INPUT_PORTS_END

static INPUT_PORTS_START( hp49g )
		PORT_INCLUDE( hp49g_kbd )
		PORT_INCLUDE( hp48_battery )
INPUT_PORTS_END


/**************************** memory *******************************/

/* In memory, nibbles are unpacked: one nibble at each address.
   This is due to the way the SATURN emulation is done.
   As a consequence only the 4 lower bits of each byte is used, the 4 higher
   bits being zeros.
   Another consequence is that ROMs must be unpacked before use.

   Because of the complex memory manager, actual address mapping is done at
   run-time.
 */

void hp48_state::hp48(address_map &map)
{
	map(0x00000, 0xfffff).noprw(); /* configured at run-time */
}


/*************************** driver ********************************/

void hp48_state::hp48_common(machine_config &config)
{
	/* cpu */
	SATURN(config, m_maincpu, 3937007); /* almost 4 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &hp48_state::hp48);
	m_maincpu->out_func().set(FUNC(hp48_state::reg_out));
	m_maincpu->in_func().set(FUNC(hp48_state::reg_in));
	m_maincpu->reset_func().set(FUNC(hp48_state::mem_reset));
	m_maincpu->config_func().set(FUNC(hp48_state::mem_config));
	m_maincpu->unconfig_func().set(FUNC(hp48_state::mem_unconfig));
	m_maincpu->id_func().set(FUNC(hp48_state::mem_id));
	m_maincpu->crc_func().set(FUNC(hp48_state::mem_crc));
	m_maincpu->rsi_func().set(FUNC(hp48_state::rsi));

	/* memory */
	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	/* video */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(64);
	m_screen->set_vblank_time(0);
	m_screen->set_size(131, 64);
	m_screen->set_visarea(0, 130, 0, 63);
	m_screen->set_screen_update(FUNC(hp48_state::screen_update_hp48));
	m_screen->set_palette(m_palette);

	/* monochrome, but with varying contrast and grayscale */
	PALETTE(config, m_palette, FUNC(hp48_state::hp48_palette), 256);

	/* sound */
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.5);
}

void hp48_state::hp48gx(machine_config &config)
{
	hp48_common(config);
	MCFG_MACHINE_START_OVERRIDE(hp48_state, hp48gx)

	/* expansion ports */
	HP48_PORT(config, m_port[0], HP48_CE2,     128*1024);
	HP48_PORT(config, m_port[1], HP48_NCE3, 4*1024*1024);

	/* serial I/O */
	//MCFG_XMODEM_ADD("rs232_x", hp48_xmodem_rs232_conf)
	//MCFG_KERMIT_ADD("rs232_k", hp48_kermit_rs232_conf)
}

void hp48_state::hp48g(machine_config &config)
{
	hp48_common(config);
	MCFG_MACHINE_START_OVERRIDE(hp48_state, hp48g);

	/* serial I/O */
	//MCFG_XMODEM_ADD("rs232_x", hp48_xmodem_rs232_conf)
	//MCFG_KERMIT_ADD("rs232_k", hp48_kermit_rs232_conf)
}

void hp48_state::hp48gp(machine_config &config)
{
	hp48_common(config);
	MCFG_MACHINE_START_OVERRIDE(hp48_state, hp48gp)

	/* serial I/O */
	//MCFG_XMODEM_ADD("rs232_x", hp48_xmodem_rs232_conf)
	//MCFG_KERMIT_ADD("rs232_k", hp48_kermit_rs232_conf)
}

void hp48_state::hp48sx(machine_config &config)
{
	hp48_common(config);
	m_maincpu->set_clock(2000000);
	MCFG_MACHINE_START_OVERRIDE(hp48_state, hp48sx)

	/* expansion ports */
	HP48_PORT(config, m_port[0], HP48_CE1, 128*1024);
	HP48_PORT(config, m_port[1], HP48_CE2, 128*1024);

	/* serial I/O */
	//MCFG_KERMIT_ADD("rs232_k", hp48_kermit_rs232_conf)
}

void hp48_state::hp48s(machine_config &config)
{
	hp48_common(config);
	m_maincpu->set_clock(2000000);
	MCFG_MACHINE_START_OVERRIDE(hp48_state, hp48s)

	/* serial I/O */
	//MCFG_KERMIT_ADD("rs232_k", hp48_kermit_rs232_conf)
}

void hp48_state::hp49g(machine_config &config)
{
	hp48_common(config);
	MCFG_MACHINE_START_OVERRIDE(hp48_state, hp49g)

	/* serial I/O */
	//MCFG_XMODEM_ADD("rs232_x", hp48_xmodem_rs232_conf)
	//MCFG_KERMIT_ADD("rs232_k", hp48_kermit_rs232_conf)
}

/**************************** I/O **********************************/

//static const xmodem_config hp48_xmodem_rs232_conf = { &hp48_rs232_start_recv_byte };
//static const kermit_config hp48_kermit_rs232_conf = { &hp48_rs232_start_recv_byte };


/**************************** ROMs *********************************/

/* In ROMs, nibbles are packed into bytes, the lowest significant nibble in each byte
   has the lowest address on the HP.

   Note that some ROMs you can find on the Internet has the reverse the two nibbles.
   First byte should read 32 in hexa, not 23.
 */


/* These ROMS are common to the G, GX, and G+ models.
   The ROM detects whether it runs a G or a GX by simply testing the memory:
   if there are 32 KB (i.e., addresses wraps-around at 0x10000), it is a G; if there are
   128 KB, it is a GX.
   When a G is detected, some specially optimized routines may be used (they use the fact that
   no extension may be physically present).
   The G+ model has always revision R.
 */
ROM_START( hp48gx )
	ROM_REGION( 0x80000, "maincpu", 0 )
	ROM_DEFAULT_BIOS("r")

	/* in chronological order, from first revision, version r is default*/
	ROM_SYSTEM_BIOS( 0, "k", "Version K" )
	ROMX_LOAD( "gxrom-k", 0x00000, 0x80000, CRC(bdd5d2ee) SHA1(afa1498238e991b1e3d07fb8b4c227b115f7bcc1), ROM_BIOS(0) )

	ROM_SYSTEM_BIOS( 1, "l", "Version L" )
	ROMX_LOAD( "gxrom-l", 0x00000, 0x80000, CRC(70958e6b) SHA1(8eebac69ff804086247b989bf320e57a2d8a59a7), ROM_BIOS(1) )

	ROM_SYSTEM_BIOS( 2, "m", "Version M" )
	ROMX_LOAD( "gxrom-m", 0x00000, 0x80000, CRC(e21a09e4) SHA1(09932d543594e459eeb94a79654168cd15e79a87), ROM_BIOS(2) )

	/* there does not seem to exist an N revision? */

	ROM_SYSTEM_BIOS( 3, "p", "Version P" )
	ROMX_LOAD( "gxrom-p", 0x00000, 0x80000, CRC(022d46df) SHA1(877a536865641f096212d1ce7296f580afbd6a2d), ROM_BIOS(3) )

	ROM_SYSTEM_BIOS( 4, "r", "Version R" )
	ROMX_LOAD( "gxrom-r", 0x00000, 0x80000, CRC(00ee1a62) SHA1(5705fc9ea791916c4456ac35e22275862411db9b), ROM_BIOS(4) )

ROM_END

#define rom_hp48g  rom_hp48gx
#define rom_hp48gp rom_hp48gx


/* These ROMS are common to the S and SX models.
   The only difference is that, the S being later, it was only shipped with revisions
   E and later.

   (Note that G/GX revisions start at K, after the S/S revisions ends...)
 */

ROM_START( hp48sx )
	ROM_REGION( 0x40000, "maincpu", 0 )
	ROM_DEFAULT_BIOS("j")

	/* in chronological order, from first revision, version j is default*/
	ROM_SYSTEM_BIOS( 0, "a", "Version A" )
	ROMX_LOAD( "sxrom-a", 0x00000, 0x40000, CRC(a87696c7) SHA1(3271b103ad99254d069e20171beb418ace72cc90), ROM_BIOS(0) )

	ROM_SYSTEM_BIOS( 1, "b", "Version B" )
	ROMX_LOAD( "sxrom-b", 0x00000, 0x40000, CRC(034f6ce4) SHA1(acd256f2efee868ce402008f4131d94b312e60bc), ROM_BIOS(1) )

	ROM_SYSTEM_BIOS( 2, "c", "Version C" )
	ROMX_LOAD( "sxrom-c", 0x00000, 0x40000, CRC(a9a0279d) SHA1(fee852d43ae6941d07a9d0d31f37e68e4f9051b1), ROM_BIOS(2) )

	ROM_SYSTEM_BIOS( 3, "d", "Version D" )
	ROMX_LOAD( "sxrom-d", 0x00000, 0x40000, CRC(f8f5dc58) SHA1(3be5f895f4c731fd4c863237c7342cab4e8c42b1), ROM_BIOS(3) )

	ROM_SYSTEM_BIOS( 4, "e", "Version E" )
	ROMX_LOAD( "sxrom-e", 0x00000, 0x40000, CRC(704ffa08) SHA1(0d498d135bf729c1d775cce522528837729e2e94), ROM_BIOS(4) )

	ROM_SYSTEM_BIOS( 5, "j", "Version J" )
	ROMX_LOAD( "sxrom-j", 0x00000, 0x40000, CRC(1a6378ef) SHA1(5235f5379f1fd7edfe9bb6bf466b60d279163e73), ROM_BIOS(5) )

	/* no F, G, H, I revisions? */

ROM_END

#define rom_hp48s rom_hp48sx

ROM_START( hp38g )
	ROM_REGION( 0x80000, "maincpu", 0 )
	ROM_LOAD( "hp38g.rom", 0x00000, 0x80000, CRC(31d9affc) SHA1(bab3f5907a16cbb087943fd77230514af8fd5ac0))
ROM_END

ROM_START( hp39g )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "hp39g.rom", 0x00000, 0x100000, CRC(28268fdc) SHA1(57a2b19075fe60307a9affa79d8e7cb550c621c3))
ROM_END

ROM_START( hp49g )
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "1.00", "Version C-1.00" )
	ROMX_LOAD("hp49gv100.rom", 0x00000, 0x200000, CRC(64c9826a) SHA1(da25371b97d439fc0003cb786dba143ee2be9160), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "1.05", "Version C-1.05" )
	ROMX_LOAD("hp49gv105.rom", 0x00000, 0x200000, CRC(cf777cac) SHA1(b1d063b6e95083799aa990e4a2718214a38a372f), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "1.10", "Version C-1.10" )
	ROMX_LOAD("hp49gv110.rom", 0x00000, 0x200000, CRC(e391efbd) SHA1(d4abad60f38faf4cb2d2d97804a24f54589dfa10), ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "1.16", "Version C-1.16" )
	ROMX_LOAD("hp49gv116.rom", 0x00000, 0x200000, CRC(dcc0b39c) SHA1(46f64b4731f5964eb114060b733aab2b23b4180c), ROM_BIOS(3))
	ROM_SYSTEM_BIOS( 4, "1.18", "Version C-1.18" )
	ROMX_LOAD("hp49gv118.rom", 0x00000, 0x200000, CRC(73a6a195) SHA1(3f283fe15a64c5cbc8c1b1254e10965957f58a84), ROM_BIOS(4))
	ROM_SYSTEM_BIOS( 5, "1.19", "Version B-1.19-6" )
	ROMX_LOAD("hp49gv119.rom", 0x00000, 0x200000, CRC(75218a18) SHA1(ec0f661f0aa7158d1f6df61f24410260b5324fa9), ROM_BIOS(5))
ROM_END

COMP( 1990, hp48sx, 0,      0, hp48sx, hp48sx, hp48_state, init_hp48, "Hewlett Packard", "HP48SX", 0 )
COMP( 1991, hp48s,  hp48sx, 0, hp48s,  hp48sx, hp48_state, init_hp48, "Hewlett Packard", "HP48S",  0 )
COMP( 1993, hp48gx, 0,      0, hp48gx, hp48gx, hp48_state, init_hp48, "Hewlett Packard", "HP48GX", 0 )
COMP( 1993, hp48g,  hp48gx, 0, hp48g,  hp48gx, hp48_state, init_hp48, "Hewlett Packard", "HP48G",  0 )
COMP( 1998, hp48gp, hp48gx, 0, hp48gp, hp48gx, hp48_state, init_hp48, "Hewlett Packard", "HP48G+", 0 )
COMP( 1999, hp49g,  0,      0, hp49g,  hp49g,  hp48_state, init_hp48, "Hewlett Packard", "HP49G",  0 )
COMP( 1995, hp38g,  0,      0, hp48g,  hp48gx, hp48_state, init_hp48, "Hewlett Packard", "HP38G",  0 )
COMP( 2000, hp39g,  0,      0, hp48g,  hp48gx, hp48_state, init_hp48, "Hewlett Packard", "HP39G",  MACHINE_NOT_WORKING )



hp49gp.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Tim Schuerewegen
/*******************************************************************************

    Hewlett Packard 49G+ Graphing Calculator

*******************************************************************************/

#include "emu.h"
#include "cpu/arm7/arm7.h"
#include "machine/s3c2410.h"
#include "screen.h"

#include <cstdarg>


namespace {

#define VERBOSE_LEVEL ( 0 )

struct lcd_spi_t
{
	int l1 = 0;
	int data = 0;
	int l3 = 0;
	uint32_t shift = 0, bits = 0;
};

class hp49gp_state : public driver_device
{
public:
	hp49gp_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_s3c2410(*this, "s3c2410"),
		m_steppingstone(*this, "steppingstone"),
		m_maincpu(*this, "maincpu")
	{ }

	void hp49gp(machine_config &config);

	void init_hp49gp();

	DECLARE_INPUT_CHANGED_MEMBER(port_changed);

private:
	uint32_t m_port[9];
	required_device<s3c2410_device> m_s3c2410;
	required_shared_ptr<uint32_t> m_steppingstone;
	lcd_spi_t m_lcd_spi;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	uint32_t s3c2410_gpio_port_r(offs_t offset);
	void s3c2410_gpio_port_w(offs_t offset, uint32_t data);
	inline void verboselog(int n_level, const char *s_fmt, ...) ATTR_PRINTF(3,4);
	void lcd_spi_reset();
	void lcd_spi_init();
	void lcd_spi_line_w( int line, int data);
	int lcd_spi_line_r( int line);
	required_device<cpu_device> m_maincpu;
	void hp49gp_map(address_map &map) ATTR_COLD;
};

/***************************************************************************
    MACHINE HARDWARE
***************************************************************************/

// LCD SPI

enum
{
	LCD_SPI_LINE_1,
	LCD_SPI_LINE_DATA,
	LCD_SPI_LINE_3
};

inline void hp49gp_state::verboselog(int n_level, const char *s_fmt, ...)
{
	if (VERBOSE_LEVEL >= n_level)
	{
		va_list v;
		char buf[32768];
		va_start( v, s_fmt);
		vsprintf( buf, s_fmt, v);
		va_end( v);
		logerror( "%s: %s", machine().describe_context( ), buf);
	}
}

void hp49gp_state::lcd_spi_reset( )
{
	verboselog( 5, "lcd_spi_reset\n");
	m_lcd_spi.l1 = 0;
	m_lcd_spi.data = 0;
	m_lcd_spi.l3 = 0;
}

void hp49gp_state::lcd_spi_init( )
{
	verboselog( 5, "lcd_spi_init\n");
	lcd_spi_reset();
}

void hp49gp_state::lcd_spi_line_w( int line, int data)
{
	switch (line)
	{
		case LCD_SPI_LINE_1 :
		{
			if (data != m_lcd_spi.l1)
			{
				if (data == 0)
				{
					m_lcd_spi.shift = 0;
					m_lcd_spi.bits = 0;
				}
				verboselog( 5, "LCD_SPI_LINE_1 <- %d\n", data);
				m_lcd_spi.l1 = data;
			}
		}
		break;
		case LCD_SPI_LINE_DATA :
		{
			if (data != m_lcd_spi.data)
			{
				verboselog( 5, "LCD_SPI_LINE_DATA <- %d\n", data);
				m_lcd_spi.data = data;
			}
		}
		break;
		case LCD_SPI_LINE_3 :
		{
			if (data != m_lcd_spi.l3)
			{
				if ((data != 0) && (m_lcd_spi.l1 == 0))
				{
					verboselog( 5, "LCD SPI write bit %d\n", m_lcd_spi.data ? 1 : 0);
					if (m_lcd_spi.bits < 8)
					{
						m_lcd_spi.shift = (m_lcd_spi.shift << 1) | (m_lcd_spi.data ? 1 : 0);
					}
					m_lcd_spi.bits++;
					if (m_lcd_spi.bits == 8)
					{
						verboselog( 5, "LCD SPI write byte %02X\n", m_lcd_spi.shift);
					}
					else if (m_lcd_spi.bits == 9)
					{
						verboselog( 5, "LCD SPI write ack %d\n", (m_lcd_spi.data ? 1 : 0));
					}
				}
				verboselog( 5, "LCD_SPI_LINE_3 <- %d\n", data);
				m_lcd_spi.l3 = data;
			}
		}
		break;
	}
}

int hp49gp_state::lcd_spi_line_r( int line)
{
	switch (line)
	{
		case LCD_SPI_LINE_1 :
		{
			verboselog( 7, "LCD_SPI_LINE_1 -> %d\n", m_lcd_spi.l1);
			return m_lcd_spi.l1;
		}
		case LCD_SPI_LINE_DATA :
		{
			verboselog( 7, "LCD_SPI_LINE_DATA -> %d\n", m_lcd_spi.data);
			return m_lcd_spi.data;
		}
		case LCD_SPI_LINE_3 :
		{
			verboselog( 7, "LCD_SPI_LINE_3 -> %d\n", m_lcd_spi.l3);
			return m_lcd_spi.l3;
		}
	}
	return 0;
}

// I/O PORT

uint32_t hp49gp_state::s3c2410_gpio_port_r(offs_t offset)
{
	uint32_t data = m_port[offset];
	switch (offset)
	{
		case S3C2410_GPIO_PORT_C :
		{
			data = data | 0xF000;
		}
		break;
		case S3C2410_GPIO_PORT_D :
		{
			data = data | 0x0008;
			data = data & ~0x3200;
			data |= (lcd_spi_line_r( LCD_SPI_LINE_1) ? 1 : 0) << 9;
			data |= (lcd_spi_line_r( LCD_SPI_LINE_DATA) ? 1 : 0) << 12;
			data |= (lcd_spi_line_r( LCD_SPI_LINE_3) ? 1 : 0) << 13;
		}
		break;
		case S3C2410_GPIO_PORT_E :
		{
			data = data | 0xC000;
		}
		break;
		case S3C2410_GPIO_PORT_F :
		{
			data = data | 0x0008;
		}
		break;
		case S3C2410_GPIO_PORT_G :
		{
			data = data & ~0xFF00;
			if ((data & 0x02) == 0) data |= (ioport( "ROW1")->read() << 8);
			if ((data & 0x04) == 0) data |= (ioport( "ROW2")->read() << 8);
			if ((data & 0x08) == 0) data |= (ioport( "ROW3")->read() << 8);
			if ((data & 0x10) == 0) data |= (ioport( "ROW4")->read() << 8);
			if ((data & 0x20) == 0) data |= (ioport( "ROW5")->read() << 8);
			if ((data & 0x40) == 0) data |= (ioport( "ROW6")->read() << 8);
			if ((data & 0x80) == 0) data |= (ioport( "ROW7")->read() << 8);
		}
		break;
		case S3C2410_GPIO_PORT_H :
		{
			data = (data | 0x0080) & ~0x0040;
		}
		break;
	}
	return data;
}

void hp49gp_state::s3c2410_gpio_port_w(offs_t offset, uint32_t data)
{
	m_port[offset] = data;
	switch (offset)
	{
		case S3C2410_GPIO_PORT_D :
		{
			lcd_spi_line_w( LCD_SPI_LINE_1, BIT( data, 9) ? 1 : 0);
			lcd_spi_line_w( LCD_SPI_LINE_DATA, BIT( data, 12) ? 1 : 0);
			lcd_spi_line_w( LCD_SPI_LINE_3, BIT( data, 13) ? 1 : 0);
		}
		break;
	}
}

// ...

INPUT_CHANGED_MEMBER(hp49gp_state::port_changed)
{
	m_s3c2410->s3c2410_request_eint(param + 8);
}

// ...

void hp49gp_state::machine_start()
{
}

void hp49gp_state::machine_reset()
{
	m_maincpu->reset();
}

/***************************************************************************
    ADDRESS MAPS
***************************************************************************/

void hp49gp_state::hp49gp_map(address_map &map)
{
	map(0x00000000, 0x001fffff).rom();
	map(0x08000000, 0x0801ffff).ram();
	map(0x08020000, 0x0803ffff).ram();
	map(0x08040000, 0x0807ffff).ram();
	map(0x40000000, 0x40000fff).ram().share("steppingstone");
}

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

void hp49gp_state::init_hp49gp()
{
	uint8_t *rom = (uint8_t *)memregion( "maincpu")->base();
	memcpy( m_steppingstone, rom, 1024);
	lcd_spi_init();
}

void hp49gp_state::hp49gp(machine_config &config)
{
	ARM9(config, m_maincpu, 400000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &hp49gp_state::hp49gp_map);

	PALETTE(config, "palette").set_entries(32768);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(160, 85);
	screen.set_visarea(0, 131 - 1, 0, 80 - 1);
	screen.set_screen_update("s3c2410", FUNC(s3c2410_device::screen_update));

	S3C2410(config, m_s3c2410, 12000000);
	m_s3c2410->set_palette_tag("palette");
	m_s3c2410->set_screen_tag("screen");
	m_s3c2410->gpio_port_r_callback().set(FUNC(hp49gp_state::s3c2410_gpio_port_r));
	m_s3c2410->gpio_port_w_callback().set(FUNC(hp49gp_state::s3c2410_gpio_port_w));
	m_s3c2410->set_lcd_flags(S3C24XX_INTERFACE_LCD_REVERSE);
}

static INPUT_PORTS_START( hp49gp )
	PORT_START("ROW1")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp49gp_state::port_changed), 1) PORT_NAME("F1 | A | Y=") PORT_CODE(KEYCODE_F1) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0xDF, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("ROW2")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp49gp_state::port_changed), 2) PORT_NAME("F2 | B | WIN") PORT_CODE(KEYCODE_F2)
	PORT_BIT( 0xDF, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("ROW3")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp49gp_state::port_changed), 3) PORT_NAME("F3 | C | GRAPH") PORT_CODE(KEYCODE_F3)
	PORT_BIT( 0xDF, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("ROW4")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp49gp_state::port_changed), 4) PORT_NAME("F4 | D | 2D/3D") PORT_CODE(KEYCODE_F4)
	PORT_BIT( 0xDF, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("ROW5")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp49gp_state::port_changed), 5) PORT_NAME("F5 | E | TBLSET") PORT_CODE(KEYCODE_F5)
	PORT_BIT( 0xDF, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("ROW6")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp49gp_state::port_changed), 6) PORT_NAME("F6 | F | TABLE") PORT_CODE(KEYCODE_F6)
	PORT_BIT( 0xDF, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("ROW7")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp49gp_state::port_changed), 7) PORT_NAME("APPS | G | FILES | BEGIN") PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0xDF, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

/***************************************************************************
    GAME DRIVERS
***************************************************************************/

ROM_START( hp49gp )
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "319", "Version 3.19" )
	ROMX_LOAD( "319.bin", 0x0000, 0x4000, CRC(6bb5ebfb) SHA1(089c4e4ee7223b489e2f06c30aab0b89131d1c3c), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "315", "Version 3.15.04" )
	ROMX_LOAD( "31504.bin", 0x0000, 0x4000, CRC(9c71825e) SHA1(0a12b2b70a8573bc90ab5be06e6b2f814b8544ae), ROM_BIOS(1) )
ROM_END

} // anonymous namespace


COMP(2009, hp49gp, 0, 0, hp49gp, hp49gp, hp49gp_state, init_hp49gp, "Hewlett Packard", "HP49G+", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



hp64k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:F. Ulivi
//
// ***************************************
// Driver for HP 64000 development system
// ***************************************
//
// Documentation used for this driver:
// [1]  HP, manual 64100-90910, dec 83 rev. - Model 64100A mainframe service manual
// [2]  HP, manual 64941-90902, apr 83 rev. - Model 64941A Flexible disc (Floppy) drive
//                                            controller service manual
//
// A 64100A system ("mainframe" in HP docs) is built around a 13 slot card cage.
// The first 4 slots are reserved for specific card types:
// J1   I/O card
// J2   Display and RAM card
// J3   CPU card
// J4   Floppy interface card
//
// The rest of the slots are for CPU emulators, logic analyzers and so on (i.e. those
// cards doing the main functions of a development system).
// This driver emulates the first 4 cards only.
//
// All cards are interconnected by 2 separate buses originating from the CPU:
// memory (16-bit data & 16-bit addresses) and I/O (16-bit data and 6-bit addresses) buses.
// The addresses on I/O bus are split in a 4-bit PA (peripheral address) and a 2-bit IC
// (register address). See also HP_MAKE_IOADDR.
// For the address mapping on the memory bus see [1] pg 229.
// Reading the schematics is complicated by the fact that all data & address
// lines of the buses are inverted.
//
// A brief description of each emulated card follows.
//
// **********
// CPU card (64100-66521 or 64100-66532)
//
// This board holds the HP custom CPU with its massive heatsink, the BIOS roms and little else.
// U30      5061-3011   HP "hybrid" CPU @ 6.25 MHz
// U8
// U9
// U10
// U11
// U18
// U19
// U20
// U21      2732        16kw of BIOS EPROMs
//
// **********
// I/O card (64100-66520)
//
// This board has most of the I/O circuits of the system.
// It interfaces:
// - Keyboard
// - RS232 line
// - IEEE-488/HP-IB bus
// - Miscellaneous peripherals (watchdog, beeper, interrupt registers, option DIP switches)
//
// Emulation of beeper sound is far from correct: it should be a 2500 Hz tone inside an
// exponentially decaying envelope (a bell sound) whereas in the emulation it's inside a
// simple rectangular envelope.
//
// U20      HP "PHI"    Custom HP-IB interface microcontroller
// U28      i8251       RS232 UART
//
// **********
// Display card (64100-66530)
//
// This card has the main DRAM of the system (64 kw) and the CRT controller that generates
// the video image.
// The framebuffer is stored in the main DRAM starting at a fixed location (0xf9f0) and it is
// fed into the CRTC by a lot of discrete TTL ICs. The transfer of framebuffer from DRAM to
// CRTC is designed to refresh the whole DRAM in parallel. For some mysterious reason the first
// display row is always blanked (its 40 words of RAM are even used for the stack!).
//
// U33      i8275       CRT controller
// U60      2716        Character generator ROM
// U23-U30
// U38-U45  HM4864      64 kw of DRAM
//
// **********
// Floppy I/F card (64941-66501)
//
// This card is optional. It interfaces 2 5.25" double-side double-density floppy drives.
// The interfacing between the 16-bit CPU and the 8-bit FDC (WD1791) is quite complex. It is
// based around a FSM that sequences the access of DMA or CPU to FDC. This FSM is implemented
// by 2 small PROMs for which no dump (AFAIK) is available.
// I tried to reverse engineer the FSM by looking at the schematics and applying some sensible
// assumptions. Then I did a sort of "clean room" re-implementation. It appears to work correctly.
//
// U4       FD1791A     Floppy disk controller
//
// A brief summary of the reverse-engineered interface of this card follows.
//
// IC Content
// ==========
// 0  DMA transfers, all words in a block but the last one
// 1  Floppy I/F register, detailed below
// 2  DMA transfers, last word in a block
// 3  Diagnostic registers (not emulated)
//
// Floppy I/F register has 2 formats, one for writing and one for reading.
// Reading this register should always be preceded by a write that starts
// the read operation (bit 11 = 0: see below).
//
// Floppy I/F register format when writing:
// Bit Content
// ===========
// 15  Clear interrupts (1)
// 14  Direction of DMA transfers (1 = write to FDC, 0 = read from FDC)
// 13  DMA enable (1)
// 12  Reset FDC (1)
// 11  Direction of access to FDC/drive control (1 = write, 0 = read)
// 10  Access to either FDC (1) or drive control (0): this selects the
//     content of lower byte (both when writing and reading)
//  9  ~A1 signal of FDC
//  8  ~A0 signal of FDC
//
// 7-0 FDC data (when bit 10 = 1)
// 7-0 Drive control (when bit 10 = 0)
//
// Floppy I/F register format when reading:
// Bit Content
// ===========
// 15  Interrupt from FDC pending (1)
// 14  Interrupt from DMA pending (1)
// 13  Drive 1 media changed (1)
// 12  Drive 1 write protected (1)
// 11  Drive 1 ready (0)
// 10  Drive 0 media changed (1)
//  9  Drive 0 write protected (1)
//  8  Drive 0 ready (0)
//
// 7-0 FDC data (when bit 10 = 1)
// 7-0 Drive control (when bit 10 = 0)
//
// Drive control register
// Bit Content
// ===========
//  7  Floppy side selection
//  6  N/U
//  5  Reset drive 1 media change (1)
//  4  Enable drive 1 motor (0)
//  3  Enable drive 1 (0)
//  2  Reset drive 0 media change (1)
//  1  Enable drive 0 motor (0)
//  0  Enable drive 0 (0)
//

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/hphybrid/hphybrid.h"
#include "imagedev/floppy.h"
#include "machine/74123.h"
#include "machine/com8116.h"
#include "machine/i8251.h"
#include "machine/rescap.h"
#include "machine/timer.h"
#include "machine/wd_fdc.h"
#include "sound/beep.h"
#include "video/i8275.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"
#include "machine/phi.h"
#include "bus/ieee488/ieee488.h"


namespace {

#define BIT_MASK(n) (1U << (n))

// Macros to clear/set single bits
#define BIT_CLR(w , n)  ((w) &= ~BIT_MASK(n))
#define BIT_SET(w , n)  ((w) |= BIT_MASK(n))

class hp64k_state : public driver_device
{
public:
	hp64k_state(const machine_config &mconfig, device_type type, const char *tag);

	void hp64k(machine_config &config);

private:
	virtual void driver_start() override;
	//virtual void machine_start();
	virtual void video_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint8_t hp64k_crtc_filter(uint8_t data);
	void hp64k_crtc_w(offs_t offset, uint16_t data);
	void hp64k_crtc_drq_w(int state);
	void hp64k_crtc_vrtc_w(int state);

	I8275_DRAW_CHARACTER_MEMBER(crtc_display_pixels);

	uint16_t hp64k_rear_sw_r();

	uint8_t int_cb(offs_t offset);
	void hp64k_update_irl(void);
	void hp64k_irl_mask_w(uint16_t data);

	TIMER_DEVICE_CALLBACK_MEMBER(hp64k_kb_scan);
	uint16_t hp64k_kb_r();

	TIMER_DEVICE_CALLBACK_MEMBER(hp64k_line_sync);
	uint16_t hp64k_deltat_r();
	void hp64k_deltat_w(uint16_t data);

	uint16_t hp64k_slot_r(offs_t offset);
	void hp64k_slot_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	void hp64k_slot_sel_w(offs_t offset, uint16_t data);

	uint16_t hp64k_flp_r(offs_t offset);
	void hp64k_flp_w(offs_t offset, uint16_t data);
	void hp64k_flp_drq_w(int state);
	void hp64k_flp_intrq_w(int state);
	void hp64k_update_floppy_dma(void);
	void hp64k_update_floppy_irq(void);
	void hp64k_update_drv_ctrl(void);
	void hp64k_floppy0_rdy(int state);
	void hp64k_floppy1_rdy(int state);
	void hp64k_floppy_idx_cb(floppy_image_device *floppy , int state);
	void hp64k_floppy_wpt_cb(floppy_image_device *floppy , int state);

	uint16_t hp64k_usart_r(offs_t offset);
	void hp64k_usart_w(offs_t offset, uint16_t data);
	void hp64k_rxrdy_w(int state);
	void hp64k_txrdy_w(int state);
	void hp64k_txd_w(int state);
	void hp64k_dtr_w(int state);
	void hp64k_rts_w(int state);
	void hp64k_loopback_w(uint16_t data);
	void hp64k_update_loopback(void);
	void hp64k_rs232_rxd_w(int state);
	void hp64k_rs232_dcd_w(int state);
	void hp64k_rs232_cts_w(int state);

	uint16_t hp64k_phi_r(offs_t offset);
	void hp64k_phi_w(offs_t offset, uint16_t data);
	void hp64k_phi_int_w(int state);
	int hp64k_phi_sys_ctrl_r();

	void hp64k_beep_w(offs_t offset, uint16_t data);
	TIMER_DEVICE_CALLBACK_MEMBER(hp64k_beeper_off);

	void hp64k_baud_clk_w(int state);
	void cpu_io_map(address_map &map) ATTR_COLD;
	void cpu_mem_map(address_map &map) ATTR_COLD;

	required_device<hp_5061_3011_cpu_device> m_cpu;
	required_device<i8275_device> m_crtc;
	required_device<palette_device> m_palette;
	required_ioport_array<4> m_io_key;
	required_device<fd1791_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device_array<ttl74123_device, 2> m_ss;
	required_ioport m_rear_panel_sw;
	required_ioport m_rs232_sw;
	required_device<beep_device> m_beeper;
	required_device<timer_device> m_beep_timer;
	required_device<com8116_device> m_baud_rate;
	required_ioport m_s5_sw;
	required_device<i8251_device> m_uart;
	required_device<rs232_port_device> m_rs232;
	required_device<phi_device> m_phi;

	// Character generator
	const uint8_t *m_chargen;

	uint32_t m_crtc_ptr;
	bool m_crtc_drq;
	bool m_vrtc;

	// Interrupt handling
	uint8_t m_irl_mask;
	uint8_t m_irl_pending;

	// State of keyboard
	ioport_value m_kb_state[ 4 ];
	uint8_t m_kb_row_col;
	bool m_kb_scan_on;
	bool m_kb_pressed;

	// Slot selection
	std::vector<uint16_t> m_low32k_ram;
	uint8_t m_slot_select;
	uint8_t m_slot_map;

	// Floppy I/F
	uint8_t m_floppy_in_latch_msb;    // U23
	uint8_t m_floppy_in_latch_lsb;    // U38
	uint8_t m_floppy_out_latch_msb;   // U22
	uint8_t m_floppy_out_latch_lsb;   // U37
	uint8_t m_floppy_if_ctrl;     // U24
	bool m_floppy_dmaen;
	bool m_floppy_dmai;
	bool m_floppy_mdci;
	bool m_floppy_intrq;
	bool m_floppy_drq;
	bool m_floppy_wpt[2];
	uint8_t m_floppy_drv_ctrl;    // U39
	uint8_t m_floppy_status;      // U25

	typedef enum {
		HP64K_FLPST_IDLE,
		HP64K_FLPST_DMAWR1,
		HP64K_FLPST_DMAWR2,
		HP64K_FLPST_DMARD1,
		HP64K_FLPST_DMARD2
	} floppy_state_t;

	floppy_state_t m_floppy_if_state;
	floppy_image_device *m_current_floppy;

	// RS232 I/F
	bool m_16x_clk;
	bool m_baud_clk;
	uint8_t m_16x_div;
	bool m_loopback;
	bool m_txd_state;
	bool m_dtr_state;
	bool m_rts_state;

	// HPIB I/F
	uint8_t m_phi_reg;
};

void hp64k_state::cpu_mem_map(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	map(0x4000, 0x7fff).rw(FUNC(hp64k_state::hp64k_slot_r), FUNC(hp64k_state::hp64k_slot_w));
	map(0x8000, 0x8001).w(FUNC(hp64k_state::hp64k_crtc_w));
	map(0x8002, 0xffff).ram();
}

void hp64k_state::cpu_io_map(address_map &map)
{
	// PA = 0, IC = [0..3]
	// Keyboard input
	map(HP_MAKE_IOADDR( 0, 0), HP_MAKE_IOADDR( 0, 3)).r(FUNC(hp64k_state::hp64k_kb_r));
	// PA = 2, IC = [0..3]
	// Line sync interrupt clear/watchdog reset
	map(HP_MAKE_IOADDR( 2, 0), HP_MAKE_IOADDR( 2, 3)).rw(FUNC(hp64k_state::hp64k_deltat_r), FUNC(hp64k_state::hp64k_deltat_w));
	// PA = 4, IC = [0..3]
	// Floppy I/F
	map(HP_MAKE_IOADDR( 4, 0), HP_MAKE_IOADDR( 4, 3)).rw(FUNC(hp64k_state::hp64k_flp_r), FUNC(hp64k_state::hp64k_flp_w));
	// PA = 5, IC = [0..3]
	// Write to USART
	map(HP_MAKE_IOADDR( 5, 0), HP_MAKE_IOADDR( 5, 3)).w(FUNC(hp64k_state::hp64k_usart_w));
	// PA = 6, IC = [0..3]
	// Read from USART
	map(HP_MAKE_IOADDR( 6, 0), HP_MAKE_IOADDR( 6, 3)).r(FUNC(hp64k_state::hp64k_usart_r));
	// PA = 7, IC = 1
	// PHI
	map(HP_MAKE_IOADDR( 7, 1), HP_MAKE_IOADDR( 7, 1)).rw(FUNC(hp64k_state::hp64k_phi_r), FUNC(hp64k_state::hp64k_phi_w));
	// PA = 7, IC = 2
	// Rear-panel switches and loopback relay control
	map(HP_MAKE_IOADDR( 7, 2), HP_MAKE_IOADDR( 7, 2)).rw(FUNC(hp64k_state::hp64k_rear_sw_r), FUNC(hp64k_state::hp64k_loopback_w));
	// PA = 9, IC = [0..3]
	// Beeper control & interrupt status read
	map(HP_MAKE_IOADDR( 9, 0), HP_MAKE_IOADDR( 9, 3)).w(FUNC(hp64k_state::hp64k_beep_w));
	// PA = 10, IC = [0..3]
	// Slot selection
	map(HP_MAKE_IOADDR(10, 0), HP_MAKE_IOADDR(10, 3)).w(FUNC(hp64k_state::hp64k_slot_sel_w));
	// PA = 12, IC = [0..3]
	// Interrupt mask
	map(HP_MAKE_IOADDR(12, 0), HP_MAKE_IOADDR(12, 3)).w(FUNC(hp64k_state::hp64k_irl_mask_w));
}

hp64k_state::hp64k_state(const machine_config &mconfig, device_type type, const char *tag) :
	driver_device(mconfig , type , tag),
	m_cpu(*this , "cpu"),
	m_crtc(*this , "crtc"),
	m_palette(*this , "palette"),
	m_io_key(*this , "KEY%u" , 0U),
	m_fdc(*this , "fdc"),
	m_floppy(*this , "fdc:%u" , 0U),
	m_ss(*this , "fdc_rdy%u" , 0U),
	m_rear_panel_sw(*this , "rear_sw"),
	m_rs232_sw(*this , "rs232_sw"),
	m_beeper(*this , "beeper"),
	m_beep_timer(*this , "beep_timer"),
	m_baud_rate(*this , "baud_rate"),
	m_s5_sw(*this , "s5_sw"),
	m_uart(*this , "uart"),
	m_rs232(*this , "rs232"),
	m_phi(*this , "phi")
{
}

void hp64k_state::driver_start()
{
	// 32kW for lower RAM
	m_low32k_ram.resize(0x8000);
}

void hp64k_state::video_start()
{
	m_chargen = memregion("chargen")->base();
}

void hp64k_state::machine_reset()
{
	m_crtc_drq = false;
	m_vrtc = false;
	m_crtc_ptr = 0;
	m_irl_mask = 0;
	m_irl_pending = 0;
	memset(&m_kb_state[ 0 ] , 0 , sizeof(m_kb_state));
	m_kb_row_col = 0;
	m_kb_scan_on = true;
	m_slot_select = 0;
	m_slot_map = 3;
	m_floppy_if_ctrl = ~0;
	m_floppy_dmaen = false;
	m_floppy_dmai = false;
	m_floppy_mdci = false;
	m_floppy_intrq = false;
	m_floppy_drv_ctrl = ~0;
	m_floppy_if_state = HP64K_FLPST_IDLE;
	m_current_floppy = nullptr;
	m_floppy_wpt[0] = false;
	m_floppy_wpt[1] = false;
	m_beeper->set_state(0);
	m_baud_rate->str_w((m_s5_sw->read() >> 1) & 0xf);
	m_16x_clk = (m_rs232_sw->read() & 0x02) != 0;
	m_loopback = false;
	m_txd_state = true;
	m_dtr_state = true;
	m_rts_state = true;
	m_phi_reg = 0;
}

uint8_t hp64k_state::hp64k_crtc_filter(uint8_t data)
{
		bool inv = (data & 0xe0) == 0xe0;

		return inv ? (data & 0xf2) : data;
}

void hp64k_state::hp64k_crtc_w(offs_t offset, uint16_t data)
{
		m_crtc->write(offset == 0 , hp64k_crtc_filter((uint8_t)data));
}

void hp64k_state::hp64k_crtc_drq_w(int state)
{
		bool crtc_drq = state != 0;
		bool prev_crtc = m_crtc_drq;
		m_crtc_drq = crtc_drq;

		if (!prev_crtc && crtc_drq) {
				address_space& prog_space = m_cpu->space(AS_PROGRAM);

				uint16_t data = prog_space.read_word(m_crtc_ptr >> 1);
				data = m_crtc_ptr & 1 ? data & 0xff : data >> 8;

				m_crtc_ptr++;

				m_crtc->dack_w(hp64k_crtc_filter(data));
		}
}

void hp64k_state::hp64k_crtc_vrtc_w(int state)
{
		bool vrtc = state != 0;

		if (!m_vrtc && vrtc) {
				m_crtc_ptr = 0xf9f0 << 1;
		}
		m_vrtc = vrtc;
}

I8275_DRAW_CHARACTER_MEMBER(hp64k_state::crtc_display_pixels)
{
		rgb_t const *const palette = m_palette->palette()->entry_list_raw();
		uint8_t chargen_byte = m_chargen[ linecount  | ((unsigned)charcode << 4) ];
		uint16_t pixels_lvid , pixels_livid;

		using namespace i8275_attributes;
		if (BIT(attrcode , VSP)) {
				pixels_lvid = pixels_livid = ~0;
		} else if (BIT(attrcode , LTEN)) {
				pixels_livid = ~0;
				if (BIT(attrcode , RVV)) {
						pixels_lvid = ~0;
				} else {
						pixels_lvid = 0;
				}
		} else if (BIT(attrcode , RVV)) {
				pixels_lvid = ~0;
				pixels_livid = (uint16_t)chargen_byte << 1;
		} else {
				pixels_lvid = ~((uint16_t)chargen_byte << 1);
				pixels_livid = ~0;
		}

		for (unsigned i = 0; i < 9; i++) {
				bool const lvid = (pixels_lvid & (1U << (8 - i))) != 0;
				bool const livid = (pixels_livid & (1U << (8 - i))) != 0;

				if (!lvid) {
						// Normal brightness
						bitmap.pix(y , x + i) = palette[ 2 ];
				} else if (livid) {
						// Black
						bitmap.pix(y , x + i) = palette[ 0 ];
				} else {
						// Half brightness
						bitmap.pix(y , x + i) = palette[ 1 ];
				}
		}

}

uint16_t hp64k_state::hp64k_rear_sw_r()
{
		return m_rear_panel_sw->read() | 0x0020;
}

uint8_t hp64k_state::int_cb(offs_t offset)
{
		if (offset == 0) {
				return (m_irl_mask & m_irl_pending);
		} else {
				return 0xff;
		}
}

void hp64k_state::hp64k_update_irl(void)
{
		m_cpu->set_input_line(HPHYBRID_IRL , (m_irl_mask & m_irl_pending) != 0);
}

void hp64k_state::hp64k_irl_mask_w(uint16_t data)
{
		m_irl_mask = (uint8_t)data;
		hp64k_update_irl();
}

TIMER_DEVICE_CALLBACK_MEMBER(hp64k_state::hp64k_kb_scan)
{
		if (m_kb_scan_on) {
				unsigned i;

				ioport_value input[ 4 ];
				input[ 0 ] = m_io_key[ 0 ]->read();
				input[ 1 ] = m_io_key[ 1 ]->read();
				input[ 2 ] = m_io_key[ 2 ]->read();
				input[ 3 ] = m_io_key[ 3 ]->read();

				for (i = 0; i < 128; i++) {
						if (++m_kb_row_col >= 128) {
								m_kb_row_col = 0;
						}

						ioport_value mask = BIT_MASK(m_kb_row_col & 0x1f);
						unsigned idx = m_kb_row_col >> 5;

						if ((input[ idx ] ^ m_kb_state[ idx ]) & mask) {
								// key changed state
								m_kb_state[ idx ] ^= mask;
								m_kb_pressed = (m_kb_state[ idx ] & mask) != 0;
								m_kb_scan_on = false;
								BIT_SET(m_irl_pending , 0);
								hp64k_update_irl();
								break;
						}
				}
		}
}

uint16_t hp64k_state::hp64k_kb_r()
{
		uint16_t ret = 0xff00 | m_kb_row_col;

		if (m_kb_pressed) {
				BIT_SET(ret , 7);
		}

		m_kb_scan_on = true;
		BIT_CLR(m_irl_pending , 0);
		hp64k_update_irl();

		return ret;
}

TIMER_DEVICE_CALLBACK_MEMBER(hp64k_state::hp64k_line_sync)
{
		BIT_SET(m_irl_pending , 2);
		hp64k_update_irl();
}

uint16_t hp64k_state::hp64k_deltat_r()
{
		BIT_CLR(m_irl_pending , 2);
		hp64k_update_irl();
		return 0;
}

void hp64k_state::hp64k_deltat_w(uint16_t data)
{
		BIT_CLR(m_irl_pending , 2);
		hp64k_update_irl();
}

uint16_t hp64k_state::hp64k_slot_r(offs_t offset)
{
		if (m_slot_select == 0x0a) {
				// Slot 10 selected
				// On this (fictional) slot is allocated the lower 32KW of RAM

				switch (m_slot_map) {
				case 0:
						// IDEN
						// ID of 32KW RAM expansion
						return 0x402;

				case 1:
						// MAP1
						// Lower half of RAM
						return m_low32k_ram[ offset ];

				default:
						// MAP2&3
						// Upper half of RAM
						return m_low32k_ram[ offset + 0x4000 ];
				}
		} else {
				return 0;
		}
}

void hp64k_state::hp64k_slot_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
		if (m_slot_select == 0x0a && m_slot_map != 0) {
				if (m_slot_map != 1) {
						// MAP2&3
						offset += 0x4000;
				}
				m_low32k_ram[ offset ] &= ~mem_mask;
				m_low32k_ram[ offset ] |= (data & mem_mask);
		}
}

void hp64k_state::hp64k_slot_sel_w(offs_t offset, uint16_t data)
{
		m_slot_map = (uint8_t)offset;
		m_slot_select = (uint8_t)((data >> 8) & 0x3f);
}

uint16_t hp64k_state::hp64k_flp_r(offs_t offset)
{
		m_cpu->dmar_w(0);

		switch (offset) {
		case 0:
				// DMA transfer, not at TC
				if (m_floppy_if_state == HP64K_FLPST_DMARD2) {
						m_floppy_if_state = HP64K_FLPST_IDLE;
				} else {
						logerror("Read from IC=0 with floppy state %d\n" , m_floppy_if_state);
				}
				break;

		case 1:
				if (m_floppy_if_state != HP64K_FLPST_IDLE) {
						logerror("read from IC=1 with floppy state %d\n" , m_floppy_if_state);
				}
				break;

		case 2:
				// DMA transfer, at TC
				if (m_floppy_if_state == HP64K_FLPST_DMARD2) {
						m_floppy_if_state = HP64K_FLPST_IDLE;
						m_floppy_dmaen = false;
						m_floppy_dmai = true;
				} else {
						logerror("Read from IC=2 with floppy state %d\n" , m_floppy_if_state);
				}
				break;

		default:
				logerror("read from IC=%d\n" , offset);
		}

		hp64k_update_floppy_irq();

		return ((uint16_t)m_floppy_out_latch_msb << 8) | (uint16_t)m_floppy_out_latch_lsb;
}

void hp64k_state::hp64k_flp_w(offs_t offset, uint16_t data)
{
		m_cpu->dmar_w(0);

		if (offset == 3) {
				return;
		}

		m_floppy_in_latch_msb = (uint8_t)(data >> 8);
		m_floppy_in_latch_lsb = (uint8_t)data;

		switch (offset) {
		case 0:
				// DMA transfer, not at TC
				if (m_floppy_if_state == HP64K_FLPST_DMAWR1) {
						m_fdc->data_w(~m_floppy_in_latch_msb);
						m_floppy_if_state = HP64K_FLPST_DMAWR2;
				} else {
						logerror("write to IC=0 with floppy state %d\n" , m_floppy_if_state);
				}
				break;

		case 1:
				if (m_floppy_if_state != HP64K_FLPST_IDLE) {
						logerror("write to IC=1 with floppy state %d\n" , m_floppy_if_state);
				}
				// I/F control register
				m_floppy_if_ctrl = m_floppy_in_latch_msb;
				if (BIT(m_floppy_if_ctrl , 4)) {
						// FDC reset
						m_fdc->soft_reset();
				}
				if (BIT(m_floppy_if_ctrl , 7)) {
						// Interrupt reset
						m_floppy_dmai = false;
						m_floppy_mdci = false;
				}
				if (BIT(m_floppy_if_ctrl , 3)) {
						// Write (to either FDC or drive control)
						if (BIT(m_floppy_if_ctrl , 2)) {
								// FDC
								m_fdc->write(~m_floppy_if_ctrl & 3 , ~m_floppy_in_latch_lsb);
						} else {
								// Drive control
								m_floppy_drv_ctrl = m_floppy_in_latch_lsb;
								hp64k_update_drv_ctrl();
						}
				} else {
						// Read
						if (BIT(m_floppy_if_ctrl , 2)) {
								// FDC
								m_floppy_out_latch_lsb = ~m_fdc->read(~m_floppy_if_ctrl & 3);
						} else {
								// Drive control
								m_floppy_out_latch_lsb = m_floppy_drv_ctrl;
						}
				}
				// MSB of output latch is always filled with status register
				m_floppy_out_latch_msb = m_floppy_status;
				m_floppy_dmaen = BIT(m_floppy_if_ctrl , 5) != 0;
				hp64k_update_floppy_dma();
				break;

		case 2:
				// DMA transfer, at TC
				if (m_floppy_if_state == HP64K_FLPST_DMAWR1) {
						m_fdc->data_w(~m_floppy_in_latch_msb);
						m_floppy_if_state = HP64K_FLPST_DMAWR2;
						m_floppy_dmaen = false;
						m_floppy_dmai = true;
				} else {
						logerror("write to IC=2 with floppy state %d\n" , m_floppy_if_state);
				}
				break;
		}

		hp64k_update_floppy_irq();
}

void hp64k_state::hp64k_flp_drq_w(int state)
{
		m_floppy_drq = state;
		hp64k_update_floppy_dma();
}

void hp64k_state::hp64k_flp_intrq_w(int state)
{
		if (state && !m_floppy_intrq && !BIT(m_floppy_if_ctrl , 7)) {
				m_floppy_mdci = true;
				hp64k_update_floppy_irq();
		}
		m_floppy_intrq = state;
}

void hp64k_state::hp64k_update_floppy_dma(void)
{
		if (m_floppy_drq && (m_floppy_dmaen || m_floppy_if_state != HP64K_FLPST_IDLE)) {
				switch (m_floppy_if_state) {
				case HP64K_FLPST_IDLE:
						if (BIT(m_floppy_if_ctrl , 6)) {
								// DMA writes
								m_cpu->dmar_w(1);
								m_floppy_if_state = HP64K_FLPST_DMAWR1;
						} else {
								// DMA reads
								m_floppy_out_latch_msb = ~m_fdc->data_r();
								m_floppy_if_state = HP64K_FLPST_DMARD1;
						}
						break;

				case HP64K_FLPST_DMAWR2:
						m_fdc->data_w(~m_floppy_in_latch_lsb);
						m_floppy_if_state = HP64K_FLPST_IDLE;
						break;

				case HP64K_FLPST_DMARD1:
						m_floppy_out_latch_lsb = ~m_fdc->data_r();
						m_cpu->dmar_w(1);
						m_floppy_if_state = HP64K_FLPST_DMARD2;
						break;

				default:
						logerror("DRQ with floppy state %d\n" , m_floppy_if_state);
				}
		}
}

void hp64k_state::hp64k_update_floppy_irq(void)
{
		if (m_floppy_dmai) {
				BIT_SET(m_floppy_status , 6);
		} else {
				BIT_CLR(m_floppy_status , 6);
		}
		if (m_floppy_mdci) {
				BIT_SET(m_floppy_status , 7);
		} else {
				BIT_CLR(m_floppy_status , 7);
		}

		bool ir4 = m_floppy_dmai || m_floppy_mdci ||
				(BIT(m_floppy_status , 2) && !BIT(m_floppy_drv_ctrl , 0)) ||
				(BIT(m_floppy_status , 5) && !BIT(m_floppy_drv_ctrl , 3));

		if (ir4) {
				BIT_SET(m_irl_pending , 4);
		} else {
				BIT_CLR(m_irl_pending , 4);
		}

		hp64k_update_irl();
}

void hp64k_state::hp64k_update_drv_ctrl(void)
{
		floppy_image_device *floppy0 = m_floppy[0]->get_device();
		floppy_image_device *floppy1 = m_floppy[1]->get_device();

		floppy0->mon_w(BIT(m_floppy_drv_ctrl , 1));
		floppy1->mon_w(BIT(m_floppy_drv_ctrl , 4));
		floppy0->ss_w(!BIT(m_floppy_drv_ctrl , 7));
		floppy1->ss_w(!BIT(m_floppy_drv_ctrl , 7));

		if (BIT(m_floppy_drv_ctrl , 2)) {
				BIT_CLR(m_floppy_status , 2);
		}
		if (BIT(m_floppy_drv_ctrl , 5)) {
				BIT_CLR(m_floppy_status , 5);
		}
		hp64k_update_floppy_irq();

		// Drive selection logic:
		// m_floppy_drv_ctrl
		// Bit 3 0 - Drive selected
		// ========================
		//     0 0 - Invalid:both drives selected. Signals to/from drive 1 are routed to FDC anyway.
		//     0 1 - Drive 1
		//     1 0 - Drive 0
		//     1 1 - None
		floppy_image_device *new_drive;

		if (!BIT(m_floppy_drv_ctrl , 3)) {
				new_drive = m_floppy[1]->get_device();
		} else if (!BIT(m_floppy_drv_ctrl , 0)) {
				new_drive = m_floppy[0]->get_device();
		} else {
				new_drive = nullptr;
		}

		if (new_drive != m_current_floppy) {
				m_fdc->set_floppy(new_drive);

				floppy0->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&hp64k_state::hp64k_floppy_idx_cb, this));
				floppy1->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&hp64k_state::hp64k_floppy_idx_cb, this));

				floppy0->setup_wpt_cb(floppy_image_device::wpt_cb(&hp64k_state::hp64k_floppy_wpt_cb, this));
				floppy1->setup_wpt_cb(floppy_image_device::wpt_cb(&hp64k_state::hp64k_floppy_wpt_cb, this));

				m_current_floppy = new_drive;
		}
}

void hp64k_state::hp64k_floppy0_rdy(int state)
{
		if (state) {
				BIT_CLR(m_floppy_status , 0);
		} else {
				BIT_SET(m_floppy_status , 0);
		}
}

void hp64k_state::hp64k_floppy1_rdy(int state)
{
		if (state) {
				BIT_CLR(m_floppy_status , 3);
		} else {
				BIT_SET(m_floppy_status , 3);
		}
}

void hp64k_state::hp64k_floppy_idx_cb(floppy_image_device *floppy , int state)
{
		if (floppy == m_floppy[0]->get_device()) {
				m_ss[0]->a_w(!state);
		} else if (floppy == m_floppy[1]->get_device()) {
				m_ss[1]->a_w(!state);
		}

		if (floppy == m_current_floppy) {
				m_fdc->index_callback(floppy , state);
		}
}

void hp64k_state::hp64k_floppy_wpt_cb(floppy_image_device *floppy , int state)
{
		if (floppy == m_floppy[0]->get_device()) {
				logerror("floppy0_wpt %d\n" , state);
				if (m_floppy_wpt[0] && !state) {
						BIT_SET(m_floppy_status , 2);
						hp64k_update_floppy_irq();
				}
				if (state) {
						BIT_SET(m_floppy_status, 1);
				} else {
						BIT_CLR(m_floppy_status, 1);
				}
				m_floppy_wpt[0] = state;
		} else if (floppy == m_floppy[1]->get_device()) {
				logerror("floppy1_wpt %d\n" , state);
				if (m_floppy_wpt[1] && !state) {
						BIT_SET(m_floppy_status , 5);
						hp64k_update_floppy_irq();
				}
				if (state) {
						BIT_SET(m_floppy_status, 4);
				} else {
						BIT_CLR(m_floppy_status, 4);
				}
				m_floppy_wpt[1] = state;
		}
}

uint16_t hp64k_state::hp64k_usart_r(offs_t offset)
{
		uint16_t tmp = m_uart->read(~offset & 1);

		// bit 8 == bit 7 rear panel switches (modem/terminal) ???

		tmp |= (m_rs232_sw->read() << 8);

		if (BIT(m_rear_panel_sw->read() , 7)) {
				BIT_SET(tmp , 8);
		}

		return tmp;
}

void hp64k_state::hp64k_usart_w(offs_t offset, uint16_t data)
{
		m_uart->write(~offset & 1, data & 0xff);
}

void hp64k_state::hp64k_rxrdy_w(int state)
{
		if (state) {
				BIT_SET(m_irl_pending , 6);
		} else {
				BIT_CLR(m_irl_pending , 6);
		}

		hp64k_update_irl();
}

void hp64k_state::hp64k_txrdy_w(int state)
{
		if (state) {
				BIT_SET(m_irl_pending , 5);
		} else {
				BIT_CLR(m_irl_pending , 5);
		}

		hp64k_update_irl();
}

void hp64k_state::hp64k_txd_w(int state)
{
		m_txd_state = state;
		if (m_loopback) {
				m_uart->write_rxd(state);
		}
		m_rs232->write_txd(state);
}

void hp64k_state::hp64k_dtr_w(int state)
{
		m_dtr_state = state;
		if (m_loopback) {
				m_uart->write_dsr(state);
		}
		m_rs232->write_dtr(state);
}

void hp64k_state::hp64k_rts_w(int state)
{
	if (BIT(m_s5_sw->read() , 0)) {
		// Full duplex, RTS/ = 0
		state = 0;
	}
	m_rts_state = state;
	if (m_loopback) {
		m_uart->write_cts(state);
	}
	m_rs232->write_rts(state);
}

void hp64k_state::hp64k_loopback_w(uint16_t data)
{
	m_phi_reg = (uint8_t)((data >> 8) & 7);
	m_loopback = BIT(data , 11);
	hp64k_update_loopback();
}

void hp64k_state::hp64k_update_loopback(void)
{
	if (m_loopback) {
		m_uart->write_rxd(m_txd_state);
		m_uart->write_dsr(m_dtr_state);
		m_uart->write_cts(m_rts_state);
	} else {
		m_uart->write_rxd(m_rs232->rxd_r());
		m_uart->write_dsr(m_rs232->dcd_r());
		m_uart->write_cts(m_rs232->cts_r());
	}
}

void hp64k_state::hp64k_rs232_rxd_w(int state)
{
	if (!m_loopback) {
		m_uart->write_rxd(state);
	}
}

void hp64k_state::hp64k_rs232_dcd_w(int state)
{
	if (!m_loopback) {
		m_uart->write_dsr(state);
	}
}

uint16_t hp64k_state::hp64k_phi_r(offs_t offset)
{
	return m_phi->reg16_r(m_phi_reg);
}

void hp64k_state::hp64k_phi_w(offs_t offset, uint16_t data)
{
	m_phi->reg16_w(m_phi_reg , data);
}

void hp64k_state::hp64k_rs232_cts_w(int state)
{
	if (!m_loopback) {
		m_uart->write_cts(state);
	}
}

void hp64k_state::hp64k_phi_int_w(int state)
{
	if (state) {
		BIT_SET(m_irl_pending , 7);
	} else {
		BIT_CLR(m_irl_pending , 7);
	}

	hp64k_update_irl();
}

int hp64k_state::hp64k_phi_sys_ctrl_r()
{
	return BIT(m_rear_panel_sw->read() , 6);
}

void hp64k_state::hp64k_beep_w(offs_t offset, uint16_t data)
{
	if (!BIT(offset , 0)) {
		m_beeper->set_state(1);
		// Duration is bogus: in the real hw envelope decays exponentially with RC=~136 ms
		m_beep_timer->adjust(attotime::from_msec(130));
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(hp64k_state::hp64k_beeper_off)
{
	m_beeper->set_state(0);
}

void hp64k_state::hp64k_baud_clk_w(int state)
{
	if (!m_16x_clk) {
		if (state && !m_baud_clk) {
			m_16x_div++;
		}
		m_baud_clk = !!state;
		state = BIT(m_16x_div , 3);
	}
	m_uart->write_txc(state);
	m_uart->write_rxc(state);
}

static INPUT_PORTS_START(hp64k)
	// Keyboard is arranged in a 8 x 16 matrix. Of the 128 possible positions, only 77 are used.
	// For key arrangement on the matrix, see [1] pg 334
	// Keys are mapped on bit b of KEYn
	// where b = (row & 1) << 4 + column, n = row >> 1
	// column = [0..15]
	// row = [0..7]
	PORT_START("KEY0")
	PORT_BIT(BIT_MASK(0)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)  PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(BIT_MASK(1)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_A)     PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(BIT_MASK(2)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_W)     PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(BIT_MASK(3)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_E)     PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(BIT_MASK(4)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_R)     PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(BIT_MASK(5)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_T)     PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(BIT_MASK(6)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)     PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(BIT_MASK(7)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_U)     PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(BIT_MASK(8)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_I)     PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(BIT_MASK(9)  , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(10) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(11) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(12) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(13) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(14) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(15) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)       PORT_CHAR('\t')
	PORT_BIT(BIT_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)     PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(BIT_MASK(18) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(19) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(20) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(21) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_7)     PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(BIT_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_8)     PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(BIT_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_9)     PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(BIT_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_0)     PORT_CHAR('0')
	PORT_BIT(BIT_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)     PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(BIT_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)    PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT(BIT_MASK(28) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)     PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(BIT_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(BIT_MASK(30) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(31) , IP_ACTIVE_HIGH , IPT_UNUSED)

	PORT_START("KEY1")
	PORT_BIT(BIT_MASK(0)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_1)     PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(BIT_MASK(1)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_2)     PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(BIT_MASK(2)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_3)     PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(BIT_MASK(3)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_4)     PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(BIT_MASK(4)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_5)     PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(BIT_MASK(5)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_6)     PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(BIT_MASK(6)  , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(7)  , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(8)  , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(9)  , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(10) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(11) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F9)        PORT_NAME("RECALL")
	PORT_BIT(BIT_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F10)       PORT_NAME("CLRLINE")
	PORT_BIT(BIT_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F11)       PORT_NAME("CAPS")
	PORT_BIT(BIT_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F12)       PORT_NAME("RESET")
	PORT_BIT(BIT_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)        PORT_NAME("SK1")
	PORT_BIT(BIT_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)        PORT_NAME("SK2")
	PORT_BIT(BIT_MASK(18) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)        PORT_NAME("SK3")
	PORT_BIT(BIT_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)        PORT_NAME("SK4")
	PORT_BIT(BIT_MASK(21) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(22) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)        PORT_NAME("SK5")
	PORT_BIT(BIT_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)        PORT_NAME("SK6")
	PORT_BIT(BIT_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)        PORT_NAME("SK7")
	PORT_BIT(BIT_MASK(26) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)        PORT_NAME("SK8")
	PORT_BIT(BIT_MASK(28) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(29) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(30) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(31) , IP_ACTIVE_HIGH , IPT_UNUSED)

	PORT_START("KEY2")
	PORT_BIT(BIT_MASK(0)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)    PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(BIT_MASK(1)  , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(2)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_S)     PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(BIT_MASK(3)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_D)     PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(BIT_MASK(4)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F)     PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(BIT_MASK(5)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_G)     PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(BIT_MASK(6)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_H)     PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(BIT_MASK(7)  , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(8)  , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(9)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_O)     PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(BIT_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_P)     PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(BIT_MASK(11) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(12) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(13) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT)    PORT_NAME("INSCHAR")
	PORT_BIT(BIT_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)       PORT_NAME("DELCHAR")
	PORT_BIT(BIT_MASK(16) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(17) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)     PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(BIT_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_X)     PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(BIT_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_C)     PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(BIT_MASK(21) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(22) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_J)     PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(BIT_MASK(24) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(25) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(BIT_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)    PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(BIT_MASK(28) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2)    PORT_CHAR('_') PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(BIT_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME)      PORT_NAME("ROLLUP")
	PORT_BIT(BIT_MASK(30) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)        PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(BIT_MASK(31) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN)      PORT_NAME("NEXTPG")

	PORT_START("KEY3")
	PORT_BIT(BIT_MASK(0)  , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(1)  , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(2)  , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(3)  , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(4)  , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(5)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_V)     PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(BIT_MASK(6)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_B)     PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(BIT_MASK(7)  , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(8)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_K)     PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(BIT_MASK(9)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_L)     PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(BIT_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)     PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(BIT_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)     PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(BIT_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(BIT_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)     PORT_CHAR(13)
	PORT_BIT(BIT_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)          PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(BIT_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)         PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(BIT_MASK(16) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(17) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(18) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(19) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(20) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(21) , IP_ACTIVE_HIGH , IPT_UNUSED)
	PORT_BIT(BIT_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)         PORT_CHAR(' ')
	PORT_BIT(BIT_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_N)             PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(BIT_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_M)             PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(BIT_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)         PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(BIT_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)          PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(BIT_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)         PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(BIT_MASK(28) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT)        PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(BIT_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_END)           PORT_NAME("ROLLDN")
	PORT_BIT(BIT_MASK(30) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)          PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(BIT_MASK(31) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP)          PORT_NAME("PREVPG")

	PORT_START("rear_sw")
	PORT_DIPNAME(0x8000 , 0x8000 , "E9-6 jumper")
	PORT_DIPSETTING(0x0000 , DEF_STR(Yes))
	PORT_DIPSETTING(0x8000 , DEF_STR(No))
	PORT_DIPNAME(0x4000 , 0x4000 , "E9-5 jumper")
	PORT_DIPSETTING(0x0000 , DEF_STR(Yes))
	PORT_DIPSETTING(0x4000 , DEF_STR(No))
	PORT_DIPNAME(0x2000 , 0x2000 , "E9-4 jumper")
	PORT_DIPSETTING(0x0000 , DEF_STR(Yes))
	PORT_DIPSETTING(0x2000 , DEF_STR(No))
	PORT_DIPNAME(0x1000 , 0x1000 , "E9-3 jumper")
	PORT_DIPSETTING(0x0000 , DEF_STR(Yes))
	PORT_DIPSETTING(0x1000 , DEF_STR(No))
	PORT_DIPNAME(0x0800 , 0x0800 , "E9-2 jumper")
	PORT_DIPSETTING(0x0000 , DEF_STR(Yes))
	PORT_DIPSETTING(0x0800 , DEF_STR(No))
	PORT_DIPNAME(0x0400 , 0x0400 , "E9-1 jumper")
	PORT_DIPSETTING(0x0000 , DEF_STR(Yes))
	PORT_DIPSETTING(0x0400 , DEF_STR(No))
	PORT_DIPNAME(0x0040 , 0x0000 , "System controller")
	PORT_DIPSETTING(0x0000 , DEF_STR(No))
	PORT_DIPSETTING(0x0040 , DEF_STR(Yes))
	PORT_DIPNAME(0x0018 , 0x0000 , "System source")
	PORT_DIPLOCATION("S1:!7,!6")
	PORT_DIPSETTING(0x0000 , "Sys bus")
	PORT_DIPSETTING(0x0008 , "Local storage-talk only")
	PORT_DIPSETTING(0x0010 , "Local storage-addressable")
	PORT_DIPSETTING(0x0018 , "Performance verification")
	PORT_DIPNAME(0x0300 , 0x0000 , "Upper bus address (N/U)")
	PORT_DIPLOCATION("S1:!2,!1")
	PORT_DIPSETTING(0x0000 , "0")
	PORT_DIPSETTING(0x0100 , "1")
	PORT_DIPSETTING(0x0200 , "2")
	PORT_DIPSETTING(0x0300 , "3")
	PORT_DIPNAME(0x0007 , 0x0000 , "System bus address")
	PORT_DIPLOCATION("S1:!5,!4,!3")
	PORT_DIPSETTING(0x0000 , "0")
	PORT_DIPSETTING(0x0001 , "1")
	PORT_DIPSETTING(0x0002 , "2")
	PORT_DIPSETTING(0x0003 , "3")
	PORT_DIPSETTING(0x0004 , "4")
	PORT_DIPSETTING(0x0005 , "5")
	PORT_DIPSETTING(0x0006 , "6")
	PORT_DIPSETTING(0x0007 , "7")
	PORT_DIPNAME(0x0080 , 0x0000 , "RS232 mode")
	PORT_DIPLOCATION("S4 IO:!8")
	PORT_DIPSETTING(0x0000 , "Terminal")
	PORT_DIPSETTING(0x0080 , "Modem")

	PORT_START("rs232_sw")
	PORT_DIPNAME(0xc0 , 0x00 , "Stop bits")
	PORT_DIPLOCATION("S4 IO:!2,!1")
	PORT_DIPSETTING(0x00 , "Invalid")
	PORT_DIPSETTING(0x40 , "1")
	PORT_DIPSETTING(0x80 , "1.5")
	PORT_DIPSETTING(0xc0 , "2")
	PORT_DIPNAME(0x20 , 0x00 , "Parity")
	PORT_DIPLOCATION("S4 IO:!3")
	PORT_DIPSETTING(0x00 , "Odd")
	PORT_DIPSETTING(0x20 , "Even")
	PORT_DIPNAME(0x10 , 0x00 , "Parity enable")
	PORT_DIPLOCATION("S4 IO:!4")
	PORT_DIPSETTING(0x00 , DEF_STR(No))
	PORT_DIPSETTING(0x10 , DEF_STR(Yes))
	PORT_DIPNAME(0x0c , 0x00 , "Char length")
	PORT_DIPLOCATION("S4 IO:!6,!5")
	PORT_DIPSETTING(0x00 , "5")
	PORT_DIPSETTING(0x04 , "6")
	PORT_DIPSETTING(0x08 , "7")
	PORT_DIPSETTING(0x0c , "8")
	PORT_DIPNAME(0x02 , 0x00 , "Baud rate factor")
	PORT_DIPLOCATION("S4 IO:!7")
	PORT_DIPSETTING(0x00 , "1x")
	PORT_DIPSETTING(0x02 , "16x")

	PORT_START("s5_sw")
	PORT_DIPNAME(0x01 , 0x00 , "Duplex")
	PORT_DIPLOCATION("S5 IO:!1")
	PORT_DIPSETTING(0x00 , "Half duplex")
	PORT_DIPSETTING(0x01 , "Full duplex")
	PORT_DIPNAME(0x1e , 0x00 , "Baud rate")
	PORT_DIPLOCATION("S5 IO:!5,!4,!3,!2")
	PORT_DIPSETTING(0x00 , "50")
	PORT_DIPSETTING(0x02 , "75")
	PORT_DIPSETTING(0x04 , "110")
	PORT_DIPSETTING(0x06 , "134.5")
	PORT_DIPSETTING(0x08 , "150")
	PORT_DIPSETTING(0x0a , "300")
	PORT_DIPSETTING(0x0c , "600")
	PORT_DIPSETTING(0x0e , "1200")
	PORT_DIPSETTING(0x10 , "1800")
	PORT_DIPSETTING(0x12 , "2000")
	PORT_DIPSETTING(0x14 , "2400")
	PORT_DIPSETTING(0x16 , "3600")
	PORT_DIPSETTING(0x18 , "4800")
	PORT_DIPSETTING(0x1a , "7200")
	PORT_DIPSETTING(0x1c , "9600")
	PORT_DIPSETTING(0x1e , "19200")
INPUT_PORTS_END

static void hp64k_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

void hp64k_state::hp64k(machine_config &config)
{
	HP_5061_3011(config, m_cpu, 6250000);
	m_cpu->set_rw_cycles(6 , 6);
	m_cpu->set_relative_mode(true);
	m_cpu->set_addrmap(AS_PROGRAM, &hp64k_state::cpu_mem_map);
	m_cpu->set_addrmap(AS_IO, &hp64k_state::cpu_io_map);
	m_cpu->set_int_cb(FUNC(hp64k_state::int_cb));

	// Actual keyboard refresh rate should be between 1 and 2 kHz
	TIMER(config, "kb_timer").configure_periodic(FUNC(hp64k_state::hp64k_kb_scan), attotime::from_hz(100));

	// Line sync timer. A line frequency of 50 Hz is assumed.
	TIMER(config, "linesync_timer").configure_periodic(FUNC(hp64k_state::hp64k_line_sync), attotime::from_hz(50));

	// Clock = 25 MHz / 9 * (112/114)
	I8275(config, m_crtc, 2729045);
	m_crtc->set_screen("screen");
	m_crtc->set_character_width(9);
	m_crtc->set_display_callback(FUNC(hp64k_state::crtc_display_pixels));
	m_crtc->drq_wr_callback().set(FUNC(hp64k_state::hp64k_crtc_drq_w));
	m_crtc->vrtc_wr_callback().set(FUNC(hp64k_state::hp64k_crtc_vrtc_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_screen_update("crtc", FUNC(i8275_device::screen_update));
	screen.set_refresh_hz(60);
	screen.set_size(720, 390);
	screen.set_visarea(0, 720-1, 0, 390-1);
	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	FD1791(config, m_fdc, 4_MHz_XTAL / 4);
	m_fdc->set_force_ready(true); // should be able to get rid of this when fdc issue is fixed
	m_fdc->intrq_wr_callback().set(FUNC(hp64k_state::hp64k_flp_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(hp64k_state::hp64k_flp_drq_w));
	FLOPPY_CONNECTOR(config, "fdc:0", hp64k_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats, true);
	FLOPPY_CONNECTOR(config, "fdc:1", hp64k_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats, true);

	TTL74123(config, m_ss[0], 0);
	m_ss[0]->set_connection_type(TTL74123_NOT_GROUNDED_NO_DIODE);
	m_ss[0]->set_resistor_value(RES_K(68.1));
	// Warning! Duration formula is not correct for LS123, actual capacitor is 10 uF
	m_ss[0]->set_capacitor_value(CAP_U(16));
	m_ss[0]->set_b_pin_value(1);
	m_ss[0]->set_clear_pin_value(1);
	m_ss[0]->out_cb().set(FUNC(hp64k_state::hp64k_floppy0_rdy));

	TTL74123(config, m_ss[1], 0);
	m_ss[1]->set_connection_type(TTL74123_NOT_GROUNDED_NO_DIODE);
	m_ss[1]->set_resistor_value(RES_K(68.1));
	m_ss[1]->set_capacitor_value(CAP_U(16));
	m_ss[1]->set_b_pin_value(1);
	m_ss[1]->set_clear_pin_value(1);
	m_ss[1]->out_cb().set(FUNC(hp64k_state::hp64k_floppy1_rdy));

	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 2500).add_route(ALL_OUTPUTS, "mono", 1.00);

	TIMER(config, m_beep_timer).configure_generic(FUNC(hp64k_state::hp64k_beeper_off));

	COM8116(config, m_baud_rate, 5.0688_MHz_XTAL);
	m_baud_rate->fr_handler().set(FUNC(hp64k_state::hp64k_baud_clk_w));

	I8251(config, m_uart, 0);
	m_uart->rxrdy_handler().set(FUNC(hp64k_state::hp64k_rxrdy_w));
	m_uart->txrdy_handler().set(FUNC(hp64k_state::hp64k_txrdy_w));
	m_uart->txd_handler().set(FUNC(hp64k_state::hp64k_txd_w));
	m_uart->dtr_handler().set(FUNC(hp64k_state::hp64k_dtr_w));
	m_uart->rts_handler().set(FUNC(hp64k_state::hp64k_rts_w));

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set(FUNC(hp64k_state::hp64k_rs232_rxd_w));
	m_rs232->dcd_handler().set(FUNC(hp64k_state::hp64k_rs232_dcd_w));
	m_rs232->cts_handler().set(FUNC(hp64k_state::hp64k_rs232_cts_w));

	PHI(config, m_phi, 0);
	m_phi->int_write_cb().set(FUNC(hp64k_state::hp64k_phi_int_w));
	m_phi->dmarq_write_cb().set(m_cpu, FUNC(hp_5061_3011_cpu_device::halt_w));
	m_phi->sys_cntrl_read_cb().set(FUNC(hp64k_state::hp64k_phi_sys_ctrl_r));
	m_phi->dio_read_cb().set(IEEE488_TAG, FUNC(ieee488_device::dio_r));
	m_phi->dio_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_dio_w));
	m_phi->eoi_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_eoi_w));
	m_phi->dav_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_dav_w));
	m_phi->nrfd_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_nrfd_w));
	m_phi->ndac_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ndac_w));
	m_phi->ifc_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ifc_w));
	m_phi->srq_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_srq_w));
	m_phi->atn_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_atn_w));
	m_phi->ren_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ren_w));

	ieee488_device &ieee(IEEE488(config, IEEE488_TAG));
	ieee.eoi_callback().set(m_phi, FUNC(phi_device::eoi_w));
	ieee.dav_callback().set(m_phi, FUNC(phi_device::dav_w));
	ieee.nrfd_callback().set(m_phi, FUNC(phi_device::nrfd_w));
	ieee.ndac_callback().set(m_phi, FUNC(phi_device::ndac_w));
	ieee.ifc_callback().set(m_phi, FUNC(phi_device::ifc_w));
	ieee.srq_callback().set(m_phi, FUNC(phi_device::srq_w));
	ieee.atn_callback().set(m_phi, FUNC(phi_device::atn_w));
	ieee.ren_callback().set(m_phi, FUNC(phi_device::ren_w));
	ieee.dio_callback().set(m_phi, FUNC(phi_device::bus_dio_w));
	IEEE488_SLOT(config, "ieee_rem", 0, remote488_devices, nullptr);
}

ROM_START(hp64k)
	ROM_REGION(0x8000, "cpu" , ROMREGION_16BIT | ROMREGION_BE | ROMREGION_INVERT)
	ROM_LOAD16_BYTE("64100_80022.bin" , 0x0000 , 0x1000 , CRC(38b2aae5) SHA1(bfd0f126bfaf3724dc501979ad2d46afc41913aa))
	ROM_LOAD16_BYTE("64100_80020.bin" , 0x0001 , 0x1000 , CRC(ac01b436) SHA1(be1e827ea1393a95abb02a52ab5cc35dc2cd96e4))
	ROM_LOAD16_BYTE("64100_80023.bin" , 0x2000 , 0x1000 , CRC(6b4bc2ce) SHA1(00e6c58ccae9640dc81cb3e92db90a8c69b02a93))
	ROM_LOAD16_BYTE("64100_80021.bin" , 0x2001 , 0x1000 , CRC(74f9d33c) SHA1(543a845a992b0ceac3e0491acdfb178df0adeb1f))
	ROM_LOAD16_BYTE("64100_80026.bin" , 0x4000 , 0x1000 , CRC(a74e834b) SHA1(a2ff9765628985d9bab4cb44ba23257a9b8d0965))
	ROM_LOAD16_BYTE("64100_80024.bin" , 0x4001 , 0x1000 , CRC(2e15a1d2) SHA1(ce4330f8f8015a26c02f0965b95baf7dfd615512))
	ROM_LOAD16_BYTE("64100_80027.bin" , 0x6000 , 0x1000 , CRC(b93c0e7a) SHA1(b239446d3d6e9d3dba6c0278b2771abe1623e1ad))
	ROM_LOAD16_BYTE("64100_80025.bin" , 0x6001 , 0x1000 , CRC(e6353085) SHA1(48d78835c798f2caf6ee539057676d4f3c8a4df9))

	ROM_REGION(0x800 , "chargen" , 0)
	ROM_LOAD("1816_1496_82s191.bin" , 0 , 0x800 , CRC(32a52664) SHA1(8b2a49a32510103ff424e8481d5ed9887f609f2f))
ROM_END

} // anonymous namespace


/*    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME */
COMP( 1979, hp64k, 0,      0,      hp64k,   hp64k, hp64k_state, empty_init, "HP",    "HP 64000" , 0)



hp700.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************************

Skeleton driver for HP-700 series terminals.

************************************************************************************************************************************/

#include "emu.h"
#include "cpu/nec/nec.h"
#include "machine/mc68681.h"
#include "machine/nvram.h"


namespace {

class hp700_state : public driver_device
{
public:
	hp700_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_duart(*this, "duart")
	{ }

	void hp700_92(machine_config &config);
	void hp700_70(machine_config &config);

private:
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	void hp700_70_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<scn2681_device> m_duart;
};

void hp700_state::mem_map(address_map &map)
{
	map(0x00000, 0x1ffff).rom().region("maincpu", 0);
	map(0x20000, 0x2ffff).ram();
	map(0x30000, 0x31fff).ram().share("nvram");
	map(0x34000, 0x34000).nopr();
	map(0x38000, 0x38fff).lrw8(
							   NAME([this] (offs_t offset) {
								   return m_duart->read(offset >> 8);
							   }),
							   NAME([this] (offs_t offset, u8 data) {
								   m_duart->write(offset >> 8, data);
							   }));
	map(0xffff0, 0xfffff).rom().region("maincpu", 0x1fff0);
}

void hp700_state::hp700_70_map(address_map &map)
{
	map(0x00000, 0x3ffff).rom().region("maincpu", 0);
	map(0x60000, 0x6ffff).ram();
	map(0x70000, 0x71fff).ram().share("nvram");
	map(0x780f0, 0x780ff).rw(m_duart, FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0xffff0, 0xfffff).rom().region("maincpu", 0x1fff0);
}

void hp700_state::io_map(address_map &map)
{
	map(0x00f2, 0x00f2).nopw();
}

static INPUT_PORTS_START(hp700_92)
INPUT_PORTS_END

void hp700_state::hp700_92(machine_config &config)
{
	V20(config, m_maincpu, 29.4912_MHz_XTAL / 3); // divider not verified
	m_maincpu->set_addrmap(AS_PROGRAM, &hp700_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &hp700_state::io_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SCN2681(config, m_duart, 29.4912_MHz_XTAL / 8); // divider not verified
	m_duart->irq_cb().set_inputline(m_maincpu, INPUT_LINE_NMI);
}

void hp700_state::hp700_70(machine_config &config)
{
	V20(config, m_maincpu, 39.3216_MHz_XTAL / 4); // divider not verified (XTAL value not readable, assumed to be same as HP 700/60)
	m_maincpu->set_addrmap(AS_PROGRAM, &hp700_state::hp700_70_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SCN2681(config, m_duart, 39.3216_MHz_XTAL / 8); // divider not verified; device type probably wrong (48-pin DIP labeled 1LVS-0001)
	m_duart->irq_cb().set_inputline(m_maincpu, INPUT_LINE_NMI);
}


/**************************************************************************************************************

Hewlett-Packard HP-700/92.
Chips: TC5564APL-15, proprietary square chip, D70108C-10 (V20), SCN2681, Beeper
Crystals: 29.4912

***************************************************************************************************************/

ROM_START(hp700_92)
	ROM_REGION(0x20000, "maincpu", 0)
	ROM_LOAD("5181-8672.u803", 0x00000, 0x20000, CRC(21440d2f) SHA1(69a3de064ae2b18adc46c2fdd0bf69620375efe7))
ROM_END

/**************************************************************************************************************

HP 700/70 Terminal with AlphaWindows capability

Board D-3416 C1093-60001
-------------
2 EPROMs
EPROM Type: M27C1001 (128KB - 1 MBit)
-------------

-------------
1LV5-0001
93491
SINGAPORE
-------------

-------------
NEC
9401Y5 V20
D70108C-10(C)'84 NEC
-------------

-------------
HP ASIC 1MH1-0202
94281 Singapore
HJP02
-------------

***************************************************************************************************************/

ROM_START(hp700_70)
	ROM_REGION(0x40000, "maincpu", 0) // "© HP 1994 REV: 3440"
	ROM_LOAD("c1093-80008.u802", 0x00000, 0x20000, CRC(25c527a6) SHA1(97e82774d25eab6fd4cc6ff7a5a473341281abb1)) // "CKSM 96A5"
	ROM_LOAD("c1093-80009.u817", 0x20000, 0x20000, CRC(369e6855) SHA1(938ac9cd120d0aa7c76011d1a5e91244a142b397)) // "CKSM 7B6B"
ROM_END

} // anonymous namespace


COMP(1987, hp700_92, 0, 0, hp700_92, hp700_92, hp700_state, empty_init, "Hewlett-Packard", "HP 700/92 Display Terminal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP(1994, hp700_70, 0, 0, hp700_70, hp700_92, hp700_state, empty_init, "Hewlett-Packard", "HP 700/70 Windowing Terminal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



hp7596a.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for Hewlett-Packard 7596A drafting plotter.

***************************************************************************/

#include "emu.h"
#include "cpu/m68000/m68000.h"

namespace {

class hp7596a_state : public driver_device
{
public:
	hp7596a_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void hp7596a(machine_config &config);

private:
	void main_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};


void hp7596a_state::main_map(address_map &map)
{
	map(0x000000, 0x01ffff).rom().region("program", 0);
	map(0x37c000, 0x383fff).ram();
}


static INPUT_PORTS_START(hp7596a)
INPUT_PORTS_END

void hp7596a_state::hp7596a(machine_config &config)
{
	M68000(config, m_maincpu, 9'980'000); // SCN68000CAN64
	m_maincpu->set_addrmap(AS_PROGRAM, &hp7596a_state::main_map);

	//EEPROM_2804(config, "eeprom"); // X2804AP-45

	//Z80DART(config, "dart"); // Z8470BPS

	//I8291A(config, "gpibc");
}

ROM_START(hp7596a)
	ROM_REGION16_BE(0x20000, "program", 0)
	ROM_LOAD16_BYTE("07595-18096_u37.bin", 0x00000, 0x10000, CRC(b1df8181) SHA1(d7ef1edf3f4b9f5a7e0fdca0bf6b72a3118da9c2))
	ROM_LOAD16_BYTE("07595-18095_u28.bin", 0x00001, 0x10000, CRC(3588c029) SHA1(cc4b4838531b9309a3fc27102c19393653b08ae4))
ROM_END

} // anonymous namespace


SYST(1988, hp7596a, 0, 0, hp7596a, hp7596a, hp7596a_state, empty_init, "Hewlett-Packard", "HP 7596A DraftMaster II", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



hp80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:F. Ulivi
//
// *******************************
// Driver for HP series 80 systems
// *******************************
//
// This driver currently emulates the HP85A & HP86B machines.
//
// What's in HP85A emulation:
// - Capricorn CPU @613 kHz
// - 32K of system ROMs
// - Optional ROMs
// - 16K of RAM
// - Alpha/graphic video
// - Internal timers
// - DC100 tape drive
// - Integrated thermal printer
// - Beeper & 1-bit bitbanged sound
// - I/O slots
//
// What's in HP86B emulation:
// - Capricorn CPU @613 kHz
// - 56K of system ROMs
// - Optional ROMs
// - 128K of RAM (through Extended Memory Controller)
// - Alpha/graphic video (with correct aspect ratio for 82913A 12" monitor)
// - Run light
// - Internal timers
// - Integrated HPIB interface (which is basically a built-in 82937 module)
// - Beeper & 1-bit bitbanged sound
// - I/O slots
//
// The HP86B was also produced with support for various non-English European languages.
// There were 3 major differences between standard (i.e. English only) and international models:
// - The keyboard controller IC translated from the matrix position to key code in the
//   standard model whereas it only reported the row/column position in the international models.
//   In the latter case the decoding was done in software.
// - The international models had an extra built-in ROM (the "Language" ROM) that handled the
//   decoding of various keyboard layouts and also provided some extra BASIC instructions to
//   deal with non-English text.
// - There were 3 video controllers that displayed different character shapes in the 00..1b range.
//
// This table summarizes the differences between hp86b models.
//
// | Model   | Kb layout        | Kb controller | Has           | Video controller | Emulator  |
// |         |                  | decodes key?  | Language ROM? | version          |           |
// |---------+------------------+---------------+---------------+------------------+-----------|
// | Std     | English          | Yes           | No            | 1st              | hp86b     |
// | Opt 001 | Swedish/Finnish  | No            | Yes           | 2nd              | hp86b_001 |
// | Opt 002 | Danish/Norwegian | No            | Yes           | 2nd              | hp86b_001 |
// | Opt 004 | German           | No            | Yes           | 3rd              | hp86b_004 |
// | Opt 006 | Spanish          | No            | Yes           | 2nd              | hp86b_001 |
// | Opt 008 | French           | No            | Yes           | 3rd              | hp86b_004 |
// | Opt 009 | Italian          | No            | Yes           | 3rd              | hp86b_004 |
// | Opt 010 | Dutch            | No            | Yes           | 3rd              | hp86b_004 |
// | Opt 020 | Swiss German     | No            | Yes           | 3rd              | hp86b_004 |
// | Opt 021 | Swiss French     | No            | Yes           | 3rd              | hp86b_004 |
//
// Special thanks to Everett Kaser for his excellent reverse engineering of Language ROM and for
// all his support.
//
// Thanks to all the people who made docs available & dumped the various ROMs.
//
// References for these systems:
// https://groups.io/g/hpseries80 - Site with tons of info on HP80 systems
// http://www.kaser.com/hp85.html - A Windows-based emulator of HP80 systems
// https://sites.google.com/site/olivier2smet2/hpseries80 - Another Windows-based emulator
// http://www.series80.org/ - *The* reference site for these machines
// http://www.akso.de/index.php?id=hp_series_80&L=-1%27 - Another interesting site
// http://www.hpmuseum.net/exhibit.php?class=1&cat=9 - Last but not least: HP museum pages for HP80

#include "emu.h"
#include "emupal.h"
#include "screen.h"
#include "cpu/capricorn/capricorn.h"
#include "speaker.h"
#include "machine/timer.h"
#include "sound/beep.h"
#include "sound/dac.h"
#include "machine/1ma6.h"
#include "hp80_optrom.h"
#include "machine/ram.h"
#include "softlist_dev.h"
#include "machine/bankdev.h"
#include "bus/hp80_io/hp80_io.h"
#include "bus/hp80_io/82937.h"
#include "imagedev/bitbngr.h"
#include "hp86b.lh"

// Debugging
#include "logmacro.h"
#define LOG_EMC_MASK (LOG_GENERAL << 1)
#define LOG_EMC(...) LOGMASKED(LOG_EMC_MASK, __VA_ARGS__)
#define LOG_IRQ_MASK (LOG_EMC_MASK << 1)
#define LOG_IRQ(...) LOGMASKED(LOG_IRQ_MASK, __VA_ARGS__)
#undef VERBOSE
//#define VERBOSE (LOG_GENERAL|LOG_EMC_MASK|LOG_IRQ_MASK)
#define VERBOSE LOG_GENERAL

// Bit manipulation
namespace {
	template<typename T> constexpr T BIT_MASK(unsigned n)
	{
		return (T)1U << n;
	}

	template<typename T> void BIT_CLR(T& w , unsigned n)
	{
		w &= ~BIT_MASK<T>(n);
	}

	template<typename T> void BIT_SET(T& w , unsigned n)
	{
		w |= BIT_MASK<T>(n);
	}

	template<typename T> void COPY_BIT(bool bit , T& w , unsigned n)
	{
		if (bit) {
			BIT_SET(w , n);
		} else {
			BIT_CLR(w , n);
		}
	}
}


namespace {

// **** Constants ****
static constexpr unsigned CPU_CLOCK = 613000;
// Time taken by hw timer updating (semi-made up) (in µsec)
static constexpr unsigned TIMER_BUSY_USEC   = 128;
static constexpr unsigned IRQ_KEYBOARD_BIT  = 0;
static constexpr unsigned IRQ_INTKEYB_BIT   = 1;
static constexpr unsigned IRQ_TIMER0_BIT    = 2;
static constexpr unsigned TIMER_COUNT       = 4;
static constexpr unsigned IRQ_IOP0_BIT      = IRQ_TIMER0_BIT + TIMER_COUNT;
// Maximum count of I/O processors (the same thing as count of I/O slots)
static constexpr unsigned IOP_COUNT         = 4;
static constexpr unsigned IRQ_BIT_COUNT     = IRQ_IOP0_BIT + IOP_COUNT;
static constexpr unsigned NO_IRQ            = IRQ_BIT_COUNT;

// *****************
//  hp80_base_state
// *****************
class hp80_base_state : public driver_device
{
public:
	hp80_base_state(const machine_config &mconfig, device_type type, const char *tag, bool has_int_keyb = false);

protected:
	void hp80_base(machine_config &config);

	virtual void cpu_mem_map(address_map &map) ATTR_COLD;
	virtual void rombank_mem_map(address_map &map) ATTR_COLD;
	virtual void unmap_optroms(address_space &space);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint8_t intack_r();

	void ginten_w(uint8_t data);
	void gintdis_w(uint8_t data);
	uint8_t keysts_r();
	void keysts_w(uint8_t data);
	uint8_t keycod_r();
	void keycod_w(uint8_t data);
	uint8_t clksts_r();
	void clksts_w(uint8_t data);
	uint8_t clkdat_r();
	void clkdat_w(uint8_t data);
	void rselec_w(uint8_t data);
	uint8_t intrsc_r();
	void intrsc_w(uint8_t data);

	TIMER_DEVICE_CALLBACK_MEMBER(kb_scan);
	TIMER_DEVICE_CALLBACK_MEMBER(timer_update);
	TIMER_DEVICE_CALLBACK_MEMBER(clk_busy_timer);

	void irl_w(offs_t offset, uint8_t data);
	void halt_w(offs_t offset, uint8_t data);

	required_device<capricorn_cpu_device> m_cpu;
	required_device<timer_device> m_clk_busy_timer;
	required_device<beep_device> m_beep;
	required_device<dac_1bit_device> m_dac;
	required_ioport m_io_key0;
	required_ioport m_io_key1;
	required_ioport m_io_key2;
	required_ioport m_io_modkeys;
	optional_ioport m_io_language;
	required_device_array<hp80_optrom_device , 6> m_rom_drawers;
	required_device<address_map_bank_device> m_rombank;
	required_device_array<hp80_io_slot_device , IOP_COUNT> m_io_slots;

	bool m_global_int_en;
	uint16_t m_int_serv;
	uint16_t m_int_acked;
	unsigned m_top_acked;
	unsigned m_top_pending;
	uint16_t m_int_en;
	uint8_t m_halt_lines;

	// State of keyboard
	ioport_value m_kb_state[ 3 ];
	bool m_int_kb_enabled;
	bool m_kb_enable;
	bool m_kb_pressed;
	bool m_kb_flipped;
	bool m_kb_lang_readout;
	bool m_kb_raw_readout;
	uint8_t m_kb_keycode;
	uint8_t m_raw_keycode;
	const bool m_has_int_keyb;

	// Timers
	typedef struct {
		uint8_t m_timer_cnt[ 4 ];
		uint8_t m_timer_reg[ 4 ];
		bool m_timer_en;
		bool m_timer_clr;
		uint8_t m_digit_to_match;
	} hw_timer_t;
	hw_timer_t m_hw_timer[ TIMER_COUNT ];
	uint8_t m_timer_idx;
	bool m_clk_busy;

	bool kb_scan_ioport(ioport_value pressed , unsigned idx_base , uint8_t& row , uint8_t& col);
	unsigned get_kb_irq() const;

	void irq_w(unsigned n_irq , bool state);
	void irq_en_w(unsigned n_irq , bool state);
	void release_irq(unsigned n_irq);
	static unsigned get_top_irq(uint16_t irqs);
	void update_int_bits();
	void update_irl();
};

hp80_base_state::hp80_base_state(const machine_config &mconfig, device_type type, const char *tag, bool has_int_keyb)
	: driver_device(mconfig , type , tag)
	, m_cpu(*this , "cpu")
	, m_clk_busy_timer(*this , "clk_busy_timer")
	, m_beep(*this , "beeper")
	, m_dac(*this , "dac")
	, m_io_key0(*this , "KEY0")
	, m_io_key1(*this , "KEY1")
	, m_io_key2(*this , "KEY2")
	, m_io_modkeys(*this, "MODKEYS")
	, m_io_language(*this , "LANGUAGE")
	, m_rom_drawers(*this , "drawer%u" , 1)
	, m_rombank(*this , "rombank")
	, m_io_slots(*this , "slot%u" , 1)
	, m_has_int_keyb(has_int_keyb)
{
}

void hp80_base_state::hp80_base(machine_config &config)
{
	HP_CAPRICORN(config, m_cpu, CPU_CLOCK);
	m_cpu->set_addrmap(AS_PROGRAM, &hp80_base_state::cpu_mem_map);
	m_cpu->intack_cb().set(FUNC(hp80_base_state::intack_r));
	config.set_perfect_quantum(m_cpu);

	ADDRESS_MAP_BANK(config, "rombank").set_map(&hp80_base_state::rombank_mem_map).set_options(ENDIANNESS_LITTLE, 8, 21, HP80_OPTROM_SIZE);

	// No idea at all about the actual keyboard scan frequency
	TIMER(config, "kb_timer").configure_periodic(FUNC(hp80_base_state::kb_scan), attotime::from_hz(100));

	// Hw timers are updated at 1 kHz rate
	TIMER(config, "hw_timer").configure_periodic(FUNC(hp80_base_state::timer_update), attotime::from_hz(1000));
	TIMER(config, m_clk_busy_timer).configure_generic(FUNC(hp80_base_state::clk_busy_timer));

	// Beeper
	SPEAKER(config, "mono").front_center();
	DAC_1BIT(config, m_dac , 0).add_route(ALL_OUTPUTS, "mono", 0.5, 0);
	BEEP(config, m_beep, CPU_CLOCK / 512).add_route(ALL_OUTPUTS, "mono", 0.5, 0);

	// Optional ROMs
	for (auto& finder : m_rom_drawers) {
		HP80_OPTROM(config, finder);
	}

	// I/O slots
	for (unsigned slot = 0; slot < 4; slot++) {
		auto& finder = m_io_slots[ slot ];
		HP80_IO_SLOT(config, finder).set_slot_no(slot);
		finder->irl_cb().set(FUNC(hp80_base_state::irl_w));
		finder->halt_cb().set(FUNC(hp80_base_state::halt_w));
	}
}

void hp80_base_state::cpu_mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x5fff).rom();
	map(0x6000, 0x7fff).m(m_rombank, FUNC(address_map_bank_device::amap8));
	map(0xff00, 0xff00).w(FUNC(hp80_base_state::ginten_w));
	map(0xff01, 0xff01).w(FUNC(hp80_base_state::gintdis_w));
	map(0xff02, 0xff02).rw(FUNC(hp80_base_state::keysts_r), FUNC(hp80_base_state::keysts_w));
	map(0xff03, 0xff03).rw(FUNC(hp80_base_state::keycod_r), FUNC(hp80_base_state::keycod_w));
	map(0xff0a, 0xff0a).rw(FUNC(hp80_base_state::clksts_r), FUNC(hp80_base_state::clksts_w));
	map(0xff0b, 0xff0b).rw(FUNC(hp80_base_state::clkdat_r), FUNC(hp80_base_state::clkdat_w));
	map(0xff18, 0xff18).w(FUNC(hp80_base_state::rselec_w));
	map(0xff40, 0xff40).rw(FUNC(hp80_base_state::intrsc_r), FUNC(hp80_base_state::intrsc_w));
}

void hp80_base_state::rombank_mem_map(address_map &map)
{
	map.unmap_value_high();
	// ROM in bank 0 is always present (it's part of system ROMs)
	map(0x0000, 0x1fff).rom();
}

void hp80_base_state::unmap_optroms(address_space &space)
{
}

void hp80_base_state::machine_start()
{
	save_item(NAME(m_global_int_en));
	save_item(NAME(m_int_serv));
	save_item(NAME(m_int_acked));
	save_item(NAME(m_top_acked));
	save_item(NAME(m_top_pending));
	save_item(NAME(m_int_en));
	save_item(NAME(m_halt_lines));
	save_pointer(NAME(m_kb_state) , 3);
	save_item(NAME(m_kb_enable));
	save_item(NAME(m_int_kb_enabled));
	save_item(NAME(m_kb_pressed));
	save_item(NAME(m_kb_flipped));
	save_item(NAME(m_kb_lang_readout));
	save_item(NAME(m_kb_raw_readout));
	save_item(NAME(m_kb_keycode));
	save_item(NAME(m_raw_keycode));
}

void hp80_base_state::machine_reset()
{
	m_int_serv = 0;
	m_int_acked = 0;
	m_top_acked = NO_IRQ;
	m_top_pending = NO_IRQ;
	m_int_en = 0;
	m_global_int_en = false;
	m_kb_state[ 0 ] = 0;
	m_kb_state[ 1 ] = 0;
	m_kb_state[ 2 ] = 0;
	m_kb_keycode = 0xff;
	m_kb_enable = true;
	m_int_kb_enabled = false;
	m_kb_pressed = false;
	m_kb_flipped = false;
	m_kb_lang_readout = false;
	m_kb_raw_readout = false;
	for (auto& timer : m_hw_timer) {
		for (unsigned i = 0; i < 4; i++) {
			timer.m_timer_cnt[ i ] = 0;
			timer.m_timer_reg[ i ] = 0;
		}
		timer.m_timer_en = false;
		timer.m_timer_clr = false;
		timer.m_digit_to_match = 0;
	}
	m_timer_idx = 0;
	m_clk_busy = false;
	update_irl();
	m_halt_lines = 0;
	m_cpu->set_input_line(INPUT_LINE_HALT , CLEAR_LINE);

	// Load optional ROMs (if any)
	unmap_optroms(m_rombank->space(AS_PROGRAM));
	for (auto& draw : m_rom_drawers) {
		LOG("Loading opt ROM in drawer %s\n" , draw->tag());
		draw->install_read_handler(m_rombank->space(AS_PROGRAM));
	}

	// Clear RSELEC
	m_rombank->set_bank(0xff);

	// Mount I/O slots in address space
	m_cpu->space(AS_PROGRAM).unmap_readwrite(0xff50 , 0xff5f);
	for (auto& io : m_io_slots) {
		io->install_read_write_handlers(m_cpu->space(AS_PROGRAM));
	}
}

// Vector table (indexed by bit no. in m_int_serv)
static const uint8_t vector_table[] = {
	0x04,   // Keyboard
	0x12,   // International keyboard (or is it 0x14?)
	0x08,   // Timer 0
	0x0a,   // Timer 1
	0x0c,   // Timer 2
	0x0e,   // Timer 3
	0x10,   // Slot 1
	0x10,   // Slot 2
	0x10,   // Slot 3
	0x10,   // Slot 4
	0x00    // No IRQ
};

uint8_t hp80_base_state::intack_r()
{
	LOG_IRQ("INTACK %u %u\n" , m_top_pending , m_top_acked);
	BIT_SET(m_int_acked , m_top_pending);
	m_top_acked = m_top_pending;
	if (m_top_pending > IRQ_IOP0_BIT && m_top_pending < IRQ_BIT_COUNT) {
		// Interrupts are disabled in all I/O translators of higher priority than
		// the one being serviced
		for (unsigned i = m_top_pending - 1; i >= IRQ_IOP0_BIT; i--) {
			irq_en_w(i , false);
		}
	}
	update_irl();
	return vector_table[ m_top_pending ];
}

void hp80_base_state::ginten_w(uint8_t data)
{
	LOG_IRQ("GINTEN\n");
	m_global_int_en = true;
	update_irl();
}

void hp80_base_state::gintdis_w(uint8_t data)
{
	LOG_IRQ("GINTDIS\n");
	m_global_int_en = false;
	update_irl();
}

uint8_t hp80_base_state::keysts_r()
{
	uint8_t res = 0;
	if (BIT(m_int_en , get_kb_irq())) {
		BIT_SET(res , 0);
	}
	if (m_kb_pressed) {
		BIT_SET(res , 1);
	}
	if (m_has_int_keyb) {
		if (m_kb_flipped) {
			BIT_SET(res , 2);
		}
		if (BIT(m_io_modkeys->read() , 2)) {
			BIT_SET(res , 6);
		}
	}
	if (BIT(m_io_modkeys->read() , 0)) {
		BIT_SET(res , 3);
	}
	if (m_global_int_en) {
		BIT_SET(res , 7);
	}
	return res;
}

void hp80_base_state::keysts_w(uint8_t data)
{
	if (BIT(data , 0)) {
		irq_en_w(get_kb_irq() , true);
	} else if (BIT(data , 1)) {
		irq_en_w(get_kb_irq() , false);
	}
	if (m_has_int_keyb) {
		m_kb_lang_readout = BIT(data , 2);
		m_kb_raw_readout = BIT(data , 3);
		if (!m_int_kb_enabled && (data & 0x0f) == 1) {
			m_int_kb_enabled = true;
		}
	}
	m_dac->write(BIT(data , 5));
	m_beep->set_state(BIT(data , 6));
	if (BIT(data , 7)) {
		m_kb_flipped = !m_kb_flipped;
	}
}

uint8_t hp80_base_state::keycod_r()
{
	if (m_kb_lang_readout && m_io_language) {
		return m_io_language->read();
	} else if (m_kb_raw_readout) {
		return m_raw_keycode;
	} else {
		return m_kb_keycode;
	}
}

void hp80_base_state::keycod_w(uint8_t data)
{
	if (m_kb_raw_readout) {
		m_kb_keycode = data;
	} else if (data == 1) {
		unsigned irq = get_kb_irq();
		irq_w(irq , false);
		m_kb_enable = true;
		release_irq(irq);
	}
}

uint8_t hp80_base_state::clksts_r()
{
	uint8_t res = 0;
	for (unsigned i = 0; i < TIMER_COUNT; i++) {
		if (BIT(m_int_en , IRQ_TIMER0_BIT + i)) {
			BIT_SET(res , i);
		}
	}
	if (!m_clk_busy) {
		BIT_SET(res , 7);
	}
	return res;
}

void hp80_base_state::clksts_w(uint8_t data)
{
	if (data == 0x0c) {
		// Set test mode (see timer_update)
		auto& timer = m_hw_timer[ m_timer_idx ];
		timer.m_digit_to_match = 1;
		timer.m_timer_cnt[ 0 ] = timer.m_timer_reg[ 0 ];
		timer.m_timer_cnt[ 1 ] = timer.m_timer_reg[ 1 ];
		timer.m_timer_cnt[ 2 ] = timer.m_timer_reg[ 2 ];
		timer.m_timer_cnt[ 3 ] = timer.m_timer_reg[ 3 ];
		LOG("Test mode enabled for timer %u\n" , m_timer_idx);
	} else {
		m_timer_idx = (data >> 6) & 3;
		auto& timer = m_hw_timer[ m_timer_idx ];
		if (BIT(data , 0)) {
			// Disable timer irq
			irq_en_w(IRQ_TIMER0_BIT + m_timer_idx , false);
		} else if (BIT(data , 1)) {
			// Enable timer irq
			irq_en_w(IRQ_TIMER0_BIT + m_timer_idx , true);
		}
		if (BIT(data , 2)) {
			// Stop timer
			timer.m_timer_en = false;
		} else if (BIT(data , 3)) {
			// Start timer
			timer.m_timer_en = true;
		}
		if (BIT(data , 4) || (BIT(data , 3) && timer.m_digit_to_match)) {
			// Clear timer
			timer.m_timer_clr = true;
			// Disable test mode
			timer.m_digit_to_match = 0;
		}
		if (BIT(data , 5)) {
			// Clear timer irq
			unsigned irq_n = IRQ_TIMER0_BIT + m_timer_idx;
			irq_w(irq_n , false);
			release_irq(irq_n);
		}
	}
}

uint8_t hp80_base_state::clkdat_r()
{
	uint8_t res;
	unsigned burst_idx = m_cpu->flatten_burst();
	if (burst_idx < 4) {
		res = m_hw_timer[ m_timer_idx ].m_timer_cnt[ burst_idx ];
	} else {
		// What happens when loading more than 4 bytes from timers?
		LOG("Reading more than 4 bytes from timer %u\n" , m_timer_idx);
		res = 0;
	}
	return res;
}

void hp80_base_state::clkdat_w(uint8_t data)
{
	unsigned burst_idx = m_cpu->flatten_burst();
	if (burst_idx < 4) {
		m_hw_timer[ m_timer_idx ].m_timer_reg[ burst_idx ] = data;
	} else {
		// What happens when storing more than 4 bytes into timers?
		LOG("Writing more than 4 bytes into timer %u\n" , m_timer_idx);
	}
}

void hp80_base_state::rselec_w(uint8_t data)
{
	m_rombank->set_bank(data);
}

uint8_t hp80_base_state::intrsc_r()
{
	if (m_top_acked >= IRQ_IOP0_BIT && m_top_acked < IRQ_BIT_COUNT) {
		LOG_IRQ("INTRSC %u\n" , m_top_acked);
		// Clear interrupt request in the slot being serviced
		m_io_slots[ m_top_acked - IRQ_IOP0_BIT ]->clear_service();
		return (uint8_t)m_io_slots[ m_top_acked - IRQ_IOP0_BIT ]->get_base_addr();
	} else {
		// Probably..
		return 0xff;
	}
}

void hp80_base_state::intrsc_w(uint8_t data)
{
	LOG_IRQ("INTRSC W %u %03x %03x %03x\n" , m_top_acked , m_int_serv , m_int_en , m_int_acked);
	for (auto& iop: m_io_slots) {
		iop->inten();
	}
	for (unsigned i = IRQ_IOP0_BIT; i < (IRQ_IOP0_BIT + IOP_COUNT); i++) {
		irq_en_w(i , true);
	}
	m_int_acked &= ~(((1U << IOP_COUNT) - 1) << IRQ_IOP0_BIT);
	update_int_bits();
}

// Outer index: key position [0..79] = r * 8 + c
// Inner index: SHIFT state (0 = no SHIFT, 1 = SHIFT)
static const uint8_t keyboard_table[ 80 ][ 2 ] = {
	// --    SHIFT              HP85            HP86
	{ 0xa2 , 0xac },    // 0,0: Down / Auto     k6 / k13
	{ 0xa1 , 0xa5 },    // 0,1: Up / Home       k5 / k12
	{ 0x83 , 0x87 },    // 0,2: k4 / k8         k4 / k11
	{ 0x82 , 0x86 },    // 0,3: k3 / k7         k3 / k10
	{ 0x81 , 0x85 },    // 0,4: k2 / k6         k2 / k9
	{ 0x80 , 0x84 },    // 0,5: k1 / k5         k1 / k8
	{ 0x96 , 0x60 },    // 0,6: LABEL KEY
	{ 0xff , 0xff },    // 0,7: N/U
	{ 0x38 , 0x2a },    // 1,0: 8
	{ 0x37 , 0x26 },    // 1,1: 7
	{ 0x36 , 0x5e },    // 1,2: 6
	{ 0x35 , 0x25 },    // 1,3: 5
	{ 0x34 , 0x24 },    // 1,4: 4
	{ 0x33 , 0x23 },    // 1,5: 3
	{ 0x32 , 0x40 },    // 1,6: 2
	{ 0x31 , 0x21 },    // 1,7: 1
	{ 0x49 , 0x69 },    // 2,0: I
	{ 0x55 , 0x75 },    // 2,1: U
	{ 0x59 , 0x79 },    // 2,2: Y
	{ 0x54 , 0x74 },    // 2,3: T
	{ 0x52 , 0x72 },    // 2,4: R
	{ 0x45 , 0x65 },    // 2,5: E
	{ 0x57 , 0x77 },    // 2,6: W
	{ 0x51 , 0x71 },    // 2,7: Q
	{ 0x4b , 0x6b },    // 3,0: K
	{ 0x4a , 0x6a },    // 3,1: J
	{ 0x48 , 0x68 },    // 3,2: H
	{ 0x47 , 0x67 },    // 3,3: G
	{ 0x46 , 0x66 },    // 3,4: F
	{ 0x44 , 0x64 },    // 3,5: D
	{ 0x53 , 0x73 },    // 3,6: S
	{ 0x41 , 0x61 },    // 3,7: A
	{ 0x4d , 0x6d },    // 4,0: M
	{ 0x4e , 0x6e },    // 4,1: N
	{ 0x42 , 0x62 },    // 4,2: B
	{ 0x56 , 0x76 },    // 4,3: V
	{ 0x43 , 0x63 },    // 4,4: C
	{ 0x58 , 0x78 },    // 4,5: X
	{ 0x5a , 0x7a },    // 4,6: Z
	{ 0x20 , 0x20 },    // 4,7: Space
	{ 0x2c , 0x3c },    // 5,0: , <
	{ 0x2e , 0x3e },    // 5,1: . >
	{ 0x2f , 0x3f },    // 5,2: / ?
	{ 0x8e , 0x90 },    // 5,3: PAUSE / STEP
	{ 0x8d , 0x8d },    // 5,4: RUN
	{ 0x2b , 0x7f },    // 5,5: KP +
	{ 0x2d , 0x7d },    // 5,6: KP -
	{ 0x2a , 0x7e },    // 5,7: KP *            N/U
	{ 0x4c , 0x6c },    // 6,0: L
	{ 0x3b , 0x3a },    // 6,1: ; :
	{ 0x27 , 0x22 },    // 6,2: ' "
	{ 0x9a , 0x9a },    // 6,3: END LINE
	{ 0x94 , 0x95 },    // 6,4: LIST / P LST
	{ 0xff , 0xff },    // 6,5: N/U
	{ 0x2a , 0x7e },    // 6,6: N/U             KP *
	{ 0x2f , 0x7b },    // 6,7: KP /
	{ 0x4f , 0x6f },    // 7,0: O
	{ 0x50 , 0x70 },    // 7,1: P
	{ 0x28 , 0x5b },    // 7,2: ( [
	{ 0x29 , 0x5d },    // 7,3: ) ]
	{ 0x8f , 0xad },    // 7,4: CONT / SCRATCH  CONT / TR/NORM
	{ 0xa0 , 0x92 },    // 7,5: -LINE / CLEAR   E / TEST
	{ 0x29 , 0x8c },    // 7,6: ) INIT
	{ 0xff , 0xff },    // 7,7: N/U
	{ 0x39 , 0x28 },    // 8,0: 9
	{ 0x30 , 0x29 },    // 8,1: 0
	{ 0x2d , 0x5f },    // 8,2: - _
	{ 0x3d , 0x2b },    // 8,3: = +
	{ 0x5c , 0x7c },    // 8,4: \ |
	{ 0x99 , 0x9b },    // 8,5: BS
	{ 0x28 , 0x8b },    // 8,6: ( RESET
	{ 0x5e , 0xa6 },    // 8,7: ^ / RESLT
	{ 0x9c , 0x93 },    // 9,0: LEFT / GRAPH    k7 / k14
	{ 0x9d , 0x89 },    // 9,1: RIGHT / COPY    -LINE / CLEAR
	{ 0xa3 , 0xa3 },    // 9,2: RPL / INS       UP / HOME
	{ 0xa4 , 0xa8 },    // 9,3: -CHAR / DEL     DOWN / A/G
	{ 0x9f , 0x9e },    // 9,4: ROLL            LEFT / I/R
	{ 0xaa , 0x88 },    // 9,5: LOAD / REW      RIGHT / -CHAR
	{ 0xa9 , 0x91 },    // 9,6: STORE / TEST    ROLL
	{ 0x8a , 0x8a }     // 9,7: PAPER ADVANCE   N/U
};

bool hp80_base_state::kb_scan_ioport(ioport_value pressed , unsigned idx_base , uint8_t& row , uint8_t& col)
{
	if (pressed) {
		unsigned bit_no = 31 - count_leading_zeros_32(pressed);
		row = (idx_base + bit_no) / 8;
		col = (idx_base + bit_no) % 8;
		return true;
	} else {
		return false;
	}
}

unsigned hp80_base_state::get_kb_irq() const
{
	return m_has_int_keyb ? IRQ_INTKEYB_BIT : IRQ_KEYBOARD_BIT;
}

TIMER_DEVICE_CALLBACK_MEMBER(hp80_base_state::kb_scan)
{
	ioport_value input[ 3 ];
	input[ 0 ] = m_io_key0->read();
	input[ 1 ] = m_io_key1->read();
	input[ 2 ] = m_io_key2->read();

	if (m_kb_enable && (!m_has_int_keyb || m_int_kb_enabled)) {
		uint8_t row;
		uint8_t col;

		bool got_key = kb_scan_ioport(input[ 0 ] & ~m_kb_state[ 0 ] , 0 , row , col) ||
			kb_scan_ioport(input[ 1 ] & ~m_kb_state[ 1 ] , 32 , row , col) ||
			kb_scan_ioport(input[ 2 ] & ~m_kb_state[ 2 ] , 64 , row , col);

		if (got_key) {
			if (m_has_int_keyb) {
				m_raw_keycode = (row << 4) + col;
			} else {
				uint8_t keycode = (row << 3) + col;
				uint8_t unshifted = keyboard_table[ keycode ][ 0 ];
				bool isalpha = unshifted >= 'A' && unshifted <= 'Z';
				ioport_value modifiers = m_io_modkeys->read();
				bool shift = BIT(modifiers , 0);
				bool caps_lock = BIT(modifiers , 1);
				bool control = BIT(modifiers , 2);
				if (isalpha) {
					shift = shift ^ caps_lock ^ m_kb_flipped;
				}
				keycode = keyboard_table[ keycode ][ shift ];
				uint8_t tmp = isalpha ? unshifted : keycode;
				if (control && (tmp & 0xe0) == 0x40) {
					keycode &= ~0xe0;
				}
				m_kb_keycode = keycode;
			}
			irq_w(get_kb_irq() , true);
			m_kb_enable = false;
		}
	}
	m_kb_pressed = input[ 0 ] != 0 ||
		input[ 1 ] != 0 ||
		input[ 2 ] != 0;

	m_kb_state[ 0 ] = input[ 0 ];
	m_kb_state[ 1 ] = input[ 1 ];
	m_kb_state[ 2 ] = input[ 2 ];
}

TIMER_DEVICE_CALLBACK_MEMBER(hp80_base_state::timer_update)
{
	for (unsigned i = 0; i < TIMER_COUNT; i++) {
		auto& timer = m_hw_timer[ i ];
		if (timer.m_timer_clr) {
			timer.m_timer_clr = false;
			timer.m_timer_cnt[ 0 ] = 0;
			timer.m_timer_cnt[ 1 ] = 0;
			timer.m_timer_cnt[ 2 ] = 0;
			timer.m_timer_cnt[ 3 ] = 0;
		} else if (timer.m_timer_en) {
			if (timer.m_digit_to_match) {
				// Timers have an undocumented mode (used by test "J" of service ROM)
				// where the counter has to match in sequence all digits of register
				// in order to raise an interrupt. In other words interrupt is generated
				// after a number of updates that's equal to the sum of all digits in
				// register + 1. My opinion is that people at HP designed this mode to
				// allow all digits in a timer to be tested quickly. Without this special
				// mode it takes more than 27 hours to check that all digits increment
				// correctly and that there are no stuck bits.
				// From an operative point of view, we copy register into counter when
				// this special mode is activated (see clksts_w). Then, at each update,
				// we decrement the digit of counter pointed to by m_digit_to_match (1 =
				// least significant digit). Each time a digit "borrows" (i.e. it decrements
				// from 0 to 9), we move on to digit at left. When m_digit_to_match reaches
				// 9, interrupt is raised and the timer stops.
				// At this point counter is always "99999999".
				if (timer.m_digit_to_match < 9) {
					while (true) {
						bool borrow = false;
						uint8_t b = timer.m_timer_cnt[ (timer.m_digit_to_match - 1) / 2 ];
						if (BIT(timer.m_digit_to_match , 0)) {
							// Least significant digit in b
							if (b & 0x0f) {
								b--;
							} else {
								b = (b & 0xf0) | 9;
								borrow = true;
							}
						} else {
							// Most significant digit in b
							if (b & 0xf0) {
								b -= 0x10;
							} else {
								b = 0x99;
								borrow = true;
							}
						}
						timer.m_timer_cnt[ (timer.m_digit_to_match - 1) / 2 ] = b;
						if (borrow) {
							timer.m_digit_to_match++;
							if (timer.m_digit_to_match == 9) {
								irq_w(IRQ_TIMER0_BIT + i , true);
								break;
							}
						} else {
							break;
						}
					}
				}
			} else {
				// Standard timer mode
				// Increment all active timers by 1
				bool carry = true;
				for (unsigned idx = 0; idx < 4 && carry; idx++) {
					carry = false;
					uint8_t b = timer.m_timer_cnt[ idx ];
					b++;
					if ((b & 0xf) > 9) {
						b += 6;
						if (b >= 0xa0) {
							b += 0x60;
							carry = true;
						}
					}
					timer.m_timer_cnt[ idx ] = b;
				}
				if (timer.m_timer_cnt[ 0 ] == timer.m_timer_reg[ 0 ] &&
					timer.m_timer_cnt[ 1 ] == timer.m_timer_reg[ 1 ] &&
					timer.m_timer_cnt[ 2 ] == timer.m_timer_reg[ 2 ] &&
					timer.m_timer_cnt[ 3 ] == timer.m_timer_reg[ 3 ]) {
					timer.m_timer_cnt[ 0 ] = 0;
					timer.m_timer_cnt[ 1 ] = 0;
					timer.m_timer_cnt[ 2 ] = 0;
					timer.m_timer_cnt[ 3 ] = 0;
					irq_w(IRQ_TIMER0_BIT + i , true);
				}
			}
		}
	}
	m_clk_busy = true;
	m_clk_busy_timer->adjust(attotime::from_usec(TIMER_BUSY_USEC));
}

TIMER_DEVICE_CALLBACK_MEMBER(hp80_base_state::clk_busy_timer)
{
	m_clk_busy = false;
}

void hp80_base_state::irl_w(offs_t offset, uint8_t data)
{
	irq_w(offset + IRQ_IOP0_BIT , data != 0);
}

void hp80_base_state::halt_w(offs_t offset, uint8_t data)
{
	bool prev_halt = m_halt_lines != 0;
	COPY_BIT(data != 0 , m_halt_lines , offset);
	bool new_halt = m_halt_lines != 0;
	if (prev_halt != new_halt) {
		LOG_IRQ("halt=%d hl=%x\n" , new_halt , m_halt_lines);
		m_cpu->set_input_line(INPUT_LINE_HALT , new_halt);
	}
}

void hp80_base_state::irq_w(unsigned n_irq , bool state)
{
	LOG_IRQ("IRQ_W %u %d\n" , n_irq , state);
	COPY_BIT(state , m_int_serv , n_irq);
	update_int_bits();
}

void hp80_base_state::irq_en_w(unsigned n_irq , bool state)
{
	LOG_IRQ("IRQ_EN_W %u %d\n" , n_irq , state);
	COPY_BIT(state , m_int_en , n_irq);
	update_int_bits();
}

void hp80_base_state::release_irq(unsigned n_irq)
{
	if (BIT(m_int_acked , n_irq)) {
		BIT_CLR(m_int_acked , n_irq);
		update_int_bits();
	}
}

unsigned hp80_base_state::get_top_irq(uint16_t irqs)
{
	unsigned top;
	for (top = 0; top < IRQ_BIT_COUNT && !BIT(irqs , 0); top++ , irqs >>= 1) {
	}
	return top;
}

void hp80_base_state::update_int_bits()
{
	m_top_pending = get_top_irq(m_int_en & m_int_serv);
	m_top_acked = get_top_irq(m_int_acked);
	update_irl();
}

void hp80_base_state::update_irl()
{
	m_cpu->set_input_line(0 , m_global_int_en && m_top_pending < m_top_acked);
}

// ************
//  hp85_state
// ************
class hp85_state : public hp80_base_state
{
public:
	hp85_state(const machine_config &mconfig, device_type type, const char *tag);

	// **** Constants of HP85 ****
	static constexpr unsigned MASTER_CLOCK  = 9808000;
	// Video memory is actually made of 16384 4-bit nibbles
	static constexpr unsigned VIDEO_MEM_SIZE    = 8192;
	static constexpr unsigned ALPHA_MEM_SIZE    = 4096;
	static constexpr unsigned GRAPH_MEM_SIZE    = 16384;
	static constexpr unsigned CRT_STS_READY_BIT     = 0;
	static constexpr unsigned CRT_STS_DISPLAY_BIT   = 1;
	static constexpr unsigned CRT_STS_BUSY_BIT      = 7;
	static constexpr unsigned CRT_CTL_RD_RQ_BIT     = 0;
	static constexpr unsigned CRT_CTL_WIPEOUT_BIT   = 1;
	static constexpr unsigned CRT_CTL_POWERDN_BIT   = 2;
	static constexpr unsigned CRT_CTL_GRAPHICS_BIT  = 7;
	// Time to read/write a byte in video memory (in master clock cycles)
	static constexpr unsigned CRT_RW_TIME           = 96;
	// Internal printer has a moving printhead with 8 vertically-arranged resistors that print dots
	// by heating thermal paper. The horizontal span of the printhead covers 224 columns.
	// In alpha mode, each sweep prints up to 32 characters. Each character has a 8x7 cell.
	// 8 pixels of cell height are covered by the printhead height, whereas 7 pixels of width
	// allow for 32 characters on a row (224 = 32 * 7).
	// After an alpha line is printed the paper advances by 10 pixel lines, so that a space of
	// 2 lines is left between alpha lines.
	// In graphic mode, printing starts at column 16 and covers 192 columns. So on each side of
	// the printed area there's a 16-column wide margin (224 = 192 + 2 * 16).
	// Once a graphic line is printed, paper advances by 8 pixel lines so that no space is inserted
	// between successive sweeps.
	// A full image of the graphic screen (256 x 192) is printed rotated 90 degrees clockwise.
	// The printer controller chip (1MA9) has an embedded character generator ROM that is used
	// when printing alpha lines. This ROM is also read by the CPU when drawing text on the graphic
	// screen (BASIC "LABEL" instruction).
	static constexpr unsigned PRT_BUFFER_SIZE      = 192;
	static constexpr unsigned PRTSTS_PAPER_OK_BIT  = 7;
	static constexpr unsigned PRTSTS_DATARDY_BIT   = 6;
	static constexpr unsigned PRTSTS_PRTRDY_BIT    = 0;
	static constexpr unsigned PRTCTL_GRAPHIC_BIT   = 7;
	//constexpr unsigned PRTCTL_POWERUP_BIT = 6;
	static constexpr unsigned PRTCTL_READGEN_BIT   = 5;
	// Time to print a line (nominal speed is 2 lines/s)
	static constexpr unsigned PRT_BUSY_MSEC        = 500;
	// Horizontal start position of graphic print (16 columns from left-hand side)
	static constexpr unsigned PRT_GRAPH_OFFSET     = 16;
	// Height of printhead
	static constexpr unsigned PRT_PH_HEIGHT        = 8;
	// Height of alpha rows
	static constexpr unsigned PRT_ALPHA_HEIGHT     = 10;
	// Width of character cells
	static constexpr unsigned PRT_CELL_WIDTH       = 7;
	// Height of graphic rows
	//constexpr unsigned PRT_GRAPH_HEIGHT   = 8;
	// Width of graphic sweeps
	static constexpr unsigned PRT_GRAPH_WIDTH      = 192;
	// Width of printhead sweeps
	static constexpr unsigned PRT_WIDTH            = 224;

	void hp85(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void vblank_w(int state);

	uint8_t crtc_r(offs_t offset);
	void crtc_w(offs_t offset, uint8_t data);
	void prtlen_w(uint8_t data);
	uint8_t prchar_r();
	void prchar_w(uint8_t data);
	uint8_t prtsts_r();
	void prtctl_w(uint8_t data);
	void prtdat_w(uint8_t data);

	TIMER_DEVICE_CALLBACK_MEMBER(vm_timer);
	TIMER_DEVICE_CALLBACK_MEMBER(prt_busy_timer);

	virtual void cpu_mem_map(address_map &map) override ATTR_COLD;
	virtual void unmap_optroms(address_space &space) override;

	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<timer_device> m_vm_timer;
	required_device<timer_device> m_prt_busy_timer;
	required_device<bitbanger_device> m_prt_graph_out;
	required_device<bitbanger_device> m_prt_alpha_out;

	// Character generators
	required_region_ptr<uint8_t> m_chargen;
	required_region_ptr<uint8_t> m_prt_chargen;

	bitmap_rgb32 m_bitmap;
	std::vector<uint8_t> m_video_mem;
	uint16_t m_crt_sad = 0;
	uint16_t m_crt_bad = 0;
	uint8_t m_crt_sts = 0;
	uint8_t m_crt_ctl = 0;
	uint8_t m_crt_read_byte = 0;
	uint8_t m_crt_write_byte = 0;

	// Printer
	uint8_t m_prtlen = 0;
	uint8_t m_prt_idx = 0;
	uint8_t m_prchar_r = 0;
	uint8_t m_prchar_w = 0;
	uint8_t m_prtsts = 0;
	uint8_t m_prtctl = 0;
	uint8_t m_prt_buffer[ PRT_BUFFER_SIZE ]{};

	attotime time_to_video_mem_availability() const;
	static void get_video_addr(uint16_t addr , uint16_t& byte_addr , bool& lsb_nibble);
	uint8_t video_mem_r(uint16_t addr , uint16_t addr_mask) const;
	void video_mem_w(uint16_t addr , uint16_t addr_mask , uint8_t data);
	void video_mem_read();
	void video_mem_write();

	uint8_t get_prt_font(uint8_t ch , unsigned col) const;
	void prt_format_alpha(unsigned row , uint8_t *pixel_row) const;
	void prt_format_graphic(unsigned row , uint8_t *pixel_row) const;
	void prt_output_row(const uint8_t *pixel_row);
	void prt_do_printing();
};

hp85_state::hp85_state(const machine_config &mconfig, device_type type, const char *tag)
	: hp80_base_state(mconfig , type , tag),
	  m_screen(*this , "screen"),
	  m_palette(*this , "palette"),
	  m_vm_timer(*this , "vm_timer"),
	  m_prt_busy_timer(*this , "prt_busy_timer"),
	  m_prt_graph_out(*this , "prt_graphic"),
	  m_prt_alpha_out(*this , "prt_alpha"),
	  m_chargen(*this , "chargen"),
	  m_prt_chargen(*this , "prt_chargen")
{
}

void hp85_state::machine_start()
{
	hp80_base_state::machine_start();
	m_screen->register_screen_bitmap(m_bitmap);
	m_video_mem.resize(VIDEO_MEM_SIZE);
}

void hp85_state::machine_reset()
{
	hp80_base_state::machine_reset();

	m_crt_sad = 0;
	m_crt_bad = 0;
	m_crt_sts = 0x7c;
	m_crt_ctl = BIT_MASK<uint8_t>(CRT_CTL_POWERDN_BIT) | BIT_MASK<uint8_t>(CRT_CTL_WIPEOUT_BIT);
	m_crt_read_byte = 0;
	m_crt_write_byte = 0;
	m_prtlen = 0;
	m_prt_idx = PRT_BUFFER_SIZE;
	m_prchar_r = 0;
	m_prchar_w = 0;
	m_prtsts = BIT_MASK<uint8_t>(PRTSTS_PAPER_OK_BIT) | BIT_MASK<uint8_t>(PRTSTS_PRTRDY_BIT);
	m_prtctl = 0;
}

uint32_t hp85_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
	return 0;
}

void hp85_state::vblank_w(int state)
{
	COPY_BIT(!state , m_crt_sts , CRT_STS_DISPLAY_BIT);
	if (state) {
		if (BIT(m_crt_ctl , CRT_CTL_WIPEOUT_BIT) || BIT(m_crt_ctl , CRT_CTL_POWERDN_BIT)) {
			// Blank video
			m_bitmap.fill(rgb_t::black());
		} else if (BIT(m_crt_ctl , CRT_CTL_GRAPHICS_BIT)) {
			// Render graphic video
			uint16_t video_start = m_crt_sad;
			for (unsigned y = 0; y < 192; y++) {
				for (unsigned x = 0; x < 256; x += 8) {
					uint8_t pixels = video_mem_r(video_start , GRAPH_MEM_SIZE / 2 - 1);
					video_start += 2;
					for (unsigned sub_x = 0; sub_x < 8; sub_x++) {
						m_bitmap.pix(y , x + sub_x) = m_palette->pen(BIT(pixels , 7));
						pixels <<= 1;
					}
				}
			}
		} else {
			// Render alpha video
			uint16_t video_start = m_crt_sad;
			for (unsigned row = 0; row < 192; row += 12) {
				for (unsigned col = 0; col < 256; col += 8) {
					uint8_t ch = video_mem_r(video_start , ALPHA_MEM_SIZE / 2 - 1);
					video_start += 2;
					for (unsigned sub_row = 0; sub_row < 12; sub_row++) {
						uint8_t pixels;
						if (sub_row < 8) {
							pixels = m_chargen[ (ch & 0x7f) * 8 + sub_row ];
						} else if (BIT(ch , 7) && (sub_row == 9 || sub_row == 10)) {
							// Underline
							pixels = 0xfe;
						} else {
							pixels = 0;
						}
						for (unsigned sub_x = 0; sub_x < 8; sub_x++) {
							m_bitmap.pix(row + sub_row , col + sub_x) = m_palette->pen(BIT(pixels , 7));
							pixels <<= 1;
						}
					}
				}
			}
		}
	}
}

uint8_t hp85_state::crtc_r(offs_t offset)
{
	uint8_t res = 0xff;

	// Read from CRT controller (1MA5)
	switch (offset) {
	case 0:
		// CRTSAD: write-only
		break;

	case 1:
		// CRTBAD: write-only
		break;

	case 2:
		// CRTSTS
		res = m_crt_sts;
		break;

	case 3:
		// CRTDAT
		res = m_crt_read_byte;
		break;
	}
	return res;
}

void hp85_state::crtc_w(offs_t offset, uint8_t data)
{
	// Write to CRT controller (1MA5)
	uint8_t burst_idx = m_cpu->flatten_burst();
	switch (offset) {
	case 0:
		// CRTSAD
		if (burst_idx == 1) {
			m_crt_sad = ((uint16_t)data << 8) | (m_crt_sad & 0xff);
		} else if (burst_idx == 0) {
			m_crt_sad = (m_crt_sad & 0xff00) | data;
		}
		break;

	case 1:
		// CRTBAD
		if (burst_idx == 1) {
			m_crt_bad = ((uint16_t)data << 8) | (m_crt_bad & 0xff);
		} else if (burst_idx == 0) {
			m_crt_bad = (m_crt_bad & 0xff00) | data;
		}
		break;

	case 2:
		// CRTCTL
		m_crt_ctl = data;
		if (BIT(m_crt_ctl , CRT_CTL_RD_RQ_BIT)) {
			BIT_CLR(m_crt_sts , CRT_STS_READY_BIT);
			BIT_SET(m_crt_sts , CRT_STS_BUSY_BIT);
			attotime vm_av = time_to_video_mem_availability();
			m_vm_timer->adjust(vm_av + attotime::from_ticks(CRT_RW_TIME , MASTER_CLOCK));
		}
		break;

	case 3:
		// CRTDAT
		{
			m_crt_write_byte = data;
			BIT_CLR(m_crt_sts , CRT_STS_READY_BIT);
			BIT_SET(m_crt_sts , CRT_STS_BUSY_BIT);
			attotime vm_av = time_to_video_mem_availability();
			m_vm_timer->adjust(vm_av + attotime::from_ticks(CRT_RW_TIME , MASTER_CLOCK));
		}
		break;
	}
}

void hp85_state::prtlen_w(uint8_t data)
{
	if (data == 0) {
		// Advance paper
		memset(m_prt_buffer , 0 , sizeof(m_prt_buffer));
		m_prt_idx = 0;
		prt_do_printing();
	} else {
		m_prtlen = data;
		if (!BIT(m_prtctl , PRTCTL_GRAPHIC_BIT)) {
			m_prt_idx = 0;
		}
	}
}

uint8_t hp85_state::prchar_r()
{
	return m_prchar_r;
}

void hp85_state::prchar_w(uint8_t data)
{
	m_prchar_w = data;
}

uint8_t hp85_state::prtsts_r()
{
	return m_prtsts;
}

void hp85_state::prtctl_w(uint8_t data)
{
	m_prtctl = data;
	BIT_SET(m_prtsts , PRTSTS_PRTRDY_BIT);
	if (BIT(m_prtctl , PRTCTL_READGEN_BIT)) {
		// Reading printer char. gen.
		m_prchar_r = get_prt_font(m_prchar_w , m_prtctl & 7);
		BIT_SET(m_prtsts , PRTSTS_DATARDY_BIT);
	} else {
		BIT_CLR(m_prtsts , PRTSTS_DATARDY_BIT);
	}
	if (BIT(m_prtctl , PRTCTL_GRAPHIC_BIT)) {
		m_prt_idx = 0;
	}
}

void hp85_state::prtdat_w(uint8_t data)
{
	m_cpu->flatten_burst();
	if (m_prt_idx < PRT_BUFFER_SIZE) {
		m_prt_buffer[ m_prt_idx++ ] = data;
		if (m_prt_idx == PRT_BUFFER_SIZE || (!BIT(m_prtctl , PRTCTL_GRAPHIC_BIT) && m_prt_idx >= m_prtlen)) {
			prt_do_printing();
			m_prt_idx = PRT_BUFFER_SIZE;
		}
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(hp85_state::prt_busy_timer)
{
	BIT_SET(m_prtsts , PRTSTS_PRTRDY_BIT);
}

TIMER_DEVICE_CALLBACK_MEMBER(hp85_state::vm_timer)
{
	if (BIT(m_crt_ctl , CRT_CTL_RD_RQ_BIT)) {
		video_mem_read();
	} else {
		video_mem_write();
	}
	BIT_CLR(m_crt_sts , CRT_STS_BUSY_BIT);
}

attotime hp85_state::time_to_video_mem_availability() const
{
	if (BIT(m_crt_ctl , CRT_CTL_WIPEOUT_BIT) || BIT(m_crt_ctl , CRT_CTL_POWERDN_BIT)) {
		// Blank video, immediate access
		return attotime::zero;
	} else if (m_screen->vblank()) {
		// Vertical blanking, immediate access
		return attotime::zero;
	} else {
		// In the active part, wait until vertical blanking
		return m_screen->time_until_vblank_start();
	}
}

void hp85_state::get_video_addr(uint16_t addr , uint16_t& byte_addr , bool& lsb_nibble)
{
	byte_addr = (addr / 2) & (VIDEO_MEM_SIZE - 1);
	lsb_nibble = BIT(addr , 0);
}

uint8_t hp85_state::video_mem_r(uint16_t addr , uint16_t addr_mask) const
{
	uint16_t byte_addr;
	bool lsb_nibble;

	get_video_addr(addr , byte_addr , lsb_nibble);

	byte_addr &= addr_mask;

	uint8_t res;

	if (lsb_nibble) {
		res = (m_video_mem[ byte_addr ] & 0x0f) << 4;
		byte_addr = (byte_addr + 1) & addr_mask;
		res |= (m_video_mem[ byte_addr ] & 0xf0) >> 4;
	} else {
		res = m_video_mem[ byte_addr ];
	}

	return res;
}

void hp85_state::video_mem_w(uint16_t addr , uint16_t addr_mask , uint8_t data)
{
	uint16_t byte_addr;
	bool lsb_nibble;

	get_video_addr(addr , byte_addr , lsb_nibble);

	byte_addr &= addr_mask;

	if (lsb_nibble) {
		m_video_mem[ byte_addr ] = (m_video_mem[ byte_addr ] & 0xf0) | (data >> 4);
		byte_addr = (byte_addr + 1) & addr_mask;
		m_video_mem[ byte_addr ] = (m_video_mem[ byte_addr ] & 0x0f) | (data << 4);
	} else {
		m_video_mem[ byte_addr ] = data;
	}
}

void hp85_state::video_mem_read()
{
	uint16_t mask;

	if (BIT(m_crt_ctl , CRT_CTL_GRAPHICS_BIT)) {
		mask = GRAPH_MEM_SIZE / 2 - 1;
	} else {
		mask = ALPHA_MEM_SIZE / 2 - 1;
	}
	m_crt_read_byte = video_mem_r(m_crt_bad , mask);
	m_crt_bad += 2;
	BIT_CLR(m_crt_ctl , CRT_CTL_RD_RQ_BIT);
	BIT_SET(m_crt_sts , CRT_STS_READY_BIT);
}

void hp85_state::video_mem_write()
{
	uint16_t mask;

	if (BIT(m_crt_ctl , CRT_CTL_GRAPHICS_BIT)) {
		mask = GRAPH_MEM_SIZE / 2 - 1;
	} else {
		mask = ALPHA_MEM_SIZE / 2 - 1;
	}
	video_mem_w(m_crt_bad , mask , m_crt_write_byte);
	m_crt_bad += 2;
}

uint8_t hp85_state::get_prt_font(uint8_t ch , unsigned col) const
{
	// Bit 7: pixel @ top
	// Bit 0: pixel @ bottom
	uint8_t column = m_prt_chargen[ (((unsigned)ch & 0x7f) << 3) | col ];
	if (BIT(ch , 7)) {
		// Underline
		BIT_SET(column , 0);
	}
	return column;
}

void hp85_state::prt_format_alpha(unsigned row , uint8_t *pixel_row) const
{
	memset(pixel_row , 0 , PRT_WIDTH);
	for (unsigned i = 0; i < m_prt_idx; i++) {
		for (unsigned j = 0; j < PRT_CELL_WIDTH; j++) {
			uint8_t pixel_col = get_prt_font(m_prt_buffer[ i ] , j);
			*pixel_row++ = BIT(pixel_col , 7 - row);
		}
	}
}

void hp85_state::prt_format_graphic(unsigned row , uint8_t *pixel_row) const
{
	memset(pixel_row , 0 , PRT_WIDTH);
	pixel_row += PRT_GRAPH_OFFSET;
	for (unsigned i = 0; i < PRT_GRAPH_WIDTH; i++) {
		*pixel_row++ = BIT(m_prt_buffer[ i ] , 7 - row);
	}
}

void hp85_state::prt_output_row(const uint8_t *pixel_row)
{
	for (unsigned i = 0; i < PRT_WIDTH; i++) {
		m_prt_graph_out->output(*pixel_row++ != 0 ? '*' : ' ');
	}
	m_prt_graph_out->output('\n');
}

void hp85_state::prt_do_printing()
{
	uint8_t pixel_row[ PRT_WIDTH ];
	for (unsigned row = 0; row < PRT_PH_HEIGHT; row++) {
		if (BIT(m_prtctl , PRTCTL_GRAPHIC_BIT)) {
			prt_format_graphic(row , pixel_row);
		} else {
			prt_format_alpha(row , pixel_row);
		}
		prt_output_row(pixel_row);
	}
	if (!BIT(m_prtctl , PRTCTL_GRAPHIC_BIT)) {
		// Dump the text line to alpha bitbanger
		for (unsigned i = 0; i < m_prt_idx; i++) {
			m_prt_alpha_out->output(m_prt_buffer[ i ]);
		}
		m_prt_alpha_out->output('\n');
		// Add 2 empty lines
		memset(pixel_row , 0 , PRT_WIDTH);
		for (unsigned i = 0; i < (PRT_ALPHA_HEIGHT - PRT_PH_HEIGHT); i++) {
			prt_output_row(pixel_row);
		}
	}
	// Start busy timer
	BIT_CLR(m_prtsts , PRTSTS_PRTRDY_BIT);
	m_prt_busy_timer->adjust(attotime::from_msec(PRT_BUSY_MSEC));
}

#define IOP_MASK(x) BIT_MASK<ioport_value>((x))

static INPUT_PORTS_START(hp85)
	// Keyboard is arranged in a matrix of 10 rows and 8 columns. In addition there are 3 keys with
	// dedicated input lines: SHIFT, SHIFT LOCK & CONTROL.
	// A key on row "r"=[0..9] and column "c"=[0..7] is mapped to bit "b" of KEY"n" input, where
	// n = r / 4
	// b = (r % 4) * 8 + c
	PORT_START("KEY0")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_NAME("Down AUTO") // 0,0: Down / Auto
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_NAME("Up Home")       // 0,1: Up / Home
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_NAME("k4 k8")         // 0,2: k4 / k8
	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_NAME("k3 k7")         // 0,3: k3 / k7
	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_NAME("k2 k6")         // 0,4: k2 / k6
	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_NAME("k1 k5")         // 0,5: k1 / k5
	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("LABEL KEY")                                                        // 0,6: LABEL KEY
	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                 // 0,7: N/U
	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')                            // 1,0: 8
	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')                            // 1,1: 7
	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')                           // 1,2: 6
	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')                           // 1,3: 5
	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')                           // 1,4: 4
	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')                           // 1,5: 3
	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')                           // 1,6: 2
	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')                           // 1,7: 1
	PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')                           // 2,0: I
	PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')                           // 2,1: U
	PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')                           // 2,2: Y
	PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')                           // 2,3: T
	PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')                           // 2,4: R
	PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')                           // 2,5: E
	PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')                           // 2,6: W
	PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')                           // 2,7: Q
	PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')                           // 3,0: K
	PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')                           // 3,1: J
	PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')                           // 3,2: H
	PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')                           // 3,3: G
	PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')                           // 3,4: F
	PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')                           // 3,5: D
	PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')                           // 3,6: S
	PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')                           // 3,7: A

	PORT_START("KEY1")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')                            // 4,0: M
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')                            // 4,1: N
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')                            // 4,2: B
	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')                            // 4,3: V
	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')                            // 4,4: C
	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')                            // 4,5: X
	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')                            // 4,6: Z
	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')                                       // 4,7: Space
	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')                        // 5,0: ,
	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')                         // 5,1: .
	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')                       // 5,2: / ?
	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("PAUSE STEP")                                                      // 5,3: PAUSE / STEP
	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("RUN")                                                             // 5,4: RUN
	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD)) PORT_NAME("KP +") // 5,5: KP +
	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) PORT_NAME("KP -") // 5,6: KP -
	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(ASTERISK)) PORT_NAME("KP *") // 5,7: KP * (not sure)
	PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l')                           // 6,0: L
	PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')                       // 6,1: ;
	PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')                      // 6,2: ' "
	PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("END LINE")                 // 6,3: END LINE
	PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("LIST P LST")                                                      // 6,4: LIST / P LST
	PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                // 6,5: N/U
	PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                // 6,6: N/U
	PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD)) PORT_NAME("KP /") // 6,7: KP /
	PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')                           // 7,0: O
	PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')                           // 7,1: P
	PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('(') PORT_CHAR('[')                   // 7,2: ( [
	PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(')') PORT_CHAR(']')                  // 7,3: ) ]
	PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("CONT SCRATCH")                                                    // 7,4: CONT / SCRATCH
	PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("-LINE CLEAR")                                                     // 7,5: -LINE / CLEAR
	PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME(") INIT")                                                          // 7,6: ) INIT
	PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                // 7,7: N/U

	PORT_START("KEY2")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')                            // 8,0: 9
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')                            // 8,1: 0
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')                        // 8,2: - _
	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')                       // 8,3: = +
	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('\\') PORT_CHAR('|')                       // 8,4: \ |
	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)                                     // 8,5: BS
	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("( RESET")                                                          // 8,6: ( RESET
	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("^ RESLT")                                                          // 8,7: ^ / RESLT
	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))    PORT_NAME("Left GRAPH") // 9,0: LEFT / GRAPH
	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_NAME("Right COPY")  // 9,1: RIGHT / COPY
	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT) PORT_NAME("RPL INS")                               // 9,2: RPL / INS
	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL) PORT_NAME("-CHAR DEL")                                // 9,3: -CHAR / DEL
	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN) PORT_NAME("ROLL")                                    // 9,4: ROLL
	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("LOAD REW")                                                        // 9,5: LOAD / REW
	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("STORE TEST")                                                      // 9,6: STORE / TEST
	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("PAPER ADVANCE")                                                   // 9,7: PAPER ADVANCE

	PORT_START("MODKEYS")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)                // Shift
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_NAME("Shift lock")   // Shift lock
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)              // Control

INPUT_PORTS_END

void hp85_state::cpu_mem_map(address_map &map)
{
	hp80_base_state::cpu_mem_map(map);
	map(0x8000, 0xbfff).ram();
	map(0xff04, 0xff07).rw(FUNC(hp85_state::crtc_r), FUNC(hp85_state::crtc_w));
	map(0xff08, 0xff09).rw("tape", FUNC(hp_1ma6_device::reg_r), FUNC(hp_1ma6_device::reg_w));
	map(0xff0c, 0xff0c).w(FUNC(hp85_state::prtlen_w));
	map(0xff0d, 0xff0d).rw(FUNC(hp85_state::prchar_r), FUNC(hp85_state::prchar_w));
	map(0xff0e, 0xff0e).rw(FUNC(hp85_state::prtsts_r), FUNC(hp85_state::prtctl_w));
	map(0xff0f, 0xff0f).w(FUNC(hp85_state::prtdat_w));
}

void hp85_state::unmap_optroms(address_space &space)
{
	// OptROMs are in rombanks [01..FF]
	space.unmap_read(HP80_OPTROM_SIZE * 1 , HP80_OPTROM_SIZE * 0x100 - 1);
}

void hp85_state::hp85(machine_config &config)
{
	hp80_base(config);

	m_cpu->set_addrmap(AS_PROGRAM, &hp85_state::cpu_mem_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(MASTER_CLOCK / 2 , 312 , 0 , 256 , 256 , 0 , 192);
	m_screen->set_screen_update(FUNC(hp85_state::screen_update));
	m_screen->screen_vblank().set(FUNC(hp85_state::vblank_w));
	PALETTE(config, m_palette, palette_device::MONOCHROME);
	TIMER(config, m_vm_timer).configure_generic(FUNC(hp85_state::vm_timer));

	TIMER(config, m_prt_busy_timer).configure_generic(FUNC(hp85_state::prt_busy_timer));

	// Tape drive
	HP_1MA6(config, "tape", 0);

	// Printer output
	BITBANGER(config, m_prt_graph_out, 0);
	BITBANGER(config, m_prt_alpha_out, 0);

	SOFTWARE_LIST(config, "optrom_list").set_original("hp85_rom");
}

ROM_START(hp85)
	ROM_REGION(0x6000 , "cpu" , 0)
	ROM_LOAD("romsys1.bin" , 0x0000 , 0x2000 , CRC(7724b1e9) SHA1(7836195389de2ac0eab7199835f5dc8f7dc41729))
	ROM_LOAD("romsys2.bin" , 0x2000 , 0x2000 , CRC(50a85263) SHA1(3cf1d08749103ee245d572550ba1b053ffc7ef57))
	ROM_LOAD("romsys3.bin" , 0x4000 , 0x2000 , CRC(0df385f0) SHA1(4c5ce5afd28f6d776f16cabbbbcc09769ff306b7))

	ROM_REGION(0x2000 , "rombank" , 0)
	ROM_LOAD("rom000.bin" , 0 , 0x2000 , CRC(e13b8ae3) SHA1(2374618d25d1a000ddb534ae4f55ebd98ce0fff3))

	ROM_REGION(0x400 , "chargen" , 0)
	ROM_LOAD("chrgen.bin" , 0 , 0x400 , CRC(9c402544) SHA1(32634fc73c1544aeeefda62ebb10349c5b40729f))

	ROM_REGION(0x400 , "prt_chargen" , 0)
	ROM_LOAD("prt_chrgen.bin" , 0 , 0x400 , CRC(abeaba27) SHA1(fbf6bdd5d96df6aa5963f8cdfdeb180402b1cc85))
ROM_END

// ************
//  hp86_state
// ************
class hp86_state : public hp80_base_state
{
public:
	hp86_state(const machine_config &mconfig, device_type type, const char *tag, bool has_int_keyb = false);

	// **** Constants of HP86 ****
	static constexpr unsigned MASTER_CLOCK  = 12260000;
	static constexpr unsigned VIDEO_MEM_SIZE    = 16384;
	static constexpr uint16_t VIDEO_ADDR_MASK   = VIDEO_MEM_SIZE - 1;
	static constexpr uint16_t VIDEO_ALPHA_N_END = 0x10e0;
	static constexpr uint16_t VIDEO_ALPHA_A_END = 0x3fc0;
	static constexpr uint16_t VIDEO_GRAPH_START = 0x10e0;
	// Time to read/write a byte in video memory (in master clock cycles) TBC
	static constexpr unsigned CRT_RW_TIME       = 24;
	// Duration of on/off states of run light (ms)
	static constexpr unsigned RULITE_ON_MS      = 373;
	static constexpr unsigned RULITE_OFF_MS     = 187;

	void hp86(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual void cpu_mem_map(address_map &map) override ATTR_COLD;
	virtual void rombank_mem_map(address_map &map) override ATTR_COLD;
	virtual void unmap_optroms(address_space &space) override;

private:
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void vblank_w(int state);
	attotime time_to_video_mem_availability() const;

	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<timer_device> m_vm_timer;
	required_device<ram_device> m_ram;
	output_finder<> m_run_light;
	required_device<timer_device> m_rulite_timer;

	// Character generator
	required_region_ptr<uint8_t> m_chargen;

	// Video
	bitmap_rgb32 m_bitmap;
	std::unique_ptr<uint8_t []> m_video_mem;
	uint16_t m_crt_sad = 0;
	uint16_t m_crt_bad = 0;
	uint8_t m_crt_sts = 0;
	uint8_t m_crt_byte = 0;
	bool m_crt_rdrq = false;

	// Extended RAM access
	uint32_t m_emc_ptr1 = 0;    // PTR1 (24 bits)
	uint32_t m_emc_ptr2 = 0;    // PTR2 (24 bits)
	uint8_t m_emc_disp = 0;     // Displacement (3 bits)
	bool m_emc_mult = false;        // Multibyte access
	uint8_t m_emc_mode = 0;     // Mode (3 bits)
	enum {
		  EMC_IDLE,
		  EMC_INDIRECT_1,
		  EMC_INDIRECT_2
	};
	int m_emc_state = 0;        // EMC indirect access state
	bool m_lmard = false;           // LMARD cycles in progress

	// Run light
	bool m_rulite = false;

	void crtsad_w(uint8_t data);
	void crtbad_w(uint8_t data);
	uint8_t crtsts_r();
	void crtsts_w(uint8_t data);
	uint8_t crtdat_r();
	void crtdat_w(uint8_t data);
	TIMER_DEVICE_CALLBACK_MEMBER(vm_timer);
	uint16_t get_video_limit() const;
	void rulite_w(uint8_t data);
	TIMER_DEVICE_CALLBACK_MEMBER(rulite_timer);
	uint8_t direct_ram_r(offs_t offset);
	void direct_ram_w(offs_t offset, uint8_t data);
	uint8_t emc_r(offs_t offset);
	void emc_w(offs_t offset, uint8_t data);
	uint32_t& get_ptr();
	void ptr12_decrement();
	void lma_cycle(int state);
	void opcode_cb(uint8_t opcode);
};

hp86_state::hp86_state(const machine_config &mconfig, device_type type, const char *tag, bool has_int_keyb)
	: hp80_base_state(mconfig , type , tag , has_int_keyb)
	, m_screen(*this , "screen")
	, m_palette(*this , "palette")
	, m_vm_timer(*this , "vm_timer")
	, m_ram(*this , "ram")
	, m_run_light(*this , "run_light")
	, m_rulite_timer(*this , "rulite_timer")
	, m_chargen(*this , "chargen")
{
}

void hp86_state::cpu_mem_map(address_map &map)
{
	hp80_base_state::cpu_mem_map(map);
	map(0x8000 , 0xfeff).rw(FUNC(hp86_state::direct_ram_r) , FUNC(hp86_state::direct_ram_w));
	map(0xffc0 , 0xffc0).w(FUNC(hp86_state::crtsad_w));
	map(0xffc1 , 0xffc1).w(FUNC(hp86_state::crtbad_w));
	map(0xffc2 , 0xffc2).rw(FUNC(hp86_state::crtsts_r) , FUNC(hp86_state::crtsts_w));
	map(0xffc3 , 0xffc3).rw(FUNC(hp86_state::crtdat_r) , FUNC(hp86_state::crtdat_w));
	map(0xffc4 , 0xffc4).w(FUNC(hp86_state::rulite_w));
	map(0xffc8 , 0xffcf).rw(FUNC(hp86_state::emc_r) , FUNC(hp86_state::emc_w));
}

void hp86_state::rombank_mem_map(address_map &map)
{
	hp80_base_state::rombank_mem_map(map);
	// rom001 (graphics)
	map(0x2000, 0x3fff).rom();
	// rom320 (mass memory)
	// rom321 (electronic disk)
	map(0x1a0000 , 0x1a3fff).rom();
}

void hp86_state::unmap_optroms(address_space &space)
{
	// OptROMs are in rombanks [02..CF] & [D2..FF]
	space.unmap_read(HP80_OPTROM_SIZE * 2 , HP80_OPTROM_SIZE * 0xd0 - 1);
	space.unmap_read(HP80_OPTROM_SIZE * 0xd2 , HP80_OPTROM_SIZE * 0x100 - 1);
}

void hp86_state::hp86(machine_config &config)
{
	hp80_base(config);

	m_cpu->opcode_cb().set(FUNC(hp86_state::opcode_cb));
	m_cpu->lma_cb().set(FUNC(hp86_state::lma_cycle));

	RAM(config , m_ram).set_default_size("128K");

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(MASTER_CLOCK , 784 , 0 , 640 , 261 , 0 , 240);
	m_screen->set_screen_update(FUNC(hp86_state::screen_update));
	m_screen->screen_vblank().set(FUNC(hp86_state::vblank_w));
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	config.set_default_layout(layout_hp86b);

	TIMER(config, m_vm_timer).configure_generic(FUNC(hp86_state::vm_timer));
	TIMER(config, m_rulite_timer).configure_generic(FUNC(hp86_state::rulite_timer));

	m_io_slots[ 0 ]->option_set("hpib" , HP82937_IO_CARD);

	SOFTWARE_LIST(config, "optrom_list").set_original("hp86_rom");
}

void hp86_state::machine_start()
{
	hp80_base_state::machine_start();

	m_run_light.resolve();

	m_screen->register_screen_bitmap(m_bitmap);
	m_video_mem = std::make_unique<uint8_t[]>(VIDEO_MEM_SIZE);

	save_pointer(NAME(m_video_mem) , VIDEO_MEM_SIZE);
	save_item(NAME(m_crt_sad));
	save_item(NAME(m_crt_bad));
	save_item(NAME(m_emc_ptr1));
	save_item(NAME(m_emc_ptr2));
	save_item(NAME(m_emc_disp));
	save_item(NAME(m_emc_mult));
	save_item(NAME(m_emc_mode));
	save_item(NAME(m_rulite));

	m_emc_ptr1 = 0;
	m_emc_ptr2 = 0;
}

void hp86_state::machine_reset()
{
	hp80_base_state::machine_reset();

	m_crt_sad = 0;
	m_crt_sts = 0x06;
	m_crt_rdrq = false;
	m_emc_state = EMC_IDLE;
	m_rulite = true;
	m_run_light = true;
	m_rulite_timer->reset();
}

uint32_t hp86_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
	return 0;
}

void hp86_state::vblank_w(int state)
{
	COPY_BIT(state , m_crt_sts , 4);
	if (state) {
		if (m_crt_sts & 0x06) {
			// Blank display
			m_bitmap.fill(rgb_t::black());
		} else {
			// Load palette for normal or inverse video
			if (BIT(m_crt_sts , 5)) {
				m_palette->set_pen_color(0 , rgb_t::green());
				m_palette->set_pen_color(1 , rgb_t::black());
			} else {
				m_palette->set_pen_color(1 , rgb_t::green());
				m_palette->set_pen_color(0 , rgb_t::black());
			}
			uint16_t limit = get_video_limit();
			if (BIT(m_crt_sts , 7)) {
				uint16_t video_ptr = VIDEO_GRAPH_START;
				unsigned dots_per_line;
				unsigned offset;
				if (BIT(m_crt_sts , 6)) {
					// GRAPH ALL mode
					dots_per_line = 544;
					offset = 48;
				} else {
					// GRAPH NORMAL mode
					dots_per_line = 400;
					offset = 120;
				}
				// Fill black bars on either side of the display
				m_bitmap.fill(m_palette->pen(0) , rectangle(0 , offset - 1 , 0 , 239));
				m_bitmap.fill(m_palette->pen(0) , rectangle(640 - offset , 639 , 0 , 239));

				for (unsigned y = 0; y < 240; y++) {
					for (unsigned x = offset; x < (dots_per_line + offset); x += 8) {
						uint8_t pixels = m_video_mem[ video_ptr ];
						if (++video_ptr >= limit) {
							video_ptr = 0;
						}
						m_bitmap.pix(y , x) = m_palette->pen(BIT(pixels , 7));
						m_bitmap.pix(y , x + 1) = m_palette->pen(BIT(pixels , 6));
						m_bitmap.pix(y , x + 2) = m_palette->pen(BIT(pixels , 5));
						m_bitmap.pix(y , x + 3) = m_palette->pen(BIT(pixels , 4));
						m_bitmap.pix(y , x + 4) = m_palette->pen(BIT(pixels , 3));
						m_bitmap.pix(y , x + 5) = m_palette->pen(BIT(pixels , 2));
						m_bitmap.pix(y , x + 6) = m_palette->pen(BIT(pixels , 1));
						m_bitmap.pix(y , x + 7) = m_palette->pen(BIT(pixels , 0));
					}
				}
			} else {
				unsigned rows;
				unsigned lines_per_row;
				if (BIT(m_crt_sts , 3)) {
					// 24 rows
					rows = 24;
					lines_per_row = 10;
				} else {
					// 16 rows
					rows = 16;
					lines_per_row = 15;
				}
				uint16_t video_ptr = m_crt_sad;
				for (unsigned row = 0; row < rows; row++) {
					for (unsigned col = 0; col < 640; col += 8) {
						uint8_t ch = m_video_mem[ video_ptr ];
						if (++video_ptr >= limit) {
							video_ptr = 0;
						}
						for (unsigned sub_row = 0; sub_row < lines_per_row; sub_row++) {
							uint8_t pixels;
							if (sub_row < 10) {
								pixels = m_chargen[ (ch & 0x7f) * 10 + sub_row ];
							} else {
								pixels = 0;
							}
							if (BIT(ch , 7)) {
								pixels = ~pixels;
							}
							unsigned y = row * lines_per_row + sub_row;
							m_bitmap.pix(y , col) = m_palette->pen(BIT(pixels , 7));
							m_bitmap.pix(y , col + 1) = m_palette->pen(BIT(pixels , 6));
							m_bitmap.pix(y , col + 2) = m_palette->pen(BIT(pixels , 5));
							m_bitmap.pix(y , col + 3) = m_palette->pen(BIT(pixels , 4));
							m_bitmap.pix(y , col + 4) = m_palette->pen(BIT(pixels , 3));
							m_bitmap.pix(y , col + 5) = m_palette->pen(BIT(pixels , 2));
							m_bitmap.pix(y , col + 6) = m_palette->pen(BIT(pixels , 1));
							m_bitmap.pix(y , col + 7) = m_palette->pen(BIT(pixels , 0));
						}
					}
				}
			}
		}
	}
}

attotime hp86_state::time_to_video_mem_availability() const
{
	if ((m_crt_sts & 0x06) != 0 || m_screen->vblank() || m_screen->hblank()) {
		// Blank video or vertical/horizontal retrace: immediate access
		return attotime::zero;
	} else {
		// In the active part, wait until next retrace
		return m_screen->time_until_pos(m_screen->vpos() , 640);
	}
}

void hp86_state::crtsad_w(uint8_t data)
{
	auto burst_idx = m_cpu->flatten_burst();
	if (burst_idx == 0) {
		m_crt_sad = (m_crt_sad & 0xff00) | data;
	} else if (burst_idx == 1) {
		m_crt_sad = (uint16_t(data) << 8) | (m_crt_sad & 0xff);
		m_crt_sad &= VIDEO_ADDR_MASK;
	}
}

void hp86_state::crtbad_w(uint8_t data)
{
	auto burst_idx = m_cpu->flatten_burst();
	if (burst_idx == 0) {
		m_crt_bad = (m_crt_bad & 0xff00) | data;
	} else if (burst_idx == 1) {
		m_crt_bad = (uint16_t(data) << 8) | (m_crt_bad & 0xff);
		m_crt_bad &= VIDEO_ADDR_MASK;
	}
}

uint8_t hp86_state::crtsts_r()
{
	return m_crt_sts;
}

void hp86_state::crtsts_w(uint8_t data)
{
	m_crt_sts = (m_crt_sts & 0x11) | (data & ~0x11);
	if (BIT(data , 0)) {
		// Read request
		BIT_SET(m_crt_sts , 0);
		m_crt_rdrq = true;
		attotime vm_av = time_to_video_mem_availability();
		m_vm_timer->adjust(vm_av + attotime::from_ticks(CRT_RW_TIME , MASTER_CLOCK));
	}
}

uint8_t hp86_state::crtdat_r()
{
	return m_crt_byte;
}

void hp86_state::crtdat_w(uint8_t data)
{
	m_crt_byte = data;
	BIT_SET(m_crt_sts , 0);
	m_crt_rdrq = false;
	attotime vm_av = time_to_video_mem_availability();
	m_vm_timer->adjust(vm_av + attotime::from_ticks(CRT_RW_TIME , MASTER_CLOCK));
}

TIMER_DEVICE_CALLBACK_MEMBER(hp86_state::vm_timer)
{
	if (m_crt_rdrq) {
		m_crt_rdrq = false;
		m_crt_byte = m_video_mem[ m_crt_bad ];
	} else {
		m_video_mem[ m_crt_bad ] = m_crt_byte;
	}
	BIT_CLR(m_crt_sts , 0);
	if (++m_crt_bad >= get_video_limit()) {
		m_crt_bad = 0;
	}
}

uint16_t hp86_state::get_video_limit() const
{
	if (BIT(m_crt_sts , 7)) {
		// Graphic mode
		return VIDEO_MEM_SIZE;
	} else if (BIT(m_crt_sts , 6)) {
		// ALPHA ALL mode
		return VIDEO_ALPHA_A_END;
	} else {
		// ALPHA NORMAL mode
		return VIDEO_ALPHA_N_END;
	}
}

void hp86_state::rulite_w(uint8_t data)
{
	bool new_rulite = !BIT(data , 0);

	if (m_rulite && !new_rulite) {
		m_run_light = false;
		m_rulite_timer->adjust(attotime::from_msec(RULITE_OFF_MS));
	} else if (!m_rulite && new_rulite) {
		m_run_light = true;
		m_rulite_timer->reset();
	}
	m_rulite = new_rulite;
}

TIMER_DEVICE_CALLBACK_MEMBER(hp86_state::rulite_timer)
{
	m_run_light = !m_run_light;
	m_rulite_timer->adjust(attotime::from_msec(m_run_light ? RULITE_ON_MS : RULITE_OFF_MS));
}

uint8_t hp86_state::direct_ram_r(offs_t offset)
{
	return m_ram->read(offset);
}

void hp86_state::direct_ram_w(offs_t offset, uint8_t data)
{
	m_ram->write(offset , data);
}

uint8_t hp86_state::emc_r(offs_t offset)
{
	auto idx = m_cpu->flatten_burst();
	uint8_t res = 0xff;

	if (m_emc_state == EMC_INDIRECT_2) {
		uint32_t& ptr = get_ptr();
		if (ptr >= 0x8000 && (ptr - 0x8000) < m_ram->size()) {
			res = m_ram->read(ptr - 0x8000);
		}
		LOG_EMC("EMC r @%06x=%02x\n" , ptr , res);
		ptr++;
	} else if (m_lmard) {
		m_emc_mode = uint8_t(offset);
		// During a LMARD pair, address 0xffc8 is returned to CPU and indirect mode is activated
		if (idx == 0) {
			res = 0xc8;
		} else if (idx == 1) {
			LOG_EMC("EMC access %u %06x\n" , m_emc_mode & 7 , get_ptr());
			m_emc_state = EMC_INDIRECT_1;
			if (BIT(m_emc_mode , 0)) {
				// Pre-decrement
				ptr12_decrement();
			}
		}
	} else {
		m_emc_mode = uint8_t(offset);
		// Read PTRx
		if (idx < 3) {
			res = uint8_t(get_ptr() >> (8 * idx));
		}
	}
	return res;
}

void hp86_state::emc_w(offs_t offset, uint8_t data)
{
	auto idx = m_cpu->flatten_burst();

	if (m_emc_state == EMC_INDIRECT_2) {
		uint32_t& ptr = get_ptr();
		LOG_EMC("EMC w @%06x=%02x\n" , ptr , data);
		if (ptr >= 0x8000 && (ptr - 0x8000) < m_ram->size()) {
			m_ram->write(ptr - 0x8000 , data);
		}
		ptr++;
	} else {
		m_emc_mode = uint8_t(offset);
		// Write PTRx
		if (idx < 3) {
			uint32_t& ptr = get_ptr();
			uint32_t mask = 0xffU << (8 * idx);
			ptr = (ptr & ~mask) | (uint32_t(data) << (8 * idx));
		}
	}
}

uint32_t& hp86_state::get_ptr()
{
	return BIT(m_emc_mode , 2) ? m_emc_ptr2 : m_emc_ptr1;
}

void hp86_state::ptr12_decrement()
{
	if (m_emc_mult) {
		get_ptr() -= m_emc_disp;
	} else {
		get_ptr()--;
	}
}

void hp86_state::lma_cycle(int state)
{
	m_lmard = state;
	if (m_emc_state == EMC_INDIRECT_1) {
		m_emc_state = EMC_INDIRECT_2;
	} else if (m_emc_state == EMC_INDIRECT_2) {
		LOG_EMC("EMC close %u %06x\n" , m_emc_mode & 7 , get_ptr());
		if (!BIT(m_emc_mode , 1)) {
			// In PTRx & PTRx- cases, bring the PTR back to start
			ptr12_decrement();
		}
		m_emc_state = EMC_IDLE;
	}
}

void hp86_state::opcode_cb(uint8_t opcode)
{
	// Intercept DRP instructions & load displacement
	if ((opcode & 0xc0) == 0x40) {
		if (BIT(opcode , 5)) {
			m_emc_disp = 8 - (opcode & 7);
		} else {
			m_emc_disp = 2 - (opcode & 1);
		}
	}

	m_emc_mult = BIT(opcode , 0);
}

static INPUT_PORTS_START(hp86)
	// Keyboard is arranged in a matrix of 10 rows and 8 columns. In addition there are 3 keys with
	// dedicated input lines: SHIFT, SHIFT LOCK & CONTROL.
	// A key on row "r"=[0..9] and column "c"=[0..7] is mapped to bit "b" of KEY"n" input, where
	// n = r / 4
	// b = (r % 4) * 8 + c
	PORT_START("KEY0")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_NAME("k6 k13")        // 0,0: k6 / k13
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_NAME("k5 k12")        // 0,1: k5 / k12
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_NAME("k4 k11")        // 0,2: k4 / k11
	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_NAME("k3 k10")        // 0,3: k3 / k10
	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_NAME("k2 k9")         // 0,4: k2 / k9
	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_NAME("k1 k8")         // 0,5: k1 / k8
	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("LABEL KEY")                                                        // 0,6: LABEL KEY
	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                 // 0,7: N/U
	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')                            // 1,0: 8
	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')                            // 1,1: 7
	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')                           // 1,2: 6
	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')                           // 1,3: 5
	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')                           // 1,4: 4
	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')                           // 1,5: 3
	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')                           // 1,6: 2
	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')                           // 1,7: 1
	PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')                           // 2,0: I
	PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')                           // 2,1: U
	PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')                           // 2,2: Y
	PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')                           // 2,3: T
	PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')                           // 2,4: R
	PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')                           // 2,5: E
	PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')                           // 2,6: W
	PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')                           // 2,7: Q
	PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')                           // 3,0: K
	PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')                           // 3,1: J
	PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')                           // 3,2: H
	PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')                           // 3,3: G
	PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')                           // 3,4: F
	PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')                           // 3,5: D
	PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')                           // 3,6: S
	PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')                           // 3,7: A

	PORT_START("KEY1")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')                            // 4,0: M
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')                            // 4,1: N
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')                            // 4,2: B
	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')                            // 4,3: V
	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')                            // 4,4: C
	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')                            // 4,5: X
	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')                            // 4,6: Z
	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')                                       // 4,7: Space
	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')                        // 5,0: ,
	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')                         // 5,1: .
	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')                       // 5,2: / ?
	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("PAUSE STEP")                                                      // 5,3: PAUSE / STEP
	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("RUN")                                                             // 5,4: RUN
	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD)) PORT_NAME("KP +") // 5,5: KP +
	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) PORT_NAME("KP -") // 5,6: KP -
	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                // 5,7: N/U
	PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l')                           // 6,0: L
	PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')                       // 6,1: ;
	PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')                      // 6,2: ' "
	PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("END LINE")                 // 6,3: END LINE
	PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("LIST P LST")                                                      // 6,4: LIST / P LST
	PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                // 6,5: N/U
	PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(ASTERISK)) PORT_NAME("KP *") // 6,6: KP *
	PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD)) PORT_NAME("KP /") // 6,7: KP /
	PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')                           // 7,0: O
	PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')                           // 7,1: P
	PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('(') PORT_CHAR('[')                   // 7,2: ( [
	PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(')') PORT_CHAR(']')                  // 7,3: ) ]
	PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("CONT TR/NORM")                                                    // 7,4: CONT / TR/NORM
	PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("E TEST")                                                          // 7,5: KP E / TEST
	PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME(") INIT")                                                          // 7,6: KP ) / INIT
	PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                // 7,7: N/U

	PORT_START("KEY2")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')                            // 8,0: 9
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')                            // 8,1: 0
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')                        // 8,2: - _
	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')                       // 8,3: = +
	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('\\') PORT_CHAR('|')                       // 8,4: \ |
	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)                                     // 8,5: BS
	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("( RESET")                                                          // 8,6: KP ( / RESET
	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("^ RESLT")                                                          // 8,7: KP ^ / RESLT
	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_NAME("k7 k14")        // 9,0: k7 / k14
	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("-LINE CLEAR")                                                      // 9,1: -LINE / CLEAR
	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_NAME("Up Home")      // 9,2: Up / Home
	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_NAME("Down A/G") // 9,3: Down / A/G
	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_NAME("Left I/R") // 9,4: LEFT / I/R
	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_NAME("Right -CHAR") // 9,5: RIGHT / -CHAR
	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN) PORT_NAME("ROLL")                                    // 9,6: ROLL
	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                // 9,7: n/u

	PORT_START("MODKEYS")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)                // Shift
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_NAME("Shift lock")   // Shift lock
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)              // Control
INPUT_PORTS_END

ROM_START(hp86b)
	ROM_REGION(0x6000 , "cpu" , 0)
	ROM_LOAD("romsys1.bin" , 0x0000 , 0x2000 , CRC(bfa473b8) SHA1(cc420742a5f03c466484a5063e0abcbc084bf298))
	ROM_LOAD("romsys2.bin" , 0x2000 , 0x2000 , CRC(2bc3ba4b) SHA1(760bef9c482f562677f80b18d6163a19ee7aea1c))
	ROM_LOAD("romsys3.bin" , 0x4000 , 0x2000 , CRC(86bf3b8b) SHA1(209c91b9b972ab514c600752e2e4af68f984612e))

	ROM_REGION(0x1a4000 , "rombank" , 0)
	ROM_LOAD("rom000.bin" , 0x0000 , 0x2000 , CRC(c3ca5c54) SHA1(2b291607de101c7206bfae9520a18f1009929e9b))
	ROM_LOAD("rom001.bin" , 0x2000 , 0x2000 , CRC(59a1616c) SHA1(e0fe840f9740bdb455fe1872869671f8712b7cff))
	ROM_LOAD("rom320.bin" , 0x1a0000 , 0x2000 , CRC(c921e2e4) SHA1(e37ac61364830cfa214e6d1b9942cc1cde6ad01f))
	ROM_LOAD("rom321.bin" , 0x1a2000 , 0x2000 , CRC(e6e5cc91) SHA1(67711de228cc48a78d04b13f0a1c91dc26f7e87c))

	ROM_REGION(0x500 , "chargen" , 0)
	ROM_LOAD("chrgen.bin" , 0 , 0x500 , CRC(e90fad22) SHA1(6b2ecef96906ead99cd688e54c507611747c8687))
ROM_END

// ****************
//  hp86_int_state
// ****************
class hp86_int_state : public hp86_state
{
public:
	hp86_int_state(const machine_config &mconfig, device_type type, const char *tag);

protected:
	virtual void rombank_mem_map(address_map &map) override ATTR_COLD;
	virtual void unmap_optroms(address_space &space) override;
};

hp86_int_state::hp86_int_state(const machine_config &mconfig, device_type type, const char *tag)
	: hp86_state(mconfig , type , tag , true)
{
}

void hp86_int_state::rombank_mem_map(address_map &map)
{
	hp86_state::rombank_mem_map(map);
	// rom030 (language)
	map(0x30000, 0x31fff).rom();
}

void hp86_int_state::unmap_optroms(address_space &space)
{
	LOG("hp86_int_state::unmap_optroms\n");
	// OptROMs are in rombanks [02..17] & [19..CF] & [D2..FF]
	space.unmap_read(HP80_OPTROM_SIZE * 2 , HP80_OPTROM_SIZE * 0x18 - 1);
	space.unmap_read(HP80_OPTROM_SIZE * 0x19 , HP80_OPTROM_SIZE * 0xd0 - 1);
	space.unmap_read(HP80_OPTROM_SIZE * 0xd2 , HP80_OPTROM_SIZE * 0x100 - 1);
}

static INPUT_PORTS_START(hp86_int)
	// Keyboard is arranged in a matrix of 11 rows and 8 columns. In addition there are 2 keys with
	// dedicated input lines: SHIFT & CONTROL.
	// A key on row "r"=[0..10] and column "c"=[0..7] is mapped to bit "b" of KEY"n" input, where
	// n = r / 4
	// b = (r % 4) * 8 + c
	PORT_START("KEY0")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')                            // 0,0: K
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')                            // 0,1: M
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')                            // 0,2: N
	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')                            // 0,3: B
	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')                            // 0,4: V
	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')                            // 0,5: Z
	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')                            // 0,6: X
	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')                            // 0,7: C
	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')                            // 1,0: 9
	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_NAME("k5 k12")        // 1,1: k5 / k12
	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_NAME("k4 k11")       // 1,2: k4 / k11
	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_NAME("k3 k10")       // 1,3: k3 / k10
	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_NAME("k2 k9")        // 1,4: k2 / k9
	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_NAME("Caps")                                // 1,5: Caps
	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("LABEL KEY")                                                       // 1,6: LABEL KEY
	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_NAME("k1 k8")        // 1,7: k1 / k8
	PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')                           // 2,0: I
	PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')                           // 2,1: J
	PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')                           // 2,2: H
	PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')                           // 2,3: G
	PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')                           // 2,4: F
	PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')                           // 2,5: A
	PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')                           // 2,6: S
	PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')                           // 2,7: D
	PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')                           // 3,0: 8
	PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')                           // 3,1: U
	PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')                           // 3,2: Y
	PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')                           // 3,3: T
	PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')                           // 3,4: R
	PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')                           // 3,5: Q
	PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')                           // 3,6: W
	PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')                           // 3,7: E

	PORT_START("KEY1")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')                            // 4,0: 0
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')                       // 4,1: = +
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('\\') PORT_CHAR('|')                       // 4,2: \ |
	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)                                     // 4,3: BS
	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("E TEST")                                                           // 4,4: KP E / TEST
	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("^ RESLT")                                                          // 4,5: KP ^ / RESLT
	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME(") INIT")                                                           // 4,6: KP ) / INIT
	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("( RESET")                                                          // 4,7: KP ( / RESET
	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')                            // 5,0: P
	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')                        // 5,1: ; :
	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')                      // 5,2: ' "
	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("END LINE")                 // 5,3: END LINE
	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))                     // 5,4: KP 4
	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(ASTERISK)) PORT_NAME("KP *") // 5,5: KP *
	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD))                     // 5,6: KP 6
	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))                     // 5,7: KP 5
	PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l')                           // 6,0: L
	PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')                        // 6,1: .
	PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')                       // 6,2: / ?
	PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("LIST P LST")                                                      // 6,3: LIST / P LST
	PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))                     // 6,4: KP 1
	PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) PORT_NAME("KP -") // 6,5: KP -
	PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))                     // 6,6: KP 3
	PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))                     // 6,7: KP 2
	PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')                           // 7,0: O
	PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')                           // 7,1: 7
	PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')                           // 7,2: 6
	PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')                           // 7,3: 5
	PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')                           // 7,4: 4
	PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')                           // 7,5: 1
	PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')                           // 7,6: 2
	PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')                           // 7,7: 3

	PORT_START("KEY2")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')                        // 8,0: - _
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('(') PORT_CHAR('[')                    // 8,1: ( [
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(')') PORT_CHAR(']')                   // 8,2: ) ]
	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("CONT TR/NORM")                                                     // 8,3: CONT / TR/NORM
	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))                      // 8,4: KP 7
	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD)) PORT_NAME("KP /") // 8,5: KP /
	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))                      // 8,6: KP 9
	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))                      // 8,7: KP 8
	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')                        // 9,0: ,
	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')                                       // 9,1: Space
	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("PAUSE STEP")                                                      // 9,2: PAUSE / STEP
	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("RUN")                                                             // 9,3: RUN
	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))                     // 9,4: KP 0
	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD)) PORT_NAME("KP +") // 9,5: KP +
	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA_PAD) PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD)) PORT_CHAR('<') PORT_NAME("KP ,") // 9,6: KP ,
	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD)) PORT_CHAR('>') PORT_NAME("KP .") // 9,7: KP .
	PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_NAME("k6 k13")       // 10,0: k6 / k13
	PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_NAME("k7 k14")       // 10,1: k7 / k14
	PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("-LINE CLEAR")                                                     // 10,2: -LINE / CLEAR
	PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_NAME("Up Home")      // 10,3: Up / Home
	PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_NAME("Down A/G") // 10,4: Down / A/G
	PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN) PORT_NAME("ROLL")                                    // 10,5: ROLL
	PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_NAME("Right -CHAR") // 10,6: RIGHT / -CHAR
	PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_NAME("Left I/R") // 10,7: LEFT / I/R

	PORT_START("MODKEYS")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)                // Shift
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)              // Control
INPUT_PORTS_END

static INPUT_PORTS_START(hp86_001)
	PORT_INCLUDE(hp86_int)

	PORT_START("LANGUAGE")
	PORT_DIPNAME(0x3f , 0 , "Language")
	PORT_DIPLOCATION("S2:7,6,5,4,3,2")
	PORT_DIPSETTING(0x00 , "English")
	PORT_DIPSETTING(0x01 , "Swedish/Finnish")
	PORT_DIPSETTING(0x02 , "Norwegian/Danish")
	PORT_DIPSETTING(0x06 , "Spanish")
INPUT_PORTS_END

static INPUT_PORTS_START(hp86_004)
	PORT_INCLUDE(hp86_int)

	PORT_START("LANGUAGE")
	PORT_DIPNAME(0x3f , 0 , "Language")
	PORT_DIPLOCATION("S2:7,6,5,4,3,2")
	PORT_DIPSETTING(0x00 , "English")
	PORT_DIPSETTING(0x04 , "German")
	PORT_DIPSETTING(0x08 , "French")
	PORT_DIPSETTING(0x09 , "Italian")
	PORT_DIPSETTING(0x0a , "Dutch")
	PORT_DIPSETTING(0x14 , "Swiss German")
	PORT_DIPSETTING(0x15 , "Swiss French")
INPUT_PORTS_END

ROM_START(hp86b_001)
	ROM_REGION(0x6000 , "cpu" , 0)
	ROM_LOAD("romsys1.bin" , 0x0000 , 0x2000 , CRC(bfa473b8) SHA1(cc420742a5f03c466484a5063e0abcbc084bf298))
	ROM_LOAD("romsys2.bin" , 0x2000 , 0x2000 , CRC(2bc3ba4b) SHA1(760bef9c482f562677f80b18d6163a19ee7aea1c))
	ROM_LOAD("romsys3.bin" , 0x4000 , 0x2000 , CRC(86bf3b8b) SHA1(209c91b9b972ab514c600752e2e4af68f984612e))

	ROM_REGION(0x1a4000 , "rombank" , 0)
	ROM_LOAD("rom000.bin" , 0x0000 , 0x2000 , CRC(c3ca5c54) SHA1(2b291607de101c7206bfae9520a18f1009929e9b))
	ROM_LOAD("rom001.bin" , 0x2000 , 0x2000 , CRC(59a1616c) SHA1(e0fe840f9740bdb455fe1872869671f8712b7cff))
	ROM_LOAD("rom030.bin" , 0x30000 , 0x2000 , CRC(14507bc0) SHA1(67ca5a15019bd7b2bfbf53bb8cfbe2ca20a6239c))
	ROM_LOAD("rom320.bin" , 0x1a0000 , 0x2000 , CRC(c921e2e4) SHA1(e37ac61364830cfa214e6d1b9942cc1cde6ad01f))
	ROM_LOAD("rom321.bin" , 0x1a2000 , 0x2000 , CRC(e6e5cc91) SHA1(67711de228cc48a78d04b13f0a1c91dc26f7e87c))

	ROM_REGION(0x500 , "chargen" , 0)
	ROM_LOAD("chrgen.bin" , 0 , 0x500 , CRC(a3d891c2) SHA1(df7c262b585e9394251640d0775474a76c199905))
ROM_END

ROM_START(hp86b_004)
	ROM_REGION(0x6000 , "cpu" , 0)
	ROM_LOAD("romsys1.bin" , 0x0000 , 0x2000 , CRC(bfa473b8) SHA1(cc420742a5f03c466484a5063e0abcbc084bf298))
	ROM_LOAD("romsys2.bin" , 0x2000 , 0x2000 , CRC(2bc3ba4b) SHA1(760bef9c482f562677f80b18d6163a19ee7aea1c))
	ROM_LOAD("romsys3.bin" , 0x4000 , 0x2000 , CRC(86bf3b8b) SHA1(209c91b9b972ab514c600752e2e4af68f984612e))

	ROM_REGION(0x1a4000 , "rombank" , 0)
	ROM_LOAD("rom000.bin" , 0x0000 , 0x2000 , CRC(c3ca5c54) SHA1(2b291607de101c7206bfae9520a18f1009929e9b))
	ROM_LOAD("rom001.bin" , 0x2000 , 0x2000 , CRC(59a1616c) SHA1(e0fe840f9740bdb455fe1872869671f8712b7cff))
	ROM_LOAD("rom030.bin" , 0x30000 , 0x2000 , CRC(14507bc0) SHA1(67ca5a15019bd7b2bfbf53bb8cfbe2ca20a6239c))
	ROM_LOAD("rom320.bin" , 0x1a0000 , 0x2000 , CRC(c921e2e4) SHA1(e37ac61364830cfa214e6d1b9942cc1cde6ad01f))
	ROM_LOAD("rom321.bin" , 0x1a2000 , 0x2000 , CRC(e6e5cc91) SHA1(67711de228cc48a78d04b13f0a1c91dc26f7e87c))

	ROM_REGION(0x500 , "chargen" , 0)
	ROM_LOAD("chrgen.bin" , 0 , 0x500 , CRC(c7d04292) SHA1(b86ed801ee9f7a57b259374b8a9810572cb03230))
ROM_END

} // anonymous namespace


COMP( 1980, hp85,      0,     0, hp85, hp85,     hp85_state,     empty_init, "HP", "HP 85", 0)
COMP( 1983, hp86b,     0,     0, hp86, hp86,     hp86_state,     empty_init, "HP", "HP 86B",0)
COMP( 1983, hp86b_001, hp86b, 0, hp86, hp86_001, hp86_int_state, empty_init, "HP", "HP 86B Opt 001",0)
COMP( 1983, hp86b_004, hp86b, 0, hp86, hp86_004, hp86_int_state, empty_init, "HP", "HP 86B Opt 004",0)



hp95lx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************

    Hewlett-Packard 95LX palmtop, code name Jaguar.

    NEC V20 CPU @ 5.37 MHz
    HP F1000-80054 "Hopper" ASIC
        Display controller
        Keyboard controler
        UART
        Interrupt controller (8259 core)
        Interval timer (8254 core)
        Real time clock
        PCMCIA 1.0 controller
        ADC (4 channels; used to measure battery voltages)
        DAC (1 channel; can be used as tone generator)
    512KB or 1MB of RAM
    1MB of BIOS ROM (banked)
        P/N 18-5301 ABD \\ HN62318BFC26
    LCD, 240x128 pixels (40x16 chars in MDA-compatible text mode)

    To do:
    - blue on green LCD palette
    - native keyboard
    - 1MB model
    - identify RTC core
    - variable refresh rate?
      When the AC adapter is plugged in, the LCD refresh rate is 73.14 Hz.
      When the AC adapter is not plugged in (ie, running off of batteries) the refresh rate is 56.8 Hz.
    - everything else

    Technical info:
    - http://web.archive.org/web/20071012040320/http://www.daniel-hertrich.de/download/95lx_devguide.zip
    - http://cd.textfiles.com/blackphilesii/PHILES/HP95/HP95DEV.ZIP

    Useful links:
    - https://hermocom.com/hplx/view-all-hp-palmtop-articles/41-95lx
    - ftp://netflora.demon.co.uk/main/TEXT/hp95lxfaq.txt
    - https://www.finseth.com/hpdata/hp95lx.php
        has keyboard layout
    - http://cfile3.uf.tistory.com/attach/152BF03E50054B2925840F
        Explorer's Guide to the HP 95LX
    - http://www.palmtoppaper.com/ptphtml/12/pt120055.htm
        Evolution of the HP Palmtops
    - http://web.archive.org/web/20150423014908/http://www.sp.uconn.edu/~mchem1/HPLX.shtml
        HPLX-L mailing list archive

    Software:
    - http://www.retroisle.com/others/hp95lx/software.php
    - http://www.mizj.com/
    - http://www.hp200lx.net/
    - http://www.nic.funet.fi/index/misc/hp95lx/Index
    - http://cd.textfiles.com/blackphilesii/PHILES/HP95/

***************************************************************************/


#include "emu.h"

#include "bus/isa/isa.h"
#include "bus/isa/isa_cards.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "cpu/nec/nec.h"
#include "machine/bankdev.h"
#include "machine/nvram.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/ram.h"
#include "sound/dac.h"

#include "screen.h"
#include "emupal.h"
#include "softlist.h"
#include "speaker.h"


#define LOG_KEYBOARD  (1U << 1)
#define LOG_DEBUG     (1U << 2)

//#define VERBOSE (LOG_GENERAL)
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"

#define LOGKBD(...) LOGMASKED(LOG_KEYBOARD, __VA_ARGS__)
#define LOGDBG(...) LOGMASKED(LOG_DEBUG, __VA_ARGS__)


namespace {

class hp95lx_state : public driver_device
{
public:
	hp95lx_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_bankdev_c000(*this, "bankdev_c000")
		, m_bankdev_d000(*this, "bankdev_d000")
		, m_bankdev_e000(*this, "bankdev_e000")
		, m_bankdev_e400(*this, "bankdev_e400")
		, m_bankdev_e800(*this, "bankdev_e800")
		, m_bankdev_ec00(*this, "bankdev_ec00")
		, m_ram(*this, RAM_TAG)
		, m_nvram2(*this, "nvram2")
		, m_nvram3(*this, "nvram3")
		, m_isabus(*this, "isa")
		, m_pic8259(*this, "pic8259")
		, m_pit8254(*this, "pit8254")
		, m_dac(*this, "dac")
		, m_screen(*this, "screen")
		, m_p_videoram(*this, "video")
		, m_p_chargen(*this, "gfx1")
	{ }

	void d300_w(offs_t offset, uint8_t data);
	uint8_t d300_r(offs_t offset);
	void e300_w(offs_t offset, uint8_t data);
	uint8_t e300_r(offs_t offset);
	void f300_w(offs_t offset, uint8_t data);
	uint8_t f300_r(offs_t offset);

	void hp95lx(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<address_map_bank_device> m_bankdev_c000;
	required_device<address_map_bank_device> m_bankdev_d000;
	required_device<address_map_bank_device> m_bankdev_e000;
	required_device<address_map_bank_device> m_bankdev_e400;
	required_device<address_map_bank_device> m_bankdev_e800;
	required_device<address_map_bank_device> m_bankdev_ec00;
	required_device<ram_device> m_ram;
	required_device<nvram_device> m_nvram2;
	optional_device<nvram_device> m_nvram3;
	required_device<isa8_device> m_isabus;
	required_device<pic8259_device> m_pic8259;
	required_device<pit8254_device> m_pit8254;
	required_device<dac_8bit_r2r_device> m_dac;
	required_device<screen_device> m_screen;

private:
	void hp95lx_palette(palette_device &palette) const;

	void keyboard_clock_w(int state);
	void keyboard_data_w(int state);
	uint8_t keyboard_r(offs_t offset);
	void keyboard_w(offs_t offset, uint8_t data);
	uint8_t video_r(offs_t offset);
	void video_w(offs_t offset, uint8_t data);
	void video_address_w(uint8_t data);
	uint8_t video_register_r();
	void video_register_w(uint8_t data);
	[[maybe_unused]] void debug_w(offs_t offset, uint8_t data);

	void hp95lx_io(address_map &map) ATTR_COLD;
	void hp95lx_map(address_map &map) ATTR_COLD;
	void hp95lx_romdos(address_map &map) ATTR_COLD;

	required_shared_ptr<u8> m_p_videoram;
	required_region_ptr<u8> m_p_chargen;

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	u8 m_mapper[32]{}, m_rtcram[2]{};

	// crtc
	u8 m_crtc[64]{}, m_cursor_start_ras = 0;
	u16 m_disp_start_addr = 0, m_cursor_addr = 0, m_window_start_addr = 0;
	int m_register_address_latch = 0;
	bool m_graphics_mode = false;

	// from pt68k4.cpp
	bool m_kclk = false;
	uint8_t m_kdata = 0;
	uint8_t m_scancode = 0;
	uint8_t m_kbdflag = 0;
	int m_kbit = 0;
};


void hp95lx_state::hp95lx_palette(palette_device &palette) const
{
	palette.set_pen_color(0, 0xa0, 0xa8, 0xa0);
	palette.set_pen_color(1, 0x30, 0x38, 0x10);
}

uint32_t hp95lx_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	if (m_graphics_mode)
	{
		for (int y = 0; y < 128; y++)
		{
			uint16_t *p = &bitmap.pix(y);
			int const offset = y * (240 / 8);

			for (int x = offset; x < offset + (240 / 8); x++)
			{
				uint16_t const gfx = m_p_videoram[x];

				for (int i = 7; i >= 0; i--)
				{
					*p++ = BIT(gfx, i);
				}
			}
		}
	}
	else
	{
		bool blink((m_screen->frame_number() % 10) > 4);

		// screen memory is normal MDA 80x25, but only a scrollable 40x16 window is displayed
		for (int y = 0; y < 128; y++)
		{
			uint16_t *p = &bitmap.pix(y);
			int const offset = (y / 8) * 160 + m_window_start_addr;

			for (int x = offset; x < offset + 80; x += 2)
			{
				uint8_t const chr = m_p_videoram[x];
				uint8_t const attr = m_p_videoram[x + 1];

				uint16_t gfx = m_p_chargen[(chr)*8 + (y % 8)];

				if ((x >> 1) == m_cursor_addr && blink && (y % 8) >= m_cursor_start_ras)
				{
					gfx = 0xff;
				}

				switch (attr)
				{
				case 0x00:
					gfx = 0;
					break;

				case 0x01:
					if (y % 8 == 7) gfx = 0xff;
					break;

				case 0x70:
					gfx ^= 0xff;
					break;
				}

				for (int i = 5; i >= 0; i--)
				{
					*p++ = BIT(gfx, i);
				}
			}
		}
	}

	return 0;
}


void hp95lx_state::machine_start()
{
	save_item(NAME(m_mapper));
	save_item(NAME(m_rtcram));
	save_item(NAME(m_crtc));
	save_item(NAME(m_graphics_mode));

	//
	save_item(NAME(m_kclk));
	save_item(NAME(m_kdata));
	save_item(NAME(m_scancode));
	save_item(NAME(m_kbdflag));
	save_item(NAME(m_kbit));

	memcpy(m_p_chargen, memregion("romdos")->base() + 0xffa6e, 0x0400);
	memcpy(m_p_chargen + 0x0400, memregion("romdos")->base() + 0xfb200, 0x0400);
}

void hp95lx_state::machine_reset()
{
	std::fill(std::begin(m_mapper), std::end(m_mapper), 0);
	std::fill(std::begin(m_rtcram), std::end(m_rtcram), 0);

	std::fill(std::begin(m_crtc), std::end(m_crtc), 0);
	m_graphics_mode = false;
	m_cursor_start_ras = 7;
	m_disp_start_addr = 0;
	m_cursor_addr = 0;

	m_kclk = true;
	m_kbit = 0;
	m_scancode = 0;
	m_kbdflag = 0;

	m_dac->write(0x7f);
}


void hp95lx_state::d300_w(offs_t offset, uint8_t data)
{
	LOG("%s: IO %04x <- %02x\n", machine().describe_context(), 0xd300 + offset, data);

	switch (offset)
	{
	case 0:
		m_window_start_addr = ((data & 0xff) << 0) | (m_window_start_addr & 0xff00);
		break;
	case 1:
		m_window_start_addr = ((data & 0x3f) << 8) | (m_window_start_addr & 0x00ff);
		break;

	case 2:
		m_graphics_mode = !BIT(data, 4);
		break;

	case 6:
	case 7:
		m_rtcram[offset - 6] = data;
		break;
	}
}

uint8_t hp95lx_state::d300_r(offs_t offset)
{
	uint8_t data = 0;

	switch (offset)
	{
	case 0:
		data = m_window_start_addr;
		break;

	case 1:
		data = m_window_start_addr >> 8;
		break;

	case 5:
		data = 0x2;
		break;

	case 6:
	case 7:
		data = m_rtcram[offset - 6];
		break;
	}

	LOG("%s: IO %04x == %02x\n", machine().describe_context(), 0xd300 + offset, data);

	return data;
}

void hp95lx_state::e300_w(offs_t offset, uint8_t data)
{
	LOG("%s: IO %04x <- %02x\n", machine().describe_context(), 0xe300 + offset, data);

	switch (offset)
	{
	case 1: // b1 = 'IR interrupt enable' (per IR.DOC)
		break;

	case 2: // interrupt control (per snd.c), 0: disable all, 127: enable all
		break;

	case 5: // DAC out (per snd.c)
		m_dac->write(data);
		break;

	case 9: // b1 = 'a2d power' (per snd.c)
		break;

	case 10: // IRFMAT register (per IR.DOC)
		break;

	case 11: // IR transmit/receive register (per IR.DOC)
		break;

	case 13: // reset?
		break;

	case 14: // keyboard output?
	case 15:
		break;
	}
}

uint8_t hp95lx_state::e300_r(offs_t offset)
{
	uint8_t data = 0;

	switch (offset)
	{
	case 2: // b6 = low battery? (FD97Eh)
		break;

	case 4: // Card Detect Register (per chap5.txt)
		data = 0x50; // memory card inserted
		break;

	case 7:
		data = 0x20;
		break;

	case 9: // FF889h.  b0 = ADC ready? (per snd2.c)
		break;

	case 10: // IRFMAT register (per IR.DOC)
		break;

	case 11: // IR transmit/receive register (per IR.DOC)
		break;

	case 14: // keyboard status?
	case 15:
		return data;
		break;
	}

	LOG("%s: IO %04x == %02x\n", machine().describe_context(), 0xe300 + offset, data);

	return data;
}

void hp95lx_state::f300_w(offs_t offset, uint8_t data)
{
	address_map_bank_device *mapper;
	const char *mapname;

	LOG("%s: IO %04x <- %02x\n", machine().describe_context(), 0xf300 + offset, data);

	if (offset >= sizeof(m_mapper)) return;

	m_mapper[offset] = data;

	switch (offset)
	{
	case 0x11: // E0000 16K
		mapper = m_bankdev_e000;
		mapname = "E0000";
		break;

	case 0x13: // E4000 16K
		mapper = m_bankdev_e400;
		mapname = "E4000";
		break;

	case 0x15: // E8000 16K
		mapper = m_bankdev_e800;
		mapname = "E8000";
		break;

	case 0x17: // EC000 16K
		mapper = m_bankdev_ec00;
		mapname = "EC000";
		break;

	case 0x18: // C0000 64K
		mapper = m_bankdev_c000;
		mapname = "C0000";
		break;

	case 0x19: // D0000 64K
		mapper = m_bankdev_d000;
		mapname = "D0000";
		break;

	default:
		LOG("MAPPER %02x unknown\n", offset);
		return;
	}

	switch (offset)
	{
	case 0x11: case 0x13: case 0x15: case 0x17:
		switch (data)
		{
		case 0: // internal
			if (m_mapper[offset - 1])
			{
				LOG("MAPPER %s <- %05X\n", mapname, (m_mapper[offset - 1] & 127) * 0x2000);
				mapper->set_bank((m_mapper[offset - 1] >> 1) & 63);
			}
			break;

		case 1: // unknown
		case 2:
			break;

		case 4: // card slot
			LOG("MAPPER %s <- %06X (card)\n", mapname, (m_mapper[offset - 1]) * 0x2000);
			mapper->set_bank(0x1000 + (m_mapper[offset - 1] >> 1));
			break;

		case 7: // XXX unmap
			LOG("MAPPER %s <- unmap\n", mapname);
			mapper->set_bank(0x400);
			break;
		}
		break;

	case 0x18: case 0x19:
		switch (data & 7)
		{
		case 0:
			if ((data & 0x87) == 0x80)
			{
				LOG("MAPPER %s <- %X0000\n", mapname, (data >> 3) & 15);
				mapper->set_bank((data >> 3) & 15);
			}
			break;

		case 4: // XXX
			break;

		case 7: // XXX unmap
			LOG("MAPPER %s <- unmap\n", mapname);
			mapper->set_bank(0x40);
			break;
		}
	}
}

uint8_t hp95lx_state::f300_r(offs_t offset)
{
	uint8_t data = 0;

	LOG("%s: IO %04x == %02x\n", machine().describe_context(), 0xf300 + offset, data);

	return data;
}

/* keyboard HLE -- adapted from pt68k4.cpp */

uint8_t hp95lx_state::keyboard_r(offs_t offset)
{
	if (offset == 0)
	{
		LOGKBD("kbd: read  %02X == %02X\n", offset + 0x60, m_scancode);
		m_pic8259->ir1_w(CLEAR_LINE);
		return m_scancode;
	}
	else
		return 0;
}

void hp95lx_state::keyboard_w(offs_t offset, uint8_t data)
{
	LOGKBD("kbd: write %02X <= %02X\n", offset + 0x60, data);
	m_pic8259->ir1_w(CLEAR_LINE);
}

void hp95lx_state::keyboard_clock_w(int state)
{
	LOGKBD("kbd: KCLK: %d kbit: %d\n", state ? 1 : 0, m_kbit);

	if ((state == ASSERT_LINE) && (!m_kclk))
	{
		if (m_kbit >= 1 && m_kbit <= 8)
		{
			m_scancode >>= 1;
			m_scancode |= m_kdata;
		}

		// stop bit?
		if (m_kbit == 9)
		{
			m_scancode >>= 1;
			m_scancode |= m_kdata;
			// FIXME: workaround, map F2..F10 to blue function keys
			if (m_scancode > 0x3B && m_scancode < 0x45) m_scancode += 0x36;
			LOGKBD("kbd: scancode %02x\n", m_scancode);
			m_kbit = 0;
			m_pic8259->ir1_w(ASSERT_LINE);
		}
		else
		{
			m_kbit++;
		}
	}

	m_kclk = (state == ASSERT_LINE) ? true : false;
}

void hp95lx_state::keyboard_data_w(int state)
{
	LOGKBD("kbd: KDATA: %d\n", state ? 1 : 0);
	m_kdata = (state == ASSERT_LINE) ? 0x80 : 0x00;
}

/* video HLE */

uint8_t hp95lx_state::video_register_r()
{
	uint8_t ret = 0;

	switch (m_register_address_latch)
	{
	case 0x0e:
		ret = (m_cursor_addr >> 8) & 0xff;
		break;

	case 0x0f:
		ret = (m_cursor_addr >> 0) & 0xff;
		break;

	/* all other registers are write only and return 0 */
	default:
		break;
	}

	return ret;
}

void hp95lx_state::video_register_w(uint8_t data)
{
	switch (m_register_address_latch)
	{
	case 0x0a:
		m_cursor_start_ras = data & 0x7f;
		break;

	case 0x0c:
		m_disp_start_addr = ((data & 0x3f) << 8) | (m_disp_start_addr & 0x00ff);
		break;

	case 0x0d:
		m_disp_start_addr = ((data & 0xff) << 0) | (m_disp_start_addr & 0xff00);
		break;

	case 0x0e:
		m_cursor_addr = ((data & 0x3f) << 8) | (m_cursor_addr & 0x00ff);
		break;

	case 0x0f:
		m_cursor_addr = ((data & 0xff) << 0) | (m_cursor_addr & 0xff00);
		m_pic8259->ir2_w(ASSERT_LINE);
		break;
	}
}

void hp95lx_state::video_address_w(uint8_t data)
{
	m_register_address_latch = data & 0x3f;
}

uint8_t hp95lx_state::video_r(offs_t offset)
{
	int data = 0xff;

	switch (offset)
	{
	case 1: case 3: case 5: case 7:
		data = video_register_r();
		break;

	case 10:
		data = 0xf0; // video_status_r(offset):
		break;
	}

	return data;
}

void hp95lx_state::video_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 0: case 2: case 4: case 6:
		video_address_w(data);
		break;

	case 1: case 3: case 5: case 7:
		video_register_w(data);
		break;
	}
}

void hp95lx_state::debug_w(offs_t offset, uint8_t data)
{
	LOGDBG("%11.6f %s debug: port %02X <= %02X\n", machine().time().as_double(), machine().describe_context(), offset + 0x90, data);
}


void hp95lx_state::hp95lx_romdos(address_map &map)
{
	map(0x0000000, 0x00fffff).rom().region("romdos", 0);
	map(0x4000000, 0x41fffff).ram().share("nvram3");
}

void hp95lx_state::hp95lx_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x7ffff).ram().share("nvram2");
	map(0xa0000, 0xaffff).rom().region("romdos", 0xe0000); // OS functions (DOS, COMMAND.COM)
	map(0xb0000, 0xb0fff).ram().share("video");
	map(0xc0000, 0xcffff).rw(m_bankdev_c000, FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0xd0000, 0xdffff).rw(m_bankdev_d000, FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0xe0000, 0xe3fff).rw(m_bankdev_e000, FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0xe4000, 0xe7fff).rw(m_bankdev_e400, FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0xe8000, 0xebfff).rw(m_bankdev_e800, FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0xec000, 0xeffff).rw(m_bankdev_ec00, FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0xf0000, 0xfffff).rom().region("romdos", 0xf0000); // BIOS ROM and SysMgr
}

void hp95lx_state::hp95lx_io(address_map &map)
{
	map.unmap_value_low();
	map(0x0020, 0x002f).rw("pic8259", FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x0040, 0x004f).rw("pit8254", FUNC(pit8254_device::read), FUNC(pit8254_device::write));
	map(0x0060, 0x0063).rw(FUNC(hp95lx_state::keyboard_r), FUNC(hp95lx_state::keyboard_w));
//  map(0x0090, 0x009f).w(FUNC(hp95lx_state::debug_w));
	map(0x03b0, 0x03bf).rw(FUNC(hp95lx_state::video_r), FUNC(hp95lx_state::video_w));
//  map(0x0070, 0x007f) RTC
	map(0xd300, 0xd30f).rw(FUNC(hp95lx_state::d300_r), FUNC(hp95lx_state::d300_w));
	map(0xe300, 0xe30f).rw(FUNC(hp95lx_state::e300_r), FUNC(hp95lx_state::e300_w));
	map(0xf300, 0xf31f).rw(FUNC(hp95lx_state::f300_r), FUNC(hp95lx_state::f300_w));
}


void hp95lx_state::hp95lx(machine_config &config)
{
	V20(config, m_maincpu, XTAL(5'370'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &hp95lx_state::hp95lx_map);
	m_maincpu->set_addrmap(AS_IO, &hp95lx_state::hp95lx_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259", FUNC(pic8259_device::inta_cb));

	ADDRESS_MAP_BANK(config, "bankdev_c000").set_map(&hp95lx_state::hp95lx_romdos).set_options(ENDIANNESS_LITTLE, 8, 32, 0x10000);
	ADDRESS_MAP_BANK(config, "bankdev_d000").set_map(&hp95lx_state::hp95lx_romdos).set_options(ENDIANNESS_LITTLE, 8, 32, 0x10000);
	ADDRESS_MAP_BANK(config, "bankdev_e000").set_map(&hp95lx_state::hp95lx_romdos).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);
	ADDRESS_MAP_BANK(config, "bankdev_e400").set_map(&hp95lx_state::hp95lx_romdos).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);
	ADDRESS_MAP_BANK(config, "bankdev_e800").set_map(&hp95lx_state::hp95lx_romdos).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);
	ADDRESS_MAP_BANK(config, "bankdev_ec00").set_map(&hp95lx_state::hp95lx_romdos).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);

	PIT8254(config, m_pit8254, 0);
	m_pit8254->set_clk<0>(XTAL(14'318'181) / 12); /* heartbeat IRQ */
	m_pit8254->out_handler<0>().set(m_pic8259, FUNC(pic8259_device::ir0_w));
	m_pit8254->set_clk<1>(XTAL(14'318'181) / 12); /* misc IRQ */
	m_pit8254->out_handler<1>().set(m_pic8259, FUNC(pic8259_device::ir2_w));

	PIC8259(config, m_pic8259, 0);
	m_pic8259->out_int_callback().set_inputline(m_maincpu, 0);

	ISA8(config, m_isabus, 0);
	m_isabus->set_memspace("maincpu", AS_PROGRAM);
	m_isabus->set_iospace("maincpu", AS_IO);

	ISA8_SLOT(config, "board0", 0, "isa", pc_isa8_cards, "com", true);

	pc_kbdc_device &pc_kbdc(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_KEYTRONIC_PC3270));
	pc_kbdc.out_clock_cb().set(FUNC(hp95lx_state::keyboard_clock_w));
	pc_kbdc.out_data_cb().set(FUNC(hp95lx_state::keyboard_data_w));

	NVRAM(config, "nvram2", nvram_device::DEFAULT_ALL_0); // RAM
	NVRAM(config, "nvram3", nvram_device::DEFAULT_ALL_0); // card slot

	SPEAKER(config, "speaker").front_center();
	DAC_8BIT_R2R(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.5); // unknown DAC

	SCREEN(config, m_screen, SCREEN_TYPE_LCD, rgb_t::white());
	m_screen->set_screen_update(FUNC(hp95lx_state::screen_update));
	m_screen->set_raw(XTAL(5'370'000) / 2, 300, 0, 240, 180, 0, 128);
	m_screen->set_palette("palette");

	PALETTE(config, "palette", FUNC(hp95lx_state::hp95lx_palette), 2);

	RAM(config, RAM_TAG).set_default_size("512K");
}


ROM_START( hp95lx )
	ROM_REGION(0x100000, "romdos", 0)
	// Version A ... ROM BIOS Ver 2.14 ... 04/02/91
	ROM_LOAD("18-5301.abd.bin", 0, 0x100000, CRC(18121c48) SHA1(c3bfb45cbbf4f57ae67fb5659da40f371d8e5c54))

	ROM_REGION(0x800,"gfx1", ROMREGION_ERASE00)
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT  CLASS          INIT         COMPANY             FULLNAME    FLAGS
COMP( 1991, hp95lx,  0,       0,      hp95lx,  0,     hp95lx_state,  empty_init,  "Hewlett-Packard",  "HP 95LX",  MACHINE_NOT_WORKING|MACHINE_NO_SOUND )



hp9825.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:F. Ulivi

// **************************
// Driver for HP 9825 systems
// **************************
//
// What's in:
// - Emulation of 9825A, 9825B, 9825T and 9831A systems
// - 4,8,12 or 16 kw (9825A & 9831A), 12 kw (9825B) or 31kw (9825T) of RAM
// - 12 kw of system ROM
// - Keyboard
// - Display & run light
// - DC100 tape drive
// - Printer (N/A on 9831A)
// - Beeper
// - Internal expansion ROMs
// - I/O expansion slots: 98032, 98034, 98035 & 98036 modules can be connected
// - For 9825T: the so-called SKOAL mechanism that transparently overlays RAM & ROM
//   in the same address space
// - External expansion ROMs
// - Configurable RAM size (9825A/9831A)
//
// Thanks to Dyke Shaffer for publishing (on https://groups.io/g/VintHPcom)
// the source code of 98217 mass memory ROM. The 98217.bin image was reconstructed
// by re-assembling the source code (this is the reason why it's marked as
// a BAD_DUMP). And thanks to Ansgar Kueckes for adapting his assembler to
// handle HP9825 source files.
// For what regards the 9825T, I'd like to thank again Dyke Shaffer for
// publishing a lot of internal HP docs about the SKOAL card. I recovered the
// content of SKOAL ROM from its printed & scanned dump.
// I'd also like to thank Paul Berger for providing the images of the optional
// mass storage ROMs (see http://www.hpmuseum.net).

#include "emu.h"

#include "hp9825_optrom.h"
#include "hp9825_tape.h"
#include "hp98x5_io_sys.h"

#include "bus/hp9845_io/hp9845_io.h"
#include "cpu/hphybrid/hphybrid.h"
#include "imagedev/bitbngr.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "sound/beep.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

// Debugging
#define VERBOSE 0
#include "logmacro.h"
#define LOG_DBG_MASK (LOG_GENERAL << 1)
#define LOG_DBG(...) LOGMASKED(LOG_DBG_MASK, __VA_ARGS__)

#include "hp9825.lh"

namespace {

// CPU clock (generated by a trimmered RC oscillator)
constexpr unsigned MAIN_CLOCK = 6000000;

// KDP chip clock
constexpr unsigned KDP_CLOCK = MAIN_CLOCK / 4;

// Peripheral Addresses (PA)
constexpr uint8_t KDP_PA = 0;
constexpr uint8_t TAPE_PA = 1;
constexpr uint8_t IO_SLOT_FIRST_PA = 2;
constexpr uint8_t IO_SLOT_LAST_PA = 15;

// KDP clocks to print 1 line of dots (~33 ms)
// This value is semi-guessed.
constexpr unsigned KDP_CLOCKS_PER_LINE = 50000;

// Printer constants
constexpr unsigned PRINT_MATRIX_H = 7;
constexpr unsigned PRINT_EMPTY_TOP = 1;
constexpr unsigned PRINT_EMPTY_BOT = 2;
constexpr unsigned PRINT_CELL_H = PRINT_MATRIX_H + PRINT_EMPTY_TOP + PRINT_EMPTY_BOT;
constexpr unsigned PRINT_MATRIX_W = 5;
constexpr unsigned PRINT_EMPTY_R = 2;
constexpr unsigned PRINT_CELL_W = PRINT_MATRIX_W + PRINT_EMPTY_R;
constexpr unsigned PRINT_COLUMNS = 16;
constexpr unsigned PRINT_PIXELS_ROW = PRINT_COLUMNS * PRINT_CELL_W - PRINT_EMPTY_R;
constexpr unsigned PRINT_WIN_ROWS = 6;
constexpr unsigned PRINT_WINDOWS = 4;
constexpr unsigned PRINT_WIN_H = PRINT_WIN_ROWS * PRINT_CELL_H;
constexpr unsigned PRINT_WIN_W = PRINT_PIXELS_ROW;
constexpr unsigned PRINT_OUT_H = PRINT_WIN_H * PRINT_WINDOWS;
constexpr unsigned PRINT_OUT_W = PRINT_PIXELS_ROW;
constexpr uint32_t PRINT_BG = 0xffffff; // White
constexpr uint32_t PRINT_FG = 0x2d1881; // Blue-violet

// Beeper constants
// Values come from R/C values on schematics
constexpr unsigned BEEPER_FREQ = 1400;
constexpr unsigned BEEPER_MS = 22;

// Bit manipulation
template<typename T> constexpr T BIT_MASK(unsigned n)
{
	return (T)1U << n;
}

template<typename T> void BIT_CLR(T& w , unsigned n)
{
	w &= ~BIT_MASK<T>(n);
}

template<typename T> void BIT_SET(T& w , unsigned n)
{
	w |= BIT_MASK<T>(n);
}


// +--------------+
// | hp98xx_state |
// +--------------+
//        ↑
//        +-------------------+
//        ↑                   ↑
// +--------------+   +--------------+
// | hp9825_state |   | hp9831_state |
// +--------------+   +--------------+
//        ↑
//        +-------------------+------------------+
//        ↑                   ↑                  ↑
// +---------------+  +---------------+  +---------------+
// | hp9825a_state |  | hp9825b_state |  | hp9825t_state |
// +---------------+  +---------------+  +---------------+
class hp98xx_state : public driver_device
{
public:
	hp98xx_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this , "cpu")
		, m_rom_drawers(*this , "drawer%u" , 0U)
		, m_io_sys(*this , "io_sys")
		, m_cursor_timer(*this , "cursor_timer")
		, m_tape(*this , "tape")
		, m_io_key(*this , "KEY%u" , 0)
		, m_shift_key(*this , "KEY_SHIFT")
		, m_prt_timer(*this , "prt_timer")
		, m_beeper(*this , "beeper")
		, m_beep_timer(*this , "beep_timer")
		, m_io_slot(*this, "slot%u", 0U)
		, m_display(*this , "char_%u_%u" , 0U , 0U)
		, m_run_light(*this , "run_light")
		, m_shift_lock(*this , "shift_lock")
		, m_tape_led(*this , "tape_led")
		, m_cassette(*this , "cassette")
	{
	}

	DECLARE_INPUT_CHANGED_MEMBER(kb_changed);

protected:
	void hp98xx_base(machine_config &config);

	virtual void machine_start() override ATTR_COLD;
	virtual void device_reset() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void print_line();

	required_device<hp_09825_67907_cpu_device> m_cpu;
	required_device_array<hp9825_optrom_device , 4> m_rom_drawers;

	// Printer
	uint8_t m_printer_mem[ PRINT_COLUMNS ];
	uint8_t m_printer_idx;
	unsigned m_printer_line;    // 0: printer idle, 1..10: line being printed

private:
	required_device<hp98x5_io_sys_device> m_io_sys;
	required_device<timer_device> m_cursor_timer;
	required_device<hp9825_tape_device> m_tape;
	required_ioport_array<4> m_io_key;
	required_ioport m_shift_key;
	required_device<timer_device> m_prt_timer;
	required_device<beep_device> m_beeper;
	required_device<timer_device> m_beep_timer;
	required_device_array<hp9845_io_slot_device , 3> m_io_slot;
	output_finder<32 , 7> m_display;
	output_finder<> m_run_light;
	output_finder<> m_shift_lock;
	output_finder<> m_tape_led;
	output_finder<> m_cassette;

	bool m_display_on;
	uint8_t m_display_mem[ 32 ];
	uint8_t m_display_idx;
	bool m_rpl_cursor;
	bool m_cursor_blink;
	bool m_any_cursor;
	uint8_t m_scancode;
	bool m_key_pressed;
	bool m_autorepeating;
	unsigned m_autorepeat_cnt;
	// SC of slots
	int m_slot_sc[ 3 ];

	void cpu_io_map(address_map &map) ATTR_COLD;

	uint16_t kb_scancode_r();
	void disp_w(uint16_t data);
	uint16_t kdp_status_r();
	void kdp_control_w(uint16_t data);
	void printer_w(uint16_t data);

	void update_display();
	TIMER_DEVICE_CALLBACK_MEMBER(cursor_blink);
	void kb_scan_ioport(ioport_value pressed , ioport_port &port , unsigned idx_base , int& max_seq_len , unsigned& max_seq_idx);
	TIMER_DEVICE_CALLBACK_MEMBER(kb_scan);

	TIMER_DEVICE_CALLBACK_MEMBER(prt_timer);

	TIMER_DEVICE_CALLBACK_MEMBER(beep_timer);

	// Slot handling
	void set_irq_slot(unsigned slot , int state);
	void set_sts_slot(unsigned slot , int state);
	void set_flg_slot(unsigned slot , int state);
	void set_dmar_slot(unsigned slot , int state);
};

void hp98xx_state::machine_start()
{
	m_display.resolve();
	m_run_light.resolve();
	m_shift_lock.resolve();
	m_tape_led.resolve();
	m_cassette.resolve();

	save_item(NAME(m_display_on));
	save_item(NAME(m_display_mem));
	save_item(NAME(m_display_idx));
	save_item(NAME(m_scancode));
}

void hp98xx_state::device_reset()
{
	// First, unmap every r/w handler in 1..12 select codes
	for (unsigned sc = IO_SLOT_FIRST_PA; sc < (IO_SLOT_LAST_PA + 1); sc++) {
		m_cpu->space(AS_IO).unmap_readwrite(sc * 4 , sc * 4 + 3);
	}

	// Then, set r/w handlers of all installed I/O cards
	int sc;
	read16m_delegate rhandler(*this);
	write16m_delegate whandler(*this);
	for (unsigned i = 0; i < 3; i++) {
		if ((sc = m_io_slot[ i ]->get_rw_handlers(rhandler , whandler)) >= 0) {
			logerror("Install R/W handlers for slot %u @ SC = %d\n", i, sc);
			m_cpu->space(AS_IO).install_readwrite_handler(sc * 4 , sc * 4 + 3 , rhandler , whandler);
		}
		m_slot_sc[ i ] = sc;
	}
}

void hp98xx_state::machine_reset()
{
	m_display_on = false;
	m_display_idx = 0;
	m_rpl_cursor = false;
	m_cursor_timer->reset();
	m_cursor_blink = true;
	update_display();
	m_scancode = 0;
	m_key_pressed = false;
	m_autorepeating = false;
	m_autorepeat_cnt = 0;
	m_run_light = false;
	m_shift_lock = false;
	m_printer_idx = 0;
	m_printer_line = 0;
	m_prt_timer->reset();
	m_beeper->set_state(0);
	m_beep_timer->reset();
}

void hp98xx_state::print_line()
{
}

void hp98xx_state::cpu_io_map(address_map &map)
{
	map.unmap_value_low();
	map(HP_MAKE_IOADDR(KDP_PA , 0) , HP_MAKE_IOADDR(KDP_PA , 0)).rw(FUNC(hp98xx_state::kb_scancode_r) , FUNC(hp98xx_state::disp_w));
	map(HP_MAKE_IOADDR(KDP_PA , 1) , HP_MAKE_IOADDR(KDP_PA , 1)).rw(FUNC(hp98xx_state::kdp_status_r) , FUNC(hp98xx_state::kdp_control_w));
	map(HP_MAKE_IOADDR(KDP_PA , 2) , HP_MAKE_IOADDR(KDP_PA , 2)).w(FUNC(hp98xx_state::printer_w));
	map(HP_MAKE_IOADDR(TAPE_PA , 0) , HP_MAKE_IOADDR(TAPE_PA , 3)).rw(m_tape , FUNC(hp9825_tape_device::tape_r) , FUNC(hp9825_tape_device::tape_w));
}

uint16_t hp98xx_state::kb_scancode_r()
{
	uint8_t res = m_scancode;
	if (BIT(m_shift_key->read() , 0) || m_shift_lock) {
		BIT_SET(res , 7);
	}
	m_io_sys->set_irq(KDP_PA , false);
	return res;
}

void hp98xx_state::disp_w(uint16_t data)
{
	if (m_display_on) {
		m_display_on = false;
		m_cursor_timer->reset();
		m_cursor_blink = true;
		m_display_idx = 0;
		update_display();
	}
	m_display_mem[ m_display_idx++ ] = uint8_t(data);
}

uint16_t hp98xx_state::kdp_status_r()
{
	uint16_t res = 0;
	if (m_io_sys->is_irq_pending(KDP_PA)) {
		BIT_SET(res , 4);
	}
	if (!BIT(m_shift_key->read() , 2)) {
		BIT_SET(res , 3);
	}
	if (m_printer_line) {
		BIT_SET(res , 2);
	}

	return res;
}

void hp98xx_state::kdp_control_w(uint16_t data)
{
	bool regen_display = false;
	if (BIT(data , 1) && !m_display_on) {
		m_display_on = true;
		// Cursor should blink at 2^-19 the KDP clock
		attotime cursor_half_period{ attotime::from_ticks(262144 , KDP_CLOCK) };
		m_cursor_timer->adjust(cursor_half_period , 0 , cursor_half_period);
		regen_display = true;
	}
	if (BIT(data , 6) && !m_rpl_cursor) {
		m_rpl_cursor = true;
		regen_display = true;
	}
	if (BIT(data , 5) && m_rpl_cursor) {
		m_rpl_cursor = false;
		regen_display = true;
	}
	if (BIT(data , 4)) {
		if (BIT(data , 3)) {
			m_run_light = !m_run_light;
		} else {
			m_run_light = false;
		}
	} else if (BIT(data , 3)) {
		m_run_light = true;
	}
	if (BIT(data , 0) && m_printer_line == 0) {
		// Start printing
		m_printer_idx = 0;
		m_printer_line++;
		m_prt_timer->adjust(attotime::from_ticks(KDP_CLOCKS_PER_LINE , KDP_CLOCK));
	}
	if (BIT(data , 2)) {
		// Start beeper
		m_beeper->set_state(1);
		m_beep_timer->adjust(attotime::from_msec(BEEPER_MS));
	}
	if (regen_display) {
		update_display();
	}
}

void hp98xx_state::printer_w(uint16_t data)
{
	m_printer_mem[ m_printer_idx ] = uint8_t(data);
	m_printer_idx = (m_printer_idx + 1) % PRINT_COLUMNS;
}

// The character generator was reverse engineered from images of printer & display test patterns.
// It is not guaranteed to be pixel-accurate though it looks quite close to the original.
static const uint8_t chargen[ 128 ][ 5 ] = {
	{ 0x08,0x1c,0x3e,0x7f,0x00 }, // 00
	{ 0x30,0x48,0x45,0x40,0x30 }, // 01
	{ 0x45,0x29,0x11,0x29,0x45 }, // 02
	{ 0x7d,0x09,0x11,0x21,0x7d }, // 03
	{ 0x38,0x44,0x44,0x38,0x44 }, // 04
	{ 0x7c,0x2a,0x4a,0x4a,0x34 }, // 05
	{ 0x7f,0x01,0x01,0x01,0x03 }, // 06
	{ 0x7d,0x09,0x05,0x05,0x79 }, // 07
	{ 0x60,0x58,0x46,0x58,0x60 }, // 08
	{ 0x38,0x44,0x44,0x3c,0x04 }, // 09
	{ 0x10,0x20,0x7f,0x20,0x10 }, // 0a
	{ 0x62,0x14,0x08,0x10,0x60 }, // 0b
	{ 0x40,0x3c,0x20,0x20,0x1c }, // 0c
	{ 0x08,0x1c,0x2a,0x08,0x08 }, // 0d
	{ 0x10,0x08,0x78,0x08,0x04 }, // 0e
	{ 0x08,0x55,0x7f,0x55,0x08 }, // 0f
	{ 0x3e,0x49,0x49,0x49,0x3e }, // 10
	{ 0x5e,0x61,0x01,0x61,0x5e }, // 11
	{ 0x30,0x4a,0x4d,0x49,0x30 }, // 12
	{ 0x78,0x14,0x15,0x14,0x78 }, // 13
	{ 0x38,0x44,0x45,0x3c,0x40 }, // 14
	{ 0x78,0x15,0x14,0x15,0x78 }, // 15
	{ 0x38,0x45,0x44,0x3d,0x40 }, // 16
	{ 0x3c,0x43,0x42,0x43,0x3c }, // 17
	{ 0x38,0x45,0x44,0x45,0x38 }, // 18
	{ 0x3e,0x41,0x40,0x41,0x3e }, // 19
	{ 0x3c,0x41,0x40,0x41,0x3c }, // 1a
	{ 0x7e,0x09,0x7f,0x49,0x49 }, // 1b
	{ 0x38,0x44,0x38,0x54,0x58 }, // 1c
	{ 0x12,0x19,0x15,0x12,0x00 }, // 1d
	{ 0x48,0x7e,0x49,0x41,0x42 }, // 1e
	{ 0x55,0x2a,0x55,0x2a,0x55 }, // 1f
	{ 0x00,0x00,0x00,0x00,0x00 }, // 20
	{ 0x00,0x5f,0x00,0x00,0x00 }, // 21
	{ 0x00,0x03,0x00,0x03,0x00 }, // 22
	{ 0x14,0x7f,0x14,0x7f,0x14 }, // 23
	{ 0x24,0x2a,0x7f,0x2a,0x12 }, // 24
	{ 0x23,0x13,0x08,0x64,0x62 }, // 25
	{ 0x36,0x49,0x56,0x20,0x50 }, // 26
	{ 0x00,0x0b,0x07,0x00,0x00 }, // 27
	{ 0x00,0x00,0x3e,0x41,0x00 }, // 28
	{ 0x00,0x41,0x3e,0x00,0x00 }, // 29
	{ 0x08,0x2a,0x1c,0x2a,0x08 }, // 2a
	{ 0x08,0x08,0x3e,0x08,0x08 }, // 2b
	{ 0x00,0x58,0x38,0x00,0x00 }, // 2c
	{ 0x08,0x08,0x08,0x08,0x08 }, // 2d
	{ 0x00,0x60,0x60,0x00,0x00 }, // 2e
	{ 0x20,0x10,0x08,0x04,0x02 }, // 2f
	{ 0x3e,0x51,0x49,0x45,0x3e }, // 30
	{ 0x00,0x42,0x7f,0x40,0x00 }, // 31
	{ 0x62,0x51,0x49,0x49,0x46 }, // 32
	{ 0x22,0x41,0x49,0x49,0x36 }, // 33
	{ 0x18,0x14,0x12,0x7f,0x10 }, // 34
	{ 0x27,0x45,0x45,0x45,0x39 }, // 35
	{ 0x3c,0x4a,0x49,0x49,0x30 }, // 36
	{ 0x01,0x71,0x09,0x05,0x03 }, // 37
	{ 0x36,0x49,0x49,0x49,0x36 }, // 38
	{ 0x06,0x49,0x49,0x29,0x1e }, // 39
	{ 0x00,0x36,0x36,0x00,0x00 }, // 3a
	{ 0x00,0x5b,0x3b,0x00,0x00 }, // 3b
	{ 0x00,0x08,0x14,0x22,0x41 }, // 3c
	{ 0x14,0x14,0x14,0x14,0x14 }, // 3d
	{ 0x41,0x22,0x14,0x08,0x00 }, // 3e
	{ 0x06,0x01,0x51,0x09,0x06 }, // 3f
	{ 0x3e,0x41,0x5d,0x55,0x1e }, // 40
	{ 0x7e,0x09,0x09,0x09,0x7e }, // 41
	{ 0x7f,0x49,0x49,0x49,0x36 }, // 42
	{ 0x3e,0x41,0x41,0x41,0x22 }, // 43
	{ 0x7f,0x41,0x41,0x41,0x3e }, // 44
	{ 0x7f,0x49,0x49,0x49,0x41 }, // 45
	{ 0x7f,0x09,0x09,0x09,0x01 }, // 46
	{ 0x3e,0x41,0x41,0x51,0x72 }, // 47
	{ 0x7f,0x08,0x08,0x08,0x7f }, // 48
	{ 0x00,0x41,0x7f,0x41,0x00 }, // 49
	{ 0x20,0x40,0x40,0x40,0x3f }, // 4a
	{ 0x7f,0x08,0x14,0x22,0x41 }, // 4b
	{ 0x7f,0x40,0x40,0x40,0x40 }, // 4c
	{ 0x7f,0x02,0x0c,0x02,0x7f }, // 4d
	{ 0x7f,0x04,0x08,0x10,0x7f }, // 4e
	{ 0x3e,0x41,0x41,0x41,0x3e }, // 4f
	{ 0x7f,0x09,0x09,0x09,0x06 }, // 50
	{ 0x3e,0x41,0x51,0x21,0x5e }, // 51
	{ 0x7f,0x09,0x19,0x29,0x46 }, // 52
	{ 0x26,0x49,0x49,0x49,0x32 }, // 53
	{ 0x01,0x01,0x7f,0x01,0x01 }, // 54
	{ 0x3f,0x40,0x40,0x40,0x3f }, // 55
	{ 0x07,0x18,0x60,0x18,0x07 }, // 56
	{ 0x7f,0x20,0x10,0x20,0x7f }, // 57
	{ 0x63,0x14,0x08,0x14,0x63 }, // 58
	{ 0x03,0x04,0x78,0x04,0x03 }, // 59
	{ 0x61,0x51,0x49,0x45,0x43 }, // 5a
	{ 0x00,0x00,0x7f,0x41,0x41 }, // 5b
	{ 0x20,0x7f,0x01,0x01,0x01 }, // 5c
	{ 0x41,0x41,0x7f,0x00,0x00 }, // 5d
	{ 0x04,0x02,0x7f,0x02,0x04 }, // 5e
	{ 0x40,0x40,0x40,0x40,0x40 }, // 5f
	{ 0x00,0x07,0x0b,0x00,0x00 }, // 60
	{ 0x38,0x44,0x44,0x3c,0x40 }, // 61
	{ 0x7f,0x48,0x44,0x44,0x38 }, // 62
	{ 0x38,0x44,0x44,0x44,0x20 }, // 63
	{ 0x38,0x44,0x44,0x48,0x7f }, // 64
	{ 0x38,0x54,0x54,0x54,0x08 }, // 65
	{ 0x08,0x7e,0x09,0x02,0x00 }, // 66
	{ 0x08,0x14,0x54,0x54,0x3c }, // 67
	{ 0x7f,0x08,0x04,0x04,0x78 }, // 68
	{ 0x00,0x44,0x7d,0x40,0x00 }, // 69
	{ 0x20,0x40,0x44,0x3d,0x00 }, // 6a
	{ 0x7f,0x10,0x28,0x44,0x00 }, // 6b
	{ 0x00,0x41,0x7f,0x40,0x00 }, // 6c
	{ 0x78,0x04,0x18,0x04,0x78 }, // 6d
	{ 0x7c,0x08,0x04,0x04,0x78 }, // 6e
	{ 0x38,0x44,0x44,0x44,0x38 }, // 6f
	{ 0x7c,0x14,0x24,0x24,0x18 }, // 70
	{ 0x18,0x24,0x14,0x7c,0x40 }, // 71
	{ 0x7c,0x08,0x04,0x04,0x00 }, // 72
	{ 0x48,0x54,0x54,0x54,0x20 }, // 73
	{ 0x04,0x3e,0x44,0x20,0x00 }, // 74
	{ 0x3c,0x40,0x40,0x20,0x7c }, // 75
	{ 0x1c,0x20,0x40,0x20,0x1c }, // 76
	{ 0x3c,0x40,0x30,0x40,0x3c }, // 77
	{ 0x44,0x28,0x10,0x28,0x44 }, // 78
	{ 0x04,0x48,0x30,0x08,0x04 }, // 79
	{ 0x44,0x64,0x54,0x4c,0x44 }, // 7a
	{ 0x08,0x7c,0x04,0x7c,0x02 }, // 7b
	{ 0x00,0x00,0x7f,0x00,0x00 }, // 7c
	{ 0x08,0x08,0x2a,0x1c,0x08 }, // 7d
	{ 0x41,0x63,0x55,0x49,0x63 }, // 7e
	{ 0x7f,0x08,0x08,0x08,0x08 }  // 7f
};

void hp98xx_state::update_display()
{
	m_any_cursor = false;
	for (unsigned i = 0; i < 32; ++i) {
		bool cursor_here = BIT(m_display_mem[ i ] , 7);
		if (cursor_here) {
			m_any_cursor = true;
		}
		bool show_cursor = m_cursor_blink && cursor_here;
		uint8_t char_code = m_display_mem[ i ] & 0x7f;
		for (unsigned j = 0; j < 7; ++j) {
			uint8_t five_dots = 0;
			if (m_display_on) {
				for (unsigned col = 0; col < 5; col++) {
					uint8_t char_col;
					if (show_cursor) {
						if (m_rpl_cursor) {
							// Replace cursor: all pixels lit
							char_col = ~0;
						} else {
							// Insert cursor: character code 0
							char_col = chargen[ 0 ][ col ];
						}
					} else {
						char_col = chargen[ char_code ][ col ];
					}
					if (BIT(char_col , j)) {
						BIT_SET(five_dots , col);
					}
				}
			}
			m_display[ i ][ j ] = five_dots;
		}
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(hp98xx_state::cursor_blink)
{
	m_cursor_blink = !m_cursor_blink;
	if (m_any_cursor) {
		update_display();
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(hp98xx_state::kb_scan)
{
	ioport_value input[ 4 ]
	{ m_io_key[ 0 ]->read(),
			m_io_key[ 1 ]->read(),
			m_io_key[ 2 ]->read(),
			m_io_key[ 3 ]->read()
			};

	if (m_key_pressed) {
		// Still pressed ?
		m_key_pressed = BIT(input[ m_scancode / 32 ] , m_scancode % 32);
	} else {
		int max_seq_len = -1;
		unsigned max_seq_idx = 0;
		for (unsigned i = 0; i < 4; i++) {
			kb_scan_ioport(input[ i ] , *m_io_key[ i ] , i << 5 , max_seq_len , max_seq_idx);
		}
		if (max_seq_len >= 0) {
			m_scancode = max_seq_idx;
			m_key_pressed = true;
			m_io_sys->set_irq(KDP_PA , true);
		}
	}

	if (m_key_pressed) {
		auto prev_cnt = m_autorepeat_cnt;
		m_autorepeat_cnt++;
		// Auto-repeat initial delay & frequency are entirely guessed..
		if (BIT(m_autorepeat_cnt , 5)) {
			// Initial delay passed
			m_autorepeating = true;
		}
		if (m_autorepeating && BIT(~prev_cnt & m_autorepeat_cnt , 3)) {
			// Repeat key every time bit 3 of autorepeat counter goes 0->1
			m_io_sys->set_irq(KDP_PA , true);
		}
	} else {
		m_autorepeating = false;
		m_autorepeat_cnt = 0;
	}
}

INPUT_CHANGED_MEMBER(hp98xx_state::kb_changed)
{
	switch (param) {
	case 0:
		// Left/right shift
		if (newval && !BIT(m_shift_key->read() , 1)) {
			m_shift_lock = false;
		}
		break;

	case 1:
		// Shift lock
		if (newval) {
			m_shift_lock = true;
		} else if (BIT(m_shift_key->read() , 0)) {
			m_shift_lock = false;
		}
		break;

	case 2:
		// Reset
		if (newval) {
			machine().schedule_soft_reset();
		}
		break;
	}
}

void hp98xx_state::kb_scan_ioport(ioport_value pressed , ioport_port &port , unsigned idx_base , int& max_seq_len , unsigned& max_seq_idx)
{
	while (pressed) {
		unsigned bit_no = 31 - count_leading_zeros_32(pressed);
		ioport_value mask = BIT_MASK<ioport_value>(bit_no);
		int seq_len = port.field(mask)->seq().length();
		if (seq_len > max_seq_len) {
			max_seq_len = seq_len;
			max_seq_idx = bit_no + idx_base;
		}
		pressed &= ~mask;
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(hp98xx_state::prt_timer)
{
	print_line();
	m_printer_line++;
	if (m_printer_line <= PRINT_CELL_H) {
		m_prt_timer->adjust(attotime::from_ticks(KDP_CLOCKS_PER_LINE , KDP_CLOCK));
	} else {
		m_printer_line = 0;
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(hp98xx_state::beep_timer)
{
	m_beeper->set_state(0);
}

void hp98xx_state::set_irq_slot(unsigned slot , int state)
{
	int sc = m_slot_sc[ slot ];
	assert(sc >= 0);
	m_io_sys->set_irq(uint8_t(sc) , state);
}

void hp98xx_state::set_sts_slot(unsigned slot , int state)
{
	int sc = m_slot_sc[ slot ];
	assert(sc >= 0);
	m_io_sys->set_sts(uint8_t(sc) , state);
}

void hp98xx_state::set_flg_slot(unsigned slot , int state)
{
	int sc = m_slot_sc[ slot ];
	assert(sc >= 0);
	m_io_sys->set_flg(uint8_t(sc) , state);
}

void hp98xx_state::set_dmar_slot(unsigned slot , int state)
{
	int sc = m_slot_sc[ slot ];
	assert(sc >= 0);
	m_io_sys->set_dmar(uint8_t(sc) , state);
}

void hp98xx_state::hp98xx_base(machine_config &config)
{
	HP_09825_67907(config , m_cpu , MAIN_CLOCK);
	// Just guessing... settings borrowed from hp9845
	m_cpu->set_rw_cycles(6 , 6);
	m_cpu->set_relative_mode(false);
	m_cpu->set_addrmap(AS_IO , &hp98xx_state::cpu_io_map);
	m_cpu->set_int_cb(m_io_sys , FUNC(hp98x5_io_sys_device::int_r));
	m_cpu->pa_changed_cb().set(m_io_sys , FUNC(hp98x5_io_sys_device::pa_w));

	// Needed when 98035 RTC module is connected or time advances at about 1/4 the correct speed (NP misses a lot of 1kHz interrupts)
	config.set_maximum_quantum(attotime::from_hz(5000));

	HP98X5_IO_SYS(config , m_io_sys , 0);
	m_io_sys->irl().set_inputline(m_cpu, HPHYBRID_IRL);
	m_io_sys->irh().set_inputline(m_cpu, HPHYBRID_IRH);
	m_io_sys->sts().set(m_cpu , FUNC(hp_09825_67907_cpu_device::status_w));
	m_io_sys->flg().set(m_cpu , FUNC(hp_09825_67907_cpu_device::flag_w));
	m_io_sys->dmar().set(m_cpu , FUNC(hp_09825_67907_cpu_device::dmar_w));

	TIMER(config , m_cursor_timer , 0).configure_generic(FUNC(hp98xx_state::cursor_blink));

	// Keyboard scan timer. A scan of the whole keyboard should take 2^14 KDP clocks.
	TIMER(config , "kb_timer" , 0).configure_periodic(FUNC(hp98xx_state::kb_scan), attotime::from_ticks(16384 , KDP_CLOCK));

	// Tape drive
	HP9825_TAPE(config , m_tape , 0);
	m_tape->flg().set([this](int state) { m_io_sys->set_flg(TAPE_PA , state); });
	m_tape->sts().set([this](int state) { m_io_sys->set_sts(TAPE_PA , state); });
	m_tape->dmar().set([this](int state) { m_io_sys->set_dmar(TAPE_PA , state); });
	m_tape->led().set([this](int state) { m_tape_led = state; });
	m_tape->cart_in().set([this](int state) { if (started()) m_cassette = state; });

	// Printer
	TIMER(config , m_prt_timer , 0).configure_generic(FUNC(hp98xx_state::prt_timer));

	// Beeper
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, BEEPER_FREQ).add_route(ALL_OUTPUTS, "mono", 1.00);
	TIMER(config , m_beep_timer , 0).configure_generic(FUNC(hp98xx_state::beep_timer));

	// I/O slots
	for (unsigned slot = 0; slot < 3; slot++) {
		auto& finder = m_io_slot[ slot ];
		hp9845_io_slot_device& tmp( HP9845_IO_SLOT(config , finder , 0) );
		tmp.irq().set([this , slot](int state) { set_irq_slot(slot , state); });
		tmp.sts().set([this , slot](int state) { set_sts_slot(slot , state); });
		tmp.flg().set([this , slot](int state) { set_flg_slot(slot , state); });
		tmp.dmar().set([this , slot](int state) { set_dmar_slot(slot , state); });
	}

	// Optional ROM slots
	for (auto& finder : m_rom_drawers) {
		HP9825_OPTROM(config , finder);
	}

	config.set_default_layout(layout_hp9825);
}

#define IOP_MASK(x) BIT_MASK<ioport_value>((x))

static INPUT_PORTS_START(hp98xx)
	// Keyboard is arranged in a 8 x 16 matrix. Of the 128 possible positions, 102 are used.
	// Keys are mapped on bit b of KEYn
	// where b = (row & 1) << 4 + column, n = row >> 1
	// column = [0..15]
	// row = [0..7]
	// 4 more keys are not in the matrix: 2 SHIFTs, 1 SHIFT LOCK and RESET key.
	// Fun fact: alphanumeric keys are arranged in the matrix so that their scancode (row/column number)
	// equals the lower case ASCII code. The person in charge of routing the keyboard PCB
	// must have loved this arrangement...
	PORT_START("KEY0")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 0,0: N/U
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC)) PORT_NAME("Stop")                    // 0,1: Stop
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Rewind")                                                                       // 0,2: Rewind
	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 0,3: N/U
	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 0,4: N/U
	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 0,5: N/U
	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 0,6: N/U
	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Result")                                                                       // 0,7: Result
	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Line insert")                                                                  // 0,8: Line insert
	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Line delete")                                                                  // 0,9: Line delete
	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_NAME("Execute")    // 0,10: Execute
	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Line recall")                                                                 // 0,11: Line recall
	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Run")                                                                         // 0,12: Run
	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Store")                                                                       // 0,13: Store
	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Display left")                                                                // 0,14: Display left
	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Display right")                                                               // 0,15: Display right
	PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_NAME("Display down")         // 1,0: Display down
	PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_NAME("Display up")               // 1,1: Display up
	PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_NAME("Clear")                                               // 1,2: Clear
	PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Print all")                                                                   // 1,3: Print all
	PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Char back")                                           // 1,4: Char back
	PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Char forward")                                       // 1,5: Char forward
	PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT) PORT_NAME("Char ins/rpl")                                      // 1,6: Char ins/rpl
	PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL) PORT_NAME("Char delete")                                          // 1,7: Char delete
	PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Step")                                                                        // 1,8: Step
	PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_NAME("Continue") PORT_CHAR(13)                             // 1,9: Continue
	PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 1,10: N/U
	PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("List")                                                                        // 1,11: List
	PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Line fetch")                                                                  // 1,12: Line fetch
	PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Erase")                                                                       // 1,13: Erase
	PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Record")                                                                      // 1,14: Record
	PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Load")                                                                        // 1,15: Load

	PORT_START("KEY1")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')                                                   // 2,0: Space
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 2,1: N/U
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 2,2: N/U
	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 2,3: N/U
	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 2,4: N/U
	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 2,5: N/U
	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 2,6: N/U
	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 2,7: N/U
	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('(') PORT_NAME("Keypad (")                         // 2,8: KP (
	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(')') PORT_NAME("Keypad )")                        // 2,9: KP )
	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(ASTERISK))                           // 2,10: KP *
	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))                           // 2,11: KP +
	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')                                   // 2,12: ,
	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))                         // 2,13: KP -
	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')                                    // 2,14: .
	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD))                         // 2,15: KP /
	PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('\'')                                      // 3,0: 0
	PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')                                       // 3,1: 1
	PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')                                       // 3,2: 2
	PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')                                       // 3,3: 3
	PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')                                       // 3,4: 4
	PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')                                       // 3,5: 5
	PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')                                       // 3,6: 6
	PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('@')                                       // 3,7: 7
	PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('[')                                       // 3,8: 8
	PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(']')                                       // 3,9: 9
	PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 3,10: N/U
	PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';')                                                  // 3,11: ;
	PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 3,12: N/U
	PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS_PAD) PORT_CHAR(UCHAR_MAMEKEY(EQUALS_PAD))                       // 3,13: =
	PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 3,14: N/U
	PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('?') PORT_CHAR(':')                                   // 3,15: ?

	PORT_START("KEY2")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 4,0: N/U
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("f0")                                                                           // 4,1: f0
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_NAME("f1")                        // 4,2: f1
	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_NAME("f2")                        // 4,3: f2
	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_NAME("f3")                        // 4,4: f3
	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_NAME("f4")                        // 4,5: f4
	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_NAME("f5")                        // 4,6: f5
	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_NAME("f6")                        // 4,7: f6
	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_NAME("f7")                        // 4,8: f7
	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_NAME("f8")                        // 4,9: f8
	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9)) PORT_NAME("f9")                       // 4,10: f9
	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10)) PORT_NAME("f10")                    // 4,11: f10
	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11)) PORT_NAME("f11")                    // 4,12: f11
	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 4,13: N/U
	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))                                 // 4,14: KP 0
	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))                                 // 4,15: KP 1
	PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))                                 // 5,0: KP 2
	PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))                                 // 5,1: KP 3
	PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))                                 // 5,2: KP 4
	PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))                                 // 5,3: KP 5
	PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD))                                 // 5,4: KP 6
	PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))                                 // 5,5: KP 7
	PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))                                 // 5,6: KP 8
	PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))                                 // 5,7: KP 9
	PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))                             // 5,8: KP .
	PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA_PAD) PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD))                         // 5,9: KP ,
	PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 5,10: N/U
	PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 5,11: N/U
	PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 5,12: N/U
	PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 5,13: N/U
	PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_NAME(u8"\u2191 \u221A")                                    // 5,14: ^ (↑ √)
	PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 5,15: N/U

	PORT_START("KEY3")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Enter exp _")                                                                  // 6,0: Enter exp
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')                                        // 6,1: A
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')                                        // 6,2: B
	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')                                        // 6,3: C
	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')                                        // 6,4: D
	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')                                        // 6,5: E
	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')                                        // 6,6: F
	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')                                        // 6,7: G
	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')                                        // 6,8: H
	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')                                        // 6,9: I
	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')                                       // 6,10: J
	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')                                       // 6,11: K
	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')                                       // 6,12: L
	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')                                       // 6,13: M
	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')                                       // 6,14: N
	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')                                       // 6,15: O
	PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')                                       // 7,0: P
	PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')                                       // 7,1: Q
	PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')                                       // 7,2: R
	PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')                                       // 7,3: S
	PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')                                       // 7,4: T
	PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')                                       // 7,5: U
	PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')                                       // 7,6: V
	PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')                                       // 7,7: W
	PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')                                       // 7,8: X
	PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')                                       // 7,9: Y
	PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')                                       // 7,10: Z
	PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME(u8"π |")                                                                       // 7,11: Pi
	PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 7,12: N/U
	PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_END) PORT_NAME(u8"\u2192")                                             // 7,13: Gazinta (→)
	PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 7,14: N/U
	PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 7,15: N/U

	PORT_START("KEY_SHIFT")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp98xx_state::kb_changed), 0) // Left and right Shift
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Shift lock") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp98xx_state::kb_changed), 1)     // Shift lock
	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Reset") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp98xx_state::kb_changed), 2)          // Reset
INPUT_PORTS_END

// +--------------+
// | hp9825_state |
// +--------------+
class hp9825_state : public hp98xx_state
{
public:
	hp9825_state(const machine_config &mconfig, device_type type, const char *tag)
		: hp98xx_state(mconfig, type, tag)
		, m_scroll_key(*this , "KEY_SCROLL")
		, m_prt_alpha_out(*this , "prt_alpha")
		, m_prt_graph_out(*this , "prt_graph")
		, m_prt_out(*this, "printer")
		, m_printer_bitmap(PRINT_OUT_W , PRINT_OUT_H)
	{
	}

	DECLARE_INPUT_CHANGED_MEMBER(scroll_changed);

	uint32_t printer_out_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

protected:
	void hp9825_base(machine_config &config);

	virtual void machine_start() override ATTR_COLD;

	virtual void print_line() override;

private:
	required_ioport m_scroll_key;
	required_device<bitbanger_device> m_prt_alpha_out;
	required_device<bitbanger_device> m_prt_graph_out;
	required_device<screen_device> m_prt_out;

	unsigned m_printer_window;  // 0 is bottom window (the one closer to printhead)
	bitmap_rgb32 m_printer_bitmap;
};

void hp9825_state::hp9825_base(machine_config &config)
{
	hp98xx_base(config);

	// Printer
	BITBANGER(config , m_prt_alpha_out , 0);
	BITBANGER(config , m_prt_graph_out , 0);
	SCREEN(config , m_prt_out , SCREEN_TYPE_RASTER);
	m_prt_out->set_screen_update(FUNC(hp9825_state::printer_out_update));
	m_prt_out->set_refresh_hz(60);
	m_prt_out->set_size(PRINT_WIN_W, PRINT_WIN_H);
	m_prt_out->set_visarea_full();
}

INPUT_CHANGED_MEMBER(hp9825_state::scroll_changed)
{
	LOG_DBG("scroll p=%d n=%d\n" , param , newval);
	switch (param) {
	case 0:
		// Scroll up
		if (newval && m_printer_window > 0) {
			m_printer_window--;
		}
		break;

	case 1:
		// Scroll down
		if (newval && m_printer_window < (PRINT_WINDOWS - 1)) {
			m_printer_window++;
		}
		break;
	}
}

void hp9825_state::machine_start()
{
	hp98xx_state::machine_start();

	m_printer_bitmap.fill(PRINT_BG);
	m_printer_window = 0;
}

void hp9825_state::print_line()
{
	// Dump text line to alpha bitbanger at start of print
	if (m_printer_line == 1) {
		for (auto c : m_printer_mem) {
			m_prt_alpha_out->output(c);
		}
		m_prt_alpha_out->output('\n');
	}

	// Shift printer bitmap one line up and clear bottom line
	{
		bitmap_rgb32 tmp{ m_printer_bitmap.width() , m_printer_bitmap.height() };
		s32 scroll = -1;
		rectangle cliprect{ 0 , m_printer_bitmap.width() - 1 , 0 , m_printer_bitmap.height() - 2 };
		copyscrollbitmap(tmp , m_printer_bitmap , 0 , nullptr , 1 , &scroll , cliprect);
		copybitmap(m_printer_bitmap , tmp , 0 , 0 , 0 , 0 , cliprect);
		m_printer_bitmap.plot_box(0 , m_printer_bitmap.height() - 1 , m_printer_bitmap.width() , 1 , PRINT_BG);
	}

	if ((m_printer_line > 0 && m_printer_line <= PRINT_EMPTY_TOP) ||
		(m_printer_line > (PRINT_EMPTY_TOP + PRINT_MATRIX_H) && m_printer_line <= PRINT_CELL_H)) {
		// Empty lines
		for (unsigned i = 0; i < PRINT_PIXELS_ROW; i++) {
			m_prt_graph_out->output(' ');
		}
	} else {
		for (unsigned i = 0; i < PRINT_COLUMNS; i++) {
			for (unsigned col = 0; col < PRINT_MATRIX_W; col++) {
				uint8_t pixels = chargen[ m_printer_mem[ i ] & 0x7f ][ col ];
				bool pixel = BIT(pixels , m_printer_line - PRINT_EMPTY_TOP - 1);
				m_prt_graph_out->output(pixel ? '*' : ' ');
				if (pixel) {
					m_printer_bitmap.pix(m_printer_bitmap.height() - 1 , i * PRINT_CELL_W + col) = PRINT_FG;
				}
			}
			m_prt_graph_out->output(' ');
			m_prt_graph_out->output(' ');
		}
	}
	m_prt_graph_out->output('\n');
}

uint32_t hp9825_state::printer_out_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	s32 scroll = -s32((PRINT_WINDOWS - m_printer_window - 1) * PRINT_WIN_H);
	copyscrollbitmap(bitmap , m_printer_bitmap , 0 , nullptr , 1 , &scroll , cliprect);

	return 0;
}

static INPUT_PORTS_START(hp9825)
	PORT_INCLUDE(hp98xx)

	PORT_START("KEY_SCROLL")
	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Scroll up") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9825_state::scroll_changed), 0)
	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Scroll down") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9825_state::scroll_changed), 1)
INPUT_PORTS_END

// +---------------+
// | hp9825a_state |
// +---------------+
class hp9825a_state : public hp9825_state
{
public:
	hp9825a_state(const machine_config &mconfig, device_type type, const char *tag)
		: hp9825_state(mconfig , type , tag)
		, m_ram(*this, RAM_TAG)
	{
	}

	void hp9825a(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<ram_device> m_ram;

	void cpu_mem_map(address_map &map) ATTR_COLD;
};

void hp9825a_state::hp9825a(machine_config &config)
{
	hp9825_base(config);
	m_cpu->set_addrmap(AS_PROGRAM , &hp9825a_state::cpu_mem_map);

	RAM(config, m_ram);

	m_ram->set_default_size("8K").set_extra_options("16K,24K,32K");

	SOFTWARE_LIST(config, "optrom_list").set_original("hp9825_rom").set_filter("A");
}

void hp9825a_state::machine_start()
{
	hp9825_state::machine_start();

	offs_t ram_start = 0x8000U - m_ram->size() / 2;

	auto space = &m_cpu->space(AS_PROGRAM);
	space->install_ram(ram_start, 0x7fff, m_ram->pointer());

	for (auto& finder : m_rom_drawers) {
		finder->set_rom_limit(ram_start);
		finder->install_rw_handlers(space , nullptr);
	}
}

void hp9825a_state::cpu_mem_map(address_map &map)
{
	map.unmap_value_low();
	map(0x0000 , 0x2fff).rom();
}

// +---------------+
// | hp9825b_state |
// +---------------+
class hp9825b_state : public hp9825_state
{
public:
	hp9825b_state(const machine_config &mconfig, device_type type, const char *tag)
		: hp9825_state(mconfig , type , tag)
	{
	}

	void hp9825b(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void cpu_mem_map(address_map &map) ATTR_COLD;
};

void hp9825b_state::hp9825b(machine_config &config)
{
	hp9825_base(config);
	m_cpu->set_addrmap(AS_PROGRAM , &hp9825b_state::cpu_mem_map);

	for (auto& finder : m_rom_drawers) {
		finder->set_rom_limit(0x5000);
	}

	SOFTWARE_LIST(config, "optrom_list").set_original("hp9825_rom").set_filter("B");
}

void hp9825b_state::machine_start()
{
	hp9825_state::machine_start();

	auto space = &m_cpu->space(AS_PROGRAM);

	for (auto& finder : m_rom_drawers) {
		finder->install_rw_handlers(space , nullptr);
	}
}

void hp9825b_state::cpu_mem_map(address_map &map)
{
	map.unmap_value_low();
	map(0x0000 , 0x2fff).rom();
	map(0x3400 , 0x3bff).rom();
	map(0x4000 , 0x4fff).rom();
	map(0x5000 , 0x7fff).ram();
}

// +---------------+
// | hp9825t_state |
// +---------------+
class hp9825t_state : public hp9825_state,
					  public device_memory_interface
{
public:
	hp9825t_state(const machine_config &mconfig, device_type type, const char *tag)
		: hp9825_state(mconfig , type , tag)
		, device_memory_interface(mconfig , *this)
		, m_skoalrom(*this , "skoalrom")
		, m_ram_space_config("ram" , ENDIANNESS_BIG , 16 , 15 , -1)
		, m_rom_space_config("rom" , ENDIANNESS_BIG , 16 , 15 , -1)
	{
	}

	void hp9825t(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void device_reset() override ATTR_COLD;

	// device_memory_interface overrides
	virtual space_config_vector memory_space_config() const override;

private:
	required_memory_region m_skoalrom;
	address_space_config m_ram_space_config;
	address_space *m_ram_space;
	address_space_config m_rom_space_config;
	address_space *m_rom_space;

	uint8_t m_cycle_type;

	// SKOAL state
	bool m_skoalbit;    // U53
	bool m_second_access;   // U57-3
	bool m_mref;            // U57-4
	bool m_ifetch_4400;     // U57-5
	uint8_t m_special_opt;  // U42
	uint16_t m_fetch_addr;

	void cpu_mem_map(address_map &map) ATTR_COLD;
	void ram_mem_map(address_map &map) ATTR_COLD;
	void rom_mem_map(address_map &map) ATTR_COLD;
	uint16_t cpu_mem_r(offs_t offset, uint16_t mem_mask = ~0);
	void cpu_mem_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	void stm(uint8_t cycle_type);
	void on_cycle_end();
	void opcode_fetch(uint16_t opcode);
	uint8_t get_skoalrom(uint16_t addr);
	bool is_rom(uint16_t addr , uint8_t cycle_type) const;
};

void hp9825t_state::hp9825t(machine_config &config)
{
	hp9825_base(config);
	m_cpu->set_addrmap(AS_PROGRAM , &hp9825t_state::cpu_mem_map);
	m_cpu->set_stm_cb(FUNC(hp9825t_state::stm));
	m_cpu->set_opcode_cb(FUNC(hp9825t_state::opcode_fetch));
	set_addrmap(0 , &hp9825t_state::ram_mem_map);
	set_addrmap(1 , &hp9825t_state::rom_mem_map);

	for (auto& finder : m_rom_drawers) {
		finder->set_rom_limit(0x6000);
	}

	SOFTWARE_LIST(config, "optrom_list").set_original("hp9825_rom").set_filter("T");
}

void hp9825t_state::machine_start()
{
	hp9825_state::machine_start();

	m_ram_space = &space(0);
	m_rom_space = &space(1);
}

void hp9825t_state::device_reset()
{
	hp98xx_state::device_reset();

	for (auto& finder : m_rom_drawers) {
		finder->install_rw_handlers(m_rom_space , m_ram_space);
	}

	// This has to be done before CPU reset or first instruction won't be fetched correctly
	m_cycle_type = 0;
	m_special_opt = 0xf;
}

device_memory_interface::space_config_vector hp9825t_state::memory_space_config() const
{
	return space_config_vector {
		std::make_pair(0 , &m_ram_space_config),
		std::make_pair(1 , &m_rom_space_config)
	};
}

void hp9825t_state::cpu_mem_map(address_map &map)
{
	map.unmap_value_low();
	map(0x0000 , 0x7fff).rw(FUNC(hp9825t_state::cpu_mem_r) , FUNC(hp9825t_state::cpu_mem_w));
}

void hp9825t_state::ram_mem_map(address_map &map)
{
	// 32 kw of RAM covering the whole address space (1st kw not accessible)
	map(0x0000 , 0x7fff).ram();
}

void hp9825t_state::rom_mem_map(address_map &map)
{
	map.unmap_value_low();
	map(0x0000 , 0x2fff).rom().region(":rom" , 0);
	map(0x3400 , 0x3bff).rom().region(":rom" , 0x6800);
	map(0x4000 , 0x53ff).rom().region(":rom" , 0x8000);
}

uint16_t hp9825t_state::cpu_mem_r(offs_t offset, uint16_t mem_mask)
{
	bool from_rom;

	if (m_cycle_type & hp_hybrid_cpu_device::CYCLE_RD_MASK) {
		if (m_cycle_type & hp_hybrid_cpu_device::CYCLE_IFETCH_MASK) {
			m_fetch_addr = offset;
		}
		from_rom = is_rom(offset , m_cycle_type);
		LOG("rd @%04x CYC=%x %d%d%d%d ROM=%d\n" , offset , m_cycle_type , m_skoalbit , m_second_access , m_mref , m_ifetch_4400 , from_rom);
		if (!(m_cycle_type & (hp_hybrid_cpu_device::CYCLE_IFETCH_MASK | hp_hybrid_cpu_device::CYCLE_DMA_MASK))) {
			on_cycle_end();
		}
		m_cycle_type = 0;
		// TODO: diagnostic read
	} else {
		// Read coming from debugger and not from CPU: fake an ifetch
		from_rom = is_rom(offset , hp_hybrid_cpu_device::CYCLE_IFETCH_MASK);
	}

	return from_rom ? m_rom_space->read_word(offset , mem_mask) : m_ram_space->read_word(offset , mem_mask);
}

void hp9825t_state::cpu_mem_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (m_cycle_type & hp_hybrid_cpu_device::CYCLE_WR_MASK) {
		if (!(m_cycle_type & hp_hybrid_cpu_device::CYCLE_DMA_MASK)) {
			on_cycle_end();
		}
		m_cycle_type = 0;
	}
	// All write cycles go to RAM
	m_ram_space->write_word(offset , data , mem_mask);
}

void hp9825t_state::stm(uint8_t cycle_type)
{
	LOG("stm %x\n" , cycle_type);
	m_cycle_type = cycle_type;
	if (m_cycle_type & hp_hybrid_cpu_device::CYCLE_IFETCH_MASK) {
		m_second_access = false;
		m_mref = false;
		m_ifetch_4400 = false;
		// In case of ifetch from register area this is kept at 0 (because cpu_mem_r is not called)
		// In case of ifetch from RAM/ROM this is set by cpu_mem_r to the fetch address
		m_fetch_addr = 0;
	} else if (m_cycle_type & hp_hybrid_cpu_device::CYCLE_RAL_MASK) {
		if (!(m_cycle_type & hp_hybrid_cpu_device::CYCLE_DMA_MASK)) {
			on_cycle_end();
		}
		m_cycle_type = 0;
	}
}

void hp9825t_state::on_cycle_end()
{
	m_second_access = false;
}

void hp9825t_state::opcode_fetch(uint16_t opcode)
{
	LOG("oc %04x\n" , opcode);
	m_cycle_type = 0;
	// memory referencing instructions
	m_mref = (opcode & 0x7000) != 0x7000;
	m_second_access = true;
	m_ifetch_4400 = (m_fetch_addr & 0x7f00) == 0x0900;
	if (BIT(m_special_opt , 3) && BIT(m_special_opt , 2)) {
		// Set SKOAL bit
		if (m_fetch_addr < 0x20) {
			// Fetch from registers -> SKOAL bit = 0
			m_skoalbit = false;
		} else if ((m_fetch_addr & 0x6000) == 0x6000) {
			// Fetch in [6000..7fff] range -> SKOAL bit = 0
			m_skoalbit = false;
		} else {
			uint8_t tmp = get_skoalrom(m_fetch_addr);
			m_skoalbit = (tmp >> ((~m_fetch_addr >> 12) & 7)) & 1;
		}
	}
	// Decode SKOAL instructions. They are ignored by the hybrid processor
	// as they are not recognized.
	if ((opcode & 0xffc0) == 0x7040) {
		m_special_opt = opcode & 0xf;
		if (!BIT(m_special_opt , 3)) {
			// RAM/ == 0
			m_skoalbit = false;
		} else if (!BIT(m_special_opt , 2)) {
			// ROM/ == 0
			m_skoalbit = true;
		}
	}
}

uint8_t hp9825t_state::get_skoalrom(uint16_t addr)
{
	return m_skoalrom->as_u8(~addr & 0x0fff);
}

bool hp9825t_state::is_rom(uint16_t addr , uint8_t cycle_type) const
{
	if ((addr & 0x6000) == 0x6000) {
		// [6000..7fff] -> always RAM
		return false;
	} else if ((cycle_type & hp_hybrid_cpu_device::CYCLE_DMA_MASK) != 0 ||
			   !BIT(m_special_opt , 1)) {
		// DMA cycle or BIT/ == 0 -> RAM
		return false;
	} else if (addr >= 0x400 && !BIT(m_special_opt , 0)) {
		// [0400..5fff] and BIN/ == 0 -> RAM
		return false;
	} else {
		bool addr_0800_7fff = (addr & 0x7800) != 0;
		bool addr_0400_07ff = !addr_0800_7fff && BIT(addr , 10);
		bool addr_0000_03ff = !addr_0800_7fff && !BIT(addr , 10);

		// U58-6
		bool force_rom;

		// ROM when one or more of these is true:
		// - addr in [0000..03ff]
		// - Ifetch cycle and addr in [0800..5fff]
		// - 2nd access of a memory-referencing instruction not in [0400..07ff] range
		// - skoalbit = 1 and instruction fetched in [0900..09ff] range
		force_rom =
			addr_0000_03ff ||
			((cycle_type & hp_hybrid_cpu_device::CYCLE_IFETCH_MASK) != 0 && addr_0800_7fff) ||
			(m_second_access && m_mref && (!BIT(m_special_opt , 2) || !addr_0400_07ff)) ||
			(m_skoalbit && m_ifetch_4400);

		if (force_rom) {
			return true;
		} else if (addr_0400_07ff && BIT(m_special_opt , 2)) {
			return false;
		} else {
			return m_skoalbit;
		}
	}
}

// +--------------+
// | hp9831_state |
// +--------------+
class hp9831_state : public hp98xx_state
{
public:
	hp9831_state(const machine_config &mconfig, device_type type, const char *tag)
		: hp98xx_state(mconfig , type , tag)
		, m_ram(*this, RAM_TAG)
	{
	}

	void hp9831(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<ram_device> m_ram;

	void cpu_mem_map(address_map &map) ATTR_COLD;
};

void hp9831_state::hp9831(machine_config &config)
{
	hp98xx_base(config);
	m_cpu->set_addrmap(AS_PROGRAM , &hp9831_state::cpu_mem_map);

	RAM(config, m_ram);

	m_ram->set_default_size("8K").set_extra_options("16K,24K,32K");

	SOFTWARE_LIST(config, "optrom_list").set_original("hp9831_rom");
}

void hp9831_state::machine_start()
{
	hp98xx_state::machine_start();

	offs_t ram_start = 0x8000U - m_ram->size() / 2;

	auto space = &m_cpu->space(AS_PROGRAM);
	space->install_ram(ram_start, 0x7fff, m_ram->pointer());

	for (auto& finder : m_rom_drawers) {
		finder->set_rom_limit(ram_start);
		finder->install_rw_handlers(space , nullptr);
	}
}

void hp9831_state::cpu_mem_map(address_map &map)
{
	map.unmap_value_low();
	map(0x0000 , 0x2fff).rom();
}

static INPUT_PORTS_START(hp9831)
	PORT_INCLUDE(hp98xx)

	PORT_MODIFY("KEY0")
	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME(u8"\u2191")                                                                     // 0,7: ↑
	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Line delete")                                                                  // 0,8: Line delete
	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Line step")                                                                    // 0,9: Line step
	PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT) PORT_NAME("Char insert")                                       // 1,6: Char insert
	PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Init")                                                                        // 1,8: Init
	PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Trace")                                                                       // 1,11: Trace
	PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Store")                                                                       // 1,14: Store

	PORT_MODIFY("KEY1")
	PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')                                   // 3,11: ;
	PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')                                   // 3,15: /

	PORT_MODIFY("KEY3")
	PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('|')                                   // 7,11: -
	PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Result")                                                                      // 7,13: Result

INPUT_PORTS_END

ROM_START(hp9825a)
	ROM_REGION(0x6000 , "cpu" , ROMREGION_16BIT | ROMREGION_BE)
	ROM_LOAD("9825asys.bin", 0x0000, 0x6000, CRC(bda0ddfa) SHA1(890993889adf0e48b6011fcc92603b89cf9af4a3))
ROM_END

ROM_START(hp9825b)
	ROM_REGION(0xa000 , "cpu" , ROMREGION_16BIT | ROMREGION_BE)
	ROM_LOAD("sysrom1.bin" , 0x0000 , 0x2000 , CRC(fe429268) SHA1(f2fe7c5abca92bd13f81b4385fc4fce0cafb0da0))
	ROM_LOAD("sysrom2.bin" , 0x2000 , 0x2000 , CRC(96093b5d) SHA1(c6ec4cafd019887df0fa849b3c7070bb74faee54))
	ROM_LOAD("sysrom3.bin" , 0x4000 , 0x2000 , CRC(f9470f67) SHA1(b80cb4a366d93bd7acc3508ce987bb11c5986b2a))
	ROM_LOAD("genio_t.bin" , 0x6800 , 0x0800 , CRC(ade1d1ed) SHA1(9af74a65b29ef1885f74164238ecf8d16ac995d6))
	ROM_LOAD("plot72.bin"  , 0x7000 , 0x0800 , CRC(0a9cb8db) SHA1(d0d126fca108f2715e1e408cb31b09ba69385ac4))
	ROM_LOAD("advpgm_t.bin", 0x8000 , 0x0800 , CRC(965b5e5a) SHA1(ff44dd15f8fa4ca03dfd970ed8b200e8a071ec13))
	ROM_LOAD("extio_t.bin" , 0x8800 , 0x1000 , CRC(a708b978) SHA1(baf53c8a2b24d059f95252baf1452188eaf6e4be))
	ROM_LOAD("strings_t.bin",0x9800 , 0x0800 , CRC(b5ca5da5) SHA1(af13abb3c15836c566863c656e1659f7e6f96d04))
ROM_END

ROM_START(hp9825t)
	ROM_REGION(0xa800 , ":rom" , ROMREGION_16BIT | ROMREGION_BE | ROMREGION_ERASE | ROMREGION_ERASE00)
	ROM_LOAD("sysrom1.bin" , 0x0000 , 0x2000 , CRC(fe429268) SHA1(f2fe7c5abca92bd13f81b4385fc4fce0cafb0da0))
	ROM_LOAD("sysrom2.bin" , 0x2000 , 0x2000 , CRC(96093b5d) SHA1(c6ec4cafd019887df0fa849b3c7070bb74faee54))
	ROM_LOAD("sysrom3.bin" , 0x4000 , 0x2000 , CRC(f9470f67) SHA1(b80cb4a366d93bd7acc3508ce987bb11c5986b2a))
	ROM_LOAD("genio_t.bin" , 0x6800 , 0x0800 , CRC(ade1d1ed) SHA1(9af74a65b29ef1885f74164238ecf8d16ac995d6))
	ROM_LOAD("plot72.bin"  , 0x7000 , 0x0800 , CRC(0a9cb8db) SHA1(d0d126fca108f2715e1e408cb31b09ba69385ac4))
	ROM_LOAD("advpgm_t.bin", 0x8000 , 0x0800 , CRC(965b5e5a) SHA1(ff44dd15f8fa4ca03dfd970ed8b200e8a071ec13))
	ROM_LOAD("extio_t.bin" , 0x8800 , 0x1000 , CRC(a708b978) SHA1(baf53c8a2b24d059f95252baf1452188eaf6e4be))
	ROM_LOAD("strings_t.bin",0x9800 , 0x0800 , CRC(b5ca5da5) SHA1(af13abb3c15836c566863c656e1659f7e6f96d04))
	ROM_LOAD("syspgm.bin"  , 0xa000 , 0x0800 , CRC(8915588f) SHA1(037f497b5ecc3216fb6b8356767cc361fb0b2945))

	ROM_REGION(0x1000 , "skoalrom" , 0)
	ROM_LOAD("skoalrom.bin" , 0 , 0x1000 , CRC(5e8124d5) SHA1(dedf7f8a10c62b444f04213956083089e97bf219))
ROM_END

ROM_START(hp9831)
	ROM_REGION(0x6000 , "cpu" , ROMREGION_16BIT | ROMREGION_BE)
	ROM_LOAD("9831sys.bin", 0x0000, 0x6000, CRC(2f92e685) SHA1(413aa65677ab0d6b5979af6c9da12c1368abcc82))
ROM_END

} // anonymous namespace


//   YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY            FULLNAME    FLAGS
COMP(1976, hp9825a, 0,      0,      hp9825a, hp9825, hp9825a_state,empty_init, "Hewlett-Packard", "HP 9825A", 0)
COMP(1980, hp9825b, 0,      0,      hp9825b, hp9825, hp9825b_state,empty_init, "Hewlett-Packard", "HP 9825B", 0)
COMP(1980, hp9825t, 0,      0,      hp9825t, hp9825, hp9825t_state,empty_init, "Hewlett-Packard", "HP 9825T", 0)
COMP(1977, hp9831,  0,      0,      hp9831,  hp9831, hp9831_state, empty_init, "Hewlett-Packard", "HP 9831A", 0)



hp9845.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder, F. Ulivi, Ansgar Kueckes
/*

    HP 9845

    http://www.hp9845.net/

*/
// *******************************
// Driver for HP 9845B/C/T systems
// *******************************
//
// What's in:
// - Emulation of 45B, 45C and 45T systems
// - Emulation of both 5061-3001 CPUs
// - LPU & PPU ROMs
// - LPU & PPU RAMs
// - Text mode screen
// - Graphic screen
// - Keyboard (US & German layouts)
// - T14 and T15 tape drive
// - Software list to load optional ROMs
// - Beeper
// - Correct character generator ROMs (a huge "thank you" to Ansgar Kueckes for the dumps!)
// - 98775 light pen controller
// - Display softkeys on 45C & 45T
// - HLE of integral printer
// What's not yet in:
// - Better naming of tape drive image (it's now "magt1" and "magt2", should be "t15" and "t14")
// - Better documentation of this file
// What's wrong:
// - Speed, as usual
// - Light pen tracing sometimes behaves erratically in 45C and 45T
// What will probably never be in:
// - Fast LPU processor (dump of microcode PROMs is not available)

#include "emu.h"
#include "hp9845.h"

#include "hp9845_optrom.h"
#include "bus/hp9845_io/hp9845_io.h"
#include "machine/timer.h"

#include "render.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "hp9845b.lh"

#include "hp9845_printer.h"

// Debugging
#define VERBOSE 0
#include "logmacro.h"

#define BIT_MASK(n) (1U << (n))

// Macros to clear/set single bits
#define BIT_CLR(w , n)  ((w) &= ~BIT_MASK(n))
#define BIT_SET(w , n)  ((w) |= BIT_MASK(n))

/*

 The 9845 has three possible display options:

 98750A: Standard monochrome (alpha with graphics option)
 98780A: Enhanced monochrome (alpha with hardware accelerated monochrome graphics)
 98770A: Color (color alpha with hardware accelerated color graphics with three planes)

 All displays use a 560x455 graphics raster. Alpha and graphics share the
 same dots within a 720x455 super matrix. All pixels have a 1:1 ratio (square
 pixels).

 The 98750A uses a 25x80 alpha area, either alpha or graphics can be enabled,
 but not both at the same time. In fact, both modes use different video circuits.

 Timing and pixel size for real 98750A are slightly different between
 alpha and graphics raster (dual raster):

                                  alpha       graphics
 ------------------------------------------------------
 Matrix:                          720x375     560x455
 Clock frequency:                 20.85 MHz   20.85 MHz
 Horizontal scan frequency:       23.4 kHz    28.7 kHz
 Horizontal retrace time:         8.2 us      8.0 us
 Frame frequency:                 60 Hz       60 Hz
 Vertical retrace time:           641 us      800 us
 Size on screen:                  9.3"x4.84"  7.9"x6.4"


 The 98770A and 98780A both use a 720x455 raster, implemented with a single video
 circuit, which again is shared by the alpha and graphics logic, with alpha
 dominant over graphics. So, nominally the alpha area for those systems can
 hold up to 30 rows with full size characters plus some lines for one row with
 cropped characters:

                                  98770A       98780A
 ------------------------------------------------------
 Matrix:                          720x455      720x455
 Clock frequency:                 29.7984 MHz  28.224 MHz
 Horizontal scan frequency:       29.1 kHz     31.5 kHz
 Horizontal retrace time:         10.02 us     4.145 us
 Frame frequency:                 60 Hz        60 Hz
 Vertical retrace time:           1.03 ms      2.22 ms
 Size on screen:                  247x154 mm   236x149 mm
 Dot size:                        0.343 mm     0.33 mm

*/

// Base address of video buffer
#define VIDEO_BUFFER_BASE_LOW       0x16000         // for 98770A and 98780A
#define VIDEO_BUFFER_BASE_HIGH      0x17000         // for 98750A

// For test "B" of alpha video to succeed this must be < 234
// Basically "B" test is designed to intentionally prevent line buffer to be filled so that display is blanked
// from 2nd row on. This in turn prevents "BAD" text to be visible on screen.
#define MAX_WORD_PER_ROW        220

// Constants of alpha video
#define VIDEO_PIXEL_CLOCK       20849400
#define VIDEO_CHAR_WIDTH        9
#define VIDEO_CHAR_HEIGHT       15
#define VIDEO_CHAR_COLUMNS      80
#define VIDEO_CHAR_TOTAL        99
#define VIDEO_CHAR_ROWS         25
#define VIDEO_ROWS_TOTAL        26
#define VIDEO_HBSTART           (VIDEO_CHAR_WIDTH * VIDEO_CHAR_COLUMNS)
#define VIDEO_HTOTAL            (VIDEO_CHAR_WIDTH * VIDEO_CHAR_TOTAL)
#define VIDEO_VTOTAL            (VIDEO_CHAR_HEIGHT * VIDEO_ROWS_TOTAL)
#define VIDEO_ACTIVE_SCANLINES  (VIDEO_CHAR_HEIGHT * VIDEO_CHAR_ROWS)
#define VIDEO_TOT_HPIXELS       (VIDEO_CHAR_WIDTH * VIDEO_CHAR_COLUMNS)

// Constants of graphic video
// Pixel clock is 20.8494 MHz (the same as alpha video)
// Horizontal counter counts in [1..727] range
// Vertical counter counts in [34..511] range
#define GVIDEO_HTOTAL           727
#define GVIDEO_HCNT_OFF         1       // Actual start value of h counter
#define GVIDEO_HBEND            (69 - GVIDEO_HCNT_OFF)
#define GVIDEO_HPIXELS          560
#define GVIDEO_HBSTART          (GVIDEO_HBEND + GVIDEO_HPIXELS)
#define GVIDEO_VTOTAL           478
#define GVIDEO_VCNT_OFF         34      // Actual start value of v counter
#define GVIDEO_VBEND            (50 - GVIDEO_VCNT_OFF)
#define GVIDEO_VPIXELS          455
#define GVIDEO_VBSTART          (GVIDEO_VBEND + GVIDEO_VPIXELS)
#define GVIDEO_MEM_SIZE         16384
#define GVIDEO_ADDR_MASK        (GVIDEO_MEM_SIZE - 1)

// Constants of 98770A video
// HBEND & VBEND probably are not really 0
#define VIDEO_770_PIXEL_CLOCK   29798400
#define VIDEO_770_HTOTAL        1024
#define VIDEO_770_HBEND         0
#define VIDEO_770_HBSTART       (VIDEO_CHAR_COLUMNS * VIDEO_CHAR_WIDTH)
#define VIDEO_770_VTOTAL        485
#define VIDEO_770_VBEND         0
#define VIDEO_770_VBSTART       (VIDEO_770_VBEND + GVIDEO_VPIXELS)
#define VIDEO_770_ALPHA_L_LIM   80  // Left-side limit of alpha-only horizontal part
#define VIDEO_770_ALPHA_R_LIM   640 // Right-side limit of alpha-only horizontal part

// Constants of 98780A video
#define VIDEO_780_PIXEL_CLOCK   28224000
#define VIDEO_780_HTOTAL        896
#define VIDEO_780_VTOTAL        525
#define VIDEO_780_HBEND         0
#define VIDEO_780_HBSTART       (VIDEO_CHAR_COLUMNS * VIDEO_CHAR_WIDTH)
#define VIDEO_780_VBEND         0
#define VIDEO_780_VBSTART       (VIDEO_780_VBEND + GVIDEO_VPIXELS)
#define VIDEO_780_ALPHA_L_LIM   80  // Left-side limit of alpha-only horizontal part
#define VIDEO_780_ALPHA_R_LIM   640 // Right-side limit of alpha-only horizontal part

#define I_GR    0xb0    // graphics intensity
#define I_AL    0xd0    // alpha intensity
#define I_CU    0xf0    // graphics cursor intensity
#define I_LP    0xff    // light pen cursor intensity

// Palette indexes (for monochromatic screens)
#define PEN_BLACK   0   // Black
#define PEN_GRAPHIC 1   // Graphics
#define PEN_ALPHA   2   // Text
#define PEN_CURSOR  3   // Graphic cursor
#define PEN_LP      4   // Light pen cursor

// Light pen constants
constexpr unsigned LP_FOV = 9;  // Field of view
constexpr unsigned LP_XOFFSET = 5;  // x-offset of LP (due to delay in hit recognition)

// Peripheral Addresses (PA)
#define PRINTER_PA          0
#define IO_SLOT_FIRST_PA    1
#define IO_SLOT_LAST_PA     12
#define GVIDEO_PA           13
#define T14_PA              14
#define T15_PA              15

#define KEY_SCAN_OSCILLATOR     327680

class hp9845_state : public driver_device
{
public:
	hp9845_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
	{ }

	void hp9845a(machine_config &config);
	void hp9835a(machine_config &config);

private:
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};

static INPUT_PORTS_START( hp9845 )
INPUT_PORTS_END

uint32_t hp9845_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

static INPUT_PORTS_START(hp9845_base)
	// Keyboard is arranged in a 8 x 16 matrix. Of the 128 possible positions, 118 are used.
	// Keys are mapped on bit b of KEYn
	// where b = (row & 1) << 4 + column, n = row >> 1
	// column = [0..15]
	// row = [0..7]
	PORT_START("KEY0")
	PORT_BIT(BIT_MASK(0)  , IP_ACTIVE_HIGH , IPT_UNUSED)    // N/U
	PORT_BIT(BIT_MASK(1)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_TOGGLE PORT_NAME("Prt all") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9845_base_state::togglekey_changed), 1) // Print All
	PORT_BIT(BIT_MASK(2)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))                           // KP +
	PORT_BIT(BIT_MASK(3)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_COMMA_PAD) PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD))                         // KP ,
	PORT_BIT(BIT_MASK(4)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))                             // KP .
	PORT_BIT(BIT_MASK(5)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))                                 // KP 0
	PORT_BIT(BIT_MASK(6)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_NAME("Execute")    // Execute
	PORT_BIT(BIT_MASK(7)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_ENTER) PORT_NAME("Cont") PORT_CHAR(13)    // Cont
	PORT_BIT(BIT_MASK(8)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))        // Right
	PORT_BIT(BIT_MASK(9)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')  // Space
	PORT_BIT(BIT_MASK(10)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')  // /
	PORT_BIT(BIT_MASK(11)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')  // <
	PORT_BIT(BIT_MASK(12)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_N)     PORT_CHAR('n') PORT_CHAR('N')  // N
	PORT_BIT(BIT_MASK(13)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_V)     PORT_CHAR('v') PORT_CHAR('V')  // V
	PORT_BIT(BIT_MASK(14)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_X)     PORT_CHAR('x') PORT_CHAR('X')  // X
	PORT_BIT(BIT_MASK(15)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)    PORT_CHAR(UCHAR_SHIFT_1)   // Shift
	PORT_BIT(BIT_MASK(16)  , IP_ACTIVE_HIGH , IPT_UNUSED)   // N/U
	PORT_BIT(BIT_MASK(17)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_TOGGLE PORT_NAME("Auto st") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9845_base_state::togglekey_changed), 2) // Auto Start
	PORT_BIT(BIT_MASK(18)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) // KP -
	PORT_BIT(BIT_MASK(19)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))         // KP 3
	PORT_BIT(BIT_MASK(20)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))         // KP 2
	PORT_BIT(BIT_MASK(21)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))         // KP 1
	PORT_BIT(BIT_MASK(22)  , IP_ACTIVE_HIGH , IPT_UNUSED)   // N/U
	PORT_BIT(BIT_MASK(23)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))  // Left
	PORT_BIT(BIT_MASK(24)  , IP_ACTIVE_HIGH , IPT_UNUSED)   // Repeat
	PORT_BIT(BIT_MASK(25)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))  // Down
	PORT_BIT(BIT_MASK(26)  , IP_ACTIVE_HIGH , IPT_UNUSED)   // N/U
	PORT_BIT(BIT_MASK(27)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')   // >
	PORT_BIT(BIT_MASK(28)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_M)     PORT_CHAR('m') PORT_CHAR('M')  // M
	PORT_BIT(BIT_MASK(29)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_B)     PORT_CHAR('b') PORT_CHAR('B')  // B
	PORT_BIT(BIT_MASK(30)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_C)     PORT_CHAR('c') PORT_CHAR('C')  // C
	PORT_BIT(BIT_MASK(31)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)     PORT_CHAR('z') PORT_CHAR('Z')  // Z

	PORT_START("KEY1")
	PORT_BIT(BIT_MASK(0)  , IP_ACTIVE_HIGH , IPT_UNUSED)    // N/U
	PORT_BIT(BIT_MASK(1)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_INSERT) PORT_NAME("Ins chr")    // Ins Char
	PORT_BIT(BIT_MASK(2)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(ASTERISK))     // KP *
	PORT_BIT(BIT_MASK(3)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD))           // KP 6
	PORT_BIT(BIT_MASK(4)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))           // KP 5
	PORT_BIT(BIT_MASK(5)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))           // KP 4
	PORT_BIT(BIT_MASK(6)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_EQUALS_PAD) PORT_CHAR(UCHAR_MAMEKEY(EQUALS_PAD)) // KP =
	PORT_BIT(BIT_MASK(7)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Pause")      // Pause
	PORT_BIT(BIT_MASK(8)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_UP)   PORT_CHAR(UCHAR_MAMEKEY(UP))    // Up
	PORT_BIT(BIT_MASK(9)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Store")  // Store
	PORT_BIT(BIT_MASK(10)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)     PORT_CHAR(';') PORT_CHAR(':')      // :
	PORT_BIT(BIT_MASK(11)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_K)     PORT_CHAR('k') PORT_CHAR('K')  // K
	PORT_BIT(BIT_MASK(12)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_H)     PORT_CHAR('h') PORT_CHAR('H')  // H
	PORT_BIT(BIT_MASK(13)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F)     PORT_CHAR('f') PORT_CHAR('F')  // F
	PORT_BIT(BIT_MASK(14)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_S)     PORT_CHAR('s') PORT_CHAR('S')  // S
	PORT_BIT(BIT_MASK(15)  , IP_ACTIVE_HIGH , IPT_UNUSED)   // N/U
	PORT_BIT(BIT_MASK(16)  , IP_ACTIVE_HIGH , IPT_UNUSED)   // N/U
	PORT_BIT(BIT_MASK(17)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Ins ln")      // Ins Ln
	PORT_BIT(BIT_MASK(18)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD)) // KP /
	PORT_BIT(BIT_MASK(19)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))         // KP 9
	PORT_BIT(BIT_MASK(20)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))         // KP 8
	PORT_BIT(BIT_MASK(21)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))         // KP 7
	PORT_BIT(BIT_MASK(22)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Result")     // Result
	PORT_BIT(BIT_MASK(23)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Run")        // Run
	PORT_BIT(BIT_MASK(24)  , IP_ACTIVE_HIGH , IPT_UNUSED)   // N/U
	PORT_BIT(BIT_MASK(25)  , IP_ACTIVE_HIGH , IPT_UNUSED)   // N/U
	PORT_BIT(BIT_MASK(26)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"') // "
	PORT_BIT(BIT_MASK(27)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_L)     PORT_CHAR('l') PORT_CHAR('L')  // L
	PORT_BIT(BIT_MASK(28)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_J)     PORT_CHAR('j') PORT_CHAR('J')  // J
	PORT_BIT(BIT_MASK(29)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_G)     PORT_CHAR('g') PORT_CHAR('G')  // G
	PORT_BIT(BIT_MASK(30)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_D)     PORT_CHAR('d') PORT_CHAR('D')  // D
	PORT_BIT(BIT_MASK(31)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_A)     PORT_CHAR('a') PORT_CHAR('A')  // A

	PORT_START("KEY2")
	PORT_BIT(BIT_MASK(0)  , IP_ACTIVE_HIGH , IPT_UNUSED)    // N/U
	PORT_BIT(BIT_MASK(1)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Del ln")          // Del Ln
	PORT_BIT(BIT_MASK(2)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Keypad ^")        // KP ^
	PORT_BIT(BIT_MASK(3)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Keypad )")        // KP )
	PORT_BIT(BIT_MASK(4)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Keypad (")        // KP (
	PORT_BIT(BIT_MASK(5)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Keypad E")        // KP E
	PORT_BIT(BIT_MASK(6)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Clear line")      // Clear Line
	PORT_BIT(BIT_MASK(7)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC)) PORT_NAME("Stop")       // Stop
	PORT_BIT(BIT_MASK(8)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')      // |
	PORT_BIT(BIT_MASK(9)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_CHAR(']') PORT_CHAR('}')   // ]
	PORT_BIT(BIT_MASK(10)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_P)     PORT_CHAR('p') PORT_CHAR('P')  // P
	PORT_BIT(BIT_MASK(11)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_I)     PORT_CHAR('i') PORT_CHAR('I')  // I
	PORT_BIT(BIT_MASK(12)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)     PORT_CHAR('y') PORT_CHAR('Y')  // Y
	PORT_BIT(BIT_MASK(13)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_R)     PORT_CHAR('r') PORT_CHAR('R')  // R
	PORT_BIT(BIT_MASK(14)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_W)     PORT_CHAR('w') PORT_CHAR('W')  // W
	PORT_BIT(BIT_MASK(15)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)  PORT_CHAR(UCHAR_SHIFT_2)   // Control
	PORT_BIT(BIT_MASK(16)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Typwtr")     // Typwtr
	PORT_BIT(BIT_MASK(17)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)  PORT_NAME("Del chr")    // Del Char
	PORT_BIT(BIT_MASK(18)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN) PORT_NAME("Roll down")   // Roll down
	PORT_BIT(BIT_MASK(19)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP) PORT_NAME("Roll up")     // Roll up
	PORT_BIT(BIT_MASK(20)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_NAME("Home")       // Home
	PORT_BIT(BIT_MASK(21)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_END) PORT_NAME("Clr to end") // Clr to end
	PORT_BIT(BIT_MASK(22)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Clear")      // Clear
	PORT_BIT(BIT_MASK(23)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)     PORT_CHAR('`') PORT_CHAR('~')      // ~
	PORT_BIT(BIT_MASK(24)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)       // BS
	PORT_BIT(BIT_MASK(25)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)    PORT_CHAR('=') PORT_CHAR('+')      // +
	PORT_BIT(BIT_MASK(26)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')      // [
	PORT_BIT(BIT_MASK(27)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_O)     PORT_CHAR('o') PORT_CHAR('O')  // O
	PORT_BIT(BIT_MASK(28)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_U)     PORT_CHAR('u') PORT_CHAR('U')  // U
	PORT_BIT(BIT_MASK(29)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_T)     PORT_CHAR('t') PORT_CHAR('T')  // T
	PORT_BIT(BIT_MASK(30)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_E)     PORT_CHAR('e') PORT_CHAR('E')  // E
	PORT_BIT(BIT_MASK(31)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)     PORT_CHAR('q') PORT_CHAR('Q')  // Q

	PORT_START("KEY3")
	PORT_BIT(BIT_MASK(0)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Tab set")    // Tab set
	PORT_BIT(BIT_MASK(1)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Recall")     // Recall
	PORT_BIT(BIT_MASK(2)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("K15")        // K15
	PORT_BIT(BIT_MASK(3)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("K14")        // K14
	PORT_BIT(BIT_MASK(4)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("K13")        // K13
	PORT_BIT(BIT_MASK(5)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_F12) PORT_CHAR(UCHAR_MAMEKEY(F12)) PORT_NAME("K12")      // K12
	PORT_BIT(BIT_MASK(6)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11)) PORT_NAME("K11")      // K11
	PORT_BIT(BIT_MASK(7)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10)) PORT_NAME("K10")      // K10
	PORT_BIT(BIT_MASK(8)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9)) PORT_NAME("K9")         // K9
	PORT_BIT(BIT_MASK(9)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_NAME("K8")         // K8
	PORT_BIT(BIT_MASK(10)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_0)     PORT_CHAR('0') PORT_CHAR(')') // 0
	PORT_BIT(BIT_MASK(11)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_8)     PORT_CHAR('8') PORT_CHAR('*')  // 8
	PORT_BIT(BIT_MASK(12)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_6)     PORT_CHAR('6') PORT_CHAR('^')  // 6
	PORT_BIT(BIT_MASK(13)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_4)     PORT_CHAR('4') PORT_CHAR('$')  // 4
	PORT_BIT(BIT_MASK(14)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_2)     PORT_CHAR('2') PORT_CHAR('@')  // 2
	PORT_BIT(BIT_MASK(15)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)   PORT_CHAR('\t')        // Tab
	PORT_BIT(BIT_MASK(16)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Tab clr")    // Tab clr
	PORT_BIT(BIT_MASK(17)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Step")  // Step
	PORT_BIT(BIT_MASK(18)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_NAME("K7") // K7
	PORT_BIT(BIT_MASK(19)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_NAME("K6") // K6
	PORT_BIT(BIT_MASK(20)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_NAME("K5") // K5
	PORT_BIT(BIT_MASK(21)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_NAME("K4") // K4
	PORT_BIT(BIT_MASK(22)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_NAME("K3") // K3
	PORT_BIT(BIT_MASK(23)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_NAME("K2") // K2
	PORT_BIT(BIT_MASK(24)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_NAME("K1") // K1
	PORT_BIT(BIT_MASK(25)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("K0") // K0
	PORT_BIT(BIT_MASK(26)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)     PORT_CHAR('-') PORT_CHAR('_')      // _
	PORT_BIT(BIT_MASK(27)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_9)     PORT_CHAR('9') PORT_CHAR('(')  // 9
	PORT_BIT(BIT_MASK(28)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_7)     PORT_CHAR('7') PORT_CHAR('&') // 7
	PORT_BIT(BIT_MASK(29)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_5)     PORT_CHAR('5') PORT_CHAR('%')  // 5
	PORT_BIT(BIT_MASK(30)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_3)     PORT_CHAR('3') PORT_CHAR('#')  // 3
	PORT_BIT(BIT_MASK(31)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_1)     PORT_CHAR('1') PORT_CHAR('!')  // 1

	PORT_START("SHIFTLOCK");
	PORT_BIT(BIT_MASK(0)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_NAME("Shift lock") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9845_base_state::togglekey_changed), 0) // Shift lock

INPUT_PORTS_END

/*
    German keyboard layout

    Remarks:
    - Most keys including umlauts map correctly to the German keyboard layout of the 9845 without special configuration,
      provided that the German keyboard firmware ROM is used on the 9845
    - '#' maps positionally correct to Shift+3
    - AltGr modifier on the Germany PC keyboard for 9845 shifted keycodes 23=| and 5=@ need to get assigned dynamically
    - ~{}\'` are not available on the German 9845 keyboard, ^ is available via keypad only
*/
static INPUT_PORTS_START(hp9845_base_de)
	PORT_INCLUDE(hp9845_base)

	PORT_MODIFY("KEY0")
	PORT_BIT(BIT_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('-') PORT_CHAR('_')           // - _
	PORT_BIT(BIT_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR(';')           // , ;
	PORT_BIT(BIT_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(':')            // . :
	PORT_BIT(BIT_MASK(31) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('y') PORT_CHAR('Y')               // Y

	PORT_MODIFY("KEY1")
	PORT_BIT(BIT_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(0x00f6) PORT_CHAR(0x00d6) // Ö
	PORT_BIT(BIT_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(0x00e4) PORT_CHAR(0x00c4) // Ä

	PORT_MODIFY("KEY2")
	PORT_BIT(BIT_MASK(8)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('<') PORT_CHAR('>')      // < >
	PORT_BIT(BIT_MASK(9)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('+') PORT_CHAR('*')      // + *
	PORT_BIT(BIT_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('z') PORT_CHAR('Z')               // Z
	PORT_BIT(BIT_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)                        // Backspace
	PORT_BIT(BIT_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR(']') PORT_CHAR('@')           // ] @
	PORT_BIT(BIT_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('[') PORT_CHAR('|')          // [ |
	PORT_BIT(BIT_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(0x00fc) PORT_CHAR(0x00dc) // Ü

	PORT_MODIFY("KEY3")
	PORT_BIT(BIT_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('=')               // 0 =
	PORT_BIT(BIT_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')               // 8 (
	PORT_BIT(BIT_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')               // 6 &
	PORT_BIT(BIT_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')               // 2 "
	PORT_BIT(BIT_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR(0x00df) PORT_CHAR('?')    // ß ?
	PORT_BIT(BIT_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')               // 9 )
	PORT_BIT(BIT_MASK(28) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('/')               // 7 /
INPUT_PORTS_END

// *******************
//  hp9845_base_state
// *******************
hp9845_base_state::hp9845_base_state(const machine_config &mconfig, device_type type, const char *tag) :
	driver_device(mconfig, type, tag),
	m_lpu(*this, "lpu"),
	m_ppu(*this, "ppu"),
	m_io_sys(*this , "io_sys"),
	m_screen(*this, "screen"),
	m_palette(*this, "palette"),
	m_gv_timer(*this, "gv_timer"),
	m_io_key(*this, "KEY%u", 0U),
	m_io_shiftlock(*this, "SHIFTLOCK"),
	m_t14(*this, "t14"),
	m_t15(*this, "t15"),
	m_beeper(*this, "beeper"),
	m_beep_timer(*this, "beep_timer"),
	m_io_slot(*this, "slot%u", 0U),
	m_ram(*this, RAM_TAG),
	m_softkeys(*this, "Softkey%u", 0U),
	m_shift_lock_led(*this, "shift_lock_led"),
	m_prt_all_led(*this, "prt_all_led"),
	m_auto_st_led(*this, "auto_st_led"),
	m_chargen(*this, "chargen")
{
}

void hp9845_base_state::setup_ram_block(unsigned block , unsigned offset)
{
	unsigned block_addr = block << 16;
	m_lpu->space(AS_PROGRAM).install_ram(block_addr , block_addr + 0x7fff , m_ram->pointer() + offset);
	m_ppu->space(AS_PROGRAM).install_ram(block_addr , block_addr + 0x7fff , m_ram->pointer() + offset);
}

void hp9845_base_state::machine_start()
{
	m_softkeys.resolve();
	m_shift_lock_led.resolve();
	m_prt_all_led.resolve();
	m_auto_st_led.resolve();

	m_screen->register_screen_bitmap(m_bitmap);

	m_t15->set_name("T15");
	m_t14->set_name("T14");

	// setup RAM dynamically for -ramsize
	// 0K..64K
	setup_ram_block(0 , 0);
	if (m_ram->size() >= 192 * 1024) {
		// 64K..192K
		setup_ram_block(004 , 0x10000);
		setup_ram_block(006 , 0x20000);
	}
	if (m_ram->size() >= 320 * 1024) {
		// 192K..320K
		setup_ram_block(010 , 0x30000);
		setup_ram_block(012 , 0x40000);
	}
	if (m_ram->size() >= 448 * 1024) {
		// 320K..448K
		setup_ram_block(014 , 0x50000);
		setup_ram_block(016 , 0x60000);
	}
}

void hp9845_base_state::device_reset()
{
	// First, unmap every r/w handler in 1..12 select codes
	for (unsigned sc = IO_SLOT_FIRST_PA; sc < (IO_SLOT_LAST_PA + 1); sc++) {
		m_ppu->space(AS_IO).unmap_readwrite(sc * 4 , sc * 4 + 3);
	}

	// Then, set r/w handlers of all installed I/O cards
	int sc;
	read16m_delegate rhandler(*this);
	write16m_delegate whandler(*this);
	for (unsigned i = 0; 4 > i; ++i) {
		if ((sc = m_io_slot[i]->get_rw_handlers(rhandler , whandler)) >= 0) {
			logerror("Install R/W handlers for slot %u @ SC = %d\n", i, sc);
			m_ppu->space(AS_IO).install_readwrite_handler(sc * 4 , sc * 4 + 3 , rhandler , whandler);
			if (m_io_slot[ i ]->has_dual_sc()) {
				logerror("Installing dual SC\n");
				m_ppu->space(AS_IO).install_readwrite_handler(sc * 4 + 4 , sc * 4 + 7 , rhandler , whandler);
			}
		}
		m_slot_sc[ i ] = sc;
	}
}

void hp9845_base_state::machine_reset()
{
	m_lpu->halt_w(1);
	m_ppu->halt_w(0);

	// Some sensible defaults
	m_video_load_mar = false;
	m_video_first_mar = false;
	m_video_byte_idx = false;
	m_video_buff_idx = false;
	m_video_blanked = false;
	m_graphic_sel = false;
	m_gv_fsm_state = GV_STAT_RESET;
	m_gv_int_en = false;
	m_gv_dma_en = false;

	m_io_sys->set_sts(GVIDEO_PA , true);

	memset(&m_kb_state[ 0 ] , 0 , sizeof(m_kb_state));
	m_kb_scancode = 0x7f;
	m_kb_status = 0;

	m_beeper->set_state(0);

	m_prt_irl = false;
}

TIMER_DEVICE_CALLBACK_MEMBER(hp9845_base_state::gv_timer)
{
		advance_gv_fsm(false , false);
}

attotime hp9845_base_state::time_to_gv_mem_availability() const
{
		if (m_graphic_sel) {
				int hpos = m_screen->hpos();
				if (hpos < (34 - GVIDEO_HCNT_OFF) || hpos >= (628 - GVIDEO_HCNT_OFF)) {
						// Access to graphic memory available now
						return attotime::zero;
				} else {
						// Wait until start of hblank
						return m_screen->time_until_pos(m_screen->vpos() , 628);
				}
		} else {
				// TODO:
				return attotime::zero;
		}
}

void hp9845_base_state::kb_scan_ioport(ioport_value pressed , ioport_port &port , unsigned idx_base , int& max_seq_len , unsigned& max_seq_idx)
{
	while (pressed) {
		unsigned bit_no = 31 - count_leading_zeros_32(pressed);
		ioport_value mask = BIT_MASK(bit_no);
		int seq_len = port.field(mask)->seq().length();
		if (seq_len > max_seq_len) {
			max_seq_len = seq_len;
			max_seq_idx = bit_no + idx_base;
		}
		pressed &= ~mask;
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(hp9845_base_state::kb_scan)
{
		ioport_value input[ 4 ]{
				m_io_key[0]->read(),
				m_io_key[1]->read(),
				m_io_key[2]->read(),
				m_io_key[3]->read() };

		// Shift lock
		ioport_value shiftlock = m_io_shiftlock->read();

		// Set status bits for "shift", "control", "auto start" & "print all" keys
		// ** Print all **
		// (R,C) = (0,1)
		// Bit 12 in kb status
		if (BIT(input[ 0 ] , 1)) {
				BIT_SET(m_kb_status , 12);
				BIT_CLR(input[ 0 ] , 1);
		} else {
				BIT_CLR(m_kb_status, 12);
		}
		// ** Auto start **
		// (R,C) = (1,1)
		// Bit 13 in kb status
		if (BIT(input[ 0 ] , 17)) {
				BIT_SET(m_kb_status , 13);
				BIT_CLR(input[ 0 ] , 17);
		} else {
				BIT_CLR(m_kb_status, 13);
		}
		// ** Control **
		// (R,C) = (4,15)
		// Bit 14 in kb status
		if (BIT(input[ 2 ] , 15)) {
				BIT_SET(m_kb_status , 14);
				BIT_CLR(input[ 2 ] , 15);
		} else {
				BIT_CLR(m_kb_status, 14);
		}
		// ** Shift **
		// (R,C) = (0,15)
		// Bit 15 in kb status
		if (BIT(input[ 0 ] , 15) || shiftlock) {
				BIT_SET(m_kb_status , 15);
				BIT_CLR(input[ 0 ] , 15);
		} else {
				BIT_CLR(m_kb_status, 15);
		}

		int max_seq_len = 0;
		unsigned max_seq_idx = 0;
		for (unsigned i = 0; 4 > i; ++i)
			kb_scan_ioport(input[i] & ~m_kb_state[i] , *m_io_key[i] , i << 5 , max_seq_len , max_seq_idx);
		// TODO: handle repeat key
		// TODO: handle ctrl+stop

		if (max_seq_len) {
			// Key pressed, store scancode & generate IRL
			m_kb_scancode = max_seq_idx;
			BIT_SET(m_kb_status, 0);
			update_kb_prt_irq();

			// Special case: pressing stop key sets LPU "status" flag
			if (max_seq_idx == 0x47) {
				m_lpu->status_w(1);
			}
		}

		memcpy(&m_kb_state[ 0 ] , &input[ 0 ] , sizeof(m_kb_state));
}

uint16_t hp9845_base_state::kb_scancode_r()
{
		return ~m_kb_scancode & 0x7f;
}

uint16_t hp9845_base_state::kb_status_r()
{
		return m_kb_status;
}

void hp9845_base_state::kb_irq_clear_w(uint16_t data)
{
		BIT_CLR(m_kb_status, 0);
		update_kb_prt_irq();
		m_lpu->status_w(0);

		if (BIT(data , 15)) {
			// Start beeper
			m_beep_timer->adjust(attotime::from_ticks(64, KEY_SCAN_OSCILLATOR / 512));
			m_beeper->set_state(1);
		}
}

void hp9845_base_state::update_kb_prt_irq()
{
	bool state = BIT(m_kb_status , 0) || m_prt_irl;
	m_io_sys->set_irq(0 , state);
}

void hp9845_base_state::set_irq_slot(unsigned slot , int state)
{
	int sc = m_slot_sc[ slot ];
	assert(sc >= 0);
	m_io_sys->set_irq(uint8_t(sc) , state);
}

void hp9845_base_state::set_sts_slot(unsigned slot , int state)
{
	int sc = m_slot_sc[ slot ];
	assert(sc >= 0);
	m_io_sys->set_sts(uint8_t(sc) , state);
}

void hp9845_base_state::set_flg_slot(unsigned slot , int state)
{
	int sc = m_slot_sc[ slot ];
	assert(sc >= 0);
	m_io_sys->set_flg(uint8_t(sc) , state);
}

void hp9845_base_state::set_irq_nextsc_slot(unsigned slot , int state)
{
	int sc = m_slot_sc[ slot ];
	assert(sc >= 0);
	m_io_sys->set_irq(uint8_t(sc + 1) , state);
}

void hp9845_base_state::set_sts_nextsc_slot(unsigned slot , int state)
{
	int sc = m_slot_sc[ slot ];
	assert(sc >= 0);
	m_io_sys->set_sts(uint8_t(sc + 1) , state);
}

void hp9845_base_state::set_flg_nextsc_slot(unsigned slot , int state)
{
	int sc = m_slot_sc[ slot ];
	assert(sc >= 0);
	m_io_sys->set_flg(uint8_t(sc + 1) , state);
}

void hp9845_base_state::set_dmar_slot(unsigned slot , int state)
{
	int sc = m_slot_sc[ slot ];
	assert(sc >= 0);
	m_io_sys->set_dmar(uint8_t(sc) , state);
}

TIMER_DEVICE_CALLBACK_MEMBER(hp9845_base_state::beeper_off)
{
	m_beeper->set_state(0);
}

void hp9845_base_state::prt_irl_w(int state)
{
	m_prt_irl = state;
	update_kb_prt_irq();
}

INPUT_CHANGED_MEMBER(hp9845_base_state::togglekey_changed)
{
	uint32_t togglekey = param;
	switch (togglekey) {
	case 0: // Shift lock
		{
			bool state = m_io_shiftlock->read();
			popmessage("SHIFT LOCK %s", state ? "ON" : "OFF");
			m_shift_lock_led = state;
		}
		break;
	case 1: // Prt all
		{
			bool state = BIT(m_io_key[0]->read(), 1);
			popmessage("PRT ALL %s", state ? "ON" : "OFF");
			m_prt_all_led = state;
		}
		break;
	case 2: // Auto st
		{
			bool state = BIT(m_io_key[0]->read(), 17);
			popmessage("AUTO ST %s", state ? "ON" : "OFF");
			m_auto_st_led = state;
		}
		break;
	}
}

// ***************
//  hp9845b_state
// ***************
class hp9845b_state : public hp9845_base_state
{
public:
	hp9845b_state(const machine_config &mconfig, device_type type, const char *tag);

	void hp9845b(machine_config &config);

private:
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual uint16_t graphic_r(offs_t offset) override;
	virtual void graphic_w(offs_t offset, uint16_t data) override;

	TIMER_DEVICE_CALLBACK_MEMBER(scanline_timer);

	void vblank_w(int state);

	void set_graphic_mode(bool graphic);
	void set_video_mar(uint16_t mar);
	void video_fill_buff(bool buff_idx);
	void video_render_buff(unsigned video_scanline , unsigned line_in_row, bool buff_idx);
	void graphic_video_render(unsigned video_scanline);

	virtual void advance_gv_fsm(bool ds , bool trigger) override;
	void update_graphic_bits();

	// Optional character generator
	required_region_ptr<uint8_t> m_optional_chargen;

	uint8_t m_video_attr = 0;
	uint16_t m_gv_cursor_w = 0;   // U38 & U39 (GS)
	std::vector<uint16_t> m_graphic_mem;
};

hp9845b_state::hp9845b_state(const machine_config &mconfig, device_type type, const char *tag)
	: hp9845_base_state(mconfig , type , tag),
	  m_optional_chargen(*this , "optional_chargen")
{
}

uint32_t hp9845b_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if (m_graphic_sel) {
		copybitmap(bitmap, m_bitmap, 0, 0, GVIDEO_HBEND, GVIDEO_VBEND, cliprect);
	} else {
		copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
	}

	return 0;
}

void hp9845b_state::machine_start()
{
	// Common part first
	hp9845_base_state::machine_start();

	m_graphic_mem.resize(GVIDEO_MEM_SIZE);

	// initialize palette
	m_palette->set_pen_color(PEN_BLACK  , 0x00, 0x00, 0x00);    // black
	m_palette->set_pen_color(PEN_GRAPHIC, 0x00, I_GR, 0x00);    // graphics
	m_palette->set_pen_color(PEN_ALPHA  , 0x00, I_AL, 0x00);    // alpha
	m_palette->set_pen_color(PEN_CURSOR , 0x00, I_CU, 0x00);    // graphics cursor
}

void hp9845b_state::machine_reset()
{
	// Common part first
	hp9845_base_state::machine_reset();

	set_video_mar(0);
	m_video_attr = 0;
	update_graphic_bits();
}

uint16_t hp9845b_state::graphic_r(offs_t offset)
{
	uint16_t res = 0;

	switch (offset) {
	case 0:
		// R4: data register
		res = m_gv_data_r;
		advance_gv_fsm(true , false);
		break;

	case 1:
		// R5: status register
		if (m_gv_int_en) {
			BIT_SET(res, 7);
		}
		if (m_gv_dma_en) {
			BIT_SET(res, 6);
		}
		BIT_SET(res, 5);    // ID
		break;

	case 2:
		// R6: data register with DMA TC
		m_gv_dma_en = false;
		res = m_gv_data_r;
		advance_gv_fsm(true , false);
		break;

	case 3:
		// R7: not mapped
		break;
	}

	//logerror("rd gv R%u = %04x\n", 4 + offset , res);

	return res;
}

void hp9845b_state::graphic_w(offs_t offset, uint16_t data)
{
		//logerror("wr gv R%u = %04x\n", 4 + offset , data);

		switch (offset) {
		case 0:
				// R4: data register
				m_gv_data_w = data;
				m_gv_cursor_w = data;
				advance_gv_fsm(true , false);
				break;

		case 1:
				// R5: command register
				m_gv_cmd = (uint8_t)(data & 0xf);
				m_gv_dma_en = BIT(data , 6) != 0;
				m_gv_int_en = BIT(data , 7) != 0;
				if (BIT(data , 5)) {
					m_gv_fsm_state = GV_STAT_RESET;
				}
				advance_gv_fsm(false , false);
				break;

		case 2:
				// R6: data register with DMA TC
				m_gv_dma_en = false;
				m_gv_data_w = data;
				m_gv_cursor_w = data;
				advance_gv_fsm(true , false);
				break;

		case 3:
				// R7: trigger
				advance_gv_fsm(false , true);
				break;
		}
}

TIMER_DEVICE_CALLBACK_MEMBER(hp9845b_state::scanline_timer)
{
	unsigned video_scanline = param;

	if (m_graphic_sel) {
		if (video_scanline >= GVIDEO_VBEND && video_scanline < GVIDEO_VBSTART) {
			graphic_video_render(video_scanline);
		}
	} else if (video_scanline < VIDEO_ACTIVE_SCANLINES) {
		unsigned row = video_scanline / VIDEO_CHAR_HEIGHT;
		unsigned line_in_row = video_scanline - row * VIDEO_CHAR_HEIGHT;

		if (line_in_row == 0) {
			// Start of new row, swap buffers
			m_video_buff_idx = !m_video_buff_idx;
			video_fill_buff(!m_video_buff_idx);
		}

		video_render_buff(video_scanline , line_in_row , m_video_buff_idx);
	}
}

void hp9845b_state::vblank_w(int state)
{
	// VBlank signal is fed into HALT flag of PPU
	m_ppu->halt_w(state);

	if (state) {
		// Start of V blank
		set_video_mar(0);
		m_video_load_mar = true;
		m_video_first_mar = true;
		m_video_byte_idx = false;
		m_video_blanked = false;
		m_video_buff_idx = !m_video_buff_idx;
		video_fill_buff(!m_video_buff_idx);
	}
}

void hp9845b_state::set_graphic_mode(bool graphic)
{
	if (graphic != m_graphic_sel) {
		m_graphic_sel = graphic;
		logerror("GS=%d\n" , graphic);
		if (m_graphic_sel) {
			m_screen->configure(GVIDEO_HTOTAL , GVIDEO_VTOTAL , rectangle(GVIDEO_HBEND , GVIDEO_HBSTART - 1 , GVIDEO_VBEND , GVIDEO_VBSTART - 1) , HZ_TO_ATTOSECONDS(VIDEO_PIXEL_CLOCK) * GVIDEO_HTOTAL * GVIDEO_VTOTAL);
			// Set graphic mode view (1.23:1 aspect ratio)
			machine().render().first_target()->set_view(1);
		} else {
			m_screen->configure(VIDEO_HTOTAL , VIDEO_VTOTAL , rectangle(0 , VIDEO_HBSTART - 1 , 0 , VIDEO_ACTIVE_SCANLINES - 1) , HZ_TO_ATTOSECONDS(VIDEO_PIXEL_CLOCK) * VIDEO_HTOTAL * VIDEO_VTOTAL);
			// Set alpha mode view (1.92:1 aspect ratio)
			machine().render().first_target()->set_view(0);
		}
	}
}

void hp9845b_state::set_video_mar(uint16_t mar)
{
	m_video_mar = (mar & 0xfff) | VIDEO_BUFFER_BASE_HIGH;
}

void hp9845b_state::video_fill_buff(bool buff_idx)
{
	unsigned char_idx = 0;
	unsigned iters = 0;
	uint8_t byte;
	address_space& prog_space = m_ppu->space(AS_PROGRAM);

	m_video_buff[ buff_idx ].full = false;

	while (1) {
		if (!m_video_byte_idx) {
			if (iters++ >= MAX_WORD_PER_ROW) {
				// Limit on accesses per row reached
				break;
			}
			m_video_word = prog_space.read_word(m_video_mar);
			if (m_video_load_mar) {
				// Load new address into MAR after start of a new frame or NWA instruction
				if (m_video_first_mar) {
					set_graphic_mode(!BIT(m_video_word , 15));
					m_video_first_mar = false;
				}
				set_video_mar(~m_video_word);
				m_video_load_mar = false;
				continue;
			} else {
				// Read normal word from frame buffer, start parsing at MSB
				set_video_mar(m_video_mar + 1);
				byte = (uint8_t)(m_video_word >> 8);
				m_video_byte_idx = true;
			}
		} else {
			// Parse LSB
			byte = (uint8_t)(m_video_word & 0xff);
			m_video_byte_idx = false;
		}
		if ((byte & 0xc0) == 0x80) {
			// Attribute command
			m_video_attr = byte & 0x1f;
		} else if ((byte & 0xc1) == 0xc0) {
			// New Word Address (NWA)
			m_video_load_mar = true;
			m_video_byte_idx = false;
		} else if ((byte & 0xc1) == 0xc1) {
			// End of line (EOL)
			// Fill rest of buffer with spaces
			memset(&m_video_buff[ buff_idx ].chars[ char_idx ] , 0x20 , 80 - char_idx);
			memset(&m_video_buff[ buff_idx ].attrs[ char_idx ] , m_video_attr , 80 - char_idx);
			m_video_buff[ buff_idx ].full = true;
			break;
		} else {
			// Normal character
			m_video_buff[ buff_idx ].chars[ char_idx ] = byte;
			m_video_buff[ buff_idx ].attrs[ char_idx ] = m_video_attr;
			char_idx++;
			if (char_idx == 80) {
				m_video_buff[ buff_idx ].full = true;
				break;
			}
		}
	}
}

void hp9845b_state::video_render_buff(unsigned video_scanline , unsigned line_in_row, bool buff_idx)
{
	if (!m_video_buff[ buff_idx ].full) {
		m_video_blanked = true;
	}

	const pen_t *pen = m_palette->pens();

	if (m_video_blanked) {
		// Blank scanline
		for (unsigned i = 0; i < VIDEO_HBSTART; i++) {
			m_bitmap.pix(video_scanline , i) = pen[ PEN_BLACK ];
		}
	} else {
		bool cursor_line = line_in_row == 12;
		bool ul_line = line_in_row == 14;
		unsigned video_frame = (unsigned)m_screen->frame_number();
		bool cursor_blink = BIT(video_frame , 3);
		bool char_blink = BIT(video_frame , 4);

		for (unsigned i = 0; i < 80; i++) {
			uint8_t charcode = m_video_buff[ buff_idx ].chars[ i ];
			uint8_t attrs = m_video_buff[ buff_idx ].attrs[ i ];
			uint16_t chrgen_addr = ((uint16_t)(charcode ^ 0x7f) << 4) | line_in_row;
			uint16_t pixels;

			if ((ul_line && BIT(attrs , 3)) ||
				(cursor_line && cursor_blink && BIT(attrs , 0))) {
				pixels = ~0;
			} else if (char_blink && BIT(attrs , 2)) {
				pixels = 0;
			} else if (BIT(attrs , 4)) {
				pixels = (uint16_t)(m_optional_chargen[ chrgen_addr ] & 0x7f) << 1;
			} else {
				pixels = (uint16_t)(m_chargen[ chrgen_addr ] & 0x7f) << 1;
			}

			if (BIT(attrs , 1)) {
				pixels = ~pixels;
			}

			for (unsigned j = 0; j < 9; j++) {
				bool pixel = (pixels & (1U << j)) != 0;

				m_bitmap.pix(video_scanline , i * 9 + j) = pen[ pixel ? PEN_ALPHA : PEN_BLACK ];
			}
		}
	}
}

void hp9845b_state::graphic_video_render(unsigned video_scanline)
{
	const pen_t *pen = m_palette->pens();
	bool yc = (video_scanline + GVIDEO_VCNT_OFF) == (m_gv_cursor_y + 6);
	bool yw;
	bool blink;

	if (m_gv_cursor_fs) {
		yw = true;
		// Steady cursor
		blink = true;
	} else {
		yw = (video_scanline + GVIDEO_VCNT_OFF) >= (m_gv_cursor_y + 2) &&
			(video_scanline + GVIDEO_VCNT_OFF) <= (m_gv_cursor_y + 10);
		// Blinking cursor (frame freq. / 16)
		blink = BIT(m_screen->frame_number() , 3) != 0;
	}

	unsigned mem_idx = 36 * (video_scanline - GVIDEO_VBEND);
	for (unsigned i = 0; i < GVIDEO_HPIXELS; i += 16) {
		uint16_t word = m_graphic_mem[ mem_idx++ ];
		unsigned x = i;
		for (uint16_t mask = 0x8000; mask != 0; mask >>= 1) {
			unsigned cnt_h = x + GVIDEO_HBEND + GVIDEO_HCNT_OFF;
			bool xc = cnt_h == (m_gv_cursor_x + 6);
			bool xw = m_gv_cursor_fs || (cnt_h >= (m_gv_cursor_x + 2) && cnt_h <= (m_gv_cursor_x + 10));
			unsigned pixel;
			if (blink && ((xw && yc) || (yw && xc && m_gv_cursor_gc))) {
				// Cursor
				pixel = PEN_CURSOR;
			} else {
				// Normal pixel
				pixel = (word & mask) != 0 ? PEN_GRAPHIC : PEN_BLACK;
			}
			m_bitmap.pix(video_scanline - GVIDEO_VBEND , x++) = pen[ pixel ];
		}
	}
}

void hp9845b_state::advance_gv_fsm(bool ds , bool trigger)
{
	bool get_out = false;

	attotime time_mem_av;

	do {
		bool act_trig = trigger || m_gv_dma_en || !BIT(m_gv_cmd , 2);

		switch (m_gv_fsm_state) {
		case GV_STAT_WAIT_DS_0:
			if ((m_gv_cmd & 0xc) == 0xc) {
				// Read command (11xx)
				m_gv_fsm_state = GV_STAT_WAIT_MEM_0;
			} else if (ds) {
				// Wait for data strobe (r/w on r4 or r6)
				m_gv_fsm_state = GV_STAT_WAIT_TRIG_0;
			} else {
				get_out = true;
			}
			break;

		case GV_STAT_WAIT_TRIG_0:
			// Wait for trigger
			if (act_trig) {
				if (BIT(m_gv_cmd , 3)) {
					// Not a cursor command
					// Load memory address
					m_gv_io_counter = ~m_gv_data_w & GVIDEO_ADDR_MASK;
					// Write commands (10xx)
					m_gv_fsm_state = GV_STAT_WAIT_DS_2;
				} else {
					// Cursor command (0xxx)
					if (BIT(m_gv_cmd , 2)) {
						// Write X cursor position (01xx)
						m_gv_cursor_x = (~m_gv_cursor_w >> 6) & 0x3ff;
					} else {
						// Write Y cursor position and type (00xx)
						m_gv_cursor_y = (~m_gv_cursor_w >> 6) & 0x1ff;
						m_gv_cursor_gc = BIT(m_gv_cmd , 1) == 0;
						m_gv_cursor_fs = BIT(m_gv_cmd , 0) != 0;
					}
					m_gv_fsm_state = GV_STAT_WAIT_DS_0;
				}
			} else {
				get_out = true;
			}
			break;

		case GV_STAT_WAIT_MEM_0:
			time_mem_av = time_to_gv_mem_availability();
			if (time_mem_av.is_zero()) {
				// Read a word from graphic memory
				m_gv_data_r = m_graphic_mem[ m_gv_io_counter ];
				m_gv_io_counter = (m_gv_io_counter + 1) & GVIDEO_ADDR_MASK;
				m_gv_fsm_state = GV_STAT_WAIT_DS_1;
			} else {
				m_gv_timer->adjust(time_mem_av);
				get_out = true;
			}
			break;

		case GV_STAT_WAIT_DS_1:
			if (ds) {
				m_gv_fsm_state = GV_STAT_WAIT_MEM_0;
			} else {
				get_out = true;
			}
			break;

		case GV_STAT_WAIT_DS_2:
			// Wait for data word to be written
			if (ds) {
				m_gv_fsm_state = GV_STAT_WAIT_TRIG_1;
			} else {
				get_out = true;
			}
			break;

		case GV_STAT_WAIT_TRIG_1:
			// Wait for trigger
			if (act_trig) {
				if (BIT(m_gv_cmd , 1)) {
					// Clear words (101x)
					m_gv_data_w = 0;
					m_gv_fsm_state = GV_STAT_WAIT_MEM_1;
				} else if (BIT(m_gv_cmd , 0)) {
					// Write a single pixel (1001)
					m_gv_fsm_state = GV_STAT_WAIT_MEM_2;
				} else {
					// Write words (1000)
					m_gv_fsm_state = GV_STAT_WAIT_MEM_1;
				}
			} else {
				get_out = true;
			}
			break;

		case GV_STAT_WAIT_MEM_1:
			time_mem_av = time_to_gv_mem_availability();
			if (time_mem_av.is_zero()) {
				// Write a full word to graphic memory
				m_graphic_mem[ m_gv_io_counter ] = m_gv_data_w;
				m_gv_io_counter = (m_gv_io_counter + 1) & GVIDEO_ADDR_MASK;
				m_gv_fsm_state = GV_STAT_WAIT_DS_2;
			} else {
				m_gv_timer->adjust(time_mem_av);
				get_out = true;
			}
			break;

		case GV_STAT_WAIT_MEM_2:
			time_mem_av = time_to_gv_mem_availability();
			if (time_mem_av.is_zero()) {
				// Write a single pixel to graphic memory
				uint16_t mask = 0x8000 >> (m_gv_data_w & 0xf);
				if (BIT(m_gv_data_w , 15)) {
					// Set pixel
					m_graphic_mem[ m_gv_io_counter ] |= mask;
				} else {
					// Clear pixel
					m_graphic_mem[ m_gv_io_counter ] &= ~mask;
				}
				// Not really needed
				m_gv_io_counter = (m_gv_io_counter + 1) & GVIDEO_ADDR_MASK;
				m_gv_fsm_state = GV_STAT_WAIT_DS_0;
			} else {
				m_gv_timer->adjust(time_mem_av);
				get_out = true;
			}
			break;

		default:
			logerror("Invalid state reached %d\n" , m_gv_fsm_state);
			m_gv_fsm_state = GV_STAT_RESET;
		}

		ds = false;
		trigger = false;
	} while (!get_out);

	update_graphic_bits();
}

void hp9845b_state::update_graphic_bits()
{
		bool gv_ready = m_gv_fsm_state == GV_STAT_WAIT_DS_0 ||
			m_gv_fsm_state == GV_STAT_WAIT_DS_1 ||
			m_gv_fsm_state == GV_STAT_WAIT_DS_2;

		m_io_sys->set_flg(GVIDEO_PA , gv_ready);

		bool irq = m_gv_int_en && !m_gv_dma_en && gv_ready;

		m_io_sys->set_irq(GVIDEO_PA , irq);

		bool dmar = gv_ready && m_gv_dma_en;

		m_io_sys->set_dmar(GVIDEO_PA , dmar);
}

// ***************
//  hp9845ct_base_state
// ***************

class hp9845ct_base_state : public hp9845_base_state
{
public:
	hp9845ct_base_state(const machine_config &mconfig, device_type type, const char *tag);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void vblank_w(int state);
	DECLARE_INPUT_CHANGED_MEMBER(softkey_changed);

protected:
	required_ioport m_io_softkeys;
	required_ioport m_lightpen_x;
	required_ioport m_lightpen_y;
	required_ioport m_lightpen_sw;

	virtual void set_graphic_mode(bool graphic , bool alpha) = 0;
	void set_video_mar(uint16_t mar);
	void video_fill_buff(bool buff_idx);
	virtual void plot(uint16_t x, uint16_t y, bool draw_erase) = 0;
	void draw_line(unsigned x0 , unsigned y0 , unsigned x1 , unsigned y1);
	void update_line_pattern();
	void pattern_fill(uint16_t x0 , uint16_t y0 , uint16_t x1 , uint16_t y1 , unsigned fill_idx);
	static uint16_t get_gv_mem_addr(unsigned x , unsigned y);
	virtual void update_graphic_bits() = 0;
	static int get_wrapped_scanline(unsigned scanline);
	void render_lp_cursor(unsigned video_scanline , unsigned pen_idx);

	void lp_r4_w(uint16_t data);
	uint16_t lp_r4_r();
	void lp_r5_w(uint16_t data);
	bool lp_segment_intersect(unsigned yline) const;
	void compute_lp_data();
	void lp_scanline_update(unsigned video_scanline);

	virtual void update_gcursor() = 0;

	bool m_alpha_sel;
	bool m_gv_sk_en;
	bool m_gv_gr_en;
	bool m_gv_opt_en;
	bool m_gv_dsa_en;
	bool m_gv_lp_status;
	bool m_gv_sk_status;
	uint16_t m_gv_lp_cursor_x;
	uint16_t m_gv_lp_cursor_y;
	bool m_gv_lp_cursor_fs;
	bool m_gv_lp_en;
	uint8_t m_gv_last_cmd;
	uint16_t m_gv_word_x_position;
	uint16_t m_gv_word_y_position;
	uint16_t m_gv_memory_control;
	uint16_t m_gv_line_type_area_fill;
	uint16_t m_gv_line_type_mask;
	uint8_t m_gv_repeat_count;
	uint16_t m_gv_xpt;
	uint16_t m_gv_ypt;
	uint16_t m_gv_last_xpt;
	uint16_t m_gv_last_ypt;
	uint16_t m_gv_lp_data[ 3 ];
	uint16_t m_gv_next_lp_data[ 3 ];
	unsigned m_gv_next_lp_scanline[ 3 ];
	bool m_gv_lp_selftest;
	bool m_gv_lp_interlace;
	bool m_gv_lp_vblank;
	bool m_gv_lp_1sthit;
	bool m_gv_lp_vbint;
	bool m_gv_lp_fullbright;
	bool m_gv_lp_threshold;
	uint16_t m_gv_lp_x;
	uint16_t m_gv_lp_y;
	bool m_gv_lp_sw;
	uint8_t m_gv_lp_reg_cnt;
	bool m_gv_lp_int_en;
	bool m_gv_lp_hit_lt192;
	bool m_gv_lp_int_256;
	uint16_t m_gv_lxc;
	uint16_t m_gv_lyc;
	uint8_t m_gv_softkey;

	static const uint16_t m_line_type[];
	static const uint16_t m_area_fill[];
};

/*
   For 9845C and 9845T we just add the light pen support via MAME's lightgun device.

   Note that the LIGHTGUN device needs '-lightgun' and '-lightgun_device mouse' for light gun emulation if no real light gun device is installed.
 */
static INPUT_PORTS_START(hp9845ct)
	PORT_INCLUDE(hp9845_base)
	PORT_START("SOFTKEYS")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey0") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9845ct_base_state::softkey_changed), 0)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9845ct_base_state::softkey_changed), 0)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey2") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9845ct_base_state::softkey_changed), 0)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey3") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9845ct_base_state::softkey_changed), 0)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey4") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9845ct_base_state::softkey_changed), 0)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey5") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9845ct_base_state::softkey_changed), 0)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey6") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9845ct_base_state::softkey_changed), 0)
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey7") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9845ct_base_state::softkey_changed), 0)

	PORT_START("LIGHTPENX")
	PORT_BIT( 0x3ff, 0x000, IPT_LIGHTGUN_X ) PORT_SENSITIVITY(20) PORT_MINMAX(0, VIDEO_TOT_HPIXELS - 1) PORT_CROSSHAIR(X, 1.0, 0.0, 0)

	PORT_START("LIGHTPENY")
	PORT_BIT( 0x3ff, 0x000, IPT_LIGHTGUN_Y ) PORT_SENSITIVITY(20) PORT_MINMAX(0, GVIDEO_VPIXELS - 1) PORT_CROSSHAIR(Y, 1.0, 0.0, 0)

	PORT_START("GKEY")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_CODE(MOUSECODE_BUTTON1) PORT_NAME("Gkey")
INPUT_PORTS_END

static INPUT_PORTS_START(hp9845ct_de)
	PORT_INCLUDE(hp9845_base_de)
	PORT_START("SOFTKEYS")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey0") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9845ct_base_state::softkey_changed), 0)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9845ct_base_state::softkey_changed), 0)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey2") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9845ct_base_state::softkey_changed), 0)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey3") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9845ct_base_state::softkey_changed), 0)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey4") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9845ct_base_state::softkey_changed), 0)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey5") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9845ct_base_state::softkey_changed), 0)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey6") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9845ct_base_state::softkey_changed), 0)
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey7") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hp9845ct_base_state::softkey_changed), 0)

	PORT_START("LIGHTPENX")
	PORT_BIT( 0x3ff, 0x000, IPT_LIGHTGUN_X ) PORT_SENSITIVITY(20) PORT_MINMAX(0, VIDEO_TOT_HPIXELS - 1) PORT_CROSSHAIR(X, 1.0, 0.0, 0)

	PORT_START("LIGHTPENY")
	PORT_BIT( 0x3ff, 0x000, IPT_LIGHTGUN_Y ) PORT_SENSITIVITY(20) PORT_MINMAX(0, GVIDEO_VPIXELS - 1) PORT_CROSSHAIR(Y, 1.0, 0.0, 0)

	PORT_START("GKEY")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_CODE(MOUSECODE_BUTTON1) PORT_NAME("Gkey")
INPUT_PORTS_END

hp9845ct_base_state::hp9845ct_base_state(const machine_config &mconfig, device_type type, const char *tag)
	: hp9845_base_state(mconfig , type , tag),
	  m_io_softkeys(*this, "SOFTKEYS"),
	  m_lightpen_x(*this, "LIGHTPENX"),
	  m_lightpen_y(*this, "LIGHTPENY"),
	  m_lightpen_sw(*this, "GKEY")
{
}

void hp9845ct_base_state::machine_start()
{
	// Common part first
	hp9845_base_state::machine_start();
}

void hp9845ct_base_state::machine_reset()
{
	// Common part first
	hp9845_base_state::machine_reset();

	m_alpha_sel = true;
	m_gv_sk_en = false;
	m_gv_gr_en = false;
	m_gv_opt_en = false;
	m_gv_dsa_en = false;
	m_gv_lp_status = false;
	m_gv_sk_status = false;
	m_gv_lp_cursor_x = 944;
	m_gv_lp_cursor_y = 50;
	m_gv_lp_cursor_fs = false;
	m_gv_lp_en = false;
	m_gv_last_cmd = 0;
	m_gv_word_x_position = 0;
	m_gv_word_y_position = 0;
	m_gv_memory_control = 0;
	m_gv_line_type_area_fill = 0;
	m_gv_line_type_mask = 0xffff;
	m_gv_repeat_count = 0;
	m_gv_xpt = 0;
	m_gv_ypt = 0;
	m_gv_last_xpt = 0;
	m_gv_last_ypt = 0;
	m_gv_lp_selftest = false;
	m_gv_lp_interlace = false;
	m_gv_lp_vblank = false;
	m_gv_lp_1sthit = false;
	m_gv_lp_vbint = false;
	m_gv_lp_fullbright = false;
	m_gv_lp_threshold = false;
	m_gv_lp_x = 0;
	m_gv_lp_y = 0;
	m_gv_lp_sw = false;
	m_gv_lp_int_en = false;

	update_graphic_bits();
}

uint32_t hp9845ct_base_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);

	return 0;
}

void hp9845ct_base_state::vblank_w(int state)
{
	// VBlank signal is fed into HALT flag of PPU
	m_ppu->halt_w(state);

	if (state) {
		// Start of V blank
		set_video_mar(0);
		m_video_load_mar = true;
		m_video_first_mar = true;
		m_video_blanked = false;
		m_video_buff_idx = !m_video_buff_idx;
		video_fill_buff(!m_video_buff_idx);

		// lightpen
		m_gv_lp_vblank = true;
		m_gv_lp_sw = m_lightpen_sw->read();
		m_gv_lp_x = m_lightpen_x->read();
		if (m_gv_lp_x > (VIDEO_TOT_HPIXELS - 1)) {
			m_gv_lp_x = VIDEO_TOT_HPIXELS - 1;
		}
		m_gv_lp_y = m_lightpen_y->read();
		if (m_gv_lp_y > (GVIDEO_VPIXELS - 1)) {
			m_gv_lp_y = GVIDEO_VPIXELS - 1;
		}
	} else {
		m_gv_lp_vblank = false;
		update_gcursor();
	}
}

INPUT_CHANGED_MEMBER(hp9845ct_base_state::softkey_changed)
{
	uint8_t softkey_data = m_io_softkeys->read();
	unsigned softkey;
	for (softkey = 0; softkey < 8; softkey++) {
		m_softkeys[softkey] = !BIT(softkey_data , 7 - softkey);
	}
	for (softkey = 0; softkey < 8 && BIT(softkey_data , 7 - softkey); softkey++) {
	}
	LOG("SK %02x => %u\n" , softkey_data , softkey);
	if (softkey < 8 && !m_gv_sk_status) {
		// softkey pressed
		m_gv_softkey = softkey;
		m_gv_sk_status = true;
		update_graphic_bits();
	}
}

void hp9845ct_base_state::set_video_mar(uint16_t mar)
{
	m_video_mar = (mar & 0x1fff) | VIDEO_BUFFER_BASE_LOW;
}

void hp9845ct_base_state::video_fill_buff(bool buff_idx)
{
	unsigned char_idx = 0;
	unsigned iters = 0;
	address_space& prog_space = m_ppu->space(AS_PROGRAM);

	m_video_buff[ buff_idx ].full = false;

	while (1) {
		if ((m_video_mar & 0x1fff) > 0x1dff) {
			// CRT buffer ends at 0x7dff
			break;
		}
		// Get video word
		if (iters++ >= MAX_WORD_PER_ROW) {
			// Limit on accesses per row reached
			break;
		}
		m_video_word = prog_space.read_word(m_video_mar);
		if (m_video_load_mar) {
			// Load new address into MAR after start of a new frame or NWA instruction
			if (m_video_first_mar) {
				set_graphic_mode(BIT(m_video_word , 15), BIT(m_video_word , 14));
				m_video_first_mar = false;
			}
			set_video_mar(~m_video_word);
			m_video_load_mar = false;
			continue;
		} else {
			// Update counter for next word fetch
			set_video_mar(m_video_mar + 1);
		}
		// Parse video word
		if (m_video_word == 0x8020) {
			// End-of-line (EOL)
			// Fill rest of buffer with spaces
			memset(&m_video_buff[ buff_idx ].chars[ char_idx ] , 0x20 , 80 - char_idx);
			memset(&m_video_buff[ buff_idx ].attrs[ char_idx ] , 0 , 80 - char_idx);
			m_video_buff[ buff_idx ].full = true;
			break;
		} else if ((m_video_word & 0xc020) == 0x8000) {
			// New word address (NWA)
			m_video_load_mar = true;
		} else if ((m_video_word & 0xc000) == 0xc000) {
			// NOP
		} else {
			// fill line buffer
			m_video_buff[ buff_idx ].chars[ char_idx ] = (uint8_t)(m_video_word & 0xff);
			m_video_buff[ buff_idx ].attrs[ char_idx ] = (uint8_t)(m_video_word >> 8);
			char_idx++;
			if (char_idx == 80) {
				m_video_buff[ buff_idx ].full = true;
				break;
			}
		}
	}
}

void hp9845ct_base_state::draw_line(unsigned x0 , unsigned y0 , unsigned x1 , unsigned y1)
{
	int dx, dy, sx, sy, x, y, err, e2;

	// draw line, vector generator uses Bresenham's algorithm
	x = x0;
	y = y0;
	dx = abs((int) (x1 - x));
	sx = x < x1 ? 1 : -1;   // actually always 1 because of normalization
	dy = abs((int) (y1 - y));
	sy = y < y1 ? 1 : -1;
	err = (dx > dy ? dx : -dy) / 2;

	for(;;){
		plot(x, y, BIT(m_gv_line_type_mask, 15));
		update_line_pattern();

		if (x == x1 && y == y1) break;

		e2 = err;
		if (e2 > -dx) {
			err -= dy;
			x += sx;
		}
		if (e2 < dy) {
			err += dx;
			y += sy;
		}
	}
}

void hp9845ct_base_state::update_line_pattern()
{
	// update line pattern
	m_gv_repeat_count++;
	if (m_gv_repeat_count > ((m_gv_line_type_area_fill >> 5) & 0xf)) {
		// Rotate m_gv_line_type_mask 1 bit to the left
		bool save_bit = BIT(m_gv_line_type_mask , 15);
		m_gv_line_type_mask = save_bit | (m_gv_line_type_mask << 1);
		m_gv_repeat_count = 0;
	}
}

void hp9845ct_base_state::pattern_fill(uint16_t x0 , uint16_t y0 , uint16_t x1 , uint16_t y1 , unsigned fill_idx)
{
	uint16_t x,y,xmax,ymax;
	uint16_t pixel_mask, fill_mask;

	x = std::min(x0 , x1);
	xmax = std::max(x0 , x1);
	y = std::min(y0 , y1);
	ymax = std::max(y0 , y1);

	for (;y <= ymax; y++) {
		fill_mask = (m_area_fill[ fill_idx ] << (y % 4) * 4) & 0xf000;
		fill_mask |= (fill_mask >> 4) | (fill_mask >> 8) | (fill_mask >> 12);
		for (;x <= xmax; x++) {
			pixel_mask = (0x8000 >> (x % 16));
			plot(x , y , (pixel_mask & fill_mask) != 0);
		}
	}
}

uint16_t hp9845ct_base_state::get_gv_mem_addr(unsigned x , unsigned y)
{
	return (uint16_t)((x + y * 35) & GVIDEO_ADDR_MASK);
}

int hp9845ct_base_state::get_wrapped_scanline(unsigned scanline)
{
	// The 770's VTOTAL applies to 780, too.
	// The 780 hw generates a line clock (GVclk in Duell's schematics) that's suppressed
	// for lines in [485..524] range so that the total number of lines per frame counted
	// by this clock matches the total count of lines in 770 (i.e. 485).
	int wrapped_cursor_y = (int)scanline;
	if (wrapped_cursor_y >= GVIDEO_VPIXELS && wrapped_cursor_y < VIDEO_770_VTOTAL) {
		wrapped_cursor_y -= VIDEO_770_VTOTAL;
	}

	return wrapped_cursor_y;
}

void hp9845ct_base_state::render_lp_cursor(unsigned video_scanline , unsigned pen_idx)
{
	int cursor_y_top = get_wrapped_scanline(m_gv_lp_cursor_y);

	bool yw;
	if (m_gv_lp_cursor_fs) {
		yw = true;
	} else {
		yw = (int)video_scanline >= cursor_y_top &&
			(int)video_scanline <= (cursor_y_top + 48);
	}
	if (!yw) {
		return;
	}

	if (!m_gv_lp_cursor_fs && m_gv_lp_cursor_x >= (VIDEO_TOT_HPIXELS + 24)) {
		return;
	}

	bool yc = video_scanline == (cursor_y_top + 24);

	const pen_t &pen = m_palette->pen(pen_idx);
	if (!yc) {
		if (m_gv_lp_cursor_x < VIDEO_TOT_HPIXELS) {
			m_bitmap.pix(video_scanline , m_gv_lp_cursor_x) = pen;
		}
	} else if (m_gv_lp_cursor_fs) {
		for (unsigned x = 0; x < VIDEO_TOT_HPIXELS; x++) {
			m_bitmap.pix(video_scanline , x) = pen;
		}
	} else {
		for (unsigned x = std::max(0 , (int)m_gv_lp_cursor_x - 24); x <= (m_gv_lp_cursor_x + 25) && x < VIDEO_TOT_HPIXELS; x++) {
			m_bitmap.pix(video_scanline , x) = pen;
		}
	}
}

void hp9845ct_base_state::lp_r4_w(uint16_t data)
{
	if (m_gv_lp_en) {
		switch (m_gv_lp_reg_cnt) {
		case 2:
			// LP Y cursor + threshold + interlace + vertical blank interrupt
			m_gv_lp_cursor_y = ((~data >> 6) & 0x1ff);
			m_gv_lp_fullbright = BIT(data, 1);
			m_gv_lp_threshold = BIT(data, 3);
			m_gv_lp_interlace = !BIT(data, 4);
			m_gv_lp_vbint = BIT(data, 5);
			LOG("LP Y cursor y = %d, threshold = %d, interlace = %d, vbint = %d\n",
				 m_gv_lp_cursor_y, m_gv_lp_threshold, m_gv_lp_interlace, m_gv_lp_vbint);
			m_gv_lp_reg_cnt--;
			break;

		case 3:
			// LP X cursor + cursor type
			m_gv_lp_cursor_x = ((data >> 6) & 0x3ff) + 1;
			m_gv_lp_cursor_fs = !BIT(data, 0);
			LOG("LP X cursor x = %d, fs = %d\n", m_gv_lp_cursor_x, m_gv_lp_cursor_fs);
			m_gv_lp_reg_cnt--;
			break;

		default:
			logerror("Writing to unmapped LP register %u\n" , m_gv_lp_reg_cnt);
		}
	}
}

uint16_t hp9845ct_base_state::lp_r4_r()
{
	uint16_t res = 0;

	if (m_gv_lp_en) {
		switch (m_gv_lp_reg_cnt) {
		case 4:
			// YLO
			res = m_gv_lp_data[ 2 ];
			m_gv_lp_reg_cnt--;
			m_gv_lp_status = false;
			m_gv_lp_1sthit = false;
			update_graphic_bits();
			break;

		case 5:
			// XLEFT
			res = m_gv_lp_data[ 1 ];
			m_gv_lp_reg_cnt--;
			break;

		case 6:
			// YHI
			res = m_gv_lp_data[ 0 ];
			if (!m_gv_lp_vblank) {
				BIT_SET(res, 12);
			}
			if (m_gv_lp_sw) {
				BIT_SET(res, 14);
			}
			if (m_gv_lp_1sthit) {
				BIT_SET(res, 15);
			}
			m_gv_lp_reg_cnt--;
			break;

		default:
			logerror("Reading from unmapped LP register %u\n" , m_gv_lp_reg_cnt);
		}
	}
	return res;
}

void hp9845ct_base_state::lp_r5_w(uint16_t data)
{
	m_gv_lp_reg_cnt = data & 7;
	m_gv_lp_en = (data & 0x700) == 0x400;   // enables writes on R4 to set LP data (actually FB bit), also enables LP command processing and LP IRQs
	m_gv_lp_int_en = (data & 0x500) == 0x400;
	m_gv_lp_selftest = m_gv_lp_en && m_gv_lp_reg_cnt == 7;
	update_graphic_bits();
}

bool hp9845ct_base_state::lp_segment_intersect(unsigned yline) const
{
	int xp = m_gv_lp_x;
	int yp = m_gv_lp_y;
	int xc = (int)m_gv_lp_cursor_x + 1 - LP_XOFFSET;

	unsigned h = (unsigned)abs((int)yline - yp);

	if (h > LP_FOV) {
		return false;
	}

	int dt = (int)sqrt(LP_FOV * LP_FOV - h * h);
	int ex = xp - dt;
	int fx = xp + dt;

	// Consider [xc..xc+24] segment (i.e. the right-hand side of cursor interlace window)
	return fx >= xc && ex <= (xc + 24);
}

void hp9845ct_base_state::compute_lp_data()
{
	// get LP hit data, returns three words for cmd=6 and one word for cmd=4
	// actually simulating the 9845 lightpen is a bit more complex, since YHI, XLEFT and YLO
	// depend on an circular field of view, moving on the screen
	// bit 0..10 x bzw y
	// bit 11 = IRQ (YHI + XLEFT + YLO)
	// bit 12 = vblank (YHI)
	// bit 13 = xwindow (YHI + XLEFT + YLO) = X is in [xcursor-24, xcursor+24] and Y in [ycursor-8,ycursor+8]
	// bit 14 = sw (YHI) bzw. ywindow (XLEFT + YLO)
	// bit 15 = 1st hit (YHI) = valid hit

	bool xwindow[ 3 ] = { false , false , false };
	bool ywindow[ 3 ] = { false , false , false };
	// hit coordinates
	uint16_t yhi = 0;
	uint16_t xleft = 0;
	uint16_t yleft = 0;
	uint16_t ylo = 0;
	uint16_t xp = m_gv_lp_x;                    // light gun pointer
	uint16_t yp = m_gv_lp_y;
	int yc = get_wrapped_scanline(m_gv_lp_cursor_y) + 24;

	if (m_gv_lp_selftest) {
		constexpr int offset = 57 - VIDEO_770_ALPHA_L_LIM;
		xwindow[ 0 ] = xwindow[ 1 ] = xwindow[ 2 ] = true;
		ywindow[ 0 ] = ywindow[ 1 ] = ywindow[ 2 ] = true;
		yhi = m_gv_lp_cursor_y + 16;    // YHI
		xleft = m_gv_lp_cursor_x + offset;  // XLEFT
		yleft = yhi;
		ylo = m_gv_lp_cursor_y + 32;    // YLO
	} else {
		// Hit in a cursor-only part.
		bool yhi_hit = false;
		uint16_t y_top = std::max(0 , (int)yp - (int)LP_FOV);

		// XLEFT: x coordinate of 1st hit in the frame or hit on cursor line
		unsigned dy = abs((int)yp - yc);
		if (dy <= LP_FOV) {
			// Hit on horizontal cursor line
			xleft = (uint16_t)std::max(0 , (int)xp - (int)sqrt(LP_FOV * LP_FOV - dy * dy));
			yleft = yc;
		} else {
			// 1st hit in the frame
			dy = abs((int)yp - (int)y_top);
			xleft = (uint16_t)std::max(0 , (int)xp - (int)sqrt(LP_FOV * LP_FOV - dy * dy));
			yleft = y_top;
		}
		xleft += LP_XOFFSET;

		if (m_gv_lp_interlace) {
			// **** Interlaced mode ****
			unsigned ywd_top = (unsigned)std::max(yc - 24 , 0);
			unsigned ywd_bot = std::min((unsigned)(GVIDEO_VPIXELS - 1) , (unsigned)(yc + 24));
			unsigned even_odd = (unsigned)m_screen->frame_number() & 1;

			// Scan the cursor window [yc-24..yc+24]
			// Only consider each other line. LSB of frame number selects either even-numbered
			// (0) or odd-numbered lines (1).
			for (unsigned line = ywd_top; line <= ywd_bot; line++) {
				if ((line & 1) == even_odd) {
					// YHI: y coordinate of 1st hit in the frame or 1st hit in cursor-only part
					bool curs_hit = lp_segment_intersect(line);
					if (!yhi_hit && curs_hit) {
						yhi = line;
						yhi_hit = true;
					}
					// YLO: y coordinate of last hit in cursor-only part
					if (curs_hit) {
						ylo = line;
					}
				}
			}
		}

		if (!m_gv_lp_interlace || !yhi_hit) {
			// **** Non-interlaced mode ****
			// YHI: y coordinate of 1st hit in the frame
			yhi = y_top;

			// YLO: y coordinate of last hit in the frame
			ylo = std::min((unsigned)(GVIDEO_VPIXELS - 1) , yp + LP_FOV);
		}

		xwindow[ 0 ] = yhi_hit;
		xwindow[ 1 ] = yhi_hit && yleft >= yhi;
		xwindow[ 2 ] = yhi_hit;

		ywindow[ 1 ] = yleft == yc;
		ywindow[ 2 ] = yleft == yc && ylo >= yleft;
	}
	m_gv_next_lp_data[ 0 ] = ~yhi & 0x1ff;  // YHI
	m_gv_next_lp_data[ 1 ] = ~xleft & 0x3ff;    // XLEFT
	m_gv_next_lp_data[ 2 ] = ~ylo & 0x1ff;  // YLO

	if (!xwindow[ 0 ]) {
		BIT_SET(m_gv_next_lp_data[ 0 ], 13);
	}
	if (!xwindow[ 1 ]) {
		BIT_SET(m_gv_next_lp_data[ 1 ], 13);
	}
	if (!xwindow[ 2 ]) {
		BIT_SET(m_gv_next_lp_data[ 2 ], 13);
	}
	if (!ywindow[ 1 ]) {
		BIT_SET(m_gv_next_lp_data[ 1 ], 14);
	}
	if (!ywindow[ 2 ]) {
		BIT_SET(m_gv_next_lp_data[ 2 ], 14);
	}

	m_gv_next_lp_scanline[ 0 ] = yhi;
	m_gv_next_lp_scanline[ 1 ] = yleft;
	m_gv_next_lp_scanline[ 2 ] = ylo;

	m_gv_lp_hit_lt192 = yhi < 192;
	LOG("LP data %d %d %d %d (%u;%d) (%u;%u) %u %u %u %04x %04x %04x\n" , m_gv_lp_selftest , m_gv_lp_interlace , m_gv_lp_sw , m_gv_lp_hit_lt192 , m_gv_lp_cursor_x , yc , m_gv_lp_x , m_gv_lp_y , yhi , xleft , ylo , m_gv_next_lp_data[ 0 ] , m_gv_next_lp_data[ 1 ] , m_gv_next_lp_data[ 2 ]);
}

void hp9845ct_base_state::lp_scanline_update(unsigned video_scanline)
{
	if (video_scanline == 0) {
		compute_lp_data();
	}

	if (video_scanline == 256 && !m_gv_lp_status && m_gv_lp_hit_lt192) {
		LOG("Hit < 192 @%d\n" , m_screen->vpos());
		m_gv_lp_status = true;
		m_gv_lp_int_256 = true;
		update_graphic_bits();
	}

	if (video_scanline == 456) {
		// VB interrupt
		if (m_gv_lp_vbint || !m_gv_lp_int_256) {
			m_gv_lp_status = true;
		}
		m_gv_lp_int_256 = false;
		update_graphic_bits();
	}

	for (unsigned i = 0; i < 3; i++) {
		if (m_gv_next_lp_scanline[ i ] == video_scanline) {
			m_gv_lp_data[ i ] = m_gv_next_lp_data[ i ];
			if (!m_gv_lp_status) {
				BIT_SET(m_gv_lp_data[ i ], 11);
			}
			m_gv_lp_1sthit = true;
		}
	}
}

const uint16_t hp9845ct_base_state::m_line_type[] = {
	0xffff, 0xaaaa, 0xff00, 0xfff0, 0xfffa, 0xfff6, 0xffb6, 0x0000
};

const uint16_t hp9845ct_base_state::m_area_fill[] = {
	0xffff, 0xefff, 0xefbf, 0xefaf, 0xafaf, 0xadaf, 0xada7, 0xada5,
	0xa5a5, 0xa4a5, 0xa4a1, 0xa4a0, 0xa0a0, 0x80a0, 0x8020, 0x8000
};

// ***************
//  hp9845c_state
// ***************
class hp9845c_state : public hp9845ct_base_state
{
public:
	hp9845c_state(const machine_config &mconfig, device_type type, const char *tag);

	void hp9845c(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual uint16_t graphic_r(offs_t offset) override;
	virtual void graphic_w(offs_t offset, uint16_t data) override;

	TIMER_DEVICE_CALLBACK_MEMBER(scanline_timer);

	virtual void set_graphic_mode(bool graphic , bool alpha) override;
	void video_render_buff(unsigned video_scanline , unsigned line_in_row, bool buff_idx);
	void graphic_video_render(unsigned video_scanline);
	virtual void plot(uint16_t x, uint16_t y, bool draw_erase) override;

	void advance_io_counter();
	virtual void advance_gv_fsm(bool ds , bool trigger) override;
	virtual void update_graphic_bits() override;

	virtual void update_gcursor() override;

	// Palette indexes
	static constexpr unsigned pen_graphic(unsigned rgb) { return rgb; }
	static constexpr unsigned pen_alpha(unsigned rgb) { return 8 + rgb; }
	static constexpr unsigned pen_cursor(unsigned rgb) { return 16 + rgb; }

	// Optional character generator
	required_region_ptr<uint8_t> m_optional_chargen;

	std::vector<uint16_t> m_graphic_mem[ 3 ];
	uint16_t m_gv_music_memory = 0;
	uint8_t m_gv_cursor_color = 0;
	uint8_t m_gv_plane = 0;
	bool m_gv_lp_int_latched = false;
	bool m_gv_sk_int_latched = false;
};

hp9845c_state::hp9845c_state(const machine_config &mconfig, device_type type, const char *tag)
	: hp9845ct_base_state(mconfig , type , tag),
	  m_optional_chargen(*this , "optional_chargen")
{
}

void hp9845c_state::machine_start()
{
	// Common part first
	hp9845ct_base_state::machine_start();

	m_graphic_mem[ 0 ].resize(GVIDEO_MEM_SIZE);
	m_graphic_mem[ 1 ].resize(GVIDEO_MEM_SIZE);
	m_graphic_mem[ 2 ].resize(GVIDEO_MEM_SIZE);

	// initialize palette
	// graphics colors
	m_palette->set_pen_color(0,  0x00, 0x00, 0x00); // black
	m_palette->set_pen_color(1,  I_GR, 0x00, 0x00); // red
	m_palette->set_pen_color(2,  0x00, I_GR, 0x00); // green
	m_palette->set_pen_color(3,  I_GR, I_GR, 0x00); // yellow
	m_palette->set_pen_color(4,  0x00, 0x00, I_GR); // blue
	m_palette->set_pen_color(5,  I_GR, 0x00, I_GR); // magenta
	m_palette->set_pen_color(6,  0x00, I_GR, I_GR); // cyan
	m_palette->set_pen_color(7,  I_GR, I_GR, I_GR); // white

	// alpha colors
	m_palette->set_pen_color(8,  0x00, 0x00, 0x00); // black
	m_palette->set_pen_color(9,  I_AL, 0x00, 0x00); // red
	m_palette->set_pen_color(10, 0x00, I_AL, 0x00); // green
	m_palette->set_pen_color(11, I_AL, I_AL, 0x00); // yellow
	m_palette->set_pen_color(12, 0x00, 0x00, I_AL); // blue
	m_palette->set_pen_color(13, I_AL, 0x00, I_AL); // magenta
	m_palette->set_pen_color(14, 0x00, I_AL, I_AL); // cyan
	m_palette->set_pen_color(15, I_AL, I_AL, I_AL); // white

	// cursor colors
	m_palette->set_pen_color(16, 0x80, 0x80, 0x80); // grey
	m_palette->set_pen_color(17, I_CU, 0x00, 0x00); // red
	m_palette->set_pen_color(18, 0x00, I_CU, 0x00); // green
	m_palette->set_pen_color(19, I_CU, I_CU, 0x00); // yellow
	m_palette->set_pen_color(20, 0x00, 0x00, I_CU); // blue
	m_palette->set_pen_color(21, I_CU, 0x00, I_CU); // magenta
	m_palette->set_pen_color(22, 0x00, I_CU, I_CU); // cyan
	m_palette->set_pen_color(23, I_CU, I_CU, I_CU); // white
}

void hp9845c_state::machine_reset()
{
	// Common part first
	hp9845ct_base_state::machine_reset();

	set_video_mar(0);

	// red -> plane #1, green -> plane #2, blue -> plane #3
	m_gv_music_memory = 0x1 | (0x2 << 3) | (0x4 << 6);
	// TODO: correct?
	m_gv_cursor_color = 7;
	m_gv_plane = 0;
	m_gv_lp_int_latched = false;
	m_gv_sk_int_latched = false;
}

uint16_t hp9845c_state::graphic_r(offs_t offset)
{
	uint16_t res = 0;

	switch (offset) {
	case 2:
		// R6: data register with DMA TC
		m_gv_dma_en = false;
		[[fallthrough]];

	case 0:
		// R4: data register
		if (m_gv_lp_en) {
			res = lp_r4_r();
		} else if (m_gv_sk_int_latched) {
			res = m_gv_softkey;
			m_gv_sk_status = false;
		} else {
			res = m_gv_data_r;
		}
		advance_gv_fsm(true , false);
		update_graphic_bits();
		break;

	case 1:
		// R5: status register
		if (m_gv_int_en) {
			BIT_SET(res, 7);
		}
		if (m_gv_dma_en) {
			BIT_SET(res, 6);
		}
		if (m_gv_lp_int_latched) {
			BIT_SET(res, 0);    // Lightpen service request
		}
		if (m_gv_sk_int_latched) {
			BIT_SET(res, 1);    // Softkey service request
		}
		// TODO: check! Should it be 10 instead?
		BIT_SET(res, 11);   // ID
		break;

	case 3:
		// R7: not mapped
		break;
	}

	LOG("rd gv R%u = %04x\n", 4 + offset , res);

	return res;
}

void hp9845c_state::graphic_w(offs_t offset, uint16_t data)
{
	LOG("wr gv R%u = %04x\n", 4 + offset , data);

	switch (offset) {
	case 0:
		// R4: data register
		m_gv_data_w = data;
		advance_gv_fsm(true , false);
		lp_r4_w(data);
		break;

	case 1:
		// R5: command register
		m_gv_cmd = (uint8_t)(data & 0xf);
		m_gv_dma_en = BIT(data , 6) != 0;
		m_gv_int_en = BIT(data , 7) != 0;
		m_gv_gr_en = BIT(data , 8); // enables graphics controller & vector generator command processing and IRQs
		m_gv_sk_en = BIT(data , 9); // enables reads on R4 to return SK keycode, also enables SK IRQs
		m_gv_opt_en = BIT(data , 11);   // not really used
		m_gv_dsa_en = BIT(data , 12);   // for factory use only (unknown)
		if (BIT(data, 5)) {
			m_gv_fsm_state = GV_STAT_RESET;     // command/reset state machine
		}
		advance_gv_fsm(false , false);
		lp_r5_w(data);
		update_graphic_bits();
		break;

	case 2:
		// R6: data register with DMA TC
		m_gv_dma_en = false;
		m_gv_data_w = data;
		advance_gv_fsm(true , false);
		lp_r4_w(data);
		break;

	case 3:
		// R7: trigger
		advance_gv_fsm(false , true);
		break;
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(hp9845c_state::scanline_timer)
{
	unsigned video_scanline = param;

	if (video_scanline >= VIDEO_770_VBEND && video_scanline < VIDEO_770_VBSTART) {
		if (m_graphic_sel) {
			graphic_video_render(video_scanline - VIDEO_770_VBEND);
		}
		unsigned row = (video_scanline - VIDEO_770_VBEND) / VIDEO_CHAR_HEIGHT;
		unsigned line_in_row = (video_scanline - VIDEO_770_VBEND) - row * VIDEO_CHAR_HEIGHT;

		if (line_in_row == 0) {
			// Start of new row, swap buffers
			m_video_buff_idx = !m_video_buff_idx;
			video_fill_buff(!m_video_buff_idx);
		}
		video_render_buff(video_scanline , line_in_row , m_video_buff_idx);
		// Lightpen cursor
		if (m_graphic_sel) {
			render_lp_cursor(video_scanline - VIDEO_770_VBEND , pen_cursor(7));
		}
	}
	lp_scanline_update(video_scanline - VIDEO_770_VBEND);
}

void hp9845c_state::set_graphic_mode(bool graphic , bool alpha)
{
	m_graphic_sel = graphic;
	m_alpha_sel = alpha;
}

void hp9845c_state::video_render_buff(unsigned video_scanline , unsigned line_in_row, bool buff_idx)
{
	if (!m_video_buff[ buff_idx ].full) {
		m_video_blanked = true;
	}

	const pen_t *pen = m_palette->pens();

	if (m_video_blanked || !m_alpha_sel) {
		// Blank scanline
		for (unsigned i = 0; i < VIDEO_770_ALPHA_L_LIM; i++) {
			m_bitmap.pix(video_scanline , i) = pen[ pen_alpha(0) ];
		}
		if (!m_graphic_sel) {
			for (unsigned i = VIDEO_770_ALPHA_L_LIM; i < VIDEO_770_ALPHA_R_LIM; i++) {
				m_bitmap.pix(video_scanline , i) = pen[ pen_alpha(0) ];
			}
		}
		for (unsigned i = VIDEO_770_ALPHA_R_LIM; i < VIDEO_TOT_HPIXELS; i++) {
			m_bitmap.pix(video_scanline , i) = pen[ pen_alpha(0) ];
		}
	} else {
		bool cursor_line = line_in_row == 12;
		bool ul_line = line_in_row == 14;
		unsigned video_frame = (unsigned)m_screen->frame_number();
		bool cursor_blink = BIT(video_frame , 4);
		bool char_blink = !BIT(video_frame , 4);

		for (unsigned i = 0; i < 80; i++) {
			uint8_t charcode = m_video_buff[ buff_idx ].chars[ i ] & 0x7f;
			uint8_t attrs = m_video_buff[ buff_idx ].attrs[ i ];
			uint16_t chrgen_addr = ((uint16_t)(charcode ^ 0x7f) << 4) | line_in_row;
			uint16_t pixels;
			uint8_t color = (attrs >> 4) & 7;

			if (ul_line && BIT(attrs , 3)) {
				// Color of underline: same as character
				pixels = ~0;
			} else if (cursor_line && cursor_blink && BIT(attrs , 0)) {
				// Color of cursor: white
				color = 7;
				pixels = ~0;
			} else if (char_blink && BIT(attrs , 2)) {
				pixels = 0;
			} else if (BIT(m_video_buff[ buff_idx ].chars[ i ] , 7)) {
				// 98770A has hw support to fill the 1st and the 9th column of character matrix
				// with pixels in 2nd and 8th columns, respectively. This feature is used in
				// 98780A to make horizontal lines of line-drawing characters appear continuous
				// (see hp9845t_state::video_render_buff).
				// Apparently, though, HP did not use this feature at all in real
				// machines (i.e. horizontal lines are broken by gaps)
				pixels = (uint16_t)(m_optional_chargen[ chrgen_addr ] & 0x7f) << 1;
			} else {
				pixels = (uint16_t)(m_chargen[ chrgen_addr ] & 0x7f) << 1;
			}

			if (BIT(attrs , 1)) {
				pixels = ~pixels;
			}

			for (unsigned j = 0; j < 9; j++) {
				bool pixel = (pixels & (1U << j)) != 0;
				unsigned x = i * 9 + j;

				if (m_graphic_sel && x >= VIDEO_770_ALPHA_L_LIM && x < VIDEO_770_ALPHA_R_LIM) {
					// alpha overlays graphics (non-dominating)
					if (pixel) {
						m_bitmap.pix(video_scanline , x) = pen[ pen_alpha(color) ];
					}
				} else {
					// Graphics disabled or alpha-only zone
					m_bitmap.pix(video_scanline , x) = pen[ pixel ? pen_alpha(color) : pen_alpha(0) ];
				}
			}
		}
	}
}

void hp9845c_state::graphic_video_render(unsigned video_scanline)
{
	// video_scanline is 0-based, i.e. the topmost visible line of graphic screen is 0
	const pen_t *pen = m_palette->pens();
	bool yc, yw;
	uint16_t word0, word1, word2;
	uint8_t pen0, pen1, pen2;

	// apply music memory
	pen0 = (m_gv_music_memory & 0x001) | ((m_gv_music_memory & 0x008) >> 2) | ((m_gv_music_memory & 0x040) >> 4);
	pen1 = ((m_gv_music_memory & 0x002) >> 1) | ((m_gv_music_memory & 0x010) >> 3) | ((m_gv_music_memory & 0x080) >> 5);
	pen2 = ((m_gv_music_memory & 0x004) >> 2) | ((m_gv_music_memory & 0x020) >> 4) | ((m_gv_music_memory & 0x100) >> 6);

	if (m_gv_cursor_fs) {
		// Full-screen cursor
		yw = false;
		yc = video_scanline == m_gv_cursor_y;
	} else if (m_gv_cursor_gc) {
		// 15 x 15 crosshair
		int cursor_y_top = get_wrapped_scanline(m_gv_cursor_y);
		int int_scanline = (int)video_scanline;
		yw = (int_scanline >= (cursor_y_top + 1) && int_scanline <= (cursor_y_top + 6)) ||
			(int_scanline >= (cursor_y_top + 10) && int_scanline <= (cursor_y_top + 15));
		yc = int_scanline == cursor_y_top + 8;
	} else {
		// 9-pixel blinking line
		int cursor_y_top = get_wrapped_scanline(m_gv_cursor_y);
		int int_scanline = (int)video_scanline;
		yw = false;
		yc = int_scanline == cursor_y_top + 8;
	}

	unsigned mem_idx = get_gv_mem_addr(0 , video_scanline);
	for (unsigned i = 0; i < GVIDEO_HPIXELS; i += 16) {
		word0 = m_graphic_mem[ 0 ][ mem_idx ];
		word1 = m_graphic_mem[ 1 ][ mem_idx ];
		word2 = m_graphic_mem[ 2 ][ mem_idx ];
		mem_idx++;
		unsigned x = i;
		for (uint16_t mask = 0x8000; mask != 0; mask >>= 1) {
			bool cursor = false;
			unsigned pixel;

			if (m_gv_cursor_fs) {
				// Full-screen cursor
				cursor = yc || (x + 111) == m_gv_cursor_x;
			} else if (m_gv_cursor_gc) {
				bool xc = (x + 103) == m_gv_cursor_x;
				bool xw = ((x + 96) <= m_gv_cursor_x && (x + 101) >= m_gv_cursor_x) ||
					((x + 105) <= m_gv_cursor_x && (x + 110) >= m_gv_cursor_x);

				// 15 x 15 crosshair
				cursor = (yc && xw) || (yw && xc);
			} else if (BIT(m_screen->frame_number() , 3)) {
				// 9-pixel blinking line
				cursor = yc && (x + 107) >= m_gv_cursor_x && (x + 99) <= m_gv_cursor_x;
			}
			if (cursor) {
				// Cursor
				pixel = pen_cursor(m_gv_cursor_color);
			} else {
				// Normal pixel
				pixel = pen_graphic(((word0 & mask) ? pen0 : 0) | ((word1 & mask) ? pen1 : 0) | ((word2 & mask) ? pen2 : 0));
			}
			m_bitmap.pix(video_scanline , VIDEO_770_ALPHA_L_LIM + x++) = pen[ pixel ];
		}
	}
}

void hp9845c_state::plot(uint16_t x, uint16_t y, bool draw_erase)
{
	uint16_t addr, pixel_mask;
	bool do_draw, do_erase, dominance;

	pixel_mask = 0x8000 >> (x & 0xf);

	addr = get_gv_mem_addr(x >> 4 , y);
	dominance = BIT(m_gv_memory_control, 6);
	if (BIT(m_gv_memory_control, 0)) {
		do_erase = dominance;
		do_draw = draw_erase;
		if (!BIT(m_gv_memory_control, 3) && draw_erase) {
			do_draw = false;
			do_erase = true;
		}
		if (do_draw)
			m_graphic_mem[0][ addr ] |= pixel_mask;
		else if (do_erase)
			m_graphic_mem[0][ addr ] &= ~pixel_mask;
	}
	if (BIT(m_gv_memory_control, 1)) {
		do_erase = dominance;
		do_draw = draw_erase;
		if (!BIT(m_gv_memory_control, 4) && draw_erase) {
			do_draw = false;
			do_erase = true;
		}
		if (do_draw)
			m_graphic_mem[1][ addr ] |= pixel_mask;
		else if (do_erase)
			m_graphic_mem[1][ addr ] &= ~pixel_mask;
	}
	if (BIT(m_gv_memory_control, 2)) {
		do_erase = dominance;
		do_draw = draw_erase;
		if (!BIT(m_gv_memory_control, 5) && draw_erase) {
			do_draw = false;
			do_erase = true;
		}
		if (do_draw)
			m_graphic_mem[2][ addr ] |= pixel_mask;
		else if (do_erase)
			m_graphic_mem[2][ addr ] &= ~pixel_mask;
	}
}

void hp9845c_state::advance_io_counter()
{
	m_gv_plane++;
	if (m_gv_plane > 2) {
		if (m_gv_io_counter < GVIDEO_ADDR_MASK) {
			m_gv_plane = 0;
			m_gv_io_counter++;
		} else {
			m_gv_plane = 2;
		}
	}
}

void hp9845c_state::advance_gv_fsm(bool ds , bool trigger)
{
	if (!m_gv_gr_en) {
		return;
	}

	bool get_out = false;

	attotime time_mem_av;

	do {
		// U73 on vector generator board
		bool act_trig = trigger || m_gv_int_en || !BIT(m_gv_cmd , 0);

		switch (m_gv_fsm_state) {
		case GV_STAT_WAIT_DS_0:
			// inital state (same as GV_STAT_RESET), command received
			if (m_gv_cmd == 0x1) {
				// read words command
				LOG("read words\n");
				m_gv_fsm_state = GV_STAT_WAIT_TRIG_0;
			} else if (ds) {
				if ((m_gv_cmd == 0x0) || (m_gv_cmd == 0x2)) {
					// write words & clear/set words commands
					if (m_gv_cmd == 0x2) LOG("clear/set words\n");
					else LOG("write words\n");
					m_gv_fsm_state = GV_STAT_WAIT_TRIG_1;   // -> write stream
				} else {
					// any other command
					m_gv_fsm_state = GV_STAT_WAIT_TRIG_0;   // -> wait for trigger
				}
			} else {
				get_out = true;
			}
			break;

		case GV_STAT_WAIT_TRIG_0:
			// process data on R4 or R6
			if (act_trig) {
				switch (m_gv_cmd) {
				case 1: // read words command
					break;
				case 0x8:   // load X I/O address
					m_gv_word_x_position = ~m_gv_data_w & 0x3f;     // 0..34
					LOG("load X I/O adress = %04x\n", m_gv_word_x_position);
					m_gv_io_counter = get_gv_mem_addr(m_gv_word_x_position , m_gv_word_y_position);
					m_gv_plane = 0;
					break;
				case 0x9:   // load Y I/O address
					m_gv_word_y_position = ~m_gv_data_w & 0x1ff;    // 0..454
					LOG("load Y I/O adress = %04x\n", m_gv_word_y_position);
					m_gv_io_counter = get_gv_mem_addr(m_gv_word_x_position , m_gv_word_y_position);
					break;
				case 0xa:   // load memory control
					m_gv_memory_control = m_gv_data_w & 0x7f;
					LOG("load memory control = %04x\n", m_gv_memory_control);
					break;
				case 0xb:   // set line type/area fill
					m_gv_line_type_area_fill =  m_gv_data_w & 0x1ff;
					if (BIT(m_gv_line_type_area_fill, 4)) {
						m_gv_line_type_mask = m_line_type[ m_gv_line_type_area_fill & 0x7 ];
						m_gv_repeat_count = 0;
					}
					LOG("set line type = %04x\n", m_gv_line_type_area_fill);
					break;
				case 0xc:   // load color mask
					m_gv_music_memory = m_gv_data_w & 0x1ff;
					LOG("load color mask = %04x\n", m_gv_music_memory);
					break;
				case 0xd:   // load end points
					m_gv_ypt = ~m_gv_data_w & 0x1ff;
					LOG("load end points y = %d\n", m_gv_ypt);
					break;
				case 0xe:   // Y cursor position & color
					m_gv_lyc = m_gv_data_w;
					break;
				case 0xf:   // X cursor position & type
					m_gv_lxc = m_gv_data_w;
					break;
				default:
					logerror("unknown 98770A command = %d, parm = 0x%04x\n", m_gv_cmd, m_gv_data_w);
				}
				if (m_gv_cmd == 1) {    // Read words
					m_gv_fsm_state = GV_STAT_WAIT_MEM_0;
				} else if (m_gv_cmd == 0xd) {
					m_gv_fsm_state = GV_STAT_WAIT_DS_2;     // -> get second data word
				} else {
					get_out = true;
					m_gv_fsm_state = GV_STAT_WAIT_DS_0;     // -> done
				}
			} else {
				get_out = true;
			}
			break;

		case GV_STAT_WAIT_MEM_0:
			// process data during read transfer
			time_mem_av = time_to_gv_mem_availability();
			if (time_mem_av.is_zero()) {
				// Read a word from graphic memory
				m_gv_data_r = m_graphic_mem[ m_gv_plane ][ m_gv_io_counter ];
				LOG("read words @%04x = %04x, plane #%d\n" , m_gv_io_counter , m_gv_data_r, m_gv_plane + 1);
				advance_io_counter();
				m_gv_fsm_state = GV_STAT_WAIT_DS_1;     // -> proceed with read stream
			} else {
				m_gv_timer->adjust(time_mem_av);
				get_out = true;
			}
			break;

		case GV_STAT_WAIT_DS_1:
			// wait for data word to be read
			if (ds) {
				// -- next word
				m_gv_fsm_state = GV_STAT_WAIT_TRIG_0;    // -> process data word
			} else {
				// -- done
				get_out = true;
			}
			break;

		case GV_STAT_WAIT_DS_2:
			// wait for data word to be written
			if (ds) {
				// -- next word
				m_gv_fsm_state = GV_STAT_WAIT_TRIG_1;   // -> process data word
			} else {
				// done
				get_out = true;
			}
			break;

		case GV_STAT_WAIT_TRIG_1:
			// process multi-word parameters & data during write transfer
			if (act_trig) {
				if (m_gv_cmd == 0xd) {
					// load endpoints command
					m_gv_xpt = ~m_gv_data_w & 0x3ff;
					if (BIT(m_gv_data_w, 10)) {
						// draw vector
						LOG("load end points x = %d (draw)\n", m_gv_xpt);
						m_gv_fsm_state = GV_STAT_WAIT_MEM_2;    // -> proceed with draw vector
					} else {
						LOG("load end points x = %d (move)\n", m_gv_xpt);
						m_gv_last_xpt = m_gv_xpt;
						m_gv_last_ypt = m_gv_ypt;
						m_gv_fsm_state = GV_STAT_WAIT_DS_0;     // -> proceed with next word pair
					}
				} else if (m_gv_cmd == 0x2) {
					// clear/set words command
					m_gv_data_w = BIT(m_gv_memory_control, m_gv_plane + 3) ? 0xffff : 0;
					m_gv_fsm_state = GV_STAT_WAIT_MEM_1;        // -> proceed with next word
				} else if (m_gv_cmd == 0x0) {
					// write words command
					m_gv_fsm_state = GV_STAT_WAIT_MEM_1;        // -> proceed with next word
				}
			} else {
				// done
				get_out = true;
			}
			break;

		case GV_STAT_WAIT_MEM_1:
			// -- transfer from bus to graphics memory to bus within write transfer
			time_mem_av = time_to_gv_mem_availability();
			if (time_mem_av.is_zero()) {
				// Write a full word to graphic memory
				LOG("write words @%04x = %04x, plane #%d\n" , m_gv_io_counter , m_gv_data_w, m_gv_plane + 1);
				if ((m_gv_cmd == 0x0) || BIT(m_gv_memory_control, m_gv_plane)) {
					m_graphic_mem[ m_gv_plane ][ m_gv_io_counter ] = m_gv_data_w;
				}
				advance_io_counter();
				m_gv_fsm_state = GV_STAT_WAIT_DS_2;             // -> proceed with write stream
			} else {
				m_gv_timer->adjust(time_mem_av);
				get_out = true;
			}
			break;

		case GV_STAT_WAIT_MEM_2:
			// vector generator
			time_mem_av = time_to_gv_mem_availability();
			if (time_mem_av.is_zero()) {
				if (BIT (m_gv_line_type_area_fill, 4)) {
					unsigned x0;
					unsigned x1;
					unsigned y0;
					unsigned y1;

					// vector generator uses normalization
					if (m_gv_xpt > m_gv_last_xpt) {
						x0 = m_gv_last_xpt;
						y0 = m_gv_last_ypt;
						x1 = m_gv_xpt;
						y1 = m_gv_ypt;
					} else {
						x0 = m_gv_xpt;
						y0 = m_gv_ypt;
						x1 = m_gv_last_xpt;
						y1 = m_gv_last_ypt;
					}
					draw_line(x0 , y0 , x1 , y1);
				} else {
					// fill area with pattern
					LOG("area fill (%d,%d) -> (%d,%d) pattern=%04x\n", m_gv_last_xpt, m_gv_last_ypt, m_gv_xpt, m_gv_ypt, m_gv_line_type_area_fill);

					pattern_fill(m_gv_xpt , m_gv_ypt , m_gv_last_xpt , m_gv_last_ypt , m_gv_line_type_area_fill & 0xf);
				}
				m_gv_last_xpt = m_gv_xpt;
				m_gv_last_ypt = m_gv_ypt;
				m_gv_fsm_state = GV_STAT_WAIT_DS_0;     // -> proceed with next word pair
			} else {
				m_gv_timer->adjust(time_mem_av);
				get_out = true;
			}
			break;

		default:
			logerror("Invalid state reached %d\n" , m_gv_fsm_state);
			m_gv_fsm_state = GV_STAT_RESET;
		}

		ds = false;
		trigger = false;
	} while (!get_out);

	update_graphic_bits();
}

void hp9845c_state::update_graphic_bits()
{
	bool lp_int = m_gv_lp_int_en && m_gv_lp_status;
	bool sk_int = m_gv_sk_status && m_gv_sk_en;

	if (lp_int && !m_gv_sk_int_latched) {
		m_gv_lp_int_latched = true;
	} else if (sk_int && !m_gv_lp_int_latched) {
		m_gv_sk_int_latched = true;
	} else if (!lp_int && !sk_int) {
		m_gv_lp_int_latched = false;
		m_gv_sk_int_latched = false;
	}

	bool gv_ready = m_gv_lp_int_latched || m_gv_sk_int_latched;

	if (m_gv_gr_en && !gv_ready) {
		gv_ready = m_gv_fsm_state == GV_STAT_WAIT_DS_0 ||
			m_gv_fsm_state == GV_STAT_WAIT_TRIG_0 ||
			m_gv_fsm_state == GV_STAT_WAIT_DS_1 ||
			m_gv_fsm_state == GV_STAT_WAIT_DS_2 ||
			m_gv_fsm_state == GV_STAT_WAIT_TRIG_1;
	}

	m_io_sys->set_flg(GVIDEO_PA , gv_ready);

	bool irq = m_gv_int_en && !m_gv_dma_en && gv_ready;

	m_io_sys->set_irq(GVIDEO_PA , irq);

	bool dmar = gv_ready && m_gv_dma_en;

	m_io_sys->set_dmar(GVIDEO_PA , dmar);
}

void hp9845c_state::update_gcursor()
{
	m_gv_cursor_color = ~m_gv_lyc & 0x7;
	m_gv_cursor_y = (~m_gv_lyc >> 6) & 0x1ff;
	m_gv_cursor_fs = BIT(m_gv_lxc, 0);
	m_gv_cursor_gc = BIT(m_gv_lxc, 1);
	m_gv_cursor_x = (m_gv_lxc >> 6) & 0x3ff;
}

// ***************
//  hp9845t_state
// ***************
class hp9845t_state : public hp9845ct_base_state
{
public:
	hp9845t_state(const machine_config &mconfig, device_type type, const char *tag);

	void hp9845t(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual uint16_t graphic_r(offs_t offset) override;
	virtual void graphic_w(offs_t offset, uint16_t data) override;

	TIMER_DEVICE_CALLBACK_MEMBER(scanline_timer);

	virtual void set_graphic_mode(bool graphic , bool alpha) override;
	void video_render_buff(unsigned video_scanline , unsigned line_in_row, bool buff_idx);
	void graphic_video_render(unsigned video_scanline);
	virtual void plot(uint16_t x, uint16_t y, bool draw_erase) override;
	void draw_arc(uint16_t x0, uint16_t y0, int xstart, int ystart, uint16_t radius, int quadrant, int& draw_counter);
	void draw_full_arc(int x0 , int y0 , int dx , int dy , int draw_counter);

	virtual void advance_gv_fsm(bool ds , bool trigger) override;
	virtual void update_graphic_bits() override;

	virtual void update_gcursor() override;

	std::vector<uint16_t> m_graphic_mem;

	bool m_gv_stat = false;
	bool m_gv_increment_to_next_row = false;

	uint16_t m_gv_scan_start_x = 0;
	uint16_t m_gv_scan_start_y = 0;
	uint8_t m_gv_rb_control = 0;
	uint16_t m_gv_rb_counter = 0;
	uint16_t m_gv_rb_memory[ 256 ]{};
	uint16_t m_gv_arc[ 4 ]{};
	uint8_t m_gv_arc_parm = 0;
	bool m_back_arrow_cursor = false;

	static const uint8_t m_back_arrow_shape[];
};

hp9845t_state::hp9845t_state(const machine_config &mconfig, device_type type, const char *tag)
	: hp9845ct_base_state(mconfig , type , tag)
{
}

void hp9845t_state::machine_start()
{
	// Common part first
	hp9845ct_base_state::machine_start();

	m_graphic_mem.resize(GVIDEO_MEM_SIZE);

	// initialize palette
	m_palette->set_pen_color(PEN_BLACK  , 0x00, 0x00, 0x00);    // black
	m_palette->set_pen_color(PEN_GRAPHIC, 0x00, I_GR, 0x00);    // graphics
	m_palette->set_pen_color(PEN_ALPHA  , 0x00, I_AL, 0x00);    // alpha
	m_palette->set_pen_color(PEN_CURSOR , 0x00, I_CU, 0x00);    // graphics cursor
	m_palette->set_pen_color(PEN_LP     , 0x00, I_LP, 0x00);    // lightpen cursor
}

void hp9845t_state::machine_reset()
{
	// Common part first
	hp9845ct_base_state::machine_reset();

	m_gv_stat = false;
	m_gv_increment_to_next_row = false;
	m_gv_scan_start_x = 0;
	m_gv_scan_start_y = 0;
	m_gv_rb_control = 0;
	m_gv_rb_counter = 0;
	memset(m_gv_rb_memory, 0, sizeof(m_gv_rb_memory));
	m_back_arrow_cursor = false;

	set_video_mar(0);
}

uint16_t hp9845t_state::graphic_r(offs_t offset)
{
	uint16_t res = 0;

	switch (offset) {
	case 2:
		// R6: data register with DMA TC
		m_gv_dma_en = false;
		[[fallthrough]];

	case 0:
		// R4: data register
		if (m_gv_lp_en) {
			res = lp_r4_r();
		} else if (m_gv_sk_en) {
			res = m_gv_softkey;
			m_gv_sk_status = false;
		} else {
			res = m_gv_data_r;
		}
		advance_gv_fsm(true , false);
		update_graphic_bits();
		break;

	case 1:
		// R5: status register
		if (m_gv_int_en) {
			BIT_SET(res, 7);
		}
		if (m_gv_dma_en) {
			BIT_SET(res, 6);
		}
		if (m_gv_lp_status && m_gv_lp_int_en) {
			BIT_SET(res, 0);    // Lightpen service request
		}
		// TODO: gsr/
		if (m_gv_sk_status) {
			BIT_SET(res, 1);    // Softkey service request
		}
		BIT_SET(res, 9);        // ID
		BIT_SET(res, 11);       // ID
		if (m_gv_stat) {
			BIT_SET(res, 13);   // error indication
		}
		break;

	case 3:
		// R7: not mapped
		break;
	}

	LOG("rd gv R%u = %04x\n", 4 + offset , res);

	return res;
}

void hp9845t_state::graphic_w(offs_t offset, uint16_t data)
{
	LOG("wr gv R%u = %04x\n", 4 + offset , data);

	switch (offset) {
	case 0:
		// R4: data register
		m_gv_data_w = data;
		advance_gv_fsm(true , false);
		lp_r4_w(data);
		if (m_gv_lp_int_en) {
			m_gv_lp_fullbright = BIT(data , 1);
		}
		break;

	case 1:
		// R5: command register
		m_gv_cmd = (uint8_t)(data & 0xf);
		m_gv_dma_en = BIT(data , 6) != 0;
		m_gv_int_en = BIT(data , 7) != 0;
		m_gv_gr_en = BIT(data , 8);       // enables graphics controller & vector generator command processing and IRQs
		m_gv_sk_en = (data & 0xb00) == 0x200;       // enables reads on R4 to return SK keycode, also enables SK IRQs
		m_gv_opt_en = BIT(data , 11);     // not really used
		m_gv_dsa_en = BIT(data , 12);     // for factory use only (function unknown)
		m_gv_fsm_state = GV_STAT_RESET;     // command/reset state machine
		lp_r5_w(data);
		advance_gv_fsm(false , false);
		break;

	case 2:
		// R6: data register with DMA TC
		m_gv_dma_en = false;
		m_gv_data_w = data;
		lp_r4_w(data);
		if (m_gv_lp_int_en) {
			m_gv_lp_fullbright = BIT(data , 1);
		}
		advance_gv_fsm(true , false);
		break;

	case 3:
		// R7: not mapped
		break;
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(hp9845t_state::scanline_timer)
{
	unsigned video_scanline = param;

	if (video_scanline >= VIDEO_780_VBEND && video_scanline < VIDEO_780_VBSTART) {
		if (m_graphic_sel) {
			graphic_video_render(video_scanline - VIDEO_780_VBEND);
		}
		unsigned row = (video_scanline - VIDEO_780_VBEND) / VIDEO_CHAR_HEIGHT;
		unsigned line_in_row = (video_scanline - VIDEO_780_VBEND) - row * VIDEO_CHAR_HEIGHT;

		if (line_in_row == 0) {
			// Start of new row, swap buffers
			m_video_buff_idx = !m_video_buff_idx;
			video_fill_buff(!m_video_buff_idx);
		}
		video_render_buff(video_scanline , line_in_row , m_video_buff_idx);
		// Lightpen cursor
		if (m_graphic_sel) {
			render_lp_cursor(video_scanline - VIDEO_780_VBEND , PEN_LP);
		}
	}
	lp_scanline_update(video_scanline - VIDEO_780_VBEND);
}

void hp9845t_state::set_graphic_mode(bool graphic , bool alpha)
{
	m_back_arrow_cursor = graphic;      // triggers back arrow cursor, 98780A uses video on/off command for enabling/disabling graphics
	m_alpha_sel = alpha;
}

void hp9845t_state::video_render_buff(unsigned video_scanline , unsigned line_in_row, bool buff_idx)
{
	if (!m_video_buff[ buff_idx ].full) {
		m_video_blanked = true;
	}

	const pen_t *pen = m_palette->pens();

	if (m_video_blanked || !m_alpha_sel) {
		// Blank scanline
		for (unsigned i = 0; i < VIDEO_780_ALPHA_L_LIM; i++) {
			m_bitmap.pix(video_scanline , i) = pen[ PEN_BLACK ];
		}
		if (!m_graphic_sel) {
			for (unsigned i = VIDEO_780_ALPHA_L_LIM; i < VIDEO_780_ALPHA_R_LIM; i++) {
				m_bitmap.pix(video_scanline , i) = pen[ PEN_BLACK ];
			}
		}
		for (unsigned i = VIDEO_780_ALPHA_R_LIM; i < VIDEO_TOT_HPIXELS; i++) {
			m_bitmap.pix(video_scanline , i) = pen[ PEN_BLACK ];
		}
	} else {
		bool cursor_line = line_in_row == 12;
		bool ul_line = line_in_row == 14;
		unsigned video_frame = (unsigned)m_screen->frame_number();
		bool cursor_blink = BIT(video_frame , 3);
		bool char_blink = BIT(video_frame , 4);

		for (unsigned i = 0; i < 80; i++) {
			uint8_t attrs = m_video_buff[ buff_idx ].attrs[ i ];
			uint16_t pixels;

			if (!BIT(attrs , 2) || char_blink) {
				if (ul_line && BIT(attrs , 3)) {
					pixels = ~0;
				} else {
					// The 98780A uses two identical 4KB ROMs interlaced to keep up with the speed of
					// the video circuit. Each of the 4K ROMs contains the full character set.
					// The 98780A fills row 0 (space between characters) controlled by row 1 and 2 from
					// the character ROM, thereby providing line drawing characters for continuous lines
					uint8_t charcode = m_video_buff[ buff_idx ].chars[ i ];
					uint16_t chrgen_addr = ((uint16_t)(charcode ^ 0xff) << 4) | (line_in_row + 1);
					pixels = (uint16_t)m_chargen[ chrgen_addr ] << 1;
					if ((charcode & 0xe0) == 0xe0 && (pixels & 0x6) == 0x6) {
						pixels |= 1;
					}
				}
			} else {
				pixels = 0;
			}

			if (cursor_blink && BIT(attrs , 0)) {
				if (m_back_arrow_cursor) {
					// back arrow cursor (HP's hardware easter egg)
					pixels |= m_back_arrow_shape[ line_in_row ];
				} else if (cursor_line) {
					pixels = ~0;
				}
			}

			if (BIT(attrs , 1)) {
				pixels = ~pixels;
			}

			for (unsigned j = 0; j < 9; j++) {
				bool pixel = (pixels & (1U << j)) != 0;
				unsigned x = i * 9 + j;

				if (m_graphic_sel && x >= VIDEO_780_ALPHA_L_LIM && x < VIDEO_780_ALPHA_R_LIM) {
					// alpha overlays graphics (non-dominating)
					if (pixel) {
						m_bitmap.pix(video_scanline , x) = pen[ PEN_ALPHA ];
					}
				} else {
					// Graphics disabled or alpha-only zone
					m_bitmap.pix(video_scanline , x) = pen[ pixel ? PEN_ALPHA : PEN_BLACK ];
				}
			}
		}
	}
}

void hp9845t_state::graphic_video_render(unsigned video_scanline)
{
	// video_scanline is 0-based, i.e. the topmost visible line of graphic screen is 0
	const pen_t *pen = m_palette->pens();
	bool yc, yw;
	uint16_t word;

	if (m_gv_cursor_fs) {
		// Full-screen cursor
		yw = false;
		yc = video_scanline == m_gv_cursor_y;
	} else if (m_gv_cursor_gc) {
		// 9 x 9 crosshair
		int cursor_y_top = get_wrapped_scanline(m_gv_cursor_y);
		int int_scanline = (int)video_scanline;
		yw = int_scanline >= cursor_y_top && int_scanline <= (cursor_y_top + 8);
		yc = int_scanline == cursor_y_top + 4;
	} else {
		// 9-pixel blinking line
		int cursor_y_top = get_wrapped_scanline(m_gv_cursor_y);
		int int_scanline = (int)video_scanline;
		yw = false;
		yc = int_scanline == cursor_y_top + 4;
	}

	unsigned mem_idx = get_gv_mem_addr(m_gv_scan_start_x >> 4, video_scanline + m_gv_scan_start_y);
	for (unsigned i = 0; i < GVIDEO_HPIXELS; i += 16) {
		word = m_graphic_mem[ mem_idx ];
		mem_idx++;
		// Is wraparound better?
		if (mem_idx > GVIDEO_ADDR_MASK) return;
		unsigned x = i;
		for (uint16_t mask = 0x8000; mask != 0; mask >>= 1) {
			bool cursor = false;
			unsigned pixel;
			if (m_gv_cursor_fs) {
				// Full-screen cursor
				cursor = yc || (x + 184) == m_gv_cursor_x;
			} else if (m_gv_cursor_gc) {
				bool xc = (x + 184) == m_gv_cursor_x;
				bool xw = (x + 180) <= m_gv_cursor_x && (x + 188) >= m_gv_cursor_x;

				// 9 x 9 crosshair
				cursor = (yc && xw) || (yw && xc);
			} else if (BIT(m_screen->frame_number() , 3)) {
				// 9-pixel blinking line
				cursor = yc && (x + 188) >= m_gv_cursor_x && (x + 180) <= m_gv_cursor_x;
			}
			if (cursor) {
				pixel = PEN_CURSOR;
			} else {
				// Normal pixel
				if (m_gv_lp_fullbright)
					pixel = word & mask ? PEN_LP : PEN_BLACK;
				else
					pixel = word & mask ? PEN_GRAPHIC : PEN_BLACK;
			}
			m_bitmap.pix(video_scanline , VIDEO_780_ALPHA_L_LIM + x++) = pen[ pixel ];
		}
	}
}

void hp9845t_state::plot(uint16_t x, uint16_t y, bool draw_erase)
{
	uint16_t addr, pixel_mask;
	bool do_draw;

	pixel_mask = 0x8000 >> (x & 0xf);
	addr = get_gv_mem_addr(x >> 4 , y);
	if (BIT(m_gv_rb_control, 1)) {
		// save graphics memory to rubber band memory
		if (m_graphic_mem[ addr ] & pixel_mask)
			m_gv_rb_memory[m_gv_rb_counter/16] |= 0x1 << (m_gv_rb_counter % 16);        // set
		else
			m_gv_rb_memory[m_gv_rb_counter/16] &= ~(0x1 << (m_gv_rb_counter % 16));     // clear
		m_gv_rb_counter++;
		if (m_gv_rb_counter > 4095) {
			m_gv_stat = true;   // we might prevent data corruption here, but the original hardware doesn't
			m_gv_rb_counter = 0;
		}
	} else if (BIT(m_gv_rb_control, 0)) {
		// restore graphics memory from rubber band memory
		if (BIT(m_gv_rb_memory[m_gv_rb_counter / 16], m_gv_rb_counter % 16))
			m_graphic_mem[ addr ] |= pixel_mask;        // set
		else
			m_graphic_mem[ addr ] &= ~pixel_mask;   // clear
		m_gv_rb_counter++;
		if (m_gv_rb_counter > 4095) {
			m_gv_stat = true;
			m_gv_rb_counter = 0;
		}
	} else {
		// draw/erase pixel
		do_draw = m_gv_memory_control ? draw_erase : !draw_erase;
		if (do_draw)
			m_graphic_mem[ addr ] |= pixel_mask;
		else
			m_graphic_mem[ addr ] &= ~pixel_mask;
	}
}

void hp9845t_state::draw_arc(uint16_t x0, uint16_t y0, int xstart, int ystart, uint16_t radius, int quadrant, int& draw_counter)
{
	int i, x1, y1, d;
	bool draw_erase;
	bool do_plot = (xstart < 0) || (ystart < 0);
	int save_x1[560];

	/* quadrants:
	 *
	 *    1 | 0
	 *   ---+---
	 *    2 | 3
	 *
	 *  Note: we are using Horn's algorithm here, however it is not 100% pixel
	 *  accurate with the original vector generator
	 */

	d = -radius;
	x1 = radius;
	y1 = 0;
	i = 0;
	while ((x1 > 0) && (draw_counter > 0)) {
		// check for plot start
		if  (!do_plot) {
			if ((quadrant % 2) == 0) {
				if ((x1 <= xstart) && (y1 >= ystart)) do_plot = true;
			} else {
				if ((y1 >= xstart) && (x1 <= ystart)) do_plot = true;
			}
		}

		// draw pixels in quadrants
		draw_erase = BIT(m_gv_line_type_mask, 15);
		if (do_plot) {
			switch (quadrant) {
			case 0:
				plot(x0 + x1, y0 - y1, draw_erase);     // quadrant 0
				break;
			case 1:
				plot(x0 - y1, y0 - x1, draw_erase);     // quadrant 1
				break;
			case 2:
				plot(x0 - x1, y0 + y1, draw_erase);     // quadrant 2
				break;
			case 3:
				plot(x0 + y1, y0 + x1, draw_erase);     // quadrant 3
				break;
			}
		}

		// update coordinates
		if (x1 > y1) {
			save_x1[i++] = x1;
			d += 2 * y1 + 1;
			y1++;
			if (do_plot) draw_counter--;
			if (d > 0) {
				x1--;
				if (do_plot) draw_counter--;
				d -= 2 * x1;
			}
			if (x1 <= y1) draw_counter++;
		}
		else {
			x1--;
			y1 = save_x1[--i];
			if (do_plot) {
				draw_counter--;
				if (save_x1[i] != save_x1[i+1])
					draw_counter--;
			}
		}
		update_line_pattern();
	}
}

void hp9845t_state::draw_full_arc(int x0 , int y0 , int dx , int dy , int draw_counter)
{
	// radius
	int radius = sqrt(dx * dx + dy * dy);

	LOG("midpoint = (%d,%d) radius = %d ctrl = %d count = %d\n", x0, y0, radius, m_gv_memory_control, draw_counter);

	/* quadrants:
	 *
	 *    1 | 0
	 *   ---+---
	 *    2 | 3
	 */

	int quadrant = 0;

	// determine the start quadrant
	if (dx > 0)
		quadrant = (dy < 0) ? 1 : 2;
	else
		quadrant = (dy <= 0) ? 0 : 3;

	draw_arc(x0, y0, abs(dx), abs(dy), radius, quadrant, draw_counter);
	while (draw_counter > 0) {
		quadrant++;
		if (quadrant > 3) {
			quadrant = 0;
		}
		draw_arc(x0, y0, -1, -1, radius, quadrant, draw_counter);
	}
}

void hp9845t_state::advance_gv_fsm(bool ds , bool trigger)
{
	if (!m_gv_gr_en) {
		return;
	}

	bool get_out = false;

	attotime time_mem_av;

	do {
		switch (m_gv_fsm_state) {
		case GV_STAT_WAIT_DS_0:
			// inital state (same as GV_STAT_RESET), command received
			if (m_gv_cmd == 0x9) {
				// read words command
				if (m_gv_last_cmd != m_gv_cmd) {
					m_gv_io_counter = get_gv_mem_addr(m_gv_word_x_position , m_gv_word_y_position);
				}
				LOG("read words, last = %x\n", m_gv_last_cmd);
				m_gv_fsm_state = GV_STAT_WAIT_MEM_0;    // -> read stream
				m_gv_last_cmd = m_gv_cmd;
			} else if (m_gv_cmd == 0xd) {
				// fast clear/set command
				m_gv_fsm_state = GV_STAT_WAIT_MEM_2;
				m_gv_last_cmd = m_gv_cmd;
			} else if (ds) {
				if (m_gv_cmd == 0x8) {
					// write words command
					if (m_gv_last_cmd != m_gv_cmd) {
						m_gv_io_counter = get_gv_mem_addr(m_gv_word_x_position , m_gv_word_y_position);
					}
					LOG("write words\n");
					m_gv_fsm_state = GV_STAT_WAIT_TRIG_1;   // -> write stream
				} else {
					// any other command
					m_gv_fsm_state = GV_STAT_WAIT_TRIG_0;   // -> wait for trigger
				}
				m_gv_last_cmd = m_gv_cmd;
			} else {
				get_out = true;
			}
			break;

		case GV_STAT_WAIT_TRIG_0:
			// process data on R4 or R6
			switch (m_gv_cmd) {
			case 0x1:   // load end points
				m_gv_ypt = m_gv_data_w & 0x3ff;
				LOG("load end points y = %d\n", m_gv_ypt);
				break;
			case 0x3:   // load arc
				m_gv_arc_parm = 0;
				m_gv_arc[ m_gv_arc_parm ] = m_gv_data_w;
				LOG("load arc parm%d = %04x\n", m_gv_arc_parm, m_gv_arc[m_gv_arc_parm]);
				m_gv_arc_parm++;
				break;
			case 0x5:   // load scan
				m_gv_scan_start_x = m_gv_data_w & 0x3ff;    // 0..559
				LOG("load scan x = %d\n", m_gv_scan_start_x);
				break;
			case 0x6:   // set line type/area fill
				m_gv_line_type_area_fill = m_gv_data_w & 0x1ff;
				if (BIT(m_gv_line_type_area_fill, 4)) {
					m_gv_line_type_mask = m_line_type[ m_gv_line_type_area_fill & 0x7 ];
					m_gv_repeat_count = 0;
				}
				LOG("set line type = %04x\n", m_gv_line_type_area_fill);
				break;
			case 0x7:   // load X/Y I/O address
				m_gv_word_y_position = m_gv_data_w & 0x1ff; // 0..454
				LOG("load X/Y I/O adress y = %04x\n", m_gv_word_y_position);
				break;
			case 0xa:   // load memory control
				// A single bit is saved (InvBit)
				m_gv_memory_control = (m_gv_data_w & 0x9) == 9 || (m_gv_data_w & 0x12) == 0x12 || (m_gv_data_w & 0x24) == 0x24;
				LOG("load memory control = %04x\n", m_gv_memory_control);
				break;
			case 0xb:   // video on/off - enable graphics video output (1=on 2=off)
				m_graphic_sel = BIT(m_gv_data_w, 0);
				LOG("video on/off parm = %d\n", m_gv_data_w & 0x3);
				break;
			case 0xc:   // load color mask (no effect, just for compatibility with 9845c), takes a single word as parameter
				break;
			case 0xe:   // Y cursor position
				m_gv_lyc = m_gv_data_w;
				break;
			case 0xf:   // X cursor position
				m_gv_lxc = m_gv_data_w;
				break;
			default:
				LOG("unknown 98780A command = %d, parm = 0x%04x\n", m_gv_cmd, m_gv_data_w);
			}
			if ((m_gv_cmd == 0x1) || (m_gv_cmd == 0x3) || (m_gv_cmd == 0x5) || (m_gv_cmd == 0x7)) {
				m_gv_fsm_state = GV_STAT_WAIT_DS_2;     // -> get second data word
			} else {
				get_out = true;
				m_gv_fsm_state = GV_STAT_WAIT_DS_0;     // -> done
			}
			break;

		case GV_STAT_WAIT_MEM_0:
			// process data during read transfer
			time_mem_av = time_to_gv_mem_availability();
			if (time_mem_av.is_zero()) {
				// Read a word from graphic memory
				m_gv_data_r = m_graphic_mem[ m_gv_io_counter ];
				LOG("read words @%04x = %04x\n" , m_gv_io_counter , m_gv_data_r);
				m_gv_io_counter = (m_gv_io_counter + 1) & GVIDEO_ADDR_MASK;
				m_gv_fsm_state = GV_STAT_WAIT_DS_1;     // -> proceed with read stream
			} else {
				m_gv_timer->adjust(time_mem_av);
				get_out = true;
			}
			break;

		case GV_STAT_WAIT_DS_1:
			// wait for data word to be read
			if (ds) {
				// -- next word
				m_gv_fsm_state = GV_STAT_WAIT_MEM_0;    // -> process data word
			} else {
				// -- done
				get_out = true;
			}
			break;

		case GV_STAT_WAIT_DS_2:
			// wait for data word to be written
			if (ds) {
				// -- next word
				m_gv_fsm_state = GV_STAT_WAIT_TRIG_1;   // -> process data word
			} else {
				// done
				get_out = true;
			}
			break;

		case GV_STAT_WAIT_TRIG_1:
			// process multi-word parameters & data during write transfer
			switch (m_gv_cmd) {
			case 0x1:
				// load endpoints command
				m_gv_xpt = m_gv_data_w & 0x3ff;
				// RB control is actually set whenever a data word is written into R4/R6 register, not just here
				m_gv_rb_control = (m_gv_data_w >> 13) & 0x7;
				if (BIT(m_gv_rb_control, 2)) {
					m_gv_rb_counter = 0;
					m_gv_stat = false;
				}
				if (BIT(m_gv_data_w, 11)) {
					// draw vector
					LOG("load end points x = %d, rb = %d (draw)\n", m_gv_xpt, m_gv_rb_control);
					m_gv_fsm_state = GV_STAT_WAIT_MEM_2;    // -> proceed with draw vector
				} else {
					LOG("load end points x = %d, rb = %d (move)\n", m_gv_xpt, m_gv_rb_control);
					m_gv_last_xpt = m_gv_xpt;
					m_gv_last_ypt = m_gv_ypt;
					m_gv_fsm_state = GV_STAT_WAIT_DS_0;     // -> proceed with next word pair
				}
				break;

			case 0x3:
				// load arc
				m_gv_arc[ m_gv_arc_parm ] = m_gv_data_w;
				LOG("load arc parm%d = %04x\n", m_gv_arc_parm, m_gv_arc[m_gv_arc_parm]);
				m_gv_arc_parm++;
				if (m_gv_arc_parm < 4) {
					m_gv_fsm_state = GV_STAT_WAIT_DS_2;     // -> proceed with next word
				} else {
					m_gv_fsm_state = GV_STAT_WAIT_MEM_2;    // -> proceed with draw vector
				}
				break;

			case 0x5:
				// load scan
				m_gv_scan_start_y = m_gv_data_w & 0x3ff;    // 0..454
				LOG("load scan y = %d\n", m_gv_scan_start_y);
				m_gv_fsm_state = GV_STAT_WAIT_DS_0;
				break;

			case 0x7:
				// load X/Y I/O address
				m_gv_word_x_position = (m_gv_data_w & 0x3f0) >> 4;  // 0..34
				m_gv_increment_to_next_row = BIT(m_gv_data_w, 11);
				m_gv_io_counter = get_gv_mem_addr(m_gv_word_x_position , m_gv_word_y_position);
				LOG("load X/Y I/O adress x = %04x increment = %d\n", m_gv_word_x_position, m_gv_increment_to_next_row);
				m_gv_fsm_state = GV_STAT_WAIT_DS_0;
				break;

			case 0x8:
				// write words command
				m_gv_fsm_state = GV_STAT_WAIT_MEM_1;        // -> proceed with next word
				break;
			}
			break;

		case GV_STAT_WAIT_MEM_1:
			// -- transfer from bus to graphics memory to bus within write transfer
			time_mem_av = time_to_gv_mem_availability();
			if (time_mem_av.is_zero()) {
				// Write a full word to graphic memory
				LOG("write words @%04x = %04x\n" , m_gv_io_counter , m_gv_data_w);
				m_graphic_mem[ m_gv_io_counter ] = m_gv_data_w;
				if (!m_gv_increment_to_next_row || (m_gv_word_x_position < 34)) {
					m_gv_io_counter = (m_gv_io_counter + 1) & GVIDEO_ADDR_MASK;
				}
				m_gv_fsm_state = GV_STAT_WAIT_DS_2;             // -> proceed with write stream
			} else {
				m_gv_timer->adjust(time_mem_av);
				get_out = true;
			}
			break;

		case GV_STAT_WAIT_MEM_2:
			// vector generator
			time_mem_av = time_to_gv_mem_availability();
			if (time_mem_av.is_zero()) {
				if (m_gv_cmd == 0xd) {
					// fast clear/set command
					if (m_gv_memory_control) {
						LOG("fast clear/set (set)\n");
						for (auto& el : m_graphic_mem) {
							el = 0xffff;
						}
					} else {
						LOG("fast clear/set (clear)\n");
						for (auto& el : m_graphic_mem) {
							el = 0;
						}
					}
				} else {
					if (m_gv_cmd == 0x3) {
						// draw arc/circle
						// drawing is performed counter-clockwise by using Horn's algorithm
						// m_gv_arc[0] is the delta last load endpoint y coordinate minus the midpoint y coordinate
						// m_gv_arc[1] is the delta last load endpoint x coordinate minus the midpoint x coordinate
						// m_gv_arc[2] is the (probably) the start count (?), actually ignored
						// m_gv_arc[3] is the total horizontal + vertical count for the 4 quadrants counter-clockwise, starting at the last load endpoint (equals 4 times radius for full circles)
						LOG("arc draw\n");

						// midpoint
						int dx = BIT(m_gv_arc[ 1 ] , 15) ? (int)m_gv_arc[ 1 ] - 65536 : m_gv_arc[ 1 ] & 0x7ff;
						int dy = BIT(m_gv_arc[ 0 ] , 15) ? (int)m_gv_arc[ 0 ] - 65536 : m_gv_arc[ 0 ];
						int x0 = m_gv_xpt + dx;
						int y0 = m_gv_ypt - dy;

						draw_full_arc(x0 , y0 , dx , dy , m_gv_arc[ 3 ]);
					} else if (BIT (m_gv_line_type_area_fill, 4)) {
						unsigned x0;
						unsigned x1;
						unsigned y0;
						unsigned y1;

						LOG("line draw (%d,%d)->(%d,%d)\n", m_gv_last_xpt, m_gv_last_ypt, m_gv_xpt, m_gv_ypt);

						// vector generator uses normalization
						if (m_gv_xpt > m_gv_last_xpt) {
							x0 = m_gv_last_xpt;
							y0 = m_gv_last_ypt;
							x1 = m_gv_xpt;
							y1 = m_gv_ypt;
						} else {
							x0 = m_gv_xpt;
							y0 = m_gv_ypt;
							x1 = m_gv_last_xpt;
							y1 = m_gv_last_ypt;
						}
						draw_line(x0 , y0 , x1 , y1);
					} else {
						// fill area with pattern
						LOG("area fill (%d,%d) -> (%d,%d) pattern=%04x\n", m_gv_last_xpt, m_gv_last_ypt, m_gv_xpt, m_gv_ypt, m_gv_line_type_area_fill);

						pattern_fill(m_gv_xpt , m_gv_ypt , m_gv_last_xpt , m_gv_last_ypt , 15 - (m_gv_line_type_area_fill & 0xf));
					}
					m_gv_last_xpt = m_gv_xpt;
					m_gv_last_ypt = m_gv_ypt;
				}
				m_gv_fsm_state = GV_STAT_WAIT_DS_0;
			} else {
				m_gv_timer->adjust(time_mem_av);
			}
			get_out = true;
			break;

		default:
			logerror("Invalid state reached %d\n" , m_gv_fsm_state);
			m_gv_fsm_state = GV_STAT_RESET;
		}

		ds = false;
	} while (!get_out);

	update_graphic_bits();
}

void hp9845t_state::update_graphic_bits()
{
	bool gv_ready = (m_gv_lp_int_en && m_gv_lp_status) || (m_gv_sk_en && m_gv_sk_status);

	if (m_gv_gr_en && !gv_ready) {
		gv_ready = m_gv_fsm_state == GV_STAT_WAIT_DS_0 ||
			m_gv_fsm_state == GV_STAT_WAIT_DS_1 ||
			m_gv_fsm_state == GV_STAT_WAIT_DS_2;
	}

	// WARNING! Race conditions here!
	// FLG and IRQ are raised together. In enhgfxb ROM a SFC instruction
	// that spins on itself waiting for FLG to be true was getting
	// stuck because the interrupt always took precedence (and the ISR
	// cleared the FLG bit).
	// In real hw there was a non-zero chance that SFC exited the loop before
	// interrupt was serviced. In case SFC stayed in the loop, it got another
	// chance at the next interrupt.
	// Fix for this problem is in commit 27004d00
	// My apologies to Tony Duell for doubting at one point the correctness
	// of his 98780A schematics.. :)
	m_io_sys->set_flg(GVIDEO_PA , gv_ready);

	bool irq = m_gv_int_en && !m_gv_dma_en && gv_ready;

	m_io_sys->set_irq(GVIDEO_PA , irq);

	bool dmar = gv_ready && m_gv_dma_en;

	m_io_sys->set_dmar(GVIDEO_PA , dmar);
}

void hp9845t_state::update_gcursor()
{
	m_gv_cursor_fs = (m_gv_lyc & 0x3) == 0;
	m_gv_cursor_gc = !BIT(m_gv_lyc , 1);
	m_gv_cursor_y = (~m_gv_lyc >> 7) & 0x1ff;
	m_gv_cursor_x = (m_gv_lxc >> 6) & 0x3ff;
}

const uint8_t hp9845t_state::m_back_arrow_shape[] = {
	0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xfc,
	0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00
};

void hp9845_state::hp9845a(machine_config &config)
{
	//HP_5061_3010(config, m_lpu, XTAL(11'400'000));
	//HP_5061_3011(config, m_ppu, XTAL(11'400'000));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_screen_update(FUNC(hp9845_state::screen_update));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.set_size(560, 455);
	screen.set_visarea(0, 560-1, 0, 455-1);

	SOFTWARE_LIST(config, "optrom_list").set_original("hp9845a_rom");
}

void hp9845_state::hp9835a(machine_config &config)
{
	//HP_5061_3001(config, m_lpu, XTAL(11'400'000));
	//HP_5061_301(1config, m_ppu, XTAL(11'400'000));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_screen_update(FUNC(hp9845_state::screen_update));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.set_size(560, 455);
	screen.set_visarea(0, 560-1, 0, 455-1);

	SOFTWARE_LIST(config, "optrom_list").set_original("hp9835a_rom");
}

/*
    Global memory map in blocks of 32 kwords / 64 kbytes each:

    block  0: 0x000000 - 0x007fff (LPU RAM)
    block  1: 0x008000 - 0x00ffff (PPU RAM, only 0x00c000 - 0x00ffff used)
    block  2: 0x010000 - 0x017fff (unused)
    block  3: 0x018000 - 0x01ffff (LPU system ROM)
    block  4: 0x020000 - 0x027fff (LPU RAM)
    block  5: 0x028000 - 0x02ffff (PPU system ROM)
    block  6: 0x030000 - 0x037fff (LPU RAM)
    block  7: 0x038000 - 0x03ffff (LPU option ROM)
    block 10: 0x040000 - 0x047fff (LPU RAM)
    block 11: 0x048000 - 0x04ffff (PPU option ROM)
    block 12: 0x050000 - 0x057fff (LPU RAM)
    block 13: 0x058000 - 0x05ffff (LPU option ROM)
    block 14: 0x060000 - 0x067fff (LPU RAM)
    block 15: 0x068000 - 0x06ffff (PPU option ROM)
    block 16: 0x070000 - 0x077fff (LPU RAM)
    block 17: 0x078000 - 0x07ffff (unused)

    notes:
    - all block numbers are octal
    - blocks 20 to 76 are reserved for 512 kbyte RAM boards (p/n 09845-66590)
    - block 45 is reserved for the Test ROM
    - memory addresses are continuous (for convenience, the mapping below uses block numbers as
      address part above 0xffff, so there are gaps between 0x8000 and 0xffff which are masked out).
    - all LPU RAM is dynamically mapped at machine start according to -ramsize option
*/

void hp9845_base_state::global_mem_map(address_map &map)
{
	map.global_mask(0x3f7fff);
	map.unmap_value_low();
	map(0x014000, 0x017fff).ram().share("ppu_ram");
	map(0x030000, 0x037fff).rom().region("lpu", 0);
	map(0x050000, 0x057fff).rom().region("ppu", 0);
}

void hp9845_base_state::ppu_io_map(address_map &map)
{
	map.unmap_value_low();
	// PA = 0, IC = 0..1
	// Internal printer
	map(HP_MAKE_IOADDR(PRINTER_PA, 0), HP_MAKE_IOADDR(PRINTER_PA, 1)).rw("printer", FUNC(hp9845_printer_device::printer_r), FUNC(hp9845_printer_device::printer_w));
	// PA = 0, IC = 2
	// Keyboard scancode input
	map(HP_MAKE_IOADDR(0, 2), HP_MAKE_IOADDR(0, 2)).r(FUNC(hp9845_base_state::kb_scancode_r));
	// PA = 0, IC = 3
	// Keyboard status input & keyboard interrupt clear
	map(HP_MAKE_IOADDR(0, 3), HP_MAKE_IOADDR(0, 3)).rw(FUNC(hp9845_base_state::kb_status_r), FUNC(hp9845_base_state::kb_irq_clear_w));
	// PA = 13, IC = 0..3
	// Graphic video
	map(HP_MAKE_IOADDR(GVIDEO_PA, 0), HP_MAKE_IOADDR(GVIDEO_PA, 3)).rw(FUNC(hp9845_base_state::graphic_r), FUNC(hp9845_base_state::graphic_w));
	// PA = 14, IC = 0..3
	// Left-hand side tape drive (T14)
	map(HP_MAKE_IOADDR(T14_PA, 0), HP_MAKE_IOADDR(T14_PA, 3)).rw(m_t14, FUNC(hp_taco_device::reg_r), FUNC(hp_taco_device::reg_w));
	// PA = 15, IC = 0..3
	// Right-hand side tape drive (T15)
	map(HP_MAKE_IOADDR(T15_PA, 0), HP_MAKE_IOADDR(T15_PA, 3)).rw(m_t15, FUNC(hp_taco_device::reg_r), FUNC(hp_taco_device::reg_w));
}

void hp9845_base_state::hp9845_base(machine_config &config)
{
	HP_5061_3001(config , m_lpu , 5700000);
	// Clock scaling takes into account the slowdown caused by DRAM refresh
	m_lpu->set_clock_scale(0.93);
	m_lpu->set_addrmap(AS_PROGRAM , &hp9845_base_state::global_mem_map);
	m_lpu->set_9845_boot_mode(true);
	m_lpu->set_rw_cycles(6 , 6);
	m_lpu->set_relative_mode(true);
	HP_5061_3001(config , m_ppu , 5700000);
	m_ppu->set_clock_scale(0.93);
	m_ppu->set_addrmap(AS_PROGRAM , &hp9845_base_state::global_mem_map);
	m_ppu->set_addrmap(AS_IO , &hp9845_base_state::ppu_io_map);
	m_ppu->set_9845_boot_mode(true);
	m_ppu->set_rw_cycles(6 , 6);
	m_ppu->set_relative_mode(true);
	m_ppu->set_int_cb(m_io_sys , FUNC(hp98x5_io_sys_device::int_r));
	m_ppu->pa_changed_cb().set(m_io_sys , FUNC(hp98x5_io_sys_device::pa_w));

	HP98X5_IO_SYS(config , m_io_sys , 0);
	m_io_sys->irl().set_inputline(m_ppu, HPHYBRID_IRL);
	m_io_sys->irh().set_inputline(m_ppu, HPHYBRID_IRH);
	m_io_sys->sts().set(m_ppu , FUNC(hp_5061_3001_cpu_device::status_w));
	m_io_sys->flg().set(m_ppu , FUNC(hp_5061_3001_cpu_device::flag_w));
	m_io_sys->dmar().set(m_ppu , FUNC(hp_5061_3001_cpu_device::dmar_w));

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);

	TIMER(config, m_gv_timer).configure_generic(FUNC(hp9845_base_state::gv_timer));

	// Actual keyboard refresh rate should be KEY_SCAN_OSCILLATOR / 128 (2560 Hz)
	TIMER(config, "kb_timer").configure_periodic(FUNC(hp9845_base_state::kb_scan), attotime::from_hz(100));

	// Beeper
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper , KEY_SCAN_OSCILLATOR / 512).add_route(ALL_OUTPUTS , "mono" , 0.50);

	TIMER(config, m_beep_timer).configure_generic(FUNC(hp9845_base_state::beeper_off));

	// Tape drives
	HP_TACO(config , m_t15 , 4000000);
	m_t15->irq().set([this](int state) { m_io_sys->set_irq(T15_PA , state); });
	m_t15->flg().set([this](int state) { m_io_sys->set_flg(T15_PA , state); });
	m_t15->sts().set([this](int state) { m_io_sys->set_sts(T15_PA , state); });
	HP_TACO(config , m_t14 , 4000000);
	m_t14->irq().set([this](int state) { m_io_sys->set_irq(T14_PA , state); });
	m_t14->flg().set([this](int state) { m_io_sys->set_flg(T14_PA , state); });
	m_t14->sts().set([this](int state) { m_io_sys->set_sts(T14_PA , state); });

	// In real machine there were 8 slots for LPU ROMs and 8 slots for PPU ROMs in
	// right-hand side and left-hand side drawers, respectively.
	// Here we do away with the distinction between LPU & PPU ROMs: in the end they
	// are visible to both CPUs at the same addresses.
	HP9845_OPTROM(config, "drawer1", 0);
	HP9845_OPTROM(config, "drawer2", 0);
	HP9845_OPTROM(config, "drawer3", 0);
	HP9845_OPTROM(config, "drawer4", 0);
	HP9845_OPTROM(config, "drawer5", 0);
	HP9845_OPTROM(config, "drawer6", 0);
	HP9845_OPTROM(config, "drawer7", 0);
	HP9845_OPTROM(config, "drawer8", 0);

	// I/O slots
	for (unsigned slot = 0; slot < 4; slot++) {
		auto& finder = m_io_slot[ slot ];
		hp9845_io_slot_device& tmp( HP9845_IO_SLOT(config , finder , 0) );
		tmp.irq().set([this , slot](int state) { set_irq_slot(slot , state); });
		tmp.sts().set([this , slot](int state) { set_sts_slot(slot , state); });
		tmp.flg().set([this , slot](int state) { set_flg_slot(slot , state); });
		tmp.irq_nextsc().set([this , slot](int state) { set_irq_nextsc_slot(slot , state); });
		tmp.sts_nextsc().set([this , slot](int state) { set_sts_nextsc_slot(slot , state); });
		tmp.flg_nextsc().set([this , slot](int state) { set_flg_nextsc_slot(slot , state); });
		tmp.dmar().set([this , slot](int state) { set_dmar_slot(slot , state); });
	}

	// LPU memory options
	RAM(config, RAM_TAG).set_default_size("192K").set_extra_options("64K, 320K, 448K");

	// Internal printer
	hp9845_printer_device& prt{ HP9845_PRINTER(config , "printer" , 0) };
	prt.irq().set(FUNC(hp9845_base_state::prt_irl_w));
	prt.flg().set([this](int state) { m_io_sys->set_flg(PRINTER_PA , state); });
	prt.sts().set([this](int state) { m_io_sys->set_sts(PRINTER_PA , state); });
}

void hp9845b_state::hp9845b(machine_config &config)
{
	hp9845_base(config);
	// video hardware
	m_screen->set_screen_update(FUNC(hp9845b_state::screen_update));
	m_screen->screen_vblank().set(FUNC(hp9845b_state::vblank_w));
	m_screen->set_color(rgb_t::green());
	// These parameters are for alpha video
	m_screen->set_raw(VIDEO_PIXEL_CLOCK , VIDEO_HTOTAL , 0 , VIDEO_HBSTART , VIDEO_VTOTAL , 0 , VIDEO_ACTIVE_SCANLINES);
	PALETTE(config, m_palette).set_entries(4);
	TIMER(config, "scantimer").configure_scanline(FUNC(hp9845b_state::scanline_timer), "screen", 0, 1);

	config.set_default_layout(layout_hp9845b);

	SOFTWARE_LIST(config, "optrom_list").set_original("hp9845b_rom");
}

void hp9845c_state::hp9845c(machine_config &config)
{
	hp9845_base(config);
	// video hardware
	m_screen->set_screen_update(FUNC(hp9845c_state::screen_update));
	m_screen->screen_vblank().set(FUNC(hp9845c_state::vblank_w));
	m_screen->set_raw(VIDEO_770_PIXEL_CLOCK , VIDEO_770_HTOTAL , VIDEO_770_HBEND , VIDEO_770_HBSTART , VIDEO_770_VTOTAL , VIDEO_770_VBEND , VIDEO_770_VBSTART);
	PALETTE(config, m_palette).set_entries(24);
	TIMER(config, "scantimer").configure_scanline(FUNC(hp9845c_state::scanline_timer), "screen", 0, 1);

	SOFTWARE_LIST(config, "optrom_list").set_original("hp9845b_rom");
}

void hp9845t_state::hp9845t(machine_config &config)
{
	hp9845_base(config);
	// video hardware
	m_screen->set_screen_update(FUNC(hp9845t_state::screen_update));
	m_screen->screen_vblank().set(FUNC(hp9845t_state::vblank_w));
	m_screen->set_color(rgb_t::green());
	m_screen->set_raw(VIDEO_780_PIXEL_CLOCK , VIDEO_780_HTOTAL , VIDEO_780_HBEND , VIDEO_780_HBSTART , VIDEO_780_VTOTAL , VIDEO_780_VBEND , VIDEO_780_VBSTART);
	PALETTE(config, m_palette).set_entries(5);
	TIMER(config, "scantimer").configure_scanline(FUNC(hp9845t_state::scanline_timer), "screen", 0, 1);

	SOFTWARE_LIST(config, "optrom_list").set_original("hp9845b_rom");
}

ROM_START( hp9845a )
	ROM_REGION( 0200000, "lpu", ROMREGION_16BIT | ROMREGION_BE )
	ROM_LOAD( "09845-65544-65547-03-system_lpu.bin", 0000000, 0200000, CRC(47beb87f) SHA1(456caefacafcf19435e1e7e68b1c1e4010841664) )

	ROM_REGION( 0200000, "ppu", ROMREGION_16BIT | ROMREGION_BE )
	ROM_LOAD( "09845-65540-65543-01-system_ppu.bin", 0000000, 0160000, CRC(bc0a34cc) SHA1(9ff215f4ba32ad85f144845d15f762a71e35588b) )
ROM_END

#define rom_hp9845s rom_hp9845a

ROM_START( hp9835a )
	ROM_REGION( 0200000, "lpu", ROMREGION_16BIT | ROMREGION_BE )
	ROM_LOAD( "1818-2800-03_00-system-lpu.bin", 0000000, 020000, CRC(e0b0977a) SHA1(5afdc6c725abff70b674e46688d8ab38ccf8f3c1) )
	ROM_LOAD( "1818-2801-03_10-system-lpu.bin", 0020000, 020000, CRC(c51c1e3a) SHA1(798964fa2e7a1fc149ce4400b694630049293119) )
	ROM_LOAD( "1818-2802-03_20-system-lpu.bin", 0040000, 020000, CRC(bba70a7e) SHA1(2d488594493f8dfcd753e462414cc51c24596a2c) )
	ROM_LOAD( "1818-2803-03_30-system-lpu.bin", 0060000, 020000, CRC(65e9eba6) SHA1(a11f5d37e8ed14a428335c43e785d635b02d1129) )
	ROM_LOAD( "1818-2804-03_40-system-lpu.bin", 0100000, 020000, CRC(ef83b695) SHA1(8ca2914609ece2c9c59ebba6ece3fcbc8929aeaf) )
	ROM_LOAD( "1818-2805-03_50-system-lpu.bin", 0120000, 020000, CRC(401d539f) SHA1(00bda59f71632c4d4fc3268c04262bb81ef0eeba) )
	ROM_LOAD( "1818-2806-03_60-system-lpu.bin", 0140000, 020000, CRC(fe353db5) SHA1(0fb52d82d3743008cdebebb20c488e34ce2fca4b) )
	ROM_LOAD( "1818-2807-03_70-system-lpu.bin", 0160000, 020000, CRC(45a3cc5e) SHA1(35c9959331acf7c98ab6a880915b03e3e783a656) )

	ROM_REGION( 0200000, "ppu", ROMREGION_16BIT | ROMREGION_BE )
	ROM_LOAD( "1818-2808-05_00-system-ppu.bin", 0000000, 020000, CRC(d0c96276) SHA1(cc578d586c4eda81469f29eb7cab7f667e0d5977) )
	ROM_LOAD( "1818-2809-05_30-system-ppu.bin", 0060000, 020000, CRC(ccdb7171) SHA1(1d24596bc1219983e7cb81f6987af094f2ca7d81) )
	ROM_LOAD( "1818-2810-05_40-system-ppu.bin", 0100000, 020000, CRC(97487d24) SHA1(823cd16671de8e6ff2c245060c99778acb6ff79c) )
	ROM_LOAD( "1818-2811-05_50-system-ppu.bin", 0120000, 020000, CRC(18aee6fd) SHA1(388d3b2a063ea2cfdfe9fb9f864fa5f08af817b0) )
	ROM_LOAD( "1818-2812-05_60-system-ppu.bin", 0140000, 020000, CRC(c0beeeae) SHA1(a5db36a7f7bad84c1013bd3ec4813c355f72427d) )
	ROM_LOAD( "1818-2813-05_70-system-ppu.bin", 0160000, 020000, CRC(75361bbf) SHA1(40f499c597da5c8c9a55a2a891976d946a54926b) )
ROM_END

#define rom_hp9835b rom_hp9835a

ROM_START( hp9845b )
	ROM_REGION(0x800 , "chargen" , 0)
	ROM_LOAD("chrgen.bin" , 0 , 0x800 , CRC(fe9e844f) SHA1(0c45ae00766ceba94a19bd5e154bd6d23e208cca))

	ROM_REGION(0x800 , "optional_chargen" , 0)
	ROM_LOAD("optional_chrgen.bin" , 0 , 0x800 , CRC(0ecfa63b) SHA1(c295e6393d1503d903c1d2ce576fa597df9746bf))

	ROM_REGION(0x10000, "lpu", ROMREGION_16BIT | ROMREGION_BE)
	ROM_LOAD("9845-lpu-standard-processor.bin", 0, 0x10000, CRC(dc266c1b) SHA1(1cf3267f13872fbbfc035b70f8b4ec6b5923f182))

	ROM_REGION(0x10000, "ppu", ROMREGION_16BIT | ROMREGION_BE)
	ROM_LOAD("9845-ppu-standard-graphics.bin", 0, 0x10000, CRC(f866510f) SHA1(3e22cd2072e3a5f3603a1eb8477b6b4a198d184d))

#if 0
	ROM_REGION( 0200000, "lpu", ROMREGION_16BIT | ROMREGION_BE )
	ROM_LOAD( "1818-0823-0827-03_00-revb-system_lpu.bin", 0000000, 020000, CRC(7e49c781) SHA1(866c9ebd98d94bb6f99692e29d2d83f55b38c4b6) )
	ROM_LOAD( "1818-0823-0827-03_10-revb-system_lpu.bin", 0020000, 020000, CRC(2f819e3d) SHA1(250886378c3ce2253229997007c7bf0be80a8d1d) )
	ROM_LOAD( "1818-0824-0828-03_20-reva-system_lpu.bin", 0040000, 020000, CRC(834f7063) SHA1(5c390ed74671e4663cc80d899d07b69fd1fb4be6) )
	ROM_LOAD( "1818-0824-0828-03_20-revb-system_lpu.bin", 0040000, 020000, CRC(aa221deb) SHA1(7878643405ee45405dc5269c3b6dc9459f39437b) )
	ROM_LOAD( "1818-0824-0828-03_30-reva-system_lpu.bin", 0060000, 020000, CRC(0ebafdb2) SHA1(80733bfb7026d39a294841221d80ec40eafffe34) )
	ROM_LOAD( "1818-0824-0828-03_30-revb-system_lpu.bin", 0060000, 020000, CRC(0ebafdb2) SHA1(80733bfb7026d39a294841221d80ec40eafffe34) )
	ROM_LOAD( "1818-0825-0829-03_40-revb-system_lpu.bin", 0100000, 020000, CRC(beb09a57) SHA1(b832b995fa21c219673f0c7cf215dee70698f4f1) )
	ROM_LOAD( "1818-0825-0829-03_50-revb-system_lpu.bin", 0120000, 020000, CRC(bbb06222) SHA1(b0bfe1b48fac61eb955e27e0ddfbea020e09e0eb) )
	ROM_LOAD( "1818-0826-0830-03_60-revc-system_lpu.bin", 0140000, 020000, CRC(5c1c3abe) SHA1(fa9f99bf7c8a6df5c71e9fd8c807f0a2ff06640d) )
	ROM_LOAD( "1818-0826-0830-03_70-revc-system_lpu.bin", 0160000, 020000, CRC(0c61a266) SHA1(0cfbf482e7f8e99c87b97c77cf178682cd7af7d6) )

	ROM_REGION( 0200000, "lpu_fast", ROMREGION_16BIT | ROMREGION_BE )
	ROM_LOAD( "1818-1506-1502-03_00-reva-system_fast_lpu.bin", 0000000, 020000, CRC(b77194d8) SHA1(6feec8605331783e6f5a2ab6d6cbd9285036e863) )
	ROM_LOAD( "1818-1506-1502-03_10-reva-system_fast_lpu.bin", 0020000, 020000, CRC(bc5557a5) SHA1(282237e561c3f2304cdeb45efa2432748581af45) )
	ROM_LOAD( "1818-1507-1503-03_20-reva-system_fast_lpu.bin", 0040000, 020000, CRC(2ebc71e2) SHA1(a2d39fb24d565465304833dfd0ff87dd5ef26fb3) )
	ROM_LOAD( "1818-1507-1503-03_30-reva-system_fast_lpu.bin", 0060000, 020000, CRC(82e56bc4) SHA1(36201f343382e533c248ddd123507a2e195cca39) )
	ROM_LOAD( "1818-1508-1504-03_40-reva-system_fast_lpu.bin", 0100000, 020000, CRC(70b0fcb0) SHA1(3f7ce60cad0ffec8344f33d584869492c7f73026) )
	ROM_LOAD( "1818-1508-1504-03_50-reva-system_fast_lpu.bin", 0120000, 020000, CRC(935fab96) SHA1(ecb1da2a0bd46e8c0da2875a1af8cf71d8f4bb56) )
	ROM_LOAD( "1818-1509-1505-03_60-reva-system_fast_lpu.bin", 0140000, 020000, CRC(f4119af7) SHA1(72a3e8b8d7d306e55f8adf0e23225bb81bc2b4ba) )
	ROM_LOAD( "1818-1509-1505-03_70-reva-system_fast_lpu.bin", 0160000, 020000, CRC(22fb0864) SHA1(4e1dce32e84ba216dbbd4116f3b22ca7f254f529) )

	ROM_REGION( 0200000, "ppu", ROMREGION_16BIT | ROMREGION_BE )
	ROM_LOAD( "1818-0833-0837-05_40-revc-system_ppu.bin", 0100000, 020000, CRC(d790795c) SHA1(7ba1e245a98379a34833a780898a784049e33b86) )
	ROM_LOAD( "1818-0833-0837-05_40-revd-system_ppu.bin", 0100000, 020000, CRC(49897e40) SHA1(780a9973ff26d40f470e2004fccceb1019f8ba7f) )
	ROM_LOAD( "1818-0833-0837-05_50-revc-system_ppu.bin", 0120000, 020000, CRC(ef8acde4) SHA1(e68648543aac2b841b08d7758949ba1339a83701) )
	ROM_LOAD( "1818-0833-0837-05_50-revd-system_ppu.bin", 0120000, 020000, CRC(54f61d07) SHA1(f807fb8a59cd9cd221f63907e6a86948a0bf7c1d) )
	ROM_LOAD( "1818-0834-0838-05_60-revc-system_ppu.bin", 0140000, 020000, CRC(20f2100a) SHA1(9304f0b069de9233d697588328f9657dbeabc254) )
	ROM_LOAD( "1818-0834-0838-05_60-revd-system_ppu.bin", 0140000, 020000, CRC(454af601) SHA1(54b56e67e855fd2d699a0dbef0b4d2e8c150c39b) )
	ROM_LOAD( "1818-0834-0838-05_70-revc-system_ppu.bin", 0160000, 020000, CRC(43f62491) SHA1(a9489b37b3fa8768ca6e503f346bd023833ae3ac) )
	ROM_LOAD( "1818-0834-0838-05_70-revd-system_ppu.bin", 0160000, 020000, CRC(43f62491) SHA1(a9489b37b3fa8768ca6e503f346bd023833ae3ac) )
	ROM_LOAD( "1818-1899-1898-05_60-reva-system_ppu.bin", 0140000, 020000, CRC(454af601) SHA1(54b56e67e855fd2d699a0dbef0b4d2e8c150c39b) )
	ROM_LOAD( "1818-1899-1898-05_70-reva-system_ppu.bin", 0160000, 020000, CRC(049604f2) SHA1(89bfd8e086bc9365f156966b0a62c3ac720fc627) )

	ROM_REGION( 0200000, "ppu_tops", ROMREGION_16BIT | ROMREGION_BE )
	ROM_LOAD( "1818-0831-0835-05_00-reva-tops_ppu.bin", 0000000, 020000, CRC(7ddce706) SHA1(746e34d3de52a17372af9a9eb1ed4974a4eae656) )
	ROM_LOAD( "1818-0831-0835-05_10-reva-tops_ppu.bin", 0020000, 020000, CRC(d7fc3d47) SHA1(a3d723fe62f047cb0c17d405d07bb0b08d08e830) )
	ROM_LOAD( "1818-1209-1208-05_00-revb-tops_ppu.bin", 0000000, 020000, CRC(0dc90614) SHA1(94c07553a62b2c86414bc95314601f90eb4e4022) )
	ROM_LOAD( "1818-1209-1208-05_10-revb-tops_ppu.bin", 0020000, 020000, CRC(4e362657) SHA1(b09098c0acd56b11ec3b72ff3e8b5a1e14ef3ae8) )
	ROM_LOAD( "1818-1592-1591-05_00-revb-tops_ppu.bin", 0000000, 020000, CRC(8cfe29a8) SHA1(f1007b6b1d3f2b603653880c44cec48b23701263) )
	ROM_LOAD( "1818-1592-1591-05_10-revb-tops_ppu.bin", 0020000, 020000, CRC(95048264) SHA1(36cfddef9d1289fdaf69596e10d95f88a520feae) )

	ROM_REGION( 0200000, "ppu_kbd_us", ROMREGION_16BIT | ROMREGION_BE )
	ROM_LOAD( "1818-0832-0836-05_20-revc-keyboard_us.bin", 0040000, 020000, CRC(3bf6268a) SHA1(65d7dfeaf34c74dbc86ebe5d3bb65c6bd10163cb) )
	ROM_LOAD( "1818-0832-0836-05_30-revc-keyboard_us.bin", 0060000, 020000, CRC(2dfc619c) SHA1(5c54ff502d1344907817210bfdfcab7f8d6b61bd) )

	ROM_REGION( 0200000, "ppu_kbd_de", ROMREGION_16BIT | ROMREGION_BE )
	ROM_LOAD( "1818-0841-0846-05_20-revc-keyboard_german.bin", 0040000, 020000, CRC(76667eca) SHA1(ac63e5d584d1f2da5668d8a9560f927f48e25e03) )
	ROM_LOAD( "1818-0841-0846-05_20-revd-keyboard_german.bin", 0060000, 020000, CRC(3bf6268a) SHA1(65d7dfeaf34c74dbc86ebe5d3bb65c6bd10163cb) )
	ROM_LOAD( "1818-0841-0846-05_30-revc-keyboard_german.bin", 0040000, 020000, CRC(2b83db22) SHA1(6eda714ce05d2d75f4c041e36b6b6df40697d94a) )
	ROM_LOAD( "1818-0841-0846-05_30-revd-keyboard_german.bin", 0060000, 020000, CRC(b4006959) SHA1(584a85f746a3b0c262fdf9e4be8e696c80cfd429) )
#endif
ROM_END

ROM_START( hp9845c )
	ROM_REGION(0x800 , "chargen" , 0)
	ROM_LOAD("chrgen.bin" , 0 , 0x800 , CRC(fe9e844f) SHA1(0c45ae00766ceba94a19bd5e154bd6d23e208cca))

	ROM_REGION(0x800 , "optional_chargen" , 0)
	ROM_LOAD("optional_chrgen.bin" , 0 , 0x800 , CRC(0ecfa63b) SHA1(c295e6393d1503d903c1d2ce576fa597df9746bf))

	ROM_REGION(0x10000, "lpu", ROMREGION_16BIT | ROMREGION_BE)
	ROM_LOAD("9845-lpu-standard-processor.bin", 0, 0x10000, CRC(dc266c1b) SHA1(1cf3267f13872fbbfc035b70f8b4ec6b5923f182))

	ROM_REGION(0x10000, "ppu", ROMREGION_16BIT | ROMREGION_BE)
	ROM_LOAD("9845-ppu-color-enhanced-graphics.bin", 0, 0x10000, CRC(96e11edc) SHA1(3f1da50edb35dfc57ec2ecfd816a8c8230e110bd))
ROM_END

ROM_START( hp9845t )
	ROM_REGION(0x1000 , "chargen" , 0)
	ROM_LOAD("1818-1395.bin" , 0 , 0x1000 , CRC(7b555edf) SHA1(3b08e094635ef02aef9a2e37b049c61bcf1ec037))

	ROM_REGION(0x10000, "lpu", ROMREGION_16BIT | ROMREGION_BE)
	ROM_LOAD("9845-lpu-standard-processor.bin", 0, 0x10000, CRC(dc266c1b) SHA1(1cf3267f13872fbbfc035b70f8b4ec6b5923f182))

	ROM_REGION(0x10000, "ppu", ROMREGION_16BIT | ROMREGION_BE)
	ROM_LOAD("9845-ppu-color-enhanced-graphics.bin", 0, 0x10000, CRC(96e11edc) SHA1(3f1da50edb35dfc57ec2ecfd816a8c8230e110bd))
ROM_END

ROM_START( hp9845b_de )
	ROM_REGION(0x800 , "chargen" , 0)
	ROM_LOAD("chrgen.bin" , 0 , 0x800 , CRC(fe9e844f) SHA1(0c45ae00766ceba94a19bd5e154bd6d23e208cca))

	ROM_REGION(0x800 , "optional_chargen" , 0)
	ROM_LOAD("optional_chrgen.bin" , 0 , 0x800 , CRC(0ecfa63b) SHA1(c295e6393d1503d903c1d2ce576fa597df9746bf))

	ROM_REGION(0x10000, "lpu", ROMREGION_16BIT | ROMREGION_BE)
	ROM_LOAD("9845-lpu-standard-processor.bin", 0, 0x10000, CRC(dc266c1b) SHA1(1cf3267f13872fbbfc035b70f8b4ec6b5923f182))

	ROM_REGION(0x10000, "ppu", ROMREGION_16BIT | ROMREGION_BE)
	ROM_LOAD("9845-ppu-standard-graphics-ger.bin", 0, 0x10000, CRC(c968363d) SHA1(bc6805403371ca49d1a137f22cd254e3b0e0dbb4))
ROM_END

ROM_START( hp9845c_de )
	ROM_REGION(0x800 , "chargen" , 0)
	ROM_LOAD("chrgen.bin" , 0 , 0x800 , CRC(fe9e844f) SHA1(0c45ae00766ceba94a19bd5e154bd6d23e208cca))

	ROM_REGION(0x800 , "optional_chargen" , 0)
	ROM_LOAD("optional_chrgen.bin" , 0 , 0x800 , CRC(0ecfa63b) SHA1(c295e6393d1503d903c1d2ce576fa597df9746bf))

	ROM_REGION(0x10000, "lpu", ROMREGION_16BIT | ROMREGION_BE)
	ROM_LOAD("9845-lpu-standard-processor.bin", 0, 0x10000, CRC(dc266c1b) SHA1(1cf3267f13872fbbfc035b70f8b4ec6b5923f182))

	ROM_REGION(0x10000, "ppu", ROMREGION_16BIT | ROMREGION_BE)
	ROM_LOAD("9845-ppu-color-enhanced-graphics-ger.bin", 0, 0x10000, CRC(a7ef79ee) SHA1(637742ed8fc8201a8e7bac62654f21c5409dfb76))
ROM_END

ROM_START( hp9845t_de )
	ROM_REGION(0x1000 , "chargen" , 0)
	ROM_LOAD("1818-1395.bin" , 0 , 0x1000 , CRC(7b555edf) SHA1(3b08e094635ef02aef9a2e37b049c61bcf1ec037))

	ROM_REGION(0x10000, "lpu", ROMREGION_16BIT | ROMREGION_BE)
	ROM_LOAD("9845-lpu-standard-processor.bin", 0, 0x10000, CRC(dc266c1b) SHA1(1cf3267f13872fbbfc035b70f8b4ec6b5923f182))

	ROM_REGION(0x10000, "ppu", ROMREGION_16BIT | ROMREGION_BE)
	ROM_LOAD("9845-ppu-color-enhanced-graphics-ger.bin", 0, 0x10000, CRC(a7ef79ee) SHA1(637742ed8fc8201a8e7bac62654f21c5409dfb76))
ROM_END

//    YEAR  NAME        PARENT   COMPAT  MACHINE  INPUT           CLASS          INIT        COMPANY            FULLNAME  FLAGS
COMP( 1977, hp9845a,    0,       0,      hp9845a, hp9845,         hp9845_state,  empty_init, "Hewlett-Packard", "9845A",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1977, hp9845s,    hp9845a, 0,      hp9845a, hp9845,         hp9845_state,  empty_init, "Hewlett-Packard", "9845S",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1979, hp9835a,    0,       0,      hp9835a, hp9845,         hp9845_state,  empty_init, "Hewlett-Packard", "9835A",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1979, hp9835b,    hp9835a, 0,      hp9835a, hp9845,         hp9845_state,  empty_init, "Hewlett-Packard", "9835B",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1979, hp9845b,    0,       0,      hp9845b, hp9845_base,    hp9845b_state, empty_init, "Hewlett-Packard", "9845B",  0 )
COMP( 1982, hp9845t,    0,       0,      hp9845t, hp9845ct,       hp9845t_state, empty_init, "Hewlett-Packard", "9845T",  0 )
COMP( 1980, hp9845c,    0,       0,      hp9845c, hp9845ct,       hp9845c_state, empty_init, "Hewlett-Packard", "9845C",  0 )
COMP( 1979, hp9845b_de, hp9845b, 0,      hp9845b, hp9845_base_de, hp9845b_state, empty_init, "Hewlett-Packard", "9845B (Germany)", 0 )
COMP( 1982, hp9845t_de, hp9845t, 0,      hp9845t, hp9845ct_de,    hp9845t_state, empty_init, "Hewlett-Packard", "9845T (Germany)", 0 )
COMP( 1980, hp9845c_de, hp9845c, 0,      hp9845c, hp9845ct_de,    hp9845c_state, empty_init, "Hewlett-Packard", "9845C (Germany)", 0 )



hp98x6.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:F. Ulivi

// **************************
// Driver for HP 98x6 systems
// **************************
//
// What's in the emulated systems:
//
// |                                Model | 9 | 9 | 9 | 9 |
// |                                      | 8 | 8 | 8 | 8 |
// |                                      | 1 | 2 | 3 | 3 |
// |                                      | 6 | 6 | 6 | 6 |
// | Feature                              | A | A | A | C |
// |--------------------------------------+---+---+---+---|
// | 8MHz M68000 CPU                      | * | * | * | * |
// | Boot ROM                             | * | * | * | * |
// | Variable size RAM                    | * | * | * | * |
// | HPIB interface                       | * | * | * | * |
// | HLE of 8041 UPI                      | * | * | * | * |
// | Large keyboard (98203B)              | * | * | * | * |
// | Knob                                 | * | * | * | * |
// | Beeper                               | * | * | * | * |
// | ID PROM                              | * | * | * | * |
// | Option ROMs                          | * | * | * | * |
// | B/W 80x25 text video w/ attributes   | * |   | * |   |
// | B/W 50x25 text video w/ attributes   |   | * |   |   |
// | B/W 400x300 graphic video            | * | * |   |   |
// | B/W 512x390 graphic video            |   |   | * |   |
// | Color 80x25 text video w/ attributes |   |   |   | * |
// | Color 512x390 graphic video          |   |   |   | * |
// | Correct char. generator              | * | * | N | N |
// | 5.25 floppy drives                   |   | 1 | 2 | 2 |
// | RS232 interface                      | * |   |   |   |
// | Expansion cards (hp98628, hp98629)   | * | * | * | * |
//
// What's not in for 9836A/C models:
// - Correct character generator
//
// Main references:
// - Olivier De Smet's standalone emulator:
//   https://sites.google.com/site/olivier2smet2/hp_projects/hp98x6
// - Tony Duell's reverse engineered schematics (available @ hpmuseum.net)
// - HP 98615-90074, Feb 85, Pascal 3.0 System's Designer Guide (available @ hpmuseum.net)

#include "emu.h"

#include "hp98x6_optrom.h"
#include "hp98x6_upi.h"

#include "bus/hp_dio/hp_dio.h"
#include "bus/ieee488/ieee488.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
#include "imagedev/floppy.h"
#include "machine/74123.h"
#include "machine/input_merger.h"
#include "machine/ins8250.h"
#include "machine/ram.h"
#include "machine/rescap.h"
#include "machine/tms9914.h"
#include "machine/wd_fdc.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"

// Debugging
#define LOG_FDC_MASK    (LOG_GENERAL << 1)
#define LOG_FDC(...)    LOGMASKED(LOG_FDC_MASK, __VA_ARGS__)
#define LOG_FDC_DATA_MASK   (LOG_FDC_MASK << 1)
#define LOG_FDC_DATA(...)   LOGMASKED(LOG_FDC_DATA_MASK, __VA_ARGS__)
#undef VERBOSE
#define VERBOSE 0
#include "logmacro.h"

namespace {

// CPU clock
constexpr auto CPU_CLOCK = 16_MHz_XTAL / 2;

// 9816: video dot clock
constexpr unsigned DOT_CLOCK_9816 = 19'988'000;

// 9826: video dot clock
constexpr auto DOT_CLOCK_9826 = 10_MHz_XTAL;

// 9836(c): video dot clock
constexpr unsigned DOT_CLOCK_9836 = 25'771'500;

// HPIB and UPI clock
constexpr auto HPIB_CLOCK = 10_MHz_XTAL / 2;

// UART clock
constexpr auto UART_CLOCK = 2.4576_MHz_XTAL;

// Bit manipulation
template<typename T> constexpr T BIT_MASK(unsigned n)
{
	return (T)1U << n;
}

template<typename T> void BIT_CLR(T& w , unsigned n)
{
	w &= ~BIT_MASK<T>(n);
}

template<typename T> void BIT_SET(T& w , unsigned n)
{
	w |= BIT_MASK<T>(n);
}

// +-------------------+
// | hp98x6_base_state |
// +-------------------+
//         ↑
//         +-------------------+
//         ↑                   ↑
// +-----------------+  +--------------+
// | hp9826_36_state |  | hp9816_state |
// +-----------------+  +--------------+
//         ↑
//         +----------------+------------------+
//         ↑                ↑                  ↑
// +--------------+  +--------------+  +---------------+
// | hp9826_state |  | hp9836_state |  | hp9836c_state |
// +--------------+  +--------------+  +---------------+
class hp98x6_base_state : public driver_device
{
protected:
	hp98x6_base_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_ram(*this, RAM_TAG)
		, m_crtc(*this, "crtc")
		, m_palette(*this, "palette")
		, m_screen(*this, "screen")
		, m_upi(*this, "upi")
		, m_hpib(*this, "hpib")
		, m_dio_bus(*this, "diobus")
		, m_irq1_merger(*this , "merge_irq1")
		, m_irq2_merger(*this , "merge_irq2")
		, m_irq3_merger(*this , "merge_irq3")
		, m_irq4_merger(*this , "merge_irq4")
		, m_irq5_merger(*this , "merge_irq5")
		, m_irq6_merger(*this , "merge_irq6")
		, m_irq7_merger(*this , "merge_irq7")
		, m_chargen(*this, "chargen")
		, m_rom_drawers(*this, "drawer%u", 0U)
	{
	}

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void hp98x6_base(machine_config &mconfig, unsigned dot_clock, int char_width);
	virtual void cpu_mem_map(address_map &map) ATTR_COLD;
	void diag_led_w(uint8_t data);
	virtual void cpu_reset_w(int state);
	void hpib_irq_w(int state);
	void upi_irq7_w(int state);

	required_device<m68000_device> m_cpu;
	required_device<ram_device> m_ram;
	required_device<mc6845_device> m_crtc;
	required_device<palette_device> m_palette;
	required_device<screen_device> m_screen;
	required_device<hp98x6_upi_device> m_upi;
	required_device<tms9914_device> m_hpib;
	required_device<bus::hp_dio::dio16_device> m_dio_bus;
	required_device<input_merger_any_high_device> m_irq1_merger;
	required_device<input_merger_any_high_device> m_irq2_merger;
	required_device<input_merger_any_high_device> m_irq3_merger;
	required_device<input_merger_any_high_device> m_irq4_merger;
	required_device<input_merger_any_high_device> m_irq5_merger;
	required_device<input_merger_any_high_device> m_irq6_merger;
	required_device<input_merger_any_high_device> m_irq7_merger;

	// Character generator
	required_region_ptr<uint8_t> m_chargen;

	required_device_array<hp98x6_optrom_device, 2> m_rom_drawers;

	bool m_hsync_en;
	bool m_graphic_en;
	bool m_hpib_irq;
	bool m_hpib_dma_en;
	bool m_upi_irq7;
};

void hp98x6_base_state::machine_start()
{
	save_item(NAME(m_hsync_en));
	save_item(NAME(m_graphic_en));
	save_item(NAME(m_hpib_irq));
	save_item(NAME(m_hpib_dma_en));
	save_item(NAME(m_upi_irq7));

	auto space = &m_cpu->space(AS_PROGRAM);

	space->install_ram(0x1000000 - m_ram->size(), 0xffffff, m_ram->pointer());

	for (auto &finder : m_rom_drawers) {
		finder->install_handlers(space);
	}
}

void hp98x6_base_state::machine_reset()
{
	m_hsync_en = false;
	m_graphic_en = false;
	m_hpib_dma_en = false;
	diag_led_w(0);
}

void hp98x6_base_state::hp98x6_base(machine_config &config, unsigned dot_clock, int char_width)
{
	M68000(config, m_cpu, CPU_CLOCK);
	m_cpu->set_addrmap(AS_PROGRAM, &hp98x6_base_state::cpu_mem_map);
	m_cpu->reset_cb().set(FUNC(hp98x6_base_state::cpu_reset_w));

	RAM(config, m_ram).set_default_size("256K").set_extra_options("512K,1M");

	MC6845(config, m_crtc, dot_clock / char_width);
	m_crtc->set_char_width(char_width);
	m_crtc->set_screen(m_screen);
	m_crtc->set_show_border_area(false);

	HP98X6_UPI(config, m_upi, HPIB_CLOCK);
	m_upi->irq7_write_cb().set(FUNC(hp98x6_base_state::upi_irq7_w));

	TMS9914(config, m_hpib, HPIB_CLOCK);
	m_hpib->int_write_cb().set(FUNC(hp98x6_base_state::hpib_irq_w));
	m_hpib->dio_read_cb().set(IEEE488_TAG, FUNC(ieee488_device::dio_r));
	m_hpib->dio_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_dio_w));
	m_hpib->eoi_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_eoi_w));
	m_hpib->dav_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_dav_w));
	m_hpib->nrfd_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_nrfd_w));
	m_hpib->ndac_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ndac_w));
	m_hpib->ifc_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ifc_w));
	m_hpib->srq_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_srq_w));
	m_hpib->atn_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_atn_w));
	m_hpib->ren_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ren_w));

	ieee488_device &ieee(IEEE488(config, IEEE488_TAG));
	ieee.eoi_callback().set(m_hpib , FUNC(tms9914_device::eoi_w));
	ieee.dav_callback().set(m_hpib , FUNC(tms9914_device::dav_w));
	ieee.nrfd_callback().set(m_hpib , FUNC(tms9914_device::nrfd_w));
	ieee.ndac_callback().set(m_hpib , FUNC(tms9914_device::ndac_w));
	ieee.ifc_callback().set(m_hpib , FUNC(tms9914_device::ifc_w));
	ieee.srq_callback().set(m_hpib , FUNC(tms9914_device::srq_w));
	ieee.atn_callback().set(m_hpib , FUNC(tms9914_device::atn_w));
	ieee.ren_callback().set(m_hpib , FUNC(tms9914_device::ren_w));
	IEEE488_SLOT(config, "ieee_dev", 0, hp_ieee488_devices, nullptr);
	IEEE488_SLOT(config, "ieee_rem", 0, remote488_devices, nullptr);

	// Optional ROM slots
	for (auto &finder : m_rom_drawers) {
		HP98X6_OPTROM(config, finder);
	}

	SOFTWARE_LIST(config, "optrom_list").set_original("hp98x6_rom");

	DIO16(config, m_dio_bus, 0);
	m_dio_bus->set_program_space(m_cpu, AS_PROGRAM);
	m_cpu->reset_cb().append(m_dio_bus, FUNC(bus::hp_dio::dio16_device::reset_in));
	// IRQ mergers
	INPUT_MERGER_ANY_HIGH(config, m_irq1_merger).output_handler().set_inputline(m_cpu, M68K_IRQ_1);
	INPUT_MERGER_ANY_HIGH(config, m_irq2_merger).output_handler().set_inputline(m_cpu, M68K_IRQ_2);
	INPUT_MERGER_ANY_HIGH(config, m_irq3_merger).output_handler().set_inputline(m_cpu, M68K_IRQ_3);
	INPUT_MERGER_ANY_HIGH(config, m_irq4_merger).output_handler().set_inputline(m_cpu, M68K_IRQ_4);
	INPUT_MERGER_ANY_HIGH(config, m_irq5_merger).output_handler().set_inputline(m_cpu, M68K_IRQ_5);
	INPUT_MERGER_ANY_HIGH(config, m_irq6_merger).output_handler().set_inputline(m_cpu, M68K_IRQ_6);
	INPUT_MERGER_ANY_HIGH(config, m_irq7_merger).output_handler().set_inputline(m_cpu, M68K_IRQ_7);

	m_dio_bus->irq1_out_cb().set(m_irq1_merger, FUNC(input_merger_any_high_device::in_w<0>));
	m_dio_bus->irq2_out_cb().set(m_irq2_merger, FUNC(input_merger_any_high_device::in_w<0>));
	m_dio_bus->irq3_out_cb().set(m_irq3_merger, FUNC(input_merger_any_high_device::in_w<0>));
	m_dio_bus->irq4_out_cb().set(m_irq4_merger, FUNC(input_merger_any_high_device::in_w<0>));
	m_dio_bus->irq5_out_cb().set(m_irq5_merger, FUNC(input_merger_any_high_device::in_w<0>));
	m_dio_bus->irq6_out_cb().set(m_irq6_merger, FUNC(input_merger_any_high_device::in_w<0>));
	m_dio_bus->irq7_out_cb().set(m_irq7_merger, FUNC(input_merger_any_high_device::in_w<0>));

	m_upi->irq1_write_cb().set(m_irq1_merger, FUNC(input_merger_any_high_device::in_w<1>));

	DIO16_SLOT(config, "slot0", 0, "diobus", dio16_hp98x6_cards, nullptr, false);
	DIO16_SLOT(config, "slot1", 0, "diobus", dio16_hp98x6_cards, nullptr, false);
}

void hp98x6_base_state::cpu_mem_map(address_map &map)
{
	map.unmap_value_high();
	// Range           DTACK Device
	// ==============================
	// 00'0000-00'ffff *     Boot ROM
	// 01'0000-01'ffff *     Diagnostic LEDs (write only)
	// 02'0000-3f'ffff *     None
	// fx'0000-ff'ffff *     RAM
	map(0x000000, 0x00ffff).rom().region("cpu", 0);
	map(0x010000, 0x01ffff).w(FUNC(hp98x6_base_state::diag_led_w)).umask16(0x00ff);
	map(0x020000, 0x3fffff).noprw();
}

void hp98x6_base_state::diag_led_w(uint8_t data)
{
	LOG("LEDS=%02x %c%c%c%c %c%c%c%c\n" ,
		data,
		BIT(data , 7) ? '.' : '*',
		BIT(data , 6) ? '.' : '*',
		BIT(data , 5) ? '.' : '*',
		BIT(data , 4) ? '.' : '*',
		BIT(data , 3) ? '.' : '*',
		BIT(data , 2) ? '.' : '*',
		BIT(data , 1) ? '.' : '*',
		BIT(data , 0) ? '.' : '*');
}

void hp98x6_base_state::cpu_reset_w(int state)
{
	if (state) {
		LOG("RESET from CPU\n");
		m_upi->reset();
		m_hpib->reset();
		m_hpib_dma_en = false;
		//m_crtc->reset();
		m_hsync_en = false;
		m_graphic_en = false;
	}
}

void hp98x6_base_state::hpib_irq_w(int state)
{
	m_hpib_irq = bool(state);
	m_irq3_merger->in_w<1>(state);
}

void hp98x6_base_state::upi_irq7_w(int state)
{
	m_upi_irq7 = bool(state);
	m_irq7_merger->in_w<1>(state);
}

// +--------------+
// | hp9816_state |
// +--------------+
class hp9816_state : public hp98x6_base_state
{
public:
	hp9816_state(const machine_config &mconfig, device_type type, const char *tag)
		: hp98x6_base_state(mconfig, type, tag)
		, m_uart(*this, "uart")
		, m_rs232(*this, "rs232")
		, m_sw2_dipswitches(*this, "sw2")
		, m_sw3_dipswitches(*this, "sw3")
	{
	}

	void hp9816(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<ins8250_device> m_uart;
	required_device<rs232_port_device> m_rs232;
	required_ioport m_sw2_dipswitches;
	required_ioport m_sw3_dipswitches;

private:
	static inline constexpr unsigned TEXT_VRAM_SIZE = 2048;
	static inline constexpr unsigned GRAPHIC_VRAM_SIZE = 16384;

	virtual void cpu_mem_map(address_map &map) override ATTR_COLD;
	uint16_t text_r(offs_t offset);
	void text_w(offs_t offset, uint16_t data, uint16_t mem_mask);
	uint16_t graphic_r(offs_t offset, uint16_t mem_mask);
	void graphic_w(offs_t offset, uint16_t data, uint16_t mem_mask);
	uint8_t hpib_r(offs_t offset);
	void hpib_w(offs_t offset, uint8_t data);
	void uart_reset();
	void uart_update_irq();
	uint8_t uart_r(offs_t offset);
	void uart_w(offs_t offset, uint8_t data);

	MC6845_UPDATE_ROW(crtc_update_row);

	virtual void cpu_reset_w(int state) override;
	void uart_irq_w(int state);

	uint16_t m_text_vram[TEXT_VRAM_SIZE];
	uint8_t m_graphic_vram[GRAPHIC_VRAM_SIZE];
	bool m_uart_int_en;
	bool m_uart_irq;
	bool m_uart_ocd3;
	bool m_uart_ocd4;
};

void hp9816_state::hp9816(machine_config &config)
{
	hp98x6_base_state::hp98x6_base(config, DOT_CLOCK_9816, 10);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER, rgb_t::white());
	// Parameters for 50 Hz frame rate
	m_screen->set_raw(DOT_CLOCK_9816, 1040, 0, 800, 384, 0, 300);
	m_screen->set_screen_update(m_crtc, FUNC(mc6845_device::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	m_crtc->set_update_row_callback(FUNC(hp9816_state::crtc_update_row));

	INS8250(config, m_uart, UART_CLOCK);
	m_uart->out_int_callback().set(FUNC(hp9816_state::uart_irq_w));
	m_uart->out_tx_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	m_uart->out_dtr_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	m_uart->out_rts_callback().set("rs232", FUNC(rs232_port_device::write_rts));
	m_uart->out_out1_callback().set("rs232", FUNC(rs232_port_device::write_spds));
	// OUT2 -> SRTS

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set(m_uart, FUNC(ins8250_device::rx_w));
	m_rs232->cts_handler().set(m_uart, FUNC(ins8250_device::cts_w));
	m_rs232->dsr_handler().set(m_uart, FUNC(ins8250_device::dsr_w));
	m_rs232->dcd_handler().set(m_uart, FUNC(ins8250_device::dcd_w));
	m_rs232->ri_handler().set(m_uart, FUNC(ins8250_device::ri_w));
}

void hp9816_state::machine_start()
{
	hp98x6_base_state::machine_start();

	save_item(NAME(m_text_vram));
	save_item(NAME(m_graphic_vram));
	save_item(NAME(m_uart_int_en));
	save_item(NAME(m_uart_ocd3));
	save_item(NAME(m_uart_ocd4));
}

void hp9816_state::machine_reset()
{
	hp98x6_base_state::machine_reset();
	uart_reset();
}

void hp9816_state::cpu_mem_map(address_map &map)
{
	hp98x6_base_state::cpu_mem_map(map);

	// Range           DTACK Device
	// ==============================
	// 40'0000-40'ffff       None
	// 41'0000-41'ffff *     Text video memory & 6845 (mirror of 51'0000)
	// 42'0000-42'ffff *     Keyboard UPI
	// 43'0000-43'ffff *     Graphic video memory (Mirror of 53'0000)
	// 44'0000-46'ffff       None
	// 47'0000-47'ffff *     HPIB
	// 48'0000-50'ffff       None
	// 51'0000-51'ffff *     Text video memory & 6845
	// 52'0000-52'ffff *     Keyboard UPI (mirror of 42'0000)
	// 53'0000-53'ffff *     Graphic video memory
	// 54'0000-56'ffff       None
	// 57'0000-57'ffff *     HPIB (mirror of 47'0000)
	// 58'0000-68'ffff       None
	// 69'0000-69'ffff *     UART
	// 6a'0000-fb'ffff       None
	map(0x400000, 0x40ffff).rw(m_cpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0x410000, 0x41ffff).mirror(0x100000).rw(FUNC(hp9816_state::text_r), FUNC(hp9816_state::text_w));
	map(0x420000, 0x42ffff).mirror(0x100000).mask(3).rw(m_upi, FUNC(hp98x6_upi_device::read), FUNC(hp98x6_upi_device::write)).umask16(0x00ff);
	map(0x430000, 0x43ffff).mirror(0x100000).rw(FUNC(hp9816_state::graphic_r), FUNC(hp9816_state::graphic_w));
	map(0x440000, 0x46ffff).rw(m_cpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0x470000, 0x47ffff).mirror(0x100000).rw(FUNC(hp9816_state::hpib_r), FUNC(hp9816_state::hpib_w)).umask16(0x00ff);
	map(0x480000, 0x50ffff).rw(m_cpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0x540000, 0x56ffff).rw(m_cpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0x580000, 0x68ffff).rw(m_cpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0x690000, 0x69ffff).rw(FUNC(hp9816_state::uart_r), FUNC(hp9816_state::uart_w)).umask16(0x00ff);
	map(0x6a0000, 0xfbffff).rw(m_cpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
}

uint16_t hp9816_state::text_r(offs_t offset)
{
	if (BIT(offset, 12)) {
		return m_text_vram[offset & (TEXT_VRAM_SIZE - 1)];
	} else if (BIT(offset, 0)) {
		return m_crtc->register_r();
	} else {
		LOG("Reading from non-existing register of 6845!\n");
		return 0;
	}
}

void hp9816_state::text_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (BIT(offset, 12)) {
		m_hsync_en = !BIT(offset, 14);
		COMBINE_DATA(&m_text_vram[offset & (TEXT_VRAM_SIZE - 1)]);
	} else if (ACCESSING_BITS_0_7) {
		if (BIT(offset, 0)) {
			m_crtc->register_w(uint8_t(data));
		} else {
			m_crtc->address_w(uint8_t(data));
		}
	} else {
		LOG("Access to 6845 on top byte!\n");
	}
}

uint16_t hp9816_state::graphic_r(offs_t offset, uint16_t mem_mask)
{
	m_graphic_en = !BIT(offset, 14);

	if (ACCESSING_BITS_0_7) {
		return m_graphic_vram[offset & (GRAPHIC_VRAM_SIZE - 1)];
	} else {
		return m_cpu->berr_r();
	}
}

void hp9816_state::graphic_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	m_graphic_en = !BIT(offset, 14);

	if (ACCESSING_BITS_0_7) {
		m_graphic_vram[offset & (GRAPHIC_VRAM_SIZE - 1)] = uint8_t(data);
	} else {
		return m_cpu->berr_w(0);
	}
}

uint8_t hp9816_state::hpib_r(offs_t offset)
{
	uint8_t res = 0;

	if (BIT(offset, 3)) {
		res = m_hpib->read(offset & 7);
	} else if (BIT(offset, 1)) {
		if (BIT(m_sw3_dipswitches->read(), 6)) {
			BIT_SET(res, 7);
		}
		if (!m_hpib->cont_r()) {
			BIT_SET(res, 6);
		}
		if (m_upi_irq7) {
			BIT_SET(res, 2);
		}
		if (BIT(m_sw3_dipswitches->read(), 7)) {
			BIT_SET(res, 0);
		}
	} else {
		if (BIT(m_sw3_dipswitches->read(), 5)) {
			BIT_SET(res, 7);
		}
		if (m_hpib_irq) {
			BIT_SET(res, 6);
		}
		BIT_SET(res, 2);
		if (m_hpib_dma_en) {
			BIT_SET(res, 0);
		}
	}

	return res;
}

void hp9816_state::hpib_w(offs_t offset, uint8_t data)
{
	if (BIT(offset, 3)) {
		m_hpib->write(offset & 7, data);
	} else {
		m_hpib_dma_en = BIT(data, 0);
	}
}

void hp9816_state::uart_reset()
{
	m_uart_int_en = false;
	m_uart_ocd4 = false;
	m_uart_ocd3 = false;
	m_uart->reset();
	uart_update_irq();
}

void hp9816_state::uart_update_irq()
{
	m_irq4_merger->in_w<1>(m_uart_irq && m_uart_int_en);
}

uint8_t hp9816_state::uart_r(offs_t offset)
{
	uint8_t res = 0;

	if (BIT(offset, 3)) {
		res = m_uart->ins8250_r(offset & 7);
	} else {
		switch (offset & 3) {
		case 0:
			if (BIT(m_sw3_dipswitches->read(), 4)) {
				BIT_SET(res, 7);
			}
			BIT_SET(res, 1);
			break;
		case 1:
			if (m_uart_int_en) {
				BIT_SET(res, 7);
			}
			if (m_uart_irq) {
				BIT_SET(res, 6);
			}
			BIT_SET(res, 4);
			break;
		case 2:
			if (m_uart_ocd3) {
				BIT_SET(res, 7);
			}
			if (m_uart_ocd4) {
				BIT_SET(res, 6);
			}
			res |= (m_sw3_dipswitches->read() & 0x0f);
			break;
		case 3:
			res = uint8_t(m_sw2_dipswitches->read());
			break;
		}
	}

	return res;
}

void hp9816_state::uart_w(offs_t offset, uint8_t data)
{
	if (BIT(offset, 3)) {
		m_uart->ins8250_w(offset & 7, data);
	} else {
		switch (offset & 3) {
		case 0:
			uart_reset();
			break;
		case 1:
			m_uart_int_en = BIT(data, 7);
			uart_update_irq();
			break;
		case 2:
			m_uart_ocd3 = BIT(data, 7);
			m_uart_ocd4 = BIT(data, 6);
			break;
		}
	}
}

MC6845_UPDATE_ROW(hp9816_state::crtc_update_row)
{
	if (m_hsync_en) {
		const pen_t *pen = m_palette->pens();
		bool clen = (ma & 0x3800) != 0x2800;
		bool glb_blink = (m_screen->frame_number() & 0x30) == 0;

		// Text video
		for (int i = 0; i < x_count; i++) {
			uint16_t char_attr = m_text_vram[(ma + i) & (TEXT_VRAM_SIZE - 1)];
			// | Bit(s) | Meaning                    |
			// |--------+----------------------------|
			// |     15 | Select alternate char. set |
			// | 14..12 | N/U                        |
			// |     11 | Half brightness            |
			// |     10 | Underline                  |
			// |      9 | Blink                      |
			// |      8 | Inverted                   |
			// |   7..0 | Character code             |

			bool half_bright = BIT(char_attr, 11);
			bool underline;
			bool blink = glb_blink && BIT(char_attr, 9);
			bool invert;
			uint16_t char_pixels;
			bool cursor;
			if (clen) {
				unsigned chargen_addr = ra | ((char_attr & 0xff) << 4);
				if (BIT(char_attr, 15)) {
					BIT_SET(chargen_addr, 12);
				}
				char_pixels = uint16_t(m_chargen[chargen_addr]) << 1;
				underline = BIT(char_attr, 10) && ra == 11;
				invert = BIT(char_attr, 8);
				cursor = cursor_x == i;
			} else {
				char_pixels = 0;
				underline = false;
				invert = false;
				cursor = false;
			}

			// Truth table of 1st video combiner
			//
			// | blink | cursor | underline | out      |
			// |-------+--------+-----------+----------|
			// |     0 |      0 |         0 | chargen  |
			// |     0 |      0 |         1 | ~chargen |
			// |     0 |      1 |         0 | ~chargen |
			// |     0 |      1 |         1 | chargen  |
			// |     1 |      0 |         0 | 0        |
			// |     1 |      0 |         1 | 0        |
			// |     1 |      1 |         0 | ~0       |
			// |     1 |      1 |         1 | ~0       |
			if (blink) {
				if (cursor) {
					char_pixels = ~0;
				} else {
					char_pixels = 0;
				}
			} else if (cursor ^ underline) {
				char_pixels = ~char_pixels;
			}

			// Truth table of 2nd video combiner
			//
			// | half_bright | Graphic | invert | Text | out |
			// |-------------+---------+--------+------+-----|
			// |           0 |       0 |      0 |    0 | 0   |
			// |           0 |       0 |      0 |    1 | 2   |
			// |           0 |       0 |      1 |    0 | 2   |
			// |           0 |       0 |      1 |    1 | 0   |
			// |           0 |       1 |      0 |    0 | 2   |
			// |           0 |       1 |      0 |    1 | 0   |
			// |           0 |       1 |      1 |    0 | 0   |
			// |           0 |       1 |      1 |    1 | 2   |
			// |           1 |       0 |      0 |    0 | 0   |
			// |           1 |       0 |      0 |    1 | 1   |
			// |           1 |       0 |      1 |    0 | 1   |
			// |           1 |       0 |      1 |    1 | 0   |
			// |           1 |       1 |      0 |    0 | 2   |
			// |           1 |       1 |      0 |    1 | 2   |
			// |           1 |       1 |      1 |    0 | 2   |
			// |           1 |       1 |      1 |    1 | 2   |
			if (invert) {
				char_pixels = ~char_pixels;
			}
			for (unsigned col = 0; col < 10; col++) {
				bitmap.pix(y, i * 10 + col) = pen[BIT(char_pixels, 9 - col) ? (half_bright ? 1 : 2) : 0];
			}
		}

		// Graphic video
		if (m_graphic_en) {
			unsigned g_cols = unsigned(x_count) * 5;
			// Hw only works correctly when x_count is 80 (-> 50 bytes per graphic line)
			unsigned g_bytes_per_line = g_cols / 8;
			unsigned g_addr = y * g_bytes_per_line;
			for (unsigned i = 0; i < g_bytes_per_line; i++) {
				uint8_t graph_pixels = m_graphic_vram[(g_addr + i) & (GRAPHIC_VRAM_SIZE - 1)];
				for (unsigned col = 0; col < 8; col++) {
					if (BIT(graph_pixels, 7 - col)) {
						// A graphic pixel covers 2 text pixels
						// Also, the hw seems to offset graphic pixels 1 text pixel to the right
						// Here this offset is not applied (as rightmost graphic pixel would be clipped)
						unsigned x_pos = i * 16 + col * 2;
						{
							auto &pix = bitmap.pix(y, x_pos);
							// Overlay rules (see 2nd video combiner table)
							//
							// |  Text | Combined |
							// | video | Video    |
							// |-------+----------|
							// |     0 | 2        |
							// |     1 | 2        |
							// |     2 | 0        |
							pix = pen[(pix == pen[2]) ? 0 : 2];
						}
						x_pos++;
						{
							auto &pix = bitmap.pix(y, x_pos);
							pix = pen[(pix == pen[2]) ? 0 : 2];
						}
					}
				}
			}
		}
	} else {
		bitmap.fill(rgb_t::black(), rectangle(0, unsigned(x_count) * 10 - 1, y, y));
	}
}

void hp9816_state::cpu_reset_w(int state)
{
	hp98x6_base_state::cpu_reset_w(state);

	if (state) {
		uart_reset();
	}
}

void hp9816_state::uart_irq_w(int state)
{
	m_uart_irq = bool(state);
	uart_update_irq();
}

static INPUT_PORTS_START(hp9816)
	PORT_START("sw2")
	PORT_DIPNAME(0x30, 0x00, "Parity type")
	PORT_DIPLOCATION("S2:4,3")
	PORT_DIPSETTING(0x00, "Odd")
	PORT_DIPSETTING(0x10, "Even")
	PORT_DIPSETTING(0x20, "One")
	PORT_DIPSETTING(0x30, "Zero")
	PORT_DIPNAME(0x08, 0x00, "Parity enable")
	PORT_DIPLOCATION("S2:5")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x08, DEF_STR(On))
	PORT_DIPNAME(0x04, 0x00, "Stop bits")
	PORT_DIPLOCATION("S2:6")
	PORT_DIPSETTING(0x00, "1")
	PORT_DIPSETTING(0x04, "2")
	PORT_DIPNAME(0x03, 0x03, "Bits/char")
	PORT_DIPLOCATION("S2:8,7")
	PORT_DIPSETTING(0x00, "5")
	PORT_DIPSETTING(0x01, "6")
	PORT_DIPSETTING(0x02, "7")
	PORT_DIPSETTING(0x03, "8")

	PORT_START("sw3")
	PORT_DIPNAME(0x0f, 0x0a, "Baud rate")
	PORT_DIPLOCATION("S3:4,3,2,1")
	PORT_DIPSETTING(0x00, "50")
	PORT_DIPSETTING(0x01, "75")
	PORT_DIPSETTING(0x02, "110")
	PORT_DIPSETTING(0x03, "134.5")
	PORT_DIPSETTING(0x04, "150")
	PORT_DIPSETTING(0x05, "200")
	PORT_DIPSETTING(0x06, "300")
	PORT_DIPSETTING(0x07, "600")
	PORT_DIPSETTING(0x08, "1200")
	PORT_DIPSETTING(0x09, "1800")
	PORT_DIPSETTING(0x0a, "2400")
	PORT_DIPSETTING(0x0b, "3600")
	PORT_DIPSETTING(0x0c, "4800")
	PORT_DIPSETTING(0x0d, "7200")
	PORT_DIPSETTING(0x0e, "9600")
	PORT_DIPSETTING(0x0f, "19200")
	PORT_DIPNAME(0x10, 0x00, "Remote keyboard")
	PORT_DIPLOCATION("S3:5")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x10, DEF_STR(On))
	PORT_DIPNAME(0x20, 0x20, "Continuous self-test")
	PORT_DIPLOCATION("S3:6")
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPSETTING(0x20, DEF_STR(Off))
	PORT_DIPNAME(0x40, 0x40, "HPIB Sys controller")
	PORT_DIPLOCATION("S3:7")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x40, DEF_STR(On))
	PORT_DIPNAME(0x80, 0x80, "CRT refresh rate")
	PORT_DIPLOCATION("S3:8")
	PORT_DIPSETTING(0x00, "50 Hz")
	PORT_DIPSETTING(0x80, "60 Hz")

INPUT_PORTS_END

ROM_START(hp9816a)
	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD("1818-3171.bin", 0, 0x2000, CRC(9cf4d4e1) SHA1(228484fd7ba7a4b59a051af083858383afe30179))

	ROM_REGION(0x100, "upi:idprom", 0)
	ROM_LOAD("prom9816.bin", 0, 0x100, CRC(f2e57a04) SHA1(7436c1334f30e6de3cf1a7c30f714c9f203cb4d3))

	ROM_REGION(0x10000, "cpu", 0)
	ROM_DEFAULT_BIOS("bios40")
	ROM_SYSTEM_BIOS(0, "bios40",  "BIOS v4.0")
	ROMX_LOAD("rom40.bin", 0x0000, 0x10000, CRC(36005480) SHA1(645a077ffd95e4c31f05cd8bbd6e4554b12813f1), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "bios30",  "BIOS v3.0")
	ROMX_LOAD("rom30.bin", 0x0000, 0x10000, CRC(05c07e75) SHA1(3066a65e6137482041f9a77d09ee2289fe0974aa), ROM_BIOS(1))
ROM_END

// +-----------------+
// | hp9826_36_state |
// +-----------------+
class hp9826_36_state : public hp98x6_base_state
{
public:
	hp9826_36_state(const machine_config &mconfig, device_type type, const char *tag)
		: hp98x6_base_state(mconfig, type, tag)
		, m_fdc(*this, "fdc")
		, m_drive0(*this, "drive0")
		, m_drive1(*this, "drive1")
		, m_ss(*this, "ss%u", 0)
		, m_fdc_timer(*this, "fdc_tmr")
		, m_sw1(*this, "sw1")
		, m_sys_ctrl_sw(*this, "sys_ctrl_sw")
		, m_idprom(*this, "idprom")
	{
	}

protected:
	// Bits in m_floppy_ctrl
	static inline constexpr unsigned FC_DRIVE_SEL_BIT = 6;
	static inline constexpr unsigned FC_SEC_BUFF_EN_BIT = 5;
	static inline constexpr unsigned FC_SEC_BUFF_DIR_BIT = 4;
	static inline constexpr unsigned FC_FDC_RST_BIT = 3;
	static inline constexpr unsigned FC_SIDE_SEL_BIT = 2;
	static inline constexpr unsigned FC_DRIVE_EN_BIT = 0;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void device_post_load() override;

	TIMER_DEVICE_CALLBACK_MEMBER(fdc_ram_io);

	void hp9826_36(machine_config &mconfig, unsigned dot_clock, int char_width);
	virtual void cpu_mem_map(address_map &map) override ATTR_COLD;
	virtual void cpu_reset_w(int state) override;
	unsigned get_sel_floppy() const;
	floppy_image_device *get_drive(unsigned idx) const;
	unsigned get_floppy_idx(floppy_image_device *floppy) const;
	void floppy_reset();
	void floppy_update_sel();
	void floppy_update_mon_hlt();
	void floppy_wp_cb(floppy_image_device *floppy, int state);
	void floppy_index_cb(floppy_image_device *floppy, int state);
	void fdc_hld_w(int state);
	uint8_t floppy_r(offs_t offset);
	void floppy_w(offs_t offset, uint8_t data);
	void fdc_irq_w(int state);
	void fdc_drq_w(int state);
	uint8_t hpib_r(offs_t offset);
	void hpib_w(offs_t offset, uint8_t data);
	uint16_t id_prom_r(offs_t offset);

	required_device<fd1793_device> m_fdc;
	required_device<floppy_connector> m_drive0;
	optional_device<floppy_connector> m_drive1;
	required_device_array<ttl74123_device, 2> m_ss;
	required_device<timer_device> m_fdc_timer;
	required_ioport m_sw1;
	required_ioport m_sys_ctrl_sw;

	// ID PROM
	required_region_ptr<uint8_t> m_idprom;

	uint8_t m_floppy_ctrl;
	uint8_t m_floppy_buffer[256];
	uint8_t m_buffer_addr;
	bool m_fdc_hld;
	bool m_fdc_irq;
	bool m_fdc_drq;
	bool m_drive_changed[2];
	floppy_image_device *m_curr_floppy;
};

void hp9826_36_state::machine_start()
{
	hp98x6_base_state::machine_start();

	save_item(NAME(m_floppy_ctrl));
	save_item(NAME(m_floppy_buffer));
	save_item(NAME(m_buffer_addr));
	save_item(NAME(m_fdc_hld));
	save_item(NAME(m_fdc_irq));
	save_item(NAME(m_fdc_drq));
	save_item(NAME(m_drive_changed));

	auto drive0 = get_drive(0);
	LOG_FDC("drive0 = %p\n", drive0);
	drive0->setup_wpt_cb(floppy_image_device::wpt_cb(&hp9826_36_state::floppy_wp_cb, this));
	drive0->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&hp9826_36_state::floppy_index_cb, this));
	auto drive1 = get_drive(1);
	LOG_FDC("drive1 = %p\n", drive1);
	if (drive1) {
		drive1->setup_wpt_cb(floppy_image_device::wpt_cb(&hp9826_36_state::floppy_wp_cb, this));
		drive1->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&hp9826_36_state::floppy_index_cb, this));
	}
}

void hp9826_36_state::machine_reset()
{
	hp98x6_base_state::machine_reset();
	m_curr_floppy = nullptr;
	floppy_reset();
}

void hp9826_36_state::device_post_load()
{
	hp98x6_base_state::device_post_load();

	m_curr_floppy = nullptr;
	// Bring m_curr_floppy in synch
	floppy_update_sel();
}

static void floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

void hp9826_36_state::hp9826_36(machine_config &mconfig, unsigned dot_clock, int char_width)
{
	hp98x6_base(mconfig, dot_clock, char_width);

	FD1793(mconfig, m_fdc, HPIB_CLOCK / 5);
	m_fdc->set_force_ready(true);
	m_fdc->dden_w(0);
	m_fdc->intrq_wr_callback().set(FUNC(hp9826_36_state::fdc_irq_w));
	m_fdc->drq_wr_callback().set(FUNC(hp9826_36_state::fdc_drq_w));
	m_fdc->hld_wr_callback().set(FUNC(hp9826_36_state::fdc_hld_w));

	FLOPPY_CONNECTOR(mconfig, m_drive0, floppies, "525dd", floppy_image_device::default_mfm_floppy_formats, true);

	// ~225 ms duration
	TTL74123(mconfig, m_ss[0], RES_K(23.7), CAP_U(33));
	m_ss[0]->set_connection_type(TTL74123_NOT_GROUNDED_NO_DIODE);
	m_ss[0]->set_a_pin_value(0);
	m_ss[0]->set_clear_pin_value(1);
	TTL74123(mconfig, m_ss[1], RES_K(23.7), CAP_U(33));
	m_ss[1]->set_connection_type(TTL74123_NOT_GROUNDED_NO_DIODE);
	m_ss[1]->set_a_pin_value(0);
	m_ss[1]->set_clear_pin_value(1);

	TIMER(mconfig, m_fdc_timer).configure_generic(FUNC(hp9826_36_state::fdc_ram_io));
}

void hp9826_36_state::cpu_mem_map(address_map &map)
{
	hp98x6_base_state::cpu_mem_map(map);

	// Range           DTACK Device
	// ==============================
	// 40'0000-41'ffff *     None
	// 42'0000-42'ffff *     Keyboard UPI
	// 43'0000-43'ffff *     None
	// 44'0000-44'ffff *     Floppy
	// 45'0000-46'ffff *     None
	// 47'0000-47'ffff *     HPIB
	// 48'0000-4f'ffff *     None
	// 50'0000-5e'ffff       None
	// 5f'0000-5f'3fff *     ID PROM & DIP switches
	// 5f'4000-fb'ffff       None
	map(0x400000, 0x41ffff).noprw();
	map(0x420000, 0x42ffff).mask(3).rw(m_upi, FUNC(hp98x6_upi_device::read), FUNC(hp98x6_upi_device::write)).umask16(0x00ff);
	map(0x430000, 0x43ffff).noprw();
	map(0x440000, 0x44ffff).rw(FUNC(hp9826_36_state::floppy_r), FUNC(hp9826_36_state::floppy_w)).umask16(0xff00);
	map(0x450000, 0x46ffff).noprw();
	map(0x470000, 0x47ffff).rw(FUNC(hp9826_36_state::hpib_r), FUNC(hp9826_36_state::hpib_w)).umask16(0x00ff);
	map(0x480000, 0x4fffff).noprw();
	map(0x500000, 0x5effff).rw(m_cpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0x5f0000, 0x5f3fff).nopw().r(FUNC(hp9826_36_state::id_prom_r));
	map(0x5f4000, 0xfbffff).rw(m_cpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
}

void hp9826_36_state::cpu_reset_w(int state)
{
	hp98x6_base_state::cpu_reset_w(state);

	if (state) {
		floppy_reset();
	}
}

unsigned hp9826_36_state::get_sel_floppy() const
{
	return BIT(m_floppy_ctrl, FC_DRIVE_SEL_BIT) ? 1 : 0;
}

floppy_image_device *hp9826_36_state::get_drive(unsigned idx) const
{
	if (idx) {
		return bool(m_drive1) ? m_drive1->get_device() : nullptr;
	} else {
		return m_drive0->get_device();
	}
}

unsigned hp9826_36_state::get_floppy_idx(floppy_image_device *floppy) const
{
	return get_drive(0) == floppy ? 0 : 1;
}

void hp9826_36_state::floppy_reset()
{
	m_floppy_ctrl = 0;
	m_fdc->mr_w(0);
	floppy_update_sel();
}

void hp9826_36_state::floppy_update_sel()
{
	floppy_image_device *new_floppy = get_drive(get_sel_floppy());

	if (new_floppy != m_curr_floppy) {
		m_curr_floppy = new_floppy;
		m_fdc->set_floppy(m_curr_floppy);
	}
	if (m_curr_floppy) {
		m_curr_floppy->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&hp9826_36_state::floppy_index_cb, this));
		m_curr_floppy->ss_w(BIT(m_floppy_ctrl, FC_SIDE_SEL_BIT));
	}
	floppy_update_mon_hlt();
}

void hp9826_36_state::floppy_update_mon_hlt()
{
	bool ss_state = bool(m_ss[get_sel_floppy()]->q_r());
	bool mon = BIT(m_floppy_ctrl, FC_DRIVE_EN_BIT) || (m_fdc_hld && ss_state);
	LOG_FDC("mon %d: hld %d ss %d\n", mon, m_fdc_hld, ss_state);
	get_drive(0)->mon_w(!mon);
	auto drive1 = get_drive(1);
	if (drive1) {
		drive1->mon_w(!mon);
	}
}

void hp9826_36_state::floppy_wp_cb(floppy_image_device *floppy, int state)
{
	LOG_FDC("floppy wp %p %d\n", floppy, state);
	if (state) {
		m_drive_changed[get_floppy_idx(floppy)] = true;
	}
}

void hp9826_36_state::floppy_index_cb(floppy_image_device *floppy, int state)
{
	LOG_FDC("floppy id %s %p %d\n", machine().time().as_string(), floppy, state);
	m_ss[get_floppy_idx(floppy)]->b_w(state);
	floppy_update_mon_hlt();
	if (floppy == m_curr_floppy) {
		m_fdc->index_callback(floppy, state);
	}
}

void hp9826_36_state::fdc_hld_w(int state)
{
	m_fdc_hld = bool(state);
	floppy_update_mon_hlt();
}

uint8_t hp9826_36_state::floppy_r(offs_t offset)
{
	uint8_t res = 0;
	if ((offset & 0x1e00) == 0x0800) {
		// Status register
		if (m_fdc_irq) {
			BIT_SET(res, 3);
		}
		// Margin error = 0 (bit 2)
		if (m_drive_changed[get_sel_floppy()]) {
			BIT_SET(res, 1);
		}
		if (m_fdc_drq) {
			BIT_SET(res, 0);
		}
	} else if (!BIT(m_floppy_ctrl, FC_SEC_BUFF_EN_BIT)) {
		if (BIT(offset, 12)) {
			// Buffer RAM
			m_buffer_addr = offset & 0xff;
			res = m_floppy_buffer[m_buffer_addr];
		} else if ((offset & 0x1800) == 0) {
			// FDC
			res = m_fdc->read(offset & 3);
		}
	}
	LOG_FDC_DATA("fdc R %04x=%02x\n", offset * 2, res);
	return res;
}

void hp9826_36_state::floppy_w(offs_t offset, uint8_t data)
{
	LOG_FDC_DATA("fdc W %s %04x=%02x\n", machine().time().as_string(), offset * 2, data);
	offs_t masked_off = offset & 0x1e00;
	if (masked_off == 0x800) {
		// Floppy control register
		m_floppy_ctrl = data;
		m_fdc->mr_w(BIT(m_floppy_ctrl, FC_FDC_RST_BIT));
		floppy_update_sel();
	} else if (masked_off == 0xa00) {
		// Disk changed reset command
		m_drive_changed[0] = false;
		m_drive_changed[1] = false;
	} else if (!BIT(m_floppy_ctrl, FC_SEC_BUFF_EN_BIT)) {
		if (BIT(offset, 12)) {
			// Buffer RAM
			m_buffer_addr = offset & 0xff;
			m_floppy_buffer[m_buffer_addr] = data;
		} else if ((offset & 0x1800) == 0) {
			// FDC
			m_fdc->write(offset & 3, data);
		}
	}
}

void hp9826_36_state::fdc_irq_w(int state)
{
	LOG_FDC("fdc IRQ %d\n", state);
	m_fdc_irq = bool(state);
	m_irq2_merger->in_w<1>(state);
}

void hp9826_36_state::fdc_drq_w(int state)
{
	LOG_FDC("fdc drq %d\n", state);
	if (!m_fdc_drq && state && BIT(m_floppy_ctrl, FC_SEC_BUFF_EN_BIT)) {
		m_fdc_timer->adjust(attotime::zero);
	}
	m_fdc_drq = bool(state);
}

TIMER_DEVICE_CALLBACK_MEMBER(hp9826_36_state::fdc_ram_io)
{
	if (BIT(m_floppy_ctrl, FC_SEC_BUFF_EN_BIT)) {
		if (BIT(m_floppy_ctrl, FC_SEC_BUFF_DIR_BIT)) {
			// FDC -> RAM
			uint8_t data = m_fdc->data_r();
			m_floppy_buffer[m_buffer_addr] = data;
			LOG_FDC_DATA("fdc RAM W @%02x=%02x\n", m_buffer_addr, data);
		} else {
			// RAM -> FDC
			m_fdc->data_w(m_floppy_buffer[m_buffer_addr]);
			LOG_FDC_DATA("fdc RAM R @%02x=%02x\n", m_buffer_addr, m_floppy_buffer[m_buffer_addr]);
		}
		m_buffer_addr++;
	}
}

uint8_t hp9826_36_state::hpib_r(offs_t offset)
{
	uint8_t res = 0;

	if (BIT(offset, 3)) {
		res = m_hpib->read(offset & 7);
	} else if (BIT(offset, 1)) {
		if (m_sys_ctrl_sw->read()) {
			BIT_SET(res, 7);
		}
		if (!m_hpib->cont_r()) {
			BIT_SET(res, 6);
		}
		if (m_upi_irq7) {
			BIT_SET(res, 2);
		}
		BIT_SET(res, 0);
	} else {
		BIT_SET(res, 7);
		if (m_hpib_irq) {
			BIT_SET(res, 6);
		}
		if (m_hpib_dma_en) {
			BIT_SET(res, 0);
		}
	}

	return res;
}

void hp9826_36_state::hpib_w(offs_t offset, uint8_t data)
{
	if (BIT(offset, 3)) {
		m_hpib->write(offset & 7, data);
	} else {
		m_hpib_dma_en = BIT(data, 0);
	}
}

uint16_t hp9826_36_state::id_prom_r(offs_t offset)
{
	uint16_t res;

	res = m_idprom[offset & 0xff];
	res |= (m_sw1->read() << 8);

	return res;
}

static INPUT_PORTS_START(hp9826_36)
	PORT_START("sw1")
	PORT_CONFNAME(0x80, 0x80, "SW1-8")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x80, DEF_STR(Off))
	PORT_CONFNAME(0x40, 0x40, "SW1-7")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x40, DEF_STR(Off))
	PORT_CONFNAME(0x20, 0x20, "SW1-6")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x20, DEF_STR(Off))
	PORT_CONFNAME(0x10, 0x00, "SW1-5")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x10, DEF_STR(Off))
	PORT_CONFNAME(0x08, 0x00, "SW1-4")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x08, DEF_STR(Off))
	PORT_CONFNAME(0x04, 0x00, "SW1-3")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x04, DEF_STR(Off))
	PORT_CONFNAME(0x02, 0x00, "SW1-2")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x02, DEF_STR(Off))
	PORT_CONFNAME(0x01, 0x00, "SW1-1")
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFSETTING(0x01, DEF_STR(Off))

	PORT_START("sys_ctrl_sw")
	PORT_CONFNAME(1, 1, "HPIB Sys controller")
	PORT_CONFSETTING(1, DEF_STR(On))
	PORT_CONFSETTING(0, DEF_STR(Off))
INPUT_PORTS_END

// +--------------+
// | hp9826_state |
// +--------------+
class hp9826_state : public hp9826_36_state
{
public:
	hp9826_state(const machine_config &mconfig, device_type type, const char *tag)
		: hp9826_36_state(mconfig, type, tag)
	{
	}

	void hp9826(machine_config &mconfig);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	static inline constexpr unsigned TEXT_VRAM_SIZE = 2048;
	static inline constexpr unsigned GRAPHIC_VRAM_SIZE = 16384;

	virtual void cpu_mem_map(address_map &map) override ATTR_COLD;
	uint16_t text_r(offs_t offset, uint16_t mem_mask);
	void text_w(offs_t offset, uint16_t data, uint16_t mem_mask);
	uint16_t graphic_r(offs_t offset, uint16_t mem_mask);
	void graphic_w(offs_t offset, uint16_t data, uint16_t mem_mask);

	MC6845_UPDATE_ROW(crtc_update_row);

	uint8_t m_text_vram[TEXT_VRAM_SIZE];
	uint8_t m_graphic_vram[GRAPHIC_VRAM_SIZE];
};

void hp9826_state::hp9826(machine_config &mconfig)
{
	hp9826_36(mconfig, DOT_CLOCK_9826.value(), 8);

	SCREEN(mconfig, m_screen, SCREEN_TYPE_RASTER, rgb_t::white());
	m_screen->set_raw(DOT_CLOCK_9826, 520, 0, 400, 321, 0, 300);
	m_screen->set_screen_update(m_crtc, FUNC(mc6845_device::screen_update));

	PALETTE(mconfig, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	m_crtc->set_update_row_callback(FUNC(hp9826_state::crtc_update_row));
}

void hp9826_state::machine_start()
{
	hp9826_36_state::machine_start();
	save_item(NAME(m_text_vram));
	save_item(NAME(m_graphic_vram));
}

void hp9826_state::cpu_mem_map(address_map &map)
{
	hp9826_36_state::cpu_mem_map(map);

	// Range           DTACK Device
	// ==============================
	// 41'0000-41'ffff *     Text video memory & 6845 (mirror of 51'0000)
	// 43'0000-43'ffff *     Graphic video memory (Mirror of 53'0000)
	map(0x410000, 0x41ffff).mirror(0x100000).rw(FUNC(hp9826_state::text_r), FUNC(hp9826_state::text_w));
	map(0x430000, 0x43ffff).mirror(0x100000).rw(FUNC(hp9826_state::graphic_r), FUNC(hp9826_state::graphic_w));
}

uint16_t hp9826_state::text_r(offs_t offset, uint16_t mem_mask)
{
	if (BIT(offset, 12)) {
		if (ACCESSING_BITS_0_7) {
			return m_text_vram[offset & (TEXT_VRAM_SIZE - 1)];
		} else {
			return m_cpu->berr_r();
		}
	} else if (BIT(offset, 0)) {
		return m_crtc->register_r();
	} else {
		LOG("Reading from non-existing register of 6845!\n");
		return 0;
	}
}

void hp9826_state::text_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (BIT(offset, 12)) {
		m_hsync_en = !BIT(offset, 14);
		if (ACCESSING_BITS_0_7) {
			m_text_vram[offset & (TEXT_VRAM_SIZE - 1)] = uint8_t(data);
		} else {
			m_cpu->berr_w(data);
		}
	} else if (ACCESSING_BITS_0_7) {
		if (BIT(offset, 0)) {
			m_crtc->register_w(uint8_t(data));
		} else {
			m_crtc->address_w(uint8_t(data));
		}
	} else {
		LOG("Access to 6845 on top byte!\n");
	}
}

uint16_t hp9826_state::graphic_r(offs_t offset, uint16_t mem_mask)
{
	m_graphic_en = !BIT(offset, 14);

	if (ACCESSING_BITS_0_7) {
		return m_graphic_vram[offset & (GRAPHIC_VRAM_SIZE - 1)];
	} else {
		return m_cpu->berr_r();
	}
}

void hp9826_state::graphic_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	m_graphic_en = !BIT(offset, 14);

	if (ACCESSING_BITS_0_7) {
		m_graphic_vram[offset & (GRAPHIC_VRAM_SIZE - 1)] = uint8_t(data);
	} else {
		return m_cpu->berr_w(0);
	}
}

MC6845_UPDATE_ROW(hp9826_state::crtc_update_row)
{
	const pen_t *pen = m_palette->pens();
	// Text enable multiplexer
	//
	// | MA13 | VSEL | RA=11 | text_en    |
	// |------+------+-------+------------|
	// |    0 |    0 |     0 | 0          |
	// |    0 |    0 |     1 | 0          |
	// |    0 |    1 |     0 | m_hsync_en |
	// |    0 |    1 |     1 | 0          |
	// |    1 |    0 |     0 | m_hsync_en |
	// |    1 |    0 |     1 | m_hsync_en |
	// |    1 |    1 |     0 | m_hsync_en |
	// |    1 |    1 |     1 | 0          |
	bool vmux[] = { false, false, m_hsync_en, false, m_hsync_en, m_hsync_en, m_hsync_en, false };
	unsigned vmux_idx = (ra & 0xb) == 0xb ? 1 : 0;
	uint16_t graph_addr = y * x_count;
	for (int i = 0; i < x_count; i++) {
		uint16_t text_addr = ma + i;
		bool vsel = (text_addr & 0x1800) == 0x1800;
		bool text_en = vmux[vmux_idx + (vsel ? 2 : 0) + (BIT(text_addr, 13) ? 4 : 0)];
		uint8_t text_pixels = text_en ? m_chargen[unsigned(m_text_vram[text_addr & (TEXT_VRAM_SIZE - 1)]) << 4 | ra] : 0;
		bool inv = text_en ? (vsel ^ (cursor_x == i)) : false;
		if (inv) {
			text_pixels = ~text_pixels;
		}
		uint8_t graph_pixels = m_graphic_en ? m_graphic_vram[(graph_addr + i) & (GRAPHIC_VRAM_SIZE - 1)] : 0;
		for (unsigned col = 0; col < 8; col++) {
			if (vsel ? BIT(graph_pixels, 7 - col) : BIT(graph_pixels, 7 - col) ^ BIT(text_pixels, 7 - col)) {
				bitmap.pix(y, i * 8 + col) = pen[2];
			} else if (vsel && BIT(text_pixels, 7 - col)) {
				bitmap.pix(y, i * 8 + col) = pen[1];
			} else {
				bitmap.pix(y, i * 8 + col) = pen[0];
			}
		}
	}
}

ROM_START(hp9826a)
	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("9826-chargen.bin", 0, 0x1000, CRC(dc070d09) SHA1(f11d45fb11b95cb02429ef54dd8a802b77436296))

	ROM_REGION(0x100, "idprom", 0)
	ROM_LOAD("prom9826.bin", 0, 0x100, CRC(358a1a8b) SHA1(2d87a445efdcd45a8606838c6d66a10cc68af85a))

	ROM_REGION(0x10000, "cpu", 0)
	ROM_DEFAULT_BIOS("bios40")
	ROM_SYSTEM_BIOS(0, "bios40",  "BIOS v4.0")
	ROMX_LOAD("rom40.bin", 0x0000, 0x10000, CRC(36005480) SHA1(645a077ffd95e4c31f05cd8bbd6e4554b12813f1), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "bios30",  "BIOS v3.0")
	ROMX_LOAD("rom30.bin", 0x0000, 0x10000, CRC(05c07e75) SHA1(3066a65e6137482041f9a77d09ee2289fe0974aa), ROM_BIOS(1))
ROM_END

// +--------------+
// | hp9836_state |
// +--------------+
class hp9836_state : public hp9826_36_state
{
public:
	hp9836_state(const machine_config &mconfig, device_type type, const char *tag)
		: hp9826_36_state(mconfig, type, tag)
		, m_frame_rate_sw(*this, "frame_rate_sw")
		, m_60hz(false)
	{
	}

	void hp9836(machine_config &mconfig);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	static inline constexpr unsigned TEXT_VRAM_SIZE = 2048;
	static inline constexpr unsigned GRAPHIC_VRAM_SIZE = 16384;

	virtual void cpu_mem_map(address_map &map) override ATTR_COLD;
	uint16_t text_r(offs_t offset, uint16_t mem_mask);
	void text_w(offs_t offset, uint16_t data, uint16_t mem_mask);
	uint16_t graphic_r(offs_t offset, uint16_t mem_mask);
	void graphic_w(offs_t offset, uint16_t data, uint16_t mem_mask);

	MC6845_UPDATE_ROW(crtc_update_row);

	required_ioport m_frame_rate_sw;

	uint16_t m_text_vram[TEXT_VRAM_SIZE];
	uint16_t m_graphic_vram[GRAPHIC_VRAM_SIZE];
	bool m_60hz;
};

void hp9836_state::hp9836(machine_config &mconfig)
{
	// Screen is arranged in this way.
	//
	// ↑  +----------------------------------------+ ↑
	// 15 | Graphic only                           |
	// ↓  +----------------------------------------+ 390
	// ↑  | Graphic & text                         | lines
	// 375| Text: 25 rows, 15 scan lines each      |
	// ↓  +----------------------------------------+ ↓
	//    ← 512 graphic pixels                     →
	//    ← 16G →← 720 text pixels (80 x 9) →← 16G →
	//    ← 48H →← 1440 half pixels         →← 48H →
	//    ← 1536 half pixels                       →
	//
	// 1 graphic pixel = 1.5 text pixels
	// 2 half pixels = 1 text pixel
	// 3 half pixels = 1 graphic pixel
	//
	// Character cell: 15 lines x 9 columns (18 half pixels)
	//
	// MAME only knows about half pixels in the horizontal direction

	// In the horizontal direction everything is doubled to account for half pixels
	hp9826_36(mconfig, DOT_CLOCK_9836 * 2, 18);

	SCREEN(mconfig, m_screen, SCREEN_TYPE_RASTER, rgb_t::white());
	// Parameters for 50 Hz frame rate
	m_screen->set_raw(DOT_CLOCK_9836 * 2, 2070, 0, 1536, 498, 0, 390);
	m_screen->set_screen_update(m_crtc, FUNC(mc6845_device::screen_update));

	PALETTE(mconfig, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	m_crtc->set_update_row_callback(FUNC(hp9836_state::crtc_update_row));
	// Expand visible area to include 16 graphic pixels (48 text half-pixels) on either side and
	// 15 graphic lines at the top
	m_crtc->set_visarea_adjust(0, 96, 0, 15);

	FLOPPY_CONNECTOR(mconfig, m_drive1, floppies, "525dd", floppy_image_device::default_mfm_floppy_formats, true);
}

void hp9836_state::machine_start()
{
	hp9826_36_state::machine_start();
	save_item(NAME(m_text_vram));
	save_item(NAME(m_graphic_vram));
	save_item(NAME(m_60hz));
}

void hp9836_state::cpu_mem_map(address_map &map)
{
	hp9826_36_state::cpu_mem_map(map);

	// Range           DTACK Device
	// ==============================
	// 51'0000-51'ffff *     Text video memory & 6845
	// 53'0000-53'ffff *     Graphic video memory
	map(0x510000, 0x51ffff).rw(FUNC(hp9836_state::text_r), FUNC(hp9836_state::text_w));
	map(0x530000, 0x53ffff).rw(FUNC(hp9836_state::graphic_r), FUNC(hp9836_state::graphic_w));
}

uint16_t hp9836_state::text_r(offs_t offset, uint16_t mem_mask)
{
	if (BIT(offset, 12)) {
		if (BIT(offset, 13)) {
			bool new_60hz = BIT(offset, 1);
			if (new_60hz != m_60hz) {
				m_60hz = new_60hz;
				LOG("60/50 Hz FF set to %d\n", m_60hz);
			}
		}
		uint16_t res = m_text_vram[offset & (TEXT_VRAM_SIZE - 1)];
		BIT_CLR(res, 15);
		if (m_frame_rate_sw->read()) {
			BIT_SET(res, 15);
		}
		return res;
	} else if (BIT(offset, 0)) {
		return m_crtc->register_r();
	} else {
		LOG("Reading from non-existing register of 6845!\n");
		return 0;
	}
}

void hp9836_state::text_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (BIT(offset, 12)) {
		m_hsync_en = !BIT(offset, 14);
		if (BIT(offset, 13)) {
			bool new_60hz = BIT(offset, 1);
			if (new_60hz != m_60hz) {
				m_60hz = new_60hz;
				LOG("60/50 Hz FF set to %d\n", m_60hz);
			}
		}
		COMBINE_DATA(&m_text_vram[offset & (TEXT_VRAM_SIZE - 1)]);
	} else if (ACCESSING_BITS_0_7) {
		if (BIT(offset, 0)) {
			m_crtc->register_w(uint8_t(data));
		} else {
			m_crtc->address_w(uint8_t(data));
		}
	} else {
		LOG("Access to 6845 on top byte!\n");
	}
}

uint16_t hp9836_state::graphic_r(offs_t offset, uint16_t mem_mask)
{
	m_graphic_en = !BIT(offset, 14);

	return m_graphic_vram[offset & (GRAPHIC_VRAM_SIZE - 1)];
}

void hp9836_state::graphic_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	m_graphic_en = !BIT(offset, 14);

	COMBINE_DATA(&m_graphic_vram[offset & (GRAPHIC_VRAM_SIZE - 1)]);
}

MC6845_UPDATE_ROW(hp9836_state::crtc_update_row)
{
	// 1536 (64 * 24) half pixels
	uint64_t gdots[24] = { 0 };
	uint64_t tdots[24] = { 0 };
	uint64_t invert[24] = { 0 };
	uint64_t half_b[24] = { 0 };

	if (y < 15 * 25) {
		// Text
		// 48 is the leftmost half-pixel of text part
		uint64_t tdots_mask = 1ULL << (63 - 48);
		unsigned tdots_pos = 0;
		// Text enable multiplexer
		//
		// | MA13 | MA12 | MA11 | text_en    |
		// |------+------+------+------------|
		// |    0 |    0 |    0 | 0          |
		// |    0 |    0 |    1 | 0          |
		// |    0 |    1 |    0 | 0          |
		// |    0 |    1 |    1 | m_hsync_en |
		// |    1 |    0 |    0 | m_hsync_en |
		// |    1 |    0 |    1 | 0          |
		// |    1 |    1 |    0 | m_hsync_en |
		// |    1 |    1 |    1 | m_hsync_en |
		bool ten[] = { false, false, false, m_hsync_en, m_hsync_en, false, m_hsync_en, m_hsync_en };

		for (int i = 0; i < x_count; i++) {
			uint16_t t_addr = ma + i;
			bool text_en = ten[(t_addr >> 11) & 7];
			uint16_t char_attr = m_text_vram[t_addr & (TEXT_VRAM_SIZE - 1)];
			// | Bit(s) | Meaning                    |
			// |--------+----------------------------|
			// | 15..12 | N/U                        |
			// |     11 | Half brightness            |
			// |     10 | Underline                  |
			// |      9 | Blink                      |
			// |      8 | Inverted                   |
			// |   7..0 | Character code             |
			bool hb = BIT(char_attr, 11);
			bool inv = false;
			uint16_t text_pixels = 0;
			bool half_shift = false;
			if (text_en) {
				inv = BIT(char_attr, 8);
				bool blink = BIT(char_attr, 9) && BIT(m_screen->frame_number(), 5);
				if (!blink) {
					uint8_t cg = m_chargen[ra | ((char_attr & 0xff) << 4)];
					text_pixels = (cg & 0x7f) << 1;
					if (cursor_x == i || (BIT(char_attr, 10) && ra == 14)) {
						text_pixels = ~text_pixels;
					}
					half_shift = BIT(cg, 7);
				}
			}
			for (unsigned j = 0; j < 18; j++) {
				unsigned bit_pos = half_shift ? (9 - (j + 1) / 2) : (8 - j / 2);
				if (text_pixels & (1 << bit_pos)) {
					tdots[tdots_pos] |= tdots_mask;
				}
				if (hb) {
					half_b[tdots_pos] |= tdots_mask;
				}
				if (inv) {
					invert[tdots_pos] |= tdots_mask;
				}
				tdots_mask >>= 1;
				if (!tdots_mask) {
					tdots_pos++;
					tdots_mask = 1ULL << 63;
				}
			}
		}
	}

	int real_y = (y + 15) % 390;
	if (m_graphic_en) {
		uint64_t gdots_mask = 1ULL << 63;
		unsigned gdots_pos = 0;
		// 32 16-bit words -> 512 pixels
		for (unsigned i = 0; i < 32; i++) {
			uint16_t w = m_graphic_vram[real_y * 32 + i];
			for (uint16_t mask = 0x8000; mask; mask >>= 1) {
				bool dot = (w & mask) != 0;
				// 1 graphic pixel = 3 half pixels
				if (dot) {
					gdots[gdots_pos] |= gdots_mask;
				}
				gdots_mask >>= 1;
				if (!gdots_mask) {
					gdots_pos++;
					gdots_mask = 1ULL << 63;
				}
				if (dot) {
					gdots[gdots_pos] |= gdots_mask;
				}
				gdots_mask >>= 1;
				if (!gdots_mask) {
					gdots_pos++;
					gdots_mask = 1ULL << 63;
				}
				if (dot) {
					gdots[gdots_pos] |= gdots_mask;
				}
				gdots_mask >>= 1;
				if (!gdots_mask) {
					gdots_pos++;
					gdots_mask = 1ULL << 63;
				}
			}
		}
	}

	// Combiner truth table
	//
	// | Gdots | Tdots | Inv | h_b | Video | Int | out |
	// |-------+-------+-----+-----+-------+-----+-----|
	// |     0 |     0 |   0 |   0 |     1 |   1 |   0 |
	// |     0 |     0 |   0 |   1 |     1 |   1 |   0 |
	// |     0 |     0 |   1 |   0 |     0 |   1 |   2 |
	// |     0 |     0 |   1 |   1 |     1 |   0 |   1 |
	// |     0 |     1 |   0 |   0 |     0 |   1 |   2 |
	// |     0 |     1 |   0 |   1 |     1 |   0 |   1 |
	// |     0 |     1 |   1 |   0 |     1 |   1 |   0 |
	// |     0 |     1 |   1 |   1 |     1 |   1 |   0 |
	// |     1 |     0 |   0 |   0 |     0 |   1 |   2 |
	// |     1 |     0 |   0 |   1 |     0 |   1 |   2 |
	// |     1 |     0 |   1 |   0 |     0 |   1 |   2 |
	// |     1 |     0 |   1 |   1 |     0 |   1 |   2 |
	// |     1 |     1 |   0 |   0 |     1 |   1 |   0 |
	// |     1 |     1 |   0 |   1 |     1 |   1 |   0 |
	// |     1 |     1 |   1 |   0 |     0 |   1 |   2 |
	// |     1 |     1 |   1 |   1 |     0 |   1 |   2 |
	const pen_t *pen = m_palette->pens();
	int real_x = 0;
	for (unsigned i = 0; i < 24; i++) {
		uint64_t gd = gdots[i];
		uint64_t not_gd = ~gd;
		uint64_t td = tdots[i];
		uint64_t not_td = ~td;
		uint64_t inv = invert[i];
		uint64_t not_inv = ~inv;
		uint64_t hb = half_b[i];
		uint64_t video = (not_gd & not_td & not_inv) |
			(gd & td & not_inv) |
			(not_gd & hb) |
			(not_gd & td & inv);
		uint64_t half_bright = ~(not_gd & (td ^ inv) & hb);
		for (uint64_t mask = 1ULL << 63; mask; mask >>= 1) {
			if (video & mask) {
				if (half_bright & mask) {
					// Video = 1 & half_bright = 1: OFF
					bitmap.pix(real_y, real_x) = pen[0];
				} else {
					// Video = 1 & half_bright = 0: MID
					bitmap.pix(real_y, real_x) = pen[1];
				}
			} else {
				// Video = 0 & half_bright = x: HI
				bitmap.pix(real_y, real_x) = pen[2];
			}
			real_x++;
		}
	}
}

static INPUT_PORTS_START(hp9836)
	PORT_INCLUDE(hp9826_36)

	PORT_START("frame_rate_sw")
	PORT_CONFNAME(1, 1, "CRT refresh rate")
	PORT_CONFSETTING(1, "50 Hz")
	PORT_CONFSETTING(0, "60 Hz")
INPUT_PORTS_END

ROM_START(hp9836a)
	ROM_REGION(0x1000, "chargen", 0)
	// Content of this ROM is not original, I reconstructed it by adapting 9816 character generator
	// AFAIK no dump is available
	ROM_LOAD("9836-chargen.bin", 0, 0x1000, BAD_DUMP CRC(69754023) SHA1(952dc7fd59e0039fa7c7436fb596ab460241f256))

	ROM_REGION(0x100, "idprom", 0)
	ROM_LOAD("prom9836.bin", 0, 0x100, CRC(52251c32) SHA1(3437c5a78b4c3323ce37340022a4843fcb9f4327))

	ROM_REGION(0x10000, "cpu", 0)
	ROM_DEFAULT_BIOS("bios40")
	ROM_SYSTEM_BIOS(0, "bios40",  "BIOS v4.0")
	ROMX_LOAD("rom40.bin", 0x0000, 0x10000, CRC(36005480) SHA1(645a077ffd95e4c31f05cd8bbd6e4554b12813f1), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "bios30",  "BIOS v3.0")
	ROMX_LOAD("rom30.bin", 0x0000, 0x10000, CRC(05c07e75) SHA1(3066a65e6137482041f9a77d09ee2289fe0974aa), ROM_BIOS(1))
ROM_END

// +---------------+
// | hp9836c_state |
// +---------------+
class hp9836c_state : public hp9826_36_state
{
public:
	hp9836c_state(const machine_config &mconfig, device_type type, const char *tag)
		: hp9826_36_state(mconfig, type, tag)
		, m_frame_rate_sw(*this, "frame_rate_sw")
		, m_clut(*this, "palette", 24 * 2, ENDIANNESS_LITTLE)
		, m_60hz(false)
	{
	}

	void hp9836c(machine_config &mconfig);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	static inline constexpr unsigned TEXT_VRAM_SIZE = 2048;
	static inline constexpr unsigned GRAPHIC_VRAM_SIZE = 131072;

	virtual void cpu_mem_map(address_map &map) override ATTR_COLD;
	uint16_t text_r(offs_t offset, uint16_t mem_mask);
	void text_w(offs_t offset, uint16_t data, uint16_t mem_mask);
	uint16_t graphic_r(offs_t offset, uint16_t mem_mask);
	void graphic_w(offs_t offset, uint16_t data, uint16_t mem_mask);

	MC6845_UPDATE_ROW(crtc_update_row);

	required_ioport m_frame_rate_sw;
	memory_share_creator<uint8_t> m_clut;

	uint16_t m_text_vram[TEXT_VRAM_SIZE];
	uint16_t m_graphic_vram[GRAPHIC_VRAM_SIZE];
	bool m_60hz;
};

void hp9836c_state::hp9836c(machine_config &mconfig)
{
	// Screen layout is the same as that of 9836A (see hp9836_state::hp9836)
	// In the horizontal direction everything is doubled to account for half pixels
	hp9826_36(mconfig, DOT_CLOCK_9836 * 2, 18);

	SCREEN(mconfig, m_screen, SCREEN_TYPE_RASTER);
	// Parameters for 50 Hz frame rate
	m_screen->set_raw(DOT_CLOCK_9836 * 2, 2070, 0, 1536, 498, 0, 390);
	m_screen->set_screen_update(m_crtc, FUNC(mc6845_device::screen_update));

	PALETTE(mconfig, m_palette).set_format(palette_device::xRGB_444, 24);
	// Entries 0..7 are fixed for text colors
	// Entries 8..23 reflect the state of CLUT memory

	m_crtc->set_update_row_callback(FUNC(hp9836c_state::crtc_update_row));
	// Expand visible area to include 16 graphic pixels (48 text half-pixels) on either side and
	// 15 graphic lines at the top
	m_crtc->set_visarea_adjust(0, 96, 0, 15);

	FLOPPY_CONNECTOR(mconfig, m_drive1, floppies, "525dd", floppy_image_device::default_mfm_floppy_formats, true);
}

void hp9836c_state::machine_start()
{
	hp9826_36_state::machine_start();
	save_item(NAME(m_text_vram));
	save_item(NAME(m_graphic_vram));
	save_item(NAME(m_60hz));

	// Fixed text colors
	// Index:b2 = blue, b1 = not green, b0 = red
	m_palette->set_pen_color(0, 0, 255, 0);     // 000: Green
	m_palette->set_pen_color(1, 255, 255, 0);   // 001: Yellow
	m_palette->set_pen_color(2, 0, 0, 0);       // 010: Black
	m_palette->set_pen_color(3, 255, 0, 0);     // 011: Red
	m_palette->set_pen_color(4, 0, 255, 255);   // 100: Cyan
	m_palette->set_pen_color(5, 255, 255, 255); // 101: White
	m_palette->set_pen_color(6, 0, 0, 255);     // 110: Blue
	m_palette->set_pen_color(7, 255, 0, 255);   // 111: Magenta
}

void hp9836c_state::cpu_mem_map(address_map &map)
{
	hp9826_36_state::cpu_mem_map(map);

	// Range           DTACK Device
	// ==============================
	// 51'0000-51'ffff *     Text video memory, CLUT & 6845
	// 52'0000-55'ffff *     Graphic video memory
	map(0x510000, 0x51ffff).rw(FUNC(hp9836c_state::text_r), FUNC(hp9836c_state::text_w));
	map(0x520000, 0x55ffff).rw(FUNC(hp9836c_state::graphic_r), FUNC(hp9836c_state::graphic_w));
}

uint16_t hp9836c_state::text_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t res = 0;

	if (BIT(offset, 12)) {
		bool texttop = (offset & 0x7c00) == 0x7c00;
		if (texttop) {
			if (BIT(offset, 9)) {
				if (BIT(offset, 0)) {
					// ID register
					res = 0x0a30;
					if (m_frame_rate_sw->read()) {
						BIT_SET(res, 3);
					}
				} else {
					// Vblank
					if (m_screen->vpos() >= 390) {
						BIT_SET(res, 0);
					}
				}
			} else  {
				// Read CLUT
				res = m_palette->read16((offset & 0xf) + 8) | 0xf000;
			}
		} else {
			// Text VRAM
			res = m_text_vram[offset & (TEXT_VRAM_SIZE - 1)];
			BIT_CLR(res, 15);
		}
	} else if (BIT(offset, 0)) {
		res = m_crtc->register_r();
	} else {
		LOG("Reading from non-existing register of 6845!\n");
	}

	return res;
}

void hp9836c_state::text_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (BIT(offset, 12)) {
		bool texttop = (offset & 0x7c00) == 0x7c00;
		if (texttop) {
			if (BIT(offset, 9)) {
				if (BIT(offset, 0)) {
					LOG("Writing %04x to 51fffe/f\n", data);
				} else {
					// Schematics and Pascal doc disagree over the address of this register
					// Pascal doc makes more sense though
					m_graphic_en = BIT(data, 0);
				}
			} else {
				if (mem_mask != 0xffff) {
					LOG("Writing to CLUT a single byte!\n");
				}
				m_palette->write16((offset & 0xf) + 8, ~data & 0xfff);
			}
		} else {
			if (BIT(offset, 13)) {
				bool new_60hz = BIT(offset, 1);
				if (new_60hz != m_60hz) {
					m_60hz = new_60hz;
					LOG("60/50 Hz FF set to %d\n", m_60hz);
				}
			}
			m_hsync_en = !BIT(offset, 14);
			COMBINE_DATA(&m_text_vram[offset & (TEXT_VRAM_SIZE - 1)]);
		}
	} else if (ACCESSING_BITS_0_7) {
		if (BIT(offset, 0)) {
			m_crtc->register_w(uint8_t(data));
		} else {
			m_crtc->address_w(uint8_t(data));
		}
	} else {
		LOG("Access to 6845 on top byte!\n");
	}
}

uint16_t hp9836c_state::graphic_r(offs_t offset, uint16_t mem_mask)
{
	return m_graphic_vram[offset & (GRAPHIC_VRAM_SIZE - 1)];
}

void hp9836c_state::graphic_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	offset &= (GRAPHIC_VRAM_SIZE - 1);
	COMBINE_DATA(&m_graphic_vram[offset]);
	// Only bits 11..8 and 3..0 are actually present
	m_graphic_vram[offset] &= 0x0f0f;
}

// Text enable multiplexer
//
// | MA13 | MA12 | MA11 | text_en    |
// |------+------+------+------------|
// |    0 |    0 |    0 | 0          |
// |    0 |    0 |    1 | 0          |
// |    0 |    1 |    0 | 0          |
// |    0 |    1 |    1 | 1          |
// |    1 |    0 |    0 | 1          |
// |    1 |    0 |    1 | 0          |
// |    1 |    1 |    0 | 1          |
// |    1 |    1 |    1 | 1          |
static const bool text_enable[] = { false, false, false, true, true, false, true, true };

MC6845_UPDATE_ROW(hp9836c_state::crtc_update_row)
{
	const pen_t *pen = m_palette->pens();
	int real_y = (y + 15) % 390;
	if (m_graphic_en) {
		// Graphic
		const uint16_t *ptr = &m_graphic_vram[real_y * 256];
		for (int real_x = 0; real_x < 1536; ptr++) {
			pen_t pen_pix = pen[8 + ((*ptr >> 8) & 0xf)];
			bitmap.pix(real_y, real_x++) = pen_pix;
			bitmap.pix(real_y, real_x++) = pen_pix;
			bitmap.pix(real_y, real_x++) = pen_pix;
			pen_pix = pen[8 + (*ptr & 0xf)];
			bitmap.pix(real_y, real_x++) = pen_pix;
			bitmap.pix(real_y, real_x++) = pen_pix;
			bitmap.pix(real_y, real_x++) = pen_pix;
		}
	} else {
		bitmap.fill(rgb_t::black(), rectangle(0, 1535, real_y, real_y));
	}
	if (y < 15 * 25) {
		// Text
		for (int i = 0; i < x_count; i++) {
			uint16_t t_addr = ma + i;
			bool text_en = m_hsync_en ? text_enable[(t_addr >> 11) & 7] : false;
			if (text_en) {
				// | Bit(s) | Meaning        |
				// |--------+----------------|
				// |     15 | N/U            |
				// |     14 | Blue           |
				// |     13 | !Green         |
				// |     12 | Red            |
				// |     11 | N/U            |
				// |     10 | Underline      |
				// |      9 | Blink          |
				// |      8 | Inverted       |
				// |   7..0 | Character code |
				uint16_t char_attr = m_text_vram[t_addr & (TEXT_VRAM_SIZE - 1)];
				bool blink = BIT(char_attr, 9) && BIT(m_screen->frame_number(), 5);
				uint16_t text_pixels;
				bool half_shift;
				if (blink) {
					text_pixels = 0;
					half_shift = false;
				} else {
					uint8_t cg = m_chargen[ra | ((char_attr & 0xff) << 4)];
					text_pixels = (cg & 0x7f) << 1;
					half_shift = BIT(cg, 7);
				}
				bool inv = BIT(char_attr, 8);
				if (inv ^ ((cursor_x == i) || (ra == 14 && BIT(char_attr, 10) && !blink))) {
					text_pixels = ~text_pixels;
				}
				if (text_pixels) {
					pen_t text_pen = pen[(char_attr >> 12) & 7];
					for (unsigned j = 0; j < 18; j++) {
						unsigned bit_pos = half_shift ? (9 - (j + 1) / 2) : (8 - j / 2);
						if (text_pixels & (1 << bit_pos)) {
							bitmap.pix(real_y, 48 + i * 18 + j) = text_pen;
						}
					}
				}
			}
		}
	}
}

ROM_START(hp9836c)
	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("9836-chargen.bin", 0, 0x1000, BAD_DUMP CRC(69754023) SHA1(952dc7fd59e0039fa7c7436fb596ab460241f256))

	ROM_REGION(0x100, "idprom", 0)
	ROM_LOAD("prom9836c.bin", 0, 0x100, CRC(092a1815) SHA1(1c143b0bb6e8715ba6fc7d45070820d7e0dba25e))

	ROM_REGION(0x10000, "cpu", 0)
	ROM_DEFAULT_BIOS("bios40")
	ROM_SYSTEM_BIOS(0, "bios40",  "BIOS v4.0")
	ROMX_LOAD("rom40.bin", 0x0000, 0x10000, CRC(36005480) SHA1(645a077ffd95e4c31f05cd8bbd6e4554b12813f1), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "bios30",  "BIOS v3.0")
	ROMX_LOAD("rom30.bin", 0x0000, 0x10000, CRC(05c07e75) SHA1(3066a65e6137482041f9a77d09ee2289fe0974aa), ROM_BIOS(1))
ROM_END

} // anonymous namespace

//   YEAR  NAME     PARENT  COMPAT  MACHINE INPUT     CLASS        INIT        COMPANY            FULLNAME    FLAGS
COMP(1982, hp9816a, 0,      0,      hp9816, hp9816,   hp9816_state,empty_init, "Hewlett-Packard", "HP 9816A", 0)
COMP(1981, hp9826a, 0,      0,      hp9826, hp9826_36,hp9826_state,empty_init, "Hewlett-Packard", "HP 9826A", 0)
COMP(1981, hp9836a, 0,      0,      hp9836, hp9836,   hp9836_state,empty_init, "Hewlett-Packard", "HP 9836A", 0)
COMP(1983, hp9836c, 0,      0,      hp9836c,hp9836,   hp9836c_state,empty_init,"Hewlett-Packard", "HP 9836C", 0)



hp9k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Gabriele D'Antona, F. Ulivi

/***************************************************************************

HP 9816
1982
MESS driver by friol (dantonag (at) gmail.com)
System reference mainly from O.De Smet
Driver started on 10/01/2012

===

Memory map:

000000-00ffff - system rom
428000-428fff - keyboard 8042 host status / data ports (8042 undumped)
510000-51ffff - videoram
530000-53ffff - graphic memory
5f0000-5f3fff - system PROM
xxxxxx-ffffff - system RAM

===

- 0x408: startup code, 68000 registers test
- 0x1002: bootrom test
- 0x1d18: PROM test
- 0x103C: prints "bootrom failed"
- 0x1150: other boot test
- 0x1164: keyboard test
- 0x1202: post-kbd test
- 0x12c6: "loading memory" test

===

TODO: boot tests fail

****************************************************************************/

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "video/mc6845.h"
//#include "machine/ins8250.h"
#include "emupal.h"
#include "screen.h"


namespace {

#define HP9816_CHDIMX 8
#define HP9816_CHDIMY 16
#define HP9816_ROWX 80
#define HP9816_ROWY 25

//

static uint8_t prom16a[256] = {
	0x00,0x00,      // checksum
	0x00,           // 256 bytes idprom
		'2', '0', '1', '0', 'A', '0', '0', '0', '0', '0', '0',      // serial in ascii DDDDCSSSSSS date code, country, serial number
		'9', '8', '1', '6', 'A', ' ', ' ',                          // product number
	0xff,           // 8 bits processor board config
	0x01,           // keyboard 98203B
	0x02,           // CRT alpha see crtid for monitor
	0x03,           // HP-IB
	0x04,           // Graphics
	0xff,           // end
	0xff,
	0xff,
	0xff,
	0xff,
	0xff,
	0xff,
	0xff,
	0xff,
	0xff,
	0xff,
	0xff,
	0xff,0xfe,0x00,0x00,            // bottom minimum address for ram size
	0xff,0xff,                      // 16 required IO cards here not used
	0xff,0xff,
	0xff,0xff,
	0xff,0xff,
	0xff,0xff,
	0xff,0xff,
	0xff,0xff,
	0xff,0xff,
	0xff,0xff,
	0xff,0xff,
	0xff,0xff,
	0xff,0xff,
	0xff,0xff,
	0xff,0xff,
	0xff,0xff,
	0xff,0xff,
	0xff,0xff,0xff,0xff,            // boot msus to try before Boot list scan
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,   // Boot file name
	0x00,0x00,0x00,0x00,            // delay in millisec before boot scan
	0x00,                           // owner byte, HP format
	0x00,                           // id prom revision byte : 0x00
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,                                 // rest reserved at 0xFF
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
};

//

class hp9k_state : public driver_device
{
public:
	hp9k_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_6845(*this, "mc6845"),
		m_gfxdecode(*this, "gfxdecode")
	{
		kbdBit=0;
		crtc_curreg=0;
		crtc_addrStartHi=0;
		crtc_addrStartLow=0;
		calc_prom_crc(prom16a);
	}

	void hp9k(machine_config &config);

	void init_hp9k();

private:

	int crtc_curreg;
	int crtc_addrStartHi;
	int crtc_addrStartLow;

	void calc_prom_crc(uint8_t* prom);
	void putChar(uint8_t thec,int x,int y,bitmap_ind16 &bitmap);

	uint8_t kbdBit;

	required_device<cpu_device> m_maincpu;
	required_device<mc6845_device> m_6845;

	uint8_t m_videoram[0x4000];
	uint8_t m_screenram[0x800];

	uint16_t hp9k_videoram_r(offs_t offset, uint16_t mem_mask = ~0);
	void hp9k_videoram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

	uint16_t hp9k_prom_r(offs_t offset, uint16_t mem_mask = ~0);
	void hp9k_prom_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

	uint16_t keyboard_r(offs_t offset, uint16_t mem_mask = ~0);
	void keyboard_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

	uint16_t leds_r(offs_t offset, uint16_t mem_mask = ~0);
	void leds_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

	[[maybe_unused]] void kbd_put(uint8_t data);

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	required_device<gfxdecode_device> m_gfxdecode;
	void hp9k_mem(address_map &map) ATTR_COLD;
};

//

void hp9k_state::calc_prom_crc(uint8_t* prom)
{
	int chksum;
	int i;

	chksum = 0;
	for (i=0; i < 256; i+=2)
	{
		chksum += ((prom[i] << 8) | prom[i+1]);
		if (chksum&0x10000) chksum++;
		chksum&=0xffff;
	}

	chksum=(chksum+1)&0xffff;

	if (chksum!=0)
	{
		chksum=(0x10000-chksum);
		prom[0]=(uint8_t)(chksum>>8);
		prom[1]=(uint8_t)(chksum&0xff);
	}
}

uint16_t hp9k_state::keyboard_r(offs_t offset, uint16_t mem_mask)
{
	//printf("keyboard read at [%x] mask [%x]\n",offset,mem_mask);
	return 0x31;
}

void hp9k_state::keyboard_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	//printf("keyboard write of [%x] at [%x]\n",data,offset);
}

uint16_t hp9k_state::leds_r(offs_t offset, uint16_t mem_mask)
{
	//printf("warning: leds read at [%x] mm [%x]\n",offset,mem_mask);
	return 0;
}

void hp9k_state::leds_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	//printf("warning: leds write of [%x] at [%x] mm [%x]\n",data,offset,mem_mask);
}

uint16_t hp9k_state::hp9k_prom_r(offs_t offset, uint16_t mem_mask)
{
	//if (mem_mask!=0xff) printf("read of PROM at [%x] mem_mask [%x]\n",offset,mem_mask);
	int k=prom16a[offset&0xff];
	if (mem_mask==0xff00) return (k<<8);
	else return k;
	//return 0;
}

void hp9k_state::hp9k_prom_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	//printf("Error: write to prom\n");
}

uint16_t hp9k_state::hp9k_videoram_r(offs_t offset, uint16_t mem_mask)
{
	offset&=0x3fff;

	//printf("videoram read at [%x] mem_mask [%x]\n",offset,mem_mask);

	if (offset==0x0001)
	{
		//printf("m6845 read at [%x] mem_mask [%x]\n",offset,mem_mask);
		return m_6845->register_r();
	}
	else
	{
		if (mem_mask==0xff00)
		{
			return m_videoram[offset]<<8;
		}
		else
		{
			return m_videoram[offset];
		}
	}
}

void hp9k_state::hp9k_videoram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	offset&=0x3fff;

	if (offset==0x0000)
	{
		//printf("6845 address write [%x] at [%x] mask [%x]\n",data,offset,mem_mask);
		data&=0x1f;
		m_6845->address_w(data);
		crtc_curreg=data;
	}
	else if (offset==0x0001)
	{
		//printf("6845 register write [%x] at [%x] mask [%x]\n",data,offset,mem_mask);
		m_6845->register_w(data);
		if (crtc_curreg==0x0c) crtc_addrStartHi=data;
		if (crtc_curreg==0x0d) crtc_addrStartLow=data;
	}
	else
	{
		//printf("videoram write [%x] at [%x]\n",data,offset);

		if (mem_mask==0xff00)
			{
				m_screenram[offset&0x7ff]=data>>8;
				m_videoram[offset&0x3fff]=data>>8;

				//uint8_t *rom = machine().region("bootrom")->base();
				//rom[0x90B]=0x00;
			}
			else
			{
				m_screenram[offset&0x7ff]=data;
				m_videoram[offset&0x3fff]=data;
			}
	}
}

void hp9k_state::hp9k_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x000909).rom().region("bootrom", 0);
	map(0x00090a, 0x00090d).rw(FUNC(hp9k_state::leds_r), FUNC(hp9k_state::leds_w));
	map(0x00090e, 0x00ffff).rom().region("bootrom", 0x90e);
	map(0x010000, 0x427fff).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0x428000, 0x428fff).rw(FUNC(hp9k_state::keyboard_r), FUNC(hp9k_state::keyboard_w));
	map(0x429000, 0x50ffff).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0x510000, 0x51ffff).rw(FUNC(hp9k_state::hp9k_videoram_r), FUNC(hp9k_state::hp9k_videoram_w));
	map(0x520000, 0x52ffff).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0x530000, 0x53ffff).ram(); // graphic memory
	map(0x540000, 0x5effff).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0x5f0000, 0x5f3fff).rw(FUNC(hp9k_state::hp9k_prom_r), FUNC(hp9k_state::hp9k_prom_w));
	//map(0x5f0000, 0x5f3fff).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0x5f4000, 0xfbffff).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0xFC0000, 0xffffff).ram(); // system ram
}


/* Input ports */
static INPUT_PORTS_START( hp9k )
INPUT_PORTS_END


void hp9k_state::init_hp9k()
{
}

static const gfx_layout hp9k_charlayout =
{
	HP9816_CHDIMX, HP9816_CHDIMY,
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ STEP8(0,1) },
	/* y offsets */
	{ STEP16(0,8) },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_hp9k )
	GFXDECODE_ENTRY( "bootrom", 0x2000, hp9k_charlayout, 0, 1 )
GFXDECODE_END

void hp9k_state::putChar(uint8_t thec,int x,int y,bitmap_ind16 &bitmap)
{
	uint8_t const *const pchar=m_gfxdecode->gfx(0)->get_data(thec);

	for (int py=0;py<HP9816_CHDIMY;py++)
	{
		for (int px=0;px<HP9816_CHDIMX;px++)
		{
			bitmap.pix((y*(HP9816_CHDIMY))+py,(x*(HP9816_CHDIMX))+px) = pchar[px+(py*8)];
		}
	}
}

uint32_t hp9k_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	//uint8_t* pvram=&m_screenram[1];

	int startAddr=((crtc_addrStartLow&0xff)|((crtc_addrStartHi<<8)))&0x3fff;
	int chStart=startAddr&0x1fff;

	for (int r=0;r<HP9816_ROWY;r++)
	{
		for (int c=0;c<HP9816_ROWX;c++)
		{
			//uint8_t thec=pvram[((c+(r*80))+160+47)&0x7ff];
			//uint8_t thec=m_videoram[((c+(r*80))+startAddr)];
			uint8_t thec=m_screenram[chStart&0x7ff];
			putChar(thec,c,r,bitmap);
			chStart++;
		}
	}

	//putChar(0x44,0,0,pscr);
	return 0;
}

void hp9k_state::kbd_put(uint8_t data)
{
	kbdBit=data;
}


void hp9k_state::hp9k(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, XTAL(8'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &hp9k_state::hp9k_mem);

	/* video hardware */

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(0));
	screen.set_size(HP9816_ROWX*HP9816_CHDIMX, HP9816_ROWY*HP9816_CHDIMY);
	screen.set_visarea(0, (HP9816_ROWX*HP9816_CHDIMX)-1, 0, (HP9816_ROWY*HP9816_CHDIMY)-1);
	screen.set_screen_update(FUNC(hp9k_state::screen_update));
	screen.set_palette("palette");

	GFXDECODE(config, m_gfxdecode, "palette", gfx_hp9k);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	MC6845(config, m_6845, XTAL(16'000'000) / 16);
	m_6845->set_screen("screen");
	m_6845->set_show_border_area(false);
	m_6845->set_char_width(8);
}

/* ROM definition */
ROM_START( hp9816 )
	ROM_REGION16_BE(0x10000, "bootrom", 0)

	ROM_DEFAULT_BIOS("bios40")
	ROM_SYSTEM_BIOS(0, "bios40",  "BIOS v4.0")
	ROMX_LOAD( "rom40.bin", 0x0000, 0x10000, CRC(36005480) SHA1(645a077ffd95e4c31f05cd8bbd6e4554b12813f1), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "bios30",  "BIOS v3.0")
	ROMX_LOAD( "rom30.bin", 0x0000, 0x10000, CRC(05c07e75) SHA1(3066a65e6137482041f9a77d09ee2289fe0974aa), ROM_BIOS(1) )

ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT       COMPANY            FULLNAME   FLAGS */
COMP( 1982, hp9816, 0,      0,      hp9k,    hp9k,  hp9k_state, init_hp9k, "Hewlett Packard", "HP 9816", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



hp9k_3xx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

  hp9k3xx.c: preliminary driver for HP9000 300 Series (aka HP9000/3xx)
  By R. Belmont

  TODO: Add DIO/DIO-II slot capability and modularize the video cards

  Currently supporting:

  310:
      MC68010 CPU @ 10 MHz
      HP custom MMU

  320:
      MC68020 CPU @ 16.67 MHz
      HP custom MMU
      MC68881 FPU

  330:
      MC68020 CPU @ 16.67 MHz
      MC68851 MMU
      MC68881 FPU

  340:
      MC68030 CPU @ 16.67 MHz w/built-in MMU
      MC68881 FPU

  360:
      MC68030 CPU @ 25 MHz w/built-in MMU
      MC68882 FPU

  370:
      MC68030 CPU @ 33 MHz w/built-in MMU
      MC68881 FPU

  380:
    MC68040 CPU @ 25 MHz w/built-in MMU and FPU

  382:
    MC68040 CPU @ 25? MHz w/built-in MMU and FPU
    Built-in VGA compatible video

  All models have an MC6840 PIT on IRQ6 clocked at 250 kHz.

  TODO:
    BBCADDR   0x420000
    RTC_DATA: 0x420001
    RTC_CMD:  0x420003
    HIL:      0x428000
    HPIB:     0x470000
    KBDNMIST: 0x478005
    DMA:      0x500000
    FRAMEBUF: 0x560000

    6840:     0x5F8001/3/5/7/9, IRQ 6

****************************************************************************/

#include "emu.h"
#include "logmacro.h"
#include "cpu/m68000/m68010.h"
#include "cpu/m68000/m68020.h"
#include "cpu/m68000/m68030.h"
#include "cpu/m68000/m68040.h"
#include "machine/6840ptm.h"
#include "bus/hp_dio/hp_dio.h"

#include "screen.h"
#include "softlist_dev.h"
#include "hp9k_3xx.lh"


namespace {

#define MAINCPU_TAG "maincpu"
#define PTM6840_TAG "ptm"

class hp9k3xx_state : public driver_device
{
public:
	hp9k3xx_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, MAINCPU_TAG),
		m_diag_led(*this, "led_diag_%u", 0U)
	{ }

	void hp9k310(machine_config &config);
	void hp9k320(machine_config &config);
	void hp9k330(machine_config &config);
	void hp9k332(machine_config &config);
	void hp9k340(machine_config &config);
	void hp9k360(machine_config &config);
	void hp9k370(machine_config &config);
	void hp9k380(machine_config &config);
	void hp9k382(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void driver_start() override;

private:
	void hp9k300(machine_config &config);
	required_device<m68000_musashi_device> m_maincpu;

	output_finder<8> m_diag_led;

	void set_bus_error(uint32_t address, bool write, uint16_t mem_mask);

	uint16_t buserror16_r(offs_t offset, uint16_t mem_mask = ~0);
	void buserror16_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint32_t buserror_r(offs_t offset, uint32_t mem_mask = ~0);
	void buserror_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);

	void led_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

	void hp9k310_map(address_map &map) ATTR_COLD;
	void hp9k320_map(address_map &map) ATTR_COLD;
	void hp9k330_map(address_map &map) ATTR_COLD;
	void hp9k332_map(address_map &map) ATTR_COLD;
	void hp9k360_map(address_map &map) ATTR_COLD;
	void hp9k370_map(address_map &map) ATTR_COLD;
	void hp9k380_map(address_map &map) ATTR_COLD;
	void hp9k382_map(address_map &map) ATTR_COLD;
	void hp9k3xx_common(address_map &map) ATTR_COLD;

	void add_dio16_bus(machine_config &mconfig);
	void add_dio32_bus(machine_config &mconfig);

	void dio_irq1_w(int state) { m_maincpu->set_input_line(M68K_IRQ_1, state); }
	void dio_irq2_w(int state) { m_maincpu->set_input_line(M68K_IRQ_2, state); }
	void dio_irq3_w(int state) { m_maincpu->set_input_line(M68K_IRQ_3, state); }
	void dio_irq4_w(int state) { m_maincpu->set_input_line(M68K_IRQ_4, state); }
	void dio_irq5_w(int state) { m_maincpu->set_input_line(M68K_IRQ_5, state); }
	void dio_irq6_w(int state) { m_maincpu->set_input_line(M68K_IRQ_6, state); }
	void dio_irq7_w(int state) { m_maincpu->set_input_line(M68K_IRQ_7, state); }

	bool m_bus_error = false;
	emu_timer *m_bus_error_timer = nullptr;
	TIMER_CALLBACK_MEMBER(bus_error_timeout);
};

// shared mappings for all 9000/3xx systems
void hp9k3xx_state::hp9k3xx_common(address_map &map)
{
	map(0x00000000, 0xffffffff).rw(FUNC(hp9k3xx_state::buserror_r), FUNC(hp9k3xx_state::buserror_w));
	map(0x00000000, 0x0001ffff).rom().region("maincpu", 0).w(FUNC(hp9k3xx_state::led_w));  // writes to 1fffc are the LED
	map(0x005f4000, 0x005f400f).ram(); // somehow coprocessor related - bootrom crashes if not present
	map(0x005f8000, 0x005f800f).rw(PTM6840_TAG, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write)).umask32(0x00ff00ff);

}

// 9000/310 - has onboard video that the graphics card used in other 3xxes conflicts with
void hp9k3xx_state::hp9k310_map(address_map &map)
{
	map(0x000000, 0xffffff).rw(FUNC(hp9k3xx_state::buserror16_r), FUNC(hp9k3xx_state::buserror16_w));
	map(0x000000, 0x01ffff).rom().region("maincpu", 0).w(FUNC(hp9k3xx_state::led_w));  // writes to 1fffc are the LED
	map(0x5f4000, 0x5f400f).ram(); // somehow coprocessor related - bootrom crashes if not present
	map(0x5f8000, 0x5f800f).rw(PTM6840_TAG, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write)).umask16(0x00ff);
	map(0x800000, 0xffffff).ram();
}

// 9000/320
void hp9k3xx_state::hp9k320_map(address_map &map)
{
	hp9k3xx_common(map);
	// main memory
	map(0xfff00000, 0xffffffff).ram();
}

// 9000/330 and 9000/340
void hp9k3xx_state::hp9k330_map(address_map &map)
{
	hp9k3xx_common(map);
	// main memory
	map(0xffc00000, 0xffffffff).ram();
}

// 9000/332, with built-in medium-res video
void hp9k3xx_state::hp9k332_map(address_map &map)
{
	hp9k3xx_common(map);
	// main memory
	map(0xffc00000, 0xffffffff).ram();
}

// 9000/360 - 16 MB RAM to run HP/UX
void hp9k3xx_state::hp9k360_map(address_map &map)
{
	hp9k3xx_common(map);
	// main memory
	map(0xff000000, 0xffffffff).ram();
}

// 9000/370 - with 48 MB RAM (max. configuration)
void hp9k3xx_state::hp9k370_map(address_map &map)
{
	hp9k3xx_common(map);
	// main memory
	map(0xfd000000, 0xffffffff).ram();
}

// 9000/380 - '040
void hp9k3xx_state::hp9k380_map(address_map &map)
{
	hp9k3xx_common(map);
	// main memory
	map(0xff800000, 0xffffffff).ram();
}

// 9000/382 - onboard VGA compatible video (where?)
void hp9k3xx_state::hp9k382_map(address_map &map)
{
	hp9k3xx_common(map);
	// main memory
	map(0xffc00000, 0xffffffff).ram();
}

/* Input ports */
static INPUT_PORTS_START( hp9k330 )
INPUT_PORTS_END

void hp9k3xx_state::driver_start()
{
	m_diag_led.resolve();
}

void hp9k3xx_state::machine_start()
{
	m_bus_error_timer = timer_alloc(FUNC(hp9k3xx_state::bus_error_timeout), this);
	m_bus_error = false;
	save_item(NAME(m_bus_error));
}

TIMER_CALLBACK_MEMBER(hp9k3xx_state::bus_error_timeout)
{
	m_bus_error = false;
}

void hp9k3xx_state::led_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (!(mem_mask & 0xff))
	   return;

	LOG("LED: %02x\n", data & 0xff);

	m_diag_led[0] = BIT(data, 0);
	m_diag_led[1] = BIT(data, 1);
	m_diag_led[2] = BIT(data, 2);
	m_diag_led[3] = BIT(data, 3);
	m_diag_led[4] = BIT(data, 4);
	m_diag_led[5] = BIT(data, 5);
	m_diag_led[6] = BIT(data, 6);
	m_diag_led[7] = BIT(data, 7);
}

void hp9k3xx_state::add_dio16_bus(machine_config &config)
{
	bus::hp_dio::dio16_device &dio16(DIO16(config, "diobus", 0));
	dio16.set_program_space(m_maincpu, AS_PROGRAM);
	m_maincpu->reset_cb().set(dio16, FUNC(bus::hp_dio::dio16_device::reset_in));

	dio16.irq1_out_cb().set(FUNC(hp9k3xx_state::dio_irq1_w));
	dio16.irq2_out_cb().set(FUNC(hp9k3xx_state::dio_irq2_w));
	dio16.irq3_out_cb().set(FUNC(hp9k3xx_state::dio_irq3_w));
	dio16.irq4_out_cb().set(FUNC(hp9k3xx_state::dio_irq4_w));
	dio16.irq5_out_cb().set(FUNC(hp9k3xx_state::dio_irq5_w));
	dio16.irq6_out_cb().set(FUNC(hp9k3xx_state::dio_irq6_w));
	dio16.irq7_out_cb().set(FUNC(hp9k3xx_state::dio_irq7_w));
}

void hp9k3xx_state::add_dio32_bus(machine_config &config)
{
	bus::hp_dio::dio32_device &dio32(DIO32(config, "diobus", 0));
	dio32.set_program_space(m_maincpu, AS_PROGRAM);

	dio32.irq1_out_cb().set(FUNC(hp9k3xx_state::dio_irq1_w));
	dio32.irq2_out_cb().set(FUNC(hp9k3xx_state::dio_irq2_w));
	dio32.irq3_out_cb().set(FUNC(hp9k3xx_state::dio_irq3_w));
	dio32.irq4_out_cb().set(FUNC(hp9k3xx_state::dio_irq4_w));
	dio32.irq5_out_cb().set(FUNC(hp9k3xx_state::dio_irq5_w));
	dio32.irq6_out_cb().set(FUNC(hp9k3xx_state::dio_irq6_w));
	dio32.irq7_out_cb().set(FUNC(hp9k3xx_state::dio_irq7_w));
}

void hp9k3xx_state::set_bus_error(uint32_t address, bool rw, uint16_t mem_mask)
{
	if (m_maincpu->m68851_buserror(address))
		return;

	if (m_bus_error)
		return;

	m_bus_error = true;
	m_maincpu->set_buserror_details(address, rw, m_maincpu->get_fc());
	m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
	m_bus_error_timer->adjust(m_maincpu->cycles_to_attotime(16)); // let rmw cycles complete
}

uint16_t hp9k3xx_state::buserror16_r(offs_t offset, uint16_t mem_mask)
{
	if (!machine().side_effects_disabled())
		set_bus_error((offset << 1) & 0xFFFFFF, true, mem_mask);
	return 0xffff;
}

void hp9k3xx_state::buserror16_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (!machine().side_effects_disabled())
		set_bus_error((offset << 1) & 0xFFFFFF, false, mem_mask);
}

uint32_t hp9k3xx_state::buserror_r(offs_t offset, uint32_t mem_mask)
{
	if (!machine().side_effects_disabled())
		set_bus_error(offset << 2, false, mem_mask);
	return 0xffffffff;
}

void hp9k3xx_state::buserror_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	if (!machine().side_effects_disabled())
		set_bus_error(offset << 2, false, mem_mask);
}

void hp9k3xx_state::hp9k300(machine_config &config)
{
	ptm6840_device &ptm(PTM6840(config, PTM6840_TAG, 250000)); // from oscillator module next to the 6840
	ptm.set_external_clocks(250000.0f, 0.0f, 250000.0f);
	ptm.o3_callback().set(PTM6840_TAG, FUNC(ptm6840_device::set_c2));
	ptm.irq_callback().set_inputline("maincpu", M68K_IRQ_6);

	SOFTWARE_LIST(config, "flop_list").set_original("hp9k3xx_flop");
	SOFTWARE_LIST(config, "cdrom_list").set_original("hp9k3xx_cdrom");
	SOFTWARE_LIST(config, "hdd_list").set_original("hp9k3xx_hdd");
	config.set_default_layout(layout_hp9k_3xx);
}

void hp9k3xx_state::hp9k310(machine_config &config)
{
	hp9k300(config);

	M68010(config, m_maincpu, 10000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &hp9k3xx_state::hp9k310_map);

	add_dio16_bus(config);

	DIO16_SLOT(config, "sl0", 0, "diobus", dio16_cards, "human_interface", true);
	DIO16_SLOT(config, "sl1", 0, "diobus", dio16_cards, "98544", false);
	DIO16_SLOT(config, "sl2", 0, "diobus", dio16_cards, "98603b", false);
	DIO32_SLOT(config, "sl3", 0, "diobus", dio32_cards, "98643", false);
	DIO16_SLOT(config, "sl4", 0, "diobus", dio16_cards, "98644", false);
	DIO16_SLOT(config, "sl5", 0, "diobus", dio16_cards, nullptr, false);
}

void hp9k3xx_state::hp9k320(machine_config &config)
{
	M68020FPU(config, m_maincpu, 16670000);
	m_maincpu->set_addrmap(AS_PROGRAM, &hp9k3xx_state::hp9k320_map);

	hp9k300(config);
	add_dio32_bus(config);

	DIO32_SLOT(config, "sl0", 0, "diobus", dio32_cards, "human_interface", true);
	DIO32_SLOT(config, "sl1", 0, "diobus", dio32_cards, "98543", false);
	DIO32_SLOT(config, "sl2", 0, "diobus", dio32_cards, "98603b", false);
	DIO32_SLOT(config, "sl3", 0, "diobus", dio32_cards, "98644", false);
	DIO32_SLOT(config, "sl4", 0, "diobus", dio32_cards, "98620", false);
	DIO32_SLOT(config, "sl5", 0, "diobus", dio32_cards, "98265a", false);
	DIO32_SLOT(config, "sl7", 0, "diobus", dio32_cards, nullptr, false);
}

void hp9k3xx_state::hp9k330(machine_config &config)
{
	M68020PMMU(config, m_maincpu, 16670000);
	m_maincpu->set_addrmap(AS_PROGRAM, &hp9k3xx_state::hp9k330_map);

	hp9k300(config);
	add_dio32_bus(config);

	DIO32_SLOT(config, "sl0", 0, "diobus", dio16_cards, "human_interface", true);
	DIO32_SLOT(config, "sl1", 0, "diobus", dio16_cards, "98544", false);
	DIO32_SLOT(config, "sl2", 0, "diobus", dio16_cards, "98603b", false);
	DIO32_SLOT(config, "sl3", 0, "diobus", dio16_cards, "98644", false);
	DIO32_SLOT(config, "sl4", 0, "diobus", dio16_cards, nullptr, false);
}

void hp9k3xx_state::hp9k332(machine_config &config)
{
	M68020PMMU(config, m_maincpu, 16670000);
	m_maincpu->set_addrmap(AS_PROGRAM, &hp9k3xx_state::hp9k332_map);

	hp9k300(config);
	add_dio16_bus(config);

	DIO16_SLOT(config, "sl0", 0, "diobus", dio16_cards, "human_interface", true);
	DIO16_SLOT(config, "sl1", 0, "diobus", dio16_cards, "98603b", false);
	DIO16_SLOT(config, "sl2", 0, "diobus", dio16_cards, "98644", false);
	DIO16_SLOT(config, "sl3", 0, "diobus", dio16_cards, "98543", false);
	DIO16_SLOT(config, "sl4", 0, "diobus", dio16_cards, nullptr, false);
}

void hp9k3xx_state::hp9k340(machine_config &config)
{
	hp9k320(config);

	M68030(config.replace(), m_maincpu, 16670000);
	m_maincpu->set_addrmap(AS_PROGRAM, &hp9k3xx_state::hp9k330_map);
}

void hp9k3xx_state::hp9k360(machine_config &config)
{
	hp9k300(config);

	M68030(config, m_maincpu, 25000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &hp9k3xx_state::hp9k360_map);

	add_dio32_bus(config);

	DIO32_SLOT(config, "sl0", 0, "diobus", dio32_cards, "human_interface", true);
	DIO32_SLOT(config, "sl1", 0, "diobus", dio32_cards, "98550", false);
	DIO32_SLOT(config, "sl2", 0, "diobus", dio32_cards, "98644", false);
	DIO32_SLOT(config, "sl3", 0, "diobus", dio32_cards, "98620", false);
	DIO32_SLOT(config, "sl4", 0, "diobus", dio32_cards, "98265a", false);
	DIO32_SLOT(config, "sl5", 0, "diobus", dio32_cards, nullptr, false);
}


void hp9k3xx_state::hp9k370(machine_config &config)
{
	hp9k360(config);

	M68030(config.replace(), m_maincpu, 33000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &hp9k3xx_state::hp9k370_map);
}

void hp9k3xx_state::hp9k380(machine_config &config)
{
	hp9k360(config);

	M68040(config.replace(), m_maincpu, 25000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &hp9k3xx_state::hp9k380_map);
}

void hp9k3xx_state::hp9k382(machine_config &config)
{
	hp9k360(config);

	M68040(config.replace(), m_maincpu, 25000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &hp9k3xx_state::hp9k382_map);
}

ROM_START( hp9k310 )
	ROM_REGION( 0x20000, MAINCPU_TAG, 0 )
	ROM_LOAD16_BYTE( "1818-3771.bin", 0x000001, 0x008000, CRC(b9e4e3ad) SHA1(ed6f1fad94a15d95362701dbe124b52877fc3ec4) )
	ROM_LOAD16_BYTE( "1818-3772.bin", 0x000000, 0x008000, CRC(a3665919) SHA1(ec1bc7e5b7990a1b09af947a06401e8ed3cb0516) )

	ROM_REGION( 0x4000, "graphics", ROMREGION_ERASEFF | ROMREGION_BE )
	ROM_LOAD16_BYTE( "98544_1818-1999.bin", 0x000000, 0x002000, CRC(8c7d6480) SHA1(d2bcfd39452c38bc652df39f84c7041cfdf6bd51) )
ROM_END

ROM_START( hp9k320 )
	ROM_REGION( 0x20000, MAINCPU_TAG, 0 )
	ROM_LOAD16_BYTE( "5061-6538.bin", 0x000001, 0x004000, CRC(d6aafeb1) SHA1(88c6b0b2f504303cbbac0c496c26b85458ac5d63) )
	ROM_LOAD16_BYTE( "5061-6539.bin", 0x000000, 0x004000, CRC(a7ff104c) SHA1(c640fe68314654716bd41b04c6a7f4e560036c7e) )
	ROM_LOAD16_BYTE( "5061-6540.bin", 0x008001, 0x004000, CRC(4f6796d6) SHA1(fd254897ac1afb8628f40ea93213f60a082c8d36) )
	ROM_LOAD16_BYTE( "5061-6541.bin", 0x008000, 0x004000, CRC(39d32998) SHA1(6de1bda75187b0878c03c074942b807cf2924f0e) )
ROM_END

ROM_START( hp9k330 )
	ROM_REGION( 0x20000, MAINCPU_TAG, 0 )
	ROM_LOAD16_BYTE( "1818-4416.bin", 0x000000, 0x010000, CRC(cd71e85e) SHA1(3e83a80682f733417fdc3720410e45a2cfdcf869) )
	ROM_LOAD16_BYTE( "1818-4417.bin", 0x000001, 0x010000, CRC(374d49db) SHA1(a12cbf6c151e2f421da4571000b5dffa3ef403b3) )
ROM_END

ROM_START( hp9k332 )
	ROM_REGION( 0x20000, MAINCPU_TAG, 0 )
	ROM_LOAD16_BYTE( "1818-4796.bin", 0x000000, 0x010000, CRC(8a7642da) SHA1(7ba12adcea85916d18b021255391bec806c32e94) )
	ROM_LOAD16_BYTE( "1818-4797.bin", 0x000001, 0x010000, CRC(98129eb1) SHA1(f3451a854060f1be1bee9f17c5c198b4b1cd61ac) )
ROM_END

ROM_START( hp9k340 )
	ROM_REGION( 0x20000, MAINCPU_TAG, 0 )
	ROM_LOAD16_BYTE( "1818-4416.bin", 0x000000, 0x010000, CRC(cd71e85e) SHA1(3e83a80682f733417fdc3720410e45a2cfdcf869) )
	ROM_LOAD16_BYTE( "1818-4417.bin", 0x000001, 0x010000, CRC(374d49db) SHA1(a12cbf6c151e2f421da4571000b5dffa3ef403b3) )

ROM_END

ROM_START( hp9k360 )
	ROM_REGION( 0x20000, MAINCPU_TAG, 0 )
	ROM_LOAD16_BYTE( "1818-4796.bin", 0x000000, 0x010000, CRC(8a7642da) SHA1(7ba12adcea85916d18b021255391bec806c32e94) )
	ROM_LOAD16_BYTE( "1818-4797.bin", 0x000001, 0x010000, CRC(98129eb1) SHA1(f3451a854060f1be1bee9f17c5c198b4b1cd61ac) )
ROM_END


ROM_START( hp9k370 )
	ROM_REGION( 0x20000, MAINCPU_TAG, 0 )
	ROM_LOAD16_BYTE( "1818-4416.bin", 0x000000, 0x010000, CRC(cd71e85e) SHA1(3e83a80682f733417fdc3720410e45a2cfdcf869) )
	ROM_LOAD16_BYTE( "1818-4417.bin", 0x000001, 0x010000, CRC(374d49db) SHA1(a12cbf6c151e2f421da4571000b5dffa3ef403b3) )
ROM_END

ROM_START( hp9k380 )
	ROM_REGION( 0x20000, MAINCPU_TAG, 0 )
	ROM_LOAD16_WORD_SWAP( "1818-5062_98754_9000-380_27c210.bin", 0x000000, 0x020000, CRC(500a0797) SHA1(4c0a3929e45202a2689e353657e5c4b58ff9a1fd) )
ROM_END

ROM_START( hp9k382 )
	ROM_REGION( 0x20000, MAINCPU_TAG, 0 )
	ROM_LOAD16_WORD_SWAP( "1818-5468_27c1024.bin", 0x000000, 0x020000, CRC(d1d9ef13) SHA1(6bbb17b9adad402fbc516dc2f3143e9c38ceef8e) )

	ROM_REGION( 0x2000, "unknown", ROMREGION_ERASEFF | ROMREGION_BE | ROMREGION_32BIT )
	ROM_LOAD( "1818-5282_8ce61e951207_28c64.bin", 0x000000, 0x002000, CRC(740442f3) SHA1(ab65bd4eec1024afb97fc2dd3bd3f017e90f49ae) )
ROM_END

} // Anonymous namespace


/*    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY            FULLNAME      FLAGS */
COMP( 1985, hp9k310, 0,       0,      hp9k310, hp9k330, hp9k3xx_state, empty_init, "Hewlett-Packard", "HP9000/310", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)
COMP( 1985, hp9k320, 0,       0,      hp9k320, hp9k330, hp9k3xx_state, empty_init, "Hewlett-Packard", "HP9000/320", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)
COMP( 1987, hp9k330, 0,       0,      hp9k330, hp9k330, hp9k3xx_state, empty_init, "Hewlett-Packard", "HP9000/330", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)
COMP( 1987, hp9k332, 0,       0,      hp9k332, hp9k330, hp9k3xx_state, empty_init, "Hewlett-Packard", "HP9000/332", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)
COMP( 1989, hp9k340, hp9k330, 0,      hp9k340, hp9k330, hp9k3xx_state, empty_init, "Hewlett-Packard", "HP9000/340", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)
COMP( 1988, hp9k360, hp9k330, 0,      hp9k360, hp9k330, hp9k3xx_state, empty_init, "Hewlett-Packard", "HP9000/360", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)
COMP( 1988, hp9k370, hp9k330, 0,      hp9k370, hp9k330, hp9k3xx_state, empty_init, "Hewlett-Packard", "HP9000/370", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)
COMP( 1991, hp9k380, 0,       0,      hp9k380, hp9k330, hp9k3xx_state, empty_init, "Hewlett-Packard", "HP9000/380", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)
COMP( 1991, hp9k382, 0,       0,      hp9k382, hp9k330, hp9k3xx_state, empty_init, "Hewlett-Packard", "HP9000/382", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)



hprot1.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders: Felipe Sanches
/***************************************************************************

  HENRY Prot I/II - brazilian document timestamp printers
  http://www.dataponto.com.br/protocoladores-prot1.html

  Driver by Felipe Sanches
  Technical info at https://www.garoa.net.br/wiki/HENRY

  Changelog:

   2014 JUN 13 [Felipe Sanches]:
   * new derivative "CARD I PCB rev.08A"
   * new derivative "CARD II PCB rev.6"
   * fixed LCD rendering (now both lines are displayed properly)
   * inverted logic of the inputs so that now we can navigate the menu

   2014 JAN 03 [Felipe Sanches]:
   * Initial driver skeleton
   * Address lines bitswaping
   * LCD works. We can see boot messages.
   * Inputs are not working correctly

TO-DO list:

======= hprotr8a ===========
Loops during boot displaying an error message related to low power supply voltage.
We need to emulate the ADM695AN chip (Microprocessor Supervisory Circuits) in order to properly boot the device.

======= hprot2r6 ===========
There are unhandled memory writes at 0xE000 and 0xA000

LCD commands are sent, but nothing shows up on screen.
The commands sent are supposed to display the message:

"*Pouca  Energia*" (LCD cmds range: 80-8F)  (cmds logged but not displayed on screen)
"*  no Sistema  *" (LCD cmds range: C0-CF)  (cmds logged but not displayed on screen)

which means something like "too little energy for the system to operate".
We need to emulate the ADM695AN chip (Microprocessor Supervisory Circuits) in order to properly boot the device.

Infinite loop is reached at address 0x7699
======= hprot1 ===========

  There seems to be an eeprom or a realtime clock placed at U2 (DIP8):
  pin1 -> 8031 pin 14 (T0: Timer 0 external input)
  pin2 -> crystal at X2 (labeled 32.768)
  pin3 -> ?
  pin4 -> GND
  pin5 -> ?
  pin6 -> 8031 pin 5 (Port 1 bit 4)
  pin7 -> 8031 pin 4 (Port 1 bit 3)
  pin8 -> VCC

****************************************************************************/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "sound/spkrdev.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#define LOG_IO_PORTS (1U << 1)

#define VERBOSE (0)
#include "logmacro.h"


namespace {

class hprot1_state : public driver_device
{
public:
	hprot1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "hd44780")
	{ }

	void hprotr8a(machine_config &config);
	void hprot2r6(machine_config &config);
	void hprot1(machine_config &config);

	void init_hprot1();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void henry_p1_w(uint8_t data);
	void henry_p3_w(uint8_t data);
	void hprot1_palette(palette_device &palette) const;
	HD44780_PIXEL_UPDATE(hprot1_pixel_update);
	void i80c31_io(address_map &map) ATTR_COLD;
	void i80c31_prg(address_map &map) ATTR_COLD;

	required_device<i80c31_device> m_maincpu;
	required_device<hd44780_device> m_lcdc;
};

void hprot1_state::i80c31_prg(address_map &map)
{
	map(0x0000, 0xffff).rom();
}

void hprot1_state::init_hprot1()
{
	uint8_t *ROM = memregion("maincpu")->base();
	uint8_t bitswapped_ROM[0x10000];

	for (int i = 0x0000; i < 0x10000; i++)
		bitswapped_ROM[i] = ROM[i];

	for (int i = 0x0000; i < 0x10000; i++)
		ROM[bitswap<16>(i, 15, 14, 13, 12, 11, 10, 9, 8, 3, 2, 1, 0, 4, 5, 6, 7)] = bitswapped_ROM[i];
}

//A4 = display RS
//A5 = display R/W
//(A11 == 0) && (A10 == 0) => display CS
//(A14 == 1) && (A15 == 1) => enable signal for the mux that selects peripherals
//11?? 00?? ??00 ???? write data
//11?? 00?? ??01 ???? write command
//11?? 00?? ??10 ???? read data
//11?? 00?? ??11 ???? read command
//mirror=0x33cf

//write: 0xc400 => U12 (74373 - possibly for the dot matrix printhead ?)
//write: 0xc800 => U11 (74373 - possibly for the dot matrix printhead ?)
//read:  0xc020 => display
//write: 0xc000 => display
//write: 0xc010 => display

//P1.4 => WhatchDog Input (after timeout resets CPU)

void hprot1_state::i80c31_io(address_map &map)
{
	map(0x0000, 0x7fff).ram();
/*TODO: verify the mirror mask value for the HD44780 device */
	map(0xc000, 0xc000).mirror(0x13cf).w(m_lcdc, FUNC(hd44780_device::control_w));
	map(0xc010, 0xc010).mirror(0x13cf).w(m_lcdc, FUNC(hd44780_device::data_w));
	map(0xc020, 0xc020).mirror(0x13cf).r(m_lcdc, FUNC(hd44780_device::control_r));
	map(0xc030, 0xc030).mirror(0x13cf).r(m_lcdc, FUNC(hd44780_device::data_r));
/*TODO: attach the watchdog/brownout reset device:
    map(0xe000,0xe0??).mirror(?).r("adm965an", FUNC(adm965an_device::data_read)); */
}

static INPUT_PORTS_START( hprot1 )
/* FIXME: I am still unsure whether all these inputs are Active High or Active Low: */
	PORT_START("inputs")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Upper Black Button") PORT_CODE(KEYCODE_A)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Lower Black Button") PORT_CODE(KEYCODE_B)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Blue Button") PORT_CODE(KEYCODE_C)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Paper Detector") PORT_CODE(KEYCODE_D)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("XMIN Endstop") PORT_CODE(KEYCODE_E)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( hprot_jumpers )
/*
There is a set of 3 jumpers that switch the communications circuitry between a
RS232 chip (U8: MAX232AN) and a differential bus transceiver (U7: SN65176BP)

It seems that all three jumpers must select the same configuration:
eighter differential bus or RS232.
And I don't see yet how this could affect the emulation of the device, so, for now, I'll
simply leave this note here but not actually include this details in the driver code.

    PORT_START("jumpers")
    PORT_DIPNAME( 0x01, 0x01, "TX")
    PORT_DIPSETTING(    0x01, "differential bus")
    PORT_DIPSETTING(    0x00, "RS232")
    PORT_DIPNAME( 0x02, 0x02, "RX")
    PORT_DIPSETTING(    0x02, "differential bus")
    PORT_DIPSETTING(    0x00, "RS232")
    PORT_DIPNAME( 0x04, 0x04, "CPU-TX")
    PORT_DIPSETTING(    0x04, "differential bus")
    PORT_DIPSETTING(    0x00, "RS232")
*/
INPUT_PORTS_END

/* TODO: meanings for the jumpers may be different among machines,
 so we may have to have individual declarations for each board. */
static INPUT_PORTS_START( hprot2r6 )
	PORT_INCLUDE(hprot1)
	PORT_INCLUDE(hprot_jumpers)
INPUT_PORTS_END

static INPUT_PORTS_START( hprotr8a )
	PORT_INCLUDE(hprot1)
	PORT_INCLUDE(hprot_jumpers)
INPUT_PORTS_END

void hprot1_state::machine_start()
{
}

void hprot1_state::machine_reset()
{
}

void hprot1_state::henry_p1_w(uint8_t data)
{
	if (data != 0xFF && data != 0xEF)
		LOGMASKED(LOG_IO_PORTS, "Write to P1: %02X\n", data);
}

void hprot1_state::henry_p3_w(uint8_t data)
{
	LOGMASKED(LOG_IO_PORTS, "Write to P3: %02X\n", data);
}

void hprot1_state::hprot1_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}

static const gfx_layout henry_prot_charlayout =
{
	5, 8,                   /* 5 x 8 characters */
	256,                    /* 256 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	{ 3, 4, 5, 6, 7},
	{ 0, 8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8},
	8*8                     /* 8 bytes */
};

static GFXDECODE_START( gfx_hprot1 )
	GFXDECODE_ENTRY( "hd44780:cgrom", 0x0000, henry_prot_charlayout, 0, 1 )
GFXDECODE_END

HD44780_PIXEL_UPDATE(hprot1_state::hprot1_pixel_update)
{
	if ( pos < 16 && line==0 )
	{
		bitmap.pix(y, pos*6 + x) = state;
	}

	if ( pos >= 64 && pos < 80 && line==0 )
	{
		bitmap.pix(y+9,(pos-64)*6 + x) = state;
	}
}

void hprot1_state::hprot1(machine_config &config)
{
	/* basic machine hardware */
	I80C31(config, m_maincpu, XTAL(10'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &hprot1_state::i80c31_prg);
	m_maincpu->set_addrmap(AS_IO, &hprot1_state::i80c31_io);
	m_maincpu->port_in_cb<1>().set_ioport("inputs");
	m_maincpu->port_out_cb<1>().set(FUNC(hprot1_state::henry_p1_w));
	m_maincpu->port_out_cb<3>().set(FUNC(hprot1_state::henry_p3_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("hd44780", FUNC(hd44780_device::screen_update));
	screen.set_size(6*16, 9*2);
	screen.set_visarea(0, 6*16-1, 0, 9*2-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(hprot1_state::hprot1_palette), 2);
	GFXDECODE(config, "gfxdecode", "palette", gfx_hprot1);

	HD44780(config, m_lcdc, 270'000); /* TODO: clock not measured, datasheet typical clock used */
	m_lcdc->set_lcd_size(2, 16);
	m_lcdc->set_pixel_update_cb(FUNC(hprot1_state::hprot1_pixel_update));

	/* TODO: figure out which RTC chip is in use. */

	/* TODO: emulate the ADM695AN chip (watchdog/brownout reset)*/
}

void hprot1_state::hprotr8a(machine_config &config)
{
	hprot1(config);

	I80C31(config.replace(), m_maincpu, 11059200); // value of X1 crystal on the PCB
	m_maincpu->set_addrmap(AS_PROGRAM, &hprot1_state::i80c31_prg);
	m_maincpu->set_addrmap(AS_IO, &hprot1_state::i80c31_io);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	/* TODO: add an RS232 interface (emulate MAX232N chip)
	(the board has GND/VCC/RX/TX pins available in a connector) */

	/* TODO: add an I2C interface (the board has GND/VCC/SDA/SCL pins available in a connector) */
}

void hprot1_state::hprot2r6(machine_config &config)
{
	hprot1(config);

	I80C31(config.replace(), m_maincpu, 11059200); // value of X1 crystal on the PCB
	m_maincpu->set_addrmap(AS_PROGRAM, &hprot1_state::i80c31_prg);
	m_maincpu->set_addrmap(AS_IO, &hprot1_state::i80c31_io);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	/* TODO: add an RS232 interface (emulate MAX232N chip) */
}

ROM_START( hprot1 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "henry_prot1_rev1_v19.bin",  0x00000, 0x10000, CRC(dd7787fd) SHA1(61a37dd406b3440d568bd6da75a9fdc8a0f0e1e3) )
ROM_END

ROM_START( hprotr8a )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "hprot_card1_rev08a.u2",  0x00000, 0x10000, CRC(e827480f) SHA1(bd53e6cce9a0832ca01f1a485ddaab43c0baa136) )
ROM_END

ROM_START( hprot2r6 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "hprot_card2_rev6.u2",  0x00000, 0x10000, CRC(791f2425) SHA1(70af8911a27921cac6d98a5cd07602a7f59c2848) )
ROM_END

} // anonymous namespace


/*    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS         INIT         COMPANY  FULLNAME                       FLAGS */
COMP( 2002, hprot1,   0,      0,      hprot1,   hprot1,   hprot1_state, init_hprot1, "HENRY", "Henry Prot I v19 (REV.1)",    MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND)
/* fw version: "R19"        Release date: February 1st, 2002.   */

COMP( 2006, hprotr8a, hprot1, 0,      hprotr8a, hprotr8a, hprot1_state, init_hprot1, "HENRY", "Henry Prot CARD I (REV.08A)", MACHINE_NOT_WORKING)
/* fw version: "V6.5QI I"   Release date: September 18th, 2006. */

COMP( 2003, hprot2r6, hprot1, 0,      hprot2r6, hprot2r6, hprot1_state, init_hprot1, "HENRY", "Henry Prot CARD II (REV.6)",  MACHINE_NOT_WORKING)
/* fw version: "V5.8CF II"  Release date: June 23rd, 2003.      */



hpz80unk.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

HP Z80-based unknown in a large metal cage

2012-05-25 Skeleton driver [Robbbert]

http://www.classiccmp.org/hp/unknown Z80 computer/

Looks like roms are in 2 banks in range C000-FFFF.
BASIC is included, if we can find out how to access it.

Commands:
Axxxx         Disassemble (. to quit)
DAxxxx,yyyy   Ascii Dump of memory
DBxxxx,yyyy   Binary Dump of memory
DHxxxx,yyyy   Hex Dump of memory
DOxxxx,yyyy   Octal dump of memory
G
H
L
MMxxxx        Modify Memory (. to quit)
Pxx           Binary Display of Port
Pxx,xx        Write to port
RC            ???
RF            ???
RM            ???
RT            ???
UC            Displays 11111111
US            ???
UZ            Displays FFFF
W             Punch papertape
X             choose Q,V,R,P (Q to quit; others ask for ram and prom ranges)
Y nothing
Z nothing

    ToDo:
    - Almost everything; there are a lot of I/O ports used
    - Hook up rom banking

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/ay31015.h"
#include "machine/clock.h"
#include "bus/rs232/rs232.h"


namespace {

class hpz80unk_state : public driver_device
{
public:
	hpz80unk_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_p_rom(*this, "rom")
		, m_uart(*this, "uart%u", 1U)
	{ }

	void hpz80unk(machine_config &config);

private:
	u8 port00_r();
	u8 port02_r();
	u8 port03_r();
	u8 port0d_r();
	u8 portfc_r();

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	u8 m_port02_data = 0U;
	void machine_reset() override ATTR_COLD;
	void machine_start() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_shared_ptr<u8> m_p_rom;
	required_device_array<ay51013_device, 3> m_uart;
};

u8 hpz80unk_state::port00_r()
{
	return (m_uart[0]->dav_r() << 1) | (m_uart[0]->tbmt_r()) | 0xfc;
}

u8 hpz80unk_state::port02_r()
{
	m_port02_data ^= 1;
	return m_port02_data;
}

u8 hpz80unk_state::port03_r()
{
	return (m_uart[1]->dav_r() << 1) | (m_uart[1]->tbmt_r()) | 0xfc;
}

u8 hpz80unk_state::port0d_r()
{
	return (m_uart[2]->dav_r() << 1) | (m_uart[2]->tbmt_r()) | 0xfc;
}

u8 hpz80unk_state::portfc_r()
{
	return 0xfe; // or it halts
}

void hpz80unk_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xbfff).ram();
	map(0xc000, 0xffff).rom().share("rom");
}

void hpz80unk_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x00).r(FUNC(hpz80unk_state::port00_r)); // uart1 status
	map(0x01, 0x01).rw("uart1", FUNC(ay31015_device::receive), FUNC(ay31015_device::transmit)); // uart1 data
	map(0x02, 0x02).r(FUNC(hpz80unk_state::port02_r));
	map(0x03, 0x03).r(FUNC(hpz80unk_state::port03_r)); // uart2 status
	map(0x04, 0x04).rw("uart2", FUNC(ay31015_device::receive), FUNC(ay31015_device::transmit)); // uart2 data
	map(0x0d, 0x0d).r(FUNC(hpz80unk_state::port0d_r)); // uart3 status
	map(0x0e, 0x0e).w("uart3", FUNC(ay31015_device::transmit)); // uart3 data
	map(0x1d, 0x1e); // top of memory is written here, big-endian
	map(0x1f, 0x1f).portr("DSW"); // select which uarts to use
	map(0xfc, 0xfc).r(FUNC(hpz80unk_state::portfc_r));
}

/* Input ports */
static INPUT_PORTS_START( hpz80unk )
	// this is a theoretical switch
	PORT_START("DSW")
	PORT_DIPNAME( 0x03, 0x00, "UART selection")
	PORT_DIPSETTING(    0x00, "In UART1, Out UART1")
	PORT_DIPSETTING(    0x01, "In UART1, Out UART2")
	PORT_DIPSETTING(    0x02, "In UART1, Out UART3")
	PORT_DIPSETTING(    0x03, "In UART2, Out UART1")
INPUT_PORTS_END


void hpz80unk_state::machine_start()
{
	save_item(NAME(m_port02_data));
}

void hpz80unk_state::machine_reset()
{
	u8* user1 = memregion("user1")->base();
	memcpy((u8*)m_p_rom, user1, 0x4000);
	m_maincpu->set_pc(0xc000);

	// no idea if these are hard-coded, or programmable
	for (auto &uart : m_uart)
	{
		uart->write_xr(0);
		uart->write_xr(1);
		uart->write_swe(0);
		uart->write_np(1);
		uart->write_tsb(0);
		uart->write_nb1(1);
		uart->write_nb2(1);
		uart->write_eps(1);
		uart->write_cs(1);
		uart->write_cs(0);
	}

	// this should be rom/ram banking
}


void hpz80unk_state::hpz80unk(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &hpz80unk_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &hpz80unk_state::io_map);

	AY51013(config, m_uart[0]); // COM2502
	m_uart[0]->read_si_callback().set("rs232a", FUNC(rs232_port_device::rxd_r));
	m_uart[0]->write_so_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
	m_uart[0]->set_auto_rdav(true);
	RS232_PORT(config, "rs232a", default_rs232_devices, "terminal");

	AY51013(config, m_uart[1]); // COM2502
	m_uart[1]->read_si_callback().set("rs232b", FUNC(rs232_port_device::rxd_r));
	m_uart[1]->write_so_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
	m_uart[1]->set_auto_rdav(true);
	RS232_PORT(config, "rs232b", default_rs232_devices, nullptr);

	AY51013(config, m_uart[2]); // COM2502
	m_uart[2]->read_si_callback().set("rs232c", FUNC(rs232_port_device::rxd_r));
	m_uart[2]->write_so_callback().set("rs232c", FUNC(rs232_port_device::write_txd));
	m_uart[2]->set_auto_rdav(true);
	RS232_PORT(config, "rs232c", default_rs232_devices, nullptr);

	clock_device &uart_clock(CLOCK(config, "uart_clock", 153600));
	uart_clock.signal_handler().set(m_uart[0], FUNC(ay51013_device::write_tcp));
	uart_clock.signal_handler().append(m_uart[0], FUNC(ay51013_device::write_rcp));
	uart_clock.signal_handler().append(m_uart[1], FUNC(ay51013_device::write_tcp));
	uart_clock.signal_handler().append(m_uart[1], FUNC(ay51013_device::write_rcp));
	uart_clock.signal_handler().append(m_uart[2], FUNC(ay51013_device::write_tcp));
	uart_clock.signal_handler().append(m_uart[2], FUNC(ay51013_device::write_rcp));
}

/* ROM definition */
ROM_START( hpz80unk )
	ROM_REGION( 0x8000, "user1", 0 )
	// 1st bank
	ROM_LOAD( "u1",        0x0000, 0x0800, CRC(080cd04a) SHA1(42004af65d44e3507a4e0f343c5bf385b6377c40) )
	ROM_LOAD( "u3",        0x0800, 0x0800, CRC(694075e1) SHA1(3db62645ade6a7f454b2d505aecc1661284c8ce2) )
	ROM_LOAD( "u5",        0x1000, 0x0800, CRC(5573bd05) SHA1(68c8f02b3fe9d77ecb83df407ca78430e118004a) )
	ROM_LOAD( "u7",        0x1800, 0x0800, CRC(d18a304a) SHA1(69dd0486bb6e4c2a22ab9da863bfb962016a321b) )
	ROM_LOAD( "u9",        0x2000, 0x0800, CRC(f7a8665c) SHA1(e39d0ba4ce2dc773622d411a25f40a6a24b45449) )
	ROM_LOAD( "u11",       0x2800, 0x0800, CRC(6c1ac77a) SHA1(50ca04ff0a11bd1c7d96f4731cef50978266ecca) )
	ROM_LOAD( "u13",       0x3000, 0x0800, CRC(8b166911) SHA1(4301dcd6840d37ccfa5bff998a0d88bebe99dc31) )
	ROM_LOAD( "u15",       0x3800, 0x0800, CRC(c6300499) SHA1(1b62d2a85c8f0b6a817e4be73ee34e0d90515c00) )
	// 2nd bank
	ROM_LOAD( "u2",        0x4000, 0x0800, CRC(080cd04a) SHA1(42004af65d44e3507a4e0f343c5bf385b6377c40) )
	ROM_LOAD( "u4",        0x4800, 0x0800, CRC(66c3745c) SHA1(d79fe764312a222ac64d325bf5f4abc7ca401d0f) )
	ROM_LOAD( "u6",        0x5000, 0x0800, CRC(80761b4c) SHA1(5f6a12fbba533308b9fe7067c67a836be436a6f0) )
	ROM_LOAD( "u8",        0x5800, 0x0800, CRC(64a2be18) SHA1(b11c08fdc9dc126038559462493f458ecdc78532) )
	ROM_LOAD( "u10",       0x6000, 0x0800, CRC(40244d09) SHA1(106f8f978de36df9f3ebbe1e2c959b60e53273a2) )
	ROM_LOAD( "u12",       0x6800, 0x0800, CRC(6eb01765) SHA1(66f9036a9f86cf3a79493330bbc06fb6932ab771) )
	ROM_LOAD( "u14",       0x7000, 0x0800, CRC(3410e682) SHA1(30d94c0c0b6478dab202a603edaccca943008e35) )
	ROM_LOAD( "u16",       0x7800, 0x0800, CRC(c03fdcab) SHA1(1081d787085add489c6e2a1d450e1a5790d18885) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY            FULLNAME                       FLAGS
COMP( 1977, hpz80unk, 0,      0,      hpz80unk, hpz80unk, hpz80unk_state, empty_init, "Hewlett-Packard", "unknown Z80-based mainframe", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



hr84.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

        Iskra široka potrošnja HR-84

        19/06/2023 Skeleton driver.

****************************************************************************/

#include "emu.h"

#include "cpu/m6809/m6809.h"
#include "imagedev/cassette.h"
#include "machine/6821pia.h"
#include "machine/clock.h"
#include "machine/kr2376.h"
#include "machine/mm58174.h"
#include "machine/timer.h"
#include "sound/spkrdev.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"
#include "utf8.h"

namespace {

class hr84_state : public driver_device
{
public:
	hr84_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_pia0(*this, "pia0")
		, m_maincpu(*this, "maincpu")
		, m_crtc(*this, "crtc")
		, m_palette(*this, "palette")
		, m_speaker(*this, "speaker")
		, m_cassette(*this, "cassette")
		, m_p_chargen(*this, "chargen")
		, m_p_videoram(*this, "videoram")
		, m_ay_5_2376(*this, "ay_5_2376")
		, m_io_brk(*this, "BRK")
		, m_modifiers(*this, "MODIFIERS")
	{ }

	void hr84(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(break_handler);

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	u8 pa_r();
	void pb_w(u8 data);

	MC6845_UPDATE_ROW(crtc_update_row);
	TIMER_DEVICE_CALLBACK_MEMBER(cassette_input);

	void hr84_mem(address_map &map) ATTR_COLD;

	required_device<pia6821_device> m_pia0;
	required_device<cpu_device> m_maincpu;
	required_device<mc6845_device> m_crtc;
	required_device<palette_device> m_palette;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cassette;
	required_region_ptr<u8> m_p_chargen;
	required_shared_ptr<u8> m_p_videoram;
	required_device<kr2376_device> m_ay_5_2376;
	required_ioport m_io_brk;
	required_ioport m_modifiers;
};


void hr84_state::hr84_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).ram(); // 8 * 4116-3
	map(0xb7e0, 0xb7e3).rw(m_pia0, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xb800, 0xbbff).ram().share("videoram"); // 2 * MM2114 (1024 * 4 bit)
	map(0xbde0, 0xbde0).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0xbde1, 0xbde1).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0xc000, 0xffff).rom().region("maincpu", 0);
}

INPUT_CHANGED_MEMBER(hr84_state::break_handler)
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, BIT(m_io_brk->read(), 0) ? CLEAR_LINE : ASSERT_LINE);
}

/* Input ports */
static INPUT_PORTS_START( hr84 )
	PORT_START("BRK")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Break") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(hr84_state::break_handler), 0)

	PORT_START("X0")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("X1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("X2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(8)                   PORT_NAME("Del")

	PORT_START("X3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0)          PORT_CHAR('0')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(U'ž') PORT_CHAR(U'Ž')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR(U'š') PORT_CHAR(U'Š')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(U'ć') PORT_CHAR(U'Ć')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)                  PORT_NAME("CR")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DOWN )      PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CHAR(10) PORT_NAME("LF")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

	PORT_START("X4")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)          PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)          PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)          PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)          PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)          PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)          PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z') PORT_CHAR('Z')

	PORT_START("X5")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)          PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)          PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)          PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)          PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)          PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)          PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)          PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)          PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)          PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC))

	PORT_START("X6")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)          PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)          PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)          PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)          PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)          PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q') PORT_CHAR('Q')

	PORT_START("X7")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_INSERT)     PORT_CHAR(U'č') PORT_CHAR(U'Č')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR(U'đ') PORT_CHAR(U'Đ')

	PORT_START("MODIFIERS")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Shift Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_TOGGLE
INPUT_PORTS_END

void hr84_state::machine_reset()
{
	m_ay_5_2376->set_input_pin( kr2376_device::KR2376_DSII, 0);
	m_ay_5_2376->set_input_pin( kr2376_device::KR2376_PII, 0);
	m_pia0->ca1_w(0);
}

// **** Video ****

/* F4 Character Displayer */
static const gfx_layout hr84_charlayout =
{
	8, 10,                  /* 8 x 10 characters */
	128,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_hr84 )
	GFXDECODE_ENTRY( "chargen", 0x0000, hr84_charlayout, 0, 1 )
GFXDECODE_END

MC6845_UPDATE_ROW( hr84_state::crtc_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u32 *p = &bitmap.pix(y);
	for (u8 x = 0; x < x_count; x++)
	{
		u16 mem = (ma + x) & 0x7ff;
		u8 chr = m_p_videoram[mem];
		u8 gfx = m_p_chargen[0x0800 | (chr<<4) | ra] ^ ((x == (cursor_x)) ? 0xff : 0);

		*p++ = palette[BIT(gfx, 7)];
		*p++ = palette[BIT(gfx, 6)];
		*p++ = palette[BIT(gfx, 5)];
		*p++ = palette[BIT(gfx, 4)];
		*p++ = palette[BIT(gfx, 3)];
		*p++ = palette[BIT(gfx, 2)];
		*p++ = palette[BIT(gfx, 1)];
		*p++ = palette[BIT(gfx, 0)];
	}
}

u8 hr84_state::pa_r()
{
	uint8_t data = m_ay_5_2376->data_r() & 0x7f;
	data |= m_ay_5_2376->get_output_pin(kr2376_device::KR2376_SO) << 7;
	return data;
}

TIMER_DEVICE_CALLBACK_MEMBER(hr84_state::cassette_input)
{
	m_pia0->cb1_w(m_cassette->input() >= 0 ? 0x01 : 0x00);
}

void hr84_state::pb_w(u8 data)
{
	m_speaker->level_w(BIT(data,6));
	m_cassette->output(BIT(data,7) ? -1.0 : +1.0);
}

// *** Machine ****

void hr84_state::hr84(machine_config &config)
{
	/* basic machine hardware */
	MC6809(config, m_maincpu, 4_MHz_XTAL); // divided by 4 internally
	m_maincpu->set_addrmap(AS_PROGRAM, &hr84_state::hr84_mem);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(14.7456_MHz_XTAL, 472, 0, 320, 312, 0, 240);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	PALETTE(config, m_palette, palette_device::MONOCHROME);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_hr84);

	/* devices */
	MC6845(config, m_crtc, 14.7456_MHz_XTAL /16); // MC6845P
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(hr84_state::crtc_update_row));

	// AY-5-2376
	KR2376_ST(config, m_ay_5_2376, 50000 * 2);
	m_ay_5_2376->x<0>().set_ioport("X0");
	m_ay_5_2376->x<1>().set_ioport("X1");
	m_ay_5_2376->x<2>().set_ioport("X2");
	m_ay_5_2376->x<3>().set_ioport("X3");
	m_ay_5_2376->x<4>().set_ioport("X4");
	m_ay_5_2376->x<5>().set_ioport("X5");
	m_ay_5_2376->x<6>().set_ioport("X6");
	m_ay_5_2376->x<7>().set_ioport("X7");
	m_ay_5_2376->shift().set([this]() { return int(BIT(m_modifiers->read(), 0) || BIT(m_modifiers->read(), 2)); });
	m_ay_5_2376->control().set([this]() { return int(BIT(m_modifiers->read(), 1)); });
	m_ay_5_2376->strobe().set(m_pia0, FUNC(pia6821_device::ca1_w));

	PIA6821(config, m_pia0, 0);
	m_pia0->readpa_handler().set(FUNC(hr84_state::pa_r));
	m_pia0->writepb_handler().set(FUNC(hr84_state::pb_w));

	/* audio hardware */
	SPEAKER(config, "mono").front_center();

	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	TIMER(config, "cassette_timer").configure_periodic(FUNC(hr84_state::cassette_input), attotime::from_hz(44100));
}

/* ROM definition */
ROM_START( hr84 )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "c.bin", 0x0000, 0x1000, CRC(7383d204) SHA1(b28a42c87e527017104accdc053ebd7bd1d48ee3))
	ROM_LOAD( "d.bin", 0x1000, 0x1000, CRC(5fb0ca41) SHA1(35259c4147b5ec90dadd37c57778d9d4024014f3))
	ROM_LOAD( "e.bin", 0x2000, 0x1000, CRC(68438449) SHA1(f4853d96b66164bb5e17bc808851b09d379c600b))
	ROM_LOAD( "f.bin", 0x3000, 0x1000, CRC(44bb2afa) SHA1(a1fd5a05cb10954b992ccfa67a1b4a2265aa8ddb))
	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "chargen.bin", 0x0000, 0x0800, CRC(bf7e24e7) SHA1(82de3dd35eb4ebdb2e668e29f8926cb269e7e76b))
	ROM_RELOAD(0x0800, 0x0800)
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY     FULLNAME      FLAGS
COMP( 1982, hr84,  0,      0,      hr84,    hr84,  hr84_state,  empty_init, "Iskra",    "HR-84",      MACHINE_SUPPORTS_SAVE )



ht6000.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    Casio HT-6000

    SD ("Spectrum Dynamic") Synthesizer

    Skeleton driver

***************************************************************************/

#include "emu.h"
#include "cpu/upd7810/upd7810.h"
#include "cpu/mcs48/mcs48.h"


namespace {

//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class ht6000_state : public driver_device
{
public:
	ht6000_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_rom2(*this, "rom2"),
		m_switches(*this, "kc%u", 0),
		m_port_a(0),
		m_led_latch(0xff),
		m_ram_card_addr(0)
	{ }

	void ht6000(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_memory_region m_rom2;
	required_ioport_array<16> m_switches;

	void maincpu_map(address_map &map) ATTR_COLD;

	void port_a_w(uint8_t data);

	void music_w(offs_t offset, uint8_t data);
	void pg1_w(uint8_t data);
	void pg2_w(uint8_t data);
	void pg3_w(uint8_t data);
	void led_w(uint8_t data);
	void led_addr_w(uint8_t data);
	void led_data_w(uint8_t data);
	uint8_t switches_r();
	uint8_t keys_r();
	void ram_card_l_w(uint8_t data);
	void ram_card_h_w(uint8_t data);
	uint8_t rom2_r(offs_t offset);

	uint8_t m_port_a;
	uint8_t m_led_latch;
	uint16_t m_ram_card_addr;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void ht6000_state::maincpu_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("maincpu", 0);
	map(0x8000, 0x9fff).ram();
	map(0xa000, 0xbfff).ram();
	map(0xc000, 0xcfff).w(FUNC(ht6000_state::music_w)); // UPD935G
	map(0xd000, 0xd0ff).w(FUNC(ht6000_state::pg1_w)); // MSM6294-07
	map(0xd100, 0xd1ff).w(FUNC(ht6000_state::pg2_w)); // MSM6294-08
	map(0xd200, 0xd2ff).w(FUNC(ht6000_state::pg3_w)); // MSM6294-09
	map(0xd300, 0xd3ff).w(FUNC(ht6000_state::led_w));
	map(0xd400, 0xd4ff).w(FUNC(ht6000_state::led_addr_w));
	map(0xd500, 0xd5ff).w(FUNC(ht6000_state::led_data_w));
	map(0xd600, 0xd6ff).r(FUNC(ht6000_state::switches_r));
	map(0xd700, 0xd7ff).r(FUNC(ht6000_state::keys_r));
	map(0xd800, 0xd8ff).w(FUNC(ht6000_state::ram_card_l_w));
	map(0xd900, 0xd9ff).w(FUNC(ht6000_state::ram_card_h_w));
	map(0xe000, 0xefff).r(FUNC(ht6000_state::rom2_r));
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( ht6000 )
	PORT_START("kc0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UPPER SYNTH. ENS.")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UPPER COSMIC DANCE")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UPPER STRING ENS.")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UPPER BRASS ENS.")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UPPER PIPE ORGAN")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LINE 1")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LINE 1")

	PORT_START("kc1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UPPER PIANO")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UPPER HARPSICHORD")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UPPER GUITAR")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UPPER TRUMPET")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UPPER VIBRAPHONE")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UPPER TONE SELECT")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LINE 2")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LINE 2")

	PORT_START("kc2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LOWER SYNTH. ENS.")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LOWER BRASS ENS.")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LOWER SYNTH. BRASS")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LOWER COSMIC DANCE")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LOWER JAZZ ORGAN")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LOWER BASS/OBBLI.")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LINE 3")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LINE 3")

	PORT_START("kc3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LOWER PIANO")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LOWER HARPSICHORD")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LOWER HARP")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LOWER GUITAR")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LOWER SYNTH. GUITAR")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ACCOMP. VARIATION")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("kc4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ROCK")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8 BEAT")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("16 BEAT")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DISCO")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("POPS")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RHYTHM VARIATION")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LINE 4")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LINE 4")

	PORT_START("kc5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SWING")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SLOW ROCK")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SAMBA")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BOSSA NOVA")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("WALTZ")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RHYTHM PRESET A")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DETUNE")

	PORT_START("kc6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LOWER PRESET")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LOWER INTERNAL")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LOWER CARD")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RHYTHM PRESET B")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("VELOCITY")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("AMPLITUDE LEVEL")

	PORT_START("kc7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UPPER PRESET")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UPPER INTERNAL")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UPPER CARD")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("AUTO HARMONIZE")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UPPER EDIT")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LOWER EDIT")

	PORT_START("kc8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RHYTHM INTERNAL")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RHYTHM CARD")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("PATTERN/MIDI")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("WRITE")

	PORT_START("kc9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("INTRO/ENDING")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CHORD MEM. RECORD/DELETE")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("OP. MEM. RECORD")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CHORD/OP. MEM. SELECT")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SYNCHRO FILL-IN")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("START/STOP")

	PORT_START("kc10")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("NORMAL")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SPLIT")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("FING'D")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CASIO CHORD")

	PORT_START("kc11")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("kc12")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("kc13")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("kc14")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("kc15")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void ht6000_state::port_a_w(uint8_t data)
{
	logerror("port_a_w: %02x\n", data);
	m_port_a = data;
}

void ht6000_state::music_w(offs_t offset, uint8_t data)
{
	// a8-a11 selects the chip (there are 4), a6-a7 selects command or data
	logerror("music_w: offset = %02x, %02x = %02x\n", offset >> 8, (offset >> 6 & 0x03), data);
}

void ht6000_state::pg1_w(uint8_t data)
{
	logerror("pg1_w: %02x\n", data);
}

void ht6000_state::pg2_w(uint8_t data)
{
	logerror("pg2_w: %02x\n", data);
}

void ht6000_state::pg3_w(uint8_t data)
{
	logerror("pg3_w: %02x\n", data);
}

void ht6000_state::led_w(uint8_t data)
{
	logerror("led_w: %02x\n", data);
}

void ht6000_state::led_addr_w(uint8_t data)
{
	if (data != 0x00)
	{
		logerror("led matrix %02x = %02x\n", data, m_led_latch);
	}
}

void ht6000_state::led_data_w(uint8_t data)
{
	m_led_latch = data;
}

uint8_t ht6000_state::switches_r()
{
	return m_switches[m_port_a & 0x0f]->read();
}

uint8_t ht6000_state::keys_r()
{
	return 0;
}

void ht6000_state::ram_card_l_w(uint8_t data)
{
	// a0-a7
	m_ram_card_addr = (m_ram_card_addr & 0xff00) | (data << 0);
}

void ht6000_state::ram_card_h_w(uint8_t data)
{
	data &= 0x1f; // a8-a12
	m_ram_card_addr = (m_ram_card_addr & 0x00ff) | (data << 8);
}

uint8_t ht6000_state::rom2_r(offs_t offset)
{
	// a12-14 from port a
	offs_t addr = ((m_port_a >> 4) & 0x07) << 12;
	return m_rom2->base()[addr | offset];
}

void ht6000_state::machine_start()
{

}

void ht6000_state::machine_reset()
{

}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void ht6000_state::ht6000(machine_config &config)
{
	upd7810_device &maincpu(UPD7810(config, "maincpu", 12_MHz_XTAL));
	maincpu.set_addrmap(AS_PROGRAM, &ht6000_state::maincpu_map);
	maincpu.pa_out_cb().set(FUNC(ht6000_state::port_a_w));

	I8049(config, "keycpu", 10_MHz_XTAL);
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( ht6000 )
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD("eac-067.bin", 0x0000, 0x8000, CRC(c3063c07) SHA1(f012add068d7d765bcb701ad372c0bab3302a776))

	ROM_REGION(0x8000, "rom2", 0)
	ROM_LOAD("eac-068.bin", 0x0000, 0x8000, CRC(bc28b60d) SHA1(6f4be2861adea57352f0d52c61e004a5c022854a))

	ROM_REGION(0x800, "keycpu", 0)
	ROM_LOAD("187_8734h7.bin", 0x000, 0x800, CRC(47b47af7) SHA1(8f0515f95dcc6e224a8a59e0c2cd7ddb4796e34e))
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY  FULLNAME   FLAGS
CONS( 1987, ht6000, 0,      0,      ht6000,  ht6000, ht6000_state, empty_init, "Casio", "HT-6000", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



ht68k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

        Hawthorne Technology TinyGiant HT68k

        29/11/2009 Skeleton driver.

Monitor commands (from ht68k manual) (must be in Uppercase)
B Load binary file from disk.
C Compare memory.
D Display memory in Hex and ASCII.
E Examine and/or change memory (2 bytes). (. to escape)
F Fill a block of memory with a value.
G Go to an address and execute program.
K Peek.
L Load program from serial port.
M Move memory.
P Poke.
R Read binary file from disk.
S System boot.
T Test printer.
W Write binary file to disk.
X Examine long and/or change memory (4 bytes). (. to escape)


Lot of infos available at: http://www.classiccmp.org/cini/ht68k.htm

****************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
#include "imagedev/floppy.h"
#include "machine/mc68681.h"
#include "machine/wd_fdc.h"
#include "softlist_dev.h"


namespace {

class ht68k_state : public driver_device
{
public:
	ht68k_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_duart(*this, "duart")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0U)
		, m_ram(*this, "mainram")
	{ }

	void ht68k(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	required_device<mc68681_device> m_duart;
	required_device<wd1770_device> m_fdc;
	required_device_array<floppy_connector, 4> m_floppy;

	void duart_txb(int state);
	void duart_output(uint8_t data);
	required_shared_ptr<uint16_t> m_ram;
	void machine_reset() override ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
};


void ht68k_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000000, 0x0007ffff).ram().share("mainram"); // 512 KB RAM / ROM at boot
	//map(0x00080000, 0x000fffff) // Expansion
	//map(0x00d80000, 0x00d8ffff) // Printer
	map(0x00e00000, 0x00e00007).mirror(0xfff8).rw(m_fdc, FUNC(wd1770_device::read), FUNC(wd1770_device::write)).umask16(0x00ff); // FDC WD1770
	map(0x00e80000, 0x00e800ff).mirror(0xff00).rw(m_duart, FUNC(mc68681_device::read), FUNC(mc68681_device::write)).umask16(0x00ff);
	map(0x00f00000, 0x00f07fff).rom().mirror(0xf8000).region("maincpu", 0);
}

/* Input ports */
static INPUT_PORTS_START( ht68k )
INPUT_PORTS_END

void ht68k_state::machine_reset()
{
	uint8_t *bios = memregion("maincpu")->base();

	memcpy((uint8_t*)m_ram.target(),bios,0x8000);

	m_fdc->reset();
	m_fdc->set_floppy(nullptr);
	m_fdc->dden_w(0);
}

void ht68k_state::duart_txb(int state)
{
	//This is the second serial channel named AUX, for modem or other serial devices.
}

void ht68k_state::duart_output(uint8_t data)
{
	logerror("%s: DUART output = %02X\n", machine().describe_context(), data);

	floppy_image_device *floppy = nullptr;

	for (int i = 0; i < 4; i++)
	{
		if (!BIT(data, 7 - i) && m_floppy[i]->get_device() != nullptr)
		{
			floppy = m_floppy[i]->get_device();
			break;
		}
	}

	m_fdc->set_floppy(floppy);

	if (floppy) {floppy->ss_w(BIT(data,3) ? 0 : 1);}
}

static void ht68k_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}


void ht68k_state::ht68k(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, 8_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &ht68k_state::mem_map);

	/* video hardware */
	MC68681(config, m_duart, 8_MHz_XTAL / 2);
	m_duart->set_clocks(500000, 500000, 1000000, 1000000);
	m_duart->irq_cb().set_inputline(m_maincpu, M68K_IRQ_3);
	m_duart->a_tx_cb().set("rs232", FUNC(rs232_port_device::write_txd));
	m_duart->b_tx_cb().set(FUNC(ht68k_state::duart_txb));
	m_duart->outport_cb().set(FUNC(ht68k_state::duart_output));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_duart, FUNC(mc68681_device::rx_a_w));

	WD1770(config, m_fdc, 8_MHz_XTAL);
	m_fdc->intrq_wr_callback().set_inputline(m_maincpu, M68K_IRQ_4);

	FLOPPY_CONNECTOR(config, "fdc:0", ht68k_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", ht68k_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:2", ht68k_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:3", ht68k_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);

	SOFTWARE_LIST(config, "flop525_list").set_original("ht68k");
}

/* ROM definition */
ROM_START( ht68k )
	ROM_REGION16_BE( 0x8000, "maincpu", 0 )
	ROM_LOAD16_BYTE( "ht68k-u4.bin", 0x0001, 0x4000, CRC(3fbcdd0a) SHA1(45fcbbf920dc1e9eada3b7b0a55f5720d08ffdd5))
	ROM_LOAD16_BYTE( "ht68k-u3.bin", 0x0000, 0x4000, CRC(1d85d101) SHA1(8ba01e1595b0b3c4fb128a4a50242f3588b89c43))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY                 FULLNAME           FLAGS
COMP( 1987, ht68k, 0,      0,      ht68k,   ht68k, ht68k_state, empty_init, "Hawthorne Technology", "TinyGiant HT68k", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



hudson_poems.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

/* Based on the "POEMS ES1 Flash ROM Writer Version 0.24 (C)2003-2004 HUDSON SOFT" string
   found in ROM this is assumed to be 'POEMS' hardware.

   https://game.watch.impress.co.jp/docs/20041209/toy166.htm
   https://forum.beyond3d.com/threads/hudson-softs-32-bit-cpu-poems-for-new-system.14358/
   https://web.archive.org/web/20021207035427/http://www.tensilica.com/html/pr_2002_10_15.html

   The above links mention Konami using this hardware for a PLAY-POEMS plug and play sports
   devices, and indicate it is based around the Xtensa instruction set, which has been confirmed
   for the single dumped device.

   https://0x04.net/~mwk/doc/xtensa.pdf

   Known PLAY-POEMS devices (all from Konami)

   2004/11/11   熱血パワプロチャンプ                                  (Baseball game)
   2004/11/11   爽快ゴルフチャンプ                                       (Golf game)
   2004/12/09   絶体絶命でんぢゃらすじーさん ミニゲームで対決じゃっ!     (Mini-Game Collection)
   2005/09/15   マリンバ天国                                          (Marimba Tengoku)
   2005/11/17   絶体絶命でんぢゃらすじーさん パーティーじゃっ!全員集合!!  (Mini-Game Collection)
   2005/11/24   ぐ〜チョコランタン スプーだいすき!プレイマット                (Kid's Floor Mat)

   -------------

   Marimba Tengoku has a secret test menu, see notes near input port definition.
   The ROM check code for that menu is at 0x661010 in ROM and does a full 32-bit word sum
   of the ROM, comparing it against the value stored in the last 4 bytes (it passes)

 */

#include "emu.h"

#include "cpu/xtensa/xtensa.h"
#include "machine/timer.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#define LOG_SPRITEBASE       (1U << 1)

#define VERBOSE (0)
#include "logmacro.h"

namespace {

class hudson_poems_state : public driver_device
{
public:
	hudson_poems_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_palette(*this, "palette"),
		m_screen(*this, "screen"),
		m_gfxdecode(*this, "gfxdecode"),
		m_mainram(*this, "mainram"),
		m_palram(*this, "palram")
	{ }

	void hudson_poems(machine_config &config);

	void init_marimba();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void draw_tile(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect, u32 tile, int xx, int yy, int gfxbase, int extrapal, bool use_flips);
	void draw_tile8(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect, u32 tile, int xx, int yy, int gfxbase, int extrapal, bool use_flips);

	void draw_sprites(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void draw_tilemap(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect, int which, int priority);

	u32 poems_random_r();
	void unk_trigger_w(offs_t offset, u32 data, u32 mem_mask);

	TIMER_DEVICE_CALLBACK_MEMBER(screen_scanline);

	template<int Which> void tilemap_map(address_map &map, u32 base) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	u32 poems_8020010_r();
	u32 poems_count_r();
	u32 poems_unk_r();
	u32 poems_8000038_r(offs_t offset, u32 mem_mask);
	u32 poems_8000200_r(offs_t offset, u32 mem_mask);
	u32 unk_aa04_r(offs_t offset, u32 mem_mask);
	void unk_aa00_w(offs_t offset, u32 data, u32 mem_mask);

	u32 fade_r(offs_t offset, u32 mem_mask);
	void fade_w(offs_t offset, u32 data, u32 mem_mask);
	void unktable_w(offs_t offset, u32 data, u32 mem_mask);
	void unktable_reset_w(offs_t offset, u32 data, u32 mem_mask);
	void set_palette_val(int entry);
	void palette_w(offs_t offset, u32 data, u32 mem_mask);
	void mainram_w(offs_t offset, u32 data, u32 mem_mask);

	void spritegfx_base_w(offs_t offset, u32 data, u32 mem_mask);
	void spritelist_base_w(offs_t offset, u32 data, u32 mem_mask);

	void dma_trigger_w(offs_t offset, u32 data, u32 mem_mask);
	void dma_fill_w(offs_t offset, u32 data, u32 mem_mask);
	void dma_source_w(offs_t offset, u32 data, u32 mem_mask);
	void dma_dest_w(offs_t offset, u32 data, u32 mem_mask);
	void dma_length_w(offs_t offset, u32 data, u32 mem_mask);
	void dma_mode_w(offs_t offset, u32 data, u32 mem_mask);

	template<int Layer> void tilemap_base_w(offs_t offset, u32 data, u32 mem_mask);
	template<int Layer> void tilemap_unk_w(offs_t offset, u32 data, u32 mem_mask);
	template<int Layer> void tilemap_cfg_w(offs_t offset, u32 data, u32 mem_mask);
	template<int Layer> void tilemap_scr_w(offs_t offset, u32 data, u32 mem_mask);
	template<int Layer> void tilemap_high_w(offs_t offset, u32 data, u32 mem_mask);

	template<int Layer> u32 tilemap_base_r(offs_t offset, u32 mem_mask);
	template<int Layer> u32 tilemap_high_r(offs_t offset, u32 mem_mask);
	template<int Layer> u32 tilemap_unk_r(offs_t offset, u32 mem_mask);
	template<int Layer> u32 tilemap_cfg_r(offs_t offset, u32 mem_mask);
	template<int Layer> u32 tilemap_scr_r(offs_t offset, u32 mem_mask);

	u16 m_unktable[256];
	u16 m_unktableoffset;

	u32 m_spritegfxbase[4];
	u32 m_spritelistbase;
	u32 m_tilemapbase[4];
	u32 m_tilemapunk[4];
	u32 m_tilemapcfg[4];
	u32 m_tilemapscr[4];
	u32 m_tilemaphigh[4];

	u32 m_fade;

	u32 m_dmamode;
	u32 m_dmasource;
	u32 m_dmadest;
	u32 m_dmalength;
	u32 m_dmafill;

	s32 m_hackcounter;

	required_device<xtensa_device> m_maincpu;
	required_device<palette_device> m_palette;
	required_device<screen_device> m_screen;
	required_device<gfxdecode_device> m_gfxdecode;
	required_shared_ptr<u32> m_mainram;
	required_shared_ptr<u32> m_palram;
};

void hudson_poems_state::machine_start()
{
	save_item(NAME(m_unktable));
	save_item(NAME(m_unktableoffset));

	save_item(NAME(m_spritegfxbase));
	save_item(NAME(m_spritelistbase));

	save_item(NAME(m_tilemapbase));
	save_item(NAME(m_tilemapunk));
	save_item(NAME(m_tilemapcfg));
	save_item(NAME(m_tilemapscr));
	save_item(NAME(m_tilemaphigh));

	save_item(NAME(m_hackcounter));

	save_item(NAME(m_fade));

	save_item(NAME(m_dmamode));
	save_item(NAME(m_dmasource));
	save_item(NAME(m_dmadest));
	save_item(NAME(m_dmalength));
	save_item(NAME(m_dmafill));
}



void hudson_poems_state::machine_reset()
{
	m_unktableoffset = 0;
	m_hackcounter = 0;

	for (int i = 0; i < 4; i++)
	{
		m_spritegfxbase[i] = m_tilemapbase[i] = m_tilemaphigh[i] = m_tilemapunk[i] = m_tilemapcfg[i] = m_tilemapscr[i] = 0;
	}

	m_fade = 0;

	m_dmamode = 0;
	m_dmasource = 0;
	m_dmadest = 0;
	m_dmalength = 0;
	m_dmafill = 0;
}

/*
You can unlock the test menu on boot by inputing a 3 step sequence while the Konami logo is being shown:
Step 1 checks if you input white or yellow 2 times
Step 2 checks if you input red or blue 2 times
Step 3 checks if you input white or green 3 times

These are not grouped inputs, so for step 1 you must input white x2 or yellow x2 instead of white x1 and yellow x1 for it to count.

*/

static INPUT_PORTS_START( hudson_poems )
	PORT_START( "IN1" )
	PORT_BIT( 0x00000002, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("White")
	PORT_BIT( 0x00000004, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("Yellow (Select Up)")
	PORT_BIT( 0x00000008, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("Red (Ok)")
	PORT_BIT( 0x00000010, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("Blue (Select Down)")
	PORT_BIT( 0x00000200, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(1) PORT_NAME("Green")
INPUT_PORTS_END

static INPUT_PORTS_START( poemgolf )
	PORT_START( "IN1" )
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_BUTTON3 )
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_BUTTON1 ) // O
INPUT_PORTS_END

static INPUT_PORTS_START( poemzet )
	PORT_START( "IN1" )
INPUT_PORTS_END

static INPUT_PORTS_START( poemspoo )
	PORT_START( "IN1" )
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

static INPUT_PORTS_START( ewalk )
	PORT_START( "IN1" )
	PORT_BIT( 0xffffffff, IP_ACTIVE_HIGH, IPT_UNKNOWN )
INPUT_PORTS_END

void hudson_poems_state::draw_sprites(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	int spritebase = (m_spritelistbase & 0x0003ffff) / 4;
	gfx_element *gfx = m_gfxdecode->gfx(2);

	for (int i = 0; i < 64; i++)
	{
		const u16 spriteword0 = m_mainram[(spritebase + i * 2) + 0] & 0xffff;
		const u16 spriteword1 = m_mainram[(spritebase + i * 2) + 0] >> 16;
		const u16 spriteword2 = m_mainram[(spritebase + i * 2) + 1] & 0xffff;
		const u16 spriteword3 = m_mainram[(spritebase + i * 2) + 1] >> 16;

		const int x = (spriteword3 & 0x03ff);
		const int y = (spriteword2 & 0x03ff);
		int tilenum = spriteword1 & 0x07ff;
		const int pal = (spriteword2 & 0x7c00)>>10;

		// is it selecting from multiple tile pages (which can have different bases?) (probably from a register somewhere)
		const int tilebase = (m_spritegfxbase[(spriteword0 & 0x0300)>>8] & 0x0003ffff) / 32; // m_spritegfxbase contains a full memory address pointer to RAM

		tilenum += tilebase;

		/* based on code analysis of function at 006707A4
		word0 ( 0abb bbBB ---- -dFf ) OR ( 1abb bbcc dddd dddd ) a = ?  b = alpha blend? (toggles between all bits on and off for battery logo) BB = sprite base  F = flipX f = flipY
		word1 ( wwhh -ttt tttt tttt ) - other bits are used, but pulled from ram? t = tile number?  ww = width hh = height
		word2 ( 0Ppp ppyy yyyy yyyy ) P = palette bank p = palette y = ypos
		word3 ( aabb bbxx xxxx xxxx ) x = xpos
		*/

		const int alpha = (spriteword0 & 0x3c00)>>10;
		const int flipx = spriteword0 & 2;
		const int flipy = spriteword0 & 1;
		int height = (spriteword1 & 0x3000)>>12;
		height = 1 << height;
		int width = (spriteword1 & 0xc000)>>14;
		width = 1 << width;

		if (alpha == 0x00)
			continue;


		if (spriteword0 & 0x8000)
		{
			// unsure for now
		}
		else
		{
			const int basetilenum = tilenum;
			for (int xx = 0; xx < width; xx++)
			{
				int xpos, ypos;

				if (!flipx)
					xpos = x + xx * 8;
				else
					xpos = x + (((width-1) * 8) - xx * 8);

				for (int yy = 0; yy < height; yy++)
				{
					if (!flipy)
						ypos = y + yy * 8;
					else
						ypos = y + (((height-1) * 8) - yy * 8);

					if (alpha == 0xf)
						gfx->transpen(bitmap, cliprect, basetilenum + xx + (yy * 0x20), pal, flipx, flipy, xpos, ypos, 0);
					else
						gfx->alpha(bitmap, cliprect, basetilenum + xx + (yy * 0x20), pal, flipx, flipy, xpos, ypos, 0, 0xff-(alpha<<4));

				}
			}
		}
	}

	/*
	popmessage("%08x %08x\n%08x %08x\n%08x %08x\n%08x %08x\n", m_mainram[spritebase + 0], m_mainram[spritebase + 1],
	                                                           m_mainram[spritebase + 2], m_mainram[spritebase + 3],
	                                                           m_mainram[spritebase + 4], m_mainram[spritebase + 5],
	                                                           m_mainram[spritebase + 6], m_mainram[spritebase + 7]);
	*/
}

void hudson_poems_state::draw_tilemap(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect, int which, int priority)
{
	int width, base, bpp, gfxbase, extrapal;

	bool use_flips;

	// guess, could be more/less bits, or somewhere else entirely, works for the 2 scenes that need it
	const int thispriority = (m_tilemapcfg[which] & 0x01f00000) >> 20;

	if (thispriority != priority)
		return;

	// seems to get set for disabled layers
	if (m_tilemapcfg[which] & 0x02000000)
		return;

	extrapal = 0;

	if (m_tilemapunk[which] == 2)
		width = 512;
	else
		width = 256;

	if (m_tilemapcfg[which] & 0x20000000)
		bpp = 8;
	else
		bpp = 4;

	// this might not be the correct enable flag
	// marimba needs 2 of the tile bits to act as flip, while poemspoo, poembase, poemgolf use them as tile number bits
	if (m_tilemapcfg[which] & 0x10000000)
		use_flips = false;
	else
		use_flips = true;


	if (m_tilemapcfg[which] & 0x00080000)
		extrapal = 1;
	else
		extrapal = 0;

	// contains a full 32-bit address
	base = (m_tilemapbase[which] & 0x0003ffff) / 4;


	// contains a full 32-bit address.  for this to work in the test mode the interrupts must be disabled as soon as test mode is entered, otherwise the pointer
	// gets overwritten with an incorrect one.  Test mode does not require interrupts to function, although the bit we use to disable them is likely incorrect.
	// this does NOT really seem to be tied to tilemap number, probably from a config reg
	// this reg?
	const int whichbase = (m_tilemapcfg[which] & 0x00000060) >> 5;
	gfxbase = (m_spritegfxbase[whichbase] & 0x0003ffff);

	int yscroll = (m_tilemapscr[which] >> 16) & 0x7ff;
	if (yscroll & 0x400)
		yscroll -= 0x800;

	int xscroll = (m_tilemapscr[which] >> 0) & 0x7ff;
	if (xscroll & 0x400)
		xscroll -= 0x800;

	int tilemap_drawheight = ((m_tilemaphigh[which] >> 16) & 0xff);
	int tilemap_drawwidth = ((m_tilemaphigh[which] >> 0) & 0x1ff);

	// 0 might be maximum, poemzet2
	if (tilemap_drawheight == 0)
		tilemap_drawheight = 240;

	// 0 might be maximum, poemzet
	if (tilemap_drawwidth == 0)
		tilemap_drawwidth = 320;

	// clamp to size of tilemap (test mode has 256 wide tilemap in RAM, but sets full 320 width?)
	if (tilemap_drawwidth > width)
		tilemap_drawwidth = width;

	// note m_tilemaphigh seems to be in pixels, we currently treat it as tiles
	// these could actually be 'end pos' regs, with unknown start regs currently always set to 0
	for (int y = 0; y < tilemap_drawheight / 8; y++)
	{
		const int ypos = (y * 8 + yscroll) & 0x1ff;

		for (int x = 0; x < tilemap_drawwidth / 8 / 2; x++)
		{
			u32 tiles = m_mainram[base + (y * width / 8 / 2) + x];
			const int xpos = (x * 16 + xscroll) & 0x1ff;

			if (bpp == 4)
			{
				draw_tile(screen, bitmap, cliprect, (tiles & 0xffff), xpos, ypos, gfxbase, extrapal, use_flips);
				draw_tile(screen, bitmap, cliprect, ((tiles >> 16) & 0xffff), xpos + 8, ypos, gfxbase, extrapal, use_flips);
			}
			else if (bpp == 8)
			{
				draw_tile8(screen, bitmap, cliprect, (tiles & 0xffff), xpos, ypos, gfxbase, extrapal, use_flips);
				draw_tile8(screen, bitmap, cliprect, ((tiles >> 16) & 0xffff), xpos + 8, ypos, gfxbase, extrapal, use_flips);
			}
		}
	}
}



void hudson_poems_state::spritegfx_base_w(offs_t offset, u32 data, u32 mem_mask)
{
	COMBINE_DATA(&m_spritegfxbase[offset]);
	LOGMASKED(LOG_SPRITEBASE, "%s: spritegfx_base_w %d %08x\n", machine().describe_context(), offset, data);
}

void hudson_poems_state::spritelist_base_w(offs_t offset, u32 data, u32 mem_mask)
{
	COMBINE_DATA(&m_spritelistbase);
}

template<int Layer> void hudson_poems_state::tilemap_base_w(offs_t offset, u32 data, u32 mem_mask) { COMBINE_DATA(&m_tilemapbase[Layer]); }
template<int Layer> void hudson_poems_state::tilemap_cfg_w(offs_t offset, u32 data, u32 mem_mask) { COMBINE_DATA(&m_tilemapcfg[Layer]); }
template<int Layer> void hudson_poems_state::tilemap_unk_w(offs_t offset, u32 data, u32 mem_mask) { COMBINE_DATA(&m_tilemapunk[Layer]); }
template<int Layer> void hudson_poems_state::tilemap_high_w(offs_t offset, u32 data, u32 mem_mask) { COMBINE_DATA(&m_tilemaphigh[Layer]); }
template<int Layer> void hudson_poems_state::tilemap_scr_w(offs_t offset, u32 data, u32 mem_mask) { COMBINE_DATA(&m_tilemapscr[Layer]); }

template<int Layer> u32 hudson_poems_state::tilemap_base_r(offs_t offset, u32 mem_mask) { return m_tilemapbase[Layer]; }
template<int Layer> u32 hudson_poems_state::tilemap_high_r(offs_t offset, u32 mem_mask) { return m_tilemaphigh[Layer]; }
template<int Layer> u32 hudson_poems_state::tilemap_unk_r(offs_t offset, u32 mem_mask) { return m_tilemapunk[Layer]; }
template<int Layer> u32 hudson_poems_state::tilemap_cfg_r(offs_t offset, u32 mem_mask) { return m_tilemapcfg[Layer]; }
template<int Layer> u32 hudson_poems_state::tilemap_scr_r(offs_t offset, u32 mem_mask) { return m_tilemapscr[Layer]; }


void hudson_poems_state::draw_tile(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect, u32 tile, int xx, int yy, int gfxbase, int extrapal, bool use_flips)
{
	gfx_element *gfx = m_gfxdecode->gfx(2);
	int flipx = 0;
	int flipy = 0;
	int pal = (tile & 0xf000)>>12;

	if (use_flips)
	{
		flipx = tile & 0x0800;
		flipy = tile & 0x0400;
		tile &= 0x3ff;
	}
	else
	{
		tile &= 0xfff;
	}

	pal += extrapal * 0x10;
	tile += gfxbase / 32;
	gfx->transpen(bitmap,cliprect,tile,pal,flipx,flipy,xx,yy, 0);
}

void hudson_poems_state::draw_tile8(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect, u32 tile, int xx, int yy, int gfxbase, int extrapal, bool use_flips)
{
	gfx_element *gfx = m_gfxdecode->gfx(3);
	int flipx = 0;
	int flipy = 0;
	int pal = 0;//(tile & 0xf000)>>8;

	if (use_flips)
	{
		flipx = tile & 0x0800;
		flipy = tile & 0x0400;
		tile &= 0x3ff;
	}
	else
	{
		tile &= 0xfff;
	}

	pal += extrapal;
	tile += gfxbase / 64;
	gfx->transpen(bitmap,cliprect,tile,pal,flipx,flipy,xx,yy, 0);
}

u32 hudson_poems_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	pen_t const *const paldata = m_palette->pens();
	bitmap.fill(paldata[0x100], cliprect); // poemzet 'play poems' logo (maybe lowest enabled tilemap is just opaque instead?)

	for (int pri = 0x1f; pri >= 0; pri--)
	{
		draw_tilemap(screen, bitmap, cliprect, 0, pri);
		draw_tilemap(screen, bitmap, cliprect, 1, pri); // title screen background
		draw_tilemap(screen, bitmap, cliprect, 2, pri); // seems to be status bar in the game demo, but suggests that tilegfx base isn't tied to tilmap number
		draw_tilemap(screen, bitmap, cliprect, 3, pri); // background for the character in game demo etc.
	}

	draw_sprites(screen, bitmap, cliprect);

	return 0;
}

u32 hudson_poems_state::poems_unk_r()
{
	return 0x00000000;
}

u32 hudson_poems_state::poems_random_r()
{
	return machine().rand();
}


u32 hudson_poems_state::poems_count_r()
{
	return ((m_hackcounter++) & 0x3) ? 0xffffffff : 0;
}

u32 hudson_poems_state::poems_8020010_r()
{
	return 0x4;
}

u32 hudson_poems_state::unk_aa04_r(offs_t offset, u32 mem_mask)
{
	return ((m_hackcounter++) & 0x3) ? 0xffffffff : 0;
}

void hudson_poems_state::unk_aa00_w(offs_t offset, u32 data, u32 mem_mask)
{
	logerror("%s: unk_aa00_w %08x %08x\n", machine().describe_context(), data, mem_mask);
}

u32 hudson_poems_state::poems_8000038_r(offs_t offset, u32 mem_mask)
{
	if (!machine().side_effects_disabled())
		if (m_maincpu->pc() != 0x2c000b5a)
			logerror("%s: poems_8000038_r %08x\n", machine().describe_context(), mem_mask);

	if (machine().rand() & 1)
		return 0xffffffff;
	else
		return 0x00000000;

	/*
	if (m_mainram[0x1baf8/4] == 0x00000000)
	    return 0xffffffff;
	else
	    return 0x00000000;
	*/
}

u32 hudson_poems_state::poems_8000200_r(offs_t offset, u32 mem_mask)
{
	if (!machine().side_effects_disabled())
		logerror("%s: poems_8000200_r %08x\n", machine().describe_context(), mem_mask);

	// in some places it loops on bit 0 being clear, in other places it seems to simply be used like an ack
	return 0x00000001;
}

void hudson_poems_state::set_palette_val(int entry)
{
	const u16 datax = m_palram[entry];
	const int b = ((datax) & 0x001f) >> 0;
	const int g = ((datax) & 0x03e0) >> 5;
	const int r = ((datax) & 0x7c00) >> 10;
	m_palette->set_pen_color(entry, pal5bit(r), pal5bit(g), pal5bit(b));
}

void hudson_poems_state::palette_w(offs_t offset, u32 data, u32 mem_mask)
{
	COMBINE_DATA(&m_palram[offset]);
	set_palette_val(offset);
}

u32 hudson_poems_state::fade_r(offs_t offset, u32 mem_mask)
{
	return m_fade;
}

void hudson_poems_state::fade_w(offs_t offset, u32 data, u32 mem_mask)
{
	logerror("%s: fade_w %08x %08x\n", machine().describe_context(), data, mem_mask);
	COMBINE_DATA(&m_fade);

	u8 val = 0x1f - ((data >> 16) & 0x1f);

	const double intensity = (double)(val & 0x1f) / (double)0x1f;
	for (int i = 0; i < 0x200; i++)
		m_palette->set_pen_contrast(i, intensity);

}

void hudson_poems_state::unktable_w(offs_t offset, u32 data, u32 mem_mask)
{
	if (mem_mask & 0x0000ffff)
	{
		if (m_unktableoffset < 256)
		{
			m_unktable[m_unktableoffset] = data & 0x0000ffff;
			m_unktableoffset++;
		}
	}

	if (mem_mask & 0xffff0000)
	{
		if (m_unktableoffset < 256)
		{
			m_unktable[m_unktableoffset] = (data & 0xffff0000)>>16;
			m_unktableoffset++;
		}
	}
}

void hudson_poems_state::unktable_reset_w(offs_t offset, u32 data, u32 mem_mask)
{
	m_unktableoffset = 0;
}

void hudson_poems_state::unk_trigger_w(offs_t offset, u32 data, u32 mem_mask)
{
	logerror("%s: unk_trigger_w %08x %08x\n", machine().describe_context(), data, mem_mask);
	m_maincpu->set_input_line(0x4, ASSERT_LINE);
}


void hudson_poems_state::mainram_w(offs_t offset, u32 data, u32 mem_mask)
{
	COMBINE_DATA(&m_mainram[offset]);

	m_gfxdecode->gfx(2)->mark_dirty(offset / 8);
	m_gfxdecode->gfx(3)->mark_dirty(offset / 16);
}

void hudson_poems_state::dma_trigger_w(offs_t offset, u32 data, u32 mem_mask)
{
	// poembase writes 00030101 here when it hasn't set src / 'mode' regs, maybe for clear operations
	//          writes 00030001 when those things are valid
	address_space &cpuspace = m_maincpu->space(AS_PROGRAM);

	logerror("%s: dma_trigger_w %08x %08x (with source %08x dest %08x length %08x mode %08x)\n", machine().describe_context(), data, mem_mask, m_dmasource, m_dmadest, m_dmalength, m_dmamode);

	if ((data == 0x00030001) && (m_dmamode == 0x00003210))
	{
		int length = m_dmalength;
		offs_t source = m_dmasource;
		offs_t dest = m_dmadest;

		while (length >= 0)
		{
			u32 dmadat = cpuspace.read_dword(source);
			cpuspace.write_dword(dest, dmadat);

			source += 4;
			dest += 4;
			length--;
		}
	}
	else if (data == 0x00030101) // mode is not used here, but fill is
	{
		int length = m_dmalength;
		offs_t dest = m_dmadest;

		while (length >= 0)
		{
			cpuspace.write_dword(dest, m_dmafill);

			dest += 4;
			length--;
		}
	}
	else
	{
		logerror("unsure how to handle this DMA, ignoring\n");
	}
}

void hudson_poems_state::dma_source_w(offs_t offset, u32 data, u32 mem_mask)
{
	logerror("%s: dma_source_w %08x %08x\n", machine().describe_context(), data, mem_mask);
	COMBINE_DATA(&m_dmasource);
}

void hudson_poems_state::dma_dest_w(offs_t offset, u32 data, u32 mem_mask)
{
	logerror("%s: dma_dest_w %08x %08x\n", machine().describe_context(), data, mem_mask);
	COMBINE_DATA(&m_dmadest);
}

void hudson_poems_state::dma_length_w(offs_t offset, u32 data, u32 mem_mask)
{
	logerror("%s: dma_length_w %08x %08x\n", machine().describe_context(), data, mem_mask);
	COMBINE_DATA(&m_dmalength);
}

void hudson_poems_state::dma_mode_w(offs_t offset, u32 data, u32 mem_mask)
{
	logerror("%s: dma_mode_w %08x %08x\n", machine().describe_context(), data, mem_mask);
	COMBINE_DATA(&m_dmamode);
}

void hudson_poems_state::dma_fill_w(offs_t offset, u32 data, u32 mem_mask)
{
	logerror("%s: dma_fill_w %08x %08x\n", machine().describe_context(), data, mem_mask);
	COMBINE_DATA(&m_dmafill);
}

template <int Which>
void hudson_poems_state::tilemap_map(address_map &map, u32 base)
{
	map(base+0x00, base+0x03).rw(FUNC(hudson_poems_state::tilemap_cfg_r<Which>), FUNC(hudson_poems_state::tilemap_cfg_w<Which>));
	map(base+0x04, base+0x07).rw(FUNC(hudson_poems_state::tilemap_base_r<Which>), FUNC(hudson_poems_state::tilemap_base_w<Which>));
	map(base+0x08, base+0x0b).rw(FUNC(hudson_poems_state::tilemap_unk_r<Which>), FUNC(hudson_poems_state::tilemap_unk_w<Which>)); // usually 1 or 2 (tilemap width in ram?)
	map(base+0x0c, base+0x0f).ram(); // usually 0 or 1
	map(base+0x10, base+0x13).ram(); // not used?
	map(base+0x14, base+0x17).ram(); // not used?
	map(base+0x18, base+0x1b).rw(FUNC(hudson_poems_state::tilemap_high_r<Which>), FUNC(hudson_poems_state::tilemap_high_w<Which>)); // top word is display height, bottom word seems to be display width
	map(base+0x1c, base+0x1f).rw(FUNC(hudson_poems_state::tilemap_scr_r<Which>), FUNC(hudson_poems_state::tilemap_scr_w<Which>)); // top word is y scroll, bottom word is x scroll
	map(base+0x20, base+0x23).ram(); // used, always 00001000 (maybe zoom?)
	map(base+0x24, base+0x27).ram(); // not used?
	map(base+0x28, base+0x2b).ram(); // not used?
	map(base+0x2c, base+0x2f).ram(); // used, always 00001000 (maybe zoom?)
	map(base+0x30, base+0x33).ram(); // not used?
	map(base+0x34, base+0x37).ram(); // not used?
	map(base+0x38, base+0x3b).ram(); // not used?
	map(base+0x3c, base+0x3f).ram(); // not used?
}

void hudson_poems_state::mem_map(address_map &map)
{
	map(0x00000000, 0x007fffff).mirror(0x20000000).rom().region("maincpu", 0);

	/////////////////// unknown
	map(0x04000040, 0x04000043).r(FUNC(hudson_poems_state::poems_random_r)).nopw();
	map(0x04000140, 0x04000143).r(FUNC(hudson_poems_state::poems_random_r)).nopw();
	map(0x04000240, 0x04000243).r(FUNC(hudson_poems_state::poems_random_r)).nopw();
	map(0x04000340, 0x04000343).r(FUNC(hudson_poems_state::poems_random_r)).nopw();

	//map(0x04000324, 0x04000327).nopw(); // uploads a table here from ROM after uploading fixed values from ROM to some other addresses
	map(0x0400033c, 0x0400033f).w(FUNC(hudson_poems_state::unk_trigger_w)); // maybe DMA?
	map(0x04001000, 0x04001003).r(FUNC(hudson_poems_state::poems_random_r)).nopw();


	map(0x0400100c, 0x0400100f).nopr(); // read in various places at end of calls, every frame, but result seems to go unused?
	map(0x04002040, 0x04002043).portr("IN1");

	/////////////////// palette / regs?

	map(0x08000038, 0x0800003b).r(FUNC(hudson_poems_state::poems_8000038_r));
	//map(0x0800003c, 0x0800003f).nopw(); // used close to the fade write code, writes FFFFFFFF

	//map(0x08000048, 0x0800004b).nopw(); // ^^
	//map(0x0800004c, 0x0800004f).nopw(); // ^^
	//map(0x08000050, 0x08000053).nopw(); // ^^ 16-bit write, sometimes writes 00000101 & 0000FFFF
	//map(0x08000054, 0x08000057).nopw(); // ^^ writes 15555555 while fading
	map(0x0800005c, 0x0800005f).rw(FUNC(hudson_poems_state::fade_r), FUNC(hudson_poems_state::fade_w));

	// are these runtime registers, or DMA sources?
	map(0x08000070, 0x0800007f).w(FUNC(hudson_poems_state::spritegfx_base_w)); // ^^ sometimes writes 2C009C00 (one of the tile data bases)

	// registers 0x080 - 0x0bf are tilemap 0
	tilemap_map<0>(map, 0x08000080);
	// registers 0x0c0 - 0x0ff are tilemap 1
	tilemap_map<1>(map, 0x080000c0);
	// registers 0x100 - 0x13f are tilemap 2
	tilemap_map<2>(map, 0x08000100);
	// registers 0x140 - 0x17f are tilemap 3
	tilemap_map<3>(map, 0x08000140);

	// registers at 0x180 are sprites

	//map(0x08000180, 0x08000183).nopw(); // gets set to 0000007F on startup (list length?)
	map(0x08000184, 0x08000187).w(FUNC(hudson_poems_state::spritelist_base_w)); // gets set to 2C009400 on startup

	map(0x08000800, 0x08000fff).ram().w(FUNC(hudson_poems_state::palette_w)).share("palram");

	/////////////////// sound regs??

	map(0x08008000, 0x08008003).nopw();

	map(0x08008200, 0x08008203).r(FUNC(hudson_poems_state::poems_8000200_r));
	map(0x08008204, 0x08008207).nopr().nopw(); // read only seems to be used to ack after write, value doesn't matter, similar code to 200 above

	map(0x08008400, 0x08008403).nopw();

	map(0x08008a00, 0x08008a7f).ram(); // filled with 32 copies of '0x0001'
	map(0x08008c00, 0x08008c7f).ram(); // filled with 32 copies of '0x0003'
	map(0x08009000, 0x080093ff).ram(); // filled with 32 copies of '0x0200, 0x0004, 0x0000, 0x0100, 0x0c02, 0x0000, 0x0000, 0x0000'
	map(0x08009400, 0x080097ff).ram(); // filled with 32 copies of '0x0000, 0x3fff, 0x0000, 0x0000, 0x3fff, 0x0000, 0x0000, 0x0000'
	map(0x08009800, 0x08009bff).ram(); // filled with 32 copies of '0x0080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000'

	map(0x0800a000, 0x0800a003).nopw();
	map(0x0800a004, 0x0800a007).nopw();

	map(0x0800aa00, 0x0800aa03).w(FUNC(hudson_poems_state::unk_aa00_w)); // writes 2c020000, which is the top of RAM? (or middle?)
	map(0x0800aa04, 0x0800aa07).r(FUNC(hudson_poems_state::unk_aa04_r));

	map(0x0800b000, 0x0800b003).w(FUNC(hudson_poems_state::unktable_w)); // writes a table of increasing 16-bit values here
	map(0x0800b004, 0x0800b007).w(FUNC(hudson_poems_state::unktable_reset_w));

	map(0x0800c040, 0x0800c05f).ram();

	///////////////////
	map(0x0801c000, 0x0801c003).w(FUNC(hudson_poems_state::dma_trigger_w));
	map(0x0801c004, 0x0801c007).w(FUNC(hudson_poems_state::dma_source_w));
	map(0x0801c008, 0x0801c00b).w(FUNC(hudson_poems_state::dma_length_w));
	map(0x0801c018, 0x0801c01b).w(FUNC(hudson_poems_state::dma_dest_w));
	map(0x0801c024, 0x0801c027).w(FUNC(hudson_poems_state::dma_fill_w));
	map(0x0801c028, 0x0801c02b).w(FUNC(hudson_poems_state::dma_mode_w));

	map(0x08020000, 0x08020003).nopr().nopw();
	map(0x08020004, 0x08020007).nopr().nopw();
	map(0x08020008, 0x0802000b).nopr();
	map(0x08020010, 0x08020013).r(FUNC(hudson_poems_state::poems_8020010_r));
	map(0x08020014, 0x08020017).nopw(); // writes 0x10
	map(0x08020018, 0x0802001b).r(FUNC(hudson_poems_state::poems_unk_r)).nopw(); // writes 0x10
	map(0x08020020, 0x08020023).r(FUNC(hudson_poems_state::poems_count_r));

	///////////////////

	map(0x2c000000, 0x2c03ffff).ram().w(FUNC(hudson_poems_state::mainram_w)).share("mainram");
}

static GFXDECODE_START( gfx_poems )
	GFXDECODE_ENTRY( "maincpu", 0, gfx_8x8x4_packed_lsb, 0, 32 )
	GFXDECODE_ENTRY( "maincpu", 0, gfx_8x8x8_raw, 0, 2 )

	GFXDECODE_RAM( "mainram", 0, gfx_8x8x4_packed_lsb, 0, 32 )
	GFXDECODE_RAM( "mainram", 0, gfx_8x8x8_raw, 0, 2 )
GFXDECODE_END

TIMER_DEVICE_CALLBACK_MEMBER(hudson_poems_state::screen_scanline)
{
	const int scanline = param;

	if (scanline == 100)
	{
		if (m_tilemapunk[0] != 1) // wrong, this is probably just tilemap size, with 1 being 256 and 2 being 512, but prevents unwanted IRQs in Test Mode
			m_maincpu->set_input_line(0x10, ASSERT_LINE);
	}

	if (scanline == 150)
	{
		m_maincpu->set_input_line(0x04, CLEAR_LINE);
	}
}

void hudson_poems_state::hudson_poems(machine_config &config)
{
	// 27Mhz XTAL, Xtensa based CPU
	XTENSA(config, m_maincpu, 27_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &hudson_poems_state::mem_map);
	// the vectors can be configured when the CPU is manufactured, so treat as config options
	m_maincpu->set_startupvector(0x00000040);
	m_maincpu->set_irq_vector(4, 0x2c0001b4); // 0x10
	m_maincpu->set_irq_vector(1, 0x2c000194); // 0x02
	m_maincpu->set_irq_vector(2, 0x2c000194); // 0x04
	// 0x2c0001b4 these are also valid vectors
	// 0x2c0001c4
	// 0x2c0001d4
	// 0x2c000000 is a register window exception (4)
	// 0x2c000040 is a register window exception (4)
	// 0x2c000080 is a register window exception (8)
	// 0x2c0000c0 is a register window exception (8)
	// 0x2c000100 is a register window exception (12)
	// 0x2c000140 is a register window exception (12)

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(0));
	screen.set_size(320, 240);
	screen.set_visarea(0, 320-1, 0, 240-1);
	screen.set_screen_update(FUNC(hudson_poems_state::screen_update));

	TIMER(config, "scantimer").configure_scanline(FUNC(hudson_poems_state::screen_scanline), "screen", 0, 1);

	GFXDECODE(config, m_gfxdecode, "palette", gfx_poems);
	PALETTE(config, m_palette).set_format(palette_device::xBGR_555, 0x200);

	SPEAKER(config, "speaker").front_center();
}

void hudson_poems_state::init_marimba()
{
}

ROM_START( marimba )
	ROM_REGION( 0x800000, "maincpu", 0 )
	ROM_LOAD( "marimbatengoku.u2", 0x000000, 0x800000, CRC(b2ac0c5b) SHA1(48f3cdf399b032d86234125eeac3fb1cdc73538a) ) // glob with TSOP pads

	ROM_REGION( 0x400, "nv", 0 )
	ROM_LOAD( "at24c08a.u4", 0x000000, 0x400, CRC(e128a679) SHA1(73fb551d87ed911bd469899343fd36d9d579af39) )
ROM_END

ROM_START( poemgolf )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "poems_golf.u2", 0x000000, 0x400000, CRC(9684a232) SHA1(8a7b97e274dfdddfb6af4df13d714947ef01f29e) ) // glob with TSOP pads

	ROM_REGION( 0x400, "nv", 0 )
	ROM_LOAD( "at24c08a.u3", 0x000000, 0x400, CRC(55856855) SHA1(27b9b42eeea8dd06be43886e40bb2396efc88a67) )
ROM_END

ROM_START( poembase )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "poems_baseball.u2", 0x000000, 0x400000, CRC(105e6c37) SHA1(2a4fe66c08a57bde25047479cbd6ed9ca7c78fa2) ) // glob with TSOP pads

	ROM_REGION( 0x400, "nv", 0 )
	ROM_LOAD( "at24c08a.u3", 0x000000, 0x400, CRC(b83afff4) SHA1(5501e490177b69d61099cc8f1439b91320572584) )
ROM_END

ROM_START( poemzet )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "poems_denjaras.u2", 0x000000, 0x400000, CRC(278d74c2) SHA1(1bd02cce890778409151b20a048892ba3692fd9b) ) // glob with TSOP pads

	ROM_REGION( 0x400, "nv", 0 )
	ROM_LOAD( "at24c08a.u3", 0x000000, 0x400, CRC(594c7c3d) SHA1(b1ddf1d30267f10dac00064b60d8a6b594ae6fc1) )
ROM_END

ROM_START( poemzet2 )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "poems_oldman.u2", 0x000000, 0x400000, CRC(d721c4a9) SHA1(59d88c7f515d10d98e00ac076ebd7ce30cb0bb27) ) // glob with TSOP pads

	ROM_REGION( 0x400, "nv", 0 )
	ROM_LOAD( "at24c08a.u3", 0x000000, 0x400, CRC(5f3d5352) SHA1(94386f5703751e66d6a62e23c344ab7711aad769) )
ROM_END

ROM_START(poemspoo)
	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD( "poems_spoo.u2", 0x000000, 0x400000, CRC(1ebc473a) SHA1(6ebc33f274f71183bcd4a93d06e565eff71e2f47) ) // glob with TSOP pads

	// seeprom position not populated
ROM_END

ROM_START( ewalk2tv )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "poem.u2", 0x000000, 0x400000, CRC(933ed5fd) SHA1(d6c818cfb50be594969e712e394e1a372774ae98) ) // glob with TSOP pads
ROM_END

} // anonymous namespace


CONS( 2005, marimba,      0,       0,      hudson_poems, hudson_poems, hudson_poems_state, init_marimba, "Konami", "Marimba Tengoku (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND )

// waits for 2c008f00 to become 0 (missing irq?) happens before it gets to the DMA transfers
CONS( 2004, poemgolf,     0,       0,      hudson_poems, poemgolf,     hudson_poems_state, init_marimba, "Konami", "Soukai Golf Champ (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND )
// waits for 2c005d00 to become 0 (missing irq?) happens before it gets to the DMA transfers
CONS( 2004, poembase,     0,       0,      hudson_poems, poemgolf,     hudson_poems_state, init_marimba, "Konami", "Nekketsu Pawapuro Champ (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND )

CONS( 2004, poemzet,      0,       0,      hudson_poems, poemzet,      hudson_poems_state, init_marimba, "Konami", "Zettai Zetsumei Dangerous Jiisan - Mini Game de Taiketsu ja!", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND )

CONS( 2005, poemzet2,     0,       0,      hudson_poems, poemzet,      hudson_poems_state, init_marimba, "Konami", "Zettai Zetsumei Dangerous Jiisan Party ja! Zen-in Shuugou!!", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND )

CONS( 2005, poemspoo,     0,       0,      hudson_poems, poemspoo,     hudson_poems_state, init_marimba, "Konami", "Goo Choco Lantan Spoo Daisuki! Playmat", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND)

// Kenshinkeikaku TV (健身計画TV) - TV unit to analyze data from e-walkeylife2 pedometer
CONS( 2006, ewalk2tv,     0,       0,      hudson_poems, ewalk,        hudson_poems_state, init_marimba, "Konami", "Kenshinkeikaku TV (for e-walkeylife2) (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND)



huebler.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/***************************************************************************

    Ausbaufaehiger Mikrocomputer mit dem U 880
    electronica Band 227/228

****************************************************************************/

/*

TODO:

    - keyboard repeat
    - shift lock is not PORT_TOGGLE, instead RS flipflop
    - tone generator
    - cassette serial clock
    - eprom programmer
    - power off

Cassette considerations

    - It operates at 4883 baud but may not be fully compatible with real
      hardware. A partial translation of the available text seems to
      indicate an overcomplicated and unreliable interface. The one
      implemented is 100% reliable at the same speed. I've been unable
      to locate any real recordings to compare against.
    - To save a range of memory: S name start end
    - To load it back: L name
    - To save an unnamed file: S "" start end
    - To load an unnamed file: L
    - When loading, if the name doesn't match, there's no message, and no
      way to determine the name.
    - For some reason, the system saves the file 3 times. Maybe it was an
      attempt to guard against the inherent unreliable design.
*/

#include "emu.h"
#include "huebler.h"
#include "speaker.h"
#include "screen.h"
#include "utf8.h"

/* Keyboard */

void amu880_state::scan_keyboard()
{
	uint8_t data = m_key_row[m_key_a8 ? m_key_d6 : m_key_d7]->read();

	int a8 = (data & 0x0f) == 0x0f;

	if (m_key_a8 && !a8)
	{
		m_key_d7 = m_key_d6;
		m_key_a4 = !(BIT(data, 1) && BIT(data, 3));
		m_key_a5 = !(BIT(data, 2) && BIT(data, 3));
	}

	m_key_a8 = a8;

	m_key_d6++;

	if (m_key_d6 == 16)
	{
		m_key_d6 = 0;
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(amu880_state::keyboard_tick)
{
	scan_keyboard();
}

/* Read/Write Handlers */

uint8_t amu880_state::keyboard_r(offs_t offset)
{
	/*

	    A0..A3  Y
	    A4      !(X0&X3)
	    A5      !(X2&X3)
	    A6      shift
	    A7      ctrl
	    A8      key pressed
	    A9      AB0

	*/

	uint8_t special = m_special->read();

	int ctrl = BIT(special, 0);
	int shift = BIT(special, 2) & BIT(special, 1);
	int ab0 = BIT(offset, 0);

	uint16_t address = (ab0 << 9) | (m_key_a8 << 8) | (ctrl << 7) | (shift << 6) | (m_key_a5 << 5) | (m_key_a4 << 4) | m_key_d7;

	return m_kb_rom->base()[address];
}

/* Memory Maps */

void amu880_state::amu880_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xe7ff).ram();
	map(0xe800, 0xefff).ram().share("video_ram");
	map(0xf000, 0xfbff).rom();
	map(0xfc00, 0xffff).ram();
}

void amu880_state::amu880_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
//  map(0x00, 0x00).mirror(0x03).w(FUNC(amu880_state::power_off_w));
//  map(0x04, 0x04).mirror(0x02).w(FUNC(amu880_state::tone_off_w));
//  map(0x05, 0x05).mirror(0x02).w(FUNC(amu880_state::tone_on_w));
	map(0x08, 0x09).mirror(0x02).r(FUNC(amu880_state::keyboard_r));
	map(0x0c, 0x0f).rw(Z80PIO2_TAG, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x10, 0x13).rw(Z80PIO1_TAG, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x14, 0x17).rw(Z80CTC_TAG, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x18, 0x1b).rw(m_sio, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
}

/* Input Ports */

static INPUT_PORTS_START( amu880 )
	PORT_START("Y0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("Y1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CLR") PORT_CODE(KEYCODE_END)

	PORT_START("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))

	PORT_START("Y3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('?')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))

	PORT_START("Y4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

	PORT_START("Y5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))

	PORT_START("Y6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR('\r')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) // <-|

	PORT_START("Y7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("HOME") PORT_CODE(KEYCODE_HOME)

	PORT_START("Y8")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("Y9")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y10")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CHAR('@') PORT_CHAR('\\')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y11")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y12")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CHAR('_') PORT_CHAR('|')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y13")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y14")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y15")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("SPECIAL")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
INPUT_PORTS_END

/* Video */

uint32_t amu880_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	pen_t const *const pen = m_palette->pens();

	for (int y = 0; y < 240; y++)
	{
		int const line = y % 10;

		for (int sx = 0; sx < 64; sx++)
		{
			uint16_t const videoram_addr = ((y / 10) * 64) + sx;
			uint8_t const videoram_data = m_video_ram[videoram_addr & 0x7ff];

			uint16_t const charrom_addr = ((videoram_data & 0x7f) << 3) | line;
			uint8_t data = m_char_rom->base()[charrom_addr & 0x3ff];

			for (int x = 0; x < 6; x++)
			{
				int const color = ((line > 7) ? 0 : BIT(data, 7)) ^ BIT(videoram_data, 7);

				bitmap.pix(y, (sx * 6) + x) = pen[color];

				data <<= 1;
			}
		}
	}

	return 0;
}

/* Z80-CTC Interface */

void amu880_state::ctc_z0_w(int state)
{
}

void amu880_state::ctc_z2_w(int state)
{
	/* cassette clock @ 39kHz */
	if (state)
	{
		m_cnt++;
		switch (m_cnt & 7) // divide by 8 to get 4883Hz
		{
			case 0:
				m_cassette->output(m_cassbit ? 1.0 : -1.0);
				break;
			case 2:
				m_sio->txca_w(0);
				m_sio->rxca_w(0);
				break;
			case 4:
				m_sio->txca_w(1);
				m_sio->rxca_w(1);
				break;
			case 6:
				m_sio->rxa_w((m_cassette->input() > 0.04) ? 1 : 0);
				break;
			default:
				break;
		}
	}
}

/* Z80-SIO Interface */

void amu880_state::cassette_w(int state)
{
	m_cassbit = state;
}

/* Z80 Daisy Chain */

static const z80_daisy_config amu880_daisy_chain[] =
{
	{ Z80CTC_TAG },
	{ Z80SIO_TAG },
	{ Z80PIO1_TAG },
	{ Z80PIO2_TAG },
	{ nullptr }
};

/* Machine Initialization */

void amu880_state::machine_start()
{
	/* register for state saving */
	save_item(NAME(m_key_d6));
	save_item(NAME(m_key_d7));
	save_item(NAME(m_key_a4));
	save_item(NAME(m_key_a5));
	save_item(NAME(m_key_a8));
}

/* Machine Driver */

/* F4 Character Displayer */
static const gfx_layout amu880_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	128,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_amu880 )
	GFXDECODE_ENTRY( "chargen", 0x0000, amu880_charlayout, 0, 1 )
GFXDECODE_END


void amu880_state::amu880(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(10'000'000)/4); // U880D
	m_maincpu->set_addrmap(AS_PROGRAM, &amu880_state::amu880_mem);
	m_maincpu->set_addrmap(AS_IO, &amu880_state::amu880_io);
	m_maincpu->set_daisy_config(amu880_daisy_chain);

	TIMER(config, "keyboard").configure_periodic(FUNC(amu880_state::keyboard_tick), attotime::from_hz(1500));

	/* video hardware */
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_screen_update(FUNC(amu880_state::screen_update));
	screen.set_raw(9000000, 576, 0*6, 64*6, 320, 0*10, 24*10);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_amu880);
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	/* devices */
	z80ctc_device& ctc(Z80CTC(config, Z80CTC_TAG, XTAL(10'000'000)/4));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	ctc.zc_callback<0>().set(FUNC(amu880_state::ctc_z0_w));
	ctc.zc_callback<1>().set(m_sio, FUNC(z80sio_device::rxtxcb_w));
	ctc.zc_callback<2>().set(FUNC(amu880_state::ctc_z2_w));

	z80pio_device& pio1(Z80PIO(config, Z80PIO1_TAG, XTAL(10'000'000)/4));
	pio1.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80pio_device& pio2(Z80PIO(config, Z80PIO2_TAG, XTAL(10'000'000)/4));
	pio2.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80SIO(config, m_sio, XTAL(10'000'000)/4); // U856
	m_sio->out_txda_callback().set(FUNC(amu880_state::cassette_w));
	m_sio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("64K");
}

/* ROMs */

ROM_START( amu880 )
	ROM_REGION( 0x10000, Z80_TAG, 0 )
	ROM_DEFAULT_BIOS( "v21" )
	ROM_SYSTEM_BIOS( 0, "v21", "H.MON v2.1" )
	ROMX_LOAD( "mon21a.bin", 0xf000, 0x0400, NO_DUMP, ROM_BIOS(0) )
	ROMX_LOAD( "mon21b.bin", 0xf400, 0x0400, NO_DUMP, ROM_BIOS(0) )
	ROMX_LOAD( "mon21c.bin", 0xf800, 0x0400, NO_DUMP, ROM_BIOS(0) )
	ROMX_LOAD( "mon21.bin", 0xf000, 0x0bdf, BAD_DUMP CRC(ba905563) SHA1(1fa0aeab5428731756bdfa74efa3c664898bf083), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v30", "H.MON v3.0" )
	ROMX_LOAD( "mon30.bin", 0xf000, 0x1000, CRC(033f8112) SHA1(0c6ae7b9d310dec093652db6e8ae84f8ebfdcd29), ROM_BIOS(1) )

	ROM_REGION( 0x4800, "hbasic", 0 )
	ROM_LOAD( "mon30p_hbasic33p.bin", 0x0000, 0x4800, CRC(c927e7be) SHA1(2d1f3ff4d882c40438a1281872c6037b2f07fdf2) )

	ROM_REGION( 0x400, "chargen", 0 )
	ROM_LOAD( "hemcfont.bin", 0x0000, 0x0400, CRC(1074d103) SHA1(e558279cff5744acef4eccf30759a9508b7f8750) )

	ROM_REGION( 0x400, "keyboard", 0 )
	ROM_LOAD( "keyboard.bin", 0x0000, 0x0400, BAD_DUMP CRC(daa06361) SHA1(b0299bd8d1686e05dbeeaed54f6c41ae543be20c) ) // typed in from manual
ROM_END

/* System Drivers */

/*    YEAR  NAME    PARENT  COMPAT  MACHINE INPUT   CLASS         INIT        COMPANY                   FULLNAME                                      FLAGS */
COMP( 1983, amu880, 0,      0,      amu880, amu880, amu880_state, empty_init, "Militaerverlag der DDR", "Ausbaufaehiger Mikrocomputer mit dem U 880", MACHINE_NO_SOUND )



hunter16.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Barry Rodewald
/**********************************************************************

    Husky Hunter 16, Hunter 2/16, Hunter 16/80

    Known RAM configurations:
    Hunter 16 - 1M, 2M, 4M
    Hunter 2/16 - 4M
    Hunter 16/80 - 2M

**********************************************************************/


#include "emu.h"

#include "cpu/nec/v25.h"
#include "video/hd61830.h"
#include "video/mc6845.h"

#include "screen.h"
#include "emupal.h"


namespace {

class hunter16_state : public driver_device
{
public:
	hunter16_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_palette(*this, "palette")
		, m_videoram(*this, "videoram")
		, m_lcdc(*this, "lcdc")
		, m_cga(*this, "cga")
	{ }

	void hunter16(machine_config &config);
	void hunter1680(machine_config &config);

protected:
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void mem_map(address_map &map) ATTR_COLD;
	void io_16_map(address_map &map) ATTR_COLD;
	void io_1680_map(address_map &map) ATTR_COLD;

	required_device<v25_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_shared_ptr<uint8_t> m_videoram;
	optional_device<hd61830_device> m_lcdc;
	optional_device<mc6845_device> m_cga;

private:
	void palette_init_hunter16(palette_device &palette);

	MC6845_UPDATE_ROW(crtc_update_row);

	void keyboard_w(uint8_t data);

	uint8_t m_keydata = 0;
	uint8_t pt_r();
};


void hunter16_state::mem_map(address_map &map)
{
	map(0x00000, 0x3ffff).ram();
	map(0xb8000, 0xbffff).ram().share("videoram");
	map(0xf0000, 0xfffff).rom().region("bios",0);
}

void hunter16_state::io_16_map(address_map &map)
{
	map.unmap_value_high();
	map(0x405, 0x405).w(FUNC(hunter16_state::keyboard_w));
	map(0x41b, 0x41b).r(m_lcdc, FUNC(hd61830_device::data_r));
	map(0x430, 0x430).w(m_lcdc, FUNC(hd61830_device::data_w));
	map(0x431, 0x431).rw(m_lcdc, FUNC(hd61830_device::status_r), FUNC(hd61830_device::control_w));
}

void hunter16_state::io_1680_map(address_map &map)
{
	map.unmap_value_high();
	map(0x3d4, 0x3d4).rw(m_cga, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x3d5, 0x3d5).rw(m_cga, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
}

static INPUT_PORTS_START( hunter2 )
	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')

	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc/Brk") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Lbl/Ins") PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)

	PORT_START("X4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR(']')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('\"')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')

	PORT_START("X5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctl/Fn")  PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("X6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')
INPUT_PORTS_END

static INPUT_PORTS_START( hunter16 )
INPUT_PORTS_END

void hunter16_state::palette_init_hunter16(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}

MC6845_UPDATE_ROW(hunter16_state::crtc_update_row)
{
	uint32_t  *p = &bitmap.pix(y);
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();

	for (int i = 0; i < x_count; i++)
	{
		uint16_t const offset = (((ma + i) << 1) & 0x1fff) | ((ra & 1) << 13);
		uint8_t data = m_videoram[offset];

		for (int bit = 7; bit >= 0; bit--)
		{
			*p = palette[BIT(data, bit)]; p++;
		}

		data = m_videoram[offset + 1];

		for (int bit = 7; bit >= 0; bit--)
		{
			*p = palette[BIT(data, bit)]; p++;
		}
	}
}

void hunter16_state::keyboard_w(uint8_t data)
{
	m_keydata = data;
}

uint8_t hunter16_state::pt_r()
{
	uint8_t data = 0xff;

	//if (BIT(m_p0, 0) == 0) data &= m_keys[0]->read();
	//if (BIT(m_p0, 1) == 0) data &= m_keys[1]->read();
	//if (BIT(m_p0, 2) == 0) data &= m_keys[2]->read();
	//if (BIT(m_p0, 3) == 0) data &= m_keys[3]->read();

	return data;
}

void hunter16_state::hunter16(machine_config &config)
{
	V25(config, m_maincpu, 16_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &hunter16_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &hunter16_state::io_16_map);
	m_maincpu->pt_in_cb().set(FUNC(hunter16_state::pt_r));

	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(240, 128);
	m_screen->set_visarea(0, 239, 0, 63);
	m_screen->set_palette(m_palette);
	m_screen->set_screen_update("lcdc", FUNC(hd61830_device::screen_update));

	PALETTE(config, m_palette, FUNC(hunter16_state::palette_init_hunter16), 2);

	HD61830(config, m_lcdc, 16_MHz_XTAL / 16);  // unknown divider
	m_lcdc->set_screen("screen");
}

void hunter16_state::hunter1680(machine_config &config)
{
	V25(config, m_maincpu, 14.318181_MHz_XTAL / 2); // 14.31818
	m_maincpu->set_addrmap(AS_PROGRAM, &hunter16_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &hunter16_state::io_1680_map);

	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_raw(14.318181_MHz_XTAL, 912, 0, 640, 296, 0, 200);
	m_screen->set_screen_update("cga", FUNC(mc6845_device::screen_update));

	PALETTE(config, m_palette, FUNC(hunter16_state::palette_init_hunter16), 2);

	MC6845(config, m_cga, 14.318181_MHz_XTAL / 16); // Chips 82C426 CGA, exact clock unknown
	m_cga->set_screen("screen");
	m_cga->set_show_border_area(false);
	m_cga->set_char_width(8);
	m_cga->set_update_row_callback(FUNC(hunter16_state::crtc_update_row));
}

ROM_START( hunter16 )
	ROM_REGION(0x20000, "bios", 0)
	ROM_DEFAULT_BIOS("304")
	ROM_SYSTEM_BIOS(0, "304", "v3.04")
	ROMX_LOAD( "v3.04.bin", 0x0000, 0x20000, CRC(e6e28685) SHA1(826f3898ee34bd19066daf770167635f9970f8a7), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "303", "v3.03")
	ROMX_LOAD( "v3.03i.bin", 0x0000, 0x20000, CRC(8b3ee7fd) SHA1(8bf962358a316c78ba91658c21e45e1e6cda9605), ROM_BIOS(1) )
	// v3.1 also know to exist
ROM_END

ROM_START( hunter216 )
	ROM_REGION(0x20000, "bios", 0)
	ROM_LOAD( "v1.0.bin", 0x0000, 0x20000, CRC(3810fad6) SHA1(efbf15726c68df5a5339f26331b674c7505d5598) )
ROM_END

ROM_START( hunter1680 )
	ROM_REGION(0x20000, "bios", 0)
	ROM_LOAD( "v4.05.bin", 0x0000, 0x20000, CRC(e0cfabf4) SHA1(183d5bf7553404302697ac89eed25f2a8bb7695c) )
ROM_END

} // anonymous namespace


/*    YEAR  NAME        PARENT    COMPAT  MACHINE     INPUT     CLASS           INIT        COMPANY                FULLNAME              FLAGS */
COMP( 1989, hunter16,   0,        0,      hunter16,   hunter16, hunter16_state, empty_init, "Husky Computers Ltd", "Husky Hunter 16",    MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1990, hunter216,  hunter16, 0,      hunter16,   hunter2,  hunter16_state, empty_init, "Husky Computers Ltd", "Husky Hunter 2/16",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1989, hunter1680, hunter16, 0,      hunter1680, hunter16, hunter16_state, empty_init, "Husky Computers Ltd", "Husky Hunter 16/80", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



hunter2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Barry Rodewald
/***************************************************************************

    Husky Hunter, Hunter 2

    http://www.seasip.info/VintagePC/husky2.html

    The main hardware difference between the original Hunter and Hunter 2 is
    the LCD panel, in the Hunter 2 it is physically larger. It is also driven
    differently. The Hunter sets up the HD61830 number of time divisions to
    be 32 and uses both address counters RAC1/RAC2, whereas the Hunter 2 sets
    number of time divisions to 64 and only uses RAC1 to address the upper half
    of the screen.
    Early models would use all 6 ROM sockets to provide 48K ROM (6 x 2764),
    whereas later models would populate 2 sockets with 2 x 32K ROM (TC57256),
    though only 48K of this is ever paged in.

    Usage:
    The RAMdisk must first be formatted using 'format' (but you'll lose the inbuilt apps).
    The function keys labels at the bottom of the screen are accessed by Ctrl 1 through 8.

    TODO:
    - Add ports 83 and 85
    - Need software
    - Add whatever image devices existed
    - Probably lots of other stuff
    - DIR command can go crazy
    - Clock doesn't advance

****************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/nsc800.h"
#include "machine/mm58274c.h"
#include "machine/nsc810.h"
#include "machine/ram.h"
#include "machine/nvram.h"
#include "sound/spkrdev.h"
#include "video/hd61830.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class hunter2_state : public driver_device
{
public:
	hunter2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_speaker(*this, "speaker")
		, m_kbd(*this, "X%u", 0)
		, m_rs232(*this, "serial")
		, m_rom(*this, "rom")
		, m_ram(*this, RAM_TAG)
		, m_nvram(*this, "nvram")
		, m_bank1(*this, "bank1")
		, m_bank2(*this, "bank2")
		, m_bank3(*this, "bank3")
		, m_battery(*this, "BATTERY")
	{ }

	void hunter2(machine_config &config);
	void hunter(machine_config &config);

	void init_hunter2();

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	uint8_t keyboard_r();
	uint8_t portb_r();
	void keyboard_w(uint8_t data);
	uint8_t portc_r();
	void display_ctrl_w(uint8_t data);
	void serial_tx_w(uint8_t data);
	void serial_dtr_w(uint8_t data);
	void serial_rts_w(uint8_t data);
	void speaker_w(uint8_t data);
	void irqctrl_w(uint8_t data);
	void memmap_w(uint8_t data);
	void hunter2_palette(palette_device &palette) const;

	void timer0_out(int state);
	void timer1_out(int state);
	void cts_w(int state);
	void rxd_w(int state);

	void hunter2_io(address_map &map) ATTR_COLD;
	void hunter2_mem(address_map &map) ATTR_COLD;

	uint8_t m_keydata = 0;
	uint8_t m_irq_mask = 0;
	required_device<cpu_device> m_maincpu;
	required_device<speaker_sound_device> m_speaker;
	required_ioport_array<7> m_kbd;
	required_device<rs232_port_device> m_rs232;
	required_memory_region m_rom;
	required_device<ram_device> m_ram;
	required_device<nvram_device> m_nvram;
	required_memory_bank m_bank1;
	required_memory_bank m_bank2;
	required_memory_bank m_bank3;
	required_ioport m_battery;
};

void hunter2_state::hunter2_mem(address_map &map)
{
	map(0x0000, 0x7fff).bankr("bank1");
	map(0x8000, 0xbfff).bankr("bank2");
	map(0xc000, 0xffff).bankrw("bank3");
}

void hunter2_state::hunter2_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x1f).rw("iotimer", FUNC(nsc810_device::read), FUNC(nsc810_device::write));
	map(0x20, 0x20).w("lcdc", FUNC(hd61830_device::data_w));
	map(0x21, 0x21).rw("lcdc", FUNC(hd61830_device::status_r), FUNC(hd61830_device::control_w));
	map(0x3e, 0x3e).r("lcdc", FUNC(hd61830_device::data_r));
	map(0x40, 0x4f).mirror(0x10).rw("rtc", FUNC(mm58274c_device::read), FUNC(mm58274c_device::write));
	map(0x60, 0x60).mirror(0x1f).w(FUNC(hunter2_state::display_ctrl_w));
	map(0x80, 0x80).mirror(0x18).noprw(); /* POWSAV - Power save control */
	map(0x81, 0x81).mirror(0x18).w(FUNC(hunter2_state::serial_tx_w));
	map(0x82, 0x82).mirror(0x18).w(FUNC(hunter2_state::serial_rts_w));
	map(0x83, 0x83).mirror(0x18).noprw(); /* POWHLD - Power control */
	map(0x84, 0x84).mirror(0x18).noprw(); /* INVCON - V24 inverter control */
	map(0x85, 0x85).mirror(0x18).w(FUNC(hunter2_state::serial_dtr_w));
	map(0x86, 0x86).mirror(0x18).w(FUNC(hunter2_state::speaker_w));
	map(0x87, 0x87).mirror(0x18).noprw(); /* AUXPWR - Turns on the auxiliary power */
	map(0xbb, 0xbb).w(FUNC(hunter2_state::irqctrl_w));
	map(0xe0, 0xe0).w(FUNC(hunter2_state::memmap_w));
}


/* Input ports */
static INPUT_PORTS_START( hunter2 )
	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')

	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc/Brk") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Lbl/Ins") PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)

	PORT_START("X4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR(']')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('\"')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')

	PORT_START("X5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctl/Fn")  PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("X6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')

	PORT_START("BATTERY")
	PORT_CONFNAME(0x04, 0x00, "Battery")
	PORT_CONFSETTING(0x00, "OK")
	PORT_CONFSETTING(0x04, "Low")
INPUT_PORTS_END

uint8_t hunter2_state::keyboard_r()
{
	uint8_t data = 0xff;
	for (int i = 0; i < 7; i++)
	{
		if (!BIT(m_keydata, i))
		{
			data &= m_kbd[i]->read();
		}
	}
	return data;
}

uint8_t hunter2_state::portb_r()
{
	uint8_t res = 0x00;

	if(m_rs232->dsr_r())
		res |= 0x80;

	return res;
}

void hunter2_state::keyboard_w(uint8_t data)
{
	m_keydata = data;
}

/*
INPUTS - This port has a number of input functions:
Bit 0 = Data in (inverted from RS-232 line)
Bit 1 = DCD (inverted from RS-232 line)
Bit 2 = Power low warning (0 = Power OK, 1 = Power low)
Bit 3 = TXCLK (inverted from RS-232 line)
*/
uint8_t hunter2_state::portc_r()
{
	uint8_t res = 0x28;

	if(m_rs232->rxd_r())
		res |= 0x01;
	if(m_rs232->dcd_r())
		res |= 0x02;
	res |= m_battery->read();

	return res;
}

/*
ANGLE - Controls display viewing angle. Input value in the range 0-0x1F.
*/
void hunter2_state::display_ctrl_w(uint8_t data)
{
/* according to the website,
Bit 2: Backlight toggle
Bits 1,0,7,6: Contrast level.
00 is being written here
*/
}

/*
V24OUT - Directly outputs to the V24 data line signal on bit 0.
The output is voltage inverted ie. 0 = +ve, 1 = -ve
*/
void hunter2_state::serial_tx_w(uint8_t data)
{
	m_rs232->write_txd(data & 0x01);
}

/*
DTR output bit
*/
void hunter2_state::serial_dtr_w(uint8_t data)
{
	m_rs232->write_dtr(data & 0x01);
}

/*
RTS output bit
*/
void hunter2_state::serial_rts_w(uint8_t data)
{
	m_rs232->write_rts(data & 0x01);
}

void hunter2_state::speaker_w(uint8_t data)
{
	m_speaker->level_w(BIT(data, 0));
}

/*
ICRREG - NSC800 internal interrupt mask register
Bit 0 = Enable normal interrupts
Bit 1 = Enable RSTC interrupts
Bit 2 = Enable RSTB interrupts
Bit 3 = Enable RSTA interrupts
*/
void hunter2_state::irqctrl_w(uint8_t data)
{
	m_irq_mask = data;
	if(!(data & 0x08))
		m_maincpu->set_input_line(NSC800_RSTA, CLEAR_LINE);
	if(!(data & 0x04))
		m_maincpu->set_input_line(NSC800_RSTB, CLEAR_LINE);
	if(!(data & 0x02))
		m_maincpu->set_input_line(NSC800_RSTC, CLEAR_LINE);
}

/*
PAGE - Memory paging register
*/
void hunter2_state::memmap_w(uint8_t data)
{
	address_space &prog_space = m_maincpu->space(AS_PROGRAM);
	uint8_t bank = data & 0x0f;

	switch (BIT(data, 7))
	{
	case 0: /* ROM */
		prog_space.install_read_bank(0x0000, 0x7fff, m_bank1);
		m_bank1->set_base(m_rom->base());
		/* select ROM bank only if present */
		if (m_rom->bytes() > (0x8000 + (0x4000 * bank)))
		{
			prog_space.install_read_bank(0x8000, 0xbfff, m_bank2);
			m_bank2->set_base(m_rom->base() + (0x8000 + (0x4000 * bank)));
		}
		else
		{
			prog_space.unmap_readwrite(0x8000, 0xbfff);
		}
		break;
	case 1: /* RAM */
		/* select RAM bank only if present */
		if (m_ram->size() > (0x4000 + (0xc000 * bank)))
		{
			prog_space.install_readwrite_bank(0x0000, 0xbfff, m_bank1);
			m_bank1->set_base(m_ram->pointer() + (0x4000 + (0xc000 * bank)));
		}
		else
		{
			prog_space.unmap_readwrite(0x0000, 0xbfff);
		}
		break;
	}
}

void hunter2_state::machine_reset()
{
	m_keydata = 0xff;
	m_irq_mask = 0;

	/* select ROM/RAM banks */
	m_bank1->set_base(m_rom->base());
	m_bank2->set_base(m_rom->base() + 0x4000);
	m_bank3->set_base(m_ram->pointer());
}

void hunter2_state::init_hunter2()
{
	m_nvram->set_base(m_ram->pointer(), m_ram->size());
}

void hunter2_state::hunter2_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}

void hunter2_state::timer0_out(int state)
{
	if(state == ASSERT_LINE)
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}

void hunter2_state::timer1_out(int state)
{
	if(m_irq_mask & 0x08)
		m_maincpu->set_input_line(NSC800_RSTA, state);
}

void hunter2_state::cts_w(int state)
{
	if(BIT(m_irq_mask, 1))
	{
		m_maincpu->set_input_line(NSC800_RSTC, state);
		popmessage("CTS: RSTC set %i\n",state);
	}
}

void hunter2_state::rxd_w(int state)
{
	if(BIT(m_irq_mask, 2))
		m_maincpu->set_input_line(NSC800_RSTB, ASSERT_LINE);
}

void hunter2_state::hunter2(machine_config &config)
{
	/* basic machine hardware */
	NSC800(config, m_maincpu, 8_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &hunter2_state::hunter2_mem);
	m_maincpu->set_addrmap(AS_IO, &hunter2_state::hunter2_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(80);
	screen.set_screen_update("lcdc", FUNC(hd61830_device::screen_update));
	screen.set_size(240, 128);
	screen.set_visarea(0, 239, 0, 63);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(hunter2_state::hunter2_palette), 2);
	hd61830_device &hd61830(HD61830(config, "lcdc", XTAL(4'915'200)/2/2)); // unknown clock
	hd61830.set_screen("screen");

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* Devices */
	mm58274c_device &rtc(MM58274C(config, "rtc", 32.768_kHz_XTAL));
	// this is all guess
	rtc.set_mode24(0); // 12 hour
	rtc.set_day1(1);   // monday

	nsc810_device &iotimer(NSC810(config, "iotimer", 0, 8_MHz_XTAL / 2, 8_MHz_XTAL / 2));
	iotimer.portA_read_callback().set(FUNC(hunter2_state::keyboard_r));
	iotimer.portB_read_callback().set(FUNC(hunter2_state::portb_r));
	iotimer.portB_write_callback().set(FUNC(hunter2_state::keyboard_w));
	iotimer.portC_read_callback().set(FUNC(hunter2_state::portc_r));
	iotimer.timer0_callback().set(FUNC(hunter2_state::timer0_out));
	iotimer.timer1_callback().set(FUNC(hunter2_state::timer1_out));

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->cts_handler().set(FUNC(hunter2_state::cts_w));
	m_rs232->rxd_handler().set(FUNC(hunter2_state::rxd_w));

	/* internal ram */
	RAM(config, m_ram).set_default_size("352K").set_extra_options("80K,144K,208K,496K").set_default_value(0xff);
	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);
}

void hunter2_state::hunter(machine_config &config)
{
	hunter2(config);

	/* video hardware */
	screen_device &screen(*subdevice<screen_device>("screen"));
	screen.set_size(240, 64);
	screen.set_visarea(0, 239, 0, 63);
}

/* ROM definition */
ROM_START( hunter )
	ROM_REGION(0x10000, "rom", ROMREGION_ERASEFF) // board has space for 6 roms, but only 2 are populated normally
	ROM_SYSTEM_BIOS(0, "1", "DEMOS 2.2 9G06h")
	ROMX_LOAD( "99-06h-00.ic50", 0x0000, 0x8000, CRC(ccc57737) SHA1(917d0d7983ab735b24e36b1e75b948c444105f28), ROM_BIOS(0) )
	ROMX_LOAD( "99-06h-01.ic51", 0x8000, 0x8000, CRC(8b1b18c7) SHA1(734d37505cf4c3c8effc75dedd129311a9b20a38), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "2", "DEMOS 2.2 9G+")
	ROMX_LOAD( "husky0iss9.ic50", 0x0000, 0x8000, CRC(77f80f33) SHA1(b0240ecfc95d32e960179af2bfa97fe5b38f7440), ROM_BIOS(1) )
	ROMX_LOAD( "husky1iss9.ic51", 0x8000, 0x8000, CRC(2c8f05fb) SHA1(5debc00037259be0d017c34e54ef4992cd6c0b38), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS(2, "3", "DEMOS Ver 2.2") // not working, maybe auto-run software from RAM
	ROMX_LOAD( "v08c-0.ic50", 0x0000, 0x2000, CRC(e32687f5) SHA1(1679e1aa78d822da57f59f1eb5560c96794a199f), ROM_BIOS(2) )
	ROMX_LOAD( "v08c-1.ic51", 0x2000, 0x2000, CRC(cc669b10) SHA1(f9a197bfc9e5800910ba71b5c4e8d810320a7a43), ROM_BIOS(2) )
	ROMX_LOAD( "v08c-2.ic52", 0x4000, 0x2000, CRC(df0a4dcb) SHA1(79e49d37ae710790c29bc49e2ec7c9c113a36561), ROM_BIOS(2) )
	ROMX_LOAD( "v08c-3.ic53", 0x6000, 0x2000, CRC(d6a7869e) SHA1(e3d3364b4083f312031e96b340fbda763b310d28), ROM_BIOS(2) )
	ROMX_LOAD( "v08c-4.ic54", 0x8000, 0x2000, CRC(0339b049) SHA1(7d5d5297ee9db348566b8f717942bde0eb67d058), ROM_BIOS(2) )
	ROMX_LOAD( "v08c-5.ic55", 0xa000, 0x2000, CRC(98fcde65) SHA1(b9f3ce9a172ab29672026912e96c4a838cbddb46), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS(3, "4", "DEMOS 2.2 9G06h (6 ROMs)")
	ROMX_LOAD( "eprom0.ic50", 0x0000, 0x2000, CRC(ad861c35) SHA1(fe8bcad60d4a0dc94cc6756d9132743f27fbb65d), ROM_BIOS(3) )
	ROMX_LOAD( "eprom1.ic51", 0x2000, 0x2000, CRC(c8c2fae8) SHA1(4235b4483ea8a091d332248074f72f220692c05e), ROM_BIOS(3) )
	ROMX_LOAD( "eprom2.ic52", 0x4000, 0x2000, CRC(b301621f) SHA1(02a0ce58405e95b48f7ac8aa291cb836a6927375), ROM_BIOS(3) )
	ROMX_LOAD( "eprom3.ic53", 0x6000, 0x2000, CRC(be6f196a) SHA1(73fc7f953b56d491ad4fcfe80636db0150a718dc), ROM_BIOS(3) )
	ROMX_LOAD( "eprom4.ic54", 0x8000, 0x2000, CRC(7a0d2228) SHA1(6667fbd5583217ea7ba9b4b4fc9dc56240b0e14e), ROM_BIOS(3) )
	ROMX_LOAD( "eprom5.ic55", 0xa000, 0x2000, CRC(48526702) SHA1(bd8f5a37e5f16511623dc128e2b95b4866b4297a), ROM_BIOS(3) )
ROM_END

ROM_START( hunter2 )
	ROM_REGION(0x10000, "rom", ROMREGION_ERASEFF) // board has space for 6 roms, but only 2 are populated normally
	ROM_SYSTEM_BIOS(0, "1", "DEMOS 2.21 9G08h V4")
	ROMX_LOAD( "tr032kx8mrom0.ic50", 0x0000, 0x8000, CRC(694d252c) SHA1(b11dbf24faf648596d92b1823e25a8e4fb7f542c), ROM_BIOS(0) )
	ROMX_LOAD( "tr032kx8mrom1.ic51", 0x8000, 0x8000, CRC(82901642) SHA1(d84f2bbd2e9e052bd161a313c240a67918f774ad), ROM_BIOS(0) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT    CLASS          INIT          COMPANY                FULLNAME          FLAGS
COMP( 1983, hunter,  hunter2, 0,      hunter,  hunter2, hunter2_state, init_hunter2, "Husky Computers Ltd", "Husky Hunter",   MACHINE_NOT_WORKING )
COMP( 1984, hunter2, 0,       0,      hunter2, hunter2, hunter2_state, init_hunter2, "Husky Computers Ltd", "Husky Hunter 2", MACHINE_NOT_WORKING )



husky.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
// thanks-to:Phill Harvey-Smith
/***************************************************************************

    DVW Husky

    Known RAM configurations:
    Husky - 32K, 48K, 96K (manual suggests 16K, 32K, 48K, 64K, 80K, 96K, 112K, 128K, 144K)

    Usage:
    On first use of BASIC enter NEW to initialise RAM.

    TODO:
    - implement power button
    - test rs232 port
    - Break key doesn't work?

****************************************************************************/

#include "emu.h"
#include "cpu/z80/nsc800.h"
#include "machine/74259.h"
#include "machine/mm58174.h"
#include "machine/nsc810.h"
#include "machine/ram.h"
#include "machine/nvram.h"
#include "sound/beep.h"
#include "bus/rs232/rs232.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class husky_state : public driver_device
{
public:
	husky_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_kbd(*this, "X%u", 0)
		, m_rs232(*this, "serial")
		, m_rom(*this, "rom")
		, m_ram(*this, "ram")
		, m_nvram(*this, "nvram")
		, m_rombank0(*this, "rombank0")
		, m_rombank1(*this, "rombank1")
		, m_rombank2(*this, "rombank2")
		, m_rombank3(*this, "rombank3")
		, m_rambank0(*this, "rambank0")
		, m_rambank1(*this, "rambank1")
		, m_rambank2(*this, "rambank2")
		, m_beeper(*this, "beeper")
		, m_palette(*this, "palette")
		, m_battery(*this, "BATTERY")
	{ }

	void husky(machine_config &config);

	void init_husky();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	uint8_t porta_r();
	void porta_w(uint8_t data);
	uint8_t portb_r();
	uint8_t portc_r();
	void portc_w(uint8_t data);
	void serial_tx_w(int state);
	void cursor_w(uint8_t data);
	void curinh_w(int state);
	void irqctrl_w(uint8_t data);
	void paga16_w(int state);
	void paga17_w(int state);
	void paga18_w(int state);
	void update_page();
	void husky_palette(palette_device &palette) const;

	void timer0_out(int state);
	void timer1_out(int state);
	void cts_w(int state);
	void rxd_w(int state);

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void husky_io(address_map &map) ATTR_COLD;
	void husky_mem(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_ioport_array<4> m_kbd;
	required_device<rs232_port_device> m_rs232;
	required_memory_region m_rom;
	required_device<ram_device> m_ram;
	required_device<nvram_device> m_nvram;
	required_memory_bank m_rombank0;
	required_memory_bank m_rombank1;
	required_memory_bank m_rombank2;
	required_memory_bank m_rombank3;
	required_memory_bank m_rambank0;
	required_memory_bank m_rambank1;
	required_memory_bank m_rambank2;
	required_device<beep_device> m_beeper;
	required_device<palette_device> m_palette;
	required_ioport m_battery;

	uint8_t m_cursor;
	uint8_t m_curinh;
	uint16_t m_keydata;
	uint8_t m_irq_mask;
	uint8_t m_page;
};

void husky_state::husky_mem(address_map &map)
{
	map(0x0000, 0x0fff).bankr("rombank0");
	map(0x1000, 0x1fff).bankr("rombank1");
	map(0x2000, 0x2fff).bankr("rombank2");
	map(0x3000, 0x3fff).bankr("rombank3");
	map(0x4000, 0x7fff).bankrw("rambank0");
	map(0x8000, 0xbfff).bankrw("rambank1");
	map(0xc000, 0xffff).bankrw("rambank2");
}

void husky_state::husky_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x1f).rw("iotimer", FUNC(nsc810_device::read), FUNC(nsc810_device::write));
	map(0x20, 0x20).mirror(0x1f).w(FUNC(husky_state::cursor_w));
	map(0x40, 0x4f).rw("rtc", FUNC(mm58174_device::read), FUNC(mm58174_device::write));
	map(0x80, 0x87).mirror(0x18).w("outlatch", FUNC(addressable_latch_device::write_d0));
	map(0xbb, 0xbb).w(FUNC(husky_state::irqctrl_w));
	map(0xc0, 0xc0).noprw(); /* PPORT0 - Parallel port 0 */
	map(0xc1, 0xc1).noprw(); /* PPORT1 - Parallel port 1 */
	map(0xc2, 0xc2).noprw(); /* ADIN - Analogue port */
}


static INPUT_PORTS_START(husky)
	PORT_START("X0")
	PORT_BIT(0x000f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('!')
	PORT_BIT(0x0020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('"')
	PORT_BIT(0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR(';')
	PORT_BIT(0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('[')
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR(']')
	PORT_BIT(0x0200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('\\')
	PORT_BIT(0x0400, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('/')
	PORT_BIT(0x0800, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('(')
	PORT_BIT(0x1000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR(')')
	PORT_BIT(0x2000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_NAME("P Break")

	PORT_START("X1")
	PORT_BIT(0x000f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('?')
	PORT_BIT(0x0020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('>')
	PORT_BIT(0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR(':')
	PORT_BIT(0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('\'')
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('`')
	PORT_BIT(0x0200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('#')
	PORT_BIT(0x0400, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('*')
	PORT_BIT(0x0800, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('=')
	PORT_BIT(0x1000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR(27) PORT_NAME("L Esc")
	PORT_BIT(0x2000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Power on/off")

	PORT_START("X2")
	PORT_BIT(0x000f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x0020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('<')
	PORT_BIT(0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('$')
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('%')
	PORT_BIT(0x0200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR(UCHAR_MAMEKEY(INSERT)) PORT_NAME("V Insert")
	PORT_BIT(0x0400, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('-')
	PORT_BIT(0x0800, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_NAME("N Left")
	PORT_BIT(0x1000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_NAME("M Right")
	PORT_BIT(0x2000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)

	PORT_START("X3")
	PORT_BIT(0x000f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_NAME("1 Help")
	PORT_BIT(0x0020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR(',')
	PORT_BIT(0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('&')
	PORT_BIT(0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('@')
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_NAME("5 Control")
	PORT_BIT(0x0200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('.')
	PORT_BIT(0x0400, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('+')
	PORT_BIT(0x0800, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_NAME("8 Down")
	PORT_BIT(0x1000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_NAME("9 Up")
	PORT_BIT(0x2000, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(UCHAR_MAMEKEY(DEL)) PORT_NAME("0 Del")

	PORT_START("BATTERY")
	PORT_CONFNAME(0x04, 0x04, "Battery")
	PORT_CONFSETTING(0x00, "Low")
	PORT_CONFSETTING(0x04, "OK")
INPUT_PORTS_END


uint8_t husky_state::porta_r()
{
	uint8_t data = 0xff;

	if (!BIT(m_keydata, 0)) data &= m_kbd[0]->read() & 0x00ff;
	if (!BIT(m_keydata, 1)) data &= m_kbd[1]->read() & 0x00ff;
	if (!BIT(m_keydata, 2)) data &= m_kbd[2]->read() & 0x00ff;
	if (!BIT(m_keydata, 3)) data &= m_kbd[3]->read() & 0x00ff;

	return data;
}

void husky_state::porta_w(uint8_t data)
{
	m_keydata = data;
}

uint8_t husky_state::portb_r()
{
	uint8_t data = 0xff;

	if (!BIT(m_keydata, 0)) data &= (m_kbd[0]->read() & 0xff00) >> 8;
	if (!BIT(m_keydata, 1)) data &= (m_kbd[1]->read() & 0xff00) >> 8;
	if (!BIT(m_keydata, 2)) data &= (m_kbd[2]->read() & 0xff00) >> 8;
	if (!BIT(m_keydata, 3)) data &= (m_kbd[3]->read() & 0xff00) >> 8;

	return data;
}

/*
Bit 2 = Power low warning (1 = Power OK, 0 = Power low)
Input bits 7,6,4,3,2
*/
uint8_t husky_state::portc_r()
{
	uint8_t data = 0x23;

	data |= m_battery->read();
	data |= m_rs232->cts_r() << 3;

	return data;
}

/*
Output bits 5,1,0
*/
void husky_state::portc_w(uint8_t data)
{
	m_beeper->set_state(!BIT(data, 0));
	m_rs232->write_rts(BIT(data, 1));
}

/*
CURSOR - Output with a number 0-7F for the cursor position on the display
*/
void husky_state::cursor_w(uint8_t data)
{
	m_cursor = data & 0x7f;
}

/*
CURINH - Inhibit cursor. BIT0 = 1 : Cursor off, BIT0 = 0 : Cursor on
*/
void husky_state::curinh_w(int state)
{
	m_curinh = state;
}

/*
V24OUT - Directly outputs to the V24 data line signal on bit 0.
The output is voltage inverted ie. 0 = +ve, 1 = -ve
*/
void husky_state::serial_tx_w(int state)
{
	m_rs232->write_txd(state);
}

/*
ICRREG - NSC800 internal interrupt mask register
Bit 0 = Enable normal interrupts
Bit 1 = Enable RSTC interrupts
Bit 2 = Enable RSTB interrupts
Bit 3 = Enable RSTA interrupts
*/
void husky_state::irqctrl_w(uint8_t data)
{
	m_irq_mask = data;
	if(!(data & 0x08))
		m_maincpu->set_input_line(NSC800_RSTA, CLEAR_LINE);
	if(!(data & 0x04))
		m_maincpu->set_input_line(NSC800_RSTB, CLEAR_LINE);
	if(!(data & 0x02))
		m_maincpu->set_input_line(NSC800_RSTC, CLEAR_LINE);
}

/*
PAGA 16, 17, 18 - Memory paging addresses
*/
void husky_state::paga16_w(int state)
{
	if (state)
		m_page |= 1 << 0;
	else
		m_page &= ~(1 << 0);

	update_page();
}

void husky_state::paga17_w(int state)
{
	if (state)
		m_page |= 1 << 1;
	else
		m_page &= ~(1 << 1);

	update_page();
}

void husky_state::paga18_w(int state)
{
	if (state)
		logerror("PAGA18 not implemented!\n");
}

void husky_state::update_page()
{
	/* ROM banks */
	m_rombank0->set_entry(m_page & 0x03);
	m_rombank1->set_entry(m_page & 0x03);
	m_rombank2->set_entry(m_page & 0x03);
	m_rombank3->set_entry(m_page & 0x03);

	/* RAM banks */
	m_rambank1->set_entry(m_page & 0x03);
	m_rambank2->set_entry(m_page & 0x03);

	address_space &prog_space = m_maincpu->space(AS_PROGRAM);

	if (m_ram->size() > 0x4000 + (0x8000 * (m_page & 0x03)))
		prog_space.install_readwrite_bank(0x8000, 0xbfff, m_rambank1);
	else
		prog_space.nop_readwrite(0x8000, 0xbfff);

	if (m_ram->size() > 0x8000 + (0x8000 * (m_page & 0x03)))
		prog_space.install_readwrite_bank(0xc000, 0xffff, m_rambank2);
	else
		prog_space.nop_readwrite(0xc000, 0xffff);
}

void husky_state::machine_start()
{
	/* configure ROM/RAM banks */
	m_rombank0->configure_entries(0, 4, m_rom->base() + 0x0000, 0x1000);
	m_rombank1->configure_entries(0, 4, m_rom->base() + 0x4000, 0x1000);
	m_rombank2->configure_entries(0, 4, m_rom->base() + 0x8000, 0x1000);
	m_rombank3->configure_entries(0, 4, m_rom->base() + 0xc000, 0x1000);
	m_rambank0->configure_entry(0, m_ram->pointer());
	m_rambank1->configure_entries(0, 4, m_ram->pointer() + 0x4000, 0x8000);
	m_rambank2->configure_entries(0, 4, m_ram->pointer() + 0x8000, 0x8000);

	/* register for save states */
	save_item(NAME(m_cursor));
	save_item(NAME(m_curinh));
	save_item(NAME(m_page));
}

void husky_state::machine_reset()
{
	m_keydata = 0xff;
	m_irq_mask = 0;
	m_page = 0x00;

	/* select ROM/RAM banks */
	m_rombank0->set_entry(0);
	m_rombank1->set_entry(0);
	m_rombank2->set_entry(0);
	m_rombank3->set_entry(0);
	m_rambank0->set_entry(0);
	m_rambank1->set_entry(0);
	m_rambank2->set_entry(0);
}

void husky_state::init_husky()
{
	m_nvram->set_base(m_ram->pointer(), m_ram->size());
}

uint32_t husky_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	/*
	The LCD panel contains 4 rows of 32 characters, each character is a 5x7 dot-matrix,
	separated by a single dot space. Beneath each character is an empty line followed by
	a 5x1 dot-matrix used for the cursor. The character rows are separated by 3 (maybe 4)
	empty lines.
	Each character is stored as 8 bytes, only 7 are used. To include cursor and spacing
	I'm drawing each character as 6x12 to form an overall screen of 192x48.
	*/

	pen_t const *const pen = m_palette->pens();

	for (int y = 0; y < 48; y++)
	{
		for (int x = 0; x < 32; x++)
		{
			uint8_t data;
			switch (y % 12)
			{
			case 0: case 1: case 2: case 3: case 4: case 5: case 6:
				/* character */
				data = m_ram->pointer()[(x * 8) + ((y / 12) * 32 * 8) + (y % 12)];

				for (int b = 0; b < 5; b++)
				{
					bitmap.pix(y, (x * 6) + b) = BIT(data, 0) ? pen[1] : pen[2];
					data >>= 1;
				}
				bitmap.pix(y, (x * 6) + 5) = pen[0];
				break;
			case 8:
				/* cursor */
				for (int b = 0; b < 5; b++)
				{
					bitmap.pix(y, (x * 6) + b) = (!m_curinh && m_cursor == (y / 12) * 32 + x) ? pen[1] : pen[2];
				}
				bitmap.pix(y, (x * 6) + 5) = pen[0];
				break;
			default:
				/* blank */
				for (int b = 0; b < 6; b++)
				{
					bitmap.pix(y, (x * 6) + b) = pen[0];
				}
				break;
			}
		}
	}
	return 0;
}

void husky_state::husky_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148)); // background
	palette.set_pen_color(1, rgb_t(92, 83, 88));    // LCD pixel on
	palette.set_pen_color(2, rgb_t(131, 136, 139)); // LCD pixel off
}

void husky_state::timer0_out(int state)
{
	if(state == ASSERT_LINE)
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}

void husky_state::timer1_out(int state)
{
	if(BIT(m_irq_mask, 7))
		m_maincpu->set_input_line(NSC800_RSTA, state);
}

void husky_state::cts_w(int state)
{
	if(BIT(m_irq_mask, 1))
		m_maincpu->set_input_line(NSC800_RSTC, state);
}

void husky_state::rxd_w(int state)
{
	if(BIT(m_irq_mask, 2))
		m_maincpu->set_input_line(NSC800_RSTB, ASSERT_LINE);
}

void husky_state::husky(machine_config &config)
{
	/* basic machine hardware */
	NSC800(config, m_maincpu, 2_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &husky_state::husky_mem);
	m_maincpu->set_addrmap(AS_IO, &husky_state::husky_io);

	cd4099_device &outlatch(CD4099(config, "outlatch")); // HCF4099BE
	outlatch.q_out_cb<0>().set_nop(); // POWSAV - Power save control
	outlatch.q_out_cb<1>().set(FUNC(husky_state::serial_tx_w));
	outlatch.q_out_cb<2>().set(FUNC(husky_state::curinh_w));
	outlatch.q_out_cb<3>().set_nop(); // POWHLD - Power control
	outlatch.q_out_cb<4>().set_nop(); // INVCON - V24 inverter control
	outlatch.q_out_cb<5>().set(FUNC(husky_state::paga16_w));
	outlatch.q_out_cb<6>().set(FUNC(husky_state::paga17_w));
	outlatch.q_out_cb<7>().set(FUNC(husky_state::paga18_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_screen_update(FUNC(husky_state::screen_update));
	screen.set_size(192, 48);
	screen.set_visarea(0, 191, 0, 47);
	PALETTE(config, m_palette, FUNC(husky_state::husky_palette), 3);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 3200).add_route(ALL_OUTPUTS, "mono", 1.0); // TODO: unknown frequency

	/* internal ram */
	RAM(config, m_ram).set_default_size("48K").set_extra_options("16K,32K,64K,80K,96K,112K,128K,144K");
	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	/* Devices */
	MM58174(config, "rtc", 32.768_kHz_XTAL); // MM58174A

	nsc810_device &iotimer(NSC810(config, "iotimer", 0, 2_MHz_XTAL / 2, 2_MHz_XTAL / 2));
	iotimer.portA_read_callback().set(FUNC(husky_state::porta_r));
	iotimer.portA_write_callback().set(FUNC(husky_state::porta_w));
	iotimer.portB_read_callback().set(FUNC(husky_state::portb_r));
	iotimer.portC_read_callback().set(FUNC(husky_state::portc_r));
	iotimer.portC_write_callback().set(FUNC(husky_state::portc_w));
	iotimer.timer0_callback().set(FUNC(husky_state::timer0_out));
	iotimer.timer1_callback().set(FUNC(husky_state::timer1_out));

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->cts_handler().set(FUNC(husky_state::cts_w));
	m_rs232->rxd_handler().set(FUNC(husky_state::rxd_w));

	/* optional ADC ICL7109CPL @ 6Mhz */
}


ROM_START( husky )
	/* EPROM's are any of 2732, 2764 or 27128 varieties */
	ROM_REGION(0x10000, "rom", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "hc19jul", "HC19JUL")
	ROMX_LOAD( "dvweprom0.ic27", 0x0000, 0x2000, CRC(4657cf89) SHA1(7f3d5a581a8f4a7ea6ef1eb93881f64dbb6af65b), ROM_BIOS(0) )
	ROM_RELOAD(                  0x2000, 0x2000)
	ROMX_LOAD( "dvweprom1.ic26", 0x4000, 0x2000, CRC(1cf2553f) SHA1(5a594820753a1389999d5c21dd7c9c01672d92f2), ROM_BIOS(0) )
	ROM_RELOAD(                  0x6000, 0x2000)
	ROMX_LOAD( "dvweprom2.ic25", 0x8000, 0x2000, CRC(6e91e6a5) SHA1(fef28f00767cb4d52e56fe7ee993fa92a2c77a1f), ROM_BIOS(0) )
	ROM_RELOAD(                  0xa000, 0x2000)
	ROMX_LOAD( "dvweprom3.ic24", 0xc000, 0x2000, CRC(595d36f4) SHA1(252d4cdff1ac19c4b1dc2d6dcf13f108d485bd4c), ROM_BIOS(0) )
	ROM_RELOAD(                  0xe000, 0x2000)
	ROM_SYSTEM_BIOS(1, "hs19jul", "HS19JUL")
	ROMX_LOAD( "hs-19-jul-0.ic27", 0x0000, 0x2000, CRC(c42ab5d5) SHA1(36dff5a25212c19fd8b88fe7288012ba6015fd31), ROM_BIOS(1) )
	ROM_RELOAD(                    0x2000, 0x2000)
	ROMX_LOAD( "hs-19-jul-1.ic26", 0x4000, 0x2000, CRC(b753891f) SHA1(fd29ea1d2e9de094d2974bbc0fe47c4f2e99e43f), ROM_BIOS(1) )
	ROM_RELOAD(                    0x6000, 0x2000)
	ROMX_LOAD( "hs-19-jul-2.ic25", 0x8000, 0x2000, CRC(a75a5eef) SHA1(26e79a1382fc051f84c46f6add223ffe46a22036), ROM_BIOS(1) )
	ROM_RELOAD(                    0xa000, 0x2000)
	ROMX_LOAD( "hs-19-jul-3.ic24", 0xc000, 0x2000, CRC(ea430f24) SHA1(dd445c8af2315e6fe02e7e7a4a0dcc0f22778b7c), ROM_BIOS(1) )
	ROM_RELOAD(                    0xe000, 0x2000)
	ROM_SYSTEM_BIOS(2, "hc03jun", "HC03JUN")
	ROMX_LOAD( "03-jun-00.ic27", 0x0000, 0x2000, CRC(e08096e5) SHA1(5c34da226a93b414fadadc7fa3fadaa2c11bb34a), ROM_BIOS(2) )
	ROM_RELOAD(                  0x2000, 0x2000)
	ROMX_LOAD( "03-jun-01.ic26", 0x4000, 0x2000, CRC(ff961693) SHA1(2f51bd07245118d03524684ceb63df97f11bffcb), ROM_BIOS(2) )
	ROM_RELOAD(                  0x6000, 0x2000)
	ROMX_LOAD( "03-jun-02.ic25", 0x8000, 0x2000, CRC(be6cf4d3) SHA1(3a7b49ec83e6612ac23149ea1f0ae4ff7a90a258), ROM_BIOS(2) )
	ROM_RELOAD(                  0xa000, 0x2000)
	ROMX_LOAD( "03-jun-03.ic24", 0xc000, 0x2000, CRC(8fd629d1) SHA1(ef2821c9ce1c1375326d80cad3bfc74e75548172), ROM_BIOS(2) )
	ROM_RELOAD(                  0xe000, 0x2000)
ROM_END

} // anonymous namespace


//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY                 FULLNAME  FLAGS
COMP( 1981, husky, 0,      0,      husky,   husky, husky_state, init_husky, "DVW Microelectronics", "Husky",  0 )



hx20.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/***************************************************************************

    Epson HX-20

    http://fjkraan.home.xs4all.nl/comp/hx20/

    Epson CM6000 Series

    These are re-badged HX-20 with revision H motherboard and keyboard overlay.

    CM6032: Takes 3x16K ROMs instead of the usual 4x8K + 8K optional. Appears to
    be from a BT phone exchange.
    Label on base states CM6000 Series, CM6032 System, MOH 89/1.

    CM6127: Takes 2x16K ROMs and 32K CMOS RAM. Also has an expansion unit
    containing 2x16K ROMs and 32K CMOS RAM.
    Label on base states CM6000 Series, CM6127H System.

****************************************************************************/

/*

    TODO:

    - m6800.cpp rewrite
    - keyboard interrupt
    - LCD controller
    - serial
    - SW6 read
    - RS-232
    - microcassette
    - printer
    - ROM cartridge
    - floppy TF-20
    - barcode reader

*/

#include "emu.h"
#include "hx20.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "utf8.h"


//**************************************************************************
//  READ/WRITE HANDLERS
//**************************************************************************

//-------------------------------------------------
//  update_interrupt -
//-------------------------------------------------

void hx20_state::update_interrupt()
{
	int irq = m_rtc_irq || m_kbrequest;

	m_maincpu->set_input_line(HD6301_IRQ1_LINE, irq);
}


//-------------------------------------------------
//  ksc_w -
//-------------------------------------------------

void hx20_state::ksc_w(uint8_t data)
{
	logerror("KSC %02x\n", data);

	m_ksc = data;
}


//-------------------------------------------------
//  krtn07_r -
//-------------------------------------------------

uint8_t hx20_state::krtn07_r()
{
	uint8_t data = 0xff;
	for (int b = 0; 8 > b; ++b)
		if (!BIT(m_ksc, b))
			data &= m_ksc_io[b]->read();

	return data;
}


//-------------------------------------------------
//  krtn89_r -
//-------------------------------------------------

uint8_t hx20_state::krtn89_r()
{
	/*

	    bit     description

	    0       KRTN8
	    1       KRTN9
	    2
	    3
	    4
	    5
	    6       _PWSW
	    7       _BUSY

	*/

	uint8_t data = 0xff;
	for (int b = 0; 8 > b; ++b)
		if (!BIT(m_ksc, b))
			data &= m_ksc_io[b]->read() >> 8;

	return data;
}


//-------------------------------------------------
//  lcd_cs_w -
//-------------------------------------------------

void hx20_state::lcd_cs_w(uint8_t data)
{
	/*

	    bit     description

	    0       LCD CS bit 0
	    1       LCD CS bit 1
	    2       LCD CS bit 2
	    3       LCD C/_D
	    4       KEY INT MASK
	    5       SERIAL POUT
	    6       MO1
	    7       MO2

	*/

	logerror("LCD CS %02x\n", data);

	// LCD
	for (auto &lcdc : m_lcdc)
		lcdc->cs_w(1);

	if (data & 0x07)
		m_lcdc[(data & 0x07) - 1]->cs_w(0);

	int const cd = BIT(data, 3);
	for (auto &lcdc : m_lcdc)
		lcdc->cd_w(cd);

	// serial
	m_sio->pout_w(BIT(data, 5));
}


//-------------------------------------------------
//  lcd_data_w -
//-------------------------------------------------

void hx20_state::lcd_data_w(uint8_t data)
{
	logerror("LCD DATA %02x\n", data);

	m_lcd_data = data;
}


//-------------------------------------------------
//  main_p1_r - main CPU port 1 read
//-------------------------------------------------

uint8_t hx20_state::main_p1_r()
{
	/*

	    bit     description

	    0       RS-232 DSR
	    1       RS-232 CTS
	    2
	    3       _INT EX
	    4       _PWA
	    5       _K.B REQUEST
	    6       SERIAL PIN
	    7       CARTRIDGE MI1 (0=ROM, 1=microcassette)

	*/

	uint8_t data = 0x98;

	// RS-232
	data |= m_rs232->dsr_r();
	data |= m_rs232->cts_r() << 1;

	// keyboard
	data |= m_kbrequest << 5;

	// serial
	data |= m_sio_pin << 6;

	return data;
}


//-------------------------------------------------
//  main_p1_w - main CPU port 1 write
//-------------------------------------------------

void hx20_state::main_p1_w(uint8_t data)
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4
	    5
	    6
	    7

	*/
}


//-------------------------------------------------
//  main_p2_r - main CPU port 2 read
//-------------------------------------------------

uint8_t hx20_state::main_p2_r()
{
	/*

	    bit     description

	    0       bar code reader data
	    1
	    2       SLAVE P34
	    3       RX
	    4
	    5
	    6
	    7

	*/

	uint8_t data = M6801_MODE_4;

	// serial
	data &= ~(!m_slave_flag << 2);

	if (m_slave_sio)
		data |= m_slave_rx << 3;
	else
		data |= m_sio_rx << 3;

	return data;
}


//-------------------------------------------------
//  main_p2_w - main CPU port 2 write
//-------------------------------------------------

void hx20_state::main_p2_w(uint8_t data)
{
	/*

	    bit     description

	    0
	    1       RS-232 TXD
	    2       serial select (0=peripheral, 1=slave 6301)
	    3
	    4       TX
	    5
	    6
	    7

	*/

	// RS-232
	m_rs232->write_txd(BIT(data, 1));

	// serial
	m_slave_sio = BIT(data, 2);

	if (m_slave_sio)
		m_slave_tx = BIT(data, 4);
	else
		m_sio->tx_w(BIT(data, 4));
}


//-------------------------------------------------
//  slave_p1_r - slave CPU port 1 read
//-------------------------------------------------

uint8_t hx20_state::slave_p1_r()
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4
	    5
	    6       printer reset pulse
	    7       printer timing pulse

	*/

	uint8_t data = 0;

	return data;
}


//-------------------------------------------------
//  slave_p1_w - slave CPU port 1 write
//-------------------------------------------------

void hx20_state::slave_p1_w(uint8_t data)
{
	/*

	    bit     description

	    0       printer head 1 (1=on)
	    1       printer head 2
	    2       printer head 3
	    3       printer head 4
	    4       printer motor (0=on)
	    5       speaker (1=on)
	    6
	    7

	*/

	// speaker
	m_speaker->level_w(BIT(data, 5));
}


//-------------------------------------------------
//  slave_p2_r - slave CPU port 2 read
//-------------------------------------------------

uint8_t hx20_state::slave_p2_r()
{
	/*

	    bit     description

	    0       RS-232 RXD / microcassette (0=read, 1=write)
	    1
	    2
	    3       TX
	    4
	    5
	    6
	    7

	*/

	uint8_t data = M6801_MODE_7;

	// serial
	data |= m_slave_tx << 3;

	return data;
}


//-------------------------------------------------
//  slave_p2_w - slave CPU port 2 write
//-------------------------------------------------

void hx20_state::slave_p2_w(uint8_t data)
{
	/*

	    bit     description

	    0
	    1       microcassette internal clock, write data
	    2       serial select (0=brake, 1=normal)
	    3
	    4       RX
	    5
	    6
	    7

	*/

	// serial
	m_slave_rx = BIT(data, 4);
}


//-------------------------------------------------
//  slave_p3_r - slave CPU port 3 read
//-------------------------------------------------

uint8_t hx20_state::slave_p3_r()
{
	/*

	    bit     description

	    0
	    1
	    2       external cassette read data
	    3
	    4
	    5
	    6
	    7

	*/

	uint8_t data = 0;

	return data;
}


//-------------------------------------------------
//  slave_p3_w - slave CPU port 3 write
//-------------------------------------------------

void hx20_state::slave_p3_w(uint8_t data)
{
	/*

	    bit     description

	    0       external cassette remote (0=on)
	    1       RS-232 RTS
	    2
	    3       external cassette write data
	    4       slave status flag
	    5       bar code power (1=on)
	    6       RS-232 power (1=on)
	    7       program power (1=on)

	*/

	// RS-232
	m_rs232->write_rts(BIT(data, 1));

	// main
	m_slave_flag = BIT(data, 4);
}


//-------------------------------------------------
//  slave_p4_r - slave CPU port 4 read
//-------------------------------------------------

uint8_t hx20_state::slave_p4_r()
{
	/*

	    bit     description

	    0       PLUG 2
	    1
	    2
	    3
	    4
	    5
	    6
	    7       RS-232 CD

	*/

	uint8_t data = 0;

	// RS-232
	data |= m_rs232->dcd_r() << 7;

	return data;
}


//-------------------------------------------------
//  slave_p4_w - slave CPU port 4 write
//-------------------------------------------------

void hx20_state::slave_p4_w(uint8_t data)
{
	/*

	    bit     description

	    0
	    1       port enable always on / printer motor control (0=open, 1=brake)
	    2       clear shift register / microcassette power switch (1=on)
	    3       ROM cartridge power switch (1=on) / microcassette command
	    4       ROM address counter clear / clock
	    5       cassette/RS-232 select (0=RS-232, 1=microcassette)
	    6       ROM cartridge select / microcassette clock (0=counter, 1=head switch)
	    7

	*/
}



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( hx20_mem )
//-------------------------------------------------

void hx20_state::hx20_mem(address_map &map)
{
	map(0x0020, 0x0020).w(FUNC(hx20_state::ksc_w));
	map(0x0022, 0x0022).r(FUNC(hx20_state::krtn07_r));
	map(0x0026, 0x0026).w(FUNC(hx20_state::lcd_cs_w));
	map(0x0028, 0x0028).r(FUNC(hx20_state::krtn89_r));
	map(0x002a, 0x002a).w(FUNC(hx20_state::lcd_data_w));
	map(0x002c, 0x002c); // mask interruption by using IC 8E in sleep mode
	map(0x0030, 0x0033); // switch memory banks (expansion unit)
	map(0x0040, 0x007f).rw(m_rtc, FUNC(mc146818_device::read_direct), FUNC(mc146818_device::write_direct));
	map(0x0100, 0x3fff).ram();
	map(0x6000, 0x7fff).rom().r(FUNC(hx20_state::optrom_r));
	map(0x8000, 0xffff).rom().region(HD6301V1_MAIN_TAG, 0);
}


//-------------------------------------------------
//  ADDRESS_MAP( cm6032_mem )
//-------------------------------------------------

void hx20_state::cm6032_mem(address_map &map)
{
	hx20_mem(map);
	map(0x4000, 0xffff).rom().region(HD6301V1_MAIN_TAG, 0);
}


//-------------------------------------------------
//  ADDRESS_MAP( cm6127_mem )
//-------------------------------------------------

void hx20_state::cm6127_mem(address_map &map)
{
	hx20_mem(map);
	map(0x4000, 0x7fff).ram();
	map(0x8000, 0xffff).rom().region(HD6301V1_MAIN_TAG, 0);
}


//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( hx20 )
//-------------------------------------------------

static INPUT_PORTS_START( hx20 )
	PORT_START("KSC0")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('_')
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PF1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_CUSTOM ) // SW6:1
	PORT_BIT( 0xfc00, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KSC1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PF2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_CUSTOM ) // SW6:2
	PORT_BIT( 0xfc00, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KSC2")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_CHAR('@') PORT_CHAR('^')
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PF3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_CUSTOM ) // SW6:3
	PORT_BIT( 0xfc00, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KSC3")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PF4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_CUSTOM ) // SW6:4
	PORT_BIT( 0xfc00, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KSC4")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PF5") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0xfc00, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KSC5")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT" " UTF8_UP) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT" " UTF8_DOWN) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PAPER FEED")
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0xfc00, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KSC6")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR('\r')
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("TAB") PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("NUM")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GRPH")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CAPS LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0xfc00, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KSC7")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("HOME CLR") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SCRN")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BREAK")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PAUSE")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INS DEL") PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("MENU")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PRTR ON/OFF")
	PORT_BIT( 0xfc00, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("SW6")
	PORT_DIPNAME( 0x07, 0x07, "Country" ) PORT_DIPLOCATION("SW6:1,2,3")
	PORT_DIPSETTING(    0x00, "Spain" )
	PORT_DIPSETTING(    0x01, "Italy" )
	PORT_DIPSETTING(    0x02, "Sweden" )
	PORT_DIPSETTING(    0x03, "Denmark" )
	PORT_DIPSETTING(    0x04, "England" )
	PORT_DIPSETTING(    0x05, "Germany" )
	PORT_DIPSETTING(    0x06, "France" )
	PORT_DIPSETTING(    0x07, "U.S.A." )
	PORT_DIPNAME( 0x08, 0x00, "Floppy Drive TF-20" ) PORT_DIPLOCATION("SW6:4")
	PORT_DIPSETTING(    0x00, "Installed" )
	PORT_DIPSETTING(    0x08, "Not Installed" )
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( hx20e )
//-------------------------------------------------

static INPUT_PORTS_START( hx20e )
	PORT_INCLUDE(hx20)

	PORT_MODIFY("SW6")
	PORT_DIPNAME( 0x07, 0x00, "Country" ) PORT_DIPLOCATION("SW6:1,2,3")
	PORT_DIPSETTING(    0x00, "Sweden (ASCII)" )
	PORT_DIPSETTING(    0x01, "Germany (ASCII)" )
	PORT_DIPSETTING(    0x02, "France (ASCII)" )
	PORT_DIPSETTING(    0x03, "Denmark" )
	PORT_DIPSETTING(    0x04, "Sweden" )
	PORT_DIPSETTING(    0x05, "Germany" )
	PORT_DIPSETTING(    0x06, "France" )
	PORT_DIPSETTING(    0x07, "Norway" )
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( cm6032 )
//-------------------------------------------------

static INPUT_PORTS_START( cm6032 )
	PORT_INCLUDE(hx20)

	PORT_MODIFY("KSC0")
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1 TOTALS") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_MODIFY("KSC1")
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2 DETAIL") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))

	PORT_MODIFY("KSC2")
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3 STATUS") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))

	PORT_MODIFY("KSC3")
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F4 AUTO") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))

	PORT_MODIFY("KSC4")
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F5 T/DATE") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))

	PORT_MODIFY("KSC7")
	PORT_BIT(0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ABORT")
	PORT_BIT(0x0020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("EXT FF")
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( cm6127 )
//-------------------------------------------------

static INPUT_PORTS_START( cm6127 )
	PORT_INCLUDE(hx20)

	PORT_MODIFY("KSC0")
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1 Checkout") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_MODIFY("KSC1")
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2 Enquiry") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))

	PORT_MODIFY("KSC2")
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3 Checkin") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))

	PORT_MODIFY("KSC3")
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F4 Audit") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))

	PORT_MODIFY("KSC4")
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F5 T/Date") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))

	PORT_MODIFY("KSC7")
	PORT_BIT(0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Abort")
	PORT_BIT(0x0008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Pause")
	PORT_BIT(0x0020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ext FF")
INPUT_PORTS_END


//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  mc146818_interface rtc_intf
//-------------------------------------------------

void hx20_state::rtc_irq_w(int state)
{
	m_rtc_irq = state;

	update_interrupt();
}



//**************************************************************************
//  VIDEO
//**************************************************************************

void hx20_state::hx20_palette(palette_device &palette) const
{
	palette.set_pen_color(0, 0xa5, 0xad, 0xa5);
	palette.set_pen_color(1, 0x31, 0x39, 0x10);
}


//-------------------------------------------------
//  SCREEN_UPDATE( hx20 )
//-------------------------------------------------

uint32_t hx20_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (auto &lcdc : m_lcdc)
		lcdc->screen_update(screen, bitmap, cliprect);

	return 0;
}


//**************************************************************************
//  OPTIONAL ROMS
//**************************************************************************

DEVICE_IMAGE_LOAD_MEMBER(hx20_state::optrom_load)
{
	uint32_t size = m_optrom->common_get_size("rom");

	if (size != 0x2000)
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported ROM size (must be 8K)");

	m_optrom->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_optrom->common_load_rom(m_optrom->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

uint8_t hx20_state::optrom_r(offs_t offset)
{
	if (m_optrom->exists())
		return m_optrom->read_rom(offset);
	else
		return 0;
}


//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( hx20 )
//-------------------------------------------------

void hx20_state::machine_start()
{
	// state saving
	save_item(NAME(m_slave_rx));
	save_item(NAME(m_slave_tx));
	save_item(NAME(m_slave_flag));
	save_item(NAME(m_ksc));
	save_item(NAME(m_kbrequest));
	save_item(NAME(m_lcd_data));
}



//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  machine_config( hx20 )
//-------------------------------------------------

void hx20_state::hx20(machine_config &config)
{
	// basic machine hardware
	HD6303R(config, m_maincpu, 2.4576_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &hx20_state::hx20_mem);
	m_maincpu->in_p1_cb().set(FUNC(hx20_state::main_p1_r));
	m_maincpu->out_p1_cb().set(FUNC(hx20_state::main_p1_w));
	m_maincpu->in_p2_cb().set(FUNC(hx20_state::main_p2_r));
	m_maincpu->out_p2_cb().set(FUNC(hx20_state::main_p2_w));
	// Port 3 = A0-A7, D0-D7
	// Port 4 = A8-A15

	HD6301V1(config, m_subcpu, 2.4576_MHz_XTAL);
	m_subcpu->in_p1_cb().set(FUNC(hx20_state::slave_p1_r));
	m_subcpu->out_p1_cb().set(FUNC(hx20_state::slave_p1_w));
	m_subcpu->in_p2_cb().set(FUNC(hx20_state::slave_p2_r));
	m_subcpu->out_p2_cb().set(FUNC(hx20_state::slave_p2_w));
	m_subcpu->in_p3_cb().set(FUNC(hx20_state::slave_p3_r));
	m_subcpu->out_p3_cb().set(FUNC(hx20_state::slave_p3_w));
	m_subcpu->in_p4_cb().set(FUNC(hx20_state::slave_p4_r));
	m_subcpu->out_p4_cb().set(FUNC(hx20_state::slave_p4_w));

	// video hardware
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.set_size(120, 32);
	screen.set_visarea(0, 120-1, 0, 32-1);
	screen.set_screen_update(FUNC(hx20_state::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(hx20_state::hx20_palette), 2);

	for (int y = 0, i = 0; 2 > y; ++y)
		for (int x = 0; 3 > x; ++x)
			UPD7227(config, m_lcdc[i++], x * 40, y * 16);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	// devices
	MC146818(config, m_rtc, 4.194304_MHz_XTAL);
	m_rtc->irq().set(FUNC(hx20_state::rtc_irq_w));

	RS232_PORT(config, RS232_TAG, default_rs232_devices, nullptr);
	CASSETTE(config, m_cassette);

	EPSON_SIO(config, m_sio, "tf20");
	m_sio->rx_callback().set(FUNC(hx20_state::sio_rx_w));
	m_sio->pin_callback().set(FUNC(hx20_state::sio_pin_w));

	// internal ram
	RAM(config, RAM_TAG).set_default_size("16K").set_extra_options("32K");

	// optional rom
	GENERIC_SOCKET(config, m_optrom, generic_plain_slot, "opt_rom", "bin,rom");
	m_optrom->set_device_load(FUNC(hx20_state::optrom_load));

	// software lists
	SOFTWARE_LIST(config, "hx20_opt_list").set_original("hx20_rom");
	SOFTWARE_LIST(config, "epson_cpm_list").set_original("epson_cpm");
}


//-------------------------------------------------
//  machine_config( cm6032 )
//-------------------------------------------------

void hx20_state::cm6032(machine_config &config)
{
	hx20(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &hx20_state::cm6032_mem);

	// optional rom
	config.device_remove("optrom");

	// software lists
	config.device_remove("epson_cpm_list");
	config.device_remove("hx20_opt_list");
}


//-------------------------------------------------
//  machine_config( cm6127 )
//-------------------------------------------------

void hx20_state::cm6127(machine_config &config)
{
	cm6032(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &hx20_state::cm6127_mem);
}


//-------------------------------------------------
//  ROM( ehx20 )
//-------------------------------------------------

ROM_START( ehx20 )
	ROM_REGION( 0x8000, HD6301V1_MAIN_TAG, ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS( "v11" )
	ROM_SYSTEM_BIOS( 0, "v10", "version 1.0" )
	ROMX_LOAD( "hx20_v10.12e", 0x6000, 0x2000, CRC(ed7482c6) SHA1(8fba63037f2418aee9e933a353b052a5ed816ead), ROM_BIOS(0) )
	ROMX_LOAD( "hx20_v10.13e", 0x4000, 0x2000, CRC(f5cc8868) SHA1(3248a1ddf0d8df7e9f2fe96955385218d760c4ad), ROM_BIOS(0) )
	ROMX_LOAD( "hx20_v10.14e", 0x2000, 0x2000, CRC(27d743ed) SHA1(ebae367b0fa5f42ac78424df2534312296fd6fdc), ROM_BIOS(0) )
	ROMX_LOAD( "hx20_v10.15e", 0x0000, 0x2000, CRC(33fbb1ab) SHA1(292ace94b4dad267aa7786dc64e68ac6f3c98aa7), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v11", "version 1.1" )
	ROMX_LOAD( "hx20_v11.12e", 0x0000, 0x2000, CRC(4de0b4b6) SHA1(f15c537824b7effde9d9b9a21e92a081fb089371), ROM_BIOS(1) )
	ROMX_LOAD( "hx20_v11.13e", 0x2000, 0x2000, CRC(10d6ae76) SHA1(3163954ed9981f70f590ee98bcc8e19e4be6527a), ROM_BIOS(1) )
	ROMX_LOAD( "hx20_v11.14e", 0x4000, 0x2000, CRC(26c203a1) SHA1(b282d7233b2689820fcf718dbe1e93d623b67e4f), ROM_BIOS(1) )
	ROMX_LOAD( "hx20_v11.15e", 0x6000, 0x2000, CRC(101cb3e8) SHA1(e0b5cf107a9387e34a0e46f54328b89696c0bdc5), ROM_BIOS(1) )

	ROM_REGION( 0x1000, HD6301V1_SLAVE_TAG, 0 )
	ROM_LOAD( "hd6301v1.6d", 0x0000, 0x1000, CRC(b36f5b99) SHA1(c6b54163bb268e4f4f5c79aa2e83ec51f775b16a) )
ROM_END


//-------------------------------------------------
//  ROM( ehx20e )
//-------------------------------------------------

ROM_START( ehx20e )
	ROM_REGION( 0x8000, HD6301V1_MAIN_TAG, ROMREGION_ERASEFF )
	ROM_LOAD( "hx20_v11e.12e", 0x0000, 0x2000, CRC(4de0b4b6) SHA1(f15c537824b7effde9d9b9a21e92a081fb089371) )
	ROM_LOAD( "hx20_v11e.13e", 0x2000, 0x2000, CRC(10d6ae76) SHA1(3163954ed9981f70f590ee98bcc8e19e4be6527a) )
	ROM_LOAD( "hx20_v11e.14e", 0x4000, 0x2000, CRC(26c203a1) SHA1(b282d7233b2689820fcf718dbe1e93d623b67e4f) )
	ROM_LOAD( "hx20_v11e.15e", 0x6000, 0x2000, CRC(fd339aa5) SHA1(860c3579c45e96c5e6a877f4fbe77abacb0d674e) )

	ROM_REGION( 0x1000, HD6301V1_SLAVE_TAG, 0 )
	ROM_LOAD( "hd6301v1.6d", 0x0000, 0x1000, CRC(b36f5b99) SHA1(c6b54163bb268e4f4f5c79aa2e83ec51f775b16a) )
ROM_END


//-------------------------------------------------
//  ROM( ecm6032 )
//-------------------------------------------------

ROM_START( ecm6032 )
	// 1988 MOSU 20120200004 H:::
	ROM_REGION( 0xc000, HD6301V1_MAIN_TAG, ROMREGION_ERASEFF )
	ROM_LOAD( "cm6032a-1_v43.11e", 0x0000, 0x4000, CRC(124797c2) SHA1(33d3418c99eb2d557151996bc09debf4ca089298) )
	ROM_LOAD( "cm6032a-2_v43.13e", 0x4000, 0x4000, CRC(b136abec) SHA1(594862e058e553826209f86e9ebbb81f29017469) )
	ROM_LOAD( "cm6032a-3_v43.15e", 0x8000, 0x4000, CRC(95c34bdc) SHA1(c7022145f37e9fd2f339f8e7ad3adce76a67ca0b) )

	ROM_REGION( 0x1000, HD6301V1_SLAVE_TAG, 0 )
	ROM_LOAD( "hd6301v1.6d", 0x0000, 0x1000, CRC(b36f5b99) SHA1(c6b54163bb268e4f4f5c79aa2e83ec51f775b16a) )
ROM_END


//-------------------------------------------------
//  ROM( ecm6127 )
//-------------------------------------------------

ROM_START( ecm6127 )
	ROM_REGION( 0x8000, HD6301V1_MAIN_TAG, ROMREGION_ERASEFF )
	ROM_LOAD( "cm6127h-1_v16.13e", 0x0000, 0x4000, CRC(80f08fb8) SHA1(f1a1b38ce0aff25e6915fb7092d9158f13fd4108) )
	ROM_LOAD( "cm6127h-2_v16.15e", 0x4000, 0x4000, CRC(2c59851e) SHA1(2b031e958497b87601bbdda15f5e974cfd931ed9) )
	ROM_REGION( 0x8000, "ext", ROMREGION_ERASEFF )
	ROM_LOAD( "cm6127h-3_v16.13b", 0x0000, 0x4000, CRC(0dbb1f51) SHA1(80aa442a42f04f661de01b8b64a6dde72751c851) ) // Expansion Unit
	ROM_LOAD( "cm6127h-4_v16.14b", 0x4000, 0x4000, CRC(348cdd4b) SHA1(88c88b40d83e3a7cc93d2db684bc70cf78aaa180) ) // Expansion Unit

	ROM_REGION( 0x1000, HD6301V1_SLAVE_TAG, 0 )
	ROM_LOAD( "hd6301v1.6d", 0x0000, 0x1000, CRC(b36f5b99) SHA1(c6b54163bb268e4f4f5c79aa2e83ec51f775b16a) )
ROM_END


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS       INIT        COMPANY  FULLNAME                FLAGS
COMP( 1983, ehx20,   0,      0,      hx20,    hx20,   hx20_state, empty_init, "Epson", "Epson HX-20",          MACHINE_NOT_WORKING )
COMP( 1983, ehx20e,  ehx20,  0,      hx20,    hx20e,  hx20_state, empty_init, "Epson", "Epson HX-20 (Europe)", MACHINE_NOT_WORKING )
COMP( 1989, ecm6032, ehx20,  0,      cm6032,  cm6032, hx20_state, empty_init, "Epson", "Epson CM6032",         MACHINE_NOT_WORKING )
COMP( 1993, ecm6127, ehx20,  0,      cm6127,  cm6127, hx20_state, empty_init, "Epson", "Epson CM6127",         MACHINE_NOT_WORKING )



i7000.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders: Felipe Sanches
/***************************************************************************

    Itautec I7000

    driver by Felipe C. da S. Sanches <juca@members.fsf.org>
    with tech info provided by Alexandre Souza (a.k.a. Tabajara).

    The portuguese Wikipedia article available at
    http://pt.wikipedia.org/wiki/Itautec_I-7000
    also provides a technical overview of this machine:

    The I-7000 was the first computer manufactured by Itautec
    (http://www.itautec.com.br/pt-br/produtos). It was originally an 8 bit CP/M
    computer that became an IBM PC-XT clone in later hardware revisions which
    took the "I-7000 PC-XT" name.

    * Released in 1982
    * Operating System: SIM/M / BASIC
    * CPU: National NSC800 D-4 at 4,00 MHz
    * Memory: 64KB to 128KB
    * keyboards: 80 keys (with a reduced numerical keypad and function keys)
    * display:
     - 40 X 25 text
     - 80 X 25 text
     - 160 X 100 (8 colors)
     - 640 X 200 (monochrome, with an expansion board)
     - 320 X 200 (16 colors, with an expansion board)
    * Expansion slots:
     - 1 frontal cart slot
     - 4 internal expansion slots
    * Ports:
     - 1 composite video output for a color monitor
     - 2 cassette interfaces
     - 1 RS-232C serial port
     - 1 parallel interface
    * Storage:
     - Cassette recorder
     - Up to 4 external floppy drives: 8" (FD/DD, 1,1MB) or 5" 1/4
     - Up to 1 external 10 MB hard-drive

****************************************************************************/

#include "emu.h"
#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/z80/nsc800.h"
#include "machine/i8279.h"
#include "machine/pit8253.h"
#include "sound/spkrdev.h"
#include "video/mc6845.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "tilemap.h"


namespace {

class i7000_state : public driver_device
{
public:
	i7000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_card(*this, "cardslot")
		, m_gfxdecode(*this, "gfxdecode")
		, m_videoram(*this, "videoram")
		, m_kbd(*this, "X%u", 0U)
	{ }

	void i7000(machine_config &config);

protected:
	virtual void video_start() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<generic_slot_device> m_card;
	required_device<gfxdecode_device> m_gfxdecode;
	required_shared_ptr<uint8_t> m_videoram;
	required_ioport_array<8> m_kbd;

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	uint8_t m_row = 0;
	tilemap_t *m_bg_tilemap = nullptr;

	TILE_GET_INFO_MEMBER(get_bg_tile_info);
	MC6845_ON_UPDATE_ADDR_CHANGED(crtc_addr);
	void i7000_palette(palette_device &palette) const;
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(card_load);

	uint8_t kbd_r();
	void scanlines_w(uint8_t data);

	void i7000_io(address_map &map) ATTR_COLD;
	void i7000_mem(address_map &map) ATTR_COLD;
};

void i7000_state::scanlines_w(uint8_t data)
{
	m_row = data;
}

uint8_t i7000_state::kbd_r()
{
	for (int i=0; i<40*25; i++){
		m_bg_tilemap->mark_tile_dirty(i);
	}

	return m_kbd[m_row & 7]->read();
}

/* Input ports */
static INPUT_PORTS_START( i7000 )
	PORT_START("X0")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("!") PORT_CHAR('!')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER)

	PORT_START("X1")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0x9D")
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0x8F")
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^R DC2") //0x12

	PORT_START("X2")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("$ ^") PORT_CHAR('$') PORT_CHAR('^')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0xA0")
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("- _") PORT_CODE(KEYCODE_MINUS)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BACKSPACE") PORT_CODE(KEYCODE_BACKSPACE)

	PORT_START("X3")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("% +") PORT_CHAR('%') PORT_CHAR('+')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0x9C")
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@") PORT_CHAR('@')

	PORT_START("X4")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 *") PORT_CODE(KEYCODE_5)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^U NAK") //0x15
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("| <") PORT_CHAR('<')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(", ;") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR(';')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^E ENQ") //0x05

	PORT_START("X5")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^O SI") //0x0F
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("#") PORT_CHAR('#')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("{") PORT_CHAR('{')

	PORT_START("X6")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^L FF") //0x0C
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^T DC4") //0x14

	PORT_START("X7")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(">") PORT_CHAR('>')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACEBAR") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("}") PORT_CHAR('}')

	PORT_START("DSW") /* DP01 */
		PORT_DIPNAME( 0x80, 0x80, "1")
		PORT_DIPSETTING(    0x00, DEF_STR( No ) )
		PORT_DIPSETTING(    0x80, DEF_STR( Yes ) )
		PORT_DIPNAME( 0x40, 0x40, "2")
		PORT_DIPSETTING(    0x00, DEF_STR( No ) )
		PORT_DIPSETTING(    0x40, DEF_STR( Yes ) )
		PORT_DIPNAME( 0x20, 0x00, "3")
		PORT_DIPSETTING(    0x00, DEF_STR( No ) )
		PORT_DIPSETTING(    0x20, DEF_STR( Yes ) )
		PORT_DIPNAME( 0x10, 0x10, "4")
		PORT_DIPSETTING(    0x00, DEF_STR( No ) )
		PORT_DIPSETTING(    0x10, DEF_STR( Yes ) )
		PORT_DIPNAME( 0x08, 0x08, "5")
		PORT_DIPSETTING(    0x00, DEF_STR( No ) )
		PORT_DIPSETTING(    0x08, DEF_STR( Yes ) )
		PORT_DIPNAME( 0x04, 0x04, "6")
		PORT_DIPSETTING(    0x00, DEF_STR( No ) )
		PORT_DIPSETTING(    0x04, DEF_STR( Yes ) )
		PORT_DIPNAME( 0x02, 0x00, "7")
		PORT_DIPSETTING(    0x00, DEF_STR( No ) )
		PORT_DIPSETTING(    0x02, DEF_STR( Yes ) )
		PORT_DIPNAME( 0x01, 0x01, "8")
		PORT_DIPSETTING(    0x00, DEF_STR( No ) )
		PORT_DIPSETTING(    0x01, DEF_STR( Yes ) )
INPUT_PORTS_END


void i7000_state::machine_start()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	if (m_card->exists())
	{
		// 0x4000 - 0xbfff   32KB ROM
		program.install_read_handler(0x4000, 0xbfff, read8sm_delegate(*m_card, FUNC(generic_slot_device::read_rom)));
	}
}

void i7000_state::i7000_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(0x33, 0x33, 0x33));
	palette.set_pen_color(1, rgb_t(0xBB, 0xBB, 0xBB));
}

/*FIXME: we still need to figure out the proper memory map
         for the maincpu and where the cartridge slot maps to. */
void i7000_state::i7000_mem(address_map &map)
{
	map(0x0000, 0x0fff).rom().region("boot", 0);
	map(0x2000, 0x2fff).ram().share("videoram");
	map(0x4000, 0xffff).ram();
//  map(0x4000, 0xbfff).rom().region("cardslot", 0);
}

void i7000_state::i7000_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
//  map(0x06, 0x06).w(FUNC(i7000_state::i7000_io_?_w));
//  map(0x08, 0x09).w(FUNC(i7000_state::i7000_io_?_w)); //printer perhaps?
//  map(0x0c, 0x0c).w(FUNC(i7000_state::i7000_io_?_w)); //0x0C and 0x10 may be related to mem page swapping. (self-test "4. PAG")
//  map(0x10, 0x10).w(FUNC(i7000_state::i7000_io_?_w));
//  map(0x14, 0x15).w(FUNC(i7000_state::i7000_io_?_w));

	map(0x18, 0x1b).rw("pit8253", FUNC(pit8253_device::read), FUNC(pit8253_device::write));

//  map(0x1c, 0x1c).w(FUNC(i7000_state::i7000_io_printer_data_w)); //ASCII data
	map(0x1d, 0x1d).portr("DSW");
//  map(0x1e, 0x1e).rw(FUNC(i7000_state::i7000_io_printer_status_r), FUNC(i7000_state::i7000_io_?_w));
//  map(0x1f, 0x1f).w(FUNC(i7000_state::i7000_io_printer_strobe_w)); //self-test routine writes 0x08 and 0x09 (it seems that bit 0 is the strobe and bit 3 is an enable signal)
//  map(0x20, 0x21).rw(FUNC(i7000_state::i7000_io_keyboard_r), FUNC(i7000_state::i7000_io_keyboard_w));

	map(0x20, 0x21).rw("i8279", FUNC(i8279_device::read), FUNC(i8279_device::write));

//  map(0x24, 0x24).r(FUNC(i7000_state::i7000_io_?_r));
//  map(0x25, 0x25).w(FUNC(i7000_state::i7000_io_?_w));

//  map(0x28, 0x2d).rw(FUNC(i7000_state::i7000_io_joystick_r), FUNC(i7000_state::i7000_io_joystick_w));

//  map(0x3b, 0x3b).w(FUNC(i7000_state::i7000_io_?_w));
//  map(0x66, 0x67).w(FUNC(i7000_state::i7000_io_?_w));
//  map(0xbb, 0xbb).w(FUNC(i7000_state::i7000_io_?_w)); //may be related to page-swapping...
}

DEVICE_IMAGE_LOAD_MEMBER(i7000_state::card_load)
{
	uint32_t const size = m_card->common_get_size("rom");

	m_card->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_BIG);
	m_card->common_load_rom(m_card->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

static const gfx_layout i7000_charlayout =
{
	8, 8,                   // 8 x 8 characters
	256,                    // 256 characters
	1,                      // 1 bits per pixel
	{ 0 },                  // no bitplanes
	// x offsets
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	// y offsets
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                     // every char takes 8 bytes
};

static GFXDECODE_START( gfx_i7000 )
	GFXDECODE_ENTRY( "gfx1", 0x0000, i7000_charlayout, 0, 8 )
GFXDECODE_END

/****************************
* Video/Character functions *
****************************/

TILE_GET_INFO_MEMBER(i7000_state::get_bg_tile_info)
{
	tileinfo.set(0, /*code:*/ m_videoram[tile_index], /*color:*/ 0, 0);
}

void i7000_state::video_start()
{
	m_bg_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(i7000_state::get_bg_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 40, 25);
}

uint32_t i7000_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	m_bg_tilemap->draw(screen, bitmap, cliprect, 0, 0);
	return 0;
}

/* ROCKWELL 6545 - Transparent Memory Addressing */
MC6845_ON_UPDATE_ADDR_CHANGED(i7000_state::crtc_addr)
{
	/* What is this mandatory function meant to do ? */
}


void i7000_state::i7000(machine_config &config)
{
	/* basic machine hardware */
	NSC800(config, m_maincpu, XTAL(4'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &i7000_state::i7000_mem);
	m_maincpu->set_addrmap(AS_IO, &i7000_state::i7000_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_size(320, 200); /* 40x25 8x8 chars */
	screen.set_visarea(0, 320-1, 0, 200-1);
	screen.set_screen_update(FUNC(i7000_state::screen_update));
	screen.set_palette("palette");

	GFXDECODE(config, m_gfxdecode, "palette", gfx_i7000);
	PALETTE(config, "palette", FUNC(i7000_state::i7000_palette), 2);

	r6545_1_device &crtc(R6545_1(config, "crtc", XTAL(20'000'000))); /* (?) */
	crtc.set_screen("screen");
	crtc.set_show_border_area(true);
	crtc.set_char_width(8);
	crtc.set_on_update_addr_change_callback(FUNC(i7000_state::crtc_addr));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	/* Programmable timer */
	pit8253_device &pit8253(PIT8253(config, "pit8253", 0));
//  pit8253.set_clk<0>(XTAL(4'000'000) / 2); /* TODO: verify on PCB */
//  pit8253.out_handler<0>().set(FUNC(i7000_state::i7000_pit_out0));
//  pit8253.set_clk<1>(XTAL(4'000'000) / 2); /* TODO: verify on PCB */
//  pit8253.out_handler<1>().set(FUNC(i7000_state::i7000_pit_out1));
	pit8253.set_clk<2>(XTAL(4'000'000) / 2); /* TODO: verify on PCB */
	pit8253.out_handler<2>().set("speaker", FUNC(speaker_sound_device::level_w));

	/* Keyboard interface */
	i8279_device &kbdc(I8279(config, "i8279", 4000000)); /* guessed value. TODO: verify on PCB */
	kbdc.out_sl_callback().set(FUNC(i7000_state::scanlines_w));         // scan SL lines
	kbdc.in_rl_callback().set(FUNC(i7000_state::kbd_r));                // kbd RL lines
	kbdc.in_shift_callback().set_constant(1);                           // TODO: Shift key
	kbdc.in_ctrl_callback().set_constant(1);                            // TODO: Ctrl key

	/* Cartridge slot */
	GENERIC_CARTSLOT(config, "cardslot", generic_romram_plain_slot, "i7000_card", "rom").set_device_load(FUNC(i7000_state::card_load));

	/* Software lists */
	SOFTWARE_LIST(config, "card_list").set_original("i7000_card");
}

ROM_START( i7000 )
	ROM_REGION( 0x1000, "boot", 0 )
	ROM_LOAD( "i7000_boot_v1_4r02_15_10_85_d52d.rom",  0x0000, 0x1000, CRC(622412e5) SHA1(bf187a095600fd46a739c35132a85b5f39b2f867) )

	ROM_REGION( 0x0800, "gfx1", 0 )
	ROM_LOAD( "i7000_chargen.rom", 0x0000, 0x0800, CRC(fb7383e9) SHA1(71a6561bb9ff3cbf74711fa7ab445f9b43f15626) )
		/*
		The character generator ROM originally dumped had
		some corrupt data that was manually fixed:

		ROM address | Originally dumped value | Manually fixed value | Comment
		    0x06A2 |                    0xF7 | 0xFE                 | This is a fix to the upper right portion of a 2x2 tile image of a big filled circle
		    0x06A3 |                    0xF7 | 0xFE                 | This is another fix to the same tile (character value: 0xD4)

		Also, characters 0x05, 0x06, 0x07 and 0x08
		as well as lowercase 'x' (0x78), uppercase 'Y' (0x59)
		may contain corrupt data, but we can't be sure,
		unless we find another Itautec I7000 computer and
		redump it's ROMs to double-check.
	*/

	ROM_REGION( 0x1000, "drive", 0 )
	ROM_LOAD( "i7000_drive_ci01.rom", 0x0000, 0x1000, CRC(d8d6e5c1) SHA1(93e7db42fbfaa8243973321c7fc8c51ed80780be) )

	ROM_REGION( 0x1000, "telex", 0 )
	ROM_LOAD( "i7000_telex_ci09.rom", 0x0000, 0x1000, CRC(c1c8fcc8) SHA1(cbf5fb600e587b998f190a9e3fb398a51d8a5e87) )
ROM_END

} // anonymous namespace


//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY    FULLNAME  FLAGS
COMP( 1982, i7000, 0,      0,      i7000,   i7000, i7000_state, empty_init, "Itautec", "I-7000", MACHINE_NOT_WORKING)



ibm3153.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Robbbert
/***************************************************************************

IBM 3153 Terminal.

2016-05-04 Skeleton driver.

A green-screen terminal with a beeper.
Chip complement:
U1    K6T0808C10-DB70 (32k static ram)
U2    D-80C32-16 (cpu)
U3    DM74LS373N
U5    LM339N
U6    DM74LS125AN
U7    K6T0808C10-DB70 (ram)
U8    K6T0808C10-DB70 (ram)
U9    598-0013040 6491 3.19 (boot rom)
U10   DS1488N
U11   74LS377N
U12   DS1489AN
U13   LSI VICTOR 006-9802760 REV B WDB36003 Y9936 (video processor)
U14   74F00PC
U16   DM74LS125AN
U17   DS1488N
U18   DS1489AN
U25   SN74F04N
U100  74F07N
Crystals:
Y1    16.000 MHz
Y2    65.089 MHz
Y3    44.976 MHz


ToDo:
- Everything!

****************************************************************************/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "emupal.h"
#include "screen.h"


namespace {

class ibm3153_state : public driver_device
{
public:
	ibm3153_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_p_chargen(*this, "chargen")
		{ }

	void ibm3153(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	void ibm3153_palette(palette_device &palette) const;
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_p_chargen;
};


void ibm3153_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x0ffff).rom().region("user1", 0);
}

void ibm3153_state::io_map(address_map &map)
{
	map(0x0000, 0xffff).ram();
	//map.unmap_value_high();
	//map.global_mask(0xff);
}


/* Input ports */
static INPUT_PORTS_START( ibm3153 )
INPUT_PORTS_END

uint32_t ibm3153_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void ibm3153_state::ibm3153_palette(palette_device &palette) const
{
	palette.set_pen_color(0, 0,   0, 0); // Black
	palette.set_pen_color(1, 0, 255, 0); // Full
	palette.set_pen_color(2, 0, 128, 0); // Dimmed
}

void ibm3153_state::machine_reset()
{
}

void ibm3153_state::ibm3153(machine_config &config)
{
	/* basic machine hardware */
	I80C32(config, m_maincpu, XTAL(16'000'000)); // no idea of clock
	m_maincpu->set_addrmap(AS_PROGRAM, &ibm3153_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &ibm3153_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(ibm3153_state::screen_update));
	screen.set_size(640, 240);
	screen.set_visarea(0, 639, 0, 239);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(ibm3153_state::ibm3153_palette), 3);
}

/* ROM definition */
ROM_START( ibm3153 )
	ROM_REGION( 0x40000, "user1", 0 )
	ROM_LOAD("598-0013040_6491_3.19.u9", 0x0000, 0x040000, CRC(7092d690) SHA1(a23a5bd5eae90e9b31fa32ef4be1258612eaaa0a) )

	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "char.bin", 0x0000, 0x2000, NO_DUMP ) // probably inside the video processor
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR   NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY  FULLNAME             FLAGS
COMP( 1999?, ibm3153, 0,      0,      ibm3153, ibm3153, ibm3153_state, empty_init, "IBM",   "IBM 3153 Terminal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



ibm3477.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/*******************************************************************************

Skeleton driver for IBM InfoWindow 3477 terminal.

                      ______________          ________
   ___________________|             |_________|       |_______________________
  |                   |_____________|         |_______|                      |
  |  BATTERY            ______     _____                                     |
  |                    |_____|    |____|                                     |
  |                             ____________    ___   ___   ___   ___        |
  |         _________          |38F5768    |   | <-SN74LS74AN |  |  |        |
  |        CXK58257M-10LL      |TC110G17AF |   |  |  |  <-SN75112N  |        |
  |                            |           |   |  |  |  |  |  |  | <-SN7510BN|
  |    ____   ____             |           |   |__|  |__|  |__|  |__|        |
  |   |___|  |___|             |___________|              _________     ____ |
  |____                                                CXK58257M-10LL  |___| |
      |        ______   ______   _________          ____________             |
      |  ___  |     |  |     |  |AMD     |         |38F5769    |  Xtal       |
      | |  |  |EPROM|  |EPROM|  |N8088-2 |         |TC110G26AF |  59.8185 MHz|
      | |  |  |     |  |     |  |        |         |           |             |
      | |  |  |     |  |     |  |________|         |           |             |
   ___| |__|  |     |  |     |                     |___________|             |
 _|__         |_____|  |_____|                                   ____        |
|   |                                                      10124N-> |        |
|___|         ||||||||||||||||                                   |  |        |
  |                                                              |__|        |
  |                            _________________    ___               ___    |
  |___________________________|                |_|_|  |               |  |___|
                                                      |_|_|_|_|_|_|_|_|

ToDo:
- Everything!

********************************************************************************/

#include "emu.h"
#include "cpu/i86/i86.h"
#include "emupal.h"
#include "screen.h"


namespace {

class ibm3477_state : public driver_device
{
public:
	ibm3477_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void ibm3477(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	void palette_init(palette_device &palette) const;
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	required_device<cpu_device> m_maincpu;
};


// Input ports
static INPUT_PORTS_START( ibm3477 )
INPUT_PORTS_END

uint32_t ibm3477_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void ibm3477_state::palette_init(palette_device &palette) const
{
	palette.set_pen_color(0, 0,   0, 0); // Black
	palette.set_pen_color(1, 0, 255, 0); // Full
	palette.set_pen_color(2, 0, 128, 0); // Dimmed
}

void ibm3477_state::machine_reset()
{
}

void ibm3477_state::ibm3477(machine_config &config)
{
	// Basic machine hardware
	I8088(config, m_maincpu, 59'818'500 / 10); // Unknown clock

	// Video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // Not accurate
	screen.set_screen_update(FUNC(ibm3477_state::screen_update));
	screen.set_size(640, 240);
	screen.set_visarea(0, 639, 0, 239);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(ibm3477_state::palette_init), 3);
}

// ROM definition
ROM_START( ibm3477 )
	ROM_REGION( 0x40000, "maincpu", 0 )
	ROM_LOAD("pn_38f5843_ibm_30-10-89.zm12", 0x00000, 0x20000, CRC(b07efb93) SHA1(84d245d17828bc4e096e0cae2d066f2209f56a8f) )
	ROM_LOAD("pn_38f5844_ibm_20-10-89.zm13", 0x20000, 0x20000, CRC(05f3c0f6) SHA1(f99096a2f3e97fe1eb540db3f3e620ddccf71bbc) )
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY  FULLNAME           FLAGS
COMP( 1998, ibm3477, 0,      0,      ibm3477, ibm3477, ibm3477_state, empty_init, "IBM",   "InfoWindow 3477", MACHINE_NO_SOUND | MACHINE_NOT_WORKING) // Spanish ROMs?



ibm5100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * IBM 5100.
 *
 * Sources:
 *  - IBM 5100 Maintenance Information Manual, SY31-0405-3, Fourth Edition (October 1979), International Business Machines Corporation
 *
 * TODO:
 *  - display registers
 *  - device address f
 *  - tape controller
 *  - disk controller
 *  - printer
 *  - communications cards
 *  - expansion feature
 *  - feature ROS (K4)
 */

#include "emu.h"

#include "ibm5100_kbd.h"

#include "cpu/palm/palm.h"
#include "sound/beep.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

//#define VERBOSE (LOG_GENERAL)
#include "logmacro.h"

namespace {

class ibm5100_state : public driver_device
{
public:
	ibm5100_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_screen(*this, "screen")
		, m_kbd(*this, "kbd")
		, m_nxr(*this, { "common", "basic", "apl" })
		, m_cgr(*this, "cgr")
		, m_pgm(*this, "pgm")
		, m_ros(*this, "ros")
		, m_conf(*this, "CONF")
		, m_disp(*this, "DISP")
		, m_lang(*this, "LANG")
	{
	}

	void common(machine_config &config);
	void ibm5100(machine_config &config);

protected:
	// driver_device implementation
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual void cpu_pgm_map(address_map &map) ATTR_COLD;
	virtual void cpu_rws_map(address_map &map) ATTR_COLD;
	virtual void cpu_ioc_map(address_map &map) ATTR_COLD;
	virtual void cpu_iod_map(address_map &map) ATTR_COLD;

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect);

	virtual void da0_ctl_w(u8 data);

	// non-executable ROS control
	u8 nxr_sts_r();
	void nxr_ctl_w(u8 data);
	virtual u8 nxr_r();
	virtual void nxr_w(u8 data);

	// keyboard
	u8 kbd_sts_r();
	void kbd_ctl_w(u8 data);

	void daf_ctl_w(u8 data);

	required_device<palm_device> m_cpu;
	required_device<screen_device> m_screen;
	required_device<ibm5100_keyboard_device> m_kbd;

	required_region_ptr_array<u16, 3> m_nxr;
	required_region_ptr<u8> m_cgr; // character generator ROS

	memory_view m_pgm;
	memory_view m_ros;

	required_ioport m_conf;
	required_ioport m_disp;
	required_ioport m_lang;

	u8 m_getb_bus;

	u8 m_nxr_ff; // non-executable ROS control flip-flops
	u8 m_bio_ff; // base I/O card flip-flops
	u16 m_nxr_address;

	std::unique_ptr<u16[]> m_rws;
};

class ibm5110_state : public ibm5100_state
{
public:
	ibm5110_state(machine_config const &mconfig, device_type type, char const *tag)
		: ibm5100_state(mconfig, type, tag)
		, m_alarm(*this, "alarm")
		, m_jmp(*this, { "L2_1", "L2_2", "K4" })
	{
	}

	void ibm5110(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual void cpu_pgm_map(address_map &map) override ATTR_COLD;
	virtual void cpu_ioc_map(address_map &map) override ATTR_COLD;

	virtual void da0_ctl_w(u8 data) override;

	// executable ROS control
	u8 exr_sts_r();
	void exr_ctl_w(u8 data);

	// non-executable ROS control
	virtual u8 nxr_r() override;
	virtual void nxr_w(u8 data) override;

private:
	required_device<beep_device> m_alarm;

	required_ioport_array<3> m_jmp;

	u8 m_exr_ff; // executable ROS flip-flops
};

enum nxr_ff_mask : u8
{
	NXR_RS     = 0x03, // ROS select
	NXR_PS     = 0x04, // put strobe
	NXR_B0     = 0x08, // data address bit 0

	NXR_COMMON = 0x00,
	NXR_BASIC  = 0x01,
	NXR_APL    = 0x02,
};

enum bio_ff_mask : u8
{
	BIO_KIE = 0x01, // keyboard interrupt enable
	BIO_KI  = 0x08, // keyboard interrupt active
	BIO_DO  = 0x10, // display off
};

enum exr_ff_mask : u8
{
	EXR_ROS2 = 0x01, // ROS2 enable
};

void ibm5100_state::machine_start()
{
	// for simplicity allocate maximum 64KiB
	m_rws = std::make_unique<u16[]>(0x8000);

	save_item(NAME(m_getb_bus));

	save_item(NAME(m_nxr_ff));
	save_item(NAME(m_bio_ff));
	save_item(NAME(m_nxr_address));

	save_pointer(NAME(m_rws), 0x8000);
}

void ibm5110_state::machine_start()
{
	ibm5100_state::machine_start();

	save_item(NAME(m_exr_ff));
}

void ibm5100_state::machine_reset()
{
	m_nxr_ff = 0;
	m_bio_ff = 0;

	m_nxr_address = 0;

	// install configured rws
	unsigned const rws_cards = (m_conf->read() & 3) + 1;
	m_cpu->space(palm_device::AS_RWS).install_ram(0x80, 0x4000 * rws_cards - 1, &m_rws[0x40]);
	m_ros[1].install_ram(0x80, 0x4000 * rws_cards - 1, &m_rws[0x40]);
}

void ibm5110_state::machine_reset()
{
	ibm5100_state::machine_reset();

	m_exr_ff = 0;
}

void ibm5100_state::cpu_pgm_map(address_map &map)
{
	map(0x0000, 0xffff).view(m_pgm);

	// normal mode program memory
	m_pgm[0](0x0000, 0xffff).view(m_ros);
	m_ros[0](0x0000, 0xffff).rom().region("ros", 0);
	// m_ros[1] RWS (configurable size)

	// interrupt mode program memory
	m_pgm[1](0x0000, 0xffff).rom().region("ros", 0);
}

void ibm5110_state::cpu_pgm_map(address_map &map)
{
	map(0x0000, 0xffff).view(m_pgm);

	// normal mode program memory
	m_pgm[0](0x0000, 0xffff).view(m_ros);
	m_ros[0](0x0000, 0x7fff).rom().region("ros1", 0);
	// m_ros[1] RWS (configurable size)
	m_ros[2](0x0000, 0x7fff).rom().region("ros2a", 0);
	m_ros[3](0x0000, 0x7fff).rom().region("ros2b", 0);

	// interrupt mode program memory
	m_pgm[1](0x0000, 0x7fff).rom().region("ros1", 0);
}

void ibm5100_state::cpu_rws_map(address_map &map)
{
	map.unmap_value_high();
}

void ibm5100_state::cpu_ioc_map(address_map &map)
{
	map(0x0, 0x0).w(FUNC(ibm5100_state::da0_ctl_w));
	map(0x1, 0x1).rw(FUNC(ibm5100_state::nxr_sts_r), FUNC(ibm5100_state::nxr_ctl_w));
	map(0x2, 0x3).noprw();
	map(0x4, 0x4).rw(FUNC(ibm5100_state::kbd_sts_r), FUNC(ibm5100_state::kbd_ctl_w));
	// 5 printer r:not used w:control
	map(0x6, 0x7).noprw();
	// 8 expansion r:status w:control
	map(0x9, 0xd).noprw();
	// e tape r:status w:control
	map(0xf, 0xf).nopr().w(FUNC(ibm5100_state::daf_ctl_w));
}

void ibm5110_state::cpu_ioc_map(address_map &map)
{
	ibm5100_state::cpu_ioc_map(map);

	map(0x2, 0x2).rw(FUNC(ibm5110_state::exr_sts_r), FUNC(ibm5110_state::exr_ctl_w));
}

void ibm5100_state::cpu_iod_map(address_map &map)
{
	map.unmap_value_high();

	map(0x0, 0x0).noprw();
	map(0x1, 0x1).rw(FUNC(ibm5100_state::nxr_r), FUNC(ibm5100_state::nxr_w));
	map(0x2, 0x3).noprw();
	map(0x4, 0x4).r(m_kbd, FUNC(ibm5100_keyboard_device::read));
	// 5 r:printer w:print data
	map(0x6, 0x7).noprw();
	// 8 expansion r:not used w:data
	map(0x9, 0xd).noprw();
	// e r:tape data w:tape
	map(0xf, 0xf).noprw();
}

void ibm5100_state::common(machine_config &config)
{
	PALM(config, m_cpu, 15'091'200);
	m_cpu->set_addrmap(palm_device::AS_PGM, &ibm5100_state::cpu_pgm_map);
	m_cpu->set_addrmap(palm_device::AS_RWS, &ibm5100_state::cpu_rws_map);
	m_cpu->set_addrmap(palm_device::AS_IOC, &ibm5100_state::cpu_ioc_map);
	m_cpu->set_addrmap(palm_device::AS_IOD, &ibm5100_state::cpu_iod_map);
	m_cpu->getb_bus().set([this](offs_t offset, u8 data) { m_getb_bus = data; });
	m_cpu->program_level().set([this](int state) { m_pgm.select(state); });

	/*
	 * Display output is 16 rows of 64 characters. Each character cell is 10x12
	 * pixels with 8x12 driven from character ROS data. The last two horizontal
	 * pixels of each character cell line and every other scan line are blank.
	 */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(15'091'200, 64*10+15, 0, 64*10, 16*12*2, 0, 16*12*2);
	m_screen->set_screen_update(FUNC(ibm5100_state::screen_update));
}

void ibm5100_state::ibm5100(machine_config &config)
{
	ibm5100_state::common(config);

	m_cpu->select_ros().set([this](int state) { m_ros.select(state); });

	IBM5100_KEYBOARD(config, m_kbd);
	m_kbd->strobe().set(
		[this](int state)
		{
			if ((m_bio_ff & BIO_KIE) && !state)
				m_cpu->set_input_line(palm_device::IRPT_REQ3, 0);
		});
}

void ibm5110_state::ibm5110(machine_config &config)
{
	ibm5100_state::common(config);

	m_cpu->select_ros().set(
		[this](int state)
		{
			if (!state && (m_exr_ff & EXR_ROS2))
				m_ros.select(BIT(m_lang->read(), 6) + 2);
			else
				m_ros.select(state);
		});

	IBM5110_KEYBOARD(config, m_kbd);
	m_kbd->strobe().set(
		[this](int state)
		{
			if (!state)
			{
				m_bio_ff |= BIO_KI;

				if (m_bio_ff & BIO_KIE)
					m_cpu->set_input_line(palm_device::IRPT_REQ3, 0);
			}
		});

	SPEAKER(config, "mono").front_center();
	BEEP(config, m_alarm, 3'885); // FIXME: frequency
	m_alarm->add_route(ALL_OUTPUTS, "mono", 0.25);

	// gfxdecode is only used to show the font data in the tile viewer
	static const gfx_layout g2_layout =
	{
		8, 12, 256, 1,
		{ 0 },
		{ 0, 1, 2, 3, 4, 5, 6, 7 },
		{ 0 * 8, 1 * 8, 2 * 8, 3 * 8, 4 * 8, 5 * 8, 6 * 8, 7 * 8, 8 * 8, 9 * 8, 10 * 8, 11 * 8 },
		8 * 16
	};

	static GFXDECODE_START(g2)
		GFXDECODE_ENTRY("cgr", 0, g2_layout, 0, 1)
	GFXDECODE_END

	PALETTE(config, "palette", palette_device::MONOCHROME);
	GFXDECODE(config, "gfx", "palette", g2);
}

u32 ibm5100_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect)
{
	static rgb_t const c[] = { rgb_t::white(), rgb_t::black() };

	// read display switches
	u8 const disp = m_disp->read();

	bool const reverse = BIT(disp, 0);
	bool const n64 = !BIT(disp, 2);
	unsigned const r32 = BIT(disp, 3) ? 32 : 0;

	// start with a blank screen
	bitmap.fill(c[reverse]);

	if (m_bio_ff & BIO_DO)
		return 0;

	// generate characters
	auto const rws = util::big_endian_cast<u8 const>(m_rws.get());
	for (unsigned char_y = 0; char_y < 16; char_y++)
	{
		// every alternate scan line is blank
		int const y = screen.visible_area().min_y + char_y * 12 * 2;

		// compute offset into rws for each row
		offs_t offset = 0x200 + char_y * 64 + r32;

		for (unsigned char_x = 0; char_x < 64; char_x++)
		{
			// skip alternate columns in l32/r32 mode
			if (!n64 && (char_x & 1))
				continue;

			int const x = screen.visible_area().min_x + char_x * 10;

			// read next character
			u8 const char_data = rws[offset++];

			// draw 8x12 character cell
			for (unsigned cell_y = 0; cell_y < 12; cell_y++)
			{
				u8 cell_data = 0;

				/*
				 * The J2 (5100) display adapter has 2KiB of character ROS used
				 * to produce 128 unique characters with normal and underlined
				 * variants. The G2 (5110) has 4KiB with 256 unique character
				 * patterns, with only selected characters having underlined
				 * variations.
				 */
				if (m_cgr.bytes() < 4096)
				{
					unsigned const underline = ((cell_y > 7) && BIT(char_data, 7)) ? 4 : 0;

					cell_data = m_cgr[(char_data & 0x7f) * 16 + cell_y + underline];
				}
				else
					cell_data = m_cgr[char_data * 16 + cell_y];

				bitmap.pix(y + cell_y * 2, x + 0) = c[BIT(cell_data, 7) ^ reverse];
				bitmap.pix(y + cell_y * 2, x + 1) = c[BIT(cell_data, 6) ^ reverse];
				bitmap.pix(y + cell_y * 2, x + 2) = c[BIT(cell_data, 5) ^ reverse];
				bitmap.pix(y + cell_y * 2, x + 3) = c[BIT(cell_data, 4) ^ reverse];
				bitmap.pix(y + cell_y * 2, x + 4) = c[BIT(cell_data, 3) ^ reverse];
				bitmap.pix(y + cell_y * 2, x + 5) = c[BIT(cell_data, 2) ^ reverse];
				bitmap.pix(y + cell_y * 2, x + 6) = c[BIT(cell_data, 1) ^ reverse];
				bitmap.pix(y + cell_y * 2, x + 7) = c[BIT(cell_data, 0) ^ reverse];
			}
		}
	}

	return 0;
}

void ibm5100_state::da0_ctl_w(u8 data)
{
	LOG("da0_ctl_w 0x%02x (%s)\n", data, machine().describe_context());

	// bit 4: 0=display off
	// bit 3: 0=display on
	if (!BIT(data, 4))
		m_bio_ff |= BIO_DO;
	else if (!BIT(data, 3))
		m_bio_ff &= ~BIO_DO;
}

void ibm5110_state::da0_ctl_w(u8 data)
{
	ibm5100_state::da0_ctl_w(data);

	// bit 0: 0=alarm on
	// bit 1: 0=alarm off
	if (!BIT(data, 0))
		m_alarm->set_state(1);
	else if (!BIT(data, 1))
		m_alarm->set_state(0);
}

u8 ibm5110_state::exr_sts_r()
{
	LOG("exr_sts_r 0x%02x (%s)\n", m_getb_bus, machine().describe_context());

	switch (m_getb_bus)
	{
	case 0x80: return m_jmp[0]->read();
	case 0x40: return m_jmp[1]->read();
	case 0x20: return m_jmp[2]->read();
	default:
		return 0;
	}
}

void ibm5110_state::exr_ctl_w(u8 data)
{
	LOG("exr_ctl_w 0x%02x (%s)\n", data, machine().describe_context());

	if (BIT(data, 7))
	{
		m_exr_ff |= EXR_ROS2;

		if (m_ros.entry() != 1)
			m_ros.select(BIT(m_lang->read(), 6) + 2);
	}
	else if (BIT(data, 6))
	{
		m_exr_ff &= ~EXR_ROS2;

		if (m_ros.entry() != 1)
			m_ros.select(0);
	}
}

u8 ibm5100_state::nxr_sts_r()
{
	u8 data = 0xff;

	if (!machine().side_effects_disabled())
	{
		data = (m_nxr_ff & NXR_PS) ? u8(m_nxr_address) : (m_nxr_address >> 8);

		m_nxr_ff ^= NXR_PS;
	}

	return data;
}

void ibm5100_state::nxr_ctl_w(u8 data)
{
	LOG("nxr_ctl_w 0x%02x (%s)\n", data, machine().describe_context());

	m_nxr_ff &= ~(NXR_B0 | NXR_PS | NXR_RS);

	if (BIT(data, 3))
		m_nxr_ff |= NXR_BASIC;
	else if (BIT(data, 2))
		m_nxr_ff |= NXR_APL;

	m_nxr_address = 0;
}

u8 ibm5100_state::nxr_r()
{
	u8 data = 0xff;

	if (!machine().side_effects_disabled())
	{
		unsigned const rs = m_nxr_ff & NXR_RS;
		bool const basic = !(rs & NXR_APL);

		// check model has selected ROS (all models have common)
		if (BIT(m_conf->read(), 2 + basic) || (basic && m_nxr_address >= 0x9000))
		{
			/*
			 * APL non-executable ROS is addressed with an unshifted 16-bit
			 * address, giving a 128KiB range; BASIC uses a shifted address,
			 * giving 64KiB. Even and odd bytes are selected by a flip-flop
			 * which is toggled with each read.
			 */
			data = BIT(m_nxr[rs][m_nxr_address >> (basic ? 1 : 0)], (m_nxr_ff & NXR_B0) ? 0 : 8, 8);
		}

		// always increment address for BASIC, only on odd bytes for APL
		if (basic || (m_nxr_ff & NXR_B0))
			m_nxr_address++;

		// toggle even/odd byte flip-flop
		m_nxr_ff ^= NXR_B0;
	}

	return data;
}

u8 ibm5110_state::nxr_r()
{
	u8 data = 0xff;

	if (!machine().side_effects_disabled())
	{
		unsigned const rs = m_nxr_ff & NXR_RS;

		// check model has selected ROS (all models have common)
		if (!(rs & NXR_APL) || BIT(m_conf->read(), 2))
			data = BIT(m_nxr[rs][m_nxr_address], (m_nxr_ff & NXR_B0) ? 0 : 8, 8);

		// increment after odd bytes
		if (m_nxr_ff & NXR_B0)
			m_nxr_address++;

		// toggle even/odd byte flip-flop
		m_nxr_ff ^= NXR_B0;
	}

	return data;
}

void ibm5100_state::nxr_w(u8 data)
{
	m_nxr_address = (m_nxr_address << 8) | data;

	if (m_nxr_ff & NXR_PS)
	{
		LOG("nxr_address 0x%04x (%s)\n", m_nxr_address, machine().describe_context());

		// data byte even/odd flip-flop is cleared except when BASIC is
		// selected and the address is odd
		if (!(m_nxr_ff & NXR_APL) && (m_nxr_address & 1))
			m_nxr_ff |= NXR_B0;
		else
			m_nxr_ff &= ~NXR_B0;
	}

	// toggle put strobe flip-flop
	m_nxr_ff ^= NXR_PS;
}

void ibm5110_state::nxr_w(u8 data)
{
	m_nxr_address = (m_nxr_address << 8) | data;

	if (m_nxr_ff & NXR_PS)
	{
		LOG("nxr_address 0x%04x (%s)\n", m_nxr_address, machine().describe_context());

		m_nxr_ff &= ~NXR_B0;
	}

	// toggle put strobe flip-flop
	m_nxr_ff ^= NXR_PS;
}

u8 ibm5100_state::kbd_sts_r()
{
	if (!machine().side_effects_disabled())
	{
		switch (m_getb_bus)
		{
		case 0x40:
			// keyboard data gate
			m_bio_ff &= ~BIO_KI;
			return m_kbd->read();
		case 0x80:
			// keyboard status gate
			return m_lang->read() | (m_bio_ff & BIO_KI);
		default:
			LOG("kbd_sts_r: unknown 0x%02x (%s)\n", m_getb_bus, machine().describe_context());
			break;
		}
	}

	return 0xff;
}

void ibm5100_state::kbd_ctl_w(u8 data)
{
	LOG("kbd_ctl_w 0x%02x (%s)\n", data, machine().describe_context());

	// bit 6: 0=reset and disable keyboard interrupts
	if (!BIT(data, 6))
	{
		m_bio_ff &= ~BIO_KIE;

		m_cpu->set_input_line(palm_device::IRPT_REQ3, 1);
	}

	// bit 4: 0=lock keyboard
	m_kbd->lock_w(BIT(data, 4));

	// bit 1: 0=enable typamatic
	// FIXME: inverted?
	m_kbd->typamatic_w(BIT(data, 1));

	// bit 0: 0=enable keyboard interrupt
	if (!BIT(data, 0))
		m_bio_ff |= BIO_KIE;
	else
		m_bio_ff &= ~BIO_KIE;
}

void ibm5100_state::daf_ctl_w(u8 data)
{
	LOG("daf_ctl_w 0x%02x (%s)\n", data, machine().describe_context());

	// bit  function
	//  7   expansion da=8
	//  6   tape da=e
	//  5   keyboard da=4
	if (BIT(data, 5))
	{
		m_bio_ff &= ~(BIO_KI | BIO_KIE);
		m_cpu->set_input_line(palm_device::IRPT_REQ3, 1);
	}
	//  4   printer da=5
	//  3   enable cycle steal
	if (BIT(data, 3))
		m_bio_ff &= ~BIO_DO;
	//  2   reset da=b
	//  1   reset da=c
	//  0   reset da=d
}

ROM_START(ibm5100)
	// Executable ROS
	ROM_REGION16_BE(0x10000, "ros", 0)
	ROM_LOAD("h2.ros", 0x0000, 0x8000, CRC(36d11e3d) SHA1(6be2c0728b88debcd557879c25781a9c7afc5224))
	ROM_LOAD("h4.ros", 0x8000, 0x8000, CRC(5d3eceb7) SHA1(e5412914d74e8149ea8a250a6560d1738555ec7e))

	// BASIC + Common and Language non-executable ROS
	/*
	 * The common ROS is physically located on the E2 card, and not on the C4
	 * BASIC card, however it is selected together with the BASIC ROS and
	 * logically appended to the address space.
	 */
	ROM_REGION16_BE(0x10000, "common", 0)
	ROM_LOAD("c4.ros", 0x0000, 0x9000, CRC(b1abeb4a) SHA1(e0151fefe63c43c8912599615ddfb7c06f111c72))
	ROM_LOAD("e2.ros", 0x9000, 0x4800, CRC(be4289c3) SHA1(008ea7bb25fda94540bf5e02eff5a59bb1c86aac))
	ROM_FILL(0xd800, 0x2800, 0xff)

	ROM_REGION16_BE(0x10000, "basic", 0)
	ROM_LOAD("c4.ros", 0x0000, 0x9000, CRC(b1abeb4a) SHA1(e0151fefe63c43c8912599615ddfb7c06f111c72))
	ROM_LOAD("e2.ros", 0x9000, 0x4800, CRC(be4289c3) SHA1(008ea7bb25fda94540bf5e02eff5a59bb1c86aac))
	ROM_FILL(0xd800, 0x2800, 0xff)

	// APL non-executable ROS
	ROM_REGION16_BE(0x20000, "apl", 0)
	ROM_LOAD("c2.ros", 0x00000, 0x8000, CRC(fba01c70) SHA1(a7dd9b60ba33021d830751df2de5513e0de452f2))
	ROM_LOAD("d2.ros", 0x08000, 0x8000, CRC(afb3ba33) SHA1(15292d1082a2d6211fbdbbb0781466506d310954))
	ROM_LOAD("d4.ros", 0x10000, 0x8000, CRC(a03570c9) SHA1(5a6e7a5b38b96c8ff47be2572272f8ed0cd31efd))
	ROM_FILL(0x18000, 0x8000, 0xff)

	// Display Adapter ROS
	/*
	 * This data was hand-made based on the character map in the documentation.
	 * It was assumed that the first 8 bytes of each character store the upper
	 * 8x8 cell, then 2x4 byte entries contain the normal and underlined lower
	 * 4 rows of the total 8x12 cell respectively.
	 */
	ROM_REGION(0x800, "cgr", 0)
	ROM_LOAD("j2.ros", 0x000, 0x800, CRC(428e5b66) SHA1(9def68eed9dc2b8f08581387f8b74b49b3faf7e7) BAD_DUMP)
ROM_END

ROM_START(ibm5110)
	// Executable ROS
	ROM_REGION16_BE(0x8000, "ros1", 0)
	ROM_LOAD("l2_1.ros", 0x0000, 0x8000, CRC(0355894f) SHA1(c76a91cbbec226feb942ccde93ecb1637c88a01b))

	// APL Executable ROS
	ROM_REGION16_BE(0x8000, "ros2a", 0)
	ROM_LOAD("l2_2a.ros", 0x0000, 0x5000, CRC(46918be9) SHA1(bf45a44f77104c55f2ccfa462af06944e6bffe1a))
	ROM_FILL(0x5000, 0x3000, 0xff)

	// BASIC Executable ROS
	ROM_REGION16_BE(0x8000, "ros2b", 0)
	ROM_LOAD("l2_2b.ros", 0x0000, 0x4000, CRC(a69dd0c1) SHA1(ecdc1363e25b72b695c517af145c50a069b6e8dc))
	ROM_FILL(0x4000, 0x3000, 0xff)

	// Common and Language non-executable ROS
	ROM_REGION16_BE(0x20000, "common", 0)
	ROM_LOAD("f2_c.ros", 0x0000, 0x4000, CRC(83beafb2) SHA1(80ad07a2a83ff395d918b69d5c81a6e94ac7af37))
	ROM_FILL(0x4000, 0x1c000, 0xff)

	// BASIC non-executable ROS
	ROM_REGION16_BE(0x20000, "basic", 0)
	ROM_LOAD("f2_b.ros", 0x0000, 0x12000, CRC(3ffb79b6) SHA1(254f5a7ae739b7bf6a800e8380cf3b6686ee9768))
	ROM_FILL(0x12000, 0x8000, 0xff)

	// APL non-executable ROS
	ROM_REGION16_BE(0x20000, "apl", 0)
	ROM_LOAD("e4.ros", 0x00000, 0x20000, CRC(1e28db20) SHA1(cf12893bb76c44756a9554828d42299b169e560a))

	// Display Adapter ROS
	/*
	 * This data was hand-made based on the character map in the documentation.
	 * It was assumed that the first 12 bytes of each character store the 8x12
	 * cell, followed by 4 empty bytes.
	 */
	ROM_REGION(0x1000, "cgr", 0)
	ROM_LOAD("g2.ros", 0x000, 0x1000, CRC(86e6a99c) SHA1(7168ba05fbac3f66bd98b8ef9fc135d0d08eb44b) BAD_DUMP)
ROM_END

static INPUT_PORTS_START(ibm5100)
	PORT_START("CONF")
	PORT_CONFNAME(0x0f, 0x0f, "Model")
	PORT_CONFSETTING(0x04, "A1 - APL 16K")
	PORT_CONFSETTING(0x05, "A2 - APL 32K")
	PORT_CONFSETTING(0x06, "A3 - APL 48K")
	PORT_CONFSETTING(0x07, "A4 - APL 64K")

	PORT_CONFSETTING(0x08, "B1 - BASIC 16K")
	PORT_CONFSETTING(0x09, "B2 - BASIC 32K")
	PORT_CONFSETTING(0x0a, "B3 - BASIC 48K")
	PORT_CONFSETTING(0x0b, "B4 - BASIC 64K")

	PORT_CONFSETTING(0x0c, "C1 - APL/BASIC 16K")
	PORT_CONFSETTING(0x0d, "C2 - APL/BASIC 32K")
	PORT_CONFSETTING(0x0e, "C3 - APL/BASIC 48K")
	PORT_CONFSETTING(0x0f, "C4 - APL/BASIC 64K")

	PORT_START("DISP")
	PORT_CONFNAME(0x01, 0x00, "Reverse")
	PORT_CONFSETTING(0x00, "Black on White")
	PORT_CONFSETTING(0x01, "White on Black")
	PORT_CONFNAME(0x02, 0x00, "Display")
	PORT_CONFSETTING(0x00, "Normal")
	PORT_CONFSETTING(0x02, "Registers")
	PORT_CONFNAME(0x0c, 0x00, "Columns")
	PORT_CONFSETTING(0x04, "L32")
	PORT_CONFSETTING(0x00, "64")
	PORT_CONFSETTING(0x0c, "R32")

	PORT_START("LANG")
	PORT_CONFNAME(0x40, 0x40, "Language")
	PORT_CONFSETTING(0x00, "APL")   PORT_CONDITION("CONF", 0x04, EQUALS, 0x04)
	PORT_CONFSETTING(0x40, "BASIC") PORT_CONDITION("CONF", 0x08, EQUALS, 0x08)
INPUT_PORTS_END

static INPUT_PORTS_START(ibm5110)
	PORT_INCLUDE(ibm5100)

	PORT_START("L2_1")
	PORT_DIPNAME(0xf0, 0xf0, "Model") PORT_DIPLOCATION("L2_1:4,3,2,1")
	PORT_DIPSETTING(0xf0, "5110-X1X")
	PORT_DIPSETTING(0xe0, "5110-X2X")

	PORT_START("L2_2")
	PORT_DIPNAME(0xf0, 0xc0, "L2_2") PORT_DIPLOCATION("L2_2:4,3,2,1")
	PORT_DIPSETTING(0xc0, DEF_STR(Unknown))

	PORT_START("K4")
	PORT_DIPNAME(0x80, 0x80, "Feature ROS") PORT_DIPLOCATION("K4:1")
	PORT_DIPSETTING(0x80, "Absent")
	PORT_DIPSETTING(0x00, "Present")
INPUT_PORTS_END

} // anonymous namespace

/*   YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY                            FULLNAME    FLAGS */
COMP(1975, ibm5100, 0,      0,      ibm5100, ibm5100, ibm5100_state, empty_init, "International Business Machines", "IBM 5100", MACHINE_NO_SOUND_HW)
COMP(1978, ibm5110, 0,      0,      ibm5110, ibm5110, ibm5110_state, empty_init, "International Business Machines", "IBM 5110", 0)



ibm5550.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Angelo Salese
/**************************************************************************************************

IBM 5550

http://ibm5550.na.coocan.jp/architec-e.html

TODO:
- No available documentation
\- it's barely known that it uses a specific "JDA" video adapter, and that it uses a specific
   incompatible bus slot;

POST codes (to port $a1):
- 0xe2: ROM check
[- 0xfc:] goes high if first $a1 check = 1 at PC=0xfc0d8
- 0xfa: PIC check #1
- 0xf6: undefined boundary check at $4c000-$ec000
- 0xe4: RAM check
- 0xe8: PIC check #2
- 0xf0: PIT check
- 0xf8: $3d0-$3d1 check (JDA adapter?)
- 0xf2: VRAM check

**************************************************************************************************/

#include "emu.h"

//#include "bus/isa/isa.h"
//#include "bus/isa/isa_cards.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "cpu/i86/i86.h"
#include "machine/am9517a.h"
#include "machine/i8251.h"
//#include "machine/i8255.h"
#include "machine/i8257.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/ram.h"
#include "machine/upd765.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"

class ibm5550_state : public driver_device
{
public:
	ibm5550_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pit(*this, "pit")
		, m_pic(*this, "pic")
		, m_dma(*this, "dma")
		, m_crtc(*this, "crtc")
		, m_screen(*this, "screen")
		, m_vram(*this, "vram")
		, m_gfxram(*this, "gfxram")
		, m_palette(*this, "palette")
	{ }

	void ibm5550(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;

	required_device<i8086_cpu_device> m_maincpu;
	required_device<pit8253_device> m_pit;
	required_device<pic8259_device> m_pic;
	required_device<am9517a_device> m_dma;
	required_device<mc6845_device> m_crtc;
	required_device<screen_device> m_screen;
	required_shared_ptr<uint16_t> m_vram;
	required_shared_ptr<uint16_t> m_gfxram;
	required_device<palette_device> m_palette;

	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	u8 m_a0_unk = 0;
};

/*
static const gfx_layout kanji_layout =
{
    32, 32,
    RGN_FRAC(1,1),
    1,
    { 0 },
    { STEP16(0,1), STEP16(16, 16) },
    { STEP16(0,16), STEP16(16*16, 16) },
    32*32
};
*/

uint32_t ibm5550_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	const u32 pitch = 82;
	bitmap.fill(m_palette->black_pen(), cliprect);

	for(int y = 0; y < 24; y++)
	{
		for(int x = 0; x < pitch; x++)
		{
			const u16 tile = m_vram[x + y * pitch] & 0x1f;

			for (int yi = 0; yi < 32; yi ++)
			{
				u16 color = m_gfxram[tile * 64 + yi];
				for (int xi = 0; xi < 16; xi ++)
				{
					const int res_x = x * 32 + xi;
					const int res_y = y * 32 + yi;
					if(cliprect.contains(res_x, res_y))
						bitmap.pix(res_y, res_x) = m_palette->pen(BIT(color, 15 - xi) ? 2 : 0);
				}
				color = m_gfxram[tile * 64 + yi + 32];
				for (int xi = 0; xi < 16; xi ++)
				{
					const int res_x = x * 32 + xi + 16;
					const int res_y = y * 32 + yi;
					if(cliprect.contains(res_x, res_y))
						bitmap.pix(res_y, res_x) = m_palette->pen(BIT(color, 15 - xi) ? 2 : 0);
				}
			}
		}
	}

	return 0;
}

void ibm5550_state::main_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x3ffff).ram(); // 256 or 512KB
	// POST test $f6 expects that all the blocks between $4c000-$ec000 returns 0xff
	map(0xd8000, 0xd8fff).ram().share("gfxram");
	map(0xe0000, 0xe0fff).ram().share("vram");
	map(0xfc000, 0xfffff).rom().region("ipl", 0);
}

void ibm5550_state::main_io(address_map &map)
{
	map.unmap_value_high();
//  map(0x00?0, 0x00?7).rw(m_dma, FUNC(am9517a_device::read), FUNC(am9517a_device::write));
	map(0x0020, 0x0021).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write));

	// tested later, with bit 6 irq from PIC
//  map(0x0040, 0x0047).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));

//  map(0x0060, 0x0060) / map(0x0064, 0x0064) standard XT keyboard

	// bit 0 on will punt before testing for $20-$21,
	// but will be required on after $4c-$ec RAM holes above
	// ... RAM protection?
	map(0x00a0, 0x00a0).lrw8(
		NAME([this] (offs_t offset) { return m_a0_unk; }),
		NAME([this] (offs_t offset, u8 data) {
			logerror("$a0 %02x\n", data);
			m_a0_unk = BIT(data, 6);
			if (data == 0xc0)
			{
				// attached to bus error?
				m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
			}
		})
	);
//  map(0x00a1, 0x00a1) LED write?
	map(0x00a1, 0x00a1).lr8(
		NAME([] (offs_t offset) {
			// read thru NMI trap above, bit 3-0 must be low
			return 0;
		})
	);
	map(0x00a2, 0x00a2).lw8(
		NAME([this] (offs_t offset, u8 data) {
			logerror("$a2 %02x\n", data);
			m_a0_unk = 0;
		})
	);

	// TODO: not right, definitely an address/data of some sort (extended regs?)
	//map(0x3d0, 0x3d0).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	//map(0x3d1, 0x3d1).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));

	// TODO: vblank
	//map(0x3da, 0x3da).lr8(
}

static INPUT_PORTS_START( ibm5550 )
INPUT_PORTS_END

void ibm5550_state::machine_reset()
{
	m_a0_unk = 0;
}

void ibm5550_state::ibm5550(machine_config &config)
{
	I8086(config, m_maincpu, XTAL(16'000'000) / 2); // driven by a TD308C/TD1100C @ 16MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &ibm5550_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &ibm5550_state::main_io);
	m_maincpu->set_irq_acknowledge_callback(m_pic, FUNC(pic8259_device::inta_cb));

	// i8087 socket

	// iDB284A
	// MN2364SPM
	// MN2364SPN
	// IBM6343868 / MN50015SPE
	// IBM6343871 / MN50015SPH
	// IBM6343866 / MN50015SPC
	// IBM6343867 / MN50015SPD
	// D8039LC
	// iP8237A-5
	AM9517A(config, m_dma, XTAL(5'000'000));

	// D765AC + SED9420C
	// HM6116LP-2
	// D7261AD
	// D8259AC-2
	PIC8259(config, m_pic);
	m_pic->out_int_callback().set_inputline(m_maincpu, 0);

	// D8253C-2
	PIT8253(config, m_pit, 0);

	// Parallel port, 36 pins
	// keyboard connector
	// TEST switch + LED

	// JDA display board

	// CX0-043C @ 40 MHz
	// HD46505SP-2, unknown pixel clock
	HD6845S(config, m_crtc, XTAL(40'000'000) / 10);
	m_crtc->set_screen(m_screen);
//  m_crtc->set_show_border_area(true);
	m_crtc->set_char_width(16);

	// IBM6343870 / MN50015SPG
	// IBM6343869 / MN50007SPF
	// x2 HM6116P-2

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_screen_update(FUNC(ibm5550_state::screen_update));
	m_screen->set_size(1280, 1024);
	m_screen->set_visarea(0, 1023, 0, 767);
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

//  ibm5160_mb_device &mb(IBM5160_MOTHERBOARD(config, "mb"));
//  mb.set_cputag(m_maincpu);
//  mb.int_callback().set_inputline(m_maincpu, 0);
//  mb.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
//  mb.kbdclk_callback().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
//  mb.kbddata_callback().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));
//  mb.set_input_default(DEVICE_INPUT_DEFAULTS_NAME(pccga));

	// FIXME: determine ISA bus clock
//  ISA8_SLOT(config, "isa1", 0, "mb:isa", pc_isa8_cards, "cga", false);
//  ISA8_SLOT(config, "isa2", 0, "mb:isa", pc_isa8_cards, "fdc_xt", false);
//  ISA8_SLOT(config, "isa3", 0, "mb:isa", pc_isa8_cards, "lpt", false);
//  ISA8_SLOT(config, "isa4", 0, "mb:isa", pc_isa8_cards, "com", false);

	/* keyboard */
//  pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_IBM_PC_XT_83));
//  kbd.out_clock_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_clock_w));
//  kbd.out_data_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_data_w));

	/* internal ram */
//  RAM(config, RAM_TAG).set_default_size("256K").set_extra_options("512K");
}

ROM_START( ibm5550 )
	ROM_REGION16_LE(0x4000, "ipl", 0)
	ROM_LOAD("ipl5550.rom", 0x0000, 0x4000, CRC(40cf34c9) SHA1(d41f77fdfa787b0e97ed311e1c084b8699a5b197))

	// integrated for later models
	ROM_REGION(0x20000, "kanji", ROMREGION_ERASEFF)
	ROM_LOAD("chargen.rom", 0x00000, 0x20000, NO_DUMP )
ROM_END

COMP( 1983, ibm5550, 0, 0, ibm5550, ibm5550, ibm5550_state, empty_init, "International Business Machines", "Multistation 5550", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



ibm6580.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************

IBM 6580 Displaywriter.

A green-screen dedicated word-processing workstation. It uses 8" floppy
disks. It could have up to 224k of ram.  Consists of:
    Electronics Module 6580
    Display 3300
    Keyboard 5330 [a "beamspring"-type]
    Diskette Unit 6360
Optional:
    Printers: 5215, 5218, 5228
    Printer Sharing feature
    Mag Card Unit
    Asynchronous and Bisynchronous communications features
    66-line display and adapter (800x1056 px, 8x16 character cell)


    All chips have IBM part numbers on them.  F.e. on system board:

    8493077 - 8086
    4178619 - 8251A
    4178617 - 8257-5
    4178623 - 8259A
    4178628 - 8255A-5
    4178625 - 8253-5


    Useful parts of PSM Feb83 (document / PDF page numbers):

    - 6-3/87 -- bus buffers ... separate ... into four sections during BAT
    - 6-5/89 -- irq levels
    - 6-6/90 -- timer tick is 50ms

    IRQ levels:

    0   incoming data for printer sharing/3277 DE
    1   transfer data to commo data link
    2   printer and mag card data xfer
    3   keyboard incoming data
    4   diskette
    5   (not in use)
    6   software timer [50 ms period]
    7   error on commo data link
    NMI "when a dump switch operation is initiated" ["memory record" button]


To do:

- verify all frequency sources, document ROM revisions
- memory size options
- bus errors, interrupts

- 92-key keyboard variant, keyboard click/beep, keyboard layouts

- 25-line video board (instant scroll, sub/superscripts, graphics mode)
- 66-line video board (apparently requires 'new' ROM)

- floppy adapter board, single and double density floppies
- "memory record" (system dump) generation to floppies
- printer
- speaker

- pass BAT with no errors (Basic Assurance Test)
- pass RNA with no errors (Resident Non-Automatic Test)
- pass PDD with no errors (Problem Determination Disk)
- pass CED with no errors (Customer Engineering Diagnostics)

- boot Textpack successfully (currently crashes with *90x* message)


Useful documents:

bitsavers://pdf/ibm/6580_Displaywriter/S241-6248-3_Displaywriter_Product_Support_Manual_Feb83.pdf
bitsavers://pdf/ibm/6580_Displaywriter/S241-6248-2_Displaywriter_6360_6580_Product_Support_Manual_May82.pdf
bitsavers://pdf/ibm/6580_Displaywriter/S241-6250-5_Displaywriter_6250_6580_Maintenance_Analysis_Procedures_May82.pdf
http://www.nostalgia8.nl/cpm/ibm/cpm6dwrm.pdf
http://www.kbdbabel.org/schematic/kbdbabel_doc_ibm_displaywriter.pdf
https://docs.google.com/spreadsheets/d/1SYY_HrBqKjSOX9W4fe5xUsjbfiCt0Umjpo4ZIwgG3Nk/edit?usp=sharing


Wanted:

Displaywriter System Manual S544-2023-0 (?) -- mentioned in US patents 4648071 and 5675827
"IBM Displaywriter System Printer Guide," Order No. S544-0861-2, Copyright 1980.
"Displaywriter System Product Support Manual," Order No. S241-6248-1, Copyright 1980


Notes on floppy drive:

    Diskette Unit 6360 models -010, -011 have Type 1D (SS/SD) drives;
    models -020, -021 -- Type 2D (DS/DD).  Each drive has "file control card".


    Useful parts of PSM Feb83 (pdf page numbers):

    - 6-7/91 -- brief description
    - 7-14..20/116..122 -- more detailed description
    - 8-13/161 -- S1 (internal diskette signal cable), system board side
    - 8-16/164 -- 5 (internal diskette signal cable), panel side
    - 8-32/180 -- B1 (diskette signal cable), diskette adapter side
    - 9-10..12/202..204 -- description of RNA tests


    S1 connector has more signals than B1.  Only these are present in B1:

    Address Bit 1-4 -- from host
    Data Bus Bit 0-7 -- bidirectional
    Interrupt 4 -- to host
    I/O Read -- from host
    I/O Write -- from host
    DMA Request -- to host
    DMA Acknowledge -- from host
    Terminal Count -- from host
    Interface Ready -- to host
    Diskette Select -- from host


    Signals between adapter board and file control cards:

    from/to 765:

    write data, inner tracks, write gate, erase gate ->
    write/erase enable, file data <-

    from/to MCU:

    drive present -> ground
    diskette sense <- always ground on 1D; 2D disk index hole sends +5V
    head engage ->
    index (1.5 to 3.0 ms pulse) <-
    select head, switch filter (2D drive) ->


    RNA test L performs in the following sequence:

    1. PORs the Diskette Adapter Cards
    2. Samples the Diskette Index Pulse
    3. Checks the Drive Set Ready Signal
    4. Engages the Read/Write Head
    5. Checks the Write/Erase Enable Line (This ensures the system will not write on the customer's diskette.)
    6. Reads the Track ID
    7. Disengages the Head.


****************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/i86/i86.h"
#include "cpu/mcs48/mcs48.h"
#include "imagedev/floppy.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/i8257.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "ibm6580_kbd.h"
//nclude "machine/ibm6580_fdc.h"
#include "machine/ram.h"
#include "machine/upd765.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"

#include "ibm6580.lh"


#define LOG_KEYBOARD  (1U << 1)
#define LOG_DEBUG     (1U << 2)

//#define VERBOSE (LOG_DEBUG)
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"

#define LOGKBD(...) LOGMASKED(LOG_KEYBOARD, __VA_ARGS__)
#define LOGDBG(...) LOGMASKED(LOG_DEBUG, __VA_ARGS__)


namespace {

#define UPD765_TAG      "upd765"


const uint8_t gfx_expand[16] = {
	0x00,   0x03,   0x0c,   0x0f,
	0x30,   0x33,   0x3c,   0x3f,
	0xc0,   0xc3,   0xcc,   0xcf,
	0xf0,   0xf3,   0xfc,   0xff
};


class ibm6580_state : public driver_device
{
public:
	ibm6580_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_p_videoram(*this, "videoram")
		, m_ram(*this, RAM_TAG)
		, m_maincpu(*this, "maincpu")
		, m_pic8259(*this, "pic8259")
		, m_pit8253(*this, "pit8253")
		, m_ppi8255(*this, "ppi8255")
		, m_dma8257(*this, "dma8257")
		, m_screen(*this, "screen")
		, m_kbd(*this, "kbd")
		, m_p_chargen(*this, "chargen")
		, m_leds(*this, "led%u", 5U)
		, m_mcu(*this, "mcu")
		, m_mcuppi(*this, "mcuppi")
		, m_fdc(*this, UPD765_TAG)
		, m_drive(*this, UPD765_TAG ":%u", 0U)
	{ }

	void ibm6580(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void pic_latch_w(uint16_t data);
	void unk_latch_w(uint16_t data);

	void p40_w(offs_t offset, uint8_t data);
	uint8_t p40_r(offs_t offset);

	void gate_open_w(offs_t offset, uint8_t data);
	void gate_close_w(offs_t offset, uint8_t data);

	void video_w(offs_t offset, uint8_t data);
	uint8_t video_r(offs_t offset);
	void vblank_w(int state);

	uint8_t kb_data_r();
	void led_w(uint8_t data);
	void ppi_c_w(uint8_t data);

	void kb_data_w(int state);
	void kb_clock_w(int state);
	void kb_clock_w_internal(int state);
	void kb_strobe_w(int state);

	void floppy_w(offs_t offset, uint8_t data);
	uint8_t floppy_r(offs_t offset);
	static void floppy_formats(format_registration &fr);
	void hrq_w(int state);
	uint8_t memory_read_byte(offs_t offset);
	void memory_write_byte(offs_t offset, uint8_t data);

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void ibm6580_io(address_map &map) ATTR_COLD;
	void ibm6580_mem(address_map &map) ATTR_COLD;

	void mcu_io(address_map &map) ATTR_COLD;
	void mcu_mem(address_map &map) ATTR_COLD;

	uint16_t m_gate = 0;
	uint8_t m_dma0pg = 0;
	uint8_t m_p40 = 0, m_p4a = 0, m_p50 = 0;
	uint8_t m_kb_data = 0, m_ppi_c = 0, m_led_state = 0;
	bool m_e000 = false;
	bool m_kb_data_bit = false, m_kb_strobe = false, m_kb_clock = false;

	uint8_t m_mcu_p1 = 0, m_mcu_p2 = 0;
	uint8_t m_mcuppi_a = 0, m_mcuppi_b = 0, m_mcuppi_c = 0;

	void mcu_io_w(offs_t offset, uint8_t data);
	uint8_t mcu_io_r(offs_t offset);

	void mcuppi_c_w(uint8_t data);

	bool m_floppy_intrq = false, m_floppy_idle = false;
	uint8_t m_floppy_mcu_wrap = 0;
	struct { floppy_image_device *image = nullptr; bool hdl = false; } m_floppy[2];

	required_shared_ptr<uint16_t> m_p_videoram;
	required_device<ram_device> m_ram;
	required_device<cpu_device> m_maincpu;
	required_device<pic8259_device> m_pic8259;
	required_device<pit8253_device> m_pit8253;
	required_device<i8255_device> m_ppi8255;
	required_device<i8257_device> m_dma8257;
	required_device<screen_device> m_screen;
	required_device<dw_keyboard_device> m_kbd;
	required_region_ptr<u8> m_p_chargen;
	output_finder<4> m_leds;
	required_device<i8048_device> m_mcu;
	required_device<i8255_device> m_mcuppi;
	required_device<upd765a_device> m_fdc;
	required_device_array<floppy_connector, 2> m_drive;
};


void ibm6580_state::p40_w(offs_t offset, uint8_t data)
{
	LOG("___ %02x(%d) <- %02x\n", 0x40 + (offset << 1), offset, data);

	switch (offset)
	{
	case 0:
		m_p40 = data | 0x80;
		break;

	case 2:
		if (data)
			m_p40 |= 4;
		break;

	case 3:
		m_dma8257->dreq0_w(BIT(data, 0));
		m_dma8257->dreq1_w(BIT(data, 1));
		m_dma8257->dreq2_w(BIT(data, 2));
		break;

	case 4:
		m_dma8257->dreq3_w(BIT(data, 0));
		break;

	case 5:
		// write_gate0 doesn't work -- counter is read back as 0
		if (BIT(data, 2))
			// hack. video test checks timer counter value and this lets it pass.
			m_pit8253->set_clockin(0, (double)26880000);
		else
			m_pit8253->set_clockin(0, 0.0);
		m_p4a = data;
		m_p50 = 0;
		break;

	case 6:
		m_dma0pg = data;
		break;

	case 7:
		break;

	case 8:
		m_p50 = data;
		break;

	case 12:
		if (data)
			m_p40 &= ~0x14;
		break;
	}
}

uint8_t ibm6580_state::p40_r(offs_t offset)
{
	uint8_t data = 0;

	switch (offset)
	{
	case 0:
		data = m_p40;
		m_p40 &= ~4;
		break;

	case 8:
		data = m_p50;
		m_p50 = 1;
		break;
	}

	LOGDBG("___ %02x == %02x\n", 0x40 + (offset << 1), data);

	return data;
}

void ibm6580_state::gate_open_w(offs_t offset, uint8_t data)
{
	LOG("___ %02x(%d) <- %02x\n", 0x60 + (offset << 1), offset, data);

	m_gate |= (1 << offset);

	switch (offset)
	{
	case 10:
		m_kbd->reset_w(1);
		break;
	}
}

void ibm6580_state::gate_close_w(offs_t offset, uint8_t data)
{
	LOG("___ %04x(%d) <- %02x\n", 0x8060 + (offset << 1), offset, data);

	m_gate &= ~(1 << offset);

	switch (offset)
	{
	case 10:
		m_kbd->reset_w(0);
		break;
	}
}

void ibm6580_state::video_w(offs_t offset, uint8_t data)
{
	LOG("Video %02x <- %02x\n", 0xe000 + (offset << 1), data);

	switch (offset)
	{
	// some kind of gate
	case 2:
		m_e000 = true;
		break;

	case 4:
		m_e000 = false;
		break;
	}
}

uint8_t ibm6580_state::video_r(offs_t offset)
{
	uint8_t data = 0;

	switch (offset)
	{
	case 8:
		// 25-line video board ID.  66-line is 0x40.
		data = 1;
		// pure guesswork.  0x20 cannot be zero when 0x10 is zero.  0x2 is unknown, may be vsync.
		data |= (m_screen->hblank() ? 4 : 0);
		data |= (m_screen->vblank() ? 8 : 0);
		data |= ((m_screen->frame_number() & 1) ? 6 : 0);
		data |= ((m_screen->vpos() > 196) ? 0x80 : 0);
		if (m_e000) {
			data |= (m_screen->vblank() ? 0x20 : 0);
			data |= (m_screen->vblank() ? 0 : 0x10);
		}
		break;
	}

	if (offset != 8)
		LOG("Video %02x == %02x\n", 0xe000 + (offset << 1), data);

	return data;
}

void ibm6580_state::vblank_w(int state)
{
//  if (state)
//      m_pic8259->ir6_w(state);

	m_p40 |= m_kbd->memory_record_r();
}

void ibm6580_state::pic_latch_w(uint16_t data)
{
	LOG("PIC latch <- %02x\n", data);

	if (data)
		m_p40 |= 8;

	m_pic8259->ir0_w(data == 2 ? ASSERT_LINE : CLEAR_LINE);
	m_pic8259->ir1_w(data == 2 ? ASSERT_LINE : CLEAR_LINE);
	m_pic8259->ir2_w(data == 2 ? ASSERT_LINE : CLEAR_LINE);
	m_pic8259->ir3_w(data == 2 ? ASSERT_LINE : CLEAR_LINE);
	m_pic8259->ir4_w(data == 2 ? ASSERT_LINE : CLEAR_LINE);
	m_pic8259->ir5_w(data == 2 ? ASSERT_LINE : CLEAR_LINE);
	m_pic8259->ir6_w(data == 2 ? ASSERT_LINE : CLEAR_LINE);
	m_pic8259->ir7_w(data == 2 ? ASSERT_LINE : CLEAR_LINE);
}

void ibm6580_state::unk_latch_w(uint16_t data)
{
	LOG("UNK latch <- %02x\n", data);

	m_p40 |= 0x10;
}

void ibm6580_state::led_w(uint8_t data)
{
	for (int i = 0; i < 4; i++)
		m_leds[i] = BIT(data, 7 - i);

	if (!BIT(m_p4a, 0))
	{
		kb_clock_w_internal(BIT(data, 1));
	}

	if (data & 0xf)
		return;

	if (data == m_led_state)
		return;

	m_led_state = data;

#ifdef VERBOSE
	switch (data >> 4)
	{
	case 0x1:
		printf ("LED 0 0001: Parity Generator/Checker\n");
		break;

	case 0xe:
		printf ("LED 0 1110: Base RAM\n");
		break;

	case 0x3:
		printf ("LED 0 0011: Processor Extension Test\n");
		break;

	case 0x4:
		printf ("LED 0 0100: Display RAM\n");
		break;

	case 0x5:
		printf ("LED 0 0101: Display Adapter Timing Test, Video Test\n");
		break;

	case 0x6:
		printf ("LED 0 0110: Keyboard Cable Test, Physical Keyboard Test\n");
		break;

	case 0x7:
		printf ("LED 0 0111: DMA Controller Test\n");
		break;

	case 0x8:
		printf ("LED 0 1000: Diskette Module Wrap Test, Adapter Test\n");
		break;

	case 0x9:
		printf ("LED 0 1001: Extra RAM Test\n");
		break;

	case 0xa:
		printf ("LED 0 1010: Bus Time-Out Test\n");
		break;

	case 0xc:
		printf ("LED 0 1100: RAM Addressability Test\n");
		break;

	default:
//      printf ("LED 0x%08x: unknown\n", data);
		break;
	}
#endif
}

void ibm6580_state::ppi_c_w(uint8_t data)
{
	uint8_t diff = m_ppi_c ^ data;

	LOGKBD("PPI Port C %02x <- %02x\n", m_ppi_c, data);

	m_ppi_c = data;

	// bit 3 -- mode 1 INTR.A out
	// bit 4 -- mode 1 INTE
	// bit 5 -- mode 1 IBF.A out
	// bit 6 -- I/O out = reset || to data input of keyboard shift register
	// bit 7 -- I/O out = invert bit 6

	// normal operation
	if (BIT(m_p4a, 0))
	{
		// Port A IBF bit
		m_kbd->ack_w(BIT(data, 5));

		// 0 = reset
		m_kbd->reset_w(BIT(data, 6));

		return;
	}

	// self-tests
	m_kb_data_bit = BIT(data, 6) ^ !BIT(m_ppi_c, 7);
	if (BIT(diff, 6)) m_ppi8255->pc4_w(!m_kb_data_bit);
}

uint8_t ibm6580_state::kb_data_r()
{
	uint8_t data = m_kb_data;

	LOGKBD("PPI Port A == %02x\n", data);

	return data;
}

void ibm6580_state::kb_data_w(int state)
{
	if (!BIT(m_p4a, 0)) return;

	m_kb_data_bit = !state;
}

void ibm6580_state::kb_clock_w(int state)
{
	if (!BIT(m_p4a, 0)) return;

	kb_clock_w_internal(state);
}

void ibm6580_state::kb_clock_w_internal(int state)
{
	if (m_kb_clock == state) return;
	m_kb_clock = state;

	if (!state)
	{
		m_kb_data = (m_kb_data >> 1) | (m_kb_data_bit << 7);
		LOGKBD("Kbd clock %d data %d -> %02x\n", state, m_kb_data_bit, m_kb_data);
	}
}

void ibm6580_state::kb_strobe_w(int state)
{
	if (!BIT(m_p4a, 0)) return;

	if (m_kb_strobe != state)
	LOGKBD("Kbd strobe %d data %02x\n", state, m_kb_data);
	m_kb_strobe = state;
	if (!state)
	{
		LOGKBD("Kbd enqueue %02x (m_ppi_c %02x)\n", m_kb_data, m_ppi_c);
	}
	m_ppi8255->pc4_w(m_kb_strobe);
}

void ibm6580_state::hrq_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state);
	m_dma8257->hlda_w(state);
}

uint8_t ibm6580_state::memory_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	return prog_space.read_byte(offset | (m_dma0pg << 16));
}

void ibm6580_state::memory_write_byte(offs_t offset, uint8_t data)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	prog_space.write_byte(offset | (m_dma0pg << 16), data);
}

// floppy adapter board

void ibm6580_state::mcu_io_w(offs_t offset, uint8_t data)
{
	LOGDBG("MCU IO %02x(%d) <- %02x\n", m_mcu_p1, m_mcu_p1 & 7, data);

	switch (m_mcu_p1 & 7)
	{
	case 0: case 1: case 2: case 3:
		m_mcuppi->write(m_mcu_p1, data);
		break;
	}
}

uint8_t ibm6580_state::mcu_io_r(offs_t offset)
{
	uint8_t data = 0;

	switch (m_mcu_p1 & 7)
	{
	case 0: case 1: case 2: case 3:
		data = m_mcuppi->read(m_mcu_p1);
		break;
	}

	LOGDBG("MCU IO %02x(%d) == %02x at %s\n", m_mcu_p1, m_mcu_p1 & 7, data, machine().describe_context());

	return data;
}

void ibm6580_state::mcuppi_c_w(uint8_t data)
{
	LOGDBG("MCU PPI PC <- %02x\n", data);
	m_mcuppi_c = data;
	m_mcu->set_input_line(MCS48_INPUT_IRQ, BIT(data, 3)); // invert?
}

// what happens on access to odd address ?
void ibm6580_state::floppy_w(offs_t offset, uint8_t data)
{
	LOG("Floppy %s %02x <- %02x\n", machine().describe_context(), 0x8150 + (offset << 1), data);

	switch (offset)
	{
	case 0: // 8150 -- mcu reset?
		m_mcu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
		break;

	case 1: // 8152 -- fdc reset?  status not checked
		// m_fdc->soft_reset();
		break;

	case 2: // 8154
		if (BIT(m_gate, 14))
			m_floppy_mcu_wrap = data;
		break;

	case 5: // 815A
		m_fdc->fifo_w(data);
		break;

	case 6: // 815C
		m_mcuppi_a = data;
		m_mcuppi->pc4_w(1);
		m_mcuppi->pc4_w(0);
		break;
	}
}

uint8_t ibm6580_state::floppy_r(offs_t offset)
{
	uint8_t data = 0;

	switch (offset)
	{
	case 0: // 8150
		// bit 4 -- ?? ready
		// bit 5 -- mcu busy
		// bit 6 -- ?? idle
		data = 8 | BIT(m_mcuppi_c, 7) << 6 | (m_mcuppi_c & 0x20);
		break;

	case 3: // 8156
		if (BIT(m_gate, 14))
			data = m_floppy_mcu_wrap << 2;
		break;

	case 4: // 8158
		data = m_fdc->msr_r();
		break;

	case 5: // 815a
		data = m_fdc->fifo_r();
		break;

	case 6: // 815c
		data = m_mcuppi->acka_r();
		break;
	}

	LOG("Floppy %s %02x == %02x\n", machine().describe_context(), 0x8150 + (offset << 1), data);

	return data;
}


void ibm6580_state::ibm6580_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x90000, 0x90001).w(FUNC(ibm6580_state::unk_latch_w));
	map(0xef000, 0xeffff).ram().share("videoram");  // 66-line vram starts at 0xec000
	map(0xfc000, 0xfffff).rom().region("user1", 0);
}

void ibm6580_state::ibm6580_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0007).rw(m_pic8259, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x0008, 0x000f).w(FUNC(ibm6580_state::pic_latch_w));
	map(0x0010, 0x0017).rw(m_ppi8255, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
	map(0x0020, 0x003f).rw(m_dma8257, FUNC(i8257_device::read), FUNC(i8257_device::write)).umask16(0x00ff);
	map(0x0040, 0x005f).rw(FUNC(ibm6580_state::p40_r), FUNC(ibm6580_state::p40_w)).umask16(0x00ff);
	map(0x0060, 0x007f).w(FUNC(ibm6580_state::gate_open_w)).umask16(0xff);
	map(0x0120, 0x0127).rw(m_pit8253, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0x0140, 0x0143).rw("upd8251a", FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0x0160, 0x0163).rw("upd8251b", FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0x4000, 0x400f).unmaprw(); // bus error reporting?
	map(0x5000, 0x500f).unmaprw(); // bus error reporting?
	map(0x6000, 0x601f).unmaprw();
	map(0x8060, 0x807f).w(FUNC(ibm6580_state::gate_close_w)).umask16(0xff);
	map(0x8150, 0x815f).rw(FUNC(ibm6580_state::floppy_r), FUNC(ibm6580_state::floppy_w)).umask16(0x00ff);  // HLE of floppy board
	map(0x81a0, 0x81af).unmaprw();
	map(0xc000, 0xc00f).unmaprw();
	map(0xe000, 0xe02f).rw(FUNC(ibm6580_state::video_r), FUNC(ibm6580_state::video_w)).umask16(0x00ff);
}

void ibm6580_state::mcu_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000, 0x7ff).rom().region("mcu", 0);
}

void ibm6580_state::mcu_io(address_map &map)
{
	map(0x00, 0xff).rw(FUNC(ibm6580_state::mcu_io_r), FUNC(ibm6580_state::mcu_io_w));
}


uint32_t ibm6580_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint16_t sy=0,ma=25;

	uint8_t fg = 1, bg = 0;

	for (uint8_t y = 0; y < 25; y++)
	{
		for (uint8_t ra = 0; ra < 16; ra++)
		{
			uint16_t *p = &bitmap.pix(sy++);

			if (m_p_videoram[ma] & 0x100)
			{
				// graphics mode
				for (uint16_t x = ma; x < ma + 80; x++)
				{
					uint8_t const chr = m_p_videoram[x];
					uint8_t const attr = m_p_videoram[x] >> 8;
					uint8_t gfx;

					switch (ra >> 1)
					{
					case 0:
						gfx = gfx_expand[chr & 15];
						break;

					case 2:
						gfx = gfx_expand[chr >> 4];
						break;

					case 4:
						gfx = gfx_expand[attr & 15];
						break;

					case 6:
						gfx = gfx_expand[attr >> 4];
						break;

					default:
						gfx = 0;
						break;
					}

					/* Display a scanline of a character */
					for (int i = 7; i >= 0; i--)
					{
						*p++ = BIT(gfx, i) ? fg : bg;
					}
				}
			}
			else
			{
				// text mode
				for (uint16_t x = ma; x < ma + 80; x++)
				{
					uint8_t const chr = m_p_videoram[x];
					uint8_t const attr = m_p_videoram[x] >> 8;
					uint16_t ca = (chr<<4);
					uint8_t gfx;

					// font 2
					if (attr & 0x02)
						ca += 0x1000;
#if 0
					// superscript
					if (attr & 0x20)
						ca |= (ra < 13) ? ra + 3 : 0;
					// subscript
					if (attr & 0x40)
						ca |= (ra > 2) ? ra - 3 : 0;
#endif

					gfx = m_p_chargen[ca | ra];

					// underline, cursor
					if (((attr & 0x08) && (ra == 13)) || ((attr & 0x04) && (ra == 14)))
						gfx = 0xff;

					// reverse video
					if (attr & 0x10)
						gfx ^= 255;

					// intense
					if (attr & 0x04)
						fg = 2;
					else
						fg = 1;

					/* Display a scanline of a character */
					for (int i = 7; i >= 0; i--)
					{
						*p++ = BIT(gfx, i) ? fg : bg;
					}
				}
			}
		}
		ma+=80;
	}
	return 0;
}


void ibm6580_state::machine_start()
{
	m_maincpu->space(AS_PROGRAM).install_ram(0, m_ram->size() - 1, m_ram->pointer());

	m_fdc->set_rate(500000); // FIXME: workaround

	m_floppy[0].image = m_fdc->subdevice<floppy_connector>("0")->get_device();
	m_floppy[1].image = m_fdc->subdevice<floppy_connector>("1")->get_device();

	m_leds.resolve();

	memset(m_p_videoram, 0x0, 0x1000);
}

void ibm6580_state::machine_reset()
{
	m_p40 = m_p4a = m_p50 = m_gate = m_ppi_c = m_led_state = 0;
	m_mcu_p1 = m_mcu_p2 = 0;
	m_mcuppi_a = m_mcuppi_b = m_mcuppi_c = 0;
	m_e000 = false;
	m_kb_data_bit = false;
	m_kb_clock = false;
	m_kb_strobe = true;
	m_kb_data = 0;

	m_pit8253->set_clockin(0, 0.0);

	m_p40 |= m_kbd->memory_record_r();

	m_floppy[0].hdl = m_floppy[1].hdl = false;

	m_fdc->set_floppy(m_drive[0]->get_device());
}

static void dw_floppies(device_slot_interface &device)
{
	device.option_add("8sssd", IBM_6360);
}

void ibm6580_state::ibm6580(machine_config &config)
{
	I8086(config, m_maincpu, 14.7456_MHz_XTAL / 3); // XTAL is confirmed, divisor is not
	m_maincpu->set_addrmap(AS_PROGRAM, &ibm6580_state::ibm6580_mem);
	m_maincpu->set_addrmap(AS_IO, &ibm6580_state::ibm6580_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259", FUNC(pic8259_device::inta_cb));

	// DMA tests need this
	config.set_perfect_quantum(m_maincpu);

	RAM(config, RAM_TAG).set_default_size("128K").set_extra_options("160K,192K,224K,256K,320K,384K");

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER, rgb_t::green());
	m_screen->set_raw(25_MHz_XTAL / 2, 833, 0, 640, 428, 0, 400);
	m_screen->set_screen_update(FUNC(ibm6580_state::screen_update));
	m_screen->set_palette("palette");
	m_screen->screen_vblank().set(FUNC(ibm6580_state::vblank_w));

	config.set_default_layout(layout_ibm6580);

	PALETTE(config, "palette", palette_device::MONOCHROME_HIGHLIGHT);

	PIC8259(config, m_pic8259, 0);
	m_pic8259->out_int_callback().set_inputline(m_maincpu, 0);

	I8255(config, m_ppi8255);
	m_ppi8255->in_pa_callback().set(FUNC(ibm6580_state::kb_data_r));
	m_ppi8255->out_pb_callback().set(FUNC(ibm6580_state::led_w));
	m_ppi8255->out_pc_callback().set(FUNC(ibm6580_state::ppi_c_w));
	m_ppi8255->tri_pa_callback().set_constant(0);
	m_ppi8255->tri_pc_callback().set_constant(0);

	PIT8253(config, m_pit8253, 0);
	m_pit8253->out_handler<0>().set([this] (int state) { m_p40 = (m_p40 & ~1) | state; });

	DW_KEYBOARD(config, m_kbd, 0);
	m_kbd->out_data_handler().set(FUNC(ibm6580_state::kb_data_w));
	m_kbd->out_clock_handler().set(FUNC(ibm6580_state::kb_clock_w));
	m_kbd->out_strobe_handler().set(FUNC(ibm6580_state::kb_strobe_w));

	I8257(config, m_dma8257, 14.7456_MHz_XTAL / 3);
	m_dma8257->out_hrq_cb().set(FUNC(ibm6580_state::hrq_w));
	m_dma8257->out_tc_cb().set(m_fdc, FUNC(upd765a_device::tc_line_w));
	m_dma8257->in_memr_cb().set(FUNC(ibm6580_state::memory_read_byte));
	m_dma8257->out_memw_cb().set(FUNC(ibm6580_state::memory_write_byte));
	m_dma8257->in_ior_cb<0>().set(m_fdc, FUNC(upd765a_device::dma_r));
	m_dma8257->out_iow_cb<0>().set(m_fdc, FUNC(upd765a_device::dma_w));

	i8251_device &upd8251a(I8251(config, "upd8251a", 0));
	upd8251a.txd_handler().set("rs232a", FUNC(rs232_port_device::write_txd));
	upd8251a.dtr_handler().set("rs232a", FUNC(rs232_port_device::write_dtr));
	upd8251a.rts_handler().set("rs232a", FUNC(rs232_port_device::write_rts));
	upd8251a.rxrdy_handler().set(m_pic8259, FUNC(pic8259_device::ir2_w));
	upd8251a.txrdy_handler().set(m_pic8259, FUNC(pic8259_device::ir2_w));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set("upd8251a", FUNC(i8251_device::write_rxd));
	rs232a.dsr_handler().set("upd8251a", FUNC(i8251_device::write_dsr));
	rs232a.cts_handler().set("upd8251a", FUNC(i8251_device::write_cts));

	i8251_device &upd8251b(I8251(config, "upd8251b", 0));
	upd8251b.txd_handler().set("rs232b", FUNC(rs232_port_device::write_txd));
	upd8251b.dtr_handler().set("rs232b", FUNC(rs232_port_device::write_dtr));
	upd8251b.rts_handler().set("rs232b", FUNC(rs232_port_device::write_rts));
	upd8251b.rxrdy_handler().set(m_pic8259, FUNC(pic8259_device::ir2_w));
	upd8251b.txrdy_handler().set(m_pic8259, FUNC(pic8259_device::ir2_w));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set("upd8251b", FUNC(i8251_device::write_rxd));
	rs232b.dsr_handler().set("upd8251b", FUNC(i8251_device::write_dsr));
	rs232b.cts_handler().set("upd8251b", FUNC(i8251_device::write_cts));

	// floppy adapter card (in the drive)

	I8048(config, m_mcu, 24_MHz_XTAL / 6); // XTAL divisor unknown
	m_mcu->set_addrmap(AS_PROGRAM, &ibm6580_state::mcu_mem);
	m_mcu->set_addrmap(AS_IO, &ibm6580_state::mcu_io);
	m_mcu->p1_in_cb().set([this] () {
		return (m_mcu_p1 & ~0x18) |
			(!m_floppy[0].image->idx_r() << 3) | (!m_floppy[1].image->idx_r() << 4);
	});
	m_mcu->p1_out_cb().set([this] (uint8_t data) { m_mcu_p1 = data; });

	I8255(config, m_mcuppi);
	m_mcuppi->in_pa_callback().set([this] () { LOGDBG("MCU PPI read A: %02X\n", m_mcuppi_a); return m_mcuppi_a; });
	m_mcuppi->out_pb_callback().set([this] (uint8_t data) { m_mcuppi_b = data; });
	m_mcuppi->out_pc_callback().set(FUNC(ibm6580_state::mcuppi_c_w));

	// NEC D765D.   READY and SELECT likely not connected.   SEEK, STEP and DIR likely connected to MCU.
	UPD765A(config, m_fdc, 24_MHz_XTAL / 3, false, false);
	m_fdc->intrq_wr_callback().set([this] (bool state) { m_floppy_intrq = state; });
	m_fdc->drq_wr_callback().set(m_dma8257, FUNC(i8257_device::dreq0_w));
	FLOPPY_CONNECTOR(config, m_drive[0], dw_floppies, "8sssd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_drive[1], dw_floppies, "8sssd", floppy_image_device::default_mfm_floppy_formats);

	SOFTWARE_LIST(config, "flop_list").set_original("ibm6580");
}

/* ROM definition */
ROM_START( ibm6580 )
	ROM_REGION16_LE( 0x4000, "user1", 0 )
	ROM_DEFAULT_BIOS("old")

	ROM_SYSTEM_BIOS(0, "old", "old bios - 1981")
	ROMX_LOAD("8493823_8k.bin", 0x0001, 0x2000, CRC(aa5524c0) SHA1(9938f2a82828b17966cb0be7fdbf73803c1f10d3), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("8493822_8k.bin", 0x0000, 0x2000, CRC(90e7e73a) SHA1(d3ee7a4d2cb8f4920b5d95e8c7f4fef06599d24e), ROM_SKIP(1) | ROM_BIOS(0))

	// disable halts in video test
	ROM_FILL(0x501,1,0x90)
	ROM_FILL(0x51b,1,0x90)
	ROM_FILL(0x52f,1,0x90)
	ROM_FILL(0x587,1,0x90)
	ROM_FILL(0x5a0,1,0x90)
	ROM_FILL(0x5a6,1,0x90)
	ROM_FILL(0x5f4,1,0x90)
	ROM_FILL(0x609,1,0x90)

	// disable rom checksum halt
	ROM_FILL(0x2027,1,0x01)

	ROM_SYSTEM_BIOS(1, "new", "new bios - 1983?")
	// was downloaded via DDT86
	ROMX_LOAD( "dwrom16kb.bin", 0x0000, 0x4000, BAD_DUMP CRC(ced87929) SHA1(907a46f288809bc93a1f59f3fbef18bd44be42d9), ROM_BIOS(1))

	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "8493383_chr.bin", 0x0000, 0x2000, CRC(779044df) SHA1(95ec46f9edf4d44c5dd3c955c73e00754d58e180))

	ROM_REGION( 0x800, "mcu", 0 )
	ROM_LOAD( "4430030_flp_8041.bin", 0x0000, 0x0400, CRC(2bb96799) SHA1(e30b0f2d790197f290858eab74ad5e151ded78c3))
ROM_END

} // anonymous namespace

/* Driver */

/*    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT  CLASS          INIT        COMPANY  FULLNAME                  FLAGS */
COMP( 1980, ibm6580, 0,      0,      ibm6580, 0,     ibm6580_state, empty_init, "IBM",   "IBM 6580 Displaywriter", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



ibmpc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol, Miodrag Milanovic
/***************************************************************************

    drivers/pc.c

Driver file for IBM PC, IBM PC XT, and related machines.

    PC-XT memory map

    00000-9FFFF   RAM
    A0000-AFFFF   NOP       or videoram EGA/VGA
    B0000-B7FFF   videoram  MDA, page #0
    B8000-BFFFF   videoram  CGA and/or MDA page #1, T1T mapped RAM
    C0000-C7FFF   NOP       or ROM EGA/VGA
    C8000-C9FFF   ROM       XT HDC #1
    CA000-CBFFF   ROM       XT HDC #2
    D0000-EFFFF   NOP       or 'adapter RAM'
    F0000-FDFFF   NOP       or ROM Basic + other Extensions
    FE000-FFFFF   ROM

IBM PC 5150
===========

- Intel 8088 at 4.77 MHz derived from a 14.31818 MHz crystal
- Onboard RAM: Revision A mainboard - min. 16KB, max. 64KB
               Revision B mainboard - min. 64KB, max. 256KB
- Total RAM (using 5161 expansion chassis or ISA memory board): 512KB (rev 1 or rev 2 bios) or 640KB (rev 3 bios)
- Graphics: MDA, CGA, or MDA and CGA
- Cassette port
- Five ISA expansion slots (short type)
- PC/XT keyboard 83-key 'IBM Model F' (some early 'IBM Model M' keyboards can produce scancodes compatible with this as well, but it was an undocumented feature unsupported by IBM)
- Optional 8087 co-processor
- Optional up to 4 (2 internal, 2 external) 160KB single-sided or 360KB double-sided 5 1/4" floppy drives
- Optional 10MB hard disk (using 5161 expansion chassis)
- Optional Game port joystick ISA card (two analog joysticks with 2 buttons each)
- Optional Parallel port ISA card, Unidirectional (upgradable to bidirectional (as is on the ps/2) with some minor hacking; IBM had all the circuitry there but it wasn't hooked up properly)
- Optional Serial port ISA card


IBM PC-JR
=========


IBM PC-XT 5160
==============

- Intel 8088 at 4.77 MHz derived from a 14.31818 MHz crystal
- Onboard RAM: Revision A mainboard (P/N 6181655) - min. 64KB, max. 256KB (4x 64KB banks of 9 IMS2600 64kx1 drams plus parity); could be upgraded to 640KB with a hack; may support the weird 256k stacked-chip dram the AT rev 1 uses)
               Revision B mainboard - min. 256KB, max 640KB (2x 256KB, 2x 64KB banks)
- Total RAM (using 5161 expansion chassis, ISA memory board, or rev B mainboard): 640KB
- Graphics: MDA, CGA, MDA and CGA, EGA, EGA and MDA, or EGA and CGA
- One internal 360KB double-sided 5 1/4" floppy drive
- PC/XT keyboard 83-key 'IBM Model F' (BIOS revisions 1 and 2) (some early 'IBM Model M' keyboards can produce scancodes compatible with this as well, but it was an undocumented feature unsupported by IBM)
- 84-key 'AT/Enhanced' keyboard or 101-key 'IBM Model M' (BIOS revisions 3 and 4)
- Eight ISA expansion slots (short type)
- Optional 8087 co-processor
- Optional second internal 360KB double-sided 5 1/4" floppy drive, if no internal hard disk
- Optional 'half height' 720KB double density 3 1/2" floppy drive
- Optional 10MB hard disk via 5161 expansion chassis
- Optional 10MB or 20MB Seagate ST-412 hard disk (in place of second floppy drive)
- Optional up to 2 external 360KB double-sided 5 1/4" floppy drive
- Optional Game port joystick ISA card (two analog joysticks with 2 buttons each)
- Optional Parallel port ISA card, Unidirectional (upgradable to bidirectional (as is on the ps/2) with some minor hacking; IBM had all the circuitry there but it wasn't hooked up properly)
- Optional Serial port ISA card


IBM PC Jr:

TODO: Which clock signals are available in a PC Jr?
      - What clock is Y1? This eventually gets passed on to the CPU (ZM40?) and some other components by a 8284 (ZM8?).
      - Is the clock attached to the Video Gate Array (ZM36?) exactly 14MHz?

IBM CGA/MDA:
Several different font roms were available, depending on what region the card was purchased in;
Currently known: (probably exist for all the standard codepages)
5788005: US (code page 437)
???????: Greek (code page 737) NOT DUMPED!
???????: Estonian/Lithuanian/Latvian (code page 775) NOT DUMPED!
???????: Icelandic (code page 861, characters 0x8B-0x8D, 0x95, 0x97-0x98, 0x9B, 0x9D, 0xA4-0xA7 differ from cp437) NOT DUMPED!
4733197: Danish/Norwegian (code page 865, characters 0x9B and 0x9D differ from cp437)
???????: Hebrew (code page 862) NOT DUMPED!

=========================
IBM Roms thanks to Frode
=========================

1504036.bin: IBM 4860 PC/jr BIOS. visible in memory at F0000-F7FFF.
1504037.bin: IBM 4860 PC/jr BIOS. visible in memory at F8000-FFFFF.

  ROM_LOAD( "1504036.bin", 0xf0000, 0x8000, CRC(de8fa668) SHA1(459341e033be1199c107e56d33680170e144b689))
  ROM_LOAD( "1504037.bin", 0xf8000, 0x8000, CRC(04c05f17) SHA1(319423cb6bb02b399ecf6e0cb82015c16ada68f5))

5601JDA.bin: IBM 5511 PC/JX BIOS. visible in memory at F0000-FFFFF.
  ROM_LOAD( "5601jda.bin", 0xf0000, 0x10000, CRC(b1e12366) SHA1(751feb16b985aa4f1ec1437493ff77e2ebd5e6a6))

7396917.bin: IBM 5140 PC/Convertible BIOS. visible in memory at F0000-F7FFF.
7396918.bin: IBM 5140 PC/Convertible BIOS. visible in memory at F8000-FFFFF.
  ROM_LOAD( "7396917.bin", 0xf0000, 0x8000, CRC(95c35652) SHA1(2bdac30715dba114fbe0895b8b4723f8dc26a90d))
  ROM_LOAD( "7396918.bin", 0xf8000, 0x8000, CRC(1b4202b0) SHA1(4797ff853ba1675860f293b6368832d05e2f3ea9))

5700019.bin: IBM 5150 PC BASIC 1.0. visible in memory at F6000-F7FFF.
5700027.bin: IBM 5150 PC BASIC 1.0. visible in memory at F8000-F9FFF.
5700035.bin: IBM 5150 PC BASIC 1.0. visible in memory at FA000-FBFFF.
5700043.bin: IBM 5150 PC BASIC 1.0. visible in memory at FC000-FDFFF.
  ROM_LOAD( "5700019.bin", 0xf6000, 0x2000, CRC(b59e8f6c) SHA1(7a5db95370194c73b7921f2d69267268c69d2511))
  ROM_LOAD( "5700027.bin", 0xf8000, 0x2000, CRC(bfff99b8) SHA1(ca2f126ba69c1613b7b5a4137d8d8cf1db36a8e6))
  ROM_LOAD( "5700035.bin", 0xfa000, 0x2000, CRC(9fe4ec11) SHA1(89af8138185938c3da3386f97d3b0549a51de5ef))
  ROM_LOAD( "5700043.bin", 0xfc000, 0x2000, CRC(ea2794e6) SHA1(22fe58bc853ffd393d5e2f98defda7456924b04f))


5700051.bin: First early IBM 5150 PC BIOS. visible in memory at FE000-FFFFF.
  ROM_LOAD( "5700051.bin", 0xfe000, 0x2000, CRC(12d33fb8) SHA1(f046058faa016ad13aed5a082a45b21dea43d346))
5700671.bin: Second early IBM 5150 PC BIOS. visible in memory at FE000-FFFFF.
  ROM_LOAD( "5700671.bin", 0xfe000, 0x2000, CRC(b7d4ec46) SHA1(bdb06f846c4768f39eeff7e16b6dbff8cd2117d2))

5000019.bin: IBM 5150 PC BASIC 1.1. visible in memory at F6000-F7FFF.
5000021.bin: IBM 5150 PC BASIC 1.1. visible in memory at F8000-F9FFF.
5000022.bin: IBM 5150 PC BASIC 1.1. visible in memory at FA000-FBFFF.
5000023.bin: IBM 5150 PC BASIC 1.1. visible in memory at FC000-FDFFF.
  ROM_LOAD( "5000019.bin", 0xf6000, 0x2000, CRC(80d3cf5d) SHA1(64769b7a8b60ffeefa04e4afbec778069a2840c9))
  ROM_LOAD( "5000021.bin", 0xf8000, 0x2000, CRC(673a4acc) SHA1(082ae803994048e225150f771794ca305f73d731))
  ROM_LOAD( "5000022.bin", 0xfa000, 0x2000, CRC(aac3fc37) SHA1(c9e0529470edf04da093bb8c8ae2536c688c1a74))
  ROM_LOAD( "5000023.bin", 0xfc000, 0x2000, CRC(3062b3fc) SHA1(5134dd64721cbf093d059ee5d3fd09c7f86604c7))


5700476.0.bin: Late IBM 5150 PC BIOS. visible in memory at FE000-FFFFF. (1981 copyright)
  ROM_LOAD( "1501476.0.bin", 0xfe000, 0x2000, CRC(9b791d3e) SHA1(0c93f07e62cd27688f7f473e9787ef5308535fa0))
5700476.1.bin: Late IBM 5150 PC BIOS. visible in memory at FE000-FFFFF. (1982 copyright)
  ROM_LOAD( "1501476.1.bin", 0xfe000, 0x2000, CRC(e88792b3) SHA1(40fce6a94dda4328a8b608c7ae2f39d1dc688af4))

5000027.bin: Early IBM 5160 PC/XT BIOS/BASIC 1.1. visible in memory at F6000-F7FFF.
5000026.bin: Prototype IBM 5160 PC/XT BIOS/BASIC 1.1. visible in memory at F8000-FFFFF.
  ROM_LOAD( "5000027.bin", 0xf6000, 0x2000, CRC(80d3cf5d) SHA1(64769b7a8b60ffeefa04e4afbec778069a2840c9))
  ROM_LOAD( "5000026.bin", 0xf8000, 0x8000, CRC(3c9b0ac3) SHA1(271c9f4cef5029a1560075550b67c3395db09fef))

6359116.bin: Early IBM 5160 PC/XT BIOS/BASIC 1.1. visible in memory at F6000-F7FFF.
  ROM_LOAD( "6359116.bin", 0xf6000, 0x2000, CRC(80d3cf5d) SHA1(64769b7a8b60ffeefa04e4afbec778069a2840c9))

1501512.bin: Early IBM 5160 PC/XT BIOS/BASIC 1.1. visible in memory at F8000-FFFFF.
  ROM_LOAD( "1501512.bin", 0xf8000, 0x8000, CRC(79522c3d) SHA1(6bac726d8d033491d52507278aa388ec04cf8b7e))

62x0854.bin: First late IBM 5160 PC/XT BIOS/BASIC 1.1. visible in memory at F0000-F7FFF. (PROM)
62x0851.bin: First late IBM 5160 PC/XT BIOS/BASIC 1.1. visible in memory at F8000-FFFFF. (PROM)
  ROM_LOAD( "62x0854.bin", 0xf0000, 0x8000, CRC(b5fb0e83) SHA1(937b43759ffd472da4fb0fe775b3842f5fb4c3b3))
  ROM_LOAD( "62x0851.bin", 0xf8000, 0x8000, CRC(1054f7bd) SHA1(e7d0155813e4c650085144327581f05486ed1484))

62x0853.bin: First late IBM 5160 PC/XT BIOS/BASIC 1.1. visible in memory at F0000-F7FFF. (EPROM)
62x0852.bin: First late IBM 5160 PC/XT BIOS/BASIC 1.1. visible in memory at F8000-FFFFF. (EPROM)
  ROM_LOAD( "62x0853.bin", 0xf0000, 0x8000, CRC(b5fb0e83) SHA1(937b43759ffd472da4fb0fe775b3842f5fb4c3b3))
  ROM_LOAD( "62x0852.bin", 0xf8000, 0x8000, CRC(1054f7bd) SHA1(e7d0155813e4c650085144327581f05486ed1484))

68x4370.bin: Second late IBM 5160 PC/XT BIOS/BASIC 1.1. visible in memory at F0000-F7FFF. (PROM)
62x0890.bin: Second late IBM 5160 PC/XT BIOS/BASIC 1.1. visible in memory at F8000-FFFFF. (PROM)
  ROM_LOAD( "68x4370.bin", 0xf0000, 0x8000, CRC(758ff036) SHA1(045e27a70407d89b7956ecae4d275bd2f6b0f8e2))
  ROM_LOAD( "62x0890.bin", 0xf8000, 0x8000, CRC(4f417635) SHA1(daa61762d3afdd7262e34edf1a3d2df9a05bcebb))

62x0819.bin: Second late IBM 5160 PC/XT BIOS/BASIC 1.1. visible in memory at F0000-F7FFF. (EPROM)
59x7268.bin: Second late IBM 5160 PC/XT BIOS/BASIC 1.1. visible in memory at F8000-FFFFF. (EPROM)
  ROM_LOAD( "62x0819.bin", 0xf0000, 0x8000, CRC(758ff036) SHA1(045e27a70407d89b7956ecae4d275bd2f6b0f8e2))
  ROM_LOAD( "59x7268.bin", 0xf8000, 0x8000, CRC(4f417635) SHA1(daa61762d3afdd7262e34edf1a3d2df9a05bcebb))

78x7460.bin: IBM 5162 PC/XT 286 BIOS. visible in even memory at F0000-FFFFF (mirror at E0000-EFFFF).
78x7461.bin: IBM 5162 PC/XT 286 BIOS. visible in odd memory at F0000-FFFFF (mirror at E0000-EFFFF).
  ROM_LOAD16_BYTE( "78x7460.bin", 0xf0000, 0x8000, CRC(1db4bd8f) SHA1(7be669fbb998d8b4626fefa7cd1208d3b2a88c31))
  ROM_LOAD16_BYTE( "78x7461.bin", 0xf0001, 0x8000, CRC(be14b453) SHA1(ec7c10087dbd53f9c6d1174e8f14212e2aec1818))

6181028.bin: First 6MHz IBM 5170 PC/AT BIOS. visible in even memory at F0000-FFFFF.
6181029.bin: First 6MHz IBM 5170 PC/AT BIOS. visible in odd memory at F0000-FFFFF.
  ROM_LOAD16_BYTE( "6181028.bin", 0xf0000, 0x8000, CRC(f6573f2a) SHA1(3e52cfa6a6a62b4e8576f4fe076c858c220e6c1a))
  ROM_LOAD16_BYTE( "6181029.bin", 0xf0001, 0x8000, CRC(7075fbb2) SHA1(a7b885cfd38710c9bc509da1e3ba9b543a2760be))

6480090.bin: Second 6MHz IBM 5170 PC/AT BIOS. visible in even memory at F0000-FFFFF.
6480091.bin: Second 6MHz IBM 5170 PC/AT BIOS. visible in odd memory at F0000-FFFFF.
  ROM_LOAD16_BYTE( "6480090.bin", 0xf0000, 0x8000, CRC(99703aa9) SHA1(18022e93a0412c8477e58f8c61a87718a0b9ab0e))
  ROM_LOAD16_BYTE( "6480091.bin", 0xf0001, 0x8000, CRC(013ef44b) SHA1(bfa15d2180a1902cb6d38c6eed3740f5617afd16))

62x0820.bin: 8MHz IBM 5170 PC/AT BIOS. visible in even memory at F0000-FFFFF. (PROM)
62x0821.bin: 8MHz IBM 5170 PC/AT BIOS. visible in odd memory at F0000-FFFFF. (PROM)
  ROM_LOAD( "62x0820.bin", 0xf0000, 0x8000, CRC(e9cc3761) SHA1(ff9373c1a1f34a32fb6acdabc189c61b01acf9aa))
  ROM_LOAD( "62x0821.bin", 0xf0001, 0x8000, CRC(b5978ccb) SHA1(2a1aeb9ae3cd7e60fc4c383ca026208b82156810))

61x9266.bin: 8MHz IBM 5170 PC/AT BIOS. visible in even memory at F0000-FFFFF. (EPROM)
61x9265.bin: 8MHz IBM 5170 PC/AT BIOS. visible in odd memory at F0000-FFFFF. (EPROM)
  ROM_LOAD( "61x9265.bin", 0xf0001, 0x8000, CRC(c32713e4) SHA1(22ed4e2be9f948682891e2fd056a97dbea01203c))
  ROM_LOAD( "61x9266.bin", 0xf0000, 0x8000, CRC(4995be7a) SHA1(8e8e5c863ae3b8c55fd394e345d8cca48b6e575c))


5788005.bin: IBM MDA/CGA font. Not mapped in PC memory. (American manufacture, otherwise similar to the European manufacture)
  ROM_LOAD( "5788005.bin", 0x0000, 0x2000, CRC(0bf56d70) SHA1(c2a8b10808bf51a3c123ba3eb1e9dd608231916f))

6359300.bin: IBM MDA/CGA font. Not mapped in PC memory. (European manufacture, otherwise similar to the American manufacture)
  ROM_LOAD( "6359300.bin", 0x0000, 0x2000, CRC(0bf56d70) SHA1(c2a8b10808bf51a3c123ba3eb1e9dd608231916f))

4733197.bin: IBM MDA/CGA Alternative font. Not mapped in PC memory.
  ROM_LOAD( "4733197.bin", 0x0000, 0x2000, CRC(650c0f85) SHA1(0c8ac77248a9856d065b1c64658e712ea55b7507))

6277356.bin: IBM EGA ROM. Visible in memory at C3FFF-C0000.
  ROM_LOAD( "6277356.bin", 0x0000, 0x4000, CRC(dc146448) SHA1(dc0794499b3e499c5777b3aa39554bbf0f2cc19b))

8222554.bin: IBM VGA ROM. Visible in memory at C0000-C5FFF.
  ROM_LOAD( "8222554.bin", 0x0000, 0x6000, CRC(6c12d745) SHA1(c0156607100e377c6c9563f23967409dab3ab92b))

5000059.bin: First version Hard drive controller ROM. Visible in memory at C8000-C9FFF.
  ROM_LOAD( "5000059.bin", 0xc8000, 0x2000, CRC(03e0ee9a) SHA1(6691be4f6a8d690c696ad8b259708d3e7e87ad89))

6359121.bin: First version Hard drive controller ROM. Visible in memory at C8000-C9FFF.
  ROM_LOAD( "6359121.bin", 0xc8000, 0x2000, CRC(03e0ee9a) SHA1(6691be4f6a8d690c696ad8b259708d3e7e87ad89))

59x7291.bin: Second version Hard drive controller ROM. Visible in memory at C8000-C8FFF.
  ROM_LOAD( "59x7291.bin", 0xc8000, 0x1000, CRC(25920437) SHA1(de970bcc5c6f1b588fbc4c76617165ce8eb2bf1d))

104839e.bin: Hard drive controller Z80 firmware ROM. Not mapped in PC memory. (Mapped in Z80 microcontroller memory at 0000-7FFF)
  ROM_LOAD( "104839e.bin", 0x0000, 0x1000, CRC(3ad32fcc) SHA1(0127fa520aaee91285cb46a640ed835b4554e4b3))

6323581.bin: 3270 Keyboard adapter ROM. The first 0x800 bytes visible in memory at C0000-C07FF. The later 0x1800 bytes visible in memory at CA000-CB7FF.
  ROM_LOAD( "6323581.bin", 0xc0000, 0x2000, CRC(cf323cbd) SHA1(93c1ef2ede02772a46dab075c32e179faa045f81))

1504161.bin: 3270 Character ROM (pixels 0-7). Not mapped in PC memory.
1504162.bin: 3270 Character ROM (pixel 8). Not mapped in PC memory.
  ROM_LOAD( "1504161.bin", 0x0000, 0x2000, CRC(d9246cf5) SHA1(2eaed495893a4e6649b04d10dada7b5ef4abd140))
  ROM_LOAD( "1504162.bin", 0x2000, 0x2000, CRC(59e1dc32) SHA1(337b5cced203345a5acfb02532d6b5f526902ee7))

6137323.bin: Professional Graphics Controller, first half of x86 firmware. Not mapped in PC memory. (Mapped in 8088-2 microcontroller memory at 00000-07FFF)
6137322.bin: Professional Graphics Controller, second half of x86 firmware. Not mapped in PC memory. (Mapped in 8088-2 microcontroller memory at 08000-0FFFF and any address within (8-F)(0-F)(3/7/B/F)00-(8-F)(0-F)(3/7/B/F)FF)
___MISSING___ 6137560.bin: Professional Graphics Controller, CGA emulation font. Not mapped in PC memory.
  ROM_LOAD( "6137322.bin", 0x8000, 0x8000, CRC(5e6cc82f) SHA1(45b3ffb5a9c51986862f8d47b3e03dcaaf4073d5))
  ROM_LOAD( "6137323.bin", 0x0000, 0x8000, CRC(f564f342) SHA1(c5ef17fd1569043cb59f61faf828ea8b0ee95526))


XC215 C 0.bin: IBM Music feature card Z80 firmware ROM. Not mapped in PC memory. (Mapped in Z80 microcontroller memory at 0000-7FFF)
  ROM_LOAD( "xc215 c 0.bin", 0x0000, 0x8000, CRC(28c58a4f) SHA1(e7edf28d20e6c146e3144526c89cd6beea64663b))

XT U44 IBM.bin: IBM 5160 PC/XT Bank-selection decoding ROM (256x4 bit). Not mapped in PC memory.
  ROM_LOAD( "xt u44 ibm.bin",0x0000, 0x0080, CRC(60ebb6e5) SHA1(e96a83b2fd6a231235374272a99353ab362b8e37))

1503033.bin: IBM 5170 PC/AT keyboard controller i8042 firmware. Not mapped in PC memory.
  ROM_LOAD( "1503033.bin", 0x0000, 0x0800, CRC(5a81c0d2) SHA1(0100f8789fb4de74706ae7f9473a12ec2b9bd729))

1503099.bin: IBM AT 84-key Keyboard firmware. Not mapped in PC memory.
  ROM_LOAD( "1503099.bin", 0x0000, 0x0400, CRC(1e921f37) SHA1(5f722bdb3b57f5a532c02a5c3f78f30d785796f2))

1385001.bin: IBM Terminal 122-key Keyboard firmware. Not mapped in PC memory.
  ROM_LOAD( "1385001.bin", 0x0000, 0x0400, CRC(c19767e9) SHA1(a3701e4617383a4de0fd5e2e86c4b74beaf94a7b))

30F9580.bin: IBM PS/2 model 30 286 even.
30F9579.bin: IBM PS/2 model 30 286 odd.
  ROM_LOAD( "30f9579.bin", 0x0000, 0x10000, CRC(1448d3cb) SHA1(13fa26d895ce084278cd5ab1208fc16c80115ebe))
  ROM_LOAD( "30f9580.bin", 0x0000, 0x10000, CRC(9965a634) SHA1(c237b1760f8a4561ec47dc70fe2e9df664e56596))

90X7415.bin: IBM PS/2 model 25/30 external FDD support adapter. visible in memory at C8000-C9FFF.
  ROM_LOAD( "90x7415.bin", 0x0000, 0x2000, CRC(02d28556) SHA1(5543a8634f90a9141cf95f6a13c71be7778ee2a1))


***************************************************************************/


#include "emu.h"
#include "cpu/i86/i86.h"
#include "machine/ram.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "machine/genpc.h"
#include "softlist_dev.h"


namespace {

class ibmpc_state : public driver_device
{
public:
	ibmpc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag) ,
		m_maincpu(*this, "maincpu") { }

	void ibm5160(machine_config &config);
	void ibm5150(machine_config &config);
	void ibm5140(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	void pc8_io(address_map &map) ATTR_COLD;
	void pc8_map(address_map &map) ATTR_COLD;
};

void ibmpc_state::pc8_map(address_map &map)
{
	map.unmap_value_high();
	map(0xf0000, 0xfffff).rom().region("bios", 0);
}

void ibmpc_state::pc8_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m("mb", FUNC(ibm5160_mb_device::map));
}

static DEVICE_INPUT_DEFAULTS_START(cga)
	DEVICE_INPUT_DEFAULTS("DSW0",0x30, 0x20)
DEVICE_INPUT_DEFAULTS_END

void ibmpc_state::ibm5150(machine_config &config)
{
	/* basic machine hardware */
	I8088(config, m_maincpu, XTAL(14'318'181)/3);
	m_maincpu->set_addrmap(AS_PROGRAM, &ibmpc_state::pc8_map);
	m_maincpu->set_addrmap(AS_IO, &ibmpc_state::pc8_io);
	m_maincpu->set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	ibm5150_mb_device &mb(IBM5150_MOTHERBOARD(config, "mb"));
	mb.set_cputag(m_maincpu);
	mb.int_callback().set_inputline(m_maincpu, 0);
	mb.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	mb.kbdclk_callback().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	mb.kbddata_callback().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));
	mb.set_input_default(DEVICE_INPUT_DEFAULTS_NAME(cga));

	ISA8_SLOT(config, "isa1", 0, "mb:isa", pc_isa8_cards, "cga", false); // FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa2", 0, "mb:isa", pc_isa8_cards, "com", false);
	ISA8_SLOT(config, "isa3", 0, "mb:isa", pc_isa8_cards, "fdc_xt", false);
	ISA8_SLOT(config, "isa4", 0, "mb:isa", pc_isa8_cards, "hdc", false);
	ISA8_SLOT(config, "isa5", 0, "mb:isa", pc_isa8_cards, nullptr, false);

	/* keyboard */
	pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_KEYTRONIC_PC3270));
	kbd.out_clock_cb().set("mb", FUNC(ibm5150_mb_device::keyboard_clock_w));
	kbd.out_data_cb().set("mb", FUNC(ibm5150_mb_device::keyboard_data_w));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("640K").set_extra_options("16K, 32K, 48K, 64K, 96K, 128K, 196K, 256K, 384K, 512K");

	/* software lists */
	SOFTWARE_LIST(config, "disk_list").set_original("ibm5150");
	SOFTWARE_LIST(config, "cass_list").set_original("ibm5150_cass");
	SOFTWARE_LIST(config, "hdd_list").set_original("ibm5150_hdd");
}


void ibmpc_state::ibm5140(machine_config &config)
{
	ibm5150(config);
	/* software lists */
	SOFTWARE_LIST(config.replace(), "disk_list").set_original("ibm5140");
}


void ibmpc_state::ibm5160(machine_config &config)
{
	/* basic machine hardware */
	I8088(config, m_maincpu, XTAL(14'318'181)/3);
	m_maincpu->set_addrmap(AS_PROGRAM, &ibmpc_state::pc8_map);
	m_maincpu->set_addrmap(AS_IO, &ibmpc_state::pc8_io);
	m_maincpu->set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	ibm5160_mb_device &mb(IBM5160_MOTHERBOARD(config, "mb"));
	mb.set_cputag(m_maincpu);
	mb.int_callback().set_inputline(m_maincpu, 0);
	mb.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	mb.kbdclk_callback().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	mb.kbddata_callback().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));
	mb.set_input_default(DEVICE_INPUT_DEFAULTS_NAME(cga));

	ISA8_SLOT(config, "isa1", 0, "mb:isa", pc_isa8_cards, "cga", false); // FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa2", 0, "mb:isa", pc_isa8_cards, "com", false);
	ISA8_SLOT(config, "isa3", 0, "mb:isa", pc_isa8_cards, "fdc_xt", false);
	ISA8_SLOT(config, "isa4", 0, "mb:isa", pc_isa8_cards, "hdc", false);
	ISA8_SLOT(config, "isa5", 0, "mb:isa", pc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa6", 0, "mb:isa", pc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa7", 0, "mb:isa", pc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa8", 0, "mb:isa", pc_isa8_cards, nullptr, false);

	/* keyboard */
	pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_IBM_PC_XT_83));
	kbd.out_clock_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_clock_w));
	kbd.out_data_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_data_w));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("640K").set_extra_options("64K, 128K, 256K, 512K");

	/* software lists */
	SOFTWARE_LIST(config, "pc_disk_list").set_original("ibm5150");
	SOFTWARE_LIST(config, "pc_hdd_list").set_original("ibm5150_hdd");
}


ROM_START( ibm5150 )
	ROM_REGION(0x10000,"bios", 0)
//  ROM_LOAD("600963.u12", 0xc8000, 0x02000, CRC(f3daf85f) SHA1(3bd29538832d3084cbddeec92593988772755283))  /* Tandon/Western Digital Fixed Disk Adapter 600963-001__TYPE_5.U12.2764.bin - Meant for an IBM PC or XT which lacked bios support for HDDs */

	/* Xebec 1210 and 1220 Z80-based ST409/ST412 MFM controllers */
//  ROM_LOAD("5000059.12d", 0xc8000, 0x02000, CRC(03e0ee9a) SHA1(6691be4f6a8d690c696ad8b259708d3e7e87ad89)) /* Xebec 1210 IBM OEM Fixed Disk Adapter - Revision 1, supplied with rev1 and rev2 XT, and PC/3270. supports 4 hdd types, selectable by jumpers (which on the older XTs were usually soldered to one setting) */
//  ROM_LOAD("62x0822.12d", 0xc8000, 0x02000, CRC(4cdd2193) SHA1(fe8f88333b5e13e170bf637a9a0090383dee454d)) /* Xebec 1210 IBM OEM Fixed Disk Adapter 62X0822__(M)_AMI_8621MAB__S68B364-P__(C)IBM_CORP_1982,1985__PHILIPPINES.12D.2364.bin - Revision 2, supplied with rev3 and rev4 XT. supports 4 hdd types, selectable by jumpers (which unlike the older XTs, were changeable without requiring soldering )*/
//  ROM_LOAD("unknown.12d", 0xc8000, 0x02000, NO_DUMP ) /* Xebec 1210 Non-IBM/Retail Fixed Disk Adapter - supports 4 hdd types, selectable by jumpers (changeable without soldering) */
//  ROM_LOAD("unknown.???", 0xc8000, 0x02000, NO_DUMP ) /* Xebec 1220 Non-IBM/Retail Fixed Disk Adapter plus Floppy Disk Adapter - supports 4 hdd types, selectable by jumpers (changeable without soldering) */

	/* IBM PC 5150 (rev 3: 1501-476 10/27/82) 5-screw case 64-256k MB w/1501981 CGA Card, ROM Basic 1.1 */
	ROM_DEFAULT_BIOS( "rev3" )

	ROM_SYSTEM_BIOS( 0, "rev3", "IBM PC 5150 1501476 10/27/82" )
	ROMX_LOAD("5000019.u29", 0x6000, 0x2000, CRC(80d3cf5d) SHA1(64769b7a8b60ffeefa04e4afbec778069a2840c9), ROM_BIOS(0))        /* ROM Basic 1.1 F6000-F7FFF; IBM P/N: 5000019, FRU: 6359109 */
	ROMX_LOAD("5000021.u30", 0x8000, 0x2000, CRC(673a4acc) SHA1(082ae803994048e225150f771794ca305f73d731), ROM_BIOS(0))        /* ROM Basic 1.1 F8000-F9FFF; IBM P/N: 5000021, FRU: 6359111 */
	ROMX_LOAD("5000022.u31", 0xa000, 0x2000, CRC(aac3fc37) SHA1(c9e0529470edf04da093bb8c8ae2536c688c1a74), ROM_BIOS(0))        /* ROM Basic 1.1 FA000-FBFFF; IBM P/N: 5000022, FRU: 6359112 */
	ROMX_LOAD("5000023.u32", 0xc000, 0x2000, CRC(3062b3fc) SHA1(5134dd64721cbf093d059ee5d3fd09c7f86604c7), ROM_BIOS(0))        /* ROM Basic 1.1 FC000-FDFFF; IBM P/N: 5000023, FRU: 6359113 */
	ROMX_LOAD("1501476.u33", 0xe000, 0x2000, CRC(e88792b3) SHA1(40fce6a94dda4328a8b608c7ae2f39d1dc688af4), ROM_BIOS(0))

	/* IBM PC 5150 (rev 1: 04/24/81) 2-screw case 16-64k MB w/MDA Card, ROM Basic 1.0 */
	ROM_SYSTEM_BIOS( 1, "rev1", "IBM PC 5150 5700051 04/24/81" )
	ROMX_LOAD("5700019.u29", 0x6000, 0x2000, CRC(b59e8f6c) SHA1(7a5db95370194c73b7921f2d69267268c69d2511), ROM_BIOS(1))        /* ROM Basic 1.0 F6000-F7FFF */
	ROMX_LOAD("5700027.u30", 0x8000, 0x2000, CRC(bfff99b8) SHA1(ca2f126ba69c1613b7b5a4137d8d8cf1db36a8e6), ROM_BIOS(1))        /* ROM Basic 1.0 F8000-F9FFF */
	ROMX_LOAD("5700035.u31", 0xa000, 0x2000, CRC(9fe4ec11) SHA1(89af8138185938c3da3386f97d3b0549a51de5ef), ROM_BIOS(1))        /* ROM Basic 1.0 FA000-FBFFF */
	ROMX_LOAD("5700043.u32", 0xc000, 0x2000, CRC(ea2794e6) SHA1(22fe58bc853ffd393d5e2f98defda7456924b04f), ROM_BIOS(1))        /* ROM Basic 1.0 FC000-FDFFF */
	ROMX_LOAD("5700051.u33", 0xe000, 0x2000, CRC(12d33fb8) SHA1(f046058faa016ad13aed5a082a45b21dea43d346), ROM_BIOS(1))

	/* IBM PC 5150 (rev 2: 10/19/81) 2-screw case, 16-64k MB w/MDA Card, ROM Basic 1.0 */
	ROM_SYSTEM_BIOS( 2, "rev2", "IBM PC 5150 5700671 10/19/81" )
	ROMX_LOAD("5700019.u29", 0x6000, 0x2000, CRC(b59e8f6c) SHA1(7a5db95370194c73b7921f2d69267268c69d2511), ROM_BIOS(2))        /* ROM Basic 1.0 F6000-F7FFF */
	ROMX_LOAD("5700027.u30", 0x8000, 0x2000, CRC(bfff99b8) SHA1(ca2f126ba69c1613b7b5a4137d8d8cf1db36a8e6), ROM_BIOS(2))        /* ROM Basic 1.0 F8000-F9FFF */
	ROMX_LOAD("5700035.u31", 0xa000, 0x2000, CRC(9fe4ec11) SHA1(89af8138185938c3da3386f97d3b0549a51de5ef), ROM_BIOS(2))        /* ROM Basic 1.0 FA000-FBFFF */
	ROMX_LOAD("5700043.u32", 0xc000, 0x2000, CRC(ea2794e6) SHA1(22fe58bc853ffd393d5e2f98defda7456924b04f), ROM_BIOS(2))        /* ROM Basic 1.0 FC000-FDFFF */
	ROMX_LOAD("5700671.u33", 0xe000, 0x2000, CRC(b7d4ec46) SHA1(bdb06f846c4768f39eeff7e16b6dbff8cd2117d2), ROM_BIOS(2))

	/* Landmark/Supersoft Diagnostics ROM */
	ROM_SYSTEM_BIOS( 3, "pclandmark", "Landmark/Supersoft Diagnostics" )
	ROMX_LOAD("5000019.u29", 0x6000, 0x2000, CRC(80d3cf5d) SHA1(64769b7a8b60ffeefa04e4afbec778069a2840c9), ROM_BIOS(3))        /* ROM Basic 1.1 F6000-F7FFF; IBM P/N: 5000019, FRU: 6359109 */
	ROMX_LOAD("5000021.u30", 0x8000, 0x2000, CRC(673a4acc) SHA1(082ae803994048e225150f771794ca305f73d731), ROM_BIOS(3))        /* ROM Basic 1.1 F8000-F9FFF; IBM P/N: 5000021, FRU: 6359111 */
	ROMX_LOAD("5000022.u31", 0xa000, 0x2000, CRC(aac3fc37) SHA1(c9e0529470edf04da093bb8c8ae2536c688c1a74), ROM_BIOS(3))        /* ROM Basic 1.1 FA000-FBFFF; IBM P/N: 5000022, FRU: 6359112 */
	ROMX_LOAD("5000023.u32", 0xc000, 0x2000, CRC(3062b3fc) SHA1(5134dd64721cbf093d059ee5d3fd09c7f86604c7), ROM_BIOS(3))        /* ROM Basic 1.1 FC000-FDFFF; IBM P/N: 5000023, FRU: 6359113 */
	ROMX_LOAD("5150_or_5160_2764_8kb.bin", 0xe000, 0x2000, CRC(4e89a4d8) SHA1(39a28fb2fe9f1aeea24ed2c0255cebca76e37ed7), ROM_BIOS(3))

	/* Z80 on the Xebec 1210 and 1220 Hard Disk Controllers */
//  ROM_REGION(0x10000, "cpu1", 0)
//  ROM_LOAD("104839re.12a", 0x0000, 0x1000, CRC(3ad32fcc) SHA1(0127fa520aaee91285cb46a640ed835b4554e4b3))  /* Xebec 1210 IBM OEM Hard Disk Controller, silkscreened "104839RE // COPYRIGHT // XEBEC 1986" - Common for both XEBEC 1210 IBM OEM revisions. Some cards have the rom marked 104839E instead (John Eliott's card is like this), but contents are the same. */
//  /* Other versions probably exist for the non-IBM/Retail 1210 and the 1220 */

	/* IBM 1501981(CGA) and 1501985(MDA) Character rom */
	ROM_REGION(0x2000,"gfx1", 0)
	ROM_LOAD("5788005.u33", 0x00000, 0x2000, CRC(0bf56d70) SHA1(c2a8b10808bf51a3c123ba3eb1e9dd608231916f)) /* "AMI 8412PI // 5788005 // (C) IBM CORP. 1981 // KOREA" */
ROM_END
ROM_START( ibm5155 )
	ROM_REGION(0x10000,"bios", 0)
	ROM_LOAD("5000027.u19", 0x0000, 0x8000, CRC(fc982309) SHA1(2aa781a698a21c332398d9bc8503d4f580df0a05))
	ROM_LOAD("1501512.u18", 0x8000, 0x8000, CRC(79522c3d) SHA1(6bac726d8d033491d52507278aa388ec04cf8b7e))
	/* IBM 1501981(CGA) and 1501985(MDA) Character rom */
	ROM_REGION(0x2000,"gfx1", 0)
	ROM_LOAD("5788005.u33", 0x00000, 0x2000, CRC(0bf56d70) SHA1(c2a8b10808bf51a3c123ba3eb1e9dd608231916f)) /* "AMI 8412PI // 5788005 // (C) IBM CORP. 1981 // KOREA" */
ROM_END
ROM_START( ibm5140 )
	ROM_REGION(0x10000,"bios", 0)
	ROM_LOAD("7396917.bin", 0x0000, 0x08000, CRC(95c35652) SHA1(2bdac30715dba114fbe0895b8b4723f8dc26a90d))
	ROM_LOAD("7396918.bin", 0x8000, 0x08000, CRC(1b4202b0) SHA1(4797ff853ba1675860f293b6368832d05e2f3ea9))
	/* IBM 1501981(CGA) and 1501985(MDA) Character rom */
	ROM_REGION(0x2000,"gfx1", 0)
	ROM_LOAD("5788005.u33", 0x00000, 0x2000, CRC(0bf56d70) SHA1(c2a8b10808bf51a3c123ba3eb1e9dd608231916f)) /* "AMI 8412PI // 5788005 // (C) IBM CORP. 1981 // KOREA" */
ROM_END
#ifdef UNUSED_DEFINITION
ROM_START( ibmpca )
	ROM_REGION(0x10000,"bios",0)
	ROM_LOAD("basicc11.f6", 0x6000, 0x2000, CRC(80d3cf5d) SHA1(64769b7a8b60ffeefa04e4afbec778069a2840c9))
	ROM_LOAD("basicc11.f8", 0x8000, 0x2000, CRC(673a4acc) SHA1(082ae803994048e225150f771794ca305f73d731))
	ROM_LOAD("basicc11.fa", 0xa000, 0x2000, CRC(aac3fc37) SHA1(c9e0529470edf04da093bb8c8ae2536c688c1a74))
	ROM_LOAD("basicc11.fc", 0xc000, 0x2000, CRC(3062b3fc) SHA1(5134dd64721cbf093d059ee5d3fd09c7f86604c7))
	ROM_LOAD("pc081682.bin", 0xe000, 0x2000, CRC(5c3f0256) SHA1(b42c78abd0a9c630a2f972ad2bae46d83c3a2a09))

	/* IBM 1501981(CGA) and 1501985(MDA) Character rom */
	ROM_REGION(0x2000,"gfx1", 0)
	ROM_LOAD("5788005.u33", 0x00000, 0x2000, CRC(0bf56d70) SHA1(c2a8b10808bf51a3c123ba3eb1e9dd608231916f)) /* "AMI 8412PI // 5788005 // (C) IBM CORP. 1981 // KOREA" */
ROM_END
#endif


ROM_START( ibm5160 )
	ROM_REGION(0x10000, "bios", 0)
//  ROM_LOAD("600963.u12", 0xc8000, 0x02000, CRC(f3daf85f) SHA1(3bd29538832d3084cbddeec92593988772755283))  /* Tandon/Western Digital Fixed Disk Adapter 600963-001__TYPE_5.U12.2764.bin */

	/* PC/3270 has a 3270 keyboard controller card, plus a rom on that card to tell the pc how to run it.
	    * Unlike the much more complex keyboard controller used in the AT/3270, this one only has one rom,
	      a motorola made "(M)1503828 // XE // 8434A XM // SC81155P" custom (an MCU?; the more complicated
	      3270/AT keyboard card uses this same exact chip), an 8254, and some logic chips.
	      Thanks to high resolution pictures provided by John Elliott, I can see that the location of the
	              chips is unlabeled (except for by absolute pin position on the back), and there are no pals or proms.
	    * The board is stickered "2683114 // 874999 // 8446 SU" on the front.
	    * The board has a single DE-9 connector where the keyboard dongle connects to.
	    * The keyboard dongle has two connectors on it: a DIN-5 connector which connects to the Motherboard's
	      keyboard port, plus an RJ45-lookalike socket which the 3270 keyboard connects to.
	    * The rom is mapped very strangely to avoid hitting the hard disk controller:
	      The first 0x800 bytes appear at C0000-C07FF, and the last 0x1800 bytes appear at 0xCA000-CB7FF
	*/
//  ROM_LOAD("6323581.bin", 0xc0000, 0x00800, CRC(cf323cbd) SHA1(93c1ef2ede02772a46dab075c32e179faa045f81))
//  ROM_LOAD("6323581.bin", 0xca000, 0x01800, CRC(cf323cbd) SHA1(93c1ef2ede02772a46dab075c32e179faa045f81) ROM_SKIP(0x800))

	/* Xebec 1210 and 1220 Z80-based ST409/ST412 MFM controllers */
//  ROM_LOAD("5000059.12d", 0xc8000, 0x02000, CRC(03e0ee9a) SHA1(6691be4f6a8d690c696ad8b259708d3e7e87ad89)) /* Xebec 1210 IBM OEM Fixed Disk Adapter - Revision 1, supplied with rev1 and rev2 XT, and PC/3270. supports 4 hdd types, selectable by jumpers (which on the older XTs were usually soldered to one setting) */
//  ROM_LOAD("62x0822.12d", 0xc8000, 0x02000, CRC(4cdd2193) SHA1(fe8f88333b5e13e170bf637a9a0090383dee454d)) /* Xebec 1210 IBM OEM Fixed Disk Adapter 62X0822__(M)_AMI_8621MAB__S68B364-P__(C)IBM_CORP_1982,1985__PHILIPPINES.12D.2364.bin - Revision 2, supplied with rev3 and rev4 XT. supports 4 hdd types, selectable by jumpers (which unlike the older XTs, were changeable without requiring soldering )*/
//  ROM_LOAD("unknown.12d", 0xc8000, 0x02000, NO_DUMP ) /* Xebec 1210 Non-IBM/Retail Fixed Disk Adapter - supports 4 hdd types, selectable by jumpers (changeable without soldering) */
//  ROM_LOAD("unknown.???", 0xc8000, 0x02000, NO_DUMP ) /* Xebec 1220 Non-IBM/Retail Fixed Disk Adapter plus Floppy Disk Adapter - supports 4 hdd types, selectable by jumpers (changeable without soldering) */


	ROM_DEFAULT_BIOS( "rev4" )

	ROM_SYSTEM_BIOS( 0, "rev1", "IBM XT 5160 08/16/82" )    /* This is a very rare ROM revision and may have only appeared on XT prototypes. Only 2 machines with this ROM set have been observed in the wild so far. */
	ROMX_LOAD("5000027.u19", 0x0000, 0x8000, CRC(fc982309) SHA1(2aa781a698a21c332398d9bc8503d4f580df0a05), ROM_BIOS(0) ) /* silkscreen "MK37050N-4 // 5000027 // ZA // (C) IBM 1982 // D MALAYSIA // 8248 B" - FRU: 6359116 - Alt Silkscreen (from yesterpc.com): "(M) // 5000027 // (C) 1983 IBM CORP // X E // 8425B NM"; Contents repeat 4 times */
	ROMX_LOAD("5000026.u18", 0x8000, 0x8000, CRC(3c9b0ac3) SHA1(271c9f4cef5029a1560075550b67c3395db09fef), ROM_BIOS(0) ) /* ceramic chip; silkscreen "MK38022 P-25 // 5000026 // ZA // (C) IBM 1982 // D DALLAS // 8304" */

	ROM_SYSTEM_BIOS( 1, "rev2", "IBM XT 5160 11/08/82" )    /* Same as PC 5155 BIOS and PC/3270 BIOS */
	ROMX_LOAD("5000027.u19", 0x0000, 0x8000, CRC(fc982309) SHA1(2aa781a698a21c332398d9bc8503d4f580df0a05), ROM_BIOS(1) ) /* silkscreen "MK37050N-4 // 5000027 // ZA // (C) IBM 1982 // D MALAYSIA // 8248 B" - FRU: 6359116 - Alt Silkscreen (from yesterpc.com): "(M) // 5000027 // (C) 1983 IBM CORP // X E // 8425B NM"; Contents repeat 4 times */
	ROMX_LOAD("1501512.u18", 0x8000, 0x8000, CRC(79522c3d) SHA1(6bac726d8d033491d52507278aa388ec04cf8b7e), ROM_BIOS(1) ) /* silkscreen "MK38036N-25 // 1501512 // ZA // (C)IBM CORP // 1981,1983 // D MALAYSIA // 8438 AP"*/

	ROM_SYSTEM_BIOS( 2, "rev3", "IBM XT 5160 01/10/86" )    /* Has enhanced keyboard support and a 3.5" drive */
	ROMX_LOAD("62x0854.u19", 0x0000, 0x8000, CRC(b5fb0e83) SHA1(937b43759ffd472da4fb0fe775b3842f5fb4c3b3), ROM_BIOS(2) )
	ROMX_LOAD("62x0851.u18", 0x8000, 0x8000, CRC(1054f7bd) SHA1(e7d0155813e4c650085144327581f05486ed1484), ROM_BIOS(2) )

	ROM_SYSTEM_BIOS( 3, "rev4", "IBM XT 5160 05/09/86" )    /* Minor bugfixes to keyboard code, supposedly */
	ROMX_LOAD("68x4370.u19", 0x0000, 0x8000, CRC(758ff036) SHA1(045e27a70407d89b7956ecae4d275bd2f6b0f8e2), ROM_BIOS(3))
	ROMX_LOAD("62x0890.u18", 0x8000, 0x8000, CRC(4f417635) SHA1(daa61762d3afdd7262e34edf1a3d2df9a05bcebb), ROM_BIOS(3))

	ROM_SYSTEM_BIOS( 4, "xtlandmark", "Landmark/Supersoft Diagnostics" )
	ROMX_LOAD("62x0854.u19", 0x0000, 0x8000, CRC(b5fb0e83) SHA1(937b43759ffd472da4fb0fe775b3842f5fb4c3b3), ROM_BIOS(4) )    /* instructions say to leave this ROM in place */
	ROMX_LOAD("5150_or_5160_27256_32kb.bin", 0x8000, 0x8000, CRC(d3603216) SHA1(6691d33f43eddd3b0a6269ef985f5be8705ba55f), ROM_BIOS(4) )


	/* IBM 1501981(CGA) and 1501985(MDA) Character rom */
	ROM_REGION(0x2000,"gfx1", 0)
	ROM_LOAD("5788005.u33", 0x00000, 0x2000, CRC(0bf56d70) SHA1(c2a8b10808bf51a3c123ba3eb1e9dd608231916f)) /* "AMI 8412PI // 5788005 // (C) IBM CORP. 1981 // KOREA" */

	/* Z80 ROM on the Xebec 1210 and 1220 Hard Disk Controllers */
//  ROM_REGION(0x10000, "cpu1", 0)
//  ROM_LOAD("104839re.12a", 0x0000, 0x1000, CRC(3ad32fcc) SHA1(0127fa520aaee91285cb46a640ed835b4554e4b3))  /* Xebec 1210 IBM OEM Hard Disk Controller, silkscreened "104839RE // COPYRIGHT // XEBEC 1986" - Common for both XEBEC 1210 IBM OEM revisions. Some cards have the rom marked 104839E instead (John Eliott's card is like this), but contents are the same. */
//  /* Other versions probably exist for the non-IBM/Retail 1210 and the 1220 */


	/* PC/3270 and AT/3270 have a set of two (optionally 3) 3270PGC programmable graphics controller cards on them which
	       have 2 extra roms, plus a number of custom chips and at least one MCU.
	   Information on these three boards plus the keyboard interface can be found at:
	   http://www.seasip.info/VintagePC/5271.html
	    *** The descriptions below are based on the cards from the AT/3270, which are slightly
	        different from the PC/3270 cards. Changes between the PC/3270 and AT/3270 video cards are
	        listed:
	        1. The card lip edge is changed on the APA and PS cards to allow them to be placed
	           in a 16-bit ISA slot.
	        2. The Main Display board is exactly the same PCB (hence cannot be placed in a 16-bit
	           ISA slot), but the socket to the left of the 8255 is now populated, and has a lot of
	           rework wires connected to various places on the PCB.
	        3. The APA board was completely redone, and no longer has an 8254 (though it does have an
	           empty socket) and has ?twice as much memory on it? (not sure about this). The APA
	           board also now connects to both main board connectors 1 and 3, instead of only
	           connector 1.
	        4. The PS board has been minorly redone to allow clearance for a 16-bit ISA connector,
	           but no other significant chip changes were made. The connector 3 still exists on the
	           board but is unpopulated. Connector 2 still connects to the Main display board as
	           before.

	    ** The Main Display Board (with one 48-pin custom, two 40 pin customs at least one of which
	           is an MCU, four 2016BP-10 srams, an 8254 and an 8255 on it, a SCN2672 (PVTC), two crystals
	           (16.257MHz and 21.676MHz) plus two mask roms ) is stickered "61X6579 // 983623 // 6390 SU"
	           on the front.
	    *  The pcb is trace-marked "6320987" on both the front and back.
	    *  The card has a DE-9 connector on it for a monitor.
	    *  The customs are marked:
	       "1503192 // TC15G008P-0009 // JAPAN       8549A" (40 pins, at U52)
	       "1503193 // TC15G008AP-0020 // JAPAN       8610A" (48 pins, at U29)
	       "(M)1503194 // XE KGA005 // 8616N XM // SC81156P" (40 pins, at U36, likely an MCU)

	    ** The All Points Addressable (Frame buffer?) card (with 2 48-pin customs on it which are
	       probably gate arrays and not MCUs, an empty socket (28 pins, U46), an Intel Id2147H-3,
	       a bank of twelve 16k*4-bit inmos ims2620p-15 DRAMs (tms4416 equivalent), and an Intel
	       D2147K 4096*1 byte SRAM) is stickered
	       "6487836 // A24969 // 6400 SU" on the back.
	    *  The pcb is trace-marked "EC 999040" on the back, and silkscreened "RC 2682819" on the front
	    *  The customs are marked:
	       "6323259 // TC15G008AP-0028 // JAPAN       8606A" (48 pins, at U67)
	       "6323260 // TC15G022AP-0018 // JAPAN       8606A" (48 pins, at U45)

	    ** The optional Programmable Symbol Card (with an AMD AM9128-10PC, and six tms4416-15NL DRAMS,
	       and a fleet of discrete logic chips, but no roms, pals, or proms) is stickered
	       "6347750 // A24866 // 6285 SU" on the front.
	    *  The PCB is trace-marked "PROGAMMABLE SYMBOL P/N 6347751 // ASSY. NO. 6347750" on the front,
	       and trace-marked "|||CIM0286 ECA2466 // 94V-O" on the back.
	*/
//  ROM_REGION(0x4000,"gfx2", 0)
//      ROM_LOAD("1504161.u11", 0x00000, 0x2000, CRC(d9246cf5) SHA1(2eaed495893a4e6649b04d10dada7b5ef4abd140)) /* silkscreen: "AMI 8613MAJ // 9591-041 // S2364B // 1504161 // PHILIPPINES" - Purpose: Pixels 0 through 7 of built-in 3270 terminal font*/
//      ROM_LOAD("1504162.u26", 0x02000, 0x2000, CRC(59e1dc32) SHA1(337b5cced203345a5acfb02532d6b5f526902ee7)) /* silkscreen: "AMI 8607MAH // 9591-042 // S2364B // 1504162 // PHILIPPINES" - Purpose: Pixel 8 of built-in 3270 terminal font*/
ROM_END

} // anonymous namespace


/***************************************************************************

  Game driver(s)

***************************************************************************/

//    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY                            FULLNAME                   FLAGS
COMP( 1981, ibm5150, 0,       0,      ibm5150, 0,     ibmpc_state, empty_init, "International Business Machines", "IBM PC 5150",             0 )
COMP( 1982, ibm5155, ibm5150, 0,      ibm5150, 0,     ibmpc_state, empty_init, "International Business Machines", "IBM PC 5155",             0 )
COMP( 1985, ibm5140, ibm5150, 0,      ibm5140, 0,     ibmpc_state, empty_init, "International Business Machines", "IBM PC 5140 Convertible", MACHINE_NOT_WORKING )
COMP( 1982, ibm5160, ibm5150, 0,      ibm5160, 0,     ibmpc_state, empty_init, "International Business Machines", "IBM XT 5160",             0 )



ibmpcjr.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
#include "emu.h"

#include "cpu/i86/i86.h"
#include "imagedev/cassette.h"
#include "machine/i8255.h"
#include "machine/ins8250.h"
#include "machine/pc_lpt.h"
#include "machine/pckeybrd.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/ram.h"
#include "sound/sn76496.h"
#include "sound/spkrdev.h"
#include "pc_t1t.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "bus/isa/fdc.h"
#include "bus/pc_joy/pc_joy.h"
#include "bus/rs232/hlemouse.h"
#include "bus/rs232/rs232.h"
#include "bus/rs232/null_modem.h"
#include "bus/rs232/terminal.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class pcjr_state : public driver_device
{
public:
	pcjr_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_pic8259(*this, "pic8259"),
		m_pit8253(*this, "pit8253"),
		m_speaker(*this, "speaker"),
		m_cassette(*this, "cassette"),
		m_cart1(*this, "cartslot1"),
		m_cart2(*this, "cartslot2"),
		m_ram(*this, RAM_TAG),
		m_fdc(*this, "fdc"),
		m_keyboard(*this, "pc_keyboard")
	{ }

	void ibmpcjx(machine_config &config);
	void ibmpcjr(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void ibmpcjr_io(address_map &map) ATTR_COLD;
	void ibmpcjr_map(address_map &map) ATTR_COLD;
	void ibmpcjx_io(address_map &map) ATTR_COLD;
	void ibmpcjx_map(address_map &map) ATTR_COLD;

	TIMER_CALLBACK_MEMBER(delayed_irq);
	TIMER_CALLBACK_MEMBER(watchdog_expired);
	TIMER_CALLBACK_MEMBER(kb_signal);

	required_device<cpu_device> m_maincpu;
	required_device<pic8259_device> m_pic8259;
	required_device<pit8253_device> m_pit8253;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cassette;
	required_device<generic_slot_device> m_cart1;
	required_device<generic_slot_device> m_cart2;
	required_device<ram_device> m_ram;
	required_device<upd765a_device> m_fdc;
	required_device<pc_keyboard_device> m_keyboard;

	void out2_changed(int state);
	void keyb_interrupt(int state);

	void pc_nmi_enable_w(uint8_t data);
	uint8_t pcjr_nmi_enable_r();
	void pic8259_set_int_line(int state);

	void pcjr_ppi_portb_w(uint8_t data);
	uint8_t pcjr_ppi_portc_r();
	void pcjr_fdc_dor_w(uint8_t data);
	uint8_t pcjx_port_1ff_r();
	void pcjx_port_1ff_w(uint8_t data);
	void pcjx_set_bank(int unk1, int unk2, int unk3);

	std::pair<std::error_condition, std::string> load_cart(device_image_interface &image, generic_slot_device *slot);
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart1_load) { return load_cart(image, m_cart1); }
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart2_load) { return load_cart(image, m_cart2); }
	void pc_speaker_set_spkrdata(uint8_t data);

	uint8_t m_pc_spkrdata = 0;
	uint8_t m_pit_out2 = 0;
	uint8_t m_pcjr_dor = 0;
	uint8_t m_pcjx_1ff_count = 0;
	uint8_t m_pcjx_1ff_val = 0;
	uint8_t m_pcjx_1ff_bankval = 0;
	uint8_t m_pcjx_1ff_bank[20][2]{};
	int m_ppi_portc_switch_high = 0;
	uint8_t m_ppi_portb = 0;

	uint8_t m_pc_keyb_data = 0;
	uint8_t m_transferring = 0;
	uint8_t m_latch = 0;
	uint32_t m_raw_keyb_data = 0;
	int m_signal_count = 0;
	uint8_t m_nmi_enabled = 0;

	emu_timer *m_pc_int_delay_timer = nullptr;
	emu_timer *m_pcjr_watchdog = nullptr;
	emu_timer *m_keyb_signal_timer = nullptr;
};

static INPUT_PORTS_START( ibmpcjr )
	PORT_START("IN0") /* IN0 */
	PORT_BIT ( 0xf0, 0xf0,   IPT_UNUSED )
	PORT_BIT ( 0x08, 0x08,   IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("pcvideo_pcjr:screen", FUNC(screen_device::vblank))
	PORT_BIT ( 0x07, 0x07,   IPT_UNUSED )
INPUT_PORTS_END

void pcjr_state::machine_start()
{
	m_maincpu->space(AS_PROGRAM).install_ram(0, m_ram->size() - 1, m_ram->pointer());

	m_pc_int_delay_timer = timer_alloc(FUNC(pcjr_state::delayed_irq), this);
	m_pcjr_watchdog = timer_alloc(FUNC(pcjr_state::watchdog_expired), this);
	m_keyb_signal_timer = timer_alloc(FUNC(pcjr_state::kb_signal), this);
}

void pcjr_state::machine_reset()
{
	m_pc_spkrdata = 0;
	m_pit_out2 = 1;
	m_ppi_portc_switch_high = 0;
	m_ppi_portb = 0;
	m_pcjr_dor = 0;
	m_speaker->level_w(0);

	m_pcjx_1ff_count = 0;
	m_pcjx_1ff_val = 0;
	m_pcjx_1ff_bankval = 0;
	memset(m_pcjx_1ff_bank, 0, sizeof(m_pcjx_1ff_bank));

	m_transferring = 0;
	m_latch = 0;
	m_raw_keyb_data = 0;
	m_nmi_enabled = 0x80;
}

TIMER_CALLBACK_MEMBER(pcjr_state::delayed_irq)
{
	m_maincpu->set_input_line(0, param ? ASSERT_LINE : CLEAR_LINE);
}

TIMER_CALLBACK_MEMBER(pcjr_state::watchdog_expired)
{
	if (m_pcjr_dor & 0x20)
		m_pic8259->ir6_w(1);
	else
		m_pic8259->ir6_w(0);
}

TIMER_CALLBACK_MEMBER(pcjr_state::kb_signal)
{
	m_raw_keyb_data = m_raw_keyb_data >> 1;
	m_signal_count--;

	if (m_signal_count <= 0)
	{
		m_keyb_signal_timer->adjust(attotime::never, 0, attotime::never);
		m_transferring = 0;
	}
}

/*************************************************************
 *
 * PCJR pic8259 configuration
 *
 * Part of the PCJR CRT POST test at address F0452/F0454 writes
 * to the PIC enabling an IRQ which is then immediately fired,
 * however it is expected that the actual IRQ is taken one
 * instruction later (the irq bit is reset by the instruction
 * at F0454). Delaying taking of an IRQ by one instruction for
 * all cases breaks floppy emulation. This seems to be a really
 * tight corner case. For now we delay the IRQ by one instruction
 * only for the PCJR and only when it's inside the POST checks.
 *
 *************************************************************/

void pcjr_state::pic8259_set_int_line(int state)
{
	uint32_t pc = m_maincpu->pc();
	if ( (pc == 0xF0453) || (pc == 0xFF196) )
	{
		m_pc_int_delay_timer->adjust( m_maincpu->cycles_to_attotime(20), state );
	}
	else
	{
		m_maincpu->set_input_line(0, state ? ASSERT_LINE : CLEAR_LINE);
	}
}

/*************************************************************************
 *
 *      PC Speaker related
 *
 *************************************************************************/
void pcjr_state::pc_speaker_set_spkrdata(uint8_t data)
{
	m_pc_spkrdata = data ? 1 : 0;
	m_speaker->level_w(m_pc_spkrdata & m_pit_out2);
}

void pcjr_state::out2_changed(int state)
{
	m_pit_out2 = state ? 1 : 0;
	m_speaker->level_w(m_pc_spkrdata & m_pit_out2);
	m_cassette->output(state ? 1.0 : -1.0);
}

/*************************************************************
 *
 * PCJR NMI and raw keybaord handling
 *
 * raw signals on the keyboard cable:
 * ---_-b0b1b2b3b4b5b6b7pa----------------------
 *    | | | | | | | | | | |
 *    | | | | | | | | | | *--- 11 stop bits ( -- = 1 stop bit )
 *    | | | | | | | | | *----- parity bit ( 0 = _-, 1 = -_ )
 *    | | | | | | | | *------- bit 7 ( 0 = _-, 1 = -_ )
 *    | | | | | | | *--------- bit 6 ( 0 = _-, 1 = -_ )
 *    | | | | | | *----------- bit 5 ( 0 = _-, 1 = -_ )
 *    | | | | | *------------- bit 4 ( 0 = _-, 1 = -_ )
 *    | | | | *--------------- bit 3 ( 0 = _-, 1 = -_ )
 *    | | | *----------------- bit 2 ( 0 = _-, 1 = -_ )
 *    | | *------------------- bit 1 ( 0 = _-, 1 = -_ )
 *    | *--------------------- bit 0 ( 0 = _-, 1 = -_ )
 *    *----------------------- start bit (always _- )
 *
 * An entire bit lasts for 440 uSec, half bit time is 220 uSec.
 * Transferring an entire byte takes 21 x 440uSec. The extra
 * time of the stop bits is to allow the CPU to do other things
 * besides decoding keyboard signals.
 *
 * These signals get inverted before going to the PCJR
 * handling hardware. The sequence for the start then
 * becomes:
 *
 * __-_b0b1.....
 *   |
 *   *---- on the 0->1 transition of the start bit a keyboard
 *         latch signal is set to 1 and an NMI is generated
 *         when enabled.
 *         The keyboard latch is reset by reading from the
 *         NMI enable port (A0h).
 *
 *************************************************************/

void pcjr_state::keyb_interrupt(int state)
{
	int data;

	if (state && (data = m_keyboard->read()))
	{
		m_latch = 1;

		if(m_transferring)
			return;

		m_pc_keyb_data = data;

		/* Calculate the raw data */
		uint8_t parity = 0;
		for (int i = 0; i < 8; i++)
		{
			if (BIT(data, i))
			{
				parity ^= 1;
			}
		}
		m_raw_keyb_data = 0;
		m_raw_keyb_data = (m_raw_keyb_data << 2) | (parity ? 1 : 2);
		for (int i = 0; i < 8; i++)
		{
			m_raw_keyb_data = (m_raw_keyb_data << 2) | ((data & 0x80) ? 1 : 2);
			data <<= 1;
		}
		/* Insert start bit */
		m_raw_keyb_data = (m_raw_keyb_data << 2) | 1;
		m_signal_count = 20 + 22;

		/* we are now transferring a byte of keyboard data */
		m_transferring = 1;

		/* Set timer */
		m_keyb_signal_timer->adjust(attotime::from_usec(220), 0, attotime::from_usec(220));
		m_maincpu->set_input_line(INPUT_LINE_NMI, m_nmi_enabled && m_latch);
	}
}

uint8_t pcjr_state::pcjr_nmi_enable_r()
{
	m_latch = 0;
	m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
	return m_nmi_enabled;
}

void pcjr_state::pc_nmi_enable_w(uint8_t data)
{
	m_nmi_enabled = data & 0x80;
	m_maincpu->set_input_line(INPUT_LINE_NMI, m_nmi_enabled && m_latch);
}

void pcjr_state::pcjr_ppi_portb_w(uint8_t data)
{
	/* KB controller port B */
	m_ppi_portb = data;
	m_ppi_portc_switch_high = data & 0x08;
	m_pit8253->write_gate2(BIT(data, 0));
	pc_speaker_set_spkrdata(data & 0x02);

	m_cassette->change_state((data & 0x08) ? CASSETTE_MOTOR_DISABLED : CASSETTE_MOTOR_ENABLED, CASSETTE_MASK_MOTOR);
}

/*
 * Port C connections on a PCJR (notes from schematics):
 * PC0 - KYBD LATCH
 * PC1 - MODEM CD INSTALLED
 * PC2 - DISKETTE CD INSTALLED
 * PC3 - ATR CD IN
 * PC4 - cassette audio
 * PC5 - OUT2 from 8253
 * PC6 - KYBD IN
 * PC7 - (keyboard) CABLE CONNECTED
 */
uint8_t pcjr_state::pcjr_ppi_portc_r()
{
	int data = 0xff;

	data &= ~0x80;
	data &= ~0x04;      /* floppy drive installed */
	if (m_ram->size() > 64 * 1024)    /* more than 64KB ram installed */
		data &= ~0x08;
	data = (data & ~0x01) | (m_latch ? 0x01 : 0x00);
	if (!(m_ppi_portb & 0x08))
	{
		double tap_val = m_cassette->input();

		if (tap_val < 0)
		{
			data &= ~0x10;
		}
		else
		{
			data |= 0x10;
		}
	}
	else
	{
		if (m_ppi_portb & 0x01)
		{
			data = (data & ~0x10) | (m_pit_out2 ? 0x10 : 0x00);
		}
	}
	data = (data & ~0x20) | (m_pit_out2 ? 0x20 : 0x00);
	data = (data & ~0x40) | ((m_raw_keyb_data & 0x01) ? 0x40 : 0x00);

	return data;
}

void pcjr_state::pcjr_fdc_dor_w(uint8_t data)
{
	logerror("fdc: dor = %02x\n", data);
	uint8_t pdor = m_pcjr_dor;
	floppy_image_device *floppy0 = m_fdc->subdevice<floppy_connector>("0")->get_device();
	floppy_image_device *floppy1 = nullptr;

	if (m_fdc->subdevice("1"))
		floppy1 = m_fdc->subdevice<floppy_connector>("1")->get_device();
	m_pcjr_dor = data;

	if (floppy0)
		floppy0->mon_w(!(m_pcjr_dor & 1));
	if (floppy1)
		floppy1->mon_w(!(m_pcjr_dor & 2));

	if (m_pcjr_dor & 1)
		m_fdc->set_floppy(floppy0);
	else if (m_pcjr_dor & 2)
		m_fdc->set_floppy(floppy1);
	else
		m_fdc->set_floppy(nullptr);

	m_fdc->reset_w(!BIT(m_pcjr_dor, 7));

	if (m_pcjr_dor & 0x20)
	{
		if ((pdor & 0x40) && !(m_pcjr_dor & 0x40))
			m_pcjr_watchdog->adjust(attotime::from_seconds(3));
	}
	else
	{
		m_pcjr_watchdog->adjust(attotime::never);
		m_pic8259->ir6_w(0);
	}
}

// pcjx port 0x1ff, some info from Toshiya Takeda

void pcjr_state::pcjx_set_bank(int unk1, int unk2, int unk3)
{
	logerror("pcjx: 0x1ff 0:%02x 1:%02x 2:%02x\n", unk1, unk2, unk3);
}

void pcjr_state::pcjx_port_1ff_w(uint8_t data)
{
	switch (m_pcjx_1ff_count)
	{
	case 0:
		m_pcjx_1ff_bankval = data;
		m_pcjx_1ff_count++;
		break;
	case 1:
		m_pcjx_1ff_bank[m_pcjx_1ff_bankval & 0x1f][0] = data;
		m_pcjx_1ff_count++;
		break;
	case 2:
		m_pcjx_1ff_bank[m_pcjx_1ff_bankval & 0x1f][1] = data;
		m_pcjx_1ff_count = 0;
		pcjx_set_bank(m_pcjx_1ff_bankval, m_pcjx_1ff_bank[m_pcjx_1ff_bankval & 0x1f][0], data);
		break;
	}
}

uint8_t pcjr_state::pcjx_port_1ff_r()
{
	if (m_pcjx_1ff_count == 2)
		pcjx_set_bank(m_pcjx_1ff_bankval, m_pcjx_1ff_bank[m_pcjx_1ff_bankval & 0x1f][0], m_pcjx_1ff_bank[m_pcjx_1ff_bankval & 0x1f][1]);

	m_pcjx_1ff_count = 0;
	return 0x60; // expansion?
}

std::pair<std::error_condition, std::string> pcjr_state::load_cart(
		device_image_interface &image,
		generic_slot_device *slot)
{
	uint32_t size = slot->common_get_size("rom");
	bool imagic_hack = false;

	if (!image.loaded_through_softlist())
	{
		int header_size = 0;

		// Check for supported header sizes
		switch (size & 0x3ff)
		{
			case 0x80:
				header_size = 0x80;
				break;
			case 0x200:
				header_size = 0x200;
				break;
			default:
				return std::make_pair(image_error::INVALIDIMAGE, "Invalid header length (must be 128 bytes or 512 bytes)");
		}
		if ((size - header_size) == 0xa000)
		{
			// alloc 64K for the imagic carts, so as to handle the necessary mirroring
			size += 0x6000;
			imagic_hack = true;
		}

		size -= header_size;
		image.fseek(header_size, SEEK_SET);
	}

	slot->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	slot->common_load_rom(slot->get_rom_base(), size, "rom");

	if (imagic_hack)
	{
		// in this case the image consists of 2x8K chunks
		// the first chunk is unique, the second is repeated 4 times up to 0xa000 size

		// mirroring
		uint8_t *ROM = slot->get_rom_base();
		memcpy(ROM + 0xe000, ROM + 0x2000, 0x2000);
		memcpy(ROM + 0xc000, ROM + 0x2000, 0x2000);
		memcpy(ROM + 0xa000, ROM + 0x2000, 0x2000);
		memcpy(ROM + 0x8000, ROM + 0x2000, 0x2000);
		memcpy(ROM + 0x6000, ROM, 0x2000);
		memcpy(ROM + 0x4000, ROM, 0x2000);
		memcpy(ROM + 0x2000, ROM, 0x2000);
	}

	return std::make_pair(std::error_condition(), std::string());
}


static void pcjr_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
	device.option_add("35dd", FLOPPY_35_DD);
}

static void pcjr_com(device_slot_interface &device)
{
	device.option_add("microsoft_mouse", MSFT_HLE_SERIAL_MOUSE);
	device.option_add("logitech_mouse", LOGITECH_HLE_SERIAL_MOUSE);
	device.option_add("wheel_mouse", WHEEL_HLE_SERIAL_MOUSE);
	device.option_add("msystems_mouse", MSYSTEMS_HLE_SERIAL_MOUSE);
	device.option_add("rotatable_mouse", ROTATABLE_HLE_SERIAL_MOUSE);
	device.option_add("terminal",SERIAL_TERMINAL);
	device.option_add("null_modem",NULL_MODEM);
}

static const gfx_layout pc_8_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	512,                    /* 512 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static const gfx_layout kanji_layout =
{
	16, 16,                 /* 8 x 8 characters */
	RGN_FRAC(1,1),                  /* 512 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ STEP16(0,1) },
	/* y offsets */
	{ STEP16(0,16) },
	16*16                   /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_pcjr )
	GFXDECODE_ENTRY("gfx1", 0x0000, pc_8_charlayout, 3, 1)
GFXDECODE_END


void pcjr_state::ibmpcjr_map(address_map &map)
{
	map.unmap_value_high();
	map(0xb8000, 0xbffff).m("pcvideo_pcjr:vram", FUNC(address_map_bank_device::amap8));
	map(0xd0000, 0xdffff).r(m_cart2, FUNC(generic_slot_device::read_rom));
	map(0xe0000, 0xeffff).r(m_cart1, FUNC(generic_slot_device::read_rom));
	map(0xf0000, 0xfffff).rom().region("bios", 0);
}


void pcjr_state::ibmpcjr_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0020, 0x0021).rw(m_pic8259, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x0040, 0x0043).rw(m_pit8253, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x0060, 0x0063).rw("ppi8255", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x00a0, 0x00a0).rw(FUNC(pcjr_state::pcjr_nmi_enable_r), FUNC(pcjr_state::pc_nmi_enable_w));
	map(0x00c0, 0x00c0).w("sn76496", FUNC(sn76496_device::write));
	map(0x00f2, 0x00f2).w(FUNC(pcjr_state::pcjr_fdc_dor_w));
	map(0x00f4, 0x00f5).m(m_fdc, FUNC(upd765a_device::map));
	map(0x0200, 0x0207).rw("pc_joy", FUNC(pc_joy_device::joy_port_r), FUNC(pc_joy_device::joy_port_w));
	map(0x02f8, 0x02ff).rw("ins8250", FUNC(ins8250_device::ins8250_r), FUNC(ins8250_device::ins8250_w));
	map(0x0378, 0x037b).rw("lpt_0", FUNC(pc_lpt_device::read), FUNC(pc_lpt_device::write));
	map(0x03d0, 0x03df).r("pcvideo_pcjr", FUNC(pcvideo_pcjr_device::read)).w("pcvideo_pcjr", FUNC(pcvideo_pcjr_device::write));
}

void pcjr_state::ibmpcjx_map(address_map &map)
{
	map.unmap_value_high();
	map(0x80000, 0x9ffff).ram().share("vram"); // TODO: remove this part of vram hack
	map(0x80000, 0xb7fff).rom().region("kanji", 0);
	map(0xb8000, 0xbffff).m("pcvideo_pcjr:vram", FUNC(address_map_bank_device::amap8));
	map(0xd0000, 0xdffff).r(m_cart1, FUNC(generic_slot_device::read_rom));
	map(0xe0000, 0xfffff).rom().region("bios", 0);
}

void pcjr_state::ibmpcjx_io(address_map &map)
{
	map.unmap_value_high();
	ibmpcjr_io(map);
	map(0x01ff, 0x01ff).rw(FUNC(pcjr_state::pcjx_port_1ff_r), FUNC(pcjr_state::pcjx_port_1ff_w));
}

void pcjr_state::ibmpcjr(machine_config &config)
{
	/* basic machine hardware */
	I8088(config, m_maincpu, XTAL(14'318'181)/3);
	m_maincpu->set_addrmap(AS_PROGRAM, &pcjr_state::ibmpcjr_map);
	m_maincpu->set_addrmap(AS_IO, &pcjr_state::ibmpcjr_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259", FUNC(pic8259_device::inta_cb));

/*
  On the PC Jr the input for clock 1 seems to be selectable
  based on bit 4(/5?) written to output port A0h. This is not
  supported yet.
 */
	PIT8253(config, m_pit8253, 0);
	m_pit8253->set_clk<0>(XTAL(14'318'181)/12);
	m_pit8253->out_handler<0>().set(m_pic8259, FUNC(pic8259_device::ir0_w));
	m_pit8253->set_clk<1>(XTAL(14'318'181)/12);
	m_pit8253->set_clk<2>(XTAL(14'318'181)/12);
	m_pit8253->out_handler<2>().set(FUNC(pcjr_state::out2_changed));

	PIC8259(config, m_pic8259, 0);
	m_pic8259->out_int_callback().set(FUNC(pcjr_state::pic8259_set_int_line));

	i8255_device &ppi(I8255(config, "ppi8255"));
	ppi.in_pa_callback().set_constant(0xff);
	ppi.out_pb_callback().set(FUNC(pcjr_state::pcjr_ppi_portb_w));
	ppi.in_pc_callback().set(FUNC(pcjr_state::pcjr_ppi_portc_r));

	ins8250_device &uart(INS8250(config, "ins8250", XTAL(1'843'200)));
	uart.out_tx_callback().set("serport", FUNC(rs232_port_device::write_txd));
	uart.out_dtr_callback().set("serport", FUNC(rs232_port_device::write_dtr));
	uart.out_rts_callback().set("serport", FUNC(rs232_port_device::write_rts));
	uart.out_int_callback().set(m_pic8259, FUNC(pic8259_device::ir3_w));

	rs232_port_device &serport(RS232_PORT(config, "serport", pcjr_com, nullptr));
	serport.rxd_handler().set("ins8250", FUNC(ins8250_uart_device::rx_w));
	serport.dcd_handler().set("ins8250", FUNC(ins8250_uart_device::dcd_w));
	serport.dsr_handler().set("ins8250", FUNC(ins8250_uart_device::dsr_w));
	serport.ri_handler().set("ins8250", FUNC(ins8250_uart_device::ri_w));
	serport.cts_handler().set("ins8250", FUNC(ins8250_uart_device::cts_w));

	/* video hardware */
	PCVIDEO_PCJR(config, "pcvideo_pcjr", 0).set_screen("pcvideo_pcjr:screen");

	GFXDECODE(config, "gfxdecode", "pcvideo_pcjr:palette", gfx_pcjr);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.80);
	SN76496(config, "sn76496", XTAL(14'318'181)/4).add_route(ALL_OUTPUTS, "mono", 0.80);

	/* printer */
	pc_lpt_device &lpt0(PC_LPT(config, "lpt_0"));
	lpt0.irq_handler().set(m_pic8259, FUNC(pic8259_device::ir7_w));

	PC_JOY(config, "pc_joy");

	/* cassette */
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	UPD765A(config, m_fdc, 8'000'000, false, false);

	FLOPPY_CONNECTOR(config, "fdc:0", pcjr_floppies, "525dd", isa8_fdc_device::floppy_formats, true);

	PC_KEYB(config, m_keyboard);
	m_keyboard->keypress().set(FUNC(pcjr_state::keyb_interrupt));

	/* cartridge */
	GENERIC_CARTSLOT(config, "cartslot1", generic_plain_slot, "ibmpcjr_cart", "bin,jrc").set_device_load(FUNC(pcjr_state::cart1_load));
	GENERIC_CARTSLOT(config, "cartslot2", generic_plain_slot, "ibmpcjr_cart", "bin,jrc").set_device_load(FUNC(pcjr_state::cart2_load));

	/* internal ram */
	RAM(config, m_ram).set_default_size("640K").set_extra_options("128K, 256K, 512K");

	/* Software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("ibmpcjr_cart");
	SOFTWARE_LIST(config, "flop_list").set_original("ibmpcjr_flop");
	SOFTWARE_LIST(config, "pc_list").set_compatible("ibm5150");
}

static GFXDECODE_START( gfx_ibmpcjx )
	GFXDECODE_ENTRY( "gfx1", 0x0000, pc_8_charlayout, 3, 1 )
	GFXDECODE_ENTRY( "kanji", 0x0000, kanji_layout, 3, 1 )
GFXDECODE_END

void pcjr_state::ibmpcjx(machine_config &config)
{
	ibmpcjr(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &pcjr_state::ibmpcjx_map);
	m_maincpu->set_addrmap(AS_IO, &pcjr_state::ibmpcjx_io);

	config.device_remove("fdc:0");
	FLOPPY_CONNECTOR(config, "fdc:0", pcjr_floppies, "35dd", isa8_fdc_device::floppy_formats, true);
	FLOPPY_CONNECTOR(config, "fdc:1", pcjr_floppies, "35dd", isa8_fdc_device::floppy_formats, true);

	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_ibmpcjx);

	/* internal ram */
	m_ram->set_default_size("512K").set_extra_options(""); // only boots with 512k currently

	/* Software lists */
	SOFTWARE_LIST(config, "jx_list").set_original("ibmpcjx");

}



ROM_START( ibmpcjr )
	ROM_REGION(0x10000,"bios", 0)
	ROM_SYSTEM_BIOS( 0, "default", "Default" )
	ROMX_LOAD("bios.rom", 0x0000, 0x10000,CRC(31e3a7aa) SHA1(1f5f7013f18c08ff50d7942e76c4fbd782412414), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "quiksilver", "Quicksilver" ) // Alternate bios to boot up faster (Synectics)
	ROMX_LOAD("quiksilv.rom", 0x0000, 0x10000, CRC(86aaa1c4) SHA1(b3d7e8ce5de17441891e0b71e5261ed01a169dc1), ROM_BIOS(1))

	ROM_REGION(0x08100,"gfx1", 0)
	ROM_LOAD("cga.chr",     0x00000, 0x01000, CRC(42009069) SHA1(ed08559ce2d7f97f68b9f540bddad5b6295294dd)) // from an unknown clone cga card
ROM_END

ROM_START( ibmpcjx )
	ROM_REGION(0x20000,"bios", ROMREGION_ERASEFF)
	ROM_DEFAULT_BIOS("unk")
	ROM_SYSTEM_BIOS( 0, "5601jda", "5601jda" )
	ROMX_LOAD("5601jda.bin", 0x10000, 0x10000, CRC(b1e12366) SHA1(751feb16b985aa4f1ec1437493ff77e2ebd5e6a6), ROM_BIOS(0))
	ROMX_LOAD("basicjx.rom",   0x08000, 0x08000, NO_DUMP, ROM_BIOS(0)) // boot fails due to this.
	ROM_SYSTEM_BIOS( 1, "unk", "unk" )
	ROMX_LOAD("ipljx.rom", 0x00000, 0x20000, CRC(36a7b2de) SHA1(777db50c617725e149bca9b18cf51ce78f6dc548), ROM_BIOS(1))

	ROM_REGION(0x08100,"gfx1", 0) //TODO: needs a different charset
	ROM_LOAD("cga.chr",     0x00000, 0x01000, BAD_DUMP CRC(42009069) SHA1(ed08559ce2d7f97f68b9f540bddad5b6295294dd)) // from an unknown clone cga card

	ROM_REGION(0x38000,"kanji", 0)
	ROM_LOAD("kanji.rom",     0x00000, 0x38000, BAD_DUMP CRC(eaa6e3c3) SHA1(35554587d02d947fae8446964b1886fff5c9d67f)) // hand-made rom
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT    CLASS       INIT        COMPANY                            FULLNAME     FLAGS
COMP( 1983, ibmpcjr, ibm5150, 0,      ibmpcjr, ibmpcjr, pcjr_state, empty_init, "International Business Machines", "IBM PC Jr", MACHINE_IMPERFECT_COLORS )
COMP( 1985, ibmpcjx, ibm5150, 0,      ibmpcjx, ibmpcjr, pcjr_state, empty_init, "International Business Machines", "IBM PC JX", MACHINE_IMPERFECT_COLORS | MACHINE_NOT_WORKING)



icatel.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders: Felipe Sanches
/***************************************************************************

    icatel - Brazilian public payphone
    manufactured by icatel http://www.icatel.com.br/

    Partial schematics (drawn based on PCB inspection) available at:
    https://github.com/garoa/Icatel/blob/master/doc/icatel.pdf

    Driver by Felipe Sanches <juca@members.fsf.org>

    Changelog:

    2014 DEC 14 [Felipe Sanches]:
    * Initial driver skeleton

***************************************************************************/

#include "emu.h"

#include "cpu/mcs51/mcs51.h"
#include "video/hd44780.h"
//#include "sound/speaker.h"

#include "debugger.h"
#include "emupal.h"
#include "screen.h"


namespace {

class icatel_state : public driver_device
{
public:
	icatel_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "hd44780")
	{ }

	void icatel(machine_config &config);

	void init_icatel();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	[[maybe_unused]] uint8_t magic_string(offs_t offset);

	uint8_t i80c31_p1_r();
	uint8_t i80c31_p3_r();
	void i80c31_p1_w(uint8_t data);
	void i80c31_p3_w(uint8_t data);

	uint8_t cn8_extension_r();
	void cn8_extension_w(uint8_t data);

	uint8_t modem_r(offs_t offset);
	void modem_w(offs_t offset, uint8_t data);

	void ci8_w(uint8_t data);
	void ci12_w(uint8_t data);
	uint8_t ci15_r();
	void ci16_w(uint8_t data);

	void icatel_palette(palette_device &palette) const;

	HD44780_PIXEL_UPDATE(icatel_pixel_update);

	void i80c31_data(address_map &map) ATTR_COLD;
	void i80c31_io(address_map &map) ATTR_COLD;
	void i80c31_prg(address_map &map) ATTR_COLD;

	required_device<i80c31_device> m_maincpu;
	required_device<hd44780_device> m_lcdc;
};

void icatel_state::i80c31_prg(address_map &map)
{
	map(0x0000, 0x7FFF).mirror(0x8000).rom();
}

void icatel_state::i80c31_io(address_map &map)
{
	map(0x0000, 0x3FFF).ram();
	map(0x8000, 0x8001).mirror(0x3F3C).w(m_lcdc, FUNC(hd44780_device::write));
	map(0x8002, 0x8003).mirror(0x3F3C).r(m_lcdc, FUNC(hd44780_device::read));
	map(0x8040, 0x8040).mirror(0x3F1F).w(FUNC(icatel_state::ci12_w)); // 74LS273
	map(0x8060, 0x8060).mirror(0x3F1F).w(FUNC(icatel_state::ci8_w));
	map(0x8080, 0x8080).mirror(0x3F1F).w(FUNC(icatel_state::ci16_w)); // card reader (?)
	map(0x80C0, 0x80C0).mirror(0x3F1F).r(FUNC(icatel_state::ci15_r)); // 74LS244 (tristate buffer)
	map(0xC000, 0xCFFF).rw(FUNC(icatel_state::cn8_extension_r), FUNC(icatel_state::cn8_extension_w));
	map(0xE000, 0xE0FF).mirror(0xF00).rw(FUNC(icatel_state::modem_r), FUNC(icatel_state::modem_w));
}

void icatel_state::i80c31_data(address_map &map)
{
//  map(0x0056,0x005A).r(FUNC(icatel_state::magic_string)); /* This is a hack! */
}

void icatel_state::init_icatel()
{
}

void icatel_state::machine_start()
{
}

void icatel_state::machine_reset()
{
}

uint8_t icatel_state::magic_string(offs_t offset)
{
//  logerror("read: magic_string, offset=%04X\n", offset);
	char mstr[] = "TP-OK";
	return mstr[offset%5];
}

uint8_t icatel_state::i80c31_p1_r()
{
	return 0x7f;
}

uint8_t icatel_state::i80c31_p3_r()
{
	return 0xff;
}

void icatel_state::i80c31_p1_w(uint8_t data)
{
}

void icatel_state::i80c31_p3_w(uint8_t data)
{
}

//----------------------------------------

uint8_t icatel_state::cn8_extension_r()
{
	/* TODO: Implement-me! */
	logerror("read: cn8_extension\n");
	return 0;
}

void icatel_state::cn8_extension_w(uint8_t data)
{
	/* TODO: Implement-me! */
	logerror("write: cn8_extension [%02x]\n", data);
}

//----------------------------------------

uint8_t icatel_state::modem_r(offs_t offset)
{
	/* TODO: Implement-me! */
	logerror("read: modem\n");
	return 0;
}

void icatel_state::modem_w(offs_t offset, uint8_t data)
{
	/* TODO: Implement-me! */
	logerror("write: modem %02x:[%02x]\n", offset, data);
}

//----------------------------------------

void icatel_state::ci8_w(uint8_t data)
{
	/* TODO: Implement-me! */
	logerror("write: ci8 [%02x]\n", data);
}

//----------------------------------------

void icatel_state::ci12_w(uint8_t data)
{
	/* TODO: Implement-me! */
	logerror("write: ci12 [%02x]\n", data);
}

//----------------------------------------

uint8_t icatel_state::ci15_r()
{
	/* TODO: Implement-me! */
	//machine().debug_break();
	//logerror("read: ci15\n");
	return (1 << 3) | (1 << 0);
}

//----------------------------------------

void icatel_state::ci16_w(uint8_t data)
{
	/* TODO: Implement-me! */
	// seems to be the card reader.
	logerror("write: ci16 [%02x]\n", data);
}

//----------------------------------------

void icatel_state::icatel_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}

static const gfx_layout prot_charlayout =
{
	5, 8,                   /* 5 x 8 characters */
	256,                    /* 256 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	{ 3, 4, 5, 6, 7},
	{ 0, 8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8},
	8*8                     /* 8 bytes */
};

static GFXDECODE_START( gfx_icatel )
	GFXDECODE_ENTRY( "hd44780:cgrom", 0x0000, prot_charlayout, 0, 1 )
GFXDECODE_END

HD44780_PIXEL_UPDATE(icatel_state::icatel_pixel_update)
{
	if ( pos < 16 && line==0 )
	{
		bitmap.pix(y, pos*6 + x) = state;
	}

	if ( pos >= 64 && pos < 80 && line==0 )
	{
		bitmap.pix(y+9,(pos-64)*6 + x) = state;
	}
}

void icatel_state::icatel(machine_config &config)
{
	/* basic machine hardware */
	I80C31(config, m_maincpu, XTAL(2'097'152));
	m_maincpu->set_addrmap(AS_PROGRAM, &icatel_state::i80c31_prg);
	m_maincpu->set_addrmap(AS_DATA, &icatel_state::i80c31_data);
	m_maincpu->set_addrmap(AS_IO, &icatel_state::i80c31_io);
	m_maincpu->port_in_cb<1>().set(FUNC(icatel_state::i80c31_p1_r));
	m_maincpu->port_out_cb<1>().set(FUNC(icatel_state::i80c31_p1_w));
	m_maincpu->port_in_cb<3>().set(FUNC(icatel_state::i80c31_p3_r));
	m_maincpu->port_out_cb<3>().set(FUNC(icatel_state::i80c31_p3_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("hd44780", FUNC(hd44780_device::screen_update));
	screen.set_size(6*16, 9*2);
	screen.set_visarea(0, 6*16-1, 0, 9*2-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(icatel_state::icatel_palette), 2);
	GFXDECODE(config, "gfxdecode", "palette", gfx_icatel);

	HD44780(config, m_lcdc, 270'000); /* TODO: clock not measured, datasheet typical clock used */
	m_lcdc->set_lcd_size(2, 16);
	m_lcdc->set_pixel_update_cb(FUNC(icatel_state::icatel_pixel_update));
}

ROM_START( icatel )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD( "icatel_tpci_em._4_v16.05.ci14",  0x00000, 0x8000, CRC(d310586e) SHA1(21736ad5a06cf9695f8cc5ff2dc2d19b101504f5) )
ROM_END

} // anonymous namespace


//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS         INIT         COMPANY   FULLNAME                            FLAGS
COMP( 1995, icatel, 0,      0,      icatel,  0,     icatel_state, init_icatel, "Icatel", "TPCI (Brazilian public payphone)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND)
/*The hardware was clearly manufactured in 1995. There's no evindence of the actual date of the firmware.*/



icebox.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/******************************************************************************************************************

Nutting Icebox

2018-08-19 Skeleton driver. It's all guesswork.

It's a case with many slots for daughter boards, and 2 floppy drives. It's an "in-circuit emulator"
for Bally / Astrocade development.

The unit has 5 daughter boards: 3 boards were photographed and are: a cpu board (Z80 and XTAL),
                    a RS232 board (2x 8251, 8 dipswitches connected to a BR1941L, 4.9152 XTAL),
                    and a board covered with TTL.

Commands:
- At the main % prompt:
-- A = Arcade
-- C = Commercial
-- D = Debug
-- ^C = boot CP/M
-- ^T = load data from disk to 0000
-- ^X = new line
- At the debug prompt: (to be done)   Q to quit

Status:
- Machine boots up, you can enter commands.
- For ^C (boot disk), it is able to read track 00 into memory. It then asks for 18x 128 bytes from track 01.
  It reads 8 of those blocks, then hangs.

To Do:
- Find out more what ports F2-FF do.

The floppy drives are a special type containing a FDC within. The FDC is a FD1771-B01 with a 4MHz XTAL.


NOTES FROM AL KOSSOW
--------------------

This is the Nutting Associates ICE box used for Bally arcade and Astrocade development.

The games are written in "Terse", a dialect of FORTH with graphics and sound extensions.

The game graphics and sound hardware were in a separate chassis connected through 50 pin ribbon cables.
There is 64K of ram that can be mapped to the addresses used where the proms in the game would be.

Development appeared to have started under CP/M, then they migrated to development in the Terse environment.
The sector size also changed from 128 to 1024 bytes. For some reason, the hardware inverts the sector data.
CP/M flipped the bits, Terse does not.

There is a 122 pin backplane, and five cards. The bus buffers for the floppies and the external hardware
are on the backplane. There is an I/O, memory mapper, dual 32k static ram, and CPU cards. The I/O card has
a three byte FIFO and a buffer for forcing 00 onto the data bus, which appears to be related to handling
breakpoints from looking at the code in ICE.ASM.

The eprom dump is for booting Terse, and earlier version of the prom is documented in ICE.ASM which may be for CP/M

The CPU card has 1K of ram, mapped to FC00. They must have moved the sector buffering to main RAM, since a
1K sector obviously wouldn't fit.

The crystal on the CPU is 9.8304MHz. It may be possible to alter the cpu speed. There is a FAST signal on the bp.

I/O registers from the ICE.ASM listing

CRTD    E0    terminal uart data
CRTS    E1    terminal uart control
LPTD    E2    printer uart data
LPTS    E3    printer uart control

DATA    E4    1771 regs
SECTOR  E5
TRACK   E6
COMMAND E7

CENABE  F0
SELECT  F1    bit 0 & 1 (select drive 0-3), bit 2 (enable floppy interrupt)
FIFO    F2    3 BYTE FIFO

EXTL    F8    These are probably for the mapping registers
EXTH    F9

WPROTL  FA
WPROTH  FB

BADRH   FC    BASE ADR? breakpoint
BADRL   FD

BTYPE   FE
MISC    FF

the 0 is forced onto the bus when bp signal IRESET/ is asserted. it appears to come from the CPU board (only
the cpu and io board are connected to it)

the box doesn't do remapping, it just has two 16 bit write-only registers that can unmap and write protect memory
in 16 4k chunks, (extl,exth), (wprotl,wproth) .

breakpoint (badrh, barl) specifies an adr to nmi on, with a 4 bit reference type in btype (wr, req, iorq, mreq)

it looks like zeroing btype disables it.

io port 0xff (misc) is on the I/O board.

The date on the backplane of the system is 1978. It originally used dynamic RAM boards for memory, there is support
on the 'mapper' board for generating row/col adrs for them, and four ram board selects. It was replaced with a static
RAM in 1982, based on the date of the boards in the system. Two selects go to each 32k board and there is a DIP switch
on the boards to say which of the four selects it responds to.

The three byte fifo and a register jams a zero onto the data bus (when?), and we have the source for the CP/M version
of the debugger/boot prom to see how interrupt handling occurs.

When the floppy controller generates a DRQ or INTRQ it also generates a Z80 INT, which uses IM 0
and forces a NOP (00) onto the bus. This allows firmware and/or disk routines to resume after a HALT.

I can sort of piece together what Terse implements and what it evolved from. I think it started as the Caltech FORTH
implemented for the PDP-10 and 11 with a lot of words stripped out, and others added. This was submitted to DECUS as
submission 11-232 but much of the early DECUS stuff has been lost, so I've not been able to find a copy to see how the
FORTH kernel was implemented. I do have the Caltech writeup on it though,
http://bitsavers.org/pdf/caltech/ovro/Caltech-OVRO_Forth_Manual_Jun78.pdf

First, ^T to boot the boot portion of Terse from Blocks 1 through 4.
The Command "5 LOAD" will boot full Terse.
The disk label says:  220 LOAD (which also boots full Terse I think?)
and then: 158 LOAD, which loads the Gorf binary into RAM

You can also use:
250 0 DIR (for example)
to see the "first line comments" for a range of blocks.  There is some stuff related to cross-compiling there I haven't tried yet.

Note: Blocks in Terse start with 1, not 0.

The "Fasterse" content on the disk is the Source for the "binary-only" version of Terse in the commercial games.  You can see that
is exactly the same as the code in the low memory of Gorf.

Also, this disk is hand-labelled 7-Feb-81.
The Gorf binary on the disk is the same as the release, except for an embedded date code - it is 18-Feb-81 on the disk.
It is 24-Feb-81 in the commercial release gorf roms.


My Forth is extremely rusty and I happen to look at disks with problems. If you try to do 5 LOAD with the FORLANG disk it comes
up with a Read Error? message.. Argh. There could be a fault in the IMD. Not sure. Great that other disks work.

Disks like 611ROTO have a disk backup system or something, and when you boot it up with ^T it hangs. I've narrowed it down to a
BUSY wait which never exits (at $308a eventually).

I think it's a quirk of the FD1771 chip thats not emulated accurately causing this. The first track of data is read using the
READ (multiple) in a loop reading 10 consecutive sectors which occurs fine, and when it's finished reading the sectors an INTERRUPT
command is issued to stop the FD1171 feeding data. According to all the datasheets I've looked at, the BUSY flag should get set to
0 immediately once the INTERRUPT command is executed (and the mame code does this), but the code in this case calls a subroutine
which waits until the BUSY flag is ON and then waits until the flag is OFF. This works elsewhere as it's used to wait until the
SEEK command is completed.


******************************************************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/i8251.h"
#include "machine/com8116.h"
#include "bus/rs232/rs232.h"
#include "machine/wd_fdc.h"


namespace {

class icebox_state : public driver_device
{
public:
	icebox_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_uart0(*this, "uart0")
		, m_uart1(*this, "uart1")
		, m_brg(*this, "brg")
		, m_fdc(*this, "fdc")
		, m_floppy0(*this, "fdc:0")
		, m_floppy1(*this, "fdc:1")
	{ }

	void icebox(machine_config &config);

private:
	void drq_w(int state);
	void intrq_w(int state);
	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void machine_reset() override ATTR_COLD;
	void port_f1_w(u8 data);
	u8 m_f1 = 0U;

	required_device<cpu_device> m_maincpu;
	required_device<i8251_device> m_uart0;
	required_device<i8251_device> m_uart1;
	required_device<com8116_device> m_brg;
	required_device<fd1771_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	required_device<floppy_connector> m_floppy1;
};


void icebox_state::mem_map(address_map &map)
{
	map(0x0000, 0xefff).ram();
	map(0xf000, 0xfbff).rom().region("maincpu", 0);
	map(0xfc00, 0xffff).ram();
}

void icebox_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0xe0, 0xe1).rw(m_uart0, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xe2, 0xe3).rw(m_uart1, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xe4, 0xe7).lrw8(
					NAME([this] (offs_t offset) { return m_fdc->read(offset^3); }),
					NAME([this] (offs_t offset, u8 data) { m_fdc->write(offset^3, data); }));
	map(0xf1, 0xf1).lw8(NAME([this] (u8 data) { port_f1_w(data); }));
}

/* Input ports */
static INPUT_PORTS_START( icebox )
	PORT_START("BAUD")
	PORT_DIPNAME( 0x0f, 0x0e, "Baud Rate for Terminal") PORT_DIPLOCATION("SW:5,6,7,8")
	PORT_DIPSETTING(    0x00, "50")
	PORT_DIPSETTING(    0x01, "75")
	PORT_DIPSETTING(    0x02, "110")
	PORT_DIPSETTING(    0x03, "134.5")
	PORT_DIPSETTING(    0x04, "150")
	PORT_DIPSETTING(    0x05, "300")
	PORT_DIPSETTING(    0x06, "600")
	PORT_DIPSETTING(    0x07, "1200")
	PORT_DIPSETTING(    0x08, "1800")
	PORT_DIPSETTING(    0x09, "2000")
	PORT_DIPSETTING(    0x0a, "2400")
	PORT_DIPSETTING(    0x0b, "3600")
	PORT_DIPSETTING(    0x0c, "4800")
	PORT_DIPSETTING(    0x0e, "9600")
	PORT_DIPSETTING(    0x0f, "19200")
	PORT_DIPNAME( 0xf0, 0x70, "Baud Rate for Printer") PORT_DIPLOCATION("SW:1,2,3,4")
	PORT_DIPSETTING(    0x00, "50")
	PORT_DIPSETTING(    0x10, "75")
	PORT_DIPSETTING(    0x20, "110")
	PORT_DIPSETTING(    0x30, "134.5")
	PORT_DIPSETTING(    0x40, "150")
	PORT_DIPSETTING(    0x50, "300")
	PORT_DIPSETTING(    0x60, "600")
	PORT_DIPSETTING(    0x70, "1200")
	PORT_DIPSETTING(    0x80, "1800")
	PORT_DIPSETTING(    0x90, "2000")
	PORT_DIPSETTING(    0xa0, "2400")
	PORT_DIPSETTING(    0xb0, "3600")
	PORT_DIPSETTING(    0xc0, "4800")
	PORT_DIPSETTING(    0xe0, "9600")
	PORT_DIPSETTING(    0xf0, "19200")
INPUT_PORTS_END

void icebox_state::machine_reset()
{
	u8 data = ioport("BAUD")->read();
	m_brg->str_w(data & 15); // Terminal
	m_brg->stt_w((data >> 4) & 15); // Printer
	m_maincpu->set_pc(0xf000);
	m_f1 = 0;
}

void icebox_state::port_f1_w(u8 data)
{
	m_f1 = data;

	floppy_image_device *floppy = nullptr;

	switch (data & 7)
	{
		case 4:
			floppy = m_floppy0->get_device();
			break;
		case 5:
			floppy = m_floppy1->get_device();
			break;
		default:
			break;
	}

	m_fdc->set_floppy(floppy);

	if (floppy)
	{
		floppy->mon_w(0);
		floppy->ss_w(0);            // side 0 ?
	}

	m_fdc->dden_w(1);                 // single density?
}

// The next byte from floppy is available. Enable CPU so it can get the NOP byte, via IM0.
void icebox_state::drq_w(int state)
{
	if (BIT(m_f1, 2))
		m_maincpu->set_input_line_and_vector(INPUT_LINE_IRQ0, state ? ASSERT_LINE : CLEAR_LINE, 0x00); // Z80
}

// The next byte from floppy is available. Enable CPU so it can get the NOP byte, via IM0.
void icebox_state::intrq_w(int state)
{
	if (BIT(m_f1, 2))
		m_maincpu->set_input_line_and_vector(INPUT_LINE_IRQ0, state ? ASSERT_LINE : CLEAR_LINE, 0x00); // Z80
}

static void floppies(device_slot_interface &device)
{
	device.option_add("flop", FLOPPY_8_SSDD); // Pertec "iCOM FD5200"
}

static DEVICE_INPUT_DEFAULTS_START( terminal ) // we need to remove bit 7 which is on the last character of each message
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END

void icebox_state::icebox(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 9'830'400 / 4); // unknown divisor
	m_maincpu->set_addrmap(AS_PROGRAM, &icebox_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &icebox_state::io_map);

	I8251(config, m_uart0, 0);
	m_uart0->txd_handler().set("rs232a", FUNC(rs232_port_device::write_txd));
	m_uart0->dtr_handler().set("rs232a", FUNC(rs232_port_device::write_dtr));
	m_uart0->rts_handler().set("rs232a", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, "terminal"));
	rs232a.rxd_handler().set(m_uart0, FUNC(i8251_device::write_rxd));
	rs232a.dsr_handler().set(m_uart0, FUNC(i8251_device::write_dsr));
	rs232a.cts_handler().set(m_uart0, FUNC(i8251_device::write_cts));
	rs232a.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal)); // must be exactly here

	I8251(config, m_uart1, 0);
	m_uart1->txd_handler().set("rs232b", FUNC(rs232_port_device::write_txd));
	m_uart1->dtr_handler().set("rs232b", FUNC(rs232_port_device::write_dtr));
	m_uart1->rts_handler().set("rs232b", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_uart1, FUNC(i8251_device::write_rxd));
	rs232b.dsr_handler().set(m_uart1, FUNC(i8251_device::write_dsr));
	rs232b.cts_handler().set(m_uart1, FUNC(i8251_device::write_cts));

	COM5016_5(config, m_brg, 4.9152_MHz_XTAL);   // BR1941L-05
	m_brg->fr_handler().set(m_uart0, FUNC(i8251_device::write_txc));
	m_brg->fr_handler().append(m_uart0, FUNC(i8251_device::write_rxc));
	m_brg->ft_handler().set(m_uart1, FUNC(i8251_device::write_txc));
	m_brg->ft_handler().append(m_uart1, FUNC(i8251_device::write_rxc));

	FD1771(config, m_fdc, 4_MHz_XTAL / 2);
	m_fdc->drq_wr_callback().set(FUNC(icebox_state::drq_w));
	m_fdc->intrq_wr_callback().set(FUNC(icebox_state::intrq_w));
	FLOPPY_CONNECTOR(config, m_floppy0, floppies, "flop", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy1, floppies, "flop", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
}

/* ROM definition */
ROM_START( icebox )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "ice0.bin", 0x0000, 0x0800, CRC(252092c0) SHA1(8b6c53994dbb1aa76fdb6b72961e12071c100809) )
	ROM_LOAD( "ice1.bin", 0x0800, 0x0800, CRC(f4dc4b93) SHA1(cd8c3b2a1ceb4e5efb35af9bcac7ebaab6a8a308) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT     CLASS         INIT          COMPANY             FULLNAME         FLAGS
COMP( 19??, icebox,  0,      0,      icebox,  icebox,   icebox_state, empty_init, "Nutting Associates", "ICEBOX",   MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW ) // Terminal beeps



icm3216.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * National Semiconductor ICM-3216 Integrated Computer Module
 *
 * Sources:
 *  - ICM-3216 Integrated Computer Module, January 1986, National Semiconductor (DS6786S-10M16)
 *  - ICM-3216 CPU Board Specification, revision PB, 10/17/85, National Semiconductor Corporation (426610289-000)
 *
 * TODO:
 *  - configurable ram size (1M, 2M, 4M, 8M)
 *  - iop and scsi
 *  - cpu timing
 *  - minibus
 *
 * WIP:
 *  - passes memory/parity diagnostics
 *  - boots to monitor (press Ctrl+C after beep)
 *  - enter $ to show version/memory information
 *  - other commands include: a,b,c,d,e,i,l,m,p,r,t,w,v and C,P,T,V
 */

#include "emu.h"

// cpus and memory
#include "cpu/ns32000/ns32000.h"
#include "cpu/z80/z80.h"

// various hardware
#include "machine/mc68681.h"
#include "machine/mm58274c.h"
#include "machine/ncr5385.h"
#include "machine/ns32081.h"
#include "machine/ns32082.h"
#include "machine/ns32202.h"

// busses and connectors
#include "bus/rs232/rs232.h"
#include "bus/nscsi/hd.h"
#include "machine/nscsi_bus.h"

#define LOG_PARITY (1U << 1)

//#define VERBOSE (LOG_GENERAL|LOG_PARITY)
#include "logmacro.h"

namespace {

class icm3216_state : public driver_device
{
public:
	icm3216_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_fpu(*this, "fpu")
		, m_mmu(*this, "mmu")
		, m_icu(*this, "icu")
		, m_rtc(*this, "rtc")
		, m_duart(*this, "duart%u", 0U)
		, m_serial(*this, "serial%u", 0U)
		, m_iop(*this, "iop")
		, m_scsi(*this, "scsi:7:ncr5385")
		, m_led(*this, "led%u", 1U)
		, m_boot(*this, "boot")
	{
	}

	// machine config
	void icm3216(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// address maps
	template <unsigned ST> void cpu_map(address_map &map) ATTR_COLD;
	void iop_mem_map(address_map &map) ATTR_COLD;
	void iop_pio_map(address_map &map) ATTR_COLD;

	// register read handlers
	u8 iop_r();
	u16 nmi_status_r();
	u8 nmi_enable_r();

	// register write handlers
	void iop_w(u8 data);
	void parity_select_w(u8 data);
	void parity_enable_w(u8 data);

	// parity handlers
	void parity_r(offs_t offset, u16 &data, u16 mem_mask);
	void parity_w(offs_t offset, u16 &data, u16 mem_mask);

	IRQ_CALLBACK_MEMBER(iop_ack);
	template <unsigned Source> void iop_int(int state);

private:
	required_device<ns32016_device> m_cpu;
	required_device<ns32081_device> m_fpu;
	required_device<ns32082_device> m_mmu;
	required_device<ns32202_device> m_icu;

	required_device<mm58274c_device> m_rtc;
	required_device_array<scn2681_device, 2> m_duart;
	required_device_array<rs232_port_device, 4> m_serial;

	required_device<z80_device> m_iop;
	required_device<ncr5385_device> m_scsi;

	output_finder<5> m_led;

	memory_view m_boot;
	memory_passthrough_handler m_boot_mph;

	enum nmi_status_mask : u16
	{
		NMI_CHIP   = 0x000f, // parity error chip(s)
		NMI_EVEN   = 0x0010, // parity error on even byte
		NMI_ODD    = 0x0020, // parity error on odd byte
		NMI_MMU    = 0x0040, // mmu interrupt
		NMI_MBIC   = 0x0080, // mbic interrupt
		NMI_PARITY = 0x0100, // parity error
	};

	enum iop_status_mask : u8
	{
		IOP_IID = 0x07, // subchannel interrupt ID
		IOP_IRS = 0x10, // interrupt request
		IOP_RST = 0x20, // scsi reset
		IOP_ABT = 0x40, // scsi abort interrupt?
		IOP_BSY = 0x80, // busy
	};

	// machine registers
	u8 m_iop_cmd;
	u8 m_iop_sts;
	u8 m_iop_vec;

	u16 m_nmi_state;      // non-maskable interrupt status register

	bool m_nmi_enable;    // non-maskable interrupt enable
	bool m_parity_enable; // parity checking enable
	bool m_parity_select; // parity mode even/odd

	// other internal state
	s32 m_parity_delta; // parity count increment/decrement
	u32 m_parity_count; // count mismatched parity writes

	std::unique_ptr<u8[]> m_parity;
	memory_passthrough_handler m_parity_mph;

	static constexpr unsigned RAM_SIZE = 0x400000;
};

void icm3216_state::machine_start()
{
	m_led.resolve();

	save_item(NAME(m_iop_cmd));
	save_item(NAME(m_iop_sts));
	save_item(NAME(m_nmi_state));
	save_item(NAME(m_nmi_enable));
	save_item(NAME(m_parity_enable));
	save_item(NAME(m_parity_select));
	save_item(NAME(m_parity_delta));
	save_item(NAME(m_parity_count));

	save_pointer(NAME(m_parity), RAM_SIZE);

	m_iop_cmd = 0;
	m_iop_sts = 0;
	m_iop_vec = 0;
}

void icm3216_state::machine_reset()
{
	m_boot.select(0);

	m_boot_mph = m_cpu->space(0).install_read_tap(0x800000, 0xffffff, "boot",
		[this](offs_t offset, uint16_t &data, uint16_t mem_mask)
		{
			if (!machine().side_effects_disabled())
			{
				m_boot.disable();
				m_boot_mph.remove();
			}
		});

	m_duart[0]->ip6_w(1); // TODO: what's this?

	m_nmi_state = 0;

	m_nmi_enable = true;
	m_parity_enable = false;
	m_parity_select = false;

	m_parity_delta = 0;
	m_parity_count = 0;

	m_parity.reset();
	m_parity_mph.remove();
}

template <unsigned ST> void icm3216_state::cpu_map(address_map &map)
{
	map(0x00'0000, RAM_SIZE - 1).ram();
	map(RAM_SIZE, 0x7f'ffff).noprw();

	if (ST == 0)
	{
		map(0x00'0000, 0x01'ffff).view(m_boot);
		m_boot[0](0x00'0000, 0x01'ffff).rom().region("eprom", 0);
	}

	map(0x80'0000, 0x81'ffff).rom().region("eprom", 0).mirror(0x100000);

	map(0xa0'0000, 0xa0'001f).umask16(0x00ff).rw(m_rtc, FUNC(mm58274c_device::read), FUNC(mm58274c_device::write));
	map(0xa0'0020, 0xa0'003f).umask16(0x00ff).rw(m_duart[0], FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0xa0'0040, 0xa0'005f).umask16(0x00ff).rw(m_duart[1], FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	//map(0xa0'0080, 0xa0'0083); // parallel

	map(0xa0'00a0, 0xa0'00a1).umask16(0x00ff).rw(FUNC(icm3216_state::iop_r), FUNC(icm3216_state::iop_w));
	map(0xa0'00c0, 0xa0'00c1).r(FUNC(icm3216_state::nmi_status_r)); // TODO: minibus hold (w)
	map(0xa0'00c2, 0xa0'00cb).umask16(0x00ff).lw8(NAME([this](offs_t offset, u8 data) { m_led[offset] = data; })); // TODO: minibus reset
	map(0xa0'00cc, 0xa0'00cc).w(FUNC(icm3216_state::parity_select_w));
	map(0xa0'00ce, 0xa0'00ce).w(FUNC(icm3216_state::parity_enable_w));

	map(0xa0'00e0, 0xa0'00e0).r(FUNC(icm3216_state::nmi_enable_r)); // TODO: minibus mhl/mcl (w)

	//map(0xc0'0000, 0xfd'ffff); // minibus memory 0x000000-0x3dffff
	//map(0xfe'0000, 0xfe'ffff); // minibus 8-bit i/o
	//map(0xff'0000, 0xff'7fff); // minibus 16-bit i/o

	map(0xff'fe00, 0xff'feff).m(m_icu, FUNC(ns32202_device::map<BIT(ST, 1)>)).umask16(0x00ff);

	if (ST == 4)
		map(0xff'ff00, 0xff'ff01).nopr(); // silence cpu nmi vector
}

void icm3216_state::iop_mem_map(address_map &map)
{
	map(0x0000, 0x3fff).rom().region("iop", 0);
	map(0x4000, 0x7fff).ram();

	//map(0xc010, 0xc010).lr8(NAME([this]() { logerror("0xc010 0x%02x (%s)\n", m_iop_cmd, machine().describe_context()); return m_iop_cmd; }));
	//map(0xc011, 0xc011).lr8(NAME([this]() { logerror("0xc011 0x%02x (%s)\n", 0x40, machine().describe_context()); return 0x40; }));
	map(0xc011, 0xc011).nopr(); // FIXME: silence

	// c010 read (after interrupt)
	// c011 read (test 0x40, test 0x80 before read16), write16 (on nmi)
	// c012 read16?
	// c013 write 0x80
	//map(0xc013, 0xc013).lw8(NAME([this](u8 data) { logerror("0xc013 0x%02x (%s)\n", data, machine().describe_context()); m_iop_sts &= ~data; }));
	// c014 write (on nmi)
	// c015 write 0x08
	// c017 write 0x00 (after read16?, clear BSY?)

	map(0xc020, 0xc02f).m(m_scsi, FUNC(ncr5385_device::map));
}

void icm3216_state::iop_pio_map(address_map &map)
{
}

static INPUT_PORTS_START(icm3216)
INPUT_PORTS_END

static void scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
}


void icm3216_state::icm3216(machine_config &config)
{
	NS32016(config, m_cpu, 20_MHz_XTAL / 2);
	m_cpu->set_addrmap(0, &icm3216_state::cpu_map<0>);
	m_cpu->set_addrmap(4, &icm3216_state::cpu_map<4>);

	NS32081(config, m_fpu, 20_MHz_XTAL / 2);
	m_cpu->set_fpu(m_fpu);

	NS32082(config, m_mmu, 20_MHz_XTAL / 2);
	m_cpu->set_mmu(m_mmu);

	// 8 bit mode, irq: 4 serial, 1 minibus, 1 scsi, 1 printer, 1 rtc, 8 free
	NS32202(config, m_icu, 18.432_MHz_XTAL / 10);
	m_icu->out_int().set_inputline(m_cpu, INPUT_LINE_IRQ0).invert();

	MM58274C(config, m_rtc, 0);

	// we are dte, therefore: tx,rx,rts,cts,dsr,dtr,dcd
	// rts o
	// cts i
	// dsr i
	// rlsd i
	// dtr o

	SCN2681(config, m_duart[0], 3'686'400); // SCN2681A
	m_duart[0]->irq_cb().set([this](int state) { logerror("irq %d\n", state); });
	m_duart[0]->a_tx_cb().set(m_serial[0], FUNC(rs232_port_device::write_txd));
	m_duart[0]->b_tx_cb().set(m_serial[1], FUNC(rs232_port_device::write_txd));

	SCN2681(config, m_duart[1], 3'686'400); // SCN2681A
	m_duart[1]->irq_cb().set([this](int state) { logerror("irq %d\n", state); });
	m_duart[1]->a_tx_cb().set(m_serial[2], FUNC(rs232_port_device::write_txd));
	m_duart[1]->b_tx_cb().set(m_serial[3], FUNC(rs232_port_device::write_txd));

	RS232_PORT(config, m_serial[0], default_rs232_devices, nullptr);
	RS232_PORT(config, m_serial[1], default_rs232_devices, nullptr);
	m_serial[0]->rxd_handler().set(m_duart[0], FUNC(scn2681_device::rx_a_w));
	m_serial[1]->rxd_handler().set(m_duart[0], FUNC(scn2681_device::rx_b_w));

	RS232_PORT(config, m_serial[2], default_rs232_devices, nullptr);
	RS232_PORT(config, m_serial[3], default_rs232_devices, "terminal");
	m_serial[2]->rxd_handler().set(m_duart[1], FUNC(scn2681_device::rx_a_w));
	m_serial[3]->rxd_handler().set(m_duart[1], FUNC(scn2681_device::rx_b_w));

	Z80(config, m_iop, 4'000'000);
	m_iop->set_addrmap(AS_PROGRAM, &icm3216_state::iop_mem_map);
	m_iop->set_addrmap(AS_IO, &icm3216_state::iop_pio_map);
	m_iop->set_irq_acknowledge_callback(FUNC(icm3216_state::iop_ack));

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:1", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:2", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:3", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:4", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:5", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:6", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr5385", NCR5385).machine_config(
		[this](device_t *device)
		{
			ncr5385_device &ncr5385(downcast<ncr5385_device &>(*device));

			ncr5385.set_clock(10'000'000);

			ncr5385.irq().set(*this, FUNC(icm3216_state::iop_int<2>));
			ncr5385.dreq().set_inputline(m_iop, INPUT_LINE_NMI);
		});
}

void icm3216_state::parity_select_w(u8 data)
{
	LOGMASKED(LOG_PARITY, "parity %s (%s)\n", BIT(data, 0) ? "odd" : "even", machine().describe_context());

	if (m_parity_select != BIT(data, 0))
	{
		if (!m_parity)
		{
			LOGMASKED(LOG_PARITY, "parity handlers installed\n");

			// allocate and initialise parity store
			m_parity = std::make_unique<u8[]>(RAM_SIZE >> 3);
			if (m_parity_select)
				std::fill_n(m_parity.get(), RAM_SIZE >> 3, u8(0xff));

			// install parity handlers
			m_parity_mph = m_cpu->space(0).install_readwrite_tap(0, RAM_SIZE - 1, "parity",
				std::bind(&icm3216_state::parity_r, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3),
				std::bind(&icm3216_state::parity_w, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

			// count mismatched parity writes
			m_parity_delta = 1;
		}
		else if (!m_parity_count)
		{
			LOGMASKED(LOG_PARITY, "parity handlers removed\n");

			m_parity_mph.remove();
			m_parity.reset();

			m_parity_delta = 0;
		}
		else
			// invert mismatched parity count direction
			m_parity_delta *= -1;

		m_parity_select = BIT(data, 0);
	}
}

void icm3216_state::parity_enable_w(u8 data)
{
	LOGMASKED(LOG_PARITY, "parity %s (%s)\n", BIT(data, 0) ? "enabled" : "disabled", machine().describe_context());

	m_parity_enable = BIT(data, 0);
}

void icm3216_state::parity_r(offs_t offset, u16 &data, u16 mem_mask)
{
	if (!machine().side_effects_disabled() && m_parity_enable && !m_nmi_state)
	{
		LOGMASKED(LOG_PARITY, "parity r 0x%06x mask 0x%04x %s (%s)\n",
			offset, mem_mask, m_parity_select ? "odd" : "even", machine().describe_context());

		for (unsigned byte = 0; byte < 2; byte++)
		{
			if (BIT(mem_mask, byte * 8, 8))
			{
				if (BIT(m_parity[offset >> 3], BIT(offset, 1, 2) * 2 + byte) != m_parity_select)
					m_nmi_state |= NMI_PARITY | (NMI_EVEN << byte);
			}
		}

		if (m_nmi_state && m_nmi_enable)
		{
			LOGMASKED(LOG_PARITY, "parity error 0x%06x (%s)\n", offset, machine().describe_context());

			m_cpu->set_input_line(INPUT_LINE_NMI, 1);
		}
	}
}

void icm3216_state::parity_w(offs_t offset, u16 &data, u16 mem_mask)
{
	LOGMASKED(LOG_PARITY, "parity w 0x%06x mask 0x%04x %s (%s)\n",
		offset, mem_mask, m_parity_select ? "odd" : "even", machine().describe_context());

	for (unsigned byte = 0; byte < 2; byte++)
	{
		unsigned const parity_bit = BIT(offset, 1, 2) * 2 + byte;

		if (BIT(mem_mask, byte * 8, 8) && BIT(m_parity[offset >> 3], parity_bit) != m_parity_select)
		{
			if (m_parity_select)
				m_parity[offset >> 3] |= 1U << parity_bit;
			else
				m_parity[offset >> 3] &= ~(1U << parity_bit);

			m_parity_count += m_parity_delta;
		}
	}

	if (!m_parity_count)
	{
		LOGMASKED(LOG_PARITY, "parity handlers removed\n");

		m_parity_mph.remove();
		m_parity.reset();

		m_parity_delta = 0;
	}
}

u16 icm3216_state::nmi_status_r()
{
	u16 const data = m_nmi_state;

	if (!machine().side_effects_disabled())
	{
		LOGMASKED(LOG_PARITY, "nmi status 0x%04x (%s)\n", data, machine().describe_context());

		m_nmi_state = 0;
	}

	return data;
}

u8 icm3216_state::nmi_enable_r()
{
	if (!machine().side_effects_disabled())
	{
		LOGMASKED(LOG_PARITY, "nmi enable (%s)\n", machine().describe_context());

		m_nmi_enable = true;
	}

	return 0;
}

u8 icm3216_state::iop_r()
{
	return m_iop_sts;
}

void icm3216_state::iop_w(u8 data)
{
	/*
	 * cmd  function
	 *  0x00   write command pointer table (followed by address, lsb first)
	 *  0x01   acknowledge interrupt
	 *  0x02   reset i/o controller?
	 *  0x03   scsi reset
	 *  0x05   reset i/o controller?
	 *  0x1n   start i/o subchannel n
	 *  0x2n   abort i/o subchannel n
	 */
	LOG("iop_w 0x%02x (%s)\n", data, machine().describe_context());
	m_iop_cmd = data;

	m_iop_sts |= IOP_BSY;

	iop_int<1>(1);
}

// iop interrupt vector bits
// bit  source
//  1   host?
//  2   scsi
//  3   reset?

IRQ_CALLBACK_MEMBER(icm3216_state::iop_ack)
{
	u8 const data = m_iop_vec;

	m_iop->set_input_line(INPUT_LINE_IRQ0, 0);

	return data;
}

template <unsigned Source> void icm3216_state::iop_int(int state)
{
	if (state)
		m_iop_vec |= 1U << Source;
	else
		m_iop_vec &= ~(1U << Source);

	m_iop->set_input_line(INPUT_LINE_IRQ0, state);
}

ROM_START(icm3216)
	ROM_REGION16_LE(0x20000, "eprom", 0)

	ROM_SYSTEM_BIOS(0, "v244", "ICM-3216 Rom Monitor V2.44")
	ROMX_LOAD("610289_002__rev_b_u34.u34", 0x0000, 0x8000, CRC(fb542bd1) SHA1(39b7f1f90ffdae07ddc547eff564096bd2092d58), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("610289_002__rev_b_u35.u35", 0x0001, 0x8000, CRC(78cfc8dd) SHA1(bb87f068a6ab7ef82d8865154856064b9e8a99a6), ROM_BIOS(0) | ROM_SKIP(1))

	ROM_REGION(0x4000, "iop", 0)
	ROM_LOAD("600045_003__rev_a.u29", 0x0000, 0x4000, CRC(865e3e8b) SHA1(c6e47d304fd67a9b384a139ef917913fc60c0b32))
ROM_END

}

/*   YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY                   FULLNAME    FLAGS */
COMP(1985, icm3216, 0,      0,      icm3216, icm3216, icm3216_state, empty_init, "National Semiconductor", "ICM-3216", MACHINE_NOT_WORKING)



idpartner.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/******************************************************************************

    Iskra Delta produced:
        1984 - Partner series
            model with Winchester disk + floppy
        1987 - Partner G series
            1F/G - model with 1 floppy disk
            2F/G - model with 2 floppy disks
            WF/G - model with Winchester disk + floppy

    Schematics and info at :
        https://github.com/tstih/idp-doc/tree/main/iskra-delta
        http://matejhorvat.si/sl/slorac/delta/partner/index.htm

*******************************************************************************/

#include "emu.h"

#include "idpart_video.h"

#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "formats/idpart_dsk.h"
#include "machine/mc14411.h"
#include "machine/mm58167.h"
#include "machine/timer.h"
#include "machine/upd765.h"
#include "machine/z80ctc.h"
#include "machine/z80dma.h"
#include "machine/z80daisy.h"
#include "machine/z80daisy_generic.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"
#include "bus/idpartner/bus.h"
#include "bus/rs232/rs232.h"

// Partner use for all pheriperials Z80 daisy chain, and since Intel 8272
// does not support it, there is actual implementation done in logic on the
// board, that does clear interrupt status on reset and acknowledge
class idpartner_floppy_daisy_device : public device_t, public device_z80daisy_interface
{
public:
	idpartner_floppy_daisy_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0);
	virtual ~idpartner_floppy_daisy_device() {};

	// callbacks
	auto int_handler() { return m_int_handler.bind(); }

	void set_vector(uint8_t vector) { m_vector = vector; }
	void int_w(int state);

protected:
	virtual void device_start() override ATTR_COLD;
	virtual void device_reset() override ATTR_COLD;

	// z80daisy_interface overrides
	virtual int z80daisy_irq_state() override;
	virtual int z80daisy_irq_ack() override;
	virtual void z80daisy_irq_reti() override;

private:
	devcb_write_line m_int_handler;

	int m_int;
	bool m_ius;
	int m_vector;
};

void idpartner_floppy_daisy_device::int_w(int state)
{
	m_int = state;
	m_int_handler(state);
}

void idpartner_floppy_daisy_device::device_start()
{
	save_item(NAME(m_int));
	save_item(NAME(m_ius));
	save_item(NAME(m_vector));
}

void idpartner_floppy_daisy_device::device_reset()
{
	m_int = CLEAR_LINE;
	m_ius = false;
	m_vector = 0xff;
}

int idpartner_floppy_daisy_device::z80daisy_irq_state()
{
	if (m_int)
		return Z80_DAISY_INT;
	else if (m_ius)
		return Z80_DAISY_IEO;
	else
		return 0;
}

int idpartner_floppy_daisy_device::z80daisy_irq_ack()
{
	if (m_int) {
		int_w(CLEAR_LINE);
		m_ius = true;
		return m_vector;
	}
	return 0;
}

void idpartner_floppy_daisy_device::z80daisy_irq_reti()
{
	if (m_ius)
		m_ius = false;
}

DEFINE_DEVICE_TYPE(IDPARTNER_FLOPPY_DAISY, idpartner_floppy_daisy_device, "idpartner_floppy_daisy", "Iskra Delta Partner floppy daisy chain device")

idpartner_floppy_daisy_device::idpartner_floppy_daisy_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: device_t(mconfig, IDPARTNER_FLOPPY_DAISY, tag, owner, clock)
	, device_z80daisy_interface(mconfig, *this)
	, m_int_handler(*this)
	, m_int(0)
	, m_ius(false)
	, m_vector(0xff)
{
}

namespace {

class idpartner_state : public driver_device
{
public:
	idpartner_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_bus(*this, "j2")
		, m_conn(*this, "j2:%d", 0U)
		, m_sio1(*this, "sio1")
		, m_sio2(*this, "sio2")
		, m_ctc(*this, "ctc")
		, m_dma(*this, "dma")
		, m_pio(*this, "pio")
		, m_fdc(*this, "fdc")
		, m_fdc_daisy(*this, "fdc_daisy")
		, m_brg(*this, "brg")
		, m_rtc(*this, "rtc")
		, m_serial(*this, "serial_%d",0U)
		, m_floppy(*this, "fdc:%d",0U)
		, m_ram(*this, "ram", 0x20000, ENDIANNESS_LITTLE)
		, m_rom(*this, "maincpu")
		, m_bankr0(*this, "bankr0")
		, m_bankw0(*this, "bankw0")
		, m_bank1(*this, "bank1")
		, m_bank2(*this, "bank2")
	{ }

	void partner_base(machine_config &config);
	void partnerw(machine_config &config);
	void partner1fg(machine_config &config);
	void partnerwfg(machine_config &config);

	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

private:
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	void update_bank();

	u8 rom_bank_r() { m_rom_enabled = false; update_bank(); return 0xff; }
	void rom_bank_w(u8 data) { m_rom_enabled = false; update_bank(); }
	u8 bank1_r() { m_bank = 0; update_bank(); return 0xff; }
	void bank1_w(u8 data) { m_bank = 0; update_bank(); }
	u8 bank2_r() { m_bank = 1; update_bank(); return 0xff; }
	void bank2_w(u8 data) { m_bank = 1; update_bank(); }
	void fdc_vector_w(u8 data) { m_fdc_daisy->set_vector(data); }
	void floppy_motor_w(u8 data) { m_floppy_motor = 1; update_floppy_motor(); }
	u8 floppy_motor_r() { return m_floppy_motor; }
	void update_floppy_motor();
	void xx2_w(int state) { if (state) { m_floppy_motor = 0; update_floppy_motor(); } }
	void write_speed1_clock(int state) { m_sio1->txca_w(state); m_sio1->rxca_w(state); }
	void write_speed2_clock(int state) { m_sio1->txcb_w(state); m_sio1->rxcb_w(state); }
	void write_speed3_clock(int state) { m_sio2->txca_w(state); m_sio2->rxca_w(state); }
	void write_speed4_clock(int state) { m_sio2->txcb_w(state); m_sio2->rxcb_w(state); }
	uint8_t memory_read_byte(offs_t offset) { return m_program.read_byte(offset); }
	void memory_write_byte(offs_t offset, uint8_t data) { m_program.write_byte(offset, data); }
	uint8_t io_read_byte(offs_t offset) { if (offset==0xf1) return m_fdc->dma_r(); else return m_io.read_byte(offset); }
	void io_write_byte(offs_t offset, uint8_t data) { if (offset==0xf1) m_fdc->dma_w(data); else m_io.write_byte(offset, data); }

	u8 m_bank;
	bool m_rom_enabled;
	u8 m_floppy_motor;

	required_device<z80_device> m_maincpu;
	required_device<bus::idpartner::bus_device> m_bus;
	required_device_array<bus::idpartner::bus_connector_device, 2> m_conn;
	required_device<z80sio_device> m_sio1;
	required_device<z80sio_device> m_sio2;
	required_device<z80ctc_device> m_ctc;
	optional_device<z80dma_device> m_dma;
	required_device<z80pio_device> m_pio;
	required_device<i8272a_device> m_fdc;
	required_device<idpartner_floppy_daisy_device> m_fdc_daisy;
	required_device<mc14411_device> m_brg;
	required_device<mm58167_device> m_rtc;
	required_device_array<rs232_port_device,4> m_serial;
	required_device_array<floppy_connector, 2> m_floppy;
	memory_share_creator<u8> m_ram;
	required_region_ptr<u8> m_rom;
	required_memory_bank m_bankr0;
	required_memory_bank m_bankw0;
	required_memory_bank m_bank1;
	required_memory_bank m_bank2;
	memory_access<16, 0, 0, ENDIANNESS_LITTLE>::specific m_program;
	memory_access<16, 0, 0, ENDIANNESS_LITTLE>::specific m_io;
};

void idpartner_state::machine_start()
{
	// 16 KB actually unused
	m_bankr0->configure_entry(0, m_ram + 0x00000);
	m_bankr0->configure_entry(1, m_ram + 0x10000);
	m_bankr0->configure_entry(2, m_rom);
	m_bankw0->configure_entry(0, m_ram + 0x00000);
	m_bankw0->configure_entry(1, m_ram + 0x10000);
	m_bankw0->configure_entry(2, m_rom);

	m_bank1->configure_entry(0, m_ram + 0x02000);
	m_bank1->configure_entry(1, m_ram + 0x12000);

	// Last 16KB is always same
	m_bank2->configure_entry(0, m_ram + 0x0c000);
	m_bank2->set_entry(0);

	m_maincpu->space(AS_PROGRAM).specific(m_program);
	m_maincpu->space(AS_IO).specific(m_io);
}

void idpartner_state::update_bank()
{
	if (m_rom_enabled)
		m_bankr0->set_entry(2);
	else
	{
		m_bankr0->set_entry(m_bank);
		m_bankw0->set_entry(m_bank);
	}
	m_bank1->set_entry(m_bank);
}

void idpartner_state::update_floppy_motor()
{
	for (auto &drive : m_floppy)
	{
		if (drive->get_device())
			drive->get_device()->mon_w(m_floppy_motor ^ 1);
	}
}

void idpartner_state::machine_reset()
{
	m_bank = 0;
	m_floppy_motor = 0;
	update_floppy_motor();
	m_rom_enabled = true;
	update_bank();
}

/* Address maps */
void idpartner_state::mem_map(address_map &map)
{
	map(0x0000, 0x1fff).bankr("bankr0").bankw("bankw0");
	map(0x2000, 0xbfff).bankrw("bank1");
	map(0xc000, 0xffff).bankrw("bank2");
}
void idpartner_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x80,0x87).rw(FUNC(idpartner_state::rom_bank_r), FUNC(idpartner_state::rom_bank_w)); // ROM bank
	map(0x88,0x8f).rw(FUNC(idpartner_state::bank1_r), FUNC(idpartner_state::bank1_w)); // RAM bank 1
	map(0x90,0x97).rw(FUNC(idpartner_state::bank2_r), FUNC(idpartner_state::bank2_w)); // RAM bank 2
	map(0x98,0x9f).rw(FUNC(idpartner_state::floppy_motor_r), FUNC(idpartner_state::floppy_motor_w)); // floppy motors
	map(0xa0,0xbf).rw(m_rtc, FUNC(mm58167_device::read), FUNC(mm58167_device::write));
	map(0xc0,0xc7).rw(m_dma, FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	map(0xc8,0xcb).mirror(0x04).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));  // CTC - A2 not connected
	map(0xd0,0xd3).mirror(0x04).rw(m_pio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0xd8,0xdb).mirror(0x04).rw(m_sio1, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w)); // SIO1 - A2 not connected
	map(0xe0,0xe3).mirror(0x04).rw(m_sio2, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w)); // SIO2 - A2 not connected
	map(0xe8,0xef).w(FUNC(idpartner_state::fdc_vector_w)); // FDC interrupt vector
	map(0xf0,0xf1).mirror(0x06).m(m_fdc, FUNC(i8272a_device::map));
	//map(0xf8,0xff) //
}

/* Input ports */
static INPUT_PORTS_START( idpartner )
INPUT_PORTS_END

static const z80_daisy_config daisy_chain[] =
{
	{ "ctc" },
	{ "dma" },
	{ "sio1" },
	{ "sio2" },
	{ "pio" },
	{ "fdc_daisy" },
	{ nullptr }
};

static void partner_floppies(device_slot_interface &device)
{
	device.option_add("fdd", FLOPPY_525_QD);
}

static void partner_floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_IDPART_FORMAT);
}


static DEVICE_INPUT_DEFAULTS_START(keyboard)
	DEVICE_INPUT_DEFAULTS("RS232_TXBAUD", 0xff, RS232_BAUD_300)
DEVICE_INPUT_DEFAULTS_END


void partner_rs232_devices(device_slot_interface &device)
{
	default_rs232_devices(device);
	device.option_add("idpart_video",  IDPART_VIDEO);
}

/* Machine driver */
void idpartner_state::partner_base(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(8'000'000) / 2);
	m_maincpu->set_daisy_config(daisy_chain);
	m_maincpu->set_addrmap(AS_PROGRAM, &idpartner_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &idpartner_state::io_map);
	m_maincpu->busack_cb().set(m_dma, FUNC(z80dma_device::bai_w));

	Z80SIO(config, m_sio1, XTAL(8'000'000) / 2);
	m_sio1->out_txda_callback().set(m_serial[0], FUNC(rs232_port_device::write_txd));
	m_sio1->out_txdb_callback().set(m_serial[1], FUNC(rs232_port_device::write_txd));
	m_sio1->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	MC14411(config, m_brg, XTAL(1'843'200));
	m_brg->rsa_w(0);
	m_brg->rsb_w(1);
	m_brg->out_f<1>().set(FUNC(idpartner_state::write_speed2_clock));
	m_brg->out_f<1>().append(FUNC(idpartner_state::write_speed3_clock));
	m_brg->out_f<1>().append(FUNC(idpartner_state::write_speed4_clock));
	m_brg->out_f<13>().set(m_ctc, FUNC(z80ctc_device::trg0)); // signal XX1

	RS232_PORT(config, m_serial[0], partner_rs232_devices, nullptr);
	m_serial[0]->rxd_handler().set(m_sio1, FUNC(z80sio_device::rxa_w));

	RS232_PORT(config, m_serial[1], default_rs232_devices, nullptr);
	m_serial[1]->rxd_handler().set(m_sio1, FUNC(z80sio_device::rxb_w));

	Z80SIO(config, m_sio2, XTAL(8'000'000) / 2);
	m_sio2->out_txda_callback().set(m_serial[2], FUNC(rs232_port_device::write_txd));
	m_sio2->out_txdb_callback().set(m_serial[3], FUNC(rs232_port_device::write_txd));
	m_sio2->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	RS232_PORT(config, m_serial[2], default_rs232_devices, nullptr);
	m_serial[2]->rxd_handler().set(m_sio2, FUNC(z80sio_device::rxa_w));

	RS232_PORT(config, m_serial[3], default_rs232_devices, nullptr);
	m_serial[3]->rxd_handler().set(m_sio2, FUNC(z80sio_device::rxb_w));

	Z80CTC(config, m_ctc, XTAL(8'000'000) / 2);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->zc_callback<0>().set(m_ctc, FUNC(z80ctc_device::trg1));
	m_ctc->zc_callback<1>().set(FUNC(idpartner_state::xx2_w));
	m_ctc->zc_callback<2>().set(m_ctc, FUNC(z80ctc_device::trg3));  // optional

	Z80DMA(config, m_dma, XTAL(8'000'000) / 2);
	m_dma->out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	m_dma->out_int_callback().set(m_fdc, FUNC(i8272a_device::tc_line_w));
	m_dma->in_mreq_callback().set(FUNC(idpartner_state::memory_read_byte));
	m_dma->out_mreq_callback().set(FUNC(idpartner_state::memory_write_byte));
	m_dma->in_iorq_callback().set(FUNC(idpartner_state::io_read_byte));
	m_dma->out_iorq_callback().set(FUNC(idpartner_state::io_write_byte));

	IDPARTNER_FLOPPY_DAISY(config, m_fdc_daisy);
	m_fdc_daisy->int_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	I8272A(config, m_fdc, XTAL(8'000'000), false);
	m_fdc->intrq_wr_callback().set(m_fdc_daisy, FUNC(idpartner_floppy_daisy_device::int_w));
	m_fdc->drq_wr_callback().set(m_dma, FUNC(z80dma_device::rdy_w));
	FLOPPY_CONNECTOR(config, m_floppy[0], partner_floppies, "fdd",   partner_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], partner_floppies, nullptr, partner_floppy_formats).enable_sound(true);

	MM58167(config, m_rtc, XTAL(32'768));

	Z80PIO(config, m_pio, XTAL(8'000'000) / 2);
	m_pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	// There is one bus connector J2, but cable goes to up to two devices
	IDPARTNER_BUS(config, m_bus, 0);
	m_bus->set_io_space(m_maincpu, AS_IO);
	m_bus->int_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_bus->nmi_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_bus->drq_handler().set(m_dma, FUNC(z80dma_device::rdy_w));
	IDPARTNER_BUS_CONNECTOR(config, m_conn[0], m_bus, idpartner_exp_devices, nullptr);
	IDPARTNER_BUS_CONNECTOR(config, m_conn[1], m_bus, idpartner_exp_devices, nullptr);
}

void idpartner_state::partnerw(machine_config &config)
{
	partner_base(config);

	m_brg->out_f<1>().append(FUNC(idpartner_state::write_speed1_clock));

	m_serial[0]->set_default_option("idpart_video");

	m_conn[1]->set_default_option("sasi");
}

void idpartner_state::partner1fg(machine_config &config)
{
	partner_base(config);

	m_brg->out_f<9>().set(FUNC(idpartner_state::write_speed1_clock));

	m_serial[0]->set_default_option("keyboard");
	m_serial[0]->set_option_device_input_defaults("keyboard", DEVICE_INPUT_DEFAULTS_NAME(keyboard));

	m_conn[0]->set_default_option("gdp");
}

void idpartner_state::partnerwfg(machine_config &config)
{
	partner_base(config);

	m_brg->out_f<9>().set(FUNC(idpartner_state::write_speed1_clock));
	// TODO: This should be 13 from MC14411 but there are timing issues when
	// SIO is at lower speed ( < 1200 baud )
	m_brg->out_f<14>().set(m_ctc, FUNC(z80ctc_device::trg0)); // signal XX1
	m_brg->out_f<13>().set_nop();

	m_serial[0]->set_default_option("keyboard");
	m_serial[0]->set_option_device_input_defaults("keyboard", DEVICE_INPUT_DEFAULTS_NAME(keyboard));

	m_conn[0]->set_default_option("gdp");
	m_conn[1]->set_default_option("sasi");
}

/* ROM definition */

ROM_START( partnerw )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "partner.e51",     0x0000, 0x800, CRC(cabcf36e) SHA1(9c391bacb8d1a742cf74803c61cc061707ab23f4) )
	// e50 is empty
ROM_END

ROM_START( partner1fg )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "partner1fg.e51",  0x0000, 0x800, CRC(571e297a) SHA1(05379c75d6ceb338e49958576f3a1c844f202a00) )
	// e50 is empty
ROM_END

ROM_START( partnerwfg )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "partnerwfg.e51",  0x0000, 0x800, CRC(81a2a3db) SHA1(22b23969d38cf2b400be0042dbdd6f8cff2536be) )
	// e50 is empty
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME        PARENT      COMPAT  MACHINE        INPUT        CLASS            INIT        COMPANY         FULLNAME         FLAGS */
COMP( 1984, partnerw,   0,          0,      partnerw,      idpartner,   idpartner_state, empty_init, "Iskra Delta",  "Partner",       MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )
COMP( 1987, partner1fg, partnerw,   0,      partner1fg,    idpartner,   idpartner_state, empty_init, "Iskra Delta",  "Partner 1F/G",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )
COMP( 1987, partnerwfg, partnerw,   0,      partnerwfg,    idpartner,   idpartner_state, empty_init, "Iskra Delta",  "Partner WF/G",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



ie15.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************

    15IE-00-013 Terminal

    A serial (RS232 or current loop) green-screen terminal, mostly VT52
    compatible (no Hold Screen mode and no graphics character set).

    Alternate character set (selected by SO/SI chars) is Cyrillic.

****************************************************************************/


#include "emu.h"

#include "bus/rs232/rs232.h"
#include "machine/ie15.h"
#include "machine/ie15_kbd.h"


namespace {

class ie15_state : public driver_device
{
public:
	ie15_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_ie15(*this, "ie15")
		, m_rs232(*this, "rs232")
	{ }

	void ie15(machine_config &config);

private:
	required_device<ie15_device> m_ie15;
	required_device<rs232_port_device> m_rs232;
};


void ie15_state::ie15(machine_config &config)
{
	IE15(config, m_ie15, 0);

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	//rs232.dcd_handler().set("ie15", FUNC(ie15_device::rs232_conn_dcd_w));
	//rs232.dsr_handler().set("ie15", FUNC(ie15_device::rs232_conn_dsr_w));
	//rs232.ri_handler().set("ie15", FUNC(ie15_device::rs232_conn_ri_w));
	//rs232.cts_handler().set("ie15", FUNC(ie15_device::rs232_conn_cts_w));
	rs232.rxd_handler().set("ie15", FUNC(ie15_device::rs232_conn_rxd_w));

	m_ie15->rs232_conn_txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_ie15->rs232_conn_dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	m_ie15->rs232_conn_rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
}


ROM_START(ie15)
ROM_END

} // anonymous namespace


//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY  FULLNAME       FLAGS
COMP( 1980, ie15, 0,      0,      ie15,    0,     ie15_state, empty_init, "USSR",  "15IE-00-013", 0)



iez80.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    Insight Enterprises Z80 Single Board Computer

    Possibly a prototype or part of a larger system. A video with more
    info is available at https://www.youtube.com/watch?v=_z1kBb-Zwpg.

    TODO:
    - Verify device hookup
    - Figure out what's at 0x20, 0x21, 0x3c, 0x3d
    - Hook up FDC
    - Hook up CRT8002 (video attributes)
    - Much more

***************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/upd765.h"
#include "machine/z80ctc.h"
#include "machine/z80dma.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"
#include "video/tms9927.h"
#include "emupal.h"
#include "screen.h"


namespace {


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class iez80_state : public driver_device
{
public:
	iez80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_crtc(*this, "crtc"),
		m_palette(*this, "palette"),
		m_chargen(*this, "chargen"),
		m_vram(*this, "vram")
	{ }

	void iez80(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;
	required_device<crt5037_device> m_crtc;
	required_device<palette_device> m_palette;
	required_region_ptr<uint8_t> m_chargen;
	required_shared_ptr<uint8_t> m_vram;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void iez80_state::mem_map(address_map &map)
{
	map(0x0000, 0x0fff).rom().region("maincpu", 0);
	map(0x2000, 0x2fff).ram().share("vram");
	map(0xe000, 0xffff).ram();
}

void iez80_state::io_map(address_map &map)
{
	map.global_mask(0xff);
//  map(0x20, 0x20).lr8([this]() { return machine().rand(); }, "unk20");
//  map(0x21, 0x21).lr8([this]() { return machine().rand(); }, "unk21");
	map(0x24, 0x27).rw("pio", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x28, 0x2b).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x2c, 0x2f).rw("dart1", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x32, 0x32).rw("dma", FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	map(0x38, 0x3b).rw("dart2", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
//  map(0x3c, 0x3c).lr8([this]() { return machine().rand(); }, "unk3c");
//  map(0x3d, 0x3d).lr8([this]() { return machine().rand(); }, "unk3d");
	map(0x40, 0x4f).rw("crtc", FUNC(crt5037_device::read), FUNC(crt5037_device::write));
}


//**************************************************************************
//  INPUT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( iez80 )
INPUT_PORTS_END


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

uint32_t iez80_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	rgb_t const *const pen = m_palette->palette()->entry_list_raw();

	rectangle cursor;
	m_crtc->cursor_bounds(cursor);

	for (int y = 0; y < 24; y++)
	{
		for (int ra = 0; ra < 12; ra++)
		{
			for (int x = 0; x < 80; x++)
			{
				uint8_t chr = m_vram[(y * 160) + (x * 2) + 0];
				// uint8_t attr = m_vram[(y * 160) + (x * 2) + 1];
				uint8_t data = m_chargen[((chr << 4) | ra) & 0x7ff];

				if (cursor.contains(x * 8, y * 12))
					data ^= 0xff;

				// draw 8 pixels of the char
				for (int i = 0; i < 8; i++)
					bitmap.pix(y * 12 + ra, x * 8 + i) = pen[BIT(data, 7 - i)];
			}
		}
	}

	return 0;
}

static const gfx_layout crt8002_charlayout =
{
	8, 12,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP8(0,1) },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8*16
};

static GFXDECODE_START( gfx_crt8002 )
	GFXDECODE_ENTRY("chargen", 0, crt8002_charlayout, 0, 1)
GFXDECODE_END


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void iez80_state::machine_start()
{
}

void iez80_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

static const z80_daisy_config daisy_chain[] =
{
	{ "ctc" },
	{ nullptr }
};

void iez80_state::iez80(machine_config &config)
{
	Z80(config, m_maincpu, 2'500'000); // unknown clock
	m_maincpu->set_addrmap(AS_PROGRAM, &iez80_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &iez80_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	Z80DMA(config, "dma", 4'000'000); // unknown clock

	z80ctc_device& ctc(Z80CTC(config, "ctc", 4'000'000)); // unknown clock
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80PIO(config, "pio", 4'000'000); // unknown clock

	Z80DART(config, "dart1", 4'000'000); // unknown clock (5.0688_MHz_XTAL near)

	Z80DART(config, "dart2", 4'000'000); // unknown clock (5.0688_MHz_XTAL near)

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(17'550'000, 816, 0, 640, 358, 0, 288); // unknown clock, hand-tuned to ~60fps
	screen.set_screen_update(FUNC(iez80_state::screen_update));

	GFXDECODE(config, "gfxdecode", "palette", gfx_crt8002);

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	CRT5037(config, m_crtc, 17'550'000 / 8); // unknown clock
	m_crtc->set_char_width(8);
	m_crtc->set_screen("screen");
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( iez80 )
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("iez80.bin", 0x0000, 0x1000, CRC(d73137d2) SHA1(a340a47c5f37bbef244d35d581ff0beeeec5d677))

	ROM_REGION(0x800, "chargen", 0)
	ROM_LOAD("8002.bin", 0x0000, 0x0800, CRC(fdd6eb13) SHA1(a094d416e66bdab916e72238112a6265a75ca690))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY                FULLNAME                FLAGS
COMP( 1983, iez80, 0,      0,      iez80,   iez80, iez80_state, empty_init, "Insight Enterprises", "Z80 SBC (prototype?)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



if800.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
/***************************************************************************

    if800

****************************************************************************/


#include "emu.h"
#include "cpu/i86/i86.h"
#include "machine/pic8259.h"
#include "video/upd7220.h"
#include "emupal.h"
#include "screen.h"


namespace {

class if800_state : public driver_device
{
public:
	if800_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_hgdc(*this, "upd7220"),
		m_video_ram(*this, "video_ram"),
		m_maincpu(*this, "maincpu"),
		m_palette(*this, "palette") { }

	void if800(machine_config &config);

private:
	required_device<upd7220_device> m_hgdc;

	required_shared_ptr<uint16_t> m_video_ram;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_device<palette_device> m_palette;
	UPD7220_DISPLAY_PIXELS_MEMBER( hgdc_display_pixels );
	void if800_io(address_map &map) ATTR_COLD;
	void if800_map(address_map &map) ATTR_COLD;
	void upd7220_map(address_map &map) ATTR_COLD;
};

UPD7220_DISPLAY_PIXELS_MEMBER( if800_state::hgdc_display_pixels )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();

	int gfx = m_video_ram[address];

	for(int xi=0;xi<16;xi++)
	{
		uint8_t pen = BIT(gfx, xi);

		bitmap.pix(y, x + xi) = palette[pen];
	}
}

void if800_state::if800_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x0ffff).ram();
	map(0xfe000, 0xfffff).rom().region("ipl", 0);
}

void if800_state::if800_io(address_map &map)
{
	map.unmap_value_high();
//  map(0x0640, 0x065f) dma?
	map(0x0660, 0x0663).rw(m_hgdc, FUNC(upd7220_device::read), FUNC(upd7220_device::write)).umask16(0x00ff);
}

/* Input ports */
static INPUT_PORTS_START( if800 )
INPUT_PORTS_END

void if800_state::machine_start()
{
}


void if800_state::machine_reset()
{
}

void if800_state::upd7220_map(address_map &map)
{
	map(0x00000, 0x3ffff).ram().share("video_ram");
}

void if800_state::if800(machine_config &config)
{
	/* basic machine hardware */
	I8086(config, m_maincpu, 8000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &if800_state::if800_map);
	m_maincpu->set_addrmap(AS_IO, &if800_state::if800_io);


//  PIC8259(config, "pic8259", 0);
	UPD7220(config, m_hgdc, 8000000 / 2);
	m_hgdc->set_addrmap(0, &if800_state::upd7220_map);
	m_hgdc->set_display_pixels(FUNC(if800_state::hgdc_display_pixels));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	screen.set_screen_update("upd7220", FUNC(upd7220_device::screen_update));

	PALETTE(config, m_palette).set_entries(8);
}

/* ROM definition */
ROM_START( if800 )
	ROM_REGION16_LE( 0x2000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "ipl.rom", 0x0000, 0x2000, CRC(36212491) SHA1(6eaa8885e2dccb6dd86def6c0c9be1870cee957f))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY         FULLNAME          FLAGS
COMP( 1985, if800, 0,      0,      if800,   if800, if800_state, empty_init, "Oki Electric", "if800 model 60", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



ikt5a.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Skeleton driver for IKT-5A terminal.

*******************************************************************************/

#include "emu.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "bus/rs232/rs232.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/eepromser.h"
#include "emupal.h"
#include "screen.h"


namespace {

class ikt5a_state : public driver_device
{
public:
	ikt5a_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_eeprom(*this, "eeprom")
		, m_keyboard(*this, "keyboard")
		, m_rs232(*this, "rs232")
		, m_chargen(*this, "chargen")
		, m_keyboard_clk(true)
		, m_keyboard_data(true)
		, m_keyboard_shifter(0)
	{
	}

	void ikt5a(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void keyboard_clk_w(int state);
	void keyboard_data_w(int state);

	void eeprom_w(u8 data);
	void keyboard_ack_w(u8 data);
	void keyboard_shift_w(u8 data);
	u8 p1_r();
	void p1_w(u8 data);
	u8 p3_r();

	void prog_map(address_map &map) ATTR_COLD;
	void ext_map(address_map &map) ATTR_COLD;

	required_device<mcs51_cpu_device> m_maincpu;
	required_device<eeprom_serial_93cxx_device> m_eeprom;
	required_device<pc_kbdc_device> m_keyboard;
	required_device<rs232_port_device> m_rs232;
	required_region_ptr<u8> m_chargen;

	bool m_keyboard_clk;
	bool m_keyboard_data;
	u8 m_keyboard_shifter;
};

void ikt5a_state::machine_start()
{
	save_item(NAME(m_keyboard_clk));
	save_item(NAME(m_keyboard_data));
	save_item(NAME(m_keyboard_shifter));
}

u32 ikt5a_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void ikt5a_state::keyboard_clk_w(int state)
{
	if (m_keyboard_clk && !state)
	{
		m_maincpu->set_input_line(MCS51_INT1_LINE, BIT(m_keyboard_shifter, 0) ? ASSERT_LINE : CLEAR_LINE);
		m_keyboard_shifter >>= 1;
		if (m_keyboard_data)
			m_keyboard_shifter |= 0x80;
	}
	m_keyboard_clk = state;
}

void ikt5a_state::keyboard_data_w(int state)
{
	m_keyboard_data = state;
}

void ikt5a_state::eeprom_w(u8 data)
{
	m_eeprom->cs_write(BIT(data, 6));
	m_eeprom->di_write(BIT(data, 3));
	m_eeprom->clk_write(BIT(data, 1));

	m_keyboard->clock_write_from_mb(!BIT(data, 0));
}

void ikt5a_state::keyboard_ack_w(u8 data)
{
	m_maincpu->set_input_line(MCS51_INT1_LINE, CLEAR_LINE);
	m_keyboard_shifter = 0;
}

void ikt5a_state::keyboard_shift_w(u8 data)
{
	m_maincpu->set_input_line(MCS51_INT1_LINE, BIT(m_keyboard_shifter, 0) ? ASSERT_LINE : CLEAR_LINE);
	m_keyboard_shifter >>= 1;
}

u8 ikt5a_state::p1_r()
{
	return 0xff;
}

void ikt5a_state::p1_w(u8 data)
{
	// TODO: P1.1 is keyboard-related
}

u8 ikt5a_state::p3_r()
{
	return 0xde | (m_eeprom->do_read() << 5) | m_rs232->rxd_r();
}

void ikt5a_state::prog_map(address_map &map)
{
	map(0x0000, 0x3fff).rom().region("maincpu", 0);
}

void ikt5a_state::ext_map(address_map &map)
{
	map(0x6400, 0x6400).mirror(0xff).w(FUNC(ikt5a_state::eeprom_w));
	map(0x7000, 0x7000).mirror(0xff).w(FUNC(ikt5a_state::keyboard_ack_w));
	map(0x7c00, 0x7c00).mirror(0xff).w(FUNC(ikt5a_state::keyboard_shift_w));
	map(0x8000, 0x9fff).ram();
}

static const gfx_layout ikt5a_charlayout =
{
	8, 14,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP8(0,1) },
	{ 15*8, 4*8, 12*8, 2*8, 10*8, 6*8, 14*8, 1*8, 9*8, 5*8, 13*8, 3*8, 11*8, 7*8 },
	16*8
};

static GFXDECODE_START(gfx_ikt5a)
	GFXDECODE_ENTRY("chargen", 0x0000, ikt5a_charlayout, 0, 1)
GFXDECODE_END

void ikt5a_state::ikt5a(machine_config &config)
{
	I80C51(config, m_maincpu, 15_MHz_XTAL / 2); // PCB 80C51BH-2 (clock uncertain)
	m_maincpu->set_addrmap(AS_PROGRAM, &ikt5a_state::prog_map);
	m_maincpu->set_addrmap(AS_IO, &ikt5a_state::ext_map);
	m_maincpu->port_in_cb<1>().set(FUNC(ikt5a_state::p1_r));
	m_maincpu->port_out_cb<1>().set(FUNC(ikt5a_state::p1_w));
	m_maincpu->port_in_cb<3>().set(FUNC(ikt5a_state::p3_r));
	m_maincpu->port_out_cb<3>().set(m_rs232, FUNC(rs232_port_device::write_txd)).bit(1);

	EEPROM_93C06_16BIT(config, m_eeprom); // ST M9306B6

	PC_KBDC(config, m_keyboard, pc_xt_keyboards, STR_KBD_IBM_PC_XT_83);
	m_keyboard->out_clock_cb().set(FUNC(ikt5a_state::keyboard_clk_w));
	m_keyboard->out_data_cb().set(FUNC(ikt5a_state::keyboard_data_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(15_MHz_XTAL, 800, 0, 640, 375, 0, 350); // timings guessed
	screen.set_screen_update(FUNC(ikt5a_state::screen_update));
	screen.screen_vblank().set_inputline(m_maincpu, MCS51_INT0_LINE);

	GFXDECODE(config, "gfxdecode", "palette", gfx_ikt5a);
	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	RS232_PORT(config, m_rs232, default_rs232_devices, "loopback");
}

static INPUT_PORTS_START(ikt5a)
INPUT_PORTS_END

ROM_START(ikt5a) // 80C51 (+xtal 15.000) // 8k ram // RGB external, uses XT keyboard
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_LOAD("ver_ih.bin",   0x0000, 0x4000, CRC(5a15b4e8) SHA1(cc0336892279b730f1596f31e129c5a898ecdc8f))

	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD("g26.bin",      0x0000, 0x2000, CRC(657668be) SHA1(212a9eb1fb9b9c16f3cc606c6befbd913ddfa395))
ROM_END

} // anonymous namespace


COMP(1993, ikt5a, 0, 0, ikt5a, ikt5a, ikt5a_state, empty_init, "Creator / Fura Elektronik", "IKT-5A", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



im01.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Radon17, RCgoff, Berger
/*******************************************************************************

Электроника ИМ-01 (Elektronika IM-01)

Soviet chess computer, produced by Svetana from 1986-1992.
ИМ-01Т is the same hardware, improved program and has 1 more difficulty level.
ИМ-05 is also on similar hardware, it was released after the Soviet dissolution.

TODO:
- emulate К1801ВМ1/2, using T11 for now and I hope it works ok
- emulate K1809BB1, IRQ is from here too (measured 177.4Hz)
- emulate К1573ХМ1
- It's running a bit too fast?: CPU clock was measured 4.61MHz, beeper
  frequency 3.73kHz and beeper duration 34.2ms. In MAME, beeper frequency is
  4.15kHz and duration is 31ms, meaning it's around 1.1 times faster, maybe
  К1801ВМ1 internal timing differs from T11, and/or MAME's T11 core timing
  itself is not 100% accurate.
- Is im01t/im05 extra RAM chip used at all? Even when trying to solve mate
  problems (level 7, and overclocked CPU), there are no writes to it.
- What is im01 0x6000-0x7fff for? it will test for both RAM and ROM in this
  area if no bus error was triggered, and will fail to boot up.
- verify im05 XTAL and IRQ
- verify im05 button functions

================================================================================

Hardware notes:

ИМ-01:
- К1801ВМ1 CPU (PDP-11 derived) @ ~4.61MHz (9216 кгц XTAL)
- 16KB ROM (2*К1809РЕ1), 2KB RAM(К1809РУ1)
- K1809BB1 (I/O, counter)
- 4-digit VFD 7seg panel(cyan, green window overlay), beeper

ИМ-01Т:
- 4KB RAM (2*К1809РУ1), but also seen with 1 К1809РУ1 like ИМ-01
- rest is same as ИМ-01

ИМ-05:
- КМ1801ВМ2 CPU (similar to К1801ВМ1)
- 24KB ROM (3*К1809РЕ1), 4KB RAM (2*КР573РУ10)
- К1573ХМ1 (I/O controller)
- rest is same as ИМ-01

================================================================================

ИМ-01 keypad legend (excluding A1-H8 and black/white):

Фиг: префиксная клавиша для ввода кода шахматной фигуры, - Prefix Key (hold)
     а также для установки фигур в начальную позицию,
     сброса фигур с доски и изменения очередности хода

НП:  установка фигур в начальную позицию                 - Reset Board*
СД:  сброс всех фигур с доски                            - Clear Board*
↓:   ввод в компьютер Вашего хода,                       - Enter
     а также фигуры при установке позиции

ПХ:  передача хода компьютера                            - Move
≡:   индикация уровня сложности                          - Set Level (hold)
↑:   индикация текущего уровня                           - Show Depth
CИ:  сброс информации на индикаторе                      - Clear Entry
?:   проверка позиции                                    - Verify Position
Вар: ввод варианта                                       - Enter Variant
     (несколько ходов подряд за белых и черных)

↺:   ход назад                                           - Take Back
И:   индикация анализируемого хода                       - Show Analyzed Move
N:   чксло ходов                                         - Show Moves
РЗ:  установка режима решения шахматных задач            - Solve Mate / Set Up*

* note: hold Фиг

ИМ-05 keypad legend (differences shown, preliminary):

Реж: режима                                              - Mode (no hold)
НП:  -                                                   - Reset
СД:  -                                                   - Cancel?
↓:   -                                                   - Confirm (level/piece)
→:   -                                                   - Forward
CТА: -                                                   - Enter Move
ПХ:  -                                                   - Hint
Р:   -                                                   - Promotion (hold)

*******************************************************************************/

#include "emu.h"

#include "cpu/t11/t11.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "im01.lh"
#include "im05.lh"


namespace {

class im01_state : public driver_device
{
public:
	im01_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void im01(machine_config &config);
	void im01t(machine_config &config);
	void im05(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<t11_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<6> m_inputs;

	u16 m_inp_mux = 0;
	u16 m_digit_data = 0;

	void im01_map(address_map &map) ATTR_COLD;
	void im01t_map(address_map &map) ATTR_COLD;
	void im05_map(address_map &map) ATTR_COLD;

	u8 irq_callback(offs_t offset);
	INTERRUPT_GEN_MEMBER(interrupt);

	// I/O handlers
	u16 mux_r(offs_t offset, u16 mem_mask);
	void mux_w(offs_t offset, u16 data, u16 mem_mask);
	u16 digit_r(offs_t offset, u16 mem_mask);
	void digit_w(offs_t offset, u16 data, u16 mem_mask);
	void digita_w(offs_t offset, u16 data, u16 mem_mask);
	u16 input_r(offs_t offset, u16 mem_mask);
	u16 inputa_r(offs_t offset, u16 mem_mask);
	void error_w(offs_t offset, u16 data, u16 mem_mask);
};

void im01_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_digit_data));
}



/*******************************************************************************
    Interrupts
*******************************************************************************/

u8 im01_state::irq_callback(offs_t offset)
{
	m_maincpu->set_input_line(t11_device::CP0_LINE, CLEAR_LINE);
	m_maincpu->set_input_line(t11_device::CP1_LINE, CLEAR_LINE);
	m_maincpu->set_input_line(t11_device::CP3_LINE, CLEAR_LINE);
	return 0;
}

INTERRUPT_GEN_MEMBER(im01_state::interrupt)
{
	// indirect interrupt vector at 0100
	m_maincpu->set_input_line(t11_device::CP0_LINE, ASSERT_LINE);
	m_maincpu->set_input_line(t11_device::CP1_LINE, ASSERT_LINE);
	m_maincpu->set_input_line(t11_device::CP3_LINE, ASSERT_LINE);
}



/*******************************************************************************
    I/O
*******************************************************************************/

// ИМ-01 / shared

u16 im01_state::mux_r(offs_t offset, u16 mem_mask)
{
	return m_inp_mux;
}

void im01_state::mux_w(offs_t offset, u16 data, u16 mem_mask)
{
	// d0-d5: input mux, digit select
	COMBINE_DATA(&m_inp_mux);
	m_display->write_my(m_inp_mux);

	// d7: speaker out
	m_dac->write(BIT(m_inp_mux, 7));
}

u16 im01_state::digit_r(offs_t offset, u16 mem_mask)
{
	return m_digit_data;
}

void im01_state::digit_w(offs_t offset, u16 data, u16 mem_mask)
{
	// d1-d7: digit segment data
	COMBINE_DATA(&m_digit_data);
	m_display->write_mx(bitswap<8>(m_digit_data,0,1,2,3,4,5,6,7));
}

u16 im01_state::input_r(offs_t offset, u16 mem_mask)
{
	u16 data = 0;

	// d8-d11: multiplexed inputs
	for (int i = 0; i < 6; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read();

	return data << 8;
}

void im01_state::error_w(offs_t offset, u16 data, u16 mem_mask)
{
	// unmapped port, it expects a bus error
	m_maincpu->pulse_input_line(t11_device::BUS_ERROR, attotime::zero);
}


// ИМ-05 specific

void im01_state::digita_w(offs_t offset, u16 data, u16 mem_mask)
{
	// digit segment data is in a different order
	digit_w(offset, data, mem_mask);
	m_display->write_mx(bitswap<8>(m_digit_data,0,1,6,4,2,3,5,7));
}

u16 im01_state::inputa_r(offs_t offset, u16 mem_mask)
{
	// inputs are reversed
	u16 data = input_r(offset, mem_mask);
	return (data & 0xf0ff) | (bitswap<4>(data,8,9,10,11) << 8);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void im01_state::im01_map(address_map &map)
{
	map(0x0000, 0x07ff).ram();
	map(0x2000, 0x5fff).rom();
	map(0xe030, 0xe031).mirror(0x1800).rw(FUNC(im01_state::mux_r), FUNC(im01_state::mux_w));
	map(0xe03c, 0xe03d).mirror(0x1800).rw(FUNC(im01_state::digit_r), FUNC(im01_state::digit_w));
	map(0xe03e, 0xe03f).mirror(0x1800).r(FUNC(im01_state::input_r));
	map(0xffe8, 0xffe9).w(FUNC(im01_state::error_w)).nopr();
}

void im01_state::im01t_map(address_map &map)
{
	im01_map(map);
	map(0x0800, 0x0fff).ram();
}

void im01_state::im05_map(address_map &map)
{
	im01t_map(map);
	map(0x6000, 0x7fff).rom();
	map(0xe03c, 0xe03d).mirror(0x1800).w(FUNC(im01_state::digita_w));
	map(0xe03e, 0xe03f).mirror(0x1800).r(FUNC(im01_state::inputa_r));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( im01 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_D) PORT_NAME("D 4 / Rook")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_C) PORT_NAME("C 3 / Bishop")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_B) PORT_NAME("B 2 / Knight")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_A) PORT_NAME("A 1 / Pawn")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME(u8"N (Show Moves)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME(u8"Вар (Enter Variant)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("? (Verify Position)")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Show Depth")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME(u8"ПХ (Move)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME(u8"CИ (Clear Entry)")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_H) PORT_NAME("H 8")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_G) PORT_NAME("G 7")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_F) PORT_NAME("F 6 / King")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_E) PORT_NAME("E 5 / Queen")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_NAME(u8"Фиг (Prefix Key)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME(u8"И (Show Analyzed Move)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("White")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME(u8"РЗ (Solve Mate / Set Up)")

	PORT_START("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME(u8"НП (Reset Board)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME(u8"СД (Clear Board)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Black")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Set Level")
INPUT_PORTS_END

static INPUT_PORTS_START( im05 )
	PORT_INCLUDE( im01 )

	PORT_MODIFY("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Forward")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_J) PORT_NAME(u8"ПХ (Hint)")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME(u8"Р (Promotion)")

	PORT_MODIFY("IN.2")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Confirm")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME(u8"CТА (Enter Move)")

	PORT_MODIFY("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME(u8"Реж (Mode)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_Z) PORT_NAME("White / 9")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME(u8"N (Show Moves)")

	PORT_MODIFY("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME(u8"НП (Reset)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME(u8"СД (Cancel)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_X) PORT_NAME("Black / 0")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void im01_state::im01(machine_config &config)
{
	// basic machine hardware
	T11(config, m_maincpu, 9.216_MHz_XTAL / 2); // actually К1801ВМ1
	m_maincpu->set_initial_mode(3 << 13);
	m_maincpu->set_addrmap(AS_PROGRAM, &im01_state::im01_map);
	m_maincpu->in_iack().set(FUNC(im01_state::irq_callback));
	m_maincpu->set_periodic_int(FUNC(im01_state::interrupt), attotime::from_hz(177)); // measured

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(5, 7);
	m_display->set_segmask(0x1f, 0x7f);
	config.set_default_layout(layout_im01);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void im01_state::im01t(machine_config &config)
{
	im01(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &im01_state::im01t_map);
}

void im01_state::im05(machine_config &config)
{
	im01(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &im01_state::im05_map);
	m_maincpu->set_periodic_int(FUNC(im01_state::interrupt), attotime::from_hz(512)); // approximation

	// video hardware
	config.set_default_layout(layout_im05);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( im01 )
	ROM_REGION16_LE( 0x10000, "maincpu", 0 )
	ROM_LOAD("0000107", 0x2000, 0x2000, CRC(2318e85b) SHA1(6a92f6464af69ad0175f323f01dd067b91d345d3) )
	ROM_LOAD("0000106", 0x4000, 0x2000, CRC(b0ab8808) SHA1(b682e9e8a5e52cd8fee5ae45277ae658bf5c32f1) )
ROM_END

ROM_START( im01t )
	ROM_REGION16_LE( 0x10000, "maincpu", 0 )
	ROM_LOAD("0000148", 0x2000, 0x2000, CRC(327c6055) SHA1(b90b3b1261d677eb93014ea9e809e45b3b25152a) )
	ROM_LOAD("0000149", 0x4000, 0x2000, CRC(43b14589) SHA1(b083b631f38a26a335226bc474669ef7f332f541) )
ROM_END

ROM_START( im05 )
	ROM_REGION16_LE( 0x10000, "maincpu", 0 )
	ROM_LOAD("0000205", 0x2000, 0x2000, CRC(8aa51bcb) SHA1(211a3fa22627e828841121a735b4625a4bbff4da) )
	ROM_LOAD("0000206", 0x4000, 0x2000, CRC(3d3ecea6) SHA1(045abe1c935a8748b5a20a11f10f34c835c54156) )
	ROM_LOAD("0000207", 0x6000, 0x2000, CRC(3323378a) SHA1(94a890bff34699c8b8c516fb446872089778cf97) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY, FULLNAME, FLAGS
SYST( 1986, im01,  0,      0,      im01,    im01,  im01_state, empty_init, "Svetlana", "Elektronika IM-01", MACHINE_SUPPORTS_SAVE )
SYST( 1991, im01t, im01,   0,      im01t,   im01,  im01_state, empty_init, "Svetlana", "Elektronika IM-01T", MACHINE_SUPPORTS_SAVE )

SYST( 1993, im05,  0,      0,      im05,    im05,  im01_state, empty_init, "Svetlana", "Elektronika IM-05", MACHINE_SUPPORTS_SAVE )



imacg3.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/****************************************************************************

    imacg3.cpp
    iMac G3 (original hardware)
    Preliminary driver by R. Belmont

    This is a "New World" PCI-based machine.

    CPU: PowerPC 750 "G3" @ 233 MHz
    Memory controller/PCI bridge: Motorola MPC106 "Grackle"
    Video: ATI Rage IIc w/2MB VRAM, ATI Rage Pro Turbo w/6MB VRAM on revised model
    USB: OPTi 82C861 PCI/USB controller
    I/O: Paddington PCI I/O ASIC (see heathrow.cpp for details)
    RAM: 2 PC66 DIMM slots, max 128 MB

    PCI addresses scanned: 14.0, 12.0, 0d.0, 0e.0, 0f.0

****************************************************************************/

#include "emu.h"
#include "bus/pci/opti82c861.h"
#include "cpu/powerpc/ppc.h"
#include "machine/dimm_spd.h"
#include "machine/i2cmem.h"
#include "machine/input_merger.h"
#include "machine/mpc106.h"
#include "machine/pci.h"
#include "machine/pci-ide.h"
#include "machine/ram.h"
#include "video/atirage.h"
#include "burgundy.h"
#include "cuda.h"
#include "heathrow.h"
#include "macadb.h"

class imac_state : public driver_device
{
public:
	void imac(machine_config &config);

	imac_state(const machine_config &mconfig, device_type type, const char *tag);

	required_device<ppc_device> m_maincpu;
	required_device<mpc106_host_device> m_mpc106;
	required_device<cuda_device> m_cuda;
	required_device<macadb_device> m_macadb;
	required_device<dimm_spd_device> m_dimm0, m_dimm1;
	required_device<i2c_24c01_device> m_edid;
	required_device<ram_device> m_ram;

private:
	u16 m_sense;

	void imac_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void cuda_reset_w(int state)
	{
		m_maincpu->set_input_line(INPUT_LINE_HALT, state);
		m_maincpu->set_input_line(INPUT_LINE_RESET, state);
	}

	void irq_w(int state)
	{
		m_maincpu->set_input_line(PPC_IRQ, state);
	}

	u16 read_sense();
	void write_sense(u16 data);
};

imac_state::imac_state(const machine_config &mconfig, device_type type, const char *tag) :
	driver_device(mconfig, type, tag),
	m_maincpu(*this, "maincpu"),
	m_mpc106(*this, "pci:00.0"),
	m_cuda(*this, "cuda"),
	m_macadb(*this, "macadb"),
	m_dimm0(*this, "dimm0"),
	m_dimm1(*this, "dimm1"),
	m_edid(*this, "edid"),
	m_ram(*this, RAM_TAG)
{
}

void imac_state::machine_start()
{
	m_sense = 0;

	m_edid->set_address(0x50<<1);

	m_mpc106->set_ram_info((u8 *)m_ram->pointer(), m_ram->size());

	// start off disabling all of the DIMMs
	m_dimm0->set_dimm_size(dimm_spd_device::SIZE_SLOT_EMPTY);
	m_dimm1->set_dimm_size(dimm_spd_device::SIZE_SLOT_EMPTY);

// TODO: iMac queries DIMM0 twice, so we have to
// halve the DIMM size here to get the requested amount.
// Cuda 3.0 may fix this, since that's what it's expecting to talk to
	switch (m_ram->size())
	{
		case 32 * 1024 * 1024:
			m_dimm0->set_dimm_size(dimm_spd_device::SIZE_16_MIB);
			break;

		case 64 * 1024 * 1024:
			m_dimm0->set_dimm_size(dimm_spd_device::SIZE_32_MIB);
			break;

		case 128 * 1024 * 1024:
			m_dimm0->set_dimm_size(dimm_spd_device::SIZE_64_MIB);
			break;
	}

	save_item(NAME(m_sense));
}

void imac_state::machine_reset()
{
	// the PPC can't be allowed to run until Cuda's ready
	m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
}

void imac_state::imac_map(address_map &map)
{
	map.unmap_value_high();
}

// EDID sense.  Rage sends a u32 where the top 16 bits are DDR, the bottom 16 bits are data.
// The return value only needs the data (the lower 16 bits) filled in.
u16 imac_state::read_sense()
{
	const u8 out = (m_sense >> 12) & 0xff;
	const u8 scl = ((out & 2) >> 1);
	const u8 sda = (out & 1);

	m_edid->write_sda(sda);
	m_edid->write_scl(scl);

	return (m_edid->read_sda() ? 0x1000 : 0) | (scl ? 0x2000 : 0);
}

void imac_state::write_sense(u16 data)
{
	m_sense = data;
}

void imac_state::imac(machine_config &config)
{
	PPC750(config, m_maincpu, 66000000);    // actually 233 MHz
	m_maincpu->ppcdrc_set_options(PPCDRC_COMPATIBLE_OPTIONS);
	m_maincpu->set_addrmap(AS_PROGRAM, &imac_state::imac_map);

	PCI_ROOT(config, "pci", 0);
	MPC106(config, m_mpc106, 0, mpc106_host_device::MAP_TYPE_B, "maincpu", "bootrom");

	paddington_device &paddington(PADDINGTON(config, "pci:10.0", 0));
	paddington.set_maincpu_tag("maincpu");
	paddington.irq_callback().set(FUNC(imac_state::irq_w));

	atirage_device &ati(ATI_RAGEIIC(config, "pci:12.0", 14.318181_MHz_XTAL));
	ati.gpio_get_cb().set(FUNC(imac_state::read_sense));
	ati.gpio_set_cb().set(FUNC(imac_state::write_sense));
	ati.set_gpio_pullups(0x3000);   // bits 8 & 9 are the I2C bus

	I2C_24C01(config, m_edid);

	OPTI_82C861(config, "pci:14.0", 0);

	MACADB(config, m_macadb, 15.6672_MHz_XTAL);

	CUDA_V2XX(config, m_cuda, XTAL(32'768));
	m_cuda->set_default_bios_tag("341s0060");
	m_cuda->reset_callback().set(FUNC(imac_state::cuda_reset_w));
	m_cuda->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
	m_cuda->via_clock_callback().set(paddington, FUNC(heathrow_device::cb1_w));
	m_cuda->via_data_callback().set(paddington, FUNC(heathrow_device::cb2_w));
	m_macadb->adb_data_callback().set(m_cuda, FUNC(cuda_device::set_adb_line));
	config.set_perfect_quantum(m_maincpu);

	paddington.pb3_callback().set(m_cuda, FUNC(cuda_device::get_treq));
	paddington.pb4_callback().set(m_cuda, FUNC(cuda_device::set_byteack));
	paddington.pb5_callback().set(m_cuda, FUNC(cuda_device::set_tip));
	paddington.cb2_callback().set(m_cuda, FUNC(cuda_device::set_via_data));

	// ALL_HIGH logically ANDs all sources, which is what we want for I2C/SMBus
	input_merger_device &sda_merger(INPUT_MERGER_ALL_HIGH(config, "sda"));
	sda_merger.output_handler().append(m_cuda, FUNC(cuda_device::set_iic_sda));

	m_cuda->iic_sda_callback().set(sda_merger, FUNC(input_merger_device::in_w<0>));
	m_cuda->iic_sda_callback().append(m_dimm0, FUNC(dimm_spd_device::sda_write));
	m_cuda->iic_sda_callback().append(m_dimm1, FUNC(dimm_spd_device::sda_write));

	DIMM_SPD(config, m_dimm0).set_address(0x50);
	m_cuda->iic_scl_callback().set(m_dimm0, FUNC(dimm_spd_device::scl_write));
	m_dimm0->sda_callback().set(sda_merger, FUNC(input_merger_device::in_w<1>));

	DIMM_SPD(config, m_dimm1).set_address(0x51);
	m_cuda->iic_scl_callback().append(m_dimm1, FUNC(dimm_spd_device::scl_write));
	m_dimm1->sda_callback().set(sda_merger, FUNC(input_merger_device::in_w<2>));

	RAM(config, m_ram);
	m_ram->set_default_size("32M");
	m_ram->set_extra_options("32M,64M,128M");

	burgundy_device &burgundy(BURGUNDY(config, "codec", 45.1584_MHz_XTAL / 2));
	burgundy.dma_output().set(paddington, FUNC(heathrow_device::codec_dma_read));

	paddington.codec_r_callback().set(burgundy, FUNC(burgundy_device::read_macrisc));
	paddington.codec_w_callback().set(burgundy, FUNC(burgundy_device::write_macrisc));

	SPEAKER(config, "speaker", 2).front();
	burgundy.add_route(0, "speaker", 1.0, 0);
	burgundy.add_route(1, "speaker", 1.0, 1);
}

ROM_START(imac)
	ROM_REGION(0x100000, "bootrom", ROMREGION_64BIT | ROMREGION_BE)
	ROM_LOAD( "imacboot.u3",  0x000000, 0x100000, CRC(80d3174b) SHA1(e7a0c71822ec1e08435099af87b38bc82d315ed5) )

	ROM_REGION(0x80, "edid", 0)
	ROM_LOAD( "imac_16843009.edid", 0x000000, 0x000080, CRC(ea3051d1) SHA1(a0b0338cdd8f364135e1496b9a33f7509f0bb104) )
ROM_END

static INPUT_PORTS_START(imac)
INPUT_PORTS_END

COMP(1998, imac, 0, 0, imac, imac, imac_state, empty_init, "Apple Computer", "iMac (Bondi blue)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



imds2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:F. Ulivi
//
// ***************************************
// Driver for Intel Intellec MDS series-II
// ***************************************
//
// Documentation used for this driver:
// [1]  Intel, manual 9800554-04 rev. D - Intellec series II MDS - Schematic drawings
// [2]  Intel, manual 9800556-02 rev. B - Intellec series II MDS - Hardware reference manual
// [3]  Intel, manual 9800555-02 rev. B - Intellec series II MDS - Hardware interface manual
// [4]  Intel, manual 9800605-02 rev. B - Intellec series II MDS - Boot/monitor listing
//
// All these manuals are available on http://www.bitsavers.org
//
// An Intellec MDS series-II is composed of the following boards:
//
// **********
// Integrated Processor Card (IPC) or Integrated Processor Board (IPB)
//
// This is the board where the OS (ISIS-II) and the application software run.
// This driver emulates an IPC.
// IPC is composed as follows:
// A83 8085A-2  CPU @ 4 MHz
//              64k of DRAM
// A82 2732     EPROM with monitor & boot firmware (assembly source of this ROM is in [4])
// A66 8259A    System PIC
// A89 8259A    Local PIC
// A86 8253     PIT
// A91 8251A    Serial channel #0
// A90 8251A    Serial channel #1
//
// **********
// I/O Controller (IOC)
//
// This board acts as a controller for all I/O of the system.
// It is structured as if it were 2 boards in one:
// One part (around 8080) controls Keyboard, CRT & floppy, the other part (around PIO 8041A) controls all parallel I/Os
// (Line printer, Paper tape puncher, Paper tape reader, I/O to PROM programmer).
// Both parts are interfaced to IPC through a bidirectional 8-bit bus.
// IOC is composed of these parts:
// A69 8080A-2  CPU @ 2.448 MHz
//              8k of DRAM
// A50 2716
// A51 2716
// A52 2716
// A53 2716     EPROMs with IOC firmware
// A58 8257     DMA controller
// A35 8253     PIT
// A1  8271     Floppy controller
// A20 8275     CRT controller
// A19 2708     Character generator ROM
// LS1          3.3 kHz beeper
// A72 8041A    CPU @ 6 MHz (PIO: parallel I/O)
//
// **********
// Keyboard controller
//
// Keyboard is controlled by a 8741 CPU that scans the key matrix and sends key codes to IOC through a 8-bit bus.
//
// A3 8741      CPU @ 3.58 MHz
//
// Huge thanks to Dave Mabry for dumping IOC firmware, KB firmware and character generator. This driver would not
// exist without his dumps.
// (https://web.archive.org/web/20080509062332/http://www.s100-manuals.com/intel/IOC_iMDX_511_Upgrade.zip)
//
// Basic usage / test info:
// To use the system set DIP switches to:
// Floppy present
// IOC mode Diagnostic
// Keyboard present
// and reset the system. The built-in diagnostic mode should start.
//
// Another test is loading ISIS-II (the Intel OS for this system). Floppy image
// 9500007-07_ISIS-II_OPERATING_SYSTEM_DISKETTE_Ver_4.3.IMD
// To load it, the "IOC mode" should be set to "On line", the floppy image
// should be mounted and the system reset. After a few seconds the ISIS-II
// prompt should appear. A command that could be tried is "DIR" that lists
// the content of floppy disk.

#include "emu.h"
#include "imds2ioc.h"

#include "cpu/i8085/i8085.h"
#include "machine/74259.h"
#include "machine/i8251.h"
#include "machine/pit8253.h"
#include "machine/pic8259.h"
#include "bus/rs232/rs232.h"
#include "bus/multibus/multibus.h"
#include "bus/multibus/isbc202.h"


namespace {

// CPU oscillator of IPC board: 8 MHz
#define IPC_XTAL_Y2     8_MHz_XTAL

class imds2_state : public driver_device
{
public:
	imds2_state(const machine_config &mconfig, device_type type, const char *tag);

	void imds2(machine_config &config);

	void xack(int state);

private:
	uint8_t ipc_mem_read(offs_t offset);
	void ipc_mem_write(offs_t offset, uint8_t data);
	void ipc_control_w(uint8_t data);
	void ipc_intr_w(int state);
	uint8_t ipcsyspic_r(offs_t offset);
	uint8_t ipclocpic_r(offs_t offset);
	void ipcsyspic_w(offs_t offset, uint8_t data);
	void ipclocpic_w(offs_t offset, uint8_t data);

	virtual void driver_start() override;
	virtual void driver_reset() override;

	void ipc_io_map(address_map &map) ATTR_COLD;
	void ipc_mem_map(address_map &map) ATTR_COLD;

	u8 bus_pio_r(offs_t offset) { return m_bus->space(AS_IO).read_byte(offset); }
	void bus_pio_w(offs_t offset, u8 data) { m_bus->space(AS_IO).write_byte(offset, data); }

	required_device<i8085a_cpu_device> m_ipccpu;
	required_device<pic8259_device> m_ipcsyspic;
	required_device<pic8259_device> m_ipclocpic;
	required_device<pit8253_device> m_ipctimer;
	required_device_array<i8251_device, 2> m_ipcusart;
	required_device<ls259_device> m_ipcctrl;
	required_device_array<rs232_port_device, 2> m_serial;
	required_device<imds2ioc_device> m_ioc;
	required_device<multibus_device> m_bus;
	required_device<multibus_slot_device> m_slot;

	required_shared_ptr<u8> m_ram;
	memory_view m_boot;
};

void imds2_state::ipc_mem_map(address_map &map)
{
	map(0x0000, 0xffff).ram().share("ram");

	// selectively map the boot/diagnostic segment
	map(0x0000, 0xefff).view(m_boot);

	// SEL_BOOT/ == 0 and START_UP/ == 0
	m_boot[0](0x0000, 0x07ff).rom().region("ipcrom", 0);
	m_boot[0](0xe800, 0xefff).rom().region("ipcrom", 0);

	// SEL_BOOT/ == 0 and START_UP/ == 1
	m_boot[1](0xe800, 0xefff).rom().region("ipcrom", 0);

	// monitor segment
	map(0xf800, 0xffff).rom().region("ipcrom", 0x800);
}

void imds2_state::ipc_io_map(address_map &map)
{
	map.unmap_value_low();

	// map PIO to Multibus by default
	map(0x00, 0xff).rw(FUNC(imds2_state::bus_pio_r), FUNC(imds2_state::bus_pio_w));

	// local bus PIO
	map(0xc0, 0xc1).rw(m_ioc, FUNC(imds2ioc_device::dbb_master_r), FUNC(imds2ioc_device::dbb_master_w));
	map(0xf0, 0xf3).rw(m_ipctimer, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xf4, 0xf5).rw(m_ipcusart[0], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xf6, 0xf7).rw(m_ipcusart[1], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xf8, 0xf9).rw(m_ioc, FUNC(imds2ioc_device::pio_master_r), FUNC(imds2ioc_device::pio_master_w));
	map(0xfa, 0xfb).rw(FUNC(imds2_state::ipclocpic_r), FUNC(imds2_state::ipclocpic_w));
	map(0xfc, 0xfd).rw(FUNC(imds2_state::ipcsyspic_r), FUNC(imds2_state::ipcsyspic_w));
	map(0xff, 0xff).w(FUNC(imds2_state::ipc_control_w));
}

imds2_state::imds2_state(const machine_config &mconfig, device_type type, const char *tag) :
	driver_device(mconfig, type, tag),
	m_ipccpu(*this, "ipccpu"),
	m_ipcsyspic(*this, "ipcsyspic"),
	m_ipclocpic(*this, "ipclocpic"),
	m_ipctimer(*this, "ipctimer"),
	m_ipcusart(*this, "ipcusart%u", 0U),
	m_ipcctrl(*this, "ipcctrl"),
	m_serial(*this, "serial%u", 0U),
	m_ioc(*this, "ioc"),
	m_bus(*this, "bus"),
	m_slot(*this, "slot1"),
	m_ram(*this, "ram"),
	m_boot(*this, "boot")
{
}

void imds2_state::ipc_control_w(uint8_t data)
{
	// See A84, pg 28 of [1]
	// b3 is ~(bit to be written)
	// b2-b0 is ~(no. of bit to be written)
	m_ipcctrl->write_bit(~data & 7, BIT(~data, 3));

	// SEL_BOOT/ == 0
	if (!m_ipcctrl->q3_r())
		// START_UP/
		m_boot.select(m_ipcctrl->q5_r());
	else
		m_boot.disable();
}

void imds2_state::ipc_intr_w(int state)
{
	m_ipccpu->set_input_line(I8085_INTR_LINE, (state != 0) && m_ipcctrl->q2_r());
}

uint8_t imds2_state::ipcsyspic_r(offs_t offset)
{
	return m_ipcsyspic->read(!BIT(offset, 0));
}

uint8_t imds2_state::ipclocpic_r(offs_t offset)
{
	return m_ipclocpic->read(!BIT(offset, 0));
}

void imds2_state::ipcsyspic_w(offs_t offset, uint8_t data)
{
	m_ipcsyspic->write(!BIT(offset, 0), data);
}

void imds2_state::ipclocpic_w(offs_t offset, uint8_t data)
{
	m_ipclocpic->write(!BIT(offset, 0), data);
}

void imds2_state::driver_start()
{
	// share local RAM on Multibus
	m_bus->space(AS_PROGRAM).install_ram(0x0000, 0xffff, m_ram.target());
}

void imds2_state::driver_reset()
{
	m_boot.select(0);
}

static INPUT_PORTS_START(imds2)
INPUT_PORTS_END

static void imds2_cards(device_slot_interface &device)
{
	device.option_add("isbc202", ISBC202);
}

void imds2_state::imds2(machine_config &config)
{
	// Y1 oscillator of IPC board: 19.6608 MHz
	constexpr auto IPC_XTAL_Y1 = 19.6608_MHz_XTAL;

	I8085A(config, m_ipccpu, IPC_XTAL_Y2);  // CLK OUT = 4 MHz
	m_ipccpu->set_addrmap(AS_PROGRAM, &imds2_state::ipc_mem_map);
	m_ipccpu->set_addrmap(AS_IO, &imds2_state::ipc_io_map);
	m_ipccpu->in_inta_func().set("ipcsyspic", FUNC(pic8259_device::acknowledge));
	//config.set_maximum_quantum(attotime::from_hz(100));

	PIC8259(config, m_ipcsyspic, 0);
	m_ipcsyspic->out_int_callback().set(FUNC(imds2_state::ipc_intr_w));
	m_ipcsyspic->in_sp_callback().set_constant(1);

	PIC8259(config, m_ipclocpic, 0);
	m_ipclocpic->out_int_callback().set(m_ipcsyspic, FUNC(pic8259_device::ir7_w));
	m_ipclocpic->in_sp_callback().set_constant(0);

	PIT8253(config, m_ipctimer, 0);
	m_ipctimer->set_clk<0>(IPC_XTAL_Y1 / 16);
	m_ipctimer->set_clk<1>(IPC_XTAL_Y1 / 16);
	m_ipctimer->set_clk<2>(IPC_XTAL_Y1 / 16);
	m_ipctimer->out_handler<0>().set(m_ipcusart[0], FUNC(i8251_device::write_txc));
	m_ipctimer->out_handler<0>().append(m_ipcusart[0], FUNC(i8251_device::write_rxc));
	m_ipctimer->out_handler<1>().set(m_ipcusart[1], FUNC(i8251_device::write_txc));
	m_ipctimer->out_handler<1>().append(m_ipcusart[1], FUNC(i8251_device::write_rxc));
	m_ipctimer->out_handler<2>().set(m_ipclocpic, FUNC(pic8259_device::ir4_w));

	I8251(config, m_ipcusart[0], IPC_XTAL_Y1 / 9);
	m_ipcusart[0]->rts_handler().set(m_ipcusart[0], FUNC(i8251_device::write_cts));
	m_ipcusart[0]->rxrdy_handler().set(m_ipclocpic, FUNC(pic8259_device::ir0_w));
	m_ipcusart[0]->txrdy_handler().set(m_ipclocpic, FUNC(pic8259_device::ir1_w));
	m_ipcusart[0]->txd_handler().set(m_serial[0], FUNC(rs232_port_device::write_txd));

	I8251(config, m_ipcusart[1], IPC_XTAL_Y1 / 9);
	m_ipcusart[1]->rxrdy_handler().set(m_ipclocpic, FUNC(pic8259_device::ir2_w));
	m_ipcusart[1]->txrdy_handler().set(m_ipclocpic, FUNC(pic8259_device::ir3_w));
	m_ipcusart[1]->txd_handler().set(m_serial[1], FUNC(rs232_port_device::write_txd));
	m_ipcusart[1]->rts_handler().set(m_serial[1], FUNC(rs232_port_device::write_rts));
	m_ipcusart[1]->dtr_handler().set(m_serial[1], FUNC(rs232_port_device::write_dtr));

	LS259(config, m_ipcctrl); // A84

	RS232_PORT(config, m_serial[0], default_rs232_devices, nullptr);
	m_serial[0]->rxd_handler().set(m_ipcusart[0], FUNC(i8251_device::write_rxd));
	m_serial[0]->dsr_handler().set(m_ipcusart[0], FUNC(i8251_device::write_dsr));

	RS232_PORT(config, m_serial[1], default_rs232_devices, nullptr);
	m_serial[1]->rxd_handler().set(m_ipcusart[1], FUNC(i8251_device::write_rxd));
	m_serial[1]->cts_handler().set(m_ipcusart[1], FUNC(i8251_device::write_cts));
	m_serial[1]->dsr_handler().set(m_ipcusart[1], FUNC(i8251_device::write_dsr));

	IMDS2IOC(config, m_ioc);
	m_ioc->master_intr_cb().set(m_ipclocpic, FUNC(pic8259_device::ir6_w));
	m_ioc->parallel_int_cb().set(m_ipclocpic, FUNC(pic8259_device::ir5_w));

	MULTIBUS(config, m_bus, IPC_XTAL_Y1 / 2);
	m_bus->xack_cb().set(FUNC(imds2_state::xack));
	MULTIBUS_SLOT(config, m_slot, m_bus, imds2_cards, nullptr, false, (IPC_XTAL_Y1 / 2).value()); // FIXME: isbc202
}

void imds2_state::xack(int state)
{
	if (state) {
		// Put CPU in wait state
		m_ipccpu->spin_until_trigger(1);
		// Rewind PC so that the "IN" & "OUT" instructions are repeated when CPU is released
		m_ipccpu->set_pc(m_ipccpu->pc() - 2);
	} else {
		// Release CPU from wait state
		m_ipccpu->trigger(1);
	}
}

ROM_START(imds2)
	// ROM definition of IPC cpu (8085A)
	ROM_REGION(0x1000, "ipcrom", 0)
	ROM_DEFAULT_BIOS("mon13")
	// 1x2732 Copyright 1979
	ROM_SYSTEM_BIOS(0, "mon13", "Series II Monitor v1.3")
	ROMX_LOAD("ipc13_a82.bin", 0x0000, 0x1000, CRC(0889394f) SHA1(b7525baf1884a7d67402dea4b5566016a9861ef2), ROM_BIOS(0))
	// 2x2716 Copyright 1978
	ROM_SYSTEM_BIOS(1, "mon12", "Series II Monitor v1.2")
	ROMX_LOAD("ipc12_a57.bin", 0x0000, 0x0800, CRC(6496efaf) SHA1(1a9c0f1b19c1807803db3f1543f51349d7fd693a), ROM_BIOS(1))
	ROMX_LOAD("ipc12_a48.bin", 0x0800, 0x0800, CRC(258dc9a6) SHA1(3fde993aee06d9af5093d7a2d9a8cbd71fed0951), ROM_BIOS(1))
	// 2x2716 Copyright 1977
	ROM_SYSTEM_BIOS(2, "mon11", "Series II Monitor v1.1")
	ROMX_LOAD("ipc11_a57.bin", 0x0000, 0x0800, CRC(ffb7c036) SHA1(6f60cdfe20621c4b633c972adcb644a1c02eaa39), ROM_BIOS(2))
	ROMX_LOAD("ipc11_a48.bin", 0x0800, 0x0800, CRC(3696ff28) SHA1(38b435e10a81629430275aec051fb0a55ec1f6fd), ROM_BIOS(2))
ROM_END

} // anonymous namespace


/*    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME */
COMP( 1979, imds2, 0,      0,      imds2,   imds2, imds2_state, empty_init, "Intel", "Intellec MDS-II", 0)



imsai.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

    Imsai MPU-B. One of the earliest single-board computers on a S100 card.

    2013-09-11 Skeleton driver.

    Chips used: i8085, i8251, i8253, 3622 fusable prom. XTAL 6MHz

    Press any key to start the monitor program.

    ToDo:
    - Banking
    - Dipswitches

****************************************************************************/

#include "emu.h"

//#include "bus/s100/s100.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8251.h"
#include "machine/pit8253.h"
#include "machine/terminal.h"


namespace {

class imsai_state : public driver_device
{
public:
	imsai_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_terminal(*this, "terminal")
		, m_pit(*this, "pit")
	{ }

	void imsai(machine_config &config);

private:
	void kbd_put(u8 data);
	u8 keyin_r();
	u8 status_r();
	void control_w(u8 data);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	u8 m_term_data = 0U;
	void machine_reset() override ATTR_COLD;
	void machine_start() override ATTR_COLD;
	memory_passthrough_handler m_rom_shadow_tap;
	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_device<generic_terminal_device> m_terminal;
	required_device<pit8253_device> m_pit;
};


void imsai_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0xd000, 0xd0ff).ram();
	map(0xd100, 0xd103).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xd800, 0xdfff).rom().region("maincpu", 0);
}

void imsai_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x02, 0x02).r(FUNC(imsai_state::keyin_r)).w(m_terminal, FUNC(generic_terminal_device::write));
	map(0x03, 0x03).r(FUNC(imsai_state::status_r));
	map(0x04, 0x05).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x12, 0x13).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x14, 0x14).r(FUNC(imsai_state::keyin_r)).w(m_terminal, FUNC(generic_terminal_device::write));
	map(0x15, 0x15).r(FUNC(imsai_state::status_r));
	map(0xf3, 0xf3).w(FUNC(imsai_state::control_w));
}

/* Input ports */
static INPUT_PORTS_START( imsai )
INPUT_PORTS_END

u8 imsai_state::keyin_r()
{
	u8 ret = m_term_data;
	m_term_data = 0;
	return ret;
}

u8 imsai_state::status_r()
{
	return (m_term_data) ? 3 : 1;
}

void imsai_state::kbd_put(u8 data)
{
	m_term_data = data;
}

void imsai_state::control_w(u8 data)
{
}

void imsai_state::machine_reset()
{
	m_term_data = 0;

	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x0000, 0x07ff, m_rom);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0xd800, 0xdfff,
			"rom_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// remove from the memory map
					m_maincpu->space(AS_PROGRAM).unmap_readwrite(0x0000, 0x07ff);
				}
			},
			&m_rom_shadow_tap);
}

void imsai_state::machine_start()
{
	save_item(NAME(m_term_data));
}

void imsai_state::imsai(machine_config &config)
{
	/* basic machine hardware */
	I8085A(config, m_maincpu, XTAL(6'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &imsai_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &imsai_state::io_map);

	/* video hardware */
	GENERIC_TERMINAL(config, m_terminal, 0);
	m_terminal->set_keyboard_callback(FUNC(imsai_state::kbd_put));

	/* Devices */
	I8251(config, "uart", 0);

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(6_MHz_XTAL / 3); // Timer 0: baud rate gen for 8251
	m_pit->out_handler<0>().set("uart", FUNC(i8251_device::write_txc));
	m_pit->out_handler<0>().append("uart", FUNC(i8251_device::write_rxc));
	m_pit->set_clk<1>(6_MHz_XTAL / 3); // Timer 1: user
	m_pit->set_clk<2>(6_MHz_XTAL / 3); // Timer 2: user
}

/* ROM definition */
ROM_START( imsai )
	ROM_REGION( 0x800, "maincpu", 0 ) // 2716 or 2708 program PROM
	ROM_LOAD( "vdb-80.rom",   0x0000, 0x0800, CRC(0afc4683) SHA1(a5419aaee00badf339d7c627f50ef8b2538e42e2) )

	ROM_REGION( 0x200, "decode", 0 ) // 512x4 address decoder ROM
	ROM_LOAD( "3622.u31", 0x000, 0x200, NO_DUMP )

	ROM_REGION( 0x20, "status", 0 ) // PROM for decoding 8085 status signals
	ROM_LOAD( "74s288.u38", 0x00, 0x20, NO_DUMP )
ROM_END

} // anonymous namespace

/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME  FLAGS
COMP( 1978, imsai, 0,      0,      imsai,   imsai, imsai_state, empty_init, "Imsai", "MPU-B",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



indiana.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, AJR
/***************************************************************************

        Indiana University 68030 board

        08/12/2009 Skeleton driver.
        01/20/2014 Added ISA bus and peripherals

****************************************************************************/

#include "emu.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "cpu/m68000/m68030.h"
#include "bus/isa/com.h"
#include "bus/isa/fdc.h"
#include "bus/isa/ide.h"
#include "bus/isa/isa.h"
#include "bus/isa/isa_cards.h"
#include "bus/isa/vga.h"
#include "machine/mc68901.h"
#include "sound/spkrdev.h"
#include "speaker.h"


namespace {

#define ISABUS_TAG "isa"

class indiana_state : public driver_device
{
public:
	indiana_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void indiana(machine_config &config);

	void init_indiana();

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	void indiana_mem(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};


void indiana_state::indiana_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000000, 0x0000ffff).mirror(0x7f800000).rom().region("user1", 0); // 64Kb of EPROM
	map(0x00100000, 0x00107fff).mirror(0x7f8f8000).ram(); // SRAM 32Kb of SRAM
	map(0x00200000, 0x002fffff).rw("mfp", FUNC(mc68901_device::read), FUNC(mc68901_device::write)).mirror(0x7f800000); // MFP
	map(0x00400000, 0x004fffff).rw(ISABUS_TAG, FUNC(isa16_device::io16_swap_r), FUNC(isa16_device::io16_swap_w)).mirror(0x7f800000); // 16 bit PC IO
	map(0x00500000, 0x005fffff).rw(ISABUS_TAG, FUNC(isa16_device::mem16_swap_r), FUNC(isa16_device::mem16_swap_w)).mirror(0x7f800000); // 16 bit PC MEM
	map(0x00600000, 0x006fffff).rw(ISABUS_TAG, FUNC(isa16_device::io_r), FUNC(isa16_device::io_w)).mirror(0x7f800000); // 8 bit PC IO
	map(0x00700000, 0x007fffff).rw(ISABUS_TAG, FUNC(isa16_device::mem_r), FUNC(isa16_device::mem_w)).mirror(0x7f800000); // 8 bit PC MEM
	map(0x80000000, 0x803fffff).ram(); // 4 MB RAM
	map(0xfffe0000, 0xfffe7fff).ram(); // SRAM mirror?
}


/* Input ports */
static INPUT_PORTS_START( indiana )
INPUT_PORTS_END


void indiana_state::machine_reset()
{
}

void indiana_state::init_indiana()
{
}

void indiana_isa_cards(device_slot_interface &device)
{
	// 8-bit
	device.option_add("fdc_at", ISA8_FDC_AT);
	device.option_add("comat", ISA8_COM_AT);
	device.option_add("vga", ISA8_VGA);

	// 16-bit
	device.option_add("ide", ISA16_IDE);
}

void indiana_state::indiana(machine_config &config)
{
	/* basic machine hardware */
	M68030(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &indiana_state::indiana_mem);

	config.set_maximum_quantum(attotime::from_msec(2)); // the MFP loses keyboard scancodes too often when longer quanta are enabled

	isa16_device &isa(ISA16(config, ISABUS_TAG, 16_MHz_XTAL / 2)); // OSC = CLK = CLK8
	isa.set_custom_spaces();
	isa.irq3_callback().set_inputline(m_maincpu, M68K_IRQ_5);
	isa.irq4_callback().set_inputline(m_maincpu, M68K_IRQ_4);
	isa.irq5_callback().set_inputline(m_maincpu, M68K_IRQ_3);
	isa.irq6_callback().set_inputline(m_maincpu, M68K_IRQ_2);
	isa.irq7_callback().set_inputline(m_maincpu, M68K_IRQ_1);
	isa.irq2_callback().set("mfp", FUNC(mc68901_device::i7_w)); // IRQ9
	isa.irq10_callback().set("mfp", FUNC(mc68901_device::i6_w));
	isa.irq11_callback().set("mfp", FUNC(mc68901_device::i5_w));
	isa.irq12_callback().set("mfp", FUNC(mc68901_device::i4_w));
	isa.irq14_callback().set("mfp", FUNC(mc68901_device::i3_w));
	isa.irq15_callback().set("mfp", FUNC(mc68901_device::i2_w));

	ISA16_SLOT(config, "isa1", 0, ISABUS_TAG, indiana_isa_cards, "vga", false);
	ISA16_SLOT(config, "isa2", 0, ISABUS_TAG, indiana_isa_cards, "fdc_at", false);
	ISA16_SLOT(config, "isa3", 0, ISABUS_TAG, indiana_isa_cards, "comat", false);
	ISA16_SLOT(config, "isa4", 0, ISABUS_TAG, indiana_isa_cards, "ide", false);

	pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_at_keyboards, STR_KBD_IBM_PC_AT_84));
	kbd.out_data_cb().set("mfp", FUNC(mc68901_device::i0_w));
	kbd.out_data_cb().append("mfp", FUNC(mc68901_device::si_w));
	kbd.out_clock_cb().set("mfp", FUNC(mc68901_device::i1_w));
	kbd.out_clock_cb().append("mfp", FUNC(mc68901_device::rc_w));

	mc68901_device &mfp(MC68901(config, "mfp", 16_MHz_XTAL / 4));
	mfp.set_timer_clock(16_MHz_XTAL / 16);
	mfp.out_irq_cb().set_inputline(m_maincpu, M68K_IRQ_6);
	mfp.out_tdo_cb().set("speaker", FUNC(speaker_sound_device::level_w));

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);
}

/* ROM definition */
ROM_START( indiana )
	ROM_REGION32_BE( 0x10000, "user1", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v9", "ver 0.9" )
	ROMX_LOAD( "prom0_9.bin", 0x0000, 0x10000, CRC(746ad75e) SHA1(7d5c123c8568b1e02ab683e8f3188d0fef78d740), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v8", "ver 0.8" )
	ROMX_LOAD( "prom0_8.bin", 0x0000, 0x10000, CRC(9d8dafee) SHA1(c824e5fe6eec08f51ef287c651a5034fe3c8b718), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "v7", "ver 0.7" )
	ROMX_LOAD( "prom0_7.bin", 0x0000, 0x10000, CRC(d6a3b6bc) SHA1(01d8cee989ab29646d9d3f8b7262b10055653d41), ROM_BIOS(2))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT          COMPANY               FULLNAME                          FLAGS
COMP( 1993, indiana, 0,      0,      indiana, indiana, indiana_state, init_indiana, "Indiana University", "Indiana University 68030 board", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



indy_indigo2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/*********************************************************************\
*
*   SGI IP22/IP24 Indigo2/Indy workstation
*
*  Known Issues:
*  - The MAC address is supplied by the NVRAM, requiring the user to
*    use "setenv -f eaddr 08:00:69:xx:yy:zz" from the Indy boot PROM
*    before any IRIX installers will proceed.
*  - The Gentoo Linux live CD hangs on starting the kernel.
*
*  Memory map:
*
*  00000000 - 0007ffff      Alias for first 512kbyte of RAM
*  00080000 - 0008ffff      EISA I/O space (VINO on Indy)
*  00090000 - 0009ffff      EISA I/O space Alias (pullups on Indy)
*  000a0000 - 07ffffff      EISA Memory
*  08000000 - 17ffffff      Low System Memory
*  18000000 - 1effffff      RESERVED - Unused
*  1f000000 - 1f3fffff      GIO64 - GFX
*  1f400000 - 1f5fffff      GIO64 - EXP0
*  1f600000 - 1f9fffff      GIO64 - EXP1 - Unused
*  1fa00000 - 1fa1ffff      Memory Controller
*  1fb00000 - 1fb7ffff      HPC3 CHIP1
*  1fb80000 - 1fbfffff      HPC3 CHIP0
*  1fc00000 - 1fffffff      BIOS
*  20000000 - 2fffffff      High System Memory
*  30000000 - 7fffffff      Reserved
*  80000000 - ffffffff      EISA Memory
*
*  IP22/IP24 has 2 pieces of PC-compatible hardware: the 8042 PS/2 keyboard/mouse
*  interface and the 8254 PIT.  Both are licensed cores embedded in the IOC custom chip.
*
*  References used:
*    MipsLinux: http://www.mips-linux.org/
*      linux-2.6.6/include/newport.h
*      linux-2.6.6/include/asm-mips/sgi/gio.h
*      linux-2.6.6/include/asm-mips/sgi/mc.h
*      linux-2.6.6/include/asm-mips/sgi/hpc3.h
*    NetBSD: http://www.netbsd.org/
*    gxemul: http://gavare.se/gxemul/
*
*  Gentoo LiveCD r5 boot instructions:
*  - Specify an appropriate LiveCD image at the command line.
*  - Enter the command interpreter and type "sashARCS". Press enter and
*    it will autoboot.
*
*  IRIX boot instructions:
*  - Specify an appropriate IRIX CD image at the command line.
*  - At the menu, choose either "run diagnostics" or "install
*    system software".
*
\*********************************************************************/

#include "emu.h"

#include "hpc3.h"
#include "ioc2.h"
#include "mc.h"
#include "vino.h"

#include "bus/gio64/gio64.h"
#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"
#include "cpu/mips/mips3.h"
#include "machine/ds1386.h"
#include "machine/edlc.h"
#include "machine/eepromser.h"
#include "machine/nscsi_bus.h"
#include "machine/saa7191.h"
#include "machine/wd33c9x.h"
#include "sound/cdda.h"

#include "emupal.h"
#include "screen.h"
#include "softlist.h"

#include "logmacro.h"


namespace {

class ip24_state : public driver_device
{
public:
	ip24_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mem_ctrl(*this, "memctrl")
		, m_scsi_ctrl(*this, "scsibus:0:wd33c93")
		, m_edlc(*this, "edlc")
		, m_eeprom(*this, "eeprom")
		, m_hal2(*this, "hal2")
		, m_hpc3(*this, "hpc3")
		, m_ioc2(*this, "ioc2")
		, m_rtc(*this, "rtc")
		, m_vino(*this, "vino")
		, m_dmsd(*this, "dmsd")
		, m_gio64(*this, "gio64")
		, m_gio64_gfx(*this, "gio64_gfx")
		, m_gio64_exp0(*this, "gio64_exp0")
		, m_gio64_exp1(*this, "gio64_exp1")
	{
	}

	void ip24_base(machine_config &config, uint32_t system_clock);
	void ip24(machine_config &config, uint32_t system_clock);
	void indy_5015(machine_config &config);
	void indy_4613(machine_config &config);
	void indy_4610(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	template <uint32_t addr_base> uint64_t bus_error_r(offs_t offset, uint64_t mem_mask = ~0);
	template <uint32_t addr_base> void bus_error_w(offs_t offset, uint64_t data, uint64_t mem_mask = ~0);

	uint8_t volume_r(offs_t offset);
	void volume_w(offs_t offset, uint8_t data);

	void ip24_map(address_map &map) ATTR_COLD;
	void ip24_base_map(address_map &map) ATTR_COLD;
	void pio0_map(address_map &map) ATTR_COLD;
	void pio1_map(address_map &map) ATTR_COLD;
	void pio2_map(address_map &map) ATTR_COLD;
	void pio3_map(address_map &map) ATTR_COLD;
	void pio5_map(address_map &map) ATTR_COLD;
	void pio6_map(address_map &map) ATTR_COLD;

	void wd33c93(device_t *device);

	static void scsi_devices(device_slot_interface &device);

	required_device<mips3_device> m_maincpu;
	required_device<sgi_mc_device> m_mem_ctrl;
	required_device<wd33c93b_device> m_scsi_ctrl;
	required_device<seeq80c03_device> m_edlc;
	required_device<eeprom_serial_93cxx_device> m_eeprom;
	required_device<hal2_device> m_hal2;
	required_device<hpc3_device> m_hpc3;
	required_device<ioc2_device> m_ioc2;
	required_device<ds1386_device> m_rtc;
	optional_device<vino_device> m_vino;
	optional_device<saa7191_device> m_dmsd;
	optional_device<gio64_device> m_gio64;
	optional_device<gio64_slot_device> m_gio64_gfx;
	optional_device<gio64_slot_device> m_gio64_exp0;
	optional_device<gio64_slot_device> m_gio64_exp1;

	uint8_t m_volume_l = 0;
	uint8_t m_volume_r = 0;
};

class ip22_state : public ip24_state
{
public:
	ip22_state(const machine_config &mconfig, device_type type, const char *tag)
		: ip24_state(mconfig, type, tag)
		, m_scsi_ctrl2(*this, "scsibus2:0:wd33c93")
	{
	}

	void indigo2_4415(machine_config &config);

private:
	uint32_t eisa_io_r();

	void wd33c93_2(device_t *device);

	void ip22_map(address_map &map) ATTR_COLD;
	void pio4_map(address_map &map) ATTR_COLD;
	void pio6_map(address_map &map) ATTR_COLD;

	required_device<wd33c93b_device> m_scsi_ctrl2;
};

template <uint32_t addr_base>
uint64_t ip24_state::bus_error_r(offs_t offset, uint64_t mem_mask)
{
	logerror("Bus error (read)\n");
	// FIXME: m_maincpu->bus_error();
	m_mem_ctrl->set_cpu_buserr(addr_base + (offset << 3), mem_mask);
	return 0;
}

template <uint32_t addr_base>
void ip24_state::bus_error_w(offs_t offset, uint64_t data, uint64_t mem_mask)
{
	logerror("Bus error (write)\n");
	// FIXME: m_maincpu->bus_error();
	m_mem_ctrl->set_cpu_buserr(addr_base + (offset << 3), mem_mask);
}

uint32_t ip22_state::eisa_io_r()
{
	return 0xffffffff;
}

uint8_t ip24_state::volume_r(offs_t offset)
{
	if (offset == 0)
		return m_volume_r;
	else
		return m_volume_l;
}

void ip24_state::volume_w(offs_t offset, uint8_t data)
{
	if (offset == 0)
	{
		m_volume_r = data;
		m_hal2->set_right_volume(data);
	}
	else
	{
		m_volume_l = data;
		m_hal2->set_left_volume(data);
	}
}

void ip24_state::ip24_base_map(address_map &map)
{
	map(0x1f000000, 0x1f9fffff).rw(m_gio64, FUNC(gio64_device::read), FUNC(gio64_device::write));
	map(0x1fa00000, 0x1fa1ffff).rw(m_mem_ctrl, FUNC(sgi_mc_device::read), FUNC(sgi_mc_device::write));
	map(0x1fb00000, 0x1fb7ffff).rw(FUNC(ip24_state::bus_error_r<0x1fb00000>), FUNC(ip24_state::bus_error_w<0x1fb00000>));
	map(0x1fb80000, 0x1fbfffff).m(m_hpc3, FUNC(hpc3_device::map));
	map(0x1fc00000, 0x1fc7ffff).rom().region("user1", 0);
}

void ip24_state::ip24_map(address_map &map)
{
	ip24_base_map(map);
	map(0x00080000, 0x0009ffff).rw(m_vino, FUNC(vino_device::read), FUNC(vino_device::write));
}

void ip24_state::pio0_map(address_map &map)
{
	map(0x00, 0xff).rw(m_hal2, FUNC(hal2_device::read), FUNC(hal2_device::write));
}

void ip24_state::pio1_map(address_map &map)
{
	map(0x00, 0xff).ram(); // hack
}

void ip24_state::pio2_map(address_map &map)
{
	map(0x00, 0x01).rw(FUNC(ip24_state::volume_r), FUNC(ip24_state::volume_w)).umask16(0x00ff);
}

void ip24_state::pio6_map(address_map &map)
{
	map(0x00, 0x2f).m("ioc2", FUNC(ioc2_guinness_device::map)).umask16(0x00ff);
}

void ip22_state::ip22_map(address_map &map)
{
	ip22_state::ip24_base_map(map);
	map(0x00080000, 0x0009ffff).r(FUNC(ip22_state::eisa_io_r));
}

void ip22_state::pio4_map(address_map &map)
{
	map(0x00, 0x0f).m("ioc2", FUNC(ioc2_full_house_device::int2_map)).umask16(0x00ff);
}

void ip22_state::pio6_map(address_map &map)
{
	map(0x00, 0x1f).m("ioc2", FUNC(ioc2_full_house_device::map)).umask16(0x00ff);
}

void ip24_state::machine_start()
{
	save_item(NAME(m_volume_l));
	save_item(NAME(m_volume_r));
}

void ip24_state::machine_reset()
{
	//m_maincpu->mips3drc_set_options(MIPS3DRC_COMPATIBLE_OPTIONS | MIPS3DRC_CHECK_OVERFLOWS);
}

static INPUT_PORTS_START( ip24 )
INPUT_PORTS_END

void ip24_state::wd33c93(device_t *device)
{
	device->set_clock(10000000);
	downcast<wd33c93b_device *>(device)->irq_cb().set(m_ioc2, FUNC(ioc2_device::scsi0_int_w));
	downcast<wd33c93b_device *>(device)->drq_cb().set(m_hpc3, FUNC(hpc3_device::scsi0_drq));
}

void ip24_state::scsi_devices(device_slot_interface &device)
{
	device.option_add("cdrom", NSCSI_CDROM_SGI);
	device.option_add("harddisk", NSCSI_HARDDISK);
	//device.set_option_machine_config("cdrom", cdrom_config);
}

static DEVICE_INPUT_DEFAULTS_START(ip22_mc)
	DEVICE_INPUT_DEFAULTS("VALID", 0x0f, 0x07)
DEVICE_INPUT_DEFAULTS_END

static DEVICE_INPUT_DEFAULTS_START(ip24_mc)
	DEVICE_INPUT_DEFAULTS("VALID", 0x0f, 0x03)
DEVICE_INPUT_DEFAULTS_END

void ip24_state::ip24_base(machine_config &config, uint32_t system_clock)
{
	SGI_MC(config, m_mem_ctrl, m_maincpu, m_eeprom, system_clock);
	m_mem_ctrl->int_dma_done_cb().set(m_ioc2, FUNC(ioc2_device::mc_dma_done_w));
	m_mem_ctrl->eisa_present().set_constant(1);

	NSCSI_BUS(config, "scsibus", 0);
	NSCSI_CONNECTOR(config, "scsibus:0").option_set("wd33c93", WD33C93B)
		.machine_config([this](device_t *device) { wd33c93(device); });
	NSCSI_CONNECTOR(config, "scsibus:1", scsi_devices, "harddisk", false);
	NSCSI_CONNECTOR(config, "scsibus:2", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsibus:3", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsibus:4", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsibus:5", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsibus:6", scsi_devices, "cdrom", false);
	NSCSI_CONNECTOR(config, "scsibus:7", scsi_devices, nullptr, false);

	// GIO64
	GIO64(config, m_gio64, m_maincpu);
	m_gio64->interrupt_cb<0>().set(m_ioc2, FUNC(ioc2_device::gio_int0_w));
	m_gio64->interrupt_cb<1>().set(m_ioc2, FUNC(ioc2_device::gio_int1_w));
	m_gio64->interrupt_cb<2>().set(m_ioc2, FUNC(ioc2_device::gio_int2_w));
	GIO64_SLOT(config, m_gio64_gfx, m_gio64, gio64_slot_device::GIO64_SLOT_GFX, gio64_cards, "xl24");
	GIO64_SLOT(config, m_gio64_exp0, m_gio64, gio64_slot_device::GIO64_SLOT_EXP0, gio64_cards, nullptr);
	GIO64_SLOT(config, m_gio64_exp1, m_gio64, gio64_slot_device::GIO64_SLOT_EXP1, gio64_cards, nullptr);

	SGI_HPC3(config, m_hpc3, m_hal2);
	m_hpc3->set_gio64_space(m_maincpu, AS_PROGRAM);
	m_hpc3->set_addrmap(hpc3_device::AS_PIO0, &ip24_state::pio0_map);
	m_hpc3->set_addrmap(hpc3_device::AS_PIO1, &ip24_state::pio1_map);
	m_hpc3->set_addrmap(hpc3_device::AS_PIO2, &ip24_state::pio2_map);
	m_hpc3->enet_intr_out_cb().set(m_ioc2, FUNC(ioc2_device::enet_int_w));
	m_hpc3->hd_rd_cb<0>().set(m_scsi_ctrl, FUNC(wd33c93b_device::indir_r));
	m_hpc3->hd_wr_cb<0>().set(m_scsi_ctrl, FUNC(wd33c93b_device::indir_w));
	m_hpc3->hd_dma_rd_cb<0>().set(m_scsi_ctrl, FUNC(wd33c93b_device::dma_r));
	m_hpc3->hd_dma_wr_cb<0>().set(m_scsi_ctrl, FUNC(wd33c93b_device::dma_w));
	m_hpc3->hd_reset_cb<0>().set(m_scsi_ctrl, FUNC(wd33c93b_device::reset_w));
	m_hpc3->bbram_rd_cb().set(m_rtc, FUNC(ds1386_device::data_r));
	m_hpc3->bbram_wr_cb().set(m_rtc, FUNC(ds1386_device::data_w));
	m_hpc3->eeprom_dati_cb().set(m_eeprom, FUNC(eeprom_serial_93cxx_device::do_read));
	m_hpc3->eeprom_dato_cb().set(m_eeprom, FUNC(eeprom_serial_93cxx_device::di_write));
	m_hpc3->eeprom_clk_cb().set(m_eeprom, FUNC(eeprom_serial_93cxx_device::clk_write));
	m_hpc3->eeprom_cs_cb().set(m_eeprom, FUNC(eeprom_serial_93cxx_device::cs_write));
	//m_hpc3->eeprom_pre_cb().set(m_eeprom, FUNC(eeprom_serial_93cxx_device::pre_write));
	m_hpc3->dma_complete_int_cb().set(m_ioc2, FUNC(ioc2_device::hpc_dma_done_w));

	SEEQ80C03(config, m_edlc);
	m_edlc->out_int_cb().set(m_hpc3, FUNC(hpc3_device::enet_intr_in_w));
	m_edlc->out_rxrdy_cb().set(m_hpc3, FUNC(hpc3_device::enet_rxrdy_w));
	m_hpc3->set_enet(m_edlc);

	SGI_HAL2(config, m_hal2);
	EEPROM_93C56_16BIT(config, m_eeprom);

	SOFTWARE_LIST(config, "sgi_mips").set_original("sgi_mips");
	SOFTWARE_LIST(config, "sgi_mips_hdd").set_original("sgi_mips_hdd");
}

void ip24_state::ip24(machine_config &config, uint32_t system_clock)
{
	ip24_base(config, system_clock);
	m_mem_ctrl->set_input_default(DEVICE_INPUT_DEFAULTS_NAME(ip24_mc));

	m_hpc3->set_addrmap(hpc3_device::AS_PIO6, &ip24_state::pio6_map);

	SGI_IOC2_GUINNESS(config, m_ioc2, m_maincpu);

	SAA7191(config, m_dmsd);
	VINO(config, m_vino);
	m_vino->set_gio64_space(m_maincpu, AS_PROGRAM);
	m_vino->i2c_data_out().set(m_dmsd, FUNC(saa7191_device::i2c_data_w));
	m_vino->i2c_data_in().set(m_dmsd, FUNC(saa7191_device::i2c_data_r));
	m_vino->i2c_stop().set(m_dmsd, FUNC(saa7191_device::i2c_stop_w));
	m_vino->interrupt_cb().set(m_ioc2, FUNC(ioc2_device::video_int_w));

	DS1386_8K(config, m_rtc, 32768);
}

void ip24_state::indy_5015(machine_config &config)
{
	constexpr uint32_t system_clock = 50'000'000;
	ip24(config, system_clock);
	R5000BE(config, m_maincpu, 3 * system_clock);
	m_maincpu->set_system_clock(system_clock);
	m_maincpu->set_icache_size(0x8000);
	m_maincpu->set_dcache_size(0x8000);
	m_maincpu->set_addrmap(AS_PROGRAM, &ip24_state::ip24_map);
}

void ip24_state::indy_4613(machine_config &config)
{
	constexpr uint32_t system_clock = 66'666'666;
	ip24(config, system_clock);
	R4600BE(config, m_maincpu, 2 * system_clock);
	m_maincpu->set_system_clock(system_clock);
	m_maincpu->set_icache_size(0x4000);
	m_maincpu->set_dcache_size(0x4000);
	m_maincpu->set_addrmap(AS_PROGRAM, &ip24_state::ip24_map);
}

void ip24_state::indy_4610(machine_config &config)
{
	constexpr uint32_t system_clock = 50'000'000;
	ip24(config, system_clock);
	R4600BE(config, m_maincpu, 2 * system_clock);
	m_maincpu->set_system_clock(system_clock);
	m_maincpu->set_icache_size(0x4000);
	m_maincpu->set_dcache_size(0x4000);
	m_maincpu->set_addrmap(AS_PROGRAM, &ip24_state::ip24_map);
}

void ip22_state::wd33c93_2(device_t *device)
{
	device->set_clock(10000000);
	downcast<wd33c93b_device *>(device)->irq_cb().set(m_ioc2, FUNC(ioc2_device::scsi1_int_w));
	downcast<wd33c93b_device *>(device)->drq_cb().set(m_hpc3, FUNC(hpc3_device::scsi1_drq));
}

void ip22_state::indigo2_4415(machine_config &config)
{
	constexpr uint32_t system_clock = 50'000'000;
	R4400BE(config, m_maincpu, 3 * system_clock);
	m_maincpu->set_system_clock(system_clock);
	m_maincpu->set_icache_size(0x4000);
	m_maincpu->set_dcache_size(0x4000);
	m_maincpu->set_addrmap(AS_PROGRAM, &ip22_state::ip22_map);

	ip24_base(config, system_clock);
	m_mem_ctrl->set_input_default(DEVICE_INPUT_DEFAULTS_NAME(ip22_mc));

	NSCSI_BUS(config, "scsibus2", 0);
	NSCSI_CONNECTOR(config, "scsibus2:0").option_set("wd33c93", WD33C93B)
		.machine_config([this](device_t *device) { wd33c93_2(device); });
	NSCSI_CONNECTOR(config, "scsibus2:1", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsibus2:2", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsibus2:3", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsibus2:4", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsibus2:5", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsibus2:6", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsibus2:7", scsi_devices, nullptr, false);

	m_hpc3->set_addrmap(hpc3_device::AS_PIO4, &ip22_state::pio4_map);
	m_hpc3->set_addrmap(hpc3_device::AS_PIO6, &ip22_state::pio6_map);
	m_hpc3->hd_rd_cb<1>().set(m_scsi_ctrl2, FUNC(wd33c93b_device::indir_r));
	m_hpc3->hd_wr_cb<1>().set(m_scsi_ctrl2, FUNC(wd33c93b_device::indir_w));
	m_hpc3->hd_dma_rd_cb<1>().set(m_scsi_ctrl2, FUNC(wd33c93b_device::dma_r));
	m_hpc3->hd_dma_wr_cb<1>().set(m_scsi_ctrl2, FUNC(wd33c93b_device::dma_w));
	m_hpc3->hd_reset_cb<1>().set(m_scsi_ctrl2, FUNC(wd33c93b_device::reset_w));

	SGI_IOC2_FULL_HOUSE(config, m_ioc2, m_maincpu);
	DS1286(config, m_rtc, 32768);
}

#define INDY_BIOS_FLAGS(bios) ROM_GROUPDWORD | ROM_BIOS(bios)

#define INDY_BIOS_R5K \
	ROM_SYSTEM_BIOS( 0, "b10", "Version 5.3 Rev B10 R4X00/R5000 Feb 12, 1996" ) \
	ROMX_LOAD( "ip24prom.070-9101-011.bin", 0x000000, 0x080000, CRC(5e7f7e3a) SHA1(ac663a4db68528e400487e98cbf901f620fb30ce), INDY_BIOS_FLAGS(0) )

#define INDY_BIOS_R4K \
	INDY_BIOS_R5K \
	ROM_SYSTEM_BIOS( 1, "b7", "Version 5.3 Rev B7 R4X00 IP24 Feb 16, 1995" ) \
	ROMX_LOAD( "ip24prom.070-9101-008.bin", 0x000000, 0x080000, CRC(ee0b55c4) SHA1(a752a4aef7e2c6086b8b0244e9f064861a11870f), INDY_BIOS_FLAGS(1) ) \
	ROM_SYSTEM_BIOS( 2, "b6", "Version 5.0 Rev B6 Sep 28, 1994" ) \
	ROMX_LOAD( "ip24prom.070-9101-007.bin", 0x000000, 0x080000, CRC(70d8d1b1) SHA1(ade54cd2ecb7064957f8602894f05685e2f4e8fb), INDY_BIOS_FLAGS(2) ) \
	ROM_SYSTEM_BIOS( 3, "b4", "Version 5.1.2 Rev B4 R4X00 Dec 9, 1993" ) \
	ROMX_LOAD( "ip24prom.070-9101-005.bin", 0x000000, 0x080000, CRC(f5e41008) SHA1(28b769b28218a1fcd0400dceef9a284dcfbdda5b), INDY_BIOS_FLAGS(3) )

/* SCC init ip225015
 * Channel A
 * 09 <- c0 Master Interrup Control: Force HW reset + enable SWI INTACK
 * 04 <- 44 Clocks: x16 mode, 1 stop bits, no parity
 * 03 <- c0 Receiver: 8 bit data, auto enables, Rx disabled
 * 05 <- e2 Transmitter: DTR set, 8 bit data, RTS set, Tx disabled
 * 0b <- 50 Clock Mode: TRxC: XTAL output, TRxC: Output, TxC from BRG, RxC from BRG
 * 0c <- 0a Low const BRG  3.6864Mhz CLK => 9600 baud
 * 0d <- 00 High Const BRG = (CLK / (2 x Desired Rate x BR Clock period)) - 2
 * 0e <- 01 Mics: BRG enable
 * 03 <- c1 Receiver: as above + Receiver enable
 * 05 <- ea Transmitter: as above + Transmitter enable
 *
 * Channel A and B init - only BRG low const differs
 * 09 <- 80 channel A reset
 * 04 <- 44 Clocks: x16 mode, 1 stop bits, no parity
 * 0f <- 81 External/Status Control: Break/Abort enabled, WR7 prime enabled
 * 07p<- 40 External read enable (RR9=WR3, RR4=WR4, RR5=WR5, RR14=WR7 and RR11=WR10)
 * 03 <- c0 Receiver: 8 bit data, auto enables, Rx disabled
 * 05 <- e2 Transmitter: DTR set, 8 bit data, RTS set, Tx disabled
 * 0b <- 50 Clock Mode: TRxC: XTAL output, TRxC: Output, TxC from BRG, RxC from BRG
 * 0e <- 00 Mics: BRG disable
 * 0c <- 0a/04 Low const BRG, 3.6864Mhz CLK => Chan A:9600 Chan B:38400
 * 0d <- 00 High Const BRG = (CLK / (2 x Desired Rate x BR Clock period)) - 2
 * 0e <- 01 Mics: BRG enable
 * 03 <- c1 Receiver: as above + Receiver enable
 * 05 <- ea Transmitter: as above + Transmitter enable
 * 00 <- 10 Reset External/status IE
*/

ROM_START( indy_4610 )
	ROM_REGION64_BE( 0x80000, "user1", 0 )
	INDY_BIOS_R4K
ROM_END

ROM_START( indy_4613 )
	ROM_REGION64_BE( 0x80000, "user1", 0 )
	INDY_BIOS_R4K
ROM_END

ROM_START( indy_5015 )
	ROM_REGION64_BE( 0x80000, "user1", 0 )
	INDY_BIOS_R5K
ROM_END

ROM_START( indigo2_4415 )
	ROM_REGION64_BE( 0x80000, "user1", 0 )
	ROM_SYSTEM_BIOS( 0, "e", "Version 5.3 Rev E R4X00 Jan 29, 1996" )
	ROMX_LOAD( "ip22prom.070-1367-012.bin", 0x000000, 0x080000, CRC(54460c16) SHA1(330d87b3a02a05fb49c85a569f6f84904587cb35), ROM_GROUPDWORD | ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "b4", "Version 5.1.2 Rev B4 R4X00 Dec 9, 1993" )
	ROMX_LOAD( "ip22prom.070-1367-002.bin", 0x000000, 0x080000, CRC(ae5ecd08) SHA1(422568ae95282ee23b2fe123267f9b915a1dc3dc), ROM_GROUPDWORD | ROM_BIOS(1) )
ROM_END

} // anonymous namespace


//    YEAR  NAME          PARENT     COMPAT  MACHINE       INPUT CLASS       INIT        COMPANY                 FULLNAME                   FLAGS
COMP( 1993, indy_4610,    0,         0,      indy_4610,    ip24, ip24_state, empty_init, "Silicon Graphics Inc", "Indy (R4600, 100MHz)",    MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_NODEVICE_MICROPHONE )
COMP( 1993, indy_4613,    indy_4610, 0,      indy_4613,    ip24, ip24_state, empty_init, "Silicon Graphics Inc", "Indy (R4600, 133MHz)",    MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_NODEVICE_MICROPHONE )
COMP( 1996, indy_5015,    indy_4610, 0,      indy_5015,    ip24, ip24_state, empty_init, "Silicon Graphics Inc", "Indy (R5000, 150MHz)",    MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_NODEVICE_MICROPHONE )
COMP( 1993, indigo2_4415, 0,         0,      indigo2_4415, ip24, ip22_state, empty_init, "Silicon Graphics Inc", "Indigo2 (R4400, 150MHz)", MACHINE_NOT_WORKING )



informer_207_100.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    Informer 207/100

    VT-100 compatible terminal

    Hardware:
    - HD68B09EP
    - 3x HM6116LP-4 (+ 3 empty sockets), 2x TC5517BPL-20
    - R6545-1AP CRTC
    - MC2681P DUART
    - MC68B50P ACIA
    - M58321 RTC (TBD: how this is used?)
    - 19.7184 MHz XTAL, 3.6864 MHz XTAL

***************************************************************************/

#include "emu.h"

#include "bus/keytronic/keytronic.h"
#include "cpu/m6809/m6809.h"
#include "machine/6850acia.h"
#include "machine/input_merger.h"
#include "machine/mc68681.h"
#include "machine/msm58321.h"
#include "machine/nvram.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"


namespace {

//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class informer_207_100_state : public driver_device
{
public:
	informer_207_100_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_crtc(*this, "crtc"),
		m_screen(*this, "screen"),
		m_palette(*this, "palette"),
		m_duart(*this, "duart"),
		m_acia(*this, "acia"),
		m_rtc(*this, "rtc"),
		m_vram(*this, "vram"),
		m_rom(*this, "maincpu"),
		m_chargen(*this, "chargen")
	{ }

	void informer_207_100(machine_config &config);

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

private:
	required_device<mc6809e_device> m_maincpu;
	required_device<r6545_1_device> m_crtc;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<scn2681_device> m_duart;
	required_device<acia6850_device> m_acia;
	required_device<msm58321_device> m_rtc;
	required_shared_ptr<uint8_t> m_vram;
	required_region_ptr<uint8_t> m_rom;
	required_region_ptr<uint8_t> m_chargen;

	void misc_output_w(uint8_t data);
	void unk_output_w(uint8_t data);
	uint8_t vector_r(offs_t offset);

	void mem_map(address_map &map) ATTR_COLD;

	MC6845_ON_UPDATE_ADDR_CHANGED(crtc_addr);
	MC6845_UPDATE_ROW(crtc_update_row);
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void informer_207_100_state::mem_map(address_map &map)
{
	map(0x0000, 0x0fff).ram().share("vram");
	map(0x1000, 0x17ff).ram().share("nvram");
	map(0x1800, 0x27ff).ram();
	map(0x2800, 0x2800).nopr(); // dummy reads
	map(0x8000, 0xffff).rom().region("maincpu", 0);
	map(0xff00, 0xff7f).unmaprw();
	map(0xff00, 0xff0f).rw(m_duart, FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0xff10, 0xff10).w(FUNC(informer_207_100_state::misc_output_w));
	map(0xff14, 0xff14).w(FUNC(informer_207_100_state::unk_output_w));
	map(0xff20, 0xff20).w(m_crtc, FUNC(mc6845_device::address_w));
	map(0xff21, 0xff21).w(m_crtc, FUNC(mc6845_device::register_w));
	map(0xff40, 0xff41).w(m_acia, FUNC(acia6850_device::write));
	map(0xff42, 0xff43).r(m_acia, FUNC(acia6850_device::read));
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( informer_207_100 )
INPUT_PORTS_END


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

MC6845_ON_UPDATE_ADDR_CHANGED( informer_207_100_state::crtc_addr )
{
//  m_video_update_address = address;
}

MC6845_UPDATE_ROW( informer_207_100_state::crtc_update_row )
{
	pen_t const *const pen = m_palette->pens();

	for (int x = 0; x < x_count; x++)
	{
//      uint8_t attr = m_vram[((ma + x) & 0x7ff) * 2 + 0];
		uint8_t code = m_vram[((ma + x) & 0x7ff) * 2 + 1];
		uint8_t data = m_chargen[(code << 4) + ra];

		if (x == cursor_x)
			data = 0xff;

		// draw 8 pixels of the character
		bitmap.pix(y, x * 10 + 9) = pen[0];
		bitmap.pix(y, x * 10 + 8) = pen[0];
		bitmap.pix(y, x * 10 + 7) = pen[BIT(data, 0)];
		bitmap.pix(y, x * 10 + 6) = pen[BIT(data, 1)];
		bitmap.pix(y, x * 10 + 5) = pen[BIT(data, 2)];
		bitmap.pix(y, x * 10 + 4) = pen[BIT(data, 3)];
		bitmap.pix(y, x * 10 + 3) = pen[BIT(data, 4)];
		bitmap.pix(y, x * 10 + 2) = pen[BIT(data, 5)];
		bitmap.pix(y, x * 10 + 1) = pen[BIT(data, 6)];
		bitmap.pix(y, x * 10 + 0) = pen[BIT(data, 7)];
	}
}

static const gfx_layout char_layout =
{
	8,12,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8*16
};

static GFXDECODE_START(chars)
	GFXDECODE_ENTRY("chargen", 0, char_layout, 0, 1)
GFXDECODE_END


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void informer_207_100_state::machine_start()
{
}

void informer_207_100_state::machine_reset()
{
}

void informer_207_100_state::misc_output_w(uint8_t data)
{
	logerror("%s: Writing $%02X to $FF10\n", machine().describe_context(), data);
}

void informer_207_100_state::unk_output_w(uint8_t data)
{
	logerror("%s: Writing $%02X to $FF14\n", machine().describe_context(), data);
}

uint8_t informer_207_100_state::vector_r(offs_t offset)
{
	// FIRQ handler seems not to explicitly acknowledge the interrupt, so do it implicitly here
	if (!BIT(offset, 3))
		m_maincpu->set_input_line(M6809_FIRQ_LINE, CLEAR_LINE);
	return m_rom[offset & 0x7fff];
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void informer_207_100_state::informer_207_100(machine_config &config)
{
	MC6809E(config, m_maincpu, 19.7184_MHz_XTAL / 10); // clock divisor guessed
	m_maincpu->set_addrmap(AS_PROGRAM, &informer_207_100_state::mem_map);
	m_maincpu->interrupt_vector_read().set(FUNC(informer_207_100_state::vector_r));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, M6809_IRQ_LINE);

	SCN2681(config, m_duart, 3.6864_MHz_XTAL);
	m_duart->irq_cb().set("mainirq", FUNC(input_merger_device::in_w<0>));
	m_duart->b_tx_cb().set("kbd", FUNC(keytronic_connector_device::ser_in_w));
	m_duart->outport_cb().set(m_acia, FUNC(acia6850_device::write_txc)).bit(3);
	m_duart->outport_cb().append(m_acia, FUNC(acia6850_device::write_rxc)).bit(3);

	ACIA6850(config, m_acia);
	m_acia->irq_handler().set("mainirq", FUNC(input_merger_device::in_w<1>));

	keytronic_connector_device &kbd(KEYTRONIC_CONNECTOR(config, "kbd", informer_207_100_keyboards, "in207100"));
	kbd.ser_out_callback().set(m_duart, FUNC(scn2681_device::rx_b_w));

	MSM58321(config, m_rtc, 32.768_kHz_XTAL);

	// video
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_color(rgb_t::green());
	m_screen->set_raw(19.7184_MHz_XTAL, 1040, 0, 800, 316, 0, 300);
	m_screen->set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	GFXDECODE(config, "gfxdecode", m_palette, chars);

	R6545_1(config, m_crtc, 19.7184_MHz_XTAL / 10); // clock divisor guessed
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(10);
	m_crtc->set_on_update_addr_change_callback(FUNC(informer_207_100_state::crtc_addr));
	m_crtc->set_update_row_callback(FUNC(informer_207_100_state::crtc_update_row));
	m_crtc->out_vsync_callback().set_inputline(m_maincpu, M6809_FIRQ_LINE, ASSERT_LINE);
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( in207100 )
	ROM_REGION(0x8000, "maincpu", 0)
	// <Label lost; EPROM type is SEEQ DQ1533-300 2764-30> (1 empty socket is directly to left)
	ROM_LOAD("79532-002.bin", 0x0000, 0x2000, CRC(848d1b45) SHA1(77dd68951ac85e5dc51b51db002d90863b0fce43))
	// 79505-002  V2.00  ED2F -2-3
	ROM_LOAD("79505-002.bin", 0x4000, 0x4000, CRC(3dfae553) SHA1(ae6849cacb07792769f93aa736f5603e28fa8635))

	ROM_REGION(0x1000, "chargen", 0)
	// 79496  REV 1.01  12-29-83
	ROM_LOAD("79496-101.bin", 0x0000, 0x1000, CRC(930ac23a) SHA1(74e6bf81b60e3504cb2b9f14a33e7c3e367dc825))

	ROM_REGION(0x220, "proms", 0)
	ROM_LOAD("82s131_z35.bin", 0x000, 0x200, CRC(5a002c87) SHA1(59e51ac7106f0925959655b1df1d8452db76943e))
	ROM_LOAD("82s123_z5.bin", 0x200, 0x020, CRC(eec80ecf) SHA1(fb58086229aed8187ecf0d24573b7e71980f271c))
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

COMP( 1983, in207100, 0, 0, informer_207_100, informer_207_100, informer_207_100_state, empty_init, "Informer Computer Terminals", "Informer 207/100", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



informer_207_376.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    Informer 207/376

    IBM 3270 compatible terminal

    Hardware:
    - M6809
    - 8253 PIT
    - Z80SCC 8530
    - 2x 6850 ACIA (up to 4)
    - 2x X2212P NOVRAM

    TODO:
    - Dump keyboard controller and emulate it (currently HLE'd)
    - Problably needs improvements to at least the Z80SCC to
      properly support synchrous modes
    - Figure out the unknown bits at 0x8400
    - Verify clock speeds

    Notes:
    - Thanks to charcole and his zmachine3270 project at
      https://github.com/charcole/zmachine3270

***************************************************************************/

#include "emu.h"
#include "cpu/m6809/m6809.h"
#include "machine/6850acia.h"
#include "machine/pit8253.h"
#include "machine/input_merger.h"
#include "machine/ripple_counter.h"
#include "machine/x2212.h"
#include "machine/z80scc.h"
#include "video/mc6845.h"
#include "sound/beep.h"
#include "bus/rs232/rs232.h"
#include "bus/rs232/printer.h"
#include "informer_207_376_kbd.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class informer_207_376_state : public driver_device
{
public:
	informer_207_376_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_novram(*this, "novram%u", 0U),
		m_crtc(*this, "crtc"),
		m_screen(*this, "screen"),
		m_palette(*this, "palette"),
		m_pit(*this, "pit"),
		m_scc(*this, "scc"),
		m_acia(*this, "acia%u", 0U),
		m_beep(*this, "beep"),
		m_ram(*this, "ram"),
		m_chargen(*this, "chargen")
	{ }

	void informer_207_376(machine_config &config);

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device_array<x2212_device, 2> m_novram;
	required_device<mc6845_device> m_crtc;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<pit8253_device> m_pit;
	required_device<scc85c30_device> m_scc;
	required_device_array<acia6850_device, 2> m_acia;
	required_device<beep_device> m_beep;
	required_shared_ptr<uint8_t> m_ram;
	required_region_ptr<uint8_t> m_chargen;

	void mem_map(address_map &map) ATTR_COLD;

	void unk_8400_w(uint8_t data);
	void crt_brightness_w(uint8_t data);
	uint8_t novram_r(address_space &space, offs_t offset);
	void novram_w(offs_t offset, uint8_t data);
	uint8_t novram_recall_r();

	MC6845_UPDATE_ROW(crtc_update_row);
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void informer_207_376_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).ram().share("ram");
	map(0x8000, 0x8000).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x8001, 0x8001).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x8400, 0x8400).lr8(NAME([] () { return 0xff; })).w(FUNC(informer_207_376_state::unk_8400_w)); // ?
	map(0x8802, 0x8803).rw(m_acia[0], FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0x8804, 0x8805).rw(m_acia[1], FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0x8c00, 0x8c00).w(FUNC(informer_207_376_state::crt_brightness_w));
	map(0x9000, 0x9003).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x9400, 0x9403).rw(m_scc, FUNC(scc85c30_device::ab_dc_r), FUNC(scc85c30_device::ab_dc_w));
	map(0x9800, 0x9800).r(FUNC(informer_207_376_state::novram_recall_r));
	map(0x9c00, 0x9cff).rw(FUNC(informer_207_376_state::novram_r), FUNC(informer_207_376_state::novram_w));
	map(0xa000, 0xffff).rom().region("maincpu", 0);
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( informer_207_376 )
INPUT_PORTS_END


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

void informer_207_376_state::crt_brightness_w(uint8_t data)
{
	// unknown algorithm for the brightness
	// default value is 6, range is 0 (off) to 15 (brightest)
	m_screen->set_brightness(256 - (256 / ((data & 0x0f) + 1)));
}

MC6845_UPDATE_ROW( informer_207_376_state::crtc_update_row )
{
	pen_t const *const pen = m_palette->pens();

	for (int x = 0; x < x_count; x++)
	{
		uint8_t const code = m_ram[ma + x];
		uint8_t data = m_chargen[(code << 4) + ra];

		if (x == cursor_x)
			data = 0xff;

		// the line above the status bar seems to be hardcoded
		if (y == 274)
			data = 0xff;

		// draw 8 pixels of the character
		bitmap.pix(y, x * 8 + 7) = pen[BIT(data, 0)];
		bitmap.pix(y, x * 8 + 6) = pen[BIT(data, 1)];
		bitmap.pix(y, x * 8 + 5) = pen[BIT(data, 2)];
		bitmap.pix(y, x * 8 + 4) = pen[BIT(data, 3)];
		bitmap.pix(y, x * 8 + 3) = pen[BIT(data, 4)];
		bitmap.pix(y, x * 8 + 2) = pen[BIT(data, 5)];
		bitmap.pix(y, x * 8 + 1) = pen[BIT(data, 6)];
		bitmap.pix(y, x * 8 + 0) = pen[BIT(data, 7)];
	}
}

static const gfx_layout char_layout =
{
	8,11,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8 },
	8*16
};

static GFXDECODE_START(chars)
	GFXDECODE_ENTRY("chargen", 0, char_layout, 0, 1)
GFXDECODE_END


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void informer_207_376_state::unk_8400_w(uint8_t data)
{
	// 7-------  beeper
	// -6------  unknown
	// --5-----  1=internal modem, 0=host rs232
	// ---43---  unknown
	// -----2--  unknown
	// ------10  unknown

	m_beep->set_state(BIT(data, 7));
}

uint8_t informer_207_376_state::novram_r(address_space &space, offs_t offset)
{
	return (m_novram[0]->read(space, offset) << 4) | (m_novram[1]->read(space, offset) & 0x0f);
}

void informer_207_376_state::novram_w(offs_t offset, uint8_t data)
{
	m_novram[0]->write(offset, data >> 4);
	m_novram[1]->write(offset, data & 0x0f);
}

uint8_t informer_207_376_state::novram_recall_r()
{
	m_novram[0]->recall(1);
	m_novram[1]->recall(1);
	m_novram[0]->recall(0);
	m_novram[1]->recall(0);

	return 0xff;
}

void informer_207_376_state::machine_start()
{
}

void informer_207_376_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void printer_devices(device_slot_interface &device)
{
	device.option_add("printer", SERIAL_PRINTER);
}

void informer_207_376_state::informer_207_376(machine_config &config)
{
	MC6809(config, m_maincpu, 36_MHz_XTAL / 4); // unknown clock divisor
	m_maincpu->set_addrmap(AS_PROGRAM, &informer_207_376_state::mem_map);

	input_merger_device &cpu_irq(INPUT_MERGER_ANY_HIGH(config, "cpu_irq"));
	cpu_irq.output_handler().set_inputline(m_maincpu, M6809_IRQ_LINE);

	X2212(config, m_novram[0]);
	X2212(config, m_novram[1]);

	PIT8253(config, m_pit);
	m_pit->set_clk<0>(2.457600_MHz_XTAL);
	m_pit->out_handler<0>().set(m_acia[1], FUNC(acia6850_device::write_txc));
	m_pit->out_handler<0>().append(m_acia[1], FUNC(acia6850_device::write_rxc));
	m_pit->set_clk<1>(2.457600_MHz_XTAL);
	m_pit->out_handler<1>().set(m_acia[0], FUNC(acia6850_device::write_txc));
	m_pit->out_handler<1>().append(m_acia[0], FUNC(acia6850_device::write_rxc));
	m_pit->out_handler<1>().append("nmi_clk", FUNC(ripple_counter_device::clock_w));

	ripple_counter_device &nmi_clk(RIPPLE_COUNTER(config, "nmi_clk")); // CD4020BE
	nmi_clk.set_stages(14);
	nmi_clk.count_out_cb().set_inputline(m_maincpu, INPUT_LINE_NMI).bit(13); // Q14

	SCC85C30(config, m_scc, 0); // externally clocked?
	m_scc->out_txda_callback().set("com1", FUNC(rs232_port_device::write_txd));
	m_scc->out_dtra_callback().set("com1", FUNC(rs232_port_device::write_dtr));
	m_scc->out_rtsa_callback().set("com1", FUNC(rs232_port_device::write_rts));
	m_scc->out_txdb_callback().set("com2", FUNC(rs232_port_device::write_txd));
	m_scc->out_dtrb_callback().set("com2", FUNC(rs232_port_device::write_dtr));
	m_scc->out_rtsb_callback().set("com2", FUNC(rs232_port_device::write_rts));
	m_scc->out_int_callback().set("cpu_irq", FUNC(input_merger_device::in_w<0>));

	ACIA6850(config, m_acia[0]);
	m_acia[0]->txd_handler().set("kbd", FUNC(informer_207_376_kbd_hle_device::rx_w));
	m_acia[0]->irq_handler().set("cpu_irq", FUNC(input_merger_device::in_w<1>));
	m_acia[0]->rts_handler().set(m_novram[0], FUNC(x2212_device::store)).invert();
	m_acia[0]->rts_handler().append(m_novram[1], FUNC(x2212_device::store)).invert();

	ACIA6850(config, m_acia[1]);
	m_acia[1]->txd_handler().set("printer", FUNC(rs232_port_device::write_txd));

	rs232_port_device &com1(RS232_PORT(config, "com1", default_rs232_devices, nullptr));
	com1.rxd_handler().set(m_scc, FUNC(scc85c30_device::rxa_w));
	com1.dcd_handler().set(m_scc, FUNC(scc85c30_device::dcda_w));
	com1.cts_handler().set(m_scc, FUNC(scc85c30_device::ctsa_w));

	rs232_port_device &com2(RS232_PORT(config, "com2", default_rs232_devices, nullptr));
	com2.rxd_handler().set(m_scc, FUNC(scc85c30_device::rxb_w));
	com2.dcd_handler().set(m_scc, FUNC(scc85c30_device::dcdb_w));
	com2.cts_handler().set(m_scc, FUNC(scc85c30_device::ctsb_w));

	RS232_PORT(config, "printer", printer_devices, nullptr);

	informer_207_376_kbd_hle_device &kbd(INFORMER_207_376_KBD_HLE(config, "kbd"));
	kbd.tx_handler().set(m_acia[0], FUNC(acia6850_device::write_rxd));

	// video
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_color(rgb_t::green());
	m_screen->set_raw(36_MHz_XTAL / 2.5, 800, 0, 640, 300, 0, 286);
	m_screen->set_screen_update(m_crtc, FUNC(mc6845_device::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	GFXDECODE(config, "gfxdecode", m_palette, chars);

	MC6845(config, m_crtc, 36_MHz_XTAL / 20);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(informer_207_376_state::crtc_update_row));

	// sound
	SPEAKER(config, "mono").front_center();
	BEEP(config, "beep", 500).add_route(ALL_OUTPUTS, "mono", 0.50); // frequency unknown
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( in207376 )
	ROM_REGION(0x6000, "maincpu", 0)
	// M2764A F1
	// 79590-023  376SNA V1.00  201C 7224  (checksum matches)
	ROM_LOAD("79590-023.z37", 0x0000, 0x2000, CRC(61d49637) SHA1(aabcd55af88ff0b6a198eef04a88f58cc8f65dcc))
	// M27128AF1
	// 79589-023  376 SNAV1.00  201C E86F  (checksum matches)
	ROM_LOAD("79589-023.z36", 0x2000, 0x4000, CRC(cdfaf629) SHA1(1f21ef6848020726ef3d7ab05166ac8590d58476))

	ROM_REGION(0x1000, "chargen", 0)
	// TMS2732AGL-25
	// 79573-001  V1.00 376/8  CHAR. GEN.
	ROM_LOAD("79573-001.z6", 0x0000, 0x1000, CRC(f704b827) SHA1(bcc56eeb8681c2bebe3a9f4b6b78c0373c06d875))
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

COMP( 1986, in207376, 0, 0, informer_207_376, informer_207_376, informer_207_376_state, empty_init, "Informer Computer Terminals", "Informer 207/376", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



informer_213.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    Informer 213 (IBM 374/SNA V.22)
    Informer 213 AE (VT-100)

    Hardware:
    - EF68B09EP
    - 2x TC5564PL-15 [8k] (next to CG ROM)
    - 1x TC5565APL-15 [8k] + 2x TMS4464-15NL [32k] (next to CPU)
    - Z0853006PSC SCC
    - ASIC (INFORMER 44223)
    - 18.432 MHz XTAL

    TODO:
    - Figure out the ASIC and how it's connected

***************************************************************************/

#include "emu.h"
#include "cpu/m6809/m6809.h"
#include "machine/nvram.h"
#include "machine/z80scc.h"
#include "bus/keytronic/keytronic.h"
#include "bus/rs232/rs232.h"
#include "bus/rs232/printer.h"
#include "sound/beep.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

namespace {

class informer_213_kbdintf_device : public device_t, public device_serial_interface
{
public:
	informer_213_kbdintf_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);

	auto int_handler() { return m_int_handler.bind(); }
	auto txd_handler() { return m_txd_handler.bind(); }

	uint8_t read();
	void write(uint8_t data);

protected:
	virtual void device_start() override ATTR_COLD;

	virtual void tra_callback() override;
	virtual void rcv_complete() override;

private:
	devcb_write_line m_int_handler;
	devcb_write_line m_txd_handler;
};

} // anonymous namespace

DEFINE_DEVICE_TYPE(INFORMER_213_KBDINTF, informer_213_kbdintf_device, "informer_213_kbdintf", "Informer 213 Keyboard Interface")

namespace {

class informer_213_state : public driver_device
{
public:
	informer_213_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_nvram(*this, "nvram%u", 0U),
		m_screen(*this, "screen"),
		m_palette(*this, "palette"),
		m_scc(*this, "scc"),
		m_beep(*this, "beep"),
		m_nvram_bank(*this, "banked"),
		m_vram(*this, "vram"),
		m_aram(*this, "aram"),
		m_chargen(*this, "chargen")
	{ }

	void informer_213(machine_config &config);

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device_array<nvram_device, 2> m_nvram;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<scc8530_device> m_scc;
	required_device<beep_device> m_beep;
	required_memory_bank m_nvram_bank;
	required_shared_ptr<uint8_t> m_vram;
	required_shared_ptr<uint8_t> m_aram;
	required_region_ptr<uint8_t> m_chargen;

	void mem_map(address_map &map) ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void vblank_w(int state);
	void vram_start_addr_w(offs_t offset, uint8_t data);
	void vram_end_addr_w(offs_t offset, uint8_t data);
	void vram_start_addr2_w(offs_t offset, uint8_t data);
	void cursor_addr_w(offs_t offset, uint8_t data);
	void cursor_start_w(uint8_t data);
	void cursor_end_w(uint8_t data);
	void screen_ctrl_w(uint8_t data);

	void bell_w(uint8_t data);

	void kbd_int_w(int state);
	uint8_t firq_vector_r();

	std::unique_ptr<uint8_t[]> m_banked_ram;

	uint16_t m_vram_start_addr;
	uint16_t m_vram_end_addr;
	uint16_t m_vram_start_addr2;
	uint8_t m_cursor_start;
	uint8_t m_cursor_end;
	uint16_t m_cursor_addr;
	uint8_t m_screen_ctrl;
	uint8_t m_firq_vector;

	memory_access<16, 0, 0, ENDIANNESS_BIG>::specific m_program;
};


//**************************************************************************
//  SERIAL KEYBOARD INTERFACE
//**************************************************************************

informer_213_kbdintf_device::informer_213_kbdintf_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	device_t(mconfig, INFORMER_213_KBDINTF, tag, owner, clock),
	device_serial_interface(mconfig, *this),
	m_int_handler(*this),
	m_txd_handler(*this)
{
}

void informer_213_kbdintf_device::device_start()
{
	set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1);
	set_rcv_rate(300);
	set_tra_rate(300);
}

uint8_t informer_213_kbdintf_device::read()
{
	if (!machine().side_effects_disabled())
		m_int_handler(CLEAR_LINE);
	return get_received_char();
}

void informer_213_kbdintf_device::write(uint8_t data)
{
	transmit_register_setup(data);
}

void informer_213_kbdintf_device::tra_callback()
{
	m_txd_handler(transmit_register_get_data_bit());
}

void informer_213_kbdintf_device::rcv_complete()
{
	receive_register_extract();
	m_int_handler(ASSERT_LINE);
}


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void informer_213_state::mem_map(address_map &map)
{
	map(0x0000, 0x0000).rw("kbdintf", FUNC(informer_213_kbdintf_device::read), FUNC(informer_213_kbdintf_device::write));
	map(0x0006, 0x0007).unmapr().w(FUNC(informer_213_state::vram_start_addr_w));
	map(0x0008, 0x0009).unmapr().w(FUNC(informer_213_state::vram_end_addr_w));
	map(0x000a, 0x000b).unmapr().w(FUNC(informer_213_state::vram_start_addr2_w));
	map(0x000d, 0x000d).unmapr().w(FUNC(informer_213_state::cursor_start_w));
	map(0x000e, 0x000e).unmapr().w(FUNC(informer_213_state::cursor_end_w));
	map(0x000f, 0x0010).unmapr().w(FUNC(informer_213_state::cursor_addr_w));
	map(0x0021, 0x0021).w(FUNC(informer_213_state::screen_ctrl_w));
	map(0x0040, 0x0043).rw(m_scc, FUNC(scc85c30_device::ab_dc_r), FUNC(scc85c30_device::ab_dc_w));
	map(0x0060, 0x0060).w(FUNC(informer_213_state::bell_w));
	map(0x0100, 0x03ff).ram().share("nvram0"); // might not be all nvram
	map(0x0400, 0x07ff).bankrw("banked"); // unknown size, data written 0x540 to 0x749
	map(0x0800, 0x5fff).ram();
	map(0x6000, 0x6fff).ram().share("vram");
	map(0x7000, 0x7fff).ram().share("aram");
	map(0x8000, 0xffff).rom().region("maincpu", 0);
	map(0xfff7, 0xfff7).r(FUNC(informer_213_state::firq_vector_r));
	map(0xfff9, 0xfff9).lr8(NAME([this] () { return m_scc->m1_r(); })); // irq vector
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( informer_213 )
INPUT_PORTS_END


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

uint32_t informer_213_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	offs_t chargen_base = (m_chargen.bytes() == 0x4000) ? 0x2000 : 0;

	// line at which vram splits into two windows
	int split = 24 - ((m_vram_start_addr2 - m_vram_start_addr) / 80);

	auto dis = machine().disable_side_effects();
	for (int y = 0; y < 26; y++)
	{
		uint8_t line_attr = 0;

		for (int x = 0; x < 80; x++)
		{
			uint16_t addr;

			// vram is split into 3 areas: display window 1, display window 2, status bar
			if (y >= 24)
				addr = 0x6000 + (y - 24) * 80 + x;
			else if (y >= split)
				addr = m_vram_start_addr + (y - split) * 80 + x;
			else
				addr = m_vram_start_addr2 + y * 80 + x;

			uint8_t code = m_program.read_byte(addr);
			uint8_t attr = m_program.read_byte(addr+0x1000);

			//logerror("%02d.%02d %04x %02x %02x\n", x, y, addr, code, attr);

			if (code == 0xc0 || code == 0xe8)
				line_attr = attr;

			// draw 9 lines
			for (int i = 0; i < 9; i++)
			{
				uint8_t data = m_chargen[chargen_base | ((code << 4) + i)];

				// conceal
				if (line_attr & 0x08 || attr & 0x08)
					data = 0x00;

				// reverse video
				if (line_attr & 0x10 || attr & 0x10)
					data ^= 0xff;

				// underline (not supported by terminal?)
				if (line_attr & 0x20 || attr & 0x20)
					data = +data;

				// blink (todo: timing)
				if (line_attr & 0x40 || attr & 0x40)
					data = m_screen->frame_number() & 0x20 ? 0x00 :data;

				if (code == 0xc0 || code == 0xe8)
					data = 0;

				if (BIT(m_screen_ctrl, 5))
					data ^= 0xff;

				// cursor
				if (BIT(m_cursor_start, 5) == 1 && addr == m_cursor_addr && y < 24)
					if (i >= (m_cursor_start & 0x0f) && i < (m_cursor_end & 0x0f))
						if (!(BIT(m_screen_ctrl, 4) == 0 && (m_screen->frame_number() & 0x20)))
							data = 0xff;

				// 6 pixels of the character
				bitmap.pix(y * 9 + i, x * 6 + 0) = BIT(data, 7) ? rgb_t::white() : rgb_t::black();
				bitmap.pix(y * 9 + i, x * 6 + 1) = BIT(data, 6) ? rgb_t::white() : rgb_t::black();
				bitmap.pix(y * 9 + i, x * 6 + 2) = BIT(data, 5) ? rgb_t::white() : rgb_t::black();
				bitmap.pix(y * 9 + i, x * 6 + 3) = BIT(data, 4) ? rgb_t::white() : rgb_t::black();
				bitmap.pix(y * 9 + i, x * 6 + 4) = BIT(data, 3) ? rgb_t::white() : rgb_t::black();
				bitmap.pix(y * 9 + i, x * 6 + 5) = BIT(data, 2) ? rgb_t::white() : rgb_t::black();
			}
		}
	}

	return 0;
}

void informer_213_state::vram_start_addr_w(offs_t offset, uint8_t data)
{
	if (offset)
		m_vram_start_addr = (m_vram_start_addr & 0xff00) | (data << 0);
	else
		m_vram_start_addr = (m_vram_start_addr & 0x00ff) | (data << 8);

	if (offset)
		logerror("vram_start_addr_w: %04x\n", m_vram_start_addr);
}

void informer_213_state::vram_end_addr_w(offs_t offset, uint8_t data)
{
	if (offset)
		m_vram_end_addr = (m_vram_end_addr & 0xff00) | (data << 0);
	else
		m_vram_end_addr = (m_vram_end_addr & 0x00ff) | (data << 8);

	if (offset)
		logerror("vram_end_addr_w: %04x\n", m_vram_end_addr);
}

void informer_213_state::vram_start_addr2_w(offs_t offset, uint8_t data)
{
	if (offset)
		m_vram_start_addr2 = (m_vram_start_addr2 & 0xff00) | (data << 0);
	else
		m_vram_start_addr2 = (m_vram_start_addr2 & 0x00ff) | (data << 8);

	if (offset)
		logerror("vram_start_addr2_w: %04x\n", m_vram_start_addr2);
}

void informer_213_state::cursor_start_w(uint8_t data)
{
	logerror("cursor_start_w: %02x\n", data);

	// 76------  unknown
	// --5-----  cursor visible
	// ---4----  unknown
	// ----3210  cursor starting line, values seen: 0 (block/off), 6 (underline)

	m_cursor_start = data;
}

void informer_213_state::cursor_end_w(uint8_t data)
{
	logerror("cursor_end_w: %02x\n", data);

	// 7654----  unknown
	// ----3210  cursor ending line, values seen: 9 (block), 8 (underline), 1 (off)

	m_cursor_end = data;
}

void informer_213_state::cursor_addr_w(offs_t offset, uint8_t data)
{
	if (offset)
		m_cursor_addr = (m_cursor_addr & 0xff00) | (data << 0);
	else
		m_cursor_addr = (m_cursor_addr & 0x00ff) | (data << 8);

	if (offset)
		logerror("cursor_addr_w: %04x\n", m_cursor_addr);
}

void informer_213_state::screen_ctrl_w(uint8_t data)
{
	logerror("screen_ctrl_w: %02x\n", data);

	// 76------  unknown
	// --5-----  screen reverse
	// ---4----  cursor blink/steady
	// ----3210  unknown

	m_screen_ctrl = data;
}

void informer_213_state::vblank_w(int state)
{
	if (state)
	{
		// real source if this interrupt is unknown, deactivated for now
#if 0
		m_firq_vector = 0x10;
		m_maincpu->set_input_line(M6809_FIRQ_LINE, HOLD_LINE);
#endif
	}
}

static const gfx_layout char_layout =
{
	6,9,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8 },
	8*16
};

static GFXDECODE_START(chars)
	GFXDECODE_ENTRY("chargen", 0, char_layout, 0, 1)
GFXDECODE_END


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void informer_213_state::bell_w(uint8_t data)
{
	logerror("bell_w: %02x\n", data);

	// 7654----  unknown
	// ----3---  ram bank
	// -----2--  beeper
	// ------10  unknown

	m_beep->set_state(BIT(data, 2));
	m_nvram_bank->set_entry(BIT(data, 3));
}

void informer_213_state::kbd_int_w(int state)
{
	if (state == CLEAR_LINE)
		return;

	m_firq_vector = 0x14;
	m_maincpu->set_input_line(M6809_FIRQ_LINE, HOLD_LINE);
}

uint8_t informer_213_state::firq_vector_r()
{
	uint8_t tmp = m_firq_vector;
	m_firq_vector = 0x00;

	return tmp;
}

void informer_213_state::machine_start()
{
	m_banked_ram = make_unique_clear<uint8_t[]>(0x800);
	membank("banked")->configure_entries(0, 2, m_banked_ram.get(), 0x400);

	m_nvram[1]->set_base(m_banked_ram.get(), 0x800);

	m_maincpu->space(AS_PROGRAM).specific(m_program);

	// register for save states
	save_item(NAME(m_vram_start_addr));
	save_item(NAME(m_vram_start_addr2));
	save_item(NAME(m_vram_end_addr));
	save_item(NAME(m_cursor_addr));
	save_item(NAME(m_screen_ctrl));
	save_item(NAME(m_firq_vector));
	save_item(NAME(m_cursor_start));
	save_item(NAME(m_cursor_end));
}

void informer_213_state::machine_reset()
{
	m_firq_vector = 0x00;
	m_vram_start_addr = 0x0084;
	m_vram_start_addr2 = 0x0084;
	m_vram_end_addr = 0xffff;
	m_cursor_start = 0x20;
	m_cursor_end = 0x09;
	m_cursor_addr = 0xffff;
	m_screen_ctrl = 0x18;
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void in213_printer_devices(device_slot_interface &device)
{
	device.option_add("printer", SERIAL_PRINTER);
}

void informer_213_state::informer_213(machine_config &config)
{
	MC6809(config, m_maincpu, 18.432_MHz_XTAL / 4); // unknown clock
	m_maincpu->set_addrmap(AS_PROGRAM, &informer_213_state::mem_map);

	NVRAM(config, m_nvram[0], nvram_device::DEFAULT_ALL_0);
	NVRAM(config, m_nvram[1], nvram_device::DEFAULT_ALL_0);

	SCC8530(config, m_scc, 18.432_MHz_XTAL / 5);
	m_scc->out_txda_callback().set("host", FUNC(rs232_port_device::write_txd));
	m_scc->out_dtra_callback().set("host", FUNC(rs232_port_device::write_dtr));
	m_scc->out_rtsa_callback().set("host", FUNC(rs232_port_device::write_rts));
	m_scc->out_txdb_callback().set("printer", FUNC(rs232_port_device::write_txd));
	m_scc->out_int_callback().set_inputline(m_maincpu, M6809_IRQ_LINE);

	keytronic_connector_device &kbd(KEYTRONIC_CONNECTOR(config, "kbd", informer_213_keyboards, "in213"));
	kbd.ser_out_callback().set("kbdintf", FUNC(informer_213_kbdintf_device::rx_w));

	rs232_port_device &rs232_host(RS232_PORT(config, "host", default_rs232_devices, nullptr));
	rs232_host.rxd_handler().set(m_scc, FUNC(scc85c30_device::rxa_w));
	rs232_host.dcd_handler().set(m_scc, FUNC(scc85c30_device::dcda_w));
	rs232_host.cts_handler().set(m_scc, FUNC(scc85c30_device::ctsa_w));

	RS232_PORT(config, "printer", in213_printer_devices, nullptr);

	// video
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_color(rgb_t::amber());
	m_screen->set_size(480, 234);
	m_screen->set_visarea_full();
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // not accurate
//  m_screen->set_raw(18.432_MHz_XTAL, 0, 0, 0, 0, 0, 0);
	m_screen->set_screen_update(FUNC(informer_213_state::screen_update));
	m_screen->screen_vblank().set(FUNC(informer_213_state::vblank_w));

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	GFXDECODE(config, "gfxdecode", m_palette, chars);

	// sound
	SPEAKER(config, "mono").front_center();
	BEEP(config, "beep", 500).add_route(ALL_OUTPUTS, "mono", 0.50); // frequency unknown

	informer_213_kbdintf_device &kbdintf(INFORMER_213_KBDINTF(config, "kbdintf"));
	kbdintf.int_handler().set(FUNC(informer_213_state::kbd_int_w));
	kbdintf.txd_handler().set("kbd", FUNC(keytronic_connector_device::ser_in_w));
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( in213 )
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_DEFAULT_BIOS("26")
	ROM_SYSTEM_BIOS(0,  "21",  "v2.1")
	// 79687-101  213 SNA 201C  CK=1C22 V2.1 (checksum matches)
	ROMX_LOAD("79687-101.bin", 0x0000, 0x8000, CRC(1ff023f3) SHA1(cbb027769d7744072045e60b020826f4f4bfe1b6), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1,  "26",  "v2.6")
	// 79687-305  PTF02 SNA  V2.6 CK=24EE (checksum matches)
	ROMX_LOAD("79687-305.bin", 0x0000, 0x8000, CRC(0638c6d6) SHA1(1906f835f255d595c5743b453614ba21acb5acae), ROM_BIOS(1))

	ROM_REGION(0x2000, "chargen", 0)
	// 79688-003  ICT 213/CG.  CK=C4E0 (checksum matches)
	// 79688-003  ICT 213/374  CK = C4E0 (checksum matches)
	ROM_LOAD("79688-003.bin", 0x0000, 0x2000, CRC(75e0da94) SHA1(c10c71fcf980a5f868a85bc264661183fa69fa72))
ROM_END

ROM_START( in213ae )
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_DEFAULT_BIOS("17")
	ROM_SYSTEM_BIOS(0,  "16",  "v1.6")
	// 79750-304-213AE_V1.6_CK-B1B8
	ROMX_LOAD("79750-304.bin", 0x0000, 0x8000, CRC(82ffe69e) SHA1(3803100aeb8f5e484bc9f4c533ef4f25223c9023), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1,  "17",  "v1.7")
	// 79750-305  213 AE V.32  CK=9D06 (checksum matches)
	ROMX_LOAD("79750-305.bin", 0x0000, 0x8000, CRC(322edc85) SHA1(7e8d95ef8550d133163ffc89dd75ed80883dee0c), ROM_BIOS(1))

	ROM_REGION(0x4000, "chargen", 0)
	// 79747-002  V.32 ME C.G.  V3.1 CK=D68C (checksum matches)
	ROM_LOAD("79747-002.bin", 0x0000, 0x4000, CRC(7425327f) SHA1(e3e67305b3b8936683724d1347a451fffe96bf0e))
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

COMP( 1990, in213  , 0, 0, informer_213, informer_213, informer_213_state, empty_init, "Informer Computer Terminals", "Informer 213",    MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1992, in213ae, 0, 0, informer_213, informer_213, informer_213_state, empty_init, "Informer Computer Terminals", "Informer 213 AE", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



innotv_innotabmax.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/******************************************************************************

   VTech InnoTV / InnoTab MAX
   'Android' based platforms

   The InnoTV outputs 1280x720, the InnoTab MAX uses the same cartridges
   (both also support games downloaded from VTech to the internal storage)

   These are NOT compatible with the original InnoTab 1/2/3, and although
   some games for the platform claims compatibility with the older platforms
   on the box, this is done through a download code rather than the cartridge
   being compatible.

   This file exists so that the Software List has a place to hook up to for
   the time being.

   InnoTV details

   Rockchip RK3168 (Main CPU / SoC)
   Rockchip RK616 ('Partner Chip for Rockchip mobile application processor'
   2x EtronTech EM6GE16EWXD-12H (RAM)
   Ricoh RC5T619 (Power Management)
   MicroSDHC 8GB card in internal slot
   Realtek RTL8188EU (Wireless)

   There don't appear to be any ROM / SPI / NAND devices onboard, so must either
   boot directly from the SD, or have some boot program internal to the SoC

   The following pinout was used for the InnoTV / InnoTab MAX cartridges

    +---------------------+
    |                     |--+
    |                     |--| I/O8
    |                     |--| I/O7
    |  +---------------+  |--| I/O6
    |  |||||||||||||||||  |--| I/O5
    |  |               |  |--| ?
    |  |     NAND      |  |--| I/O1
    |  |               |  |--| I/O2
    |  |               |  |--| I/O3
    |  |               |  |--| I/O4
    |  |TC58NVG1S3HTA00|  |--| GND
    |  |               |  |--| GND
    |  |               |  |--| CLE
    |  |               |  |--| ALE
    |  |               |  |--| WE
    |  |               |  |--| WP
    |  |               |  |--| VCC
    |  |||||||||||||||||  |--| VCC
    |  +---------------+  |--| CE
    |                     |--| RE
    |                     |--| RY/BY
    |                     |--+
    +---------------------+

*******************************************************************************/

#include "emu.h"

#include "cpu/arm7/arm7.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "screen.h"


namespace {

class vtech_innotv_innotabmax_state : public driver_device
{
public:
	vtech_innotv_innotabmax_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cart(*this, "cartslot")
		, m_screen(*this, "screen")
		, m_cart_region(nullptr)
	{ }

	void vtech_innotv_innotabmax(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void main_map(address_map &map) ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	required_device<cpu_device> m_maincpu;
	required_device<generic_slot_device> m_cart;
	required_device<screen_device> m_screen;
	memory_region *m_cart_region;
};



void vtech_innotv_innotabmax_state::machine_start()
{
	// if there's a cart, override the standard mapping
	if (m_cart && m_cart->exists())
	{
		m_cart_region = memregion(std::string(m_cart->tag()) + GENERIC_ROM_REGION_TAG);
	}
}

void vtech_innotv_innotabmax_state::machine_reset()
{
}

DEVICE_IMAGE_LOAD_MEMBER(vtech_innotv_innotabmax_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

static INPUT_PORTS_START( vtech_innotv_innotabmax )
INPUT_PORTS_END

uint32_t vtech_innotv_innotabmax_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void vtech_innotv_innotabmax_state::main_map(address_map &map)
{
}

void vtech_innotv_innotabmax_state::vtech_innotv_innotabmax(machine_config &config)
{
	ARM9(config, m_maincpu, 240000000); // unknown core type / frequency, but confirmed as ARM based
	m_maincpu->set_addrmap(AS_PROGRAM, &vtech_innotv_innotabmax_state::main_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(10));
	m_screen->set_size(1280, 720);
	m_screen->set_visarea(0, 1280-1, 0, 720-1);
	m_screen->set_screen_update(FUNC(vtech_innotv_innotabmax_state::screen_update));

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "vtech_innotv_innotabmax_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(vtech_innotv_innotabmax_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("vtech_innotv_cart");
}

ROM_START( innotv )
	DISK_REGION( "internalsd" )
	DISK_IMAGE( "8gb_sdhc_internal", 0, SHA1(443a0a9cc830387317d3218955b72295ee5a88eb) )
ROM_END

} // anonymous namespace


CONS( 2015, innotv,      0,         0,      vtech_innotv_innotabmax,    vtech_innotv_innotabmax,  vtech_innotv_innotabmax_state,  empty_init, "VTech", "InnoTV", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



instruct.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Signetics Instructor 50

2010-04-08 Skeleton driver.
2012-05-20 Connected digits, system boots. [Robbbert]
2012-05-20 Connected keyboard, system mostly usable. [Robbbert]
2013-10-15 Fixed various regressions. [Robbbert]

From looking at a blurry picture of it, this is what I can determine:
    - Left side: 8 toggle switches, with a round red led above each one.
    - Below this is the Port Address Switch with choice of 'Non-Extended', 'Extended' or 'Memory'.
    - To the right of this is another toggle switch labelled 'Interrupt', the
      choices are 'Direct' and 'Indirect'.
    - Above this switch are 2 more round red leds: FLAG and RUN.
    - Middle: a 4 down x3 across keypad containing the function keys. The
      labels (from left to right, top to bottom) are:
      SENS, WCAS, BKPT, INT, RCAS, REG, MON, STEP, MEM, RST, RUN, ENT/NXT.
    - Right side: a 4x4 hexadecimal keypad. The keys are:
      C, D, E, F, 8, 9, A, B, 4, 5, 6, 7, 0, 1, 2, 3
    - Above, extending from one side to the other is a metal plate with
      printed mnemonics. At the right edge are sockets to connect up the
      MIC and EAR cords to a cassette player.
    - At the back is a S100 interface.

Quick usage:
    - Look at memory: Press minus key. Enter an address. Press UP key to see the next.
    - Look at registers: Press R. Press 0. Press UP key to see the next.
    - Set PC register: Press R. Press C. Type in new address, Press UP.
    - Load a tape: Press L, enter file number (1 digit), press UP. On
      completion of a successful load, HELLO will be displayed.

Pasting a test program: (page 2-4 of the user manual, modified)
    - Paste this: QRF0^751120F005000620FA7EF97A84011F0003-0P
    - You should see the LEDs flashing as they count upwards.

ToDO:
    - Connect round led for Run.
    - Last Address Register
    - Initial Jump Logic
    - Single-step and Breakpoint don't stop execution because of the above.
    - The "Port Address Switch" which selects which of the 3 sources will
      be used for port_r and port_w. Currently all 3 are selected at once.

****************************************************************************/

#include "emu.h"

#include "cpu/s2650/s2650.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "speaker.h"
#include "video/pwm.h"

#include "instruct.lh"


namespace {

class instruct_state : public driver_device
{
public:
	instruct_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_p_ram(*this, "mainram")
		, m_p_smiram(*this, "smiram")
		, m_p_extram(*this, "extram")
		, m_cass(*this, "cassette")
		, m_display(*this, "display")
		, m_io_keyboard(*this, "X%u", 0U)
		, m_leds(*this, "led%u", 0U)
	{ }

	void instruct(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	uint8_t port_r();
	uint8_t portfc_r();
	uint8_t portfd_r();
	uint8_t portfe_r();
	int sense_r();
	void flag_w(int state);
	void port_w(uint8_t data);
	void portf8_w(uint8_t data);
	void portf9_w(uint8_t data);
	void portfa_w(uint8_t data);
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);
	INTERRUPT_GEN_MEMBER(t2l_int);
	void data_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	uint16_t m_lar = 0U;
	uint8_t m_digit = 0U;
	u8 m_seg = 0U;
	bool m_cassin = 0;
	bool m_irqstate = 0;
	required_device<s2650_device> m_maincpu;
	required_shared_ptr<uint8_t> m_p_ram;
	required_shared_ptr<uint8_t> m_p_smiram;
	required_shared_ptr<uint8_t> m_p_extram;
	required_device<cassette_image_device> m_cass;
	required_device<pwm_display_device> m_display;
	required_ioport_array<6> m_io_keyboard;
	output_finder<9> m_leds;
};

// flag led
void instruct_state::flag_w(int state)
{
	m_leds[8] = !state;
}

// user port
void instruct_state::port_w(uint8_t data)
{
	for (int i = 0; i < 8; i++)
		m_leds[i] = !BIT(data, i);
}

// cassette port
void instruct_state::portf8_w(uint8_t data)
{
	if (BIT(data, 4))
		m_cass->output(BIT(data, 3) ? -1.0 : +1.0);
	else
		m_cass->output(0.0);

	m_cassin = BIT(data, 7);
}

// segment output
void instruct_state::portf9_w(uint8_t data)
{
	m_seg = data;
	m_display->matrix(m_digit, m_seg);
}

// digit & keyrow-scan select
void instruct_state::portfa_w(uint8_t data)
{
	m_digit = data;
	m_display->matrix(m_digit, m_seg);
}

// user switches
uint8_t instruct_state::port_r()
{
	return ioport("USW")->read();
}

// last address register A0-7 copied to 17E9 at boot
uint8_t instruct_state::portfc_r()
{
	return m_lar;
}

// last address register A8-14 copied to 17E8 at boot
uint8_t instruct_state::portfd_r()
{
	return (m_lar >> 8) & 0x7f;
}

// read keyboard
uint8_t instruct_state::portfe_r()
{
	u8 data = 15;

	for (uint8_t i = 0; i < 6; i++)
		if (BIT(m_digit, i))
			data &= m_io_keyboard[i]->read();

	return data;
}


// Read cassette and SENS key
int instruct_state::sense_r()
{
	if (m_cassin)
		return (m_cass->input() > 0.03) ? 1 : 0;
	else
		return BIT(ioport("HW")->read(), 0);
}

INTERRUPT_GEN_MEMBER( instruct_state::t2l_int )
{
	uint8_t hwkeys = ioport("HW")->read();

	// check RST key
	if (BIT(hwkeys, 3))
	{
		m_maincpu->set_state_int(S2650_PC, 0);
		return;
	}
	else
	// check MON key
	if (BIT(hwkeys, 2))
	{
		m_maincpu->set_state_int(S2650_PC, 0x1800);
		return;
	}
	else
	{
		uint8_t switches = ioport("SW")->read();

		// Check INT sw & key
		if (BIT(switches, 1))
			device.execute().set_input_line(0, BIT(hwkeys, 1) ? ASSERT_LINE : CLEAR_LINE);
		else
		// process ac input
		{
			m_irqstate ^= 1;
			device.execute().set_input_line(0, m_irqstate ? ASSERT_LINE : CLEAR_LINE);
		}
	}
}

void instruct_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0ffe).ram().share("mainram");
	map(0x0fff, 0x0fff).rw(FUNC(instruct_state::port_r), FUNC(instruct_state::port_w));
	map(0x1780, 0x17ff).ram().share("smiram");
	map(0x1800, 0x1fff).rom().region("roms", 0);
	map(0x2000, 0x7fff).ram().share("extram");
}

void instruct_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x07, 0x07).rw(FUNC(instruct_state::port_r), FUNC(instruct_state::port_w));
	map(0xf8, 0xf8).w(FUNC(instruct_state::portf8_w));
	map(0xf9, 0xf9).w(FUNC(instruct_state::portf9_w));
	map(0xfa, 0xfa).w(FUNC(instruct_state::portfa_w));
	map(0xfc, 0xfc).r(FUNC(instruct_state::portfc_r));
	map(0xfd, 0xfd).r(FUNC(instruct_state::portfd_r));
	map(0xfe, 0xfe).r(FUNC(instruct_state::portfe_r));
}

void instruct_state::data_map(address_map &map)
{
	map.unmap_value_high();
	map(S2650_DATA_PORT, S2650_DATA_PORT).rw(FUNC(instruct_state::port_r), FUNC(instruct_state::port_w));
}

/* Input ports */
static INPUT_PORTS_START( instruct )
	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')

	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')

	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("X4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("WCAS") PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RCAS") PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("STEP") PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RUN") PORT_CODE(KEYCODE_X) PORT_CHAR('X')

	PORT_START("X5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BKPT") PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("REG") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MEM") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENT/NXT") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')

	PORT_START("HW")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SENS") PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("INT") PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MON") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RST") PORT_CODE(KEYCODE_P) PORT_CHAR('P')

	PORT_START("SW")
	PORT_DIPNAME( 0x01, 0x00, "INT") // Interrupt jumps to 0007 or *0007
	PORT_DIPSETTING(    0x01, "Indirect")
	PORT_DIPSETTING(    0x00, "Direct")
	PORT_DIPNAME( 0x02, 0x00, "AC/INT") // Interrupt comes from INT key or from power supply
	PORT_DIPSETTING(    0x02, "INT")
	PORT_DIPSETTING(    0x00, "AC")

	PORT_START("USW")
	PORT_DIPNAME( 0x01, 0x00, "Switch A") PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(    0x01, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x02, 0x02, "Switch B") PORT_DIPLOCATION("SW1:2")
	PORT_DIPSETTING(    0x02, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x04, 0x04, "Switch C") PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(    0x04, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x08, 0x00, "Switch D") PORT_DIPLOCATION("SW1:4")
	PORT_DIPSETTING(    0x08, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x10, 0x00, "Switch E") PORT_DIPLOCATION("SW1:5")
	PORT_DIPSETTING(    0x10, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x20, 0x20, "Switch F") PORT_DIPLOCATION("SW1:6")
	PORT_DIPSETTING(    0x20, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x40, 0x40, "Switch G") PORT_DIPLOCATION("SW1:7")
	PORT_DIPSETTING(    0x40, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x80, 0x00, "Switch H") PORT_DIPLOCATION("SW1:8")
	PORT_DIPSETTING(    0x80, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
INPUT_PORTS_END


void instruct_state::machine_reset()
{
	m_irqstate = 0;
	m_cassin = 0;
	port_w(0); // turn round leds off
	m_maincpu->set_state_int(S2650_PC, 0x1800);
}

void instruct_state::machine_start()
{
	m_leds.resolve();

	save_item(NAME(m_lar));
	save_item(NAME(m_digit));
	save_item(NAME(m_seg));
	save_item(NAME(m_cassin));
	save_item(NAME(m_irqstate));
}

QUICKLOAD_LOAD_MEMBER(instruct_state::quickload_cb)
{
	int const quick_length = image.length();
	if (quick_length < 0x0100)
		return std::make_pair(image_error::INVALIDLENGTH, "File too short (must be at least 256 bytes)");
	else if (quick_length > 0x8000)
		return std::make_pair(image_error::INVALIDLENGTH, "File too long (must be no more than 32K)");

	std::vector<uint8_t> quick_data(quick_length);
	uint16_t read_ = image.fread(&quick_data[0], quick_length);
	if (read_ != quick_length)
		return std::make_pair(image_error::UNSPECIFIED, "Cannot read file");
	else if (quick_data[0] != 0xc5)
		return std::make_pair(image_error::INVALIDIMAGE, "Invalid header");

	uint16_t const exec_addr = quick_data[1] * 256 + quick_data[2];
	if (exec_addr >= quick_length)
	{
		return std::make_pair(
				image_error::INVALIDIMAGE,
				util::string_format("Exec address %04X beyond end of file %04X", exec_addr, quick_length));
	}

	// load to 0000-0FFE (standard ram + extra)
	read_ = std::min<uint16_t>(quick_length, 0xfff);
	m_p_ram[0] = 0x1f;  // add jump for RST key
	for (uint16_t i = 1; i < read_; i++)
		m_p_ram[i] = quick_data[i];

	// load to 1780-17BF (spare ram inside 2656)
	if (quick_length > 0x1780)
	{
		read_ = std::min<uint16_t>(quick_length, 0x17c0);
		for (uint16_t i = 0x1780; i < read_; i++)
			m_p_smiram[i-0x1780] = quick_data[i];
	}

	// put start address into PC so it can be debugged
	m_p_smiram[0x68] = m_p_ram[1];
	m_p_smiram[0x69] = m_p_ram[2];

	// load to 2000-7FFF (optional extra RAM)
	if (quick_length > 0x2000)
	{
		for (uint16_t i = 0x2000; i < quick_length; i++)
			m_p_extram[i-0x2000] = quick_data[i];
	}

	// display a message about the loaded quickload
	image.message(" Quickload: size=%04X : exec=%04X", quick_length, exec_addr);

	// Start the quickload - JP exec_addr
	m_maincpu->set_state_int(S2650_PC, 0);

	return std::make_pair(std::error_condition(), std::string());
}

void instruct_state::instruct(machine_config &config)
{
	/* basic machine hardware */
	S2650(config, m_maincpu, XTAL(3'579'545) / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &instruct_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &instruct_state::io_map);
	m_maincpu->set_addrmap(AS_DATA, &instruct_state::data_map);
	m_maincpu->set_periodic_int(FUNC(instruct_state::t2l_int), attotime::from_hz(120));
	m_maincpu->sense_handler().set(FUNC(instruct_state::sense_r));
	m_maincpu->flag_handler().set(FUNC(instruct_state::flag_w));
	// Set vector from INDIRECT sw
	m_maincpu->intack_handler().set([this]() { return BIT(ioport("SW")->read(), 0) ? 0x87 : 0x07; });

	/* video hardware */
	config.set_default_layout(layout_instruct);
	PWM_DISPLAY(config, m_display).set_size(8, 8);
	m_display->set_segmask(0xff, 0xff);

	/* quickload */
	QUICKLOAD(config, "quickload", "pgm", attotime::from_seconds(1)).set_load_callback(FUNC(instruct_state::quickload_cb));

	SPEAKER(config, "mono").front_center();

	/* cassette */
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
}

/* ROM definition */
ROM_START( instruct )
	ROM_REGION( 0x0800, "roms", 0 )
	ROM_LOAD( "instruct.rom", 0x0000, 0x0800, CRC(131715a6) SHA1(4930b87d09046113ab172ba3fb31f5e455068ec7) )

	ROM_REGION( 0x8020, "proms", 0 )
	ROM_LOAD( "82s123.33",    0x0000, 0x0020, CRC(b7aecef0) SHA1(b39fb35e8b6ab67b31f8f310fd5d56304bcd4123) )
	ROM_LOAD( "82s103.20",    0x0020, 0x8000, NO_DUMP )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY      FULLNAME                   FLAGS
COMP( 1978, instruct, 0,      0,      instruct, instruct, instruct_state, empty_init, "Signetics", "Signetics Instructor 50", MACHINE_SUPPORTS_SAVE )



intchess.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger, Achim
/*******************************************************************************

Intelligent Games / SciSys Intelligent Chess

Developed by Intelligent Games, the same group of people that worked on SciSys
Super System III and Mark V. Manufactured by SciSys, their main business partner
at the time. The visual interface is an evolution of "Tolinka".

It was advertised in a 1980 brochure by SciSys, but it looks like SciSys didn't
sell this chess computer. It was marketed by Intelligent Games themselves.
The UK version wasn't widely released, the German version was more common.

Hardware notes:
- PCB label: INTELLIGENT GAMES Ltd, (C) 1980, IG3
- Synertek 6502A @ ~1.1MHz
- Synertek 6522 VIA
- 2*4KB ROM(Synertek 2332), 2KB RAM(4*M5L2114LP)
- 256 bytes PROM(MMI 6336-1J), 256x4 VRAM(2101-1), RF video
- MM74C923N keyboard encoder, 20 buttons
- cassette deck with microphone
- 4-digit 7seg display

TODO:
- colors are estimated from photos (black and white are obvious, but the green
  and cyan are not standard 0x00ff00 / 0x00ffff)
- video timing is unknown, sprite offsets are estimated from photos

*******************************************************************************/

#include "emu.h"

#include "cpu/m6502/m6502.h"
#include "imagedev/cassette.h"
#include "machine/6522via.h"
#include "machine/mm74c922.h"
#include "machine/timer.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

// internal artwork
#include "intchess.lh"


namespace {

class intchess_state : public driver_device
{
public:
	intchess_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_via(*this, "via"),
		m_encoder(*this, "encoder"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_vram(*this, "vram"),
		m_gfxdecode(*this, "gfxdecode"),
		m_screen(*this, "screen"),
		m_palette(*this, "palette"),
		m_cass(*this, "cassette")
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(reset_button);

	void intchess(machine_config &config);

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<via6522_device> m_via;
	required_device<mm74c923_device> m_encoder;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_shared_ptr<u8> m_vram;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<cassette_image_device> m_cass;

	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void seg_w(u8 data);
	void control_w(u8 data);
	u8 control_r();

	void init_palette(palette_device &palette) const;
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void vram_w(offs_t offset, u8 data);

	TIMER_DEVICE_CALLBACK_MEMBER(cass_input);
};

INPUT_CHANGED_MEMBER(intchess_state::reset_button)
{
	// assume that reset button is tied to 6502/6522
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
	if (newval)
		m_via->reset();
}



/*******************************************************************************
    Video
*******************************************************************************/

void intchess_state::init_palette(palette_device &palette) const
{
	palette.set_pen_color(0, 0xb0, 0xd0, 0xff); // cyan
	palette.set_pen_color(1, 0x00, 0x00, 0x00); // black
	palette.set_pen_color(2, 0x88, 0xa8, 0x50); // green
	palette.set_pen_color(3, 0xff, 0xff, 0xff); // white
}

u32 intchess_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	// draw chessboard background
	for (int y = cliprect.top(); y <= cliprect.bottom(); y++)
		for (int x = cliprect.left(); x <= cliprect.right(); x++)
			bitmap.pix(y, x) = ((x / 20) ^ (y / 16)) << 1 & 2;

	// draw the sprites
	for (int i = 0; i < 64; i++)
	{
		int code = m_vram[i] & 7;
		int color = m_vram[i] >> 3 & 1;
		int x = (i % 8) * 20 + 2;
		int y = (i / 8) * 16;

		m_gfxdecode->gfx(0)->transpen(bitmap, cliprect, code, color, 0, 0, x, y, 0);
	}

	return 0;
}

void intchess_state::vram_w(offs_t offset, u8 data)
{
	// d0-d2: sprite index
	// d3: color
	// d4-d7: N/C (4-bit RAM chip)
	m_vram[offset] = data & 0xf;
}



/*******************************************************************************
    I/O
*******************************************************************************/

void intchess_state::seg_w(u8 data)
{
	// PA1-PA7: 7seg data
	// PA0: ?
	m_display->write_mx(bitswap<8>(~data,0,1,2,3,4,5,6,7));
}

void intchess_state::control_w(u8 data)
{
	// PB0-PB3: digit select
	m_display->write_my(data & 0xf);

	// PB5-PB7 to cassette deck
	// PB5: speaker
	m_dac->write(BIT(data, 5));

	// PB6: ?
	// PB7: cassette output
	m_cass->output(BIT(data, 7) ? +1.0 : -1.0);
}

u8 intchess_state::control_r()
{
	// PB4: 74C923 data available
	return m_encoder->da_r() ? 0x10 : 0x00;
}

TIMER_DEVICE_CALLBACK_MEMBER(intchess_state::cass_input)
{
	// cassette input is tied to NMI
	bool state = ((m_cass->get_state() & CASSETTE_MASK_UISTATE) == CASSETTE_PLAY) && (m_cass->input() < -0.04);
	m_maincpu->set_input_line(INPUT_LINE_NMI, state ? ASSERT_LINE : CLEAR_LINE);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void intchess_state::main_map(address_map &map)
{
	map(0x0000, 0x03ff).mirror(0x0400).ram();
	map(0x0800, 0x0bff).mirror(0x0400).ram();
	map(0x1000, 0x1000).r(m_encoder, FUNC(mm74c923_device::read));
	map(0x1800, 0x18ff).ram().w(FUNC(intchess_state::vram_w)).share("vram");
	map(0xa800, 0xa80f).m(m_via, FUNC(via6522_device::map));
	map(0xc000, 0xdfff).mirror(0x2000).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( intchess ) // see comments for German version labels
	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("A 1 / Pawn")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("E 5 / Queen")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level / Clear Square")                   // Spielstärke / Feld Frei
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Clear")   // Löschen
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Flash")

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("B 2 / Knight")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("F 6 / King")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game / Clear Board")                 // Neue Partie / Brett Frei
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter") // Eingabe
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back") // Zurück

	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("C 3 / Bishop")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("G 7 / White")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Mode")      // Modus
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Find")      // Check
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Next Best") // Altern

	PORT_START("X4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("D 4 / Rook")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("H 8 / Black")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("Record")    // Speichern
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("Place")     // Setzen
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Step")      // Vor

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(intchess_state::reset_button), 0) PORT_NAME("Reset") // Start
INPUT_PORTS_END



/*******************************************************************************
    GFX Layouts
*******************************************************************************/

static const gfx_layout sprite_layout =
{
	16,16,
	RGN_FRAC(1,1),
	1,
	{ RGN_FRAC(0,1) },
	{ STEP8(8*16,1), STEP8(0,1) },
	{ STEP16(0,1*8) },
	16*16
};

static GFXDECODE_START( gfx_intchess )
	GFXDECODE_ENTRY( "sprites", 0, sprite_layout, 0, 2 )
GFXDECODE_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void intchess_state::intchess(machine_config &config)
{
	// basic machine hardware
	M6502(config, m_maincpu, 4.433619_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &intchess_state::main_map);

	MOS6522(config, m_via, 4.433619_MHz_XTAL / 4); // DDRA = 0xff, DDRB = 0xef
	m_via->writepa_handler().set(FUNC(intchess_state::seg_w));
	m_via->writepb_handler().set(FUNC(intchess_state::control_w));
	m_via->readpb_handler().set(FUNC(intchess_state::control_r));
	m_via->irq_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	MM74C923(config, m_encoder, 0); // timing parameters unknown
	m_encoder->da_wr_callback().set(m_via, FUNC(via6522_device::write_ca2));
	m_encoder->x1_rd_callback().set_ioport("X1");
	m_encoder->x2_rd_callback().set_ioport("X2");
	m_encoder->x3_rd_callback().set_ioport("X3");
	m_encoder->x4_rd_callback().set_ioport("X4");
	m_encoder->data_tri_callback().set(m_encoder, FUNC(mm74c923_device::read));

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50); // PAL
	m_screen->set_size(8*20 + 32, 8*16 + 32);
	m_screen->set_visarea(0, 8*20-1, 0, 8*16-1);
	m_screen->set_screen_update(FUNC(intchess_state::screen_update));
	m_screen->set_palette(m_palette);
	m_screen->screen_vblank().set(m_via, FUNC(via6522_device::write_cb2));

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_intchess);
	PALETTE(config, m_palette, FUNC(intchess_state::init_palette), 4);

	PWM_DISPLAY(config, m_display).set_size(4, 8);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_intchess);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);

	// cassette
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "speaker", 0.05);
	TIMER(config, "cass_input").configure_periodic(FUNC(intchess_state::cass_input), attotime::from_usec(10));
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( intchess )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("c45015_ytv-lrom.u9", 0xc000, 0x1000, CRC(eef04467) SHA1(5bdcb8d596b91aa06c6ef1ed53ef14d0d13f4194) ) // 2332
	ROM_LOAD("c45016_ytv-hrom.u8", 0xd000, 0x1000, CRC(7e6f85b4) SHA1(4cd15257eae04067160026f9a062a28581f46227) ) // "

	ROM_REGION( 0x100, "sprites", 0 )
	ROM_LOAD("igp.u15", 0x000, 0x100, CRC(bf8358e0) SHA1(880e0d9bd8a75874ba9e51dfb5999b8fcd321a4f) ) // 6336-1
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1980, intchess, 0,      0,      intchess, intchess, intchess_state, empty_init, "Intelligent Games / SciSys", "Intelligent Chess", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_COLORS | MACHINE_IMPERFECT_GRAPHICS )



inteladv.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    VTech Intelligence Advance E/R Lerncomputer

***************************************************************************/

#include "emu.h"
#include "cpu/m6502/st2204.h"
#include "cpu/m6502/st2205u.h"


namespace {

class inteladv_state : public driver_device
{
public:
	inteladv_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void inteladv(machine_config &config);
	void dyndesk(machine_config &config);

	void st2205u_power_w(int state);
	void st2202_power_w(int state);

private:
	void inteladv_map(address_map &map) ATTR_COLD;
	void dyndesk_map(address_map &map) ATTR_COLD;

	required_device<st2xxx_device> m_maincpu;
};

void inteladv_state::inteladv_map(address_map &map)
{
	map(0x000000, 0x7fffff).rom().region("maincpu", 0);
}

void inteladv_state::dyndesk_map(address_map &map)
{
	map(0x000000, 0x1fffff).rom().region("maincpu", 0).mirror(0x400000); // why mirrored?
	map(0x800000, 0x807fff).ram();
}

void inteladv_state::st2205u_power_w(int state)
{
	if (!state)
		m_maincpu->set_state_int(st2xxx_device::ST_IREQ, m_maincpu->state_int(st2xxx_device::ST_IREQ) | 0x0020);
}

void inteladv_state::st2202_power_w(int state)
{
	if (!state)
		m_maincpu->set_state_int(st2xxx_device::ST_IREQ, m_maincpu->state_int(st2xxx_device::ST_IREQ) | 0x0010);
}

static INPUT_PORTS_START(inteladv)
	PORT_START("POWER")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_POWER_ON) PORT_WRITE_LINE_MEMBER(FUNC(inteladv_state::st2205u_power_w))
INPUT_PORTS_END

static INPUT_PORTS_START(dyndesk)
	PORT_START("POWER")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_POWER_ON) PORT_WRITE_LINE_MEMBER(FUNC(inteladv_state::st2202_power_w))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_START)
INPUT_PORTS_END

void inteladv_state::inteladv(machine_config &config)
{
	ST2205U(config, m_maincpu, 4000000);
	m_maincpu->set_addrmap(AS_DATA, &inteladv_state::inteladv_map);
	m_maincpu->in_pa_callback().set_ioport("POWER");
}

void inteladv_state::dyndesk(machine_config &config)
{
	ST2202(config, m_maincpu, 4000000);
	m_maincpu->set_addrmap(AS_DATA, &inteladv_state::dyndesk_map);
	m_maincpu->in_pa_callback().set_ioport("POWER");
}

ROM_START( inteladv )
	ROM_REGION( 0x800000, "maincpu", 0 )
	ROM_LOAD( "vtechinteladv.bin", 0x000000, 0x800000, CRC(e24dbbcb) SHA1(7cb7f25f5eb123ae4c46cd4529aafd95508b2210) )
ROM_END

ROM_START( dyndesk )
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_LOAD( "27-07710-000.u9", 0x000000, 0x200000, CRC(092b0303) SHA1(e3a58cac9b0a1c68f1bdb5ea0af0b0dd223fb340) )
	// PCB also has Hynix HY62WT081ED70C (32Kx8 CMOS SRAM) at U2
ROM_END

ROM_START( cars2lap )
	ROM_REGION( 0x200000, "maincpu", 0 )
	// Flash dump contains some 65C02 code starting at $000D6A, but mostly appears to be custom bytecode or non-executable data banked in 32K blocks
	ROM_LOAD("n25s16.u6", 0x00000, 0x200000, CRC(ec1ba96e) SHA1(51b8844ae77adf20f74f268d380d268c9ce19785))
ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY  FULLNAME                              FLAGS
COMP( 2005, inteladv, 0,      0,      inteladv, inteladv, inteladv_state, empty_init, "VTech", "Intelligence Advance E/R (Germany)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 2003, dyndesk,  0,      0,      dyndesk,  dyndesk,  inteladv_state, empty_init, "VTech", "DynamiDesk (Germany)",               MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 2012, cars2lap, 0,      0,      dyndesk,  dyndesk,  inteladv_state, empty_init, "VTech", "CARS 2 Laptop (Germany)",            MACHINE_NO_SOUND | MACHINE_NOT_WORKING ) // probably doesn't belong here



intellec4.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Vas Crabb
/*
 Intel INTELLEC® 4

 The MOD 4 system has a 4004/4008/4009 chipset.  It has front panel
 switches for holding or pulsing the CPU's TEST line, and a CPU LED
 driven by clock phase 2.

 The MOD 40 system has a 4040/4289 chipset.  It replaces the TEST
 switches with STOP and SINGLE STEP switches, and replaces the CPU LED
 with a RUN LED that's lit when the CPU isn't acknowledging a STOP or
 HALT condition.

 The MOD 4 and MOD 40 systems use the same monitor PROM.  The monitor
 program doesn't use any 4040-specific features, and the console RAM
 readback feature avoids the need for the RPM instruction by latching
 the data and reading it through the ROM port space.

 Strictly speaking, the INTELLEC® 4 and INTELLEC® 4/MOD 4 are different
 machines.  The former uses an earlier control board without support
 for MOD 40 features; the latter uses the same control board as the
 MOD 40 jumpered to provide TEST line control rather than 4040 STOP
 and SINGLE STEP features.

 The MOD 40 repurposes the pins originally used for ROM 1 input port
 lines to expose stop/interrupt request/acknowledge.  It's pretty
 obvious that the MOD 40 was designed as a minimal modification.  A
 set of X1 display LEDs would have been fantastic, as it would show the
 contents of the accumulator with the 4040 CPU, while the X3 LEDs really
 aren't very useful.  However, since the 4004 just echoes M2 during X1,
 the control board has no provision for latching data during this cycle
 and would have required significant layout changes to cater for this.

 Set the terminal for 110 1/8/N/2 to talk to the monitor.
 It only accepts uppercase letters, digits, comma, and carriage return.
 Paper tape reader run/stop is sent to RTS on the serial port.

 TODO:
 * Expose general-purpose I/O?
 */
#include "emu.h"

#include "imm6_76.h"

#include "bus/intellec4/intellec4.h"
#include "bus/rs232/rs232.h"
#include "cpu/mcs40/mcs40.h"
#include "machine/bankdev.h"

#include "intlc44.lh"
#include "intlc440.lh"


namespace {

/***********************************************************************
    INTELLEC® 4 base driver
***********************************************************************/

class intellec4_state : public driver_device
{
public:
	void bus_cycle(mcs40_cpu_device_base::phase step, u8 sync, u8 data);

	u8 pm_read(offs_t offset);
	void pm_write(offs_t offset, u8 data);

	u8 rom0_in();
	u8 rom2_in();
	u8 rom3_in();
	u8 rome_in();
	u8 romf_in();

	void rom0_out(u8 data);
	void rom1_out(u8 data);
	void rom2_out(u8 data);
	void rom3_out(u8 data);
	void rome_out(u8 data);
	void romf_out(u8 data);

	void ram0_out(u8 data);
	void ram1_out(u8 data);

	// universal slot outputs
	void bus_reset_4002(int state);
	void bus_user_reset(int state);

	// front panel switches
	DECLARE_INPUT_CHANGED_MEMBER(sw_reset);
	DECLARE_INPUT_CHANGED_MEMBER(sw_reset_mode);
	template <unsigned N> DECLARE_INPUT_CHANGED_MEMBER(sw_prg_mode);
	DECLARE_INPUT_CHANGED_MEMBER(sw_run);
	DECLARE_INPUT_CHANGED_MEMBER(sw_next_inst);
	DECLARE_INPUT_CHANGED_MEMBER(sw_decr);
	DECLARE_INPUT_CHANGED_MEMBER(sw_incr);
	DECLARE_INPUT_CHANGED_MEMBER(sw_load);
	DECLARE_INPUT_CHANGED_MEMBER(sw_cma_enable);
	DECLARE_INPUT_CHANGED_MEMBER(sw_cma_write);
	DECLARE_INPUT_CHANGED_MEMBER(sw_prgm_pwr);
	DECLARE_INPUT_CHANGED_MEMBER(sw_do_enable);

protected:
	intellec4_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "maincpu")
		, m_bus(*this, "bus")
		, m_prg_ram(*this, "ram")
		, m_sw_mode(*this, "MODE")
		, m_program_banks(*this, "prgbank")
		, m_rom_port_banks(*this, "rpbank")
		, m_prom_programmer(*this, "promprg")
		, m_tty(*this, "tty")
		, m_memory(*this, "memory"), m_status(*this, "status")
		, m_sw_control(*this, "CONTROL")
		, m_sw_addr_data(*this, "ADDRDAT")
		, m_sw_passes(*this, "PASSES")
		, m_sw_prom_prgm(*this, "PROM")
		, m_led_address(*this, "led_address_a%u_%u", 1U, 0U)
		, m_led_instruction(*this, "led_instruction_m%u_%u", 1U, 0U)
		, m_led_active_bank(*this, "led_active_bank_%u", 0U)
		, m_led_execution(*this, "led_execution_x%u_%u", 2U, 0U)
		, m_led_last_ptr(*this, "led_last_ptr_x%u_%u", 2U, 0U)
		, m_led_status_ptr_valid(*this, "led_status_ptr_valid")
		, m_led_status_search(*this, "led_status_search")
		, m_led_mode_mon(*this, "led_mode_mon")
		, m_led_mode_ram(*this, "led_mode_ram")
		, m_led_mode_prom(*this, "led_mode_prom")
	{
	}

	virtual void driver_start() override;
	virtual void driver_reset() override;

	void intellec4_program_banks(address_map &map) ATTR_COLD;
	void intellec4_rom_port_banks(address_map &map) ATTR_COLD;

	void intellec4_rom(address_map &map) ATTR_COLD;
	void intellec4_ram_memory(address_map &map) ATTR_COLD;
	void intellec4_rom_ports(address_map &map) ATTR_COLD;
	void intellec4_ram_status(address_map &map) ATTR_COLD;
	void intellec4_ram_ports(address_map &map) ATTR_COLD;
	void intellec4_program_memory(address_map &map) ATTR_COLD;

	void intellec4(machine_config &config);

	required_device<mcs40_cpu_device_base>              m_cpu;
	required_device<bus::intellec4::univ_bus_device>    m_bus;
	required_shared_ptr<u8>                             m_prg_ram;
	required_ioport                                     m_sw_mode;

private:
	enum
	{
		BANK_PRG_MON = 0,
		BANK_PRG_PROM,
		BANK_PRG_RAM,
		BANK_PRG_NONE,

		BANK_IO_MON = 0,
		BANK_IO_PROM,
		BANK_IO_NEITHER,

		BIT_SW_RESET = 2,
		BIT_SW_RESET_MODE,
		BIT_SW_MON,
		BIT_SW_RAM,
		BIT_SW_PROM,

		BIT_SW_RUN = 0,
		BIT_SW_NEXT_INST,
		BIT_SW_DECR,
		BIT_SW_INCR,
		BIT_SW_LOAD,
		BIT_SW_CMA_ENABLE,
		BIT_SW_CMA_WRITE,

		BIT_SW_PRGM_PWR = 0,
		BIT_SW_DATA_OUT_ENABLE
	};

	TIMER_CALLBACK_MEMBER(reset_expired);

	// LED update helpers
	void display_address(u16 value, u16 mask);
	void display_instruction(u8 value, u8 mask);
	void display_active_bank(u8 value);
	void display_execution(u8 value, u8 mask);
	void display_pointer(u8 value, u8 mask);

	void trigger_reset();
	void check_4002_reset();
	void reset_panel();

	required_device<address_map_bank_device>    m_program_banks, m_rom_port_banks;
	required_device<intel_imm6_76_device>       m_prom_programmer;
	required_device<rs232_port_device>          m_tty;

	required_shared_ptr<u8>     m_memory, m_status;
	required_ioport             m_sw_control, m_sw_addr_data, m_sw_passes;
	required_ioport             m_sw_prom_prgm;
	output_finder<3, 4>         m_led_address;
	output_finder<2, 4>         m_led_instruction;
	output_finder<4>            m_led_active_bank;
	output_finder<2, 4>         m_led_execution;
	output_finder<2, 4>         m_led_last_ptr;
	output_finder<>             m_led_status_ptr_valid;
	output_finder<>             m_led_status_search;
	output_finder<>             m_led_mode_mon;
	output_finder<>             m_led_mode_ram;
	output_finder<>             m_led_mode_prom;

	emu_timer   *m_reset_timer = nullptr;

	// program memory access
	u8      m_ram_page = 0U, m_ram_data = 0U;
	bool    m_ram_write = false;

	// PROM programmer
	u8      m_prom_addr = 0U, m_prom_data = 0U;

	// control board state
	bool    m_cpu_reset = false;
	bool    m_ff_prg_mode[3] = { false, false, false };

	// current state of signals from bus
	bool    m_bus_reset_4002 = false, m_bus_user_reset = false;

	// front panel state
	u16     m_latched_addr = 0U, m_display_addr = 0U;
	u8      m_display_instr = 0U, m_display_exec = 0U, m_display_ptr = 0U;
	u8      m_pass_counter = 0U;
	bool    m_panel_reset = false;
	bool    m_next_inst = false, m_adr_cmp_latch = false, m_search_complete = false;
	bool    m_src = false, m_pointer_valid = false;
	bool    m_cma_enable = false, m_cma_write = false;

	// current state of front panel switches
	bool    m_sw_reset = false, m_sw_reset_mode = false;
	bool    m_sw_prg_mode[3] = { false, false, false };
	bool    m_sw_run = false;
};


/*----------------------------------
  Common front panel switches
----------------------------------*/

INPUT_PORTS_START(intellec4)
	PORT_START("MODE")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW,  IPT_KEYPAD )             PORT_NAME("RESET")                                         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(intellec4_state::sw_reset),       0)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("RESET MODE")                                    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(intellec4_state::sw_reset_mode),  0)
	PORT_BIT( 0x0010, IP_ACTIVE_LOW,  IPT_KEYPAD )             PORT_NAME("MON")              PORT_CODE(KEYCODE_1_PAD)     PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(intellec4_state::sw_prg_mode<0>), 0)
	PORT_BIT( 0x0020, IP_ACTIVE_LOW,  IPT_KEYPAD )             PORT_NAME("RAM")              PORT_CODE(KEYCODE_2_PAD)     PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(intellec4_state::sw_prg_mode<1>), 0)
	PORT_BIT( 0x0040, IP_ACTIVE_LOW,  IPT_KEYPAD )             PORT_NAME("PROM")             PORT_CODE(KEYCODE_3_PAD)     PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(intellec4_state::sw_prg_mode<2>), 0)

	PORT_START("CONTROL")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("RUN")                                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(intellec4_state::sw_run),         0)
	PORT_BIT( 0x0002, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("NEXT INST")                                     PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(intellec4_state::sw_next_inst),   0)
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYPAD )             PORT_NAME("DECR")             PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(intellec4_state::sw_decr),        0)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYPAD )             PORT_NAME("INCR")             PORT_CODE(KEYCODE_PLUS_PAD)  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(intellec4_state::sw_incr),        0)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYPAD )             PORT_NAME("LOAD")             PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(intellec4_state::sw_load),        0)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("CMA ENABLE")                                    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(intellec4_state::sw_cma_enable),  0)
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYPAD )             PORT_NAME("CMA WRITE")        PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(intellec4_state::sw_cma_write),   0)

	PORT_START("ADDRDAT")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 0")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 1")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 2")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 3")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 4")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 5")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 6")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 7")
	PORT_BIT( 0x0100, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 8")
	PORT_BIT( 0x0200, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 9")
	PORT_BIT( 0x0400, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 10")
	PORT_BIT( 0x0800, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 11")

	PORT_START("PASSES")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("PASSES 0")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("PASSES 1")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("PASSES 2")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("PASSES 3")

	PORT_START("PROM")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("PRGM PROM PWR")                                 PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(intellec4_state::sw_prgm_pwr),    0)
	PORT_CONFNAME( 0x0002, 0x0002, "PROM PROGRAMMER DATA OUT ENABLE" )                                                    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(intellec4_state::sw_do_enable),   0)
	PORT_CONFSETTING(      0x0000, DEF_STR(Off) )
	PORT_CONFSETTING(      0x0002, DEF_STR(On)  )
INPUT_PORTS_END


/*----------------------------------
  System bus cycle handler
----------------------------------*/

void intellec4_state::bus_cycle(mcs40_cpu_device_base::phase step, u8 sync, u8 data)
{
	switch (step)
	{
	case mcs40_cpu_device_base::phase::A1:
		if (m_cma_enable)
		{
			display_address(m_latched_addr, 0x0fffU);
			display_instruction(m_prg_ram[m_latched_addr], 0xffU);
		}
		else if (!m_search_complete)
		{
			display_address(u16(data), 0x000fU);
			m_src = false;
		}
		if (m_cma_write)
		{
			m_prg_ram[m_latched_addr] = u8(~m_sw_addr_data->read() & 0x00ffU);
			m_cma_write = false;
		}
		break;
	case mcs40_cpu_device_base::phase::A2:
		if (!m_search_complete && !m_cma_enable)
			display_address(u16(data) << 4, 0x00f0U);
		break;
	case mcs40_cpu_device_base::phase::A3:
		if (!m_search_complete && !m_cma_enable)
		{
			display_address(u16(data) << 8, 0x0f00U);
			display_active_bank(~m_cpu->get_cm_ram());
		}
		break;
	case mcs40_cpu_device_base::phase::M1:
		if (!m_search_complete && !m_cma_enable)
			display_instruction(data << 4, 0xf0U);
		break;
	case mcs40_cpu_device_base::phase::M2:
		if (!m_search_complete && !m_cma_enable)
			display_instruction(data, 0x0fU);
		break;
	case mcs40_cpu_device_base::phase::X1:
		// not connected to anything
		break;
	case mcs40_cpu_device_base::phase::X2:
		if (!m_search_complete && !m_cma_enable)
		{
			display_execution(data << 4, 0xf0U);
			m_src = BIT(~m_cpu->get_cm_rom(), 0);
			if (m_src)
				display_pointer(data << 4, 0xf0U);
		}
		break;
	case mcs40_cpu_device_base::phase::X3:
		if (m_search_complete != m_adr_cmp_latch)
			m_led_status_search = m_search_complete = m_adr_cmp_latch;
		if (!m_search_complete && !m_cma_enable)
		{
			display_execution(data, 0x0fU);
			if (m_src)
			{
				display_pointer(data, 0x0fU);
				if (!m_panel_reset && !m_pointer_valid)
					m_led_status_ptr_valid = m_pointer_valid = true;
			}
		}
		if (!m_panel_reset && !m_adr_cmp_latch)
			m_adr_cmp_latch = (m_latched_addr == m_display_addr) && !((m_pass_counter ^ m_sw_passes->read()) & 0x0fU);
		if (!m_search_complete && !m_panel_reset && !m_cma_enable && !m_sw_run && (m_latched_addr == m_display_addr))
			m_pass_counter = (m_pass_counter + 1U) & 0x0fU;
		if (m_adr_cmp_latch && !m_next_inst && !m_search_complete)
			m_led_status_search = m_search_complete = true;
		if (!m_cpu_reset && !m_cma_enable && !m_sw_run)
			m_panel_reset = false;
		break;
	}
}


/*----------------------------------
  Program memory access handlers
----------------------------------*/

u8 intellec4_state::pm_read(offs_t offset)
{
	if (!machine().side_effects_disabled())
	{
		// always causes data to be latched
		u16 const addr((u16(m_ram_page) << 8) | ((offset >> 1) & 0x00ffU));
		m_ram_data = m_prg_ram[addr];
	}

	// the C outputs of the 4289 are always high for RPM/WPM so it's equivalent to romf_in
	return m_ram_data & 0x0fU;
}

void intellec4_state::pm_write(offs_t offset, u8 data)
{
	// always causes data to be latched
	u16 const addr((u16(m_ram_page) << 8) | ((offset >> 1) & 0x00ffU));
	m_ram_data = m_prg_ram[addr];
	if (m_ram_write)
	{
		bool const first(BIT(offset, 0));
		m_prg_ram[addr] = (m_ram_data & (first ? 0x0fU : 0xf0U)) | ((data & 0x0fU) << (first ? 4 : 0));
	}
}


/*----------------------------------
  I/O port handlers
----------------------------------*/

u8 intellec4_state::rom0_in()
{
	// bit 0 of this port is ANDed with the TTY input
	return m_tty->rxd_r() ? 0x0eU : 0x0fU;
}

u8 intellec4_state::rom2_in()
{
	// lower nybble of PROM programmer data
	return m_prom_programmer->do_r() & 0x0fU;
}

u8 intellec4_state::rom3_in()
{
	// upper nybble of PROM programmer data
	return (m_prom_programmer->do_r() >> 4) & 0x0fU;
}

u8 intellec4_state::rome_in()
{
	// upper nybble of RAM data latch
	return (m_ram_data >> 4) & 0x0fU;
}

u8 intellec4_state::romf_in()
{
	// lower nybble of RAM data latch
	return m_ram_data & 0x0fU;
}

void intellec4_state::rom0_out(u8 data)
{
	// lower nybble of PROM programmer address
	m_prom_addr = (m_prom_addr & 0xf0U) | (data & 0x0fU);
	m_prom_programmer->a_w(m_prom_addr);
}

void intellec4_state::rom1_out(u8 data)
{
	// upper nybble of PROM programmer address
	m_prom_addr = (m_prom_addr & 0x0fU) | ((data << 4) & 0xf0U);
	m_prom_programmer->a_w(m_prom_addr);
}

void intellec4_state::rom2_out(u8 data)
{
	// lower nybble of PROM programmer data
	m_prom_data = (m_prom_data & 0xf0U) | (data & 0x0fU);
	m_prom_programmer->di_w(m_prom_data);
}

void intellec4_state::rom3_out(u8 data)
{
	// upper nybble of PROM programmer data
	m_prom_data = (m_prom_data & 0x0fU) | ((data << 4) & 0xf0U);
	m_prom_programmer->di_w(m_prom_data);
}

void intellec4_state::rome_out(u8 data)
{
	// bit 0 of this port enables program memory write
	m_ram_write = BIT(data, 0);
}

void intellec4_state::romf_out(u8 data)
{
	// sets the program memory page for read/write operations
	m_ram_page = data & 0x0fU;
}

void intellec4_state::ram0_out(u8 data)
{
	// bit 0 of this port controls the TTY current loop
	m_tty->write_txd(BIT(data, 0));
}

void intellec4_state::ram1_out(u8 data)
{
	// bit 0 of this port controls the paper tape motor (0 = stop, 1 = run)
	m_tty->write_rts(BIT(~data, 0));

	// bits 1 and 2 of this port enable PROM write
	m_prom_programmer->r_w_a(BIT(~data, 1));
	m_prom_programmer->r_w(BIT(~data, 2));
}


/*----------------------------------
  Bus signal handlers
----------------------------------*/

void intellec4_state::bus_reset_4002(int state)
{
	m_bus_reset_4002 = 0 == state;
	check_4002_reset();
}

void intellec4_state::bus_user_reset(int state)
{
	if (!state)
		trigger_reset();
	m_bus_user_reset = 0 == state;
}


/*----------------------------------
  Front panel switch handlers
----------------------------------*/

INPUT_CHANGED_MEMBER(intellec4_state::sw_reset)
{
	if (!newval && oldval)
		trigger_reset();
	m_sw_reset = !bool(newval);
}

INPUT_CHANGED_MEMBER(intellec4_state::sw_reset_mode)
{
	m_sw_reset_mode = bool(newval);
	if (m_cpu_reset)
	{
		m_bus->reset_4002_in(m_sw_reset_mode ? 0 : 1);
		check_4002_reset();
	}
}

template <unsigned N> INPUT_CHANGED_MEMBER(intellec4_state::sw_prg_mode)
{
	static constexpr int prg_banks[3] = { BANK_PRG_MON, BANK_PRG_RAM, BANK_PRG_PROM };
	static constexpr int io_banks[3] = { BANK_IO_MON, BANK_IO_NEITHER, BANK_IO_PROM };

	std::tuple<output_finder<> &, output_finder<> &, output_finder<> &> mode_leds(m_led_mode_mon, m_led_mode_ram, m_led_mode_prom);

	if (oldval && !newval)
	{
		if (((0U == N) || !m_sw_prg_mode[0]) && ((1U == N) || !m_sw_prg_mode[1]) && ((2U == N) || !m_sw_prg_mode[2]))
		{
			if (!m_ff_prg_mode[N])
			{
				m_program_banks->set_bank(prg_banks[N]);
				m_rom_port_banks->set_bank(io_banks[N]);
				std::get<N>(mode_leds) = m_ff_prg_mode[N] = true;
			}
			trigger_reset();
		}
		else
		{
			m_program_banks->set_bank(BANK_PRG_NONE);
			m_rom_port_banks->set_bank(BANK_IO_NEITHER);
		}
		if ((0U != N) && m_ff_prg_mode[0])
			std::get<0>(mode_leds) = m_ff_prg_mode[0] = false;
		if ((1U != N) && m_ff_prg_mode[1])
			std::get<1>(mode_leds) = m_ff_prg_mode[1] = false;
		if ((2U != N) && m_ff_prg_mode[2])
			std::get<2>(mode_leds) = m_ff_prg_mode[2] = false;
	}
	m_sw_prg_mode[N] = !bool(newval);
}

INPUT_CHANGED_MEMBER(intellec4_state::sw_run)
{
	m_sw_run = !bool(newval);
	if (m_sw_run)
		reset_panel();
}

INPUT_CHANGED_MEMBER(intellec4_state::sw_next_inst)
{
	m_next_inst = !bool(newval);
}

INPUT_CHANGED_MEMBER(intellec4_state::sw_decr)
{
	// connected to a pulse generator circuit
	if (newval && !oldval)
	{
		m_latched_addr = (m_latched_addr - 1U) & 0x0fffU;
		reset_panel();
	}
}

INPUT_CHANGED_MEMBER(intellec4_state::sw_incr)
{
	// connected to a pulse generator circuit
	if (newval && !oldval)
	{
		m_latched_addr = (m_latched_addr + 1U) & 0x0fffU;
		reset_panel();
	}
}

INPUT_CHANGED_MEMBER(intellec4_state::sw_load)
{
	// connected to a pulse generator circuit
	if (newval && !oldval)
	{
		m_latched_addr = ~m_sw_addr_data->read() & 0x0fffU;
		reset_panel();
	}
}

INPUT_CHANGED_MEMBER(intellec4_state::sw_cma_enable)
{
	m_cma_enable = bool(newval);
	if (m_cma_enable)
		reset_panel();
}

INPUT_CHANGED_MEMBER(intellec4_state::sw_cma_write)
{
	if (newval && !oldval)
		m_cma_write = m_cma_enable;
}

INPUT_CHANGED_MEMBER(intellec4_state::sw_prgm_pwr)
{
	m_prom_programmer->prgm_prom_pwr(newval);
}

INPUT_CHANGED_MEMBER(intellec4_state::sw_do_enable)
{
	m_prom_programmer->data_out_enable(newval);
}


/*----------------------------------
  driver_device implementation
----------------------------------*/

void intellec4_state::driver_start()
{
	m_reset_timer = timer_alloc(FUNC(intellec4_state::reset_expired), this);

	m_led_address.resolve();
	m_led_instruction.resolve();
	m_led_active_bank.resolve();
	m_led_execution.resolve();
	m_led_last_ptr.resolve();
	m_led_status_ptr_valid.resolve();
	m_led_status_search.resolve();
	m_led_mode_mon.resolve();
	m_led_mode_ram.resolve();
	m_led_mode_prom.resolve();

	save_item(NAME(m_ram_page));
	save_item(NAME(m_ram_data));
	save_item(NAME(m_ram_write));

	save_item(NAME(m_prom_addr));
	save_item(NAME(m_prom_data));

	save_item(NAME(m_cpu_reset));
	save_item(NAME(m_ff_prg_mode));

	save_item(NAME(m_bus_reset_4002));
	save_item(NAME(m_bus_user_reset));

	save_item(NAME(m_latched_addr));
	save_item(NAME(m_display_addr));
	save_item(NAME(m_display_instr));
	save_item(NAME(m_display_exec));
	save_item(NAME(m_display_ptr));
	save_item(NAME(m_pass_counter));
	save_item(NAME(m_panel_reset));
	save_item(NAME(m_next_inst));
	save_item(NAME(m_adr_cmp_latch));
	save_item(NAME(m_search_complete));
	save_item(NAME(m_src));
	save_item(NAME(m_pointer_valid));
	save_item(NAME(m_cma_enable));
	save_item(NAME(m_cma_write));

	save_item(NAME(m_sw_reset));
	save_item(NAME(m_sw_reset_mode));
	save_item(NAME(m_sw_prg_mode));
	save_item(NAME(m_sw_run));

	m_ff_prg_mode[0] = true;
	m_ff_prg_mode[1] = m_ff_prg_mode[2] = false;
};

void intellec4_state::driver_reset()
{
	// set stuff according to initial state of front panel
	ioport_value const sw_mode(m_sw_mode->read()), sw_control(m_sw_control->read());
	m_sw_reset       = BIT(~sw_mode,    BIT_SW_RESET);
	m_sw_reset_mode  = BIT( sw_mode,    BIT_SW_RESET_MODE);
	m_sw_prg_mode[0] = BIT(~sw_mode,    BIT_SW_MON);
	m_sw_prg_mode[1] = BIT(~sw_mode,    BIT_SW_RAM);
	m_sw_prg_mode[2] = BIT(~sw_mode,    BIT_SW_PROM);
	m_sw_run         = BIT(~sw_control, BIT_SW_RUN);
	m_next_inst      = BIT(~sw_control, BIT_SW_NEXT_INST);
	m_cma_enable     = BIT( sw_control, BIT_SW_CMA_ENABLE);
	m_ff_prg_mode[0] = m_ff_prg_mode[0] && !m_sw_prg_mode[1] && !m_sw_prg_mode[2];
	m_ff_prg_mode[1] = m_ff_prg_mode[1] && !m_sw_prg_mode[0] && !m_sw_prg_mode[2];
	m_ff_prg_mode[2] = m_ff_prg_mode[2] && !m_sw_prg_mode[0] && !m_sw_prg_mode[1];
	m_panel_reset = m_panel_reset || m_cma_enable || m_sw_run;
	if (m_panel_reset)
	{
		m_pass_counter = 0U;
		m_adr_cmp_latch = false;
		m_search_complete = m_search_complete && m_next_inst;
		m_pointer_valid = false;
	}

	// ensure we're consistent with the state of the bus
	m_bus_reset_4002 = 0 == m_bus->reset_4002_out();
	m_bus_user_reset = 0 == m_bus->user_reset_out();

	// ensure device inputs are all in the correct state
	m_cpu->set_input_line(INPUT_LINE_RESET, m_cpu_reset ? ASSERT_LINE : CLEAR_LINE);
	m_bus->cpu_reset_in(m_cpu_reset ? 0 : 1);
	m_bus->reset_4002_in((m_cpu_reset && m_sw_reset_mode) ? 0 : 1);

	// set up the PROM programmer
	ioport_value const sw_prom_prgm(m_sw_prom_prgm->read());
	m_prom_programmer->data_out_enable(BIT(sw_prom_prgm, BIT_SW_DATA_OUT_ENABLE));
	m_prom_programmer->data_in_positive(1); // not jumpered in, onboard pullup
	m_prom_programmer->data_out_positive(0); // jumpered to ground
	m_prom_programmer->prgm_prom_pwr(BIT(sw_prom_prgm, BIT_SW_PRGM_PWR));

	// set front panel LEDs
	m_led_status_ptr_valid = m_pointer_valid;
	m_led_status_search    = m_search_complete;
	m_led_mode_mon         = m_ff_prg_mode[0];
	m_led_mode_ram         = m_ff_prg_mode[1];
	m_led_mode_prom        = m_ff_prg_mode[2];
}


/*----------------------------------
  System address spaces
----------------------------------*/

void intellec4_state::intellec4_program_banks(address_map &map)
{
	map.unmap_value_low();

	// 0x0000...0x0fff MON
	map(0x0000, 0x03ff).rom().region("monitor", 0x0000);

	// 0x1000...0x1fff PROM

	// 0x1000...0x1fff RAM
	map(0x2000, 0x2fff).readonly().share("ram");

	// 0x3000...0x3fff unmapped in case someone presses two mode switches at once
}

void intellec4_state::intellec4_rom_port_banks(address_map &map)
{
	map.unmap_value_high();

	// 0x0000...0x07ff MON
	map(0x0000, 0x000f).mirror(0x1f00).rw(FUNC(intellec4_state::rom0_in), FUNC(intellec4_state::rom0_out));
	map(0x0010, 0x001f).mirror(0x1f00).w(FUNC(intellec4_state::rom1_out));
	map(0x0020, 0x002f).mirror(0x1f00).rw(FUNC(intellec4_state::rom2_in), FUNC(intellec4_state::rom2_out));
	map(0x0030, 0x003f).mirror(0x1f00).rw(FUNC(intellec4_state::rom3_in), FUNC(intellec4_state::rom3_out));
	map(0x00e0, 0x00ef).mirror(0x1f00).rw(FUNC(intellec4_state::rome_in), FUNC(intellec4_state::rome_out));
	map(0x00f0, 0x00ff).mirror(0x1f00).rw(FUNC(intellec4_state::romf_in), FUNC(intellec4_state::romf_out));

	// 0x0800...0x0fff PROM

	// 0x1000...0x17ff neither

	// 0x1800...0x1fff unused
}


/*---------------------------------
  CPU views of address spaces
---------------------------------*/

void intellec4_state::intellec4_rom(address_map &map)
{
	map.unmap_value_low();
	map(0x0000, 0x0fff).m(m_program_banks, FUNC(address_map_bank_device::amap8));
}

void intellec4_state::intellec4_ram_memory(address_map &map)
{
	map.unmap_value_low();
	map(0x0000, 0x00ff).ram().share("memory"); // 4 * 4002
}

void intellec4_state::intellec4_rom_ports(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).m("rpbank", FUNC(address_map_bank_device::amap8));
}

void intellec4_state::intellec4_ram_status(address_map &map)
{
	map.unmap_value_low();
	map(0x0000, 0x003f).ram().share("status"); // 4 * 4002
}

void intellec4_state::intellec4_ram_ports(address_map &map)
{
	map(0x00, 0x00).w(FUNC(intellec4_state::ram0_out));
	map(0x01, 0x01).w(FUNC(intellec4_state::ram1_out));
}

void intellec4_state::intellec4_program_memory(address_map &map)
{
	map.unmap_value_low();
	map(0x0000, 0x01ff).rw(FUNC(intellec4_state::pm_read), FUNC(intellec4_state::pm_write));
}


/*----------------------------------
  Common machine configuration
----------------------------------*/

DEVICE_INPUT_DEFAULTS_START(terminal)
	DEVICE_INPUT_DEFAULTS("RS232_TXBAUD",    0x00ff, RS232_BAUD_110)
	DEVICE_INPUT_DEFAULTS("RS232_RXBAUD",    0x00ff, RS232_BAUD_110)
	DEVICE_INPUT_DEFAULTS("RS232_DATABITS",  0x00ff, RS232_DATABITS_8)
	DEVICE_INPUT_DEFAULTS("RS232_PARITY",    0x00ff, RS232_PARITY_NONE)
	DEVICE_INPUT_DEFAULTS("RS232_STOPBITS",  0x00ff, RS232_STOPBITS_2)
	DEVICE_INPUT_DEFAULTS("TERM_CONF",       0x0040, 0x0000) // auto CR on LF off
	DEVICE_INPUT_DEFAULTS("TERM_CONF",       0x0080, 0x0000) // auto LF on CR off
	DEVICE_INPUT_DEFAULTS("TERM_CONF",       0x0100, 0x0000) // local echo off
DEVICE_INPUT_DEFAULTS_END

DEVICE_INPUT_DEFAULTS_START(null_modem)
	DEVICE_INPUT_DEFAULTS("RS232_TXBAUD",    0x00ff, RS232_BAUD_110)
	DEVICE_INPUT_DEFAULTS("RS232_RXBAUD",    0x00ff, RS232_BAUD_110)
	DEVICE_INPUT_DEFAULTS("RS232_DATABITS",  0x00ff, RS232_DATABITS_8)
	DEVICE_INPUT_DEFAULTS("RS232_PARITY",    0x00ff, RS232_PARITY_NONE)
	DEVICE_INPUT_DEFAULTS("RS232_STOPBITS",  0x00ff, RS232_STOPBITS_2)
	DEVICE_INPUT_DEFAULTS("FLOW_CONTROL",    0x0007, 0x0000) // no flow control
DEVICE_INPUT_DEFAULTS_END

void intellec4_state::intellec4(machine_config &config)
{
	ADDRESS_MAP_BANK(config, m_program_banks, 0);
	m_program_banks->set_map(&intellec4_state::intellec4_program_banks);
	m_program_banks->set_endianness(ENDIANNESS_LITTLE);
	m_program_banks->set_data_width(8);
	m_program_banks->set_addr_width(14);
	m_program_banks->set_stride(0x1000);

	ADDRESS_MAP_BANK(config, m_rom_port_banks, 0);
	m_rom_port_banks->set_map(&intellec4_state::intellec4_rom_port_banks);
	m_rom_port_banks->set_endianness(ENDIANNESS_LITTLE);
	m_rom_port_banks->set_data_width(8);
	m_rom_port_banks->set_addr_width(14);
	m_rom_port_banks->set_stride(0x1000);

	INTEL_IMM6_76(config, m_prom_programmer, 0);

	RS232_PORT(config, m_tty, default_rs232_devices, "terminal");
	m_tty->set_option_device_input_defaults("terminal",   DEVICE_INPUT_DEFAULTS_NAME(terminal));
	m_tty->set_option_device_input_defaults("null_modem", DEVICE_INPUT_DEFAULTS_NAME(null_modem));

	INTELLEC4_UNIV_BUS(config, m_bus, 518000. / 7);
	m_bus->set_rom_space(m_program_banks, AS_PROGRAM);
	m_bus->set_rom_ports_space(m_rom_port_banks, AS_PROGRAM);
	m_bus->set_memory_space(m_cpu, mcs40_cpu_device_base::AS_RAM_MEMORY);
	m_bus->set_status_space(m_cpu, mcs40_cpu_device_base::AS_RAM_STATUS);
	m_bus->set_ram_ports_space(m_cpu, mcs40_cpu_device_base::AS_RAM_PORTS);
	m_bus->reset_4002_out_cb().set(FUNC(intellec4_state::bus_reset_4002));
	m_bus->user_reset_out_cb().set(FUNC(intellec4_state::bus_user_reset));
	INTELLEC4_UNIV_SLOT(config, "j7",  5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, "imm4_90");
	INTELLEC4_UNIV_SLOT(config, "j8",  5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, "imm6_26");
	INTELLEC4_UNIV_SLOT(config, "j9",  5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
	INTELLEC4_UNIV_SLOT(config, "j10", 5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
	INTELLEC4_UNIV_SLOT(config, "j11", 5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
	INTELLEC4_UNIV_SLOT(config, "j12", 5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
	INTELLEC4_UNIV_SLOT(config, "j13", 5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
	INTELLEC4_UNIV_SLOT(config, "j14", 5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
	INTELLEC4_UNIV_SLOT(config, "j15", 5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
	INTELLEC4_UNIV_SLOT(config, "j16", 5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
	INTELLEC4_UNIV_SLOT(config, "j17", 5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
	INTELLEC4_UNIV_SLOT(config, "j18", 5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
	INTELLEC4_UNIV_SLOT(config, "j19", 5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
}


/*----------------------------------
  Timer handlers
----------------------------------*/

TIMER_CALLBACK_MEMBER(intellec4_state::reset_expired)
{
	if (m_cpu_reset)
	{
		m_cpu_reset = false;
		m_cpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
		m_bus->cpu_reset_in(1);
		if (m_sw_reset_mode)
		{
			m_bus->reset_4002_in(1);
			check_4002_reset();
		}
	}
}


/*----------------------------------
  LED update helpers
----------------------------------*/

void intellec4_state::display_address(u16 value, u16 mask)
{
	u16 const diff((value ^ m_display_addr) & mask);
	m_display_addr ^= diff;
	m_led_address[0][0] = BIT(m_display_addr, 0);
	m_led_address[0][1] = BIT(m_display_addr, 1);
	m_led_address[0][2] = BIT(m_display_addr, 2);
	m_led_address[0][3] = BIT(m_display_addr, 3);
	m_led_address[1][0] = BIT(m_display_addr, 4);
	m_led_address[1][1] = BIT(m_display_addr, 5);
	m_led_address[1][2] = BIT(m_display_addr, 6);
	m_led_address[1][3] = BIT(m_display_addr, 7);
	m_led_address[2][0] = BIT(m_display_addr, 8);
	m_led_address[2][1] = BIT(m_display_addr, 9);
	m_led_address[2][2] = BIT(m_display_addr, 10);
	m_led_address[2][3] = BIT(m_display_addr, 11);
}

void intellec4_state::display_instruction(u8 value, u8 mask)
{
	u16 const diff((value ^ m_display_instr) & mask);
	m_display_instr ^= diff;
	m_led_instruction[1][0] = BIT(m_display_instr, 0);
	m_led_instruction[1][1] = BIT(m_display_instr, 1);
	m_led_instruction[1][2] = BIT(m_display_instr, 2);
	m_led_instruction[1][3] = BIT(m_display_instr, 3);
	m_led_instruction[0][0] = BIT(m_display_instr, 4);
	m_led_instruction[0][1] = BIT(m_display_instr, 5);
	m_led_instruction[0][2] = BIT(m_display_instr, 6);
	m_led_instruction[0][3] = BIT(m_display_instr, 7);
}

void intellec4_state::display_active_bank(u8 value)
{
	m_led_active_bank[0] = BIT(value, 0);
	m_led_active_bank[1] = BIT(value, 1);
	m_led_active_bank[2] = BIT(value, 2);
	m_led_active_bank[3] = BIT(value, 3);
}

void intellec4_state::display_execution(u8 value, u8 mask)
{
	u16 const diff((value ^ m_display_exec) & mask);
	m_display_exec ^= diff;
	m_led_execution[1][0] = BIT(m_display_exec, 0);
	m_led_execution[1][1] = BIT(m_display_exec, 1);
	m_led_execution[1][2] = BIT(m_display_exec, 2);
	m_led_execution[1][3] = BIT(m_display_exec, 3);
	m_led_execution[0][0] = BIT(m_display_exec, 4);
	m_led_execution[0][1] = BIT(m_display_exec, 5);
	m_led_execution[0][2] = BIT(m_display_exec, 6);
	m_led_execution[0][3] = BIT(m_display_exec, 7);
}

void intellec4_state::display_pointer(u8 value, u8 mask)
{
	u16 const diff((value ^ m_display_ptr) & mask);
	m_display_ptr ^= diff;
	m_led_last_ptr[1][0] = BIT(m_display_ptr, 0);
	m_led_last_ptr[1][1] = BIT(m_display_ptr, 1);
	m_led_last_ptr[1][2] = BIT(m_display_ptr, 2);
	m_led_last_ptr[1][3] = BIT(m_display_ptr, 3);
	m_led_last_ptr[0][0] = BIT(m_display_ptr, 4);
	m_led_last_ptr[0][1] = BIT(m_display_ptr, 5);
	m_led_last_ptr[0][2] = BIT(m_display_ptr, 6);
	m_led_last_ptr[0][3] = BIT(m_display_ptr, 7);
}


/*----------------------------------
  Internal helpers
----------------------------------*/

void intellec4_state::trigger_reset()
{
	// (re-)trigger the reset monostable
	if (!m_sw_reset && !m_sw_prg_mode[0] && !m_sw_prg_mode[1] && !m_sw_prg_mode[2] && !m_bus_user_reset)
	{
		// 9602 with Rx = 27kΩ, Cx = 0.1µF
		// K * Rx(kΩ) * Cx(pF) * (1 + (1 / Rx(kΩ))) = 0.34 * 27 * 100000 * (1 + (1 / 27)) = 952000ns
		m_reset_timer->adjust(attotime::from_usec(952));
		if (!m_cpu_reset)
		{
			m_cpu_reset = true;
			m_cpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
			m_bus->cpu_reset_in(0);
			reset_panel();
			if (m_sw_reset_mode)
			{
				m_bus->reset_4002_in(0);
				check_4002_reset();
			}
		}
	}
}

void intellec4_state::check_4002_reset()
{
	// FIXME: this really takes multiple cycles to erase, and prevents writes while held
	if ((m_cpu_reset && m_sw_reset_mode) || m_bus_reset_4002)
	{
		std::fill_n(&m_memory[0], m_memory.bytes(), 0U);
		std::fill_n(&m_status[0], m_memory.bytes(), 0U);
	}
}

void intellec4_state::reset_panel()
{
	if (!m_panel_reset)
	{
		m_pass_counter = 0U;
		m_panel_reset = true;
		m_adr_cmp_latch = false;
		if (m_search_complete && !m_next_inst)
			m_led_status_search = m_search_complete = false;
		if (m_pointer_valid)
			m_led_status_ptr_valid = m_pointer_valid = false;
	}
}



/***********************************************************************
    MOD 4 driver
***********************************************************************/

class mod4_state : public intellec4_state
{
public:
	mod4_state(machine_config const &mconfig, device_type type, char const *tag)
		: intellec4_state(mconfig, type, tag)
		, m_led_status_cpu(*this, "led_status_cpu")
	{
	}

	// universal slot outputs
	void bus_test(int state);

	// front panel switches
	DECLARE_INPUT_CHANGED_MEMBER(sw_hold);
	DECLARE_INPUT_CHANGED_MEMBER(sw_one_shot);

	void mod4(machine_config &config);

protected:
	virtual void driver_start() override;
	virtual void driver_reset() override;

private:
	enum
	{
		BIT_SW_HOLD = 0,
		BIT_SW_ONE_SHOT
	};

	TIMER_CALLBACK_MEMBER(one_shot_expired);

	output_finder<> m_led_status_cpu;

	emu_timer   *m_one_shot_timer = nullptr;

	// control board state
	bool    m_one_shot = false;

	// current state of signals from bus
	bool    m_bus_test = false;

	// current state of front panel switches
	bool    m_sw_hold = false;
};


/*----------------------------------
  MOD 4-specific switches
----------------------------------*/

INPUT_PORTS_START(mod4)
	PORT_INCLUDE(intellec4)

	PORT_MODIFY("MODE")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("HOLD")     PORT_CODE(KEYCODE_LEFT)  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mod4_state::sw_hold),     0)
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYPAD )             PORT_NAME("ONE SHOT") PORT_CODE(KEYCODE_RIGHT) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mod4_state::sw_one_shot), 0)
INPUT_PORTS_END


/*----------------------------------
  Bus signal handlers
----------------------------------*/

void mod4_state::bus_test(int state)
{
	if (!m_one_shot && !m_sw_hold)
		m_cpu->set_input_line(I4004_TEST_LINE, state ? CLEAR_LINE : ASSERT_LINE);
	m_bus_test = 0 == state;
}


/*----------------------------------
  Front panel switch handlers
----------------------------------*/

INPUT_CHANGED_MEMBER(mod4_state::sw_hold)
{
	// overridden by the one-shot monostable and the bus test signal
	if (!m_one_shot)
	{
		if (!m_bus_test)
			m_cpu->set_input_line(I4004_TEST_LINE, newval ? CLEAR_LINE : ASSERT_LINE);
		m_bus->test_in(newval ? 1 : 0);
	}
	m_sw_hold = !bool(newval);
}

INPUT_CHANGED_MEMBER(mod4_state::sw_one_shot)
{
	if (newval && !oldval)
	{
		// 9602 with Rx = 20kΩ, Cx = 0.005µF
		// K * Rx(kΩ) * Cx(pF) * (1 + (1 / Rx(kΩ))) = 0.34 * 20 * 5000 * (1 + (1 / 20)) = 35700ns
		m_one_shot_timer->adjust(attotime::from_nsec(35700));
		if (!m_one_shot)
		{
			m_one_shot = true;
			if (!m_sw_hold)
			{
				m_bus->test_in(0);
				if (!m_bus_test)
					m_cpu->set_input_line(I4004_TEST_LINE, ASSERT_LINE);
			}
		}
	}
}


/*----------------------------------
  MOD 4-specific configuration
----------------------------------*/

void mod4_state::mod4(machine_config &config)
{
	intellec4(config);

	i4004_cpu_device &cpu(I4004(config, m_cpu, 5.185_MHz_XTAL / 7));
	cpu.set_rom_map(&mod4_state::intellec4_rom);
	cpu.set_ram_memory_map(&mod4_state::intellec4_ram_memory);
	cpu.set_rom_ports_map(&mod4_state::intellec4_rom_ports);
	cpu.set_ram_status_map(&mod4_state::intellec4_ram_status);
	cpu.set_ram_ports_map(&mod4_state::intellec4_ram_ports);
	cpu.set_program_memory_map(&mod4_state::intellec4_program_memory);
	cpu.set_bus_cycle_cb(FUNC(mod4_state::bus_cycle));
	cpu.sync_cb().set(m_bus, FUNC(bus::intellec4::univ_bus_device::sync_in));

	m_bus->test_out_cb().set(FUNC(mod4_state::bus_test));

	config.set_default_layout(layout_intlc44);
}


/*----------------------------------
  driver_device implementation
----------------------------------*/

void mod4_state::driver_start()
{
	intellec4_state::driver_start();

	m_led_status_cpu.resolve();

	m_one_shot_timer = timer_alloc(FUNC(mod4_state::one_shot_expired), this);

	save_item(NAME(m_one_shot));

	save_item(NAME(m_bus_test));

	save_item(NAME(m_sw_hold));

	m_one_shot = false;
}

void mod4_state::driver_reset()
{
	intellec4_state::driver_reset();

	// set stuff according to initial state of front panel
	m_sw_hold = BIT(~m_sw_mode->read(), BIT_SW_HOLD);

	// ensure we're consistent with the state of the bus
	m_bus_test = 0 == m_bus->test_out();

	// ensure device inputs are all in the correct state
	m_cpu->set_input_line(I4004_TEST_LINE, (m_one_shot || m_bus_test || m_sw_hold) ? ASSERT_LINE : CLEAR_LINE);
	m_bus->test_in((m_one_shot || m_sw_hold) ? 0 : 1);

	// set front panel LEDs
	m_led_status_cpu = true; // actually driven by clock phase 2 - let's assume it's always running
}


/*----------------------------------
  Timer handlers
----------------------------------*/

TIMER_CALLBACK_MEMBER(mod4_state::one_shot_expired)
{
	m_one_shot = false;
	if (!m_sw_hold)
	{
		m_bus->test_in(1);
		if (!m_bus_test)
			m_cpu->set_input_line(I4004_TEST_LINE, CLEAR_LINE);
	}
}



/***********************************************************************
    MOD 40 driver
***********************************************************************/

class mod40_state : public intellec4_state
{
public:
	mod40_state(machine_config const &mconfig, device_type type, char const *tag)
		: intellec4_state(mconfig, type, tag)
		, m_led_status_run(*this, "led_status_run")
	{
	}

	void stp_ack(int state);

	// universal slot outputs
	void bus_stop(int state);
	void bus_test(int state);

	// front panel switches
	DECLARE_INPUT_CHANGED_MEMBER(sw_stop);
	DECLARE_INPUT_CHANGED_MEMBER(sw_single_step);

	void mod40(machine_config &config);

protected:
	virtual void driver_start() override;
	virtual void driver_reset() override;

private:
	enum
	{
		BIT_SW_STOP = 0,
		BIT_SW_SINGLE_STEP
	};

	TIMER_CALLBACK_MEMBER(single_step_expired);

	output_finder<> m_led_status_run;

	emu_timer   *m_single_step_timer = nullptr;

	// control board state
	bool    m_stp_ack = false, m_single_step = false;

	// current state of signals from bus
	bool    m_bus_stop = false;

	// current state of front panel switches
	bool    m_sw_stop = false;
};


/*----------------------------------
  MOD 40-specific switches
----------------------------------*/

INPUT_PORTS_START(mod40)
	PORT_INCLUDE(intellec4)

	PORT_MODIFY("MODE")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("STOP")        PORT_CODE(KEYCODE_LEFT)  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mod40_state::sw_stop),        0)
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYPAD )             PORT_NAME("SINGLE STEP") PORT_CODE(KEYCODE_RIGHT) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mod40_state::sw_single_step), 0)
INPUT_PORTS_END


/*----------------------------------
  CPU output handlers
----------------------------------*/

void mod40_state::stp_ack(int state)
{
	// resets the single-step monostable
	if (m_stp_ack && state)
	{
		m_single_step_timer->reset();
		m_single_step = false;
		if (m_sw_stop)
		{
			m_bus->stop_in(0);
			if (!m_bus_stop)
				m_cpu->set_input_line(I4040_STP_LINE, ASSERT_LINE);
		}
	}
	m_stp_ack = 0 == state;
	m_led_status_run = !m_stp_ack;
	m_bus->stop_acknowledge_in(state);
}


void mod40_state::bus_stop(int state)
{
	// will not allow the CPU to step/run
	if (m_single_step || !m_sw_stop)
		m_cpu->set_input_line(I4040_STP_LINE, state ? CLEAR_LINE : ASSERT_LINE);
	m_bus_stop = 0 == state;
}


/*----------------------------------
  Bus signal handlers
----------------------------------*/

void mod40_state::bus_test(int state)
{
	m_cpu->set_input_line(I4040_TEST_LINE, state ? CLEAR_LINE : ASSERT_LINE);
}


/*----------------------------------
  Front panel switch handlers
----------------------------------*/

INPUT_CHANGED_MEMBER(mod40_state::sw_stop)
{
	// overridden by the single-step monostable and the bus stop signal
	if (!m_single_step)
	{
		if (!m_bus_stop)
			m_cpu->set_input_line(I4040_STP_LINE, newval ? CLEAR_LINE : ASSERT_LINE);
		m_bus->stop_in(newval ? 1 : 0);
	}
	m_sw_stop = !bool(newval);
}

INPUT_CHANGED_MEMBER(mod40_state::sw_single_step)
{
	// (re-)triggers the single-step monostable
	if (m_stp_ack && newval && !oldval)
	{
		// 9602 with Rx = 20kΩ, Cx = 0.005µF
		// K * Rx(kΩ) * Cx(pF) * (1 + (1 / Rx(kΩ))) = 0.34 * 20 * 5000 * (1 + (1 / 20)) = 35700ns
		m_single_step_timer->adjust(attotime::from_nsec(35700));
		if (!m_single_step)
		{
			m_single_step = true;
			if (m_sw_stop)
			{
				m_bus->stop_in(1);
				if (!m_bus_stop)
					m_cpu->set_input_line(I4040_STP_LINE, CLEAR_LINE);
			}
		}
	}
}


/*----------------------------------
  MOD 40-specific configuration
----------------------------------*/

void mod40_state::mod40(machine_config &config)
{
	intellec4(config);

	i4040_cpu_device &cpu(I4040(config, m_cpu, 5.185_MHz_XTAL / 7));
	cpu.set_rom_map(&mod40_state::intellec4_rom);
	cpu.set_ram_memory_map(&mod40_state::intellec4_ram_memory);
	cpu.set_rom_ports_map(&mod40_state::intellec4_rom_ports);
	cpu.set_ram_status_map(&mod40_state::intellec4_ram_status);
	cpu.set_ram_ports_map(&mod40_state::intellec4_ram_ports);
	cpu.set_program_memory_map(&mod40_state::intellec4_program_memory);
	cpu.set_bus_cycle_cb(FUNC(mod40_state::bus_cycle));
	cpu.sync_cb().set(m_bus, FUNC(bus::intellec4::univ_bus_device::sync_in));
	cpu.stp_ack_cb().set(FUNC(mod40_state::stp_ack));

	m_bus->stop_out_cb().set(FUNC(mod40_state::bus_stop));
	m_bus->test_out_cb().set(FUNC(mod40_state::bus_test));

	config.set_default_layout(layout_intlc440);
}


/*----------------------------------
  driver_device implementation
----------------------------------*/

void mod40_state::driver_start()
{
	intellec4_state::driver_start();

	m_led_status_run.resolve();

	m_single_step_timer = timer_alloc(FUNC(mod40_state::single_step_expired), this);

	save_item(NAME(m_stp_ack));
	save_item(NAME(m_single_step));

	save_item(NAME(m_bus_stop));

	save_item(NAME(m_sw_stop));

	m_stp_ack = m_single_step = false;
}

void mod40_state::driver_reset()
{
	intellec4_state::driver_reset();

	// set stuff according to initial state of front panel
	m_sw_stop = BIT(~m_sw_mode->read(), BIT_SW_STOP);

	// ensure we're consistent with the state of the bus
	m_bus_stop = 0 == m_bus->stop_out();

	// ensure device inputs are all in the correct state
	m_cpu->set_input_line(I4040_TEST_LINE, m_bus->test_out() ? CLEAR_LINE : ASSERT_LINE);
	m_cpu->set_input_line(I4040_STP_LINE, ((m_sw_stop && !m_single_step) || m_bus_stop) ? ASSERT_LINE : CLEAR_LINE);
	m_bus->test_in(1);
	m_bus->stop_in((m_sw_stop && !m_single_step) ? 0 : 1);

	// set front panel LEDs
	m_led_status_run = !m_stp_ack;
}


/*----------------------------------
  Timer handlers
----------------------------------*/

TIMER_CALLBACK_MEMBER(mod40_state::single_step_expired)
{
	m_single_step = false;
	if (m_sw_stop)
	{
		m_bus->stop_in(0);
		if (!m_bus_stop)
			m_cpu->set_input_line(I4040_STP_LINE, ASSERT_LINE);
	}
}


/***********************************************************************
    ROM definitions
***********************************************************************/

ROM_START(intlc44)
	ROM_REGION(0x0400, "monitor", 0) // 4 * 1702A
	ROM_DEFAULT_BIOS("v2.1")
	ROM_SYSTEM_BIOS(0, "v2.1", "MON 4 V2.1")
	ROMX_LOAD("mon_4-000-v_2.1.a1",      0x0000, 0x0100, CRC(8d1f56ff) SHA1(96bc19be9be4e92195fad82d7a3cadb763ab6e3f), ROM_BIOS(0))
	ROMX_LOAD("mon_4-100-v_2.1.a2",      0x0100, 0x0100, CRC(66562a4f) SHA1(040749c45e95dfc39b3397d0c31c8b4c11f0a5fc), ROM_BIOS(0))
	ROMX_LOAD("mon_4-200-v_2.1.a3",      0x0200, 0x0100, CRC(fe039c68) SHA1(1801cfcc7514412865c0fdc7d1800fcf583a2d2a), ROM_BIOS(0))
	ROMX_LOAD("mon_4-300-v_2.1.a4",      0x0300, 0x0100, CRC(3724d5af) SHA1(b764b3bb3541fbda875f7a7655f46aa54b332631), ROM_BIOS(0))
ROM_END

ROM_START(intlc440)
	ROM_REGION(0x0400, "monitor", 0) // 4 * 1702A
	ROM_DEFAULT_BIOS("v2.1")
	ROM_SYSTEM_BIOS(0, "v2.1", "MON 4 V2.1")
	ROMX_LOAD("mon_4-000-v_2.1.a1",      0x0000, 0x0100, CRC(8d1f56ff) SHA1(96bc19be9be4e92195fad82d7a3cadb763ab6e3f), ROM_BIOS(0))
	ROMX_LOAD("mon_4-100-v_2.1.a2",      0x0100, 0x0100, CRC(66562a4f) SHA1(040749c45e95dfc39b3397d0c31c8b4c11f0a5fc), ROM_BIOS(0))
	ROMX_LOAD("mon_4-200-v_2.1.a3",      0x0200, 0x0100, CRC(fe039c68) SHA1(1801cfcc7514412865c0fdc7d1800fcf583a2d2a), ROM_BIOS(0))
	ROMX_LOAD("mon_4-300-v_2.1.a4",      0x0300, 0x0100, CRC(3724d5af) SHA1(b764b3bb3541fbda875f7a7655f46aa54b332631), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v2.1_1200", "MON 4 V2.1 1200 Baud hack")
	ROMX_LOAD("mon_4-000-v_2.1.a1",      0x0000, 0x0100, CRC(8d1f56ff) SHA1(96bc19be9be4e92195fad82d7a3cadb763ab6e3f), ROM_BIOS(1))
	ROMX_LOAD("i40_mon-1.a2",            0x0100, 0x0100, CRC(cd9fecd6) SHA1(9c4fb85118c881687fd4b324e5089df05d1e63d1), ROM_BIOS(1))
	ROMX_LOAD("i40_mon-2.a3",            0x0200, 0x0100, CRC(037de128) SHA1(3694636e1f4e23688b36ea9ee755a0c5888f4328), ROM_BIOS(1))
	ROMX_LOAD("1200_baud-i40_mon-f3.a4", 0x0300, 0x0100, CRC(f3198d79) SHA1(b7903073b69f487b6f78842c08694f12225d85f0), ROM_BIOS(1))
ROM_END

} // anonymous namespace


/***********************************************************************
    Machine definitions
***********************************************************************/

//    YEAR   NAME      PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME             FLAGS
COMP( 1973?, intlc44,  0,      0,      mod4,    mod4,  mod4_state,  empty_init, "Intel", "INTELLEC 4/MOD 4",  MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1974?, intlc440, 0,      0,      mod40,   mod40, mod40_state, empty_init, "Intel", "INTELLEC 4/MOD 40", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



intellec8.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Robbbert
/***************************************************************************

Intellec 8 MCS

A development machine from Intel for the 8008 CPU, with Front Panel.
It has the usual array of switches, lights and buttons.

****************************************************************************/

#include "emu.h"
#include "cpu/i8008/i8008.h"

namespace {

class intlc8_state : public driver_device
{
public:
	intlc8_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void intlc8(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};

void intlc8_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom();  // ROM - 2 KB expandable to 16 KB?
	map(0x2000, 0x3fff).ram();  // RAM - 8 KB expandable to 16 KB?
}

void intlc8_state::io_map(address_map &map)
{
	map.unmap_value_high();
}

// Input ports
static INPUT_PORTS_START( intlc8 )
INPUT_PORTS_END

void intlc8_state::machine_reset()
{
}

void intlc8_state::machine_start()
{
}

void intlc8_state::intlc8(machine_config &config)
{
	// basic machine hardware
	I8008(config, m_maincpu, 800000);   // 750 or 800 kHz clock?
	m_maincpu->set_addrmap(AS_PROGRAM, &intlc8_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &intlc8_state::io_map);
}


ROM_START( intlc8 )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )  // order of roms is a guess and imo some are missing?
	ROM_LOAD( "miss0.bin",    0x0000, 0x0100, NO_DUMP )
	ROM_LOAD( "miss1.bin",    0x0100, 0x0100, NO_DUMP )
	ROM_LOAD( "rom1.bin",     0x0200, 0x0100, CRC(0ae76bc7) SHA1(374a545cad8406ad862a5f2f1f03c6b6434fd3d8) )
	ROM_LOAD( "rom4.bin",     0x0300, 0x0100, CRC(4340fdfe) SHA1(d37f3bd65c2970736ac075c7e6d3d87d018c3ea4) )
	ROM_LOAD( "rom2.bin",     0x0400, 0x0100, CRC(dd1a71f4) SHA1(e33a2b64bea18c0aa58230167eb23bae464431be) )
	ROM_LOAD( "rom5.bin",     0x0500, 0x0100, CRC(d631224c) SHA1(812d37ac98daf1252bc8d087da679f3ad1f9d961) )
	ROM_LOAD( "rom3.bin",     0x0600, 0x0100, CRC(97c7ab95) SHA1(650316b820b84393bb73c4c56c11e177d658ce4a) )
	ROM_LOAD( "miss7.bin",    0x0700, 0x0100, NO_DUMP )
ROM_END

// FIXME: these are for an 8080-based system and possibly don't belong here

ROM_START( intlc8m80 )
	ROM_DEFAULT_BIOS("884a")
	ROM_SYSTEM_BIOS(0, "880", "880")
	ROM_SYSTEM_BIOS(1, "884a", "884A")

	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )

	ROMX_LOAD( "3000_1702a.chip_0",                  0x0000, 0x0100, CRC(a72cdab9) SHA1(c9931f1b55558383054a02b40fa716e9658ce3c4), ROM_BIOS(0) )
	ROMX_LOAD( "3100h_1702a.chip_1",                 0x0100, 0x0100, CRC(62fbc181) SHA1(e704b5b198e43ff0b9c57067e375070a7674f3dd), ROM_BIOS(0) )
	ROMX_LOAD( "3200_1702a.chip_2",                  0x0200, 0x0100, CRC(65215a39) SHA1(aaaae0f226a0d0f4eeac51e836b1ea674eb0f996), ROM_BIOS(0) )
	// Empty sockets from CHIP 3 to CHIP 4
	ROMX_LOAD( "icon_2708-p_mon8_3800_1702a.chip_5", 0x0300, 0x0100, CRC(00445543) SHA1(63ab074ba8c43905e2a43761b5cc5e0618f6e403), ROM_BIOS(0) )
	ROMX_LOAD( "prog_3600_1702a.chip_6",             0x0400, 0x0100, CRC(61a99002) SHA1(2be84f3191d6de5e184a8c7a40312c663b4c1817), ROM_BIOS(0) )
	ROMX_LOAD( "1702a.chip_7",                       0x0500, 0x0100, CRC(90d9a76b) SHA1(3d66bc7aa5caa2abb487dfe28ab989b373fe6703), ROM_BIOS(0) )
	ROMX_LOAD( "3800_mon8_1702a.chip_8",             0x0600, 0x0100, CRC(ee7a08dc) SHA1(f12a690d72f08ef333e65634e0e60aff40746b7a), ROM_BIOS(0) )
	ROMX_LOAD( "3900h_mon8_1702a.chip_9",            0x0700, 0x0100, CRC(d2795e4d) SHA1(f3ef8e197fcfaf6752da8cee6ee776b7a450cef2), ROM_BIOS(0) )
	ROMX_LOAD( "3a00h_mon8_1702a.chip_a",            0x0800, 0x0100, CRC(c92f98e3) SHA1(dcb4316c6f037666a2a4b88df7e18ca74e754dd0), ROM_BIOS(0) )
	ROMX_LOAD( "3b00h_mon8_1702a.chip_b",            0x0900, 0x0100, CRC(23083008) SHA1(57af12b20f160d5faa99ad2bda597f21e52078c9), ROM_BIOS(0) )
	ROMX_LOAD( "3c00h_mon8_1702a.chip_c",            0x0a00, 0x0100, CRC(32f5c81b) SHA1(2371c0e087486c8bcb909575f158fd5ac9209bc8), ROM_BIOS(0) )
	ROMX_LOAD( "3d00h_mon8_1702a.chip_d",            0x0b00, 0x0100, CRC(5307307a) SHA1(f38adac5e1a8bb015e23f13be5ab434394e6495f), ROM_BIOS(0) )
	ROMX_LOAD( "3e00h_mon8_1702a.chip_e",            0x0c00, 0x0100, NO_DUMP,                                                      ROM_BIOS(0) )
	ROMX_LOAD( "3f00h_mon8_1702a.chip_f",            0x0d00, 0x0100, CRC(beca9bd7) SHA1(8162306bfbd94a373736b9e8e9f426af104d744e), ROM_BIOS(0) )

	ROMX_LOAD( "1702a.chip_0",                       0x0000, 0x0100, CRC(64a1aa3a) SHA1(7158e866eb222b1fbc1f573cdc748f5aedd6d0d4), ROM_BIOS(1) )
	ROMX_LOAD( "1702a.chip_1",                       0x0100, 0x0100, CRC(4583b6c3) SHA1(f9073ecfe0c043756437af595aa87b7224bf370d), ROM_BIOS(1) )
	ROMX_LOAD( "1702a.chip_2",                       0x0200, 0x0100, CRC(5a2951cc) SHA1(d9252365a330cd390ebe029517c83d9e579750a2), ROM_BIOS(1) )
	ROMX_LOAD( "1702a.chip_3",                       0x0300, 0x0100, CRC(37b1b90b) SHA1(b88872d56d03efe6ebc550fc9608e55ca0cae3cd), ROM_BIOS(1) )
	ROMX_LOAD( "1702a.chip_4",                       0x0400, 0x0100, CRC(dcbcc405) SHA1(1dc65e9c73416353e2650158183ff4ff33a9eb0e), ROM_BIOS(1) )
	// Empty sockets from CHIP 5 to CHIP 6
	ROMX_LOAD( "3700_1702a.chip_7",                  0x0500, 0x0100, CRC(cf5a0f6e) SHA1(13841fe28ac310f00d4c55ed07fee8be556ecc9b), ROM_BIOS(1) )
	ROMX_LOAD( "1702a.chip_8",                       0x0600, 0x0100, CRC(fee2425f) SHA1(0210012153d9e294f46ab3653b4eb439125b6cfe), ROM_BIOS(1) )
	ROMX_LOAD( "3900_1702a.chip_9",                  0x0700, 0x0100, CRC(d2795e4d) SHA1(f3ef8e197fcfaf6752da8cee6ee776b7a450cef2), ROM_BIOS(1) )
	ROMX_LOAD( "1702a.chip_a",                       0x0800, 0x0100, CRC(c92f98e3) SHA1(dcb4316c6f037666a2a4b88df7e18ca74e754dd0), ROM_BIOS(1) )
	ROMX_LOAD( "1702a.chip_b",                       0x0900, 0x0100, CRC(23083008) SHA1(57af12b20f160d5faa99ad2bda597f21e52078c9), ROM_BIOS(1) )
	ROMX_LOAD( "3c00_1702a.chip_c",                  0x0a00, 0x0100, CRC(32f5c81b) SHA1(2371c0e087486c8bcb909575f158fd5ac9209bc8), ROM_BIOS(1) )
	ROMX_LOAD( "1702a.chip_d",                       0x0b00, 0x0100, CRC(5307307a) SHA1(f38adac5e1a8bb015e23f13be5ab434394e6495f), ROM_BIOS(1) )
	ROMX_LOAD( "3e00_1702a.chip_e",                  0x0c00, 0x0100, CRC(a90bd1d4) SHA1(b85a4a3d6515aa4ae298a800192077716a060f85), ROM_BIOS(1) )
	ROMX_LOAD( "1702a.chip_f",                       0x0d00, 0x0100, CRC(ae7c919b) SHA1(ab9e2f70ef19d969ce7238784118ae53a5c7ac85), ROM_BIOS(1) )
ROM_END

} // Anonymous namespace

//    YEAR  NAME        PARENT      COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY  FULLNAME             FLAGS
COMP( 1973, intlc8,     0,          0,      intlc8,  intlc8, intlc8_state, empty_init, "Intel", "intellec 8",        MACHINE_NO_SOUND | MACHINE_NOT_WORKING ) // MOD8 or MOD80?
COMP( 1974, intlc8m80,  0,          0,      intlc8,  intlc8, intlc8_state, empty_init, "Intel", "intellec 8/Mod 80", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



intellect02.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Alex_LG, Berger
/*******************************************************************************

Интеллект-02 (Intellect-02) driver

This is a Soviet electronic board game console, a dozen or so cartridge games were
announced(can't say for certain how many released). PCB labels have prefix ДМП,
it's assumed to have been designed by НИИ БРЭА (SRI BREA). First shown in 1983,
produced during around 1985-1992.

Hardware notes:
- КР580ВМ80А CPU (i8080A clone) @ 1.5MHz
- КР580ИК55 (i8255 clone)
- 1 KB RAM (8*КР565РУ2), cartridge port
- 4-digit 7seg panel, 2 leds, 16 buttons, game board above it

The chess/checkers board is detachable, with a board for Kalah underneath it.

The hardware is very similar to Fidelity Chess Challenger 3. Actually, one of the
first cartridges for this system, a Chess program, is a modified Chess Challenger 3 ROM.
The chess engine was kept identical. (note: the "lose" LED is used for "check" in this game)
The 2nd(4-level) chess cartridge is completely different, not a CC3 clone.

Intellect-01 looks like it didn't get further than a prototype. It was a dedicated
chess computer, probably a clone of CC3.

Keypad legend:

СБ - сброс (reset)
ВВ - ввод (input)
ВИ - выбор игры (game select)
СТ - стирание (erase)
ПП - просмотр позиции (view position)
УИ - уровень игры (game level)

*******************************************************************************/

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8255.h"
#include "sound/beep.h"
#include "video/pwm.h"

#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "intellect02.lh"


namespace {

class intel02_state : public driver_device
{
public:
	intel02_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ppi8255(*this, "ppi8255"),
		m_display(*this, "display"),
		m_beeper(*this, "beeper"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// machine configs
	void intel02(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(reset_button);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<i8255_device> m_ppi8255;
	required_device<pwm_display_device> m_display;
	required_device<beep_device> m_beeper;
	required_ioport_array<2> m_inputs;

	u8 m_digit_data = 0;
	u8 m_led_select = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_display();
	u8 input_r();
	void digit_w(u8 data);
	void control_w(u8 data);
};

void intel02_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_digit_data));
	save_item(NAME(m_led_select));
}

INPUT_CHANGED_MEMBER(intel02_state::reset_button)
{
	// reset button goes to 8080/8255 RESET pins simultaneously (via 7474, and also a maze of NAND gates to who knows where)
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
	if (newval)
		m_ppi8255->reset();
}



/*******************************************************************************
    I/O
*******************************************************************************/

// I8255 PPI

void intel02_state::update_display()
{
	m_display->matrix(m_led_select, m_digit_data);
}

u8 intel02_state::input_r()
{
	// d0-d3: buttons through a maze of logic gates
	// basically giving each button its own 4-bit scancode
	u8 data = count_leading_zeros_32(m_inputs[0]->read()) - 17;

	// d4: Vcc, d5-d7: buttons (direct)
	return data | (~m_inputs[1]->read() << 4 & 0xf0);
}

void intel02_state::digit_w(u8 data)
{
	// d0-d6: digit segment data, d7: N/C
	m_digit_data = bitswap<7>(data,0,1,2,3,4,5,6);
	update_display();
}

void intel02_state::control_w(u8 data)
{
	// d0-d5: select digit/leds
	m_led_select = data;
	update_display();

	// d6: N/C (it's a delay timer on CC3)

	// d7: enable beeper
	m_beeper->set_state(BIT(data, 7));
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void intel02_state::main_map(address_map &map)
{
	map(0x0000, 0x1fff).r("cartslot", FUNC(generic_slot_device::read_rom));
	map(0xf000, 0xf3ff).ram();
}

void intel02_state::main_io(address_map &map)
{
	map.global_mask(0xff);
	map(0xf4, 0xf7).rw(m_ppi8255, FUNC(i8255_device::read), FUNC(i8255_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( intel02 )
	PORT_START("IN.0")
	PORT_BIT(0x0007, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME(u8"ПП (View Position)")
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME(u8"УИ (Game Level)")
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_H) PORT_NAME("H8")
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_G) PORT_NAME("G7")
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_F) PORT_NAME("F6")
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_E) PORT_NAME("E5")
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_D) PORT_NAME("D4")
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_C) PORT_NAME("C3")
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_B) PORT_NAME("B2")
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_A) PORT_NAME("A1")
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME(u8"ВВ (Input)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME(u8"ВИ (Game Select)")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME(u8"СТ (Erase)")

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(intel02_state::reset_button), 0) PORT_NAME(u8"СБ (Reset)")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void intel02_state::intel02(machine_config &config)
{
	// basic machine hardware
	I8080A(config, m_maincpu, 1'500'000); // measured (no XTAL)
	m_maincpu->set_addrmap(AS_PROGRAM, &intel02_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &intel02_state::main_io);

	I8255(config, m_ppi8255);
	m_ppi8255->in_pa_callback().set(FUNC(intel02_state::input_r));
	m_ppi8255->out_pb_callback().set(FUNC(intel02_state::digit_w));
	m_ppi8255->tri_pb_callback().set_constant(0);
	m_ppi8255->out_pc_callback().set(FUNC(intel02_state::control_w));
	m_ppi8255->tri_pc_callback().set_constant(0x80);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(6, 7);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_intellect02);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	BEEP(config, m_beeper, 3640); // measured, from RC circuit
	m_beeper->add_route(ALL_OUTPUTS, "speaker", 0.25);

	// cartridge
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "intellect02").set_must_be_loaded(true);
	SOFTWARE_LIST(config, "cart_list").set_original("intellect02");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( intel02 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
	// nothing here, it's on a cartridge
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1985, intel02, 0,      0,      intel02, intel02, intel02_state, empty_init, "BREA Research Institute", "Intellect-02", MACHINE_SUPPORTS_SAVE )



interpro.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * An emulation of the Intergraph InterPro/InterServe family of CLIPPER based
 * UNIX workstations.
 *
 * The first systems were built using the original C100 CLIPPER CPU, and used
 * an additional Intel 80186 as an I/O processor, later upgraded to a C300 and
 * 80386 IOP. Around 1990, the CLIPPER became fast enough to obviate the need
 * for the separate I/O processor, and systems from that point used the main
 * CPU for both compute and I/O, along with some custom ASICs.
 *
 * Over the lifespan of the InterPro, there were five distinct families of
 * systems, varying mainly in terms of the primary CPU, as follows:
 *
 *   Year  Family    Models                   CPU
 *   1986  amethyst  32C/100/200              C100 (80186 IOP)
 *                   300                      C100 (80386 IOP)
 *   1988  topaz     3000/4000/5000           C300/C300Plus (80386 IOP)
 *   1990  emerald   6000/6100/6200/6500      C300/C300Plus
 *   1990  turquoise 2000                     C300
 *   1991  emerald   6600                     C4?
 *   1992  sapphire  2400/6400                C4T
 *   1993  sapphire  2500/2700/6700/6800      C4I
 *   1994  sapphire  2800                     C4I
 *
 * Individual models and some of their specific attributes include:
 *
 *   Model  Year  CPU    Performance           Clock   Family       Bus
 *   6000   1990  C300   10 MIPS               40MHz   emerald      SRX
 *          1991         12 MIPS
 *   6100   1990  C300+? 14 MIPS                       emerald      IOI, 12-slot
            1991         15.5 MIPS
 *   6500   1990  C300+  20 MIPS                       emerald      IOI, QWIC bus?, 12-slot
 *   6200   1990  C300+  14 MIPS               60MHz   emerald
 *          1991         18 MIPS
 *   2000   1990  C300   12.5 MIPS             50MHz?  turquoise    CBUS
 *          1991         16 MIPS
 *   6600   1991  C400   40 MIPS                       emerald      IOI, SRX bus?
 *   2400   1992  C4T    36 MIPS/33 SPECmarks  40MHz?  sapphire     CBUS
 *   6400   1992  C4T    36 MIPS/33 SPECmarks  40MHz   sapphire     SRX
 *   2700   1993  C400I  61 MIPS?/40.1 SPECmark89      sapphire 2   CBUS
 *   6700   1993  C400I  61 MIPS?/40.1 SPECmark89      sapphire 2   SRX
 *   6800   1993  C400I  85 MIPS/67.2 SPECmark89       sapphire 3   SRX
 *   2500   1993  C400I  19.9 SPECint92                sapphire
 *   2800   1994  C400I                                sapphire 3   CBUS?
 *
 * IOI == I/O Interface (another type of bus?)
 * Sapphire 2 w/CBUS supports RETRY (maybe bus retry?)
 *
 * With some exceptions, system models are numbered ABCD, where:
 *
 *   A: case type (2=desktop, 6=minicase)
 *   B: CPU type (0=C300, 4=C4T, 6=C400?, 7/8/5 = C400I)
 *   C: graphics type (0=none, 2=GT, 3=GT+, 5=GTII, 4=EDGE-1, 8=EDGE-2/2+)
 *   D: usually 0, 6xxx systems have 5, 7 and 9 options (backplane type?)
 *
 * Both the desktop and minicase units supported expansion slots with a variety
 * of cards, although with different profiles and connectors between 2xxx and
 * 6xxx systems. The 2xxx bus is referred to as CBUS, with the 6xxx bus known
 * as SRX bus; SR (shared resource) bus is used to refer to either type. The
 * bus supported a range of add-in cards, ranging from expanded SCSI and serial
 * controllers, to specialised scanning and plotting controllers, a VME bridge,
 * and most importantly various single and dual-headed graphics boards.
 *
 * The InterPro graphics options included the GT range, generally fitted to the
 * desktops, and EDGE graphics for the 6xxx systems. Systems with no graphics
 * were operated through a serial terminal on serial port 2, and were branded
 * InterServe rather than InterPro.
 *
 *   Model     Year    Performance
 *   GT        1990?   360k 2D vec/s (in a 2020)
 *   EDGE-1            8 planes + 1 highlight plane, double buffered (6040)
 *   EDGE-2            24 bit, 400k 2D vec/s, 350k 3D vec/s (6280)
 *   GT+               500k 2D vec/s, 300k 2D vec/s (in a 2430)
 *                     760k 2D vec/s, 530k 3D vec/s (in a 2730)
 *   GTII              800k 2D vec/s, 500k 3D vec/s (in a 6450)
 *                     830k 2D vec/s, 640k 3D vec/s (in a 6750)
 *                     900k 2D vec/s, 700k 3D vec/s (in a 6850)
 *   EDGE II+          600k 2D vec/s, 500k 3D vec/s, 50k shaded poly/s (in a 6480)
 *
 * GT graphics are also referred to in various places as Memory Mapped Graphics
 * or MMG. EDGE stands for Extensible Display Geometry Engine.
 *
 * This driver currently supports the Emerald, Turquoise and Sapphire systems
 * (i.e. 2x00 and 6x00 models), but not all of the variants of the Emerald at
 * this point. GT/GTDB or EDGE graphics can be used depending on model, or
 * graphics and keyboard uninstalled and a serial terminal used instead.
 *
 * Key parts lists for the supported models are as follows.
 *
 * 2000 Turquoise (PCB962 rev A, PCB824 rev J)
 *
 *   Ref   Part                      Function
 *   U37   Intel 82072               Floppy drive controller
 *   U39   Intel 82586               Ethernet controller
 *   U40   Zilog 8530 SCC            Keyboard and console serial controller
 *   U41   Zilog 8530 SCC            Serial controller for serial port 0 and 1
 *   U42   Xilinx XC3020-50          Plotter control FPGA?
 *   U43   (MPRGM610C)               Bitstream for XC3020?
 *   U54   4.9152 MHz crystal        Clock source for 8530s?
 *   U55   20.0 MHz crystal
 *   U57   24.0 MHz crystal          Clock source for 53C90A?
 *   U61   NCR 53C90A                SCSI controller
 *   U63   CIDC84607 TC110G75CY-0011 Intergraph I/O gate array?
 *   U116  Dallas DS1287             RTC and NVRAM
 *   U137?                           diagnostic 7-segment LED?
 *   U171  128 kB EPROM (MPRGM530E)  Boot ROM MSB
 *   U172  128 kB EPROM (MPRGM540E)  Boot ROM LSB
 *
 * 2400 Sapphire (SMT047 rev 0, SMT038?)
 *
 *   Ref   Part                      Function
 *   U31   Zilog Z85C30 SCC          Keyboard and console serial controller
 *   U32   Zilog Z85230 ESCC         Serial controller for serial port 0 and 1
 *   U34   Xilinx XC3020-50          Plotter control FPGA?
 *   U35   128 kB EPROM (MPRGW510B)  Boot ROM
 *   U43?  (MPRGM610P)               Bitstream for XC3020?
 *   U44   Intel 82596SX-20          Ethernet controller
 *   U67   Intel N28F010-200         128Kx8 flash memory (Y226 0B03 0592)
 *   U68   CYID21603 TC150G89AF
 *   U71   LSI L1A6104 CICD 95801    Intergraph I/O gate array
 *   U76   Intel N28F010-200         128Kx8 flash memory (Y225 0B?? 27??)
 *   U81   NCR 53C94                 SCSI controller
 *   U86   24.0 MHz crystal          Clock source for 53C94?
 *   U87   4.9152 MHz crystal        Clock source for 8530s?
 *   U88   20.0 MHz crystal          Clock source for 82596?
 *   U91   Intel N82077AA-1          Floppy drive controller
 *   U96   32.0 MHz crystal
 *   U97   40.0 MHz crystal
 *   U112? (MPRG4230A)               node ID prom?
 *   U113? Dallas DS1287             RTC and NVRAM
 *   U117?                           diagnostic 7-segment LED?
 *   U118? (MPRG X510R)
 *   U155  CYID21704 TC140G54AF
 *
 * 2700 Sapphire (SMT128 rev 0, SMT104 rev A)
 *
 *   Ref   Part                      Function
 *   U31   Zilog Z85C30 SCC          Serial controller for serial port 0 and 1
 *   U32   Zilog Z85230 ESCC         Keyboard and console serial controller
 *   U34   Xilinx XC3020-70          Plotter control FPGA?
 *   U35   128 kB EPROM (MPRGZ530A)  Boot ROM
 *   U43   GAL16V8B (MPRGM610P)      Bitstream for XC3020?
 *   U44   Intel 82596SX-20          Ethernet controller
 *   U68   CYID21603 TC150G89AF
 *   U67   Intel N28F010             128Kx8 flash memory (Y226 0C30 4291)
 *   U71   LSI L1A7374 CICD094A3     Intergraph I/O gate array
 *   U76   Intel N28F010             128Kx8 flash memory (Y225 0C30 4220)
 *   U81   NCR 53C94                 SCSI controller
 *   U86   24.0 MHz crystal          Clock source for 82077
 *   U87   4.9152 MHz crystal        Clock source for 8530s?
 *   U88   20.0 MHz crystal          Clock source for 82596?
 *   U91   Intel N82077SL-1          Floppy drive controller
 *   U96   29.0 MHz crystal
 *   U97   40.0 MHz crystal
 *   U112  82s123 (MPRG4230A)        node ID prom?
 *   U113  Dallas DS12887            RTC and NVRAM
 *   U117  [empty]                   diagnostic 7-segment LED?
 *   U118  82S123 (MPRGZ260F)
 *   U155  CYID21704 TC140G54AF
 *
 * 6000 (PCB765 rev B, PCB82409 rev D)
 *
 *   Ref   Part                      Function
 *   U264  Xilinx XC3020-50          Plotter control FPGA?
 *   U294  4.9152 MHz crystal
 *   U287  Z8530H-6JC                Serial controller
 *   U288  Z8530H-6JC                Serial controller
 *   U318  24.0 MHz crystal
 *   U323  20.0 MHz crystal
 *   U324  Intel 82586-10            Ethernet controller
 *   U303  CICD84607 TC110G75CY
 *   U311  NCR 53C90A                SCSI controller
 *   U336  27C010 (MPRGG360F)        128Kx8 Boot EPROM MSB
 *   U341  Dallas DS1287             RTC and NVRAM
 *   U349  27C010 (MPRGG350F)        128Kx8 Boot EPROM LSB
 *   U347  Intel 82072               Floppy drive controller
 *
 * 6400 (SMT046 rev B, SMT082)
 *
 * 6800 Sapphire (SMT127 rev B, SMT104 rev C)
 *
 *   Ref   Part                      Function
 *   U53   LSI L1A7751 CICD094A3     Intergraph I/O gate array
 *   U55   Xilinx XC3020-70          Plotter control FPGA?
 *   U57   CYID21704 TC140G54AF
 *   U69   CYID21603 TC150G89AF
 *   U73   20.0 MHz crystal          Clock source for 82596?
 *   U78   29.0 MHz crystal
 *   U88   4.9152 MHz crystal        Clock source for 8530s?
 *   U94   40.0 MHz crystal
 *   U95   CICD 89703 TC110G11AT
 *   U108  24.0 MHz crystal          Clock source for 82077
 *   U117  Intel N28F010             128Kx8 flash memory (Y225 0C40 6010)
 *   U118  Zilog Z85C30 SCC          Keyboard and console serial controller
 *   U119  Zilog Z85230 ESCC         Serial controller for serial port 0 and 1
 *   U130  Intel N28F010             128Kx8 flash memory (Y226 0C40 6280)
 *   U136  NCR 53C94                 SCSI controller
 *   U144  27C1024 (MPRGZ530A)       64Kx16 Boot EPROM
 *   U145  Dallas DS12887            RTC and NVRAM
 *   U159  Intel 82596SX-20          Ethernet controller
 *   U164  Intel N82077SL-1?         Floppy drive controller
 *
 * CPU daughter-boards
 *
 *   PCB824 Rev J (MPCBA5507)
 *   CPCB82409 rev D (MPCB92108) - 6000 C311 + 2xC322 + 80MHz crystal
 *
 *   SMT082 (MSMT0820B, 36MHz?) - 6400 (SMT046 "6400 36-MHz Series System Board")
 *   SMT03? 2400/6400 - (MSMT03804 -> rev 2 cammu, goes with "6400 36-MHz Series System Board", MSMT0380A eco 3+ use rev 3 cammu)
 *   SMT019 (MSMT019 C4E CPU Assembly)
 *   SMT104 Rev A - 2700/6700 (aka MSMT1040A "C4I CPU", C4 CPU REV 3 + C4 FPU REV 3 + C4I CAMMU)
 *
 *   PCB962   2000 System Board                  MPCB824 C300
 *   PCB???   6000 System Board w/?MB            MPCB824 C300
 *   SMT046   6400 36-MHz Series System Board    MSMT03804 rev 2 CAMMU/30MHz Kryptonite Rev 3 CAMMU/32MHz Kryptonite Rev 3 CAMMU/MSMT0820B(36MHz)
 *   SMT047   2400 Series System Board           MSMT03804 rev 2 CAMMU/MSMT0380A eco 3+ Rev 3 CAMMU
 *   SMT098A  6400 32-MHz Sapphire System Board
 *   SMT098B  6400 32-MHz Sapphire System Board
 *   SMT127   6700 Series System Board           MSMT1040A C4I: C4 CPU Rev 3 + C4 FPU Rev 3 + C4I CAMMU
 *   SMT128   2700 Series System Board           MSMT1040A C4I: C4 CPU Rev 3 + C4 FPU Rev 3 + C4I CAMMU
 *   SMT144   6800 Series System Board           integrated cpu?
 *   SMT145   2800 Series System Board
 */

#include "emu.h"

#include "cpu/clipper/clipper.h"
#include "machine/cammu.h"

#include "interpro_ioga.h"
#include "interpro_mcga.h"
#include "interpro_sga.h"
#include "interpro_arbga.h"

#include "imagedev/floppy.h"
#include "machine/ram.h"
#include "machine/28fxxx.h"
#include "machine/mc146818.h"
#include "machine/z80scc.h"
#include "machine/upd765.h"
#include "machine/i82586.h"

#include "machine/ncr53c90.h"
#include "machine/nscsi_bus.h"
#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"

#include "bus/rs232/rs232.h"

#include "bus/interpro/sr/sr.h"
#include "bus/interpro/sr/sr_cards.h"
#include "bus/interpro/keyboard/keyboard.h"
#include "bus/interpro/mouse/mouse.h"

#include "softlist.h"

#include "machine/input_merger.h"

#include "debugger.h"

#include "interpro.lh"

#define VERBOSE 0
#include "logmacro.h"

namespace {

class interpro_state : public driver_device
{
public:
	interpro_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "cpu")
		, m_ram(*this, "ram")
		, m_mcga(*this, "mcga")
		, m_sga(*this, "sga")
		, m_fdc(*this, "fdc")
		, m_scc1(*this, "scc1")
		, m_scc2(*this, "scc2")
		, m_rtc(*this, "rtc")
		, m_scsibus(*this, "scsi")
		, m_eth(*this, "eth")
		, m_ioga(*this, "ioga")
		, m_eprom(*this, "eprom")
		, m_softlist(*this, "softlist")
		, m_diag_led(*this, "digit0")
	{
	}

	required_device<clipper_device> m_maincpu;
	required_device<ram_device> m_ram;

	required_device<interpro_mcga_device> m_mcga;
	required_device<interpro_sga_device> m_sga;
	required_device<upd765_family_device> m_fdc;
	required_device<z80scc_device> m_scc1;
	required_device<z80scc_device> m_scc2;
	required_device<mc146818_device> m_rtc;
	required_device<nscsi_bus_device> m_scsibus;
	required_device<i82586_base_device> m_eth;
	required_device<interpro_ioga_device> m_ioga;
	required_region_ptr<u16> m_eprom;

	required_device<software_list_device> m_softlist;

	void init_common();

	virtual u32 unmapped_r(address_space &space, offs_t offset);
	virtual void unmapped_w(offs_t offset, u32 data);

	enum error_mask : u16
	{
		ERROR_BPID4    = 0x0001,
		ERROR_SRXMMBE  = 0x0002,
		ERROR_SRXHOG   = 0x0004,
		ERROR_SRXNEM   = 0x0008,
		ERROR_SRXVALID = 0x0010,
		ERROR_CBUSNMI  = 0x0020,
		ERROR_CBUSBG   = 0x00c0,
		ERROR_BG       = 0x0070,
		ERROR_BUSHOG   = 0x0080
	};
	u16 error_r();

	void led_w(offs_t offset, u16 data, u16 mem_mask = ~0);

	enum status_mask : u16
	{
		STATUS_YELLOW_ZONE = 0x0001,
		STATUS_SRNMI       = 0x0002,
		STATUS_PWRLOSS     = 0x0004,
		STATUS_RED_ZONE    = 0x0008,
		STATUS_BP          = 0x00f0
	};
	u16 status_r() { return m_status; }

	virtual u16 ctrl1_r() = 0;
	virtual void ctrl1_w(offs_t offset, u16 data, u16 mem_mask = ~0) = 0;
	virtual u16 ctrl2_r() = 0;
	virtual void ctrl2_w(offs_t offset, u16 data, u16 mem_mask = ~0) = 0;

	u8 nodeid_r(address_space &space, offs_t offset);

	void ioga(machine_config &config);
	void interpro_serial(machine_config &config);
	void interpro(machine_config &config);
	static void interpro_scsi_adapter(device_t *device);
	static void interpro_cdrom(device_t *device);
	void interpro_boot_map(address_map &map) ATTR_COLD;
	void interpro_common_map(address_map &map) ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	output_finder<> m_diag_led;
	emu_timer *m_reset_timer = nullptr;

	u16 m_error = 0;
	u16 m_status = 0;
	u16 m_led = 0;
};

class emerald_state : public interpro_state
{
public:
	emerald_state(const machine_config &mconfig, device_type type, const char *tag)
		: interpro_state(mconfig, type, tag)
		, m_d_cammu(*this, "cammu_d")
		, m_i_cammu(*this, "cammu_i")
		, m_scsi(*this, "scsi:7:host")
		, m_bus(*this, "slot")
	{
	}

	void error_w(u8 data) { m_error = data; }

	enum ctrl1_mask : u16
	{
		CTRL1_FLOPLOW    = 0x0001, // 3.5" floppy select
		CTRL1_FLOPRDY    = 0x0002, // floppy ready enable?
		CTRL1_LEDENA     = 0x0004, // led display enable
		CTRL1_LEDDP      = 0x0008, // led right decimal point enable
		CTRL1_ETHLOOP    = 0x0010, // ethernet loopback enable?
		CTRL1_ETHDTR     = 0x0020, // modem dtr pin enable?
		CTRL1_ETHRMOD    = 0x0040, // remote modem configured (read)?
		CTRL1_CLIPRESET  = 0x0040, // hard reset (write)?
		CTRL1_FIFOACTIVE = 0x0080  // plotter fifo active?
	};
	u16 ctrl1_r() override { return m_ctrl1; }
	void ctrl1_w(offs_t offset, u16 data, u16 mem_mask = ~0) override;

	enum ctrl2_mask : u16
	{
		CTRL2_PWRUP     = 0x0001, // power supply voltage adjust?
		CRTL2_PWRENA    = 0x0002, // ?
		CTRL2_HOLDOFF   = 0x0004, // power supply shut down delay
		CTRL2_EXTNMIENA = 0x0008, // power nmi enable
		CTRL2_COLDSTART = 0x0010, // cold start flag
		CTRL2_RESET     = 0x0020, // soft reset
		CTRL2_BUSENA    = 0x0040, // clear bus grant error
		CTRL2_FRCPARITY = 0x0080, // ?

		CTRL2_WMASK     = 0x000f
	};
	u16 ctrl2_r() override { return m_ctrl2; }
	void ctrl2_w(offs_t offset, u16 data, u16 mem_mask = ~0) override;

	required_device<cammu_c3_device> m_d_cammu;
	required_device<cammu_c3_device> m_i_cammu;
	required_device<ncr53c90a_device> m_scsi;
	required_device<srx_bus_device> m_bus;

	void emerald(machine_config &config);
	void ip6000(machine_config &config);
	void interpro_82586_map(address_map &map) ATTR_COLD;
	void emerald_base_map(address_map &map) ATTR_COLD;
	void emerald_main_map(address_map &map) ATTR_COLD;
	void emerald_io_map(address_map &map) ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u16 m_ctrl1 = 0;
	u16 m_ctrl2 = 0;
};

class turquoise_state : public interpro_state
{
public:
	turquoise_state(const machine_config &mconfig, device_type type, const char *tag)
		: interpro_state(mconfig, type, tag)
		, m_d_cammu(*this, "cammu_d")
		, m_i_cammu(*this, "cammu_i")
		, m_kbd_port(*this, "kbd")
		, m_mse_port(*this, "mse")
		, m_scsi(*this, "scsi:7:host")
		, m_bus(*this, "slot")
	{
	}

	void error_w(u8 data) { m_error = data; }

	enum ctrl1_mask : u16
	{
		CTRL1_FLOPLOW    = 0x0001, // 3.5" floppy select
		CTRL1_FLOPRDY    = 0x0002, // floppy ready enable?
		CTRL1_LEDENA     = 0x0004, // led display enable
		CTRL1_LEDDP      = 0x0008, // led right decimal point enable
		CTRL1_ETHLOOP    = 0x0010, // ethernet loopback enable?
		CTRL1_ETHDTR     = 0x0020, // modem dtr pin enable?
		CTRL1_ETHRMOD    = 0x0040, // remote modem configured (read)?
		CTRL1_CLIPRESET  = 0x0040, // hard reset (write)?
		CTRL1_FIFOACTIVE = 0x0080  // plotter fifo active?
	};
	u16 ctrl1_r() override { return m_ctrl1; }
	void ctrl1_w(offs_t offset, u16 data, u16 mem_mask = ~0) override;

	enum ctrl2_mask : u16
	{
		CTRL2_PWRUP     = 0x0001, // power supply voltage adjust?
		CRTL2_PWRENA    = 0x0002, // ?
		CTRL2_HOLDOFF   = 0x0004, // power supply shut down delay
		CTRL2_EXTNMIENA = 0x0008, // power nmi enable
		CTRL2_COLDSTART = 0x0010, // cold start flag
		CTRL2_RESET     = 0x0020, // soft reset
		CTRL2_BUSENA    = 0x0040, // clear bus grant error
		CTRL2_FRCPARITY = 0x0080, // ?

		CTRL2_WMASK     = 0x000f
	};
	u16 ctrl2_r() override { return m_ctrl2; }
	void ctrl2_w(offs_t offset, u16 data, u16 mem_mask = ~0) override;

	required_device<cammu_c3_device> m_d_cammu;
	required_device<cammu_c3_device> m_i_cammu;
	required_device<interpro_keyboard_port_device> m_kbd_port;
	required_device<interpro_mouse_port_device> m_mse_port;
	required_device<ncr53c90a_device> m_scsi;
	required_device<cbus_bus_device> m_bus;

	void turquoise(machine_config &config);
	void ip2000(machine_config &config);
	void interpro_82586_map(address_map &map) ATTR_COLD;
	void turquoise_base_map(address_map &map) ATTR_COLD;
	void turquoise_main_map(address_map &map) ATTR_COLD;
	void turquoise_io_map(address_map &map) ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u16 m_ctrl1 = 0;
	u16 m_ctrl2 = 0;
};

class sapphire_state : public interpro_state
{
public:
	sapphire_state(const machine_config &mconfig, device_type type, const char *tag)
		: interpro_state(mconfig, type, tag)
		, m_mmu(*this, "cammu")
		, m_scsi(*this, "scsi:7:host")
		, m_arbga(*this, "arbga")
		, m_flash_lsb(*this, "flash_lsb")
		, m_flash_msb(*this, "flash_msb")
	{
	}

	virtual u32 unmapped_r(address_space &space, offs_t offset) override;
	virtual void unmapped_w(offs_t offset, u32 data) override;

	enum ctrl1_mask : u16
	{
		CTRL1_FLOPLOW    = 0x0001, // 3.5" floppy select
								   // unused
		CTRL1_LEDENA     = 0x0004, // led display enable
		CTRL1_LEDDP      = 0x0008, // led right decimal point enable
		CTRL1_MMBE       = 0x0010, // mmbe enable
		CTRL1_ETHDTR     = 0x0020, // modem dtr pin enable
		CTRL1_ETHRMOD    = 0x0040, // 0 = sytem configured for remote modems
		CTRL1_FIFOACTIVE = 0x0080  // 0 = plotter fifos reset
	};
	u16 ctrl1_r() override { return m_ctrl1; }
	void ctrl1_w(offs_t offset, u16 data, u16 mem_mask = ~0) override;

	enum ctrl2_mask : u16
	{
		CTRL2_PWRUP     = 0x0003, // power supply voltage adjust
		CTRL2_HOLDOFF   = 0x0004, // power supply shut down delay
		CTRL2_EXTNMIENA = 0x0008, // power nmi enable
		CTRL2_COLDSTART = 0x0010, // cold start flag
		CTRL2_RESET     = 0x0020, // soft reset
		CTRL2_BUSENA    = 0x0040, // clear bus grant error
		CTRL2_FLASHEN   = 0x0080, // flash eprom write enable
	};
	u16 ctrl2_r() override { return m_ctrl2; }
	void ctrl2_w(offs_t offset, u16 data, u16 mem_mask = ~0) override;

	required_device<cammu_c4_device> m_mmu;
	required_device<ncr53c94_device> m_scsi;
	required_device<interpro_arbga_device> m_arbga;
	required_device<intel_28f010_device> m_flash_lsb;
	required_device<intel_28f010_device> m_flash_msb;

	void sapphire(machine_config &config);

	void interpro_82596_map(address_map &map) ATTR_COLD;
	void sapphire_base_map(address_map &map) ATTR_COLD;
	void sapphire_main_map(address_map &map) ATTR_COLD;
	void sapphire_io_map(address_map &map) ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u16 m_ctrl1 = 0;
	u16 m_ctrl2 = 0;
};

class cbus_sapphire_state : public sapphire_state
{
public:
	cbus_sapphire_state(const machine_config &mconfig, device_type type, const char *tag)
		: sapphire_state(mconfig, type, tag)
		, m_kbd_port(*this, "kbd")
		, m_mse_port(*this, "mse")
		, m_bus(*this, "slot")
	{
	}

	void cbus_sapphire(machine_config &config);

	void ip2500(machine_config &config);
	void ip2400(machine_config &config);
	void ip2700(machine_config &config);
	void ip2800(machine_config &config);

protected:
	required_device<interpro_keyboard_port_device> m_kbd_port;
	required_device<interpro_mouse_port_device> m_mse_port;
	required_device<cbus_bus_device> m_bus;
};

class srx_sapphire_state : public sapphire_state
{
public:
	srx_sapphire_state(const machine_config &mconfig, device_type type, const char *tag)
		: sapphire_state(mconfig, type, tag)
		, m_bus(*this, "slot")
	{
	}

	void srx_sapphire(machine_config &config);

	void ip6400(machine_config &config);
	void ip6700(machine_config &config);
	void ip6800(machine_config &config);

protected:
	required_device<srx_bus_device> m_bus;
};

void interpro_state::machine_start()
{
	m_diag_led.resolve();

	save_item(NAME(m_error));
	save_item(NAME(m_status));
	save_item(NAME(m_led));
}

void interpro_state::machine_reset()
{
	m_error = 0;
	m_status = 0;
	m_led = 0;
}

void emerald_state::machine_start()
{
	interpro_state::machine_start();

	save_item(NAME(m_ctrl1));
	save_item(NAME(m_ctrl2));

	m_ctrl1 = 0;
	// FIXME: disabled for now to avoid cold start diagnostic errors
	m_ctrl2 = 0; // CTRL2_COLDSTART
}

void turquoise_state::machine_start()
{
	interpro_state::machine_start();

	save_item(NAME(m_ctrl1));
	save_item(NAME(m_ctrl2));

	m_ctrl1 = 0;
	// FIXME: disabled for now to avoid cold start diagnostic errors
	m_ctrl2 = 0; // CTRL2_COLDSTART
}

void sapphire_state::machine_start()
{
	interpro_state::machine_start();

	save_item(NAME(m_ctrl1));
	save_item(NAME(m_ctrl2));

	m_ctrl1 = 0;
	// FIXME: disabled for now to avoid cold start diagnostic errors
	m_ctrl2 = 0; // CTRL2_COLDSTART
}

void emerald_state::machine_reset()
{
	interpro_state::machine_reset();

	// deassert floppy ready
	m_fdc->ready_w(true);
}

void turquoise_state::machine_reset()
{
	interpro_state::machine_reset();

	// deassert floppy ready
	m_fdc->ready_w(true);
}

void sapphire_state::machine_reset()
{
	interpro_state::machine_reset();

	// flash rom requires the following
	m_status = 0x400;
}

void interpro_state::init_common()
{
	// FIXME: not all memory sizes are reported properly using fdm "5 inqhw" and
	// "optimum_memory" commands

	// 16 = reports 16M, banks empty?
	// 32 = reports 16M, banks empty?
	// 64 = reports 128M, 16x8
	// 128 = reports 128M, 16x8
	// 256 = reports 256M, 32x8

	// map the configured ram
	m_maincpu->space(0).install_ram(0, m_ram->mask(), m_ram->pointer());
}

void interpro_state::led_w(offs_t offset, u16 data, u16 mem_mask)
{
	// 7-segment decode patterns (hex digits) borrowed from wico.cpp (mc14495)
	static const u8 patterns[16] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71 };

	m_diag_led = patterns[data & 0xf];

	COMBINE_DATA(&m_led);
}

void emerald_state::ctrl1_w(offs_t offset, u16 data, u16 mem_mask)
{
	LOG("control register 1 data 0x%04x mem_mask 0x%04x (%s)\n", data, mem_mask, machine().describe_context());

	// check if led decimal point changes state
	if ((data ^ m_ctrl1) & CTRL1_LEDDP)
		m_led = (m_led + 0x80) & 0xff;

	// FIXME: select 3.5" or 5.25" floppy drive?
	//if ((data ^ m_sreg_ctrl1) & CTRL1_FLOPLOW)
	//  logerror("floplow %d\n", data & CTRL1_FLOPLOW ? 1 : 0);

	// FIXME: floppy ready line handling - this is strange but working
	if (data & CTRL1_FLOPRDY)
		m_fdc->ready_w(!(data & CTRL1_FLOPRDY));

	COMBINE_DATA(&m_ctrl1);
}

void turquoise_state::ctrl1_w(offs_t offset, u16 data, u16 mem_mask)
{
	LOG("control register 1 data 0x%04x mem_mask 0x%04x (%s)\n", data, mem_mask, machine().describe_context());

	// check if led decimal point changes state
	if ((data ^ m_ctrl1) & CTRL1_LEDDP)
		m_led = (m_led + 0x80) & 0xff;

	// FIXME: select 3.5" or 5.25" floppy drive?
	//if ((data ^ m_sreg_ctrl1) & CTRL1_FLOPLOW)
	//  logerror("floplow %d\n", data & CTRL1_FLOPLOW ? 1 : 0);

	// FIXME: floppy ready line handling - this is strange but working
	if (data & CTRL1_FLOPRDY)
		m_fdc->ready_w(!(data & CTRL1_FLOPRDY));

	COMBINE_DATA(&m_ctrl1);
}

void sapphire_state::ctrl1_w(offs_t offset, u16 data, u16 mem_mask)
{
	LOG("control register 1 data 0x%04x mem_mask 0x%04x (%s)\n", data, mem_mask, machine().describe_context());

	// check if led decimal point changes state
	if ((data ^ m_ctrl1) & CTRL1_LEDDP)
		m_led = (m_led + 0x80) & 0xff;

	// FIXME: select 3.5" or 5.25" floppy drive?
	//if ((data ^ m_ctrl1) & CTRL1_FLOPLOW)
	//  logerror("floplow %d\n", data & CTRL1_FLOPLOW ? 1 : 0);

	COMBINE_DATA(&m_ctrl1);
}

void emerald_state::ctrl2_w(offs_t offset, u16 data, u16 mem_mask)
{
	LOG("control register 2 data 0x%04x mem_mask 0x%04x (%s)\n", data, mem_mask, machine().describe_context());
	if (data & CTRL2_RESET)
	{
		m_ctrl2 &= ~CTRL2_COLDSTART;

		machine().schedule_soft_reset();
	}
	else
		m_ctrl2 = (m_ctrl2 & ~CTRL2_WMASK) | (data & CTRL2_WMASK);
}

void turquoise_state::ctrl2_w(offs_t offset, u16 data, u16 mem_mask)
{
	LOG("control register 2 data 0x%04x mem_mask 0x%04x (%s)\n", data, mem_mask, machine().describe_context());
	if (data & CTRL2_RESET)
	{
		m_ctrl2 &= ~CTRL2_COLDSTART;

		machine().schedule_soft_reset();
	}
	else
		m_ctrl2 = (m_ctrl2 & ~CTRL2_WMASK) | (data & CTRL2_WMASK);
}

void sapphire_state::ctrl2_w(offs_t offset, u16 data, u16 mem_mask)
{
	LOG("control register 2 data 0x%04x mem_mask 0x%04x (%s)\n", data, mem_mask, machine().describe_context());
	if (data & CTRL2_RESET)
	{
		m_ctrl2 &= ~CTRL2_COLDSTART;

		machine().schedule_soft_reset();
	}
	else
		m_ctrl2 = (m_ctrl2 & ~0x4d) | (data & 0x4d);

	// enable/disable programming power on both flash devices
	m_flash_lsb->vpp(data & CTRL2_FLASHEN ? ASSERT_LINE : CLEAR_LINE);
	m_flash_msb->vpp(data & CTRL2_FLASHEN ? ASSERT_LINE : CLEAR_LINE);
}

u16 interpro_state::error_r()
{
	const u16 result = m_error;

	// clear error register on read
	if (!machine().side_effects_disabled())
		m_error = 0;

	return result;
}

u32 interpro_state::unmapped_r(address_space &space, offs_t offset)
{
	if (!machine().side_effects_disabled())
	{
		// flag non-existent memory error in system error register
		m_error |= (ERROR_SRXNEM | ERROR_SRXVALID);

		// tell ioga to raise a bus error
		m_ioga->bus_error(interpro_ioga_device::BINFO_BERR | interpro_ioga_device::BINFO_SNAPOK, offset << 2);
	}

	return space.unmap();
}

void interpro_state::unmapped_w(offs_t offset, u32 data)
{
	if (!machine().side_effects_disabled())
	{
		// flag non-existent memory error in system error register
		m_error |= (ERROR_SRXNEM | ERROR_SRXVALID);

		// tell ioga to raise a bus error
		m_ioga->bus_error(interpro_ioga_device::BINFO_BERR | interpro_ioga_device::BINFO_SNAPOK, offset << 2);
	}
}

u32 sapphire_state::unmapped_r(address_space &space, offs_t offset)
{
	// check if non-existent memory errors are enabled
	if (!machine().side_effects_disabled())
		if (m_arbga->tctrl_r() & interpro_arbga_device::TCTRL_ENNEM)
		{
			// flag non-existent memory error in system error register
			m_error |= (ERROR_SRXNEM | ERROR_SRXVALID);

			// tell ioga to raise a bus error
			m_ioga->bus_error(interpro_ioga_device::BINFO_BERR | interpro_ioga_device::BINFO_SNAPOK, offset << 2);
		}

	return space.unmap();
}

void sapphire_state::unmapped_w(offs_t offset, u32 data)
{
	// check if non-existent memory errors are enabled
	if (m_arbga->tctrl_r() & interpro_arbga_device::TCTRL_ENNEM)
	{
		// flag non-existent memory error in system error register
		m_error |= (ERROR_SRXNEM | ERROR_SRXVALID);

		// tell ioga to raise a bus error
		m_ioga->bus_error(interpro_ioga_device::BINFO_BERR | interpro_ioga_device::BINFO_SNAPOK, offset << 2);
	}
}

u8 interpro_state::nodeid_r(address_space &space, offs_t offset)
{
	// FIXME: hard coded node id for now
	switch (offset)
	{
		// read system node id prom (contains last 3 bytes of mac address)
	case 0: return 0x12;
	case 1: return 0x34;
	case 2: return 0x56;
	case 3: return 0x9c; // checksum - sum of other bytes
	}

	return space.unmap();
}

void interpro_state::interpro_common_map(address_map &map)
{
	//map(0x00000000, 0xffffffff).rw(FUNC(interpro_state::unmapped_r), FUNC(interpro_state::unmapped_w));

	// FIXME: don't know what this range is for
	map(0x08000000, 0x08000fff).noprw();

	map(0x4f007e00, 0x4f007eff).m(m_sga, FUNC(interpro_sga_device::map));

	map(0x7f000100, 0x7f00011f).m(m_fdc, FUNC(upd765_family_device::map)).umask32(0x000000ff);
	map(0x7f000300, 0x7f000301).r(FUNC(interpro_state::error_r));
	map(0x7f000304, 0x7f000305).rw(FUNC(interpro_state::status_r), FUNC(interpro_state::led_w));
	map(0x7f000308, 0x7f000309).rw(FUNC(interpro_state::ctrl1_r), FUNC(interpro_state::ctrl1_w));
	map(0x7f00030c, 0x7f00030d).rw(FUNC(interpro_state::ctrl2_r), FUNC(interpro_state::ctrl2_w));

	map(0x7f000400, 0x7f00040f).rw(m_scc1, FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w)).umask32(0x000000ff);
	map(0x7f000410, 0x7f00041f).rw(m_scc2, FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w)).umask32(0x000000ff);
	map(0x7f000500, 0x7f000500).rw(m_rtc, FUNC(mc146818_device::data_r), FUNC(mc146818_device::data_w));
	map(0x7f000600, 0x7f000600).w(m_rtc, FUNC(mc146818_device::address_w));

	// the system board id prom
	map(0x7f000700, 0x7f00077f).rom().region("idprom", 0);

	map(0x7f0fff00, 0x7f0fffff).m(m_ioga, FUNC(interpro_ioga_device::map));
}

void emerald_state::emerald_base_map(address_map &map)
{
	interpro_common_map(map);

	map(0x40000000, 0x4000003f).m(m_mcga, FUNC(interpro_mcga_device::map));

	// scsi registers have unusual address mapping
	map(0x7f000201, 0x7f000201).rw(m_scsi, FUNC(ncr53c90a_device::tcounter_lo_r), FUNC(ncr53c90a_device::tcount_lo_w));
	map(0x7f000205, 0x7f000205).rw(m_scsi, FUNC(ncr53c90a_device::tcounter_hi_r), FUNC(ncr53c90a_device::tcount_hi_w));
	map(0x7f000209, 0x7f000209).rw(m_scsi, FUNC(ncr53c90a_device::fifo_r), FUNC(ncr53c90a_device::fifo_w));
	map(0x7f00020d, 0x7f00020d).rw(m_scsi, FUNC(ncr53c90a_device::command_r), FUNC(ncr53c90a_device::command_w));
	map(0x7f000211, 0x7f000211).rw(m_scsi, FUNC(ncr53c90a_device::status_r), FUNC(ncr53c90a_device::bus_id_w));
	map(0x7f000215, 0x7f000215).rw(m_scsi, FUNC(ncr53c90a_device::istatus_r), FUNC(ncr53c90a_device::timeout_w));
	map(0x7f000219, 0x7f000219).rw(m_scsi, FUNC(ncr53c90a_device::seq_step_r), FUNC(ncr53c90a_device::sync_period_w));
	map(0x7f00021d, 0x7f00021d).rw(m_scsi, FUNC(ncr53c90a_device::fifo_flags_r), FUNC(ncr53c90a_device::sync_offset_w));
	map(0x7f000221, 0x7f000221).rw(m_scsi, FUNC(ncr53c90a_device::conf_r), FUNC(ncr53c90a_device::conf_w));
	map(0x7f000225, 0x7f000225).w(m_scsi, FUNC(ncr53c90a_device::clock_w));
	map(0x7f000229, 0x7f000229).w(m_scsi, FUNC(ncr53c90a_device::test_w));
	map(0x7f00022d, 0x7f00022d).rw(m_scsi, FUNC(ncr53c90a_device::conf2_r), FUNC(ncr53c90a_device::conf2_w));

	map(0x7f000300, 0x7f000300).w(FUNC(emerald_state::error_w));

	map(0x7f000600, 0x7f00067f).rom().region("nodeid", 0);
}

void emerald_state::emerald_main_map(address_map &map)
{
	//map(0x00000000, 0xffffffff).rw(FUNC(interpro_state::unmapped_r), FUNC(interpro_state::unmapped_w));

	emerald_base_map(map);

	map(0x00000000, 0x00ffffff).ram().share("ram");
	map(0x7f100000, 0x7f13ffff).lr16([this] (offs_t offset) { return m_eprom[offset]; }, "eprom");
}

void turquoise_state::turquoise_base_map(address_map &map)
{
	interpro_common_map(map);

	map(0x40000000, 0x4000003f).m(m_mcga, FUNC(interpro_mcga_device::map));

	// scsi registers have unusual address mapping
	map(0x7f000201, 0x7f000201).rw(m_scsi, FUNC(ncr53c90a_device::tcounter_lo_r), FUNC(ncr53c90a_device::tcount_lo_w));
	map(0x7f000205, 0x7f000205).rw(m_scsi, FUNC(ncr53c90a_device::tcounter_hi_r), FUNC(ncr53c90a_device::tcount_hi_w));
	map(0x7f000209, 0x7f000209).rw(m_scsi, FUNC(ncr53c90a_device::fifo_r), FUNC(ncr53c90a_device::fifo_w));
	map(0x7f00020d, 0x7f00020d).rw(m_scsi, FUNC(ncr53c90a_device::command_r), FUNC(ncr53c90a_device::command_w));
	map(0x7f000211, 0x7f000211).rw(m_scsi, FUNC(ncr53c90a_device::status_r), FUNC(ncr53c90a_device::bus_id_w));
	map(0x7f000215, 0x7f000215).rw(m_scsi, FUNC(ncr53c90a_device::istatus_r), FUNC(ncr53c90a_device::timeout_w));
	map(0x7f000219, 0x7f000219).rw(m_scsi, FUNC(ncr53c90a_device::seq_step_r), FUNC(ncr53c90a_device::sync_period_w));
	map(0x7f00021d, 0x7f00021d).rw(m_scsi, FUNC(ncr53c90a_device::fifo_flags_r), FUNC(ncr53c90a_device::sync_offset_w));
	map(0x7f000221, 0x7f000221).rw(m_scsi, FUNC(ncr53c90a_device::conf_r), FUNC(ncr53c90a_device::conf_w));
	map(0x7f000225, 0x7f000225).w(m_scsi, FUNC(ncr53c90a_device::clock_w));
	map(0x7f000229, 0x7f000229).w(m_scsi, FUNC(ncr53c90a_device::test_w));
	map(0x7f00022d, 0x7f00022d).rw(m_scsi, FUNC(ncr53c90a_device::conf2_r), FUNC(ncr53c90a_device::conf2_w));

	map(0x7f000300, 0x7f000300).w(FUNC(turquoise_state::error_w));

	map(0x7f000600, 0x7f00067f).rom().region("nodeid", 0);
}

void turquoise_state::turquoise_main_map(address_map &map)
{
	//map(0x00000000, 0xffffffff).rw(FUNC(interpro_state::unmapped_r), FUNC(interpro_state::unmapped_w));

	turquoise_base_map(map);

	map(0x00000000, 0x00ffffff).ram().share("ram");
	map(0x7f100000, 0x7f13ffff).lr16([this] (offs_t offset) { return m_eprom[offset]; }, "eprom");
}

void sapphire_state::sapphire_base_map(address_map &map)
{
	interpro_common_map(map);

	map(0x40000000, 0x4000004f).m(m_mcga, FUNC(interpro_fmcc_device::map));
	map(0x7f000200, 0x7f0002ff).m(m_arbga, FUNC(interpro_arbga_device::map));

	map(0x7f000600, 0x7f00060f).r(FUNC(sapphire_state::nodeid_r)).umask32(0x000000ff);

	// scsi registers have unusual address mapping
	map(0x7f001001, 0x7f001001).rw(m_scsi, FUNC(ncr53c94_device::tcounter_lo_r), FUNC(ncr53c94_device::tcount_lo_w));
	map(0x7f001101, 0x7f001101).rw(m_scsi, FUNC(ncr53c94_device::tcounter_hi_r), FUNC(ncr53c94_device::tcount_hi_w));
	map(0x7f001201, 0x7f001201).rw(m_scsi, FUNC(ncr53c94_device::fifo_r), FUNC(ncr53c94_device::fifo_w));
	map(0x7f001301, 0x7f001301).rw(m_scsi, FUNC(ncr53c94_device::command_r), FUNC(ncr53c94_device::command_w));
	map(0x7f001401, 0x7f001401).rw(m_scsi, FUNC(ncr53c94_device::status_r), FUNC(ncr53c94_device::bus_id_w));
	map(0x7f001501, 0x7f001501).rw(m_scsi, FUNC(ncr53c94_device::istatus_r), FUNC(ncr53c94_device::timeout_w));
	map(0x7f001601, 0x7f001601).rw(m_scsi, FUNC(ncr53c94_device::seq_step_r), FUNC(ncr53c94_device::sync_period_w));
	map(0x7f001701, 0x7f001701).rw(m_scsi, FUNC(ncr53c94_device::fifo_flags_r), FUNC(ncr53c94_device::sync_offset_w));
	map(0x7f001801, 0x7f001801).rw(m_scsi, FUNC(ncr53c94_device::conf_r), FUNC(ncr53c94_device::conf_w));
	map(0x7f001901, 0x7f001901).w(m_scsi, FUNC(ncr53c94_device::clock_w));
	map(0x7f001a01, 0x7f001a01).w(m_scsi, FUNC(ncr53c94_device::test_w));
	map(0x7f001b01, 0x7f001b01).rw(m_scsi, FUNC(ncr53c94_device::conf2_r), FUNC(ncr53c94_device::conf2_w));
	map(0x7f001c01, 0x7f001c01).w(m_scsi, FUNC(ncr53c94_device::conf3_w));
	map(0x7f001f01, 0x7f001f01).w(m_scsi, FUNC(ncr53c94_device::fifo_align_w));
}

void sapphire_state::sapphire_main_map(address_map &map)
{
	//map(0x00000000, 0xffffffff).rw(FUNC(sapphire_state::unmapped_r), FUNC(sapphire_state::unmapped_w));

	sapphire_base_map(map);

	map(0x00000000, 0x00ffffff).ram().share("ram");
	map(0x7f100000, 0x7f11ffff).lr16([this] (offs_t offset) { return m_eprom[offset]; }, "eprom");
	map(0x7f180000, 0x7f1fffff).rw(m_flash_lsb, FUNC(intel_28f010_device::read), FUNC(intel_28f010_device::write)).umask32(0x00ff00ff).mask(0x3ffff);
	map(0x7f180000, 0x7f1fffff).rw(m_flash_msb, FUNC(intel_28f010_device::read), FUNC(intel_28f010_device::write)).umask32(0xff00ff00).mask(0x3ffff);

	// HACK: for SRX bus Sapphire only
	//map(0x8f007f80, 0x8f007fff).rom().region("idprom", 0);
}

void emerald_state::emerald_io_map(address_map &map)
{
	emerald_base_map(map);
}

void turquoise_state::turquoise_io_map(address_map &map)
{
	turquoise_base_map(map);
}

void sapphire_state::sapphire_io_map(address_map &map)
{
	sapphire_base_map(map);

	map(0x00000000, 0x00001fff).m(m_mmu, FUNC(cammu_c4_device::map));
}

void interpro_state::interpro_boot_map(address_map &map)
{
	// FIXME: the real system may have some initial boot instructions in this boot
	// memory space which jump to the start of the boot eprom code, or there may
	// be some special address decoding logic for boot. For now, we fake it in the
	// CPU by hard-coding the start address to point at the eprom.
	map(0x00000000, 0x00001fff).ram();
}

void emerald_state::interpro_82586_map(address_map &map)
{
	map(0x00000000, 0x00ffffff).rw(m_ioga, FUNC(emerald_ioga_device::eth_r), FUNC(emerald_ioga_device::eth_w));
}

void turquoise_state::interpro_82586_map(address_map &map)
{
	map(0x00000000, 0x00ffffff).rw(m_ioga, FUNC(turquoise_ioga_device::eth_r), FUNC(turquoise_ioga_device::eth_w));
}

void sapphire_state::interpro_82596_map(address_map &map)
{
	map(0x00000000, 0xffffffff).rw(m_ioga, FUNC(sapphire_ioga_device::eth_r), FUNC(sapphire_ioga_device::eth_w));
}

void interpro_state::interpro_serial(machine_config &config)
{
	input_merger_device &scc_int(INPUT_MERGER_ANY_HIGH(config, "scc_int"));

	/*
	 * Documentation states that all three serial ports have RxD, TxD, CTS and
	 * RTS signals connected, and serial port 0 also has RI, DTR and DTS(?).
	 * Serial diagnostics pass all tests (except internal loopback which is not
	 * supported by z80scc_device) when a dec_loopback device is installed. The
	 * diagnostic tests also indicate that DCD is connected on all three ports.
	 *
	 * The documentation consistently refers to a DTS signal on serial port 0,
	 * but this appears to be an error or typo, as it doesn't match any known
	 * RS-232 signal; possibly it should be DSR?
	 */
	// scc1 channel A (serial port 1)
	rs232_port_device &port1(RS232_PORT(config, "serial1", default_rs232_devices, nullptr));
	port1.cts_handler().set(m_scc1, FUNC(z80scc_device::ctsa_w));
	port1.dcd_handler().set(m_scc1, FUNC(z80scc_device::dcda_w));
	port1.rxd_handler().set(m_scc1, FUNC(z80scc_device::rxa_w));
	m_scc1->out_rtsa_callback().set(port1, FUNC(rs232_port_device::write_rts));
	m_scc1->out_txda_callback().set(port1, FUNC(rs232_port_device::write_txd));
	m_scc1->out_wreqa_callback().set(m_ioga, FUNC(interpro_ioga_device::drq_serial1)).invert();

	// scc1 channel B (serial port 2)
	rs232_port_device &port2(RS232_PORT(config, "serial2", default_rs232_devices, nullptr));
	port2.cts_handler().set(m_scc1, FUNC(z80scc_device::ctsb_w));
	port2.dcd_handler().set(m_scc1, FUNC(z80scc_device::dcdb_w));
	port2.rxd_handler().set(m_scc1, FUNC(z80scc_device::rxb_w));
	m_scc1->out_rtsb_callback().set(port2, FUNC(rs232_port_device::write_rts));
	m_scc1->out_txdb_callback().set(port2, FUNC(rs232_port_device::write_txd));
	m_scc1->out_wreqb_callback().set(m_ioga, FUNC(interpro_ioga_device::drq_serial2)).invert();

	m_scc1->out_int_callback().set(scc_int, FUNC(input_merger_device::in_w<0>));

	// scc2 channel B (serial port 0)
	rs232_port_device &port0(RS232_PORT(config, "serial0", default_rs232_devices, nullptr));
	port0.cts_handler().set(m_scc2, FUNC(z80scc_device::ctsb_w));
	port0.dcd_handler().set(m_scc2, FUNC(z80scc_device::dcdb_w));
	port0.ri_handler().set(m_scc2, FUNC(z80scc_device::syncb_w));
	port0.rxd_handler().set(m_scc2, FUNC(z80scc_device::rxb_w));
	m_scc2->out_dtrb_callback().set(port0, FUNC(rs232_port_device::write_dtr));
	m_scc2->out_rtsb_callback().set(port0, FUNC(rs232_port_device::write_rts));
	m_scc2->out_txdb_callback().set(port0, FUNC(rs232_port_device::write_txd));
	m_scc2->out_wreqb_callback().set(m_ioga, FUNC(interpro_ioga_device::drq_serial0)).invert();

	m_scc2->out_int_callback().set(scc_int, FUNC(input_merger_device::in_w<1>));

	scc_int.output_handler().set(m_ioga, FUNC(interpro_ioga_device::ir11_w));
}

static void interpro_scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
	device.option_add("cdrom", NSCSI_CDROM);
}

void interpro_state::interpro_scsi_adapter(device_t *device)
{
	ncr53c90_device &adapter = downcast<ncr53c90_device &>(*device);

	adapter.set_clock(24_MHz_XTAL);

	adapter.irq_handler_cb().set(":ioga", FUNC(interpro_ioga_device::ir0_w));
	adapter.drq_handler_cb().set(":ioga", FUNC(interpro_ioga_device::drq_scsi));
}

void interpro_state::interpro_cdrom(device_t *device)
{
	downcast<nscsi_cdrom_device &>(*device).set_block_size(512);
}

void interpro_state::ioga(machine_config &config)
{
	m_ioga->out_nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_ioga->out_irq_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ioga->out_irq_vector_callback().set(m_maincpu, FUNC(clipper_device::set_ivec));

	// ioga dma and serial dma channels
	//m_ioga->dma_r_callback<0>().set(unknown); // plotter
	m_ioga->dma_r_callback<1>().set("scsi:7:host", FUNC(ncr53c90a_device::dma_r));
	m_ioga->dma_w_callback<1>().set("scsi:7:host", FUNC(ncr53c90a_device::dma_w));
	m_ioga->dma_r_callback<2>().set(m_fdc, FUNC(upd765_family_device::dma_r));
	m_ioga->dma_w_callback<2>().set(m_fdc, FUNC(upd765_family_device::dma_w));
	m_ioga->serial_dma_r_callback<0>().set(m_scc2, FUNC(z80scc_device::db_r));
	m_ioga->serial_dma_w_callback<0>().set(m_scc2, FUNC(z80scc_device::db_w));
	m_ioga->serial_dma_r_callback<1>().set(m_scc1, FUNC(z80scc_device::da_r));
	m_ioga->serial_dma_w_callback<1>().set(m_scc1, FUNC(z80scc_device::da_w));
	m_ioga->serial_dma_r_callback<2>().set(m_scc1, FUNC(z80scc_device::db_r));
	m_ioga->serial_dma_w_callback<2>().set(m_scc1, FUNC(z80scc_device::db_w));

	// ioga floppy terminal count, ethernet channel attention
	m_ioga->fdc_tc_callback().set(m_fdc, FUNC(upd765_family_device::tc_line_w));
	m_ioga->eth_ca_callback().set(m_eth, FUNC(i82586_base_device::ca));
}

void interpro_state::interpro(machine_config &config)
{
	RAM(config, m_ram, 0);
	m_ram->set_default_size("16M");
	m_ram->set_extra_options("32M,64M,128M,256M");

	// memory control gate array

	// srx gate array
	INTERPRO_SGA(config, m_sga, 0);
	m_sga->berr_callback().set(m_ioga, FUNC(interpro_ioga_device::bus_error));

	// floppy

	// serial

	// real-time clock/non-volatile memory
	MC146818(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->set_use_utc(true);
	m_rtc->irq().set(m_ioga, FUNC(interpro_ioga_device::ir9_w));

	// scsi bus and devices
	NSCSI_BUS(config, m_scsibus, 0);

	nscsi_connector &harddisk(NSCSI_CONNECTOR(config, "scsi:0", 0));
	interpro_scsi_devices(harddisk);
	harddisk.set_default_option("harddisk");

	nscsi_connector &cdrom(NSCSI_CONNECTOR(config, "scsi:4", 0));
	interpro_scsi_devices(cdrom);
	cdrom.set_default_option("cdrom");
	cdrom.set_option_machine_config("cdrom", interpro_cdrom);

	interpro_scsi_devices(NSCSI_CONNECTOR(config, "scsi:1", 0));
	interpro_scsi_devices(NSCSI_CONNECTOR(config, "scsi:2", 0));
	interpro_scsi_devices(NSCSI_CONNECTOR(config, "scsi:3", 0));
	interpro_scsi_devices(NSCSI_CONNECTOR(config, "scsi:5", 0));
	interpro_scsi_devices(NSCSI_CONNECTOR(config, "scsi:6", 0));

	// ethernet

	// i/o gate array

	// system layout
	config.set_default_layout(layout_interpro);

	// software lists
	SOFTWARE_LIST(config, m_softlist).set_original("interpro");
}

void emerald_state::emerald(machine_config &config)
{
	interpro(config);

	CLIPPER_C300(config, m_maincpu, 12.5_MHz_XTAL); // 40MHz?
	m_maincpu->set_addrmap(0, &emerald_state::emerald_main_map);
	m_maincpu->set_addrmap(1, &emerald_state::emerald_io_map);
	m_maincpu->set_addrmap(2, &emerald_state::interpro_boot_map);
	m_maincpu->set_irq_acknowledge_callback(m_ioga, FUNC(interpro_ioga_device::acknowledge_interrupt));

	CAMMU_C3(config, m_i_cammu, 0);
	m_i_cammu->exception_callback().set(m_maincpu, FUNC(clipper_device::set_exception));

	CAMMU_C3(config, m_d_cammu, 0);
	m_d_cammu->exception_callback().set(m_maincpu, FUNC(clipper_device::set_exception));
	m_d_cammu->add_linked(m_i_cammu);

	// boot fails memory test without this
	m_ram->set_default_value(0);

	// memory control gate array
	INTERPRO_MCGA(config, m_mcga, 0);

	// floppy controller
	I82072(config, m_fdc, 24_MHz_XTAL);
	m_fdc->set_ready_line_connected(false);
	m_fdc->intrq_wr_callback().set(m_ioga, FUNC(interpro_ioga_device::ir1_w));
	m_fdc->drq_wr_callback().set(m_ioga, FUNC(interpro_ioga_device::drq_floppy));

	// connect a 3.5" drive at id 3
	//FLOPPY_CONNECTOR(config, "fdc:2", "525hd", FLOPPY_525_HD, true, floppy_image_device::default_pc_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:3", "35hd", FLOPPY_35_HD, true, floppy_image_device::default_pc_floppy_formats).enable_sound(true);

	// serial controllers and ports
	SCC85C30(config, m_scc1, 4.9152_MHz_XTAL);
	SCC85C30(config, m_scc2, 4.9152_MHz_XTAL);
	interpro_serial(config);

	// scsi host adapter
	nscsi_connector &adapter(NSCSI_CONNECTOR(config, "scsi:7", 0));
	adapter.option_add_internal("host", NCR53C90A);
	adapter.set_default_option("host");
	adapter.set_fixed(true);
	adapter.set_option_machine_config("host", interpro_scsi_adapter);

	// ethernet controller
	I82586(config, m_eth, 10_MHz_XTAL);
	m_eth->out_irq_cb().set(m_ioga, FUNC(interpro_ioga_device::ir12_w));
	m_eth->set_addrmap(0, &emerald_state::interpro_82586_map);

	// i/o gate array
	EMERALD_IOGA(config, m_ioga, 0);
	m_ioga->set_memory(m_maincpu, 0);
	ioga(config);

	// srx bus
	SRX_BUS(config, m_bus, 0);
	m_bus->set_main_space(m_maincpu, 0);
	m_bus->set_io_space(m_maincpu, 1);

	m_bus->out_irq0_cb().set(m_ioga, FUNC(interpro_ioga_device::ir3_w));
	m_bus->out_irq1_cb().set(m_ioga, FUNC(interpro_ioga_device::ir4_w));
	m_bus->out_irq2_cb().set(m_ioga, FUNC(interpro_ioga_device::ir5_w));
	m_bus->out_irq3_cb().set(m_ioga, FUNC(interpro_ioga_device::ir6_w));
}

void turquoise_state::turquoise(machine_config &config)
{
	interpro(config);

	CLIPPER_C300(config, m_maincpu, 12.5_MHz_XTAL); // 40Mhz?
	m_maincpu->set_addrmap(0, &turquoise_state::turquoise_main_map);
	m_maincpu->set_addrmap(1, &turquoise_state::turquoise_io_map);
	m_maincpu->set_addrmap(2, &turquoise_state::interpro_boot_map);
	m_maincpu->set_irq_acknowledge_callback(m_ioga, FUNC(interpro_ioga_device::acknowledge_interrupt));

	CAMMU_C3(config, m_i_cammu, 0);
	m_i_cammu->exception_callback().set(m_maincpu, FUNC(clipper_device::set_exception));

	CAMMU_C3(config, m_d_cammu, 0);
	m_d_cammu->exception_callback().set(m_maincpu, FUNC(clipper_device::set_exception));
	m_d_cammu->add_linked(m_i_cammu);

	// boot fails memory test without this
	m_ram->set_default_value(0);

	// memory control gate array
	INTERPRO_MCGA(config, m_mcga, 0);

	// floppy controller
	I82072(config, m_fdc, 24_MHz_XTAL);
	m_fdc->set_ready_line_connected(false);
	m_fdc->intrq_wr_callback().set(m_ioga, FUNC(interpro_ioga_device::ir1_w));
	m_fdc->drq_wr_callback().set(m_ioga, FUNC(interpro_ioga_device::drq_floppy));

	// connect a 3.5" drive at id 3
	FLOPPY_CONNECTOR(config, "fdc:3", "35hd", FLOPPY_35_HD, true, floppy_image_device::default_pc_floppy_formats).enable_sound(true);

	// serial controllers and ports
	SCC85C30(config, m_scc1, 4.9152_MHz_XTAL);
	SCC85C30(config, m_scc2, 4.9152_MHz_XTAL);
	interpro_serial(config);

	// keyboard port
	INTERPRO_KEYBOARD_PORT(config, m_kbd_port, interpro_keyboard_devices, nullptr);
	m_kbd_port->rxd_handler_cb().set(m_scc2, FUNC(z80scc_device::rxa_w));
	m_scc2->out_txda_callback().set(m_kbd_port, FUNC(interpro_keyboard_port_device::write_txd));

	// mouse port
	INTERPRO_MOUSE_PORT(config, m_mse_port, interpro_mouse_devices, nullptr);
	m_mse_port->state_func().set(m_ioga, FUNC(interpro_ioga_device::mouse_status_w));

	// scsi host adapter
	nscsi_connector &adapter(NSCSI_CONNECTOR(config, "scsi:7", 0));
	adapter.option_add_internal("host", NCR53C90A);
	adapter.set_default_option("host");
	adapter.set_fixed(true);
	adapter.set_option_machine_config("host", interpro_scsi_adapter);

	// ethernet controller
	I82586(config, m_eth, 10_MHz_XTAL);
	m_eth->out_irq_cb().set(m_ioga, FUNC(interpro_ioga_device::ir12_w));
	m_eth->set_addrmap(0, &turquoise_state::interpro_82586_map);

	// i/o gate array
	TURQUOISE_IOGA(config, m_ioga, 0);
	m_ioga->set_memory(m_maincpu, 0);
	ioga(config);

	// cbus bus
	CBUS_BUS(config, m_bus, 0);
	m_bus->set_main_space(m_maincpu, 0);
	m_bus->set_io_space(m_maincpu, 1);

	m_bus->out_irq0_cb().set(m_ioga, FUNC(interpro_ioga_device::ir3_w));
	m_bus->out_irq1_cb().set(m_ioga, FUNC(interpro_ioga_device::ir4_w));
	m_bus->out_irq2_cb().set(m_ioga, FUNC(interpro_ioga_device::ir5_w));
	m_bus->out_irq3_cb().set(m_ioga, FUNC(interpro_ioga_device::ir6_w));
}

void sapphire_state::sapphire(machine_config &config)
{
	interpro(config);

	CLIPPER_C400(config, m_maincpu, 12.5_MHz_XTAL);
	m_maincpu->set_addrmap(0, &sapphire_state::sapphire_main_map);
	m_maincpu->set_addrmap(1, &sapphire_state::sapphire_io_map);
	m_maincpu->set_addrmap(2, &sapphire_state::interpro_boot_map);
	m_maincpu->set_irq_acknowledge_callback(m_ioga, FUNC(interpro_ioga_device::acknowledge_interrupt));

	// FIXME: 2400/6400 should be C4T cammu?
	CAMMU_C4I(config, m_mmu, 0);
	m_mmu->exception_callback().set(m_maincpu, FUNC(clipper_device::set_exception));

	// memory control gate array
	INTERPRO_FMCC(config, m_mcga, 0);

	// floppy controller
	N82077AA(config, m_fdc, 24_MHz_XTAL, n82077aa_device::mode_t::PS2);
	m_fdc->intrq_wr_callback().set(m_ioga, FUNC(interpro_ioga_device::ir1_w));
	m_fdc->drq_wr_callback().set(m_ioga, FUNC(interpro_ioga_device::drq_floppy));

	// connect a 3.5" drive at id 1
	FLOPPY_CONNECTOR(config, "fdc:1", "35hd", FLOPPY_35_HD, true, floppy_image_device::default_pc_floppy_formats).enable_sound(true);

	// srx arbiter gate array
	INTERPRO_ARBGA(config, m_arbga, 0);

	// serial controllers and ports
	SCC85230(config, m_scc1, 4.9152_MHz_XTAL);
	SCC85C30(config, m_scc2, 4.9152_MHz_XTAL);
	interpro_serial(config);

	// scsi host adapter
	nscsi_connector &adapter(NSCSI_CONNECTOR(config, "scsi:7", 0));
	adapter.option_add_internal("host", NCR53C94);
	adapter.set_default_option("host");
	adapter.set_fixed(true);
	adapter.set_option_machine_config("host", interpro_scsi_adapter);

	// ethernet controller
	I82596_LE16(config, m_eth, 20_MHz_XTAL);
	m_eth->out_irq_cb().set(m_ioga, FUNC(interpro_ioga_device::ir12_w));
	m_eth->set_addrmap(0, &sapphire_state::interpro_82596_map);

	// i/o gate array
	SAPPHIRE_IOGA(config, m_ioga, 0);
	m_ioga->set_memory(m_maincpu, 0);
	ioga(config);

	// flash memory
	INTEL_28F010(config, m_flash_lsb);
	INTEL_28F010(config, m_flash_msb);
}

void turquoise_state::ip2000(machine_config &config)
{
	turquoise(config);

	// default is 2020 with GT graphics
	m_kbd_port->set_default_option("lle_en_us");
	m_mse_port->set_default_option("interpro_mouse");

	CBUS_SLOT(config, "slot:0", 0, m_bus, cbus_cards, "mpcb963", false);
	CBUS_SLOT(config, "slot:1", 0, m_bus, cbus_cards, nullptr, false);

	m_softlist->set_filter("2000");
}

void cbus_sapphire_state::cbus_sapphire(machine_config &config)
{
	sapphire(config);

	// keyboard port
	INTERPRO_KEYBOARD_PORT(config, m_kbd_port, interpro_keyboard_devices, nullptr);
	m_kbd_port->rxd_handler_cb().set(m_scc2, FUNC(z80scc_device::rxa_w));
	m_scc2->out_txda_callback().set(m_kbd_port, FUNC(interpro_keyboard_port_device::write_txd));

	// mouse port
	INTERPRO_MOUSE_PORT(config, m_mse_port, interpro_mouse_devices, nullptr);
	m_mse_port->state_func().set(m_ioga, FUNC(interpro_ioga_device::mouse_status_w));

	// cbus bus
	CBUS_BUS(config, m_bus, 0);
	m_bus->set_main_space(m_maincpu, 0);
	m_bus->set_io_space(m_maincpu, 1);

	m_bus->out_irq0_cb().set(m_ioga, FUNC(interpro_ioga_device::ir3_w));
	m_bus->out_irq1_cb().set(m_ioga, FUNC(interpro_ioga_device::ir4_w));
	m_bus->out_irq2_cb().set(m_ioga, FUNC(interpro_ioga_device::ir5_w));
	m_bus->out_irq3_cb().set(m_ioga, FUNC(interpro_ioga_device::ir6_w));
}

void srx_sapphire_state::srx_sapphire(machine_config &config)
{
	sapphire(config);

	// srx bus
	SRX_BUS(config, m_bus, 0);
	m_bus->set_main_space(m_maincpu, 0);
	m_bus->set_io_space(m_maincpu, 1);

	m_bus->out_irq0_cb().set(m_ioga, FUNC(interpro_ioga_device::ir3_w));
	m_bus->out_irq1_cb().set(m_ioga, FUNC(interpro_ioga_device::ir4_w));
	m_bus->out_irq2_cb().set(m_ioga, FUNC(interpro_ioga_device::ir5_w));
	m_bus->out_irq3_cb().set(m_ioga, FUNC(interpro_ioga_device::ir6_w));
}

void cbus_sapphire_state::ip2400(machine_config &config)
{
	cbus_sapphire(config);

	//m_maincpu->set_clock(50_MHz_XTAL);
	m_mmu->set_cammu_id(cammu_c4i_device::CID_C4IR0);

	// default is 2430 with GT+ graphics
	m_kbd_port->set_default_option("lle_en_us");
	m_mse_port->set_default_option("interpro_mouse");

	CBUS_SLOT(config, "slot:0", 0, m_bus, cbus_cards, "msmt070", false);
	CBUS_SLOT(config, "slot:1", 0, m_bus, cbus_cards, nullptr, false);

	m_softlist->set_filter("2400");
}

void cbus_sapphire_state::ip2500(machine_config &config)
{
	cbus_sapphire(config);

	//m_maincpu->set_clock(?);
	// FIXME: don't know which cammu revision
	m_mmu->set_cammu_id(cammu_c4i_device::CID_C4IR2);

	// default is 2530 with GT+ graphics
	m_kbd_port->set_default_option("lle_en_us");
	m_mse_port->set_default_option("interpro_mouse");

	CBUS_SLOT(config, "slot:0", 0, m_bus, cbus_cards, "msmt070", false);
	CBUS_SLOT(config, "slot:1", 0, m_bus, cbus_cards, nullptr, false);

	m_softlist->set_filter("2500");
}

void cbus_sapphire_state::ip2700(machine_config &config)
{
	cbus_sapphire(config);

	//m_maincpu->set_clock(?);
	m_mmu->set_cammu_id(cammu_c4i_device::CID_C4IR2);

	// default is 2730 with GT+ graphics
	m_kbd_port->set_default_option("lle_en_us");
	m_mse_port->set_default_option("interpro_mouse");

	CBUS_SLOT(config, "slot:0", 0, m_bus, cbus_cards, "msmt070", false);
	CBUS_SLOT(config, "slot:1", 0, m_bus, cbus_cards, nullptr, false);

	m_softlist->set_filter("2700");
}

void cbus_sapphire_state::ip2800(machine_config &config)
{
	cbus_sapphire(config);

	//m_maincpu->set_clock(?);
	// FIXME: don't know which cammu revision
	m_mmu->set_cammu_id(cammu_c4i_device::CID_C4IR2);

	// default is 2830 with GT+ graphics
	m_kbd_port->set_default_option("lle_en_us");
	m_mse_port->set_default_option("interpro_mouse");

	CBUS_SLOT(config, "slot:0", 0, m_bus, cbus_cards, "msmt070", false);
	CBUS_SLOT(config, "slot:1", 0, m_bus, cbus_cards, nullptr, false);

	m_softlist->set_filter("2800");
}

void emerald_state::ip6000(machine_config &config)
{
	emerald(config);

	// default is 6040 with EDGE-1 graphics
	SRX_SLOT(config, "slot:1", 0, m_bus, srx_cards, nullptr, false);
	SRX_SLOT(config, "slot:2", 0, m_bus, srx_cards, nullptr, false);
	SRX_SLOT(config, "slot:3", 0, m_bus, srx_cards, nullptr, false);
	SRX_SLOT(config, "slot:4", 0, m_bus, srx_cards, "mpcb828", false);

	m_softlist->set_filter("6000");
}

void srx_sapphire_state::ip6400(machine_config &config)
{
	srx_sapphire(config);

	//m_maincpu->set_clock(36_MHz_XTAL);
	m_mmu->set_cammu_id(cammu_c4i_device::CID_C4IR0);

	// default is 6450 with GT II graphics
	SRX_SLOT(config, "slot:1", 0, m_bus, srx_cards, nullptr, false);
	SRX_SLOT(config, "slot:2", 0, m_bus, srx_cards, nullptr, false);
	SRX_SLOT(config, "slot:3", 0, m_bus, srx_cards, nullptr, false);
	SRX_SLOT(config, "slot:4", 0, m_bus, srx_cards, "mpcbb68", false);

	// EDGE-2 graphics (6480)
	//SRX_SLOT(config, "slot:3", 0, m_bus, srx_cards, "mpcb030", false);
	//SRX_SLOT(config, "slot:4", 0, m_bus, srx_cards, "mpcba63", false);

	m_softlist->set_filter("6400");
}

void srx_sapphire_state::ip6700(machine_config &config)
{
	srx_sapphire(config);

	//m_maincpu->set_clock(?);
	// FIXME: don't know which cammu revision
	m_mmu->set_cammu_id(cammu_c4i_device::CID_C4IR2);

	// default is 6780 with EDGE-2 Plus graphics
	SRX_SLOT(config, "slot:1", 0, m_bus, srx_cards, nullptr, false);
	SRX_SLOT(config, "slot:2", 0, m_bus, srx_cards, nullptr, false);
	SRX_SLOT(config, "slot:3", 0, m_bus, srx_cards, "msmt094", false);
	SRX_SLOT(config, "slot:4", 0, m_bus, srx_cards, "mpcb896", false);

	m_softlist->set_filter("6700");
}

void srx_sapphire_state::ip6800(machine_config &config)
{
	srx_sapphire(config);

	//m_maincpu->set_clock(?);
	// FIXME: don't know which cammu revision
	m_mmu->set_cammu_id(cammu_c4i_device::CID_C4IR2);

	// default is 6880 with EDGE-2 Plus graphics
	SRX_SLOT(config, "slot:1", 0, m_bus, srx_cards, nullptr, false);
	SRX_SLOT(config, "slot:2", 0, m_bus, srx_cards, nullptr, false);
	SRX_SLOT(config, "slot:3", 0, m_bus, srx_cards, "msmt094", false);
	SRX_SLOT(config, "slot:4", 0, m_bus, srx_cards, "mpcb896", false);

	m_softlist->set_filter("6800");
}

ROM_START(ip2000)
	ROM_REGION32_LE(0x80, "nodeid", 0)
	ROM_LOAD32_BYTE("nodeid.bin", 0x0, 0x20, CRC(a38397a6) SHA1(9f45fb932bbe231c95b3d5470dcd1fa1c846486f))

	ROM_REGION32_LE(0x80, "idprom", 0)
	ROM_LOAD32_BYTE("mpcb962a.bin", 0x0, 0x20, CRC(e391342c) SHA1(02e03aad760b6651b8599c3a41c7c457983ee97d))

	ROM_REGION16_LE(0x40000, "eprom", 0)
	ROM_SYSTEM_BIOS(0, "ip2000", "InterPro/InterServe 20x0 EPROM")
	ROMX_LOAD("mprgm530e__26_apr_91k.u171", 0x00001, 0x20000, CRC(e4c470cb) SHA1(ff1917bfa963988d739a9dbf0b8f034fe49f2f8c), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("mprgm540e__06_may_91k.u172", 0x00000, 0x20000, CRC(03225843) SHA1(03cfcd5b8ae0057240ef808a40108cb5d082eb63), ROM_SKIP(1) | ROM_BIOS(0))
ROM_END

ROM_START(ip2400)
	// feature[0] & 0x02: C4I cammu if set
	ROM_REGION32_LE(0x80, "idprom", 0)
	ROM_LOAD32_BYTE("msmt0470.bin", 0x0, 0x20, CRC(498c80df) SHA1(18a49732ac9d04b20a77747c1b946c2e88abb087))

	ROM_REGION16_LE(0x20000, "eprom", 0)
	ROM_SYSTEM_BIOS(0, "ip2400", "InterPro/InterServe 24x0 EPROM")
	ROMX_LOAD("mprgw510b__05_16_92.u35", 0x00000, 0x20000, CRC(3b2c4545) SHA1(4e4c98d1cd1035a04be8527223f44d0b687ec3ef), ROM_BIOS(0))

	ROM_REGION(0x20000, "flash_lsb", 0)
	ROM_LOAD_OPTIONAL("y226.u67", 0x00000, 0x20000, CRC(46c0b105) SHA1(7c4a104e4fb3d0e5e8db7c911cdfb3f5c4fb0218))

	ROM_REGION(0x20000, "flash_msb", 0)
	ROM_LOAD_OPTIONAL("y225.u76", 0x00000, 0x20000, CRC(54d95730) SHA1(a4e114dee1567d8aa31eed770f7cc366588f395c))
ROM_END

ROM_START(ip2500)
	ROM_REGION32_LE(0x80, "idprom", 0)
	ROM_LOAD32_BYTE("msmt1000.bin", 0x0, 0x20, CRC(548046c0) SHA1(ce7646e868f3aa35642d7f9348f6b9e91693918e))

	// FIXME: undumped - probably identical to ip2700 EPROM
	ROM_REGION16_LE(0x20000, "eprom", 0)
	ROM_SYSTEM_BIOS(0, "ip2500", "InterPro/InterServe 25x0 EPROM")
	ROMX_LOAD("ip2500_eprom.bin", 0x00000, 0x20000, CRC(467ce7bd) SHA1(53faee40d5df311f53b24c930e434cbf94a5c4aa) BAD_DUMP, ROM_BIOS(0))

	ROM_REGION(0x20000, "flash_lsb", 0)
	ROM_LOAD_OPTIONAL("y226.u67", 0x00000, 0x20000, CRC(46c0b105) SHA1(7c4a104e4fb3d0e5e8db7c911cdfb3f5c4fb0218))

	ROM_REGION(0x20000, "flash_msb", 0)
	ROM_LOAD_OPTIONAL("y225.u76", 0x00000, 0x20000, CRC(54d95730) SHA1(a4e114dee1567d8aa31eed770f7cc366588f395c))
ROM_END

ROM_START(ip2700)
	// feature[0] & 0x04: supports RETRY if clear
	ROM_REGION32_LE(0x80, "idprom", 0)
	ROM_LOAD("mprgz260f_9310270_82s123.u118", 0x000, 0x020, CRC(bef9a02b) SHA1(5a50f8922fc0a7ae5f7934ccc1e178f3c8ec8489))

	ROM_REGION16_LE(0x20000, "eprom", 0)
	ROM_SYSTEM_BIOS(0, "ip2700", "InterPro/InterServe 27x0 EPROM")
	ROMX_LOAD("mprgz530a_9311290.u35", 0x00000, 0x20000, CRC(467ce7bd) SHA1(53faee40d5df311f53b24c930e434cbf94a5c4aa), ROM_BIOS(0))

	ROM_REGION(0x20000, "flash_lsb", 0)
	ROM_LOAD_OPTIONAL("y226_0c3_1210.u67", 0x00000, 0x20000, CRC(46c0b105) SHA1(7c4a104e4fb3d0e5e8db7c911cdfb3f5c4fb0218))

	ROM_REGION(0x20000, "flash_msb", 0)
	ROM_LOAD_OPTIONAL("y225_0c3_1211.u76", 0x00000, 0x20000, CRC(54d95730) SHA1(a4e114dee1567d8aa31eed770f7cc366588f395c))

	ROM_REGION(0x2e5, "plds", 0)
	ROM_LOAD("mprgm610p_9311290_gal16v8b.u43", 0x000, 0x117, CRC(9b825384) SHA1(7d531394a046e2507844423a7fea834730843ce3))
	ROM_LOAD("y215_0c31_1230_gal22v10.u72",    0x000, 0x2e5, CRC(eb24fd27) SHA1(e5b8a32617e2ed73f61ddc4fe07cb9aa6065186b))
	ROM_LOAD("y216_0d31_1290_gal16v8.u58",     0x000, 0x117, CRC(6c67101f) SHA1(4b7ec3a12d10fe56cbd4b0ab2ab6ac89331575bf))
	ROM_LOAD("y279_0a31_1291_gal20v8.u57",     0x000, 0x157, CRC(1b4e3d4f) SHA1(8b5108d9332f29bb4f91825e5d37818c1787413f))
	ROM_LOAD("y277_0a31_1300_gal22v10.u70",    0x000, 0x2e5, CRC(51b0fc4a) SHA1(6ecc9aebf46f060d8886c76c8d33f717aabfa768))
	ROM_LOAD("y472_0a31_1300_gal22v10.u56",    0x000, 0x2e5, CRC(92d12cc3) SHA1(591c781744f62a69d3ac1dc6dbfd740693d8b036))
	ROM_LOAD("y515_0a31_1300_gal22v10.u69",    0x000, 0x2e5, CRC(ee95d0d8) SHA1(3111ad9c6cde9c1213edd3c13adb237cdb22746f))
	ROM_LOAD("y283_0a31_1290_gal20v8.u80",     0x000, 0x157, CRC(c3290b00) SHA1(c107c779fd886ec138f0835d24bb84ea4fc3f909))
	ROM_LOAD("y224_0b31_1300_gal20v8.u75",     0x000, 0x157, CRC(57c0a723) SHA1(6040c1ce92d4e46a2a0b98303263d91067901577))
	ROM_LOAD("y219_0b31_1300_gal16v8.u66",     0x000, 0x117, CRC(3306bcb0) SHA1(239460760f1d187fb6e2e3c49d9608eafe383c0c))
	ROM_LOAD("y221_0a31_1290_gal16v8.u82",     0x000, 0x117, CRC(fc8923d6) SHA1(fabeb0c5f8fcf3dcbbaa21964a058340dfebb063))
	ROM_LOAD("y222_0a31_1290_gal16v8.u74",     0x000, 0x117, CRC(d812cf9d) SHA1(0541d5c6acd6713f2dcb9d17f367e6e4cc649543))
	ROM_LOAD("y218_0b31_1180_gal22v10.u65",    0x000, 0x2e5, CRC(706a8018) SHA1(143e468b1d93949d316b987a9f61a1b4eb93063d))

	ROM_REGION(0x020, "proms", 0)
	ROM_LOAD("mprg4230a_9311300_82s123.u112", 0x000, 0x020, CRC(8633fa2c) SHA1(e93fb6a65c78202a79c94adbf05d438fc6774333))
	ROM_LOAD("mprgy920f_9305070_82s123.u71",  0x000, 0x020, CRC(4fec770c) SHA1(25bfb863336315975568f137e7e76bdc8101ab02))

	ROM_REGION(0x080, "nvram", 0)
	ROM_LOAD("ds12887.u113", 0x000, 0x080, CRC(108116fb) SHA1(9cc538bf1ad784e8f1a1d02ae24fb13370f1a533))

	ROM_REGION(0x11b9, "scprom", 0)
	ROM_LOAD("h300_pb29_xc1736d.u64", 0x0000, 0x11b9, CRC(e9ad2194) SHA1(d8dbecad5f2cdbf19b821b500a8db6a64303b322))
ROM_END

ROM_START(ip2800)
	ROM_REGION32_LE(0x80, "idprom", 0)
	ROM_LOAD32_BYTE("msmt1450.bin", 0x0, 0x20, CRC(61c7a305) SHA1(efcd045cbdfda8df44eaad761b0ba99367973cd7))

	ROM_REGION16_LE(0x20000, "eprom", 0)
	ROM_SYSTEM_BIOS(0, "ip2800", "InterPro/InterServe 28x0 EPROM")
	ROMX_LOAD("ip2800_eprom.bin", 0x00000, 0x20000, CRC(467ce7bd) SHA1(53faee40d5df311f53b24c930e434cbf94a5c4aa), ROM_BIOS(0))

	ROM_REGION(0x20000, "flash_lsb", 0)
	ROM_LOAD_OPTIONAL("y226.u67", 0x00000, 0x20000, CRC(46c0b105) SHA1(7c4a104e4fb3d0e5e8db7c911cdfb3f5c4fb0218))

	ROM_REGION(0x20000, "flash_msb", 0)
	ROM_LOAD_OPTIONAL("y225.u76", 0x00000, 0x20000, CRC(54d95730) SHA1(a4e114dee1567d8aa31eed770f7cc366588f395c))
ROM_END

ROM_START(ip6000)
	ROM_REGION32_LE(0x80, "nodeid", 0)
	ROM_LOAD32_BYTE("nodeid.bin", 0x0, 0x20, CRC(a38397a6) SHA1(9f45fb932bbe231c95b3d5470dcd1fa1c846486f))

	// feature[0] & 0x01: 1/4 bus clock if clear
	// feature[0] & 0x02: configurable console port if clear
	ROM_REGION32_LE(0x80, "idprom", 0)
	ROM_LOAD32_BYTE("mpcb765b.bin", 0x0, 0x20, CRC(6da05794) SHA1(fef8a9c17491f3d3ceb35c56a628f47d49166b57))

	ROM_REGION16_LE(0x40000, "eprom", 0)
	ROM_SYSTEM_BIOS(0, "ip6000", "InterPro/InterServe 60x0 EPROM")
	ROMX_LOAD("mprgg360f__04_may_90v.u336", 0x00001, 0x20000, CRC(9e8b798b) SHA1(54412e26a468e038fb44ffa322ed3ddfae423c17), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("mprgg350f__04_may_90v.u349", 0x00000, 0x20000, CRC(32ab99fd) SHA1(202a6082bade8a084b8cd25109daff8443f6a5c7), ROM_SKIP(1) | ROM_BIOS(0))
ROM_END

ROM_START(ip6400)
	ROM_REGION32_LE(0x80, "idprom", 0)
	ROM_LOAD32_BYTE("msmt046b.bin", 0x0, 0x20, CRC(3e8ffc77) SHA1(719a3f8b01bb506c9cb876506d63d167550bcd0a))

	// FIXME: use 2400 eprom until we have a 6400 dump
	ROM_REGION16_LE(0x20000, "eprom", 0)
	ROM_SYSTEM_BIOS(0, "ip6400", "InterPro/InterServe 64x0 EPROM")
	ROMX_LOAD("ip6400_eprom.bin", 0x00000, 0x20000, BAD_DUMP CRC(3b2c4545) SHA1(4e4c98d1cd1035a04be8527223f44d0b687ec3ef), ROM_BIOS(0))

	ROM_REGION(0x20000, "flash_lsb", 0)
	ROM_LOAD_OPTIONAL("flash.lsb", 0x00000, 0x20000, CRC(46c0b105) SHA1(7c4a104e4fb3d0e5e8db7c911cdfb3f5c4fb0218))

	ROM_REGION(0x20000, "flash_msb", 0)
	ROM_LOAD_OPTIONAL("flash.msb", 0x00000, 0x20000, CRC(54d95730) SHA1(a4e114dee1567d8aa31eed770f7cc366588f395c))
ROM_END

ROM_START(ip6700)
	ROM_REGION32_LE(0x80, "idprom", 0)
	ROM_LOAD32_BYTE("msmt127b.bin", 0x0, 0x20, CRC(cc112f65) SHA1(8533a31b4733fd91bb87effcd276fc93f2858629))

	ROM_REGION16_LE(0x20000, "eprom", 0)
	ROM_SYSTEM_BIOS(0, "ip6700", "InterPro/InterServe 67x0 EPROM")
	ROMX_LOAD("mprgz530a.u144", 0x00000, 0x20000, CRC(467ce7bd) SHA1(53faee40d5df311f53b24c930e434cbf94a5c4aa), ROM_BIOS(0))

	ROM_REGION(0x20000, "flash_lsb", 0)
	ROM_LOAD_OPTIONAL("y226.u130", 0x00000, 0x20000, CRC(46c0b105) SHA1(7c4a104e4fb3d0e5e8db7c911cdfb3f5c4fb0218))

	ROM_REGION(0x20000, "flash_msb", 0)
	ROM_LOAD_OPTIONAL("y225.u117", 0x00000, 0x20000, CRC(54d95730) SHA1(a4e114dee1567d8aa31eed770f7cc366588f395c))
ROM_END

ROM_START(ip6800)
	ROM_REGION32_LE(0x80, "idprom", 0)
	ROM_LOAD32_BYTE("msmt127b.bin", 0x0, 0x20, CRC(cc112f65) SHA1(8533a31b4733fd91bb87effcd276fc93f2858629))

	ROM_REGION16_LE(0x20000, "eprom", 0)
	ROM_SYSTEM_BIOS(0, "ip6800", "InterPro/InterServe 68x0 EPROM")
	ROMX_LOAD("mprgz530a__9406270.u144", 0x00000, 0x20000, CRC(467ce7bd) SHA1(53faee40d5df311f53b24c930e434cbf94a5c4aa), ROM_BIOS(0))

	ROM_REGION(0x20000, "flash_lsb", 0)
	ROM_LOAD_OPTIONAL("y226.u130", 0x00000, 0x20000, CRC(46c0b105) SHA1(7c4a104e4fb3d0e5e8db7c911cdfb3f5c4fb0218))

	ROM_REGION(0x20000, "flash_msb", 0)
	ROM_LOAD_OPTIONAL("y225.u117", 0x00000, 0x20000, CRC(54d95730) SHA1(a4e114dee1567d8aa31eed770f7cc366588f395c))
ROM_END

}

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS                INIT         COMPANY        FULLNAME                    FLAGS
COMP( 1990, ip2000, 0,      0,      ip2000,  0,     turquoise_state,     init_common, "Intergraph", "InterPro/InterServe 20x0", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)
COMP( 1992, ip2400, 0,      0,      ip2400,  0,     cbus_sapphire_state, init_common, "Intergraph", "InterPro/InterServe 24x0", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)
COMP( 1993, ip2500, 0,      0,      ip2500,  0,     cbus_sapphire_state, init_common, "Intergraph", "InterPro/InterServe 25x0", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)
COMP( 1993, ip2700, 0,      0,      ip2700,  0,     cbus_sapphire_state, init_common, "Intergraph", "InterPro/InterServe 27x0", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)
COMP( 1994, ip2800, 0,      0,      ip2800,  0,     cbus_sapphire_state, init_common, "Intergraph", "InterPro/InterServe 28x0", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)
COMP( 1990, ip6000, 0,      0,      ip6000,  0,     emerald_state,       init_common, "Intergraph", "InterPro/InterServe 60x0", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)
COMP( 1992, ip6400, 0,      0,      ip6400,  0,     srx_sapphire_state,  init_common, "Intergraph", "InterPro/InterServe 64x0", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)
COMP( 1993, ip6700, 0,      0,      ip6700,  0,     srx_sapphire_state,  init_common, "Intergraph", "InterPro/InterServe 67x0", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)
COMP( 1993, ip6800, 0,      0,      ip6800,  0,     srx_sapphire_state,  init_common, "Intergraph", "InterPro/InterServe 68x0", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)



intv.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nathan Woods,Frank Palazzolo
/************************************************************************
 *  Mattel Intellivision + Keyboard Component Drivers
 *
 *  Frank Palazzolo
 *  Kyle Davis
 *
 *  TBD:
 *          Add tape support (intvkbd)
 *          Add runtime tape loading
 *          Fix memory system workaround
 *            (memory handler stuff in CP1610, debugger, and shared mem)
 *          STIC
 *            reenable dirty support
 *          Cleanup
 *            Separate stic & video better, get rid of *2 for kbd comp
 *          Add better runtime cart loading
 *          Switch to tilemap system
 * Note from kevtris about IntelliVoice Hookup:
<kevtris> the intv uses a special chip
<kevtris> called the SPB640
<kevtris> it is really cool and lame at the same time
<kevtris> it holds 64 10 bit words, which it simply serializes and sends to the SP0256
<kevtris> the SP0256-012 just blindly jumps to 02000h or something when you try to play one of the samples
<kevtris> the SPB640 sits at 2000h and when it sees that address come up, it will send out the 640 bits to the '256
<kevtris> this means you cannot use any gotos/gosubs in your speech data, but that is OK
<kevtris> it's just a single serial stream.  the intv simply watches the buffer state and refills it periodically.  there's enough buffer to keep it full for 1 frame
<kevtris> that's about it
<kevtris> the samples are stored in the game ROMs, and are easy to extract

9502, 9503 and 9504 are the chips used in the intv1 (the sears one may use different chips)
9505 is used in some carts
9503 and 9506 are the chips used in the intv2

Known roms so far:
RO-3-9502-011 = 4KiB(2Kiw) self-decoding mask rom with decoder & bus, (DIP40 containing 1/2 of EXEC, maps at words 0000-0fff or 1000-1fff [unclear which, guess is 0000], i/o maps at ?), located at U5 on intv1
RO-3-9503-003 = 2KiB(1Kiw) self-decoding mask rom with decoder & bus, (DIP40 containing GROM, maps at words 0x3000-0x37FF, i/o maps at ?), located at U21 in intv1, U5 in intv2
RO-3-9504-021 = 4KiB(2Kiw) self-decoding mask rom without decoder or bus, (DIP28 containing 1/2 of EXEC, maps at words 0000-0fff or 1000-1fff [unclear which, guess is 1000]), located at U6 in intv1
RO-3-9506-010 = 8KiB(4Kiw)+512B(256W) self-decoding mask rom with decoder & bus, (DIP40 containing EXEC2, maps at words 0000-1fff and 0400-04FF, i/o maps at ?) located at U6 in intv2

rom types (suspected, not proven yet):
RO-3-9502 = 4KiB (2Kiw) self decoding address mask rom with external address decoder & bus (DIP40)
RO-3-9503 = 2KiB (1Kiw) self decoding address mask rom with external address decoder & bus (DIP40)
RO-3-9504 = 4KiB (2Kiw) self decoding address mask rom without external address decoder or bus (DIP28)
RO-3-9505 = 8KiB (4Kiw) self decoding address mask rom without external address decoder or bus (DIP28)
RO-3-9506 = 8KiB (4Kiw) self decoding address mask rom with external address decoder & bus (DIP40)

 *
 ************************************************************************/


#include "emu.h"
#include "intv.h"

#include "cpu/m6502/m6502.h"
#include "cpu/cp1610/cp1610.h"
#include "sound/ay8910.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


#ifndef VERBOSE
#ifdef MAME_DEBUG
#define VERBOSE 1
#else
#define VERBOSE 0
#endif
#endif

static constexpr rgb_t intv_colors[] =
{
	{ 0x00, 0x00, 0x00 }, // BLACK
	{ 0x00, 0x2d, 0xff }, // BLUE
	{ 0xff, 0x3d, 0x10 }, // RED
	{ 0xc9, 0xcf, 0xab }, // TAN
	{ 0x38, 0x6b, 0x3f }, // DARK GREEN
	{ 0x00, 0xa7, 0x56 }, // GREEN
	{ 0xfa, 0xea, 0x50 }, // YELLOW
	{ 0xff, 0xfc, 0xff }, // WHITE
	{ 0xbd, 0xac, 0xc8 }, // GRAY
	{ 0x24, 0xb8, 0xff }, // CYAN
	{ 0xff, 0xb4, 0x1f }, // ORANGE
	{ 0x54, 0x6e, 0x00 }, // BROWN
	{ 0xff, 0x4e, 0x57 }, // PINK
	{ 0xa4, 0x96, 0xff }, // LIGHT BLUE
	{ 0x75, 0xcc, 0x80 }, // YELLOW GREEN
	{ 0xb5, 0x1a, 0x58 }  // PURPLE
};

void intv_state::intv_palette(palette_device &palette) const
{
	int k = 0;
	// Two copies of everything (why?)

	for (int i = 0; i < 16; i++)
	{
		palette.set_indirect_color(i, intv_colors[i]);
		palette.set_indirect_color(i + 16, intv_colors[i]);
	}

	for (int i = 0; i < 16; i++)
	{
		for (int j = 0; j < 16; j++)
		{
			palette.set_pen_indirect(k++, i);
			palette.set_pen_indirect(k++, j);
		}
	}

	for (int i = 0; i < 16; i++)
	{
		for (int j = 16; j < 32; j++)
		{
			palette.set_pen_indirect(k++, i);
			palette.set_pen_indirect(k++, j);
		}
	}
}

/* graphics output */

static const gfx_layout intvkbd_charlayout =
{
	8, 8,
	256,
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8 * 8
};

static GFXDECODE_START( gfx_intvkbd )
	GFXDECODE_ENTRY( "gfx1", 0x0000, intvkbd_charlayout, 0, 256 )
GFXDECODE_END


/*
        Bit 7   Bit 6   Bit 5   Bit 4   Bit 3   Bit 2   Bit 1   Bit 0

 Row 0  NC      NC      NC      NC      NC      NC      CTRL    SHIFT
 Row 1  NC      NC      NC      NC      NC      NC      RPT     LOCK
 Row 2  NC      /       ,       N       V       X       NC      SPC
 Row 3  (right) .       M       B       C       Z       NC      CLS
 Row 4  (down)  ;       K       H       F       S       NC      TAB
 Row 5  ]       P       I       Y       R       W       NC      Q
 Row 6  (up)    -       9       7       5       3       NC      1
 Row 7  =       0       8       6       4       2       NC      [
 Row 8  (return)(left)  O       U       T       E       NC      ESC
 Row 9  DEL     '       L       J       G       D       NC      A

2008-05 FP:
The keyboard layout is quite strange, with '[' and ']' at the two ends of the 1st row,
'Esc' in the 2nd row (between 'Tab' and 'Q'), and with Cursor keys and 'Enter' where
you would expect the braces. Moreover, Shift + Cursor keys produce characters.
The emulated layout moves 'Esc', '[' and ']' to their usual position.

Moreover, a small note on natural keyboard support: currently
- "Clear Screen" is mapped to 'Left Alt'
- "Repeat" is mapped to 'F1'
- "Lock" is mapped to 'F2'
*/

static INPUT_PORTS_START( intvkbd )
	PORT_START("ROW0")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("ROW1")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("REPEAT") PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LOCK") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_MAMEKEY(F2))

	PORT_START("ROW2")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)   PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)   PORT_CHAR(',')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)       PORT_CHAR('N')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)       PORT_CHAR('V')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)       PORT_CHAR('X')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)   PORT_CHAR(' ')

	PORT_START("ROW3")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)   PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR('>')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)    PORT_CHAR('.')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)       PORT_CHAR('M')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)       PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)       PORT_CHAR('C')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)       PORT_CHAR('Z')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Clear Screen") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT))

	PORT_START("ROW4")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)    PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CHAR('\\')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)   PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)       PORT_CHAR('K')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)       PORT_CHAR('H')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)       PORT_CHAR('F')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)       PORT_CHAR('S')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)     PORT_CHAR('\t')

	PORT_START("ROW5")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)       PORT_CHAR('P')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)       PORT_CHAR('I')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)       PORT_CHAR('Y')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)       PORT_CHAR('R')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)       PORT_CHAR('W')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)       PORT_CHAR('Q')

	PORT_START("ROW6")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)  PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CHAR('|')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)   PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)       PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)       PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)       PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)       PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)       PORT_CHAR('1') PORT_CHAR('!')

	PORT_START("ROW7")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)  PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)       PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)       PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)       PORT_CHAR('6') PORT_CHAR(0xA2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)       PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)       PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{') // this one would be 1st row, 1st key (at 'Esc' position)

	PORT_START("ROW8")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)    PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CHAR('<')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)       PORT_CHAR('O')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)       PORT_CHAR('U')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)       PORT_CHAR('T')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)       PORT_CHAR('E')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)     PORT_CHAR(UCHAR_MAMEKEY(ESC)) // this one would be 2nd row, 2nd key (between 'Tab' and 'Q')

	PORT_START("ROW9")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)   PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)       PORT_CHAR('L')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)       PORT_CHAR('J')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)       PORT_CHAR('G')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)       PORT_CHAR('D')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NC")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)       PORT_CHAR('A')

	PORT_START("TEST")  /* For tape drive testing... */
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)
INPUT_PORTS_END

void intv_state::intv_mem(address_map &map)
{
	map(0x0000, 0x003f).rw(FUNC(intv_state::stic_r), FUNC(intv_state::stic_w));
	map(0x0100, 0x01ef).rw(FUNC(intv_state::ram8_r), FUNC(intv_state::ram8_w));
	map(0x01f0, 0x01ff).rw(m_sound, FUNC(ay8914_device::read), FUNC(ay8914_device::write)).umask16(0x00ff);
	map(0x0200, 0x035f).rw(FUNC(intv_state::ram16_r), FUNC(intv_state::ram16_w));
	map(0x0400, 0x04ff).r(m_cart, FUNC(intv_cart_slot_device::read_rom04));
	map(0x1000, 0x1fff).rom().region("maincpu", 0x1000 << 1);   // Exec ROM, 10-bits wide
	map(0x2000, 0x2fff).r(m_cart, FUNC(intv_cart_slot_device::read_rom20));
	map(0x3000, 0x37ff).r(m_stic, FUNC(stic_device::grom_read)); // GROM,     8-bits wide
	map(0x3800, 0x39ff).rw(FUNC(intv_state::gram_r), FUNC(intv_state::gram_w));     // GRAM,     8-bits wide
	map(0x3a00, 0x3bff).rw(FUNC(intv_state::gram_r), FUNC(intv_state::gram_w));     // GRAM Alias, 8-bits wide
	map(0x4000, 0x47ff).r(m_cart, FUNC(intv_cart_slot_device::read_rom40));
	map(0x4800, 0x4fff).r(m_cart, FUNC(intv_cart_slot_device::read_rom48));
	map(0x5000, 0x5fff).r(m_cart, FUNC(intv_cart_slot_device::read_rom50));
	map(0x6000, 0x6fff).r(m_cart, FUNC(intv_cart_slot_device::read_rom60));
	map(0x7000, 0x7fff).r(m_cart, FUNC(intv_cart_slot_device::read_rom70));
	map(0x8000, 0x8fff).r(m_cart, FUNC(intv_cart_slot_device::read_rom80));
	map(0x9000, 0x9fff).r(m_cart, FUNC(intv_cart_slot_device::read_rom90));
	map(0xa000, 0xafff).r(m_cart, FUNC(intv_cart_slot_device::read_roma0));
	map(0xb000, 0xbfff).r(m_cart, FUNC(intv_cart_slot_device::read_romb0));
	map(0xc000, 0xcfff).r(m_cart, FUNC(intv_cart_slot_device::read_romc0));
	map(0xd000, 0xdfff).r(m_cart, FUNC(intv_cart_slot_device::read_romd0));
	map(0xe000, 0xefff).r(m_cart, FUNC(intv_cart_slot_device::read_rome0));
	map(0xf000, 0xffff).r(m_cart, FUNC(intv_cart_slot_device::read_romf0));
}

void intv_state::intvoice_mem(address_map &map)
{
	map(0x0000, 0x003f).rw(FUNC(intv_state::stic_r), FUNC(intv_state::stic_w));
	map(0x0080, 0x0081).rw("voice", FUNC(intv_voice_device::read_speech), FUNC(intv_voice_device::write_speech)); // Intellivoice
	map(0x0100, 0x01ef).rw(FUNC(intv_state::ram8_r), FUNC(intv_state::ram8_w));
	map(0x01f0, 0x01ff).rw(m_sound, FUNC(ay8914_device::read), FUNC(ay8914_device::write)).umask16(0x00ff);
	map(0x0200, 0x035f).rw(FUNC(intv_state::ram16_r), FUNC(intv_state::ram16_w));
	map(0x0400, 0x04ff).r("voice", FUNC(intv_voice_device::read_rom04));
	map(0x1000, 0x1fff).rom().region("maincpu", 0x1000 << 1);   // Exec ROM, 10-bits wide
	map(0x2000, 0x2fff).r("voice", FUNC(intv_voice_device::read_rom20));
	map(0x3000, 0x37ff).r(m_stic, FUNC(stic_device::grom_read)); // GROM,     8-bits wide
	map(0x3800, 0x39ff).rw(FUNC(intv_state::gram_r), FUNC(intv_state::gram_w));     // GRAM,     8-bits wide
	map(0x3a00, 0x3bff).rw(FUNC(intv_state::gram_r), FUNC(intv_state::gram_w));     // GRAM Alias, 8-bits wide
	map(0x4000, 0x47ff).r("voice", FUNC(intv_voice_device::read_rom40));
	map(0x4800, 0x4fff).r("voice", FUNC(intv_voice_device::read_rom48));
	map(0x5000, 0x5fff).r("voice", FUNC(intv_voice_device::read_rom50));
	map(0x6000, 0x6fff).r("voice", FUNC(intv_voice_device::read_rom60));
	map(0x7000, 0x7fff).r("voice", FUNC(intv_voice_device::read_rom70));
	map(0x8000, 0x8fff).r("voice", FUNC(intv_voice_device::read_rom80));
	map(0x9000, 0x9fff).r("voice", FUNC(intv_voice_device::read_rom90));
	map(0xa000, 0xafff).r("voice", FUNC(intv_voice_device::read_roma0));
	map(0xb000, 0xbfff).r("voice", FUNC(intv_voice_device::read_romb0));
	map(0xc000, 0xcfff).r("voice", FUNC(intv_voice_device::read_romc0));
	map(0xd000, 0xdfff).r("voice", FUNC(intv_voice_device::read_romd0));
	map(0xe000, 0xefff).r("voice", FUNC(intv_voice_device::read_rome0));
	map(0xf000, 0xffff).r("voice", FUNC(intv_voice_device::read_romf0));
}

void intv_state::intv2_mem(address_map &map)
{
	map(0x0000, 0x003f).rw(FUNC(intv_state::stic_r), FUNC(intv_state::stic_w));
	map(0x0100, 0x01ef).rw(FUNC(intv_state::ram8_r), FUNC(intv_state::ram8_w));
	map(0x01f0, 0x01ff).rw(m_sound, FUNC(ay8914_device::read), FUNC(ay8914_device::write)).umask16(0x00ff);
	map(0x0200, 0x035f).rw(FUNC(intv_state::ram16_r), FUNC(intv_state::ram16_w));
	map(0x0400, 0x04ff).rom().region("maincpu", 0x400 << 1);    // Exec ROM, 10-bits wide
	map(0x1000, 0x1fff).rom().region("maincpu", 0x1000 << 1);   // Exec ROM, 10-bits wide
	map(0x2000, 0x2fff).r(m_cart, FUNC(intv_cart_slot_device::read_rom20));
	map(0x3000, 0x37ff).r(m_stic, FUNC(stic_device::grom_read)); // GROM,     8-bits wide
	map(0x3800, 0x39ff).rw(FUNC(intv_state::gram_r), FUNC(intv_state::gram_w)); // GRAM,     8-bits wide
	map(0x3a00, 0x3bff).rw(FUNC(intv_state::gram_r), FUNC(intv_state::gram_w)); // GRAM Alias, 8-bits wide
	map(0x4000, 0x47ff).r(m_cart, FUNC(intv_cart_slot_device::read_rom40));
	map(0x4800, 0x4fff).r(m_cart, FUNC(intv_cart_slot_device::read_rom48));
	map(0x5000, 0x5fff).r(m_cart, FUNC(intv_cart_slot_device::read_rom50));
	map(0x6000, 0x6fff).r(m_cart, FUNC(intv_cart_slot_device::read_rom60));
	map(0x7000, 0x7fff).r(m_cart, FUNC(intv_cart_slot_device::read_rom70));
	map(0x8000, 0x8fff).r(m_cart, FUNC(intv_cart_slot_device::read_rom80));
	map(0x9000, 0x9fff).r(m_cart, FUNC(intv_cart_slot_device::read_rom90));
	map(0xa000, 0xafff).r(m_cart, FUNC(intv_cart_slot_device::read_roma0));
	map(0xb000, 0xbfff).r(m_cart, FUNC(intv_cart_slot_device::read_romb0));
	map(0xc000, 0xcfff).r(m_cart, FUNC(intv_cart_slot_device::read_romc0));
	map(0xd000, 0xdfff).r(m_cart, FUNC(intv_cart_slot_device::read_romd0));
	map(0xe000, 0xefff).r(m_cart, FUNC(intv_cart_slot_device::read_rome0));
	map(0xf000, 0xffff).r(m_cart, FUNC(intv_cart_slot_device::read_romf0));
}

void intv_state::intvecs_mem(address_map &map)
{
	map(0x0000, 0x003f).rw(FUNC(intv_state::stic_r), FUNC(intv_state::stic_w));
	map(0x0080, 0x0081).rw("speech", FUNC(sp0256_device::spb640_r), FUNC(sp0256_device::spb640_w)); /* Intellivoice */
	// map(0x00e0, 0x00e3).rw(FUNC(intv_state::intv_ecs_uart_r), FUNC(intv_state::intv_ecs_uart_w));
	map(0x00f0, 0x00ff).rw("ecs", FUNC(intv_ecs_device::read_ay), FUNC(intv_ecs_device::write_ay)); /* ecs psg */
	map(0x0100, 0x01ef).rw(FUNC(intv_state::ram8_r), FUNC(intv_state::ram8_w));
	map(0x01f0, 0x01ff).rw(m_sound, FUNC(ay8914_device::read), FUNC(ay8914_device::write)).umask16(0x00ff);
	map(0x0200, 0x035f).rw(FUNC(intv_state::ram16_r), FUNC(intv_state::ram16_w));
	map(0x0400, 0x04ff).r("ecs", FUNC(intv_ecs_device::read_rom04));
	map(0x1000, 0x1fff).rom().region("maincpu", 0x1000<<1); /* Exec ROM, 10-bits wide */
	map(0x2000, 0x2fff).rw("ecs", FUNC(intv_ecs_device::read_rom20), FUNC(intv_ecs_device::write_rom20));
	map(0x3000, 0x37ff).r(m_stic, FUNC(stic_device::grom_read)); /* GROM,     8-bits wide */
	map(0x3800, 0x39ff).rw(FUNC(intv_state::gram_r), FUNC(intv_state::gram_w));       /* GRAM,     8-bits wide */
	map(0x3a00, 0x3bff).rw(FUNC(intv_state::gram_r), FUNC(intv_state::gram_w));       /* GRAM Alias,     8-bits wide */
	map(0x4000, 0x47ff).rw("ecs", FUNC(intv_ecs_device::read_ram), FUNC(intv_ecs_device::write_ram));
	map(0x4800, 0x4fff).r("ecs", FUNC(intv_ecs_device::read_rom48));
	map(0x5000, 0x5fff).r("ecs", FUNC(intv_ecs_device::read_rom50));
	map(0x6000, 0x6fff).r("ecs", FUNC(intv_ecs_device::read_rom60));
	map(0x7000, 0x7fff).rw("ecs", FUNC(intv_ecs_device::read_rom70), FUNC(intv_ecs_device::write_rom70));
	map(0x8000, 0x8fff).r("ecs", FUNC(intv_ecs_device::read_rom80));
	map(0x9000, 0x9fff).r("ecs", FUNC(intv_ecs_device::read_rom90));
	map(0xa000, 0xafff).r("ecs", FUNC(intv_ecs_device::read_roma0));
	map(0xb000, 0xbfff).r("ecs", FUNC(intv_ecs_device::read_romb0));
	map(0xc000, 0xcfff).r("ecs", FUNC(intv_ecs_device::read_romc0));
	map(0xd000, 0xdfff).r("ecs", FUNC(intv_ecs_device::read_romd0));
	map(0xe000, 0xefff).rw("ecs", FUNC(intv_ecs_device::read_rome0), FUNC(intv_ecs_device::write_rome0));
	map(0xf000, 0xffff).rw("ecs", FUNC(intv_ecs_device::read_romf0), FUNC(intv_ecs_device::write_romf0));
}

void intv_state::intvkbd_mem(address_map &map)
{
	map(0x0000, 0x003f).rw(FUNC(intv_state::stic_r), FUNC(intv_state::stic_w));
	map(0x0100, 0x01ef).rw(FUNC(intv_state::ram8_r), FUNC(intv_state::ram8_w));
	map(0x01f0, 0x01ff).rw(m_sound, FUNC(ay8914_device::read), FUNC(ay8914_device::write)).umask16(0x00ff);
	map(0x0200, 0x035f).rw(FUNC(intv_state::ram16_r), FUNC(intv_state::ram16_w));
	map(0x0400, 0x04ff).r(m_cart, FUNC(intv_cart_slot_device::read_rom04));
	map(0x1000, 0x1fff).rom().region("maincpu", 0x1000<<1); /* Exec ROM, 10-bits wide */
	map(0x2000, 0x2fff).r(m_cart, FUNC(intv_cart_slot_device::read_rom20));
	map(0x3000, 0x37ff).r(m_stic, FUNC(stic_device::grom_read)); /* GROM,     8-bits wide */
	map(0x3800, 0x39ff).rw(FUNC(intv_state::gram_r), FUNC(intv_state::gram_w));       /* GRAM,     8-bits wide */
	map(0x3a00, 0x3bff).rw(FUNC(intv_state::gram_r), FUNC(intv_state::gram_w));       /* GRAM Alias,     8-bits wide */
	map(0x4000, 0x47ff).r(m_cart, FUNC(intv_cart_slot_device::read_rom40));
	map(0x4800, 0x4fff).r(m_cart, FUNC(intv_cart_slot_device::read_rom48));
	map(0x5000, 0x5fff).r(m_cart, FUNC(intv_cart_slot_device::read_rom50));
	map(0x6000, 0x6fff).r(m_cart, FUNC(intv_cart_slot_device::read_rom60));
	map(0x7000, 0x7fff).rom().region("maincpu", 0x7000<<1); /* Keyboard ROM */
	map(0x8000, 0xbfff).ram().w(FUNC(intv_state::intvkbd_dualport16_w)).share("dualport_ram");  /* Dual-port RAM */
	map(0xc000, 0xcfff).r(m_cart, FUNC(intv_cart_slot_device::read_romc0));
	map(0xd000, 0xdfff).r(m_cart, FUNC(intv_cart_slot_device::read_romd0));
	map(0xe000, 0xefff).r(m_cart, FUNC(intv_cart_slot_device::read_rome0));
	map(0xf000, 0xffff).r(m_cart, FUNC(intv_cart_slot_device::read_romf0));
}

void intv_state::intvkbd2_mem(address_map &map)
{
	map.unmap_value_high();  /* Required because of probing */
	map(0x0000, 0x3fff).rw(FUNC(intv_state::intvkbd_dualport8_lsb_r), FUNC(intv_state::intvkbd_dualport8_lsb_w));  /* Dual-port RAM */
	map(0x4000, 0x40bf).rw(FUNC(intv_state::intvkbd_io_r), FUNC(intv_state::intvkbd_io_w));
	map(0x40c0, 0x40cf).rw(m_crtc, FUNC(tms9927_device::read), FUNC(tms9927_device::write));
	map(0x4200, 0x7fff).rw(FUNC(intv_state::intvkbd_dualport8_msb_r), FUNC(intv_state::intvkbd_dualport8_msb_w));  /* Dual-port RAM */
	map(0xb7f8, 0xb7ff).rw(FUNC(intv_state::intvkbd_periph_r), FUNC(intv_state::intvkbd_periph_w));
	map(0xb800, 0xbfff).ram().share("videoram"); /* Text Display */
	map(0xc000, 0xdfff).rom();
	map(0xe000, 0xffff).r(FUNC(intv_state::intvkb_iocart_r));
}


/* This is needed because MAME core does not allow PULSE_LINE.
    The time interval is not critical, although it should be below 1000. */

TIMER_CALLBACK_MEMBER(intv_state::interrupt2_complete)
{
	m_keyboard->set_input_line(0, CLEAR_LINE);
}

INTERRUPT_GEN_MEMBER(intv_state::interrupt2)
{
	m_keyboard->set_input_line(0, ASSERT_LINE);
	m_int2_complete_timer->adjust(m_keyboard->cycles_to_attotime(100));
}

void intv_state::intv(machine_config &config)
{
	/* basic machine hardware */
	cp1610_cpu_device &maincpu(CP1610(config, m_maincpu, XTAL(3'579'545)/4));        /* Colorburst/4 */
	maincpu.set_addrmap(AS_PROGRAM, &intv_state::intv_mem);
	maincpu.set_vblank_int("screen", FUNC(intv_state::interrupt));
	maincpu.iab().set(FUNC(intv_state::iab_r));
	config.set_maximum_quantum(attotime::from_hz(60));

	/* video hardware */
	STIC(config, m_stic, XTAL(3'579'545));
	m_stic->set_screen("screen");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(59.92);
	//screen.set_vblank_time(ATTOSECONDS_IN_USEC(2400)); /* not accurate */
	screen.set_screen_update(FUNC(intv_state::screen_update_intv));
	screen.set_size(stic_device::SCREEN_WIDTH*INTV_X_SCALE, stic_device::SCREEN_HEIGHT*INTV_Y_SCALE);
	screen.set_visarea(0, stic_device::SCREEN_WIDTH*INTV_X_SCALE-1, 0, stic_device::SCREEN_HEIGHT*INTV_Y_SCALE-1);
	screen.set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(intv_state::intv_palette), 0x400, 32);

	INTV_CONTROL_PORT(config, "iopt_right_ctrl", intv_control_port_devices, "handctrl");
	INTV_CONTROL_PORT(config, "iopt_left_ctrl", intv_control_port_devices, "handctrl");

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	AY8914(config, m_sound, XTAL(3'579'545)/2);
	m_sound->port_a_read_callback().set("iopt_right_ctrl", FUNC(intv_control_port_device::ctrl_r));
	m_sound->port_b_read_callback().set("iopt_left_ctrl", FUNC(intv_control_port_device::ctrl_r));
	m_sound->add_route(ALL_OUTPUTS, "mono", 0.33);

	/* cartridge */
	INTV_CART_SLOT(config, m_cart, intv_cart, nullptr);

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("intv");
	SOFTWARE_LIST(config, "ecs_list").set_compatible("intvecs");
}

void intv_state::intv2(machine_config &config)
{
	intv(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &intv_state::intv2_mem);
}

void intv_state::intvoice(machine_config &config)
{
	intv(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &intv_state::intvoice_mem);

	config.device_remove("cartslot");
	INTV_ROM_VOICE(config, "voice", 0);
}

void intv_state::intvecs(machine_config &config)
{
	intv(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &intv_state::intvecs_mem);

	config.device_remove("cartslot");
	INTV_ROM_ECS(config, "ecs", 0);

	sp0256_device &speech(SP0256(config, "speech", 3120000));
	/* The Intellivoice uses a speaker with its own volume control so the relative volumes to use are subjective */
	speech.add_route(ALL_OUTPUTS, "mono", 1.00);

	/* cassette */
	//CASSETTE(config, "cassette");

	/* software lists */
	config.device_remove("ecs_list");
	SOFTWARE_LIST(config.replace(), "cart_list").set_original("intvecs");
	SOFTWARE_LIST(config, "intv_list").set_compatible("intv");
}

void intv_state::intvkbd(machine_config &config)
{
	intv(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &intv_state::intvkbd_mem);

	M6502(config, m_keyboard, XTAL(7'159'090)/8);
	m_keyboard->set_addrmap(AS_PROGRAM, &intv_state::intvkbd2_mem);
	m_keyboard->set_vblank_int("screen", FUNC(intv_state::interrupt2));

	config.set_maximum_quantum(attotime::from_hz(6000));

	/* video hardware */
	GFXDECODE(config, m_gfxdecode, m_palette, gfx_intvkbd);

	/* crt controller */
	TMS9927(config, m_crtc, XTAL(7'159'090)/8);
	m_crtc->set_char_width(8);
	m_crtc->set_overscan(
		stic_device::OVERSCAN_LEFT_WIDTH*stic_device::X_SCALE*INTVKBD_X_SCALE,
		stic_device::OVERSCAN_RIGHT_WIDTH*stic_device::X_SCALE*INTVKBD_X_SCALE,
		stic_device::OVERSCAN_TOP_HEIGHT*stic_device::Y_SCALE*INTVKBD_Y_SCALE,
		stic_device::OVERSCAN_BOTTOM_HEIGHT*stic_device::Y_SCALE*INTVKBD_Y_SCALE);

	subdevice<screen_device>("screen")->set_screen_update(FUNC(intv_state::screen_update_intvkbd));

	/* I/O cartslots for BASIC */
	GENERIC_CARTSLOT(config, m_iocart1, generic_plain_slot, "intbasic_cart");
	GENERIC_CARTSLOT(config, m_iocart2, generic_plain_slot, "intbasic_cart");
}

ROM_START(intv) // the intv1 exec rom should be two roms: RO-3-9502-011.U5 and RO-3-9504-021.U6
	ROM_REGION(0x10000<<1,"maincpu", ROMREGION_ERASEFF)
	ROM_LOAD16_WORD( "exec.bin", (0x1000<<1)+0, 0x2000, CRC(cbce86f7) SHA1(5a65b922b562cb1f57dab51b73151283f0e20c7a))
ROM_END

#define rom_intvoice rom_intv

// the later intellivision 2's exec rom is a single ro-3-9506-010 at location ic6 holding 8k plus 512 bytes; the 1st 512 bytes are at 0x400 and the 8k at 0x1000
ROM_START(intv2)
	ROM_REGION(0x10000<<1,"maincpu", ROMREGION_ERASEFF)
	ROM_LOAD16_WORD_SWAP( "ro-3-9506-010.ic6", (0x400<<1)+0, 0x200, CRC(dd7e1237) SHA1(fb821a643b7714ed4c812553cd3f668766fd44ab))
	ROM_CONTINUE( (0x1000<<1)+0, 0x2000 )
ROM_END

ROM_START(intvsrs) // the intv1 sears exec rom should be two roms: RO-3-9502-???.U5 and RO-3-9504-???.U6 but the correct names are unknown as of yet
	ROM_REGION(0x10000<<1,"maincpu", ROMREGION_ERASEFF)
	ROM_LOAD16_WORD( "searsexc.bin", (0x1000<<1)+0, 0x2000, CRC(ea552a22) SHA1(834339de056d42a35571cae7fd5b04d1344001e9))
ROM_END

ROM_START(intvecs) // the intv1 exec rom should be two roms: RO-3-9502-011.U5 and RO-3-9504-021.U6
	ROM_REGION(0x10000<<1,"maincpu", ROMREGION_ERASEFF)
	ROM_LOAD16_WORD( "exec.bin", (0x1000<<1)+0, 0x2000, CRC(cbce86f7) SHA1(5a65b922b562cb1f57dab51b73151283f0e20c7a))

	ROM_REGION( 0x10000<<1, "speech", 0 )
	/* SP0256-012 Speech chip w/2KiB mask rom */
	ROM_LOAD( "sp0256-012.bin",   0x1000, 0x0800, CRC(0de7579d) SHA1(618563e512ff5665183664f52270fa9606c9d289) )
ROM_END

/*
Intellivision Keyboard Component - Prototype
-------------------------------------------------------------

GI    9333B-0104         0104.U20  4Kx8  Mask ROM, 6502 code
Intel 2732 "CPU 2D"     CPU2D.U21  4Kx8  EPROM, 6502 code

TI    8S030N  1149-0360  0360.U58  32x8  Timing prom?
TI    8S030N  1149-0370  0370.U74  32x8  Timing prom?

GI    RO-3-9502-024       024.U60  2Kx10 Mask ROM+Addr Decoder, CP1600 code
GI    9316B-4D72         4D72.U62  2Kx8  Mask ROM, CP1600 code (upper)
GI    9316B-4D71         4D71.U63  2Kx8  Mask ROM, CP1600 code (lower)
GI    9316B-4C52         4C52.U34  2Kx8  Mask ROM, Alphanumerics

Main board also includes:

    2  2114 DRAMS        1Kx4  Character memory
    10 MM5290J DRAMS    16Kx10 CP1600 memory?
    1  6502
    1  Mystery 40-pin chip (under heat sink)
       (actually a SMC CRT5027 aka TI TMS9927 CRT controller)
*/

ROM_START(intvkbd) // the intv1 exec rom should be two roms: RO-3-9502-011.U5 and RO-3-9504-021.U6
	ROM_REGION(0x10000<<1,"maincpu", ROMREGION_ERASEFF)
	ROM_LOAD16_WORD( "exec.bin", 0x1000<<1, 0x2000, CRC(cbce86f7) SHA1(5a65b922b562cb1f57dab51b73151283f0e20c7a))
	ROM_LOAD16_WORD( "024.u60",  0x7000<<1, 0x1000, CRC(4f7998ec) SHA1(ec006d0ae9002e9d56d83a71f5f2eddd6a456a40))
	ROM_LOAD16_BYTE( "4d72.u62", 0x7800<<1, 0x0800, CRC(aa57c594) SHA1(741860d489d90f5882ca53daa3169b6abacdf130))
	ROM_LOAD16_BYTE( "4d71.u63", (0x7800<<1)+1, 0x0800, CRC(069b2f0b) SHA1(070850bb32f8474107cc52c5183cfaa32d640f9a))

	ROM_REGION(0x10000,"keyboard",0)
	ROM_LOAD( "0104.u20",  0xc000, 0x1000, CRC(5c6f1256) SHA1(271931fb354dfae6a1a5697ee888924a89a15ca8))
	ROM_RELOAD( 0xe000, 0x1000 )
	ROM_LOAD("cpu2d.u21",  0xd000, 0x1000, CRC(2c2dba33) SHA1(0db5d177fec3f8ae89abeef2e6900ad4f3460266))
	ROM_RELOAD( 0xf000, 0x1000 )

	ROM_REGION(0x00800,"gfx1",0)
	ROM_LOAD( "4c52.u34",  0x0000, 0x0800, CRC(cbeb2e96) SHA1(f0e17adcd278fb376c9f90833c7fbbb60193dbe3))

	ROM_REGION(0x0100,"proms",0)
	ROM_LOAD( "0360.u58", 0x00, 0x20, CRC(1295528a) SHA1(b35e598891f1185e02cbacb4811d2334357abd79))
	ROM_LOAD( "0370.u74", 0x20, 0x20, CRC(19da5096) SHA1(76af50e4fd29649fc4837120c245321a8fc84cd3))
ROM_END

void intv_state::init_intv()
{
	m_stic->set_x_scale(INTV_X_SCALE);
	m_stic->set_y_scale(INTV_Y_SCALE);
	m_is_keybd = 0;
}

void intv_state::init_intvkbd()
{
	m_stic->set_x_scale(INTVKBD_X_SCALE);
	m_stic->set_y_scale(INTVKBD_Y_SCALE);
	m_is_keybd = 1;
}


/***************************************************************************

  Game driver(s)

***************************************************************************/

/*    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT    CLASS       INIT          COMPANY               FULLNAME, FLAGS */
CONS( 1979, intv,     0,      0,      intv,     0,       intv_state, init_intv,    "Mattel Electronics", "Intellivision", MACHINE_SUPPORTS_SAVE )
CONS( 1981, intvsrs,  intv,   0,      intv,     0,       intv_state, init_intv,    "Sears",              "Super Video Arcade", MACHINE_SUPPORTS_SAVE )
COMP( 1981, intvkbd,  intv,   0,      intvkbd,  intvkbd, intv_state, init_intvkbd, "Mattel Electronics", "Intellivision Keyboard Component (Unreleased)", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
CONS( 1982, intv2,    intv,   0,      intv2,    0,       intv_state, init_intv,    "Mattel Electronics", "Intellivision II", MACHINE_SUPPORTS_SAVE )

// made up, user friendlier machines with pre-mounted passthu expansions
COMP( 1982, intvoice, intv,   0,      intvoice, 0,       intv_state, init_intv,    "Mattel Electronics", "Intellivision w/IntelliVoice expansion", MACHINE_SUPPORTS_SAVE )
COMP( 1983, intvecs,  intv,   0,      intvecs,  0,       intv_state, init_intv,    "Mattel Electronics", "Intellivision w/Entertainment Computer System + Intellivoice expansions", MACHINE_SUPPORTS_SAVE )



ip12.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * Silicon Graphics IP12 systems.
 *
 *   Year  Model   Board  Type  CPU    Clock  I/D Cache    Code Name(s)
 *   1991  4D/30   IP14   IP12  R3000  30MHz  64KiB/64KiB  Magnum
 *   1991  4D/35   IP12   IP12  R3000  36MHz  64KiB/64KiB  Magnum
 *   1991  Indigo  IP12   IP12  R3000  33MHz  32KiB/32KiB  Hollywood, 4DRPC, HP1
 *
 * TODO:
 *  - VME-based V30/V35
 *  - DSP
 *
 */

/*
 * WIP
 * ---
 * setenv bootmode d  # enable diagnostic output (bootmode c is normal)
 *
 * ide usage: report=5; <test>
 *
 * installing IRIX 4.0.5/5.3 (IRIX CDROM at SCSI ID 4):
 *  - boot -f dksc(0,4,8)sashIP12
 *  - boot -f dksc(0,4,7)stand/fx.IP12 --x
 *  - create root partition and label disk
 *  - use firmware option 2 and inst to install OS from CDROM
 *
 * after installation, disable windowing system (until graphics work):
 *  - shroot (or sh and prefix commands with chroot /root)
 *  - /etc/chkconfig -f windowsystem off
 *  - /etc/chkconfig -f xdm off
 *  - for IRIX 5.3, also edit /etc/inittab to spawn getty on console
 *
 */

#include "emu.h"
#include "cpu/mips/mips1.h"
#include "cpu/dsp56000/dsp56000.h"

#include "machine/dp8573a.h"
#include "machine/edlc.h"
#include "machine/eepromser.h"
#include "machine/input_merger.h"
#include "machine/nscsi_bus.h"
#include "machine/wd33c9x.h"
#include "machine/z80scc.h"

#include "bus/rs232/rs232.h"
#include "bus/rs232/hlemouse.h"
#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"

#include "hpc1.h"
#include "int2.h"
#include "kbd.h"
#include "light.h"
#include "pic1.h"

//#define VERBOSE (LOG_GENERAL)
#include "logmacro.h"

namespace {

class ip12_state : public driver_device
{
public:
	ip12_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_pic(*this, "pic")
		, m_hpc(*this, "hpc")
		, m_int(*this, "int")
		, m_nvram(*this, "nvram")
		, m_rtc(*this, "rtc")
		, m_scsi(*this, "scsi:0:wd33c93a")
		, m_eth(*this, "eth")
		, m_scc(*this, "scc%u", 0U)
		, m_scc_irq(*this, "scc_irq")
		, m_dsp(*this, "dsp")
		, m_gfx(*this, "gfx")
	{
	}

	void indigo(machine_config &config);
	void pi4d30(machine_config &config);
	void pi4d35(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void ip12(machine_config &config);
	void pi4d3x(machine_config &config);

	void ip12_map(address_map &map) ATTR_COLD;
	void pi4d3x_map(address_map &map) ATTR_COLD;
	void pbus_map(address_map &map) ATTR_COLD;

	required_device<r3000a_device> m_cpu;
	required_device<sgi_pic1_device> m_pic;
	required_device<hpc1_device> m_hpc;
	required_device<sgi_int2_device> m_int;
	required_device<eeprom_serial_93cxx_device> m_nvram;
	required_device<dp8572a_device> m_rtc;
	required_device<wd33c93a_device> m_scsi;
	required_device<seeq8003_device> m_eth;
	required_device_array<scc85c30_device, 3> m_scc;
	required_device<input_merger_any_high_device> m_scc_irq;
	required_device<dsp56001_device> m_dsp;
	required_device<sgi_lg1_device> m_gfx;

	/*
	 * board revisions according to NetBSD source:
	 *   0x0000-0x6000 -> IP12 4D/3x
	 *   0x7000        -> IP12 VIP12
	 *   0x8000-0xd000 -> IP12 HP1
	 *   0xe000-0xf000 -> IP12 HPLC
	 */
	u32 m_brdrev;
};

void ip12_state::machine_start()
{
}

void ip12_state::machine_reset()
{
}

void ip12_state::ip12_map(address_map &map)
{
	// 0x0000'0000-0fff'ffff // local memory
	// 0x1000'0000-1eff'ffff // vme
	// 0x1f00'0000-1fbf'ffff // local i/o
	// 0x1fc0'0000-1fff'ffff // boot prom

	// 0x2000'0000-3fff'ffff // vme (normally inaccessible)
	// 0x4000'0000-ffff'ffff // unused (normally inaccessible), bus error (read) interrupt (write)

	map(0x1000'0000, 0x1eff'ffff).lr32([this]() { m_cpu->berr_w(1); return 0; }, "buserror"); // vme

	map(0x1f3f'0000, 0x1f3f'7fff).m(m_gfx, FUNC(sgi_lg1_device::map));
	// 1f3f'8000-1f3f'ffff // lg2x2 second head

	// 0x1f40'0000-0x1f5'ffff gio slot 0
	// 0x1f60'0000-0x1f7'ffff gio slot 1

	//map(0x1f90'0000, 0x1f97'ffff); // hpc 3
	//map(0x1f98'0000, 0x1f9f'ffff); // hpc 2
	map(0x1fa0'0000, 0x1faf'ffff).m(m_pic, FUNC(sgi_pic1_device::map));
	//map(0x1fb0'0000, 0x1fb7'ffff); // hpc 1
	//map(0x1fb8'0000, 0x1fbf'ffff); // hpc 0

	map(0x1fb8'0000, 0x1fb8'ffff).m(m_hpc, FUNC(hpc1_device::map));
	map(0x1fb8'0100, 0x1fb8'011f).m(m_eth, FUNC(seeq8003_device::map)).umask32(0xff);
	map(0x1fb8'0120, 0x1fb8'0127).rw(m_scsi, FUNC(wd33c93a_device::indir_r), FUNC(wd33c93a_device::indir_w)).umask32(0xff00);
	map(0x1fb8'01c0, 0x1fb8'01ff).m(m_int, FUNC(sgi_int2_device::map)).umask32(0xff);
	map(0x1fb8'0d00, 0x1fb8'0d0f).rw(m_scc[0], FUNC(scc85c30_device::ab_dc_r), FUNC(scc85c30_device::ab_dc_w)).umask32(0xff);
	map(0x1fb8'0d10, 0x1fb8'0d1f).rw(m_scc[1], FUNC(scc85c30_device::ab_dc_r), FUNC(scc85c30_device::ab_dc_w)).umask32(0xff);
	map(0x1fb8'0d30, 0x1fb8'0d3f).noprw(); // TODO: left/right headphone gain mdacs
	map(0x1fb8'0e00, 0x1fb8'0e7f).rw(m_rtc, FUNC(dp8572a_device::read), FUNC(dp8572a_device::write)).umask32(0xff);

	map(0x1fbd'0000, 0x1fbd'0003).lr32([this]() { return m_brdrev; }, "board_rev"); // board revision register

	map(0x1fbe'0000, 0x1fbf'ffff).ram().share("dsp_ram"); // 3xTC55328J-35 (24 bits x 32k)

	map(0x1fc0'0000, 0x1fc3'ffff).rom().region("prom", 0);
}

void ip12_state::pi4d3x_map(address_map &map)
{
	ip12_map(map);

	map(0x1fb8'0d20, 0x1fb8'0d2f).rw(m_scc[2], FUNC(scc85c30_device::ab_dc_r), FUNC(scc85c30_device::ab_dc_w)).umask32(0xff);
}

// HPC1 PBUS decode?
// 00100-0011f == 00040-00047 -> net
// 00120-00127 == 00048-00049 -> scsi
// 001c0-001ff == 00070-0007f -> int2
// 00d00-00d3f == 00340-0034f -> fff0-ffff  duart
// 00e00-00e7f == 00380-0039f -> ffc0-ffdf  rtc
// 60000-7ffff == 18000-1ffff -> 8000-ffff? ram
//
// Actel/AUD1 and audio adc/dac via DSP SCI
//
void ip12_state::pbus_map(address_map &map)
{
	map(0xffc0, 0xffdf).rw(m_rtc, FUNC(dp8572a_device::read), FUNC(dp8572a_device::write));

	map(0xfff0, 0xfff3).rw(m_scc[0], FUNC(scc85c30_device::ab_dc_r), FUNC(scc85c30_device::ab_dc_w));
	map(0xfff4, 0xfff7).rw(m_scc[1], FUNC(scc85c30_device::ab_dc_r), FUNC(scc85c30_device::ab_dc_w));
	map(0xfff8, 0xfffb).rw(m_scc[2], FUNC(scc85c30_device::ab_dc_r), FUNC(scc85c30_device::ab_dc_w));
	//map(0xfffc, 0xffff).rw(m_scc[3], FUNC(scc85c30_device::ab_dc_r), FUNC(scc85c30_device::ab_dc_w));
}

static DEVICE_INPUT_DEFAULTS_START(pi4d3x_pic1)
	DEVICE_INPUT_DEFAULTS("VALID", 0x0f, 0x0f)
DEVICE_INPUT_DEFAULTS_END

static DEVICE_INPUT_DEFAULTS_START(indigo_pic1)
	DEVICE_INPUT_DEFAULTS("VALID", 0x0f, 0x07)
DEVICE_INPUT_DEFAULTS_END

static void scsi_devices(device_slot_interface &device)
{
	device.option_add("cdrom", NSCSI_CDROM_SGI).machine_config(
		[](device_t *device)
		{
			downcast<nscsi_cdrom_device &>(*device).set_block_size(512);
		});
	device.option_add("harddisk", NSCSI_HARDDISK);
}

void ip12_state::ip12(machine_config &config)
{
	SGI_PIC1(config, m_pic, 0);
	m_pic->set_bus(m_cpu, AS_PROGRAM);

	SGI_HPC1(config, m_hpc, 0);
	m_hpc->set_addrmap(0, &ip12_state::pbus_map);
	m_hpc->set_gio(m_cpu, AS_PROGRAM);
	m_hpc->set_enet(m_eth);
	m_hpc->int_w().set(m_int, FUNC(sgi_int2_device::lio0_w<sgi_int2_device::LIO0_ETHERNET>));
	m_hpc->dma_r_cb<0>().set(m_scsi, FUNC(wd33c93a_device::dma_r));
	m_hpc->dma_w_cb<0>().set(m_scsi, FUNC(wd33c93a_device::dma_w));
	m_hpc->eeprom_dati().set(m_nvram, FUNC(eeprom_serial_93cxx_device::do_read));
	m_hpc->eeprom_out().set(
		[this](u8 data)
		{
			// TODO: bit 0 console led
			m_nvram->di_write(BIT(data, 3));
			m_nvram->cs_write(BIT(data, 1));
			m_nvram->clk_write(BIT(data, 2));
		});

	SGI_INT2(config, m_int, 10_MHz_XTAL);
	m_int->write_intr<1>().set_inputline(m_cpu, INPUT_LINE_IRQ1);
	m_int->write_intr<2>().set_inputline(m_cpu, INPUT_LINE_IRQ2);
	m_int->write_intr<3>().set_inputline(m_cpu, INPUT_LINE_IRQ3);
	m_int->write_intr<4>().set_inputline(m_cpu, INPUT_LINE_IRQ4);
	m_int->write_intr<5>().set_inputline(m_cpu, INPUT_LINE_IRQ5);
	// TODO: write_led and write_poweroff outputs
	//
	//  0  scc.clk.sel      external clock on for hp1 port 0
	//  1  ser0.sel         rts/tx+ for hp1 port 0 (rts=1)
	//  2  ser1.sel         rts/tx+ for hp1 port 1 (rts=1)
	//  3  clr.retrace-     disable/enable vert. retrace intr.
	//  4  poweroff

	EEPROM_93C56_16BIT(config, m_nvram);

	DP8572A(config, m_rtc, 32.768_kHz_XTAL).set_use_utc(true);

	NSCSI_BUS(config, "scsi", 0);
	NSCSI_CONNECTOR(config, "scsi:0").option_set("wd33c93a", WD33C93A).machine_config(
		[this](device_t *device)
		{
			wd33c93a_device &scsi = downcast<wd33c93a_device &>(*device);

			scsi.set_clock(10_MHz_XTAL);
			scsi.irq_cb().set(m_int, FUNC(sgi_int2_device::lio0_w<sgi_int2_device::LIO0_SCSI>));
			scsi.drq_cb().set(m_hpc, FUNC(hpc1_device::write_drq<0>));
		});
	NSCSI_CONNECTOR(config, "scsi:1", scsi_devices, "harddisk", false);
	NSCSI_CONNECTOR(config, "scsi:2", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:3", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:4", scsi_devices, "cdrom", false);
	NSCSI_CONNECTOR(config, "scsi:5", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:6", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:7", scsi_devices, nullptr, false);

	SEEQ8003(config, m_eth, 0);
	m_eth->out_int_cb().set(m_hpc, FUNC(hpc1_device::write_int));
	m_eth->out_rxrdy_cb().set(m_hpc, FUNC(hpc1_device::write_drq<1>));
	m_hpc->dma_r_cb<1>().set(m_eth, FUNC(seeq8003_device::fifo_r));
	m_hpc->dma_w_cb<1>().set(m_eth, FUNC(seeq8003_device::fifo_w));

	// 24.576MHz
	// 22.5792MHz
	// 3.6720MHz
	input_merger_any_high_device &scc_irq(INPUT_MERGER_ANY_HIGH(config, "scc_irq"));
	scc_irq.output_handler().set(m_int, FUNC(sgi_int2_device::lio0_w<sgi_int2_device::LIO0_DUART>));

	// duart 0: keyboard/mouse ports
	SCC85C30(config, m_scc[0], 10_MHz_XTAL);
	m_scc[0]->configure_channels(3'686'400, 0, 3'686'400, 0);
	m_scc[0]->out_int_callback().set(scc_irq, FUNC(input_merger_any_high_device::in_w<0>));

	sgi_kbd_port_device &kbd_port(SGI_KBD_PORT(config, "keyboard_port"));
	kbd_port.option_set("keyboard", SGI_KBD);
	rs232_port_device &mouse_port(RS232_PORT(config, "mouse_port", 0));
	mouse_port.option_set("mouse", SGI_HLE_SERIAL_MOUSE);
	m_scc[0]->out_txda_callback().set(kbd_port, FUNC(sgi_kbd_port_device::write_txd));
	kbd_port.rxd_handler().set(m_scc[0], FUNC(scc85c30_device::rxa_w));
	mouse_port.rxd_handler().set(m_scc[0], FUNC(scc85c30_device::rxb_w));

	// duart 1: serial ports
	SCC85C30(config, m_scc[1], 10_MHz_XTAL);
	m_scc[1]->configure_channels(3'686'400, 0, 3'686'400, 0);
	m_scc[1]->out_int_callback().set(scc_irq, FUNC(input_merger_any_high_device::in_w<1>));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, nullptr));
	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	m_scc[1]->out_txda_callback().set(rs232a, FUNC(rs232_port_device::write_txd));
	m_scc[1]->out_dtra_callback().set(rs232a, FUNC(rs232_port_device::write_dtr));
	m_scc[1]->out_rtsa_callback().set(rs232a, FUNC(rs232_port_device::write_rts));
	m_scc[1]->out_txdb_callback().set(rs232b, FUNC(rs232_port_device::write_txd));
	m_scc[1]->out_dtrb_callback().set(rs232b, FUNC(rs232_port_device::write_dtr));
	m_scc[1]->out_rtsb_callback().set(rs232b, FUNC(rs232_port_device::write_rts));
	rs232a.cts_handler().set(m_scc[1], FUNC(scc85c30_device::ctsa_w));
	rs232a.dcd_handler().set(m_scc[1], FUNC(scc85c30_device::dcda_w));
	rs232a.rxd_handler().set(m_scc[1], FUNC(scc85c30_device::rxa_w));
	rs232b.cts_handler().set(m_scc[1], FUNC(scc85c30_device::ctsb_w));
	rs232b.dcd_handler().set(m_scc[1], FUNC(scc85c30_device::dcdb_w));
	rs232b.rxd_handler().set(m_scc[1], FUNC(scc85c30_device::rxb_w));

	// duart 2: "Apple" RS-422 serial ports (4D/PI only)
	SCC85C30(config, m_scc[2], 10_MHz_XTAL); // Z8513010VSC ESCC
	m_scc[2]->configure_channels(3'686'400, 0, 3'686'400, 0);
	m_scc[2]->out_int_callback().set(scc_irq, FUNC(input_merger_any_high_device::in_w<1>));

	DSP56001(config, m_dsp, 20_MHz_XTAL);
	//m_dsp->moda_w(1);
	//m_dsp->modb_w(0);

	SGI_LG1(config, m_gfx);
	m_gfx->write_vblank().set(m_int, FUNC(sgi_int2_device::lio1_w<sgi_int2_device::LIO1_GIO2>));
}

void ip12_state::pi4d3x(machine_config &config)
{
	ip12(config);

	m_pic->set_input_default(DEVICE_INPUT_DEFAULTS_NAME(pi4d3x_pic1));

	// serial port 3
	// FIXME: HSKO/HSKI/GPI
	rs232_port_device &rs232c(RS232_PORT(config, "rs232c", default_rs232_devices, nullptr));
	m_scc[2]->out_txda_callback().set(rs232c, FUNC(rs232_port_device::write_txd));
	rs232c.rxd_handler().set(m_scc[2], FUNC(z80scc_device::rxa_w));

	// serial port 4
	// FIXME: HSKO/HSKI/GPI
	rs232_port_device &rs232d(RS232_PORT(config, "rs232d", default_rs232_devices, nullptr));
	m_scc[2]->out_txdb_callback().set(rs232d, FUNC(rs232_port_device::write_txd));
	rs232d.rxd_handler().set(m_scc[2], FUNC(z80scc_device::rxb_w));
}

void ip12_state::indigo(machine_config &config)
{
	R3000A(config, m_cpu, 66.0_MHz_XTAL / 2, 32768, 32768);
	m_cpu->set_addrmap(AS_PROGRAM, &ip12_state::ip12_map);
	// FIXME: route fpu interrupt via int2
	m_cpu->set_fpu(r3000a_device::MIPS_R3010A, INPUT_LINE_IRQ0);

	ip12(config);

	m_pic->set_input_default(DEVICE_INPUT_DEFAULTS_NAME(indigo_pic1));

	m_brdrev = 0x8000;
}

void ip12_state::pi4d30(machine_config &config)
{
	R3000A(config, m_cpu, 30_MHz_XTAL, 65536, 65536);
	m_cpu->set_addrmap(AS_PROGRAM, &ip12_state::pi4d3x_map);
	// FIXME: route fpu interrupt via int2
	m_cpu->set_fpu(r3000a_device::MIPS_R3010A, INPUT_LINE_IRQ0);

	pi4d3x(config);

	m_brdrev = 0x0000;
}

void ip12_state::pi4d35(machine_config &config)
{
	R3000A(config, m_cpu, 36_MHz_XTAL, 65536, 65536);
	m_cpu->set_addrmap(AS_PROGRAM, &ip12_state::pi4d3x_map);
	// FIXME: route fpu interrupt via int2
	m_cpu->set_fpu(r3000a_device::MIPS_R3010A, INPUT_LINE_IRQ0);

	pi4d3x(config);

	// FIXME: higher revisions fail scsi power fuse diagnostic
	m_brdrev = 0x0000;
}

ROM_START(indigo)
	ROM_REGION32_BE(0x40000, "prom", 0) // Am27C2048 128Kx16

	// dumped over serial connection from boot monitor and swapped
	ROM_SYSTEM_BIOS(0, "401-rev-c", "SGI Version 4.0.1 Rev C LG1/GR2, Jul 9, 1992")
	ROMX_LOAD("ip12prom.070-8088-xxx.u56", 0x000000, 0x040000, CRC(25ca912f) SHA1(94b3753d659bfe50b914445cef41290122f43880), ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(0))

	// dumped with EPROM programmer
	ROM_SYSTEM_BIOS(1, "401-rev-d", "SGI Version 4.0.1 Rev D LG1/GR2, Mar 24, 1992")
	ROMX_LOAD("ip12prom.070-8088-002.u56", 0x000000, 0x040000, CRC(ea4329ef) SHA1(b7d67d0e30ae8836892f7170dd4757732a0a3fd6), ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "sni", "SGI Version 4.0.1 Rev C LG1/GR2,  Feb 14, 1992")
	ROMX_LOAD("070-8088-001__a.u56", 0x000000, 0x040000, CRC(40eae7a0) SHA1(d60ef74cf04a16d9dad6b9594a66724d944b1208), ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(2))
ROM_END

ROM_START(pi4d30)
	ROM_REGION32_BE(0x80000, "prom", 0)
	ROM_SYSTEM_BIOS(0, "4.0.1c", "SGI Version 4.0.1 Rev C GR1/GR2/LG1,  Feb 14, 1992")
	ROMX_LOAD("ip14prom.bin", 0x000000, 0x080000, NO_DUMP, ROM_BIOS(0))
ROM_END

ROM_START(pi4d35)
	ROM_REGION32_BE(0x80000, "prom", 0) // TC574096D-120 (262,144x16-bit EEPROM)

	ROM_SYSTEM_BIOS(0, "4.0.1d", "SGI Version 4.0.1 Rev D LG1/GR2,  Mar 24, 1992")
	ROMX_LOAD("ip12prom.002be.u61", 0x000000, 0x040000, CRC(d35f105c) SHA1(3d08dfb961d7512bd8ed41cb6e01e89d14134f09), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "4.0.1c", "SGI Version 4.0.1 Rev C GR1/GR2/LG1,  Feb 14, 1992")
	ROMX_LOAD("ip12prom.070-8086-002.u61", 0x000000, 0x080000, CRC(543cfc3f) SHA1(a7331876f11bff40c960f822923503eca17191c5), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "4.0a", "SGI Version 4.0 Rev A IP12,  Aug 22, 1991")
	ROMX_LOAD("ip12prom.070-8045-002.u61", 0x000000, 0x040000, CRC(fe999bae) SHA1(eb054c365a6e018be3b9ae44169c0ffc6447c6f0), ROM_BIOS(2))
ROM_END

} // anonymous namespace

//   YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY             FULLNAME               FLAGS
COMP(1991, pi4d30,  0,      0,      pi4d30,  0,     ip12_state, empty_init, "Silicon Graphics", "Personal IRIS 4D/30", MACHINE_NO_SOUND | MACHINE_IMPERFECT_GRAPHICS)
COMP(1991, pi4d35,  0,      0,      pi4d35,  0,     ip12_state, empty_init, "Silicon Graphics", "Personal IRIS 4D/35", MACHINE_NO_SOUND | MACHINE_IMPERFECT_GRAPHICS)
COMP(1991, indigo,  0,      0,      indigo,  0,     ip12_state, empty_init, "Silicon Graphics", "IRIS Indigo",         MACHINE_NO_SOUND | MACHINE_IMPERFECT_GRAPHICS)



ip20.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * Silicon Graphics Indigo R4000/R4400 aka 4DRPC-50/Blackjack/HP2 (also VME-based V50)
 *
 * INT2    local I/O interrupt multiplexor
 * HPC1.5  high-performance peripheral controller
 * MC      memory controller
 *
 */
 /* board revs of IP20 are: 1 & 2 were early blackjack spins
  * 10 decimal == 0x0a == 1010binary == VME IP 20 board (V50)
  * V50 board lacks audio hardware
  * The upper bit used to change the standard (rev 2) board
  * to the V50 designation (i.e the 0x8 bit) used to be known
  * internally as the 'Hollywood processor 1 bit' or hp1 bit
  */

#include "emu.h"
#include "cpu/mips/r4000.h"
#include "cpu/dsp56000/dsp56000.h"

#include "machine/dp8573a.h"
#include "machine/edlc.h"
#include "machine/eepromser.h"
#include "machine/input_merger.h"
#include "machine/nscsi_bus.h"
#include "machine/wd33c9x.h"
#include "machine/z80scc.h"

#include "bus/rs232/rs232.h"
#include "bus/rs232/hlemouse.h"
#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"

#include "hpc1.h"
#include "int2.h"
#include "kbd.h"
#include "light.h"
#include "mc.h"

//#define VERBOSE (LOG_GENERAL)
#include "logmacro.h"

namespace {

class ip20_state : public driver_device
{
public:
	ip20_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_eerom(*this, "eerom")
		, m_mc(*this, "mc")
		, m_hpc(*this, "hpc")
		, m_int(*this, "int")
		, m_nvram(*this, "nvram")
		, m_rtc(*this, "rtc")
		, m_scsi(*this, "scsi:0:wd33c93a")
		, m_eth(*this, "eth")
		, m_scc(*this, "scc%u", 0U)
		, m_dsp(*this, "dsp")
		, m_gfx(*this, "gfx")
	{
	}

	void indigo_r4000(machine_config &config);
	void indigo_r4400(machine_config &config);

protected:
	void ip20(machine_config &config);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void cpu_map(address_map &map) ATTR_COLD;

	required_device<r4000_base_device> m_cpu;
	required_device<eeprom_serial_93cxx_device> m_eerom;
	required_device<sgi_mc_device> m_mc;
	required_device<hpc1_device> m_hpc;
	required_device<sgi_int2_device> m_int;
	required_device<eeprom_serial_93cxx_device> m_nvram;
	required_device<dp8572a_device> m_rtc;
	required_device<wd33c93a_device> m_scsi;
	required_device<seeq8003_device> m_eth;
	required_device_array<scc85c30_device, 3> m_scc;
	required_device<dsp56001_device> m_dsp;
	required_device<sgi_lg1_device> m_gfx;
};

void ip20_state::machine_start()
{
}

void ip20_state::machine_reset()
{
}

void ip20_state::cpu_map(address_map &map)
{
	//map(0x1fbd9000, 0x1fbd903f).rw(FUNC(indigo_state::int_r), FUNC(indigo_state::int_w));

	//map(0x0000'0000, 0x0007'ffff); // system memory alias (512K)
	//map(0x0008'0000, 0x0008'ffff); // EISA I/O space (64K)
	//map(0x0009'0000, 0x0009'ffff); // EISA I/O space alias (64K)
	//map(0x000a'0000, 0x07ff'ffff); // EISA memory (128M)
	//map(0x0800'0000, 0x17ff'ffff); // physical memory segment 0 (256M)
	//map(0x1800'0000, 0x1eff'ffff); // reserved (future GIO space) (112M)
	//map(0x1f00'0000, 0x1f3f'ffff); // graphics system (4M)
	//map(0x1f40'0000, 0x1f5f'ffff); // gio64 expansion slot 0 (2M)
	//map(0x1f60'0000, 0x1f9f'ffff); // gio64 expansion slot 1 (4M)
	//map(0x1fa0'0000, 0x1faf'ffff); // mc registers (1M)
	//map(0x1fb0'0000, 0x1fbf'ffff); // hpc and i/o devices (1M)
	//map(0x1fc0'0000, 0x1fff'ffff); // boot prom (4M)
	//map(0x2000'0000, 0x2fff'ffff); // physical memory segment 1 (256M)
	//map(0x3000'0000, 0x7fff'ffff); // reserved (1.25G)
	//map(0x8000'0000, 0xffff'ffff); // EISA memory (2G)

	//map(0x1000'0000, 0x1eff'ffff).lr32([this]() { m_cpu->berr_w(1); return 0; }, "buserror"); // vme

	map(0x1f3f'0000, 0x1f3f'7fff).m(m_gfx, FUNC(sgi_lg1_device::map));

	map(0x1fa0'0000, 0x1fa1'ffff).rw(m_mc, FUNC(sgi_mc_device::read), FUNC(sgi_mc_device::write));

	map(0x1fb8'0000, 0x1fb8'ffff).m(m_hpc, FUNC(hpc1_device::map));
	map(0x1fb8'0100, 0x1fb8'011f).m(m_eth, FUNC(seeq8003_device::map)).umask32(0xff);
	map(0x1fb8'0120, 0x1fb8'0127).rw(m_scsi, FUNC(wd33c93a_device::indir_r), FUNC(wd33c93a_device::indir_w)).umask32(0xff00);
	map(0x1fb8'01c0, 0x1fb8'01ff).m(m_int, FUNC(sgi_int2_device::map)).umask32(0xff);
	map(0x1fb8'0d00, 0x1fb8'0d0f).rw(m_scc[0], FUNC(scc85c30_device::ab_dc_r), FUNC(scc85c30_device::ab_dc_w)).umask32(0xff);
	map(0x1fb8'0d10, 0x1fb8'0d1f).rw(m_scc[1], FUNC(scc85c30_device::ab_dc_r), FUNC(scc85c30_device::ab_dc_w)).umask32(0xff);
	//map(0x1fb8'0d20, 0x1fb8'0d2f).rw(m_scc[2], FUNC(scc85c30_device::ab_dc_r), FUNC(scc85c30_device::ab_dc_w)).umask32(0xff); // V50 only?
	map(0x1fb8'0d30, 0x1fb8'0d37).noprw(); // TODO: left/right headphone gain mdacs
	map(0x1fb8'0e00, 0x1fb8'0e7f).rw(m_rtc, FUNC(dp8572a_device::read), FUNC(dp8572a_device::write)).umask32(0xff);
	map(0x1fbd'0000, 0x1fbd'0003).lr32([]() { return 0x0000'8000; }, "board_rev"); // board revision register

	map(0x1fbe'0000, 0x1fbf'ffff).ram().share("dsp_ram"); // 3xTC55328J-35 (24 bits x 32k)

	map(0x1fc0'0000, 0x1fc7'ffff).rom().region("prom", 0);
}

static DEVICE_INPUT_DEFAULTS_START(ip20_mc)
	DEVICE_INPUT_DEFAULTS("VALID", 0x0f, 0x07)
DEVICE_INPUT_DEFAULTS_END

static void scsi_devices(device_slot_interface &device)
{
	device.option_add("cdrom", NSCSI_CDROM_SGI).machine_config(
		[](device_t *device)
		{
			downcast<nscsi_cdrom_device &>(*device).set_block_size(512);
		});
	device.option_add("harddisk", NSCSI_HARDDISK);
}

void ip20_state::indigo_r4000(machine_config &config)
{
	R4000(config, m_cpu, 50'000'000);

	ip20(config);
}

void ip20_state::indigo_r4400(machine_config &config)
{
	R4400(config, m_cpu, 75'000'000);

	ip20(config);
}

void ip20_state::ip20(machine_config &config)
{
	m_cpu->set_addrmap(AS_PROGRAM, &ip20_state::cpu_map);

	EEPROM_93C56_16BIT(config, m_eerom);

	SGI_MC(config, m_mc, m_cpu, m_eerom, 50'000'000);
	m_mc->eisa_present().set_constant(0);
	m_mc->set_input_default(DEVICE_INPUT_DEFAULTS_NAME(ip20_mc));

	SGI_HPC1(config, m_hpc, 0);
	m_hpc->set_gio(m_cpu, AS_PROGRAM);
	m_hpc->set_enet(m_eth);
	m_hpc->int_w().set(m_int, FUNC(sgi_int2_device::lio0_w<sgi_int2_device::LIO0_ETHERNET>));
	m_hpc->dma_r_cb<0>().set(m_scsi, FUNC(wd33c93a_device::dma_r));
	m_hpc->dma_w_cb<0>().set(m_scsi, FUNC(wd33c93a_device::dma_w));
	m_hpc->eeprom_dati().set(m_nvram, FUNC(eeprom_serial_93cxx_device::do_read));
	m_hpc->eeprom_out().set(
		[this](u8 data)
		{
			// TODO: bit 0 console led
			m_nvram->di_write(BIT(data, 3));
			m_nvram->cs_write(BIT(data, 1));
			m_nvram->clk_write(BIT(data, 2));
		});

	SGI_INT2(config, m_int, 10_MHz_XTAL);
	m_int->write_intr<1>().set_inputline(m_cpu, INPUT_LINE_IRQ0);
	m_int->write_intr<2>().set_inputline(m_cpu, INPUT_LINE_IRQ1);
	// TODO: write_led and write_poweroff outputs
	//
	//  0   cache parity error?
	//  1   1Hz heartbeat
	//  2   off when cpu is idle
	//  3   graphics
	//  4   poweroff?

	EEPROM_93C56_16BIT(config, m_nvram);

	DP8572A(config, m_rtc, 32.768_kHz_XTAL).set_use_utc(true);

	NSCSI_BUS(config, "scsi", 0);
	NSCSI_CONNECTOR(config, "scsi:0").option_set("wd33c93a", WD33C93A).machine_config(
		[this](device_t *device)
		{
			wd33c93a_device &scsi = downcast<wd33c93a_device &>(*device);

			scsi.set_clock(10_MHz_XTAL);
			scsi.irq_cb().set(m_int, FUNC(sgi_int2_device::lio0_w<sgi_int2_device::LIO0_SCSI>));
			scsi.drq_cb().set(m_hpc, FUNC(hpc1_device::write_drq<0>));
		});
	NSCSI_CONNECTOR(config, "scsi:1", scsi_devices, "harddisk", false);
	NSCSI_CONNECTOR(config, "scsi:2", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:3", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:4", scsi_devices, "cdrom", false);
	NSCSI_CONNECTOR(config, "scsi:5", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:6", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:7", scsi_devices, nullptr, false);

	SEEQ8003(config, m_eth, 0);
	m_eth->out_int_cb().set(m_hpc, FUNC(hpc1_device::write_int));
	m_eth->out_rxrdy_cb().set(m_hpc, FUNC(hpc1_device::write_drq<1>));
	m_hpc->dma_r_cb<1>().set(m_eth, FUNC(seeq8003_device::fifo_r));
	m_hpc->dma_w_cb<1>().set(m_eth, FUNC(seeq8003_device::fifo_w));

	input_merger_any_high_device &scc_irq(INPUT_MERGER_ANY_HIGH(config, "scc_irq"));
	scc_irq.output_handler().set(m_int, FUNC(sgi_int2_device::lio0_w<sgi_int2_device::LIO0_DUART>));

	// duart 0
	SCC85C30(config, m_scc[0], 10_MHz_XTAL);
	m_scc[0]->configure_channels(3'686'400, 0, 3'686'400, 0);
	m_scc[0]->out_int_callback().set(scc_irq, FUNC(input_merger_any_high_device::in_w<0>));

	sgi_kbd_port_device &kbd_port(SGI_KBD_PORT(config, "keyboard_port"));
	kbd_port.option_set("keyboard", SGI_KBD);
	rs232_port_device &mouse_port(RS232_PORT(config, "mouse_port", 0));
	mouse_port.option_set("mouse", SGI_HLE_SERIAL_MOUSE);
	m_scc[0]->out_txda_callback().set(kbd_port, FUNC(sgi_kbd_port_device::write_txd));
	kbd_port.rxd_handler().set(m_scc[0], FUNC(scc85c30_device::rxa_w));
	mouse_port.rxd_handler().set(m_scc[0], FUNC(scc85c30_device::rxb_w));

	// duart 1
	SCC85C30(config, m_scc[1], 10_MHz_XTAL);
	m_scc[1]->configure_channels(3'686'400, 0, 3'686'400, 0);
	m_scc[1]->out_int_callback().set(scc_irq, FUNC(input_merger_any_high_device::in_w<1>));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, nullptr));
	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	m_scc[1]->out_txda_callback().set(rs232a, FUNC(rs232_port_device::write_txd));
	m_scc[1]->out_dtra_callback().set(rs232a, FUNC(rs232_port_device::write_dtr));
	m_scc[1]->out_rtsa_callback().set(rs232a, FUNC(rs232_port_device::write_rts));
	m_scc[1]->out_txdb_callback().set(rs232b, FUNC(rs232_port_device::write_txd));
	m_scc[1]->out_dtrb_callback().set(rs232b, FUNC(rs232_port_device::write_dtr));
	m_scc[1]->out_rtsb_callback().set(rs232b, FUNC(rs232_port_device::write_rts));
	rs232a.cts_handler().set(m_scc[1], FUNC(scc85c30_device::ctsa_w));
	rs232a.dcd_handler().set(m_scc[1], FUNC(scc85c30_device::dcda_w));
	rs232a.rxd_handler().set(m_scc[1], FUNC(scc85c30_device::rxa_w));
	rs232b.cts_handler().set(m_scc[1], FUNC(scc85c30_device::ctsb_w));
	rs232b.dcd_handler().set(m_scc[1], FUNC(scc85c30_device::dcdb_w));
	rs232b.rxd_handler().set(m_scc[1], FUNC(scc85c30_device::rxb_w));

	// duart 2
	SCC85C30(config, m_scc[2], 10_MHz_XTAL);
	m_scc[2]->configure_channels(3'686'400, 0, 3'686'400, 0);
	m_scc[2]->out_int_callback().set(scc_irq, FUNC(input_merger_any_high_device::in_w<2>));

	DSP56001(config, m_dsp, 20_MHz_XTAL);

	SGI_LG1(config, m_gfx);
	m_gfx->write_vblank().set(m_int, FUNC(sgi_int2_device::lio1_w<sgi_int2_device::LIO1_GIO2>));
}

ROM_START(indigo_r4000)
	ROM_REGION64_BE(0x80000, "prom", 0)
	ROM_SYSTEM_BIOS(0, "405g-rev-b", "SGI Version 4.0.5G Rev B IP20, Nov 10, 1992") // dumped over serial connection from boot monitor and swapped
	ROMX_LOAD("ip20prom.070-8116-005.bin", 0x000000, 0x080000, CRC(1875b645) SHA1(52f5d7baea3d1bc720eb2164104c177e23504345), ROM_GROUPDWORD | ROM_REVERSE | ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "405d-rev-a", "SGI Version 4.0.5D Rev A IP20, Aug 19, 1992")
	ROMX_LOAD("ip20prom.070-8116-004.bin", 0x000000, 0x080000, CRC(940d960e) SHA1(596aba530b53a147985ff3f6f853471ce48c866c), ROM_GROUPDWORD | ROM_REVERSE | ROM_BIOS(1))

	// hand-made content sets eaddr 08:00:69:12:34:56 and netaddr 192.168.137.2
	ROM_REGION16_LE(0x100, "nvram", 0)
	ROM_LOAD("nvram.bin", 0x000, 0x100, CRC(b8367798) SHA1(61af4c9dba69e9f0552c10770f16044421730b6d))
ROM_END

#define rom_indigo_r4400 rom_indigo_r4000
} // anonymous namespace

//   YEAR  NAME          PARENT  COMPAT  MACHINE       INPUT  CLASS       INIT        COMPANY             FULLNAME             FLAGS
COMP(1992, indigo_r4000, 0,      0,      indigo_r4000, 0,     ip20_state, empty_init, "Silicon Graphics", "IRIS Indigo R4000", MACHINE_NO_SOUND | MACHINE_IMPERFECT_GRAPHICS)
COMP(1991, indigo_r4400, 0,      0,      indigo_r4400, 0,     ip20_state, empty_init, "Silicon Graphics", "IRIS Indigo R4400", MACHINE_NO_SOUND | MACHINE_IMPERFECT_GRAPHICS)



ip6.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * Silicon Graphics Personal IRIS 4D/20 and 4D/25.
 *
 *   Year  Model  Board  Type  CPU    Clock    I/D Cache    Code Name
 *   1988  4D/20  IP6    IP6   R2000  12.5MHz  16KiB/8KiB   Eclipse
 *   1989  4D/25  IP10   IP6   R3000  20MHz    64KiB/32KiB  Eclipse
 *         VIP10  VIP10  IP6   R3000  20MHz    64KiB/32KiB  VME Eclipse
 *
 * Sources:
 *   - VME-Eclipse CPU (VIP10) Specification, Silicon Graphics, Inc.
 *   - SGI IP-6 Schematic
 *   - http://www.futuretech.blinkenlights.nl/pitechrep.html
 *   - https://hardware.majix.org/computers/sgi.pi/index.shtml
 *   - http://archive.irix.cc/sgistuff/hardware/systems/personal.html
 *   - https://github.com/NetBSD/src/tree/trunk/sys/arch/sgimips/
 *
 * TODO:
 *  - audio, printer
 *  - devicify ioc1
 *  - ide test failures
 *    - lca2 (unimplemented fpga program/readback)
 *    - nvram4 (security mode?)
 *    - fpu (cvt.?.? invalid operation exceptions)
 */

 /*
  * WIP
  * --
  * 4D/2x doesn't support CDROM, so to install IRIX, mount the .iso
  * as if it's a hard disk: ... -scsi:6 harddisk -hard2 pathname.iso
  *
  * install IRIX 4.0.5/5.3:
  *  - monitor: eaddr 08:00:69:12:34:56                         # set ethernet mac address
  *  - monitor: init
  *  - monitor: dksc(0,6,8)sash.IP6 dksc(0,6,7)stand/fx.IP6 -x  # start fx disk partition/label utility
  *  - fx: r; ro; ..; l; sync; ..; exit                         # create root partition and label disk
  *  - monitor: setenv tapedevice dksc(0,6,8)
  *  - monitor: dksc(0,6,8)sash.IP6 -m                          # copy/boot from miniroot
  *  - inst: sh                                                 # escape to shell from inst
  *  - sh: mkdir /mnt; mount /dev/dsk/dks0d6s7 /mnt; exit       # mount distribution media
  *  - inst: from /mnt/dist
  *  - inst: go                                                 # may take ~1.5 hours
  *  - inst: shroot
  *  - sh: chkconfig windowsystem off; chkconfig xdm off        # disable graphics subsystems (until graphics works)
  *  - sh: edit /etc/hosts and /etc/inittab                     # change host IP address, enable getty on console
  *  - inst: quit                                               # complete installation and reboot
  */

#include "emu.h"

#include "ctl1.h"
#include "kbd.h"
#include "sgi_gr1.h"

// cpu and memory
#include "cpu/mips/mips1.h"
#include "cpu/dsp56000/dsp56000.h"
#include "machine/eepromser.h"

// other devices
#include "machine/am79c90.h"
#include "machine/dp8573a.h"
#include "machine/edlc.h"
#include "machine/mc68681.h"
#include "machine/pit8253.h"
#include "machine/wd33c9x.h"

// buses and connectors
#include "machine/nscsi_bus.h"
#include "bus/nscsi/hd.h"
#include "bus/nscsi/cd.h"
#include "bus/rs232/rs232.h"
#include "bus/rs232/hlemouse.h"

#include "softlist.h"

#include "4dpi.lh"

#define VERBOSE (LOG_GENERAL)
#include "logmacro.h"

namespace {

class ip6_state : public driver_device
{
public:
	ip6_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_ctl(*this, "ctl")
		, m_eeprom(*this, "eeprom")
		, m_rtc(*this, "rtc")
		, m_pit(*this, "pit")
		, m_scsi(*this, "scsi:0:wd33c93")
		, m_enet(*this, "enet")
		, m_duart(*this, "duart%u", 0U)
		, m_serial(*this, "serial%u", 0U)
		, m_gfx(*this, "gfx")
		, m_softlist(*this, "softlist")
		, m_leds(*this, "led%u", 0U)
		, m_vme_isr(0)
	{
	}

	void pi4d20(machine_config &config);
	void pi4d25(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<mips1_device_base> m_cpu;
	required_device<sgi_ctl1_device> m_ctl;
	required_device<eeprom_serial_93c56_16bit_device> m_eeprom;

	required_device<dp8572a_device> m_rtc;
	required_device<pit8254_device> m_pit;
	required_device<wd33c9x_base_device> m_scsi;
	required_device<am7990_device> m_enet;
	required_device_array<scn2681_device, 2> m_duart;
	required_device_array<rs232_port_device, 2> m_serial;
	required_device<sgi_gr1_device> m_gfx;
	required_device<software_list_device> m_softlist;

	enum leds : unsigned
	{
		LED_HBT = 0, // heartbeat (1Hz)
		LED_CPU = 1, // cpu activity
		LED_GFX = 2, // graphics
		LED_FPU = 3, // fpu present
		LED_CON = 4, // console
	};
	output_finder<5> m_leds;

	void common(machine_config &config);
	void map(address_map &map) ATTR_COLD;

	template <unsigned N> void lio_interrupt(int state) { lio_interrupt(N, state); }
	void lio_interrupt(unsigned number, int state);
	void scsi_drq(int state);

	u8 sysid_r();

	u32 buserror_r(offs_t offset)
	{
		m_cpu->berr_w(1);
		return 0;
	}
	void buserror_w(offs_t offset, u32 data, u32 mem_mask)
	{
		m_cpu->set_input_line(INPUT_LINE_IRQ5, 1);
	}

	enum sysid_mask : u8
	{
		SYSID_SERDATA = 0x01, // serial memory data output state
		SYSID_FPPRES  = 0x02, // floating point processor present (active low)
		SYSID_SERCLK  = 0x04, // serial memory clock
		SYSID_VMEFBT  = 0x04, // vme fast bus timeout
		SYSID_GDMAERR = 0x08, // error in graphics dma
		SYSID_GDMAEN  = 0x10, // graphics dma busy
		SYSID_GDMARDY = 0x20, // asserted at end of graphics dma
		SYSID_GDMARST = 0x40, // asserted in reset of graphics dma
		SYSID_VMERMW  = 0x80, // asserted in vme read-modify-write
	};

	enum lio_int_number : unsigned
	{
		LIO_D0     = 0, // duart 0 interrupt
		LIO_D1     = 1, // duart 1 interrupt
		LIO_VR     = 2, // vertical retrace interrupt
		LIO_CENTR  = 3, // parallel port interrupt
		LIO_SCSI   = 4, // scsi interrupt
		LIO_ENET   = 5, // ethernet interrupt
		LIO_GE     = 6, // ge interrupt
		LIO_FIFO   = 7, // fifo full interrupt
		LIO_AC     = 8, // vme ac fail interrupt
		LIO_VRSTAT = 9, // vert retrace status: no interrupt
	};

	enum cpuauxctl_mask : u8
	{
		CPUAUXCTRL_LED = 0x0f, // diagnostic leds (active low)
		CPUAUXCTRL_PE  = 0x10, // console led (active low)
		CPUAUXCTRL_CS  = 0x20, // eeprom chip select
		CPUAUXCTRL_CLK = 0x40, // serial clock
		CPUAUXCTRL_GR  = 0x80, // graphics reset (active low)
	};

	u8 m_sysid = 0;
	u8 m_vme_isr = 0;
	u8 m_vme_imr = 0;
	u8 m_cpuauxctl = 0;

	u16 m_lio_isr = 0;
	u8 m_lio_imr = 0;
	bool m_lio_int = false;
	int m_lio_fifo = 0;

	u16 m_dmalo = 0;
	u8 m_mapindex = 0;
	std::unique_ptr<u16 []> m_dmahi;

	u32 m_gdma_dabr = 0;   // descriptor array base
	u32 m_gdma_bufadr = 0; // buffer address
	u16 m_gdma_burst = 0;  // burst/delay
	u16 m_gdma_buflen = 0; // buffer length
};

void ip6_state::map(address_map &map)
{
	// silence local memory
	map(0x00000000, 0x0fffffff).noprw();

	// vme address space produces bus errors by default
	map(0x10000000, 0x1effffff).rw(FUNC(ip6_state::buserror_r), FUNC(ip6_state::buserror_w));

	// TODO: 1 32-bit 6U VME slot
	//map(0x10000000, 0x1bffffff); // vme a32 modifier 0x09 non-privileged
	//map(0x1c000000, 0x1cffffff); // vme a24 modifier 0x3d privileged
	//map(0x1d000000, 0x1d0fffff); // vme a16 modifier 0x2d privileged
	//map(0x1d100000, 0x1d1fffff); // vme a16 modifier 0x29 non-privileged
	//map(0x1df00000, 0x1dffffff).umask32(0x0000ff00); // VME_IACK: vme interrupt acknowledge
	//map(0x1e000000, 0x1effffff); // vme a24 modifier 0x39 non-privileged

	//map(0x1f000000, 0x1fbfffff); // local I/O (duarts, timers, etc.)
	map(0x1f000000, 0x1f007fff).m(m_gfx, FUNC(sgi_gr1_device::map)).mirror(0x8000);

	map(0x1f800000, 0x1f800003).rw(m_ctl, FUNC(sgi_ctl1_device::memcfg_r), FUNC(sgi_ctl1_device::memcfg_w)).umask32(0xff000000);
	map(0x1f800000, 0x1f800003).r(FUNC(ip6_state::sysid_r)).umask32(0x00ff0000);

	map(0x1f840000, 0x1f840003).lrw8(NAME([this]() { return m_vme_isr; }), NAME([this](u8 data) { m_vme_isr = data; })).umask32(0x000000ff);
	map(0x1f840008, 0x1f84000b).lrw8(NAME([this]() { return m_vme_imr; }), NAME([this](u8 data) { m_vme_imr = data; })).umask32(0x000000ff);

	map(0x1f880000, 0x1f880003).rw(m_ctl, FUNC(sgi_ctl1_device::cpucfg_r), FUNC(sgi_ctl1_device::cpucfg_w)).umask32(0xffff);

	//map(0x1f8c0000, 0x1f8c0003); // lca readback trigger (b)
	map(0x1f8e0000, 0x1f8e0003).lrw8(
		NAME([this]() { return m_cpuauxctl; }),
		[this](u8 data)
		{
			// cpu leds
			m_leds[LED_HBT] = BIT(data, 0);
			m_leds[LED_CPU] = BIT(data, 1);
			m_leds[LED_GFX] = BIT(data, 2);
			m_leds[LED_FPU] = BIT(data, 3);

			// console led
			m_leds[LED_CON] = BIT(data, 4);

			// serial eeprom chip select and clock out
			m_eeprom->cs_write(BIT(data, 5));
			m_eeprom->clk_write(BIT(data, 6));

			m_gfx->reset_w(BIT(data, 7));

			m_cpuauxctl = data;
		}, "cpuauxctl_w").umask32(0xff000000);

	map(0x1f900000, 0x1f900003).lrw16(
		NAME([this]() { return m_dmalo; }),
		[this](u16 data)
		{
			m_dmalo = data;
			m_mapindex = 0;
		}, "dmalo_w").umask32(0x0000ffff);

	map(0x1f910000, 0x1f910003).lrw8(
		NAME([this]() { return m_mapindex; }),
		NAME([this](u8 data) { m_mapindex = data; })).umask32(0x000000ff);

	/*
	 * DMA address mapping table is a pair of CY7C128-35PC 2048x8 SRAMs which
	 * read/write to data bus D27-12. A10 is tied high, giving 1024 entries.
	 */
	map(0x1f920000, 0x1f920fff).lrw16(
		NAME([this](offs_t offset) { return m_dmahi[offset]; }),
		NAME([this](offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_dmahi[offset]); })).umask32(0x0000ffff);

	// emulation can ignore dma flush
	map(0x1f940000, 0x1f940003).nopw();

	map(0x1f950000, 0x1f9501ff).rw(m_enet, FUNC(am7990_device::regs_r), FUNC(am7990_device::regs_w)).umask32(0xffff0000);
	map(0x1f960000, 0x1f960003).lr8([this]() { m_enet->reset_w(1); return 0; }, "etherrdy").umask32(0xff000000);
	map(0x1f960004, 0x1f960007).lr8([this]() { m_enet->reset_w(0); return 0; }, "etherrst").umask32(0xff000000);
	//map(0x1f960008, 0x1f96000b).rw().umask32(0xff000000); // etherwait: wait state control

	map(0x1f980000, 0x1f980003).lr16(NAME([this]() { return m_lio_isr; })).umask32(0x0000ffff);
	map(0x1f980008, 0x1f98000b).lrw8(
		NAME([this]() { return m_lio_imr; }),
		[this](u8 data)
		{
			m_lio_imr = data;

			// fifo interrupt status follows line state if not enabled
			if (!BIT(m_lio_imr, LIO_FIFO))
			{
				if (m_lio_fifo)
					m_lio_isr |= (1U << LIO_FIFO);
				else
					m_lio_isr &= ~(1U << LIO_FIFO);
			}

			// update interrupt line
			bool const lio_int = ~m_lio_isr & m_lio_imr;
			if (m_lio_int ^ lio_int)
			{
				m_lio_int = lio_int;
				m_cpu->set_input_line(INPUT_LINE_IRQ1, m_lio_int);
			}
		}, "lio_imr_w").umask32(0x000000ff);

	// 1 0 a7 a6 a5 a4 a3 a2 a1 a0 1 0  lance dmahi
	// 0 0 a7 a6 a5 a4 a3 a2 a1 a0 1 0  scsi dmahi
	// 0 1 a7 a6 a5 a4 a3 a2 a1 a0 1 0  printer/audio dmahi

	// TODO: printer/audio
	//map(0x1f970000, 0x1f970003).r().umask32(0x00ff0000); // pbstat - printer byte status
	//map(0x1f9c0000, 0x1f9c0003).rw().umask32(0x0000ffff); // prdmact - dma byte count
	//map(0x1f9c0004, 0x1f9c0007).w().umask32(0x00ff0000); // aogndac - audio output gain
	//map(0x1f9c0104, 0x1f9c0107).w().umask32(0x00ff0000);
	//map(0x1f9c0204, 0x1f9c0207).r().umask32(0x00ff0000); // prdmast - dma status
	//map(0x1f9c0304, 0x1f9c0307).r().umask32(0x00ff0000); // a/dreg - a/d i/o
	//map(0x1f9d0000, 0x1f9d0003).w().umask32(?); // pchrld - reload registers
	//map(0x1f9d0004, 0x1f9d0007).rw().umask32(0x0000ffff); // prdmalo - dma low addr reg
	//map(0x1f9e0000, 0x1f9e0003).rw().umask32(0x000000ff); // mapindex - printer map index (5-bit)
	//map(0x1f9e0004, 0x1f9e0007).w().umask32(0xff000000); // dmastop
	//map(0x1f9e0008, 0x1f9e000b).w().umask32(0x000000ff); // prswack - soft ack
	//map(0x1f9e000c, 0x1f9e000f).w().umask32(0xff000000); // dmastart
	//map(0x1f9f0000, 0x1f9f0003).r().umask32(0xff000000); // prdy - turn off reset
	//map(0x1f9f0004, 0x1f9f0007).r().umask32(0xff000000); // prst - turn on reset
	//map(0x1f9f0008, 0x1f9f000b).rw().umask32(0x0000ffff); // prdmacn - dma control
	//map(0x1f9f000c, 0x1f9f000f).rw().umask32(0xffffffff); // prdmadr - dma data reg

	// HACK: pass diagnostic iom3: ioc multiplexer registers test
	map(0x1f9c0000, 0x1f9c0003).ram().umask32(0x0000ffff);
	map(0x1f9d0004, 0x1f9d0007).ram().umask32(0x0000ffff);
	map(0x1f9e0000, 0x1f9e0003).ram().umask32(0x000000ff);
	map(0x1f9f000c, 0x1f9f000f).ram().umask32(0xffffffff);

	map(0x1fa00000, 0x1fa00003).lr8([this]() { m_cpu->set_input_line(INPUT_LINE_IRQ4, 0); return 0; }, "timer1_ack").umask32(0xff000000);
	map(0x1fa20000, 0x1fa20003).lr8([this]() { m_cpu->set_input_line(INPUT_LINE_IRQ2, 0); return 0; }, "timer0_ack").umask32(0xff000000);

	// FIXME: route irq5 through ctl1
	map(0x1fa40000, 0x1fa40003).lr32([this]() { m_cpu->set_input_line(INPUT_LINE_IRQ5, 0); return m_ctl->erradr_r(); }, "erradr");

	map(0x1fa40004, 0x1fa40007).rw(m_ctl, FUNC(sgi_ctl1_device::refadr_r), FUNC(sgi_ctl1_device::refadr_w));

	map(0x1fa40008, 0x1fa4000b).lrw32(NAME([this]() { return m_gdma_dabr; }), NAME([this](u32 data) { m_gdma_dabr = data; }));
	map(0x1fa4000c, 0x1fa4000f).lrw32(NAME([this]() { return m_gdma_bufadr; }), NAME([this](u32 data) { m_gdma_bufadr = data; }));
	map(0x1fa40010, 0x1fa40013).lrw16(NAME([this]() { return m_gdma_burst; }), NAME([this](u16 data) { m_gdma_burst = data; })).umask32(0xffff0000);
	map(0x1fa40010, 0x1fa40013).lrw16(NAME([this]() { return m_gdma_buflen; }), NAME([this](u16 data) { m_gdma_buflen = data; })).umask32(0x0000ffff);

	map(0x1fa60000, 0x1fa60003).lrw8([this]() { m_sysid |= SYSID_VMERMW; return 0; }, "vmermw_r", [this](u8 data) { m_sysid |= SYSID_VMERMW; }, "vmermw_w").umask32(0xff000000);
	//map(0x1fa60004, 0x1fa60007).rw("actpup").umask32(0xff000000); // turn on active bus pullup
	map(0x1fa60018, 0x1fa6001b).lrw8([this]() { m_sysid |= SYSID_VMEFBT; return 0; }, "vmefbon_r", [this](u8 data) { m_sysid |= SYSID_VMEFBT; }, "vmefbon_w").umask32(0xff000000);
	map(0x1fa6001c, 0x1fa6001f).lrw8([this]() { m_sysid &= ~SYSID_VMEFBT; return 0; }, "vmefbof_r", [this](u8 data) { m_sysid &= ~SYSID_VMEFBT; }, "vmefbof_w").umask32(0xff000000);
	map(0x1fa60020, 0x1fa60023).nopr(); // reload gfx dma burst/delay reg (FIXME: silenced)
	//map(0x1fa60024, 0x1fa60027).rw("enraso").umask32(0xff000000); // enable ctl ras decoder

	map(0x1fa80000, 0x1fa80003).lr8([this]() { m_scsi->reset_w(0); return 0; }, "scsirdy").umask32(0xff000000);
	map(0x1fa80004, 0x1fa80007).lr8([this]() { m_scsi->reset_w(1); return 0; }, "scsirst").umask32(0xff000000);
	// TODO: IOC2 configuration register, bus error on IOC1
	//map(0x1fa80008, 0x1fa8000b).lr8([]() { return 0x0f; }, "scsibstat").umask32(0x0000ff00);
	map(0x1fa80008, 0x1fa8000b).rw(FUNC(ip6_state::buserror_r), FUNC(ip6_state::buserror_w));

	map(0x1faa0000, 0x1faa0003).rw(m_ctl, FUNC(sgi_ctl1_device::clrerr_r), FUNC(sgi_ctl1_device::clrerr_w));
	map(0x1faa0004, 0x1faa0007).r(m_ctl, FUNC(sgi_ctl1_device::parerr_r)).umask32(0x00ff0000);

	map(0x1fac0000, 0x1fac0003).lrw8([this]() { lio_interrupt<LIO_VR>(1); return 0; }, "vrrst_r", [this](u8 data) { lio_interrupt<LIO_VR>(1); }, "vrrst_w").umask32(0xff000000);

	map(0x1fb00000, 0x1fb00003).rw(m_scsi, FUNC(wd33c93_device::indir_addr_r), FUNC(wd33c93_device::indir_addr_w)).umask32(0x00ff0000);
	map(0x1fb00100, 0x1fb00103).rw(m_scsi, FUNC(wd33c93_device::indir_reg_r), FUNC(wd33c93_device::indir_reg_w)).umask32(0x00ff0000);

	map(0x1fb40000, 0x1fb4000f).rw(m_pit, FUNC(pit8254_device::read), FUNC(pit8254_device::write)).umask32(0xff000000);

	map(0x1fb80000, 0x1fb800ff).lrw8(
		NAME([this](offs_t offset) { return m_duart[BIT(offset, 0)]->read(offset >> 2); }),
		NAME([this](offs_t offset, u8 data) { m_duart[BIT(offset, 0)]->write(offset >> 2, data); })).umask32(0xff000000);

	map(0x1fbc0000, 0x1fbc007f).rw(m_rtc, FUNC(dp8572a_device::read), FUNC(dp8572a_device::write)).umask32(0xff000000);

	map(0x1fc00000, 0x1fc3ffff).rom().region("boot", 0);

	// unused memory address space produces bus errors
	map(0x40000000, 0xffffffff).rw(FUNC(ip6_state::buserror_r), FUNC(ip6_state::buserror_w));
}

static void scsi_devices(device_slot_interface &device)
{
	device.option_add("cdrom", NSCSI_CDROM_SGI).machine_config(
		[](device_t *device)
		{
			downcast<nscsi_cdrom_device &>(*device).set_block_size(512);
		});
	device.option_add("harddisk", NSCSI_HARDDISK);
}

void ip6_state::pi4d20(machine_config &config)
{
	R3000(config, m_cpu, 25_MHz_XTAL / 2, 16384, 8192);
	m_cpu->set_fpu(mips1_device_base::MIPS_R3010);

	common(config);
}

void ip6_state::pi4d25(machine_config &config)
{
	R3000(config, m_cpu, 20_MHz_XTAL, 65536, 32768);
	m_cpu->set_fpu(mips1_device_base::MIPS_R3010);

	common(config);
}

static DEVICE_INPUT_DEFAULTS_START(ip6_ctl1)
	DEVICE_INPUT_DEFAULTS("VALID", 0x000f, 0x000f)
DEVICE_INPUT_DEFAULTS_END

void ip6_state::common(machine_config &config)
{
	m_cpu->set_addrmap(AS_PROGRAM, &ip6_state::map);
	m_cpu->in_brcond<0>().set([]() { return 1; }); // writeback complete

	SGI_CTL1(config, m_ctl);
	m_ctl->set_bus(m_cpu, AS_PROGRAM);
	m_ctl->set_input_default(DEVICE_INPUT_DEFAULTS_NAME(ip6_ctl1));
	m_ctl->serdout().set(m_eeprom, FUNC(eeprom_serial_93c56_16bit_device::di_write));
	m_ctl->cpuberr().set(m_cpu, FUNC(mips1core_device_base::berr_w));

	EEPROM_93C56_16BIT(config, m_eeprom);

	DP8572A(config, m_rtc, 32.768_kHz_XTAL);

	PIT8254(config, m_pit);
	m_pit->set_clk<2>(3.6864_MHz_XTAL);
	m_pit->out_handler<0>().set([this](int state) { if (state) m_cpu->set_input_line(INPUT_LINE_IRQ2, 1); });
	m_pit->out_handler<1>().set([this](int state) { if (state) m_cpu->set_input_line(INPUT_LINE_IRQ4, 1); });
	m_pit->out_handler<2>().set(m_pit, FUNC(pit8254_device::write_clk0));
	m_pit->out_handler<2>().append(m_pit, FUNC(pit8254_device::write_clk1));

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0").option_set("wd33c93", WD33C93).machine_config(
		[this](device_t *device)
		{
			wd33c9x_base_device &wd33c93(downcast<wd33c9x_base_device &>(*device));

			wd33c93.set_clock(10000000);
			wd33c93.irq_cb().set(*this, FUNC(ip6_state::lio_interrupt<LIO_SCSI>)).invert();
			wd33c93.drq_cb().set(*this, FUNC(ip6_state::scsi_drq));
		});
	NSCSI_CONNECTOR(config, "scsi:1", scsi_devices, "harddisk", false);
	NSCSI_CONNECTOR(config, "scsi:2", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:3", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:4", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:5", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:6", scsi_devices, "cdrom", false);
	NSCSI_CONNECTOR(config, "scsi:7", scsi_devices, nullptr, false);

	AM7990(config, m_enet);
	m_enet->intr_out().set(FUNC(ip6_state::lio_interrupt<LIO_ENET>));
	m_enet->dma_in().set(
		[this](offs_t offset)
		{
			unsigned const page = 0x200 + ((offset >> 12) & 0xff);
			u32 const address = (u32(m_dmahi[page]) << 12) | (offset & 0xfff);

			return m_cpu->space(0).read_word(address);
		});
	m_enet->dma_out().set(
		[this](offs_t offset, u16 data, u16 mem_mask)
		{
			unsigned const page = 0x200 + ((offset >> 12) & 0xff);
			u32 const address = (u32(m_dmahi[page]) << 12) | (offset & 0xfff);

			m_cpu->space(0).write_word(address, data, mem_mask);
		});

	// duart 0 (keyboard/mouse)
	SCN2681(config, m_duart[0], 3.6864_MHz_XTAL); // SCN2681AC1N24
	sgi_kbd_port_device &keyboard_port(SGI_KBD_PORT(config, "keyboard_port", default_sgi_kbd_devices, "keyboard"));
	rs232_port_device &mouse_port(RS232_PORT(config, "mouse_port",
		[](device_slot_interface &device)
		{
			device.option_add("mouse", SGI_HLE_SERIAL_MOUSE);
		},
		"mouse"));

	// duart 0 outputs
	m_duart[0]->irq_cb().set(FUNC(ip6_state::lio_interrupt<LIO_D0>)).invert();
	m_duart[0]->a_tx_cb().set(keyboard_port, FUNC(sgi_kbd_port_device::write_txd));
	m_duart[0]->b_tx_cb().set(mouse_port, FUNC(rs232_port_device::write_txd));

	// duart 0 inputs
	keyboard_port.rxd_handler().set(m_duart[0], FUNC(scn2681_device::rx_a_w));
	mouse_port.rxd_handler().set(m_duart[0], FUNC(scn2681_device::rx_b_w));

	// duart 1 (serial ports)
	SCN2681(config, m_duart[1], 3.6864_MHz_XTAL); // SCN2681AC1N40
	RS232_PORT(config, m_serial[0], default_rs232_devices, nullptr);
	RS232_PORT(config, m_serial[1], default_rs232_devices, nullptr);

	// duart 1 outputs
	m_duart[1]->irq_cb().set(FUNC(ip6_state::lio_interrupt<LIO_D1>)).invert();
	m_duart[1]->a_tx_cb().set(m_serial[0], FUNC(rs232_port_device::write_txd));
	m_duart[1]->b_tx_cb().set(m_serial[1], FUNC(rs232_port_device::write_txd));
	m_duart[1]->outport_cb().set(
		[this](u8 data)
		{
			m_serial[0]->write_rts(BIT(data, 0));
			m_serial[1]->write_rts(BIT(data, 1));
			m_duart[1]->ip5_w(BIT(data, 3));
			m_duart[1]->ip6_w(BIT(data, 3));
			m_serial[0]->write_dtr(BIT(data, 4));
			m_serial[1]->write_dtr(BIT(data, 5));
		});

	// duart 1 inputs
	m_serial[0]->rxd_handler().set(m_duart[1], FUNC(scn2681_device::rx_a_w));
	m_serial[0]->cts_handler().set(m_duart[1], FUNC(scn2681_device::ip0_w));
	m_serial[0]->dcd_handler().set(m_duart[1], FUNC(scn2681_device::ip3_w));

	m_serial[1]->rxd_handler().set(m_duart[1], FUNC(scn2681_device::rx_b_w));
	m_serial[1]->cts_handler().set(m_duart[1], FUNC(scn2681_device::ip1_w));
	m_serial[1]->dcd_handler().set(m_duart[1], FUNC(scn2681_device::ip2_w));

	// graphics
	SGI_GR1(config, m_gfx);
	m_gfx->out_vblank().set(
		[this](int state)
		{
			if (state)
			{
				m_lio_isr &= ~(1U << LIO_VRSTAT);
				lio_interrupt<LIO_VR>(0);
			}
			else
				m_lio_isr |= (1U << LIO_VRSTAT);
		});
	m_gfx->out_int().set(*this, FUNC(ip6_state::lio_interrupt<LIO_GE>)).invert();
	m_gfx->out_int_fifo().set(*this, FUNC(ip6_state::lio_interrupt<LIO_FIFO>)).invert();

	// TODO: vme slot, cpu interrupt 0

	SOFTWARE_LIST(config, m_softlist).set_original("sgi_mips");

	config.set_default_layout(layout_4dpi);
}

void ip6_state::machine_start()
{
	m_sysid = 0;

	m_lio_isr = 0x3ff;
	m_lio_imr = 0;
	m_lio_int = false;

	m_dmahi = make_unique_clear<u16 []>(2048);

	m_leds.resolve();
}

void ip6_state::lio_interrupt(unsigned number, int state)
{
	u16 const mask = 1 << number;

	if (number == LIO_FIFO)
	{
		m_lio_fifo = state;

		// special handling for enabled fifo interrupt
		if ((m_lio_imr & mask) && !(m_lio_isr & mask))
			return;
	}

	// record interrupt state
	if (state)
		m_lio_isr |= mask;
	else
		m_lio_isr &= ~mask;

	// update interrupt line
	bool const lio_int = ~m_lio_isr & m_lio_imr;
	if (m_lio_int ^ lio_int)
	{
		m_lio_int = lio_int;
		m_cpu->set_input_line(INPUT_LINE_IRQ1, m_lio_int);
	}
}

void ip6_state::scsi_drq(int state)
{
	if (state)
	{
		u32 const addr = (u32(m_dmahi[m_mapindex]) << 12) | (m_dmalo & 0x0fff);

		if (m_dmalo & 0x8000)
			m_cpu->space(0).write_byte(addr, m_scsi->dma_r());
		else
			m_scsi->dma_w(m_cpu->space(0).read_byte(addr));

		m_dmalo = (m_dmalo + 1) & 0x8fff;

		if (!(m_dmalo & 0xfff))
			m_mapindex++;
	}
}

u8 ip6_state::sysid_r()
{
	u8 data = m_sysid;

	if (m_eeprom->do_read())
		data |= SYSID_SERDATA;

	return data;
}

ROM_START(pi4d20)
	ROM_REGION32_BE(0x40000, "boot", 0)
	// 3.2e firmware has been found in both 4D/20 and 4D/25 hardware
	ROM_SYSTEM_BIOS(0, "3.2e", "Version 3.2 Rev E, Fri Jul 14 14:37:38 PDT 1989 SGI")
	ROMX_LOAD("070_8000_007_boot_0.h1c5", 0x000000, 0x010000, CRC(e448b865) SHA1(f0276b76360ea0b3250dbdaa7a1e57ea8f6144d6), ROM_BIOS(0) | ROM_SKIP(3))
	ROMX_LOAD("070_8000_007_boot_1.h1d2", 0x000001, 0x010000, CRC(59fda717) SHA1(ef3ccb1f8a815e7b13c79deeea0d73006deed09f), ROM_BIOS(0) | ROM_SKIP(3))
	ROMX_LOAD("070_8000_007_boot_2.h1d9", 0x000002, 0x010000, CRC(569146ad) SHA1(5442a13ed93afdaa55c1951b97e335cf60dde834), ROM_BIOS(0) | ROM_SKIP(3))
	ROMX_LOAD("070_8000_007_boot_3.h1e6", 0x000003, 0x010000, CRC(682977c3) SHA1(d9bcf7cdc5caef4221929fe26eccf34253fa7f29), ROM_BIOS(0) | ROM_SKIP(3))

	ROM_SYSTEM_BIOS(1, "3.1c", "Version 4D1-3.1 Rev C, Tue Jan 10 15:11:42 PST 1989 SGI")
	ROMX_LOAD("070_8000_005_boot_0.h1c5", 0x000000, 0x010000, CRC(c7a182de) SHA1(56038f54b5a3254960ad7c8232f1a7cf058b9ead), ROM_BIOS(1) | ROM_SKIP(3))
	ROMX_LOAD("070_8000_005_boot_1.h1d2", 0x000001, 0x010000, CRC(4b1395f5) SHA1(926f3172b79ebaf7040ff04b0cfdc3d48d03293c), ROM_BIOS(1) | ROM_SKIP(3))
	ROMX_LOAD("070_8000_005_boot_2.h1d9", 0x000002, 0x010000, CRC(e0a55120) SHA1(0b675489ea94bf85a5a0e5f0ebf0c0b7ff5fc389), ROM_BIOS(1) | ROM_SKIP(3))
	ROMX_LOAD("070_8000_005_boot_3.h1e6", 0x000003, 0x010000, CRC(11536526) SHA1(5149f453347ae566e9fee4447615dff88c7f6a37), ROM_BIOS(1) | ROM_SKIP(3))

	ROM_SYSTEM_BIOS(2, "3.1a", "Version 4D1-3.1 Rev A, Tue Aug  9 17:50:39 PDT 1988 SGI")
	ROMX_LOAD("070_8000_003_boot_0.h1c5", 0x000000, 0x010000, CRC(32cdcdc5) SHA1(1568ae3877193d3c93bdcccd755736b6cd82cbf3), ROM_BIOS(2) | ROM_SKIP(3))
	ROMX_LOAD("070_8000_003_boot_1.h1d2", 0x000001, 0x010000, CRC(4b3a02ee) SHA1(248e5cea8d3218686a044e5b013a1213441a5332), ROM_BIOS(2) | ROM_SKIP(3))
	ROMX_LOAD("070_8000_003_boot_2.h1d9", 0x000002, 0x010000, CRC(3d80d19c) SHA1(42fb95f4d76ede073c5d9bbc2f144b4f2fbf407f), ROM_BIOS(2) | ROM_SKIP(3))
	ROMX_LOAD("070_8000_003_boot_3.h1e6", 0x000003, 0x010000, CRC(48b9322c) SHA1(600554a0ccc4bab4881a96a9886eea40cd04c8e4), ROM_BIOS(2) | ROM_SKIP(3))
ROM_END

ROM_START(pi4d25)
	ROM_REGION32_BE(0x40000, "boot", 0)
	ROM_SYSTEM_BIOS(0, "3.2e", "Version 3.2 Rev E, Fri Jul 14 14:37:38 PDT 1989 SGI")
	ROMX_LOAD("070_8000_007_boot_0.h1c5", 0x000000, 0x010000, CRC(e448b865) SHA1(f0276b76360ea0b3250dbdaa7a1e57ea8f6144d6), ROM_BIOS(0) | ROM_SKIP(3))
	ROMX_LOAD("070_8000_007_boot_1.h1d2", 0x000001, 0x010000, CRC(59fda717) SHA1(ef3ccb1f8a815e7b13c79deeea0d73006deed09f), ROM_BIOS(0) | ROM_SKIP(3))
	ROMX_LOAD("070_8000_007_boot_2.h1d9", 0x000002, 0x010000, CRC(569146ad) SHA1(5442a13ed93afdaa55c1951b97e335cf60dde834), ROM_BIOS(0) | ROM_SKIP(3))
	ROMX_LOAD("070_8000_007_boot_3.h1e6", 0x000003, 0x010000, CRC(682977c3) SHA1(d9bcf7cdc5caef4221929fe26eccf34253fa7f29), ROM_BIOS(0) | ROM_SKIP(3))
ROM_END

} // anonymous namespace

//   YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY             FULLNAME               FLAGS
COMP(1988, pi4d20, 0,      0,      pi4d20,  0,     ip6_state, empty_init, "Silicon Graphics", "Personal IRIS 4D/20", MACHINE_NO_SOUND | MACHINE_IMPERFECT_GRAPHICS)
COMP(1989, pi4d25, 0,      0,      pi4d25,  0,     ip6_state, empty_init, "Silicon Graphics", "Personal IRIS 4D/25", MACHINE_NO_SOUND | MACHINE_IMPERFECT_GRAPHICS)



ipc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

        Intel iPB and iPC

        17/12/2009 Skeleton driver.

        22/04/2011 Connected to a terminal, it responds. Modernised.

        --> When started, you must press Space, then it will start to work.

        Monitor commands:
        A
        Dn n - dump memory
        E
        Fn n n - fill memory
        G
        Hn n - hex arithmetic
        Mn n n - move (copy) memory block
        N
        Q
        R
        Sn - modify a byte of memory
        W - display memory in Intel? format
        X - show and modify registers


        Preliminary Memory Map
        E800-F7FF BIOS ROM area
        F800-FFFF Monitor ROM (or other user interface)

        I/O F4/F5 main console input and output
        I/O F6/F7 alternate console input

        ToDo:
        - Everything!
        - iPC - Find missing rom F800-FFFF

****************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8251.h"
#include "machine/pit8253.h"
#include "bus/rs232/rs232.h"


namespace {

class ipc_state : public driver_device
{
public:
	ipc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void ipc(machine_config &config);

private:
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	virtual void machine_reset() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
};


void ipc_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xdfff).ram();
	map(0xe800, 0xffff).rom().region("roms", 0);
}

void ipc_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0xf0, 0xf3).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xf4, 0xf5).rw("uart1", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xf6, 0xf7).rw("uart2", FUNC(i8251_device::read), FUNC(i8251_device::write));
}

/* Input ports */
static INPUT_PORTS_START( ipc )
INPUT_PORTS_END


void ipc_state::machine_reset()
{
	m_maincpu->set_state_int(i8085a_cpu_device::I8085_PC, 0xE800);
}


void ipc_state::ipc(machine_config &config)
{
	/* basic machine hardware */
	I8085A(config, m_maincpu, XTAL(19'660'800) / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &ipc_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &ipc_state::io_map);

	pit8253_device &pit(PIT8253(config, "pit", 0));
	pit.set_clk<0>(XTAL(19'660'800) / 16);
	pit.set_clk<1>(XTAL(19'660'800) / 16);
	pit.set_clk<2>(XTAL(19'660'800) / 16);
	pit.out_handler<0>().set("uart1", FUNC(i8251_device::write_txc));
	pit.out_handler<0>().append("uart1", FUNC(i8251_device::write_rxc));
	pit.out_handler<1>().set("uart2", FUNC(i8251_device::write_txc));
	pit.out_handler<1>().append("uart2", FUNC(i8251_device::write_rxc));

	i8251_device &uart1(I8251(config, "uart1", 0)); // 8 data bits, no parity, 1 stop bit, 9600 baud
	uart1.txd_handler().set("rs232a", FUNC(rs232_port_device::write_txd));
	uart1.dtr_handler().set("rs232a", FUNC(rs232_port_device::write_dtr));
	uart1.rts_handler().set("rs232a", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, "terminal"));
	rs232a.rxd_handler().set("uart1", FUNC(i8251_device::write_rxd));
	rs232a.dsr_handler().set("uart1", FUNC(i8251_device::write_dsr));
	rs232a.cts_handler().set("uart1", FUNC(i8251_device::write_cts));

	i8251_device &uart2(I8251(config, "uart2", 0)); // 8 data bits, no parity, 2 stop bits, 2400 baud
	uart2.txd_handler().set("rs232b", FUNC(rs232_port_device::write_txd));
	uart2.dtr_handler().set("rs232b", FUNC(rs232_port_device::write_dtr));
	uart2.rts_handler().set("rs232b", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set("uart2", FUNC(i8251_device::write_rxd));
	rs232b.dsr_handler().set("uart2", FUNC(i8251_device::write_dsr));
	rs232b.cts_handler().set("uart2", FUNC(i8251_device::write_cts));
}

/* ROM definition */
ROM_START( ipb )
	ROM_REGION( 0x1800, "roms", 0 )
	ROM_LOAD( "ipb_e8_v1.3.bin", 0x0000, 0x0800, CRC(fc9d4703) SHA1(2ce078e1bcd8b24217830c54bcf04c5d146d1b76) )
	ROM_LOAD( "ipb_f8_v1.3.bin", 0x1000, 0x0800, CRC(966ba421) SHA1(d6a904c7d992a05ed0f451d7d34c1fc8de9547ee) )
ROM_END

ROM_START( ipc )
	ROM_REGION( 0x1800, "roms", ROMREGION_ERASEFF )
	ROM_LOAD( "ipc_v1.3_104584-001.u82", 0x0000, 0x1000, CRC(0889394f) SHA1(b7525baf1884a7d67402dea4b5566016a9861ef2) )
	ROM_LOAD( "ipc_f8_v1.3.bin", 0x1000, 0x0800, NO_DUMP )   // rom name unknown
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY  FULLNAME  FLAGS */
COMP( 19??, ipb,  0,      0,      ipc,     ipc,   ipc_state, empty_init, "Intel", "iPB",    MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 19??, ipc,  ipb,    0,      ipc,     ipc,   ipc_state, empty_init, "Intel", "iPC",    MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



ipds.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

        Intel iPDS

        17/12/2009 Skeleton driver.

****************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "video/i8275.h"
#include "machine/keyboard.h"
#include "emupal.h"
#include "screen.h"


namespace {

class ipds_state : public driver_device
{
public:
	ipds_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_crtc(*this, "i8275"),
		m_palette(*this, "palette")
	{
	}

	void ipds(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	required_device<i8275_device> m_crtc;
	required_device<palette_device> m_palette;
	uint8_t ipds_b0_r();
	uint8_t ipds_b1_r();
	uint8_t ipds_c0_r();
	void ipds_b1_w(uint8_t data);
	void kbd_put(u8 data);
	I8275_DRAW_CHARACTER_MEMBER( crtc_display_pixels );
	uint8_t m_term_data = 0U;
	virtual void machine_reset() override ATTR_COLD;
	void ipds_io(address_map &map) ATTR_COLD;
	void ipds_mem(address_map &map) ATTR_COLD;
};

uint8_t ipds_state::ipds_b0_r()
{
	if (m_term_data)
		return 0xc0;
	else
		return 0x80;
}

uint8_t ipds_state::ipds_b1_r()
{
	uint8_t ret = m_term_data;
	m_term_data = 0;
	return ret;
}

uint8_t ipds_state::ipds_c0_r() { return 0x55; }

void ipds_state::ipds_b1_w(uint8_t data)
{
}

void ipds_state::ipds_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).rom();
	map(0x0800, 0xffff).ram();
}

void ipds_state::ipds_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0xb0, 0xb0).r(FUNC(ipds_state::ipds_b0_r));
	map(0xb1, 0xb1).rw(FUNC(ipds_state::ipds_b1_r), FUNC(ipds_state::ipds_b1_w));
	map(0xc0, 0xc0).r(FUNC(ipds_state::ipds_c0_r));
}

/* Input ports */
static INPUT_PORTS_START( ipds )
INPUT_PORTS_END

void ipds_state::machine_reset()
{
}

I8275_DRAW_CHARACTER_MEMBER( ipds_state::crtc_display_pixels )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	uint8_t *charmap = memregion("chargen")->base();
	uint8_t pixels = charmap[(linecount & 7) + (charcode << 3)] ^ 0xff;

	using namespace i8275_attributes;

	if (BIT(attrcode, VSP))
		pixels = 0;

	if (BIT(attrcode, LTEN))
		pixels = 0xff;

	if (BIT(attrcode, RVV))
		pixels ^= 0xff;

	bool hlgt = BIT(attrcode, HLGT);
	for(int i=0;i<6;i++)
		bitmap.pix(y, x + i) = palette[(pixels >> (5-i)) & 1 ? (hlgt ? 2 : 1) : 0];
}

/* F4 Character Displayer */
static const gfx_layout ipds_charlayout =
{
	8, 11,                  /* 8 x 11 characters */
	128,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_ipds )
	GFXDECODE_ENTRY( "chargen", 0x0000, ipds_charlayout, 0, 1 )
GFXDECODE_END


void ipds_state::kbd_put(u8 data)
{
	m_term_data = data;
}

void ipds_state::ipds(machine_config &config)
{
	/* basic machine hardware */
	I8085A(config, m_maincpu, XTAL(19'660'800) / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &ipds_state::ipds_mem);
	m_maincpu->set_addrmap(AS_IO, &ipds_state::ipds_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_screen_update("i8275", FUNC(i8275_device::screen_update));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_ipds);
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	I8275(config, m_crtc, XTAL(19'660'800) / 4);
	m_crtc->set_character_width(6);
	m_crtc->set_display_callback(FUNC(ipds_state::crtc_display_pixels));

	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(ipds_state::kbd_put));
}

/* ROM definition */
ROM_START( ipds )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "164180.bin", 0x0000, 0x0800, CRC(10ba23ce) SHA1(67a9d03a08cdd7c70a867fb7e069703c7a0b9094) )

	ROM_REGION( 0x10000, "slavecpu", ROMREGION_ERASEFF )
	ROM_LOAD( "164196.bin", 0x0000, 0x1000, CRC(d3e18bc6) SHA1(01bc233be154bdfea9e8015839c5885cdaa08f11) )

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD( "163642.bin", 0x0000, 0x0800, CRC(3d81b2d1) SHA1(262a42920f15f1156ad0717c876cc0b2ed947ec5) )

	ROM_REGION(0x1000, "user1", 0)
	ROM_LOAD( "162806-001.u24", 0x0000, 0x0800, CRC(e6ba41ca) SHA1(a2d3438b728670b75c359ff49146ef57c08bf0f1) )
	ROM_LOAD( "163775-001.u8",  0x0800, 0x0800, CRC(0442163e) SHA1(737732adcfe22823db37112e28758464f5d9af7b) )
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY  FULLNAME  FLAGS */
COMP( 1982, ipds, 0,      0,      ipds,    ipds,  ipds_state, empty_init, "Intel", "iPDS",   MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



iphone2g.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Melissa Goad
/***************************************************************************

  iphone2g.cpp

  Driver file to handle emulation of the original iPhone

***************************************************************************/

#include "emu.h"
#include "cpu/arm7/arm7.h"
#include "machine/vic_pl192.h"
#include "screen.h"

class iphone2g_spi_device : public device_t, public device_memory_interface
{
public:
	iphone2g_spi_device(const machine_config &mconfig, const char* tag, device_t *owner, uint32_t clock = 0);

	auto out_irq_cb() { return m_out_irq_func.bind(); }

	void map(address_map &map) ATTR_COLD;

protected:
	iphone2g_spi_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);

	// device-level overrides
	virtual void device_start() override ATTR_COLD;
	virtual void device_reset() override ATTR_COLD;

	virtual space_config_vector memory_space_config() const override;

	TIMER_CALLBACK_MEMBER(send_irq);

private:
	address_space_config m_mmio_config;

	devcb_write_line m_out_irq_func;

	emu_timer *m_irq_timer;
	u8 m_cmd = 0;
	u8 m_tx_data = 0;
	u32 m_ctrl = 0;
	u16 m_status = 0;
};

DECLARE_DEVICE_TYPE(IPHONE2G_SPI, iphone2g_spi_device)

TIMER_CALLBACK_MEMBER(iphone2g_spi_device::send_irq)
{
	m_out_irq_func(1);
}

void iphone2g_spi_device::map(address_map &map)
{
	map(0x00,0x03).lrw32(NAME([this](offs_t offset){ return m_ctrl; }), NAME([this](offs_t offset, u32 data){
		if(data & 1)
		{
			m_status |= 0xfff2;
			m_cmd = m_tx_data;
			m_irq_timer->adjust(attotime::from_hz(1'000));
		}
		m_ctrl = data;
	}));
	map(0x08, 0x09).lr16([this](offs_t offset){ return m_status; }, "status").umask32(0x0000ffff);
	map(0x10, 0x10).lrw8(NAME([this](offs_t offset){ return m_tx_data; }), NAME([this](offs_t offset, u8 data){ m_tx_data = data; })).umask32(0x000000ff);
	map(0x20, 0x20).lr8([this](offs_t offset){
		// FIXME: make this less hacky
		switch(m_cmd)
		{
			case 0x95: return 0x01;
			case 0xda: return 0x71;
			case 0xdb: return 0xc2;
			case 0xdc: return 0x00;
		}
		return 0;
	}, "rx_data");
}

device_memory_interface::space_config_vector iphone2g_spi_device::memory_space_config() const
{
	return space_config_vector{
		std::make_pair(0, &m_mmio_config)
	};
}

void iphone2g_spi_device::device_start()
{
	save_item(NAME(m_cmd));
	save_item(NAME(m_ctrl));
	save_item(NAME(m_tx_data));
	save_item(NAME(m_status));

	m_irq_timer = timer_alloc(FUNC(iphone2g_spi_device::send_irq), this);
}

void iphone2g_spi_device::device_reset()
{
	m_cmd = m_ctrl= m_status = m_tx_data = 0;
}

DEFINE_DEVICE_TYPE(IPHONE2G_SPI, iphone2g_spi_device, "iphone2g_spi", "iPhone 2G SPI controller")

iphone2g_spi_device::iphone2g_spi_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, type, tag, owner, clock)
	, device_memory_interface(mconfig, *this)
	, m_mmio_config("mmio", ENDIANNESS_LITTLE, 32, 32, 0)
	, m_out_irq_func(*this)
{
}

iphone2g_spi_device::iphone2g_spi_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: iphone2g_spi_device(mconfig, IPHONE2G_SPI, tag, owner, clock)
{
}

class iphone2g_timer_device : public device_t, public device_memory_interface
{
public:
	iphone2g_timer_device(const machine_config &mconfig, const char* tag, device_t *owner, uint32_t clock = 0);

	auto out_irq_cb() { return m_out_irq_func.bind(); }

	void map(address_map &map) ATTR_COLD;
	void timer_map(address_map &map) ATTR_COLD;

protected:
	iphone2g_timer_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);

	// device-level overrides
	virtual void device_start() override ATTR_COLD;
	virtual void device_reset() override ATTR_COLD;

	virtual space_config_vector memory_space_config() const override;

	TIMER_CALLBACK_MEMBER(send_irq);

private:
	address_space_config m_mmio_config;

	devcb_write_line m_out_irq_func;

	emu_timer *m_irq_timer;

	struct timer
	{
		u16 config = 0;
		u8 state = 0;
		u32 count_buffer[2]{}, count = 0;
	} timers[7];

	//u64 m_ticks = 0;   not used
};

DECLARE_DEVICE_TYPE(IPHONE2G_TIMER, iphone2g_timer_device)

TIMER_CALLBACK_MEMBER(iphone2g_timer_device::send_irq)
{
	m_out_irq_func(1);
}

void iphone2g_timer_device::map(address_map &map)
{
}

device_memory_interface::space_config_vector iphone2g_timer_device::memory_space_config() const
{
	return space_config_vector{
		std::make_pair(0, &m_mmio_config)
	};
}

void iphone2g_timer_device::device_start()
{
	m_irq_timer = timer_alloc(FUNC(iphone2g_timer_device::send_irq), this);
}

void iphone2g_timer_device::device_reset()
{
}

DEFINE_DEVICE_TYPE(IPHONE2G_TIMER, iphone2g_timer_device, "iphone2g_timer", "iPhone 2G timers")

iphone2g_timer_device::iphone2g_timer_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, type, tag, owner, clock)
	, device_memory_interface(mconfig, *this)
	, m_mmio_config("mmio", ENDIANNESS_LITTLE, 32, 32, 0)
	, m_out_irq_func(*this)
{
}

iphone2g_timer_device::iphone2g_timer_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: iphone2g_timer_device(mconfig, IPHONE2G_TIMER, tag, owner, clock)
{
}


namespace {

class iphone2g_state : public driver_device
{
public:
	iphone2g_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_vic0(*this, "vic0"),
		m_vic1(*this, "vic1"),
		m_spi(*this, {"spi0", "spi1", "spi2"}),
		m_timers(*this, "timers"),
		m_ram(*this, "ram"),
		m_bios(*this, "bios"),
		m_screen(*this, "screen")
	{ }

	void iphone2g(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint32_t clock1_r(offs_t offset);

private:
	required_device<cpu_device> m_maincpu;
	required_device<vic_pl192_device> m_vic0;
	required_device<vic_pl192_device> m_vic1;
	required_device_array<iphone2g_spi_device, 3> m_spi;
	required_device<iphone2g_timer_device> m_timers;
	optional_shared_ptr<uint32_t> m_ram;
	required_region_ptr<uint32_t> m_bios;
	required_device<screen_device> m_screen;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void mem_map(address_map &map) ATTR_COLD;
};

uint32_t iphone2g_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

uint32_t iphone2g_state::clock1_r(offs_t offset)
{
	uint32_t ret = 0;
	switch(offset)
	{
		case 0x40/4: ret = 1; break; //boot rom needs this to not infinite loop at startup.
	}

	logerror("%s: Clock1 read: offset %08x data %08x\n", machine().describe_context(), offset << 2, ret);

	return ret;
}

void iphone2g_state::mem_map(address_map &map)
{
	map(0x00000000, 0x0000ffff).mirror(0x20000000).rom().region("bios", 0);            /* BIOS */
	map(0x22000000, 0x224fffff).ram();                                                 /* SRAM */
	map(0x38e00000, 0x38e00fff).m(m_vic0, FUNC(vic_pl192_device::map));
	map(0x38e01000, 0x38e01fff).m(m_vic1, FUNC(vic_pl192_device::map));
	map(0x3c300000, 0x3c3000ff).m(m_spi[0], FUNC(iphone2g_spi_device::map));
	map(0x3c500000, 0x3c500fff).r(FUNC(iphone2g_state::clock1_r)).nopw();
	map(0x3ce00000, 0x3ce000ff).m(m_spi[1], FUNC(iphone2g_spi_device::map));
	map(0x3d200000, 0x3d2000ff).m(m_spi[2], FUNC(iphone2g_spi_device::map));
}

void iphone2g_state::machine_start()
{
	//std::copy_n(m_bios.target(), m_bios.length(), m_ram.target());
}

void iphone2g_state::machine_reset()
{
}

void iphone2g_state::iphone2g(machine_config &config)
{
	/* Basic machine hardware */
	ARM1176JZF_S(config, m_maincpu, XTAL(12'000'000) * 103 / 3); //412 MHz, downclocked from 600 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &iphone2g_state::mem_map);

	PL192_VIC(config, m_vic0);
	m_vic0->out_irq_cb().set_inputline("maincpu", arm7_cpu_device::ARM7_IRQ_LINE);
	m_vic0->out_fiq_cb().set_inputline("maincpu", arm7_cpu_device::ARM7_FIRQ_LINE);

	IPHONE2G_SPI(config, m_spi[0], XTAL(12'000'000));
	m_spi[0]->out_irq_cb().set(m_vic0, FUNC(vic_pl192_device::irq_w<0x09>));

	IPHONE2G_SPI(config, m_spi[1], XTAL(12'000'000));
	m_spi[1]->out_irq_cb().set(m_vic0, FUNC(vic_pl192_device::irq_w<0x0a>));

	IPHONE2G_SPI(config, m_spi[2], XTAL(12'000'000));
	m_spi[2]->out_irq_cb().set(m_vic0, FUNC(vic_pl192_device::irq_w<0x0b>));

	IPHONE2G_TIMER(config, m_timers, XTAL(12'000'000));
	m_timers->out_irq_cb().set(m_vic0, FUNC(vic_pl192_device::irq_w<0x07>));

	PL192_VIC(config, m_vic1);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(XTAL(12'000'000), 320, 0, 320, 480, 0, 480); //Complete guess
	m_screen->set_screen_update(FUNC(iphone2g_state::screen_update));
}

ROM_START(iphone2g)
	ROM_REGION32_LE(0x10000, "bios", 0)
	ROM_LOAD("s5l8900-bootrom.bin", 0x00000, 0x10000, CRC(beb15cd1) SHA1(079a3acab577eb52cc349ea811af3cbd5d01b8f5))
ROM_END

} // anonymous namespace


/*    YEAR  NAME     PARENT  COMPAT  MACHINE     INPUT   STATE       INIT        COMPANY            FULLNAME      FLAGS */
// console section
CONS( 2007, iphone2g, 0, 0, iphone2g, 0, iphone2g_state, empty_init, "Apple", "iPhone (A1203)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



iq151.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

        IQ-151

        12/05/2009 Skeleton driver.

        07/June/2011 Added screen & keyboard (by looking at the Z80 code)


This computer depends on RAM just happening to be certain values at powerup.
If the conditions are not met, it may crash.

Monitor Commands:
C Call (address)
D Dump memory, any key to dump more, Return to finish
F Fill memory (start, end, withwhat)
G Goto (address)
L Cassette load
M Move (source start, source end, destination)
R Run
S Edit memory
W Cassette save (start, end, goto (0 for null))
X Display/Edit registers


ToDo:
- Add whatever devices may exist.

- Line 32 does not scroll, should it show?
  (could be reserved for a status line in a terminal mode)

- Note that the system checks for 3E at C000, if exist, jump to C000;
  otherwise then checks for non-FF at C800, if so, jumps to C800. Could be
  extra roms or some sort of boot device.

- Key beep sounds better if clock speed changed to 1MHz, but it is still
  highly annoying. Press Ctrl-G to hear the 2-tone bell.

- Cassette support is tested only in the emulator (monitor, BASIC and AMOS),
  needs to be tested with a real cassette dump.

****************************************************************************/

#include "emu.h"

#include "cpu/i8085/i8085.h"
#include "imagedev/cassette.h"
#include "machine/i8255.h"
#include "machine/pic8259.h"
#include "machine/timer.h"
#include "sound/spkrdev.h"

// cartridge slot
#include "bus/iq151/iq151.h"
#include "bus/iq151/rom.h"
#include "bus/iq151/disc2.h"
#include "bus/iq151/minigraf.h"
#include "bus/iq151/ms151a.h"
#include "bus/iq151/staper.h"
#include "bus/iq151/grafik.h"
#include "bus/iq151/video32.h"
#include "bus/iq151/video64.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class iq151_state : public driver_device
{
public:
	iq151_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pic(*this, "pic8259")
		, m_speaker(*this, "speaker")
		, m_cassette(*this, "cassette")
		, m_carts(*this, "slot%u", 1U)
		, m_boot_view(*this, "boot_view")
		, m_keyboard(*this, "X%X", 0U)
	{ }

	void iq151(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(iq151_break);

private:
	uint8_t keyboard_row_r();
	uint8_t keyboard_column_r();
	uint8_t ppi_portc_r();
	void ppi_portc_w(uint8_t data);
	void boot_bank_w(uint8_t data);
	uint8_t cartslot_r(offs_t offset);
	void cartslot_w(offs_t offset, uint8_t data);
	uint8_t cartslot_io_r(offs_t offset);
	void cartslot_io_w(offs_t offset, uint8_t data);
	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);

	INTERRUPT_GEN_MEMBER(iq151_vblank_interrupt);
	TIMER_DEVICE_CALLBACK_MEMBER(cassette_timer);
	void iq151_io(address_map &map) ATTR_COLD;
	void iq151_mem(address_map &map) ATTR_COLD;

	required_device<i8080_cpu_device> m_maincpu;
	required_device<pic8259_device> m_pic;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cassette;
	required_device_array<iq151cart_slot_device, 5> m_carts;
	memory_view m_boot_view;
	required_ioport_array<9> m_keyboard;

	uint8_t m_vblank_irq_state;
	uint8_t m_cassette_clk;
	uint8_t m_cassette_data;
};

uint8_t iq151_state::keyboard_row_r()
{
	uint8_t data = 0xff;

	for (int i = 0; i < 8; i++)
	{
		data &= m_keyboard[i]->read();
	}

	return data;
}

uint8_t iq151_state::keyboard_column_r()
{
	uint8_t data = 0x00;

	for (int i = 0; i < 8; i++)
	{
		if (m_keyboard[i]->read() == 0xff)
			data |= (1 << i);
	}

	return data;
}

uint8_t iq151_state::ppi_portc_r()
{
	uint8_t data = 0x00;

	if (m_cassette_data & 0x06)
	{
		// cassette read
		data |= ((m_cassette_clk & 1) << 5);
		data |= (m_cassette->input() > 0.00 ? 0x80 : 0x00);
	}
	else
	{
		// kb read
		data = m_keyboard[8]->read();
	}

	return (data & 0xf0) | (m_cassette_data & 0x0f);
}


void iq151_state::ppi_portc_w(uint8_t data)
{
	m_speaker->level_w(BIT(data, 3));
	m_cassette_data = data;
}

void iq151_state::boot_bank_w(uint8_t data)
{
	if (BIT(data, 0))
		m_boot_view.disable();
	else
		m_boot_view.select(0);
}


//**************************************************************************
//  Cartridge slot emulation
//**************************************************************************

uint8_t iq151_state::cartslot_r(offs_t offset)
{
	uint8_t data = 0xff;

	for (auto & elem : m_carts)
		elem->read(offset, data);

	return data;
}

void iq151_state::cartslot_w(offs_t offset, uint8_t data)
{
	for (auto & elem : m_carts)
		elem->write(offset, data);
}

uint8_t iq151_state::cartslot_io_r(offs_t offset)
{
	uint8_t data = 0xff;

	for (auto & elem : m_carts)
		elem->io_read(offset, data);

	return data;
}

void iq151_state::cartslot_io_w(offs_t offset, uint8_t data)
{
	for (auto & elem : m_carts)
		elem->io_write(offset, data);
}

void iq151_state::iq151_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(iq151_state::cartslot_r), FUNC(iq151_state::cartslot_w));

	map(0x0000, 0x7fff).ram();
	map(0x0000, 0x07ff).view(m_boot_view);
	m_boot_view[0](0x0000, 0x07ff).rom().region("maincpu", 0x0800);
	//m_boot_view[0](0x0000, 0x07ff).nopw(); // TODO: write ignored when boot ROM is selected?
	map(0xf000, 0xffff).rom().region("maincpu", 0x0000);
}

void iq151_state::iq151_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0xff).rw(FUNC(iq151_state::cartslot_io_r), FUNC(iq151_state::cartslot_io_w));

	map(0x80, 0x80).w(FUNC(iq151_state::boot_bank_w));
	map(0x84, 0x87).rw("ppi8255", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x88, 0x89).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
}


INPUT_CHANGED_MEMBER(iq151_state::iq151_break)
{
	m_pic->ir5_w(newval & 1);
}

/* Input ports */
static INPUT_PORTS_START( iq151 )
	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 !") PORT_CODE(KEYCODE_1)       PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 \"") PORT_CODE(KEYCODE_2)      PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 #") PORT_CODE(KEYCODE_3)       PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 $") PORT_CODE(KEYCODE_4)       PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 %") PORT_CODE(KEYCODE_5)       PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 &") PORT_CODE(KEYCODE_6)       PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 '") PORT_CODE(KEYCODE_7)       PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 (") PORT_CODE(KEYCODE_8)       PORT_CHAR('8') PORT_CHAR('(')

	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)     PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)     PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)     PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)     PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)     PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)     PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)     PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)     PORT_CHAR('I') PORT_CHAR('i')

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)     PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)     PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)     PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)     PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)     PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)     PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)     PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)     PORT_CHAR('K') PORT_CHAR('k')

	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)     PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)     PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)     PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)     PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)     PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)     PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)     PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA)       PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("X4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 )") PORT_CODE(KEYCODE_9)       PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)     PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)     PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP)        PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER)     PORT_CHAR(13)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del") PORT_CODE(KEYCODE_DEL)     PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ins") PORT_CODE(KEYCODE_INSERT)      PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)           PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_START("X5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)     PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)     PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("; +") PORT_CODE(KEYCODE_COLON)       PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH)       PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\ |") PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DL") PORT_CODE(KEYCODE_F6)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)           PORT_CHAR(UCHAR_MAMEKEY(F2))

	PORT_START("X6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^ ~") PORT_CODE(KEYCODE_EQUALS)      PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[ {") PORT_CODE(KEYCODE_OPENBRACE)   PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("] }") PORT_CODE(KEYCODE_CLOSEBRACE)  PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5)           PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("IL") PORT_CODE(KEYCODE_F7)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Home") PORT_CODE(KEYCODE_HOME)       PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4)           PORT_CHAR(UCHAR_MAMEKEY(F4))

	PORT_START("X7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("- =") PORT_CODE(KEYCODE_MINUS)       PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@ `") PORT_CODE(KEYCODE_TILDE)       PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(": *") PORT_CODE(KEYCODE_QUOTE)       PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)     PORT_CHAR(' ')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("_") PORT_CODE(KEYCODE_END) // its actually some sort of graphic character
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)       PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3)           PORT_CHAR(UCHAR_MAMEKEY(F3))

	PORT_START("X8")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT)  PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("FA") PORT_CODE(KEYCODE_RSHIFT)       // Function A
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("FB") PORT_CODE(KEYCODE_RCONTROL)     // Function B

	PORT_START("BREAK")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BREAK") PORT_CODE(KEYCODE_ESC)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iq151_state::iq151_break), 0)  PORT_CHAR(UCHAR_MAMEKEY(ESC))
INPUT_PORTS_END


INTERRUPT_GEN_MEMBER(iq151_state::iq151_vblank_interrupt)
{
	m_pic->ir6_w(m_vblank_irq_state & 1);
	m_vblank_irq_state ^= 1;
}

TIMER_DEVICE_CALLBACK_MEMBER(iq151_state::cassette_timer)
{
	m_cassette_clk ^= 1;

	m_cassette->output((m_cassette_data & 1) ^ (m_cassette_clk & 1) ? +1 : -1);
}

void iq151_state::machine_start()
{
}

void iq151_state::machine_reset()
{
	m_boot_view.select(0);

	m_vblank_irq_state = 0;
}

// this machine don't have a built-in video controller, but uses external cartridge
uint32_t iq151_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	bitmap.fill(0, cliprect);

	for (auto & elem : m_carts)
		elem->video_update(bitmap, cliprect);

	return 0;
}

static void iq151_cart(device_slot_interface &device)
{
	device.option_add("video32",  IQ151_VIDEO32);       // video32
	device.option_add("video64",  IQ151_VIDEO64);       // video64
	device.option_add("grafik",   IQ151_GRAFIK);        // Grafik
	device.option_add("disc2",    IQ151_DISC2);         // Disc 2
	device.option_add("minigraf", IQ151_MINIGRAF);      // Aritma Minigraf 0507
	device.option_add("ms151a",   IQ151_MS151A);        // MS151A XY Plotter
	device.option_add("staper",   IQ151_STAPER);        // STAPER
	device.option_add("basic6",   IQ151_BASIC6);        // BASIC6
	device.option_add("basicg",   IQ151_BASICG);        // BASICG
	device.option_add("amos1",    IQ151_AMOS1);         // AMOS cart 1
	device.option_add("amos2",    IQ151_AMOS2);         // AMOS cart 2
	device.option_add("amos3",    IQ151_AMOS3);         // AMOS cart 3
}

void iq151_state::iq151(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, XTAL(2'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &iq151_state::iq151_mem);
	m_maincpu->set_addrmap(AS_IO, &iq151_state::iq151_io);
	m_maincpu->set_vblank_int("screen", FUNC(iq151_state::iq151_vblank_interrupt));
	m_maincpu->in_inta_func().set("pic8259", FUNC(pic8259_device::acknowledge));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(iq151_state::screen_update));
	screen.set_size(64*8, 32*8);
	screen.set_visarea(0, 32*8-1, 0, 32*8-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	PIC8259(config, m_pic, 0);
	m_pic->out_int_callback().set_inputline(m_maincpu, 0);

	i8255_device &ppi(I8255(config, "ppi8255"));
	ppi.in_pa_callback().set(FUNC(iq151_state::keyboard_row_r));
	ppi.in_pb_callback().set(FUNC(iq151_state::keyboard_column_r));
	ppi.in_pc_callback().set(FUNC(iq151_state::ppi_portc_r));
	ppi.out_pc_callback().set(FUNC(iq151_state::ppi_portc_w));

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("iq151_cass");

	TIMER(config, "cassette_timer").configure_periodic(FUNC(iq151_state::cassette_timer), attotime::from_hz(2000));

	/* cartridge */
	IQ151CART_SLOT(config, m_carts[0], iq151_cart, nullptr);
	m_carts[0]->set_screen_tag("screen");
	m_carts[0]->out_irq0_callback().set(m_pic, FUNC(pic8259_device::ir0_w));
	m_carts[0]->out_irq1_callback().set(m_pic, FUNC(pic8259_device::ir1_w));
	m_carts[0]->out_irq2_callback().set(m_pic, FUNC(pic8259_device::ir2_w));
	m_carts[0]->out_irq3_callback().set(m_pic, FUNC(pic8259_device::ir3_w));
	m_carts[0]->out_irq4_callback().set(m_pic, FUNC(pic8259_device::ir4_w));
	IQ151CART_SLOT(config, m_carts[1], iq151_cart, nullptr);
	m_carts[1]->set_screen_tag("screen");
	m_carts[1]->out_irq0_callback().set(m_pic, FUNC(pic8259_device::ir0_w));
	m_carts[1]->out_irq1_callback().set(m_pic, FUNC(pic8259_device::ir1_w));
	m_carts[1]->out_irq2_callback().set(m_pic, FUNC(pic8259_device::ir2_w));
	m_carts[1]->out_irq3_callback().set(m_pic, FUNC(pic8259_device::ir3_w));
	m_carts[1]->out_irq4_callback().set(m_pic, FUNC(pic8259_device::ir4_w));
	IQ151CART_SLOT(config, m_carts[2], iq151_cart, nullptr);
	m_carts[2]->set_screen_tag("screen");
	m_carts[2]->out_irq0_callback().set(m_pic, FUNC(pic8259_device::ir0_w));
	m_carts[2]->out_irq1_callback().set(m_pic, FUNC(pic8259_device::ir1_w));
	m_carts[2]->out_irq2_callback().set(m_pic, FUNC(pic8259_device::ir2_w));
	m_carts[2]->out_irq3_callback().set(m_pic, FUNC(pic8259_device::ir3_w));
	m_carts[2]->out_irq4_callback().set(m_pic, FUNC(pic8259_device::ir4_w));
	IQ151CART_SLOT(config, m_carts[3], iq151_cart, nullptr);
	m_carts[3]->set_screen_tag("screen");
	m_carts[3]->out_irq0_callback().set(m_pic, FUNC(pic8259_device::ir0_w));
	m_carts[3]->out_irq1_callback().set(m_pic, FUNC(pic8259_device::ir1_w));
	m_carts[3]->out_irq2_callback().set(m_pic, FUNC(pic8259_device::ir2_w));
	m_carts[3]->out_irq3_callback().set(m_pic, FUNC(pic8259_device::ir3_w));
	m_carts[3]->out_irq4_callback().set(m_pic, FUNC(pic8259_device::ir4_w));
	IQ151CART_SLOT(config, m_carts[4], iq151_cart, "video32");
	m_carts[4]->set_screen_tag("screen");
	m_carts[4]->out_irq0_callback().set(m_pic, FUNC(pic8259_device::ir0_w));
	m_carts[4]->out_irq1_callback().set(m_pic, FUNC(pic8259_device::ir1_w));
	m_carts[4]->out_irq2_callback().set(m_pic, FUNC(pic8259_device::ir2_w));
	m_carts[4]->out_irq3_callback().set(m_pic, FUNC(pic8259_device::ir3_w));
	m_carts[4]->out_irq4_callback().set(m_pic, FUNC(pic8259_device::ir4_w));

	/* Software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("iq151_cart");
	SOFTWARE_LIST(config, "flop_list").set_original("iq151_flop");
}

/* ROM definition */
ROM_START( iq151 )
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASE )
	/* A number of bios versions here. The load address is shown for each */
	ROM_SYSTEM_BIOS( 0, "orig", "Original" )
	ROMX_LOAD( "iq151_monitor_orig.rom", 0x0000, 0x1000, CRC(acd10268) SHA1(4d75c73f155ed4dc2ac51a9c22232f869cca95e2), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "disasm", "Disassembler" )
	ROMX_LOAD( "iq151_monitor_disasm.rom", 0x0000, 0x1000, CRC(45c2174e) SHA1(703e3271a124c3ef9330ae399308afd903316ab9), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "cpm", "CPM" )
	ROMX_LOAD( "iq151_monitor_cpm.rom", 0x0000, 0x1000, CRC(26f57013) SHA1(4df396edc375dd2dd3c82c4d2affb4f5451066f1), ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "cpmold", "CPM (old)" )
	ROMX_LOAD( "iq151_monitor_cpm_old.rom", 0x0000, 0x1000, CRC(6743e1b7) SHA1(ae4f3b1ba2511a1f91c4e8afdfc0e5aeb0fb3a42), ROM_BIOS(3))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  STATE        INIT        COMPANY         FULLNAME  FLAGS
COMP( 198?, iq151, 0,      0,      iq151,   iq151, iq151_state, empty_init, "ZPA Novy Bor", "IQ-151", 0 )



iquest.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/******************************************************************************

   Leapfrog IQuest

   has LCD display

*******************************************************************************/

#include "emu.h"

#include "cpu/mcs51/mcs51.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "machine/bankdev.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "screen.h"


namespace {

class leapfrog_iquest_state : public driver_device
{
public:
	leapfrog_iquest_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_cart(*this, "cartslot")
		, m_screen(*this, "screen")
		, m_maincpu(*this, "maincpu")
		, m_rombank(*this, "rombank")
		, m_cart_region(nullptr)
	{ }

	void leapfrog_iquest(machine_config &config);

protected:
	void leapfrog_base(machine_config &config);

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	required_device<generic_slot_device> m_cart;
	required_device<screen_device> m_screen;

	void rx_line_hack(int state);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void prog_map(address_map &map) ATTR_COLD;
	void ext_map(address_map &map) ATTR_COLD;

	required_device<mcs51_cpu_device> m_maincpu;

	void rom_map(address_map &map) ATTR_COLD;

	required_device<address_map_bank_device> m_rombank;
	memory_region *m_cart_region;

	uint8_t m_lowerbank[2];
	uint8_t m_upperbank[2];

	uint8_t lowerbank_r(offs_t offset);
	uint8_t upperbank_r(offs_t offset);
	void lowerbank_w(offs_t offset, uint8_t data);
	void upperbank_w(offs_t offset, uint8_t data);

	uint8_t m_iobank[2];

	uint8_t iobank_r(offs_t offset);
	void iobank_w(offs_t offset, uint8_t data);


	uint8_t lowerwindow_r(offs_t offset);
	uint8_t upperwindow_r(offs_t offset);

	uint8_t iowindow_r(offs_t offset);
	void iowindow_w(offs_t offset, uint8_t data);

	uint8_t unk_ff80_r();
	void unk_ff80_w(uint8_t data);
	uint8_t m_ff80 = 0;

	uint8_t unk_fc00_r();
	uint8_t unk_fc01_r(offs_t offset);

	uint8_t unk_fc2f_r();
	uint8_t unk_fc3f_r();
	void unk_fc3f_w(uint8_t data);

	void unk_fc22_w(uint8_t data);

	uint8_t unk_fce5_r();
	void unk_fce5_w(uint8_t data);

	uint8_t m_fce5 = 0;

	uint8_t unk_ff00_01_r(offs_t offset);

	void unk_ff81_84_w(offs_t offset, uint8_t data);
	uint8_t m_ff81_84[4]{};

	uint8_t unk_ff91_93_r(offs_t offset);
	void unk_ff91_93_w(offs_t offset, uint8_t data);
	uint8_t m_ff91_93[3]{};

	uint8_t unk_ffa8_r();
	void unk_ffa8_w(uint8_t data);
	uint8_t m_ffa8 = 0;

	void unk_ffa9_w(uint8_t data);

	uint8_t port0_r();
	void port0_w(u8 data);
	uint8_t port1_r();
	void port1_w(u8 data);
	uint8_t port2_r();
	void port2_w(u8 data);
	uint8_t port3_r();
	void port3_w(u8 data);
};


class leapfrog_turboextreme_state : public leapfrog_iquest_state
{
public:
	leapfrog_turboextreme_state(const machine_config &mconfig, device_type type, const char *tag)
		: leapfrog_iquest_state(mconfig, type, tag)
	{ }

	void leapfrog_turboex(machine_config &config);
};

class leapfrog_turbotwistmath_state : public leapfrog_iquest_state
{
public:
	leapfrog_turbotwistmath_state(const machine_config &mconfig, device_type type, const char *tag)
		: leapfrog_iquest_state(mconfig, type, tag)
	{ }

	void leapfrog_turbotwistmath(machine_config &config);
};

class leapfrog_turbotwistvocabulator_state : public leapfrog_iquest_state
{
public:
	leapfrog_turbotwistvocabulator_state(const machine_config &mconfig, device_type type, const char *tag)
		: leapfrog_iquest_state(mconfig, type, tag)
	{ }

	void leapfrog_turbotwistvocabulator(machine_config &config);
};

class leapfrog_turbotwistspelling_state : public leapfrog_iquest_state
{
public:
	leapfrog_turbotwistspelling_state(const machine_config &mconfig, device_type type, const char *tag)
		: leapfrog_iquest_state(mconfig, type, tag)
	{ }

	void leapfrog_turbotwistspelling(machine_config &config);
};


class leapfrog_turbotwistbrainquest_state : public leapfrog_iquest_state
{
public:
	leapfrog_turbotwistbrainquest_state(const machine_config &mconfig, device_type type, const char *tag)
		: leapfrog_iquest_state(mconfig, type, tag)
	{ }

	void leapfrog_turbotwistbrainquest(machine_config &config);
};


void leapfrog_iquest_state::machine_start()
{
	// if there's a cart, override the standard mapping
	if (m_cart && m_cart->exists())
	{
		m_cart_region = memregion(std::string(m_cart->tag()) + GENERIC_ROM_REGION_TAG);
	}
}

void leapfrog_iquest_state::machine_reset()
{
	m_lowerbank[0] = m_lowerbank[1] = 0x00;
	m_upperbank[0] = m_upperbank[1] = 0x00;
	m_iobank[0] = m_iobank[1] = 0x00;

	m_rombank->set_bank(0);
	m_ff80 = 0x00;

	m_fce5 = 0x00;

	m_ff91_93[0] = m_ff91_93[1] = m_ff91_93[2] = 0x00;
}


uint8_t leapfrog_iquest_state::lowerbank_r(offs_t offset)
{
	return m_lowerbank[offset];
}

uint8_t leapfrog_iquest_state::upperbank_r(offs_t offset)
{
	return m_upperbank[offset];
}


void leapfrog_iquest_state::lowerbank_w(offs_t offset, uint8_t data)
{
	m_lowerbank[offset] = data;
}

void leapfrog_iquest_state::upperbank_w(offs_t offset, uint8_t data)
{
	m_upperbank[offset] = data;
}

uint8_t leapfrog_iquest_state::iobank_r(offs_t offset)
{
	return m_iobank[offset];
}


void leapfrog_iquest_state::iobank_w(offs_t offset, uint8_t data)
{
	//printf("iobank_w %d, %02x\n", offset, data);
	m_iobank[offset] = data;
}


void leapfrog_iquest_state::rom_map(address_map &map)
{
	map(0x00000000, 0x003fffff).rom().region("maincpu", 0);
	map(0x00400000, 0x007fffff).ram();
}

uint8_t leapfrog_iquest_state::lowerwindow_r(offs_t offset)
{
	uint32_t bank = ((m_lowerbank[0] << 8) | (m_lowerbank[1])) * 0x8000;
	return m_rombank->read8(bank + offset);
}

uint8_t leapfrog_iquest_state::upperwindow_r(offs_t offset)
{
	uint32_t bank = ((m_upperbank[0] << 8) | (m_upperbank[1])) * 0x8000;
	return m_rombank->read8(bank + offset);
}

uint8_t leapfrog_iquest_state::iowindow_r(offs_t offset)
{
	uint32_t bank = ((m_iobank[0] << 8) | (m_iobank[1])) * 0x8000;
	return m_rombank->read8(bank + offset);
}

void leapfrog_iquest_state::iowindow_w(offs_t offset, uint8_t data)
{
	uint32_t bank = ((m_iobank[0] << 8) | (m_iobank[1])) * 0x8000;
	m_rombank->write8(bank + offset, data);
}


void leapfrog_iquest_state::prog_map(address_map &map)
{
	map(0x0000, 0x7fff).r(FUNC(leapfrog_iquest_state::lowerwindow_r));
	map(0x8000, 0xffff).r(FUNC(leapfrog_iquest_state::upperwindow_r));
}

uint8_t leapfrog_iquest_state::unk_ff80_r()
{
	logerror("%s: unk_ff80_r\n", machine().describe_context());
	return m_ff80;
}

void leapfrog_iquest_state::unk_ff80_w(uint8_t data)
{
	// must return what is written for some startup tests
	logerror("%s: m_ff80 %02x\n", machine().describe_context(), data);
	m_ff80 = data;
}

uint8_t leapfrog_iquest_state::unk_fc00_r()
{
	logerror("%s: unk_fc00_r\n", machine().describe_context());
	return 0x00;// machine().rand();
}

uint8_t leapfrog_iquest_state::unk_fc01_r(offs_t offset)
{
	logerror("%s: unk_fc01_r %d\n", machine().describe_context(), offset);
	return 0xff;// machine().rand();
}

uint8_t leapfrog_iquest_state::unk_fc2f_r()
{
	logerror("%s: unk_fc2f_r\n", machine().describe_context());
	return 0x00;// machine().rand();
}

uint8_t leapfrog_iquest_state::unk_fc3f_r()
{
	logerror("%s: unk_fc3f_r\n", machine().describe_context());
	return 0x00;// machine().rand();
}

void leapfrog_iquest_state::unk_fc3f_w(uint8_t data)
{
	logerror("%s: unk_fc3f_w %02x\n", machine().describe_context(), data);
}

void leapfrog_iquest_state::unk_fc22_w(uint8_t data)
{
	logerror("%s: unk_fc22_w %02x\n", machine().describe_context(), data);
}

uint8_t leapfrog_iquest_state::unk_ff00_01_r(offs_t offset)
{
	// read around the time of fc22 writes
	logerror("%s: unk_ff00_01_r %d\n", machine().describe_context(), offset);
	return 0x00;
}

uint8_t leapfrog_iquest_state::unk_fce5_r()
{
	logerror("%s: unk_fce5_r\n", machine().describe_context());
	return 0x00;// m_fce5;// machine().rand();
}

void leapfrog_iquest_state::unk_fce5_w(uint8_t data)
{
	// repeated read/write pattern on this address
	logerror("%s: unk_fce5_w %02x\n", machine().describe_context(), data);
	m_fce5 = data;
}

/*

in all cases ff91-ff93 writes appear to be an address in the current main space
which is 0x10000-0x17fff, 0x10000-0x17fff in ROM at the time of writing
or for the later writes  0x10000-0x17fff, 0x20000-0x27fff  (the b448 is from 0x23448 for example)

each of the blocks pointed to is preceded by a 0x00 byte? (maybe 0x00 is a terminator for previous block?)

the blocks being pointed at during startup are debug text, c style strings for 'printf' functions, complete with
formatting characters while the ff81 to ff84 area contains the parameters.

it seems likely this is just a RAM area and these are temporary storage for the debug print function,
either for futher processing and output to an actual debug console, or shared with another device (ff80, which is
possibly the start of this RAM area, is written after putting these pointers in RAM)

*/

uint8_t leapfrog_iquest_state::unk_ff91_93_r(offs_t offset)
{
	logerror("%s: unk_ff91_93_r %d\n", machine().describe_context(), offset);
	return 0x00;// m_ff91_93[offset];
}

void leapfrog_iquest_state::unk_ff91_93_w(offs_t offset, uint8_t data)
{
	// these 3 values are written together
	m_ff91_93[offset] = data;

	// form is ff then 2 other values, these are pointers into the main space it seems

	if (offset == 2)
	{
		logerror("%s: write to ff91 to ff93 region %02x %02x %02x (current banks are %08x %08x %08x)\n", machine().describe_context(), m_ff91_93[0], m_ff91_93[1], m_ff91_93[2], ((m_lowerbank[0] << 8) | (m_lowerbank[1])) * 0x8000, ((m_upperbank[0] << 8) | (m_upperbank[1])) * 0x8000, ((m_iobank[0] << 8) | (m_iobank[1])) * 0x8000);

		uint16_t pointer = (m_ff91_93[1] << 8) | (m_ff91_93[2]);

		address_space& spc = m_maincpu->space(AS_PROGRAM);
		char readdat = 0x00;

		std::string textout;
		do
		{
			readdat = spc.read_byte(pointer++);
			textout.append(1, readdat);
		} while (readdat != 0x00);
		logerror("%s: DEBUG MESSAGE: %s", machine().describe_context(), textout);
		logerror("\n");
	}
}

void leapfrog_iquest_state::unk_ff81_84_w(offs_t offset, uint8_t data)
{
	// these 4 values are written together, with FFA8 before and FFA9 after
	// form is usually 00 00 then 2 used values, maybe coordinates?
	// used in conjunction with unk_ff91_93_w writes above
	m_ff81_84[offset] = data;

	if (offset == 3)
	{
		logerror("%s: write to ff81 to ff84 region %02x %02x %02x %02x\n", machine().describe_context(), m_ff81_84[0], m_ff81_84[1], m_ff81_84[2], m_ff81_84[3]);

		uint16_t pointer = (m_ff81_84[2] << 8) | (m_ff81_84[3]);

		if (pointer != 0x00)
		{
			address_space& spc = m_maincpu->space(AS_PROGRAM);
			char readdat = 0x00;

			std::string textout;
			do
			{
				readdat = spc.read_byte(pointer++);
				textout.append(1, readdat);
			} while (readdat != 0x00);

			//logerror("%s: %s", machine().describe_context(), textout);
			//logerror("\n");
		}
	}
}

uint8_t leapfrog_iquest_state::unk_ffa8_r()
{
	logerror("%s: read from ffa8 ----------- POSSIBLE END OF DEBUG TEXT OPERATION?\n", machine().describe_context());
	return 0x00;
}

void leapfrog_iquest_state::unk_ffa8_w(uint8_t data)
{
	logerror("%s: write to ffa8 %02x ----------- POSSIBLE START OF DEBUG TEXT OPERATION?\n", machine().describe_context(), data);
	m_ffa8 = data;
}

void leapfrog_iquest_state::unk_ffa9_w(uint8_t data)
{
	logerror("%s: write to ffa9 %02x ----------- POSSIBLE TRIGGER DEBUG TEXT OPERATION?? (current banks are %08x %08x %08x)\n", machine().describe_context(), data, ((m_lowerbank[0] << 8) | (m_lowerbank[1])) * 0x8000, ((m_upperbank[0] << 8) | (m_upperbank[1])) * 0x8000, ((m_iobank[0] << 8) | (m_iobank[1])) * 0x8000);
}




void leapfrog_iquest_state::ext_map(address_map &map)
{
	map(0x0000, 0x7fff).rw(FUNC(leapfrog_iquest_state::iowindow_r), FUNC(leapfrog_iquest_state::iowindow_w)); // assume this accesses the same space, with different bank register

	map(0xc260, 0xc52f).ram(); // = clears 0x2d0 bytes (90*64 / 8) display buffer?
	map(0xc530, 0xc7ff).ram(); // = clears 0x2d0 bytes (90*64 / 8) display buffer?

	//map(0xf001, 0xf056).ram(); // written as a block
	map(0xf000, 0xf5ff).ram(); // ? 0xf400 - 0xf427 written as a block, other areas uncertain, might be more registers in here as there are reads too

	map(0xfc00, 0xfc00).r(FUNC(leapfrog_iquest_state::unk_fc00_r));
	map(0xfc01, 0xfc04).r(FUNC(leapfrog_iquest_state::unk_fc01_r));

	map(0xfc06, 0xfc07).rw(FUNC(leapfrog_iquest_state::lowerbank_r), FUNC(leapfrog_iquest_state::lowerbank_w)); // ROM / RAM window in main space at 0000-7fff
	map(0xfc08, 0xfc09).rw(FUNC(leapfrog_iquest_state::upperbank_r), FUNC(leapfrog_iquest_state::upperbank_w)); // ROM / RAM window in main space at 8000-ffff
	map(0xfc0a, 0xfc0b).rw(FUNC(leapfrog_iquest_state::iobank_r), FUNC(leapfrog_iquest_state::iobank_w)); // ROM / RAM window in ext space at 0000-7fff

	map(0xfc22, 0xfc22).w(FUNC(leapfrog_iquest_state::unk_fc22_w));

	map(0xfc2f, 0xfc2f).r(FUNC(leapfrog_iquest_state::unk_fc2f_r));
	map(0xfc3f, 0xfc3f).rw(FUNC(leapfrog_iquest_state::unk_fc3f_r), FUNC(leapfrog_iquest_state::unk_fc3f_w));

	map(0xfce5, 0xfce5).rw(FUNC(leapfrog_iquest_state::unk_fce5_r), FUNC(leapfrog_iquest_state::unk_fce5_w));

	map(0xff00, 0xff01).r(FUNC(leapfrog_iquest_state::unk_ff00_01_r));

	// it seems more likely that this is just RAM, as that after setting pointers in RAM, they're used to construct
	// strings to transmit over the serial.
	// however, mapping this area as RAM instead results in the program stalling much earlier, waiting for $24.3 to
	// be cleared.
	// 017658: 20 23 fd  jb    $24.3,$17658
	//
	//The only realistic place for this to be cleared is deep in the interrupt handler for
	// Serial Receive/Transmit
	// 010023: 02 76 9e  ljmp  $769E
	// (which eventually can reach)
	// 016991: c2 23     clr   $24.3
	// however I'm uncertain how to get the driver to trigger this at all.
	if (0)
	{
		map(0xff80, 0xffff).ram();
	}
	else
	{
		map(0xff80, 0xff80).rw(FUNC(leapfrog_iquest_state::unk_ff80_r), FUNC(leapfrog_iquest_state::unk_ff80_w));

		map(0xff81, 0xff84).w(FUNC(leapfrog_iquest_state::unk_ff81_84_w));

		map(0xff91, 0xff93).rw(FUNC(leapfrog_iquest_state::unk_ff91_93_r), FUNC(leapfrog_iquest_state::unk_ff91_93_w));

		map(0xffa8, 0xffa8).rw(FUNC(leapfrog_iquest_state::unk_ffa8_r), FUNC(leapfrog_iquest_state::unk_ffa8_w));
		map(0xffa9, 0xffa9).w(FUNC(leapfrog_iquest_state::unk_ffa9_w));
	}
}

DEVICE_IMAGE_LOAD_MEMBER(leapfrog_iquest_state::cart_load)
{
	uint32_t size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

static INPUT_PORTS_START( leapfrog_iquest )
INPUT_PORTS_END

uint32_t leapfrog_iquest_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

// doesn't help?
void leapfrog_iquest_state::rx_line_hack(int state)
{
	if (0)
	{
		// HACK: force past the wait loop if we're treating ff80 - ffff as RAM
		address_space& spc = m_maincpu->space(AS_DATA);
		uint8_t readdat = spc.read_byte(0x24);
		readdat &= ~0x08;
		spc.write_byte(0x24, readdat);
	}
}

uint8_t leapfrog_iquest_state::port0_r()
{
	logerror("%s: port0_r\n", machine().describe_context());
	return 0x00;
}

uint8_t leapfrog_iquest_state::port1_r()
{
	logerror("%s: port1_r\n", machine().describe_context());
	return 0x00;
}

uint8_t leapfrog_iquest_state::port2_r()
{
	logerror("%s: port2_r\n", machine().describe_context());
	return 0x00;
}

uint8_t leapfrog_iquest_state::port3_r()
{
	logerror("%s: port3_r\n", machine().describe_context());
	return 0x00;
}

void leapfrog_iquest_state::port0_w(u8 data)
{
	logerror("%s: port0_w %02x\n", machine().describe_context(), data);
}

void leapfrog_iquest_state::port1_w(u8 data)
{
	logerror("%s: port1_w %02x\n", machine().describe_context(), data);
}

void leapfrog_iquest_state::port2_w(u8 data)
{
	logerror("%s: port2_w %02x\n", machine().describe_context(), data);
}

void leapfrog_iquest_state::port3_w(u8 data)
{
	logerror("%s: port3_w %02x\n", machine().describe_context(), data);
}



void leapfrog_iquest_state::leapfrog_base(machine_config &config)
{
	// seems to have an IRQ vector at 002b, which would suggest it's an 8052 or similar, rather than plain 8031?
	//I8052(config, m_maincpu, 96000000/10); // unknown clock
	I8032(config, m_maincpu, 96000000/10); // unknown clock
	m_maincpu->set_addrmap(AS_PROGRAM, &leapfrog_iquest_state::prog_map);
	m_maincpu->set_addrmap(AS_IO, &leapfrog_iquest_state::ext_map);
	m_maincpu->port_in_cb<0>().set(FUNC(leapfrog_iquest_state::port0_r));
	m_maincpu->port_out_cb<0>().set(FUNC(leapfrog_iquest_state::port0_w));
	m_maincpu->port_in_cb<1>().set(FUNC(leapfrog_iquest_state::port1_r));
	m_maincpu->port_out_cb<1>().set(FUNC(leapfrog_iquest_state::port1_w));
	m_maincpu->port_in_cb<2>().set(FUNC(leapfrog_iquest_state::port2_r));
	m_maincpu->port_out_cb<2>().set(FUNC(leapfrog_iquest_state::port2_w));
	m_maincpu->port_in_cb<3>().set(FUNC(leapfrog_iquest_state::port3_r));
	m_maincpu->port_out_cb<3>().set(FUNC(leapfrog_iquest_state::port3_w));

	ADDRESS_MAP_BANK(config, "rombank").set_map(&leapfrog_iquest_state::rom_map).set_options(ENDIANNESS_LITTLE, 8, 31, 0x80000000);
}

void leapfrog_iquest_state::leapfrog_iquest(machine_config &config)
{
	leapfrog_iquest_state::leapfrog_base(config);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(10));
	m_screen->set_size(90, 64);
	m_screen->set_visarea(0, 90-1, 0, 64-1);
	m_screen->set_screen_update(FUNC(leapfrog_iquest_state::screen_update));
	m_screen->screen_vblank().set(FUNC(leapfrog_iquest_state::rx_line_hack));

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "leapfrog_iquest_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(leapfrog_iquest_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("leapfrog_iquest_cart");
}

void leapfrog_turboextreme_state::leapfrog_turboex(machine_config &config)
{
	leapfrog_iquest_state::leapfrog_base(config);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(10));
	m_screen->set_size(64, 32); // unknown resolution, lower than iquest
	m_screen->set_visarea(0, 64-1, 0, 32-1);
	m_screen->set_screen_update(FUNC(leapfrog_turboextreme_state::screen_update));
	m_screen->screen_vblank().set(FUNC(leapfrog_turboextreme_state::rx_line_hack));

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "leapfrog_turboextreme_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(leapfrog_turboextreme_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("turboextreme_cart");
}

void leapfrog_turbotwistmath_state::leapfrog_turbotwistmath(machine_config &config)
{
	leapfrog_iquest_state::leapfrog_base(config);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(10));
	m_screen->set_size(64, 8); // unknown resolution, single row display
	m_screen->set_visarea(0, 64-1, 0, 8-1);
	m_screen->set_screen_update(FUNC(leapfrog_turbotwistmath_state::screen_update));
	m_screen->screen_vblank().set(FUNC(leapfrog_turbotwistmath_state::rx_line_hack));

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "leapfrog_turbotwistmath_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(leapfrog_turbotwistmath_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("ttwist_math_cart");
}

void leapfrog_turbotwistspelling_state::leapfrog_turbotwistspelling(machine_config &config)
{
	leapfrog_iquest_state::leapfrog_base(config);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(10));
	m_screen->set_size(64, 8); // unknown resolution, single row display
	m_screen->set_visarea(0, 64-1, 0, 8-1);
	m_screen->set_screen_update(FUNC(leapfrog_turbotwistspelling_state::screen_update));
	m_screen->screen_vblank().set(FUNC(leapfrog_turbotwistspelling_state::rx_line_hack));

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "leapfrog_turbotwistspelling_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(leapfrog_turbotwistspelling_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("ttwist_spelling_cart");
}

void leapfrog_turbotwistvocabulator_state::leapfrog_turbotwistvocabulator(machine_config &config)
{
	leapfrog_iquest_state::leapfrog_base(config);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(10));
	m_screen->set_size(64, 8); // unknown resolution, single row display
	m_screen->set_visarea(0, 64-1, 0, 8-1);
	m_screen->set_screen_update(FUNC(leapfrog_turbotwistvocabulator_state::screen_update));
	m_screen->screen_vblank().set(FUNC(leapfrog_turbotwistvocabulator_state::rx_line_hack));

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "leapfrog_turbotwistvocabulator_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(leapfrog_turbotwistvocabulator_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("ttwist_vocabulator_cart");
}


void leapfrog_turbotwistbrainquest_state::leapfrog_turbotwistbrainquest(machine_config &config)
{
	leapfrog_iquest_state::leapfrog_base(config);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(10));
	m_screen->set_size(64, 8); // unknown resolution, single row display
	m_screen->set_visarea(0, 64-1, 0, 8-1);
	m_screen->set_screen_update(FUNC(leapfrog_turbotwistbrainquest_state::screen_update));
	m_screen->screen_vblank().set(FUNC(leapfrog_turbotwistbrainquest_state::rx_line_hack));

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "leapfrog_turbotwistbrainquest_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(leapfrog_turbotwistbrainquest_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("ttwist_brainquest_cart");
}

ROM_START( iquest )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "iquest.bin", 0x000000, 0x400000, CRC(f785dc4e) SHA1(ec002c18df536737334fe6b7db0e7342bad7b66b))

	// there is also a 39vf512 flash containing user data
ROM_END

ROM_START( turboex )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "turbotwistextreme.bin", 0x000000, 0x400000, CRC(d7cabcff) SHA1(7813811c0518aba017f7a79fd477be5ed9fa7529))
ROM_END

ROM_START( ttwistm )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "turbotwistmath.bin", 0x000000, 0x200000, CRC(a21d3723) SHA1(d0ae245621d7bc92bdf9fd683908690db6e25133))
ROM_END

ROM_START( ttwistsp ) // PCB marks ROM glob as 8M
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "ttspelling.bin", 0x000000, 0x100000, CRC(09715a8e) SHA1(5d7eb7b714b95012aeb03c37dd0b71a1c025bdda))
ROM_END

ROM_START( ttwistvc ) // PCB marks ROM glob as 8M
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "vocabulator.bin", 0x000000, 0x100000, CRC(71f9c4c9) SHA1(8cafb42bd56c7db99949781e42b6ad4991ee2246))
ROM_END

ROM_START( ttwistbq )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "turbotwistbrainquest.bin", 0x000000, 0x200000, CRC(b184a517) SHA1(181975da58b3d117a389c54ac025b6a9b24342b2))
ROM_END

ROM_START( ttwistfb ) // PCB marks ROM glob as 16M
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "factblaster.bin", 0x000000, 0x200000, CRC(8efd63f5) SHA1(2cbd1299006ad9743d846e774afe7a134ba0fce3))
ROM_END

} // anonymous namespace


//    year, name,        parent,    compat, machine,                         input,            class,                                init,       company,    fullname,                         flags
// it is unknown if the versions of IQuest without 4.0 on the case have different system ROM
CONS( 2004, iquest,      0,         0,      leapfrog_iquest,                 leapfrog_iquest,  leapfrog_iquest_state,                empty_init, "LeapFrog", "IQuest 4.0 (US)",                MACHINE_NO_SOUND | MACHINE_NOT_WORKING )

CONS( 2004, turboex,     0,         0,      leapfrog_turboex,                leapfrog_iquest,  leapfrog_turboextreme_state,          empty_init, "LeapFrog", "Turbo Extreme (US)",             MACHINE_NO_SOUND | MACHINE_NOT_WORKING )

// from a silver unit with orange lettering (there are different case styles, it is unknown if the software changed)
CONS( 2002, ttwistm,     0,         0,      leapfrog_turbotwistmath,         leapfrog_iquest,  leapfrog_turbotwistmath_state,        empty_init, "LeapFrog", "Turbo Twist Math (US)",          MACHINE_NO_SOUND | MACHINE_NOT_WORKING )

// Brain Quest / Fact Blaster are compatible with the same cartridges
CONS( 200?, ttwistfb,    0,         0,      leapfrog_turbotwistbrainquest,   leapfrog_iquest,  leapfrog_turbotwistbrainquest_state,  empty_init, "LeapFrog", "Turbo Twist Fact Blaster (US)",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2002, ttwistbq,    0,         0,      leapfrog_turbotwistbrainquest,   leapfrog_iquest,  leapfrog_turbotwistbrainquest_state,  empty_init, "LeapFrog", "Turbo Twist Brain Quest (US)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING )

// from a green unit with blue edges
CONS( 2000, ttwistsp,    0,         0,      leapfrog_turbotwistspelling,     leapfrog_iquest,  leapfrog_turbotwistspelling_state,    empty_init, "LeapFrog", "Turbo Twist Spelling (US)",      MACHINE_NO_SOUND | MACHINE_NOT_WORKING )

CONS( 2001, ttwistvc,    0,         0,      leapfrog_turbotwistvocabulator,  leapfrog_iquest,  leapfrog_turbotwistvocabulator_state, empty_init, "LeapFrog", "Turbo Twist Vocabulator (US)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING )

// Undumped units

// These have 2 digit 7-seg style displays, it is unknown if they are on related hardware
// Twist & Shout Addition
// Twist & Shout Subtraction
// Twist & Shout Multiplication
// Twist & Shout Division

// This appears to have a 4 digit 7-seg style display, unknown if it is on related hardware
// Twist & Shout Phonics



iqunlim.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco

/*

IQ Unlimited - GERMAN:
      +------------------------------------------------------------------------------+
      |                                                                              |
+-----+                                                                              |
|                                                                                    |
|                                                                                    |
|                                                                                    |
|  +----+                                                +---+                       |
|  | A2 |                +----+                          |   |                       |
|  |    |                | A4 |                          |A1 |        +-+            |
|  +----+                +----+                          |   |        | |            |
|                                                        |   |        +-+            |
|                                                        |   |                       |
|                                                        +---+                    +--+
|                                                                                 |
|                                                       +-------+                 |
+--+                                                    |65C5L5K|            +----+
   |                                                    | HC374 |            |
+--+                         +----------+               +-------+            +--+
|                            |DragonBall|                                       |
| C         +----+           |EZ        |               +-------+             C |
| A         | A3 |           |          |               |65C5L5K|             A |
| R         +----+           |LSC414328P|               | HC374 |             R |
| T                          |U16  IJ75C|               +-------+             T |
| R                          | HHAV984S |                                     R |
| I                          +----------+                                     I |
| D  CARD 1 +------------+                                            CARD 0  D |
| G         | AM29F0400  |                                                    G |
| E         |            |     +------+                +--------+             E |
|           +------------+     | LGS  |                |LHMN5KR7|               |
| S                            |      |                |        |             S |
| L                            |GM71C1|                |  1998  |             L |
| O       GER                  |8163CJ|                |        |             O |
| T       038                  |6     |                |27-06126|             T |
|                              |      |                |-007    |               |
+--+                           |      |                |        |            +--+
   |                           |      |                |  VTECH |            |
   |                           +------+                +--------+            |
   |                                                   35-19600-200  703139-G|
   +-------------------------------------------------------------------------+

A1 = 98AHCLT / 27-05992-0-0 / VTech
A2 = 9932 HBL / C807U-1442 / 35016B / Japan
A3 = ACT139
A4 = MAX232

*/

#include "emu.h"
#include "machine/mc68328.h"
#include "machine/ram.h"
#include "video/mc68328lcd.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"

#include "screen.h"
#include "softlist_dev.h"

#define LOG_CARD_A_READ     (1U << 1)
#define LOG_CARD_A_WRITE    (1U << 2)
#define LOG_CARD_B_READ     (1U << 3)
#define LOG_CARD_B_WRITE    (1U << 4)
#define LOG_ALL             (LOG_CARD_A_READ | LOG_CARD_A_WRITE | LOG_CARD_B_READ | LOG_CARD_B_WRITE)

#define VERBOSE (LOG_ALL)
#include "logmacro.h"

namespace
{

class iqunlim_state : public driver_device
{
public:
	iqunlim_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ram(*this, RAM_TAG),
		m_lcdctrl(*this, "lcdctrl"),
		m_screen(*this, "screen"),
		m_cart(*this, "cartslot")
	{ }

	void iqunlim(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void mem_map(address_map &map) ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void card4x_w(offs_t offset, uint16_t data, uint16_t mem_mask = 0xffff);
	uint16_t card4x_r(offs_t offset, uint16_t mem_mask = 0xffff);
	void card5x_w(offs_t offset, uint16_t data, uint16_t mem_mask = 0xffff);
	uint16_t card5x_r(offs_t offset, uint16_t mem_mask = 0xffff);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	required_device<mc68ez328_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<mc68328_lcd_device> m_lcdctrl;
	required_device<screen_device> m_screen;
	required_device<generic_slot_device> m_cart;
};

void iqunlim_state::machine_start()
{
	address_space &space = m_maincpu->space(AS_PROGRAM);
	space.install_ram(0x00000000, m_ram->size() - 1, m_ram->pointer());
}

void iqunlim_state::machine_reset()
{
	// Copy ROM vectors into RAM
	memcpy(m_ram->pointer(), memregion("ipl")->base(), 0x100);
}

void iqunlim_state::mem_map(address_map &map)
{
	map(0x02000000, 0x023fffff).rom().region("ipl", 0); // 68EZ328 /CSA0 pin selects System ROM after bootup
	map(0x03000000, 0x0307ffff).ram(); // Region used by the internal flash memory
	map(0x04000000, 0x04ffffff).rw(FUNC(iqunlim_state::card4x_r), FUNC(iqunlim_state::card4x_w)); // Region used by Card B
	map(0x05000000, 0x05ffffff).rw(FUNC(iqunlim_state::card5x_r), FUNC(iqunlim_state::card5x_w)); // Region used by Card A
}

uint32_t iqunlim_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	m_lcdctrl->video_update(bitmap, cliprect);
	return 0;
}

uint16_t iqunlim_state::card4x_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t data = 0;
	LOGMASKED(LOG_CARD_B_READ, "card4x_read[%08x]: %04x\n", offset << 1, data);
	return data;
}

void iqunlim_state::card4x_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	LOGMASKED(LOG_CARD_B_WRITE, "card4x_write[%08x]: %04x\n", offset << 1, data);
}

uint16_t iqunlim_state::card5x_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t data = 0xffff;
	if (m_cart)
	{
		data = m_cart->read16_rom(offset, mem_mask);
	}
	LOGMASKED(LOG_CARD_A_READ, "card5x_read[%08x]: %04x\n", offset << 1, data);
	return data;
}

void iqunlim_state::card5x_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	LOGMASKED(LOG_CARD_A_WRITE, "card5x_write[%08x]: %04x\n", offset << 1, data);
}

static INPUT_PORTS_START( iqunlim )
INPUT_PORTS_END

DEVICE_IMAGE_LOAD_MEMBER(iqunlim_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_BIG);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}


void iqunlim_state::iqunlim(machine_config &config)
{
	// Basic machine hardware
	MC68EZ328(config, m_maincpu, 32768*506);
	m_maincpu->set_addrmap(AS_PROGRAM, &iqunlim_state::mem_map);

	m_maincpu->out_flm().set(m_lcdctrl, FUNC(mc68328_lcd_device::flm_w));
	m_maincpu->out_llp().set(m_lcdctrl, FUNC(mc68328_lcd_device::llp_w));
	m_maincpu->out_lsclk().set(m_lcdctrl, FUNC(mc68328_lcd_device::lsclk_w));
	m_maincpu->out_ld().set(m_lcdctrl, FUNC(mc68328_lcd_device::ld_w));
	m_maincpu->set_lcd_info_changed(m_lcdctrl, FUNC(mc68328_lcd_device::lcd_info_changed));

	RAM(config, RAM_TAG).set_default_size("2M");

	// Video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(480, 260);
	m_screen->set_visarea(0, 480 - 1, 0, 260 - 1);
	m_screen->set_screen_update(FUNC(iqunlim_state::screen_update));

	MC68328_LCD(config, m_lcdctrl, 0);

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "iqunlim_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(iqunlim_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("iqunlim_cart");
}

ROM_START( iqunlim )
	ROM_REGION16_BE(0x400000, "ipl", 0)
	ROM_LOAD16_WORD_SWAP( "27-06122-006.bin", 0x000000, 0x400000, CRC(811b1b19) SHA1(bac99ce408ed0a3b6449db88b363293b46ce69b9) )
ROM_END

ROM_START( iqunlimgr )
	ROM_REGION16_BE(0x400000, "ipl", 0)
	ROM_LOAD16_WORD_SWAP( "27-06126-007.bin", 0x000000, 0x400000, CRC(2e99cfef) SHA1(790869ffcf7fd666def8ff57fce0691062b3cec5) )
ROM_END

} // anonymous namespace

COMP( 1995, iqunlim,         0, 0, iqunlim, iqunlim, iqunlim_state, empty_init, "VTech / Integrated Systems Inc.", "IQ Unlimited",           MACHINE_NO_SOUND | MACHINE_NOT_WORKING) // COPYRIGHT 1995 INTERGRATED SYSTEMS, INC.
COMP( 1995, iqunlimgr, iqunlim, 0, iqunlim, iqunlim, iqunlim_state, empty_init, "VTech / Integrated Systems Inc.", "IQ Unlimited (Germany)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING) // COPYRIGHT 1995 INTERGRATED SYSTEMS, INC.



iris_power.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/**********************************************************************

    SGI IRIS POWER Series skeleton driver

    To Do: Everything

    Memory map:
    1fc00000 - 1fc1ffff      Boot ROM

    POWER Series models

    Year  Model   Board  CPU    FPU    Clock    I/D Cache
    1988  4D/120  IP5    R2000  R2010  16.7MHz  64KiB/64KiB
          4D/210  IP9    R3000  R3010  25MHz    64KiB/64KiB
          4D/2x0  IP7    R3000  R3010  25MHz    64KiB/64KiB
          4D/3x0  IP7    R3000  R3010  33MHz    64KiB/64KiB
    1991  4D/4x0  IP15   R3000  R3010  40MHz    64KiB/64KiB

    The second digit indicates the number of CPU boards and/or
    CPUs; models with an 'x' support configurations of 1, 2, 4
    or 8 CPU boards.

**********************************************************************/

#include "emu.h"
#include "cpu/mips/mips1.h"

#define LOG_UNKNOWN     (1U << 1)
#define LOG_ALL         (LOG_UNKNOWN)

#define VERBOSE         (0)
#include "logmacro.h"


namespace {

class ip15_state : public driver_device
{
public:
	ip15_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void ip15(machine_config &config);

protected:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<r3000_device> m_maincpu;
};

void ip15_state::mem_map(address_map &map)
{
	map(0x1fc00000, 0x1fc1ffff).rom().region("user1", 0);
}

static INPUT_PORTS_START( ip15 )
INPUT_PORTS_END

void ip15_state::ip15(machine_config &config)
{
	R3000(config, m_maincpu, 40_MHz_XTAL, 65536, 65536);
	m_maincpu->set_addrmap(AS_PROGRAM, &ip15_state::mem_map);
}

ROM_START( 4d410 )
	ROM_REGION32_BE( 0x20000, "user1", 0 )
	ROMX_LOAD( "ip15prom.070-040x-008.bin", 0x000000, 0x20000, CRC(7290eb66) SHA1(af4285e8db2a9b44fd0fb8a1df4f92faffe87e45), ROM_GROUPDWORD )
ROM_END

} // anonymous namespace


//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY                 FULLNAME  FLAGS
COMP( 1991, 4d410, 0,      0,      ip15,    ip15,  ip15_state, empty_init, "Silicon Graphics Inc", "4D/410", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



iris3130.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/****************************************************************************

    SGI IRIS 3130 skeleton driver

        0x00000000 - ?              RAM (?)
        0x30000000 - 0x30017fff     ROM (3x32k)
        0x30800000 - 0x30800000     Mouse Buttons (1)
        0x31000000 - 0x31000001     Mouse Quadrature (2)
        0x31800000 - 0x31800001     DIP Switches
        0x32000000 - 0x3200000f     DUART0 (serial console on channel B at 19200 baud 8N1, channel A set to 600 baud 8N1 (mouse?))
        0x32800000 - 0x3280000f     DUART1 (printer/modem?)
        0x33000000 - 0x330007ff     SRAM (2k)
        0x34000000 - 0x34000000     Clock Control (1)
        0x35000000 - 0x35000000     Clock Data (1)
        0x36000000 - 0x36000000     Kernel Base (1)
        0x38000000 - 0x38000001     Status Register (2)
        0x39000000 - 0x39000000     Parity control (1)
        0x3a000000 - 0x3a000000     Multibus Protection (1)
        0x3b000000 - 0x3b000003     Page Table Map Base (4)
        0x3c000000 - 0x3c000001     Text/Data Base (2)
        0x3d000000 - 0x3d000001     Text/Data Limit (2)
        0x3e000000 - 0x3e000001     Stack Base (2)
        0x3f000000 - 0x3f000001     Stack Limit (2)

    TODO:
        Most everything

    Interrupts:
        M68K:
            6 - DUART

****************************************************************************/

#include "emu.h"

#include "imagedev/snapquik.h"

#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
#include "cpu/m68000/m68020.h"
#include "machine/bankdev.h"
#include "machine/mc146818.h"
#include "machine/mc68681.h"

#include <vector>

#define LOG_RTC             (1U << 1)
#define LOG_INVALID_SEGMENT (1U << 2)
#define LOG_OTHER           (1U << 3)

#define VERBOSE     (0)

#include "logmacro.h"


namespace {

class iris3000_state : public driver_device
{
public:
	iris3000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_storagercpu(*this, "storager"),
		m_mainram(*this, "mainram"),
		m_duarta(*this, "duart68681a"),
		m_duartb(*this, "duart68681b"),
		m_text_data_space(*this, "text_data"),
		m_stack_space(*this, "stack"),
		m_kernel_space(*this, "kernel"),
		m_system_space(*this, "system"),
		m_multibus_mem_space(*this, "multibus_mem"),
		m_multibus_io_space(*this, "multibus_io"),
		m_geometry_pipe_space(*this, "geometry_pipe"),
		m_fpa_space(*this, "fpa"),
		m_page_table_map(*this, "ptmap"),
		m_rtc(*this, "rtc"),
		m_dips(*this, "DIPS")
	{
	}

	void iris3130(machine_config &config);

	DECLARE_QUICKLOAD_LOAD_MEMBER(load_romboard);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void text_data_map(address_map &map) ATTR_COLD;
	void stack_map(address_map &map) ATTR_COLD;
	void kernel_map(address_map &map) ATTR_COLD;
	void system_map(address_map &map) ATTR_COLD;
	void multibus_mem_map(address_map &map) ATTR_COLD;
	void multibus_io_map(address_map &map) ATTR_COLD;
	void geometry_pipe_map(address_map &map) ATTR_COLD;
	void fpa_map(address_map &map) ATTR_COLD;

	uint32_t mmu_r(offs_t offset, uint32_t mem_mask = ~0);
	void mmu_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);

	uint8_t mouse_buttons_r();
	void mouse_buttons_w(uint8_t data);
	uint16_t mouse_quad_r(offs_t offset, uint16_t mem_mask = ~0);
	void mouse_quad_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t dips_r(offs_t offset, uint16_t mem_mask = ~0);
	uint8_t clock_ctrl_r();
	void clock_ctrl_w(uint8_t data);
	uint8_t clock_data_r();
	void clock_data_w(uint8_t data);
	uint8_t kernel_base_r(offs_t offset);
	void kernel_base_w(offs_t offset, uint8_t data);
	uint16_t status_r(offs_t offset, uint16_t mem_mask = ~0);
	void status_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint8_t parity_ctrl_r();
	void parity_ctrl_w(uint8_t data);
	uint8_t multibus_prot_r();
	void multibus_prot_w(uint8_t data);
	uint32_t page_table_map_r(offs_t offset, uint32_t mem_mask = ~0);
	void page_table_map_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint16_t text_data_base_r(offs_t offset, uint16_t mem_mask = ~0);
	void text_data_base_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t text_data_limit_r(offs_t offset, uint16_t mem_mask = ~0);
	void text_data_limit_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t stack_base_r(offs_t offset, uint16_t mem_mask = ~0);
	void stack_base_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t stack_limit_r(offs_t offset, uint16_t mem_mask = ~0);
	void stack_limit_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint8_t romboard_r(offs_t offset);
	[[maybe_unused]] void romboard_w(offs_t offset, uint8_t data);
	void duarta_irq_handler(int state);
	void duartb_irq_handler(int state);

	void mem_map(address_map &map) ATTR_COLD;
	void storager_map(address_map &map) ATTR_COLD;

	required_device<m68020_device> m_maincpu;
	required_device<m68000_device> m_storagercpu;
	required_shared_ptr<uint32_t> m_mainram;
	required_device<mc68681_device> m_duarta;
	required_device<mc68681_device> m_duartb;

	required_device<address_map_bank_device> m_text_data_space;
	required_device<address_map_bank_device> m_stack_space;
	required_device<address_map_bank_device> m_kernel_space;
	required_device<address_map_bank_device> m_system_space;
	required_device<address_map_bank_device> m_multibus_mem_space;
	required_device<address_map_bank_device> m_multibus_io_space;
	required_device<address_map_bank_device> m_geometry_pipe_space;
	required_device<address_map_bank_device> m_fpa_space;

	required_shared_ptr<uint32_t> m_page_table_map;
	required_device<mc146818_device> m_rtc;
	required_ioport m_dips;

	enum
	{
		MOUSE_BUTTON_RIGHT      = 0x01,
		MOUSE_BUTTON_MIDDLE     = 0x02,
		MOUSE_BUTTON_LEFT       = 0x04,
		BOARD_REV1              = 0x60, /* Board revision - #1 */
		BOARD_REV2              = 0x50, /* Board revision - #2 */

		MOUSE_XFIRE     = 0x01, /* X Quadrature Fired, active low */
		MOUSE_XCHANGE   = 0x02, /* MOUSE_XCHANGE ? x-- : x++ */
		MOUSE_YFIRE     = 0x04, /* Y Quadrature Fired, active low */
		MOUSE_YCHANGE   = 0x08, /* MOUSE_YCHANGE ? y-- : y++ */

		PAR_UR      = 0x01, /* Check parity on user-mode reads */
		PAR_UW      = 0x02, /* Check parity on user-mode writes */
		PAR_KR      = 0x04, /* Check parity on kernel-mode reads */
		PAR_KW      = 0x08, /* Check parity on kernel-mode writes */
		PAR_DIS0    = 0x10, /* Disable access to DUART0 and LEDs */
		PAR_DIS1    = 0x20, /* Disable access to DUART1 */
		PAR_MBR     = 0x40, /* Check parity on multibus reads */
		PAR_MBW     = 0x80, /* Check parity on multibus writes */

		MBP_DCACC   = 0x01, /* Display controller access (I/O page 4) */
		MBP_UCACC   = 0x02, /* Update controller access (I/O page 3) */
		MBP_GFACC   = 0x04, /* Allow GF access (I/O page 1) */
		MBP_DMACC   = 0x08, /* Allow GL2 DMA access (0x8nnnnn - x0bnnnnn) */
		MBP_LIOACC  = 0x10, /* Allow lower I/O access (0x0nnnnn - 0x7nnnnn) */
		MBP_HIOACC  = 0x20, /* Allow upper I/O access (0x8nnnnn - 0xfnnnnn) */
		MBP_LMACC   = 0x40, /* Allow lower memory access (0x0nnnnn - 0x7nnnnn) */
		MBP_HMACC   = 0x80, /* Allow upper memory access (0x8nnnnn - 0xfnnnnn) */

		STATUS_DIAG0        = 0,
		STATUS_DIAG1        = 1,
		STATUS_DIAG2        = 2,
		STATUS_DIAG3        = 3,
		STATUS_ENABEXT      = 4,
		STATUS_ENABINT      = 5,
		STATUS_BINIT        = 6,
		STATUS_NOTBOOT      = 7,
		STATUS_USERFPA      = 8,
		STATUS_USERGE       = 9,
		STATUS_SLAVE        = 10,
		STATUS_ENABCBRQ     = 11,
		STATUS_NOTGEMASTER  = 12,
		STATUS_GENBAD       = 13,
		STATUS_ENABWDOG     = 14,
		STATUS_QUICK_TOUT   = 15
	};

	std::vector<uint8_t> m_file_data;
	uint8_t m_mouse_buttons;
	uint16_t m_mouse_quadrature;
	uint16_t m_text_data_base;
	uint16_t m_text_data_limit;
	uint16_t m_stack_base;
	uint16_t m_stack_limit;
	uint16_t m_status;
	uint8_t m_parity_ctrl;
	uint8_t m_multibus_prot;
};

QUICKLOAD_LOAD_MEMBER(iris3000_state::load_romboard)
{
	m_file_data.resize(image.length());

	if (image.length() == 0 || image.fread(&m_file_data[0], image.length()) != image.length())
	{
		m_file_data.clear();
		return std::make_pair(image_error::UNSPECIFIED, std::string());
	}
	return std::make_pair(std::error_condition(), std::string());
}

/***************************************************************************
    MACHINE FUNCTIONS
***************************************************************************/

uint32_t iris3000_state::mmu_r(offs_t offset, uint32_t mem_mask)
{
	const uint8_t type = (offset >> 26) & 0xf;
	const uint32_t vaddr = offset & 0x03ffffff;

	const uint32_t page_offset = vaddr & 0x000003ff;
	const uint32_t page_index = (vaddr >> 10) & 0x3fff;
	const uint32_t phys_page = m_page_table_map[page_index] & 0x1fff;
	const uint32_t paddr = (phys_page << 10) | page_offset;

	uint32_t ret;
	switch (type)
	{
	case 0: // Text/Data Segment
		ret = m_text_data_space->read32(paddr, mem_mask);
		break;
	case 1: // Stack Segment
		ret = m_stack_space->read32(paddr, mem_mask);
		break;
	case 2: // Kernel Segment
		ret = m_kernel_space->read32(paddr, mem_mask);
		break;
	case 3: // System Segment
		ret = m_system_space->read32(vaddr, mem_mask);
		break;
	case 4: // Multibus Memory Segment
		ret = m_multibus_mem_space->read32(vaddr, mem_mask);
		break;
	case 5: // Multibus I/O Segment
		ret = m_multibus_io_space->read32(vaddr, mem_mask);
		break;
	case 6: // Geometry Pipe Segment
		ret = m_geometry_pipe_space->read32(vaddr, mem_mask);
		break;
	case 15: // Floating-Point Accelerator Segment
		ret = m_fpa_space->read32(vaddr, mem_mask);
		break;
	default: // Unused/Unknown Segment
		LOGMASKED(LOG_INVALID_SEGMENT, "%s: Invalid segment read: %08x & %08x\n", machine().describe_context(), offset << 2, mem_mask);
		ret = 0;
		break;
	}
	return ret;
}

void iris3000_state::mmu_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	const uint8_t type = (offset >> 26) & 0xf;
	const uint32_t vaddr = offset & 0x03ffffff;

	const uint32_t page_offset = vaddr & 0x000003ff;
	const uint32_t page_index = (vaddr >> 10) & 0x3fff;
	const uint32_t phys_page = m_page_table_map[page_index] & 0x1fff;
	const uint32_t paddr = (phys_page << 10) | page_offset;

	switch (type)
	{
	case 0: // Text/Data Segment
		m_text_data_space->write32(paddr, data, mem_mask);
		break;
	case 1: // Stack Segment
		m_stack_space->write32(paddr, data, mem_mask);
		break;
	case 2: // Kernel Segment
		m_kernel_space->write32(paddr, data, mem_mask);
		break;
	case 3: // System Segment
		m_system_space->write32(vaddr, data, mem_mask);
		break;
	case 4: // Multibus Memory Segment
		m_multibus_mem_space->write32(vaddr, data, mem_mask);
		break;
	case 5: // Multibus I/O Segment
		m_multibus_io_space->write32(vaddr, data, mem_mask);
		break;
	case 6: // Geometry Pipe Segment
		m_geometry_pipe_space->write32(vaddr, data, mem_mask);
		break;
	case 15: // Floating-Point Accelerator Segment
		m_fpa_space->write32(vaddr, data, mem_mask);
		break;
	default: // Unused/Unknown Segment
		LOGMASKED(LOG_INVALID_SEGMENT, "%s: Invalid segment write: %08x = %08x & %08x\n", machine().describe_context(), offset << 2, data, mem_mask);
		break;
	}
}

uint8_t iris3000_state::mouse_buttons_r()
{
	LOGMASKED(LOG_OTHER, "%s: mouse_buttons_r: %02x\n", machine().describe_context(), m_mouse_buttons | BOARD_REV1);
	return m_mouse_buttons | BOARD_REV1;
}

void iris3000_state::mouse_buttons_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: mouse_buttons_w (ignored): %02x\n", machine().describe_context(), data);
}

uint16_t iris3000_state::mouse_quad_r(offs_t offset, uint16_t mem_mask)
{
	LOGMASKED(LOG_OTHER, "%s: mouse_quad_r: %04x & %04x\n", machine().describe_context(), m_mouse_quadrature, mem_mask);
	return m_mouse_quadrature;
}

void iris3000_state::mouse_quad_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	LOGMASKED(LOG_OTHER, "%s: mouse_quad_w (ignored): %04x & %04x\n", machine().describe_context(), data, mem_mask);
}

uint16_t iris3000_state::dips_r(offs_t offset, uint16_t mem_mask)
{
	const uint16_t data = m_dips->read();
	LOGMASKED(LOG_OTHER, "%s: dips_r: %04x & %04x\n", machine().describe_context(), data, mem_mask);
	return data;
}

uint8_t iris3000_state::clock_ctrl_r()
{
	const uint8_t data = m_rtc->data_r(); // FIXME: really?
	LOGMASKED(LOG_RTC, "%s: clock_ctrl_r: %02x\n", machine().describe_context(), data);
	return data;
}

void iris3000_state::clock_ctrl_w(uint8_t data)
{
	LOGMASKED(LOG_RTC, "%s: clock_ctrl_w: %02x\n", machine().describe_context(), data);
	m_rtc->data_w(data); // FIXME: really?
}

uint8_t iris3000_state::clock_data_r()
{
	uint8_t data = m_rtc->get_address(); // FIXME: really?
	LOGMASKED(LOG_RTC, "%s: clock_data_r: %02x\n", machine().describe_context(), data);
	return data;
}

void iris3000_state::clock_data_w(uint8_t data)
{
	LOGMASKED(LOG_RTC, "%s: clock_data_w: %02x\n", machine().describe_context(), data);
	m_rtc->address_w(data); // FIXME: really?
}

uint8_t iris3000_state::kernel_base_r(offs_t offset)
{
	switch(offset)
	{
		default:
			LOGMASKED(LOG_OTHER, "%s: kernel_base_r: Unknown Register %08x\n", machine().describe_context(), 0x36000000 + offset);
			break;
	}
	return 0;
}

void iris3000_state::kernel_base_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
		default:
			LOGMASKED(LOG_OTHER, "%s: kernel_base_w: Unknown Register %08x = %02x\n", machine().describe_context(), 0x36000000 + offset, data);
			break;
	}
}

uint16_t iris3000_state::status_r(offs_t offset, uint16_t mem_mask)
{
	LOGMASKED(LOG_OTHER, "%s: status_r: %04x & %04x\n", machine().describe_context(), m_status, mem_mask);
	return m_status;
}

void iris3000_state::status_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	LOGMASKED(LOG_OTHER, "%s: status_w: %04x & %04x (DIAG:%x, ENABEXT:%d, ENABINT:%d, BINIT:%d, NOTBOOT:%d)\n", machine().describe_context(), data, mem_mask,
		data & 0xf, BIT(data, STATUS_ENABEXT), BIT(data, STATUS_ENABINT), BIT(data, STATUS_BINIT), BIT(data, STATUS_NOTBOOT));
	LOGMASKED(LOG_OTHER, "%s:                       (USERFPA:%d, USERGE:%d, SLAVE:%d, ENABCBRQ:%d)\n", machine().describe_context(),
		BIT(data, STATUS_USERFPA), BIT(data, STATUS_USERGE), BIT(data, STATUS_SLAVE), BIT(data, STATUS_ENABCBRQ));
	LOGMASKED(LOG_OTHER, "%s:                       (NOTGEMASTER:%d, GENBAD:%d, ENABWDOG:%d, QUICK_TOUT:%d)\n", machine().describe_context(),
		BIT(data, STATUS_NOTGEMASTER), BIT(data, STATUS_GENBAD), BIT(data, STATUS_ENABWDOG), BIT(data, STATUS_QUICK_TOUT));
	COMBINE_DATA(&m_status);
}

uint8_t iris3000_state::parity_ctrl_r()
{
	LOGMASKED(LOG_OTHER, "%s: parity_ctrl_r: %02x\n", m_parity_ctrl);
	return m_parity_ctrl;
}

void iris3000_state::parity_ctrl_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: parity_ctrl_w: %02x\n", machine().describe_context(), data);
	m_parity_ctrl = data;
}

uint8_t iris3000_state::multibus_prot_r()
{
	LOGMASKED(LOG_OTHER, "%s: multibus_prot_r: %02x\n", machine().describe_context(), m_multibus_prot);
	return m_multibus_prot;
}

void iris3000_state::multibus_prot_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: multibus_prot_w: %02x\n", machine().describe_context(), data);
	m_multibus_prot = data;
}

uint32_t iris3000_state::page_table_map_r(offs_t offset, uint32_t mem_mask)
{
	LOGMASKED(LOG_OTHER, "%s: page_table_map_r: %08x = %08x & %08x\n", machine().describe_context(), 0x3b000000 + (offset << 2), m_page_table_map[offset], mem_mask);
	return m_page_table_map[offset];
}

void iris3000_state::page_table_map_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOGMASKED(LOG_OTHER, "%s: page_table_map_w: %08x = %08x & %08x\n", machine().describe_context(), 0x3b000000 + (offset << 2), data, mem_mask);
	COMBINE_DATA(&m_page_table_map[offset]);
}

uint16_t iris3000_state::text_data_base_r(offs_t offset, uint16_t mem_mask)
{
	LOGMASKED(LOG_OTHER, "%s: text_data_base_r: %04x & %04x\n", machine().describe_context(), m_text_data_base, mem_mask);
	return m_text_data_base;
}

void iris3000_state::text_data_base_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	LOGMASKED(LOG_OTHER, "%s: text_data_base_w: %04x & %04x\n", machine().describe_context(), data, mem_mask);
	COMBINE_DATA(&m_text_data_base);
}


uint16_t iris3000_state::text_data_limit_r(offs_t offset, uint16_t mem_mask)
{
	LOGMASKED(LOG_OTHER, "%s: text_data_limit_r: %04x & %04x\n", machine().describe_context(), m_text_data_limit, mem_mask);
	return m_text_data_limit;
}

void iris3000_state::text_data_limit_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	LOGMASKED(LOG_OTHER, "%s: text_data_limit_w: %04x & %04x\n", machine().describe_context(), data, mem_mask);
	COMBINE_DATA(&m_text_data_limit);
}


uint16_t iris3000_state::stack_base_r(offs_t offset, uint16_t mem_mask)
{
	LOGMASKED(LOG_OTHER, "%s: stack_base_r: %04x & %04x\n", machine().describe_context(), m_stack_base, mem_mask);
	return m_stack_base;
}

void iris3000_state::stack_base_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	LOGMASKED(LOG_OTHER, "%s: stack_base_w: %04x & %04x\n", machine().describe_context(), data, mem_mask);
	COMBINE_DATA(&m_stack_base);
}


uint16_t iris3000_state::stack_limit_r(offs_t offset, uint16_t mem_mask)
{
	LOGMASKED(LOG_OTHER, "%s: stack_limit_r: %04x & %04x\n", machine().describe_context(), m_stack_limit, mem_mask);
	return m_stack_limit;
}

void iris3000_state::stack_limit_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	LOGMASKED(LOG_OTHER, "%s: stack_limit_w: %04x & %04x\n", machine().describe_context(), data, mem_mask);
	COMBINE_DATA(&m_stack_limit);
}

uint8_t iris3000_state::romboard_r(offs_t offset)
{
	const uint8_t data = offset < m_file_data.size() ? m_file_data[offset] : 0;
	LOGMASKED(LOG_OTHER, "%s: romboard_r: %08x = %02x\n", machine().describe_context(), 0x40000000 + offset, data);
	return data;
}

void iris3000_state::romboard_w(offs_t offset, uint8_t data)
{
	if (offset > m_file_data.size())
		m_file_data.resize(offset + 1);
	m_file_data[offset] = data;
	LOGMASKED(LOG_OTHER, "%s: romboard write: %08x = %02x\n", machine().describe_context(), 0x40000000 + offset, data);
}

void iris3000_state::machine_start()
{
	m_mouse_buttons = 0;
}

void iris3000_state::machine_reset()
{
	uint32_t *src = (uint32_t*)(memregion("maincpu")->base());
	uint32_t *dst = m_mainram;
	memcpy(dst, src, 8);
}

/***************************************************************************
    ADDRESS MAPS
***************************************************************************/

void iris3000_state::text_data_map(address_map &map)
{
	map(0x0000000, 0x03fffff).ram().share("mainram");
}

void iris3000_state::stack_map(address_map &map)
{
	map(0x0000000, 0x03fffff).ram().share("mainram");
}

void iris3000_state::kernel_map(address_map &map)
{
	map(0x0000000, 0x03fffff).ram().share("mainram");
}

void iris3000_state::system_map(address_map &map)
{
	map(0x0000000, 0x0017fff).rom().region("maincpu", 0);
	map(0x0800000, 0x0800003).rw(FUNC(iris3000_state::mouse_buttons_r), FUNC(iris3000_state::mouse_buttons_w));
	map(0x1000000, 0x1000003).rw(FUNC(iris3000_state::mouse_quad_r), FUNC(iris3000_state::mouse_quad_w));
	map(0x1800000, 0x1800003).r(FUNC(iris3000_state::dips_r));
	map(0x2000000, 0x200000f).rw(m_duarta, FUNC(mc68681_device::read), FUNC(mc68681_device::write));
	map(0x2800000, 0x280000f).rw(m_duartb, FUNC(mc68681_device::read), FUNC(mc68681_device::write));
	map(0x3000000, 0x30007ff).ram();
	map(0x4000000, 0x4000003).rw(FUNC(iris3000_state::clock_ctrl_r), FUNC(iris3000_state::clock_ctrl_w));
	map(0x5000000, 0x5000003).rw(FUNC(iris3000_state::clock_data_r), FUNC(iris3000_state::clock_data_w));
	map(0x6000000, 0x6000003).rw(FUNC(iris3000_state::kernel_base_r), FUNC(iris3000_state::kernel_base_w));
	map(0x8000000, 0x8000003).rw(FUNC(iris3000_state::status_r), FUNC(iris3000_state::status_w));
	map(0x9000000, 0x9000003).rw(FUNC(iris3000_state::parity_ctrl_r), FUNC(iris3000_state::parity_ctrl_w));
	map(0xa000000, 0xa000003).rw(FUNC(iris3000_state::multibus_prot_r), FUNC(iris3000_state::multibus_prot_w));
	map(0xb000000, 0xb00ffff).rw(FUNC(iris3000_state::page_table_map_r), FUNC(iris3000_state::page_table_map_w)).share("ptmap");
	map(0xc000000, 0xc000003).rw(FUNC(iris3000_state::text_data_base_r), FUNC(iris3000_state::text_data_base_w));
	map(0xd000000, 0xd000003).rw(FUNC(iris3000_state::text_data_limit_r), FUNC(iris3000_state::text_data_limit_w));
	map(0xe000000, 0xe000003).rw(FUNC(iris3000_state::stack_base_r), FUNC(iris3000_state::stack_base_w));
	map(0xf000000, 0xf000003).rw(FUNC(iris3000_state::stack_limit_r), FUNC(iris3000_state::stack_limit_w));
}

void iris3000_state::multibus_mem_map(address_map &map)
{
	map(0x0200000, 0x03fffff).r(FUNC(iris3000_state::romboard_r));
}

void iris3000_state::multibus_io_map(address_map &map)
{
}

void iris3000_state::geometry_pipe_map(address_map &map)
{
}

void iris3000_state::fpa_map(address_map &map)
{
}

void iris3000_state::mem_map(address_map &map)
{
	map(0x00000000, 0xffffffff).rw(FUNC(iris3000_state::mmu_r), FUNC(iris3000_state::mmu_w));
}

void iris3000_state::storager_map(address_map& map)
{
	map(0x00000000, 0x0000ffff).rom().region("storagercpu", 0);
}

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

void iris3000_state::duarta_irq_handler(int state)
{
	m_maincpu->set_input_line(M68K_IRQ_6, state);
}

void iris3000_state::duartb_irq_handler(int state)
{
	m_maincpu->set_input_line(M68K_IRQ_6, state);
}

static DEVICE_INPUT_DEFAULTS_START( ip2_terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_19200 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_19200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void iris3000_state::iris3130(machine_config &config)
{
	/* basic machine hardware */
	M68020(config, m_maincpu, 16000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &iris3000_state::mem_map);

	M68000(config, m_storagercpu, 10000000);
	m_storagercpu->set_addrmap(AS_PROGRAM, &iris3000_state::storager_map);

	ADDRESS_MAP_BANK(config, "text_data").set_map(&iris3000_state::text_data_map).set_options(ENDIANNESS_BIG, 32, 32);
	ADDRESS_MAP_BANK(config, "stack").set_map(&iris3000_state::stack_map).set_options(ENDIANNESS_BIG, 32, 32);
	ADDRESS_MAP_BANK(config, "kernel").set_map(&iris3000_state::kernel_map).set_options(ENDIANNESS_BIG, 32, 32);
	ADDRESS_MAP_BANK(config, "system").set_map(&iris3000_state::system_map).set_options(ENDIANNESS_BIG, 32, 32);
	ADDRESS_MAP_BANK(config, "multibus_mem").set_map(&iris3000_state::multibus_mem_map).set_options(ENDIANNESS_BIG, 32, 32);
	ADDRESS_MAP_BANK(config, "multibus_io").set_map(&iris3000_state::multibus_io_map).set_options(ENDIANNESS_BIG, 32, 32);
	ADDRESS_MAP_BANK(config, "geometry_pipe").set_map(&iris3000_state::geometry_pipe_map).set_options(ENDIANNESS_BIG, 32, 32);
	ADDRESS_MAP_BANK(config, "fpa").set_map(&iris3000_state::fpa_map).set_options(ENDIANNESS_BIG, 32, 32);

	MC68681(config, m_duarta, 3.6864_MHz_XTAL); /* Y3 3.6864MHz Xtal ??? copy-over from dectalk */
	m_duarta->irq_cb().set(FUNC(iris3000_state::duarta_irq_handler));
	m_duarta->b_tx_cb().set("rs232", FUNC(rs232_port_device::write_txd));

	MC68681(config, m_duartb, 3.6864_MHz_XTAL); /* Y3 3.6864MHz Xtal ??? copy-over from dectalk */
	m_duartb->irq_cb().set(FUNC(iris3000_state::duartb_irq_handler));

	MC146818(config, m_rtc, 4.194304_MHz_XTAL);

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_duarta, FUNC(mc68681_device::rx_b_w));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(ip2_terminal));

	quickload_image_device &quickload(QUICKLOAD(config, "quickload", ""));
	quickload.set_load_callback(FUNC(iris3000_state::load_romboard));
}

static INPUT_PORTS_START( iris3130 )
	PORT_START("DIPS")
	PORT_DIPNAME( 0x8000, 0x8000, "Master/Slave" )
	PORT_DIPSETTING(    0x0000, "Slave" )
	PORT_DIPSETTING(    0x8000, "Master" )
	PORT_BIT( 0x6000, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_DIPNAME( 0x1800, 0x1800, "RS232 Console Speed" )
	PORT_DIPSETTING(    0x0000, "9600 Baud" )
	PORT_DIPSETTING(    0x0800, "300 Baud" )
	PORT_DIPSETTING(    0x1000, "1200 Baud" )
	PORT_DIPSETTING(    0x1800, "19200 Baud" )
	PORT_DIPNAME( 0x0700, 0x0000, "Display Setting" )
	PORT_DIPSETTING(    0x0000, "60Hz Non-Interlaced / 60Hz Non-Interlaced" )
	PORT_DIPSETTING(    0x0100, "60Hz Non-Interlaced / 30Hz Interlaced" )
	PORT_DIPSETTING(    0x0200, "60Hz Non-Interlaced / NTSC RS 170A" )
	PORT_DIPSETTING(    0x0300, "60Hz Non-Interlaced / PAL" )
	PORT_DIPSETTING(    0x0400, "30Hz Interlaced / 60Hz Non-Interlaced" )
	PORT_DIPSETTING(    0x0500, "30Hz Interlaced / 30Hz Interlaced" )
	PORT_DIPSETTING(    0x0600, "30Hz Interlaced / NTSC RS 170A" )
	PORT_DIPSETTING(    0x0700, "30Hz Interlaced / PAL" )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_DIPNAME( 0x0040, 0x0000, "Enable Dual-Head Display" )
	PORT_DIPSETTING(    0x0000, "Use Primary Display" )
	PORT_DIPSETTING(    0x0040, "Use Secondary Display" )
	PORT_DIPNAME( 0x0020, 0x0000, "Verbose Boot" )
	PORT_DIPSETTING(    0x0000, "Be Verbose" )
	PORT_DIPSETTING(    0x0020, "Be Quiet" )
	PORT_DIPNAME( 0x0010, 0x0000, "Auto-Boot" )
	PORT_DIPSETTING(    0x0000, "Enter PROM Monitor" )
	PORT_DIPSETTING(    0x0010, "Auto-Boot" )
	PORT_DIPNAME( 0x000f, 0x0005, "Boot Media" )
	PORT_DIPSETTING(    0x0000, "Hard Disk (IP, SD, MD)" )
	PORT_DIPSETTING(    0x0001, "Cartridge Tape" )
	PORT_DIPSETTING(    0x0002, "Floppy Disk (SF, MF)" )
	PORT_DIPSETTING(    0x0003, "Ethernet using XNS" )
	PORT_DIPSETTING(    0x0005, "Enter PROM Monitor" )
	PORT_DIPSETTING(    0x0006, "Boot from PROM Board" )
	PORT_DIPSETTING(    0x0007, "TCP/UDP Netboot" )
	PORT_DIPSETTING(    0x0009, "Interphase SMD Disk Boot" )
	PORT_DIPSETTING(    0x000a, "Storager Tape Boot (1)" )
	PORT_DIPSETTING(    0x000b, "Storager Tape Boot (2)" )
	PORT_DIPSETTING(    0x000c, "Stoarger Hard Disk Boot" )
	PORT_DIPSETTING(    0x000d, "DSD Tape Boot (1)" )
	PORT_DIPSETTING(    0x000e, "DSD Tape Boot (2)" )
	PORT_DIPSETTING(    0x000f, "DSD Hard Disk Boot" )
INPUT_PORTS_END

/***************************************************************************

  ROM definition(s)

***************************************************************************/

ROM_START( iris3130 )
	ROM_REGION32_BE(0x18000, "maincpu", 0)
	ROM_LOAD( "sgi-ip2-u91.nolabel.od",    0x00000, 0x8000, CRC(32e1f6b5) SHA1(2bd928c3fe2e364b9a38189158e9bad0e5271a59) )
	ROM_LOAD( "sgi-ip2-u92.nolabel.od",    0x08000, 0x8000, CRC(13dbfdb3) SHA1(3361fb62f7a8c429653700bccfc3e937f7508182) )
	ROM_LOAD( "sgi-ip2-u93.ip2.2-008.od",  0x10000, 0x8000, CRC(bf967590) SHA1(1aac48e4f5531a25c5482f64de5cd3c7a9931f11) )

	ROM_REGION16_BE(0x10000, "storagercpu", 0)
	ROM_LOAD16_BYTE( "5808423a.bin", 0x0000, 0x8000, CRC(161e6a90) SHA1(d4dcbf630a83e4c5994d8331ac85d81130400e33) )
	ROM_LOAD16_BYTE( "5808523a.bin", 0x0001, 0x8000, CRC(4c99e4b8) SHA1(899855e54c4520816ad43eb19b972b45783ccb6b) )
ROM_END

} // Anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY                 FULLNAME           FLAGS
COMP( 1985, iris3130, 0,      0,      iris3130, iris3130, iris3000_state, empty_init, "Silicon Graphics Inc", "IRIS 3130 (IP2)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



irisha.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

Irisha driver by Miodrag Milanovic

2008-03-27 Preliminary driver.

Jump addresses:
  Option 1: 0800 (Monitor - the only choice that works)
  Option 2: 046E
  Option 3: 0423 (then jumps to 4000)
  Option 4: 0501
  Option 5: 042E

TODO:
- Fix options 2,3,4,5
- Other emulator has yellow text on blue background
- Need info on fdc (used in option 3) ports 20-2F,38
- Currently no way to load or save software, and does any exist?
- Other emulator has clones/variants with different menus

****************************************************************************/


#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "sound/spkrdev.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class irisha_state : public driver_device
{
public:
	irisha_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_p_videoram(*this, "videoram")
		, m_maincpu(*this, "maincpu")
		, m_pit(*this, "pit")
		, m_speaker(*this, "speaker")
		, m_keyboard(*this, "LINE%u", 0U)
	{ }

	void irisha(machine_config &config);

private:
	uint8_t keyboard_r();
	uint8_t portb_r();
	uint8_t portc_r();
	void porta_w(uint8_t data);
	void portb_w(uint8_t data);
	void portc_w(uint8_t data);
	void speaker_w(int state);
	TIMER_CALLBACK_MEMBER(irisha_key);
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	required_shared_ptr<uint8_t> m_p_videoram;

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	bool m_sg1_line = false;
	bool m_keypressed = false;
	uint8_t m_keyboard_cnt = 0;
	uint8_t m_ppi_porta = 0;
	uint8_t m_ppi_portc = 0;
	emu_timer *m_key_timer = nullptr;
	void update_speaker();
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_device<pit8253_device> m_pit;
	required_device<speaker_sound_device> m_speaker;
	required_ioport_array<10> m_keyboard;
};


/* Address maps */
void irisha_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	map(0x4000, 0xdfff).ram();
	map(0xe000, 0xffff).ram().share("videoram");
}

void irisha_state::io_map(address_map &map)
{
	map(0x04, 0x05).r(FUNC(irisha_state::keyboard_r));
	map(0x06, 0x07).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x08, 0x0B).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x0C, 0x0F).rw("pic", FUNC(pic8259_device::read), FUNC(pic8259_device::write)).mask(0x01);
	map(0x10, 0x13).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
}

/* Input ports */
static INPUT_PORTS_START( irisha )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')

	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')

	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR(0xA6)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('_')

	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_F8)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Caps") PORT_CODE(KEYCODE_CAPSLOCK)

	PORT_START("LINE7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Back") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))

	PORT_START("LINE8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_0_PAD) //
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_0_PAD)

	PORT_START("LINE9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
INPUT_PORTS_END

/*************************************************

    Video

*************************************************/

uint32_t irisha_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint16_t ma=0;

	for (uint16_t y = 0; y < 200; y++)
	{
		uint16_t *p = &bitmap.pix(y);

		for (uint16_t x = ma; x < ma+40; x++)
		{
			uint8_t const gfx = m_p_videoram[x];

			/* Display a scanline of a character */
			*p++ = BIT(gfx, 7);
			*p++ = BIT(gfx, 6);
			*p++ = BIT(gfx, 5);
			*p++ = BIT(gfx, 4);
			*p++ = BIT(gfx, 3);
			*p++ = BIT(gfx, 2);
			*p++ = BIT(gfx, 1);
			*p++ = BIT(gfx, 0);
		}
		ma+=40;
	}
	return 0;
}

/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_irisha )
	GFXDECODE_ENTRY( "maincpu", 0x3800, charlayout, 0, 1 )
GFXDECODE_END


/*************************************************

    i8255

*************************************************/

uint8_t irisha_state::portb_r()
{
	if (m_keypressed==1)
	{
		m_keypressed = 0;
		return 0x80;
	}

	return 0x00;
}

uint8_t irisha_state::portc_r()
{
	logerror("irisha_8255_portc_r\n");
	return 0;
}

void irisha_state::porta_w(uint8_t data)
{
	logerror("irisha_8255_porta_w %02x\n",data);

	m_ppi_porta = data;

	update_speaker();
}

void irisha_state::portb_w(uint8_t data)
{
	logerror("irisha_8255_portb_w %02x\n",data);
}

void irisha_state::portc_w(uint8_t data)
{
	//logerror("irisha_8255_portc_w %02x\n",data);

	if (BIT(data, 6))
		m_pit->write_gate2((BIT(m_ppi_porta, 5) && !BIT(data, 5)) ? 1 : 0);

	m_ppi_portc = data;

	update_speaker();
}


/*************************************************

    Sound

*************************************************/

void irisha_state::update_speaker()
{
	int level = ( (BIT(m_ppi_portc, 5)) || (BIT(m_ppi_porta, 4)) || !m_sg1_line) ? 1 : 0;

	m_speaker->level_w(level);
}


void irisha_state::speaker_w(int state)
{
	m_sg1_line = state;
	update_speaker();
}


/*************************************************

    Keyboard

*************************************************/

TIMER_CALLBACK_MEMBER(irisha_state::irisha_key)
{
	m_keypressed = 1;
	m_keyboard_cnt = 0;
}

uint8_t irisha_state::keyboard_r()
{
	uint8_t keycode = 0xff;

	if (m_keyboard_cnt!=0 && m_keyboard_cnt<11)
		keycode = m_keyboard[m_keyboard_cnt-1]->read() ^ 0xff;

	m_keyboard_cnt++;

	return keycode;
}


/*************************************************

    Machine

*************************************************/

void irisha_state::machine_start()
{
	m_key_timer = timer_alloc(FUNC(irisha_state::irisha_key), this);
	m_key_timer->adjust(attotime::from_msec(30), 0, attotime::from_msec(30));
	save_item(NAME(m_sg1_line));
	save_item(NAME(m_keypressed));
	save_item(NAME(m_keyboard_cnt));
	save_item(NAME(m_ppi_porta));
	save_item(NAME(m_ppi_portc));
}

void irisha_state::machine_reset()
{
	m_sg1_line = 0;
	m_keypressed = 0;
	m_keyboard_cnt = 0;
	m_ppi_porta = 0;
	m_ppi_portc = 0;
}

/* Machine driver */
void irisha_state::irisha(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, XTAL(16'000'000) / 9);
	m_maincpu->set_addrmap(AS_PROGRAM, &irisha_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &irisha_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(320, 200);
	screen.set_visarea(0, 320-1, 0, 200-1);
	screen.set_screen_update(FUNC(irisha_state::screen_update));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_irisha);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* Devices */
	I8251(config, "uart", 0);

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(16_MHz_XTAL / 9);
	m_pit->out_handler<0>().set("pic", FUNC(pic8259_device::ir0_w));
	m_pit->set_clk<1>(16_MHz_XTAL / 9 / 8 / 8);
	m_pit->out_handler<1>().set("uart", FUNC(i8251_device::write_txc));
	m_pit->out_handler<1>().append("uart", FUNC(i8251_device::write_rxc));
	m_pit->set_clk<2>(16_MHz_XTAL / 9);
	m_pit->out_handler<2>().set(FUNC(irisha_state::speaker_w));

	i8255_device &ppi(I8255(config, "ppi"));
	ppi.out_pa_callback().set(FUNC(irisha_state::porta_w));
	ppi.in_pb_callback().set(FUNC(irisha_state::portb_r));
	ppi.out_pb_callback().set(FUNC(irisha_state::portb_w));
	ppi.in_pc_callback().set(FUNC(irisha_state::portc_r));
	ppi.out_pc_callback().set(FUNC(irisha_state::portc_w));

	pic8259_device &pic8259(PIC8259(config, "pic", 0));
	pic8259.out_int_callback().set_inputline(m_maincpu, 0);
}

/* ROM definition */

ROM_START( irisha )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "ir_bootm.bin", 0x0000, 0x2000, CRC(7f9f4f0e) SHA1(05f97e1a1d7a15f4451129dba6c0bddc87ea748e))
	ROM_LOAD( "ir_conou.bin", 0x2000, 0x2000, CRC(bf92beed) SHA1(696c482ba53bc6261db11061ecc7141c67f1d820))
ROM_END

} // anonymous namespace


/* Driver */
//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY  FULLNAME  FLAGS
COMP( 1983, irisha,      0,      0, irisha,  irisha, irisha_state, empty_init, "MGU",   "Irisha", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



is48x.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

    Skeleton driver for Decision Data IS-48x/LM-48xC IBM-compatible coax workstation displays.

***********************************************************************************************************************************/

#include "emu.h"
#include "cpu/i86/i186.h"
#include "cpu/bcp/dp8344.h"
#include "machine/eeprompar.h"
#include "video/mc6845.h"
#include "screen.h"


namespace {

class is48x_state : public driver_device
{
public:
	is48x_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_bcp(*this, "bcp")
		, m_crtc(*this, "crtc")
	{ }

	void is482(machine_config &config);

private:
	MC6845_UPDATE_ROW(update_row);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void bcp_inst_map(address_map &map) ATTR_COLD;
	void bcp_data_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<dp8344_device> m_bcp;
	required_device<mc6845_device> m_crtc;
};

MC6845_UPDATE_ROW(is48x_state::update_row)
{
}

void is48x_state::mem_map(address_map &map)
{
	map(0x00000, 0x07fff).ram(); // W24257S-70LL
	map(0x40000, 0x47fff).rw(m_bcp, FUNC(dp8344_device::remote_read), FUNC(dp8344_device::remote_write));
	map(0x50000, 0x51fff).ram(); // CY7C185-35VC
	map(0x54000, 0x55fff).ram(); // CY7C185-35VC
	map(0x60000, 0x67fff).rw("eeprom", FUNC(eeprom_parallel_28xx_device::read), FUNC(eeprom_parallel_28xx_device::write));
	map(0x80000, 0xfffff).rom().region("program", 0);
}

void is48x_state::io_map(address_map &map)
{
	map(0x8005, 0x8005).nopw();
	map(0x8080, 0x8080).w(m_crtc, FUNC(mc6845_device::address_w));
	map(0x8081, 0x8081).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x8101, 0x8101).nopr();
	map(0x8180, 0x8180).rw(m_bcp, FUNC(dp8344_device::cmd_r), FUNC(dp8344_device::cmd_w));
}

void is48x_state::bcp_inst_map(address_map &map)
{
	map(0x0000, 0x1fff).ram(); // CY7C185-35VC x2
}

void is48x_state::bcp_data_map(address_map &map)
{
	map(0x0000, 0x7fff).ram(); // W24257S-70LL
	map(0xc000, 0xffff).ram();
}

static INPUT_PORTS_START(is482)
INPUT_PORTS_END

void is48x_state::is482(machine_config &config)
{
	I80188(config, m_maincpu, 16_MHz_XTAL); // N80C188-16
	m_maincpu->set_addrmap(AS_PROGRAM, &is48x_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &is48x_state::io_map);

	DP8344B(config, m_bcp, 18.867_MHz_XTAL); // DP8344BV
	m_bcp->set_auto_start(false);
	m_bcp->set_addrmap(AS_PROGRAM, &is48x_state::bcp_inst_map);
	m_bcp->set_addrmap(AS_DATA, &is48x_state::bcp_data_map);

	EEPROM_28256(config, "eeprom"); // AT28C256

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(60_MHz_XTAL / 2, 770, 0, 560, 532, 0, 475); // FIXME: vertical rate is supposed to be 75 Hz
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	HD6845S(config, m_crtc, 60_MHz_XTAL / 28); // HD46505SP-1
	m_crtc->set_char_width(14); // guess
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_update_row_callback(FUNC(is48x_state::update_row));
}

ROM_START(is482) // "IS-488-A" on case
	ROM_REGION(0x80000, "program", 0)
	ROM_LOAD("is-482_u67_s008533243.bin", 0x00000, 0x80000, CRC(1e23ac17) SHA1(aadc73bc0454c5b1c33d440dc511009dc6b7f9e0)) // M27C4001-10FI
ROM_END

} // anonymous namespace


COMP(199?, is482, 0, 0, is482, is482, is48x_state, empty_init, "Decision Data", "IS-482 Workstation", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



isbc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

        Intel iSBC series

        09/12/2009 Skeleton driver.

Notes:

isbc86 commands: BYTE WORD REAL EREAL ROMTEST. ROMTEST works, the others hang.

Press capital-U to drop into the monitor on the isbc 86/05 and 86/30
The 86/05 can boot floppies with the b command but appears to mostly be
able to deal with 256byte sectors so fails to load the irmx 512byte sector images.

****************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/i86/i86.h"
#include "cpu/i86/i286.h"
#include "machine/74259.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/i8255.h"
#include "machine/i8251.h"
#include "machine/z80sio.h"
#include "bus/centronics/ctronics.h"
#include "bus/isbx/isbx.h"
#include "isbc_215g.h"
#include "isbc_208.h"


namespace {

class isbc_state : public driver_device
{
public:
	isbc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_uart8251(*this, "uart8251")
		, m_uart8274(*this, "uart8274")
		, m_pic_0(*this, "pic_0")
		, m_pic_1(*this, "pic_1")
		, m_centronics(*this, "centronics")
		, m_cent_status_in(*this, "cent_status_in")
		, m_statuslatch(*this, "statuslatch")
		, m_bios(*this, "bios")
		, m_biosram(*this, "biosram")
		, m_sbx(*this, "sbx%u", 1U)
	{ }

	void isbc2861(machine_config &config);
	void isbc86(machine_config &config);
	void rpc86(machine_config &config);
	void isbc8605(machine_config &config);
	void isbc286(machine_config &config);
	void isbc8630(machine_config &config);
	void sm1810(machine_config &config);

private:
	void write_centronics_ack(int state);

	void isbc86_tmr2_w(int state);
	void isbc286_tmr2_w(int state);
//  void isbc_uart8274_irq(int state);
	uint8_t get_slave_ack(offs_t offset);
	void ppi_c_w(uint8_t data);
	void upperen_w(uint8_t data);
	uint16_t bioslo_r(offs_t offset);
	void bioslo_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

	void edge_intr_clear_w(uint8_t data);
	void status_register_w(uint8_t data);
	void nmi_mask_w(int state);
	void bus_intr_out1_w(int state);
	void bus_intr_out2_w(int state);
	void isbc2861_mem(address_map &map) ATTR_COLD;
	void isbc286_io(address_map &map) ATTR_COLD;
	void isbc286_mem(address_map &map) ATTR_COLD;
	void isbc8605_io(address_map &map) ATTR_COLD;
	void isbc8630_io(address_map &map) ATTR_COLD;
	void isbc86_mem(address_map &map) ATTR_COLD;
	void isbc_io(address_map &map) ATTR_COLD;
	void rpc86_io(address_map &map) ATTR_COLD;
	void rpc86_mem(address_map &map) ATTR_COLD;
	void sm1810_mem(address_map &map) ATTR_COLD;
	void sm1810_io(address_map &map) ATTR_COLD;

	virtual void machine_reset() override ATTR_COLD;

	static void cfg_fdc_qd(device_t *device);

	required_device<cpu_device> m_maincpu;
	optional_device<i8251_device> m_uart8251;
//  optional_device<i8274_device> m_uart8274;
	optional_device<i8274_device> m_uart8274;
	required_device<pic8259_device> m_pic_0;
	optional_device<pic8259_device> m_pic_1;
	optional_device<centronics_device> m_centronics;
	optional_device<input_buffer_device> m_cent_status_in;
	optional_device<ls259_device> m_statuslatch;
	optional_memory_region m_bios;
	optional_shared_ptr<u16> m_biosram;
	optional_device_array<isbx_slot_device, 2> m_sbx;

	bool m_upperen = 0;
	offs_t m_megabyte_page = 0;
	bool m_nmi_enable = 0;
	bool m_override = 0;
	bool m_megabyte_enable = 0;
};

void isbc_state::machine_reset()
{
	if(m_centronics)
	{
		m_centronics->write_busy(0);  // centronics_device sets busy to 1 at reset causing spurious irqs
		m_pic_1->ir7_w(0);
	}
	if(m_uart8251)
		m_uart8251->write_cts(0);
	m_upperen = false;
	m_megabyte_page = 0;
}

void isbc_state::rpc86_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0xcffff).ram();
	map(0xf8000, 0xfffff).rom().region("bios", 0);
}

void isbc_state::rpc86_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0080, 0x008f).rw("sbx1", FUNC(isbx_slot_device::mcs0_r), FUNC(isbx_slot_device::mcs0_w)).umask16(0x00ff);
	map(0x0090, 0x009f).rw("sbx1", FUNC(isbx_slot_device::mcs1_r), FUNC(isbx_slot_device::mcs1_w)).umask16(0x00ff);
	map(0x00a0, 0x00af).rw("sbx2", FUNC(isbx_slot_device::mcs0_r), FUNC(isbx_slot_device::mcs0_w)).umask16(0x00ff);
	map(0x00b0, 0x00bf).rw("sbx2", FUNC(isbx_slot_device::mcs1_r), FUNC(isbx_slot_device::mcs1_w)).umask16(0x00ff);
	map(0x00c0, 0x00c3).rw(m_pic_0, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x00c4, 0x00c7).rw(m_pic_0, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x00c8, 0x00cf).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
	map(0x00d0, 0x00d7).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0x00d8, 0x00db).rw(m_uart8251, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0x00dc, 0x00df).rw(m_uart8251, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
}

void isbc_state::isbc8605_io(address_map &map)
{
	rpc86_io(map);
	map(0x0000, 0x002f).m("isbc_208", FUNC(isbc_208_device::map));
}

void isbc_state::isbc8630_io(address_map &map)
{
	rpc86_io(map);
	map(0x00c0, 0x00c7).w(FUNC(isbc_state::edge_intr_clear_w)).umask16(0xff00);
	map(0x00c8, 0x00df).w(FUNC(isbc_state::status_register_w)).umask16(0xff00);
	map(0x0100, 0x0100).w("isbc_215g", FUNC(isbc_215g_device::write));
}

void isbc_state::sm1810_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0xeffff).ram();
	map(0xf8000, 0xfffff).rom().region("bios", 0);
}

void isbc_state::sm1810_io(address_map &map)
{
	rpc86_io(map);
	map(0x00c0, 0x00c7).w(FUNC(isbc_state::edge_intr_clear_w)).umask16(0xff00);
	map(0x00c8, 0x00df).w(FUNC(isbc_state::status_register_w)).umask16(0xff00);
	map(0x00ca, 0x00cb).lr8(NAME([]() { return 0x40; })).umask16(0x00ff); // it reads this without configuring the ppi
	map(0xefe0, 0xefe0).w("isbc_215g", FUNC(isbc_215g_device::write));
}

void isbc_state::isbc86_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0xfbfff).ram();
	map(0xfc000, 0xfffff).rom().region("bios", 0);
}

void isbc_state::isbc_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00c0, 0x00c3).rw(m_pic_0, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x00c4, 0x00c7).rw(m_pic_0, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x00c8, 0x00cf).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
	map(0x00d0, 0x00d7).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0x00d8, 0x00db).rw(m_uart8251, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0x00dc, 0x00df).rw(m_uart8251, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
}

void isbc_state::isbc286_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0080, 0x008f).rw("sbx1", FUNC(isbx_slot_device::mcs0_r), FUNC(isbx_slot_device::mcs0_w)).umask16(0x00ff);
	map(0x0080, 0x008f).rw("sbx1", FUNC(isbx_slot_device::mcs0_r), FUNC(isbx_slot_device::mcs0_w)).umask16(0xff00);
	map(0x0090, 0x009f).rw("sbx1", FUNC(isbx_slot_device::mcs1_r), FUNC(isbx_slot_device::mcs1_w)).umask16(0x00ff);
	map(0x0090, 0x009f).rw("sbx1", FUNC(isbx_slot_device::mcs1_r), FUNC(isbx_slot_device::mcs1_w)).umask16(0xff00);
	map(0x00a0, 0x00af).rw("sbx2", FUNC(isbx_slot_device::mcs0_r), FUNC(isbx_slot_device::mcs0_w)).umask16(0x00ff);
	map(0x00a0, 0x00af).rw("sbx2", FUNC(isbx_slot_device::mcs0_r), FUNC(isbx_slot_device::mcs0_w)).umask16(0xff00);
	map(0x00b0, 0x00bf).rw("sbx2", FUNC(isbx_slot_device::mcs1_r), FUNC(isbx_slot_device::mcs1_w)).umask16(0x00ff);
	map(0x00b0, 0x00bf).rw("sbx2", FUNC(isbx_slot_device::mcs1_r), FUNC(isbx_slot_device::mcs1_w)).umask16(0xff00);
	map(0x00c0, 0x00c3).rw(m_pic_0, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x00c4, 0x00c7).rw(m_pic_1, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x00c8, 0x00cf).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
	map(0x00c8, 0x00cf).w(FUNC(isbc_state::upperen_w)).umask16(0xff00);
	map(0x00d0, 0x00d7).rw("pit", FUNC(pit8254_device::read), FUNC(pit8254_device::write)).umask16(0x00ff);
	map(0x00d8, 0x00df).rw(m_uart8274, FUNC(i8274_device::cd_ba_r), FUNC(i8274_device::cd_ba_w)).umask16(0x00ff);
	map(0x0100, 0x0100).w("isbc_215g", FUNC(isbc_215g_device::write));
}

void isbc_state::isbc286_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0xdffff).ram();
	map(0xe0000, 0xfffff).rom().region("bios", 0);
	map(0xfe0000, 0xffffff).rom().region("bios", 0);
}

void isbc_state::isbc2861_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0xdffff).ram();
	map(0xe0000, 0xfffff).rw(FUNC(isbc_state::bioslo_r), FUNC(isbc_state::bioslo_w)).share("biosram");
//  map(0x100000, 0x1fffff).ram(); // FIXME: XENIX doesn't like this, IRMX is okay with it
	map(0xff0000, 0xffffff).rom().region("bios", 0);
}

/* Input ports */
static INPUT_PORTS_START( isbc )
INPUT_PORTS_END

static DEVICE_INPUT_DEFAULTS_START( isbc86_terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_300 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_300 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END

static DEVICE_INPUT_DEFAULTS_START( isbc286_terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void isbc_state::isbc86_tmr2_w(int state)
{
	m_uart8251->write_rxc(state);
	m_uart8251->write_txc(state);
}

uint8_t isbc_state::get_slave_ack(offs_t offset)
{
	if (offset == 7)
		return m_pic_1->acknowledge();

	return 0x00;
}

void isbc_state::isbc286_tmr2_w(int state)
{
	m_uart8274->rxca_w(state);
	m_uart8274->txca_w(state);
}

void isbc_state::write_centronics_ack(int state)
{
	m_cent_status_in->write_bit4(state);

	if(state)
		m_pic_1->ir7_w(1);
}

void isbc_state::ppi_c_w(uint8_t data)
{
	m_centronics->write_strobe(data & 1);

	if(data & 0x80)
		m_pic_1->ir7_w(0);
}

void isbc_state::upperen_w(uint8_t data)
{
	m_upperen = true;
}

uint16_t isbc_state::bioslo_r(offs_t offset)
{
	if(m_upperen)
		return m_biosram[offset];
	else if(offset >= 0x8000)
		return m_bios->as_u16(offset - 0x8000);
	return 0xffff;
}

void isbc_state::bioslo_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if(m_upperen)
		COMBINE_DATA(&m_biosram[offset]);
}

#if 0
void isbc_state::isbc_uart8274_irq(int state)
{
	m_uart8274->m1_r(); // always set
	m_pic_0->ir6_w(state);
}
#endif

void isbc_state::edge_intr_clear_w(uint8_t data)
{
	// reset U32 flipflop
}

void isbc_state::status_register_w(uint8_t data)
{
	m_megabyte_page = (data & 0xf0) << 16;
	m_statuslatch->write_bit(data & 0x07, BIT(data, 3));
}

void isbc_state::nmi_mask_w(int state)
{
	// combined with NMI input by 74LS08 AND gate at U12
	m_nmi_enable = state;
}

void isbc_state::bus_intr_out1_w(int state)
{
	// Multibus interrupt request (active high)
}

void isbc_state::bus_intr_out2_w(int state)
{
	// Multibus interrupt request (active high)
}

void isbc_state::cfg_fdc_qd(device_t *device)
{
	dynamic_cast<device_slot_interface &>(*device->subdevice("u14:0")).set_default_option("525qd");
}

void isbc_state::isbc86(machine_config &config)
{
	/* basic machine hardware */
	I8086(config, m_maincpu, XTAL(5'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &isbc_state::isbc86_mem);
	m_maincpu->set_addrmap(AS_IO, &isbc_state::isbc_io);
	m_maincpu->set_irq_acknowledge_callback("pic_0", FUNC(pic8259_device::inta_cb));

	PIC8259(config, m_pic_0, 0);
	m_pic_0->out_int_callback().set_inputline(m_maincpu, 0);

	pit8253_device &pit(PIT8253(config, "pit", 0));
	pit.set_clk<0>(XTAL(22'118'400)/18);
	pit.out_handler<0>().set(m_pic_0, FUNC(pic8259_device::ir0_w));
	pit.set_clk<1>(XTAL(22'118'400)/18);
	pit.set_clk<2>(XTAL(22'118'400)/18);
	pit.out_handler<2>().set(FUNC(isbc_state::isbc86_tmr2_w));

	I8255A(config, "ppi");

	I8251(config, m_uart8251, 0);
	m_uart8251->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_uart8251->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	m_uart8251->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_uart8251->rxrdy_handler().set("pic_0", FUNC(pic8259_device::ir6_w));

	/* video hardware */
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_uart8251, FUNC(i8251_device::write_rxd));
	rs232.cts_handler().set(m_uart8251, FUNC(i8251_device::write_cts));
	rs232.dsr_handler().set(m_uart8251, FUNC(i8251_device::write_dsr));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(isbc86_terminal));
}

void isbc_state::rpc86(machine_config &config)
{
	/* basic machine hardware */
	I8086(config, m_maincpu, XTAL(5'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &isbc_state::rpc86_mem);
	m_maincpu->set_addrmap(AS_IO, &isbc_state::rpc86_io);
	m_maincpu->set_irq_acknowledge_callback("pic_0", FUNC(pic8259_device::inta_cb));

	PIC8259(config, m_pic_0, 0);
	m_pic_0->out_int_callback().set_inputline(m_maincpu, 0);

	pit8253_device &pit(PIT8253(config, "pit", 0));
	pit.set_clk<0>(XTAL(22'118'400)/18);
	pit.out_handler<0>().set(m_pic_0, FUNC(pic8259_device::ir2_w));
	pit.set_clk<1>(XTAL(22'118'400)/144);
	pit.set_clk<2>(XTAL(22'118'400)/18);
	pit.out_handler<2>().set(FUNC(isbc_state::isbc86_tmr2_w));

	I8255A(config, "ppi");

	I8251(config, m_uart8251, 0);
	m_uart8251->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_uart8251->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	m_uart8251->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_uart8251->rxrdy_handler().set("pic_0", FUNC(pic8259_device::ir6_w));
	m_uart8251->txrdy_handler().set("pic_0", FUNC(pic8259_device::ir7_w));

	/* video hardware */
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_uart8251, FUNC(i8251_device::write_rxd));
	//rs232.cts_handler().set(m_uart8251, FUNC(i8251_device::write_cts));
	rs232.dsr_handler().set(m_uart8251, FUNC(i8251_device::write_dsr));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(isbc286_terminal));

	ISBX_SLOT(config, m_sbx[0], 0, isbx_cards, nullptr);
	//m_sbx[0]->mintr0().set("pic_0", FUNC(pic8259_device::ir3_w));
	//m_sbx[0]->mintr1().set("pic_0", FUNC(pic8259_device::ir4_w));
	ISBX_SLOT(config, m_sbx[1], 0, isbx_cards, nullptr);
	//m_sbx[1]->mintr0().set("pic_0", FUNC(pic8259_device::ir5_w));
	//m_sbx[1]->mintr1().set("pic_0", FUNC(pic8259_device::ir6_w));
}

void isbc_state::isbc8605(machine_config &config)
{
	rpc86(config);

	m_maincpu->set_addrmap(AS_IO, &isbc_state::isbc8605_io);

	ISBC_208(config, "isbc_208", 0, m_maincpu).irq_callback().set(m_pic_0, FUNC(pic8259_device::ir5_w));
}

void isbc_state::isbc8630(machine_config &config)
{
	rpc86(config);

	m_maincpu->set_addrmap(AS_IO, &isbc_state::isbc8630_io);

	ISBC_215G(config, "isbc_215g", 0, 0x100, m_maincpu).irq_callback().set(m_pic_0, FUNC(pic8259_device::ir5_w));

	LS259(config, m_statuslatch); // U14
//  m_statuslatch->q_out_cb<0>().set("pit", FUNC(pit8253_device::write_gate0));
//  m_statuslatch->q_out_cb<1>().set("pit", FUNC(pit8253_device::write_gate1));
	m_statuslatch->q_out_cb<2>().set(FUNC(isbc_state::nmi_mask_w));
	m_statuslatch->q_out_cb<3>().set([this] (int state) { m_override = state; }); // 1 = access onboard dual-port RAM
	m_statuslatch->q_out_cb<4>().set(FUNC(isbc_state::bus_intr_out1_w));
	m_statuslatch->q_out_cb<5>().set(FUNC(isbc_state::bus_intr_out2_w));
	m_statuslatch->q_out_cb<5>().append_output("led0").invert(); // ds1
	m_statuslatch->q_out_cb<6>().set_output("led1").invert(); // ds3
	m_statuslatch->q_out_cb<7>().set([this] (int state) { m_megabyte_enable = !state; });
}

void isbc_state::isbc286(machine_config &config)
{
	/* basic machine hardware */
	I80286(config, m_maincpu, XTAL(16'000'000)/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &isbc_state::isbc286_mem);
	m_maincpu->set_addrmap(AS_IO, &isbc_state::isbc286_io);
	m_maincpu->set_irq_acknowledge_callback("pic_0", FUNC(pic8259_device::inta_cb));

	PIC8259(config, m_pic_0, 0);
	m_pic_0->out_int_callback().set_inputline(m_maincpu, 0);
	m_pic_0->in_sp_callback().set_constant(1);
	m_pic_0->read_slave_ack_callback().set(FUNC(isbc_state::get_slave_ack));

	PIC8259(config, m_pic_1, 0);
	m_pic_1->out_int_callback().set(m_pic_0, FUNC(pic8259_device::ir7_w));
	m_pic_1->in_sp_callback().set_constant(0);

	pit8254_device &pit(PIT8254(config, "pit", 0));
	pit.set_clk<0>(XTAL(22'118'400)/18);
	pit.out_handler<0>().set(m_pic_0, FUNC(pic8259_device::ir0_w));
	pit.set_clk<1>(XTAL(22'118'400)/18);
	pit.out_handler<1>().set(m_uart8274, FUNC(i8274_device::rxtxcb_w));
	pit.set_clk<2>(XTAL(22'118'400)/18);
	pit.out_handler<2>().set(FUNC(isbc_state::isbc286_tmr2_w));

	i8255_device &ppi(I8255A(config, "ppi"));
	ppi.out_pa_callback().set("cent_data_out", FUNC(output_latch_device::write));
	ppi.in_pb_callback().set(m_cent_status_in, FUNC(input_buffer_device::read));
	ppi.out_pc_callback().set(FUNC(isbc_state::ppi_c_w));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set(FUNC(isbc_state::write_centronics_ack));
	m_centronics->busy_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit7));
	m_centronics->fault_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit6));

	INPUT_BUFFER(config, m_cent_status_in, 0);

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

#if 0
	I8274(config, m_uart8274, XTAL(16'000'000)/4);
	m_uart8274->out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
	m_uart8274->out_dtra_callback().set("rs232a", FUNC(rs232_port_device::write_dtr));
	m_uart8274->out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts));
	m_uart8274->out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
	m_uart8274->out_dtrb_callback().set("rs232b", FUNC(rs232_port_device::write_dtr));
	m_uart8274->out_rtsb_callback().set("rs232b", FUNC(rs232_port_device::write_rts));
	m_uart8274->out_int_callback().set(FUNC(isbc_state::isbc_uart8274_irq));
#else
	I8274(config, m_uart8274, XTAL(16'000'000)/4);
	m_uart8274->out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
	m_uart8274->out_dtra_callback().set("rs232a", FUNC(rs232_port_device::write_dtr));
	m_uart8274->out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts));
	m_uart8274->out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
	m_uart8274->out_dtrb_callback().set("rs232b", FUNC(rs232_port_device::write_dtr));
	m_uart8274->out_rtsb_callback().set("rs232b", FUNC(rs232_port_device::write_rts));
//  m_uart8274->out_int_callback().set(FUNC(isbc_state::isbc_uart8274_irq));
	m_uart8274->out_int_callback().set(m_pic_0, FUNC(pic8259_device::ir6_w));
#endif

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_uart8274, FUNC(i8274_device::rxa_w));
	rs232a.dcd_handler().set(m_uart8274, FUNC(i8274_device::dcda_w));
	rs232a.cts_handler().set(m_uart8274, FUNC(i8274_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, "terminal"));
	rs232b.rxd_handler().set(m_uart8274, FUNC(i8274_device::rxb_w));
	rs232b.dcd_handler().set(m_uart8274, FUNC(i8274_device::dcdb_w));
	rs232b.cts_handler().set(m_uart8274, FUNC(i8274_device::ctsb_w));
	rs232b.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(isbc286_terminal));

	ISBX_SLOT(config, m_sbx[0], 0, isbx_cards, nullptr);
	m_sbx[0]->mintr0().set("pic_1", FUNC(pic8259_device::ir3_w));
	m_sbx[0]->mintr1().set("pic_1", FUNC(pic8259_device::ir4_w));
	ISBX_SLOT(config, m_sbx[1], 0, isbx_cards, nullptr);
	m_sbx[1]->mintr0().set("pic_1", FUNC(pic8259_device::ir5_w));
	m_sbx[1]->mintr1().set("pic_1", FUNC(pic8259_device::ir6_w));

	ISBC_215G(config, "isbc_215g", 0, 0x100, m_maincpu).irq_callback().set(m_pic_0, FUNC(pic8259_device::ir5_w));
}

void isbc_state::isbc2861(machine_config &config)
{
	isbc286(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &isbc_state::isbc2861_mem);
}

void isbc_state::sm1810(machine_config &config)
{
	rpc86(config);
	m_maincpu->set_clock(XTAL(4'000'000)); // calibrated clock to pass self test
	m_maincpu->set_addrmap(AS_PROGRAM, &isbc_state::sm1810_mem);
	m_maincpu->set_addrmap(AS_IO, &isbc_state::sm1810_io);

	m_uart8251->dtr_handler().set(m_uart8251, FUNC(i8251_device::write_dsr));
	isbc_215g_device &isbc215(ISBC_215G(config, "isbc_215g", 0, 0xefe0, m_maincpu));
	isbc215.irq_callback().set(m_pic_0, FUNC(pic8259_device::ir5_w));
	isbc215.subdevice<isbx_slot_device>("sbx2")->set_option_machine_config("fdc_218a", cfg_fdc_qd);

	LS259(config, m_statuslatch); // U14
//  m_statuslatch->q_out_cb<0>().set("pit", FUNC(pit8253_device::write_gate0));
//  m_statuslatch->q_out_cb<1>().set("pit", FUNC(pit8253_device::write_gate1));
	m_statuslatch->q_out_cb<2>().set(FUNC(isbc_state::nmi_mask_w));
	m_statuslatch->q_out_cb<3>().set([this] (int state) { m_override = state; }); // 1 = access onboard dual-port RAM
	m_statuslatch->q_out_cb<4>().set(FUNC(isbc_state::bus_intr_out1_w));
	m_statuslatch->q_out_cb<5>().set(FUNC(isbc_state::bus_intr_out2_w));
	m_statuslatch->q_out_cb<5>().append_output("led0").invert(); // ds1
	m_statuslatch->q_out_cb<6>().set_output("led1").invert(); // ds3
	m_statuslatch->q_out_cb<7>().set([this] (int state) { m_megabyte_enable = !state; });
}

/* ROM definition */
ROM_START( isbc86 )
	ROM_REGION16_LE( 0x4000, "bios", ROMREGION_ERASEFF )
	ROM_LOAD16_BYTE( "8612_2u.bin", 0x0001, 0x1000, CRC(84fa14cf) SHA1(783e1459ab121201fd49368d4bf769c1bab6447a))
	ROM_LOAD16_BYTE( "8612_2l.bin", 0x0000, 0x1000, CRC(922bda5f) SHA1(15743e69f3aba56425fa004d19b82ec20532fd72))
	ROM_LOAD16_BYTE( "8612_3u.bin", 0x2001, 0x1000, CRC(68d47c3e) SHA1(16c17f26b33daffa84d065ff7aefb581544176bd))
	ROM_LOAD16_BYTE( "8612_3l.bin", 0x2000, 0x1000, CRC(17f27ad2) SHA1(c3f379ac7d67dc4a0a7a611a0bc6323b8a3d4840))
ROM_END

ROM_START( isbc8605 )
	ROM_REGION16_LE( 0x8000, "bios", ROMREGION_ERASEFF )
	ROM_LOAD( "i8605mon.bin", 0x4000, 0x4000, CRC(e16acb6e) SHA1(eb9a3fd21f7609d44f8052b6a0603ecbb52dc3f3))
ROM_END

ROM_START( isbc8630 )
	ROM_REGION16_LE( 0x8000, "bios", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "14378", "14378" )
	ROMX_LOAD( "143780-001_isdm_for_isbc_86-30_socket_u57_i2732a.bin", 0x4000, 0x1000, CRC(db0ef880) SHA1(8ef296066d16881217618e54b410d12157f318ea), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "143782-001_isdm_for_isbc_86-30_socket_u39_i2732a.bin", 0x4001, 0x1000, CRC(ea1ebe78) SHA1(f03b63659e8f5e96f481dbc6c2ddef1d22850ebb), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "143781-001_isdm_for_isbc_86-30_socket_u58_i2732a.bin", 0x6000, 0x1000, CRC(93732612) SHA1(06e751d0f5ab1fe2c52fd79f6f4725ccf3379791), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "143783-001_isdm_for_isbc_86-30_socket_u40_i2732a.bin", 0x6001, 0x1000, CRC(337102d5) SHA1(535f63d24c3948187b208ea594f979bc33579a15), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "14503", "14503" )
	ROMX_LOAD( "145032-001_u57.bin", 0x0000, 0x2000, CRC(09a24dea) SHA1(e21277f1d4d72e0858846f7293ac48417b392e3b), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "145030-001_u39.bin", 0x0001, 0x2000, CRC(c58f3a98) SHA1(76f6d5be8ea6854a98f6555320cfcdb814e5c633), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "145033-001_u58.bin", 0x4000, 0x2000, CRC(496aca5f) SHA1(c09f4d2254ece1eb139ef5fd4ad0ce6a55376da5), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "145031-001_u40.bin", 0x4001, 0x2000, CRC(150fcd90) SHA1(4bca0f46b9b05ef0124bac5dea09ddd952e73af2), ROM_SKIP(1) | ROM_BIOS(1))
ROM_END

ROM_START( isbc286 )
	ROM_REGION16_LE( 0x20000, "bios", ROMREGION_ERASEFF )
	ROM_LOAD16_BYTE( "u79.bin", 0x00001, 0x10000, CRC(144182ea) SHA1(4620ca205a6ac98fe2636183eaead7c4bfaf7a72))
	ROM_LOAD16_BYTE( "u36.bin", 0x00000, 0x10000, CRC(22db075f) SHA1(fd29ea77f5fc0697c8f8b66aca549aad5b9db3ea))
ROM_END

/*
 * :uart8274 A Reg 00 <- 18 - Channel reset command
 * :uart8274 A Reg 04 <- 44 - x16 clock, 1 stop bit, no parity
 * :uart8274 A Reg 05 <- ea - Tx Enabled, Tx 8 bits, Send Break 0, RTS=0, <DTR=0
 * :uart8274 A Reg 03 <- c1 - Rx Enabled, Rx 8 bits, No Auto Enable
 * :uart8274 B Reg 00 <- 18 - Channel reset command
 * :uart8274 B Reg 04 <- 44 - x16 clock, 1 stop bit, no parity
 * :uart8274 B Reg 05 <- ea - Tx Enabled, Transmitter Bits/Character 8, Send Break 0, RTS=0, DTR=0
 * :uart8274 B Reg 03 <- c1 - Rx 8 bits, No Auto Enables, Rx Enabled,

 * :uart8274 B Reg 00 <- 18 - Channel reset command
 * :uart8274 B Reg 04 <- 4e - x16 clock, 2 stop bit, even parity but parity disabled
 * :uart8274 B Reg 05 <- ea - Tx Enabled, Tx 8 bits, Send Break 0, RTS=0, DTR=0
 * :uart8274 B Reg 03 <- c1 - Rx Enabled, Rx 8 bits, No Auto Enables
 * :uart8274 B Reg 07 <- 00 - Hi SYNC bits
 * :uart8274 B Reg 06 <- 00 - Lo SYNC bits
 * :uart8274 A Reg 02 <- 04 - RTSB selected, non vectored mode, 85-1 mode selected, A over B interleaved int prios
 * :uart8274 B Reg 02 <- 26 - interrupt vector 26
 * :uart8274 B Reg 01 <- 00 - Rx INT/DMA int disabled, no vector modification

 * :uart8274 B Reg 00 <- 18 - Channel reset command
 * :uart8274 B Reg 00 <- 18 - Channel reset command
 * :uart8274 B Reg 04 <- 44 - x16 clock, 1 stop bit, no parity
 * :uart8274 B Reg 05 <- ea - Tx Enabled, Tx 8 bits, Send Break 0, RTS=0, DTR=0
 * :uart8274 B Reg 03 <- c1 - Rx Enabled, Rx 8 bits, No Auto Enables
 * :uart8274 B Reg 00 <- 28 - Reset Transmitter Interrupt Pending
 * :uart8274 B Reg 00 <- 28 - Reset Transmitter Interrupt Pending
 * :uart8274 B Reg 00 <- 28 - Reset Transmitter Interrupt Pending
 * :uart8274 B Reg 00 <- 28 - Reset Transmitter Interrupt Pending

 * :uart8274 A Reg 00 <- 18 - Channel reset command
 * :uart8274 A Reg 04 <- 4e - x16 clock, 2 stop bit, even parity but parity disabled
 * :uart8274 A Reg 05 <- ea - Tx Enabled, Tx 8 bits, Send Break 0, RTS=0, DTR=0
 * :uart8274 A Reg 03 <- c1 - Rx Enabled, Rx 8 bits, No Auto Enables
 * :uart8274 A Reg 07 <- 00 - Hi SYNC bits
 * :uart8274 A Reg 06 <- 00 - Lo SYNC bits
 * :uart8274 A Reg 02 <- 04 - RTSB selected, non vectored mode, 85-1 mode selected, A over B interleaved int prios
 * :uart8274 B Reg 02 <- 26 - interrupt vector 26
 * :uart8274 A Reg 01 <- 00 - Rx INT/DMA int disabled, no vector modification

 * :uart8274 A Reg 01 -> ?? - Read out Status Register 1 (Errors and All Sent flag)
 * :uart8274 A Reg 05 <- e2 - Tx Disabled, Tx 8 bits, Send Break 0, RTS=0, DTR=0
 * :uart8274 A Reg 03 <- c0 - Rx Disabled, Rx 8 bits, No Auto Enables
 * :uart8274 A Reg 05 <- ea - Tx Enabled, Tx 8 bits, Send Break 0, RTS=0, DTR=0
 * :uart8274 A Reg 04 <- 4e - x16 clock, 2 stop bit, even parity but parity disabled
 * :uart8274 A Reg 05 <- ea - Tx Enabled, Tx 8 bits, Send Break 0, RTS=0, DTR=0
 * :uart8274 A Reg 03 <- c1 - Rx Enabled, Rx 8 bits, No Auto Enables
 * :uart8274 A Reg 07 <- 00 - Hi SYNC bits
 * :uart8274 A Reg 06 <- 00 - Lo SYNC bits
 * :uart8274 A Reg 02 <- 04 - RTSB selected, non vectored mode, 85-1 mode selected, A over B interleaved int prios
 * :uart8274 B Reg 02 <- 26 - interrupt vector 26
 * :uart8274 A Reg 01 <- 00 - Rx INT/DMA int disabled, no vector modification
 * :uart8274 A Reg 02 <- 04 - RTSB selected, non vectored mode, 85-1 mode selected, A over B interleaved int prios

 * :uart8274 B Reg 02 <- a5 - interrupt vector a5
 * :uart8274 B Reg 02 <- 00 - interrupt vector 0

 * :uart8274 B Reg 04 <- 44 - x16 clock, 1 stop bit, no parity
 * :uart8274 B Reg 01 <- 1e - Wait disabled, Int mode 3, vector modified, Tx int/DMA enabled
 * :uart8274 A Reg 04 <- 44 - x16 clock, 1 stop bit, no parity
 * :uart8274 A Reg 01 <- 1e - Wait disabled, Int mode 3, vector modified, Tx int/DMA enabled

 * :uart8274 B Reg 04 <- 44 - x16 clock, 1 stop bit, no parity
 * :uart8274 B Reg 01 <- 1e - Wait disabled, Int mode 3, vector modified, Tx int/DMA enabled
 * :uart8274 B Reg 03 <- c1 - Rx Enabled, Rx 8 bits, No Auto Enables
 * :uart8274 B Reg 05 <- ea - Tx Enabled, Tx 8 bits, Send Break 0, RTS=0, DTR=0

 * :uart8274 B Reg 04 <- 44 - x16 clock, 1 stop bit, no parity
 * :uart8274 B Reg 01 <- 1e - Wait disabled, Int mode 3, vector modified, Tx int/DMA enabled
 * :uart8274 B Reg 03 <- c1 - Rx Enabled, Rx 8 bits, No Auto Enables
 * :uart8274 B Reg 05 <- ea - Tx Enabled, Tx 8 bits, Send Break 0, RTS=0, DTR=0
*/
ROM_START( isbc2861 )
	ROM_REGION16_LE( 0x10000, "bios", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v11", "iSDM Monitor V1.1" )
	ROMX_LOAD( "174894-001.bin", 0x0000, 0x4000, CRC(79e4f7af) SHA1(911a4595d35e6e82b1149e75bb027927cd1c1658), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "174894-002.bin", 0x0001, 0x4000, CRC(66747d21) SHA1(4094b1f10a8bc7db8d6dd48d7128e14e875776c7), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "174894-003.bin", 0x8000, 0x4000, CRC(c98c7f17) SHA1(6e9a14aedd630824dccc5eb6052867e73b1d7db6), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "174894-004.bin", 0x8001, 0x4000, CRC(61bc1dc9) SHA1(feed5a5f0bb4630c8f6fa0d5cca30654a80b4ee5), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v10", "iSDM Monitor V1.0" )
	ROMX_LOAD( "rmx286-_in_socket_u41_on_isbc_286-10.bin.u41", 0x0000, 0x4000, CRC(00996834) SHA1(a17a0f8909be642d89199660b24574b71a9d0c13), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "rmx286-_in_socket_u76_on_isbc_286-10.bin.u76", 0x0001, 0x4000, CRC(90c9c7e8) SHA1(b5f961ab236976713266fe7a378e8750825fd5dc), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "rmx286-_in_socket_u40_on_isbc_286-10.bin.u40", 0x8000, 0x4000, CRC(35716c9b) SHA1(5b717b4c2f6c59ec140635df7448294a22123a16), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "rmx286-_in_socket_u75_on_isbc_286-10.bin.u75", 0x8001, 0x4000, CRC(68c3eb50) SHA1(3eeef2676e4fb187adb8ab50645f4bd172426c15), ROM_SKIP(1) | ROM_BIOS(1))
ROM_END

ROM_START( isbc28612 )
	ROM_REGION16_LE( 0x10000, "bios", ROMREGION_ERASEFF )
	ROM_LOAD16_BYTE( "176346-001.bin", 0x0000, 0x8000, CRC(f86c8be5) SHA1(e2bb16b0aeb718219e65d61edabd7838ef34c560))
	ROM_LOAD16_BYTE( "176346-002.bin", 0x0001, 0x8000, CRC(b964c6c3) SHA1(c3de8541182e32b3568fde77da8c435eab397498))
ROM_END

ROM_START( rpc86 )
	ROM_REGION16_LE( 0x8000, "bios", ROMREGION_ERASEFF )
	ROM_LOAD16_BYTE( "145068-001.bin", 0x4001, 0x1000, CRC(0fa9db83) SHA1(4a44f8683c263c9ef6850cbe05aaa73f4d4d4e06))
	ROM_LOAD16_BYTE( "145069-001.bin", 0x6001, 0x1000, CRC(1692a076) SHA1(0ce3a4a867cb92340871bb8f9c3e91ce2984c77c))
	ROM_LOAD16_BYTE( "145070-001.bin", 0x4000, 0x1000, CRC(8c8303ef) SHA1(60f94daa76ab9dea6e309ac580152eb212b847a0))
	ROM_LOAD16_BYTE( "145071-001.bin", 0x6000, 0x1000, CRC(a49681d8) SHA1(e81f8b092cfa2d1737854b1fa270a4ce07d61a9f))
ROM_END

ROM_START( sm1810 )
	ROM_REGION16_LE( 0x8000, "bios", ROMREGION_ERASEFF )
	ROM_LOAD16_BYTE( "sm1810.42-1.06-1.bin", 0x0000, 0x2000, CRC(de8b42e7) SHA1(bb93335b4ef79638f88c38adedfb7dd9ed9d1e31))
	ROM_LOAD16_BYTE( "sm1810.42-1.06-0.bin", 0x0001, 0x2000, CRC(352bb060) SHA1(2112fcbf9903ad8af29a5a8d4b57eaeb5cd74739))
	ROM_LOAD16_BYTE( "sm1810.42-1.06-2.bin", 0x4000, 0x2000, CRC(ae015240) SHA1(2c345a9e0832a0f26493bda394b2c4ad7ada7aad))
	ROM_LOAD16_BYTE( "sm1810.42-1.06-3.bin", 0x4001, 0x2000, CRC(9741a51a) SHA1(c9d3a6a5c51fe9814986517b0f4cbeae8200babc))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME       PARENT  COMPAT  MACHINE   INPUT  CLASS       INIT        COMPANY  FULLNAME       FLAGS */
COMP( 19??, rpc86,     0,      0,      rpc86,    isbc,  isbc_state, empty_init, "Intel", "RPC 86",      MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)
COMP( 1978, isbc86,    0,      0,      isbc86,   isbc,  isbc_state, empty_init, "Intel", "iSBC 86/12A", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)
COMP( 1981, isbc8605,  0,      0,      isbc8605, isbc,  isbc_state, empty_init, "Intel", "iSBC 86/05",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)
COMP( 1981, isbc8630,  0,      0,      isbc8630, isbc,  isbc_state, empty_init, "Intel", "iSBC 86/30",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)
COMP( 19??, isbc286,   0,      0,      isbc286,  isbc,  isbc_state, empty_init, "Intel", "iSBC 286",    MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)
COMP( 1983, isbc2861,  0,      0,      isbc2861, isbc,  isbc_state, empty_init, "Intel", "iSBC 286/10", MACHINE_NO_SOUND_HW)
COMP( 1983, isbc28612, 0,      0,      isbc2861, isbc,  isbc_state, empty_init, "Intel", "iSBC 286/12", MACHINE_NO_SOUND_HW)
COMP( 19??, sm1810,    0,      0,      sm1810,   isbc,  isbc_state, empty_init, "<unknown>", "SM1810",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)



isbc660.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * Intel iSBC 660 System Chassis
 *
 * This is a bare system chassis with an 8-slot backplane into which a variety
 * of Multibus boards may be installed.
 *
 * Sources:
 *  - http://www.nj7p.org/Manuals/PDFs/Intel/AFN-00285A.pdf
 *
 * TODO:
 *  - additional cards
 */

#include "emu.h"

#include "bus/multibus/multibus.h"
#include "bus/multibus/isbc8024.h"

#define VERBOSE 0
#include "logmacro.h"

namespace {

class isbc660_state : public driver_device
{
public:
	isbc660_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_bus(*this, "bus")
	{
	}

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

public:
	// machine config
	void isbc660(machine_config &config);

private:
	required_device<multibus_device> m_bus;
};

void isbc660_state::machine_start()
{
}

void isbc660_state::machine_reset()
{
}

static void isbc660_cards(device_slot_interface &device)
{
	device.option_add("isbc8024", ISBC8024);
}

void isbc660_state::isbc660(machine_config &config)
{
	MULTIBUS(config, m_bus, 10_MHz_XTAL); // FIXME: clock driven by bus master

	MULTIBUS_SLOT(config, "slot1", m_bus, isbc660_cards, nullptr, false);
	MULTIBUS_SLOT(config, "slot2", m_bus, isbc660_cards, nullptr, false);
	MULTIBUS_SLOT(config, "slot3", m_bus, isbc660_cards, nullptr, false);
	MULTIBUS_SLOT(config, "slot4", m_bus, isbc660_cards, nullptr, false);
	MULTIBUS_SLOT(config, "slot5", m_bus, isbc660_cards, nullptr, false);
	MULTIBUS_SLOT(config, "slot6", m_bus, isbc660_cards, nullptr, false);
	MULTIBUS_SLOT(config, "slot7", m_bus, isbc660_cards, nullptr, false);
	MULTIBUS_SLOT(config, "slot8", m_bus, isbc660_cards, nullptr, false);
}

ROM_START(isbc660)
ROM_END

}

/*   YEAR  NAME     PARENT COMPAT MACHINE  INPUT CLASS          INIT        COMPANY  FULLNAME    FLAGS */
COMP(1985, isbc660, 0,     0,     isbc660, 0,    isbc660_state, empty_init, "Intel", "iSBC 660", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)



isbc8010.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes,Ryan Holtz
/***************************************************************************

        Intel SBC 80/10 and SBC 80/10A Single Board Computers

The difference between the SBC 80/10 and SBC 80/10A is in the type and
quantity of memory available on each board.

There is no speaker or storage facility in the standard kit.

Download the User Manual to get the operating procedures.

Monitor Commands:
D  Display memory command
G  Program execute command
I  Insert instructions into memory
M  Move memory command
R  Read hexadecimal file
S  Substitute memory command
W  Write hexadecimal file
X  Examine and modify CPU registers command

No known manual or schematic of the video board.

****************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8255.h"
#include "machine/i8251.h"
#include "machine/clock.h"
#include "bus/rs232/rs232.h"
//#include "machine/ay31015.h"


namespace {

#define I8255A_1_TAG    "ppi8255_1"
#define I8255A_2_TAG    "ppi8255_2"
#define I8251A_TAG      "usart"
#define I8251A_BAUD_TAG "usart_baud"
#define RS232_TAG       "rs232"

class isbc8010_state : public driver_device
{
public:
	isbc8010_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_usart(*this, I8251A_TAG)
		, m_ppi_0(*this, I8255A_1_TAG)
		, m_ppi_1(*this, I8255A_2_TAG)
		, m_rs232(*this, RS232_TAG)
		, m_usart_baud_rate(*this, I8251A_BAUD_TAG)
		, m_usart_divide_counter(0)
		, m_usart_clock_state(0)
	{ }

	void isbc8010b(machine_config &config);
	void isbc8010a(machine_config &config);
	void isbc8010(machine_config &config);

private:
	[[maybe_unused]] uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void usart_clock_tick(int state);

	void isbc8010_io(address_map &map) ATTR_COLD;
	void isbc8010_mem(address_map &map) ATTR_COLD;
	void isbc8010a_mem(address_map &map) ATTR_COLD;
	void isbc8010b_mem(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<i8251_device> m_usart;
	required_device<i8255_device> m_ppi_0;
	required_device<i8255_device> m_ppi_1;
	required_device<rs232_port_device> m_rs232;
	required_ioport m_usart_baud_rate;

	uint8_t m_usart_divide_counter;
	uint8_t m_usart_clock_state;
};

void isbc8010_state::isbc8010_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).rom();
	map(0x3c00, 0x3fff).ram();
}

void isbc8010_state::isbc8010a_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom();
	map(0x3c00, 0x3fff).ram();
}

void isbc8010_state::isbc8010b_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3bff).rom();
	map(0x3c00, 0x3fff).ram();
}

void isbc8010_state::isbc8010_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0xe4, 0xe7).rw(m_ppi_0, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xe8, 0xeb).rw(m_ppi_1, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xec, 0xed).mirror(0x02).rw(m_usart, FUNC(i8251_device::read), FUNC(i8251_device::write));
	//map(0xf0, 0xf7) MCS0 - iSBX Multimodule
	//map(0xf8, 0xff) MCS1 - iSBX Multimodule
}

static INPUT_PORTS_START( isbc8010 )
	PORT_START(I8251A_BAUD_TAG)
	PORT_DIPNAME( 0x3f, 0x01, "i8251 Baud Rate" )
	PORT_DIPSETTING(    0x01, "4800")
	PORT_DIPSETTING(    0x02, "2400")
	PORT_DIPSETTING(    0x04, "1200")
	PORT_DIPSETTING(    0x08, "600")
	PORT_DIPSETTING(    0x10, "300")
	PORT_DIPSETTING(    0x20, "150")
	PORT_DIPSETTING(    0x40, "75")
INPUT_PORTS_END

#if 0
/* Graphics Output */
const gfx_layout sdk80_charlayout =
{
	7, 8,               /* character cell is 7 pixels wide by 8 pixels high */
	64,                 /* 64 characters in 2513 character generator ROM */
	1,                  /* 1 bitplane */
	{ 0 },
	/* 5 visible pixels per row, starting at bit 3, with MSB being 0: */
	{ 3, 4, 5, 6, 7 },
	/* pixel rows stored from top to bottom: */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8 * 8               /* 8 8-bit pixel rows per character */
};

static GFXDECODE_START( gfx_isbc8010 )
	GFXDECODE_ENTRY( "gfx1", 0x0000, sdk80_charlayout, 0, 1 )
GFXDECODE_END
#endif

uint32_t isbc8010_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void isbc8010_state::usart_clock_tick(int state)
{
	uint8_t old_counter = m_usart_divide_counter;
	m_usart_divide_counter++;

	uint8_t transition = (old_counter ^ m_usart_divide_counter) & m_usart_baud_rate->read();
	if (transition)
	{
		m_usart->write_txc(m_usart_clock_state);
		m_usart->write_rxc(m_usart_clock_state);
		m_usart_clock_state ^= 1;
	}
}

static DEVICE_INPUT_DEFAULTS_START( terminal ) // set up terminal to default to 4800
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_4800 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_4800 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void isbc8010_state::isbc8010(machine_config &config)
{
	/* basic machine hardware */
	I8080A(config, m_maincpu, XTAL(18'432'000)/9);
	m_maincpu->set_addrmap(AS_PROGRAM, &isbc8010_state::isbc8010_mem);
	m_maincpu->set_addrmap(AS_IO, &isbc8010_state::isbc8010_io);

	I8251(config, m_usart, 0);
	m_usart->txd_handler().set(RS232_TAG, FUNC(rs232_port_device::write_txd));
	m_usart->dtr_handler().set(RS232_TAG, FUNC(rs232_port_device::write_dtr));
	m_usart->rts_handler().set(RS232_TAG, FUNC(rs232_port_device::write_rts));

	I8255A(config, m_ppi_0);
	I8255A(config, m_ppi_1);

	RS232_PORT(config, m_rs232, default_rs232_devices, "terminal");
	m_rs232->rxd_handler().set(m_usart, FUNC(i8251_device::write_rxd));
	m_rs232->dsr_handler().set(m_usart, FUNC(i8251_device::write_dsr));
	m_rs232->cts_handler().set(m_usart, FUNC(i8251_device::write_cts));
	m_rs232->set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	clock_device &usart_clock(CLOCK(config, "usart_clock", XTAL(18'432'000)/60));
	usart_clock.signal_handler().set(FUNC(isbc8010_state::usart_clock_tick));

	/* video hardware */
	// 96364 crt controller

//  screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
//  screen.set_refresh_hz(60);
	/* Video is blanked for 70 out of 262 scanlines per refresh cycle.
	   Each scanline is composed of 65 character times, 40 of which
	   are visible, and each character time is 7 dot times; a dot time
	   is 2 cycles of the fundamental 14.31818 MHz oscillator.  The
	   total blanking time is about 4450 microseconds. */
//  screen.set_vblank_time(ATTOSECONDS_IN_USEC((int) (70 * 65 * 7 * 2 / 14.31818)));
	/* It would be nice if we could implement some sort of display
	   overscan here. */
//  screen.set_size(40 * 7, 24 * 8);
//  screen.set_visarea(0, 40 * 7 - 1, 0, 24 * 8 - 1);
//  screen.set_screen_update(FUNC(sdk80_state::screen_update));
//  screen.set_palette("palette");

//  GFXDECODE(config, "gfxdecode", "palette", gfx_sdk80);

//  PALETTE(config, "palette", palette_device::MONOCHROME);

	// Video board UART
//  ay31015_device &hd6402(AY31015(config, "hd6402", 0));
//  MCFG_AY31015_TX_CLOCK(( XTAL(16'000'000) / 16 ) / 256)
//  MCFG_AY31015_RX_CLOCK(( XTAL(16'000'000) / 16 ) / 256)
//  hd6402.read_si_callback().set(FUNC(sdk80_state::nascom1_hd6402_si));
//  hd6402.write_so_callback().set(FUNC(sdk80_state::nascom1_hd6402_so));

//  clock_device &uart_clock(CLOCK(config, "uart_clock", (XTAL(16'000'000) / 16) / 256));
//  uart_clock.signal_handler().set("hd6402", FUNC(ay31015_device::write_tcp));
//  uart_clock.signal_handler().append("hd6402", FUNC(ay31015_device::write_rcp));

	/* Devices */
//  i8279_device &kbdc(I8279(config, "i8279", 3100000)); // based on divider
//  kbdc.out_irq_callback().set_inputline("maincpu", I8085_RST55_LINE); // irq
//  kbdc.out_sl_callback().set(FUNC(sdk80_state::scanlines_w));         // scan SL lines
//  kbdc.out_disp_callback().set(FUNC(sdk80_state::digit_w));           // display A&B
//  kbdc.in_rl_callback().set(FUNC(sdk80_state::kbd_r));                // kbd RL lines
//  kbdc.in_shift_callback().set_constant(1);                           // Shift key
//  kbdc.in_ctrl_callback().set_constant(1);
}

void isbc8010_state::isbc8010a(machine_config &config)
{
	isbc8010(config);
	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &isbc8010_state::isbc8010a_mem);
}

void isbc8010_state::isbc8010b(machine_config &config)
{
	isbc8010(config);
	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &isbc8010_state::isbc8010b_mem);
}

/* ROM definition */
ROM_START( isbc8010 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS("mon11")
	ROM_SYSTEM_BIOS( 0, "mon11", "80/10 Monitor v1.1" )
	//ROMX_LOAD( "sbc80p.a23", 0x0000, 0x0400, CRC(bd49a7d6) SHA1(3b2f18abf35efe05f38eb08bf0c6d0f45fa7ae0a), ROM_BIOS(0)) // bad byte at 0x10d
	ROMX_LOAD( "sbc80p.a23", 0x0000, 0x0400, CRC(30e58470) SHA1(9ceca8b683e9348d37577d950136024ce9f47b6a), ROM_BIOS(0)) // see issue #6336
	ROMX_LOAD( "sbc80p.a24", 0x0400, 0x0400, CRC(18131631) SHA1(6fb29df38e056c966dcc95885bc59c2a3caf4baf), ROM_BIOS(0))

	ROM_SYSTEM_BIOS( 1, "bas80", "BASIC-80" ) // See notes at issue #6338
	ROMX_LOAD( "basic_blc_1.a24", 0x0000, 0x0400, CRC(b5e75aee) SHA1(6bd1eb9586d72544e8afb4ae43ecedcefa14da33), ROM_BIOS(1))
	ROMX_LOAD( "basic_blc_2.a25", 0x0400, 0x0400, CRC(0a9ad1ed) SHA1(92c47eadcf8b18eeedcccaa3deb9f1518aaceeae), ROM_BIOS(1))
	ROMX_LOAD( "basic_blc_3.a26", 0x0800, 0x0400, CRC(bc898e4b) SHA1(adc000534db0f736a75fbceed360dc220e02c30d), ROM_BIOS(1))
	ROMX_LOAD( "basic_blc_4.a27", 0x0c00, 0x0400, CRC(568e8b6d) SHA1(22960193d3b0ae1b5d876d8c3b3f3b40db01358c), ROM_BIOS(1))

	/* 512-byte Signetics 2513 character generator ROM at location D2-D3 */
	ROM_REGION(0x0200, "gfx1",0)
	ROM_LOAD("s2513.d2", 0x0000, 0x0200, CRC(a7e567fc) SHA1(b18aae0a2d4f92f5a7e22640719bbc4652f3f4ee))

	/* 256x4 PROM located on the video board, schematic location P7, to be moved into separate device later */
	ROM_REGION( 0x0120, "proms", 0 )
	ROM_LOAD( "6300__d7.p7",    0x0020, 0x0100, CRC(3eb3a8e4) SHA1(19097b5f60d1030f8b82d9f1d3a241f93e5c75d6) )
ROM_END

#define rom_isbc8010a rom_isbc8010
#define rom_isbc8010b rom_isbc8010

} // anonymous namespace


/*    YEAR  NAME       PARENT    COMPAT  MACHINE    INPUT     CLASS           INIT        COMPANY  FULLNAME       FLAGS */
COMP( 1975, isbc8010,  0,        0,      isbc8010,  isbc8010, isbc8010_state, empty_init, "Intel", "iSBC 80/10",  MACHINE_NO_SOUND_HW )
COMP( 1977, isbc8010a, isbc8010, 0,      isbc8010a, isbc8010, isbc8010_state, empty_init, "Intel", "iSBC 80/10A", MACHINE_NO_SOUND_HW )
COMP( 1979, isbc8010b, isbc8010, 0,      isbc8010b, isbc8010, isbc8010_state, empty_init, "Intel", "iSBC 80/10B", MACHINE_NO_SOUND_HW )



isbc8030.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/***************************************************************************

        Intel SBC 80/30 Single Board Computer

System Initialization (Reset):
The USART clock is initially set at 9600 baud. Two "U" characters are used
to check for baud rate. When the first "U" character is entered it is
checked for 9600, 4800, 2400, and 1200 baud rate. If a match is found then
that baud rate is set into the clock. If not, then a second "U" character
must be entered. The second "U" character is checked for 600, 300, 150,
and 110 baud. When the baud rate has been successfully determined, the sign
-on message "80/30 MONITOR" will be displayed on the console. When the
monitor is ready for a command, it will prompt with a period ".".

Download the User Manual to get the operating procedures.

Monitor Commands:
D  Display memory command
G  Program execute command
N  Single step command
I  Insert instruction into memory
M  Move memory command
R  Read hexadecimal file
S  Substitute memory command
W  Write hexadecimal file
X  Examine and modify CPU registers

****************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/i8255.h"
#include "machine/i8251.h"
#include "bus/rs232/rs232.h"


namespace {

class isbc8030_state : public driver_device
{
public:
	isbc8030_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_usart(*this, "usart")
		, m_ppi(*this, "ppi8255")
		, m_pic(*this, "pic8259")
		, m_pit(*this, "pit8253")
		, m_rs232(*this, "rs232")
	{ }

	void isbc8030(machine_config &config);

private:
	void isbc8030_io(address_map &map) ATTR_COLD;
	void isbc8030_mem(address_map &map) ATTR_COLD;

	required_device<i8085a_cpu_device> m_maincpu;
	required_device<i8251_device> m_usart;
	required_device<i8255_device> m_ppi;
	required_device<pic8259_device> m_pic;
	required_device<pit8253_device> m_pit;
	required_device<rs232_port_device> m_rs232;
};

void isbc8030_state::isbc8030_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom();
	map(0x2000, 0xffff).ram();
}

void isbc8030_state::isbc8030_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0xd8, 0xd9).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0xdc, 0xdf).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xe8, 0xeb).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xec, 0xed).mirror(0x02).rw(m_usart, FUNC(i8251_device::read), FUNC(i8251_device::write));
}

static INPUT_PORTS_START( isbc8030 )
INPUT_PORTS_END

void isbc8030_state::isbc8030(machine_config &config)
{
	I8085A(config, m_maincpu, XTAL(22'118'400) / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &isbc8030_state::isbc8030_mem);
	m_maincpu->set_addrmap(AS_IO, &isbc8030_state::isbc8030_io);
	m_maincpu->in_inta_func().set(m_pic, FUNC(pic8259_device::acknowledge));

	PIC8259(config, m_pic, 0);
	m_pic->out_int_callback().set_inputline(m_maincpu, 0);

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(XTAL(22'118'400) / 18);
	m_pit->out_handler<0>().set(m_pic, FUNC(pic8259_device::ir0_w));
	m_pit->set_clk<1>(XTAL(22'118'400) / 18);
	m_pit->set_clk<2>(XTAL(22'118'400) / 18);
	m_pit->out_handler<2>().set(m_usart, FUNC(i8251_device::write_rxc));
	m_pit->out_handler<2>().append(m_usart, FUNC(i8251_device::write_txc));

	I8251(config, m_usart, 0);
	m_usart->txd_handler().set(m_rs232, FUNC(rs232_port_device::write_txd));
	m_usart->dtr_handler().set(m_rs232, FUNC(rs232_port_device::write_dtr));
	m_usart->rts_handler().set(m_rs232, FUNC(rs232_port_device::write_rts));

	I8255A(config, m_ppi, 0);

	RS232_PORT(config, m_rs232, default_rs232_devices, "terminal");
	m_rs232->rxd_handler().set(m_usart, FUNC(i8251_device::write_rxd));
	m_rs232->dsr_handler().set(m_usart, FUNC(i8251_device::write_dsr));
	m_rs232->cts_handler().set(m_usart, FUNC(i8251_device::write_cts));
}

/* ROM definition */
ROM_START( isbc8030 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "mon830.bin", 0x0000, 0x0800, CRC(cda15115) SHA1(242dad14a919568178b363c3e27f22ec0a5849b3))
ROM_END

} // anonymous namespace


/*    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY  FULLNAME      FLAGS */
COMP( 1978, isbc8030, 0,      0,      isbc8030, isbc8030, isbc8030_state, empty_init, "Intel", "iSBC 80/30", MACHINE_NO_SOUND_HW )



iskr103x.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************

    drivers/iskr103x.c

    Driver file for Iskra-1030, 1031

    TODO
    - fix cyrillic chargen upload for CGA and MDA
    - replace DIP switch definition
    - keyboard test is not passing (code 301)
    - hard disk is connected but untested

***************************************************************************/


#include "emu.h"
#include "machine/genpc.h"

#include "bus/isa/xsu_cards.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "cpu/i86/i86.h"
#include "machine/pc_lpt.h"
#include "machine/ram.h"

#include "softlist.h"


namespace {

class iskr103x_state : public driver_device
{
public:
	iskr103x_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void iskr1030m(machine_config &config);
	void iskr1031(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	void iskr1031_io(address_map &map) ATTR_COLD;
	void iskr1031_map(address_map &map) ATTR_COLD;
};

void iskr103x_state::iskr1031_map(address_map &map)
{
	map.unmap_value_high();
	map(0xf0000, 0xfffff).rom().region("bios", 0);
}


void iskr103x_state::iskr1031_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m("mb", FUNC(ibm5160_mb_device::map));
}


static DEVICE_INPUT_DEFAULTS_START(iskr1030m)
	DEVICE_INPUT_DEFAULTS("DSW0", 0x30, 0x20)
	DEVICE_INPUT_DEFAULTS("DSW0", 0x01, 0x01)
DEVICE_INPUT_DEFAULTS_END

static DEVICE_INPUT_DEFAULTS_START(iskr1031)
	DEVICE_INPUT_DEFAULTS("DSW0", 0x30, 0x20)
DEVICE_INPUT_DEFAULTS_END


void iskr103x_state::iskr1030m(machine_config &config)
{
	/* basic machine hardware */
	I8086(config, m_maincpu, 4772720);
	m_maincpu->set_addrmap(AS_PROGRAM, &iskr103x_state::iskr1031_map);
	m_maincpu->set_addrmap(AS_IO, &iskr103x_state::iskr1031_io);
	m_maincpu->set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	ibm5160_mb_device &mb(IBM5160_MOTHERBOARD(config, "mb"));
	mb.set_cputag(m_maincpu);
	mb.int_callback().set_inputline(m_maincpu, 0);
	mb.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	mb.kbdclk_callback().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	mb.kbddata_callback().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));
	mb.set_input_default(DEVICE_INPUT_DEFAULTS_NAME(iskr1030m));

	ISA8_SLOT(config, "isa1", 0, "mb:isa", iskr103x_isa8_cards, "cga_iskr1030m", false); // FIXME: determine IS bus clock
	ISA8_SLOT(config, "isa2", 0, "mb:isa", iskr103x_isa8_cards, "fdc_xt", false);
	ISA8_SLOT(config, "isa3", 0, "mb:isa", iskr103x_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa4", 0, "mb:isa", iskr103x_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa5", 0, "mb:isa", iskr103x_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa6", 0, "mb:isa", iskr103x_isa8_cards, nullptr, false);

	pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_EC_1841));
//  pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_ISKR_1030));
	kbd.out_clock_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_clock_w));
	kbd.out_data_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_data_w));

	RAM(config, RAM_TAG).set_default_size("640K").set_extra_options("64K, 128K, 256K, 512K");
}

void iskr103x_state::iskr1031(machine_config &config)
{
	iskr1030m(config);
	subdevice<ibm5160_mb_device>("mb")->set_input_default(DEVICE_INPUT_DEFAULTS_NAME(iskr1031));
	subdevice<isa8_slot_device>("isa1")->set_default_option("cga_iskr1031");
}

ROM_START( iskr1030m )
	ROM_REGION16_LE(0x10000,"bios", 0)
	ROMX_LOAD( "iskra-1030m_0.rom", 0xc000, 0x2000, CRC(0d698e19) SHA1(2fe117c9f4f8c4b59085d5a41f919d743c425fdd), ROM_SKIP(1))
	ROMX_LOAD( "iskra-1030m_1.rom", 0xc001, 0x2000, CRC(fe808337) SHA1(b0b7ebe14324ada8aa9a6926a82b18e80f78a257), ROM_SKIP(1))
ROM_END

ROM_START( iskr1031 )
	ROM_REGION16_LE(0x10000,"bios", 0)
	ROM_SYSTEM_BIOS(0, "v1", "v1")
	ROMX_LOAD( "150-02.bin", 0xc000, 0x2000, CRC(e33fb974) SHA1(f5f3ece67c025c0033716ff516e1a34fbeb32749), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "150-03.bin", 0xc001, 0x2000, CRC(8c482258) SHA1(90ef48955e0df556dc06a000a797ef42ccf430c5), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v2", "v2")
	ROMX_LOAD( "150-06.bin", 0xc000, 0x2000, CRC(1adbf969) SHA1(08c0a0fc50a75e6207b1987bae389cca60893eac), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "150-07.bin", 0xc001, 0x2000, CRC(0dc4b65a) SHA1(c96f066251a7343eac8113ea9dcb2cb12d0334d5), ROM_SKIP(1) | ROM_BIOS(1))
ROM_END

} // anonymous namespace


/***************************************************************************

  Game driver(s)

***************************************************************************/

//     YEAR  NAME       PARENT   COMPAT  MACHINE    INPUT  CLASS           INIT        COMPANY      FULLNAME       FLAGS
COMP ( 1989, iskr1030m, ibm5150, 0,      iskr1030m, 0,     iskr103x_state, empty_init, "Schetmash", "Iskra 1030M", MACHINE_NOT_WORKING )
COMP ( 1989, iskr1031,  ibm5150, 0,      iskr1031,  0,     iskr103x_state, empty_init, "<unknown>", "Iskra 1031",  0 )



itc4.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/************************************************************************************************************

    Skeleton driver for temperature controller by Oxford Instruments Ltd.

PCB
  _______________________________________________________________
 |                                                              |
 |                           ___                 ___            |
 |             ___           |__|                |  |           |
 |            HP2200                 ________    |__|           |
 |                                   |_______|                  |
 |                    ___                                       |
 |                    |LF3478N      ________     ________       |
 |                    |__|          |DIPSx10|    |DIPSx10|      |
 | ::::::::::::                                                 |
 |_||||||||||||_________________________________________________|
   ||||||||||||
  _||||||||||||__________________________________________________
 | ::::::::::::                                     ___   ___   |
 |                                       74HC273P->|  |  |  |   |
 |  ___ <-B98-3-R100K                              |  |  |  |   |
 |  |  |         ____________   ____________       |__|  |__|   |
 |  |  |        |Z84C3006PEC|  | DS1220Y   |                    |
 |  |__|        |Z80_CTC____|  |___________|                    |
 |        ___                                                   |
 |        |  |    _________     ____________       ___________  |
 |        |  |   |_74HC02P|    | EPROM     |      | NM232C   |  |
 |74HC244P->_|                 |___________|      |__________|  |
 |        ___                                                   |
 |74HC273P-> |    _________     ________________   ____________ |
 | ..     |  |   |_74HC32P|    |Z84C0006PEC    |  |D8251AFC   | |
 | ..     |__|                 |Z80_CPU________|  |___________| |
 | ..                                                           |
 | ..     ___     _________                                     |
 |74HC137P-> |   |_74HC32P|       SWITCH                        |
 |        |  |                                ___               |
 |        |__|   Xtal            _________   |  |<-L8939H       |
 |               8.000 MHz      MC14526BCP   |__|               |
 |                _________      _________               ___    |
 |               |_74HC74P|     |_74HC74P|             LF353N   |
 |                                                              |
 |     :::::::::::::   ······                                 ::|
 |______________________________________________________________|

FRONT CONTROL PANEL (o=LED, SW=switch)
  _____________________________________________________________________________________________________________________
 | __________________________________ ____________ _____________________ _____________ ___________ _________ ________ |
 ||              DISPLAY            ||  SWEEP    ||       HEATER        ||  GAS FLOW  || CONTROL  || ADJUST ||       ||
 ||         ______________________  ||           ||       ____________  ||            ||          ||        ||       ||
 ||  o 1   |     __  __  __  __  |  || o HOLD    ||  o 1 |            | ||            || o LOCK   ||  RAISE ||       ||
 ||  o 2   |+ | |__||__||__||__| |  || o SWEEP   ||  o 2 | |||||||||| | ||            || o REMOTE ||   SW   ||       ||
 ||  o 3   |- | |__||__||__||__| |  || o PROGRAM ||  o 3 |   o    o   | ||            || o LOCAL  ||        ||       ||
 ||        |_____________________|  ||           ||      |____________| ||            ||          ||        ||       ||
 ||         CAL                     ||  RUN /    ||                     ||  o     o   ||          ||        ||   o   ||
 || SENSOR   o   PROP INT DERIV SET || PROGRAM   ||  SENSOR AUTO  MAN   || AUTO  MAN  || LOC/REM  ||  LOWER || POWER ||
 ||   SW   LIMIT  SW   SW   SW   SW ||   SW      ||    SW    SW    SW   ||  SW    SW  ||    SW    ||   SW   ||   SW  ||
 ||          o                      ||           ||                     ||            ||          ||        ||       ||
 ||_________________________________||___________||_____________________||____________||__________||________||_______||
 |____________________________________________________________________________________________________________________|

BACK PANEL
 -RS-232
 -AUXILIARY (DA15)
 -SENSOR 1 (DE9)

************************************************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/i8251.h"
#include "machine/nvram.h"
#include "machine/z80ctc.h"


namespace {

class itc4_state : public driver_device
{
public:
	itc4_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void itc4(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
};


void itc4_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).rom().region("program", 0);
	map(0x8000, 0x87ff).ram().share("nvram");
}

void itc4_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x78, 0x7b).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0xd8, 0xd9).rw("usart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xb8, 0xbf).noprw(); // 7-segment display?
	map(0xe8, 0xe8).nopw();
}


static INPUT_PORTS_START(itc4)
INPUT_PORTS_END


static const z80_daisy_config daisy_chain[] =
{
	{ "ctc" },
	{ nullptr }
};

void itc4_state::itc4(machine_config &config)
{
	Z80(config, m_maincpu, 8_MHz_XTAL / 2); // Z84C0006PEC
	m_maincpu->set_addrmap(AS_PROGRAM, &itc4_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &itc4_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // DS1220Y

	z80ctc_device &ctc(Z80CTC(config, "ctc", 8_MHz_XTAL / 2)); // Z84C3006PEC
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	ctc.set_clk<0>(8_MHz_XTAL / 4); // guess
	ctc.set_clk<1>(8_MHz_XTAL / 4); // guess
	ctc.set_clk<2>(8_MHz_XTAL / 4); // guess

	i8251_device &usart(I8251(config, "usart", 8_MHz_XTAL / 4)); // µPD8251AFC
	usart.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	usart.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	usart.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set("usart", FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set("usart", FUNC(i8251_device::write_dsr));
	rs232.cts_handler().set("usart", FUNC(i8251_device::write_cts));
}

ROM_START(itc4)
	ROM_REGION(0x4000, "program", 0)
	ROM_LOAD("itc4_2.04_70fa.u2", 0x0800, 0x0800, CRC(f2d3f050) SHA1(77947dd9584ffec22c67940582efbeb3e9553e07)) // M27128
	ROM_CONTINUE(0x0000, 0x0800) // "(c) OXFORD 1988"
	ROM_CONTINUE(0x1800, 0x0800)
	ROM_CONTINUE(0x1000, 0x0800)
	ROM_CONTINUE(0x2800, 0x0800)
	ROM_CONTINUE(0x2000, 0x0800)
	ROM_CONTINUE(0x3800, 0x0800)
	ROM_CONTINUE(0x3000, 0x0800)
ROM_END

} // anonymous namespace


SYST(1988, itc4, 0, 0, itc4, itc4, itc4_state, empty_init, "Oxford Instruments", "ITC-4 Intelligent Temperature Controller (Version 2.04)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



itt1700.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

Skeleton driver for ITT Courier 1700 compact IBM 3278-compatible display terminal.

Next to the CPU is a 40-pin DIP marked:

    AMI 8421MAF
    2651-P2
    C02805
    PHILIPPINES

This device may be related to the Intel 8251, but it is definitely not a SCN2651 equivalent.

************************************************************************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "cpu/mcs48/mcs48.h"
#include "itt1700_kbd.h"
#include "video/mc6845.h"
#include "screen.h"
#include "speaker.h"


namespace {

class itt1700_state : public driver_device
{
public:
	itt1700_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_videoram(*this, "videoram")
		, m_chargen(*this, "chargen")
	{
	}

	void itt1700(machine_config &config);

private:
	MC6845_UPDATE_ROW(update_row);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_shared_ptr<u8> m_videoram;
	required_region_ptr<u8> m_chargen;
};


MC6845_UPDATE_ROW(itt1700_state::update_row)
{
	u32 *px = &bitmap.pix(y);
	u16 page = 0x800;

	for (int i = 0; i < x_count; i++)
	{
		u8 chr = m_videoram[((ma + i) & 0x7ff) | page];
		u16 dots = m_chargen[u16(chr) << 4 | ra] << 1;
		rgb_t fg = rgb_t::white();
		rgb_t bg = rgb_t::black();

		for (int n = 9; n > 0; n--, dots <<= 1)
			*px++ = BIT(dots, 8) ? fg : bg;
	}
}

void itt1700_state::mem_map(address_map &map)
{
	map(0x0000, 0x0fff).rom().region("maincpu", 0);
	map(0x2000, 0x3fff).ram().share("videoram");
	map(0x4000, 0x7fff).ram();
	map(0x8000, 0x8000).nopr();
}

void itt1700_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x10, 0x10).w("crtc", FUNC(hd6845s_device::address_w));
	map(0x11, 0x11).w("crtc", FUNC(hd6845s_device::register_w));
	map(0x20, 0x21).rw("upi", FUNC(i8741a_device::upi41_master_r), FUNC(i8741a_device::upi41_master_w));
}

static INPUT_PORTS_START(itt1700)
INPUT_PORTS_END

void itt1700_state::itt1700(machine_config &config)
{
	Z80(config, m_maincpu, 17.35_MHz_XTAL / 5); // divider guessed
	m_maincpu->set_addrmap(AS_PROGRAM, &itt1700_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &itt1700_state::io_map);

	upi41_cpu_device &upi(I8741A(config, "upi", 16.6698_MHz_XTAL / 3)); // clock guessed
	upi.p1_out_cb().set("keyboard", FUNC(itt1700_keyboard_device::clock_w)).bit(0);
	upi.p1_out_cb().append("keyboard", FUNC(itt1700_keyboard_device::line1_w)).bit(1);
	upi.p1_out_cb().append("keyboard", FUNC(itt1700_keyboard_device::line2_w)).bit(2);
	// P20-P22 = PWM LEDs? (too high-frequency to be speaker output)
	upi.t0_in_cb().set("keyboard", FUNC(itt1700_keyboard_device::sense_r));

	ITT1700_KEYBOARD(config, "keyboard");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(16.6698_MHz_XTAL, 882, 0, 720, 315, 0, 300);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	hd6845s_device &crtc(HD6845S(config, "crtc", 16.6698_MHz_XTAL / 9)); // on video board
	crtc.set_char_width(9);
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_update_row_callback(FUNC(itt1700_state::update_row));
}

ROM_START(itt1700)
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("173562-007_2732_u32.bin", 0x0000, 0x1000, CRC(b1f4b349) SHA1(89ca344b89a0a746cdedcefdf5ff594287a5aa92))

	ROM_REGION(0x0400, "upi", 0)
	ROM_LOAD("173561-002_8741_u17.bin", 0x0000, 0x0400, CRC(7af333fa) SHA1(1edd77e9e32e3c7280f0906625e06718a0ba109f))

	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD("173563-001_2764_u56.bin", 0x0000, 0x2000, CRC(8ca58ab9) SHA1(b92e3985dd13afcf63dbb279f5fb9668d5eb645b))
ROM_END

} // anonymous namespace


COMP(1983, itt1700, 0, 0, itt1700, itt1700, itt1700_state, empty_init, "ITT Courier", "ITT 1700", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



itt3030.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont, Olivier Galibert
/***************************************************************************

    ITT 3030

    When the machine is started you get a black screen.
    Hold down B until the cursor appears. It will then boot from the floppy.

    ToDo:
    - Check Beeper
    - finish hooking up keyboard
    - serial port
    - daisy chain
    - ...


    CPU Board, all ICs shown:

    |-----------------------------------------------------------------|
    |                                                                 |
    |    74LS640N          Z80_Combo            74LS138N              |
    |                                                       74LS00N   |
    |    74LS240N                               74LS74AN              |
    |                                                       74LS00N   |
    |C   74LS240N          Z80_CPU              74LS240N             C|
    |N                                                      74LS74AN N|
    |1   74LS241N                               74LS240N             2|
    |                      ROM_1      74LS20N               74LS38N   |
    |    74LS240N                               74LS240N              |
    |                                 74LS04N               74LS02N   |
    |    74LS138N                               74LS74AN              |
    |                                                       74LS175N  |
    |    75154N        74LS156N                 74LS00N               |
    |                                                       74LS123N  |
    |    75150P 75150P 74LS175N   X1  74LS00N   74LS132N              |
    |-----------------------------------------------------------------|

    Z80_Combo:  Mostek MK3886 Z80 Combo Chip, Serial, Timer, 256 bytes RAM, Interrupt Controller
    Z80_CPU:    Zilog Z80A CPU
    ROM_1:      NEC D2716D marked "BOOTV1.2"
    X1:         Crystal 4,194 MHz
    CN1:        Bus Connector
    CN2:        Memory Board Connector

----------------------------------------------------------------------------------

    Video / Keyboard Combination board, all ICs shown:

    |-----------------------------------------------------------------|
    |                                                                 |
    |         X1     74276N      MCU_1                  74LS85N       |
    |                                                                 |
    |    74LS138N    74LS240                            74LS240N      |
    |                            75LS257AN  74LS166AN                 |
    |    74LS08N     74LS85N                            74LS241N      |
    |                            75LS257AN  ROM_1                    C|
    |    74LS132N    74LS32N                            74LS240N     N|
    |                            75LS257AN                           1|
    |    74LS10N     74LS08N                            74LS240N      |
    |                            75LS257AN  RAM_1                     |
    |    74LS163AN   74LS173AN                          74LS374N      |
    |                                                                 |
    |    74LS86N     74LS240N                           74LS640N      |
    |                            Video_1                              |
    |    74LS74AN    74LS240N                           74LS640N      |
    |-----------------------------------------------------------------|

    X1:     Crystal 6 MHz
    MCU_1:  NEC D8741AD marked "V1.1 3030"
    ROM_1:  MBM 2716 marked "GB 136-0"
    RAM_1:  NEC D4016D
    Video_1 Video-IC SND5027E, compatible with TMS9927

----------------------------------------------------------------------------------

    Floppy Controller board, all ICs shown

    |-----------------------------------------------------------------|
    |                                                                 |
    |  X1   74LS51N    F    74LS74AN   74LS02N        MC4044P         |
    |                  D                        567                   |
    |     74LS04N      C    74LS00N    74LS01N  :::   MC4024P         |
    |                                                                 |
    |   74LS00N        1    74LS74AN   74LS74AN       74LS14N        C|
    |                  7                                             N|
    |  74LS240N        9    74LS161N   74LS393N   74LS74AN           1|
    |                  1                                              |
    |   74LS132N            74LS14N    74LS14N    74LS374N            |
    |                                                                 |
    |    74LS123N   74LS04N  74LS163N   74LS14N    74LS241N           |
    |                                                                 |
    |     74LS393N   74LS138  74LS175N   74LS85N    74LS645N          |
    |                                                                 |
    |-----------------------------------------------------------------|

    X1:     Crystal 8 MHz
    FDC:    Siemens SAB1791-02P
    567:    Jumper Pad (emtpy)

----------------------------------------------------------------------------------

    256K RAM board, all ICs shown:

    |-----------------------------------------------------------------|
    |                                                                 |
    |   HM4864P   HM4864P   HM4864P   HM4864P       74LS245N          |
    |                                                                 |
    |   HM4864P   HM4864P   HM4864P   HM4864P       P     74LS14N     |
    |                                               R                 |
    |   HM4864P   HM4864P   HM4864P   HM4864P       M     74LS00N     |
    |                                                                C|
    |   HM4864P   HM4864P   HM4864P   HM4864P       AM         A     N|
    |                                               29         M     1|
    |   HM4864P   HM4864P   HM4864P   HM4864P       66         2      |
    |                                               PC         9      |
    |   HM4864P   HM4864P   HM4864P   HM4864P                  6      |
    |                                               AM         4      |
    |   HM4864P   HM4864P   HM4864P   HM4864P       29         8      |
    |                                               66         P      |
    |   HM4864P   HM4864P   HM4864P   HM4864P       PC         C      |
    |                                                      SN7474N    |
    |-----------------------------------------------------------------|

    PRM:    N82S129F 1K Bipolar PROM
            AM2966PC: Octal Dynamic Memory Drivers with Three-State Outputs
            AM29648PC
    CN1:    Connector to CN2 of Z80 CPU card

----------------------------------------------------------------------------------

    Parallel I/O board, all ICs shown:

    |-------------------------------------|                                                                 |
    |                                     |
    |  74   74                            |
    |  LS   LS        Z80A PIO            |
    |  00   14                            |
    |   N    N                            |
    |                                     |
    |                                     |
    |  74   74        74   74   D4   74   |
    |  LS   LS        LS   LS   I3   LS   |
    |  13   14        24   85   P2   64   |
    |  2N    N        1N    N    1   0N   |
    |                                     |
    |             CN1                     |
    |                                     |
    |             74LS00N                 |
    |-------------------------------------|

    CN1: Bus connector
    DIP: 4x DIP current setting: off-on-on-off, sets the address for the parallel port

----------------------------------------------------------------------------------

Beeper Circuit, all ICs shown:

    |---------------------------|                                                                 |
    |                           |
    |   BEEP       74LS132N     |
    | R1                        |
    |              74LS14N      |
    |                           |
    |   74LS132N   74LS193N     |
    |                           |
    |   74LS74AN   74LS165N     |
    |            CN1            |
    |---------------------------|

    CN1: Connector to mainboard
    R1:  looks like a potentiometer
    BEEP: Beeper ... touted in the manual as "Hupe" ... i.e. "horn" :)

----------------------------------------------------------------------------------

    Other boards and extensions mentioned in the manual:
    - S100 bus adapter board
    - IEEE 488 bus adapter board
    - 64K memory board
    - 8086 CPU board
    - external harddisk
    - TV adapter B/W (TV, Save/Load from Audio Cassette) with PROM/RAM/BASIC-Module with 16K or 32K RAM
    - TV adapter color with connection to Video / Keyboard combination card
    - Monitor adapters B/W and color
    - Video / Keyboard interface 2 with grayscale, 8 colors, loadable character set, blinking
    - Graphics Adapter with 16 colours, hi-res 512x256 pixels
    - RTC
    - Arithmetics chip

***************************************************************************/


#include "emu.h"

#include "cpu/mcs48/mcs48.h"        //Keyboard MCU ... talks to the 8278 on the keyboard circuit
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/bankdev.h"
#include "machine/ram.h"
#include "machine/wd_fdc.h"
#include "sound/beep.h"
#include "video/tms9927.h"          //Display hardware

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/itt3030_dsk.h"

#include "utf8.h"


namespace {

#define MAIN_CLOCK XTAL_4.194MHz

//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class itt3030_state : public driver_device
{
public:
	itt3030_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_kbdmcu(*this, "kbdmcu")
		, m_ram(*this, "mainram")
		, m_crtc(*this, "crt5027")
		, m_48kbank(*this, "lowerbank")
		, m_fdc (*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0)
		, m_beep(*this, "beeper")
		, m_keyrows(*this, "ROW.%u", 0)
		, m_vram(*this, "vram")
		, m_gfxdecode(*this, "gfxdecode")
		, m_palette(*this, "palette")
	{ }

	void itt3030(machine_config &config);

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// screen updates
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	uint8_t vsync_r();
	void beep_w(uint8_t data);
	void bank_w(uint8_t data);
	int kbd_matrix_r();
	void kbd_matrix_w(uint8_t data);
	uint8_t kbd_port2_r();
	void kbd_port2_w(uint8_t data);
	uint8_t fdc_r(offs_t offset);
	void fdc_w(offs_t offset, uint8_t data);
	uint8_t fdc_stat_r();
	void fdc_cmd_w(uint8_t data);
	static void itt3030_floppy_formats(format_registration &fr);

	void fdcirq_w(int state);
	void fdcdrq_w(int state);
	void fdchld_w(int state);
	void itt3030_palette(palette_device &palette) const;

	void itt3030_io(address_map &map) ATTR_COLD;
	void itt3030_map(address_map &map) ATTR_COLD;
	void lower48_map(address_map &map) ATTR_COLD;

	// devices
	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<i8741a_device> m_kbdmcu;
	required_device<ram_device> m_ram;
	required_device<crt5027_device> m_crtc;
	required_device<address_map_bank_device> m_48kbank;
	required_device<fd1791_device> m_fdc;
	required_device_array<floppy_connector, 3> m_floppy;
	required_device<beep_device> m_beep;

	required_ioport_array<16> m_keyrows;

	// shared pointers
	required_shared_ptr<uint8_t> m_vram;

	uint8_t m_kbdclk = 0, m_kbdread = 0, m_kbdport2 = 0;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<palette_device> m_palette;
	floppy_image_device *m_curfloppy = nullptr;
	bool m_fdc_irq = false, m_fdc_drq = false, m_fdc_hld = false;
};

//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

// The lower 48K is switchable among the first 48K of each of 8 48K banks numbered 0-7 or "bank 8" which is the internal ROM and VRAM
// The upper 16K is always the top 16K of the first bank, F5 can set this to 32K
// Port F6 bits 7-5 select banks 0-7, bit 4 enables bank 8

void itt3030_state::itt3030_map(address_map &map)
{
	map(0x0000, 0xbfff).m(m_48kbank, FUNC(address_map_bank_device::amap8));
}

void itt3030_state::lower48_map(address_map &map)
{
	map(0x60000, 0x607ff).rom().region("maincpu", 0);   // begin "page 8"
	map(0x60800, 0x60fff).rom().region("maincpu", 0);
	map(0x61000, 0x610ff).ram().mirror(0x100);  // only 256 bytes, but ROM also clears 11xx?
	map(0x63000, 0x63fff).ram().share("vram");
}

void itt3030_state::itt3030_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x20, 0x2f).rw(m_crtc, FUNC(crt5027_device::read), FUNC(crt5027_device::write));
	map(0x30, 0x31).rw(m_kbdmcu, FUNC(i8741a_device::upi41_master_r), FUNC(i8741a_device::upi41_master_w));
	map(0x32, 0x32).w(FUNC(itt3030_state::beep_w));
	map(0x35, 0x35).r(FUNC(itt3030_state::vsync_r));
	map(0x50, 0x53).rw(FUNC(itt3030_state::fdc_r), FUNC(itt3030_state::fdc_w));
	map(0x54, 0x54).rw(FUNC(itt3030_state::fdc_stat_r), FUNC(itt3030_state::fdc_cmd_w));
	map(0xf6, 0xf6).w(FUNC(itt3030_state::bank_w));
}


//**************************************************************************
//  INPUTS
//**************************************************************************

int itt3030_state::kbd_matrix_r()
{
	return m_kbdread;
}

void itt3030_state::kbd_matrix_w(uint8_t data)
{
//  printf("matrix_w: %02x (col %d row %d clk %d)\n", data, m_kbdcol, m_kbdrow, (data & 0x80) ? 1 : 0);

	if ((data & 0x80) && (!m_kbdclk))
	{
		const ioport_value tmp_read = m_keyrows[(data >> 3) & 0xf]->read() & (1 << (data & 0x7));
		m_kbdread = (tmp_read != 0) ? 1 : 0;
	}

	m_kbdclk = (data & 0x80) ? 1 : 0;
}

// bit 2 is UPI-41 host IRQ to Z80
void itt3030_state::kbd_port2_w(uint8_t data)
{
	m_kbdport2 = data;
}

uint8_t itt3030_state::kbd_port2_r()
{
	return m_kbdport2;
}


//**************************************************************************
//  KEYBOARD
//**************************************************************************

static INPUT_PORTS_START( itt3030 )
	PORT_START("ROW.0")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')

	PORT_START("ROW.1")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')

	PORT_START("ROW.2")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F6") PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('/')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')

	PORT_START("ROW.3")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F7") PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')

	PORT_START("ROW.4")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift Lock") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("ROW.5")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')

	PORT_START("ROW.6")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')   // actually UK pound symbol
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')

	PORT_START("ROW.7")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')

	PORT_START("ROW.8")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(R)") PORT_CODE(KEYCODE_F12) PORT_CHAR('=')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('~')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL) PORT_CHAR('^')    // PC doesn't have 3 keys to the right of L, so we sub DEL for the 3rd one
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("ROW.9")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(CL)") PORT_CODE(KEYCODE_F13) PORT_CHAR(4)    // produces control-D always
	PORT_BIT(0x001e, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("ROW.10")
	PORT_BIT(0x001f, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("ROW.11")
	PORT_BIT(0x001f, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("ROW.12")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F8") PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR(';')

	PORT_START("ROW.13")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_F9)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('=')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(':')

	PORT_START("ROW.14")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_F10)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('@') PORT_CHAR('?')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('<') PORT_CHAR('>')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('-') PORT_CHAR('`')

	PORT_START("ROW.15")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_F11) PORT_CHAR(27)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('/') PORT_CHAR('\\')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('+') PORT_CHAR('*')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_MAMEKEY(RSHIFT))
INPUT_PORTS_END


//**************************************************************************
//  VIDEO
//**************************************************************************

uint8_t itt3030_state::vsync_r()
{
	uint8_t ret = 0;

	if (m_screen->vblank())
	{
		ret |= 0xc0;    // set both bits 6 and 7 if vblank
	}

	if (m_screen->hblank())
	{
		ret |= 0x80;    // set only bit 7 if hblank
	}

	return ret;
}

void itt3030_state::bank_w(uint8_t data)
{
	int bank = 8;

	if (BIT(data, 4))
	{
		bank = (BIT(data, 5) << 2) | (BIT(data, 6) << 1) | BIT(data, 7);
	}

	//  printf("bank_w: new value %02x, m_bank %x, bank %x\n", data, m_bank, bank);

	m_48kbank->set_bank(bank);
}

uint32_t itt3030_state::screen_update( screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	int start = m_crtc->upscroll_offset();
	for(int y = 0; y < 24; y++ )
	{
		int vramy = (start + y) % 24;
		for(int x = 0; x < 80; x++ )
		{
			uint8_t code = m_vram[x + vramy*128];
			int invert = code & 0x80 ? 1 : 0;
			code &= 0x7f;
			m_gfxdecode->gfx(invert)->opaque(bitmap,cliprect,  code , 0, 0,0, x*8,y*12);
		}
	}

	return 0;
}

static const gfx_layout charlayout =
{
	8, 16,              /* 8x16 characters */
	128,                /* 128 characters */
	1,                /* 1 bits per pixel */
	{0},                /* no bitplanes; 1 bit per pixel */
	{7, 6, 5, 4, 3, 2, 1, 0},
	{ 0*8,  1*8,  2*8,  3*8,  4*8,  5*8,  6*8,  7*8,
		8*8,  9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16                    /* size of one char */
};

static GFXDECODE_START( gfx_itt3030 )
	GFXDECODE_ENTRY( "gfx1", 0, charlayout,     0, 1 )
	GFXDECODE_ENTRY( "gfx1", 0, charlayout,     0, 1 )
GFXDECODE_END

void itt3030_state::itt3030_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t::black());
	palette.set_pen_color(1, rgb_t(215, 229, 82));
	palette.set_pen_color(2, rgb_t::black());
}

//**************************************************************************
//  SOUND
//**************************************************************************

void itt3030_state::beep_w(uint8_t data)
{
	m_beep->set_state(data&1);
}

//**************************************************************************
//  FLOPPY
//**************************************************************************

void itt3030_state::fdcirq_w(int state)
{
	m_fdc_irq = state;
}


void itt3030_state::fdcdrq_w(int state)
{
	m_fdc_drq = state;
}

void itt3030_state::fdchld_w(int state)
{
	m_fdc_hld = state;
}

/*
    7 Data Request (DRQ - inverted 1791-Signal)
    6 Interrupt Request (INTRQ - 1791-Signal)
    5 Head Load (HLD - inverted 1791-Signal)
    4 Ready 3 (Drive 3 ready)
    3 Ready 2 (Drive 2 ready)
    2 Ready l (Drive 1 ready)
    1 Write protect (the disk in the selected drive is write protected)
    0 HLT (Halt signal during head load and track change)
*/
uint8_t itt3030_state::fdc_stat_r()
{
	uint8_t res = 0;
	floppy_image_device *floppy1 = m_floppy[0] ? m_floppy[0]->get_device() : nullptr;
	floppy_image_device *floppy2 = m_floppy[1] ? m_floppy[1]->get_device() : nullptr;
	floppy_image_device *floppy3 = m_floppy[2] ? m_floppy[2]->get_device() : nullptr;

	res = m_fdc_drq ? 0x80 : 0x00;
	res |= m_fdc_irq ? 0x40 : 0x00;
	res |= m_fdc_hld ? 0x00 : 0x20;
	if (floppy3) res |= !floppy3->ready_r() ? 0x10 : 0;
	if (floppy2) res |= !floppy2->ready_r() ? 0x08 : 0;
	if (floppy1) res |= !floppy1->ready_r() ? 0x04 : 0;
	if (m_curfloppy) res |= m_curfloppy->wpt_r() ? 0x02 : 0;

	return res;
}

/* As far as we can tell, the mess of ttl de-inverts the bus */
uint8_t itt3030_state::fdc_r(offs_t offset)
{
	return m_fdc->read(offset) ^ 0xff;
}

void itt3030_state::fdc_w(offs_t offset, uint8_t data)
{
	m_fdc->write(offset, data ^ 0xff);
}

/*
    7 SEL1 - Select drive 1
    6 SEL2 - Select drive 2
    5 SEL3 - Select drive 3
    4 MOTOR - Motor on
    3 DOOR - Drive door lock drives 1 + 2 (not possible with all drives)
    2 SIDESEL - Select disk side
    1 KOMP - write comp on/off
    0 RG J - Change separator stage to read
*/
void itt3030_state::fdc_cmd_w(uint8_t data)
{
	floppy_image_device *floppy = nullptr;

	logerror("%02x to fdc_cmd_w: motor %d side %d\n", data, (data & 0x10)>>4, (data & 4)>>2);

	// select drive
	if (data & 0x80)
	{
		floppy = m_floppy[0] ? m_floppy[0]->get_device() : nullptr;
	}
	else if (data & 0x40)
	{
		floppy = m_floppy[1] ? m_floppy[1]->get_device() : nullptr;
	}
	else if (data & 0x20)
	{
		floppy = m_floppy[2] ? m_floppy[2]->get_device() : nullptr;
	}

	// selecting a new drive?
	if (floppy != m_curfloppy)
	{
		m_fdc->set_floppy(floppy);
		m_curfloppy = floppy;
	}

	if (floppy != nullptr)
	{
		// side select
		floppy->ss_w((data & 4) ? 1 : 0);

		// motor control (active low)
		floppy->mon_w((data & 0x10) ? 0 : 1);
	}
}

//**************************************************************************
//  FLOPPY - Drive definitions
//**************************************************************************

void itt3030_state::itt3030_floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_ITT3030_FORMAT);
}


static void itt3030_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
	device.option_add("525qd", FLOPPY_525_QD);
}




//**************************************************************************
//  MACHINE
//**************************************************************************

void itt3030_state::machine_start()
{
	save_item(NAME(m_kbdread));
	m_48kbank->space(AS_PROGRAM).install_ram(0, m_ram->size() - 16384, m_ram->pointer());
	m_maincpu->space(AS_PROGRAM).install_ram(0xc000, 0xffff, m_ram->pointer() + m_ram->size() - 16384);
	m_gfxdecode->gfx(1)->set_colorbase(1);

	m_kbdclk = 0;   // must be initialized here b/c mcs48_reset() causes write of 0xff to all ports
}

void itt3030_state::machine_reset()
{
	m_48kbank->set_bank(8);
	m_kbdread = 1;
	m_kbdclk = 1;
	m_fdc_irq = m_fdc_drq = m_fdc_hld = 0;
	m_curfloppy = nullptr;
}

void itt3030_state::itt3030(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &itt3030_state::itt3030_map);
	m_maincpu->set_addrmap(AS_IO, &itt3030_state::itt3030_io);

	// Schematics + i8278 datasheet says:
	// Port 1 goes to the keyboard matrix.
	// bits 0-2 select bit to read back, bits 3-6 choose column to read from, bit 7 clocks the process (rising edge strobes the row, falling edge reads the data)
	// T0 is the key matrix return
	// pin 23 is the UPI-41 host IRQ line, it's unknown how it's connected to the Z80
	I8741A(config, m_kbdmcu, 6_MHz_XTAL);
	m_kbdmcu->t0_in_cb().set(FUNC(itt3030_state::kbd_matrix_r));
	m_kbdmcu->p1_out_cb().set(FUNC(itt3030_state::kbd_matrix_w));
	m_kbdmcu->p2_in_cb().set(FUNC(itt3030_state::kbd_port2_r));
	m_kbdmcu->p2_out_cb().set(FUNC(itt3030_state::kbd_port2_w));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(250));
	m_screen->set_screen_update(FUNC(itt3030_state::screen_update));
	m_screen->set_size(80*8, 24*12);
	m_screen->set_visarea(0, 80*8-1, 0, 24*12-1);
	m_screen->set_palette(m_palette);

	/* devices */
	ADDRESS_MAP_BANK(config, "lowerbank").set_map(&itt3030_state::lower48_map).set_options(ENDIANNESS_LITTLE, 8, 20, 0xc000);

	CRT5027(config, m_crtc, 6_MHz_XTAL / 8).set_char_width(8);

	FD1791(config, m_fdc, 20_MHz_XTAL / 20);
	m_fdc->intrq_wr_callback().set(FUNC(itt3030_state::fdcirq_w));
	m_fdc->drq_wr_callback().set(FUNC(itt3030_state::fdcdrq_w));
	m_fdc->hld_wr_callback().set(FUNC(itt3030_state::fdchld_w));
	FLOPPY_CONNECTOR(config, "fdc:0", itt3030_floppies, "525qd", itt3030_state::itt3030_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", itt3030_floppies, "525qd", itt3030_state::itt3030_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:2", itt3030_floppies, "525qd", itt3030_state::itt3030_floppy_formats);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_itt3030);

	PALETTE(config, m_palette, FUNC(itt3030_state::itt3030_palette), 3);

	/* internal ram */
	RAM(config, "mainram").set_default_size("256K");

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 3250).add_route(ALL_OUTPUTS, "mono", 1.00);

	SOFTWARE_LIST(config, "flop_list").set_original("itt3030");
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( itt3030 )
	ROM_REGION( 0x0800, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "bootv1.2.bin", 0x0000, 0x0800, CRC(90279d45) SHA1(a39a3f31f4f98980b1ef50805870837fbf72261d))
	ROM_REGION( 0x0800, "gfx1", ROMREGION_ERASE00 )
	ROM_LOAD( "gb136-0.bin", 0x0000, 0x0800, CRC(6a3895a8) SHA1(f3b977ffa2f54c346521c9ef034830de8f404621))
	ROM_REGION( 0x0400, "kbdmcu", ROMREGION_ERASE00 )
	ROM_LOAD( "8741ad.bin", 0x0000, 0x0400, CRC(cabf4394) SHA1(e5d1416b568efa32b578ca295a29b7b5d20c0def))
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

COMP( 1982, itt3030, 0, 0, itt3030, itt3030, itt3030_state, empty_init, "ITT RFA", "ITT3030", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



itt9216.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

Skeleton driver for ITT Courier 9216 IBM 3179-compatible color display terminal.

************************************************************************************************************************************/

#include "emu.h"
#include "cpu/m68000/m68008.h"
#include "cpu/mcs48/mcs48.h"


namespace {

class itt9216_state : public driver_device
{
public:
	itt9216_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_program(*this, "program")
		, m_chargen(*this, "chargen")
	{
	}

	void itt9216(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u8 ram_rom_r(offs_t offset);
	void ram_w(offs_t offset, u8 data);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_program;
	required_region_ptr<u8> m_chargen;

	std::unique_ptr<u8[]> m_mainram;
	bool m_rom_enabled = true;
};

void itt9216_state::machine_start()
{
	m_mainram = make_unique_clear<u8[]>(0x10000);

	save_item(NAME(m_rom_enabled));
	save_pointer(NAME(m_mainram), 0x10000);
}

void itt9216_state::machine_reset()
{
	m_rom_enabled = true;
}

u8 itt9216_state::ram_rom_r(offs_t offset)
{
	if (m_rom_enabled)
		return m_program[offset & 0x1fff];
	else
		return m_mainram[offset];
}

void itt9216_state::ram_w(offs_t offset, u8 data)
{
	m_mainram[offset] = data;
	if (!machine().side_effects_disabled())
		m_rom_enabled = false;
}

void itt9216_state::mem_map(address_map &map)
{
	map(0x00000, 0x0ffff).rw(FUNC(itt9216_state::ram_rom_r), FUNC(itt9216_state::ram_w));
	map(0x10000, 0x11fff).ram();
	map(0x58000, 0x59fff).rom().region("program", 0);
	map(0x62000, 0x62fff).ram();
}

static INPUT_PORTS_START(itt9216)
INPUT_PORTS_END

void itt9216_state::itt9216(machine_config &config)
{
	M68008(config, m_maincpu, 8000000); // clock unknown
	m_maincpu->set_addrmap(AS_PROGRAM, &itt9216_state::mem_map);

	I8741A(config, "upi", 6000000).set_disable(); // clock unknown
}

// XTALs: 38.080 MHz (U7), 11.250(?) MHz (U1)
// ICs on main board: MC68008P8 (U33), 701188-001 PLCC ASIC (U58), empty PLCC (U45), beeper
// ICs on keyboard simulator board: 700881-001 PLCC ASIC (U12), DP8340N (U16), DP8341N (U15), D8741A (U1)
ROM_START(itt9216)
	ROM_REGION(0x2000, "program", 0)
	ROM_LOAD("174054-007.u52", 0x0000, 0x2000, CRC(be1f85c8) SHA1(8c44ff6166c43b524f41133053fa82f5c48047d8))

	ROM_REGION(0x400, "upi", 0)
	ROM_LOAD("174065-003.u1", 0x000, 0x400, NO_DUMP)

	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD("174055-004.u40", 0x0000, 0x2000, CRC(c8611425) SHA1(31fbdd6ff72a96c59277b6edac9a6360f6e1e49e))
ROM_END

} // anonymous namespace


COMP(1986, itt9216, 0, 0, itt9216, itt9216, itt9216_state, empty_init, "ITT Courier", "ITT 9216-X", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



ivant.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Excalibur Ivan The Terrible (model 701E, H8/3216 version)
Excalibur Igor (model 711E)

This is the newer version of Ivan, see ivanto.cpp for the first version. It's
on very different hardware. Manufacturing was moved to Ewig. Ivan's model number
is the same as the first version, but it's easy to distinguish between them.

The chess engine is by Ron Nelson, similar to the one in Excalibur Mirage. It
has speech, and also sound effects that are reminiscent of Battle Chess.

Hardware notes:
- PCB label: EXCALIBUR ELECTRONICS, INC. 4/18/97, IVANT2, 00-33352-000
- Hitachi H8/3216 MCU (only 32KB out of 48KB internal ROM used), 12MHz XTAL
- small daughterboard (27C080 pinout) with an 1MB ROM under epoxy, contents is
  identical to the 1st version of Ivan after unscrambling
- 8-bit DAC (Yageo 10L503G resistor array), KA8602 amplifier
- LCD with 5 7segs and custom segments
- no LEDs, button sensors chessboard

Igor is on the same base hardware. The PCB (label: EXCALIBUR ELECTRONICS, INC.
510-1005A01, 5/28/97 IGOR) looks a bit different, but the connections are the
same as Ivan. It has a H8/3214 MCU and the sound ROM is 128KB instead of 1MB.

There's also a newer version of Igor from 2000 (model 711E-2) on much weaker
hardware, it has a Samsung KS57C2308 MCU instead.

TODO:
- it does a cold boot at every reset, so nvram won't work properly unless MAME
  adds some kind of auxillary autosave state feature at power-off

BTANB:
- speech sound is a bit scratchy, it has background noise like a tape recorder

*******************************************************************************/

#include "emu.h"

#include "cpu/h8/h83217.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "excal_igor.lh"
#include "excal_ivant.lh"


namespace {

class ivant_state : public driver_device
{
public:
	ivant_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_soundrom(*this, "soundrom"),
		m_lcd_pwm(*this, "lcd_pwm"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_out_lcd(*this, "s%u.%u", 0U, 0U)
	{ }

	void init_ivant();

	template <typename T> void cpu_config(T &maincpu);
	void shared(machine_config &config);
	void ivant(machine_config &config);
	void igor(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<h8_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_region_ptr<u8> m_soundrom;
	required_device<pwm_display_device> m_lcd_pwm;
	required_device<dac_8bit_r2r_device> m_dac;
	required_ioport_array<2> m_inputs;
	output_finder<2, 24> m_out_lcd;

	u8 m_lcd_com = 0;
	u8 m_dac_data = 0;
	emu_timer *m_irqtimer;

	u8 m_port1 = 0xff;
	u8 m_port2 = 0xff;
	u8 m_port5 = 0xff;
	u8 m_port7 = 0xff;

	// I/O handlers
	void lcd_pwm_w(offs_t offset, u8 data);
	void update_lcd();
	void update_dac();
	u8 read_inputs();

	TIMER_CALLBACK_MEMBER(update_irq) { read_inputs(); }

	void p1_w(u8 data);
	void p2_w(u8 data);
	u8 p3_r();
	void p4_w(u8 data);
	u8 p5_r();
	void p5_w(offs_t offset, u8 data, u8 mem_mask);
	u8 p6_r();
	void p7_w(u8 data);
};



/*******************************************************************************
    Initialization
*******************************************************************************/

void ivant_state::init_ivant()
{
	u8 *rom = memregion("soundrom")->base();
	const u32 len = memregion("soundrom")->bytes();

	// descramble data lines
	for (int i = 0; i < len; i++)
		rom[i] = bitswap<8>(rom[i], 2,3,1,4,0,5,6,7);

	// descramble address lines
	std::vector<u8> buf(len);
	memcpy(&buf[0], rom, len);
	for (int i = 0; i < len; i++)
		rom[i] = buf[bitswap<20>(i,19,18,17,16, 15,14,12,13,6,3,8,10, 11,9,7,5,4,2,1,0)];
}

void ivant_state::machine_start()
{
	m_out_lcd.resolve();

	// periodically check for interrupts
	m_irqtimer = timer_alloc(FUNC(ivant_state::update_irq), this);
	attotime period = attotime::from_msec(1);
	m_irqtimer->adjust(period, 0, period);

	// register for savestates
	save_item(NAME(m_lcd_com));
	save_item(NAME(m_dac_data));
	save_item(NAME(m_port1));
	save_item(NAME(m_port2));
	save_item(NAME(m_port5));
	save_item(NAME(m_port7));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void ivant_state::lcd_pwm_w(offs_t offset, u8 data)
{
	m_out_lcd[offset & 0x3f][offset >> 6] = data;
}

void ivant_state::update_lcd()
{
	u32 lcd_segs = bitswap<8>(m_port1,0,1,2,3,4,5,6,7) << 16 | bitswap<8>(m_port2,0,1,2,3,4,5,6,7) << 8 | m_port7;

	for (int i = 0; i < 2; i++)
	{
		// LCD common is 0/1/Hi-Z
		const u32 data = BIT(m_lcd_com, i + 2) ? (BIT(m_lcd_com, i) ? ~lcd_segs : lcd_segs) : 0;
		m_lcd_pwm->write_row(i, data);
	}
}

void ivant_state::update_dac()
{
	m_dac->write((m_port5 & 4) ? 0x80 : m_dac_data);
}

u8 ivant_state::read_inputs()
{
	u8 data = 0;

	// get input mux from P2/P7/P5
	u16 inp_mux = BIT(~m_port5, 5) << 9 | bitswap<8>(~m_port7,0,1,7,6,5,4,3,2) << 1 | BIT(~m_port2, 6);

	// read buttons
	for (int i = 0; i < 2; i++)
		if (BIT(inp_mux, i + 8))
			data |= m_inputs[i]->read();

	// read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(inp_mux, i))
			data |= m_board->read_file(i, true);

	// P64-P66 are also IRQ pins (the ON button is IRQ0)
	if (!machine().side_effects_disabled())
		for (int i = 0; i < 3; i++)
			m_maincpu->set_input_line(INPUT_LINE_IRQ0 + i, BIT(data, i + 4) ? ASSERT_LINE : CLEAR_LINE);

	return ~data;
}

void ivant_state::p1_w(u8 data)
{
	// P10-P17: sound ROM address + LCD segs
	m_port1 = data;
}

void ivant_state::p2_w(u8 data)
{
	// P20-P27: sound ROM address + LCD segs
	// P26: input mux low bit
	m_port2 = data;
	read_inputs();
}

u8 ivant_state::p3_r()
{
	// P30-P37: read sound ROM
	u32 address = bitswap<4>(m_port7,4,7,6,5) << 16 | m_port2 << 8 | m_port1;
	return (m_port5 & 4) ? 0xff : m_soundrom[address & (m_soundrom.bytes() - 1)];
}

void ivant_state::p4_w(u8 data)
{
	// P40-P47 (not P46): DAC data
	m_dac_data = (m_dac_data & 0x40) | (data & 0xbf);
	update_dac();
}

u8 ivant_state::p5_r()
{
	// P50: multiplexed inputs high bit
	return read_inputs() >> 7 | 0xfe;
}

void ivant_state::p5_w(offs_t offset, u8 data, u8 mem_mask)
{
	// P51: DAC bit 6
	m_dac_data = (m_dac_data & 0xbf) | BIT(data, 1) << 6;

	// P52: sound ROM CS, KA8602 mute
	// P55: input mux high bit
	m_port5 = data;
	update_dac();
	read_inputs();

	// P53,P54: LCD common
	u8 lcd_com = (mem_mask >> 1 & 0xc) | (data >> 3 & 3);
	if (lcd_com != m_lcd_com)
	{
		m_lcd_com = lcd_com;
		update_lcd();
	}
}

u8 ivant_state::p6_r()
{
	// P60-P66: multiplexed inputs part
	return read_inputs() | 0x80;
}

void ivant_state::p7_w(u8 data)
{
	// P70-P77: input mux part + LCD segs
	// P74-P77: sound ROM address
	m_port7 = data;
	read_inputs();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( ivant )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_CODE(KEYCODE_LEFT) PORT_NAME("No / Left")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Repeat")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Verify / Queen")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Option")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("Black / White")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Yes / Right")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Mode")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Set Up / King")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Multi-Move / Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F2) PORT_NAME("Off / Save")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F1) PORT_CODE(KEYCODE_C) PORT_NAME("On / Clear")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Move / Pawn")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Level / Rook")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Hint / Knight")

	PORT_START("BATT")
	PORT_CONFNAME( 0x40, 0x00, "Battery Status" )
	PORT_CONFSETTING(    0x40, "Low" )
	PORT_CONFSETTING(    0x00, DEF_STR( Normal ) )
	PORT_BIT(0xbf, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( igor ) // same buttons, different layout
	PORT_INCLUDE( ivant )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Takeback")

	PORT_MODIFY("IN.1")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Multi-Move / Bishop")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Level / Rook")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Hint / Knight")

	PORT_MODIFY("BATT") // read, but discarded
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

template <typename T>
void ivant_state::cpu_config(T &maincpu)
{
	maincpu.nvram_enable_backup(true);
	maincpu.standby_cb().set(maincpu, FUNC(T::nvram_set_battery));
	maincpu.standby_cb().append([this](int state) { if (state) m_lcd_pwm->clear(); });
	maincpu.write_port1().set(FUNC(ivant_state::p1_w));
	maincpu.write_port2().set(FUNC(ivant_state::p2_w));
	maincpu.read_port3().set(FUNC(ivant_state::p3_r));
	maincpu.read_port4().set_ioport("BATT").invert();
	maincpu.write_port4().set(FUNC(ivant_state::p4_w));
	maincpu.read_port5().set(FUNC(ivant_state::p5_r));
	maincpu.write_port5().set(FUNC(ivant_state::p5_w));
	maincpu.read_port6().set(FUNC(ivant_state::p6_r));
	maincpu.write_port7().set(FUNC(ivant_state::p7_w));
}

void ivant_state::shared(machine_config &config)
{
	// basic machine hardware
	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	//m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_lcd_pwm).set_size(2, 24);
	m_lcd_pwm->output_x().set(FUNC(ivant_state::lcd_pwm_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/6, 723/6);
	screen.set_visarea_full();

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_8BIT_R2R(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.5);
}

void ivant_state::ivant(machine_config &config)
{
	H83216(config, m_maincpu, 12_MHz_XTAL);
	cpu_config<h83216_device>(downcast<h83216_device &>(*m_maincpu));

	shared(config);

	config.set_default_layout(layout_excal_ivant);
}

void ivant_state::igor(machine_config &config)
{
	H83214(config, m_maincpu, 12_MHz_XTAL);
	cpu_config<h83214_device>(downcast<h83214_device &>(*m_maincpu));

	shared(config);

	config.set_default_layout(layout_excal_igor);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( ivant )
	ROM_REGION16_BE( 0xc000, "maincpu", 0 )
	ROM_LOAD("1997_rcn_1003a_excal_hd6433216l01p.ic1", 0x0000, 0xc000, CRC(6fcc34b1) SHA1(13bcb3d6766e6f3acb7d0f669337e8e40d5ed449) )

	ROM_REGION( 0x100000, "soundrom", 0 )
	ROM_LOAD("sound.ic2", 0x000000, 0x100000, CRC(7f9a78c9) SHA1(b80d33955496698c9288047e0854b335882bcdc7) ) // no label

	ROM_REGION( 89047, "screen", 0 )
	ROM_LOAD("ivant.svg", 0, 89047, CRC(fe514f65) SHA1(da5a56882bd241d01a6c49cb1cb066b88473c445) )
ROM_END

ROM_START( igor )
	ROM_REGION16_BE( 0x8000, "maincpu", 0 )
	ROM_LOAD("1997_rcn_1002a_excal_hd6433214l02p.ic1", 0x0000, 0x8000, CRC(adbc7e07) SHA1(0d297ad2fd0d18312966195cfad4658da4bc4442) )

	ROM_REGION( 0x20000, "soundrom", 0 )
	ROM_LOAD("sound.ic2", 0x00000, 0x20000, CRC(bc540da3) SHA1(68647ce1c7e87eba90d9d1912921213af03e3c5d) ) // no label

	ROM_REGION( 89047, "screen", 0 )
	ROM_LOAD("ivant.svg", 0, 89047, CRC(fe514f65) SHA1(da5a56882bd241d01a6c49cb1cb066b88473c445) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY, FULLNAME, FLAGS
SYST( 1997, ivant, 0,      0,      ivant,   ivant, ivant_state, init_ivant, "Excalibur Electronics", "Ivan The Terrible (H8/3216 version)", MACHINE_SUPPORTS_SAVE )

SYST( 1997, igor,  0,      0,      igor,    igor,  ivant_state, init_ivant, "Excalibur Electronics", "Igor (Excalibur)", MACHINE_SUPPORTS_SAVE )



ivanto.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Excalibur Ivan The Terrible (model 701E, H8/3256 version)

This is the first version, see ivant.cpp for the newer version. It was produced
in a factory owned by Eric White's company (ex-CXG), hence it's not that strange
that the LCD is the same as the one in CXG Sphinx Legend and Krypton Challenge.
Ron Nelson was reluctant about it, since Eric White was the same person that
bootlegged his Fidelity Chess Challenger 10.

Hardware notes:
- PCB label: EXCALIBUR ELECTRONICS, INC. 6/28/96, IVANT
- Hitachi H8/3256 MCU (mode 1), 20MHz XTAL
- 8-bit DAC (8L503 resistor array), KA8602 amplifier, 1MB ROM (custom label)
- LCD with 5 7segs and custom segments
- no LEDs, button sensors chessboard

The MCU used here is a HD6433256A33P from Excalibur Mirage, the internal ROM
was disabled. It runs at a higher frequency than the H8/3216 version, but
is actually a bit slower due to the H8/325 /2 clock divider.

TODO:
- it does a cold boot at every reset, so nvram won't work properly unless MAME
  adds some kind of auxillary autosave state feature at power-off

BTANB:
- speech sound is scratchy (worse than ivant), it has spikes here and there,
  verified with a digital capture, final (analog) output on the real thing
  sounds a bit better than MAME though

*******************************************************************************/

#include "emu.h"

#include "cpu/h8/h8325.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "excal_ivanto.lh"


namespace {

class ivanto_state : public driver_device
{
public:
	ivanto_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_rombank(*this, "rombank"),
		m_board(*this, "board"),
		m_lcd_pwm(*this, "lcd_pwm"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_out_lcd(*this, "s%u.%u", 0U, 0U)
	{ }

	void ivanto(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD { m_dac->write(0x80); }

private:
	// devices/pointers
	required_device<h83256_device> m_maincpu;
	required_memory_bank m_rombank;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_lcd_pwm;
	required_device<dac_8bit_r2r_device> m_dac;
	required_ioport_array<2> m_inputs;
	output_finder<2, 24> m_out_lcd;

	u16 m_inp_mux = 0;
	u32 m_latch = 0;
	u32 m_lcd_segs = 0;
	u8 m_lcd_com = 0;
	emu_timer *m_irqtimer;

	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void lcd_pwm_w(offs_t offset, u8 data);
	void update_lcd();
	void lcd_com_w(offs_t offset, u8 data, u8 mem_mask);

	void latch_w(offs_t offset, u8 data);
	u8 read_inputs();

	TIMER_CALLBACK_MEMBER(update_irq) { read_inputs(); }

	u8 p4_r();
	void p4_w(u8 data);
	void p5_w(u8 data);
	u8 p6_r();
	void p6_w(u8 data);
};

void ivanto_state::machine_start()
{
	m_out_lcd.resolve();
	m_rombank->configure_entries(0, 64, memregion("rombank")->base(), 0x4000);

	// periodically check for interrupts
	m_irqtimer = timer_alloc(FUNC(ivanto_state::update_irq), this);
	attotime period = attotime::from_msec(1);
	m_irqtimer->adjust(period, 0, period);

	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_latch));
	save_item(NAME(m_lcd_segs));
	save_item(NAME(m_lcd_com));
}



/*******************************************************************************
    I/O
*******************************************************************************/

// LCD

void ivanto_state::lcd_pwm_w(offs_t offset, u8 data)
{
	m_out_lcd[offset & 0x3f][offset >> 6] = data;
}

void ivanto_state::update_lcd()
{
	for (int i = 0; i < 2; i++)
	{
		// LCD common is 0/1/Hi-Z
		const u32 data = BIT(m_lcd_com, i + 2) ? (BIT(m_lcd_com, i) ? ~m_lcd_segs : m_lcd_segs) : 0;
		m_lcd_pwm->write_row(i, data);
	}
}

void ivanto_state::lcd_com_w(offs_t offset, u8 data, u8 mem_mask)
{
	// P70,P71: LCD common
	m_lcd_com = (mem_mask << 2 & 0xc) | (data & 3);
	update_lcd();
}


// misc

void ivanto_state::latch_w(offs_t offset, u8 data)
{
	// a0-a2,d0-d3: 4*74259
	u32 mask = 1 << offset;
	for (int i = 0; i < 4; i++)
	{
		m_latch = (m_latch & ~mask) | (BIT(data, i) ? mask : 0);
		mask <<= 8;
	}

	// 74259(all) Q0,Q1: DAC data
	m_dac->write(bitswap<8>(m_latch,0,8,16,24,1,9,17,25));

	// 74259(all) Q2-Q7: LCD segments
	m_lcd_segs = 0;
	for (int i = 0; i < 4; i++)
		m_lcd_segs |= (m_latch >> (i * 8 + 2) & 0x3f) << (i * 6);

	update_lcd();

	// 74259(2) Q4-Q7, 74259(3) Q2-Q6: input mux low
	m_inp_mux = (m_inp_mux & 0x200) | (~m_latch >> 22 & 0x1f0) | (~m_latch >> 20 & 0xf);
	read_inputs();
}

u8 ivanto_state::read_inputs()
{
	u8 data = 0;

	// read buttons
	for (int i = 0; i < 2; i++)
		if (BIT(m_inp_mux, i + 8))
			data |= m_inputs[i]->read();

	// read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_file(i, true);

	// P64-P66 are also IRQ pins (the ON button is IRQ0)
	if (!machine().side_effects_disabled())
		for (int i = 0; i < 3; i++)
			m_maincpu->set_input_line(INPUT_LINE_IRQ0 + i, BIT(data, i + 4) ? ASSERT_LINE : CLEAR_LINE);

	return ~data;
}

u8 ivanto_state::p4_r()
{
	// P47: multiplexed inputs high bit
	return (read_inputs() & 0x80) | 0x7f;
}

void ivanto_state::p4_w(u8 data)
{
	// P40-P45: ROM bank
	m_rombank->set_entry(~data & 0x3f);
}

void ivanto_state::p5_w(u8 data)
{
	// P54: KA8602 mute
	m_dac->set_output_gain(0, (data & 0x10) ? 0.0 : 1.0);

	// P55: input mux high bit
	m_inp_mux = (m_inp_mux & 0x1ff) | BIT(~data, 5) << 9;
	read_inputs();
}

u8 ivanto_state::p6_r()
{
	// P60-P66: multiplexed inputs part
	return read_inputs() | 0x80;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void ivanto_state::main_map(address_map &map)
{
	map(0x0000, 0x0007).mirror(0xfff8).w(FUNC(ivanto_state::latch_w));
	map(0x0000, 0x7fff).rom();
	map(0x8000, 0xbfff).bankr(m_rombank);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( ivanto )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_CODE(KEYCODE_LEFT) PORT_NAME("No / Left")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Repeat")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Mode")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Option")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("Black / White")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Yes / Right")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Set Up / King")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Verify / Queen")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Multi-Move / Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F2) PORT_NAME("Off / Save")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F1) PORT_CODE(KEYCODE_C) PORT_NAME("On / Clear")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Move / Pawn")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Hint / Knight")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Level / Rook")

	PORT_START("BATT")
	PORT_CONFNAME( 0x08, 0x00, "Battery Status" )
	PORT_CONFSETTING(    0x08, "Low" )
	PORT_CONFSETTING(    0x00, DEF_STR( Normal ) )
	PORT_BIT(0xf7, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void ivanto_state::ivanto(machine_config &config)
{
	// basic machine hardware
	H83256(config, m_maincpu, 20_MHz_XTAL);
	m_maincpu->set_mode(1);
	m_maincpu->set_addrmap(AS_PROGRAM, &ivanto_state::main_map);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(h83256_device::nvram_set_battery));
	m_maincpu->standby_cb().append([this](int state) { if (state) m_lcd_pwm->clear(); });
	m_maincpu->read_port4().set(FUNC(ivanto_state::p4_r));
	m_maincpu->write_port4().set(FUNC(ivanto_state::p4_w));
	m_maincpu->write_port5().set(FUNC(ivanto_state::p5_w));
	m_maincpu->read_port6().set(FUNC(ivanto_state::p6_r));
	m_maincpu->read_port7().set_ioport("BATT").invert();
	m_maincpu->write_port7().set(FUNC(ivanto_state::lcd_com_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	//m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_lcd_pwm).set_size(2, 24);
	m_lcd_pwm->output_x().set(FUNC(ivanto_state::lcd_pwm_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/5, 697/5);
	screen.set_visarea_full();

	config.set_default_layout(layout_excal_ivanto);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_8BIT_R2R(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.5);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( ivanto )
	ROM_REGION16_BE( 0x8000, "maincpu", 0 )
	ROM_LOAD("at27c256r.ic11", 0x0000, 0x8000, CRC(51a97959) SHA1(ea595001fd5eaa07ade96307a8d89d5fb44682ab) ) // no label

	ROM_REGION16_BE( 0x100000, "rombank", 0 )
	ROM_LOAD("1996_701e_excalibur_ivan_t.ic2", 0x000000, 0x100000, CRC(f7c386a1) SHA1(357ca0b0c0b1409d33876b6fa00c5ad74b2643fc) )

	ROM_REGION( 109652, "screen", 0 )
	ROM_LOAD("regency.svg", 0, 109652, CRC(6840c49e) SHA1(a9c91143c5bea5ab41fe323e719da4a46ab9d631) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1996, ivanto, ivant,  0,      ivanto,  ivanto, ivanto_state, empty_init, "Excalibur Electronics", "Ivan The Terrible (H8/3256 version)", MACHINE_SUPPORTS_SAVE )



jade.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

    Single board Z80 computer on a S100 card (not the JGZ80).
    The Jade SPIO board adds four CTCs, two SIOs and one PIO.

    2013-09-12 Skeleton driver.

    No info found as yet.

    Type HE to get a list of commands.

    If you hit a key as soon as it is started, the system will present a
    prompt and work. Otherwise it runs into the weeds because the rom banking
    isn't yet emulated.

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "bus/rs232/rs232.h"
//#include "bus/s100/s100.h"


namespace {

class jade_state : public driver_device
{
public:
	jade_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void jade(machine_config &config);

private:
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};


void jade_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).rom().region("roms", 0);
	map(0x0800, 0xffff).ram();
}

void jade_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x28, 0x2b).rw("ctc2", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x30, 0x33).rw("sio", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
	map(0x40, 0x43).rw("ctc1", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
}

/* Input ports */
static INPUT_PORTS_START( jade )
INPUT_PORTS_END


void jade_state::jade(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &jade_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &jade_state::io_map);

	Z80CTC(config, "ctc1", 4_MHz_XTAL);

	z80ctc_device &ctc2(Z80CTC(config, "ctc2", 4_MHz_XTAL));
	ctc2.set_clk<0>(4_MHz_XTAL / 2);
	ctc2.zc_callback<0>().set("sio", FUNC(z80sio_device::rxca_w));
	ctc2.zc_callback<0>().append("sio", FUNC(z80sio_device::txca_w));

	/* Devices */
	z80sio_device& sio(Z80SIO(config, "sio", 4_MHz_XTAL));
	//sio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0); // no evidence of a daisy chain because IM2 is not set
	sio.out_txda_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	sio.out_dtra_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	sio.out_rtsa_callback().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("sio", FUNC(z80sio_device::rxa_w));
	rs232.cts_handler().set("sio", FUNC(z80sio_device::ctsa_w));
}

/* ROM definition */
ROM_START( jade )
	ROM_REGION( 0x800, "roms", 0 )
	ROM_LOAD( "jade.rom", 0x0000, 0x0800, CRC(90c4a1ef) SHA1(8a93a11051cc27f3edca24f0f4297ebe0099964e) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY                   FULLNAME                                                            FLAGS
COMP( 1983, jade, 0,      0,      jade,    jade,  jade_state, empty_init, "Jade Computer Products", "unknown S-100 computer with Serial/Parallel/Interrupt Controller", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



jaguar.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Aaron Giles,Nathan Woods,Angelo Salese, Robbbert
/***************************************************************************

    Atari Jaguar (Home) & Atari CoJag (Arcade) hardware

    The CoJag arcade system is based on the Jaguar system, but with an upgraded
    main CPU (68020 or MIPS instead of the plain 68000 found in the Jaguar)

    driver by Aaron Giles
    console support originally by Nathan Woods

    CoJag Games supported:
        * Area 51 (4 Sets)
        * Maximum Force (3 Sets)
        * Area 51/Maximum Force Duo (2 Sets)
        * Vicious Circle
        * Fishin' Frenzy
        * Freeze

    To do:
        * (CoJag) map out unused RAM per-game via memory_nop_read/write
        * (Jaguar) support is very poor, most games aren't properly playable
          or have severe performance issues or crashes related to the unsafe
          blitter code. Please refer to jaguar SW list file for more details.
        * The code (GPU/DSP access) should probably be refactored around the
          16-bit interface from the plain 68k, the driver currently uses
          trampoline functions due to the original driver being entirely
          32-bit due to the CPUs on CoJag.

    Note: There is believed to be a 68020 version of Maximum Force
            (not confirmed or dumped)

****************************************************************************

    Area51/Maximum Force (c)1997 Atari Games
    Maximum Force
    A055451

    Components:
    sdt79r3041-20j
    Atari Jaguar CPU V1.0 6sc880hf106
    Atari Jaguar DSP V1.0 sc414201ft (has Motorola logo)
    Altera epm7128elc84-15 marked A-21652
    VIA vt83c461 IDE controller
    Actel a1010b marked A-22096 near IDE and gun inputs
    Dallas ds1232s watchdog
    52MHz osc near Altera PLCC
    40MHz osc near 79R3041
    14.318180MHz osc near Jag DSP
    12x hm514260cj7 RAM (near Jaguar CPU/DSP)
    4x  sdt71256 RAM (near Boot ROMs's)
    Atmel atf16v8b marked a-21647 (near Jag CPU)
    Altera ep22lc-10 marked A-21648 (near Jag DSP)
    ICT 22cv10aj marked A-21649 (near Jag CPU)
    ICT 22cv10aj marked A-21650 (near Jag CPU)
    ICT 22cv10aj marked A-21651 (near Jag CPU)
    tea6320t
    AKM ak4310vm
    tda1554q amplifier
    Microchip 28c16a-15 BRAM

    ROM's:
    27c4001
    R3K MAX/A51 KIT
    LL
    (c)1997 Atari
    V 1.0

    27c4001
    R3K MAX/A51 KIT
    LH
    (c)1997 Atari
    V 1.0

    27c4001
    R3K MAX/A51 KIT
    HL
    (c)1997 Atari
    V 1.0

    27c4001
    R3K MAX/A51 KIT
    HH
    (c)1997 Atari
    V 1.0

    Jumpers:
    jsp1 (1/2 connected= w/sub 2/3 connected= normal speaker)
    jsp2 (1/2 connected= w/sub 2/3 connected= normal speaker)
    jamaud (1/2 connected=stereo 2/3 connected=mono)
    jimpr (1/2 connected=hi video R impedance 2/3 connected=lo)
    jimpg (1/2 connected=hi video G impedance 2/3 connected=lo)
    jimpb (1/2 connected=hi video B impedance 2/3 connected=lo)

    Connectors:
    idea  standard IDE connector
    ideb laptop size IDE connector
    jgun1 8 pin gun input
    jgun2 8 pin gun input
    xtracoin 1 6 pin (coin3/4 bills?)
    jvupdn 3 pin (?)
    jsync 3 pin (?)
    JAMMA
    jspkr left/right/subwoofer output
    hdpower 4 pin PC power connector for HD

****************************************************************************

Area 51 (Time Warner Interactive License)
Atari/Mesa Logic, 1995

This game runs on Atari Cojag 68k hardware.

PCB Layouts
-----------

Main Board:

COJAG A053538
ATARI GAMES (C) 1994
(Atari comment near AMP - 'MMM, DONUTS.')
|--------------------------------------------------|
|TDA1554Q  TEA6320T   AK4310            JXCLKTRM   |
|JSPKR     ST91D314                        XC7336  |
|         JSP1 JWELLSR  PAL4  PAL5                 |
|   JSP4  JSP2 PAL1            45160 45160  *   *  |
| HDPOWER JSP3 JWELLSB                        JXBUS|
| SELFTEST     PAL2     PAL6   45160 45160  *   *  |
|     LED      JWELLSG                             |
|     LED              |------|45160 45160  *   *  |
|J    LED              |JAGUAR|                    |
|A                     | CPU  |45160 45160  *   *  |
|M                     |      |                    |
|M                     |------|                    |
|A                                                 |
|   14.31818MHz        |------|                    |
|   LM613              |JAGUAR|                    |
|   52MHz  PAL3        | DSP  |                    |
|                      |      |                    |
|  JSYNC               |------|                    |
|  JVUPDN           DIP8                           |
|              A1010B                              |
|JPLY3   JVCR            VT83C461                  |
|                                                  |
|                               LED                |
|  JPLY4    JGUN2  JGUN1  JIDEB                    |
|--------------------------------------------------|
Notes:
      JAGUAR CPU - Atari Jaguar CPU (QFP208)
      JAGUAR DSP - Atari Jaguar DSP (QFP160)
      45160      - TMS45160DZ-70 512K x16 DRAM (SOJ40)
                   Note: This RAM is in banks. There are 4 banks, each bank is 2MBytes x 64bit
                   Only banks 0 and 1 are populated.
      *          - Unpopulated DRAM positions (banks 2 and 3)
      TDA1554Q   - Philips TDA1554Q power AMP
      TEA6320T   - Philips TEA6320T op AMP (SOP32)
      AK4310     - AKM AK4310VM (SOIC24)
      ST91D314   - STS Microelectronics ST91D314 3403 op AMP (SOIC14)
      PAL1       - ATMEL ATF16V8B PAL (labelled '136105-0006', PLCC20)
      PAL2       - ATMEL ATF16V8B PAL (labelled '136105-0006', PLCC20)
      PAL3       - ATMEL ATF16V8B PAL (labelled '136105-0007', PLCC20)
      PAL4, PAL5 - Philips PL22V10-10A PAL (labelled 'MYF 136105-0008', PLCC28)
      PAL6       - ATMEL ATF16V8B (labelled '136105-0005', PLCC20)
      A1010B     - ACTEL A1010B Complex Programmable Logic Device (labeled 'MYF 136105-0010', PLCC44)
      XC7336     - Xilinx XC7336 Complex PRogrammable Logic Device (labelled 'MYF 136105-0009', PLCC44)
      VT83C461   - VIA VT83C461 IDE Hard Drive Controller (QFP100)
      LM613      - (DIP16)
      SELFTEST   - Test Switch
      HDPOWER    - Standard (PC-type) 4 pin hard drive power connector
      JSP1       - 3 pin jumper block, set to 2-3
      JSP2       - 3 pin jumper block, set to 2-3
      JSP3       - 3 pin jumper block, jumper not installed
      JSP4       - 3 pin jumper block, set to 2-3
      JWELLSR    - 3 pin jumper block, set to 2-3
      JWELLSB    - 3 pin jumper block, set to 2-3
      JWELLSG    - 3 pin jumper block, set to 2-3
      JSYNC      - 3 pin jumper block, jumper not installed
      JVUPDN     - 3 pin jumper block, jumper not installed
      JVCR       - 3 pin jumper block, jumper not installed
      JXCLKTRM   - 2 pin header, not shorted
      JPLY3      - Connector for player 3 controls
      JPLY4      - Connector for player 4 controls
      JGUN1      - Connector for player 1 gun
      JGUN2      - Connector for player 2 gun
      JIDEB      - Connector for 40 pin IDE cable connected to Quantum Fireball 1080AT IDE hard drive (C/H/S = 2112/16/63)
      JSPKR      - Connector for left/right speaker for stereo sound output
      DIP8       - Unpopulated DIP8 socket
      JXBUS      - 96 pin connector to top board


Top Board:

EC20X32
A053448
ATARI GAMES (C) 1994
|------------------------------|
|               136105-0002C.3P|
| 71256                        |
|               136105-0001C.3M|
| 71256                        |
|               136105-0000C.3K|
| 71256                        |
|               136105-0003C.3H|
| 71256                        |
|              LED             |
|                              |
| AT28C16  DS1232   XC7354     |
|                              |
|                              |
|      MC68EC020               |
|                              |
| 50MHz                        |
|------------------------------|
Notes:
      MC68EC020       - Motorola 68EC020FG25 CPU clocked at 25MHz (QFP100)
      AT28C16         - ATMEL 2k x8 EEPROM (DIP24)
      XC7354          - Xilinx XC7354 Complex Programmable Logic Device (labelled 'MYF 136105-0004', PLCC68, socketed)
      DS1232          - Dallas DS1232 System Reset IC (DIP8)
      71256           - 32K x8 SRAM (SOJ28)
      136105-0002C.3P - 27C040 EPROM (labelled 'AREA 51 136105-0002C (C)1995 ATARI GMS CS 55FE', DIP32)
      136105-0001C.3M - 27C040 EPROM (labelled 'AREA 51 136105-0001C (C)1995 ATARI GMS CS 3DFD', DIP32)
      136105-0000C.3K - 27C040 EPROM (labelled 'AREA 51 136105-0000C (C)1995 ATARI GMS CS 63FC', DIP32)
      136105-0003C.3H - 27C040 EPROM (labelled 'AREA 51 136105-0003C (C)1995 ATARI GMS CS 45FF', DIP32)

****************************************************************************

Maximum Force
Atari, 1997

PCB Layout
----------

MAXIMUM FORCE A055451 ATARI GAMES (C) 1996
|-----------------------------------------------------------------------------------|
|HM514260CJ7 HM514260CJ7 HM514260CJ7 HM514260CJ7            AT28C16                 |
|                                                                                   |
|HM514260CJ7 HM514260CJ7 HM514260CJ7 HM514260CJ7            PROGLL.17Y   PROGLH.21Y |
|                                                                                   |
|HM514260CJ7 HM514260CJ7 HM514260CJ7 HM514260CJ7            PROGHL.21V   PROGHH.21V |
|                                                                                   |
|                                                                                   |
|             |------|      |------|                                                |
|             |JAGUAR|      |JAGUAR|                                                |
|GAL     GAL  |GPU   |      |DSP   |                     |----------|               |
|             |------|      |------|                     |          |               |
|                                                        |IDT       |               |
|GAL     GAL                                     40MHz   |79R3041-20|               |
|                                          GAL           |          |               |
|                                    14.31818MHz         |----------|           IDE |
|AK4310VM                                                                      CONN |
|                                                       |-----------|               |
|LM78L05                                     52MHz      |ALTERA MAX |               |
|                                                       |EPM7128ELC84               |
|        TEA6320                                        |           |               |
|78L09                                    LM613         |           |      VIA      |
|            MONO/STEREO                                |-----------|      VT83C461 |
|TDA1554           SELFTEST                                                         |
|                                                  DS1232      ACTEL_A1010B         |
|           HDD_PWR                                                                 |
| JSPKR |--|           JAMMA            |--| JSYNC  JVUPDN   XTRACOIN1  JGUN1  JGUN2|
|-------|  |----------------------------|  |----------------------------------------|
Notes:
      JGUN1 / JGUN2 - Connector for Guns
      VIA VT83C461 - IDE Controller
      JSPKR - Speaker Output Connector (for use with Stereo jumper)

****************************************************************************

    Memory map (TBA)

    ========================================================================
    MAIN CPU
    ========================================================================

    ------------------------------------------------------------
    000000-3FFFFF   R/W   xxxxxxxx xxxxxxxx   DRAM 0
    400000-7FFFFF   R/W   xxxxxxxx xxxxxxxx   DRAM 1
    800000-BFFFFF   R     xxxxxxxx xxxxxxxx   Graphic ROM bank
    C00000-DFFFFF   R     xxxxxxxx xxxxxxxx   Sound ROM bank
    F00000-F000FF   R/W   xxxxxxxx xxxxxxxx   Tom Internal Registers
    F00400-F005FF   R/W   xxxxxxxx xxxxxxxx   CLUT - color lookup table A
    F00600-F007FF   R/W   xxxxxxxx xxxxxxxx   CLUT - color lookup table B
    F00800-F00D9F   R/W   xxxxxxxx xxxxxxxx   LBUF - line buffer A
    F01000-F0159F   R/W   xxxxxxxx xxxxxxxx   LBUF - line buffer B
    F01800-F01D9F   R/W   xxxxxxxx xxxxxxxx   LBUF - line buffer currently selected
    F02000-F021FF   R/W   xxxxxxxx xxxxxxxx   GPU control registers
    F02200-F022FF   R/W   xxxxxxxx xxxxxxxx   Blitter registers
    F03000-F03FFF   R/W   xxxxxxxx xxxxxxxx   Local GPU RAM
    F08800-F08D9F   R/W   xxxxxxxx xxxxxxxx   LBUF - 32-bit access to line buffer A
    F09000-F0959F   R/W   xxxxxxxx xxxxxxxx   LBUF - 32-bit access to line buffer B
    F09800-F09D9F   R/W   xxxxxxxx xxxxxxxx   LBUF - 32-bit access to line buffer currently selected
    F0B000-F0BFFF   R/W   xxxxxxxx xxxxxxxx   32-bit access to local GPU RAM
    F10000-F13FFF   R/W   xxxxxxxx xxxxxxxx   Jerry
    F14000-F17FFF   R/W   xxxxxxxx xxxxxxxx   Joysticks and GPIO0-5
    F18000-F1AFFF   R/W   xxxxxxxx xxxxxxxx   Jerry DSP
    F1B000-F1CFFF   R/W   xxxxxxxx xxxxxxxx   Local DSP RAM
    F1D000-F1DFFF   R     xxxxxxxx xxxxxxxx   Wavetable ROM
    ------------------------------------------------------------

  Jaguar console schematics include a ADC0844 at U16, selected by GPIOL5. This IC
  may or may not be populated.

  Jaguar System Notes:

    Protection Check

    At power on, a checksum is performed on the cart to ensure it has been
    certified by Atari. The actual checksum calculation is performed by the GPU,
    the result being left in GPU RAM at address f03000. The GPU is instructed to
    do the calculation when the bios sends a 1 to f02114 while it is in the
    initialisation stage. The bios then loops, waiting for the GPU to finish the
    calculation. When it does, it sets bit 15 of f02114 high. The bios then does
    the compare of the checksum. The checksum algorithm is unknown, but the
    final result must be 03d0dead. The bios checks for this particular result,
    and if found, the cart is allowed to start. Otherwise, the background turns
    red, and the console freezes.


    Jaguar Logo

    A real Jaguar will show the red Jaguar logo, the falling white Atari letters,
    and the turning jaguar's head, accompanied by the sound of a flushing toilet.
    The cart will then start. All Jaguar emulators (including this one) skip the
    logo with the appropriate memory hack. The cart can also instruct the logo
    be skipped by placing non-zero at location 800408. We do the same thing when
    the cart is loaded (see the DEVICE_IMAGE_LOAD section below).


    Start Address

    The start address of a cart may be found at 800404. It is normally 802000.

***************************************************************************/


#include "emu.h"
#include "jaguar.h"

#include "bus/ata/hdd.h"
#include "bus/generic/carts.h"
#include "cpu/jaguar/jaguar.h"
#include "cpu/m68000/m68000.h"
#include "cpu/m68000/m68020.h"
#include "cpu/mips/mips1.h"
#include "imagedev/cdromimg.h"
#include "imagedev/snapquik.h"
#include "machine/eepromser.h"
#include "machine/vt83c461.h"
#include "machine/watchdog.h"
#include "sound/cdda.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "cdrom.h"

#define COJAG_CLOCK         XTAL(52'000'000)
#define R3000_CLOCK         XTAL(40'000'000)
#define M68K_CLOCK          XTAL(50'000'000)


/*************************************
 *
 *  Local variables
 *
 *************************************/

void cojag_devices(device_slot_interface &device)
{
	device.option_add("hdd", IDE_HARDDISK);
}


/*************************************
 *
 *  Machine init
 *
 *************************************/

void jaguar_state::machine_start()
{
	/* configure banks for gfx/sound ROMs */
	if (m_romboard_region != nullptr)
	{
		uint8_t *romboard = m_romboard_region->base();

		/* graphics banks */
		if (m_maingfxbank.found())
		{
			m_maingfxbank->configure_entries(0, 2, romboard + 0x800000, 0x400000);
		}
		m_gpugfxbank->configure_entries(0, 2, romboard + 0x800000, 0x400000);

		/* sound banks */
		m_mainsndbank->configure_entries(0, 8, romboard + 0x000000, 0x200000);
		m_dspsndbank->configure_entries(0, 8, romboard + 0x000000, 0x200000);
	}
}

void jaguar_state::machine_reset()
{
	m_protection_check = 0;

	/* 68020 only: copy the interrupt vectors into RAM */
	if (!m_is_r3000)
	{
		if (m_rom_base.found())
		{
			for (offs_t addr = 0; addr < 0x400; addr += 4)    // do not increase, or Doom breaks
				m_shared_ram[addr/4] = m_rom_base[addr/2] << 16 | m_rom_base[addr/2+1];
		}
		else
			std::copy_n(reinterpret_cast<uint32_t *>(memregion("maincpu")->base()), 0x100, &m_shared_ram[0]);
	}

	/* reset banks for gfx/sound ROMs */
	if (m_romboard_region != nullptr)
	{
		/* graphics banks */
		if (m_maingfxbank.found())
		{
			m_maingfxbank->set_entry(0);
		}
		m_gpugfxbank->set_entry(0);

		/* sound banks */
		m_mainsndbank->set_entry(0);
		m_dspsndbank->set_entry(0);
	}

	/* clear any spinuntil stuff */
	gpu_resume();
	dsp_resume();

	/* halt the CPUs */
	m_gpu->go_w(false);
	m_dsp->go_w(false);

	/* set blitter idle flag */
	m_blitter_status = 1;
	m_joystick_data = 0xffffffff;
	m_eeprom_bit_count = 0;

	if ((m_using_cart) && (m_config_io->read() & 2))
	{
		m_cart_base[0x102] = 1;
		m_using_cart = false;
	}
}

void jaguarcd_state::machine_reset()
{
	jaguar_state::machine_reset();

	m_shared_ram[0x4/4] = 0x00802000; /* hack until I understand */

	m_butch_cmd_index = 0;
	m_butch_cmd_size = 1;
}


/********************************************************************
*
*  EEPROM
*  ======
*
*   The EEPROM is accessed by a serial protocol using the registers
*   0xF14000 (read data), F14800 (increment clock, write data), F15000 (reset for next word)
*
********************************************************************/
/*
emu_file jaguar_state::*jaguar_nvram_fopen( uint32_t openflags)
{
    device_image_interface *image = dynamic_cast<device_image_interface *>(machine().device("cart"));
    osd_file::error filerr;
    emu_file *file;
    if (image->exists())
    {
        std::string fname(machine().system().name, PATH_SEPARATOR, image->basename_noext(), ".nv");
        filerr = mame_fopen( SEARCHPATH_NVRAM, fname, openflags, &file);
        return (filerr == osd_file::error::NONE) ? file : nullptr;
    }
    else
        return nullptr;
}

void jaguar_state::jaguar_nvram_load()
{
    emu_file *nvram_file = nullptr;
    device_t *device;

    for (device = machine().m_devicelist.first(); device != nullptr; device = device->next())
    {
        device_nvram_func nvram = (device_nvram_func)device->get_config_fct(DEVINFO_FCT_NVRAM);
        if (nvram != nullptr)
        {
            if (nvram_file == nullptr)
                nvram_file = jaguar_nvram_fopen(machine, OPEN_FLAG_READ);
            (*nvram)(device, nvram_file, 0);
        }
    }
    if (nvram_file != nullptr)
        mame_fclose(nvram_file);
}


void jaguar_state::jaguar_nvram_save()
{
    emu_file *nvram_file = nullptr;
    device_t *device;

    for (device = machine().m_devicelist.first(); device != nullptr; device = device->next())
    {
        device_nvram_func nvram = (device_nvram_func)device->get_config_fct(DEVINFO_FCT_NVRAM);
        if (nvram != nullptr)
        {
            if (nvram_file == nullptr)
                nvram_file = jaguar_nvram_fopen(machine, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
            // check nvram_file to avoid crash when no image is mounted or cannot be created
            if (nvram_file)
                (*nvram)(device, nvram_file, 1);
        }
    }

    if (nvram_file != nullptr)
        mame_fclose(nvram_file);
}

static NVRAM_HANDLER( jaguar )
{
    if (read_or_write)  {
        jaguar_nvram_save(machine);
    }
    else
    {
        if (file)
            jaguar_nvram_load(machine);
    }
}
*/
void jaguar_state::eeprom_w(uint32_t data)
{
	m_eeprom_bit_count++;
	if (m_eeprom_bit_count != 9)        /* kill extra bit at end of address */
	{
		m_eeprom->di_write(data >> 31);
		m_eeprom->clk_write(0);
		m_eeprom->clk_write(1);
	}
}

uint32_t jaguar_state::eeprom_clk()
{
	if (!machine().side_effects_disabled())
	{
		m_eeprom->clk_write(0);
		m_eeprom->clk_write(1); /* get next bit when reading */
	}
	return 0;
}

uint32_t jaguar_state::eeprom_cs()
{
	if (!machine().side_effects_disabled())
	{
		m_eeprom->cs_write(CLEAR_LINE);   /* must do at end of an operation */
		m_eeprom->cs_write(ASSERT_LINE);        /* enable chip for next operation */
		m_eeprom->di_write(1);           /* write a start bit */
		m_eeprom->clk_write(0);
		m_eeprom->clk_write(1);
		m_eeprom_bit_count = 0;
	}
	return 0;
}



/*************************************
 *
 *  Misc. control bits
 *
 *************************************/

uint32_t jaguar_state::misc_control_r()
{
	/*  D7    = board reset (low)
	    D6    = audio must & reset (high)
	    D5    = volume control data (invert on write)
	    D4    = volume control clock
	    D3-D1 = audio bank 2-0
	    D0    = shared memory select (0=XBUS) */

	return m_misc_control_data ^ 0x20;
}


void jaguar_state::misc_control_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	logerror("%s:misc_control_w(%02X)\n", machine().describe_context(), data);

	/*  D7    = board reset (low)
	    D6    = audio must & reset (high)
	    D5    = volume control data (invert on write)
	    D4    = volume control clock
	    D3-D1 = audio bank 2-0
	    D0    = shared memory select (0=XBUS) */

	/* handle resetting the DSPs */
	if (!(data & 0x80))
	{
		/* clear any spinuntil stuff */
		gpu_resume();
		dsp_resume();

		/* halt the CPUs */
		m_gpu->go_w(false);
		m_dsp->go_w(false);
	}

	/* adjust banking */
	if (m_romboard_region != nullptr)
	{
		m_mainsndbank->set_entry((data >> 1) & 7);
		m_dspsndbank->set_entry((data >> 1) & 7);
	}

	COMBINE_DATA(&m_misc_control_data);
}


/*************************************
 *
 *  32-bit access to the GPU
 *
 *************************************/

uint32_t jaguar_state::gpuctrl_r(offs_t offset, uint32_t mem_mask)
{
	return m_gpu->iobus_r(offset, mem_mask);
}

void jaguar_state::gpuctrl_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	m_gpu->iobus_w(offset, data, mem_mask);
}


/*************************************
 *
 *  32-bit access to the DSP
 *
 *************************************/

uint32_t jaguar_state::dspctrl_r(offs_t offset, uint32_t mem_mask)
{
	return m_dsp->iobus_r(offset, mem_mask);
}

void jaguar_state::dspctrl_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	m_dsp->iobus_w(offset, data, mem_mask);
}


/*************************************
 *
 *  Input ports
 *
 *  Information from "The Jaguar Underground Documentation"
 *  by Klaus and Nat!
 *
 *************************************/

uint32_t jaguar_state::joystick_r()
{
	uint16_t joystick_result = 0xfffe;
	uint16_t joybuts_result = 0xffef;

	/*
	 *   16        12        8         4         0
	 *   +---------+---------+---------^---------+
	 *   |  pad 1  |  pad 0  |      unused       |
	 *   +---------+---------+-------------------+
	 *     15...12   11...8          7...0
	 *
	 *   Reading this register gives you the output of the selected columns
	 *   of the pads.
	 *   The buttons pressed will appear as cleared bits.
	 *   See the description of the column addressing to map the bits
	 *   to the buttons.
	 */

	for (int i = 0; i < 8; i++)
	{
		if ((m_joystick_data & (0x10000 << i)) == 0)
		{
			joystick_result &= m_joy[i]->read();
			joybuts_result &= m_buttons[i]->read();
		}
	}

	joystick_result |= m_eeprom->do_read();
	joybuts_result |= (m_config_io->read() & 0x10);

	return (joystick_result << 16) | joybuts_result;
}

void jaguar_state::joystick_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	/*
	 *   16        12         8         4         0
	 *   +-+-------^------+--+---------+---------+
	 *   |r|    unused    |mu|  col 1  |  col 0  |
	 *   +-+--------------+--+---------+---------+
	 *    15                8   7...4     3...0
	 *
	 *   col 0:   column control of joypad 0
	 *
	 *      Here you select which column of the joypad to poll.
	 *      The columns are:
	 *
	 *                Joystick       Joybut
	 *      col_bit|11 10  9  8     1    0
	 *      -------+--+--+--+--    ---+------
	 *         0   | R  L  D  U     A  PAUSE       (RLDU = Joypad directions)
	 *         1   | 1  4  7  *     B
	 *         2   | 2  5  8  0     C
	 *         3   | 3  6  9  #   OPTION
	 *
	 *      You select a column my clearing the appropriate bit and setting
	 *      all the other "column" bits.
	 *
	 *
	 *   col1:    column control of joypad 1
	 *
	 *      This is pretty much the same as for joypad EXCEPT that the
	 *      column addressing is reversed (strange!!)
	 *
	 *                Joystick      Joybut
	 *      col_bit|15 14 13 12     3    2
	 *      -------+--+--+--+--    ---+------
	 *         4   | 3  6  9  #   OPTION
	 *         5   | 2  5  8  0     C
	 *         6   | 1  4  7  *     B
	 *         7   | R  L  D  U     A  PAUSE     (RLDU = Joypad directions)
	 *
	 *   mute (mu):   sound control
	 *
	 *      You can turn off the sound by clearing this bit.
	 *
	 *   read enable (r):
	 *
	 *      Set this bit to read from the joysticks, clear it to write
	 *      to them.
	 */
	COMBINE_DATA(&m_joystick_data);
}


/*************************************
 *
 *  Output ports
 *
 *************************************/

void jaguar_state::latch_w(uint32_t data)
{
	logerror("%08X:latch_w(%X)\n", m_maincpu->pcbase(), data);

	/* adjust banking */
	if (m_romboard_region != nullptr)
	{
		if (m_maingfxbank.found())
		{
			m_maingfxbank->set_entry(data & 1);
		}
		m_gpugfxbank->set_entry(data & 1);
	}
}



/*************************************
 *
 *  EEPROM access
 *
 *************************************/

uint32_t jaguar_state::eeprom_data_r(offs_t offset)
{
	if (m_is_r3000)
		return m_nvram[offset] | 0xffffff00;
	else
		return m_nvram[offset] | 0x00ffffff;
}


void jaguar_state::eeprom_enable_w(uint32_t data)
{
	m_eeprom_enable = true;
}


void jaguar_state::eeprom_data_w(offs_t offset, uint32_t data)
{
//  if (m_eeprom_enable)
	{
		if (m_is_r3000)
			m_nvram[offset] = data & 0x000000ff;
		else
			m_nvram[offset] = data & 0xff000000;
	}
//  else
//      logerror("%s:error writing to disabled EEPROM\n", machine().describe_context());
	m_eeprom_enable = false;
}



/*************************************
 *
 *  GPU synchronization & speedup
 *
 *************************************/

/*
    Explanation:

    The GPU generally sits in a tight loop waiting for the main CPU to store
    a jump address into a specific memory location. This speedup is designed
    to catch that loop, which looks like this:

        load    (r28),r21
        jump    (r21)
        nop

    When nothing is pending, the GPU keeps the address of the load instruction
    at (r28) so that it loops back on itself. When the main CPU wants to execute
    a command, it stores an alternate address to (r28).

    Even if we don't optimize this case, we do need to detect when a command
    is written to the GPU in order to improve synchronization until the GPU
    has finished. To do this, we start a temporary high frequency timer and
    run it until we get back to the spin loop.
*/

void jaguar_state::gpu_jump_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	/* update the data in memory */
	COMBINE_DATA(m_gpu_jump_address);
	logerror("%08X:GPU jump address = %08X\n", m_gpu->pcbase(), *m_gpu_jump_address);

	/* if the GPU is suspended, release it now */
	gpu_resume();

	/* start the sync timer going, and note that there is a command pending */
	m_gpu_sync_timer->adjust(attotime::zero);
	m_gpu_command_pending = true;
}


uint32_t jaguar_state::gpu_jump_r()
{
	/* if the current GPU command is just pointing back to the spin loop, and */
	/* we're reading it from the spin loop, we can optimize */
	if (*m_gpu_jump_address == m_gpu_spin_pc && m_gpu->pcbase() == m_gpu_spin_pc)
	{
#if ENABLE_SPEEDUP_HACKS
		/* spin if we're allowed */
		if (m_hacks_enabled) gpu_suspend();
#endif

		/* no command is pending */
		m_gpu_command_pending = false;
	}

	/* return the current value */
	return *m_gpu_jump_address;
}



/*************************************
 *
 *  Main CPU speedup (R3000 games)
 *
 *************************************/

/*
    Explanation:

    Instead of sitting in a tight loop, the CPU will run the random number
    generator over and over while waiting for an interrupt. In order to catch
    that, we snoop the memory location it is polling, and see if it is read
    at least 5 times in a row, each time less than 200 cycles apart. If so,
    we assume it is spinning. Also, by waiting for 5 iterations, we let it
    crank through some random numbers, just not several thousand every frame.
*/

uint32_t jaguar_state::cojagr3k_main_speedup_r()
{
	uint64_t curcycles = m_maincpu->total_cycles();

	/* if it's been less than main_speedup_max_cycles cycles since the last time */
	if (curcycles - m_main_speedup_last_cycles < m_main_speedup_max_cycles)
	{
		/* increment the count; if we hit 5, we can spin until an interrupt comes */
		if (m_main_speedup_hits++ > 5)
		{
			m_maincpu->spin_until_interrupt();
			m_main_speedup_hits = 0;
		}
	}

	/* if it's been more than main_speedup_max_cycles cycles, reset our count */
	else
		m_main_speedup_hits = 0;

	/* remember the last cycle count */
	m_main_speedup_last_cycles = curcycles;

	/* return the real value */
	return *m_main_speedup;
}



/*************************************
 *
 *  Additional main CPU speedup
 *  (Freeze only)
 *
 *************************************/

/*
    Explanation:

    The main CPU hands data off to the GPU to process. But rather than running
    in parallel, the main CPU just sits and waits for the result. This speedup
    makes sure we don't waste time emulating that spin loop.
*/

uint32_t jaguar_state::main_gpu_wait_r()
{
	if (m_gpu_command_pending)
		m_maincpu->spin_until_interrupt();
	return *m_main_gpu_wait;
}



/*************************************
 *
 *  Main CPU speedup (Area 51)
 *
 *************************************/

/*
    Explanation:

    Very similar to the R3000 code, except we need to verify that the value in
    *main_speedup is actually 0.
*/

void jaguar_state::area51_main_speedup_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	uint64_t curcycles = m_maincpu->total_cycles();

	/* store the data */
	COMBINE_DATA(m_main_speedup);

	/* if it's been less than 400 cycles since the last time */
	if (*m_main_speedup == 0 && curcycles - m_main_speedup_last_cycles < 400)
	{
		/* increment the count; if we hit 5, we can spin until an interrupt comes */
		if (m_main_speedup_hits++ > 5)
		{
			m_maincpu->spin_until_interrupt();
			m_main_speedup_hits = 0;
		}
	}

	/* if it's been more than 400 cycles, reset our count */
	else
		m_main_speedup_hits = 0;

	/* remember the last cycle count */
	m_main_speedup_last_cycles = curcycles;
}


/*
    Explanation:

    The Area 51/Maximum Force duo writes to a non-aligned address, so our check
    against 0 must handle that explicitly.
*/

void jaguar_state::area51mx_main_speedup_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	uint64_t curcycles = m_maincpu->total_cycles();

	/* store the data */
	COMBINE_DATA(&m_main_speedup[offset]);

	/* if it's been less than 450 cycles since the last time */
	if (((m_main_speedup[0] << 16) | (m_main_speedup[1] >> 16)) == 0 && curcycles - m_main_speedup_last_cycles < 450)
	{
		/* increment the count; if we hit 5, we can spin until an interrupt comes */
		if (m_main_speedup_hits++ > 10)
		{
			m_maincpu->spin_until_interrupt();
			m_main_speedup_hits = 0;
		}
	}

	/* if it's been more than 450 cycles, reset our count */
	else
		m_main_speedup_hits = 0;

	/* remember the last cycle count */
	m_main_speedup_last_cycles = curcycles;
}



/*************************************
 *
 *  Main CPU memory handlers
 *
 *************************************/

// surely these should be 16-bit natively if the standard Jaguar is driven by a plain 68k?
// all these trampolines are not good for performance ;-)

uint16_t jaguar_state::gpuctrl_r16(offs_t offset, uint16_t mem_mask){ if (!(offset&1)) { return gpuctrl_r(offset>>1, mem_mask<<16) >> 16;  } else { return gpuctrl_r(offset>>1, mem_mask); } }
void jaguar_state::gpuctrl_w16(offs_t offset, uint16_t data, uint16_t mem_mask){ if (!(offset&1)) { gpuctrl_w(offset>>1, data << 16, mem_mask << 16); } else { gpuctrl_w(offset>>1, data, mem_mask); } }
uint16_t jaguar_state::blitter_r16(offs_t offset, uint16_t mem_mask){ if (!(offset&1)) { return blitter_r(offset>>1, mem_mask<<16) >> 16;  } else { return blitter_r(offset>>1, mem_mask); } }
void jaguar_state::blitter_w16(offs_t offset, uint16_t data, uint16_t mem_mask){ if (!(offset&1)) { blitter_w(offset>>1, data << 16, mem_mask << 16); } else { blitter_w(offset>>1, data, mem_mask); } }
uint16_t jaguar_state::serial_r16(offs_t offset){ if (!(offset&1)) { return serial_r(offset>>1) >> 16;  } else { return serial_r(offset>>1); } }
void jaguar_state::serial_w16(offs_t offset, uint16_t data){ if (!(offset&1)) { serial_w(offset>>1, data << 16); } else { serial_w(offset>>1, data); } }
uint16_t jaguar_state::dspctrl_r16(offs_t offset, uint16_t mem_mask){ if (!(offset&1)) { return dspctrl_r(offset>>1, mem_mask<<16) >> 16;  } else { return dspctrl_r(offset>>1, mem_mask); } }
void jaguar_state::dspctrl_w16(offs_t offset, uint16_t data, uint16_t mem_mask){ if (!(offset&1)) { dspctrl_w(offset>>1, data << 16, mem_mask << 16); } else { dspctrl_w(offset>>1, data, mem_mask); } }
uint16_t jaguar_state::eeprom_cs16(offs_t offset){ if (!(offset&1)) { return eeprom_cs() >> 16;  } else { return eeprom_cs(); } }
uint16_t jaguar_state::eeprom_clk16(offs_t offset){ if (!(offset&1)) { return eeprom_clk() >> 16;  } else { return eeprom_clk(); } }
void jaguar_state::eeprom_w16(offs_t offset, uint16_t data){ if (!(offset&1)) { eeprom_w(data << 16); } else { eeprom_w(data); } }
uint16_t jaguar_state::joystick_r16(offs_t offset){ if (!(offset&1)) { return joystick_r() >> 16;  } else { return joystick_r(); } }
void jaguar_state::joystick_w16(offs_t offset, uint16_t data, uint16_t mem_mask){ if (!(offset&1)) { joystick_w(offset>>1, data << 16, mem_mask << 16); } else { joystick_w(offset>>1, data, mem_mask); } }

uint32_t jaguar_state::shared_ram_r(offs_t offset){ return m_shared_ram[offset]; }
void jaguar_state::shared_ram_w(offs_t offset, uint32_t data, uint32_t mem_mask){ COMBINE_DATA(&m_shared_ram[offset]); }
uint32_t jaguar_state::rom_base_r(offs_t offset){ return m_rom_base[offset*2+1] << 16 | m_rom_base[offset*2]; }
uint32_t jaguar_state::wave_rom_r(offs_t offset){ return m_wave_rom[offset*2+1] << 16 | m_wave_rom[offset*2]; }
uint32_t jaguarcd_state::cd_bios_r(offs_t offset){ return m_cd_bios[offset*2+1] << 16 | m_cd_bios[offset*2]; }
uint32_t jaguar_state::dsp_ram_r(offs_t offset){ return m_dsp_ram[offset]; }
void jaguar_state::dsp_ram_w(offs_t offset, uint32_t data, uint32_t mem_mask){ COMBINE_DATA(&m_dsp_ram[offset]); }
uint32_t jaguar_state::gpu_clut_r(offs_t offset){ return m_gpu_clut[offset]; }
void jaguar_state::gpu_clut_w(offs_t offset, uint32_t data, uint32_t mem_mask){ COMBINE_DATA(&m_gpu_clut[offset]); }
uint32_t jaguar_state::gpu_ram_r(offs_t offset){ return m_gpu_ram[offset]; }
void jaguar_state::gpu_ram_w(offs_t offset, uint32_t data, uint32_t mem_mask){ COMBINE_DATA(&m_gpu_ram[offset]); }

uint16_t jaguar_state::shared_ram_r16(offs_t offset){ if (!(offset&1)) { return shared_ram_r(offset>>1) >> 16;  } else { return shared_ram_r(offset>>1); } }
void jaguar_state::shared_ram_w16(offs_t offset, uint16_t data, uint16_t mem_mask){ if (!(offset&1)) { shared_ram_w(offset>>1, data << 16, mem_mask << 16); } else { shared_ram_w(offset>>1, data, mem_mask); } }
uint16_t jaguar_state::cart_base_r16(offs_t offset){ if (!(offset&1)) { return m_cart_base[offset>>1] >> 16;  } else { return m_cart_base[offset>>1] & 0xffff; } }
uint16_t jaguar_state::dsp_ram_r16(offs_t offset){ if (!(offset&1)) { return dsp_ram_r(offset>>1) >> 16;  } else { return dsp_ram_r(offset>>1); } }
void jaguar_state::dsp_ram_w16(offs_t offset, uint16_t data, uint16_t mem_mask){ if (!(offset&1)) { dsp_ram_w(offset>>1, data << 16, mem_mask << 16); } else { dsp_ram_w(offset>>1, data, mem_mask); } }
uint16_t jaguar_state::gpu_clut_r16(offs_t offset){ if (!(offset&1)) { return gpu_clut_r(offset>>1) >> 16;  } else { return gpu_clut_r(offset>>1); } }
void jaguar_state::gpu_clut_w16(offs_t offset, uint16_t data, uint16_t mem_mask){ if (!(offset&1)) { gpu_clut_w(offset>>1, data << 16, mem_mask << 16); } else { gpu_clut_w(offset>>1, data, mem_mask); } }
uint16_t jaguar_state::gpu_ram_r16(offs_t offset){ if (!(offset&1)) { return gpu_ram_r(offset>>1) >> 16;  } else { return gpu_ram_r(offset>>1); } }
void jaguar_state::gpu_ram_w16(offs_t offset, uint16_t data, uint16_t mem_mask){ if (!(offset&1)) { gpu_ram_w(offset>>1, data << 16, mem_mask << 16); } else { gpu_ram_w(offset>>1, data, mem_mask); } }

void jaguar_state::console_base_map(address_map &map)
{
	map(0x000000, 0x1fffff).mirror(0x200000).rw(FUNC(jaguar_state::shared_ram_r16), FUNC(jaguar_state::shared_ram_w16));
	map(0xe00000, 0xe1ffff).rom().region("mainrom", 0);
	map(0xf00000, 0xf003ff).rw(FUNC(jaguar_state::tom_regs_r), FUNC(jaguar_state::tom_regs_w)); // might be reversed endian of the others..
	map(0xf00400, 0xf005ff).mirror(0x000200).rw(FUNC(jaguar_state::gpu_clut_r16), FUNC(jaguar_state::gpu_clut_w16));
	map(0xf02100, 0xf021ff).rw(FUNC(jaguar_state::gpuctrl_r16), FUNC(jaguar_state::gpuctrl_w16));
	map(0xf02200, 0xf022ff).mirror(0x008000).rw(FUNC(jaguar_state::blitter_r16), FUNC(jaguar_state::blitter_w16));
	map(0xf03000, 0xf03fff).mirror(0x008000).rw(FUNC(jaguar_state::gpu_ram_r16), FUNC(jaguar_state::gpu_ram_w16));
	map(0xf10000, 0xf103ff).rw(FUNC(jaguar_state::jerry_regs_r), FUNC(jaguar_state::jerry_regs_w)); // might be reversed endian of the others..
	map(0xf14000, 0xf14003).rw(FUNC(jaguar_state::joystick_r16), FUNC(jaguar_state::joystick_w16));
	map(0xf14800, 0xf14803).rw(FUNC(jaguar_state::eeprom_clk16), FUNC(jaguar_state::eeprom_w16));  // GPIO0
	map(0xf15000, 0xf15003).r(FUNC(jaguar_state::eeprom_cs16));               // GPIO1
	map(0xf1a100, 0xf1a13f).rw(FUNC(jaguar_state::dspctrl_r16), FUNC(jaguar_state::dspctrl_w16));
	map(0xf1a140, 0xf1a17f).rw(FUNC(jaguar_state::serial_r16), FUNC(jaguar_state::serial_w16));
	map(0xf1b000, 0xf1cfff).rw(FUNC(jaguar_state::dsp_ram_r16), FUNC(jaguar_state::dsp_ram_w16));
	map(0xf1d000, 0xf1dfff).rom().region("waverom", 0);
}

void jaguar_state::jaguar_map(address_map &map)
{
	console_base_map(map);
	map(0x800000, 0xdfffff).r(FUNC(jaguar_state::cart_base_r16));
}

void jaguar_state::cpu_space_map(address_map &map)
{
	map(0xfffff0, 0xffffff).m(m_maincpu, FUNC(m68000_base_device::autovectors_map));
	map(0xfffff5, 0xfffff5).lr8([] () -> u8 { return 0x40; }, "level2");
}

/*
CD-Rom emulation, chip codename Butch (the HW engineer was definitely obsessed with T&J somehow ...)
TODO: this needs to be device-ized, of course ...

[0x00]: irq register
(R)
-x-- ---- ---- ---- CD uncorrectable data error pending
--x- ---- ---- ---- Response from CD drive pending
---x ---- ---- ---- Command to CD drive pending
---- x--- ---- ---- Subcode data pending
---- -x-- ---- ---- Frame pending
---- --x- ---- ---- CD data FIFO half-full flag pending
(W)
---- ---- -x-- ---- CIRC failure irq
---- ---- --x- ---- CD module command RX buffer full irq
---- ---- ---x ---- CD module command TX buffer empty irq
---- ---- ---- x--- Enable pre-set subcode time-match found irq
---- ---- ---- -x-- Enable CD subcode frame-time irq
---- ---- ---- --x- Enable CD data FIFO half full irq
---- ---- ---- ---x set to enable irq
[0x04]: DSA control register
[0x0a]: DSA TX/RX data (sends commands with this)
    0x01 Play Title (?)
    0x02 Stop
    0x03 Read TOC
    0x04 Pause
    0x05 Unpause
    0x09 Get Title Len
    0x0a Open Tray
    0x0b Close Tray
    0x0d Get Comp Time
    0x10 Goto ABS Min
    0x11 Goto ABS Sec
    0x12 Goto ABS Frame
    0x14 Read Long TOC
    0x15 Set Mode
    0x16 Get Error
    0x17 Clear Error
    0x18 Spin Up
    0x20 Play AB Min
    0x21 Play AB Sec
    0x22 Play AB Frame
    0x23 Stop AB Min
    0x24 Stop AB Sec
    0x25 Stop AB Frame
    0x26 AB Release
    0x50 Get Disc Status
    0x51 Set Volume
    0x54 Get Maxsession
    0x70 Set DAC mode (?)
    0xa0-0xaf User Define (???)
    0xf0 Service
    0xf1 Sledge
    0xf2 Focus
    0xf3 Turntable
    0xf4 Radial

[0x10]: I2S bus control register
[0x14]: CD subcode control register
[0x18]: Subcode data register A
[0x1C]: Subcode data register B
[0x20]: Subcode time and compare enable
[0x24]: I2S FIFO data
[0x28]: I2S FIFO data (old)
[0x2c]: ? (used at start-up)

*/

uint16_t jaguarcd_state::butch_regs_r16(offs_t offset){ if (!(offset&1)) { return butch_regs_r(offset>>1) >> 16;  } else { return butch_regs_r(offset>>1); } }
void jaguarcd_state::butch_regs_w16(offs_t offset, uint16_t data, uint16_t mem_mask){ if (!(offset&1)) { butch_regs_w(offset>>1, data << 16, mem_mask << 16); } else { butch_regs_w(offset>>1, data, mem_mask); } }

uint32_t jaguarcd_state::butch_regs_r(offs_t offset)
{
	switch(offset*4)
	{
		case 8: //DS DATA
			//m_butch_regs[0] &= ~0x2000;
			return m_butch_cmd_response[(m_butch_cmd_index++) % m_butch_cmd_size];
	}

	return m_butch_regs[offset];
}

void jaguarcd_state::butch_regs_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	COMBINE_DATA(&m_butch_regs[offset]);

	switch(offset*4)
	{
		case 8: //DS DATA
			switch((m_butch_regs[offset] & 0xff00) >> 8)
			{
				case 0x03: // Read TOC
				{
					if(!m_cdrom->exists()) // No disc
					{
						m_butch_cmd_response[0] = 0x400;
						m_butch_regs[0] |= 0x2000;
						m_butch_cmd_index = 0;
						m_butch_cmd_size = 1;
						return;
					}
					if(m_butch_regs[offset] & 0xff) // Multi Session CD, TODO
					{
						m_butch_cmd_response[0] = 0x0029; // illegal value
						m_butch_regs[0] |= 0x2000;
						m_butch_cmd_index = 0;
						m_butch_cmd_size = 1;
						return;
					}

					uint32_t msf = m_cdrom->get_track_start(0) + 150;

					/* first track number */
					m_butch_cmd_response[0] = 0x2000 | 1;
					/* last track number */
					m_butch_cmd_response[1] = 0x2100 | m_cdrom->get_last_track();

					/* start of first track minutes */
					m_butch_cmd_response[2] = 0x2200 | ((msf / 60) / 60);
					/* start of first track seconds */
					m_butch_cmd_response[3] = 0x2300 | (msf / 60) % 60;
					/* start of first track frame */
					m_butch_cmd_response[4] = 0x2400 | (msf % 75);
					m_butch_regs[0] |= 0x2000;
					m_butch_cmd_index = 0;
					m_butch_cmd_size = 5;
					break;
				}

				case 0x14: // Read Long TOC
				{
					if(!m_cdrom->exists()) // No disc
					{
						m_butch_cmd_response[0] = 0x400;
						m_butch_regs[0] |= 0x2000;
						m_butch_cmd_index = 0;
						m_butch_cmd_size = 1;
						return;
					}

					int ntrks = m_cdrom->get_last_track();

					for(int i=0;i<ntrks;i++)
					{
						uint32_t msf = m_cdrom->get_track_start(i) + 150;

						/* track number */
						m_butch_cmd_response[i*5+0] = 0x6000 | (i+1);
						/* attributes (?) */
						m_butch_cmd_response[i*5+1] = 0x6100 | 0x00;

						/* start of track minutes */
						m_butch_cmd_response[i*5+2] = 0x6200 | ((msf / 60) / 60);
						/* start of track seconds */
						m_butch_cmd_response[i*5+3] = 0x6300 | (msf / 60) % 60;
						/* start of track frame */
						m_butch_cmd_response[i*5+4] = 0x6400 | (msf % 75);
					}
					m_butch_regs[0] |= 0x2000;
					m_butch_cmd_index = 0;
					m_butch_cmd_size = 5*ntrks;
					break;
				}

				case 0x15: // Set Mode
					m_butch_regs[0] |= 0x2000;
					m_butch_cmd_response[0] = 0x1700 | (m_butch_regs[offset] & 0xff);
					m_butch_cmd_index = 0;
					m_butch_cmd_size = 1;
					break;

				case 0x70: // Set DAC Mode
					m_butch_regs[0] |= 0x2000;
					m_butch_cmd_response[0] = 0x7000 | (m_butch_regs[offset] & 0xff);
					m_butch_cmd_index = 0;
					m_butch_cmd_size = 1;
					break;

				default:
					logerror("%04x CMD\n", m_butch_regs[offset]);
					break;
			}
			break;
	}
}

void jaguarcd_state::jaguarcd_map(address_map &map)
{
	console_base_map(map);
	map(0x800000, 0x83ffff).rom().region("cdbios", 0);
	map(0xdfff00, 0xdfff3f).rw(FUNC(jaguarcd_state::butch_regs_r16), FUNC(jaguarcd_state::butch_regs_w16));
}


/*************************************
 *
 *  Main CPU memory handlers
 *
 *************************************/

void jaguar_state::r3000_map(address_map &map)
{
	map(0x04000000, 0x047fffff).ram().share("sharedram");
	map(0x04e00030, 0x04e0003f).rw(m_ide, FUNC(vt83c461_device::config_r), FUNC(vt83c461_device::config_w));
	map(0x04e001f0, 0x04e001f7).rw(m_ide, FUNC(vt83c461_device::cs0_r), FUNC(vt83c461_device::cs0_w));
	map(0x04e003f0, 0x04e003f7).rw(m_ide, FUNC(vt83c461_device::cs1_r), FUNC(vt83c461_device::cs1_w));
	map(0x04f00000, 0x04f003ff).rw(FUNC(jaguar_state::tom_regs_r), FUNC(jaguar_state::tom_regs_w));
	map(0x04f00400, 0x04f007ff).ram().share("gpuclut");
	map(0x04f02100, 0x04f021ff).rw(FUNC(jaguar_state::gpuctrl_r), FUNC(jaguar_state::gpuctrl_w));
	map(0x04f02200, 0x04f022ff).rw(FUNC(jaguar_state::blitter_r), FUNC(jaguar_state::blitter_w));
	map(0x04f03000, 0x04f03fff).mirror(0x00008000).ram().share("gpuram");
	map(0x04f10000, 0x04f103ff).rw(FUNC(jaguar_state::jerry_regs_r), FUNC(jaguar_state::jerry_regs_w));
	map(0x04f16000, 0x04f1600b).r(FUNC(jaguar_state::cojag_gun_input_r)); // GPIO2
	map(0x04f17000, 0x04f17003).lr16(NAME([this] () { return uint16_t(m_system->read()); })); // GPIO3
	map(0x04f17800, 0x04f17803).w(FUNC(jaguar_state::latch_w));          // GPIO4
	map(0x04f17c00, 0x04f17c03).portr("P1_P2");      // GPIO5
	map(0x04f1a100, 0x04f1a13f).rw(FUNC(jaguar_state::dspctrl_r), FUNC(jaguar_state::dspctrl_w));
	map(0x04f1a140, 0x04f1a17f).rw(FUNC(jaguar_state::serial_r), FUNC(jaguar_state::serial_w));
	map(0x04f1b000, 0x04f1cfff).ram().share("dspram");

	map(0x06000000, 0x06000003).rw(FUNC(jaguar_state::misc_control_r), FUNC(jaguar_state::misc_control_w));
	map(0x10000000, 0x1007ffff).ram().share("mainram");
	map(0x12000000, 0x120fffff).ram().share("mainram2");    // tested in self-test only?
	map(0x14000004, 0x14000007).w("watchdog", FUNC(watchdog_timer_device::reset32_w));
	map(0x16000000, 0x16000003).w(FUNC(jaguar_state::eeprom_enable_w));
	map(0x18000000, 0x18001fff).rw(FUNC(jaguar_state::eeprom_data_r), FUNC(jaguar_state::eeprom_data_w)).share("nvram");
	map(0x1fc00000, 0x1fdfffff).rom().region("maincpu", 0);
}

void jaguar_state::r3000_rom_map(address_map &map)
{
	r3000_map(map);
	map(0x04800000, 0x04bfffff).bankr("maingfxbank");
	map(0x04c00000, 0x04dfffff).bankr("mainsndbank");
}


void jaguar_state::m68020_map(address_map &map)
{
	map(0x000000, 0x7fffff).ram().share("sharedram");
	map(0x800000, 0x9fffff).rom().region("maincpu", 0);
	map(0xa00000, 0xa1ffff).ram().share("mainram");
	map(0xa20000, 0xa21fff).rw(FUNC(jaguar_state::eeprom_data_r), FUNC(jaguar_state::eeprom_data_w)).share("nvram");
	map(0xa30000, 0xa30003).w("watchdog", FUNC(watchdog_timer_device::reset32_w));
	map(0xa40000, 0xa40003).w(FUNC(jaguar_state::eeprom_enable_w));
	map(0xb70000, 0xb70003).rw(FUNC(jaguar_state::misc_control_r), FUNC(jaguar_state::misc_control_w));
//  map(0xc00000, 0xdfffff).bankr("mainsndbank");
	map(0xe00030, 0xe0003f).rw(m_ide, FUNC(vt83c461_device::config_r), FUNC(vt83c461_device::config_w));
	map(0xe001f0, 0xe001f7).rw(m_ide, FUNC(vt83c461_device::cs0_r), FUNC(vt83c461_device::cs0_w));
	map(0xe003f0, 0xe003f7).rw(m_ide, FUNC(vt83c461_device::cs1_r), FUNC(vt83c461_device::cs1_w));
	map(0xf00000, 0xf003ff).rw(FUNC(jaguar_state::tom_regs_r), FUNC(jaguar_state::tom_regs_w));
	map(0xf00400, 0xf007ff).ram().share("gpuclut");
	map(0xf02100, 0xf021ff).rw(FUNC(jaguar_state::gpuctrl_r), FUNC(jaguar_state::gpuctrl_w));
	map(0xf02200, 0xf022ff).rw(FUNC(jaguar_state::blitter_r), FUNC(jaguar_state::blitter_w));
	map(0xf03000, 0xf03fff).mirror(0x008000).ram().share("gpuram");
	map(0xf10000, 0xf103ff).rw(FUNC(jaguar_state::jerry_regs_r), FUNC(jaguar_state::jerry_regs_w));
	map(0xf16000, 0xf1600b).r(FUNC(jaguar_state::cojag_gun_input_r)); // GPIO2
	map(0xf17000, 0xf17003).lr16(NAME([this] () { return uint16_t(m_system->read()); })); // GPIO3
//  map(0xf17800, 0xf17803).w(FUNC(jaguar_state::(latch_w));          // GPIO4
	map(0xf17c00, 0xf17c03).portr("P1_P2");      // GPIO5
	map(0xf1a100, 0xf1a13f).rw(FUNC(jaguar_state::dspctrl_r), FUNC(jaguar_state::dspctrl_w));
	map(0xf1a140, 0xf1a17f).rw(FUNC(jaguar_state::serial_r), FUNC(jaguar_state::serial_w));
	map(0xf1b000, 0xf1cfff).ram().share("dspram");
}


/*************************************
 *
 *  GPU memory handlers
 *
 *************************************/

void jaguar_state::gpu_map(address_map &map)
{
	map(0x000000, 0x7fffff).ram().share("sharedram");
	map(0xe00030, 0xe0003f).rw(m_ide, FUNC(vt83c461_device::config_r), FUNC(vt83c461_device::config_w));
	map(0xe001f0, 0xe001f7).rw(m_ide, FUNC(vt83c461_device::cs0_r), FUNC(vt83c461_device::cs0_w));
	map(0xe003f0, 0xe003f7).rw(m_ide, FUNC(vt83c461_device::cs1_r), FUNC(vt83c461_device::cs1_w));
	map(0xf00000, 0xf003ff).rw(FUNC(jaguar_state::tom_regs_r), FUNC(jaguar_state::tom_regs_w));
	map(0xf00400, 0xf007ff).ram().share("gpuclut");
	map(0xf02100, 0xf021ff).rw(FUNC(jaguar_state::gpuctrl_r), FUNC(jaguar_state::gpuctrl_w));
	map(0xf02200, 0xf022ff).rw(FUNC(jaguar_state::blitter_r), FUNC(jaguar_state::blitter_w));
	map(0xf03000, 0xf03fff).ram().share("gpuram");
	map(0xf10000, 0xf103ff).rw(FUNC(jaguar_state::jerry_regs_r), FUNC(jaguar_state::jerry_regs_w));
}

void jaguar_state::gpu_rom_map(address_map &map)
{
	gpu_map(map);
	map(0x800000, 0xbfffff).bankr("gpugfxbank");
	map(0xc00000, 0xdfffff).bankr("dspsndbank");
}


/*************************************
 *
 *  DSP memory handlers
 *
 *************************************/

void jaguar_state::dsp_map(address_map &map)
{
	map(0x000000, 0x7fffff).ram().share("sharedram");
	map(0xf10000, 0xf103ff).rw(FUNC(jaguar_state::jerry_regs_r), FUNC(jaguar_state::jerry_regs_w));
	map(0xf1a100, 0xf1a13f).rw(FUNC(jaguar_state::dspctrl_r), FUNC(jaguar_state::dspctrl_w));
	map(0xf1a140, 0xf1a17f).rw(FUNC(jaguar_state::serial_r), FUNC(jaguar_state::serial_w));
	map(0xf1b000, 0xf1cfff).ram().share("dspram");
	map(0xf1d000, 0xf1dfff).r(FUNC(jaguar_state::wave_rom_r));
}

void jaguar_state::dsp_rom_map(address_map &map)
{
	dsp_map(map);
	map(0x800000, 0xbfffff).bankr("gpugfxbank");
	map(0xc00000, 0xdfffff).bankr("dspsndbank");
}

/* ToDo, these maps SHOULD be merged with the ones above */

void jaguar_state::console_base_gpu_map(address_map &map)
{
	map.global_mask(0xffffff);
	map(0x000000, 0x1fffff).ram().mirror(0x200000).share("sharedram");
	map(0xe00000, 0xe1ffff).r(FUNC(jaguar_state::rom_base_r));
	map(0xf00000, 0xf003ff).rw(FUNC(jaguar_state::tom_regs_r), FUNC(jaguar_state::tom_regs_w));
	map(0xf00400, 0xf005ff).mirror(0x000200).ram().share("gpuclut");
	map(0xf02100, 0xf021ff).rw(FUNC(jaguar_state::gpuctrl_r), FUNC(jaguar_state::gpuctrl_w));
	map(0xf02200, 0xf022ff).mirror(0x008000).rw(FUNC(jaguar_state::blitter_r), FUNC(jaguar_state::blitter_w));
	map(0xf03000, 0xf03fff).mirror(0x008000).ram().share("gpuram");
	map(0xf10000, 0xf103ff).rw(FUNC(jaguar_state::jerry_regs_r), FUNC(jaguar_state::jerry_regs_w));
	map(0xf14000, 0xf14003).rw(FUNC(jaguar_state::joystick_r), FUNC(jaguar_state::joystick_w));
	map(0xf1a100, 0xf1a13f).rw(FUNC(jaguar_state::dspctrl_r), FUNC(jaguar_state::dspctrl_w));
	map(0xf1a140, 0xf1a17f).rw(FUNC(jaguar_state::serial_r), FUNC(jaguar_state::serial_w));
	map(0xf1b000, 0xf1cfff).ram().share("dspram");
	map(0xf1d000, 0xf1dfff).r(FUNC(jaguar_state::wave_rom_r));
}

void jaguar_state::jag_gpu_dsp_map(address_map &map)
{
	console_base_gpu_map(map);
	map(0x800000, 0xdfffff).rom().region("cart", 0);
}

void jaguarcd_state::jagcd_gpu_dsp_map(address_map &map)
{
	console_base_gpu_map(map);
	map(0x800000, 0x83ffff).r(FUNC(jaguarcd_state::cd_bios_r));
	map(0xdfff00, 0xdfff3f).rw(FUNC(jaguarcd_state::butch_regs_r), FUNC(jaguarcd_state::butch_regs_w));
}


/*************************************
 *
 *  Port definitions
 *
 *************************************/

/* "SYSTEM" is read at 0x04f17000
    D23-20 = /SER-4-1
    D19-16 = COINR4-1
    D7     = /VSYNCNEQ
    D6     = /S-TEST
    D5     = /VOLUMEUP
    D4     = /VOLUMEDOWN
    D3-D0  = ACTC4-1
*/
static INPUT_PORTS_START( area51 )
	PORT_START("P1_P2")
	PORT_BIT( 0x000000ff, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x00000100, IP_ACTIVE_LOW, IPT_START2 )
	PORT_BIT( 0x0000fe00, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_BIT( 0x00ff0000, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x01000000, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0xfe000000, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("SYSTEM")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_COIN1 )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_COIN2 )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_COIN3 )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_COIN4 )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_VOLUME_DOWN )
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_VOLUME_UP )
	PORT_SERVICE( 0x0040, IP_ACTIVE_LOW )           // s-test
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_CUSTOM )  // vsyncneq
	PORT_BIT( 0xff00, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("FAKE1_X")               /* fake analog X */
	PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 320.0/(320.0 - 7 -7), 0, 0) PORT_SENSITIVITY(50) PORT_KEYDELTA(10)

	PORT_START("FAKE1_Y")               /* fake analog Y */
	PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, (240.0 - 1)/240, 0.0, 0) PORT_SENSITIVITY(70) PORT_KEYDELTA(10)

	PORT_START("FAKE2_X")               /* fake analog X */
	PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 320.0/(320.0 - 7 -7), 0.0, 0) PORT_SENSITIVITY(50) PORT_KEYDELTA(10) PORT_PLAYER(2)

	PORT_START("FAKE2_Y")               /* fake analog Y */
	PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, (240.0 - 1)/240, 0.0, 0) PORT_SENSITIVITY(70) PORT_KEYDELTA(10) PORT_PLAYER(2)

	PORT_START("IN3")           /* gun triggers */
	PORT_BIT( 0x00010000, IP_ACTIVE_LOW, IPT_CUSTOM )  // gun data valid
	PORT_BIT( 0x00020000, IP_ACTIVE_LOW, IPT_CUSTOM )  // gun data valid
	PORT_BIT( 0x00040000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x00080000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0xfff00000, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END


static INPUT_PORTS_START( freezeat )
	PORT_START("P1_P2")
	PORT_BIT( 0x000000ff, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x00000100, IP_ACTIVE_LOW, IPT_START2 )
	PORT_BIT( 0x00000200, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x00000400, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0x00000800, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2)
	PORT_BIT( 0x00001000, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x00002000, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x00004000, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x00008000, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)

	PORT_BIT( 0x00ff0000, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x01000000, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x02000000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x04000000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x08000000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1)
	PORT_BIT( 0x10000000, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x20000000, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x40000000, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x80000000, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)

	PORT_START("SYSTEM")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_COIN1 )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_COIN2 )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_COIN3 )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_COIN4 )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_CUSTOM )  // volume down
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_CUSTOM )  // volume up
	PORT_SERVICE( 0x0040, IP_ACTIVE_LOW )           // s-test
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_CUSTOM )  // vsyncneq
	PORT_BIT( 0xff00, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN3")
	PORT_BIT( 0x000f0000, IP_ACTIVE_HIGH, IPT_CUSTOM ) // coin returns
	PORT_BIT( 0x00f00000, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0xff000000, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END


static INPUT_PORTS_START( fishfren )
	PORT_START("P1_P2")
	PORT_BIT( 0x000000ff, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x00000100, IP_ACTIVE_LOW, IPT_START2 )
	PORT_BIT( 0x00000200, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x00000400, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0x00000800, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2)
	PORT_BIT( 0x00001000, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x00002000, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x00004000, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x00008000, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)

	PORT_BIT( 0x00ff0000, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x01000000, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x02000000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x04000000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x08000000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1)
	PORT_BIT( 0x10000000, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x20000000, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x40000000, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x80000000, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)

	PORT_START("SYSTEM")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_COIN1 )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_COIN2 )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_COIN3 )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_COIN4 )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_CUSTOM )  // volume down
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_CUSTOM )  // volume up
	PORT_SERVICE( 0x0040, IP_ACTIVE_LOW )           // s-test
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_CUSTOM )  // vsyncneq
	PORT_BIT( 0xff00, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN3")
	PORT_BIT( 0x000f0000, IP_ACTIVE_HIGH, IPT_CUSTOM ) // coin returns
	PORT_BIT( 0x00f00000, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0xff000000, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END


static INPUT_PORTS_START( vcircle )
	PORT_START("P1_P2")
	PORT_BIT( 0x00000001, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(2)
	PORT_BIT( 0x00000002, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(2)
	PORT_BIT( 0x00000004, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(2)
	PORT_BIT( 0x000000f8, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x00000100, IP_ACTIVE_LOW, IPT_START2 )
	PORT_BIT( 0x00000200, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x00000400, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0x00000800, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2)
	PORT_BIT( 0x00001000, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x00002000, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x00004000, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x00008000, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)

	PORT_BIT( 0x00010000, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1)
	PORT_BIT( 0x00020000, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(1)
	PORT_BIT( 0x00040000, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(1)
	PORT_BIT( 0x00f80000, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x01000000, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x02000000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x04000000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x08000000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1)
	PORT_BIT( 0x10000000, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x20000000, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x40000000, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x80000000, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)

	PORT_START("SYSTEM")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_COIN1 )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_COIN2 )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_COIN3 )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_COIN4 )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_VOLUME_DOWN )
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_VOLUME_UP )
	PORT_SERVICE( 0x0040, IP_ACTIVE_LOW )           // s-test
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_CUSTOM )  // vsyncneq
	PORT_BIT( 0xff00, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN3")
	PORT_BIT( 0x000f0000, IP_ACTIVE_HIGH, IPT_CUSTOM ) // coin returns
	PORT_BIT( 0x00f00000, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0xff000000, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END


static INPUT_PORTS_START( jaguar )
	PORT_START("JOY0")
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0xf0ff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("JOY1")
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P1 Keypad 1") PORT_CODE(KEYCODE_1) PORT_PLAYER(1)
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P1 Keypad 4") PORT_CODE(KEYCODE_4) PORT_PLAYER(1)
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P1 Keypad 7") PORT_CODE(KEYCODE_7) PORT_PLAYER(1)
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P1 Keypad *") PORT_CODE(KEYCODE_K) PORT_PLAYER(1)
	PORT_BIT( 0xf0ff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("JOY2")
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P1 Keypad 2") PORT_CODE(KEYCODE_2) PORT_PLAYER(1)
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P1 Keypad 5") PORT_CODE(KEYCODE_5) PORT_PLAYER(1)
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P1 Keypad 8") PORT_CODE(KEYCODE_8) PORT_PLAYER(1)
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P1 Keypad 0") PORT_CODE(KEYCODE_0) PORT_PLAYER(1)
	PORT_BIT( 0xf0ff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("JOY3")
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P1 Keypad 3") PORT_CODE(KEYCODE_3) PORT_PLAYER(1)
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P1 Keypad 6") PORT_CODE(KEYCODE_6) PORT_PLAYER(1)
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P1 Keypad 9") PORT_CODE(KEYCODE_9) PORT_PLAYER(1)
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P1 Keypad #") PORT_CODE(KEYCODE_L) PORT_PLAYER(1)
	PORT_BIT( 0xf0ff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("JOY4")
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 Keypad 3") PORT_PLAYER(2)
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 Keypad 6") PORT_PLAYER(2)
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 Keypad 9") PORT_PLAYER(2)
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 Keypad #") PORT_PLAYER(2)
	PORT_BIT( 0x0fff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("JOY5")
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 Keypad 2") PORT_PLAYER(2)
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 Keypad 5") PORT_PLAYER(2)
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 Keypad 8") PORT_PLAYER(2)
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 Keypad 0") PORT_PLAYER(2)
	PORT_BIT( 0x0fff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("JOY6")
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 Keypad 1") PORT_PLAYER(2)
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 Keypad 4") PORT_PLAYER(2)
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 Keypad 7") PORT_PLAYER(2)
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 Keypad *") PORT_PLAYER(2)
	PORT_BIT( 0x0fff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("JOY7")
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x0fff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("BUTTONS0")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("P1 Pause") PORT_CODE(KEYCODE_I) PORT_PLAYER(1)
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("P1 A") PORT_PLAYER(1)
	PORT_BIT( 0xfffc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("BUTTONS1")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("P1 B") PORT_PLAYER(1)
	PORT_BIT( 0xfffd, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("BUTTONS2")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("P1 C") PORT_PLAYER(1)
	PORT_BIT( 0xfffd, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("BUTTONS3")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("P1 Option") PORT_CODE(KEYCODE_O) PORT_PLAYER(1)
	PORT_BIT( 0xfffd, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("BUTTONS4")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("P2 Option") PORT_PLAYER(2)
	PORT_BIT( 0xfffd, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("BUTTONS5")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("P2 C") PORT_PLAYER(2)
	PORT_BIT( 0xfffd, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("BUTTONS6")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("P2 B") PORT_PLAYER(2)
	PORT_BIT( 0xfffd, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("BUTTONS7")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("P2 Pause") PORT_PLAYER(2)
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("P2 A") PORT_PLAYER(2)
	PORT_BIT( 0xfffc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("CONFIG")
	PORT_CONFNAME( 0x02, 0x00, "Show Logo")
	PORT_CONFSETTING(    0x00, "Yes")
	PORT_CONFSETTING(    0x02, "No")
	PORT_CONFNAME( 0x10, 0x10, "TV System")
	PORT_CONFSETTING(    0x00, "PAL")
	PORT_CONFSETTING(    0x10, "NTSC")
INPUT_PORTS_END


/*************************************
 *
 *  Machine driver
 *
 *************************************/

void jaguar_state::video_config(machine_config &config, const XTAL clock)
{
	JAGUARGPU(config, m_gpu, clock);
	m_gpu->irq().set(FUNC(jaguar_state::gpu_cpu_int));

	JAGUARDSP(config, m_dsp, clock);
	m_dsp->irq().set(FUNC(jaguar_state::dsp_cpu_int));

	// TODO: Tom
	// TODO: Object Processor

	JAG_BLITTER(config, m_blitter, clock);
}

void jaguar_state::cojagr3k(machine_config &config)
{
	/* basic machine hardware */
	R3041(config, m_maincpu, R3000_CLOCK / 2).set_endianness(ENDIANNESS_BIG); // divider not verified, but chip is rated for 20 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &jaguar_state::r3000_map);

	video_config(config, COJAG_CLOCK/2);
	m_gpu->set_addrmap(AS_PROGRAM, &jaguar_state::gpu_map);
	m_dsp->set_addrmap(AS_PROGRAM, &jaguar_state::dsp_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	WATCHDOG_TIMER(config, "watchdog");

	VT83C461(config, m_ide).options(cojag_devices, "hdd", nullptr, true);
	m_ide->irq_handler().set(FUNC(jaguar_state::external_int));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_video_attributes(VIDEO_UPDATE_BEFORE_VBLANK);
	m_screen->set_raw(COJAG_PIXEL_CLOCK/2, 456, 42, 402, 262, 17, 257);
	m_screen->set_screen_update(FUNC(jaguar_state::screen_update));

	PALETTE(config, m_palette, FUNC(jaguar_state::jagpal_ycc), 65536);

	/* sound hardware */
	SPEAKER(config, "speaker", 2).front();
	DAC_16BIT_R2R_TWOS_COMPLEMENT(config, m_ldac, 0).add_route(ALL_OUTPUTS, "speaker", 1.0, 0); // unknown DAC
	DAC_16BIT_R2R_TWOS_COMPLEMENT(config, m_rdac, 0).add_route(ALL_OUTPUTS, "speaker", 1.0, 1); // unknown DAC

	// TODO: subwoofer speaker
}

void jaguar_state::cojagr3k_rom(machine_config &config)
{
	cojagr3k(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &jaguar_state::r3000_rom_map);
	m_gpu->set_addrmap(AS_PROGRAM, &jaguar_state::gpu_rom_map);
	m_dsp->set_addrmap(AS_PROGRAM, &jaguar_state::dsp_rom_map);

	m_ide->slot(0).set_default_option(nullptr);
}

void jaguar_state::cojag68k(machine_config &config)
{
	cojagr3k(config);

	/* basic machine hardware */
	M68EC020(config.replace(), m_maincpu, M68K_CLOCK/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &jaguar_state::m68020_map);
}

void jaguar_state::jaguar(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, JAGUAR_CLOCK/2); // MC68000FN12F 16 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &jaguar_state::jaguar_map);
	m_maincpu->set_addrmap(m68000_device::AS_CPU_SPACE, &jaguar_state::cpu_space_map);

	video_config(config, JAGUAR_CLOCK);
	m_gpu->set_addrmap(AS_PROGRAM, &jaguar_state::jag_gpu_dsp_map);
	m_dsp->set_addrmap(AS_PROGRAM, &jaguar_state::jag_gpu_dsp_map);

//  MCFG_NVRAM_HANDLER(jaguar)

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_video_attributes(VIDEO_UPDATE_BEFORE_VBLANK);
	m_screen->set_raw(JAGUAR_CLOCK, 456, 42, 402, 262, 17, 257);
	m_screen->set_screen_update(FUNC(jaguar_state::screen_update));

	PALETTE(config, m_palette, FUNC(jaguar_state::jagpal_ycc), 65536);

	/* sound hardware */
	SPEAKER(config, "speaker", 2).front();
	DAC_16BIT_R2R_TWOS_COMPLEMENT(config, m_ldac, 0).add_route(ALL_OUTPUTS, "speaker", 1.0, 0); // unknown DAC
	DAC_16BIT_R2R_TWOS_COMPLEMENT(config, m_rdac, 0).add_route(ALL_OUTPUTS, "speaker", 1.0, 1); // unknown DAC

	/* quickload */
	QUICKLOAD(config, "quickload", "abs,bin,cof,jag,prg,rom", attotime::from_seconds(1)).set_load_callback(FUNC(jaguar_state::quickload_cb));

	/* cartridge */
	generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "jaguar_cart", "j64"));
	cartslot.set_device_load(FUNC(jaguar_state::cart_load));

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("jaguar");

	EEPROM_93C46_16BIT(config, m_eeprom);
}

void jaguarcd_state::jaguarcd(machine_config &config)
{
	jaguar(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &jaguarcd_state::jaguarcd_map);

	m_gpu->set_addrmap(AS_PROGRAM, &jaguarcd_state::jagcd_gpu_dsp_map);

	m_dsp->set_addrmap(AS_PROGRAM, &jaguarcd_state::jagcd_gpu_dsp_map);

	CDROM(config, "cdrom").set_interface("cdrom");

	// TODO: software list, requires multisession support first
}


/*************************************
 *
 *  Driver initialization
 *
 *************************************/

void jaguar_state::fix_endian( void *base, uint32_t size )
{
	uint32_t *mem = reinterpret_cast<uint32_t *>(base);

	for (uint32_t i = 0; i < size; i+=4)
		mem[i/4] = big_endianize_int32(mem[i/4]);
}

void jaguar_state::init_jaguar()
{
	m_is_cojag = false;
	m_hacks_enabled = false;
	save_item(NAME(m_joystick_data));

	/* Initialize for no cartridge present */
	m_using_cart = false;
}

void jaguarcd_state::init_jaguarcd()
{
	m_hacks_enabled = false;
	save_item(NAME(m_joystick_data));
}

std::pair<std::error_condition, std::string> jaguar_state::quickload_cb(snapshot_image_device &image)
{
	offs_t quickload_begin = 0x1000, start = 0x4000, skip = 0;

	offs_t quickload_size = std::min(offs_t(image.length()), 0x20000 - start);

	image.fread( &m_shared_ram[quickload_begin], quickload_size);

	fix_endian(&m_shared_ram[quickload_begin], quickload_size);

	/* Deal with some of the numerous homebrew header systems */
		/* COF */
	if ((m_shared_ram[0x1000] & 0xffff0000) == 0x01500000)
	{
		start = m_shared_ram[0x100e];
		skip = m_shared_ram[0x1011];
	}
	else    /* PRG */
	if (((m_shared_ram[0x1000] & 0xffff0000) == 0x601A0000) && (m_shared_ram[0x1007] == 0x4A414752))
	{
		uint32_t type = m_shared_ram[0x1008] >> 16;
		start = ((m_shared_ram[0x1008] & 0xffff) << 16) | (m_shared_ram[0x1009] >> 16);
		skip = 28;
		if (type == 2) skip = 42;
		else if (type == 3) skip = 46;
	}
	else    /* ABS with header */
	if ((m_shared_ram[0x1000] & 0xffff0000) == 0x601B0000)
	{
		start = ((m_shared_ram[0x1005] & 0xffff) << 16) | (m_shared_ram[0x1006] >> 16);
		skip = 36;
	}

	else    /* A header used by Badcoder */
	if ((m_shared_ram[0x1000] & 0xffff0000) == 0x72000000)
		skip = 96;

	else    /* ABS binary */
	if (image.is_filetype("abs"))
		start = 0xc000;

	else    /* JAG binary */
	if (image.is_filetype("jag"))
		start = 0x5000;
	else
	if (image.is_filetype("rom"))
		start = 0x802000;

	quickload_size = image.length();

	/* Now that we have the info, reload the file */
	if ((start + quickload_size) < 0x200000)
	{
		memset(m_shared_ram, 0, 0x200000);
		image.fseek(skip, SEEK_SET);
		image.fread( &m_shared_ram[start/4], quickload_size-skip);
		fix_endian(&m_shared_ram[start/4], quickload_size-skip);
	}
	else
	if (start >= 0x800000)
	{
		image.fseek(skip, SEEK_SET);
		image.fread( &m_cart_base[(start - 0x800000) / 4], quickload_size - skip);
		fix_endian(&m_cart_base[(start - 0x800000) / 4], quickload_size - skip);
	}
	else
		return std::make_pair(image_error::UNSUPPORTED, "Unsupported start address for this quickload.");

	/* Some programs are too lazy to set a stack pointer */
	m_maincpu->set_state_int(M68K_SP, 0x1000);
	m_shared_ram[0]=0x1000;

	/* Transfer control to image */
	m_maincpu->set_pc(quickload_begin);
	m_shared_ram[1]=quickload_begin;
	return std::make_pair(std::error_condition(), std::string());
}

DEVICE_IMAGE_LOAD_MEMBER( jaguar_state::cart_load )
{
	uint32_t size, load_offset = 0;

	if (!image.loaded_through_softlist())
	{
		size = image.length();

		/* Load cart into memory */
		image.fread(&m_cart_base[load_offset/4], size);
		fix_endian(&m_cart_base[load_offset/4], size);
	}
	else
	{
		size = image.get_software_region_length("rom");

		memcpy(&m_cart_base[0], image.get_software_region("rom"), size);
	}

	memset(&m_shared_ram[0], 0, 0x200000);

	/* Skip the logo */
	m_using_cart = true;
	//  m_cart_base[0x102] = 1;

	/* Transfer control to the bios */
	m_maincpu->reset();
	return std::make_pair(std::error_condition(), std::string());
}


/*************************************
 *
 *  ROM definition(s)
 *
 *  Date Information comes from either
 *   ROM labels or from the Self-Test
 *   as "Main"
 *
 *************************************/

/* Home System */

ROM_START( jaguar )
	ROM_REGION16_BE( 0x20000, "mainrom", 0 )
	ROM_LOAD16_WORD( "jagboot.rom", 0x00000, 0x20000, CRC(fb731aaa) SHA1(f8991b0c385f4e5002fa2a7e2f5e61e8c5213356) )

	ROM_REGION32_BE( 0x600000, "cart", ROMREGION_ERASE00 )

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )
ROM_END

ROM_START( jaguarcd )
	ROM_REGION16_BE( 0x20000, "mainrom", 0 )
	ROM_LOAD16_WORD( "jagboot.rom", 0x00000, 0x20000, CRC(fb731aaa) SHA1(f8991b0c385f4e5002fa2a7e2f5e61e8c5213356) )

	ROM_REGION32_BE( 0x600000, "cart", ROMREGION_ERASE00 )
	// TODO: cart needs to be removed (CD BIOS runs in the cart space)

	ROM_REGION16_BE(0x40000, "cdbios", 0 )
	ROM_SYSTEM_BIOS( 0, "default", "Jaguar CD" )
	ROMX_LOAD( "jag_cd.bin", 0x00000, 0x040000, CRC(687068d5) SHA1(73883e7a6e9b132452436f7ab1aeaeb0776428e5), ROM_GROUPWORD | ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "dev", "Jaguar Developer CD" )
	ROMX_LOAD( "jagdevcd.bin", 0x00000, 0x040000, CRC(55a0669c) SHA1(d61b7b5912118f114ef00cf44966a5ef62e455a5), ROM_GROUPWORD | ROM_BIOS(1) )

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )
ROM_END


/****************************************

       ROM & Hard Disk based games

****************************************/

ROM_START( area51t ) /* 68020 based, Area51 Time Warner License - MAIN: Oct 17 1996 17:15:41 / OS: 2.03CJ Oct 17 1996 17:15:01 */
	ROM_REGION( 0x200000, "maincpu", 0 )    /* 2MB for 68020 code */
	ROM_LOAD32_BYTE( "136105-0003-q_h.3h", 0x00000, 0x80000, CRC(0681f398) SHA1(9e96db5a4ff90800685a5b95f8d758d211d3b982) ) /* Also found labeled as AREA51, 68K, D2FF, 3H, 11/20/96 (each item on a separate line) */
	ROM_LOAD32_BYTE( "136105-0002-q_p.3p", 0x00001, 0x80000, CRC(f76cfc68) SHA1(01a781b42b61279e09e0cb1d924e2a3e0df44591) ) /* Also found labeled as AREA51, 68K, 69FE, 3P, 11/20/96 (each item on a separate line) */
	ROM_LOAD32_BYTE( "136105-0001-q_m.3m", 0x00002, 0x80000, CRC(f422b4a8) SHA1(f95ef428be18adafae65e35f412eb03dcdaf7ed4) ) /* Also found labeled as AREA51, 68K, FCFD, 3M, 11/20/96 (each item on a separate line) */
	ROM_LOAD32_BYTE( "136105-0000-q_k.3k", 0x00003, 0x80000, CRC(1fb2f2b5) SHA1(cbed65463dd93eaf945750a9dc3a123d1c6bda42) ) /* Also found labeled as AREA51, 68K, 65FC, 3K, 11/20/96 (each item on a separate line) */

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )

	DISK_REGION( "ide:0:hdd" )
	DISK_IMAGE( "area51t", 0, SHA1(d2865cc7b1bb08a4393a72013a90e18d8a8f9860) )
ROM_END

ROM_START( area51ta ) /* 68020 based, Area51 Time Warner License - MAIN: Nov 27 1995 15:51:56 / OS 2.03CJ Nov 15 1995 13:32:32 */
	ROM_REGION( 0x200000, "maincpu", 0 )    /* 2MB for 68020 code */
	ROM_LOAD32_BYTE( "136105-0003c.3h", 0x00000, 0x80000, CRC(e70a97c4) SHA1(39dabf6bf3dc6f717a587f362d040bfb332be9e1) ) /* Usually found with "orange" labels */
	ROM_LOAD32_BYTE( "136105-0002c.3p", 0x00001, 0x80000, CRC(e9c9f4bd) SHA1(7c6c50372d45dca8929767241b092339f3bab4d2) )
	ROM_LOAD32_BYTE( "136105-0001c.3m", 0x00002, 0x80000, CRC(6f135a81) SHA1(2d9660f240b14481e8c46bc98713e9dc12035063) )
	ROM_LOAD32_BYTE( "136105-0000c.3k", 0x00003, 0x80000, CRC(94f50c14) SHA1(a54552e3ac5c4f481ba4f2fc7d724534576fe76c) )

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )

	DISK_REGION( "ide:0:hdd" )
	DISK_IMAGE( "area51t", 0, SHA1(d2865cc7b1bb08a4393a72013a90e18d8a8f9860) )
ROM_END

ROM_START( area51a ) /* 68020 based, Area51 Atari Games License - MAIN: Oct 25 1995 11:08:10 / OS: 2.03CJ Oct 25 1995 10:19:38 */
	ROM_REGION( 0x200000, "maincpu", 0 )    /* 2MB for 68020 code */
	ROM_LOAD32_BYTE( "136105-0003a.3h", 0x00000, 0x80000, CRC(116d37e6) SHA1(5d36cae792dd349faa77cd2d8018722a28ee55c1) ) /* Usually found with "green" labels */
	ROM_LOAD32_BYTE( "136105-0002a.3p", 0x00001, 0x80000, CRC(eb10f539) SHA1(dadc4be5a442dd4bd17385033056555e528ed994) )
	ROM_LOAD32_BYTE( "136105-0001a.3m", 0x00002, 0x80000, CRC(c6d8322b) SHA1(90cf848a4195c51b505653cc2c74a3b9e3c851b8) )
	ROM_LOAD32_BYTE( "136105-0000a.3k", 0x00003, 0x80000, CRC(729eb1b7) SHA1(21864b4281b1ad17b2903e3aa294e4be74161e80) )

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )

	DISK_REGION( "ide:0:hdd" )
	DISK_IMAGE( "area51", 0, SHA1(3b303bc37e206a6d7339352c869f050d04186f11) )
ROM_END

ROM_START( area51 ) /* R3000 based, Area51 Atari Games License - MAIN: Oct 24 1996 12:02:23 / GUTS: 2.06CJ Nov 11 1996 11:46:43 */
	ROM_REGION( 0x200000, "maincpu", 0 )    /* 2MB for IDT 79R3041 code */
	ROM_LOAD32_BYTE( "2-c_area_51_hh.hh", 0x00000, 0x80000, CRC(13af6a1e) SHA1(69da54ed6886e825156bbcc256e8d7abd4dc1ff8) ) /* Green labels: 2-C AREA 51 HH  - also found labeled as AREA 51 HH   136105-1004 */
	ROM_LOAD32_BYTE( "2-c_area_51_hl.hl", 0x00001, 0x80000, CRC(8ab6649b) SHA1(9b4945bc04f8a73161638a2c5fa2fd84c6fd31b4) ) /* Green labels: 2-C AREA 51 HL  - also found labeled as AREA 51 HL   136105-1003 */
	ROM_LOAD32_BYTE( "2-c_area_51_lh.lh", 0x00002, 0x80000, CRC(a6524f73) SHA1(ae377a6803a4f7d1bbcc111725af121a3e82317d) ) /* Green labels: 2-C AREA 51 LH  - also found labeled as AREA 51 LH   136105-1002 */
	ROM_LOAD32_BYTE( "2-c_area_51_ll.ll", 0x00003, 0x80000, CRC(471b15d2) SHA1(4b5f45ee140b03a6be61475cae1c2dbef0f07457) ) /* Green labels: 2-C AREA 51 LL  - also found labeled as AREA 51 LL   136105-1001 */

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )

	DISK_REGION( "ide:0:hdd" )
	DISK_IMAGE( "area51", 0, SHA1(3b303bc37e206a6d7339352c869f050d04186f11) )
ROM_END


ROM_START( maxforce ) /* R3000 based, labeled as "Maximum Force 5-23-97 v1.05" - Usually found with "light grey" labels */
	ROM_REGION( 0x200000, "maincpu", 0 )    /* 2MB for IDT 79R3041 code */
	ROM_LOAD32_BYTE( "1.05_maximum_force_hh_5-23-97.hh", 0x00000, 0x80000, CRC(ec7f8167) SHA1(0cf057bfb1f30c2c9621d3ed25021e7ba7bdd46e) ) /* Also found labeled as "MAXIMUM FORCE EE FIX PROG" */
	ROM_LOAD32_BYTE( "1.05_maximum_force_hl_5-23-97.hl", 0x00001, 0x80000, CRC(3172611c) SHA1(00f14f871b737c66c20f95743740d964d0be3f24) )
	ROM_LOAD32_BYTE( "1.05_maximum_force_lh_5-23-97.lh", 0x00002, 0x80000, CRC(84d49423) SHA1(88d9a6724f1118f2bbef5dfa27accc2b65c5ba1d) )
	ROM_LOAD32_BYTE( "1.05_maximum_force_ll_5-23-97.ll", 0x00003, 0x80000, CRC(16d0768d) SHA1(665a6d7602a7f2f5b1f332b0220b1533143d56b1) )

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )

	DISK_REGION( "ide:0:hdd" )
	DISK_IMAGE( "maxforce", 0, SHA1(d54e7a8f3866bb2a1d28ae637e7c92ffa4dbe558) )
ROM_END

ROM_START( maxf_102 ) /* R3000 based, labeled as "Maximum Force 2-27-97 v1.02" - Usually found with "yellow" labels */
	ROM_REGION( 0x200000, "maincpu", 0 )    /* 2MB for IDT 79R3041 code */
	ROM_LOAD32_BYTE( "1.02_maximum_force_hh_2-27-97.hh", 0x00000, 0x80000, CRC(8ff7009d) SHA1(da22eae298a6e0e36f503fa091ac3913423dcd0f) ) /* Also found labeled as MAX, FORCE, V. 1.02, PROG, HH, 46FF, 2/27/97 (each item on a separate line) */
	ROM_LOAD32_BYTE( "1.02_maximum_force_hl_2-27-97.hl", 0x00001, 0x80000, CRC(96c2cc1d) SHA1(b332b8c042b92c736131c478cefac1c3c2d2673b) ) /* Also found labeled as MAX, FORCE, V. 1.02, PROG, HL, 14FE, 2/27/97 (each item on a separate line) */
	ROM_LOAD32_BYTE( "1.02_maximum_force_lh_2-27-97.lh", 0x00002, 0x80000, CRC(459ffba5) SHA1(adb40db6904e84c17f32ac6518fd2e994da7883f) ) /* Also found labeled as MAX, FORCE, V. 1.02, PROG, LH, 15FD, 2/27/97 (each item on a separate line) */
	ROM_LOAD32_BYTE( "1.02_maximum_force_ll_2-27-97.ll", 0x00003, 0x80000, CRC(e491be7f) SHA1(cbe281c099a4aa87067752d68cf2bb0ab3900531) ) /* Also found labeled as MAX, FORCE, V. 1.02, PROG, LL, 15FC, 2/27/97 (each item on a separate line) */

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )

	DISK_REGION( "ide:0:hdd" )
	DISK_IMAGE( "maxforce", 0, SHA1(d54e7a8f3866bb2a1d28ae637e7c92ffa4dbe558) )
ROM_END

ROM_START( maxf_ng ) /* R3000 based - MAIN: Apr 18 1997 11:08:45 */
	ROM_REGION( 0x200000, "maincpu", 0 )    /* 2MB for IDT 79R3041 code */
	ROM_LOAD32_BYTE( "maximum_force_no_gore_hh.hh", 0x00000, 0x80000, CRC(08791c02) SHA1(9befbff3201c7d345109b26c296fd8548dbfc95b) )
	ROM_LOAD32_BYTE( "maximum_force_no_gore_hl.hl", 0x00001, 0x80000, CRC(52cf482c) SHA1(ff98b3f04987acef82a97a2ad35a9085fa84e6d5) )
	ROM_LOAD32_BYTE( "maximum_force_no_gore_lh.lh", 0x00002, 0x80000, CRC(ab4ee992) SHA1(69f0fe111d3f5f31151d2922579e5073e484b1e1) )
	ROM_LOAD32_BYTE( "maximum_force_no_gore_ll.ll", 0x00003, 0x80000, CRC(674aab43) SHA1(f79d790538756d1100b7e4ffed192a62a031a2cb) )

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )

	ROM_REGION( 0x800, "user2", 0 ) /* 28C16 style eeprom, currently loaded but not used */
	ROM_LOAD( "28c16.17z", 0x000, 0x800, CRC(1cdd9088) SHA1(4f01f02ff95f31ced87a3cdd7f171afd92551266) )

	DISK_REGION( "ide:0:hdd" )
	DISK_IMAGE( "maxforce", 0, SHA1(d54e7a8f3866bb2a1d28ae637e7c92ffa4dbe558) )
ROM_END


ROM_START( area51mx )   /* 68020 based - MAIN: Apr 22 1998 17:53:57 / GUTS: 2.04CJ Apr 22 1998 17:45:35 */
	ROM_REGION( 0x200000, "maincpu", 0 ) /* 2MB for 68020 code */
	ROM_LOAD32_BYTE( "2.0_68020_max-a51_kit_3h.3h", 0x00000, 0x80000, CRC(47cbf30b) SHA1(23377bcc65c0fc330d5bc7e76e233bae043ac364) ) /* Labeled as 2.0 68020 MAX/A51 KIT 3H */
	ROM_LOAD32_BYTE( "2.0_68020_max-a51_kit_3p.3p", 0x00001, 0x80000, CRC(a3c93684) SHA1(f6b3357bb69900a176fd6bc6b819b2f57b7d0f59) ) /* Labeled as 2.0 68020 MAX/A51 KIT 3P */
	ROM_LOAD32_BYTE( "2.0_68020_max-a51_kit_3m.3m", 0x00002, 0x80000, CRC(d800ac17) SHA1(3d515c8608d8101ee9227116175b3c3f1fe22e0c) ) /* Labeled as 2.0 68020 MAX/A51 KIT 3M */
	ROM_LOAD32_BYTE( "2.0_68020_max-a51_kit_3k.3k", 0x00003, 0x80000, CRC(0e78f308) SHA1(adc4c8e441eb8fe525d0a6220eb3a2a8791a7289) ) /* Labeled as 2.0 68020 MAX/A51 KIT 3K */

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )

	DISK_REGION( "ide:0:hdd" )
	DISK_IMAGE( "area51mx", 0, SHA1(5ff10f4e87094d4449eabf3de7549564ca568c7e) )
ROM_END

ROM_START( a51mxr3k ) /* R3000 based - MAIN: Feb 10 1998 11:52:51 / GUTS: 2.07CJ Feb  5 1998 18:52:26 */
	ROM_REGION( 0x200000, "maincpu", 0 )    /* 2MB for IDT 79R3041 code */
	ROM_LOAD32_BYTE( "1.0_r3k_max-a51_kit_hh.hh", 0x00000, 0x80000, CRC(a984dab2) SHA1(debb3bc11ff49e87a52e89a69533a1bab7db700e) ) /* Labeled as 1.0 R3K MAX/A51 KIT HH */
	ROM_LOAD32_BYTE( "1.0_r3k_max-a51_kit_hl.hl", 0x00001, 0x80000, CRC(0af49d74) SHA1(c19f26056a823fd32293e9a7b3ea868640eabf49) ) /* Labeled as 1.0 R3K MAX/A51 KIT HL */
	ROM_LOAD32_BYTE( "1.0_r3k_max-a51_kit_lh.lh", 0x00002, 0x80000, CRC(d7d94dac) SHA1(2060a74715f36a0d7f5dd0855eda48ad1f20f095) ) /* Labeled as 1.0 R3K MAX/A51 KIT LH */
	ROM_LOAD32_BYTE( "1.0_r3k_max-a51_kit_ll.ll", 0x00003, 0x80000, CRC(ece9e5ae) SHA1(7e44402726f5afa6d1670b27aa43ad13d21c4ad9) ) /* Labeled as 1.0 R3K MAX/A51 KIT LL */

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )

	DISK_REGION( "ide:0:hdd" )
	DISK_IMAGE( "area51mx", 0, SHA1(5ff10f4e87094d4449eabf3de7549564ca568c7e) )
ROM_END

ROM_START( a51mxr3ka ) /* R3000 based - MAIN: Feb  2 1998 14:10:29 / GUTS: 2.07CJ Jan  9 1998 21:11:55 */
	ROM_REGION( 0x200000, "maincpu", 0 )    /* 2MB for IDT 79R3041 code */
	ROM_LOAD32_BYTE( "maxa51_combo_r3k_hh_prog_2-02-98_67ff.hh", 0x00000, 0x80000, CRC(6af8950a) SHA1(33ae123065b14ed8d83635f3351ac5b5c136d206) ) /* Labeled as MAXA51  COMBO  R3K  HH  PROG  2/02/98  67FF (each item on a separate line) */
	ROM_LOAD32_BYTE( "maxa51_combo_r3k_hl_prog_2-02-98_72fe.hl", 0x00001, 0x80000, CRC(30dc3eea) SHA1(2b4e8d43ee28b2d1446c84ff79553a7ce1909f60) ) /* Labeled as MAXA51  COMBO  R3K  HL  PROG  2/02/98  72FE (each item on a separate line) */
	ROM_LOAD32_BYTE( "maxa51_combo_r3k_lh_prog_2-02-98_7ffd.lh", 0x00002, 0x80000, CRC(2c2124af) SHA1(6158644ef126f842a1a4f145141ce847302bbd62) ) /* Labeled as MAXA51  COMBO  R3K  LH  PROG  2/02/98  7FFD (each item on a separate line) */
	ROM_LOAD32_BYTE( "maxa51_combo_r3k_ll_prog_2-02-98_b3fc.ll", 0x00003, 0x80000, CRC(083f4429) SHA1(2be8db7c756a095c87f056da49b8e8832f18bca9) ) /* Labeled as MAXA51  COMBO  R3K  LL  PROG  2/02/98  B3FC (each item on a separate line) */

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )

	DISK_REGION( "ide:0:hdd" )
	DISK_IMAGE( "area51mx", 0, SHA1(5ff10f4e87094d4449eabf3de7549564ca568c7e) )
ROM_END


ROM_START( vcircle )
	ROM_REGION( 0x200000, "maincpu", 0 )    /* 2MB for R3000 code */
	ROM_LOAD32_BYTE( "hh", 0x00000, 0x80000, CRC(7276f5f5) SHA1(716287e370a4f300b1743103f8031afc82de38ca) )
	ROM_LOAD32_BYTE( "hl", 0x00001, 0x80000, CRC(146060a1) SHA1(f291989f1f0ef228757f1990fb14da5ff8f3cf8d) )
	ROM_LOAD32_BYTE( "lh", 0x00002, 0x80000, CRC(be4b2ef6) SHA1(4332b3036e9cb12685e914d085d9a63aa856f0be) )
	ROM_LOAD32_BYTE( "ll", 0x00003, 0x80000, CRC(ba8753eb) SHA1(0322e0e37d814a38d08ba191b1a97fb1a55fe461) )

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )

	DISK_REGION( "ide:0:hdd" )
	DISK_IMAGE( "vcircle", 0, SHA1(bfa79c4cacdc9c2cd6362f62a23056b3e35a2034) )
ROM_END



/****************************************

       ROM based games

****************************************/

ROM_START( fishfren )
	ROM_REGION( 0x200000, "maincpu", 0 )    /* 2MB for R3000 code */
	ROM_LOAD32_BYTE( "hh", 0x00000, 0x80000, CRC(2ef79767) SHA1(abcea584f2cbd71b05f9d7e61f40ca9da6799215) )
	ROM_LOAD32_BYTE( "hl", 0x00001, 0x80000, CRC(7eefd4a2) SHA1(181be04836704098082fd78cacc68ffa70e77892) )
	ROM_LOAD32_BYTE( "lh", 0x00002, 0x80000, CRC(bbe9ed15) SHA1(889af29afe6d984b39105aa238400392a5dfb2c5) )
	ROM_LOAD32_BYTE( "ll", 0x00003, 0x80000, CRC(d70d0f2c) SHA1(2689cbe56ae3d491348b241528b0fe345fa8484c) )

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )

	ROM_REGION32_BE( 0x1000000, "romboard", 0 ) /* 16MB for 64-bit ROM data */
	ROM_LOAD64_BYTE( "l63-56", 0x000000, 0x100000, CRC(42764ea5) SHA1(805245f01006bd974fbac56f688cfcf137ddc914) )
	ROM_LOAD64_BYTE( "l55-48", 0x000001, 0x100000, CRC(0c7592bb) SHA1(d5bd6b872abad58947842205f9eac46fd065e88f) )
	ROM_LOAD64_BYTE( "l47-40", 0x000002, 0x100000, CRC(6d7dcdb1) SHA1(914dae3b9df5c861f794b683571c5fb0c2c3c3fd) )
	ROM_LOAD64_BYTE( "l39-32", 0x000003, 0x100000, CRC(ef3b8d98) SHA1(858c3342e9693bfe887b91dde1116a1656a1a105) )
	ROM_LOAD64_BYTE( "l31-24", 0x000004, 0x100000, CRC(132d628e) SHA1(3ff9fa86092eb01f21ca3ccf1ee1e3a583cbdecb) )
	ROM_LOAD64_BYTE( "l23-16", 0x000005, 0x100000, CRC(b841f039) SHA1(79f661aee009aef2f5ad4122ae3e0ac94097a427) )
	ROM_LOAD64_BYTE( "l15-08", 0x000006, 0x100000, CRC(0800214e) SHA1(5372f2c3470619a4967958c76055486f76b5f150) )
	ROM_LOAD64_BYTE( "l07-00", 0x000007, 0x100000, CRC(f83b2e78) SHA1(83ee9d2bfba83e04fb794270926bd3e558c9aaa4) )
	ROM_LOAD64_BYTE( "h63-56", 0x800000, 0x080000, CRC(67740765) SHA1(8b22413d25e0dbfe2227d1a8a023961a4c13cb76) )
	ROM_LOAD64_BYTE( "h55-48", 0x800001, 0x080000, CRC(ffed0091) SHA1(6c8104acd7e6d95a111f9c7a4d3b6984293d72c4) )
	ROM_LOAD64_BYTE( "h47-40", 0x800002, 0x080000, CRC(6f448f72) SHA1(3a298b9851e4ba7aa611aa6c2b0dcf06f4301463) )
	ROM_LOAD64_BYTE( "h39-32", 0x800003, 0x080000, CRC(25a5bd67) SHA1(79f29bd36afb4574b9c923eee293964284713540) )
	ROM_LOAD64_BYTE( "h31-24", 0x800004, 0x080000, CRC(e7088cc0) SHA1(4cb184de748c5633e669a4675e6db9920d34811e) )
	ROM_LOAD64_BYTE( "h23-16", 0x800005, 0x080000, CRC(ab477a76) SHA1(ae9aa97dbc758cd741710fe08c6ea94a0a318451) )
	ROM_LOAD64_BYTE( "h15-08", 0x800006, 0x080000, CRC(25a423f1) SHA1(7530cf2e28e0755bfcbd70789ef5cbbfb3d94f9f) )
	ROM_LOAD64_BYTE( "h07-00", 0x800007, 0x080000, CRC(0f5f4cc6) SHA1(caa2b514fb1f2a815e63f7b8c6b79ce2dfa308c4) )
	ROM_COPY( "romboard", 0x800000, 0xc00000, 0x400000 )
ROM_END

ROM_START( freezeat )
	ROM_REGION( 0x200000, "maincpu", 0 )    /* 2MB for R3000 code */
	ROM_LOAD32_BYTE( "prog_eng.hh",          0x000000, 0x040000, CRC(f7cffafd) SHA1(62369de4cf0a5abab86f6bcf9621028b9e171ec3) )
	ROM_LOAD32_BYTE( "prog_eng.hl",          0x000001, 0x040000, CRC(17150705) SHA1(c5a32d334bffb58a816920cc1251a21acc5a6f92) )
	ROM_LOAD32_BYTE( "prog_eng.lh",          0x000002, 0x040000, CRC(12a903bf) SHA1(41f5949d7ed2081917af8411f92666b754564b37) )
	ROM_LOAD32_BYTE( "prog_eng.ll",          0x000003, 0x040000, CRC(cf69f971) SHA1(132b06f5fb49801fff7e5deb7aa71b44d5b1c6ca) )

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )

	ROM_REGION32_BE( 0x1000000, "romboard", 0 ) /* 16MB for 64-bit ROM data */
	ROM_LOAD64_BYTE( "fish_gr0.63-56", 0x000000, 0x100000, CRC(b61061c5) SHA1(aeb409aa5073232d80ed81b27946e753290234f4) )
	ROM_LOAD64_BYTE( "fish_gr0.55-48", 0x000001, 0x100000, CRC(c85acf42) SHA1(c3365caeb126a83a7e7afcda25f05849ceb5c98b) )
	ROM_LOAD64_BYTE( "fish_gr0.47-40", 0x000002, 0x100000, CRC(67f78f59) SHA1(40b256a8939fad365c7e896cff4a959fcc70a477) )
	ROM_LOAD64_BYTE( "fish_gr0.39-32", 0x000003, 0x100000, CRC(6be0508a) SHA1(20f617278ce1666348822d80686cecd8d9b1bc78) )
	ROM_LOAD64_BYTE( "fish_gr0.31-24", 0x000004, 0x100000, CRC(905606e0) SHA1(866cd98ea2399fed96f76b16dce751e2c7cfdc98) )
	ROM_LOAD64_BYTE( "fish_gr0.23-16", 0x000005, 0x100000, CRC(cdeef6fa) SHA1(1b4d58951b662040540e7d51f88c1b6f282562ee) )
	ROM_LOAD64_BYTE( "fish_gr0.15-08", 0x000006, 0x100000, CRC(ad81f204) SHA1(58584a6c8c6cfb6366eaa10aba8a226e419f5ce9) )
	ROM_LOAD64_BYTE( "fish_gr0.07-00", 0x000007, 0x100000, CRC(10ce7254) SHA1(2a88d45dbe78ea8358ecd8522b38d775a2fdb34a) )
	ROM_LOAD64_BYTE( "fish_eng.63-56", 0x800000, 0x100000, CRC(4a03f971) SHA1(1ae5ad9a6cd2d612c6519193134dcd5a3f6a5049) )
	ROM_LOAD64_BYTE( "fish_eng.55-48", 0x800001, 0x100000, CRC(6bc00de0) SHA1(b1b180c33906826703452875ce250b28352e2797) )
	ROM_LOAD64_BYTE( "fish_eng.47-40", 0x800002, 0x100000, CRC(41ccc677) SHA1(76ee042632cfdcc99a9bfb75f2a4ef04e08f101b) )
	ROM_LOAD64_BYTE( "fish_eng.39-32", 0x800003, 0x100000, CRC(59a8fa03) SHA1(19e91a4791e0d2dbd8578cee0fa07c491204b0dc) )
	ROM_LOAD64_BYTE( "fish_eng.31-24", 0x800004, 0x100000, CRC(c3bb50a1) SHA1(b868ac0812d1c13feae82d293bb323a93a72e1d3) )
	ROM_LOAD64_BYTE( "fish_eng.23-16", 0x800005, 0x100000, CRC(237cfc93) SHA1(15f61dc621c5328cc7752c76b2b1dae265a5e886) )
	ROM_LOAD64_BYTE( "fish_eng.15-08", 0x800006, 0x100000, CRC(65bec279) SHA1(5e99972279ee9ad32e67866fc63799579a10f2dd) )
	ROM_LOAD64_BYTE( "fish_eng.07-00", 0x800007, 0x100000, CRC(13fa20ad) SHA1(0a04fdea025109c0e604ef2a6d58cfb3adce9bd1) )
ROM_END

ROM_START( freezeatjp )
	ROM_REGION( 0x200000, "maincpu", 0 )    /* 2MB for R3000 code */
	ROM_LOAD32_BYTE( "prog_jpn.hh",          0x000000, 0x040000, CRC(989302bf) SHA1(232927ec0a52b8bb587a3c206af8e1c6cde67860) )
	ROM_LOAD32_BYTE( "prog_jpn.hl",          0x000001, 0x040000, CRC(6262b760) SHA1(12ca749f5cdc6db7d19f88a21f5f955b80206784) )
	ROM_LOAD32_BYTE( "prog_jpn.lh",          0x000002, 0x040000, CRC(c6a12b0c) SHA1(971242b5b09e15164e7c335e684b5043510c6462) )
	ROM_LOAD32_BYTE( "prog_jpn.ll",          0x000003, 0x040000, CRC(241ea755) SHA1(0db3cfbe577fc78387528390ebb14dbb7a09c97d) )

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )

	ROM_REGION32_BE( 0x1000000, "romboard", 0 ) /* 16MB for 64-bit ROM data */
	ROM_LOAD64_BYTE( "fish_gr0.63-56", 0x000000, 0x100000, CRC(b61061c5) SHA1(aeb409aa5073232d80ed81b27946e753290234f4) )
	ROM_LOAD64_BYTE( "fish_gr0.55-48", 0x000001, 0x100000, CRC(c85acf42) SHA1(c3365caeb126a83a7e7afcda25f05849ceb5c98b) )
	ROM_LOAD64_BYTE( "fish_gr0.47-40", 0x000002, 0x100000, CRC(67f78f59) SHA1(40b256a8939fad365c7e896cff4a959fcc70a477) )
	ROM_LOAD64_BYTE( "fish_gr0.39-32", 0x000003, 0x100000, CRC(6be0508a) SHA1(20f617278ce1666348822d80686cecd8d9b1bc78) )
	ROM_LOAD64_BYTE( "fish_gr0.31-24", 0x000004, 0x100000, CRC(905606e0) SHA1(866cd98ea2399fed96f76b16dce751e2c7cfdc98) )
	ROM_LOAD64_BYTE( "fish_gr0.23-16", 0x000005, 0x100000, CRC(cdeef6fa) SHA1(1b4d58951b662040540e7d51f88c1b6f282562ee) )
	ROM_LOAD64_BYTE( "fish_gr0.15-08", 0x000006, 0x100000, CRC(ad81f204) SHA1(58584a6c8c6cfb6366eaa10aba8a226e419f5ce9) )
	ROM_LOAD64_BYTE( "fish_gr0.07-00", 0x000007, 0x100000, CRC(10ce7254) SHA1(2a88d45dbe78ea8358ecd8522b38d775a2fdb34a) )
	ROM_LOAD64_BYTE( "fish_jpn.63-56", 0x800000, 0x100000, CRC(78c65a1f) SHA1(1a97737c222809930bde9df7a55e1ff1581a3202) )
	ROM_LOAD64_BYTE( "fish_jpn.55-48", 0x800001, 0x100000, CRC(9ffac0f1) SHA1(3ba5f8de32a5febb5d3d22f59ccb477834d33934) )
	ROM_LOAD64_BYTE( "fish_jpn.47-40", 0x800002, 0x100000, CRC(18543fb7) SHA1(4ab9969de9a66d6b7b70cfa5290c1cf7bce54838) )
	ROM_LOAD64_BYTE( "fish_jpn.39-32", 0x800003, 0x100000, CRC(22578f15) SHA1(4e314b22456ed4e4282406e990207e3b9bdf6203) )
	ROM_LOAD64_BYTE( "fish_jpn.31-24", 0x800004, 0x100000, CRC(5c41b91b) SHA1(ce354c8a4a3872b009e8af9f75e8f4f0892c7a7e) )
	ROM_LOAD64_BYTE( "fish_jpn.23-16", 0x800005, 0x100000, CRC(c2462646) SHA1(207d51a2aae076bc78548cf96325e670ea41609c) )
	ROM_LOAD64_BYTE( "fish_jpn.15-08", 0x800006, 0x100000, CRC(f8d998ec) SHA1(ffce4a16fbb2fff3dc0a29c0cede4dfe6316e97b) )
	ROM_LOAD64_BYTE( "fish_jpn.07-00", 0x800007, 0x100000, CRC(e7e0daa5) SHA1(108da84cc6b4df7ae88cfdacd27c1728e59cdb81) )
ROM_END

ROM_START( freezeat2 )
	ROM_REGION( 0x200000, "maincpu", 0 )    /* 2MB for R3000 code */
	ROM_LOAD32_BYTE( "prog.hh",  0x000000, 0x040000, CRC(a8aefa52) SHA1(ba95da93035520de4b15245f68217c59dfb69dbd) ) // sldh
	ROM_LOAD32_BYTE( "prog.hl",  0x000001, 0x040000, CRC(152dd641) SHA1(52fa260baf1979ed8f15f8abcbbeebd8e595d0e4) ) // sldh
	ROM_LOAD32_BYTE( "prog.lh",  0x000002, 0x040000, CRC(416d26ed) SHA1(11cf3b88415a8a5d0bb8e1df08603a85202186ef) ) // sldh
	ROM_LOAD32_BYTE( "prog.ll",  0x000003, 0x040000, CRC(d6a5dbc8) SHA1(0e2176c35cbc59b2a5283366210409d0e930bac7) ) // sldh

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )

	ROM_REGION32_BE( 0x1000000, "romboard", 0 ) /* 16MB for 64-bit ROM data */
	ROM_LOAD64_BYTE( "fish_gr0.63-56", 0x000000, 0x100000, CRC(99d0dc75) SHA1(b32126eea70c7584d1c34a6ca33282fbaf4b03aa) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.55-48", 0x000001, 0x100000, CRC(2dfdfe62) SHA1(e0554d36ef5cf4b6ce171857ea4f2737f11286a5) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.47-40", 0x000002, 0x100000, CRC(722aee2a) SHA1(bc79433131bed5b08453d1b80324a28a552783de) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.39-32", 0x000003, 0x100000, CRC(919e31b4) SHA1(3807d4629d8277c780dba888c23d17ba47803f27) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.31-24", 0x000004, 0x100000, CRC(a957ac95) SHA1(ddfaca994c06976bee8b123857904e64f40b7f31) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.23-16", 0x000005, 0x100000, CRC(a147ec66) SHA1(6291008158d581b81e025ed34ff0950983c12c67) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.15-08", 0x000006, 0x100000, CRC(206d2f38) SHA1(6aca89df26d3602ff1da3c23f19e0782439623ff) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.07-00", 0x000007, 0x100000, CRC(06559831) SHA1(b2c022457425d7900337cfa2fd1622336c0c0bc5) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.63-56", 0x800000, 0x100000, CRC(30c624d2) SHA1(4ced77d1663169d0cb37d6728ec52e67f05064c5) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.55-48", 0x800001, 0x100000, CRC(049cd60f) SHA1(8a7615a76b57a4e6ef5d95a5ee6c56086671dbb6) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.47-40", 0x800002, 0x100000, CRC(d6aaf3bf) SHA1(1c597bdc0e61fd0941cff5a8a93f24f108bd0daa) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.39-32", 0x800003, 0x100000, CRC(7d6ebc69) SHA1(668769297f75f9c367bc5cde26419ed092fc9dd8) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.31-24", 0x800004, 0x100000, CRC(6e5fee1f) SHA1(1eca79c8d395f881d0a05f10073998fcae70c3b1) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.23-16", 0x800005, 0x100000, CRC(a8b1e9b4) SHA1(066285928e574e656510b90bc212a8d86660bd07) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.15-08", 0x800006, 0x100000, CRC(c90080e6) SHA1(a764bdd6b4e9e727f7468a53424a9211ec5fd5a8) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.07-00", 0x800007, 0x100000, CRC(1f20c020) SHA1(71b32386dc0444264f2f1e2a81899e0e9260994c) ) // sldh
ROM_END

ROM_START( freezeat3 )
	ROM_REGION( 0x200000, "maincpu", 0 )    /* 2MB for R3000 code */
	ROM_LOAD32_BYTE( "prog.hh",  0x000000, 0x040000, CRC(863942e6) SHA1(c7429c8a5c86ff93c64950e201cffca83dd7b7b0) ) // sldh
	ROM_LOAD32_BYTE( "prog.hl",  0x000001, 0x040000, CRC(2acc18ef) SHA1(ead02566f7641b1d1066bd2e257b695e5c7e8437) ) // sldh
	ROM_LOAD32_BYTE( "prog.lh",  0x000002, 0x040000, CRC(948cf20c) SHA1(86c757aa3c849ef5ba94ed4d5dbf10e833dab6bd) ) // sldh
	ROM_LOAD32_BYTE( "prog.ll",  0x000003, 0x040000, CRC(5f44969e) SHA1(32345d7c56a3a890e71f8c71f25414d442b60af8) ) // sldh

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )

	ROM_REGION32_BE( 0x1000000, "romboard", 0 ) /* 16MB for 64-bit ROM data */
	ROM_LOAD64_BYTE( "fish_gr0.63-56", 0x000000, 0x100000, CRC(36799449) SHA1(bb706fe7fdc68f840702a127eed7d4519dd45869) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.55-48", 0x000001, 0x100000, CRC(23959947) SHA1(a35a6e62c7b2be57d41b1b64be93713cbf897f0a) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.47-40", 0x000002, 0x100000, CRC(4657e4e0) SHA1(b6c07182babcb0a106bf4a8f2e3f524371dd882d) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.39-32", 0x000003, 0x100000, CRC(b6ea4b64) SHA1(176f94f14307c40b9c611d6f6bc9118e498cdfad) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.31-24", 0x000004, 0x100000, CRC(7d4ce71f) SHA1(a1cf5aa9df8dd29c777c10cfdce0925981584261) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.23-16", 0x000005, 0x100000, CRC(02db4fd1) SHA1(fce6f31802bf36d6b006f0b212f553bdf21f9374) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.15-08", 0x000006, 0x100000, CRC(7d496d6c) SHA1(f82db0621729a00acf4077482e9dfab040ac829b) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.07-00", 0x000007, 0x100000, CRC(3aa389d8) SHA1(52502f2f3c91d7c29261f60fe8f489a352399c96) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.63-56", 0x800000, 0x100000, CRC(ead678c9) SHA1(f83d467f6685965b6176b10adbd4e35ef808baf3) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.55-48", 0x800001, 0x100000, CRC(3591e752) SHA1(df242d2f724edfd78f7191f0ba7a8cde2c09b25f) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.47-40", 0x800002, 0x100000, CRC(e29a7a6c) SHA1(0bfb26076b390492eed81d4c4f0852c64fdccfce) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.39-32", 0x800003, 0x100000, CRC(e980f957) SHA1(78e8ef07f443ce7991a46005627d5802d36d731c) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.31-24", 0x800004, 0x100000, CRC(d90c5221) SHA1(7a330f39f3751d58157f872d92c3c2b91fe60d14) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.23-16", 0x800005, 0x100000, CRC(9be0d4de) SHA1(9bb67a1f1db77483e896fed7096c1e23c153ede4) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.15-08", 0x800006, 0x100000, CRC(122248af) SHA1(80dd5486106d475bd9f6d78919ebeb176e7becff) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.07-00", 0x800007, 0x100000, CRC(5ae08327) SHA1(822d8292793509ebfbfce27e92a74c78c4328bda) ) // sldh
ROM_END

ROM_START( freezeat4 )
	ROM_REGION( 0x200000, "maincpu", 0 )    /* 2MB for R3000 code */
	ROM_LOAD32_BYTE( "prog.hh",  0x000000, 0x040000, CRC(80336f5e) SHA1(9946e8eebec2cd68db059f40f535ea212f41913d) ) // sldh
	ROM_LOAD32_BYTE( "prog.hl",  0x000001, 0x040000, CRC(55125520) SHA1(13be4fbf32bcd94a2ea97fd690bd1dfdff146d33) ) // sldh
	ROM_LOAD32_BYTE( "prog.lh",  0x000002, 0x040000, CRC(9d99c794) SHA1(f443f05a5979db66d61ef4174f0369a1cf4b7793) ) // sldh
	ROM_LOAD32_BYTE( "prog.ll",  0x000003, 0x040000, CRC(e03700e0) SHA1(24d41750f02ee7e8fb379e517751b661400aa521) ) // sldh

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )

	ROM_REGION32_BE( 0x1000000, "romboard", 0 ) /* 16MB for 64-bit ROM data */
	ROM_LOAD64_BYTE( "fish_gr0.63-56", 0x000000, 0x100000, CRC(36799449) SHA1(bb706fe7fdc68f840702a127eed7d4519dd45869) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.55-48", 0x000001, 0x100000, CRC(23959947) SHA1(a35a6e62c7b2be57d41b1b64be93713cbf897f0a) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.47-40", 0x000002, 0x100000, CRC(4657e4e0) SHA1(b6c07182babcb0a106bf4a8f2e3f524371dd882d) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.39-32", 0x000003, 0x100000, CRC(b6ea4b64) SHA1(176f94f14307c40b9c611d6f6bc9118e498cdfad) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.31-24", 0x000004, 0x100000, CRC(7d4ce71f) SHA1(a1cf5aa9df8dd29c777c10cfdce0925981584261) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.23-16", 0x000005, 0x100000, CRC(02db4fd1) SHA1(fce6f31802bf36d6b006f0b212f553bdf21f9374) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.15-08", 0x000006, 0x100000, CRC(7d496d6c) SHA1(f82db0621729a00acf4077482e9dfab040ac829b) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.07-00", 0x000007, 0x100000, CRC(3aa389d8) SHA1(52502f2f3c91d7c29261f60fe8f489a352399c96) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.63-56", 0x800000, 0x100000, CRC(c91b6ee4) SHA1(58d2d6b1b9847150b8b3e358842c4a097ef91475) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.55-48", 0x800001, 0x100000, CRC(65528e55) SHA1(18020cababed379f77149b7e89e80b294766df31) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.47-40", 0x800002, 0x100000, CRC(8fe4187f) SHA1(c9ceec40688617e1251142465d0e608f80a83e40) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.39-32", 0x800003, 0x100000, CRC(fdf05a42) SHA1(849e224b68be2fb396ee4cb4729517470af7c282) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.31-24", 0x800004, 0x100000, CRC(bb2cd741) SHA1(ac55a54c702d222cb1b9bb480b0f7a71bc315878) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.23-16", 0x800005, 0x100000, CRC(ea8c5984) SHA1(eca1619c17dfac154a2024ec49b4b4f9f06a50c9) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.15-08", 0x800006, 0x100000, CRC(0b00c816) SHA1(879b0e9d92fe737d740c348dc1cc376c8abfbdb8) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.07-00", 0x800007, 0x100000, CRC(a84335c3) SHA1(340f5ddb9bff1ecd469eab8be36cc0ede84f1f5e) ) // sldh
ROM_END

ROM_START( freezeat5 )
	ROM_REGION( 0x200000, "maincpu", 0 )    /* 2MB for R3000 code */
	ROM_LOAD32_BYTE( "prog.hh",  0x000000, 0x040000, CRC(95c4fc64) SHA1(cd00efe7f760ef1e4cdc4bc8a3b368427cb15d8a) ) // sldh
	ROM_LOAD32_BYTE( "prog.hl",  0x000001, 0x040000, CRC(ffb9cb71) SHA1(35d6a5440d63bc5b94c4447645365039169da368) ) // sldh
	ROM_LOAD32_BYTE( "prog.lh",  0x000002, 0x040000, CRC(3ddacd80) SHA1(79f9650531847eefd83908b6ea1e8362688b377c) ) // sldh
	ROM_LOAD32_BYTE( "prog.ll",  0x000003, 0x040000, CRC(95ebefb0) SHA1(b88b12adabd7b0902c3a78919bcec8d9a2b04168) ) // sldh

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )

	ROM_REGION32_BE( 0x1000000, "romboard", 0 ) /* 16MB for 64-bit ROM data */
	ROM_LOAD64_BYTE( "fish_gr0.63-56", 0x000000, 0x100000, CRC(404a10c3) SHA1(8e353ac7608bd54f0fea610c85166ad14f2faadb) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.55-48", 0x000001, 0x100000, CRC(0b262f2f) SHA1(2a963cb5c3344091406d090edfdda498709c6aa6) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.47-40", 0x000002, 0x100000, CRC(43f86d26) SHA1(b31d36b11052514b5bcd5bf8e400457ca572c306) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.39-32", 0x000003, 0x100000, CRC(5cf0228f) SHA1(7a8c59cf9a7744e9f332db5f661f507323375968) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.31-24", 0x000004, 0x100000, CRC(7a24ff98) SHA1(db9e0e8bb417f187267a6e4fc1e66ff060ee4096) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.23-16", 0x000005, 0x100000, CRC(ea163c93) SHA1(d07ed26191d36497c56b15774625a49ecb958386) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.15-08", 0x000006, 0x100000, CRC(d364534f) SHA1(153908bb8929a898945f768f8bc3d853c6aeaceb) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.07-00", 0x000007, 0x100000, CRC(7ba4cb0d) SHA1(16bd487123f499b7080596dc76253081179a0f66) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.63-56", 0x800000, 0x100000, CRC(0e1fc4a9) SHA1(a200bb0af5f1e2c3f8d221ae4e9ba55b9dfb8550) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.55-48", 0x800001, 0x100000, CRC(b696b875) SHA1(16dc4d5cee3f08360cf19926584419c21d781f45) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.47-40", 0x800002, 0x100000, CRC(e78d9302) SHA1(f8b5ed992c433d63677edbeafd3e465b1d42b455) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.39-32", 0x800003, 0x100000, CRC(9b50374c) SHA1(d8af3c9d8e0459e24b974cdf2e75c7c39582912f) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.31-24", 0x800004, 0x100000, CRC(b6a19b7e) SHA1(5668b27db4dade8efb1524b8ecd1fe78498e8460) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.23-16", 0x800005, 0x100000, CRC(ff835b67) SHA1(19da2de1d067069871c33c8b25fd2eac2d03f627) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.15-08", 0x800006, 0x100000, CRC(8daf6995) SHA1(2f44031378b5fb1ba1f80a966dbe902316dc6fe8) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.07-00", 0x800007, 0x100000, CRC(3676ac70) SHA1(640c4d4f53ca2bcae2009e402fd6ad70e40defa4) ) // sldh
ROM_END

ROM_START( freezeat6 )
	ROM_REGION( 0x200000, "maincpu", 0 )    /* 2MB for R3000 code */
	ROM_LOAD32_BYTE( "prog.hh",  0x000000, 0x040000, CRC(120711fe) SHA1(387e3cc8a1a9ea7d65c528387891d09ed9889fe3) ) // sldh
	ROM_LOAD32_BYTE( "prog.hl",  0x000001, 0x040000, CRC(18dd292a) SHA1(00e79851140716985f43594142c97e510a06b24a) ) // sldh
	ROM_LOAD32_BYTE( "prog.lh",  0x000002, 0x040000, CRC(ce387e72) SHA1(021a274da0b828550a47c3778e1059d4e759693a) ) // sldh
	ROM_LOAD32_BYTE( "prog.ll",  0x000003, 0x040000, CRC(9b307b7c) SHA1(71b696802fe7c867525d2626351dcfacedabd696) ) // sldh

	ROM_REGION16_BE( 0x1000, "waverom", 0 )
	ROM_LOAD16_WORD("jagwave.rom", 0x0000, 0x1000, CRC(7a25ee5b) SHA1(58117e11fd6478c521fbd3fdbe157f39567552f0) )

	ROM_REGION32_BE( 0x1000000, "romboard", 0 ) /* 16MB for 64-bit ROM data */
	ROM_LOAD64_BYTE( "fish_gr0.63-56", 0x000000, 0x100000, CRC(293a3308) SHA1(e4c88759c3b8f8a359db83817dbd0428350b4f7e) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.55-48", 0x000001, 0x100000, CRC(18bb4bdf) SHA1(1f6c49b3b5946390fa7582b531f8d9af3baa2567) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.47-40", 0x000002, 0x100000, CRC(1faedcc6) SHA1(1e4ecbe4553fb3ebfbd03bd7e16066ccb531d00b) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.39-32", 0x000003, 0x100000, CRC(536bc349) SHA1(06d7ac38b2c8cdc85e2cb531bba9c836e50c8247) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.31-24", 0x000004, 0x100000, CRC(813d4a31) SHA1(e024f9da2f15a482d8142870baf487297b995ed9) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.23-16", 0x000005, 0x100000, CRC(f881514b) SHA1(a694f90621e2c1569a6a5ed8920838ba5506f72e) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.15-08", 0x000006, 0x100000, CRC(d7634655) SHA1(d7ac83c0fa5d0ec57d096d4d704fe99ee8160e09) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr0.07-00", 0x000007, 0x100000, CRC(3fca32a3) SHA1(22753a9678e04d9355238e013e58d9f45315579d) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.63-56", 0x800000, 0x100000, CRC(a2b89d3a) SHA1(9cfcd0b88dea192ba39efcdccc78d1a0fd8f3388) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.55-48", 0x800001, 0x100000, CRC(766822a8) SHA1(2c9b14542a5467c1a3451559ea296da09c2cfdb9) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.47-40", 0x800002, 0x100000, CRC(112b519c) SHA1(f0e1ed1b8ad271fa9708f513b11d5cca6e550668) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.39-32", 0x800003, 0x100000, CRC(435b5d37) SHA1(ecb6e7271d993f8e315b85e69166838e66dd41a8) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.31-24", 0x800004, 0x100000, CRC(2637ae7f) SHA1(5e0bd0e08d8c1eaae725b4d55030c2698abd46e7) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.23-16", 0x800005, 0x100000, CRC(e732f1bf) SHA1(a228aee0cc36a0089716f20bfa75d87750692adb) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.15-08", 0x800006, 0x100000, CRC(7d4e2d9e) SHA1(4cb9b754b7585df4cae6bdd7085a57729d53e643) ) // sldh
	ROM_LOAD64_BYTE( "fish_gr1.07-00", 0x800007, 0x100000, CRC(8ea036af) SHA1(1f9baec6712e0ba0e8a744529e41799217760194) ) // sldh
ROM_END


/*************************************
 *
 *  Driver initialization
 *
 *************************************/

void jaguar_state::cojag_common_init(uint16_t gpu_jump_offs, uint16_t spin_pc)
{
	m_is_cojag = true;

	/* copy over the ROM */
	m_is_r3000 = (m_maincpu->type() == R3041);

	/* install synchronization hooks for GPU */
	if (m_is_r3000)
		m_maincpu->space(AS_PROGRAM).install_write_handler(0x04f0b000 + gpu_jump_offs, 0x04f0b003 + gpu_jump_offs, write32s_delegate(*this, FUNC(jaguar_state::gpu_jump_w)));
	else
		m_maincpu->space(AS_PROGRAM).install_write_handler(0xf0b000 + gpu_jump_offs, 0xf0b003 + gpu_jump_offs, write32s_delegate(*this, FUNC(jaguar_state::gpu_jump_w)));
	m_gpu->space(AS_PROGRAM).install_read_handler(0xf03000 + gpu_jump_offs, 0xf03003 + gpu_jump_offs, read32smo_delegate(*this, FUNC(jaguar_state::gpu_jump_r)));
	m_gpu_jump_address = &m_gpu_ram[gpu_jump_offs/4];
	m_gpu_spin_pc = 0xf03000 + spin_pc;
}


void jaguar_state::init_area51a()
{
	m_hacks_enabled = true;
	cojag_common_init(0x5c4, 0x5a0);

#if ENABLE_SPEEDUP_HACKS
	/* install speedup for main CPU */
	m_maincpu->space(AS_PROGRAM).install_write_handler(0xa02030, 0xa02033, write32s_delegate(*this, FUNC(jaguar_state::area51_main_speedup_w)));
	m_main_speedup = &m_mainram[0x2030/4];
#endif
}


void jaguar_state::init_area51()
{
	m_hacks_enabled = true;
	cojag_common_init(0x0c0, 0x09e);

#if ENABLE_SPEEDUP_HACKS
	/* install speedup for main CPU */
	m_main_speedup_max_cycles = 120;
	m_maincpu->space(AS_PROGRAM).install_read_handler(0x100062e8, 0x100062eb, read32smo_delegate(*this, FUNC(jaguar_state::cojagr3k_main_speedup_r)));
	m_main_speedup = &m_mainram[0x62e8/4];
#endif
}

void jaguar_state::init_maxforce()
{
	m_hacks_enabled = true;
	cojag_common_init(0x0c0, 0x09e);

	/* patch the protection */
	memregion("maincpu")->as_u32(0x220/4) = 0x03e00008;

#if ENABLE_SPEEDUP_HACKS
	/* install speedup for main CPU */
	m_main_speedup_max_cycles = 120;
	m_maincpu->space(AS_PROGRAM).install_read_handler(0x1000865c, 0x1000865f, read32smo_delegate(*this, FUNC(jaguar_state::cojagr3k_main_speedup_r)));
	m_main_speedup = &m_mainram[0x865c/4];
#endif
}


void jaguar_state::init_area51mx()
{
	m_hacks_enabled = true;
	cojag_common_init(0x0c0, 0x09e);

	/* patch the protection */
	memregion("maincpu")->as_u32(0x418/4) = 0x4e754e75;

#if ENABLE_SPEEDUP_HACKS
	/* install speedup for main CPU */
	m_maincpu->space(AS_PROGRAM).install_write_handler(0xa19550, 0xa19557, write32s_delegate(*this, FUNC(jaguar_state::area51mx_main_speedup_w)));
	m_main_speedup = &m_mainram[0x19550/4];
#endif
}


void jaguar_state::init_a51mxr3k()
{
	m_hacks_enabled = true;
	cojag_common_init(0x0c0, 0x09e);

	/* patch the protection */
	memregion("maincpu")->as_u32(0x220/4) = 0x03e00008;

#if ENABLE_SPEEDUP_HACKS
	/* install speedup for main CPU */
	m_main_speedup_max_cycles = 120;
	m_maincpu->space(AS_PROGRAM).install_read_handler(0x10006f0c, 0x10006f0f, read32smo_delegate(*this, FUNC(jaguar_state::cojagr3k_main_speedup_r)));
	m_main_speedup = &m_mainram[0x6f0c/4];
#endif
}


void jaguar_state::init_fishfren()
{
	m_hacks_enabled = true;
	cojag_common_init(0x578, 0x554);

#if ENABLE_SPEEDUP_HACKS
	/* install speedup for main CPU */
	m_main_speedup_max_cycles = 200;
	m_maincpu->space(AS_PROGRAM).install_read_handler(0x10021b60, 0x10021b63, read32smo_delegate(*this, FUNC(jaguar_state::cojagr3k_main_speedup_r)));
	m_main_speedup = &m_mainram[0x21b60/4];
#endif
}


void jaguar_state::init_freeze_common(offs_t main_speedup_addr)
{
	cojag_common_init(0x0bc, 0x09c);

#if ENABLE_SPEEDUP_HACKS
	/* install speedup for main CPU */
	m_main_speedup_max_cycles = 200;
	if (main_speedup_addr != 0) {
		m_maincpu->space(AS_PROGRAM).install_read_handler(main_speedup_addr, main_speedup_addr + 3, read32smo_delegate(*this, FUNC(jaguar_state::cojagr3k_main_speedup_r)));
		m_main_speedup = &m_mainram[(main_speedup_addr - 0x10000000)/4];
	}
	m_maincpu->space(AS_PROGRAM).install_read_handler(0x0400d900, 0x0400d900 + 3, read32smo_delegate(*this, FUNC(jaguar_state::main_gpu_wait_r)));
	m_main_gpu_wait = &m_shared_ram[0xd900/4];
#endif
}

void jaguar_state::init_freezeat()  { m_hacks_enabled = true; init_freeze_common(0x1001a9f4); }
void jaguar_state::init_freezeat2() { m_hacks_enabled = true; init_freeze_common(0x1001a8c4); }
void jaguar_state::init_freezeat3() { m_hacks_enabled = true; init_freeze_common(0x1001a134); }
void jaguar_state::init_freezeat4() { m_hacks_enabled = true; init_freeze_common(0x1001a134); }
void jaguar_state::init_freezeat5() { m_hacks_enabled = true; init_freeze_common(0x10019b34); }
void jaguar_state::init_freezeat6() { m_hacks_enabled = true; init_freeze_common(0x10019684); }

void jaguar_state::init_vcircle()
{
	m_hacks_enabled = true;
	cojag_common_init(0x5c0, 0x5a0);

#if ENABLE_SPEEDUP_HACKS
	/* install speedup for main CPU */
	m_main_speedup_max_cycles = 50;
	m_maincpu->space(AS_PROGRAM).install_read_handler(0x12005b34, 0x12005b37, read32smo_delegate(*this, FUNC(jaguar_state::cojagr3k_main_speedup_r)));
	m_main_speedup = &m_mainram[0x5b34/4];
	m_main_speedup = &m_mainram2[0x5b34/4]; // FIXME: overwriting immediately after assigning?
#endif
}



/*************************************
 *
 *  Game driver(s)
 *
 *************************************/

/*    YEAR  NAME        PARENT    COMPAT  MACHINE   INPUT     CLASS           INIT           COMPANY    FULLNAME */
CONS( 1993, jaguar,     0,        0,      jaguar,   jaguar,   jaguar_state,   init_jaguar,   "Atari",   "Jaguar (NTSC)",    MACHINE_UNEMULATED_PROTECTION | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING )
CONS( 1995, jaguarcd,   jaguar,   0,      jaguarcd, jaguar,   jaguarcd_state, init_jaguarcd, "Atari",   "Jaguar CD (NTSC)", MACHINE_UNEMULATED_PROTECTION | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING )

/*    YEAR  NAME        PARENT    MACHINE       INPUT     CLASS         INIT            ROT   COMPANY        FULLNAME */
GAME( 1996, area51,     0,        cojagr3k,     area51,   jaguar_state, init_area51,    ROT0, "Atari Games", "Area 51 (R3000)", 0 )
GAME( 1995, area51t,    area51,   cojag68k,     area51,   jaguar_state, init_area51a,   ROT0, "Atari Games (Time Warner license)", "Area 51 (Time Warner license, Oct 17, 1996)", 0 )
GAME( 1995, area51ta,   area51,   cojag68k,     area51,   jaguar_state, init_area51a,   ROT0, "Atari Games (Time Warner license)", "Area 51 (Time Warner license, Nov 27, 1995)", 0 )
GAME( 1995, area51a,    area51,   cojag68k,     area51,   jaguar_state, init_area51a,   ROT0, "Atari Games", "Area 51 (Atari Games license, Oct 25, 1995)", 0 )
GAME( 1995, fishfren,   0,        cojagr3k_rom, fishfren, jaguar_state, init_fishfren,  ROT0, "Time Warner Interactive", "Fishin' Frenzy (prototype)", 0 )
GAME( 1996, freezeat,   0,        cojagr3k_rom, freezeat, jaguar_state, init_freezeat,  ROT0, "Atari Games", "Freeze (Atari) (prototype, English voice, 96/10/25)", 0 )
GAME( 1996, freezeatjp, freezeat, cojagr3k_rom, freezeat, jaguar_state, init_freezeat,  ROT0, "Atari Games", "Freeze (Atari) (prototype, Japanese voice, 96/10/25)", 0 )
GAME( 1996, freezeat2,  freezeat, cojagr3k_rom, freezeat, jaguar_state, init_freezeat2, ROT0, "Atari Games", "Freeze (Atari) (prototype, 96/10/18)", 0 )
GAME( 1996, freezeat3,  freezeat, cojagr3k_rom, freezeat, jaguar_state, init_freezeat3, ROT0, "Atari Games", "Freeze (Atari) (prototype, 96/10/07)", 0 )
GAME( 1996, freezeat4,  freezeat, cojagr3k_rom, freezeat, jaguar_state, init_freezeat4, ROT0, "Atari Games", "Freeze (Atari) (prototype, 96/10/03)", 0 )
GAME( 1996, freezeat5,  freezeat, cojagr3k_rom, freezeat, jaguar_state, init_freezeat5, ROT0, "Atari Games", "Freeze (Atari) (prototype, 96/09/20, AMOA-96)", 0 )
GAME( 1996, freezeat6,  freezeat, cojagr3k_rom, freezeat, jaguar_state, init_freezeat6, ROT0, "Atari Games", "Freeze (Atari) (prototype, 96/09/07, Jamma-96)", 0 )
GAME( 1996, maxforce,   0,        cojagr3k,     area51,   jaguar_state, init_maxforce,  ROT0, "Atari Games", "Maximum Force v1.05", 0 )
GAME( 1996, maxf_102,   maxforce, cojagr3k,     area51,   jaguar_state, init_maxforce,  ROT0, "Atari Games", "Maximum Force v1.02", 0 )
GAME( 1996, maxf_ng,    maxforce, cojagr3k,     area51,   jaguar_state, init_maxforce,  ROT0, "Atari Games", "Maximum Force (No Gore version)", 0 )
GAME( 1998, area51mx,   0,        cojag68k,     area51,   jaguar_state, init_area51mx,  ROT0, "Atari Games", "Area 51 / Maximum Force Duo v2.0", 0 )
GAME( 1998, a51mxr3k,   area51mx, cojagr3k,     area51,   jaguar_state, init_a51mxr3k,  ROT0, "Atari Games", "Area 51 / Maximum Force Duo (R3000, 2/10/98)", 0 )
GAME( 1998, a51mxr3ka,  area51mx, cojagr3k,     area51,   jaguar_state, init_a51mxr3k,  ROT0, "Atari Games", "Area 51 / Maximum Force Duo (R3000, 2/02/98)", 0 )
GAME( 1996, vcircle,    0,        cojagr3k,     vcircle,  jaguar_state, init_vcircle,   ROT0, "Atari Games", "Vicious Circle (prototype)", 0 )



jaminator.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Devin Acker

/***************************************************************************
    Noise Toys Inc. "Jaminator"

    This toy guitar was originally sold by Worlds of Wonder in 1990, along with
    six optional ROM cartridges. In 1993, it was also licensed to Arrow Micro-Techs
    (AMT) and Yamaha, who distributed it along with several new cartridges.

    A message from the development team is used as ROM padding:
    """
    (C)1990 Noise Toys Inc
    Code by Steve Capps(MAD - Je t'aime beaucoup)
    Hardware by Ray DuFlon
    Music by Ed Bogas(Des - te amo)
    """

    Main hardware:
    U101: "DEVO" sound and mapper ASIC
          ("(C)1987 NOISE TOYS INC", "WOW DEVO 33073-01 CF61909N" or "AMT DEVO CF61909N")
    U102: OKI MSM80C39
    U104: 1Mbit mask ROM (DIP28, 23C1000 pinout)

    TODO:
    - Link cable
    - Clickable layout?

***************************************************************************/


#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/rescap.h"
#include "sound/cf61909.h"
#include "sound/flt_biquad.h"
#include "sound/flt_rc.h"

#include "softlist_dev.h"
#include "speaker.h"

namespace {

//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class jaminator_state : public driver_device
{
public:
	jaminator_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_devo(*this, "devo"),
		m_cart(*this, "cart"),
		m_inputs(*this, "COL%u", 1), // labeling from PCB silkscreen
		m_bender(*this, "BENDER"),
		m_led_power(*this, "led_power")
	{ }

	void jaminator(machine_config &config);

	void input_sel_w(u8 data);
	ioport_value input_r();
	ioport_value bender_r();

	// link cable not emulated yet, but output needs to be looped back too (used for starting songs, etc)
	void link_data_w(u8 data) { m_link_data = data; }
	ioport_value link_data_r() { return m_link_data; }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void main_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void sound_map(address_map &map) ATTR_COLD;

	required_device<i8039_device> m_maincpu;
	required_device<cf61909_device> m_devo;
	required_device<generic_slot_device> m_cart;
	required_ioport_array<7> m_inputs;
	required_ioport m_bender;
	output_finder<> m_led_power;

	u8 m_input_sel;
	u8 m_link_data;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void jaminator_state::main_map(address_map &map)
{
	// TODO: program ROM banking for executable cartridges (do any exist?)
	map(0x000, 0x7ff).mirror(0x800).rom().region("devo", 0);
}

void jaminator_state::io_map(address_map &map)
{
	map(0x00, 0xff).rw(m_devo, FUNC(cf61909_device::read), FUNC(cf61909_device::write));
}

void jaminator_state::sound_map(address_map &map)
{
	map(0x00000, 0x1ffff).rom().region("devo", 0);
	map(0x20000, 0x3ffff).nopr(); // cart
}

//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( jaminator )
	PORT_START("COL1")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("String 1")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("String 2")
	PORT_BIT(0x4, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("String 3")
	PORT_BIT(0x8, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Start / Next")

	PORT_START("COL2")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("Key 1")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Key 2")
	PORT_BIT(0x4, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Key 3")
	PORT_BIT(0x8, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Key 4")

	PORT_START("COL3")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Drum Pad 1")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Drum Pad 2")
	PORT_BIT(0x4, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("Drum Pad 3")
	PORT_BIT(0x8, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_NAME("Finale")

	PORT_START("COL4")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_1) PORT_NAME("Fret 1")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_2) PORT_NAME("Fret 2")
	PORT_BIT(0x4, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_3) PORT_NAME("Fret 3")
	PORT_BIT(0x8, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_4) PORT_NAME("Fret 4")

	PORT_START("COL5")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_5) PORT_NAME("Fret 5")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_6) PORT_NAME("Fret 6")
	PORT_BIT(0x4, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_7) PORT_NAME("Fret 7")
	PORT_BIT(0x8, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_8) PORT_NAME("Fret 8")

	PORT_START("COL6")
	PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_9) PORT_NAME("Fret 9")
	PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_0) PORT_NAME("Fret 10")
	PORT_BIT(0x4, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("Fret 11")
	PORT_BIT(0x8, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("Fret 12")

	PORT_START("COL7")
	PORT_BIT(0xf, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(jaminator_state::bender_r))

	PORT_START("BENDER")
	PORT_BIT(0xff, 0x78, IPT_PADDLE) PORT_NAME("Bender Bar") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00, 0xef)

	PORT_START("P1")
	PORT_BIT(0x0f, IP_ACTIVE_LOW,  IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(jaminator_state::input_r))
	PORT_BIT(0x10, IP_ACTIVE_LOW,  IPT_OUTPUT ) // link cable clock
	PORT_BIT(0x20, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(jaminator_state::link_data_w))
	PORT_BIT(0x40, IP_ACTIVE_LOW,  IPT_BUTTON1) PORT_NAME("Select")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(jaminator_state::link_data_r))

	PORT_START("P2")
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT(0xf0, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(jaminator_state::input_sel_w))

	/*
	* T0 is connected to pin 1 on the link port, which is pulled up by a 10k resistor.
	* Connecting it to ground causes percussion tracks to be omitted when playing songs
	*/
	PORT_START("T0")
	PORT_CONFNAME(0x1, 0x1, "Percussion Tracks")
	PORT_CONFSETTING(0x0, DEF_STR( Off ))
	PORT_CONFSETTING(0x1, DEF_STR( On ))
INPUT_PORTS_END


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void jaminator_state::machine_start()
{
	m_led_power.resolve();
	m_input_sel = 0;
	m_link_data = 0;

	if (m_cart->exists())
		m_devo->space().install_read_handler(0x20000, 0x3ffff, read8sm_delegate(*m_cart, FUNC(generic_slot_device::read_rom)));

	save_item(NAME(m_input_sel));
	save_item(NAME(m_link_data));
}

//**************************************************************************
void jaminator_state::machine_reset()
{
	m_led_power = 1;
}

//**************************************************************************
void jaminator_state::input_sel_w(u8 data)
{
	m_input_sel = data & 0xf;

	if (m_input_sel == 0x7)
	{
		m_led_power = 0;
		m_devo->reset();
		m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
	}
}

//**************************************************************************
ioport_value jaminator_state::input_r()
{
	if (m_input_sel < 0x7)
		return m_inputs[m_input_sel]->read();

	return 0;
}

//**************************************************************************
ioport_value jaminator_state::bender_r()
{
	// the bender PCB only has 15 contact positions (0-14), but the ROM recognizes 16 values
	static const u8 bendval[] = {
		0xf, 0x7, 0x3, 0xb, 0x9, 0x1, 0x5, 0xd,
		0xc, 0x4, 0x0, 0x8, 0xa, 0x2, 0x6, 0xe
	};
	return bendval[m_bender->read() >> 4];
}

//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void jaminator_state::jaminator(machine_config &config)
{
	I8039(config, m_maincpu, 11_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &jaminator_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &jaminator_state::io_map);
	m_maincpu->p1_in_cb().set_ioport("P1");
	m_maincpu->p1_out_cb().set_ioport("P1");
	m_maincpu->p2_out_cb().set_ioport("P2");
	m_maincpu->t0_in_cb().set_ioport("T0");

	GENERIC_CARTSLOT(config, m_cart, generic_linear_slot, "jaminator", "bin");
	SOFTWARE_LIST(config, "cart_list").set_original("jaminator");

	SPEAKER(config, "speaker").front_center();

	CF61909(config, m_devo, 11_MHz_XTAL);
	m_devo->set_addrmap(0, &jaminator_state::sound_map);
	m_devo->add_route(0, "rcfilter", 1.0);
	m_maincpu->t1_in_cb().set(m_devo, FUNC(cf61909_device::sync_r));

	filter_rc_device &rcfilter(FILTER_RC(config, "rcfilter"));
	rcfilter.set_lowpass(RES_R(510) + RES_K(15), CAP_N(6.8));
	rcfilter.add_route(0, "biquad", 1.0);

	filter_biquad_device &biquad(FILTER_BIQUAD(config, "biquad"));
	biquad.opamp_sk_lowpass_setup(RES_K(10), RES_K(10), RES_K(39), RES_K(1), CAP_N(6.8), CAP_N(6.8));
	biquad.add_route(0, "speaker", 1.0);
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( jaminator )
	ROM_REGION(0x20000, "devo", 0)
	ROM_LOAD("amta361.u104", 0x00000, 0x20000, CRC(f3f798ed) SHA1(08bef43e9689608f40a57b77724de5f6d2652693))
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY            FULLNAME     FLAGS
SYST( 1990, jaminator, 0,      0,      jaminator, jaminator, jaminator_state, empty_init, "Noise Toys Inc.", "Jaminator", MACHINE_SUPPORTS_SAVE )



jazz.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * An emulation of systems based on the Jazz computer architecture, originally
 * developed by Microsoft. Specific systems which implemented this architecture
 * include the MIPS Magnum/Millenium 4000 and Olivetti M700-10.
 *
 * References:
 *
 *   https://www.linux-mips.org/wiki/Jazz
 *   http://gunkies.org/wiki/MIPS_Magnum
 *   http://www.sensi.org/~alec/mips/mips-history.html
 *
 * TODO
 *   - big-endian support for RISC/os
 *   - EISA bus and slots
 *   - slotify and improve graphics board
 *   - other models/variants
 *   - sound and other loose ends
 *
 * Unconfirmed parts lists from ARCSystem reference design (which appears to
 * be very similar or identical to the Jazz system) taken from:
 *   https://www.linux-mips.org/archives/riscy/1993-12/msg00013.html
 *
 *   Ref   Part                      Function
 *
 * System board:
 *
 *         Dallas DS1287             RTC and NVRAM
 *         Dallas DS1225Y            8k non-volatile SRAM
 *         WD16C552                  Dual serial and parallel port controller
 *         Intel N82077A             Floppy drive controller
 *         National DP83932BFV       Ethernet controller
 *         Intel 82358               EISA Bus Controller
 *         Intel 82357               EISA Integrated System Peripheral (ISP)
 *         Intel 82352 x 2           EISA Bus Buffer (EBB)
 *         Emulex FAS216             SCSI controller (similar to NCR 53CF94-2;
 *                                   older boards have NCR 53C94 instead)
 *         27C01                     128k EPROM
 *         28F020                    256k flash memory
 *         NEC μPD31432              ARC address path ASIC
 *         NEC μPD31431 x 2          ARC data path ASIC
 *         NEC μPD30400              R4000PC/50 CPU
 *
 * Audio board:
 *
 *         Crystal CS4215            Audio codec
 *         Altera FPGA x 4           Audio DMA
 *
 * Video board:
 *
 *         27C010                    128k EPROM
 *         IMS G364-11S              Video controller
 *         NEC μPD42274V-80 x 16     256kx4 VRAM (2MiB)
 */

#include "emu.h"

#include "cpu/mips/r4000.h"

 // memory
#include "machine/ram.h"
#include "machine/nvram.h"
#include "machine/28fxxx.h"

// various hardware
#include "mct_adr.h"
#include "machine/dp83932c.h"
#include "machine/mc146818.h"
#include "machine/ins8250.h"
#include "machine/ncr53c90.h"
#include "machine/upd765.h"
#include "machine/at_keybc.h"
#include "machine/pc_lpt.h"
#include "machine/i82357.h"

// video
#include "screen.h"
#include "video/ims_cvc.h"

// audio
#include "sound/spkrdev.h"
#include "speaker.h"

// busses and connectors
#include "machine/nscsi_bus.h"
#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"
#include "bus/rs232/rs232.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "bus/pc_kbd/keyboards.h"

#include "imagedev/floppy.h"
#include "softlist.h"

#include "jazz.lh"

#define VERBOSE 0
#include "logmacro.h"

namespace {

class jazz_state : public driver_device
{
public:
	jazz_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_ram(*this, "ram")
		, m_vram(*this, "vram")
		, m_mct_adr(*this, "mct_adr")
		, m_scsibus(*this, "scsi")
		, m_scsi(*this, "scsi:7:ncr53cf94")
		, m_fdc(*this, "fdc")
		, m_rtc(*this, "rtc")
		, m_nvram(*this, "nvram")
		, m_flash(*this, "flash")
		, m_kbdc(*this, "kbdc")
		, m_net(*this, "net")
		, m_screen(*this, "screen")
		, m_cvc(*this, "g364")
		, m_ace(*this, "ace%u", 0)
		, m_lpt(*this, "lpt")
		, m_isp(*this, "isp")
		, m_buzzer(*this, "buzzer")
		, m_softlist(*this, "softlist")
		, m_led(*this, "led0")
	{
	}

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// address maps
	void cpu_map(address_map &map) ATTR_COLD;
	void mct_map(address_map &map) ATTR_COLD;

	// machine config
	void jazz(machine_config &config);

	void led_w(u8 data);

public:
	void mmr4000be(machine_config &config);
	void mmr4000le(machine_config &config);

	void init_common();

protected:
	// devices
	required_device<r4000_device> m_cpu;
	required_device<ram_device> m_ram;
	required_device<ram_device> m_vram;
	required_device<mct_adr_device> m_mct_adr;
	required_device<nscsi_bus_device> m_scsibus;
	required_device<ncr53cf94_device> m_scsi;
	required_device<n82077aa_device> m_fdc;
	required_device<mc146818_device> m_rtc;
	required_device<nvram_device> m_nvram;
	required_device<amd_28f020_device> m_flash;
	required_device<ps2_keyboard_controller_device> m_kbdc;
	required_device<dp83932c_device> m_net;
	required_device<screen_device> m_screen;
	required_device<g364_device> m_cvc;
	required_device_array<ns16550_device, 2> m_ace;
	required_device<pc_lpt_device> m_lpt;
	required_device<i82357_device> m_isp;
	required_device<speaker_sound_device> m_buzzer;
	required_device<software_list_device> m_softlist;

	output_finder<> m_led;
};

void jazz_state::machine_start()
{
	m_led.resolve();
}

void jazz_state::machine_reset()
{
	// HACK: make sure the RTC is running
	m_rtc->write_direct(0x0a, 0x20);
}

void jazz_state::init_common()
{
	// map the configured ram and vram
	m_mct_adr->space(0).install_ram(0x00000000, 0x00000000 | m_ram->mask(), m_ram->pointer());
	m_mct_adr->space(0).install_ram(0x40000000, 0x40000000 | m_vram->mask(), m_vram->pointer());
}

void jazz_state::cpu_map(address_map &map)
{
	map(0x00000000, 0xffffffff).rw(m_mct_adr, FUNC(mct_adr_device::r4k_r), FUNC(mct_adr_device::r4k_w));
}

void jazz_state::mct_map(address_map &map)
{
	map(0x1fc00000, 0x1fc3ffff).r(m_flash, FUNC(amd_28f020_device::read));

	// VDR1
	//map(0x60000000, 0x60001fff).m(m_cvc, FUNC(g364_device::map));
	//map(0x60010000, 0x60010000); // id (r/w)
	//map(0x60020000, 0x60020000); // reset

	// VDR1F "Fission"
	map(0x60000000, 0x6007ffff).rom().region("graphics", 0);
	map(0x60080000, 0x60081fff).m(m_cvc, FUNC(g364_device::map));
	map(0x60100000, 0x60100000).lw8([this] (u8 data) { m_cvc->set_swapped(ENDIANNESS_NATIVE == ENDIANNESS_BIG); }, "little_endian");
	map(0x60102000, 0x60102000).lw8([this] (u8 data) { m_cvc->set_swapped(ENDIANNESS_NATIVE == ENDIANNESS_LITTLE); }, "big_endian");
	map(0x60180000, 0x60180000).lw8([this] (u8 data) { m_cvc->reset(); }, "g364_reset");
	//map(0x60182000, 0x60182000); // TODO: monitor

	// VDR2
	//map(0x60000000, 0x60000fff).m(m_cvc, FUNC(g300_device::map));
	//map(0x60008000, 0x60008fff).m(m_cursor, FUNC(bt431_device::map)).umask32(0x000000ff);

	map(0x80000000, 0x80000fff).m(m_mct_adr, FUNC(mct_adr_device::map));

	map(0x80001000, 0x800010ff).m(m_net, FUNC(dp83932c_device::map)).umask32(0x0000ffff);
	map(0x80002000, 0x8000200f).m(m_scsi, FUNC(ncr53cf94_device::map));
	map(0x80003000, 0x8000300f).m(m_fdc, FUNC(n82077aa_device::map));

	// LE: only reads 4000
	// BE: read 400d, write 400d, write 400c
	map(0x80004000, 0x8000400f).lrw8(
			NAME([this] (offs_t offset) { return m_rtc->data_r(); }),
			NAME([this] (offs_t offset, u8 data) { m_rtc->data_w(data); }));
	map(0x80005000, 0x80005000).rw(m_kbdc, FUNC(ps2_keyboard_controller_device::data_r), FUNC(ps2_keyboard_controller_device::data_w));
	map(0x80005001, 0x80005001).rw(m_kbdc, FUNC(ps2_keyboard_controller_device::status_r), FUNC(ps2_keyboard_controller_device::command_w));
	map(0x80006000, 0x80006007).rw(m_ace[0], FUNC(ns16550_device::ins8250_r), FUNC(ns16550_device::ins8250_w));
	map(0x80007000, 0x80007007).rw(m_ace[1], FUNC(ns16550_device::ins8250_r), FUNC(ns16550_device::ins8250_w));
	map(0x80008000, 0x80008003).rw(m_lpt, FUNC(pc_lpt_device::read), FUNC(pc_lpt_device::write));
	map(0x80009000, 0x8000afff).ram().share("nvram");
	map(0x8000b000, 0x8000b007).lr8(
			[] (offs_t offset)
			{
				// mac address and checksum
				static u8 const mac[] = { 0x00, 0x00, 0x6b, 0x12, 0x34, 0x56, 0x00, 0xf7 };

				return mac[offset];
			},
			"mac");

	//map(0x8000c000, 0x8000cfff) // sound
	//map(0x8000d000, 0x8000dfff).noprw(); // dummy dma device?
	map(0x8000d600, 0x8000d607).nopw();

	map(0x8000f000, 0x8000f000).w(FUNC(jazz_state::led_w));

	// lots of byte data written to 800
	//map(0x800e0000, 0x800fffff).m() // dram config

	map(0x90000000, 0x90ffffff).m(m_isp, FUNC(i82357_device::map));

	// HACK: empty eisa slots
	map(0x90001c80, 0x90001c87).ram();
	map(0x90002c80, 0x90002c87).ram();
	map(0x90003c80, 0x90003c87).ram();
	map(0x90004c80, 0x90004c87).ram();

	//map(0x91000000, 0x91ffffff).m();
	//map(0x92000000, 0x92ffffff).m(); // EISA I/O ports?
	//map(0x93000000, 0x93ffffff).m(); // EISA memory

	map(0xf0000000, 0xf0000001).r(m_mct_adr, FUNC(mct_adr_device::isr_r));
	map(0xf0000002, 0xf0000003).rw(m_mct_adr, FUNC(mct_adr_device::imr_r), FUNC(mct_adr_device::imr_w));

	map(0xfff00000, 0xfff3ffff).r(m_flash, FUNC(amd_28f020_device::read)); // mirror?
}

static void jazz_scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
	device.option_add("cdrom", NSCSI_CDROM);
}

void jazz_state::jazz(machine_config &config)
{
	R4000(config, m_cpu, 50_MHz_XTAL);
	m_cpu->set_addrmap(0, &jazz_state::cpu_map);

	RAM(config, m_ram);
	m_ram->set_default_size("16M");
	m_ram->set_extra_options("32M,64M,128M,256M");
	m_ram->set_default_value(0);

	RAM(config, m_vram);
	m_vram->set_default_size("2M");
	m_vram->set_default_value(0);

	// local bus dma, timer and interrupt controller
	MCT_ADR(config, m_mct_adr, 0);
	m_mct_adr->set_addrmap(0, &jazz_state::mct_map);
	m_mct_adr->out_int_dma_cb().set_inputline(m_cpu, INPUT_LINE_IRQ0);
	m_mct_adr->out_int_device_cb().set_inputline(m_cpu, INPUT_LINE_IRQ1);
	m_mct_adr->out_int_timer_cb().set_inputline(m_cpu, INPUT_LINE_IRQ4);
	m_mct_adr->eisa_iack_cb().set(m_isp, FUNC(i82357_device::eisa_irq_ack));

	// scsi bus and devices
	NSCSI_BUS(config, m_scsibus);
	NSCSI_CONNECTOR(config, "scsi:0", jazz_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:1", jazz_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", jazz_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", jazz_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", jazz_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", jazz_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", jazz_scsi_devices, "cdrom");

	// scsi host adapter
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr53cf94", NCR53CF94).clock(40000000).machine_config(
		[this] (device_t *device)
		{
			ncr53cf94_device &adapter = downcast<ncr53cf94_device &>(*device);

			adapter.irq_handler_cb().set(m_mct_adr, FUNC(mct_adr_device::irq<5>));
			adapter.drq_handler_cb().set(m_mct_adr, FUNC(mct_adr_device::drq<0>));

			subdevice<mct_adr_device>(":mct_adr")->dma_r_cb<0>().set(adapter, FUNC(ncr53cf94_device::dma_r));
			subdevice<mct_adr_device>(":mct_adr")->dma_w_cb<0>().set(adapter, FUNC(ncr53cf94_device::dma_w));
		});

	// floppy controller and drive
	N82077AA(config, m_fdc, 24_MHz_XTAL);
	m_fdc->intrq_wr_callback().set(m_mct_adr, FUNC(mct_adr_device::irq<1>));
	m_fdc->drq_wr_callback().set(m_mct_adr, FUNC(mct_adr_device::drq<1>));
	FLOPPY_CONNECTOR(config, "fdc:0", "35hd", FLOPPY_35_HD, true, floppy_image_device::default_pc_floppy_formats).enable_sound(false);
	m_mct_adr->dma_r_cb<1>().set(m_fdc, FUNC(n82077aa_device::dma_r));
	m_mct_adr->dma_w_cb<1>().set(m_fdc, FUNC(n82077aa_device::dma_w));

	DS1287(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->set_binary(true);
	m_rtc->set_epoch(1980);

	NVRAM(config, m_nvram, nvram_device::DEFAULT_ALL_0);

	AMD_28F020(config, m_flash);

	// pc keyboard connector
	pc_kbdc_device &kbd_con(PC_KBDC(config, "kbd", pc_at_keyboards, STR_KBD_MICROSOFT_NATURAL));
	kbd_con.out_clock_cb().set(m_kbdc, FUNC(ps2_keyboard_controller_device::kbd_clk_w));
	kbd_con.out_data_cb().set(m_kbdc, FUNC(ps2_keyboard_controller_device::kbd_data_w));

	// auxiliary connector
	pc_kbdc_device &aux_con(PC_KBDC(config, "aux", ps2_mice, STR_HLE_PS2_MOUSE));
	aux_con.out_clock_cb().set(m_kbdc, FUNC(ps2_keyboard_controller_device::aux_clk_w));
	aux_con.out_data_cb().set(m_kbdc, FUNC(ps2_keyboard_controller_device::aux_data_w));

	// keyboard controller
	PS2_KEYBOARD_CONTROLLER(config, m_kbdc, 12_MHz_XTAL);
	// FIXME: reset is probably routed through the MCT-ADR
	m_kbdc->hot_res().set([this] (int state) { machine().schedule_soft_reset(); });
	m_kbdc->kbd_clk().set(kbd_con, FUNC(pc_kbdc_device::clock_write_from_mb));
	m_kbdc->kbd_data().set(kbd_con, FUNC(pc_kbdc_device::data_write_from_mb));
	m_kbdc->kbd_irq().set(m_mct_adr, FUNC(mct_adr_device::irq<6>));
	m_kbdc->aux_clk().set(aux_con, FUNC(pc_kbdc_device::clock_write_from_mb));
	m_kbdc->aux_data().set(aux_con, FUNC(pc_kbdc_device::data_write_from_mb));
	m_kbdc->aux_irq().set(m_mct_adr, FUNC(mct_adr_device::irq<7>));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(78643200, 1280, 0, 1280, 1024, 0, 1024);
	m_screen->set_screen_update(m_cvc, FUNC(g364_device::screen_update));
	m_screen->screen_vblank().set(m_mct_adr, FUNC(mct_adr_device::irq<3>)); // maybe?

	G364(config, m_cvc, 5_MHz_XTAL); // FIXME: guessed clock
	m_cvc->set_screen(m_screen);
	m_cvc->set_vram(m_vram);

	// WD16C552 (two 16550 + pc_lpt)
	NS16550(config, m_ace[0], 4233600);
	rs232_port_device &serial0(RS232_PORT(config, "serial0", default_rs232_devices, nullptr));

	m_ace[0]->out_dtr_callback().set(serial0, FUNC(rs232_port_device::write_dtr));
	m_ace[0]->out_rts_callback().set(serial0, FUNC(rs232_port_device::write_rts));
	m_ace[0]->out_tx_callback().set(serial0, FUNC(rs232_port_device::write_txd));
	m_ace[0]->out_int_callback().set(m_mct_adr, FUNC(mct_adr_device::irq<8>));

	serial0.cts_handler().set(m_ace[0], FUNC(ns16550_device::cts_w));
	serial0.dcd_handler().set(m_ace[0], FUNC(ns16550_device::dcd_w));
	serial0.dsr_handler().set(m_ace[0], FUNC(ns16550_device::dsr_w));
	serial0.ri_handler().set(m_ace[0], FUNC(ns16550_device::ri_w));
	serial0.rxd_handler().set(m_ace[0], FUNC(ns16550_device::rx_w));

	NS16550(config, m_ace[1], 8_MHz_XTAL);
	rs232_port_device &serial1(RS232_PORT(config, "serial1", default_rs232_devices, nullptr));

	m_ace[1]->out_dtr_callback().set(serial1, FUNC(rs232_port_device::write_dtr));
	m_ace[1]->out_rts_callback().set(serial1, FUNC(rs232_port_device::write_rts));
	m_ace[1]->out_tx_callback().set(serial1, FUNC(rs232_port_device::write_txd));
	m_ace[1]->out_int_callback().set(m_mct_adr, FUNC(mct_adr_device::irq<9>));

	serial1.cts_handler().set(m_ace[1], FUNC(ns16550_device::cts_w));
	serial1.dcd_handler().set(m_ace[1], FUNC(ns16550_device::dcd_w));
	serial1.dsr_handler().set(m_ace[1], FUNC(ns16550_device::dsr_w));
	serial1.ri_handler().set(m_ace[1], FUNC(ns16550_device::ri_w));
	serial1.rxd_handler().set(m_ace[1], FUNC(ns16550_device::rx_w));

	PC_LPT(config, m_lpt, 0);
	m_lpt->irq_handler().set(m_mct_adr, FUNC(mct_adr_device::irq<0>));

	// TODO: sound, interrupt 2, drq 2(l) & 3(r)

	// buzzer
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_buzzer);
	m_buzzer->add_route(ALL_OUTPUTS, "mono", 0.50);

	DP83932C(config, m_net, 20_MHz_XTAL);
	m_net->out_int_cb().set(m_mct_adr, FUNC(mct_adr_device::irq<4>));
	m_net->set_bus(m_mct_adr, 1);

	I82357(config, m_isp, 14.318181_MHz_XTAL);
	m_isp->out_rtc_address_cb().set(m_rtc, FUNC(mc146818_device::address_w));
	m_isp->out_int_cb().set_inputline(m_cpu, INPUT_LINE_IRQ2);
	m_isp->out_nmi_cb().set_inputline(m_cpu, INPUT_LINE_IRQ3);
	m_isp->out_spkr_cb().set(m_buzzer, FUNC(speaker_sound_device::level_w));

	// TODO: 4 EISA slots

	config.set_default_layout(layout_jazz);

	// software list
	SOFTWARE_LIST(config, m_softlist).set_original("jazz");
}

void jazz_state::led_w(u8 data)
{
	// 7-segment diagnostic led
	static u8 const patterns[16] =
	{
			  // test      output
		0x3f, // mct-adr     0
		0x06, // network     1
		0x5b, // scsi        2
		0x4f, // floppy      3
		0x66, // isp/rtc     4
		0x6d, // keyboard    5
		0x7d, // serial      6
		0x07, // nvram       7
		0x7f, //             8?
		0x6f, // video       9
		0x77, // memory      A
		0x40, //             -
		0x39, // flash       C
		0x00, //           (blank)
		0x79, // cpu         E
		0x71, //             F?
	};

	m_led = patterns[data & 0xf] | (BIT(data, 4) ? 0x80 : 0);
}

void jazz_state::mmr4000be(machine_config &config)
{
	jazz(config);

	m_cpu->set_config(r4000_device::CONFIG_BE, r4000_device::CONFIG_BE);
}

void jazz_state::mmr4000le(machine_config &config)
{
	jazz(config);

	m_cpu->set_config(0, r4000_device::CONFIG_BE);
}

ROM_START(mmr4000be)
	ROM_REGION32_LE(0x40000, "flash", 0)
	ROM_SYSTEM_BIOS(0, "riscos", "R4000 RISC/os PROM")
	ROMX_LOAD("riscos.bin", 0x00000, 0x40000, CRC(cea6bc8f) SHA1(3e47b4ad5d1a0c7aac649e6aef3df1bf86fc938b), ROM_BIOS(0))

	ROM_REGION32_LE(0x800000, "graphics", 0)
	ROM_LOAD64_BYTE("mips_g364.bin", 0x000000, 0x020000, CRC(be6a726e) SHA1(225c198f6a7f8445dac3de052ecceecbb5be6bc7) BAD_DUMP)
ROM_END

ROM_START(mmr4000le)
	ROM_REGION32_LE(0x40000, "flash", 0)
	ROM_SYSTEM_BIOS(0, "ntprom", "R4000 Windows NT PROM")
	ROMX_LOAD("ntprom.bin", 0x00000, 0x40000, CRC(d91018d7) SHA1(316de17820192c89b8ee6d9936ab8364a739ca53), ROM_BIOS(0))

	ROM_REGION32_LE(0x800000, "graphics", 0)
	// Jazz G300 (8.125MHz video clock, Bt431)
	//ROM_LOAD64_BYTE("jazz_g300.bin", 0x00, 0x40, CRC(258eb00a) SHA1(6e3fd0272957524de82e7042d6e36aca492c4d26) BAD_DUMP)
	// Jazz G364 (8.125MHz video clock)
	//ROM_LOAD64_BYTE("jazz_g364.bin", 0x000000, 0x020000, CRC(495fb417) SHA1(c341f3d498822ec1ee07a70076d7bbbf7aa60cb5) BAD_DUMP)
	// Jazz VXL (aka Jaguar, part number 09-00184, Bt484 or Bt485)
	//ROM_LOAD64_BYTE("jazz_vxl.bin", 0x000000, 0x010000, CRC(8edf1a62) SHA1(7750833eac0708ee79f01f36523554d29a094692) BAD_DUMP)
	// MIPS G364 (5MHz video clock, part number 09-00176)
	ROM_LOAD64_BYTE("mips_g364.bin", 0x000000, 0x020000, CRC(be6a726e) SHA1(225c198f6a7f8445dac3de052ecceecbb5be6bc7) BAD_DUMP)
ROM_END

}

/*   YEAR   NAME       PARENT  COMPAT  MACHINE    INPUT  CLASS       INIT         COMPANY  FULLNAME             FLAGS */
COMP(1992,  mmr4000be, 0,      0,      mmr4000be, 0,     jazz_state, init_common, "MIPS",  "Magnum R4000 (be)", MACHINE_NO_SOUND)
COMP(1992,  mmr4000le, 0,      0,      mmr4000le, 0,     jazz_state, init_common, "MIPS",  "Magnum R4000 (le)", MACHINE_NO_SOUND)



jensen.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * An emulation of the DECpc AXP/150 (code named Jensen), nearly identical
 * DEC 2000 Model 300 AXP, and DEC 2000 Model 500 AXP (code named Culzean).
 *
 * References:
 *
 *   ftp://ftp.netbsd.org/pub/NetBSD/misc/dec-docs/ek-a0638-td.pdf.gz
 *   http://www.decuslib.com/decus/vlt97a/everhart/jensen.info
 *
 * TODO
 *   - everything (skeleton only)
 *
 */
/*
 * WIP notes
 *
 * flash contains two compressed (deflate) blocks:
 *
 *   block 0 offset 0x71c00 compressed 0x031737 decompressed 0x72800
 *   block 1 offset 0xd2008 compressed 0x01c31b decompressed 0x47a00
 */
#include "emu.h"

#include "cpu/alpha/alpha.h"
#include "cpu/alpha/alphad.h"

 // memory
#include "machine/ram.h"
#include "machine/xc1700e.h"
#include "machine/intelfsh.h"

// various hardware
#include "machine/i82357.h"

// busses and connectors
#include "bus/rs232/rs232.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "bus/pc_kbd/keyboards.h"

#include "debugger.h"

#define VERBOSE 0
#include "logmacro.h"

namespace {

class jensen_state : public driver_device
{
public:
	jensen_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_ram(*this, "ram")
		, m_srom(*this, "srom")
		, m_feprom(*this, "feprom%u", 0)
		, m_isp(*this, "isp")
	{
	}

	// machine config
	void jensen(machine_config &config);

	void d2k300axp(machine_config &config);
	void d2k500axp(machine_config &config);
	void dpcaxp150(machine_config &config);

	void init_common();

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// address maps
	void local_memory(address_map &map) ATTR_COLD;
	void local_io(address_map &map) ATTR_COLD;
	void eisa_memory(address_map &map) ATTR_COLD;
	void eisa_io(address_map &map) ATTR_COLD;

private:
	// devices
	required_device<alpha_device> m_cpu;
	required_device<ram_device> m_ram;
	required_device<xc1765e_device> m_srom;
	required_device_array<intel_e28f008sa_device, 2> m_feprom;

	required_device<i82357_device> m_isp;

	// machine state
	u8 m_hae = 0;
	u8 m_sysctl = 0;
	u8 m_spare = 0;
};

void jensen_state::machine_start()
{
}

void jensen_state::machine_reset()
{
}

void jensen_state::init_common()
{
	// map the configured ram
	m_cpu->space(0).install_ram(0x00000000, 0x00000000 | m_ram->mask(), m_ram->pointer());
}

void jensen_state::local_memory(address_map &map)
{
	//map(0x00000000, 0x07ffffff); // valid range
}

void jensen_state::local_io(address_map &map)
{
	map(0x00000000, 0x0000001f); // EISA INTA cycle
	map(0x80000000, 0x9fffffff).lr8(NAME([this] (offs_t offset) { return m_feprom[0]->read(offset >> 9); }));
	map(0xa0000000, 0xbfffffff).lr8(NAME([this] (offs_t offset) { return m_feprom[1]->read(offset >> 9); }));

	//map(0xc0000000, 0xc1ffffff); // vl82c106 bits 24:9 -> vlsi 15:0 i.e. >> 9

	map(0xd0000000, 0xd0000000).lrw8(NAME([this] () { return m_hae; }), NAME([this] (u8 data) { m_hae = data & 0x7f; }));
	map(0xe0000000, 0xe0000000).lrw8(NAME([this] () { return m_sysctl; }), NAME([this] (u8 data) { m_sysctl = data; logerror("led %x\n", data & 0xf); }));
	map(0xf0000000, 0xf0000000).lrw8(NAME([this] () { return m_spare; }), NAME([this] (u8 data) { m_spare = data; }));
}

void jensen_state::eisa_memory(address_map &map)
{
	map(0x00000000, 0xffffffff).lrw8(
			NAME([this] (offs_t offset)
			{
				LOG("eisa_memory_r 0x%08x\n", (u32(m_hae) << 25) | (offset >> 7));

				return 0;
			}),
			NAME([this] (offs_t offset, u8 data)
			{
				LOG("eisa_memory_w 0x%08x data 0x%02x\n", (u32(m_hae) << 25) | (offset >> 7), data);
			}));
}

void jensen_state::eisa_io(address_map &map)
{
	map(0x00000000, 0xffffffff).lrw8(
			NAME([this] (offs_t offset)
			{
				LOG("eisa_io_r offset 0x%08x address 0x%08x count %d (%s)\n", offset,
					(u32(m_hae) << 25) | (offset >> 7), (offset >> 5) & 3, machine().describe_context());

				return 0;
			}),
			NAME([this] (offs_t offset, u8 data)
			{
				LOG("eisa_io_w offset 0x%08x address 0x%08x count %d data 0x%02x (%s)\n",
					offset, (u32(m_hae) << 25) | (offset >> 7), (offset >> 5) & 3, data, machine().describe_context());
			}));
}

void jensen_state::jensen(machine_config &config)
{
	DEC_21064(config, m_cpu, 300'000'000).set_clock_scale(0.5);
	m_cpu->set_dasm_type(alpha_disassembler::dasm_type::TYPE_NT);
	m_cpu->set_addrmap(0, &jensen_state::local_memory);
	m_cpu->set_addrmap(2, &jensen_state::local_io);
	m_cpu->set_addrmap(4, &jensen_state::eisa_memory);
	m_cpu->set_addrmap(6, &jensen_state::eisa_io);
	m_cpu->srom_oe_w().set(m_srom, FUNC(xc1765e_device::reset_w));
	m_cpu->srom_data_r().set(m_srom, FUNC(xc1765e_device::data_r));

	RAM(config, m_ram);
	m_ram->set_default_size("16M");
	m_ram->set_extra_options("32M,64M,80M,128M");
	m_ram->set_default_value(0);

	XC1765E(config, m_srom);

	// TODO: 1x1M + 1x256K flash?
	INTEL_E28F008SA(config, m_feprom[0]);
	INTEL_E28F008SA(config, m_feprom[1]);

	// keyboard connector
	[[maybe_unused]] pc_kbdc_device &kbd_con(PC_KBDC(config, "kbd", pc_at_keyboards, STR_KBD_MICROSOFT_NATURAL));
	//kbd_con.out_clock_cb().set(m_kbdc, FUNC(ps2_keyboard_controller_device::kbd_clk_w));
	//kbd_con.out_data_cb().set(m_kbdc, FUNC(ps2_keyboard_controller_device::kbd_data_w));

	// TODO: VL82C106 (rtc, dual serial, parallel, dual ps/2)
	// TODO: 18.432 MHz crystal
#if 0
	rs232_port_device &com1(RS232_PORT(config, "com1", default_rs232_devices, nullptr));
	m_ace[0]->out_dtr_callback().set(serial0, FUNC(rs232_port_device::write_dtr));
	m_ace[0]->out_rts_callback().set(serial0, FUNC(rs232_port_device::write_rts));
	m_ace[0]->out_tx_callback().set(serial0, FUNC(rs232_port_device::write_txd));
	m_ace[0]->out_int_callback().set(m_mct_adr, FUNC(jazz_mct_adr_device::irq<8>));

	serial0.cts_handler().set(m_ace[0], FUNC(ns16550_device::cts_w));
	serial0.dcd_handler().set(m_ace[0], FUNC(ns16550_device::dcd_w));
	serial0.dsr_handler().set(m_ace[0], FUNC(ns16550_device::dsr_w));
	serial0.ri_handler().set(m_ace[0], FUNC(ns16550_device::ri_w));
	serial0.rxd_handler().set(m_ace[0], FUNC(ns16550_device::rx_w));

	rs232_port_device &com2(RS232_PORT(config, "com2", default_rs232_devices, nullptr));
	m_ace[1]->out_dtr_callback().set(serial1, FUNC(rs232_port_device::write_dtr));
	m_ace[1]->out_rts_callback().set(serial1, FUNC(rs232_port_device::write_rts));
	m_ace[1]->out_tx_callback().set(serial1, FUNC(rs232_port_device::write_txd));
	m_ace[1]->out_int_callback().set(m_mct_adr, FUNC(jazz_mct_adr_device::irq<9>));

	serial1.cts_handler().set(m_ace[1], FUNC(ns16550_device::cts_w));
	serial1.dcd_handler().set(m_ace[1], FUNC(ns16550_device::dcd_w));
	serial1.dsr_handler().set(m_ace[1], FUNC(ns16550_device::dsr_w));
	serial1.ri_handler().set(m_ace[1], FUNC(ns16550_device::ri_w));
	serial1.rxd_handler().set(m_ace[1], FUNC(ns16550_device::rx_w));

	PC_LPT(config, m_lpt, 0);
	m_lpt->irq_handler().set(m_mct_adr, FUNC(jazz_mct_adr_device::irq<0>));
#endif

	I82357(config, m_isp, 14.318181_MHz_XTAL);
	m_isp->out_int_cb().set_inputline(m_cpu, INPUT_LINE_IRQ2);
	m_isp->out_nmi_cb().set_inputline(m_cpu, INPUT_LINE_IRQ3);

	// TODO: six EISA slots

	// DECpc AXP 150 usually fitted with:
	// 30-40383-01 Adaptec AHA-1742A (SCSI + floppy)
	// 30-40385-01 Compaq QVision 1024/E (1MB VGA)

	// Optional
	// DE422-SA Digital EISA Ethernet Controller (10 Mbps TP and BNC connectors)
}

void jensen_state::d2k300axp(machine_config &config)
{
	jensen(config);
}

void jensen_state::d2k500axp(machine_config &config)
{
	jensen(config);
}

void jensen_state::dpcaxp150(machine_config &config)
{
	jensen(config);
}

ROM_START(d2k300axp)
	ROM_REGION32_LE(0x2000, "srom", 0)
	ROM_LOAD("srom.bin", 0x00000, 0x2000, NO_DUMP)

	ROM_REGION32_LE(0x100000, "feprom1", 0)
	ROM_SYSTEM_BIOS(0, "feprom", "Unknown version")
	ROMX_LOAD("feprom1.bin", 0x00000, 0x100000, NO_DUMP, ROM_BIOS(0))
ROM_END

ROM_START(d2k500axp)
	ROM_REGION32_LE(0x2000, "srom", 0)
	ROM_LOAD("srom.bin", 0x00000, 0x2000, NO_DUMP)

	ROM_REGION32_LE(0x100000, "feprom1", 0)
	ROM_SYSTEM_BIOS(0, "feprom", "Unknown version")
	ROMX_LOAD("feprom1.bin", 0x00000, 0x100000, NO_DUMP, ROM_BIOS(0))
ROM_END

ROM_START(dpcaxp150)
	/*
	 * This region corresponds to the 8KiB Xilinx XC1765 serial prom which is
	 * loaded into the Alpha primary instruction cache at reset. This dump is
	 * stored least-significant bit first (which suits the way it's loaded by
	 * the Alpha, and was the default treatment by the dumping device).
	 */
	ROM_REGION32_LE(0x2000, "srom", 0)
	ROM_LOAD("005v2__ax06.bin", 0x00000, 0x2000, CRC(d017ea39) SHA1(4d2d11ac94adebe797e749f6ef1e1e523bd1e9f3))

	/*
	 * This region corresponds to FEPROM1, the 1MiB Intel E28F008SA flash
	 * memory. Documentation indicates FEPROM0 may be either 256KiB or 1MiB,
	 * and may map to the unpopulated nearby PLCC socket. Some revisions of the
	 * main board and the documentation indicate a SIMM of some kind rather
	 * than directly mounted memory devices, but presumably have equivalent
	 * sizes and content.
	 */
	ROM_REGION32_LE(0x100000, "feprom1", 0)

	// source: extracted from ftp://ftp.hp.com/pub/alphaserver/firmware/retired_platforms/alphapc/dec2000_axp150/dec2000_v2_2.exe
	ROM_SYSTEM_BIOS(0, "v22", "Version 2.2, 12-FEB-1996")
	ROMX_LOAD("001z5__bl07.v22", 0x00000, 0x100000, CRC(1edb9c98) SHA1(a45f0dde236e189a57afc1ed354201180ab2f234), ROM_BIOS(0))

	// source: extracted from https://archive.org/download/ntrisc/alpha/ftp.alphant.com/Drivers/fw150v431.zip
	ROM_SYSTEM_BIOS(1, "v19", "Version 1.9, 22-JUN-1995")
	ROMX_LOAD("001z5__bl07.v19", 0x00000, 0x100000, CRC(53e981ee) SHA1(c726981441af88d8a224b1f81efee3a6ec95f227), ROM_BIOS(1))

	// source: extracted from https://archive.org/download/decpcaxp/DEC%20PC%20AXP%20Firmware%20Upgrade%20No9%20GXE.img
	ROM_SYSTEM_BIOS(2, "v13", "Version 1.3, 10-JUN-1994")
	ROMX_LOAD("001z5__bl07.v13", 0x00000, 0x100000, CRC(8035f370) SHA1(2ebd75267ab7373d344efe44e700645ed31b44cd), ROM_BIOS(2))

	// source: extracted from https://archive.org/download/ntrisc/alpha/ftp.alphant.com/Drivers/fw35-1.zip
	ROM_SYSTEM_BIOS(3, "vff", "Version f.f, 19-MAY-1994")
	ROMX_LOAD("001z5__bl07.vff", 0x00000, 0x100000, CRC(99238153) SHA1(ce543bd11937bdf24c4d9e898e917438c2163a20), ROM_BIOS(3))
ROM_END

}

/*    YEAR   NAME       PARENT  COMPAT  MACHINE    INPUT  CLASS         INIT         COMPANY  FULLNAME                  FLAGS */
COMP( 1993,  d2k300axp, 0,      0,      d2k300axp, 0,     jensen_state, init_common, "Digital Equipment Corporation",   "DEC 2000 Model 300 AXP", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP( 1993,  d2k500axp, 0,      0,      d2k500axp, 0,     jensen_state, init_common, "Digital Equipment Corporation",   "DEC 2000 Model 500 AXP", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP( 1993,  dpcaxp150, 0,      0,      dpcaxp150, 0,     jensen_state, init_common, "Digital Equipment Corporation",   "DECpc AXP 150",          MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



jonos.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Jonos Escort

2013-09-12 Skeleton driver

It seems there were about 6 models of Escort, mostly Z-80A based running
CP/M. However, this one appears to be an 8085-based terminal.

Haven't found any info.

There are interrupt handlers at 5.5 (0x002c) and 6.5 (0x0034).


****************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/keyboard.h"
#include "emupal.h"
#include "screen.h"


namespace {

class jonos_state : public driver_device
{
public:
	jonos_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_p_videoram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
	{ }

	void jonos(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	u8 keyboard_r(offs_t offset);
	void cursor_w(offs_t offset, u8 data);
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void kbd_put(u8 data);

	void mem_map(address_map &map) ATTR_COLD;

	u8 m_framecnt = 0U;
	u8 m_term_data = 0U;
	u8 m_curs_ctrl = 0U;
	u16 m_curs_pos = 0U;
	required_device<cpu_device> m_maincpu;
	required_shared_ptr<u8> m_p_videoram;
	required_region_ptr<u8> m_p_chargen;
};



void jonos_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).rom().region("roms", 0);
	map(0x1800, 0x27ff).ram().share("videoram");
	map(0x3000, 0x3001).w(FUNC(jonos_state::cursor_w)); // unknown device
	map(0x4000, 0x4001); // unknown device
	map(0x5000, 0x5003).r(FUNC(jonos_state::keyboard_r)); // unknown device
	map(0x6000, 0x6001); // unknown device
}

/* Input ports */
static INPUT_PORTS_START( jonos )
INPUT_PORTS_END

void jonos_state::kbd_put(u8 data)
{
	m_term_data = data;
}

u8 jonos_state::keyboard_r(offs_t offset)
{
	if (offset == 0)
	{
		u8 data = m_term_data;
		m_term_data = 0;
		return data;
	}
	else
	if (m_term_data && offset == 2)
		return 0x20;

	return 0;
}

void jonos_state::cursor_w(offs_t offset, u8 data)
{
	if (offset == 1) // control byte
		m_curs_ctrl = (data == 0x80) ? 1 : 0;
	else
	if (m_curs_ctrl == 1)
	{
		m_curs_pos = (m_curs_pos & 0xff00) | data;
		m_curs_ctrl++;
	}
	else
	if (m_curs_ctrl == 2)
		m_curs_pos = (m_curs_pos & 0xff) | (data << 8);
}

void jonos_state::machine_start()
{
	save_item(NAME(m_term_data));
	save_item(NAME(m_curs_ctrl));
	save_item(NAME(m_curs_pos));
	save_item(NAME(m_framecnt));
}

void jonos_state::machine_reset()
{
	m_curs_ctrl = 0;
	m_curs_pos = 0;
	m_term_data = 0;
	m_framecnt = 0;
}

uint32_t jonos_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	m_framecnt++;
	u16 sy=0;
	u16 ma = (m_p_videoram[0x7da] + (m_p_videoram[0x7db] << 8)) & 0x7ff;

	for (u8 y = 0; y < 24; y++)
	{
		for (u8 ra = 0; ra < 12; ra++)
		{
			u16 *p = &bitmap.pix(sy++);
			u16 cpos = y << 8;

			for (u16 x = ma; x < ma + 80; x++)
			{
				u8 chr = m_p_videoram[x];
				u8 inv = (BIT(chr, 7) ^ ((cpos == m_curs_pos) && BIT(m_framecnt, 5))) ? 0xff : 0;
				chr &= 0x7f;

				u8 gfx;
				if (ra < 8)
					gfx = m_p_chargen[(chr<<3) | ra ] ^ inv;
				else
					gfx = m_p_chargen[(chr<<3) | (ra&7) | 0x400] ^ inv;

				/* Display a scanline of a character */
				*p++ = BIT(gfx, 0);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 7);
				cpos++;
			}
		}
		ma+=80;
		if (ma > 0x77f)
			ma -= 0x780;
	}
	return 0;
}

/* F4 Character Displayer */
static const gfx_layout jonos_charlayout =
{
	8, 12,                   /* 8 x 12 characters */
	128,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 1024*8, 1025*8, 1026*8, 1027*8 },
	8*8                    /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_jonos )
	GFXDECODE_ENTRY( "chargen", 0x0000, jonos_charlayout, 0, 1 )
GFXDECODE_END


void jonos_state::jonos(machine_config &config)
{
	/* basic machine hardware */
	I8085A(config, m_maincpu, XTAL(16'000'000) / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &jonos_state::mem_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(jonos_state::screen_update));
	screen.set_size(640, 288);
	screen.set_visarea(0, 639, 0, 287);
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_jonos);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(jonos_state::kbd_put));
}


/* ROM definition */
ROM_START( jonos )
	ROM_REGION( 0x1000, "roms", 0 )
	ROM_LOAD( "jocrts04.rom", 0x0000, 0x1000, CRC(6a3d4048) SHA1(bdb0bc2c8c4e54261376e4ea3c2827d00d3d89bc) )

	ROM_REGION( 0x0800, "user1", 0 ) // Z80 code for serial & parallel ports
	ROM_LOAD( "joz80.rom",    0x0000, 0x0800, CRC(de1e8998) SHA1(270df08caf30cc8f18e740ef05dc8727a925a5da) )

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "jochset0.rom", 0x0000, 0x0800, CRC(1d8e9640) SHA1(74f3604acc71f9bc1e1f9479f6438feda79293a2) )
ROM_END

} // Anonymous namespace


/* Driver */

//   YEAR   NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME  FLAGS
COMP( 198?, jonos, 0,      0,      jonos,   jonos, jonos_state, empty_init, "Jonos", "Escort", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



jornada.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/***************************************************************************

    HP Jornada PDA skeleton driver

***************************************************************************/

#include "emu.h"
#include "cpu/arm7/arm7.h"
#include "machine/m950x0.h"
#include "machine/sa1110.h"
#include "machine/sa1111.h"
#include "sound/uda1344.h"
#include "video/sed1356.h"
#include "screen.h"
#include "emupal.h"
#include "speaker.h"

#define LOG_MCU     (1U << 1)
#define LOG_ALL     (LOG_MCU)

#define VERBOSE     (0)
#include "logmacro.h"

namespace
{

class jornada_state : public driver_device
{
public:
	jornada_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, "ram")
		, m_sa_periphs(*this, "onboard")
		, m_companion(*this, "companion")
		, m_epson(*this, "epson")
		, m_codec(*this, "codec")
		, m_nvram(*this, "nvram")
		, m_kbd_ports(*this, "KBD%u", 0U)
		, m_pen_x(*this, "PENX")
		, m_pen_y(*this, "PENY")
		, m_pen_button(*this, "PENZ")
	{ }

	void jornada720(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(key_changed);
	DECLARE_INPUT_CHANGED_MEMBER(pen_changed);

	enum : u8
	{
		MCU_TXDUMMY     = 0x11,
		MCU_TXDUMMY2    = 0x88
	};


	enum
	{
		KEY_Q = 0x21,
		KEY_W = 0x22,
		KEY_E = 0x23,
		KEY_R = 0x24,
		KEY_T = 0x25,
		KEY_Y = 0x26,
		KEY_U = 0x27,
		KEY_I = 0x28,
		KEY_O = 0x29,
		KEY_P = 0x2a,
		KEY_A = 0x31,
		KEY_S = 0x32,
		KEY_D = 0x33,
		KEY_F = 0x34,
		KEY_G = 0x35,
		KEY_H = 0x36,
		KEY_J = 0x37,
		KEY_K = 0x38,
		KEY_L = 0x39,
		KEY_Z = 0x41,
		KEY_X = 0x42,
		KEY_C = 0x43,
		KEY_V = 0x44,
		KEY_B = 0x45,
		KEY_N = 0x46,
		KEY_M = 0x47,

		KEY_0 = 0x1a,
		KEY_1 = 0x11,
		KEY_2 = 0x12,
		KEY_3 = 0x13,
		KEY_4 = 0x14,
		KEY_5 = 0x15,
		KEY_6 = 0x16,
		KEY_7 = 0x17,
		KEY_8 = 0x18,
		KEY_9 = 0x19,

		KEY_QL1 = 0x02,
		KEY_QL2 = 0x03,
		KEY_QL3 = 0x04,
		KEY_QL4 = 0x05,
		KEY_QL5 = 0x06,
		KEY_QL6 = 0x07,
		KEY_QL7 = 0x08,
		KEY_QL8 = 0x09,
		KEY_QL9 = 0x0a,
		KEY_QL10 = 0x0b,
		KEY_QL11 = 0x0c,

		KEY_SLASH = 0x78,
		KEY_BACKSLASH = 0x2b,
		KEY_MINUS = 0x1b,
		KEY_EQUALS = 0x1c,
		KEY_COMMA = 0x48,
		KEY_PERIOD = 0x49,
		KEY_QUOTE = 0x4b,
		KEY_COLON = 0x3a,

		KEY_ON_OFF = 0x7f,
		KEY_WIN = 0x71,
		KEY_FN = 0x66,
		KEY_BACKSPACE = 0x2c,
		KEY_CTRL = 0x72,
		KEY_ALT = 0x65,
		KEY_LSHIFT = 0x53,
		KEY_RSHIFT = 0x5c,
		KEY_DEL = 0x79,
		KEY_SPACE = 0x74,
		KEY_TAB = 0x51,
		KEY_ESC = 0x01,
		KEY_VOL_UP = 0x0e,
		KEY_VOL_DOWN = 0x0d,
		KEY_PLAY = 0x0f,
		KEY_UP = 0x5a,
		KEY_DOWN = 0x6a,
		KEY_LEFT = 0x69,
		KEY_RIGHT = 0x6b,
		KEY_ENTER = 0x4c
	};

	enum
	{
		PEN_X,
		PEN_Y,
		PEN_BUTTON
	};

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void device_reset_after_children() override;

	static constexpr u32 SA1110_CLOCK = 206000000;

	void main_map(address_map &map) ATTR_COLD;

	void cpu_rts_to_mcu(int state);
	void mcu_assemble_touch_data();
	void mcu_process_byte(u8 value);
	void mcu_byte_received(u16 data);
	void eeprom_data_received(u16 data);
	void eeprom_select(int state);

	enum mcu_state : int
	{
		MCU_IDLE,

		MCU_KBD_SEND_COUNT,
		MCU_KBD_SEND_CODES,

		MCU_TOUCH_SEND_DATA,

		MCU_BATTERY_SEND_DATA
	};

	// devices
	required_device<cpu_device> m_maincpu;
	required_shared_ptr<u32> m_ram;
	required_device<sa1110_periphs_device> m_sa_periphs;
	required_device<sa1111_device> m_companion;
	required_device<sed1356_device> m_epson;
	required_device<uda1344_device> m_codec;
	required_device<m95020_device> m_nvram;

	required_ioport_array<3> m_kbd_ports;
	required_ioport m_pen_x;
	required_ioport m_pen_y;
	required_ioport m_pen_button;

	// MCU-related members
	bool m_cpu_to_mcu_rts;
	int m_mcu_state;
	u8 m_mcu_key_send_idx;
	u8 m_mcu_key_codes[2][8];
	u8 m_mcu_key_count[2];
	u8 m_mcu_key_idx[2];
	u8 m_mcu_touch_send_idx;
	u8 m_mcu_touch_data[2][8];
	u8 m_mcu_touch_count[2];
	u8 m_mcu_touch_idx[2];
	u8 m_mcu_battery_data[3];
	u8 m_mcu_battery_idx;
	u8 m_mcu_rx_fifo[8];
	u8 m_mcu_rx_count;
};

void jornada_state::main_map(address_map &map)
{
	map(0x00000000, 0x01ffffff).rom().region("firmware", 0);
	//map(0x1a000000, 0x1a000fff).noprw(); // Debug Attachment Region
	map(0x1a00013c, 0x1a00013f).noprw();
	map(0x1a000400, 0x1a000403).noprw();
	map(0x40000000, 0x40001fff).m(m_companion, FUNC(sa1111_device::map));
	map(0x48000000, 0x481fffff).m(m_epson, FUNC(sed1356_device::map));
	map(0x48200000, 0x4827ffff).m(m_epson, FUNC(sed1356_device::vram_map));
	map(0x80000000, 0xbfffffff).m(m_sa_periphs, FUNC(sa1110_periphs_device::map));
	map(0xc0000000, 0xc1ffffff).ram().share("ram");
	map(0xe0000000, 0xe0003fff).noprw(); // Cache-Flush Region 0
	map(0xe0100000, 0xe01003ff).noprw(); // Cache-Flush Region 1
}

void jornada_state::device_reset_after_children()
{
	driver_device::device_reset_after_children();

	m_sa_periphs->gpio_in<4>(0); // Flag as plugged into AC power
	m_sa_periphs->gpio_in<9>(1); // Pen input is active-low
	m_sa_periphs->gpio_in<26>(0); // Flag as charging
}

void jornada_state::cpu_rts_to_mcu(int state)
{
	const bool old = m_cpu_to_mcu_rts;
	m_cpu_to_mcu_rts = (bool)state;
	if (!old || m_cpu_to_mcu_rts || m_mcu_rx_count == 0)
		return;

	for (u8 i = 0; i < m_mcu_rx_count; i++)
	{
		mcu_process_byte(m_mcu_rx_fifo[i]);
	}
	m_mcu_rx_count = 0;
}

void jornada_state::mcu_assemble_touch_data()
{
	const u16 pen_x = m_pen_x->read();
	const u16 pen_y = m_pen_y->read();
	const u16 x0 = pen_x;
	const u16 x1 = (pen_x + 1) & 0x3ff;
	const u16 x2 = (pen_x - 1) & 0x3ff;
	const u16 y0 = pen_y;
	const u16 y1 = (pen_y + 1) & 0x3ff;
	const u16 y2 = (pen_y - 1) & 0x3ff;
	const u8 touch_recv_idx = 1 - m_mcu_touch_send_idx;
	m_mcu_touch_data[touch_recv_idx][0] = (u8)x0;
	m_mcu_touch_data[touch_recv_idx][1] = (u8)x1;
	m_mcu_touch_data[touch_recv_idx][2] = (u8)x2;
	m_mcu_touch_data[touch_recv_idx][3] = (u8)y0;
	m_mcu_touch_data[touch_recv_idx][4] = (u8)y1;
	m_mcu_touch_data[touch_recv_idx][5] = (u8)y2;
	m_mcu_touch_data[touch_recv_idx][6] = (u8)(((x0 >> 8) & 0x03) | ((x1 >> 6) & 0xc0) | ((x2 >> 4) & 0x30));
	m_mcu_touch_data[touch_recv_idx][7] = (u8)(((y0 >> 8) & 0x03) | ((y1 >> 6) & 0xc0) | ((y2 >> 4) & 0x30));
	m_mcu_touch_count[touch_recv_idx] = 8;
}

void jornada_state::mcu_process_byte(u8 value)
{
	u8 response = MCU_TXDUMMY;
	switch (m_mcu_state)
	{
	case MCU_IDLE:
		switch (value)
		{
		case 0x90:
			LOGMASKED(LOG_MCU, "mcu_byte_received in MCU_IDLE: GetScanKeyCode, entering KBD_SEND_COUNT state\n");
			m_mcu_state = MCU_KBD_SEND_COUNT;
			m_mcu_key_send_idx = 1 - m_mcu_key_send_idx;
			break;
		case 0xa0:
			LOGMASKED(LOG_MCU, "mcu_byte_received in MCU_IDLE: GetTouchSamples, entering TOUCH_SEND_DATA state\n");
			m_mcu_state = MCU_TOUCH_SEND_DATA;
			m_mcu_touch_send_idx = 1 - m_mcu_touch_send_idx;
			break;
		case 0xc0:
			LOGMASKED(LOG_MCU, "mcu_byte_received in MCU_IDLE: GetBatteryData, entering BATTERY_SEND_DATA state\n");
			m_mcu_state = MCU_BATTERY_SEND_DATA;
			m_mcu_battery_idx = 0;
			m_mcu_battery_data[0] = 0xff; // LSB of main battery level
			m_mcu_battery_data[1] = 0xff; // LSB of backup battery level
			m_mcu_battery_data[2] = 0x0f; // MSBs of both battery levels (backup in 3:2, main in 1:0)
			break;
		default:
			LOGMASKED(LOG_MCU, "mcu_byte_received in MCU_IDLE: Unknown (%02x), ignoring and sending TxDummy response\n", value);
			break;
		}
		break;

	case MCU_KBD_SEND_COUNT:
		if (value == MCU_TXDUMMY || value == MCU_TXDUMMY2)
		{
			LOGMASKED(LOG_MCU, "mcu_byte_received in MCU_KBD_SEND_COUNT: TxDummy, entering KBD_SEND_CODES state\n");
			response = m_mcu_key_count[m_mcu_key_send_idx];
			m_mcu_state = MCU_KBD_SEND_CODES;
		}
		else
		{
			LOGMASKED(LOG_MCU, "mcu_byte_received in MCU_KBD_SEND_COUNT: Unknown (%02x), sending ErrorCode response and returning to IDLE state\n", value);
			response = 0;
		}
		break;

	case MCU_BATTERY_SEND_DATA:
		if (value == MCU_TXDUMMY || value == MCU_TXDUMMY2)
		{
			response = m_mcu_battery_data[m_mcu_battery_idx];
			m_mcu_battery_idx++;
			if (m_mcu_battery_idx < 3)
			{
				LOGMASKED(LOG_MCU, "mcu_byte_received in MCU_BATTERY_SEND_DATA: TxDummy, sending battery data %02x with %d remaining\n", response, 3 - m_mcu_battery_idx);
			}
			else
			{
				LOGMASKED(LOG_MCU, "mcu_byte_received in MCU_BATTERY_SEND_DATA: TxDummy, sending battery data %02x and returning to IDLE state\n", response);
				m_mcu_state = MCU_IDLE;
				m_mcu_battery_idx = 0;
			}
		}
		else
		{
			LOGMASKED(LOG_MCU, "mcu_byte_received in MCU_BATTERY_SEND_DATA: Unknown (%02x), sending ErrorCode response and returning to IDLE state\n", value);
			response = 0;
		}
		break;

	case MCU_TOUCH_SEND_DATA:
		if (value == MCU_TXDUMMY || value == MCU_TXDUMMY2)
		{
			m_mcu_touch_count[m_mcu_touch_send_idx]--;
			response = m_mcu_touch_data[m_mcu_touch_send_idx][m_mcu_touch_idx[m_mcu_touch_send_idx]];
			m_mcu_touch_idx[m_mcu_touch_send_idx]++;
			if (m_mcu_touch_count[m_mcu_touch_send_idx])
			{
				LOGMASKED(LOG_MCU, "mcu_byte_received in MCU_TOUCH_SEND_DATA: TxDummy, sending touch data %02x with %d remaining\n", response, m_mcu_touch_count[m_mcu_touch_send_idx]);
			}
			else
			{
				LOGMASKED(LOG_MCU, "mcu_byte_received in MCU_TOUCH_SEND_DATA: TxDummy, sending touch data %02x and returning to IDLE state\n", response);
				m_mcu_state = MCU_IDLE;
				m_mcu_touch_idx[m_mcu_touch_send_idx] = 0;
			}
		}
		else
		{
			LOGMASKED(LOG_MCU, "mcu_byte_received in MCU_TOUCH_SEND_DATA: Unknown (%02x), sending ErrorCode response and returning to IDLE state\n", value);
			response = 0;
		}
		break;

	case MCU_KBD_SEND_CODES:
		if (value == MCU_TXDUMMY || value == MCU_TXDUMMY2)
		{
			m_mcu_key_count[m_mcu_key_send_idx]--;
			response = m_mcu_key_codes[m_mcu_key_send_idx][m_mcu_key_idx[m_mcu_key_send_idx]];
			m_mcu_key_idx[m_mcu_key_send_idx]++;
			if (m_mcu_key_count[m_mcu_key_send_idx])
			{
				LOGMASKED(LOG_MCU, "mcu_byte_received in MCU_KBD_SEND_CODES: TxDummy, sending scan code %02x with %d remaining\n", response, m_mcu_key_count[m_mcu_key_send_idx]);
			}
			else
			{
				LOGMASKED(LOG_MCU, "mcu_byte_received in MCU_KBD_SEND_CODES: TxDummy, sending scan code %02x and returning to IDLE state\n", response);
				m_mcu_state = MCU_IDLE;
				m_mcu_key_idx[m_mcu_key_send_idx] = 0;
			}
		}
		else
		{
			LOGMASKED(LOG_MCU, "mcu_byte_received in MCU_KBD_SEND_CODES: Unknown (%02x), sending ErrorCode response and returning to IDLE state\n");
			response = 0;
		}
		break;

	default:
		LOGMASKED(LOG_MCU, "mcu_byte_received in %08x: %02x\n", m_mcu_state, value);
		break;
	}

	response = bitswap<8>(response, 0, 1, 2, 3, 4, 5, 6, 7);
	m_sa_periphs->ssp_in((u16)response);
}

void jornada_state::mcu_byte_received(u16 data)
{
	const u8 raw_value = (u8)(data >> 8);
	const u8 value = bitswap<8>(raw_value, 0, 1, 2, 3, 4, 5, 6, 7);

	if (m_mcu_rx_count == 0 && !m_cpu_to_mcu_rts)
	{
		mcu_process_byte(value);
		return;
	}

	m_mcu_rx_fifo[m_mcu_rx_count++] = value;
}

void jornada_state::eeprom_select(int state)
{
	m_nvram->select_w(!state);
}

void jornada_state::eeprom_data_received(u16 data)
{
	const u8 response = m_nvram->access((u8)data);
	m_companion->ssp_in((u16)response);
}

INPUT_CHANGED_MEMBER(jornada_state::key_changed)
{
	u8 scan_code = (u8)param;

	m_sa_periphs->gpio_in<0>(1);
	m_sa_periphs->gpio_in<0>(0);

	const u8 key_recv_idx = 1 - m_mcu_key_send_idx;
	if (m_mcu_key_count[key_recv_idx] < 8)
	{
		m_mcu_key_codes[key_recv_idx][m_mcu_key_count[key_recv_idx]] = scan_code | (newval ? 0x00 : 0x80);
		m_mcu_key_count[key_recv_idx]++;
	}
}

INPUT_CHANGED_MEMBER(jornada_state::pen_changed)
{
	switch (param)
	{
	case PEN_X:
	case PEN_Y:
		if (m_pen_button->read() && m_mcu_state == MCU_IDLE)
		{
			logerror("Pen move, queueing data\n");
			mcu_assemble_touch_data();
			m_sa_periphs->gpio_in<9>(1);
			m_sa_periphs->gpio_in<9>(0);
		}
		break;
	case PEN_BUTTON:
		if (newval)
		{
			logerror("PEN_BUTTON, newval set (assembling touch data)\n");
			m_sa_periphs->gpio_in<9>(0);
			mcu_assemble_touch_data();
		}
		else
		{
			logerror("PEN_BUTTON, newval not set\n");
			m_sa_periphs->gpio_in<9>(1);
		}
		break;
	}
}

static INPUT_PORTS_START( jornada720 )
	PORT_START("KBD0")
	PORT_BIT(0x00000001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_Q)
	PORT_BIT(0x00000002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_W)
	PORT_BIT(0x00000004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_E)
	PORT_BIT(0x00000008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_R)
	PORT_BIT(0x00000010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_T)
	PORT_BIT(0x00000020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_Y)
	PORT_BIT(0x00000040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_U)
	PORT_BIT(0x00000080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_I)
	PORT_BIT(0x00000100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_O)
	PORT_BIT(0x00000200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_P)
	PORT_BIT(0x00000400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_A)
	PORT_BIT(0x00000800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_S)
	PORT_BIT(0x00001000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_D)
	PORT_BIT(0x00002000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_F)
	PORT_BIT(0x00004000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_G)
	PORT_BIT(0x00008000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_H)
	PORT_BIT(0x00010000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_J)
	PORT_BIT(0x00020000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_K)
	PORT_BIT(0x00040000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_L)
	PORT_BIT(0x00080000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_Z)
	PORT_BIT(0x00100000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_X)
	PORT_BIT(0x00200000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_C)
	PORT_BIT(0x00400000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_V)
	PORT_BIT(0x00800000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_B)
	PORT_BIT(0x01000000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_N)
	PORT_BIT(0x02000000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_M)
	PORT_BIT(0xfc000000, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("KBD1")
	PORT_BIT(0x00000001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_0)
	PORT_BIT(0x00000002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_1)
	PORT_BIT(0x00000004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_2)
	PORT_BIT(0x00000008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_3)
	PORT_BIT(0x00000010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_4)
	PORT_BIT(0x00000020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_5)
	PORT_BIT(0x00000040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_6)
	PORT_BIT(0x00000080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_7)
	PORT_BIT(0x00000100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_8)
	PORT_BIT(0x00000200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_9)
	PORT_BIT(0x00000400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH)      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_SLASH)
	PORT_BIT(0x00000800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_BACKSLASH)
	PORT_BIT(0x00001000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_MINUS)
	PORT_BIT(0x00002000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("=") PORT_CODE(KEYCODE_EQUALS)     PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_EQUALS)
	PORT_BIT(0x00004000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA)      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_COMMA)
	PORT_BIT(0x00008000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_PERIOD)
	PORT_BIT(0x00010000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("\'") PORT_CODE(KEYCODE_QUOTE)     PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_QUOTE)
	PORT_BIT(0x00020000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON)      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_COLON)
	PORT_BIT(0xfffc0000, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("KBD2")
	PORT_BIT(0x00000001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Power")          PORT_CODE(KEYCODE_END)       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_ON_OFF)
	PORT_BIT(0x00000002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Windows Key")    PORT_CODE(KEYCODE_LALT)      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_WIN)
	PORT_BIT(0x00000004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Fn")             PORT_CODE(KEYCODE_RCONTROL)  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_FN)
	PORT_BIT(0x00000008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Backspace")      PORT_CODE(KEYCODE_BACKSPACE) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_BACKSPACE)
	PORT_BIT(0x00000010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Control")        PORT_CODE(KEYCODE_LCONTROL)  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_CTRL)
	PORT_BIT(0x00000020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Alt")            PORT_CODE(KEYCODE_RALT)      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_ALT)
	PORT_BIT(0x00000040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Shift")     PORT_CODE(KEYCODE_LSHIFT)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_LSHIFT)
	PORT_BIT(0x00000080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Shift")    PORT_CODE(KEYCODE_RSHIFT)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_RSHIFT)
	PORT_BIT(0x00000100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Delete")         PORT_CODE(KEYCODE_DEL)       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_DEL)
	PORT_BIT(0x00000200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Space")          PORT_CODE(KEYCODE_SPACE)     PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_SPACE)
	PORT_BIT(0x00000400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab")            PORT_CODE(KEYCODE_TAB)       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_TAB)
	PORT_BIT(0x00000800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Escape")         PORT_CODE(KEYCODE_ESC)       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_ESC)
	PORT_BIT(0x00001000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Volume Up")      PORT_CODE(KEYCODE_PGUP)      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_VOL_UP)
	PORT_BIT(0x00002000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Volume Down")    PORT_CODE(KEYCODE_PGDN)      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_VOL_DOWN)
	PORT_BIT(0x00004000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Play")           PORT_CODE(KEYCODE_END)       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_PLAY)
	PORT_BIT(0x00008000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Up")             PORT_CODE(KEYCODE_UP)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_UP)
	PORT_BIT(0x00010000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Down")           PORT_CODE(KEYCODE_DOWN)      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_DOWN)
	PORT_BIT(0x00020000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left")           PORT_CODE(KEYCODE_LEFT)      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_LEFT)
	PORT_BIT(0x00040000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right")          PORT_CODE(KEYCODE_RIGHT)     PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_RIGHT)
	PORT_BIT(0x00080000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Quicklaunch 1")  PORT_CODE(KEYCODE_F1)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_QL1)
	PORT_BIT(0x00100000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Quicklaunch 2")  PORT_CODE(KEYCODE_F2)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_QL2)
	PORT_BIT(0x00200000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Quicklaunch 3")  PORT_CODE(KEYCODE_F3)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_QL3)
	PORT_BIT(0x00400000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Quicklaunch 4")  PORT_CODE(KEYCODE_F4)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_QL4)
	PORT_BIT(0x00800000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Quicklaunch 5")  PORT_CODE(KEYCODE_F5)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_QL5)
	PORT_BIT(0x01000000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Quicklaunch 6")  PORT_CODE(KEYCODE_F6)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_QL6)
	PORT_BIT(0x02000000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Quicklaunch 7")  PORT_CODE(KEYCODE_F7)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_QL7)
	PORT_BIT(0x04000000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Quicklaunch 8")  PORT_CODE(KEYCODE_F8)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_QL8)
	PORT_BIT(0x08000000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Quicklaunch 9")  PORT_CODE(KEYCODE_F9)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_QL9)
	PORT_BIT(0x10000000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Quicklaunch 10") PORT_CODE(KEYCODE_F10)       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_QL10)
	PORT_BIT(0x20000000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Quicklaunch 11") PORT_CODE(KEYCODE_F10)       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_QL11)
	PORT_BIT(0x40000000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Enter")          PORT_CODE(KEYCODE_ENTER)     PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::key_changed), jornada_state::KEY_ENTER)
	PORT_BIT(0x80000000, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("PENX")
	PORT_BIT(0x3ff, 0x1ff, IPT_LIGHTGUN_X) PORT_NAME("Pen X") PORT_MINMAX(0, 1023) PORT_SENSITIVITY(50) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::pen_changed), jornada_state::PEN_X)

	PORT_START("PENY")
	PORT_BIT(0x3ff, 0x1ff, IPT_LIGHTGUN_Y) PORT_NAME("Pen Y") PORT_MINMAX(0, 1023) PORT_SENSITIVITY(50) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::pen_changed), jornada_state::PEN_Y)

	PORT_START("PENZ")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Pen Touch") PORT_CODE(MOUSECODE_BUTTON1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jornada_state::pen_changed), jornada_state::PEN_BUTTON)
	PORT_BIT(0xfe, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

void jornada_state::machine_start()
{
	save_item(NAME(m_cpu_to_mcu_rts));
	save_item(NAME(m_mcu_state));
	save_item(NAME(m_mcu_key_send_idx));
	save_item(NAME(m_mcu_key_codes));
	save_item(NAME(m_mcu_key_count));
	save_item(NAME(m_mcu_key_idx));
	save_item(NAME(m_mcu_touch_send_idx));
	save_item(NAME(m_mcu_touch_data));
	save_item(NAME(m_mcu_touch_count));
	save_item(NAME(m_mcu_touch_idx));
	save_item(NAME(m_mcu_battery_data));
	save_item(NAME(m_mcu_battery_idx));
	save_item(NAME(m_mcu_rx_fifo));
	save_item(NAME(m_mcu_rx_count));
}

void jornada_state::machine_reset()
{
	m_mcu_state = MCU_IDLE;
	m_cpu_to_mcu_rts = false;

	m_mcu_key_send_idx = 0;
	memset(m_mcu_key_codes[0], 0, sizeof(m_mcu_key_codes[0]));
	memset(m_mcu_key_codes[1], 0, sizeof(m_mcu_key_codes[1]));
	memset(m_mcu_key_count, 0, sizeof(m_mcu_key_count));
	memset(m_mcu_key_idx, 0, sizeof(m_mcu_key_idx));

	m_mcu_touch_send_idx = 0;
	memset(m_mcu_touch_data[0], 0, sizeof(m_mcu_touch_data[0]));
	memset(m_mcu_touch_data[1], 0, sizeof(m_mcu_touch_data[1]));
	memset(m_mcu_touch_count, 0, sizeof(m_mcu_touch_count));
	memset(m_mcu_touch_idx, 0, sizeof(m_mcu_touch_idx));

	memset(m_mcu_battery_data, 0, sizeof(m_mcu_battery_data));
	m_mcu_battery_idx = 0;

	memset(m_mcu_rx_fifo, 0, sizeof(m_mcu_rx_fifo));
	m_mcu_rx_count = 0;

	LOGMASKED(LOG_MCU, "MCU State: %08x\n", m_mcu_state);
}

void jornada_state::jornada720(machine_config &config)
{
	SA1110(config, m_maincpu, SA1110_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &jornada_state::main_map);

	SA1110_PERIPHERALS(config, m_sa_periphs, SA1110_CLOCK, m_maincpu);
	m_sa_periphs->ssp_out().set(FUNC(jornada_state::mcu_byte_received));
	m_sa_periphs->gpio_out<10>().set(FUNC(jornada_state::cpu_rts_to_mcu));

	SA1111(config, m_companion, 3.6864_MHz_XTAL, m_maincpu);
	m_companion->set_audio_codec_tag(m_codec);
	m_companion->pb_out<0>().set(FUNC(jornada_state::eeprom_select));
	m_companion->ssp_out().set(FUNC(jornada_state::eeprom_data_received));
	m_companion->l3_addr_out().set(m_codec, FUNC(uda1344_device::l3_addr_w));
	m_companion->l3_data_out().set(m_codec, FUNC(uda1344_device::l3_data_w));
	m_companion->i2s_out().set(m_codec, FUNC(uda1344_device::i2s_input_w));
	m_companion->irq_out().set(m_sa_periphs, FUNC(sa1110_periphs_device::gpio_in<1>));

	M95020(config, m_nvram);

	UDA1344(config, m_codec);
	m_codec->l3_ack_out().set(m_companion, FUNC(sa1111_device::l3wd_in));
	m_codec->add_route(0, "speaker", 0.5, 0);
	m_codec->add_route(1, "speaker", 0.5, 1);

	SPEAKER(config, "speaker", 2).front();

	SED1356(config, m_epson);
	m_epson->set_screen("screen");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_size(640, 240);
	screen.set_visarea(0, 640-1, 0, 240-1);
	screen.set_screen_update(m_epson, FUNC(sed1356_device::screen_update));
}

/***************************************************************************

  System driver(s)

***************************************************************************/

ROM_START( jorn720 )
	ROM_REGION32_LE( 0x2000000, "firmware", ROMREGION_ERASE00 )
	ROM_LOAD( "jornada720.bin", 0x0000000, 0x2000000, CRC(5fcd433a) SHA1(f05f7b377b582a7355bf119d74435f0ee6104cca) )

	ROM_REGION( 0x100, "nvram", ROMREGION_ERASE00 )
	ROM_LOAD( "jorn720_eeprom.bin", 0x000, 0x100, CRC(9bc1d53a) SHA1(793d6ff355e2e9b3e75574ff80edfa5af2aaeee6) BAD_DUMP )
ROM_END

} // anonymous namespace

COMP( 2000, jorn720, 0, 0, jornada720, jornada720, jornada_state, empty_init, "Hewlett Packard", "Jornada 720", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



jr100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic,Robbbert
/***************************************************************************

JR-100 National / Panasonic

2010-08-23 Initial driver version

CPU: User manual for JR100U states it is a MN1800 (==MC6802), but the photo
shows it to be a Fujitsu MB8861H, which is a MC6800 with extra instructions.
The manual also states the clock to be 890kHz but in fact it is 894kHz.
MB14392 custom LSI generates the various clock signals for the computer.

List of extra instructions:
0x71 NIM And Immediate with memory    data & M -> M       Index, 8,3   71,data,index
     V is reset; if result = 0, Z is set, N is reset; else Z is reset, N is set
0x72 OIM Or Immediate with memory     data | M -> M       Index, 8,3   72,data,index
     V is reset; if result = 0, Z is set, N is reset; else Z is reset, N is set
0x75 XIM Xor Immediate with memory    data ^ M -> M       Index, 8,3   75,data,index
     if result = 0, Z is set, N is reset; else Z is reset, N is set
0x7B TMM Test under mask with memory  (data & M) ^ data   Index, 7,3   7B,data,index
     flags not stated
0xEC ADX Add Index Register           X + data -> X       Immed,4,2
     "adds 1 byte to the index register"?  flags not stated
0xFC ADX Add Index Register           X + (M) -> X        Extend,7,3
     "adds 2 bytes to the index register"?  flags not stated. One page says result goes to M.

There is also an MB8861 equivalent of the MC6802 which is called MB8871. Behaviour
of undefined instructions is not the same as the Motorola parts. Variants:
MB8861N : 1MHz
MB8861E : 1.3MHz
MB8861H : 2MHz

Similar to MC6875, the MB8867 could be used as a clock oscillator and divide by 8
circuit to drive the CPU directly.

CC02 joystick input register (active high)
bit 7,6: undefined
bit 5: 0
bit 4: button
bit 3: down
bit 2: up
bit 1: left
bit 0: right

TODO:
- Need software.
- Paste drops many characters.
- Cassette not working correctly. If the VIA divider is 32, then SAVE produces
  a good 600 baud recording (verified on super80). But the divider must be 16
  to LOAD the tape back in. It is suspected that the VIA has a timer bug.


****************************************************************************/

#include "emu.h"

#include "cpu/m6800/m6800.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "machine/6522via.h"
#include "machine/timer.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "multibyte.h"
#include "utf8.h"


namespace {

class jr100_state : public driver_device
{
public:
	jr100_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_ram(*this, "ram")
		, m_pcg(*this, "pcg")
		, m_vram(*this, "vram")
		, m_rom(*this, "maincpu")
		, m_via(*this, "via")
		, m_cassette(*this, "cassette")
		, m_speaker(*this, "speaker")
		, m_region_maincpu(*this, "maincpu")
		, m_io_keyboard(*this, "LINE%u", 0U)
		, m_maincpu(*this, "maincpu")
		, m_sound_timer(nullptr)
	{ }

	void jr100(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	uint8_t m_keyboard_line = 0U;
	bool m_use_pcg = false;
	bool m_pb7 = false;
	uint32_t screen_update_jr100(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	TIMER_CALLBACK_MEMBER(sound_tick);
	uint8_t pb_r();
	void pa_w(uint8_t data);
	void pb_w(uint8_t data);
	void cb2_w(int state);
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);

	void mem_map(address_map &map) ATTR_COLD;

	required_shared_ptr<uint8_t> m_ram;
	required_shared_ptr<uint8_t> m_pcg;
	required_shared_ptr<uint8_t> m_vram;
	required_region_ptr<uint8_t> m_rom;
	required_device<via6522_device> m_via;
	required_device<cassette_image_device> m_cassette;
	required_device<speaker_sound_device> m_speaker;
	required_memory_region m_region_maincpu;
	required_ioport_array<9> m_io_keyboard;
	required_device<m6800_cpu_device> m_maincpu;

	emu_timer *m_sound_timer = nullptr;
};


void jr100_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).ram().share("ram");
	//map(0x4000, 0x7fff).ram();   expansion ram
	//map(0x8000, 0xbfff).rom();   expansion rom
	map(0xc000, 0xc0ff).ram().share("pcg");
	map(0xc100, 0xc3ff).ram().share("vram");
	map(0xc800, 0xc80f).m(m_via, FUNC(via6522_device::map));
	//map(0xcc00, 0xcfff).;   expansion i/o
	//map(0xd000, 0xd7ff).rom();   expansion rom for printer control
	//map(0xd800, 0xdfff).rom();   expansion rom
	map(0xe000, 0xffff).rom().region("maincpu", 0);
}

// Input ports - names in [ ] are screen actions; otherwise the text is literally printed onscreen
INPUT_PORTS_START( jr100 )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z [Line Insert]") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X [Cancel]") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C [Break]") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0xE0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A Auto") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S Stop") PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D Dim") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F For") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G Goto") PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0xE0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q Gosub") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W Ret") PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E End") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R Run") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T Then") PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0xE0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 ! [Home]") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 \" Verify") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR(34)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 # Save") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 $ Load") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 % [Delete]") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0xE0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 & " UTF8_LEFT) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 ' " UTF8_DOWN) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR(39)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 ( " UTF8_UP) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 ) " UTF8_RIGHT) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0 ^ [Insert]") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('^')
	PORT_BIT(0xE0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y Locate") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U @ If") PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('@')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"I ¥ Input") PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O [ Option") PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('[')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P ] Print") PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR(']')
	PORT_BIT(0xE0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H Poke") PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J Rnd(") PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K ? Read") PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('?')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L / List") PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('/')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("; + Rem") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0xE0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("LINE7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V [Graphics]") PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B [Hcopy]") PORT_CODE(KEYCODE_B) PORT_CHAR('B') // HCOPY doesn't seem to do anything
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N Next") PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M _ Cls") PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('_')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(", < Data") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0xE0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("LINE8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(". > Peek(") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(32)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(": * Chr$(") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("- = [Del]") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0xE0, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

void jr100_state::machine_start()
{
	if (!m_sound_timer)
		m_sound_timer = timer_alloc(FUNC(jr100_state::sound_tick), this);

	save_item(NAME(m_keyboard_line));
	save_item(NAME(m_use_pcg));
	save_item(NAME(m_pb7));
}

void jr100_state::machine_reset()
{
	attotime timer_period = attotime::from_hz(XTAL(14'318'181) / 16);
	m_sound_timer->adjust(timer_period, 0, timer_period);
}

uint32_t jr100_state::screen_update_jr100(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 sy=0,ma=0;

	for (u8 y = 0; y < 24; y++)
	{
		for (u8 ra = 0; ra < 8; ra++)
		{
			uint16_t *p = &bitmap.pix(sy++);
			for (u16 x = ma; x < ma + 32; x++)
			{
				u8 chr = m_vram[x];
				bool const attr = BIT(chr, 7);
				chr &= 0x7f;
				u8 gfx;
				// ATTR is inverted for normal char or use PCG in case of CMODE1
				if (m_use_pcg && attr && (chr < 32))
					gfx = m_pcg[(chr<<3) | ra];
				else
					gfx = m_rom[(chr<<3) | ra] ^ (attr ? 0xff : 0);

				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma += 32;
	}

	return 0;
}

static const gfx_layout tilesrom_layout =
{
	8,8,
	128,
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7, 8 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};

static const gfx_layout tilesram_layout =
{
	8,8,
	32,
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7, 8 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};

static GFXDECODE_START( gfx_jr100 )
	GFXDECODE_ENTRY( "maincpu", 0x0000, tilesrom_layout, 0, 1 )   // inside rom
	GFXDECODE_RAM  ( "pcg",     0x0000, tilesram_layout, 0, 1 )   // user defined
GFXDECODE_END

uint8_t jr100_state::pb_r()
{
	uint8_t data = 0x1f;
	if (m_keyboard_line < 9)
		data = m_io_keyboard[m_keyboard_line]->read() & 0xbf;
	data |= (m_pb7 ? 0x40 : 0);
	return data;
}

void jr100_state::pa_w(uint8_t data)
{
	m_keyboard_line = data & 0x0f;
}

void jr100_state::pb_w(uint8_t data)
{
	m_use_pcg = BIT(data, 5);
	m_pb7 = BIT(data, 7);
	m_speaker->level_w(m_pb7);
	m_via->write_pb6(m_pb7);
}

void jr100_state::cb2_w(int state)
{
	m_cassette->output(state ? -1.0 : +1.0);
}

TIMER_CALLBACK_MEMBER(jr100_state::sound_tick)
{
	double level = (m_cassette->input());
	if (level > 0.0)
	{
		m_via->write_ca1(0);
		m_via->write_cb1(0);
	}
	else
	{
		m_via->write_ca1(1);
		m_via->write_cb1(1);
	}
}

QUICKLOAD_LOAD_MEMBER(jr100_state::quickload_cb)
{
	int quick_length;
	uint8_t buf[0x10000];
	int read_;
	quick_length = image.length();
	if (quick_length >= 0xffff)
		return std::make_pair(image_error::INVALIDLENGTH, std::string());
	read_ = image.fread(buf, quick_length);
	if (read_ != quick_length)
		return std::make_pair(image_error::UNSPECIFIED, std::string());

	if (buf[0]!=0x50 || buf[1]!=0x52 || buf[2]!=0x4F || buf[3]!=0x47)
		// this is not PRG
		return std::make_pair(image_error::INVALIDIMAGE, std::string());

	int pos = 4;
	if (get_u32le(&buf[pos])!=1)
		// not version 1 of PRG file
		return std::make_pair(image_error::INVALIDIMAGE, std::string());

	pos += 4;
	uint32_t len = get_u32le(&buf[pos]); pos+= 4;
	pos += len; // skip name
	uint32_t start_address = get_u32le(&buf[pos]); pos+= 4;
	uint32_t code_length   = get_u32le(&buf[pos]); pos+= 4;
	uint32_t flag          = get_u32le(&buf[pos]); pos+= 4;

	uint32_t end_address = start_address + code_length - 1;
	// copy code
	memcpy(m_ram + start_address,buf + pos,code_length);
	if (flag == 0)
	{
		m_ram[end_address + 1] =  0xdf;
		m_ram[end_address + 2] =  0xdf;
		m_ram[end_address + 3] =  0xdf;
		put_u16be(&m_ram[6 ], end_address);
		put_u16be(&m_ram[8 ], end_address + 1);
		put_u16be(&m_ram[10], end_address + 2);
		put_u16be(&m_ram[12], end_address + 3);
	}

	return std::make_pair(std::error_condition(), std::string());
}

void jr100_state::jr100(machine_config &config)
{
	/* basic machine hardware */
	M6800(config, m_maincpu, XTAL(14'318'181) / 16);  //actually MB8861
	m_maincpu->set_addrmap(AS_PROGRAM, &jr100_state::mem_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(256, 192); /* border size not accurate */
	screen.set_visarea(0, 256 - 1, 0, 192 - 1);
	screen.set_screen_update(FUNC(jr100_state::screen_update_jr100));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_jr100);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	MOS6522(config, m_via, XTAL(14'318'181) / 16);   // see note in TODO
	m_via->readpb_handler().set(FUNC(jr100_state::pb_r));
	m_via->writepa_handler().set(FUNC(jr100_state::pa_w));
	m_via->writepb_handler().set(FUNC(jr100_state::pb_w));
	m_via->cb2_handler().set(FUNC(jr100_state::cb2_w));
	m_via->irq_handler().set_inputline(m_maincpu, M6800_IRQ_LINE);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	CASSETTE(config, m_cassette, 0);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	/* quickload */
	QUICKLOAD(config, "quickload", "prg", attotime::from_seconds(2)).set_load_callback(FUNC(jr100_state::quickload_cb));
}


/* ROM definition */
ROM_START( jr100 )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "jr100.ic5", 0x0000, 0x2000, CRC(951d08a1) SHA1(edae3daaa94924e444bbe485ac2bcd5cb5b22ca2))
ROM_END

ROM_START( jr100u )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "jr100u.ic5", 0x0000, 0x2000, CRC(f589dd8d) SHA1(78a51f2ae055bf4dc1b0887a6277f5dbbd8ba512))
ROM_END

} // Anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY      FULLNAME   FLAGS
COMP( 1981, jr100,  0,      0,      jr100,   jr100, jr100_state, empty_init, "National",  "JR-100",  MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1981, jr100u, jr100,  0,      jr100,   jr100, jr100_state, empty_init, "Panasonic", "JR-100U", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



jr200.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese, Roberto Zandona'
/***************************************************************************

JR-200 (c) 1982 National / Panasonic

BIOS will jump to D800 if it contains 7E. If not, it will jump to A000
 if it contains CE. Otherwise, the Monitor is entered. From the monitor,
 GA000 for cold start Basic, or GE79D for warm start Basic.

To get to the monitor from Basic type MON. Commands must be in UPPERcase.
Dnnnn : Display block of hex (D for next block)
Gnnnn : Go to address
Mnnnn : Modify memory at address

Cassette (if it worked):
- POKE 43,0 for 2400 baud, or POKE 43,1 for 600 baud (affects SAVE only);
- SAVE "x" (1-16 chars) to save. LOAD to load (it autodetects the speed).
- 600 baud uses Kansas City format, each byte has a start bit, 8 data bits
  (LSB first) and 3 stop bits. 1 = 2 cycles @1200Hz; 0 = 4 cycles @2400Hz
- 2400 baud has the same byte format, but each bit is represented in this
  way: 1 = half cycle @1200Hz; 0 = 1 cycle @2400Hz.
- Since it is not working, programs can be entered via the Paste facility.

TODO:
- Timings are basically screwed, it takes too much to load the POST but
  then the cursor blink is too fast
- keyboard MCU irq and data polling simulation should be inside a timer
  callback
- MN1544 4-bit CPU core and ROM dump
--- Character Generator ROM
--- Keyboard
--- Joysticks
--- Cassette baud switch
--- 128 bytes of RAM
- MN1271 device to be emulated
--- 4x 8-bit I/O ports
--- 3-bit port
--- timers
--- sound
--- cassette
--- serial / RS-232
--- interface between the 2 CPUs
- JR200 keyboard includes Kana characters, Kana On, Kana off, GRAPH
- JR200U keyboard omits Kana, but has GRAPH ON and GRAPH OFF instead.
- Keyboard matrix and decoding incomplete.
- The BREAK key on the unit is actually a soft reset.
- The Chargen interfaces to the MN1271, so that the text character
  definitions can be copied to D000-D7FF ram (via 0150-016F). The user can
  modify these characters with pokes. Since we have no information, some
  guesswork has been used to feed in the required info.
- Sound command never ends (SOUND 1000,50 should output 1000 Hz for 1 sec)
- Keyclick not working (POKE 0,64 should activate it)
- Cassette not implemented (needs MN1271 to work)
- Kana character 0xAC ('`) displays wrongly; chargen and manual conflict.

****************************************************************************/

#include "emu.h"

#include "cpu/m6800/m6800.h"
#include "sound/beep.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "utf8.h"


namespace {

class jr200_state : public driver_device
{
public:
	jr200_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_vram(*this, "vram")
		, m_cram(*this, "cram")
		, m_mn1271_ram(*this, "mn1271_ram")
		, m_maincpu(*this, "maincpu")
		, m_beeper(*this, "beeper")
		, m_pcg1(*this, "pcg1")
		, m_pcg2(*this, "pcg2")
		, m_gfx_rom(*this, "gfx_rom")
		, m_gfx_ram(*this, "gfx_ram")
		, m_io_keyboard(*this, "X%d", 0U)
		, m_gfxdecode(*this, "gfxdecode")
		, m_palette(*this, "palette")
	{ }

	void jr200(machine_config &config);
	DECLARE_INPUT_CHANGED_MEMBER(nmi_button);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_shared_ptr<uint8_t> m_vram;
	required_shared_ptr<uint8_t> m_cram;
	required_shared_ptr<uint8_t> m_mn1271_ram;
	uint8_t m_border_col = 0;
	uint8_t m_old_keydata = 0;
	uint8_t m_freq_reg[2]{};
	u16 m_autorepeat = 0;
	u8 m_port_ctr = 0;
	int m_port_cnt = 0;
	emu_timer *m_timer_d = nullptr;
	uint8_t mcu_keyb_r();
	void unknown_port_w(u8);
	void jr200_beep_w(uint8_t data);
	void jr200_beep_freq_w(offs_t offset, uint8_t data);
	void jr200_border_col_w(uint8_t data);
	uint8_t mn1271_io_r(offs_t offset);
	void mn1271_io_w(offs_t offset, uint8_t data);
	uint32_t screen_update_jr200(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	TIMER_CALLBACK_MEMBER(timer_d_callback);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<beep_device> m_beeper;
	required_shared_ptr<uint8_t> m_pcg1;
	required_shared_ptr<uint8_t> m_pcg2;
	required_region_ptr<u8> m_gfx_rom;
	required_shared_ptr<uint8_t> m_gfx_ram;
	required_ioport_array<11> m_io_keyboard;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<palette_device> m_palette;
};



/* TODO: double check this */
static const uint8_t jr200_keycodes[6][7][8] =
{
	/* unshifted */
	{
	{ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 },
	{ 0x39, 0x30, 0x2d, 0x5e, 0x08, 0x7f, 0x13, 0x5c },
	{ 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69 },
	{ 0x6f, 0x70, 0x40, 0x5b, 0x0d, 0x61, 0x73, 0x64 },
	{ 0x66, 0x67, 0x68, 0x6a, 0x6b, 0x6c, 0x3b, 0x3a },
	{ 0x5d, 0x7a, 0x78, 0x63, 0x76, 0x62, 0x6e, 0x6d },
	{ 0x2c, 0x2e, 0x2f, 0x20, 0x1e, 0x1d, 0x1f, 0x1c },
	},

	/* shifted */
	{
	{ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28 },
	{ 0x29, 0x20, 0x3d, 0x5f, 0x08, 0x7f, 0x13, 0x7c },
	{ 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49 },
	{ 0x4f, 0x50, 0x60, 0x7b, 0x0d, 0x41, 0x53, 0x44 },
	{ 0x46, 0x47, 0x48, 0x4a, 0x4b, 0x4c, 0x2b, 0x2a },
	{ 0x7d, 0x5a, 0x58, 0x43, 0x56, 0x42, 0x4e, 0x4d },
	{ 0x3c, 0x3e, 0x3f, 0x20, 0x1e, 0x1d, 0x1f, 0x1c },
	},

	/* graph on */
	{
	{ 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88 },
	{ 0x89, 0x30, 0xed, 0x8c, 0x08, 0x7f, 0x13, 0x8e },
	{ 0x98, 0x9b, 0x99, 0xec, 0xeb, 0x9a, 0xe9, 0x90 },
	{ 0x8d, 0xe0, 0xea, 0x5b, 0x0d, 0x91, 0x92, 0x93 },
	{ 0x94, 0x95, 0x96, 0x97, 0xef, 0xf0, 0x3b, 0x3a },
	{ 0x5d, 0xfa, 0xf8, 0xe3, 0xf6, 0xe2, 0xee, 0x8a },
	{ 0x2c, 0x2e, 0x2f, 0x20, 0x1e, 0x1d, 0x1f, 0x1c },
	},

	/* graph on shifted*/
	{
	{ 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88 },
	{ 0x89, 0x30, 0xed, 0x8c, 0x08, 0x7f, 0x13, 0x8e },
	{ 0x9e, 0xff, 0x9f, 0x9c, 0x8f, 0x9a, 0xe9, 0xfe },
	{ 0x9d, 0xfc, 0xea, 0x5b, 0x0d, 0xf1, 0xf7, 0xe5 },
	{ 0xf2, 0xf4, 0xf9, 0xf5, 0xfb, 0xfd, 0x3b, 0x3a },
	{ 0x5d, 0xe1, 0xf3, 0xe4, 0xe6, 0xe7, 0xe8, 0x8b },
	{ 0x2c, 0x2e, 0x2f, 0x20, 0x1e, 0x1d, 0x1f, 0x1c },
	},
	/* kana on */
	{
	{ 0xc7, 0xcc, 0xb1, 0xb3, 0xaa, 0xab, 0xac, 0xad },
	{ 0xae, 0xdc, 0xce, 0xcd, 0x08, 0x7f, 0x13, 0xb0 },
	{ 0xc0, 0xc3, 0xb2, 0xbd, 0xb6, 0xdd, 0xc5, 0xc6 },
	{ 0xd7, 0xbe, 0xde, 0xdf, 0x0d, 0xc1, 0xc4, 0xbc },
	{ 0xca, 0xb7, 0xb8, 0xcf, 0xc9, 0xd8, 0xda, 0xb9 },
	{ 0xd1, 0xaf, 0xbb, 0xbf, 0xcb, 0xba, 0xd0, 0xd3 },
	{ 0xc8, 0xd9, 0xd2, 0x20, 0x1e, 0x1d, 0x1f, 0x1c },
	},

	/* kana on shifted*/
	{
	{ 0xc7, 0xcc, 0xb1, 0xb3, 0xaa, 0xab, 0xac, 0xad },
	{ 0xae, 0xa6, 0xce, 0xcd, 0x08, 0x7f, 0x13, 0xb0 },
	{ 0xc0, 0xc3, 0xb2, 0xbd, 0xb6, 0xdd, 0xc5, 0xc6 },
	{ 0xd7, 0xbe, 0xde, 0xa2, 0x0d, 0xc1, 0xc4, 0xbc },
	{ 0xca, 0xb7, 0xb8, 0xcf, 0xc9, 0xd8, 0xda, 0xb9 },
	{ 0xa3, 0xaf, 0xbb, 0xbf, 0xcb, 0xba, 0xd0, 0xd3 },
	{ 0xa4, 0xa1, 0xa5, 0x20, 0x1e, 0x1d, 0x1f, 0x1c },
	}
};


uint32_t jr200_state::screen_update_jr200(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	bitmap.fill(m_border_col, cliprect);

	for (int y = 0; y < 24; y++)
	{
		for (int x = 0; x < 32; x++)
		{
			uint8_t tile = m_vram[x + y*32];
			uint8_t attr = m_cram[x + y*32];

			for(int yi=0;yi<8;yi++)
			{
				for(int xi=0;xi<8;xi++)
				{
					int pen = 0;
					if(attr & 0x80) //bitmap mode
					{
						/*
						    this mode draws 4 x 4 dot blocks, by combining lower 6 bits of tile and attribute vram

						    tile def
						    00xx x--- up-right
						    00-- -xxx up-left
						    attr def
						    10xx x--- down-right
						    10-- -xxx down-left
						*/
						u8 step = ((xi & 4) ? 3 : 0) + ((yi & 4) ? 6 : 0);

						pen = ((((attr & 0x3f) << 6) | (tile & 0x3f)) >> (step)) & 0x07;
					}
					else // tile mode
					{
						pen = BIT(m_gfx_ram[(tile*8)+yi], 7-xi) ? (attr & 0x7) : BIT(attr, 3, 3);
						if (BIT(attr, 6))
						{
							if ((tile >= 0x20) && (tile < 0x40))
							{
								tile &= 0x1f;
								pen = BIT(m_pcg1[(tile*8)+yi], 7-xi) ? (attr & 0x7) : BIT(attr, 3, 3);
							}
							else
							if ((tile >= 0x40) && (tile < 0x60))
							{
								tile &= 0x1f;
								pen = BIT(m_pcg2[(tile*8)+yi], 7-xi) ? (attr & 0x7) : BIT(attr, 3, 3);
							}
						}
					}

					bitmap.pix(y*8+yi+16, x*8+xi+16) = m_palette->pen(pen);
				}
			}
		}
	}

	return 0;
}


/*

I/O Device

*/

uint8_t jr200_state::mcu_keyb_r()
{
	if (m_port_ctr == 1)
		return m_gfx_rom[m_port_cnt];
	if (m_port_ctr == 2)
		return m_io_keyboard[10]->read();

	u8 modifiers = m_io_keyboard[7]->read();
	u8 table = 0, keydata = 0;
	u8 ret = 0;
	// KANA
	if (modifiers & 0x40)
		table = 4;
	// GRAPH
	if (modifiers & 0x10)
		table = 2;
	// SHIFT
	if (modifiers & 0x06)
		table ++;

	/* scan keyboard */
	for (u8 row = 0; row < 7; row++)
	{
		uint8_t data = m_io_keyboard[row]->read();

		for (u8 col = 0; col < 8; col++)
		{
			if (!BIT(data, col))
			{
				/* latch key data */
				keydata = jr200_keycodes[table][row][col];
				// LOCK
				if ((modifiers & 0x01) && ((keydata & 0xdf) > 0x40) && ((keydata & 0xdf) < 0x5b))
					keydata ^= 0x20;
				// CTRL
				if ((modifiers & 0x08) && (keydata > 0x40) && (keydata < 0x80))
					keydata &= 0x1f;
			}
		}
	}

	// Autorepeat handler
	// This might need to be done away with if games don't like it (should any be found)
	if (keydata && (m_old_keydata == keydata))
	{
		m_autorepeat++;
		if (m_autorepeat == 2) // initial keypress
			ret = keydata;
		else
		if (m_autorepeat == 0x330) // pause
		{
			ret = keydata;
			m_autorepeat = 0x2e0; // repeat speed (pause - this)
		}
	}
	else
	if (m_old_keydata != keydata)
	{
		// new key or none
		m_old_keydata = keydata;
		m_autorepeat = 0;
	}

	return ret;
}

void jr200_state::jr200_beep_w(uint8_t data)
{
	/* writing 0x0e enables the beeper, writing anything else disables it */
	m_beeper->set_state(((data & 0xf) == 0x0e) ? 1 : 0);
}

void jr200_state::jr200_beep_freq_w(offs_t offset, uint8_t data)
{
	m_freq_reg[offset] = data;

	u32 beep_freq = ((m_freq_reg[0]<<8) | (m_freq_reg[1] & 0xff)) + 1;

	if (beep_freq)
		m_beeper->set_clock(84000 / beep_freq);
	else
		m_beeper->set_clock(0);
}

void jr200_state::jr200_border_col_w(uint8_t data)
{
	m_border_col = data;
}


TIMER_CALLBACK_MEMBER(jr200_state::timer_d_callback)
{
	m_maincpu->set_input_line(0, HOLD_LINE);
}

// get data from chargen for bios to copy to D000-D7FF.
// After that, one more copy for the cassette dipswitch
void jr200_state::unknown_port_w(u8 data)
{
	if ((m_port_ctr == 0) && (data == 0x31))
	{
		m_port_ctr++;
		m_port_cnt = -1;
	}
	else
	if ((m_port_ctr == 1) && (data == 0x73))
	{
		m_port_cnt++;
		if (m_port_cnt == 0x800)
			m_port_ctr++;
	}
	else
	if ((m_port_ctr == 2) && (data == 0x73))
		m_port_ctr++;
}

uint8_t jr200_state::mn1271_io_r(offs_t offset)
{
	uint8_t retVal = m_mn1271_ram[offset];

	switch(offset+0xc800)
	{
		case 0xc801: retVal= mcu_keyb_r(); break;
		case 0xc803: retVal= (m_mn1271_ram[0x03] & 0xcf) | 0x30;  break;//---x ---- printer status ready (ACTIVE HIGH)
		case 0xc807: retVal= (m_mn1271_ram[0x07] & 0x80) | 0x60; break;
		case 0xc80a: retVal= (m_mn1271_ram[0x0a] & 0xfe); break;
		case 0xc80c: retVal= (m_mn1271_ram[0x0c] & 0xdf) | 0x20; break;
		case 0xc80e: retVal= 0; break;
		case 0xc810: retVal= 0; break;
		case 0xc816: retVal= 0x4e; break;
		case 0xc81c: retVal= (m_mn1271_ram[0x1c] & 0xfe) | 1;  break;//bit 0 needs to be high otherwise system refuses to boot
		case 0xc81d: retVal= (m_port_ctr == 3) ? (m_mn1271_ram[0x1d] & 0xed) : 1; break;
	}
	//logerror("mn1271_io_r [%04x] = %02x\n",offset+0xc800,retVal);
	return retVal;
}

void jr200_state::mn1271_io_w(offs_t offset, uint8_t data)
{
	m_mn1271_ram[offset] = data;//printf("%X=%X ",offset,data);
	switch(offset+0xc800)
	{
		case 0xc803: unknown_port_w(data); break;
		case 0xc805: break; //LPT printer port W
		case 0xc816: if (data!=0) {
					m_timer_d->adjust(attotime::zero, 0, attotime::from_hz(XTAL(14'318'181)) * (m_mn1271_ram[0x17]*0x100 + m_mn1271_ram[0x18]));
				} else {
					m_timer_d->adjust(attotime::zero, 0,  attotime::zero);
				}
				break;
		case 0xc819: jr200_beep_w(data); break;
		case 0xc81a:
		case 0xc81b: jr200_beep_freq_w(offset-0x1a,data); break;
	}
}

void jr200_state::mem_map(address_map &map)
{
/*
    0000-3fff RAM
    4000-4fff RAM ( 4k expansion)
    4000-7fff RAM (16k expansion)
    4000-bfff RAM (32k expansion)
*/
	// 32K RAM
	map(0x0000, 0x7fff).ram();
	// BASIC ROM
	map(0xa000, 0xbfff).rom();
	// PCG 1
	map(0xc000, 0xc0ff).ram().share("pcg1");
	// Videoram
	map(0xc100, 0xc3ff).ram().share("vram");
	// PCG 2
	map(0xc400, 0xc4ff).ram().share("pcg2");
	// Attribute RAM
	map(0xc500, 0xc7ff).ram().share("cram");
	// MN1271 PIA/timer
	map(0xc800, 0xc81f).mirror(0x01e0).rw(FUNC(jr200_state::mn1271_io_r), FUNC(jr200_state::mn1271_io_w)).share("mn1271_ram");
	// YLHSD61K201F (or HD61K201F) CRTC
	map(0xca00, 0xcbff).w(FUNC(jr200_state::jr200_border_col_w));
	// External I/O area
	map(0xcc00, 0xcfff);
	// RAM-based normal characters
	map(0xd000, 0xd7ff).ram().share("gfx_ram");
	// System extension (presumably via the External Bus Connector - there's no "cart" slot)
	map(0xd800, 0xdfff).rom();
	// BIOS
	map(0xe000, 0xffff).rom();
}

/* Input ports */
static INPUT_PORTS_START( jr200 )
//  PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("screen", FUNC(screen_device::vblank))

	PORT_START("X0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR(0x27)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')

	PORT_START("X1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') PORT_CHAR('_')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RUBOUT") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INS") PORT_CODE(KEYCODE_INSERT)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("YEN") PORT_CODE(KEYCODE_HOME) PORT_CHAR(0x5c) PORT_CHAR(0x7c)

	PORT_START("X2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')

	PORT_START("X3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@') PORT_CHAR(0x60)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')

	PORT_START("X4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')

	PORT_START("X5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')

	PORT_START("X6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

	PORT_START("X7")
	// This key does not exist, it's for our convenience only
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("LEFT SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RIGHT SHIFT") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL)
	// on JR200U there's GRAPH ON and GRAPH OFF keys
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("GRAPH") PORT_CODE(KEYCODE_RCONTROL)
	// Kana is on JR200 only
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("KANA OFF") PORT_CODE(KEYCODE_LALT)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("KANA ON") PORT_CODE(KEYCODE_RALT)
	// This key does a soft reset, so needs a proper NMI handler
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("BREAK") PORT_CODE(KEYCODE_PGDN) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(jr200_state::nmi_button), 0)

	PORT_START("X8")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED ) // reserved for Joystick 1

	PORT_START("X9")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED ) // reserved for Joystick 2

	PORT_START("X10")
	PORT_DIPNAME( 0x01, 0x00, "Cassette Baud")  // mounted on the underside of the unit
	PORT_DIPSETTING(    0x00, "2400")
	PORT_DIPSETTING(    0x01, "600")
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(jr200_state::nmi_button)
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, newval ? ASSERT_LINE : CLEAR_LINE);
}

static const gfx_layout tiles8x8_layout =
{
	8,8,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7, 8 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};

static GFXDECODE_START( gfx_jr200 )
	GFXDECODE_RAM( "gfx_ram", 0, tiles8x8_layout, 0, 1 )
	GFXDECODE_RAM( "pcg1", 0, tiles8x8_layout, 0, 1 )
	GFXDECODE_RAM( "pcg2", 0, tiles8x8_layout, 0, 1 )
GFXDECODE_END

void jr200_state::machine_start()
{
	m_timer_d = timer_alloc(FUNC(jr200_state::timer_d_callback), this);
	save_item(NAME(m_border_col));
	save_item(NAME(m_old_keydata));
	save_item(NAME(m_freq_reg));
	save_item(NAME(m_autorepeat));
	save_item(NAME(m_port_ctr));
	save_item(NAME(m_port_cnt));
}

void jr200_state::machine_reset()
{
	m_autorepeat = 0;
	m_old_keydata = 0;
	m_port_ctr = 0;
	m_port_cnt = 0;
	memset(m_mn1271_ram,0,0x20);
}


void jr200_state::jr200(machine_config &config)
{
	/* basic machine hardware */
	M6808(config, m_maincpu, XTAL(14'318'181) / 4); /* MN1800A, ? MHz assumption that it is same as JR-100*/
	m_maincpu->set_addrmap(AS_PROGRAM, &jr200_state::mem_map);

//  MN1544(config, "mn1544", ?);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(16 + 256 + 16, 16 + 192 + 16); /* border size not accurate */
	screen.set_visarea(0, 16 + 256 + 16 - 1, 0, 16 + 192 + 16 - 1);
	screen.set_screen_update(FUNC(jr200_state::screen_update_jr200));
	screen.set_palette(m_palette);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_jr200);
	PALETTE(config, m_palette, palette_device::BRG_3BIT);

	SPEAKER(config, "mono").front_center();

	// All sounds are produced by the MN1271

	BEEP(config, m_beeper, 0).add_route(ALL_OUTPUTS,"mono",0.50);
}



/* ROM definition */
ROM_START( jr200 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "rom1.bin",   0xa000, 0x2000, CRC(bfed707b) SHA1(551823e7ca63f459eb46eb4c7a3e1e169fba2ca2))
	ROM_LOAD( "rom2.bin",   0xe000, 0x2000, CRC(a1cb5027) SHA1(5da98d4ce9cba8096d98e6f2de60baa1673406d0))

	ROM_REGION( 0x10000, "mn1544", ROMREGION_ERASEFF )
	ROM_LOAD( "mn1544.bin",  0x0000, 0x0400, NO_DUMP )

	ROM_REGION( 0x0800, "gfx_rom", ROMREGION_ERASEFF )
	ROM_LOAD( "char.rom", 0x0000, 0x0800, CRC(cb641624) SHA1(6fe890757ebc65bbde67227f9c7c490d8edd84f2) )
ROM_END

ROM_START( jr200u )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "basic.rom",  0xa000, 0x2000, CRC(cc53eb52) SHA1(910927b98a8338ba072173d79613422a8cb796da) )
	ROM_LOAD( "jr200u.bin", 0xe000, 0x2000, CRC(37ca3080) SHA1(17d3fdedb4de521da7b10417407fa2b61f01a77a) )

	ROM_REGION( 0x10000, "mn1544", ROMREGION_ERASEFF )
	ROM_LOAD( "mn1544.bin",  0x0000, 0x0400, NO_DUMP )

	ROM_REGION( 0x0800, "gfx_rom", ROMREGION_ERASEFF )
	ROM_LOAD( "char.rom", 0x0000, 0x0800, CRC(cb641624) SHA1(6fe890757ebc65bbde67227f9c7c490d8edd84f2) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY      FULLNAME   FLAGS
COMP( 1982, jr200,  0,      0,      jr200,   jr200, jr200_state, empty_init, "National",  "JR-200",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
COMP( 1982, jr200u, jr200,  0,      jr200,   jr200, jr200_state, empty_init, "Panasonic", "JR-200U", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



jtc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder, Robbbert
/***************************************************************************************

Jugend+Technik CompJU+TEr

2009-07-15 Skeleton driver.

2018-09: Made mostly working

To Do:
- Figure out how to use the so-called "Basic", all documents are in German.
- Fix any remaining CPU bugs
- On jtces40, the use of ALT key will usually freeze the system. Normal, or a bug?
- On jtces40, no backspace?
- On jtces40, is there a way to type lower case?
- On jtces40, hires gfx and colours to fix.

****************************************************************************************/

#include "emu.h"
#include "bus/centronics/ctronics.h"
#include "cpu/z8/z8.h"
#include "imagedev/cassette.h"
#include "machine/ram.h"
#include "sound/spkrdev.h"
#include "imagedev/snapquik.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"
#include "utf8.h"


namespace {

#define UB8830D_TAG     "ub8830d"
#define CENTRONICS_TAG  "centronics"

#define JTC_ES40_VIDEORAM_SIZE  0x2000

class jtc_state : public driver_device
{
public:
	jtc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, UB8830D_TAG)
		, m_ram(*this, RAM_TAG)
		, m_cassette(*this, "cassette")
		, m_speaker(*this, "speaker")
		, m_centronics(*this, CENTRONICS_TAG)
		, m_video_ram(*this, "videoram")
	{ }

	void basic(machine_config &config);
	void jtc(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	void p2_w(u8 data);
	u8 p3_r();
	void p3_w(u8 data);
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);
	int m_centronics_busy = 0;
	void write_centronics_busy(int state);
	required_device<z8_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<cassette_image_device> m_cassette;
	required_device<speaker_sound_device> m_speaker;
	required_device<centronics_device> m_centronics;
	optional_shared_ptr<u8> m_video_ram;

private:
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void jtc_mem(address_map &map) ATTR_COLD;
};

class jtces88_state : public jtc_state
{
public:
	using jtc_state::jtc_state;
	void jtces88(machine_config &config);
};


class jtces23_state : public jtc_state
{
public:
	using jtc_state::jtc_state;
	void jtces23(machine_config &config);
private:
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void jtc_es23_mem(address_map &map) ATTR_COLD;
};


class jtces40_state : public jtc_state
{
public:
	jtces40_state(const machine_config &mconfig, device_type type, const char *tag)
		: jtc_state(mconfig, type, tag)
		, m_video_ram_40(*this, "videoram40", JTC_ES40_VIDEORAM_SIZE, ENDIANNESS_BIG)
		, m_color_ram_r(*this, "color_ram_r", JTC_ES40_VIDEORAM_SIZE, ENDIANNESS_BIG)
		, m_color_ram_g(*this, "color_ram_g", JTC_ES40_VIDEORAM_SIZE, ENDIANNESS_BIG)
		, m_color_ram_b(*this, "color_ram_b", JTC_ES40_VIDEORAM_SIZE, ENDIANNESS_BIG)
	{ }
	void jtces40(machine_config &config);
private:
	virtual void video_start() override ATTR_COLD;
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	u8 videoram_r(offs_t offset);
	void videoram_w(offs_t offset, u8 data);
	void banksel_w(u8 data);
	u8 m_video_bank = 0U;

	memory_share_creator<uint8_t> m_video_ram_40;
	memory_share_creator<uint8_t> m_color_ram_r;
	memory_share_creator<uint8_t> m_color_ram_g;
	memory_share_creator<uint8_t> m_color_ram_b;
	void jtc_es40_mem(address_map &map) ATTR_COLD;
	void es40_palette(palette_device &palette) const;
};


/* Read/Write Handlers */

void jtc_state::p2_w(u8 data)
{
	/*

	    bit     description

	    P20
	    P21
	    P22
	    P23
	    P24
	    P25     centronics strobe output
	    P26     V4093 pins 1,2
	    P27     DL299 pin 18

	*/

	m_centronics->write_strobe(BIT(data, 5));
}

void jtc_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

u8 jtc_state::p3_r()
{
	/*

	    bit     description

	    P30     tape input
	    P31
	    P32
	    P33     centronics busy input

	*/

	u8 data = 0;

	data |= ((m_cassette)->input() < 0.0) ? 1 : 0;
	data |= m_centronics_busy << 3;

	return data;
}

void jtc_state::p3_w(u8 data)
{
	/*

	    bit     description

	    P34
	    P35
	    P36     tape output
	    P37     speaker output

	*/

	/* tape */
	if (BIT(data, 7))
		m_cassette->output( BIT(data, 6) ? +1.0 : -1.0);
	else
	/* speaker */
		m_speaker->level_w(BIT(data, 6));
}

u8 jtces40_state::videoram_r(offs_t offset)
{
	u8 data = 0;

	if (BIT(m_video_bank, 7)) data |= m_color_ram_r[offset];
	if (BIT(m_video_bank, 6)) data |= m_color_ram_g[offset];
	if (BIT(m_video_bank, 5)) data |= m_color_ram_b[offset];
	if (BIT(m_video_bank, 4)) data |= m_video_ram[offset];

	return data;
}

void jtces40_state::videoram_w(offs_t offset, u8 data)
{
	if (BIT(m_video_bank, 7)) m_color_ram_r[offset] = data;
	if (BIT(m_video_bank, 6)) m_color_ram_g[offset] = data;
	if (BIT(m_video_bank, 5)) m_color_ram_b[offset] = data;
	if (BIT(m_video_bank, 4)) m_video_ram_40[offset] = data;
}

void jtces40_state::banksel_w(u8 data)
{
	m_video_bank = data;
}

/* Memory Maps */

void jtc_state::jtc_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0800, 0x0fff).rom();
	map(0x2000, 0x27ff).rom();
	map(0x7001, 0x7001).mirror(0x0ff0).portr("Y1");
	map(0x7002, 0x7002).mirror(0x0ff0).portr("Y2");
	map(0x7003, 0x7003).mirror(0x0ff0).portr("Y3");
	map(0x7004, 0x7004).mirror(0x0ff0).portr("Y4");
	map(0x7005, 0x7005).mirror(0x0ff0).portr("Y5");
	map(0x7006, 0x7006).mirror(0x0ff0).portr("Y6");
	map(0x7007, 0x7007).mirror(0x0ff0).portr("Y7");
	map(0x7008, 0x7008).mirror(0x0ff0).portr("Y8");
	map(0x7009, 0x7009).mirror(0x0ff0).portr("Y9");
	map(0x700a, 0x700a).mirror(0x0ff0).portr("Y10");
	map(0x700b, 0x700b).mirror(0x0ff0).portr("Y11");
	map(0x700c, 0x700c).mirror(0x0ff0).portr("Y12");
	map(0x700d, 0x700d).mirror(0x0ff0).portr("Y13");
	map(0x700e, 0x700e).mirror(0x0ff0).portr("Y14");
	map(0x700f, 0x700f).mirror(0x0ff0).portr("Y15");
	map(0xe000, 0xfdff).ram();
	map(0xfe00, 0xffff).ram().share("videoram");
}

void jtces23_state::jtc_es23_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0800, 0x17ff).rom();
	map(0x7000, 0x7000).mirror(0x0ff0).portr("Y0");
	map(0x7001, 0x7001).mirror(0x0ff0).portr("Y1");
	map(0x7002, 0x7002).mirror(0x0ff0).portr("Y2");
	map(0x7003, 0x7003).mirror(0x0ff0).portr("Y3");
	map(0x7004, 0x7004).mirror(0x0ff0).portr("Y4");
	map(0x7005, 0x7005).mirror(0x0ff0).portr("Y5");
	map(0x7006, 0x7006).mirror(0x0ff0).portr("Y6");
	map(0x7007, 0x7007).mirror(0x0ff0).portr("Y7");
	map(0x7008, 0x7008).mirror(0x0ff0).portr("Y8");
	map(0x7009, 0x7009).mirror(0x0ff0).portr("Y9");
	map(0x700a, 0x700a).mirror(0x0ff0).portr("Y10");
	map(0x700b, 0x700b).mirror(0x0ff0).portr("Y11");
	map(0x700c, 0x700c).mirror(0x0ff0).portr("Y12");
	map(0x700d, 0x700d).mirror(0x0ff0).portr("Y13");
	map(0x700e, 0x700e).mirror(0x0ff0).portr("Y14");
	map(0x700f, 0x700f).mirror(0x0ff0).portr("Y15");
	map(0xe000, 0xf7ff).ram();
	map(0xf800, 0xffff).ram().share("videoram");
}

void jtces40_state::jtc_es40_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0800, 0x1fff).rom();
	map(0x4000, 0x5fff).rw(FUNC(jtces40_state::videoram_r), FUNC(jtces40_state::videoram_w));
	map(0x6000, 0x63ff).w(FUNC(jtces40_state::banksel_w));
	map(0x7000, 0x7000).mirror(0x0ff0).portr("Y0");
	map(0x7001, 0x7001).mirror(0x0ff0).portr("Y1");
	map(0x7002, 0x7002).mirror(0x0ff0).portr("Y2");
	map(0x7003, 0x7003).mirror(0x0ff0).portr("Y3");
	map(0x7004, 0x7004).mirror(0x0ff0).portr("Y4");
	map(0x7005, 0x7005).mirror(0x0ff0).portr("Y5");
	map(0x7006, 0x7006).mirror(0x0ff0).portr("Y6");
	map(0x7007, 0x7007).mirror(0x0ff0).portr("Y7");
	map(0x7008, 0x7008).mirror(0x0ff0).portr("Y8");
	map(0x7009, 0x7009).mirror(0x0ff0).portr("Y9");
	map(0x700a, 0x700a).mirror(0x0ff0).portr("Y10");
	map(0x700b, 0x700b).mirror(0x0ff0).portr("Y11");
	map(0x700c, 0x700c).mirror(0x0ff0).portr("Y12");
	map(0x700d, 0x700d).mirror(0x0ff0).portr("Y13");
	map(0x700e, 0x700e).mirror(0x0ff0).portr("Y14");
	map(0x700f, 0x700f).mirror(0x0ff0).portr("Y15");
	map(0x8000, 0xffff).ram();//BANK(1)
}

/* Input Ports */

static INPUT_PORTS_START( jtc )
	PORT_START("Y1")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("\xE2\x86\x91 \xE2\x86\x93") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("\xE2\x86\x90 \xE2\x86\x92") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("Y2")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')

	PORT_START("Y3")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')

	PORT_START("Y4")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')

	PORT_START("Y5")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('?')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')

	PORT_START("Y6")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('*')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')

	PORT_START("Y7")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('%')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')

	PORT_START("Y8")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR(0x2030) // per mille
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')

	PORT_START("Y9")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("Y10")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')

	PORT_START("Y11")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR('*') PORT_CHAR(':')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("Y12")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CLR") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('+') PORT_CHAR(';')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)

	PORT_START("Y13")
	PORT_START("Y14")
	PORT_START("Y15")
INPUT_PORTS_END

static INPUT_PORTS_START( jtces23 )
	PORT_START("Y0")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CONTROL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("Y1")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')

	PORT_START("Y2")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')

	PORT_START("Y3")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')

	PORT_START("Y4")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')

	PORT_START("Y5")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')

	PORT_START("Y6")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')

	PORT_START("Y7")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')

	PORT_START("Y8")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('@')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('[')

	PORT_START("Y9")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(']')

	PORT_START("Y10")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("Y11")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('<')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('+') PORT_CHAR('\\')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RET") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)

	PORT_START("Y12")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('>')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) //PORT_CODE() PORT_CHAR('=')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) //PORT_CODE() PORT_CHAR('*') PORT_CHAR('^')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("Y13")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // DBS
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // DEL
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // SPA

	PORT_START("Y14")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // INS
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // HOM
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))

	PORT_START("Y15")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // CLS
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // SOL
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // RET
INPUT_PORTS_END

static INPUT_PORTS_START( jtces40 )
	PORT_START("Y0")

	PORT_START("Y1")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SHT3") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SHT2") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SHT1") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("Y2")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')

	PORT_START("Y3")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')

	PORT_START("Y4")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')

	PORT_START("Y5")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')

	PORT_START("Y6")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')

	PORT_START("Y7")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')

	PORT_START("Y8")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')

	PORT_START("Y9")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('@')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('[') PORT_CHAR('{')

	PORT_START("Y10")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(']') PORT_CHAR('}')

	PORT_START("Y11")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') PORT_CHAR('=') PORT_CHAR('_')

	PORT_START("Y12")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('<') PORT_CHAR('>') PORT_CHAR('^')
	// can't find this: PORT_NAME("CLR") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('+') PORT_CHAR('-') PORT_CHAR('\\')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('*') PORT_CHAR('/') PORT_CHAR('|')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RET") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_CHAR('?') PORT_CHAR('~')

	PORT_START("Y13")
	PORT_START("Y14")
	PORT_START("Y15")
INPUT_PORTS_END

QUICKLOAD_LOAD_MEMBER(jtc_state::quickload_cb)
{
	address_space &space = m_maincpu->space(AS_PROGRAM);
	u16 quick_addr;
	std::vector<u8> quick_data;

	int quick_length = image.length();
	if (image.is_filetype("jtc"))
	{
		if (quick_length < 0x0088)
			return std::make_pair(image_error::INVALIDLENGTH, "File too short");
		else if (quick_length > 0x8000)
			return std::make_pair(image_error::INVALIDLENGTH, "File too long");

		quick_data.resize(quick_length+1);
		int const read_ = image.fread(&quick_data[0], quick_length);
		if (read_ != quick_length)
			return std::make_pair(image_error::UNSPECIFIED, "Cannot read the file");

		quick_addr = quick_data[0x12] * 256 + quick_data[0x11];
		quick_length = quick_data[0x14] * 256 + quick_data[0x13] - quick_addr + 0x81;
		if (image.length() != quick_length)
			return std::make_pair(image_error::INVALIDIMAGE, "Invalid file header");

		for (int i = 0x80; i < image.length(); i++)
			space.write_byte(quick_addr+i-0x80, quick_data[i]);

		// display a message about the loaded quickload
		image.message(" Quickload: size=%04X : loaded at %04X",quick_length,quick_addr);

		return std::make_pair(std::error_condition(), std::string());
	}
	else if (image.is_filetype("bin"))
	{
		quick_addr = 0xe000;
		if (quick_length > 0x8000)
			return std::make_pair(image_error::INVALIDLENGTH, "File too long");

		quick_data.resize(quick_length+1);
		u16 read_ = image.fread( &quick_data[0], quick_length);
		if (read_ != quick_length)
			return std::make_pair(image_error::UNSPECIFIED, "Cannot read the file");

		for (int i = 0; i < image.length(); i++)
			space.write_byte(quick_addr+i, quick_data[i]);

		// display a message about the loaded quickload
		image.message(" Quickload: size=%04X : loaded at %04X",quick_length,quick_addr);

		m_maincpu->set_pc(quick_addr);

		return std::make_pair(std::error_condition(), std::string());
	}

	return std::make_pair(image_error::UNSUPPORTED, std::string());
}


/* Video */

u32 jtc_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (u8 y = 0; y < 64; y++)
	{
		for (u8 sx = 0; sx < 8; sx++)
		{
			u8 const data = m_video_ram[(y * 8) + sx];

			for (u8 i = 0; i < 8; i++)
				bitmap.pix(y, (sx * 8) + i) = BIT(data, 7-i);
		}
	}

	return 0;
}

u32 jtces23_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (u8 y = 0; y < 128; y++)
	{
		for (u8 sx = 0; sx < 16; sx++)
		{
			u8 const data = m_video_ram[(y * 16) + sx];

			for (u8 i = 0; i < 8; i++)
				bitmap.pix(y, (sx * 8) + i) = BIT(data, 7-i);
		}
	}

	return 0;
}

void jtces40_state::es40_palette(palette_device &palette) const
{
	for (u8 i = 8; i < 16; i++)
		palette.set_pen_color(i, rgb_t(BIT(i, 0) ? 0xc0 : 0, BIT(i, 1) ? 0xc0 : 0, BIT(i, 2) ? 0xc0 : 0));
}

void jtces40_state::video_start()
{
	/* register for state saving */
	save_item(NAME(m_video_bank));
	save_item(NAME(m_centronics_busy));
}

u32 jtces40_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 ma = 0;

	for (int y = 0; y < 64; y++)
	{
		for (int z = 0; z < 3; z++)
		{
			for (int x = 0; x < 40; x++)
			{
				u8 const data = m_video_ram_40[ma + x];
				u8 const r = ~m_color_ram_r[ma + x];
				u8 const g = ~m_color_ram_g[ma + x];
				u8 const b = ~m_color_ram_b[ma + x];

				for (int i = 0; i < 8; i++)
					bitmap.pix(y*3+z, (x * 8) + 7 - i) = (BIT(r, i) << 0) | (BIT(g, i) << 1) | (BIT(b, i) << 2) | (BIT(data, i) << 3);
			}
			ma+=40;
		}
		ma+=8;
	}

	return 0;
}

/* Machine Initialization */

void jtc_state::machine_start()
{
	/* register for state saving */
}

/* Machine Driver */

/* F4 Character Displayer */
static const gfx_layout jtces23_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	64,                 /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static const gfx_layout jtces40_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	128,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_jtces23 )
	GFXDECODE_ENTRY( UB8830D_TAG, 0x1000, jtces23_charlayout, 0, 1 )
GFXDECODE_END

static GFXDECODE_START( gfx_jtces40 )
	GFXDECODE_ENTRY( UB8830D_TAG, 0x1000, jtces40_charlayout, 0, 8 )
GFXDECODE_END

void jtc_state::basic(machine_config &config)
{
	/* basic machine hardware */
	UB8830D(config, m_maincpu, XTAL(8'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &jtc_state::jtc_mem);
	m_maincpu->p2_out_cb().set(FUNC(jtc_state::p2_w));
	m_maincpu->p3_in_cb().set(FUNC(jtc_state::p3_r));
	m_maincpu->p3_out_cb().set(FUNC(jtc_state::p3_w));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	/* cassette */
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	/* printer */
	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(jtc_state::write_centronics_busy));

	/* quickload */
	QUICKLOAD(config, "quickload", "jtc,bin", attotime::from_seconds(2)).set_load_callback(FUNC(jtc_state::quickload_cb));
}

void jtc_state::jtc(machine_config &config)
{
	basic(config);
	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(jtc_state::screen_update));
	screen.set_size(64, 64);
	screen.set_visarea(0, 64-1, 0, 64-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* internal ram */
	RAM(config, m_ram).set_default_size("2K");
}

void jtces88_state::jtces88(machine_config &config)
{
	jtc(config);

	/* internal ram */
	m_ram->set_default_size("4K");
}

void jtces23_state::jtces23(machine_config &config)
{
	basic(config);
	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &jtces23_state::jtc_es23_mem);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(jtces23_state::screen_update));
	screen.set_size(128, 128);
	screen.set_visarea(0, 128-1, 0, 128-1);
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_jtces23);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("4K");
}

void jtces40_state::jtces40(machine_config &config)
{
	basic(config);
	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &jtces40_state::jtc_es40_mem);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(jtces40_state::screen_update));
	screen.set_size(320, 192);
	screen.set_visarea(0, 320-1, 0, 192-1);
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_jtces40);
	PALETTE(config, "palette", FUNC(jtces40_state::es40_palette), 16);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("8K").set_extra_options("16K,32K");
}

/* ROMs */

ROM_START( jtc )
	ROM_REGION( 0x2800, UB8830D_TAG, 0 )
	ROM_LOAD( "u883rom.bin", 0x0000, 0x0800, CRC(2453c8c1) SHA1(816f5d08f8064b69b1779eb6661fde091aa58ba8) )
	ROM_LOAD( "os2k_0800.bin", 0x0800, 0x0800, CRC(c81a2e19) SHA1(97c3b36c7b555081e084403e8f800fc9dbf5e68d) ) // u2716c1.bin
	ROM_LOAD( "u2716c2.bin", 0x2000, 0x0800, NO_DUMP ) // doesn't seem to be needed?
ROM_END

ROM_START( jtces88 )
	ROM_REGION( 0x2800, UB8830D_TAG, 0 )
	ROM_LOAD( "u883rom.bin", 0x0000, 0x0800, CRC(2453c8c1) SHA1(816f5d08f8064b69b1779eb6661fde091aa58ba8) )
	ROM_LOAD( "es1988_0800.bin", 0x0800, 0x0800, CRC(af3e882f) SHA1(65af0d0f5f882230221e9552707d93ed32ba794d) )
	ROM_LOAD( "es1988_2000.bin", 0x2000, 0x0800, CRC(5ff87c1e) SHA1(fbd2793127048bd9706970b7bce84af2cb258dc5) )
ROM_END

ROM_START( jtces23 )
	ROM_REGION( 0x2800, UB8830D_TAG, 0 )
	ROM_LOAD( "u883rom.bin", 0x0000, 0x0800, CRC(2453c8c1) SHA1(816f5d08f8064b69b1779eb6661fde091aa58ba8) )
	ROM_LOAD( "es23_0800.bin", 0x0800, 0x1000, CRC(16128b64) SHA1(90fb0deeb5660f4a2bb38d51981cc6223d5ddf6b) )
ROM_END

ROM_START( jtces40 )
	ROM_REGION( 0x2800, UB8830D_TAG, 0 )
	ROM_LOAD( "u883rom.bin", 0x0000, 0x0800, CRC(2453c8c1) SHA1(816f5d08f8064b69b1779eb6661fde091aa58ba8) )
	ROM_LOAD( "es40_0800.bin", 0x0800, 0x1800, CRC(770c87ce) SHA1(1a5227ba15917f2a572cb6c27642c456f5b32b90) )
ROM_END

} // anonymous namespace


/* System Drivers */

/*    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY           FULLNAME                    FLAGS */
COMP( 1987, jtc,     0,      0,      jtc,     jtc,     jtc_state,     empty_init, "Jugend+Technik", "CompJU+TEr",               MACHINE_SUPPORTS_SAVE )
COMP( 1988, jtces88, jtc,    0,      jtces88, jtc,     jtces88_state, empty_init, "Jugend+Technik", "CompJU+TEr (EMR-ES 1988)", MACHINE_SUPPORTS_SAVE )
COMP( 1989, jtces23, jtc,    0,      jtces23, jtces23, jtces23_state, empty_init, "Jugend+Technik", "CompJU+TEr (ES 2.3)",      MACHINE_SUPPORTS_SAVE )
COMP( 1990, jtces40, jtc,    0,      jtces40, jtces40, jtces40_state, empty_init, "Jugend+Technik", "CompJU+TEr (ES 4.0)",      MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )



juicebox.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Tim Schuerewegen
/*******************************************************************************

    Mattel Juice Box

    (C) 2011 Tim Schuerewegen

*******************************************************************************/

#include "emu.h"
#include "cpu/arm7/arm7.h"
#include "machine/s3c44b0.h"
#include "machine/smartmed.h"
#include "sound/dac.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include <cstdarg>


namespace {

//#define JUICEBOX_ENTER_DEBUG_MENU
//#define JUICEBOX_DISPLAY_ROM_ID

#define VERBOSE_LEVEL ( 0 )

class juicebox_state : public driver_device
{
public:
	juicebox_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_s3c44b0(*this, "s3c44b0")
		, m_smartmedia(*this, "smartmedia")
		, m_port_g(*this, "PORTG")
	{ }

	void juicebox(machine_config &config);

	void init_juicebox();

	DECLARE_INPUT_CHANGED_MEMBER(port_changed);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<s3c44b0_device> m_s3c44b0;
	required_device<smartmedia_image_device> m_smartmedia;
	required_ioport m_port_g;

	uint32_t port[9];

	struct jb_smc_t
	{
		int add_latch;
		int cmd_latch;
		int busy;
	};

	jb_smc_t smc;

	#if defined(JUICEBOX_ENTER_DEBUG_MENU) || defined(JUICEBOX_DISPLAY_ROM_ID)
	int port_g_read_count;
	#endif
	uint32_t juicebox_nand_r(offs_t offset, uint32_t mem_mask = ~0);
	void juicebox_nand_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	inline void verboselog(int n_level, const char *s_fmt, ...) ATTR_PRINTF(3,4);
	void smc_reset();
	void smc_init();
	uint8_t smc_read();
	void smc_write(uint8_t data);
	uint32_t s3c44b0_gpio_port_r(offs_t offset);
	void s3c44b0_gpio_port_w(offs_t offset, uint32_t data);
	//void s3c44b0_i2s_data_w(offs_t offset, uint16_t data);
	void juicebox_map(address_map &map) ATTR_COLD;
};

inline void juicebox_state::verboselog(int n_level, const char *s_fmt, ...)
{
	if (VERBOSE_LEVEL >= n_level)
	{
		va_list v;
		char buf[32768];
		va_start( v, s_fmt);
		vsprintf( buf, s_fmt, v);
		va_end( v);
		logerror( "%s: %s", machine().describe_context(), buf);
	}
}


/***************************************************************************
    MACHINE HARDWARE
***************************************************************************/


// SMARTMEDIA

void juicebox_state::smc_reset( )
{
	verboselog(5, "smc_reset\n");
	smc.add_latch = 0;
	smc.cmd_latch = 0;
	smc.busy = 0;
}

void juicebox_state::smc_init( )
{
	verboselog(5, "smc_init\n");
	smc_reset();
}

uint8_t juicebox_state::smc_read( )
{
	uint8_t data;
	if (m_smartmedia->is_present())
	{
		data = m_smartmedia->data_r();
	}
	else
	{
		data = 0xFF;
	}
	verboselog(5, "smc_read %08X\n", data);
	return data;
}

void juicebox_state::smc_write( uint8_t data)
{
	verboselog(5, "smc_write %08X\n", data);
	if (m_smartmedia->is_present())
	{
		if (smc.cmd_latch)
		{
			verboselog(5, "smartmedia_command_w %08X\n", data);
			m_smartmedia->command_w(data);
		}
		else if (smc.add_latch)
		{
			verboselog(5, "smartmedia_address_w %08X\n", data);
			m_smartmedia->address_w(data);
		}
		else
		{
			verboselog(5, "smartmedia_data_w %08X\n", data);
			m_smartmedia->data_w(data);
		}
	}
}

uint32_t juicebox_state::s3c44b0_gpio_port_r(offs_t offset)
{
	uint32_t data = port[offset];
	switch (offset)
	{
		case S3C44B0_GPIO_PORT_A :
		{
			data = 0x00000210;
		}
		break;
		case S3C44B0_GPIO_PORT_B :
		{
			data = 0x00000000;
		}
		break;
		case S3C44B0_GPIO_PORT_C :
		{
			// 0C0062C4 -> bit 8 is turned on
			//data |= 1 << 13;
			data = 0x0000FF15;
			data &= (1 << 2) | (1 << 9) | (1 << 10) | (1 << 11);
			data |= (1 << 2) | (1 << 9) | (1 << 10) | (1 << 11);
		}
		break;
		case S3C44B0_GPIO_PORT_D :
		{
			data = 0x00000010;
		}
		break;
		case S3C44B0_GPIO_PORT_E :
		{
			data = 0x000000DF;
		}
		break;
		case S3C44B0_GPIO_PORT_F :
		{
			data = data | (0 << 7);
			data = 0x0000009F;
			data &= ~0xE0;
			if (smc.cmd_latch) data = data | 0x00000020;
			if (smc.add_latch) data = data | 0x00000040;
			if (!smc.busy) data = data | 0x00000080;
		}
		break;
		case S3C44B0_GPIO_PORT_G :
		{
			data = 0x0000009F;
			data = (data & ~0x1F) | (m_port_g->read() & 0x1F);
			#if defined(JUICEBOX_ENTER_DEBUG_MENU)
			if (port_g_read_count++ < 1)
			{
				data = 0x00000095; // PLAY + REVERSE
			}
			#elif defined(JUICEBOX_DISPLAY_ROM_ID)
			if (port_g_read_count++ < 3)
			{
				data = 0x0000008A; // RETURN + FORWARD + STAR
			}
			#endif
		}
		break;
	}
//  data = ((machine().rand() & 0xFF) << 24) | ((machine().rand() & 0xFF) << 16) | ((machine().rand() & 0xFF) << 8) | ((machine().rand() & 0xFF) << 0);
	return data;
}

void juicebox_state::s3c44b0_gpio_port_w(offs_t offset, uint32_t data)
{
	port[offset] = data;
	switch (offset)
	{
		case S3C44B0_GPIO_PORT_F :
		{
			smc.cmd_latch = ((data & 0x00000020) != 0);
			smc.add_latch = ((data & 0x00000040) != 0);
			verboselog( 5, "s3c44b0_gpio_port_w - nand cle %d ale %d\n", (data & 0x20) ? 1 : 0, (data & 0x40) ? 1 : 0);
		}
		break;
	}
}

// ...

uint32_t juicebox_state::juicebox_nand_r(offs_t offset, uint32_t mem_mask)
{
	uint32_t data = 0;
	if (ACCESSING_BITS_0_7) data = data | (smc_read() <<  0);
	if (ACCESSING_BITS_8_15) data = data | (smc_read() <<  8);
	if (ACCESSING_BITS_16_23) data = data | (smc_read() << 16);
	if (ACCESSING_BITS_24_31) data = data | (smc_read() << 24);
	verboselog( 5, "juicebox_nand_r %08X %08X %08X\n", offset, mem_mask, data);
	return data;
}

void juicebox_state::juicebox_nand_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	verboselog( 5, "juicebox_nand_w %08X %08X %08X\n", offset, mem_mask, data);
	if (ACCESSING_BITS_0_7) smc_write((data >>  0) & 0xFF);
	if (ACCESSING_BITS_8_15) smc_write((data >>  8) & 0xFF);
	if (ACCESSING_BITS_16_23) smc_write((data >> 16) & 0xFF);
	if (ACCESSING_BITS_24_31) smc_write((data >> 24) & 0xFF);
}

// ...

INPUT_CHANGED_MEMBER(juicebox_state::port_changed)
{
	m_s3c44b0->request_eint(param);
}

// ...

void juicebox_state::machine_start()
{
	address_space &space = m_maincpu->space(AS_PROGRAM);

	smc_init();

	space.install_readwrite_handler(0x01c00000, 0x01c0000b, read32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::cpuwrap_r)), write32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::cpuwrap_w)));
	space.install_readwrite_handler(0x01d00000, 0x01d0002b, read32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::uart_0_r)),  write32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::uart_0_w)));
	space.install_readwrite_handler(0x01d04000, 0x01d0402b, read32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::uart_1_r)),  write32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::uart_1_w)));
	space.install_readwrite_handler(0x01d14000, 0x01d14013, read32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::sio_r)),     write32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::sio_w)));
	space.install_readwrite_handler(0x01d18000, 0x01d18013, read32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::iis_r)),     write32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::iis_w)));
	space.install_readwrite_handler(0x01d20000, 0x01d20057, read32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::gpio_r)),    write32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::gpio_w)));
	space.install_readwrite_handler(0x01d30000, 0x01d3000b, read32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::wdt_r)),     write32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::wdt_w)));
	space.install_readwrite_handler(0x01d40000, 0x01d4000b, read32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::adc_r)),     write32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::adc_w)));
	space.install_readwrite_handler(0x01d50000, 0x01d5004f, read32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::pwm_r)),     write32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::pwm_w)));
	space.install_readwrite_handler(0x01d60000, 0x01d6000f, read32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::iic_r)),     write32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::iic_w)));
	space.install_readwrite_handler(0x01d80000, 0x01d8000f, read32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::clkpow_r)),  write32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::clkpow_w)));
	space.install_readwrite_handler(0x01e00000, 0x01e0003f, read32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::irq_r)),     write32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::irq_w)));
	space.install_readwrite_handler(0x01e80000, 0x01e8001b, read32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::zdma_0_r)),  write32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::zdma_0_w)));
	space.install_readwrite_handler(0x01e80020, 0x01e8003b, read32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::zdma_1_r)),  write32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::zdma_1_w)));
	space.install_readwrite_handler(0x01f00000, 0x01f00047, read32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::lcd_r)),     write32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::lcd_w)));
	space.install_readwrite_handler(0x01f80000, 0x01f8001b, read32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::bdma_0_r)),  write32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::bdma_0_w)));
	space.install_readwrite_handler(0x01f80020, 0x01f8003b, read32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::bdma_1_r)),  write32s_delegate(*m_s3c44b0, FUNC(s3c44b0_device::bdma_1_w)));
}

void juicebox_state::machine_reset()
{
	m_maincpu->reset();
	smc_reset();
}

/***************************************************************************
    ADDRESS MAPS
***************************************************************************/

void juicebox_state::juicebox_map(address_map &map)
{
	map(0x00000000, 0x007fffff).rom();
	map(0x04000000, 0x04ffffff).rw(FUNC(juicebox_state::juicebox_nand_r), FUNC(juicebox_state::juicebox_nand_w));
	map(0x0c000000, 0x0c1fffff).ram().mirror(0x00600000);
}

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

void juicebox_state::init_juicebox()
{
	// do nothing
}

void juicebox_state::juicebox(machine_config &config)
{
	ARM7(config, m_maincpu, 66000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &juicebox_state::juicebox_map);

	PALETTE(config, "palette").set_entries(32768);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(240, 160);
	screen.set_visarea(0, 240 - 1, 0, 160 - 1);
	screen.set_screen_update("s3c44b0", FUNC(s3c44b0_device::video_update));

	SPEAKER(config, "speaker").front_center();
	DAC_16BIT_R2R_TWOS_COMPLEMENT(config, "dac", 0).add_route(ALL_OUTPUTS, "speaker", 1.0); // unknown DAC

	S3C44B0(config, m_s3c44b0, 10000000);
	m_s3c44b0->set_cpu("maincpu");
	m_s3c44b0->gpio_port_r_cb().set(FUNC(juicebox_state::s3c44b0_gpio_port_r));
	m_s3c44b0->gpio_port_w_cb().set(FUNC(juicebox_state::s3c44b0_gpio_port_w));
	m_s3c44b0->i2s_data_w_cb().set("dac", FUNC(dac_word_interface::data_w));

	SMARTMEDIA(config, m_smartmedia, 0);

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("juicebox");
}

static INPUT_PORTS_START( juicebox )
	PORT_START( "PORTG" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(juicebox_state::port_changed), 0) PORT_NAME("RETURN") PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(juicebox_state::port_changed), 0) PORT_NAME("PLAY") PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(juicebox_state::port_changed), 0) PORT_NAME("FORWARD") PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(juicebox_state::port_changed), 0) PORT_NAME("REVERSE") PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(juicebox_state::port_changed), 0) PORT_NAME("STAR") PORT_PLAYER(1)
INPUT_PORTS_END

/***************************************************************************
    GAME DRIVERS
***************************************************************************/

ROM_START( juicebox )
	ROM_REGION( 0x800000, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "juicebox", "Juice Box v.28 08242004" )
	ROMX_LOAD( "juicebox.rom", 0, 0x800000, CRC(ac731197) SHA1(8278891c3531b3b6b5fec2a97a3ef6f0de1ac81d), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "uclinuxp", "uClinux 2.4.24-uc0 (patched)" )
	ROMX_LOAD( "newboot.rom", 0, 0x1A0800, CRC(443f48b7) SHA1(38f0dc07b5cf02b972a851aa9e87f5d93d03f629), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "uclinux", "uClinux 2.4.24-uc0" )
	ROMX_LOAD( "image.rom", 0, 0x19E400, CRC(6c0308bf) SHA1(5fe21a38a4cd0d86bb60920eb100138b0e924d90), ROM_BIOS(2) )
ROM_END

} // Anonymous namespace


COMP(2004, juicebox, 0, 0, juicebox, juicebox, juicebox_state, init_juicebox, "Mattel", "Juice Box", 0)



juku.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best, Märt Põder
/***************************************************************************

    Juku E5101/E5104

    Hardware:
    - КР580ВМ80A (=КР580ИК80A)
    - КР580ИР82
    - КР580ВА86 x3
    - КР580ВА87 x3
    - КР580ВИ53 x3
    - КР580ВК38
    - КР580ВН59
    - КР580ВВ51A x2
    - КР580ВВ55A x2 (=КР580ИК55)
    - КР1818ВГ93 (on all E5104 production models)

    Note:
    - In the monitor, enter A or B to start BASIC/Assembler
      and T to boot from tape/disk/network

    TODO:
    - E5103 (Juss) and IBM AT keyboard layouts?
    - Work out how the floppy interface really works?
    - Tape? (split up to E5101 batch as tape only?)
    - Separate FDC 1791, 1792 and 1793 versions
    - И41 (=Multibus-1) compatibility?
    - Network?
    - Ramdisk?
    - Memory extensions?

***************************************************************************/

#include "emu.h"

#include "jukumouse.h"

#include "cpu/i8085/i8085.h"
#include "imagedev/floppy.h"
#include "machine/74148.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/wd_fdc.h"
#include "sound/spkrdev.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/juku_dsk.h"

//#define VERBOSE 1
//#define LOG_OUTPUT_FUNC osd_printf_info

#include "logmacro.h"


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

namespace {

static constexpr int DEFAULT_WIDTH = 320;
static constexpr int DEFAULT_HEIGHT = 240;
static constexpr int VERT_FRONT_PORCH = 25;
static constexpr int VERT_BACK_PORCH = 1+72-VERT_FRONT_PORCH;
static constexpr int HORIZ_FRONT_PORCH = 8*8;
static constexpr int HORIZ_BACK_PORCH = 24*8-HORIZ_FRONT_PORCH;

static constexpr int HORIZ_PERIOD = DEFAULT_WIDTH+HORIZ_FRONT_PORCH+HORIZ_BACK_PORCH;
static constexpr int VERT_PERIOD = DEFAULT_HEIGHT+VERT_FRONT_PORCH+VERT_BACK_PORCH;

static constexpr double SPEAKER_LEVELS[3] = {0, 0.5, 1};

class juku_state : public driver_device
{
public:
	juku_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_rom(*this, "maincpu"),
		m_exp(*this, "expcart"),
		m_ram(*this, "ram"),
		m_ext(*this, "ext%u", 0U, 0x8000U, ENDIANNESS_LITTLE),
		m_mode(*this, "mode"),
		m_pic(*this, "pic"),
		m_pit(*this, "pit%u", 0U),
		m_pio(*this, "pio%u", 0U),
		m_sio(*this, "sio%u", 0U),
		m_fdc(*this, "fdc"),
		m_floppy(*this, "fdc:%u", 0U),
		m_key_encoder(*this, "keyenc"),
		m_keys(*this, "COL.%u", 0U),
		m_key_special(*this, "SPECIAL"),
		m_screen(*this, "screen"),
		m_speaker(*this, "speaker"),
		m_mouse(*this, "mouse")
	{ }

	void juku(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<i8080a_cpu_device> m_maincpu;
	required_region_ptr<uint8_t> m_rom;
	optional_region_ptr<uint8_t> m_exp;
	required_shared_ptr<uint8_t> m_ram;
	memory_share_array_creator<uint8_t, 1> m_ext;
	memory_view m_mode;
	required_device<pic8259_device> m_pic;
	required_device_array<pit8253_device, 3> m_pit;
	required_device_array<i8255_device, 2> m_pio;
	required_device_array<i8251_device, 2> m_sio;
	required_device<kr1818vg93_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<ttl74148_device> m_key_encoder;
	required_ioport_array<16> m_keys;
	required_ioport m_key_special;
	required_device<screen_device> m_screen;
	required_device<speaker_sound_device> m_speaker;
	optional_device<juku_mouse_device> m_mouse;

	int32_t m_width, m_height, m_hbporch, m_vbporch;

	uint8_t m_contrdat;

	int16_t m_height_lsb, m_vblank_period_lsb;
	int16_t m_monitor_bits, m_empty_screen_on_update;

	bool m_beep_state, m_beep_level;

	uint8_t m_fdc_cur_cmd;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	void pio0_porta_w(uint8_t data);
	uint8_t pio0_portb_r();
	void pio0_portc_w(uint8_t data);

	void screen_width(uint8_t data);
	void screen_hblank_period(uint8_t data);
	void screen_hfporch(uint8_t data);
	void screen_height(uint8_t data);
	void screen_vblank_period(uint8_t data);
	void screen_vfporch(uint8_t data);
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	// helpers to coordinate screen mode switching
	void adjust_monitor_params(uint8_t monitor_bits);

	void speaker_w(int state);

	static void floppy_formats(format_registration &fr);
	void fdc_drq_w(int state);
	void fdc_cmd_w(uint8_t data);
	uint8_t fdc_data_r();
	void fdc_data_w(uint8_t data);
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void juku_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).ram().share(m_ram);
	map(0x0000, 0xffff).view(m_mode);
	m_mode[0](0x0000, 0x3fff).rom().region("maincpu", 0x0000);
	m_mode[1](0xd800, 0xffff).rom().region("maincpu", 0x1800);
	// optional BASIC expansion cartridge
	m_mode[2](0x4000, 0xbfff).rom().region("expcart", 0x0000);
	// no info on programs actually using extra 32kb "memory window"
	//m_mode[2](0x4000, 0xbfff).ram().share(m_ext[0]);
	m_mode[2](0xd800, 0xffff).rom().region("maincpu", 0x1800);
	m_mode[3]; // let everything fall through to RAM
}

void juku_state::io_map(address_map &map)
{
	map(0x00, 0x01).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x04, 0x07).rw(m_pio[0], FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x08, 0x0b).rw(m_sio[0], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x0c, 0x0f).rw(m_pio[1], FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x10, 0x13).rw(m_pit[0], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x14, 0x17).rw(m_pit[1], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	// write functions for sceen timer config to adjust emulated display
	map(0x10, 0x10).w(FUNC(juku_state::screen_width));
	map(0x14, 0x14).w(FUNC(juku_state::screen_height));
	map(0x11, 0x11).w(FUNC(juku_state::screen_hblank_period));
	map(0x12, 0x12).w(FUNC(juku_state::screen_hfporch));
	map(0x15, 0x15).w(FUNC(juku_state::screen_vblank_period));
	map(0x16, 0x16).w(FUNC(juku_state::screen_vfporch));
	map(0x18, 0x1b).rw(m_pit[2], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x1c, 0x1f).rw(m_fdc, FUNC(kr1818vg93_device::read), FUNC(kr1818vg93_device::write));
	// functions for floppy drive operation fixes
	map(0x1c, 0x1c).w(FUNC(juku_state::fdc_cmd_w));
	map(0x1f, 0x1f).rw(FUNC(juku_state::fdc_data_r), FUNC(juku_state::fdc_data_w));
	// mapping for cassette version (E5101?)
	// map(0x1c, 0x1d).rw(m_sio[1], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x80, 0x80).r(m_mouse, FUNC(juku_mouse_device::mouse_port_r)); // TODO: turn this into a Multibus expansion
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( juku )
	PORT_START("COL.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')// n N
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') // y Y
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6') PORT_CHAR('&') // 6 &
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') // h H
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') // x X
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') // w W
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2') PORT_CHAR('"') // 2 "
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') // s S
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') // v V
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') // r R
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4') PORT_CHAR('$') // 4 $
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') // f F
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_NAME("CAPS LOCK")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') // b B
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') // t T
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5') PORT_CHAR('%') // 5 %
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') // g G
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') // z Z
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') // q Q
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1') PORT_CHAR('!') // 1 !
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') // a A
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR(3) // c C
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') // e E
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3') PORT_CHAR('#') // 3 #
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') // d D
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') // m M
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') // u U
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7') PORT_CHAR(39) // 7 '
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') // j J
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL.8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL) PORT_NAME("DEL")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN) PORT_CHAR(']') // ] õ
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT) PORT_NAME("ERASE") // ERASE
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13) PORT_NAME("RETURN")
	PORT_DIPNAME(0x40, 0x00, "B4_0") PORT_DIPLOCATION("CONTRDAT1:01")
	PORT_DIPSETTING(   0x00, DEF_STR(Off))
	PORT_DIPSETTING(   0x40, DEF_STR(On))
	PORT_DIPNAME(0x80, 0x80, "B5_0") PORT_DIPLOCATION("CONTRDAT2:01")
	PORT_DIPSETTING(   0x00, DEF_STR(Off))
	PORT_DIPSETTING(   0x80, DEF_STR(On))

	PORT_START("COL.9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_END) PORT_CHAR('[') // [ ö
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP) PORT_NAME("Ä  Ü") // Ä Ü
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_DIPNAME(0x40, 0x00, "B4_1") PORT_DIPLOCATION("CONTRDAT1:02")
	PORT_DIPSETTING(   0x00, DEF_STR(Off))
	PORT_DIPSETTING(   0x40, DEF_STR(On))
	PORT_DIPNAME(0x80, 0x80, "B5_1") PORT_DIPLOCATION("CONTRDAT2:02")
	PORT_DIPSETTING(   0x00, DEF_STR(Off))
	PORT_DIPSETTING(   0x80, DEF_STR(On))

	PORT_START("COL.10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_NAME("õ  Õ") // õ Õ
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_NAME("Ö  Õ") // Ö Õ
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(':') PORT_CHAR('*') // : *
	PORT_DIPNAME(0x40, 0x00, "B4_2") PORT_DIPLOCATION("CONTRDAT1:03")
	PORT_DIPSETTING(   0x00, DEF_STR(Off))
	PORT_DIPSETTING(   0x40, DEF_STR(On))
	PORT_DIPNAME(0x80, 0x80, "B5_2") PORT_DIPLOCATION("CONTRDAT2:03")
	PORT_DIPSETTING(   0x00, DEF_STR(Off))
	PORT_DIPSETTING(   0x80, DEF_STR(On))

	PORT_START("COL.11")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(';') PORT_CHAR('+') // ; +
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(32)
	// Picture of machine shows "\ ^" here. You can use Ü to represent ^ in BASIC or switch to ASCII font in EKDOS to make ^ show as expected.
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('\\') PORT_CHAR('^') PORT_NAME("ü  Ü") // ü Ü
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR('-') PORT_CHAR('=') // - =
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_NAME("ä  Ä") // ä Ä
	PORT_DIPNAME(0x40, 0x00, "B4_3") PORT_DIPLOCATION("CONTRDAT1:04")
	PORT_DIPSETTING(   0x00, DEF_STR(Off))
	PORT_DIPSETTING(   0x40, DEF_STR(On))
	PORT_DIPNAME(0x80, 0x80, "B5_3") PORT_DIPLOCATION("CONTRDAT2:04")
	PORT_DIPSETTING(   0x00, DEF_STR(Off))
	PORT_DIPSETTING(   0x80, DEF_STR(On))

	PORT_START("COL.12")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?') // / ?
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') // p P
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0') PORT_CHAR('_') // 0 _
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_NAME("ö  Ö") // ö Ö
	PORT_DIPNAME(0x40, 0x00, "B4_4") PORT_DIPLOCATION("CONTRDAT1:05")
	PORT_DIPSETTING(   0x00, DEF_STR(Off))
	PORT_DIPSETTING(   0x40, DEF_STR(On))
	PORT_DIPNAME(0x80, 0x80, "B5_4") PORT_DIPLOCATION("CONTRDAT2:05")
	PORT_DIPSETTING(   0x00, DEF_STR(Off))
	PORT_DIPSETTING(   0x80, DEF_STR(On))

	PORT_START("COL.13")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>') // . >
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(UCHAR_MAMEKEY(LEFT),8)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') // o O
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9') PORT_CHAR(')') // 9 )
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') // l L
	PORT_DIPNAME(0x40, 0x00, "B4_5") PORT_DIPLOCATION("CONTRDAT1:06")
	PORT_DIPSETTING(   0x00, DEF_STR(Off))
	PORT_DIPSETTING(   0x40, DEF_STR(On))
	PORT_DIPNAME(0x80, 0x80, "B5_5") PORT_DIPLOCATION("CONTRDAT2:06")
	PORT_DIPSETTING(   0x00, DEF_STR(Off))
	PORT_DIPSETTING(   0x80, DEF_STR(On))

	PORT_START("COL.14")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR(UCHAR_MAMEKEY(F8)) // "7-8-9-F8" on numpad (popular gaming controls)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(',') PORT_CHAR('<') // , <
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CODE(KEYCODE_BACKSLASH2) PORT_NAME("LAT RUS") // LAT/RUS
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') // i I
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8') PORT_CHAR('(') // 8 (
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') // k K
	PORT_DIPNAME(0x40, 0x00, "B4_6") PORT_DIPLOCATION("CONTRDAT1:07")
	PORT_DIPSETTING(   0x00, DEF_STR(Off))
	PORT_DIPSETTING(   0x40, DEF_STR(On))
	PORT_DIPNAME(0x80, 0x80, "B5_6") PORT_DIPLOCATION("CONTRDAT2:07")
	PORT_DIPSETTING(   0x00, DEF_STR(Off))
	PORT_DIPSETTING(   0x80, DEF_STR(On))

	PORT_START("COL.15")
	PORT_BIT(0x3f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_DIPNAME(0x40, 0x00, "B4_7") PORT_DIPLOCATION("CONTRDAT1:08")
	PORT_DIPSETTING(   0x00, DEF_STR(Off))
	PORT_DIPSETTING(   0x40, DEF_STR(On))
	PORT_DIPNAME(0x80, 0x80, "B5_7") PORT_DIPLOCATION("CONTRDAT2:08")
	PORT_DIPSETTING(   0x00, DEF_STR(Off))
	PORT_DIPSETTING(   0x80, DEF_STR(On))

	PORT_START("SPECIAL")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
INPUT_PORTS_END


//**************************************************************************
//  VIDEO
//**************************************************************************

inline uint16_t bcd_value(uint16_t val)
{
	return
		((val>>12) & 0xf) *  1000 +
		((val>> 8) & 0xf) *   100 +
		((val>> 4) & 0xf) *    10 +
		( val      & 0xf);
}

void juku_state::screen_width(uint8_t data)
{
	m_pit[0]->write(0x10, data);
	m_screen->set_size(bcd_value(data)*8, m_screen->height());
	adjust_monitor_params(0b0000'0001);
}

void juku_state::screen_height(uint8_t data)
{
	m_pit[1]->write(0x14, data);

	if (m_height_lsb == -1) {
		m_height_lsb = (int)data;
	} else {
		m_screen->set_size(m_screen->width(), ((uint16_t)data << 8) + (uint8_t)m_height_lsb);
		m_height_lsb = -1;
		adjust_monitor_params(0b0000'1000);
	}
}

void juku_state::screen_hblank_period(uint8_t data)
{
	m_pit[0]->write(0x11, data);
	m_width = m_screen->width()-bcd_value(data)*8;
	adjust_monitor_params(0b0000'0010);
}

void juku_state::screen_vblank_period(uint8_t data)
{
	m_pit[1]->write(0x15, data);

	if (m_vblank_period_lsb == -1) {
		m_vblank_period_lsb = (int)data;
	} else {
		m_height = m_screen->height()-bcd_value(((uint16_t)data<<8) + (uint8_t)m_vblank_period_lsb) - 1;
		m_vblank_period_lsb = -1;
		adjust_monitor_params(0b0001'0000);
	}
}

void juku_state::screen_hfporch(uint8_t data)
{
	m_pit[0]->write(0x12, data);
	m_hbporch = m_screen->width()-m_width-bcd_value(data)*8;
	adjust_monitor_params(0b0000'0100);
}

void juku_state::screen_vfporch(uint8_t data)
{
	m_pit[1]->write(0x16, data);
	m_vbporch = m_screen->height()-m_height-bcd_value(data);
	adjust_monitor_params(0b0010'0000);
}

/*
 * In changing screen resolution mimic a real monitor and allow tweaking
 * individual parameters without immediately changing the visible area
 */
void juku_state::adjust_monitor_params(uint8_t monitor_bits)
{
	// --5-----  ver front fporch
	// ---4----  ver blank period
	// ----3---  screen height
	// -----2--  hor front porch
	// ------1-  hor blank period
	// -------0  screen width

	m_monitor_bits |= monitor_bits;

	// mostly to make screen positioning tools behave decently
	m_empty_screen_on_update = 2;

	// don't adjust monitor params unless all six screen params in ports 10h-12h and 14h-16h are set
	// horizontal/vertical rates in 10h and 14h are set in BIOS and not changed in normal video mode switching
	// expect changing params in order AND vertical front porch in 16h as the final step
	if (monitor_bits == 0b0010'0000 && m_monitor_bits == 0b0011'1111) {
		m_screen->set_visarea(m_hbporch, m_hbporch + m_width - 1, m_vbporch, m_vbporch + m_height - 1);
		m_monitor_bits = 0b000'01001;
	}
}

uint32_t juku_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	int y_max = (m_vbporch < 0) ? (m_height + m_vbporch) : (m_vbporch+m_height > m_screen->height()) ? m_screen->height() : m_height;
	int x_max = (m_hbporch < 0) ? (m_width + m_hbporch) : (m_hbporch+m_width > m_screen->width()) ? m_screen->width() : m_width;

	if (m_empty_screen_on_update) {
		m_empty_screen_on_update--;
		bitmap.fill(0);
	}

	for (int y = 0; y < y_max; y++) {
		uint32_t *dest = &bitmap.pix(std::max(y+m_vbporch, 0)) + std::max(m_hbporch, 0);
		for (int x = 0; x < x_max; x++)
			*dest++ = BIT(m_ram[0xd800 + (y * (m_width / 8) + x / 8)], 7 - (x % 8)) ? rgb_t::white() : rgb_t::black();
	}

	return 0;
}


//**************************************************************************
//  FLOPPY DISK
//**************************************************************************

void juku_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_JUKU_FORMAT);
}

static void juku_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
	device.option_add("525ssqd", FLOPPY_525_SSQD);
}

void juku_state::fdc_drq_w(int state)
{
	// clear HALT state of CPU when data is ready to read
	if (state)
		m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
}

void juku_state::fdc_cmd_w(uint8_t data)
{
	m_fdc_cur_cmd = data;
	m_fdc->cmd_w(data);
}

uint8_t juku_state::fdc_data_r()
{
	// on read commands (100xxxxx, 11000xxx, 11100xxx) and fdc reports busy
	if ( ((m_fdc_cur_cmd >> 5) == 0x4 || (m_fdc_cur_cmd >> 5) == 0x6 || (m_fdc_cur_cmd >> 3) == 0x1c) && m_fdc->drq_r() == 0 && (m_fdc->status_r() & 0x1) == 0x1 ) {
		// cpu tries to read data without drq active. halt it and reset the
		// pc back to the beginning of the instruction
		m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
		m_maincpu->set_state_int(i8080a_cpu_device::I8085_PC, m_maincpu->pc() - 2);

		return 0;
	}

	return m_fdc->data_r();
}

void juku_state::fdc_data_w(uint8_t data)
{
	// on write commands (101xxxxx, 11110xxx) and fdc reports busy
	if ( ((m_fdc_cur_cmd >> 5) == 0x5 || (m_fdc_cur_cmd >> 3) == 0x1e) && m_fdc->drq_r() == 0 && (m_fdc->status_r() & 0x1) == 0x1 ) {
		// cpu tries to write data without drq, halt it and reset pc
		m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
		m_maincpu->set_state_int(i8080a_cpu_device::I8085_PC, m_maincpu->pc() - 2);

		return;
	}

	m_fdc->data_w(data);
}


//**************************************************************************
//  SOUND
//**************************************************************************

void juku_state::speaker_w(int state)
{
	m_speaker->level_w((m_beep_state = state) << (m_beep_level ? 1 : 0));
}


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void juku_state::pio0_porta_w(uint8_t data)
{
	// 7-------  stb
	// -6------  pren
	// --5-----  not used?
	// ---4----  audc
	// ----3210  keyboard column

	uint8_t col_data = m_keys[data & 0xf]->read();

	for (int i = 0; i < 6; i++)
		m_key_encoder->input_line_w(i, BIT(col_data, i));
	m_contrdat = (col_data & 0b1100'0000) >> 6; // decoded by 2x К555ИД7

	if (m_beep_level != BIT(data, 4))
		m_speaker->level_w(m_beep_state << ((m_beep_level = BIT(data, 4)) ? 1 : 0));

	m_key_encoder->update();
}

uint8_t juku_state::pio0_portb_r()
{
	// 7-------  ctrl
	// -6------  shift
	// --54----  contrdat?
	// ----321-  keyboard data
	// -------0  key pressed

	uint8_t data = 0;

	data |= m_key_special->read();
	data |= m_contrdat << 4;
	data |= m_key_encoder->output_r() << 1;
	data |= m_key_encoder->output_valid_r();

	return data;
}

void juku_state::pio0_portc_w(uint8_t data)
{
	// 7-------  (cas?) pof
	// -6------  (cas?) stop / floppy side select
	// --5-----  (cas?) rn / floppy drive select
	// ---4----  (cas?) ff / floppy density (sd = 1, dd = 0)
	// ----3---  (cas?) play / floppy size (8" = 1, 5.25" = 0)
	// -----2--  (cas?) rec / floppy motor (on = 1, off = 0)
	// ------10  memory mode

	floppy_image_device *floppy = m_floppy[BIT(data, 5)]->get_device();
	m_fdc->set_floppy(floppy);
	m_fdc->dden_w(BIT(data,4));
	// TODO: 8" floppy select

	if (floppy) {
		floppy->mon_w(!BIT(data,2));
		floppy->ss_w(BIT(data, 6));
	}

	m_mode.select(data & 0b11);
}

void juku_state::machine_start()
{
	// register for save states
	save_item(NAME(m_width));
	save_item(NAME(m_height));
	save_item(NAME(m_hbporch));
	save_item(NAME(m_vbporch));
	save_item(NAME(m_contrdat));
	save_item(NAME(m_monitor_bits));
	save_item(NAME(m_height_lsb));
	save_item(NAME(m_vblank_period_lsb));
	save_item(NAME(m_empty_screen_on_update));
	save_item(NAME(m_beep_state));
	save_item(NAME(m_beep_level));
	save_item(NAME(m_fdc_cur_cmd));
}

void juku_state::machine_reset()
{
	m_mode.select(0);
	m_key_encoder->enable_input_w(0);
	m_beep_state = 0;
	m_beep_level = 0;
	m_contrdat = 0b11;
	m_fdc_cur_cmd = 0;
	m_width = DEFAULT_WIDTH, m_height = DEFAULT_HEIGHT;
	m_hbporch = HORIZ_BACK_PORCH, m_vbporch = VERT_BACK_PORCH;
	m_height_lsb = -1;
	m_vblank_period_lsb = -1;
	m_monitor_bits = 0U;
	m_empty_screen_on_update = 0;
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void juku_state::juku(machine_config &config)
{
	// КР580ВМ80A @ 2 MHz (=KP580ИK80А)
	I8080A(config, m_maincpu, 20_MHz_XTAL/10);
	m_maincpu->set_addrmap(AS_PROGRAM, &juku_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &juku_state::io_map);
	m_maincpu->in_inta_func().set(m_pic, FUNC(pic8259_device::acknowledge));

	// КР580ВН59
	PIC8259(config, m_pic, 0);
	m_pic->out_int_callback().set_inputline(m_maincpu, 0);

	// КР580ВИ53 #1
	PIT8253(config, m_pit[0], 0);
	m_pit[0]->set_clk<0>(16_MHz_XTAL/16); // РК171 16000kHz variations on board
	m_pit[0]->set_clk<1>(16_MHz_XTAL/16); // РК170ББ-14ГC 16000kHz in specs
	m_pit[0]->set_clk<2>(16_MHz_XTAL/16);
	m_pit[0]->out_handler<0>().set(m_pit[1], FUNC(pit8253_device::write_clk0));
	m_pit[0]->out_handler<0>().append(m_pit[0], FUNC(pit8253_device::write_gate1));
	m_pit[0]->out_handler<0>().append(m_pit[0], FUNC(pit8253_device::write_gate2));
	//m_pit[0]->out_handler<1>().set(?, ?); // HOR RTR

	// КР580ВИ53 #2
	PIT8253(config, m_pit[1], 0);
	m_pit[0]->out_handler<2>().set(m_pit[1], FUNC(pit8253_device::write_clk1)); // HOR SYNC DSL
	m_pit[0]->out_handler<2>().append(m_pit[1], FUNC(pit8253_device::write_clk2));
	m_pit[1]->out_handler<0>().append(m_pit[1], FUNC(pit8253_device::write_gate1));
	m_pit[1]->out_handler<0>().append(m_pit[1], FUNC(pit8253_device::write_gate2));
	m_pit[1]->out_handler<1>().set(m_pic, FUNC(pic8259_device::ir5_w)); // VER RTR / FRAME INT
	//m_pit[1]->out_handler<2>().set(m_pit[1], FUNC(pit8253_device::write_clk1)); // VERT SYNC DSL
	//m_pit[1]->out_handler<2>().append(m_pit[1], FUNC(pit8253_device::write_clk2));

	// КР580ВИ53 #3
	PIT8253(config, m_pit[2], 0);
	m_pit[2]->set_clk<0>(16_MHz_XTAL/13); // 1.23 MHz
	m_pit[2]->set_clk<1>(16_MHz_XTAL/8); // 2 MHz
	m_pit[1]->out_handler<1>().append(m_pit[2], FUNC(pit8253_device::write_clk2)); // ~49.92 Hz
	//m_pit[2]->out_handler<0>().set(?, ?); // BAUD RATE
	m_pit[2]->out_handler<1>().append(FUNC(juku_state::speaker_w)); // SOUND
	//m_pit[2]->out_handler<2>().set(?, ?); // SYNC BAUD RATE

	// КР580ВВ55A #1 (=КР580ИК55)
	I8255A(config, m_pio[0]);
	m_pio[0]->out_pa_callback().set(FUNC(juku_state::pio0_porta_w));
	m_pio[0]->in_pb_callback().set(FUNC(juku_state::pio0_portb_r));
	m_pio[0]->out_pc_callback().set(FUNC(juku_state::pio0_portc_w));

	// КР580ВВ55A #2
	I8255A(config, m_pio[1]);

	// КР580ВВ51A
	I8251(config, m_sio[0], 0);
	m_sio[0]->rxrdy_handler().set(m_pic, FUNC(pic8259_device::ir2_w));
	m_sio[0]->txrdy_handler().set(m_pic, FUNC(pic8259_device::ir3_w));

	// КР580ВВ51A (instead of FDC?)
	I8251(config, m_sio[1], 0);
	m_sio[1]->rxrdy_handler().set(m_pic, FUNC(pic8259_device::ir0_w));
	m_sio[1]->txrdy_handler().set(m_pic, FUNC(pic8259_device::ir1_w));

	// Электроника МС 6105.1 "Колокольчик" (DEC VR201 analog)
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(16_MHz_XTAL/16, HORIZ_PERIOD/8, HORIZ_BACK_PORCH, HORIZ_BACK_PORCH+DEFAULT_WIDTH, VERT_PERIOD, VERT_BACK_PORCH, VERT_BACK_PORCH+DEFAULT_HEIGHT);
	m_screen->set_size(HORIZ_PERIOD, VERT_PERIOD);
	m_screen->set_screen_update(FUNC(juku_state::screen_update));

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1);
	m_speaker->set_levels(3, SPEAKER_LEVELS);

	// К155ИВ1
	TTL74148(config, m_key_encoder, 0);

	// E4701 (joystick like mouse device)
	JUKU_MOUSE(config, m_mouse);
	m_mouse->int_handler().set(m_pic, FUNC(pic8259_device::ir6_w));

	// КР1818ВГ93 (for E6502 disk drive)
	KR1818VG93(config, m_fdc, 16_MHz_XTAL/16);
	m_fdc->drq_wr_callback().set(FUNC(juku_state::fdc_drq_w));
	FLOPPY_CONNECTOR(config, "fdc:0", juku_floppies, "525qd", juku_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", juku_floppies, "525qd", juku_state::floppy_formats);

	SOFTWARE_LIST(config, "floppy_list").set_original("juku");
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( juku )
	ROM_DEFAULT_BIOS("3.43m_37")
	ROM_REGION(0x4000, "maincpu", 0)

	// Monitor 3.3 with Bootstrap 3.3, FDC 1791 from early prototypes
	// Does not seem to be compatible with JBASIC expansion cartridge
	ROM_SYSTEM_BIOS(0, "jmon3.3", "Monitor/Bootstrap 3.3 \\w JBASIC")
	ROMX_LOAD("jmon33.bin", 0x0000, 0x4000, CRC(ed22c287) SHA1(76407d99bf83035ef526d980c9468cb04972608c), ROM_BIOS(0))

	// RomBios 3.42 with Janet 1.2/Bootstrap 4.1, screen 53x24, FDC 1791/2 from Juss prototype (E5103?)
	// Id: "EktaSoft '88  Serial #0024"
	ROM_SYSTEM_BIOS(1, "3.42_24", "Disk/Net \\w JUSS keyb (3.42 #0024)")
	ROMX_LOAD("ekta24.bin", 0x0000, 0x4000, CRC(6ce7ee3b) SHA1(a7185d747c94cd519868692ed3d10fade90dd6d5), ROM_BIOS(1))

	// RomBios 2.43m with TapeBios/Bootstrap 4.1, screen 53x24 (true E5101?)
	// Id: "EktaSoft '88  Serial #0032"
	ROM_SYSTEM_BIOS(2, "2.43m_32", "Tape/Disk (2.43m #0032)")
	ROMX_LOAD("ekta32.bin", 0x0000, 0x4000, CRC(72c0da53) SHA1(57311d53f6fe1e87e0755990f400253caccd4795), ROM_BIOS(2))

	// RomBios 3.43m with Janet 1.2/Bootstrap 4.1, screen 53x24 from Juss prototype (E5103?)
	// Id: "EktaSoft '88  Serial #0035"
	ROM_SYSTEM_BIOS(3, "3.43m_35", "Disk/Net \\w JUSS keyb (3.43m #0035)")
	ROMX_LOAD("ekta35.bin", 0x0000, 0x4000, CRC(85a017bc) SHA1(7aa03497d88cfab9315aa3987765bc06ecb70013), ROM_BIOS(3))

	// RomBios 3.43m with Janet 1.2/Bootstrap 4.1 from widespread Baltijets batch (E5104)
	// Id: "EktaSoft '88  Serial #0037"
	ROM_SYSTEM_BIOS(4, "3.43m_37", "Disk/Net (3.43m #0037)")
	ROMX_LOAD("ekta37.bin", 0x0000, 0x4000, CRC(2c1c9cad) SHA1(29366d74c0e27129f2484a973f7a6de659b90cf4), ROM_BIOS(4))

	// RomBios 2.43m with TapeBios/Bootstrap 4.1, screen 53x24, modified for IBM AT keyboard (homebrew)
	// Id: "EktaSoft '90  Serial #0043"
	ROM_SYSTEM_BIOS(5, "2.43m_43", "Tape/Disk \\w AT keyb (2.43m #0043)")
	ROMX_LOAD("ekta43.bin", 0x0000, 0x4000, CRC(05678f9f) SHA1(a7419bfd8249871cc7dbf5c6ea85022d6963fc9a), ROM_BIOS(5))

	ROM_REGION(0x8000, "expcart", 0)

	// EKTA JBASIC cartridge (buggy) seems similar to v1.1 from 14.09.1987.
	// There is also a version with additional HEX$ directive for EKDOS.
	// Initial E5101 had JBASIC onboard with early RomBios/Monitor versions.
	ROMX_LOAD("jbasic11.bin", 0x0000, 0x2000, CRC(bdc471ca) SHA1(3d96ba589aa21d44412efb099a144fbe23a2f52f), ROM_BIOS(1))
	ROMX_LOAD("jbasic11.bin", 0x0000, 0x2000, CRC(bdc471ca) SHA1(3d96ba589aa21d44412efb099a144fbe23a2f52f), ROM_BIOS(2))
	ROMX_LOAD("jbasic11.bin", 0x0000, 0x2000, CRC(bdc471ca) SHA1(3d96ba589aa21d44412efb099a144fbe23a2f52f), ROM_BIOS(3))
	ROMX_LOAD("jbasic11.bin", 0x0000, 0x2000, CRC(bdc471ca) SHA1(3d96ba589aa21d44412efb099a144fbe23a2f52f), ROM_BIOS(4))
	ROMX_LOAD("jbasic11.bin", 0x0000, 0x2000, CRC(bdc471ca) SHA1(3d96ba589aa21d44412efb099a144fbe23a2f52f), ROM_BIOS(5))
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY  FULLNAME      FLAGS
COMP( 1988, juku, 0,      0,      juku,    juku,  juku_state, empty_init, "EKTA",  "Juku E5104", MACHINE_SUPPORTS_SAVE)



junior.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
// thanks-to:Manfred Schneider
/***************************************************************************

        Elektor Junior

2009-07-17 Skeleton driver.

This is heavily based on the KIM-1, the keycodes and operation being
identical.

Pasting:
        0-F : as is
        + (inc) : ^
        AD : -
        DA : =
        GO : X

(note: DA only works when addressing RAM)

Test Paste:
        =11^22^33^44^55^66^77^88^99^-0000
        Now press up-arrow to confirm the data has been entered.

NMI and IRQ:
- When NMI is activated, it jumps to the vector located at 1A7A.
- When IRQ is activated, it jumps to the vector located at 1A7E.
- The user needs to populate these vectors before use, or the system
  will crash when the debugging switches are used. Use RST to escape
  from this.


To Do:
- Single-Step switch (and associated LED) needs to be hooked up

****************************************************************************/

#include "emu.h"
#include "cpu/m6502/m6502.h"
#include "machine/mos6530.h"
#include "machine/timer.h"
#include "video/pwm.h"
#include "junior.lh"


namespace {

class junior_state : public driver_device
{
public:
	junior_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_riot(*this, "riot")
		, m_maincpu(*this, "maincpu")
		, m_display(*this, "display")
		, m_io_keyboard(*this, "LINE%u", 0U)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(junior_reset);
	DECLARE_INPUT_CHANGED_MEMBER(junior_stop);
	void junior(machine_config &config);

private:
	u8 riot_a_r();
	void riot_a_w(u8 data);
	void riot_b_w(u8 data);
	u8 m_digit = 0U;
	u8 m_seg = 0U;

	virtual void machine_start() override ATTR_COLD;

	void mem_map(address_map &map) ATTR_COLD;

	required_device<mos6532_device> m_riot;
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_ioport_array<4> m_io_keyboard;
};




void junior_state::mem_map(address_map &map)
{
	map.global_mask(0x1FFF);
	map.unmap_value_high();
	map(0x0000, 0x03ff).ram(); // 1K RAM
	map(0x1a00, 0x1a7f).m(m_riot, FUNC(mos6532_device::ram_map));
	map(0x1a80, 0x1a9f).m(m_riot, FUNC(mos6532_device::io_map));
	map(0x1c00, 0x1fff).rom().region("maincpu", 0); // Monitor
}


INPUT_CHANGED_MEMBER(junior_state::junior_reset)
{
	if (newval == 0)
		m_maincpu->reset();
}

INPUT_CHANGED_MEMBER(junior_state::junior_stop)
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, newval ? CLEAR_LINE : ASSERT_LINE);
}


/* Input ports */
static INPUT_PORTS_START( junior )
PORT_START("LINE0")         /* IN0 keys row 0 */
	PORT_BIT( 0x80, 0x00, IPT_UNUSED )
	PORT_BIT( 0x40, 0x40, IPT_KEYBOARD ) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x20, 0x20, IPT_KEYBOARD ) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x10, 0x10, IPT_KEYBOARD ) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x08, 0x08, IPT_KEYBOARD ) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x04, 0x04, IPT_KEYBOARD ) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x02, 0x02, IPT_KEYBOARD ) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x01, 0x01, IPT_KEYBOARD ) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')

	PORT_START("LINE1")         /* IN1 keys row 1 */
	PORT_BIT( 0x80, 0x00, IPT_UNUSED )
	PORT_BIT( 0x40, 0x40, IPT_KEYBOARD ) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x20, 0x20, IPT_KEYBOARD ) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x10, 0x10, IPT_KEYBOARD ) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x08, 0x08, IPT_KEYBOARD ) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x04, 0x04, IPT_KEYBOARD ) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x02, 0x02, IPT_KEYBOARD ) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x01, 0x01, IPT_KEYBOARD ) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')

	PORT_START("LINE2")         /* IN2 keys row 2 */
	PORT_BIT( 0x80, 0x00, IPT_UNUSED )
	PORT_BIT( 0x40, 0x40, IPT_KEYBOARD ) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x20, 0x20, IPT_KEYBOARD ) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x10, 0x10, IPT_KEYBOARD ) PORT_NAME("AD") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT( 0x08, 0x08, IPT_KEYBOARD ) PORT_NAME("DA") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=')
	PORT_BIT( 0x04, 0x04, IPT_KEYBOARD ) PORT_NAME("+") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT( 0x02, 0x02, IPT_KEYBOARD ) PORT_NAME("GO") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x01, 0x01, IPT_KEYBOARD ) PORT_NAME("PC") PORT_CODE(KEYCODE_F6)

	PORT_START("LINE3")         /* IN3 STEP and RESET keys, MODE switch */
	PORT_BIT( 0x80, 0x00, IPT_UNUSED )
	PORT_BIT( 0x40, 0x40, IPT_KEYBOARD ) PORT_NAME("S1: STOP") PORT_CODE(KEYCODE_F7) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(junior_state::junior_stop), 0)
	PORT_BIT( 0x20, 0x20, IPT_KEYBOARD ) PORT_NAME("S2: RST") PORT_CODE(KEYCODE_F3) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(junior_state::junior_reset), 0)
	PORT_DIPNAME(0x10, 0x10, "S24: SS (NumLock)") PORT_CODE(KEYCODE_NUMLOCK) PORT_TOGGLE
	PORT_DIPSETTING( 0x00, "single step")
	PORT_DIPSETTING( 0x10, "run")
	PORT_DIPNAME(0x08, 0x08, "S25: Display")
	PORT_DIPSETTING( 0x00, "Off")
	PORT_DIPSETTING( 0x08, "On")
	PORT_BIT( 0x04, 0x00, IPT_UNUSED )
	PORT_BIT( 0x02, 0x00, IPT_UNUSED )
	PORT_BIT( 0x01, 0x00, IPT_UNUSED )
INPUT_PORTS_END



u8 junior_state::riot_a_r()
{
	u8 data = 0xff;

	if (m_digit < 3)
		data &= m_io_keyboard[m_digit]->read();

	return data;

}

void junior_state::riot_a_w(u8 data)
{
	m_seg = ~data;
	if (BIT(m_io_keyboard[3]->read(), 3))
		m_display->matrix(1 << m_digit, m_seg);
	else
		m_display->matrix(1 << m_digit, 0);
}


void junior_state::riot_b_w(u8 data)
{
	m_digit = (data >> 1) & 15;
	if (BIT(m_io_keyboard[3]->read(), 3))
		m_display->matrix(1 << m_digit, m_seg);
	else
		m_display->matrix(1 << m_digit, 0);
}


void junior_state::machine_start()
{
	save_item(NAME(m_digit));
	save_item(NAME(m_seg));
}


void junior_state::junior(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, 1_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &junior_state::mem_map);

	/* video hardware */
	config.set_default_layout(layout_junior);
	PWM_DISPLAY(config, m_display).set_size(10, 7);
	m_display->set_segmask(0x3f0, 0x7f);

	/* Devices */
	MOS6532(config, m_riot, 1_MHz_XTAL);
	m_riot->pa_rd_callback().set(FUNC(junior_state::riot_a_r));
	m_riot->pa_wr_callback().set(FUNC(junior_state::riot_a_w));
	m_riot->pb_wr_callback().set(FUNC(junior_state::riot_b_w));
	m_riot->irq_wr_callback().set_inputline(m_maincpu, M6502_IRQ_LINE);
}


/* ROM definition */
ROM_START( junior )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_DEFAULT_BIOS("orig")

	ROM_SYSTEM_BIOS( 0, "orig", "Original ESS503" )
	ROMX_LOAD( "ess503.ic2", 0x0000, 0x0400, CRC(9e804f8c) SHA1(181bdb69fb4711cb008e7966747d4775a5e3ef69), ROM_BIOS(0))

	ROM_SYSTEM_BIOS( 1, "mod-orig", "Mod-Original (2708)" )
	ROMX_LOAD( "junior-mod.ic2", 0x0000, 0x0400, CRC(ee8aa69d) SHA1(a132a51603f1a841c354815e6d868b335ac84364), ROM_BIOS(1))

	ROM_SYSTEM_BIOS( 2, "2732", "Just monitor (2732)" )
	ROMX_LOAD( "junior27321a.ic2", 0x0000, 0x0400, CRC(e22f24cc) SHA1(a6edb52a9eea5e99624c128065e748e5a3fb2e4c), ROM_BIOS(2))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY    FULLNAME           FLAGS */
COMP( 1980, junior, 0,      0,      junior,  junior, junior_state, empty_init, "Elektor", "Junior Computer", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



junior80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Not much is known about this Hungarian Z80-based machine, not even the name
of the manufacturer. It comes with 64K of RAM, and 2x 5.25 floppy drives.
Video is a CGA card.

Notes found at vcfed:

1988, hungarian unknown, junior80
2x 5.25 FDD DSDD 80T, 64K ram, CGA graphics card, Serial card with 2 ports
 which is in addition to those ports on the main board.
So this are the main chips on the logic board:
Address / Chip
00H-03H I8255
00H - input port for keyboard scan codes
01H - input port for system jumpers and parallel interface extra lines
PB6-7 - extra line parallel port B
PB4-5 - extra line parallel port A
PB3 - 0 - parallel keyboard / 1-serial keyboard
PB2 - 0 - internal console / 1 - serial console
PB1 - disk drive 2,3 - 0=5.25", 1=8"
PB0 - disk drive 0,1 - 0=5.25", 1=8"
02H
PC7 - second channel I8253 - 0 deactivated, 1 activated
PC6 - speaker modulation
PC4-5 - extra lines parallel port B
PC2-3 - extra lines parallel port A
PC1 - activate / deactivate serial keyboard
PC0 - comutation 0-RAM / 1 -ROM
03H command port for I8255

10H-13H Z80A SIO
10H - dataport A
11H - dataport B
12H - command port A
13H - command port B
THE code in ROM programms z80SIO: port A unprogrammed, port B: 8 bits, 2 stop-bits, no parity,
 clockx16 - when the OS is loaded from disk, both channels are programmed identical.

20-23H Z80A CTC
20H - data port - interrupt vector for channel 0 (in 8mhz, out 250khz)
21H - command port data channel 1
22H - command port data channel 2
23H - command port data channel 3
both ROM and OS(from disk) programm the z80A ctc like this:
channel 0: counter with no interrupt
channel 1: counter with interrupt on falling edge of the page signal of the display unit
channel 2: counter with interrupt on falling edge of the keyboard interrupt
channel 3: counter with interrupt on rising edge ot the fdd controller interrupt

30H-38H i8257 DMA
30H - DMA channel 0 - memory address for DMA transfer start
31H - DMA channel 0 - number of bytes to transfer
32H - DMA channel 1 - memory address for DMA transfer start
33H - DMA channel 1 - number of bytes to transfer
34H - DMA channel 2 - memory address for DMA transfer start
35H - DMA channel 2 - number of bytes to transfer
36H - DMA channel 3 - memory address for DMA transfer start
37H - DMA channel 4 - number of bytes to transfer
38H - state command of DMA chip
the only programmed channel is channel 1, used for data transfer between ram and fdd controller

40H-41H - I8272 floppy drive controller
40H - command and state of I8272
41H - dataport
48H - commands for fdd controller
D6,D7 - x
D5 - reset I8272 (0 active)
D4 - type of disk drive (0 - 8", 1- 5.25")
D0-3 - start stop drive motor (drive 0-3)
49H - load the scan address into the hardware in graphics mode
4AH - command port of display controller (discrete logic!)
D6,7 - x
D5 - blinking activated (1 active)
D4 - mode selection 0 = 320x200 1=640x200
D3 - display 0=off 1 = on
D2 - validate acces display ram (16k display ram on board) - 1 active
D1 - 0 - graphics mode / 1 - text mode
D0 - 0 = 40x25, 1= 80x25
4BH - color selection and text page selection for display controller
D6-7 - selection of working text page
D5 - select color palette
D4 - backgroung
D3 - bright
D2 - red
D1 - green
D0 - blue

4CH - address port - row - select the first row displayed in text mode
4DH - selection port of clock for transmit and receive for Z80 SIO
D1-7 - x
D0 - 0= internal clock, 1= external clock
4FH - reset port for serial keyboard - keyboard interrupt is deactivated by writing or reading of 4FH

50H-53H - z80A PIO
50H - data port A
51H - data port B
52H - interrupt vector A
53H - interrupt vector B
programmed in rom and from os like this: port A input with interrupt, port B - output with interrupt

60H-6FH ramdisk

70H-73H - i8253
70H - dataport channel 0
71H - dataport channel 1
72H - dataport channel 2
73H - command port I8253
- i8253 programmed in rom and from os like: channel 0 unprogrammed,
channel 1: clock divider for 9600 bps clock
channel 2: clock divider for sound generator
when the os loads, channel 0 is programmed like channel 1

***************************************************************************


To Do: Almost everything.
Status: Just a closet skeleton


***************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/pit8253.h"
#include "machine/i8255.h"
#include "machine/i8257.h"
#include "machine/upd765.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "machine/z80pio.h"
//#include "bus/rs232/rs232.h"


namespace {

class junior80_state : public driver_device
{
public:
	junior80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ctc(*this, "ctc")
		, m_dma(*this, "dma")
		, m_pio(*this, "pio")
		, m_pit(*this, "pit")
		, m_ppi(*this, "ppi")
		, m_uart(*this, "uart")
		, m_fdc(*this, "fdc")
		, m_floppy0(*this, "fdc:0")
		, m_floppy1(*this, "fdc:1")
		//, m_io_keyboard(*this, "LINE%u", 0U)
	{ }

	void junior80(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	void drive_w(offs_t offset, u8 data);

	floppy_image_device *m_floppy = 0;
	required_device<cpu_device> m_maincpu;
	required_device<z80ctc_device> m_ctc;
	required_device<i8257_device> m_dma;
	required_device<z80pio_device> m_pio;
	required_device<pit8253_device> m_pit;
	required_device<i8255_device> m_ppi;
	required_device<z80sio_device> m_uart;
	required_device<i8272a_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	required_device<floppy_connector> m_floppy1;
	//required_ioport_array<8> m_io_keyboard;
};


void junior80_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).ram();
	map(0x0000, 0x07ff).rom();
}

void junior80_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x03).mirror(0x0c).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x10, 0x13).mirror(0x0c).rw(m_uart, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x20, 0x23).mirror(0x0c).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x30, 0x38).rw(m_dma, FUNC(i8257_device::read), FUNC(i8257_device::write));
	map(0x40, 0x41).mirror(0x06).m(m_fdc, FUNC(i8272a_device::map));
	map(0x48, 0x48).w(FUNC(junior80_state::drive_w));
	//map(0x49, 0x4d) video/graphics control
	//map(0x4f, 0x4f) kbd ack
	map(0x50, 0x53).mirror(0x0c).rw(m_pio, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	//map(0x60, 0x6f) ramdisk
	map(0x70, 0x73).mirror(0x0c).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
}

static INPUT_PORTS_START( junior80 )
INPUT_PORTS_END


/*************************************
 *
 *              Port handlers.
 *
 *************************************/


void junior80_state::drive_w(offs_t offset, u8 data)
{
	m_floppy = nullptr;

	if (BIT(data, 0)) m_floppy = m_floppy0->get_device();
	if (BIT(data, 1)) m_floppy = m_floppy1->get_device();
	//if (BIT(data, 2)) m_floppy = m_floppy2->get_device();
	//if (BIT(data, 3)) m_floppy = m_floppy3->get_device();

	m_fdc->set_floppy(m_floppy);

	if (m_floppy)
	{
		m_floppy->mon_w(0);
		//m_floppy->ss_w(BIT(data, 4));
	}

	//m_fdc->dden_w(!BIT(data, 6));
	m_fdc->set_unscaled_clock(BIT(data, 4) ? 4000000 : 8000000);
	if (!BIT(data, 5))
		m_fdc->reset();
}


/*************************************
 *  Machine              *
 *************************************/

void junior80_state::machine_start()
{
}

void junior80_state::machine_reset()
{
	m_floppy = nullptr;
}

static void junior80_floppies(device_slot_interface &device)
{
	device.option_add("fdd", FLOPPY_525_QD);
}


void junior80_state::junior80(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 2'500'000);  // 2.5 or 4MHz selectable by jumpers
	m_maincpu->set_addrmap(AS_PROGRAM, &junior80_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &junior80_state::io_map);

	// devices
	I8272A(config, m_fdc, 8_MHz_XTAL / 2);
	FLOPPY_CONNECTOR(config, "fdc:0", junior80_floppies, "fdd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", junior80_floppies, "fdd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	Z80PIO(config, m_pio, 0);
	Z80SIO(config, m_uart, 0);
	Z80CTC(config, m_ctc, 0);
	PIT8253(config, m_pit, 0);
	I8255(config, m_ppi, 0);

	I8257(config, m_dma, 0);
	//m_dma->out_hrq_cb().set(FUNC(junior80_state::hrq_w));
	//m_dma->in_memr_cb().set(FUNC(junior80_state::memory_r));
	//m_dma->out_memw_cb().set(FUNC(junior80_state::memory_w));
	//m_dma->in_ior_cb<1>().set(m_fdc, FUNC(upd765_device::data_r));
	//m_dma->out_iow_cb<1>().set(m_fdc, FUNC(upd765_device::data_w));
}


/***************************************************************************

  Game driver(s)

***************************************************************************/


ROM_START(junior80)
	ROM_REGION(0x0800, "maincpu",0)
	ROM_LOAD( "junior80_seria_321-ok.ic46", 0x0000, 0x0800, CRC(07f09842) SHA1(c7591a1006ae59d6353859ca401c57ff6eb1d4ff) )
ROM_END

} // anonymous namespace


//    YEAR  NAME        PARENT    COMPAT    MACHINE     INPUT        CLASS           INIT            COMPANY          FULLNAME            FLAGS
COMP( 1988, junior80,   0,        0,        junior80,   junior80,    junior80_state, empty_init,     "<unknown>",     "Junior 80",        MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



juno106.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Roland Juno-106 synthesizer.

****************************************************************************/

#include "emu.h"
#include "mb63h114.h"
//#include "bus/midi/midi.h"
#include "cpu/upd7810/upd7810.h"
#include "machine/nvram.h"
#include "machine/pit8253.h"


namespace {

class juno106_state : public driver_device
{
public:
	juno106_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_modulecpu(*this, "modulecpu")
		, m_mac(*this, "mac")
		, m_pit(*this, "pit%u", 1U)
	{
	}

	void juno106(machine_config &config);
	void mks7(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void cpu_latch_w(u8 data);
	void module_pit_w(offs_t offset, u8 data);
	void module_latch_w(u8 data);

	void bass_w(u8 data);
	void dcom_w(u8 data);
	void rhythm_w(offs_t offset, u8 data);

	void main_map(address_map &map) ATTR_COLD;
	void mks7_main_map(address_map &map) ATTR_COLD;
	void module_map(address_map &map) ATTR_COLD;

	required_device<upd7810_device> m_maincpu;
	required_device<upd7810_device> m_modulecpu;
	optional_device<mb63h114_device> m_mac;
	required_device_array<pit8253_device, 2> m_pit;
};

void juno106_state::machine_start()
{
}


void juno106_state::cpu_latch_w(u8 data)
{
}

void juno106_state::bass_w(u8 data)
{
}

void juno106_state::dcom_w(u8 data)
{
}

void juno106_state::rhythm_w(offs_t offset, u8 data)
{
	m_mac->xst_w(offset & 0xff);
}

void juno106_state::module_pit_w(offs_t offset, u8 data)
{
	if (!BIT(offset, 12))
		m_pit[0]->write((offset & 0x300) >> 8, data);
	if (!BIT(offset, 13))
		m_pit[1]->write((offset & 0x300) >> 8, data);
}

void juno106_state::module_latch_w(u8 data)
{
}

void juno106_state::main_map(address_map &map)
{
	map(0x0000, 0x1fff).rom().region("assigner", 0);
	map(0x0000, 0x0000).mirror(0x1fff).w(FUNC(juno106_state::cpu_latch_w));
	map(0x2000, 0x27ff).mirror(0x1800).ram().share("nvram");
}

void juno106_state::mks7_main_map(address_map &map)
{
	map(0x0000, 0x1fff).rom().region("mainboard", 0);
	map(0x2800, 0x2800).mirror(0x07ff).w(FUNC(juno106_state::bass_w));
	map(0x3000, 0x3000).mirror(0x07ff).w(FUNC(juno106_state::dcom_w));
	map(0x3800, 0x3fff).w(FUNC(juno106_state::rhythm_w));
}

void juno106_state::module_map(address_map &map)
{
	map(0x0000, 0x0fff).mirror(0x3000).rom().region("module", 0);
	map(0x0000, 0x2fff).w(FUNC(juno106_state::module_pit_w));
	map(0x3000, 0x3000).mirror(0x0fff).w(FUNC(juno106_state::module_latch_w));
}


static INPUT_PORTS_START(juno106)
INPUT_PORTS_END

static INPUT_PORTS_START(mks7)
INPUT_PORTS_END

void juno106_state::juno106(machine_config &config)
{
	UPD7810(config, m_maincpu, 12_MHz_XTAL); // µPD7810G or µPD7811G-101-36
	m_maincpu->set_addrmap(AS_PROGRAM, &juno106_state::main_map);

	UPD7810(config, m_modulecpu, 12_MHz_XTAL); // µPD7810G or µPD7811G-102-36
	m_modulecpu->set_addrmap(AS_PROGRAM, &juno106_state::module_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // TC5517APL or MB8416-20L + battery

	PIT8253(config, m_pit[0]); // M82C53
	PIT8253(config, m_pit[1]); // M82C53
}

void juno106_state::mks7(machine_config &config)
{
	juno106(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &juno106_state::mks7_main_map);

	config.device_remove("nvram"); // no battery or external RAM

	MB63H114(config, "mac", 1.6_MHz_XTAL);
}

ROM_START(juno106)
	ROM_REGION(0x2000, "assigner", 0)
	ROM_LOAD("v5 a_5.ic2", 0x0000, 0x2000, CRC(4818686b) SHA1(4ab414a70281d382cb45478a62a6467e8dbb0a5d))

	ROM_REGION(0x2000, "module", 0)
	ROM_LOAD("v2 b_2.ic37", 0x0000, 0x2000, CRC(e0dc721e) SHA1(892b919a2476b269d916ba01dd5a81a25e044171))
ROM_END

ROM_START(mks7)
	ROM_REGION(0x2000, "mainboard", 0)
	ROM_LOAD("mks7-a-main.ic43", 0x0000, 0x2000, CRC(27d72dfb) SHA1(f5ed299e87d42ffccff70d8f0a406385050b4f46))

	ROM_REGION(0x10000, "mac", 0)
	ROM_LOAD("hn61256p_c71.ic14", 0x0000, 0x8000, NO_DUMP)
	ROM_LOAD("hn61256p_c72.ic13", 0x8000, 0x8000, NO_DUMP)

	ROM_REGION(0x8000, "ride", 0)
	ROM_LOAD("hn61256p_c44.ic22", 0x0000, 0x8000, NO_DUMP)

	ROM_REGION(0x8000, "crash", 0)
	ROM_LOAD("hn61256p_c42.ic21", 0x0000, 0x8000, NO_DUMP)

	ROM_REGION(0x2000, "module", 0)
	ROM_LOAD("mks7-b-module.ic38", 0x0000, 0x2000, CRC(306c6c54) SHA1(725e24199056c2788380b70acbc1a3842780e225))
ROM_END

} // anonymous namespace


SYST(1984, juno106, 0, 0, juno106, juno106, juno106_state, empty_init, "Roland", "Juno-106 Programmable Polyphonic Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1985, mks7,    0, 0, mks7,    mks7,    juno106_state, empty_init, "Roland", "MKS-7 Super Quartet",                          MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



juno6.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Roland Juno-6 analog synthesizer.

    The original JU-6 had no MIDI support, but this feature could be and was
    added through retrofits since the same 8049 program supports MIDI on the
    Juno-60.

****************************************************************************/

#include "emu.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/74259.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/pit8253.h"


namespace {

class juno6_state : public driver_device
{
public:
	juno6_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pit(*this, "pit%u", 1U)
		, m_latch(*this, "latch")
		, m_usart(*this, "usart")
	{
	}

	void juno6(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	u8 keyboard_r();
	u8 db_r();
	void db_w(u8 data);
	void p2_w(u8 data);

	void ext_map(address_map &map) ATTR_COLD;

	required_device<mcs48_cpu_device> m_maincpu;
	required_device_array<pit8253_device, 2> m_pit;
	required_device<cd4099_device> m_latch;
	required_device<i8251_device> m_usart;

	u8 m_db = 0;
};

void juno6_state::machine_start()
{
	save_item(NAME(m_db));
}


u8 juno6_state::keyboard_r()
{
	return 0;
}

u8 juno6_state::db_r()
{
	u8 p2 = m_maincpu->p2_r();

	if (!BIT(p2, 3))
		return m_usart->read(p2 & 0x01);

	return 0xff;
}

void juno6_state::db_w(u8 data)
{
	u8 p2 = m_maincpu->p2_r();

	if (!BIT(p2, 3))
		m_usart->write(p2 & 0x01, data);
	if (!BIT(p2, 4))
		m_pit[0]->write(p2 & 0x03, data);
	if (!BIT(p2, 5))
		m_pit[1]->write(p2 & 0x03, data);

	m_db = data;
}

void juno6_state::p2_w(u8 data)
{
	if (!BIT(data, 7))
		m_latch->write_bit(data & 0x07, BIT(m_db, 7));
}

void juno6_state::ext_map(address_map &map)
{
	map(0x00, 0x00).mirror(0xff).r(FUNC(juno6_state::db_r));
}


static INPUT_PORTS_START(juno6)
INPUT_PORTS_END

void juno6_state::juno6(machine_config &config)
{
	I8049(config, m_maincpu, 11_MHz_XTAL);
	m_maincpu->set_addrmap(AS_IO, &juno6_state::ext_map);
	m_maincpu->p1_in_cb().set(FUNC(juno6_state::keyboard_r));
	m_maincpu->bus_out_cb().set(FUNC(juno6_state::db_w));
	m_maincpu->p2_out_cb().set(FUNC(juno6_state::p2_w));

	PIT8253(config, m_pit[0]);
	PIT8253(config, m_pit[1]);

	CD4099(config, m_latch);

	I8251(config, m_usart, 4_MHz_XTAL / 2);
	m_usart->rts_handler().set(m_usart, FUNC(i8251_device::write_cts));

	clock_device &midiclock(CLOCK(config, "midiclock", 4_MHz_XTAL / 8));
	midiclock.signal_handler().set(m_usart, FUNC(i8251_device::write_txc));
	midiclock.signal_handler().append(m_usart, FUNC(i8251_device::write_txc));
}

ROM_START(juno6)
	ROM_REGION(0x800, "maincpu", 0)
	ROM_LOAD("upd8049c-238.ic56", 0x000, 0x800, CRC(18fa7cc5) SHA1(eebda5bbe6c3153a5549de1853e69da403811df3))
ROM_END

} // anonymous namespace


SYST(1982, juno6, 0, 0, juno6, juno6, juno6_state, empty_init, "Roland", "Juno-6 (JU-6) Polyphonic Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



jupace.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder, Robbbert
/***************************************************************************

Jupiter Ace memory map

    CPU: Z80
        0000-1fff ROM
        2000-23ff Mirror of 2400-27FF
        2400-27ff RAM (1K RAM used for screen and edit/cassette buffer)
        2800-2bff Mirror of 2C00-2FFF
        2c00-2fff RAM (1K RAM for char set, write only)
        3000-3bff Mirror of 3C00-3FFF
        3c00-3fff RAM (1K RAM standard)
        4000-7fff RAM (16K Expansion)
        8000-ffff RAM (48K Expansion)

Interrupts:

    IRQ:
        50Hz vertical sync

Ports:

    Out 0xfe:
        Tape and buzzer

    In 0xfe:
        Keyboard input, tape, and buzzer

    https://www.jupiter-ace.co.uk/

TODO:

    - Ace Colour Board
    - 96K ram expansion
    - Deep Thought disc interface (6850, 6821)

***************************************************************************/


#include "emu.h"
#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "bus/centronics/ctronics.h"
#include "machine/i8255.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "machine/z80pio.h"
#include "sound/ay8910.h"
#include "sound/sp0256.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/ace_tap.h"


namespace {

class ace_state : public driver_device
{
public:
	ace_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ppi(*this, "i8255")
		, m_z80pio(*this, "z80pio")
		, m_speaker(*this, "speaker")
		, m_cassette(*this, "cassette")
		, m_centronics(*this, "centronics")
		, m_ram(*this, "ram")
		, m_sp0256(*this, "sp0256")
		, m_video_ram(*this, "video_ram")
		, m_char_ram(*this, "char_ram")
		, m_key(*this, "KEY%u", 0)
		, m_joy(*this, "JOY")
	{ }

	void ace(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	SNAPSHOT_LOAD_MEMBER(snapshot_cb);
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	uint8_t io_r(offs_t offset);
	void io_w(uint8_t data);

	template<int N> uint8_t ppi_r() { return m_ppi->read(N); }
	template<int N> void ppi_w(uint8_t data) { m_ppi->write(N, data); }
	uint8_t sby_r();
	void ald_w(uint8_t data);

	template<int N> uint8_t pio_r() { return m_z80pio->read(N); }
	template<int N> void pio_w(uint8_t data) { m_z80pio->write(N, data); }
	uint8_t pio_pa_r();
	void pio_pa_w(uint8_t data);

	TIMER_DEVICE_CALLBACK_MEMBER(set_irq);
	TIMER_DEVICE_CALLBACK_MEMBER(clear_irq);

	void ace_io(address_map &map) ATTR_COLD;
	void ace_mem(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<i8255_device> m_ppi;
	required_device<z80pio_device> m_z80pio;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cassette;
	required_device<centronics_device> m_centronics;
	required_device<ram_device> m_ram;
	required_device<sp0256_device> m_sp0256;
	required_shared_ptr<uint8_t> m_video_ram;
	required_shared_ptr<uint8_t> m_char_ram;
	required_ioport_array<8> m_key;
	required_ioport m_joy;
};


/* Load in .ace files. These are memory images of 0x2000 to 0x7fff
   and compressed as follows:

   ED 00        : End marker
   ED <cnt> <byt>   : repeat <byt> count <cnt:1-240> times
   <byt>        : <byt>
*/

//**************************************************************************
//  Snapshot Handling
//**************************************************************************

SNAPSHOT_LOAD_MEMBER(ace_state::snapshot_cb)
{
	if (m_ram->size() < 0x4000)
		return std::make_pair(image_error::UNSUPPORTED, "At least 16KB RAM expansion required");

	u16 ace_index = 0x2000;
	bool done = false;

	logerror("Loading file %s.\r\n", image.filename());
	std::vector<uint8_t> RAM(0x10000);
	while (!done && (ace_index < 0x8001))
	{
		uint8_t ace_repeat, ace_byte;
		image.fread(&ace_byte, 1);
		if (ace_byte == 0xed)
		{
			image.fread(&ace_byte, 1);
			switch (ace_byte)
			{
			case 0x00:
					logerror("File loaded!\r\n");
					done = true;
					break;
			case 0x01:
					image.fread(&ace_byte, 1);
					RAM[ace_index++] = ace_byte;
					break;
			default:
					image.fread(&ace_repeat, 1);
					for (u8 loop = 0; loop < ace_byte; loop++)
						RAM[ace_index++] = ace_repeat;
					break;
			}
		}
		else
			RAM[ace_index++] = ace_byte;
	}

	logerror("Decoded %X bytes.\r\n", ace_index-0x2000);

	if (!done)
		return std::make_pair(image_error::INVALIDIMAGE, "Invalid snapshot file: EOF marker not found");

	// patch CPU registers
	// Some games do not follow the standard, and have rubbish in the CPU area. So,
	// we check that some other bytes are correct.
	// 2080 = memory size of original machine, should be 0000 or 8000 or C000.
	// 2118 = new stack pointer, do not use if between 8000 and FF00.

	ace_index = RAM[0x2080] | (RAM[0x2081] << 8);

	cpu_device *cpu = m_maincpu;
	if ((ace_index & 0x3fff) == 0)
	{
		cpu->set_state_int(Z80_AF, RAM[0x2100] | (RAM[0x2101] << 8));
		cpu->set_state_int(Z80_BC, RAM[0x2104] | (RAM[0x2105] << 8));
		cpu->set_state_int(Z80_DE, RAM[0x2108] | (RAM[0x2109] << 8));
		cpu->set_state_int(Z80_HL, RAM[0x210c] | (RAM[0x210d] << 8));
		cpu->set_state_int(Z80_IX, RAM[0x2110] | (RAM[0x2111] << 8));
		cpu->set_state_int(Z80_IY, RAM[0x2114] | (RAM[0x2115] << 8));
		cpu->set_pc(RAM[0x211c] | (RAM[0x211d] << 8));
		cpu->set_state_int(Z80_AF2, RAM[0x2120] | (RAM[0x2121] << 8));
		cpu->set_state_int(Z80_BC2, RAM[0x2124] | (RAM[0x2125] << 8));
		cpu->set_state_int(Z80_DE2, RAM[0x2128] | (RAM[0x2129] << 8));
		cpu->set_state_int(Z80_HL2, RAM[0x212c] | (RAM[0x212d] << 8));
		cpu->set_state_int(Z80_IM, RAM[0x2130]);
		cpu->set_state_int(Z80_IFF1, RAM[0x2134]);
		cpu->set_state_int(Z80_IFF2, RAM[0x2138]);
		cpu->set_state_int(Z80_I, RAM[0x213c]);
		cpu->set_state_int(Z80_R, RAM[0x2140]);

		if ((RAM[0x2119] < 0x80) || !ace_index)
			cpu->set_state_int(Z80_SP, RAM[0x2118] | (RAM[0x2119] << 8));
	}

	// copy data to the address space
	address_space &space = cpu->space(AS_PROGRAM);
	for (ace_index = 0x2000; ace_index < 0x8000; ace_index++)
		space.write_byte(ace_index, RAM[ace_index]);

	return std::make_pair(std::error_condition(), std::string());
}



//**************************************************************************
//  READ/WRITE HANDLERS
//**************************************************************************

//-------------------------------------------------
//  I/O port 0xFE
//-------------------------------------------------

uint8_t ace_state::io_r(offs_t offset)
{
	uint8_t data = 0xff;

	// read keyboard rows
	for (int i = 0; i < 8; i++)
		if (!BIT(offset, i + 8))
			data &= m_key[i]->read();

	if (m_cassette->input() > 0.03)
		data &= ~0x20;

	if (!machine().side_effects_disabled())
		m_speaker->level_w(0);

	return data;
}

void ace_state::io_w(uint8_t data)
{
	m_cassette->output(BIT(data, 3) ? +1.0 : -1.0);
	m_speaker->level_w(1);
}


//-------------------------------------------------
//  I8255A
//-------------------------------------------------

uint8_t ace_state::sby_r()
{
	/*

	    bit     description

	    PC0     SP0256 SBY
	    PC1
	    PC2
	    PC3
	    PC4
	    PC5
	    PC6
	    PC7

	*/

	return m_sp0256->sby_r();
}

void ace_state::ald_w(uint8_t data)
{
	/*

	    bit     description

	    PA0     SP0256 A1
	    PA1     SP0256 A2
	    PA2     SP0256 A3
	    PA3     SP0256 A4
	    PA4     SP0256 A5
	    PA5     SP0256 A6
	    PA6     SP0256 _ALD
	    PA7

	*/

	if (!BIT(data, 6))
	{
		m_sp0256->ald_w(data & 0x3f);
	}
}


//-------------------------------------------------
//  Z80PIO
//-------------------------------------------------

uint8_t ace_state::pio_pa_r()
{
	/*

	    bit     description

	    PA0
	    PA1     RxD
	    PA2
	    PA3
	    PA4
	    PA5
	    PA6
	    PA7

	*/

	return 0;
}

void ace_state::pio_pa_w(uint8_t data)
{
	/*

	    bit     description

	    PA0     RTS
	    PA1
	    PA2     CTS
	    PA3     TxD
	    PA4
	    PA5
	    PA6     STB
	    PA7

	*/

	// centronics strobe
	m_centronics->write_strobe(!BIT(data, 6));
}



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void ace_state::ace_mem(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x2000, 0x23ff).mirror(0x0400).ram().share(m_video_ram);
	map(0x2800, 0x2bff).mirror(0x0400).ram().share(m_char_ram);
	map(0x3000, 0x33ff).mirror(0x0c00).ram();
	map(0x4000, 0xffff).ram();
}

void ace_state::ace_io(address_map &map)
{
	map(0x00, 0x00).mirror(0x00fe).select(0xff00).rw(FUNC(ace_state::io_r), FUNC(ace_state::io_w));
	map(0x01, 0x01).mirror(0xff00).portr(m_joy);
	map(0x41, 0x41).mirror(0xff80).rw(FUNC(ace_state::ppi_r<0>), FUNC(ace_state::ppi_w<0>));
	map(0x43, 0x43).mirror(0xff80).rw(FUNC(ace_state::ppi_r<1>), FUNC(ace_state::ppi_w<1>));
	map(0x45, 0x45).mirror(0xff80).rw(FUNC(ace_state::ppi_r<2>), FUNC(ace_state::ppi_w<2>));
	map(0x47, 0x47).mirror(0xff80).rw(FUNC(ace_state::ppi_r<3>), FUNC(ace_state::ppi_w<3>));
	map(0x81, 0x81).mirror(0xff38).rw(FUNC(ace_state::pio_r<0>), FUNC(ace_state::pio_w<0>));
	map(0x83, 0x83).mirror(0xff38).rw(FUNC(ace_state::pio_r<1>), FUNC(ace_state::pio_w<1>));
	map(0x85, 0x85).mirror(0xff38).rw(FUNC(ace_state::pio_r<2>), FUNC(ace_state::pio_w<2>));
	map(0x87, 0x87).mirror(0xff38).rw(FUNC(ace_state::pio_r<3>), FUNC(ace_state::pio_w<3>));
	map(0xfd, 0xfd).mirror(0xff00).w("ay8910", FUNC(ay8910_device::address_w));
	map(0xff, 0xff).mirror(0xff00).rw("ay8910", FUNC(ay8910_device::data_r), FUNC(ay8910_device::data_w));
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

static INPUT_PORTS_START( ace )
	PORT_START("KEY0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RSHIFT)     PORT_CHAR(UCHAR_SHIFT_2) PORT_NAME("Symbol Shift")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(':')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)          PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(U'£')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)          PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR('?')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)          PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR('~')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)          PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR('|')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)          PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR('\\')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)          PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR('{')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)          PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR('}')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)          PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)          PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)          PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR('<')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)          PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR('>')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!') // DELETE LINE
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('@') // CAPS LOCK
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$') // INVERSE VIDEO
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_LEFT) PORT_CHAR('5') PORT_CHAR('%') PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0)          PORT_CHAR('0') PORT_CHAR('_') // DELETE
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')') // GRAPHICS
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR('8') PORT_CHAR('(') PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_DOWN)  PORT_CHAR('7') PORT_CHAR('\'') PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_UP)    PORT_CHAR('6') PORT_CHAR('&') PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR('"')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)          PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(';')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I') PORT_CHAR(U'©')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(']')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR('[')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)          PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR('=')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)          PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR('+')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)          PORT_CHAR('j') PORT_CHAR('J') PORT_CHAR('-')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)          PORT_CHAR('h') PORT_CHAR('H') PORT_CHAR('^') PORT_NAME(u8"h  H  \u2191") // U+2191 = ↑
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ') PORT_NAME("Space / Break")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)          PORT_CHAR('m') PORT_CHAR('M') PORT_CHAR('.')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)          PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(',')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)          PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR('*')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)          PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR('/')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("JOY")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END



//**************************************************************************
//  VIDEO
//**************************************************************************

//-------------------------------------------------
//  gfx_layout
//-------------------------------------------------

static const gfx_layout ace_charlayout =
{
	8,8,
	128,
	1,
	{ RGN_FRAC(0,1) },
	{ STEP8(0,1) },
	{ STEP8(0,8) },
	8*8
};

static GFXDECODE_START( gfx_ace )
	GFXDECODE_RAM( "char_ram", 0, ace_charlayout, 0, 1 )
GFXDECODE_END


//-------------------------------------------------
//  interrupt
//-------------------------------------------------

TIMER_DEVICE_CALLBACK_MEMBER(ace_state::set_irq)
{
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
}

TIMER_DEVICE_CALLBACK_MEMBER(ace_state::clear_irq)
{
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
}


//-------------------------------------------------
//  screen_update
//-------------------------------------------------

uint32_t ace_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint16_t sy = 56, ma = 0;
	for (uint8_t y = 0; y < 24; y++)
	{
		for (uint8_t ra = 0; ra < 8; ra++)
		{
			uint16_t *p = &bitmap.pix(sy++, 40);

			for (uint16_t x = ma; x < ma+32; x++)
			{
				uint8_t const chr = m_video_ram[x];

				// get pattern of pixels for that character scanline
				uint8_t const gfx = m_char_ram[((chr & 0x7f) << 3) | ra] ^ (BIT(chr, 7) ? 0xff : 0);

				// display a scanline of a character (8 pixels)
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma += 32;
	}
	return 0;
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

void ace_state::machine_start()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	// configure RAM
	switch (m_ram->size())
	{
	case 1*1024:
		program.unmap_readwrite(0x4000, 0xffff);
		break;

	case 16*1024:
		program.unmap_readwrite(0x8000, 0xffff);
		break;

	case 32*1024:
		program.unmap_readwrite(0xc000, 0xffff);
		break;
	}
}



//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

void ace_state::ace(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 6.5_MHz_XTAL/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &ace_state::ace_mem);
	m_maincpu->set_addrmap(AS_IO, &ace_state::ace_io);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_screen_update(FUNC(ace_state::screen_update));
	screen.set_raw(6.5_MHz_XTAL, 416, 0, 336, 312, 0, 304);
	screen.set_palette("palette");

	TIMER(config, "set_irq").configure_scanline(FUNC(ace_state::set_irq), "screen", 31*8, 264);
	TIMER(config, "clear_irq").configure_scanline(FUNC(ace_state::clear_irq), "screen", 32*8, 264);

	PALETTE(config, "palette", palette_device::MONOCHROME);

	GFXDECODE(config, "gfxdecode", "palette", gfx_ace);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 1.00);

	AY8910(config, "ay8910", 6.5_MHz_XTAL / 2).add_route(ALL_OUTPUTS, "mono", 0.25);

	SP0256(config, m_sp0256, 3_MHz_XTAL);
	m_sp0256->add_route(ALL_OUTPUTS, "mono", 0.25);

	// devices
	CASSETTE(config, m_cassette);
	m_cassette->set_formats(ace_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("jupace_cass");

	I8255A(config, m_ppi);
	m_ppi->in_pb_callback().set(FUNC(ace_state::sby_r));
	m_ppi->out_pb_callback().set(FUNC(ace_state::ald_w));

	Z80PIO(config, m_z80pio, 6.5_MHz_XTAL/2);
	m_z80pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_z80pio->in_pa_callback().set(FUNC(ace_state::pio_pa_r));
	m_z80pio->out_pa_callback().set(FUNC(ace_state::pio_pa_w));
	m_z80pio->out_pb_callback().set("cent_data_out", FUNC(output_latch_device::write));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	// internal ram
	RAM(config, "ram").set_default_size("1K").set_extra_options("16K,32K,48K");

	// snapshot
	snapshot_image_device &snapshot(SNAPSHOT(config, "snapshot", "ace"));
	snapshot.set_delay(attotime::from_double(1.0));
	snapshot.set_load_callback(FUNC(ace_state::snapshot_cb));
	snapshot.set_interface("jupace_snap");

	SOFTWARE_LIST(config, "cass_list").set_original("jupace_cass");
	SOFTWARE_LIST(config, "snap_list").set_original("jupace_snap");
}



//**************************************************************************
//  ROMS
//**************************************************************************

ROM_START( jupace )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "rom-a.z1", 0x0000, 0x1000, CRC(dc8438a5) SHA1(8fa97eb71e5dd17c7d190c6587ee3840f839347c) )
	ROM_LOAD( "rom-b.z2", 0x1000, 0x1000, CRC(4009f636) SHA1(98c5d4bcd74bcf014268cf4c00b2007ea5cc21f3) )

	ROM_REGION( 0x1000, "fdc", 0 ) // Deep Thought disc interface
	ROM_LOAD( "dos_4.bin", 0x0000, 0x1000, CRC(04c70448) SHA1(53ddcced6ae2feafd687a3b55864726656b71412) )

	ROM_REGION( 0x800, "sp0256", 0 )
	ROM_LOAD( "sp0256-al2.ic1", 0x000, 0x800, CRC(b504ac15) SHA1(e60fcb5fa16ff3f3b69d36c7a6e955744d3feafc) )
ROM_END

} // anonymous namespace



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY           FULLNAME       FLAGS
COMP( 1981, jupace, 0,      0,      ace,     ace,   ace_state, empty_init, "Jupiter Cantab", "Jupiter Ace", MACHINE_SUPPORTS_SAVE )



jupiter.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/**************************************************************************

Wave Mate Jupiter


Jupiter 2
*********
Status: Preliminary
Doesn't show anything until a disk is loaded



Jupiter 3
*********
Status: Preliminary
Hangs if your input line starts with 'k'.



ToDo: (both)
- Connect all devices
- Everything!

***************************************************************************/

#include "emu.h"
#include "cpu/m6800/m6800.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/keyboard.h"
#include "machine/6850acia.h"
#include "bus/rs232/rs232.h"
#include "machine/ram.h"
#include "machine/wd_fdc.h"
#include "emupal.h"
#include "screen.h"


namespace {

#define MCM6571AP_TAG   "vid125_6c"
#define S6820_TAG       "vid125_4a"
#define Z80_TAG         "cpu126_4c"
#define INS1771N1_TAG   "fdi027_4c"
#define MC6820P_TAG     "fdi027_4b"
#define MC6850P_TAG     "rsi068_6a"
#define MC6821P_TAG     "sdm058_4b"

class jupiter2_state : public driver_device
{
public:
	jupiter2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, MCM6571AP_TAG)
		, m_acia0(*this, "acia0")
		, m_acia1(*this, "acia1")
	{ }

	void jupiter2(machine_config &config);

	void init_jupiter2();

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void jupiter2_mem(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<acia6850_device> m_acia0;
	required_device<acia6850_device> m_acia1;
};

class jupiter3_state : public driver_device
{
public:
	jupiter3_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, Z80_TAG)
		, m_p_videoram(*this, "videoram")
		, m_p_ram(*this, "ram")
		, m_p_chargen(*this, "chargen")
	{ }

	void jupiter3(machine_config &config);

	void init_jupiter3();

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void kbd_put(u8 data);
	uint8_t status_r();
	uint8_t key_r();
	uint8_t ff_r();

	void jupiter3_io(address_map &map) ATTR_COLD;
	void jupiter3_mem(address_map &map) ATTR_COLD;

	uint8_t m_term_data = 0U;
	required_device<cpu_device> m_maincpu;
	required_shared_ptr<uint8_t> m_p_videoram;
	required_shared_ptr<uint8_t> m_p_ram;
	required_region_ptr<u8> m_p_chargen;
};



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( jupiter_m6800_mem )
//-------------------------------------------------

void jupiter2_state::jupiter2_mem(address_map &map)
{
	map(0x0000, 0x7fff).ram();
	map(0xc000, 0xcfff).ram();  // Video RAM
	map(0xf000, 0xff00).rom().region(MCM6571AP_TAG, 0);
//  map(0xff58, 0xff5c) Cartridge Disk Controller PIA
//  map(0xff60, 0xff76) DMA Controller
//  map(0xff80, 0xff83) Floppy PIA
	map(0xff84, 0xff87).rw(INS1771N1_TAG, FUNC(wd_fdc_device_base::read), FUNC(wd_fdc_device_base::write));
//  map(0xff90, 0xff93) Hytype Parallel Printer PIA
//  map(0xffa0, 0xffa7) Persci Floppy Disk Controller
//  map(0xffb0, 0xffb3) Video PIA
	map(0xffc0, 0xffc1).rw(m_acia0, FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // Serial Port 0 ACIA
	map(0xffc4, 0xffc5).rw(m_acia1, FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // Serial Port 1 ACIA
//  map(0xffc8, 0xffc9) Serial Port 2 ACIA
//  map(0xffcc, 0xffcd) Serial Port 3 ACIA
//  map(0xffd0, 0xffd1) Serial Port 4 ACIA / Cassette
//  map(0xffd4, 0xffd5) Serial Port 5 ACIA / EPROM Programmer (2704/2708)
//  map(0xffd8, 0xffd9) Serial Port 6 ACIA / Hardware Breakpoint Registers
//  map(0xffdc, 0xffdd) Serial Port 7 ACIA
	map(0xfff8, 0xffff).rom().region(MCM6571AP_TAG, 0x0ff8); // vectors
}



//-------------------------------------------------
//  ADDRESS_MAP( jupiter3_mem )
//-------------------------------------------------

void jupiter3_state::jupiter3_mem(address_map &map)
{
	map(0x0000, 0xbfff).ram().share("ram");
	map(0xc000, 0xdfff).ram().share("videoram");
	map(0xe000, 0xefff).rom().region(Z80_TAG, 0);
	map(0xf000, 0xffff).ram();
}


//-------------------------------------------------
//  ADDRESS_MAP( jupiter3_io )
//-------------------------------------------------

void jupiter3_state::jupiter3_io(address_map &map)
{
	map.global_mask(0xff);
	map(0xa1, 0xa4).r(FUNC(jupiter3_state::ff_r));
	map(0xb0, 0xb0).r(FUNC(jupiter3_state::status_r));
	map(0xb2, 0xb2).r(FUNC(jupiter3_state::key_r));
}

uint8_t jupiter3_state::ff_r()
{
	return 0xfd;
}

//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( jupiter )
//-------------------------------------------------

static INPUT_PORTS_START( jupiter )
INPUT_PORTS_END

uint8_t jupiter3_state::key_r()
{
	uint8_t ret = m_term_data;
	m_term_data = 0;
	return ret;
}

uint8_t jupiter3_state::status_r()
{
	return (m_term_data) ? 0x80 : 0x00;
}

void jupiter3_state::kbd_put(u8 data)
{
	if (data)
		m_term_data = data ^ 0x80;
}


//**************************************************************************
//  VIDEO
//**************************************************************************

uint32_t jupiter3_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint16_t sy=0,ma=0;

	for (uint8_t y = 0; y < 32; y++)
	{
		for (uint8_t ra = 0; ra < 10; ra++)
		{
			uint16_t *p = &bitmap.pix(sy++);

			for (uint16_t x = ma; x < ma + 64; x++)
			{
				uint8_t gfx = 0;
				if (ra < 9)
				{
					uint8_t chr = m_p_videoram[x];
					gfx = m_p_chargen[(chr<<4) | ra ];
				}

				/* Display a scanline of a character */
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma+=64;
	}
	return 0;
}




//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

static void jupiter_floppies(device_slot_interface &device)
{
	device.option_add("525ssdd", FLOPPY_525_SSDD);
}


//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( jupiter )
//-------------------------------------------------

void jupiter2_state::machine_start()
{
}


//-------------------------------------------------
//  MACHINE_START( jupiter3 )
//-------------------------------------------------

void jupiter3_state::machine_reset()
{
	uint8_t* ROM = memregion(Z80_TAG)->base();
	memcpy(m_p_ram, ROM, 0x1000);
	m_maincpu->set_pc(0xe000);

	m_term_data = 0;
}



//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  machine_config( jupiter )
//-------------------------------------------------

void jupiter2_state::jupiter2(machine_config &config)
{
	// basic machine hardware
	M6800(config, m_maincpu, 2000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &jupiter2_state::jupiter2_mem);

	// devices
	FD1771(config, INS1771N1_TAG, 1000000);
	FLOPPY_CONNECTOR(config, INS1771N1_TAG":0", jupiter_floppies, "525ssdd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, INS1771N1_TAG":1", jupiter_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);

	ACIA6850(config, m_acia0, XTAL(2'000'000)); // unknown frequency
	m_acia0->txd_handler().set("serial0", FUNC(rs232_port_device::write_txd));
	m_acia0->rts_handler().set("serial0", FUNC(rs232_port_device::write_rts));

	rs232_port_device &serial0(RS232_PORT(config, "serial0", default_rs232_devices, "terminal"));
	serial0.rxd_handler().set(m_acia0, FUNC(acia6850_device::write_rxd));
	serial0.cts_handler().set(m_acia0, FUNC(acia6850_device::write_cts));

	ACIA6850(config, m_acia1, XTAL(2'000'000)); // unknown frequency
	m_acia1->txd_handler().set("serial1", FUNC(rs232_port_device::write_txd));
	m_acia1->rts_handler().set("serial1", FUNC(rs232_port_device::write_rts));

	rs232_port_device &serial1(RS232_PORT(config, "serial1", default_rs232_devices, "terminal"));
	serial1.rxd_handler().set(m_acia1, FUNC(acia6850_device::write_rxd));
	serial1.cts_handler().set(m_acia1, FUNC(acia6850_device::write_cts));

	// internal ram
	RAM(config, RAM_TAG).set_default_size("64K");
}


//-------------------------------------------------
//  machine_config( jupiter3 )
//-------------------------------------------------

void jupiter3_state::jupiter3(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 4000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &jupiter3_state::jupiter3_mem);
	m_maincpu->set_addrmap(AS_IO, &jupiter3_state::jupiter3_io);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(jupiter3_state::screen_update));
	screen.set_size(512, 320);
	screen.set_visarea(0, 512-1, 0, 320-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	// devices
	FD1771(config, INS1771N1_TAG, 1000000);
	FLOPPY_CONNECTOR(config, INS1771N1_TAG":0", jupiter_floppies, "525ssdd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, INS1771N1_TAG":1", jupiter_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);

	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(jupiter3_state::kbd_put));

	// internal ram
	RAM(config, RAM_TAG).set_default_size("64K");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( jupiter2 )
//-------------------------------------------------

ROM_START( jupiter2 )
	ROM_REGION( 0x1000, MCM6571AP_TAG, ROMREGION_INVERT ) // address and data lines are inverted
	ROM_LOAD( "idb v1.1 for 60k jii f000.1c", 0x0000, 0x0400, CRC(50893aae) SHA1(da0222c4cb6188f6cfc657fc33558d0a6a41cd1a) )
	ROM_LOAD( "idb v1.1 for 60k jii f400.6c", 0x0400, 0x0400, CRC(a435344a) SHA1(bc4f4143301b10ec762ecc0cb69e5a9d4c4bef7b) )
	ROM_LOAD( "idb v1.1 for 60k jii f800.1d", 0x0800, 0x0400, CRC(ab82df45) SHA1(be7ea5347ff0582401e26c2fa10e13463cbe57c6) )
	ROM_LOAD( "boot_v2.6_sn5d00000000000003_fc00.6d", 0x0c00, 0x0400, CRC(8f33e4ed) SHA1(fb206e5019c166583ff516de3608ae86d2636d2a) )
	ROM_LOAD( "jupiter ii boot rom v2.6 12_18_82 s_n 5d000...0015.6d", 0x0c00, 0x0400, CRC(f87cefdf) SHA1(229ea961e6036ec39e0ae33abc7f554bf9d8361b) )
ROM_END


//-------------------------------------------------
//  ROM( jupiter3 )
//-------------------------------------------------

ROM_START( jupiter3 )
	ROM_REGION( 0x1000, Z80_TAG, ROMREGION_INVERT ) // address and data lines are inverted
	ROM_LOAD( "jove 2.0 78_034 4v2d000 1.1c", 0x0000, 0x0400, CRC(be92a76c) SHA1(9c7d9b37c2bbf0c2e9465421e3e1bcf3dd9e66a6) )
	ROM_LOAD( "jove 2.0 78_034 4v2d000 2.6c", 0x0400, 0x0400, CRC(ee98dd32) SHA1(0513261c7c0d911225ea957ee67394871a36ada4) )
	ROM_LOAD( "jove 2.0 78_034 4v2d000 3.1d", 0x0800, 0x0400, CRC(51476b1d) SHA1(ab6f4eb244bcf9718aafdae67da086ec81f33fa6) )
	ROM_LOAD( "jove 2.0 78_034 4v2d000 4.6d", 0x0c00, 0x0400, CRC(16a9595d) SHA1(06150278650590497732e1f3f42356de56737921) )

	// character generator is missing, using one from c10 for now
	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "c10_char.bin", 0x0000, 0x2000, BAD_DUMP CRC(cb530b6f) SHA1(95590bbb433db9c4317f535723b29516b9b9fcbf))
ROM_END



//**************************************************************************
//  DRIVER INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  DRIVER_INIT( jupiter )
//-------------------------------------------------

void jupiter2_state::init_jupiter2()
{
	uint8_t *rom = memregion(MCM6571AP_TAG)->base();
	uint8_t inverted[0x1000];

	memcpy(inverted, rom, 0x1000);

	for (offs_t addr = 0; addr < 0x400; addr++)
	{
		// invert address lines
		rom[0x3ff - addr] = inverted[addr];
		rom[0x7ff - addr] = inverted[addr + 0x400];
		rom[0xbff - addr] = inverted[addr + 0x800];
		rom[0xfff - addr] = inverted[addr + 0xc00];
	}
}


//-------------------------------------------------
//  DRIVER_INIT( jupiter3 )
//-------------------------------------------------

void jupiter3_state::init_jupiter3()
{
	uint8_t *rom = memregion(Z80_TAG)->base();
	uint8_t inverted[0x1000];

	memcpy(inverted, rom, 0x1000);

	for (offs_t addr = 0; addr < 0x400; addr++)
	{
		// invert address lines
		rom[0x3ff - addr] = inverted[addr];
		rom[0x7ff - addr] = inverted[addr + 0x400];
		rom[0xbff - addr] = inverted[addr + 0x800];
		rom[0xfff - addr] = inverted[addr + 0xc00];
	}
}

} // Anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT    CLASS           INIT           COMPANY      FULLNAME       FLAGS
COMP( 1976, jupiter2, 0,      0,      jupiter2, jupiter, jupiter2_state, init_jupiter2, "Wave Mate", "Jupiter II",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )
COMP( 1976, jupiter3, 0,      0,      jupiter3, jupiter, jupiter3_state, init_jupiter3, "Wave Mate", "Jupiter III", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



k1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for Kawai K1 synthesizer.

***************************************************************************/

#include "emu.h"
#include "mb63h158.h"
#include "cpu/upd78k/upd78k3.h"
#include "machine/nvram.h"


namespace {

class kawai_k1_state : public driver_device
{
public:
	kawai_k1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_mpu(*this, "mpu")
	{
	}

	void k1(machine_config &config);
	void k1m(machine_config &config);

private:
	void k1_map(address_map &map) ATTR_COLD;
	void k1m_map(address_map &map) ATTR_COLD;

	required_device<upd78310_device> m_mpu;
};


void kawai_k1_state::k1m_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("coderom", 0);
	map(0x8000, 0x9fff).ram();
	map(0xa000, 0xbfff).ram().share("toneram");
}

void kawai_k1_state::k1_map(address_map &map)
{
	k1m_map(map);
	map(0xf000, 0xf0ff).mirror(0x700).r("sensor", FUNC(mb63h158_device::read));
}


static INPUT_PORTS_START(k1)
INPUT_PORTS_END

void kawai_k1_state::k1(machine_config &config)
{
	UPD78310(config, m_mpu, 12_MHz_XTAL); // µPD78310G-36
	m_mpu->set_addrmap(AS_PROGRAM, &kawai_k1_state::k1_map);

	NVRAM(config, "toneram", nvram_device::DEFAULT_ALL_0); // LC3564PL-12 + battery

	MB63H158(config, "sensor", 7.2_MHz_XTAL);
}

void kawai_k1_state::k1m(machine_config &config)
{
	k1(config);

	m_mpu->set_addrmap(AS_PROGRAM, &kawai_k1_state::k1m_map);
	config.device_remove("sensor");
}

ROM_START(k1)
	ROM_REGION(0x8000, "coderom", 0)
	//ROM_SYSTEM_BIOS(0, "v1.3", "Version 1.3")
	ROM_LOAD("p104c_e4dp.u20", 0x0000, 0x8000, CRC(5d266846) SHA1(42a2820873817ca05dd085dfb728e8bcadb1342a)) // TC54256AP

	ROM_REGION(0x80000, "waverom", 0)
	ROM_LOAD("p106-m8dw.u8", 0x00000, 0x80000, CRC(b411b848) SHA1(1ea7ca6270b5c128f4e24fa540d205411982e4c1)) // MN234002KAD
ROM_END

ROM_START(k1m)
	ROM_REGION(0x8000, "coderom", 0)
	ROM_SYSTEM_BIOS(0, "v1.5", "Version 1.5")
	ROMX_LOAD("p105e.ic7", 0x0000, 0x8000, CRC(ff560060) SHA1(28d55ec30acbbc1c41d09b7704725f226d1f682c), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v1.1", "Version 1.1")
	ROMX_LOAD("p105a.ic7", 0x0000, 0x8000, CRC(957e8f55) SHA1(b5db5e619acc0a4af3b85a450ad51ce7f2a21900), ROM_BIOS(1))

	ROM_REGION(0x80000, "waverom", 0)
	ROM_LOAD("waverom_p106-m8dw.ic16", 0x00000, 0x80000, CRC(b411b848) SHA1(1ea7ca6270b5c128f4e24fa540d205411982e4c1))
ROM_END

ROM_START(k1r)
	ROM_REGION(0x8000, "coderom", 0)
	ROM_SYSTEM_BIOS(0, "v1.4", "Version 1.4")
	ROMX_LOAD("p163a.ic7",    0x0000, 0x8000, CRC(5c0e5b4b) SHA1(2348989ddf9398d66cfdff4675dc8e56a5a20d93), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v1.3", "Version 1.3")
	ROMX_LOAD("k1r_v1.3.ic7", 0x0000, 0x8000, CRC(25b395b0) SHA1(1e0e1735b7f2c8cb1c9946048c5a7b8e848db2ea), ROM_BIOS(1))

	ROM_REGION(0x80000, "waverom", 0)
	ROM_LOAD("waverom_p106-m8dw.ic16", 0x00000, 0x80000, CRC(b411b848) SHA1(1ea7ca6270b5c128f4e24fa540d205411982e4c1))
ROM_END

ROM_START(k1rii)
	ROM_REGION(0x8000, "coderom", 0)
	//ROM_SYSTEM_BIOS(0, "v1.0", "Version 1.0")
	ROM_LOAD("p205_e4dp.bin", 0x0000, 0x8000, CRC(be15cf1f) SHA1(07a2b7d55cc1f5c49a73e8bc2f50d2f6f0d6c4a8))

	ROM_REGION(0x80000, "waverom", 0)
	ROM_LOAD("p106.bin", 0x00000, 0x80000, NO_DUMP)
ROM_END

} // anonymous namespace


SYST(1988, k1,  0,  0, k1,  k1, kawai_k1_state, empty_init, "Kawai Musical Instrument Manufacturing", "K1 Digital Multi-Dimensional Synthesizer",         MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1988, k1m, k1, 0, k1m, k1, kawai_k1_state, empty_init, "Kawai Musical Instrument Manufacturing", "K1m Digital Multi-Dimensional Synthesizer Module", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1988, k1r, k1, 0, k1m, k1, kawai_k1_state, empty_init, "Kawai Musical Instrument Manufacturing", "K1r Digital Multi-Dimensional Synthesizer Module", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

SYST(1989, k1rii, 0, 0, k1m, k1, kawai_k1_state, empty_init, "Kawai Musical Instrument Manufacturing", "K1rII Digital Multi-Dimensional Synthesizer Module", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



k1003.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/*******************************************************************************************************

Robotron K1003

2009-11-20 Skeleton driver.

The last of the K-1000 series of electronic programmable desktop calculators.

K-1001 : Enter via keyboard, results are on the LED display, much like any other advanced calculator
         or adding machine.

K-1002 : Has an additional magnetic stripe reader/writer. You can save your programs to the flexible
         card and insert it in another session. Each card holds about 200 bytes. Further, the unit has
         a cartridge slot where preset mathematical packages could be inserted (such as Statistics module).

K-1003 : All of the above, plus a thermal printer. Maximum ram is 4k.


TODO:
- Need schematic, unable to locate one
- keyboard to be worked out
- Cartslots
- Printer
- Magnetic cards

******************************************************************************************************/

#include "emu.h"
#include "cpu/i8008/i8008.h"
#include "k1003.lh"


namespace {

class k1003_state : public driver_device
{
public:
	k1003_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_io_keyboard(*this, "X%u", 0U)
		, m_digits(*this, "digit%u", 0U)
		{ }

	void k1003(machine_config &config);

private:
	uint8_t port2_r();
	uint8_t key_r();
	void disp_1_w(uint8_t data);
	void disp_2_w(uint8_t data);
	void seg_w(uint8_t data);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	uint8_t m_disp_1 = 0U;
	uint8_t m_disp_2 = 0U;
	u8 m_digit = 0U;
	[[maybe_unused]] uint8_t bit_to_dec(uint8_t val);
	void machine_start() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_ioport_array<8> m_io_keyboard;
	output_finder<16> m_digits;
};


void k1003_state::mem_map(address_map &map)
{
	map(0x0000, 0x07ff).rom();
	map(0x0800, 0x17ff).ram();
	map(0x1800, 0x1fff).rom();
	map(0x2000, 0x27ff).rom();
	map(0x2800, 0x2fff).ram();
	map(0x3000, 0x3aff).rom();
}

// if non-zero, jump to cart?
uint8_t k1003_state::port2_r()
{
	return 0x00;
}

uint8_t k1003_state::key_r()
{//printf("%X ",m_digit);
	u8 data = 0;
	for (u8 i = 0; i < 8; i++)
		if (BIT(m_digit, i))
			data |= m_io_keyboard[i]->read();

	return data;
}


void k1003_state::disp_1_w(uint8_t data)
{
	m_disp_1 = data;
}

void k1003_state::disp_2_w(uint8_t data)
{
	m_disp_2 = data;
}

uint8_t k1003_state::bit_to_dec(uint8_t val) // not used atm
{
	if (BIT(val,0)) return 0;
	if (BIT(val,1)) return 1;
	if (BIT(val,2)) return 2;
	if (BIT(val,3)) return 3;
	if (BIT(val,4)) return 4;
	if (BIT(val,5)) return 5;
	if (BIT(val,6)) return 6;
	if (BIT(val,7)) return 7;
	return 0;
}

void k1003_state::seg_w(uint8_t data)
{
	data = bitswap<8>(data, 0,1,2,3,4,5,6,7);

	for (u8 i = 0; i < 8; i++)
	{
		m_digits[i] = (m_digits[i] & ~data) | (BIT(m_disp_1, i) ? data : 0);
		m_digits[i+8] = (m_digits[i+8] & ~data) | (BIT(m_disp_2, i) ? data : 0);
	}
}

void k1003_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x1f);
	map(0x00, 0x00).r(FUNC(k1003_state::key_r));
	map(0x02, 0x02).r(FUNC(k1003_state::port2_r));

	map(0x08, 0x08).w(FUNC(k1003_state::seg_w));
	map(0x09, 0x09).w(FUNC(k1003_state::disp_2_w));
	map(0x0a, 0x0a).w(FUNC(k1003_state::disp_1_w));
	map(0x10, 0x10).lw8(NAME([this] (u8 data) { m_digit = ~data; }));
	map(0x11, 0x13).nopw();   // stop error.log rapidly filling up.
}

/* Input ports */
static INPUT_PORTS_START( k1003 )
	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)

	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH)

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_END)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)

	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)

	PORT_START("X4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)

	PORT_START("X5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("]")  PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("~")  PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DEL")PORT_CODE(KEYCODE_BACKSPACE)

	PORT_START("X6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LF") PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER)

	PORT_START("X7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)
INPUT_PORTS_END


void k1003_state::machine_start()
{
	m_digits.resolve();
	save_item(NAME(m_disp_1));
	save_item(NAME(m_disp_2));
	save_item(NAME(m_digit));
}

void k1003_state::k1003(machine_config &config)
{
	/* basic machine hardware */
	I8008(config, m_maincpu, 800000);
	m_maincpu->set_addrmap(AS_PROGRAM, &k1003_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &k1003_state::io_map);

	/* video hardware */
	config.set_default_layout(layout_k1003);
}


/* ROM definition */
ROM_START( k1003 )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "k1003.01", 0x0000, 0x0100, CRC(9342f67d) SHA1(75d33cad89cf47e8e691a6ddbb86a8c11f454434))
	ROM_LOAD( "k1003.02", 0x0100, 0x0100, CRC(a6846b2b) SHA1(a38b15ae0ac3f216e49aef4618363ea0d262fe52))
	ROM_LOAD( "k1003.03", 0x0200, 0x0100, CRC(3fddd922) SHA1(9c9f28ad8a611d8a0911d2935ffa262976a0272d))
	ROM_LOAD( "k1003.04", 0x0300, 0x0100, CRC(ec3edbe9) SHA1(d58064db7f2d085760088da1899f44f7a5b02923))
	ROM_LOAD( "k1003.05", 0x0400, 0x0100, CRC(93836b34) SHA1(7397c9748c1adb347270529464f1e10d3ac92879))
	ROM_LOAD( "k1003.06", 0x0500, 0x0100, CRC(79e39c8f) SHA1(bffc24a47867834d749d32307dca1053231a1b62))
	ROM_LOAD( "k1003.07", 0x0600, 0x0100, CRC(1f7279e0) SHA1(1e625adf606f87a55b6e8827fc9764d8a777903a))
	ROM_LOAD( "k1003.08", 0x0700, 0x0100, CRC(4b950957) SHA1(268c7dbf52a85bdf4eb5ebbe6d01aae45e853a72))

	ROM_LOAD( "k1003.09", 0x1800, 0x0100, CRC(f3ec866f) SHA1(0b274be7290c9d469205136851c48595cd4f15e2))
	ROM_LOAD( "k1003.10", 0x1900, 0x0100, CRC(c4af2cf7) SHA1(1815030d08072542fa56c4063b6de1d64b146887))
	ROM_LOAD( "k1003.11", 0x1a00, 0x0100, CRC(473ef6db) SHA1(45372e09babf6fa08875e53d43e90d53f9cc0ec1))
	ROM_LOAD( "k1003.12", 0x1b00, 0x0100, CRC(8af505d4) SHA1(21302f2bb660ddaf5e4526335ec457479f1673d5))
	ROM_LOAD( "k1003.13", 0x1c00, 0x0100, CRC(753166da) SHA1(90b91bac845f5d0ecd3af4d67174d351e478b632))
	ROM_LOAD( "k1003.14", 0x1d00, 0x0100, CRC(a885a676) SHA1(e899ea5b97734360421d2696e0a75d65fa8a031c))
	ROM_LOAD( "k1003.15", 0x1e00, 0x0100, CRC(db63b0cd) SHA1(726d80bb34862301df773b8587acccbeedfcc38d))
	ROM_LOAD( "k1003.16", 0x1f00, 0x0100, CRC(9457f1bd) SHA1(aae0a7c0a63d8213a57850383aaf92927577be7a))

	// Math pack
	ROM_LOAD( "026.bin",  0x2000, 0x0100, CRC(d678e80c) SHA1(bdf696e9704c286ed0ad5ffbdae206580a277c38))
	ROM_LOAD( "027.bin",  0x2100, 0x0100, CRC(dbe2ca8e) SHA1(2060bbb6b6dee87c98ddcf84a49069547749ae9b))
	ROM_LOAD( "028.bin",  0x2200, 0x0100, CRC(2cd742ed) SHA1(ec3f1ba548c64b0fe538af365a383f2341c81988))
	ROM_LOAD( "029.bin",  0x2300, 0x0100, CRC(12165b43) SHA1(7bb2c97893a07196cf245c8ac3a64c0ee49bb75e))
	ROM_LOAD( "030.bin",  0x2400, 0x0100, CRC(545dd7e0) SHA1(ab9f2e8cd4d3d4ba8accf74caec20de2e3094a66))
	ROM_LOAD( "031.bin",  0x2500, 0x0100, CRC(315d27d2) SHA1(26bb39d50781eed8d8be4ad6e1f13bc2b4672ce2))
	ROM_LOAD( "032.bin",  0x2600, 0x0100, CRC(d03f7cc7) SHA1(5a1c0614eed6dfe21d0d1776f409c1c2209a74d1))
	ROM_LOAD( "033.bin",  0x2700, 0x0100, CRC(efaeb541) SHA1(decdbbd3c4084dc18b34577f78ddf7044341764a))

	ROM_LOAD( "k1003.17", 0x3000, 0x0100, CRC(9031390b) SHA1(6f99a9f643b19770a373242edb0df8f342bbc230))
	ROM_LOAD( "k1003.18", 0x3100, 0x0100, CRC(38435ffe) SHA1(7db78d304fe8a8f71c067babcdcf3c06da908ad3))
	ROM_LOAD( "k1003.19", 0x3200, 0x0100, CRC(3cfddbda) SHA1(7e9d5c6126d0f08fcb7d88d0cc26eb24467f7321))
	ROM_LOAD( "k1003.20", 0x3300, 0x0100, CRC(08707172) SHA1(fef18e407ebec13d34c2bd6925ffae533139551e))
	ROM_LOAD( "k1003.21", 0x3400, 0x0100, CRC(4038b284) SHA1(f6ee7fd8fb06a73a7d1ed71ca7022f1c76c207bd))
	ROM_LOAD( "k1003.22", 0x3500, 0x0100, CRC(04691d40) SHA1(72f96994811f435adecc74585f3dfaa89d0f192a))
	ROM_LOAD( "k1003.23", 0x3600, 0x0100, CRC(a2f7170c) SHA1(2cd9a64019c2cd7f6f6146d1886de6a1aa5ccc88))
	ROM_LOAD( "k1003.24", 0x3700, 0x0100, CRC(c0935c12) SHA1(f3fc7c3fa97b2bcb9ec522002ee760a226eb329a))
	ROM_LOAD( "k1003.25", 0x3800, 0x0100, CRC(a827aec0) SHA1(a8f582a9a6b31581d8a174a0bc2f4588d6b53400))
	ROM_LOAD( "k1003.26", 0x3900, 0x0100, CRC(fc949804) SHA1(088b63b7f8704efb6867be899b81d64a294af6be))
	ROM_LOAD( "k1003.27", 0x3a00, 0x0100, CRC(ddcdd065) SHA1(e29c6f2dd1e4da125d150e28cf51f8c558ec9ee5))
	// 0x3b00 - missing on board - returning 0xff

	ROM_REGION( 0x1000, "user1", 0 )
	// k1003-extension
	ROM_LOAD( "040.bin",  0x0000, 0x0100, CRC(06865678) SHA1(91a4f0a32e93d315d4f78a732472c08a380205fc) )
	ROM_LOAD( "041.bin",  0x0100, 0x0100, CRC(dcd776b3) SHA1(cf8082d31be9bea1e9672d0b92006f14719ba7a6) )
	ROM_LOAD( "042.bin",  0x0200, 0x0100, CRC(e74aca0d) SHA1(f515b52862f0f31551ec008bf6f27c5fdf78c32a) )
	ROM_LOAD( "043.bin",  0x0300, 0x0100, CRC(770820e5) SHA1(c34b6a7bde43758c7c3c7041ce1dee06456fe5c4) )
	ROM_LOAD( "044.bin",  0x0400, 0x0100, CRC(82bd3f5a) SHA1(c4290507de8d295c0b7f04ac2179365b8f73d7c7) )
	ROM_LOAD( "045.bin",  0x0500, 0x0100, CRC(66c85afb) SHA1(937828a6aee46cbcad86bb1776deb7f9b1dc69ff) )
	// k1003-statistics
	ROM_LOAD( "435.bin",  0x0800, 0x0100, CRC(31203b34) SHA1(5638c45e23b279c5c8960d5d7f2b16acb2e47ed2) )
	ROM_LOAD( "436.bin",  0x0900, 0x0100, CRC(dd2d4eb5) SHA1(c436419ef71cdf4dfc4d118301749f746f9cd6e5) )
	ROM_LOAD( "437.bin",  0x0a00, 0x0100, CRC(00433159) SHA1(69fab3b875c285e6abcab43b0ba6ef916d0a1484) )
	ROM_LOAD( "439.bin",  0x0b00, 0x0100, CRC(3f5be050) SHA1(f2730078a8346e8670b632048f358a47ff57941e) )
	ROM_LOAD( "440.bin",  0x0c00, 0x0100, CRC(856685fa) SHA1(dbd33194a4eb9ed037f8129bf8b9d4628aab8151) )
	ROM_LOAD( "441.bin",  0x0d00, 0x0100, CRC(257df6a3) SHA1(65c0429b0e352434b7b661e63cacfe74ac1d1ac9) )
	ROM_LOAD( "442.bin",  0x0e00, 0x0100, CRC(d037e0bb) SHA1(5ae8ad62673bd732a05232645c523206024f9afb) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY     FULLNAME  FLAGS
COMP( 1978, k1003, 0,      0,      k1003,   k1003, k1003_state, empty_init, "Robotron", "K1003",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



k28.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle, David Viens
/*******************************************************************************

Tiger Electronics K-2-8 (model 7-232) Sold in Hong Kong, distributed in US as:
- Coleco: Talking Teacher (model 8100)
- Sears: Talkatron: Learning Computer

1981 K-2-8 models 7-230 and 7-231 are on different hardware, having a different
keyboard, VFD, and the SC-01-A speech chip, emulated in k28o.cpp.

Hardware notes:
- PCB label: 201223A (main), REV0 ET828D (LCD)
- TMS1400 MP7324 (die label: TMS1400, MP7324, 28L 01D D000 R100)
- SMC1112 under epoxy (die label: SMC1112 D2N0), 8*14-seg display
- TMS5110ANL, CM62084 VSM (16KB)
- module slot (not compatible with the 1981 version(s))

TODO:
- dump/add module #5

*******************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/tms1000/smc1102.h"
#include "cpu/tms1000/tms1400.h"
#include "machine/tms6100.h"
#include "sound/tms5110.h"

#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "k28.lh"


namespace {

class k28_state : public driver_device
{
public:
	k28_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_subcpu(*this, "subcpu"),
		m_tms5100(*this, "tms5100"),
		m_tms6100(*this, "tms6100"),
		m_cart(*this, "cartslot"),
		m_inputs(*this, "IN.%u", 0),
		m_digits(*this, "digit%u", 0U)
	{ }

	void k28(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(power_on);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<tms1400_cpu_device> m_maincpu;
	required_device<smc1112_cpu_device> m_subcpu;
	required_device<tms5110_device> m_tms5100;
	required_device<tms6100_device> m_tms6100;
	optional_device<generic_slot_device> m_cart;
	required_ioport_array<9> m_inputs;
	output_finder<8> m_digits;

	bool m_power_on = false;
	u16 m_inp_mux = 0;
	u32 m_r = 0;
	u32 m_digit_data[4] = { };

	void power_off();
	u8 read_k();
	void write_o(u16 data);
	void write_r(u32 data);

	u8 sub_read_k();
	void write_segs(offs_t offset, u32 data);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
};

void k28_state::machine_start()
{
	m_digits.resolve();

	// register for savestates
	save_item(NAME(m_power_on));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_r));
	save_item(NAME(m_digit_data));
}



/*******************************************************************************
    Power
*******************************************************************************/

void k28_state::machine_reset()
{
	m_power_on = true;
	m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
	m_subcpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
}

INPUT_CHANGED_MEMBER(k28_state::power_on)
{
	if (newval && !m_power_on)
		machine_reset();
}

void k28_state::power_off()
{
	m_power_on = false;
	m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
	m_subcpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);

	// clear display
	for (int i = 0; i < 8; i++)
		m_digits[i] = 0;
}



/*******************************************************************************
    Cartridge
*******************************************************************************/

DEVICE_IMAGE_LOAD_MEMBER(k28_state::cart_load)
{
	u32 const size = m_cart->common_get_size("rom");

	if (size > 0x4000)
		return std::make_pair(image_error::INVALIDLENGTH, "Invalid image file size (must be no more than 16K)");

	u8 *const base = memregion("tms6100")->base() + 0x4000;
	m_cart->common_load_rom(base, size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}



/*******************************************************************************
    I/O
*******************************************************************************/

// maincpu side

void k28_state::write_r(u32 data)
{
	// R1234: TMS5100 CTL8421
	u32 r = bitswap<5>(data,0,1,2,3,4) | (data & ~0x1f);
	m_tms5100->ctl_w(r & 0xf);

	// R0: TMS5100 PDC pin
	m_tms5100->pdc_w(data & 1);

	// R5: input mux high bit
	m_inp_mux = (m_inp_mux & 0xff) | (data << 3 & 0x100);

	// R7-R10: subcpu K interrupt
	m_subcpu->set_input_line(SMC1102_INPUT_LINE_K, (data & 0x780) ? ASSERT_LINE : CLEAR_LINE);

	// R6: power-off request, on falling edge
	if (~data & m_r & 0x40)
		power_off();

	m_r = r;
}

void k28_state::write_o(u16 data)
{
	// O0-O7: input mux low
	m_inp_mux = (m_inp_mux & ~0xff) | data;
}

u8 k28_state::read_k()
{
	u8 data = 0;

	// K: multiplexed inputs
	for (int i = 0; i < 9; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read();

	// and TMS5100 CTL (also tied to R1234)
	return data | m_tms5100->ctl_r() | (m_r & 0xf);
}


// subcpu side

void k28_state::write_segs(offs_t offset, u32 data)
{
	m_digit_data[offset & 3] = data;

	for (int d = 0; d < 8; d++)
	{
		// 4 segments per common
		u16 segs = 0;
		for (int i = 0; i < 4; i++)
			segs = segs << 4 | (m_digit_data[i] >> (d * 4) & 0xf);

		m_digits[d] = bitswap<14>(segs,6,10,13,1,5,9,11,8,12,4,2,7,15,14);
	}
}

u8 k28_state::sub_read_k()
{
	// K: maincpu R7-R10
	return m_r >> 7 & 0xf;
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( k28 )
	PORT_START("IN.0") // O0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_NAME("Off") // -> auto_power_off
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('A') PORT_NAME("A/1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('J') PORT_NAME("J/0")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')

	PORT_START("IN.1") // O1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(k28_state::power_on), 0) PORT_NAME("On")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('B') PORT_NAME("B/2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR('K') PORT_NAME("K/+")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')

	PORT_START("IN.2") // O2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("Repeat")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('C') PORT_NAME("C/3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR('L') PORT_NAME("L/-")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')

	PORT_START("IN.3") // O3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("Prompt")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('D') PORT_NAME("D/4")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR('M') PORT_NAME(u8"M/×")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')

	PORT_START("IN.4") // O4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_HOME) PORT_NAME("Menu")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('E') PORT_NAME("E/5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR('N') PORT_NAME(u8"N/÷")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')

	PORT_START("IN.5") // O5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_END) PORT_NAME("Module")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('F') PORT_NAME("F/6")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')

	PORT_START("IN.6") // O6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_NAME("Select")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('G') PORT_NAME("G/7")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')

	PORT_START("IN.7") // O7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_NAME("Clear")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('H') PORT_NAME("H/8")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')

	PORT_START("IN.8") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('I') PORT_NAME("I/9")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13) PORT_NAME("Enter")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void k28_state::k28(machine_config &config)
{
	constexpr u32 MASTER_CLOCK = 640'000; // approximation

	// basic machine hardware
	TMS1400(config, m_maincpu, MASTER_CLOCK/2);
	m_maincpu->read_k().set(FUNC(k28_state::read_k));
	m_maincpu->write_o().set(FUNC(k28_state::write_o));
	m_maincpu->write_r().set(FUNC(k28_state::write_r));

	SMC1112(config, m_subcpu, 32.768_kHz_XTAL);
	m_subcpu->read_k().set(FUNC(k28_state::sub_read_k));
	m_subcpu->write_segs().set(FUNC(k28_state::write_segs));

	config.set_perfect_quantum(m_subcpu);

	config.set_default_layout(layout_k28);

	// sound hardware
	TMS6100(config, m_tms6100, MASTER_CLOCK/4);

	SPEAKER(config, "mono").front_center();
	TMS5110A(config, m_tms5100, MASTER_CLOCK);
	m_tms5100->m0().set(m_tms6100, FUNC(tms6100_device::m0_w));
	m_tms5100->m1().set(m_tms6100, FUNC(tms6100_device::m1_w));
	m_tms5100->addr().set(m_tms6100, FUNC(tms6100_device::add_w));
	m_tms5100->data().set(m_tms6100, FUNC(tms6100_device::data_line_r));
	m_tms5100->romclk().set(m_tms6100, FUNC(tms6100_device::clk_w));
	m_tms5100->add_route(ALL_OUTPUTS, "mono", 0.5);

	// cartridge
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "k28", "vsm,bin");
	m_cart->set_device_load(FUNC(k28_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("k28");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( k28 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mp7324", 0x0000, 0x1000, CRC(8d304cf2) SHA1(d649b6477ea8634b3a3ba34dde7e5e913855801f) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 557, "maincpu:opla", 0 )
	ROM_LOAD( "tms1400_k28_output.pla", 0, 557, CRC(3a5c7005) SHA1(3fe5819c138a90e7fc12817415f2622ca81b40b2) )

	ROM_REGION( 0x0800, "subcpu", 0 )
	ROM_LOAD( "smc1112_d2n0", 0x0000, 0x0800, CRC(e985fd67) SHA1(a0b0280920bf0ac02a1aaf02d534dddbae829433) )

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // 4000-7fff = space reserved for cartridge
	ROM_LOAD( "cm62084", 0x0000, 0x4000, CRC(cd1376f7) SHA1(96fa484c392c451599bc083b8376cad9c998df7d) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY, FULLNAME, FLAGS
SYST( 1985, k28,  0,      0,      k28,     k28,   k28_state, empty_init, "Tiger Electronics", "K-2-8: Talking Learning Computer (model 7-232)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )



k28o.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Kevin Horton
/*******************************************************************************

Tiger Electronics K-2-8: Talking Learning Computer (model 7-230/7-231)

3 models exist:
- 7-230: darkblue case, toy-ish looks
- 7-231: gray case, hardware is the same
- 7-232: this one is on completely different hardware*

*: See k28.cpp for the newer version. Considering the different look and feel
and the 4 year difference, k28o is not a MAME clone set of k28. It's more like
a predecessor instead of an older revision.

Hardware notes:
- PCB label: PB-123 WIZARD, TIGER
- Intel P8021 MCU with 1KB internal ROM
- MM5445N VFD driver, 9-digit alphanumeric display same as snmath
- 2*TMS6100 (32KB VSM)
- SC-01-A speech chip
- module slot

6 modules were announced (see back of the box), but it's not known if they were
actually released.

TODO:
- plosive consonants are very difficult to hear, it's an issue in votrax.cpp
- add module slot

*******************************************************************************/

#include "emu.h"

#include "cpu/mcs48/mcs48.h"
#include "machine/tms6100.h"
#include "video/mm5445.h"
#include "video/pwm.h"
#include "sound/votrax.h"

#include "speaker.h"

// internal artwork
#include "k28o.lh"


namespace {

class k28o_state : public driver_device
{
public:
	k28o_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_vfd(*this, "vfd"),
		m_display(*this, "display"),
		m_tms6100(*this, "tms6100"),
		m_speech(*this, "speech"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void k28o(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(power_on);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<i8021_device> m_maincpu;
	required_device<mm5445_device> m_vfd;
	required_device<pwm_display_device> m_display;
	required_device<tms6100_device> m_tms6100;
	required_device<votrax_sc01_device> m_speech;
	required_ioport_array<7> m_inputs;

	bool m_power_on = false;
	attotime m_onbutton_time;
	u8 m_inp_mux = 0;
	u8 m_phoneme = 0x3f;
	int m_speech_strobe = 0;
	u64 m_vfd_data = 0;

	void vfd_output_w(u64 data);
	void mcu_p0_w(u8 data);
	u8 mcu_p1_r();
	u8 mcu_p2_r();
	void mcu_p2_w(u8 data);

	void power_off();
};

void k28o_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_power_on));
	save_item(NAME(m_onbutton_time));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_phoneme));
	save_item(NAME(m_speech_strobe));
	save_item(NAME(m_vfd_data));
}



/*******************************************************************************
    Power
*******************************************************************************/

void k28o_state::machine_reset()
{
	m_vfd_data = 0;
	m_power_on = true;
	m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);

	// it relies on reading the on-button as pressed when it's turned on
	m_onbutton_time = machine().time() + attotime::from_msec(250);
}

INPUT_CHANGED_MEMBER(k28o_state::power_on)
{
	if (newval && !m_power_on)
		machine_reset();
}

void k28o_state::power_off()
{
	m_power_on = false;
	m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
	m_display->clear();
}



/*******************************************************************************
    I/O
*******************************************************************************/

// MM5445 VFD

void k28o_state::vfd_output_w(u64 data)
{
	// O1-O16: digit segment data
	// O17-O25: digit select
	u16 seg_data = bitswap<16>(data,15,14,2,6,5,3,1,7,12,11,10,13,0,4,9,8);
	m_display->matrix(data >> 16, seg_data);

	// O26: power-off request on falling edge
	if (~data & m_vfd_data & 1 << 25)
		power_off();
	m_vfd_data = data;
}


// I8021 ports

void k28o_state::mcu_p0_w(u8 data)
{
	// d0,d1: phoneme high bits
	// d0-d2: input mux high bits
	m_inp_mux = (m_inp_mux & 0xf) | (~data << 4 & 0x70);
	m_phoneme = (m_phoneme & 0xf) | (data << 4 & 0x30);

	// d3: SC-01 strobe, latch phoneme on rising edge
	int strobe = data >> 3 & 1;
	if (strobe && !m_speech_strobe)
		m_speech->write(m_phoneme);
	m_speech_strobe = strobe;

	// d5: VFD driver data enable
	m_vfd->enable_w(data >> 5 & 1);

	// d4: VSM chip enable
	// d6: VSM M0
	// d7: VSM M1
	m_tms6100->cs_w(~data >> 4 & 1);
	m_tms6100->m0_w(data >> 6 & 1);
	m_tms6100->m1_w(data >> 7 & 1);
	m_tms6100->clk_w(1);
	m_tms6100->clk_w(0);
}

u8 k28o_state::mcu_p1_r()
{
	u8 data = 0;

	// multiplexed inputs (active low)
	for (int i = 0; i < 7; i++)
		if (BIT(m_inp_mux, i))
		{
			data |= m_inputs[i]->read();

			// force press on-button at boot
			if (i == 5 && machine().time() < m_onbutton_time)
				data |= 1;
		}

	return data ^ 0xff;
}

u8 k28o_state::mcu_p2_r()
{
	// d3: VSM data
	return (m_tms6100->data_line_r()) ? 8 : 0;
}

void k28o_state::mcu_p2_w(u8 data)
{
	// d0: VFD driver serial data
	m_vfd->data_w(data & 1);

	// d0-d3: VSM data, input mux and SC-01 phoneme lower nibble
	m_tms6100->add_w(data);
	m_inp_mux = (m_inp_mux & ~0xf) | (~data & 0xf);
	m_phoneme = (m_phoneme & ~0xf) | (data & 0xf);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( k28o )
	PORT_START("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_NAME("Yes/True")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_NAME("No/False")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_NAME("Select")

	PORT_START("IN.1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_END) PORT_NAME("Scroll")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_NAME("<")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_NAME("Erase/Clear")

	PORT_START("IN.2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_HOME) PORT_NAME("Menu")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_NAME(">")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("Enter/Start")

	PORT_START("IN.3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("Prompt")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")

	PORT_START("IN.4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("Say It Again(Repeat)")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")

	PORT_START("IN.5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_ON ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(k28o_state::power_on), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("=")

	PORT_START("IN.6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF ) // -> auto_power_off
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void k28o_state::k28o(machine_config &config)
{
	// basic machine hardware
	I8021(config, m_maincpu, 3.579545_MHz_XTAL);
	m_maincpu->bus_out_cb().set(FUNC(k28o_state::mcu_p0_w));
	m_maincpu->p1_in_cb().set(FUNC(k28o_state::mcu_p1_r));
	m_maincpu->p2_in_cb().set(FUNC(k28o_state::mcu_p2_r));
	m_maincpu->p2_out_cb().set(FUNC(k28o_state::mcu_p2_w));
	m_maincpu->prog_out_cb().set("vfd", FUNC(mm5445_device::clock_w));
	m_maincpu->t1_in_cb().set("speech", FUNC(votrax_sc01_device::request));

	TMS6100(config, m_tms6100, 3.579545_MHz_XTAL / 15); // CLK tied to 8021 ALE pin

	// video hardware
	MM5445(config, m_vfd).output_cb().set(FUNC(k28o_state::vfd_output_w));
	PWM_DISPLAY(config, m_display).set_size(9, 16);
	m_display->set_segmask(0x1ff, 0x3fff);

	config.set_default_layout(layout_k28o);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	VOTRAX_SC01A(config, "speech", 760000).add_route(ALL_OUTPUTS, "mono", 0.5); // measured 760kHz on its RC pin
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( k28o )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "p8021_7-230-itl", 0x0000, 0x0400, CRC(15536d20) SHA1(fac98ce652340ffb2d00952697c3a9ce75393fa4) )

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // 8000-bfff? = space reserved for cartridge
	ROM_LOAD( "cm62050u", 0x0000, 0x4000, CRC(6afb8645) SHA1(e22435568ed11c6516a3b4008131f99cd4e47aa9) )
	ROM_LOAD( "cm62051u", 0x4000, 0x4000, CRC(0fa61baa) SHA1(831be669423ba60c7f85a896b4b09a1295478bd9) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY, FULLNAME, FLAGS
SYST( 1981, k28o,  0,      0,      k28o,    k28o,  k28o_state, empty_init, "Tiger Electronics", "K-2-8: Talking Learning Computer (model 7-230)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )



k4.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for Kawai K4 synthesizer.

***************************************************************************/

#include "emu.h"
#include "cpu/upd78k/upd78k3.h"
//#include "machine/nvram.h"


namespace {

class kawai_k4_state : public driver_device
{
public:
	kawai_k4_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_mpu(*this, "mpu")
	{
	}

	void k4(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<upd78310_device> m_mpu;
};


void kawai_k4_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("coderom", 0);
	map(0x8000, 0xbfff).rom().region("coderom", 0x8000); // TODO: banked with other stuff
}


static INPUT_PORTS_START(k4)
INPUT_PORTS_END

void kawai_k4_state::k4(machine_config &config)
{
	UPD78310(config, m_mpu, 12_MHz_XTAL); // µPD78310G-36
	m_mpu->set_addrmap(AS_PROGRAM, &kawai_k4_state::mem_map);

	//NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // TC51832SP-12 + battery
}

ROM_START(k4)
	ROM_REGION(0x10000, "coderom", 0)
	ROM_SYSTEM_BIOS(0, "v1.4", "Version 1.4")
	ROMX_LOAD("k4_v14.u8",    0x00000, 0x10000, CRC(bf463780) SHA1(acb391fc8d074420a0dcdf0cbea523f6e7601e66), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v1.3", "Version 1.3")
	ROMX_LOAD("p206b.u8",     0x00000, 0x10000, CRC(4108dec4) SHA1(8666583bc2f7a6792586325d8b98ffb684e51259), ROM_BIOS(1)) // 27C512
	ROM_SYSTEM_BIOS(2, "v1.0", "Version 1.0")
	ROMX_LOAD("p206_e5dp.u8", 0x00000, 0x10000, CRC(fdf47a8e) SHA1(2a074da55746cc51f1e06d3f88afc296750f3ea6), ROM_BIOS(2)) // 27C512

	ROM_REGION(0x180000, "waverom", 0)
	ROM_LOAD("d941_p202-m8dw.u30", 0x000000, 0x80000, NO_DUMP) // TC534000P
	ROM_LOAD("d942_p203-m8dw.u29", 0x080000, 0x80000, NO_DUMP) // TC534000P
	ROM_LOAD("d943_p204-m8dw.u31", 0x100000, 0x80000, NO_DUMP) // TC534000P
ROM_END

ROM_START(k4r)
	ROM_REGION(0x10000, "coderom", 0)
	ROM_SYSTEM_BIOS(0, "v1.4", "Version 1.4")
	ROMX_LOAD("p207c.u7",    0x00000, 0x10000, CRC(17bdaaf6) SHA1(a1220fd479e154be679596ab18a1e76ae722a39d), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v1.3", "Version 1.3")
	ROMX_LOAD("k4r_v1.3.u7", 0x00000, 0x10000, CRC(1b36d5ce) SHA1(afe25260e3ada1646d6c04cebb4c6dec41f742c0), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v1.2", "Version 1.2")
	ROMX_LOAD("k4r_v1.2.u7", 0x00000, 0x10000, CRC(8b4953bc) SHA1(7bdb0d15bfe396fe6c2499c75137d71aef6c2ca8), ROM_BIOS(2))

	ROM_REGION(0x180000, "waverom", 0)
	ROM_LOAD("d941_p202-m8dw.u40", 0x000000, 0x80000, NO_DUMP) // TC534000P
	ROM_LOAD("d942_p203-m8dw.u39", 0x080000, 0x80000, NO_DUMP) // TC534000P
	ROM_LOAD("d943_p204-m8dw.u38", 0x100000, 0x80000, NO_DUMP) // TC534000P
ROM_END

} // anonymous namespace


SYST(1989, k4,  0,  0, k4, k4, kawai_k4_state, empty_init, "Kawai Musical Instrument Manufacturing", "K4 16-bit Digital Synthesizer",         MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1989, k4r, k4, 0, k4, k4, kawai_k4_state, empty_init, "Kawai Musical Instrument Manufacturing", "K4r 16-bit Digital Synthesizer Module", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



k5.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for Kawai K5 synthesizer.

***************************************************************************/

#include "emu.h"
#include "cpu/nec/v5x.h"
#include "video/t6963c.h"


namespace {

class kawai_k5_state : public driver_device
{
public:
	kawai_k5_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void k5(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;
	void lcd_map(address_map &map) ATTR_COLD;

	required_device<v40_device> m_maincpu;
};


void kawai_k5_state::mem_map(address_map &map)
{
	map(0x00000, 0x0ffff).rom().region("coderom", 0);
	map(0x11000, 0x117ff).ram();
	map(0x14000, 0x142ff).ram();
	map(0x16800, 0x16801).rw("lcdc", FUNC(t6963c_device::read), FUNC(t6963c_device::write));
	map(0x17000, 0x17000).lr8([]() { return 0x04; }, "unknown_poll_r");
	map(0x17800, 0x17800).nopw();
	map(0x18000, 0x1ffff).ram();
	map(0x20000, 0x2ffff).ram();
	map(0xf0000, 0xfffff).rom().region("coderom", 0);
}

void kawai_k5_state::lcd_map(address_map &map)
{
	map(0x0000, 0x1fff).ram();
}


static INPUT_PORTS_START(k5)
INPUT_PORTS_END

void kawai_k5_state::k5(machine_config &config)
{
	V40(config, m_maincpu, 16'000'000); // XTAL unknown
	m_maincpu->set_addrmap(AS_PROGRAM, &kawai_k5_state::mem_map);

	T6963C(config, "lcdc", 0).set_addrmap(0, &kawai_k5_state::lcd_map);
}

ROM_START(k5)
	ROM_REGION(0x10000, "coderom", 0)
	//ROM_SYSTEM_BIOS(0, "v1.3", "Version 1.3")
	ROM_LOAD("k5_1.3.bin", 0x00000, 0x10000, CRC(cbefe520) SHA1(8c53867ebc403d24320a57a5ff2d37ab6c1fc994))

	ROM_REGION(0x400, "lcdc:cgrom", ROMREGION_ERASE00)
ROM_END

ROM_START(k5m)
	ROM_REGION(0x10000, "coderom", 0)
	//ROM_SYSTEM_BIOS(0, "v1.2", "Version 1.2")
	ROM_LOAD("k5m_1.2.bin", 0x00000, 0x10000, CRC(d2ab0fac) SHA1(34f0c2685b39e459a915ed5effa5b7a4cd2a1f8a))

	ROM_REGION(0x400, "lcdc:cgrom", ROMREGION_ERASE00)
ROM_END

} // anonymous namespace


SYST(1987, k5,  0,  0, k5, k5, kawai_k5_state, empty_init, "Kawai Musical Instrument Manufacturing", "K5 Digital Multi-Dimensional Synthesizer",         MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1987, k5m, k5, 0, k5, k5, kawai_k5_state, empty_init, "Kawai Musical Instrument Manufacturing", "K5m Digital Multi-Dimensional Synthesizer Module", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



k8915.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************

Robotron K8915

2010-08-30

When it says DIAGNOSTIC RAZ P, press enter.

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "bus/rs232/rs232.h"
#include "emupal.h"
#include "screen.h"


namespace {

class k8915_state : public driver_device
{
public:
	k8915_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_bank1(*this, "bank1")
		, m_p_videoram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
	{ }

	void k8915(machine_config &config);

private:
	void k8915_a8_w(u8 data);
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	u8 m_framecnt = 0U;
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_memory_bank    m_bank1;
	required_shared_ptr<u8> m_p_videoram;
	required_region_ptr<u8> m_p_chargen;
};


void k8915_state::k8915_a8_w(u8 data)
{
// seems to switch ram and rom around.
	m_bank1->set_entry((data == 0x87) ? 0 : 1);
}

void k8915_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).ram().share("mainram").bankr("bank1");
	map(0x1000, 0x17ff).ram().share("videoram");
	map(0x1800, 0xffff).ram();
}

void k8915_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x50, 0x53).rw("sio", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x58, 0x5b).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0xa8, 0xa8).w(FUNC(k8915_state::k8915_a8_w));
}

/* Input ports */
static INPUT_PORTS_START( k8915 )
INPUT_PORTS_END

void k8915_state::machine_reset()
{
	m_bank1->set_entry(1);
}

void k8915_state::machine_start()
{
	m_bank1->configure_entry(0, m_ram);
	m_bank1->configure_entry(1, m_rom);
	save_item(NAME(m_framecnt));
}

u32 k8915_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 sy=0,ma=0;

	m_framecnt++;

	for (u8 y = 0; y < 25; y++)
	{
		for (u8 ra = 0; ra < 10; ra++)
		{
			u16 *p = &bitmap.pix(sy++);

			for (u16 x = ma; x < ma + 80; x++)
			{
				u8 gfx = 0;

				if (ra < 9)
				{
					u8 chr = m_p_videoram[x];

					/* Take care of flashing characters */
					if ((chr & 0x80) && (m_framecnt & 0x08))
						chr = 0x20;

					chr &= 0x7f;

					gfx = m_p_chargen[(chr<<4) | ra ];
				}

				/* Display a scanline of a character */
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma+=80;
	}
	return 0;
}


void k8915_state::k8915(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'915'200) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &k8915_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &k8915_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(k8915_state::screen_update));
	screen.set_size(640, 250);
	screen.set_visarea(0, 639, 0, 249);
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	z80ctc_device& ctc(Z80CTC(config, "ctc", XTAL(4'915'200) / 2));
	ctc.set_clk<2>(XTAL(4'915'200) / 2);
	ctc.zc_callback<2>().set("sio", FUNC(z80sio_device::rxtxcb_w));

	z80sio_device& sio(Z80SIO(config, "sio", XTAL(4'915'200) / 2));
	sio.out_txdb_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	sio.out_dtrb_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	sio.out_rtsb_callback().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "keyboard"));
	rs232.rxd_handler().set("sio", FUNC(z80sio_device::rxb_w));
	rs232.dcd_handler().set("sio", FUNC(z80sio_device::dcdb_w));
	rs232.cts_handler().set("sio", FUNC(z80sio_device::ctsb_w));
}


/* ROM definition */
ROM_START( k8915 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "k8915.bin", 0x0000, 0x1000, CRC(ca70385f) SHA1(a34c14adae9be821678aed7f9e33932ee1f3e61c))

	/* character generator not dumped, using the one from 'c10' for now */
	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "c10_char.bin", 0x0000, 0x2000, BAD_DUMP CRC(cb530b6f) SHA1(95590bbb433db9c4317f535723b29516b9b9fcbf))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY     FULLNAME  FLAGS
COMP( 1982, k8915,  0,      0,      k8915,   k8915, k8915_state, empty_init, "Robotron", "K8915",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



kaypro.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/*************************************************************************************************

    The Kaypro 2/83 computer - the very first Kaypro II - 2 full height floppy drives.
    Each disk was single sided, and could hold 191k. The computer had 2x pio
    and 1x sio. One of the sio ports communicated with the keyboard with a coiled
    telephone cord, complete with modular plug on each end. The keyboard carries
    its own Intel 87C51 processor and is an intelligent device.

    Non-Linear Systems changed its name to Kaypro Corporation in July 1983.

    Kaypro 5 notes:
    The Kaycomp Kay Pro V / Non-Linear Systems Kaypro 5: https://retrocmp.de/kaypro/kay-p1_vers.htm#kay-5,
    a version of the Kaypro II, but with a 5MB harddisk instead of one of the floppy drives, was announced
    in December 1982, but apart from the relevant articles of the time, only one photo can be found.

    Kaypro 10 notes:
    This machine comes with a 10MB hard drive, split into 2 5MB partitions. It also has one floppy drive.
    The drive letters change depending on what drive it was booted from. The boot drive is always A:.
    - If booted from floppy:
      A: floppy
      B: HD partition 1
      C: HD partition 2
    - If booted from HD (presumably partition 1)
      A: HD partition 1
      B: HD partition 2
      C: floppy

    ToDo:
    - Find original Kaycomp ROMs
    - Need dump of 87C51 cpu in a Kaypro II keyboard variant

    - Kaypro 2x, 4a: floppy not working "No operating system present on this disk"
    - Kaypro 10: Boots from floppy, but needs hard drive added.
    - "Univeral"-ROM 81-478A: Boots, but fails with CP/M "BDOS Error on A:", was working until MAME v0190
    - fix Kayplus 84 ROM screen corruption
    - Kaypro iip88, 484p88: works as a normal Kaypro 4, extra hardware not done
    - Kaypro Robie: has twin 2.6MB 5.25 floppy drives which we don't support, no software available
    - Handyman ROM is really an add-on board with 16K proprietory RAM

    - Hard Disk not emulated.
      The controller is a WD1002-HD0 (original version, for Winchester drives), HD is 10MB, e.g. Shugart 712

    - Modem chips TMS99531, TMS99532 to be developed.

    - Once everything works, sort out parent and compat relationships.

**************************************************************************************************/

#include "emu.h"
#include "kaypro.h"
#include "formats/kaypro_dsk.h"

#include "bus/keytronic/keytronic.h"
#include "bus/rs232/rs232.h"
#include "machine/clock.h"
#include "machine/com8116.h"
#include "machine/z80sio.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


u8 kaypro84_state::kaypro484_87_r() { return 0x7f; }    /* to bypass unemulated HD controller */

/***********************************************************

    Address Maps

************************************************************/

void kaypro_state::kaypro_map(address_map &map)
{
	map(0x0000, 0x2fff).bankr(m_bankr).bankw(m_bankw);
	map(0x3000, 0x3fff).bankrw(m_bank3);
	map(0x4000, 0xffff).ram();
}

void kayproii_state::kayproii_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x03).w("brg", FUNC(com8116_device::stt_w));
	map(0x04, 0x07).rw("sio", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
	map(0x08, 0x0b).rw(m_pio_g, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x0c, 0x0f).w("brg", FUNC(com8116_device::str_w));
	map(0x10, 0x13).rw(m_fdc, FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	map(0x1c, 0x1f).rw(m_pio_s, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
}

void kaypro84_state::kaypro10_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x03).w("brg", FUNC(com8116_device::str_w));
	map(0x04, 0x07).rw("sio_1", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
	map(0x08, 0x0b).w("brg", FUNC(com8116_device::stt_w));
	map(0x0c, 0x0f).rw("sio_2", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x10, 0x13).rw(m_fdc, FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	map(0x14, 0x17).rw(FUNC(kaypro84_state::kaypro484_system_port_r), FUNC(kaypro84_state::kaypro484_system_port_w));
	map(0x18, 0x1b).w("cent_data_out", FUNC(output_latch_device::write));
	map(0x1c, 0x1c).rw(FUNC(kaypro84_state::kaypro484_status_r), FUNC(kaypro84_state::kaypro484_index_w));
	map(0x1d, 0x1d).r(m_crtc, FUNC(mc6845_device::register_r)).w(FUNC(kaypro84_state::kaypro484_register_w));
	map(0x1f, 0x1f).rw(FUNC(kaypro84_state::kaypro484_videoram_r), FUNC(kaypro84_state::kaypro484_videoram_w));

	/* The below are not emulated */
/*  map(0x80, 0x80) Hard drive controller card I/O port - 10MB hard drive only fitted to the Kaypro 10
    map(0x81, 0x81) Hard Drive READ error register, WRITE precomp
    map(0x82, 0x82) Hard Drive Sector register count I/O
    map(0x83, 0x83) Hard Drive Sector register number I/O
    map(0x84, 0x84) Hard Drive Cylinder low register I/O
    map(0x85, 0x85) Hard Drive Cylinder high register I/O
    map(0x86, 0x86) Hard Drive Size / Drive / Head register I/O
    map(0x87, 0x87) Hard Drive READ status register, WRITE command register */
	map(0x20, 0x86).noprw();
	map(0x87, 0x87).r(FUNC(kaypro84_state::kaypro484_87_r));
}

void kaypro84_state::kaypro484_io(address_map &map)
{
	kaypro10_io(map);
	map(0x20, 0x23).rw("z80pio", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x24, 0x24).mirror(3).rw(FUNC(kaypro84_state::rtc_r), FUNC(kaypro84_state::rtc_w));
}


static INPUT_PORTS_START(kaypro)
	// everything comes from the keyboard device
INPUT_PORTS_END


/***************************************************************

    F4 CHARACTER DISPLAYER

****************************************************************/
static const gfx_layout kayproii_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static const gfx_layout kaypro484_charlayout =
{
	8, 16,                  /* 8 x 16 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_kayproii )
	GFXDECODE_ENTRY( "chargen", 0x0000, kayproii_charlayout, 0, 1 )
GFXDECODE_END

static GFXDECODE_START( gfx_kaypro484 )
	GFXDECODE_ENTRY( "chargen", 0x0000, kaypro484_charlayout, 0, 1 )
GFXDECODE_END

/***************************************************************

    Interfaces

****************************************************************/

static const z80_daisy_config kayproii_daisy_chain[] =
{
	{ "sio" },          /* sio */
	{ "z80pio_s" },     /* System pio */
	{ "z80pio_g" },     /* General purpose pio */
	{ nullptr }
};

static const z80_daisy_config kaypro10_daisy_chain[] =
{
	{ "sio_1" },        /* sio for RS232C and keyboard */
	{ "sio_2" },        /* sio for serial printer and inbuilt modem */
	{ nullptr }
};

static const z80_daisy_config kaypro484_daisy_chain[] =
{
	{ "z80pio" },
	{ "sio_1" },        /* sio for RS232C and keyboard */
	{ "sio_2" },        /* sio for serial printer and inbuilt modem */
	{ nullptr }
};


/***********************************************************

    Machine Driver

************************************************************/


static void kaypro_floppies(device_slot_interface &device)
{
	device.option_add("525ssdd", FLOPPY_525_SSDD);
	device.option_add("525dd", FLOPPY_525_DD);
	device.option_add("525qd", FLOPPY_525_QD);
}

void kaypro_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_KAYPROII_FORMAT);
	fr.add(FLOPPY_KAYPRO2X_FORMAT);
}

void kayproii_state::kayproii(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 20_MHz_XTAL / 8);
	m_maincpu->set_addrmap(AS_PROGRAM, &kayproii_state::kaypro_map);
	m_maincpu->set_addrmap(AS_IO, &kayproii_state::kayproii_io);
	m_maincpu->set_daisy_config(kayproii_daisy_chain);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER, rgb_t::green());
	m_screen->set_raw(13.9776_MHz_XTAL, 128 * 7, 0, 80 * 7, 260, 0, 240);
	m_screen->set_screen_update(FUNC(kayproii_state::screen_update_kayproii));
	m_screen->set_palette(m_palette);

	TIMER(config, m_floppy_timer).configure_generic(FUNC(kayproii_state::floppy_timer));

	GFXDECODE(config, "gfxdecode", m_palette, gfx_kayproii);
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 950).add_route(ALL_OUTPUTS, "mono", 1.00); /* piezo-device needs to be measured */

	/* devices */
	QUICKLOAD(config, "quickload", "com,cpm", attotime::from_seconds(3)).set_load_callback(FUNC(kayproii_state::quickload_cb));

	keytronic_connector_device &kbd(KEYTRONIC_CONNECTOR(config, "kbd", kaypro_keyboards, "kayproii"));
	kbd.ser_out_callback().set("sio", FUNC(z80sio_device::rxb_w));
	kbd.ser_out_callback().append("sio", FUNC(z80sio_device::syncb_w));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(kayproii_state::write_centronics_busy));

	output_latch_device &latch(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(latch);

	rs232_port_device &serial(RS232_PORT(config, "serial", default_rs232_devices, nullptr));
	serial.rxd_handler().set("sio", FUNC(z80sio_device::rxa_w));
	serial.rxd_handler().append("sio", FUNC(z80sio_device::synca_w)); // TODO: confirm this is connected
	serial.cts_handler().set("sio", FUNC(z80sio_device::ctsa_w));
	serial.dcd_handler().set("sio", FUNC(z80sio_device::dcda_w));

	com8116_device &brg(COM8116(config, "brg", 5.0688_MHz_XTAL)); // WD1943, SMC8116
	brg.ft_handler().set("sio", FUNC(z80sio_device::rxca_w));
	brg.ft_handler().append("sio", FUNC(z80sio_device::txca_w));
	brg.fr_handler().set("sio", FUNC(z80sio_device::rxtxcb_w));

	Z80PIO(config, m_pio_g, 20_MHz_XTAL / 8);
	m_pio_g->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio_g->out_pa_callback().set("cent_data_out", FUNC(output_latch_device::write));

	Z80PIO(config, m_pio_s, 20_MHz_XTAL / 8);
	m_pio_s->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio_s->in_pa_callback().set(FUNC(kayproii_state::pio_system_r));
	m_pio_s->out_pa_callback().set(FUNC(kayproii_state::kayproii_pio_system_w));

	z80sio_device& sio(Z80SIO(config, "sio", 20_MHz_XTAL / 8));
	sio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	sio.out_txda_callback().set("serial", FUNC(rs232_port_device::write_txd));
	sio.out_dtra_callback().set("serial", FUNC(rs232_port_device::write_dtr));
	sio.out_rtsa_callback().set("serial", FUNC(rs232_port_device::write_rts));
	sio.out_txdb_callback().set("kbd", FUNC(keytronic_connector_device::ser_in_w));

	FD1793(config, m_fdc, 20_MHz_XTAL / 20);
	m_fdc->intrq_wr_callback().set(FUNC(kayproii_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(kayproii_state::fdc_drq_w));
	m_fdc->set_force_ready(true);
	FLOPPY_CONNECTOR(config, "fdc:0", kaypro_floppies, "525ssdd", kayproii_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", kaypro_floppies, "525ssdd", kayproii_state::floppy_formats).enable_sound(true);
	SOFTWARE_LIST(config, "flop_list").set_original("kaypro").set_filter("A");
}

void kayproii_state::kayproiv(machine_config &config)
{
	kayproii(config);
	m_pio_s->out_pa_callback().set(FUNC(kayproii_state::kayproiv_pio_system_w));
	config.device_remove("fdc:0");
	config.device_remove("fdc:1");
	FLOPPY_CONNECTOR(config, "fdc:0", kaypro_floppies, "525dd", kayproii_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", kaypro_floppies, "525dd", kayproii_state::floppy_formats).enable_sound(true);
	SOFTWARE_LIST(config.replace(), "flop_list").set_original("kaypro").set_filter("D");
}

void kayproii_state::omni2(machine_config &config)
{
	kayproiv(config);
	m_screen->set_screen_update(FUNC(kayproii_state::screen_update_omni2));
}

void kaypro84_state::kaypro484(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &kaypro84_state::kaypro_map);
	m_maincpu->set_addrmap(AS_IO, &kaypro84_state::kaypro484_io);
	m_maincpu->set_daisy_config(kaypro484_daisy_chain);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(18_MHz_XTAL, 856, 0, 640, 426, 0, 400);
	m_screen->set_screen_update(FUNC(kaypro84_state::screen_update_kaypro484));

	TIMER(config, m_floppy_timer).configure_generic(FUNC(kaypro84_state::floppy_timer));

	GFXDECODE(config, "gfxdecode", m_palette, gfx_kaypro484);
	PALETTE(config, m_palette, FUNC(kaypro84_state::kaypro_palette), 3);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 950).add_route(ALL_OUTPUTS, "mono", 1.00); // piezo-device needs to be measured

	/* devices */
	MC6845(config, m_crtc, 18_MHz_XTAL / 8);
	m_crtc->set_screen(m_screen);
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(kaypro84_state::kaypro484_update_row));

	QUICKLOAD(config, "quickload", "com,cpm", attotime::from_seconds(3)).set_load_callback(FUNC(kaypro84_state::quickload_cb));

	keytronic_connector_device &kbd(KEYTRONIC_CONNECTOR(config, "kbd", kaypro_keyboards, "kaypro10"));
	kbd.ser_out_callback().set("sio_1", FUNC(z80sio_device::rxb_w));
	kbd.ser_out_callback().append("sio_1", FUNC(z80sio_device::syncb_w));

	CLOCK(config, "kbdtxrxc", 16_MHz_XTAL / 16 / 13 / 16).signal_handler().set("sio_1", FUNC(z80sio_device::rxtxcb_w));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(kaypro84_state::write_centronics_busy));

	output_latch_device &latch(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(latch);

	rs232_port_device &modem(RS232_PORT(config, "modem", default_rs232_devices, nullptr));
	modem.rxd_handler().set("sio_1", FUNC(z80sio_device::rxa_w));
	modem.rxd_handler().append("sio_1", FUNC(z80sio_device::synca_w));
	modem.cts_handler().set("sio_1", FUNC(z80sio_device::ctsa_w));
	modem.dcd_handler().set("sio_1", FUNC(z80sio_device::dcda_w));

	rs232_port_device &serprn(RS232_PORT(config, "serprn", default_rs232_devices, nullptr));
	serprn.rxd_handler().set("sio_2", FUNC(z80sio_device::rxa_w));
	serprn.rxd_handler().append("sio_2", FUNC(z80sio_device::synca_w));
	serprn.cts_handler().set("sio_2", FUNC(z80sio_device::ctsa_w));

	z80sio_device& sio_1(Z80SIO(config, "sio_1", 16_MHz_XTAL / 4));
	sio_1.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0); // FIXME: use a combiner
	sio_1.out_txda_callback().set("modem", FUNC(rs232_port_device::write_txd));
	sio_1.out_dtra_callback().set("modem", FUNC(rs232_port_device::write_dtr));
	sio_1.out_rtsa_callback().set("modem", FUNC(rs232_port_device::write_rts));
	sio_1.out_txdb_callback().set("kbd", FUNC(keytronic_connector_device::ser_in_w));

	z80sio_device& sio_2(Z80SIO(config, "sio_2", 16_MHz_XTAL / 4)); /* extra sio for modem and printer */
	sio_2.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0); // FIXME: use a combiner
	sio_2.out_txda_callback().set("serprn", FUNC(rs232_port_device::write_txd));

	com8116_device &brg(COM8116(config, "brg", 5.0688_MHz_XTAL)); // WD1943, SMC8116
	brg.fr_handler().set("sio_1", FUNC(z80sio_device::rxca_w));
	brg.fr_handler().append("sio_1", FUNC(z80sio_device::txca_w));
	brg.ft_handler().set("sio_2", FUNC(z80sio_device::rxca_w));
	brg.ft_handler().append("sio_2", FUNC(z80sio_device::txca_w));

	FD1793(config, m_fdc, 16_MHz_XTAL / 16);
	m_fdc->intrq_wr_callback().set(FUNC(kaypro84_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(kaypro84_state::fdc_drq_w));
	m_fdc->set_force_ready(true);
	FLOPPY_CONNECTOR(config, "fdc:0", kaypro_floppies, "525dd", kaypro84_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", kaypro_floppies, "525dd", kaypro84_state::floppy_formats).enable_sound(true);
	SOFTWARE_LIST(config, "flop_list").set_original("kaypro").set_filter("C");

	z80pio_device &pio(Z80PIO(config, "z80pio", 16_MHz_XTAL / 4));
	pio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	pio.out_pa_callback().set(FUNC(kaypro84_state::rtc_address_w));

	MM58167(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->irq().set("z80pio", FUNC(z80pio_device::pa6_w));
}

void kaypro84_state::kaypro4x(machine_config &config)
{
	kaypro484(config);
	m_fdc->set_clock(16_MHz_XTAL / 8);
	m_fdc->set_force_ready(false);
	SOFTWARE_LIST(config.replace(), "flop_list").set_original("kaypro").set_filter("F");
}

void kaypro84_state::kaypro1(machine_config &config)
{
	kaypro484(config);
	SOFTWARE_LIST(config.replace(), "flop_list").set_original("kaypro").set_filter("G");
}

void kaypro84_state::kaypro10(machine_config &config)
{
	kaypro484(config);
	m_maincpu->set_addrmap(AS_IO, &kaypro84_state::kaypro10_io);
	m_maincpu->set_daisy_config(kaypro10_daisy_chain);
	config.device_remove("z80pio");
	config.device_remove("rtc");
	config.device_remove("fdc:1");  // only has 1 floppy drive
	// need to add hard drive & controller
	SOFTWARE_LIST(config.replace(), "flop_list").set_original("kaypro").set_filter("E");
}

void kaypro84_state::kaypro1084(machine_config &config)
{
	kaypro484(config);
	config.device_remove("fdc:1");  // only has 1 floppy drive
	// need to add hard drive & controller
	SOFTWARE_LIST(config.replace(), "flop_list").set_original("kaypro").set_filter("E");
}

void kaypro84_state::kaypronew2(machine_config &config)
{
	kaypro484(config);
	config.device_remove("fdc:1");  // only has 1 floppy drive
	SOFTWARE_LIST(config.replace(), "flop_list").set_original("kaypro").set_filter("G");
}

void kaypro84_state::kaypro284(machine_config &config)
{
	kaypro484(config);
	config.device_remove("fdc:0");
	config.device_remove("fdc:1");
	FLOPPY_CONNECTOR(config, "fdc:0", kaypro_floppies, "525ssdd", kaypro84_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", kaypro_floppies, "525ssdd", kaypro84_state::floppy_formats).enable_sound(true);
	SOFTWARE_LIST(config.replace(), "flop_list").set_original("kaypro").set_filter("B");
}

void kaypro_state::init_kaypro()
{
	m_ram = make_unique_clear<u8[]>(0x4000);
	m_vram = make_unique_clear<u8[]>(0x1000);
	m_dummy = std::make_unique<u8[]>(0x3000);

	u8 *m = memregion("roms")->base();
	u8 *r = m_ram.get();
	u8 *v = m_vram.get();
	u8 *d = m_dummy.get();

	m_bankr->configure_entry(1, &m[0]);
	m_bankr->configure_entry(0, r);
	m_bankw->configure_entry(1, d);
	m_bankw->configure_entry(0, r);
	m_bank3->configure_entry(1, v);
	m_bank3->configure_entry(0, r+0x3000);
}


/***********************************************************

    Game driver

************************************************************/

/* The detested bios "universal rom" is part number 81-478 */

// Kaypro II
ROM_START(kayproii)
	/* The original board could take a 2716 or 2732 */
	ROM_REGION(0x3000, "roms",0)
	ROM_SYSTEM_BIOS( 0, "149", "81-149 for Kaypro Bd. 81-110")
	ROMX_LOAD("81-149.u47",   0x0000, 0x0800, CRC(28264bc1) SHA1(a12afb11a538fc0217e569bc29633d5270dfa51b), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "149b", "81-149B for Kaypro Bd. 81-110")
	ROMX_LOAD("81-149b.u47",  0x0000, 0x0800, CRC(c008549e) SHA1(b9346a16f5f9ffb6bb0eb1766c348b74056485a8), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "149c", "81-149C for Kaypro Bd. 81-110")
	ROMX_LOAD("81-149c.u47",  0x0000, 0x0800, CRC(1272aa65) SHA1(027fee2f5f17ba71a4738f00188e132e326536ff), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "232", "81-232 for Kaypro Bd. 81-240")
	ROMX_LOAD("81-232.u47",   0x0000, 0x1000, CRC(4918fb91) SHA1(cd9f45cc3546bcaad7254b92c5d501c40e2ef0b2), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "roadrunner", "Highland Microkit Roadrunner 1.5")
	ROMX_LOAD("kaypro_ii_roadrunner_1_5.bin",  0x0000, 0x1000, CRC(ca11357d) SHA1(8e8a6d6e0d31d1051db9a24601f12a3b4639b3bb), ROM_BIOS(4) ) // does not boot here but originally comes from a II
	ROM_SYSTEM_BIOS( 5, "turbo", "Advent Turbo ROM")
	ROMX_LOAD("trom34_3.rom",  0x0000, 0x1000, CRC(908a4c0e) SHA1(6e220479715a812d9116b0927a9ff2792f82b2a7), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6, "kplus83", "MICROCode Consulting KayPLUS 83")
	ROMX_LOAD("kplus83.rom",  0x0000, 0x2000, CRC(5e9b817d) SHA1(26ea875ee3659a964cbded4ed0c82a3af42db64b), ROM_BIOS(6) )
	ROM_SYSTEM_BIOS( 7, "pro8v3", "MicroCornucopia_Pro8_V3.3")
	ROMX_LOAD("pro8-3.rom",  0x0000, 0x1000, CRC(f2d4c598) SHA1(269b2fddeb98db3e5eba2056ff250dff72b0561e), ROM_BIOS(7) )

	ROM_REGION(0x0800, "chargen", ROMREGION_INVERT)
	ROM_LOAD("81-146.u43",   0x0000, 0x0800, CRC(4cc7d206) SHA1(5cb880083b94bd8220aac1f87d537db7cfeb9013) )
ROM_END

// Kaypro IV (or 4'83)
ROM_START(kayproiv)
	ROM_REGION(0x3000, "roms",0)
	ROM_SYSTEM_BIOS( 0, "232", "81-232 for Kaypro Bd. 81-240")
	ROMX_LOAD("81-232.u47",   0x0000, 0x1000, CRC(4918fb91) SHA1(cd9f45cc3546bcaad7254b92c5d501c40e2ef0b2), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "roadrunner", "Highland Microkit Roadrunner 1.5")
	ROMX_LOAD("kaypro_ii_roadrunner_1_5.bin",  0x0000, 0x1000, CRC(ca11357d) SHA1(8e8a6d6e0d31d1051db9a24601f12a3b4639b3bb), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "turbo", "Advent Turbo ROM")
	ROMX_LOAD("trom34_3.rom",  0x0000, 0x1000, CRC(908a4c0e) SHA1(6e220479715a812d9116b0927a9ff2792f82b2a7), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "kplus83", "MICROCode Consulting KayPLUS 83")
	ROMX_LOAD("kplus83.rom",  0x0000, 0x2000, CRC(5e9b817d) SHA1(26ea875ee3659a964cbded4ed0c82a3af42db64b), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "pro8v3", "MicroCornucopia_Pro8_V3.3")
	ROMX_LOAD("pro8-3.rom",  0x0000, 0x1000, CRC(f2d4c598) SHA1(269b2fddeb98db3e5eba2056ff250dff72b0561e), ROM_BIOS(4) )

	ROM_REGION(0x0800, "chargen", ROMREGION_INVERT)
	ROM_LOAD("81-146.u43",   0x0000, 0x0800, CRC(4cc7d206) SHA1(5cb880083b94bd8220aac1f87d537db7cfeb9013) )
ROM_END

// Kaypro 10, '83 model, hard disk cable connector in the middle of the mainboard, no space for modem or RTC
ROM_START(kaypro10)
	ROM_REGION(0x3000, "roms",0)
	ROM_SYSTEM_BIOS( 0, "188", "V1.9 for Kaypro Bd. 81-180")
	ROMX_LOAD("81-188.u42",   0x0000, 0x1000, CRC(6cbd6aa0) SHA1(47004f8c6e17407e4f8d613c9520f9316716d9e2), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "x", "V1.7")
	ROMX_LOAD("x.bin",        0x0000, 0x0fff, BAD_DUMP CRC(01e2e7b2) SHA1(fc2f8dc8a077d0c89a74463328efa1c444662d88), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "turbo", "Advent Turbo ROM")
	ROMX_LOAD("trom34.rom",   0x0000, 0x2000, CRC(0ec6d39a) SHA1(8c2a92b8642e144452c28300bf50a00a11a060cd), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "kplus83", "MICROCode Consulting KayPLUS 83")
	ROMX_LOAD("kplus83.rom",  0x0000, 0x2000, CRC(5e9b817d) SHA1(26ea875ee3659a964cbded4ed0c82a3af42db64b), ROM_BIOS(3) )

	ROM_REGION(0x1000, "chargen",0)
	ROM_LOAD("81-187.u31",   0x0000, 0x1000, CRC(5f72da5b) SHA1(8a597000cce1a7e184abfb7bebcb564c6bf24fb7) )
ROM_END

// Kaypro 10, '84 model, hard disk cable on the right hand side of the mainboard
ROM_START(kaypro1084)
	ROM_REGION(0x3000, "roms",0)
	ROM_SYSTEM_BIOS( 0, "302", "V1.9E for Kaypro Bd. 81-181")
	ROMX_LOAD("81-302.u42",   0x0000, 0x1000, CRC(3f9bee20) SHA1(b29114a199e70afe46511119b77a662e97b093a0), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "1.9ee", "V1.9ee")
	ROMX_LOAD("rom19ee.bin",  0x0000, 0x0fee, BAD_DUMP CRC(c3515bd0) SHA1(48a0a43c164e4d3e75e8e916498421ef616943cf), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "277", "V1.9E(F)")
	ROMX_LOAD("81-277.u42",   0x0000, 0x1000, CRC(e4e1831f) SHA1(1de31ed532a461ace7a4abad1f6647eeddceb3e7), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "478", "V2.01 for Kaypro Bd. 81-582 (universal)")
	ROMX_LOAD("81-478.u42",   0x0000, 0x2000, CRC(de618380) SHA1(c8d6312e6eeb62a53e741f1ff3b878bdcb7b5aaa), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "turbo", "Advent Turbo ROM")
	ROMX_LOAD("trom34.rom",   0x0000, 0x2000, CRC(0ec6d39a) SHA1(8c2a92b8642e144452c28300bf50a00a11a060cd), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "kplus", "MICROCode Consulting KayPLUS 84")
	ROMX_LOAD("kplus84.rom",   0x0000, 0x2000, CRC(4551905a) SHA1(48f0964edfad05b214810ae5595638245c30e5c0), ROM_BIOS(5) )

	ROM_REGION(0x1000, "chargen",0)
	ROM_LOAD("81-187.u31",   0x0000, 0x1000, CRC(5f72da5b) SHA1(8a597000cce1a7e184abfb7bebcb564c6bf24fb7) )
ROM_END

ROM_START(kaypro484) // later renamed in 2X (or 2X MTC to signify the inclusion of Modem and RTC in comparison with the "old" 2X)
	ROM_REGION(0x3000, "roms",0)
	ROM_SYSTEM_BIOS( 0, "292a", "81-292a for Kaypro Bd. 81-184")
	ROMX_LOAD("81-292a.u34",  0x0000, 0x1000, CRC(241f27a5) SHA1(82711289d19e9b165e35324da010466d225e503a), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "turbo", "Advent Turbo ROM")
	ROMX_LOAD("trom34.rom",   0x0000, 0x2000, CRC(0ec6d39a) SHA1(8c2a92b8642e144452c28300bf50a00a11a060cd), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "kplus", "MICROCode Consulting KayPLUS 84")
	ROMX_LOAD("kplus84.rom",   0x0000, 0x2000, CRC(4551905a) SHA1(48f0964edfad05b214810ae5595638245c30e5c0), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "pro884", "MicroCornucopia pro884 SuperMax 2.7")
	ROMX_LOAD("pro884mx.rom",   0x0000, 0x2000, CRC(febc6f51) SHA1(1f009aa9b7c9a3eddd0ee6ea7321a1c47c3e9807), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "pro8v5", "MicroCornucopia Pro8 V5")
	ROMX_LOAD("pro884v5.rom",  0x0000, 0x2000, CRC(fe0051b1) SHA1(cac429154d40e21174ae05ceb0017b62473cdebd), ROM_BIOS(4) )

	ROM_REGION(0x1000, "chargen",0)
	ROM_LOAD("81-235.u9",    0x0000, 0x1000, CRC(5f72da5b) SHA1(8a597000cce1a7e184abfb7bebcb564c6bf24fb7) )
ROM_END

 // Kaypro 2'84, like the 4'84, but two single sided drives, no RTC, no modem
ROM_START(kaypro284)
	ROM_REGION(0x3000, "roms",0)
	ROM_SYSTEM_BIOS( 0, "292a", "81-292a for Kaypro Bd. 81-184")
	ROMX_LOAD("81-292a.u34",  0x0000, 0x1000, CRC(241f27a5) SHA1(82711289d19e9b165e35324da010466d225e503a), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "turbo", "Advent Turbo ROM")
	ROMX_LOAD("trom34.rom",   0x0000, 0x2000, CRC(0ec6d39a) SHA1(8c2a92b8642e144452c28300bf50a00a11a060cd), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "kplus", "MICROCode Consulting KayPLUS 84")
	ROMX_LOAD("kplus84.rom",   0x0000, 0x2000, CRC(4551905a) SHA1(48f0964edfad05b214810ae5595638245c30e5c0), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "pro884", "MicroCornucopia pro884 SuperMax 2.7")
	ROMX_LOAD("pro884mx.rom",   0x0000, 0x2000, CRC(febc6f51) SHA1(1f009aa9b7c9a3eddd0ee6ea7321a1c47c3e9807), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "pro8v5", "MicroCornucopia Pro8 V5")
	ROMX_LOAD("pro884v5.rom",  0x0000, 0x2000, CRC(fe0051b1) SHA1(cac429154d40e21174ae05ceb0017b62473cdebd), ROM_BIOS(4) )

	ROM_REGION(0x1000, "chargen",0)
	ROM_LOAD("81-235.u9",    0x0000, 0x1000, CRC(5f72da5b) SHA1(8a597000cce1a7e184abfb7bebcb564c6bf24fb7) )
ROM_END

// Kaypro 2X, a 4'84 without modem and RTC, later the 4'84 is renamed 2X, this fully decked out variant is called 2X MTC
ROM_START(kaypro2x)
	ROM_REGION(0x8000, "roms",0)
	ROM_SYSTEM_BIOS( 0, "292a", "81-292a for Kaypro Bd. 81-184")
	ROMX_LOAD("81-292a.u34",  0x0000, 0x1000, CRC(241f27a5) SHA1(82711289d19e9b165e35324da010466d225e503a), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "292", "V2.00 (early universal)" )
	ROMX_LOAD("81-292.u34",   0x0000, 0x2000, CRC(5eb69aec) SHA1(525f955ca002976e2e30ac7ee37e4a54f279fe96), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "478", "V2.01 for Kaypro Bd. 81-580 (universal)")
	ROMX_LOAD("81-478.u42",   0x0000, 0x2000, CRC(de618380) SHA1(c8d6312e6eeb62a53e741f1ff3b878bdcb7b5aaa), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "turbo", "Advent Turbo ROM")
	ROMX_LOAD("trom34.rom",   0x0000, 0x2000, CRC(0ec6d39a) SHA1(8c2a92b8642e144452c28300bf50a00a11a060cd), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "kplus", "MICROCode Consulting KayPLUS 84")
	ROMX_LOAD("kplus84.rom",   0x0000, 0x2000, CRC(4551905a) SHA1(48f0964edfad05b214810ae5595638245c30e5c0), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "pro884", "MicroCornucopia pro884 SuperMax 2.7")
	ROMX_LOAD("pro884mx.rom",   0x0000, 0x2000, CRC(febc6f51) SHA1(1f009aa9b7c9a3eddd0ee6ea7321a1c47c3e9807), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6, "pro8v5", "MicroCornucopia Pro8 V5")
	ROMX_LOAD("pro884v5.rom",  0x0000, 0x2000, CRC(fe0051b1) SHA1(cac429154d40e21174ae05ceb0017b62473cdebd), ROM_BIOS(6) )
	ROM_SYSTEM_BIOS( 7, "handyman", "Hitech Research Handyman")                                                             // http://content.thetechnickel.com/misc/kaypro-handyman/kaypro-4-plus-88-06.jpg
	ROMX_LOAD( "handyman.bin", 0x0000, 0x8000, CRC(f020d82c) SHA1(576a6608270d4ec7cf814c9de46ecf4e2869d30a), ROM_BIOS(7) )  // fits any classic Kaypro, needs its own 16K RAM

	ROM_REGION(0x1000, "chargen",0)
	ROM_LOAD("81-235.u9",    0x0000, 0x1000, CRC(5f72da5b) SHA1(8a597000cce1a7e184abfb7bebcb564c6bf24fb7) )
ROM_END

// Kaypro II'84 plus 88, the "KAYPRO-88" board has 128k or 256k of its own ram on it, it's a factory installed SWP CoPower 88
ROM_START(kayproiip88)
	ROM_REGION(0x3000, "roms",0)
	ROM_SYSTEM_BIOS( 0, "232", "81-232 for Kaypro Bd. 81-240")
	ROMX_LOAD("81-232.u47",   0x0000, 0x1000, CRC(4918fb91) SHA1(cd9f45cc3546bcaad7254b92c5d501c40e2ef0b2), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "kplus83", "MICROCode Consulting KayPLUS 83")
	ROMX_LOAD("kplus83.rom",  0x0000, 0x2000, CRC(5e9b817d) SHA1(26ea875ee3659a964cbded4ed0c82a3af42db64b), ROM_BIOS(1) )

	ROM_REGION(0x0800, "chargen", ROMREGION_INVERT)
	ROM_LOAD("81-146.u43",   0x0000, 0x0800, CRC(4cc7d206) SHA1(5cb880083b94bd8220aac1f87d537db7cfeb9013) )

	ROM_REGION(0x1000, "8088cpu",0)
	ROM_LOAD("81-356.u29",   0x0000, 0x1000, CRC(948556db) SHA1(6e779866d099cc0dc8c6369bdfb37a923ac448a4) )
ROM_END

// Kaypro 4'84 plus 88, the "KAYPRO-88" board has 128k or 256k of its own ram on it, it's a factory installed SWP CoPower 88
ROM_START(kaypro484p88)
	ROM_REGION(0x3000, "roms",0)
	ROM_SYSTEM_BIOS( 0, "292a", "292A")
	ROMX_LOAD("81-292a.u34",  0x0000, 0x1000, CRC(241f27a5) SHA1(82711289d19e9b165e35324da010466d225e503a), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "kplus", "MICROCode Consulting KayPLUS 84")
	ROMX_LOAD("kplus84.rom",   0x0000, 0x2000, CRC(4551905a) SHA1(48f0964edfad05b214810ae5595638245c30e5c0), ROM_BIOS(1) )

	ROM_REGION(0x1000, "chargen",0)
	ROM_LOAD("81-235.u9",    0x0000, 0x1000, CRC(5f72da5b) SHA1(8a597000cce1a7e184abfb7bebcb564c6bf24fb7) )
ROM_END

// Kaypro New 2, no modem, no RTC, single DS/DD disk drive
ROM_START(kaypronew2)
	ROM_REGION(0x3000, "roms",0)
	ROM_SYSTEM_BIOS( 0, "478", "V2.01 for Kaypro Bd. 81-294 (universal")
	ROMX_LOAD("81-478.u42",   0x0000, 0x2000, CRC(de618380) SHA1(c8d6312e6eeb62a53e741f1ff3b878bdcb7b5aaa), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "kplus", "MICROCode Consulting KayPLUS 84")
	ROMX_LOAD("kplus84.rom",   0x0000, 0x2000, CRC(4551905a) SHA1(48f0964edfad05b214810ae5595638245c30e5c0), ROM_BIOS(1) )

	ROM_REGION(0x1000, "chargen",0)
	ROM_LOAD("81-235.u9",    0x0000, 0x1000, CRC(5f72da5b) SHA1(8a597000cce1a7e184abfb7bebcb564c6bf24fb7) )
ROM_END

// "Desktop" PC with two high density, 2.8MB floppy disk drivers over the monitor
ROM_START(robie)
	ROM_REGION(0x3000, "roms",0)
	ROM_SYSTEM_BIOS( 0, "326", "V1.7R")
	ROMX_LOAD("81-326.u34",   0x0000, 0x2000, CRC(7f0c3f68) SHA1(54b088a1b2200f9df4b9b347bbefb0115f3a4976), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "u", "V1.4")
	ROMX_LOAD("robie_u.u34",  0x0000, 0x2000, CRC(da7248b5) SHA1(1dc053b3e44ead47255cc166b7b4b0adaeb3dd3d), ROM_BIOS(1) ) // rom number unknown

	ROM_REGION(0x1000, "chargen",0)
	ROM_LOAD("81-235.u9",    0x0000, 0x1000, CRC(5f72da5b) SHA1(8a597000cce1a7e184abfb7bebcb564c6bf24fb7) )
ROM_END

// Kaypro 4X, a Robie in the standard portable Kaypro enclosure
ROM_START(kaypro4x)
	ROM_REGION(0x3000, "roms",0)
	ROM_LOAD("81-326.u34",   0x0000, 0x2000, CRC(7f0c3f68) SHA1(54b088a1b2200f9df4b9b347bbefb0115f3a4976) )

	ROM_REGION(0x1000, "chargen",0)
	ROM_LOAD("81-235.u9",    0x0000, 0x1000, CRC(5f72da5b) SHA1(8a597000cce1a7e184abfb7bebcb564c6bf24fb7) )
ROM_END

// Kaypro 1, equivalent to "old" 2X, before 4'84 became 2X
ROM_START(kaypro1)
	ROM_REGION(0x3000, "roms",0)
	ROM_SYSTEM_BIOS( 0, "478", "V2.01 for Kaypro Bd. 81-294 (universal")
	ROMX_LOAD("81-478.u42",   0x0000, 0x2000, CRC(de618380) SHA1(c8d6312e6eeb62a53e741f1ff3b878bdcb7b5aaa), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "turbo", "Advent Turbo ROM")
	ROMX_LOAD("trom34.rom",   0x0000, 0x2000, CRC(0ec6d39a) SHA1(8c2a92b8642e144452c28300bf50a00a11a060cd), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "kplus", "MICROCode Consulting KayPLUS 84")
	ROMX_LOAD("kplus84.rom",   0x0000, 0x2000, CRC(4551905a) SHA1(48f0964edfad05b214810ae5595638245c30e5c0), ROM_BIOS(2) )

	ROM_REGION(0x1000, "chargen",0)
	ROM_LOAD("81-235.u9",    0x0000, 0x1000, CRC(5f72da5b) SHA1(8a597000cce1a7e184abfb7bebcb564c6bf24fb7) )
ROM_END

// Omni II logic analyzer
ROM_START(omni2)
	ROM_REGION(0x3000, "roms",0)
	ROM_LOAD("omni2.u47",    0x0000, 0x1000, CRC(2883f9e0) SHA1(d98c784e62853582d298bf7ca84c75872847ac9b) )

	ROM_REGION(0x0800, "chargen", ROMREGION_INVERT)
	ROM_LOAD("omni2.u43",    0x0000, 0x0800, CRC(049b3381) SHA1(46f1d4f038747ba9048b075dc617361be088f82a) )
ROM_END

// Omni 4 logic analyzer
ROM_START(omni4)
	ROM_REGION(0x3000, "roms",0)
	ROM_LOAD("omni4.u34",    0x0000, 0x2000, CRC(f24e8521) SHA1(374f2e2b791a807f103744a22c9c8f3af55f1033) )

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("omni4.u9",    0x0000, 0x1000, CRC(579665a6) SHA1(261fcdc5a44821de9484340cbe429110400140b4) )
ROM_END


/*    YEAR  NAME          PARENT     COMPAT  MACHINE     INPUT   CLASS           INIT         COMPANY               FULLNAME */
COMP( 1982, kayproii,     0,         0,      kayproii,   kaypro, kayproii_state, init_kaypro, "Non-Linear Systems", "Kaypro II - 2/83", MACHINE_SUPPORTS_SAVE )
COMP( 1983, kayproiv,     kayproii,  0,      kayproiv,   kaypro, kayproii_state, init_kaypro, "Non-Linear Systems", "Kaypro IV - 4/83", MACHINE_SUPPORTS_SAVE ) // model 81-004
COMP( 1983, kaypro10,     0,         0,      kaypro10,   kaypro, kaypro84_state, init_kaypro, "Non-Linear Systems", "Kaypro 10 - 1983", MACHINE_SUPPORTS_SAVE )
COMP( 1983, kayproiip88,  kayproii,  0,      kayproii,   kaypro, kayproii_state, init_kaypro, "Kaypro Corporation", "Kaypro 4 plus88 - 4/83" , MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) // model 81-004 with an added 8088 daughterboard and rom
COMP( 1984, kaypro484,    0,         0,      kaypro484,  kaypro, kaypro84_state, init_kaypro, "Kaypro Corporation", "Kaypro 4/84", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) // model 81-015
COMP( 1984, kaypro284,    kaypro484, 0,      kaypro284,  kaypro, kaypro84_state, init_kaypro, "Kaypro Corporation", "Kaypro 2/84", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) // model 81-015
COMP( 1984, kaypro484p88, kaypro484, 0,      kaypro484,  kaypro, kaypro84_state, init_kaypro, "Kaypro Corporation", "Kaypro 4/84 plus88", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) // model 81-015 with an added 8088 daughterboard and rom
COMP( 1984, kaypro1084,   kaypro10,  0,      kaypro1084, kaypro, kaypro84_state, init_kaypro, "Kaypro Corporation", "Kaypro 10", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) // model 81-005
COMP( 1984, robie,        0,         0,      kaypro4x,   kaypro, kaypro84_state, init_kaypro, "Kaypro Corporation", "Kaypro Robie", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1985, kaypro2x,     kaypro484, 0,      kaypro484,  kaypro, kaypro84_state, init_kaypro, "Kaypro Corporation", "Kaypro 2x", MACHINE_SUPPORTS_SAVE ) // model 81-025
COMP( 1985, kaypronew2,   0,         0,      kaypronew2, kaypro, kaypro84_state, init_kaypro, "Kaypro Corporation", "Kaypro New 2", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1985, kaypro4x,     robie,     0,      kaypro4x,   kaypro, kaypro84_state, init_kaypro, "Kaypro Corporation", "Kaypro 4x", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1986, kaypro1,      kaypro484, 0,      kaypro1,    kaypro, kaypro84_state, init_kaypro, "Kaypro Corporation", "Kaypro 1", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 198?, omni2,        kayproii,  0,      omni2,      kaypro, kayproii_state, init_kaypro, "Kaypro Corporation", "Omni II Logic Analyzer", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 198?, omni4,        kaypro484, 0,      kaypro1,    kaypro, kaypro84_state, init_kaypro, "Omni Logic Inc.",    "Omni 4 Logic Analyzer", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



kc.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Kevin Thacker,Sandro Ronco
/******************************************************************************

    kc.cpp
    system driver

    A big thankyou to Torsten Paul for his great help with this
    driver!


    Kevin Thacker [MESS driver]

******************************************************************************/

#include "emu.h"
#include "kc.h"

#include "machine/input_merger.h"
#include "softlist_dev.h"

#include "screen.h"
#include "speaker.h"


void kc85_4_state::kc85_4_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(kc85_4_state::expansion_io_read), FUNC(kc85_4_state::expansion_io_write));

	map(0x0084, 0x0085).mirror(0xff00).rw(FUNC(kc85_4_state::kc85_4_84_r), FUNC(kc85_4_state::kc85_4_84_w));
	map(0x0086, 0x0087).mirror(0xff00).rw(FUNC(kc85_4_state::kc85_4_86_r), FUNC(kc85_4_state::kc85_4_86_w));
	map(0x0088, 0x008b).mirror(0xff00).rw(m_z80pio, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x008c, 0x008f).mirror(0xff00).rw(m_z80ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
}

void kc85_4_state::kc85_4_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).bankrw("bank1");
	map(0x4000, 0x7fff).bankrw("bank2");
	map(0x8000, 0xa7ff).bankrw("bank3");
	map(0xa800, 0xbfff).bankrw("bank6");
	map(0xc000, 0xdfff).bankr("bank4");
	map(0xe000, 0xffff).bankr("bank5");
}

void kc_state::kc85_2_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).bankrw("bank1");
	map(0x4000, 0x7fff).bankrw("bank2");
	map(0x8000, 0xbfff).bankrw("bank3");
	map(0xc000, 0xdfff).bankr("bank4");
	map(0xe000, 0xffff).bankr("bank5");
}

void kc_state::kc85_2_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(kc_state::expansion_io_read), FUNC(kc_state::expansion_io_write));

	map(0x0088, 0x008b).mirror(0xff00).rw(m_z80pio, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x008c, 0x008f).mirror(0xff00).rw(m_z80ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
}

static INPUT_PORTS_START( kc85 )
INPUT_PORTS_END


/* priority derived from schematics */
static const z80_daisy_config kc85_daisy_chain[] =
{
	{ "z80ctc" },
	{ "z80pio" },
	{ nullptr }
};

static const double kc85_speaker_levels[64] = {
	-1.0, -0.96875, -0.9375, -0.90625, -0.875, -0.84375, -0.8125, -0.78125,
	-0.75, -0.71875, -0.6875, -0.65625, -0.625, -0.59375, -0.5625, -0.53125,
	-0.5, -0.46875, -0.4375, -0.40625, -0.375, -0.34375, -0.3125, -0.28125,
	-0.25, -0.21875, -0.1875, -0.15625, -0.125, -0.09375, -0.0625, -0.03125,
	0.0, 0.03125, 0.0625, 0.09375, 0.125, 0.15625, 0.1875, 0.21875,
	0.25, 0.28125, 0.3125, 0.34375, 0.375, 0.40625, 0.4375, 0.46875,
	0.5, 0.53125, 0.5625, 0.59375, 0.625, 0.65625, 0.6875, 0.71875,
	0.75, 0.78125, 0.8125, 0.84375, 0.875, 0.90625, 0.9375, 0.96875
};

static const double kc85_4_speaker_levels[32] = {
	-1.0, -0.9375, -0.875, -0.8125, -0.75, -0.6875, -0.625, -0.5625,
	-0.5, -0.4375, -0.375, -0.3125, -0.25, -0.1875, -0.125, -0.0625,
	0.0, 0.0625, 0.125, 0.1875, 0.25, 0.3125, 0.375, 0.4375,
	0.5, 0.5625, 0.625, 0.6875, 0.75, 0.8125, 0.875, 0.9375
};

void kc85_cart(device_slot_interface &device)
{
	device.option_add("standard", KC_STANDARD); // standard 8KB ROM module
	device.option_add("m006", KC_M006);         // BASIC
	device.option_add("m011", KC_M011);         // 64KB RAM
	device.option_add("m022", KC_M022);         // 16KB RAM
	device.option_add("m032", KC_M032);         // 256KB segmented RAM
	device.option_add("m033", KC_M033);         // TypeStar
	device.option_add("m034", KC_M034);         // 512KB segmented RAM
	device.option_add("m035", KC_M035);         // 1MB segmented RAM
	device.option_add("m036", KC_M036);         // 128KB segmented RAM
}

void kc85_exp(device_slot_interface &device)
{
	device.option_add("d002", KC_D002);         // D002 Bus Driver
	device.option_add("d004", KC_D004);         // D004 Floppy Disk Interface
	device.option_add("d004gide", KC_D004_GIDE); // D004 Floppy Disk + GIDE Interface
}


void kc_state::kc85_slots(machine_config &config)
{
	/* devices */
	QUICKLOAD(config, "quickload", "kcc", attotime::from_seconds(2)).set_load_callback(FUNC(kc_state::quickload_cb));

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(kc_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("kc_cass");

	INPUT_MERGER_ANY_HIGH(config, "irq").output_handler().set_inputline(m_maincpu, 0);
	INPUT_MERGER_ANY_HIGH(config, "nmi").output_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);
	INPUT_MERGER_ANY_HIGH(config, "halt").output_handler().set_inputline(m_maincpu, INPUT_LINE_HALT);

	/* cartridge slot */
	KCCART_SLOT(config, m_expansions[0], kc85_cart, "m011");
	m_expansions[0]->set_next_slot(m_expansions[1]);
	m_expansions[0]->irq().set("irq", FUNC(input_merger_device::in_w<0>));
	m_expansions[0]->nmi().set("nmi", FUNC(input_merger_device::in_w<0>));
	m_expansions[0]->halt().set("halt", FUNC(input_merger_device::in_w<0>));

	KCCART_SLOT(config, m_expansions[1], kc85_cart, nullptr);
	m_expansions[1]->set_next_slot(m_expansions[2]);
	m_expansions[1]->irq().set("irq", FUNC(input_merger_device::in_w<1>));
	m_expansions[1]->nmi().set("nmi", FUNC(input_merger_device::in_w<1>));
	m_expansions[1]->halt().set("halt", FUNC(input_merger_device::in_w<1>));

	/* expansion interface */
	KCEXP_SLOT(config, m_expansions[2], kc85_exp, nullptr);
	m_expansions[2]->irq().set("irq", FUNC(input_merger_device::in_w<2>));
	m_expansions[2]->nmi().set("nmi", FUNC(input_merger_device::in_w<2>));
	m_expansions[2]->halt().set("halt", FUNC(input_merger_device::in_w<2>));

	/* Software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("kc_cart");
	SOFTWARE_LIST(config, "flop_list").set_original("kc_flop");
	SOFTWARE_LIST(config, "cass_list").set_original("kc_cass");
}

void kc_state::kc85_base(machine_config &config, uint32_t clock)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, clock);
	m_maincpu->set_daisy_config(kc85_daisy_chain);

	config.set_maximum_quantum(attotime::from_hz(60));

	Z80PIO(config, m_z80pio, clock);
	m_z80pio->out_int_callback().set_inputline(m_maincpu, 0);
	m_z80pio->in_pa_callback().set(FUNC(kc_state::pio_porta_r));
	m_z80pio->out_pa_callback().set(FUNC(kc_state::pio_porta_w));
	m_z80pio->out_ardy_callback().set(FUNC(kc_state::pio_ardy_cb));
	m_z80pio->in_pb_callback().set(FUNC(kc_state::pio_portb_r));
	m_z80pio->out_brdy_callback().set(FUNC(kc_state::pio_brdy_cb));

	Z80CTC(config, m_z80ctc, clock);
	m_z80ctc->intr_callback().set_inputline(m_maincpu, 0);
	m_z80ctc->zc_callback<1>().set(FUNC(kc_state::ctc_zc1_callback));
	m_z80ctc->zc_callback<2>().set(FUNC(kc_state::video_toggle_blink_state));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(XTAL(28'375'160)/2, 908, 0, 320, 312, 0, 256);
	m_screen->set_palette("palette");
	TIMER(config, "scantimer").configure_scanline(FUNC(kc_state::kc_scanline), "screen", 0, 1);

	PALETTE(config, "palette", FUNC(kc_state::kc85_palette), KC85_PALETTE_SIZE);

	kc_keyboard_device &keyboard(KC_KEYBOARD(config, "keyboard", XTAL(4'000'000)));
	keyboard.out_wr_callback().set(FUNC(kc_state::keyboard_cb));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER(config, "outl").front_left();
	SPEAKER(config, "outr").front_right();
	SPEAKER_SOUND(config, m_dac);
	m_dac->add_route(ALL_OUTPUTS, "mono", 0.5);
	SPEAKER_SOUND(config, m_tapeout_left).add_route(ALL_OUTPUTS, "outl", 0.25);
	SPEAKER_SOUND(config, m_tapeout_right).add_route(ALL_OUTPUTS, "outr", 0.25);

	kc85_slots(config);
}

void kc_state::kc85_2_3(machine_config &config, uint32_t clock)
{
	kc85_base(config, KC85_2_CLOCK);

	m_maincpu->set_addrmap(AS_PROGRAM, &kc_state::kc85_2_mem);
	m_maincpu->set_addrmap(AS_IO, &kc_state::kc85_2_io);

	m_screen->set_screen_update(FUNC(kc_state::screen_update));

	m_dac->set_levels(64, kc85_speaker_levels);

	RAM(config, m_ram).set_default_size("16K");
}

void kc_state::kc85_2(machine_config &config)
{
	kc85_2_3(config, KC85_2_CLOCK);

	m_z80pio->out_pb_callback().set(FUNC(kc_state::pio_portb_w));
	m_z80ctc->zc_callback<0>().set(FUNC(kc_state::ctc_zc0_callback));
}

void kc85_3_state::kc85_3(machine_config &config)
{
	kc85_2_3(config, KC85_2_CLOCK);

	m_z80pio->out_pb_callback().set(FUNC(kc85_3_state::pio_portb_w));
	m_z80ctc->zc_callback<0>().set(FUNC(kc85_3_state::ctc_zc0_callback));

	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.125);
}

void kc85_4_state::kc85_4(machine_config &config)
{
	kc85_base(config, KC85_4_CLOCK);

	m_maincpu->set_addrmap(AS_PROGRAM, &kc85_4_state::kc85_4_mem);
	m_maincpu->set_addrmap(AS_IO, &kc85_4_state::kc85_4_io);

	m_z80pio->out_pb_callback().set(FUNC(kc85_4_state::pio_portb_w));
	m_z80ctc->zc_callback<0>().set(FUNC(kc85_4_state::ctc_zc0_callback));

	m_screen->set_screen_update(FUNC(kc85_4_state::screen_update));

	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.125);
	m_dac->set_levels(32, kc85_4_speaker_levels);

	RAM(config, m_ram).set_default_size("64K");
}

void kc85_4_state::kc85_5(machine_config &config)
{
	kc85_4(config);

	m_ram->set_default_size("256K");
}


ROM_START(kc85_2)
	ROM_REGION(0x4000, "caos", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "hc900", "HC900 CAOS" )
	ROMX_LOAD("hc900.852",    0x2000, 0x2000, CRC(e6f4c0ab) SHA1(242a777788c774c5f764313361b1e0a65139ab32), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "caos22", "CAOS 2.2" )
	ROMX_LOAD("caos__e0.852", 0x2000, 0x2000, CRC(48d5624c) SHA1(568dd59bfad4c604ba36bc05b094fc598a642f85), ROM_BIOS(1))
ROM_END

ROM_START(kc85_3)
	ROM_REGION(0x2000, "basic", 0)
	ROM_LOAD( "basic_c0.853", 0x0000, 0x2000, CRC(dfe34b08) SHA1(c2e3af55c79e049e811607364f88c703b0285e2e))

	ROM_REGION(0x4000, "caos", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "caos31", "CAOS 3.1" )
	ROMX_LOAD("caos__e0.853", 0x2000, 0x2000, CRC(639e4864) SHA1(efd002fc9146116936e6e6be0366d2afca33c1ab), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "caos33", "CAOS 3.3" )
	ROMX_LOAD("caos33.853",   0x2000, 0x2000, CRC(ca0fecad) SHA1(20447d27c9aa41a1c7a3d6ad0699edb06a207aa6), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "caos34", "CAOS 3.4" )
	ROMX_LOAD("caos34.853",   0x2000, 0x2000, CRC(d0245a3e) SHA1(ee9f8e7427b9225ae2cecbcfb625d629ab6a601d), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "pi88ge", "OS PI/88 (yellow/blue)" )
	ROMX_LOAD("pi88_ge.853",  0x2000, 0x2000, CRC(4bf0cfde) SHA1(b8373a44e4553197e3dd23008168d5214b878837), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "pi88sw", "OS PI/88 (black/white)" )
	ROMX_LOAD("pi88_sw.853",  0x2000, 0x2000, CRC(f7d2e8fc) SHA1(9b5c068f10ff34bc3253f5b51abad51c8da9dd5d), ROM_BIOS(4))
	ROM_SYSTEM_BIOS(5, "pi88ws", "OS PI/88 (white/blue)" )
	ROMX_LOAD("pi88_ws.853",  0x2000, 0x2000, CRC(9ef4efbf) SHA1(b8b6f606b76bce9fb7fcd61a14120e5e026b6b6e), ROM_BIOS(5))
ROM_END

ROM_START(kc85_4)
	ROM_REGION(0x2000, "basic", 0)
	ROM_LOAD("basic_c0.854", 0x0000, 0x2000, CRC(dfe34b08) SHA1(c2e3af55c79e049e811607364f88c703b0285e2e))
	ROM_REGION(0x4000, "caos", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "caos42", "CAOS 4.2" )
	ROMX_LOAD("caos__c0.854", 0x0000, 0x1000, CRC(57d9ab02) SHA1(774fc2496a59b77c7c392eb5aa46420e7722797e), ROM_BIOS(0))
	ROMX_LOAD("caos__e0.854", 0x2000, 0x2000, CRC(ee273933) SHA1(4300f7ff813c1fb2d5c928dbbf1c9e1fe52a9577), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "caos41", "CAOS 4.1" )
	ROMX_LOAD("caos41c.854", 0x0000, 0x1000, CRC(c7e1c011) SHA1(acd998e3d9e8f592cd884aafc8ac4d291e40e097), ROM_BIOS(1))
	ROMX_LOAD("caos41e.854", 0x2000, 0x2000, CRC(60e045e5) SHA1(e19819fb477dcb742a13729a9bf5943d63abe863), ROM_BIOS(1))
ROM_END

ROM_START(kc85_5)
	ROM_REGION(0x8000, "basic", 0)
	ROM_LOAD("basic_c0.855", 0x0000, 0x8000, CRC(0ed9f8b0) SHA1(be2c68a5b461014c57e33a127c3ffb32b0ff2346))

	ROM_REGION(0x4000, "caos", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "caos44", "CAOS 4.4" )
	ROMX_LOAD("caos__c0.855",0x0000, 0x2000, CRC(f56d5c18) SHA1(2cf8023ee71ca50b92f9f151b7519f59727d1c79), ROM_BIOS(0))
	ROMX_LOAD("caos__e0.855",0x2000, 0x2000, CRC(1dbc2e6d) SHA1(53ba4394d96e287ff8af01322af1e9879d4e77c4), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "caos43", "CAOS 4.3" )
	ROMX_LOAD("caos43c.855", 0x0000, 0x2000, CRC(2f0f9eaa) SHA1(5342be5104206d15e7471b094c7749a8a3d708ad), ROM_BIOS(1))
	ROMX_LOAD("caos43e.855", 0x2000, 0x2000, CRC(b66fc6c3) SHA1(521ac2fbded4148220f8af2d5a5ab99634364079), ROM_BIOS(1))
ROM_END

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS         INIT        COMPANY                                               FULLNAME           FLAGS
COMP( 1987, kc85_2, 0,      0,      kc85_2,  kc85,  kc_state,     empty_init, u8"VEB Mikroelektronik \"Wilhelm Pieck\" Mühlhausen", "HC900 / KC 85/2", MACHINE_NOT_WORKING)
COMP( 1987, kc85_3, kc85_2, 0,      kc85_3,  kc85,  kc85_3_state, empty_init, u8"VEB Mikroelektronik \"Wilhelm Pieck\" Mühlhausen", "KC 85/3",         MACHINE_NOT_WORKING)
COMP( 1989, kc85_4, kc85_2, 0,      kc85_4,  kc85,  kc85_4_state, empty_init, u8"VEB Mikroelektronik \"Wilhelm Pieck\" Mühlhausen", "KC 85/4",         MACHINE_NOT_WORKING)
COMP( 1989, kc85_5, kc85_2, 0,      kc85_5,  kc85,  kc85_4_state, empty_init, u8"VEB Mikroelektronik \"Wilhelm Pieck\" Mühlhausen", "KC 85/5",         MACHINE_NOT_WORKING)



kdt6.cpp
<---------------------------------------------------------------------->
// license: GPL-2.0+
// copyright-holders: Dirk Best
/***************************************************************************

    Kontron KDT6

    This is the base board for various machines, it needs to be combined
    with an I/O board and a bus board. This gives us
    - KDT6 + 9xx/IOC + 9xx/BUS: PSI908/PSI9C
    - KDT6 + 98/IOC + 98/BUS: PSI98

***************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/timer.h"
#include "machine/z80ctc.h"
#include "machine/z80dma.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"
#include "machine/upd765.h"
#include "machine/upd1990a.h"
#include "machine/clock.h"
#include "video/mc6845.h"
#include "sound/beep.h"
#include "sound/spkrdev.h"
#include "bus/centronics/ctronics.h"
#include "bus/psi_kbd/psi_kbd.h"
#include "bus/rs232/rs232.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"
#include "softlist_dev.h"
#include "kdt6.lh"


namespace {

//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class kdt6_state : public driver_device
{
public:
	kdt6_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_cpu(*this, "maincpu"),
		m_dma(*this, "dma"),
		m_sio(*this, "sio"),
		m_crtc(*this, "crtc"),
		m_page_r(*this, "page%x_r", 0), m_page_w(*this, "page%x_w", 0),
		m_boot(*this, "boot"),
		m_palette(*this, "palette"),
		m_gfx(*this, "gfx"),
		m_rtc(*this, "rtc"),
		m_fdc(*this, "fdc"),
		m_floppy(*this, "fdc:%u", 0U),
		m_beeper(*this, "beeper"),
		m_beep_timer(*this, "beep_timer"),
		m_centronics(*this, "centronics"),
		m_dip_s2(*this, "S2"),
		m_keyboard(*this, "kbd"),
		m_rs232b(*this, "rs232b"),
		m_drive_led(*this, "drive%u_led", 0U),
		m_rom_view(*this, "rom_view"),
		m_sasi_dma(false),
		m_dma_map(0),
		m_status0(0), m_status1(0), m_status2(0),
		m_video_address(0)
	{ }

	void psi98(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	uint8_t memory_r(offs_t offset);
	void memory_w(offs_t offset, uint8_t data);
	uint8_t io_r(offs_t offset);
	void io_w(offs_t offset, uint8_t data);
	uint8_t mapper_r(offs_t offset);
	void mapper_w(offs_t offset, uint8_t data);
	uint8_t sasi_ctrl_r();
	void sasi_ctrl_w(uint8_t data);
	void dma_map_w(uint8_t data);
	void status0_w(uint8_t data);
	uint8_t status1_r();
	void status1_w(uint8_t data);
	void status2_w(uint8_t data);

	uint8_t video_data_r();
	void video_data_w(uint8_t data);
	void video_data_inc_w(uint8_t data);
	void video_data_dec_w(uint8_t data);
	void video_address_latch_high_w(uint8_t data);
	void video_address_latch_low_w(uint8_t data);

	TIMER_DEVICE_CALLBACK_MEMBER(beeper_off);

	void fdc_tc_w(uint8_t data);
	void fdc_drq_w(int state);
	void drive0_led_cb(floppy_image_device *floppy, int state);
	void drive1_led_cb(floppy_image_device *floppy, int state);

	MC6845_UPDATE_ROW(crtc_update_row);
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void pio_porta_w(uint8_t data);
	void keyboard_rx_w(int state);
	void rs232b_rx_w(int state);
	void siob_tx_w(int state);

	void psi98_io(address_map &map) ATTR_COLD;
	void psi98_mem(address_map &map) ATTR_COLD;

	required_device<z80_device> m_cpu;
	required_device<z80dma_device> m_dma;
	required_device<z80sio_device> m_sio;
	required_device<mc6845_device> m_crtc;
	required_memory_bank_array<16> m_page_r, m_page_w;
	required_memory_region m_boot;
	required_device<palette_device> m_palette;
	required_region_ptr<u8> m_gfx;
	required_device<upd1990a_device> m_rtc;
	required_device<upd765a_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<beep_device> m_beeper;
	required_device<timer_device> m_beep_timer;
	required_device<centronics_device> m_centronics;
	required_ioport m_dip_s2;
	required_device<psi_keyboard_bus_device> m_keyboard;
	required_device<rs232_port_device> m_rs232b;
	output_finder<2> m_drive_led;

	memory_view m_rom_view;

	std::unique_ptr<uint8_t[]> m_ram;
	std::unique_ptr<uint16_t[]> m_vram; // 10-bit
	std::unique_ptr<uint8_t[]> m_dummy_r, m_dummy_w;

	bool m_sasi_dma;
	uint8_t m_dma_map;
	uint8_t m_status0;
	uint8_t m_status1;
	uint8_t m_status2;
	uint16_t m_mapper[16]; // 12-bit
	uint16_t m_video_address;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void kdt6_state::psi98_mem(address_map &map)
{
	for (int i = 0; i < 16; i++)
	{
		map((i << 12) + 0x0000, (i << 12) + 0x0fff).bankr(m_page_r[i]).bankw(m_page_w[i]);
	}
	// override the region 0x0000 to 0x1fff here to enable prom reading
	map(0x0000, 0x1fff).view(m_rom_view);
	m_rom_view[0](0x0000, 0x1fff).rom().region("boot", 0);
}

void kdt6_state::psi98_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x00).rw(m_dma, FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	map(0x04, 0x07).rw(m_sio, FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
	map(0x08, 0x0b).rw("ctc1", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x0c, 0x0f).rw("pio", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x10, 0x13).rw("ctc2", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x14, 0x14).r(m_fdc, FUNC(upd765a_device::msr_r));
	map(0x15, 0x15).rw(m_fdc, FUNC(upd765a_device::fifo_r), FUNC(upd765a_device::fifo_w));
	map(0x18, 0x18).w(m_crtc, FUNC(mc6845_device::address_w));
	map(0x19, 0x19).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x1c, 0x1c).w(FUNC(kdt6_state::status0_w));
	map(0x1d, 0x1d).r(m_keyboard, FUNC(psi_keyboard_bus_device::key_data_r));
	map(0x1e, 0x1e).rw(m_fdc, FUNC(upd765a_device::dma_r), FUNC(upd765a_device::dma_w));
	map(0x1f, 0x1f).w(FUNC(kdt6_state::fdc_tc_w));
	map(0x20, 0x2f).rw(FUNC(kdt6_state::mapper_r), FUNC(kdt6_state::mapper_w));
	map(0x30, 0x30).rw(FUNC(kdt6_state::video_data_r), FUNC(kdt6_state::video_data_w));
	map(0x31, 0x31).w(FUNC(kdt6_state::video_data_inc_w));
	map(0x36, 0x36).w(FUNC(kdt6_state::video_data_dec_w));
	map(0x37, 0x37).w(FUNC(kdt6_state::video_data_inc_w));
	map(0x38, 0x38).w(FUNC(kdt6_state::status1_w));
	map(0x39, 0x39).r(FUNC(kdt6_state::status1_r));
	map(0x3a, 0x3a).w(FUNC(kdt6_state::status2_w));
	map(0x3b, 0x3b).rw(FUNC(kdt6_state::sasi_ctrl_r), FUNC(kdt6_state::sasi_ctrl_w));
	map(0x3c, 0x3c).w(FUNC(kdt6_state::dma_map_w));
#if 0
	map(0x3d, 0x3d) WATCHDOG
	map(0x3e, 0x3e) WATCHDOG TRIGGER
	map(0x3f, 0x3f) SASI DATA
#endif
	map(0x40, 0x40).w(FUNC(kdt6_state::video_address_latch_high_w));
	map(0x41, 0x41).w(FUNC(kdt6_state::video_address_latch_low_w));
}


//**************************************************************************
//  INPUTS
//**************************************************************************

INPUT_PORTS_START( psi98 )
	PORT_START("S2")
	PORT_DIPNAME(0x01, 0x01, "SIO B") PORT_DIPLOCATION("S2:1")
	PORT_DIPSETTING(0x00, "RS232")
	PORT_DIPSETTING(0x01, "Keyboard")
INPUT_PORTS_END


//**************************************************************************
//  FLOPPY
//**************************************************************************

static void kdt6_floppies(device_slot_interface &device)
{
	device.option_add("fd55f", TEAC_FD_55F);
}

void kdt6_state::fdc_tc_w(uint8_t data)
{
	m_fdc->tc_w(1);
	m_fdc->tc_w(0);
}

void kdt6_state::fdc_drq_w(int state)
{
	if (!m_sasi_dma && BIT(m_status0, 4) == 0)
		m_dma->rdy_w(state);
}

void kdt6_state::drive0_led_cb(floppy_image_device *floppy, int state)
{
	m_drive_led[0] = state;
}

void kdt6_state::drive1_led_cb(floppy_image_device *floppy, int state)
{
	m_drive_led[1] = state;
}


//**************************************************************************
//  VIDEO
//**************************************************************************

void kdt6_state::video_address_latch_high_w(uint8_t data)
{
	m_video_address &= 0x00ff;
	m_video_address |= (data << 8);
}

void kdt6_state::video_address_latch_low_w(uint8_t data)
{
	m_video_address &= 0xff00;
	m_video_address |= (data << 0);
}

uint8_t kdt6_state::video_data_r()
{
	return m_vram[m_video_address];
}

void kdt6_state::video_data_w(uint8_t data)
{
	m_vram[m_video_address] = ((m_status1 & 0x0c) << 6) | data;
}

void kdt6_state::video_data_inc_w(uint8_t data)
{
	m_vram[m_video_address++] = ((m_status1 & 0x0c) << 6) | data;
}

void kdt6_state::video_data_dec_w(uint8_t data)
{
	m_vram[m_video_address--] = ((m_status1 & 0x0c) << 6) | data;
}

uint32_t kdt6_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	m_crtc->screen_update(screen, bitmap, cliprect);
	return 0;
}

MC6845_UPDATE_ROW( kdt6_state::crtc_update_row )
{
	pen_t const *const pen = m_palette->pens();

	for (int i = 0; i < x_count; i++)
	{
		if (BIT(m_status1, 6))
		{
			// text mode
			uint16_t const code = m_vram[((m_status1 & 0x03) << 14) | (ma + i)];
			uint8_t const data = m_gfx[((code & 0xff) << 4) | ra];

			int const inverse = BIT(code, 8) | BIT(m_status1, 5) | ((i == cursor_x) ? 1 : 0);
			int const blink = BIT(code, 9) & m_crtc->cursor_r();

			// draw 8 pixels of the character
			for (int x = 0; x < 8; x++)
			{
				int const color = BIT(data, 7 - x);
				bitmap.pix(y, x + i*8) = pen[blink ? 0 : (color ^ inverse)];
			}
		}
		else
		{
			// gfx mode
			uint8_t const data = m_vram[(ma << 4) | (ra << 6) | i];

			// draw 8 pixels of the cell
			for (int x = 0; x < 8; x++)
				bitmap.pix(y, x + i*8) = pen[BIT(data, 7 - x)];
		}
	}
}


//**************************************************************************
//  SOUND
//**************************************************************************

TIMER_DEVICE_CALLBACK_MEMBER( kdt6_state::beeper_off )
{
	m_beeper->set_state(0);
}


//**************************************************************************
//  EXTERNAL I/O
//**************************************************************************

void kdt6_state::pio_porta_w(uint8_t data)
{
	m_centronics->write_strobe(BIT(data, 0));
	m_centronics->write_init(BIT(data, 1));
}

void kdt6_state::keyboard_rx_w(int state)
{
	if (machine().phase() >= machine_phase::RESET)
	{
		if ((m_dip_s2->read() & 0x01) == 0x01)
			m_sio->rxb_w(state);
	}
}

void kdt6_state::rs232b_rx_w(int state)
{
	if (machine().phase() >= machine_phase::RESET)
	{
		if ((m_dip_s2->read() & 0x01) == 0x00)
			m_sio->rxb_w(state);
	}
}

void kdt6_state::siob_tx_w(int state)
{
	if (machine().phase() >= machine_phase::RESET)
	{
		if ((m_dip_s2->read() & 0x01) == 0x01)
			m_keyboard->tx_w(state);
		else
			m_rs232b->write_txd(state);
	}
}


//**************************************************************************
//  MACHINE
//**************************************************************************

uint8_t kdt6_state::memory_r(offs_t offset)
{
	return m_ram[m_dma_map << 16 | offset];
}

void kdt6_state::memory_w(offs_t offset, uint8_t data)
{
	m_ram[m_dma_map << 16 | offset] = data;
}

uint8_t kdt6_state::io_r(offs_t offset)
{
	return m_cpu->space(AS_IO).read_byte(offset);
}

void kdt6_state::io_w(offs_t offset, uint8_t data)
{
	m_cpu->space(AS_IO).write_byte(offset, data);
}

uint8_t kdt6_state::sasi_ctrl_r()
{
	uint8_t data = 0;

	// 7-------  sasi select
	// -6------  sasi reset
	// --5-----  sasi input/output
	// ---4----  sasi control/data
	// ----3---  sasi message
	// -----2--  sasi request
	// ------1-  sasi busy
	// -------0  data output upd1990

	data |= m_rtc->data_out_r() << 0;
	data |= 0xfe;

	return data;
}

void kdt6_state::sasi_ctrl_w(uint8_t data)
{
	logerror("sasi_ctrl_w: %02x\n", data);
	m_sasi_dma = bool(BIT(data, 2) == 0);
}

void kdt6_state::dma_map_w(uint8_t data)
{
	m_dma_map = data;
}

uint8_t kdt6_state::mapper_r(offs_t offset)
{
	return m_mapper[offset];
}

void kdt6_state::mapper_w(offs_t offset, uint8_t data)
{
	m_mapper[offset] = ((m_status2 & 0x0f) << 8) | data;

	if (BIT(m_status1, 7) == 0)
	{
		offs_t const addr = bitswap(m_mapper[offset], 8, 9, 10, 11, 7, 6, 5, 4, 0, 1, 2, 3) << 12;

		m_page_r[offset]->set_base(addr < 0x40000 ? &m_ram[addr] : &m_dummy_r[0]);
		m_page_w[offset]->set_base(addr < 0x40000 ? &m_ram[addr] : &m_dummy_w[0]);

		if (0)
			logerror("map_page: %x -> %06x\n", offset, addr);
	}
}

void kdt6_state::status0_w(uint8_t data)
{
	logerror("status0_w: %02x\n", data);

	// 7-------  floppy motor
	// -6------  floppy drive type
	// --5-----  prom off
	// ---4----  dma trigger (floppy/sio)
	// ----3---  character generator a12
	// -----2--  sound
	// ------1-  system frequency (0 = half)
	// -------0  watchdog enable

	m_cpu->set_unscaled_clock((XTAL(16'000'000) / 4) * (BIT(data, 1) ? 1 : 0.5));

	if ((BIT(m_status0, 2) ^ BIT(data, 2)) && BIT(data, 2))
	{
		m_beeper->set_state(1);
		m_beep_timer->adjust(attotime::from_msec(250)); // timing unknown
	}

	if (BIT(data, 5))
		m_rom_view.disable();
	else
		m_rom_view.select(0);

	for (auto &floppy : m_floppy)
		if (floppy->get_device())
			floppy->get_device()->mon_w(BIT(data, 7) ? 0 : 1);

	m_status0 = data;
}

uint8_t kdt6_state::status1_r()
{
	// 7-------  disable memory mapper (1 = disable)
	// -6------  display mode (1 = text, 0 = gfx)
	// --5-----  video invert
	// ---4----  select 3 video memory bank
	// ----3---  video bit 9
	// -----2--  video bit 8
	// ------1-  video scroll address bit 15
	// -------0  video scroll address bit 14

	return m_status1;
}

void kdt6_state::status1_w(uint8_t data)
{
	logerror("status1_w: %02x\n", data);

	// memory mapper disabled?
	if (BIT(data, 7))
	{
		for (unsigned i = 0; i < 16; i++)
		{
			m_page_r[i]->set_base(&m_ram[i * 0x1000]);
			m_page_w[i]->set_base(&m_ram[i * 0x1000]);
		}
	}

	// note that enabling the memory mapper doesn't mean that the pages are automatically mapped in
	// it still needs a write to the mapper register to activate

	m_status1 = data;
}

void kdt6_state::status2_w(uint8_t data)
{
	if (0)
		logerror("status2_w: %02x\n", data);

	// 7-------  rtc chip select
	// -6------  rtc output enable
	// --5-----  rtc strobe
	// ---4----  rtc clock
	// ----321-  rtc control 2-0
	// -------0  rtc data
	// ----3210  memory mapper bit 0-3

	m_rtc->cs_w(BIT(data, 7));
	m_rtc->oe_w(BIT(data, 6));
	m_rtc->stb_w(BIT(data, 5));
	m_rtc->clk_w(BIT(data, 4));
	m_rtc->c2_w(BIT(data, 3));
	m_rtc->c1_w(BIT(data, 2));
	m_rtc->c0_w(BIT(data, 1));
	m_rtc->data_in_w(BIT(data, 0));

	m_status2 = data & 0x0f;
}

void kdt6_state::machine_start()
{
	m_drive_led.resolve();

	// 256 kb ram, 64 kb vram (and two dummy regions for invalid pages)
	m_ram = std::make_unique<uint8_t[]>(0x40000);
	m_vram = std::make_unique<uint16_t[]>(0x10000);
	m_dummy_r = std::make_unique<uint8_t[]>(0x1000);
	m_dummy_w = std::make_unique<uint8_t[]>(0x1000);

	m_fdc->set_rate(250000);

	if (m_floppy[0]->get_device())
		m_floppy[0]->get_device()->setup_led_cb(floppy_image_device::led_cb(&kdt6_state::drive0_led_cb, this));
	if (m_floppy[1]->get_device())
		m_floppy[1]->get_device()->setup_led_cb(floppy_image_device::led_cb(&kdt6_state::drive1_led_cb, this));

	// register for save states
	save_pointer(NAME(m_ram), 0x40000);
	save_pointer(NAME(m_vram), 0x10000);
	save_item(NAME(m_sasi_dma));
	save_item(NAME(m_dma_map));
	save_item(NAME(m_status0));
	save_item(NAME(m_status1));
	save_item(NAME(m_status2));
	save_item(NAME(m_mapper));
	save_item(NAME(m_video_address));
}

void kdt6_state::machine_reset()
{
	// status0 is forced to 0 on reset
	status0_w(0);
}


//**************************************************************************
//  MACHINE DEFINITIONS
//**************************************************************************

static const z80_daisy_config daisy_chain_intf[] =
{
	{ "dma" },
	{ "ctc1" },
	{ "sio" },
	{ "ctc2" },
	{ "pio" },
	{ nullptr }
};

void kdt6_state::psi98(machine_config &config)
{
	Z80(config, m_cpu, XTAL(16'000'000) / 4);
	m_cpu->set_addrmap(AS_PROGRAM, &kdt6_state::psi98_mem);
	m_cpu->set_addrmap(AS_IO, &kdt6_state::psi98_io);
	m_cpu->set_daisy_config(daisy_chain_intf);
	m_cpu->busack_cb().set(m_dma, FUNC(z80dma_device::bai_w));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_raw(XTAL(13'516'800), 824, 48, 688, 274, 0, 250);
	screen.set_screen_update(FUNC(kdt6_state::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME);
	config.set_default_layout(layout_kdt6);

	MC6845(config, m_crtc, XTAL(13'516'800) / 8);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(kdt6_state::crtc_update_row));
	m_crtc->out_vsync_callback().set("ctc2", FUNC(z80ctc_device::trg2));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 1000); // frequency unknown
	m_beeper->add_route(ALL_OUTPUTS, "mono", 0.50);
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	TIMER(config, m_beep_timer).configure_generic(FUNC(kdt6_state::beeper_off));

	Z80DMA(config, m_dma, 16_MHz_XTAL / 4);
	m_dma->out_busreq_callback().set_inputline(m_cpu, Z80_INPUT_LINE_BUSRQ);
	m_dma->out_int_callback().set_inputline(m_cpu, INPUT_LINE_IRQ0);
	m_dma->in_mreq_callback().set(FUNC(kdt6_state::memory_r));
	m_dma->out_mreq_callback().set(FUNC(kdt6_state::memory_w));
	m_dma->in_iorq_callback().set(FUNC(kdt6_state::io_r));
	m_dma->out_iorq_callback().set(FUNC(kdt6_state::io_w));

	// jumper J3 allows selection of 16MHz / 8 instead
	clock_device &uart_clk(CLOCK(config, "uart_clk", XTAL(9'830'400) / 8));
	uart_clk.signal_handler().set("ctc1", FUNC(z80ctc_device::trg1));
	uart_clk.signal_handler().append("ctc1", FUNC(z80ctc_device::trg2));

	z80ctc_device &ctc1(Z80CTC(config, "ctc1", 16_MHz_XTAL / 4));
	ctc1.intr_callback().set_inputline(m_cpu, INPUT_LINE_IRQ0);
	ctc1.zc_callback<1>().set(m_sio, FUNC(z80sio_device::rxtxcb_w));
	ctc1.zc_callback<2>().set(m_sio, FUNC(z80sio_device::rxca_w));
	ctc1.zc_callback<2>().append(m_sio, FUNC(z80sio_device::txca_w));

	z80ctc_device &ctc2(Z80CTC(config, "ctc2", 16_MHz_XTAL / 4));
	ctc2.intr_callback().set_inputline(m_cpu, INPUT_LINE_IRQ0);
	ctc2.zc_callback<0>().set("speaker", FUNC(speaker_sound_device::level_w));
	ctc2.zc_callback<2>().set("ctc2", FUNC(z80ctc_device::trg3));

	Z80SIO(config, m_sio, 16_MHz_XTAL / 4);
	m_sio->out_int_callback().set_inputline(m_cpu, INPUT_LINE_IRQ0);
	m_sio->out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
	m_sio->out_dtra_callback().set("rs232a", FUNC(rs232_port_device::write_dtr));
	m_sio->out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts));
	m_sio->out_txdb_callback().set(FUNC(kdt6_state::siob_tx_w));
	m_sio->out_dtrb_callback().set(m_rs232b, FUNC(rs232_port_device::write_dtr));
	m_sio->out_rtsb_callback().set(m_rs232b, FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_sio, FUNC(z80sio_device::rxa_w));
	rs232a.dcd_handler().set(m_sio, FUNC(z80sio_device::dcda_w));
	rs232a.dsr_handler().set(m_sio, FUNC(z80sio_device::synca_w));
	rs232a.cts_handler().set(m_sio, FUNC(z80sio_device::ctsa_w)).invert();

	RS232_PORT(config, m_rs232b, default_rs232_devices, nullptr);
	m_rs232b->rxd_handler().set(FUNC(kdt6_state::rs232b_rx_w));
	m_rs232b->dcd_handler().set(m_sio, FUNC(z80sio_device::dcdb_w));
	m_rs232b->dsr_handler().set(m_sio, FUNC(z80sio_device::syncb_w));
	m_rs232b->cts_handler().set(m_sio, FUNC(z80sio_device::ctsb_w)).invert();

	z80pio_device &pio(Z80PIO(config, "pio", 16_MHz_XTAL / 4));
	pio.out_int_callback().set_inputline(m_cpu, INPUT_LINE_IRQ0);
	pio.out_pa_callback().set(FUNC(kdt6_state::pio_porta_w));
	pio.in_pb_callback().set("cent_data_in", FUNC(input_buffer_device::read));
	pio.out_pb_callback().set("cent_data_out", FUNC(output_latch_device::write));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->set_data_input_buffer("cent_data_in");
	m_centronics->fault_handler().set("pio", FUNC(z80pio_device::pa2_w));
	m_centronics->perror_handler().set("pio", FUNC(z80pio_device::pa3_w));
	m_centronics->busy_handler().set("pio", FUNC(z80pio_device::pa4_w));
	m_centronics->select_handler().set("pio", FUNC(z80pio_device::pa5_w));

	INPUT_BUFFER(config, "cent_data_in");
	output_latch_device &latch(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(latch);

	UPD1990A(config, m_rtc);

	UPD765A(config, m_fdc, 8'000'000, true, true);
	m_fdc->intrq_wr_callback().set("ctc1", FUNC(z80ctc_device::trg0));
	m_fdc->drq_wr_callback().set(FUNC(kdt6_state::fdc_drq_w));
	FLOPPY_CONNECTOR(config, "fdc:0", kdt6_floppies, "fd55f", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", kdt6_floppies, "fd55f", floppy_image_device::default_mfm_floppy_formats);

	SOFTWARE_LIST(config, "floppy_list").set_original("psi98");

	PSI_KEYBOARD_INTERFACE(config, m_keyboard, "hle");
	m_keyboard->rx().set(FUNC(kdt6_state::keyboard_rx_w));
	m_keyboard->key_strobe().set("ctc2", FUNC(z80ctc_device::trg1));

	// 6 ECB slots
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( psi98 )
	ROM_REGION(0x2000, "boot", 0)
	ROM_LOAD("td_62b60_1.prom1", 0x0000, 0x1000, CRC(dbc01eeb) SHA1(3ecab997623836599a5e0b2a05d0f743d93edf00)) // SUM16: 0a84 (file: 29ca)
	ROM_LOAD("boot_v61_a.prom2", 0x1000, 0x1000, CRC(6dd9a5e3) SHA1(b812947009d83e9c6119e731ba7d40fe3597d42c)) // SUM16: d4ec (file: b1fb)

	ROM_REGION(0x1000, "gfx", 0)
	ROM_LOAD("ch6_d-e.bin", 0x0000, 0x1000, CRC(e7bca335) SHA1(454ca3b8b8ac66464870e4bd5497050038d771c8))      // SUM16: 39b1 (file: 5f55)

	// PALs
	// 1-FF5B  12L6  memory address decoder
	// 2-0F61  12L8  i/o address decoder
	// 3-C7BF  10H8  interrupt priority controller
	// 4-EC5E  16L8  bus controller
	// 5-1126  16H2  fdc write precompensation
	// 6-1BA7  10L8  video memory access controller
	// 7-C1ED  16L8  video memory timing controller
	// 8-CD9F  12H6  fdc timing generator
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY    FULLNAME  FLAGS
COMP( 1984, psi98, 0,      0,      psi98,   psi98, kdt6_state, empty_init, "Kontron", "PSI98",  0 )



kidsupstar.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:


/*********************************************************************************

    Skeleton driver for VTech Kidi SuperStar LightShow (karaoke + games).
    GeneralPlus based. VTech PCB 35-178500-200-263.
    Video from the real hardware: https://youtu.be/ru6zKr2fbTk?si=1ZAAt9a1fapj23BM

    Two big globs on the PCB back side (U4 and U8).

*********************************************************************************/


#include "emu.h"

#include "cpu/arm7/arm7.h"

#include "screen.h"
#include "speaker.h"


namespace {


class kidsupstar_state : public driver_device
{
public:
	kidsupstar_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen")
	{ }

	void kidsupstar(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;

	uint32_t screen_update_kidsupstar(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};

uint32_t kidsupstar_state::screen_update_kidsupstar(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void kidsupstar_state::machine_start()
{
}

void kidsupstar_state::machine_reset()
{
}

static INPUT_PORTS_START( kidsupstar )
INPUT_PORTS_END

void kidsupstar_state::kidsupstar(machine_config &config)
{
	ARM9(config, m_maincpu, 240'000'000); // Unknown core and frequency

	SCREEN(config, m_screen, SCREEN_TYPE_LCD); // Monochrome 48x48 LCD screen
	m_screen->set_refresh_hz(60); // Guess
	m_screen->set_size(48, 48);
	m_screen->set_visarea(0, 48-1, 0, 48-1);
	m_screen->set_screen_update(FUNC(kidsupstar_state::screen_update_kidsupstar));

	SPEAKER(config, "mono").front_left();
}

// Spanish machine, may be different between regions.
ROM_START( kidsupstar )
	ROM_REGION( 0x010000, "maincpu", 0 )
	ROM_LOAD( "internal.u4",              0x000000, 0x010000, NO_DUMP ) // Unknown CPU type, unknown internal ROM size

	ROM_REGION( 0x400000, "program", 0 )
	ROM_LOAD( "winbond_w25q32fvssiq.ic7", 0x000000, 0x400000, CRC(9233f8b4) SHA1(eb5accb9c3f0a3fe0d0141a84d7e08fc356b4959) )

	ROM_REGION( 0x010000, "soundcpu", 0 )
	ROM_LOAD( "internal.u8",              0x000000, 0x010000, NO_DUMP ) // Unknown CPU type, unknown internal ROM size

	ROM_REGION( 0x400000, "music", 0 )
	ROM_LOAD( "zbit_25vq32.ic3",          0x000000, 0x400000, CRC(45a8d68c) SHA1(c00d17527b0e585c9358e873c3449b259814f025) )

	ROM_REGION( 0x400000, "user", 0 )
	ROM_LOAD( "zbit_25vq32.ic13",         0x000000, 0x400000, CRC(9233f8b4) SHA1(eb5accb9c3f0a3fe0d0141a84d7e08fc356b4959) ) // May contain user data, needs a factory reset
ROM_END

} // anonymous namespace


CONS( 2016, kidsupstar, 0, 0, kidsupstar, kidsupstar, kidsupstar_state, empty_init, "VTech", "Kidi SuperStar LightShow", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



kim1.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Juergen Buchmueller
/******************************************************************************

MOS Technology KIM-1

The cassette interface
======================
The KIM-1 stores data on cassette using 2 frequencies: ~3700Hz (high) and ~2400Hz
(low). A high tone is output for 9 cycles and a low tone for 6 cycles. A logic bit
is encoded using 3 sequences of high and low tones. It always starts with a high
tone and ends with a low tone. The middle tone is high for a logic 0 and low for
0 logic 1.

These high and low tone signals are fed to a circuit containing a LM565 PLL and
a 311 comparator. For a high tone a 1 is passed to DB7 of 6530-U2 for a low tone
a 0 is passed. The KIM-1 software measures the time it takes for the signal to
change from 1 to 0.

How to use cassette:
    00F1      00 to clear decimal mode
    17F5-17F6 start address low and high
    17F7-17F8 end address low and high
    17F9      2 digit program ID
    1800      press GO to save tape
    1873      press GO to load tape
NOTE: save end address is next address from program end

How to use the console:
Enable using Input Settings/Toggle Inputs TTY On.
Connect to the console at 2400 bps 8N2 (speeds from 110 to 9600 bps should work).
Hit <Delete> or <Return> on the console and it should display "KIM"
and accept monitor commands:

<hex address> <space>    Show data at <address>
<hex data> .             Write to current address
<Return>                 Advance to next address
<Line Feed>              Move to previous address
<Delete>                 Terminate memory edit
L                        Load program from paper tape
Q                        Save memory to paper tape (saves from current address to $17F7, $17F8)
G                        Go from current address

Keyboard and Display logic
==========================
PA0-PA6 of 6530-U2 are connected to the columns of the keyboard matrix. These
columns are also connected to segments A-G of the LEDs. PB1-PB3 of 6530-U2 are
connected to a 74145 BCD which connects outputs 0-2 to the rows of the keyboard
matrix. Outputs 4-9 of the 74145 are connected to LEDs U18-U23

When a key is pressed the corresponding input to PA0-PA6 is set low and the KIM-1
software reads this signal. The KIM-1 software sends an output signal to PA0-PA6
and the corresponding segments of an LED are illuminated.

LED: six 7-segment LEDs
    left 4 digits (address)
    right 2 digits (data)
Keyboard: 23 keys and SST switch
    0-F  16 keys to enter data
    AD   address entry mode
    DA   data entry mode
    +    increment address
    PC   recalls address stored in the Program Counter
    RS   system reset
    GO   execute program
    ST   program stop
    SST  single step slide switch

Paste test:
    R-0100=11^22^33^44^55^66^77^88^99^-0100=
    Press UP to verify data.


TODO:
- LEDs should be dark at startup (RS key to activate)

******************************************************************************/

#include "emu.h"

#include "bus/kim1/cards.h"
#include "bus/kim1/kim1bus.h"
#include "bus/rs232/rs232.h"
#include "cpu/m6502/m6502.h"
#include "imagedev/cassette.h"
#include "machine/mos6530.h"
#include "machine/timer.h"
#include "video/pwm.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "formats/kim1_cas.h"

#include "kim1.lh"


namespace {

//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class kim1_state : public driver_device
{
public:
	kim1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_miot(*this, "miot%u", 0)
		, m_digit_pwm(*this, "digit_pwm")
		, m_cass(*this, "cassette")
		, m_rs232(*this, "rs232")
		, m_row(*this, "ROW%u", 0U)
		, m_special(*this, "SPECIAL")
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(trigger_reset);
	DECLARE_INPUT_CHANGED_MEMBER(trigger_nmi);
	void kim1(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<m6502_device> m_maincpu;
	required_device_array<mos6530_device, 2> m_miot;
	required_device<pwm_display_device> m_digit_pwm;
	required_device<cassette_image_device> m_cass;
	required_device<rs232_port_device> m_rs232;
	required_ioport_array<4> m_row;
	required_ioport m_special;

	int m_sync_state = 0;
	bool m_k7 = false;
	bool m_tty_in = false;
	uint8_t m_u2_port_b = 0;
	uint8_t m_311_output = 0;
	uint32_t m_cassette_high_count = 0;

	void mem_map(address_map &map) ATTR_COLD;
	void sync_map(address_map &map) ATTR_COLD;

	uint8_t sync_r(offs_t offset);
	void sync_w(int state);

	uint8_t u2_read_a();
	void u2_write_a(uint8_t data);
	uint8_t u2_read_b();
	void u2_write_b(uint8_t data);

	TIMER_DEVICE_CALLBACK_MEMBER(cassette_input);
	void tty_callback(int data);
};

void kim1_state::machine_start()
{
	// Register for save states
	save_item(NAME(m_sync_state));
	save_item(NAME(m_k7));
	save_item(NAME(m_tty_in));
	save_item(NAME(m_u2_port_b));
	save_item(NAME(m_311_output));
	save_item(NAME(m_cassette_high_count));
}

void kim1_state::machine_reset()
{
	m_311_output = 0;
	m_cassette_high_count = 0;
}


static DEVICE_INPUT_DEFAULTS_START(terminal)
	DEVICE_INPUT_DEFAULTS("RS232_RXBAUD", 0xff, RS232_BAUD_2400)
	DEVICE_INPUT_DEFAULTS("RS232_TXBAUD", 0xff, RS232_BAUD_2400)
	DEVICE_INPUT_DEFAULTS("RS232_DATABITS", 0xff, RS232_DATABITS_8)
	DEVICE_INPUT_DEFAULTS("RS232_PARITY", 0xff, RS232_PARITY_NONE)
	DEVICE_INPUT_DEFAULTS("RS232_STOPBITS", 0xff, RS232_STOPBITS_2)
DEVICE_INPUT_DEFAULTS_END


//**************************************************************************
//  I/O
//**************************************************************************

INPUT_CHANGED_MEMBER(kim1_state::trigger_reset)
{
	// RS key triggers system reset via 556 timer
	if (newval)
		machine().schedule_soft_reset();
}

INPUT_CHANGED_MEMBER(kim1_state::trigger_nmi)
{
	// ST key triggers NMI via 556 timer
	if (newval)
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}

uint8_t kim1_state::sync_r(offs_t offset)
{
	// A10-A12 to 74145
	if (!machine().side_effects_disabled())
		m_k7 = bool(~offset & 0x1c00);

	return m_maincpu->space(AS_PROGRAM).read_byte(offset);
}

void kim1_state::sync_w(int state)
{
	// Signal NMI at falling edge of SYNC when SST is enabled and K7 line is high
	if (m_sync_state && !state && m_k7 && BIT(m_special->read(), 2))
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);

	m_sync_state = state;
}

uint8_t kim1_state::u2_read_a()
{
	uint8_t data = 0x7f;

	// Read from keyboard
	offs_t const sel = (m_u2_port_b >> 1) & 0x0f;
	if (4U > sel)
		data = m_row[sel]->read() & 0x7f;

	// Read from serial console
	data = data | (m_rs232->rxd_r() << 7);

	return data;
}

void kim1_state::u2_write_a(uint8_t data)
{
	// Write to 7-segment LEDs
	m_digit_pwm->write_mx(data & 0x7f);
}

uint8_t kim1_state::u2_read_b()
{
	if (m_u2_port_b & 0x20)
		return 0xff;

	// Load from cassette
	return 0x7f | (m_311_output ^ 0x80);
}

void kim1_state::u2_write_b(uint8_t data)
{
	m_u2_port_b = data;

	// Select 7-segment LED
	m_digit_pwm->write_my(1 << (data >> 1 & 0xf) >> 4);

	// Cassette write/speaker update
	if (data & 0x20)
		m_cass->output((data & 0x80) ? -1.0 : 1.0);

	// Write bit 0 to serial console. The hardware ANDs it with TTY in.
	m_rs232->write_txd(BIT(data, 0) & (m_tty_in ? 1 : 0));
}

TIMER_DEVICE_CALLBACK_MEMBER(kim1_state::cassette_input)
{
	double tap_val = m_cass->input();

	if (tap_val <= 0.0)
	{
		if (m_cassette_high_count)
		{
			m_311_output = (m_cassette_high_count < 8) ? 0x80 : 0;
			m_cassette_high_count = 0;
		}
	}

	if (tap_val > 0.0)
		m_cassette_high_count++;
}


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void kim1_state::mem_map(address_map &map)
{
	map(0x0000, 0x03ff).mirror(0xe000).ram();
	map(0x1700, 0x170f).mirror(0xe030).m(m_miot[1], FUNC(mos6530_device::io_map));
	map(0x1740, 0x174f).mirror(0xe030).m(m_miot[0], FUNC(mos6530_device::io_map));
	map(0x1780, 0x17bf).mirror(0xe000).m(m_miot[1], FUNC(mos6530_device::ram_map));
	map(0x17c0, 0x17ff).mirror(0xe000).m(m_miot[0], FUNC(mos6530_device::ram_map));
	map(0x1800, 0x1bff).mirror(0xe000).m(m_miot[1], FUNC(mos6530_device::rom_map));
	map(0x1c00, 0x1fff).mirror(0xe000).m(m_miot[0], FUNC(mos6530_device::rom_map));
}

void kim1_state::sync_map(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(kim1_state::sync_r));
}

// Called when serial data comes in from console.
void kim1_state::tty_callback(int data)
{
	// Save state as it is needed by u2_write_b()
	m_tty_in = data;

	// Send data back to terminal to simulate the KIM-1 hardware
	// echo. The hardware ANDs this with U2 port B port 0.
	m_rs232->write_txd(data & BIT(m_u2_port_b, 0));
}


//**************************************************************************
//  INPUT PORTS
//**************************************************************************

static INPUT_PORTS_START( kim1 )
	PORT_START("ROW0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0')

	PORT_START("ROW1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7')

	PORT_START("ROW2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_NAME("PC")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_NAME("GO")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_UP) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR('^') PORT_NAME("+")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_NAME("DA")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR('-') PORT_NAME("AD")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')

	PORT_START("ROW3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_TOGGLE PORT_NAME("TTY")

	PORT_START("SPECIAL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_NAME("ST") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(kim1_state::trigger_nmi), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_NAME("RS") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(kim1_state::trigger_reset), 0)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1) PORT_TOGGLE PORT_NAME("SST")
INPUT_PORTS_END


//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

void kim1_state::kim1(machine_config &config)
{
	// basic machine hardware
	M6502(config, m_maincpu, 1_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &kim1_state::mem_map);
	m_maincpu->set_addrmap(AS_OPCODES, &kim1_state::sync_map);
	m_maincpu->sync_cb().set(FUNC(kim1_state::sync_w));

	// video hardware
	PWM_DISPLAY(config, m_digit_pwm).set_size(6, 7);
	m_digit_pwm->set_segmask(0x3f, 0x7f);
	config.set_default_layout(layout_kim1);

	// devices
	MOS6530(config, m_miot[0], 1_MHz_XTAL); // U2
	m_miot[0]->pa_rd_callback().set(FUNC(kim1_state::u2_read_a));
	m_miot[0]->pa_wr_callback().set(FUNC(kim1_state::u2_write_a));
	m_miot[0]->pb_rd_callback().set(FUNC(kim1_state::u2_read_b));
	m_miot[0]->pb_wr_callback().set(FUNC(kim1_state::u2_write_b));

	MOS6530(config, m_miot[1], 1_MHz_XTAL); // U3

	CASSETTE(config, m_cass);
	m_cass->set_formats(kim1_cassette_formats);
	m_cass->set_default_state(CASSETTE_STOPPED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cass->set_interface("kim1_cass");

	// serial console/tty
	rs232_port_device &m_rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	m_rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));
	m_rs232.rxd_handler().set(FUNC(kim1_state::tty_callback));

	SPEAKER(config, "mono").front_center();

	TIMER(config, "cassette_timer").configure_periodic(FUNC(kim1_state::cassette_input), attotime::from_hz(44100));

	// KIM-1 has two edge connectors for expansion; you could plug them into a backplane,
	// and that's what we're abstracting here.
	KIM1BUS(config, "bus", 0).set_space(m_maincpu, AS_PROGRAM);
	KIM1BUS_SLOT(config, "sl1", 0, "bus", kim1_cards, nullptr);
	KIM1BUS_SLOT(config, "sl2", 0, "bus", kim1_cards, nullptr);
	KIM1BUS_SLOT(config, "sl3", 0, "bus", kim1_cards, nullptr);
	KIM1BUS_SLOT(config, "sl4", 0, "bus", kim1_cards, nullptr);
	KIM1BUS_SLOT(config, "sl5", 0, "bus", kim1_cards, nullptr);

	// software list
	SOFTWARE_LIST(config, "cass_list").set_original("kim1_cass");
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( kim1 )
	ROM_REGION( 0x400, "miot0", 0 )
	ROM_LOAD("6530-002.u2", 0x0000, 0x0400, CRC(2b08e923) SHA1(054f7f6989af3a59462ffb0372b6f56f307b5362))

	ROM_REGION( 0x400, "miot1", 0 )
	ROM_LOAD("6530-003.u3", 0x0000, 0x0400, CRC(a2a56502) SHA1(60b6e48f35fe4899e29166641bac3e81e3b9d220))
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY           FULLNAME  FLAGS
COMP( 1976, kim1, 0,      0,      kim1,    kim1,  kim1_state, empty_init, "MOS Technology", "KIM-1",  MACHINE_SUPPORTS_SAVE)



kisssite.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

/*

PCB contains the following major components


ESS VideoDrive
ES3207FP B390
TTV32098A

ESS VideoDrive
ES3210F Q390
TTT22869A

SAMSUNG C039A
S1L9223A01-Q0

SAMSUNG C031
S5L9284D01-Q0

ASD  AE43BH4016I-35
50G00290919D

F 037B
KA9259D

HOLTEK
GT27C020-70
A039K1523-2

PT6312LQ
PTC  0014Z

74F125D
C034205
fnn0040L

74F125D
C034205
fnn0040L

*/

#include "emu.h"

#include "bus/ata/atapicdr.h"
#include "cpu/mipsx/mipsx.h"

#include "softlist_dev.h"


namespace {

class kisssite_state : public driver_device

{
public:
	kisssite_state(machine_config const &mconfig, device_type type, char const *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu")
	{
	}

	void kisssite(machine_config &config) ATTR_COLD;

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;

	void mem(address_map &map) ATTR_COLD;
};


void kisssite_state::machine_reset()
{
	m_maincpu->set_state_int(STATE_GENPC, 0x1cffff80); // might actually be 0x7fffff80 with a ROM mirror
}

void kisssite_state::mem(address_map &map)
{
	map(0x00000000, 0x0007ffff).ram();
	map(0x1c000000, 0x1c03ffff).mirror(0x00fc0000).rom().region("maincpu", 0);
	// registers at 0x20000000-0x2000ffff, ES6008 datasheet could be helpful
}

void kisssite_state::kisssite(machine_config &config)
{
	MIPSX(config, m_maincpu, 60'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &kisssite_state::mem);

	CDROM(config, "cdrom").set_interface("cdrom");
	SOFTWARE_LIST(config, "cd_list").set_original("kisssite_cd");
}

INPUT_PORTS_START(kisssite)
INPUT_PORTS_END

ROM_START(kisssite)
	ROM_REGION32_BE(0x040000, "maincpu", 0 )
	ROM_LOAD("ht27c020.u10", 0x000000, 0x040000, CRC(ccedce2b) SHA1(28dd3dfd0b8de0c5aa1c37d193ffc479d46563a1) )
ROM_END

} // anonymous namespace

SYST(200?, kisssite, 0,         0,      kisssite, kisssite, kisssite_state, empty_init, "Tomy", "Kiss-Site", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



kminus.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/*******************************************************************************

    Kyber Minus

*******************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/keyboard.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"
#include "machine/wd_fdc.h"
#include "video/mc6845.h"
#include "bus/rs232/rs232.h"
#include "imagedev/floppy.h"
#include "screen.h"
#include "softlist.h"


namespace {

class kminus_state : public driver_device
{
public:
	kminus_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_chargen(*this, "chargen")
		, m_videoram(*this, "videoram")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0U)
		, m_view(*this, "bootview")
	{ }

	void kminus(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	MC6845_UPDATE_ROW(update_row);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_region_ptr<uint8_t> m_chargen;
	required_shared_ptr<uint8_t> m_videoram;
	required_device<fd1793_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	floppy_image_device *m_selected_floppy = nullptr;

	memory_view m_view;

	uint8_t pio1_pa_r();
	void pio1_pa_w(uint8_t data);
	uint8_t pio1_pb_r();
	void pio1_pb_w(uint8_t data);

	void kbd_put(uint8_t data);
	uint8_t kbd_r();
	uint8_t m_kbd_data = 0;
};


MC6845_UPDATE_ROW(kminus_state::update_row)
{
	uint32_t *p = &bitmap.pix(y);

	for (int column = 0; column < x_count; column++)
	{
		uint8_t chr = m_videoram[(ma + column) & 0x7ff];
		uint16_t addr = (chr << 4) | (ra & 0x0f);
		uint16_t data = m_chargen[(0x800 | addr) & 0xfff];
		//uint16_t data = m_chargen[addr & 0xfff];

		if (column == cursor_x)
		{
			data = 0xff;
		}

		for (int bit = 0; bit < 8; bit++)
		{
			*p++ = BIT(data, 0) ? rgb_t::white() : rgb_t::black();
			data >>= 1;
		}
	}
}


void kminus_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).view(m_view);
	m_view[0](0x0000, 0x0fff).rom().region("maincpu", 0).mirror(0xf000);
	m_view[1](0x0000, 0xffff).ram();
	m_view[1](0xc000, 0xc7ff).ram().share("videoram");
	m_view[1](0xf000, 0xffff).rom().region("maincpu", 0);
}

void kminus_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x40, 0x43).rw("dart", FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
	map(0x48, 0x4b).rw("pio0", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x50, 0x53).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x58, 0x5b).rw("pio1", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x60, 0x63).rw("fdc", FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	map(0x68, 0x6f).noprw(); // sel1
	map(0x70, 0x77).noprw(); // sel2
	map(0x78, 0x78).w("crtc", FUNC(mc6845_device::address_w));
	map(0x79, 0x79).w("crtc", FUNC(mc6845_device::register_w));
	map(0x7a, 0x7a).r("crtc", FUNC(mc6845_device::status_r));
	map(0x7b, 0x7b).r("crtc", FUNC(mc6845_device::register_r));
	map(0x7c, 0x7c).lr8([]() { return 0x00; }, "ready"); //22 s3 r: b7 15 s4, b1 printer ready? w: printer data?
	//map(0x7d, 0x7d).w(); // 9 s4 video out chargen related? writes 0x10
	map(0x7e, 0x7e).lw8([this](uint8_t data) { logerror("enable_ram: %02x\n", data); m_view.select(1); }, "enable_ram_w"); // 13 s4 write $03 enable video ram? bits 3,1,0 vram [b1 vram
	map(0x7f, 0x7f).r(FUNC(kminus_state::kbd_r)); // 17 s5 kbd
}


uint8_t kminus_state::pio1_pa_r()
{
	/*

	bit     signal      description

	2       WPRT        write protect
	3       READY       ready
	4       FD2S        disk is two sided
	5       IP          index pulse
	6       INTRQ
	7       DRQ

	*/

	uint8_t data = 0x00;

	if (m_selected_floppy)
	{
		data |= m_selected_floppy->wpt_r() << 2;
		data |= m_selected_floppy->ready_r() << 3;
		data |= m_selected_floppy->twosid_r() << 4;
		data |= m_selected_floppy->idx_r() << 5;
	}

	data |= m_fdc->intrq_r() ? 0x00 : 0x40;
	data |= m_fdc->drq_r() ? 0x00 : 0x80;

	logerror("pio_pa_r: %02x\n", data);

	return data;
}

void kminus_state::pio1_pa_w(uint8_t data)
{
	/*

	bit     signal      description

	0       ?           wd1000 hard controller
	1       ?           wd1000 hard controller

	*/

	logerror("pio_pa_w: %02x\n", data);
}

uint8_t kminus_state::pio1_pb_r()
{
	/*

	bit     signal      description

	4       TG43        track > 43
	6       F5/F8       5.25"/8" select

	*/

	uint8_t data = 0x40;

	logerror("pio_pb_r: %02x\n", data);

	return data;
}

void kminus_state::pio1_pb_w(uint8_t data)
{
	/*

	bit     signal      description

	0       DS0         drive select 0
	1       DS1         drive select 1
	2       DS2         drive select 2
	3       DS3         drive select 3
	5       DDEN        density select
	7       SIDE1       side 1 select

	*/

	// drive select
	if (BIT(data, 0)) m_selected_floppy = m_floppy[0]->get_device();
	if (BIT(data, 1)) m_selected_floppy = m_floppy[1]->get_device();
	if (BIT(data, 2)) m_selected_floppy = nullptr; // floppy 2
	if (BIT(data, 3)) m_selected_floppy = nullptr; // floppy 3

	m_fdc->set_floppy(m_selected_floppy);

	// density select
	m_fdc->dden_w(BIT(data, 5));

	if (m_selected_floppy)
	{
		// side select
		m_selected_floppy->ss_w(!BIT(data, 7));
		m_selected_floppy->mon_w(0);
	}

	logerror("pio_pb_w: %02x\n", data);
}


void kminus_state::kbd_put(uint8_t data)
{
	//data &= 0x7f;
	/* allow backspace to work */
	//if (data == 8) data = 0x7f;
	// assert strobe
	m_kbd_data = data;
}

uint8_t kminus_state::kbd_r()
{
	uint8_t data = m_kbd_data;

	m_kbd_data = 0x00;

	return data;
}


void kminus_state::machine_start()
{
	save_item(NAME(m_kbd_data));
}

void kminus_state::machine_reset()
{
	m_view.select(0);
}


static const z80_daisy_config daisy_chain[] =
{
	{ "pio1" },
	{ "dart" },
	{ "pio0" },
	{ "ctc" },
	{ nullptr }
};

void kminus_state::kminus(machine_config &config)
{
	Z80(config, m_maincpu, 3.6864_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &kminus_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &kminus_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	z80pio_device &pio0(Z80PIO(config, "pio0", 3.6864_MHz_XTAL));
	pio0.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80pio_device &pio1(Z80PIO(config, "pio1", 3.6864_MHz_XTAL));
	pio1.in_pa_callback().set(FUNC(kminus_state::pio1_pa_r));
	pio1.out_pa_callback().set(FUNC(kminus_state::pio1_pa_w));
	pio1.in_pb_callback().set(FUNC(kminus_state::pio1_pb_r));
	pio1.out_pb_callback().set(FUNC(kminus_state::pio1_pb_w));
	pio1.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80ctc_device &ctc(Z80CTC(config, "ctc", 3.6864_MHz_XTAL));
	ctc.zc_callback<0>().set("dart", FUNC(z80dart_device::txca_w));
	ctc.zc_callback<0>().append("dart", FUNC(z80dart_device::rxca_w));
	ctc.zc_callback<1>().set("dart", FUNC(z80dart_device::txca_w));
	ctc.zc_callback<2>().set("dart", FUNC(z80dart_device::rxtxcb_w));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80dart_device &dart(Z80DART(config, "dart", 3.6864_MHz_XTAL));
	dart.out_txda_callback().set("terminal", FUNC(rs232_port_device::write_txd));
	dart.out_dtra_callback().set("terminal", FUNC(rs232_port_device::write_dtr));
	dart.out_rtsa_callback().set("terminal", FUNC(rs232_port_device::write_rts));
	dart.out_txdb_callback().set("serial", FUNC(rs232_port_device::write_txd));
	dart.out_dtrb_callback().set("serial", FUNC(rs232_port_device::write_dtr));
	dart.out_rtsb_callback().set("serial", FUNC(rs232_port_device::write_rts));
	dart.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	rs232_port_device &rs232a(RS232_PORT(config, "terminal", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set("dart", FUNC(z80dart_device::rxa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "serial", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set("dart", FUNC(z80dart_device::rxb_w));

	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(kminus_state::kbd_put));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(26_MHz_XTAL / 2, 840, 0, 640, 309, 0, 250);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	mc6845_device &crtc(MC6845(config, "crtc", 26_MHz_XTAL / 16));
	crtc.set_screen("screen");
	crtc.set_char_width(8);
	crtc.set_show_border_area(false);
	crtc.set_update_row_callback(FUNC(kminus_state::update_row));

	FD1793(config, m_fdc, 8_MHz_XTAL / 8);
	m_fdc->ready_wr_callback().set("pio1", FUNC(z80pio_device::pa3_w));
	m_fdc->intrq_wr_callback().set("pio1", FUNC(z80pio_device::pa6_w)).invert();
	m_fdc->drq_wr_callback().set("pio1", FUNC(z80pio_device::pa7_w)).invert();
	//m_fdc->set_force_ready(true);
	FLOPPY_CONNECTOR(config, m_floppy[0], "525qd", FLOPPY_525_QD, true, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], "525qd", FLOPPY_525_QD, true, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	//SOFTWARE_LIST(config, "flop_list").set_original("kminus_flop");
}


ROM_START(kminus)
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("kyberware_firmware.u83", 0x0000, 0x1000, CRC(fb7fa0b6) SHA1(7bc54743452d587e3b3ac3fcc088c2c979fa7c8e))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("kyberware_chargen.u24", 0x0000, 0x1000, CRC(18d9e79e) SHA1(e0a80dab109c0ac8b7f214d597955c97618208b3))
ROM_END

} // anonymous namespace


//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY              FULLNAME       FLAGS
COMP( 1982, kminus, 0,      0,      kminus,  0,      kminus_state, empty_init, "Kyber Calcolatori", "Kyber Minus", MACHINE_NOT_WORKING )



kn01.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont

/*
 * Digital Equipment Corporation DECstation 2100/3100.
 *
 * Model  Board  Codename  CPU               RAM
 * 2100   KN01   PMIN      R2000 @ 12.5MHz   24M
 * 3100   KN01   PMAX      R2000 @ 16.67MHz  24M
 *
 * Sources:
 *  - DECstation 3100 Desktop Workstation Functional Specification, Revision 1.3, August 28, 1990, Workstation Systems Engineering, Digital Equipment Corporation
 *
 * TODO:
 *  - scsi
 *  - devicify pcc
 *  - mouse
 */

/*
 * ds3100 wip
 * - http://www.vanade.com/~blc/DS3100/bootrom.html
 * - http://www.vanade.com/~blc/DS3100/3100test.html
 *
 * - press Ctrl+C at flashing >> prompt to enter monitor
 * - boot -f rz(0,4,0)vmunix to boot ULTRIX from CD-ROM at SCSI ID=4
 *     ?0bd-36 rst err csr a exp 00000000 00000010
 *     ?446 scsi rst
 *     ?470 bt err: rz(0,4,0)vmunix
 *
 * diagnostic test failures
 *  D  passes with rs232 loopback on com_port and prt_port
 *  k  keyboard
 *  P  mouse not emulated
 *  r  passes if seconds increment within 300,000 iterations of loop
 *  s  sii not emulated
 */

#include "emu.h"

#include "dc7061.h"
#include "dc7085.h"
#include "lk201.h"

#include "cpu/mips/mips1.h"

#include "machine/am79c90.h"
#include "machine/mc146818.h"
#include "machine/nscsi_bus.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "video/bt47x.h"

#include "bus/rs232/rs232.h"
#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"

#include "screen.h"

#include "endianness.h"

#include "kn01.lh"

#define VERBOSE (0)

#include "logmacro.h"

namespace {

class kn01_state : public driver_device
{
public:
	kn01_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_mram(*this, "mram")
		, m_esar(*this, "esar")
		, m_rtc(*this, "rtc")
		, m_dz(*this, "dc7085")
		, m_sii(*this, "scsi:6:sii")
		, m_lance(*this, "am79c90")
		, m_lk201(*this, "lk201")
		, m_screen(*this, "screen")
		, m_vdac(*this, "bt478")
		, m_scantimer(*this, "scantimer")
		, m_vram(*this, "vram")
		, m_config(*this, "config")
		, m_leds(*this, "led%u", 0U)
	{
	}

	void pmax(machine_config &config) { kn01(config, 33.33_MHz_XTAL / 2); }
	void pmin(machine_config &config) { kn01(config, 25_MHz_XTAL / 2); }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void kn01(machine_config &config, XTAL clock);

	void map(address_map &map) ATTR_COLD;

	u16 status_r();
	void control_w(u16 data);

	u16 pcc_r(offs_t offset);
	void pcc_w(offs_t offset, u16 data);

	void vram_w(offs_t offset, u32 data, u32 mem_mask);
	void plane_mask_w(u8 data);

	void memerr_w(offs_t offset, u32 data, u32 mem_mask);

	TIMER_DEVICE_CALLBACK_MEMBER(scanline_timer);

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect);

private:
	enum sys_csr_mask : u16
	{
		VRGTRB  = 0x0001, // video dac voltage red > blue
		VRGTRG  = 0x0002, // video dac voltage red > green
		VBGTRG  = 0x0004, // video dac voltage blue > green
		TXDIS   = 0x0100, // disable serial transmit drivers
		VINT    = 0x0200, // pcc programmable area detect 2
		MEMERR  = 0x0400, // bus timeout on write
		MONO    = 0x0800, // monochrome framebuffer installed
		CRSRTST = 0x1000, // pcc test output
		PARDIS  = 0x2000, // memory parity disable
		STATUS  = 0x4000, // self-test completed successfully
		MNFMOD  = 0x8000, // manufacturing self test jumper installed
	};

	enum pcc_regnum : unsigned
	{
		PCC_CMDR   =  0,
		PCC_XPOS   =  1,
		PCC_YPOS   =  2,
		PCC_XMIN1  =  3,
		PCC_XMAX1  =  4,
		PCC_YMIN1  =  5,
		PCC_YMAX1  =  6,
		PCC_XMIN2  = 11,
		PCC_XMAX2  = 12,
		PCC_YMIN2  = 13,
		PCC_YMAX2  = 14,
		PCC_MEMORY = 15,
	};

	enum msr_mask : u16
	{
		MSR_DSR3 = 0x0001,
		MSR_DSR2 = 0x0200,
	};

	required_device<mips1_device_base> m_cpu;
	required_device<ram_device> m_mram;
	required_region_ptr<u8> m_esar;

	required_device<mc146818_device> m_rtc;
	required_device<dc7085_device> m_dz;
	required_device<dc7061_device> m_sii;
	required_device<am79c90_device> m_lance;

	optional_device<lk201_device> m_lk201;

	optional_device<screen_device> m_screen;
	optional_device<bt478_device> m_vdac;
	optional_device<timer_device> m_scantimer;
	optional_shared_ptr<u32> m_vram;

	required_ioport m_config;
	output_finder<8> m_leds;

	u16 m_status;

	u32 m_plane_mask;

	u16 m_pcc_regs[16];

	std::unique_ptr<u16[]> m_dram; // disk ram
	std::unique_ptr<u16[]> m_nram; // network ram

	u16 m_msr;
	u32 m_weaddr; // write error address
};

uint32_t kn01_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect)
{
	u32 const *pixel_pointer = m_vram;

	if (m_status & MONO)
	{
		for (int y = screen.visible_area().min_y; y <= screen.visible_area().max_y; y++)
		{
			for (int x = screen.visible_area().min_x; x <= screen.visible_area().max_x; x += 32)
			{
				u32 const pixel_data = *pixel_pointer++;

				for (unsigned i = 0; i < 32; i++)
					bitmap.pix(y, x + i) = m_vdac->pen_color(BIT(pixel_data, i) * 128);
			}

			pixel_pointer += 1024 / 32;
		}
	}
	else
	{
		for (int y = screen.visible_area().min_y; y <= screen.visible_area().max_y; y++)
		{
			for (int x = screen.visible_area().min_x; x <= screen.visible_area().max_x; x += 4)
			{
				u32 const pixel_data = *pixel_pointer++;

				bitmap.pix(y, x + 3) = m_vdac->pen_color(u8(pixel_data >> 24));
				bitmap.pix(y, x + 2) = m_vdac->pen_color(u8(pixel_data >> 16));
				bitmap.pix(y, x + 1) = m_vdac->pen_color(u8(pixel_data >> 8));
				bitmap.pix(y, x + 0) = m_vdac->pen_color(u8(pixel_data >> 0));
			}
		}
	}

	return 0;
}

u16 kn01_state::pcc_r(offs_t offset)
{
	return m_pcc_regs[offset];
}

void kn01_state::pcc_w(offs_t offset, u16 data)
{
	m_pcc_regs[offset] = data;
}

void kn01_state::plane_mask_w(u8 data)
{
	m_plane_mask = (u32(data) << 24) | (u32(data) << 16) | (u32(data) << 8) | (u32(data) << 0);
}

TIMER_DEVICE_CALLBACK_MEMBER(kn01_state::scanline_timer)
{
	int scanline = m_screen->vpos();

	if ((scanline == m_pcc_regs[PCC_YMIN2]) && (m_pcc_regs[PCC_CMDR] & 0x0400) && !(m_status & VINT))
	{
		m_status |= VINT;
		m_cpu->set_input_line(INPUT_LINE_IRQ4, ASSERT_LINE);
	}

	if ((scanline == m_pcc_regs[PCC_YMIN1]) && (m_pcc_regs[PCC_CMDR] & 0x0100))
	{
		auto const vram = util::little_endian_cast<u8 const>(m_vram.target());

		int const x = m_pcc_regs[PCC_XMIN1] - 212;
		int const y = m_pcc_regs[PCC_YMIN1] - 34;
		//printf("sampling for VRGTRB and friends at X=%d Y=%d\n", x, y);
		m_status &= ~(VBGTRG | VRGTRG | VRGTRB);
		if ((x >= 0) && (x <= 1023) && (y >= 0) && (y <= 863))
		{
			rgb_t const rgb = m_vdac->pen_color(vram[(y * 1024) + x]);

			//printf("R=%d, G=%d, B=%d\n", r, g, b);
			if (rgb.r() > rgb.b()) m_status |= VRGTRB;
			if (rgb.r() > rgb.g()) m_status |= VRGTRG;
			if (rgb.b() > rgb.g()) m_status |= VBGTRG;
		}
	}
}

void kn01_state::memerr_w(offs_t offset, u32 data, u32 mem_mask)
{
	LOG("memerr_w 0x%08x mask 0x%08x\n", offset << 2, mem_mask);
	m_status |= MEMERR;
	switch (mem_mask)
	{
	case 0x0000'ff00:
		m_weaddr = (offset << 2) | 1;
		break;
	case 0x00ff'0000:
		m_weaddr = (offset << 2) | 2;
		break;
	case 0xff00'0000:
		m_weaddr = (offset << 2) | 3;
		break;
	case 0xffff'0000:
		m_weaddr = (offset << 2) | 2;
		break;
	default:
		m_weaddr = offset << 2;
		break;
	}

	m_cpu->set_input_line(INPUT_LINE_IRQ4, ASSERT_LINE);
}

void kn01_state::machine_start()
{
	m_dram = std::make_unique<u16[]>(65536);
	m_nram = std::make_unique<u16[]>(32768);

	m_leds.resolve();

	m_cpu->space(AS_PROGRAM).install_ram(0, m_mram->mask(), m_mram->pointer());

	m_cpu->space(AS_PROGRAM).install_readwrite_tap(0x1c000000, 0x1c00001b, "dz_delay",
		[this](offs_t offset, u32 &data, u32 mem_mask) { m_cpu->eat_cycles(13); },
		[this](offs_t offset, u32 &data, u32 mem_mask) { m_cpu->eat_cycles(18); });

#if 0
	m_cpu->space(AS_PROGRAM).install_readwrite_tap(0x1d000000, 0x1d0000ff, "rtc_delay",
		[this](offs_t offset, u32 &data, u32 mem_mask) { m_cpu->eat_cycles(13); },
		[this](offs_t offset, u32 &data, u32 mem_mask) { m_cpu->eat_cycles(16); });

	switch (m_config->read() & ~MNFMOD)
	{
	case 0x0800: // monochrome
		m_cpu->space(AS_PROGRAM).unmap_readwrite(0x0fc20000, 0x0fcfffff);
		break;

	case 0x0801: // none
		m_cpu->space(AS_PROGRAM).unmap_readwrite(0x0fc00000, 0x0fcfffff);
		break;
	}
#endif
}

void kn01_state::machine_reset()
{
	m_status = m_config->read() & (MNFMOD | MONO);

	for (auto &l : m_leds)
		l = 0;

	m_plane_mask = 0;
}

void kn01_state::vram_w(offs_t offset, u32 data, u32 mem_mask)
{
	mem_mask &= m_plane_mask;

	COMBINE_DATA(&m_vram[offset]);
}

u16 kn01_state::status_r()
{
	return m_status;
}

void kn01_state::control_w(u16 data)
{
	// update LEDs
	for (unsigned i = 0; i < 8; i++)
		m_leds[i] = BIT(data, i);

	if (data & VINT)
	{
		m_cpu->set_input_line(INPUT_LINE_IRQ4, CLEAR_LINE);
		m_status &= ~VINT;
	}

	if (data & MEMERR)
	{
		m_cpu->set_input_line(INPUT_LINE_IRQ4, CLEAR_LINE);
		m_status &= ~MEMERR;
	}

	if (data & PARDIS)
	{
		// TODO: disable parity checking
	}

	if (data & STATUS)
	{
		// TODO: assert status output
	}

	m_status = (m_status & ~(TXDIS | PARDIS | STATUS)) | (data & (TXDIS | PARDIS | STATUS));
}

void kn01_state::map(address_map &map)
{
	map(0x00000000, 0x1fffffff).w(FUNC(kn01_state::memerr_w));

	map(0x0fc00000, 0x0fcfffff).ram().share("vram").w(FUNC(kn01_state::vram_w));

	map(0x10000000, 0x10000000).w(FUNC(kn01_state::plane_mask_w));
	map(0x11000000, 0x1100003f).rw(FUNC(kn01_state::pcc_r), FUNC(kn01_state::pcc_w)).umask32(0x0000ffff);
	map(0x12000000, 0x1200001f).m(m_vdac, FUNC(bt478_device::map)).umask32(0x000000ff).mirror(0xe0);
	map(0x17000000, 0x17000003).lr32(NAME([this]() { return m_weaddr; }));
	map(0x18000000, 0x18000007).rw(m_lance, FUNC(am79c90_device::regs_r), FUNC(am79c90_device::regs_w)).umask32(0x0000ffff);
	map(0x19000000, 0x1901ffff).lrw16(
		[this](offs_t offset) { return m_nram[offset]; }, "nram_r",
		[this](offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_nram[offset]); }, "nram_w").umask32(0xffff);
	map(0x1a000000, 0x1a000057).m(m_sii, FUNC(dc7061_device::map)).umask32(0xffff);
	map(0x1b000000, 0x1b03ffff).lrw16(
		[this](offs_t offset) { return m_dram[offset]; }, "dram_r",
		[this](offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_dram[offset]); }, "dram_w").umask32(0xffff);
	map(0x1c000000, 0x1c00001b).m(m_dz, FUNC(dc7085_device::map)).umask32(0xffff);
	map(0x1c000018, 0x1c000019).lr16([this]() { return m_msr; }, "msr_r");
	map(0x1d000000, 0x1d0000ff).rw(m_rtc, FUNC(mc146818_device::read_direct), FUNC(mc146818_device::write_direct)).umask32(0x000000ff);
	map(0x1d000000, 0x1d00007f).lr8([this](offs_t offset) { return m_esar[offset]; }, "esar_r").umask32(0xff00);
	map(0x1e000000, 0x1e000001).rw(FUNC(kn01_state::status_r), FUNC(kn01_state::control_w));
	map(0x1fc00000, 0x1fc3ffff).rom().region("eprom", 0);
}

static void dec_scsi_devices(device_slot_interface &device)
{
	device.option_add("cdrom", NSCSI_CDROM);
	device.option_add("harddisk", NSCSI_HARDDISK);
}

void kn01_state::kn01(machine_config &config, XTAL clock)
{
	R2000(config, m_cpu, clock, 65536, 65536);
	m_cpu->set_endianness(ENDIANNESS_LITTLE);
	m_cpu->set_fpu(mips1_device_base::MIPS_R3010Av4, INPUT_LINE_IRQ5);
	m_cpu->in_brcond<0>().set_constant(1);
	m_cpu->set_addrmap(AS_PROGRAM, &kn01_state::map);

	RAM(config, m_mram);
	m_mram->set_default_size("24MiB");
	m_mram->set_extra_options("4MiB,8MiB,12MiB,16MiB,20MiB");

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(69169800, 1280, 212, 1024+212, 901, 34, 864+34);
	m_screen->set_screen_update(FUNC(kn01_state::screen_update));

	TIMER(config, m_scantimer, 0);
	m_scantimer->configure_scanline(FUNC(kn01_state::scanline_timer), "screen", 0, 1);

	BT478(config, m_vdac, 69169800);

	AM79C90(config, m_lance, XTAL(12'500'000));
	m_lance->intr_out().set_inputline(m_cpu, INPUT_LINE_IRQ1).invert();
	m_lance->dma_in().set([this](offs_t offset) { return m_nram[(offset & 0xffff) >> 1]; });
	m_lance->dma_out().set([this](offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_nram[(offset & 0xffff) >> 1]); });

	DS1287(config, m_rtc, XTAL(32'768));
	m_rtc->set_binary(true);
	m_rtc->irq().set_inputline(m_cpu, INPUT_LINE_IRQ3);

	/*
	 * 0: keyboard (rx/tx)
	 * 1: mouse (rx/tx)
	 * 2: modem (rx/tx, dtr/dsr)
	 * 3: printer/console (rx/tx)
	 */
	DC7085(config, m_dz, 15.2064_MHz_XTAL);
	m_dz->int_cb().set_inputline(m_cpu, INPUT_LINE_IRQ2);

	LK201(config, m_lk201, 0);
	m_dz->tx_cb<0>().set([this](int state) { if (!(m_status & TXDIS)) m_lk201->rx_w(state); });
	m_lk201->tx_handler().set(m_dz, FUNC(dc7085_device::rx_w<0>));

	// TODO: kn01 prom requires a "mouse terminator" (Tx to Rx loopback) when no mouse is connected
	m_dz->tx_cb<1>().set([this](int state) { if (!(m_status & TXDIS)) m_dz->rx_w<1>(state); });

	rs232_port_device &com(RS232_PORT(config, "com_port", default_rs232_devices, nullptr));
	m_dz->tx_cb<2>().set([this, &com](int state) { if (!(m_status & TXDIS)) com.write_txd(state); });
	m_dz->dtr_cb<2>().set(com, FUNC(rs232_port_device::write_dtr));
	com.rxd_handler().set(m_dz, FUNC(dc7085_device::rx_w<2>));
	com.dsr_handler().set(
		[this](int state)
		{
			if (state)
				m_msr |= MSR_DSR2;
			else
				m_msr &= ~MSR_DSR2;
		});

	rs232_port_device &prt(RS232_PORT(config, "prt_port", default_rs232_devices, nullptr));
	m_dz->tx_cb<3>().set([this, &prt](int state) { if (!(m_status & TXDIS)) prt.write_txd(state); });
	m_dz->dtr_cb<3>().set(prt, FUNC(rs232_port_device::write_dtr));
	prt.rxd_handler().set(m_dz, FUNC(dc7085_device::rx_w<3>));
	prt.dsr_handler().set(
		[this](int state)
		{
			if (state)
				m_msr |= MSR_DSR3;
			else
				m_msr &= ~MSR_DSR3;
		});

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", dec_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:1", dec_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", dec_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", dec_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", dec_scsi_devices, "cdrom");
	NSCSI_CONNECTOR(config, "scsi:5", dec_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6").option_set("sii", DC7061).clock(20_MHz_XTAL).machine_config(
		[this](device_t *device)
		{
			dc7061_device &sii = downcast<dc7061_device &>(*device);

			sii.sys_int().set_inputline(m_cpu, INPUT_LINE_IRQ0);
		});
	NSCSI_CONNECTOR(config, "scsi:7", dec_scsi_devices, nullptr);

	config.set_default_layout(layout_kn01);
}

static INPUT_PORTS_START(kn01)
	PORT_START("config")
	PORT_DIPNAME(0x0801, 0x0000, "Graphics Mode")
	PORT_DIPSETTING(     0x0000, "Color")
	PORT_DIPSETTING(     0x0800, "Monochrome")
	PORT_DIPSETTING(     0x0801, "None")

	PORT_DIPNAME(0x8000, 0x8000, "Manufacturing Mode")
	PORT_DIPSETTING(     0x8000, DEF_STR(Off))
	PORT_DIPSETTING(     0x0000, DEF_STR(On))
INPUT_PORTS_END

ROM_START( ds3100 )
	ROM_REGION32_LE( 0x40000, "eprom", 0 )
	ROM_LOAD( "kn01-aa.v7.01.img", 0x000000, 0x040000, CRC(e2478aa7) SHA1(e789387c52df3e0d83fde97cb48314627ea90b93) )

	// hand-crafted following the documentation and logic of the "t e" diagnostic test
	ROM_REGION(0x20, "esar", 0)
	ROM_LOAD("esar.bin", 0x00, 0x20, CRC(ff083e3b) SHA1(1714338d8747ec434e77b72e7bd81f77aacf27d2))
ROM_END

#define rom_ds2100 rom_ds3100

} // anonymous namespace

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY                          FULLNAME           FLAGS
COMP( 1989, ds2100, 0,      0,      pmin,    kn01,  kn01_state, empty_init, "Digital Equipment Corporation", "DECstation 2100", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1989, ds3100, 0,      0,      pmax,    kn01,  kn01_state, empty_init, "Digital Equipment Corporation", "DECstation 3100", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



kn02.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/****************************************************************************

    decstation.cpp: MIPS-based DECstation family

    WANTED: boot ROM dumps for KN02CA/KN04CA (MAXine) systems.

    NOTE: after all the spew of failing tests, press 'q' at the MORE prompt and
    wait a few seconds for the PROM monitor to appear.
    Type 'ls' for a list of commands (this is a very UNIX-flavored PROM monitor).

    Machine types:
        Personal DECstation 5000/xx (MAXine/KN02CA for R3000, KN04CA? for R4000)
            20, 25, or 33 MHz R3000 or 100 MHz R4000
            40 MiB max RAM
            Serial: DEC "DZ" quad-UART for keyboard/mouse, SCC8530 for modem/printer
            SCSI: NCR53C94
            Ethernet: AMD7990 "LANCE" controller
            Audio/ISDN: AMD AM79C30
            Color 1024x768 8bpp video on-board
            2 TURBOchannel slots

        DECstation 5000/1xx: (3MIN/KN02BA, KN04BA? for R4000):
            20, 25, or 33 MHz R3000 or 100 MHz R4000
            128 MiB max RAM
            Serial: 2x SCC8530
            SCSI: NCR53C94
            Ethernet: AMD7990 "LANCE" controller
            No on-board video
            3 TURBOchannel slots

        DECstation 5000/200: (3MAX/KN02):
            25 MHz R3000
            480 MiB max RAM
            Serial: DEC "DZ" quad-UART
            SCSI: NCR53C94
            Ethernet: AMD7990 "LANCE" controllor

        DECstation 5000/240 (3MAX+/KN03AA), 5000/260 (3MAX+/KN05)
            40 MHz R3400, or 120 MHz R4400.
            480 MiB max RAM
            Serial: 2x SCC8530
            SCSI: NCR53C94
            Ethernet: AMD7990 "LANCE" controller

****************************************************************************/

#include "emu.h"

#include "decioga.h"
#include "lk201.h"
#include "sfb.h"

#include "cpu/mips/mips1.h"

#include "machine/am79c90.h"
#include "machine/mc146818.h"
#include "machine/ncr53c90.h"
#include "machine/nscsi_bus.h"
#include "machine/ram.h"
#include "machine/z80scc.h"
#include "video/bt459.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"
#include "bus/rs232/rs232.h"

#include "screen.h"

namespace {

class kn02ba_state : public driver_device
{
public:
	kn02ba_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_screen(*this, "screen")
		, m_sfb(*this, "sfb")
		, m_lk201(*this, "lk201")
		, m_ioga(*this, "ioga")
		, m_rtc(*this, "rtc")
		, m_scc(*this, "scc%u", 0U)
		, m_asc(*this, "scsi:7:asc")
		, m_vrom(*this, "gfx")
		, m_bt459(*this, "bt459")
		, m_lance(*this, "am79c90")
	{
	}

	void m120(machine_config &config) { kn02ba(config, 20'000'000); }
	void m125(machine_config &config) { kn02ba(config, 25'000'000); }
	void m133(machine_config &config) { kn02ba(config, 33'300'000); }

protected:
	void kn02ba(machine_config &config, u32 clock);

	uint32_t cfb_r(offs_t offset);
	void cfb_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	required_device<mips1_device_base> m_cpu;
	required_device<screen_device> m_screen;
	optional_device<decsfb_device> m_sfb;
	optional_device<lk201_device> m_lk201;
	required_device<dec_ioga_device> m_ioga;
	required_device<mc146818_device> m_rtc;
	required_device_array<z80scc_device, 2> m_scc;
	optional_device<ncr53c94_device> m_asc;
	optional_memory_region m_vrom;
	optional_device<bt459_device> m_bt459;
	required_device<am79c90_device> m_lance;

	void map(address_map &map) ATTR_COLD;

	u8 *m_vrom_ptr;
};

/***************************************************************************
    VIDEO HARDWARE
***************************************************************************/

uint32_t kn02ba_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	m_bt459->screen_update(screen, bitmap, cliprect, (uint8_t *)m_sfb->get_vram());
	return 0;
}

uint32_t kn02ba_state::cfb_r(offs_t offset)
{
	uint32_t const addr = offset << 2;

	//logerror("cfb_r: reading at %x\n", addr);

	if (addr < 0x80000)
	{
		return m_vrom_ptr[addr>>2] & 0xff;
	}

	if ((addr >= 0x100000) && (addr < 0x100200))
	{
	}

	if ((addr >= 0x200000) && (addr < 0x400000))
	{
	}

	return 0xffffffff;
}

void kn02ba_state::cfb_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	uint32_t const addr = offset << 2;

	if ((addr >= 0x100000) && (addr < 0x100200))
	{
		return;
	}

	if ((addr >= 0x1c0000) && (addr < 0x200000))
	{
		//printf("Bt459: %08x (mask %08x) @ %x\n", data, mem_mask, offset<<2);
		return;
	}

	if ((addr >= 0x200000) && (addr < 0x400000))
	{
	}
}

/***************************************************************************
    MACHINE FUNCTIONS
***************************************************************************/

void kn02ba_state::machine_start()
{
	if (m_vrom)
		m_vrom_ptr = m_vrom->base();
}

void kn02ba_state::machine_reset()
{
	m_ioga->set_dma_space(&m_cpu->space(AS_PROGRAM));
}

/***************************************************************************
    ADDRESS MAPS
***************************************************************************/

void kn02ba_state::map(address_map &map)
{
	map(0x00000000, 0x07ffffff).ram();  // full 128 MB
	map(0x10000000, 0x1007ffff).rw(FUNC(kn02ba_state::cfb_r), FUNC(kn02ba_state::cfb_w));
	map(0x10100000, 0x101001ff).rw(m_sfb, FUNC(decsfb_device::read), FUNC(decsfb_device::write));
	map(0x101c0000, 0x101c000f).m("bt459", FUNC(bt459_device::map)).umask32(0x000000ff);
	map(0x10200000, 0x103fffff).rw(m_sfb, FUNC(decsfb_device::vram_r), FUNC(decsfb_device::vram_w));
	map(0x1c000000, 0x1c07ffff).m(m_ioga, FUNC(dec_ioga_device::map));
	map(0x1c0c0000, 0x1c0c0007).rw(m_lance, FUNC(am79c90_device::regs_r), FUNC(am79c90_device::regs_w)).umask32(0x0000ffff);
	map(0x1c100000, 0x1c100003).rw(m_scc[0], FUNC(z80scc_device::ca_r), FUNC(z80scc_device::ca_w)).umask32(0x0000ff00);
	map(0x1c100004, 0x1c100007).rw(m_scc[0], FUNC(z80scc_device::da_r), FUNC(z80scc_device::da_w)).umask32(0x0000ff00);
	map(0x1c100008, 0x1c10000b).rw(m_scc[0], FUNC(z80scc_device::cb_r), FUNC(z80scc_device::cb_w)).umask32(0x0000ff00);
	map(0x1c10000c, 0x1c10000f).rw(m_scc[0], FUNC(z80scc_device::db_r), FUNC(z80scc_device::db_w)).umask32(0x0000ff00);
	map(0x1c180000, 0x1c180003).rw(m_scc[1], FUNC(z80scc_device::ca_r), FUNC(z80scc_device::ca_w)).umask32(0x0000ff00);
	map(0x1c180004, 0x1c180007).rw(m_scc[1], FUNC(z80scc_device::da_r), FUNC(z80scc_device::da_w)).umask32(0x0000ff00);
	map(0x1c180008, 0x1c18000b).rw(m_scc[1], FUNC(z80scc_device::cb_r), FUNC(z80scc_device::cb_w)).umask32(0x0000ff00);
	map(0x1c18000c, 0x1c18000f).rw(m_scc[1], FUNC(z80scc_device::db_r), FUNC(z80scc_device::db_w)).umask32(0x0000ff00);
	map(0x1c200000, 0x1c2000ff).rw(m_rtc, FUNC(mc146818_device::read_direct), FUNC(mc146818_device::write_direct)).umask32(0x000000ff);
	map(0x1c300000, 0x1c30003f).m(m_asc, FUNC(ncr53c94_device::map)).umask32(0x000000ff);
	map(0x1fc00000, 0x1fc3ffff).rom().region("user1", 0);
}

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

static void dec_scsi_devices(device_slot_interface &device)
{
	device.option_add("cdrom", NSCSI_CDROM);
	device.option_add("harddisk", NSCSI_HARDDISK);
}

void kn02ba_state::kn02ba(machine_config &config, u32 clock)
{
	R3000A(config, m_cpu, clock, 65536, 131072);
	m_cpu->set_endianness(ENDIANNESS_LITTLE);
	m_cpu->set_fpu(mips1_device_base::MIPS_R3010Av4);
	m_cpu->in_brcond<0>().set_constant(1);
	m_cpu->set_addrmap(AS_PROGRAM, &kn02ba_state::map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(130000000, 1704, 32, (1280+32), 1064, 3, (1024+3));
	m_screen->set_screen_update(FUNC(kn02ba_state::screen_update));

	DECSFB(config, m_sfb, 25'000'000);  // clock based on white paper which quotes "40ns" gate array cycle times
//  m_sfb->int_cb().set(FUNC(dec_ioga_device::slot0_irq_w));

	BT459(config, m_bt459, 83'020'800);

	AM79C90(config, m_lance, XTAL(12'500'000));
	m_lance->intr_out().set("ioga", FUNC(dec_ioga_device::lance_irq_w));
	m_lance->dma_in().set("ioga", FUNC(dec_ioga_device::lance_dma_r));
	m_lance->dma_out().set("ioga", FUNC(dec_ioga_device::lance_dma_w));

	DECSTATION_IOGA(config, m_ioga, XTAL(12'500'000));
	m_ioga->irq_out().set_inputline(m_cpu, INPUT_LINE_IRQ3);

	MC146818(config, m_rtc, XTAL(32'768));
	m_rtc->irq().set("ioga", FUNC(dec_ioga_device::rtc_irq_w));
	m_rtc->set_binary(true);

	SCC85C30(config, m_scc[0], XTAL(14'745'600)/2);
	m_scc[0]->out_int_callback().set("ioga", FUNC(dec_ioga_device::scc0_irq_w));
	m_scc[0]->out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
	m_scc[0]->out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));

	SCC85C30(config, m_scc[1], XTAL(14'745'600)/2);
	m_scc[1]->out_int_callback().set("ioga", FUNC(dec_ioga_device::scc1_irq_w));
	m_scc[1]->out_txdb_callback().set(m_lk201, FUNC(lk201_device::rx_w));

	LK201(config, m_lk201, 0);
	m_lk201->tx_handler().set(m_scc[1], FUNC(z80scc_device::rxb_w));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_scc[0], FUNC(z80scc_device::rxa_w));
	rs232a.dcd_handler().set(m_scc[0], FUNC(z80scc_device::dcda_w));
	rs232a.cts_handler().set(m_scc[0], FUNC(z80scc_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_scc[0], FUNC(z80scc_device::rxb_w));
	rs232b.dcd_handler().set(m_scc[0], FUNC(z80scc_device::dcdb_w));
	rs232b.cts_handler().set(m_scc[0], FUNC(z80scc_device::ctsb_w));

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", dec_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:1", dec_scsi_devices, "cdrom");
	NSCSI_CONNECTOR(config, "scsi:2", dec_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", dec_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", dec_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", dec_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", dec_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:7").option_set("asc", NCR53C94).clock(10_MHz_XTAL).machine_config(
		[this](device_t *device)
		{
			ncr53c94_device &asc = downcast<ncr53c94_device &>(*device);

			asc.irq_handler_cb().set_inputline(m_cpu, INPUT_LINE_IRQ0);
		});
}

static INPUT_PORTS_START(kn02ba)
	PORT_START("UNUSED") // unused IN0
	PORT_BIT(0xffff, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

/***************************************************************************

  ROM definition(s)

***************************************************************************/

ROM_START( ds5k133 )
	ROM_REGION32_LE( 0x40000, "user1", 0 )
	// 5.7j                                                                                                                                                                                                                                 sx
	ROM_LOAD( "ds5000-133_005eb.bin", 0x000000, 0x040000, CRC(76a91d29) SHA1(140fcdb4fd2327daf764a35006d05fabfbee8da6) )

	ROM_REGION32_LE( 0x20000, "gfx", 0 )
	ROM_LOAD( "pmagb-ba-rom.img", 0x000000, 0x020000, CRC(91f40ab0) SHA1(a39ce6ed52697a513f0fb2300a1a6cf9e2eabe33) )
ROM_END

} // anonymous namespace

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT  COMPANY                          FULLNAME               FLAGS
//COMP( 1992, ds5k20,  0,      0,      m20,     kn02ca, kn02ca_state, init, "Digital Equipment Corporation", "DECstation 5000/20",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
//COMP( 1992, ds5k120, 0,      0,      m120,    kn02ba, kn02ba_state, init, "Digital Equipment Corporation", "DECstation 5000/120", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1992, ds5k133, 0,      0,      m133,    kn02ba, kn02ba_state, empty_init, "Digital Equipment Corporation", "DECstation 5000/133", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



kn5000.cpp
<---------------------------------------------------------------------->
// license:GPL2+
// copyright-holders:Felipe Sanches
/******************************************************************************

    Technics SX-KN5000 music keyboard driver

******************************************************************************/

#include "emu.h"
#include "bus/technics/kn5000/hdae5000.h"
#include "cpu/tlcs900/tmp94c241.h"
#include "imagedev/floppy.h"
#include "machine/gen_latch.h"
#include "machine/upd765.h"
#include "video/pc_vga.h"
#include "screen.h"
#include "kn5000.lh"

class mn89304_vga_device : public svga_device
{
public:
	// construction/destruction
	mn89304_vga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

protected:
	virtual void device_reset() override ATTR_COLD;

	virtual void palette_update() override;
	virtual void recompute_params() override;
	virtual uint16_t offset() override;
};

DEFINE_DEVICE_TYPE(MN89304_VGA, mn89304_vga_device, "mn89304_vga", "MN89304 VGA")

// TODO: nothing is known about this, configured out of usage in here for now.
mn89304_vga_device::mn89304_vga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: svga_device(mconfig, MN89304_VGA, tag, owner, clock)
{
	// ...
}

void mn89304_vga_device::device_reset()
{
	svga_device::device_reset();
	svga.rgb8_en = 1;
}

// sets up mode 0, by default it will throw 155 Hz, assume divided by 3
void mn89304_vga_device::recompute_params()
{
	u8 xtal_select = (vga.miscellaneous_output & 0x0c) >> 2;
	int xtal;

	switch(xtal_select & 3)
	{
		case 0: xtal = XTAL(25'174'800).value() / 3; break;
		case 1: xtal = XTAL(28'636'363).value() / 3; break;
		case 2:
		default:
			throw emu_fatalerror("MN89304: setup ext. clock select");
	}

	recompute_params_clock(1, xtal);
}


void mn89304_vga_device::palette_update()
{
	// 4bpp RAMDAC
	for (int i = 0; i < 256; i++)
	{
		set_pen_color(
			i,
			pal4bit(vga.dac.color[3*(i & vga.dac.mask) + 0]),
			pal4bit(vga.dac.color[3*(i & vga.dac.mask) + 1]),
			pal4bit(vga.dac.color[3*(i & vga.dac.mask) + 2])
		);
	}
}

uint16_t mn89304_vga_device::offset()
{
	return svga_device::offset() << 3;
}


namespace {

class kn5000_state : public driver_device
{
public:
	kn5000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_subcpu(*this, "subcpu")
		, m_maincpu_latch(*this, "maincpu_latch")
		, m_subcpu_latch(*this, "subcpu_latch")
		, m_fdc(*this, "fdc")
		, m_com_select(*this, "COM_SELECT")
		, m_extension(*this, "extension")
		, m_CPL_SEG(*this, "CPL_SEG%u", 0U)
		, m_CPR_SEG(*this, "CPR_SEG%u", 0U)
		, m_checking_device_led_cn11(*this, "checking_device_led_cn11")
		, m_checking_device_led_cn12(*this, "checking_device_led_cn12")
		, m_CPL_LED(*this, "CPL_%u", 0U)
		, m_CPR_LED(*this, "CPR_%u", 0U)
		, m_led_row(0)
		, m_mstat(0)
		, m_sstat(0)
	{ }

	void kn5000(machine_config &config);

private:
	required_device<tmp94c241_device> m_maincpu;
	required_device<tmp94c241_device> m_subcpu;
	required_device<generic_latch_8_device> m_maincpu_latch;
	required_device<generic_latch_8_device> m_subcpu_latch;
	required_device<upd72067_device> m_fdc;
	required_ioport m_com_select;
	required_device<kn5000_extension_connector> m_extension;

	required_ioport_array<11> m_CPL_SEG; // buttons on "Control Panel Left" PCB
	required_ioport_array<11> m_CPR_SEG; // buttons on "Control Panel Right" PCB
	output_finder<> m_checking_device_led_cn11;
	output_finder<> m_checking_device_led_cn12;
	output_finder<50> m_CPL_LED;
	output_finder<69> m_CPR_LED;
	uint8_t m_led_row;
	uint8_t m_mstat;
	uint8_t m_sstat;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint8_t cpanel_left_buttons_r(offs_t offset);
	uint8_t cpanel_right_buttons_r(offs_t offset);
	void cpanel_leds_w(offs_t offset, uint8_t data);

	void maincpu_mem(address_map &map) ATTR_COLD;
	void subcpu_mem(address_map &map) ATTR_COLD;
};

void kn5000_state::maincpu_mem(address_map &map)
{
	map(0x000000, 0x0fffff).ram(); // 1Mbyte = 2 * 4Mbit DRAMs @ IC9, IC10 (CS3)
	map(0x008e4a, 0x008e54).r(FUNC(kn5000_state::cpanel_right_buttons_r));
	map(0x008e5a, 0x008e64).r(FUNC(kn5000_state::cpanel_left_buttons_r));
	map(0x008f38, 0x008f39).w(FUNC(kn5000_state::cpanel_leds_w));
	//FIXME: map(0x110000, 0x11ffff).m(m_fdc, FUNC(upd765a_device::map)); // Floppy Controller @ IC208
	//FIXME: map(0x120000, 0x12ffff).w(m_fdc, FUNC(upd765a_device::dack_w)); // Floppy DMA Acknowledge
	map(0x140000, 0x14ffff).r(m_maincpu_latch, FUNC(generic_latch_8_device::read)); // @ IC23
	map(0x140000, 0x14ffff).w(m_subcpu_latch, FUNC(generic_latch_8_device::write)); // @ IC22
	map(0x1703b0, 0x1703df).m("vga", FUNC(mn89304_vga_device::io_map)); // LCD controller @ IC206
	map(0x1a0000, 0x1bffff).rw("vga", FUNC(mn89304_vga_device::mem_linear_r), FUNC(mn89304_vga_device::mem_linear_w));
	map(0x1e0000, 0x1fffff).ram(); // 1Mbit SRAM @ IC21 (CS0)  Note: I think this is the message "ERROR in back-up SRAM"
	map(0x300000, 0x3fffff).rom().region("custom_data", 0); // 8MBit FLASH ROM @ IC19 (CS5)
	map(0x400000, 0x7fffff).rom().region("rhythm_data", 0); // 32MBit ROM @ IC14 (A22=1 and CS5)
	//map(0x800000, 0x82ffff).rom().region("subprogram", 0); // not sure yet in which chip this is stored, but I suspect it should be IC19
	map(0x800000, 0x9fffff).mirror(0x200000).rom().region("table_data", 0); //2 * 8MBit ROMs @ IC1, IC3 (CS2)
	map(0xe00000, 0xffffff).mask(0x1fffff).rom().region("program", 0); //2 * 8MBit FLASH ROMs @ IC4, IC6
}

void kn5000_state::subcpu_mem(address_map &map)
{
	// There seems to also be devices at 110000, 130000 and 1e0000

	map(0x000000, 0x0fffff).ram(); // 1Mbyte = 2 * 4Mbit DRAMs @ IC28, IC29
	//map(0x110000, 0x11????).rw(FUNC(kn5000_state::tone_generator_r), FUNC(kn5000_state::tone_generator_w)); // @ IC303
	map(0x120000, 0x12ffff).r(m_subcpu_latch, FUNC(generic_latch_8_device::read)); // @ IC22
	map(0x120000, 0x12ffff).w(m_maincpu_latch, FUNC(generic_latch_8_device::write)); // @ IC23
	//map(0x130000, 0x13????).rw(FUNC(kn5000_state::dsp1_r), FUNC(kn5000_state::dsp1_w)); // @ IC311
	map(0xfe0000, 0xffffff).rom().region("subcpu", 0); // 1Mbit MASK ROM @ IC30

	//Note:
	// DSP2 @ IC302 uses a serial #0 pins but I think it is bitbanging those pins.
}

static void kn5000_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
}

static INPUT_PORTS_START(kn5000)
	PORT_START("CN11")
	PORT_DIPNAME(0x01, 0x01, "Main CPU Checking Device")
	PORT_DIPSETTING(   0x00, DEF_STR(On))
	PORT_DIPSETTING(   0x01, DEF_STR(Off))

	PORT_START("CN12")
	PORT_DIPNAME(0x01, 0x01, "Sub CPU Checking Device")
	PORT_DIPSETTING(   0x00, DEF_STR(On))
	PORT_DIPSETTING(   0x01, DEF_STR(Off))

	PORT_START("COM_SELECT")
	PORT_DIPNAME(0xf0, 0xe0, "Computer Interface Selection")
	PORT_DIPSETTING(   0xe0, "MIDI")
	PORT_DIPSETTING(   0xd0, "PC1")
	PORT_DIPSETTING(   0xb0, "PC2")
	PORT_DIPSETTING(   0x70, "Mac")

	PORT_START("AREA")
	PORT_DIPNAME(0x06, 0x06, "Area Selection")
	PORT_DIPSETTING(   0x02, "Thailand, Indonesia, Iran, U.A.E., Panama, Argentina, Peru, Brazil")
	PORT_DIPSETTING(   0x04, "USA, Mexico")
	PORT_DIPSETTING(   0x06, "Other")

/*
    Actual full list of regions (but it is unclear if there's any
    other hardware difference among them):

    PORT_DIPSETTING(   0x04, "(M): U.S.A.")
    PORT_DIPSETTING(   0x06, "(MC): Canada")
    PORT_DIPSETTING(   0x04, "(XM): Mexico")
    PORT_DIPSETTING(   0x06, "(EN): Norway, Sweden, Denmark, Finland")
    PORT_DIPSETTING(   0x06, "(EH): Holland, Belgium")
    PORT_DIPSETTING(   0x06, "(EF): France, Italy")
    PORT_DIPSETTING(   0x06, "(EZ): Germany")
    PORT_DIPSETTING(   0x06, "(EW): Switzerland")
    PORT_DIPSETTING(   0x06, "(EA): Austria")
    PORT_DIPSETTING(   0x06, "(EP): Spain, Portugal, Greece, South Africa")
    PORT_DIPSETTING(   0x06, "(EK): United Kingdom")
    PORT_DIPSETTING(   0x06, "(XL): New Zealand")
    PORT_DIPSETTING(   0x06, "(XR): Australia")
    PORT_DIPSETTING(   0x06, "(XS): Malaysia")
    PORT_DIPSETTING(   0x06, "(MD): Saudi Arabia, Hong Kong, Kuwait")
    PORT_DIPSETTING(   0x06, "(XT): Taiwan")
    PORT_DIPSETTING(   0x02, "(X): Thailand, Indonesia, Iran, U.A.E., Panama, Argentina, Peru, Brazil")
    PORT_DIPSETTING(   0x06, "(XP): Philippines")
    PORT_DIPSETTING(   0x06, "(XW): Singapore")
*/

	PORT_START("CPR_SEG0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("TRANSPOSE -")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("TRANSPOSE +")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("CPR_SEG1")  // SOUND GROUP
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("ORGAN & ACCORDION")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("ORCHESTRAL PAD")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SYNTH")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("BASS")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DIGITAL DRAWBAR")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("ACCORDION REGISTER")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("GM SPECIAL")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DRUM KITS")

	PORT_START("CPR_SEG2")  // SOUND GROUP
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PIANO")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("GUITAR")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("STRINGS & VOCAL")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("BRASS")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("FLUTE")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SAX & REED")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MALLET & ORCH PERC")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("WORLD PERC")

	PORT_START("CPR_SEG3")  // EFFECT
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SUSTAIN")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DIGITAL EFFECT")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DSP EFFECT")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DIGITAL REVERB")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("ACOUSTIC ILLUSION")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("CPR_SEG4")  // PART SELECT
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("LEFT")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RIGHT 2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RIGHT 1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("ENTERTAINER")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CONDUCTOR: LEFT")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CONDUCTOR: RIGHT 2")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CONDUCTOR: RIGHT 1")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("TECHNI CHORD")

	PORT_START("CPR_SEG5")  // SEQUENCER
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SEQUENCER: PLAY")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SEQUENCER: EASY REC")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SEQUENCER: MENU")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("CPR_SEG6")  // PANEL MEMORY
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PM 1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PM 2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PM 3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PM 4")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PM 5")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PM 6")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PM 7")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PM 8")

	PORT_START("CPR_SEG7")  // PANEL MEMORY
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PANEL MEMORY: SET")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PANEL MEMORY: NEXT BANK")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PANEL MEMORY: BANK VIEW")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("CPR_SEG8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("R1/R2 OCTAVE -")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("R1/R2 OCTAVE +")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("START/STOP")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SYNCHRO & BREAK")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("TAP TEMPO")

	PORT_START("CPR_SEG9")  // SOUND GROUP
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MEMORY A")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MEMORY B")

	PORT_START("CPR_SEG10")  // MENU
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MENU: SOUND")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MENU: CONTROL")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MENU: MIDI")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MENU: DISK")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("CPL_SEG0")  // RHYTHM GROUP
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("STANDARD ROCK")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("R & ROLL & BLUES")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("POP & BALLAD")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("FUNK & FUSION")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SOUL & MODERN DANCE")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("BIG BAND & SWING")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("JAZZ COMBO")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("CPL_SEG1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MEMORY") // Composer
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MENU") // Composer
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SET") // Sound Arranger
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("ON/OFF") // Sound Arranger
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MUSIC STYLIST")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("FADE IN")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("FADE OUT")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("CPL_SEG2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("FILL IN 1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("FILL IN 2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("INTRO & ENDING 1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("INTRO & ENDING 2")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PAGE DOWN")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PAGE UP")

	PORT_START("CPL_SEG3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DEMO")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MSP BANK")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MSP MENU")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MSP STOP/RECORD")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("CPL_SEG4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("VARIATION 1") // VARIATION & MSA
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("VARIATION 2") // VARIATION & MSA
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("VARIATION 3") // VARIATION & MSA
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("VARIATION 4") // VARIATION & MSA
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MUSIC STYLE ARRANGER")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SPLIT POINT")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("AUTO PLAY CHORD")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("CPL_SEG5")  // MANUAL SEQUENCE PADS
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MSP 1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MSP 2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MSP 3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MSP 4")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MSP 5")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MSP 6")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("CPL_SEG6")  // RHYTHM GROUP
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("U.S. TRAD")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("COUNTRY")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("LATIN")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MARCH & WALTZ")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PARTY TIME")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SHOWTIME & TRAD DANCE")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("WORLD")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CUSTOM")

	PORT_START("CPL_SEG7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RIGHT 5")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RIGHT 4")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DISPLAY HOLD")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("EXIT")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DOWN 7")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("UP 7")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DOWN 8")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("UP 8")

	PORT_START("CPL_SEG8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RIGHT 3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RIGHT 2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RIGHT 1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DOWN 5")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("UP 5")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DOWN 6")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("UP 6")

	PORT_START("CPL_SEG9")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("LEFT 5")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("LEFT 4")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("LEFT 3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DOWN 3")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("UP 3")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DOWN 4")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("UP 4")

	PORT_START("CPL_SEG10")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("LEFT 2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("LEFT 1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("HELP")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("OTHER PARTS/TR")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DOWN 1")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("UP 1")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DOWN 2")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("UP 2")
INPUT_PORTS_END


uint8_t kn5000_state::cpanel_left_buttons_r(offs_t offset)
{
	return m_CPL_SEG[offset]->read();
}

uint8_t kn5000_state::cpanel_right_buttons_r(offs_t offset)
{
	return m_CPR_SEG[offset]->read();
}


void kn5000_state::cpanel_leds_w(offs_t offset, uint8_t data)
{
	if ((offset & 1) == 0)
		m_led_row = data;

	if ((offset & 1) == 1)
	{
		switch (m_led_row)
		{
			case 0x00:
				m_CPR_LED[1] = BIT(data, 0); // D101 - EFFECT: SUSTAIN
				m_CPR_LED[2] = BIT(data, 1); // D102 - EFFECT: DIGITAL EFFECT
				m_CPR_LED[3] = BIT(data, 2); // D103 - EFFECT: DSP EFFECT
				m_CPR_LED[4] = BIT(data, 3); // D104 - EFFECT: DIGITAL REVERB
				m_CPR_LED[5] = BIT(data, 4); // D105 - EFFECT: ACCOUSTIC ILLUSION
				m_CPR_LED[6] = BIT(data, 5); // D106 - SEQUENCER: PLAY
				m_CPR_LED[7] = BIT(data, 6); // D107 - SEQUENCER: EASY REC
				m_CPR_LED[8] = BIT(data, 7); // D108 - SEQUENCER: MENU
				break;

			case 0x01:
				m_CPR_LED[9] = BIT(data, 0); // D109 - PIANO
				m_CPR_LED[10] = BIT(data, 1); // D110 - GUITAR
				m_CPR_LED[11] = BIT(data, 2); // D111 - STRINGS & VOCAL
				m_CPR_LED[12] = BIT(data, 3); // D112 - BRASS
				m_CPR_LED[13] = BIT(data, 4); // D113 - FLUTE
				m_CPR_LED[14] = BIT(data, 5); // D114 - SAX & REED
				m_CPR_LED[15] = BIT(data, 6); // D115 - MALLET & ORCH PERC
				m_CPR_LED[16] = BIT(data, 7); // D116 - WORLD PERC
				break;

			case 0x02:
				m_CPR_LED[17] = BIT(data, 0); // D117 - ORGAN & ACCORDION
				m_CPR_LED[18] = BIT(data, 1); // D118 - ORCHESTRAL PAD
				m_CPR_LED[19] = BIT(data, 2); // D119 - SYNTH
				m_CPR_LED[20] = BIT(data, 3); // D120 - BASS
				m_CPR_LED[21] = BIT(data, 4); // D121 - DIGITAL DRAWBAR
				m_CPR_LED[22] = BIT(data, 5); // D122 - ACCORDION REGISTER
				m_CPR_LED[23] = BIT(data, 6); // D123 - GM SPECIAL
				m_CPR_LED[24] = BIT(data, 7); // D124 - DRUM KITS
				break;

			case 0x03:
				m_CPR_LED[25] = BIT(data, 0); // D125 - PANEL MEMORY 1
				m_CPR_LED[26] = BIT(data, 1); // D126 - PANEL MEMORY 2
				m_CPR_LED[27] = BIT(data, 2); // D127 - PANEL MEMORY 3
				m_CPR_LED[28] = BIT(data, 3); // D128 - PANEL MEMORY 4
				m_CPR_LED[29] = BIT(data, 4); // D129 - PANEL MEMORY 5
				m_CPR_LED[30] = BIT(data, 5); // D130 - PANEL MEMORY 6
				m_CPR_LED[31] = BIT(data, 6); // D131 - PANEL MEMORY 7
				m_CPR_LED[32] = BIT(data, 7); // D132 - PANEL MEMORY 8
				break;

			case 0x04:
				m_CPR_LED[33] = BIT(data, 0); // D133 - PART SELECT: LEFT
				m_CPR_LED[34] = BIT(data, 1); // D134 - PART SELECT: RIGHT 2
				m_CPR_LED[35] = BIT(data, 2); // D135 - PART SELECT: RIGHT 1
				m_CPR_LED[36] = BIT(data, 3); // D136 - ENTERTAINER
				m_CPR_LED[37] = BIT(data, 4); // D137 - CONDUCTOR: LEFT
				m_CPR_LED[38] = BIT(data, 5); // D138 - CONDUCTOR: RIGHT 2
				m_CPR_LED[39] = BIT(data, 6); // D139 - CONDUCTOR: RIGHT 1
				m_CPR_LED[40] = BIT(data, 7); // D140 - TECHNI CHORD
				break;

			case 0x08:
				m_CPR_LED[49] = BIT(data, 0); // D149 - MENU: SOUND
				m_CPR_LED[50] = BIT(data, 1); // D150 - MENU: CONTROL
				m_CPR_LED[51] = BIT(data, 2); // D151 - MENU: MIDI
				m_CPR_LED[52] = BIT(data, 3); // D152 - MENU: DISK
				break;

			case 0x0a:
				m_CPR_LED[57] = BIT(data, 0); // D157 - MEMORY A
				m_CPR_LED[58] = BIT(data, 1); // D158 - MEMORY B
				break;

			case 0x0b:
				m_CPR_LED[61] = BIT(data, 0); // D161 - SYNCHRO & BREAK
				m_CPR_LED[62] = BIT(data, 1); // D162 - R1/R2 OCTAVE MINUS
				m_CPR_LED[63] = BIT(data, 2); // D163 - R1/R2 OCTAVE PLUS
				m_CPR_LED[64] = BIT(data, 3); // D164 - BANK VIEW
				break;

			case 0x0c:
				m_CPR_LED[65] = BIT(data, 0); // D165 - START/STOP 1 BEAT
				m_CPR_LED[66] = BIT(data, 1); // D166 - START/STOP 2 BEAT
				m_CPR_LED[67] = BIT(data, 2); // D167 - START/STOP 3 BEAT
				m_CPR_LED[68] = BIT(data, 3); // D168 - START/STOP 4 BEAT
				break;

			case 0xc0:
				m_CPL_LED[1] = BIT(data, 0); // D101 - COMPOSER: MEMORY
				m_CPL_LED[2] = BIT(data, 1); // D102 - COMPOSER: MENU
				m_CPL_LED[3] = BIT(data, 2); // D103 - SOUND ARRANGER: SET
				m_CPL_LED[4] = BIT(data, 3); // D104 - SOUND ARRANGER: ON/OFF
				m_CPL_LED[5] = BIT(data, 4); // D105 - MUSIC STYLIST
				m_CPL_LED[6] = BIT(data, 5); // D106 - FADE IN
				m_CPL_LED[7] = BIT(data, 6); // D107 - FADE OUT
				m_CPL_LED[8] = BIT(data, 7); // D108 - DISPLAY HOLD
				break;

			case 0xc1:
				m_CPL_LED[9] = BIT(data, 0); // D109 - U.S. TRAD
				m_CPL_LED[10] = BIT(data, 1); // D110 - COUNTRY
				m_CPL_LED[11] = BIT(data, 2); // D111 - LATIN
				m_CPL_LED[12] = BIT(data, 3); // D112 - MARCH & WALTZ
				m_CPL_LED[13] = BIT(data, 4); // D113 - PARTY TIME
				m_CPL_LED[14] = BIT(data, 5); // D114 - SHOW TIME & TRAD DANCE
				m_CPL_LED[15] = BIT(data, 6); // D115 - WORLD
				m_CPL_LED[16] = BIT(data, 7); // D116 - CUSTOM
				break;

			case 0xc2:
				m_CPL_LED[17] = BIT(data, 0); // D117 - STANDARD ROCK
				m_CPL_LED[18] = BIT(data, 1); // D118 - R & ROLL & BLUES
				m_CPL_LED[19] = BIT(data, 2); // D119 - POP & BALLAD
				m_CPL_LED[20] = BIT(data, 3); // D120 - FUNK & FUSION
				m_CPL_LED[21] = BIT(data, 4); // D121 - SOUL & MODERN DANCE
				m_CPL_LED[22] = BIT(data, 5); // D122 - BIG BAND & SWING
				m_CPL_LED[23] = BIT(data, 6); // D123 - JAZZ COMBO
				m_CPL_LED[24] = BIT(data, 7); // D124 - MANUAL SEQUENCE PADS: MENU
				break;

			case 0xc3:
				m_CPL_LED[25] = BIT(data, 0); // D125 - VARIATION & MSA 1
				m_CPL_LED[26] = BIT(data, 1); // D126 - VARIATION & MSA 2
				m_CPL_LED[27] = BIT(data, 2); // D127 - VARIATION & MSA 3
				m_CPL_LED[28] = BIT(data, 3); // D128 - VARIATION & MSA 4
				m_CPL_LED[29] = BIT(data, 4); // D129 - MUSIC STYLE ARRANGER
				m_CPL_LED[30] = BIT(data, 5); // D130 - AUTO PLAY CHORD
				break;

			case 0xc4:
				m_CPL_LED[33] = BIT(data, 0); // D133 - FILL IN 1
				m_CPL_LED[34] = BIT(data, 1); // D134 - FILL IN 2
				m_CPL_LED[35] = BIT(data, 2); // D135 - INTRO & ENDING 1
				m_CPL_LED[36] = BIT(data, 3); // D136 - INTRO & ENDING 2
				m_CPL_LED[37] = BIT(data, 4); // D137 - SPLIT POINT INDICATOR (LEFT)
				m_CPL_LED[38] = BIT(data, 5); // D138 - SPLIT POINT INDICATOR (CENTER)
				m_CPL_LED[39] = BIT(data, 6); // D139 - SPLIT POINT INDICATOR (RIGHT)
				m_CPL_LED[40] = BIT(data, 7); // D140 - TEMPO/PROGRAM
				break;

			case 0xc8:
				m_CPL_LED[49] = BIT(data, 0); // D149 - OTHER PARTS/TR
				break;

			case 0xff:
				break;
		}
	}
	return;
}

void kn5000_state::machine_start()
{
	save_item(NAME(m_mstat));
	save_item(NAME(m_sstat));

	m_extension->program_map(m_maincpu->space(AS_PROGRAM));

	m_checking_device_led_cn11.resolve();
	m_checking_device_led_cn12.resolve();
	m_CPL_LED.resolve();
	m_CPR_LED.resolve();
}

void kn5000_state::machine_reset()
{
	m_checking_device_led_cn11 = 0;
	m_checking_device_led_cn12 = 0;
}

void kn5000_state::kn5000(machine_config &config)
{
	// Note: The CPU has an internal clock doubler
	TMP94C241(config, m_maincpu, 2 * 8_MHz_XTAL); // TMP94C241F @ IC5
	// Address bus is set to 32 bits by the pins AM1=+5v and AM0=GND
	m_maincpu->set_addrmap(AS_PROGRAM, &kn5000_state::maincpu_mem);
	// Interrupt 4: FDCINT
	// Interrupt 5: FDCIRQ
	// Interrupt 6: FDC.H/D
	// Interrupt 7: FDC.I/O
	// Interrupt 9: HDDINT
	// Interrupt A <edge>: ~CPSCK "Control Panel Serial Clock"
	// ~NMI: SNS
	// TC0: FDCTC


	// MAINCPU PORT 7:
	//   bit 5 (~BUSRQ pin): RY/~BY pin of maincpu ROMs
	m_maincpu->port7_read().set_constant(1 << 5); // checked at EF3735 (v10 ROM)


	// MAINCPU PORT 8:
	//   bit 6 (~WAIT pin) (input): Something involving VGA.RDY, FDC.DMAACK
	//                              and shift-register @ IC18


	// MAINCPU PORT A:
	//   bit 0 (output) = sub_cpu ~RESET / SRST
	m_maincpu->porta_write().set([this] (u8 data) {
		m_subcpu->set_input_line(INPUT_LINE_RESET, BIT(data, 0) ? CLEAR_LINE : ASSERT_LINE);
	});

	// MAINCPU PORT C:
	//   bit 0 (input) = "check terminal" switch
	//   bit 1 (output) = "check terminal" LED
	m_maincpu->portc_read().set_ioport("CN11");
	m_maincpu->portc_write().set([this] (u8 data) {
		m_checking_device_led_cn11 = (BIT(data, 1) == 0);
	});


	// MAINCPU PORT D:
	//   bit 0 (output) = FDCRST
	//   bit 6 (input) = FD.I/O
	m_maincpu->portd_write().set(m_fdc, FUNC(upd72067_device::reset_w)).bit(0);
	// TODO: bit 6!


	// MAINCPU PORT E:
	//   bit 0 (input) = +5v
	//   bit 2 (input) = HDDRDY
	//   bit 4 (?) = MICSNS
	m_maincpu->porte_read().set_constant(1); //checked at EF05A6 (v10 ROM)
	// FIXME: Bit 0 should only be 1 if the
	// optional hard-drive extension board is disabled;


	// MAINCPU PORT F:
	//   bit 2 (OUTPUT) = Something related to "RESET CONTROL" circuits?


	// MAINCPU PORT G:
	//   bit 2 (input) = FS1  (Foot Switches and Foot Controler ?)
	//   bit 3 (input) = FS2
	//   bit 4 (input) = FC1
	//   bit 5 (input) = FC2
	//   bit 6 (input) = FC3
	//   bit 7 (input) = FC4


	// MAINCPU PORT H:
	m_maincpu->porth_read().set_ioport("AREA"); // checked at EF083E (v10 ROM)


	// MAINCPU PORT Z:
	//   bit 0 = (output) MSTAT0
	//   bit 1 = (output) MSTAT1
	//   bit 2 = (input) SSTAT0
	//   bit 3 = (input) SSTAT1
	//   bit 4 = (input) COM.PC2
	//   bit 5 = (input) COM.PC1
	//   bit 6 = (input) COM.MAC
	//   bit 7 = (input) COM.MIDI
	m_maincpu->portz_read().set([this] {
		return m_com_select->read() | (m_sstat << 2);
	});
	m_maincpu->portz_write().set([this] (u8 data) {
		m_mstat = data & 3;
	});


	// RX0/TX0 = MRXD/MTXD
	// RX1/TX1 = CPDATA
	// SCLK1   = CPSCK

	// AN0 = EXP (expression pedal?)
	// AN1 = AFT

	// Note: The CPU has an internal clock doubler
	TMP94C241(config, m_subcpu, 2*10_MHz_XTAL); // TMP94C241F @ IC27
	// Address bus is set to 8 bits by the pins AM1=GND and AM0=GND
	m_subcpu->set_addrmap(AS_PROGRAM, &kn5000_state::subcpu_mem);

	// SUBCPU PORT C:
	//   bit 0 (input) = "check terminal" switch
	//   bit 1 (output) = "check terminal" LED
	m_subcpu->portc_read().set_ioport("CN12");
	m_subcpu->portc_write().set([this] (u8 data) {
		m_checking_device_led_cn12 = (BIT(data, 1) == 0);
	});


	// SUBCPU PORT D:
	//   bit 0 = (output) SSTAT0
	//   bit 1 = (output) SSTAT1
	//   bit 2 = (input) MSTAT0
	//   bit 3 (not used)
	//   bit 4 = (input) MSTAT1
	m_subcpu->portd_read().set([this] {
		return (BIT(m_mstat, 0) << 2) | (BIT(m_mstat, 1) << 4);
	});
	m_subcpu->portd_write().set([this] (u8 data) {
		m_sstat = data & 3;
	});


	GENERIC_LATCH_8(config, m_maincpu_latch); // @ IC23
	m_maincpu_latch->data_pending_callback().set_inputline(m_maincpu, TLCS900_INT0);

	GENERIC_LATCH_8(config, m_subcpu_latch); //  @ IC22
	m_subcpu_latch->data_pending_callback().set_inputline(m_subcpu, TLCS900_INT0);

	UPD72067(config, m_fdc, 32'000'000); // actual controller is UPD72068GF-3B9 at IC208
	m_fdc->intrq_wr_callback().set_inputline(m_maincpu, TLCS900_INT4);
	m_fdc->drq_wr_callback().set_inputline(m_maincpu, TLCS900_INT5);
	m_fdc->hdl_wr_callback().set_inputline(m_maincpu, TLCS900_INT6);
	//m_fdc->??_wr_callback().set_inputline(m_maincpu, TLCS900_INT7);
	//FIXME:
	// Interrupt 4: FDCINT
	// Interrupt 5: FDCIRQ
	// Interrupt 6: FDC.H/D
	// Interrupt 7: FDC.I/O

	FLOPPY_CONNECTOR(config, "fdc:0", kn5000_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	/* Extension port */
	KN5000_EXTENSION(config, m_extension, kn5000_extension_intf, nullptr);
	m_extension->irq_callback().set_inputline(m_maincpu, TLCS900_INT9);

	/* video hardware */
	// LCD Controller MN89304 @ IC206 24_MHz_XTAL
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_raw(XTAL(40'000'000)/6, 424, 0, 320, 262, 0, 240);
	screen.set_screen_update("vga", FUNC(mn89304_vga_device::screen_update));

	mn89304_vga_device &vga(MN89304_VGA(config, "vga", 0));
	vga.set_screen("screen");
	vga.set_vram_size(0x100000);

	config.set_default_layout(layout_kn5000);
}

ROM_START(kn5000)
	ROM_DEFAULT_BIOS("v10")
	ROM_SYSTEM_BIOS(0, "v10", "Version 10 - August 2nd, 1999")
	ROM_SYSTEM_BIOS(1, "v9", "Version 9 - January 26th, 1999")
	ROM_SYSTEM_BIOS(2, "v8", "Version 8 - November 13th, 1998")
	ROM_SYSTEM_BIOS(3, "v7", "Version 7 - June 26th, 1998")
	ROM_SYSTEM_BIOS(4, "v6", "Version 6 - January 16th, 1998") // sometimes refered to as "update6v0"
	ROM_SYSTEM_BIOS(5, "v5", "Version 5 - November 12th, 1997") // sometimes refered to as "update5v0"
	ROM_SYSTEM_BIOS(6, "v4", "Version 4") // I have a v4 board but haven't dumped it yet
	ROM_SYSTEM_BIOS(7, "v3", "Version 3") // I have a v3 board but haven't dumped it yet

	ROM_REGION16_LE(0x200000, "program" , 0) // main cpu

	// FIXME: These are actually stored in a couple flash rom chips IC6 (even) and IC4 (odd)
	//
	// Note: These ROMs from v5 to v10 were extracted from the system update floppies
	//       which were compressed using LZSS.
	//
	//       System update disks for older versions were not found yet, so dumping
	//       efforts will require other methods.
	//
	//       More info at:
	//       https://github.com/felipesanches/kn5000_homebrew/blob/main/kn5000_extract.py

	ROMX_LOAD("kn5000_v10_program.rom", 0x00000, 0x200000, CRC(00303406) SHA1(1f2abc5b1b7b9e16fdf796f26d939edaceded354), ROM_BIOS(0))
	ROMX_LOAD("kn5000_v9_program.rom",  0x00000, 0x200000, CRC(c791d765) SHA1(d9a3b462b1f9302402e8d37aacd15f069f56abd9), ROM_BIOS(1))
	ROMX_LOAD("kn5000_v8_program.rom",  0x00000, 0x200000, CRC(46b4b242) SHA1(a10a6f5a35175b74c3cfb42cef3bdf571c2858bb), ROM_BIOS(2))
	ROMX_LOAD("kn5000_v7_program.rom",  0x00000, 0x200000, CRC(a5a25eb0) SHA1(4c682cb248034a2de04c688b0a45654b8726bffb), ROM_BIOS(3))
	ROMX_LOAD("kn5000_v6_program.rom",  0x00000, 0x200000, CRC(0205db30) SHA1(51108e2d75b180a034395e90bd40ca2bd2a0adfb), ROM_BIOS(4))
	ROMX_LOAD("kn5000_v5_program.rom",  0x00000, 0x200000, CRC(fbd035e3) SHA1(7b69a8aaa84ee3d337acc0c29c34154c5da2df32), ROM_BIOS(5))
	ROMX_LOAD("kn5000_v4_program.rom",  0x00000, 0x200000, NO_DUMP, ROM_BIOS(6))
	ROMX_LOAD("kn5000_v3_program.rom",  0x00000, 0x200000, NO_DUMP, ROM_BIOS(7))

	// Note: I've never seen boards with versions 1 or 2.

	// Note: Even though this "subprogram" address range contain executable code for the subcpu, it is actually loaded by the maincpu
	//       from a flash rom and then transfered to the subcpu RAM via the inter-cpu communications latches at some point during boot.
	ROM_REGION16_LE(0x30000, "subprogram", 0)
	ROMX_LOAD("kn5000_subprogram_v142.rom", 0x000000, 0x030000, CRC(fe3b640a) SHA1(5c3a2b9311318c19e1a29ca460dea693bcb2c405), ROM_BIOS(0)) // v10
	ROMX_LOAD("kn5000_subprogram_v142.rom", 0x000000, 0x030000, CRC(fe3b640a) SHA1(5c3a2b9311318c19e1a29ca460dea693bcb2c405), ROM_BIOS(1)) // v9
	ROMX_LOAD("kn5000_subprogram_v141.rom", 0x000000, 0x030000, CRC(4f6ea155) SHA1(39b0dd7b23abd3cdfedce65dd4fef0e2ab16ab69), ROM_BIOS(2)) // v8
	ROMX_LOAD("kn5000_subprogram_v141.rom", 0x000000, 0x030000, CRC(4f6ea155) SHA1(39b0dd7b23abd3cdfedce65dd4fef0e2ab16ab69), ROM_BIOS(3)) // v7
	ROMX_LOAD("kn5000_subprogram_v140.rom", 0x000000, 0x030000, CRC(d9a537aa) SHA1(b7f471522ab3125e5eb42c7368d57a56084ce32a), ROM_BIOS(4)) // v6
	ROMX_LOAD("kn5000_subprogram_v140.rom", 0x000000, 0x030000, CRC(d9a537aa) SHA1(b7f471522ab3125e5eb42c7368d57a56084ce32a), ROM_BIOS(5)) // v5
	ROMX_LOAD("kn5000_subprogram_v139.rom", 0x000000, 0x030000, NO_DUMP, ROM_BIOS(6)) // v4

	ROM_REGION16_LE(0x20000, "subcpu", 0)
	ROM_LOAD("kn5000_subcpu_boot.ic30", 0x00000, 0x20000, BAD_DUMP CRC(a45ceb77) SHA1(d29429a9a1ef7a718fa88c1aa38d0f7238ba5d94)) // Ranges fe0800-ff7800 and ff9800-fff000 not dumped yet. Assumed here as being filled with 0xFF.

	ROM_REGION16_LE(0x200000, "table_data", 0)
	ROM_LOAD32_WORD("kn5000_table_data_rom_even.ic3", 0x000000, 0x100000, CRC(b6f0becd) SHA1(1fd2604236b8d12ea7281fad64d72746eb00c525))
	ROM_LOAD32_WORD("kn5000_table_data_rom_odd.ic1",  0x000002, 0x100000, CRC(cd907eac) SHA1(bedf09d606d476f3e6d03e590709715304cf7ea5))

	ROM_REGION16_LE(0x100000, "custom_data", 0)
	ROM_LOAD("kn5000_custom_data_rom.ic19", 0x000000, 0x100000, CRC(5de11a6b) SHA1(4709f815d3d03ce749c51f4af78c62bf4a5e3d94))
	// IC19 is a flash ROM. The contents here were dumped from a system that had it already programmed by the initial data disk.
	// Maybe it could also be declared as NVRAM here?

	ROM_REGION16_LE(0x400000, "rhythm_data", 0)
	ROM_LOAD("kn5000_rhythm_data_rom.ic14", 0x000000, 0x400000, CRC(76d11a5e) SHA1(e4b572d318c9fe7ba00e5b44ea783e89da9c68bd))

	ROM_REGION16_LE(0x1000000, "waveform", 0)
	ROM_LOAD("kn5000_waveform_rom.ic304", 0x000000, 0x400000, NO_DUMP)
	ROM_LOAD("kn5000_waveform_rom.ic305", 0x400000, 0x400000, NO_DUMP)
	ROM_LOAD("kn5000_waveform_rom.ic306", 0x800000, 0x400000, NO_DUMP)
	ROM_LOAD("kn5000_waveform_rom.ic307", 0xc00000, 0x400000, CRC(20ff4629) SHA1(4b511bff6625f4655cabd96a263bf548d2ef4bf7))
ROM_END

} // anonymous namespace

//   YEAR  NAME   PARENT  COMPAT  MACHINE INPUT   STATE         INIT        COMPANY      FULLNAME             FLAGS
CONS(1998, kn5000,    0,       0, kn5000, kn5000, kn5000_state, empty_init, "Technics", "SX-KN5000", MACHINE_NOT_WORKING|MACHINE_NO_SOUND)



konin.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

Mera-Elzab Konin

It's an industrial computer used in Poland

No information has been found. All code is guesswork.

2011-12-29 Skeleton driver.
2016-07-15 Added terminal and uart.

Press E to see some messages.

Terminal settings: 8 data bits, 2 stop bits, no parity @ 9600

****************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8212.h"
#include "machine/i8214.h"
#include "machine/i8251.h"
#include "machine/pit8253.h"
#include "machine/i8255.h"
#include "bus/rs232/rs232.h"


namespace {

class konin_state : public driver_device
{
public:
	konin_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_picu(*this, "picu")
		, m_ioppi(*this, "ioppi")
		, m_iopit(*this, "iopit")
	{ }

	void konin(machine_config &config);

private:
	void picu_r3_w(int state);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_device<i8214_device> m_picu;
	required_device<i8255_device> m_ioppi;
	required_device<pit8253_device> m_iopit;
};

void konin_state::picu_r3_w(int state)
{
	m_picu->r_w(4, !state);
}

void konin_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x4fff).rom();
	map(0x5000, 0x7fff).ram();
	map(0xf200, 0xf200).nopw(); // watchdog?
	map(0xf400, 0xfbff).ram();
	map(0xfc80, 0xfc83).rw("mainppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xfc84, 0xfc87).rw("mainpit", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xff00, 0xffff).ram();
}

void konin_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x24, 0x24).w(m_picu, FUNC(i8214_device::b_w));
	map(0x80, 0x83).lrw8(
		NAME([this](offs_t offset) { return m_ioppi->read(offset^3); }),
		NAME([this](offs_t offset, u8 data) { m_ioppi->write(offset^3, data); }));
	map(0xf6, 0xf6).rw("uart", FUNC(i8251_device::status_r), FUNC(i8251_device::control_w));
	map(0xf7, 0xf7).rw("uart", FUNC(i8251_device::data_r), FUNC(i8251_device::data_w));
	map(0xf8, 0xfb).lrw8(
		NAME([this](offs_t offset) { return m_iopit->read(offset^3); }),
		NAME([this](offs_t offset, u8 data) { m_iopit->write(offset^3, data); }));
}

/* Input ports */
static INPUT_PORTS_START( konin )
INPUT_PORTS_END


void konin_state::machine_start()
{
}

void konin_state::konin(machine_config &config)
{
	/* basic machine hardware */
	i8080_cpu_device &maincpu(I8080(config, m_maincpu, XTAL(4'000'000)));
	maincpu.set_addrmap(AS_PROGRAM, &konin_state::mem_map);
	maincpu.set_addrmap(AS_IO, &konin_state::io_map);
	maincpu.out_inte_func().set(m_picu, FUNC(i8214_device::inte_w));
	maincpu.set_irq_acknowledge_callback("intlatch", FUNC(i8212_device::inta_cb));

	i8212_device &intlatch(I8212(config, "intlatch", 0));
	intlatch.md_rd_callback().set_constant(0);
	intlatch.di_rd_callback().set(m_picu, FUNC(i8214_device::vector_r));
	intlatch.int_wr_callback().set_inputline("maincpu", I8085_INTR_LINE);

	I8214(config, m_picu, XTAL(4'000'000));
	m_picu->int_wr_callback().set("intlatch", FUNC(i8212_device::stb_w));

	pit8253_device &mainpit(PIT8253(config, "mainpit", 0));
	// wild guess at UART clock and source
	mainpit.set_clk<0>(1536000);
	mainpit.out_handler<0>().set("uart", FUNC(i8251_device::write_txc));
	mainpit.out_handler<0>().append("uart", FUNC(i8251_device::write_rxc));

	I8255(config, "mainppi", 0);

	PIT8253(config, m_iopit, 0);

	I8255(config, m_ioppi, 0);

	i8251_device &uart(I8251(config, "uart", 0));
	uart.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	uart.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	uart.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	uart.rxrdy_handler().set(FUNC(konin_state::picu_r3_w));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("uart", FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set("uart", FUNC(i8251_device::write_dsr));
	rs232.cts_handler().set("uart", FUNC(i8251_device::write_cts));
}

/* ROM definition */
ROM_START( konin )
	ROM_REGION( 0x5000, "maincpu", 0 )
	ROM_LOAD( "001.bin", 0x0000, 0x0800, CRC(0b13208a) SHA1(38ea17be591b729158d601c03bfd9954f32e0e67))
	ROM_LOAD( "008.bin", 0x0800, 0x0800, CRC(f003e407) SHA1(11f79ef3b90788cf627ee39705bbbd04dbf45f50))
	ROM_LOAD( "007.bin", 0x1000, 0x0800, CRC(3d390c03) SHA1(ac2fe31c065e8f630381d6cebd2eb58b403c1e02))
	ROM_LOAD( "006.bin", 0x1800, 0x0800, CRC(68c9732e) SHA1(f40a79719dca485a2db29be5c0c781f559c2551c))
	ROM_LOAD( "005.bin", 0x2000, 0x0800, CRC(14548ac4) SHA1(8987e528b3e479c4c5941366628f34f086d06838))
	ROM_LOAD( "004.bin", 0x2800, 0x0800, CRC(8a354cff) SHA1(24d9f1fb15458fc96f5265f79d54e030b68d9fc9))
	ROM_LOAD( "002.bin", 0x3000, 0x0800, CRC(791fb30d) SHA1(8dfbe0edb741e02cfdd138432999f89480b20471))
	ROM_LOAD( "003.bin", 0x3800, 0x0800, CRC(27dc9864) SHA1(0d3da7fd1db895883c106f5133f8c7228333ecc8))
	ROM_LOAD( "009.bin", 0x4000, 0x0800, CRC(80947d15) SHA1(0757fb191913d79f306874684f9fc082ce18a28e))
	ROM_LOAD( "010.bin", 0x4800, 0x0800, CRC(f0157e0c) SHA1(60ace1eaf0ba01a45987c2286e18f3d56441c994))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY       FULLNAME  FLAGS
COMP( 198?, konin, 0,      0,      konin,   konin, konin_state, empty_init, "Mera-Elzab", "Konin",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



korgds8.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Korg DS-8 & 707 synthesizers.

****************************************************************************/

#include "emu.h"
#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
//#include "bus/midi/midi.h"
#include "cpu/upd7810/upd7810.h"
#include "machine/nvram.h"
#include "sound/ymopm.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

namespace {

class korg_ds8_state : public driver_device
{
public:
	korg_ds8_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_card(*this, "card")
		, m_kbd(*this, "KBD%u", 0U)
		, m_sw(*this, "SW%u", 0U)
		, m_scan(0)
		, m_card_bank(0)
	{
	}

	void ds8(machine_config &config);
	void korg707(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	HD44780_PIXEL_UPDATE(lcd_pixel_update);
	HD44780_PIXEL_UPDATE(korg707_pixel_update);

	u8 kbd_sw_r();
	void scan_w(u8 data);
	u8 card_r(offs_t offset);
	void card_w(offs_t offset, u8 data);
	void ddl_w(u8 data);
	void line_mute_w(u8 data);
	void led_data_w(offs_t offset, u8 data);

	void mem_map(address_map &map) ATTR_COLD;

	void palette_init_ds8(palette_device &palette);

	required_device<upd7810_device> m_maincpu;
	required_device<generic_slot_device> m_card;
	required_ioport_array<16> m_kbd;
	optional_ioport_array<8> m_sw;

	u8 m_scan;
	u16 m_card_bank;
};

void korg_ds8_state::machine_start()
{
	save_item(NAME(m_scan));
	save_item(NAME(m_card_bank));
}

HD44780_PIXEL_UPDATE(korg_ds8_state::lcd_pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 40)
		bitmap.pix(line * 8 + y, pos * 6 + x) = state;
}

HD44780_PIXEL_UPDATE(korg_ds8_state::korg707_pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 20)
		bitmap.pix(line * 8 + y, pos * 6 + x) = state;
}

u8 korg_ds8_state::kbd_sw_r()
{
	if (BIT(m_scan, 4))
		return m_sw[m_scan & 0x07].read_safe(0xff);
	else
		return m_kbd[m_scan & 0x0f]->read();
}

void korg_ds8_state::scan_w(u8 data)
{
	m_scan = data;
}

u8 korg_ds8_state::card_r(offs_t offset)
{
	return m_card->read_ram(offset | m_card_bank);
}

void korg_ds8_state::card_w(offs_t offset, u8 data)
{
	m_card->write_ram(offset | m_card_bank, data);
}

void korg_ds8_state::ddl_w(u8 data)
{
}

void korg_ds8_state::line_mute_w(u8 data)
{
	m_card_bank = (BIT(data, 7) ? 0x2000 : 0) | (BIT(data, 6) ? 0x4000 : 0);
}

void korg_ds8_state::led_data_w(offs_t offset, u8 data)
{
}

void korg_ds8_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("program", 0);
	map(0x8000, 0x9fff).rw(FUNC(korg_ds8_state::card_r), FUNC(korg_ds8_state::card_w));
	map(0xa000, 0xbfff).ram().share("nvram");
	map(0xc000, 0xc7ff).ram();
	map(0xe000, 0xe001).mirror(0x1e).rw("fm", FUNC(ym2164_device::read), FUNC(ym2164_device::write));
	map(0xe020, 0xe020).mirror(0x1f).w(FUNC(korg_ds8_state::ddl_w));
	map(0xe040, 0xe040).mirror(0x1f).w(FUNC(korg_ds8_state::line_mute_w));
	map(0xe060, 0xe060).mirror(0x1f).rw("lcdc", FUNC(hd44780_device::db_r), FUNC(hd44780_device::db_w));
	map(0xe080, 0xe081).mirror(0x1e).w(FUNC(korg_ds8_state::led_data_w));
}


static INPUT_PORTS_START(ds8)
	PORT_START("KBD0") // CN3A/B-1
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KBD1") // CN3A/B-2
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KBD2") // CN3A/B-3
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KBD3") // CN3A/B-4
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KBD4") // CN3A/B-5
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KBD5") // CN3A/B-6
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KBD6") // CN3A/B-7
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KBD7") // CN3A/B-8
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KBD8") // CN3A/B-9
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KBD9") // CN4A/B-1
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KBD10") // CN4A/B-2
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KBD11") // CN4A/B-3
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KBD12") // CN4A/B-4
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KBD13") // CN4A/B-5
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KBD14") // CN4A/B-6
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KBD15") // CN4A/B-7
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SW0") // CN5A/B-5
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SW1") // CN5A/B-5
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SW2") // CN5A/B-5
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SW3") // CN5A/B-5
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SW4") // CN5A/B-1
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)
INPUT_PORTS_END

void korg_ds8_state::palette_init_ds8(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(131, 136, 139));
	palette.set_pen_color(1, rgb_t( 92,  83,  88));
}

void korg_ds8_state::ds8(machine_config &config)
{
	UPD78C10(config, m_maincpu, 12_MHz_XTAL); // µPD78C10CW
	m_maincpu->set_addrmap(AS_PROGRAM, &korg_ds8_state::mem_map);
	m_maincpu->pa_in_cb().set(FUNC(korg_ds8_state::kbd_sw_r));
	m_maincpu->pb_out_cb().set(FUNC(korg_ds8_state::scan_w));
	m_maincpu->pc_out_cb().set("lcdc", FUNC(hd44780_device::rs_w)).bit(5);
	m_maincpu->pc_out_cb().append("lcdc", FUNC(hd44780_device::rw_w)).bit(6);
	m_maincpu->pc_out_cb().append("lcdc", FUNC(hd44780_device::e_w)).bit(7);
	m_maincpu->set_pc_pullups(0x0e);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // µPD4364 + battery

	GENERIC_CARTSLOT(config, m_card, generic_plain_slot, nullptr, "ds8_card");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6*40, 8*2);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(korg_ds8_state::palette_init_ds8), 2);

	hd44780_device &lcdc(HD44780(config, "lcdc", 270'000)); // TODO: clock not measured, datasheet typical clock used
	lcdc.set_lcd_size(2, 40);
	lcdc.set_pixel_update_cb(FUNC(korg_ds8_state::lcd_pixel_update));

	SPEAKER(config, "speaker", 2).front();

	ym2164_device &fm(YM2164(config, "fm", 3.579545_MHz_XTAL)); // YM2164 + YM3012
	fm.add_route(0, "speaker", 1.00, 0);
	fm.add_route(1, "speaker", 1.00, 1);
}

void korg_ds8_state::korg707(machine_config &config)
{
	ds8(config);

	screen_device &screen(*subdevice<screen_device>("screen"));
	screen.set_size(6*20, 8*2);
	screen.set_visarea_full();

	hd44780_device &lcdc(*subdevice<hd44780_device>("lcdc"));
	lcdc.set_lcd_size(2, 20);
	lcdc.set_pixel_update_cb(FUNC(korg_ds8_state::korg707_pixel_update));
}

ROM_START(ds8)
	ROM_REGION(0x8000, "program", 0)
	ROM_LOAD("870214.ic9", 0x0000, 0x8000, CRC(6b418e7c) SHA1(f16f9d87f1d424335a04ab36fef9386ed0b7b159))
ROM_END

ROM_START(korg707)
	ROM_REGION(0x8000, "program", 0)
	ROM_LOAD("870904.ic7", 0x0000, 0x8000, CRC(3eb80aae) SHA1(8574f48c9a1724c483ac8ee7c82ea46d1f583d6d)) // 27C256
ROM_END

} // anonymous namespace


SYST(1986, ds8,     0, 0, ds8,     ds8, korg_ds8_state, empty_init, "Korg", "DS-8 Digital Synthesizer",    MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1987, korg707, 0, 0, korg707, ds8, korg_ds8_state, empty_init, "Korg", "707 Performing Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



korgdss1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Korg DSS-1 synthesizer.

****************************************************************************/

#include "emu.h"
#include "bus/midi/midi.h"
#include "bus/nscsi/devices.h"
#include "cpu/i8085/i8085.h"
#include "cpu/m6800/m6801.h"
#include "cpu/nec/v5x.h"
#include "imagedev/floppy.h"
#include "machine/gen_latch.h"
#include "machine/i8155.h"
#include "machine/i8255.h"
#include "machine/ncr5380.h"
#include "machine/nscsi_bus.h"
#include "machine/pit8253.h"
#include "machine/upd765.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"


namespace {

class korg_dss1_state : public driver_device
{
public:
	korg_dss1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu1(*this, "cpu1")
		, m_cpu2(*this, "cpu2")
		, m_io1(*this, "io1")
		, m_io2(*this, "io2")
		, m_latch(*this, "latch%u", 1U)
		, m_pit(*this, "pit%u", 1U)
		, m_fdc(*this, "fdc")
		, m_lcdc(*this, "lcdc")
		, m_rombank(*this, "rombank")
	{
	}

	void dss1(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	u8 klm782_ga1_r(offs_t offset);
	void klm782_ga1_w(offs_t offset, u8 data);
	u8 klm782_ga2_1_r(offs_t offset);
	void klm782_ga2_1_w(offs_t offset, u8 data);
	u8 klm782_ga2_2_r(offs_t offset);
	void klm782_ga2_2_w(offs_t offset, u8 data);
	u8 klm782_ga3_1_r(offs_t offset);
	void klm782_ga3_1_w(offs_t offset, u8 data);
	u8 klm782_ga3_2_r(offs_t offset);
	void klm782_ga3_2_w(offs_t offset, u8 data);
	void dmaram_lsb_w(u8 data);
	void dmaram_msb_w(u8 data);
	u8 dmaram_lsb_r();
	u8 dmaram_msb_r();
	void mode_select_w(u8 data);

	void klm780(machine_config &config);
	void klm781(machine_config &config);
	void klm782(machine_config &config);

private:
	HD44780_PIXEL_UPDATE(lcd_pixel_update);

	void bank_switch_w(u8 data);
	void panel_led_w(u8 data);
	void fdc_tc_w(int state);
	void sed9420c_trgin_w(int state);
	u8 fdc_r(offs_t offset);
	void fdc_w(offs_t offset, u8 data);

	void vcf_vca_ef_w(u8 data);
	u8 cpu2_p5_r();
	void ad_select_w(u8 data);
	void da_lsb_w(u8 data);
	void da_msb_w(u8 data);
	u8 kbd_r();
	void kbd_w(u8 data);

	void cpu1_map(address_map &map) ATTR_COLD;
	void cpu2_map(address_map &map) ATTR_COLD;

	void palette_init_dss1(palette_device &palette);

protected:
	optional_device<cpu_device> m_cpu1;
	required_device<hd6303x_cpu_device> m_cpu2;
	required_device<i8155_device> m_io1;
	required_device<i8255_device> m_io2;
	required_device_array<generic_latch_8_device, 2> m_latch;
	required_device_array<pit8253_device, 6> m_pit;
	required_device<upd765a_device> m_fdc;
	required_device<hd44780_device> m_lcdc;
	optional_memory_bank m_rombank;
};

class korg_dssmsrk_state : public korg_dss1_state
{
public:
	korg_dssmsrk_state(const machine_config &mconfig, device_type type, const char *tag)
		: korg_dss1_state(mconfig, type, tag)
		, m_msrkcpu(*this, "msrkcpu")
		, m_scsic(*this, "scsi:7:scsic")
	{
	}

	void dssmsrk(machine_config &config);

private:
	void msrk_map(address_map &map) ATTR_COLD;
	void msrk_io_map(address_map &map) ATTR_COLD;

	u8 fdc_r(offs_t offset);
	void fdc_w(offs_t offset, u8 data);

	required_device<v40_device> m_msrkcpu;
	required_device<ncr53c80_device> m_scsic;
};

void korg_dss1_state::machine_start()
{
	if (m_rombank.found())
		m_rombank->configure_entries(0, 2, memregion("klm780")->base(), 0x8000);
}

void korg_dss1_state::machine_reset()
{
	if (m_rombank.found())
		m_rombank->set_entry(0);
}

HD44780_PIXEL_UPDATE(korg_dss1_state::lcd_pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 20)
		bitmap.pix(line * 8 + y, pos * 6 + x) = state;
}


void korg_dss1_state::bank_switch_w(u8 data)
{
	m_rombank->set_entry(data & 0x01);
}

void korg_dss1_state::panel_led_w(u8 data)
{
	// TODO
	// TODO
}

void korg_dss1_state::fdc_tc_w(int state)
{
	if (m_cpu1.found())
		m_fdc->tc_w(state);
	else
	{
		// TODO: MSRK rejumpers this to control SED9420C's MIN/STD input instead
	}
}

void korg_dss1_state::sed9420c_trgin_w(int state)
{
	// TODO
}

u8 korg_dss1_state::fdc_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		m_cpu1->adjust_icount(-1);

	if (BIT(offset, 0))
		return m_fdc->fifo_r();
	else
		return m_fdc->msr_r();
}

u8 korg_dssmsrk_state::fdc_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		m_msrkcpu->adjust_icount(-1);

	if (BIT(offset, 0))
		return m_fdc->fifo_r();
	else
		return m_fdc->msr_r();
}

void korg_dss1_state::fdc_w(offs_t offset, u8 data)
{
	if (!machine().side_effects_disabled())
		m_cpu1->adjust_icount(-1);

	if (BIT(offset, 0))
		m_fdc->fifo_w(data);
}

void korg_dssmsrk_state::fdc_w(offs_t offset, u8 data)
{
	if (!machine().side_effects_disabled())
		m_msrkcpu->adjust_icount(-1);

	if (BIT(offset, 0))
		m_fdc->fifo_w(data);
}

void korg_dss1_state::vcf_vca_ef_w(u8 data)
{
	// TODO
}

u8 korg_dss1_state::cpu2_p5_r()
{
	// TODO: other bits
	return m_latch[0]->pending_r() | (m_latch[1]->pending_r() << 1);
}

void korg_dss1_state::ad_select_w(u8 data)
{
	// TODO
}

void korg_dss1_state::da_lsb_w(u8 data)
{
	// TODO
}

void korg_dss1_state::da_msb_w(u8 data)
{
	// TODO
}

u8 korg_dss1_state::kbd_r()
{
	// TODO
	return 0;
}

void korg_dss1_state::kbd_w(u8 data)
{
	// TODO
}

u8 korg_dss1_state::klm782_ga1_r(offs_t offset)
{
	// TODO
	return 0;
}

void korg_dss1_state::klm782_ga1_w(offs_t offset, u8 data)
{
	// TODO
}

u8 korg_dss1_state::klm782_ga2_1_r(offs_t offset)
{
	// TODO
	return 0;
}

void korg_dss1_state::klm782_ga2_1_w(offs_t offset, u8 data)
{
	// TODO
}

u8 korg_dss1_state::klm782_ga2_2_r(offs_t offset)
{
	// TODO
	return 0;
}

void korg_dss1_state::klm782_ga2_2_w(offs_t offset, u8 data)
{
	// TODO
}

u8 korg_dss1_state::klm782_ga3_1_r(offs_t offset)
{
	// TODO
	return 0;
}

void korg_dss1_state::klm782_ga3_1_w(offs_t offset, u8 data)
{
	// TODO
}

u8 korg_dss1_state::klm782_ga3_2_r(offs_t offset)
{
	// TODO
	return 0;
}

void korg_dss1_state::klm782_ga3_2_w(offs_t offset, u8 data)
{
	// TODO
}

void korg_dss1_state::dmaram_lsb_w(u8 data)
{
	// TODO
}

void korg_dss1_state::dmaram_msb_w(u8 data)
{
	// TODO
}

u8 korg_dss1_state::dmaram_lsb_r()
{
	// TODO
	return 0;
}

u8 korg_dss1_state::dmaram_msb_r()
{
	// TODO
	return 0;
}

void korg_dss1_state::mode_select_w(u8 data)
{
	// TODO
}

void korg_dss1_state::cpu1_map(address_map &map)
{
	map(0x0000, 0x7fff).bankr("rombank");
	map(0x8000, 0x9fff).rom().region("klm780", 0x10000);
	map(0xa000, 0xbfff).ram();
	map(0xc000, 0xc0ff).mirror(0x200).rw(m_io1, FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w));
	map(0xc100, 0xc107).mirror(0x2f8).rw(m_io1, FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
	map(0xc400, 0xc403).mirror(0x3fc).rw(m_io2, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xc800, 0xc800).mirror(0x3ff).w(FUNC(korg_dss1_state::bank_switch_w));
	map(0xd000, 0xd003).mirror(0x300).rw(m_pit[0], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xd004, 0xd007).mirror(0x300).rw(m_pit[1], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xd008, 0xd00b).mirror(0x300).rw(m_pit[2], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xd00c, 0xd00f).mirror(0x300).rw(m_pit[3], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xd010, 0xd013).mirror(0x300).rw(m_pit[4], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xd014, 0xd017).mirror(0x300).rw(m_pit[5], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xd018, 0xd018).mirror(0x303).w(FUNC(korg_dssmsrk_state::dmaram_lsb_w));
	map(0xd01c, 0xd01c).mirror(0x303).w(FUNC(korg_dssmsrk_state::dmaram_msb_w));
	map(0xd020, 0xd027).mirror(0x318).rw(FUNC(korg_dssmsrk_state::klm782_ga3_1_r), FUNC(korg_dssmsrk_state::klm782_ga3_1_w));
	map(0xd040, 0xd047).mirror(0x318).rw(FUNC(korg_dssmsrk_state::klm782_ga3_2_r), FUNC(korg_dssmsrk_state::klm782_ga3_2_w));
	map(0xd060, 0xd061).mirror(0x31e).rw(FUNC(korg_dssmsrk_state::klm782_ga1_r), FUNC(korg_dssmsrk_state::klm782_ga1_w));
	map(0xd080, 0xd08f).mirror(0x310).rw(FUNC(korg_dssmsrk_state::klm782_ga2_1_r), FUNC(korg_dssmsrk_state::klm782_ga2_1_w));
	map(0xd0a0, 0xd0af).mirror(0x310).rw(FUNC(korg_dssmsrk_state::klm782_ga2_2_r), FUNC(korg_dssmsrk_state::klm782_ga2_2_w));
	map(0xd0c0, 0xd0c0).mirror(0x31f).r(FUNC(korg_dssmsrk_state::dmaram_lsb_r));
	map(0xd0e0, 0xd0e0).mirror(0x31f).r(FUNC(korg_dssmsrk_state::dmaram_msb_r));
	map(0xd400, 0xd400).mirror(0x3fe).w(m_latch[0], FUNC(generic_latch_8_device::write));
	map(0xd800, 0xd800).mirror(0x3fe).r(m_latch[1], FUNC(generic_latch_8_device::read));
	map(0xdc00, 0xdc01).mirror(0x3fe).rw(FUNC(korg_dss1_state::fdc_r), FUNC(korg_dss1_state::fdc_w));
	map(0xe000, 0xffff).ram();
}

void korg_dssmsrk_state::msrk_map(address_map &map)
{
	map.global_mask(0x3ffff);
	map(0x00000, 0x07fff).rom().region("klm780", 0);
	map(0x08000, 0x0bfff).ram();
	map(0x0c000, 0x0c0ff).mirror(0x200).rw(m_io1, FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w));
	map(0x0c100, 0x0c107).mirror(0x2f8).rw(m_io1, FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
	map(0x0c400, 0x0c403).mirror(0x3fc).rw(m_io2, FUNC(i8255_device::read), FUNC(i8255_device::write));
	//map(0x0cc00, 0x0cc1f).mirror(0x3e0).w(FUNC(korg_dssmsrk_state::msrk_ga_w));
	map(0x0d000, 0x0d003).mirror(0x300).rw(m_pit[0], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x0d004, 0x0d007).mirror(0x300).rw(m_pit[1], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x0d008, 0x0d00b).mirror(0x300).rw(m_pit[2], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x0d00c, 0x0d00f).mirror(0x300).rw(m_pit[3], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x0d010, 0x0d013).mirror(0x300).rw(m_pit[4], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x0d014, 0x0d017).mirror(0x300).rw(m_pit[5], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x0d018, 0x0d018).mirror(0x303).w(FUNC(korg_dssmsrk_state::dmaram_lsb_w));
	map(0x0d01c, 0x0d01c).mirror(0x303).w(FUNC(korg_dssmsrk_state::dmaram_msb_w));
	map(0x0d020, 0x0d027).mirror(0x318).rw(FUNC(korg_dssmsrk_state::klm782_ga3_1_r), FUNC(korg_dssmsrk_state::klm782_ga3_1_w));
	map(0x0d040, 0x0d047).mirror(0x318).rw(FUNC(korg_dssmsrk_state::klm782_ga3_2_r), FUNC(korg_dssmsrk_state::klm782_ga3_2_w));
	map(0x0d060, 0x0d061).mirror(0x31e).rw(FUNC(korg_dssmsrk_state::klm782_ga1_r), FUNC(korg_dssmsrk_state::klm782_ga1_w));
	map(0x0d080, 0x0d08f).mirror(0x310).rw(FUNC(korg_dssmsrk_state::klm782_ga2_1_r), FUNC(korg_dssmsrk_state::klm782_ga2_1_w));
	map(0x0d0a0, 0x0d0af).mirror(0x310).rw(FUNC(korg_dssmsrk_state::klm782_ga2_2_r), FUNC(korg_dssmsrk_state::klm782_ga2_2_w));
	map(0x0d0c0, 0x0d0c0).mirror(0x31f).r(FUNC(korg_dssmsrk_state::dmaram_lsb_r));
	map(0x0d0e0, 0x0d0e0).mirror(0x31f).r(FUNC(korg_dssmsrk_state::dmaram_msb_r));
	map(0x0d400, 0x0d400).mirror(0x3fe).w(m_latch[0], FUNC(generic_latch_8_device::write));
	map(0x0d800, 0x0d800).mirror(0x3fe).r(m_latch[1], FUNC(generic_latch_8_device::read));
	map(0x0e000, 0x0ffff).ram();
	map(0x10000, 0x17fff).mirror(0x8000).rom().region("klm780", 0x8000);
	map(0x30000, 0x3ffff).rom().region("msrk", 0);
}

void korg_dssmsrk_state::msrk_io_map(address_map &map)
{
	map(0xc800, 0xc807).mirror(0x3f8).rw(m_scsic, FUNC(ncr53c80_device::read), FUNC(ncr53c80_device::write));
	map(0xdc00, 0xdc01).mirror(0x3fe).rw(FUNC(korg_dssmsrk_state::fdc_r), FUNC(korg_dssmsrk_state::fdc_w));
}

void korg_dss1_state::cpu2_map(address_map &map)
{
	map(0x0100, 0x0100).mirror(0x3cff).w(FUNC(korg_dss1_state::da_lsb_w));
	map(0x0200, 0x0200).mirror(0x3cff).w(FUNC(korg_dss1_state::da_msb_w));
	map(0x0300, 0x0300).mirror(0x3cff).r(m_latch[0], FUNC(generic_latch_8_device::read));
	map(0x0300, 0x0300).mirror(0x3cff).w(m_latch[1], FUNC(generic_latch_8_device::write));
	map(0x4000, 0x4000).mirror(0x3fff).rw(FUNC(korg_dss1_state::kbd_r), FUNC(korg_dss1_state::kbd_w));
	map(0x8000, 0x9fff).mirror(0x2000).ram();
	map(0xc000, 0xffff).rom().region("klm781", 0);
}


static INPUT_PORTS_START(dss1)
INPUT_PORTS_END

static void dss1_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
}

void korg_dss1_state::palette_init_dss1(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(131, 136, 139));
	palette.set_pen_color(1, rgb_t( 92,  83,  88));
}

void korg_dss1_state::klm780(machine_config &config)
{
	I8085A(config, m_cpu1, 10_MHz_XTAL); // uPD8085AC-2
	m_cpu1->set_addrmap(AS_PROGRAM, &korg_dss1_state::cpu1_map);

	I8155(config, m_io1, 10_MHz_XTAL / 2); // uPD8155HC-2
	m_io1->out_to_callback().set_inputline(m_cpu1, I8085_RST75_LINE);
	m_io1->in_pa_callback().set(m_lcdc, FUNC(hd44780_device::db_r));
	m_io1->out_pa_callback().set(m_lcdc, FUNC(hd44780_device::db_w));
	m_io1->out_pb_callback().set(FUNC(korg_dss1_state::panel_led_w));
	m_io1->out_pc_callback().set(m_lcdc, FUNC(hd44780_device::e_w)).bit(0);
	m_io1->out_pc_callback().append(m_lcdc, FUNC(hd44780_device::rw_w)).bit(1);
	m_io1->out_pc_callback().append(m_lcdc, FUNC(hd44780_device::rs_w)).bit(2);
	m_io1->out_pc_callback().append(m_fdc, FUNC(upd765a_device::reset_w)).bit(3).invert();
	m_io1->out_pc_callback().append(FUNC(korg_dss1_state::fdc_tc_w)).bit(4);
	m_io1->out_pc_callback().append(FUNC(korg_dss1_state::sed9420c_trgin_w)).bit(5);

	I8255(config, m_io2); // uPD8255AC-2
	m_io2->out_pc_callback().set(FUNC(korg_dss1_state::mode_select_w));

	UPD765A(config, m_fdc, 16_MHz_XTAL / 4, true, true); // uPD765AC; clocked through SED9420C
	m_fdc->intrq_wr_callback().set_inputline(m_cpu1, I8085_INTR_LINE);
	FLOPPY_CONNECTOR(config, "fdc:0", dss1_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", dss1_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	GENERIC_LATCH_8(config, m_latch[0]);
	m_latch[0]->data_pending_callback().set_inputline(m_cpu1, I8085_RST65_LINE).invert();

	GENERIC_LATCH_8(config, m_latch[1]);
	m_latch[1]->data_pending_callback().set_inputline(m_cpu1, I8085_RST55_LINE);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6*20, 8*2);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(korg_dss1_state::palette_init_dss1), 2);

	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 20);
	m_lcdc->set_pixel_update_cb(FUNC(korg_dss1_state::lcd_pixel_update));
}

void korg_dss1_state::klm781(machine_config &config)
{
	HD6303X(config, m_cpu2, 8_MHz_XTAL); // HD63B03X
	m_cpu2->set_addrmap(AS_PROGRAM, &korg_dss1_state::cpu2_map);
	m_cpu2->out_p2_cb().set(FUNC(korg_dss1_state::vcf_vca_ef_w));
	m_cpu2->in_p5_cb().set(FUNC(korg_dss1_state::cpu2_p5_r));
	m_cpu2->out_p6_cb().set(FUNC(korg_dss1_state::ad_select_w));
	m_cpu2->out_ser_tx_cb().set("midi_out", FUNC(midi_port_device::write_txd));

	MIDI_PORT(config, "midi_in", midiin_slot, "midiin");
	MIDI_PORT(config, "midi_out", midiout_slot, "midiout");
}

void korg_dss1_state::klm782(machine_config &config)
{
	PIT8253(config, m_pit[0]);
	PIT8253(config, m_pit[1]);
	PIT8253(config, m_pit[2]);
	PIT8253(config, m_pit[3]);
	PIT8253(config, m_pit[4]);
	PIT8253(config, m_pit[5]);
}

void korg_dss1_state::dss1(machine_config &config)
{
	klm780(config);
	klm781(config);
	klm782(config);
}

void korg_dssmsrk_state::dssmsrk(machine_config &config)
{
	V40(config, m_msrkcpu, 16_MHz_XTAL); // uPD70208
	m_msrkcpu->set_addrmap(AS_PROGRAM, &korg_dssmsrk_state::msrk_map);
	m_msrkcpu->set_addrmap(AS_IO, &korg_dssmsrk_state::msrk_io_map);
	m_msrkcpu->out_hreq_cb().set_inputline(m_msrkcpu, INPUT_LINE_HALT);
	m_msrkcpu->out_hreq_cb().append(m_msrkcpu, FUNC(v40_device::hack_w));
	m_msrkcpu->in_ior_cb<0>().set(m_fdc, FUNC(upd765a_device::dma_r));
	m_msrkcpu->out_iow_cb<0>().set(m_fdc, FUNC(upd765a_device::dma_w));
	m_msrkcpu->in_ior_cb<1>().set(m_scsic, FUNC(ncr53c80_device::dma_r));
	m_msrkcpu->out_iow_cb<1>().set(m_scsic, FUNC(ncr53c80_device::dma_w));
	m_msrkcpu->out_eop_cb().set(m_fdc, FUNC(upd765a_device::tc_line_w));
	//m_msrkcpu->out_eop_cb().append(m_scsic, FUNC(ncr53c80_device::eop_w));
	m_msrkcpu->tout1_cb().set(m_msrkcpu, FUNC(v40_device::tclk_w));

	klm780(config);
	config.device_remove("cpu1");
	m_io1->set_clock(16_MHz_XTAL / 4); // CLKOUT divider not verified
	m_io1->out_to_callback().set_inputline(m_msrkcpu, INPUT_LINE_IRQ1);
	m_fdc->intrq_wr_callback().set_inputline(m_msrkcpu, INPUT_LINE_IRQ6);
	m_fdc->drq_wr_callback().set(m_msrkcpu, FUNC(v40_device::dreq_w<0>)); // FIXME: delayed by 74HCT164
	m_latch[0]->data_pending_callback().set_inputline(m_msrkcpu, INPUT_LINE_IRQ2).invert();
	m_latch[1]->data_pending_callback().set_inputline(m_msrkcpu, INPUT_LINE_IRQ3);

	klm781(config);
	klm782(config);

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:1", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:7").option_set("scsic", NCR53C80).machine_config([this] (device_t *device) {
		auto &scsic = downcast<ncr53c80_device &>(*device); // 48-pin DIP
		scsic.irq_handler().set_inputline(m_msrkcpu, INPUT_LINE_IRQ4);
		scsic.drq_handler().set(m_msrkcpu, FUNC(v40_device::dreq_w<1>));
	});
}


ROM_START(dss1)
	ROM_REGION(0x12000, "klm780", 0)
	ROM_SYSTEM_BIOS(0, "v36", "Version 36")
	ROM_SYSTEM_BIOS(1, "v34", "Version 34")
	ROM_SYSTEM_BIOS(2, "v31", "Version 31")
	ROMX_LOAD("860336.ic19", 0x00000, 0x8000, CRC(44515595) SHA1(f4873c219cda4158506acbfb06abb8a72224049d), ROM_BIOS(0)) // 27256 (bank 0)
	ROMX_LOAD("860334.ic19", 0x00000, 0x8000, CRC(076c5956) SHA1(752a26761c63e46f1a6efa1b19a60bafa7a5bc42), ROM_BIOS(1))
	ROMX_LOAD("860331.ic19", 0x00000, 0x8000, CRC(fcefcc79) SHA1(2332ffcbbe61d460d1929e65cbaa01e0766ba51f), ROM_BIOS(2))
	ROMX_LOAD("860436.ic18", 0x08000, 0x8000, CRC(c8830cf9) SHA1(2bcdcfd9afc9e6b2078afdf9c027622b705780a9), ROM_BIOS(0)) // 27256 (bank 1)
	ROMX_LOAD("860434.ic18", 0x08000, 0x8000, CRC(5b253cac) SHA1(20507004ead025b03b2f7a31af87c9dae968e8eb), ROM_BIOS(1))
	ROMX_LOAD("860431.ic18", 0x08000, 0x8000, CRC(ed0e4238) SHA1(b7e56a3f414dd13bb6b9ef40262c6b93e52f5254), ROM_BIOS(2))
	ROMX_LOAD("860536.ic12", 0x10000, 0x2000, CRC(547388a1) SHA1(4c3dfaebe48f441955f2a6bd42dd4d30eb63b913), ROM_BIOS(0)) // 2764 (not banked)
	ROMX_LOAD("860534.ic12", 0x10000, 0x2000, CRC(9b5792b7) SHA1(e031d7f3a5c4f67399a75050bd02343ed6f8926d), ROM_BIOS(1))
	ROMX_LOAD("860531.ic12", 0x10000, 0x2000, CRC(a8173858) SHA1(e1d898594ad9e65a95478cf1ec14b9b3cafd0159), ROM_BIOS(2))

	ROM_REGION(0x4000, "klm781", 0)
	ROMX_LOAD("860236.ic15", 0x0000, 0x4000, CRC(b4ea379a) SHA1(66b3586b6fb7fa5edf70a933e49f626452ffe006), ROM_BIOS(0))
	ROMX_LOAD("860234.ic15", 0x0000, 0x4000, CRC(5766cdb5) SHA1(ded6c5758fbf90fd65b80d5b08e72349d38555d2), ROM_BIOS(1))
	ROMX_LOAD("860231.ic15", 0x0000, 0x4000, CRC(2559b3aa) SHA1(8b7998ffc24405d0ca08a93c23c18f97aff74d16), ROM_BIOS(2))
ROM_END

ROM_START(dssmsrk)
	ROM_REGION(0x10000, "klm780", 0)
	ROM_LOAD("113000.ic18", 0x00000, 0x8000, CRC(6fa13d52) SHA1(8de0e4e8ac4afe0d0fb116b6c8b64739ba81a722))
	ROM_LOAD("113001.ic19", 0x08000, 0x8000, CRC(ec7e2473) SHA1(dacea4a545ce3ed1fc11d9025e86dfd1d32a222c))
	// EPROM at IC12 is replaced with D4364C-15 SRAM

	ROM_REGION(0x4000, "klm781", 0)
	ROM_LOAD("860236.ic15", 0x0000, 0x4000, CRC(b4ea379a) SHA1(66b3586b6fb7fa5edf70a933e49f626452ffe006))

	ROM_REGION(0x10000, "msrk", 0)
	ROM_LOAD("113003.u30", 0x00000, 0x10000, CRC(81b17db3) SHA1(af8a3167e06641d41b9b9e6e024335c2eb827274))
ROM_END

} // anonymous namespace


SYST(1986, dss1,    0,    0, dss1,    dss1, korg_dss1_state,    empty_init, "Korg",               "DSS-1 Digital Sampling Synthesizer",                        MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1987, dssmsrk, dss1, 0, dssmsrk, dss1, korg_dssmsrk_state, empty_init, "Korg / Sound Logic", "DSS-1 Digital Sampling Synthesizer (Memory/SCSI Retrofit)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



korgdvp1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Korg DVP-1 MIDI vocoder.

****************************************************************************/

#include "emu.h"
//#include "bus/midi/midi.h"
#include "cpu/upd7810/upd7810.h"
#include "cpu/tms32010/tms32010.h"
#include "machine/nvram.h"


namespace {

class korgdvp1_state : public driver_device
{
public:
	korgdvp1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_dsp(*this, "dsp%u", 1U)
	{
	}

	void dvp1(machine_config &config);

private:
	u8 inputs_r();
	u8 dsp_int_r();
	void control_w(u8 data);
	void leds_w(offs_t offset, u8 data);
	void dsp_w(offs_t offset, u8 data);

	void main_map(address_map &map) ATTR_COLD;

	required_device<upd7810_device> m_maincpu;
	required_device_array<tms32010_device, 2> m_dsp;
};


u8 korgdvp1_state::inputs_r()
{
	return 0xff;
}

u8 korgdvp1_state::dsp_int_r()
{
	// PB3 = BUSY
	// PB6 = INT0
	// PB7 = INT1
	return 0xff;
}

void korgdvp1_state::control_w(u8 data)
{
	// PB0, PB1 = input select (HC139)
	// PB2 = MUTE
	// PB4 = DSP RES
	// PB5 = ROM/RAM
}

void korgdvp1_state::leds_w(offs_t offset, u8 data)
{
	for (int i = 0; i < 8; i++)
		if (BIT(offset, i))
			logerror("%s: Writing %02X to A%d LEDs\n", machine().describe_context(), data, i);
}

void korgdvp1_state::dsp_w(offs_t offset, u8 data)
{
	for (int i = 0; i < 2; i++)
		if (!BIT(offset, 9 - i))
			logerror("%s: Writing %02X to DSP %d\n", machine().describe_context(), data, i + 1);
}

void korgdvp1_state::main_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("program", 0);
	map(0x8000, 0x87ff).mirror(0x1800).ram().share("nvram");
	map(0xa000, 0xa0ff).mirror(0x1f00).w(FUNC(korgdvp1_state::leds_w));
	map(0xc000, 0xc000).select(0x300).mirror(0x1cff).w(FUNC(korgdvp1_state::dsp_w));
}


static INPUT_PORTS_START(dvp1)
INPUT_PORTS_END

void korgdvp1_state::dvp1(machine_config &config)
{
	UPD7810(config, m_maincpu, 12_MHz_XTAL); // µPD7811-161-36 (according to parts list) but with both mode pins pulled up
	m_maincpu->set_addrmap(AS_PROGRAM, &korgdvp1_state::main_map);
	m_maincpu->pa_in_cb().set(FUNC(korgdvp1_state::inputs_r));
	m_maincpu->pb_in_cb().set(FUNC(korgdvp1_state::dsp_int_r));
	m_maincpu->pb_out_cb().set(FUNC(korgdvp1_state::control_w));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // HM6116LP-4 + 3V lithium battery

	TMS32010(config, m_dsp[0], 20_MHz_XTAL).set_disable();
	TMS32010(config, m_dsp[1], 20_MHz_XTAL).set_disable();
}

ROM_START(dvp1)
	ROM_REGION(0x8000, "program", 0) // Version: SEP 28, 1985
	ROM_LOAD("850803.ic6", 0x0000, 0x8000, CRC(1170db85) SHA1(4ce773dd22c56982b9493f89dce62111eec596b3)) // MBM27256-25

	ROM_REGION16_LE(0xc00, "dsp1", 0) // 1536 x 16 internal bootloader
	ROM_LOAD("tms320m10nl.ic9", 0x000, 0xc00, NO_DUMP)

	ROM_REGION16_LE(0xc00, "dsp2", 0) // 1536 x 16 internal bootloader (almost certainly identical to DSP 1)
	ROM_LOAD("tms320m10nl.ic8", 0x000, 0xc00, NO_DUMP)
ROM_END

} // anonymous namespace


SYST(1985, dvp1, 0, 0, dvp1, dvp1, korgdvp1_state, empty_init, "Korg", "DVP-1 Digital Voice Processor", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



korgdw8k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Korg DW-8000 and EX-8000 synthesizers.

****************************************************************************/

#include "emu.h"
#include "cpu/m6800/m6801.h"
//#include "bus/midi/midi.h"
#include "machine/nvram.h"


namespace {

class korgdw8k_state : public driver_device
{
public:
	korgdw8k_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rombank(*this, "rombank")
	{
	}

	void dw8000(machine_config &config);
	void dw8000ex(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void dac_w(u8 data);
	void vcfa_w(u8 data);
	void ddl_w(u8 data);
	void led_data_w(u8 data);
	void led_addr_w(u8 data);
	void bank_w(u8 data);

	void mem_map(address_map &map) ATTR_COLD;
	void expanded_mem_map(address_map &map) ATTR_COLD;

	required_device<hd6303x_cpu_device> m_maincpu;
	optional_memory_bank m_rombank;
};

void korgdw8k_state::machine_start()
{
	if (m_rombank.found())
		m_rombank->configure_entries(0, 2, memregion("program")->base(), 0x4000);
}

void korgdw8k_state::machine_reset()
{
	if (m_rombank.found())
		bank_w(0);
}

void korgdw8k_state::dac_w(u8 data)
{
}

void korgdw8k_state::vcfa_w(u8 data)
{
}

void korgdw8k_state::ddl_w(u8 data)
{
}

void korgdw8k_state::led_data_w(u8 data)
{
}

void korgdw8k_state::led_addr_w(u8 data)
{
}

void korgdw8k_state::bank_w(u8 data)
{
	//m_rombank->set_entry(BIT(data, 0));
	m_rombank->set_entry(1);
}


void korgdw8k_state::mem_map(address_map &map)
{
	map(0x3000, 0x37ff).ram().share("nvram");
	map(0x3800, 0x3fff).ram();
	map(0x4040, 0x4040).w(FUNC(korgdw8k_state::dac_w));
	map(0x4060, 0x4060).w(FUNC(korgdw8k_state::vcfa_w));
	map(0x4080, 0x4080).w(FUNC(korgdw8k_state::ddl_w));
	map(0x40a0, 0x40a0).w(FUNC(korgdw8k_state::led_data_w));
	map(0x40c0, 0x40c0).w(FUNC(korgdw8k_state::led_addr_w));
	map(0xc000, 0xffff).rom().region("program", 0);
}

void korgdw8k_state::expanded_mem_map(address_map &map)
{
	mem_map(map);
	map(0x8000, 0xbfff).ram();
	map(0xc000, 0xffff).bankr("rombank").w(FUNC(korgdw8k_state::bank_w));
}


static INPUT_PORTS_START(dw8000)
INPUT_PORTS_END

void korgdw8k_state::dw8000(machine_config &config)
{
	HD6303X(config, m_maincpu, 8_MHz_XTAL); // HD63B03XP
	m_maincpu->set_addrmap(AS_PROGRAM, &korgdw8k_state::mem_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // HM6116LP-4 + battery
}

void korgdw8k_state::dw8000ex(machine_config &config)
{
	dw8000(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &korgdw8k_state::expanded_mem_map);
}

ROM_START(dw8000)
	ROM_REGION(0x4000, "program", 0)
	ROM_SYSTEM_BIOS(0, "v13", "Version 13")
	ROMX_LOAD("dw8000-v0713.ic38", 0x0000, 0x4000, CRC(d8d89329) SHA1(a0e3402528036a2ba5767e707b8d2e4e365dbd4a), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v12", "Version 12")
	ROMX_LOAD("dw-8000-v12.ic38",  0x0000, 0x4000, CRC(f21b3848) SHA1(2e91791e71205049446e75ec53267d54150e1ac7), ROM_BIOS(1))

	ROM_REGION(0x20000, "waveform", 0) // Standard waves
	ROM_LOAD("hn613256p-t70_32004086.ic46", 0x00000, 0x08000, CRC(f24d5e8b) SHA1(bf8eafffd55e70d6a515abbc311667d3bc12157b))
	ROM_LOAD("hn613256p-t71_32004087.ic45", 0x08000, 0x08000, CRC(a8c5a80c) SHA1(c2c4fb748f1fbdea3abe20072105eedebc321ece))
	ROM_LOAD("hn613256p-cb4_32004088.ic44", 0x10000, 0x08000, CRC(2610d6f6) SHA1(6894b8368f64f62bb541b3f88e58d88f7138b3a5))
	ROM_LOAD("hn613256p-cb5_32004089.ic43", 0x18000, 0x08000, CRC(0958145d) SHA1(fd7fd60bde8739a937bd0af11caac76e566b4a00))
ROM_END

ROM_START(dw8000ex)
	ROM_REGION(0x8000, "program", 0)
	ROM_LOAD("korg-dw8000-ex.bin", 0x0000, 0x8000, CRC(beb31c83) SHA1(e9879ccb6c3417c099e0b673852cad6c79026c73))

	ROM_REGION(0x20000, "waveform", 0) // Standard waves
	ROM_LOAD("hn613256p-t70_32004086.ic46", 0x00000, 0x08000, CRC(f24d5e8b) SHA1(bf8eafffd55e70d6a515abbc311667d3bc12157b))
	ROM_LOAD("hn613256p-t71_32004087.ic45", 0x08000, 0x08000, CRC(a8c5a80c) SHA1(c2c4fb748f1fbdea3abe20072105eedebc321ece))
	ROM_LOAD("hn613256p-cb4_32004088.ic44", 0x10000, 0x08000, CRC(2610d6f6) SHA1(6894b8368f64f62bb541b3f88e58d88f7138b3a5))
	ROM_LOAD("hn613256p-cb5_32004089.ic43", 0x18000, 0x08000, CRC(0958145d) SHA1(fd7fd60bde8739a937bd0af11caac76e566b4a00))

	ROM_REGION(0x20000, "exp", 0) // Expansion waves ("Version E") (TBD: do these actually require a different expansion?)
	ROM_LOAD("exp-1.bin", 0x00000, 0x08000, CRC(14a5d504) SHA1(76ba6a715f1126d4cce59f172943c9f710ce981a))
	ROM_LOAD("exp-3.bin", 0x08000, 0x08000, CRC(1d2fd4a0) SHA1(41864bf1180cc226d149bcfd02496882affdad19))
	ROM_LOAD("exp-2.bin", 0x10000, 0x08000, CRC(e4a445ca) SHA1(53513fbf29523567d87ed119ecf1b116da904abc))
	ROM_LOAD("exp-4.bin", 0x18000, 0x08000, CRC(bfd0701f) SHA1(07d3b903df3b980b4d0df0d64315083abdd86b41))
ROM_END

ROM_START(ex8000)
	ROM_REGION(0x4000, "program", 0)
	ROM_LOAD("ex8000_firmware.ic38", 0x0000, 0x4000, CRC(a6d0fcdc) SHA1(a5eed30248c8d51b8d2e00e4b23fdcdf2a2f94af))

	ROM_REGION(0x20000, "waveform", 0)
	ROM_LOAD("hn613256p-t70_32004086.ic46", 0x00000, 0x08000, CRC(f24d5e8b) SHA1(bf8eafffd55e70d6a515abbc311667d3bc12157b))
	ROM_LOAD("hn613256p-t71_32004087.ic45", 0x08000, 0x08000, CRC(a8c5a80c) SHA1(c2c4fb748f1fbdea3abe20072105eedebc321ece))
	ROM_LOAD("hn613256p-cb4_32004088.ic44", 0x10000, 0x08000, CRC(2610d6f6) SHA1(6894b8368f64f62bb541b3f88e58d88f7138b3a5))
	ROM_LOAD("hn613256p-cb5_32004089.ic43", 0x18000, 0x08000, CRC(0958145d) SHA1(fd7fd60bde8739a937bd0af11caac76e566b4a00))
ROM_END

} // anonymous namespace


SYST(1985, dw8000,   0,      0, dw8000,   dw8000, korgdw8k_state, empty_init, "Korg",               "DW-8000 Programmable Digital Waveform Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1985, dw8000ex, dw8000, 0, dw8000ex, dw8000, korgdw8k_state, empty_init, "Korg / Musitronics", "DW-8000-EX Programmable Digital Waveform Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1985, ex8000,   dw8000, 0, dw8000,   dw8000, korgdw8k_state, empty_init, "Korg",               "EX-8000 Programmable Polyphonic Synthe Module", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



korgm1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese, AJR
/**************************************************************************************************

Korg M1 (c) 1988

Notes:
- Memory cards are MSX/PC-9801 Bee Card re-branded Korg MCR-*, one row of 32-pins plus
  write protect dip and a CR2016 battery slot.
  Four known RAM options:
  - MCR-02, 128 kB
  - MCR-03, 256 kB
  - MCR-04, 1024 kB x 4 banks
  - Radiusz Bee-Card, custom made multibank card with rotary knob, 16 x 32 kB/8 x 64 kB/4 x 128 kB
- "Ext. Cards" are coupled with MPC-** (Bee Card) & MSC-** (PCM data) cards, latter is a 19x2-pins
   that fits in the back of the synth.
   Numbered official releases goes from 00P (factory preset) to 16 + some third party options.

**************************************************************************************************/

#include "emu.h"
#include "cpu/nec/v5x.h"
#include "machine/adc0808.h"
#include "machine/cxd1095.h"
#include "machine/nvram.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"
//#include "speaker.h"


namespace {

class korgm1_state : public driver_device
{
public:
	korgm1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_adc(*this, "adc")
		, m_lcdc(*this, "lcdc")
		, m_panel_sw(*this, "Y%u", 0U)
	{ }

	void korgm1(machine_config &config);

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual void video_start() override ATTR_COLD;

private:
	HD44780_PIXEL_UPDATE(lcd_pixel_update);

	void pio_pa_w(u8 data);
	u8 panel_sw_r();
	void panel_leds_w(u8 data);

	void korgm1_map(address_map &map) ATTR_COLD;
	void korgm1_io(address_map &map) ATTR_COLD;

	void palette_init(palette_device &palette);

	// devices
	required_device<v50_device> m_maincpu;
	required_device<adc0808_device> m_adc;
	required_device<hd44780_device> m_lcdc;
	optional_ioport_array<8> m_panel_sw;

	u8 m_selected_sw = 0;
};

void korgm1_state::video_start()
{
}

HD44780_PIXEL_UPDATE(korgm1_state::lcd_pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 40)
		bitmap.pix(line * 8 + y, pos * 6 + x) = state;
}

void korgm1_state::pio_pa_w(u8 data)
{
	m_selected_sw = data & 0x07;

	m_adc->address_w((data & 0x38) >> 3);
	m_adc->start_w(BIT(data, 6));

	// PA7 controls /RESET on TG I/TG II/VDF I/VDF II/MDE
}

u8 korgm1_state::panel_sw_r()
{
	// PB6: to pin 32 of Bee Card (Card St.), 0 = card inserted
	return 0xc0 | m_panel_sw[m_selected_sw].read_safe(0x3f);
}

void korgm1_state::panel_leds_w(u8 data)
{
}

void korgm1_state::korgm1_map(address_map &map)
{
	map(0x00000, 0x0ffff).ram().share("nvram"); // 64 KB
//  map(0x50000, 0x5ffff).lr8(NAME([this] (offs_t offset) {
//      return m_beecard[offset];
//  })).umask16(0x00ff); //  memory card 32 KB
	map(0xe0000, 0xfffff).rom().region("ipl", 0);
}

void korgm1_state::korgm1_io(address_map &map)
{
//  map(0x0000, 0x00ff); internal peripheral (?)
//  map(0x0100, 0x01ff); VDF 1 (MB87404)
//  map(0x0200, 0x02ff); VDF 2 (MB87404)
//  map(0x0500, 0x0503); MDE (MB87405)
//  map(0x0600, 0x0601); OPZ 1 (8-bit port)
//  map(0x0700, 0x0701); OPZ 2 (8-bit port)
//  map(0x0800, 0x0801); SCAN (8-bit port) (keyboard)
	map(0x0900, 0x0900).r(m_adc, FUNC(adc0808_device::data_r));
	map(0x0a00, 0x0a0f).rw("pio", FUNC(cxd1095_device::read), FUNC(cxd1095_device::write)).umask16(0x00ff);
//  map(0x0b00, 0x0b01); LCDC (8-bit port)
//  map(0x1000, 0x11ff); TG (MB87402)
//  map(0x2000, 0x23ff); SCSI
//  map(0x3000, 0x33ff); FDC
//  TG 2?
}

static INPUT_PORTS_START( korgm1 )
	PORT_START("Y0")
	PORT_BIT(0x03, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("INT Mode (SW1)")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("CARD Mode (SW5)")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("COMBI Mode (SW2)")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("EDIT COMBI Mode (SW6)")

	PORT_START("Y1")
	PORT_BIT(0x03, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PROG Mode? (SW3)")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("EDIT PROG Mode (SW7)")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SEQ Mode? (SW4)") // tight loops waiting for an irq, if bypassed goes in "New Song" mode
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("GLOBAL Mode (SW8)")

	PORT_START("Y2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Key Up (SW9)") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Key Down (SW10)") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Cursor A (SW11)") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Cursor B (SW12)") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Cursor C (SW13)") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Cursor D (SW14)") PORT_CODE(KEYCODE_4)

	PORT_START("Y3")
	// Freezes decimal digits when editing function (stands on LEFT of 0 key)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Bank Hold key (SW15)") PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW16")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW17")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW18")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW19")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW20")

	PORT_START("Y4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Keypad 0 (SW31)") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Keypad 1 (SW27)") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Keypad 2 (SW28)") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Keypad 3 (SW29)") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Keypad 4 (SW24)") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("Y5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Keypad 5 (SW25)") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Keypad 6 (SW26)") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Keypad 7 (SW21)") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Keypad 8 (SW22)") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Keypad 9 (SW23)") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("Y6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Cursor E (SW30)") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Cursor F (SW32)") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Cursor G (SW33)") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Cursor H (SW34)") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Page -") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Page +") PORT_CODE(KEYCODE_RIGHT)

	PORT_START("AN6")
	// enough to fix battery low level
	PORT_BIT(0x7f, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

static INPUT_PORTS_START( korgm1r )
	PORT_INCLUDE( korgm1 )
	// TODO: slightly different
INPUT_PORTS_END

void korgm1_state::machine_start()
{
	save_item(NAME(m_selected_sw));
}

void korgm1_state::machine_reset()
{
}


void korgm1_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t( 69,  63,  66));
	palette.set_pen_color(1, rgb_t(131, 136, 139));
}

void korgm1_state::korgm1(machine_config &config)
{
	/* basic machine hardware */
	V50(config, m_maincpu, 32_MHz_XTAL / 2); // µPD70216
	m_maincpu->set_addrmap(AS_PROGRAM, &korgm1_state::korgm1_map);
	m_maincpu->set_addrmap(AS_IO, &korgm1_state::korgm1_io);
	m_maincpu->tout2_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ2);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // 2x µPD43256C-15L + battery

	cxd1095_device &pio(CXD1095(config, "pio")); // CXD1095Q
	pio.out_porta_cb().set(FUNC(korgm1_state::pio_pa_w));
	pio.in_portb_cb().set(FUNC(korgm1_state::panel_sw_r));
	pio.out_portc_cb().set(FUNC(korgm1_state::panel_leds_w));
	pio.in_portd_cb().set(m_lcdc, FUNC(hd44780_device::db_r));
	pio.out_portd_cb().set(m_lcdc, FUNC(hd44780_device::db_w));
	// TODO: porte input (PE3 connected to Ext. Card status (0=inserted?), PE0-PE2 to "LCD Unit")
	pio.out_porte_cb().set(m_lcdc, FUNC(hd44780_device::rs_w)).bit(0);
	pio.out_porte_cb().append(m_lcdc, FUNC(hd44780_device::rw_w)).bit(1);
	pio.out_porte_cb().append(m_lcdc, FUNC(hd44780_device::e_w)).bit(2);

	M58990(config, m_adc, 32_MHz_XTAL / 32); // M58990P-1 (µPD65013G-402 divides V50 CLKOUT by 8)
	// Joystick, "value" and After Touch routes here
//  m_adc->in_callback<0>().set_ioport("AN0"); // pitch joystick
//  m_adc->in_callback<1>().set_ioport("AN1"); // mg. int joystick
	m_adc->in_callback<6>().set_ioport("AN6"); // to internal battery

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_color(rgb_t::green());
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6*40, 8*2);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(korgm1_state::palette_init), 2);

	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 40);
	m_lcdc->set_pixel_update_cb(FUNC(korgm1_state::lcd_pixel_update));

	/* sound hardware */
	//SPEAKER(config, "mono").front_center();
}


/***************************************************************************

  Game driver(s)

***************************************************************************/

ROM_START( korgm1 )
	ROM_REGION16_LE( 0x20000, "ipl", 0 )
	ROM_LOAD16_BYTE( "rev19-ic23-880219.bin", 0x00001, 0x10000, CRC(397bce2d) SHA1(8df016ae172904eb94bc782a054bd783878c6f75) )
	ROM_LOAD16_BYTE( "rev19-ic32-880319.bin", 0x00000, 0x10000, CRC(4e33fc09) SHA1(2b96e054a08bf1091310d9367e35fbcf52003a80) )

	ROM_REGION( 0x8000, "tgrom", ROMREGION_ERASE00 )
	ROM_LOAD( "mb83512-15p-259.ic30", 0x0000, 0x8000, NO_DUMP )
	ROM_IGNORE( 0x8000 )

	ROM_REGION( 0x4000, "vdf1", ROMREGION_ERASE00 )
	ROM_LOAD( "upd23c512-039.ic26", 0x0000, 0x4000, NO_DUMP )
	ROM_CONTINUE( 0x0000, 0x4000 )
	ROM_CONTINUE( 0x0000, 0x4000 )
	ROM_IGNORE( 0x4000 )

	ROM_REGION( 0x4000, "vdf2", ROMREGION_ERASE00 )
	ROM_LOAD( "upd23c512-039.ic20", 0x0000, 0x4000, NO_DUMP )
	ROM_CONTINUE( 0x0000, 0x4000 )
	ROM_CONTINUE( 0x0000, 0x4000 )
	ROM_CONTINUE( 0x0000, 0x4000 )
ROM_END

ROM_START( korgm1ex )
	ROM_REGION16_LE( 0x20000, "ipl", 0 )
	ROM_LOAD16_BYTE( "v. 1.29 27c512 hi ic23.bin", 0x00001, 0x10000, CRC(ff96bae7) SHA1(8e84755e0b48e47b9fa46958ff33b4111602950b) )
	ROM_LOAD16_BYTE( "v. 1.29 27c512 lo ic32.bin", 0x00000, 0x10000, CRC(730cedf1) SHA1(ae1e5aee1e296714dc518dc0184c7b9b398df11d) )

	ROM_REGION( 0x8000, "tgrom", ROMREGION_ERASE00 )
	ROM_LOAD( "mb83512-15p-259.ic30", 0x0000, 0x8000, NO_DUMP )
	ROM_IGNORE( 0x8000 )

	ROM_REGION( 0x4000, "vdf1", ROMREGION_ERASE00 )
	ROM_LOAD( "upd23c512-039.ic26", 0x0000, 0x4000, NO_DUMP )
	ROM_CONTINUE( 0x0000, 0x4000 )
	ROM_CONTINUE( 0x0000, 0x4000 )
	ROM_IGNORE( 0x4000 )

	ROM_REGION( 0x4000, "vdf2", ROMREGION_ERASE00 )
	ROM_LOAD( "upd23c512-039.ic20", 0x0000, 0x4000, NO_DUMP )
	ROM_CONTINUE( 0x0000, 0x4000 )
	ROM_CONTINUE( 0x0000, 0x4000 )
	ROM_CONTINUE( 0x0000, 0x4000 )
ROM_END

ROM_START( korgm1r )
	ROM_REGION16_LE( 0x20000, "ipl", 0 )
	ROM_LOAD16_BYTE( "880506_ic23 high _27c512_ m1r.bin", 0x00001, 0x10000, CRC(c8b09803) SHA1(30579364d224e8422941acd15b8c7cfc5ffc0e8d) )
	ROM_LOAD16_BYTE( "880606_ic32 low _27c512_ m1r.bin",  0x00000, 0x10000, CRC(ad3d3c21) SHA1(6f0e4778edc48017dcd813c2688967e362c14328) )

	ROM_REGION( 0x8000, "tgrom", ROMREGION_ERASE00 )
	ROM_LOAD( "mb83512-15p-259.ic30", 0x0000, 0x8000, NO_DUMP )
	ROM_IGNORE( 0x8000 )

	ROM_REGION( 0x4000, "vdf1", ROMREGION_ERASE00 )
	ROM_LOAD( "upd23c512-039.ic26", 0x0000, 0x4000, NO_DUMP )
	ROM_CONTINUE( 0x0000, 0x4000 )
	ROM_CONTINUE( 0x0000, 0x4000 )
	ROM_IGNORE( 0x4000 )

	ROM_REGION( 0x4000, "vdf2", ROMREGION_ERASE00 )
	ROM_LOAD( "upd23c512-039.ic20", 0x0000, 0x4000, NO_DUMP )
	ROM_CONTINUE( 0x0000, 0x4000 )
	ROM_CONTINUE( 0x0000, 0x4000 )
	ROM_CONTINUE( 0x0000, 0x4000 )
ROM_END

ROM_START( korgm1rex )
	ROM_REGION16_LE( 0x20000, "ipl", 0 )
	ROM_LOAD16_BYTE( "880512 _ic23 high _27c512_ m1rex.bin", 0x00001, 0x10000, CRC(67059dc6) SHA1(91c387689323f6bf9a03ba18df76f29106f87e4a) )
	ROM_LOAD16_BYTE( "880612 _ic32 low _27c512_ m1rex.bin",  0x00000, 0x10000, CRC(056112b2) SHA1(4d7b552034b1a6f09d7ff30b74f4351ddecc2e24) )

	ROM_REGION( 0x8000, "tgrom", ROMREGION_ERASE00 )
	ROM_LOAD( "mb83512-15p-259.ic30", 0x0000, 0x8000, NO_DUMP )
	ROM_IGNORE( 0x8000 )

	ROM_REGION( 0x4000, "vdf1", ROMREGION_ERASE00 )
	ROM_LOAD( "upd23c512-039.ic26", 0x0000, 0x4000, NO_DUMP )
	ROM_CONTINUE( 0x0000, 0x4000 )
	ROM_CONTINUE( 0x0000, 0x4000 )
	ROM_IGNORE( 0x4000 )

	ROM_REGION( 0x4000, "vdf2", ROMREGION_ERASE00 )
	ROM_LOAD( "upd23c512-039.ic20", 0x0000, 0x4000, NO_DUMP )
	ROM_CONTINUE( 0x0000, 0x4000 )
	ROM_CONTINUE( 0x0000, 0x4000 )
	ROM_CONTINUE( 0x0000, 0x4000 )
ROM_END

ROM_START( korgm1p1 )
	ROM_REGION16_LE( 0x20000, "ipl", 0 )
	ROM_LOAD16_BYTE( "plus-1_lsb.bin", 0x00000, 0x10000, CRC(13a2add2) SHA1(7993ace91a10f0f523113538bb7be6fa29ad52f8) )
	ROM_LOAD16_BYTE( "plus-1_msb.bin", 0x00001, 0x10000, CRC(ce6d8473) SHA1(eb45dde07d78ed086da01bb0431c1d8d2dc9a15e) )

	ROM_REGION( 0x8000, "tgrom", 0 )
	ROM_LOAD( "ic30.bin", 0x0000, 0x8000, CRC(8b23ba0f) SHA1(4759e8918961f080f2607d8213a789c7a81228d0) )
	ROM_IGNORE( 0x8000 )

	ROM_REGION( 0x4000, "vdf1", 0 )
	ROM_LOAD( "ic26.bin", 0x0000, 0x4000, CRC(8b23ba0f) SHA1(4759e8918961f080f2607d8213a789c7a81228d0) )
	ROM_CONTINUE( 0x0000, 0x4000 )
	ROM_CONTINUE( 0x0000, 0x4000 )
	ROM_IGNORE( 0x4000 )

	ROM_REGION( 0x4000, "vdf2", 0 )
	ROM_LOAD( "ic20.bin", 0x0000, 0x4000, CRC(8b23ba0f) SHA1(4759e8918961f080f2607d8213a789c7a81228d0) )
	ROM_CONTINUE( 0x0000, 0x4000 )
	ROM_CONTINUE( 0x0000, 0x4000 )
	ROM_CONTINUE( 0x0000, 0x4000 )

	ROM_REGION16_LE( 0x400000, "pcm", 0 )
	ROM_LOAD16_BYTE( "ic36.bin", 0x000000, 0x080000, CRC(5a3adc4c) SHA1(964fd245e46b4adf63b9b03b0490117b6df5e272) )
	ROM_LOAD16_BYTE( "ic40.bin", 0x000001, 0x080000, CRC(77ac122b) SHA1(aa5a99ac697d673cf758eae323cbaeca0bb6f243) )
	ROM_LOAD16_BYTE( "ic37.bin", 0x100000, 0x080000, CRC(fe15f7ca) SHA1(aa339c0972662d38773cdb533ceb0feb30b62eda) )
	ROM_LOAD16_BYTE( "ic41.bin", 0x100001, 0x080000, CRC(e0dce6ad) SHA1(6952b5b8c2a72c3a515444f1d1841dc27fadd62a) )
	ROM_LOAD16_BYTE( "ic38.bin", 0x200000, 0x080000, CRC(14554a91) SHA1(d2ecd6e7a8fdf6b52fc083628b92233a13db8198) )
	ROM_LOAD16_BYTE( "ic42.bin", 0x200001, 0x080000, CRC(bf404899) SHA1(9bda08cf5bddb733f203ed2f6622f18d9635d575) )
	ROM_LOAD16_BYTE( "ic39.bin", 0x300000, 0x080000, CRC(ef8324c4) SHA1(6cbbd8421f777f3d39644474daac2b15f9636810) )
	ROM_LOAD16_BYTE( "ic43.bin", 0x300001, 0x080000, CRC(77c39278) SHA1(bf87f16fa6af1a3076c19c12901b02cbefac3fce) )

	// TODO: move to SW list
	ROM_REGION( 0x8000, "beecard", 0 )
	ROM_LOAD( "plus-1_prog_card.bin", 0x0000, 0x8000, CRC(c31b3bce) SHA1(ef341868800bc98c9677f6f9227e8a2342504391) )

	ROM_REGION( 0x400000, "multisound", 0 )
	ROM_LOAD( "u201.bin", 0x000000, 0x100000, CRC(19fc1c37) SHA1(2badf71d0f104341436f4a35b1255b2c4d3e67bf) )
	ROM_LOAD( "u202.bin", 0x100000, 0x100000, CRC(bb272f7e) SHA1(4c73c97dc313313ec2142e5b73defd38d1fcbddf) )
	ROM_LOAD( "u203.bin", 0x200000, 0x100000, CRC(65eb825d) SHA1(8289b06759cf6e4eafe04c940f11312466352e14) )
	ROM_LOAD( "u204.bin", 0x300000, 0x100000, CRC(ff8d6307) SHA1(3021fe2ce88884e57b397bc5dfb4b29afc56eec4) )
ROM_END

} // anonymous namespace


SYST(1988, korgm1,    0,      0, korgm1, korgm1,  korgm1_state, empty_init, "Korg",                 "M1 Music Workstation (Rev 19)",    MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
SYST(1988, korgm1ex,  korgm1, 0, korgm1, korgm1,  korgm1_state, empty_init, "Korg",                 "M1 EX Music Workstation (v1.29)",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
SYST(1988, korgm1r,   korgm1, 0, korgm1, korgm1r, korgm1_state, empty_init, "Korg",                 "M1R Music Workstation (v1.06)",    MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
SYST(1988, korgm1rex, korgm1, 0, korgm1, korgm1r, korgm1_state, empty_init, "Korg",                 "M1R EX Music Workstation (v1.12)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
SYST(1993, korgm1p1,  korgm1, 0, korgm1, korgm1,  korgm1_state, empty_init, "InVision Interactive", "M1 Plus+1 Music Workstation",      MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



korgtriton.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Antonio "Willy" Malara

/*
Korg TRITON skeleton driver

CPU: SH2 SH7043 / SH7045
FLASH: 2 * MX29F1610
LCD controller: M66271FP
Floppy disk controller: HD63266
Serial Interface: uPD71051
SCSI controller: MB86604L
Sound: 2 * "TGL96" MB87F1710-PFV-S (proprietary)

This driver runs the embedded firmware with skeleton devices
until the boot splash screen is shown.
*/


#include "emu.h"
#include "cpu/sh/sh7042.h"
#include "machine/i8251.h"
#include "screen.h"

#include <algorithm>

#define VERBOSE (LOG_GENERAL)
#include "logmacro.h"

namespace {

class korgtriton_state : public driver_device
{
public:
	korgtriton_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, "ram")
		, m_lcdcm(*this, "lcdcm")
		, m_screen(*this, "screen")
		, m_scu(*this, "scu")
	{ }

	void korgtriton(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	static inline constexpr unsigned SCREEN_WIDTH = 320;
	static inline constexpr unsigned SCREEN_HEIGHT = 240;

	required_device<sh7043_device> m_maincpu;
	required_shared_ptr<u32> m_ram;
	required_shared_ptr<u32> m_lcdcm;
	required_device<screen_device> m_screen;
	required_device<i8251_device> m_scu;

	u8 m_lcdcio[SCREEN_WIDTH / 8 * SCREEN_HEIGHT] = { }; // 4 bytes per pixel

	// SCU: hack to provide data that makes the software proceed,
	//      instead of using the actual device, see also comment
	//      in map() below
	u8 m_scu_hack_status_idx = 0;
	u8 m_scu_hack_data_idx = 0;

	u16 m_tgl[0x1000] = { };

	u8 m_moss_hack_idx = 0;

	void map(address_map &map) ATTR_COLD;

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	u32 pa_r();
	void pa_w(u32 data);

	u32 pe_r();
	void pe_w(u32 data);

	void scu_txrdy_w(int state);
	void scu_rxrdy_w(int state);

	u8 scu_r(offs_t offs);
	void scu_w(offs_t offs, u8 data);

	u8 lcdcio_r(offs_t offs);
	void lcdcio_w(offs_t offs, u8 data);

	u8 tgl_r(offs_t offs);
	void tgl_w(offs_t offs, u8 data);

	u8 moss_r(offs_t offs);
	void moss_w(offs_t offs, u8 data);
};

static INPUT_PORTS_START(korgtriton)
INPUT_PORTS_END

void korgtriton_state::machine_start()
{
}

void korgtriton_state::machine_reset()
{
	m_scu_hack_status_idx = 0;
	m_scu_hack_data_idx = 0;

	std::fill(std::begin(m_tgl), std::end(m_tgl), 0);
	std::fill(std::begin(m_lcdcio), std::end(m_lcdcio), 0);
}

void korgtriton_state::map(address_map &map)
{
	// on chip rom
	map(0x000000, 0x03ffff).rom().region("maincpu", 0);

	// cs0 space (32 bits access)
	// to SAMPLING interface

	// cs1 space (32 bits access)
	map(0x400000, 0x7fffff).rom(); // FLASH

	// cs2 space (16 bits access)
	// map(0x800000, 0x8fffff).ram(); // SPC
	map(0x900000, 0x9fffff).ram().share(m_lcdcm);
	map(0xa00000, 0xafffff).rw(FUNC(korgtriton_state::lcdcio_r), FUNC(korgtriton_state::lcdcio_w));
	map(0xb00000, 0xbfffff).rw(FUNC(korgtriton_state::tgl_r), FUNC(korgtriton_state::tgl_w));
	map(0xd00000, 0xdfffff).rw(FUNC(korgtriton_state::moss_r), FUNC(korgtriton_state::moss_w));

	// map(0xe00000, 0xefffff).ram(); // FDC

	// TODO: figure out why clocking the SCU device from port E is not working
	// map(0xf00000, 0xf00001).rw(m_scu, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xf00000, 0xf00001).rw(FUNC(korgtriton_state::scu_r), FUNC(korgtriton_state::scu_w));

	// System DRAM
	map(0x01000000, 0x01ffffff).ram().share(m_ram);
}

void korgtriton_state::korgtriton(machine_config &config)
{
	SH7043(config, m_maincpu, 7_MHz_XTAL * 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &korgtriton_state::map);

	m_maincpu->read_porta().set(FUNC(korgtriton_state::pa_r));
	m_maincpu->write_porta().set(FUNC(korgtriton_state::pa_w));
	m_maincpu->read_porte().set(FUNC(korgtriton_state::pe_r));
	m_maincpu->write_porte().set(FUNC(korgtriton_state::pe_w));

	// 320x240 screen, should be a simple framebuffer at LCDCIO
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(0));
	m_screen->set_size(SCREEN_WIDTH, SCREEN_HEIGHT);
	m_screen->set_visarea_full();
	m_screen->set_screen_update(FUNC(korgtriton_state::screen_update));

	I8251(config, m_scu, 1021800);
	m_scu->txrdy_handler().set(FUNC(korgtriton_state::scu_txrdy_w));
	m_scu->rxrdy_handler().set(FUNC(korgtriton_state::scu_rxrdy_w));
}

u32 korgtriton_state::pa_r()
{
	return 0;
}

void korgtriton_state::pa_w(u32 data)
{
	LOG("pa_w: %08x\n", data);
}

u32 korgtriton_state::pe_r()
{
	return 0;
}

void korgtriton_state::pe_w(u32 data)
{
	LOG("pe_w: %08x\n", data);
}

u32 korgtriton_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const int byte_width = SCREEN_WIDTH / 8;

	for (int y = 0; y < SCREEN_HEIGHT; ++y)
	{
		u32 *const dst = &bitmap.pix(y);
		for (int x_byte = 0; x_byte < byte_width; ++x_byte)
		{
			const u8 byte = m_lcdcio[y * byte_width + x_byte];

			for (int bit = 0; bit < 8; ++bit)
			{
				const int x = x_byte * 8 + (7 - bit); // MSB first
				const u32 color = BIT(byte, bit) ? 0xffffffff : 0xff000000;
				dst[x] = color;
			}
		}
	}
	return 0;
}

u8 korgtriton_state::lcdcio_r(offs_t offs)
{
	return (offs < sizeof(m_lcdcio)) ? m_lcdcio[offs] : 0;
}

void korgtriton_state::lcdcio_w(offs_t offs, u8 data)
{
	if (offs < std::size(m_lcdcio))
		m_lcdcio[offs] = data;
}

void korgtriton_state::scu_txrdy_w(int state)
{
	LOG("upd71051_txrdy_w: %d\n", state);
}

void korgtriton_state::scu_rxrdy_w(int state)
{
	LOG("upd71051_rxrdy_w: %d\n", state);
}

u8 korgtriton_state::scu_r(offs_t offs)
{
	int res = 0;

	if (offs == 0)
	{
		const static u8 data[] = { 0x66, 0x31 };
		res = data[m_scu_hack_data_idx & 1];
		if (!machine().side_effects_disabled())
			m_scu_hack_data_idx++;
	}

	if (offs == 1)
	{
		res = m_scu_hack_status_idx;
		if (!machine().side_effects_disabled())
			m_scu_hack_status_idx++;
	}

	LOG("scu_read: %08x -> %02x\n", offs, res);
	return res;
}

void korgtriton_state::scu_w(offs_t offs, u8 data)
{
	LOG("scu_write: %08x %02x\n", offs, data);
}

u8 korgtriton_state::tgl_r(offs_t offs)
{
	int res = 0;

	if (offs < std::size(m_tgl))
		res = m_tgl[offs];

	if (offs == 0)
		res = 4;

	LOG("tgl_read: %08x -> %02x\n", offs, res);
	return res;
}

void korgtriton_state::tgl_w(offs_t offs, u8 data) {
	LOG("tgl_write: %08x %02x\n", offs, data);

	if (offs == 0x609 && data == 0x01)
		data = 0;

	if (offs == 0xe09 && data == 0x01)
		data = 0;

	if (offs < std::size(m_tgl))
		m_tgl[offs] = data;
}

u8 korgtriton_state::moss_r(offs_t offs)
{
	if (!machine().side_effects_disabled())
		m_moss_hack_idx++;

	return m_moss_hack_idx;
}

void korgtriton_state::moss_w(offs_t offs, u8 data)
{
}

// The following rom images are taken from the Triton OS floppy disks.
// The ROM files are missing the first two bytes (checksum) present in
// the files obtainable from floppy disks online.

ROM_START( korgtriton )
	ROM_REGION( 0x00800000, "maincpu", 0 )
	ROM_LOAD("int01092.710", 0x00000000, 0x00008000, CRC(135bfd09) SHA1(8e57c6d8460801ebcce22310cecf90eb6a807099))
	ROM_LOAD("int13092.710", 0x00008000, 0x00018000, CRC(d1b8ce69) SHA1(7cff969ed4c663c3aca89014dd776e780b23d81e))

	ROM_LOAD("ext03092.710", 0x00400000, 0x000c0000, CRC(84166ac3) SHA1(e53be4deb982621365463f9f90b20e55160d1c19))
	ROM_LOAD("ext34092.710", 0x004c0000, 0x00100000, CRC(bd949d24) SHA1(509461138549921d1e3b4e266f81b9dbd5d71ade))
	ROM_LOAD("ext74092.710", 0x005c0000, 0x00100000, CRC(e50c0186) SHA1(def100dc06dc05dd09ba41524f2843f1e778c504))
	ROM_LOAD("extb1092.710", 0x006c0000, 0x00040000, CRC(d49503f8) SHA1(47280c1cd423e1046cca31fd125cdae54c46b8f1))
ROM_END

ROM_START( korgtritona )
	ROM_REGION( 0x00800000, "maincpu", 0 )
	ROM_LOAD("int01088.710", 0x00000000, 0x00008000, CRC(135bfd09) SHA1(8e57c6d8460801ebcce22310cecf90eb6a807099))
	ROM_LOAD("int13088.710", 0x00008000, 0x00018000, CRC(d1b8ce69) SHA1(7cff969ed4c663c3aca89014dd776e780b23d81e))

	ROM_LOAD("ext03088.710", 0x00400000, 0x000c0000, CRC(ceb89b44) SHA1(63b03113bf355b59fdd8c19c2d00410f9def3289))
	ROM_LOAD("ext34088.710", 0x004c0000, 0x00100000, CRC(eb16fc15) SHA1(b8b08668c9bc5407838974b09d9357762909c12e))
	ROM_LOAD("ext74088.710", 0x005c0000, 0x00100000, CRC(c6c4bc87) SHA1(9dd42877b989653fe1fd4afae8240dc2c9852fae))
	ROM_LOAD("extb1088.710", 0x006c0000, 0x00040000, CRC(dd234fd3) SHA1(80e315a95932ed5cb079f65c018b8b9188f9eaa9))
ROM_END

ROM_START( korgtritonb )
	ROM_REGION( 0x00800000, "maincpu", 0 )
	ROM_LOAD("int01080.710", 0x00000000, 0x00008000, CRC(ef0d7b3a) SHA1(5b20814dbd118df746437a5aa0b9bc371b8bada0))
	ROM_LOAD("int13080.710", 0x00008000, 0x00018000, CRC(295bd0f8) SHA1(5f21cbaa95d9622aa0cfcf8b453d8c98b5dc54cd))

	ROM_LOAD("ext03080.710", 0x00400000, 0x000c0000, CRC(12e681b1) SHA1(fb3299cffd8fc11ccbf5d7228e1d35f8a824579c))
	ROM_LOAD("ext34080.710", 0x004c0000, 0x00100000, CRC(a84ea53c) SHA1(c949bef347f0cf02a16a6a332aeedcff31c6b956))
	ROM_LOAD("ext74080.710", 0x005c0000, 0x00100000, CRC(281cef3b) SHA1(f348fb53f2ce954fda8004d310566bec663374f4))
	ROM_LOAD("extb1080.710", 0x006c0000, 0x00040000, CRC(83fdb6dd) SHA1(c0b2573484bbdc5261e1aece42f995e8700a55ab))
ROM_END

} // anonymous namespace

SYST(1999, korgtriton,  0,          0, korgtriton, korgtriton, korgtriton_state, empty_init, "Korg", "Triton Music Workstation/Sampler (v2.5.3)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
SYST(1999, korgtritona, korgtriton, 0, korgtriton, korgtriton, korgtriton_state, empty_init, "Korg", "Triton Music Workstation/Sampler (v2.5.0)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
SYST(1999, korgtritonb, korgtriton, 0, korgtriton, korgtriton, korgtriton_state, empty_init, "Korg", "Triton Music Workstation/Sampler (v2.0.0)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



korgws.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Korg WaveStation vector synthesizer.

****************************************************************************/

#include "emu.h"
#include "cpu/h16/hd641016.h"
#include "cpu/m6502/m3745x.h"
//#include "video/t6963c.h"


namespace {

class korgws_state : public driver_device
{
public:
	korgws_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ksp(*this, "ksp")
	{
	}

	void korgws(machine_config &config);
	void korgwssr(machine_config &config);

private:
	void h16_map(address_map &map) ATTR_COLD;
	void wssr_map(address_map &map) ATTR_COLD;

	required_device<hd641016_device> m_maincpu;
	required_device<m3745x_device> m_ksp;
};


void korgws_state::h16_map(address_map &map)
{
	map(0x000000, 0x000007).rom().region("sysroms", 0); // reset vectors
	map(0xc00000, 0xc3ffff).rom().region("sysroms", 0);
}

void korgws_state::wssr_map(address_map &map)
{
	map(0x000000, 0x000007).rom().region("sysroms", 0); // reset vectors
	map(0xc00000, 0xc7ffff).rom().region("sysroms", 0);
}


static INPUT_PORTS_START(korgws)
INPUT_PORTS_END

void korgws_state::korgws(machine_config &config)
{
	HD641016(config, m_maincpu, 20_MHz_XTAL); // HD641016CP10
	m_maincpu->set_addrmap(AS_PROGRAM, &korgws_state::h16_map);

	M37450(config, m_ksp, 20_MHz_XTAL / 2).set_disable(); // M37450M4-233FP
}

void korgws_state::korgwssr(machine_config &config)
{
	korgws(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &korgws_state::wssr_map);
}

ROM_START(korgwsex)
	ROM_REGION16_BE(0x40000, "sysroms", 0) // original EPROM type is NEC D27C1000A-12
	ROM_LOAD16_BYTE("wsex_319_u18.bin", 0x00000, 0x20000, CRC(623eba63) SHA1(5c5e0842d74a9fefbdea01817325a3349f6f9dd6))
	ROM_LOAD16_BYTE("wsex_319_u19.bin", 0x00001, 0x20000, CRC(c7b00f02) SHA1(196e4e403cbae728ec21d27d7b5762963e559028))

	ROM_REGION(0x2000, "ksp", 0)
	ROM_LOAD("m37450m4-233fp.ic34", 0x0000, 0x2000, NO_DUMP)

	ROM_REGION(0x200000, "exroms", 0)
	ROM_LOAD("ws3p7.bin", 0x000000, 0x80000, CRC(ac2e21e3) SHA1(57cd4701a5ca91a74536d7f7ead4074cfe605607))
	ROM_LOAD("ws3p8.bin", 0x080000, 0x80000, CRC(7480bacf) SHA1(8d6e61c11ba909767413df2cae09259e6d8b1476))
	ROM_LOAD16_BYTE("ws3p9.bin", 0x100000, 0x80000, CRC(80a23b7b) SHA1(10b1831b1c2d5d63dd9f4e74cfcfb73c2862f59e))
	ROM_LOAD16_BYTE("ws4p0.bin", 0x100001, 0x80000, CRC(adf239cf) SHA1(a2236f1b0f63c3f854f8ec8751aeb9e167ea0f47))
ROM_END

ROM_START(korgwsad)
	ROM_REGION16_BE(0x40000, "sysroms", 0)
	ROM_SYSTEM_BIOS(0, "v125", "v1.25 Firmware")
	ROMX_LOAD("wsad_1p25_910205l.ic41", 0x00000, 0x20000, CRC(22e5631e) SHA1(ea51e2f2e9effc3985d547d60b109c40e93d4d33), ROM_BIOS(0) | ROM_SKIP(1)) // 27C1000-15
	ROMX_LOAD("wsad_1p25_910305r.ic40", 0x00001, 0x20000, CRC(6a201cbe) SHA1(ee4194baec787d112795b2f34fb21d752c592179), ROM_BIOS(0) | ROM_SKIP(1)) // MBM27C1000-15z
	ROM_SYSTEM_BIOS(1, "v118", "v1.18 Firmware")
	ROMX_LOAD("wsad_118_ic204", 0x00000, 0x20000, CRC(ee3fd19e) SHA1(726c7c3cc201d3360d6274118c13871f8aa46fbd), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("wsad_118_ic304", 0x00001, 0x20000, CRC(4ad559a6) SHA1(f0db05586e7c7be0ce93edd1e07e83d27b69b661), ROM_BIOS(1) | ROM_SKIP(1))

	ROM_REGION(0x2000, "ksp", 0)
	ROM_LOAD("m37450m4-233fp.bin", 0x0000, 0x2000, NO_DUMP)
ROM_END

ROM_START(korgwssr)
	ROM_REGION16_BE(0x80000, "sysroms", 0)
	ROM_LOAD16_BYTE("920305.ic22", 0x00000, 0x40000, CRC(8307a5da) SHA1(31ce491afb74ac1dfa20ae7320b8d1370fa93698))
	ROM_LOAD16_BYTE("920405.ic23", 0x00001, 0x40000, CRC(2d367253) SHA1(c0f8a104a036dd39d220491cc15713158ab3d62a))

	ROM_REGION(0x2000, "ksp", 0)
	ROM_LOAD("m37450m4-233fp.bin", 0x0000, 0x2000, NO_DUMP)
ROM_END

} // anonymous namespace


SYST(1992, korgwsex, 0, 0, korgws,   korgws, korgws_state, empty_init, "Korg", "WaveStation EX", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1991, korgwsad, 0, 0, korgws,   korgws, korgws_state, empty_init, "Korg", "WaveStation A/D", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1992, korgwssr, 0, 0, korgwssr, korgws, korgws_state, empty_init, "Korg", "WaveStation SR", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



korgz3.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Korg Z3 synthesizer.

****************************************************************************/

#include "emu.h"
#include "cpu/m6800/m6801.h"
#include "cpu/nec/nec.h"
#include "machine/adc0808.h"
#include "machine/nvram.h"
#include "machine/pic8259.h"
#include "sound/ymopz.h"
#include "speaker.h"


namespace {

class korgz3_state : public driver_device
{
public:
	korgz3_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_synthcpu(*this, "synthcpu")
		, m_adc(*this, "adc")
		, m_p5(0)
		, m_adc_port(0)
	{
	}

	void korgz3(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void p5_w(u8 data);
	u8 adc_port_r();
	void adc_port_w(u8 data);

	void main_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void synth_map(address_map &map) ATTR_COLD;

	required_device<v30_device> m_maincpu;
	required_device<hd6301y_cpu_device> m_synthcpu;
	required_device<adc0808_device> m_adc;

	u8 m_p5;
	u8 m_adc_port;
};

void korgz3_state::machine_start()
{
	save_item(NAME(m_p5));
	save_item(NAME(m_adc_port));
}


void korgz3_state::p5_w(u8 data)
{
	m_adc->start_w(BIT(data, 5));
	if (BIT(data, 6) && !BIT(m_p5, 6))
		m_adc->address_w(m_adc_port & 0x07);

	m_p5 = data;
}

u8 korgz3_state::adc_port_r()
{
	if (BIT(m_p5, 7))
		return m_adc->data_r();

	return 0xff;
}

void korgz3_state::adc_port_w(u8 data)
{
	m_adc_port = data;
}


void korgz3_state::main_map(address_map &map)
{
	map(0x00000, 0x07fff).ram();
	map(0xfc000, 0xfffff).rom().region("v30_program", 0xc000);
}

void korgz3_state::io_map(address_map &map)
{
	map(0x0010, 0x0013).rw("pic", FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
}

void korgz3_state::synth_map(address_map &map)
{
	map(0x2000, 0x2000).nopr();
	map(0x3800, 0x3801).rw("ymsnd", FUNC(ym2414_device::read), FUNC(ym2414_device::write));
	map(0x4000, 0x7fff).ram().share("nvram");
	map(0x8000, 0xffff).rom().region("hd6303_program", 0);
}


static INPUT_PORTS_START(korgz3)
INPUT_PORTS_END

void korgz3_state::korgz3(machine_config &config)
{
	// All clocks unknown

	V30(config, m_maincpu, 8'000'000); // D70116C-8
	m_maincpu->set_addrmap(AS_PROGRAM, &korgz3_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &korgz3_state::io_map);
	m_maincpu->set_irq_acknowledge_callback("pic", FUNC(pic8259_device::inta_cb));

	pic8259_device &pic(PIC8259(config, "pic")); // not visible on PCB; part of MB603112 ASIC?
	pic.out_int_callback().set_inputline(m_maincpu, 0);

	HD6303Y(config, m_synthcpu, 12'000'000); // HD63C03YP
	m_synthcpu->set_addrmap(AS_PROGRAM, &korgz3_state::synth_map);
	m_synthcpu->out_p5_cb().set(FUNC(korgz3_state::p5_w));
	m_synthcpu->in_p6_cb().set(FUNC(korgz3_state::adc_port_r));
	m_synthcpu->out_p6_cb().set(FUNC(korgz3_state::adc_port_w));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // D43256AC-12LL + battery

	M58990(config, m_adc, 1'000'000); // M58990P-1

	SPEAKER(config, "speaker", 2).front();

	ym2414_device &ymsnd(YM2414(config, "ymsnd", 3'579'545)); // YM2414B
	ymsnd.add_route(0, "speaker", 0.60, 0);
	ymsnd.add_route(1, "speaker", 0.60, 1);
}

ROM_START(korgz3)
	ROM_REGION16_LE(0x10000, "v30_program", 0)
	ROM_LOAD16_BYTE("881405.ic5", 0x00000, 0x08000, CRC(16467e6f) SHA1(b83e10609ef0a2b095f73b1580810736fd56f693))
	ROM_LOAD16_BYTE("881505.ic4", 0x00001, 0x08000, CRC(9899a1de) SHA1(99dafee8061d3397b99f39ae74ae684fb5a1b495))

	ROM_REGION(0x8000, "hd6303_program", 0)
	ROM_LOAD("881605.ic13", 0x0000, 0x8000, CRC(39ca77fa) SHA1(b9073ef1dfad7f9d07558d2389875ebe26835068))
ROM_END

} // anonymous namespace


SYST(1988, korgz3, 0, 0, korgz3, korgz3, korgz3_state, empty_init, "Korg", "Z3 Guitar Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



koto_zevio.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

// The Zevio SoC was developed by Koto Laboratory, the same company behind the Wonderswan
// is it related to Ponto-1 in epoch_tv_globe.cpp, as Koto is credited there too
#include "emu.h"

#include "cpu/arm7/arm7.h"

#include "screen.h"
#include "speaker.h"


namespace {

class zevio_state : public driver_device
{
public:
	zevio_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
	{ }

	void zevio(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	uint32_t z900b0014_r();
	uint32_t zb8000024_r();

	void arm_map(address_map &map) ATTR_COLD;
};

uint32_t zevio_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void zevio_state::machine_start()
{
}

void zevio_state::machine_reset()
{
}

uint32_t zevio_state::z900b0014_r()
{
	return machine().rand();
}

uint32_t zevio_state::zb8000024_r()
{
	return machine().rand();
}

static INPUT_PORTS_START( zevio )
INPUT_PORTS_END

void zevio_state::arm_map(address_map &map)
{
	map(0x00000000, 0x007fffff).rom().region("maincpu", 0);

	map(0x10000000, 0x10ffffff).ram();

	map(0x900a0f04, 0x900a0f07).nopw();
	map(0x900b0014, 0x900b0017).r(FUNC(zevio_state::z900b0014_r));

	map(0xb8000024, 0xb8000027).r(FUNC(zevio_state::zb8000024_r));

	map(0xb8000800, 0xb8000fff).ram();
}


void zevio_state::zevio(machine_config &config)
{
	ARM9(config, m_maincpu, 72000000); // unknown ARM core, unknown frequency
	m_maincpu->set_addrmap(AS_PROGRAM, &zevio_state::arm_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320, 262);
	m_screen->set_visarea(0, 320-1, 0, 240-1);
	m_screen->set_screen_update(FUNC(zevio_state::screen_update));

	SPEAKER(config, "speaker", 2).front();
}


// ドラゴンボールＺ スカウターバトル体感かめはめ波 おらとおめえとスカウター
ROM_START( dbzscout )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "mr27t6402l.ic6", 0x000000, 0x800000, CRC(9cb896d6) SHA1(4185ee4593c2ef3b637f6004d1f80dadd4530902) )
ROM_END

ROM_START( dbzonep )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "mr27t6402l.u1", 0x000000, 0x800000, CRC(57f7c319) SHA1(65118a9c61defc75cefe5e45062c0a4788e2a26c) )
	// original dump had the first 0x100 bytes repeated again at the end, why?

	ROM_REGION( 0x800, "eeprom", ROMREGION_ERASEFF )
	ROM_LOAD( "s24cs16a.u6", 0x000000, 0x800, CRC(a1724ea8) SHA1(93a6f73e30f47b6a0c83f62dfd9d8236473518a8) )
ROM_END

} // anonymous namespace

CONS( 2007, dbzscout,     0,              0,      zevio, zevio, zevio_state, empty_init, "Bandai / Koto", "Dragon Ball Z: Scouter Battle Taikan Kamehameha: Ora to Omee to Scouter (Japan)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2008, dbzonep,      0,              0,      zevio, zevio, zevio_state, empty_init, "Bandai / Koto", "Dragon Ball Z x One Piece: Battle Taikan Gum-Gum no Kamehameha: Omee no Koe de Ora o Yobu (Japan)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



kramermc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

Kramer MC driver by Miodrag Milanovic

2008-09-13 Preliminary driver.

This is a homebrew computer by Manfred Kramer, of Germany. The "Y23VO" that
appears at startup was the author's amateur radio callsign at the time. (It
changed after reunification.)

The computer was intended to have a CTC, a SIO, and a FDC (uPD765A), however
there's no code to make use of them. The system PIO is intended for
keyboard, serial, and beeper.

Commands:
A : Assembler
B : Basic
C : Copy
D : Dump (Hex dump of memory)
E : End-file
F : Fill
G : Go
H : Hex Arithmetic
I : Input device (IT or IL or IU)
J : Jump ram
K : Ram test
L : List device (LT or LL or LU)
M : Move
O : Output device (OT or OL or OU)
P : Disassemble
R : Read external storage (expects intel format from the keyboard)
S : Substitute (edit ram content)
T : Text editor
V : Verify
W : Write (sends bits to the PIO port A)
X : Show primary registers
Z : Show secondary registers

Device choices: T (terminal), L (cassette), U (user device)

There's numerous bugs in the rom's keyboard routine. Shift has to be pressed
and released before the key you want to shift. Lowercase cannot be input. The
Ctrl key doesn't work. The . , / keys are incorrectly already shifted.

Even though cassette and terminal support are supposed to exist, the device
redirection commands do nothing.

The natural keyboard has been added, although shifted characters will not
work, or for paste either, because of the shift-key bug. Further, paste
drops every second character.

****************************************************************************/


#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/z80pio.h"
#include "screen.h"
#include "emupal.h"
#include "sound/spkrdev.h"
#include "speaker.h"


namespace {

class kramermc_state : public driver_device
{
public:
	kramermc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_gfxdecode(*this, "gfxdecode")
		, m_videoram(*this, "videoram")
		, m_speaker(*this, "speaker")
		, m_palette(*this, "palette")
		, m_io_keyboard(*this, "LINE%u", 0)
	{ }

	void kramermc(machine_config &config);

	void init_kramermc();

private:
	u8 m_porta = 0U;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	uint32_t screen_update_kramermc(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	uint8_t port_a_r();
	uint8_t port_b_r();
	void port_a_w(uint8_t data);
	required_device<cpu_device> m_maincpu;
	required_device<gfxdecode_device> m_gfxdecode;
	required_shared_ptr<u8> m_videoram;
	required_device<speaker_sound_device> m_speaker;
	required_device<palette_device> m_palette;
	required_ioport_array<8> m_io_keyboard;
	void kramermc_io(address_map &map) ATTR_COLD;
	void kramermc_mem(address_map &map) ATTR_COLD;
};


/* Address maps */
void kramermc_state::kramermc_mem(address_map &map)
{
	map(0x0000, 0x03ff).rom();  // Monitor
	map(0x0400, 0x07ff).rom();  // Debugger
	map(0x0800, 0x0bff).rom();  // Reassembler
	map(0x0c00, 0x0fff).ram();  // System RAM
	map(0x1000, 0x7fff).ram();  // User RAM
	map(0x8000, 0xafff).rom();  // BASIC
	map(0xc000, 0xc3ff).rom();  // Editor
	map(0xc400, 0xdfff).rom();  // Assembler
	map(0xfc00, 0xffff).ram().share("videoram");  // Video RAM
}

void kramermc_state::kramermc_io(address_map &map)
{
	map.global_mask(0xff);
	map(0xfc, 0x0ff).rw("pio", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
}

/* Input ports */
static INPUT_PORTS_START( kramermc )
	PORT_START("LINE0")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_BACKSLASH)     // It cancels the input line
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("<") PORT_CODE(KEYCODE_COMMA) PORT_CHAR('<') PORT_CHAR(',')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)   // If pressed twice, prints a graphic minus
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_START("LINE1")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('P')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('O')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)  // Prints underscore
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('K')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('L')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_START("LINE2")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('I')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('U')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('N')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('H')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('M')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('J')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR(39)
	PORT_START("LINE3")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('T')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('V')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('G')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_START("LINE4")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('S')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_START("LINE5")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('W')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR(34)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_START("LINE6")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(32)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl")  PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)  // Prints a graphic cube
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(">") PORT_CODE(KEYCODE_STOP) PORT_CHAR('>') PORT_CHAR('.')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('?') PORT_CHAR('/')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("=") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('-')
	PORT_START("LINE7")
		PORT_BIT(0xFF, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

uint8_t kramermc_state::port_a_r()
{
	return m_porta;
}

uint8_t kramermc_state::port_b_r()
{
	u8 key_row = (m_porta >> 1) & 7;
	return m_io_keyboard[key_row]->read();
}

void kramermc_state::port_a_w(uint8_t data)
{
	m_porta = data;
	m_speaker->level_w(BIT(data, 5));
}

void kramermc_state::machine_start()
{
	save_item(NAME(m_porta));
}

void kramermc_state::machine_reset()
{
	m_porta = 0xff;
}

const gfx_layout kramermc_charlayout =
{
	8, 8,               /* 8x8 characters */
	256,                /* 256 characters */
	1,                /* 1 bits per pixel */
	{0},                /* no bitplanes; 1 bit per pixel */
	{0, 1, 2, 3, 4, 5, 6, 7},
	{0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8},
	8*8                 /* size of one char */
};

static GFXDECODE_START( gfx_kramermc )
	GFXDECODE_ENTRY( "chargen", 0x0000, kramermc_charlayout, 0, 1 )
GFXDECODE_END

uint32_t kramermc_state::screen_update_kramermc(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for(u8 y = 0; y < 16; y++ )
		for(u8 x = 0; x < 64; x++ )
			m_gfxdecode->gfx(0)->opaque(bitmap,cliprect, m_videoram[y*64+x], 0, 0, 0, x*8, y*8);

	return 0;
}

/* Machine driver */
void kramermc_state::kramermc(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 1500000);
	m_maincpu->set_addrmap(AS_PROGRAM, &kramermc_state::kramermc_mem);
	m_maincpu->set_addrmap(AS_IO, &kramermc_state::kramermc_io);

	z80pio_device& pio(Z80PIO(config, "pio", 1500000));
	pio.in_pa_callback().set(FUNC(kramermc_state::port_a_r));
	pio.out_pa_callback().set(FUNC(kramermc_state::port_a_w));
	pio.in_pb_callback().set(FUNC(kramermc_state::port_b_r));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(64*8, 16*8);
	screen.set_visarea(0, 64*8-1, 0, 16*8-1);
	screen.set_screen_update(FUNC(kramermc_state::screen_update_kramermc));
	screen.set_palette(m_palette);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_kramermc);

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.5);
}

/* ROM definition */
ROM_START( kramermc )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "io-mon.kmc",   0x0000, 0x0400, CRC(ba230fc8) SHA1(197d4ede31ee8768dd4a17854ee21c468e98b3d6) )
	ROM_LOAD( "debugger.kmc", 0x0400, 0x0400, CRC(5ea3d9e1) SHA1(42e5ced4f965124ae50ec7ac9861d6b668cfab99) )
	ROM_LOAD( "reass.kmc",    0x0800, 0x0400, CRC(7cc8e605) SHA1(3319a96aad710441af30dace906b9725e07ca92c) )
	ROM_LOAD( "basic.kmc",    0x8000, 0x3000, CRC(7531801e) SHA1(61d055495ffcc4a281ef0abc3e299ea95f42544b) )
	ROM_LOAD( "editor.kmc",   0xc000, 0x0400, CRC(2fd4cb84) SHA1(505615a218865aa8becde13848a23e1241a14b96) )
	ROM_LOAD( "ass.kmc",      0xc400, 0x1c00, CRC(9a09422e) SHA1(a578d2cf0ea6eb35dcd13e4107e15187de906097) )

	ROM_REGION(0x0800, "chargen",0)
	ROM_LOAD ("chargen.kmc",  0x0000, 0x0800, CRC(1ba52f9f) SHA1(71bbad90dd427d0132c871a4d3848ab3d4d84b8a) )
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT           COMPANY           FULLNAME       FLAGS */
COMP( 1987, kramermc, 0,      0,      kramermc, kramermc, kramermc_state, empty_init, "Manfred Kramer", "Kramer MC", MACHINE_SUPPORTS_SAVE )




krokha.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************

    Кроха (Krokha, "Tiny"), TV game by Контур (SKB Kontur).
    It was apparently only for SKB Kontur workers, not sold in stores.

    К580ВМ80А @ 2MHz, 2KB RAM. Screen is 48x32 monochrome text.
    Joystick, built-in 1-bit speaker.

    Only known cartridge has 5 built-in games:
    - Breakout
    - Tetris
    - Snake
    - Xonix
    - Air Defence

    https://zx-pk.ru/threads/26306-igrovaya-pristavka-quot-krokha-quot.html
        discussion

    http://www.nedopc.org/forum/viewtopic.php?f=90&t=11458
        discussion + schematics

    http://alemorf.ru/comps/kroha/index.html
        photos

    TODO:
    - second joystick?
    - video timing

****************************************************************************/

#include "emu.h"

#include "cpu/i8085/i8085.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

namespace {

class krokha_state : public driver_device
{
public:
	krokha_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_p_videoram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
		, m_speaker(*this, "speaker")
	{ }

	static constexpr feature_type imperfect_features() { return feature::CONTROLS; }

	void krokha(machine_config &config);

private:
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void krokha_mem(address_map &map) ATTR_COLD;

	void status_callback(uint8_t data);
	void speaker_w(uint8_t data);

	required_device<i8080a_cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_shared_ptr<u8> m_p_videoram;
	required_region_ptr<u8> m_p_chargen;
	required_device<speaker_sound_device> m_speaker;
};

//

void krokha_state::status_callback(uint8_t data)
{
	if (data & i8080a_cpu_device::STATUS_INTA)
	{
		// interrupt acknowledge
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
	}
}

void krokha_state::speaker_w(uint8_t data)
{
	m_speaker->level_w(BIT(data, 1));
}

//

void krokha_state::krokha_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom();
	map(0xe000, 0xe7ff).ram().mirror(0x0800).share("videoram");
	map(0xf7ff, 0xf7ff).portr("P1").w(FUNC(krokha_state::speaker_w));
}

static INPUT_PORTS_START( krokha )
	PORT_START("P1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

uint32_t krokha_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (int y = 0; y < 32; y++)
	{
		for (int x = 0; x < 64; x++)
		{
			for (int ra = 0; ra < 8; ra++)
			{
				uint8_t gfx = m_p_chargen[m_p_videoram[x << 5 | y] << 3 | ra];
				for (int i = 0; i < 8; i++)
				{
					int dx = x << 3 | i;
					int dy = y << 3 | ra;
					if (cliprect.contains(dx, dy))
						bitmap.pix(dy, dx) = BIT(gfx, i ^ 7);
				}
			}
		}
	}

	return 0;
}


void krokha_state::krokha(machine_config &config)
{
	I8080A(config, m_maincpu, 8_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &krokha_state::krokha_mem);
	m_maincpu->out_status_func().set(FUNC(krokha_state::status_callback));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	m_screen->set_size(64 * 8, 32 * 8);
	m_screen->set_visarea(16 * 8, 64 * 8 - 1, 0 * 8, 32 * 8 - 1);
	m_screen->set_screen_update(FUNC(krokha_state::screen_update));
	m_screen->set_palette("palette");
	m_screen->screen_vblank().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	PALETTE(config, "palette", palette_device::MONOCHROME);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.25);
}

ROM_START( krokha )
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("bios.bin", 0x0000, 0x2000, CRC(e37556f4) SHA1(b1da9d7338eb227b0aff5675719f7a2aab607e66))

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD("font.bin", 0x0000, 0x0800, CRC(2f4fcfb5) SHA1(175cafe3dc9291f505d69aced9c405c38b7f7086))
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT  COMP  MACHINE  INPUT   CLASS         INIT        COMPANY        FULLNAME  FLAGS
CONS( 1990, krokha,  0,      0,    krokha,  krokha, krokha_state, empty_init, "SKB Kontur",  "Krokha", MACHINE_SUPPORTS_SAVE )



kron.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Joakim Larsson Edstrom
/***************************************************************************
 *
 *  KRON K-180 VGA terminal server
 *
 *  27/10/2015
 *
 * I bought this hardware on Ebay to have something with a Z80 CPU to play with.
 * The hardware is a serial terminal controller with VGA output and a PC keyboard
 * and was manufactured mid 90s by a company from Vinnitsa, Ukraine called KRON.
 * There is a character generator with support for both western and cyrilic characters.
 * The PCB is also filled with chips with cyrrilic characters on but thanks to this
 * page I managed to translate most of them into western TTL logic names:
 *
 * - http://ganswijk.home.xs4all.nl/chipdir/soviet/
 * - https://frakaday.blogspot.se/p/kron180.html - schematics and more info
 *
 * +-----||||||||||||||||||||-|||||||||||----|||||||||||||||||||-|||||||||-||||||||+
 * |     |     RS232C       | |Serial PN|    | CENTRONICS PN   | | VGA   | |KEYBRD||
 * |     +----------------XP1 +-------XP2    +---------------XS3 +-----XS2 +----XS1|
 * |                      +---DD27 +-----DD20 +-------DD34   +----DD3 +-------DD33 |
 * |                      | 1488|  |CD75189|  | 74299   |    | 7407 | | 74244.1 |  |
 * |                      +-----+  +-------+  +---------+    +------+ +---------+  |
 * |                                           +------DD17   +----DD9              |
 * |                               +-----DD35  | 74670   |   | 7403 |              |
 * |     +--------------------DD10 | 7474  |  ++------DD12  ++----DD14  +-----DD13 |
 * |     |   Z8018006PSC         | +-------+  | 74374    |  | 74151 |   | 74174 |  |
 * |     |   Z180 MPU            |            +----------+  +-------+   +-------+  |
 * |     +-----------------------+ +-----DD36                           +------DD6 |
 * |                               |7432.1 |  +-------DD2    +----DD7   | 7474  |  |
 * |                               +-------+  | 74374   |    |7474.1|   +-------+  |
 * |                                          +---------+    +------+              |
 * |                              +------DD15   +-----DD1    +----DD19             |
 * |                              | 74138  |    | 74166 |    |7474.2|              |
 * |+-------BQ1 +-------------DD4 +--------+    +-------+    +------+              |
 * ||XTAL     | |               |  +-----DD16   +-----DD18      +-DD28  ..-^-..    |
 * ||12.280MHz| | NM27C512      |  | 7432  |    | 74670 |   93C46CB1| /         \  |
 * |+---------+ +---------------+  +-----DD37   +-------+   EEPROM--+/  Beeper   \ |
 * |+------DD26                    | 7400  |     +----DD32  +----DD23|     O     | |
 * || 74299   |                    +-------+     | 7474  |  | 74259 |\   BQ2     / |
 * |+---------+  +------------DD5                +-------+  +-------+ \         /  |
 * |+---------+  | HY6264A      |  +-----DD8     +----DD31   +---DD25  ''--_--''   |
 * || 74374   |  | 8Kb SRAM     |  | 7408  |     | 7474  |   | 7414 |           +XS4
 * |+------DD24  +--------------+  +-------+     +-------+   +------+           |P |
 * |                DD30-------+   +-----DD29 +---------BQ3  +---DD11           |W |
 * |                 | 74244   |   | 74393 |  |XTL 29.3MHz|  | 7404 |           |R |
 * +-----------------+---------+---+-------+--+-----------+--+------+--------------+
 *
 * 74299 - 8 bit bidirectional universal shift/storage register
 * 7407  - 6 open collector drivers
 * 74244 - Octal buffers/line drivers/line receivers
 * 74670 - 4 x 4 register file with 3-state outputs
 * 74151 - 1 of 8 demultiplexor
 * 74374 - octal D type flip flops
 * 74174 - 6 D type  flip flops with clear
 * 75189 - quadruple line receivers
 * 74395 - 4 bit universal shift registers
 * 74393 - dual 4 bit counters
 * 74259 - 8 bit adressable latches
 * 74166 - 8 bit shift register
 *
 *
 * Keyboard interface
 * ------------------
 * XT: 1 start bit + 8 data bits @ ~2kbps
 * AT: 1 start bit + 8 data bits + 1 odd parity bit @ ~10kbps
 *
 * Pin 1 CLK/CTS -
 * Pin 2 RxD   (AT:+ TxD/RTS) - via R to GND + Pin 4 (out) 7407 + pin 11 (DS0) 74299 8 bit shift register
 *       u                                 Pin 3 (in)  7407 + pin 11 (INT2) CPU + pin 1 74299
 *  1         3                            + pin 4 (*PRE) 7474.2 + pin 6 (*Q) 7474.2
 *    4  2  5                              Pin 3 (CLK) 7474.2 + pin 8 (4Y) 7414
 *                                         Pin 2 (D) 7474.2 + Pin 17 (Q7 serial out) 74299
 * Pin 3 Reset (AT:+ NC)
 * Pin 4 GND                  - GND
 * Pin 5 +5v                  - VCC
 *
 * Identified chips
 * -----------------
 * Z180 MPU (Z8018006PCS)
 * NM27C512Q 64Kb EPROM, but A15 tied to +5v and schematics supports only 27256
 * HY6264A 8Kb SRAM
 * 93C46B1 128 bytes EEPROM
 *
 * Misc findings
 * --------------
 * - $17B9 might be keyboard input routine
 * - indentified used OUT ports: $00, $02, $04, $07, $08, $0A, $0C, $0E, $0F, $40, $60
 * - identified used IN ports: $10 (keyboard?), $30
 * - screen memory at 0x8600
 * - each position has 2 bytes <character> + <mode>
 * - mode 0x08 is double height
 * - characters seems to follow IBM PC Code page 437 for opening screen
 * - terminal defaults to cyrillic characterset possibly due to setting in EEPROM
 * - http://www.phantom.sannata.ru/forum/index.php?t=5200 - Kron-2 for sale
 * - http://f-picture.net/fp/3b2a0496b981437a9c3f90ed236363c9 - Picture of Kron-2
 * - http://www.kron.com.ua/
 */

#include "emu.h"
#include "cpu/z180/z180.h"
#include "machine/pckeybrd.h"
#include "emupal.h"
#include "screen.h"

#define LOG_IO     (1U << 1)
#define LOG_SCAN   (1U << 2)
#define LOG_SCREEN (1U << 3)
#define LOG_KBD    (1U << 4)
#define LOG_READ   (1U << 5)
#define LOG_CS     (1U << 6)

//#define VERBOSE (LOG_IO)
//#define LOG_OUTPUT_STREAM std::cout

#include "logmacro.h"

#define LOGIO(...)     LOGMASKED(LOG_IO,     __VA_ARGS__)
#define LOGSCAN(...)   LOGMASKED(LOG_SCAN,   __VA_ARGS__)
#define LOGSCREEN(...) LOGMASKED(LOG_SCREEN, __VA_ARGS__)
#define LOGKBD(...)    LOGMASKED(LOG_KBD,    __VA_ARGS__)
#define LOGR(...)      LOGMASKED(LOG_READ,   __VA_ARGS__)
#define LOGCS(...)     LOGMASKED(LOG_CS,     __VA_ARGS__)

#ifdef _MSC_VER
#define FUNCNAME __func__
#else
#define FUNCNAME __PRETTY_FUNCTION__
#endif


namespace {

class kron180_state : public driver_device
{
public:
	kron180_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_chargen(*this, "chargen")
		, m_vram(*this, "videoram")
		, m_keyboard(*this, "pc_keyboard")
	{ }

	void kron180(machine_config &config);

private:
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void keyb_interrupt(int state);
	void sn74259_w(offs_t offset, uint8_t data) { LOGIO("%s %02x = %02x\n", FUNCNAME, offset & 0x07, offset & 0x08 ? 1 : 0); }
	void ap5_w(offs_t offset, uint8_t data) { LOGIO("%s %02x = %02x\n", FUNCNAME, offset, data); }
	uint8_t ap5_r(offs_t offset) { LOGIO("%s() %02x = %02x\n", FUNCNAME, offset, 1); return 1; }
	void wkb_w(offs_t offset, uint8_t data) { LOGIO("%s %02x = %02x\n", FUNCNAME, offset, data); }
	void sn74299_w(offs_t offset, uint8_t data) { LOGIO("%s %02x = %02x\n", FUNCNAME, offset, data); }
	uint8_t sn74299_r(offs_t offset) { LOGIO("%s() %02x = %02x\n", FUNCNAME, offset, 1); return 1; }
	void txen_w(offs_t offset, uint8_t data) { LOGIO("%s %02x = %02x\n", FUNCNAME, offset, data); }
	void kbd_reset_w(offs_t offset, uint8_t data) { LOGIO("%s %02x = %02x\n", FUNCNAME, offset, data); }
	void dreq_w(offs_t offset, uint8_t data) { LOGIO("%s %02x = %02x\n", FUNCNAME, offset, data); }
	void kron180_iomap(address_map &map) ATTR_COLD;
	void kron180_mem(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_region_ptr<uint8_t> m_chargen;
	required_shared_ptr<uint8_t> m_vram;
	required_device<pc_keyboard_device> m_keyboard;
	uint8_t m_kbd_data = 0;
};

void kron180_state::kron180_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).rom().region("roms", 0x8000);
	map(0x8000, 0x85ff).ram().mirror(0x6000);
	map(0x8600, 0x9fff).ram().share(m_vram).mirror(0x6000);
}

/*   IO decoding
 *
 *   A7 A6 A5 A4 A3 A2 A1 A0
 *    0  x  x  x  x  x  x  x - 74138 selected
 *    0  0  0  0  x  x  x  x - 74259 selected
 *    0  0  0  0  D  0  0  0 - EEPROM Data In
 *    0  0  0  0  D  0  0  1 - COMDTR
 *    0  0  0  0  D  0  1  0 - EEPROM CS
 *    0  0  0  0  D  0  1  1 - Vertical Sync
 *    0  0  0  0  D  1  0  0 - TBRQ and BLANK set
 *    0  0  0  0  D  1  0  1 - BLINK
 *    0  0  0  0  D  1  1  0 - CSTR
 *    0  0  0  0  D  1  1  1 - EEPROM CLK
 *    0  0  0  1  x  x  x  x - AP5
 *    0  0  1  0  x  x  x  x - WKB
 *    0  0  1  1  x  x  x  x - 74299 G1+G2
 *    0  1  0  0  x  x  x  x - Reset of 74299
 *    0  1  0  1  x  x  x  x - Enable TX?
 *    0  1  1  0  x  x  x  x - Reset KBD
 *    0  1  1  1  x  x  x  x - DKA/DREQ0 Z180 = D0
 *
 * Now, in parallel there is a lot of stuff going on in the upper I/O address lines
 * they are driving the character generator and some other signals
 *  A19 - not available on the DIP64 package
 *  A18 - multiplexed pin used as Tout pulsing the VT1 signal
 *  A17 - WA on DD17 (74670) and DD18 (74670)
 *  A16 - WB on DD 17 (74670) and DD18 (74670)
 *
 * At the moment we emulate the screen at a high level so I just disregard the special functions on A16 - A18 by mirroring the mapping below
 */
void kron180_state::kron180_iomap(address_map &map)
{
	map.global_mask(0xff);
	map(0x0000, 0x000f).w(FUNC(kron180_state::sn74259_w));
	map(0x0010, 0x001f).rw(FUNC(kron180_state::ap5_r), FUNC(kron180_state::ap5_w));
	map(0x0020, 0x002f).w(FUNC(kron180_state::wkb_w));
	map(0x0030, 0x003f).r(FUNC(kron180_state::sn74299_r));
	map(0x0040, 0x004f).w(FUNC(kron180_state::sn74299_w));
	map(0x0050, 0x005f).w(FUNC(kron180_state::txen_w));
	map(0x0060, 0x006f).w(FUNC(kron180_state::kbd_reset_w));
	map(0x0070, 0x007f).w(FUNC(kron180_state::dreq_w));
}

/* Input ports */
static INPUT_PORTS_START (kron180)
INPUT_PORTS_END

/* Video TODO: find and understand the char table within main rom */
uint32_t kron180_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	LOGSCREEN("%s()\n", FUNCNAME);
	int vramad = 0;
	for (int row = 0; row < 25 * 8; row += 8)
	{
		uint8_t charcode;
		for (int col = 0; col < 80 * 8; col += 8)
		{
			/* look up the character data */
			charcode = m_vram[vramad];
			if (VERBOSE && charcode != 0x20 && charcode != 0) LOGSCREEN("\n %c at X=%d Y=%d: ", charcode, col, row);
			uint8_t const *chardata = &m_chargen[(charcode * 8) + 8];
			/* plot the character */
			for (int y = 0; y < 8; y++)
			{
				chardata--;
				if (VERBOSE && charcode != 0x20 && charcode != 0) LOGSCREEN("\n  %02x: ", *chardata);
				for (int x = 0; x < 8; x++)
				{
					if (VERBOSE && charcode != 0x20 && charcode != 0) LOGSCREEN(" %02x: ", *chardata);
					bitmap.pix(row + (8 - y), col + (8 - x)) = (*chardata & (1 << x)) ? 1 : 0;
				}
			}
			vramad += 2;
		}
		if (VERBOSE && charcode != 0x20 && charcode != 0) LOGSCREEN("\n");
		vramad += 96; // Each row is aligned at a 128 byte boundary
	}

	return 0;
}

/* Interrupt Handling */
#if 0
void kron180_state::irq0_ack_w(uint8_t data)
{
	m_irq0_ack = data;
	if ((data & 1) == 1)
		m_maincpu->set_input_line(0, CLEAR_LINE);
}

INTERRUPT_GEN_MEMBER(kron180_state::interrupt)
{
	if ((m_irq0_ack & 1) == 1)
	{
		device.execute().set_input_line(0, ASSERT_LINE);
	}
}
#endif

void kron180_state::keyb_interrupt(int state)
{
	if(state && (m_kbd_data = m_keyboard->read()))
	{
		LOGKBD("%s(%02x)\n", FUNCNAME, m_kbd_data);
		m_maincpu->set_input_line(2, ASSERT_LINE);
		/* TODO: store and present this to K180 in a good way. */
	}
}

/*
 * Machine configuration
 */
void kron180_state::kron180(machine_config &config)
{
	/* basic machine hardware */
	HD64180RP(config, m_maincpu, XTAL(12'288'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &kron180_state::kron180_mem);
	m_maincpu->set_addrmap(AS_IO, &kron180_state::kron180_iomap);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_refresh_hz(50);
	screen.set_screen_update(FUNC(kron180_state::screen_update));
	screen.set_size(80 * 10, 24 * 10);
	screen.set_visarea(0, 639, 0, 199); // TODO: This need to be fixed once the real char table is used...
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* keyboard TODO: fix it, doesn't work yet */
	PC_KEYB(config, m_keyboard);
	m_keyboard->keypress().set(FUNC(kron180_state::keyb_interrupt));
}

/* ROM definitions */
ROM_START (kron180)
	ROM_REGION(0x10000, "roms", 0)
	ROM_LOAD ("k180dd4-2.8m.bin", 0x000000, 0x10000, CRC (ae0642ad) SHA1 (2c53a714de6af4b64e46fcd34bca6d4438511765))

	ROM_REGION(0x1000, "chargen",0) /* TODO: This character rom is taken from ibmjr rom set and will be replaced */
	ROM_LOAD( "cga.chr", 0x0000, 0x1000, CRC(42009069) SHA1(ed08559ce2d7f97f68b9f540bddad5b6295294dd) )
ROM_END

} // anonymous namespace


/* Driver */
//     YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY     FULLNAME      FLAGS
COMP ( 1995, kron180, 0,      0,      kron180, kron180, kron180_state, empty_init, "Kron Ltd", "Kron K-180", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



krypton_regency.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Krypton Regency (model 933)

It was manufactured by Timorite, Ltd. (Eric White's company), the chess engine is
by Gyula Horváth, similar to the one in CXG Sphinx Legend.

To start a new game, keep holding the NEW GAME button until the display says HELLO.

Hardware notes (Systema Challenge fitted with Regency MCU):
- PCB label: LCD CHESS 938, JAN.1994, REV.1, EB-093801-01
- Hitachi H8/3256 MCU, 20MHz XTAL
- LCD with 5 7segs and custom segments
- piezo, 16 LEDs (optional), button sensors chessboard

A26 MCU was used in:
- Krypton Regency (with or without LEDs)
- Excalibur Avenger (suspected, Excalibur brand Comet, with newer MCU)
- Excalibur Legend III (suspected, Excalibur brand Regency)
- Systema Challenge (1996 version)

TODO:
- CXG Sphinx Legend may be on the same hardware? if so, move driver to cxg folder
- is Krypton a product brand, or a company alias for the Chinese factory behind it?
- it does a cold boot at every reset, so nvram won't work properly unless MAME
  adds some kind of auxillary autosave state feature at power-off

*******************************************************************************/

#include "emu.h"

#include "cpu/h8/h8325.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "krypton_regency.lh"


namespace {

class regency_state : public driver_device
{
public:
	regency_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_led_pwm(*this, "led_pwm"),
		m_lcd_pwm(*this, "lcd_pwm"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_out_lcd(*this, "s%u.%u", 0U, 0U)
	{ }

	void regency(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(in1_changed) { update_irq2(); }

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<h83256_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_led_pwm;
	required_device<pwm_display_device> m_lcd_pwm;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<3> m_inputs;
	output_finder<2, 24> m_out_lcd;

	u16 m_inp_mux = 0;
	u8 m_inp_mux2 = 0;
	u32 m_lcd_segs = 0;
	u8 m_lcd_com = 0;

	// I/O handlers
	void lcd_pwm_w(offs_t offset, u8 data);
	void update_lcd();
	template <int N> void lcd_segs_w(u8 data);

	void standby(int state);
	int update_irq2();

	void p2_w(u8 data);
	void p5_w(offs_t offset, u8 data, u8 mem_mask);
	u8 p6_r();
	void p6_w(offs_t offset, u8 data, u8 mem_mask);
	u8 p7_r();
	void p7_w(u8 data);
};

void regency_state::machine_start()
{
	m_out_lcd.resolve();

	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_inp_mux2));
	save_item(NAME(m_lcd_segs));
	save_item(NAME(m_lcd_com));
}



/*******************************************************************************
    I/O
*******************************************************************************/

// power

void regency_state::standby(int state)
{
	// clear display
	if (state)
	{
		m_lcd_pwm->clear();
		m_led_pwm->clear();
	}
}

int regency_state::update_irq2()
{
	// 2nd button row is tied to IRQ2 (used for on/off button)
	int state = (m_inp_mux2 & m_inputs[1]->read()) ? ASSERT_LINE : CLEAR_LINE;
	m_maincpu->set_input_line(INPUT_LINE_IRQ2, state);

	return state;
}


// LCD

void regency_state::lcd_pwm_w(offs_t offset, u8 data)
{
	m_out_lcd[offset & 0x3f][offset >> 6] = data;
}

void regency_state::update_lcd()
{
	u32 lcd_segs = bitswap<24>(m_lcd_segs,1,0, 15,14,13,12,11,10,9,8, 16,17,23,22,21,20,19,18, 25,26,27,28,29,31);

	for (int i = 0; i < 2; i++)
	{
		// LCD common is 0/1/Hi-Z
		const u32 data = BIT(m_lcd_com, i + 2) ? (BIT(m_lcd_com, i) ? ~lcd_segs : lcd_segs) : 0;
		m_lcd_pwm->write_row(i, data);
	}
}

template <int N>
void regency_state::lcd_segs_w(u8 data)
{
	// P1x, P3x, P4x, P6x: LCD segments
	const u8 shift = 8 * N;
	m_lcd_segs = (m_lcd_segs & ~(0xff << shift)) | (data << shift);
	update_lcd();
}


// misc

void regency_state::p2_w(u8 data)
{
	// P20-P27: input mux (chessboard), LED data
	m_inp_mux = (m_inp_mux & 0x300) | (data ^ 0xff);
	m_led_pwm->write_mx(~data);
}

void regency_state::p5_w(offs_t offset, u8 data, u8 mem_mask)
{
	// P50: LCD common 2
	m_lcd_com = (m_lcd_com & 5) | (data << 1 & 2) | (mem_mask << 3 & 8);
	update_lcd();

	// P51: speaker out
	m_dac->write(BIT(~data, 1));

	// P52: input mux part
	m_inp_mux = (m_inp_mux & 0x2ff) | (BIT(~data, 2) << 8);

	// P54,P55: LED select
	m_led_pwm->write_my(~data >> 4 & 3);
}

u8 regency_state::p6_r()
{
	// P65: battery status
	u8 data = m_inputs[2]->read() << 5 & 0x20;

	// P66: IRQ2 pin
	if (!machine().side_effects_disabled() && update_irq2())
		data |= 0x40;

	return ~data;
}

void regency_state::p6_w(offs_t offset, u8 data, u8 mem_mask)
{
	// P60,P61: LCD segs
	lcd_segs_w<0>(data & 3);

	// P62: LCD common 1
	m_lcd_com = (m_lcd_com & 0xa) | (BIT(data, 2)) | (mem_mask & 4);
	update_lcd();

	// P66: input mux part
	m_inp_mux = (m_inp_mux & 0x1ff) | (BIT(~data, 6) << 9);
}

u8 regency_state::p7_r()
{
	// P70-P77: multiplexed inputs
	u8 data = 0;

	// read buttons
	for (int i = 0; i < 2; i++)
		if (BIT(m_inp_mux, i + 8))
			data |= m_inputs[i]->read();

	// read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_file(i ^ 7);

	return ~data;
}

void regency_state::p7_w(u8 data)
{
	// P70-P77: input mux (other way around)
	m_inp_mux2 = ~data;
	update_irq2();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

#define PORT_CHANGED_IN1() \
	PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(regency_state::in1_changed), 0)

static INPUT_PORTS_START( regency )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight / Lose")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Set-Up / Features")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Step Forward")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Multi-Move / Analysis")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook / Win")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn / Rating")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_IN1() PORT_CODE(KEYCODE_F1) PORT_NAME("On / Off")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_IN1() PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_IN1() PORT_CODE(KEYCODE_M) PORT_NAME("Move / Swap Board")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_IN1() PORT_CODE(KEYCODE_O) PORT_NAME("Sound / Style")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_IN1() PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_IN1() PORT_CODE(KEYCODE_H) PORT_NAME("Hint / Info")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_IN1() PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop / Draw")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHANGED_IN1() PORT_CODE(KEYCODE_N) PORT_NAME("New Game / Clear Board")

	PORT_START("IN.2")
	PORT_CONFNAME( 0x01, 0x01, "Battery Status" )
	PORT_CONFSETTING(    0x00, "Low" )
	PORT_CONFSETTING(    0x01, DEF_STR( Normal ) )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void regency_state::regency(machine_config &config)
{
	// basic machine hardware
	H83256(config, m_maincpu, 20_MHz_XTAL);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(h83256_device::nvram_set_battery));
	m_maincpu->standby_cb().append(FUNC(regency_state::standby));
	m_maincpu->write_port1().set(FUNC(regency_state::lcd_segs_w<2>));
	m_maincpu->read_port2().set_constant(0xef); // hardware config?
	m_maincpu->write_port2().set(FUNC(regency_state::p2_w));
	m_maincpu->write_port3().set(FUNC(regency_state::lcd_segs_w<1>));
	m_maincpu->write_port4().set(FUNC(regency_state::lcd_segs_w<3>));
	m_maincpu->read_port5().set_constant(0xff);
	m_maincpu->write_port5().set(FUNC(regency_state::p5_w));
	m_maincpu->read_port6().set(FUNC(regency_state::p6_r));
	m_maincpu->write_port6().set(FUNC(regency_state::p6_w));
	m_maincpu->read_port7().set(FUNC(regency_state::p7_r));
	m_maincpu->write_port7().set(FUNC(regency_state::p7_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	//m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_lcd_pwm).set_size(2, 24);
	m_lcd_pwm->output_x().set(FUNC(regency_state::lcd_pwm_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/5, 697/5);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_led_pwm).set_size(2, 8);
	config.set_default_layout(layout_krypton_regency);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( regency )
	ROM_REGION16_BE( 0xc000, "maincpu", 0 )
	ROM_LOAD("1996_933_timorite_hd6433256a26p.ic1", 0x0000, 0xc000, CRC(72eb3f2b) SHA1(30e4166e351210475cf9709b0feb717d9d3ac747) )

	ROM_REGION( 109652, "screen", 0 )
	ROM_LOAD("regency.svg", 0, 109652, CRC(6840c49e) SHA1(a9c91143c5bea5ab41fe323e719da4a46ab9d631) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1996, regency, 0,      0,      regency, regency, regency_state, empty_init, "Krypton / Timorite", "Regency", MACHINE_SUPPORTS_SAVE )



krz2000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    krz2000.cpp - Kurzweil K2000 series

    Skeleton driver by R. Belmont
    Additional improvements by Peter Sobot

    Hardware in brief:
        TMP68301 CPU @ 16 MHz (with debug port on RXD2/TXD2)
        uPD72064 FDC
        85C30 SCSI (53C80 on schematic?)
        M37450 @ 10 MHz on I/O board to handle panel/display/keyboard scanning, LED control, LCD contrast, and the audio switching matrix
         (16K of ROM stored on U69, 384 bytes of RAM, 8 A/D inputs, 2 D/A outputs, 6 ports of I/O, and a 31.25Kbaud serial link to the maincpu)
        HD6303 on I/O board to manage reverb DSP program loading
        VLSI ASIC "Calvin" - Handles maincpu and PCM ROM/RAM addressing, as well as resampling (and possibly DRAM refresh)
         (on later K2000 and all later K-series units this is replaced by a more advanced VLSI ASIC "Janis")
         (service manual says "sample fetching tasks")
        2x VLSI ASIC "Hobbes" - specialized DSP processors for sound synthesis (oscillators, filters, etc)
         (service manual says "wave form generation and DSP functions")
         (each Hobbes chip handles a subset of available voices, and seems independently programmable)
        Digitech DSP256 Multi Effects VLSI Chip (reverb, chorus, delay, etc)
         (EPROM seems avaialble on eBay, but the chip itself is quite custom)

***************************************************************************/

#include "emu.h"

#include "bus/midi/midi.h"
#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"
#include "cpu/m6502/m3745x.h"
#include "cpu/m68000/tmp68301.h"
#include "imagedev/floppy.h"
#include "machine/bankdev.h"
#include "machine/ncr5380.h"
#include "machine/upd765.h"
#include "video/t6963c.h"

#include "emupal.h"
#include "screen.h"
#include "softlist.h"
#include "speaker.h"

namespace {

class k2000_state : public driver_device
{
public:
	k2000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		// , m_scanner(*this, "scanner")
		, m_1m_bank(*this, "bank1m")
		, m_mainram(*this, "mainram")
		// , m_midi(*this, "midi")
		// , m_fdc(*this, "fdc")
	{ }

	void k2000(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void k2000_map(address_map &map) ATTR_COLD;
	void bank_map_1m(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	// required_device<cpu_device> m_scanner;
	required_device<address_map_bank_device> m_1m_bank;
	required_shared_ptr<uint16_t> m_mainram;
	// optional_device<midi_port_device> m_midi;
	// required_device<upd765a_device> m_fdc;

	void swap_bit_ctrl_w(uint16_t data);
	uint16_t battery_voltage_and_keyscanner_version(offs_t offset);

	uint16_t calvin_read(offs_t offset);
	void calvin_write(offs_t offset, uint16_t data);

	uint16_t hobbes0_read(offs_t offset);
	void hobbes0_write(offs_t offset, uint16_t data);

	uint16_t hobbes1_read(offs_t offset);
	void hobbes1_write(offs_t offset, uint16_t data);

	void k2000_palette(palette_device &palette) const;

	TIMER_CALLBACK_MEMBER(boot_hack_timer);
	emu_timer *m_boot_hack_timer;
};

/**
 * From the K2000/K2000R service manual:
 *
 *  The 68301 processor's 16 megabits (sic) of address space are divided into 16 equal memory blocks,
 *  numbered 0-15. The first time the K2000 is powered up, Block 0 processes data for the boot
 *  EPROM and setup EPROM, while Block 1 processes data for the PSRAM. Many of the K2000's basic
 *  functions utilize the PSRAM, which accesses the higher address space of Block 1 in the CPU.
 *  This slows down the access time somewhat, since the address space of the CPU is accessed in
 *  sequence, beginning with Block 0.
 *
 *  To optimize the processing speed, the K2000 performs a one-time software operation when
 *  powered up for the first time. The memory management unit (MMU) in the gate array logic chip
 *  (GAL) located at U35 on the engine board executes a "swap bit" function which makes Block 0
 *  of the CPU responsible for the PSRAM, and Block 1 responsible for the Boot EPROM and Setup
 *  EPROM. Consequently the PSRAM functions are processed as early as possible.
 *
 * Note this "16 equal memory blocks" idea is really just the top hex digit of the address -
 * i.e.: 0x?FFFFF
 */
void k2000_state::swap_bit_ctrl_w(uint16_t data)
{
	data &= 0xff;
	logerror("%02x written to swap_bit_ctrl_w\n", data);
	// bit 4:
	//  0 = program ROM at 0x000000, work RAM    at 0x100000
	//  1 = work RAM    at 0x000000, program ROM at 0x100000
	m_1m_bank->set_bank((data >> 4) & 1);
}

uint16_t k2000_state::battery_voltage_and_keyscanner_version(offs_t offset)
{
	logerror("reading battery voltage and keyscanner version at %02x\n", offset);
	uint8_t battery_voltage = 0x36;
	uint8_t keyscanner_version = 0x12;
	return (((uint16_t) battery_voltage) << 8) | ((uint16_t) keyscanner_version);
}

uint16_t k2000_state::calvin_read(offs_t offset)
{
	logerror("Read from Calvin at %06x\n", offset);
	return 0;
}

void k2000_state::calvin_write(offs_t offset, uint16_t data)
{
	logerror("Write to Calvin at %06x = %04x\n", offset, data);
}

uint16_t k2000_state::hobbes0_read(offs_t offset)
{
	logerror("Read from Hobbes[0] at %06x\n", offset);
	return 0;
}

void k2000_state::hobbes0_write(offs_t offset, uint16_t data)
{
	logerror("Write to Hobbes[0] at %06x = %04x\n", offset, data);
}


uint16_t k2000_state::hobbes1_read(offs_t offset)
{
	logerror("Read from Hobbes[1] at %06x\n", offset);
	return 0;
}

void k2000_state::hobbes1_write(offs_t offset, uint16_t data)
{
	logerror("Write to Hobbes[1] at %06x = %04x\n", offset, data);
}

void k2000_state::machine_start()
{
	m_boot_hack_timer = timer_alloc(FUNC(k2000_state::boot_hack_timer), this);
	m_boot_hack_timer->adjust(attotime::from_seconds(2), 0, attotime::never);
}

void k2000_state::machine_reset()
{
	m_1m_bank->set_bank(0);
}

TIMER_CALLBACK_MEMBER(k2000_state::boot_hack_timer)
{
	logerror("Jumping to interrupt vector to continue boot sequence");
	m_maincpu->set_pc(m_1m_bank->read32(0x80));
}

void k2000_state::k2000_map(address_map &map)
{
	// word writes to 000000 region - unknown
	// word writes to 000090 region - unknown
	// word writes to 000180 region - unknown
	// word writes to 000240 region - unknown

	// The engine ROMs are mapped onto 0x0 when ENGROMCSb is low, which is IO3 of GODOT (pin 18)
	// The setup ROMs are mapped onto 0x100000 when SUROMCSb is low, which is the IO2 output (pin 17) of GODOT
	// This implies that IO3 of GODOT is low iff CA{20,23} are also low, and IO2 of GODOT is low iff CA{20,23} = 0x01

	// Block 0, Block 1: ROM and RAM (depending on bank switch)
	map(0x000000, 0x1fffff).m(m_1m_bank, FUNC(address_map_bank_device::amap16));

	// Lots of writes to 0x372B68 -> 0x372C8C

	// Block 4: PRAM?
	// "The K2000 comes standard with 116KB, and with the PRAM upgrade, totals 760KB."
	// Another source says:
	//  For the K2000 Series, the total memory allocated to all of these objects is
	//  120K expandable to 760K with the PRAM option. For the K2500 Series, it is 256k,
	//  expandable to 1280k with the PRAM option. For the K2600 Series, it is 486k,
	//  expandable to 1506k with the PRAM option.
	// These are pretty odd numbers - but it sounds like ~8KB of the RAM space is used
	// for OS functions, meaning the built-in RAM might only be 128KB in total.
	// The K2000 schematic shows 2x 1M "NVRAM" chips, one for the low byte, and one for
	// the high byte, which means we should (?) actually have 2 * 1024 * 1024 / 8 = 262,144 bytes?
	//
	// The boot routines seem to write to 0x400000 and compare the value saved there, so
	// this is probably the optional PRAM expansion.
	map(0x400000, 0x400000 + ((2 * 1024 * 1024) / 8) - 1).ram();

	// Init routine sets 0x4BAB44 -> 0x4BFFFA to 0x0000
	// Reads from 0x4BFFFC and 0x4BFFFE seem to happen on startup.
	// HOBBES0CSb = NAND(CS1', CA19)  = high unless CS1 is high and CA19 is high
	// HOBBES1CSb = NAND(CS1', !CA19) = high unless CS1 is high and CA19 is low
	// 0xFFFC00 is the Address Decoder area that controls which part of memory causes CS1 to be "enabled" (low)
	// At runtime,
	//  0xFFFC00 = 0x50 =
	//  0xFFFC01 = 0x3F
	//  0xFFFC02 = 0x00
	//  0xFFFC03 = 0x30
	//   For CS1:
	//  0xFFFC04 = 0x60 = 0b0110 0000 = Start address of the memory area for CS1 = A21 + A22
	//  0xFFFC05 = 0x3F = 0b0011 1111 = Size of memory area = M19 + M18 + M17 + M16 + M15-M9 + M8 = 1MB
	//  0xFFFC06 = 0x00
	//  0xFFFC07 = 0x30 = 0b0011 0000 = Area enabled, use external DTACK
	// By this logic, Hobbes chips are mapped to 0x100000, 0x200000, 0x400000, 0x600000
	// And their address mask is 0x000FFF00, which means the effective ranges are:
	// 0x100???00

	// Hobbes chip selection seems to look at CA19 and CS1

	// Internal sample ROM is switched on SBA20,SBA21, which corresponds to CA21,CA22 (one bit off)
	// meaning the internal address space for accessing the sample ROM is 0x200000 through 0x6fffff (?)
	// Blocks 2, 3, 4, 5, 6
	// map(0x200000, 0x2fffff).rom().region("pcm", 0);        // Block 2: Sample ROM
	// map(0x300000, 0x3fffff).rom().region("pcm", 0x100000); // Block 3: Sample ROM
	// map(0x400000, 0x4fffff).rom().region("pcm", 0x200000); // Block 4: Sample ROM
	// Writes to 0x4FFD82, 0x4FFD88, 0x4FFE24

	// Hypothesis: Calvin (or Janis) is mapped at 0x500000
	//  Writes to 0x500402, 0x500418, 0x50041A early in the boot process
	//  Initialization then writes zeros in a descending ladder:
	//  0x500400, 0x500402
	//  0x5003FC, 0x5003FE, ...
	//  down to 0x500004 (then 0x500006)

	// Calvin memory?
	map(0x500000, 0x5003ff).rw(FUNC(k2000_state::calvin_read), FUNC(k2000_state::calvin_write));
	// Calvin control registers?
	map(0x500400, 0x5004ff).rw(FUNC(k2000_state::calvin_read), FUNC(k2000_state::calvin_write));
	// Reads and writes to 0x580000

	// Hobbes (it seems) is connected in the 0x600000 range
	// The second Hobbes chip (?) seems connected at 0x680000
	// Initialization seems to:
	//  - write to 0x600300, followed by 0x600xxx where xxx is in 0x400 through 0x5FF
	//  - write to 0x6?0002, 0x6?0006, 0x6?0004, through 0x6?000F4
	//  - write to 0x600004, 0x600014, 0x600024... through 0x6002F4
	//    (with a repeating pattern of [0x02, 0x00, 0x26, 0x24, 0x4A, 0x48])
	//  - write to 0x680004, 0x680014, 0x680024... through 0x6802F4
	//    (with a repeating pattern of [0x80, 0x02, 0xA4, 0x26, 0xC6, 0x4A])
	// Each Hobbes chip seems to have two control registers, mapped at 0x600300/0x680300 and 0x600400/0x680400

	// Hobbes memory?
	map(0x600000, 0x6002ff).rw(FUNC(k2000_state::hobbes0_read), FUNC(k2000_state::hobbes0_write));
	// Hobbes control registers?
	map(0x600300, 0x6005ff).rw(FUNC(k2000_state::hobbes0_read), FUNC(k2000_state::hobbes0_write));

	// Hobbes memory?
	map(0x680000, 0x6802FF).rw(FUNC(k2000_state::hobbes1_read), FUNC(k2000_state::hobbes1_write));
	// Hobbes control registers?
	map(0x680300, 0x6805FF).rw(FUNC(k2000_state::hobbes1_read), FUNC(k2000_state::hobbes1_write));

	// SROM - Setup ROM?
	uint32_t srom_base = 0x280000;
	map(srom_base, srom_base + 0x40000 - 1).rom().region("maincpu", 0x100000);

	// Calvin chip has a 20-bit address bus with A0 tied low and A12-15 tied high, giving it access to 0x?0??[0-E]
	// Later models' Janis chips use 24-bit address buses (or at least they're all connected in the schematic)
	// Hobbes has a 10-bit address bus with A0 tied low, giving access to 0x0 through 0x3FF

	// Block 7: LCD control and bank switching
	map(0x700000, 0x700003).rw("lcd", FUNC(lm24014h_device::read), FUNC(lm24014h_device::write)).umask16(0x00ff);

	// Reads from 0x740000 (floppy?)
	// Writes to 0x760000
	// Writes to 0x7C0000

	// Single byte reads seem to happen at 0x740000, which seems to be FDCCTLCSb?
	// map(0x740000, 0x74000f).
	map(0x7e0000, 0x7e0001).w(FUNC(k2000_state::swap_bit_ctrl_w));

	// Some sort of hardware mapping - initial firmware boot expects a 4 at $78000B
	map(0x780000, 0x78ffff).ram();

	// When (CPUASb + GODOT I6) and (GODOT I/O0 + IOCSb) are low,
	// Address bits 17, 18, 19 get demultiplexed onto:
	// 0, 0, 0 => LCDCSb
	// 0, 0, 1 => FDCCSb
	// 0, 1, 0 => FDCCTLCSb
	// 0, 1, 1 => FDCOUTCSb
	// 1, 0, 0 => SCSICSb
	// 1, 0, 1 => SCSIDMACSb
	// 1, 1, 0 => CPUEXPCSb
	// 1, 1, 1 => MCTLREGCSb (OR'ed with WRITELOb to provide input into the Memory Management Control Register)

	// map(0x800000, 0x8fffff); // Block 8: ???
	// map(0x900000, 0x9fffff); // Block 9: ???
	// map(0xa00000, 0xffffff); // Blocks 10, 11, 12, 13, 14: Orchestral ROM
	// map(0x1000000, 0x18fffff); // Blocks 15, 16, 17, 18, 19: Contemporary ROM
}

void k2000_state::bank_map_1m(address_map &map)
{
	map(0x000000, 0x0fffff).rom().region("maincpu", 0);
	map(0x100000, 0x1fffff).ram().share("mainram");

	// The "stride" of this bank map is 0x200000, so this is the second "view" of the same data
	map(0x200000, 0x2fffff).ram().share("mainram");
	map(0x300000, 0x3fffff).rom().region("maincpu", 0);

	// Unsure if the battery voltage and keyscanner data is memory-mapped at these ranges (unlikely)
	// or if this data is written to some time during the boot process. If it's the latter,
	// this will have to change when loading different versions of the ROM (or someone has to figure
	// out where the battery voltage is actually read from).
	map(0x100000 + 0x1052, 0x100000 + 0x1053).r(FUNC(k2000_state::battery_voltage_and_keyscanner_version));
	map(0x200000 + 0x1052, 0x200000 + 0x1053).r(FUNC(k2000_state::battery_voltage_and_keyscanner_version));
}

void k2000_state::k2000(machine_config &config)
{
	TMP68301(config, m_maincpu, XTAL(12'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &k2000_state::k2000_map);

	// MIDI_PORT(config, m_midi, midiin_slot, "midiin");
	// TODO: Uncomment when the TMP68301 has serial/MIDI support, to wire up MIDI I/O and scanner.
	// MIDI_PORT(config, m_midi, midiout_slot, "midiout");

	// M37450(config, m_scanner, XTAL(10'000'000));
	// The "Scanner" sub-CPU is connected to main CPU via:
	//  - MIDI_FM_ENG, going to P34/RxD, which comes from SERTxD, which is INTMIDITxD == TxD1
	//  - 37450_TXD, coming from P35/TxD, which is NAND'd together with 6303_TXD
	//    and inverted to provide SERRxD to the engine (maincpu), which goes to INTMIDIRxD
	//    eventually ending up at RxD1
	// Essentially, the 68k's second built-in serial port runs at 31250Hz, which is MIDI baud rate,
	// which is how it talks to the scanner to tell it to operate the LEDs on the front of the case.
	// The external MIDI port(s) are connected directly to the 68K's first serial port, RxD0 and TxD0.
	// (RxD2 and TxD2 are hooked up to a "debug port" which the manual says requires additional
	// hardware - this is untested.)
	// The scanner's ROM comes from an NTE2764 (U69, 28-pin DIP) EPROM of which 16Kb are used.
	// (Note that on the K2500 and above, the scanner is an M37451M8-FP the ROM is 16KB, but stored on an M27256.)
	// The scanner ROM from the K2500 may be compatible with the K2000, and can be pulled from "U2"
	// on the K2500 Audio Scanner Board (Rev D)

	ADDRESS_MAP_BANK(config, "bank1m").set_map(&k2000_state::bank_map_1m).set_options(ENDIANNESS_BIG, 16, 24, 0x200000);

	// uPD765AC; clocked through SED9420C
	// UPD765A(config, m_fdc, 16_MHz_XTAL / 4, true, true);
	// m_fdc->intrq_wr_callback().set_inputline(m_maincpu, I6);
	// FLOPPY_CONNECTOR(config, "fdc:0", dss1_floppies, "35dd", floppy_image_device::default_floppy_formats).enable_sound(true);
	// FLOPPY_CONNECTOR(config, "fdc:1", dss1_floppies, nullptr, floppy_image_device::default_floppy_formats).enable_sound(true);

	PALETTE(config, "palette", FUNC(k2000_state::k2000_palette), 2);

	LM24014H(config, "lcd");

	SPEAKER(config, "speaker", 2).front();
}

void k2000_state::k2000_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(131, 136, 139));
	palette.set_pen_color(1, rgb_t( 92,  83,  88));
}


static INPUT_PORTS_START( k2000 )
INPUT_PORTS_END

ROM_START( k2000 )
	ROM_REGION(0x140000, "maincpu", 0)
	// note that since this firmware revision ends with a J, it is intended for a K2000 with the "Janis" Sample/Addressing ASIC (the later mainboard)
	// the earlier board has firmware revisions which end with a C, for the "Calvin" Sample/Addressing ASIC, on the older K2000 mainboard
	ROM_LOAD16_BYTE( "k2j-k2rj_eng_hi__v2.0j_3b69__=c=_1993_yca.tms27c040.u6", 0x000000, 0x080000, CRC(35c17fc3) SHA1(b91deec0127669b46af05a2acaa212e29e49abfb) ) // TMS27C040 EPROM with sticker: "K2J-K2RJ ENG HI // V2.0J 3B69 // (C) 1993 YCA" @ U6
	ROM_LOAD16_BYTE( "k2j-k2rj_eng_lo__v2.0j_0db0__=c=_1993_yca.tms27c040.u3", 0x000001, 0x080000, CRC(11c7f436) SHA1(c2afe84b58d71932f223097ea01812eb513bd740) ) // TMS27C040 EPROM with sticker: "K2J-K2RJ ENG LO // V2.0J 0DB0 // (C) 1993 YCA" @ U3
	ROM_LOAD16_BYTE( "k2j-k2rj_su_hi__v12ts_5e89__=c=_1993_yca.m27c1001.u5", 0x100000, 0x020000, CRC(16e0bdb7) SHA1(962fa10896f6a95210d752be28f02640869893a4) ) // MC27C1001 EPROM with sticker: "K2J-K2RJ SU HI // V12TS 5E89 // (C) 1993 YCA" @ U5
	ROM_LOAD16_BYTE( "k2j-k2rj_su_lo__v12ts_2f52__=c=_1993_yca.m27c1001.u2", 0x100001, 0x020000, CRC(cb11e837) SHA1(bcdf3d5abe8c53727a142008acb2755ed0ecc6ea) ) // MC27C1001 EPROM with sticker: "K2J-K2RJ SU LO // V12TS 2F52 // (C) 1993 YCA" @ U2

	ROM_REGION(0x2000000, "pcm", 0) // 23c080(?) 8MBit mask roms, byte-mode, in pairs. These are common on at least several members of the K2000 series; each has a unique number stored in the JEDEC id field.
	ROM_LOAD16_BYTE( "k2m1h_25da__830106-01__=c=1991_yca__japan_9322_d.ide6ed.u38", 0x000001, 0x100000, CRC(f110b0e7) SHA1(d8731b74b1ca6761f8fd3f6360bfe2f1cc3077bc) ) // Silkscreen: "K2M1H 25DA // 830106-01 // (C)1991 YCA // JAPAN 9322 D" @ U38; chip JEDEC id = e6 ed
	ROM_LOAD16_BYTE( "k2m1l_20fe__830107-01__=c=1991_yca__japan_9324_d.idb9a4.u43", 0x000000, 0x100000, CRC(00715fbe) SHA1(99f661096031b794de216c74ce9b780e9889d344) ) // Silkscreen: "K2M1L 20FE // 830107-01 // (C)1991 YCA // JAPAN 9324 D" @ U43; chip JEDEC id = b9 a4
	ROM_LOAD16_BYTE( "k2m2h_de34__830108-01__=c=1991_yca__japan_9324_d.id0000.u39", 0x200001, 0x100000, CRC(99aae00e) SHA1(7045fb6b19b046f3f068a3581b6498ee62603fb4) ) // Silkscreen: "K2M2H DE34 // 830108-01 // (C)1991 YCA // JAPAN 9324 D" @ U39; chip JEDEC id = 00 00
	ROM_LOAD16_BYTE( "k2m2l_1bdf__830109-01__=c=1991_yca__japan_9324_d.id836c.u44", 0x200000, 0x100000, CRC(b2acd497) SHA1(24d3e84016fa08a990ce4c39294ad47fb0cab3d0) ) // Silkscreen: "K2M2L 1BDF // 830109-01 // (C)1991 YCA // JAPAN 9324 D" @ U44; chip JEDEC id = 83 6c
	ROM_LOAD16_BYTE( "k2m3h_0e87__830110-01__=c=1991_yca__japan_9324_d.iddbfa.u40", 0x400001, 0x100000, CRC(f448694f) SHA1(484593d072c43fe442cd8cc6cc40cd24677b35cc) ) // Silkscreen: "K2M3H 0E87 // 830110-01 // (C)1991 YCA // JAPAN 9324 D" @ U40; chip JEDEC id = db fa
	ROM_LOAD16_BYTE( "k2m3l_3cde__830111-01__=c=1991_yca__japan_9329_d.ide8b5.u46", 0x400000, 0x100000, CRC(be8408f9) SHA1(fbeab2d690532d055d424be52d937e2729b3daac) ) // Silkscreen: "K2M3L 3CDE // 830111-01 // (C)1991 YCA // JAPAN 9329 D" @ U46; chip JEDEC id = e8 b5
	ROM_LOAD16_BYTE( "k2m4h_3f2d__830112-01__=c=1991_yca__japan_9324_d.iddbdb.u42", 0x600001, 0x100000, CRC(da8666f5) SHA1(4d0f306cad9a3a96cf1232b9b8df12fae044a1d6) ) // Silkscreen: "K2M4H 3F2D // 830112-01 // (C)1991 YCA // JAPAN 9324 D" @ U42; chip JEDEC id = db db
	ROM_LOAD16_BYTE( "k2m4l_2e6d__830113-01__=c=1991_yca__japan_9327_d.id3199.u47", 0x600000, 0x100000, CRC(6eb73185) SHA1(fe48fe44be90a856251974750b1eac7f5291e1e6) ) // Silkscreen: "K2M4L 2E6D // 830113-01 // (C)1991 YCA // JAPAN 9327 D" @ U47; chip JEDEC id = 31 99

	/* The K2000 can be expanded by installing up to two
	 *  "K2000 Sound Rom Babybd" P/N 331022-01 sound modules, onto a
	 *  "K2000 SOUND ROM DAUGHTERBOARD" RMB-k P/N 331021-03 daughterboard,
	 * as well as upgrading the board firmware to turn the unit into a
	 * K2000R. The shipping 'K2000R' model just needs this daughterboard
	 * to be added. (Did the K2000R come with the daughterboard populated?)
	 * http://tk386.com/k2000_innards/DSC_8139.jpg
	 *
	 * The 'babyboards' have two mezzanine connectors on them and plug into
	 * the daughterboard, which itself connects to the K2000 mainboard via
	 * connector J12 (a 3-row eurocard connector with 16 pins per row).
	 * The babyboard ROMs are 23c1610 16MBit mask ROMs, used in word mode.
	 *
	 * These same 4-ROM expansion modules can be used on the K2000, K2500,
	 * K2600, and is included in the K2661 from factory.
	 *
	 * Note that the K2600 uses a similar part called RMB-26 which has an
	 * additional 2 ROM slots intended for more dense single-chip ROMs.
	 * see http://kurzweil.com/accessory/rmb-26_soundblock_daughterboard/
	 */


	/* 'Orchestral' babyboard AKA "ROM 1" or 'Kurzweil RM-126':
	 * Note: There are several variations on the ROM markings for the ROMs on
	 * this babyboard, depending on what year it was produced.
	 * The contents are presumed (but not proven) to be the same.
	 * The older board variant presumably has a part number of K91B10162.
	 */
	// older board variant:
	// https://images.reverb.com/image/upload/s--DdCT5Kan--/a_180/f_auto,t_large/v1511800096/wiysqmlb9udd4hgxpppz.jpg
	// https://thumbs.worthpoint.com/zoom/images1/1/0716/15/kurzweil-rm1-orchestral-rom-block_1_3dca4ab8eb35682075d5e5cb77e4876c.jpg
	// Silkscreen: "KZK ROM 1-1 // 830114-01 N // (C) 1993 YCA" @ U1
	// Silkscreen: "KZK ROM 1-2 // 830115-01 N // (C) 1993 YCA" @ U2
	// Silkscreen: "KZK ROM 1-3 // 830116-01 N // (C) 1993 YCA" @ U3
	// Silkscreen: "KZK ROM 1-4 // 830117-01 N // (C) 1993 YCA" @ U4
	// newer board variant:
	// https://media.sweetwater.com/api/i/q-82__ha-8bb2b6f5cf9d25a9__hmac-757ef5825b7f607b540327a23b1e1ace1fe15df5/images/items/750/RM126-large.jpg
	ROM_LOAD( "k2xxrom1-1__830114-01n__2003kurzweil__0402.u1", 0x800000, 0x200000, CRC(39b55c26) SHA1(5937365835a9c1038dfd73d8624f4d7ab0ec48fa) ) // Silkscreen: "K2xxROM1-1 // 830114-01N // <square symbol>2003KURZWEIL // 0402" @ U1
	ROM_LOAD( "k2xxrom1-2__830115-01n__2003kurzweil__0402.u2", 0xa00000, 0x200000, CRC(fba51423) SHA1(4cfdb08c297dca15fddd13963bed0ef501b67c55) ) // Silkscreen: "K2xxROM1-2 // 830115-01N // <square symbol>2003KURZWEIL // 0402" @ U2
	ROM_LOAD( "k2xxrom1-3__830116-01n__2003kurzweil__0402.u3", 0xc00000, 0x200000, CRC(4efedf7f) SHA1(b6888d533a6338aae4d51d3b70f964eb96266069) ) // Silkscreen: "K2xxROM1-3 // 830116-01N // <square symbol>2003KURZWEIL // 0402" @ U3
	ROM_LOAD( "k2xxrom1-4__830117-01n__2003kurzweil__0402.u4", 0xe00000, 0x200000, CRC(4b83d510) SHA1(061a0e954779cb7214ca17bb3a879bb7bc36eddd) ) // Silkscreen: "K2xxROM1-4 // 830117-01N // <square symbol>2003KURZWEIL // 0402" @ U4


	// 'Contemporary' babyboard AKA "ROM 2" or 'Kurzweil RM-226':
	// see https://images.reverb.com/image/upload/s--t9U6Ex2u--/a_180/f_auto,t_large/v1511749743/r0fglivtamtoqrevhatt.jpg
	// and https://images.reverb.com/image/upload/s--6eU1RGQ4--/a_exif,c_limit,e_unsharp_mask:80,f_auto,fl_progressive,g_south,h_620,q_90,w_620/v1524633114/gxnbseznct6cptmspizt.jpg
	// Note there is a older version of contemporary with roms 830131-01
	// thru 830134-01; this older version is a VERY MUCH WANTED dump.
	// It only seems to have been produced during a short period in 1994-1995?
	// The old version may share some roms with the Kurzweil SP76 and SP88
	// Stage Pianos.
	// https://www.mobikin.com/images/android/rom.jpg

	// newer contemporary is 830146-01 4463, 830147-01 C9BE, 830148-01 93B2, 830134-01 F265
	ROM_LOAD( "k2xxrom2-1__830146-01n__2003kurzweil__0402.u1", 0x1000000, 0x200000, CRC(03628775) SHA1(1db7dd514c2a7b810d0e5fef6f614498d695879e) ) // Silkscreen: "K2xxROM2-1 // 830146-01N // <square symbol>2003KURZWEIL // 0402" @ U1; from alt ROM labels, sum16 is 4463
	ROM_LOAD( "k2xxrom2-2__830147-01n__2003kurzweil__0402.u2", 0x1200000, 0x200000, CRC(7b81c227) SHA1(954d41e9fce54eb4a4ce81b5095227a1478a6828) ) // Silkscreen: "K2xxROM2-2 // 830147-01N // <square symbol>2003KURZWEIL // 0402" @ U2; from alt ROM labels, sum16 is c9be
	ROM_LOAD( "k2xxrom2-3__830148-01n__2003kurzweil__0403.u3", 0x1400000, 0x200000, CRC(62af1ba7) SHA1(8910abfd33a939d8a20cd69576c94342f194e23a) ) // Silkscreen: "K2xxROM2-3 // 830148-01N // <square symbol>2003KURZWEIL // 0403" @ U3; from alt ROM labels, sum16 is 93b2
	ROM_LOAD( "k2xxrom2-4__830134-01n__2003kurzweil__0402.u4", 0x1600000, 0x200000, CRC(11f3dfb6) SHA1(a74e040b316f7a6042368c6ae9c2b0cda8656614) ) // Silkscreen: "K2xxROM2-4 // 830134-01N // <square symbol>2003KURZWEIL // 0402" @ U4; from alt ROM labels, sum16 is f265

	// Note1: the roms 830122-01 thru 830127-01 are from the Kurzweil MK-10
	// see https://images.reverb.com/image/upload/s--sBo5xoMU--/a_exif,c_limit,e_unsharp_mask:80,f_auto,fl_progressive,g_south,h_1600,q_80,w_1600/v1504391767/v7iopgveuh6z42kxzobo.jpg
	// Note2: the rom 830129-01 is from the Kurzweil RG200

	ROM_REGION(0x3000, "pals", 0)
	ROM_LOAD( "pseudo_v4d.u11.gal16v8b.jed", 0x000000, 0x000bd0, CRC(43561132) SHA1(a0c567c81022bc7fb83023d89556ccd5aa1ab36d) )
	ROM_LOAD( "sndram_v1.u50.gal16v8b.jed", 0x001000, 0x000bd0, CRC(cabc9335) SHA1(968fa5baa43c7589c901f09b12085437834aeb37) )
	// GODOT seems to take in the top 4 maincpu address lines (A20-A24) and multiplex/decode onto 11 (?) I/O lines
	// Effectively takes care of the "memory block" logic (MMU/bank switching) that the service manual describes
	ROM_LOAD( "godot_v5.u10.gal20v8a.jed", 0x002000, 0x00066f, CRC(c6517456) SHA1(b82530d46afdca5f6460e77ac11710cad55a6b89) )
	// There's an additional GAL20V8 for the MMU - exact logic unknown, but the MCTL[0-3] lines are used, which seem
	// like the "address space select" lines - so at least one or more of those address space areas map directly to SBA[22-25], which
	// seem to be the Sample Bank address lines.
ROM_END

} // anonymous namespace

SYST( 1990, k2000, 0, 0, k2000, k2000, k2000_state, empty_init, "Kurzweil Music Systems", "K2000", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



ksp10.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Kawai KSP10 digital piano.

****************************************************************************/

#include "emu.h"
#include "cpu/tlcs900/tmp96c141.h"


namespace {

class kawai_ksp10_state : public driver_device
{
public:
	kawai_ksp10_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void ksp10(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<tmp96c141_device> m_maincpu;
};


void kawai_ksp10_state::mem_map(address_map &map)
{
	map(0x000000, 0x07ffff).rom().region("firmware", 0);
	map(0x400000, 0x407fff).ram(); // NVRAM?
}


static INPUT_PORTS_START(ksp10)
INPUT_PORTS_END

void kawai_ksp10_state::ksp10(machine_config &config)
{
	TMP96C141(config, m_maincpu, 10'000'000); // clock unknown
	m_maincpu->set_addrmap(AS_PROGRAM, &kawai_ksp10_state::mem_map);
}

ROM_START(ksp10)
	ROM_REGION16_LE(0x80000, "firmware", 0)
	ROM_LOAD("u3_hp041b_e8dp_tms27c240.bin", 0x00000, 0x80000, CRC(b64e7a97) SHA1(bac0f345d0a039a3315883a6ca8eefe659709a26))

	ROM_REGION16_LE(0x40000, "samples", 0)
	ROM_LOAD("u21_hp042a_e7dp_mbm27c2048.bin", 0x00000, 0x40000, CRC(e21b1141) SHA1(181c2beed18da2efa2f0e45cb3233adf6b932127))
ROM_END

} // anonymous namespace


SYST(199?, ksp10, 0, 0, ksp10, ksp10, kawai_ksp10_state, empty_init, "Kawai Musical Instruments Manufacturing", "KSP10 Digital Piano", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



ktm3.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    KTM-3 (c) 1980 Synertek Systems Corp.

    This is a sort of single-board video display terminal. The second 6502,
    which uses part of the same program ROM as the first 6502 and runs on the
    inverse phases of its clock, appears to function as a crude CRTC,
    generating character addresses and sync signals with its address outputs.

    Only a few address lines are decoded at all. The resulting mirroring might
    not be accurately emulated yet.

*******************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/m6502/m6502.h"
#include "machine/clock.h"
#include "machine/mos6551.h"
#include "screen.h"


namespace {

class ktm3_state : public driver_device
{
public:
	ktm3_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_pcpu(*this, "pcpu")
		, m_vcpu(*this, "vcpu")
		, m_acia(*this, "acia")
		, m_key_matrix(*this, "KEY%u", 0U)
		, m_option_sw(*this, "OPTION")
		, m_chargen(*this, "chargen")
		, m_ram(*this, "ram%u", 0U)
	{
	}

	void ktm3(machine_config &config);

	int ac_r();
	template <int N> int sw_r();

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	u8 allram_r(offs_t offset);
	void allram_w(offs_t offset, u8 data);
	u8 keyboard_r(offs_t offset);

	void pcpu_map(address_map &map) ATTR_COLD;
	void vcpu_map(address_map &map) ATTR_COLD;

	void signal_w(int state);

	required_device<cpu_device> m_pcpu;
	required_device<cpu_device> m_vcpu;
	required_device<mos6551_device> m_acia;
	required_ioport_array<10> m_key_matrix;
	required_ioport m_option_sw;
	required_region_ptr<u8> m_chargen;
	required_shared_ptr_array<u8, 2> m_ram;

	bool m_signal = false;
};

void ktm3_state::machine_start()
{
	m_acia->write_cts(0);

	save_item(NAME(m_signal));
}

u32 ktm3_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

u8 ktm3_state::allram_r(offs_t offset)
{
	return m_ram[0][offset] & m_ram[1][offset];
}

void ktm3_state::allram_w(offs_t offset, u8 data)
{
	m_ram[0][offset] = m_ram[1][offset] = data;
}

u8 ktm3_state::keyboard_r(offs_t offset)
{
	u8 ret = 0xff;

	for (int i = 0; i < 10; i++)
		if (BIT(offset, i))
			ret &= m_key_matrix[i]->read();

	return ret ^ 0xff;
}

void ktm3_state::pcpu_map(address_map &map)
{
	map(0x0000, 0x03ff).mirror(0xf000).rw(FUNC(ktm3_state::allram_r), FUNC(ktm3_state::allram_w));
	map(0x0400, 0x0400).mirror(0xe3ff).portr("SPECIAL");
	map(0x0800, 0x0bff).mirror(0xe000).r(FUNC(ktm3_state::keyboard_r));
	map(0x1400, 0x17ff).mirror(0xe000).ram().share("ram0"); // 2x SY2114L-3
	map(0x1800, 0x1bff).mirror(0xc000).ram().share("ram1"); // 2x SY2114L-3
	map(0x3800, 0x3fff).mirror(0xc000).rom().region("program", 0x000);
	map(0x5c00, 0x5c03).mirror(0x83fc).rw(m_acia, FUNC(mos6551_device::read), FUNC(mos6551_device::write));
}

void ktm3_state::vcpu_map(address_map &map)
{
	map(0x0000, 0x00ff).mirror(0xf800).rw(FUNC(ktm3_state::allram_r), FUNC(ktm3_state::allram_w));
	map(0x0100, 0x01ff).mirror(0xfc00).rom().region("program", 0x000);
	map(0x0300, 0x03ff).mirror(0xfc00).rom().region("program", 0x100);
}

void ktm3_state::signal_w(int state)
{
	m_signal = state;
}

int ktm3_state::ac_r()
{
	return m_signal;
}

template <int N>
int ktm3_state::sw_r()
{
	return BIT(m_option_sw->read(), N);
}

static INPUT_PORTS_START(ktm3)
	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x1b) PORT_NAME("Esc") PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('9') PORT_CHAR(')') PORT_CODE(KEYCODE_9)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('i') PORT_CHAR('I') PORT_CODE(KEYCODE_I)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('k') PORT_CHAR('K') PORT_CODE(KEYCODE_K)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('a') PORT_CHAR('A') PORT_CODE(KEYCODE_A)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('z') PORT_CHAR('Z') PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_SHIFT_1) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR('"') PORT_CODE(KEYCODE_2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('q') PORT_CHAR('Q') PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0') PORT_CODE(KEYCODE_0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('o') PORT_CHAR('O') PORT_CODE(KEYCODE_O)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('l') PORT_CHAR('L') PORT_CODE(KEYCODE_L)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('s') PORT_CHAR('S') PORT_CODE(KEYCODE_S)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('x') PORT_CHAR('X') PORT_CODE(KEYCODE_X)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('w') PORT_CHAR('W') PORT_CODE(KEYCODE_W)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(':') PORT_CHAR('*') PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('p') PORT_CHAR('P') PORT_CODE(KEYCODE_P)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHAR('+') PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('d') PORT_CHAR('D') PORT_CODE(KEYCODE_D)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('c') PORT_CHAR('C') PORT_CODE(KEYCODE_C)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL)

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR('$') PORT_CODE(KEYCODE_4)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('e') PORT_CHAR('E') PORT_CODE(KEYCODE_E)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('-') PORT_CHAR('=') PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x0a) PORT_NAME("Line Feed") PORT_CODE(KEYCODE_RALT) // actually between P and Return key
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('@') PORT_CHAR('`') PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('f') PORT_CHAR('F') PORT_CODE(KEYCODE_F)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('v') PORT_CHAR('V') PORT_CODE(KEYCODE_V)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(ktm3_state::sw_r<0>))

	PORT_START("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('r') PORT_CHAR('R') PORT_CODE(KEYCODE_R)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('^') PORT_CHAR('~') PORT_CODE(KEYCODE_TILDE) // actually between - and Home/Clear keys
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x0d) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('[') PORT_CHAR('{') PORT_CODE(KEYCODE_OPENBRACE) // actually to right of @ `
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('g') PORT_CHAR('G') PORT_CODE(KEYCODE_G)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('b') PORT_CHAR('B') PORT_CODE(KEYCODE_B)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(ktm3_state::sw_r<1>))

	PORT_START("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR('&') PORT_CODE(KEYCODE_6)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('t') PORT_CHAR('T') PORT_CODE(KEYCODE_T)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CHAR(UCHAR_MAMEKEY(END)) PORT_NAME("Home  Clear") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('_') PORT_CHAR(UCHAR_MAMEKEY(DEL)) PORT_CODE(KEYCODE_BACKSPACE) // actually to right of Return key
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(']') PORT_CHAR('}') PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('h') PORT_CHAR('H') PORT_CODE(KEYCODE_H)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('n') PORT_CHAR('N') PORT_CODE(KEYCODE_N)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(ktm3_state::sw_r<2>))

	PORT_START("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR('\'') PORT_CODE(KEYCODE_7)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('y') PORT_CHAR('Y') PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(' ') PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('/') PORT_CHAR('?') PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\\') PORT_CHAR('|') PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('j') PORT_CHAR('J') PORT_CODE(KEYCODE_J)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_M)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(ktm3_state::sw_r<3>))

	PORT_START("KEY7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('8') PORT_CHAR('(') PORT_CODE(KEYCODE_8)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('u') PORT_CHAR('U') PORT_CODE(KEYCODE_U)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('.') PORT_CHAR('>') PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',') PORT_CHAR('<') PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(ktm3_state::sw_r<4>))

	PORT_START("KEY8")
	PORT_BIT(0x7f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(ktm3_state::sw_r<5>))

	PORT_START("KEY9")
	PORT_BIT(0x7f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(ktm3_state::sw_r<6>))

	PORT_START("OPTION")
	PORT_DIPNAME(0x01, 0x01, DEF_STR(Unused)) PORT_DIPLOCATION("SW:1")
	PORT_DIPSETTING(0x01, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPNAME(0x02, 0x00, "Conversation Mode") PORT_DIPLOCATION("SW:2")
	PORT_DIPSETTING(0x02, "Half Duplex")
	PORT_DIPSETTING(0x00, "Full Duplex")
	PORT_DIPNAME(0x04, 0x04, "Stop Bits") PORT_DIPLOCATION("SW:3")
	PORT_DIPSETTING(0x04, "1")
	PORT_DIPSETTING(0x00, "2")
	PORT_DIPNAME(0x18, 0x18, "Parity") PORT_DIPLOCATION("SW:4,5")
	PORT_DIPSETTING(0x00, "Even")
	PORT_DIPSETTING(0x08, "Odd")
	PORT_DIPSETTING(0x10, "Mark")
	PORT_DIPSETTING(0x18, "Space")
	PORT_DIPNAME(0xe0, 0xc0, "Baud Rate") PORT_DIPLOCATION("SW:6,7,8")
	PORT_DIPSETTING(0x00, "109.92")
	PORT_DIPSETTING(0x20, "300")
	PORT_DIPSETTING(0x40, "600")
	PORT_DIPSETTING(0x60, "1200")
	PORT_DIPSETTING(0x80, "2400")
	PORT_DIPSETTING(0xa0, "4800")
	PORT_DIPSETTING(0xc0, "9600")
	PORT_DIPSETTING(0xe0, "19200")

	PORT_START("SPECIAL")
	PORT_BIT(0x3f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(ktm3_state::ac_r))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(ktm3_state::sw_r<7>))

	PORT_START("JUMPER")
	PORT_DIPNAME(0x01, 0x00, "Columns") PORT_DIPLOCATION("J1:1")
	PORT_DIPSETTING(0x01, "40")
	PORT_DIPSETTING(0x00, "80")
INPUT_PORTS_END

void ktm3_state::ktm3(machine_config &config)
{
	M6502(config, m_pcpu, 14.745_MHz_XTAL / 15); // SY6502 at U2; divider not verified
	m_pcpu->set_addrmap(AS_PROGRAM, &ktm3_state::pcpu_map);

	M6502(config, m_vcpu, 14.745_MHz_XTAL / 15); // SY6502 at U1; divider not verified
	m_vcpu->set_addrmap(AS_PROGRAM, &ktm3_state::vcpu_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(14.745_MHz_XTAL, 930, 0, 600, 262, 0, 240); // parameters guessed
	screen.set_screen_update(FUNC(ktm3_state::screen_update));

	MOS6551(config, m_acia, 14.745_MHz_XTAL / 15); // SY6551
	m_acia->set_xtal(14.745_MHz_XTAL / 8);
	m_acia->irq_handler().set_inputline(m_pcpu, m6502_device::IRQ_LINE);
	m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_acia->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_acia->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "loopback"));
	rs232.rxd_handler().set(m_acia, FUNC(mos6551_device::write_rxd));
	rs232.cts_handler().set(m_acia, FUNC(mos6551_device::write_dsr));
	rs232.dcd_handler().set(m_acia, FUNC(mos6551_device::write_dcd));

	CLOCK(config, "60hz", 60).signal_handler().set(FUNC(ktm3_state::signal_w));
}

ROM_START(ktm3)
	ROM_REGION(0x800, "program", 0)
	ROM_LOAD("02-9001-126.bin", 0x000, 0x800, CRC(d7441e28) SHA1(bf0c05bfdcfd9083183325336d9702c67b7de63c))

	ROM_REGION(0x800, "chargen", 0)
	ROM_LOAD("02-0061-a.bin", 0x000, 0x800, CRC(9739e2ac) SHA1(672059b7618afb6c19632663d58a854ea9ec2401))
ROM_END

} // anonymous namespace


COMP(1980, ktm3, 0, 0, ktm3, ktm3, ktm3_state, empty_init, "Synertek Systems", "KTM-3", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)



kyocera.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/******************************************************************************************

    Kyocera Kyotronics 85 (and similar laptop computers)

    2009/04 Very Preliminary Driver (video emulation courtesy of very old code by
            Hamish Coleman)

    Comments about bit usage from Tech References and Virtual T source.

    Supported systems:
      - Kyosei Kyotronic 85
      - Olivetti M10 (slightly diff hw, BIOS is shifted by 2 words)
      - NEC PC-8201A (slightly diff hw)
      - TRS-80 Model 100
      - Tandy Model 102 (slightly diff hw)
      - Tandy Model 200 (diff video & rtc)

    To Do:
      - Find dumps of systems which could easily be added:
        * Olivetti M10 Modem (US) (diff BIOS than the European version)
        * NEC PC-8201 (original Japanese version of PC-8201A)
        * NEC PC-8300 (similar hardware to PC-8201)
        * NEC PC-8300 w/BradyWriter II ROMs

    - bar code reader (!RxDB -> RST5.5, Hewlett-Packard HREDS-3050 interface)
    - un-Y2K-hack tandy200
    - keyboard is unresponsive for couple of seconds after boot, and rather slow thereafter
    - soft power on/off
    - pc8201 48K RAM option
    - pc8201 NEC PC-8241A video interface (TMS9918, 16K videoRAM, 8K ROM)
    - pc8201 NEC PC-8233 floppy controller
    - pc8201 NEC floppy disc drives (PC-8031-1W, PC-8031-2W, PC-80S31)
    - trsm100 Tandy Portable Disk Drive (TPDD: 100k 3?", TPDD2: 200k 3?") (undumped HD63A01V1 MCU + full custom uPD65002, serial comms via IM6042)
    - trsm100 Chipmunk disk drive (384k 3?") (full custom logic, not going to happen)
    - trsm100 RS232/modem select
    - tandy200 RTC alarm
    - tandy200 TCM5089 DTMF sound
    - international keyboard option ROMs
    - cassette is not working on pc8201, pc8201a, npc8300
    - natural keyboard is far too slow to be usable; paste is useless

    10 FOR A=0 TO 255
    20 PRINT CHR$(A);
    30 NEXT A


                          * PC-8201/8300 HARDWARE PORT DEFINITIONS *

                -Port-
    Name       Hex  Dec   Notes
    --------   ---  ---   -----------------------------------------
    A8255      070  112   Video interface port A (8255)
    B8255      071  113   Video interface port B (8255)
    C8255      072  114   Video interface port C (8255)
    CW8255     073  115   Video interface command/mode port (8255)

******************************************************************************************/


#include "emu.h"
#include "kyocera.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "utf8.h"

/* Read/Write Handlers */

uint8_t pc8201_state::bank_r()
{
	/*

	    bit     signal      description

	    0       LADR1       select address 0 to 7fff
	    1       LADR2       select address 0 to 7fff
	    2       HADR1       select address 8000 to ffff
	    3       HADR2       select address 8000 to ffff
	    4
	    5
	    6       SELB        serial interface status bit 1
	    7       SELA        serial interface status bit 0

	*/

	return (m_iosel << 5) | m_bank;
}

void pc8201_state::bankswitch(uint8_t data)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	int rom_bank = data & 0x03;
	int ram_bank = (data >> 2) & 0x03;

	m_bank = data & 0x0f;

	if (rom_bank > 1)
	{
		/* RAM */
		program.install_readwrite_bank(0x0000, 0x7fff, m_bank1);
	}
	else
	{
		/* ROM */
		program.install_read_bank(0x0000, 0x7fff, m_bank1);
		program.unmap_write(0x0000, 0x7fff);
	}

	m_bank1->set_entry(rom_bank);

	switch (ram_bank)
	{
	case 0:
		if (m_ram->size() > 16 * 1024)
		{
			program.install_readwrite_bank(0x8000, 0xffff, m_bank2);
		}
		else
		{
			program.unmap_readwrite(0x8000, 0xbfff);
			program.install_readwrite_bank(0xc000, 0xffff, m_bank2);
		}
		break;

	case 1:
		program.unmap_readwrite(0x8000, 0xffff);
		break;

	case 2:
		if (m_ram->size() > 32 * 1024)
			program.install_readwrite_bank(0x8000, 0xffff, m_bank2);
		else
			program.unmap_readwrite(0x8000, 0xffff);
		break;

	case 3:
		if (m_ram->size() > 64 * 1024)
			program.install_readwrite_bank(0x8000, 0xffff, m_bank2);
		else
			program.unmap_readwrite(0x8000, 0xffff);
		break;
	}

	m_bank2->set_entry(ram_bank);
}

void pc8201_state::bank_w(uint8_t data)
{
	/*

	    bit     signal      description

	    0       LADR1       select address 0 to 7fff
	    1       LADR2       select address 0 to 7fff
	    2       HADR1       select address 8000 to ffff
	    3       HADR2       select address 8000 to ffff
	    4
	    5
	    6
	    7

	*/
//printf("bank %02x\n",data);
	bankswitch(data);
}

void pc8201_state::scp_w(uint8_t data)
{
	/*

	    bit     signal      description

	    0
	    1
	    2
	    3       REMOTE      cassette motor
	    4       TSTB        RTC strobe
	    5       PSTB        printer strobe
	    6       SELB        serial interface select bit 1
	    7       SELA        serial interface select bit 0

	*/

	/* cassette motor */
	m_cassette->change_state(BIT(data,3) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);

	/* RTC strobe */
	m_rtc->stb_w(BIT(data, 4));

	/* printer strobe */
	m_centronics->write_strobe(BIT(data, 5));

	/* serial interface select */
	m_iosel = data >> 5;
}

uint8_t kc85_state::uart_r()
{
	if (!machine().side_effects_disabled())
	{
		m_uart->drr_w(0);
		m_uart->drr_w(1);
	}

	return m_uart->read();
}

void kc85_state::uart_ctrl_w(uint8_t data)
{
	/*

	    bit     signal      description

	    0       SBS         stop bit select
	    1       EPE         even parity enable
	    2       PI          parity inhibit
	    3       CLS1        character length select bit 1
	    4       CLS2        character length select bit 2
	    5
	    6
	    7

	*/

	m_uart->sbs_w(BIT(data, 0));
	m_uart->epe_w(BIT(data, 1));
	m_uart->pi_w(BIT(data, 2));
	m_uart->cls1_w(BIT(data, 3));
	m_uart->cls2_w(BIT(data, 4));

	m_uart->crl_w(1);
}

uint8_t kc85_state::uart_status_r()
{
	/*

	    bit     signal      description

	    0       CD          carrier detect
	    1       OE          overrun error
	    2       FE          framing error
	    3       PE          parity error
	    4       TBRE        transmit buffer register empty
	    5       RP
	    6       +5V
	    7       _LPS        low power sensor

	*/

	uint8_t data = 0x40;

	// carrier detect
	data |= m_rs232->dcd_r();

	// overrun error
	data |= m_uart->oe_r() << 1;

	// framing error
	data |= m_uart->fe_r() << 2;

	// parity error
	data |= m_uart->pe_r() << 3;

	// transmit buffer register empty
	data |= m_uart->tbre_r() << 4;

	// rp TODO
	data |= 0x20;

	// low power sensor
	data |= BIT(m_battery->read(), 0) << 7;

	return data;
}

uint8_t pc8201_state::uart_status_r()
{
	/*

	    bit     signal      description

	    0       _DCD/_RD    data carrier detect / ring detect
	    1       OE          overrun error
	    2       FE          framing error
	    3       PE          parity error
	    4       TBRE        transmit buffer register empty
	    5       RP
	    6       +5V
	    7       _LPS        low power signal

	*/

	uint8_t data = 0x40;

	// data carrier detect / ring detect
	data |= m_rs232->dcd_r();

	// overrun error
	data |= m_uart->oe_r() << 1;

	// framing error
	data |= m_uart->fe_r() << 2;

	// parity error
	data |= m_uart->pe_r() << 3;

	// transmit buffer register empty
	data |= m_uart->tbre_r() << 4;

	// rp TODO
	data |= 0x20;

	// low power sensor
	data |= BIT(m_battery->read(), 0) << 7;

	return data;
}

void pc8201_state::romah_w(uint8_t data)
{
	/*

	    bit     signal

	    0       A16
	    1       ROM SEL
	    2
	    3
	    4
	    5
	    6
	    7

	*/

	// ROM address bit 16
	m_rom_addr = (BIT(data, 0) << 16) | (m_rom_addr & 0xffff);

	// ROM select
	m_rom_sel = BIT(data, 1);
}

void pc8201_state::romal_w(uint8_t data)
{
	/*

	    bit     signal

	    0       A0
	    1       A1
	    2       A2
	    3       A3
	    4       A4
	    5       A5
	    6       A6
	    7       A7

	*/

	m_rom_addr = (m_rom_addr & 0x1ff00) | data;
}

void pc8201_state::romam_w(uint8_t data)
{
	/*

	    bit     signal

	    0       A8
	    1       A9
	    2       A10
	    3       A11
	    4       A12
	    5       A13
	    6       A14
	    7       A15

	*/

	m_rom_addr = (m_rom_addr & 0x100ff) | (data << 8);
}

uint8_t pc8201_state::romrd_r()
{
	uint8_t data = 0xff;

	if (m_rom_sel)
		data = m_cas_cart->read_rom(m_rom_addr & 0x1ffff);

	return data;
}

void kc85_state::modem_w(uint8_t data)
{
	/*

	    bit     signal      description

	    0                   telephone line signal selection relay output
	    1       EN          MC14412 enable output
	    2
	    3
	    4
	    5
	    6
	    7

	*/

	//m_modem->en_w(BIT(data, 1));
}

void kc85_state::ctrl_w(uint8_t data)
{
	/*

	    bit     signal      description

	    0       _STROM      ROM selection (0=standard, 1=option)
	    1       _STROBE     printer strobe output
	    2       STB         RTC strobe output
	    3       _REMOTE     cassette motor
	    4
	    5
	    6
	    7

	*/

	/* ROM bank selection */
	m_bank1->set_entry(BIT(data, 0));

	/* printer strobe */
	m_centronics->write_strobe(BIT(data, 1));

	/* RTC strobe */
	m_rtc->stb_w(BIT(data, 2));

	/* cassette motor */
	m_cassette->change_state(BIT(data,3) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
}

uint8_t kc85_state::keyboard_r()
{
	uint8_t data = 0xff;

	for (u8 i = 0; i < 9; i++)
		if (!BIT(m_keylatch, i)) data &= m_y[i]->read();

	return data;
}

void tandy200_state::bankswitch(uint8_t data)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	int rom_bank = data & 0x03;
	int ram_bank = (data >> 2) & 0x03;

	m_bank = data & 0x0f;

	if (rom_bank == 3)
	{
		/* invalid ROM bank */
		program.unmap_readwrite(0x0000, 0x7fff);
	}
	else
	{
		program.install_read_bank(0x0000, 0x7fff, m_bank1);
		program.unmap_write(0x0000, 0x7fff);
		m_bank1->set_entry(rom_bank);
	}

	if (m_ram->size() < ((ram_bank + 1) * 24 * 1024))
	{
		/* invalid RAM bank */
		program.unmap_readwrite(0xa000, 0xffff);
	}
	else
	{
		program.install_readwrite_bank(0xa000, 0xffff, m_bank2);
		m_bank2->set_entry(ram_bank);
	}
}

uint8_t tandy200_state::bank_r()
{
	return m_bank;
}

void tandy200_state::bank_w(uint8_t data)
{
	bankswitch(data);
}

uint8_t tandy200_state::stbk_r()
{
	uint8_t data = 0xff;

	for (u8 i = 0; i < 9; i++)
		if (!BIT(m_keylatch, i)) data &= m_y[i]->read();

	return data;
}

void tandy200_state::stbk_w(uint8_t data)
{
	/*

	    bit     signal  description

	    0       _PSTB   printer strobe output
	    1       REMOTE  cassette motor
	    2
	    3
	    4
	    5
	    6
	    7

	*/

	/* printer strobe */
	m_centronics->write_strobe(BIT(data, 0));

	/* cassette motor */
	m_cassette->change_state(BIT(data,1) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
}

uint8_t kc85_state::lcd_r(offs_t offset)
{
	uint8_t data = 0;

	for (uint8_t i = 0; i < 10; i++)
		if (BIT(m_keylatch, i))
			data |= m_lcdc[i]->read(offset);

	return data;
}

void kc85_state::lcd_w(offs_t offset, uint8_t data)
{
	for (uint8_t i = 0; i < 10; i++)
		if (BIT(m_keylatch, i))
			m_lcdc[i]->write(offset, data);
}

/* Memory Maps */

void kc85_state::kc85_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).bankr("bank1");
	map(0x8000, 0xffff).bankrw("bank2");
}

void pc8201_state::pc8201_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).bankrw("bank1");
	map(0x8000, 0xffff).bankrw("bank2");
}

void tandy200_state::tandy200_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).bankr("bank1");
	map(0x8000, 0x9fff).rom();
	map(0xa000, 0xffff).bankrw("bank2");
}

void kc85_state::kc85_io(address_map &map)
{
	map.unmap_value_high();
//  map(0x70, 0x70).mirror(0x0f); optional RAM unit
//  map(0x80, 0x80).mirror(0x0f); optional I/O controller unit
//  map(0x90, 0x90).mirror(0x0f); optional answering telephone unit
//  map(0xa0, 0xa0).mirror(0x0f); optional modem
	map(0xb0, 0xb7).mirror(0x08).rw(I8155_TAG, FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
	map(0xc0, 0xc0).mirror(0x0f).r(FUNC(kc85_state::uart_r)).w(m_uart, FUNC(im6402_device::write));
	map(0xd0, 0xd0).mirror(0x0f).rw(FUNC(kc85_state::uart_status_r), FUNC(kc85_state::uart_ctrl_w));
	map(0xe0, 0xe0).mirror(0x0f).rw(FUNC(kc85_state::keyboard_r), FUNC(kc85_state::ctrl_w));
	map(0xf0, 0xf1).mirror(0x0e).rw(FUNC(kc85_state::lcd_r), FUNC(kc85_state::lcd_w));
}

void kc85_state::trsm100_io(address_map &map)
{
	kc85_io(map);
	map(0xa0, 0xa0).mirror(0x0f).w(FUNC(kc85_state::modem_w));
}

void pc8201_state::pc8201_io(address_map &map)
{
	map.unmap_value_high();
//  map(0x70, 0x70).mirror(0x0f); optional video interface 8255
	map(0x80, 0x80).mirror(0x03).w(FUNC(pc8201_state::romah_w));
	map(0x84, 0x84).mirror(0x03).w(FUNC(pc8201_state::romal_w));
	map(0x88, 0x88).mirror(0x03).w(FUNC(pc8201_state::romam_w));
	map(0x8c, 0x8c).mirror(0x03).r(FUNC(pc8201_state::romrd_r));
	map(0x90, 0x90).mirror(0x0f).w(FUNC(pc8201_state::scp_w));
	map(0xa0, 0xa0).mirror(0x0f).rw(FUNC(pc8201_state::bank_r), FUNC(pc8201_state::bank_w));
	map(0xb0, 0xb7).mirror(0x08).rw(I8155_TAG, FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
	map(0xc0, 0xc0).mirror(0x0f).r(FUNC(pc8201_state::uart_r)).w(m_uart, FUNC(im6402_device::write));
	map(0xd0, 0xd0).mirror(0x0f).r(FUNC(pc8201_state::uart_status_r)).w(FUNC(pc8201_state::uart_ctrl_w));
	map(0xe0, 0xe0).mirror(0x0f).r(FUNC(pc8201_state::keyboard_r));
	map(0xf0, 0xf1).mirror(0x0e).rw(FUNC(pc8201_state::lcd_r), FUNC(pc8201_state::lcd_w));
}

void tandy200_state::tandy200_io(address_map &map)
{
	map.unmap_value_high();
	map(0x90, 0x9f).rw(m_rtc, FUNC(rp5c01_device::read), FUNC(rp5c01_device::write));
//  map(0xa0, 0xa0).mirror(0x0f).w(TCM5089_TAG, FUNC(tcm5089_device::write));
	map(0xb0, 0xb7).mirror(0x08).rw(I8155_TAG, FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
	map(0xc0, 0xc1).mirror(0x0e).rw(I8251_TAG, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xd0, 0xd0).mirror(0x0f).rw(FUNC(tandy200_state::bank_r), FUNC(tandy200_state::bank_w));
	map(0xe0, 0xe0).mirror(0x0f).rw(FUNC(tandy200_state::stbk_r), FUNC(tandy200_state::stbk_w));
	map(0xf0, 0xf0).mirror(0x0e).rw(m_lcdc, FUNC(hd61830_device::data_r), FUNC(hd61830_device::data_w));
	map(0xf1, 0xf1).mirror(0x0e).rw(m_lcdc, FUNC(hd61830_device::status_r), FUNC(hd61830_device::control_w));
}

/* Input Ports */

static INPUT_PORTS_START( kc85 )
	PORT_START("Y0")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')

	PORT_START("Y1")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')

	PORT_START("Y2")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')

	PORT_START("Y3")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR(']')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("Y4")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')

	PORT_START("Y5")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')

	PORT_START("Y6")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PRINT") PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LABEL") PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PASTE") PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\xE2\x86\x92|") PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DEL BKSP") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("Y7")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F8") PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F7") PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F6") PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F5") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_START("Y8")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PAUSE BREAK") PORT_CODE(KEYCODE_F12) PORT_CHAR(UCHAR_MAMEKEY(F12))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CAPS LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("NUM") PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CODE") PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(RALT))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GRAPH") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("BATTERY")
	PORT_CONFNAME( 0x01, 0x01, "Battery Status" )
	PORT_CONFSETTING( 0x01, DEF_STR( Normal ) )
	PORT_CONFSETTING( 0x00, "Low Battery" )
INPUT_PORTS_END

static INPUT_PORTS_START( pc8201 )
	PORT_INCLUDE( kc85 )

	PORT_MODIFY("Y3")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR('^') PORT_CHAR('_')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(165) PORT_CHAR(']')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@') PORT_CHAR('[')

	PORT_MODIFY("Y4")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')

	PORT_MODIFY("Y5")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DEL  BS") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PAST  INS") PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')

	PORT_MODIFY("Y6")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("TAB") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))

	PORT_MODIFY("Y7")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("STOP") PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("f.5") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("f.4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("f.3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("f.2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("f.1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_MODIFY("Y8")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CAPS LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("JIS") PORT_CODE(KEYCODE_0_PAD)
INPUT_PORTS_END

static INPUT_PORTS_START( pc8201a )
	PORT_INCLUDE( kc85 )

	PORT_MODIFY("Y3")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@') PORT_CHAR('^')

	PORT_MODIFY("Y4")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')

	PORT_MODIFY("Y5")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PAST INS") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RALT) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('_')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')

	PORT_MODIFY("Y6")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\xE2\x86\x92|") PORT_CODE(KEYCODE_TAB)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DEL BKSP") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)

	PORT_MODIFY("Y7")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("STOP") PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("f.5") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("f.4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("f.3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("f.2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("f.1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_MODIFY("Y8")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CAPS LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

/* There's more than one keyboard layout for the m10. A quick search showed
- QWERTZ with +* (in top left corner) [Germany?]
- QWERTY with [{ [UK and US versions]
- QZERTY with $& [Italy?]
but our machine is different again, unable to find a specific photo of it. Could be French. */
static INPUT_PORTS_START( olivm10 )
	PORT_START("Y0")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('(') PORT_CHAR('5')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('\'') PORT_CHAR('4')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('"') PORT_CHAR('3')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR(233) PORT_CHAR('2')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR(163) PORT_CHAR('1')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('$') PORT_CHAR('*')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('a') PORT_CHAR('A')

	PORT_START("Y1")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR(')') PORT_CHAR(176)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR(224) PORT_CHAR('0')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR(199) PORT_CHAR('9')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('_') PORT_CHAR('8')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR(232) PORT_CHAR('7')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('-') PORT_CHAR('6')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')

	PORT_START("Y2")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR(',') PORT_CHAR('?')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(';') PORT_CHAR('.')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR(':') PORT_CHAR('/')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('!') PORT_CHAR(167)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(249) PORT_CHAR('%')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('^') PORT_CHAR(168)
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('`') PORT_CHAR('&')

	PORT_START("Y3")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('<') PORT_CHAR('>')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('q') PORT_CHAR('Q')

	PORT_START("Y4")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')

	PORT_START("Y5")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')

	PORT_START("Y6")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PASTE") PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\xE2\x86\x92|") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DEL BS") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))

	PORT_START("Y7")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F8") PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F7") PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F6") PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F5") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_START("Y8")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PAUSE BREAK") PORT_CODE(KEYCODE_F12) PORT_CHAR(UCHAR_MAMEKEY(F12))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CAPS LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("NUM") PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(RALT))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GRAPH") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PRINT") PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11))
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LABEL") PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10))

	PORT_START("BATTERY")
	PORT_CONFNAME( 0x01, 0x01, "Battery Status" )
	PORT_CONFSETTING( 0x01, DEF_STR( Normal ) )
	PORT_CONFSETTING( 0x00, "Low Battery" )
INPUT_PORTS_END


/* 8155 Interface */

void kc85_state::i8155_pa_w(uint8_t data)
{
	/*

	    bit     description

	    0       LCD chip select 0, key scan 0, RTC C0
	    1       LCD chip select 1, key scan 1, RTC C1
	    2       LCD chip select 2, key scan 2, RTC C2
	    3       LCD chip select 3, key scan 3, RTC CLK
	    4       LCD chip select 4, key scan 4, RTC DATA IN
	    5       LCD chip select 5, key scan 5
	    6       LCD chip select 6, key scan 6
	    7       LCD chip select 7, key scan 7

	*/

	/* LCD, keyboard */
	m_keylatch = (m_keylatch & 0x300) | data;

	/* RTC */
	m_rtc->c0_w(BIT(data, 0));
	m_rtc->c1_w(BIT(data, 1));
	m_rtc->c2_w(BIT(data, 2));
	m_rtc->clk_w(BIT(data, 3));
	m_rtc->data_in_w(BIT(data, 4));
}

void kc85_state::i8155_pb_w(uint8_t data)
{
	/*

	    bit     signal      description

	    0                   LCD chip select 8, key scan 8
	    1                   LCD chip select 9
	    2       _MC         melody control output
	    3       DCD/_RD     RS232 DCD/_RD select (0=RS232, 1=modem)
	    4       APO         auto power off output
	    5       BELL        buzzer output (0=ring, 1=not ring)
	    6       _DTR        RS232 data terminal ready output
	    7       _RTS        RS232 request to send output

	*/

	/* LCD, keyboard */
	m_keylatch = (data << 8 & 0x300) | (m_keylatch & 0xff);

	/* beeper */
	m_buzzer = BIT(data, 2);
	m_bell = BIT(data, 5);

	if (m_buzzer) m_speaker->level_w(m_bell);

	// RS-232
	m_rs232->write_dtr(BIT(data, 6));
	m_rs232->write_rts(BIT(data, 7));
}

void kc85_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

void kc85_state::write_centronics_select(int state)
{
	m_centronics_select = state;
}

uint8_t kc85_state::i8155_pc_r()
{
	/*

	    bit     description

	    0       CDI         clock data input
	    1       SLCT        _BUSY signal from printer
	    2       BUSY        BUSY signal from printer
	    3       BCR         bar code reader data input
	    4       _CTS        RS232 clear to send input
	    5       _DSR        RS232 DSR input

	*/

	uint8_t data = 0;

	// clock data input
	data |= m_rtc->data_out_r();

	// centronics busy
	data |= m_centronics_select << 1;
	data |= m_centronics_busy << 2;

	// RS-232
	data |= m_rs232->cts_r() << 4;
	data |= m_rs232->dsr_r() << 5;

	return data;
}

void kc85_state::i8155_to_w(int state)
{
	if (!m_buzzer && m_bell)
	{
		m_speaker->level_w(state);
	}

	m_uart->trc_w(state);
	m_uart->rrc_w(state);
}

void tandy200_state::i8155_pa_w(uint8_t data)
{
	/*

	    bit     description

	    0       print data 0, key scan 0
	    1       print data 1, key scan 1
	    2       print data 2, key scan 2
	    3       print data 3, key scan 3
	    4       print data 4, key scan 4
	    5       print data 5, key scan 5
	    6       print data 6, key scan 6
	    7       print data 7, key scan 7

	*/

	m_cent_data_out->write(data);

	m_keylatch = (m_keylatch & 0x100) | data;
}

void tandy200_state::i8155_pb_w(uint8_t data)
{
	/*

	    bit     signal      description

	    0                   key scan 8
	    1       ORIG/ANS    (1=ORIG, 0=ANS)
	    2       _BUZZER     (0=data from 8155 TO, 1=data from PB2)
	    3       _RS232C     (1=modem, 0=RS-232)
	    4       PCS         power cut signal
	    5       BELL        buzzer data output
	    6       MEN         modem enable output
	    7       CALL        connects and disconnects the phone line

	*/

	/* keyboard */
	m_keylatch = (BIT(data, 0) << 8) | (m_keylatch & 0xff);

	/* beeper */
	m_buzzer = BIT(data, 2);
	m_bell = BIT(data, 5);

	if (m_buzzer) m_speaker->level_w(m_bell);
}

void tandy200_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

void tandy200_state::write_centronics_select(int state)
{
	m_centronics_select = state;
}

uint8_t tandy200_state::i8155_pc_r()
{
	/*

	    bit     signal  description

	    0       _LPS    low power sense input
	    1       _BUSY   not busy input
	    2       BUSY    busy input
	    3       BCR     bar code reader data input
	    4       CD      carrier detect input
	    5       CDBD    carrier detect break down input

	*/

	uint8_t data = 0x01;

	// centronics
	data |= m_centronics_select << 1;
	data |= m_centronics_busy << 2;

	// RS-232
	data |= m_rs232->dcd_r() << 4;

	return data;
}

void tandy200_state::i8155_to_w(int state)
{
	if (!m_buzzer && m_bell)
	{
		m_speaker->level_w(state);
	}
}

/* Machine Drivers */

void kc85_state::machine_start()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	std::string region_tag;
	m_opt_region = memregion(region_tag.assign(m_opt_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());

	/* initialize RTC */
	m_rtc->cs_w(1);
	m_rtc->oe_w(1);

	/* configure ROM banking */
	program.install_read_bank(0x0000, 0x7fff, m_bank1);
	program.unmap_write(0x0000, 0x7fff);
	m_bank1->configure_entry(0, m_rom->base());
	m_bank1->configure_entry(1, m_opt_region ? m_opt_region->base() : m_rom->base());
	m_bank1->set_entry(0);

	/* configure RAM banking */
	switch (m_ram->size())
	{
	case 16 * 1024:
		program.unmap_readwrite(0x8000, 0xbfff);
		program.install_readwrite_bank(0xc000, 0xffff, m_bank2);
		break;

	case 32 * 1024:
		program.install_readwrite_bank(0x8000, 0xffff,m_bank2);
		break;
	}

	m_bank2->configure_entry(0, m_ram->pointer());
	m_bank2->set_entry(0);

	/* register for state saving */
	save_item(NAME(m_bank));
	save_item(NAME(m_keylatch));
	save_item(NAME(m_buzzer));
	save_item(NAME(m_bell));
	save_item(NAME(m_centronics_busy));
	save_item(NAME(m_centronics_select));
}

void pc8201_state::machine_start()
{
	uint8_t *ram = m_ram->pointer();

	std::string region_tag;
	m_opt_region = memregion(region_tag.assign(m_opt_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());

	/* initialize RTC */
	m_rtc->cs_w(1);
	m_rtc->oe_w(1);

	/* configure ROM banking */
	m_bank1->configure_entry(0, m_rom->base());
	m_bank1->configure_entry(1, m_opt_region ? m_opt_region->base() : m_rom->base());
	m_bank1->configure_entries(2, 2, ram + 0x8000, 0x8000);
	m_bank1->set_entry(0);

	/* configure RAM banking */
	m_bank2->configure_entry(0, ram);
	m_bank2->configure_entries(2, 2, ram + 0x8000, 0x8000);
	m_bank2->set_entry(0);

	bankswitch(0);

	/* register for state saving */
	save_item(NAME(m_bank));
	save_item(NAME(m_keylatch));
	save_item(NAME(m_buzzer));
	save_item(NAME(m_bell));
	save_item(NAME(m_centronics_busy));
	save_item(NAME(m_centronics_select));
	save_item(NAME(m_rom_sel));
	save_item(NAME(m_rom_addr));
	save_item(NAME(m_iosel));
}

void trsm100_state::machine_start()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	std::string region_tag;
	m_opt_region = memregion(region_tag.assign(m_opt_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());

	/* initialize RTC */
	m_rtc->cs_w(1);
	m_rtc->oe_w(1);

	/* configure ROM banking */
	program.install_read_bank(0x0000, 0x7fff, m_bank1);
	program.unmap_write(0x0000, 0x7fff);
	m_bank1->configure_entry(0, m_rom->base());
	m_bank1->configure_entry(1, m_opt_region ? m_opt_region->base() : m_rom->base());
	m_bank1->set_entry(0);

	/* configure RAM banking */
	switch (m_ram->size())
	{
	case 8 * 1024:
		program.unmap_readwrite(0x8000, 0xcfff);
		program.install_readwrite_bank(0xe000, 0xffff, m_bank2);
		break;

	case 16 * 1024:
		program.unmap_readwrite(0x8000, 0xbfff);
		program.install_readwrite_bank(0xc000, 0xffff, m_bank2);
		break;

	case 24 * 1024:
		program.unmap_readwrite(0x8000, 0x9fff);
		program.install_readwrite_bank(0xa000, 0xffff, m_bank2);
		break;

	case 32 * 1024:
		program.install_readwrite_bank(0x8000, 0xffff, m_bank2);
		break;
	}

	m_bank2->configure_entry(0, m_ram->pointer());
	m_bank2->set_entry(0);

	/* register for state saving */
	save_item(NAME(m_bank));
	save_item(NAME(m_keylatch));
	save_item(NAME(m_buzzer));
	save_item(NAME(m_bell));
	save_item(NAME(m_centronics_busy));
	save_item(NAME(m_centronics_select));
}

void tandy200_state::machine_start()
{
	m_opt_region = memregion(std::string(m_opt_cart->tag()) + GENERIC_ROM_REGION_TAG);

	m_bank = 0;
	m_tp = 0;

	/* configure ROM banking */
	m_bank1->configure_entry(0, m_rom->base());
	m_bank1->configure_entry(1, m_rom->base() + 0x10000);
	m_bank1->configure_entry(2, m_opt_region ? m_opt_region->base() : m_rom->base());
	m_bank1->set_entry(0);

	/* configure RAM banking */
	m_bank2->configure_entries(0, 3, m_ram->pointer(), 0x6000);
	m_bank2->set_entry(0);

	/* register for state saving */
	save_item(NAME(m_bank));
	save_item(NAME(m_keylatch));
	save_item(NAME(m_buzzer));
	save_item(NAME(m_bell));
	save_item(NAME(m_centronics_busy));
	save_item(NAME(m_centronics_select));
	save_item(NAME(m_tp));
}

void kc85_state::kc85_sod_w(int state)
{
	m_cassette->output(state ? +1.0 : -1.0);
}

int kc85_state::kc85_sid_r()
{
	return (m_cassette->input() > 0.04) ? 0 : 1;
}

void tandy200_state::kc85_sod_w(int state)
{
	m_cassette->output(state ? +1.0 : -1.0);
}

int tandy200_state::kc85_sid_r()
{
	return (m_cassette->input() > 0.04) ? 0 : 1;
}

TIMER_DEVICE_CALLBACK_MEMBER(tandy200_state::tandy200_tp_tick)
{
	m_maincpu->set_input_line(I8085_RST75_LINE, m_tp);

	m_tp = !m_tp;
}

void kc85_state::kc85(machine_config &config)
{
	/* basic machine hardware */
	I8085A(config, m_maincpu, XTAL(4'915'200));
	m_maincpu->set_addrmap(AS_PROGRAM, &kc85_state::kc85_mem);
	m_maincpu->set_addrmap(AS_IO, &kc85_state::kc85_io);
	m_maincpu->in_sid_func().set(FUNC(kc85_state::kc85_sid_r));
	m_maincpu->out_sod_func().set(FUNC(kc85_state::kc85_sod_w));

	/* video hardware */
	kc85_video(config);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	/* devices */
	i8155_device &i8155(I8155(config, I8155_TAG, XTAL(4'915'200)/2));
	i8155.out_pa_callback().set(FUNC(kc85_state::i8155_pa_w));
	i8155.out_pb_callback().set(FUNC(kc85_state::i8155_pb_w));
	i8155.in_pc_callback().set(FUNC(kc85_state::i8155_pc_r));
	i8155.out_to_callback().set(FUNC(kc85_state::i8155_to_w));

	UPD1990A(config, m_rtc);
	m_rtc->tp_callback().set_inputline(m_maincpu, I8085_RST75_LINE);

	IM6402(config, m_uart, 0, 0);
	m_uart->tro_callback().set(RS232_TAG, FUNC(rs232_port_device::write_txd));
	m_uart->dr_callback().set_inputline(m_maincpu, I8085_RST65_LINE);

	rs232_port_device &rs232(RS232_PORT(config, RS232_TAG, default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_uart, FUNC(im6402_device::rri_w));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(kc85_state::write_centronics_busy));
	m_centronics->select_handler().set(FUNC(kc85_state::write_centronics_select));

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	/* option ROM cartridge */
	GENERIC_CARTSLOT(config, m_opt_cart, generic_linear_slot, "trsm100_cart", "bin,rom");

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("trsm100");

	/* internal ram */
	RAM(config, m_ram).set_default_size("16K").set_extra_options("32K");
}

void pc8201_state::pc8201(machine_config &config)
{
	/* basic machine hardware */
	I8085A(config, m_maincpu, XTAL(4'915'200));
	m_maincpu->set_addrmap(AS_PROGRAM, &pc8201_state::pc8201_mem);
	m_maincpu->set_addrmap(AS_IO, &pc8201_state::pc8201_io);
	m_maincpu->in_sid_func().set(FUNC(pc8201_state::kc85_sid_r));
	m_maincpu->out_sod_func().set(FUNC(pc8201_state::kc85_sod_w));

	/* video hardware */
	kc85_video(config);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	/* devices */
	i8155_device &i8155(I8155(config, I8155_TAG, XTAL(4'915'200)/2));
	i8155.out_pa_callback().set(FUNC(pc8201_state::i8155_pa_w));
	i8155.out_pb_callback().set(FUNC(pc8201_state::i8155_pb_w));
	i8155.in_pc_callback().set(FUNC(pc8201_state::i8155_pc_r));
	i8155.out_to_callback().set(FUNC(pc8201_state::i8155_to_w));

	UPD1990A(config, m_rtc);
	m_rtc->tp_callback().set_inputline(m_maincpu, I8085_RST75_LINE);

	IM6402(config, m_uart, 0, 0);
	m_uart->tro_callback().set(RS232_TAG, FUNC(rs232_port_device::write_txd));
	m_uart->dr_callback().set_inputline(m_maincpu, I8085_RST65_LINE);

	rs232_port_device &rs232(RS232_PORT(config, RS232_TAG, default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_uart, FUNC(im6402_device::rri_w));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(pc8201_state::write_centronics_busy));
	m_centronics->select_handler().set(FUNC(pc8201_state::write_centronics_select));

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	/* option ROM cartridge */
	GENERIC_CARTSLOT(config, m_opt_cart, generic_linear_slot, "pc8201_cart", "bin,rom");

	/* 128KB ROM cassette */
	GENERIC_CARTSLOT(config, "cas_cartslot", generic_linear_slot, "pc8201_cart2", "bin,rom");

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("pc8201");

	/* internal ram */
	RAM(config, m_ram).set_default_size("16K").set_extra_options("32K,64K,96K");
}

void pc8201_state::pc8300(machine_config &config)
{
	pc8201(config);
	m_ram->set_default_size("32K").set_extra_options("64K,96K");
}

void trsm100_state::trsm100(machine_config &config)
{
	/* basic machine hardware */
	I8085A(config, m_maincpu, XTAL(4'915'200));
	m_maincpu->set_addrmap(AS_PROGRAM, &trsm100_state::kc85_mem);
	m_maincpu->set_addrmap(AS_IO, &trsm100_state::trsm100_io);
	m_maincpu->in_sid_func().set(FUNC(trsm100_state::kc85_sid_r));
	m_maincpu->out_sod_func().set(FUNC(trsm100_state::kc85_sod_w));

	/* video hardware */
	kc85_video(config);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	/* devices */
	i8155_device &i8155(I8155(config, I8155_TAG, XTAL(4'915'200)/2));
	i8155.out_pa_callback().set(FUNC(trsm100_state::i8155_pa_w));
	i8155.out_pb_callback().set(FUNC(trsm100_state::i8155_pb_w));
	i8155.in_pc_callback().set(FUNC(trsm100_state::i8155_pc_r));
	i8155.out_to_callback().set(FUNC(trsm100_state::i8155_to_w));

	UPD1990A(config, m_rtc);
	m_rtc->tp_callback().set_inputline(m_maincpu, I8085_RST75_LINE);

	IM6402(config, m_uart, 0, 0);
	m_uart->tro_callback().set(RS232_TAG, FUNC(rs232_port_device::write_txd));
	m_uart->dr_callback().set_inputline(m_maincpu, I8085_RST65_LINE);

	rs232_port_device &rs232(RS232_PORT(config, RS232_TAG, default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_uart, FUNC(im6402_device::rx_w));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

//  MCFG_MC14412_ADD(MC14412_TAG, XTAL(1'000'000))

	/* option ROM cartridge */
	GENERIC_CARTSLOT(config, m_opt_cart, generic_linear_slot, "trsm100_cart", "bin,rom");

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("trsm100");

	/* internal ram */
	RAM(config, m_ram).set_default_size("8K").set_extra_options("16K,24K,32K");
}

void trsm100_state::tandy102(machine_config &config)
{
	trsm100(config);
	m_ram->set_default_size("24K").set_extra_options("32K");
}

void tandy200_state::tandy200(machine_config &config)
{
	/* basic machine hardware */
	I8085A(config, m_maincpu, XTAL(4'915'200));
	m_maincpu->set_addrmap(AS_PROGRAM, &tandy200_state::tandy200_mem);
	m_maincpu->set_addrmap(AS_IO, &tandy200_state::tandy200_io);
	m_maincpu->in_sid_func().set(FUNC(tandy200_state::kc85_sid_r));
	m_maincpu->out_sod_func().set(FUNC(tandy200_state::kc85_sod_w));

	/* video hardware */
	tandy200_video(config);

	/* TP timer */
	TIMER(config, "tp").configure_periodic(FUNC(tandy200_state::tandy200_tp_tick), attotime::from_hz(XTAL(4'915'200)/2/8192));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);

//  TCM5089(config, TCM5089_TAG, XTAL(3'579'545));

	/* devices */
	i8155_device &i8155(I8155(config, I8155_TAG, XTAL(4'915'200)/2));
	i8155.out_pa_callback().set(FUNC(tandy200_state::i8155_pa_w));
	i8155.out_pb_callback().set(FUNC(tandy200_state::i8155_pb_w));
	i8155.in_pc_callback().set(FUNC(tandy200_state::i8155_pc_r));
	i8155.out_to_callback().set(FUNC(tandy200_state::i8155_to_w));

	RP5C01(config, m_rtc, XTAL(32'768));

	i8251_device &i8251(I8251(config, I8251_TAG, XTAL(4'915'200)/2));
	i8251.txd_handler().set(RS232_TAG, FUNC(rs232_port_device::write_txd));
	i8251.dtr_handler().set(RS232_TAG, FUNC(rs232_port_device::write_dtr));
	i8251.rts_handler().set(RS232_TAG, FUNC(rs232_port_device::write_rts));
	i8251.rxrdy_handler().set_inputline(m_maincpu, I8085_RST65_LINE);

	rs232_port_device &rs232(RS232_PORT(config, RS232_TAG, default_rs232_devices, nullptr));
	rs232.rxd_handler().set(I8251_TAG, FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set(I8251_TAG, FUNC(i8251_device::write_dsr));

//  MCFG_MC14412_ADD(MC14412_TAG, XTAL(1'000'000))
	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(tandy200_state::write_centronics_busy));
	m_centronics->select_handler().set(FUNC(tandy200_state::write_centronics_select));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	/* option ROM cartridge */
	GENERIC_CARTSLOT(config, m_opt_cart, generic_linear_slot, "tandy200_cart", "bin,rom");

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("tandy200");

	/* internal ram */
	RAM(config, m_ram).set_default_size("24K").set_extra_options("48K,72K");
}

/* ROMs */

ROM_START( kc85 )
	ROM_REGION( 0x8000, I8085_TAG, 0 )
	ROM_LOAD( "kc85rom.bin", 0x0000, 0x8000, CRC(8a9ddd6b) SHA1(9d18cb525580c9e071e23bc3c472380aa46356c0) )
ROM_END

ROM_START( pc8201 )
	ROM_REGION( 0x10000, I8085_TAG, 0 )
	ROM_LOAD( "3256a41-3b1 n 82 basic.rom0", 0x0000, 0x8000, CRC(3dbaa484) SHA1(9886a973faa639ca9e0ba478790bab20e5163495) )
ROM_END

ROM_START( pc8201a )
	ROM_REGION( 0x10000, I8085_TAG, 0 )
	ROM_LOAD( "pc8201rom.rom", 0x0000, 0x8000, CRC(30555035) SHA1(96f33ff235db3028bf5296052acedbc94437c596) )
ROM_END

ROM_START( npc8300 )
	ROM_REGION( 0x10000, I8085_TAG, 0 )
	ROM_LOAD( "831000-438_n83a_basic_1986_microsoft_8716_z01.bin", 0x0000, 0x8000, CRC(a3c15dcb) SHA1(f0322dfe3f2e951de043bf6d0973e6ffc2c87181))
ROM_END

ROM_START( trsm100 )
	/*
	    Board Code  ROM type            ROM Code            Comment
	    -------------------------------------------------------------------
	    PLX110CH1X  custom              LH535618            early North America
	    PLX110EH1X  27C256 compatible   3256C07-3J1/11US    late North America
	    PLX120CH1X  27C256 compatible   3256C05-3E1/11EP    European/Italian
	*/
	ROM_REGION( 0x8000, I8085_TAG, 0 )
	ROM_LOAD( "m100rom.m12",  0x0000, 0x8000, CRC(730a3611) SHA1(094dbc4ac5a4ea5cdf51a1ac581a40a9622bb25d) )
ROM_END

ROM_START( m10 )
	// 3256C02-4B3/I        Italian
	ROM_REGION( 0x8000, I8085_TAG, 0 )
	ROM_LOAD( "m10rom.m12", 0x0000, 0x8000, CRC(f0e8447a) SHA1(d58867276213116a79f7074109b7d7ce02e8a3af) )
ROM_END

ROM_START( tandy102 )
	ROM_REGION( 0x8000, I8085_TAG, 0 )
	ROM_LOAD( "m102rom.m12", 0x0000, 0x8000, CRC(08e9f89c) SHA1(b6ede7735a361c80419f4c9c0e36e7d480c36d11) )
ROM_END

ROM_START( tandy200 )
	ROM_REGION( 0x18000, I8085_TAG, 0 )
	ROM_LOAD( "rom 1-1.m15", 0x00000, 0x8000, NO_DUMP )
	ROM_LOAD( "rom 1-2.m13", 0x08000, 0x2000, NO_DUMP )
	ROM_LOAD( "rom 2.m14",   0x10000, 0x8000, NO_DUMP )
	ROM_LOAD( "t200rom.bin", 0x0000, 0xa000, BAD_DUMP CRC(e3358b38) SHA1(35d4e6a5fb8fc584419f57ec12b423f6021c0991) ) /* Y2K hacked */
	ROM_CONTINUE(           0x10000, 0x8000 )
ROM_END

/* System Drivers */

/*    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT    CLASS           INIT        COMPANY              FULLNAME */
COMP( 1983, kc85,     0,       0,      kc85,     kc85,    kc85_state,     empty_init, "Kyosei",            "Kyotronic 85 (Japan)", MACHINE_SUPPORTS_SAVE )
COMP( 1983, m10,      kc85,    0,      kc85,     olivm10, kc85_state,     empty_init, "Olivetti",          "M-10",                 MACHINE_SUPPORTS_SAVE )
//COMP( 1983, m10m,     kc85,    0,      kc85,     olivm10, kc85_state,     empty_init, "Olivetti",          "M-10 Modem (US)",      MACHINE_SUPPORTS_SAVE )
COMP( 1983, trsm100,  0,       0,      trsm100,  kc85,    trsm100_state,  empty_init, "Tandy Radio Shack", "TRS-80 Model 100",     MACHINE_SUPPORTS_SAVE )
COMP( 1986, tandy102, trsm100, 0,      tandy102, kc85,    trsm100_state,  empty_init, "Tandy Radio Shack", "Tandy 102",            MACHINE_SUPPORTS_SAVE )
COMP( 1983, pc8201,   0,       0,      pc8201,   pc8201,  pc8201_state,   empty_init, "NEC",               "PC-8201 (Japan)",      MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1983, pc8201a,  pc8201,  0,      pc8201,   pc8201a, pc8201_state,   empty_init, "NEC",               "PC-8201A",             MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1987, npc8300,  pc8201,  0,      pc8300,   pc8201a, pc8201_state,   empty_init, "NEC",               "PC-8300",              MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1984, tandy200, 0,       0,      tandy200, kc85,    tandy200_state, empty_init, "Tandy Radio Shack", "Tandy 200",            MACHINE_SUPPORTS_SAVE )



labtam.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * Labtam International 3000 Systems
 *
 * Sources:
 *  -
 *
 * TODO:
 *  - additional cards
 */

/*
3000 1983
???

3003 1983
Desktop
Board:
one - Z80 SBC
one - 8086 VDU/COMM
one - WD1002-HDO (Like Kaypro-10) https://retrocmp.de/kaypro/kay-p2_hrdw.htm#hdc
one - FDD 5'25 - Connect to WD1002 board
one - MFM HDD 5'25 NEC D5124 10MB - Connect to WD1002 board

3006 1983
Monoblock
Board:
one - Z80 SBC
one - 8086 VDU/COMM
two - FDD 8" - Connect to Z80 SBC board (to WD2793A on board)
one - FDD 5"25 - Connect to Z80 SBC board (to WD2793A on board)

3015-V32 1985
Tower
Board:
one - Z80 SBC
four - 8086 VDU/COMM (one board with main ROM and others slave ROM)
one - V32 (main processor board on CPU - NS32032, MMU - NS32082, FPU - NS32081, 2 Megabyte RAM), Optional SCSI contoler on board.
one or two - Additional one or two RAM board with 6 Megabyte.
one - INTERPHASE SMD 2190 (Controller SMD HDD)
one - Xylogics 472 (Controller 1/2 inch TAPE with PERTEC interface)
 */

#include "emu.h"

#include "bus/multibus/multibus.h"

#include "bus/multibus/labtam_3232.h"
#include "bus/multibus/labtam_vducom.h"
#include "bus/multibus/labtam_z80sbc.h"

#define VERBOSE 0
#include "logmacro.h"

namespace {

class labtam_state : public driver_device
{
public:
	labtam_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_bus(*this, "bus")
	{
	}

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

public:
	// machine config
	void labtam(machine_config &config);

private:
	required_device<multibus_device> m_bus;
};

void labtam_state::machine_start()
{
}

void labtam_state::machine_reset()
{
}

static void labtam_cards(device_slot_interface &device)
{
	device.option_add("labtam_3232",    LABTAM_3232);
	device.option_add("labtam_8086cpu", LABTAM_8086CPU);
	device.option_add("labtam_vducom",  LABTAM_VDUCOM);
	device.option_add("labtam_z80sbc",  LABTAM_Z80SBC);
}

void labtam_state::labtam(machine_config &config)
{
	MULTIBUS(config, m_bus, 10_MHz_XTAL); // FIXME: clock driven by bus master

	MULTIBUS_SLOT(config, "slot0", m_bus, labtam_cards, nullptr, false);
	MULTIBUS_SLOT(config, "slot1", m_bus, labtam_cards, nullptr, false);
	MULTIBUS_SLOT(config, "slot2", m_bus, labtam_cards, nullptr, false);
	MULTIBUS_SLOT(config, "slot3", m_bus, labtam_cards, "labtam_8086cpu", false);
	MULTIBUS_SLOT(config, "slot4", m_bus, labtam_cards, "labtam_vducom", false);
	MULTIBUS_SLOT(config, "slot5", m_bus, labtam_cards, "labtam_z80sbc", false);
}

ROM_START(labtam)
ROM_END

}

/*   YEAR  NAME    PARENT COMPAT MACHINE  INPUT  CLASS          INIT        COMPANY                 FULLNAME    FLAGS */
COMP(1983, labtam, 0,     0,     labtam,  0,     labtam_state,  empty_init, "Labtam International", "3006", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)



laser3k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

  laser3k.c
  Driver for VTech Laser 3000 / Dick Smith Electronics "The Cat"

  This machine is somewhat similar to a 48K Apple II if you blur your eyes
  a lot, but it generally fits in poorly with 100% compatible machines
  (no chance of a compatible language card or auxmem) so it gets its own driver.

  An "emulation cartridge" is required to run Apple II software; this appears
  to be just a language card with 16K bytes of RAM that overlay the BASIC
  ROM. The cartridge connector is based on the Apple II peripheral slot, but
  has nothing connected to the /DEVSEL pin and adds several nonstandard features.

  Banking theory:
  - 6502 has 4 banking windows, 0000-3FFF, 4000-7FFF, 8000-BFFF, C000-FFFF
  - Physical address space is 0x00000-0x3FFFF.  ROM and I/O at the top, RAM
    up to 0x2FFFF (192k max).
  - Each window has a bank number register at physical 3C07C/D/E/F

  Technical manual at:
  http://mirrors.apple2.org.za/Apple%20II%20Documentation%20Project/Computers/LASER/LASER%203000/Manuals/The%20Cat%20Technical%20Reference%20Manual.pdf

  TODO:
    - Finish keyboard
    - RGB graphics mode
    - FDC C800 page appears to be inside the FDC cartridge, need a dump :(  (can hack to use IWM in the meantime)
    - Centronics printer port (data at 3c090, read ack at 3c1c0, read busy at 3c1c2)

***************************************************************************/

#include "emu.h"

#include "bus/a2gameio/gameio.h"
#include "cpu/m6502/m6502.h"
#include "imagedev/cassette.h"
#include "machine/bankdev.h"
#include "machine/ram.h"
#include "machine/kb3600.h"
#include "sound/sn76496.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "utf8.h"


namespace {

enum
{
	TEXT = 0,
	HIRES,
	RGB,
	DHIRES
};

#define BLACK   0
#define DKRED   1
#define DKBLUE  2
#define PURPLE  3
#define DKGREEN 4
#define DKGRAY  5
#define BLUE    6
#define LTBLUE  7
#define BROWN   8
#define ORANGE  9
#define GRAY    10
#define PINK    11
#define GREEN   12
#define YELLOW  13
#define AQUA    14
#define WHITE   15

class laser3k_state : public driver_device
{
public:
	laser3k_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_ram(*this, "mainram")
		, m_bank(*this, "bank%u", 0U)
		, m_ay3600(*this, "ay3600")
		, m_speaker(*this, "speaker")
		, m_sn(*this, "sn76489")
		, m_cassette(*this, "tape")
		, m_gamepad(*this, "gamepad")
		, m_kbspecial(*this, "keyb_special")
	{ }

	void laser3k(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	uint8_t ram_r(offs_t offset);
	void ram_w(offs_t offset, uint8_t data);
	uint8_t io_r(offs_t offset);
	void io_w(offs_t offset, uint8_t data);
	uint8_t io2_r(offs_t offset);

	void laser3k_palette(palette_device &palette) const;
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void text_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow);
	void hgr_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow);
	void dhgr_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow);

	int ay3600_shift_r();
	int ay3600_control_r();
	void ay3600_data_ready_w(int state);

	void banks_map(address_map &map) ATTR_COLD;
	void laser3k_map(address_map &map) ATTR_COLD;

	void plot_text_character(bitmap_ind16 &bitmap, int xpos, int ypos, int xscale, uint32_t code, const uint8_t *textgfx_data, uint32_t textgfx_datalen);
	void do_io(int offset);

	required_device<m6502_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<ram_device> m_ram;
	required_device_array<address_map_bank_device, 4> m_bank;
	required_device<ay3600_device> m_ay3600;
	required_device<speaker_sound_device> m_speaker;
	required_device<sn76489_device> m_sn;
	required_device<cassette_image_device> m_cassette;
	required_device<apple2_gameio_device> m_gamepad;
	required_ioport m_kbspecial;

	uint8_t m_bankval[4];
	int m_flash;
	int m_speaker_state;
	int m_cassette_state;
	double m_joystick_x1_time, m_joystick_y1_time, m_joystick_x2_time, m_joystick_y2_time;
	double m_x_calibration, m_y_calibration;
	int m_disp_page;
	int m_bg_color, m_fg_color, m_border_color;
	uint16_t m_lastchar, m_strobe;
	uint8_t m_transchar;
	bool m_80col;
	bool m_mix;
	int m_gfxmode;
	std::unique_ptr<uint16_t[]> m_hires_artifact_map;
	std::unique_ptr<uint16_t[]> m_dhires_artifact_map;
};

/***************************************************************************
    ADDRESS MAP
***************************************************************************/

void laser3k_state::laser3k_map(address_map &map)
{
	map(0x0000, 0x3fff).m(m_bank[0], FUNC(address_map_bank_device::amap8));
	map(0x4000, 0x7fff).m(m_bank[1], FUNC(address_map_bank_device::amap8));
	map(0x8000, 0xbfff).m(m_bank[2], FUNC(address_map_bank_device::amap8));
	map(0xc000, 0xffff).m(m_bank[3], FUNC(address_map_bank_device::amap8));
}

void laser3k_state::banks_map(address_map &map)
{
	map(0x00000, 0x2ffff).rw(FUNC(laser3k_state::ram_r), FUNC(laser3k_state::ram_w));
	map(0x38000, 0x3bfff).rom().region("maincpu", 0);
	map(0x3c000, 0x3c0ff).rw(FUNC(laser3k_state::io_r), FUNC(laser3k_state::io_w));
	map(0x3c100, 0x3c1ff).r(FUNC(laser3k_state::io2_r));
	map(0x3c200, 0x3ffff).rom().region("maincpu", 0x4200);
}

void laser3k_state::machine_start()
{
	static const uint8_t hires_artifact_color_table[] =
	{
		BLACK,  PURPLE, GREEN,  WHITE,
		BLACK,  BLUE,   ORANGE, WHITE
	};

	static const uint8_t dhires_artifact_color_table[] =
	{
		BLACK,      DKGREEN,    BROWN,  GREEN,
		DKRED,      DKGRAY,     ORANGE, YELLOW,
		DKBLUE,     BLUE,       GRAY,   AQUA,
		PURPLE,     LTBLUE,     PINK,   WHITE
	};
	int i, j;
	uint16_t c;

	/* 2^3 dependent pixels * 2 color sets * 2 offsets */
	m_hires_artifact_map = std::make_unique<uint16_t[]>(8 * 2 * 2);

	/* 2^4 dependent pixels */
	m_dhires_artifact_map = std::make_unique<uint16_t[]>(16);

	/* build hires artifact map */
	for (i = 0; i < 8; i++)
	{
		for (j = 0; j < 2; j++)
		{
			if (i & 0x02)
			{
				if ((i & 0x05) != 0)
					c = 3;
				else
					c = j ? 2 : 1;
			}
			else
			{
				if ((i & 0x05) == 0x05)
					c = j ? 1 : 2;
				else
					c = 0;
			}
			m_hires_artifact_map[ 0 + j*8 + i] = hires_artifact_color_table[(c + 0) % 8];
			m_hires_artifact_map[16 + j*8 + i] = hires_artifact_color_table[(c + 4) % 8];
		}
	}

	/* build double hires artifact map */
	for (i = 0; i < 16; i++)
	{
		m_dhires_artifact_map[i] = dhires_artifact_color_table[i];
	}

	m_cassette_state = 0;
	m_cassette->output(-1.0f);

	m_x_calibration = attotime::from_nsec(10800).as_double();
	m_y_calibration = attotime::from_nsec(10800).as_double();
	m_joystick_x1_time = m_joystick_x2_time = m_joystick_y1_time = m_joystick_y2_time = 0.0;
}

void laser3k_state::machine_reset()
{
	m_bankval[0] = 0;
	m_bankval[1] = 1;
	m_bankval[2] = 2;
	m_bankval[3] = 0xf;
	m_bank[0]->set_bank(m_bankval[0]);
	m_bank[1]->set_bank(m_bankval[1]);
	m_bank[2]->set_bank(m_bankval[2]);
	m_bank[3]->set_bank(m_bankval[3]);

	m_flash = 0;
	m_speaker_state = 0;
	m_disp_page = 0;
	m_bg_color = 0;
	m_fg_color = 15;
	m_border_color = 0;
	m_strobe = 0;
	m_transchar = 0;
	m_lastchar = 0;
	m_80col = 0;
	m_mix = false;
	m_gfxmode = TEXT;

	uint8_t *rom = (uint8_t *)memregion("maincpu")->base();

	// patch out disk controller ID for now so it drops right into BASIC
	rom[0x4607] = 0;
}

uint8_t laser3k_state::ram_r(offs_t offset)
{
	return m_ram->read(offset);
}

void laser3k_state::ram_w(offs_t offset, uint8_t data)
{
	m_ram->write(offset, data);
}

// most softswitches don't care about read vs write, so handle them here
void laser3k_state::do_io(int offset)
{
	switch (offset)
	{
		case 0x08:  // set border color to black
			m_border_color = 0;
			break;
		case 0x09:  // set border color to red
			m_border_color = 1;
			break;
		case 0x0a:  // set border color to green
			m_border_color = 12;
			break;
		case 0x0b:  // set border color to yellow
			m_border_color = 13;
			break;
		case 0x0c:  // set border color to blue
			m_border_color = 6;
			break;
		case 0x0d:  // set border color to magenta
			m_border_color = 3;
			break;
		case 0x0e:  // set border color to cyan
			m_border_color = 14;
			break;
		case 0x0f:  // set border color to white
			m_border_color = 15;
			break;

		case 0x18:  // set bg color to black
			m_bg_color = 0;
			break;
		case 0x19:  // set bg color to red
			m_bg_color = 1;
			break;
		case 0x1a:  // set bg color to green
			m_bg_color = 12;
			break;
		case 0x1b:  // set bg color to yellow
			m_bg_color = 13;
			break;
		case 0x1c:  // set bg color to blue
			m_bg_color = 6;
			break;
		case 0x1d:  // set bg color to magenta
			m_bg_color = 3;
			break;
		case 0x1e:  // set bg color to cyan
			m_bg_color = 14;
			break;
		case 0x1f:  // set bg color to white
			m_bg_color = 15;
			break;

		case 0x20:
			m_cassette_state ^= 1;
			m_cassette->output(m_cassette_state ? 1.0f : -1.0f);
			break;

		case 0x28:  // set fg color to normal
			m_fg_color = 15;
			break;
		case 0x29:  // set fg color to red
			m_fg_color = 1;
			break;
		case 0x2a:  // set fg color to green
			m_fg_color = 12;
			break;
		case 0x2b:  // set fg color to yellow
			m_fg_color = 13;
			break;
		case 0x2c:  // set fg color to blue
			m_fg_color = 6;
			break;
		case 0x2d:  // set fg color to magenta
			m_fg_color = 3;
			break;
		case 0x2e:  // set fg color to cyan
			m_fg_color = 14;
			break;
		case 0x2f:  // set fg color to white
			m_fg_color = 15;
			break;

		case 0x30:
			m_speaker_state ^= 1;
			m_speaker->level_w(m_speaker_state);
			break;

		case 0x4c:  // low resolution (40 column)
			m_80col = false;
			m_maincpu->set_unscaled_clock(1021800);
			break;

		case 0x4d:  // RGB mode
			m_gfxmode = RGB;
			break;

		case 0x4e:  // double hi-res
			m_80col = true;
			m_gfxmode = DHIRES;
			m_maincpu->set_unscaled_clock(1021800*2);
			break;

		case 0x4f:  // high resolution (80 column).  Yes, the CPU clock also doubles when the pixel clock does (!)
			m_80col = true;
			m_maincpu->set_unscaled_clock(1021800*2);
			break;

		case 0x50:  // graphics mode
			m_gfxmode = HIRES;
			break;

		case 0x51:  // text mode
			m_gfxmode = TEXT;
			break;

		case 0x52:  // no mix
			m_mix = false;
			break;

		case 0x53:  // mixed mode
			m_mix = true;
			break;

		case 0x54:  // set page 1
			m_disp_page = 0;
			break;

		case 0x55:  // set page 2
			m_disp_page = 1;
			break;

		case 0x56:  // disable emulation (?)
			break;

		case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:  // paddle input reset
			if (machine().time().as_double() >= m_joystick_x1_time)
				m_joystick_x1_time = machine().time().as_double() + m_x_calibration * m_gamepad->pdl0_r();
			if (machine().time().as_double() >= m_joystick_y1_time)
				m_joystick_y1_time = machine().time().as_double() + m_y_calibration * m_gamepad->pdl1_r();
			if (machine().time().as_double() >= m_joystick_x2_time)
				m_joystick_x2_time = machine().time().as_double() + m_x_calibration * m_gamepad->pdl2_r();
			if (machine().time().as_double() >= m_joystick_y2_time)
				m_joystick_y2_time = machine().time().as_double() + m_y_calibration * m_gamepad->pdl3_r();
			break;

		default:
			printf("do_io: unknown softswitch @ %x\n", offset);
			break;
	}
}

uint8_t laser3k_state::io_r(offs_t offset)
{
	switch (offset)
	{
		case 0x00:  // keyboard latch
			return m_transchar | m_strobe;

		case 0x10:  // keyboard strobe
			{
				uint8_t rv = m_transchar | m_strobe;
				if (!machine().side_effects_disabled())
					m_strobe = 0;
				return rv;
			}

		case 0x60: // cassette in
			return m_cassette->input() > 0.0 ? 0x80 : 0;

		case 0x61:  // button 0
			return m_gamepad->sw0_r() ? 0x80 : 0;

		case 0x62:  // button 1
			return m_gamepad->sw1_r() ? 0x80 : 0;

		case 0x63:  // button 2
			return m_gamepad->sw2_r() ? 0x80 : 0;

		case 0x64:  // joy 1 X axis
			if (!m_gamepad->is_device_connected()) return 0x80;
			return (machine().time().as_double() < m_joystick_x1_time) ? 0x80 : 0;

		case 0x65:  // joy 1 Y axis
			if (!m_gamepad->is_device_connected()) return 0x80;
			return (machine().time().as_double() < m_joystick_y1_time) ? 0x80 : 0;

		case 0x66: // joy 2 X axis
			if (!m_gamepad->is_device_connected()) return 0x80;
			return (machine().time().as_double() < m_joystick_x2_time) ? 0x80 : 0;

		case 0x67: // joy 2 Y axis
			if (!m_gamepad->is_device_connected()) return 0x80;
			return (machine().time().as_double() < m_joystick_y2_time) ? 0x80 : 0;

		case 0x7c:
			return m_bankval[0];

		case 0x7d:
			return m_bankval[1];

		case 0x7e:
			return m_bankval[2];

		case 0x7f:
			return m_bankval[3];

		default:
			if (!machine().side_effects_disabled())
				do_io(offset);
			break;
	}

	return 0xff;
}

void laser3k_state::io_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
		case 0x10:  // clear keyboard latch
			m_strobe = 0;
			break;

		case 0x68: case 0x69: case 0x6a: case 0x6b: case 0x6c: case 0x6d: case 0x6e: case 0x6f:  // SN76489 sound
			m_sn->write(data);
			break;

		case 0x78:  // called "SYSTEM" in the boot ROM listing, but unsure what it does
			break;

		case 0x7c:  // bank 0
			m_bankval[0] = data & 0xf;
			m_bank[0]->set_bank(m_bankval[0]);
			break;

		case 0x7d:  // bank 1
			m_bankval[1] = data & 0xf;
			m_bank[1]->set_bank(m_bankval[1]);
			break;

		case 0x7e:  // bank 2
			m_bankval[2] = data & 0xf;
			m_bank[2]->set_bank(m_bankval[2]);
			break;

		case 0x7f:  // bank 3
			m_bankval[3] = data & 0xf;
			m_bank[3]->set_bank(m_bankval[3]);
			break;

		default:
			do_io(offset);
			break;
	}
}

uint8_t laser3k_state::io2_r(offs_t offset)
{
	switch (offset)
	{
		case 0xc2:  // h-blank status
			return m_screen->hblank() ? 0x80 : 0x00;

		case 0xc3:  // v-blank status
			return m_screen->vblank() ? 0x80 : 0x00;

		case 0xc5:  // CPU 1/2 MHz status?
			return 0x00;

		default:
			printf("io2_r @ unknown %x\n", offset);
			break;
	}

	return 0xff;
}

void laser3k_state::plot_text_character(bitmap_ind16 &bitmap, int xpos, int ypos, int xscale, uint32_t code,
	const uint8_t *textgfx_data, uint32_t textgfx_datalen)
{
	int fg = m_fg_color;
	int bg = m_bg_color;

	/* look up the character data */
	uint8_t const *const chardata = &textgfx_data[(code * 8) % textgfx_datalen];

	if (m_flash && (code >= 0x40) && (code <= 0x7f))
	{
		/* we're flashing; swap */
		using std::swap;
		swap(fg, bg);
	}

	for (int y = 0; y < 8; y++)
	{
		for (int x = 0; x < 7; x++)
		{
			uint16_t color = (chardata[y] & (1 << (6-x))) ? fg : bg;

			for (int i = 0; i < xscale; i++)
			{
				bitmap.pix(ypos + y, xpos + (x * xscale) + i) = color;
			}
		}
	}
}

void laser3k_state::text_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
	int row, col;
	uint32_t start_address;
	uint32_t address;
	uint8_t *m_a2_videoram = m_ram->pointer();

	if (m_80col)
	{
		start_address = (m_disp_page == 0) ? 0x1000 : 0x1800;
	}
	else
	{
		start_address = (m_disp_page == 0) ? 0x400 : 0x800;
	}

	m_flash = ((machine().time() * 4).seconds() & 1) ? 1 : 0;

	beginrow = std::max(beginrow, cliprect.min_y - (cliprect.min_y % 8));
	endrow = std::min(endrow, cliprect.max_y - (cliprect.max_y % 8) + 7);

	for (row = beginrow; row <= endrow; row += 8)
	{
		if (m_80col)
		{
			for (col = 0; col < 40; col++)
			{
				/* calculate address */
				address = start_address + ((((row/8) & 0x07) << 7) | (((row/8) & 0x18) * 5 + col));

				plot_text_character(bitmap, col * 7, row, 1, m_a2_videoram[address],
					memregion("gfx1")->base(), memregion("gfx1")->bytes());
				plot_text_character(bitmap, (col + 40) * 7, row, 1, m_a2_videoram[address+0x400],
					memregion("gfx1")->base(), memregion("gfx1")->bytes());
			}
		}
		else
		{
			for (col = 0; col < 40; col++)
			{
				/* calculate address */
				address = start_address + ((((row/8) & 0x07) << 7) | (((row/8) & 0x18) * 5 + col));
				plot_text_character(bitmap, col * 14, row, 2, m_a2_videoram[address],
					memregion("gfx1")->base(), memregion("gfx1")->bytes());
			}
		}
	}
}

void laser3k_state::hgr_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
	/* sanity checks */
	if (beginrow < cliprect.min_y)
		beginrow = cliprect.min_y;
	if (endrow > cliprect.max_y)
		endrow = cliprect.max_y;
	if (endrow < beginrow)
		return;

	uint8_t const *const vram = m_ram->pointer() + (m_disp_page ? 0x4000 : 0x2000);

	uint8_t vram_row[42];
	vram_row[0] = 0;
	vram_row[41] = 0;

	for (int row = beginrow; row <= endrow; row++)
	{
		for (int col = 0; col < 40; col++)
		{
			int offset = ((((row/8) & 0x07) << 7) | (((row/8) & 0x18) * 5 + col)) | ((row & 7) << 10);
			vram_row[1+col] = vram[offset];
		}

		uint16_t *p = &bitmap.pix(row);

		for (int col = 0; col < 40; col++)
		{
			uint32_t w =    (((uint32_t) vram_row[col+0] & 0x7f) <<  0)
						|   (((uint32_t) vram_row[col+1] & 0x7f) <<  7)
						|   (((uint32_t) vram_row[col+2] & 0x7f) << 14);

			uint16_t const *const artifact_map_ptr = &m_hires_artifact_map[((vram_row[col+1] & 0x80) >> 7) * 16];
			for (int b = 0; b < 7; b++)
			{
				uint16_t v = artifact_map_ptr[((w >> (b + 7-1)) & 0x07) | (((b ^ col) & 0x01) << 3)];
				*(p++) = v;
				*(p++) = v;
			}
		}
	}
}

void laser3k_state::dhgr_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
	/* sanity checks */
	if (beginrow < cliprect.min_y)
		beginrow = cliprect.min_y;
	if (endrow > cliprect.max_y)
		endrow = cliprect.max_y;
	if (endrow < beginrow)
		return;

	uint8_t const *const vram = m_ram->pointer() + (m_disp_page ? 0x8000 : 0x4000);

	uint8_t vram_row[82];
	vram_row[0] = 0;
	vram_row[81] = 0;

	for (int row = beginrow; row <= endrow; row++)
	{
		for (int col = 0; col < 40; col++)
		{
			int offset = ((((row/8) & 0x07) << 7) | (((row/8) & 0x18) * 5 + col)) | ((row & 7) << 10);
			if (col < 40)
			{
				vram_row[1+(col*2)+0] = vram[offset];
				vram_row[1+(col*2)+1] = vram[offset+1];
			}
			else
			{
				// FIXME: this is unreachable - loop bound is checks (col < 40) so these columns can never be copied
				vram_row[1+(col*2)+0] = vram[offset+0x2000];
				vram_row[1+(col*2)+1] = vram[offset+0x2001];
			}
		}

		uint16_t *p = &bitmap.pix(row);

		for (int col = 0; col < 80; col++)
		{
			uint32_t w =    (((uint32_t) vram_row[col+0] & 0x7f) <<  0)
						|   (((uint32_t) vram_row[col+1] & 0x7f) <<  7)
						|   (((uint32_t) vram_row[col+2] & 0x7f) << 14);

			for (int b = 0; b < 7; b++)
			{
				uint16_t v = m_dhires_artifact_map[((((w >> (b + 7-1)) & 0x0F) * 0x11) >> (((2-(col*7+b))) & 0x03)) & 0x0F];
				*(p++) = v;
			}
		}
	}
}

uint32_t laser3k_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	switch (m_gfxmode)
	{
		case TEXT:
			text_update(screen, bitmap, cliprect, 0, 191);
			break;

		case HIRES:
			if (m_mix)
			{
				hgr_update(screen, bitmap, cliprect, 0, 159);
				text_update(screen, bitmap, cliprect, 160, 191);
			}
			else
			{
				hgr_update(screen, bitmap, cliprect, 0, 191);
			}
			break;

		case RGB:
			break;

		case DHIRES:
			if (m_mix)
			{
				dhgr_update(screen, bitmap, cliprect, 0, 159);
				text_update(screen, bitmap, cliprect, 160, 191);
			}
			else
			{
				dhgr_update(screen, bitmap, cliprect, 0, 191);
			}
			break;
	}

	return 0;
}

int laser3k_state::ay3600_shift_r()
{
	// either shift key
	if (m_kbspecial->read() & 0x06)
	{
		return ASSERT_LINE;
	}

	return CLEAR_LINE;
}

int laser3k_state::ay3600_control_r()
{
	if (m_kbspecial->read() & 0x08)
	{
		return ASSERT_LINE;
	}

	return CLEAR_LINE;
}

static const uint8_t key_remap[0x32][4] =
{
/*    norm shft ctrl both */
	{ 0x33,0x23,0x33,0x23 },    /* 3 #     00     */
	{ 0x34,0x24,0x34,0x24 },    /* 4 $     01     */
	{ 0x35,0x25,0x35,0x25 },    /* 5 %     02     */
	{ 0x36,0x5e,0x35,0x53 },    /* 6 ^     03     */
	{ 0x37,0x26,0x37,0x26 },    /* 7 &     04     */
	{ 0x38,0x2a,0x38,0x2a },    /* 8 *     05     */
	{ 0x39,0x28,0x39,0x28 },    /* 9 (     06     */
	{ 0x30,0x29,0x30,0x29 },    /* 0 )     07     */
	{ 0x3b,0x3a,0x3b,0x3a },    /* ; :     08     */
	{ 0x2d,0x5f,0x2d,0x1f },    /* - _     09     */
	{ 0x51,0x51,0x11,0x11 },    /* q Q     0a     */
	{ 0x57,0x57,0x17,0x17 },    /* w W     0b     */
	{ 0x45,0x45,0x05,0x05 },    /* e E     0c     */
	{ 0x52,0x52,0x12,0x12 },    /* r R     0d     */
	{ 0x54,0x54,0x14,0x14 },    /* t T     0e     */
	{ 0x59,0x59,0x19,0x19 },    /* y Y     0f     */
	{ 0x55,0x55,0x15,0x15 },    /* u U     10     */
	{ 0x49,0x49,0x09,0x09 },    /* i I     11     */
	{ 0x4f,0x4f,0x0f,0x0f },    /* o O     12     */
	{ 0x50,0x50,0x10,0x10 },    /* p P     13     */
	{ 0x44,0x44,0x04,0x04 },    /* d D     14     */
	{ 0x46,0x46,0x06,0x06 },    /* f F     15     */
	{ 0x47,0x47,0x07,0x07 },    /* g G     16     */
	{ 0x48,0x48,0x08,0x08 },    /* h H     17     */
	{ 0x4a,0x4a,0x0a,0x0a },    /* j J     18     */
	{ 0x4b,0x4b,0x0b,0x0b },    /* k K     19     */
	{ 0x4c,0x4c,0x0c,0x0c },    /* l L     1a     */
	{ 0x3d,0x2b,0x3d,0x2b },    /* = +     1b     */
	{ 0x08,0x08,0x08,0x08 },    /* Left    1c     */
	{ 0x15,0x15,0x15,0x15 },    /* Right   1d     */
	{ 0x5a,0x5a,0x1a,0x1a },    /* z Z     1e     */
	{ 0x58,0x58,0x18,0x18 },    /* x X     1f     */
	{ 0x43,0x43,0x03,0x03 },    /* c C     20     */
	{ 0x56,0x56,0x16,0x16 },    /* v V     21     */
	{ 0x42,0x42,0x02,0x02 },    /* b B     22     */
	{ 0x4e,0x4e,0x0e,0x0e },    /* n N     23     */
	{ 0x4d,0x4d,0x0d,0x0d },    /* m M     24     */
	{ 0x2c,0x3c,0x2c,0x3c },    /* , <     25     */
	{ 0x2e,0x3e,0x2e,0x3e },    /* . >     26     */
	{ 0x2f,0x3f,0x2f,0x3f },    /* / ?     27     */
	{ 0x53,0x53,0x13,0x13 },    /* s S     28     */
	{ 0x32,0x40,0x32,0x00 },    /* 2 @     29     */
	{ 0x31,0x21,0x31,0x31 },    /* 1 !     2a     */
	{ 0x9b,0x9b,0x9b,0x9b },    /* Escape  2b     */
	{ 0x41,0x41,0x01,0x01 },    /* a A     2c     */
	{ 0x20,0x20,0x20,0x20 },    /* Space   2d     */
	{ 0x27,0x22,0x27,0x22 },    /* ' "     2e     */
	{ 0x00,0x00,0x00,0x00 },    /* 0x2f unused    */
	{ 0x00,0x00,0x00,0x00 },    /* 0x30 unused    */
	{ 0x0d,0x0d,0x0d,0x0d },    /* Enter   31     */
};

void laser3k_state::ay3600_data_ready_w(int state)
{
	if (state == ASSERT_LINE)
	{
		int mod = 0;
		m_lastchar = m_ay3600->b_r();

		mod = (m_kbspecial->read() & 0x06) ? 0x01 : 0x00;
		mod |= (m_kbspecial->read() & 0x08) ? 0x02 : 0x00;

//      printf("lastchar = %02x\n", m_lastchar & 0x3f);

		m_transchar = key_remap[m_lastchar&0x3f][mod];

		if (m_transchar != 0)
		{
			m_strobe = 0x80;
//          printf("new char = %04x (%02x)\n", m_lastchar&0x3f, m_transchar);
		}
	}
}

/***************************************************************************
    INPUT PORTS
***************************************************************************/

static INPUT_PORTS_START( laser3k )
	PORT_START("X0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)  PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)  PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)  PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)  PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)  PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)  PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)  PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)      PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)  PORT_CHAR('-') PORT_CHAR('_')

	PORT_START("X1")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)  PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)  PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)  PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)  PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)  PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)  PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)  PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)  PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)  PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)  PORT_CHAR('P') PORT_CHAR('p')

	PORT_START("X2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)  PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)  PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)  PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)  PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)  PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)  PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)  PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT)      PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT)     PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

	PORT_START("X3")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)  PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)  PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)  PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)  PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)          PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)  PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)  PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)  PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)   PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)  PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("X4")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)  PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)  PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)      PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Esc")      PORT_CODE(KEYCODE_ESC)      PORT_CHAR(27)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)  PORT_CHAR(' ')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)  PORT_CHAR('\'') PORT_CHAR('\"')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Return")   PORT_CODE(KEYCODE_ENTER)    PORT_CHAR(13)

	PORT_START("X5")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X6")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X7")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X8")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("keyb_special")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Shift")   PORT_CODE(KEYCODE_LSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Shift")  PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Control")      PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RESET")        PORT_CODE(KEYCODE_F12)
INPUT_PORTS_END

// this is an apple II palette; it seems more likely the
// actual laser3000 has a digital RGB palette...
static constexpr rgb_t laser3k_pens[] =
{
	{ 0x00, 0x00, 0x00 }, // Black
	{ 0xe3, 0x1e, 0x60 }, // Dark Red
	{ 0x60, 0x4e, 0xbd }, // Dark Blue
	{ 0xff, 0x44, 0xfd }, // Purple
	{ 0x00, 0xa3, 0x60 }, // Dark Green
	{ 0x9c, 0x9c, 0x9c }, // Dark Gray
	{ 0x14, 0xcf, 0xfd }, // Medium Blue
	{ 0xd0, 0xc3, 0xff }, // Light Blue
	{ 0x60, 0x72, 0x03 }, // Brown
	{ 0xff, 0x6a, 0x3c }, // Orange
	{ 0x9c, 0x9c, 0x9c }, // Light Grey
	{ 0xff, 0xa0, 0xd0 }, // Pink
	{ 0x14, 0xf5, 0x3c }, // Light Green
	{ 0xd0, 0xdd, 0x8d }, // Yellow
	{ 0x72, 0xff, 0xd0 }, // Aquamarine
	{ 0xff, 0xff, 0xff }  // White
};

/* Initialize the palette */
void laser3k_state::laser3k_palette(palette_device &palette) const
{
	palette.set_pen_colors(0, laser3k_pens);
}

void laser3k_state::laser3k(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, 14_MHz_XTAL / 7); // 1 or 2 MHz depending on soft switch
	m_maincpu->set_addrmap(AS_PROGRAM, &laser3k_state::laser3k_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_size(300*2, 192);
	m_screen->set_visarea(0, (280*2)-1,0,192-1);
	m_screen->set_screen_update(FUNC(laser3k_state::screen_update));
	m_screen->set_palette("palette");

	PALETTE(config, "palette", FUNC(laser3k_state::laser3k_palette), std::size(laser3k_pens));

	/* memory banking */
	for (auto &bank : m_bank)
		ADDRESS_MAP_BANK(config, bank).set_map(&laser3k_state::banks_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);

	RAM(config, "mainram").set_default_size("192K");

	APPLE2_GAMEIO(config, m_gamepad, apple2_gameio_device::joystick_options, nullptr);
	m_gamepad->set_sw_pullups(true); // 3K9 pullups to +5V

	/* the 8048 isn't dumped, so substitute modified real Apple II h/w */
	AY3600(config, m_ay3600, 0);
	m_ay3600->x0().set_ioport("X0");
	m_ay3600->x1().set_ioport("X1");
	m_ay3600->x2().set_ioport("X2");
	m_ay3600->x3().set_ioport("X3");
	m_ay3600->x4().set_ioport("X4");
	m_ay3600->x5().set_ioport("X5");
	m_ay3600->x6().set_ioport("X6");
	m_ay3600->x7().set_ioport("X7");
	m_ay3600->x8().set_ioport("X8");
	m_ay3600->shift().set(FUNC(laser3k_state::ay3600_shift_r));
	m_ay3600->control().set(FUNC(laser3k_state::ay3600_control_r));
	m_ay3600->data_ready().set(FUNC(laser3k_state::ay3600_data_ready_w));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);
	SN76489(config, m_sn, 14_MHz_XTAL / 7).add_route(ALL_OUTPUTS, "mono", 0.50);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED);
	m_cassette->set_interface("apple2_cass");
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
}

ROM_START(las3000)
	ROM_REGION(0x0800,"gfx1",0)
	ROM_LOAD ( "341-0036.chr", 0x0000, 0x0800, CRC(64f415c6) SHA1(f9d312f128c9557d9d6ac03bfad6c3ddf83e5659))

	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD ( "las3000.rom", 0x0000, 0x8000, CRC(9c7aeb09) SHA1(3302adf41e258cf50210c19736948c8fa65e91de))

	ROM_REGION(0x100, "fdc", 0)
	ROM_LOAD ( "l3kdisk.rom", 0x0000, 0x0100, CRC(2d4b1584) SHA1(989780b77e100598124df7b72663e5a31a3339c0))
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY             FULLNAME      FLAGS
COMP( 1983, las3000, 0,      0,      laser3k, laser3k, laser3k_state, empty_init, "Video Technology", "Laser 3000", MACHINE_NOT_WORKING )



lb186.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Carl
// TODO: SCSI, requires NCR5380 BSY IRQs
#include "emu.h"
#include "cpu/i86/i186.h"
#include "machine/wd_fdc.h"
#include "machine/mc68681.h"
#include "bus/nscsi/hd.h"
#include "bus/rs232/rs232.h"
#include "machine/ncr5380.h"
#include "imagedev/floppy.h"
#include "formats/naslite_dsk.h"


namespace {

class lb186_state : public driver_device
{
public:
	lb186_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_fdc(*this, "fdc")
		, m_floppies(*this, "fdc:%u", 0U)
		, m_scsi(*this, "scsibus:7:ncr5380")
	{
	}

	void lb186(machine_config &config);

private:
	void sio_out_w(uint8_t data);
	void drive_sel_w(uint8_t data);
	static void floppy_formats(format_registration &fr);
	static void ncr5380(device_t *device);
	void lb186_io(address_map &map) ATTR_COLD;
	void lb186_map(address_map &map) ATTR_COLD;

	required_device<i80186_cpu_device> m_maincpu;
	required_device<wd1772_device> m_fdc;
	required_device_array<floppy_connector, 4> m_floppies;
	required_device<ncr5380_device> m_scsi;
};

void lb186_state::sio_out_w(uint8_t data)
{
	m_fdc->mr_w(BIT(data, 5));
	m_maincpu->tmrin1_w(BIT(data, 3) ? ASSERT_LINE : CLEAR_LINE);
}

void lb186_state::drive_sel_w(uint8_t data)
{
	m_fdc->dden_w(BIT(data, 5));

	unsigned int drive = data & 0xf;
	switch(drive)
	{
		case 0:
			return;
		case 1:
			drive = 0;
			break;
		case 2:
			drive = 1;
			break;
		case 4:
			drive = 2;
			break;
		case 8:
			drive = 3;
			break;
		default:
			logerror("More than one drive enabled!\n");
			return;
	}

	floppy_image_device *const floppy = m_floppies[drive]->get_device();
	m_fdc->set_floppy(floppy);
	floppy->ss_w(BIT(data, 4));
}

void lb186_state::lb186_map(address_map &map)
{
	map(0x00000, 0x3ffff).ram(); // fixed 256k for now
	map(0xfc000, 0xfffff).rom().region("bios", 0);
}

void lb186_state::lb186_io(address_map &map)
{
	map.unmap_value_high();
	map(0x1000, 0x101f).rw("duart", FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask16(0x00ff);
	map(0x1080, 0x108f).rw(m_scsi, FUNC(ncr5380_device::read), FUNC(ncr5380_device::write)).umask16(0x00ff);
	map(0x1100, 0x1107).rw(m_fdc, FUNC(wd1772_device::read), FUNC(wd1772_device::write)).umask16(0x00ff);
	map(0x1180, 0x1180).rw(m_scsi, FUNC(ncr5380_device::dma_r), FUNC(ncr5380_device::dma_w));
	map(0x1200, 0x1200).w(FUNC(lb186_state::drive_sel_w));
}

static void lb186_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

void lb186_state::ncr5380(device_t *device)
{
	devcb_base *devcb;
	(void)devcb;
	downcast<ncr5380_device &>(*device).irq_handler().set(":maincpu", FUNC(i80186_cpu_device::int1_w));
	downcast<ncr5380_device &>(*device).drq_handler().set(":maincpu", FUNC(i80186_cpu_device::drq0_w));
}

static void scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
	device.option_add_internal("ncr5380", NCR5380);
}

void lb186_state::floppy_formats(format_registration &fr)
{
	fr.add_pc_formats();
	fr.add(FLOPPY_NASLITE_FORMAT);
}

void lb186_state::lb186(machine_config &config)
{
	I80186(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &lb186_state::lb186_map);
	m_maincpu->set_addrmap(AS_IO, &lb186_state::lb186_io);

	scn2681_device &duart(SCN2681(config, "duart", 3.6864_MHz_XTAL));
	duart.irq_cb().set(m_maincpu, FUNC(i80186_cpu_device::int0_w));
	duart.a_tx_cb().set("rs232_1", FUNC(rs232_port_device::write_txd));
	duart.b_tx_cb().set("rs232_2", FUNC(rs232_port_device::write_txd));
	duart.outport_cb().set(FUNC(lb186_state::sio_out_w));

	rs232_port_device &rs232_1(RS232_PORT(config, "rs232_1", default_rs232_devices, "terminal"));
	rs232_1.rxd_handler().set("duart", FUNC(scn2681_device::rx_a_w));
	rs232_port_device &rs232_2(RS232_PORT(config, "rs232_2", default_rs232_devices, nullptr));
	rs232_2.rxd_handler().set("duart", FUNC(scn2681_device::rx_b_w));

	WD1772(config, m_fdc, 16_MHz_XTAL / 2);
	m_fdc->intrq_wr_callback().set(m_maincpu, FUNC(i80186_cpu_device::int2_w));
	m_fdc->drq_wr_callback().set(m_maincpu, FUNC(i80186_cpu_device::drq0_w));
	FLOPPY_CONNECTOR(config, m_floppies[0], lb186_floppies, "525dd", lb186_state::floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppies[1], lb186_floppies, nullptr, lb186_state::floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppies[2], lb186_floppies, nullptr, lb186_state::floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppies[3], lb186_floppies, nullptr, lb186_state::floppy_formats);

	NSCSI_BUS(config, "scsibus");
	NSCSI_CONNECTOR(config, "scsibus:0", scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsibus:1", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:2", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:3", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:4", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:5", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:6", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:7", scsi_devices, "ncr5380", true).set_option_machine_config("ncr5380", lb186_state::ncr5380);
}

ROM_START( lb186 )
	ROM_REGION16_LE(0x4000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "v335", "BIOS Version 3.35") // 28 January 1987
	ROMX_LOAD("a75515_v3.35.rom", 0x0000, 0x2000, CRC(245824fb) SHA1(b39ed91d421513f5912fdbc290aaa3f1b7d4f1e0), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("a75516_v3.35.rom", 0x0001, 0x2000, CRC(9d9a5e22) SHA1(070be31c622f50508e8cbdb797c79978b6a4b8f6), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "ramdisk", "RAM Disk BIOS Version 1.00")
	ROMX_LOAD("a75523.rom", 0x0000, 0x2000, CRC(2d22e826) SHA1(e366e489f580b440131ad5212722391b60af90cd), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("a75524.rom", 0x0001, 0x2000, CRC(9c9b249c) SHA1(e988e92d9fa6fe66f89ef748021e9a0501d2807e), ROM_SKIP(1) | ROM_BIOS(1))
ROM_END

} // anonymous namespace


COMP( 1985, lb186, 0, 0, lb186, 0, lb186_state, empty_init, "Ampro Computers", "Little Board/186", MACHINE_NO_SOUND_HW )



lbpc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Ampro Little Board/PC.

    This is unusual among PC/XT-compatible machines in that many standard
    peripheral functions, including the interrupt and refresh controllers,
    are integrated into the V40 CPU itself, with some software assistance
    to compensate for DMAC incompatibilities. Two Vadem SDIP64 ASICs and a
    standard FDC and UART provide most other PC-like hardware features. The
    BIOS also supports the onboard SCSI controller.

****************************************************************************/

#include "emu.h"
#include "bus/isa/isa.h"
#include "bus/isa/isa_cards.h"
#include "bus/nscsi/devices.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "bus/rs232/rs232.h"
#include "cpu/nec/v5x.h"
#include "imagedev/floppy.h"
#include "machine/ins8250.h"
#include "machine/ncr5380.h"
#include "machine/upd765.h"
#include "sound/spkrdev.h"
#include "softlist_dev.h"
#include "speaker.h"

#define VERBOSE 0
#include "logmacro.h"


namespace {

class lbpc_state : public driver_device
{
public:
	lbpc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_expbus(*this, "expbus")
		, m_fdc(*this, "fdc")
		, m_scsic(*this, "scsi:7:ncr")
		, m_kbd(*this, "kbd")
		, m_speaker(*this, "speaker")
		, m_dma_channel(0xff)
		, m_eop_active(false)
		, m_port61(0xff)
		, m_speaker_data(false)
		, m_kbd_clock(true)
		, m_kbd_data(true)
		, m_kbd_irq(false)
		, m_kbd_input(0xff)
	{
	}

	void lbpc(machine_config &config);

	int hsi_r();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u8 exp_dack1_r();
	void exp_dack1_w(u8 data);
	void iochck_w(int state);
	template <int Line> void dmaak_w(int state);
	void eop_w(int state);

	void keyboard_shift_in();
	void kbd_clock_w(int state);
	void kbd_data_w(int state);
	u8 keyboard_r();
	u8 port61_r();
	void port61_w(u8 data);
	u8 port62_r();
	void tout2_w(int state);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<v40_device> m_maincpu;
	required_device<isa8_device> m_expbus;
	required_device<wd37c65c_device> m_fdc;
	required_device<ncr53c80_device> m_scsic;
	required_device<pc_kbdc_device> m_kbd;
	required_device<speaker_sound_device> m_speaker;

	u8 m_dma_channel;
	bool m_eop_active;
	u8 m_port61;
	bool m_speaker_data;
	bool m_kbd_clock;
	bool m_kbd_data;
	bool m_kbd_irq;
	u8 m_kbd_input;
};


void lbpc_state::machine_start()
{
	save_item(NAME(m_dma_channel));
	save_item(NAME(m_eop_active));
	save_item(NAME(m_port61));
	save_item(NAME(m_speaker_data));
	save_item(NAME(m_kbd_clock));
	save_item(NAME(m_kbd_irq));
	save_item(NAME(m_kbd_input));
}

void lbpc_state::machine_reset()
{
	port61_w(0);
}


u8 lbpc_state::exp_dack1_r()
{
	return m_expbus->dack_r(0);
}

void lbpc_state::exp_dack1_w(u8 data)
{
	m_expbus->dack_w(0, data);
}

void lbpc_state::iochck_w(int state)
{
	// TODO
}

template <int Line>
void lbpc_state::dmaak_w(int state)
{
	m_expbus->dack_line_w(Line + 1, state);
	if (!state)
	{
		m_dma_channel = Line;
		if (m_eop_active)
		{
			m_expbus->eop_w(Line + 1, ASSERT_LINE);
			if (Line == 1)
				m_fdc->tc_w(1);
			if (Line == 2)
				m_scsic->eop_w(1);
		}
	}
	else if (m_dma_channel == Line)
	{
		m_dma_channel = 0xff;
		if (m_eop_active)
		{
			m_expbus->eop_w(Line + 1, CLEAR_LINE);
			if (Line == 1)
				m_fdc->tc_w(0);
			if (Line == 2)
				m_scsic->eop_w(0);
		}
	}
}

void lbpc_state::eop_w(int state)
{
	m_eop_active = state == ASSERT_LINE;
	if (m_dma_channel != 0xff)
	{
		m_expbus->eop_w(m_dma_channel + 1, state);
		if (m_dma_channel == 1)
			m_fdc->tc_w(m_eop_active);
		if (m_dma_channel == 2)
			m_scsic->eop_w(m_eop_active);
	}
}

void lbpc_state::keyboard_shift_in()
{
	if (BIT(m_port61, 7) || m_kbd_irq)
		return;

	if (BIT(m_kbd_input, 0))
	{
		m_kbd_irq = true;
		m_maincpu->set_input_line(INPUT_LINE_IRQ1, 1);
	}

	m_kbd_input >>= 1;
	if (m_kbd_data)
	{
		m_kbd_input |= 0x80;
		LOG("%s: Shifting in 1 bit (%02X)\n", machine().describe_context(), m_kbd_input);
	}
	else
		LOG("%s: Shifting in 0 bit (%02X)\n", machine().describe_context(), m_kbd_input);
}

void lbpc_state::kbd_clock_w(int state)
{
	if (m_kbd_clock && !state)
		keyboard_shift_in();
	m_kbd_clock = state;
}

void lbpc_state::kbd_data_w(int state)
{
	m_kbd_data = state;
}

u8 lbpc_state::keyboard_r()
{
	return m_kbd_input;
}

u8 lbpc_state::port61_r()
{
	return m_port61;
}

void lbpc_state::port61_w(u8 data)
{
	if (BIT(m_port61, 1) && !BIT(data, 1))
		m_speaker->level_w(0);
	else if (!BIT(m_port61, 1) && BIT(data, 1))
		m_speaker->level_w(m_speaker_data);
	m_maincpu->tctl2_w(BIT(data, 0));

	if (BIT(m_port61, 3) != BIT(data, 3))
	{
		LOG("%s: PB3 changed to %d\n", machine().describe_context(), BIT(data, 3));
		m_kbd->data_write_from_mb(BIT(data, 3));
	}
	if (BIT(m_port61, 6) != BIT(data, 6))
	{
		LOG("%s: PB6 changed to %d\n", machine().describe_context(), BIT(data, 6));
		m_kbd->clock_write_from_mb(BIT(data, 6));
	}
	if (BIT(m_port61, 7) != BIT(data, 7))
	{
		LOG("%s: PB7 changed to %d\n", machine().describe_context(), BIT(data, 7));
		if (BIT(data, 7))
		{
			m_kbd_input = 0;
			if (m_kbd_irq)
			{
				m_kbd_irq = false;
				m_maincpu->set_input_line(INPUT_LINE_IRQ1, 0);
			}
		}
	}
	if (BIT(m_port61, 2) != BIT(data, 2))
		LOG("%s: PB2 changed to %d\n", machine().describe_context(), BIT(data, 2));

	m_port61 = data;
}

u8 lbpc_state::port62_r()
{
	return 0;
}

void lbpc_state::tout2_w(int state)
{
	m_speaker_data = state;
	if (BIT(m_port61, 1))
		m_speaker->level_w(state);
}

int lbpc_state::hsi_r()
{
	// TODO
	return 0;
}

void lbpc_state::mem_map(address_map &map)
{
	map(0x00000, 0x9ffff).ram(); // 256K, 512K or 768K DRAM
	// 0xE0000–0xEFFFF: empty socket
	// 0xF0000-0xF7FFF: empty socket
	map(0xf8000, 0xfffff).rom().region("bios", 0);
}

void lbpc_state::io_map(address_map &map)
{
	map(0x0060, 0x0060).r(FUNC(lbpc_state::keyboard_r));
	map(0x0061, 0x0061).rw(FUNC(lbpc_state::port61_r), FUNC(lbpc_state::port61_w));
	map(0x0062, 0x0062).r(FUNC(lbpc_state::port62_r));
	map(0x0330, 0x0337).rw(m_scsic, FUNC(ncr53c80_device::read), FUNC(ncr53c80_device::write));
	map(0x0338, 0x0338).portr("JUMPERS");
	map(0x0378, 0x037f).unmaprw(); // parallel printer port (ASIC1)
	map(0x03f2, 0x03f2).w(m_fdc, FUNC(wd37c65c_device::dor_w));
	map(0x03f4, 0x03f5).m(m_fdc, FUNC(wd37c65c_device::map));
	map(0x03f7, 0x03f7).w(m_fdc, FUNC(wd37c65c_device::ccr_w));
	map(0x03f8, 0x03ff).rw("com", FUNC(ins8250_device::ins8250_r), FUNC(ins8250_device::ins8250_w));
}


static INPUT_PORTS_START(lbpc)
	PORT_START("JUMPERS")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_READ_LINE_MEMBER(FUNC(lbpc_state::hsi_r))
	PORT_DIPNAME(0x28, 0x28, "Drive A Type") PORT_DIPLOCATION("W26+W28:1,2")
	PORT_DIPSETTING(0x28, "360K, 5-1/4\"")
	PORT_DIPSETTING(0x08, "1.2M, 5-1/4\"")
	PORT_DIPSETTING(0x20, "720K, 3-1/2\"")
	PORT_DIPSETTING(0x00, "1.44M, 3-1/2\"")
	PORT_DIPNAME(0x50, 0x50, "Drive B Type") PORT_DIPLOCATION("W27+W29:1,2")
	PORT_DIPSETTING(0x50, "360K, 5-1/4\"")
	PORT_DIPSETTING(0x10, "1.2M, 5-1/4\"")
	PORT_DIPSETTING(0x40, "720K, 3-1/2\"")
	PORT_DIPSETTING(0x00, "1.44M, 3-1/2\"")
	PORT_DIPNAME(0x07, 0x07, "SCSI Initiator ID") PORT_DIPLOCATION("W25-W23:3,2,1")
	PORT_DIPSETTING(0x00, "0")
	PORT_DIPSETTING(0x01, "1")
	PORT_DIPSETTING(0x02, "2")
	PORT_DIPSETTING(0x03, "3")
	PORT_DIPSETTING(0x04, "4")
	PORT_DIPSETTING(0x05, "5")
	PORT_DIPSETTING(0x06, "6")
	PORT_DIPSETTING(0x07, "7")
INPUT_PORTS_END


static void lbpc_floppies(device_slot_interface &device)
{
	device.option_add("525sd", FLOPPY_525_SD);
	device.option_add("525hd", FLOPPY_525_HD);
	device.option_add("35dd", FLOPPY_35_DD);
	device.option_add("35hd", FLOPPY_35_HD);
}

void lbpc_state::lbpc(machine_config &config)
{
	V40(config, m_maincpu, 14.318181_MHz_XTAL); // 7.16 MHz operating frequency
	m_maincpu->set_addrmap(AS_PROGRAM, &lbpc_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &lbpc_state::io_map);
	m_maincpu->set_tclk(14.318181_MHz_XTAL / 12); // generated by ASIC1
	m_maincpu->tout2_cb().set(FUNC(lbpc_state::tout2_w));
	m_maincpu->out_hreq_cb().set_inputline(m_maincpu, INPUT_LINE_HALT);
	m_maincpu->out_hreq_cb().append(m_maincpu, FUNC(v40_device::hack_w));
	m_maincpu->out_eop_cb().set(FUNC(lbpc_state::eop_w));
	m_maincpu->in_memr_cb().set([this] (offs_t offset) { return m_maincpu->space(AS_PROGRAM).read_byte(offset); });
	m_maincpu->out_memw_cb().set([this] (offs_t offset, u8 data) { m_maincpu->space(AS_PROGRAM).write_byte(offset, data); });
	m_maincpu->out_dack_cb<0>().set(FUNC(lbpc_state::dmaak_w<0>));
	m_maincpu->in_ior_cb<0>().set(FUNC(lbpc_state::exp_dack1_r));
	m_maincpu->out_iow_cb<0>().set(FUNC(lbpc_state::exp_dack1_w));
	m_maincpu->out_dack_cb<1>().set(FUNC(lbpc_state::dmaak_w<1>));
	m_maincpu->in_ior_cb<1>().set(m_fdc, FUNC(wd37c65c_device::dma_r));
	m_maincpu->out_iow_cb<1>().set(m_fdc, FUNC(wd37c65c_device::dma_w));
	m_maincpu->out_dack_cb<2>().set(FUNC(lbpc_state::dmaak_w<2>));
	m_maincpu->in_ior_cb<2>().set(m_scsic, FUNC(ncr53c80_device::dma_r));
	m_maincpu->out_iow_cb<2>().set(m_scsic, FUNC(ncr53c80_device::dma_w));

	PC_KBDC(config, m_kbd, pc_xt_keyboards, STR_KBD_IBM_PC_XT_83);
	m_kbd->out_clock_cb().set(FUNC(lbpc_state::kbd_clock_w));
	m_kbd->out_data_cb().set(FUNC(lbpc_state::kbd_data_w));

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.5);

	ins8250_device &com(INS8250(config, "com", 1.8432_MHz_XTAL)); // NS8250AV
	com.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ4);
	com.out_rts_callback().set("serial", FUNC(rs232_port_device::write_rts)); // J3 pin 4
	com.out_tx_callback().set("serial", FUNC(rs232_port_device::write_txd)); // J3 pin 5
	com.out_dtr_callback().set("serial", FUNC(rs232_port_device::write_dtr)); // J3 pin 7

	wd37c65c_device &fdc(WD37C65C(config, m_fdc, 16_MHz_XTAL, 9.6_MHz_XTAL)); // WD37C65BJM
	// 9.6 MHz XTAL is optional and not supported by the BIOS, but can still be installed
	fdc.intrq_wr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ6);
	fdc.drq_wr_callback().set(m_maincpu, FUNC(v40_device::dreq_w<1>));

	FLOPPY_CONNECTOR(config, "fdc:0", lbpc_floppies, "525sd", floppy_image_device::default_pc_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", lbpc_floppies, nullptr, floppy_image_device::default_pc_floppy_formats);
	SOFTWARE_LIST(config, "disk_list").set_original("ibm5150");

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:1", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr", NCR53C80).machine_config([this] (device_t *device) {
		downcast<ncr53c80_device &>(*device).irq_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ5);
		downcast<ncr53c80_device &>(*device).drq_handler().set(m_maincpu, FUNC(v40_device::dreq_w<2>));
	});

	rs232_port_device &serial(RS232_PORT(config, "serial", default_rs232_devices, nullptr));
	serial.dcd_handler().set("com", FUNC(ins8250_device::dcd_w)); // J3 pin 1
	serial.dsr_handler().set("com", FUNC(ins8250_device::dsr_w)); // J3 pin 2
	serial.rxd_handler().set("com", FUNC(ins8250_device::rx_w)); // J3 pin 3
	serial.cts_handler().set("com", FUNC(ins8250_device::cts_w)); // J3 pin 6
	serial.ri_handler().set("com", FUNC(ins8250_device::ri_w)); // J3 pin 8

	ISA8(config, m_expbus, 14.318181_MHz_XTAL / 2);
	m_expbus->set_memspace(m_maincpu, AS_PROGRAM);
	m_expbus->set_iospace(m_maincpu, AS_IO);
	m_expbus->drq1_callback().set(m_maincpu, FUNC(v40_device::dreq_w<0>));
	m_expbus->irq2_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ2);
	m_expbus->irq3_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ3);
	m_expbus->iochck_callback().set(FUNC(lbpc_state::iochck_w));

	ISA8_SLOT(config, "exp", 0, m_expbus, pc_isa8_cards, "ega", false);
}


ROM_START(lbpc)
	ROM_REGION(0x8000, "bios", 0)
	// "Firmware Version 1.0H  03/08/89"
	ROM_LOAD("lbpc-bio.rom", 0x0000, 0x8000, CRC(47bddf8b) SHA1(8a04fe34502f9f3bfe1e233762bbd5bbdd1c455d))
ROM_END

} // anonymous namespace


COMP(1989, lbpc, 0, 0, lbpc, lbpc, lbpc_state, empty_init, "Ampro Computers", "Little Board/PC", MACHINE_NOT_WORKING)



lc80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder, hap
/***************************************************************************

LC-80 by VEB Mikroelektronik "Karl Marx" Erfurt

When first started, the screen is blank. Wait about 8 seconds for it to
introduce itself, then you may use it or paste to it. The decimal points
indicate which side of the display you will be updating.

Pasting:
    0-F : as is
    +,- : as is
    ADR : ,
    DAT : .
    EX : X

Test Paste:
    ,2000.11+22+33+44+55+66+77+88+99+,2000
    Now press + to confirm the data has been entered.


Hardware notes (2-ROM version from schematics):
- UD880D CPU (Z80 clone) @ D201, ~900kHz no XTAL
- 2KB ROM (2*U505D @ D202/D203)
- 1KB RAM (2*U214D @ D204/D205)
- 2*UD855D (Z80 PIO clone) @ D206/D207
- UD857D (Z80 CTC clone) @ D208
- cassette port
- 6*7seg display, 2 leds, piezo

The PCB is literally inside a leather map that can be closed like a book.
It has a nice calculator style keypad attached to it.

The memory can be expanded. There's an export version with more RAM/ROM by
default and a faster U880D CPU (3300.0kHz XTAL). It included a chess program
called SC-80 that can be started by executing ADR C800. Press ADR again for
NEW GAME, and use the 1-8 number keys for A1-H8. A keypad overlay was included.
The SC-80 chess engine appears to be the same one as in Chess-Master.


TODO:
- KSD11 switch
- CTC clock inputs ("user bus")
- Most characters are lost when pasting, probably global paste speed problem,
  works fine if you overclock the cpu.
- Add internal artwork for the clickable keypad, and an overlay for SC-80?

****************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/z80daisy.h"
#include "imagedev/cassette.h"
#include "machine/ram.h"
#include "machine/z80pio.h"
#include "machine/z80ctc.h"
#include "sound/spkrdev.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "lc80.lh"


namespace {

class lc80_state : public driver_device
{
public:
	lc80_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ram(*this, "ram"),
		m_pio(*this, "pio%u", 0),
		m_ctc(*this, "ctc"),
		m_display(*this, "display"),
		m_cassette(*this, "cassette"),
		m_speaker(*this, "speaker"),
		m_inputs(*this, "IN.%u", 0U),
		m_halt_led(*this, "halt")
	{ }

	void lc80(machine_config &config);
	void lc80a(machine_config &config);
	void lc80e(machine_config &config);
	void lc80_2(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(trigger_reset);
	DECLARE_INPUT_CHANGED_MEMBER(trigger_nmi);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device_array<z80pio_device, 2> m_pio;
	required_device<z80ctc_device> m_ctc;
	required_device<pwm_display_device> m_display;
	required_device<cassette_image_device> m_cassette;
	required_device<speaker_sound_device> m_speaker;
	required_ioport_array<6> m_inputs;
	output_finder<> m_halt_led;

	u8 m_matrix = 0;

	void lc80_mem(address_map &map) ATTR_COLD;
	void lc80a_mem(address_map &map) ATTR_COLD;
	void lc80e_mem(address_map &map) ATTR_COLD;
	void lc80_2_mem(address_map &map) ATTR_COLD;
	void lc80_io(address_map &map) ATTR_COLD;

	void halt_w(int state) { m_halt_led = state; }
	void ctc_z0_w(int state);
	void ctc_z1_w(int state);
	void ctc_z2_w(int state);
	void pio1_pa_w(u8 data);
	u8 pio1_pb_r();
	void pio1_pb_w(u8 data);
	u8 pio2_pb_r();
};


// Memory Maps

void lc80_state::lc80_mem(address_map &map)
{
	map.global_mask(0x3fff);
	map(0x0000, 0x03ff).mirror(0x0400).rom();
	map(0x0800, 0x0bff).mirror(0x0400).rom();
}

void lc80_state::lc80a_mem(address_map &map)
{
	map.global_mask(0x3fff);
	map(0x0000, 0x07ff).rom();
}

void lc80_state::lc80_2_mem(address_map &map)
{
	map.global_mask(0x3fff);
	map(0x0000, 0x0fff).rom();
}

void lc80_state::lc80e_mem(address_map &map)
{
	map.global_mask(0x7fff);
	map(0x0000, 0x1fff).rom();
	map(0x4000, 0x4fff).rom();
}

void lc80_state::lc80_io(address_map &map)
{
	map.global_mask(0x1f);
	map(0x14, 0x17).rw(m_pio[0], FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x18, 0x1b).rw(m_pio[1], FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x0c, 0x0f).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
}


// Input Ports

INPUT_CHANGED_MEMBER(lc80_state::trigger_reset)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}

INPUT_CHANGED_MEMBER(lc80_state::trigger_nmi)
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, newval ? ASSERT_LINE : CLEAR_LINE);
}

static INPUT_PORTS_START( lc80 )
	PORT_START("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("LD") PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("ST") PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("EX") PORT_CODE(KEYCODE_X) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR('X')

	PORT_START("IN.1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0')

	PORT_START("IN.2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR('+')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4')

	PORT_START("IN.3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8')

	PORT_START("IN.4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')

	PORT_START("IN.5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR('-')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DAT") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("ADR") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',')

	PORT_START("SPECIAL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RES") PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(lc80_state::trigger_reset), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("NMI") PORT_CODE(KEYCODE_F2) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(lc80_state::trigger_nmi), 0)
INPUT_PORTS_END


// Z80-CTC Interface

void lc80_state::ctc_z0_w(int state)
{
}

void lc80_state::ctc_z1_w(int state)
{
}

void lc80_state::ctc_z2_w(int state)
{
}


// Z80-PIO Interface

void lc80_state::pio1_pa_w(u8 data)
{
	/*

	    bit     description

	    PA0     VQE23 segment B
	    PA1     VQE23 segment F
	    PA2     VQE23 segment A
	    PA3     VQE23 segment G
	    PA4     VQE23 segment DP
	    PA5     VQE23 segment C
	    PA6     VQE23 segment E
	    PA7     VQE23 segment D

	*/

	m_display->write_mx(bitswap<8>(~data, 4, 3, 1, 6, 7, 5, 0, 2));
}

u8 lc80_state::pio1_pb_r()
{
	// PB0: tape input
	return (m_cassette->input() < +0.0);
}

void lc80_state::pio1_pb_w(u8 data)
{
	/*

	    bit     description

	    PB0     tape input (pio1_pb_r)
	    PB1     tape output, speaker output, OUT led
	    PB2     digit 0
	    PB3     digit 1
	    PB4     digit 2
	    PB5     digit 3
	    PB6     digit 4
	    PB7     digit 5

	*/

	// tape output
	m_cassette->output(BIT(data, 1) ? +1.0 : -1.0);

	// speaker
	m_speaker->level_w(BIT(~data, 1));

	// 7segs/led/keyboard
	m_display->write_my(~data >> 1);
	m_matrix = ~data >> 2 & 0x3f;
}

u8 lc80_state::pio2_pb_r()
{
	/*

	    bit     description

	    PB0
	    PB1
	    PB2
	    PB3
	    PB4     key row 0
	    PB5     key row 1
	    PB6     key row 2
	    PB7     key row 3

	*/

	u8 data = 0;

	for (int i = 0; i < 6; i++)
		if (BIT(m_matrix, i))
			data |= m_inputs[i]->read() << 4;

	return data ^ 0xf0;
}


// Z80 Daisy Chain

static const z80_daisy_config lc80_daisy_chain[] =
{
	{ "ctc" },
	{ "pio1" },
	{ "pio0" },
	{ nullptr }
};


// Machine Initialization

void lc80_state::machine_start()
{
	m_halt_led.resolve();

	// install RAM
	address_space &program = m_maincpu->space(AS_PROGRAM);
	const offs_t mirror = (program.map()->m_globalmask & 0xc000) | 0x1000;
	const offs_t start = 0x2000;
	program.install_ram(start, start + m_ram->size() - 1, mirror, m_ram->pointer());

	// register for state saving
	save_item(NAME(m_matrix));
}


// Machine Driver

void lc80_state::lc80(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 900000); // UD880D
	m_maincpu->set_addrmap(AS_PROGRAM, &lc80_state::lc80_mem);
	m_maincpu->set_addrmap(AS_IO, &lc80_state::lc80_io);
	m_maincpu->halt_cb().set(FUNC(lc80_state::halt_w));
	m_maincpu->set_daisy_config(lc80_daisy_chain);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1+6, 8);
	m_display->set_segmask(0x3f << 1, 0xff);
	config.set_default_layout(layout_lc80);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	// devices
	Z80CTC(config, m_ctc, 900000);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->zc_callback<0>().set(FUNC(lc80_state::ctc_z0_w));
	m_ctc->zc_callback<1>().set(FUNC(lc80_state::ctc_z1_w));
	m_ctc->zc_callback<2>().set(FUNC(lc80_state::ctc_z2_w));

	Z80PIO(config, m_pio[0], 900000);
	m_pio[0]->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio[0]->out_pa_callback().set(FUNC(lc80_state::pio1_pa_w));
	m_pio[0]->in_pb_callback().set(FUNC(lc80_state::pio1_pb_r));
	m_pio[0]->out_pb_callback().set(FUNC(lc80_state::pio1_pb_w));

	Z80PIO(config, m_pio[1], 900000);
	m_pio[1]->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio[1]->in_pb_callback().set(FUNC(lc80_state::pio2_pb_r));

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);

	RAM(config, m_ram).set_extra_options("1K,2K,3K,4K");
	m_ram->set_default_size("1K");
}

void lc80_state::lc80a(machine_config &config)
{
	lc80(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &lc80_state::lc80a_mem);
}

void lc80_state::lc80_2(machine_config &config)
{
	lc80(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &lc80_state::lc80_2_mem);
	m_ram->set_default_size("4K");
}

void lc80_state::lc80e(machine_config &config)
{
	lc80(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &lc80_state::lc80e_mem);
	m_ram->set_default_size("4K");

	// it is running almost twice as fast
	const XTAL clk = 3.3_MHz_XTAL / 2;
	m_maincpu->set_clock(clk);
	m_ctc->set_clock(clk);
	m_pio[0]->set_clock(clk);
	m_pio[1]->set_clock(clk);
}


// ROMs

ROM_START( lc80 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "bm075.d202", 0x0000, 0x0400, CRC(e754ef53) SHA1(044440b13e62addbc3f6a77369cfd16f99b39752) ) // U505
	ROM_LOAD( "bm076.d203", 0x0800, 0x0400, CRC(2b544da1) SHA1(3a6cbd0c57c38eadb7055dca4b396c348567d1d5) ) // "
ROM_END

ROM_START( lc80a )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "lc80_2716.bin", 0x0000, 0x0800, CRC(b3025934) SHA1(6fff953f0f1eee829fd774366313ab7e8053468c) ) // 2716
ROM_END

ROM_START( lc80_2 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "lc80_2.bin", 0x0000, 0x1000, CRC(2e06d768) SHA1(d9cddaf847831e4ab21854c0f895348b7fda20b8) )
ROM_END

ROM_START( lc80e )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "lc80e-0000-schach.rom", 0x0000, 0x1000, CRC(e3cca61d) SHA1(f2be3f2a9d3780d59657e49b3abeffb0fc13db89) )
	ROM_LOAD( "lc80e-1000-schach.rom", 0x1000, 0x1000, CRC(b0323160) SHA1(0ea019b0944736ae5b842bf9aa3537300f259b98) )
	ROM_LOAD( "lc80e-c000-schach.rom", 0x4000, 0x1000, CRC(9c858d9c) SHA1(2f7b3fd046c965185606253f6cd9372da289ca6f) )
ROM_END

} // anonymous namespace


// System Drivers

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY, FULLNAME, FLAGS
COMP( 1984, lc80,   0,      0,      lc80,    lc80,  lc80_state, empty_init, "VEB Mikroelektronik \"Karl Marx\" Erfurt", "Lerncomputer LC 80 (set 1)", MACHINE_SUPPORTS_SAVE )
COMP( 1984, lc80a,  lc80,   0,      lc80a,   lc80,  lc80_state, empty_init, "VEB Mikroelektronik \"Karl Marx\" Erfurt", "Lerncomputer LC 80 (set 2)", MACHINE_SUPPORTS_SAVE )
COMP( 1984, lc80e,  lc80,   0,      lc80e,   lc80,  lc80_state, empty_init, "VEB Mikroelektronik \"Karl Marx\" Erfurt", "Lerncomputer LC 80 (export)", MACHINE_SUPPORTS_SAVE )
COMP( 1991, lc80_2, lc80,   0,      lc80_2,  lc80,  lc80_state, empty_init, "hack (Eckart Buschendorf)", "Lerncomputer LC 80.2", MACHINE_SUPPORTS_SAVE )



lcmate2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco, Robbbert
/***************************************************************************

        Laser Compumate2

        17/04/2011 Initial driver by Robbbert, using info supplied by
        DMEnduro. He owns one of these machines.
        21/04/2011 Hooked up the lcd controller and added keyboard
        input. [Sandro Ronco]
        22/04/2011 Small adjustments resulting from team investigations.

        Chips: Z80 CPU, 8K of RAM, 32K ROM, 'VTCL', '8826KX',
               RP5C15 RTC.

        Display: 2 lines of 20 characters LCD.

        There are no devices for file saving or loading, is more of
        a PDA format with all programs (address book, etc) in the ROM.

        Sound: a piezo speaker.

        ToDo:
        - RTC doesn't remember the time
        - Alarm doesn't work
        - Reset/On button to be added
        - The ROM has INT and NMI code; investigate if something uses it.


****************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/nvram.h"
#include "machine/rp5c15.h"
#include "sound/spkrdev.h"
#include "video/hd44780.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "utf8.h"


namespace {

class lcmate2_state : public driver_device
{
public:
	lcmate2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "hd44780")
		, m_rtc(*this, "rtc")
		, m_speaker(*this, "speaker")
		, m_kbdlines(*this, "LINE%u", 0U)
	{ }

	void lcmate2(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<hd44780_device> m_lcdc;
	required_device<rp5c15_device> m_rtc;
	required_device<speaker_sound_device> m_speaker;
	required_ioport_array<8> m_kbdlines;

	u8 key_r(offs_t offset);
	void speaker_w(u8 data);
	void bankswitch_w(u8 data);
	void lcmate2_palette(palette_device &palette) const;
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
};

void lcmate2_state::speaker_w(u8 data)
{
	m_speaker->level_w(BIT(data, 6));
}

// offsets are FE,FD,FB,F7,EF,DF,BF,7F to scan a particular row, or 00 to check if any key pressed
u8 lcmate2_state::key_r(offs_t offset)
{
	u8 data = 0xff;

	for (int i = 0; i < 8; i++)
	{
		if (!BIT(offset, i))
			data &= m_kbdlines[i]->read();
	}

	return data;
}

void lcmate2_state::bankswitch_w(u8 data)
{
	membank("rombank")->set_entry(data&0x0f);
}

void lcmate2_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).rom();
	map(0x4000, 0x7fff).bankr("rombank");
	map(0x8000, 0x9fff).ram().mirror(0x6000).share("nvram");
}

void lcmate2_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x000f).rw(m_rtc, FUNC(rp5c15_device::read), FUNC(rp5c15_device::write));
	map(0x1000, 0x1000).w(FUNC(lcmate2_state::speaker_w));
	map(0x1fff, 0x1fff).w(FUNC(lcmate2_state::bankswitch_w));

	map(0x3000, 0x3001).w(m_lcdc, FUNC(hd44780_device::write));
	map(0x3002, 0x3003).r(m_lcdc, FUNC(hd44780_device::read));

	map(0x5000, 0x50ff).r(FUNC(lcmate2_state::key_r));
}

/* Input ports */
// Alternate keyboard layout exists. Different keys are 7&  `~  ?  +/  ,<  .>  ON/RESET, with removal of Numlock and [{ ]}
// Machine starts up with capslock on - turn it off to access lowercase. Natural keyboard - use End key.
static INPUT_PORTS_START( lcmate2 )
	PORT_START("LINE0")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_UP)  PORT_CODE(KEYCODE_UP)           PORT_CHAR(UCHAR_MAMEKEY(UP))
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER")  PORT_CODE(KEYCODE_ENTER)        PORT_CHAR(13)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ESC")    PORT_CODE(KEYCODE_ESC)          PORT_CHAR(UCHAR_MAMEKEY(ESC),27)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT")  PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL")   PORT_CODE(KEYCODE_LCONTROL)     PORT_CHAR(UCHAR_SHIFT_2)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q")      PORT_CODE(KEYCODE_Q)            PORT_CHAR('q')  PORT_CHAR('Q')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1")      PORT_CODE(KEYCODE_1)            PORT_CHAR('1')  PORT_CHAR('!')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE1")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",")      PORT_CODE(KEYCODE_COMMA)        PORT_CHAR(',')  PORT_CHAR('/')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K")      PORT_CODE(KEYCODE_K)            PORT_CHAR('k')  PORT_CHAR('K')
		// Natural keyboard: UCHAR_MAMEKEY(CAPSLOCK) does not disengage capslock on the emulated machine, so using END instead
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CAPSLOCK")   PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(END))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z")      PORT_CODE(KEYCODE_Z)            PORT_CHAR('z')  PORT_CHAR('Z')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A")      PORT_CODE(KEYCODE_A)            PORT_CHAR('a')  PORT_CHAR('A')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W")      PORT_CODE(KEYCODE_W)            PORT_CHAR('w')  PORT_CHAR('W')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2")      PORT_CODE(KEYCODE_2)            PORT_CHAR('2')  PORT_CHAR('@')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE2")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".")      PORT_CODE(KEYCODE_STOP)         PORT_CHAR('.')  PORT_CHAR('?')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O")      PORT_CODE(KEYCODE_O)            PORT_CHAR('o')  PORT_CHAR('O')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[")      PORT_CODE(KEYCODE_OPENBRACE)    PORT_CHAR('[')  PORT_CHAR('{')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X")      PORT_CODE(KEYCODE_X)            PORT_CHAR('x')  PORT_CHAR('X')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S")      PORT_CODE(KEYCODE_S)            PORT_CHAR('s')  PORT_CHAR('S')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E")      PORT_CODE(KEYCODE_E)            PORT_CHAR('e')  PORT_CHAR('E')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3")      PORT_CODE(KEYCODE_3)            PORT_CHAR('3')  PORT_CHAR('#')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE3")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";")      PORT_CODE(KEYCODE_COLON)        PORT_CHAR(';')  PORT_CHAR(':')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-")      PORT_CODE(KEYCODE_MINUS)        PORT_CHAR('-')  PORT_CHAR('=')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]")      PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_CHAR(']')  PORT_CHAR('}')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C")      PORT_CODE(KEYCODE_C)            PORT_CHAR('c')  PORT_CHAR('C')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D")      PORT_CODE(KEYCODE_D)            PORT_CHAR('d')  PORT_CHAR('D')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R")      PORT_CODE(KEYCODE_R)            PORT_CHAR('r')  PORT_CHAR('R')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4")      PORT_CODE(KEYCODE_4)            PORT_CHAR('4')  PORT_CHAR('$')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE4")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("'")      PORT_CODE(KEYCODE_QUOTE)        PORT_CHAR('\'') PORT_CHAR('\"')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DEL")    PORT_CODE(KEYCODE_BACKSPACE)    PORT_CHAR(UCHAR_MAMEKEY(DEL),8)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE")  PORT_CODE(KEYCODE_SPACE)        PORT_CHAR(' ')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V")      PORT_CODE(KEYCODE_V)            PORT_CHAR('v')  PORT_CHAR('V')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F")      PORT_CODE(KEYCODE_F)            PORT_CHAR('f')  PORT_CHAR('F')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T")      PORT_CODE(KEYCODE_T)            PORT_CHAR('t')  PORT_CHAR('T')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5")      PORT_CODE(KEYCODE_5)            PORT_CHAR('5')  PORT_CHAR('%')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE5")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L")      PORT_CODE(KEYCODE_L)            PORT_CHAR('l')  PORT_CHAR('L')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P")      PORT_CODE(KEYCODE_P)            PORT_CHAR('p')  PORT_CHAR('P')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT)   PORT_CODE(KEYCODE_LEFT)      PORT_CHAR(UCHAR_MAMEKEY(LEFT))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B")      PORT_CODE(KEYCODE_B)            PORT_CHAR('b')  PORT_CHAR('B')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G")      PORT_CODE(KEYCODE_G)            PORT_CHAR('g')  PORT_CHAR('G')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y")      PORT_CODE(KEYCODE_Y)            PORT_CHAR('y')  PORT_CHAR('Y')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6")      PORT_CODE(KEYCODE_6)            PORT_CHAR('6')  PORT_CHAR('^')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE6")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0")      PORT_CODE(KEYCODE_0)            PORT_CHAR('0')  PORT_CHAR(')')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT)  PORT_CODE(KEYCODE_RIGHT)     PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N")      PORT_CODE(KEYCODE_N)            PORT_CHAR('n')  PORT_CHAR('N')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H")      PORT_CODE(KEYCODE_H)            PORT_CHAR('h')  PORT_CHAR('H')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U")      PORT_CODE(KEYCODE_U)            PORT_CHAR('u')  PORT_CHAR('U')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7")      PORT_CODE(KEYCODE_7)            PORT_CHAR('7')  PORT_CHAR('+')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE7")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NUMLOck") PORT_CODE(KEYCODE_NUMLOCK)     PORT_CHAR(UCHAR_MAMEKEY(NUMLOCK))
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9")      PORT_CODE(KEYCODE_9)            PORT_CHAR('9')  PORT_CHAR('(')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN)   PORT_CODE(KEYCODE_DOWN)      PORT_CHAR(UCHAR_MAMEKEY(DOWN))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M")      PORT_CODE(KEYCODE_M)            PORT_CHAR('m')  PORT_CHAR('M')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J")      PORT_CODE(KEYCODE_J)            PORT_CHAR('j')  PORT_CHAR('J')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I")      PORT_CODE(KEYCODE_I)            PORT_CHAR('i')  PORT_CHAR('I')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8")      PORT_CODE(KEYCODE_8)            PORT_CHAR('8')  PORT_CHAR('*')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

void lcmate2_state::lcmate2_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}

void lcmate2_state::machine_start()
{
	membank("rombank")->configure_entries(0, 0x10, (u8*)memregion("maincpu")->base(), 0x4000);
}

static const gfx_layout charlayout =
{
	5, 8,   /* 5 x 8 characters */
	256,    /* 256 characters */
	1,  /* 1 bits per pixel */
	{ 0 },  /* no bitplanes */
	{ 3, 4, 5, 6, 7},
	{ 0, 8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8},
	8*8 /* 8 bytes */
};

static GFXDECODE_START( gfx_lcmate2 )
	GFXDECODE_ENTRY( "hd44780:cgrom", 0x0000, charlayout, 0, 1 )
GFXDECODE_END


void lcmate2_state::lcmate2(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(3'579'545)); // confirmed
	m_maincpu->set_addrmap(AS_PROGRAM, &lcmate2_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &lcmate2_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("hd44780", FUNC(hd44780_device::screen_update));
	screen.set_size(120, 18);
	screen.set_visarea(0, 120-1, 0, 18-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(lcmate2_state::lcmate2_palette), 2);
	GFXDECODE(config, "gfxdecode", "palette", gfx_lcmate2);

	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 20);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* Devices */
	RP5C15(config, m_rtc, XTAL(32'768));
}

/* ROM definition */
ROM_START( lcmate2 )
	ROM_REGION( 0x40000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "u2.bin",  0x00000, 0x08000, CRC(521931b9) SHA1(743a6e2928c4365fbf5ed9a173e2c1bfe695850f) )
	ROM_LOAD( "u3.bin",  0x20000, 0x20000, CRC(84fe767a) SHA1(8dd306f203e1220f0eab1a284be3095e2642c5b6) ) // spell library
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY  FULLNAME             FLAGS
COMP( 1984, lcmate2, 0,      0,      lcmate2, lcmate2, lcmate2_state, empty_init, "VTech", "Laser Compumate 2", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



ld50.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Devin Acker
/*
    Casio LD-50 electronic drums

    Unlike most Casio instruments, this is either rebranded or outsourced.
    It's unclear who developed it, or if it was ever sold under other brands,
    but the data ROM mentions "SharpWin".

    Main board:
        bottom left:  "DIGITAL DRUM [LD-50] DATE: 2001/07/20"
        bottom right: "LD50 Main PCB Rev.1"

        IC1:   Burr-Brown PCM1717E DAC
        IC2:   Mitsubishi M62429FP volume control
        IC4:   Philips P87C52UBAA MCU
        IC5:   AMD AM29F040 ROM
        IC7/8/9/10: 4x 74HC374
        IC11:  Jess Technology HE80085 voice MCU (COB, letter "B" handwritten on epoxy)
               "256KB ROM 128B RAM, 24I/O, EXT, D/A, OP, PWM, 2X16BIT TIMER, WDT" (PIC clone or similar?)
               https://web.archive.org/web/20010824054714/http://www.jesstech.com/partslist.phtml?cat1=Microcontroller&cat2=8-Bits+MCU+Series&cat3=HE80+Series
        IC12:  Dream SAM9793 MIDI synth
        XTAL1: 9.6MHz (for SAM9793)
        XTAL2: 12MHz (for 87C52)

    A 10-pin header at the upper left connects to the LCD.

    Most of the sound is provided by the SAM9793, which is just a MIDI
    synth on a chip that takes in serial MIDI input and outputs I2S.
    The four sound effect pads also trigger PCM samples separately,
    via the black blob at IC11.

    The external ROM contains the demo and rhythms, which are all stored
    as standard type 0 MIDI files.

    To activate the test mode, hold "Rhythm" and "Assign" when powering on.
    From here, hitting the drum pads will display time/velocity measurements.

    Service manual and schematics:
    https://archive.org/details/casio-ld-50-sm

    TODO:
    - LCD artwork
    - clickable layout?
    - possibly connect a MIDI out port in lieu of the SAM9793
      (MCS51 core needs proper serial output first)
    - dump/emulate the HE80085 somehow
 */

#include "emu.h"

#include "bus/midi/midioutport.h"
#include "cpu/mcs51/mcs51.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"

namespace {

class ld50_state : public driver_device
{
public:
	ld50_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "lcdc")
		, m_datarom(*this, "datarom")
		, m_inputs(*this, "IN%u", 0U)
		, m_pads(*this, "PADS")
		, m_dial(*this, "DIAL")
		, m_led(*this, "led%d", 0U)
	{
	}

	void ld50(machine_config &config);

	ioport_value dial_r();

private:
	u8 port0_r();
	void port0_w(u8 data);
	u8 port1_r();
	void port2_w(u8 data);
	void port3_w(u8 data);

	virtual void driver_start() override;
	virtual void driver_reset() override;

	HD44780_PIXEL_UPDATE(lcd_update);
	void palette_init(palette_device &palette);

	required_device<mcs51_cpu_device> m_maincpu;
	required_device<hd44780_device> m_lcdc;

	required_memory_region m_datarom;
	required_ioport_array<5> m_inputs;
	required_ioport m_pads;
	required_ioport m_dial;

	output_finder<4> m_led;

	u8 m_port[4];
	u32 m_rom_addr;
	u16 m_sound_data;
	u16 m_volume_data;
};

HD44780_PIXEL_UPDATE(ld50_state::lcd_update)
{
	if (x < 6 && y < 8 && line < 2 && pos < 8)
		bitmap.pix(y, line * 48 + pos * 6 + x) = state;
}

void ld50_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(255, 255, 255));
	palette.set_pen_color(1, rgb_t(0, 0, 0));
}


u8 ld50_state::port0_r()
{
	u8 data = 0xf0 | (m_lcdc->db_r() >> 4);
	if (!BIT(m_port[2], 3))
		data &= m_datarom->base()[m_rom_addr & 0x7ffff];

	return data;
}

void ld50_state::port0_w(u8 data)
{
	m_lcdc->db_w(data << 4);
	m_port[0] = data;
}

u8 ld50_state::port1_r()
{
	/*
	bits 0-3: drum pads (active high)
	bits 4-7: multiplexed inputs (active low)

	The drum pads are read during the timer 0 interrupt.
	Note velocity is based on the number of timer intervals that the pad remains pressed.
	*/
	return (m_port[1] & 0xf0) | m_pads->read();
}

void ld50_state::port2_w(u8 data)
{
	/*
	bit 0: reset output
	bit 1: unused
	bit 2: auto power off
	bit 3: ROM output enable
	bit 4: ROM address low byte latch
	bit 5: ROM address mid byte latch
	bit 6: ROM address hi byte & input latch
	bit 7: LED and LCD control latch
	*/

	const u8 set = data & ~m_port[2];
	m_port[2] = data;

	if (BIT(set, 4))
	{
		m_rom_addr = (m_rom_addr & 0x7ff00) | m_port[0];
	}
	if (BIT(set, 5))
	{
		m_rom_addr = (m_rom_addr & 0x700ff) | (m_port[0] << 8);
	}
	if (BIT(set, 6))
	{
		m_rom_addr = (m_rom_addr & 0x0ffff) | ((m_port[0] & 0xe0) << 11);

		m_port[1] |= 0xf0;
		for (int i = 0; i < 5; i++)
		{
			if (!BIT(m_port[0], i))
				m_port[1] &= (m_inputs[i]->read() << 4);
		}
	}
	if (BIT(set, 7))
	{
		for (unsigned i = 0; i < 4; i++)
			m_led[i] = !BIT(m_port[0], i);

		m_lcdc->rw_w(BIT(m_port[0], 6));
		m_lcdc->rs_w(BIT(m_port[0], 7));
	}
}

void ld50_state::port3_w(u8 data)
{
	/*
	bit 0: LCD enable
	bit 1: MIDI Tx (TODO)
	bit 2: unused
	bit 3: trigger sound effect
	bit 4: shift register clock (for volume)
	bit 5: shift register data
	bit 6: shift register clock (for DAC and sound effects)
	bit 7: DAC data latch

	The LD-50 transmits MIDI both through the UART registers and manually via port 3.
	Demo/rhythm playback uses the UART, while drum pads, effect values, etc. are
	bit-banged through the port instead (why?)
	*/

	const u8 set = data & ~m_port[3];
	const u8 clr = ~data & m_port[3];
	m_port[3] = data;

	m_lcdc->e_w(BIT(data, 0));

	if (BIT(set, 3))
	{
		logerror("play sound effect %u\n", m_sound_data & 0x1f);
	}
	if (BIT(set, 4))
	{
		// 11-bit data, least significant bit first
		m_volume_data >>= 1;
		m_volume_data |= (BIT(data, 5) << 15);
	}
	if (BIT(clr, 4) && BIT(data, 5))
	{
		logerror("volume cmd (data = 0x%03x)\n", m_volume_data >> 5);
	}
	if (BIT(set, 6))
	{
		// 16-bit data, most significant bit first
		m_sound_data <<= 1;
		m_sound_data |= BIT(data, 5);
	}
	if (BIT(clr, 7))
	{
		logerror("DAC cmd (data = 0x%04x)\n", m_sound_data);
	}
}

ioport_value ld50_state::dial_r()
{
	// return the dial position as a 2-bit gray code
	const u8 val = m_dial->read();
	return (val >> 6) ^ (val >> 7);
}


void ld50_state::driver_start()
{
	m_led.resolve();

	save_item(NAME(m_port));
	save_item(NAME(m_rom_addr));
	save_item(NAME(m_sound_data));
	save_item(NAME(m_volume_data));
}

void ld50_state::driver_reset()
{
	memset(m_port, 0xff, sizeof m_port);

	m_rom_addr = 0;
	m_sound_data = 0;
	m_volume_data = 0;
}

void ld50_state::ld50(machine_config &config)
{
	// CPU
	I87C52(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->port_in_cb<0>().set(FUNC(ld50_state::port0_r));
	m_maincpu->port_out_cb<0>().set(FUNC(ld50_state::port0_w));
	m_maincpu->port_in_cb<1>().set(FUNC(ld50_state::port1_r));
	m_maincpu->port_out_cb<2>().set(FUNC(ld50_state::port2_w));
	m_maincpu->port_out_cb<3>().set(FUNC(ld50_state::port3_w));

	// LCD
	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 8);
	m_lcdc->set_pixel_update_cb(FUNC(ld50_state::lcd_update));

	// screen (for testing only)
	// TODO: the actual LCD with custom segments
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6 * 16, 8);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(ld50_state::palette_init), 2);
}

INPUT_PORTS_START(ld50)
	PORT_START("PADS")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Drum Pad 4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Drum Pad 3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Drum Pad 2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Drum Pad 1")

	PORT_START("DIAL")
	PORT_BIT( 0xff, 0x00, IPT_DIAL ) PORT_SENSITIVITY(50) PORT_KEYDELTA(50) PORT_REVERSE

	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON8 ) PORT_NAME("SE Pad 4")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON7 ) PORT_NAME("SE Pad 3")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("SE Pad 2")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("SE Pad 1")

	PORT_START("IN1")
	PORT_BIT( 0x03, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(ld50_state::dial_r))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Effect")

	PORT_START("IN2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_EQUALS)    PORT_NAME("Assign")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS)     PORT_NAME("Drum Set")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_0)         PORT_NAME("SE Set")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_9)         PORT_NAME("Rhythm")

	PORT_START("IN3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8)         PORT_NAME("Play Level")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7)         PORT_NAME("Light")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6)         PORT_NAME("Tempo")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5)         PORT_NAME("Start/Stop")

	PORT_START("IN4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4)         PORT_NAME("Synchro Start")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3)         PORT_NAME("Demo")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2)         PORT_NAME("Rhythm Vol.")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1)         PORT_NAME("Main Vol.")
INPUT_PORTS_END

ROM_START(ld50)
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("87c52.ic4", 0x0000, 0x2000, CRC(126108cc) SHA1(cb8d7359628f8e519862cec73e38078275c3bd48))

	ROM_REGION(0x80000, "datarom", 0)
	ROM_LOAD("ld50.ic5", 0x00000, 0x80000, CRC(acaee847) SHA1(7235aefd72260b6d9d1f652c643022515a880781))
ROM_END

} // anonymous namespace

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY  FULLNAME     FLAGS
SYST( 2002, ld50,    0,      0,      ld50,    ld50,    ld50_state,    empty_init, "Casio", "LD-50",     MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



leadsinger2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/******************************************************************************

-------- Label on product --------

Enter Tech
Music Video Karaoke
MODEL: LS-K2

-------- Boot Screen --------

MVK
Music Video / MP3 Karaoke
Leadsinger II
www.leadsinger.com

-------- Main SoC --------

SUNPLUS
SPHE8104AW
0729-H
0001697

-------- ROM --------

EXCELSEMI
ES29LV800EB-70TG
0720

-------- RAM --------

SAMSUNG 710
K4S641632K-U75

-------- Other --------

27Mhz Xtal
Card Slot

*******************************************************************************/

#include "emu.h"

#include "cpu/mips/mips3.h"

#include "screen.h"
#include "speaker.h"

namespace {

class leadsng2_state : public driver_device
{
public:
	leadsng2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
	{ }

	void leadsng2(machine_config &config) ATTR_COLD;

	void init_leadsng2();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void mem_map(address_map &map);

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};

uint32_t leadsng2_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void leadsng2_state::machine_start()
{

}

void leadsng2_state::machine_reset()
{
}

void leadsng2_state::mem_map(address_map &map)
{
	map(0x18000000, 0x180fffff).mirror(0x07c00000).rom().region("maincpu", 0);
}

static INPUT_PORTS_START( leadsng2 )
INPUT_PORTS_END

void leadsng2_state::leadsng2(machine_config &config)
{
	// unknown CPU core (MIPS-based, little endian?)
	R4400LE(config, m_maincpu, 27_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &leadsng2_state::mem_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320, 262);
	m_screen->set_visarea(0, 320-1, 0, 240-1);
	m_screen->set_screen_update(FUNC(leadsng2_state::screen_update));

	SPEAKER(config, "speaker", 2).front();
}

ROM_START( leadsng2 )
	ROM_REGION( 0x100000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "ls-k2_es29lv800eb_004a225b.bin", 0x000000, 0x100000, CRC(e70f1e1f) SHA1(5aa3187adffcba5bd4a9e3e89a1c210d7f1a978e) )
ROM_END

void leadsng2_state::init_leadsng2()
{
	memory_region *rgn = memregion("maincpu");
	for (offs_t offset = 0; offset < rgn->bytes(); offset++)
		rgn->as_u8(offset) ^= (offset & 0x54) ^ 0xa5;
}

} // anonymous namespace

CONS( 200?, leadsng2,       0,              0,      leadsng2, leadsng2, leadsng2_state, init_leadsng2, "Enter Tech", "Leadsinger II (LS-K2)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



leappad.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/******************************************************************************

    LEAPPAD:
    Example-Video: https://www.youtube.com/watch?v=LtUhENu5TKc
    The LEAPPAD is basically comparable to the SEGA PICO, but without
    Screen-Output! Each "Game" consists of two parts (Book + Cartridge).
    Insert the cartridge into the system and add the Book on the Top of the
    "console" and you can click on each pages and hear sounds or
    learning-stuff on each page...
    Note: The Cocopad shares the same BIOS as the Leappad (CRC32 c886cddc)

    MY FIRST LEAPPAD:
    Basically the same as the LEAPPAD, but for even younger kids! (Cartridge
    internal PCB's are identical to LEAPPAD).
    Example Video: https://www.youtube.com/watch?v=gsf8XYV1Tpg

    LITTLE TOUCH LEAPPAD:
    Same as the other LEAPPAD models, but aimed at babies.

    Don't get confused by the name "LEAPPAD", as it looks like Leapfrog
    also released some kind of Tablet with this name, and they even released
    a new "LEAPPAD" in around 2016:
    https://www.youtube.com/watch?v=MXFSgj6xLTU , which nearly looks like the
    same, but is most likely technically completely different...

    The cartridges pinout is the same on the three systems:
       A1  N/C (A21?)
       A2  A20
       A3  A19
       A4  A8
       A5  A9
       A6  A6
       A7  A5
       A8  A4
       A9  A3
      A10  A2
      A11  A1
      A12  A0
      A13  N/C (R/W? /CE2?)
      A14  /CE
      A15  /OE
      A16  D0
      A17  D1
      A18  D2
      A19  D3
      A20  VCC
      B1   N/C (A22?)
      B2   N/C (A23?)
      B3   A18
      B4   A17
      B5   A7
      B6   GND
      B7   A10
      B8   A11
      B9   A12
      B10  A13
      B11  A14
      B12  A15
      B13  A16
      B14  GND
      B15  A-1
      B16  D7
      B17  D6
      B18  D5
      B19  D4
      B20  GND

    Cocopad BIOS pinout:
         +-----------+
     A23-|           |- GND
     A21-|           |- A22
     A18-|           |- A20
     A17-|           |- A19
     A07-|           |- A08
     A06-|           |- A09
     A05-|           |- A10
     A04-|           |- A11
     A03-|           |- A12
     A02-|           |- A13
     A01-|           |- A14
     A00-|           |- A15
      CE-|           |- A16
     GND-|           |- CE
      OE-|           |- A-1
     D00-|           |- D07
     D01-|           |- D06
     D02-|           |- D05
     D03-|           |- D04
     VCC-|           |- GND
         +-----------+
*******************************************************************************/

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/mcs51/mcs51.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class leapfrog_leappad_state : public driver_device
{
public:
	leapfrog_leappad_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cart(*this, "cartslot")
		, m_cart_region(nullptr)
	{ }

	void leapfrog_leappad(machine_config &config);
	void leapfrog_mfleappad(machine_config &config);
	void leapfrog_ltleappad(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void prog_map(address_map &map) ATTR_COLD;
	void ext_map(address_map &map) ATTR_COLD;

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	required_device<mcs51_cpu_device> m_maincpu;
	required_device<generic_slot_device> m_cart;
	memory_region *m_cart_region;
};



void leapfrog_leappad_state::machine_start()
{
	// if there's a cart, override the standard mapping
	if (m_cart && m_cart->exists())
	{
		m_cart_region = memregion(std::string(m_cart->tag()) + GENERIC_ROM_REGION_TAG);
	}
}

void leapfrog_leappad_state::machine_reset()
{
}

void leapfrog_leappad_state::prog_map(address_map &map)
{
	map(0x0000, 0xffff).rom().region("maincpu", 0x10000); // TODO: banking
}

void leapfrog_leappad_state::ext_map(address_map &map)
{
}

DEVICE_IMAGE_LOAD_MEMBER(leapfrog_leappad_state::cart_load)
{
	uint32_t size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

static INPUT_PORTS_START( leapfrog_leappad )
INPUT_PORTS_END


void leapfrog_leappad_state::leapfrog_leappad(machine_config &config)
{
	I8032(config, m_maincpu, 96000000/10); //  LeapPad Leapfrog 05-9-01 FS80A363  (which exact type is it?)
	m_maincpu->set_addrmap(AS_PROGRAM, &leapfrog_leappad_state::prog_map);
	m_maincpu->set_addrmap(AS_IO, &leapfrog_leappad_state::ext_map);

	// screenless

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "leapfrog_leappad_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(leapfrog_leappad_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("leapfrog_leappad_cart");
}

void leapfrog_leappad_state::leapfrog_mfleappad(machine_config &config)
{
	I8032(config, m_maincpu, 96000000/10); //  LeapPad Leapfrog 05-9-01 FS80A363  (which exact type is it?)
	m_maincpu->set_addrmap(AS_PROGRAM, &leapfrog_leappad_state::prog_map);
	m_maincpu->set_addrmap(AS_IO, &leapfrog_leappad_state::ext_map);

	// screenless

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "leapfrog_mfleappad_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(leapfrog_leappad_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("leapfrog_mfleappad_cart");
}

void leapfrog_leappad_state::leapfrog_ltleappad(machine_config &config)
{
	I8032(config, m_maincpu, 96000000/10); // (which exact type is it?)
	m_maincpu->set_addrmap(AS_PROGRAM, &leapfrog_leappad_state::prog_map);
	m_maincpu->set_addrmap(AS_IO, &leapfrog_leappad_state::ext_map);

	// screenless

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "leapfrog_ltleappad_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(leapfrog_leappad_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("leapfrog_ltleappad_cart");
}


// All of these contain the string "Have you copied our ROM?" near the date codes

ROM_START( leappad )
	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS("ila2_universal")
	ROM_SYSTEM_BIOS( 0, "ila2_universal",  "Universal" )
	ROMX_LOAD( "leappadbios.bin",       0x000000, 0x100000, CRC(c886cddc) SHA1(f8a83b156feb28315d2321758678e141600a0d4e), ROM_BIOS(0) ) // contains "Aug 06 2001.16:33:16.155-00450.LeapPad ILA2 Universal Base ROM" and "Copyright (c) 1998-2001 Knowledge Kids Enterprises, Inc."
	ROM_SYSTEM_BIOS( 1, "2mb_canada_full", "Canada" )
	ROMX_LOAD( "leappadbioscanada.bin", 0x000000, 0x200000, CRC(cc12e3db) SHA1(adf52232adcfd4de5d8e31c0e0c09be61718a9d4), ROM_BIOS(1) ) // contains "Jan 23 2004 11:28:40 152-10620 2MB Canada Full Base ROM" and "Copyright (c) 2000-2004 LeapFrog Enterprises, Inc."

	ROM_REGION( 0x8000, "bootloader", 0) // Main MCU (LeapFrog FS80A363) internal ROM (exact size unknown)
	ROM_LOAD( "fs80a363.u4", 0x0000, 0x8000, NO_DUMP )
ROM_END

ROM_START( mfleappad )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS("internat_v1.3")
	ROM_SYSTEM_BIOS( 0, "internat_v1.3",  "International" )
	ROMX_LOAD( "myfirstleappadinternational.bin", 0x000000, 0x100000, CRC(4dc0c4d5) SHA1(573ecf2efaccf70e619cf54d63be9169e469ee6f), ROM_BIOS(0) ) // contains "May 07 2002 10:53:14 152-00932 MFLP International base ROM V1.3" and "Copyright (c) 2002 LeapFrog Enterprises, Inc."
	ROM_SYSTEM_BIOS( 1, "us_2004",        "US" )
	ROMX_LOAD( "myfirstleappadbios.bin",          0x000000, 0x400000, CRC(19174c16) SHA1(e0ba644fdf38fd5f91ab8c4b673c4a658cc3e612), ROM_BIOS(1) ) // contains "Feb 13 2004.10:58:53.152-10573.MFLP US Base ROM - 2004" and "Copyright (c) 2004 LeapFrog Enterprises, Inc."

	ROM_REGION( 0x8000, "bootloader", 0) // Main MCU (LeapFrog FS80A363) internal ROM (exact size unknown)
	ROM_LOAD( "fs80a363.u4", 0x0000, 0x8000, NO_DUMP )
ROM_END

ROM_START( leappadmic )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS("us_2004")
	ROM_SYSTEM_BIOS( 0, "us_2004", "US" )
	ROMX_LOAD( "leapfrogvpadwithmic.bin", 0x000000, 0x800000, CRC(c289b660) SHA1(fa163661260942eab92e34ae802b3da0a6130a39), ROM_BIOS(0) ) // contains "Apr 29 2004 12:09:09 152-10793 MIB - LeapPad Plus Microphone baseROM - US 2004" and "Copyright (c) 2000-2004 LeapFrog Enterprises, Inc."

	ROM_REGION( 0x8000, "bootloader", 0) // Main MCU (LeapFrog FS80A363) internal ROM (exact size unknown)
	ROM_LOAD( "fs80a363.u4", 0x0000, 0x8000, NO_DUMP )

	// Sunplus PA7790 sound MCU internal ROM (exact size unknown)
	// It is unknown if other LeapPad models use the same MCU
	ROM_REGION( 0x8000, "soundmcu", 0)
	ROM_LOAD( "pa7790.u15",  0x0000, 0x8000, NO_DUMP )
ROM_END

ROM_START( ltleappad )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS("mar_10_2005")
	ROM_SYSTEM_BIOS( 0, "mar_10_2005", "Universal" ) // Includes the game 'One Bear in the Bedroom'
	ROMX_LOAD( "littletouchleappadbios.bin",      0x000000, 0x400000, CRC(13687b26) SHA1(6ec1a47aaef9c9ed134bb143c2631f4d89d7c236), ROM_BIOS(0) ) // contains "Mar 10 2005 07:01:53 152-11244" and "Copyright (c) 2002-2005 LeapFrog Enterprises, Inc."
	ROM_SYSTEM_BIOS( 1, "germany",     "Germany" )
	ROMX_LOAD( "leappad_little_touch_german.bin", 0x000000, 0x400000, CRC(39ee76a2) SHA1(34f1b6e075e10e14380d925944f4c84d068ec58e), ROM_BIOS(1) ) // contains "Jan 11 2005 10:45:42 152-11010 Full Base ROM: V1.0 - Germany"

	ROM_REGION( 0x8000, "bootloader", 0) // Main MCU (LeapFrog FS80A363) internal ROM (exact size unknown)
	ROM_LOAD( "fs80a363.u4", 0x0000, 0x8000, NO_DUMP )
ROM_END

} // anonymous namespace


//    year, name,       parent, compat, machine,            input,            class,                  init,       company,    fullname,                  flags
CONS( 2001, leappad,    0,      0,      leapfrog_leappad,   leapfrog_leappad, leapfrog_leappad_state, empty_init, "LeapFrog", "LeapPad",                 MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2002, mfleappad,  0,      0,      leapfrog_mfleappad, leapfrog_leappad, leapfrog_leappad_state, empty_init, "LeapFrog", "My First LeapPad",        MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2004, leappadmic, 0,      0,      leapfrog_leappad,   leapfrog_leappad, leapfrog_leappad_state, empty_init, "LeapFrog", "LeapPad Plus Microphone", MACHINE_NO_SOUND | MACHINE_NOT_WORKING ) // Compatible with regular LeapPad carts
CONS( 2005, ltleappad,  0,      0,      leapfrog_ltleappad, leapfrog_leappad, leapfrog_leappad_state, empty_init, "LeapFrog", "Little Touch LeapPad",    MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



leapster.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/*
    LeapFrog - Leapster

    educational system from 2003, software is all developed in MXFlash

    hwspecs


    CPU:
      Custom ASIC (ARCTangent 5.1 CPU @ 96MHz)

    Memory:
      Leapster: 2MB onboard RAM, 256 bytes NVRAM.
      Leapster2: 16MB RAM, 128kbytes NVRAM

    Media type:
      Cartridges of 4-16MB with between 2 and 512kb NVRAM

    Graphics:
      4Mb ATI chip.

    Audio:
      Custom

    Screen:
      160x160 CSTN with touchscreen.


    The Leapster 2 also has
        USB 1.1 (client only) + full-sized SD slot.


    many magic numbers in the BIOS ROM match the [strings:VALID_FLAGS] table in
    https://github.com/tsbiberdorf/MqxSrc/blob/master/tools/tad/mqx.tad
    does this mean the System is running on the MQX RTOS?
    https://www.synopsys.com/dw/ipdir.php?ds=os_mqx_software
    indicates it was available for ARC processors

*/

/* Cartridge pinout - for games list see hash/leapster.xml

CARTRIDGE-PINOUT:
-----------------
Look at the Cartridge-Slot:

                   B        A
                  ------------
                  VCC |01| VCC
                   NC |02| VSS
                  D11 |03| D04
                  D03 |04| D12
                  D10 |05| D05
                  VSS |06| D02
                  D13 |07| D09
                   NC |08| NC
                  D06 |09| D01
                  D14 |10| D08
                  ----|--|----
                  D07 |11| VSS
                  D00 |12| D15
                 Byte |13| OE
                   NC |14| A22
                   NC |15| A23
                   CE |16| A16
                  A00 |17| A15
                  A01 |18| A14
                  A02 |19| A13
                  VSS |20| A03
                  A12 |21| A04
                  A11 |22| A05
                  A10 |23| A06
                  A09 |24| A07
                  A08 |25| A17
                  A19 |26| A18
                  A20 |27| A21
                   WE |28| VSS
PIN7 of 24LC02B <---| |29| |---> PIN7 of 24LC02B
                   NC |30| |---> PIN6 of 24LC02B
                  ----------




PCB - Handheld-Console:

               +-----------------------------+
               |                             |
  +------------|                             |------------+
  |            | C A R T R I D G E - S L O T |            |
  |            |                             |            |
  |            +-----------------------------+            |
  |                                                       |
  |ASY 310-10069    +-------------------+                 |
  |                 |                   |                 |
  |                 |                   |                 |
  |LEAPSTER MAIN    |                   |                 |
  |Leap Frog        |                 U3|                 |
  |(c) 2004         +-------------------+     +-----+     |
  |                                           |ISSI |     |
  |                                           |     |     |
  |                      +---------+          |IS42S|     |
  |                      |         |          |16100|     |
   \       +-+           | EPOXY   |          |AT-7T|    /
    \      |A|           |   BLOCK |          |     |   /
     \     +-+           |         |          |   U2|  /
      \                  |       U1|          +-----+ /
       \                 +---------+                 /
        \                                           /
         \                                         /
          \                                       /
           \                                     /
            \                                   /
             \                                 /
              \                               /
               \                             /
                \                           /
                 \-------------------------/


A = 24LC02B / SN0429


ETCHES ON THE BACK OF THE PCB:

"FAB-300-10069-C"

"702800254.01A"
"SW1208 Rev.5"



PCB - Cartridge:
FRONT:

+-------------------------------------+
| LEAPSTER ROM CARTRIDGE              |
|   +--+     +---------+    Leap Frog |
|   |B1|U2   |         |     (c) 2003 |
+-+ +--+     |E P O X Y|            +-+
  |          |       U3|            |
+-+          +---------+            +-+
|                       20232-003-1020|
| ASY 310-10028             REV:00    |
+-+                    +-+          +-+
  |||||||||||||||||||||| ||||||||||||
  +--------------------+ +----------+
 A30                                A01

B1: 24L002B

24L002B:

             +-----+
  (GND)<- A0-|     |-VCC
  (GND)<- A1-|     |-WP
  (GND)<- A2-|     |-SCL
         VSS-|     |-SDA
             +-----+



PCB - LEAPSTER-TV:

+-----------------------------------------------------------------------+
|                                                                       |
|                                                                       |
|           20300+003+0015                                              |
 \           REV:06                                                    /
  |                                                                   |
  |                                                                   |
  |                 +----+     +-------+                              |
   \                |    |     | EPOXY |                             /
    |   +----+      |    |     |       |                            |
    |   |EPOX|      |    |     |       |                            |
    |   |Y   |      |    |     |     U1|    +-+                     |
     \  +----+      |    |     +-------+    | |                    /
      |             |  U2|                  +-+U4                 |
      |             +----+                                        |
      |                     +--------------+   ROADRUNNER CONSOLE |
       \                    | AM29PL160CB  |   Leap Frog (c) 2006/
        |                   | -90SF        |   Asy 310-10378    |
        |                   |            U6|                    |
        |                   +--------------+                    |
         \                                                     /
          |          +-----------------------------+          |
          |          | C A R T R I D G E - S L O T |          |
          |          +-----------------------------+          |
           \                                                 /
            +-----------------------------------------------+

*/

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/arcompact/arcompact.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"


namespace {

class leapster_state : public driver_device
{
public:
	leapster_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_cart(*this, "cartslot"),
		m_palette(*this, "palette")
		{ }

	void leapster(machine_config &config);

	void init_leapster();

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint32_t screen_update_leapster(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	INTERRUPT_GEN_MEMBER(testirq);

	uint32_t leapster_1801000_r();
	uint32_t leapster_1801004_r();
	uint32_t leapster_1801008_r();
	uint32_t leapster_180100c_r();
	uint32_t leapster_1801018_r();
	uint32_t leapster_1809004_r();
	uint32_t leapster_1809008_r();
	uint32_t leapster_180b000_r();
	uint32_t leapster_180b004_r();
	uint32_t leapster_180b008_r();
	uint32_t leapster_180d400_r();
	uint32_t leapster_180d514_r();
	uint32_t leapster_180d800_r();

	void leapster_aux0047_w(uint32_t data);
	uint32_t leapster_aux0048_r();
	void leapster_aux0048_w(uint32_t data);
	void leapster_aux004b_w(uint32_t data);

	void leapster_aux0010_w(uint32_t data);
	uint32_t leapster_aux0011_r();
	void leapster_aux0011_w(uint32_t data);
	void leapster_aux001a_w(uint32_t data);
	uint32_t leapster_aux001b_r();

	void leapster_aux(address_map &map) ATTR_COLD;
	void leapster_map(address_map &map) ATTR_COLD;

	uint16_t m_1a_data[0x800];
	int m_1a_pointer;

	required_device<arcompact_device> m_maincpu;
	required_device<generic_slot_device> m_cart;
	required_device<palette_device> m_palette;

	memory_region *m_cart_rom = nullptr;
};


static INPUT_PORTS_START( leapster )
INPUT_PORTS_END

void leapster_state::leapster_aux0010_w(uint32_t data)
{
}

void leapster_state::leapster_aux0011_w(uint32_t data)
{
	// unknown, written with 1a
}

void leapster_state::leapster_aux001a_w(uint32_t data)
{
	// probably not palette, but it does load 0x1000 words of increasing value on startup, so could be?
	m_1a_data[m_1a_pointer & 0x7ff] = data;

	uint8_t r = (data >> 12) & 0x7;
	uint8_t g = (data >> 8) & 0xf;
	uint8_t b = (data >> 4) & 0xf;

	m_palette->set_pen_color(m_1a_pointer & 0x7ff, rgb_t(pal3bit(r), pal4bit(g), pal4bit(b)));
	m_1a_pointer++;
}

uint32_t leapster_state::leapster_aux0011_r()
{
	// unknown, read when 11/1a are being written
	logerror("%s: leapster_aux0011_r\n", machine().describe_context());
	return 0x00000000;
}

uint32_t leapster_state::leapster_aux001b_r()
{
	// unknown, read when 11/1a are being written
	logerror("%s: leapster_aux001b_r\n", machine().describe_context());
	return 0x00000000;
}

void leapster_state::leapster_aux0047_w(uint32_t data)
{
	logerror("%s: leapster_aux0047_w %08x\n", machine().describe_context(), data);
}

uint32_t leapster_state::leapster_aux0048_r()
{
	logerror("%s: leapster_aux0048_r\n", machine().describe_context());
	return 0x00000000;
}

void leapster_state::leapster_aux0048_w(uint32_t data)
{
	logerror("%s: leapster_aux0047_w %08x\n", machine().describe_context(), data);
}

void leapster_state::leapster_aux004b_w(uint32_t data)
{
	logerror("%s: leapster_aux004b_w %08x\n", machine().describe_context(), data);
}

uint32_t leapster_state::leapster_1801000_r()
{
	logerror("%s: leapster_1801000_r\n", machine().describe_context());
	return 0x00000000;
}

uint32_t leapster_state::leapster_1801004_r()
{
	logerror("%s: leapster_1801004_r\n", machine().describe_context());
	return 0x00000000;
}

uint32_t leapster_state::leapster_1801008_r()
{
	logerror("%s: leapster_1801004_r\n", machine().describe_context());
	return 0x00000000;
}

uint32_t leapster_state::leapster_180100c_r()
{
	logerror("%s: leapster_180100c_r\n", machine().describe_context());
	return 0x00000000;
}

uint32_t leapster_state::leapster_1801018_r()
{
	logerror("%s: leapster_1801018_r\n", machine().describe_context());
	return 0x00000000;
}

uint32_t leapster_state::leapster_1809004_r()
{
	logerror("%s: leapster_1809004_r (return usually checked against 0x00200000)\n", machine().describe_context());
	// does an AND with 0x00200000 and often jumps to dead loops if that fails
	return 0x00200000;
}

uint32_t leapster_state::leapster_1809008_r()
{
	logerror("%s: leapster_1809008_r\n", machine().describe_context());
	return 0x00000000;
}

uint32_t leapster_state::leapster_180b000_r()
{
	logerror("%s: leapster_180b000_r\n", machine().describe_context());
	return 0x00000000;
}

uint32_t leapster_state::leapster_180b004_r()
{
	// leapster2 BIOS checks if this is 0
	logerror("%s: leapster_180b004_r\n", machine().describe_context());
	return 0xffffffff;
}

uint32_t leapster_state::leapster_180b008_r()
{
	// checks bit 1 (using BMSK instruction and AND instruction)
	// writes back to same address?
	logerror("%s: leapster_180b008_r\n", machine().describe_context());
	return 0x00000001;
}

uint32_t leapster_state::leapster_180d400_r()
{
	logerror("%s: leapster_180d400_r (return usually checked against 0x0030d400)\n", machine().describe_context());
	// does a BRLO.ND against it
	// loops against 0x0030d400 (3,200,000) at 4003A52A for example
	return 0x0030d400;
}

uint32_t leapster_state::leapster_180d514_r()
{
	logerror("%s: leapster_180d514_r (return usually checked against 0x0030d400)\n", machine().describe_context());
	// leapster -bios 0 does a BRNE in a loop comparing with 0x80
	return 0x00000080;
}

uint32_t leapster_state::leapster_180d800_r()
{
	logerror("%s: leapster_180d800_r (return usually checked against 0x00027100 or 0x00003e80)\n", machine().describe_context());
	// does a BRLO.ND against it
	// loops against 0x00027100 (160,000)
	// loops against 0x00003e80 (16,000) in other places 4003A56C for example
	return 0x00027100;
}

uint32_t leapster_state::screen_update_leapster(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

DEVICE_IMAGE_LOAD_MEMBER( leapster_state::cart_load )
{
	uint32_t size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM32_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

void leapster_state::machine_start()
{
	std::string region_tag;
	m_cart_rom = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());

	if (m_cart_rom)
	{
		m_maincpu->space(AS_PROGRAM).install_rom(0x80000000, 0x807fffff, m_cart_rom->base());
	}

	save_item(NAME(m_1a_data));
}

void leapster_state::machine_reset()
{
	m_1a_pointer = 0;
	for (int i = 0; i < 0x800; i++)
		m_1a_data[i] = 0;
}

void leapster_state::leapster_map(address_map &map)
{
//  A vector table is copied from 0x00000000 to 0x3c000000, but it is unclear if that is a BIOS mirror
//  or if it should be copying a different table.
	map(0x00000000, 0x007fffff).mirror(0x40000000).rom().region("maincpu", 0);
	//map(0x40000000, 0x407fffff).rom().region("maincpu", 0);

//  map(0x01800000, 0x0180ffff).ram();

	map(0x01801000, 0x01801003).r(FUNC(leapster_state::leapster_1801000_r));
	map(0x01801004, 0x01801007).r(FUNC(leapster_state::leapster_1801004_r));
	map(0x01801008, 0x0180100b).r(FUNC(leapster_state::leapster_1801008_r));
	map(0x0180100c, 0x0180100f).r(FUNC(leapster_state::leapster_180100c_r));
	map(0x01801018, 0x0180101b).r(FUNC(leapster_state::leapster_1801018_r));

	map(0x01809004, 0x01809007).r(FUNC(leapster_state::leapster_1809004_r));
	map(0x01809008, 0x0180900b).r(FUNC(leapster_state::leapster_1809008_r));

	map(0x0180b000, 0x0180b003).r(FUNC(leapster_state::leapster_180b000_r));
	map(0x0180b004, 0x0180b007).r(FUNC(leapster_state::leapster_180b004_r));
	map(0x0180b008, 0x0180b00b).r(FUNC(leapster_state::leapster_180b008_r));

	map(0x0180d400, 0x0180d403).r(FUNC(leapster_state::leapster_180d400_r));

	map(0x0180d514, 0x0180d517).r(FUNC(leapster_state::leapster_180d514_r));

	map(0x0180d800, 0x0180d803).r(FUNC(leapster_state::leapster_180d800_r));

	map(0x03000000, 0x030007ff).ram(); // puts stack here, writes a pointer @ 0x03000000 on startup
	map(0x03000800, 0x0300ffff).ram(); // some of the later models need to store stack values here (or code execution has gone wrong?)
	map(0x3c000000, 0x3c1fffff).ram(); // vector base gets moved here with new IRQ table, puts task stacks etc. here
	map(0x3c200000, 0x3fffffff).ram();
	// map(0x80000000, 0x807fffff).bankr("cartrom"); // game ROM pointers are all to the 80xxxxxx region, so I assume it maps here - installed if a cart is present
}


void leapster_state::leapster_aux(address_map &map)
{
	// addresses used here aren't known internal ARC addresses, so are presumed to be Leapster specific
	map(0x000000010, 0x000000010).w(FUNC(leapster_state::leapster_aux0010_w));
	map(0x000000011, 0x000000011).rw(FUNC(leapster_state::leapster_aux0011_r), FUNC(leapster_state::leapster_aux0011_w));
	map(0x00000001a, 0x00000001a).w(FUNC(leapster_state::leapster_aux001a_w));
	map(0x00000001b, 0x00000001b).r(FUNC(leapster_state::leapster_aux001b_r));

	map(0x000000047, 0x000000047).w(FUNC(leapster_state::leapster_aux0047_w));
	map(0x000000048, 0x000000048).rw(FUNC(leapster_state::leapster_aux0048_r), FUNC(leapster_state::leapster_aux0048_w));
	map(0x00000004b, 0x00000004b).w(FUNC(leapster_state::leapster_aux004b_w));
}

INTERRUPT_GEN_MEMBER(leapster_state::testirq)
{
	m_maincpu->set_input_line(0, ASSERT_LINE);
}

void leapster_state::leapster(machine_config &config)
{
	// Basic machine hardware
	// CPU is ArcTangent-A5 '5.1' (ARCompact core)
	ARCA5(config, m_maincpu, 96000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &leapster_state::leapster_map);
	m_maincpu->set_addrmap(AS_IO, &leapster_state::leapster_aux);
	m_maincpu->set_default_vector_base(0x40000000);
	m_maincpu->set_vblank_int("screen", FUNC(leapster_state::testirq));

	// Video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_size(160, 160);
	screen.set_visarea(0, 160-1, 0, 160-1);
	screen.set_screen_update(FUNC(leapster_state::screen_update_leapster));

	PALETTE(config, "palette").set_format(palette_device::xRGB_444, 0x800).set_endianness(ENDIANNESS_BIG);

	// Cartridge
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "leapster_cart", "bin").set_device_load(FUNC(leapster_state::cart_load));

	// Software lists
	SOFTWARE_LIST(config, "cart_list").set_original("leapster");
}

#define ROM_LOAD_BIOS(bios,name,offset,length,hash) \
		ROMX_LOAD(name, offset, length, hash, ROM_BIOS(bios))

/* There are various build dates and revisions for different parts of the code, the date listed is the newest on in each ROM.
   This is always in the same place relative to the rest of the data.

   V2.1 sets (except TV) are apparently larger because "Learning with Leap" was built in.
*/

ROM_START(leapster)
	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_SYSTEM_BIOS( 0, "uni15", "Universal v1.5" ) // 152-10346 Leapster BaseROM Universal v1.5 - Sep 04 2003 10:46:47
	ROM_LOAD_BIOS( 0, "155-10072-a.bin"   , 0x00000, 0x200000, CRC(af05e5a0) SHA1(d4468d060543ba7e44785041093bc98bcd9afa07) )

	// most (all?) ROMs below seem to be from LMAX devices, or Leapster 2 devices based on the boot logos contained within!
	// TODO: properly sort once they boot in the driver
	ROM_SYSTEM_BIOS( 1, "us21",  "USA v2.1" )       // 152-11265 Leapster BaseROM US v2.1        - Apr 13 2005 15:34:57
	ROM_LOAD_BIOS( 1, "152-11265_2.1.bin",  0x00000, 0x800000, CRC(9639b3ae) SHA1(002873b782e823c7a8159deed16c78c149f2afab) )
	ROM_SYSTEM_BIOS( 2, "uk21",  "UK v2.1" )        // 152-11452 Leapster BaseROM UK v2.1        - Aug 30 2005 16:01:46
	ROM_LOAD_BIOS( 2, "leapster2_1004.bin", 0x00000, 0x800000, CRC(b466e14d) SHA1(910c234f03e76b7de55b8aa0a0c62fd1daae4910) )
	ROM_SYSTEM_BIOS( 3, "ger21", "German v2.1" )    // 152-11435 Leapster BaseROM German v2.1    - Oct 21 2005 18:53:59
	ROM_LOAD_BIOS( 3, "leapster2_1006.bin", 0x00000, 0x800000, BAD_DUMP CRC(a69ed8ca) SHA1(e6aacba0c39b1465f344c2b07ff1cbd8a395adac) ) // BADADDR xxx-xxxxxxxxxxxxxxxxxxx
	ROM_SYSTEM_BIOS( 4, "sp10",  "Spanish v1.0" )   // 152-11546 Leapster Baserom SP v1.0        - Apr 03 2006 06:26:00
	ROM_LOAD_BIOS( 4, "leapster2_1008.bin", 0x00000, 0x800000, CRC(b43345e7) SHA1(31c27e79568115bf36e5ef668f528e3005054152) )
	ROM_SYSTEM_BIOS( 5, "connb5",  "Connected B5" )  // 152-12076 Leapster Connected Baserom B5   - Feb 29 2008 18:11:21
	ROM_LOAD_BIOS( 5, "152-12076_b5.bin",   0x00000, 0x800000, CRC(4d223022) SHA1(bdc10ad70aa7641716e16fbea16bd0ef35f6e85e) ) // is this a Leapster 2 BIOS or another device called "Leapster Connected"?
	ROM_DEFAULT_BIOS( "uni15" )
ROM_END

ROM_START(leapstertv)
	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_SYSTEM_BIOS( 0, "uni2111", "Universal v2.1.11" ) // 152-11594 LeapsterTv Baserom Universal.v2.1.11 - Apr 13 2006 16:36:08
	ROM_LOAD_BIOS( 0, "am29pl160cb-90sf.bin", 0x00000, 0x200000, CRC(194cc724) SHA1(000a79d75c19f2e43532ce0b31f0dca0bed49eab) )
	ROM_DEFAULT_BIOS( "uni2111" )
ROM_END

ROM_START(leapster2)
	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_SYSTEM_BIOS( 0, "2xcip3_m9", "2x CIP3 m9" ) // 152-12659 Leapster 2x CIP3 Baserom m9 - Mar 29 2011 14:13:45
	ROM_LOAD_BIOS( 0, "152-12659_m9.bin",  0x00000, 0x800000, CRC(57bde604) SHA1(4b5eaac1e40bc605eb4cf6d4ad212343334762fd) )
	ROM_SYSTEM_BIOS( 1, "2x_06",     "2x 0.6" )     // 152-12206 Leapster 2x Baserom 0.6     - Feb 02 2009 17:15:38
	ROM_LOAD_BIOS( 1, "152-12206_0.6.bin", 0x00000, 0x800000, CRC(fa94d9a7) SHA1(c5bd84146701dc4a7635b0e37adedb90747adf32) )
	ROM_DEFAULT_BIOS( "2xcip3_m9" )
ROM_END

ROM_START(leapsterlmx)
	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASE00)
	ROM_SYSTEM_BIOS( 0, "lmax_2_2",    "v2.2" )     // 152-11476 LMAX Baserom v2.2    - Jan 12 2006 11:22:50
	ROM_LOAD_BIOS( 0, "152-11476_v2.2.bin",    0x00000, 0x800000, CRC(e1140475) SHA1(42089165db67005b6a0180e894ff8f36b97a081e) )
	ROM_SYSTEM_BIOS( 1, "lmax_us_2_1", "USA v2.1" ) // 152-11238 LMAX BaseROM US v2.1 - Mar 04 2005 12:01:01
	ROM_LOAD_BIOS( 1, "152-11238_us_v2.1.bin", 0x00000, 0x800000, CRC(80bb4e58) SHA1(7d8b1c23d08ce76a89cff1112957377c6a1d4b63) )
	ROM_DEFAULT_BIOS( "lmax_2_2" )
ROM_END

void leapster_state::init_leapster()
{
}

} // anonymous namespace


CONS( 2003, leapster,    0,        0, leapster, leapster, leapster_state, init_leapster, "LeapFrog", "Leapster",       MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2005, leapstertv,  leapster, 0, leapster, leapster, leapster_state, init_leapster, "LeapFrog", "Leapster TV",    MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2005, leapsterlmx, leapster, 0, leapster, leapster, leapster_state, init_leapster, "LeapFrog", "Leapster L-MAX", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2009, leapster2,   leapster, 0, leapster, leapster, leapster_state, init_leapster, "LeapFrog", "Leapster 2",     MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



leapster_explorer.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/******************************************************************************

    LeapFrog Leapster Explorer

     - runs Linux
     - unknown ARM9 based SoC

     Internal ROM not currently dumped, this file exists to reference the
     Software List

*******************************************************************************/

#include "emu.h"

#include "cpu/arm7/arm7.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class leapfrog_leapster_explorer_state : public driver_device
{
public:
	leapfrog_leapster_explorer_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_cart(*this, "cartslot")
		, m_cart_region(nullptr)
	{ }

	void leapfrog_leapster_explorer(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	required_device<cpu_device> m_maincpu;

	required_device<screen_device> m_screen;
	required_device<generic_slot_device> m_cart;
	memory_region *m_cart_region;

	uint32_t screen_update_innotab(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};

uint32_t leapfrog_leapster_explorer_state::screen_update_innotab(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void leapfrog_leapster_explorer_state::machine_start()
{
	// if there's a cart, override the standard mapping
	if (m_cart && m_cart->exists())
	{
		m_cart_region = memregion(std::string(m_cart->tag()) + GENERIC_ROM_REGION_TAG);
	}
}

DEVICE_IMAGE_LOAD_MEMBER(leapfrog_leapster_explorer_state::cart_load)
{
	uint32_t size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

static INPUT_PORTS_START( leapfrog_leapster_explorer )
INPUT_PORTS_END


void leapfrog_leapster_explorer_state::leapfrog_leapster_explorer(machine_config& config)
{
	ARM9(config, m_maincpu, 393000000); // unknown ARM9 type

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320, 262);
	m_screen->set_visarea(0, 320 - 1, 0, 240 - 1);
	m_screen->set_screen_update(FUNC(leapfrog_leapster_explorer_state::screen_update_innotab));

	SPEAKER(config, "speaker", 2).front();

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "leapfrog_leapster_explorer_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(leapfrog_leapster_explorer_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("leapster_explorer_cart");
}

ROM_START( leapexpr )
	ROM_REGION( 0x0100000, "maincpu", ROMREGION_ERASEFF )
	// unknown internal ROM
	ROM_LOAD( "internal.rom", 0x000000, 0x0100000, NO_DUMP )
ROM_END

} // anonymous namespace


CONS( 2010, leapexpr,     0,       0,      leapfrog_leapster_explorer, leapfrog_leapster_explorer, leapfrog_leapster_explorer_state, empty_init, "LeapFrog", "Leapster Explorer",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



learnwin.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Scott Stone
/***************************************************************************

learnwin.cpp

Learning-Window Teaching Computer by VTech / Spiel Master by Yuvo (German)

Info from Kevin Horton (Kevtris):
The -081 on the 'speech' cart board had that mystery sp0256 next to it with its markings ground off.
The chip with the marking ground off is the most common SP0256 chip, the SP0256-AL2 chip
(it was common enough to be sold by electronic stores like Radio Shack in the USA)
and used on the currah speech cart for c64 and many many other places.

The rom for this exists in MAME as:
ROM_LOAD( "sp0256-al2.bin", 0x1000, 0x0800, CRC(b504ac15) SHA1(e60fcb5fa16ff3f3b69d36c7a6e955744d3feafc) )
On this type of v-tech/yeno speech cart, it had its internal rom disabled by tying one of the pins high on the board.

    Readme for the SPR128A chips:
    Chip Label - filename Address UndersideMark Type
    SPR128A-080 - sp128_80.bin 0000-3FFF 27-0643 L.window self-mapped
    SPR128A-081 - sp128_81.bin 0000-3FFF 27-0644 L.window self-mapped
    SPR128A-093 - sp128_93.bin 0000-3FFF 27-0754-00 L.window self-mapped

    Chip Label - Product
    SPR128A-080 - Learning Window system (German)
    SPR128A-081 - Learning Window speech cart (German)
    SPR128A-093 - Learning Window cart Alphabet Salat (German)

Part Numbers and Descriptions
SPR128A-046    -  Learning Window system US (rev 1)
SPR128A-047    -  Learning Window speech cart US (rev 1)
SPR128A-049    -  Learning Window cart Number Power  US
SPR128A-050    -  Learning Window cart Alphabet Soup  U.S.
SPR128A-055    -  Learning Window cart IQ Builder U.S.
SPR128A-069    -  Learning Window system french
SPR128A-077    -  Learning Window system US (rev 2)
SPR128A-080    -  Learning Window system (German)
SPR128A-081    -  Learning Window speech cart (German)
SPR128A-088    -  Learning Window system US (rev 3)
SPR128A-089    -  Learning Window speech cart US (rev 2)
SPR128A-093    -  Learning Window cart Alphabet Salat (German)
SPR128A-099    -  Learning Window speech cart US (rev 3)

***************************************************************************/


#include "emu.h"

#include "sound/sp0256.h"

#include "screen.h"
#include "speaker.h"


namespace {

class learnwin_state : public driver_device
{
public:
	learnwin_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
	{
	}

	void learnwin(machine_config &config);

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
	{
		bitmap.fill(rgb_t::black(), cliprect);
		return 0;
	}

	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
};


static INPUT_PORTS_START( learnwin )
	/* dummy active high structure */
	PORT_START("SYSA")
	PORT_DIPNAME( 0x01, 0x00, "SYSA" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )

	/* dummy active low structure */
	PORT_START("DSWA")
	PORT_DIPNAME( 0x01, 0x01, "DSWA" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END


void learnwin_state::machine_start()
{
}


void learnwin_state::machine_reset()
{
}


void learnwin_state::learnwin(machine_config &config)
{
	/* video hardware */
//  screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
//  screen.set_refresh_hz(60);
//  screen.set_size(48, 32);
//  screen.set_visarea(0, 47, 0, 31);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
}


/***************************************************************************

  Machine driver(s)

***************************************************************************/

ROM_START( learnwin )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "sp128_88.bin",  0x0000, 0x4000, CRC(b71719b0) SHA1(22dc76d21a7ea29398b502d049a44d9aeeb97a87) ) // system
	ROM_LOAD( "sp128_99.bin",  0x4000, 0x4000, CRC(525e0d14) SHA1(444df5d31308f3e8dc8608d1c04277f506d4308b) ) // speech cart
	ROM_LOAD( "sp0256-al2.bin",0x8000, 0x0800, CRC(b504ac15) SHA1(e60fcb5fa16ff3f3b69d36c7a6e955744d3feafc) ) // speech cart voice chip
	ROM_LOAD( "toshiba_t7984", 0xc000, 0x2000, NO_DUMP ) // unknown - possible MCU
ROM_END

ROM_START( learnwin2 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "sp128_77.bin",  0x0000, 0x4000, CRC(2c1d66e5) SHA1(2d25f80ace39d07d815f873541abb4071958b519) ) // system
	ROM_LOAD( "sp128_89.bin",  0x4000, 0x4000, CRC(455fd900) SHA1(3909e8b7267b53e07e2d03e1d81efab66efedafa) ) // speech cart
	ROM_LOAD( "sp0256-al2.bin",0x8000, 0x0800, CRC(b504ac15) SHA1(e60fcb5fa16ff3f3b69d36c7a6e955744d3feafc) ) // speech cart voice chip
	ROM_LOAD( "toshiba_t7984", 0xc000, 0x2000, NO_DUMP ) // unknown - possible MCU
ROM_END

ROM_START( learnwin1 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "sp128_46.bin",  0x0000, 0x4000, CRC(a200771f) SHA1(347eda0a284457a82225c607c3bdd7baea0945c1) ) // system
	ROM_LOAD( "sp128_47.bin",  0x4000, 0x4000, CRC(c177641b) SHA1(c4d32019c4453f661f803ee3f018bdc9427de548) ) // speech cart
	ROM_LOAD( "sp0256-al2.bin",0x8000, 0x0800, CRC(b504ac15) SHA1(e60fcb5fa16ff3f3b69d36c7a6e955744d3feafc) ) // speech cart voice chip
	ROM_LOAD( "toshiba_t7984", 0xc000, 0x2000, NO_DUMP ) // unknown - possible MCU
ROM_END

ROM_START( learnwinf )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "sp128.069",     0x0000, 0x4000, CRC(af6076e1) SHA1(4efca85b86ca03724ee23360868de30cb2aa10ca) ) // system
	ROM_LOAD( "sp128_xx.bin",  0x4000, 0x4000, NO_DUMP ) // speech cart
	ROM_LOAD( "sp0256-al2.bin",0x8000, 0x0800, CRC(b504ac15) SHA1(e60fcb5fa16ff3f3b69d36c7a6e955744d3feafc) ) // speech cart voice chip
	ROM_LOAD( "toshiba_t7984", 0xc000, 0x2000, NO_DUMP ) // unknown - possible MCU
ROM_END

ROM_START( spielmast )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "sp128_80.bin",  0x0000, 0x4000, CRC(e5dcc22f) SHA1(7da642d52e980ef917e56b7488e2b8d1b63bd757) ) // system
	ROM_LOAD( "sp128_81.bin",  0x4000, 0x4000, CRC(9f761217) SHA1(d85bc443a7d5856335f3e4cf8742512555345af8) ) // speech cart
	ROM_LOAD( "sp0256-al2.bin",0x8000, 0x0800, CRC(b504ac15) SHA1(e60fcb5fa16ff3f3b69d36c7a6e955744d3feafc) ) // speech cart voice chip
	ROM_LOAD( "toshiba_t7984", 0xc000, 0x2000, NO_DUMP ) // unknown - possible MCU
ROM_END

} // anonymous namespace


//**************************************************************************
//  GAME DRIVERS
//**************************************************************************

COMP( 1986, learnwin,  0,        0, learnwin, learnwin, learnwin_state, empty_init, "VTech", "Learning-Window Teaching Machine (Rev 3)",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1986, learnwin2, learnwin, 0, learnwin, learnwin, learnwin_state, empty_init, "VTech", "Learning-Window Teaching Machine (Rev 2)",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1986, learnwin1, learnwin, 0, learnwin, learnwin, learnwin_state, empty_init, "VTech", "Learning-Window Teaching Machine (Rev 1)",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1986, learnwinf, learnwin, 0, learnwin, learnwin, learnwin_state, empty_init, "VTech", "Learning-Window Teaching Machine (French)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1986, spielmast, learnwin, 0, learnwin, learnwin, learnwin_state, empty_init, "Yuvo",  "Spiel Master (German)",                     MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



lee1214.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Lee Data 1214 terminal (IBM 3278-compatible).

****************************************************************************/

#include "emu.h"
#include "cpu/i86/i186.h"
#include "machine/eeprompar.h"
#include "machine/z80sio.h"
//#include "video/mc6845.h"
//#include "screen.h"


namespace {

class lee1214_state : public driver_device
{
public:
	lee1214_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void lee1214(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	[[maybe_unused]] u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	required_device<cpu_device> m_maincpu;
};

void lee1214_state::mem_map(address_map &map)
{
	map(0x00000, 0x007ff).ram();
	map(0x80000, 0x80fff).ram();
	map(0xa0000, 0xa1fff).ram();
	map(0xf8000, 0xfffff).rom().region("maincpu", 0);
}

void lee1214_state::io_map(address_map &map)
{
	map(0x0000, 0x0003).rw("mpsc", FUNC(i8274_device::cd_ba_r), FUNC(i8274_device::cd_ba_w));
	//map(0x0100, 0x0100).w("crtc", FUNC(mc6845_device::address_w));
	//map(0x0101, 0x0101).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
}

void lee1214_state::machine_start()
{
}

u32 lee1214_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

static INPUT_PORTS_START(lee1214)
INPUT_PORTS_END

void lee1214_state::lee1214(machine_config &config)
{
	I80188(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &lee1214_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &lee1214_state::io_map);

	i8274_device &mpsc(I8274(config, "mpsc", 4'000'000));
	mpsc.out_int_callback().set("maincpu", FUNC(i80188_cpu_device::int0_w));

	EEPROM_2816(config, "eeprom");
}

ROM_START(lee1214d) // X1 = 12 MHz, X2 = 41.028 MHz, X3 = 24.823 MHz, X5 = 5.9335 MHz
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD("13311502_u41_27256.bin", 0x0000, 0x8000, CRC(6f469221) SHA1(deef1d83c41fdbe09b830dc45698988cf89003e0))

	ROM_REGION(0x0800, "eeprom", 0)
	ROM_LOAD("13300001_u19_2816.bin", 0x0000, 0x0800, CRC(30411ecd) SHA1(8755a1e0a36fe96d438bf2ee35cb0917fbc97e52))
ROM_END

} // anonymous namespace


COMP(1985, lee1214d, 0, 0, lee1214, lee1214, lee1214_state, empty_init, "Lee Data", "1214D Display Terminal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



lee1220.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Lee Data 1220 terminal (IBM 3278-compatible).

****************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/am9517a.h"
#include "machine/i8251.h"
#include "machine/pit8253.h"
//#include "machine/wd1933.h"
#include "video/mc6845.h"
#include "screen.h"

#include <algorithm>


namespace {

class lee1220_state : public driver_device
{
public:
	lee1220_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_crtc(*this, "crtc")
	{
	}

	void lee1220(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	MC6845_UPDATE_ROW(update_row);

	u8 sdlc_r(offs_t offset);
	void sdlc_w(offs_t offset, u8 data);
	u8 c0_r();

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	//required_device<wd1933_device> m_sdlc;
	required_device<hd6845s_device> m_crtc;

	u8 m_sdlc_reg[6];
};

void lee1220_state::machine_start()
{
	std::fill_n(&m_sdlc_reg[0], 6, 0);
	save_item(NAME(m_sdlc_reg));
}

MC6845_UPDATE_ROW(lee1220_state::update_row)
{
}


u8 lee1220_state::sdlc_r(offs_t offset)
{
	return m_sdlc_reg[offset]; //m_sdlc->read(~offset & 7);
}

void lee1220_state::sdlc_w(offs_t offset, u8 data)
{
	logerror("%s: Writing %02X to WD1933 register %d\n", machine().describe_context(), data, offset);
	m_sdlc_reg[offset] = data;
	//m_sdlc->write(~offset & 7, ~data);
}

u8 lee1220_state::c0_r()
{
	return 0;
}

void lee1220_state::mem_map(address_map &map)
{
	map(0x0000, 0x2fff).rom().region("program", 0);
	map(0x4000, 0x4fff).ram();
	map(0x7800, 0x7fff).ram();
	map(0x8000, 0xbfff).ram();
}

void lee1220_state::io_map(address_map &map)
{
	map(0x80, 0x8f).rw("dmac", FUNC(am9517a_device::read), FUNC(am9517a_device::write));
	map(0x90, 0x95).rw(FUNC(lee1220_state::sdlc_r), FUNC(lee1220_state::sdlc_w));
	map(0xa0, 0xa0).w("crtc", FUNC(hd6845s_device::address_w));
	map(0xa1, 0xa1).w("crtc", FUNC(hd6845s_device::register_w));
	map(0xb0, 0xb1).rw("usart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xc0, 0xc0).r(FUNC(lee1220_state::c0_r));
	map(0xd0, 0xd3).w("pit", FUNC(pit8253_device::write));
}

static INPUT_PORTS_START(lee1220)
INPUT_PORTS_END

void lee1220_state::lee1220(machine_config &config)
{
	I8085A(config, m_maincpu, 10'000'000); // Intel P8085AH-2
	m_maincpu->set_addrmap(AS_PROGRAM, &lee1220_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &lee1220_state::io_map);

	AM9517A(config, "dmac", 5'000'000); // Intel P8237A-5

	I8748(config, "kbdmcu", 5.9904_MHz_XTAL);

	//WD1933(config, m_sdlc); // WDC WD1933PL-11

	i8251_device &usart(I8251(config, "usart", 0)); // NEC D8251AC
	usart.rxrdy_handler().set_inputline(m_maincpu, I8085_RST55_LINE);

	PIT8253(config, "pit"); // Intel D8253-5

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_raw(28'944'000, 1152, 0, 960, 420, 0, 400);
	screen.set_screen_update(m_crtc, FUNC(hd6845s_device::screen_update));

	HD6845S(config, m_crtc, 2'412'000); // Hitachi HD46505SP/HD6845SP
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(12);
	m_crtc->set_update_row_callback(FUNC(lee1220_state::update_row));
}

// Main board XTALs: X1 = 28.944 MHz, X2 = 47.46816 MHz
// TTL daughterboard XTAL: X1 = 5.0688 MHz
// Keyboard XTAL: #20 = 5.9904 MHz
ROM_START(lee1220)
	ROM_REGION(0x3000, "program", 0)
	ROM_LOAD("13260303.u21", 0x0000, 0x1000, CRC(28692f78) SHA1(60957e773cb4350d82b02db8f741bd32a43365f1))
	ROM_LOAD("13260403.u22", 0x1000, 0x1000, CRC(3c54a251) SHA1(8d2849a2590be5cf765d26ff80a170b263a4b080))
	ROM_LOAD("13260503.u23", 0x2000, 0x1000, CRC(0872e674) SHA1(24d07a2cfccf03e90334daea810fe0e7c533e5ec))

	ROM_REGION(0x400, "kbdmcu", 0)
	ROM_LOAD("03278_d8748.2", 0x000, 0x400, CRC(a63ce4d8) SHA1(a713f3aae5e9096a627fab13573eee2170b42b1a))
ROM_END

} // anonymous namespace


COMP(1983, lee1220, 0, 0, lee1220, lee1220, lee1220_state, empty_init, "Lee Data", "1220 Display Terminal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



leonardo.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

SciSys Kasparov Leonardo, Saitek Kasparov Galileo.

This is SciSys's answer to H+G Mephisto modular chesscomputers, but unlike the
Mephistos, these boards are actual chesscomputers and not an accessory.

NOTE: In order for NVRAM to work properly, press the STOP button to turn off
the chesscomputer before exiting MAME. Other than ACL (which is an unemulated
hardware button that disconnects the battery), there is no known method to
force a cold boot. So if NVRAM somehow becomes broken, remove the NVRAM files.

They called the expansion capability "OSA", for "Open Systems Architecture".
A serial port for linking to a PC or homecomputer, and a parallel port for
expansion modules. The expansion modules are basically entire chesscomputers,
making the whole thing combined a 'dual brain' chesscomputer. The embedded chess
engine is by Julio Kaplan and Craig Barnes, same as the one in Turbo S-24K.

OSA serial link transmission format: 1 start bit, 8 data bits, 1 stop bit, no
parity. The default baudrate is 1200. To establish a connection, command "o" must
be entered first (from eg. a terminal), and then the Comm. LED will turn on.

Hardware notes:

Leonardo (1986):
- HD6301Y0P MCU @ 12MHz
- 32KB ROM(27C256)
- 8KB RAM(M5M5165P-15 or compatible)
- magnet sensors chessboard with 16 leds

The 6301Y0 was seen with internal maskrom serial A96 and B40. It is actually
one from another SciSys chesscomputer (Turbo 16K or Turbo S-24K). It appears to
be running in mode 1 (expanded mode): the internal ROM is disabled and the MCU
can be emulated as if it's a 6303Y.

Galileo (1988):
- HD6303YP MCU @ 12MHz
- almost same as Leonardo

Galileo PCB is different, but essentially it's the same hardware as Leonardo.
The 1.4 ROM is identical to it too, even though it's a different MCU type.
And on the outside, the button panel was redesigned a bit.

Known expansion modules:
- Maestro (65C02, Kaplan/Barnes)
- Analyst (65C02, Kaplan/Barnes)
- Brute Force (prototype) (65C02?, Ulf Rathsman)
- Brute Force (H8, Frans Morsch)
- Sparc (SPARClite, Spracklen's)

The H8 Brute Force module doesn't work with the 1st program version of Leonardo,
this is mentioned in the repair manual and it says it requires an EPROM upgrade.
The Sparc module doesn't appear to work with it either. Moreover, the Sparc module
manual mentions that for it to work properly on Leonardo, the chesscomputer needs
to be upgraded with an EMI PCB (power supply related, meaningless for emulation).

*******************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "bus/saitek_osa/expansion.h"
#include "cpu/m6800/m6801.h"
#include "machine/input_merger.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "saitek_galileo.lh"
#include "saitek_leonardo.lh"


namespace {

class leo_state : public driver_device
{
public:
	leo_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_expansion(*this, "exp"),
		m_stb(*this, "stb"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_rs232(*this, "rs232"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(go_button);

	void leonardo(machine_config &config);
	void leonardoa(machine_config &config);
	void galileo(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<hd6303y_cpu_device> m_maincpu;
	required_device<saitekosa_expansion_device> m_expansion;
	required_device<input_merger_device> m_stb;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_device<rs232_port_device> m_rs232;
	required_ioport_array<9> m_inputs;

	int m_ack_state = 0;
	int m_rts_state = 0;
	u8 m_inp_mux = 0;
	u8 m_led_data[2] = { };

	void main_map(address_map &map) ATTR_COLD;

	void update_display();
	void mux_w(u8 data);
	void leds_w(u8 data);
	u8 p1_r();
	void p1_w(u8 data);
	void exp_rts_w(int state);

	u8 p2_r();
	void p2_w(u8 data);
	u8 p6_r();
	void p5_w(u8 data);
	u8 p5_r();
	void p6_w(u8 data);
};

void leo_state::machine_start()
{
	save_item(NAME(m_ack_state));
	save_item(NAME(m_rts_state));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_led_data));
}



/*******************************************************************************
    I/O
*******************************************************************************/

// power

void leo_state::machine_reset()
{
	m_stb->in_clear<0>();
}

INPUT_CHANGED_MEMBER(leo_state::go_button)
{
	if (newval && m_maincpu->standby())
	{
		m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
		machine_reset();
	}
}


// misc

void leo_state::update_display()
{
	m_display->matrix_partial(0, 8, 1 << (m_inp_mux & 0xf), m_led_data[0]);
	m_display->matrix_partial(8, 2, 1 << BIT(m_inp_mux, 5), (m_inp_mux << 2 & 0x300) | m_led_data[1]);
}

void leo_state::mux_w(u8 data)
{
	// d0-d3: input/chessboard led mux
	// d5: button led select
	// d6,d7: button led data
	m_inp_mux = data ^ 0xc0;
	update_display();

	// d4: speaker out
	m_dac->write(BIT(data, 4));
}

void leo_state::leds_w(u8 data)
{
	// d0-d7: button led data
	m_led_data[1] = ~data;
	update_display();
}

u8 leo_state::p1_r()
{
	// ? this is where 6301 port 1 is, but 6303 doesn't have port 1
	return 0xff;
}

void leo_state::p1_w(u8 data)
{
	// ? " (toggles bit 0)
}

void leo_state::exp_rts_w(int state)
{
	// recursive NAND with ACK-P
	if (state && m_ack_state)
		m_expansion->ack_w(m_ack_state);
	m_rts_state = state;
}


// MCU ports

u8 leo_state::p2_r()
{
	u8 data = 0;

	// P20-P22: multiplexed inputs
	if ((m_inp_mux & 0xf) <= 8)
		data = m_inputs[m_inp_mux & 0xf]->read();

	// P23: serial rx
	data |= m_rs232->rxd_r() << 3;

	return ~data ^ 8;
}

void leo_state::p2_w(u8 data)
{
	// P24: serial tx (TTL)
	m_rs232->write_txd(BIT(data, 4));

	// P25,P26: chessboard led column data
	m_led_data[0] = (m_led_data[0] & ~3) | (~data >> 5 & 3);
	update_display();
}

u8 leo_state::p5_r()
{
	// P51: N/C, d4: IS strobe (handled with inputline)
	return 0xff ^ 0x10;
}

void leo_state::p5_w(u8 data)
{
	// P52: expansion NMI-P
	m_expansion->nmi_w(BIT(data, 2));

	// P53: NAND with STB-P
	m_stb->in_w<1>(BIT(data, 3));

	// P55: expansion ACK-P (recursive NAND with RTS-P)
	int ack_state = BIT(data, 5);
	if (m_rts_state || !ack_state)
		m_expansion->ack_w(ack_state);
	m_ack_state = ack_state;

	// P56,P57: chessboard led row data
	m_led_data[0] = (m_led_data[0] & 3) | (~data >> 4 & 0xc);
	update_display();

	// P50: power-off on falling edge
	m_expansion->pw_w(data & 1);
}

u8 leo_state::p6_r()
{
	// P60-P67: read chessboard sensors and module data
	return ~m_board->read_file(m_inp_mux & 0xf) & m_expansion->data_r();
}

void leo_state::p6_w(u8 data)
{
	// P60-P67: module data
	m_expansion->data_w(data);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void leo_state::main_map(address_map &map)
{
	map(0x0002, 0x0002).rw(FUNC(leo_state::p1_r), FUNC(leo_state::p1_w)); // external
	map(0x4000, 0x5fff).ram().share("nvram");
	map(0x6000, 0x6000).mirror(0x0fff).w(FUNC(leo_state::mux_w));
	map(0x7000, 0x7000).mirror(0x0fff).w(FUNC(leo_state::leds_w));
	map(0x8000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( leonardo )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Tab / Color")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_CODE(KEYCODE_EQUALS) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_CUSTOM) // freq sel
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Function")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Sound")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_CUSTOM) // freq sel
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_J) PORT_NAME("Stop")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Library")

	PORT_START("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Info")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Play")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_L) PORT_NAME("Level")

	PORT_START("IN.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Normal")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Analysis")

	PORT_START("IN.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_CUSTOM) // freq sel
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Set Up")

	PORT_START("IN.8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_CONFNAME( 0x04, 0x04, "Battery Status" )
	PORT_CONFSETTING(    0x00, "Low" )
	PORT_CONFSETTING(    0x04, DEF_STR( Normal ) )

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(leo_state::go_button), 0) PORT_NAME("Go")
INPUT_PORTS_END

static INPUT_PORTS_START( galileo ) // same buttons, but different locations
	PORT_INCLUDE( leonardo )

	PORT_MODIFY("IN.2")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Tab / Color")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_EQUALS) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")

	PORT_MODIFY("IN.3")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Function")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Sound")

	PORT_MODIFY("IN.4")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("Stop")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Library")

	PORT_MODIFY("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Info")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Play")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_L) PORT_NAME("Level")

	PORT_MODIFY("IN.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Normal")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Analysis")

	PORT_MODIFY("IN.7")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("Set Up")

	PORT_MODIFY("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(leo_state::go_button), 0) PORT_NAME("Go")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void leo_state::leonardo(machine_config &config)
{
	// basic machine hardware
	HD6303Y(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &leo_state::main_map);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(hd6303y_cpu_device::nvram_set_battery));
	m_maincpu->standby_cb().append([this](int state) { if (state) m_display->clear(); });
	m_maincpu->in_p2_cb().set(FUNC(leo_state::p2_r));
	m_maincpu->out_p2_cb().set(FUNC(leo_state::p2_w));
	m_maincpu->in_p5_cb().set(FUNC(leo_state::p5_r));
	m_maincpu->out_p5_cb().set(FUNC(leo_state::p5_w));
	m_maincpu->in_p6_cb().set(FUNC(leo_state::p6_r));
	m_maincpu->out_p6_cb().set(FUNC(leo_state::p6_w));

	INPUT_MERGER_ANY_LOW(config, m_stb);
	m_stb->output_handler().set_inputline(m_maincpu, M6801_IS3_LINE);

	config.set_maximum_quantum(attotime::from_hz(6000));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8+2, 8+2);
	config.set_default_layout(layout_saitek_leonardo);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);

	// expansion module (configure after video)
	SAITEKOSA_EXPANSION(config, m_expansion, saitekosa_expansion_modules);
	m_expansion->stb_handler().set(m_stb, FUNC(input_merger_device::in_w<0>));
	m_expansion->rts_handler().set(FUNC(leo_state::exp_rts_w));

	// rs232 (configure after expansion module)
	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
}

void leo_state::leonardoa(machine_config &config)
{
	leonardo(config);
	m_board->set_delay(attotime::from_msec(250)); // slower chessboard response?
}

void leo_state::galileo(machine_config &config)
{
	leonardo(config);
	config.set_default_layout(layout_saitek_galileo);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( leonardo ) // OSA version string: Version 1.4
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("sw6.1_osa1.4_510.u9", 0x8000, 0x8000, CRC(e39676b2) SHA1(288c5f2608277cb4c3ca71cb2e642a6a62c01dca) ) // D27C256AD-12
ROM_END

ROM_START( leonardoa ) // OSA version string: Leonardo Chess System - Version 1.2
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("sx6_617l_osa1.2.u9", 0x8000, 0x8000, CRC(4620f827) SHA1(4ae566646d032dd5bcca48316dd90a11e06772f1) ) // D27C256AD-12
ROM_END

ROM_START( leonardob ) // OSA version string: Leonardo Chess System - Version 1.0
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("sw6-830b.u9", 0x8000, 0x8000, CRC(dc892c1b) SHA1(5f7a92080a4062e1de61c7273a2fd0cfd9ede9f3) ) // D27C256AD-15
ROM_END

ROM_START( galileo ) // OSA version string: Version 1.4
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("sw6.1_osa1.4_510.u9", 0x8000, 0x8000, CRC(e39676b2) SHA1(288c5f2608277cb4c3ca71cb2e642a6a62c01dca) ) // MBM27C256H-10
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT    COMPAT  MACHINE    INPUT      CLASS      INIT        COMPANY, FULLNAME, FLAGS
SYST( 1986, leonardo,  0,        0,      leonardo,  leonardo,  leo_state, empty_init, "SciSys / Heuristic Software", "Kasparov Leonardo (v1.4)", MACHINE_SUPPORTS_SAVE )
SYST( 1986, leonardoa, leonardo, 0,      leonardoa, leonardo,  leo_state, empty_init, "SciSys / Heuristic Software", "Kasparov Leonardo (v1.2)", MACHINE_SUPPORTS_SAVE )
SYST( 1986, leonardob, leonardo, 0,      leonardoa, leonardo,  leo_state, empty_init, "SciSys / Heuristic Software", "Kasparov Leonardo (v1.0)", MACHINE_SUPPORTS_SAVE )

SYST( 1988, galileo,   leonardo, 0,      galileo,   galileo,   leo_state, empty_init, "Saitek / Heuristic Software", "Kasparov Galileo (v1.4)", MACHINE_SUPPORTS_SAVE )



lft.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/******************************************************************************************

2013-09-09 Skeleton of LFT computer system.

These are the monitor programs for 80186-based "S100+" systems by L/F Technologies. Many
of these boards were distributed under the IMS International name. Documentation available
online is woefully inadequate.

LFT1230 does extensive testing of devices at boot time. As they are mostly missing,
it gets caught in a loop. There's unknown devices in the i/o 008x, 00Ax, 00Cx range.
The devices at 008x and 00Cx would appear to be the same type.
BUG:If the rtc fails to clear the interrupt pin (first test), it cannot print the error
as the SCC hasn't yet been set up.
***Status: hangs at start


LFT1510 doesn't bother with the tests. The i/o ports seem to be the same.
***Status: mostly working


Note: Backspace/delete performs oddly.

******************************************************************************************/

#include "emu.h"
#include "cpu/i86/i186.h"
#include "machine/mm58167.h"
#include "machine/z80scc.h"
#include "bus/rs232/rs232.h"
//#include "bus/s100/s100.h"


namespace {

class lft_state : public driver_device
{
public:
	lft_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rtc(*this, "rtc")
		, m_scc(*this, "scc")
	{ }

	void lft(machine_config &config);

private:
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	virtual void machine_reset() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_device<mm58167_device> m_rtc;
	required_device<scc8530_device> m_scc;
};

void lft_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x5ffff).ram();
	map(0xfc000, 0xfffff).rom().region("roms", 0);
}

void lft_state::io_map(address_map &map)
{
	map.unmap_value_high();
	//map.global_mask(0x3ff);
	//map(0x0080, 0x0087) // unknown device
	//map(0x00a0, 0x00a?) // unknown device
	//map(0x00c0, 0x00c7) // unknown device
	map(0x0100, 0x0107).rw(m_scc, FUNC(z80scc_device::dc_ab_r), FUNC(z80scc_device::dc_ab_w)).umask16(0x00ff);
	map(0x0180, 0x01bf).rw(m_rtc, FUNC(mm58167_device::read), FUNC(mm58167_device::write)).umask16(0x00ff);
	//map(0x0200, 0x0207) // unknown device
}


/* Input ports */
static INPUT_PORTS_START( lft )
INPUT_PORTS_END

void lft_state::machine_reset()
{
}

void lft_state::lft(machine_config &config)
{
	/* basic machine hardware */
	I80186(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &lft_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &lft_state::io_map);

	// Devices
	MM58167(config, m_rtc, 32.768_kHz_XTAL);

	SCC8530(config, m_scc, 4.9152_MHz_XTAL);
	m_scc->out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
	m_scc->out_dtra_callback().set("rs232a", FUNC(rs232_port_device::write_dtr));
	m_scc->out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_scc, FUNC(scc8530_device::rxa_w));
	rs232a.dcd_handler().set(m_scc, FUNC(scc8530_device::dcda_w));
	rs232a.cts_handler().set(m_scc, FUNC(scc8530_device::ctsa_w));

	m_scc->out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
	m_scc->out_dtrb_callback().set("rs232b", FUNC(rs232_port_device::write_dtr));
	m_scc->out_rtsb_callback().set("rs232b", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, "terminal"));
	rs232b.rxd_handler().set(m_scc, FUNC(scc8530_device::rxb_w));
	rs232b.dcd_handler().set(m_scc, FUNC(scc8530_device::dcdb_w));
	rs232b.cts_handler().set(m_scc, FUNC(scc8530_device::ctsb_w));
}


/* ROM definition */
ROM_START( lft1230 )
	ROM_REGION16_LE(0x4000, "roms", 0)
	ROM_LOAD16_BYTE( "1230lf29", 0x0000, 0x2000, CRC(11c87367) SHA1(0879650aa98e19a4e6ca7b6ee7874f81c9c8ccfa) )
	ROM_LOAD16_BYTE( "1230lf42", 0x0001, 0x2000, CRC(ab82b620) SHA1(8c7d93950703f348e5ce0f9e376d157dd6098c6a) )
ROM_END

ROM_START( lft1510 )
	ROM_REGION16_LE(0x4000, "roms", 0)
	ROM_LOAD16_BYTE( "1510lfev", 0x2000, 0x1000, CRC(47dbb290) SHA1(b557e9a54a30d9a16edfdef4a6b12a5393d30bf3) )
	ROM_IGNORE(0x1000)
	ROM_LOAD16_BYTE( "1510lfod", 0x2001, 0x1000, CRC(ba8c23fc) SHA1(d4b82f69fccd653b31e7bd05ee884b323ff0007b) )
	ROM_IGNORE(0x1000)
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY             FULLNAME                      FLAGS
COMP( 1986, lft1510, 0,       0,      lft,     lft,   lft_state, empty_init, "L/F Technologies", "A1510 186 User Processor",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1985, lft1230, lft1510, 0,      lft,     lft,   lft_state, empty_init, "L/F Technologies", "A1230 186 Master Processor", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



lft_chiptune.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/**********************************************************************

 ATmega88-based chiptune players by Linus Åkesson and kryo

**********************************************************************/

#include "emu.h"
#include "cpu/avr8/avr8.h"
#include "sound/dac.h"
#include "speaker.h"


namespace {

#define MASTER_CLOCK    8000000

class lft_chiptune_state : public driver_device
{
public:
	lft_chiptune_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_dac(*this, "dac")
	{
	}

	void chiptune(machine_config &config);

protected:
	void prg_map(address_map &map) ATTR_COLD;
	void data_map(address_map &map) ATTR_COLD;

	required_device<atmega88_device> m_maincpu;
	required_device<dac_byte_interface> m_dac;
};

//**************************************************************************
//  MEMORY
//**************************************************************************

void lft_chiptune_state::prg_map(address_map &map)
{
	map(0x0000, 0x1fff).rom();
}

void lft_chiptune_state::data_map(address_map &map)
{
	map(0x0100, 0x04ff).ram();
}

//**************************************************************************
//  MACHINE
//**************************************************************************

static INPUT_PORTS_START( empty_input )
INPUT_PORTS_END

void lft_chiptune_state::chiptune(machine_config &config)
{
	/* basic machine hardware */
	ATMEGA88(config, m_maincpu, MASTER_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &lft_chiptune_state::prg_map);
	m_maincpu->set_addrmap(AS_DATA, &lft_chiptune_state::data_map);
	m_maincpu->set_eeprom_tag("eeprom");
	m_maincpu->gpio_out<atmega88_device::GPIOD>().set(m_dac, FUNC(dac_8bit_r2r_device::write));

	/* sound hardware */
	SPEAKER(config, "avr8").front_center();
	DAC_8BIT_R2R(config, m_dac, 0).add_route(0, "avr8", 0.9);
}

ROM_START( powernin )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "powernin.bin", 0x0000, 0x2000, CRC(67458936) SHA1(26a86846a24dd974723a66bea6c22baf51c7bec9) )
	ROM_REGION( 0x200, "eeprom", 0 )
	ROM_LOAD( "eeprom.raw", 0x0000, 0x0200, CRC(bd7bc39f) SHA1(9d0ac37bb3ec8c95990fd37a962a17a95ce97aa0) )
ROM_END

ROM_START( hwchiptn )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "hwchiptn.bin", 0x0000, 0x2000, CRC(0706eda8) SHA1(df227467bf4901978493efccaef6c4dfc32d5e62) )
	ROM_REGION( 0x200, "eeprom", 0 )
	ROM_LOAD( "eeprom.raw", 0x0000, 0x0200, CRC(bd7bc39f) SHA1(9d0ac37bb3ec8c95990fd37a962a17a95ce97aa0) )
ROM_END

} // anonymous namespace


/*   YEAR  NAME      PARENT  COMPAT  MACHINE     INPUT        CLASS               INIT        COMPANY                 FULLNAME */
CONS(2007, hwchiptn, 0,      0,      chiptune,   empty_input, lft_chiptune_state, empty_init, u8"Linus Åkesson / kryo", "The Hardware Chiptune Project", 0)
CONS(2009, powernin, 0,      0,      chiptune,   empty_input, lft_chiptune_state, empty_init, u8"Linus Åkesson", "Power Ninja Action Challenge", 0)



lft_craft.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/**********************************************************************

 Craft, an ATmega88-based demo by Linus Åkesson

**********************************************************************/

#include "emu.h"
#include "cpu/avr8/avr8.h"
#include "sound/dac.h"
#include "screen.h"
#include "emupal.h"
#include "speaker.h"

namespace {

#define MASTER_CLOCK        20000000

#define VISIBLE_CYCLES      480
#define HSYNC_CYCLES        155
#define LINE_CYCLES         (VISIBLE_CYCLES + HSYNC_CYCLES)
#define VISIBLE_LINES       480
#define VSYNC_LINES         45
#define LINES_PER_FRAME     (VISIBLE_LINES + VSYNC_LINES)
#define CYCLES_PER_FRAME    (LINES_PER_FRAME * LINE_CYCLES)
#define PIXELS_PER_FRAME    (CYCLES_PER_FRAME)

class lft_craft_state : public driver_device
{
public:
	lft_craft_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_dac(*this, "dac")
		, m_screen(*this, "screen")
		, m_palette(*this, "palette")
	{
	}

	void craft(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void prg_map(address_map &map) ATTR_COLD;
	void data_map(address_map &map) ATTR_COLD;

	void port_b_w(uint8_t data);
	void port_c_w(uint8_t data);
	void port_d_w(uint8_t data);

	void init_palette(palette_device &palette) const;
	void video_update();
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	required_device<atmega88_device> m_maincpu;
	required_device<dac_byte_interface> m_dac;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;

	uint8_t m_gpio_b = 0;
	uint8_t m_gpio_c = 0;

	uint32_t m_last_cycles = 0;
	uint64_t m_frame_start_cycle = 0;

	uint8_t m_latched_color = 0;
	std::unique_ptr<uint8_t[]> m_pixels;
};

//**************************************************************************
//  GPIO
//**************************************************************************

void lft_craft_state::port_b_w(uint8_t data)
{
	const uint8_t old = m_gpio_b;
	m_gpio_b = data;
	const uint8_t changed = data ^ old;

	if (BIT(changed, 1) && BIT(data, 1))
	{
		m_frame_start_cycle = machine().time().as_ticks(MASTER_CLOCK);
		video_update();
	}
	if (BIT(changed, 3))
	{
		video_update();
		m_latched_color = (data & 0x08) ? (m_gpio_c & 0x3f) : 0x3f;
	}
}

void lft_craft_state::port_c_w(uint8_t data)
{
	m_gpio_c = data;
	video_update();
	m_latched_color = data;
}

void lft_craft_state::port_d_w(uint8_t data)
{
	m_dac->write((data & 0x02) | ((data & 0xf4) >> 2));
}

//**************************************************************************
//  MEMORY
//**************************************************************************

void lft_craft_state::prg_map(address_map &map)
{
	map(0x0000, 0x1fff).rom();
}

void lft_craft_state::data_map(address_map &map)
{
	map(0x0100, 0x04ff).ram();
}

//**************************************************************************
//  VIDEO
//**************************************************************************

void lft_craft_state::init_palette(palette_device &palette) const
{
	for (int i = 0; i < 0x40; i++)
	{
		uint8_t r = 0x55 * ((i & 0x30) >> 4);
		uint8_t g = 0x55 * ((i & 0x0c) >> 2);
		uint8_t b = 0x55 * (i & 0x03);
		palette.set_pen_color(i, rgb_t(r, g, b));
	}
}

void lft_craft_state::video_update()
{
	uint64_t cycles = machine().time().as_ticks(MASTER_CLOCK);
	uint32_t frame_cycles = (uint32_t)(cycles - m_frame_start_cycle);

	if (m_last_cycles < frame_cycles)
	{
		for (uint32_t pixidx = m_last_cycles; pixidx < frame_cycles && pixidx < PIXELS_PER_FRAME; pixidx++)
		{
			m_pixels[pixidx] = m_latched_color;
		}
	}

	m_last_cycles = frame_cycles;
}

uint32_t lft_craft_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const pen_t *pens = m_palette->pens();
	for(int y = 0; y < LINES_PER_FRAME; y++)
	{
		uint32_t *dst = &bitmap.pix(y);
		uint8_t *src = &m_pixels[y * LINE_CYCLES];
		for(int x = 0; x < LINE_CYCLES; x++)
		{
			*dst++ = pens[*src++];
		}
	}
	return 0;
}

//**************************************************************************
//  MACHINE
//**************************************************************************

static INPUT_PORTS_START( empty_input )
INPUT_PORTS_END

void lft_craft_state::machine_start()
{
	m_pixels = std::make_unique<uint8_t[]>(PIXELS_PER_FRAME);

	save_item(NAME(m_gpio_b));
	save_item(NAME(m_gpio_c));
	save_item(NAME(m_last_cycles));
	save_item(NAME(m_frame_start_cycle));

	save_item(NAME(m_latched_color));
	save_pointer(NAME(m_pixels), PIXELS_PER_FRAME);
}

void lft_craft_state::machine_reset()
{
	m_gpio_b = 0;
	m_gpio_c = 0;

	m_frame_start_cycle = 0;
	m_last_cycles = 0;

	m_latched_color = 0;
	memset(&m_pixels[0], 0, PIXELS_PER_FRAME);
}

void lft_craft_state::craft(machine_config &config)
{
	ATMEGA88(config, m_maincpu, MASTER_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &lft_craft_state::prg_map);
	m_maincpu->set_addrmap(AS_DATA, &lft_craft_state::data_map);
	m_maincpu->set_eeprom_tag("eeprom");
	m_maincpu->gpio_in<atmega88_device::GPIOB>().set([this]() { return m_gpio_b; });
	m_maincpu->gpio_in<atmega88_device::GPIOC>().set([this]() { return m_gpio_c; });
	m_maincpu->gpio_out<atmega88_device::GPIOB>().set(FUNC(lft_craft_state::port_b_w));
	m_maincpu->gpio_out<atmega88_device::GPIOC>().set(FUNC(lft_craft_state::port_c_w));
	m_maincpu->gpio_out<atmega88_device::GPIOD>().set(FUNC(lft_craft_state::port_d_w));

	PALETTE(config, m_palette, FUNC(lft_craft_state::init_palette), 64);
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(MASTER_CLOCK, 635, 47, 527, 525, 36, 516);
	m_screen->set_screen_update(FUNC(lft_craft_state::screen_update));

	SPEAKER(config, "avr8").front_center();

	DAC_6BIT_R2R(config, m_dac, 0).add_route(0, "avr8", 0.25); // pd1/pd2/pd4/pd5/pd6/pd7 + 2k(x7) + 1k(x5)
}

ROM_START( craft )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "craft.bin", 0x0000, 0x2000, CRC(2e6f9ad2) SHA1(75e495bf18395d74289ca7ee2649622fc4010457) )
	ROM_REGION( 0x200, "eeprom", 0 )
	ROM_LOAD( "eeprom.raw", 0x0000, 0x0200, CRC(e18a2af9) SHA1(81fc6f2d391edfd3244870214fac37929af0ac0c) )
ROM_END

} // anonymous namespace


/*   YEAR  NAME      PARENT  COMPAT  MACHINE     INPUT        CLASS            INIT        COMPANY            FULLNAME */
CONS(2008, craft,    0,      0,      craft,      empty_input, lft_craft_state, empty_init, u8"Linus Åkesson", "Craft", MACHINE_IMPERFECT_GRAPHICS)



lft_phasor.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/**********************************************************************

 Phasor, an ATmega88-based demo by Linus Åkesson

**********************************************************************/

#include "emu.h"
#include "cpu/avr8/avr8.h"
#include "sound/dac.h"
#include "screen.h"
#include "emupal.h"
#include "speaker.h"


namespace {

#define MASTER_CLOCK        17734470

#define SAMPLES_PER_FRAME    (355255)

class lft_phasor_state : public driver_device
{
public:
	lft_phasor_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_dac(*this, "dac")
		, m_screen(*this, "screen")
		, m_palette(*this, "palette")
	{
	}

	void phasor(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void prg_map(address_map &map) ATTR_COLD;
	void data_map(address_map &map) ATTR_COLD;

	void init_palette(palette_device &palette) const;

	void port_b_w(uint8_t data);

	void video_update();
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	required_device<atmega88_device> m_maincpu;
	required_device<dac_byte_interface> m_dac;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;

	uint8_t m_gpio_b = 0;

	uint64_t m_last_cycles = 0;
	uint64_t m_frame_start_cycle = 0;

	uint8_t m_latched_sample = 0;
	bool m_in_blanking = false;
	uint64_t m_blanking_start = 0;
	uint32_t m_sample_x = 0;
	uint32_t m_sample_y = 0;
	std::unique_ptr<uint8_t[]> m_samples;
};

//**************************************************************************
//  MEMORY
//**************************************************************************

void lft_phasor_state::prg_map(address_map &map)
{
	map(0x0000, 0x1fff).rom();
}

void lft_phasor_state::data_map(address_map &map)
{
	map(0x0100, 0x04ff).ram();
}

//**************************************************************************
//  GPIO
//**************************************************************************

void lft_phasor_state::port_b_w(uint8_t data)
{
	video_update();
	m_gpio_b = data;
}

//**************************************************************************
//  VIDEO
//**************************************************************************

void lft_phasor_state::init_palette(palette_device &palette) const
{
	for (int i = 0; i < 0x10; i++)
	{
		uint8_t gray = (uint8_t)i;
		gray |= gray << 4;
		palette.set_pen_color(i, rgb_t(gray, gray, gray));
	}
}

void lft_phasor_state::video_update()
{
	const uint64_t cycles = machine().time().as_ticks(MASTER_CLOCK);

	if (cycles == m_last_cycles)
		return;

	if (m_latched_sample == 0 && !m_in_blanking)
	{
		m_in_blanking = true;
		m_blanking_start = cycles;
	}
	else if (m_latched_sample != 0 && m_in_blanking)
	{
		m_in_blanking = false;
		const uint64_t blank_duration = cycles - m_blanking_start;
		if (blank_duration < 80) // Approximate length of hblank
		{
			m_sample_y++;
			m_sample_x = 0;
		}
		else
		{
			m_sample_y = 0;
			m_sample_x = 0;
			m_frame_start_cycle = machine().time().as_ticks(MASTER_CLOCK);
		}
	}

	if (m_last_cycles < cycles && !m_in_blanking)
	{
		const uint8_t shift = (m_gpio_b & 4);
		uint32_t sample_pix = m_sample_y * 1135 + m_sample_x;
		for (uint64_t idx = m_last_cycles; idx < cycles && sample_pix < SAMPLES_PER_FRAME; idx++)
		{
			m_samples[sample_pix++] = (m_latched_sample >> shift) & 0x0f;
			m_sample_x++;
		}
	}

	m_last_cycles = cycles;
}

uint32_t lft_phasor_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const pen_t *pens = m_palette->pens();
	for(int y = 0; y < 313; y++)
	{
		uint32_t *dst = &bitmap.pix(y);
		uint8_t *src = &m_samples[y * 1135];
		for(int x = 0; x < 1135; x++)
		{
			*dst++ = pens[*src++];
		}
	}
	return 0;
}

//**************************************************************************
//  MACHINE
//**************************************************************************

static INPUT_PORTS_START( empty_input )
INPUT_PORTS_END

void lft_phasor_state::machine_start()
{
	m_samples = std::make_unique<uint8_t[]>(SAMPLES_PER_FRAME);

	save_item(NAME(m_gpio_b));
	save_item(NAME(m_last_cycles));
	save_item(NAME(m_frame_start_cycle));

	save_item(NAME(m_latched_sample));
	save_item(NAME(m_in_blanking));
	save_item(NAME(m_blanking_start));
	save_item(NAME(m_sample_x));
	save_item(NAME(m_sample_y));
	save_pointer(NAME(m_samples), SAMPLES_PER_FRAME);
}

void lft_phasor_state::machine_reset()
{
	m_gpio_b = 0;

	m_frame_start_cycle = 0;
	m_last_cycles = 0;

	m_latched_sample = 0;
	m_in_blanking = true;
	m_blanking_start = 0;
	m_sample_x = 0;
	m_sample_y = 0;
	memset(&m_samples[0], 0, SAMPLES_PER_FRAME);
}

void lft_phasor_state::phasor(machine_config &config)
{
	ATMEGA88(config, m_maincpu, MASTER_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &lft_phasor_state::prg_map);
	m_maincpu->set_addrmap(AS_DATA, &lft_phasor_state::data_map);
	m_maincpu->set_eeprom_tag("eeprom");
	m_maincpu->gpio_in<atmega88_device::GPIOB>().set([this]() { return m_gpio_b; });
	m_maincpu->gpio_out<atmega88_device::GPIOB>().set(FUNC(lft_phasor_state::port_b_w));
	m_maincpu->gpio_out<atmega88_device::GPIOC>().set([this](uint8_t data) { m_dac->write(data & 0x3f); });
	m_maincpu->gpio_out<atmega88_device::GPIOD>().set([this](uint8_t data) { m_latched_sample = data; });

	PALETTE(config, m_palette, FUNC(lft_phasor_state::init_palette), 0x10);
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(MASTER_CLOCK, 1135, 0, 1064, 313, 6, 310);
	m_screen->set_screen_update(FUNC(lft_phasor_state::screen_update));

	SPEAKER(config, "avr8").front_center();

	DAC_6BIT_R2R(config, m_dac, 0).add_route(0, "avr8", 0.5);
}

ROM_START( phasor )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "phasor.bin", 0x0000, 0x2000, CRC(300ef49b) SHA1(36b26137f5e8359dc9c2b746621a98bdd6634d2f) )
	ROM_REGION( 0x200, "eeprom", 0 )
	ROM_LOAD( "eeprom.raw", 0x0000, 0x0200, CRC(49036547) SHA1(d98c4d02771e80499c56dd71ad3d07597102f9b7) )
ROM_END

} // anonymous namespace


/*   YEAR  NAME      PARENT  COMPAT  MACHINE     INPUT        CLASS             INIT        COMPANY            FULLNAME */
CONS(2010, phasor,   0,      0,      phasor,     empty_input, lft_phasor_state, empty_init, u8"Linus Åkesson", "Phasor", MACHINE_IMPERFECT_GRAPHICS)



lg-dvd.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
/*
  Rather skeleton driver for a LG GP40NW10 usb dvd writer.

  Main cpu, a MT1839, seems to be a mcs51 variant.
*/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"


namespace {

class lg_dvd_state : public driver_device
{
public:
	lg_dvd_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void lg(machine_config &config);

private:
	void lg_dvd_map(address_map &map) ATTR_COLD;

	required_device<i80c52_device> m_maincpu;
};

static INPUT_PORTS_START( lg )
INPUT_PORTS_END

void lg_dvd_state::lg_dvd_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("maincpu", 0);
}

void lg_dvd_state::lg(machine_config &config)
{
	I80C52(config, m_maincpu, XTAL(16'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &lg_dvd_state::lg_dvd_map);
}

ROM_START( lggp40 )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "bios0", "1.00" )
	ROMX_LOAD( "firm-1.00.bin", 0x000000, 0x100000, CRC(c7f24f3b) SHA1(c2ce96c02ab419fb7e0b38703cdaeeccb2b7f34b), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "bios1", "1.01" )
	ROMX_LOAD( "firm-1.01.bin", 0x000000, 0x100000, CRC(28820e0c) SHA1(c5f2c1e14e6cff2e57c5196cabcebfaaff7284ce), ROM_BIOS(1) )
ROM_END

} // anonymous namespace


SYST( 2011, lggp40, 0, 0, lg, lg, lg_dvd_state, empty_init, "LG", "GP40NW10 dvd writer", MACHINE_NOT_WORKING|MACHINE_NO_SOUND_HW )



librie.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:

/*

Skeleton driver for Sony Librie e-book readers.

  Librie EBR-1000 (PCB EBX-5003):
    __________________________________________________________________________
   |                    ________                                             |
   |                    M5M5V416C ________                                    \__
   |                    | RAM   | M5M5V416C                                     |
   |                    |_______| | RAM   |                                     |
   |                              |_______|                                     |
   |                   ___________                                              |
   |                   |         |                                              |
   |                   APOLLO 1.18                 SONY                        _|_
   |                   T6TW8XBG-001                B-LIBR01X           POWER->|___|
   |                   |_________|                 EBX-5003                     |
   |                           ______              1-860-998-14                _|__
   |                           |IC1611        ____                       USB->|___|
   |                           |FLASH|        |||||                             |
   |                           |_____|                                         _|___
   | ________    __________   __________                         AUDIO JACK ->|____|
   | | SONY  |   | IC1224  |  | IC1203  |                                       |
   | CXD3452GA   | FLASH   |  | FLASH   |                                       |
   | |_______|   |_________|  |_________|                                       |
   |                                                                            |
   |  ___      ____________       _________                                     |
   |  |=|    V54C3256164VBUT7     | MX-1   |                                    |
   |  |=|      |  RAM      |     MC9328MX1VN20           _______                |
   |  |=|      |___________|      DragonBall             |      |               |
   |  |=|                         |________|             |      |               |
   |  |=|                                                |IC1125|               |
   |  |=|                             ____________       |FLASH |               /
   |  |=|                           V54C3256164VBUT7     |______|              |
   |                 __________       |  RAM      |     _______                |
   |                 |||||||||||      |___________|     |      |   ________    |
   |__________________________________________________  |      |   |       |   |
                                                      | |IC1126|   |H8/3802|   |
                                                      | |FLASH |   |_______|   |
                                                      | |______|               |
                                                      |________________________|

   CXD3452GA is the MS I/F CONTROLLER, according to the PRS-500 Service Manual (also uses this chip).
*/

#include "emu.h"

#include "cpu/arm7/arm7.h"
#include "cpu/h8/h83002.h"

#include "screen.h"
#include "speaker.h"


namespace {

class librie_state : public driver_device
{
public:
	librie_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_mcu(*this, "mcu")
	{ }

	void ebx5003(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	required_device<h83002_device> m_mcu;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};


uint32_t librie_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}


static INPUT_PORTS_START( ebx5003 )
INPUT_PORTS_END

void librie_state::ebx5003(machine_config &config)
{
	ARM920T(config, m_maincpu, 66'000'000); // ARM920T core, unknown clock

	screen_device&screen(SCREEN(config, "screen", SCREEN_TYPE_LCD)); // Not LCD, but e-Ink, 800×600, 167 PPI, four-level greyscale
	screen.set_refresh_hz(60);
	screen.set_size(600, 800);
	screen.set_visarea(0, 600 - 1, 0, 800 - 1);
	screen.set_screen_update(FUNC(librie_state::screen_update));

	// TODO: Apollo 1.18 T6TW8XBG-001 e-Ink screen controller

	SPEAKER(config, "speaker").front_center();

	H83002(config, m_mcu, 66'000'000); // Actually a Hitachi H8/3802 HD6473802FP. Unknown clock
}


ROM_START( librie ) // TODO: factory-reset
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_LOAD( "02nor0.ic1203", 0x000000, 0x200000, CRC(b5b11902) SHA1(9df606009d0e526a5b968c7487c11cd49db2d222) )
	ROM_LOAD( "02nor1.ic1204", 0x200000, 0x200000, CRC(228c2618) SHA1(06c3b82a59fff27a8cc5cbb74e41898817c86fa3) )

	ROM_REGION( 0x3180000, "user", 0 )
	ROM_LOAD( "uca690dc.ic1106", 0x0000000, 0x2100000, CRC(8489e4e5) SHA1(944aae4e3e546569b070ed9f843f218260325ed8) )
	ROM_LOAD( "upb514ff.ic1107", 0x2100000, 0x1080000, CRC(64f62a30) SHA1(0bdb03f439554dd6a66b19225712091e5634b746) )

	ROM_REGION( 0x80000, "extra", 0 )
	ROM_LOAD( "c_29lv400.ic1611", 0x00000, 0x80000, NO_DUMP )

	ROM_REGION( 0x4000, "mcu", 0 ) // H8/3802 HD6473802FP
	ROM_LOAD( "v22.ic801", 0x0000, 0x4000, NO_DUMP ) // 16 kbytes ROM
ROM_END

} // anonymous namespace


SYST( 2004, librie, 0, 0, ebx5003, ebx5003, librie_state, empty_init, "Sony", "Librie EBR 1000", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



lilith.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************************

2017-10-27 Skeleton of Lilith, a Modula-2 single-user workstation.

CPU consists of various parts including AM2901 and AM2911.

************************************************************************************************************************************/

#include "emu.h"


namespace {

class lilith_state : public driver_device
{
public:
	lilith_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		//      , m_maincpu(*this, "maincpu")
	{ }

	void lilith(machine_config &config);
private:
	//  required_device<cpu_device> m_maincpu;
};

static INPUT_PORTS_START( lilith )
INPUT_PORTS_END

void lilith_state::lilith(machine_config &config)
{
}

ROM_START( lilith )
	ROM_REGION( 0x10000, "maincpu", 0 )
	// main?
	ROM_LOAD( "150-0001-01a.bin", 0x000000, 0x000100, CRC(2991dbc6) SHA1(89a4f9f4f45495547062efcb035fb87906410ab9) )
	ROM_LOAD( "150-0001-02a.bin", 0x000000, 0x000100, CRC(2be08757) SHA1(19ad7fbea742953c10ba3da652e560547bc92032) )
	ROM_LOAD( "150-0001-03a.bin", 0x000000, 0x000100, CRC(c2103f8a) SHA1(b970f4b05d74b6d2a6477247b7f50acd024452f4) )
	ROM_LOAD( "150-0001-04a.bin", 0x000000, 0x000100, CRC(d5315408) SHA1(a52d2e535bff141e9976b6684b0aaf5d0089f6e6) )
	ROM_LOAD( "150-0002-01a.bin", 0x000000, 0x000800, CRC(2235b7ba) SHA1(bca0843bc61ba39f16f582eda9aa6c2188b7115a) )
	ROM_LOAD( "150-0002-02a.bin", 0x000000, 0x000800, CRC(c56b64cf) SHA1(42d6b2a394e0360905b30231eaa1b257602fe696) )
	ROM_LOAD( "150-0002-03a.bin", 0x000000, 0x000800, CRC(2c3ac942) SHA1(08c6e0f307e7eec3cfe9a55ed0a8c8eab4bd2931) )
	ROM_LOAD( "150-0002-04a.bin", 0x000000, 0x000800, CRC(f297e2e0) SHA1(f323b5fda7b4ce13ce5da5267b5375e257806410) )
	ROM_LOAD( "150-0002-05a.bin", 0x000000, 0x000800, CRC(009a46d3) SHA1(20050a0cc0084b10f6892161cb43dc6be286423b) )
	// fdc?
	ROM_LOAD( "fd6502u1.bin", 0x000000, 0x000800, CRC(24ab745a) SHA1(6229f7d08e5a9adbba045ce3db232c704d87ba0d) )
	ROM_LOAD( "fd6502u2.bin", 0x000000, 0x000800, CRC(a8cffbc3) SHA1(384d56cdc7d383cf9f160086827b9e0fb310cfdf) )
	// proms?
	ROM_LOAD( "hortim.bin",   0x000000, 0x000800, CRC(4f9a596a) SHA1(b2cec32f6b7bc23e6f83065cb20e0eae55ecc53a) )
	ROM_LOAD( "pd_u38.bin",   0x000000, 0x000100, CRC(724bf954) SHA1(5c8554d3a0f341516fe050fe09ed8e232af7e89e) )
	ROM_LOAD( "rx_u20.bin",   0x000000, 0x000100, CRC(658b523e) SHA1(070b5bf424e9f1fc3bc3dfc6126501c25f3b9ef3) )
	ROM_LOAD( "tx_u23.bin",   0x000000, 0x000100, CRC(4c3e2e9f) SHA1(9a88a016f3f74a15bcb635dd73d2129512ca53e8) )
	ROM_LOAD( "vertim.bin",   0x000000, 0x000800, CRC(304a8714) SHA1(dcd3542b8722144b80a638dda0f6df740a92c941) )
	ROM_LOAD( "xx_u40.bin",   0x000000, 0x000100, CRC(434e3456) SHA1(7679b3841b105345582e9bda758a134a2578cfbd) )
	// keyboard
	ROM_LOAD( "kbd_eprom.bin", 0x000000, 0x000800, CRC(40fa5230) SHA1(473ca714959fc35aa2a0ab9310aee2aedffa2163) )
ROM_END

} // anonymous namespace


COMP( 1984, lilith, 0, 0, lilith, lilith, lilith_state, empty_init, "DISER", "Lilith", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



linndrum.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:m1macrophage

/*
The LinnDrum (unofficially also known as LM-2) is a digital drum machine. It
stores and plays digital samples (recordings of acoustic dumps), some of which
are processed by analog filters or other analog circuits.

The firmware runs on a Z80. It controls the UI (reads buttons, drives displays
and LEDs), synchronization with other devices, cassette I/O, and voice
triggering.

The LinnDrum has 10 voice cores, not including the "click" (metronome) and
"beep" sounds. Many of the voices have variations (loudness, pitch, sample
selection, decay time), bringing the number of possible sounds to 28. However,
end-users can only trigger 23 sounds, and can control mixing and panning
for 15 of them (some are grouped together). Each voice core can run
independently, for a max polyphony of 10. Only one variation per core can be
active at a time. The "click" and "beep" sounds can be played independently of
each other and the voice cores.

There are multiple voice architectures:

* The "mux drums" section consists of 8 voice cores. Bass, cabasa, tambourine,
  clap and cowbell (4K samples each), hi-hat (16K) and ride and crash cymbals
  (32K each). Voices other than the clap and cowbell can be attenuated, to
  create 2 loudness variations. The clap and cowbell always play at full volume.
  There is a single, time-multiplexed DAC used for all voices, and each voice
  has its own output. All 8 voices can be played simultaneously, but only a
  single loudness variation of each. The bass drum is post-processed by a
  CEM3320 VCF with a hardcoded envelope, the output of which is routed to the
  mixer and an individual output jack. The rest of the voices are sent to the
  mixer unfiltered, but their individual outputs have a ~15.9KHz RC LPF. The hat
  is post-processed by a CEM3360 VCA, which can operate in 2 variations: slow
  decay (open hat) and faster, user-controlled decay (closed hat). There's a
  single clock for all voices, which is tuned by a trimmer on the circuit board.

* The "snare / sidestick" section consists of a single voice core (and single
  DAC) which can either play the sidestick, or the snare voice (4K samples
  each). The two voices have individual outputs, and their volume and pan can be
  set independently. There are 4 possible loudness variations for each voice,
  though the end-user only has access to 3 for the snare and 1 for the
  sidestick. The voice core output is post-processed by a single-pole lowpass RC
  filter. The end-user can control the sample rate via a tuning knob.
  This section also includes the circuit for the "click" sound (metronome),
  which triggers pulses of fixed length, and the circuit for  the "beep" sound,
  which allows the firmware to generate pulses of arbitrary length.

* The "tom / conga" section is similar to the "snare / sidestick" one. It
  consists of a single voice core (and single DAC) which can either play the tom
  or the conga voice (8K samples each). There are 3 pitch variations for the
  tom, and 2 pitch variations for the conga. The end-user can set the 5 pitches
  using the corresponding tuning knobs. Each of those 5 variations has its own
  output, and can be mixed and panned independently, even though only a single
  one can be active at a time. All variations are post-processed by a CEM3320
  VCF with a hardcoded envelope.

The driver is based on the LinnDrum's service manual and schematics, and is
intended as an educational tool.

Reasons for MACHINE_IMPERFECT_SOUND:
* Missing a few sample checksums.
* Missing bass drum LPF and filter envelope.
* Missing tom / conga LPF and filter envelope.
* Linear, instead of tanh response for hi-hat VCA.

PCBoards:
* CPU board. 2 sections in schematics:
  * Computer.
  * Input/Output.
* DRM board. 4 sections in schematics:
  * MUX drums.
  * MUX drum output stage.
  * Snare/Sidestick.
  * Tom/Cga.
* MXR board.
* 5 VSR board (power supply).

Usage:

The driver includes an interactive layout. Make sure to enable plugins so that
sliders and knobs can be manipulated with the mouse. It is recommended to run
with a high sample rate.

Example:
./mame -window -samplerate 96000 -plugins -plugin layout linndrum
*/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/nvram.h"
#include "machine/output_latch.h"
#include "machine/rescap.h"
#include "machine/timer.h"
#include "sound/dac76.h"
#include "sound/flt_biquad.h"
#include "sound/flt_rc.h"
#include "sound/flt_vol.h"
#include "sound/mixer.h"
#include "sound/spkrdev.h"
#include "sound/va_eg.h"
#include "sound/va_vca.h"
#include "speaker.h"

#include "linn_linndrum.lh"

#define LOG_KEYBOARD         (1U << 1)
#define LOG_DEBOUNCE         (1U << 2)
#define LOG_TEMPO            (1U << 3)
#define LOG_TEMPO_CHANGE     (1U << 4)
#define LOG_STROBES          (1U << 5)
#define LOG_TAPE_SYNC_ENABLE (1U << 6)
#define LOG_MIX              (1U << 7)
#define LOG_PITCH            (1U << 8)
#define LOG_HAT_EG           (1U << 9)

#define VERBOSE (LOG_GENERAL)
//#define LOG_OUTPUT_FUNC osd_printf_info

#include "logmacro.h"

namespace {

enum mux_voices
{
	MV_TAMBOURINE = 0,
	MV_CABASA,
	MV_CLAP,
	MV_COWBELL,
	MV_BASS,
	MV_HAT,
	MV_RIDE,
	MV_CRASH,
	NUM_MUX_VOICES
};

// Names (excluding the TV_ prefix) match those in the schematics.
enum tom_voices
{
	TV_LOW_CONGA = 0,
	TV_HI_CONGA,
	TV_LOW_TOMS,
	TV_MID_TOMS,
	TV_HI_TOMS,
	NUM_TOM_VOICES
};

// Names (excluding the TK_ prefix) match those on the tuning UI.
enum tuning_knobs
{
	TK_SNARE = 0,
	TK_HI_TOMS,
	TK_MID_TOMS,
	TK_LO_TOMS,
	TK_HI_CONGAS,
	TK_LO_CONGAS,
	NUM_TUNING_KNOBS
};

// Names (excluding the MIX_ prefix) match those on the mixer UI.
enum mixer_channels
{
	MIX_BASS = 0,
	MIX_SNARE,
	MIX_SIDESTICK,
	MIX_HIHAT,
	MIX_HITOMS,
	MIX_MIDTOMS,
	MIX_LOTOMS,
	MIX_RIDE,
	MIX_CRASH,
	MIX_CABASA,
	MIX_TAMB,
	MIX_HICONGAS,
	MIX_LOCONGAS,
	MIX_COWBELL,
	MIX_CLAPS,
	MIX_CLICK,
	NUM_MIXER_CHANNELS
};

}  // anonymous namespace


class linndrum_audio_device : public device_t
{
public:
	linndrum_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0) ATTR_COLD;

	void mux_drum_w(int voice, u8 data, bool is_strobe = true);
	void snare_w(u8 data);  // Snare and sidestick.
	void tom_w(u8 data);  // Tom and conga.
	void strobe_click_w(u8 data);
	void beep_w(int state);

	DECLARE_INPUT_CHANGED_MEMBER(mix_changed);
	DECLARE_INPUT_CHANGED_MEMBER(master_volume_changed);
	DECLARE_INPUT_CHANGED_MEMBER(mux_drum_tuning_changed);
	DECLARE_INPUT_CHANGED_MEMBER(snare_tuning_changed);
	DECLARE_INPUT_CHANGED_MEMBER(tom_tuning_changed);
	DECLARE_INPUT_CHANGED_MEMBER(hat_decay_changed);

protected:
	void device_add_mconfig(machine_config &config) override ATTR_COLD;
	void device_start() override ATTR_COLD;
	void device_reset() override ATTR_COLD;

private:
	static void write_dac(dac76_device& dac, u8 sample);
	static s32 get_ls267_freq(const std::array<s32, 2>& freq_range_hz, float cv);
	static float get_snare_tom_pitch_cv(float v);

	TIMER_DEVICE_CALLBACK_MEMBER(hat_trigger_timer_tick);
	TIMER_DEVICE_CALLBACK_MEMBER(mux_timer_tick);
	TIMER_DEVICE_CALLBACK_MEMBER(snare_timer_tick);
	TIMER_DEVICE_CALLBACK_MEMBER(click_timer_tick);
	TIMER_DEVICE_CALLBACK_MEMBER(tom_timer_tick);

	void update_volume_and_pan(int channel);
	void update_master_volume();
	void update_mux_drum_pitch();
	void update_snare_pitch();
	void update_tom_pitch();
	void update_hat_decay();

	// Mux drums.
	required_ioport m_mux_tuning_trimmer;
	required_ioport m_hat_decay_pot;
	required_memory_region_array<NUM_MUX_VOICES> m_mux_samples;
	required_device<timer_device> m_mux_timer;  // 74LS627 (U77A).
	required_device_array<dac76_device, NUM_MUX_VOICES> m_mux_dac;  // AM6070 (U88).
	required_device_array<filter_volume_device, NUM_MUX_VOICES> m_mux_volume;  // CD4053 (U90), R60, R62.
	required_device<timer_device> m_hat_trigger_timer;  // U37B (LM556).
	required_device<va_rc_eg_device> m_hat_eg;
	required_device<va_vca_device> m_hat_vca;  // CEM3360 (U91B).
	bool m_hat_open = false;
	bool m_hat_triggered = false;
	std::array<bool, NUM_MUX_VOICES> m_mux_counting = { false, false, false, false, false, false, false, false };
	std::array<u16, NUM_MUX_VOICES> m_mux_counters = { 0, 0, 0, 0, 0, 0, 0, 0 };

	// Snare / sidestick.
	required_memory_region m_snare_samples;  // 2732 ROM (U79).
	required_memory_region m_sidestick_samples;  // 2732 ROMs (U78).
	required_device<timer_device> m_snare_timer;  // 74L627 (U80A).
	required_device<dac76_device> m_snare_dac;  // AM6070 (U92).
	required_device<filter_volume_device> m_snare_out;  // U90A (CD4053) pin 12 (ax).
	required_device<filter_volume_device> m_sidestick_out;  // U90A (CD4053) pin 13 (ay).
	required_device<timer_device> m_click_timer;  // 556 (U65A).
	required_device<speaker_sound_device> m_click;
	required_device<speaker_sound_device> m_beep;
	bool m_snare_counting = false;  // /Q1 of U41 (74LS74).
	u16 m_snare_counter = 0;  // 13-bit counter (2 x 74LS393, U61, U62).
	bool m_sidestick_selected = false;  // Chooses between snare and sidestick.

	// Tom / conga.
	required_ioport_array<NUM_TUNING_KNOBS> m_tuning_knobs;
	required_memory_region m_tom_samples;  // 2 x 2732 ROMs (U68, U69).
	required_memory_region m_conga_samples;  // 2 x 2732 ROMs (U66, U67).
	required_device<timer_device> m_tom_timer;  // 74LS627 (U77B).
	required_device<dac76_device> m_tom_dac;  // AM6070 (U82).
	required_device_array<filter_volume_device, NUM_TOM_VOICES> m_tom_out;  // U87 (CD4051) outputs 0, 1, 4, 5, 6.
	bool m_tom_counting = false;  // /Q1 of U73 (74LS74).
	u16 m_tom_counter = 0;  // 14-bit counter (2 x 74LS393, U70, U71).
	bool m_tom_selected = false;  // Selects between tom and conga.
	s8 m_tom_selected_pitch = 0;

	// Mixer.
	required_ioport_array<NUM_MIXER_CHANNELS> m_volume;
	required_ioport_array<NUM_MIXER_CHANNELS> m_pan;
	required_ioport m_master_volume;
	required_device_array<filter_rc_device, NUM_MIXER_CHANNELS - 1> m_voice_hpf;
	required_device<filter_biquad_device> m_click_bpf;
	required_device<mixer_device> m_left_mixer;  // 4558 op-amp (U1A).
	required_device<mixer_device> m_right_mixer;  // 4558 op-amp (U1B).
	required_device<speaker_device> m_out;        // 4558 op-amp (U2A, U2B).

	static constexpr const float MIXER_R_PRE_FADER[NUM_MIXER_CHANNELS] =
	{
		RES_R(0),    // bass
		RES_R(0),    // snare
		RES_R(0),    // sidestick
		RES_K(5.1),  // hihat
		RES_R(0),    // hi tom
		RES_R(0),    // mid tom
		RES_R(0),    // low tom
		RES_K(10),   // ride
		RES_K(5.1),  // crash
		RES_K(10),   // cabasa
		RES_K(10),   // tambourine
		RES_R(0),    // hi conga
		RES_R(0),    // low conga
		RES_K(5.1),  // cowbell
		RES_K(2.4),  // clap
		RES_R(0),    // click
	};

	static constexpr const float MIXER_R_FEEDBACK = RES_K(33);
	static constexpr const float MIXER_R_BEEP = RES_K(510);  // Same value for left and right.
	static constexpr const float OUTPUT_R_INPUT = RES_K(10);  // Input resistor of output stage opamp.
	static constexpr const float OUTPUT_R_FEEDBACK = RES_K(10);
	static constexpr const float OUTPUT_C_FEEDBACK = CAP_P(1000);
	static constexpr const float R_ON_CD4053 = RES_R(125);  // Typical Ron resistance for 15V supply (-7.5V / 7.5V).

	static constexpr const float VPLUS = 15;  // Volts.
	static constexpr const float VCC = 5;  // Volts.
	static constexpr const float MUX_DAC_IREF = VPLUS / (RES_K(15) + RES_K(15));  // R55 + R57.
	static constexpr const float TOM_DAC_IREF = MUX_DAC_IREF;  // Configured in the same way.
	// All DAC current-to-voltage converter resistors, for both positive and
	// negative values, are 2.49 KOhm.
	static constexpr const float R_DAC_I2V = RES_K(2.49);  // R58, R59, R127, R126, tom DAC I2V (missing designation).

	// Constants for hi hat envelope generator circuit.
	static constexpr const float HAT_C22 = CAP_U(1);
	static constexpr const float HAT_R33 = RES_M(1);
	static constexpr const float HAT_R34 = RES_K(10);
	static constexpr const float HAT_DECAY_POT_R_MAX = RES_K(100);
	static constexpr const float HAT_EG2CV_SCALER = RES_VOLTAGE_DIVIDER(RES_K(8.2), RES_K(10));  // R67, R66.

	// The audio pipeline operates on voltage magnitudes. This scaler normalizes
	// the final output's range to approximately: -1 - 1.
	static constexpr const float VOLTAGE_TO_SOUND_SCALER = 0.2F;

	// These frequency ranges were eyeballed from figure 6 of the 74LS627
	// datasheet: https://www.ti.com/product/SN74LS628 .
	static constexpr const std::array<s32, 2> MUX_TIMER_HZ_RANGE = { 250'000, 2'330'000 };  // C14: 330pF.
	static constexpr const std::array<s32, 2> SNARE_TIMER_HZ_RANGE = { 8'000, 80'000 };  // C111: 0.01uF.
	static constexpr const std::array<s32, 2> TOM_TIMER_HZ_RANGE = { 8'000, 80'000 };  // C?: 0.01uF.

	// CV input impedance. The datasheet provides typical and max currents,
	// given for 1V and 5V. These work out to 100K typical, and
	// 20K minimum impedances. Using the advertised typical value.
	static constexpr const float LS627_CV_INPUT_R = RES_K(100);

	static constexpr const char *MUX_VOICE_NAMES[NUM_MUX_VOICES] =
	{
		"TAMBOURINE", "CABASA", "CLAP", "COWBELL", "BASS", "HAT", "RIDE", "CRASH"
	};
	static constexpr const char *TOM_VOICE_NAMES[NUM_TOM_VOICES] =
	{
		"LOW CONGA", "HI CONGA", "LOW TOMS", "MID TOMS", "HI TOMS"
	};
	static constexpr const char *MIXER_CHANNEL_NAMES[NUM_MIXER_CHANNELS] =
	{
		"BASS", "SNARE", "SIDESTICK", "HIHAT", "HITOMS", "MIDTOMS", "LOTOMS",
		"RIDE", "CRASH", "CABASA", "TAMB", "HICONGAS", "LOCONGAS",
		"COWBELL", "CLAP", "CLICK"
	};
};

DEFINE_DEVICE_TYPE(LINNDRUM_AUDIO, linndrum_audio_device, "linndrum_audio_device", "LinnDrum audio circuits");

linndrum_audio_device::linndrum_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: device_t(mconfig, LINNDRUM_AUDIO, tag, owner, clock)
	, m_mux_tuning_trimmer(*this, ":pot_mux_tuning")
	, m_hat_decay_pot(*this, ":pot_hihat_decay")
	, m_mux_samples(*this, ":sample_mux_drum_%u", 0)
	, m_mux_timer(*this, "mux_drum_timer")
	, m_mux_dac(*this, "mux_drums_virtual_dac_%u", 1)
	, m_mux_volume(*this, "mux_drums_volume_control_%u", 1)
	, m_hat_trigger_timer(*this, "hat_trigger_timer")
	, m_hat_eg(*this, "hat_eg")
	, m_hat_vca(*this, "hat_vca")
	, m_snare_samples(*this, ":sample_snare")
	, m_sidestick_samples(*this, ":sample_sidestick")
	, m_snare_timer(*this, "snare_sidestick_timer")
	, m_snare_dac(*this, "snare_sidestick_dac")
	, m_snare_out(*this, "snare_output")
	, m_sidestick_out(*this, "sidestick_output")
	, m_click_timer(*this, "click_timer")
	, m_click(*this, "click")
	, m_beep(*this, "beep")
	, m_tuning_knobs(*this, ":pot_tuning_%u", 1)
	, m_tom_samples(*this, ":sample_tom")
	, m_conga_samples(*this, ":sample_conga")
	, m_tom_timer(*this, "tom_conga_timer")
	, m_tom_dac(*this, "tom_conga_dac")
	, m_tom_out(*this, "tom_conga_out_%u", 0)
	, m_volume(*this, ":pot_gain_%u", 1)
	, m_pan(*this, ":pot_pan_%u", 1)
	, m_master_volume(*this, ":pot_volume")
	, m_voice_hpf(*this, "voice_hpf_%u", 1)
	, m_click_bpf(*this, "click_bpf")
	, m_left_mixer(*this, "lmixer")
	, m_right_mixer(*this, "rmixer")
	, m_out(*this, "speaker")
{
}

void linndrum_audio_device::mux_drum_w(int voice, u8 data, bool is_strobe)
{
	assert(voice >= 0 && voice < NUM_MUX_VOICES);

	m_mux_counting[voice] = BIT(data, 0);
	if (!m_mux_counting[voice])
		m_mux_counters[voice] = 0;

	// Volume variations are controlled by a CD4053 MUX (U90B), whose "select"
	// input is connected to the active voice's D1 (via a 74LS151 encoder, U35).
	// Depending on how the mux is configured, the audio signal will either
	// remain unchanged, or it will get attenuated by a voltage divider.
	// The MUX is always configured in the "no attenuation" mode for the clap
	// and cowbell voices.
	static constexpr const float ATTENUATION = RES_VOLTAGE_DIVIDER(RES_K(10), RES_K(3.3) + R_ON_CD4053);  // R60, R62.
	const bool attenuate = !BIT(data, 1) && voice != MV_CLAP && voice != MV_COWBELL;
	m_mux_volume[voice]->set_gain(attenuate ? ATTENUATION : 1);

	if (voice == MV_HAT)
	{
		m_hat_open = BIT(data, 2);
		m_hat_triggered = is_strobe;
		update_hat_decay();
		if (is_strobe)
			m_hat_trigger_timer->adjust(PERIOD_OF_555_MONOSTABLE(RES_K(510), CAP_U(0.01)));  // R8, C4.
		LOGMASKED(LOG_HAT_EG, "Hat EG write: %x, %d.\n", data, is_strobe);
	}

	LOGMASKED(LOG_STROBES, "Strobed mux drum %s: %02x (gain: %f)\n",
			  MUX_VOICE_NAMES[voice], data, m_mux_volume[voice]->gain());
}

void linndrum_audio_device::snare_w(u8 data)
{
	m_snare_counting = BIT(data, 0);
	if (!m_snare_counting)
	{
		m_snare_counter = 0;
		write_dac(*m_snare_dac, 0);  // DAC is disabled. Output goes to 0.
	}

	m_sidestick_selected = BIT(data, 1);  // Play sidestick instead of snare.
	if (m_sidestick_selected)
	{
		m_snare_out->set_gain(0);
		m_sidestick_out->set_gain(1);
	}
	else
	{
		m_snare_out->set_gain(1);
		m_sidestick_out->set_gain(0);
	}

	// Snare and sidestick volume is set by controlling the reference current to
	// the DAC. Bits D2 and D3 from the voice data bus (latched by U42, 74LS74)
	// control the voltage at the end of R72 and R71.

	// While there is a capacitor (C29, 0.01 UF) attached to the DAC's Iref
	// input, it is too small to have a musical effect. Changes in current will
	// stablizie within 0.1 ms, and such changes only happen at voice trigger.

	static constexpr const float R0 = RES_K(3.3);  // R70.
	static constexpr const float R1 = RES_K(380);  // R69.
	static constexpr const float R2 = RES_K(22);   // R72.
	static constexpr const float R3 = RES_K(5.6);  // R71.

	static constexpr const float a = R0 * R2 * R3;
	static constexpr const float b = R0 * R1 * R3;
	static constexpr const float c = R0 * R1 * R2;
	static constexpr const float d = R1 * R2 * R3;

	// Compute DAC reference current.
	const float v2 = BIT(data, 2) ? VCC : 0;
	const float v3 = BIT(data, 3) ? VCC : 0;
	const float iref = (a * VPLUS + b * v2 + c * v3) / (R0 * (a + b + c + d));
	m_snare_dac->set_fixed_iref(iref);

	LOGMASKED(LOG_STROBES, "Strobed snare / sidestick: %02x (iref: %f)\n", data, iref);
}

void linndrum_audio_device::tom_w(u8 data)
{
	m_tom_counting = BIT(data, 0);
	if (!m_tom_counting)
	{
		m_tom_counter = 0;
		write_dac(*m_tom_dac, 0);  // DAC is disabled. Output goes to 0.
	}

	m_tom_selected = BIT(data, 1);  // Play tom instead of conga.
	// It is possible for neither the tom nor the conga ROM to be selected. This
	// can only happen when the voice is disabled, so it does not affect the
	// emulation.

	// Address for the pitch control MUX (U81) and output selection MUX (U87).
	// Both are CD4051s.
	const u8 variation = bitswap<3>(data, 1, 3, 2);  // MUX: C, B, A.

	m_tom_selected_pitch = -1;
	switch (variation)
	{
		case 0: m_tom_selected_pitch = TV_LOW_CONGA; break;
		case 1: m_tom_selected_pitch = TV_HI_CONGA; break;
		case 4: m_tom_selected_pitch = TV_LOW_TOMS; break;
		case 5: m_tom_selected_pitch = TV_MID_TOMS; break;
		case 6: m_tom_selected_pitch = TV_HI_TOMS; break;
		default: LOG("Firmware bug: invalid pitch variation for tom/conga.\n");
	}
	update_tom_pitch();

	// If the tom/conga voice is disabled, The INH input of the output MUX (U87)
	// will be high, and all outputs will be disconnected. That's in contrast to
	// the pitch MUX (U81), which is always enabled.
	// Disconnected outputs have their voltage pulled to 0 by a 10K resistor.
	const s8 selected_output = m_tom_counting ? m_tom_selected_pitch : -1;
	for (int i = 0; i < NUM_TOM_VOICES; ++i)
		m_tom_out[i]->set_gain((i == selected_output) ? 1 : 0);

	LOGMASKED(LOG_STROBES, "Strobed tom / conga: %02x (is_tom: %d, pitch:%d, output: %d, %s)\n",
			  data, m_tom_selected, m_tom_selected_pitch, selected_output,
			  (selected_output >= 0) ? TOM_VOICE_NAMES[selected_output] : "none");
}

void linndrum_audio_device::strobe_click_w(u8 /*data*/)
{
	m_click_timer->adjust(PERIOD_OF_555_MONOSTABLE(RES_K(100), CAP_U(0.01)));  // R10, C12.
	m_click->level_w(1);
	LOGMASKED(LOG_STROBES, "Strobed click.\n");
}

void linndrum_audio_device::beep_w(int state)
{
	// Beep signal is inverted by U76B (74LS00).
	m_beep->level_w(state ? 0 : 1);
	LOGMASKED(LOG_STROBES, "Beep: %d\n", state);
}

DECLARE_INPUT_CHANGED_MEMBER(linndrum_audio_device::mix_changed)
{
	update_volume_and_pan(param);
}

DECLARE_INPUT_CHANGED_MEMBER(linndrum_audio_device::master_volume_changed)
{
	update_master_volume();
}

DECLARE_INPUT_CHANGED_MEMBER(linndrum_audio_device::mux_drum_tuning_changed)
{
	update_mux_drum_pitch();
}

DECLARE_INPUT_CHANGED_MEMBER(linndrum_audio_device::snare_tuning_changed)
{
	update_snare_pitch();
}

DECLARE_INPUT_CHANGED_MEMBER(linndrum_audio_device::tom_tuning_changed)
{
	update_tom_pitch();
}

DECLARE_INPUT_CHANGED_MEMBER(linndrum_audio_device::hat_decay_changed)
{
	update_hat_decay();
}

void linndrum_audio_device::device_add_mconfig(machine_config &config)
{
	// *** Mux drums section.

	TIMER(config, m_mux_timer).configure_generic(FUNC(linndrum_audio_device::mux_timer_tick));  // 74LS627 (U77A).

	// The actual "mux drums" hardware has a single AM6070, which is
	// time-multiplexed across the 8 voices. Implementing it that way is
	// possible, but requires a sample rate of at least 240KHz (8 x ~30K) for
	// reasonable results. It also requires emulating audio sample & hold
	// functionality. So 8 "virtual" DACs and volume-control MUXes are used
	// instead.
	for (int voice = 0; voice < NUM_MUX_VOICES; ++voice)
	{
		DAC76(config, m_mux_dac[voice], 0);  // AM6070 (U88).
		m_mux_dac[voice]->configure_voltage_output(R_DAC_I2V, R_DAC_I2V);  // R58, R59.
		m_mux_dac[voice]->set_fixed_iref(MUX_DAC_IREF);
		FILTER_VOLUME(config, m_mux_volume[voice]);  // CD4053 (U90), R60, R62 (see mux_drum_w()).
		m_mux_dac[voice]->add_route(0, m_mux_volume[voice], 1.0);
	}

	TIMER(config, m_hat_trigger_timer).configure_generic(FUNC(linndrum_audio_device::hat_trigger_timer_tick));  // LM556 (U37B).
	VA_RC_EG(config, m_hat_eg).set_c(HAT_C22);
	VA_VCA(config, m_hat_vca).configure_cem3360_linear_cv();
	m_mux_volume[MV_HAT]->add_route(0, m_hat_vca, 1.0, 0);
	m_hat_eg->add_route(0, m_hat_vca, HAT_EG2CV_SCALER, 1);

	// *** Snare / sidestick section.

	TIMER(config, m_snare_timer).configure_generic(FUNC(linndrum_audio_device::snare_timer_tick));  // 74LS627 (U80A).
	DAC76(config, m_snare_dac, 0);  // AM6070 (U92)
	m_snare_dac->configure_voltage_output(R_DAC_I2V, R_DAC_I2V);  // R127, R126.

	// The DAC's current outputs are processed by a current-to-voltage converter
	// that embeds an RC filter. This consists of an op-amp (U103), R127 and C65
	// (for positive voltages), and R126 and C31 (for negative voltages). The
	// two resistors and capacitors have the same value.
	auto &snare_dac_filter = FILTER_RC(config, "snare_sidestick_dac_filter");
	snare_dac_filter.set_lowpass(R_DAC_I2V, CAP_P(2700));  // R127-C65, R126-C31. Cutoff: ~23.7KHz.
	m_snare_dac->add_route(0, snare_dac_filter, 1.0);

	FILTER_VOLUME(config, m_snare_out);
	FILTER_VOLUME(config, m_sidestick_out);
	snare_dac_filter.add_route(0, m_snare_out, 1.0);
	snare_dac_filter.add_route(0, m_sidestick_out, 1.0);

	TIMER(config, m_click_timer).configure_generic(FUNC(linndrum_audio_device::click_timer_tick));  // 556 (U65A).
	static const double LEVELS[2] = { 0, VCC };
	SPEAKER_SOUND(config, m_click).set_levels(2, LEVELS);
	SPEAKER_SOUND(config, m_beep).set_levels(2, LEVELS);

	// *** Tom / conga section.

	TIMER(config, m_tom_timer).configure_generic(FUNC(linndrum_audio_device::tom_timer_tick));  // 74LS627 (U77B).
	DAC76(config, m_tom_dac, 0);  // AM6070 (U82).
	// Schematic is missing the second resistor, but that's almost certainly an error.
	// It is also missing component designations.
	m_tom_dac->configure_voltage_output(R_DAC_I2V, R_DAC_I2V);
	m_tom_dac->set_fixed_iref(TOM_DAC_IREF);
	for (int i = 0; i < NUM_TOM_VOICES; ++i)
	{
		FILTER_VOLUME(config, m_tom_out[i]);  // One of U87'S (CD4051) outputs.
		m_tom_dac->add_route(0, m_tom_out[i], 1.0);
	}

	// *** Mixer.
	const std::array<device_sound_interface *, NUM_MIXER_CHANNELS> voice_outputs =
	{
		m_mux_volume[MV_BASS],
		m_snare_out,
		m_sidestick_out,
		m_hat_vca,
		m_tom_out[TV_HI_TOMS],
		m_tom_out[TV_MID_TOMS],
		m_tom_out[TV_LOW_TOMS],
		m_mux_volume[MV_RIDE],
		m_mux_volume[MV_CRASH],
		m_mux_volume[MV_CABASA],
		m_mux_volume[MV_TAMBOURINE],
		m_tom_out[TV_HI_CONGA],
		m_tom_out[TV_LOW_CONGA],
		m_mux_volume[MV_COWBELL],
		m_mux_volume[MV_CLAP],
		m_click,
	};

	MIXER(config, m_left_mixer);  // U1A
	MIXER(config, m_right_mixer);  // U1B

	assert(voice_outputs.size() - 1 == MIX_CLICK);
	for (int i = 0; i < voice_outputs.size() - 1; ++i)  // Skip "click".
	{
		// The filter and gain will be configured in update_volume_and_pan().
		FILTER_RC(config, m_voice_hpf[i]);
		voice_outputs[i]->add_route(0, m_voice_hpf[i], 1.0);
		m_voice_hpf[i]->add_route(0, m_left_mixer, 1.0);
		m_voice_hpf[i]->add_route(0, m_right_mixer, 1.0);
	}

	FILTER_BIQUAD(config, m_click_bpf);  // Configured in update_volume_and_pan().
	voice_outputs[MIX_CLICK]->add_route(0, m_click_bpf, 1.0);
	m_click_bpf->add_route(0, m_left_mixer, 1.0);
	m_click_bpf->add_route(0, m_right_mixer, 1.0);

	auto &beep_hpf = FILTER_RC(config, "beep_hpf");
	const float rc_r = RES_2_PARALLEL(MIXER_R_BEEP, MIXER_R_BEEP);
	beep_hpf.set_rc(filter_rc_device::HIGHPASS, rc_r, 0, 0, CAP_U(0.1));  // C17. Cutoff: ~6.24 Hz.
	m_beep->add_route(0, beep_hpf, 1.0);
	// The mixers are inverting.
	beep_hpf.add_route(0, m_left_mixer, -MIXER_R_FEEDBACK / MIXER_R_BEEP);
	beep_hpf.add_route(0, m_right_mixer, -MIXER_R_FEEDBACK / MIXER_R_BEEP);

	// The left/right output op-amps (U2A, U2B) also have a capacitor in their
	// feedback loop, which turns them into LPFs.
	auto &left_rc = FILTER_RC(config, "left_output_lpf");
	auto &right_rc = FILTER_RC(config, "right_output_lpf");
	left_rc.set_lowpass(OUTPUT_R_FEEDBACK, OUTPUT_C_FEEDBACK);  // Cutoff: ~15.9KHz.
	right_rc.set_lowpass(OUTPUT_R_FEEDBACK, OUTPUT_C_FEEDBACK);
	m_left_mixer->add_route(0, left_rc, 1.0);
	m_right_mixer->add_route(0, right_rc, 1.0);

	SPEAKER(config, m_out, 2).front();
	// Gain will be set in update_master_volume().
	left_rc.add_route(0, m_out, 1.0, 0);
	right_rc.add_route(0, m_out, 1.0, 1);
}

void linndrum_audio_device::device_start()
{
	save_item(NAME(m_hat_open));
	save_item(NAME(m_hat_triggered));
	save_item(NAME(m_mux_counting));
	save_item(NAME(m_mux_counters));
	save_item(NAME(m_snare_counting));
	save_item(NAME(m_snare_counter));
	save_item(NAME(m_sidestick_selected));
	save_item(NAME(m_tom_counting));
	save_item(NAME(m_tom_counter));
	save_item(NAME(m_tom_selected));
	save_item(NAME(m_tom_selected_pitch));
}

void linndrum_audio_device::device_reset()
{
	for (int i = 0; i < NUM_MIXER_CHANNELS; ++i)
		update_volume_and_pan(i);
	update_master_volume();
	update_mux_drum_pitch();
	update_snare_pitch();
	update_tom_pitch();
	update_hat_decay();
}

void linndrum_audio_device::write_dac(dac76_device &dac, u8 sample)
{
	dac.update();
	dac.sb_w(BIT(sample, 7));
	dac.b1_w(BIT(sample, 6));
	dac.b2_w(BIT(sample, 5));
	dac.b3_w(BIT(sample, 4));
	dac.b4_w(BIT(sample, 3));
	dac.b5_w(BIT(sample, 2));
	dac.b6_w(BIT(sample, 1));
	dac.b7_w(BIT(sample, 0));
}

s32 linndrum_audio_device::get_ls267_freq(const std::array<s32, 2>& freq_range, float cv)
{
	// The relationship between CV and frequency is approximately linear. The
	// frequency range is set by an external capacitor.
	static constexpr const float MIN_CV = 0;
	static constexpr const float MAX_CV = VCC;
	const float alpha = (freq_range[1] - freq_range[0]) / (MAX_CV - MIN_CV);
	return s32(roundf(freq_range[0] + alpha * cv));
}

float linndrum_audio_device::get_snare_tom_pitch_cv(float v_tune)
{
	// The tom/conga and snare tuning knobs are combined with resistors to
	// produce a tuning voltage (v_tune). The tom/conga and snare networks are
	// different. But v_tune for both is processed by identical circuits.
	// Component designations below are for the snare circuit, but values are
	// the same for the tom one.

	static constexpr const float R96 = RES_K(30);
	static constexpr const float R97 = RES_K(47);
	static constexpr const float R100 = RES_K(10);
	static constexpr const float EXTERNAL_CV = 0;  // External CV not yet emulated.

	// U95B (4558 opamp) mixes v_tune with external CV. v_tune is applied to
	// the + input, while the (inverted and scaled) external CV is applied to
	// the - input, via R97.
	const float opamp_out = v_tune + (v_tune - EXTERNAL_CV) * R96 / R97;

	// The output is sent to the timer's CV input via a 10K resistor. That CV is
	// loaded by the timer input's impedance.
	const float cv = opamp_out * RES_VOLTAGE_DIVIDER(R100, LS627_CV_INPUT_R);

	// There are clamping diodes attached to the CV input.
	return std::clamp<float>(cv, 0, VCC);
}

TIMER_DEVICE_CALLBACK_MEMBER(linndrum_audio_device::hat_trigger_timer_tick)
{
	m_hat_triggered = false;
	update_hat_decay();
	LOGMASKED(LOG_HAT_EG, "Hat EG started decay.\n");
}

TIMER_DEVICE_CALLBACK_MEMBER(linndrum_audio_device::mux_timer_tick)
{
	// The timer on the actual hardware ticks 4 times per voice. A combination
	// of counters, latches, decoders, encoders and analog multiplexers achieves
	// the following:
	// Tick 0: Selects next voice and enables DAC.
	//         - ROM address counter is incremented, if voice is enabled.
	//           If the counter reaches its max, the voice will be disabled and
	//           the counter will get cleared.
	//         - ROM is enabled, and outputs are written to the DAC.
	//         - Volume variation is configured (for voices that support it).
	//         - If the voice is not enabled, the ROM address will be 0, which
	//           according to the service manual always stores a 0.
	// Tick 1: No-op. Waits for DAC to settle.
	// Tick 2: Sample & Hold (S&H) for the selected voice is enabled.
	// Tick 3: S&H is disabled. DAC is disabled, driving its output to 0.

	// The emulation here does all of the above, for all voices, in a single
	// timer tick. The timer period has been adjusted accordingly.

	for (int voice = 0; voice < NUM_MUX_VOICES; ++voice)
	{
		if (m_mux_counting[voice])
		{
			++m_mux_counters[voice];
			if (m_mux_counters[voice] >= m_mux_samples[voice]->bytes())
			{
				// All outputs in the voice's data latch (74LS74) are cleared.
				mux_drum_w(voice, 0, false);
			}
		}

		const u8 sample = m_mux_samples[voice]->as_u8(m_mux_counters[voice]);
		write_dac(*m_mux_dac[voice], sample);
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(linndrum_audio_device::snare_timer_tick)
{
	if (!m_snare_counting)
		return;

	++m_snare_counter;
	if (BIT(m_snare_counter, 12))  // Counter reached 0x1000 (4096).
	{
		// All outputs of U41 and U42 (74LS74 flip-flops) are cleared.
		snare_w(0);
		return;
	}

	u8 sample = 0;
	if (m_sidestick_selected)
		sample = m_sidestick_samples->as_u8(m_snare_counter);
	else
		sample = m_snare_samples->as_u8(m_snare_counter);
	write_dac(*m_snare_dac, sample);
}

TIMER_DEVICE_CALLBACK_MEMBER(linndrum_audio_device::click_timer_tick)
{
	m_click->level_w(0);
}

TIMER_DEVICE_CALLBACK_MEMBER(linndrum_audio_device::tom_timer_tick)
{
	if (!m_tom_counting)
		return;

	++m_tom_counter;
	if (BIT(m_tom_counter, 13))  // Counter reached 0x2000 (8192).
	{
		// All outputs of U42B and U73B (74LS74 flip-flops) are cleared.
		tom_w(0);
		return;
	}

	u8 sample = 0;
	if (m_tom_selected)
		sample = m_tom_samples->as_u8(m_tom_counter);
	else
		sample = m_conga_samples->as_u8(m_tom_counter);
	write_dac(*m_tom_dac, sample);
}

void linndrum_audio_device::update_volume_and_pan(int channel)
{
	assert(channel >= 0 && channel < NUM_MIXER_CHANNELS);

	static constexpr const float R_VOL_MAX = RES_K(5);
	static constexpr const float R_PAN_MAX = RES_K(10);
	static constexpr const float R1 = RES_K(5.6);
	static constexpr const float R2 = RES_K(5.6);
	static constexpr const float R3 = RES_K(15);
	static constexpr const float R4 = RES_K(15);

	// Since we are interested in voltage gain, rather than actual voltage,
	// use 1V as the voice's output.
	static constexpr const float V_VOICE = 1;
	// DC-blocking capacitor. Same value for all voice outputs except for the
	// "click" and "beep" sounds.
	static constexpr const float C_VOICE = CAP_U(10);

	const s32 volume = m_volume[channel]->read();
	const float r_vol_bottom = R_VOL_MAX * RES_AUDIO_POT_LAW(volume / 100.0F);
	const float r_vol_top = R_VOL_MAX - r_vol_bottom;
	const float r_pan_left = m_pan[channel]->read() * R_PAN_MAX / 100.0F;
	const float r_pan_right = R_PAN_MAX - r_pan_left;

	const float r0 = MIXER_R_PRE_FADER[channel] + r_vol_top;
	const float r_right_gnd = R1 + RES_2_PARALLEL(r_pan_right, R3);
	const float r_left_gnd = R2 + RES_2_PARALLEL(r_pan_left, R4);

	// Resistance to ground as seen from the volume pot's wiper and the voice's output.
	const float r_wiper_gnd = RES_3_PARALLEL(r_vol_bottom, r_left_gnd, r_right_gnd);
	const float r_voice_gnd = r0 + r_wiper_gnd;

	float gain_left = 0;
	float gain_right = 0;
	if (volume > 0)
	{
		// Calculate voltage scale factor at the wiper of the volume pot.
		const float i0 = V_VOICE / r_voice_gnd;
		const float v_pot = V_VOICE - i0 * r0;

		// Calculate voltage at the input resistor (R3) of the U1A summing
		// op-amp, and use it to compute amp gain.
		const float i1 = v_pot / r_right_gnd;
		const float v_input_right = v_pot - i1 * R1;
		gain_right = v_input_right * MIXER_R_FEEDBACK / R3;

		// Calculate voltage at the input resistor (R4) of the U1B summing
		// op-amp, and use it to compute amp gain.
		const float i2 = v_pot / r_left_gnd;
		const float v_input_left = v_pot - i2 * R2;
		gain_left = v_input_left * MIXER_R_FEEDBACK / R4;
	}

	device_sound_interface *mixer_input = nullptr;
	if (channel == MIX_CLICK)
		mixer_input = m_click_bpf;
	else
		mixer_input = m_voice_hpf[channel];

	// Using -gain_*, because the summing op-amps are inverting.
	mixer_input->set_route_gain(0, m_left_mixer, 0, -gain_left);
	mixer_input->set_route_gain(0, m_right_mixer, 0, -gain_right);
	LOGMASKED(LOG_MIX, "Gain update for %s - left: %f, right: %f\n",
			  MIXER_CHANNEL_NAMES[channel], gain_left, gain_right);

	if (channel == MIX_CLICK)
	{
		// Compared to the other voices, the click uses a different value for
		// the DC blocking capacitor. Furthermore, there is a capacitor to ground
		// at the volume pot wiper. The resulting filter acts as an HPF (cutoff:
		// ~1.15 KHz) when the click volume is at max, and becomes a BPF whose
		// characteristics change as the volume decreases.

		// Capacitor at the output of the 556 timer.
		static constexpr const float C_CLICK_DCBLOCK = CAP_U(0.01);  // C37
		// Capacitor to ground, at the wiper of the "click" volume fader.
		static constexpr const float C_CLICK_WIPER = CAP_U(0.047);  // No designation.

		// All "click" filter configurations result in singificant attenutation
		// (< 0.2x), which makes the click very quiet. The filter characteristics
		// (gain and Fc) have been verified with simulations. So it is possible
		// there is an error in the schematic. Different capacitor values, or a
		// supply of 15V (instead of the stated 5V) for the 556 timer generating
		// the click could explain this.
		// For now, scale by some number to match the volume of other voices.
		static constexpr const float CLICK_GAIN_CORRECTION = 5;

		if (volume >= 100)
		{
			// HPF transfer function: H(s) = (g * s) / (s + w) where
			//   g = C1 / (C1 + C2)
			//   w = 1 / (R * (C1 + C2))
			const float fc = 1.0F / (2 * float(M_PI) * r_voice_gnd * (C_CLICK_DCBLOCK + C_CLICK_WIPER));
			const float gain = C_CLICK_DCBLOCK / (C_CLICK_DCBLOCK + C_CLICK_WIPER);
			m_click_bpf->modify(filter_biquad_device::biquad_type::HIGHPASS1P1Z, fc, 1, gain * CLICK_GAIN_CORRECTION);
			LOGMASKED(LOG_MIX, "- HPF cutoff: %.2f Hz, Gain: %.3f\n", fc, gain);
		}
		else if (volume > 0)
		{
			filter_biquad_device::biquad_params p = m_click_bpf->rc_rr_bandpass_calc(r0, r_wiper_gnd, C_CLICK_DCBLOCK, C_CLICK_WIPER);
			// The filter params above include the effect of the volume
			// potentiometer. But `gain_left` and `gain_right` already incorporate
			// that effect. So it needs to be undone from the filter's gain.
			p.gain /= RES_VOLTAGE_DIVIDER(r0, r_wiper_gnd);
			// See comments for CLICK_GAIN_CORRECTION.
			p.gain *= CLICK_GAIN_CORRECTION;
			m_click_bpf->modify(p);
			LOGMASKED(LOG_MIX, "- BPF cutoff: %.2f Hz, Q: %.3f, Gain: %.3f\n", p.fc, p.q, p.gain);
		}
		// Else, if the volume is 0, don't change the BPF's configuration to avoid divisions by 0.
	}
	else
	{
		// The rest of the voices just have a DC-blocking filter. Its exact cutoff
		// will depend on the volume and pan settings, but it won't be audible.
		m_voice_hpf[channel]->filter_rc_set_RC(filter_rc_device::HIGHPASS, r_voice_gnd, 0, 0, C_VOICE);
		LOGMASKED(LOG_MIX, "- HPF cutoff: %.2f Hz\n", 1.0F / (2 * float(M_PI) * r_voice_gnd * C_VOICE));
	}
}

void linndrum_audio_device::update_master_volume()
{
	static constexpr const float R_MASTER_VOLUME_MAX = RES_K(10);

	const float r_pot_bottom = R_MASTER_VOLUME_MAX * RES_AUDIO_POT_LAW(m_master_volume->read() / 100.0F);
	const float r_pot_top = R_MASTER_VOLUME_MAX - r_pot_bottom;
	const float v_input = RES_VOLTAGE_DIVIDER(r_pot_top, RES_2_PARALLEL(r_pot_bottom, OUTPUT_R_INPUT));

	const float gain = v_input * OUTPUT_R_FEEDBACK / OUTPUT_R_INPUT;
	const float final_gain = gain * VOLTAGE_TO_SOUND_SCALER;

	// Using -final_gain, because the output opamps (U2A, U2B) are inverting.
	m_out->set_input_gain(0, -final_gain);
	m_out->set_input_gain(1, -final_gain);

	LOGMASKED(LOG_MIX, "Master volume updated. Gain: %f, final gain: %f\n", gain, final_gain);
}

void linndrum_audio_device::update_mux_drum_pitch()
{
	static constexpr const int TIMER_TICKS_PER_VOICE = 4;
	static constexpr const float POT_MAX = RES_K(10);
	static constexpr const float R19A = RES_K(18);

	const float pot_bottom = m_mux_tuning_trimmer->read() * POT_MAX / 100.0F;
	const float pot_top = POT_MAX - pot_bottom;
	const float cv = VPLUS * RES_VOLTAGE_DIVIDER(R19A + pot_top, RES_2_PARALLEL(pot_bottom, LS627_CV_INPUT_R));
	const s32 freq = get_ls267_freq(MUX_TIMER_HZ_RANGE, cv);

	// See comments in mux_timer_tick() for the reason for this adjustment.
	const s32 adjusted_freq = float(freq) / (NUM_MUX_VOICES * TIMER_TICKS_PER_VOICE);
	const attotime period = attotime::from_hz(s32(roundf(adjusted_freq)));
	m_mux_timer->adjust(period, 0, period);

	LOGMASKED(LOG_PITCH, "Updated mux drum pitch. CV: %f, freq: %d, adjusted: %d\n",
			  cv, freq, adjusted_freq);
}

void linndrum_audio_device::update_snare_pitch()
{
	static constexpr const float POT_MAX = RES_K(100);
	static constexpr const float R101 = RES_K(75);
	static constexpr const float R98 = RES_K(22);

	const float pot_bottom = m_tuning_knobs[TK_SNARE]->read() * POT_MAX / 100.0F;
	const float pot_top = POT_MAX - pot_bottom;
	const float v_pot = VPLUS * RES_VOLTAGE_DIVIDER(pot_top, RES_2_PARALLEL(pot_bottom, R101 + R98));
	const float cv = get_snare_tom_pitch_cv(v_pot * RES_VOLTAGE_DIVIDER(R101, R98));

	const s32 freq = get_ls267_freq(SNARE_TIMER_HZ_RANGE, cv);
	const attotime period = attotime::from_hz(freq);
	m_snare_timer->adjust(period, 0, period);

	LOGMASKED(LOG_PITCH, "Updated snare pitch. CV: %f, freq: %d Hz\n", cv, freq);
}

void linndrum_audio_device::update_tom_pitch()
{
	static constexpr const float R_POT_MAX = RES_K(100);

	// A map from `enum tom_voices` to `enum tuning_knobs`.
	static constexpr const int VOICE_TO_KNOB_MAP[NUM_TOM_VOICES] =
	{
		TK_LO_CONGAS,
		TK_HI_CONGAS,
		TK_LO_TOMS,
		TK_MID_TOMS,
		TK_HI_TOMS
	};

	if (m_tom_selected_pitch < 0)
	{
		LOG("Firmware or driver bug: floating input to pitch CV op-amp.\n");
		return;
	}

	// The pitch CV for each variation appears on an input of MUX U81 (CD4051).
	// The CV of the currently selected variation is routed to the timer (74LS627, U77B).
	// Compute the CV of the selected variation.
	const int knob_index = VOICE_TO_KNOB_MAP[m_tom_selected_pitch];
	const s32 knob_value = m_tuning_knobs[knob_index]->read();
	const float r_pot_bottom = knob_value * R_POT_MAX / 100.0F;
	const float r_pot_top = R_POT_MAX - r_pot_bottom;
	const float v_mux_out = VPLUS * RES_VOLTAGE_DIVIDER(RES_K(390) + r_pot_top, r_pot_bottom);
	const float cv = get_snare_tom_pitch_cv(v_mux_out);

	const s32 freq = get_ls267_freq(SNARE_TIMER_HZ_RANGE, cv);
	const attotime period = attotime::from_hz(freq);

	// Only restart the timer if there is a change. Many calls to this function
	// will not result in a frequency change.
	if (m_tom_timer->period() != period)
		m_tom_timer->adjust(period, 0, period);

	LOGMASKED(LOG_PITCH, "Updated tom pitch: %d, %d. CV: %f, freq: %d\n",
			  knob_index, knob_value, cv, freq);
}

void linndrum_audio_device::update_hat_decay()
{
	// When the hat is configed as "open", the EG will discharge through a 1M
	// resistor. When the hat is "closed", a CD4053 will add a parallel
	// discharge path through the "hihat decay" pot.
	float eg_r = HAT_R33;
	if (!m_hat_open)
	{
		const float r_decay_pot = HAT_DECAY_POT_R_MAX * m_hat_decay_pot->read() / 100.0F;
		eg_r = RES_2_PARALLEL(HAT_R33, HAT_R34 + R_ON_CD4053 + r_decay_pot);
	}
	m_hat_eg->set_r(eg_r);

	// When the hat is strobed, it will trigger a 556 (U37A) timer in monostable
	// mode (see mux_drum_w()). The timer's output pin is connected to the EG
	// capacitor via a diode, and will maintain that capacitor at VCC until the
	// timer expires. At that point, the EG capacitor will discharge through eg_r.
	if (m_hat_triggered)
		m_hat_eg->set_instant_v(VCC);
	else
		m_hat_eg->set_target_v(0);

	LOGMASKED(LOG_HAT_EG, "Hat decay. Open: %d, EG R: %f\n", m_hat_open, eg_r);
}

namespace {

constexpr const char MAINCPU_TAG[] = "z80";
constexpr const char NVRAM_TAG[] = "nvram";
constexpr const char AUDIO_TAG[] = "linndrum_audio";

class linndrum_state : public driver_device
{
public:
	static constexpr feature_type unemulated_features() { return feature::TAPE; }

	linndrum_state(const machine_config &mconfig, device_type type, const char *tag) ATTR_COLD
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, MAINCPU_TAG)
		, m_audio(*this, AUDIO_TAG)
		, m_tempo_timer(*this, "tempo_timer_556_u30a")
		, m_debounce_timer(*this, "debounce_timer_556_u30b")
		, m_keyboard(*this, "keyboard_col_%d", 0)
		, m_playstop(*this, "play_stop")
		, m_triggers(*this, "trigger_inputs")
		, m_tempo_pot(*this, "pot_tempo")
		, m_step_display(*this, "display_step_%d", 0U)
		, m_pattern_display(*this, "display_pattern_%d", 0U)
		, m_tape_sync_out(*this, "output_tape_sync")
	{
	}

	void linndrum(machine_config &config) ATTR_COLD;

	DECLARE_INPUT_CHANGED_MEMBER(tempo_pot_adjusted);

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

private:
	u8 keyboard_r(offs_t offset);
	u8 inport_r();
	template<int Display> void display_w(u8 data);

	u8 start_debounce_r();
	void start_debounce_w(u8 data);
	TIMER_DEVICE_CALLBACK_MEMBER(debounce_timer_elapsed);

	void update_tempo_timer();
	TIMER_DEVICE_CALLBACK_MEMBER(tempo_timer_tick);

	void tape_sync_enable_w(int state);
	void update_tape_sync_out();

	void voice_data_enable_w(int state);
	u8 get_voice_data(u8 data) const;

	void memory_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_device<linndrum_audio_device> m_audio;
	required_device<timer_device> m_tempo_timer;  // 556, U30A.
	required_device<timer_device> m_debounce_timer;  // 556, U30B.
	required_ioport_array<6> m_keyboard;
	required_ioport m_playstop;
	required_ioport m_triggers;
	required_ioport m_tempo_pot;
	output_finder<2> m_step_display;  // 2 x MAN4710.
	output_finder<2> m_pattern_display;  // 2 x MAN4710.
	output_finder<> m_tape_sync_out;

	bool m_debouncing = false;
	bool m_tempo_state = false;
	bool m_tape_sync_enabled = false;
	bool m_voice_data_enabled = false;  // Enables/disables U19 (74LS365).

	static constexpr const int DISPLAY_STEP = 0;
	static constexpr const int DISPLAY_PATTERN = 1;
};

u8 linndrum_state::keyboard_r(offs_t offset)
{
	// Columns in the keyboard matrix are selected by A0-A5. Each of these
	// address lines are inverted by U29 (74LS05). A high at Ax enables column
	// x. If more than one of A0-A5 are set, multiple columns will be selected.
	// Rows are pulled up by a resistor array (RP8), buffered by U28 (74LS244)
	// and connected to D0-D5.
	u8 selected = 0x3f;  // D0-D5.
	for (int i = 0; i < 6; ++i)
		if (BIT(offset, i))
			selected &= m_keyboard[i]->read();

	const u8 d6 = m_debouncing ? 1 : 0;

	// The play/stop button is not part of the keyboard matrix. It is always
	// read at D7, regardless of the selected column. The play/stop button and
	// footswitch are wired such that if any of them is grounded (activated) a
	// 0 will be returned in D7.
	const bool play_button_pressed = !(m_playstop->read() & 0x01);
	const bool play_footswitch_pressed = !(m_playstop->read() & 0x02);
	const u8 d7 = (play_button_pressed || play_footswitch_pressed) ? 0 : 1;

	if (selected != 0x3f || d7 == 0)
	{
		LOGMASKED(LOG_KEYBOARD,
				"Offset: %02x, keys: %02x, debounce: %d, play: %d\n",
				offset, selected, d6, d7);
	}

	return (d7 << 7) | (d6 << 6) | selected;
}

u8 linndrum_state::inport_r()
{
	const u8 d0 = m_tempo_state ? 1 : 0;  // Tempo.

	// The cassette input and sync input circuits are identical.
	// TODO: Determine value at rest.
	const u8 d1 = 0;  // Sync input. TODO: implement.
	const u8 d2 = 0;  // Cassette input. TODO: implement.

	const u8 triggers = m_triggers->read() & 0x1f;

	return (triggers << 3) | (d2 << 2) | (d1 << 1) | d0;
}

template<int Display> void linndrum_state::display_w(u8 data)
{
	static constexpr const u8 PATTERNS[16] = // 4 x 74LS47 (U24-U27).
	{
		0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7c, 0x07,
		0x7f, 0x67, 0x58, 0x4c, 0x62, 0x69, 0x78, 0x00,
	};

	const u8 ms_digit = PATTERNS[(data >> 4) & 0x0f];
	const u8 ls_digit = PATTERNS[data & 0x0f];

	static_assert(Display == DISPLAY_STEP || Display == DISPLAY_PATTERN);
	if (Display == DISPLAY_STEP)
	{
		m_step_display[0] = ms_digit;
		m_step_display[1] = ls_digit;
	}
	else
	{
		m_pattern_display[0] = ms_digit;
		m_pattern_display[1] = ls_digit;
	}
}

u8 linndrum_state::start_debounce_r()
{
	if (!machine().side_effects_disabled())
		start_debounce_w(0);
	return 0x00;  // D0-D7 pulled low by RP10.
}

void linndrum_state::start_debounce_w(u8 /*data*/)
{
	static constexpr const float R3 = RES_K(30);
	static constexpr const float C3 = CAP_U(0.1);
	static constexpr attotime INTERVAL = PERIOD_OF_555_MONOSTABLE(R3, C3);

	m_debouncing = true;
	m_debounce_timer->adjust(INTERVAL);
	LOGMASKED(LOG_DEBOUNCE, "Debounce timer: start\n");
}

TIMER_DEVICE_CALLBACK_MEMBER(linndrum_state::debounce_timer_elapsed)
{
	m_debouncing = false;
	LOGMASKED(LOG_DEBOUNCE, "Debounce timer: elapsed\n");
}

void linndrum_state::update_tempo_timer()
{
	static constexpr const float R1 = RES_K(5.1);
	static constexpr const float R2 = RES_K(18);
	static constexpr const float C2 = CAP_U(0.1);
	static constexpr const float P1_MAX = RES_K(100);

	// Using `100 - pot value` because the higher (the more clockwise) the pot
	// is turned, the lower the resistance and the fastest the tempo.
	const float tempo_r = (100 - m_tempo_pot->read()) * P1_MAX / 100.0F;
	const attotime period = PERIOD_OF_555_ASTABLE(R1, R2 + tempo_r, C2);
	m_tempo_timer->adjust(period, 0, period);
	LOGMASKED(LOG_TEMPO_CHANGE, "Tempo adjusted: %f\n", period.as_double());
}

TIMER_DEVICE_CALLBACK_MEMBER(linndrum_state::tempo_timer_tick)
{
	// The output of the 556 clocks an 74LS74 (U8B) that is wired in a
	// divide-by-2 configuration. Each time the timer elapses, m_tempo_state
	// will get negated.
	m_tempo_state = !m_tempo_state;
	update_tape_sync_out();
	LOGMASKED(LOG_TEMPO, "Tempo timer elapsed: %d\n", m_tempo_state);
}

void linndrum_state::tape_sync_enable_w(int state)
{
	LOGMASKED(LOG_TAPE_SYNC_ENABLE, "Tape sync enable: %d\n", state);
	m_tape_sync_enabled = bool(state);
	update_tape_sync_out();
}

void linndrum_state::update_tape_sync_out()
{
	// tape_sync_enable (U16 latch, O5) is NANDed with the current tempo
	// state (U34, 74LS00). The output is then inverted by Q3/R20/R21.
	if (m_tape_sync_enabled)
		m_tape_sync_out = m_tempo_state ? 1 : 0;
	else
		m_tape_sync_out = 0;
}

void linndrum_state::voice_data_enable_w(int state)
{
	// Controls whether data (D0-D3) is transmitted to the voice circuits. This
	// is done by U19 (74LS365 hex buffer. Enable inputs are active-low).
	// This is usually disabled to prevent interference from the "noisy" data
	// bus to the voice circuits.
	m_voice_data_enabled = !state;
}

u8 linndrum_state::get_voice_data(u8 data) const
{
	if (m_voice_data_enabled)
	{
		return data & 0x0f;  // Voice data bus is 4 bits wide.
	}
	else
	{
		LOG("Firmware bug: floating voice data bus when strobing a voice.\n");
		return 0x0f;  // Floating TTL inputs. Will likely resolve to 1s.
	}
}

void linndrum_state::memory_map(address_map &map)
{
	// Signal names (such as "/READ INPORT") are taken from the schematics.

	map.global_mask(0x3fff);  // A14 and A15 are not used.

	map(0x0000, 0x1f7f).rom();  // 2 x 2732. U14-U13.
	map(0x1f80, 0x1f80).mirror(0x003f).r(FUNC(linndrum_state::inport_r));  // /READ INPORT.

	// Writes to: 0x1f80 - 0x1fbf: /OUT STROBE EN.
	// Outputs are selected by two 74LS138 (U20, U21). U20 is enabled when A3 is
	// 0, U21 when it is 1.
	map(0x1f80, 0x1f80).mirror(0x0030).w(FUNC(linndrum_state::display_w<DISPLAY_PATTERN>));  // U23 latch.
	map(0x1f81, 0x1f81).mirror(0x0030).w(FUNC(linndrum_state::display_w<DISPLAY_STEP>));  // U22 latch.
	map(0x1f82, 0x1f82).mirror(0x0030).w("latch_u18", FUNC(output_latch_device::write));  // LEDs.
	map(0x1f83, 0x1f83).mirror(0x0030).w("latch_u17", FUNC(output_latch_device::write));  // LEDs.
	map(0x1f84, 0x1f84).mirror(0x0030).w("latch_u16", FUNC(output_latch_device::write));  // LEDs & outputs.

	// Voice strobes.
	map(0x1f85, 0x1f85).mirror(0x0030).lw8(NAME([this] (u8 data) { m_audio->mux_drum_w(MV_BASS, get_voice_data(data)); }));
	map(0x1f86, 0x1f86).mirror(0x0030).lw8(NAME([this] (u8 data) { m_audio->snare_w(get_voice_data(data)); }));
	map(0x1f87, 0x1f87).mirror(0x0030).lw8(NAME([this] (u8 data) { m_audio->mux_drum_w(MV_HAT, get_voice_data(data)); }));
	map(0x1f88, 0x1f88).mirror(0x0030).lw8(NAME([this] (u8 data) { m_audio->tom_w(get_voice_data(data)); }));
	map(0x1f89, 0x1f89).mirror(0x0030).lw8(NAME([this] (u8 data) { m_audio->mux_drum_w(MV_RIDE, get_voice_data(data)); }));
	map(0x1f8a, 0x1f8a).mirror(0x0030).lw8(NAME([this] (u8 data) { m_audio->mux_drum_w(MV_CRASH, get_voice_data(data)); }));
	map(0x1f8b, 0x1f8b).mirror(0x0030).lw8(NAME([this] (u8 data) { m_audio->mux_drum_w(MV_CABASA, get_voice_data(data)); }));
	map(0x1f8c, 0x1f8c).mirror(0x0030).lw8(NAME([this] (u8 data) { m_audio->mux_drum_w(MV_TAMBOURINE, get_voice_data(data)); }));
	map(0x1f8d, 0x1f8d).mirror(0x0030).lw8(NAME([this] (u8 data) { m_audio->mux_drum_w(MV_COWBELL, get_voice_data(data)); }));
	map(0x1f8e, 0x1f8e).mirror(0x0030).lw8(NAME([this] (u8 data) { m_audio->mux_drum_w(MV_CLAP, get_voice_data(data)); }));
	map(0x1f8f, 0x1f8f).mirror(0x0030).w(m_audio, FUNC(linndrum_audio_device::strobe_click_w));  // No voice data sent.

	map(0x1fc0, 0x1fff).r(FUNC(linndrum_state::keyboard_r));  // /READ KEYBD.
	map(0x2000, 0x3fff).ram().share(NVRAM_TAG); // 4 x HM6116LP4. U12-U9.
}

void linndrum_state::io_map(address_map &map)
{
	map.global_mask(0xff);

	// The debouncing timer is started by the Z80's /IORQ signal itself. So
	// there is no difference between read and write, and the address and data
	// bits don't matter.
	map(0x00, 0x00).mirror(0xff).r(FUNC(linndrum_state::start_debounce_r)).w(FUNC(linndrum_state::start_debounce_w));
}

void linndrum_state::machine_start()
{
	m_step_display.resolve();
	m_pattern_display.resolve();
	m_tape_sync_out.resolve();

	save_item(NAME(m_debouncing));
	save_item(NAME(m_tempo_state));
	save_item(NAME(m_tape_sync_enabled));
	save_item(NAME(m_voice_data_enabled));
}

void linndrum_state::machine_reset()
{
	update_tempo_timer();
}

void linndrum_state::linndrum(machine_config &config)
{
	// D0-D7 and A0-A11 are pulled low by RP10 and RP9.
	Z80(config, m_maincpu, 4_MHz_XTAL / 2);  // Divided by 74LS74, U8A.
	m_maincpu->set_addrmap(AS_PROGRAM, &linndrum_state::memory_map);
	m_maincpu->set_addrmap(AS_IO, &linndrum_state::io_map);

	NVRAM(config, NVRAM_TAG, nvram_device::DEFAULT_ALL_0);

	TIMER(config, m_tempo_timer).configure_generic(  // 556, U30A.
		FUNC(linndrum_state::tempo_timer_tick));
	TIMER(config, m_debounce_timer).configure_generic(  // 556, U30B.
		FUNC(linndrum_state::debounce_timer_elapsed));

	LINNDRUM_AUDIO(config, m_audio);

	config.set_default_layout(layout_linn_linndrum);

	// Latches connected to cathodes of LEDs (through resistors), so they are
	// active-low.

	output_latch_device &u18(OUTPUT_LATCH(config, "latch_u18"));
	u18.bit_handler<0>().set_output("led_1_8").invert();
	u18.bit_handler<1>().set_output("led_1_8_t").invert();
	u18.bit_handler<2>().set_output("led_1_16").invert();
	u18.bit_handler<3>().set_output("led_1_16_t").invert();
	u18.bit_handler<4>().set_output("led_1_32").invert();
	u18.bit_handler<5>().set_output("led_1_32_t").invert();
	u18.bit_handler<6>().set_output("led_hi").invert();
	u18.bit_handler<7>().set_output("led_play_stop").invert();

	output_latch_device &u17(OUTPUT_LATCH(config, "latch_u17"));
	u17.bit_handler<0>().set_output("led_a").invert();
	u17.bit_handler<1>().set_output("led_b").invert();
	u17.bit_handler<2>().set_output("led_c").invert();
	u17.bit_handler<3>().set_output("led_d").invert();
	u17.bit_handler<4>().set_output("led_e").invert();
	u17.bit_handler<5>().set_output("led_f").invert();
	u17.bit_handler<6>().set_output("led_load").invert();
	u17.bit_handler<7>().set_output("led_store").invert();

	output_latch_device &u16(OUTPUT_LATCH(config, "latch_u16"));
	u16.bit_handler<0>().set_output("led_percussion").invert();
	u16.bit_handler<1>().set_output("led_ext_sync").invert();
	u16.bit_handler<2>().set_output("led_pattern").invert();
	u16.bit_handler<2>().append_output("led_song");  // Inverted by U31A.
	u16.bit_handler<3>().set(FUNC(linndrum_state::voice_data_enable_w));
	u16.bit_handler<4>().set(m_audio, FUNC(linndrum_audio_device::beep_w));
	u16.bit_handler<5>().set(FUNC(linndrum_state::tape_sync_enable_w));
	// Output voltage divided by R24/R25 to 0V - 2.5V.
	u16.bit_handler<6>().set_output("output_cassette");
	// Inverted by Q4/R22/R23.
	u16.bit_handler<7>().set_output("output_trigger").invert();
}

DECLARE_INPUT_CHANGED_MEMBER(linndrum_state::tempo_pot_adjusted)
{
	update_tempo_timer();
}

// PORT_NAMEs are based on the annotations in the schematic.
INPUT_PORTS_START(linndrum)
	PORT_START("keyboard_col_0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("6") PORT_CODE(KEYCODE_6)

	PORT_START("keyboard_col_1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("7") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SONG/PAT.") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SONG#") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("END") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("DELETE") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("INSERT") PORT_CODE(KEYCODE_T)

	PORT_START("keyboard_col_2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("<-") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("->") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("ENTER") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("STORE")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("LOAD")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("EXT.SYNC") PORT_CODE(KEYCODE_L)

	PORT_START("keyboard_col_3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("BPM/TRIG.") PORT_CODE(KEYCODE_O)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SIDESTICK") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SNARE 1") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SNARE 2") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SNARE 3") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("BASS 1") PORT_CODE(KEYCODE_B)

	PORT_START("keyboard_col_4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("BASS 2") PORT_CODE(KEYCODE_N)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("CRASH") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PERC.") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("CABASA1 / HIHAT1") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("CABASA2 / HIHAT2") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("TAMP 1 / HIHAT O") PORT_CODE(KEYCODE_D)

	PORT_START("keyboard_col_5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("TAMP 2 / HI TOM") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("HI CONGA / MID TOM") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("LO CONGA / LO TOM") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("COWBELL / RIDE 1") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("CLAPS / RIDE 2") PORT_CODE(KEYCODE_K)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)

	// The play-stop button and footswitch input are both connected together
	// in a way that if any of them is low, D6 of the keyboard port will read
	// low.
	PORT_START("play_stop")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PLAY/STOP") PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PLAY/STOP FOOTSWITCH")

	// The trigger circuit will detect fast changes in the "trigger" inputs, and
	// output a pulse. Slow changes won't trigger a pulse. So there is some
	// flexibility in the types of input accepted. For now, trigger inputs
	// are implemented as digital inputs (traditional trigger signals).
	// TODO: Determine if active high or active low.
	PORT_START("trigger_inputs")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TRIGGER 5")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TRIGGER 4")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TRIGGER 3")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TRIGGER 2")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TRIGGER 1")

	PORT_START("pot_tempo")
	PORT_ADJUSTER(80, "TEMPO") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(linndrum_state::tempo_pot_adjusted), 0)

	PORT_START("pot_volume")
	PORT_ADJUSTER(90, "MASTER VOLUME") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::master_volume_changed), 0)

	PORT_START("pot_mux_tuning")  // Internal trimmer. Not accessible by the end-user.
	PORT_ADJUSTER(25, "TRIMMER: MUX DRUM TUNING") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mux_drum_tuning_changed), 0)

	PORT_START("pot_tuning_1")
	PORT_ADJUSTER(25, "SNARE TUNING") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::snare_tuning_changed), 0)
	PORT_START("pot_tuning_2")
	PORT_ADJUSTER(60, "HI TOM TUNING") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::tom_tuning_changed), 0)
	PORT_START("pot_tuning_3")
	PORT_ADJUSTER(50, "MID TOM TUNING") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::tom_tuning_changed), 0)
	PORT_START("pot_tuning_4")
	PORT_ADJUSTER(40, "LO TOM TUNING") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::tom_tuning_changed), 0)
	PORT_START("pot_tuning_5")
	PORT_ADJUSTER(52, "HI CONGAS TUNING") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::tom_tuning_changed), 0)
	PORT_START("pot_tuning_6")
	PORT_ADJUSTER(40, "LO CONGAS TUNING") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::tom_tuning_changed), 0)
	PORT_START("pot_hihat_decay")
	PORT_ADJUSTER(50, "HIHAT DECAY") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::hat_decay_changed), 0)

	PORT_START("pot_pan_1")
	PORT_ADJUSTER(50, "BASS PAN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_BASS)
	PORT_START("pot_pan_2")
	PORT_ADJUSTER(50, "SNARE PAN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_SNARE)
	PORT_START("pot_pan_3")
	PORT_ADJUSTER(50, "SIDESTICK PAN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_SIDESTICK)
	PORT_START("pot_pan_4")
	PORT_ADJUSTER(50, "HIHAT PAN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_HIHAT)
	PORT_START("pot_pan_5")
	PORT_ADJUSTER(50, "HI TOM PAN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_HITOMS)
	PORT_START("pot_pan_6")
	PORT_ADJUSTER(50, "MID TOM PAN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_MIDTOMS)
	PORT_START("pot_pan_7")
	PORT_ADJUSTER(50, "LO TOM PAN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_LOTOMS)
	PORT_START("pot_pan_8")
	PORT_ADJUSTER(50, "RIDE PAN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_RIDE)
	PORT_START("pot_pan_9")
	PORT_ADJUSTER(50, "CRASH PAN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_CRASH)
	PORT_START("pot_pan_10")
	PORT_ADJUSTER(50, "CABASA PAN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_CABASA)
	PORT_START("pot_pan_11")
	PORT_ADJUSTER(50, "TAMB PAN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_TAMB)
	PORT_START("pot_pan_12")
	PORT_ADJUSTER(50, "HI CONGA PAN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_HICONGAS)
	PORT_START("pot_pan_13")
	PORT_ADJUSTER(50, "LO CONGA PAN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_LOCONGAS)
	PORT_START("pot_pan_14")
	PORT_ADJUSTER(50, "COWBELL PAN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_COWBELL)
	PORT_START("pot_pan_15")
	PORT_ADJUSTER(50, "CLAPS PAN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_CLAPS)
	PORT_START("pot_pan_16")
	PORT_ADJUSTER(50, "CLICK PAN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_CLICK)

	PORT_START("pot_gain_1")
	PORT_ADJUSTER(100, "BASS GAIN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_BASS)
	PORT_START("pot_gain_2")
	PORT_ADJUSTER(100, "SNARE GAIN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_SNARE)
	PORT_START("pot_gain_3")
	PORT_ADJUSTER(100, "SIDESTICK GAIN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_SIDESTICK)
	PORT_START("pot_gain_4")
	PORT_ADJUSTER(100, "HIHAT GAIN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_HIHAT)
	PORT_START("pot_gain_5")
	PORT_ADJUSTER(100, "HI TOM GAIN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_HITOMS)
	PORT_START("pot_gain_6")
	PORT_ADJUSTER(100, "MID TOM GAIN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_MIDTOMS)
	PORT_START("pot_gain_7")
	PORT_ADJUSTER(100, "LO TOM GAIN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_LOTOMS)
	PORT_START("pot_gain_8")
	PORT_ADJUSTER(100, "RIDE GAIN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_RIDE)
	PORT_START("pot_gain_9")
	PORT_ADJUSTER(100, "CRASH GAIN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_CRASH)
	PORT_START("pot_gain_10")
	PORT_ADJUSTER(100, "CABASA GAIN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_CABASA)
	PORT_START("pot_gain_11")
	PORT_ADJUSTER(100, "TAMB GAIN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_TAMB)
	PORT_START("pot_gain_12")
	PORT_ADJUSTER(100, "HI CONGA GAIN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_HICONGAS)
	PORT_START("pot_gain_13")
	PORT_ADJUSTER(100, "LO CONGA GAIN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_LOCONGAS)
	PORT_START("pot_gain_14")
	PORT_ADJUSTER(100, "COWBELL GAIN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_COWBELL)
	PORT_START("pot_gain_15")
	PORT_ADJUSTER(100, "CLAPS GAIN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_CLAPS)
	PORT_START("pot_gain_16")
	PORT_ADJUSTER(100, "CLICK GAIN") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(linndrum_audio_device::mix_changed), MIX_CLICK)
INPUT_PORTS_END

ROM_START(linndrum)
	ROM_REGION(0x2000, MAINCPU_TAG, 0)
	ROM_LOAD("ld_2.1_a.u14", 0x000000, 0x001000, CRC(566d720e) SHA1(91b7a515e3d18a28b7f5428765ed79114a5a00fb))
	ROM_LOAD("ld_2.1_b.u13", 0x001000, 0x001000, CRC(9c9a5520) SHA1(6e4573051254051c75f9071fd8dfcd5a9184f9cc))

	// All sample ROMs are 2732.
	// ROM file name format: sticker_label.silscreen_label.component_designation

	ROM_REGION(0x1000, "sample_mux_drum_0", 0)  // Tambourine.
	ROM_LOAD("tamb1.tamb.u25", 0x000000, 0x001000, CRC(0309eba3) SHA1(89a1910b5224a1db91c31100cfe81ebd36610027))

	ROM_REGION(0x1000, "sample_mux_drum_1", 0)  // Cabasa.
	ROM_LOAD("cbsa.u26", 0x000000, 0x001000, NO_DUMP)
	ROM_FILL(0x000000, 0x001000, 0x00)  // Silence. Remove if checksum becomes available.

	ROM_REGION(0x1000, "sample_mux_drum_2", 0)  // Claps.
	ROM_LOAD("clps.u27", 0x000000, 0x001000, NO_DUMP)
	ROM_FILL(0x000000, 0x001000, 0x00)  // Silence. Remove if checksum becomes available.

	ROM_REGION(0x1000, "sample_mux_drum_3", 0)  // Cowbell.
	ROM_LOAD("cwbl1.cwbl.u28", 0x000000, 0x001000, CRC(819d4a2c) SHA1(04d6fb88dd8751336617e50ce840fa63e6002942))

	ROM_REGION(0x1000, "sample_mux_drum_4", 0)  // Bass.
	ROM_LOAD("bass.u29", 0x000000, 0x001000, NO_DUMP)
	ROM_FILL(0x000000, 0x001000, 0x00)  // Silence. Remove if checksum becomes available.

	ROM_REGION(0x4000, "sample_mux_drum_5", 0)  // Hi hat.
	ROM_LOAD("hat1a.hat1.u30", 0x000000, 0x001000, CRC(20b35416) SHA1(9aed28369b9b3f4a088c3f2ee9c88ed4b029a3ae))
	ROM_LOAD("hat1b.hat2.u31", 0x001000, 0x001000, CRC(fb4b3bac) SHA1(99a6cada1e741a7294b9aa08df36489644253acb))
	ROM_LOAD("hat1c.hat3.u32", 0x002000, 0x001000, CRC(62d1e667) SHA1(516df46a6e1050151f4b1696568a4ffbde0eba7c))
	ROM_LOAD("hat1d.hat4.u33", 0x003000, 0x001000, CRC(01819ab1) SHA1(b0cfece5568375340eab16f8523ddb8599013310))

	ROM_REGION(0x8000, "sample_mux_drum_6", 0)  // Ride cymbal.
	ROM_LOAD("ride1a.rid1.u9", 0x000000, 0x001000, CRC(3d0a852f) SHA1(58ea6cda2ad1a8b6506ad89ba3f5c47584013ef0))
	ROM_LOAD("ride1b.rid2.u10", 0x001000, 0x001000, CRC(5bb0e082) SHA1(6f4535d08ac013d804cc2d9fd9fedca2b8515c99))
	ROM_LOAD("ride1c.rid3.u8", 0x002000, 0x001000, CRC(fa48f0e3) SHA1(a455b6d8d8dde9a7903919c87263b392f0510c8a))
	ROM_LOAD("ride1d.rid4.u7", 0x003000, 0x001000, CRC(3a8b4133) SHA1(f0a2f7a0db1024e7e263000f74d127f53bf6df94))
	ROM_LOAD("ride1e.rid5.u6", 0x004000, 0x001000, CRC(e30dc9a2) SHA1(933d2fa23a371831e779315f61e9a8f281ffab7c))
	ROM_LOAD("ride1f.rid6.u5", 0x005000, 0x001000, CRC(ba63cc66) SHA1(ba4f2fd13d89c37eab30301b96338c6a9a69ad9d))
	ROM_LOAD("ride1g.rid7.u4", 0x006000, 0x001000, CRC(d5e24b3e) SHA1(fba0fa24e8afd7686b480ace1838587fe2a6691c))
	ROM_LOAD("ride1h.rid8.u3", 0x007000, 0x001000, CRC(4ac922bb) SHA1(f86b3fbd079ef59b5e260250b95ec75829a1c31d))

	ROM_REGION(0x8000, "sample_mux_drum_7", 0)  // Crash cymbal.
	ROM_LOAD("crsh1a.crs1.u14", 0x000000, 0x001000, CRC(85ce8dc5) SHA1(a1fb4064f7d02df21ef898dab1bd4df9754f2420))
	ROM_LOAD("crsh1b.crs2.u13", 0x001000, 0x001000, CRC(3681d6f0) SHA1(1ad7e202eb9a82af03949bcf3ba281932bf83f5b))
	ROM_LOAD("crsh1c.crs3.u12", 0x002000, 0x001000, CRC(ff1a4d87) SHA1(6e10d141f9a8dbbf951d72fbdeda4e8386fd5bc8))
	ROM_LOAD("crsh1d.crs4.u15", 0x003000, 0x001000, CRC(7f623944) SHA1(03c1294df1e057442aacdb004d3ebd8e94628b15))
	ROM_LOAD("crsh1e.crs5.u16", 0x004000, 0x001000, CRC(851c306a) SHA1(4f41a31e2e8273df7664a65c5e5a3528b312627a))
	ROM_LOAD("crsh1f.crs6.u17", 0x005000, 0x001000, CRC(2fee35fe) SHA1(53b68ffe940beee4dcf568af0ed129a02a2948b9))
	ROM_LOAD("crsh1g.crs7.u18", 0x006000, 0x001000, CRC(53f939bb) SHA1(980368e122793d1318e3643b87e06e053690f0de))
	ROM_LOAD("crsh1h.crs8.u11", 0x007000, 0x001000, CRC(7b9f55c2) SHA1(dd0eb89ad89e56a28d8dccb78a979acb954968a1))

	ROM_REGION(0x1000, "sample_sidestick", 0)
	ROM_LOAD("sstk1.sstk.u78", 0x000000, 0x001000, CRC(61af39e3) SHA1(5648674854a8db80656bf729c4f353b75d101d7b))

	ROM_REGION(0x1000, "sample_snare", 0)
	ROM_LOAD("snar9.snar.u79", 0x000000, 0x001000, CRC(83478583) SHA1(bac791208270eff1f2f362511d6418873c47827c))

	ROM_REGION(0x2000, "sample_conga", 0)
	ROM_LOAD("cnga1a.cga1.u66", 0x000000, 0x001000, CRC(1a579539) SHA1(169741786c44026f2b6ef4052cccb2a27ba41e19))
	ROM_LOAD("cnga1b.cga2.u67", 0x001000, 0x001000, CRC(02434d69) SHA1(451398fbf9ac94a1773f4f40ef4ca32d3c857537))

	ROM_REGION(0x2000, "sample_tom", 0)
	ROM_LOAD("tom6a.tom1.u68", 0x000000, 0x001000, CRC(75f83e43) SHA1(386aa53311e6f8cea56e8021b19855a5ba586f52))
	ROM_LOAD("tom6b.tom2.u69", 0x001000, 0x001000, CRC(c7633ca4) SHA1(60ab77bf21897b55cc8d2844ce1cc0c65958c939))
ROM_END

}  // anonymous namespace

SYST(1982, linndrum, 0, 0, linndrum, linndrum, linndrum_state, empty_init, "Linn Electronics", "LinnDrum", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND)



linux4004.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Vas Crabb
/*
 Linux/4004

 http://dmitry.gr/?r=05.Projects&proj=35.%20Linux4004

 A MIPS-I emulator with paravirtualised hardware running on an Intel
 4004 CPU.  It looks enough like a DECstation 3100 with an R3000 CPU to
 satisfy Linux.

 TODO:
 * Emulate the VFD (Futaba M402SD10FJ or Noritake CU40025-UW6J)
 */
#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/mcs40/mcs40.h"
#include "machine/sc16is741.h"
#include "machine/spi_psram.h"
#include "machine/spi_sdcard.h"

#include "softlist_dev.h"

#include <algorithm>

#include "lnux4004.lh"


namespace {

class linux4004_state : public driver_device
{
public:
	static constexpr feature_type unemulated_features() { return feature::GRAPHICS; }

	linux4004_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "u1")
		, m_psram(*this, "u%u", 5U)
		, m_sdcard(*this, "sd")
		, m_uart(*this, "u9")
		, m_memory(*this, "memory")
		, m_status(*this, "status")
		, m_rom_bank(*this, "rom")
		, m_led_pc(*this, "pc%u", 0U)
		, m_led_sdcard(*this, "storage")
	{
	}

	void linux4004(machine_config &config) ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	template <unsigned N> void psram_sio_w(offs_t offset, u8 data, u8 mem_mask);
	template <unsigned N> void miso_w(int state);

	u8 u3_r();
	void u4002_1_3_w(u8 data);
	void u4002_1_4_w(u8 data);
	void u4002_2_3_w(u8 data);
	template <unsigned N> void led_pc_w(offs_t offset, u8 data);

	void umips_rom(address_map &map) ATTR_COLD;
	void umips_ram(address_map &map) ATTR_COLD;
	void umips_status(address_map &map) ATTR_COLD;
	void umips_rom_ports(address_map &map) ATTR_COLD;
	void umips_ram_ports(address_map &map) ATTR_COLD;

	required_device<i4004_cpu_device> m_cpu;
	required_device_array<spi_psram_device, 2> m_psram;
	required_device<spi_sdcard_device> m_sdcard;
	required_device<sc16is741a_device> m_uart;
	required_shared_ptr<u8> m_memory;
	required_shared_ptr<u8> m_status;
	required_memory_bank m_rom_bank;

	output_finder<32> m_led_pc;
	output_finder<> m_led_sdcard;

	u8 m_psram_so[2];
	u8 m_u3_in;
};


INPUT_PORTS_START(linux4004)
	PORT_START("CONF")
	PORT_CONFNAME(0x03, 0x00, "TLB Entries")
	PORT_CONFSETTING(   0x03, "4")
	PORT_CONFSETTING(   0x02, "8")
	PORT_CONFSETTING(   0x01, "12")
	PORT_CONFSETTING(   0x00, "16")
	PORT_CONFNAME(0x04, 0x00, "U4002-2-4 Installed")
	PORT_CONFSETTING(   0x00, DEF_STR(No))
	PORT_CONFSETTING(   0x04, DEF_STR(Yes))
INPUT_PORTS_END


void linux4004_state::linux4004(machine_config &config)
{
	config.set_default_layout(layout_lnux4004);

	I4004(config, m_cpu, 5.5296_MHz_XTAL / 7);
	m_cpu->set_rom_map(&linux4004_state::umips_rom);
	m_cpu->set_ram_memory_map(&linux4004_state::umips_ram);
	m_cpu->set_ram_status_map(&linux4004_state::umips_status);
	m_cpu->set_rom_ports_map(&linux4004_state::umips_rom_ports);
	m_cpu->set_ram_ports_map(&linux4004_state::umips_ram_ports);

	SPI_PSRAM(config, m_psram[0]);
	m_psram[0]->set_size(8 << 20); // supports 4 MiB to 16 MiB
	m_psram[0]->sio_cb().set(FUNC(linux4004_state::psram_sio_w<0>));

	SPI_PSRAM(config, m_psram[1]);
	m_psram[1]->set_size(4 << 20); // supports 128 KiB to 16 MiB
	m_psram[1]->sio_cb().set(FUNC(linux4004_state::psram_sio_w<1>));

	SPI_SDCARD(config, m_sdcard, 0U);
	m_sdcard->spi_miso_callback().set(FUNC(linux4004_state::miso_w<3>));

	SC16IS741A(config, m_uart, 3.072_MHz_XTAL);
	m_uart->so_cb().set(FUNC(linux4004_state::miso_w<2>));
	m_uart->irq_cb().set_inputline(m_cpu, I4004_TEST_LINE).invert();
	m_uart->tx_cb().set("rs232", FUNC(rs232_port_device::write_txd));
	m_uart->rts_cb().set("rs232", FUNC(rs232_port_device::write_rts));

	auto &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_uart, FUNC(sc16is741a_device::rx_w));
	rs232.cts_handler().set(m_uart, FUNC(sc16is741a_device::cts_w));

	SOFTWARE_LIST(config, "sdcard_list").set_original("lnux4004");
}


void linux4004_state::machine_start()
{
	m_rom_bank->configure_entries(0, 2, memregion("4004firmware")->base(), 0x1000);
	m_led_pc.resolve();
	m_led_sdcard.resolve();

	std::fill(std::begin(m_psram_so), std::end(m_psram_so), 1);
	m_u3_in = 0;

	save_item(NAME(m_psram_so));
	save_item(NAME(m_u3_in));
}

void linux4004_state::machine_reset()
{
	ioport_value const conf(ioport("CONF")->read());
	auto const tlb_empty(BIT(conf, 0, 2));
	auto const u4002_2_4(BIT(conf, 2));

	m_cpu->space(i4004_cpu_device::AS_RAM_MEMORY).unmap_readwrite(0x0000, 0x02ff);
	m_cpu->space(i4004_cpu_device::AS_RAM_STATUS).unmap_readwrite(0x00, 0xbf);
	m_cpu->space(i4004_cpu_device::AS_RAM_PORTS).unmap_readwrite(0x08, 0x0b);
	m_cpu->space(i4004_cpu_device::AS_RAM_MEMORY).install_ram(0x0000, 0x02ff - (tlb_empty * 0x40), m_memory.target());
	m_cpu->space(i4004_cpu_device::AS_RAM_STATUS).install_ram(0x00, 0xbf - (tlb_empty * 0x10), m_status.target());
	m_cpu->space(i4004_cpu_device::AS_RAM_PORTS).install_write_handler(0x08, 0x0b - tlb_empty, emu::rw_delegate(*this, FUNC(linux4004_state::led_pc_w<4>)));
	if (!u4002_2_4)
	{
		m_cpu->space(i4004_cpu_device::AS_RAM_MEMORY).unmap_readwrite(0x01c0, 0x01ff);
		m_cpu->space(i4004_cpu_device::AS_RAM_STATUS).unmap_readwrite(0x70, 0x7f);
	}

	std::fill(m_memory.begin(), m_memory.end(), 0);
	std::fill(m_status.begin(), m_status.end(), 0);

	u4002_1_3_w(0);
	u4002_1_4_w(0);
	u4002_2_3_w(0);
	for (auto &led : m_led_pc)
		led = 0;
}


template <unsigned N>
void linux4004_state::psram_sio_w(offs_t offset, u8 data, u8 mem_mask)
{
	m_psram_so[N] = BIT(data | ~mem_mask, 1);
	miso_w<0>(m_psram_so[0] & m_psram_so[1]);
}

template <unsigned N>
void linux4004_state::miso_w(int state)
{
	m_u3_in = (m_u3_in & ~(u8(1) << N)) | (u8(state ? 1 : 0) << N);
}


u8 linux4004_state::u3_r()
{
	return m_u3_in;
}

void linux4004_state::u4002_1_3_w(u8 data)
{
	m_sdcard->spi_mosi_w(BIT(~data, 0));
	m_sdcard->spi_clock_w(BIT(~data, 1));
	m_sdcard->spi_ss_w(BIT(data, 2) ? ASSERT_LINE : CLEAR_LINE);
	m_led_sdcard = BIT(~data, 2);
	m_rom_bank->set_entry(BIT(data, 3) ^ 0x01);
}

void linux4004_state::u4002_1_4_w(u8 data)
{
	m_uart->si_w(BIT(~data, 0)); // TODO: also connected to VFD
	m_uart->sclk_w(BIT(~data, 1)); // TODO: also connected to VFD
	// VFD_NCS_HV
	m_uart->cs_w(BIT(~data, 3));
}

void linux4004_state::u4002_2_3_w(u8 data)
{
	m_psram[0]->si_w(BIT(~data, 0));
	m_psram[1]->si_w(BIT(~data, 0));
	m_psram[0]->sclk_w(BIT(~data, 1));
	m_psram[1]->sclk_w(BIT(~data, 1));
	m_psram[0]->ce_w(BIT(~data, 2));
	m_psram[1]->ce_w(BIT(~data, 3));
}

template <unsigned N>
void linux4004_state::led_pc_w(offs_t offset, u8 data)
{
	for (unsigned i = 0; 4 > i; ++i)
		m_led_pc[i | ((N + offset) << 2)] = BIT(data, i);
}


void linux4004_state::umips_rom(address_map &map)
{
	map.unmap_value_low();
	map.global_mask(0x0fff);
	map(0x0000, 0x0fff).bankr(m_rom_bank);
}

void linux4004_state::umips_ram(address_map &map)
{
	map.unmap_value_low();
	map(0x0000, 0x02ff).ram().share(m_memory); // up to twelve 4002 chips
}

void linux4004_state::umips_status(address_map &map)
{
	map.unmap_value_low();
	map(0x00, 0xbf).ram().share(m_status); // up to twelve 4002 chips
}

void linux4004_state::umips_rom_ports(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x0ff);
	map(0x000, 0x0ff).r(FUNC(linux4004_state::u3_r));
}

void linux4004_state::umips_ram_ports(address_map &map)
{
	map(0x00, 0x03).w(FUNC(linux4004_state::led_pc_w<0>));
	map(0x04, 0x04).w(FUNC(linux4004_state::u4002_1_3_w));
	map(0x05, 0x05).w(FUNC(linux4004_state::u4002_1_4_w));
	map(0x06, 0x06).w(FUNC(linux4004_state::u4002_2_3_w));
	map(0x08, 0x0b).w(FUNC(linux4004_state::led_pc_w<4>));
}


ROM_START(lnux4004)
	ROM_REGION(0x2000, "4004firmware", 0)
	ROM_LOAD("umips.u4", 0x0000, 0x2000, CRC(27dd98c1) SHA1(a9d2b1990e7ae8ce4a53950430c5186d4cf55a01)) // AT28C64B
ROM_END

} // anonymous namespace


SYST( 2024, lnux4004, 0, 0, linux4004, linux4004, linux4004_state, empty_init, "Dmitry Grinberg", "Linux/4004", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



lisa.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Raphael Nabet
/*********************************************************************

    drivers/lisa.cpp

    Experimental LISA driver

    Raphael Nabet, 2000

*********************************************************************/

#include "emu.h"
#include "lisa.h"

#include "cpu/cop400/cop400.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/ap_dsk35.h"


/***************************************************************************
    ADDRESS MAP
***************************************************************************/

void lisa_state::lisa_map(address_map &map)
{
	map(0x000000, 0xffffff).rw(FUNC(lisa_state::lisa_r), FUNC(lisa_state::lisa_w));           /* no fixed map, we use an MMU */
}

void lisa_state::lisa_fdc_map(address_map &map)
{
	map.global_mask(0x1fff); // only 8k of address space
	map(0x0000, 0x03ff).ram().share("fdc_ram");             /* RAM (shared with 68000) */
	map(0x0400, 0x07ff).rw(FUNC(lisa_state::lisa_fdc_io_r), FUNC(lisa_state::lisa_fdc_io_w)); /* disk controller (IWM and TTL logic) */
	map(0x0800, 0x0fff).noprw();
	map(0x1000, 0x1fff).rom().region("fdccpu", 0);     /* ROM */
}

void lisa_state::lisa210_fdc_map(address_map &map)
{
	map.global_mask(0x1fff); // only 8k of address space
	map(0x0000, 0x03ff).ram().share("fdc_ram");             /* RAM (shared with 68000) */
	map(0x0400, 0x07ff).noprw();                                     /* nothing, or RAM wrap-around ??? */
	map(0x0800, 0x0bff).rw(FUNC(lisa_state::lisa_fdc_io_r), FUNC(lisa_state::lisa_fdc_io_w)); /* disk controller (IWM and TTL logic) */
	map(0x0c00, 0x0fff).noprw();                                     /* nothing, or IO port wrap-around ??? */
	map(0x1000, 0x1fff).rom().region("fdccpu", 0);         /* ROM */
}

/***************************************************************************
    MACHINE DRIVER
***************************************************************************/

/* Lisa1 and Lisa 2 machine */
void lisa_state::lisa(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, 20.37504_MHz_XTAL / 4); // CPUCK is nominally 5 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &lisa_state::lisa_map);
	m_maincpu->set_vblank_int("screen", FUNC(lisa_state::lisa_interrupt));

	cop421_cpu_device &iocop(COP421(config, "iocop", 3.93216_MHz_XTAL)); // U9F (I/O board)
	iocop.set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, true);
	iocop.read_si().set_constant(1); // FIXME: actually tied to VIA CA2 but both pulled up to +5
	iocop.read_g().set_constant(15);

	cop421_cpu_device &kbcop(COP421(config, "kbcop", 3932160)); // same clock as other COP?
	kbcop.set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, true);
	kbcop.read_si().set_constant(0);
	kbcop.read_g().set_constant(15);

	M6504(config, m_fdc_cpu, 2000000);        /* 16.000 MHz / 8 in when DIS asserted, 16.000 MHz / 9 otherwise (?) */
	m_fdc_cpu->set_addrmap(AS_PROGRAM, &lisa_state::lisa_fdc_map);

	config.set_maximum_quantum(attotime::from_hz(60));

	LS259(config, m_latch); // U4E (CPU board)
	m_latch->q_out_cb<0>().set(FUNC(lisa_state::diag1_w));
	m_latch->q_out_cb<1>().set(FUNC(lisa_state::diag2_w));
	m_latch->q_out_cb<2>().set(FUNC(lisa_state::seg1_w));
	m_latch->q_out_cb<3>().set(FUNC(lisa_state::seg2_w));
	m_latch->q_out_cb<4>().set(FUNC(lisa_state::setup_w));
	m_latch->q_out_cb<5>().set(FUNC(lisa_state::sfmsk_w));
	m_latch->q_out_cb<6>().set(FUNC(lisa_state::vtmsk_w));
	m_latch->q_out_cb<7>().set(FUNC(lisa_state::hdmsk_w));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(20.37504_MHz_XTAL, 896, 0, 720, 379, 0, 364);
	//m_screen->set_raw(20_MHz_XTAL, 896, 0, 720, 374, 0, 360); // according to Lisa Hardware Reference Manual
	m_screen->set_screen_update(FUNC(lisa_state::screen_update_lisa));
	m_screen->set_palette("palette");

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 1.00);

	/* nvram */
	NVRAM(config, m_nvram);
	m_nvram->set_custom_handler(FUNC(lisa_state::nvram_init));

	/* devices */
	IWM(config, m_fdc, 8_MHz_XTAL);
//  m_iwm->phases_cb().set(FUNC(mac128_state::phases_w));
//  m_iwm->devsel_cb().set(FUNC(mac128_state::devsel_w));

	applefdintf_device::add_35(config, m_floppy[0]);
	applefdintf_device::add_35(config, m_floppy[1]);

	/* software lists */
	SOFTWARE_LIST(config, "disk_list").set_original("lisa");

	/* via */
	MOS6522(config, m_via0, 20.37504_MHz_XTAL / 40); // CPU E clock (nominally 500 kHz)
	m_via0->writepa_handler().set(FUNC(lisa_state::COPS_via_out_a));
	m_via0->writepb_handler().set(FUNC(lisa_state::COPS_via_out_b));
	m_via0->ca2_handler().set(FUNC(lisa_state::COPS_via_out_ca2));
	m_via0->cb2_handler().set(FUNC(lisa_state::COPS_via_out_cb2));
	m_via0->irq_handler().set(FUNC(lisa_state::COPS_via_irq_func));

	MOS6522(config, m_via1, 20.37504_MHz_XTAL / 40); // CPU E clock (nominally 500 kHz)

	SCC8530(config, m_scc, 7833600);
}

void lisa_state::lisa210(machine_config &config)
{
	lisa(config);
	m_fdc_cpu->set_addrmap(AS_PROGRAM, &lisa_state::lisa210_fdc_map);

	/* via */
	m_via0->set_clock(1250000);
	m_via1->set_clock(1250000);
}

void lisa_state::macxl(machine_config &config)
{
	lisa210(config);
	m_screen->set_size(   768/* ???? */, 447/* ???? */);
	m_screen->set_visarea(0, 608-1, 0, 431-1);
}

/* 2008-05 FP:
Small note about natural keyboard support: currently,
- "Clear" (on the Keypad) is mapped to 'F1'
- "Enter" (different from Return) is mapped to 'F2' */

static INPUT_PORTS_START( lisa )
	PORT_START("MOUSE_X") /* Mouse - X AXIS */
	PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1)

	PORT_START("MOUSE_Y") /* Mouse - Y AXIS */
	PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1)

	/* pseudo-input ports with keyboard layout (based on pictures) */
	PORT_START("LINE0")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Mouse Button")

	PORT_START("LINE1")
	PORT_BIT(0xFFFF, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("LINE2")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Keypad Clear") PORT_CODE(KEYCODE_NUMLOCK) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD)         PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Keypad + [\xe2\x97\x80]") PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Keypad * [\xe2\x96\xb6]") PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(ASTERISK))
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)             PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)             PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)             PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Keypad / [\xe2\x96\xb2]") PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD))
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)             PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)             PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)             PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Keypad , [\xe2\x96\xbc]") PORT_CODE(KEYCODE_COMMA_PAD) PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD))    // this one would be between '+' and 'Enter' on a modern keypad.
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD)           PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)             PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)             PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD)         PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))

	PORT_START("LINE3")
	PORT_BIT(0xFFFF, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("LINE4")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)             PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)            PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)         PORT_CHAR('\\') PORT_CHAR('|') // this one would be 2nd row, 3rd key from 'P'
#if 1
	/* US layout */
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_UNUSED)
#else
	/* European layout */
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("~  `") PORT_CODE(KEYCODE_BACKSLASH2)
	/* 2008-05 FP: Differences in European Layout (based on a couple of pictures found in the web):
	- at KEYCODE_ESC, "`  ~" is replaced by "?  #"
	- Shift + 3  gives the pound symbol (\xC2\xA3)
	- There is no "\  |" key after "]  }"
	- There is an additional key at 3rd row, 3rd key from 'L', and it's  "`  ~"
	- Between Left Shift and Z there is another key, but the image is not clear on that key
	*/
#endif
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)             PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)     PORT_CHAR(8)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_MENU) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)         PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT(0x0C00, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)         PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)         PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Option") PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(RALT))
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("LINE5")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)             PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)             PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)             PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)             PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)             PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)             PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)     PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)    PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)             PORT_CHAR('l') PORT_CHAR('M')
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)             PORT_CHAR('m') PORT_CHAR('L')
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)         PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)         PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)         PORT_CHAR(' ')
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)         PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)          PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)             PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("LINE6")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)             PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)             PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)             PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)             PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)             PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)             PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)             PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)             PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)           PORT_CHAR('`') PORT_CHAR('~')
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)             PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)             PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)             PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)             PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)             PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)             PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)             PORT_CHAR('n') PORT_CHAR('N')

	PORT_START("LINE7")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)             PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)             PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)             PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)             PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)             PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)             PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)             PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)             PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)           PORT_CHAR('\t')
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)             PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)             PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)             PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Option") PORT_CODE(KEYCODE_LALT)                 PORT_CHAR(UCHAR_MAMEKEY(LALT))
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Alpha Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE  PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT)                     PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Command") PORT_CODE(KEYCODE_LCONTROL)                 PORT_CHAR(UCHAR_SHIFT_2)
INPUT_PORTS_END

	/* Note we're missing a whole bunch of lisa bootrom revisions; based on http://www.cs.dartmouth.edu/~woz/bootrom.pdf :
	?A?(10/12/82) ?B?(11/19/82) ?C?(1/28/83) D(5/12/83) 3B(9/8/83) E(10/20/83) F(12/21/83) G(2/8/84) and H(2/24/84) are known to exist. Earlier prototypes existed as well. Only F and H are dumped. */
	/* Based on http://www.cs.dartmouth.edu/~woz/bootrom.pdf and other information, at least the following systems existed:
	 * Lisa: two 890K twiggy drives, old MB (slow 500khz-clock parallel port via), old i/o board w/io40 disk rom, supports profile hdd, all bootrom revs (practically speaking, it only appeared with bootroms A, B, C, D, E or F)
	 * Lisa2 (aka Lisa2/5): one 400K SSDD drive, old MB (slow 500khz-clock parallel port via), old i/o board w/ioa8 disk rom, supports profile hdd, bootrom revs "3B", E, F, G, H
	 * Lisa2/10: one 400K SSDD drive, new MB (fast 1.25MHz-clock parallel port via), new i/o board w/ioa8 disk rom, internal widget 10mb drive (no profile hdd ports), bootrom revs F, G, H
	 * MacXL: one 400K SSDD drive, new MB (fast 1.25MHz-clock parallel port via), new i/o board w/io88 disk rom, internal widget 10mb drive (no profile hdd ports), bootrom rev 3A, different screen aspect, no serial number in video state rom so lisa office would not run
	 * Sun-remanufactured MacXL: one 800K DSDD drive, new MB (fast 1.25MHz-clock parallel port via), sun-made custom disk rom (3 revisions exist), internal custom 10mb,20mb, or 40mb drive (?no profile hdd ports?), bootrom rev 3A, different screen aspect, no serial number in video state rom so lisa apps would not run
	 */
		/* the old i/o board has a battery pack on it which often leaks and destroys the board, and has a socket for an amd 2915 math co-procesor; the new i/o board lacks the battery pack and the socket for the coprocessor, and integrates some of the function of the old twiggy-to-400k drive convertor board (which all lisa2/5s had) on it, requiring a mod to be done to the old convertor if used with a new i/o board.*/
	/* Twiggy disk format notes: twiggy disks seem to have wide '48tpi' heads, but cram 62.5tpi on the media by closely spacing the tracks! Twiggy media is DSHD-grade (needing strong magnetic field to set due to high data rate, see http://www.folklore.org/StoryView.py?project=Macintosh&story=Hide_Under_This_Desk.txt). The twiggy format is *PROBABLY* GCR encoding similar to apple2 and mac800k. The disks are 5.25" disks with TWO holes for the drive heads on both sides of the media, and the write protect notch in an unusual place (the index hole is in its normal position, though). By using variable motor speed similar to the later apple 3.5" disks, double sided disks, and tight track spacing, 871,424 bytes are stored per disk. see http://www.brouhaha.com/~eric/retrocomputing/lisa/twiggy.html
	The drives were notoriously unreliable and were replaced by a single SSDD Sony-made varialble speed 400k drive in the lisa2, which was also used on the original macintosh. */
	/* which systems used the 341-0193-A parallel interface card rom? */


ROM_START( lisa ) /* with twiggy drives, io40 i/o rom; technically any of the bootroms will work on this. */
	ROM_REGION16_BE(0x204000,"maincpu",0)   /* 68k rom and ram */
	ROM_DEFAULT_BIOS( "revh" )

	ROM_SYSTEM_BIOS( 0, "revh", "LISA Bootrom Rev H (2/24/84)")
	ROMX_LOAD("341-0175-h", 0x000000, 0x2000, CRC(adfd4516) SHA1(97a89ce1218b8aa38f69f92f6f363f435c887914), ROM_SKIP(1) | ROM_BIOS(0)) // 341-0175-H LISA Bootrom Rev H (2/24/84) (High)
	ROMX_LOAD("341-0176-h", 0x000001, 0x2000, CRC(546d6603) SHA1(2a81e4d483f50ae8a2519621daeb7feb440a3e4d), ROM_SKIP(1) | ROM_BIOS(0)) // 341-0176-H LISA Bootrom Rev H (2/24/84) (Low)

	ROM_SYSTEM_BIOS( 1, "revg", "LISA Bootrom Rev G (2/08/84)") // limited test release before release of rom rev H
	ROMX_LOAD("341-0175-g", 0x000000, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(1)) // 341-0175-G LISA Bootrom Rev G (2/08/84) (High)
	ROMX_LOAD("341-0176-g", 0x000001, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(1)) // 341-0176-G LISA Bootrom Rev G (2/08/84) (Low)

	ROM_SYSTEM_BIOS( 2, "revf", "LISA Bootrom Rev F (12/21/83)")
	ROMX_LOAD("341-0175-f", 0x000000, 0x2000, CRC(701b9dab) SHA1(b116e5fada7b9a51f1b6e25757b2814d1b2737a5), ROM_SKIP(1) | ROM_BIOS(2)) // 341-0175-F LISA Bootrom Rev F (12/21/83) (High)
	ROMX_LOAD("341-0176-f", 0x000001, 0x2000, CRC(036010b6) SHA1(ac93e6dbe4ce59396d7d191ee3e3e79a504e518f), ROM_SKIP(1) | ROM_BIOS(2)) // 341-0176-F LISA Bootrom Rev F (12/21/83) (Low)

	ROM_SYSTEM_BIOS( 3, "reve", "LISA Bootrom Rev E (10/20/83)")
	ROMX_LOAD("341-0175-e", 0x000000, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(3)) // 341-0175-E LISA Bootrom Rev E (10/20/83) (High)
	ROMX_LOAD("341-0176-e", 0x000001, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(3)) // 341-0176-E LISA Bootrom Rev E (10/20/83) (Low)

	ROM_SYSTEM_BIOS( 4, "revd", "LISA Bootrom Rev D (5/12/83)")
	ROMX_LOAD("341-0175-d", 0x000000, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(4)) // 341-0175-D LISA Bootrom Rev D (5/12/83) (High)
	ROMX_LOAD("341-0176-d", 0x000001, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(4)) // 341-0176-D LISA Bootrom Rev D (5/12/83) (Low)

	ROM_SYSTEM_BIOS( 5, "revc", "LISA Bootrom Rev C (1/28/83?)")
	ROMX_LOAD("341-0175-c", 0x000000, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(5)) // 341-0175-C LISA Bootrom Rev C (1/28/83) (High)
	ROMX_LOAD("341-0176-c", 0x000001, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(5)) // 341-0176-C LISA Bootrom Rev C (1/28/83) (Low)

	ROM_SYSTEM_BIOS( 6, "revb", "LISA Bootrom Rev B (11/19/82?)")
	ROMX_LOAD("341-0175-b", 0x000000, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(6)) // 341-0175-B LISA Bootrom Rev B (11/19/82?) (High)
	ROMX_LOAD("341-0176-b", 0x000001, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(6)) // 341-0176-B LISA Bootrom Rev B (11/19/82?) (Low)

	ROM_SYSTEM_BIOS( 7, "reva", "LISA Bootrom Rev A (10/12/82?)") // possibly only prototypes
	ROMX_LOAD("341-0175-a", 0x000000, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(7)) // 341-0175-A LISA Bootrom Rev A (10/12/82?) (High)
	ROMX_LOAD("341-0176-a", 0x000001, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(7)) // 341-0176-A LISA Bootrom Rev A (10/12/82?) (Low)

	ROM_REGION( 0x400, "iocop", 0 )
	ROM_LOAD("341-0064a.u9f", 0x000, 0x400, CRC(e6849910) SHA1(d46e67df75c9e3e773d20542fb9d5b1d2ac0fb9b))

	ROM_REGION( 0x400, "kbcop", 0 )
	ROM_LOAD("341-0064a.u9f", 0x000, 0x400, CRC(e6849910) SHA1(d46e67df75c9e3e773d20542fb9d5b1d2ac0fb9b))

	ROM_REGION(0x1000,"fdccpu",0)       // 6504 RAM and ROM
	// note: other ?prototype? revisions of this rom for the lisa probably exist as well
	ROM_LOAD( "341-0138f.bin", 0x0000, 0x1000, CRC(edd8d560) SHA1(872211d21386cd9625b3735d7682e2b2ecff05b4) )

	ROM_REGION(0x4000,"profile", 0)     // Profile/5 HDD
	ROM_LOAD_OPTIONAL("341-0080-b", 0x0000, 0x800, CRC(26df0b8d) SHA1(08f6689afb517e0a2bdaa48433003e62a66ae3c7)) // 341-0080-B z8 MCU piggyback ROM

	// TODO: the 341-0193-A parallel interface card rom should be loaded here as well for the lisa 1 and 2/5?

	ROM_REGION(0x100,"gfx1",0)      // video ROM (includes S/N)
	ROM_LOAD("vidstate.rom", 0x00, 0x100, CRC(75904783) SHA1(3b0023bd90f2ca1be0b099160a566b044856885d))
ROM_END

ROM_START( lisa2 ) /* internal apple codename was 'pepsi'; has one SSDD 400K drive, ioa8 i/o rom */
	ROM_REGION16_BE(0x204000,"maincpu",0)   /* 68k rom and ram */
	ROM_DEFAULT_BIOS( "revh" )

	ROM_SYSTEM_BIOS( 0, "revh", "LISA Bootrom Rev H (2/24/84)")
	ROMX_LOAD("341-0175-h", 0x000000, 0x2000, CRC(adfd4516) SHA1(97a89ce1218b8aa38f69f92f6f363f435c887914), ROM_SKIP(1) | ROM_BIOS(0)) // 341-0175-H LISA Bootrom Rev H (2/24/84) (High)
	ROMX_LOAD("341-0176-h", 0x000001, 0x2000, CRC(546d6603) SHA1(2a81e4d483f50ae8a2519621daeb7feb440a3e4d), ROM_SKIP(1) | ROM_BIOS(0)) // 341-0176-H LISA Bootrom Rev H (2/24/84) (Low)

	ROM_SYSTEM_BIOS( 1, "revg", "LISA Bootrom Rev G (2/08/84)") // limited test release before release of rom rev H
	ROMX_LOAD("341-0175-g", 0x000000, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(1)) // 341-0175-G LISA Bootrom Rev G (2/08/84) (High)
	ROMX_LOAD("341-0176-g", 0x000001, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(1)) // 341-0176-G LISA Bootrom Rev G (2/08/84) (Low)

	ROM_SYSTEM_BIOS( 2, "revf", "LISA Bootrom Rev F (12/21/83)")
	ROMX_LOAD("341-0175-f", 0x000000, 0x2000, CRC(701b9dab) SHA1(b116e5fada7b9a51f1b6e25757b2814d1b2737a5), ROM_SKIP(1) | ROM_BIOS(2)) // 341-0175-F LISA Bootrom Rev F (12/21/83) (High)
	ROMX_LOAD("341-0176-f", 0x000001, 0x2000, CRC(036010b6) SHA1(ac93e6dbe4ce59396d7d191ee3e3e79a504e518f), ROM_SKIP(1) | ROM_BIOS(2)) // 341-0176-F LISA Bootrom Rev F (12/21/83) (Low)

	ROM_SYSTEM_BIOS( 3, "reve", "LISA Bootrom Rev E (10/20/83)")
	ROMX_LOAD("341-0175-e", 0x000000, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(3)) // 341-0175-E LISA Bootrom Rev E (10/20/83) (High)
	ROMX_LOAD("341-0176-e", 0x000001, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(3)) // 341-0176-E LISA Bootrom Rev E (10/20/83) (Low)

	ROM_SYSTEM_BIOS( 4, "rev3b", "LISA Bootrom Rev 3B (9/8/83)") // Earliest lisa2 rom, prototype.
	ROMX_LOAD("341-0175-3b", 0x000000, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(4)) // ?label? 341-0175-3b LISA Bootrom Rev 3B (9/8/83) (High)
	ROMX_LOAD("341-0176-3b", 0x000001, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(4)) // ?label? 341-0176-3b LISA Bootrom Rev 3B (9/8/83) (Low)

	ROM_REGION( 0x400, "iocop", 0 )
	ROM_LOAD("341-0064a.u9f", 0x000, 0x400, CRC(e6849910) SHA1(d46e67df75c9e3e773d20542fb9d5b1d2ac0fb9b))

	ROM_REGION( 0x400, "kbcop", 0 )
	ROM_LOAD("341-0064a.u9f", 0x000, 0x400, CRC(e6849910) SHA1(d46e67df75c9e3e773d20542fb9d5b1d2ac0fb9b))

	ROM_REGION(0x1000,"fdccpu",0)       // 6504 RAM and ROM
	ROM_LOAD("341-0290-b", 0x0000, 0x1000, CRC(bc6364f1) SHA1(f3164923330a51366a06d9d8a4a01ec7b0d3a8aa)) // 341-0290-B LISA 2/5 Disk Rom (ioa8), supports profile on external port

	ROM_REGION(0x4000,"profile", 0)     // Profile/5 HDD
	ROM_LOAD_OPTIONAL("341-0080-b", 0x0000, 0x800, CRC(26df0b8d) SHA1(08f6689afb517e0a2bdaa48433003e62a66ae3c7)) // 341-0080-B z8 MCU piggyback ROM

	// TODO: the 341-0193-A parallel interface card rom should be loaded here as well for the lisa 1 and 2/5?

	ROM_REGION(0x100,"gfx1",0)      // video ROM (includes S/N)
	ROM_LOAD("vidstate.rom", 0x00, 0x100, CRC(75904783) SHA1(3b0023bd90f2ca1be0b099160a566b044856885d))
ROM_END

ROM_START( lisa210 ) /* newer motherboard and i/o board; has io88 i/o rom, built in widget hdd */
	ROM_REGION16_BE(0x204000,"maincpu", 0)  /* 68k rom and ram */
	ROM_DEFAULT_BIOS( "revh" )
	ROM_SYSTEM_BIOS(0, "revh", "LISA Bootrom Rev H (2/24/84)")
	ROMX_LOAD("341-0175-h", 0x000000, 0x2000, CRC(adfd4516) SHA1(97a89ce1218b8aa38f69f92f6f363f435c887914), ROM_SKIP(1) | ROM_BIOS(0)) // 341-0175-H LISA Bootrom Rev H (2/24/84) (High)
	ROMX_LOAD("341-0176-h", 0x000001, 0x2000, CRC(546d6603) SHA1(2a81e4d483f50ae8a2519621daeb7feb440a3e4d), ROM_SKIP(1) | ROM_BIOS(0)) // 341-0176-H LISA Bootrom Rev H (2/24/84) (Low)

	ROM_SYSTEM_BIOS(1, "revg", "LISA Bootrom Rev G (2/08/84)") // limited test release before release of rom rev H
	ROMX_LOAD("341-0175-g", 0x000000, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(1)) // 341-0175-G LISA Bootrom Rev G (2/08/84) (High)
	ROMX_LOAD("341-0176-g", 0x000001, 0x2000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(1)) // 341-0176-G LISA Bootrom Rev G (2/08/84) (Low)

	ROM_SYSTEM_BIOS(2, "revf", "LISA Bootrom Rev F (12/21/83)")
	ROMX_LOAD("341-0175-f", 0x000000, 0x2000, CRC(701b9dab) SHA1(b116e5fada7b9a51f1b6e25757b2814d1b2737a5), ROM_SKIP(1) | ROM_BIOS(2)) // 341-0175-F LISA Bootrom Rev F (12/21/83) (High)
	ROMX_LOAD("341-0176-f", 0x000001, 0x2000, CRC(036010b6) SHA1(ac93e6dbe4ce59396d7d191ee3e3e79a504e518f), ROM_SKIP(1) | ROM_BIOS(2)) // 341-0176-F LISA Bootrom Rev F (12/21/83) (Low)

	ROM_REGION( 0x400, "iocop", 0 )
	ROM_LOAD("341-0064a.u9f", 0x000, 0x400, CRC(e6849910) SHA1(d46e67df75c9e3e773d20542fb9d5b1d2ac0fb9b))

	ROM_REGION( 0x400, "kbcop", 0 )
	ROM_LOAD("341-0064a.u9f", 0x000, 0x400, CRC(e6849910) SHA1(d46e67df75c9e3e773d20542fb9d5b1d2ac0fb9b))

#if 1
	ROM_REGION(0x1000,"fdccpu", 0)      // 6504 RAM and ROM
	ROM_LOAD("341-0281-d", 0x0000, 0x1000, CRC(e343fe74) SHA1(a0e484ead2d2315fca261f39fff2f211ff61b0ef)) // 341-0281-D LISA 2/10 Disk Rom (io88), supports widget on internal port
#else
	ROM_REGION(0x1000,"fdccpu", 0)      // 6504 RAM and ROM
	ROM_LOAD("341-8003-c", 0x0000, 0x1000, CRC(8c67959a) SHA1(aa446b0c4acb4cb6c9d0adfbbea900fb8c04c1e9)) // 341-8003-C Sun Mac XL Disk rom for 800k drives (Rev C, from Goodwill XL) (io88800k)
	// Note: there are two earlier/alternate versions of this rom as well which are dumped */
#endif

	ROM_REGION(0x4000,"widget", 0)      // Widget HDD controller
	ROM_LOAD("341-0288-a", 0x0000, 0x800, CRC(a26ef1c6) SHA1(5aaeb6ff7f7d4f7ce7c70402f75e82533635dda4)) // 341-0288-A z8 MCU piggyback ROM
	ROM_LOAD("341-0289-d", 0x2000, 0x2000, CRC(25e86e95) SHA1(72a346c2074d2256adde491b930023ebdcb5f51a)) // 341-0289-D external rom on widget board

	ROM_REGION(0x100,"gfx1", 0)     // video ROM (includes S/N)
	ROM_LOAD("vidstate.rom", 0x00, 0x100, CRC(75904783) SHA1(3b0023bd90f2ca1be0b099160a566b044856885d))
ROM_END

ROM_START( macxl )
	ROM_REGION16_BE(0x204000,"maincpu", 0)  /* 68k rom and ram */
	ROM_LOAD16_BYTE("341-0347-a", 0x000000, 0x2000, CRC(80add605) SHA1(82215688b778d8c712a8186235f7981e3dc4dd7f)) // 341-0347-A Mac XL '3A' Bootrom Hi (boot3a.hi)
	ROM_LOAD16_BYTE("341-0346-a", 0x000001, 0x2000, CRC(edf5222f) SHA1(b0388ee8dbbc51a2d628473dc29b65ce913fcd76)) // 341-0346-A Mac XL '3A' Bootrom Lo (boot3a.lo)

	ROM_REGION( 0x400, "iocop", 0 )
	ROM_LOAD("341-0064a.u9f", 0x000, 0x400, CRC(e6849910) SHA1(d46e67df75c9e3e773d20542fb9d5b1d2ac0fb9b))

	ROM_REGION( 0x400, "kbcop", 0 )
	ROM_LOAD("341-0064a.u9f", 0x000, 0x400, CRC(e6849910) SHA1(d46e67df75c9e3e773d20542fb9d5b1d2ac0fb9b))

#if 1
	ROM_REGION(0x1000,"fdccpu", 0)      // 6504 RAM and ROM
	ROM_LOAD("341-0281-d", 0x0000, 0x1000, CRC(e343fe74) SHA1(a0e484ead2d2315fca261f39fff2f211ff61b0ef)) // 341-0281-D LISA 2/10 Disk Rom (io88), supports widget on internal port
#else
	ROM_REGION(0x1000,"fdccpu", 0)      // 6504 RAM and ROM
	ROM_LOAD("341-8003-c", 0x0000, 0x1000, CRC(8c67959a) SHA1(aa446b0c4acb4cb6c9d0adfbbea900fb8c04c1e9)) // 341-8003-C Sun Mac XL Disk rom for 800k drives (Rev C, from Goodwill XL) (io88800k)
	// Note: there are two earlier/alternate versions of this ROM as well which are dumped
#endif

	ROM_REGION(0x100,"gfx1", 0)     // video ROM (includes S/N) ; no dump known, although Lisa ROM works fine at our level of emulation
	ROM_LOAD("vidstatem.rom", 0x00, 0x100, BAD_DUMP CRC(75904783) SHA1(3b0023bd90f2ca1be0b099160a566b044856885d))
ROM_END

/*    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT          COMPANY           FULLNAME */
COMP( 1983, lisa,    0,      0,      lisa,    lisa,  lisa_state, init_lisa2,   "Apple Computer", "Lisa",         MACHINE_NOT_WORKING )
COMP( 1984, lisa2,   0,      0,      lisa,    lisa,  lisa_state, init_lisa2,   "Apple Computer", "Lisa2",        MACHINE_NOT_WORKING )
COMP( 1984, lisa210, lisa2,  0,      lisa210, lisa,  lisa_state, init_lisa210, "Apple Computer", "Lisa2/10",     MACHINE_NOT_WORKING )
COMP( 1985, macxl,   lisa2,  0,      macxl,   lisa,  lisa_state, init_mac_xl,  "Apple Computer", "Macintosh XL", /*MACHINE_NOT_WORKING*/0 )




lk3000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Lexicon LK-3000, a pocket computer.
It was also licensed to Nixdorf.

Hardware notes:
- 3870 MCU (Motorola brand) @ 4MHz(2MHz internal)
- optional external ROM or RAM
- 4*Litronix DL1414 (16*17segs total)
- 33-button keypad

The CPU and memory is on the cartridge. In theory, any hardware configuration
is possible, but it looks like they always used a 3870.

cartridge types known:
- CPU + 4/8KB ROM (ROM may be unpopulated)
- CPU + 2*4KB ROM
- CPU + 1KB battery-backed RAM (2*NEC D444)

TODO:
- external ram should be battery-backed

*******************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/f8/f8.h"
#include "machine/f3853.h"
#include "video/dl1416.h"

#include "softlist_dev.h"

// internal artwork
#include "lk3000.lh"


namespace {

class lk3000_state : public driver_device
{
public:
	lk3000_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_dl1414(*this, "dl1414_%u", 0),
		m_cart(*this, "cartslot"),
		m_inputs(*this, "IN.%u", 0),
		m_digits(*this, "digit%u", 0U)
	{ }

	void lk3000(machine_config &config);

	// CLR button is tied to MCU RESET pin
	DECLARE_INPUT_CHANGED_MEMBER(reset_button) { m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE); }

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device_array<dl1414_device, 4> m_dl1414;
	required_device<generic_slot_device> m_cart;
	required_ioport_array<8> m_inputs;
	output_finder<16> m_digits;

	u8 m_p0 = 0;
	u8 m_p1 = 0;
	u8 m_p4 = 0;
	u8 m_p5 = 0;

	bool m_has_ram = false;
	u8 m_ram[0x400];

	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
	template <int N> void update_display(offs_t offset, u16 data);

	u8 p0_r();
	void p0_w(u8 data);
	u8 p1_r();
	void p1_w(u8 data);
	u8 p4_r();
	void p4_w(u8 data);
	u8 p5_r();
	void p5_w(u8 data);
};

void lk3000_state::machine_start()
{
	m_digits.resolve();
	memset(m_ram, 0xff, 0x400);

	// register for savestates
	save_item(NAME(m_p0));
	save_item(NAME(m_p1));
	save_item(NAME(m_p4));
	save_item(NAME(m_p5));
	save_item(NAME(m_has_ram));
	save_item(NAME(m_ram));
}



/*******************************************************************************
    I/O
*******************************************************************************/

// misc

DEVICE_IMAGE_LOAD_MEMBER(lk3000_state::cart_load)
{
	u32 size = m_cart->common_get_size("rom");
	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	// extra ram (optional)
	m_has_ram = image.get_feature("ram") != nullptr;

	return std::make_pair(std::error_condition(), std::string());
}

template <int N>
void lk3000_state::update_display(offs_t offset, u16 data)
{
	m_digits[N * 4 + offset] = data;
}


// 3870 ports

u8 lk3000_state::p0_r()
{
	return m_p0;
}

void lk3000_state::p0_w(u8 data)
{
	m_p0 = data;
}

u8 lk3000_state::p1_r()
{
	u8 data = m_p1;

	// P10-P13: multiplexed inputs
	if (m_p4 & 0x10)
		data |= m_inputs[~m_p4 & 7]->read();

	// read rom data
	if (m_p0 & 0x80)
	{
		// P00-P06: A0-A6, P50-P54: A7-A11, P55 selects chip
		u16 offset = (~m_p0 & 0x7f) | (~m_p5 << 7 & 0x1f80);
		data |= ~m_cart->read_rom(offset + 0x800);
	}

	// read ram data
	if (m_has_ram && m_p4 & 0xc0)
	{
		u16 offset = (m_p0 & 0xff) | (m_p5 << 8 & 0x300);
		data |= m_ram[offset];
	}

	return data;
}

void lk3000_state::p1_w(u8 data)
{
	// P10-P15,_P15: DL1414 data
	for (int i = 0; i < 4; i++)
		m_dl1414[i]->data_w((~data & 0x3f) | (data << 1 & 0x40));

	m_p1 = data;
}

u8 lk3000_state::p4_r()
{
	return m_p4;
}

void lk3000_state::p4_w(u8 data)
{
	// P40,P41: DL1414 address
	for (int i = 0; i < 4; i++)
		m_dl1414[i]->addr_w(~data & 3);

	// P42,P43,GND,P45: 7442 to DL1414 _WR
	for (int i = 0; i < 4; i++)
		m_dl1414[i]->wr_w(BIT(1 << (~data >> 2 & 0xb), i));

	// P46: write ram data
	if (m_has_ram && ~data & m_p4 & 0x40)
	{
		u16 offset = (m_p0 & 0xff) | (m_p5 << 8 & 0x300);
		m_ram[offset] = m_p1;
	}

	// P40,P41,P42,P44: 7442 to keypad mux
	m_p4 = data;
}

u8 lk3000_state::p5_r()
{
	return m_p5;
}

void lk3000_state::p5_w(u8 data)
{
	m_p5 = data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void lk3000_state::main_map(address_map &map)
{
	map.global_mask(0x7ff);
	map(0x0000, 0x07ff).r("cartslot", FUNC(generic_slot_device::read_rom));
}

void lk3000_state::main_io(address_map &map)
{
	map(0x00, 0x00).rw(FUNC(lk3000_state::p0_r), FUNC(lk3000_state::p0_w));
	map(0x01, 0x01).rw(FUNC(lk3000_state::p1_r), FUNC(lk3000_state::p1_w));
	map(0x04, 0x07).rw("psu", FUNC(f38t56_device::read), FUNC(f38t56_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( lk3000 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_NAME("A / MET")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_NAME("B / US")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_NAME("C / X>M")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_NAME("D / K")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_NAME("J / C1")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_NAME("K / C2")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_NAME("L / RM")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_NAME("M / %")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_NAME("S / EXC")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_NAME("T / +/-")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_NAME("U / M+")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("V / 0")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('e') PORT_NAME("E / 7")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('f') PORT_NAME("F / 8")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('g') PORT_NAME("G / 9")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR('h') PORT_NAME(u8"H / ÷")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('n') PORT_NAME("N / 4")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('o') PORT_NAME("O / 5")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('p') PORT_NAME("P / 6")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR('q') PORT_NAME("Q / +")

	PORT_START("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('w') PORT_NAME("W / 1")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('x') PORT_NAME("X / 2")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('y') PORT_NAME("Y / 3")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR('z') PORT_NAME("Z / .")

	PORT_START("IN.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('?') PORT_NAME("? / P2")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_NAME("bs / P1")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR('r') PORT_NAME("R / -")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR('i') PORT_NAME(u8"I / ×")

	PORT_START("IN.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("def / P4")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t') PORT_NAME("stp / P3")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(' ') PORT_NAME("sp / =")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_NAME("f")

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(lk3000_state::reset_button), 0) PORT_CHAR(127) PORT_NAME("clr")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void lk3000_state::lk3000(machine_config &config)
{
	// basic machine hardware
	F8(config, m_maincpu, 4_MHz_XTAL/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &lk3000_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &lk3000_state::main_io);
	m_maincpu->set_irq_acknowledge_callback("psu", FUNC(f38t56_device::int_acknowledge));

	auto &psu(F38T56(config, "psu", 4_MHz_XTAL/2));
	psu.set_int_vector(0x20);
	psu.int_req_callback().set_inputline("maincpu", F8_INPUT_LINE_INT_REQ);
	psu.read_a().set(FUNC(lk3000_state::p4_r));
	psu.write_a().set(FUNC(lk3000_state::p4_w));
	psu.read_b().set(FUNC(lk3000_state::p5_r));
	psu.write_b().set(FUNC(lk3000_state::p5_w));

	// video hardware
	DL1414T(config, m_dl1414[0], 0U).update().set(FUNC(lk3000_state::update_display<0>));
	DL1414T(config, m_dl1414[1], 0U).update().set(FUNC(lk3000_state::update_display<1>));
	DL1414T(config, m_dl1414[2], 0U).update().set(FUNC(lk3000_state::update_display<2>));
	DL1414T(config, m_dl1414[3], 0U).update().set(FUNC(lk3000_state::update_display<3>));
	config.set_default_layout(layout_lk3000);

	// cartridge
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "lk3000");
	m_cart->set_device_load(FUNC(lk3000_state::cart_load));
	m_cart->set_must_be_loaded(true);

	SOFTWARE_LIST(config, "cart_list").set_original("lk3000");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( lk3000 )
	ROM_REGION( 0x0800, "maincpu", ROMREGION_ERASE00 )
	// none here, it's in the module slot
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1979, lk3000, 0,      0,      lk3000,  lk3000, lk3000_state, empty_init, "Lexicon", "LK-3000", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



llc1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/******************************************************************************

LLC1 driver by Miodrag Milanovic

2009-04-17 Preliminary driver.

2012-07-?? Updates by Robbbert

Handy addresses: (Press X then the 4 digits then Enter)
  0800 = BASIC cold start
  0803 = BASIC warm start
  13BE = display Monitor logo
This machine has an 8-digit LED display with hex keyboard,
and also a 64x16 monochrome screen with full keyboard.
The monitor uses the hex keyboard, while Basic uses the full keyboard.
Monitor output is on the digits, but the single-step command displays
a running register dump on the main screen.
There are no storage facilities, and no sound.
BASIC is integer only (-32768 to 32767), about 6k of space, and all
input is to be in uppercase. It does have minimal string capability,
but there is no $ sign, so how to create a string variable is unknown.
To exit back to the monitor, type BYE.
The user instructions of the monitor (in German) are here:
http://www.jens-mueller.org/jkcemu/llc1.html

ToDo:
- Get good dump of monitor rom, has one known bad byte, possibly more.
- In Basic, when it first scrolls, the start of the line shifts a character
  to the right. Unknown if a bug or a bad byte. (patched)


*******************************************************************************/


#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/keyboard.h"
#include "machine/z80ctc.h"
#include "machine/z80daisy.h"
#include "machine/z80pio.h"

#include "emupal.h"
#include "screen.h"

#include "llc1.lh"


namespace {

class llc1_state : public driver_device
{
public:
	llc1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ctc(*this, "ctc")
		, m_p_chargen(*this, "chargen")
		, m_vram(*this, "videoram")
		, m_inputs(*this, "X%u", 4U)
		, m_digits(*this, "digit%u", 0U)
	{ }

	void llc1(machine_config &config);
	DECLARE_INPUT_CHANGED_MEMBER(z3_button);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void kbd_put(u8 data);
	u8 port1a_r();
	u8 port2a_r();
	u8 port2b_r();
	void port1a_w(u8 data);
	void port1b_w(u8 data);
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	u8 m_porta = 0U;
	u8 m_term_data = 0U;
	required_device<z80_device> m_maincpu;
	required_device<z80ctc_device> m_ctc;
	required_region_ptr<u8> m_p_chargen;
	required_shared_ptr<u8> m_vram;
	required_ioport_array<3> m_inputs;
	output_finder<8> m_digits;
};


/* Address maps */
void llc1_state::mem_map(address_map &map)
{
	map(0x0000, 0x07ff).rom(); // Monitor ROM
	map(0x0800, 0x13ff).rom(); // BASIC ROM
	map(0x1400, 0x1bff).ram(); // RAM
	map(0x1c00, 0x1fff).ram().share("videoram"); // Video RAM
}

void llc1_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0xEC, 0xEF).rw("pio2", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0xF4, 0xF7).rw("pio1", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0xF8, 0xFB).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
}

/* Input ports */
static INPUT_PORTS_START( llc1 )
	PORT_START("X4") // out F4,BF
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
	PORT_START("X5") // out F4,DF
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("REG") PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M (Mem)") PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ST (Start)") PORT_CODE(KEYCODE_ENTER) PORT_CHAR('^') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DF (Reset)") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
	PORT_START("X6") // out F4,EF
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ES (Step)") PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DL (Go)") PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("HP (BP)") PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(llc1_state::z3_button), 0)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED) // does nothing
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(llc1_state::z3_button)
{
	m_ctc->trg3(newval);
}

void llc1_state::kbd_put(u8 data)
{
	static constexpr u8 s1[16]={0x40, 0x1e, 0x12, 0x1b, 0x19, 0x14, 0x15, 0x1d, 0x16, 0x17, 0x1c, 0x3c, 0x3f, 0x3d, 0x3e, 0x10}; // 0x20 to 0x2F
	static constexpr u8 s2[7] ={0x1a, 0x11, 0x7c, 0x13, 0x7b, 0x1f, 0x00}; // 0x3A to 0x40
	static constexpr u8 s3[6] ={0x5c, 0x00, 0x5b, 0x7e, 0x00, 0x5e}; // 0x5B to 0x60

	m_term_data = data;

	if ((data >= 0x20) && (data <= 0x2f))
		m_term_data = s1[data-0x20];
	else if ((data >= 0x3a) && (data <= 0x40))
		m_term_data = s2[data-0x3a];
	else if ((data >= 0x5b) && (data <= 0x60))
		m_term_data = s3[data-0x5b];
	else if (data >= 0x7b)
		m_term_data = 0;

	if (m_term_data)
		m_term_data |= 0x80;
}

// LLC1 BASIC keyboard
u8 llc1_state::port2b_r()
{
	if (BIT(m_term_data, 7))
	{
		m_term_data &= 0x7f;
		return 0xff;
	}
	else
		return m_term_data;
}

u8 llc1_state::port2a_r()
{
	return 0;
}

// LLC1 Monitor keyboard
u8 llc1_state::port1a_r()
{
	u8 data = 0;
	if (!BIT(m_porta, 4))
		data = m_inputs[0]->read();
	if (!BIT(m_porta, 5))
		data = m_inputs[1]->read();
	if (!BIT(m_porta, 6))
		data = m_inputs[2]->read();
	if (data & 0xf0)
		data = (data >> 4) | 0x80;

	return data;
}

void llc1_state::port1a_w(u8 data)
{
	m_porta = data;
}

void llc1_state::port1b_w(u8 data)
{
	static u8 count = 0, digit = 0;

	if (data == 0)
	{
		digit = 0;
		count = 0;
	}
	else
		count++;

	if (count == 1)
	{
		if (digit < 8)
			m_digits[digit] = data & 0x7f;
	}
	else
	if (count == 3)
	{
		count = 0;
		digit++;
	}
}

void llc1_state::machine_reset()
{
	m_term_data = 0;
}

void llc1_state::machine_start()
{
	m_digits.resolve();
	save_item(NAME(m_porta));
	save_item(NAME(m_term_data));
}

u32 llc1_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 sy=0,ma=0;

	for (u8 y = 0; y < 16; y++)
	{
		for (u8 ra = 0; ra < 8; ra++)
		{
			u16 *p = &bitmap.pix(sy++);

			for (u16 x = ma; x < ma + 64; x++)
			{
				u8 const inv = (m_vram[x] & 0x80) ? 0xff : 0;
				u8 const chr = m_vram[x] & 0x7f;

				/* get pattern of pixels for that character scanline */
				u8 const gfx = m_p_chargen[ chr | (ra << 7) ] ^ inv;

				/* Display a scanline of a character (8 pixels) */
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma+=64;
	}
	return 0;
}

static const z80_daisy_config daisy_chain[] =
{
	{ "pio2" },
	{ "ctc" },
	{ nullptr }
};

/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	128,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0, 1*128*8, 2*128*8, 3*128*8, 4*128*8, 5*128*8, 6*128*8, 7*128*8 },
	8                   /* every char takes 8 x 1 bytes */
};

static GFXDECODE_START( gfx_llc1 )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END

/* Machine driver */
void llc1_state::llc1(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(2'000'000));
	m_maincpu->set_daisy_config(daisy_chain);
	m_maincpu->set_addrmap(AS_PROGRAM, &llc1_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &llc1_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(64*8, 16*8);
	screen.set_visarea(0, 64*8-1, 0, 16*8-1);
	screen.set_screen_update(FUNC(llc1_state::screen_update));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_llc1);
	PALETTE(config, "palette", palette_device::MONOCHROME);
	config.set_default_layout(layout_llc1);

	z80pio_device& pio1(Z80PIO(config, "pio1", XTAL(2'000'000)));
	pio1.in_pa_callback().set(FUNC(llc1_state::port1a_r));
	pio1.out_pa_callback().set(FUNC(llc1_state::port1a_w));
	pio1.out_pb_callback().set(FUNC(llc1_state::port1b_w));

	z80pio_device& pio2(Z80PIO(config, "pio2", XTAL(2'000'000)));
	pio2.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	pio2.in_pa_callback().set(FUNC(llc1_state::port2a_r));
	pio2.in_pb_callback().set(FUNC(llc1_state::port2b_r));

	Z80CTC(config, m_ctc, XTAL(2'000'000));
	// timer 0 irq does digit display
	// timer 3 is kicked off by a key press, and scans the monitor keyboard.
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->zc_callback<0>().set(m_ctc, FUNC(z80ctc_device::trg1));
	m_ctc->zc_callback<1>().set(m_ctc, FUNC(z80ctc_device::trg2));

	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(llc1_state::kbd_put));
}

/* ROM definition */

ROM_START( llc1 )
	ROM_REGION( 0x1400, "maincpu", 0 )
	//ROM_LOAD( "llc1-monitor.rom", 0x0000, 0x0800, BAD_DUMP CRC(0e81378d) SHA1(0fbb6eca016d0f439ea1c9aa0cb0affb5f49ea69) )
	ROM_LOAD( "llc1mon.bin",  0x0000, 0x0800, BAD_DUMP CRC(e291dd63) SHA1(31a71bef84f7c164a270d0895cb645e078e9c6f2) )
	ROM_LOAD( "llc1_tb1.bin", 0x0800, 0x0400, CRC(0d9d4039) SHA1(b515e385af57f4faf3a9f7b4a1edd59a1c1ea260) )
	ROM_LOAD( "llc1_tb2.bin", 0x0c00, 0x0400, CRC(28bfea2a) SHA1(a68a8b87bfc931627ddd8d124b153e511477fbaf) )
	ROM_LOAD( "llc1_tb3.bin", 0x1000, 0x0400, CRC(fe5e3132) SHA1(cc3b191e41f5772a4b86b8eb0ebe6fce67872df6) )
	ROM_FILL(0x02dc, 1, 0x0f) // fix display of AF in the reg command (confirmed from monitor listing)
	ROM_FILL(0x1361, 1, 0x40) // fix scrolling in Basic

	ROM_REGION( 0x0400, "chargen", 0 )
	ROM_LOAD ("llc1_zg.bin",  0x0000, 0x0400, CRC(fa2cd659) SHA1(1fa5f9992f35929f656c4ce55ed6980c5da1772b) )
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT       COMPANY  FULLNAME  FLAGS */
COMP( 1984, llc1, 0,      0,      llc1,    llc1,  llc1_state, empty_init, "SCCH",  "LLC-1",  MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



llc2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/******************************************************************************

LLC2 driver by Miodrag Milanovic

2009-04-17 Preliminary driver.

2012-07-?? Updates by Robbbert

The BEL character plays a short tune.
In the monitor, it is case sensitive, most commands are uppercase,
 but some are lowercase.
To start Basic, the command is b. To quit basic, use BYE.
Inside Basic, it is not case-sensitive.

ToDo:
- Unofficial expansions
- Need software

*******************************************************************************/


#include "emu.h"

#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "k7659kb.h"
#include "machine/ram.h"
#include "machine/z80ctc.h"
#include "machine/z80daisy.h"
#include "machine/z80pio.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class llc2_state : public driver_device
{
public:
	llc2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_speaker(*this, "speaker")
		, m_vram(*this, "videoram")
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_p_chargen(*this, "chargen")
		, m_cass(*this, "cassette")
		, m_ctc(*this, "ctc")
		, m_bankr(*this, "bankr%u", 0U)
		, m_bankw(*this, "bankw%u", 0U)
	{ }

	void llc2(machine_config &config);

	void init_llc2();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void rom_disable_w(u8 data);
	void basic_enable_w(u8 data);
	u8 port1b_r();
	u8 port2a_r();
	void port1b_w(u8 data);
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	bool m_rv = 0;
	required_device<speaker_sound_device> m_speaker;
	required_shared_ptr<u8> m_vram;
	required_device<z80_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_region_ptr<u8> m_p_chargen;
	required_device<cassette_image_device> m_cass;
	required_device<z80ctc_device> m_ctc;
	required_memory_bank_array<2> m_bankr;
	required_memory_bank_array<2> m_bankw;
};

/* Address maps */
void llc2_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).bankr(m_bankr[0]).bankw(m_bankw[0]);
	map(0x4000, 0x5fff).bankr(m_bankr[1]).bankw(m_bankw[1]);
	map(0x6000, 0xbfff).ram();
	map(0xc000, 0xffff).ram().share("videoram");
}

void llc2_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0xE0, 0xE3).w(FUNC(llc2_state::rom_disable_w));
	map(0xE4, 0xE7).rw("pio2", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0xE8, 0xEB).rw("pio1", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0xEC, 0xEC).w(FUNC(llc2_state::basic_enable_w));
	map(0xF8, 0xFB).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
}

/* Input ports */
static INPUT_PORTS_START( llc2 )
INPUT_PORTS_END


/* Driver initialization */
void llc2_state::init_llc2()
{
	u8 *r = m_ram->pointer();
	u8 *m = memregion("maincpu")->base();

	m_bankr[0]->configure_entry(0, r);
	m_bankr[0]->configure_entry(1, m);
	m_bankw[0]->configure_entry(0, r);
	m_bankr[1]->configure_entry(0, r+0x4000);
	m_bankr[1]->configure_entry(1, m+0x4000);
	m_bankw[1]->configure_entry(0, r+0x4000);
}

void llc2_state::machine_reset()
{
	m_bankr[0]->set_entry(1);
	m_bankw[0]->set_entry(0);
	m_bankr[1]->set_entry(0);
	m_bankw[1]->set_entry(0);
}

void llc2_state::rom_disable_w(u8 data)
{
	m_bankr[0]->set_entry(0);
}

void llc2_state::basic_enable_w(u8 data)
{
	m_bankr[1]->set_entry(BIT(data, 1));
}

u8 llc2_state::port1b_r()
{
	u8 data = 0xfd;

	if (m_cass->input() > 0.03)
		data |= 0x02;

	return data;
}

void llc2_state::port1b_w(u8 data)
{
	m_cass->output(BIT(data, 0) ? -1.0 : +1.0);
	m_speaker->level_w(BIT(data, 6));
	m_rv = BIT(data, 5);
}

u8 llc2_state::port2a_r()
{
	return 0; // bit 2 low or hangs on ^Z^X^C sequence
}

void llc2_state::machine_start()
{
	save_item(NAME(m_rv));
}

u32 llc2_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u8 const inv1 = m_rv ? 0xff : 0;
	u16 sy = 0, ma = 0;

	for (u8 y = 0; y < 32; y++)
	{
		for (u8 ra = 0; ra < 8; ra++)
		{
			u8 inv = 0;
			u16 *p = &bitmap.pix(sy++);

			for (u16 x = ma; x < ma + 64; x++)
			{
				u8 chr = m_vram[x];
				if (chr==0x11) // inverse on
				{
					inv=0xff;
					chr=0x0f; // must not show
				}
				else if (chr==0x10) // inverse off
					inv=0;

				/* get pattern of pixels for that character scanline */
				u8 const gfx = m_p_chargen[ (chr << 3) | ra ] ^ inv ^ inv1;

				/* Display a scanline of a character (8 pixels) */
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma+=64;
	}
	return 0;
}

static const z80_daisy_config daisy_chain[] =
{
	{ "ctc" },
	{ "pio1" },
	{ "pio2" },
	{ nullptr }
};

/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_llc2 )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END

/* Machine driver */
void llc2_state::llc2(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 12_MHz_XTAL / 4);
	m_maincpu->set_daisy_config(daisy_chain);
	m_maincpu->set_addrmap(AS_PROGRAM, &llc2_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &llc2_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(64*8, 32*8);
	screen.set_visarea(0, 64*8-1, 0, 32*8-1);
	screen.set_screen_update(FUNC(llc2_state::screen_update));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_llc2);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	z80pio_device& pio1(Z80PIO(config, "pio1", 12_MHz_XTAL / 4));
	pio1.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	pio1.in_pa_callback().set(K7659_KEYBOARD_TAG, FUNC(k7659_keyboard_device::read));
	pio1.in_pb_callback().set(FUNC(llc2_state::port1b_r));
	pio1.out_pb_callback().set(FUNC(llc2_state::port1b_w));

	z80pio_device& pio2(Z80PIO(config, "pio2", 12_MHz_XTAL / 4));
	pio2.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	pio2.in_pa_callback().set(FUNC(llc2_state::port2a_r));

	Z80CTC(config, m_ctc, 12_MHz_XTAL / 4);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->set_clk<0>(12_MHz_XTAL / 8);
	m_ctc->set_clk<1>(12_MHz_XTAL / 8);
	m_ctc->set_clk<2>(50);      // comes from deep in the video section, assumed to be 50Hz
	m_ctc->zc_callback<2>().set(m_ctc, FUNC(z80ctc_device::trg3));

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	K7659_KEYBOARD(config, K7659_KEYBOARD_TAG, 0);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("64K");
}

/* ROM definition */
ROM_START( llc2 )
	ROM_REGION( 0x6000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "scchmon_91.d35", 0x0000, 0x1000, CRC(218d8236) SHA1(b8297272cc79751afc2eb8688d99b40691346dcb) )
	ROM_LOAD( "gsbasic.bin",    0x4000, 0x2000, CRC(78a5f388) SHA1(e7b475b98dce36b24540ad11eb89046ddb4f02af) )

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD ("llc2font.d17",   0x0000, 0x0800, CRC(ce53e55d) SHA1(da23d93f14a8a1f8d82bb72470a96b0bfd81ed1b) )
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT       COMPANY  FULLNAME  FLAGS */
COMP( 1984, llc2, llc1,   0,      llc2,    llc2,  llc2_state, init_llc2, "SCCH",  "LLC-2",  MACHINE_SUPPORTS_SAVE )



lms46.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/******************************************************************************

    Skeleton driver for Litek LMS46 SBC.

    No additional information is known about this system.

******************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/msm5832.h"


namespace {

class lms46_state : public driver_device
{
public:
	lms46_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rtc(*this, "rtc")
	{
	}

	void lms46(machine_config &mconfig);

private:
	u8 rtc_r(offs_t offset);
	void rtc_w(offs_t offset, u8 data);
	u8 busy_r();
	void misc_control_w(u8 data);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<msm5832_device> m_rtc;
};


u8 lms46_state::rtc_r(offs_t offset)
{
	m_rtc->cs_w(1);
	m_rtc->read_w(1);
	m_rtc->address_w(offset);
	u8 data = m_rtc->data_r();
	m_rtc->read_w(0);
	m_rtc->cs_w(0);
	return data;
}

void lms46_state::rtc_w(offs_t offset, u8 data)
{
	m_rtc->cs_w(1);
	m_rtc->address_w(offset);
	m_rtc->data_w(data & 0x0f);
	m_rtc->write_w(1);
	m_rtc->write_w(0);
	m_rtc->cs_w(0);
}

u8 lms46_state::busy_r()
{
	// Code loops endlessly until bit 0 clears
	return 0;
}

void lms46_state::misc_control_w(u8 data)
{
	m_rtc->hold_w(BIT(data, 5));
}

void lms46_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).rom().region("bios", 0).nopw();
	map(0x8000, 0x87ff).ram(); // possibly NVRAM
	map(0x8800, 0x8fff).ram();
}

void lms46_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x0f).rw(FUNC(lms46_state::rtc_r), FUNC(lms46_state::rtc_w));
	map(0x10, 0x10).r(FUNC(lms46_state::busy_r));
	map(0x20, 0x20).w(FUNC(lms46_state::misc_control_w));
	map(0x30, 0x30).nopr(); // Watchdog reset? (value unused)
}


static INPUT_PORTS_START(lms46)
INPUT_PORTS_END

void lms46_state::lms46(machine_config &config)
{
	Z80(config, m_maincpu, 4000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &lms46_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &lms46_state::io_map);

	MSM5832(config, m_rtc, 32768);
}


ROM_START(lms46)
	ROM_REGION(0x4000, "bios", 0)
	// Original dump has been extensively hand-patched between 0000 and 00EB where bit 3 was stuck high.
	// More single-bit errors in this area are likely, and some current guesses might be wrong.
	// Code organization also suggests the use of two separate 8K ROMs rather than one 16K ROM.
	ROM_LOAD("lms4002.rom", 0x0000, 0x4000, CRC(d9bc2384) SHA1(979038c53f5611bb9078d6a44e1c521093207881) BAD_DUMP)
ROM_END

} // anonymous namespace


COMP(1988, lms46, 0, 0, lms46, lms46, lms46_state, empty_init, "Litek Information Systems", "LMS46-V9", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



lnw80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************
Memory map

0000-37ff ROM                 R   D0-D7
37de      UART status         R/W D0-D7
37df      UART data           R/W D0-D7
37e0      for the realtime clock
37e1      select disk drive 0         W
37e2      cassette drive latch address    W
37e3      select disk drive 1         W
37e4      select which cassette unit      W   D0-D1 (D0 selects unit 1, D1 selects unit 2)
37e5      select disk drive 2         W
37e7      select disk drive 3         W
37e0-37e3 floppy motor            W   D0-D3
          or floppy head select   W   D3
37e8      send a byte to printer  W   D0-D7
37e8      read printer status     R   D7
37ec-37ef FDC WD179x              R/W D0-D7
37ec      command             W   D0-D7
37ec      status              R   D0-D7
37ed      track               R/W D0-D7
37ee      sector              R/W D0-D7
37ef      data                R/W D0-D7
3800-38ff keyboard matrix         R   D0-D7
3900-3bff unused - kbd mirrored
3c00-3fff video RAM               R/W D0-D5,D7 (or D0-D7)
4000-ffff RAM

Interrupts:
- IRQ mode 1
- NMI

Has non-addressable links to set the baud rate. Receive and Transmit clocks are tied together.

Cassette baud rates:
- 500 baud @1.77MHz and 1000 baud @4MHz.

I/O ports
FF:
- bits 0 and 1 are for writing a cassette
- bit 2 must be high to turn the cassette motor on
- bit 3 switches the display between 64 or 32 characters per line
- bit 6 remembers the 32/64 screen mode (inverted)
- bit 7 is for reading from a cassette

FE:
- bit 0 is for selecting inverse video of the whole screen
- bit 1 chooses text or graphics screen
- bit 2 enables colour
- bit 3 is for selecting roms (low) or 16k hires area (high)

Shift and Right-arrow will enable 32 cpl.

SYSTEM commands:
    - Press Break (End key) to quit
    - Press Enter to exit with error
    - xxxx to load program xxxx from tape.
    - / to execute last program loaded
    - /nnnnn to execute program at nnnnn (decimal)

About the RTC - The hardware side exists, but special software is needed to get the clock to work.
    By default, nothing happens.

********************************************************************************************************

To Do / Status:
--------------

- basically works
- hi-res and colour are coded and pass the test suite, but need some real programs to check with.
- investigate expansion-box
- none of my collection of lnw80-specific floppies will work; some crash MAME

*******************************************************************************************************/

#include "emu.h"
#include "trs80.h"
#include "trs80_quik.h"
#include "machine/input_merger.h"
#include "formats/td0_dsk.h"
#include "softlist_dev.h"
#include "utf8.h"

namespace {

class lnw80_state : public trs80_state
{
public:
	lnw80_state(const machine_config &mconfig, device_type type, const char *tag)
		: trs80_state(mconfig, type, tag)
		, m_p_gfxram(*this, "gfxram")
		, m_lnw_bank(*this, "lnw_banked_mem")
	{ }

	void lnw80(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	static void floppy_formats(format_registration &fr);
	void lnw80_fe_w(u8 data);
	u8 lnw80_fe_r();
	void lnw80_palette(palette_device &palette) const;
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void lnw80_io(address_map &map) ATTR_COLD;
	void lnw80_mem(address_map &map) ATTR_COLD;

	u8 m_lnw_mode = 0U;
	required_shared_ptr<u8> m_p_gfxram;
	memory_view m_lnw_bank;
};


void lnw80_state::lnw80_mem(address_map &map)
{
	map(0x0000, 0x3fff).view(m_lnw_bank);
	m_lnw_bank[0](0x0000, 0x2fff).rom().region("maincpu", 0);
	m_lnw_bank[0](0x37de, 0x37de).rw(FUNC(lnw80_state::sys80_f9_r), FUNC(lnw80_state::sys80_f8_w));
	m_lnw_bank[0](0x37e0, 0x37e3).rw(FUNC(lnw80_state::irq_status_r), FUNC(lnw80_state::motor_w));
	m_lnw_bank[0](0x37e4, 0x37e7).w(FUNC(lnw80_state::cassunit_w));
	m_lnw_bank[0](0x37e8, 0x37eb).rw(FUNC(lnw80_state::printer_r), FUNC(lnw80_state::printer_w));
	m_lnw_bank[0](0x37ec, 0x37ef).rw(FUNC(lnw80_state::fdc_r), FUNC(lnw80_state::fdc_w));
	m_lnw_bank[0](0x3800, 0x3bff).r(FUNC(lnw80_state::keyboard_r));
	m_lnw_bank[0](0x3c00, 0x3fff).ram().share(m_p_videoram);
	m_lnw_bank[1](0x0000, 0x3fff).ram().share(m_p_gfxram);
	map(0x4000, 0xffff).ram();
}

void lnw80_state::lnw80_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0xe8, 0xe8).rw(FUNC(lnw80_state::port_e8_r), FUNC(lnw80_state::port_e8_w));
	map(0xe9, 0xe9).portr("E9");
	map(0xea, 0xea).rw(FUNC(lnw80_state::port_ea_r), FUNC(lnw80_state::port_ea_w));
	map(0xeb, 0xeb).rw(m_uart, FUNC(ay31015_device::receive), FUNC(ay31015_device::transmit));
	map(0xfe, 0xfe).rw(FUNC(lnw80_state::lnw80_fe_r), FUNC(lnw80_state::lnw80_fe_w));
	map(0xff, 0xff).rw(FUNC(lnw80_state::port_ff_r), FUNC(lnw80_state::port_ff_w));
}

/**************************************************************************
   w/o SHIFT                             with SHIFT
   +-------------------------------+     +-------------------------------+
   | 0   1   2   3   4   5   6   7 |     | 0   1   2   3   4   5   6   7 |
+--+---+---+---+---+---+---+---+---+  +--+---+---+---+---+---+---+---+---+
|0 | @ | A | B | C | D | E | F | G |  |0 | ` | a | b | c | d | e | f | g |
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|1 | H | I | J | K | L | M | N | O |  |1 | h | i | j | k | l | m | n | o |
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|2 | P | Q | R | S | T | U | V | W |  |2 | p | q | r | s | t | u | v | w |
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|3 | X | Y | Z | [ | \ | ] | ^ | _ |  |3 | x | y | z | { | | | } | ~ |   |
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|4 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |  |4 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|5 | 8 | 9 | : | ; | , | - | . | / |  |5 | 8 | 9 | * | + | < | = | > | ? |
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|6 |ENT|CLR|BRK|UP |DN |LFT|RGT|SPC|  |6 |ENT|CLR|BRK|UP |DN |LFT|RGT|SPC|
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|7 |SHF|   |   |   |   |   |   |   |  |7 |SHF|   |   |   |   |   |   |   |
+--+---+---+---+---+---+---+---+---+  +--+---+---+---+---+---+---+---+---+

***************************************************************************/

static INPUT_PORTS_START( lnw80 )
	PORT_START("LINE0")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('@')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)          PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)          PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)          PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)          PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)          PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)          PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)          PORT_CHAR('G') PORT_CHAR('g')

	PORT_START("LINE1")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)          PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)          PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)          PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)          PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)          PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)          PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)          PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)          PORT_CHAR('O') PORT_CHAR('o')

	PORT_START("LINE2")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)          PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)          PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)          PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)          PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)          PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)          PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)          PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)          PORT_CHAR('W') PORT_CHAR('w')

	PORT_START("LINE3")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)          PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)          PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)          PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)        PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)        PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("_") PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR('_')
	PORT_BIT(0x28, 0x00, IPT_UNUSED)

	PORT_START("LINE4")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)          PORT_CHAR('0')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("LINE5")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME(": *") PORT_CODE(KEYCODE_MINUS)    PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("; +") PORT_CODE(KEYCODE_COLON)    PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA)    PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("- =") PORT_CODE(KEYCODE_EQUALS)   PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP)     PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH)    PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("LINE6")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)      PORT_CHAR(13)
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("Clear") PORT_CODE(KEYCODE_HOME)       PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_END)        PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)        PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	/* backspace do the same as cursor left */
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_BACKSPACE)   PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')

	PORT_START("LINE7")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("Control") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0xee, 0x00, IPT_UNUSED)

	PORT_START("RESET")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(PAUSE)) PORT_WRITE_LINE_DEVICE_MEMBER("nmigate", FUNC(input_merger_device::in_w<0>))

	PORT_START("CONFIG")
	PORT_CONFNAME(    0x80, 0x00,   "Floppy Disc Drives")
	PORT_CONFSETTING(   0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(   0x80, DEF_STR( On ) )
	PORT_CONFNAME(    0x40, 0x00,   "CPU Speed")
	PORT_CONFSETTING(   0x00, "1.77 MHz" )
	PORT_CONFSETTING(   0x40, "4 MHz" )

	PORT_START("E9")    // these are the power-on uart settings
	PORT_BIT(0x07, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_DIPNAME( 0x88, 0x08, "Parity")
	PORT_DIPSETTING(    0x08, DEF_STR(None))
	PORT_DIPSETTING(    0x00, "Odd")
	PORT_DIPSETTING(    0x80, "Even")
	PORT_DIPNAME( 0x10, 0x10, "Stop Bits")
	PORT_DIPSETTING(    0x10, "2")
	PORT_DIPSETTING(    0x00, "1")
	PORT_DIPNAME( 0x60, 0x60, "Bits")
	PORT_DIPSETTING(    0x00, "5")
	PORT_DIPSETTING(    0x20, "6")
	PORT_DIPSETTING(    0x40, "7")
	PORT_DIPSETTING(    0x60, "8")

	PORT_START("BAUD")
	PORT_DIPNAME( 0xff, 0x06, "Baud Rate")
	PORT_DIPSETTING(    0x00, "110")
	PORT_DIPSETTING(    0x01, "300")
	PORT_DIPSETTING(    0x02, "600")
	PORT_DIPSETTING(    0x03, "1200")
	PORT_DIPSETTING(    0x04, "2400")
	PORT_DIPSETTING(    0x05, "4800")
	PORT_DIPSETTING(    0x06, "9600")
	PORT_DIPSETTING(    0x07, "19200")
INPUT_PORTS_END


/*************************************
 *
 *              Port handlers.
 *
 *************************************/


u8 lnw80_state::lnw80_fe_r()
{
	return m_lnw_mode;
}


/* lnw80 can switch out all the devices, roms and video ram to be replaced by graphics ram. */
void lnw80_state::lnw80_fe_w(u8 data)
{
/* lnw80 video options
    d3 bankswitch lower 16k between roms and hires ram (1=hires)
    d2 enable colour    \
    d1 hres             /   these 2 are the bits from the MODE command of LNWBASIC
    d0 inverse video (entire screen) */

	m_lnw_mode = data;

	m_lnw_bank.select(BIT(data, 3));
}


/*************************************
 *  Machine              *
 *************************************/

void lnw80_state::machine_start()
{
	save_item(NAME(m_cpl));
	save_item(NAME(m_irq));
	save_item(NAME(m_mask));
	save_item(NAME(m_reg_load));
	save_item(NAME(m_lnw_mode));
	save_item(NAME(m_cassette_data));
	save_item(NAME(m_old_cassette_val));
	save_item(NAME(m_cols));
	save_item(NAME(m_timeout));

	m_reg_load=1;

	m_cassette_data_timer = timer_alloc(FUNC(lnw80_state::cassette_data_callback), this);
	m_cassette_data_timer->adjust( attotime::zero, 0, attotime::from_hz(11025) );
}

void lnw80_state::machine_reset()
{
	m_cpl = 0;
	m_cols = 0xff;
	m_cassette_data = false;
	const u16 s_bauds[8]={ 110, 300, 600, 1200, 2400, 4800, 9600, 19200 };
	u16 s_clock = s_bauds[m_io_baud->read()] << 4;
	m_uart_clock->set_unscaled_clock(s_clock);

	m_maincpu->set_unscaled_clock(BIT(m_io_config->read(), 6) ? (16_MHz_XTAL / 4) : (16_MHz_XTAL / 9));  // HI-LO switch
	m_reg_load = 1;
	lnw80_fe_w(0);
}

/* 8-bit video, 64/80 characters per line = lnw80 */
u32 lnw80_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	static const u16 rows[] = { 0, 0x200, 0x100, 0x300, 1, 0x201, 0x101, 0x301 };
	u16 sy=0,ma=0;
	bool inv = BIT(m_lnw_mode, 0);
	u8 mode = BIT(m_lnw_mode, 1, 2);
	u8 cols = BIT(mode, 0) ? 80 : 64;
	u8 skip = 1;
	if (mode == 0)
	{
		skip = m_cpl ? 2 : 1;
		if (skip == 2)
			cols >>= 1;
	}

	if (cols != m_cols)
	{
		m_cols = cols;
		screen.set_visible_area(0, cols*6-1, 0, 16*12-1);
	}

	u8 bg=7,fg=0;
	if (inv)
	{
		bg = 0;
		fg = 7;
	}

	switch (mode)
	{
		case 0:                 // MODE 0
			for (u16 y = 0; y < 16; y++)
			{
				for (u16 ra = 0; ra < 12; ra++)
				{
					u16 *p = &bitmap.pix(sy++);

					for (u16 x = ma; x < ma + 64; x+=skip)
					{
						u8 chr = m_p_videoram[x];

						if (chr & 0x80)
						{
							u8 gfxbit = (ra & 0x0c)>>1;
							/* Display one line of a lores character (6 pixels) */
							*p++ = BIT(chr, gfxbit) ? fg : bg;
							*p++ = BIT(chr, gfxbit) ? fg : bg;
							*p++ = BIT(chr, gfxbit) ? fg : bg;
							gfxbit++;
							*p++ = BIT(chr, gfxbit) ? fg : bg;
							*p++ = BIT(chr, gfxbit) ? fg : bg;
							*p++ = BIT(chr, gfxbit) ? fg : bg;
						}
						else
						{
							/* get pattern of pixels for that character scanline */
							u8 gfx;
							if (ra < 8)
								gfx = m_p_chargen[(chr<<1) | rows[ra] ];
							else
								gfx = 0;

							/* Display a scanline of a character (6 pixels) */
							*p++ = BIT(gfx, 2) ? fg : bg;
							*p++ = BIT(gfx, 1) ? fg : bg;
							*p++ = BIT(gfx, 6) ? fg : bg;
							*p++ = BIT(gfx, 7) ? fg : bg;
							*p++ = BIT(gfx, 5) ? fg : bg;
							*p++ = BIT(gfx, 3) ? fg : bg;
						}
					}
				}
				ma+=64;
			}
			break;

		case 1:                  // MODE 1
			for (u16 y = 0; y < 0x400; y+=0x40)
			{
				for (u16 ra = 0; ra < 0x3000; ra+=0x400)
				{
					u16 *p = &bitmap.pix(sy++);

					for (u16 x = 0; x < 0x40; x++)
					{
						u8 gfx = m_p_gfxram[ y | x | ra];
						/* Display 6 pixels in normal region */
						*p++ = BIT(gfx, 0) ? fg : bg;
						*p++ = BIT(gfx, 1) ? fg : bg;
						*p++ = BIT(gfx, 2) ? fg : bg;
						*p++ = BIT(gfx, 3) ? fg : bg;
						*p++ = BIT(gfx, 4) ? fg : bg;
						*p++ = BIT(gfx, 5) ? fg : bg;
					}

					for (u16 x = 0; x < 0x10; x++)
					{
						u8 gfx = m_p_gfxram[ 0x3000 | x | (ra & 0xc00) | ((ra & 0x3000) >> 8)];
						/* Display 6 pixels in extended region */
						*p++ = BIT(gfx, 0) ? fg : bg;
						*p++ = BIT(gfx, 1) ? fg : bg;
						*p++ = BIT(gfx, 2) ? fg : bg;
						*p++ = BIT(gfx, 3) ? fg : bg;
						*p++ = BIT(gfx, 4) ? fg : bg;
						*p++ = BIT(gfx, 5) ? fg : bg;
					}
				}
			}
			break;

		case 2:                  // MODE 2
			/* it seems the text video ram can have an effect in this mode,
			    not explained clearly, so not emulated */
			for (u16 y = 0; y < 0x400; y+=0x40)
			{
				for (u16 ra = 0; ra < 0x3000; ra+=0x400)
				{
					u16 *p = &bitmap.pix(sy++);

					for (u16 x = 0; x < 0x40; x++)
					{
						u8 gfx = m_p_gfxram[ y | x | ra];
						/* Display 6 pixels in normal region */
						fg = BIT(gfx, 3, 3);
						*p++ = fg;
						*p++ = fg;
						*p++ = fg;
						fg = BIT(gfx, 0, 3);
						*p++ = fg;
						*p++ = fg;
						*p++ = fg;
					}
				}
			}
			break;

		case 3:                  // MODE 3
			/* the manual does not explain at all how colour is determined
			    for the extended area. Further, the background colour
			    is not mentioned anywhere. Black is assumed. */
			for (u16 y = 0; y < 0x400; y+=0x40)
			{
				for (u16 ra = 0; ra < 0x3000; ra+=0x400)
				{
					u16 *p = &bitmap.pix(sy++);

					for (u16 x = 0; x < 0x40; x++)
					{
						u8 gfx = m_p_gfxram[ y | x | ra];
						fg = BIT(m_p_videoram[ x | y ], 3, 3);
						/* Display 6 pixels in normal region */
						*p++ = BIT(gfx, 0) ? fg : bg;
						*p++ = BIT(gfx, 1) ? fg : bg;
						*p++ = BIT(gfx, 2) ? fg : bg;
						fg = BIT(m_p_videoram[ 0x3c00 | x | y ], 0, 3);
						*p++ = BIT(gfx, 3) ? fg : bg;
						*p++ = BIT(gfx, 4) ? fg : bg;
						*p++ = BIT(gfx, 5) ? fg : bg;
					}

					for (u16 x = 0; x < 0x10; x++)
					{
						u8 gfx = m_p_gfxram[ 0x3000 | x | (ra & 0xc00) | ((ra & 0x3000) >> 8)];
						fg = BIT(m_p_gfxram[ 0x3c00 | x | y ], 3, 3);
						/* Display 6 pixels in extended region */
						*p++ = BIT(gfx, 0) ? fg : bg;
						*p++ = BIT(gfx, 1) ? fg : bg;
						*p++ = BIT(gfx, 2) ? fg : bg;
						fg = BIT(m_p_gfxram[ 0x3c00 | x | y ], 0, 3);
						*p++ = BIT(gfx, 3) ? fg : bg;
						*p++ = BIT(gfx, 4) ? fg : bg;
						*p++ = BIT(gfx, 5) ? fg : bg;
					}
				}
			}
			break;
	}
	return 0;
}

/***************************************************************************
  Palettes
***************************************************************************/

/* Levels are unknown - guessing */
static constexpr rgb_t lnw80_pens[] =
{
	{ 220, 220, 220 }, // white
	{   0, 175,   0 }, // green
	{ 200, 200,   0 }, // yellow
	{ 255,   0,   0 }, // red
	{ 255,   0, 255 }, // magenta
	{   0,   0, 175 }, // blue
	{   0, 255, 255 }, // cyan
	{   0,   0,   0 }  // black
};

void lnw80_state::lnw80_palette(palette_device &palette) const
{
	palette.set_pen_colors(0, lnw80_pens);
}

/**************************** F4 CHARACTER DISPLAYER ***********************************************************/
static const gfx_layout lnw80_charlayout =
{
	8, 8,           /* 8 x 8 characters */
	128,            /* 128 characters */
	1,          /* 1 bits per pixel */
	{ 0 },          /* no bitplanes */
	/* x offsets */
	{ 7, 5, 6, 1, 0, 2, 4, 3 },
	/* y offsets */
	{  0*8, 512*8, 256*8, 768*8, 1*8, 513*8, 257*8, 769*8 },
	8*2        /* every char takes 8 bytes */
};

static GFXDECODE_START(gfx_lnw80)
	GFXDECODE_ENTRY( "chargen", 0, lnw80_charlayout, 0, 4 )
GFXDECODE_END


void lnw80_state::floppy_formats(format_registration &fr)
{
	fr.add(FLOPPY_JV1_FORMAT);
	fr.add(FLOPPY_TD0_FORMAT);
}

static void lnw80_floppies(device_slot_interface &device)
{
	device.option_add("sssd", FLOPPY_525_QD); // QD allows the 80-track boot disks to work.
}


void lnw80_state::lnw80(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 16_MHz_XTAL / 9);
	m_maincpu->set_addrmap(AS_PROGRAM, &lnw80_state::lnw80_mem);
	m_maincpu->set_addrmap(AS_IO, &lnw80_state::lnw80_io);
	m_maincpu->halt_cb().set("nmigate", FUNC(input_merger_device::in_w<1>));

	input_merger_device &nmigate(INPUT_MERGER_ANY_HIGH(config, "nmigate"));
	nmigate.output_handler().set_inputline(m_maincpu, INPUT_LINE_NMI); // TODO: also causes SYSRES on expansion bus

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	// LNW80 Theory of Operations gives H and V periods as 15.750kHz and 59.66Hz, probably due to rounding the calculated ~15.7468kHz to 4 figures
	screen.set_raw(3.579545_MHz_XTAL * 3, 682, 0, 480, 264, 0, 192); // 10.738MHz generated by tank circuit (top left of page 2 of schematics)
	screen.set_screen_update(FUNC(lnw80_state::screen_update));
	screen.set_palette("palette");
	PALETTE(config, "palette", FUNC(lnw80_state::lnw80_palette), 8);
	GFXDECODE(config, "gfxdecode", "palette", gfx_lnw80);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	/* devices */
	CASSETTE(config, m_cassette);
	m_cassette->set_formats(trs80l2_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("trs80_cass");

	TRS80_QUICKLOAD(config, "quickload", m_maincpu, attotime::from_seconds(1));

	FD1771(config, m_fdc, 4_MHz_XTAL / 4);
	m_fdc->intrq_wr_callback().set(FUNC(lnw80_state::intrq_w));

	FLOPPY_CONNECTOR(config, m_floppy[0], lnw80_floppies, "sssd", lnw80_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], lnw80_floppies, "sssd", lnw80_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[2], lnw80_floppies, nullptr, lnw80_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[3], lnw80_floppies, nullptr, lnw80_state::floppy_formats).enable_sound(true);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit7));
	m_centronics->perror_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit6));
	m_centronics->select_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit5));
	m_centronics->fault_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit4));

	INPUT_BUFFER(config, m_cent_status_in);

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	CLOCK(config, m_uart_clock, 19200 * 16);
	m_uart_clock->signal_handler().set(m_uart, FUNC(ay31015_device::write_rcp));
	m_uart_clock->signal_handler().append(m_uart, FUNC(ay31015_device::write_tcp));

	AY31015(config, m_uart);
	m_uart->read_si_callback().set("rs232", FUNC(rs232_port_device::rxd_r));
	m_uart->write_so_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	//MCFG_AY31015_WRITE_DAV_CB(WRITELINE( , , ))
	m_uart->set_auto_rdav(true);
	RS232_PORT(config, "rs232", default_rs232_devices, nullptr);

	SOFTWARE_LIST(config, "cass_list").set_original("trs80_cass").set_filter("1"); // L
	SOFTWARE_LIST(config, "quik_list").set_original("trs80_quik").set_filter("1"); // L
	SOFTWARE_LIST(config, "flop_list").set_original("trs80_flop").set_filter("1"); // L
}


/***************************************************************************

  Game driver(s)

***************************************************************************/

ROM_START(lnw80)
	ROM_REGION(0x3000, "maincpu", 0)
	ROM_LOAD("lnw_a.u78",      0x0000, 0x0800, CRC(e09f7e91) SHA1(cd28e72efcfebde6cf1c7dbec4a4880a69e683da) )
	ROM_LOAD("lnw_a1.u75",     0x0800, 0x0800, CRC(ac297d99) SHA1(ccf31d3f9d02c3b68a0ee3be4984424df0e83ab0) )
	ROM_LOAD("lnw_b.u79",      0x1000, 0x0800, CRC(c4303568) SHA1(13e3d81c6f0de0e93956fa58c465b5368ea51682) )
	ROM_LOAD("lnw_b1.u76",     0x1800, 0x0800, CRC(3a5ea239) SHA1(8c489670977892d7f2bfb098f5df0b4dfa8fbba6) )
	ROM_LOAD("lnw_c.u80",      0x2000, 0x0800, CRC(2ba025d7) SHA1(232efbe23c3f5c2c6655466ebc0a51cf3697be9b) )
	ROM_LOAD("lnw_c1.u77",     0x2800, 0x0800, CRC(ed547445) SHA1(20102de89a3ee4a65366bc2d62be94da984a156b) )

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD("lnw_chr.u100",   0x0000, 0x0800, CRC(c89b27df) SHA1(be2a009a07e4378d070002a558705e9a0de59389) )

	ROM_REGION(0x0020, "proms", 0)
	ROM_LOAD_OPTIONAL("lnw_ntsc.u130",  0x0000, 0x0020, CRC(b990a207) SHA1(1a1cc3150cbfed76b1c88c0d561f9bee954f3234) )
ROM_END

} // anonymous namespace

//    YEAR  NAME         PARENT    COMPAT    MACHINE   INPUT    CLASS        INIT        COMPANY          FULLNAME    FLAGS
COMP( 1981, lnw80,       0,        trs80l2,  lnw80,    lnw80,   lnw80_state, empty_init, "LNW Research",  "LNW-80",   MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



lola8a.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************

Lola 8 and Lola 8A

Ivo Lola Ribar Institute

2013-08-28 Skeleton driver.


Lola 8 and Lola 8 NK
====================

Lola 8 is predecessor Lola 8A, but essentially a different computer with more
differences than similarities.

D1 and D2 are sockets for ROMs, but 4K or 8K can be used (solder jumper)
D3 and D4 can be used for ROM or RAM chips.
D5,D6 and D7 only for RAM, all are 2K (HM6116 or MB8416)
F6 for video RAM is 1K or 2K, note that only 1K is actually used

Quartz crystal can be 15 or 16MHz (noted on board, default 16)

There are jumper solder connections on board named Z1-Z7 (that many I have
managed to detect on board), that are used to configure if a certain socket
is used for RAM or ROM and also size. This for sure is used to configure
video memory start, but hard to detect all differences on images.

PCB revision 112964

Lola 8 has an integrated matrix keyboard that appears to be with very strange
layout, but please note that char rom content, even being a good read, does not
have proper characters for ASCII codes of lowercase letters on expected places.

Lola 8 NK is using the same keyboard as Lola 8A later, just column 0 and 1 are
switched. PCB is same revision but it is missing lower part for integrated
matrix keyboard and new keyboard is connected over connector.

Lola 8A
=======

BASIC commands : (must be in UPPERcase)

    LET NEXT IF GOTO GOSUB RETURN READ DATA FOR CLS INPUT DIM STOP END RESTORE
    REM CLEAR PUSH POKE PRINT OUT ERROR USR CURSOR NORMAL INVERSE PLOT UNPLOT
    ELSE WIPE COLOUR CENTRE RANGE DRAW CIRCLE LOAD SAVE VERIFY HLOAD HSAVE HVERIFY
    DLOAD DSAVE DVERIFY MERGE CAT RUN NEW ON LIST DEF MON GWIND TWIND UNDER
    SPC OFF TAB THEN TO STEP AND OR XOR NOT ABS LEN SQR INT ASC CHR VAL STR MID
    ARG CALL RND LEFT RIGHT DOT SGN SIN FREE PI FN TAN COS POP PEEK INP LN EXP ATN

Image display is mono, COLOUR x (x = 0 to 3) command switch drawing in mode
    0 - nothing
    1 - set 1 on drawn pixel
    2 - set 0 on drawn pixel
    3 - xor drawn pixel with current value

Unknown how to produce sound - there's no commands.

MON: (Guesswork by trying things)
    - A0 : display addresses A0 to A7
    - 0,FFF : display addresses 0 to FFE (yes it leaves one out)
    - G,R : displays registers (R B=3 : set B register to 3)
    - M,N,Q,T : display or set a specific "register"? (Q : display Q ; Q=6 : set Q to 6)
    - SP : display or set SP (SP=3B00 : set SP)
    - PC : display or set PC
    - L : disassembler (L 0,FF : disassemble range 0 to FF)
    - K : single-step? (address set by G) (K 99 : single-step at 99)

Control Keys
    - A : adds additional line for each char (making image higher)
    - G : back to normal
    - C : break? (only does a newline)
    - L : clear screen
    - M : same as pressing Enter
    - Y : invert screen

TO DO
    - How to use the sound?
    - Need software
    - Need manuals & schematics

****************************************************************************/

#include "emu.h"

#include "cpu/i8085/i8085.h"
#include "imagedev/cassette.h"
#include "machine/ram.h"
#include "sound/ay8910.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


#define AY8910_TAG "g12"
#define HD46505SP_TAG "h45"


namespace {

class lola8_base_state : public driver_device
{
public:
	lola8_base_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_cass(*this, "cassette")
		, m_palette(*this, "palette")
		, m_screen(*this, "screen")
		, m_hd6845(*this, HD46505SP_TAG)
		, m_ay8910(*this, AY8910_TAG)
		, m_p_videoram(*this, "videoram")
		, m_io_keyboard(*this, "KEY.%u", 0U)
		, m_io_modifier(*this, "MODIFIER")
	{ }

	void lola_base(machine_config &config);

protected:
	void crtc_vsync(int state);
	int cass_r();
	void cass_w(int state);

	memory_passthrough_handler m_rom_shadow_tap;
	required_device<i8085a_cpu_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_device<ram_device> m_ram;
	required_device<cassette_image_device> m_cass;
	required_device<palette_device> m_palette;
	required_device<screen_device> m_screen;
	required_device<hd6845s_device> m_hd6845;
	required_device<ay8910_device> m_ay8910;
	required_shared_ptr<u8> m_p_videoram;
	required_ioport_array<10> m_io_keyboard;
	required_ioport m_io_modifier;
};

class lola8_state : public lola8_base_state
{
public:
	lola8_state(const machine_config &mconfig, device_type type, const char *tag)
		: lola8_base_state(mconfig, type, tag)
		, m_p_chargen(*this, "chargen")
	{ }

	void lola8(machine_config &config);

private:
	void machine_start() override ATTR_COLD;

	u8 port_b_r();
	void port_a_w(u8 data);

	MC6845_UPDATE_ROW(crtc_update_row);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_region_ptr<u8> m_p_chargen;
protected:
	u8 m_porta = 0U;
};

class lola8nk_state : public lola8_state
{
public:
	lola8nk_state(const machine_config &mconfig, device_type type, const char *tag)
		: lola8_state(mconfig, type, tag)
	{ }

	void lola8nk(machine_config &config);

private:
	void machine_start() override ATTR_COLD;

	u8 port_b_r();
	void mem_map(address_map &map) ATTR_COLD;
};

class lola8a_state : public lola8_base_state
{
public:
	lola8a_state(const machine_config &mconfig, device_type type, const char *tag)
		: lola8_base_state(mconfig, type, tag)
	{ }

	void lola8a(machine_config &config);

private:
	void machine_reset() override ATTR_COLD;
	void machine_start() override ATTR_COLD;

	u8 port_a_r();
	void port_b_w(u8 data);

	MC6845_UPDATE_ROW(crtc_update_row);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	u8 m_portb = 0U;
};

/* Memory maps */
void lola8_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).rom().region("maincpu", 0);
	map(0x5000, 0x53ff).ram().share("videoram"); // MK4801AN at E6
	// RAM starts at 0x5800
}

void lola8nk_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).rom().region("maincpu", 0);
	map(0x6000, 0x63ff).ram().share("videoram"); // MB8146 at E6, only 1K used
	// RAM starts at 0x6800
}

void lola8_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0xf9, 0xf9).w(m_ay8910, FUNC(ay8910_device::address_w));
	map(0xfa, 0xfa).rw(m_ay8910, FUNC(ay8910_device::data_r), FUNC(ay8910_device::data_w));
	map(0xfc, 0xfc).rw(m_hd6845, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0xfd, 0xfd).rw(m_hd6845, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
}

/* I/O maps */
void lola8a_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	// Sockets for RAM 6264 at G45, F45, E45 and D45
	// should be populated in order, usual configuration
	// is 16K with G45 and F45 populated
	map(0x8000, 0x9fff).rom().region("maincpu", 0); // 2764A at B45
	map(0xa000, 0xbfff).rom().region("maincpu", 0x2000); // 2764A at C45
	map(0xc000, 0xdfff).rom().region("maincpu", 0x4000); // 2764A at H67
	map(0xe000, 0xffff).ram().share("videoram"); // 6264 at G67
}

void lola8a_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x80, 0x80).w(m_ay8910, FUNC(ay8910_device::address_w));
	map(0x84, 0x84).w(m_ay8910, FUNC(ay8910_device::data_w));
	map(0x88, 0x88).r(m_ay8910, FUNC(ay8910_device::data_r));
	map(0x90, 0x90).rw(m_hd6845, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x92, 0x92).rw(m_hd6845, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
}

/* Input ports */
static INPUT_PORTS_START( lola8 )
	PORT_START("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('o')
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('l')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('i')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('g')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('t')
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('n')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('p')
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR(']')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('j')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('s')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('x')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL) PORT_CHAR(8)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('m')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('k')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('h')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('e')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('b')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('[')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('v')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('u')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('f')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('r')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('=') PORT_CHAR('-')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.8")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.9")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("MODIFIER")
	PORT_BIT(0x3f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
INPUT_PORTS_END

static INPUT_PORTS_START( lola8a )
	PORT_START("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(U'Ž') PORT_CHAR(U'ž')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(U'Č') PORT_CHAR(U'č')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('=') PORT_CHAR('-')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_END) PORT_CHAR(U'Š') PORT_CHAR(U'š')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(U'Ć') PORT_CHAR(U'ć')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('}')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(':') PORT_CHAR('@')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RET") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL) PORT_CHAR(8)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("MODIFIER")
	PORT_BIT(0x3f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
INPUT_PORTS_END

/* Video */
/* F4 Character Displayer */
static const gfx_layout lola8_charlayout =
{
	8, 10,                  /* 8 x 10 characters */
	128,                    /* 128 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_lola8 )
	GFXDECODE_ENTRY( "chargen", 0x0800, lola8_charlayout, 0, 1 )
GFXDECODE_END

MC6845_UPDATE_ROW( lola8_state::crtc_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u32 *p = &bitmap.pix(y);
	for (u8 x = 0; x < x_count; x++)
	{
		u16 mem = (ma + x) & 0x3ff;
		u8 chr = m_p_videoram[mem];
		u8 gfx = m_p_chargen[0x0800 | (chr<<4) | ra] ^ ((x == (cursor_x)) ? 0xff : 0);

		*p++ = palette[BIT(gfx, 7)];
		*p++ = palette[BIT(gfx, 6)];
		*p++ = palette[BIT(gfx, 5)];
		*p++ = palette[BIT(gfx, 4)];
		*p++ = palette[BIT(gfx, 3)];
		*p++ = palette[BIT(gfx, 2)];
		*p++ = palette[BIT(gfx, 1)];
		*p++ = palette[BIT(gfx, 0)];
	}
}

MC6845_UPDATE_ROW( lola8a_state::crtc_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u32 *p = &bitmap.pix(y);
	u8 inv = BIT(ma,13) ? 0xff : 0x00;
	ma &= 0x7ff;

	for (u8 x = 0; x < x_count; x++)
	{
		u16 mem = (x+ma)*8 + ra;
		u8 gfx = m_p_videoram[mem] ^ ((cursor_x == x) ? 0xff : 0) ^ inv;
		if (ra == 8) // empty line when Ctrl-A is used
			gfx = inv;

		*p++ = palette[BIT(gfx, 7)];
		*p++ = palette[BIT(gfx, 6)];
		*p++ = palette[BIT(gfx, 5)];
		*p++ = palette[BIT(gfx, 4)];
		*p++ = palette[BIT(gfx, 3)];
		*p++ = palette[BIT(gfx, 2)];
		*p++ = palette[BIT(gfx, 1)];
		*p++ = palette[BIT(gfx, 0)];
	}
}

/* Keyboard */
u8 lola8_state::port_b_r()
{
	u8 data = 0xff;
	for(int i=0;i<8;i++)
	{
		if (BIT(m_porta,i)==0)
		{
			data = m_io_keyboard[i]->read();
			break;
		}
	}
	return data & m_io_modifier->read();
}

// On Lola 8 NK same keyboard as on 8A is used
// but column 0 and 1 wires are switched
u8 lola8nk_state::port_b_r()
{
	u8 data = 0xff, kbrow = m_porta & 15;
	if (kbrow < 10)
		data = m_io_keyboard[kbrow]->read() & m_io_modifier->read();
	return bitswap<8>(data, 7, 6, 5, 4, 3, 2, 0, 1);
}

void lola8_state::port_a_w(u8 data)
{
	m_porta = data;
}

u8 lola8a_state::port_a_r()
{
	u8 data = 0xff, kbrow = m_portb & 15;

	if (kbrow < 10)
		data = m_io_keyboard[kbrow]->read() & m_io_modifier->read();

	return data;
}

void lola8a_state::port_b_w(u8 data)
{
	m_portb = data;
}

/* Cassette */
int lola8_base_state::cass_r()
{
	return (m_cass->input() < 0.03);
}

void lola8_base_state::cass_w(int state)
{
	m_cass->output(state ? -1.0 : +1.0);
}

/* Machine start / reset */
void lola8a_state::machine_reset()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x0000, 0x1fff, m_rom);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0x8000, 0x9fff,
			"rom_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// reinstall RAM over the ROM shadow
					m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x1fff, m_ram->pointer());
				}
			},
			&m_rom_shadow_tap);
}

void lola8_state::machine_start()
{
	m_maincpu->space(AS_PROGRAM).install_ram(0x5800, 0x5800 + m_ram->size() - 1, m_ram->pointer());
	save_item(NAME(m_porta));
}

void lola8nk_state::machine_start()
{
	m_maincpu->space(AS_PROGRAM).install_ram(0x6800, 0x6800 + m_ram->size() - 1, m_ram->pointer());
	save_item(NAME(m_porta));
}

void lola8a_state::machine_start()
{
	// Add just RAM over 8K, first 8K will be added after reset
	if (m_ram->size() > 8 * 1024)
		m_maincpu->space(AS_PROGRAM).install_ram(0x2000, m_ram->size() - 1, m_ram->pointer() + 0x2000);
	save_item(NAME(m_portb));
}

void lola8_base_state::crtc_vsync(int state)
{
	m_maincpu->set_input_line(I8085_RST75_LINE, state? ASSERT_LINE : CLEAR_LINE);
}

/* Machine configuration */
void lola8_base_state::lola_base(machine_config &config)
{
	/* basic machine hardware */
	I8085A(config, m_maincpu, 0);
	m_maincpu->in_sid_func().set(FUNC(lola8a_state::cass_r));
	m_maincpu->out_sod_func().set(FUNC(lola8a_state::cass_w));

	/* audio hardware */
	SPEAKER(config, "mono").front_center();

	AY8910(config, m_ay8910, 0);
	m_ay8910->add_route(ALL_OUTPUTS, "mono", 1.0);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_screen_update(m_hd6845, FUNC(hd6845s_device::screen_update));

	HD6845S(config, m_hd6845, 0); // HD6845 == HD46505S
	m_hd6845->set_screen("screen");
	m_hd6845->set_show_border_area(false);
	m_hd6845->set_char_width(8);
	m_hd6845->out_vsync_callback().set(FUNC(lola8a_state::crtc_vsync));

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	/* Cassette */
	CASSETTE(config, m_cass);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
}

void lola8_state::lola8(machine_config &config)
{
	lola_base(config);

	/* basic machine hardware */
	m_maincpu->set_clock(XTAL(16'000'000) / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &lola8_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &lola8_state::io_map);

	m_ay8910->set_clock(XTAL(16'000'000) / 16);
	m_ay8910->port_a_write_callback().set(FUNC(lola8_state::port_a_w));
	m_ay8910->port_b_read_callback().set(FUNC(lola8_state::port_b_r));

	/* video hardware */
	m_screen->set_raw(16_MHz_XTAL, 480, 0, 320, 312, 0, 250);

	m_hd6845->set_clock(XTAL(16'000'000) / 16);
	m_hd6845->set_update_row_callback(FUNC(lola8_state::crtc_update_row));

	GFXDECODE(config, "gfxdecode", m_palette, gfx_lola8);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("10K").set_extra_options("2K,4K,6K,8K,10K");
}

void lola8nk_state::lola8nk(machine_config &config)
{
	lola8(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &lola8nk_state::mem_map);

	m_ay8910->port_b_read_callback().set(FUNC(lola8nk_state::port_b_r));

	/* video hardware */
	m_screen->set_raw(16_MHz_XTAL, 512, 0, 320, 312, 0, 225);

	/* internal ram */
	RAM(config.replace(), RAM_TAG).set_default_size("6K").set_extra_options("2K,4K,6K");
}

void lola8a_state::lola8a(machine_config &config)
{
	lola_base(config);

	/* basic machine hardware */
	m_maincpu->set_clock(XTAL(4'915'200));
	m_maincpu->set_addrmap(AS_PROGRAM, &lola8a_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &lola8a_state::io_map);

	m_ay8910->set_clock(XTAL(4'915'200) / 4);
	m_ay8910->port_a_read_callback().set(FUNC(lola8a_state::port_a_r));
	m_ay8910->port_b_write_callback().set(FUNC(lola8a_state::port_b_w));

	/* video hardware */
	m_screen->set_raw(8_MHz_XTAL, 512, 0, 320, 313, 0, 200);

	m_hd6845->set_clock(XTAL(8'000'000) / 8);
	m_hd6845->set_update_row_callback(FUNC(lola8a_state::crtc_update_row));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("16K").set_extra_options("8K,16K,24K,32K");
}

/* ROM definition */
ROM_START( lola8 )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "l83es2 r0+r1 8.5.85.d1", 0x0000, 0x2000, CRC(c036595f) SHA1(72447b55f053d90dfd31c335b68637fd7b942a41))
	ROM_LOAD( "l83es2 r2+r3 8.5.85.d2", 0x2000, 0x2000, CRC(17adfefd) SHA1(d20e52348ac9b1a60d2a845b5f804d93bfb00b0f))
	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "lo8 03.01 crt r0 17.4.85.fg6", 0x0000, 0x1000, CRC(3fe1e7c4) SHA1(1e76b61f5cb6800f8f263fd970afe88a7b2bab64))
ROM_END

ROM_START( lola8nk )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "l8bmny r0 v04 15.5.85.d1", 0x0000, 0x1000, CRC(ec16f0b8) SHA1(9d909eccfbeba07d4520cf369fe514764841f6f9))
	ROM_LOAD( "l8bmny r1 v04 15.5.85.d2", 0x1000, 0x1000, CRC(d79fb5e4) SHA1(1b0c04a34472cc456575760193a6b9b26a749c32))
	ROM_LOAD( "l8bmny r2 v04 15.5.85.d3", 0x2000, 0x1000, CRC(c7f32f39) SHA1(1ef1475502894f43c13f244ec3f60672b6d6db2b))
	ROM_LOAD( "l8bmny r3 v04 15.5.85.d4", 0x3000, 0x1000, CRC(879e4c31) SHA1(4cd9701e502308c8ef732a53dc6055f75060ea84))
	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "chl8ny crt 14.5.85.fg6", 0x0800, 0x0800, CRC(f078c61a) SHA1(08d0072a52b5de03e46264a05eb49699191976b5))
	ROM_REGION( 0x40, "proms", 0 )
	ROM_LOAD( "prom.e12", 0x00, 0x20, CRC(6887a88c) SHA1(de34ea9345ba4a5b7728602d7c074424765a9635))
	ROM_LOAD( "prom.b5",  0x20, 0x20, CRC(afbe3aa1) SHA1(757c115971013381f6aeb5c5c566150e715267d1))
ROM_END

ROM_START( lola8a )
	ROM_REGION( 0x6000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "lola 8a r0 w06 22.11.86.b45", 0x0000, 0x2000, CRC(aca1fc08) SHA1(f7076d937bb53b0addcba2a5b7c05ab75d6d0d93))
	ROM_LOAD( "lola 8a r1 w06 22.11.86.c45", 0x2000, 0x2000, CRC(99f8ec9b) SHA1(88eafd09c479f177525fa0039cf04d74bae39dab))
	ROM_LOAD( "lola 8a r2 w06 22.11.86.h67", 0x4000, 0x2000, CRC(1e7cd46b) SHA1(048b2583ee7baeb9621e629b79ed64583ac5d554))
ROM_END

} // anonymous namespace

/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY                    FULLNAME     FLAGS
COMP( 1985, lola8,  0,      0,      lola8,   lola8,  lola8_state,  empty_init, "Institut Ivo Lola Ribar", "Lola 8",    MACHINE_SUPPORTS_SAVE )
COMP( 1985, lola8nk,lola8,  0,      lola8nk, lola8a, lola8nk_state,empty_init, "Institut Ivo Lola Ribar", "Lola 8 NK", MACHINE_SUPPORTS_SAVE )
COMP( 1986, lola8a, lola8,  0,      lola8a,  lola8a, lola8a_state, empty_init, "Institut Ivo Lola Ribar", "Lola 8A",   MACHINE_SUPPORTS_SAVE )



luna_68k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * Omron Luna M68K systems.
 *
 * Sources:
 *   - https://wiki.netbsd.org/ports/luna68k/
 *
 * TODO:
 *   - skeleton only
 */
/*
 * WIP
 *
 * This driver is currently based on a VME-based Luna with a 25MHz 68030, which
 * differs from the systems supported by NetBSD. Boards installed are:
 *
 *  C25   CPU, FPU, serial, RTC, 68030 + 68882 @ 25MHz?
 *  IOC2  I/O controller (floppy, SCSI, serial), 68000 @ 10MHz?
 *  GPU8  graphics processor + serial, 68020 @ 20MHz? + +68881 @ 16MHz?
 *  DPU8  video/framebuffer, Bt458 @ 108MHz
 *  CMC   communications (GPIB, Ethernet, serial), 68020 @ 12.5MHz?
 *
 * This specific machine may be an SX-9100 Model 90?
 */
#include "emu.h"

#include "cpu/m68000/m68030.h"

// memory
#include "machine/ram.h"

// various hardware
#include "machine/mc146818.h"
#include "machine/z80sio.h"
#include "machine/am9513.h"

// busses and connectors
#include "bus/rs232/rs232.h"
#include "bus/nscsi/hd.h"

// ioc2
#include "cpu/m68000/m68000.h"
#include "machine/hd63450.h"
#include "machine/mb87030.h"
#include "machine/wd_fdc.h"
#include "machine/z80scc.h"
#include "machine/z8536.h"

// gpu
#include "cpu/m68000/m68020.h"
#include "machine/mc68681.h"
#include "machine/mc68901.h"
#include "video/bt45x.h"
#include "machine/nvram.h"

#define VERBOSE 0
#include "logmacro.h"

namespace {

class luna_68k_state : public driver_device
{
public:
	luna_68k_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_ram(*this, "ram")
		, m_rtc(*this, "rtc")
		, m_sio(*this, "sio")
		, m_stc(*this, "stc")
		, m_serial(*this, "serial%u", 0U)
		, m_eprom(*this, "eprom")
		// ioc2
		, m_ioc_cpu(*this, "ioc")
		, m_ioc_dma(*this, "dma%u", 0U)
		, m_ioc_spc(*this, "scsi%u:7:spc", 0U)
		, m_ioc_fdc(*this, "fdc")
		, m_ioc_scc(*this, "scc")
		, m_ioc_cio(*this, "cio")
		, m_ioc_ram(*this, "ioc_ram")
		, m_ioc_boot(*this, "ioc_boot")
		// gpu
		, m_gpu_cpu(*this, "gpu")
		, m_gpu_dac(*this, "dac")
		, m_gpu_mfp(*this, "mfp")
		, m_gpu_tty(*this, "tty")
		, m_gpu_duart(*this, "duart%u", 0U)
	{
	}

	// machine config
	void luna(machine_config &config);

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// address maps
	void cpu_map(address_map &map) ATTR_COLD;
	void cpu_autovector_map(address_map &map) ATTR_COLD;

	void ioc_cpu_map(address_map &map);
	void gpu_cpu_map(address_map &map);

private:
	u32 bus_error_r(offs_t offset)
	{
		if (!machine().side_effects_disabled())
		{
			LOG("bus_error_r 0x%x (%s)\n", offset << 2, machine().describe_context());
			m_cpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
		}

		return 0;
	}

	u16 ioc_ram_r(offs_t offset)
	{
		return m_ioc_ram[offset];
	}

	void ioc_ram_w(offs_t offset, u16 data, u16 mem_mask)
	{
		m_ioc_ram[offset] = (m_ioc_ram[offset] & ~mem_mask) | (data & mem_mask);
	}

	void ioc_boot_disable_w(offs_t offset, u16 data)
	{
		if (!machine().side_effects_disabled())
			m_ioc_boot.disable();
		m_ioc_ram[offset & 0x1ffff] = data;
	}

	void ram_size_w(u32 data)
	{
		// FIXME: ram size/enable?
		if (data != m_ram_size)
		{
			m_ram_size = data;
			m_cpu->space(0).install_ram(0, m_ram->mask(), m_ram->pointer());
		}
	}

	// devices
	required_device<m68030_device> m_cpu;
	required_device<ram_device> m_ram;
	required_device<ds1287_device> m_rtc;
	required_device<upd7201_device> m_sio;
	required_device<am9513_device> m_stc;
	required_device_array<rs232_port_device, 2> m_serial;

	required_region_ptr<u32> m_eprom;

	// ioc2
	required_device<m68000_device> m_ioc_cpu;
	required_device_array<hd63450_device, 2> m_ioc_dma;
	required_device_array<mb89352_device, 2> m_ioc_spc;
	required_device<mb8877_device> m_ioc_fdc;
	required_device<z80scc_device> m_ioc_scc;
	required_device<z8536_device> m_ioc_cio;
	required_shared_ptr<u16> m_ioc_ram;
	memory_view m_ioc_boot;

	// gpu
	required_device<m68020fpu_device> m_gpu_cpu;
	required_device<bt458_device> m_gpu_dac;
	required_device<mc68901_device> m_gpu_mfp;
	required_device<rs232_port_device> m_gpu_tty;
	required_device_array<mc68681_device, 2> m_gpu_duart;

	u32 m_ram_size = 0U;
};

void luna_68k_state::machine_start()
{
}

void luna_68k_state::machine_reset()
{
	// mirror eprom at reset
	m_cpu->space(AS_PROGRAM).install_rom(0, m_eprom.bytes() - 1, m_eprom);

	m_ioc_boot.select(0);
}

void luna_68k_state::cpu_map(address_map &map)
{
	map(0x20280000, 0x202bffff).rw(FUNC(luna_68k_state::ioc_ram_r), FUNC(luna_68k_state::ioc_ram_w));

	map(0x30000000, 0x3fffffff).r(FUNC(luna_68k_state::bus_error_r));
	if (0) // FIXME: won't boot to monitor if this is enabled
	{
		map(0x30000d00, 0x30000d1f).m(m_ioc_spc[0], FUNC(mb89352_device::map)).umask32(0x00ff00ff);
		map(0x30000d20, 0x30000d3f).m(m_ioc_spc[1], FUNC(mb89352_device::map)).umask32(0x00ff00ff);
	}

	map(0x40000000, 0x4001ffff).rom().region("eprom", 0);

	map(0x50000000, 0x50000007).rw(m_sio, FUNC(upd7201_device::ba_cd_r), FUNC(upd7201_device::ba_cd_w)).umask32(0xff00ff00);
	map(0x58000000, 0x5800007f).rw(m_rtc, FUNC(ds1287_device::read_direct), FUNC(ds1287_device::write_direct)).umask32(0xff00ff00);
	map(0x60000000, 0x60000003).rw(m_stc, FUNC(am9513_device::read16), FUNC(am9513_device::write16));

	map(0x70000000, 0x70000003).lr32([]() { return 0x000000fc; }, "sw3"); // FIXME: possibly CPU board DIP switch? (1=UP)
	map(0x78000000, 0x78000003).w(FUNC(luna_68k_state::ram_size_w));

	map(0xd01f8000, 0xd01f8003).r(FUNC(luna_68k_state::bus_error_r)); // acrtc graphics
	map(0xe1f00038, 0xe1f0003b).nopr(); // jrc graphics
}

void luna_68k_state::cpu_autovector_map(address_map &map)
{
	map(0xfffffff3, 0xfffffff3).lr8(NAME([]() { return m68000_base_device::autovector(1); }));
	map(0xfffffff5, 0xfffffff5).lr8(NAME([]() { return m68000_base_device::autovector(2); }));
	map(0xfffffff7, 0xfffffff7).lr8(NAME([]() { return m68000_base_device::autovector(3); }));
	map(0xfffffff9, 0xfffffff9).lr8(NAME([]() { return m68000_base_device::autovector(4); }));
	map(0xfffffffb, 0xfffffffb).lr8(NAME([]() { return m68000_base_device::autovector(5); }));
	map(0xfffffffd, 0xfffffffd).lr8(NAME([]() { return m68000_base_device::autovector(6); }));
	map(0xffffffff, 0xffffffff).lr8(NAME([]() { return m68000_base_device::autovector(7); }));
}

void luna_68k_state::ioc_cpu_map(address_map &map)
{
	// am8530h-6pc scc @ 4.9152MHz
	// mb89352 x 2 scsi
	// mb8877a
	// hd63450ps10 x 2 dma
	// z0853606psc cio

	map(0x000000, 0x03ffff).ram().share(m_ioc_ram).mirror(0x100000); // HM62256LP-10x8 (32768x8) - 256KB
	map(0x000000, 0x000fff).view(m_ioc_boot);
	m_ioc_boot[0](0x000000, 0x000fff).rom().region("ioc", 0).w(FUNC(luna_68k_state::ioc_boot_disable_w));
	map(0xfc0000, 0xfcffff).rom().region("ioc", 0);
	map(0xfe0000, 0xfe0fff).rom().region("ioc", 0);

	map(0xfef400, 0xfef400).rw(m_ioc_scc, FUNC(z80scc_device::da_r), FUNC(z80scc_device::da_w));
	map(0xfef401, 0xfef401).rw(m_ioc_scc, FUNC(z80scc_device::ca_r), FUNC(z80scc_device::ca_w));
	map(0xfef402, 0xfef402).rw(m_ioc_scc, FUNC(z80scc_device::db_r), FUNC(z80scc_device::db_w));
	map(0xfef403, 0xfef403).rw(m_ioc_scc, FUNC(z80scc_device::cb_r), FUNC(z80scc_device::cb_w));
}

void luna_68k_state::gpu_cpu_map(address_map &map)
{
	map(0x00000000, 0x0003ffff).rom().region("gpu", 0);

	map(0x80000000, 0x80bfffff).ram(); // M5M41000BJ  1mb  (1m x 1) dynamic RAM (8x12) - 12MB

	map(0xb0080000, 0xb008001f).rw(m_gpu_mfp, FUNC(mc68901_device::read), FUNC(mc68901_device::write));
	map(0xb0081000, 0xb008100f).rw(m_gpu_duart[0], FUNC(mc68681_device::read), FUNC(mc68681_device::write));
	map(0xb0082000, 0xb008200f).rw(m_gpu_duart[1], FUNC(mc68681_device::read), FUNC(mc68681_device::write));

	map(0xb0090000, 0xb00900ff).ram().share("gpu_nvram"); // MBM2212-20 256x4 NVRAM x 2 - 256B

	map(0xc0000000, 0xc000ffff).ram(); // M5M5178P-55 64kb (8k x 8) static RAM (2x4)   -  64kB
	map(0xf0000000, 0xf003ffff).ram(); // M5M5258P-35 256kb (64k x 4) static RAM (x8)  - 256kB
}

static DEVICE_INPUT_DEFAULTS_START(terminal)
	DEVICE_INPUT_DEFAULTS("RS232_RXBAUD", 0xff, RS232_BAUD_19200)
	DEVICE_INPUT_DEFAULTS("RS232_TXBAUD", 0xff, RS232_BAUD_19200)
DEVICE_INPUT_DEFAULTS_END

static void scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
}

void luna_68k_state::luna(machine_config &config)
{
	M68030(config, m_cpu, 50_MHz_XTAL / 2);
	m_cpu->set_addrmap(AS_PROGRAM, &luna_68k_state::cpu_map);
	m_cpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &luna_68k_state::cpu_autovector_map);

	// 8 SIMMs for RAM arranged as two groups of 4, soldered
	RAM(config, m_ram);
	m_ram->set_default_size("16M");

	DS1287(config, m_rtc, 32'768);

	UPD7201(config, m_sio, 9'830'000); // D9.83B0
	m_sio->out_int_callback().set_inputline(m_cpu, M68K_IRQ_6);

	// console
	RS232_PORT(config, m_serial[0], default_rs232_devices, "terminal");
	m_serial[0]->set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));
	m_sio->out_txda_callback().set(m_serial[0], FUNC(rs232_port_device::write_txd));
	m_sio->out_dtra_callback().set(m_serial[0], FUNC(rs232_port_device::write_dtr));
	m_sio->out_rtsa_callback().set(m_serial[0], FUNC(rs232_port_device::write_rts));
	m_serial[0]->rxd_handler().set(m_sio, FUNC(upd7201_device::rxa_w));
	m_serial[0]->cts_handler().set(m_sio, FUNC(upd7201_device::ctsa_w));

	// keyboard
	RS232_PORT(config, m_serial[1], default_rs232_devices, nullptr);
	m_sio->out_txdb_callback().set(m_serial[1], FUNC(rs232_port_device::write_txd));
	m_sio->out_dtrb_callback().set(m_serial[1], FUNC(rs232_port_device::write_dtr));
	m_sio->out_rtsb_callback().set(m_serial[1], FUNC(rs232_port_device::write_rts));
	m_serial[1]->rxd_handler().set(m_sio, FUNC(upd7201_device::rxb_w));
	m_serial[1]->cts_handler().set(m_sio, FUNC(upd7201_device::ctsb_w));

	AM9513(config, m_stc, 9'830'000); // FIXME: clock? sources?
	// TODO: clock interrupt 5?
	// TODO: soft interrupt 1?
	m_stc->fout_cb().set(m_stc, FUNC(am9513_device::gate1_w)); // assumption based on a common configuration
	m_stc->out1_cb().set_inputline(m_cpu, M68K_IRQ_7);
	m_stc->out4_cb().set(m_sio, FUNC(upd7201_device::rxca_w));
	m_stc->out4_cb().append(m_sio, FUNC(upd7201_device::txca_w));
	m_stc->out5_cb().set(m_sio, FUNC(upd7201_device::rxcb_w));
	m_stc->out5_cb().append(m_sio, FUNC(upd7201_device::txcb_w));

	// IOC2
	M68000(config, m_ioc_cpu, 10_MHz_XTAL);
	m_ioc_cpu->set_addrmap(AS_PROGRAM, &luna_68k_state::ioc_cpu_map);

	HD63450(config, m_ioc_dma[0], 20'000'000 / 2, "ioc");
	HD63450(config, m_ioc_dma[1], 20'000'000 / 2, "ioc");

	// internal SCSI
	NSCSI_BUS(config, "scsi0");
	NSCSI_CONNECTOR(config, "scsi0:0", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:1", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:2", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:3", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:4", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:5", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:6", scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi0:7").option_set("spc", MB89352).machine_config(
		[](device_t *device)
		{
			mb89352_device &spc = downcast<mb89352_device &>(*device);

			spc.set_clock(10'000'000);
			//spc.out_irq_callback().set(spc_irq, FUNC(input_merger_any_high_device::in_w<0>));
		});

	// external SCSI
	NSCSI_BUS(config, "scsi1");
	NSCSI_CONNECTOR(config, "scsi1:0", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:1", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:2", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:3", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:4", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:5", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:6", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:7").option_set("spc", MB89352).machine_config(
		[](device_t *device)
		{
			mb89352_device &spc = downcast<mb89352_device &>(*device);

			spc.set_clock(10'000'000);
			//spc.out_irq_callback().set(spc_irq, FUNC(input_merger_any_high_device::in_w<1>));
		});

	MB8877(config, m_ioc_fdc, 0);
	SCC8530(config, m_ioc_scc, 4.9152_MHz_XTAL); // AM8530H-6PC
	m_ioc_scc->configure_channels(4'915'200, 4'915'200, 4'915'200, 4'915'200);
	Z8536(config, m_ioc_cio, 10'000'000);

	// GPU
	M68020FPU(config, m_gpu_cpu, 33'340'000 / 2);
	m_gpu_cpu->set_addrmap(AS_PROGRAM, &luna_68k_state::gpu_cpu_map);

	BT458(config, m_gpu_dac, 108'000'000);

	MC68901(config, m_gpu_mfp, 3.6864_MHz_XTAL);
	m_gpu_mfp->set_timer_clock(3.6864_MHz_XTAL);
	m_gpu_mfp->out_tdo_cb().set(m_gpu_mfp, FUNC(mc68901_device::tc_w));
	m_gpu_mfp->out_tdo_cb().append(m_gpu_mfp, FUNC(mc68901_device::rc_w));
	//m_gpu_mfp->out_irq_cb().set_inputline(m_gpu_cpu, M68K_IRQ_7);

	RS232_PORT(config, m_gpu_tty, default_rs232_devices, nullptr);
	m_gpu_mfp->out_so_cb().set(m_gpu_tty, FUNC(rs232_port_device::write_txd));
	m_gpu_tty->rxd_handler().set(m_gpu_mfp, FUNC(mc68901_device::si_w));

	MC68681(config, m_gpu_duart[0], 3.6864_MHz_XTAL);
	MC68681(config, m_gpu_duart[1], 3.6864_MHz_XTAL);

	NVRAM(config, "gpu_nvram");
}

ROM_START(luna)
	ROM_REGION32_BE(0x20000, "eprom", 0)
	ROM_LOAD16_WORD_SWAP("0283__ac__8117__1.05.ic88", 0x00000, 0x20000, CRC(c46dec54) SHA1(22ef9274f4ef85d446d56cce13a68273dc55f10a))

	// HACK: force firmware to reinitialize nvram at first boot
	ROM_REGION(64, "rtc", 0)
	ROM_FILL(0, 64, 0xff)

	ROM_REGION16_BE(0x10000, "ioc", 0)
	ROM_LOAD16_BYTE("8145__h__3.24.ic108", 0x0000, 0x8000, CRC(d2dde582) SHA1(e34c15e43869be573272503d1f47e9e244536396))
	ROM_LOAD16_BYTE("8145__l__3.24.ic100", 0x0001, 0x8000, CRC(4863329b) SHA1(881623c3a64260f5cc1be066dbb47799d1f2ce14))

	ROM_REGION32_BE(0x40000, "gpu", 0)
	ROM_LOAD("jaw-2500__rom0__v1.21.rom0", 0x00000, 0x20000, CRC(915e0e86) SHA1(1115a8d3101f6d16e397016ae02fc64202edfc3a))
	ROM_LOAD("jaw-2500__rom1__v1.21.rom1", 0x20000, 0x20000, CRC(b4c21f3f) SHA1(577833dfbbceba8ee32fd2ac5b1809f860143d44))
ROM_END

} // anonymous namespace

/*   YEAR   NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS           INIT         COMPANY  FULLNAME  FLAGS */
COMP(1989?, luna, 0,      0,      luna,    0,     luna_68k_state, empty_init,  "Omron", "Luna",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



luna_88k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * Omron Luna 88K and 88K² systems.
 *
 * Sources:
 *  - Tetsuya Isaki's nono Luna emulator (http://www.pastel-flower.jp/~isaki/nono/)
 *  - OpenBSD source code
 *
 * TODO:
 *  - xp i/o controller
 *  - crt controller
 *  - slotify graphics
 *  - expansion slots
 *  - abort/power switches
 *  - multi-cpu configurations
 *
 * WIP:
 *  - UniOS working
 *  - scsi issues with OpenBSD
 */

#include "emu.h"

#include "luna_kbd.h"

#include "cpu/m88000/m88000.h"
#include "cpu/z180/hd647180x.h"

#include "machine/am79c90.h"
#include "machine/clock.h"
#include "machine/i8255.h"
#include "machine/input_merger.h"
#include "machine/mb87030.h"
#include "machine/mc146818.h"
#include "machine/mc88200.h"
#include "machine/nscsi_bus.h"
#include "machine/ram.h"
#include "machine/timekpr.h"
#include "machine/z80sio.h"
#include "video/bt45x.h"
#include "video/hd44780.h"

// busses and connectors
#include "bus/nscsi/hd.h"
#include "bus/rs232/rs232.h"

#include "emupal.h"
#include "screen.h"

#include "debugger.h"

#define VERBOSE 0
#include "logmacro.h"

namespace {

class luna_88k_state_base : public driver_device
{
public:
	luna_88k_state_base(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_cmmu(*this, "cmmu%u", 0U)
		, m_ram(*this, "ram")
		, m_iop(*this, "iop")
		, m_sio(*this, "sio")
		, m_pio(*this, "pio%u", 0U)
		, m_serial(*this, "serial%u", 0U)
		, m_ramdac(*this, "ramdac")
		, m_vram(*this, "vram", 0x20'0000, ENDIANNESS_BIG)
		, m_lcdc(*this, "lcdc")
		, m_3port_ram(*this, "3port_ram")
		, m_boot(*this, "boot")
		, m_sw(*this, "SW%u", 1U)
		, m_irq_state(false)
		, m_irq_active{}
		, m_nram(nullptr)
	{
	}

	void common_config(machine_config &config, XTAL clock);

	DECLARE_INPUT_CHANGED_MEMBER(abort) { irq(0, 7, newval); }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual void cpu_map(address_map &map) ATTR_COLD;

	void iop_map_mem(address_map &map) ATTR_COLD;
	void iop_map_pio(address_map &map) ATTR_COLD;

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect);

	void plane_common_w(offs_t offset, u32 data, u32 mem_mask);
	void logic_common_w(offs_t offset, u32 data);

	template <unsigned Plane> u32 plane_r(offs_t offset, u32 data);
	template <unsigned Plane> void plane_w(offs_t offset, u32 data, u32 mem_mask);
	template <unsigned Plane> void logic_w(offs_t offset, u32 data);

	u32 irq_ctl_r(offs_t offset);
	void irq_ctl_w(offs_t offset, u32 data);

	void irq(unsigned cpu, unsigned interrupt, int state);
	template <unsigned CPU, unsigned Interrupt> void irq(int state) { irq(CPU, Interrupt, state); }

	void irq_check();

	u16 net_r(offs_t offset) { return m_nram[u16(offset >> 1)]; }
	void net_w(offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_nram[u16(offset >> 1)]); }

private:
	required_device<mc88100_device> m_cpu;
	required_device_array<mc88200_device, 2> m_cmmu;
	required_device<ram_device> m_ram;
	required_device<hd647180x_device> m_iop;
	required_device<upd7201_device> m_sio;
	required_device_array<i8255_device, 2> m_pio;
	required_device_array<rs232_port_device, 2> m_serial;

	required_device<bt458_device> m_ramdac;
	memory_share_creator<u32> m_vram;
	required_device<ks0066_device> m_lcdc;

	required_shared_ptr<u32> m_3port_ram;
	memory_view m_boot;

	required_ioport_array<2> m_sw;

	// video state
	u8 m_plane_active;
	u8 m_plane_func[8];
	u32 m_plane_mask[8];

	// interrupt state
	bool m_irq_state;
	u8 m_irq_active[4];
	u8 m_irq_mask[4];

	util::endian_cast<u32, u16, util::endianness::big> m_nram;
};

class luna88k_state : public luna_88k_state_base
{
public:
	luna88k_state(machine_config const &mconfig, device_type type, char const *tag)
		: luna_88k_state_base(mconfig, type, tag)
		, m_rtc(*this, "rtc")
		, m_spc(*this, "scsi:7:spc")
		, m_net(*this, "net")
		, m_eprom(*this, "eprom")
	{
	}

	void luna88k(machine_config &config);
	void init();

protected:
	virtual void cpu_map(address_map &map) override ATTR_COLD;

	required_device<m48t02_device> m_rtc;
	required_device<mb89352_device> m_spc;
	required_device<am7990_device> m_net;
	required_region_ptr<u32> m_eprom;
};

class luna88k2_state : public luna_88k_state_base
{
public:
	luna88k2_state(machine_config const &mconfig, device_type type, char const *tag)
		: luna_88k_state_base(mconfig, type, tag)
		, m_rtc(*this, "rtc")
		, m_spc(*this, "scsi%u:7:spc", 0U)
		, m_net(*this, "net%u", 0U)
		, m_eprom(*this, "eprom")
		, m_fzrom(*this, "fzrom")
	{
	}

	void luna88k2(machine_config &config);
	void init();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void cpu_map(address_map &map) override ATTR_COLD;

	required_device<mc146818_device> m_rtc;
	required_device_array<mb89352_device, 2> m_spc;
	required_device_array<am7990_device, 2> m_net;
	required_region_ptr<u32> m_eprom;
	required_region_ptr<u8> m_fzrom;

private:
	u8 m_fzrom_addr;
};

void luna88k_state::init()
{
	/*
	 * HACK: avoid firmware data access exception handler returning to SXIP
	 * causing infinite loop. Does hardware only recognize the bus error in
	 * the second stage of the pipeline?
	 */
	m_eprom[0x16bc >> 2] = 0x80204080; // ldcr r1,sxip
	m_eprom[0x16c0 >> 2] = 0x60210004; // addu r1,r1,0x4
}

void luna88k2_state::init()
{
	// HACK: bypass abort switch test failure
	m_eprom[0x1fa58 >> 2] = 0xf4406000;

	// HACK: bypass xp int2/int5 test failure
	m_eprom[0x1fa8c >> 2] = 0xf4406000;

	// HACK: bypass power switch test failure
	m_eprom[0x1e48c >> 2] = 0xf5806000;

	// HACK: bypass PC-I/F test failure
	m_eprom[0x1e4c0 >> 2] = 0xf7206000;
}

void luna_88k_state_base::machine_start()
{
	save_item(NAME(m_plane_active));
	save_item(NAME(m_plane_func));
	save_item(NAME(m_plane_mask));

	save_item(NAME(m_irq_state));
	save_item(NAME(m_irq_active));
	save_item(NAME(m_irq_mask));

	m_nram = util::big_endian_cast<u16>(m_3port_ram.target());
}

void luna88k2_state::machine_start()
{
	luna_88k_state_base::machine_start();

	save_item(NAME(m_fzrom_addr));
}

void luna_88k_state_base::machine_reset()
{
	m_boot.select(0);

	// TODO: disabled until firmware is dumped
	m_iop->suspend(SUSPEND_REASON_RESET, false);

	m_plane_active = 0;
	for (u8 &f : m_plane_func)
		f = 5;
	for (u32 &m : m_plane_mask)
		m = 0xffff'ffffU;

	for (u8 &i : m_irq_mask)
		i = 0;

	irq_check();
}

void luna_88k_state_base::cpu_map(address_map &map)
{
	map(0x0000'0000, 0x03ff'ffff).view(m_boot);
	m_boot[0](0x0000'0000, 0x0003'ffff).rom().region("eprom", 0);
	m_boot[1](0x0000'0000, 0x00ff'ffff).ram();

	map(0x0400'0000, 0x3fff'ffff).lrw32(
		[this]() { m_cmmu[1]->bus_error_w(1); return 0; }, "bus_error",
		[this](u32 data) { m_cmmu[1]->bus_error_w(1); }, "bus_error");

	map(0x4100'0000, 0x4103'ffff).rom().region("eprom", 0);
	map(0x4100'0000, 0x4103'ffff).lw32([this](offs_t offset, u32 data) { m_boot.select(1); }, "boot");

	map(0x4300'0000, 0x4300'03ff).rom().region("fuserom", 0);

	map(0x4900'0000, 0x4900'000f).rw(m_pio[0], FUNC(i8255_device::read), FUNC(i8255_device::write)).umask32(0xff000000);
	map(0x4d00'0000, 0x4d00'000f).rw(m_pio[1], FUNC(i8255_device::read), FUNC(i8255_device::write)).umask32(0xff000000);
	map(0x5100'0000, 0x5100'000f).rw(m_sio, FUNC(upd7201_device::ba_cd_r), FUNC(upd7201_device::ba_cd_w)).umask32(0xff000000);
	//map(0x6100'0000, 0x6100'0003); // tas register
	map(0x6300'0000, 0x6300'0003).nopr(); // power switch?
	map(0x6300'0000, 0x6300'000f).lw32([this](offs_t offset, u32 data) { irq(offset, 6, 0); }, "sysclk_clr");
	map(0x6500'0000, 0x6500'000f).rw(FUNC(luna_88k_state_base::irq_ctl_r), FUNC(luna_88k_state_base::irq_ctl_w));
	map(0x6900'0000, 0x6900'000f).lrw32(
		[this](offs_t offset) { irq(offset, 1, 0); return 0xffffffff; }, "softint_clr",
		[this](offs_t offset, u32 data) { irq(offset, 1, 1); }, "softint_set");
	map(0x6b00'0000, 0x6b00'000f).lr32([this](offs_t offset) { irq(offset, 1, 0); return 0xffffffff; }, "softint_clr");
	// 0x6d00'0000 reset cpu 0-3, all
	map(0x6d00'0010, 0x6d00'0013).lw32([this](u32 data) { machine().schedule_soft_reset(); }, "reset");

	map(0x7100'0000, 0x7101'ffff).ram().share("3port_ram");

	map(0x8000'0000, 0x9fff'ffff).lr32([this]() { m_cmmu[1]->bus_error_w(1); return 0; }, "bus_error");

	//map(0xb100'0000, 0xb100'ffff); // rfcnt (pad,vert_loc,pad,horiz_loc)
	map(0xb104'0000, 0xb104'ffff).lw8([this](u8 data) { LOG("plane_active 0x%02x\n", data); m_plane_active = data; }, "plane_active");
	map(0xb108'0000, 0xb10b'ffff).w(FUNC(luna_88k_state_base::plane_common_w));
	map(0xb10c'0000, 0xb10f'ffff).rw(FUNC(luna_88k_state_base::plane_r<0>), FUNC(luna_88k_state_base::plane_w<0>));
	map(0xb110'0000, 0xb113'ffff).rw(FUNC(luna_88k_state_base::plane_r<1>), FUNC(luna_88k_state_base::plane_w<1>));
	map(0xb114'0000, 0xb117'ffff).rw(FUNC(luna_88k_state_base::plane_r<2>), FUNC(luna_88k_state_base::plane_w<2>));
	map(0xb118'0000, 0xb11b'ffff).rw(FUNC(luna_88k_state_base::plane_r<3>), FUNC(luna_88k_state_base::plane_w<3>));
	map(0xb11c'0000, 0xb11f'ffff).rw(FUNC(luna_88k_state_base::plane_r<4>), FUNC(luna_88k_state_base::plane_w<4>));
	map(0xb120'0000, 0xb123'ffff).rw(FUNC(luna_88k_state_base::plane_r<5>), FUNC(luna_88k_state_base::plane_w<5>));
	map(0xb124'0000, 0xb127'ffff).rw(FUNC(luna_88k_state_base::plane_r<6>), FUNC(luna_88k_state_base::plane_w<6>));
	map(0xb128'0000, 0xb12b'ffff).rw(FUNC(luna_88k_state_base::plane_r<7>), FUNC(luna_88k_state_base::plane_w<7>));
	map(0xb12c'0000, 0xb12c'ffff).w(FUNC(luna_88k_state_base::logic_common_w));

	map(0xb130'0000, 0xb130'ffff).w(FUNC(luna_88k_state_base::logic_w<0>));
	map(0xb134'0000, 0xb134'ffff).w(FUNC(luna_88k_state_base::logic_w<1>));
	map(0xb138'0000, 0xb138'ffff).w(FUNC(luna_88k_state_base::logic_w<2>));
	map(0xb13c'0000, 0xb13c'ffff).w(FUNC(luna_88k_state_base::logic_w<3>));
	map(0xb140'0000, 0xb140'ffff).w(FUNC(luna_88k_state_base::logic_w<4>));
	map(0xb144'0000, 0xb144'ffff).w(FUNC(luna_88k_state_base::logic_w<5>));
	map(0xb148'0000, 0xb148'ffff).w(FUNC(luna_88k_state_base::logic_w<6>));
	map(0xb14c'0000, 0xb14c'ffff).w(FUNC(luna_88k_state_base::logic_w<7>));

	map(0xc100'0000, 0xc100'000f).m(m_ramdac, FUNC(bt458_device::map)).umask32(0xff000000).mirror(0x0010'0000);

	// 0xd000'0000 board check register?
	// 0xd100'0000 crtc-ii
	// 0xd180'0000 bitmap board identify rom
}

void luna88k_state::cpu_map(address_map &map)
{
	luna_88k_state_base::cpu_map(map);

	map(0x4500'0000, 0x4500'1fff).rw(m_rtc, FUNC(m48t02_device::read), FUNC(m48t02_device::write)).umask32(0xff000000);
	map(0xe100'0000, 0xe100'003f).m(m_spc, FUNC(mb89352_device::map)).umask32(0xff000000);
	map(0xf100'0000, 0xf100'0007).rw(m_net, FUNC(am7990_device::regs_r), FUNC(am7990_device::regs_w)).umask32(0xffff0000);
}

void luna88k2_state::cpu_map(address_map &map)
{
	luna_88k_state_base::cpu_map(map);

	map(0x4500'0000, 0x4500'0000).w(m_rtc, FUNC(ds1397_device::address_w));
	map(0x4500'0001, 0x4500'0001).rw(m_rtc, FUNC(ds1397_device::data_r), FUNC(ds1397_device::data_w));
	map(0x4700'0000, 0x4700'003f).rw(m_rtc, FUNC(ds1397_device::xram_r), FUNC(ds1397_device::xram_w));

	// 0x8100'0000 ext board A
	// 0x8300'0000 ext board B
	// 0x9000'0000 pc-98 ext board
	// 0x9100'0000 pc-9801 irq 4

	map(0xe100'0000, 0xe100'003f).m(m_spc[0], FUNC(mb89352_device::map)).umask32(0xff000000);
	map(0xe100'0040, 0xe100'007f).m(m_spc[1], FUNC(mb89352_device::map)).umask32(0xff000000);

	map(0xf100'0000, 0xf100'0007).rw(m_net[0], FUNC(am7990_device::regs_r), FUNC(am7990_device::regs_w)).umask32(0xffff0000);
	map(0xf100'0008, 0xf100'0009).lrw16(
		[this]()
		{
			u8 const data = m_fzrom[m_fzrom_addr >> 1];

			return BIT(m_fzrom_addr, 0) ? BIT(data, 0, 4) : BIT(data, 4, 4);
		}, "fzrom_r",
		[this](u16 data) { m_fzrom_addr = data; }, "fzrom_w");
	map(0xf100'0010, 0xf100'0017).rw(m_net[1], FUNC(am7990_device::regs_r), FUNC(am7990_device::regs_w)).umask32(0xffff0000);
}

void luna_88k_state_base::iop_map_mem(address_map &map)
{
	//map(0x0'0000, 0x0'ffff).ram().share("iop_ram");
}

void luna_88k_state_base::iop_map_pio(address_map &map)
{
	map(0x0049, 0x0049).nopr();
}

static void scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
}

void keyboard_devices(device_slot_interface &device)
{
	device.option_add("keyboard", LUNA_KEYBOARD);
}

void luna_88k_state_base::common_config(machine_config &config, XTAL clock)
{
	MC88100(config, m_cpu, clock.value());
	m_cpu->set_addrmap(AS_PROGRAM, &luna_88k_state_base::cpu_map);
	m_cpu->set_cmmu_code([this](u32 const address) -> mc88200_device & { return *m_cmmu[0]; });
	m_cpu->set_cmmu_data([this](u32 const address) -> mc88200_device & { return *m_cmmu[1]; });

	MC88200(config, m_cmmu[0], clock.value(), 0x07).set_mbus(m_cpu, AS_PROGRAM); // cpu0 cmmu i0
	MC88200(config, m_cmmu[1], clock.value(), 0x06).set_mbus(m_cpu, AS_PROGRAM); // cpu0 cmmu d0

	// 6 SIMMs for RAM arranged as three groups of 2?
	RAM(config, m_ram);
	m_ram->set_default_size("16M");

	clock_device &sys_clk(CLOCK(config, "sys_clk", 200 / 2));
	sys_clk.signal_handler().set([this](int state) { if (state) irq(0, 6, 1); });

	HD647180X(config, m_iop, 12'288'000);
	m_iop->set_addrmap(AS_PROGRAM, &luna_88k_state_base::iop_map_mem);
	m_iop->set_addrmap(AS_IO, &luna_88k_state_base::iop_map_pio);

	UPD7201(config, m_sio, 19'660'800); // ?
	m_sio->out_int_callback().set(&luna_88k_state_base::irq<0, 5>, "irq0,5");

	// RS-232C-A
	RS232_PORT(config, m_serial[0], default_rs232_devices, nullptr);
	m_sio->out_txda_callback().set(m_serial[0], FUNC(rs232_port_device::write_txd));
	m_sio->out_dtra_callback().set(m_serial[0], FUNC(rs232_port_device::write_dtr));
	m_sio->out_rtsa_callback().set(m_serial[0], FUNC(rs232_port_device::write_rts));
	m_serial[0]->rxd_handler().set(m_sio, FUNC(upd7201_device::rxa_w));
	m_serial[0]->cts_handler().set(m_sio, FUNC(upd7201_device::ctsa_w));

	// keyboard/mouse
	RS232_PORT(config, m_serial[1], keyboard_devices, "keyboard");
	m_sio->out_txdb_callback().set(m_serial[1], FUNC(rs232_port_device::write_txd));
	m_sio->out_dtrb_callback().set(m_serial[1], FUNC(rs232_port_device::write_dtr));
	m_sio->out_rtsb_callback().set(m_serial[1], FUNC(rs232_port_device::write_rts));
	m_serial[1]->rxd_handler().set(m_sio, FUNC(upd7201_device::rxb_w));
	m_serial[1]->cts_handler().set(m_sio, FUNC(upd7201_device::ctsb_w));

	clock_device &sio_clk(CLOCK(config, "sio_clk", 19.660'800_MHz_XTAL / 128));
	sio_clk.signal_handler().append(m_sio, FUNC(upd7201_device::rxca_w));
	sio_clk.signal_handler().append(m_sio, FUNC(upd7201_device::txca_w));
	sio_clk.signal_handler().append(m_sio, FUNC(upd7201_device::rxcb_w));
	sio_clk.signal_handler().append(m_sio, FUNC(upd7201_device::txcb_w));

	I8255A(config, m_pio[0], 8'000'000); // M5M82C55AFP-2
	/*
	 * pio0
	 *   port a: dipsw1 (r/o)
	 *   port b: dipsw2 (r/o)
	 *   port c: host interrupt control (r/w)
	 *     bit  function
	 *      0   xp_int1_req (intr b)
	 *      1   unused (ibf b)
	 *      2   xp_int1_ena (inte b)
	 *      3   xp_int5_req (intr a)
	 *      4   xp_int5_ena (inte a)
	 *      5   unused (ibf a)
	 *      6   parity (pc6 output to enable parity error)
	 *      7   xp_reset (pc7 output to reset hd647180 xp)
	 */
	m_pio[0]->in_pa_callback().set([this]() { return m_sw[0]->read(); });
	m_pio[0]->in_pb_callback().set([this]() { return m_sw[1]->read(); });
	m_pio[0]->out_pc_callback().set(
		[this](u8 data)
		{
			LOG("iop reset %d\n", BIT(data, 7));
#if 0
			if (BIT(data, 7))
				m_iop->suspend(SUSPEND_REASON_RESET, false);
			else
				m_iop->resume(SUSPEND_REASON_RESET);
#endif
		});
	m_pio[0]->out_pc_callback().append(m_pio[0], FUNC(i8255_device::pc2_w)).bit(1);
	m_pio[0]->out_pc_callback().append(m_pio[0], FUNC(i8255_device::pc4_w)).bit(5);

	I8255A(config, m_pio[1], 8'000'000); // M5M82C55AFP-2
	/*
	 * pio1
	 *   port a: lcd data (r/w)
	 *   port b: interrupt i/o processor
	 *   port c: interrupt status, powerdown, lcd control
	 *     bit  function
	 *      0
	 *      1   xp intreq input?
	 *      2
	 *      3
	 *      4   power off?
	 *      5   lcd rw
	 *      6   lcd rs
	 *      7   lcd e
	 */
	m_pio[1]->in_pa_callback().set(m_lcdc, FUNC(ks0066_device::db_r));
	m_pio[1]->out_pa_callback().set(m_lcdc, FUNC(ks0066_device::db_w));
	m_pio[1]->out_pc_callback().append(m_lcdc, FUNC(ks0066_device::rw_w)).bit(5);
	m_pio[1]->out_pc_callback().append(m_lcdc, FUNC(ks0066_device::rs_w)).bit(6);
	m_pio[1]->out_pc_callback().append(m_lcdc, FUNC(ks0066_device::e_w)).bit(7);


	// TODO: crt timing control by HD6445CP4
	screen_device &crt(SCREEN(config, "crt", SCREEN_TYPE_RASTER));
	crt.set_raw(108'992'000, 2048, 0, 1280, 1024, 0, 1024);
	crt.set_screen_update(FUNC(luna_88k_state_base::screen_update));

	BT458(config, m_ramdac, 108'992'000);

	KS0066(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_default_bios_tag("f00");
	m_lcdc->set_function_set_at_any_time(true);
	m_lcdc->set_lcd_size(2, 16);

	palette_device &palette(PALETTE(config, "palette", palette_device::MONOCHROME));

	screen_device &lcd(SCREEN(config, "lcd", SCREEN_TYPE_LCD));
	lcd.set_raw(192'000, 40 * 6, 0, 16 * 6, 2 * 8, 0, 2 * 8);
	lcd.set_screen_update(m_lcdc, FUNC(ks0066_device::screen_update));
	lcd.set_palette(palette);
}

void luna88k_state::luna88k(machine_config &config)
{
	luna_88k_state_base::common_config(config, 50_MHz_XTAL / 2);

	M48T02(config, m_rtc);

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:1", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:7").option_set("spc", MB89352).machine_config(
		[this](device_t *device)
		{
			mb89352_device &spc = downcast<mb89352_device &>(*device);

			spc.set_clock(8_MHz_XTAL);
			spc.out_irq_callback().set(*this, &luna88k_state::irq<0, 3>, "irq0,3");
		});

	AM7990(config, m_net, 40_MHz_XTAL / 4);
	m_net->intr_out().set(&luna88k_state::irq<0, 4>, "irq0,4").invert();
	m_net->dma_in().set(FUNC(luna88k_state::net_r));
	m_net->dma_out().set(FUNC(luna88k_state::net_w));
}

void luna88k2_state::luna88k2(machine_config &config)
{
	luna_88k_state_base::common_config(config, 33.333_MHz_XTAL);

	DS1397(config, m_rtc, 32'768);
	m_rtc->set_epoch(1990);

	input_merger_any_high_device &spc_irq(INPUT_MERGER_ANY_HIGH(config, "spc_irq"));
	spc_irq.output_handler().set(&luna88k2_state::irq<0, 3>, "irq0,3");

	NSCSI_BUS(config, "scsi0");
	NSCSI_CONNECTOR(config, "scsi0:0", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:1", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:2", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:3", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:4", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:5", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:6", scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi0:7").option_set("spc", MB89352).machine_config(
		[&spc_irq](device_t *device)
		{
			mb89352_device &spc = downcast<mb89352_device &>(*device);

			spc.set_clock(8_MHz_XTAL);
			spc.out_irq_callback().set(spc_irq, FUNC(input_merger_any_high_device::in_w<0>));
		});

	NSCSI_BUS(config, "scsi1");
	NSCSI_CONNECTOR(config, "scsi1:0", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:1", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:2", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:3", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:4", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:5", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:6", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:7").option_set("spc", MB89352).machine_config(
		[&spc_irq](device_t *device)
		{
			mb89352_device &spc = downcast<mb89352_device &>(*device);

			spc.set_clock(8_MHz_XTAL);
			spc.out_irq_callback().set(spc_irq, FUNC(input_merger_any_high_device::in_w<1>));
		});

	input_merger_any_low_device &net_irq(INPUT_MERGER_ANY_LOW(config, "net_irq"));
	net_irq.output_handler().set(&luna88k2_state::irq<0, 4>, "irq0,4");

	AM7990(config, m_net[0], 40_MHz_XTAL / 4);
	m_net[0]->intr_out().set(net_irq, FUNC(input_merger_any_low_device::in_w<0>));
	m_net[0]->dma_in().set(FUNC(luna88k2_state::net_r));
	m_net[0]->dma_out().set(FUNC(luna88k2_state::net_w));

	AM7990(config, m_net[1], 40_MHz_XTAL / 4);
	m_net[1]->intr_out().set(net_irq, FUNC(input_merger_any_low_device::in_w<1>));
	m_net[1]->dma_in().set(FUNC(luna88k2_state::net_r));
	m_net[1]->dma_out().set(FUNC(luna88k2_state::net_w));
}

u32 luna_88k_state_base::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect)
{
	for (int y = screen.visible_area().min_y; y <= screen.visible_area().max_y; y++)
	{
		for (int x = screen.visible_area().min_x; x <= screen.visible_area().max_x; x += 32)
		{
			unsigned const offset = (y * 64) + (x >> 5);

			u32 const plane0 = m_vram[0 * 0x1'0000 + offset];
			u32 const plane1 = m_vram[1 * 0x1'0000 + offset];
			u32 const plane2 = m_vram[2 * 0x1'0000 + offset];
			u32 const plane3 = m_vram[3 * 0x1'0000 + offset];
			u32 const plane4 = m_vram[4 * 0x1'0000 + offset];
			u32 const plane5 = m_vram[5 * 0x1'0000 + offset];
			u32 const plane6 = m_vram[6 * 0x1'0000 + offset];
			u32 const plane7 = m_vram[7 * 0x1'0000 + offset];

			for (unsigned p = 0; p < 32; p++)
			{
				u8 const index
					= BIT(plane0, 31 - p) << 0
					| BIT(plane1, 31 - p) << 1
					| BIT(plane2, 31 - p) << 2
					| BIT(plane3, 31 - p) << 3
					| BIT(plane4, 31 - p) << 4
					| BIT(plane5, 31 - p) << 5
					| BIT(plane6, 31 - p) << 6
					| BIT(plane7, 31 - p) << 7;

				bitmap.pix(y, x + p) = m_ramdac->pen_color(index);
			}
		}
	}

	return 0;
}

void luna_88k_state_base::plane_common_w(offs_t offset, u32 data, u32 mem_mask)
{
	if (BIT(m_plane_active, 0)) plane_w<0>(offset, data, mem_mask);
	if (BIT(m_plane_active, 1)) plane_w<1>(offset, data, mem_mask);
	if (BIT(m_plane_active, 2)) plane_w<2>(offset, data, mem_mask);
	if (BIT(m_plane_active, 3)) plane_w<3>(offset, data, mem_mask);
	if (BIT(m_plane_active, 4)) plane_w<4>(offset, data, mem_mask);
	if (BIT(m_plane_active, 5)) plane_w<5>(offset, data, mem_mask);
	if (BIT(m_plane_active, 6)) plane_w<6>(offset, data, mem_mask);
	if (BIT(m_plane_active, 7)) plane_w<7>(offset, data, mem_mask);
}

void luna_88k_state_base::logic_common_w(offs_t offset, u32 data)
{
	LOG("logic_common func 0x%x mask 0x%08x\n", offset & 0xf, data);

	if (BIT(m_plane_active, 0)) logic_w<0>(offset, data);
	if (BIT(m_plane_active, 1)) logic_w<1>(offset, data);
	if (BIT(m_plane_active, 2)) logic_w<2>(offset, data);
	if (BIT(m_plane_active, 3)) logic_w<3>(offset, data);
	if (BIT(m_plane_active, 4)) logic_w<4>(offset, data);
	if (BIT(m_plane_active, 5)) logic_w<5>(offset, data);
	if (BIT(m_plane_active, 6)) logic_w<6>(offset, data);
	if (BIT(m_plane_active, 7)) logic_w<7>(offset, data);
}

template <unsigned Plane> u32 luna_88k_state_base::plane_r(offs_t offset, u32 data)
{
	return m_vram[Plane * 0x1'0000 + offset];
}

template <unsigned Plane> void luna_88k_state_base::plane_w(offs_t offset, u32 data, u32 mem_mask)
{
	u32 const memory = m_vram[Plane * 0x1'0000 + offset];

	// logic operations and mask per HM53462 datasheet
	switch (m_plane_func[Plane])
	{
	case 0x0: data = 0; break;
	case 0x1: data &= memory; break;
	case 0x2: data = ~data & memory; break;
	case 0x3: break; // X4 -> X1
	case 0x4: data &= ~memory; break;
	case 0x5: break;
	case 0x6: data ^= memory; break;
	case 0x7: data |= memory; break;
	case 0x8: data = ~data & ~memory; break;
	case 0x9: data = (data & memory) | (~data & ~memory); break;
	case 0xa: data = ~data; break;
	case 0xb: data = ~data | memory; break;
	case 0xc: data = ~memory; break;
	case 0xd: data |= ~memory; break;
	case 0xe: data = ~data | ~memory; break;
	case 0xf: data = 0xffffffffU; break;
	}

	mem_mask &= m_plane_mask[Plane];

	COMBINE_DATA(&m_vram[Plane * 0x1'0000 + offset]);
}

template <unsigned Plane> void luna_88k_state_base::logic_w(offs_t offset, u32 data)
{
	if (VERBOSE & LOG_GENERAL)
	{
		static char const *const func[] =
		{
			"0", "AND1", "AND2", "X4->X1", "AND3", "THROUGH", "EOR", "OR1",
			"NOR", "ENOR", "INV1", "OR2", "INV2", "OR3", "NAND", "1"
		};

		LOG("logic plane %u %s func 0x%1x mask 0x%08x\n", Plane, func[offset & 0xf], offset & 0xf, data);
	}

	m_plane_func[Plane] = offset & 0xf;
	m_plane_mask[Plane] = data;
}

u32 luna_88k_state_base::irq_ctl_r(offs_t offset)
{
	u32 data = u32(m_irq_mask[offset]) << 18;

	u8 const active = m_irq_active[offset] & (0x80 | m_irq_mask[offset] << 1);
	if (active)
	{
		unsigned const level = 31 - count_leading_zeros_32(active);

		data |= (level << 29);
	}

	return data;
}

void luna_88k_state_base::irq_ctl_w(offs_t offset, u32 data)
{
	m_irq_mask[offset] = BIT(data, 26, 6);

	irq_check();
}

void luna_88k_state_base::irq(unsigned cpu, unsigned interrupt, int state)
{
	if (state)
		m_irq_active[cpu] |= (1U << interrupt);
	else
		m_irq_active[cpu] &= ~(1U << interrupt);

	irq_check();
}

void luna_88k_state_base::irq_check()
{
	bool irq_state = m_irq_active[0] & (0x80 | m_irq_mask[0] << 1);

	if (irq_state != m_irq_state)
	{
		m_irq_state = irq_state;
		m_cpu->set_input_line(INPUT_LINE_IRQ0, m_irq_state);
	}
}

static INPUT_PORTS_START(luna88k)
	PORT_START("SW1")
	PORT_DIPNAME(0x80, 0x80, "Start") PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(0x00, "Monitor")  // single-user
	PORT_DIPSETTING(0x80, "Autoboot") // multi-user
	PORT_DIPNAME(0x40, 0x40, "Console") PORT_DIPLOCATION("SW1:2")
	PORT_DIPSETTING(0x00, "Serial")
	PORT_DIPSETTING(0x40, "Graphics")
	PORT_DIPNAME(0x20, 0x00, "SW1#3") PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x20, DEF_STR(On))
	PORT_DIPNAME(0x10, 0x00, "SW1#4") PORT_DIPLOCATION("SW1:4")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x10, DEF_STR(On))
	PORT_DIPNAME(0x08, 0x00, "SW1#5") PORT_DIPLOCATION("SW1:5")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x08, DEF_STR(On))
	PORT_DIPNAME(0x04, 0x00, "SW1#6") PORT_DIPLOCATION("SW1:6")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x04, DEF_STR(On))
	PORT_DIPNAME(0x02, 0x00, "SW1#7") PORT_DIPLOCATION("SW1:7")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x02, DEF_STR(On))
	PORT_DIPNAME(0x01, 0x01, "Mode") PORT_DIPLOCATION("SW1:8")
	PORT_DIPSETTING(0x00, "Diagnostic")
	PORT_DIPSETTING(0x01, "Normal")

	// user-defined switches
	PORT_START("SW2")
	PORT_DIPNAME(0x80, 0x00, "SW2#1") PORT_DIPLOCATION("SW2:1")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x80, DEF_STR(On))
	PORT_DIPNAME(0x40, 0x00, "SW2#2") PORT_DIPLOCATION("SW2:2")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x40, DEF_STR(On))
	PORT_DIPNAME(0x20, 0x00, "SW2#3") PORT_DIPLOCATION("SW2:3")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x20, DEF_STR(On))
	PORT_DIPNAME(0x10, 0x00, "SW2#4") PORT_DIPLOCATION("SW2:4")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x10, DEF_STR(On))
	PORT_DIPNAME(0x08, 0x00, "SW2#5") PORT_DIPLOCATION("SW2:5")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x08, DEF_STR(On))
	PORT_DIPNAME(0x04, 0x00, "SW2#6") PORT_DIPLOCATION("SW2:6")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x04, DEF_STR(On))
	PORT_DIPNAME(0x02, 0x00, "SW2#7") PORT_DIPLOCATION("SW2:7")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x02, DEF_STR(On))
	PORT_DIPNAME(0x01, 0x00, "SW2#8") PORT_DIPLOCATION("SW2:8")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x01, DEF_STR(On))

	PORT_START("ABORT")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Abort") PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(luna_88k_state_base::abort), 0)
INPUT_PORTS_END

static INPUT_PORTS_START(luna88k2)
	PORT_START("SW1")
	PORT_DIPNAME(0x80, 0x80, "Start") PORT_DIPLOCATION("SW1:!1")
	PORT_DIPSETTING(0x00, "Monitor")  // single-user
	PORT_DIPSETTING(0x80, "Autoboot") // multi-user
	PORT_DIPNAME(0x40, 0x40, "Console") PORT_DIPLOCATION("SW1:!2")
	PORT_DIPSETTING(0x00, "Serial")
	PORT_DIPSETTING(0x40, "Graphics")
	PORT_DIPNAME(0x20, 0x00, "SW1#3") PORT_DIPLOCATION("SW1:!3")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x20, DEF_STR(On))
	PORT_DIPNAME(0x10, 0x00, "SW1#4") PORT_DIPLOCATION("SW1:!4")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x10, DEF_STR(On))
	PORT_DIPNAME(0x08, 0x00, "SW1#5") PORT_DIPLOCATION("SW1:!5")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x08, DEF_STR(On))
	PORT_DIPNAME(0x04, 0x00, "SW1#6") PORT_DIPLOCATION("SW1:!6")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x04, DEF_STR(On))
	PORT_DIPNAME(0x02, 0x00, "SW1#7") PORT_DIPLOCATION("SW1:!7")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x02, DEF_STR(On))
	PORT_DIPNAME(0x01, 0x01, "Mode") PORT_DIPLOCATION("SW1:!8")
	PORT_DIPSETTING(0x00, "Diagnostic")
	PORT_DIPSETTING(0x01, "Normal")

	// user-defined switches
	PORT_START("SW2")
	PORT_DIPNAME(0x80, 0x00, "SW2#1") PORT_DIPLOCATION("SW2:!1")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x80, DEF_STR(On))
	PORT_DIPNAME(0x40, 0x00, "SW2#2") PORT_DIPLOCATION("SW2:!2")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x40, DEF_STR(On))
	PORT_DIPNAME(0x20, 0x00, "SW2#3") PORT_DIPLOCATION("SW2:!3")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x20, DEF_STR(On))
	PORT_DIPNAME(0x10, 0x00, "SW2#4") PORT_DIPLOCATION("SW2:!4")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x10, DEF_STR(On))
	PORT_DIPNAME(0x08, 0x00, "SW2#5") PORT_DIPLOCATION("SW2:!5")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x08, DEF_STR(On))
	PORT_DIPNAME(0x04, 0x00, "SW2#6") PORT_DIPLOCATION("SW2:!6")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x04, DEF_STR(On))
	PORT_DIPNAME(0x02, 0x00, "SW2#7") PORT_DIPLOCATION("SW2:!7")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x02, DEF_STR(On))
	PORT_DIPNAME(0x01, 0x00, "SW2#8") PORT_DIPLOCATION("SW2:!8")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x01, DEF_STR(On))

	PORT_START("ABORT")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Abort") PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(luna_88k_state_base::abort), 0)
INPUT_PORTS_END

ROM_START(luna88k)
	// 2 x 27C1024
	ROM_REGION32_BE(0x40000, "eprom", 0)
	ROM_SYSTEM_BIOS(0, "l122", "ROM Version 1.22")
	ROMX_LOAD("l122hi.ic92", 0x00000, 0x20000, CRC(4cdccd8f) SHA1(e2503fa2cfd4a17a881562315b5bd8f46f028186), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(0))
	ROMX_LOAD("l122lo.ic102", 0x00002, 0x20000, CRC(992b3f32) SHA1(57a7150eabf46e6aa324f9d0b55b792154b6f61f), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "l110", "ROM Version 1.10")
	ROMX_LOAD("l110hi.ic92", 0x00000, 0x20000, CRC(9f0c2d37) SHA1(c7ac5d8b5995958bf91ecd2ea0b669c43a224fec), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(1))
	ROMX_LOAD("l110lo.ic102", 0x00002, 0x20000, CRC(61f5604e) SHA1(6a835c562a18a4d527c2f8ea85a7f94d56362f8a), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "l103", "ROM Version 1.03")
	ROMX_LOAD("l103hi.ic92", 0x00000, 0x20000, CRC(cb862bc9) SHA1(a581b96f52eebf81e911f61694e487d46cc0b8ed), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(2))
	ROMX_LOAD("l103lo.ic102", 0x00002, 0x20000, CRC(1a004082) SHA1(c09218ba8b60f44ef9e7e59b7ffacef5fccb0667), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(2))

	ROM_SYSTEM_BIOS(3, "l006", "L006")
	ROMX_LOAD("l006hi.ic92", 0x00000, 0x20000, CRC(50d0bedb) SHA1(9dc8501a724347f068684c4bc0cf8a5f2505db59), ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(3))
	ROMX_LOAD("l006lo.ic102", 0x00002, 0x20000, CRC(f9d3647c) SHA1(9b5c105c5bd57bb4018b679634d051d6052e6428) BAD_DUMP, ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(3))

	/*
	 * This PROM contains the Ethernet MAC address stored in the upper 4 bits
	 * of each byte. The content decodes to nul-terminated ASCII string and a
	 * checkum:
	 *
	 *   ENADDR=00000Axxxxxx
	 *
	 * This hash matches content hand-crafted to assign ficticious station
	 * address 00:00:0a:12:34:56.
	 */
	ROM_REGION32_BE(0x400, "fuserom", ROMREGION_ERASEFF)
	ROM_LOAD32_BYTE("fuserom.ic132", 0x000, 0x100, CRC(29704768) SHA1(09f8e5dc13fa9e42a268f6d3c036e8f485e41d60))

	ROM_REGION(0x4000, "iop", 0)
	ROM_LOAD("hd647180x.ic13", 0x0000, 0x4000, NO_DUMP) // HD647180X0FS6
ROM_END

ROM_START(luna88k2)
	ROM_REGION32_BE(0x40000, "eprom", 0)
	ROM_LOAD32_WORD_SWAP("7187__high__1.37.bin", 0x00000, 0x20000, CRC(a7515231) SHA1(86b3e42a8df6fa33cf68f372f4053b240c8cc4e2)) // HN27C1024HCC-85
	ROM_LOAD32_WORD_SWAP("7187__low__1.37.bin",  0x00002, 0x20000, CRC(8e65ea4a) SHA1(288300c71c0e92f114cb84fa293a4839d2e181a6)) // HN27C1024HCC-85

	/*
	 * This PROM contains machine and board revision identifiers stored in the
	 * top 4 bits of each byte. The low 4 bits read back (from software) as 0x7
	 * or 0xf reflecting the parity of the data bits or some other side-effect.
	 * The content decodes to two nul-terminated ASCII strings and a checkum:
	 *
	 *   MNAME=LUNA88K+
	 *   BDVER=A
	 */
	ROM_REGION32_BE(0x400, "fuserom", ROMREGION_ERASEFF)
	ROM_LOAD32_BYTE("sn82s129n.bin", 0x000, 0x100, CRC(780e4617) SHA1(f939cad237b3b4317cf6093e9b56661cdf60455e))

	ROM_REGION(0x4000, "iop", 0)
	ROM_LOAD("hd647180x.ic13", 0x0000, 0x4000, NO_DUMP) // HD647180X0FS6

	/*
	 * This PROM is installed on the network card and holds the MAC identifiers
	 * for up to two channels. Each identifier is encoded as ASCII characters
	 * in the following form:
	 *
	 *   ETHERx00000Ayyyyyyzz
	 *
	 * Where:
	 *   x is a channel number ('0' or '1')
	 *   y is the station number portion of the MAC
	 *   z is the cumulative XOR of the preceding even/odd bytes
	 *
	 * This hash matches content hand-crafted to assign ficticious station
	 * addresses to two channels.
	 */
	ROM_REGION(0x80, "fzrom", 0)
	ROM_LOAD("82s129.ic18", 0x00, 0x80, CRC(ff83b526) SHA1(0f583efcfe1955edcff7fbb8d1e36328848aac1f))
ROM_END

} // anonymous namespace

/*   YEAR   NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT  COMPANY  FULLNAME       FLAGS */
COMP(1991?, luna88k,  0,      0,      luna88k,  luna88k,  luna88k_state,  init, "Omron", "Luna 88K",    0)
COMP(1992?, luna88k2, 0,      0,      luna88k2, luna88k2, luna88k2_state, init, "Omron", u8"Luna 88K²", 0)



lviv.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Krzysztof Strzecha
/*******************************************************************************

PK-01 Lviv driver by Krzysztof Strzecha

Big thanks go to:
Anton V. Ignatichev for informations about Lviv hardware.
Dr. Volodimir Mosorov for two Lviv machines.

What's new:
-----------
28.02.2003      Snapshot verifying function added.
07.01.2003  Support for .SAV snapshots. Joystick support (there are strange
        problems with "Doroga (1991)(-)(Ru).lvt".
21.12.2002  Cassette support rewritten, WAVs saving and loading are working now.
08.12.2002  Comments on emulation status updated. Changed 'lvive' to 'lvivp'.
        ADC r instruction in I8080 core fixed (Arkanoid works now).
        Original keyboard layout added.
20.07.2002  "Reset" key fixed. I8080 core fixed (all BASIC commands works).
        now). Unsupported .lvt files versions aren't now loaded.
xx.07.2002  Improved port and memory mapping (Raphael Nabet).
        Hardware description updated (Raphael Nabet).
27.03.2002  CPU clock changed to 2.5MHz.
        New Lviv driver added for different ROM revision.
24.03.2002  Palette emulation.
        Bit 7 of port 0xc1 emulated - speaker enabled/disabled.
        Some notes about hardware added.
        "Reset" key added.
23.03.2002  Hardware description and notes on emulation status added.
        Few changes in keyboard mapping.

Notes on emulation status and to do list:
-----------------------------------------
1. LIMITATION: Printer is not emulated.
2. LIMITATION: Timings are not implemented, due to it emulated machine runs
   twice fast as original.
3. LIMITATION: .RSS files are not supported.
4. LIMITATION: Some usage notes and trivia are needed in sysinfo.dat.

Lviv technical information
==========================

CPU:
----
    I8080 2.5MHz (2MHz in first machines)

Memory map:
-----------
    start-up map (cleared by the first I/O write operation done by the CPU):
    0000-3fff ROM mirror #1
    4000-7fff ROM mirror #2
    8000-bfff ROM mirror #3
    c000-ffff ROM

    normal map with video RAM off:
    0000-3fff RAM
    4000-7fff RAM
    8000-bfff RAM
    c000-ffff ROM

    normal map with video RAM on:
    0000-3fff mirrors 8000-bfff
    4000-7fff video RAM
    8000-bfff RAM
    c000-ffff ROM

Interrupts:
-----------
    No interrupts in Lviv.

Ports:
------
    Only A4-A5 are decoded.  A2-A3 is ignored in the console, but could be used by extension
    devices.

    C0-C3   8255 PPI
        Port A: extension slot output, printer data
            bits 0-4 joystick scanner output
        Port B: palette control, extension slot input or output
            sound on/off
            bit 7 sound on/off
            bits 0-6 palette select
        Port C: memory page changing, tape input and output,
            printer control, sound
            bits 0-3 extension slot input
            bits 4-7 extension slot output
            bit 7: joystick scanner input
            bit 6: printer control AC/busy
            bit 5: not used
            bit 4: tape in
            bit 3: not used
            bit 2: printer control SC/strobe
            bit 1: memory paging, 0 - video ram, 1 - ram
            bit 0: tape out, sound

    D0-D3   8255 PPI
        Port A:
            keyboard scanning
        Port B:
            keyboard reading
        Port C:
            keyboard scanning/reading

Keyboard:
---------
    Reset - connected to CPU reset line

                     Port D0
    --------T-------T-------T-------T-------T-------T-------T-------??
    |   7   |   6   |   5   |   4   |   3   |   2   |   1   |   0   |
    +-------+-------+-------+-------+-------+-------+-------+-------+---??
    | Shift |   ;   |       |  CLS  | Space |   R   |   G   |   6   | 0 |
    +-------+-------+-------+-------+-------+-------+-------+-------+---+
    |   Q   |Russian|       |  (G)  |   B   |   O   |   [   |   7   | 1 |
    +-------+-------+-------+-------+-------+-------+-------+-------+---+
    |   ^   |  Key  |   J   |  (B)  |   @   |   L   |   ]   |   8   | 2 |
    +-------+-------+-------+-------+-------+-------+-------+-------+---+
    |   X   |   P   |   N   |   5   |  Alt  |  Del  | Enter | Ready | 3 |
    +-------+-------+-------+-------+-------+-------+-------+-------+---+ Port D1
    |   T   |   A   |   E   |   4   |   _   |   .   |  Run  |  Tab  | 4 |
    +-------+-------+-------+-------+-------+-------+-------+-------+---+
    |   I   |   W   |   K   |   3   | Latin |   \   |   :   |   -   | 5 |
    +-------+-------+-------+-------+-------+-------+-------+-------+---+
    |   M   |   Y   |   U   |   2   |   /   |   V   |   H   |   0   | 6 |
    +-------+-------+-------+-------+-------+-------+-------+-------+---+
    |   S   |   F   |   C   |   1   |   ,   |   D   |   Z   |   9   | 7 |
    L-------+-------+-------+-------+-------+-------+-------+-------+----

             Port D2
    --------T-------T-------T-------??
    |   3   |   2   |   1   |   0   |
    +-------+-------+-------+-------+-----??
    | Right | Home  |ScrPrn |PrnLock|  4  |
    +-------+-------+-------+-------+-----+
    |  Up   |  F5   |  F0   |ScrLock|  5  |
    +-------+-------+-------+-------+-----+ Port D2
    | Left  |  F4   |  F1   | Sound |  6  |
    +-------+-------+-------+-------+-----+
    | Down  |  F3   |  F2   |  (R)  |  7  |
    L-------+-------+-------+-------+------

    Notes:
        CLS - clear screen
        (G) - clear screen with border and set COLOR 0,0,0
        (B) - clear screen with border and set COLOR 1,0,6
        (R) - clear screen with border and set COLOR 0,7,3
        Sound   - sound on/off
        ScrLock - screen lock
        PrnLock - printer on/off
        ScrPrn  - screen and printer output mode
        Russian - russian keyboard mode
        Latin   - latin keyboard mode
        Right   - cursor key
        Up  - cursor key
        Left    - cursor key
        Down    - cursor key
        Keyword - BASIC keyword


Video:
-----
    Screen resolution is 256x256 pixels. 4 colors at once are possible,
    but there is a possibility of palette change. Bits 0..6 of port 0xc1
    are used for palette setting.

    One byte of video-RAM sets 4 pixels. Colors of pixels are corrected
    by current palette. Each bits combination (2 bits sets one pixel on
    the display), corrected with palette register, sets REAL pixel color.

    PBx - bit of port 0xC1 numbered x
    R,G,B - output color components
    == - "is equal"
    ! - inversion

    00   R = PB3 == PB4; G = PB5; B = PB2 == PB6;
    01   R = PB4; G = !PB5; B = PB6;
    10   R = PB0 == PB4; G = PB5; B = !PB6;
    11   R = !PB4; G = PB1 == PB5; B = PB6;

    Bit combinations are result of concatenation of appropriate bits of
    high and low byte halfs.

    Example:
    ~~~~~~~~

    Some byte of video RAM:  1101 0001
    Value of port 0xC1:      x000 1110

    1101
    0001
    ----
    10 10 00 11

    1st pixel (10): R = 1; G = 0; B = 1;
    2nd pixel (10): R = 1; G = 0; B = 1;
    3rd pixel (00): R = 0; G = 0; B = 0;
    4th pixel (11): R = 1; G = 0; B = 0;


Sound:
------
    Buzzer connected to port 0xc2 (bit 0).
    Bit 7 of port 0xc1 - enable/disable speaker.


Timings:
--------

    The CPU timing is controlled by a KR580GF24 (Sovietic copy of i8224) connected to a 18MHz(?)
    oscillator. CPU frequency must be 18MHz/9 = 2MHz.

    Memory timing uses a 8-phase clock, derived from a 20MHz(?) video clock (called VCLK0 here:
    in the schematics, it comes from pin 6 of V8, and it is labelled "0'" in the video clock bus).
    This clock is divided by G7, G6 and D5 to generate the signals we call VCLK1-VCLK11.  The memory
    clock phases Phi0-Phi7 are generated in D7, whereas PHI'14 and PHI'15 are generated in D8.

    When the CPU accesses RAM, wait states are inserted until the RAM transfer is complete.

    CPU clock: 18MHz/9 = 2MHz
    memory cycle time: 20MHz/8 = 2.5MHz
    CPU memory access time: (min) approx. 9/20MHz = 450ns
                            (max) approx. 25/20MHz = 1250ns
    pixel clock: 20MHz/4 = 5MHz
    screen size: 256*256
    HBL: 64 pixel clock cycles
    VBL: 64 lines
    horizontal frequency: 5MHZ/(256+64) = 15.625kHz
    vertical frequency: 15.625kHz/(256+64) = 48.83Hz

             |<--------VIDEO WINDOW--------->|<----------CPU WINDOW--------->|<--
            _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _
    VCLK0    |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |
            _     ___     ___     ___     ___     ___     ___     ___     ___     ___
    VCLK1    |___|   |___|   |___|   |___|   |___|   |___|   |___|   |___|   |___|   |
            _         _______         _______         _______         _______
    VCLK2    |_______|       |_______|       |_______|       |_______|       |______|
            _                 _______________                 _______________
    VCLK3    |_______________|               |_______________|               |_______
            _                                 _______________________________
    VCLK4    |_______________________________|                               |_______

              _                               _                               _
    PHI0    _| |_____________________________| |_____________________________| |_____
                  _                               _                               _
    PHI1    _____| |_____________________________| |_____________________________| |_
                      _                               _
    PHI2    _________| |_____________________________| |_____________________________
                          _                               _
    PHI3    _____________| |_____________________________| |_________________________
                              _                               _
    PHI4    _________________| |_____________________________| |_____________________
                                  _                               _
    PHI5    _____________________| |_____________________________| |_________________
                                      _                               _
    PHI6    _________________________| |_____________________________| |_____________
                                          _                               _
    PHI7    _____________________________| |_____________________________| |_________
                                                                      _
    PHI'14  _________________________________________________________| |_____________
                                                                          _
    PHI'15  _____________________________________________________________| |_________
            __________             __________________________________________________
    RAS*              \___________/                   \_a_________/
            ______________                 __________________________________________
    CAS*                  \_______________/               \_a_____________/
            _________________________________________________________________________
    WR*                                                       \_b_________////////
            _________________________________________________________________________
    WRM*    \\\\\\\\\\\\\\\\\\\\\\\\\\_b__________________________________///////////
                        _________________________________________________________________________
        RDM*    \\\\\\\\\\\\\\\\\\\\\\\\\\_c __________________________________///////////
                        _________________________________________________________________________
    RA      \\\\\\\\\\\\\\\\\\\\\\\\\\_a__________________________________/

    DRAM
    ADDRESS video row /\ video column /XXX\CPU row (a)/\  CPU column (a)  /\ video row

    a: only if the CPU is requesting a RAM read/write
    b: only if the CPU is requesting a RAM write
    c: only if the CPU is requesting a RAM read


*******************************************************************************/

#include "emu.h"
#include "lviv.h"

#include "cpu/i8085/i8085.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "formats/lviv_lvt.h"


/* I/O ports */

void lviv_state::io_map(address_map &map)
{
	map(0x00, 0xff).rw(FUNC(lviv_state::io_r), FUNC(lviv_state::io_w));
}

/* memory w/r functions */

void lviv_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).bankrw(m_bank[0]);
	map(0x4000, 0x7fff).bankrw(m_bank[1]);
	map(0x8000, 0xbfff).bankrw(m_bank[2]);
	map(0xc000, 0xffff).bankrw(m_bank[3]);
}


/* keyboard input */
static INPUT_PORTS_START (lviv)
	PORT_START("KEY0") /* 2nd PPI port A bit 0 low */
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)            PORT_CHAR('6') PORT_CHAR('&')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)            PORT_CHAR('7') PORT_CHAR('\'')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)            PORT_CHAR('8') PORT_CHAR('(')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ready") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)          PORT_CHAR('\t')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)        PORT_CHAR('-') PORT_CHAR('=')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)            PORT_CHAR('0')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)            PORT_CHAR('9') PORT_CHAR(')')
	PORT_START("KEY1") /* 2nd PPI port A bit 1 low */
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)            PORT_CHAR('G')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)            PORT_CHAR('[')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)            PORT_CHAR(']')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)        PORT_CHAR(13)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Run") PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(DEL))
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_CHAR(':') PORT_CHAR('*')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)    PORT_CHAR('H')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)            PORT_CHAR('Z')
	PORT_START("KEY2") /* 2nd PPI port A bit 2 low */
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)            PORT_CHAR('R')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)            PORT_CHAR('O')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)            PORT_CHAR('L')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_END)          PORT_CHAR('.') PORT_CHAR('>')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)    PORT_CHAR('\\')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)        PORT_CHAR('V')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)            PORT_CHAR('D')
	PORT_START("KEY3") /* 2nd PPI port A bit 3 low */
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)        PORT_CHAR(' ')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)        PORT_CHAR('B')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)         PORT_CHAR('@')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Alt") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_MAMEKEY(RSHIFT))
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)       PORT_CHAR('_')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Latin") PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)        PORT_CHAR('/') PORT_CHAR('?')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME)         PORT_CHAR(',') PORT_CHAR('<')
	PORT_START("KEY4") /* 2nd PPI port A bit 4 low */
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cls") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("(G)") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("(B)") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)            PORT_CHAR('5') PORT_CHAR('%')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)            PORT_CHAR('4') PORT_CHAR(164)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)            PORT_CHAR('3') PORT_CHAR('#')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)            PORT_CHAR('2') PORT_CHAR('"')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)            PORT_CHAR('1') PORT_CHAR('!')
	PORT_START("KEY5") /* 2nd PPI port A bit 5 low */
		PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)            PORT_CHAR('J')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)            PORT_CHAR('N')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)            PORT_CHAR('E')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)            PORT_CHAR('K')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)            PORT_CHAR('U')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)            PORT_CHAR('C')
	PORT_START("KEY6") /* 2nd PPI port A bit 6 low */
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR(';') PORT_CHAR('+')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Russian") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)            PORT_CHAR('P')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)            PORT_CHAR('A')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)            PORT_CHAR('W')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)            PORT_CHAR('Y')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)            PORT_CHAR('F')
	PORT_START("KEY7") /* 2nd PPI port A bit 7 low */
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)            PORT_CHAR('Q')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)            PORT_CHAR('^')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)            PORT_CHAR('X')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)            PORT_CHAR('T')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)            PORT_CHAR('I')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)            PORT_CHAR('M')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)            PORT_CHAR('S')
	PORT_START("KEY8") /* 2nd PPI port C bit 0 low */
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PrnLck") PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ScrLck") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Sound") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("(R)") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_START("KEY9") /* 2nd PPI port C bit 1 low */
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ScrPrn") PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F0") PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9))
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_START("KEY10") /* 2nd PPI port C bit 2 low */
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Home") PORT_CODE(KEYCODE_PGUP) PORT_CHAR(UCHAR_MAMEKEY(PGUP))
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_SCRLOCK) PORT_CHAR(UCHAR_MAMEKEY(SCRLOCK))
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F12) PORT_CHAR(UCHAR_MAMEKEY(F12))
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11))
	PORT_START("KEY11") /* 2nd PPI port C bit 3 low */
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_START("RESET") /* CPU */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_PGDN) PORT_CHAR(UCHAR_MAMEKEY(PGDN)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(lviv_state::reset_button), 0)
	PORT_START("JOY") /* Joystick */
		PORT_BIT(0x01,  IP_ACTIVE_HIGH, IPT_JOYSTICK_UP)
		PORT_BIT(0x02,  IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN)
		PORT_BIT(0x04,  IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT)
		PORT_BIT(0x08,  IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT)
		PORT_BIT(0x10,  IP_ACTIVE_HIGH, IPT_BUTTON1)
		PORT_BIT(0x20,  IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x40,  IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x80,  IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END


/* machine definition */
void lviv_state::lviv(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, 2500000);
	m_maincpu->set_addrmap(AS_PROGRAM, &lviv_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &lviv_state::io_map);
	config.set_maximum_quantum(attotime::from_hz(60));

	I8255(config, m_ppi[0]);
	m_ppi[0]->in_pa_callback().set(FUNC(lviv_state::ppi0_porta_r));
	m_ppi[0]->out_pa_callback().set(FUNC(lviv_state::ppi0_porta_w));
	m_ppi[0]->in_pb_callback().set(FUNC(lviv_state::ppi0_portb_r));
	m_ppi[0]->out_pb_callback().set(FUNC(lviv_state::ppi0_portb_w));
	m_ppi[0]->in_pc_callback().set(FUNC(lviv_state::ppi0_portc_r));
	m_ppi[0]->out_pc_callback().set(FUNC(lviv_state::ppi0_portc_w));

	I8255(config, m_ppi[1]);
	m_ppi[1]->in_pa_callback().set(FUNC(lviv_state::ppi1_porta_r));
	m_ppi[1]->out_pa_callback().set(FUNC(lviv_state::ppi1_porta_w));
	m_ppi[1]->in_pb_callback().set(FUNC(lviv_state::ppi1_portb_r));
	m_ppi[1]->out_pb_callback().set(FUNC(lviv_state::ppi1_portb_w));
	m_ppi[1]->in_pc_callback().set(FUNC(lviv_state::ppi1_portc_r));
	m_ppi[1]->out_pc_callback().set(FUNC(lviv_state::ppi1_portc_w));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(0);
	m_screen->set_size(256, 256);
	m_screen->set_visarea(0, 256-1, 0, 256-1);
	m_screen->set_screen_update(FUNC(lviv_state::screen_update));
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(lviv_state::lviv_palette), std::size(s_palette));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	/* snapshot */
	SNAPSHOT(config, "snapshot", "sav").set_load_callback(FUNC(lviv_state::snapshot_cb));

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(lviv_lvt_format);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("lviv_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("lviv");

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("64K");
}


ROM_START(lviv)
	ROM_REGION(0x4000,"maincpu",0)
	ROM_SYSTEM_BIOS( 0, "lviv", "Lviv/L'vov" )
	ROMX_LOAD("lviv.bin", 0x0000, 0x4000, CRC(44a347d9) SHA1(74e067493b2b7d9ab17333202009a1a4f5e460fd), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "lviva", "Lviv/L'vov (alternate)" )
	ROMX_LOAD("lviva.bin", 0x0000, 0x4000, CRC(551622f5) SHA1(b225f3542b029d767b7db9dce562e8a3f77f92a2), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "lvivp", "Lviv/L'vov (prototype)" )
	ROMX_LOAD("lvivp.bin", 0x0000, 0x4000, CRC(f171c282) SHA1(c7dc2bdb02400e6b5cdcc50040eb06f506a7ed84), ROM_BIOS(2))
ROM_END

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY                         FULLNAME      FLAGS */
COMP( 1989, lviv, 0,      0,      lviv,    lviv,  lviv_state, empty_init, "Lviv Polytechnical Institute", "PK-01 Lviv", MACHINE_SUPPORTS_SAVE )



lw30.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Bartman/Abyss

#include "emu.h"

#include "cpu/z180/z180.h"
#include "imagedev/floppy.h"
#include "machine/timer.h"
#include "sound/beep.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "formats/lw30_dsk.h"

#include "util/utf8.h"


#define LOG_FLOPPY (1U << 1)

//#define VERBOSE (LOG_GENERAL | LOG_FLOPPY)
//#define VERBOSE (LOG_GENERAL)
#include "logmacro.h"

#define LOGFLOPPY(...) LOGMASKED(LOG_FLOPPY, __VA_ARGS__)


// command line parameters:
// -log -debug -window -intscalex 2 -intscaley 2 lw30 -resolution 960x256 -flop roms\lw30\tetris.img

// floppy: see src\devices\bus\vtech\memexp\floppy.cpp

//////////////////////////////////////////////////////////////////////////
// LW-30
//////////////////////////////////////////////////////////////////////////

// *** Hit Ctrl+Q during bootup to be able to start programs (like Tetris) from disk

/***************************************************************************

Brother LW-30
1991

Hardware:

#7
Hitachi
HD64180RP6
8-bit CMOS Micro Processing Unit
fully compatible with Zilog Z80180 (Z180)
6 MHz, DP-64S, Address Space 512 K Byte
MuRata CST12MTW 12.00 MHz Ceramic Resonator

#8
Mitsubishi
M65122ASP
UA5445-B LC-1

#6
NEC
D23C4001EC-172
UA2849-A
4MBit Mask ROM for Dictionary

#5
LH532H07
UA5362-A
2MBit Mask ROM

#11
Hitachi
HM6264ALP-15
High Speed CMOS Static RAM (8kbit x 8) 150ns

#10
Mitsubishi
M65017FP
UA5498-A
Murata CST4MGW 4.00 MHz Ceramic Resonator

#3, #4
Mitsubishi
HD74LS157P

#1, #2
NEC
D41464C-10
Dynamic NMOS RAM (64kbit x 4) 100ns

QA1, QA2
Mitsubishi
M54587P

#12
Texas Instruments
SN74HC04N

Floppy - custom single sided 3.5" DD
240kb capacity
256 bytes/sector
12 sectors/track
78 tracks
custom 5-to-8 GCR encoding (very similar to Apple II's 5-and-3 encoding)
300 rpm
FF FF FF used as sync-start, AB sync-mark for sector header, DE sync-mark for sector data
FAT12 File System

ROHM
BA6580DK
Read/Write Amplifier for FDD

see https://github.com/BartmanAbyss/brother-hardware/tree/master/1G%20-%20Brother%20LW-30 for datasheets, photos

Emulation Status:
Printer not working
Floppy Disk writing not working
Floppy Disk Sync not implemented (reading works)
Dictionary ROM not working
Cursor shapes not implemented except block cursor
Keyboard not 100% (mostly copied from LW-350)

TODO: find self-test; verify RAM address map
// 8kb SRAM, 64kb DRAM <- where?

***************************************************************************/

namespace {

class lw30_state : public driver_device
{
public:
	lw30_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		maincpu(*this, "maincpu"),
		screen(*this, "screen"),
		floppy(*this, "floppy"),
		beeper(*this, "beeper"),
		m_io_kbrow(*this, "kbrow.%u", 0),
		rom(*this, "maincpu"),
		font_normal(*this, "font_normal"),
		font_bold(*this, "font_bold")
	{
		//video_control = 0b00000010; // TEST LW-10 screen height
	}

	void lw30(machine_config& config);

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices
	required_device<hd64180rp_device> maincpu;
	required_device<screen_device> screen;

	required_device<floppy_connector> floppy;
	required_device<beep_device> beeper;
	required_ioport_array<9> m_io_kbrow;
	required_region_ptr<uint8_t> rom, font_normal, font_bold;

	// floppy
	uint8_t floppy_data = 0;
	uint8_t io_88 = 0;
	uint8_t io_98 = 0;
	uint8_t floppy_control = 0; // stepper motor control
	uint8_t floppy_steps = 0; // quarter track
	uint8_t floppy_shifter = 0, floppy_latch = 0;
	bool floppy_read_until_zerobit = false;

	// video
	uint8_t videoram[0x2000]; // 80 chars * 14 lines; 2 bytes per char (attribute, char)
	uint8_t video_cursor_x, video_cursor_y, video_pos_x, video_pos_y, video_control, io_b8;
	uint8_t cursor_state;

	// screen updates
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	uint8_t illegal_r(offs_t offset)
	{
		if(!machine().side_effects_disabled())
			LOG("%s: unmapped memory read from %0*X\n", machine().describe_context(), 6, offset);
		return 0;
	}
	void illegal_w(offs_t offset, uint8_t data)
	{
		LOG("%s: unmapped memory write to %0*X = %0*X\n", machine().describe_context(), 6, offset, 2, data);
	}

	// IO
	void video_cursor_x_w(uint8_t data) // 70
	{
		video_cursor_x = data;
	}
	void video_cursor_y_w(uint8_t data) // 71
	{
		video_cursor_y = data;
	}
	void video_pos_x_w(uint8_t data) // 72
	{
		video_pos_x = data;
	}
	void video_pos_y_w(uint8_t data) // 73
	{
		video_pos_y = data;
	}
	uint8_t video_data_r() // 74
	{
		uint8_t data = 0x00;
		if(video_pos_y < 0x20)
			data = videoram[video_pos_y * 256 + video_pos_x];
		else {
			if(!machine().side_effects_disabled())
				LOG("%s: video_data_r out of range: x=%u, y=%u\n", machine().describe_context(), video_pos_x, video_pos_y);
		}

		return data;
	}

	void video_data_w(uint8_t data) // 74
	{
		if(video_pos_y < 0x20)
			videoram[video_pos_y * 256 + video_pos_x] = data;
		else
			LOG("%s: video_data_w out of range: x=%u, y=%u\n", machine().describe_context(), video_pos_x, video_pos_y);

		video_pos_x++;
		if(video_pos_x == 0)
			video_pos_y++;
	}

	uint8_t video_control_r() // 75
	{
		return video_control;
	}
	void video_control_w(uint8_t data)
	{
		video_control = data; // | 0b00000010; // TEST LW-10 screen height
	}
	// 76
	uint8_t io_77_r() // config
	{
		// TODO: use PORT_CONFNAME, etc
		uint8_t out = 0x20; // 14 lines
		out |= 0x00; // german
		//out |= 0x01; // french
		//out |= 0x02; // german
		return ~out;
	}

	// Floppy
	TIMER_DEVICE_CALLBACK_MEMBER(floppy_timer_callback)
	{
		auto floppy_device = floppy->get_device();
		if(floppy_device->ready_r() != false)
			return;

		floppy_latch <<= 1;

		attotime now = machine().time();
		attotime when = now - attotime::from_usec(4);
		attotime reversal = floppy_device->get_next_transition(when);
		if(reversal > when && reversal <= now)
			floppy_latch |= 1;

		floppy_shifter++;
		if((floppy_read_until_zerobit && (floppy_latch & 1) == 0) || (!floppy_read_until_zerobit && floppy_shifter == 8)) {
			//LOGFLOPPY("%s: floppy_timer_callback: floppy_read_until_zerobit=%d latch=%02X\n", machine().describe_context(), floppy_read_until_zerobit, floppy_latch);
			floppy_control |= 0x80; // floppy_data_available = true;
			floppy_data = floppy_latch;
			floppy_latch = floppy_shifter = 0;
			floppy_read_until_zerobit = false;
		}
		//LOGFLOPPY("%s: floppy_timer_callback: IO_80=%02X, shifter=%u offset=%u\n", machine().describe_context(), io_80, floppy_shifter, floppy_track_offset % cache.size());
		//LOGFLOPPY("%s: read_io_80 track=%d,offset=%4x => %02x\n", callstack(), floppy_steps / 4, floppy_track_offset, ret);
	}

	uint8_t floppy_data_r() // 80
	{
		if(!machine().side_effects_disabled()) {
			floppy_control &= ~0x80; // floppy_data_available = false;
			LOGFLOPPY("%s: read %02X from IO 80\n", machine().describe_context(), floppy_data);
		}
		return floppy_data;
	}
	void floppy_data_w(uint8_t data)
	{
		LOGFLOPPY("%s: write %02X to IO 80\n", machine().describe_context(), data);
		floppy_data = data;
	}

	uint8_t io_88_r()
	{
		// bit 0: set in start_write; cleared in end_write
		// bit 1: pulsed after 3*0xFF sync (read next floppydata until zero-bit)
		// bit 2: cleared in stepper routines, rst28_06
		// bit 3: set in start_write; cleared in end_write
		// bit 5: cleared in rst28_06; motor-on?
		if(!machine().side_effects_disabled())
			LOGFLOPPY("%s: read %02X from IO 88\n", machine().describe_context(), io_88);
		return io_88;
	}
	void io_88_w(uint8_t data)
	{
		LOGFLOPPY("%s: write %02X to IO 88\n", machine().describe_context(), data);
		io_88 = data;
		floppy->get_device()->mon_w((io_88 & (1 << 5)) == 0);
	}

	uint8_t floppy_status_r() // 90
	{
		// bit 7 set; data ready from floppy
		// bit 6 clear; unknown meaning
		// bit 5 clear; unknown meaning
		// bit 4 clear; unknown meaning
		// bit 3-0: stepper motor
		if(!machine().side_effects_disabled())
			LOGFLOPPY("%s: read %02X from IO 90\n", machine().describe_context(), floppy_control);
		return floppy_control;
	}
	void floppy_stepper_w(uint8_t data)
	{
		LOGFLOPPY("%s: write %02X to IO 90\n", machine().describe_context(), data);
		// write directly to 4-wire bipolar stepper motor (see stepper_table)
		// a rotation to the left means decrease quarter-track
		// a rotation to the right means increase quarter-track
		const auto rol4 = [](uint8_t d) { return ((d << 1) & 0b1111) | ((d >> 3) & 0b0001); };
		const auto ror4 = [](uint8_t d) { return ((d >> 1) & 0b0111) | ((d << 3) & 0b1000); };
		const auto old_track = floppy_steps / 4;
		switch(data & 0xf) {
		case 0b0011:
		case 0b0110:
		case 0b1100:
		case 0b1001:
			if((data & 0x0f) == rol4(floppy_control))
				floppy_steps--;
			else if((data & 0x0f) == ror4(floppy_control))
				floppy_steps++;
			else
				LOGFLOPPY("%s: illegal step %02x=>%02x\n", machine().describe_context(), floppy_control, data);
			break;
		default:
			LOGFLOPPY("%s: initial step %02x=>%02x\n", machine().describe_context(), floppy_control, data);
			break;
		}
		const auto new_track = floppy_steps / 4;
		auto floppy_device = floppy->get_device();
		if(new_track != old_track) {
			floppy_device->dir_w(new_track < old_track);
			floppy_device->stp_w(true);
			floppy_device->stp_w(false);
		}
		LOGFLOPPY("%s: floppy_steps=%3d => old_track=%2d new_track=%2d cyl=%2d\n", machine().describe_context(), floppy_steps, old_track, new_track, floppy_device->get_cyl());
		assert(floppy_device->get_cyl() == new_track);
		floppy_control = (floppy_control & 0xf0) | (data & 0x0f);
	}

	uint8_t io_98_r()
	{
		// mirrored in RAM
		// bit 0: cleared in rst28_06 in mirror
		// bit 2: cleared before formatting in mirror; set after formatting
		// bit 3: cleared before formatting in mirror
		// bit 4: cleared before writing in mirror; set after writing
		if(!machine().side_effects_disabled()) {
			if(io_88 & 0b10)
				floppy_read_until_zerobit = true;
			else
				floppy_read_until_zerobit = false;

			LOGFLOPPY("%s: read %02X from IO 98\n", machine().describe_context(), io_98);
		}
		return io_98;
	}
	void io_98_w(uint8_t data)
	{
		LOGFLOPPY("%s: write %02X to IO 98\n", machine().describe_context(), data);
		io_98 = data;
	}

	uint8_t illegal_io_r(offs_t offset, uint8_t mem_mask = ~0)
	{
		if(!machine().side_effects_disabled())
			LOGFLOPPY("%s: unmapped IO read from %0*X & %0*X\n", machine().describe_context(), 4, offset + 0x40, 2, mem_mask);
		return 0;
	}
	void illegal_io_w(offs_t offset, uint8_t data, uint8_t mem_mask = ~0)
	{
		LOGFLOPPY("%s: unmapped IO write to %0*X = %0*X & %0*X\n", machine().describe_context(), 4, offset + 0x40, 2, data, 2, mem_mask);
	}

	uint8_t io_b0_r()
	{
		// Tetris reads bit 3, needed for correct keyboard layout
		return 0b1000;
	}
	uint8_t io_b8_r() // B8 (keyboard)
	{
		// keyboard matrix
		if(io_b8 <= 8)
			return m_io_kbrow[io_b8]->read();
		return 0x00;
	}
	void io_b8_w(uint8_t data)
	{
		io_b8 = data;
	}

	void beeper_w(uint8_t data) // F0
	{
		beeper->set_state(~data & 0x01);
	}

	void irqack_w(uint8_t) // F8
	{
		maincpu->set_input_line(INPUT_LINE_IRQ1, CLEAR_LINE);
	}

	TIMER_DEVICE_CALLBACK_MEMBER(int1_timer_callback)
	{
		maincpu->set_input_line(INPUT_LINE_IRQ1, ASSERT_LINE);
	}

	TIMER_DEVICE_CALLBACK_MEMBER(cursor_timer_callback)
	{
		cursor_state = !cursor_state;
	}

	static void floppy_formats(format_registration &fr)
	{
		fr.add(FLOPPY_LW30_FORMAT);
	}

	static void lw30_floppies(device_slot_interface &device) ATTR_COLD
	{
		device.option_add("35ssdd", FLOPPY_35_SSDD);
	}

	void map_program(address_map &map) ATTR_COLD
	{
		map(0x00000, 0x01fff).rom();
		map(0x02000, 0x05fff).ram();
		map(0x06000, 0x3ffff).rom();
		map(0x50000, 0x51fff).ram(); // ???
		map(0x61000, 0x61fff).ram();
		map(0x42000, 0x45fff).rom().region("maincpu", 0x02000).w(FUNC(lw30_state::illegal_w)); // => ROM 0x02000-0x05fff
		map(0x65000, 0x70fff).ram();
	}

	void map_io(address_map &map) ATTR_COLD
	{
		map.global_mask(0xff);
		map(0x00, 0x3f).noprw(); // Z180 internal registers

		// video
		map(0x70, 0x70).w(FUNC(lw30_state::video_cursor_x_w));
		map(0x71, 0x71).w(FUNC(lw30_state::video_cursor_y_w));
		map(0x72, 0x72).w(FUNC(lw30_state::video_pos_x_w));
		map(0x73, 0x73).w(FUNC(lw30_state::video_pos_y_w));
		map(0x74, 0x74).rw(FUNC(lw30_state::video_data_r), FUNC(lw30_state::video_data_w));
		map(0x75, 0x75).rw(FUNC(lw30_state::video_control_r), FUNC(lw30_state::video_control_w));
		map(0x76, 0x76).noprw(); // NOP just to shut up the log
		map(0x77, 0x77).r(FUNC(lw30_state::io_77_r)).nopw(); // NOP just to shut up the log

		// floppy
		map(0x80, 0x80).rw(FUNC(lw30_state::floppy_data_r), FUNC(lw30_state::floppy_data_w));
		map(0x88, 0x88).rw(FUNC(lw30_state::io_88_r), FUNC(lw30_state::io_88_w));
		map(0x90, 0x90).rw(FUNC(lw30_state::floppy_status_r), FUNC(lw30_state::floppy_stepper_w));
		map(0x98, 0x98).rw(FUNC(lw30_state::io_98_r), FUNC(lw30_state::io_98_w));

		map(0xa8, 0xa8).noprw(); // NOP just to shut up the log
		map(0xb0, 0xb0).r(FUNC(lw30_state::io_b0_r));
		map(0xb8, 0xb8).rw(FUNC(lw30_state::io_b8_r), FUNC(lw30_state::io_b8_w));
		map(0xd8, 0xd8).noprw(); // NOP just to shut up the log
		map(0xf0, 0xf0).w(FUNC(lw30_state::beeper_w));
		map(0xf8, 0xf8).w(FUNC(lw30_state::irqack_w));

		//map(0x40, 0xff).rw(FUNC(lw30_state::illegal_io_r), FUNC(lw30_state::illegal_io_w));
	}
};

uint32_t lw30_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	// based on LW-350 ROM draw_char routine @ 6B14
	enum attrs : uint8_t {
		UNDERLINE           = 0b00000001,
		OVERLINE            = 0b00000010,
		BOLD                = 0b00000100,
		VERTICAL_LINE       = 0b00001000,
		INVERT_FULL         = 0b00010000,
		INVERT_UPPER_HALF   = 0b00100000,
		INVERT_LOWER_HALF   = 0b01000000
	};

	const rgb_t palette[]{
		0xffffffff,
		0xff000000,
	};

	enum control : uint8_t {
		DISPLAY_ON          = 0b00000001,
		HALF_HEIGHT         = 0b00000010, // 64px height (LW-10/20) instead of 128px height (LW-30)
		BITMAP_MODE         = 0b00001000,
		TILE_MODE           = 0b00100000, // 8x8 tiles at videoram[0x1000]
	};

	if(video_control & DISPLAY_ON) {
		if(video_control & TILE_MODE) {
			uint8_t pixmap[60 * 128]; // pixel data
			for(auto y = 0; y < 16; y++) {
				for(auto x = 0; x < 60; x++) {
					const auto atr = videoram[y * 256 + x * 2 + 0];
					const auto chr = videoram[y * 256 + x * 2 + 1];
					const auto fnt = &videoram[0x1000 + chr * 8 + ((atr & BOLD) ? 0x800 : 0)];
					uint8_t charbuf[8];
					for(int i = 0; i < 8; i++) {
						charbuf[i] = fnt[i];
					}
					if(atr & UNDERLINE) {
						charbuf[7] = 0xff;
					}
					if(atr & VERTICAL_LINE) {
						for(int i = 0; i < 8; i++) {
							charbuf[i] |= 0b1;
						}
					}

					for(int i = 0; i < 8; i++) {
						pixmap[(y * 8 + i) * 60 + x] = charbuf[i];
					}
				}
			}
			for(auto y = 0; y < 128; y++) {
				uint32_t *p = &bitmap.pix(y);
				for(auto x = 0; x < 480; x += 8) {
					const auto gfx = pixmap[y * 60 + x / 8];
					*p++ = palette[BIT(gfx, 7)];
					*p++ = palette[BIT(gfx, 6)];
					*p++ = palette[BIT(gfx, 5)];
					*p++ = palette[BIT(gfx, 4)];
					*p++ = palette[BIT(gfx, 3)];
					*p++ = palette[BIT(gfx, 2)];
					*p++ = palette[BIT(gfx, 1)];
					*p++ = palette[BIT(gfx, 0)];
				}
			}
		} else if(video_control & BITMAP_MODE) {
			for(auto y = 0; y < 128; y++) {
				uint32_t *p = &bitmap.pix(y);
				for(auto x = 0; x < 480; x += 8) {
					const auto gfx = videoram[y * 64 + x / 8];
					*p++ = palette[BIT(gfx, 7)];
					*p++ = palette[BIT(gfx, 6)];
					*p++ = palette[BIT(gfx, 5)];
					*p++ = palette[BIT(gfx, 4)];
					*p++ = palette[BIT(gfx, 3)];
					*p++ = palette[BIT(gfx, 2)];
					*p++ = palette[BIT(gfx, 1)];
					*p++ = palette[BIT(gfx, 0)];
				}
			}
		} else {
			// default font
			uint8_t pixmap[80 * 128]{}; // pixel data
			for(auto y = 0; y < 14; y++) {
				for(auto x = 0; x < 80; x++) {
					const auto atr = videoram[y * 256 + x * 2 + 0];
					const auto chr = videoram[y * 256 + x * 2 + 1];
					const auto fnt = (atr & BOLD) ? &font_bold[chr * 8] : &font_normal[chr * 8];
					uint8_t charbuf[9];
					charbuf[0] = 0x00;
					for(int i = 0; i < 8; i++) {
						charbuf[i + 1] = fnt[i];
					}

					if(atr & UNDERLINE) {
						charbuf[8] = 0xff;
					}
					if(atr & OVERLINE) {
						charbuf[0] = 0xff;
					}
					if(atr & VERTICAL_LINE) {
						for(int i = 0; i < 9; i++) {
							charbuf[i] |= 0b1;
						}
					}
					if(atr & INVERT_FULL) {
						for(int i = 0; i < 9; i++) {
							charbuf[i] ^= 0xff;
						}
					}
					if(atr & INVERT_LOWER_HALF) {
						for(int i = 4; i < 9; i++) {
							charbuf[i] ^= 0xff;
						}
					}
					if(atr & INVERT_UPPER_HALF) {
						for(int i = 0; i < 5; i++) {
							charbuf[i] ^= 0xff;
						}
					}

					for(int i = 0; i < 9; i++) {
						pixmap[(y * 9 + i) * 80 + x] = charbuf[i];
					}
				}
			}

			// draw cursor; TODO: shape
			if(cursor_state) {
				const auto cursor_x = video_cursor_x & 0x7f;
				const auto cursor_y = (video_cursor_x >> 7) | ((video_cursor_y & 7) << 1);
				if(cursor_x < 80 && cursor_y < 14) {
					for(int i = 0; i < 9; i++) {
						pixmap[(cursor_y * 9 + i) * 80 + cursor_x] ^= 0xff;
					}
				}
			}
			for(auto y = 0; y < 128; y++) {
				uint32_t *p = &bitmap.pix(y);
				for(auto x = 0; x < 640; x += 8) {
					const auto gfx = pixmap[y * 80 + x / 8];
					*p++ = palette[BIT(gfx, 5)];
					*p++ = palette[BIT(gfx, 4)];
					*p++ = palette[BIT(gfx, 3)];
					*p++ = palette[BIT(gfx, 2)];
					*p++ = palette[BIT(gfx, 1)];
					*p++ = palette[BIT(gfx, 0)];
				}
			}
		}
	} else {
		// display off
		for(auto y = 0; y < 128; y++) {
			uint32_t *p = &bitmap.pix(y);
			for(auto x = 0; x < 480; x++) {
				*p++ = palette[0];
			}
		}
	}

	return 0;
}

void lw30_state::machine_start()
{
	screen->set_visible_area(0, 480 - 1, 0, 128 - 1);

	// patch out printer init
	rom[0x280f4] = 0x00;

	// patch out autosave load
	rom[0x28c3a] = rom[0x28c3a + 1] = rom[0x28c3a + 2] = 0x00;

	// always jump to "zusatzprogramme" (otherwise hit Ctrl+Q during bootup)
	//rom[0x28103] = 0xc3;

	// floppy debugging
	//if(machine().debug_enabled()) {
	//  machine().debugger().console().execute_command(R"(bp 6a2c,1,{logerror "expect AB; A=%02X\n",a; g})", false);
	//  machine().debugger().console().execute_command(R"(bp 6617,1,{logerror "expect DE; A=%02X\n",a; g})", false);
	//}
}

void lw30_state::machine_reset()
{
	cursor_state = 0;
	video_cursor_x = video_cursor_y = 0;
	video_pos_x = video_pos_y = 0;
	video_control = 0;
	// TODO more reset variables

	memcpy(&videoram[0x1000], font_normal, 0x800);
}

static INPUT_PORTS_START(lw30)
	PORT_START("kbrow.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_4)      PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_3)      PORT_CHAR('3') PORT_CHAR(U'§')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_W)      PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_E)      PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_D)      PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_X)      PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_TAB)    PORT_CHAR(UCHAR_MAMEKEY(TAB))

	PORT_START("kbrow.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_5)      PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_6)      PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_R)      PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_T)      PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_C)      PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_F)      PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_UP)                 PORT_CODE(KEYCODE_UP)     PORT_CHAR(UCHAR_MAMEKEY(UP))

	PORT_START("kbrow.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_8)      PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_7)      PORT_CHAR('7') PORT_CHAR('/')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_Z)      PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_H)      PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_G)      PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_V)      PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_END)    PORT_CHAR(UCHAR_MAMEKEY(END))

	PORT_START("kbrow.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_Q)          PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_A)          PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_S)          PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_CAPSLOCK)   PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))

	PORT_START("kbrow.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_J)          PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_B)          PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_N)          PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

	PORT_START("kbrow.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_MINUS)      PORT_CHAR(U'ß') PORT_CHAR('?')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_0)          PORT_CHAR('0')  PORT_CHAR('=')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_P)          PORT_CHAR('p')  PORT_CHAR('P')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_O)          PORT_CHAR('o')  PORT_CHAR('O')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_M)          PORT_CHAR('m')  PORT_CHAR('M')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')  PORT_CHAR(';')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_MENU)       PORT_CHAR(UCHAR_MAMEKEY(MENU))

	PORT_START("kbrow.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("FILE/SPELL")            PORT_CODE(KEYCODE_HOME)       PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_COLON)      PORT_CHAR(U'ö') PORT_CHAR(U'Ö')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('+') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR(U'ü') PORT_CHAR(U'Ü')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kbrow.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TW/WP/LAYOUT")          PORT_CODE(KEYCODE_PRTSCR)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CANCEL")                PORT_CODE(KEYCODE_PAUSE)      PORT_CHAR(UCHAR_MAMEKEY(CANCEL))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(UCHAR_MAMEKEY(ENTER))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(UCHAR_MAMEKEY(BACKSPACE))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    //PORT_CODE(KEYCODE_)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("WORD OUT/LINE OUT")     //PORT_CODE(KEYCODE_)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(UCHAR_MAMEKEY(SPACE))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_LSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("kbrow.8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(U'´') PORT_CHAR(U'`')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_L)          PORT_CHAR('l')  PORT_CHAR('K')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    //PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_K)          PORT_CHAR('k')  PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')  PORT_CHAR(':')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('-')  PORT_CHAR('_')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(U'ä') PORT_CHAR(U'Ä')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

void lw30_state::lw30(machine_config &config)
{
	// basic machine hardware
	HD64180RP(config, maincpu, 12'000'000 / 2);
	maincpu->set_addrmap(AS_PROGRAM, &lw30_state::map_program);
	maincpu->set_addrmap(AS_IO, &lw30_state::map_io);

	// video hardware
	SCREEN(config, screen, SCREEN_TYPE_RASTER);
	screen->set_color(rgb_t(6, 245, 206));
	screen->set_physical_aspect(480, 128);
	screen->set_refresh_hz(78.1);
	screen->set_screen_update(FUNC(lw30_state::screen_update));
	screen->set_size(480, 128);

	// floppy disk
	FLOPPY_CONNECTOR(config, floppy, lw30_state::lw30_floppies, "35ssdd", lw30_state::floppy_formats).enable_sound(true);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, beeper, 4'000).add_route(ALL_OUTPUTS, "mono", 1.0); // 4.0 kHz

	// timers
	TIMER(config, "timer_1khz").configure_periodic(FUNC(lw30_state::int1_timer_callback), attotime::from_hz(1000));
	TIMER(config, "timer_floppy").configure_periodic(FUNC(lw30_state::floppy_timer_callback), attotime::from_usec(4));
	TIMER(config, "timer_cursor").configure_periodic(FUNC(lw30_state::cursor_timer_callback), attotime::from_msec(512));
}

/***************************************************************************
  Machine driver(s)
***************************************************************************/

ROM_START( lw30 )
	ROM_REGION( 0x40000, "maincpu", 0 )
	ROM_LOAD("ua5362-a", 0x00000, 0x40000, CRC(dac77867) SHA1(5c7ab30dec55a24eb1b7f241e5015e3836ebf077))
	ROM_REGION(0x80000, "dictionary", 0)
	ROM_LOAD("ua2849-a", 0x00000, 0x80000, CRC(fa8712eb) SHA1(2d3454138c79e75604b30229c05ed8fb8e7d15fe))
	ROM_REGION(0x800, "font_normal", 0)
	ROM_LOAD("font-normal", 0x00000, 0x800, CRC(56a8b45d) SHA1(3f2860667ee56944cf5a79bfd4e80bebf532b51a))
	ROM_REGION(0x800, "font_bold", 0)
	ROM_LOAD("font-bold", 0x00000, 0x800, CRC(d81b79c4) SHA1(fa6be6f9dd0d7ae6d001802778272ecce8f425bc))
ROM_END

} // anonymous namespace

//    YEAR  NAME  PARENT COMPAT   MACHINE INPUT  CLASS           INIT              COMPANY         FULLNAME  FLAGS
COMP( 1991, lw30, 0,     0,       lw30,   lw30,  lw30_state,     empty_init,       "Brother",      "LW-30",  MACHINE_NODEVICE_PRINTER )



lw350.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Bartman/Abyss

#include "emu.h"

#include "cpu/z180/z180.h"
#include "imagedev/floppy.h"
#include "imagedev/floppy.h"
#include "machine/timer.h"
#include "machine/upd765.h"
#include "sound/beep.h"
#include "video/mc6845.h"

#include "debug/debugcpu.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "util/utf8.h"

// command line parameters:
// -log -debug -window -intscalex 2 -intscaley 2 lw350 -resolution 960x256 -flop roms\lw350\Brother_LW-200-300_GW-24-45_Ver1.0_SpreadsheetProgramAndDataStorageDisk.img

/***************************************************************************

Brother LW-350
1995

Hardware:

#4
Hitachi HG62F33R63FH
US0021-A
CMOS Gate Array
3,297 gates, QFPS-136
Murata Ceralock CST-MXW 16.00 MHz Ceramic Resonator

#1
Hitachi HD63266F
CMOS Floppy Disk Controller
QFP-64
Murata Ceralock CST-MXW 16.00 MHz Ceramic Resonator

#2
Hitachi HM658128ALP-10
01105330
131072-word x 8-bit High Speed CMOS Pseudo Static RAM
DP-32
100 ns

#3
Hitachi HN62334BP
UC6273-A-LWB6
524288-word x 8-bit CMOS Mask Programmable ROM
DP-32
150 ns

#5
Hitachi HD64180ZP8
8-bit CMOS Micro Processing Unit
fully compatible with Zilog Z80180 (Z180)
8 MHz, DP-64S, Address Space 512 K Byte
Murata Ceralock CST-MXW 16.00 MHz Ceramic Resonator

1.44MB Floppy Drive
MS-DOS compatible FAT12 disk format

Hidden Keys during "DECKEL OFFEN!" ("Case Open!")
- Ctrl+Shift+Cursor Right: LCD Test Menu
- Ctrl+Shift+Backspace: Adjustment Printer Menu

Hidden Keys during "SCHREIBMASCHINE" ("Typewriter")
- Ctrl+Shift+Cursor Right: LCD Test Menu
- Ctrl+Shift+Backspace: Self Test Menu
- Ctrl+Shift+Enter: Self Print Menu

Emulation Status:
- Printer not working

see https://github.com/BartmanAbyss/brother-hardware/tree/master/2G%20-%20Brother%20LW-350 for datasheets, photos

***************************************************************************/

namespace {

class lw350_state : public driver_device
{
public:
	lw350_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		maincpu(*this, "maincpu"),
		screen(*this, "screen"),
		floppy(*this, "fdc:0"),
		fdc(*this, "fdc"),
		beeper(*this, "beeper"),
		io_kbrow(*this, "kbrow.%u", 0),
		rombank(*this, "rom"),
		vram(*this, "vram")
	{ }

	void lw350(machine_config &config) ATTR_COLD;

protected:
	// driver_device overrides
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;
	void video_start() override ATTR_COLD;

private:
	// devices
	required_device<hd64180rp_device> maincpu;
	required_device<screen_device> screen;
	required_device<floppy_connector> floppy;
	required_device<hd63266f_device> fdc;
	required_device<beep_device> beeper;
	required_ioport_array<9> io_kbrow;
	required_memory_bank rombank;

	required_shared_ptr<u8> vram;
	uint8_t io_70, io_b8, io_90;
	int fdc_drq;

	// screen updates
	uint32_t screen_update(screen_device& screen, bitmap_rgb32& bitmap, const rectangle& cliprect);

	uint8_t illegal_r(offs_t offset, uint8_t mem_mask = ~0) {
		logerror("%s: unmapped memory read from %0*X & %0*X\n", machine().describe_context(), 6, offset, 2, mem_mask);
		return 0;
	}
	void illegal_w(offs_t offset, uint8_t data, uint8_t mem_mask = ~0) {
		logerror("%s: unmapped memory write to %0*X = %0*X & %0*X\n", machine().describe_context(), 6, offset, 2, data, 2, mem_mask);
	}

	// IO
	void io_70_w(uint8_t data) {
		io_70 = data;
	}
	uint8_t io_74_r() {
		// 0x00: 7 lines display (64 pixels height)
		// 0x80: 14 lines display (128 pixels height)
		return 0x80;
	}
	uint8_t io_a8_r() {
		// bit 0: case open
		// bit 2: carriage return indicator
		//return 0x01; // case open
		return 0x00;
	}
	uint8_t io_b8_r() {
		// keyboard matrix
		if(io_b8 <= 8)
			return io_kbrow[io_b8]->read();

		switch(io_b8) {
		// get language
		case 0x09:  // valid results (see get_index_from_language)
			//return ~0x20; // french
			//return ~0x10; // french
			//return ~0x08; // german
			//return ~0x04; // french
			//return ~0x02; // french
			//return ~0x01; // german
			return ~0x00; // german
		default:   return 0x00;
		}
	}
	void io_b8_w(uint8_t data) {
		io_b8 = data;
	}
	void rombank_w(uint8_t data) { // E0
		rombank->set_entry(data & 0x03);
	}
	void beeper_w(uint8_t data) { // F0
		beeper->set_state(~data & 0x01);
	}
	void irqack_w(uint8_t) { // F8
		maincpu->set_input_line(INPUT_LINE_IRQ1, CLEAR_LINE);
	}

	uint8_t io_7e_r() {
		return 0x80; // 1.44mb floppy
	}
	void io_7e_w(uint8_t data) { // 7e
	}
	uint8_t io_90_r() { // 90
		return floppy ? (!floppy->get_device()->idx_r()) << 6 : 0;
	}

	TIMER_DEVICE_CALLBACK_MEMBER(io_90_timer_callback);
	TIMER_DEVICE_CALLBACK_MEMBER(int1_timer_callback);

	void map_program(address_map &map) ATTR_COLD {
		map(0x00000, 0x01fff).rom();
		map(0x02000, 0x05fff).ram();
		map(0x06000, 0x3ffff).rom();
		map(0x40000, 0x5ffff).bankr(rombank);
		map(0x60000, 0x617ff).ram();
		map(0x61800, 0x63fff).ram().share(vram);
		map(0x64000, 0x71fff).ram();
		map(0x72000, 0x75fff).rom().region("maincpu", 0x2000); // => ROM 0x02000-0x05fff
		map(0x76000, 0x7ffff).ram();
	}

	void map_io(address_map &map) ATTR_COLD {
		map.global_mask(0xff);
		map(0x00, 0x3f).noprw(); // Z180 internal registers
		map(0x70, 0x70).w(FUNC(lw350_state::io_70_w));
		map(0x74, 0x74).r(FUNC(lw350_state::io_74_r));

		// floppy
		map(0x78, 0x78).rw(fdc, FUNC(upd765a_device::msr_r), FUNC(hd63266f_device::abort_w));
		map(0x79, 0x79).lrw8(
				[this] () { return (fdc_drq ? fdc->dma_r() : fdc->fifo_r()); }, "fdc_r",
				[this] (u8 data) { fdc_drq ? fdc->dma_w(data) : fdc->fifo_w(data); }, "fdc_w");
		map(0x7a, 0x7a).r(fdc, FUNC(hd63266f_device::extstat_r));
		map(0x7e, 0x7e).rw(FUNC(lw350_state::io_7e_r), FUNC(lw350_state::io_7e_w));
		map(0x90, 0x90).r(FUNC(lw350_state::io_90_r));

		// printer
		map(0xa8, 0xa8).r(FUNC(lw350_state::io_a8_r));

		map(0xb8, 0xb8).rw(FUNC(lw350_state::io_b8_r), FUNC(lw350_state::io_b8_w));
		map(0xd8, 0xd8).nopw();
		map(0xe0, 0xe0).w(FUNC(lw350_state::rombank_w));
		map(0xf0, 0xf0).w(FUNC(lw350_state::beeper_w));
		map(0xf8, 0xf8).w(FUNC(lw350_state::irqack_w));

		//map(0x40, 0xff).rw(FUNC(lw350_state::illegal_io_r), FUNC(lw350_state::illegal_io_w));
	}
};

void lw350_state::video_start()
{
}

uint32_t lw350_state::screen_update(screen_device& screen, bitmap_rgb32& bitmap, const rectangle& cliprect)
{
	// video on?
	if(!BIT(io_70, 0))
		return 0;

	// backlight on?
	//if(BIT(io_70, 7))
		//...

	const rgb_t palette[]{
		0xffffffff,
		0xff000000,
	};

	for(auto y = 0; y < 128; y++) {
		uint32_t* p = &bitmap.pix(y);
		for(auto x = 0; x < 640; x += 8) {
			auto gfx = vram[y * 80 + x / 8];
			//*p++ = palette[BIT(gfx, 7)];
			//*p++ = palette[BIT(gfx, 6)];
			*p++ = palette[BIT(gfx, 5)];
			*p++ = palette[BIT(gfx, 4)];
			*p++ = palette[BIT(gfx, 3)];
			*p++ = palette[BIT(gfx, 2)];
			*p++ = palette[BIT(gfx, 1)];
			*p++ = palette[BIT(gfx, 0)];
		}
	}
	return 0;
}

static INPUT_PORTS_START(lw350)
	PORT_START("kbrow.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_4)      PORT_CHAR('4') PORT_CHAR('%')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_3)      PORT_CHAR('3') PORT_CHAR(U'§')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_W)      PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_E)      PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_D)      PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_X)      PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_TAB)    PORT_CHAR(UCHAR_MAMEKEY(TAB))

	PORT_START("kbrow.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_5)      PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_6)      PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_R)      PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_T)      PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_C)      PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_F)      PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_UP)                 PORT_CODE(KEYCODE_UP)     PORT_CHAR(UCHAR_MAMEKEY(UP))

	PORT_START("kbrow.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_8)      PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_7)      PORT_CHAR('7') PORT_CHAR('/')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_Z)      PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_H)      PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_G)      PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_V)      PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G.S.END")               PORT_CODE(KEYCODE_END)    PORT_CHAR(UCHAR_MAMEKEY(END))

	PORT_START("kbrow.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_Q)          PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_A)          PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_S)          PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_CAPSLOCK)   PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))

	PORT_START("kbrow.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_J)          PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_B)          PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_N)          PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

	PORT_START("kbrow.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_MINUS)      PORT_CHAR(U'ß') PORT_CHAR('?')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_0)          PORT_CHAR('0')  PORT_CHAR('=')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_P)          PORT_CHAR('p')  PORT_CHAR('P')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_O)          PORT_CHAR('o')  PORT_CHAR('O')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_M)          PORT_CHAR('m')  PORT_CHAR('M')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')  PORT_CHAR(';')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_MENU)       PORT_CHAR(UCHAR_MAMEKEY(MENU))

	PORT_START("kbrow.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Inhalt")                PORT_CODE(KEYCODE_HOME)       PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_COLON)      PORT_CHAR(U'ö') PORT_CHAR(U'Ö')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('+')  PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR(U'ü') PORT_CHAR(U'Ü')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kbrow.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SM/Layout")             PORT_CODE(KEYCODE_PRTSCR)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("STORNO")                PORT_CODE(KEYCODE_PAUSE)      PORT_CHAR(UCHAR_MAMEKEY(CANCEL))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(UCHAR_MAMEKEY(ENTER))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(UCHAR_MAMEKEY(BACKSPACE))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    //PORT_CODE(KEYCODE_)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Horz/Vert")             //PORT_CODE(KEYCODE_)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(UCHAR_MAMEKEY(SPACE))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_LSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("kbrow.8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(U'´')  PORT_CHAR(U'`')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_L)          PORT_CHAR('l')   PORT_CHAR('L')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('\'')  PORT_CHAR(U'°')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_K)          PORT_CHAR('k')   PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')   PORT_CHAR(':')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('-')   PORT_CHAR('_')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(U'ä')  PORT_CHAR(U'Ä')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

TIMER_DEVICE_CALLBACK_MEMBER(lw350_state::int1_timer_callback)
{
	maincpu->set_input_line(INPUT_LINE_IRQ1, ASSERT_LINE);
}

//uint8_t char_attribute = 0x00;

void lw350_state::machine_start()
{
	auto rom = memregion("maincpu")->base();
	rombank->configure_entries(0, 4, rom, 0x20000);
	screen->set_visible_area(0, 480 - 1, 0, 128 - 1);

	fdc->set_floppy(floppy->get_device());

	// ROM patches

	// force jump to self-test menu
//  rom[0x280f2] = 0x20;
//  rom[0x280f2+1] = 0x05;

	// force jump to lcd-test menu
//  rom[0x280f2] = 0x2c;
//  rom[0x280f2+1] = 0x05;

	// force jump to self-print menu
//  rom[0x280f2] = 0x32;
//  rom[0x280f2 + 1] = 0x05;

	// set initial mode
	//rom[0x29a12] = 0x0a;

	// patch out printer init
	rom[0x280df] = 0x00;

	// force RAM DOWN
	//rom[0x280c3] = rom[0x280be];
	//rom[0x280c3 + 1] = rom[0x280be + 1];

	// patch out self test bit 5 check; causes reboot loop
	//rom[0x280c0] = 0x00;
	//rom[0x280c1] = 0x00;
	//rom[0x280c2] = 0x00;

	// force bold font
	//rom[0x6b29] = 0x00;
	//rom[0x6b29+1] = 0x00;

	// char attributes
	//rom[0x6b47] = 0x3e;
	//rom[0x6b48] = char_attribute;
	//rom[0x6b49] = 0x00;
	//rom[0x6b4a] = 0x00;
	//rom[0x6b4b] = 0x00;
	//rom[0x6b4c] = 0x00;
}

void lw350_state::machine_reset()
{
	io_90 = 0x00;
	fdc->reset();
	fdc_drq = 0;
}

static void lw350_floppies(device_slot_interface& device) {
	device.option_add("35dd", FLOPPY_35_DD);
	device.option_add("35hd", FLOPPY_35_HD);
}

void lw350_state::lw350(machine_config &config) {
	// basic machine hardware
	HD64180RP(config, maincpu, 16'000'000 / 2);
	maincpu->set_addrmap(AS_PROGRAM, &lw350_state::map_program);
	maincpu->set_addrmap(AS_IO, &lw350_state::map_io);
	maincpu->tend0_wr_callback().set([this] (int state) { fdc->tc_w((fdc_drq && state) ? 1 : 0); });
	maincpu->rts0_wr_callback().set(fdc, FUNC(hd63266f_device::rate_w));
	TIMER(config, "1khz").configure_periodic(FUNC(lw350_state::int1_timer_callback), attotime::from_hz(1000));

	// video hardware
	SCREEN(config, screen, SCREEN_TYPE_RASTER);
	screen->set_color(rgb_t(6, 245, 206));
	screen->set_physical_aspect(480, 128);
	screen->set_refresh_hz(78.1);
	screen->set_screen_update(FUNC(lw350_state::screen_update));
	screen->set_size(480, 128);

	HD63266F(config, fdc, XTAL(16'000'000));
	fdc->drq_wr_callback().set([this] (int state) { fdc_drq = state; maincpu->set_input_line(Z180_INPUT_LINE_DREQ0, state); });
	fdc->set_ready_line_connected(false);
	fdc->set_select_lines_connected(false);
	fdc->inp_rd_callback().set([this] () { return floppy->get_device()->dskchg_r(); });

	FLOPPY_CONNECTOR(config, floppy, lw350_floppies, "35hd", floppy_image_device::default_pc_floppy_formats);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, "beeper", 4'000).add_route(ALL_OUTPUTS, "mono", 1.0); // 4.0 kHz
}

//////////////////////////////////////////////////////////////////////////
// LW-450
//////////////////////////////////////////////////////////////////////////

/***************************************************************************

Brother LW-450
1992

Hardware:

#13
Hitachi
HD64180RF6X
8-bit CMOS Micro Processing Unit
fully compatible with Zilog Z80180 (Z180)
6 MHz, FP-80B, Address Space 1 M Byte
MuRata CST12MTW 12.00 MHz Ceramic Resonator

#12
Mitsubishi
M65020FP
UA7777-A

#15
Hitachi
HD74LS368AP

#10, #11
Hitachi
HD74LS157P

#7, #8
NEC
D41464C-10
65,536 x 4-Bit
Dynamic NMOS RAM
100 ns
18-pin plastic DIP

#1
NEC
D23C4001EC-172
UA2849-A
4MBit Mask ROM

#2
AMD
AM27C020
2BC04
2MBit (256K x 8-Bit) CMOS EPROM

#3
Hitachi
HM65256BSP-10
32,768 word x 8-bit High Speed Pseudo Static RAM
100 ns
DP-28N

#4
Hitachi HD63266F
CMOS Floppy Disk Controller
QFP-64
Murata Ceralock CST-MXW 16.00 MHz Ceramic Resonator

#5
Hitachi
HD6445P4
CRTC-II (CRT Controller)
DP-40
80 system Bus Interface
4.0 MHz Bus Timing

#9
Mitsubishi
M65133FP
UA7550-A

#14
Hitachi
HM62256AF-8
32,768 word x 8-bit High Speed CMOS Static RAM
85 ns
FP-28

720kb Floppy Drive

D-SUB9 CRT Connector
HSYNC 18.5 kHz
VSYNC 50 Hz
PC MDA standard
  DB9 connector 1+2 GND, 3+4+5 nc, 6 Intensity, 7 Brightness, 8 HSync, 9 VSync
  HSync positive, VSync negative active

see https://github.com/BartmanAbyss/brother-hardware/tree/master/1G%20-%20Brother%20LW-450 for datasheets, photos

Emulation Status:
Printer not working
Floppy Read has some problems (directory working, but LW-450 reports illegal file format when trying to load a .wpt file (content verified with LW-350), but writing seems fine)
Dictionary ROM probably not correctly mapped

***************************************************************************/

constexpr int MDA_CLOCK = 16'257'000;

class lw450_state : public driver_device
{
public:
	lw450_state(const machine_config &mconfig, device_type type, const char* tag) :
		driver_device(mconfig, type, tag),
		maincpu(*this, "maincpu"),
		screen(*this, "screen"),
		palette(*this, "palette"),
		floppy(*this, "fdc:0"),
		fdc(*this, "fdc"),
		m_crtc(*this, "hd6445"),
		vram(*this, "vram"),
		speaker(*this, "beeper"),
		io_kbrow(*this, "kbrow.%u", 0),
		rom(*this, "maincpu"),
		rombank(*this, "dictionary")
	{ }

	void lw450(machine_config &config) ATTR_COLD;

protected:
	// driver_device overrides
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;
	void video_start() override ATTR_COLD;

private:
	// devices
	required_device<z80180_device> maincpu; // use z80180 instead of hd64180rp, because hd64180rf doesn't have hd64180rp's 19-bit address width
	required_device<screen_device> screen;
	required_device<palette_device> palette;
	required_device<floppy_connector> floppy;
	required_device<hd63266f_device> fdc;
	required_device<hd6345_device> m_crtc;
	required_shared_ptr<uint8_t> vram;
	required_device<beep_device> speaker;
	required_ioport_array<9> io_kbrow;
	required_region_ptr<uint8_t> rom;
	required_memory_bank rombank;

	uint8_t io_72, io_73, io_74, io_75; // gfx
	uint8_t io_b8;
	uint32_t framecnt;

	uint8_t illegal_r(offs_t offset) {
		if(!machine().side_effects_disabled())
			logerror("%s: unmapped memory read from %0*X\n", machine().describe_context(), 6, offset);
		return 0;
	}
	void illegal_w(offs_t offset, uint8_t data) {
		if(!machine().side_effects_disabled())
			logerror("%s: unmapped memory write to %0*X = %0*X\n", machine().describe_context(), 6, offset, 2, data);
	}

	uint8_t rom72000_r(offs_t offset) {
		return rom[0x02000 + offset];
	}

	// IO
	uint8_t io_b0_r() { return ~0x00; }
	uint8_t io_b8_r() {
		// keyboard matrix
		if(io_b8 <= 8)
			return io_kbrow[io_b8]->read();
		return 0x00;
	}
	void io_b8_w(uint8_t data) {
		io_b8 = data;
	}
	void rombank_w(uint8_t data) { // E0
		if(data >= 4 && data < 8)
			rombank->set_entry(data - 4);
	}
	void beeper_w(uint8_t data) { // F0
		speaker->set_state(~data & 0x01);
	}
	void irqack_w(uint8_t) { // F8
		maincpu->set_input_line(INPUT_LINE_IRQ1, CLEAR_LINE);
	}

	// CRTC
	MC6845_UPDATE_ROW(crtc_update_row);
	void crtc_vsync(int state);
	void io_72_w(uint8_t data) { io_72 = data; }
	void io_73_w(uint8_t data) { io_73 = data; }
	void io_74_w(uint8_t data);
	void io_75_w(uint8_t data) { io_75 = data; }

	TIMER_DEVICE_CALLBACK_MEMBER(int1_timer_callback);

	void map_program(address_map& map) ATTR_COLD;
	void map_io(address_map& map) ATTR_COLD;
};

void lw450_state::video_start()
{
}

void lw450_state::map_program(address_map &map)
{
	map(0x00000, 0x01fff).rom();
	map(0x02000, 0x05fff).ram();
	map(0x06000, 0x3ffff).rom();
	map(0x40000, 0x5ffff).bankr(rombank);
	map(0x62000, 0x71fff).ram(); // D-RAM UPPER/LOWER
	map(0x72000, 0x75fff).r(FUNC(lw450_state::rom72000_r)); // => ROM 0x02000-0x05fff
	map(0x78000, 0x7ffff).ram(); // PS-RAM
	map(0xf8000, 0xfffff).ram().share(vram); // VRAM
	// text vram @ F8000-F8C80 (2*80 bytes/line)
	// font @ FC000-FD000 pitch 16
}

void lw450_state::map_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x3f).noprw(); // Z180 internal registers

	map(0x70, 0x70).w("hd6445", FUNC(hd6345_device::address_w));
	map(0x71, 0x71).w("hd6445", FUNC(hd6345_device::register_w));
	map(0x72, 0x72).w(FUNC(lw450_state::io_72_w));
	map(0x73, 0x73).w(FUNC(lw450_state::io_73_w));
	map(0x74, 0x74).w(FUNC(lw450_state::io_74_w));
	map(0x75, 0x75).w(FUNC(lw450_state::io_75_w));

	// floppy
	map(0x78, 0x7a).m(fdc, FUNC(hd63266f_device::map));

	map(0xb0, 0xb0).r(FUNC(lw450_state::io_b0_r));
	map(0xb8, 0xb8).rw(FUNC(lw450_state::io_b8_r), FUNC(lw450_state::io_b8_w));
	map(0xe0, 0xe0).w(FUNC(lw450_state::rombank_w));
	map(0xf0, 0xf0).w(FUNC(lw450_state::beeper_w));
	map(0xf8, 0xf8).w(FUNC(lw450_state::irqack_w));

	//map(0x40, 0xff) AM_READWRITE(illegal_io_r, illegal_io_w)
}

// CRTC
//////////////////////////////////////////////////////////////////////////

TIMER_DEVICE_CALLBACK_MEMBER(lw450_state::int1_timer_callback)
{
	maincpu->set_input_line(INPUT_LINE_IRQ1, ASSERT_LINE);
}

void lw450_state::crtc_vsync(int state)
{
	if(state) {
		framecnt++;
	}
}

void lw450_state::io_74_w(uint8_t data)
{
	io_74 = data;
	if(io_74 & 0x04) {
		// graphics mode
		m_crtc->set_char_width(8);
		m_crtc->set_clock(MDA_CLOCK / 8);
	} else {
		// text mode
		m_crtc->set_char_width(9);
		m_crtc->set_clock(MDA_CLOCK / 9);
	}
}

//(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint16_t ma, uint8_t ra, uint16_t y, uint8_t x_count, int8_t cursor_x, int de, int hbp, int vbp)
MC6845_UPDATE_ROW(lw450_state::crtc_update_row)
{
	// IO             72 73 74 75
	// ============================
	// typewriter:    05 02 08 f8 text     inverted
	// crt test menu: 07 02 00 f8 text
	// main menu:     05 02 0c f8 graphics inverted

	// based on LW-450 CRT Test Menu
	enum attrs {
		underline           = 0b00000001,
		extended_charset    = 0b00000100,
		bold                = 0b00001000,
		reverse             = 0b00010000,
		blink               = 0b10000000,
	};

	rgb_t const *const palette = this->palette->palette()->entry_list_raw();
	uint32_t* p = &bitmap.pix(y);
	uint16_t chr_base = ra;

	// video off
	if(!(io_72 & 0b1)) {
		for(int x = 0; x < cliprect.width(); x++)
			*p++ = palette[0];
		return;
	}

	// graphics mode
	if(io_74 & 0x04) {
		uint8_t bg = 0, fg = 1;
		// inverse display
		if(!(io_72 & 0x02))
			std::swap(bg, fg);

		for(int x = 0; x < x_count; x++) {
			auto data = vram[(ma + x + (ra << 14)) & 0x7fff];
			*p++ = palette[(data & 0x80) ? fg : bg];
			*p++ = palette[(data & 0x40) ? fg : bg];
			*p++ = palette[(data & 0x20) ? fg : bg];
			*p++ = palette[(data & 0x10) ? fg : bg];
			*p++ = palette[(data & 0x08) ? fg : bg];
			*p++ = palette[(data & 0x04) ? fg : bg];
			*p++ = palette[(data & 0x02) ? fg : bg];
			*p++ = palette[(data & 0x01) ? fg : bg];
		}
	} else {
		// text mode
		auto charram = &vram[0];
		auto fontram = &vram[0x4000];

		for(int x = 0; x < x_count; x++) {
			uint16_t offset = ((ma + x) << 1) & 0x0FFF;
			auto atr = charram[offset];
			uint32_t chr = charram[offset + 1];
			if(atr & extended_charset) chr += 0x100;
			auto data = fontram[chr_base + chr * 16];
			uint8_t bit9 = 0x00;
			if(atr & extended_charset) bit9 = BIT(data, 0) ? 0xff : 0x00;
			uint8_t bg = 0, fg = 1;

			if(atr & bold)
				fg = 2;

			// inverse display
			if(!(io_72 & 0x02))
				std::swap(bg, fg);

			if((atr & underline) && ra == 13) {
				data = bit9 = 0xff;
			}
			if(atr & reverse)
				std::swap(bg, fg);

			if(x == cursor_x) {
				data = 0xff;
				bit9 = 0x00;
			} else {
				if((atr & blink) && (framecnt & 0x10)) // TODO: check blinking frequency
					data = 0x00;
			}

			for(int b = 0; b < 8; b++)
				*p++ = BIT(data, 7 - b) ? palette[fg] : palette[bg];
			*p++ = BIT(bit9, 7) ? palette[fg] : palette[bg];
		}
	}
}

// PIN 21 (Character Clock) of CRTC-II: menu: 2.0 MHz; schreibmaschine: 1.8 MHz

// these timings are all at MDA clock
// bootup:          [:hd6445] M6845 config screen: HTOTAL: 882  VTOTAL: 370  MAX_X: 719  MAX_Y: 319  HSYNC: 729-863  VSYNC: 320-335  Freq: 49.816133fps
// menu:            [:hd6445] M6845 config screen: HTOTAL: 990  VTOTAL: 370  MAX_X: 809  MAX_Y: 319  HSYNC: 819-953  VSYNC: 334-349  Freq: 44.381646fps
// schreibmaschine: [:hd6445] M6845 config screen: HTOTAL: 882  VTOTAL: 370  MAX_X: 719  MAX_Y: 319  HSYNC: 729-863  VSYNC: 320-335  Freq: 49.816133fps

void lw450_state::machine_start()
{
	rombank->configure_entries(0, 4, memregion("dictionary")->base(), 0x20000);

	palette->set_pen_color(0, rgb_t(0, 0, 0));
	palette->set_pen_color(1, rgb_t(0xaa, 0xaa, 0xaa));
	palette->set_pen_color(2, rgb_t(0xff, 0xff, 0xff));

	// patch out printer init
	rom[0x280db] = 0x00;
}

void lw450_state::machine_reset()
{
	framecnt = 0;
}

static const gfx_layout pc_16_charlayout {
	8, 16,                  // 8 x 16 characters
	256,                    // 256 characters
	1,                      // 1 bits per pixel
	{ 0 },                  // no bitplanes
	{ 0, 1, 2, 3, 4, 5, 6, 7 }, // x offsets
	{ 0 * 8, 1 * 8, 2 * 8, 3 * 8, 4 * 8, 5 * 8, 6 * 8, 7 * 8, 8 * 8, 9 * 8, 10 * 8, 11 * 8, 12 * 8, 13 * 8, 14 * 8, 15 * 8 },   // y offsets
	16*8                 // every char takes 2 x 8 bytes
};

static GFXDECODE_START( gfx_lw450 )
	GFXDECODE_RAM("vram", 0x4000, pc_16_charlayout, 0, 1)
GFXDECODE_END

void lw450_state::lw450(machine_config &config) {
	// basic machine hardware
	Z80180(config, maincpu, 12'000'000 / 2);
	maincpu->set_addrmap(AS_PROGRAM, &lw450_state::map_program);
	maincpu->set_addrmap(AS_IO, &lw450_state::map_io);
	maincpu->tend0_wr_callback().set(fdc, FUNC(hd63266f_device::tc_line_w));
	TIMER(config, "1khz").configure_periodic(FUNC(lw450_state::int1_timer_callback), attotime::from_hz(1000));

	// video hardware
	SCREEN(config, screen, SCREEN_TYPE_RASTER);
	screen->set_color(rgb_t::white());
	screen->set_physical_aspect(720, 320);
	screen->set_screen_update("hd6445", FUNC(hd6345_device::screen_update));
	screen->set_raw(MDA_CLOCK, 882, 0, 729, 370, 0, 320); // based on bootup crtc values

	GFXDECODE(config, "gfxdecode", palette, gfx_lw450);
	PALETTE(config, palette).set_entries(4);

	// CRTC
	HD6345(config, m_crtc, MDA_CLOCK / 9);
	m_crtc->set_screen(screen);
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(9);
	m_crtc->set_update_row_callback(FUNC(lw450_state::crtc_update_row));
	m_crtc->out_vsync_callback().set(FUNC(lw450_state::crtc_vsync));

	HD63266F(config, fdc, XTAL(16'000'000));
	fdc->drq_wr_callback().set_inputline(maincpu, Z180_INPUT_LINE_DREQ0);
	fdc->set_ready_line_connected(false);
	fdc->set_select_lines_connected(false);

	FLOPPY_CONNECTOR(config, floppy, lw350_floppies, "35hd", floppy_image_device::default_pc_floppy_formats);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, "beeper", 4'000).add_route(ALL_OUTPUTS, "mono", 1.0); // 4.0 kHz
}

/***************************************************************************
  Machine driver(s)
***************************************************************************/

ROM_START( lw350 )
	ROM_REGION( 0x80000, "maincpu", 0 )
	ROM_LOAD("uc6273-a-lwb6", 0x00000, 0x80000, CRC(5e85d1ec) SHA1(4ca68186fc70f30ccac95429604c88db4f0c34d2))
//  ROM_LOAD("patched", 0x00000, 0x80000, CRC(5e85d1ec) SHA1(4ca68186fc70f30ccac95429604c88db4f0c34d2))
ROM_END

ROM_START( lw450 )
	ROM_REGION( 0x40000, "maincpu", 0 )
	ROM_LOAD("2bc04", 0x00000, 0x40000, CRC(96c2a6f1) SHA1(eb47e37ea46e3becc1b4453286f120682a0a1ddc))
	ROM_REGION(0x80000, "dictionary", 0)
	ROM_LOAD("ua2849-a", 0x00000, 0x80000, CRC(fa8712eb) SHA1(2d3454138c79e75604b30229c05ed8fb8e7d15fe))
ROM_END

} // anonymous namespace

//    YEAR  NAME  PARENT COMPAT   MACHINE INPUT  CLASS           INIT              COMPANY         FULLNAME  FLAGS
COMP( 1995, lw350,  0,   0,       lw350,  lw350, lw350_state,    empty_init,       "Brother",      "LW-350", MACHINE_NODEVICE_PRINTER )
COMP( 1992, lw450,  0,   0,       lw450,  lw350, lw450_state,    empty_init,       "Brother",      "LW-450", MACHINE_NODEVICE_PRINTER )



lw700i.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    Brother LW-700i and friends word processor/typewriters

    Preliminary driver by R. Belmont

    Main CPU: Hitachi H8/3003
    FDC: HD63266F (uPD765 derivative)
    256KiB RAM
    Dot-matrix LCD (480x128)

****************************************************************************/

#include "emu.h"

#include "cpu/h8/h83003.h"
#include "imagedev/floppy.h"
#include "machine/at28c16.h"
#include "machine/nvram.h"
#include "machine/timer.h"
#include "machine/upd765.h"

#include "screen.h"
#include "speaker.h"

#include "utf8.h"


namespace {

class lw700i_state : public driver_device
{
public:
	lw700i_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mainram(*this, "mainram")
		, m_screen(*this, "screen")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:1")
		, m_keyboard(*this, "X%u", 0)
	{ }

	void lw700i(machine_config &config) ATTR_COLD;

protected:
	// driver_device implementation
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

private:
	uint8_t p7_r();
	uint8_t pb_r();
	void pb_w(uint8_t data);

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	TIMER_DEVICE_CALLBACK_MEMBER(vbl_interrupt);

	void main_map(address_map &map) ATTR_COLD;

	// devices
	required_device<h83003_device> m_maincpu;
	required_shared_ptr<uint16_t> m_mainram;
	required_device<screen_device> m_screen;
	required_device<hd63266f_device> m_fdc;
	required_device<floppy_connector> m_floppy;
	required_ioport_array<9> m_keyboard;

	uint8_t m_keyrow = 0;
};

uint8_t lw700i_state::p7_r()
{
	//("Read P7 (PC=%x)\n", m_maincpu->pc());
	// must be non-zero; f0 = French, fe = German, ff = English
	if (m_keyrow == 0xf)
	{
		return 0xff;
	}

	if (m_keyrow < 9)
	{
		return m_keyboard[m_keyrow]->read();
	}

	return 0xff;
}

uint8_t lw700i_state::pb_r()
{
	return 0;
}

void lw700i_state::pb_w(uint8_t data)
{
	//printf("%x to keyboard row\n", data);
	m_keyrow = data;
}

TIMER_DEVICE_CALLBACK_MEMBER(lw700i_state::vbl_interrupt)
{
	int scanline = m_screen->vpos();

	if (scanline == 1)
	{
		m_maincpu->set_input_line(5, ASSERT_LINE);
		// not sure where this is coming from, fix hang
		m_maincpu->space(0).write_byte(0xffffff05, 0);
	}
	else if (scanline == 2)
	{
		m_maincpu->set_input_line(5, CLEAR_LINE);
	}
}

void lw700i_state::machine_reset()
{
	m_fdc->rate_w(1);  // machine supports 250k and 500k rate, this must be controlled somewhere
}

void lw700i_state::machine_start()
{
}

void lw700i_state::video_start()
{
}

uint32_t lw700i_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	static const uint32_t palette[2] = { 0xffffff, 0 };
	uint8_t const *const pVRAM = (uint8_t *)m_mainram.target() + 0x3e200;

	for (int y = 0; y < 128; y++)
	{
		uint32_t *scanline = &bitmap.pix(y);
		for (int x = 0; x < 480/8; x++)
		{
			uint8_t const pixels = pVRAM[(y * (480/8)) + (BYTE_XOR_BE(x))];

			*scanline++ = palette[BIT(pixels, 7)];
			*scanline++ = palette[BIT(pixels, 6)];
			*scanline++ = palette[BIT(pixels, 5)];
			*scanline++ = palette[BIT(pixels, 4)];
			*scanline++ = palette[BIT(pixels, 3)];
			*scanline++ = palette[BIT(pixels, 2)];
			*scanline++ = palette[BIT(pixels, 1)];
			*scanline++ = palette[BIT(pixels, 0)];
		}
	}

	return 0;
}

void lw700i_state::main_map(address_map &map)
{
	map(0x000000, 0x1fffff).rom().region("maincpu", 0x0000);
	map(0x600000, 0x63ffff).ram().share("mainram"); // 256K of main RAM
	map(0xe00000, 0xe00003).m(m_fdc, FUNC(hd63266f_device::map));
	map(0xf00048, 0xf00049).ram();
}

// row 0:   | 4 | 3 | W | E | D | X | ? | Enter? |
// row 1:   | 5 | 6 | R | T | C | F | ? | DArr |
// row 2:   | 8 | 7 | Y | H | G | V | ? | ? |
// row 3:   | 1 | 2 | Q | Z | A | S | ? | Shift Lock? |
// row 4:   | 9 | J | I | U | B | N | ? | RArr |
// row 5:   | - | 0 | P | O | M | , | ? | Menu |
// row 6:   | ? | ; |2/3| | |LAr|UAr| ? | ? |
// row 7:   | ? | ? |ENT|BkS| ? | ? | ? | Shift? |
// row 8:   |3/4| L | = | K | . |1/2| * | ? |

static INPUT_PORTS_START( lw700i )
	PORT_START("X0")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)  PORT_CHAR('4') PORT_CHAR('@')
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)  PORT_CHAR('3') PORT_CHAR('/')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)  PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)  PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)  PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)  PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("X1")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)  PORT_CHAR('5') PORT_CHAR(0xa3)
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)  PORT_CHAR('6') PORT_CHAR('_')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)  PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)  PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)  PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)  PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN)      PORT_CODE(KEYCODE_DOWN)     PORT_CHAR(UCHAR_MAMEKEY(DOWN))

	PORT_START("X2")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)  PORT_CHAR('8') PORT_CHAR('\'')
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)  PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)  PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)  PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)  PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)  PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("X3")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)  PORT_CHAR('1') PORT_CHAR(0x2021)
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)  PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)  PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)  PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)  PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)  PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK)    PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))

	PORT_START("X4")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)  PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)  PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)  PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)  PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)  PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)  PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT)     PORT_CODE(KEYCODE_RIGHT)    PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

	PORT_START("X5")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)  PORT_CHAR('-') PORT_CHAR('?')
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)      PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)  PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)  PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)  PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)  PORT_CHAR(',')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Menu")      PORT_CODE(KEYCODE_ESC)      PORT_CHAR(27)

	PORT_START("X6")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(0x2154) PORT_CHAR(0x2153)
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('|') PORT_CHAR('$')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT)      PORT_CODE(KEYCODE_LEFT)     PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_UP)        PORT_CODE(KEYCODE_UP)       PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("X7")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Print")
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return")       PORT_CODE(KEYCODE_ENTER)    PORT_CHAR(13)
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(8)
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Clear")
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left Shift")   PORT_CODE(KEYCODE_LSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("X8")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR(0xbe) PORT_CHAR(0xbc)
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)  PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)  PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR(0xbd) PORT_CHAR('%')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_UNKNOWN)

INPUT_PORTS_END

static void lw700i_floppies(device_slot_interface &device)
{
	device.option_add("35hd", FLOPPY_35_HD);
}

void lw700i_state::lw700i(machine_config &config)
{
	H83003(config, m_maincpu, XTAL(16'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &lw700i_state::main_map);
	m_maincpu->read_port7().set(FUNC(lw700i_state::p7_r));
	m_maincpu->read_portb().set(FUNC(lw700i_state::pb_r));
	m_maincpu->write_portb().set(FUNC(lw700i_state::pb_w));

	m_maincpu->tend2().set(m_fdc, FUNC(hd63266f_device::tc_line_w));
	TIMER(config, "scantimer").configure_scanline(FUNC(lw700i_state::vbl_interrupt), "screen", 0, 1);

	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500) /* not accurate */);
	m_screen->set_screen_update(FUNC(lw700i_state::screen_update));
	m_screen->set_size(640, 400);
	m_screen->set_visarea(0, 480, 0, 128);

	HD63266F(config, m_fdc, XTAL(16'000'000));
	m_fdc->intrq_wr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ4);
	m_fdc->drq_wr_callback().set_inputline(m_maincpu, H8_INPUT_LINE_DREQ2); // dreq2 is not connected in the hd83003
	m_fdc->inp_rd_callback().set([this](){ return m_floppy->get_device()->dskchg_r(); });
	FLOPPY_CONNECTOR(config, m_floppy, lw700i_floppies, "35hd", floppy_image_device::default_pc_floppy_formats);
}

ROM_START(lw700i)
	ROM_REGION(0x200000, "maincpu", 0)      /* H8/3003 program ROM */
	ROM_LOAD16_WORD_SWAP( "mx24969b.bin", 0x000000, 0x200000, CRC(78d88d04) SHA1(3cda632c7190257abd20e121575767e8e9a18b1c) )
ROM_END

} // anonymous namespace


COMP( 1995, lw700i,    0, 0, lw700i, lw700i, lw700i_state, empty_init, "Brother", "LW-700i", MACHINE_NOT_WORKING|MACHINE_NO_SOUND )



lw840.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Bartman/Abyss

#include "emu.h"

#include "cpu/h8/h83003.h"
#include "imagedev/floppy.h"
#include "machine/timer.h"
#include "machine/upd765.h"
#include "sound/beep.h"

#include "debug/debugcpu.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "formats/pc_dsk.h"

#include "util/endianness.h"
#include "util/utf8.h"


// command line parameters:
// -window -debug -log lw840 -flop "c:\schreibmaschine\Brother LW-840ic\LW840-ic_V1.0_Disk1_Graphic_Art_Print_Program, etc..img"

// trace:
// trace msg,0,true,{tracelog "ER0=%08X,ER1=%08X,ER2=%08X,ER3=%08X,ER4=%08X,ER5=%08X,ER6=%08X,ER7=%08X ",er0,er1,er2,er3,er4,er5,er6,er7}

/***************************************************************************

Brother LW-840ic
1997

Main-PCB B48J300

Hardware:

#1
Analog Devices
ADM211EARS
EMI/EMC Compliant, +/-15 kV ESD Protected, RS-232 Line Drivers/Receivers

#5
LG/Goldstar/SK Hynix
GM82C765B
Floppy Disk Subsystem Controller
16 MHz (XT2), 18 MHz (XT1)
clone of WD37C65

#6
NKK
N341256SJ
CMOS SRAM (32k x 8)

#8
Brother/Hitachi
UC9201-000
HG71C207FD
MPU
208 pins
H8/3003-alike; can't be H8/3002, because 'Port C' is used in ROM
H8/300H Advanced Mode
14.74 MHz CPU Core (XT4), 20 MHz Printer Control (XT3)

#10
Nippon Steel Semiconductor
NN514260J-60
Fast Page Mode CMOS 256k x 16bit Dynamic RAM

#11
Macronix
US3122-A
64316JW1
5 Volt 32-Mbit (4M x 8 / 2M x 16) Mask ROM with Page Mode

see https://github.com/BartmanAbyss/brother-hardware/tree/master/4G%20-%20Brother%20LW-840ic for datasheets, manuals, photos

Emulation Status:
- Keyboard not 100%
- Screen Refresh Rate not yet measured
- Printer not working

***************************************************************************/

class gm82c765b_device : public upd765_family_device
{
public:
	gm82c765b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	virtual void map(address_map& map) override ATTR_COLD
	{
		map(0x0, 0x0).r(FUNC(gm82c765b_device::msr_r));
		map(0x1, 0x1).rw(FUNC(gm82c765b_device::fifo_r), FUNC(gm82c765b_device::fifo_w));
		map(0x2, 0x2).w(FUNC(gm82c765b_device::dor_w));
		map(0x6, 0x6).rw(FUNC(gm82c765b_device::dma_r), FUNC(gm82c765b_device::dma_w));
	}
};

DEFINE_DEVICE_TYPE(GM82C765B, gm82c765b_device, "gm82c765b", "GoldStar GM82C765B FDC") // also sold with Hynix branding

gm82c765b_device::gm82c765b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	upd765_family_device(mconfig, GM82C765B, tag, owner, clock)
{
	ready_polled = true;
	ready_connected = false;
	select_connected = true;
}

namespace {

class lw840_state : public driver_device
{
public:
	lw840_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		maincpu(*this, "maincpu"),
		screen(*this, "screen"),
		fdc(*this, "fdc"),
		beeper(*this, "beeper"),
		io_kbrow(*this, "kbrow.%u", 0),
		rom(*this, "maincpu"),
		sram(*this, "sram")
	{ }

	void lw840(machine_config &config) ATTR_COLD;

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices
	required_device<h83003_device> maincpu;
	required_device<screen_device> screen;
	required_device<gm82c765b_device> fdc;
	required_device<beep_device> beeper;
	required_ioport_array<9> io_kbrow;
	required_region_ptr<uint16_t> rom;
	required_shared_ptr<uint16_t> sram;

	uint8_t keyboard{};
	uint8_t irq_toggle{};

	// screen updates
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	uint8_t port7_r()
	{
		auto row = keyboard & 0x0f;
		if(row <= 8)
			return io_kbrow[row]->read();

		// seems to be able to control power-on self test if not 0xff
		return 0xff;
	}
	uint16_t keyboard_r()
	{
		return keyboard << 8;
	}
	void keyboard_w(uint16_t data)
	{
		keyboard = data >> 8;
	}
	TIMER_DEVICE_CALLBACK_MEMBER(int2_timer_callback)
	{
		irq_toggle = ~irq_toggle;
		maincpu->set_input_line(INPUT_LINE_IRQ2, irq_toggle ? ASSERT_LINE : CLEAR_LINE);
	}

	static void floppy_formats(format_registration &fr) ATTR_COLD
	{
		fr.add(FLOPPY_PC_FORMAT);
	}

	void fdc_interrupt(int state)
	{
		maincpu->set_input_line(INPUT_LINE_IRQ4, state ? ASSERT_LINE : CLEAR_LINE);
	}
	void fdc_drq(int state)
	{
		maincpu->set_input_line(H8_INPUT_LINE_DREQ0, state ? ASSERT_LINE : CLEAR_LINE);
	}
	uint16_t disk_inserted_r()
	{
		// bit#6: disk inserted
		// bit#7: ??1.44mb
		// bit#1: ???
		// bit#0: ???

		// FIXME: hack: always disk inserted, HD-disk
		return 0b01000000 << 8;
	}

	void map_program(address_map &map) ATTR_COLD
	{
		map(0x000000, 0x3fffff).rom();
		map(0x5f8000, 0x5fffff).ram().share(sram); // SRAM
		map(0x600000, 0x67ffff).ram(); // DRAM
		map(0xe00000, 0xe00007).m(fdc, FUNC(gm82c765b_device::map));
		map(0xe00030, 0xe00041).noprw(); // just to shut up the error.log
		map(0xec0000, 0xec0001).r(FUNC(lw840_state::keyboard_r)).w(FUNC(lw840_state::keyboard_w));
		map(0xec0004, 0xec0005).r(FUNC(lw840_state::disk_inserted_r));
	}
};

uint32_t lw840_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const rgb_t palette[]{
		0xffffffff,
		0xff000000,
	};

	uint16_t const *const vram = &sram[0x300 / sizeof(uint16_t)];

	for(auto y = std::max<int32_t>(cliprect.top(), 0); y <= std::min<int32_t>(cliprect.bottom(), 400 - 1); y++) {
		uint32_t *p = &bitmap.pix(y);
		for(auto x = 0; x < 640; x += 16) {
			auto gfx = vram[(y * 640 + x) / 16];
			for(int i = 15; i >= 0; i--)
				*p++ = palette[BIT(gfx, i)];
		}
	}
	return 0;
}

void lw840_state::machine_start()
{
	screen->set_visible_area(0, 640 - 1, 0, 400 - 1);
	fdc->set_rate(500'000);

	// patches here
	auto rom8 = util::big_endian_cast<uint8_t>(rom.target());
	//rom8[0x34a] = 0x47; // always branch to mem_test_error
	rom8[0x102] = rom8[0x102 + 1] = 0xff; // skip printer check
	//rom8[0xa380] = rom8[0xa380 + 1] = 0; // skip FDC init
}

void lw840_state::machine_reset()
{
}

static INPUT_PORTS_START(lw840)
	PORT_START("kbrow.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('/')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_W)          PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_E)          PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_D)          PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_X)          PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_TAB)        PORT_CHAR(UCHAR_MAMEKEY(TAB))

	PORT_START("kbrow.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_5)          PORT_CHAR('5') PORT_CHAR(U'£')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('_')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_R)          PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_T)          PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_C)          PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_F)          PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_UP)                 PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))

	PORT_START("kbrow.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('\'')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_Z)          PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_H)          PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_G)          PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_V)          PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("HELP/P BREAK")          PORT_CODE(KEYCODE_END)

	PORT_START("kbrow.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('*')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_Q)          PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_A)          PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_S)          PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_CAPSLOCK)   PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))

	PORT_START("kbrow.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_J)          PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_B)          PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_N)          PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

	PORT_START("kbrow.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-') PORT_CHAR('?')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_0)          PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_O)          PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_M)          PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR(';')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_MENU)       PORT_CHAR(UCHAR_MAMEKEY(MENU))

	PORT_START("kbrow.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("INSERT")                PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';')  PORT_CHAR(':')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(0x2154) PORT_CHAR(0x2153) // ⅔ ⅓
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR(U'|') PORT_CHAR('$')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("kbrow.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PRINT/LAYOUT")          PORT_CODE(KEYCODE_PRTSCR)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CANCEL")                PORT_CODE(KEYCODE_PAUSE)      PORT_CHAR(UCHAR_MAMEKEY(CANCEL))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(UCHAR_MAMEKEY(ENTER))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(UCHAR_MAMEKEY(BACKSPACE))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    //PORT_CODE(KEYCODE_)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Horz/Vert")             //PORT_CODE(KEYCODE_)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(UCHAR_MAMEKEY(SPACE))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_LSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("kbrow.8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(U'¼') PORT_CHAR(U'¾')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_L)          PORT_CHAR('l')  PORT_CHAR('L')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('+')  PORT_CHAR('=')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_K)          PORT_CHAR('k')  PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_SLASH)      PORT_CHAR(U'½') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                                    PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR(U'·') PORT_CHAR(U'÷')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

static void lw840_floppies(device_slot_interface &device)
{
	device.option_add("35hd", FLOPPY_35_HD);
}

void lw840_state::lw840(machine_config &config)
{
	// basic machine hardware
	H83003(config, maincpu, 14'745'600);
	maincpu->set_addrmap(AS_PROGRAM, &lw840_state::map_program);
	maincpu->read_port7().set(FUNC(lw840_state::port7_r));
	maincpu->tend0().set("fdc", FUNC(gm82c765b_device::tc_line_w));

	TIMER(config, "2khz").configure_periodic(FUNC(lw840_state::int2_timer_callback), attotime::from_hz(2*1000));

	// video hardware
	SCREEN(config, screen, SCREEN_TYPE_RASTER);
	screen->set_color(rgb_t::white());
	screen->set_physical_aspect(640, 400);
	screen->set_screen_update(FUNC(lw840_state::screen_update));
	screen->set_refresh_hz(78.1); // TODO: measure!
	screen->set_size(640, 400);

	// floppy
	GM82C765B(config, fdc, 0);
	fdc->intrq_wr_callback().set(FUNC(lw840_state::fdc_interrupt));
	fdc->drq_wr_callback().set(FUNC(lw840_state::fdc_drq));
	FLOPPY_CONNECTOR(config, "fdc:0", lw840_floppies, "35hd", lw840_state::floppy_formats);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, "beeper", 4'000).add_route(ALL_OUTPUTS, "mono", 1.0); // 4.0 kHz
}

ROM_START( lw840 )
	ROM_REGION(0x400000, "maincpu", 0)
	ROM_LOAD("us3122-a", 0x00000, 0x400000, CRC(70a3a4a6) SHA1(11e32c7da58800d69af29089f7e7deeab513b1ae))
ROM_END

} // anonymous namespace

//    YEAR  NAME  PARENT COMPAT   MACHINE INPUT  CLASS         INIT        COMPANY         FULLNAME    FLAGS
COMP( 1997, lw840,  0,   0,       lw840,  lw840, lw840_state,  empty_init, "Brother",      "LW-840ic", MACHINE_NODEVICE_PRINTER )



lwriter.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Joakim Larsson Edstrom, Jonathan Gevaryahu
/******************************************************************************

    Apple LaserWriter II NT driver

        0x000000 - 0x1fffff     SRAM/ROM (switches based on overlay)
        0x200000 - 0x3fffff     ROM
        0x400000 - 0x5fffff     RAM
        0x600000 - 0x7fffff     ??? more RAM?
        0x800000 - 0x9fffff     LED/Printer Controls(MSB), FIFO to print mechanism(LSB)
        0xa00000 - 0xbfffff     Zilog 8530 SCC (Serial Control Chip) Read
        0xc00000 - 0xdfffff     Zilog 8530 SCC (Serial Control Chip) Write
        0xe00000 - 0xefffff     Rockwell 6522 VIA
        0xf00000 - 0xffffef     ??? (the ROM appears to be accessing here)
        0xfffff0 - 0xffffff     ???Auto Vector??

    TODO:
    - Get the board to pass its self test, it fails long before it even bothers reading the dipswitches
    - Hook up the rest of the VIA pins to a canon printer HLE stub
    - Hook up ADB bitbang device to the VIA CB1, CB2 and PortA pins
    - Hook up VIA Port A, bits 5 and 6 to the SW1 and SW2 panel switches
    - Everything else
    DONE:
    - Hook up SCC and VIA interrupt pins to the 68k
    Future:
    - Let the board identify itself to a emulated mac driver so it displays the printer icon on the desktop

    Self Test LEDs at 0x800000-800001 most significant nybble:
    0x8 - <Passes> cpu check? (displayed before the overlay is disabled)
    0xF - <Passes> 200000-3fffff ROM checksum
    0xE - <Passes> 400000-400007 Low half of DRAM individual bit tests (walking ones and zeroes)
    0xD - <Passes> 5ffff8-5fffff High half of DRAM individual bit tests (walking ones and zeroes)
    0xC - <Passes> 400000-5fffff comprehensive DRAM data test
    0xB - <Passes?> Unknown test
    0xA - <Passes> dies if 600000-7fffff doesn't mirror 400000-5fffff ?
    0x8 - <Passes> SRAM test 000000-000FFF (2e3616-2e3664)
    0x0 - main loop? starts at 2e0002 (2e2fd0... 2db764 is the end of the 'clear ram 41d53f down to 400000' loop...),
    touches the via and fires an int, but runs off into the weeds
    If one of the self tests fails, the uppermost bit will oscillate (c000 4000 c000 4000 etc) forever

******************************************************************************/
/*
 * Hardware: 68000@11.16 MHz
             8530 SCC
             "6523" VIA (actually a custom-marked Rockwell R65NC22, not to be confused with the MOS 6523/65C23 TPI;
                         may also be sourced as VLSI VL65C22V-02PC)
             2MB DRAM
             2KB SRAM
             X2804 EEPROM (custom marked as 335-0022) [note that technically a 2808 or 2816 can go here and will work too]
             1MB ROM
             MMI67L401 64x4 FIFO, x2
             342-0440-A PIC (for ADB)

   +------------------------------------------------------------------------------------------------------------------------+=====+
   |      1           2            3           4            5          6          7          8        9        10     11    |     #
   |    +------+    +------+   +---------+                                                +-------+  +------+         +-+   |     #
   |A   |511000|    | F257 |   |335-0022 |                                                | 0296  |  |22.3210         | | J2|     #
   |    +------+    +------+   |EEPROM   |                     +-------+                  +-------+  |XTAL  |         | |   |     #
   |B   |511000|    | RP2B |   +---------+                     |       |                             +------+         | |   |     #
   |    +------+    +------+                                   | 68000 |                  +-------+  +-------+        +-+   |     #
   |C   |511000|    | F257 |                                   |       |                  | 0558  |  | F175  |        +-+   |     #
   |    +------+    +------+   +---------+                     |       |                  +-------+  +-------+        | |   |     #
   |D   |511000|    +------+   |Am9128-10|                     |       |        +-------+ +-------+  +-------+        | |   |     #
   |    +------+    | F257 |   |SRAM     |                     |       |        | 0559  | | 0557  |  | LS393 |        | |   |     #
   |E   |511000|    +------+   +---------+                     |       |        +-------+ +-------+  +-------+        +-+   |  F  #
   |    +------+    +------+   +---------+                     |       |                                      +------+      |     #
   |F   |511000|    | F138 |   |Am9128-10|                     |       |                +-----------------+   |26LS32|    J3|  R  #
   |    +------+    +------+   |SRAM     |                     |       |                | Z8530B1C        |   +------+      |     #
   |G   |511000|               +--------++                     |       |                | SCC             |   |26LS32|      |  O  #
   |    +------+    +------+   | F244   |                      |       |     +----+     +-----------------+   +------+      |     #
   |H   |511000|    | RP2H |   +--------+                      +-------+     |7705|                           +------+      |  N  #
   |    +------+    +------+   | F244   |                                    +----+     +-----------------+   |26LS30|      |     #
   |J   |511000|    | 0259 |   +--------+-+----------+                   o------o       | 338-6523        |   +------+      |  T  #
   |    +------+    +------+   | TC531000 | TC531000 |                   | CONN |       | VIA             |   |26LS30|      |     #
   |K   |511000|    +------+   |  ROM H3  |  ROM L3  |          +------+ |   == |       +-----------------+   +------+      |     #
   |    +------+    | RP2L |   +----------+----------+          | F02  | |   == |             +-------+                     |     #
   |L   |511000|    +------+   +----------+----------+          +------+ |   == |             | LS14  |                     |     #
   |    +------+    | 0259 |   | TC531000 | TC531000 |  +------++------+ |   == |             +-------+                     |     #
   |M   |511000|    +------+   |  ROM H2  |  ROM L2  |  |67L401|| LS166| |   == |             | LS14  |                     |     #
   |    +------+    +------+   +----------+----------+  +------++------+ |   == |             +-------+ +------+            |     #
   |N   |511000|    | F245 |   +----------+----------+  +------++------+ |   == |                       | 0440A|            |     #
   |    +------+    +------+   | TC531000 | TC531000 |  |67L401|| LS00 | |   == |                       +------+            |     #
   |P   |511000|    +------+   |  ROM H1  |  ROM L1  |  +------++------+ o------o      +------+                             |     #
   |    +------+    | F245 |   +----------+----------+         +-------+               |TL497 |                             |     #
   |R   |511000|    +------+   +----------+----------+         | LS273 |               +------+                             |     #
   |    +------+    +------+   | TC531000 | TC531000 |         +-------+                                                    |     #
   |S   |511000|    | RP2S |   |  ROM H0  |  ROM L0  |         | LS05 |                                                     |     #
   |    +------+    +------+   +----------+----------+         +------+  (c)1987                                          J4|     #
   |     DRAM                       LASERWRITER II NT                    Apple Computer               640-4105              |     #
   +------------------------------------------------------------------------------------------------------------------------+=====+
 */

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
#include "machine/6522via.h"
#include "machine/z80scc.h"

#include "screen.h"

#define LOG_VIDEO   (1U << 1)

//#define VERBOSE (LOG_COMMAND)
#include "logmacro.h"

namespace {

enum print_state {
	READING_CMD,
	WRITING_RESULT
};

class lwriter_state : public driver_device
{
public:
	lwriter_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_scc(*this, "scc")
		, m_via(*this, "via")
		, m_screen(*this, "screen")
		, m_vram_offset(0)
		, m_dsw1(*this, "DSW1")
		, m_dram_ptr(*this, "dram")
		, m_sram_ptr(*this, "sram")
		, m_rom_ptr(*this, "rom")
		, m_overlay(1)
		, m_via_pb(0)
		, m_print_sc(1)
		, m_print_state(READING_CMD)
		, m_print_bit(0)
		, m_print_cmd(0)
		, m_print_result(0)
		, m_sbsy(0)
		, m_cbsy(0)
		, m_vsync(0)
		, m_fifo_count(0)
		, m_pb6_tick_count(0)
	{ }

	void lwriter(machine_config &config);
	void lwriter2nt(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// A guess based on not very much
	static constexpr unsigned FB_HEIGHT = 3434;
	static constexpr unsigned FB_WIDTH = 2520;

	uint16_t bankedarea_r(offs_t offset);
	void bankedarea_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint8_t eeprom_r(offs_t offset);
	void eeprom_w(offs_t offset, uint8_t data, uint8_t mem_mask = ~0);
	void dram_or_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	void led_out_w(uint8_t data);
	void fifo_out_w(uint8_t data);
	uint8_t via_pa_r();
	void via_pa_w(uint8_t data);
	void via_pa_lw_w(uint8_t data);
	void via_ca2_w(int state);
	uint8_t via_pb_r();
	uint8_t via_pb_lw2nt_r();
	void write_dtr(int state);
	void via_pb_w(uint8_t data);
	void via_cb1_w(int state);
	void via_cb2_w(int state);
	void via_int_w(int state);
	emu_timer *m_pb6_timer;
	TIMER_CALLBACK_MEMBER(pb6_tick);
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	//void scc_int(int state);
	void maincpu_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<scc8530_device> m_scc;
	required_device<via6522_device> m_via;
	optional_device<screen_device> m_screen;
	std::unique_ptr<u8[]> m_vram;
	size_t m_vram_offset;
	required_ioport m_dsw1;

	required_shared_ptr<uint16_t> m_dram_ptr;
	required_region_ptr<uint16_t> m_sram_ptr, m_rom_ptr;
	bool m_overlay;
	uint8_t m_via_pb;
	bool m_print_sc;
	print_state m_print_state;
	int m_print_bit;
	int m_print_cmd;
	int m_print_result;
	bool m_sbsy;
	bool m_cbsy;
	bool m_vsync;
	int m_fifo_count;
	int m_pb6_tick_count;
	int m_vbl_count;
	int m_reset_count;
};

/*
Address maps (x = ignored; * = selects address within this range)
68k address map:
a23 a22 a21 a20 a19 a18 a17 a16 a15 a14 a13 a12 a11 a10 a9  a8  a7  a6  a5  a4  a3  a2  a1  (a0 via UDS/LDS)
*   *   *                                                                                           PAL16R6 U80
*   *   *   *   *   *                                                                               decoded by pals
Overlay ON:
0   0   0   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *       R   ROM
0   0   0   0   0   0   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *       R   ROMEN1
0   0   0   0   0   1   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *       R   ROMEN2
0   0   0   0   1   0   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *       R   ROMEN3
0   0   0   0   1   1   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *       R   ROMEN4
0   0   0   1   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x       OPEN BUS
Overlay OFF:
0   0   0   ?   ?  ?0?  x   x   x   x   x   x   *   *   *   *   *   *   *   *   *   *   *   *       RW  SRAM
Unknown:
?   ?   ?   ?   ?   ?   x   x   x   x   x   x  (*) (*)  *   *   *   *   *   *   *   *   *   1       RW  2804 EEPROM
  (technically a10 and a11 are ignored, but if a 2808 or 2816 is put in this spot the address lines do connect to the appropriate pins)
Common:
0   0   1   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *       R   ROM
0   0   1   0   0   0   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *       R   ROMEN1
0   0   1   0   0   1   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *       R   ROMEN2
0   0   1   0   1   0   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *       R   ROMEN3
0   0   1   0   1   1   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *       R   ROMEN4
0   0   1   1   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x       OPEN BUS
0   1   0   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *       RW  DRAM
0   1   1   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *       RW ???? DRAM mirror?
1   0   0   ?   ?   ?   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   0       W   64x8 FIFO
1   0   0   ?   ?   ?   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   1       W   Status LEDs and mech
1   0   1   ?   ?   ?   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   *   *   1       R   8530 SCC Read
1   1   0   ?   ?   ?   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   *   *   0       W   8530 SCC Write
1   1   1  ?x? ?0?  ?   x   x   x   x   x   x   x   x   x   x   x   x   x   *   *   *   *   0       RW  65C22 VIA
1   1   1  ?x? ?1?  ?   x   x   x   x   x   x   x   x   x   x   x   x   x   *   *   *   *   0       RW  debugger rom/pod area
              |               |               |               |               |
map when overlay is set:
000000-1fffff ROM (second half is open bus)
map when overlay is clear:
000000-03ffff SRAM(?)
040000-1fffff ????
200000-3fffff ROM (second half is open bus)
400000-5fffff DRAM
600000-7fffff DRAM mirror
800000-83ffff LEDs and status bits to printer mechanism, FIFO
840000-9fffff unknown
a00000-a3ffff SCC read
a40000-bfffff unknown
c00000-c3ffff SCC write
c40000-dfffff unknown
e00000-e3ffff VIA
e40000-f7ffff unknown
f80000-fbffff debug area (first read must be 0xAAAA5555, then 68k will jump to address of second read)
fc0000-ffffff unknown

The ADB bit-bang transceiver MCU connects to the VIA CB1 (adbclk) and CB2 (adbdata) pins,
as well as PA0 (ST1), PA2 (ST2) and PA3 (ADB /INT)
*/

void lwriter_state::maincpu_map(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x1fffff).rw(FUNC(lwriter_state::bankedarea_r), FUNC(lwriter_state::bankedarea_w));
	map(0x200000, 0x2fffff).rom().region("rom", 0); // 1MB ROM
	//map(0x300000, 0x3fffff) // open bus?
	map(0x400000, 0x5fffff).ram().share(m_dram_ptr); // 2MB DRAM
	// DRAM appears to mirrored through 0x600000 but when written to does a bitwise-or with the existing data
	// instead of replacing it. This presumeably improves the performance of these operations by avoiding
	// an extra memory read.
	map(0x600000, 0x7fffff).readonly().share(m_dram_ptr).w(FUNC(lwriter_state::dram_or_w));
	map(0x800000, 0x800000).w(FUNC(lwriter_state::led_out_w)).mirror(0x1ffffe); // mirror is a guess given that the pals can only decode A18-A23
	map(0x800001, 0x800001).w(FUNC(lwriter_state::fifo_out_w)).mirror(0x1ffffe); // mirror is a guess given that the pals can only decode A18-A23
	map(0xc00001, 0xc00001).w(m_scc, FUNC(scc8530_device::cb_w)).mirror(0x1ffff8);
	map(0xc00005, 0xc00005).w(m_scc, FUNC(scc8530_device::db_w)).mirror(0x1ffff8);
	map(0xa00000, 0xa00000).r(m_scc, FUNC(scc8530_device::cb_r)).mirror(0x1ffff8);
	map(0xa00004, 0xa00004).r(m_scc, FUNC(scc8530_device::db_r)).mirror(0x1ffff8);

	map(0xc00003, 0xc00003).w(m_scc, FUNC(scc8530_device::ca_w)).mirror(0x1ffff8);
	map(0xc00007, 0xc00007).w(m_scc, FUNC(scc8530_device::da_w)).mirror(0x1ffff8);
	map(0xa00002, 0xa00002).r(m_scc, FUNC(scc8530_device::ca_r)).mirror(0x1ffff8);
	map(0xa00006, 0xa00006).r(m_scc, FUNC(scc8530_device::da_r)).mirror(0x1ffff8);

	map(0xe00000, 0xe0001f).m(m_via, FUNC(via6522_device::map)).umask16(0x00ff).mirror(0x17ffe0);

	// overlaps with SCC but only uses even addresses
	map(0xc00000, 0xc00400).rw(FUNC(lwriter_state::eeprom_r), FUNC(lwriter_state::eeprom_w)).umask16(0xff00);
}

static INPUT_PORTS_START( lwriter )
	PORT_START("DSW1")
	// Switch 1 | Switch 2 | switchsetting value | Meaning
	// Down       Down                         0   serial batch mode 1200 baud (0)
	// Up         Down                         1   serial batch mode 9600 baud (1)
	// Down       Up                           2   diablo emulation (special switch = 0)
	// Down       Up                           2   executive mode (special switch = 1)
	// Up         Up                           3   apple talk
	PORT_DIPNAME(0x60, 0x20, "Switch")
	PORT_DIPSETTING(  0x00, "Serial batch mode 1200 baud")
	PORT_DIPSETTING(  0x40, "Serial batch mode 9600 baud")
	PORT_DIPSETTING(  0x20, "Diablo emulation (special switch = 0) / Executive mode (special switch = 1)")
	PORT_DIPSETTING(  0x60, "AppleTalk")
INPUT_PORTS_END

// 300 dots/inch: 1,863,813 Hz (from the CX manual) / (8 bits per byte) / (48 bytes written per interrupt / 24 ticks per interrupt) = ~116488.3
#define PB6_CLK 116488

/* Start it up */
void lwriter_state::machine_start()
{
	m_vram = make_unique_clear<uint8_t []>(FB_WIDTH * FB_HEIGHT / 8);

	// do stuff here later on like setting up printer mechanisms HLE timers etc
	m_pb6_timer = timer_alloc(FUNC(lwriter_state::pb6_tick), this);

	m_pb6_timer->adjust(attotime::from_hz(PB6_CLK));
	// Initialize ca1 to 1 so that we don't miss the first interrupt/transition to 0
	m_via->write_ca1(1);
}

void lwriter_state::machine_reset()
{
}

/* Overlay area */
uint16_t lwriter_state::bankedarea_r(offs_t offset)
{
	if (m_overlay)
	{
		return m_rom_ptr[offset];
	}
	else if (offset <= 0x01ffff)
	{
		if ((offset > 0x7ff) && !machine().side_effects_disabled()) { logerror("Attempt to read banked area (with overlay off) past end of SRAM from offset %08X!\n",offset<<1); }
		return m_sram_ptr[offset&0x7FF];
	}
	if (!machine().side_effects_disabled()) { logerror("Attempt to read banked area (with overlay off) past end of SRAM from offset %08X! Returning 0xFFFF!\n",offset<<1); }
	return 0xFFFF;
}

void lwriter_state::bankedarea_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (m_overlay)
	{
		if(!machine().side_effects_disabled()) { logerror("Attempt to write banked area (with overlay ON) with data %04X to offset %08X IGNORED!\n",data, offset<<1); }
		return;
	}
	else if (offset <= 0x01ffff)
	{
		if ((offset > 0x7ff) && !machine().side_effects_disabled()) { logerror("Attempt to write banked area (with overlay off) with data %04X to offset %08X!\n",data, offset<<1); }
		COMBINE_DATA(&m_sram_ptr[offset&0x7FF]);
		return;
	}
	if (!machine().side_effects_disabled()) { logerror("Attempt to write banked area (with overlay off) with data %04X to offset %08X IGNORED!\n", data, offset<<1); }
}

void lwriter_state::dram_or_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	m_dram_ptr[offset] |= data & mem_mask;
}

uint8_t lwriter_state::eeprom_r(offs_t offset)
{
	uint8_t result = 0;
	// adjust offset to match real hardware mapping
	switch (offset)
	{
		case 0x80:
		case 0x81:
		case 0x82:
		case 0x83: {
			constexpr uint8_t signature[] = {0x7a, 0x53, 0xda, 0x71};
			result = signature[offset-0x80];
			break;
		}
		case 0xb0:
			result = 0x1; // disable printing the test page (dostartpage)
			break;
		case 0xf3:
			result = 0x1; // special switch procedure 1 to invoke executive (58 + 0xb9)
			break;
	}

	logerror("eeprom_r! %x %x\n", offset, result);
	return result;
}

void lwriter_state::eeprom_w(offs_t offset, uint8_t data, uint8_t mem_mask)
{
	logerror("eeprom_w! %x %x %x\n", offset, data, mem_mask);
}

TIMER_CALLBACK_MEMBER(lwriter_state::pb6_tick)
{
	m_pb6_tick_count += 1;
	m_via->write_pb6(0);
	m_via->write_pb6(1);
	m_pb6_timer->adjust(attotime::from_hz(PB6_CLK));
}

/* 4 diagnostic LEDs, plus 4 i/o lines for the printer */
/* 0 - print cbsy
 * 1 - print prnt
 * 2 - print vsync
 * 3 - print cprdy
 * 4 - led 1
 * 5 - led 2
 * 6 - led 3
 * 7 - led 4
 */
void lwriter_state::led_out_w(uint8_t data)
{
	//popmessage("LED status: %02X\n", data&0xFF);
	logerror("LED status: %02X\n", data&0xFF);
	m_cbsy = data & 1;
	if (!m_vsync && (data & 4)) { // vsync
		LOGMASKED(LOG_VIDEO, "vsync\n");
		m_vbl_count = 0;
	}
	m_vsync = (data & 4);
	popmessage("LED status: %x %x %x %x %x %x %x %x\n", data&0x80, data&0x40, data&0x20, data&0x10, data&0x8, data&0x4, data&0x2, data&0x1);
}

/* FIFO to printer, 64 bytes long */
void lwriter_state::fifo_out_w(uint8_t data)
{
	m_fifo_count += 8;
	if (m_vbl_count >= FB_HEIGHT) {
		m_vbl_count = 0;
	}

	m_vram[m_vbl_count * FB_WIDTH/8 + m_vram_offset] = data;
	m_vram_offset++;
	if (m_vram_offset > (FB_WIDTH*FB_HEIGHT/8)) {
		LOGMASKED(LOG_VIDEO, "vram reset\n");
		m_vram_offset = 0;
	}
}
/* via port a bits */
/* 0 - ?
 * 1 - print sbsy
 * 2 - ?
 * 3 - ?
 * 4 - print vsreq
 * 5 - switch setting low
 * 6 - switch setting high
 * 7 - print (STATUS/COMMAND Message Line)
 */
uint8_t lwriter_state::via_pa_r()
{
	logerror(" VIA: Port A read!\n");
	uint8_t result = m_dsw1->read();
	if (m_sbsy)
	{
		result |= 2;
	}
	result |= (m_print_sc << 7);
	return result | 0x1C;
}

void lwriter_state::via_pa_w(uint8_t data)
{
	logerror(" VIA: Port A written with data of 0x%02x!\n", data);
}

void lwriter_state::via_pa_lw_w(uint8_t data)
{
	via_pa_w(data);
	m_cbsy = data & 1;
}

void lwriter_state::via_ca2_w(int state)
{
	logerror(" VIA: CA2 written with %d!\n", state);
}

/* via port b bits:
 * 0 - ?
 * 1 - print rdy
 * 2 - print pprdy
 * 3 - overlay
 * 4 - scc w/req
 * 5 - resetfifo
 * 6 - timer 2 clk (mroclk)
 * 7 - vbl
 */
uint8_t lwriter_state::via_pb_r()
{
	logerror(" VIA: Port B read!\n");
	return 0xFB;
}

// The 3rd bit (PPRDY) is used for talking with the print controller
// and is inverted on II NT
uint8_t lwriter_state::via_pb_lw2nt_r()
{
	return via_pb_r() ^ 0x4;
}

void lwriter_state::via_pb_w(uint8_t data)
{
	if ((data & 0x20) && ((m_via_pb & 0x20) == 0)) {
		m_reset_count++;
		LOGMASKED(LOG_VIDEO, "reset fifo %d pb6 %d\n", m_fifo_count, m_pb6_tick_count);
	}
	if ((data & 0x80) && ((m_via_pb & 0x80) == 0)) {
		m_vbl_count++;
		LOGMASKED(LOG_VIDEO, "vbl fifo:%d vbl:%d vram_off: %ld\n", m_fifo_count, m_vbl_count, m_vram_offset);
		m_vram_offset = 0;
	}

	logerror(" VIA: Port B written with data of 0x%02x!\n", data);
	/* Like early Mac models which had VIA A4 control overlaying, the
	 * LaserWriter II NT overlay is controlled by VIA B3 */
	m_overlay = BIT(data,3);
	m_via_pb = data;
}

void lwriter_state::via_cb1_w(int state)
{
	logerror(" VIA: CB1 written with %d!\n", state);
}

void lwriter_state::via_cb2_w(int state)
{
	logerror(" VIA: CB2 written with %d!\n", state);
}

void lwriter_state::via_int_w(int state)
{
	logerror(" VIA: INT output set to %d!\n", state);
	//TODO: this is likely wrong, the VPA pin which controls whether autovector is enabled or not is controlled by PAL U8D, which is not dumped.
	m_maincpu->set_input_line(M68K_IRQ_1, (state ? ASSERT_LINE : CLEAR_LINE));
}

/* scc stuff */
/*
void lwriter_state::scc_int(int state)
{
    logerror(" SCC: INT output set to %d!\n", state);
    //m_via->set_input_line(VIA_CA1, state ? ASSERT_LINE : CLEAR_LINE);
    m_via->write_ca1(state);
}*/

#define CPU_CLK (22.321_MHz_XTAL / 2) // Based on pictures form here: http://picclick.co.uk/Apple-Postscript-LaserWriter-IINT-Printer-640-4105-M6009-Mainboard-282160713108.html#&gid=1&pid=7
#define RXC_CLK ((CPU_CLK.value() - (87 * 16 * 70)) / 3) // Tuned to get 9600 baud according to manual, needs rework based on real hardware
/* These are from LBP-CX Series Video Interface Manual:
 * http://beefchicken.com/retro/laserwriter/LBP-CX%20Series%20Video%20Interface%20Service%20Manual.pdf
 */
#define SR0 1
#define SR1 2
#define SR2 4
#define SR4 8
#define SR5 0xb
#define EC0 0x40
#define EC1 0x43
#define EC2 0x45
#define EC3 0x46
#define EC4 0x49
#define EC5 0x4a
#define EC6 0x4c
#define EC7 0x4f
#define EC14 0x5d

void lwriter_state::write_dtr(int state)
{
	// DTR seems to be used as a clock for communication with
	// the print controller. We arbitrarily choose state == 1
	// to run our code
	if (state == 1 && (m_cbsy || m_sbsy))
	{
		logerror("pb line %d %d %d!\n", m_via_pb & 1, m_print_state, m_print_bit);
		switch (m_print_state)
		{
			case READING_CMD: {
				m_print_cmd <<= 1;
				m_print_cmd |= (m_via_pb & 1);
				m_print_bit += 1;
				if (m_print_bit == 8)
				{
					logerror("PRINT_CMD %x!\n", m_print_cmd);
					m_print_bit = 0;
					m_print_state = WRITING_RESULT;
					m_sbsy = true;
					// The status messages have odd parity in the high bit
					// right now we just manually make sure that's true.
					switch (m_print_cmd)
					{
						case SR1:
						case SR2:
						case SR4:
							m_print_result = 1 << 7; // parity
							break;
						case SR5: // paper size
							m_print_result = 1 << 6; // A4
							break;
						case SR0:
						default:
							m_print_result = 1 << 1; // print request
							break;
					}
					m_print_cmd = 0;
				}
				break;
			}
			case WRITING_RESULT: {
				m_print_sc = m_print_result & 0x1;
				m_print_result >>= 1;
				m_print_bit += 1;
				if (m_print_bit == 8)
				{
					logerror("PRINT_RESULT!\n");
					m_print_bit = 0;
					m_print_state = READING_CMD;
					m_sbsy = false;
				}
				break;
			}
		}
	}
}

uint32_t lwriter_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	auto const f = [] (auto x) { return x ? u32(0) : u32(0xffffffff); };
	for (int y = 0; y < FB_HEIGHT; y++) {
		for (int x = 0; x < FB_WIDTH; x += 8) {
			uint32_t *scanline = &bitmap.pix(y, x);
			uint8_t const pixels = m_vram[y * FB_WIDTH/8 + x/8];
			*scanline++ = f(BIT(pixels, 7));
			*scanline++ = f(BIT(pixels, 6));
			*scanline++ = f(BIT(pixels, 5));
			*scanline++ = f(BIT(pixels, 4));
			*scanline++ = f(BIT(pixels, 3));
			*scanline++ = f(BIT(pixels, 2));
			*scanline++ = f(BIT(pixels, 1));
			*scanline++ = f(BIT(pixels, 0));
		}
	}

	return 0;
}

void lwriter_state::lwriter2nt(machine_config &config)
{
	lwriter(config);
	m_via->readpb_handler().set(FUNC(lwriter_state::via_pb_lw2nt_r));
	m_via->writepa_handler().set(FUNC(lwriter_state::via_pa_w));
}

void lwriter_state::lwriter(machine_config &config)
{
	M68000(config, m_maincpu, CPU_CLK);
	m_maincpu->set_addrmap(AS_PROGRAM, &lwriter_state::maincpu_map);

	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(30);
	m_screen->set_size(FB_WIDTH, FB_HEIGHT);
	m_screen->set_visarea_full();
	m_screen->set_screen_update(FUNC(lwriter_state::screen_update));

	SCC8530(config, m_scc, CPU_CLK);
	m_scc->configure_channels(RXC_CLK, 0, RXC_CLK, 0);
	/* Port A */
	m_scc->out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
	m_scc->out_dtra_callback().set("rs232a", FUNC(rs232_port_device::write_dtr));
	m_scc->out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts));
	/* Port B */
	m_scc->out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
	m_scc->out_dtrb_callback().set(FUNC(lwriter_state::write_dtr));
	m_scc->out_rtsb_callback().set("rs232b", FUNC(rs232_port_device::write_rts));
	/* Interrupt */
	// The "CA1 Latch/Interrupt Control" bit in VIA PCR gets set to "negative
	// active edge" so we invert the SCC interrupt.
	m_scc->out_int_callback().set(m_via, FUNC(via6522_device::write_ca1)).invert();
	//m_scc->out_int_callback().set(FUNC(lwriter_state::scc_int));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, "terminal"));
	rs232a.rxd_handler().set(m_scc, FUNC(scc8530_device::rxa_w));
	rs232a.cts_handler().set(m_scc, FUNC(scc8530_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, "terminal"));
	rs232b.rxd_handler().set(m_scc, FUNC(scc8530_device::rxb_w));
	rs232b.cts_handler().set(m_scc, FUNC(scc8530_device::ctsb_w));

	R65NC22(config, m_via, CPU_CLK/10); // 68000 E clock presumed
	m_via->readpa_handler().set(FUNC(lwriter_state::via_pa_r));
	m_via->readpb_handler().set(FUNC(lwriter_state::via_pb_r));
	m_via->writepa_handler().set(FUNC(lwriter_state::via_pa_lw_w));
	m_via->writepb_handler().set(FUNC(lwriter_state::via_pb_w));
	m_via->cb1_handler().set(FUNC(lwriter_state::via_cb1_w));
	m_via->ca2_handler().set(FUNC(lwriter_state::via_ca2_w));
	m_via->cb2_handler().set(FUNC(lwriter_state::via_cb2_w));
	m_via->irq_handler().set(FUNC(lwriter_state::via_int_w));
}

/* SCC init sequence
 * :scc B Reg 09 <- c0 - Master Interrupt Control - Device reset
 * -
 * :scc A Reg 0f <- 00 - External/Status Control Bits - Disable all
 * :scc B Reg 05 <- 02 - Tx setup: 5 bits, Tx disable, RTS:1 DTR:0
 * :scc B Reg 05 <- 00 - Tx setup: 5 bits, Tx disable, RTS:0 DTR:0
 * -
 * :scc A Reg 09 <- c0 - Master Interrupt Control - Device reset
 *
 * -
 * :scc A Reg 0f <- 00 - External/Status Control Bits - Disable all
 * :scc A Reg 04 <- 4c - Setting up Asynchrounous mode: 2 Stop bits, No parity, 16x clock
 * :scc A Reg 0b <- 50 - Clock Mode Control - TTL clk on RTxC, Rx and Tx clks from BRG, TRxC is input
 * :scc A Reg 0e <- 00 - Misc Control Bits - BRG clk is RTxC, BRG is disabled
 * :scc A Reg 0c <- 0a - Low byte of baudrate generator constant
 * :scc A Reg 0d <- 00 - Hi byte of baudrate generator constant
 * :scc A Reg 0e <- 01 - BRG enabled with external clk from RTxC
 * :scc A Reg 0a <- 00 - Synchronous parameters, all turned off
 * :scc A Reg 03 <- c1 - Rx setup: 8 bits, Rx enabled
 * :scc A Reg 05 <- 6a - Tx setup: 8 bits, Tx enable, RTS:1 DTR:0
 * -
 * :scc A Reg 01 <- 00 - Rx interrupt disabled
 * :scc A Reg 01 <- 30 - Wait/Ready on receive, Rx int an all characters, parity affect vector
 * :scc A Reg 00 <- 30 - Error Reset command
 * -
 * :scc A Reg 01 <- 01 - External interrupt enabled, Rx ints disabled
 * :scc A Reg 00 <- 30 - Error Reset command
 * :scc A Reg 00 <- 30 - Error Reset command
 * - last three loops
*/


#define ROM_LOAD16_BYTE_BIOS(bios,name,offset,length,hash)     ROMX_LOAD(name, offset, length, hash, ROM_SKIP(1) | ROM_BIOS(bios))

ROM_START(lwriter)
	ROM_REGION16_BE( 0x200000, "rom", ROMREGION_ERASEFF )

	ROM_SYSTEM_BIOS(0, "rev47", "PostScript Version 47.0")
	ROM_LOAD16_BYTE_BIOS(0, "342-0568a.rom", 0x000001, 0x10000, CRC (83341c75) SHA1 (d7c65d09abaaf862fef00ac4df7a094ddedd24c5)) // Label: "342-0568-A // (C) '87 ADOBE SYS // (C) 81 LINOTYPE // POSTSCRIPT"
	ROM_LOAD16_BYTE_BIOS(0, "342-0569a.rom", 0x000000, 0x10000, CRC (47d33a6b) SHA1 (0e79fa9204f9be6539abcdb619a17a4ced912b13)) // Label: "342-0569-A // (C) '87 ADOBE SYS // (C) 81 LINOTYPE // POSTSCRIPT"
	ROM_LOAD16_BYTE_BIOS(0, "342-0570a.rom", 0x020001, 0x10000, CRC (38753dd2) SHA1 (931eb3386fe0fff1de1311b2bc1cee8ee02ed599)) // Label: "342-0570-A // (C) '87 ADOBE SYS // (C) 81 LINOTYPE // POSTSCRIPT"
	ROM_LOAD16_BYTE_BIOS(0, "342-0571a.rom", 0x020000, 0x10000, CRC (08888acd) SHA1 (f771306d8f876e6e4ed14f3c6e5b71dff75cf49e)) // Label: "342-0571-A // (C) '87 ADOBE SYS // (C) 81 LINOTYPE // POSTSCRIPT"
	ROM_LOAD16_BYTE_BIOS(0, "342-0572a.rom", 0x040001, 0x10000, CRC (0a64af91) SHA1 (22cd61ed7c2f64bfd4ddbd7b5cde64311a3db5e6)) // Label: "342-0572-A // (C) '87 ADOBE SYS // (C) 81 LINOTYPE // POSTSCRIPT"
	ROM_LOAD16_BYTE_BIOS(0, "342-0573a.rom", 0x040000, 0x10000, CRC (f8e529fe) SHA1 (8a4511a4c12eb24c731e1de747886aacfa2057d5)) // Label: "342-0573-A // (C) '87 ADOBE SYS // (C) 81 LINOTYPE // POSTSCRIPT"
	ROM_LOAD16_BYTE_BIOS(0, "342-0574a.rom", 0x060001, 0x10000, CRC (bb694699) SHA1 (2e208b30e8d05725f7e8b469974b6357008fbb1d)) // Label: "342-0574-A // (C) '87 ADOBE SYS // (C) 81 LINOTYPE // POSTSCRIPT"
	ROM_LOAD16_BYTE_BIOS(0, "342-0575a.rom", 0x060000, 0x10000, CRC (c21c1d22) SHA1 (9fc6cd059380c11588c182fb8ec6422e5db472e1)) // Label: "342-0575-A // (C) '87 ADOBE SYS // (C) 81 LINOTYPE // POSTSCRIPT"

	ROM_SYSTEM_BIOS(1, "rev2", "PostScript Version 38.0")
	ROM_LOAD16_BYTE_BIOS(1, "342-0081a_l0.bin", 0x000001, 0x10000, CRC (a76c91df) SHA1 (c62ef2ede8ce7ba92ec75b3fcb3ddbb288fe235b)) // Label: "342-0081A-L0 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(1, "342-0082a_h0.bin", 0x000000, 0x10000, CRC (3342d008) SHA1 (bc01749bd9a9bc129a4100ee64e09a428b0619c1)) // Label: "342-0082A-H0 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(1, "342-0083a_l1.bin", 0x020001, 0x10000, CRC (8569fb1e) SHA1 (0e004f649078949d5c70d6b92774e4696f3f3cd4)) // Label: "342-0083A-L1 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(1, "342-0084a_h1.bin", 0x020000, 0x10000, CRC (a4d939bf) SHA1 (1585d5e651349f2857d8934cfda85fc4012c2c91)) // Label: "342-0084A-H1 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(1, "342-0085a_l2.bin", 0x040001, 0x10000, CRC (a77e5efc) SHA1 (73b60da77d433d97ecbe9e28558836da8c1cc259)) // Label: "342-0085A-L2 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(1, "342-0086a_h2.bin", 0x040000, 0x10000, CRC (5cf037a1) SHA1 (1ea7177fa11ecdd02b794144c182de0836eb4110)) // Label: "342-0086A-H3 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(1, "342-0087a_l3.bin", 0x060001, 0x10000, CRC (8186bd91) SHA1 (4e3623efc4926be8d6182b702642bf634ae23f82)) // Label: "342-0087A-L4 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(1, "342-0088a_h3.bin", 0x060000, 0x10000, CRC (ecf425ec) SHA1 (353c957a16edf8d3a685c1c8bfe896e26d4a15ed)) // Label: "342-0088A-H4 // (C) '85 ADOBE SYS // POSTSCRIPT TM"

	ROM_REGION16_BE( 0x1000, "sram", ROMREGION_ERASEFF )
ROM_END

ROM_START(lwriterplus)
	ROM_REGION16_BE( 0x200000, "rom", ROMREGION_ERASEFF )

	ROM_SYSTEM_BIOS(0, "rev47", "PostScript Version 47.0")
	ROM_LOAD16_BYTE_BIOS(0, "342-0089a.l0", 0x000001, 0x10000, CRC (d5dc7d6e) SHA1 (b9a1f807facf6a6de92fae5887044df961d73ab1)) // Label: "342-0089-A+L0 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(0, "342-0090a.h0", 0x000000, 0x10000, CRC (32dc1f96) SHA1 (f3647b11c712979f6c5658a15a3e8647bd4d1a1d)) // Label: "342-0090-A+H0 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(0, "342-0091a.l1", 0x020001, 0x10000, CRC (a24dcb05) SHA1 (9edfb94a1e6723a7580caed629418ee1d2472a84)) // Label: "342-0091-A+L1 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(0, "342-0092a.h1", 0x020000, 0x10000, CRC (8600e85d) SHA1 (332308825f78a768e30eaa36f10f0ac1c5eacc19)) // Label: "342-0092-A+H1 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(0, "342-0093a.l2", 0x040001, 0x10000, CRC (3c8fd0f7) SHA1 (36315be1ed691b24c49471eab0cd93a4242d6e10)) // Label: "342-0093-A+L2 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(0, "342-0094a.h2", 0x040000, 0x10000, CRC (e1a9d862) SHA1 (ffdd96eb70f54c6bb10dfc94f49ce2d916a74ab6)) // Label: "342-0094-A+H2 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(0, "342-0095a.l3", 0x060001, 0x10000, CRC (47f637f3) SHA1 (d04548144f906a8d89b826692812acdcbfbed144)) // Label: "342-0095-A+L3 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(0, "342-0096a.h3", 0x060000, 0x10000, CRC (3213e057) SHA1 (94d2ac1849b48628004877521c882e0f828f97b3)) // Label: "342-0096-A+H3 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(0, "342-0097a.l4", 0x080001, 0x10000, CRC (9ecdc5fc) SHA1 (336ecaaf29c5396c30a11aaa86533f0598cb50b3)) // Label: "342-0097-A+L4 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(0, "342-0098a.h4", 0x080000, 0x10000, CRC (867657e3) SHA1 (0c9c29bac49fdfcd26f22a751be71508add0a25a)) // Label: "342-0098-A+H4 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(0, "342-0099a.l5", 0x0a0001, 0x10000, CRC (820c0f63) SHA1 (0f8d45fc886f996fbcb4103961810b673e9ab7e4)) // Label: "342-0099-A+L5 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(0, "342-0100a.h5", 0x0a0000, 0x10000, CRC (40aeb030) SHA1 (2a34280a6b2ab54d1c82145ec1a8aaac1f57ae15)) // Label: "342-0100-A+H5 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(0, "342-0101a.l6", 0x0c0001, 0x10000, CRC (aed532c4) SHA1 (39d7d3ae1d35d8b4ec33fd8c88a569c607628e2a)) // Label: "342-0101-A+L6 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(0, "342-0102a.h6", 0x0c0000, 0x10000, CRC (653979d1) SHA1 (bf56df6a7eaee2bc8edfbc45f78d1abb19e6807a)) // Label: "342-0102-A+H6 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(0, "342-0103b.l7", 0x0e0001, 0x10000, CRC (dbafb1ed) SHA1 (b9f5b65b04f8f804c473b62c292dc83d14b2ab33)) // Label: "342-0103-B+L7 // (C) '85 ADOBE SYS // POSTSCRIPT TM"
	ROM_LOAD16_BYTE_BIOS(0, "342-0104b.h7", 0x0e0000, 0x10000, CRC (50c72f89) SHA1 (bdbc0e282121f7dc4d701aa12f94296e436b504b)) // Label: "342-0104-B+H7 // (C) '85 ADOBE SYS // POSTSCRIPT TM"

	ROM_SYSTEM_BIOS(1, "rev42", "PostScript Version 42.2")
	ROM_LOAD16_BYTE_BIOS(1, "f0.bin", 0x000001, 0x10000, CRC (d971aeef) SHA1 (5d9a4cebdeab7bc87f6f1f821a95bfb5ad2ea252))
	ROM_LOAD16_BYTE_BIOS(1, "e0.bin", 0x000000, 0x10000, CRC (9df59887) SHA1 (ffb720cd1067cbbc3b2404bb9e77cc1ce768064a))
	ROM_LOAD16_BYTE_BIOS(1, "f1.bin", 0x020001, 0x10000, CRC (5216141e) SHA1 (6c53955cdc121ee5bee042a0582e31872c8707a9))
	ROM_LOAD16_BYTE_BIOS(1, "e1.bin", 0x020000, 0x10000, CRC (660a977b) SHA1 (c1257d87ddee6485dbf49b0c8da11b38947a0932))
	ROM_LOAD16_BYTE_BIOS(1, "f2.bin", 0x040001, 0x10000, CRC (8947a995) SHA1 (d7f4e2c1ce4f66cb55dd09c6fb876ac765ffca28))
	ROM_LOAD16_BYTE_BIOS(1, "e2.bin", 0x040000, 0x10000, CRC (3ae6e11b) SHA1 (a95e29abd808041bc00851a2dde1bb0951eb787d))
	ROM_LOAD16_BYTE_BIOS(1, "f3.bin", 0x060001, 0x10000, CRC (ca23252f) SHA1 (85714dd8716a1971c31a0609b8726a23f9d15cfc))
	ROM_LOAD16_BYTE_BIOS(1, "e3.bin", 0x060000, 0x10000, CRC (ae91d2a2) SHA1 (84b449d984ab539aef13ece4a0093fff041bd5e3))
	ROM_LOAD16_BYTE_BIOS(1, "f4.bin", 0x080001, 0x10000, CRC (5ad31e1b) SHA1 (90310fa158986ae88adec6de1d2a72a2ff161699))
	ROM_LOAD16_BYTE_BIOS(1, "e4.bin", 0x080000, 0x10000, CRC (987d6796) SHA1 (29a916eb76c953ec0b11b68b4b38dedb305d0c54))
	ROM_LOAD16_BYTE_BIOS(1, "f5.bin", 0x0a0001, 0x10000, CRC (c687c0ab) SHA1 (22e757fa46860b9ee4e97883b884084beb0f9f78))
	ROM_LOAD16_BYTE_BIOS(1, "e5.bin", 0x0a0000, 0x10000, CRC (b4ce7883) SHA1 (ef93d2ab821fe30ab78d749584656772fd95c42d))
	ROM_LOAD16_BYTE_BIOS(1, "f6.bin", 0x0c0001, 0x10000, CRC (3f60a380) SHA1 (d18cd39bb253054807c92f9fbbd756b460b7be5b))
	ROM_LOAD16_BYTE_BIOS(1, "e6.bin", 0x0c0000, 0x10000, CRC (0b2e9058) SHA1 (3ac9e02e70ef9bf1c732efe5912eac0a2fb58a35))
	ROM_LOAD16_BYTE_BIOS(1, "f7.bin", 0x0e0001, 0x10000, CRC (82478865) SHA1 (e6d56e04a586a646ef44bc15460572e8f7a4b602))
	ROM_LOAD16_BYTE_BIOS(1, "e7.bin", 0x0e0000, 0x10000, CRC (07a5548e) SHA1 (23b90b5e2dbaf5fa6a78929033e40c8ded919bad))

	ROM_REGION16_BE( 0x1000, "sram", ROMREGION_ERASEFF )
ROM_END

ROM_START(lwriter2nt)
	ROM_REGION16_BE( 0x200000, "rom", ROMREGION_ERASEFF )
	ROM_LOAD16_BYTE("342-0545.l0", 0x000001, 0x20000, CRC (6431742d) SHA1 (040bd5b84b49b86f2b0fe9ece378bbc7a10a94ec)) // Label: "342-0545-A JAPAN // TC531000CP-F700 // (C) 87 APPLE 8940EAI // (C) 83-87 ADOBE V47.0 // (C) 81 LINOTYPE" TC531000 @L0
	ROM_LOAD16_BYTE("342-0546.h0", 0x000000, 0x20000, CRC (c592bfb7) SHA1 (b595ae225238f7fabd1566a3133ea6154e082e2d)) // Label: "342-0546-A JAPAN // TC531000CP-F701 // (C) 87 APPLE 8940EAI // (C) 83-87 ADOBE V47.0 // (C) 81 LINOTYPE" TC531000 @H0
	ROM_LOAD16_BYTE("342-0547.l1", 0x040001, 0x20000, CRC (205a5ea8) SHA1 (205fefbb5c67a07d57cb6184c69648321a34a8fe)) // Label: "342-0547-A JAPAN // TC531000CP-F702 // (C) 87 APPLE 8940EAI // (C) 83-87 ADOBE V47.0 // (C) 81 LINOTYPE" TC531000 @L1
	ROM_LOAD16_BYTE("342-0548.h1", 0x040000, 0x20000, CRC (f616e1c3) SHA1 (b9e2cd4d07990b2d1936be97b6e89ef21f06b462)) // Label: "342-0548-A JAPAN // TC531000CP-F703 // (C) 87 APPLE 8940EAI // (C) 83-87 ADOBE V47.0 // (C) 81 LINOTYPE" TC531000 @H1
	ROM_LOAD16_BYTE("342-0549.l2", 0x080001, 0x20000, CRC (0b0b051a) SHA1 (64a80085001570c3f99d9865031715bf49bd7698)) // Label: "342-0549-A JAPAN // TC531000CP-F704 // (C) 87 APPLE 8940EAI // (C) 83-87 ADOBE V47.0 // (C) 81 LINOTYPE" TC531000 @L2
	ROM_LOAD16_BYTE("342-0550.h2", 0x080000, 0x20000, CRC (82adcf85) SHA1 (e2ab728afdae802c0c67fc25c9ba278b9cb04e31)) // Label: "342-0550-A JAPAN // TC531000CP-F705 // (C) 87 APPLE 8940EAI // (C) 83-87 ADOBE V47.0 // (C) 81 LINOTYPE" TC531000 @H2
	ROM_LOAD16_BYTE("342-0551.l3", 0x0c0001, 0x20000, CRC (176b3346) SHA1 (eb8dfc7e44f2bc884097e51a47e2f10ee091c9e9)) // Label: "342-0551-A JAPAN // TC531000CP-F706 // (C) 87 APPLE 8940EAI // (C) 83-87 ADOBE V47.0 // (C) 81 LINOTYPE" TC531000 @L3
	ROM_LOAD16_BYTE("342-0552.h3", 0x0c0000, 0x20000, CRC (69b175c6) SHA1 (a84c82be1ec7e373bb097ee74b941920a3b091aa)) // Label: "342-0552-A JAPAN // TC531000CP-F707 // (C) 87 APPLE 8940EAI // (C) 83-87 ADOBE V47.0 // (C) 81 LINOTYPE" TC531000 @H3

	ROM_REGION16_BE( 0x1000, "sram", ROMREGION_ERASEFF )
ROM_END

} // anonymous namespace


/*    YEAR  NAME         PARENT  COMPAT  MACHINE     INPUT    STATE          INIT        COMPANY            FULLNAME             FLAGS */
CONS( 1985, lwriter,     0,      0,      lwriter,    lwriter, lwriter_state, empty_init, "Apple Computer",  "LaserWriter",       MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
CONS( 1986, lwriterplus, 0,      0,      lwriter,    lwriter, lwriter_state, empty_init, "Apple Computer",  "LaserWriter Plus",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
CONS( 1988, lwriter2nt,  0,      0,      lwriter2nt, lwriter, lwriter_state, empty_init, "Apple Computer",  "LaserWriter II NT", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



lynx.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Peter Trauner
/******************************************************************************

 Atari Lynx
 PeT peter.trauner@utanet.at 2000,2001

 info found in bastian schick's bll
 and in cc65 for lynx

 TODO:
 - ComLynx emulation is missing/imperfect
 - Verify timings from real hardware
 - EEPROM Support in some homebrew cartridges?
 - Lynx II support, needs internal ROM dump

******************************************************************************/

#include "emu.h"
#include "lynx.h"

#include "cpu/m6502/g65sc02.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "lynx.lh"

void lynx_state::cpu_map(address_map &map)
{
	map(0x0000, 0xffff).ram().share(m_dram);
	map(0xfc00, 0xfcff).view(m_suzy_view);
	m_suzy_view[0](0xfc00, 0xfcff).rw(FUNC(lynx_state::suzy_read), FUNC(lynx_state::suzy_write));
	map(0xfd00, 0xfdff).view(m_mikey_view);
	m_mikey_view[0](0xfd00, 0xfdff).rw(FUNC(lynx_state::mikey_read), FUNC(lynx_state::mikey_write));
	map(0xfe00, 0xffff).view(m_rom_view);
	m_rom_view[0](0xfe00, 0xfff7).rom().region("maincpu", 0x0000);
	map(0xfff8, 0xfff8).ram(); // Reserved for future hardware (RAM)
	map(0xfff9, 0xfff9).rw(FUNC(lynx_state::memory_config_r), FUNC(lynx_state::memory_config_w));
	map(0xfff8, 0xffff).view(m_vector_view);
	m_vector_view[0](0xfffa, 0xffff).rom().region("maincpu", 0x01fa);
}

static INPUT_PORTS_START( lynx )
	PORT_START("JOY")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Opt 2") PORT_CODE(KEYCODE_2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Opt 1") PORT_CODE(KEYCODE_1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )

	PORT_START("PAUSE")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME(DEF_STR(Pause)) PORT_CODE(KEYCODE_3)
	//PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) CART0 Strobe
	//PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) CART1 Strobe
	// power on and power off buttons
INPUT_PORTS_END

void lynx_state::video_start()
{
	m_screen->register_screen_bitmap(m_bitmap);
}

u32 lynx_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
	return 0;
}

// callback for Mikey call of shift(3) which shall act on the timer_count_down
void lynx_state::sound_cb()
{
	timer_count_down(1);
}

void lynx_state::lynx(machine_config &config)
{
	/* basic machine hardware */
	G65SC02(config, m_maincpu, XTAL(16'000'000) / 4); /* vti core, integrated in vlsi, stz, but not bbr bbs */
	m_maincpu->set_addrmap(AS_PROGRAM, &lynx_state::cpu_map);
	config.set_maximum_quantum(attotime::from_hz(60));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(XTAL(16'000'000) / 16 / (158 + 1) / (104 + 1)); // default config from machine_reset(), actually variable
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_screen_update(FUNC(lynx_state::screen_update));
	m_screen->set_size(160, 105); // 102 visible scanline + 3 blank scanline, horizontal unknown/unverified, variable?
	m_screen->set_visarea(0, 160-1, 0, 102-1);
	config.set_default_layout(layout_lynx);

	PALETTE(config, m_palette).set_entries(0x10);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	LYNX_SND(config, m_sound, XTAL(16'000'000));
	m_sound->set_timer_delegate(FUNC(lynx_state::sound_cb));
	m_sound->add_route(ALL_OUTPUTS, "mono", 0.50);

	/* devices */
	QUICKLOAD(config, "quickload", "o").set_load_callback(FUNC(lynx_state::quickload_cb));

	generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "lynx_cart", "lnx,lyx"));
	cartslot.set_must_be_loaded(true);
	cartslot.set_device_load(FUNC(lynx_state::cart_load));

	/* Software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("lynx");
}

// Lynx II has Hayato replaces Mikey, Compatible but supports stereo
#if 0
void lynx_state::lynx2(machine_config &config)
{
	lynx(config);

	/* sound hardware */
	config.device_remove("mono");
	SPEAKER(config, "speaker", 2).front();
	LYNX2_SND(config.replace(), m_sound, XTAL(16'000'000));
	m_sound->set_timer_delegate(FUNC(lynx_state::sound_cb));
	m_sound->add_route(0, "speaker", 0.50, 0);
	m_sound->add_route(1, "speaker", 0.50, 1);
}
#endif

/* these 2 dumps are saved from an running machine,
   and therefor the rom byte at 0xfff9 is not readable!
   (memory configuration)
   these 2 dumps differ only in this byte!
*/

ROM_START(lynx)
	ROM_REGION(0x200,"maincpu", 0)
	ROM_SYSTEM_BIOS( 0, "default",   "rom save" )
	ROMX_LOAD("lynx.bin",  0x00000, 0x200, BAD_DUMP CRC(e1ffecb6) SHA1(de60f2263851bbe10e5801ef8f6c357a4bc077e6), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "a", "alternate rom save" )
	ROMX_LOAD("lynxa.bin", 0x00000, 0x200, BAD_DUMP CRC(0d973c9d) SHA1(e4ed47fae31693e016b081c6bda48da5b70d7ccb), ROM_BIOS(1))
ROM_END

#if 0
ROM_START(lynx2)
	ROM_REGION(0x200,"maincpu", 0)
	ROM_LOAD("lynx2.bin", 0, 0x200, NO_DUMP)
ROM_END
#endif


QUICKLOAD_LOAD_MEMBER(lynx_state::quickload_cb)
{
	u8 header[10]; // 80 08 dw Start dw Len B S 9 3
	if (image.fread( header, sizeof(header)) != sizeof(header))
		return std::make_pair(image_error::UNSPECIFIED, std::string());

	/* Check the image */
	auto err = verify_cart((const char*)header, LYNX_QUICKLOAD);
	if (err.first)
		return err;

	uint16_t const start = header[3] | (header[2]<<8); //! big endian format in file format for little endian cpu
	uint16_t const length = (header[5] | (header[4]<<8)) - 10;

	std::vector<u8> data;
	data.resize(length);
	if (image.fread(&data[0], length) != length)
		return std::make_pair(image_error::INVALIDIMAGE, "Invalid length in file header");

	address_space &space = m_maincpu->space(AS_PROGRAM);
	for (int i = 0; i < length; i++)
		space.write_byte(start + i, data[i]);

	u8 *rom = memregion("maincpu")->base();
	rom[0x1fc] = start & 0xff;
	rom[0x1fd] = start >> 8;
	space.write_byte(0x1fc, start & 0xff);
	space.write_byte(0x1fd, start >> 8);

	m_maincpu->set_pc(start);

	return std::make_pair(std::error_condition(), std::string());
}

/***************************************************************************

  Game driver(s)

***************************************************************************/

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY  FULLNAME  FLAGS */
CONS( 1989, lynx, 0,      0,      lynx,    lynx,  lynx_state, empty_init, "Atari", "Lynx",   MACHINE_SUPPORTS_SAVE )
// CONS( 1991, lynx2,  lynx,  0,      lynx2,  lynx, lynx_state, empty_init, "Atari",  "Lynx II",    MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )



m20.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Christian Grossler
/*

 Olivetti M20 skeleton driver, by incog (19/05/2009)

Needs a proper Z8001 CPU core, check also

ftp://ftp.groessler.org/pub/chris/olivetti_m20/misc/bios/rom.s

---

APB notes:

0xfc903 checks for the string TEST at 0x3f4-0x3f6, does an int 0xfe if so, unknown purpose

Error codes:
Triangle    Test CPU registers and instructions
Square      Test ROM
4 vertical lines    CPU call or trap instructions failed
Diamond     Test system RAM
E C0     8255 parallel interface IC test failed
E C1     6845 CRT controller IC test failed
E C2     1797 floppy disk controller chip failed
E C3     8253 timer IC failed
E C4     8251 keyboard interface failed
E C5     8251 keyboard test failed
E C6     8259 PIC IC test failed
E K0     Keyboard did not respond
E K1     Keyboard responds, but self test failed
E D1     Disk drive 1 test failed
E D0     Disk drive 0 test failed
E I0     Non-vectored interrupt error
E I1     Vectored interrupt error

*************************************************************************************************/


#include "emu.h"
#include "m20_8086.h"
#include "m20_kbd.h"

#include "bus/rs232/rs232.h"
#include "cpu/i86/i86.h"
#include "cpu/z8000/z8000.h"
#include "imagedev/floppy.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/ram.h"
#include "machine/wd_fdc.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"

#include "formats/m20_dsk.h"


namespace {

class m20_state : public driver_device
{
public:
	m20_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ram(*this, RAM_TAG),
		m_kbdi8251(*this, "i8251_1"),
		m_ttyi8251(*this, "i8251_2"),
		m_i8255(*this, "ppi8255"),
		m_i8259(*this, "i8259"),
		m_fd1797(*this, "fd1797"),
		m_floppy0(*this, "fd1797:0:5dd"),
		m_floppy1(*this, "fd1797:1:5dd"),
		m_apb(*this, "apb"),
		m_palette(*this, "palette")
	{
	}

	void m20(machine_config &config);

private:
	required_device<z8001_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<i8251_device> m_kbdi8251;
	required_device<i8251_device> m_ttyi8251;
	required_device<i8255_device> m_i8255;
	required_device<pic8259_device> m_i8259;
	required_device<fd1797_device> m_fd1797;
	required_device<floppy_image_device> m_floppy0;
	required_device<floppy_image_device> m_floppy1;
	optional_device<m20_8086_device> m_apb;

	required_device<palette_device> m_palette;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint16_t i8259_r(offs_t offset);
	void i8259_w(offs_t offset, uint16_t data);
	uint16_t port21_r();
	void port21_w(uint16_t data);
	void tty_clock_tick_w(int state);
	void kbd_clock_tick_w(int state);
	void timer_tick_w(int state);
	void int_w(int state);
	MC6845_UPDATE_ROW(update_row);

	void m20_data_mem(address_map &map) ATTR_COLD;
	void m20_io(address_map &map) ATTR_COLD;
	void m20_program_mem(address_map &map) ATTR_COLD;

	offs_t m_memsize = 0;
	uint8_t m_port21 = 0;
	void install_memory();

	static void floppy_formats(format_registration &fr);
	uint16_t viack_r();
	uint16_t nviack_r();
};


#define MAIN_CLOCK 4000000 /* 4 MHz */
#define PIXEL_CLOCK 4.433619_MHz_XTAL


MC6845_UPDATE_ROW( m20_state::update_row )
{
	uint32_t *p = &bitmap.pix(y);
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	uint16_t *vram = (uint16_t *)m_ram->pointer();
	uint16_t offset = ((ma | (ra << 1)) << 4);

	for ( int i = 0; i < x_count; i++ )
	{
		uint16_t data = vram[ offset + i ];

		for ( int j = 15; j >= 0; j-- )
		{
			*p = palette[( data & 1 << j ) ? 1 : 0];
			p++;
		}
	}
}

/*
port21      =   0x21        !TTL latch
!   (see hw1 document, fig 2-33, pg 2-47)
!       Output                      Input
!       ======                      =====
!   B0  0 selects floppy 0
!       1 deselects floppy 0
!   B1  0 selects floppy 1
!       1 deselects floppy 1
!   B2  Motor On (Not Used)
!   B3  0 selects double density    0 => Skip basic tests
!       1 selects single density    1 => Perform basic tests
!                                   Latched copy when B7 is written to Port21
!   B4  Uncommitted output          0 => 128K card(s) present
!                                   1 => 32K card(s) present
!                                   Cannot mix types!
!   B5  Uncommitted output          0 => 8-colour card present
!                                   1 => 4-colour card present
!   B6  Uncommitted output          0 => RAM
!                                   1 => ROM (???)
!   B7  See B3 input                0 => colour card present
*/

uint16_t m20_state::port21_r()
{
	//printf("port21 read\n");
	return m_port21;
}

void m20_state::port21_w(uint16_t data)
{
	//printf("port21 write: data 0x%x\n", data);
	m_port21 = (m_port21 & 0xf8) | (data & 0x7);

	// floppy drive select
	if (data & 1) {
		m_floppy0->mon_w(0);
		m_fd1797->set_floppy(m_floppy0);
	}
	else
		m_floppy0->mon_w(1);

	if (data & 2) {
		m_floppy1->mon_w(0);
		m_fd1797->set_floppy(m_floppy1);
	}
	else
		m_floppy1->mon_w(1);

	if(!(data & 3))
		m_fd1797->set_floppy(nullptr);

	// density select 1 - sd, 0 - dd
	m_fd1797->dden_w(data & 8);
}

uint16_t m20_state::i8259_r(offs_t offset)
{
	return m_i8259->read(offset)<<1;
}

void m20_state::i8259_w(offs_t offset, uint16_t data)
{
	m_i8259->write(offset, (data>>1));
}

void m20_state::tty_clock_tick_w(int state)
{
	m_ttyi8251->write_txc(state);
	m_ttyi8251->write_rxc(state);
}

void m20_state::kbd_clock_tick_w(int state)
{
	m_kbdi8251->write_txc(state);
	m_kbdi8251->write_rxc(state);
}

void m20_state::timer_tick_w(int state)
{
	/* The output of the 8253 is connected to a 74LS74 flop chip.
	 * The output of the flop chip is connected to NVI CPU input.
	 * The flop is reset by a 1:8 decoder which compares CPU ST0-ST3
	 * outputs to detect an interrupt acknowledge transaction.
	 * 8253 is programmed in square wave mode, not rate
	 * generator mode.
	 */
	if(m_apb)
		m_apb->nvi_w(state);
	if(state)
		m_maincpu->set_input_line(z8001_device::NVI_LINE, ASSERT_LINE);
}


/* Memory map description (by courtesy of Dwight Elvey)

    DRAM0 = motherboard (128K)
    DRAM1 = first slot from keyboard end
    DRAM2 = second slot from keyboard end
    DRAM3 = third slot from keyboard end
    SRAM0 = memory window for expansion slot
    ROM0  = ROM

Expansion cards are either 32K or 128K. They cannot be mixed, all installed
cards need to be the same type.

B/W, 32K cards, 3 cards => 224K of memory:
<0>0000 D DRAM0  4000 I DRAM0  4000  <8>0000 D DRAM0 18000 I DRAM0  8000
<0>4000 D DRAM1  4000 I DRAM0  8000  <8>4000 D DRAM0 1C000 I DRAM0  C000
<0>8000 D DRAM2  0000 I DRAM0  C000  <8>8000 D DRAM2  4000 I DRAM1  4000
<0>C000 D DRAM2  4000 I DRAM0 10000  <8>C000 D DRAM3  0000 I DRAM2  0000
<1>0000 D DRAM0 14000 I DRAM0  8000  <9>0000 D DRAM0 18000 I DRAM0 18000
<1>4000 D DRAM0 18000 I DRAM0  C000  <9>4000 D DRAM0 1C000 I DRAM0 1C000
<1>8000 D DRAM0 1C000 I DRAM0 10000  <9>8000 D DRAM2  4000 I DRAM2  4000
<1>C000 D DRAM1  0000 I None         <9>C000 D DRAM3  0000 I DRAM3  0000
<2>0000 D DRAM0 14000 I DRAM0 14000  <A>0000 D DRAM0  8000 I DRAM0  8000
<2>4000 D DRAM0 18000 I DRAM0 18000  <A>4000 D DRAM0  C000 I DRAM0  C000
<2>8000 D DRAM0 1C000 I DRAM0 1C000  <A>8000 D DRAM1  4000 I DRAM1  4000
<2>C000 D DRAM1  0000 I DRAM1  0000  <A>C000 D DRAM2  0000 I DRAM2  0000
<3>0000 D DRAM0  0000 I DRAM0  0000  <B>0000 D DRAM3  4000 I DRAM3  4000
<3>4000 D None        I None         <B>4000 D None        I None
<3>8000 D None        I None         <B>8000 D None        I None
<3>C000 D None        I None         <B>C000 D None        I None
<4>0000 D  ROM0  0000 I  ROM0  0000  <C>0000 D DRAM3  4000 I None
<4>4000 D DRAM3  0000 I  ROM0 10000  <C>4000 D None        I None
<4>8000 D DRAM3  4000 I  ROM0 14000  <C>8000 D None        I None
<4>C000 D None        I  ROM0 18000  <C>C000 D None        I None
<5>0000 D DRAM0  8000 I  ROM0 10000  <D>0000 D None        I None
<5>4000 D DRAM0  C000 I  ROM0 14000  <D>4000 D None        I None
<5>8000 D DRAM0 10000 I  ROM0 18000  <D>8000 D None        I None
<5>C000 D SRAM0  0000 I SRAM0  0000  <D>C000 D None        I None
<6>0000 D DRAM0  8000 I DRAM0  8000  <E>0000 D None        I None
<6>4000 D DRAM0  C000 I DRAM0  C000  <E>4000 D None        I None
<6>8000 D DRAM0 10000 I DRAM0 10000  <E>8000 D None        I None
<6>C000 D None        I None         <E>C000 D None        I None
<7>0000 D  ROM0  0000 I  ROM0  0000  <F>0000 D None        I None
<7>4000 D  ROM0 10000 I  ROM0 10000  <F>4000 D None        I None
<7>8000 D  ROM0 14000 I  ROM0 14000  <F>8000 D None        I None
<7>C000 D  ROM0 18000 I  ROM0 18000  <F>C000 D None        I None

B/W, 128K cards, 3 cards => 512K of memory:
<0>0000 D DRAM0  4000 I DRAM0  4000  <8>0000 D DRAM0 18000 I DRAM0  8000
<0>4000 D DRAM1  4000 I DRAM0  8000  <8>4000 D DRAM0 1C000 I DRAM0  C000
<0>8000 D DRAM2  0000 I DRAM0  C000  <8>8000 D DRAM1  C000 I DRAM1  4000
<0>C000 D DRAM2  4000 I DRAM0 10000  <8>C000 D DRAM1 10000 I DRAM1  8000
<1>0000 D DRAM0 14000 I DRAM0  8000  <9>0000 D DRAM0 18000 I DRAM0 18000
<1>4000 D DRAM0 18000 I DRAM0  C000  <9>4000 D DRAM0 1C000 I DRAM0 1C000
<1>8000 D DRAM0 1C000 I DRAM0 10000  <9>8000 D DRAM1  C000 I DRAM1  C000
<1>C000 D DRAM1  0000 I None         <9>C000 D DRAM1 10000 I DRAM1 10000
<2>0000 D DRAM0 14000 I DRAM0 14000  <A>0000 D DRAM0  8000 I DRAM0  8000
<2>4000 D DRAM0 18000 I DRAM0 18000  <A>4000 D DRAM0  C000 I DRAM0  C000
<2>8000 D DRAM0 1C000 I DRAM0 1C000  <A>8000 D DRAM1  4000 I DRAM1  4000
<2>C000 D DRAM1  0000 I DRAM1  0000  <A>C000 D DRAM1  8000 I DRAM1  8000
<3>0000 D DRAM0  0000 I DRAM0  0000  <B>0000 D DRAM1 14000 I DRAM1 14000
<3>4000 D None        I None         <B>4000 D DRAM1 18000 I DRAM1 18000
<3>8000 D None        I None         <B>8000 D DRAM1 1C000 I DRAM1 1C000
<3>C000 D None        I None         <B>C000 D DRAM2  0000 I DRAM2  0000
<4>0000 D  ROM0  0000 I  ROM0  0000  <C>0000 D DRAM2  4000 I DRAM2  4000
<4>4000 D DRAM3  0000 I None         <C>4000 D DRAM2  8000 I DRAM2  8000
<4>8000 D DRAM3  4000 I None         <C>8000 D DRAM2  C000 I DRAM2  C000
<4>C000 D None        I None         <C>C000 D DRAM2 10000 I DRAM2 10000
<5>0000 D DRAM0  8000 I  ROM0 10000  <D>0000 D DRAM2 14000 I DRAM2 14000
<5>4000 D DRAM0  C000 I  ROM0 14000  <D>4000 D DRAM2 18000 I DRAM2 18000
<5>8000 D DRAM0 10000 I  ROM0 18000  <D>8000 D DRAM2 1C000 I DRAM2 1C000
<5>C000 D SRAM0  0000 I SRAM0  0000  <D>C000 D DRAM3  0000 I DRAM3  0000
<6>0000 D DRAM0  8000 I DRAM0  8000  <E>0000 D DRAM3  4000 I DRAM3  4000
<6>4000 D DRAM0  C000 I DRAM0  C000  <E>4000 D DRAM3  8000 I DRAM3  8000
<6>8000 D DRAM0 10000 I DRAM0 10000  <E>8000 D DRAM3  C000 I DRAM3  C000
<6>C000 D None        I None         <E>C000 D DRAM3 10000 I DRAM3 10000
<7>0000 D  ROM0  0000 I  ROM0  0000  <F>0000 D DRAM3 14000 I DRAM3 14000
<7>4000 D  ROM0 10000 I  ROM0 10000  <F>4000 D DRAM3 18000 I DRAM3 18000
<7>8000 D  ROM0 14000 I  ROM0 14000  <F>8000 D DRAM3 1C000 I DRAM3 1C000
<7>C000 D  ROM0 18000 I  ROM0 18000  <F>C000 D DRAM3  0000 I DRAM3  0000
*/


void m20_state::m20_program_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x40000, 0x41fff).rom().region("maincpu", 0x00000);
}

void m20_state::m20_data_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x40000, 0x41fff).rom().region("maincpu", 0x00000);
}


void m20_state::install_memory()
{
	m_memsize = m_ram->size();
	uint8_t *memptr = m_ram->pointer();
	address_space& pspace = m_maincpu->space(AS_PROGRAM);
	address_space& dspace = m_maincpu->space(AS_DATA);

	/* install mainboard memory (aka DRAM0) */

	/* <0>0000 */
	pspace.install_ram(0x0000, 0x3fff, 0, memptr + 0x4000);
	dspace.install_ram(0x0000, 0x3fff, 0, memptr + 0x4000);
	/* <0>4000 */
	pspace.install_ram(0x4000, 0x7fff, 0, memptr + 0x8000);
	/* <0>8000 */
	pspace.install_ram(0x8000, 0xbfff, 0, memptr + 0xc000);
	/* <0>C000 */
	pspace.install_ram(0xc000, 0xcfff, 0, memptr + 0x10000);
	/* <1>0000 */
	pspace.install_ram(0x10000, 0x13fff, 0, memptr + 0x8000);
	dspace.install_ram(0x10000, 0x13fff, 0, memptr + 0x14000);
	/* <1>4000 */
	pspace.install_ram(0x14000, 0x17fff, 0, memptr + 0xc000);
	dspace.install_ram(0x14000, 0x17fff, 0, memptr + 0x18000);
	/* <1>8000 */
	pspace.install_ram(0x18000, 0x1bfff, 0, memptr + 0x10000);
	dspace.install_ram(0x18000, 0x1bfff, 0, memptr + 0x1c000);
	/* <1>c000 empty*/
	/* <2>0000 */
	pspace.install_ram(0x20000, 0x23fff, 0, memptr + 0x14000);
	dspace.install_ram(0x20000, 0x23fff, 0, memptr + 0x14000);
	/* <2>4000 */
	pspace.install_ram(0x24000, 0x27fff, 0, memptr + 0x18000);
	dspace.install_ram(0x24000, 0x27fff, 0, memptr + 0x18000);
	/* <2>8000 */
	pspace.install_ram(0x28000, 0x2bfff, 0, memptr + 0x1c000);
	dspace.install_ram(0x28000, 0x2bfff, 0, memptr + 0x1c000);
	/* <2>c000 empty*/
	/* <3>0000 (video buffer) */
	pspace.install_ram(0x30000, 0x33fff, 0, memptr + 0x0000);
	dspace.install_ram(0x30000, 0x33fff, 0, memptr + 0x0000);


	/* <5>0000 */
	dspace.install_ram(0x50000, 0x53fff, 0, memptr + 0x8000);
	/* <5>4000 */
	dspace.install_ram(0x54000, 0x57fff, 0, memptr + 0xc000);
	/* <5>8000 */
	dspace.install_ram(0x58000, 0x5bfff, 0, memptr + 0x10000);
	/* <5>c000 expansion bus */
	/* <6>0000 */
	pspace.install_ram(0x60000, 0x63fff, 0, memptr + 0x8000);
	dspace.install_ram(0x60000, 0x63fff, 0, memptr + 0x8000);
	/* <6>4000 */
	pspace.install_ram(0x64000, 0x67fff, 0, memptr + 0xc000);
	dspace.install_ram(0x64000, 0x67fff, 0, memptr + 0xc000);
	/* <6>8000 */
	pspace.install_ram(0x68000, 0x6bfff, 0, memptr + 0x10000);
	dspace.install_ram(0x68000, 0x6bfff, 0, memptr + 0x10000);
	/* <6>c000 empty*/
	/* segment <7> expansion ROM? */
	/* <8>0000 */
	pspace.install_ram(0x80000, 0x83fff, 0, memptr + 0x8000);
	dspace.install_ram(0x80000, 0x83fff, 0, memptr + 0x18000);
	/* <8>4000 */
	pspace.install_ram(0x84000, 0x87fff, 0, memptr + 0xc000);
	dspace.install_ram(0x84000, 0x87fff, 0, memptr + 0x1c000);
	/* <9>0000 */
	pspace.install_ram(0x90000, 0x93fff, 0, memptr + 0x18000);
	dspace.install_ram(0x90000, 0x93fff, 0, memptr + 0x18000);
	/* <9>4000 */
	pspace.install_ram(0x94000, 0x97fff, 0, memptr + 0x1c000);
	dspace.install_ram(0x94000, 0x97fff, 0, memptr + 0x1c000);
	/* <A>0000 */
	pspace.install_ram(0xa0000, 0xa3fff, 0, memptr + 0x8000);
	dspace.install_ram(0xa0000, 0xa3fff, 0, memptr + 0x8000);
	/* <A>4000 */
	pspace.install_ram(0xa4000, 0xa7fff, 0, memptr + 0xc000);
	dspace.install_ram(0xa4000, 0xa7fff, 0, memptr + 0xc000);

	if (m_memsize > 128 * 1024) {
		/* install memory expansions (DRAM1..DRAM3) */

		if (m_memsize < 256 * 1024) {
			/* 32K expansion cards */

			/* DRAM1, 32K */

			/* prog
			   map( 0x2c000, 0x2ffff ).ram().share("dram1_0000");
			   map( 0x88000, 0x8bfff ).ram().share("dram1_4000");
			   map( 0xa8000, 0xabfff ).ram().share("dram1_4000");
			*/
			pspace.install_ram(0x2c000, 0x2ffff, 0, memptr + 0x20000);
			pspace.install_ram(0x88000, 0x8bfff, 0, memptr + 0x24000);
			pspace.install_ram(0xa8000, 0xabfff, 0, memptr + 0x24000);

			/*
			  data
			  map( 0x04000, 0x07fff ).ram().share("dram1_4000");
			  map( 0x1c000, 0x1ffff ).ram().share("dram1_0000");
			  map( 0x2c000, 0x2ffff ).ram().share("dram1_0000");
			  map( 0xa8000, 0xabfff ).ram().share("dram1_4000");
			*/
			dspace.install_ram(0x4000, 0x7fff, 0, memptr + 0x24000);
			dspace.install_ram(0x1c000, 0x1ffff, 0, memptr + 0x20000);
			dspace.install_ram(0x2c000, 0x2ffff, 0, memptr + 0x20000);
			dspace.install_ram(0xa8000, 0xabfff, 0, memptr + 0x24000);

			if (m_memsize > 128 * 1024 + 32768) {
				/* DRAM2, 32K */

				/* prog
				   map( 0x8c000, 0x8ffff ).ram().share("dram2_0000");
				   map( 0x98000, 0x9bfff ).ram().share("dram2_4000");
				   map( 0xac000, 0xaffff ).ram().share("dram2_0000");
				*/
				pspace.install_ram(0x8c000, 0x8ffff, 0, memptr + 0x28000);
				pspace.install_ram(0x98000, 0x9bfff, 0, memptr + 0x2c000);
				pspace.install_ram(0xac000, 0xaffff, 0, memptr + 0x28000);

				/* data
				   map( 0x08000, 0x0bfff ).ram().share("dram2_0000");
				   map( 0x0c000, 0x0ffff ).ram().share("dram2_4000");
				   map( 0x88000, 0x8bfff ).ram().share("dram2_4000");
				   map( 0x98000, 0x9bfff ).ram().share("dram2_4000");
				   map( 0xac000, 0xaffff ).ram().share("dram2_0000");
				 */
				dspace.install_ram(0x8000, 0xbfff, 0, memptr + 0x28000);
				dspace.install_ram(0xc000, 0xffff, 0, memptr + 0x2c000);
				dspace.install_ram(0x88000, 0x8bfff, 0, memptr + 0x2c000);
				dspace.install_ram(0x98000, 0x9bfff, 0, memptr + 0x2c000);
				dspace.install_ram(0xac000, 0xaffff, 0, memptr + 0x28000);
			}
			if (m_memsize > 128 * 1024 + 2 * 32768) {
				/* DRAM3, 32K */

				/* prog
				   map( 0x9c000, 0x9ffff ).ram().share("dram3_0000");
				   map( 0xb0000, 0xb3fff ).ram().share("dram3_4000");
				*/
				pspace.install_ram(0x9c000, 0x9ffff, 0, memptr + 0x30000);
				pspace.install_ram(0xb0000, 0xb3fff, 0, memptr + 0x34000);

				/* data
				   map( 0x44000, 0x47fff ).ram().share("dram3_0000");
				   map( 0x48000, 0x4bfff ).ram().share("dram3_4000");
				   map( 0x8c000, 0x8ffff ).ram().share("dram3_0000");
				   map( 0x9c000, 0x9ffff ).ram().share("dram3_0000");
				   map( 0xb0000, 0xb3fff ).ram().share("dram3_4000");
				   map( 0xc0000, 0xc3fff ).ram().share("dram3_4000");
				 */
				dspace.install_ram(0x44000, 0x47fff, 0, memptr + 0x30000);
				dspace.install_ram(0x48000, 0x4bfff, 0, memptr + 0x34000);
				dspace.install_ram(0x8c000, 0x8ffff, 0, memptr + 0x30000);
				dspace.install_ram(0x9c000, 0x9ffff, 0, memptr + 0x30000);
				dspace.install_ram(0xb0000, 0xb3fff, 0, memptr + 0x34000);
				dspace.install_ram(0xc0000, 0xc3fff, 0, memptr + 0x34000);
			}
		}
		else {
			/* 128K expansion cards */

			/* DRAM1, 128K */

			/* prog
			   map( 0x2c000, 0x2ffff ).ram().share("dram1_0000");
			   map( 0x88000, 0x8bfff ).ram().share("dram1_4000");
			   map( 0x8c000, 0x8ffff ).ram().share("dram1_8000");
			   map( 0x98000, 0x9bfff ).ram().share("dram1_c000");
			   map( 0x9c000, 0x9ffff ).ram().share("dram1_10000");
			   map( 0xa8000, 0xabfff ).ram().share("dram1_4000");
			   map( 0xac000, 0xaffff ).ram().share("dram1_8000");
			   map( 0xb0000, 0xb3fff ).ram().share("dram1_14000");
			   map( 0xb4000, 0xb7fff ).ram().share("dram1_18000");
			   map( 0xb8000, 0xbbfff ).ram().share("dram1_1c000");
			*/
			pspace.install_ram(0x2c000, 0x2ffff, 0, memptr + 0x20000);
			pspace.install_ram(0x88000, 0x8bfff, 0, memptr + 0x24000);
			pspace.install_ram(0x8c000, 0x8ffff, 0, memptr + 0x28000);
			pspace.install_ram(0x98000, 0x9bfff, 0, memptr + 0x2c000);
			pspace.install_ram(0x9c000, 0x9ffff, 0, memptr + 0x30000);
			pspace.install_ram(0xa8000, 0xabfff, 0, memptr + 0x24000);
			pspace.install_ram(0xac000, 0xaffff, 0, memptr + 0x28000);
			pspace.install_ram(0xb0000, 0xb3fff, 0, memptr + 0x34000);
			pspace.install_ram(0xb4000, 0xb7fff, 0, memptr + 0x38000);
			pspace.install_ram(0xb8000, 0xbbfff, 0, memptr + 0x3c000);

			/* data
			   map( 0x04000, 0x07fff ).ram().share("dram1_4000");
			   map( 0x1c000, 0x1ffff ).ram().share("dram1_0000");
			   map( 0x2c000, 0x2ffff ).ram().share("dram1_0000");
			   map( 0x88000, 0x8bfff ).ram().share("dram1_c000");
			   map( 0x8c000, 0x8ffff ).ram().share("dram1_10000");
			   map( 0x98000, 0x9bfff ).ram().share("dram1_c000");
			   map( 0x9c000, 0x9ffff ).ram().share("dram1_10000");
			   map( 0xa8000, 0xabfff ).ram().share("dram1_4000");
			   map( 0xac000, 0xaffff ).ram().share("dram1_8000");
			   map( 0xb0000, 0xb3fff ).ram().share("dram1_14000");
			   map( 0xb4000, 0xb7fff ).ram().share("dram1_18000");
			   map( 0xb8000, 0xbbfff ).ram().share("dram1_1c000");
			 */
			dspace.install_ram(0x4000, 0x7fff, 0, memptr + 0x24000);
			dspace.install_ram(0x1c000, 0x1ffff, 0, memptr + 0x20000);
			dspace.install_ram(0x2c000, 0x2ffff, 0, memptr + 0x20000);
			dspace.install_ram(0x88000, 0x8bfff, 0, memptr + 0x2c000);
			dspace.install_ram(0x8c000, 0x8ffff, 0, memptr + 0x30000);
			dspace.install_ram(0x98000, 0x9bfff, 0, memptr + 0x2c000);
			dspace.install_ram(0x9c000, 0x9ffff, 0, memptr + 0x30000);
			dspace.install_ram(0xa8000, 0xabfff, 0, memptr + 0x24000);
			dspace.install_ram(0xac000, 0xaffff, 0, memptr + 0x28000);
			dspace.install_ram(0xb0000, 0xb3fff, 0, memptr + 0x34000);
			dspace.install_ram(0xb4000, 0xb7fff, 0, memptr + 0x38000);
			dspace.install_ram(0xb8000, 0xbbfff, 0, memptr + 0x3c000);

			if (m_memsize > 256 * 1024) {
				/* DRAM2, 128K */

				/* prog
				   map( 0xbc000, 0xbffff ).ram().share("dram2_0000");

				   map( 0xc0000, 0xc3fff ).ram().share("dram2_4000");
				   map( 0xc4000, 0xc7fff ).ram().share("dram2_8000");
				   map( 0xc8000, 0xcbfff ).ram().share("dram2_c000");
				   map( 0xcc000, 0xcffff ).ram().share("dram2_10000");

				   map( 0xd0000, 0xd3fff ).ram().share("dram2_14000");
				   map( 0xd4000, 0xd7fff ).ram().share("dram2_18000");
				   map( 0xd8000, 0xdbfff ).ram().share("dram2_1c000");
				 */
				pspace.install_ram(0xbc000, 0xbffff, 0, memptr + 0x40000);
				pspace.install_ram(0xc0000, 0xc3fff, 0, memptr + 0x44000);
				pspace.install_ram(0xc4000, 0xc7fff, 0, memptr + 0x48000);
				pspace.install_ram(0xc8000, 0xcbfff, 0, memptr + 0x4c000);
				pspace.install_ram(0xcc000, 0xcffff, 0, memptr + 0x50000);
				pspace.install_ram(0xd0000, 0xd3fff, 0, memptr + 0x54000);
				pspace.install_ram(0xd4000, 0xd7fff, 0, memptr + 0x58000);
				pspace.install_ram(0xd8000, 0xdbfff, 0, memptr + 0x5c000);

				/* data
				   map( 0x08000, 0x0bfff ).ram().share("dram2_0000");
				   map( 0x0c000, 0x0ffff ).ram().share("dram2_4000");

				   map( 0xbc000, 0xbffff ).ram().share("dram2_0000");

				   map( 0xc0000, 0xc3fff ).ram().share("dram2_4000");
				   map( 0xc4000, 0xc7fff ).ram().share("dram2_8000");
				   map( 0xc8000, 0xcbfff ).ram().share("dram2_c000");
				   map( 0xcc000, 0xcffff ).ram().share("dram2_10000");

				   map( 0xd0000, 0xd3fff ).ram().share("dram2_14000");
				   map( 0xd4000, 0xd7fff ).ram().share("dram2_18000");
				   map( 0xd8000, 0xdbfff ).ram().share("dram2_1c000");
				*/
				dspace.install_ram(0x8000, 0xbfff, 0, memptr + 0x40000);
				dspace.install_ram(0xc000, 0xffff, 0, memptr + 0x44000);
				dspace.install_ram(0xbc000, 0xbffff, 0, memptr + 0x40000);
				dspace.install_ram(0xc0000, 0xc3fff, 0, memptr + 0x44000);
				dspace.install_ram(0xc4000, 0xc7fff, 0, memptr + 0x48000);
				dspace.install_ram(0xc8000, 0xcbfff, 0, memptr + 0x4c000);
				dspace.install_ram(0xcc000, 0xcffff, 0, memptr + 0x50000);
				dspace.install_ram(0xd0000, 0xd3fff, 0, memptr + 0x54000);
				dspace.install_ram(0xd4000, 0xd7fff, 0, memptr + 0x58000);
				dspace.install_ram(0xd8000, 0xdbfff, 0, memptr + 0x5c000);
			}
			if (m_memsize > 384 * 1024) {
				/* DRAM3, 128K */

				/* prog
				   map( 0xdc000, 0xdffff ).ram().share("dram3_0000");

				   map( 0xe0000, 0xe3fff ).ram().share("dram3_4000");
				   map( 0xe4000, 0xe7fff ).ram().share("dram3_8000");
				   map( 0xe8000, 0xebfff ).ram().share("dram3_c000");
				   map( 0xec000, 0xeffff ).ram().share("dram3_10000");

				   map( 0xf0000, 0xf3fff ).ram().share("dram3_14000");
				   map( 0xf4000, 0xf7fff ).ram().share("dram3_18000");
				   map( 0xf8000, 0xfbfff ).ram().share("dram3_1c000");
				   map( 0xfc000, 0xfffff ).ram().share("dram3_0000");
				*/
				pspace.install_ram(0xdc000, 0xdffff, 0, memptr + 0x60000);
				pspace.install_ram(0xe0000, 0xe3fff, 0, memptr + 0x64000);
				pspace.install_ram(0xe4000, 0xe7fff, 0, memptr + 0x68000);
				pspace.install_ram(0xe8000, 0xebfff, 0, memptr + 0x6c000);
				pspace.install_ram(0xec000, 0xeffff, 0, memptr + 0x70000);
				pspace.install_ram(0xf0000, 0xf3fff, 0, memptr + 0x74000);
				pspace.install_ram(0xf4000, 0xf7fff, 0, memptr + 0x78000);
				pspace.install_ram(0xf8000, 0xfbfff, 0, memptr + 0x7c000);
				pspace.install_ram(0xfc000, 0xfffff, 0, memptr + 0x60000);

				/* data
				   map( 0x44000, 0x47fff ).ram().share("dram3_0000");
				   map( 0x48000, 0x4bfff ).ram().share("dram3_4000");
				   map( 0xdc000, 0xdffff ).ram().share("dram3_0000");

				   map( 0xe0000, 0xe3fff ).ram().share("dram3_4000");
				   map( 0xe4000, 0xe7fff ).ram().share("dram3_8000");
				   map( 0xe8000, 0xebfff ).ram().share("dram3_c000");
				   map( 0xec000, 0xeffff ).ram().share("dram3_10000");

				   map( 0xf0000, 0xf3fff ).ram().share("dram3_14000");
				   map( 0xf4000, 0xf7fff ).ram().share("dram3_18000");
				   map( 0xf8000, 0xfbfff ).ram().share("dram3_1c000");
				   map( 0xfc000, 0xfffff ).ram().share("dram3_0000");
				*/
				dspace.install_ram(0x44000, 0x47fff, 0, memptr + 0x60000);
				dspace.install_ram(0x48000, 0x4bfff, 0, memptr + 0x64000);
				dspace.install_ram(0xdc000, 0xdffff, 0, memptr + 0x60000);
				dspace.install_ram(0xe0000, 0xe3fff, 0, memptr + 0x64000);
				dspace.install_ram(0xe4000, 0xe7fff, 0, memptr + 0x68000);
				dspace.install_ram(0xe8000, 0xebfff, 0, memptr + 0x6c000);
				dspace.install_ram(0xec000, 0xeffff, 0, memptr + 0x70000);
				dspace.install_ram(0xf0000, 0xf3fff, 0, memptr + 0x74000);
				dspace.install_ram(0xf4000, 0xf7fff, 0, memptr + 0x78000);
				dspace.install_ram(0xf8000, 0xfbfff, 0, memptr + 0x7c000);
				dspace.install_ram(0xfc000, 0xfffff, 0, memptr + 0x60000);
			}
		}
	}
}

void m20_state::m20_io(address_map &map)
{
	map.unmap_value_high();

	map(0x00, 0x07).rw(m_fd1797, FUNC(fd1797_device::read), FUNC(fd1797_device::write)).umask16(0x00ff);

	map(0x20, 0x21).rw(FUNC(m20_state::port21_r), FUNC(m20_state::port21_w));

	map(0x61, 0x61).w("crtc", FUNC(mc6845_device::address_w));
	map(0x62, 0x62).w("crtc", FUNC(mc6845_device::address_w)); // FIXME
	map(0x63, 0x63).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x64, 0x64).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));

	map(0x80, 0x87).rw(m_i8255, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);

	map(0xa0, 0xa3).rw(m_kbdi8251, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);

	map(0xc0, 0xc3).rw(m_ttyi8251, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);

	map(0x120, 0x127).rw("pit8253", FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);

	map(0x140, 0x143).rw(FUNC(m20_state::i8259_r), FUNC(m20_state::i8259_w));

	map(0x3ffa, 0x3ffd).w(m_apb, FUNC(m20_8086_device::handshake_w));
}

uint16_t m20_state::viack_r()
{
	return m_i8259->acknowledge()<<1;
}

uint16_t m20_state::nviack_r()
{
	m_maincpu->set_input_line(z8001_device::NVI_LINE, CLEAR_LINE);
	return 0xffff;
}

void m20_state::int_w(int state)
{
	if(m_apb && !m_apb->halted())
		m_apb->vi_w(state);
	m_maincpu->set_input_line(z8001_device::VI_LINE, state ? ASSERT_LINE : CLEAR_LINE);
}

void m20_state::machine_start()
{
	install_memory();
}

void m20_state::machine_reset()
{
	uint8_t *ROM = memregion("maincpu")->base();
	uint8_t *RAM = (uint8_t *)(m_ram->pointer() + 0x4000);

	if (m_memsize >= 256 * 1024)
		m_port21 = 0xdf;
	else
		m_port21 = 0xff;

	if(system_bios() > 0)  // bits have different meanings?
		m_port21 &= ~8;

	m_fd1797->reset();

	memcpy(RAM, ROM, 8);  // we need only the reset vector
	m_kbdi8251->write_cts(0);
	if (m_apb)
		m_apb->halt();
}


static void m20_floppies(device_slot_interface &device)
{
	device.option_add("5dd", FLOPPY_525_DD);
}

void m20_state::floppy_formats(format_registration &fr)
{
	fr.add_pc_formats();
	fr.add(FLOPPY_M20_FORMAT);
}

static void keyboard(device_slot_interface &device)
{
	device.option_add("m20", M20_KEYBOARD);
}

void m20_state::m20(machine_config &config)
{
	/* basic machine hardware */
	Z8001(config, m_maincpu, MAIN_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &m20_state::m20_program_mem);
	m_maincpu->set_addrmap(AS_DATA, &m20_state::m20_data_mem);
	m_maincpu->set_addrmap(AS_IO, &m20_state::m20_io);
	m_maincpu->viack().set(FUNC(m20_state::viack_r));
	m_maincpu->nviack().set(FUNC(m20_state::nviack_r));

	RAM(config, RAM_TAG).set_default_size("160K").set_default_value(0).set_extra_options("128K,192K,224K,256K,384K,512K");

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(512, 256);
	screen.set_visarea(0, 512-1, 0, 256-1);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	/* Devices */
	FD1797(config, m_fd1797, 1000000);
	m_fd1797->intrq_wr_callback().set(m_i8259, FUNC(pic8259_device::ir0_w));
	FLOPPY_CONNECTOR(config, "fd1797:0", m20_floppies, "5dd", m20_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "fd1797:1", m20_floppies, "5dd", m20_state::floppy_formats);

	mc6845_device &crtc(MC6845(config, "crtc", PIXEL_CLOCK/8)); /* hand tuned to get ~50 fps */
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(16);
	crtc.set_update_row_callback(FUNC(m20_state::update_row));

	I8255A(config, m_i8255, 0);

	I8251(config, m_kbdi8251, 0);
	m_kbdi8251->txd_handler().set("kbd", FUNC(rs232_port_device::write_txd));
	m_kbdi8251->rxrdy_handler().set(m_i8259, FUNC(pic8259_device::ir4_w));

	I8251(config, m_ttyi8251, 0);
	m_ttyi8251->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_ttyi8251->rxrdy_handler().set(m_i8259, FUNC(pic8259_device::ir3_w));
	m_ttyi8251->txrdy_handler().set(m_i8259, FUNC(pic8259_device::ir5_w));

	pit8253_device &pit8253(PIT8253(config, "pit8253", 0));
	pit8253.set_clk<0>(1230782);
	pit8253.out_handler<0>().set(FUNC(m20_state::tty_clock_tick_w));
	pit8253.set_clk<1>(1230782);
	pit8253.out_handler<1>().set(FUNC(m20_state::kbd_clock_tick_w));
	pit8253.set_clk<2>(1230782);
	pit8253.out_handler<2>().set(FUNC(m20_state::timer_tick_w));

	PIC8259(config, m_i8259, 0);
	m_i8259->out_int_callback().set(FUNC(m20_state::int_w));

	rs232_port_device &kbd(RS232_PORT(config, "kbd", keyboard, "m20"));
	kbd.rxd_handler().set(m_kbdi8251, FUNC(i8251_device::write_rxd));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_ttyi8251, FUNC(i8251_device::write_rxd));

	M20_8086(config, m_apb, m_maincpu, m_i8259, RAM_TAG);

	SOFTWARE_LIST(config, "flop_list").set_original("m20");
}

ROM_START(m20)
	ROM_REGION(0x2000,"maincpu", 0)
	ROM_SYSTEM_BIOS( 0, "m20", "M20 1.0" )
	ROMX_LOAD("m20.bin", 0x0000, 0x2000, CRC(5c93d931) SHA1(d51025e087a94c55529d7ee8fd18ff4c46d93230), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "m20-20d", "M20 2.0d" )
	ROMX_LOAD("m20-20d.bin", 0x0000, 0x2000, CRC(cbe265a6) SHA1(c7cb9d9900b7b5014fcf1ceb2e45a66a91c564d0), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "m20-20f", "M20 2.0f" )
	ROMX_LOAD("m20-20f.bin", 0x0000, 0x2000, CRC(db7198d8) SHA1(149d8513867081d31c73c2965dabb36d5f308041), ROM_BIOS(2))
ROM_END

ROM_START(m40)
	ROM_REGION(0x14000,"maincpu", 0)
	ROM_SYSTEM_BIOS( 0, "m40-81", "M40 15.dec.81" )
	ROMX_LOAD( "m40rom-15-dec-81", 0x0000, 0x2000, CRC(e8e7df84) SHA1(e86018043bf5a23ff63434f9beef7ce2972d8153), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "m40-82", "M40 17.dec.82" )
	ROMX_LOAD( "m40rom-17-dec-82", 0x0000, 0x2000, CRC(cf55681c) SHA1(fe4ae14a6751fef5d7bde49439286f1da3689437), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "m40-41", "M40 4.1" )
	ROMX_LOAD( "m40rom-4.1", 0x0000, 0x2000, CRC(cf55681c) SHA1(fe4ae14a6751fef5d7bde49439286f1da3689437), ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "m40-60", "M40 6.0" )
	ROMX_LOAD( "m40rom-6.0", 0x0000, 0x4000, CRC(8114ebec) SHA1(4e2c65b95718c77a87dbee0288f323bd1c8837a3), ROM_BIOS(3))

	ROM_REGION(0x4000, "apb_bios", ROMREGION_ERASEFF) // Processor board with 8086
ROM_END

// CPU board: Z8001BPS CPU, Z8010BPS MMU, 32MHz XTAL, P8253 PIT, MC68B50P ACIA, 1 4-dip bank, 512K RAM
// FDC board: D765AC-2 FDC, D8237AC-5 DMAC, GA04-CF11051, TMP8253P-5, 1 4-dip bank, 1 barely readable chip (AMI8520JFT or something resembling it)
// there are other undocumented PCBs. It uses 2x 8 inch floppy drives
ROM_START(m44) // TODO: implement different hardware. Split to another driver?
	ROM_REGION( 0x14000, "maincpu", 0 ) // 14 MAR. 86 REL B.1
	ROM_LOAD16_BYTE( "pd30.128.c06", 0x0000, 0x4000, CRC(8155dc69) SHA1(ed65f842e2857ad10170c697d945745fd7d47f9c) )
	ROM_LOAD16_BYTE( "pd29.128.a06", 0x0001, 0x4000, CRC(74d7de4b) SHA1(dd3a69ff29a2f1292f3a7db73bd2447bd664e54b) )

	ROM_REGION( 0x114, "plds", 0 )
	ROM_LOAD( "pl46.j09", 0x000, 0x114, NO_DUMP ) // PLD, chip type unknown
ROM_END

} // anonymous namespace


//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY     FULLNAME           FLAGS
COMP( 1981, m20,  0,      0,      m20,     0,     m20_state, empty_init, "Olivetti", "Olivetti L1 M20", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1981, m40,  m20,    0,      m20,     0,     m20_state, empty_init, "Olivetti", "Olivetti L1 M40", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1986, m44,  0,      0,      m20,     0,     m20_state, empty_init, "Olivetti", "Olivetti L1 M44", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



m24.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Carl
/****************************************************************************

    Olivetti M24 emulation

    http://olivettim24.hadesnet.org/index.html
    https://sites.google.com/site/att6300shrine/Home
    http://www.ti99.com/exelvision/website/index.php?page=logabax-persona-1600

    The AT&T PC6300, the Xerox 6060 and the Logabax Persona 1600 were
    badge-engineered Olivetti M24s.

    The Olivetti M21 was a portable version of the M24 that sported a 9"
    monochrome monitor.

    http://www.computinghistory.org.uk/det/43175/Olivetti-M21/
    https://www.nightfallcrew.com/23/02/2014/repairing-a-defective-olivetti-m21/

****************************************************************************/

#include "emu.h"

#include "bus/isa/isa.h"
#include "bus/isa/isa_cards.h"
#include "cpu/i86/i86.h"
#include "cpu/tms7000/tms7000.h"
#include "imagedev/floppy.h"
#include "machine/am9517a.h"
#include "machine/i8087.h"
#include "m24_kbd.h"
#include "m24_z8000.h"
#include "machine/mm58174.h"
#include "machine/pit8253.h"
#include "machine/pic8259.h"
#include "machine/ram.h"
#include "sound/spkrdev.h"
#include "speaker.h"

#include "formats/naslite_dsk.h"
#include "formats/m20_dsk.h"

#include "softlist_dev.h"


namespace {

class m24_state : public driver_device
{
public:
	m24_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ram(*this, RAM_TAG),
		m_isabus(*this, "isabus"),
		m_dmac(*this, "dmac"),
		m_pic(*this, "pic"),
		m_pit(*this, "pit"),
		m_speaker(*this, "speaker"),
		m_kbc(*this, "kbc"),
		m_keyboard(*this, "keyboard"),
		m_z8000_apb(*this, "z8000_apb"),
		m_dsw0(*this, "DSW0"),
		m_nmi_enable(false)
	{ }

	void olivetti(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void dma_segment_w(offs_t offset, u8 data);
	void dma_hrq_w(int state);
	u8 dma_memory_read(offs_t offset);
	void dma_memory_write(offs_t offset, u8 data);
	template <int Channel> u8 dma_io_read(offs_t offset);
	template <int Channel> void dma_io_write(offs_t offset, u8 data);
	template <int Channel> void dma_dack_w(int state);
	void dma_tc_w(int state);
	void dreq0_ck_w(int state);
	void speaker_ck_w(int state);
	void update_speaker();

	u8 keyboard_data_r();
	u8 keyboard_status_r();
	void keyboard_data_w(u8 data);

	void ctrlport_a_w(u8 data);
	u8 ctrlport_a_r();
	u8 ctrlport_b_r();

	void alt_w(u8 data);
	void chck_w(int state);
	void int87_w(int state);
	void nmi_enable_w(u8 data);
	void update_nmi();

	required_device<i8086_cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<isa8_device> m_isabus;
	required_device<am9517a_device> m_dmac;
	required_device<pic8259_device> m_pic;
	required_device<pit8253_device> m_pit;
	required_device<speaker_sound_device> m_speaker;
	required_device<tms7000_device> m_kbc;
	required_device<m24_keyboard_device> m_keyboard;
	optional_device<m24_z8000_device> m_z8000_apb;
	required_ioport m_dsw0;

	u8 m_dma_segment[4];
	u8 m_dma_active;
	bool m_tc;
	bool m_dreq0_ck;

	u8 m_ctrlport_a;
	u8 m_ctrlport_b;

	bool m_87int;
	bool m_chck_active;
	bool m_nmi_enable;

	u8 m_pa, m_kbcin, m_kbcout;
	bool m_kbcibf, m_kbdata, m_i86_halt, m_i86_halt_perm;

	u8 pa_r();
	void pb_w(u8 data);
	u8 kbcdata_r();
	void kbcdata_w(u8 data);
	void kbcin_w(int state);
	void int_w(int state);
	void halt_i86_w(int state);
	static void floppy_formats(format_registration &fr);

	static void cfg_m20_format(device_t *device);
	void kbc_map(address_map &map) ATTR_COLD;
	void m24_io(address_map &map) ATTR_COLD;
	void m24_map(address_map &map) ATTR_COLD;
};

void m24_state::machine_start()
{
	m_maincpu->space(AS_PROGRAM).install_ram(0, m_ram->size() - 1, m_ram->pointer());

	std::fill_n(&m_dma_segment[0], 4, 0);
	m_dma_active = 0;
	m_tc = false;
	m_dreq0_ck = true;

	m_ctrlport_a = 0;
	m_ctrlport_b = 0;

	m_87int = false;
	m_chck_active = false;
	m_nmi_enable = false;

	save_item(NAME(m_dma_segment));
	save_item(NAME(m_dma_active));
	save_item(NAME(m_tc));
	save_item(NAME(m_dreq0_ck));
	save_item(NAME(m_ctrlport_a));
	save_item(NAME(m_ctrlport_b));
	save_item(NAME(m_87int));
	save_item(NAME(m_chck_active));
	save_item(NAME(m_nmi_enable));
	save_item(NAME(m_pa));
	save_item(NAME(m_kbcin));
	save_item(NAME(m_kbcout));
	save_item(NAME(m_kbcibf));
	save_item(NAME(m_kbdata));
	save_item(NAME(m_i86_halt));
	save_item(NAME(m_i86_halt_perm));
}

void m24_state::machine_reset()
{
	ctrlport_a_w(0);
	nmi_enable_w(0);
	m_pa = 0x40;
	m_kbcibf = false;
	m_kbdata = true;
	m_i86_halt = false;
	m_i86_halt_perm = false;
	if(m_z8000_apb)
		m_z8000_apb->halt_w(ASSERT_LINE);
}

void m24_state::dma_segment_w(offs_t offset, u8 data)
{
	m_dma_segment[offset] = data & 0x0f;
}

void m24_state::dma_hrq_w(int state)
{
	if(!m_i86_halt)
		m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);
	if(m_z8000_apb && !m_z8000_apb->halted())
		m_z8000_apb->halt_w(state ? ASSERT_LINE : CLEAR_LINE);

	/* Assert HLDA */
	m_dmac->hack_w(state);
}

u8 m24_state::dma_memory_read(offs_t offset)
{
	const int seg = (BIT(m_dma_active, 2) ? 0 : 2) | (BIT(m_dma_active, 3) ? 0 : 1);
	return m_maincpu->space(AS_PROGRAM).read_byte(offset | u32(m_dma_segment[seg]) << 16);
}

void m24_state::dma_memory_write(offs_t offset, u8 data)
{
	const int seg = (BIT(m_dma_active, 2) ? 0 : 2) | (BIT(m_dma_active, 3) ? 0 : 1);
	m_maincpu->space(AS_PROGRAM).write_byte(offset | u32(m_dma_segment[seg]) << 16, data);
}

template <int Channel>
u8 m24_state::dma_io_read(offs_t offset)
{
	return m_isabus->dack_r(Channel);
}

template <int Channel>
void m24_state::dma_io_write(offs_t offset, u8 data)
{
	m_isabus->dack_w(Channel, data);
}

template <int Channel>
void m24_state::dma_dack_w(int state)
{
	m_isabus->dack_line_w(Channel, state);

	if (!state)
	{
		m_dma_active |= 1 << Channel;
		if (Channel == 0)
			m_dmac->dreq0_w(0);
		if (m_tc)
			m_isabus->eop_w(Channel, ASSERT_LINE);
	}
	else
	{
		m_dma_active &= ~(1 << Channel);
		if (m_tc)
			m_isabus->eop_w(Channel, CLEAR_LINE);
	}
}

void m24_state::dma_tc_w(int state)
{
	m_tc = (state == ASSERT_LINE);
	for (int channel = 0; channel < 4; channel++)
		if (BIT(m_dma_active, channel))
			m_isabus->eop_w(channel, state);
}

void m24_state::dreq0_ck_w(int state)
{
	if (state && !m_dreq0_ck && !BIT(m_dma_active, 0))
		m_dmac->dreq0_w(1);

	m_dreq0_ck = state;
}

void m24_state::speaker_ck_w(int state)
{
	if (state)
		m_ctrlport_b |= 0x20;
	else
		m_ctrlport_b &= 0xdf;

	update_speaker();
}

void m24_state::update_speaker()
{
	if (BIT(m_ctrlport_a, 1) && BIT(m_ctrlport_b, 5))
	{
		m_speaker->level_w(1);
		m_ctrlport_b &= 0xef;
	}
	else
	{
		m_speaker->level_w(0);
		m_ctrlport_b |= 0x10;
	}
}

u8 m24_state::keyboard_data_r()
{
	if (!machine().side_effects_disabled())
	{
		m_pa |= 0x40;
		m_pic->ir1_w(0);
	}
	return m_kbcout;
}

u8 m24_state::keyboard_status_r()
{
	return (m_kbcibf ? 2 : 0) | ((m_pa & 0x40) ? 0 : 1);
}

void m24_state::keyboard_data_w(u8 data)
{
	m_kbc->set_input_line(TMS7000_INT1_LINE, ASSERT_LINE);
	m_kbcibf = true;
	m_kbcin = data;
}

void m24_state::ctrlport_a_w(u8 data)
{
	const bool spkrdata_en_dis = BIT(data ^ m_ctrlport_a, 1);
	const bool iochk_en_dis = BIT(data ^ m_ctrlport_a, 4);

	m_pit->write_gate2(BIT(data, 0));

	if (BIT(m_ctrlport_a, 4) && !m_chck_active)
		m_ctrlport_b &= 0xbf;

	if (BIT(data, 6))
		m_pa |= 4;
	else
		m_pa &= ~4;

	m_ctrlport_a = data;

	if (spkrdata_en_dis)
		update_speaker();
	if (iochk_en_dis)
		update_nmi();
}

u8 m24_state::ctrlport_a_r()
{
	return m_ctrlport_a;
}

u8 m24_state::ctrlport_b_r()
{
	// Bit 0 = NC
	// Bit 1 = SW4 (8087 present)
	// Bit 2 = ~RI1
	// Bit 3 = ~DSR1
	// Bit 4 = SPKR
	// Bit 5 = OUT2 (8253)
	// Bit 6 = IOCHK
	// Bit 7 = MBMERR (MRD parity check)

	if (BIT(m_dsw0->read(), 4))
		m_ctrlport_b |= 0x02;
	else
		m_ctrlport_b &= 0xfd;

	return m_ctrlport_b;
}

void m24_state::alt_w(u8 data)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, (data & 0x40) ? ASSERT_LINE : CLEAR_LINE);
	m_i86_halt = true;
	m_i86_halt_perm = true;
}

void m24_state::chck_w(int state)
{
	m_chck_active = (state == 0);
	if (m_chck_active)
	{
		if (!BIT(m_ctrlport_b, 6))
		{
			m_ctrlport_b |= 0x40;
			update_nmi();
		}
	}
	else if (BIT(m_ctrlport_a, 4))
		m_ctrlport_b &= 0xbf;
}

void m24_state::int87_w(int state)
{
	m_87int = state;
	update_nmi();
}

void m24_state::nmi_enable_w(u8 data)
{
	m_nmi_enable = BIT(data, 7);
	update_nmi();
}

void m24_state::update_nmi()
{
	if (m_nmi_enable && ((m_87int && BIT(m_dsw0->read(), 4)) || (BIT(m_ctrlport_b, 6) && !BIT(m_ctrlport_a, 4))))
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
	else
		m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
}

u8 m24_state::pa_r()
{
	return m_pa & (m_kbdata ? 0xff : 0xfd);
}

void m24_state::pb_w(u8 data)
{
	m_keyboard->clock_w(!BIT(data, 0));
	m_keyboard->data_w(!BIT(data, 1));
	m_pa = (m_pa & ~3) | (~data & 3);
}

u8 m24_state::kbcdata_r()
{
	m_kbc->set_input_line(TMS7000_INT1_LINE, CLEAR_LINE);
	m_kbcibf = false;
	return m_kbcin;
}

void m24_state::kbcdata_w(u8 data)
{
	m_pa &= ~0x40;
	m_pic->ir1_w(1);
	m_kbcout = data;
}

void m24_state::kbcin_w(int state)
{
	m_kbdata = state;
}

void m24_state::int_w(int state)
{
	if(!m_i86_halt)
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, state ? ASSERT_LINE : CLEAR_LINE);
	if(m_z8000_apb && !m_z8000_apb->halted())
		m_z8000_apb->int_w(state ? ASSERT_LINE : CLEAR_LINE);
}

void m24_state::halt_i86_w(int state)
{
	if(m_i86_halt_perm)
		return;
	m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);
	m_i86_halt = state ? true : false;
}

void m24_state::m24_map(address_map &map)
{
	map.unmap_value_high();
	map(0xf8000, 0xfffff).rom().region("bios", 0);
}

void m24_state::m24_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x000f).rw(m_dmac, FUNC(am9517a_device::read), FUNC(am9517a_device::write));
	map(0x0020, 0x0021).mirror(0xe).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x0040, 0x0043).mirror(0xc).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x0060, 0x0060).rw(FUNC(m24_state::keyboard_data_r), FUNC(m24_state::keyboard_data_w));
	map(0x0061, 0x0061).rw(FUNC(m24_state::ctrlport_a_r), FUNC(m24_state::ctrlport_a_w));
	map(0x0062, 0x0062).r(FUNC(m24_state::ctrlport_b_r));
	map(0x0064, 0x0064).r(FUNC(m24_state::keyboard_status_r));
	map(0x0065, 0x0065).w(FUNC(m24_state::alt_w));
	map(0x0066, 0x0067).portr("DSW0");
	map(0x0070, 0x007f).rw("mm58174an", FUNC(mm58174_device::read), FUNC(mm58174_device::write));
	map(0x0080, 0x0083).mirror(0xc).w(FUNC(m24_state::dma_segment_w));
	map(0x00a0, 0x00a1).mirror(0xe).w(FUNC(m24_state::nmi_enable_w));
	map(0x80c1, 0x80c1).rw(m_z8000_apb, FUNC(m24_z8000_device::handshake_r), FUNC(m24_z8000_device::handshake_w));
}

void m24_state::kbc_map(address_map &map)
{
	map(0x8000, 0x8fff).r(FUNC(m24_state::kbcdata_r));
	map(0xa000, 0xafff).w(FUNC(m24_state::kbcdata_w));
	map(0xf800, 0xffff).rom().region("kbc", 0);
}

static INPUT_PORTS_START( m24 )
	PORT_START("DSW0")
	PORT_DIPNAME( 0x8f, 0x89, "RAM banks")
	PORT_DIPSETTING(    0x01, "128K" )
	PORT_DIPSETTING(    0x82, "256K" )
	PORT_DIPSETTING(    0x84, "512K - 256/256" )
	PORT_DIPSETTING(    0x08, "512K - 512/0" )
	PORT_DIPSETTING(    0x85, "640K - 256/384" )
	PORT_DIPSETTING(    0x8d, "640K - 128/512" )
	PORT_DIPSETTING(    0x89, "640K - 512/128" )
	PORT_DIPNAME( 0x10, 0x00, "8087 installed")
	PORT_DIPSETTING(    0x00, DEF_STR(No) )
	PORT_DIPSETTING(    0x10, DEF_STR(Yes) )
	PORT_DIPNAME( 0x20, 0x00, "Serial Port")
	PORT_DIPSETTING(    0x20, "Z8530 SCC")
	PORT_DIPSETTING(    0x00, "INS8250" )

	//PORT_START("DSW1")
	PORT_DIPNAME( 0x0100, 0x0000, "FDD Type")
	PORT_DIPSETTING(    0x0000, "360K" )
	PORT_DIPSETTING(    0x0100, "720K" )
	PORT_DIPNAME( 0x0200, 0x0200, "FDD spinup")
	PORT_DIPSETTING(    0x0000, "Slow" )
	PORT_DIPSETTING(    0x0200, "Fast" )
	PORT_DIPNAME( 0x0400, 0x0400, "HDD ROM")
	PORT_DIPSETTING(    0x0000, "Internal" )
	PORT_DIPSETTING(    0x0400, "External" )
	PORT_DIPNAME( 0x0800, 0x0000, "Scroll rate")
	PORT_DIPSETTING(    0x0800, "Slow" )
	PORT_DIPSETTING(    0x0000, "Fast")
	PORT_DIPNAME( 0x3000, 0x2000, "Graphics adapter")
	PORT_DIPSETTING(    0x0000, "EGA/VGA" )
	PORT_DIPSETTING(    0x1000, "Color 40x25" )
	PORT_DIPSETTING(    0x2000, "Color 80x25" )
	PORT_DIPSETTING(    0x3000, "Monochrome" )
	PORT_DIPNAME( 0xc000, 0x4000, "Number of floppy drives")
	PORT_DIPSETTING(    0x0000, "1" )
	PORT_DIPSETTING(    0x4000, "2" )
	PORT_DIPSETTING(    0x8000, "3" )
	PORT_DIPSETTING(    0xc000, "4" )
INPUT_PORTS_END

void m24_state::floppy_formats(format_registration &fr)
{
	fr.add_pc_formats();
	fr.add(FLOPPY_NASLITE_FORMAT);
	fr.add(FLOPPY_M20_FORMAT);
}

void m24_state::cfg_m20_format(device_t *device)
{
	device->subdevice<floppy_connector>("fdc:0")->set_formats(m24_state::floppy_formats);
	device->subdevice<floppy_connector>("fdc:1")->set_formats(m24_state::floppy_formats);
}

void m24_state::olivetti(machine_config &config)
{
	/* basic machine hardware */
	I8086(config, m_maincpu, 24_MHz_XTAL / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &m24_state::m24_map);
	m_maincpu->set_addrmap(AS_IO, &m24_state::m24_io);
	m_maincpu->set_irq_acknowledge_callback("pic", FUNC(pic8259_device::inta_cb));
	m_maincpu->esc_opcode_handler().set("ndp", FUNC(i8087_device::insn_w));
	m_maincpu->esc_data_handler().set("ndp", FUNC(i8087_device::addr_w));

	i8087_device &i8087(I8087(config, "ndp", 24_MHz_XTAL / 3));
	i8087.set_space_86(m_maincpu, AS_PROGRAM);
	i8087.irq().set(FUNC(m24_state::int87_w));
	i8087.busy().set_inputline(m_maincpu, INPUT_LINE_TEST);

	AM9517A(config, m_dmac, 24_MHz_XTAL / 6); // 8237A-4
	m_dmac->out_hreq_callback().set(FUNC(m24_state::dma_hrq_w));
	m_dmac->in_memr_callback().set(FUNC(m24_state::dma_memory_read));
	m_dmac->out_memw_callback().set(FUNC(m24_state::dma_memory_write));
	m_dmac->in_ior_callback<1>().set(FUNC(m24_state::dma_io_read<1>));
	m_dmac->in_ior_callback<2>().set(FUNC(m24_state::dma_io_read<2>));
	m_dmac->in_ior_callback<3>().set(FUNC(m24_state::dma_io_read<3>));
	m_dmac->out_iow_callback<1>().set(FUNC(m24_state::dma_io_write<1>));
	m_dmac->out_iow_callback<2>().set(FUNC(m24_state::dma_io_write<2>));
	m_dmac->out_iow_callback<3>().set(FUNC(m24_state::dma_io_write<3>));
	m_dmac->out_dack_callback<0>().set(FUNC(m24_state::dma_dack_w<0>));
	m_dmac->out_dack_callback<1>().set(FUNC(m24_state::dma_dack_w<1>));
	m_dmac->out_dack_callback<2>().set(FUNC(m24_state::dma_dack_w<2>));
	m_dmac->out_dack_callback<3>().set(FUNC(m24_state::dma_dack_w<3>));
	m_dmac->out_eop_callback().set(FUNC(m24_state::dma_tc_w));

	PIC8259(config, m_pic);
	m_pic->in_sp_callback().set_constant(1);
	m_pic->out_int_callback().set(FUNC(m24_state::int_w));

	PIT8253(config, m_pit); // 8253-5
	m_pit->set_clk<0>(3.6864_MHz_XTAL / 3); // divided by LS175 at 8T
	m_pit->set_clk<1>(3.6864_MHz_XTAL / 3);
	m_pit->set_clk<2>(3.6864_MHz_XTAL / 3);
	m_pit->out_handler<0>().set(m_pic, FUNC(pic8259_device::ir0_w));
	m_pit->out_handler<1>().set(FUNC(m24_state::dreq0_ck_w));
	m_pit->out_handler<2>().set(FUNC(m24_state::speaker_ck_w));

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);

	ISA8(config, m_isabus, 24_MHz_XTAL / 6);
	m_isabus->set_memspace(m_maincpu, AS_PROGRAM);
	m_isabus->set_iospace(m_maincpu, AS_IO);
	m_isabus->irq2_callback().set(m_pic, FUNC(pic8259_device::ir2_w));
	m_isabus->irq3_callback().set(m_pic, FUNC(pic8259_device::ir3_w));
	m_isabus->irq4_callback().set(m_pic, FUNC(pic8259_device::ir4_w));
	m_isabus->irq5_callback().set(m_pic, FUNC(pic8259_device::ir5_w));
	m_isabus->irq6_callback().set(m_pic, FUNC(pic8259_device::ir6_w));
	m_isabus->irq7_callback().set(m_pic, FUNC(pic8259_device::ir7_w));
	m_isabus->drq1_callback().set(m_dmac, FUNC(am9517a_device::dreq1_w));
	m_isabus->drq2_callback().set(m_dmac, FUNC(am9517a_device::dreq2_w));
	m_isabus->drq3_callback().set(m_dmac, FUNC(am9517a_device::dreq3_w));
	m_isabus->iochck_callback().set(FUNC(m24_state::chck_w));

	ISA8_SLOT(config, "mb1", 0, m_isabus, pc_isa8_cards, "cga_m24", true);
	ISA8_SLOT(config, "mb2", 0, m_isabus, pc_isa8_cards, "fdc_xt", true).set_option_machine_config("fdc_xt", cfg_m20_format);
	ISA8_SLOT(config, "mb3", 0, m_isabus, pc_isa8_cards, "lpt", true);
	ISA8_SLOT(config, "mb4", 0, m_isabus, pc_isa8_cards, "com", true);

	ISA8_SLOT(config, "isa1", 0, m_isabus, pc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa2", 0, m_isabus, pc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa3", 0, m_isabus, pc_isa8_cards, nullptr, false);

	// 2 banks of 16 64Kx1 or 256Kx1 DRAMs on motherboard
	RAM(config, m_ram).set_default_size("640K").set_extra_options("128K, 256K, 512K");

	TMS7000(config, m_kbc, 24_MHz_XTAL / 6);
	m_kbc->set_addrmap(AS_PROGRAM, &m24_state::kbc_map);
	m_kbc->in_porta().set(FUNC(m24_state::pa_r));
	m_kbc->out_portb().set(FUNC(m24_state::pb_w));

	M24_KEYBOARD(config, m_keyboard, 0);
	m_keyboard->out_data_handler().set(FUNC(m24_state::kbcin_w));

	MM58174(config, "mm58174an", 32.768_kHz_XTAL);

	M24_Z8000(config, m_z8000_apb, 0); // TODO: make this a slot device (uses custom bus connector)
	m_z8000_apb->halt_callback().set(FUNC(m24_state::halt_i86_w));

	/* software lists */
	SOFTWARE_LIST(config, "disk_list").set_original("ibm5150");
	SOFTWARE_LIST(config, "m24_disk_list").set_original("m24");
}

ROM_START( m21 )
	ROM_REGION16_LE(0x8000,"bios", 0)
	ROMX_LOAD( "bios_m24_144_even.bin", 0x4000, 0x2000, CRC(5f3d7084) SHA1(d55c0d8472b45e4c4ca9cb0066cd5c122056ba8e), ROM_SKIP(1))
	ROMX_LOAD( "bios_m24_144_odd.bin", 0x4001, 0x2000, CRC(18fd8db8) SHA1(f2c9d189f7ded88946a99432abd7106d509a7411), ROM_SKIP(1))

	ROM_REGION(0x800, "kbc", 0)
	ROM_LOAD("pdbd.tms2516.kbdmcu_replacement_board.10u", 0x000, 0x800, CRC(b8c4c18a) SHA1(25b4c24e19ff91924c53557c66513ab242d926c6))
ROM_END

ROM_START( m24 )
	ROM_REGION16_LE(0x8000,"bios", 0)
	ROM_SYSTEM_BIOS(0,"v1.1","v1.1")
	ROMX_LOAD("m24_bios11h.rom",0x4001, 0x2000, CRC(f08e859a) SHA1(c2ede7ce4472c77462d1d841e2b47e8b306c563d), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("m24_bios11l.rom", 0x4000, 0x2000, CRC(ec494e66) SHA1(51259cf9fd9f6a6855d52730206ff66ad3367ea4), ROM_SKIP(1) | ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1,"v1.21","v1.21")
	ROMX_LOAD("m24_bios121h.rom",0x4001, 0x2000, CRC(93292715) SHA1(863eccfb3beca6e64c5b0cc070c64394bad7da82), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("m24_bios121l.rom", 0x4000, 0x2000, CRC(1acbc9d7) SHA1(d3696e38853cea31e70ffa4e13e127ec7551bf57), ROM_SKIP(1) | ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2,"v1.36","v1.36")
	ROMX_LOAD("m24_bios136h.rom",0x4001, 0x2000, CRC(25cbf8ba) SHA1(1ab90985852544d2c12b47bb7f20f9faccabdf88), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD("m24_bios136l.rom", 0x4000, 0x2000, CRC(e2f738c0) SHA1(da9771325a5021cf9908997e0e0d14e47258125f), ROM_SKIP(1) | ROM_BIOS(2))

	ROM_SYSTEM_BIOS(3,"v1.43","v1.43")
	ROMX_LOAD("olivetti_m24_version_1.43_high.bin",0x4001, 0x2000, CRC(04e697ba) SHA1(1066dcc849e6289b5ac6372c84a590e456d497a6), ROM_SKIP(1) | ROM_BIOS(3))
	ROMX_LOAD("olivetti_m24_version_1.43_low.bin", 0x4000, 0x2000, CRC(ff7e0f10) SHA1(13423011a9bae3f3193e8c199f98a496cab48c0f), ROM_SKIP(1) | ROM_BIOS(3))

	ROM_REGION(0x800, "kbc", 0)
	ROM_LOAD("pdbd.tms2516.kbdmcu_replacement_board.10u", 0x000, 0x800, CRC(b8c4c18a) SHA1(25b4c24e19ff91924c53557c66513ab242d926c6))
ROM_END

ROM_START( m240 )
	ROM_REGION16_LE(0x8000,"bios", 0)
	ROMX_LOAD("olivetti_m240_pch5_2.04_high.bin", 0x0001, 0x4000, CRC(ceb97b59) SHA1(84fabbeab355e0a4c9445910f2b7d1ec98886642), ROM_SKIP(1))
	ROMX_LOAD("olivetti_m240_pch6_2.04_low.bin",  0x0000, 0x4000, CRC(c463aa94) SHA1(a30c763c1ace9f3ff79e7136b252d624108a50ae), ROM_SKIP(1))

	// is this one the same?
	ROM_REGION(0x800, "kbc", 0)
	ROM_LOAD("pdbd.tms2516.kbdmcu_replacement_board.10u", 0x000, 0x800, BAD_DUMP CRC(b8c4c18a) SHA1(25b4c24e19ff91924c53557c66513ab242d926c6))
ROM_END

} // Anonymous namespace


COMP( 1984, m21,  ibm5150, 0, olivetti, m24, m24_state, empty_init, "Olivetti", "M21",  MACHINE_NOT_WORKING )
COMP( 1983, m24,  ibm5150, 0, olivetti, m24, m24_state, empty_init, "Olivetti", "M24",  MACHINE_NOT_WORKING )
COMP( 1987, m240, ibm5150, 0, olivetti, m24, m24_state, empty_init, "Olivetti", "M240", MACHINE_NOT_WORKING )



m3.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Robbbert, Dirk Best
/***************************************************************************

    LSI M-THREE

    Models:
    - M-THREE/100 (SA400, 5.25" single sided)
    - M-THREE/110 (SA410, 5.25" single sided)
    - M-THREE/150 (SA450, 5.25" double sided)
    - M-THREE/160 (SA460, 5.25" double sided)
    - M-THREE/200 (SA800, 8" single sided)
    - M-THREE/250 (SA850, 8" double sided) [emulated by default]
    - M-THREE/320 (SA800, 8" single sided, Winchester 5 MB)
    - M-THREE/325 (SA850, 8" double sided, Winchester 5 MB)
    - M-THREE/340 (SA800, 8" single sided, Winchester 10 MB)
    - M-THREE/345 (SA850, 8" double sided, Winchester 10 MB)

    Hardware:
    - MK3880N-IRL (Z80)
    - 64 KB RAM
    - Z80 CTC
    - 2x D8255AC PPI
    - D8251A
    - MC6845P CRTC
    - FD1793 FDC
    - XTAL X1 21.??? Mhz (unreadable)
    - XTAL X2 16 MHz
    - XTAL X3 4.9152 MHz
    - Keyboard XTAL 6.1?? MHz (assumed to be 6.144 MHz)

    TODO:
    - Initial PC is currently hacked to f000
    - Verify/fix floppy hookup
    - Printer interface
    - Buzzer
    - Map the rest of the keys, verify existing keys
    - Switch FDC to 1 MHz for 5.25" drives

    Notes:
    - Y to boot from floppy, ESC to enter monitor, any other key to
      boot from IDE

***************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/mcs48/mcs48.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/wd_fdc.h"
#include "machine/z80ctc.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"


namespace {


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class m3_state : public driver_device
{
public:
	m3_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ctc(*this, "ctc"),
		m_ppi(*this, "ppi%u", 0U),
		m_chargen(*this, "chargen"),
		m_vram(*this, "vram"),
		m_palette(*this, "palette"),
		m_fdc(*this, "fdc"),
		m_floppy(*this, "fdc:%u", 0U),
		m_kbdmcu(*this, "kbdmcu"),
		m_special(*this, "SPECIAL"),
		m_keys(*this, "K%02u", 0U)
	{ }

	void m3(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;
	required_device<z80ctc_device> m_ctc;
	required_device_array<i8255_device, 2> m_ppi;
	required_region_ptr<uint8_t> m_chargen;
	required_shared_ptr<uint8_t> m_vram;
	required_device<palette_device> m_palette;
	required_device<fd1793_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<i8035_device> m_kbdmcu;
	required_ioport m_special;
	required_ioport_array<16> m_keys;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void kbd_mem_map(address_map &map) ATTR_COLD;
	void kbd_io_map(address_map &map) ATTR_COLD;

	int kbd_t0_r();
	int kbd_t1_r();
	uint8_t kbd_p1_r();
	void kbd_p1_w(uint8_t data);
	void kbd_p2_w(uint8_t data);
	void kbd_data_w(uint8_t data);
	int m_kbd_col = 0;
	int m_kbd_row = 0;
	uint8_t m_kbd_data = 0U;

	void ppi2_pa_w(uint8_t data);
	uint8_t ppi2_pb_r();

	MC6845_UPDATE_ROW(crtc_update_row);

	void fdc_intrq_w(int state);
	void fdc_drq_w(int state);
	uint8_t fdc_data_r(offs_t offset);
	void fdc_data_w(offs_t offset, uint8_t data);

	bool m_nmi_enabled = false;
	bool m_nmi_taken = false;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void m3_state::mem_map(address_map &map)
{
	map(0x0000, 0xe7ff).ram();
	map(0xe800, 0xefff).ram().share("vram");
	map(0xf000, 0xffff).rom().region("maincpu", 0);
}

void m3_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x80, 0x83).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x84, 0x84).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x85, 0x85).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x88, 0x88).rw(m_fdc, FUNC(fd1793_device::status_r), FUNC(fd1793_device::cmd_w));
	map(0x89, 0x89).rw(m_fdc, FUNC(fd1793_device::track_r), FUNC(fd1793_device::track_w));
	map(0x8a, 0x8a).rw(m_fdc, FUNC(fd1793_device::sector_r), FUNC(fd1793_device::sector_w));
	map(0x8b, 0x8b).rw(FUNC(m3_state::fdc_data_r), FUNC(m3_state::fdc_data_w));
	map(0x8c, 0x8d).rw("usart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x90, 0x93).rw(m_ppi[0], FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x94, 0x97).rw(m_ppi[1], FUNC(i8255_device::read), FUNC(i8255_device::write));
}

void m3_state::kbd_mem_map(address_map &map)
{
	map(0x000, 0x7ff).rom().mirror(0x800).region("keyboard", 0);
}

void m3_state::kbd_io_map(address_map &map)
{
	map(0x30, 0x30).mirror(0xf).w(FUNC(m3_state::kbd_data_w));
}


//**************************************************************************
//  INPUT DEFINITIONS
//**************************************************************************

// unmapped keys:
//
// INIT PROG F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14
// INS_CHAR DEL_CHAR INS_LINE DEL_LINE CLEAR_FORE CLEAR_PAGE PROT_MODE BLOCK_MODE
// SEND_LINE SEND_FORE SEND_PAGE PAGE_FWD UP PAGE_BACK LEFT HOME RIGHT
// BREAK SET_TAB DOWN CLEAR_TAB

static INPUT_PORTS_START( m3 )
	PORT_START("SPECIAL")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) // or shift-lock?
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)   PORT_CODE(KEYCODE_RSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT) PORT_NAME("Repeat")

	PORT_START("K00")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)     PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)     PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(": (?)")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("b4")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("b3")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)     PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("85")

	PORT_START("K01")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)     PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)     PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0f")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("b2")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)     PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("86")

	PORT_START("K02")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)     PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("05")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("96")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)     PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("89")

	PORT_START("K03")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)     PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 (?)")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("95")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8a")

	PORT_START("K04")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)     PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("11")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("94")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8b")

	PORT_START("K05")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("cd")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)     PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("93")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|') // actually ¦
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8c")

	PORT_START("K06")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)     PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("92")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)   PORT_CHAR(9)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8d")

	PORT_START("K07")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)  PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)     PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('_')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("91")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR('\r')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8e")

	PORT_START("K08")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)     PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)   PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("90")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("bc")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LF")          PORT_CHAR('\n')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8f")

	PORT_START("K09")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)     PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)     PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("cd")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("b1")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("b0")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)     PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("87")

	PORT_START("K10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)     PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)     PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)     PORT_CHAR('0')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("97")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)     PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("88")

	PORT_START("K11")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)     PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)     PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("03")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("b5")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)     PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("84")

	PORT_START("K12")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)     PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)     PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("cd")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("b7")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("b6")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)     PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("83")

	PORT_START("K13")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)     PORT_CHAR('3') PORT_CHAR(0xa3) // £
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)     PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ea")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("b8")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)     PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("82")

	PORT_START("K14")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)     PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)     PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("02")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ba")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("b9")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)     PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("81")

	PORT_START("K15")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)     PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)     PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("c3")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("bb")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA_PAD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)   PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)     PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("80")
INPUT_PORTS_END

int m3_state::kbd_t0_r()
{
	// outputs 26 alternating 0 and 1 to i/o port 0x00 when active
	return 1;
}

int m3_state::kbd_t1_r()
{
	// checked right before outputing a new keycode
	return 0;
}

uint8_t m3_state::kbd_p1_r()
{
	uint8_t data = 0;

	// ---4----  key down
	// ----3---  repeat
	// -----2--  control
	// ------1-  shift
	// -------0  caps-lock, shift-lock?

	data |= BIT(m_keys[m_kbd_col]->read(), m_kbd_row) ? 0x00 : 0x10;
	data |= m_special->read();

	return data;
}

void m3_state::kbd_p1_w(uint8_t data)
{
	// 765-----  row select

	m_kbd_row = data >> 5;
}

void m3_state::kbd_p2_w(uint8_t data)
{
	// 7654----  column select
	// ----3210  unused

	m_kbd_col = data >> 4;
}

void m3_state::kbd_data_w(uint8_t data)
{
	m_kbd_data = data;

	m_ppi[1]->pc2_w(0);
	m_ppi[1]->pc2_w(1);
}


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

MC6845_UPDATE_ROW( m3_state::crtc_update_row )
{
	rgb_t const *const pen = m_palette->palette()->entry_list_raw();

	for (uint16_t x = 0; x < x_count; x++)
	{
		uint16_t mem = (ma + x) & 0x7ff;
		uint8_t chr = m_vram[mem];
		uint8_t gfx = m_chargen[((chr << 4) | ra) & 0x7ff];

		// invert?
		if (BIT(chr, 7))
			gfx ^= 0xff;

		// cursor?
		if (x == cursor_x)
			gfx ^= 0xff;

		// draw 7 pixels of the character
		for (int i = 0; i < 7; i++)
			bitmap.pix(y, x * 7 + i) = pen[BIT(gfx, 6 - i)];
	}
}

static const gfx_layout charlayout =
{
	7, 10,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8 },
	8*16
};

static GFXDECODE_START(chars)
	GFXDECODE_ENTRY("chargen", 0, charlayout, 0, 1)
GFXDECODE_END


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void m3_state::fdc_intrq_w(int state)
{
	if (state)
	{
		m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, CLEAR_LINE);
		m_nmi_taken = false;
	}

	m_ctc->trg1(state);
}

void m3_state::fdc_drq_w(int state)
{
	if (state)
	{
		m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, CLEAR_LINE);

		if (m_nmi_enabled)
		{
			m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);

			m_nmi_enabled = false;
			m_nmi_taken = true;
		}
	}
}

uint8_t m3_state::fdc_data_r(offs_t offset)
{
	if (m_nmi_taken && m_fdc->drq_r() == 0)
	{
		m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE);
		m_maincpu->retry_access();

		return 0;
	}

	return m_fdc->data_r();
}

void m3_state::fdc_data_w(offs_t offset, uint8_t data)
{
	if (m_nmi_taken && m_fdc->drq_r() == 0)
	{
		m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE);
		m_maincpu->retry_access();
	}

	m_fdc->data_w(data);
}

void m3_state::ppi2_pa_w(uint8_t data)
{
	floppy_image_device *floppy = nullptr;

	// 7-------  not used?
	// -6------  buzzer
	// --5-----  not used?
	// ---4----  nmi enable
	// ----3---  unknown
	// -----2--  floppy side
	// ------10  drive select

	logerror("ppi2_pa_w: %02x\n", data);

	if (!BIT(data, 0) && m_floppy[0])
		floppy = m_floppy[0]->get_device();
	else if (!BIT(data, 1) && m_floppy[1])
		floppy = m_floppy[1]->get_device();

	m_fdc->set_floppy(floppy);

	if (floppy)
		floppy->ss_w(BIT(data, 2));

	m_nmi_enabled = bool(BIT(~data, 4));
}

uint8_t m3_state::ppi2_pb_r()
{
	return m_kbd_data;
}

void m3_state::machine_start()
{
	// register for save states
	save_item(NAME(m_kbd_col));
	save_item(NAME(m_kbd_row));
	save_item(NAME(m_kbd_data));
	save_item(NAME(m_nmi_enabled));
	save_item(NAME(m_nmi_taken));
}

void m3_state::machine_reset()
{
	m_maincpu->set_pc(0xf000);

	m_nmi_enabled = false;
	m_nmi_taken = false;
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

static const z80_daisy_config daisy_chain[] =
{
	{ "ctc" },
	{ nullptr }
};

static DEVICE_INPUT_DEFAULTS_START( rs232_printer_defaults )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

static void m3_floppies(device_slot_interface &device)
{
	device.option_add("sa400", FLOPPY_525_SSSD_35T);
	device.option_add("sa410", FLOPPY_525_SSQD);
	device.option_add("sa450", FLOPPY_525_DD);
	device.option_add("sa460", FLOPPY_525_QD);
	device.option_add("sa800", FLOPPY_8_SSDD);
	device.option_add("sa850", FLOPPY_8_DSDD);
}

void m3_state::m3(machine_config &config)
{
	Z80(config, m_maincpu, 4.9152_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &m3_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &m3_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	Z80CTC(config, m_ctc, 0); // unknown clock
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->set_clk<0>(4.9152_MHz_XTAL / 4);
	m_ctc->zc_callback<0>().set("usart", FUNC(i8251_device::write_txc));
	m_ctc->zc_callback<0>().append("usart", FUNC(i8251_device::write_rxc));

	I8255(config, m_ppi[0]);

	I8255(config, m_ppi[1]);
	m_ppi[1]->out_pa_callback().set(FUNC(m3_state::ppi2_pa_w));
	m_ppi[1]->in_pb_callback().set(FUNC(m3_state::ppi2_pb_r));

	i8251_device &usart(I8251(config, "usart", 0)); // unknown clock
	usart.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	usart.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	usart.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.set_option_device_input_defaults("printer", DEVICE_INPUT_DEFAULTS_NAME(rs232_printer_defaults));
	rs232.rxd_handler().set("usart", FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set("usart", FUNC(i8251_device::write_dsr));
	rs232.cts_handler().set("usart", FUNC(i8251_device::write_cts));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_raw(21'840'000 / 2, 707, 0, 560, 309, 0, 240); // unknown clock, hand-tuned to ~50 fps
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	GFXDECODE(config, "gfxdecode", m_palette, chars);

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	mc6845_device &crtc(MC6845(config, "crtc", 21'840'000 / 2 / 7)); // unknown clock
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(7);
	crtc.set_update_row_callback(FUNC(m3_state::crtc_update_row));
	crtc.out_vsync_callback().set(m_ctc, FUNC(z80ctc_device::trg2));

	// floppy
	FD1793(config, m_fdc, 16_MHz_XTAL / 8);
	m_fdc->intrq_wr_callback().set(FUNC(m3_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(m3_state::fdc_drq_w));
	FLOPPY_CONNECTOR(config, "fdc:0", m3_floppies, "sa850", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", m3_floppies, "sa850", floppy_image_device::default_mfm_floppy_formats);

	SOFTWARE_LIST(config, "floppy_list").set_original("m3");

	// keyboard
	I8035(config, m_kbdmcu, 6.144_MHz_XTAL);
	m_kbdmcu->set_addrmap(AS_PROGRAM, &m3_state::kbd_mem_map);
	m_kbdmcu->set_addrmap(AS_IO, &m3_state::kbd_io_map);
	m_kbdmcu->p1_in_cb().set(FUNC(m3_state::kbd_p1_r));
	m_kbdmcu->p1_out_cb().set(FUNC(m3_state::kbd_p1_w));
	m_kbdmcu->p2_out_cb().set(FUNC(m3_state::kbd_p2_w));
	m_kbdmcu->t0_in_cb().set(FUNC(m3_state::kbd_t0_r));
	m_kbdmcu->t1_in_cb().set(FUNC(m3_state::kbd_t1_r));
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( m3 )
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("bootstrap_prom_034.bin", 0x0000, 0x0800, CRC(7fdb051e) SHA1(7aa24d4f44b6a0c8f7f647667f4997432c186cac))

	// Homebrew Monitor ROM, written by Steve Hunt. Uses the socket of the HDD ROM.
	ROM_LOAD("monitor_prom_v19_2017-07-05.bin", 0x0800, 0x0800, CRC(0608848f) SHA1(9a82cb49056ff1a1d53ce2bd026537a6914a4847))

	ROM_REGION(0x800, "chargen", 0)
	ROM_LOAD("6845crt_font_prom_033.bin", 0x000, 0x800, CRC(cc29f664) SHA1(4197530d9455d665fd4773f95bb6394f6b056dec))

	ROM_REGION(0x800, "keyboard", 0)
	ROM_LOAD("keyboard_prom_032.bin", 0x000, 0x800, CRC(21548355) SHA1(ee4ce4af9c78474263dd58e0f19e79e5b00926fa))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS     INIT        COMPANY  FULLNAME   FLAGS
COMP( 1982, m3,   0,      0,      m3,      m3,    m3_state, empty_init, "LSI",   "M-THREE", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



m5.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder, Ales Dlabac
/***************************************************************************

    Sord m.5

    http://www.retropc.net/mm/m5/
    http://www.museo8bits.es/wiki/index.php/Sord_M5 not working
    http://k5.web.klfree.net/content/view/10/11/ not working
    http://k5.web.klfree.net/images/stories/sord/m5heap.htm  not working
    http://k5.klfree.net/index.php?option=com_content&task=view&id=5&Itemid=3
    http://k5.klfree.net/index.php?option=com_content&task=view&id=10&Itemid=11
    http://k5.klfree.net/index.php?option=com_content&task=view&id=14&Itemid=3
    http://www.dlabi.cz/?s=sord
    https://www.facebook.com/groups/59667560188/
    http://www.oldcomp.cz/viewtopic.php?f=103&t=1164

****************************************************************************/

/***************************************************************************

TODO:

    - fd5 floppy
    - SI-5 serial interface (8251, ROM)
    - ramdisk for KRX Memory expansion
    - rewrite fd5 floppy as unpluggable device
    - move dipswitch declaration to softwarelist file?
    - 64krx: get windows ROM version with cpm & ramdisk support (Stuchlik S.E.I. version)

    - brno mod: make the dsk image writeable
    - brno mod: in console version lost data on RAMDISK after soft reset
    - brno mod: add support for lzr floppy disc format
    - brno mod: include basic-i



CHANGELOG:

10.02.2016
    - fixed bug: crash if rom card was only cart
    - fixed bug: when em-5 selected monitor rom wasn't paged in
    - brno mod: spin motor on upon restart
    - brno mod: windowed boot as default rom
    - brno mod: fixed bug: tape command in menu now works

05.02.2016
    - added BRNO modification - 1024kB Ramdisk + CP/M support
    - 32/64KB RAM expansions EM-5, 64KBI, 64KBF, 64KRX
    - since now own version of rom and slot handlers
    - 2 slots for carts


******************************************************************************


Controlling (paging) of homebrew 64KB RAM carts
================================================

Used ports:
EM-64, 64KBI:   OUT 6CH,00H - enables ROM
                OUT 6CH,01H - enables RAM
64KBF:          OUT 30H,00000xxxB   - enables RAM or ROM, see bellow
64KRD, 64KRX:   OUT 7FH,00000000B   - enables RAM
                OUT 7FH,11111111B   - enables ROM
                OUT 7FH,xxxxxxxxB   - enables RAM and ROM, see bellow

===========================================================================================================================

RAM/ROM modes of EM-64/64KBI cart
------------------------------------------
mode 0: 0x0000-0x6fff ROM 0x7000-0xffff RAM (it is possible to limit actual ROM size by DIP switch only to 32kb)
mode 1: 0x0000-0xffff RAM

===========================================================================================================================

RAM/ROM modes of 64KBF version 2C cart
------------------------------------------
Memory paging is done by using "OUT &30,mod".

MODE    READ                            WRITE
----------------------------------------------------------------------
 00 8 KB MON + 20 KB BF + 36 KB RAM     28 KB DIS + 36 KB RAM
 01 64 KB RAM                           64 KB RAM
 02 8 KB MON + 56 KB RAM                64 KB RAM
 03 64 KB RAM                           28 KB DIS + 36 KB RAM
 04 64 KB RAM                           16 KB DIS + 48 KB RAM
 05 8 KB MON + 20 KB BF + 36 KB RAM     64 KB RAM
 06 8 KB MON + 20 KB DIS + 36 KB RAM    64 KB RAM
 07 64 KB DIS                           64 KB DIS

Version LZR ( 2C )
================

+------------+
|////////////|  READ ONLY AREA
+------------+
|\\\\\\\\\\\\|  WRITE ONLY AREA
+------------+
|XXXXXXXXXXXX|  R&W AREA
+------------+
|            |  DISABLED R&W
+------------+

      0   0   0   1   1   2   2   2   3   3   4   4   4   5   5   6   6
kB    0   4   8   2   6   0   4   8   2   6   0   4   8   2   6   0   4
      +-------+-------------------+
ROM   |MONITOR|      BASIC-F      |
      +-------+-------+-------+---+---+-------+-------+-------+-------+
RAM   |       |       |       |       |       |       |       |       |
      +-------+-------+-------+-------+-------+-------+-------+-------+
CART  |       |       |       |       |       |       |       |       |
      +-------+-------+-------+-------+-------+-------+-------+-------+


Mode
    +-------+-------------------+
    |///////|///////////////////|
    +-------+-------+-------+---+---+-------+-------+-------+-------+
M0  |       |       |       |   |XXX|XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|
    +-------+-------+-------+-------+-------+-------+-------+-------+

    +-------+-------------------+
    |       |                   |
    +-------+-------+-------+---+---+-------+-------+-------+-------+
M1  |XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|
    +-------+-------+-------+-------+-------+-------+-------+-------+

    +-------+-------------------+
    |///////|                   |
    +-------+-------+-------+---+---+-------+-------+-------+-------+
M2  |\\\\\\\|XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|
    +-------+-------+-------+-------+-------+-------+-------+-------+

    +-------+-------------------+
    |       |                   |
    +-------+-------+-------+---+---+-------+-------+-------+-------+
M3  |///////|///////|///////|///|XXX|XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|
    +-------+-------+-------+-------+-------+-------+-------+-------+

    +-------+-------------------+
    |       |                   |
    +-------+-------+-------+---+---+-------+-------+-------+-------+
M4  |///////|///////|XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|
    +-------+-------+-------+-------+-------+-------+-------+-------+

    +-------+-------------------+
    |///////|///////////////////|
    +-------+-------+-------+---+---+-------+-------+-------+-------+
M5  |\\\\\\\|\\\\\\\|\\\\\\\|\\\|XXX|XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|
    +-------+-------+-------+-------+-------+-------+-------+-------+

    +-------+-------------------+
    |///////|                   |
    +-------+-------+-------+---+---+-------+-------+-------+-------+
M6  |\\\\\\\|\\\\\\\|\\\\\\\|\\\|XXX|XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|
    +-------+-------+-------+---+---+-------+-------+-------+-------+
            |///////|///////|///|
            +-------+-------+---+

    +-------+-------------------+
    |       |                   |
    +-------+-------+-------+---+---+-------+-------+-------+-------+
M7  |       |       |       |       |       |       |       |       |
    +-------+-------+-------+-------+-------+-------+-------+-------+
    |XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXX|
    +---------------------------------------------------------------+

===========================================================================================================

Memory map of ROM and RAM in configuration SORD M5 + 64 KRX memory cart
-----------------------------------------------------------------------

         cart     inside Sord   inside Sord     cart        cart
FFFF +----------+ +----------+ +----------+ +----------+ +----------+
     |          |                           |          | |          |
     |          |                           | EPROM 16K| | EPROM 16K|
     |          |                           |        5 | |        7 |
C000 |   DRAM   |                           +----------+ +----------+
     |          |                           |          | |          |
     |          |                           | EPROM 16K| | EPROM 16K|
     |          |                           |        4 | |        6 |
7FFF +----------+ +----------+              +----------+ +----------+
                  |   SRAM   |
7000 +----------+ +----------+
     |          |
6000 |          |                           +----------+
     |          |                           |          |
5000 |          |                           | EPROM 8K |
     |          |                           |        3 |
4000 |          |              +----------+ +----------+
     |          |              |          |
3000 |   DRAM   |              | EPROM 8K |
     |          |              |        2 |
2000 |          |              +----------+
     |          |              |          |
1000 |          |              | EPROM 8K |
     |          |              |        1 |
0000 +----------+ +----------+ +----------+ +----------+ +----------+

1 - MONITOR ROM
2 - WINDOWS + BASIC-F 3rd part
3 - BASIC-I
4 - 2nd part of BASIC-F + 1st part of BASIC-F
5 - 1st part of BASIC-G + 2nd part of BASIC-G
6 - 1st part of MSX 1.C
7 - 2nd part of MSX 1.C

Note: position 3 could be replaced with SRAM 8KB with battery power backup!

Upon powering up either SRAM + 1,2,3,4,5 or SRAM + 1,2,3,6,7 are selected.
Switching between 4,5 and 6,7 is provided by hw switch, selecting ROM/RAM mode happens
using OUT (7FH),A, where each bit of A means 8KB memory chunk ( state: 0=RAM,
1=ROM, bit: 0=1, 1=2, 2=3, 3=always SRAM, 4=4, 5=5, 6=6, 7=7 ).


*/

/*
*************************************************************
*                       BRNO MOD                            *
*************************************************************
HW and SW was originally created by Pavel Brychta with help of Jiri Kubin and L. Novak
This driver mod was implemented by Ales Dlabac with great help of Pavel Brychta. Without him this would never happen
This mod exists in two versions. First one is "windows"(brno_rom12.rom) version and was created by Ladislav Novak.
Second version version is "pure text" and was created by Pavel Brychta and Jiri Kubin

Function:
Whole Sord's address area (0000-FFFF) is divided to 16 4kB banks. To this 16 banks
you can map any of possible 256 ramdisc blocks what allows user to have 1024kB large ramdisc.
Of course to be able to realise this is necessary page out all roms

As pagination port MMU(page select) is used.
For RAM write protection port CASEN is used. 0=access to ramdisk enabled, 0xff=ramdisk access disabled(data protection), &80=ROM2+48k RAM, &81=ROM2+4k RAM(this is not implemented)
For ROM page out port RAMEN is used. 0=rom enable; 0xff=rom+sord ram disabled (ramdisk visible)

SORD M5 RAM memory map in address area 7000H-7FFFH
7000H     7300H                   7800H                        7E00H     7FFFH
  +---------+-----------------------+----------------------------+---------+
  |    a.   |                       |            c.              |   d.    |

a. SORD system variables and stack
c. Area where the first sector of 1st track is loaded, simultaneously is reserved for Hook program
d. Reserved for memory tests and ramdisk mapping(pagination). After boot is used as buffer for cursor position,
   type of floppy and so on. Area consists of:

7FFFH .... bootloader version
7FFEH .... identification byte of floppy - is transferred from EPROM, it might be changed by SETUP
7FFDH .... number of last Ramdisk segment of RAM
7FFBH .... address of cursor in VRAM in 40 columns CRT. For 80 columns CRT both bytes are zero
7FF9H .... X,Y cursor actual position for 40 columns CRTs. In case of 80 columns CRT both bytes are zero
7203H .... Actual memory bank buffer

System floppy disk header on track 00 of 1st sector
         byte 0-1  ... system disk identification SY
         byte 2    ... # of physical sectors for BIOS or DOS plus # of segments for DIR
         byte 3-4  ... Start address for loading of BIOS or DOS
         byte 5    ... # of bytes for possible HOOK program
         byte 6-   ... HOOK program, or either BIOS or DOS

In case of HOOK, bytes 8 and 9 contains characters 'H' and 'O' for HOOK testing

Few other notes:
 Ramdisc warm boot is provided by pressing Ctrl+C


 Floppy formats as follows:

 A: Ramdisk 1024kB, 8 sectors,
 B: Floppy format "Heat Magnolia" SingleSide SingleDensity , 40 tracks, 9 sectors, 512  sec. length, 128 dirs, offset 3, 166kB
 C: Floppy format "Robotron aka PC1715", DS DD,              80 tracks, 5 sectors, 1024 sec. length, 128 dirs, offset 2, 780kB

**********************************************************************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/ram.h"
#include "machine/upd765.h"
#include "machine/wd_fdc.h" //brno mod
#include "machine/z80daisy.h"
#include "machine/z80ctc.h"
#include "sound/sn76496.h"
#include "video/tms9928a.h"

#include "bus/rs232/rs232.h"
#include "bus/centronics/ctronics.h"
#include "bus/m5/rom.h"
#include "bus/m5/slot.h"

#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "imagedev/snapquik.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "formats/m5_dsk.h"
#include "formats/sord_cas.h"


namespace {

class m5_state : public driver_device
{
public:
	m5_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ctc(*this, "ctc")
		, m_fd5cpu(*this, "z80fd5")
		, m_ppi(*this, "ppi")
		, m_fdc(*this, "upd765")
		, m_floppy0(*this, "upd765:0:525dd")
		, m_cassette(*this, "cassette")
		, m_cart1(*this, "cartslot1")
		, m_cart2(*this, "cartslot2")
		, m_centronics(*this, "centronics")
		, m_ram(*this, RAM_TAG)
		, m_reset(*this, "RESET")
		, m_DIPS(*this, "DIPS")
	{ }

	void m5(machine_config &config);
	void pal(machine_config &config);
	void ntsc(machine_config &config);

protected:
	required_device<z80_device> m_maincpu;
	required_device<z80ctc_device> m_ctc;
	//I've changed following devices to optional since we have to remove them in BRNO mod (I don't know better solution)
	optional_device<cpu_device> m_fd5cpu;
	optional_device<i8255_device> m_ppi;
	optional_device<upd765a_device> m_fdc;
	optional_device<floppy_image_device> m_floppy0;
	required_device<cassette_image_device> m_cassette;
	optional_device<m5_cart_slot_device> m_cart1;
	optional_device<m5_cart_slot_device> m_cart2;
	required_device<centronics_device> m_centronics;
	optional_device<ram_device> m_ram;
	required_ioport m_reset;
	optional_ioport m_DIPS;

	m5_cart_slot_device *m_cart_ram = nullptr;
	m5_cart_slot_device *m_cart = nullptr;

	bool m_centronics_busy = false;

	u8 sts_r();
	void com_w(u8 data);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u8 ppi_pa_r();
	void ppi_pa_w(u8 data);
	void ppi_pb_w(u8 data);
	u8 ppi_pc_r();
	void ppi_pc_w(u8 data);

	u8 fd5_data_r();
	void fd5_data_w(u8 data);
	u8 fd5_com_r();
	void fd5_com_w(u8 data);
	void fd5_ctrl_w(u8 data);
	void fd5_tc_w(u8 data);

	static void floppy_formats(format_registration &fr);

	void write_centronics_busy(int state);

	// memory
	u8 mem64KBI_r();
	void mem64KBI_w(offs_t offset, u8 data);
	void mem64KBF_w(u8 data);
	void mem64KRX_w(offs_t offset, u8 data);

	void fd5_io(address_map &map) ATTR_COLD;
	void fd5_mem(address_map &map) ATTR_COLD;
	void m5_io(address_map &map) ATTR_COLD;
	void m5_mem(address_map &map) ATTR_COLD;

private:
	u8 m_ram_mode = 0;
	u8 m_ram_type = 0;
	memory_region *m_cart_rom = nullptr;

	// floppy state for fd5
	u8 m_fd5_data = 0;
	u8 m_fd5_com = 0;
	int m_intra = 0;
	int m_ibfa = 0;
	int m_obfa = 0;
};


class brno_state : public m5_state
{
public:
	brno_state(const machine_config &mconfig, device_type type, const char *tag)
		: m5_state(mconfig, type, tag)
		, m_rom_view(*this, "rom")
		, m_ram_view(*this, "ram")
		, m_wdfdc(*this, "wd")
		, m_floppy0(*this, "wd:0")
		, m_floppy1(*this, "wd:1")
	{ }

	void brno(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u8 ramdisk_r(offs_t offset);
	void ramdisk_w(offs_t offset, u8 data);
	void mmu_w(offs_t offset, u8 data);
	void ramsel_w(u8 data);
	void romsel_w(u8 data);
	void fd_w(u8 data);

	static void floppy_formats(format_registration &fr);

	DECLARE_SNAPSHOT_LOAD_MEMBER(brno);

	void brno_io(address_map &map) ATTR_COLD;
	void m5_mem_brno(address_map &map) ATTR_COLD;

	memory_view m_rom_view;
	memory_view m_ram_view;
	required_device<wd2797_device> m_wdfdc;
	required_device<floppy_connector> m_floppy0;
	optional_device<floppy_connector> m_floppy1;
	floppy_image_device *m_floppy = nullptr;

	u8 m_rammap[16]{}; // memory map
};


//**************************************************************************
//  MEMORY BANKING
//**************************************************************************

void m5_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

//-------------------------------------------------
//  sts_r -
//-------------------------------------------------

u8 m5_state::sts_r()
{
	/*

	    bit     description

	    0       cassette input
	    1       busy
	    2
	    3
	    4
	    5
	    6
	    7       RESET key

	*/

	u8 data = 0;

	// cassette input
	data |= m_cassette->input() >= 0 ? 1 : 0;

	// centronics busy
	if (m_centronics_busy)
		data |= 2;

	// RESET key
	data |= m_reset->read();

	return data;
}


//-------------------------------------------------
//  com_w -
//-------------------------------------------------

void m5_state::com_w(u8 data)
{
	/*

	    bit     description

	    0       cassette output, centronics strobe
	    1       cassette remote
	    2
	    3
	    4
	    5
	    6
	    7

	*/

	// cassette output
	m_cassette->output( BIT(data, 0) ? -1.0 : 1.0);

	// centronics strobe
	m_centronics->write_strobe(BIT(data, 0));

	// cassette remote
	m_cassette->change_state(BIT(data,1) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
}



//**************************************************************************
//  FD-5
//**************************************************************************

//-------------------------------------------------
//  fd5_data_r -
//-------------------------------------------------

u8 m5_state::fd5_data_r()
{
	m_ppi->pc6_w(0);

	return m_fd5_data;
}


//-------------------------------------------------
//  fd5_data_w -
//-------------------------------------------------

void m5_state::fd5_data_w(u8 data)
{
	m_fd5_data = data;

	m_ppi->pc4_w(0);
}


//-------------------------------------------------
//  fd5_com_r -
//-------------------------------------------------

u8 m5_state::fd5_com_r()
{
	/*

	    bit     description

	    0       ?
	    1       1?
	    2       IBFA?
	    3       OBFA?
	    4
	    5
	    6
	    7

	*/

	return m_obfa << 3 | m_ibfa << 2 | 0x02;
}


//-------------------------------------------------
//  fd5_com_w -
//-------------------------------------------------

void m5_state::fd5_com_w(u8 data)
{
	/*

	    bit     description

	    0       PPI PC2
	    1       PPI PC0
	    2       PPI PC1
	    3
	    4
	    5
	    6
	    7

	*/

	m_fd5_com = data;
}


//-------------------------------------------------
//  fd5_com_w -
//-------------------------------------------------

void m5_state::fd5_ctrl_w(u8 data)
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4
	    5
	    6
	    7

	*/

	m_floppy0->mon_w(!BIT(data, 0));
}


//-------------------------------------------------
//  fd5_com_w -
//-------------------------------------------------

void m5_state::fd5_tc_w(u8 data)
{
	m_fdc->tc_w(true);
	m_fdc->tc_w(false);
}

//**************************************************************************
//  64KBI support for oldest memory module
//**************************************************************************

u8 m5_state::mem64KBI_r() //in 0x6c
{
	return BIT(m_ram_mode, 0);
}

void m5_state::mem64KBI_w(offs_t offset, u8 data) //out 0x6c
{
	if (m_ram_type != MEM64KBI) return;

#if 0 // FIXME: disabled for now
	address_space &program = m_maincpu->space(AS_PROGRAM);
	std::string region_tag;
	m_cart_rom = memregion(region_tag.assign(m_cart_ram->tag()).append(M5SLOT_ROM_REGION_TAG).c_str());
	memory_region *ram_region = memregion(region_tag.assign(m_cart_ram->tag()).append(":ram").c_str());

	if (m_ram_mode == BIT(data, 0))
		return;

	m_ram_mode = BIT(data, 0);

	//if 32kb only mode don't map top ram
	if (m_ram_mode && (m_DIPS->read() & 4) != 4)
	{
		program.install_ram(0x0000, 0x6fff, ram_region->base());
	}
	else
	{
		program.install_rom(0x0000, 0x1fff, memregion("maincpu")->base());
		program.unmap_write(0x0000, 0x1fff);

		//if AUTOSTART is on don't load any ROM cart
		if (m_cart && (m_DIPS->read() & 2) != 2)
		{
			program.install_read_handler(0x2000, 0x6fff, read8sm_delegate(*m_cart, FUNC(m5_cart_slot_device::read_rom)));
			program.unmap_write(0x2000, 0x3fff);
		}
		else
			program.unmap_readwrite(0x2000, 0x3fff);
	}
#endif

	logerror("64KBI: ROM %s", m_ram_mode == 0 ? "enabled\n" : "disabled\n");
}

//**************************************************************************
//  64KBF paging
//**************************************************************************

void m5_state::mem64KBF_w(u8 data) //out 0x30
{
	if (m_ram_type != MEM64KBF) return;

#if 0 // FIXME: disabled for now
	address_space &program = m_maincpu->space(AS_PROGRAM);
	std::string region_tag;
	m_cart_rom = memregion(region_tag.assign(m_cart_ram->tag()).append(M5SLOT_ROM_REGION_TAG).c_str()); //ROM region of the cart
	memory_region *ram_region=memregion(region_tag.assign(m_cart_ram->tag()).append(":ram").c_str());   //RAM region of the cart
	memory_region *rom_region=memregion(region_tag.assign(m_cart->tag()).append(M5SLOT_ROM_REGION_TAG).c_str()); //region where clasic ROM cartridge resides

	if (m_ram_mode == data)
		return;

	m_ram_mode = data;

	switch(m_ram_mode)
	{
		case 0:
			program.unmap_write(0x0000, 0x6fff);
			membank("bank1r")->set_base(memregion("maincpu")->base());
			membank("bank2r")->set_base(m_cart_rom->base());
			membank("bank3r")->set_base(m_cart_rom->base()+0x2000);
			membank("bank4r")->set_base(m_cart_rom->base()+0x4000);
			membank("bank5r")->set_base(ram_region->base()+0x8000);     membank("bank5w")->set_base(ram_region->base()+0x8000);
			membank("bank6r")->set_base(ram_region->base()+0xc000);     membank("bank6w")->set_base(ram_region->base()+0xc000);
			break;
		case 1:
			program.install_write_bank(0x0000,0x1fff,membank("bank1w"));
			program.install_write_bank(0x2000,0x3fff,membank("bank2w"));
			program.install_write_bank(0x4000,0x5fff,membank("bank3w"));
			program.install_write_bank(0x6000,0x6fff,membank("bank4w"));
			membank("bank1r")->set_base(ram_region->base()+0x0000);     membank("bank1w")->set_base(ram_region->base()+0x0000);
			membank("bank2r")->set_base(ram_region->base()+0x2000);     membank("bank2w")->set_base(ram_region->base()+0x2000);
			membank("bank3r")->set_base(ram_region->base()+0x4000);     membank("bank3w")->set_base(ram_region->base()+0x4000);
			membank("bank4r")->set_base(ram_region->base()+0x6000);     membank("bank4w")->set_base(ram_region->base()+0x6000);
			membank("bank5r")->set_base(ram_region->base()+0x8000);     membank("bank5w")->set_base(ram_region->base()+0x8000);
			membank("bank6r")->set_base(ram_region->base()+0xc000);     membank("bank6w")->set_base(ram_region->base()+0xc000);
			break;
		case 2:
			program.install_write_bank(0x0000,0x1fff,membank("bank1w"));
			program.install_write_bank(0x2000,0x3fff,membank("bank2w"));
			program.install_write_bank(0x4000,0x5fff,membank("bank3w"));
			program.install_write_bank(0x6000,0x6fff,membank("bank4w"));
			membank("bank1r")->set_base(memregion("maincpu")->base());  membank("bank1w")->set_base(ram_region->base()+0x0000);
			membank("bank2r")->set_base(ram_region->base()+0x2000);     membank("bank2w")->set_base(ram_region->base()+0x2000);
			membank("bank3r")->set_base(ram_region->base()+0x4000);     membank("bank3w")->set_base(ram_region->base()+0x4000);
			membank("bank4r")->set_base(ram_region->base()+0x6000);     membank("bank4w")->set_base(ram_region->base()+0x6000);
			membank("bank5r")->set_base(ram_region->base()+0x8000);     membank("bank5w")->set_base(ram_region->base()+0x8000);
			membank("bank6r")->set_base(ram_region->base()+0xc000);     membank("bank6w")->set_base(ram_region->base()+0xc000);
			break;
		case 3:
			program.unmap_write(0x0000, 0x6fff);
			membank("bank1r")->set_base(ram_region->base()+0x0000);
			membank("bank2r")->set_base(ram_region->base()+0x2000);
			membank("bank3r")->set_base(ram_region->base()+0x4000);
			membank("bank4r")->set_base(ram_region->base()+0x6000);
			membank("bank5r")->set_base(ram_region->base()+0x8000);     membank("bank5w")->set_base(ram_region->base()+0x8000);
			membank("bank6r")->set_base(ram_region->base()+0xc000);     membank("bank6w")->set_base(ram_region->base()+0xc000);
			break;
		case 4:
			program.unmap_write(0x0000, 0x3fff);
			program.install_write_bank(0x4000,0x5fff,membank("bank3w"));
			program.install_write_bank(0x6000,0x6fff,membank("bank4w"));
			membank("bank1r")->set_base(ram_region->base()+0x0000);
			membank("bank2r")->set_base(ram_region->base()+0x2000);
			membank("bank3r")->set_base(ram_region->base()+0x4000);     membank("bank3w")->set_base(ram_region->base()+0x4000);
			membank("bank4r")->set_base(ram_region->base()+0x6000);     membank("bank4w")->set_base(ram_region->base()+0x6000);
			membank("bank5r")->set_base(ram_region->base()+0x8000);     membank("bank5w")->set_base(ram_region->base()+0x8000);
			membank("bank6r")->set_base(ram_region->base()+0xc000);     membank("bank6w")->set_base(ram_region->base()+0xc000);
			break;
		case 5:
			program.install_write_bank(0x0000,0x1fff,membank("bank1w"));
			program.install_write_bank(0x2000,0x3fff,membank("bank2w"));
			program.install_write_bank(0x4000,0x5fff,membank("bank3w"));
			program.install_write_bank(0x6000,0x6fff,membank("bank4w"));
			membank("bank1r")->set_base(memregion("maincpu")->base());  membank("bank1w")->set_base(ram_region->base()+0x0000);
			membank("bank2r")->set_base(m_cart_rom->base());            membank("bank2w")->set_base(ram_region->base()+0x2000);
			membank("bank3r")->set_base(m_cart_rom->base()+0x2000);     membank("bank3w")->set_base(ram_region->base()+0x4000);
			membank("bank4r")->set_base(m_cart_rom->base()+0x4000);     membank("bank4w")->set_base(ram_region->base()+0x6000);
			membank("bank5r")->set_base(ram_region->base()+0x8000);     membank("bank5w")->set_base(ram_region->base()+0x8000);
			membank("bank6r")->set_base(ram_region->base()+0xc000);     membank("bank6w")->set_base(ram_region->base()+0xc000);
			break;
		case 6:
			program.install_write_bank(0x0000,0x1fff,membank("bank1w"));
			program.install_write_bank(0x2000,0x3fff,membank("bank2w"));
			program.install_write_bank(0x4000,0x5fff,membank("bank3w"));
			program.install_write_bank(0x6000,0x6fff,membank("bank4w"));
			membank("bank1r")->set_base(memregion("maincpu")->base());  membank("bank1w")->set_base(ram_region->base()+0x0000);
			membank("bank2r")->set_base(rom_region->base()+0x0000);     membank("bank2w")->set_base(ram_region->base()+0x2000);
			membank("bank3r")->set_base(rom_region->base()+0x2000);     membank("bank3w")->set_base(ram_region->base()+0x4000);
			membank("bank4r")->set_base(rom_region->base()+0x4000);     membank("bank4w")->set_base(ram_region->base()+0x6000);
			membank("bank5r")->set_base(ram_region->base()+0x8000);     membank("bank5w")->set_base(ram_region->base()+0x8000);
			membank("bank6r")->set_base(ram_region->base()+0xc000);     membank("bank6w")->set_base(ram_region->base()+0xc000);
			break;
		case 7: //probably this won't work - it should redirect rw to another ram module
			program.install_write_bank(0x0000,0x1fff,membank("bank1w"));
			program.install_write_bank(0x2000,0x3fff,membank("bank2w"));
			program.install_write_bank(0x4000,0x5fff,membank("bank3w"));
			program.install_write_bank(0x6000,0x6fff,membank("bank4w"));
			program.install_readwrite_bank(0x7000,0x7fff,membank("sram"));
			membank("bank1r")->set_base(rom_region->base()+0x0000);     membank("bank1w")->set_base(rom_region->base()+0x0000);
			membank("bank2r")->set_base(rom_region->base()+0x2000);     membank("bank2w")->set_base(rom_region->base()+0x2000);
			membank("bank3r")->set_base(rom_region->base()+0x4000);     membank("bank3w")->set_base(rom_region->base()+0x4000);
			membank("bank4r")->set_base(rom_region->base()+0x6000);     membank("bank4w")->set_base(rom_region->base()+0x6000);
			membank("sram")->set_base(rom_region->base()+0x7000);
			membank("bank5r")->set_base(rom_region->base()+0x8000);     membank("bank5w")->set_base(rom_region->base()+0x8000);
			membank("bank6r")->set_base(rom_region->base()+0xc000);     membank("bank6w")->set_base(rom_region->base()+0xc000);
			break;
	}
#endif

	logerror("64KBF RAM mode set to %d\n", m_ram_mode);
}

//**************************************************************************
//  64KRX paging
//**************************************************************************

void m5_state::mem64KRX_w(offs_t offset, u8 data) //out 0x7f
{
	if (m_ram_type != MEM64KRX) return;
	if (m_ram_mode == data) return;

#if 0 // FIXME: disabled for now
	address_space &program = m_maincpu->space(AS_PROGRAM);
	std::string region_tag;
	m_cart_rom = memregion(region_tag.assign(m_cart_ram->tag()).append(M5SLOT_ROM_REGION_TAG).c_str());
	memory_region *ram_region=memregion(region_tag.assign(m_cart_ram->tag()).append(":ram").c_str());

	m_ram_mode = data;

	BIT(m_ram_mode, 0) ? membank("bank1r")->set_base(memregion("maincpu")->base())  :   membank("bank1r")->set_base(ram_region->base());
	BIT(m_ram_mode, 1) ? membank("bank2r")->set_base(m_cart_rom->base())            :   membank("bank2r")->set_base(ram_region->base()+0x2000);
	BIT(m_ram_mode, 2) ? membank("bank3r")->set_base(m_cart_rom->base()+0x2000)     :   membank("bank3r")->set_base(ram_region->base()+0x4000);

	if ((m_DIPS->read() & 0x01))
	{
		BIT(m_ram_mode, 4) ? membank("bank5r")->set_base(m_cart_rom->base()+0x6000) :   membank("bank5r")->set_base(ram_region->base()+0x8000);
		BIT(m_ram_mode, 5) ? membank("bank6r")->set_base(m_cart_rom->base()+0xa000) :   membank("bank6r")->set_base(ram_region->base()+0xc000);
	}
	else
	{
		BIT(m_ram_mode, 6) ? membank("bank5r")->set_base(m_cart_rom->base()+0xe000) :   membank("bank5r")->set_base(ram_region->base()+0x8000);
		BIT(m_ram_mode, 7) ? membank("bank6r")->set_base(m_cart_rom->base()+0x12000):   membank("bank6r")->set_base(ram_region->base()+0xc000);
	}

	//if KRX ROM is paged out page in cart ROM if any
	if (m_cart && BIT(m_ram_mode, 1) == 0 )
	{
		program.install_read_handler(0x2000, 0x6fff, read8sm_delegate(*m_cart, FUNC(m5_cart_slot_device::read_rom)));
		program.unmap_write(0x2000, 0x6fff);
	}
#endif

	logerror("64KRX RAM mode set to %02x\n", m_ram_mode);
}


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( m5_mem )
//-------------------------------------------------

void m5_state::m5_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom().region("maincpu", 0); //monitor rom(bios)
	//map(0x0000, 0x1fff).bankr("bank1r").bankw("bank1w");
	//map(0x2000, 0x3fff).bankr("bank2r").bankw("bank2w");
	//map(0x4000, 0x5fff).bankr("bank3r").bankw("bank3w");
	//map(0x6000, 0x6fff).bankr("bank4r").bankw("bank4w");
	map(0x7000, 0x7fff).ram();                                         //4kb internal RAM
	//map(0x8000, 0xbfff).bankr("bank5r").bankw("bank5w");
	//map(0xc000, 0xffff).bankr("bank6r").bankw("bank6w");
}


//-------------------------------------------------
//  ADDRESS_MAP( m5_io )
//-------------------------------------------------

void m5_state::m5_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x03).mirror(0x0c).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x10, 0x11).mirror(0x0e).rw("tms9928a", FUNC(tms9928a_device::read), FUNC(tms9928a_device::write));
	map(0x20, 0x20).mirror(0x0f).w("sgc", FUNC(sn76489a_device::write));
	map(0x30, 0x30).mirror(0x08).portr("Y0").w(FUNC(m5_state::mem64KBF_w)); // 64KBF paging
	map(0x31, 0x31).mirror(0x08).portr("Y1");
	map(0x32, 0x32).mirror(0x08).portr("Y2");
	map(0x33, 0x33).mirror(0x08).portr("Y3");
	map(0x34, 0x34).mirror(0x08).portr("Y4");
	map(0x35, 0x35).mirror(0x08).portr("Y5");
	map(0x36, 0x36).mirror(0x08).portr("Y6");
	map(0x37, 0x37).mirror(0x08).portr("JOY");
	map(0x40, 0x40).mirror(0x0f).w("cent_data_out", FUNC(output_latch_device::write));
	map(0x50, 0x50).mirror(0x0f).rw(FUNC(m5_state::sts_r), FUNC(m5_state::com_w));
//  map(0x60, 0x63) SIO
	map(0x6c, 0x6c).rw(FUNC(m5_state::mem64KBI_r), FUNC(m5_state::mem64KBI_w)); //EM-64/64KBI paging
	map(0x70, 0x73) /*.mirror(0x0c) don't know if necessary mirror this*/ .rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x7f, 0x7f).w(FUNC(m5_state::mem64KRX_w)); //64KRD/64KRX paging
}


//-------------------------------------------------
//  ADDRESS_MAP( fd5_mem )
//-------------------------------------------------

void m5_state::fd5_mem(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	map(0x4000, 0xffff).ram();
}


//-------------------------------------------------
//  ADDRESS_MAP( fd5_io )
//-------------------------------------------------

void m5_state::fd5_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x01).m(m_fdc, FUNC(upd765a_device::map));
	map(0x10, 0x10).rw(FUNC(m5_state::fd5_data_r), FUNC(m5_state::fd5_data_w));
	map(0x20, 0x20).w(FUNC(m5_state::fd5_com_w));
	map(0x30, 0x30).r(FUNC(m5_state::fd5_com_r));
	map(0x40, 0x40).w(FUNC(m5_state::fd5_ctrl_w));
	map(0x50, 0x50).w(FUNC(m5_state::fd5_tc_w));
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( m5 )
//-------------------------------------------------

static INPUT_PORTS_START( m5 )
	PORT_START("Y0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Func") PORT_CODE(KEYCODE_TAB) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)      PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT)      PORT_CHAR(UCHAR_MAMEKEY(RSHIFT))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)       PORT_CHAR(' ')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)       PORT_CHAR(13)

	PORT_START("Y1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)           PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)           PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)           PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)           PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)           PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)           PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)           PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)           PORT_CHAR('8') PORT_CHAR('(')

	PORT_START("Y2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)           PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)           PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)           PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)           PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)           PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)           PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)           PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)           PORT_CHAR('i') PORT_CHAR('I')

	PORT_START("Y3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)           PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)           PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)           PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)           PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)           PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)           PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)           PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)           PORT_CHAR('k') PORT_CHAR('K')

	PORT_START("Y4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)           PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)           PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)           PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)           PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)           PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)           PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)           PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)       PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("Y5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)           PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)           PORT_CHAR('0')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)       PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)  PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)        PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)       PORT_CHAR('/') PORT_CHAR('?') PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("_  Triangle") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('_')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)   PORT_CHAR('\\') PORT_CHAR('|') // backslash ok for m5p, shows as ¥ on m5.

	PORT_START("Y6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)           PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)           PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)   PORT_CHAR('@') PORT_CHAR('`') PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)  PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)           PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)       PORT_CHAR(';') PORT_CHAR('+') PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)       PORT_CHAR(':') PORT_CHAR('*') PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)   PORT_CHAR(']') PORT_CHAR('}')

	PORT_START("JOY")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_PLAYER(1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP)    PORT_PLAYER(1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT)  PORT_PLAYER(1)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN)  PORT_PLAYER(1)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_PLAYER(2)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP)    PORT_PLAYER(2)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT)  PORT_PLAYER(2)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN)  PORT_PLAYER(2)

	PORT_START("RESET")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC)) /* 1st line, 1st key from right! */

	PORT_START("DIPS")
	PORT_DIPNAME(0x01, 0x01, "KRX: BASIC[on]/MSX[off]") //switching between BASIC and MSX ROMs which share same address area
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x01, DEF_STR( On ))
	PORT_DIPNAME(0x02, 0x00, "KBI: AUTOSTART")  //pages out cart and starts loading from tape
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x02, DEF_STR( On ))
	PORT_DIPNAME(0x04, 0x00, "KBI: 32kb only") //compatible with em-5
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x04, DEF_STR( On ))
INPUT_PORTS_END

//-------------------------------------------------
//  I8255 Interface
//-------------------------------------------------

u8 m5_state::ppi_pa_r()
{
	return m_fd5_data;
}

u8 m5_state::ppi_pc_r()
{
	/*

	    bit     description

	    0       ?
	    1       ?
	    2       ?
	    3
	    4       STBA
	    5
	    6       ACKA
	    7

	*/

	return (
			/* FD5 bit 0-> M5 bit 2 */
			((m_fd5_com & 0x01)<<2) |
			/* FD5 bit 2-> M5 bit 1 */
			((m_fd5_com & 0x04)>>1) |
			/* FD5 bit 1-> M5 bit 0 */
			((m_fd5_com & 0x02)>>1)
	);
}

void m5_state::ppi_pa_w(u8 data)
{
	m_fd5_data = data;
}

void m5_state::ppi_pb_w(u8 data)
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4
	    5
	    6
	    7

	*/

	if (data == 0xf0)
	{
		m_fd5cpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
		m_fd5cpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
	}
}

void m5_state::ppi_pc_w(u8 data)
{
	/*

	    bit     description

	    0
	    1
	    2
	    3       INTRA
	    4
	    5       IBFA
	    6
	    7       OBFA

	*/

	m_intra = BIT(data, 3);
	m_ibfa = BIT(data, 5);
	m_obfa = BIT(data, 7);
}

//-------------------------------------------------
//  upd765_interface fdc_intf
//-------------------------------------------------

void m5_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_M5_FORMAT);
}

static void m5_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

static void m5_cart(device_slot_interface &device)
{
	device.option_add_internal("std",  M5_ROM_STD);
	device.option_add_internal("ram",  M5_ROM_RAM);
}

//-------------------------------------------------
//  z80_daisy_config m5_daisy_chain
//-------------------------------------------------

static const z80_daisy_config m5_daisy_chain[] =
{
	{ "ctc" },
	{ nullptr }
};


//-------------------------------------------------
//  BRNO mod code below
//-------------------------------------------------


//-------------------------------------------------
//  ADDRESS_MAP( m5_mem_brno )
//-------------------------------------------------


void brno_state::m5_mem_brno(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).view(m_ram_view);
	m_ram_view[0](0x0000, 0xffff).rw(FUNC(brno_state::ramdisk_r), FUNC(brno_state::ramdisk_w));
	map(0x0000, 0x7fff).view(m_rom_view);
	m_rom_view[0](0x0000, 0x3fff).rom().region("maincpu", 0).unmapw();
	m_rom_view[0](0x7000, 0x7fff).ram();
}

//-------------------------------------------------
//  ADDRESS_MAP( brno_io )
//-------------------------------------------------
void brno_state::brno_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x03).mirror(0xff0c).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x10, 0x11).mirror(0xff0e).rw("tms9928a", FUNC(tms9928a_device::read), FUNC(tms9928a_device::write));
	map(0x20, 0x20).mirror(0xff0f).w("sgc", FUNC(sn76489a_device::write));
	map(0x30, 0x30).mirror(0xff00).portr("Y0");
	map(0x31, 0x31).mirror(0xff00).portr("Y1");
	map(0x32, 0x32).mirror(0xff00).portr("Y2");
	map(0x33, 0x33).mirror(0xff00).portr("Y3");
	map(0x34, 0x34).mirror(0xff00).portr("Y4");
	map(0x35, 0x35).mirror(0xff00).portr("Y5");
	map(0x36, 0x36).mirror(0xff00).portr("Y6");
	map(0x37, 0x37).mirror(0xff00).portr("JOY");
	map(0x40, 0x40).mirror(0xff0f).w("cent_data_out", FUNC(output_latch_device::write));
	map(0x50, 0x50).mirror(0xff0f).rw(FUNC(brno_state::sts_r), FUNC(brno_state::com_w));
	map(0x60, 0x61).mirror(0xff02).rw("sio", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x64, 0x64).select(0xf000).mirror(0x0f03).w(FUNC(brno_state::mmu_w));         //  MMU - page select (ramdisk memory paging)
	map(0x68, 0x68).mirror(0xff03).w(FUNC(brno_state::ramsel_w));                     //  CASEN
	map(0x6c, 0x6c).mirror(0xff03).w(FUNC(brno_state::romsel_w));                     //  RAMEN
	map(0x70, 0x73).mirror(0xff00).rw("pio", FUNC(i8255_device::read), FUNC(i8255_device::write)); //  PIO
	map(0x78, 0x7b).mirror(0xff00).rw(m_wdfdc, FUNC(wd_fdc_device_base::read), FUNC(wd_fdc_device_base::write));   //  WD2797 registers -> 78 - status/cmd, 79 - track #, 7a - sector #, 7b - data
	map(0x7c, 0x7c).mirror(0xff03).w(FUNC(brno_state::fd_w));                         //  drive select
}


u8 brno_state::ramdisk_r(offs_t offset)
{
	offset = (offset & 0x0fff) | u32(m_rammap[offset >> 12]) << 12;
	if (offset < m_ram->size())
		return m_ram->read(offset);
	else
		return 0xff;
}

void brno_state::ramdisk_w(offs_t offset, u8 data)
{
	offset = (offset & 0x0fff) | u32(m_rammap[offset >> 12]) << 12;
	if (offset < m_ram->size())
		m_ram->write(offset, data);
}

void brno_state::mmu_w(offs_t offset, u8 data)
{
	u8 ramcpu = offset >> 12;
	u8 rambank = ~data;
	m_rammap[ramcpu] = rambank;

	//logerror("RAMdisk page change(CPURAM<=BANK): &%X000<=%02X at %s\n", ramcpu, rambank, machine().describe_context());
}

void brno_state::ramsel_w(u8 data) //out 6b
{
	// 0=access to ramdisk enabled, 0xff=ramdisk access disabled(data protection), &80=ROM2+48k RAM, &81=ROM2+4k RAM
	// Note that RAM disk is not banked in 0000-3FFF or 7000-7FFF unless ROM is disabled
	if (!BIT(data, 0))
		m_ram_view.select(0);
	else
		m_ram_view.disable();

	//logerror("CASEN change: out (&6b),%x\n",data);
}

void brno_state::romsel_w(u8 data) //out 6c
{
	// 0=rom enable; 0xff=rom+sord ram disabled (ramdisk visible)
	if (!BIT(data, 0))
		m_rom_view.select(0);
	else
		m_rom_view.disable();

	//logerror("RAMEN change: out (&6c),%x\n",data);
}


//-------------------------------------------------
//  FD port 7c - Floppy select
//-------------------------------------------------

void brno_state::fd_w(u8 data)
{
	floppy_image_device *floppy;
	m_floppy = nullptr;
	int disk = 0;

	floppy = m_floppy0->get_device();
	if (floppy)
	{
		if(BIT(data,0))
		{
			m_floppy= floppy;
			disk=1;
		}
		else
		{
			floppy->mon_w(1);
		}
	}
	floppy = m_floppy1->get_device();
	if (floppy)
	{
		if(BIT(data,1))
		{
			m_floppy= floppy;
			disk=2;
		}
		else
		{
			floppy->mon_w(1);
		}
	}

	m_wdfdc->set_floppy(m_floppy);
	if (m_floppy)
	{
		m_floppy->set_rpm(300);
		m_floppy->mon_w(0);
		logerror("Select floppy %d\n", disk);
	}
}



static void brno_floppies(device_slot_interface &device)
{
	device.option_add("35hd", FLOPPY_35_DD);
}


//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( m5 )
//-------------------------------------------------
void m5_state::machine_start()
{
	// register for state saving
	save_item(NAME(m_fd5_data));
	save_item(NAME(m_fd5_com));
	save_item(NAME(m_intra));
	save_item(NAME(m_ibfa));
	save_item(NAME(m_obfa));
	save_item(NAME(m_centronics_busy));
}

void m5_state::machine_reset()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	std::string region_tag;

	//is ram/rom cart plugged in?
	if (m_cart1->exists())
	{
		if (m_cart1->get_type() > 0)
			m_cart_ram = m_cart1;
		else
			m_cart = m_cart1;
	}

	if (m_cart2->exists())
	{
		if (m_cart2->get_type() > 0)
			m_cart_ram = m_cart2;
		else
			m_cart = m_cart2;
	}

	// no cart inserted - there is nothing to do - not allowed in original Sord m5
	if (m_cart_ram == nullptr && m_cart == nullptr)
	{
	//  membank("bank1r")->set_base(memregion("maincpu")->base());
		program.unmap_write(0x0000, 0x1fff);
	//  program.unmap_readwrite(0x2000, 0x6fff); //if you uncomment this line Sord starts cassette loading but it is not correct on real hw
		program.unmap_readwrite(0x8000, 0xffff);
		return;
	}

	//cart is ram module
	if (m_cart_ram)
	{
		m_ram_type=m_cart_ram->get_type();

		m_cart_rom = memregion(region_tag.assign(m_cart_ram->tag()).append(M5SLOT_ROM_REGION_TAG).c_str());
		//memory_region *ram_region = memregion(region_tag.assign(m_cart_ram->tag()).append(":ram").c_str());

		switch (m_ram_type)
		{
			case EM_5:
				program.install_rom(0x0000, 0x1fff, memregion("maincpu")->base());
				program.unmap_write(0x0000, 0x1fff);
				program.install_readwrite_handler(0x8000, 0xffff, read8sm_delegate(*m_cart_ram, FUNC(m5_cart_slot_device::read_ram)), write8sm_delegate(*m_cart_ram, FUNC(m5_cart_slot_device::write_ram)));
				if (m_cart)
				{
					program.install_read_handler(0x2000, 0x6fff, read8sm_delegate(*m_cart, FUNC(m5_cart_slot_device::read_rom)));
					program.unmap_write(0x2000, 0x6fff);
				}
				break;
#if 0 // FIXME: disabled for now
			case MEM64KBI:
				program.install_rom(0x0000, 0x1fff, memregion("maincpu")->base());
				program.unmap_write(0x0000, 0x1fff);
				program.install_ram(0x8000, 0xffff, ram_region->base()+0x8000);

				//if AUTOSTART is on then page out cart and start tape loading
				if (m_cart && ((m_DIPS->read() & 2) != 2))
				{
					program.install_read_handler(0x2000, 0x3fff, read8sm_delegate(*m_cart, FUNC(m5_cart_slot_device::read_rom)));
					program.unmap_write(0x2000, 0x3fff);
				}
				else
					program.unmap_readwrite(0x2000, 0x6fff); //monitor rom is testing this area for 0xFFs otherwise thinks there is some ROM cart plugged in

				break;
			case MEM64KBF:
				program.unmap_write(0x0000, 0x6fff);
				membank("bank1r")->set_base(memregion("maincpu")->base());
				membank("bank2r")->set_base(m_cart_rom->base());
				membank("bank3r")->set_base(m_cart_rom->base()+0x2000);
				membank("bank4r")->set_base(m_cart_rom->base()+0x4000);
				membank("bank5r")->set_base(ram_region->base()+0x8000); membank("bank5w")->set_base(ram_region->base()+0x8000);
				membank("bank6r")->set_base(ram_region->base()+0xc000); membank("bank6w")->set_base(ram_region->base()+0xc000);
				break;
			case MEM64KRX:
				membank("bank1r")->set_base(memregion("maincpu")->base());    membank("bank1w")->set_base(ram_region->base());
				membank("bank2r")->set_base(m_cart_rom->base());            membank("bank2w")->set_base(ram_region->base()+0x2000);
				membank("bank3r")->set_base(m_cart_rom->base()+0x2000);     membank("bank3w")->set_base(ram_region->base()+0x4000);
				membank("bank4r")->set_base(ram_region->base()+0x6000);     membank("bank4w")->set_base(ram_region->base()+0x6000);

				//page in BASIC or MSX
				if ((m_DIPS->read() & 0x01))
				{
					membank("bank5r")->set_base(m_cart_rom->base()+0x6000); membank("bank5w")->set_base(ram_region->base()+0x8000);
					membank("bank6r")->set_base(m_cart_rom->base()+0xa000); membank("bank6w")->set_base(ram_region->base()+0xc000);
				}
				else
				{
					membank("bank5r")->set_base(m_cart_rom->base()+0xe000);  membank("bank5w")->set_base(ram_region->base()+0x8000);
					membank("bank6r")->set_base(m_cart_rom->base()+0x12000); membank("bank6w")->set_base(ram_region->base()+0xc000);
				}
				break;
#endif
			default:
				program.unmap_readwrite(0x8000, 0xffff);
		}
		//I don't have idea what to do with savestates, please someone take care of it
		//m_cart_ram->save_ram();
	}
	else
		//ram cart wasn't found so if rom cart present install it
		if (m_cart)
		{
			program.install_rom(0x0000, 0x1fff, memregion("maincpu")->base());
			program.unmap_write(0x0000, 0x1fff);
			program.install_read_handler(0x2000, 0x6fff, read8sm_delegate(*m_cart, FUNC(m5_cart_slot_device::read_rom)));
			program.unmap_write(0x2000, 0x6fff);
		}
	m_ram_mode=0;
}



void brno_state::machine_start()
{
	std::fill(std::begin(m_rammap), std::end(m_rammap), 0);

	save_item(NAME(m_rammap));
	save_item(NAME(m_centronics_busy));
}

void brno_state::machine_reset()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	//is ram/rom cart plugged in?
	if (m_cart1->exists())
	{
		if (m_cart1->get_type() > 0)
			m_cart_ram=m_cart1;
		else
			m_cart=m_cart1;
	}
	if (m_cart2->exists())
	{
		if (m_cart2->get_type() > 0)
			m_cart_ram=m_cart2;
		else
			m_cart=m_cart2;
	}

	if (m_cart)
	{
		program.install_read_handler(0x2000, 0x6fff, read8sm_delegate(*m_cart, FUNC(m5_cart_slot_device::read_rom)));
		//program.unmap_write(0x2000, 0x6fff);
	}

	// enable ROM1+ROM2
	m_rom_view.select(0);
	m_ram_view.disable();

	floppy_image_device *floppy = nullptr;
	floppy = m_floppy0->get_device();
	m_wdfdc->set_floppy(floppy);
	floppy->mon_w(0);
}

//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  machine_config( m5 )
//-------------------------------------------------

void m5_state::m5(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 10.738635_MHz_XTAL / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &m5_state::m5_mem);
	m_maincpu->set_addrmap(AS_IO, &m5_state::m5_io);
	m_maincpu->set_daisy_config(m5_daisy_chain);

	Z80(config, m_fd5cpu, 16_MHz_XTAL / 4);
	m_fd5cpu->set_addrmap(AS_PROGRAM, &m5_state::fd5_mem);
	m_fd5cpu->set_addrmap(AS_IO, &m5_state::fd5_io);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SN76489A(config, "sgc", 10.738635_MHz_XTAL / 3).add_route(ALL_OUTPUTS, "mono", 1.00);

	// devices
	Z80CTC(config, m_ctc, 10.738635_MHz_XTAL / 3);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->set_clk<2>(10.738635_MHz_XTAL / 24);
	// CK0 = EXINT, CK1 = GND, CK2 = TCK, CK3 = VDP INT
	// ZC2 = EXCLK

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(m5_state::write_centronics_busy));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(sordm5_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("m5_cass");

	I8255(config, m_ppi);
	m_ppi->in_pa_callback().set(FUNC(m5_state::ppi_pa_r));
	m_ppi->out_pa_callback().set(FUNC(m5_state::ppi_pa_w));
	m_ppi->out_pb_callback().set(FUNC(m5_state::ppi_pb_w));
	m_ppi->in_pc_callback().set(FUNC(m5_state::ppi_pc_r));
	m_ppi->out_pc_callback().set(FUNC(m5_state::ppi_pc_w));

	UPD765A(config, m_fdc, 16_MHz_XTAL / 4, true, true); // clocked by SED9420C
	m_fdc->intrq_wr_callback().set_inputline(m_fd5cpu, INPUT_LINE_IRQ0);
	FLOPPY_CONNECTOR(config, "upd765:0", m5_floppies, "525dd", m5_state::floppy_formats);

	// cartridge
	M5_CART_SLOT(config, m_cart1, m5_cart, nullptr);
	M5_CART_SLOT(config, m_cart2, m5_cart, nullptr);

	// software lists
	SOFTWARE_LIST(config, "cart_list").set_original("m5_cart");
	SOFTWARE_LIST(config, "cass_list").set_original("m5_cass");
	//SOFTWARE_LIST(config, "flop_list").set_original("m5_flop");

	// internal ram
	//68K is not possible, 'cos internal ram always overlays any expansion memory in that area
	//RAM(config, m_ram).set_default_size("4K").set_extra_options("36K,64K");
}


//-------------------------------------------------
//  machine_config( ntsc )
//-------------------------------------------------

void m5_state::ntsc(machine_config &config)
{
	m5(config);
	// video hardware
	tms9928a_device &vdp(TMS9928A(config, "tms9928a", 10.738635_MHz_XTAL));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	vdp.int_callback().set(m_ctc, FUNC(z80ctc_device::trg3)).invert();
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);
}


//-------------------------------------------------
//  machine_config( pal )
//-------------------------------------------------

void m5_state::pal(machine_config &config)
{
	m5(config);
	// video hardware
	tms9929a_device &vdp(TMS9929A(config, "tms9928a", 10.738635_MHz_XTAL));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	vdp.int_callback().set(m_ctc, FUNC(z80ctc_device::trg3)).invert();
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);
}

//-------------------------------------------------
//  machine_config( m5p_brno )
//-------------------------------------------------


void brno_state::brno(machine_config &config)
{
	m5(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &brno_state::m5_mem_brno);
	m_maincpu->set_addrmap(AS_IO, &brno_state::brno_io);


	//remove devices used for fd5 floppy
	config.device_remove("z80fd5");
	config.device_remove("ppi");
	config.device_remove("upd765");

	// video hardware
	tms9929a_device &vdp(TMS9929A(config, "tms9928a", 10.738635_MHz_XTAL));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	vdp.int_callback().set(m_ctc, FUNC(z80ctc_device::trg3)).invert();
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	// RAM disk (maximum is 1024kB 256x 4kB banks)
	RAM(config, m_ram).set_default_size("512K").set_extra_options("256K,768K,1M");

	i8251_device &sio(I8251(config, "sio", 10.738635_MHz_XTAL / 3));
	sio.txd_handler().set("serial", FUNC(rs232_port_device::write_txd));
	sio.rts_handler().set("serial", FUNC(rs232_port_device::write_rts));

	rs232_port_device &serial(RS232_PORT(config, "serial", default_rs232_devices, nullptr));
	serial.rxd_handler().set("sio", FUNC(i8251_device::write_rxd));
	serial.cts_handler().set("sio", FUNC(i8251_device::write_cts));

	m_ctc->zc_callback<2>().set("sio", FUNC(i8251_device::write_txc));
	m_ctc->zc_callback<2>().append("sio", FUNC(i8251_device::write_rxc));

	I8255(config, "pio");

	// floppy
	WD2797(config, m_wdfdc, 4_MHz_XTAL / 4);
	FLOPPY_CONNECTOR(config, m_floppy0, brno_floppies, "35hd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy1, brno_floppies, "35hd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	// only one floppy drive
	//config.device_remove("wd:1");

	//SNAPSHOT(config, "snapshot", "rmd", 0).set_load_callback(brno_state::snapshot_cb));

	// software list
	SOFTWARE_LIST(config, "flop_list").set_original("m5_flop");
}


//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( m5 )
//-------------------------------------------------

ROM_START( m5 )
	ROM_REGION( 0x7000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "sordjap.ic21", 0x0000, 0x2000, CRC(92cf9353) SHA1(b0a4b3658fde68cb1f344dfb095bac16a78e9b3e) )

	ROM_REGION( 0x4000, "z80fd5", 0 )
	ROM_LOAD( "sordfd5.rom", 0x0000, 0x4000, CRC(7263bbc5) SHA1(b729500d3d2b2e807d384d44b76ea5ad23996f4a))
ROM_END


//-------------------------------------------------
//  ROM( m5p )
//-------------------------------------------------

ROM_START( m5p )
	ROM_REGION( 0x7000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "sordint.ic21", 0x0000, 0x2000, CRC(78848d39) SHA1(ac042c4ae8272ad6abe09ae83492ef9a0026d0b2) )

	ROM_REGION( 0x4000, "z80fd5", 0 )
	ROM_LOAD( "sordfd5.rom", 0x0000, 0x4000, CRC(7263bbc5) SHA1(b729500d3d2b2e807d384d44b76ea5ad23996f4a))
ROM_END

//-------------------------------------------------
//  ROM( brno )
//-------------------------------------------------

ROM_START( m5p_brno )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "sordint.ic21", 0x0000, 0x2000, CRC(78848d39) SHA1(ac042c4ae8272ad6abe09ae83492ef9a0026d0b2)) // monitor rom
	ROM_LOAD( "brno_win.rom", 0x2000, 0x2000, CRC(f4cfb2ee) SHA1(23f41d2d9ac915545409dd0163f3dc298f04eea2)) //windows
	//ROM_LOAD( "brno_rom12.rom", 0x2000, 0x4000, CRC(cac52406) SHA1(91f6ba97e85a2b3a317689635d425ee97413bbe3)) //windows+BI
	//ROM_LOAD( "brno_boot.rom", 0x2000, 0xd80, CRC(60008729) SHA1(fb26e2ae9f74b0ae0d723b417a038a8ef3d72782))
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY  FULLNAME                 FLAGS
COMP( 1983, m5,       0,      0,      ntsc,    m5,    m5_state,   empty_init, "Sord",  "m.5 (Japan)",           0 )
COMP( 1983, m5p,      m5,     0,      pal,     m5,    m5_state,   empty_init, "Sord",  "m.5 (Europe)",          0 )
COMP( 1983, m5p_brno, m5,     0,      brno,    m5,    brno_state, empty_init, "Sord",  "m.5 (Europe) BRNO mod", 0 )



m6805evs.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/******************************************************************************************************

Motorola M68HC05EVS evaluation system

Chips:
Main board: XC68HC26P, R65C52P2, MS62256l-70PC, MS6264L-70PC, eprom. Xtal = 3.6864MHz
Emulator board: MC68HC705P9CP, undumped 28-pin prom. Xtal = 4MHz

R65C52 = Dual ACIA with inbuilt baud rate divider, uses 8 addresses, uses the 3.6864MHz crystal
XC68HC26P = PPI (3 ports), uses 8 addresses.

2014-01-12 Skeleton driver

Memory map for MC68HC705P9:
0000, 0000  PORTA       Port A data register
0001, 0001  PORTB       Port B data register
0002, 0002  PORTC       Port C data register
0003, 0003  PORTD       Port D data register
0004, 0004  DDRA        Data direction register A
0005, 0005  DDRB        Data direction register B
0006, 0006  DDRC        Data direction register C
0007, 0007  DDRD        Data direction register D
0008, 0009              unimplemented
000A, 000A  SCR         SIOP control register
000B, 000B  SSR         SIOP status register
0009, 0009  SDR         SIOP data register
000D, 0011              unimplemented
0012, 0012  TCR         Timer control register
0013, 0013  TDR         Timer data register
0014, 0014  ICRH        Input capture register high
0015, 0015  ICRL        Input capture register low
0016, 0016  OCRH        Output compare register high
0017, 0017  OCRL        Output compare register low
0018, 0018  TRH         Timer register high
0019, 0019  TRL         Timer register low
001A, 001A  ATRH        Alternate timer register high
001B, 001B  ATRL        Alternate timer register low
001C, 001C  EPROG       EPROM programming register
001D, 001D  ADDR        ADC data register
001E, 001E  ADSCR       ADC status/control register
001F, 001F              reserved
0020, 004F              Page zero user EPROM
0050, 007F              unimplemented
0080, 00FF              RAM
0100, 08FF              User EPROM
0900, 0900  MOR         Mask option register
0901, 1EFF              unimplemented
1F00, 1FEF              Bootloader ROM
1FF1, 1FF7              reserved
1FF8, 1FF9              Timer interrupt vector
1FFA, 1FFB              External interrupt vector
1FFC, 1FFD              Software interrupt vector
1FFE, 1FFF              Reset vector


ToDo:
- Everything

******************************************************************************************************/

#include "emu.h"
#include "cpu/m6805/m68hc05.h"


namespace {

class m6805evs_state : public driver_device
{
public:
	m6805evs_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void m6805evs(machine_config &config);

private:
	[[maybe_unused]] void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	virtual void machine_reset() override ATTR_COLD;
};


void m6805evs_state::mem_map(address_map &map)
{
	map.unmap_value_high();

	//map(0x0800, 0x1fff).rom().region("eprom", 0x0800);
	//map(0xfff0, 0xffff).rom().region("eprom", 0xfff0);
}

static INPUT_PORTS_START( m6805evs )
INPUT_PORTS_END

void m6805evs_state::machine_reset()
{
}

void m6805evs_state::m6805evs(machine_config &config)
{
	// FIXME: should this be MC68HC05E0 instead?
	// (MC68HC705P9 doesn't use an external EPROM either and is also incompatible)
	M68HC05C8(config, m_maincpu, XTAL(4'000'000));
	//m_maincpu->set_addrmap(AS_PROGRAM, &m6805evs_state::mem_map);
}

ROM_START(m6805evs)
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD( "evsbug12.bin", 0x0000, 0x2000, CRC(8b581aef) SHA1(eacf425cc8a042085ccc4097cc61570b633b1e38) )

	ROM_REGION(0x2000, "mcu", 0)
	ROM_LOAD( "mc68hc705p9cp.bin", 0x0000, 0x2000, NO_DUMP)
ROM_END

} // anonymous namespace


/***************************************************************************

  Game driver(s)

***************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY     FULLNAME      FLAGS
COMP( 1990, m6805evs, 0,      0,      m6805evs, m6805evs, m6805evs_state, empty_init, "Motorola", "M68HC05EVS", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



m68705prg.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Vas Crabb
/*
 * Simple 68705 programmer
 * The 68705-based parts have an internal bootstrap ROM that can be used
 * to copy data to the internal EPROM.  The circuit just consists of a
 * 12-bit counter, a ROM, and some discrete glue attached to port B.
 * Data is read on port A.
 *
 * Put the external ROM in cart1 and the initial MCU image in cart2
 * (with nothing in cart2 it assumes a blank MCU).
 *
 * To do a dry run and verify, just toggle off reset (button 1).  The
 * bootstrap will run through the programming procedure but EPROM write
 * will be disabled.  It will then proceed to compare the EPROM contents
 * to the external ROM contents.
 *
 * To program the EPROM, toggle Vpp enable (button 2) and then toggle
 * off reset.  The bootstrap will write the contents of the external ROM
 * to the internal EPROM and then verify the result.
 *
 * When programming is complete, the "Programmed" LED will be lit.  When
 * verification is complete, the "Verified" LED will be lit.  If
 * verification fails, the program stops and the "Address" digits show
 * the address one past the location that failed for P3/P5, or the
 * location that failed for R3/U3.
 */
#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/m6805/m68705.h"

#include "m68705prg.lh"


namespace {

class m68705prg_state_base : public driver_device
{
public:
	m68705prg_state_base(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_sw(*this, "SW")
		, m_mcu_region(*this, "mcu")
		, m_eprom_image(*this, "eprom_image")
		, m_mcu_image(*this, "mcu_image")
		, m_digits(*this, "digit%u", 0U)
		, m_leds(*this, "led%u", 0U)
		, m_input_poll_timer(nullptr)
		, m_addr(0x0000)
		, m_pb_val(0xff)
	{
	}

protected:
	void m68705prg(machine_config &config);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(eprom_load)
	{
		auto const desired(m_mcu_region.bytes());
		auto const actual(m_eprom_image->common_get_size("rom"));
		if (desired > actual)
		{
			return std::make_pair(
					image_error::INVALIDLENGTH,
					util::string_format("Unsupported EPROM size (must be at least %u bytes)", desired));
		}
		else
		{
			m_eprom_image->rom_alloc(desired, GENERIC_ROM8_WIDTH, ENDIANNESS_BIG);
			m_eprom_image->common_load_rom(m_eprom_image->get_rom_base(), desired, "rom");
			return std::make_pair(std::error_condition(), std::string());
		}
	}

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(mcu_load)
	{
		auto const desired(m_mcu_region.bytes());
		auto const actual(m_mcu_image->common_get_size("rom"));
		if (desired != actual)
		{
			return std::make_pair(
					image_error::INVALIDLENGTH,
					util::string_format("Incorrect MCU EPROM size (must be %u bytes)", desired));
		}
		else
		{
			m_mcu_image->common_load_rom(&m_mcu_region[0], actual, "rom");
			return std::make_pair(std::error_condition(), std::string());
		}
	}

	virtual void machine_start() override
	{
		m_digits.resolve();
		m_leds.resolve();

		save_item(NAME(m_addr));
		save_item(NAME(m_pb_val));

		m_addr = 0x0000;
		m_pb_val = 0xff;

		m_input_poll_timer = timer_alloc(FUNC(m68705prg_state_base::input_poll_callback), this);
		m_input_poll_timer->adjust(attotime::from_hz(120), 0, attotime::from_hz(120));
	}

	virtual void machine_reset() override
	{
		m_digits[0] = s_7seg[(m_addr >> 0) & 0x0f];
		m_digits[1] = s_7seg[(m_addr >> 4) & 0x0f];
		m_digits[2] = s_7seg[(m_addr >> 8) & 0x0f];
	}

	virtual TIMER_CALLBACK_MEMBER(input_poll_callback)
	{
	}

	required_ioport                         m_sw;
	required_region_ptr<u8>                 m_mcu_region;
	required_device<generic_slot_device>    m_eprom_image;
	required_device<generic_slot_device>    m_mcu_image;
	output_finder<3>                        m_digits;
	output_finder<4>                        m_leds;

	emu_timer *     m_input_poll_timer;

	u16             m_addr;
	u8              m_pb_val;

	static u8 const s_7seg[16];
};

u8 const m68705prg_state_base::s_7seg[16] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71 };


template <typename Device>
class m68705prg_state : public m68705prg_state_base
{
public:
	m68705prg_state(const machine_config &mconfig, device_type type, const char *tag)
		: m68705prg_state_base(mconfig, type, tag)
		, m_mcu(*this, "mcu")
	{
	}

	void prg(machine_config &config);

protected:
	void pb_w(u8 data)
	{
		// PB4: address counter reset (active high)
		// PB3: address counter clock (falling edge)
		// PB2: Verified LED (active low)
		// PB1: Programmed LED (active low)
		// PB0: apply Vpp (active low)

		if (BIT(data, 4))
			m_addr = 0x0000;
		else if (!BIT(data, 3) && BIT(m_pb_val, 3))
			m_addr = (m_addr + 1) & 0x0fff;
		m_leds[0] = !BIT(data, 2);
		m_leds[1] = !BIT(data, 1);
		m_leds[3] = !BIT(data, 0) && BIT(m_sw->read(), 1);
		m_mcu->set_input_line(M68705_VPP_LINE, (!BIT(data, 0) && BIT(m_sw->read(), 1)) ? ASSERT_LINE : CLEAR_LINE);

		m_pb_val = data;

		u8 const *const ptr(m_eprom_image->get_rom_base());
		m_mcu->pa_w(ptr ? ptr[m_addr & (m_mcu_region.length() - 1)] : 0xff);

		m_digits[0] = s_7seg[(m_addr >> 0) & 0x0f];
		m_digits[1] = s_7seg[(m_addr >> 4) & 0x0f];
		m_digits[2] = s_7seg[(m_addr >> 8) & 0x0f];
	}

	virtual void machine_reset() override
	{
		m68705prg_state_base::machine_reset();

		m_sw->field(0x01)->live().value = 0;
		m_sw->field(0x02)->live().value = 0;

		m_leds[2] = 1;
		m_leds[3] = 0;

		m_mcu->set_input_line(M68705_IRQ_LINE, ASSERT_LINE);
		m_mcu->set_input_line(M68705_VPP_LINE, CLEAR_LINE);
		m_mcu->set_input_line(M68705_VIHTP_LINE, ASSERT_LINE);
		m_mcu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
	}

	virtual TIMER_CALLBACK_MEMBER(input_poll_callback) override
	{
		ioport_value const switches(m_sw->read());
		bool const reset(!BIT(switches, 0));
		bool const vpp(BIT(switches, 1) && !BIT(m_pb_val, 0));

		m_leds[2] = reset;
		m_leds[3] = vpp;

		m_mcu->set_input_line(M68705_VPP_LINE, vpp ? ASSERT_LINE : CLEAR_LINE);
		m_mcu->set_input_line(INPUT_LINE_RESET, reset ? ASSERT_LINE : CLEAR_LINE);
	}

	required_device<Device> m_mcu;
};

typedef m68705prg_state<m68705p3_device> p3prg_state;
typedef m68705prg_state<m68705p5_device> p5prg_state;
typedef m68705prg_state<m68705r3_device> r3prg_state;
typedef m68705prg_state<m68705u3_device> u3prg_state;


INPUT_PORTS_START(m68705prg)
	PORT_START("SW")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_TOGGLE PORT_NAME("Reset")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_TOGGLE PORT_NAME("Vpp")
INPUT_PORTS_END


void m68705prg_state_base::m68705prg(machine_config &config)
{
	config.set_perfect_quantum("mcu");

	GENERIC_SOCKET(config, m_eprom_image, generic_plain_slot, "eprom", "bin,rom");
	m_eprom_image->set_device_load(FUNC(m68705prg_state_base::eprom_load));

	GENERIC_SOCKET(config, m_mcu_image, generic_plain_slot, "mcu", "bin,rom");
	m_mcu_image->set_device_load(FUNC(m68705prg_state_base::mcu_load));

	config.set_default_layout(layout_m68705prg);
}

template<> void p3prg_state::prg(machine_config &config)
{
	m68705prg(config);
	M68705P3(config, m_mcu, 1_MHz_XTAL);
	m_mcu->portb_w().set(FUNC(p3prg_state::pb_w));
}

template<> void p5prg_state::prg(machine_config &config)
{
	m68705prg(config);
	M68705P5(config, m_mcu, 1_MHz_XTAL);
	m_mcu->portb_w().set(FUNC(p5prg_state::pb_w));
}

template<> void r3prg_state::prg(machine_config &config)
{
	m68705prg(config);
	M68705R3(config, m_mcu, 1_MHz_XTAL);
	m_mcu->portb_w().set(FUNC(r3prg_state::pb_w));
}

template<> void u3prg_state::prg(machine_config &config)
{
	m68705prg(config);
	M68705U3(config, m_mcu, 1_MHz_XTAL);
	m_mcu->portb_w().set(FUNC(u3prg_state::pb_w));
}


ROM_START( 705p3prg )
	ROM_REGION( 0x0800, "mcu", 0 )
	ROM_FILL(0x0000, 0x0800, 0x00)
ROM_END

ROM_START( 705p5prg )
	ROM_REGION( 0x0800, "mcu", 0 )
	ROM_FILL(0x0000, 0x0800, 0x00)
ROM_END

ROM_START( 705r3prg )
	ROM_REGION( 0x1000, "mcu", 0 )
	ROM_FILL(0x0000, 0x1000, 0x00)
ROM_END

ROM_START( 705u3prg )
	ROM_REGION( 0x1000, "mcu", 0 )
	ROM_FILL(0x0000, 0x1000, 0x00)
ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT    COMPAT  MACHINE  INPUT      CLASS        INIT        COMPANY     FULLNAME                FLAGS
COMP( 1984, 705p5prg, 0,        0,      prg,     m68705prg, p5prg_state, empty_init, "Motorola", "MC68705P5 Programmer", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1984, 705p3prg, 705p5prg, 0,      prg,     m68705prg, p3prg_state, empty_init, "Motorola", "MC68705P3 Programmer", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1984, 705r3prg, 705p5prg, 0,      prg,     m68705prg, r3prg_state, empty_init, "Motorola", "MC68705R3 Programmer", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1984, 705u3prg, 705p5prg, 0,      prg,     m68705prg, u3prg_state, empty_init, "Motorola", "MC68705U3 Programmer", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )



m79152pc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic,AJR
/***************************************************************************

    Mera-Elzab 79152pc

    This system provides a half-featured emulation of the ADM-3A or similar
    terminals by TeleVideo and Wyse. The “half-featured” part is that some
    commands are not recognized at all and others are merely filtered out.

    The 8035 here serves as a soft CRTC, counting horizontal scans and
    outputting row addresses (dependent on scrolling) and vertical sync
    pulses. The present emulation produces incorrect video output at the
    vertical margins and is extremely prone to desyncing.

    “PC Shadow” is the name of the software this terminal either runs or
    interfaces with. The actual keyboard is unknown, but is almost
    certainly PC-XT compatible. The character set is a nonstandard variant
    of CP 437 that incorporates a few Polish letters.

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/i8212.h"
#include "machine/pit8253.h"
#include "machine/i8255.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "sound/beep.h"
#include "bus/centronics/ctronics.h"
#include "bus/rs232/rs232.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class m79152pc_state : public driver_device
{
public:
	m79152pc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_videoram(*this, "videoram")
		, m_attributes(*this, "attributes")
		, m_chargen(*this, "chargen")
		, m_maincpu(*this, "maincpu")
		, m_mcu(*this, "mcu")
		, m_uart(*this, "uart")
		, m_screen(*this, "screen")
		, m_beep(*this, "beep")
	{ }

	void m79152pc(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void beep_w(offs_t offset, uint8_t data);
	void latch_full_w(int state);
	int mcu_t0_r();
	int mcu_t1_r();
	void mcu_p1_w(u8 data);
	void mcu_p2_w(u8 data);
	void lc_reset_w(u8 data);

	TIMER_CALLBACK_MEMBER(hsync_on);
	TIMER_CALLBACK_MEMBER(hsync_off);

	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void screen_draw_line(bitmap_ind16 &bitmap, unsigned y);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void mcu_map(address_map &map) ATTR_COLD;
	void mcu_io_map(address_map &map) ATTR_COLD;

	required_shared_ptr<u8> m_videoram;
	required_shared_ptr<u8> m_attributes;
	required_region_ptr<u8> m_chargen;

	required_device<z80_device> m_maincpu;
	required_device<mcs48_cpu_device> m_mcu;
	required_device<z80sio_device> m_uart;
	required_device<screen_device> m_screen;
	required_device<beep_device> m_beep;

	u8 m_line_base = 0;
	u8 m_line_count = 0;
	bool m_latch_full = false;
	u8 m_mcu_p2 = 0;
	bool m_hsync = false;

	emu_timer *m_hsync_on_timer = nullptr;
	emu_timer *m_hsync_off_timer = nullptr;
};

void m79152pc_state::beep_w(offs_t offset, uint8_t data)
{
	m_beep->set_state(BIT(offset, 2));
}

void m79152pc_state::latch_full_w(int state)
{
	m_latch_full = state == ASSERT_LINE;
}

int m79152pc_state::mcu_t0_r()
{
	return m_latch_full ? 0 : 1;
}

int m79152pc_state::mcu_t1_r()
{
	return m_hsync ? 0 : 1;
}

void m79152pc_state::mcu_p1_w(u8 data)
{
	m_line_base = data;
}

void m79152pc_state::mcu_p2_w(u8 data)
{
	m_mcu_p2 = data;
}

void m79152pc_state::lc_reset_w(u8 data)
{
	m_line_count = (data >> 4) & 0xf;
}

void m79152pc_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).rom().region("maincpu", 0);
	map(0x4000, 0x47ff).ram();
	map(0x8000, 0x8fff).ram().share("videoram");
	map(0x9000, 0x9fff).ram().share("attributes");
}

void m79152pc_state::io_map(address_map &map)
{
	//map.unmap_value_high();
	map.global_mask(0xff);
	map(0x40, 0x43).rw(m_uart, FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
	map(0x44, 0x47).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x48, 0x4b).w("pit", FUNC(pit8253_device::write));
	map(0x4c, 0x4c).w("mculatch", FUNC(i8212_device::strobe));
	map(0x54, 0x57).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x58, 0x58).select(4).w(FUNC(m79152pc_state::beep_w));
}

void m79152pc_state::mcu_map(address_map &map)
{
	map(0x000, 0x7ff).rom().region("mcu", 0);
}

void m79152pc_state::mcu_io_map(address_map &map)
{
	map(0x00, 0x00).mirror(0xff).r("mculatch", FUNC(i8212_device::read)).w(FUNC(m79152pc_state::lc_reset_w));
}

/* Input ports */
static INPUT_PORTS_START( m79152pc )
INPUT_PORTS_END


TIMER_CALLBACK_MEMBER(m79152pc_state::hsync_on)
{
	m_screen->update_now();
	m_mcu->set_input_line(MCS48_INPUT_IRQ, ASSERT_LINE);
	m_hsync = true;
	m_line_count = (m_line_count + 1) & 0xf;
}

TIMER_CALLBACK_MEMBER(m79152pc_state::hsync_off)
{
	unsigned vpos = m_screen->vpos();
	m_mcu->set_input_line(MCS48_INPUT_IRQ, CLEAR_LINE);
	m_hsync_on_timer->adjust(m_screen->time_until_pos(vpos, 640));
	m_hsync = false;
}

void m79152pc_state::screen_draw_line(bitmap_ind16 &bitmap, unsigned y)
{
	const u16 ma = u16(m_line_base) << 4;
	const u8 ra = m_line_count & 0xf;

	u16 *p = &bitmap.pix(y++);

	for (u16 x = ma; x < ma + 80; x++)
	{
		// BIT(attr, 3) should probably be blinking
		// BIT(attr, 1) may be used for high-intensity text
		u8 chr = m_videoram[x];
		u8 attr = m_attributes[x];
		u8 gfx = m_chargen[(chr << 4) | (BIT(attr, 2) && ra == 15 ? 3 : ra)];
		if (BIT(attr, 0))
			gfx ^= 0xff;

		*p++ = BIT(gfx, 7);
		*p++ = BIT(gfx, 6);
		*p++ = BIT(gfx, 5);
		*p++ = BIT(gfx, 4);
		*p++ = BIT(gfx, 3);
		*p++ = BIT(gfx, 2);
		*p++ = BIT(gfx, 1);
		*p++ = BIT(gfx, 0);
	}
}

u32 m79152pc_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (unsigned y = cliprect.top(); y <= cliprect.bottom(); y++)
		screen_draw_line(bitmap, y);

	return 0;
}

/* F4 Character Displayer */
static const gfx_layout m79152pc_charlayout =
{
	8, 12,                  /* 8 x 16 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_m79152pc )
	GFXDECODE_ENTRY( "chargen", 0x0000, m79152pc_charlayout, 0, 1 )
GFXDECODE_END


void m79152pc_state::machine_start()
{
	m_latch_full = false;
	m_mcu_p2 = 0xff;
	m_line_base = 0;
	m_line_count = 0;
	m_hsync = false;

	m_hsync_on_timer = timer_alloc(FUNC(m79152pc_state::hsync_on), this);
	m_hsync_off_timer = timer_alloc(FUNC(m79152pc_state::hsync_off), this);
	m_hsync_off_timer->adjust(m_screen->time_until_pos(9, 0), 0, m_screen->scan_period());

	save_item(NAME(m_latch_full));
	save_item(NAME(m_mcu_p2));
	save_item(NAME(m_line_base));
	save_item(NAME(m_line_count));
	save_item(NAME(m_hsync));
}

static const z80_daisy_config daisy_chain[] =
{
	{ "ctc" },
	{ "uart" },
	{ nullptr }
};

void m79152pc_state::m79152pc(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 4'000'000); // UA880D
	m_maincpu->set_addrmap(AS_PROGRAM, &m79152pc_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &m79152pc_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	I8035(config, m_mcu, 6'000'000); // NEC D8035HLC
	m_mcu->set_addrmap(AS_PROGRAM, &m79152pc_state::mcu_map);
	m_mcu->set_addrmap(AS_IO, &m79152pc_state::mcu_io_map);
	m_mcu->t0_in_cb().set(FUNC(m79152pc_state::mcu_t0_r));
	m_mcu->t1_in_cb().set(FUNC(m79152pc_state::mcu_t1_r));
	m_mcu->p1_out_cb().set(FUNC(m79152pc_state::mcu_p1_w));
	m_mcu->p2_out_cb().set(FUNC(m79152pc_state::mcu_p2_w));
	m_mcu->p2_out_cb().append("ctc", FUNC(z80ctc_device::trg0)).bit(6); // determines beep duration
	m_mcu->p2_out_cb().append("ctc", FUNC(z80ctc_device::trg3)).bit(6);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(50 * 720 * 324, 720, 0, 640, 324, 0, 250);
	m_screen->set_screen_update(FUNC(m79152pc_state::screen_update));
	m_screen->set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_m79152pc);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	pit8253_device &pit(PIT8253(config, "pit", 0)); // КР580ВИ53
	pit.set_clk<1>(921600);
	pit.set_clk<2>(921600);
	pit.out_handler<1>().set(m_uart, FUNC(z80sio_device::txcb_w));
	pit.out_handler<2>().set(m_uart, FUNC(z80sio_device::rxcb_w));

	i8212_device &mculatch(I8212(config, "mculatch")); // CEMI UCY74S412
	mculatch.md_rd_callback().set_constant(0);
	mculatch.int_wr_callback().set(m_uart, FUNC(z80sio_device::ctsb_w)).invert();
	mculatch.int_wr_callback().append(FUNC(m79152pc_state::latch_full_w));

	i8255_device &ppi(I8255A(config, "ppi")); // NEC D8255AD-2
	ppi.out_pb_callback().set("printer", FUNC(centronics_device::write_data0)).bit(0);
	ppi.out_pb_callback().append("printer", FUNC(centronics_device::write_data1)).bit(1);
	ppi.out_pb_callback().append("printer", FUNC(centronics_device::write_data2)).bit(2);
	ppi.out_pb_callback().append("printer", FUNC(centronics_device::write_data3)).bit(3);
	ppi.out_pb_callback().append("printer", FUNC(centronics_device::write_data4)).bit(4);
	ppi.out_pb_callback().append("printer", FUNC(centronics_device::write_data5)).bit(5);
	ppi.out_pb_callback().append("printer", FUNC(centronics_device::write_data6)).bit(6);
	ppi.out_pb_callback().append("printer", FUNC(centronics_device::write_data7)).bit(7);
	ppi.out_pc_callback().set("printer", FUNC(centronics_device::write_strobe)).bit(1);

	centronics_device &printer(CENTRONICS(config, "printer", centronics_devices, nullptr));
	printer.ack_handler().set("ppi", FUNC(i8255_device::pc2_w));

	z80ctc_device &ctc(Z80CTC(config, "ctc", 4'000'000));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	ctc.set_clk<2>(921600);
	ctc.zc_callback<2>().set(m_uart, FUNC(z80sio_device::txca_w));
	ctc.zc_callback<2>().append(m_uart, FUNC(z80sio_device::rxca_w));

	// FIXME: Channel A should be the modem channel. Channel B should be a PC keyboard
	// that outputs XT scancodes, which are then rebroadcast through channel A!
	Z80SIO(config, m_uart, 4'000'000); // UB8560D
	m_uart->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_uart->out_txda_callback().set("keyboard", FUNC(rs232_port_device::write_txd));
	m_uart->out_dtra_callback().set("keyboard", FUNC(rs232_port_device::write_dtr));
	m_uart->out_rtsa_callback().set("keyboard", FUNC(rs232_port_device::write_rts));
	m_uart->out_txdb_callback().set("modem", FUNC(rs232_port_device::write_txd));
	m_uart->out_dtrb_callback().set("modem", FUNC(rs232_port_device::write_dtr));
	m_uart->out_rtsb_callback().set("modem", FUNC(rs232_port_device::write_rts));

	rs232_port_device &keyboard(RS232_PORT(config, "keyboard", default_rs232_devices, "keyboard"));
	keyboard.rxd_handler().set(m_uart, FUNC(z80sio_device::rxa_w));
	keyboard.cts_handler().set(m_uart, FUNC(z80sio_device::ctsa_w));
	rs232_port_device &modem(RS232_PORT(config, "modem", default_rs232_devices, nullptr));
	modem.rxd_handler().set(m_uart, FUNC(z80sio_device::rxb_w));
	//modem.cts_handler().set(m_uart, FUNC(z80sio_device::ctsb_w));

	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 1000);
	m_beep->add_route(ALL_OUTPUTS, "mono", 0.50);
}

/* ROM definition */
ROM_START( m79152pc )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "left.bin", 0x0000, 0x4000, CRC(8cd677fc) SHA1(7ad28f3ba984383f24a36639ca27fc1eb5a5d002))

	ROM_REGION( 0x1000, "chargen", ROMREGION_INVERT )
	ROM_LOAD( "right.bin", 0x0000, 0x1000, CRC(93f83fdc) SHA1(e8121b3d175c46c02828f43ec071a7d9c62e7c26)) // chargen

	ROM_REGION( 0x0800, "mcu", 0 )
	ROM_LOAD( "char.bin",  0x0000, 0x0800, CRC(da3792a5) SHA1(b4a4f0d61d8082b7909a346a5b01494c53cf8d05))

	ROM_REGION( 0x0200, "proms", 0 )
	ROM_LOAD( "7641apc.bin", 0x0000, 0x0200, NO_DUMP)
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY       FULLNAME         FLAGS
COMP( 198?, m79152pc, 0,      0,      m79152pc, m79152pc, m79152pc_state, empty_init, "Mera-Elzab", "MERA 79152 PC", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)



m8120.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 *  Motorola M8120 system.
 */

#include "emu.h"

#include "bus/vme/vme.h"
#include "bus/vme/mvme187.h"

namespace {

class m8120_state : public driver_device
{
public:
	m8120_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
	{
	}

	void m8120(machine_config &config);
};

void m8120_state::m8120(machine_config &config)
{
	VME(config, "vme");

	VME_SLOT(config, "vme:slot1").option_set("mvme187", VME_MVME187);
}

ROM_START(m8120)
ROM_END

} // anonymous namespace

//    YEAR   NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY     FULLNAME  FLAGS
COMP( 1991,  m8120, 0,      0,      m8120,   0,     m8120_state, empty_init, "Motorola", "M8120",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



mac128.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: R. Belmont, O. Galibert
/****************************************************************************

    drivers/mac128.cpp
    Original-style Macintosh emulation

    These are all 68000 machines in the original Mac form factor with the
    original Mac audio and video.

    Unitron Mac 512: Brazilian Mac 512K(E?) clone.
    Unitron 1024: Brazilian Mac Plus clone.

    Driver by R. Belmont and O. Galibert, with thanks to the original Mac
    driver authors Nathan Woods and Raphael Nabet.
    Thanks also to SCSI guru Patrick Mackinlay and keyboard/mouse wrangler
    Vas Crabb.

    Mac 128K/512K: the original machines with 128K or 512K of RAM.
    Mac Plus: floppy now double-sided 800K, SIMM slots for memory expansion,
              SCSI interface added.
    Mac 512KE: a Mac 512K with the 800K floppy drive and the newer Mac Plus ROMs.
    Mac SE: Mac Plus with ADB for the keyboard and mouse interface, and an
            expansion slot.
    Mac SE FDHD: Mac SE with the IWM and 800K drive upgraded to SWIM and
                 the 1.44MB "SuperDrive".
    Mac Classic: Cost-reduced Mac SE FDHD.

    Memory Map:
    0x000000 - 0x3fffff     RAM/ROM (switches based on overlay)
    0x400000 - 0x4fffff     ROM
    0x580000 - 0x5fffff     5380 NCR/Symbios SCSI peripherals chip (Mac Plus only)
    0x600000 - 0x6fffff     RAM
    0x800000 - 0x9fffff     Zilog 8530 SCC (Serial Communications Controller) Read
    0xa00000 - 0xbfffff     Zilog 8530 SCC (Serial Communications Controller) Write
    0xc00000 - 0xdfffff     IWM (Integrated Woz Machine) floppy controller
    0xe80000 - 0xefffff     Rockwell 6522 VIA
    0xf00000 - 0xffffef     Open bus??? (the ROM appears to be accessing here)
    0xfffff0 - 0xffffff     Auto Vector

    Interrupts:
        M68K:
            Level 1 from VIA
            Level 2 from SCC
            Level 4 from "programmer switch" (not implemented)
        VIA:
            CA1 from VBLANK
            CA2 from 1 Hz clock (RTC)
            CB1 from Keyboard Clock
            CB2 from Keyboard Data
            SR  from Keyboard Data Ready

        SCC:
            PB_EXT (DCDB)  from mouse Y circuitry
            PA_EXT (DCDA)  from mouse X circuitry

    The MC68000's FC outputs are not used even for autovectoring. The
    VIA's address range is overdecoded to generate VPA for both.

VIA notes:

Original 128K Macs use Synertek SYP6522 VIAs. Synertek's AN5 (March 1982)
claims that their VIAs have a difference in shift register behavior
compared to those from other manufacturers. However, this particular
difference is not relevant to the Mac hardware, and later 512K Macs
switched away from SYP6522 (Apple part number 338-6522-A) to either
Rockwell R6522AP (R6522-66) or VTI VL6522-02PC. R6522AP is the most
common VIA on the Mac Plus.

Apple continued sourcing VIAs from the same two manufacturers for the
Mac SE, though their ADB-era VIAs are newer CMOS models with a fix for the
shift register's CB1 input synchronizer which Apple may have specifically
requested. The new Rockwell version (R65NC22) is only labeled with Apple
part number 338-6523 (later Macs use a PLCC version which Apple numbered
338S6523), but VLSI Technology's VL65C22V-02PC is not so disguised.

Raster timings from the BBU ERS:
There are 512 visible pixels (32.68 microseconds) per scanline plus 192 pixels
(12.25 microseconds) of hblank.  Sound/PWM are fetched at the end of hblank.
Vertically there are 28 lines of vblank followed by 342 displayed lines.
Scanline 0 is the start of vblank.

****************************************************************************/

#include "emu.h"

#include "adbmodem.h"
#include "macrtc.h"
#include "mactoolbox.h"

#include "bus/mackbd/mackbd.h"
#include "bus/macpds/hyperdrive.h"
#include "bus/macpds/pds_tpdfpd.h"
#include "bus/nscsi/cd.h"
#include "bus/nscsi/devices.h"
#include "cpu/m68000/m68000.h"
#include "machine/6522via.h"
#include "machine/iwm.h"
#include "machine/swim1.h"
#include "machine/ncr5380.h"
#include "machine/nscsi_bus.h"
#include "machine/rescap.h"
#include "bus/nscsi/devices.h"
#include "machine/ram.h"
#include "machine/applefdintf.h"
#include "machine/timer.h"
#include "machine/z80scc.h"
#include "macadb.h"
#include "macscsi.h"
#include "sound/dac.h"
#include "sound/flt_biquad.h"
#include "sound/flt_rc.h"
#include "sound/spkrdev.h"

#include "formats/ap_dsk35.h"

#include "emupal.h"
#include "screen.h"
#include "softlist.h"
#include "speaker.h"

namespace {

static constexpr auto C7M   = (15.6672_MHz_XTAL / 2);
static constexpr auto C3_7M = (15.6672_MHz_XTAL / 4).value();

// video parameters
static constexpr s32 MAC_H_VIS   = 512;
static constexpr s32 MAC_V_VIS   = 342;
static constexpr s32 MAC_H_TOTAL = 704; // (512 visible + 192 hblank)
static constexpr s32 MAC_V_TOTAL = 370; // (28 vblank + 342 visible)
static constexpr s32 MAC_V_FIRST = 28;  // first visible line

// sound buffer locations
static constexpr int MAC_MAIN_SND_BUF_OFFSET = (0x0300>>1); // (end of memory minus 0x0300; for the typical macplus case, this is 0x3ffd00-0x3fffe3 in 16 bit blocks)
static constexpr int MAC_ALT_SND_BUF_OFFSET  = (0x5F00>>1); // (end of memory minus 0x5F00)

class mac128_state : public driver_device
{
public:
	mac128_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_via(*this, "via6522_0"),
		m_adbmodem(*this, "adbmodem"),
		m_macadb(*this, "macadb"),
		m_ram(*this, RAM_TAG),
		m_scsibus(*this, "scsi"),
		m_scsihelp(*this, "scsihelp"),
		m_ncr5380(*this, "scsi:7:ncr5380"),
		m_iwm(*this, "fdc"),
		m_floppy(*this, "fdc:%d", 0U),
		m_mackbd(*this, "kbd"),
		m_rtc(*this, "rtc"),
		m_screen(*this, "screen"),
		m_scanline_timer(*this, "scantimer"),
		m_hblank_timer(*this, "hblanktimer"),
		m_dac(*this, "macdac"),
		m_dac_timer(*this, "dactimer"),
		m_filter(*this, "dacfilter"),
		m_volfilter(*this, "volfilter"),
		m_outfilter(*this, "outfilter"),
		m_scc(*this, "scc"),
		m_mouse0(*this, "MOUSE0"),
		m_mouse1(*this, "MOUSE1"),
		m_mouse2(*this, "MOUSE2"),
		m_cur_floppy(nullptr),
		m_hdsel(0),
		m_devsel(0),
		m_pwm_count_total(0), m_pwm_count_1(0),
		m_pwm_current_rpm{0.0f, 0.0f},
		m_overlay(false),
		m_mouse_bit{0, 0}, m_mouse_last{0, 0},
		m_mouse_last_m{0, 0}, m_mouse_count{0, 0},
		m_screen_buffer(false),
		m_scc_interrupt(false), m_via_interrupt(false),
		m_scsi_interrupt(false), m_scsi_interrupt_mask(false),
		m_last_taken_interrupt(false),
		m_scsi_drq(false),
		m_snd_enable(false),
		m_main_buffer(false),
		m_snd_vol(0),
		m_adb_irq_pending(false),
		m_drive_select(false),
		m_ram_ptr(nullptr), m_rom_ptr(nullptr),
		m_ram_mask(0), m_ram_size(0)
	{
	}

	void mac512ke(machine_config &config) ATTR_COLD;
	void mac128k(machine_config &config) ATTR_COLD;
	void mac512k(machine_config &config) ATTR_COLD;
	void macplus(machine_config &config) ATTR_COLD;
	void macse(machine_config &config) ATTR_COLD;
	void macsefd(machine_config &config) ATTR_COLD;
	void macclasc(machine_config &config) ATTR_COLD;

	void mac_driver_init() ATTR_COLD;

private:
	required_device<m68000_device> m_maincpu;
	required_device<via6522_device> m_via;
	optional_device<adbmodem_device> m_adbmodem;
	optional_device<macadb_device> m_macadb;
	required_device<ram_device> m_ram;
	optional_device<nscsi_bus_device> m_scsibus;
	optional_device<mac_scsi_helper_device> m_scsihelp;
	optional_device<ncr5380_device> m_ncr5380;
	required_device<applefdintf_device> m_iwm;
	required_device_array<floppy_connector, 2> m_floppy;
	optional_device<mac_keyboard_port_device> m_mackbd;
	required_device<rtc3430042_device> m_rtc;
	required_device<screen_device> m_screen;
	required_device<timer_device> m_scanline_timer;
	required_device<timer_device> m_hblank_timer;
	required_device<speaker_sound_device> m_dac;
	required_device<timer_device> m_dac_timer;
	required_device<filter_biquad_device> m_filter;
	required_device<filter_biquad_device> m_volfilter;
	required_device<filter_rc_device> m_outfilter;
	required_device<z80scc_device> m_scc;

	optional_ioport m_mouse0, m_mouse1, m_mouse2;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void scc_mouse_irq(int x, int y);
	void set_via_interrupt(int value);
	void field_interrupts();
	void mouse_callback();

	u16 ram_r(offs_t offset);
	void ram_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	void ram_w_se(offs_t offset, u16 data, u16 mem_mask = ~0);
	u16 ram_600000_r(offs_t offset);
	void ram_600000_w(offs_t offset, u16 data, u16 mem_mask = ~ 0);
	u16 mac_via_r(offs_t offset);
	void mac_via_w(offs_t offset, u16 data);
	u16 mac_autovector_r(offs_t offset);
	void mac_autovector_w(offs_t offset, u16 data);
	u16 mac_iwm_r(offs_t offset, u16 mem_mask = ~0);
	void mac_iwm_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	u16 scsi_r(offs_t offset, u16 mem_mask = ~0);
	void scsi_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	u16 macse_scsi_r(offs_t offset, u16 mem_mask = ~0);
	void macse_scsi_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	void scsi_irq_w(int state);
	void scsi_drq_w(int state);
	void scsi_berr_w(u8 data);
	void set_scc_interrupt(int state);
	void vblank_w(int state);

	void adb_irq_w(int state) { m_adb_irq_pending = state; }

	TIMER_DEVICE_CALLBACK_MEMBER(mac_scanline);
	TIMER_DEVICE_CALLBACK_MEMBER(mac_hblank);
	TIMER_DEVICE_CALLBACK_MEMBER(mac_dac);
	u32 screen_update_mac(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	u8 mac_via_in_a();
	u8 mac_via_in_b();
	u8 mac_via_in_b_se();
	void mac_via_out_a(u8 data);
	void mac_via_out_b(u8 data);
	void mac_via_out_a_se(u8 data);
	void mac_via_out_b_se(u8 data);
	void mac_via_irq(int state);
	void update_volume();

	void mac512ke_map(address_map &map) ATTR_COLD;
	void macplus_map(address_map &map) ATTR_COLD;
	void macse_map(address_map &map) ATTR_COLD;

	floppy_image_device *m_cur_floppy;
	int m_hdsel, m_devsel;
	int m_pwm_count_total, m_pwm_count_1;
	float m_pwm_current_rpm[2]{};

	void phases_w(u8 phases);
	void devsel_w(u8 devsel);
	void devsel_se_w(u8 devsel);
	void snd_push(u8 data);
	void pwm_push(u8 data);

	bool m_overlay;

	u8 m_mouse_bit[2]{}, m_mouse_last[2]{};
	s16 m_mouse_last_m[2]{}, m_mouse_count[2]{};
	bool m_screen_buffer;

	// interrupts
	bool m_scc_interrupt, m_via_interrupt, m_scsi_interrupt, m_scsi_interrupt_mask;
	s32 m_last_taken_interrupt;

	// DRQ
	bool m_scsi_drq;

	bool m_snd_enable;
	bool m_main_buffer;
	u8 m_snd_vol;
	bool m_adb_irq_pending;
	bool m_drive_select;
	u16 *m_ram_ptr, *m_rom_ptr;
	u32 m_ram_mask, m_ram_size;
};

void mac128_state::machine_start()
{
	m_ram_ptr = (u16*)m_ram->pointer();
	m_ram_size = m_ram->size()>>1;
	m_ram_mask = m_ram_size - 1;
	m_rom_ptr = (u16*)memregion("bootrom")->base();

	save_item(NAME(m_overlay));
	save_item(NAME(m_mouse_bit));
	save_item(NAME(m_mouse_last));
	save_item(NAME(m_mouse_last_m));
	save_item(NAME(m_mouse_count));
	save_item(NAME(m_screen_buffer));
	save_item(NAME(m_scc_interrupt));
	save_item(NAME(m_via_interrupt));
	save_item(NAME(m_scsi_interrupt));
	save_item(NAME(m_scsi_interrupt_mask));
	save_item(NAME(m_scsi_drq));
	save_item(NAME(m_last_taken_interrupt));
	save_item(NAME(m_snd_enable));
	save_item(NAME(m_main_buffer));
	save_item(NAME(m_snd_vol));
	save_item(NAME(m_adb_irq_pending));
	save_item(NAME(m_drive_select));
	save_item(NAME(m_pwm_count_total));
	save_item(NAME(m_pwm_count_1));
	save_item(NAME(m_pwm_current_rpm));

	m_mouse_bit[0] = m_mouse_bit[1] = 0;
	m_mouse_last[0] = m_mouse_last[1] = 0;
}

void mac128_state::machine_reset()
{
	m_last_taken_interrupt = -1;
	m_overlay = true;
	m_screen_buffer = true;
	m_last_taken_interrupt = 0;
	m_snd_enable = false;
	m_main_buffer = true;
	m_snd_vol = 3;
	m_adb_irq_pending = false;
	m_drive_select = false;
	m_scsi_interrupt_mask = false;
	m_pwm_count_total = 0;
	m_pwm_count_1 = 0;
	m_pwm_current_rpm[0] = 302.5; // Speed for 0% duty cycle
	m_pwm_current_rpm[1] = 302.5;
	m_scsi_drq = false;

	m_via->write_pb6(0);
}

u16 mac128_state::ram_r(offs_t offset)
{
	if (m_overlay)
	{
		return m_rom_ptr[offset & 0x7ffff];
	}

	return m_ram_ptr[offset & m_ram_mask];
}

void mac128_state::ram_w(offs_t offset, u16 data, u16 mem_mask)
{
	if (!m_overlay)
	{
		COMBINE_DATA(&m_ram_ptr[offset & m_ram_mask]);
	}
}

void mac128_state::ram_w_se(offs_t offset, u16 data, u16 mem_mask)
{
	m_overlay = false;
	COMBINE_DATA(&m_ram_ptr[offset & m_ram_mask]);
}

u16 mac128_state::ram_600000_r(offs_t offset)
{
	return m_ram_ptr[offset & m_ram_mask];
}

void mac128_state::ram_600000_w(offs_t offset, u16 data, u16 mem_mask)
{
	COMBINE_DATA(&m_ram_ptr[offset & m_ram_mask]);
}

void mac128_state::field_interrupts()
{
	s32 take_interrupt = -1;

	if ((m_scc_interrupt) || (m_scsi_interrupt && m_scsi_interrupt_mask))
	{
		take_interrupt = 2;
	}
	else if (m_via_interrupt)
	{
		take_interrupt = 1;
	}

//  printf("field_interrupts: take %d\n", take_interrupt);

	if (m_last_taken_interrupt > -1)
	{
		m_maincpu->set_input_line(m_last_taken_interrupt, CLEAR_LINE);
		m_last_taken_interrupt = -1;
	}

	if (take_interrupt > -1)
	{
		m_maincpu->set_input_line(take_interrupt, ASSERT_LINE);
		m_last_taken_interrupt = take_interrupt;
	}
}

void mac128_state::set_scc_interrupt(int state)
{
//  printf("SCC IRQ: %d\n", state);
	m_scc_interrupt = state;
	field_interrupts();
}

void mac128_state::set_via_interrupt(int value)
{
	m_via_interrupt = value;
	field_interrupts();
}

void mac128_state::update_volume()
{
	/* LS161 audio PWM counters TC (SND) -> LS04 inverter (/SND) ->
	 * -> CD4016 gate A pulling a 5.1V zener-regulated signal to ground if input is high ->
	 * -> Sallen-key low-pass filter (R1 = 47K, R2 = 47K, C1 = 0.001uF, C2 = 470pF)
	 *  (FC of 4939.3903Hz, Q of 0.7293, Gain of 1.0) ->
	 * -> Differentiator (bandpass) Filter:
	 *   ->\-> r13 (470k) ------------------------>|
	 *     |-> r12 (470k) -> CD4016 D (pa0 != 0) ->|
	 *     |-> r17 (150k) -> CD4016 C (pa1 != 0) ->|
	 *     |-> r16 (68k)  -> CD4016 B (pa2 != 0) ->\ ->
	 *   -> DC blocking caps (2x .1uf in parallel) ->
	 *   -> Push-Pull +12v/-12v current amplifier for the speaker (can be safely ignored) ->
	 * -> R/C low-pass filter (R = 47Ohm, C = .1uf) ->
	 * -> Audio Jack -> Speaker
	 */
	constexpr double res_ohm_tbl[8] =
		{
			//       R13                  R16                 R17                  R12
			(1.0 / ((1.0 / RES_K(470)))),                                                               // 470,000 ohms, gain of ~-7.426 fc1 = 1.692hz fc2 = 3.653hz
			(1.0 / ((1.0 / RES_K(470)) + (1.0 / RES_K(470)))),                                          // 235,000 ohms, gain of ~-1.409 fc1 = 3.425hz fc2 = 3.611hz
			(1.0 / ((1.0 / RES_K(470)) + (1.0 / RES_K(150)))),                                          // 113,710 ohms, gain of ~+4.888 fc1 = 7.017hz fc2 = 3.671hz
			(1.0 / ((1.0 / RES_K(470)) + (1.0 / RES_K(150)) + (1.0 / RES_K(470)))),                     //  91,558 ohms, gain of ~+6.766 fc1 = 8.816hz fc2 = 3.671hz
			(1.0 / ((1.0 / RES_K(470)) + (1.0 / RES_K(68)))),                                           //  59,405 ohms, gain of ~+10.512 fc1 = 13.245hz fc2 = 3.656khz
			(1.0 / ((1.0 / RES_K(470)) + (1.0 / RES_K(68)) + (1.0 / RES_K(470)))),                      //  52,739 ohms, gain of ~+11.541 fc1 = 15.141hz fc2 = 3.656khz
			(1.0 / ((1.0 / RES_K(470)) + (1.0 / RES_K(68)) + (1.0 / RES_K(150)))),                      //  42,552 ohms, gain of ~+13.397 fc1 = 18.756hz fc2 = 3.656khz
			(1.0 / ((1.0 / RES_K(470)) + (1.0 / RES_K(68)) + (1.0 / RES_K(150)) + (1.0 / RES_K(470))))  //  39,020 ohms, gain of ~+14.146 fcl = 20hz fch = 3.607khz
		};

	m_volfilter->opamp_diff_bandpass_modify(res_ohm_tbl[m_snd_vol & 7], RES_K(200), CAP_U(.2), CAP_P(220)); // variable based on cd4016, R15, C11+C12, C10
}

void mac128_state::vblank_w(int state)
{
	m_via->write_ca1(!state);
}

TIMER_DEVICE_CALLBACK_MEMBER(mac128_state::mac_scanline)
{
	const int scanline = param;

	m_via->write_pb6(0);         // H4 signal
								 // During the visible portion (first 512 pixels) of a scanline, (i.e. the
								 // first 512 15.6672MHz clock cycles on a scanline, which is 256 CPU C7M
								 // cycles), there are four CPU cycles used, followed by four /DTACK cycles
								 // used to retrieve a word from DRAM, for every block of 8 CPU cycles.
								 // * [v = clock during visible portion, h = clock during hblank portion]
								 // * C16M: v v v v v v v v v v v v v v v v v v...v v h h h h h h h h h h...
								 // * C7M:  v   v   v   v   v   v   v   v   v  ...v   h   h   h   h   h  ...
								 // *       CPU CPU CPU CPU RAM RAM RAM RAM CPU...RAM CPU CPU CPU CPU CPU...
								 // In addition, the very last 4 cycles on a scanline are used by the
								 // sound/pwm system to grab the sound/pwm word.
								 // * (H4 goes active here:         4 4 4 4 4 4 4 4 ?                      )
								 // * C16M:...h h h h h h h h h h h h h h h h h h h h v v v v v v v v v v...
								 // * C7M: ...h   h   h   h   h   h   h   h   h   h   v   v   v   v   v  ...
								 // *      ...CPU CPU CPU CPU CPU CPU SND SND SND SND CPU CPU CPU CPU RAM
								 // This effectively means that the cpu loses access to the memory bus for
								 // 4 out of the 352 C7M cycles on every scanline, and an additional 128 C7M
								 // cycles on a visible scanline not in vblank.
	if (scanline >= MAC_V_FIRST) // on a visible scanline
	{
		m_maincpu->adjust_icount(-128);
	}

	m_hblank_timer->adjust(m_screen->time_until_pos(scanline, MAC_H_TOTAL - 9));

	if ((!(scanline % 10)) && (!m_macadb))
	{
		mouse_callback();
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(mac128_state::mac_hblank)
{
	const int scanline = m_screen->vpos();
	u16 *mac_snd_buf_ptr;

	m_via->write_pb6(1); // H4 signal
	m_maincpu->adjust_icount(-4);
	if (m_main_buffer)
	{
		mac_snd_buf_ptr = (u16 *)(m_ram_ptr + m_ram_size - MAC_MAIN_SND_BUF_OFFSET);
	}
	else
	{
		mac_snd_buf_ptr = (u16 *)(m_ram_ptr + m_ram_size - MAC_ALT_SND_BUF_OFFSET);
	}

	// The sound "DAC" is a 1-bit PWM output driven by two 4-bit LS161 counters
	// chained together. These counters are reset at the end of HBLANK, and
	// count up once every C7M clock (2 pixels per clock), with the TC (SND)
	// output connected (through two inverters) to the speaker filter. The
	// counters count a max of 256 C7M clocks before reaching terminal count
	// and halting themselves, and there are 704 / 2 = 352 C7M clocks per
	// scanline, so this means the TC (SND) output over time is an asymmetric
	// PWM squarewave, low from between 0 (if the load value is 0xff) and
	// 255 (if the load value is 0x00) of the 352 C7M clocks per scanline,
	// and high the remainder of the time. This has a significant DC offset
	// due to the remaining clocks where the TC signal remains high.
	// The counters can be forced to reset and be held at a value of 0x00 if
	// the VIA PB7(/SNDRES) pin is held active(low), and this conversely will
	// cause the TC (SND) counter pin to output a constant low level for as
	// long as /SNDRES is held active.
	// Some games such as Lode Runner use the sound manager "swMode" function,
	// which uses the /SNDRES pin to alternately force the sound output low
	// vs running normally in a square wave. During this time, the software is
	// leaving the actual sound buffer FIFO values at a constant 0x80.
	// So unless we force the 1-bit PWM to have a value of "always low" while
	// PB7 is low, we get almost no sound in Lode Runner, and probably other
	// games/software as well.
	snd_push(mac_snd_buf_ptr[scanline] >> 8);
	pwm_push(mac_snd_buf_ptr[scanline] & 0xff);

	m_via->write_pb6(0);
}

void mac128_state::snd_push(u8 data)
{
	if ((data < 0xff) || (!m_snd_enable))
	{
		m_dac->level_w(0);
		if (m_snd_enable)
		{
			m_dac_timer->adjust(m_maincpu->cycles_to_attotime(0xff - data));
		}
		else
		{
			m_dac_timer->adjust(attotime::never);
		}
	}
	else // value is 0xff; TC is already high.
	{
		m_dac->level_w(1);
		m_dac_timer->adjust(attotime::never);
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(mac128_state::mac_dac)
{
	if (m_snd_enable)
	{
		m_dac->level_w(1);
	}
	m_dac_timer->adjust(attotime::never);
}

void mac128_state::pwm_push(u8 data)
{
	// The PWM works by sending pulses with a specific duty cycle.
	// The lengths sent by the firmware are in the range 1-40, which
	// means the total number of time slots is probably 42, to ensure
	// at least one edge always happens.  To get a better precision
	// the firmware dithers between two values over a cycle of 10
	// pulses, giving internally a 0-399 possible range mapping to a
	// 11-410 real length out of 420 total, with a duty cycle ranging
	// from 2.6% to 97.6%.  The firmware calibrates from the drive
	// actual rpm as measured through the tachometer with indexes 128
	// and 256 at startup and keeps an eye on the actual rpm
	// afterwards to avoid temperature drift.

	// The length counter is a 6-bits lfsr with taps on bits 0 and 1
	// and insertion on bit 5.  The firmware writes a value so that
	// the length is reached when the counter hits 0x20.

	static constexpr u8 value_to_length[64] = {
		 0,  1, 59,  2, 60, 40, 54,  3,
		61, 32, 49, 41, 55, 19, 35,  4,
		62, 52, 30, 33, 50, 12, 14, 42,
		56, 16, 27, 20, 36, 23, 44,  5,
		63, 58, 39, 53, 31, 48, 18, 34,
		51, 29, 11, 13, 15, 26, 22, 43,
		57, 38, 47, 17, 28, 10, 25, 21,
		37, 46,  9, 24, 45,  8,  7,  6
	};

	m_pwm_count_1 += value_to_length[data & 0x3f];

	m_pwm_count_total ++;

	if (m_pwm_count_total == 100)
	{
		// The documentation requires:
		// - duty cycle of 9.4%, 305 < rpm < 380 (middle 342.5)
		// - duty cycle of 91%,  625 < rpm < 780 (middle 702.5)
		// - linear between these two points

		int internal_index = m_pwm_count_1 / (m_pwm_count_total/10) - 11;
		if(internal_index < 0)
			internal_index = 0;
		if(internal_index > 399)
			internal_index = 399;

		float duty_cycle = internal_index / 419.0;
		float rpm = (duty_cycle - 0.094) * (702.5 - 342.5) / (0.91 - 0.094) + 342.5;


		// Only change when you get the same value twice consecutively
		// to avoid changing multiple times when in transition.
		if (rpm == m_pwm_current_rpm[1] && m_pwm_current_rpm[1] != m_pwm_current_rpm[0])
		{
			logerror("PWM index %3d duty cycle %5.1f%% rpm %f\n", internal_index, 100*duty_cycle, rpm);

			if (m_cur_floppy && m_cur_floppy->type() == OAD34V)
			{
				m_iwm->sync();
				m_cur_floppy->set_rpm(rpm);
			}
		}

		m_pwm_current_rpm[0] = m_pwm_current_rpm[1];
		m_pwm_current_rpm[1] = rpm;
		m_pwm_count_1 = 0;
		m_pwm_count_total = 0;
	}
}

void mac128_state::scsi_irq_w(int state)
{
	m_scsi_interrupt = state;
	field_interrupts();
}


void mac128_state::scsi_drq_w(int state)
{
	m_scsi_drq = state;
}

void mac128_state::scsi_berr_w(u8 data)
{
	m_maincpu->trigger_bus_error();
}

u16 mac128_state::scsi_r(offs_t offset, u16 mem_mask)
{
	const int reg = (offset >> 3) & 0xf;

	if (ACCESSING_BITS_0_7)
	{
		if ((offset >= 0x100) && (m_scsi_drq))
		{
			return m_ncr5380->dma_r();
		}

		return m_ncr5380->read(reg);
	}

	if ((offset >= 0x100) && (m_scsi_drq))
	{
		return u16(m_ncr5380->dma_r()) << 8;
	}

	return u16(m_ncr5380->read(reg)) << 8;
}

void mac128_state::scsi_w(offs_t offset, u16 data, u16 mem_mask)
{
	const int reg = (offset >> 3) & 0xf;

	// here we can take advantage of 68000 byte smearing
	if ((offset >= 0x100) && (m_scsi_drq))
	{
		m_ncr5380->dma_w(data & 0xff);
	}

	m_ncr5380->write(reg, data & 0xff);
}

u16 mac128_state::macse_scsi_r(offs_t offset, u16 mem_mask)
{
	const int reg = (offset>>3) & 0xf;
	return m_scsihelp->read_wrapper(BIT(offset, 8), reg)<<8;
}

void mac128_state::macse_scsi_w(offs_t offset, u16 data, u16 mem_mask)
{
	const int reg = (offset>>3) & 0xf;
	m_scsihelp->write_wrapper(BIT(offset, 8), reg, data>>8);
}

void mac128_state::scc_mouse_irq(int x, int y)
{
	// DCD lines are active low in hardware but active high to software
	if (x)
	{
		m_scc->dcda_w(m_mouse_last[0] ? 1 : 0);
		if (x < 0)
		{
			m_mouse_bit[0] = m_mouse_last[0] ? 0 : 1;
		}
		else
		{
			m_mouse_bit[0] = m_mouse_last[0] ? 1 : 0;
		}
		m_mouse_last[0] = !m_mouse_last[0];
	}
	if (y)
	{
		m_scc->dcdb_w(m_mouse_last[1] ? 1 : 0);
		if (y < 0)
		{
			m_mouse_bit[1] = m_mouse_last[1] ? 0 : 1;
		}
		else
		{
			m_mouse_bit[1] = m_mouse_last[1] ? 1 : 0;
		}
		m_mouse_last[1] = !m_mouse_last[1];
	}
}

u16 mac128_state::mac_iwm_r(offs_t offset, u16 mem_mask)
{
	u16 result = m_iwm->read((offset >> 8) & 0xf);
	return (result << 8) | result;
}

void mac128_state::mac_iwm_w(offs_t offset, u16 data, u16 mem_mask)
{
	if (ACCESSING_BITS_0_7)
		m_iwm->write((offset >> 8) & 0xf, data & 0xff);
	else
		m_iwm->write((offset >> 8) & 0xf, data>>8);
}

void mac128_state::mac_via_irq(int state)
{
	/* interrupt the 68k (level 1) */
	set_via_interrupt(state);
}

u16 mac128_state::mac_via_r(offs_t offset)
{
	u16 data;

	offset >>= 8;
	offset &= 0x0f;

	data = m_via->read(offset);
	return (data & 0xff) | (data << 8);
}

void mac128_state::mac_via_w(offs_t offset, u16 data)
{
	offset >>= 8;
	offset &= 0x0f;

	m_via->write(offset, (data >> 8) & 0xff);
}

void mac128_state::mac_autovector_w(offs_t offset, u16 data)
{
	/* This should throw an exception */
	/* Not yet implemented */
}

u16 mac128_state::mac_autovector_r(offs_t offset)
{
	/* This should throw an exception */
	/* Not yet implemented */
	return 0;
}

u8 mac128_state::mac_via_in_a()
{
	return 0x81;
}

u8 mac128_state::mac_via_in_b()
{
	int val = 0x40;

	val |= m_mouse_bit[1] << 5; // Mouse Y2
	val |= m_mouse_bit[0] << 4; // Mouse X2
	val |= BIT(~m_mouse0->read(), 0) << 3;

	val |= m_rtc->data_r();

//  printf("%s VIA1 IN_B = %02x\n", machine().describe_context().c_str(), val);

	return val;
}

u8 mac128_state::mac_via_in_b_se()
{
	int val = 0;

	if (!m_adb_irq_pending)
	{
		val |= 0x08;
	}

	val |= m_rtc->data_r();

//  printf("%s VIA1 IN_B = %02x\n", machine().describe_context().c_str(), val);

	return val;
}

void mac128_state::mac_via_out_a(u8 data)
{
//  printf("%s VIA1 OUT A: %02x (PC %x)\n", machine().describe_context().c_str(), data);

	//set_scc_waitrequest((data & 0x80) >> 7);
	m_screen_buffer = BIT(data, 6);

	int hdsel = BIT(data, 5);
	if (hdsel != m_hdsel)
	{
		if (m_cur_floppy)
		{
			m_cur_floppy->ss_w(hdsel);
		}
		m_hdsel = hdsel;
	}

	/* Early Mac models had VIA A4 control overlaying.  In the Mac SE and
	 * later models, overlay was set on reset, but cleared on the first
	 * access to the ROM's normal address space. */
	m_overlay = BIT(data, 4);
	m_main_buffer = BIT(data, 3);
	m_snd_vol = data & 0x07;
	update_volume();
}

void mac128_state::mac_via_out_a_se(u8 data)
{
//  printf("%s VIA OUT A: %02x (PC %x)\n", machine().describe_context().c_str(), data);

	//set_scc_waitrequest((data & 0x80) >> 7);
	m_screen_buffer = BIT(data, 6);

	int hdsel = BIT(data, 5);
	if (hdsel != m_hdsel)
	{
		if (m_cur_floppy)
		{
			m_cur_floppy->ss_w(hdsel);
		}
		m_hdsel = hdsel;
	}

	// on SE only this selects which floppy drive (0 = upper, 1 = lower)
	if (m_drive_select != BIT(data, 4))
	{
		m_drive_select = BIT(data, 4);
		devsel_se_w(m_devsel);
	}

	// BIT(data, 3) controls synchronous modem support for SCC channel A
	m_snd_vol = data & 0x07;
	update_volume();
}

void mac128_state::mac_via_out_b(u8 data)
{
//  printf("%s VIA1 OUT B: %02x\n", machine().describe_context().c_str(), data);

	m_snd_enable = !BIT(data, 7);
	if (!m_snd_enable)
	{
		m_dac->level_w(0);
	}
	m_rtc->ce_w(BIT(data, 2));
	m_rtc->data_w(BIT(data, 0));
	m_rtc->clk_w(BIT(data, 1));
}

void mac128_state::mac_via_out_b_se(u8 data)
{
//  printf("%s VIA OUT B: %02x\n", machine().describe_context().c_str(), data);

	m_snd_enable = !BIT(data, 7);
	if (!m_snd_enable)
	{
		m_dac->level_w(0);
	}

	m_scsi_interrupt_mask = !BIT(data, 6); // only the SE and classic can actually enable this interrupt

	m_adbmodem->set_via_state((data & 0x30) >> 4);

	m_rtc->ce_w(BIT(data, 2));
	m_rtc->data_w(BIT(data, 0));
	m_rtc->clk_w(BIT(data, 1));
	field_interrupts(); // it is possible the mask above was enabled by the last write while a scsi interrupt was pending, hence a scsi interrupt should immediately fire.
}

/* *************************************************************************
 * Mouse
 * *************************************************************************/

void mac128_state::mouse_callback()
{
	// see if it moved in the x coord
	const int new_mx = m_mouse1->read();
	if (new_mx != m_mouse_last_m[0])
	{
		int diff = new_mx - m_mouse_last_m[0];

		// check for wrap
		if (diff > 0x80)
			diff -= 0x100;
		else if (diff < -0x80)
			diff += 0x100;

		m_mouse_count[0] += diff;
		m_mouse_last_m[0] = new_mx;
	}

	// see if it moved in the y coord
	const int new_my = m_mouse2->read();
	if (new_my != m_mouse_last_m[1])
	{
		int diff = new_my - m_mouse_last_m[1];

		// check for wrap
		if (diff > 0x80)
			diff -= 0x100;
		else if (diff < -0x80)
			diff += 0x100;

		m_mouse_count[1] += diff;
		m_mouse_last_m[1] = new_my;
	}

	// update any remaining count and then return
	int x_needs_update = 0;
	if (m_mouse_count[0] < 0)
	{
		m_mouse_count[0]++;
		x_needs_update = -1;
	}
	else if (m_mouse_count[0])
	{
		m_mouse_count[0]--;
		x_needs_update = 1;
	}
	int y_needs_update = 0;
	if (m_mouse_count[1] < 0)
	{
		m_mouse_count[1]++;
		y_needs_update = 1;
	}
	else if (m_mouse_count[1])
	{
		m_mouse_count[1]--;
		y_needs_update = -1;
	}

	if (x_needs_update || y_needs_update)
	{
		// assert Port B External Interrupt on the SCC
		scc_mouse_irq(x_needs_update, y_needs_update);
	}
}

void mac128_state::mac_driver_init()
{
	m_scsi_interrupt = m_scsi_interrupt_mask = 0;

	memset(m_ram->pointer(), 0, m_ram->size());
}

static constexpr u32 MAC_MAIN_SCREEN_BUF_OFFSET = (0x5900>>1);
static constexpr u32 MAC_ALT_SCREEN_BUF_OFFSET  = (0xd900>>1);

u32 mac128_state::screen_update_mac(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u32 const video_base = m_ram_size - (m_screen_buffer ? MAC_MAIN_SCREEN_BUF_OFFSET : MAC_ALT_SCREEN_BUF_OFFSET);
	u16 const *video_ram = (const u16 *) (m_ram_ptr + video_base);

	for (int y = MAC_V_FIRST; y < MAC_V_TOTAL; y++)
	{
		u16 *const line = &bitmap.pix(y);

		for (int x = 0; x < MAC_H_VIS; x += 16)
		{
			u16 const word = *(video_ram++);
			for (int b = 0; b < 16; b++)
			{
				line[x + b] = (word >> (15 - b)) & 0x0001;
			}
		}
	}
	return 0;
}


void mac128_state::phases_w(u8 phases)
{
	if (m_cur_floppy)
		m_cur_floppy->seek_phase_w(phases);
}

void mac128_state::devsel_w(u8 devsel)
{
	if (devsel == 1)
		m_cur_floppy = m_floppy[0]->get_device();
	else if (devsel == 2)
		m_cur_floppy = m_floppy[1]->get_device();
	else
		m_cur_floppy = nullptr;

	m_iwm->set_floppy(m_cur_floppy);
	if (m_cur_floppy)
	{
		m_cur_floppy->ss_w(m_hdsel);
		if (m_cur_floppy->type() == OAD34V)
			m_cur_floppy->set_rpm(m_pwm_current_rpm[1]);
	}
}

void mac128_state::devsel_se_w(u8 devsel)
{
	// m_drive_select = 0 for the SE's two internal drives, 1 for the single external
	if (!m_drive_select)
	{
		if (devsel == 1)
			m_cur_floppy = m_floppy[0]->get_device();
		else if (devsel == 2)
			m_cur_floppy = m_floppy[1]->get_device();
		else
			m_cur_floppy = nullptr;
	}
	else
	{
		m_cur_floppy = nullptr;
	}

	m_iwm->set_floppy(m_cur_floppy);
	if (m_cur_floppy)
	{
		m_cur_floppy->ss_w(m_hdsel);
		if (m_cur_floppy->type() == OAD34V)
			m_cur_floppy->set_rpm(m_pwm_current_rpm[1]);
	}

	m_devsel = devsel;
}

/***************************************************************************
    ADDRESS MAPS
***************************************************************************/

/* ROM mirroring notes
 a23 a22 a21 a20 a19 a18 a17 a16 a15 a14 a13 a12 a11 a10 a09 a08 a07 a06 a05 a04 a03 a02 a01[a00]
   0  [a   b]  0 |[x] [x] [x]  x | *   *   *   * | *   *   *   * | *   *   *   * | *   *   *   *2 - mac 128/512 (64k rom w/overlay on) (a b = active if bits are 0 0, 0 1, or 1 0)
   0   1  ?0?  0 |[x] [x] [x]  x | *   *   *   * | *   *   *   * | *   *   *   * | *   *   *   *2 - mac 128/512 (64k rom w/overlay off)
   0  [a   b]  0 |[x] [x] [x]  * | *   *   *   * | *   *   *   * | *   *   *   * | *   *   *   *2 - mac 128/512 (128k rom w/overlay on) (a b = active if bits are 0 0, 0 1, or 1 0)
   0   1  ?0?  0 |[x] [x] [x]  * | *   *   *   * | *   *   *   * | *   *   *   * | *   *   *   *2 - mac 128/512 (128k rom w/overlay off)
   0   x   0  ?0?|[x] [x]  0   * | *   *   *   * | *   *   *   * | *   *   *   * | *   *   *   *2 - mac plus (128k rom w/overlay on)
   0   1   0  ?0?|[x] [x]  0   * | *   *   *   * | *   *   *   * | *   *   *   * | *   *   *   *2 - mac plus (128k rom w/overlay off)
   0   x   0  ?0?|[x] [x]  *   * | *   *   *   * | *   *   *   * | *   *   *   * | *   *   *   *2 - mac plus (256k mask rom[2] w/overlay on)
   0   1   0  ?0?|[x] [x]  *   * | *   *   *   * | *   *   *   * | *   *   *   * | *   *   *   *2 - mac plus (256k mask rom[2] w/overlay off)
 [x] = available for expanded ROM by using patch wires, as is done in some homebrew/mods
 [2] - The Mac Plus never shipped with 256k of mask ROM; (it shipped with 128k
        of mask rom on two JEDEC 23512 64KiB mask roms), but A17 was wired to
        /OE (pin 22) as it would be on a non-JEDEC Toshiba tc531000/NEC 23c1024
        28 pin 128KiB mask ROM.
       The Mac SE did ship with a pair of tc531000/23c1024 mask ROMs.
 *2  - A00 is not decoded for the ROMs; one of the ROMs connects to D15-D8 and the other to D7-D0
 */

 void mac128_state::mac512ke_map(address_map &map)
{
	map(0x000000, 0x3fffff).rw(FUNC(mac128_state::ram_r), FUNC(mac128_state::ram_w));
	map(0x400000, 0x4fffff).rom().region("bootrom", 0);
	map(0x600000, 0x6fffff).rw(FUNC(mac128_state::ram_600000_r), FUNC(mac128_state::ram_600000_w));
	map(0x800000, 0x9fffff).r(m_scc, FUNC(z80scc_device::dc_ab_r)).umask16(0xff00);
	map(0xa00000, 0xbfffff).w(m_scc, FUNC(z80scc_device::dc_ab_w)).umask16(0x00ff);
	map(0xc00000, 0xdfffff).rw(FUNC(mac128_state::mac_iwm_r), FUNC(mac128_state::mac_iwm_w));
	map(0xe80000, 0xefffff).before_time(m_maincpu, FUNC(m68000_device::vpa_sync)).after_delay(m_maincpu, FUNC(m68000_device::vpa_after)).rw(FUNC(mac128_state::mac_via_r), FUNC(mac128_state::mac_via_w));
	map(0xfffff0, 0xffffff).rw(FUNC(mac128_state::mac_autovector_r), FUNC(mac128_state::mac_autovector_w));
}

void mac128_state::macplus_map(address_map &map)
{
	map(0x000000, 0x3fffff).rw(FUNC(mac128_state::ram_r), FUNC(mac128_state::ram_w));
	map(0x400000, 0x4fffff).rom().region("bootrom", 0);
	map(0x580000, 0x5fffff).rw(FUNC(mac128_state::scsi_r), FUNC(mac128_state::scsi_w));
	map(0x800000, 0x9fffff).r(m_scc, FUNC(z80scc_device::dc_ab_r)).umask16(0xff00);
	map(0xa00000, 0xbfffff).w(m_scc, FUNC(z80scc_device::dc_ab_w)).umask16(0x00ff);
	map(0xc00000, 0xdfffff).rw(FUNC(mac128_state::mac_iwm_r), FUNC(mac128_state::mac_iwm_w));
	map(0xe80000, 0xefffff).before_time(m_maincpu, FUNC(m68000_device::vpa_sync)).after_delay(m_maincpu, FUNC(m68000_device::vpa_after)).rw(FUNC(mac128_state::mac_via_r), FUNC(mac128_state::mac_via_w));
	map(0xfffff0, 0xffffff).rw(FUNC(mac128_state::mac_autovector_r), FUNC(mac128_state::mac_autovector_w));
}

void mac128_state::macse_map(address_map &map)
{
	map(0x000000, 0x3fffff).rw(FUNC(mac128_state::ram_r), FUNC(mac128_state::ram_w_se));
	map(0x400000, 0x4fffff).rom().region("bootrom", 0);
	map(0x580000, 0x5fffff).rw(FUNC(mac128_state::macse_scsi_r), FUNC(mac128_state::macse_scsi_w));
	map(0x900000, 0x9fffff).r(m_scc, FUNC(z80scc_device::dc_ab_r)).umask16(0xff00);
	map(0xb00000, 0xbfffff).w(m_scc, FUNC(z80scc_device::dc_ab_w)).umask16(0x00ff);
	map(0xd00000, 0xdfffff).rw(FUNC(mac128_state::mac_iwm_r), FUNC(mac128_state::mac_iwm_w));
	map(0xe80000, 0xefffff).before_time(m_maincpu, FUNC(m68000_device::vpa_sync)).after_delay(m_maincpu, FUNC(m68000_device::vpa_after)).rw(FUNC(mac128_state::mac_via_r), FUNC(mac128_state::mac_via_w));
	map(0xfffff0, 0xffffff).rw(FUNC(mac128_state::mac_autovector_r), FUNC(mac128_state::mac_autovector_w));
}

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

static void mac_pds_cards(device_slot_interface &device)
{
	device.option_add("hyperdrive", PDS_HYPERDRIVE);  // GCC HyperDrive ST-506 interface
}

void mac128_state::mac512ke(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, C7M);        /* 7.8336 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &mac128_state::mac512ke_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");
	m_maincpu->set_tas_write_callback(NAME([] (offs_t offset, u8 data) { })); // TAS read-modify-write cycles are not supported on pre-SE Macs
	config.set_maximum_quantum(attotime::from_hz(60));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(15.6672_MHz_XTAL, MAC_H_TOTAL, 0, MAC_H_VIS, MAC_V_TOTAL, MAC_V_FIRST, MAC_V_TOTAL);
	m_screen->set_native_aspect();
	m_screen->set_screen_update(FUNC(mac128_state::screen_update_mac));
	m_screen->screen_vblank().set(FUNC(mac128_state::vblank_w));
	m_screen->set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	TIMER(config, m_scanline_timer).configure_scanline(FUNC(mac128_state::mac_scanline), "screen", 0, 1);
	TIMER(config, m_hblank_timer).configure_generic(FUNC(mac128_state::mac_hblank));

	/* sound hardware */
	SPEAKER(config, "speaker").front_center();
	FILTER_RC(config, m_outfilter).set_rc(filter_rc_device::LOWPASS, RES_R(47), 0, 0, CAP_U(0.1)); // RC filter R7, C1
	m_outfilter->add_route(ALL_OUTPUTS, "speaker", 1.0);
	FILTER_BIQUAD(config, m_volfilter).opamp_diff_bandpass_setup(RES_K(39.020), RES_K(200), CAP_U(0.2), CAP_P(220));                     // variable differentiator filter based on cd4016, R15, C11+C12, C10
	m_volfilter->add_route(ALL_OUTPUTS, m_outfilter, 0.195);                                                                             // this filter has a max gain of ~5.126, so we diminish it by the inverse of that (0.195)
	FILTER_BIQUAD(config, m_filter).opamp_sk_lowpass_setup(RES_K(47), RES_K(47), RES_M(999.99), RES_R(0.001), CAP_U(0.001), CAP_P(470)); // R18, R14, absent, short, C18, C19
	m_filter->add_route(ALL_OUTPUTS, m_volfilter, 1.0);
	SPEAKER_SOUND(config, m_dac, 0).add_route(ALL_OUTPUTS, m_filter, 1.839); // speaker_filtered_1bit_pwm has an effective gain of 0.54375 so invert that
	TIMER(config, m_dac_timer).configure_generic(FUNC(mac128_state::mac_dac));
	/* devices */
	RTC3430042(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->cko_cb().set(m_via, FUNC(via6522_device::write_ca2));

	IWM(config, m_iwm, C7M);
	m_iwm->phases_cb().set(FUNC(mac128_state::phases_w));
	m_iwm->devsel_cb().set(FUNC(mac128_state::devsel_w));

	applefdintf_device::add_35(config, m_floppy[0]);
	applefdintf_device::add_35(config, m_floppy[1]);

	SCC85C30(config, m_scc, C7M);
	m_scc->configure_channels(C3_7M, 0, C3_7M, 0);
	m_scc->out_int_callback().set(FUNC(mac128_state::set_scc_interrupt));

	MOS6522(config, m_via, C7M/10);
	m_via->readpa_handler().set(FUNC(mac128_state::mac_via_in_a));
	m_via->readpb_handler().set(FUNC(mac128_state::mac_via_in_b));
	m_via->writepa_handler().set(FUNC(mac128_state::mac_via_out_a));
	m_via->writepb_handler().set(FUNC(mac128_state::mac_via_out_b));
	m_via->cb2_handler().set(m_mackbd, FUNC(mac_keyboard_port_device::data_w));
	m_via->irq_handler().set(FUNC(mac128_state::mac_via_irq));

	MAC_KEYBOARD_PORT(config, m_mackbd, mac_keyboard_devices, "pad");
	m_mackbd->clock_cb().set(m_via, FUNC(via6522_device::write_cb1));
	m_mackbd->data_cb().set(m_via, FUNC(via6522_device::write_cb2));

	/* internal ram */
	RAM(config, m_ram);
	m_ram->set_default_size("512K");

	MACPDS(config, "macpds", "maincpu");
	MACPDS_SLOT(config, "pds", "macpds", mac_pds_cards, nullptr);

	// software list
	SOFTWARE_LIST(config, "flop_mac35_orig").set_original("mac_flop_orig").set_filter("MC68000,mac512ke");
	SOFTWARE_LIST(config, "flop_mac35_clean").set_original("mac_flop_clcracked").set_filter("MC68000,mac512ke");
	SOFTWARE_LIST(config, "flop35_list").set_original("mac_flop").set_filter("MC68000,mac512ke");
}

void mac128_state::mac128k(machine_config &config)
{
	mac512ke(config);
	m_ram->set_default_size("128K");

	RTC3430040(config.replace(), m_rtc, 32.768_kHz_XTAL);

	IWM(config.replace(), m_iwm, C7M);
	m_iwm->phases_cb().set(FUNC(mac128_state::phases_w));
	m_iwm->devsel_cb().set(FUNC(mac128_state::devsel_w));

	applefdintf_device::add_35_sd(config, m_floppy[0]);
	applefdintf_device::add_35_sd(config, m_floppy[1]);

	subdevice<software_list_device>("flop_mac35_orig")->set_filter("MC68000,mac128k");
	subdevice<software_list_device>("flop_mac35_clean")->set_filter("MC68000,mac128k");
	subdevice<software_list_device>("flop35_list")->set_filter("MC68000,mac128k");
}

void mac128_state::mac512k(machine_config &config)
{
	mac128k(config);
	m_ram->set_default_size("512K");

	subdevice<software_list_device>("flop_mac35_orig")->set_filter("MC68000,mac512k");
	subdevice<software_list_device>("flop_mac35_clean")->set_filter("MC68000,mac512k");
	subdevice<software_list_device>("flop35_list")->set_filter("MC68000,mac512k");
}

void mac128_state::macplus(machine_config &config)
{
	mac512ke(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &mac128_state::macplus_map);

	m_mackbd->set_default_option("usp");

	// SCSI bus and devices
	// These machines were strictly external CD-ROMs so sound didn't route back into them; the AppleCD SC had
	// RCA jacks for connection to speakers/a stereo.
	SPEAKER(config, "speakers", 2).front();

	NSCSI_BUS(config, m_scsibus);
	NSCSI_CONNECTOR(config, "scsi:0", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:1", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3").option_set("cdrom", NSCSI_CDROM_APPLE).machine_config(
		[](device_t *device)
		{
			device->subdevice<cdda_device>("cdda")->add_route(0, "^^speakers", 1.0, 0);
			device->subdevice<cdda_device>("cdda")->add_route(1, "^^speakers", 1.0, 1);
		});
	NSCSI_CONNECTOR(config, "scsi:4", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", mac_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr5380", NCR5380).machine_config([this](device_t *device) {
		ncr5380_device &adapter = downcast<ncr5380_device &>(*device);
		// The IRQ pin (23) on the NCR5380 is unconnected on the Mac Plus, and will cause it to crash if it is
		adapter.drq_handler().set(*this, FUNC(mac128_state::scsi_drq_w));
	});

	subdevice<software_list_device>("flop_mac35_orig")->set_filter("MC68000,macplus");
	subdevice<software_list_device>("flop_mac35_clean")->set_filter("MC68000,macplus");
	subdevice<software_list_device>("flop35_list")->set_filter("MC68000,macplus");
	SOFTWARE_LIST(config, "hdd_list").set_original("mac_hdd").set_filter("MC68000,macplus");
	SOFTWARE_LIST(config, "cd_list").set_original("mac_cdrom").set_filter("MC68000,macplus");

	/* internal ram */
	m_ram->set_default_size("4M");
	m_ram->set_extra_options("1M,2M,2560K,4M");
}

static void mac_sepds_cards(device_slot_interface &device)
{
	device.option_add("radiusfpd", PDS_SEDISPLAY);  // Radius Full Page Display card for SE
}

void mac128_state::macse(machine_config &config)
{
	macplus(config);

	M68000(config.replace(), m_maincpu, C7M);
	m_maincpu->set_addrmap(AS_PROGRAM, &mac128_state::macse_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	config.device_remove("kbd");
	config.device_remove("pds");

	IWM(config.replace(), m_iwm, C7M*2);
	m_iwm->phases_cb().set(FUNC(mac128_state::phases_w));
	m_iwm->devsel_cb().set(FUNC(mac128_state::devsel_se_w));

	applefdintf_device::add_35(config, m_floppy[0]);
	applefdintf_device::add_35(config, m_floppy[1]);

	MAC_SCSI_HELPER(config, m_scsihelp);
	m_scsihelp->scsi_read_callback().set(m_ncr5380, FUNC(ncr5380_device::read));
	m_scsihelp->scsi_write_callback().set(m_ncr5380, FUNC(ncr5380_device::write));
	m_scsihelp->scsi_dma_read_callback().set(m_ncr5380, FUNC(ncr5380_device::dma_r));
	m_scsihelp->scsi_dma_write_callback().set(m_ncr5380, FUNC(ncr5380_device::dma_w));
	m_scsihelp->cpu_halt_callback().set_inputline(m_maincpu, INPUT_LINE_HALT);
	m_scsihelp->timeout_error_callback().set(FUNC(mac128_state::scsi_berr_w));

	subdevice<nscsi_connector>("scsi:7")->set_option_machine_config("ncr5380", [this](device_t *device) {
		ncr5380_device &adapter = downcast<ncr5380_device &>(*device);
		// The INT pin (23) on the NCR5380 is connected to the the GLU PAL16L8 pin 3.
		// The VIA PB6 pin is connected to the GLU PAL16L8 pin 2, and the open-collector and /OE
		// gated PAL16L8 output pin 14 is wired-OR with the VIA /IRQ pin to the 68K /IPL0 pin.
		// This effectively performs a NAND of !(PB6) and INT to produce /SCSIIRQ.
		adapter.irq_handler().set(*this, FUNC(mac128_state::scsi_irq_w));
		adapter.drq_handler().set(m_scsihelp, FUNC(mac_scsi_helper_device::drq_w));
	});

	ADBMODEM(config, m_adbmodem, C7M);
	m_adbmodem->via_clock_callback().set(m_via, FUNC(via6522_device::write_cb1));
	m_adbmodem->via_data_callback().set(m_via, FUNC(via6522_device::write_cb2));
	m_adbmodem->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
	m_adbmodem->irq_callback().set(FUNC(mac128_state::adb_irq_w));

	MACADB(config, m_macadb, C7M);
	m_macadb->adb_data_callback().set(m_adbmodem, FUNC(adbmodem_device::set_adb_line));

	R65NC22(config.replace(), m_via, C7M/10);
	m_via->readpa_handler().set(FUNC(mac128_state::mac_via_in_a));
	m_via->readpb_handler().set(FUNC(mac128_state::mac_via_in_b_se));
	m_via->writepa_handler().set(FUNC(mac128_state::mac_via_out_a_se));
	m_via->writepb_handler().set(FUNC(mac128_state::mac_via_out_b_se));
	m_via->cb2_handler().set(m_adbmodem, FUNC(adbmodem_device::set_via_data));
	m_via->irq_handler().set(FUNC(mac128_state::mac_via_irq));

	/* internal ram */
	m_ram->set_default_size("4M");
	m_ram->set_extra_options("2M,2560K,4M");

	MACPDS(config, "sepds", "maincpu");
	MACPDS_SLOT(config, "pds", "sepds", mac_sepds_cards, nullptr);

	subdevice<software_list_device>("flop_mac35_orig")->set_filter("MC68000,macse");
	subdevice<software_list_device>("flop_mac35_clean")->set_filter("MC68000,macse");
	subdevice<software_list_device>("flop35_list")->set_filter("MC68000,macse");
	subdevice<software_list_device>("hdd_list")->set_filter("MC68000,macse");
	subdevice<software_list_device>("cd_list")->set_filter("MC68000,macse");
}

void mac128_state::macsefd(machine_config &config)
{
	macse(config);

	SWIM1(config.replace(), m_iwm, C7M*2);
	m_iwm->phases_cb().set(FUNC(mac128_state::phases_w));
	m_iwm->devsel_cb().set(FUNC(mac128_state::devsel_se_w));

	applefdintf_device::add_35_hd(config, m_floppy[0]);
	applefdintf_device::add_35_hd(config, m_floppy[1]);

	SOFTWARE_LIST(config, "flop35hd_list").set_original("mac_hdflop").set_filter("MC68000,macse");
}

void mac128_state::macclasc(machine_config &config)
{
	macsefd(config);

//  config.device_remove("pds");
//  config.device_remove("sepds");

	NSCSI_CONNECTOR(config.replace(), "scsi:7").option_set("ncr5380", NCR53C80).machine_config([this](device_t *device) {
		ncr5380_device &adapter = downcast<ncr5380_device &>(*device);
		adapter.irq_handler().set(*this, FUNC(mac128_state::scsi_irq_w));
		adapter.drq_handler().set(m_scsihelp, FUNC(mac_scsi_helper_device::drq_w));
	});

	subdevice<software_list_device>("flop_mac35_orig")->set_filter("MC68000,macclasc");
	subdevice<software_list_device>("flop_mac35_clean")->set_filter("MC68000,macclasc");
	subdevice<software_list_device>("flop35_list")->set_filter("MC68000,macclasc");
	subdevice<software_list_device>("hdd_list")->set_filter("MC68000,macclasc");
	subdevice<software_list_device>("cd_list")->set_filter("MC68000,macclasc");
	subdevice<software_list_device>("flop35hd_list")->set_filter("MC68000,macclasc");
}

static INPUT_PORTS_START( macplus )
	PORT_START("MOUSE0") /* Mouse - button */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Mouse Button") PORT_CODE(MOUSECODE_BUTTON1)

	PORT_START("MOUSE1") /* Mouse - X AXIS */
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(0)

	PORT_START("MOUSE2") /* Mouse - Y AXIS */
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(0)
INPUT_PORTS_END

static INPUT_PORTS_START( macadb )
INPUT_PORTS_END

/***************************************************************************

  Machine driver(s)

***************************************************************************/

/*
ROM_START( mactw )
    ROM_REGION16_BE(0x100000, "bootrom", 0)
    ROM_LOAD( "rom4.3t_07-04-83.bin", 0x0000, 0x10000, CRC(d2c42f18) SHA1(f868c09ca70383a69751c37a5a3110a9597462a4) )
ROM_END

    // one twiggy mac (SN 1042) has the following sum16s on the 4x 16k eproms on the "512 EPROM ADAPTER" daughterboard:
    // U3 EPROM // 1 HI: "H1 // A04A"
    // U4 EPROM // 0 HI: "H0 // B6ED"
    // U5 EPROM // 1 LOW: "Lo1 // 6C8C"
    // U6 EPROM // 0 LOW: "Lo0 // F332"
    // This version (Rom Version 4.4T?) was publicly dumped by Adam Goolevitch, but one of the SUM16s (B5ED) does not match the ROM label (B6ED).
    // The ROM has been marked as bad pending a redump or other verification of correctness.
    ROMX_LOAD("h0__b6ed.u4",  0x00000, 0x04000, BAD_DUMP CRC(87136a61) SHA1(a33fc3b7908783a5742f06884fe44260447e5d55), ROM_SKIP(1) ) // SUM16: B5ED does not match written label
    ROMX_LOAD("lo0__f332.u6", 0x00001, 0x04000, CRC(3d04b1c5) SHA1(11fe22e8ce415edf4133d7cee559dce4ab0f7974), ROM_SKIP(1) ) // SUM16: F332
    ROMX_LOAD("h1__a04a.u3",  0x08000, 0x04000, CRC(fa9ae0d1) SHA1(e89826325caf053aad2c09d134c8c7483d442821), ROM_SKIP(1) ) // SUM16: A04A
    ROMX_LOAD("lo1__6c8c.u5", 0x08001, 0x04000, CRC(ca78a04e) SHA1(724d7d7b585c375cd5797e4334d6ab6267e798dc), ROM_SKIP(1) ) // SUM16: 6C8C


    // one twiggy mac (SN 1072, upc MA1M830241240) has the following sum16s on the 4x 16k EPROMs on the "512 EPROM ADAPTER" daughterboard:
    // U3 EPROM // 1 HI: "Rom 2.45 // H 1 // 1D79"
    // U4 EPROM // 0 HI: "ROM 2.45 // High0 // D4DF"
    // U5 EPROM // 1 LOW: "ROM 2.45 // LOW1 // 977F"
    // U6 EPROM // 0 LOW: "ROM 2.45 // LOW0 // C813"
    // This Twiggy mac has a prototype IWM labeled "<signetics> 8248 // XXX-X299 (C) // APPLE 82"
    // The ROM of this twiggy mac has been tentatively dated between March and April 1983, possibly March 11, 1983.
    // See https://macgui.com/news/article.php?t=517
*/

ROM_START( mac128k )
	ROM_REGION16_BE(0x100000, "bootrom", 0)
	// Apple used at least 3 manufacturers for these ROMs, but they're always Apple part numbers 342-0220-A and 342-0221-A
	ROMX_LOAD("342-0220-a.u6d",  0x00000, 0x08000, CRC(198210ad) SHA1(2590ff4af5ac0361babdf0dc5da18e2eecad454a), ROM_SKIP(1) )
	ROMX_LOAD("342-0221-a.u8d",  0x00001, 0x08000, CRC(fd2665c2) SHA1(8507932a854bd28196a17785c8b1851cb53eaf64), ROM_SKIP(1) )
	/* Labels seen in the wild:
	VTi:
	"<VTi logo along side> // 416 VH 2605 // 23256-1020 // 342-0220-A // (C)APPLE 83 // KOREA-AE"
	"<VTi logo along side> // 416 VH 2826 // 23256-1023 // 342-0221-A // (C)APPLE 83 // KOREA-AE"
	Synertek:
	"<Synertek 'S' logo> 8416 G // C19728 // 342-0220-A // (C)APPLE 83"
	"<Synertek 'S' logo> 8410 G // C19729 // 342-0221-A // (C)APPLE 83"
	Hitachi:
	[can't find reference for rom-hi]
	"<Hitachi 'target' logo> 8413 // 3256 016 JAPAN // (C)APPLE 83 // 342-0221-A"

	References:
	http://www.vintagecomputer.net/apple/Macintosh/Macintosh_motherboard.jpg
	https://upload.wikimedia.org/wikipedia/commons/3/34/Macintosh-motherboard.jpg
	https://68kmla.org/forums/uploads/monthly_01_2016/post-2105-0-31195100-1452296677.jpg
	https://68kmla.org/forums/uploads/monthly_12_2014/post-2597-0-46269000-1419299800.jpg
	http://cdn.cultofmac.com/wp-content/uploads/2014/01/12A-128k-Motherboard.jpg
	*/

	//ROM_REGION(0x3000, "pals", 0)
	// @U14E 342-0186-A  PAL16R8  ASG
	// @U2D  342-0187-A  PAL16L8  BMU1
	// @U3D  342-0189-A  PAL16R6  TSG
	// @U2E  342-0191-A  PAL16R4  BMU0
	// @U1E  342-0251-A  PAL16R8  LAG
	// @U1D  342-0254-A  PAL16R4A TSM
ROM_END

ROM_START( mac512k )
	ROM_REGION16_BE(0x100000, "bootrom", 0)
	ROMX_LOAD("342-0220-b.u6d",  0x00000, 0x08000, CRC(0dce9a3f) SHA1(101ca6570f5a273e400d1a8bc63e15ee0e94153e), ROM_SKIP(1) ) // "<VTi logo along side> 512 VH 6434 // 23256-1104 // 342-0220-B // (C) APPLE 84 // KOREA-A"
	ROMX_LOAD("342-0221-b.u8d",  0x00001, 0x08000, CRC(d51f376e) SHA1(575586109e876cffa4a4d472cb38771aa21b70cb), ROM_SKIP(1) ) // "<VTi logo along side> 512 VH 6709 // 23256-1105 // 342-0221-B // (C) APPLE 84 // KOREA-A"
	// reference: http://i.ebayimg.com/images/g/Uj8AAOSwvzRXy2tW/s-l1600.jpg

	//ROM_REGION(0x3000, "pals", 0)
	// @U14E 342-0186-A  PAL16R8  ASG
	// @U2D  342-0187-A  PAL16L8  BMU1
	// @U3D  342-0189-A  PAL16R6  TSG
	// @U2E  342-0191-A  PAL16R4  BMU0
	// @U1E  342-0251-A  PAL16R8  LAG
	// @U1D  342-0254-A  PAL16R4A TSM
ROM_END

ROM_START( unitron )
	ROM_REGION16_BE(0x100000, "bootrom", 0)
	ROM_LOAD16_WORD( "unitron_512.rom", 0x00000, 0x10000, CRC(1eabd37f) SHA1(a3d3696c08feac6805effb7ee07b68c2bf1a8dd7) )
	// pals are different from mac 128/512/512ke
ROM_END

ROM_START( utrn1024 )
	ROM_REGION16_BE(0x100000, "bootrom", 0)
	// CRCs match the original "Lonely Hearts" version 1 Mac Plus ROM: 4d1eeee1
	ROMX_LOAD( "342-0341-a.u6d", 0x000000, 0x010000, CRC(5095fe39) SHA1(be780580033d914b5035d60b5ebbd66bd1d28a9b), ROM_SKIP(1) ) // not correct label
	ROMX_LOAD( "342-0342-a.u8d", 0x000001, 0x010000, CRC(fb766270) SHA1(679f529fbfc05f9cc98924c53457d2996dfcb1a7), ROM_SKIP(1) ) // not correct label
	// unknown pals
ROM_END

ROM_START( mac512ke ) // 512ke has been observed with any of the v3, v2 or v1 macplus romsets installed, and v1 romsets are more common here than in the plus, since the 512ke lacks scsi, which is the cause of the major bug fixed between v1 and v2, hence 512ke is unaffected and was a good way for apple to use up the buggy roms rather than destroying them.
	ROM_REGION16_BE(0x100000, "bootrom", 0)
	ROM_SYSTEM_BIOS(0, "v3", "Loud Harmonicas")
	ROMX_LOAD( "342-0341-c.u6d", 0x000000, 0x010000, CRC(f69697e6) SHA1(41317614ac71eb94941e9952f6ea37407e21ffff), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "342-0342-b.u8d", 0x000001, 0x010000, CRC(49f25913) SHA1(72f658c02bae265e8845899582575fb7c784ee87), ROM_SKIP(1) | ROM_BIOS(0) )
	ROM_FILL(0x20000, 0x2, 0xff)    // ROM checks for same contents at 20000 and 40000 to determine if SCSI is present
	ROM_FILL(0x40000, 0x2, 0xaa)
	ROM_SYSTEM_BIOS(1, "v2", "Lonely Heifers")
	ROMX_LOAD( "342-0341-b.u6d", 0x000000, 0x010000, CRC(65341487) SHA1(bf43fa4f5a3dcbbac20f1fe1deedee0895454379), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "342-0342-a.u8d", 0x000001, 0x010000, CRC(fb766270) SHA1(679f529fbfc05f9cc98924c53457d2996dfcb1a7), ROM_SKIP(1) | ROM_BIOS(1) )
	ROM_FILL(0x20000, 0x2, 0xff)
	ROM_FILL(0x40000, 0x2, 0xaa)
	ROM_SYSTEM_BIOS(2, "v1", "Lonely Hearts")
	ROMX_LOAD( "342-0341-a.u6d", 0x000000, 0x010000, CRC(5095fe39) SHA1(be780580033d914b5035d60b5ebbd66bd1d28a9b), ROM_SKIP(1) | ROM_BIOS(2) )
	ROMX_LOAD( "342-0342-a.u8d", 0x000001, 0x010000, CRC(fb766270) SHA1(679f529fbfc05f9cc98924c53457d2996dfcb1a7), ROM_SKIP(1) | ROM_BIOS(2) )
	ROM_FILL(0x20000, 0x2, 0xff)
	ROM_FILL(0x40000, 0x2, 0xaa)
	/* from Technical note HW11 (https://www.fenestrated.net/mirrors/Apple%20Technotes%20(As%20of%202002)/hw/hw_11.html)
	1st version (Lonely Hearts, checksum 4D 1E EE E1)
	Bug in the SCSI driver; won't boot if external drive is turned off. We only produced about
	one and a half months worth of these.

	2nd version (Lonely Heifers, checksum 4D 1E EA E1):
	Fixed boot bug. This version is the vast majority of beige Macintosh Pluses.

	3rd version (Loud Harmonicas, checksum 4D 1F 81 72):
	Fixed bug for drives that return Unit Attention on power up or reset. Basically took the
	SCSI bus Reset command out of the boot sequence loop, so it will only reset once
	during boot sequence.
	*/
	/* Labels seen in the wild:
	v3/4d1f8172:
	    'ROM-HI' @ U6D:
	        "VLSI // 740 SA 1262 // 23512-1054 // 342-0341-C // (C)APPLE '83-'86 // KOREA A"
	        "342-0341-C // (C)APPLE 85,86 // (M)AMI 8849MBL // PHILLIPINES"
	    'ROM-LO' @ U8D:
	        "VLSI // 740 SA 1342 // 23512-1055 // 342-0342-B // (C)APPLE '83-'86 // KOREA A"
	        "<VLSI logo>VLSI // 8905AV 0 AS759 // 23512-1055 // 342-0342-B // (C)APPLE '85-'86"
	v2/4d1eeae1:
	    'ROM-HI' @ U6D:
	        "VTI // 624 V0 8636 // 23512-1010 // 342-0341-B // (C)APPLE '85 // MEXICO R"
	    'ROM-LO' @ U8D:
	        "VTI // 622 V0 B637 // 23512-1007 // 342-0342-A // (C)APPLE '83-'85 // KOREA A"
	v1/4d1eeee1:
	    'ROM-HI' @ U6D:
	        GUESSED, since this ROM is very rare: "VTI // 62? V0 86?? // 23512-1008 // 342-0341-A // (C)APPLE '83-'85 // KOREA A"
	    'ROM-LO' @ U8D is same as v2/4d1eeae1 'ROM-LO' @ U8D
	*/

	//ROM_REGION(0x3000, "pals", 0)
	// @U14E 342-0186-A  PAL16R8  ASG
	// @U2D  342-0187-A  PAL16L8  BMU1
	// @U3D  342-0189-A  PAL16R6  TSG
	// @U2E  342-0191-A  PAL16R4  BMU0
	// @U1E  342-0251-A  PAL16R8  LAG
	// @U1D  342-0254-A  PAL16R4A TSM
ROM_END

ROM_START( macplus ) // same notes as above apply here as well
	ROM_REGION16_BE(0x100000, "bootrom", 0)
	ROM_SYSTEM_BIOS(0, "v3", "Loud Harmonicas")
	ROMX_LOAD( "342-0341-c.u6d", 0x000000, 0x010000, CRC(f69697e6) SHA1(41317614ac71eb94941e9952f6ea37407e21ffff), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "342-0342-b.u8d", 0x000001, 0x010000, CRC(49f25913) SHA1(72f658c02bae265e8845899582575fb7c784ee87), ROM_SKIP(1) | ROM_BIOS(0) )
	ROM_FILL(0x20000, 0x2, 0xff)    // ROM checks for same contents at 20000 and 40000 to determine if SCSI is present
	ROM_FILL(0x40000, 0x2, 0xaa)
	ROM_SYSTEM_BIOS(1, "v2", "Lonely Heifers")
	ROMX_LOAD( "342-0341-b.u6d", 0x000000, 0x010000, CRC(65341487) SHA1(bf43fa4f5a3dcbbac20f1fe1deedee0895454379), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "342-0342-a.u8d", 0x000001, 0x010000, CRC(fb766270) SHA1(679f529fbfc05f9cc98924c53457d2996dfcb1a7), ROM_SKIP(1) | ROM_BIOS(1) )
	ROM_FILL(0x20000, 0x2, 0xff)
	ROM_FILL(0x40000, 0x2, 0xaa)
	ROM_SYSTEM_BIOS(2, "v1", "Lonely Hearts")
	ROMX_LOAD( "342-0341-a.u6d", 0x000000, 0x010000, CRC(5095fe39) SHA1(be780580033d914b5035d60b5ebbd66bd1d28a9b), ROM_SKIP(1) | ROM_BIOS(2) )
	ROMX_LOAD( "342-0342-a.u8d", 0x000001, 0x010000, CRC(fb766270) SHA1(679f529fbfc05f9cc98924c53457d2996dfcb1a7), ROM_SKIP(1) | ROM_BIOS(2) )
	ROM_FILL(0x20000, 0x2, 0xff)
	ROM_FILL(0x40000, 0x2, 0xaa)
	ROM_SYSTEM_BIOS(3, "romdisk", "mac68k.info self-boot (1/1/2015)")
	ROMX_LOAD( "modplus-harp2.bin", 0x000000, 0x028000, CRC(ba56078d) SHA1(debdf328ac73e1662d274a044d8750224f47edef), ROM_GROUPWORD | ROM_BIOS(3) )
	ROM_SYSTEM_BIOS(4, "romdisk2", "bigmessofwires.com ROMinator (2/25/2015)")
	ROMX_LOAD( "rominator-20150225-lo.bin", 0x000001, 0x080000, CRC(62cf2a0b) SHA1(f78ebb0919dd9e094bef7952b853b70e66d05e01), ROM_SKIP(1) | ROM_BIOS(4) )
	ROMX_LOAD( "rominator-20150225-hi.bin", 0x000000, 0x080000, CRC(a28ba8ec) SHA1(9ddcf500727955c60db0ff24b5ca2458f53fd89a), ROM_SKIP(1) | ROM_BIOS(4) )

	//ROM_REGION(0x3000, "pals", 0)
	// @U11E 342-0517-A  PAL16??  ASG
	// @U2D  341-0514-A  PAL16L8  BMU1
	// @U3D  342-0516-A  PAL16R6  TSG
	// @U3E  342-0519-A  PAL20??  CAS
	// @U2E  342-0520-A  PAL20R4A BMU2
	// @U1E  342-0515-A  PAL16R8  LAG
	// @U1D  342-0522-A  VP16RP8MPC (PAL16R4A on schem) TSM
ROM_END

ROM_START( macplusj )
	ROM_REGION16_BE(0x100000, "bootrom", 0)
	ROMX_LOAD( "342-0441-a.u6d", 0x000000, 0x020000, CRC(ba5b74fb) SHA1(a39d10753d355144d6f483d9d3dc72993aa4b345), ROM_SKIP(1) )
	ROMX_LOAD( "342-0442-a.u8d", 0x000001, 0x020000, CRC(19e552a7) SHA1(00ca715e653101bf5f42cf86fc5be11e028e2a6b), ROM_SKIP(1) )
ROM_END

ROM_START( macse )
	ROM_REGION16_BE(0x100000, "bootrom", 0)
	ROM_LOAD16_WORD( "macse.rom",  0x00000, 0x40000, CRC(0f7ff80c) SHA1(58532b7d0d49659fd5228ac334a1b094f0241968))
	// GLU HAL (mask PAL)
ROM_END

ROM_START( macsefd )
	ROM_REGION16_BE(0x100000, "bootrom", 0)
	ROM_LOAD( "be06e171.rom", 0x000000, 0x040000, CRC(f530cb10) SHA1(d3670a90273d12e53d86d1228c068cb660b8c9d1) )
	// GLU HAL (mask PAL)
ROM_END

ROM_START( macclasc )
	ROM_REGION16_BE(0x100000, "bootrom", 0) // a49f9914, second half of chip dump is the 6.0.3 XO rom disk
	// this dump is big endian
	ROM_LOAD( "341-0813__=c=1983-90_apple__japan__910d_d.27c4096_be.ue1", 0x000000, 0x080000, CRC(510d7d38) SHA1(ccd10904ddc0fb6a1d216b2e9effd5ec6cf5a83d) )
ROM_END

} // anonymous namespace


/*    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT    CLASS         INIT              COMPANY              FULLNAME */
//COMP( 1983, mactw,    0,       0,      mac128k,  macplus, mac128_state, mac_driver_init, "Apple Computer",    "Macintosh (4.3T Prototype)",  MACHINE_SUPPORTS_SAVE )
COMP( 1984, mac128k,  0,       0,      mac128k,  macplus, mac128_state, mac_driver_init,  "Apple Computer",    "Macintosh 128k",  MACHINE_SUPPORTS_SAVE )
COMP( 1984, mac512k,  mac128k, 0,      mac512k,  macplus, mac128_state, mac_driver_init,  "Apple Computer",    "Macintosh 512k",  MACHINE_SUPPORTS_SAVE )
COMP( 1986, mac512ke, macplus, 0,      mac512ke, macplus, mac128_state, mac_driver_init,  "Apple Computer",    "Macintosh 512ke", MACHINE_SUPPORTS_SAVE )
COMP( 1985, unitron,  macplus, 0,      mac512ke, macplus, mac128_state, mac_driver_init,  "bootleg (Unitron)", "Mac 512",  MACHINE_SUPPORTS_SAVE )
COMP( 1986, macplus,  0,       0,      macplus,  macplus, mac128_state, mac_driver_init,  "Apple Computer",    "Macintosh Plus",  MACHINE_SUPPORTS_SAVE )
COMP( 1986, macplusj, macplus, 0,      macplus,  macplus, mac128_state, mac_driver_init,  "Apple Computer",    "Macintosh Plus (Japan)",  MACHINE_SUPPORTS_SAVE )
COMP( 1985, utrn1024, macplus, 0,      macplus,  macplus, mac128_state, mac_driver_init,  "bootleg (Unitron)", "Unitron 1024",  MACHINE_SUPPORTS_SAVE )
COMP( 1987, macse,    0,       0,      macse,    macadb, mac128_state,  mac_driver_init,  "Apple Computer",   "Macintosh SE",  MACHINE_SUPPORTS_SAVE )
COMP( 1989, macsefd,  0,       0,      macsefd,  macadb, mac128_state,  mac_driver_init,  "Apple Computer",   "Macintosh SE (FDHD)",  MACHINE_SUPPORTS_SAVE )
COMP( 1990, macclasc, 0,       0,      macclasc, macadb, mac128_state,  mac_driver_init,  "Apple Computer",   "Macintosh Classic",  MACHINE_SUPPORTS_SAVE )



mac3.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/***************************************************************************

    Mac III by L.J.Technical Systems

    Keys:
    0-9,A-F: hex input
    M      : memory display and edit
    P      : port display and edit
    L      : load from cassette
    S      : save to cassette
    G      : program run
    R      : register display and edit
    +      : up (use UP-arrow key)
    -      : down (use DOWN-arrow key)

    TODO:
    - fix cassette interface, maybe a SCN2681 issue.
    - keypad logic is guessed, need schematic.

****************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/m6502/m6502.h"
#include "imagedev/cassette.h"
#include "machine/6522via.h"
#include "machine/input_merger.h"
#include "machine/mc68681.h"

#include "speaker.h"

#include "mac3.lh"


#define VERBOSE 0
#include "logmacro.h"


namespace {

class mac3_state : public driver_device
{
public:
	static constexpr feature_type imperfect_features() { return feature::TAPE; }

	mac3_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_duart(*this, "duart")
		, m_cassette(*this, "cassette")
		, m_keypad(*this, "X%u", 0U)
		, m_digits(*this, "digit%u", 0U)
	{ }

	void mac3(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<scn2681_device> m_duart;
	required_device<cassette_image_device> m_cassette;
	required_ioport_array<8> m_keypad;
	output_finder<8> m_digits;

	void mem_map(address_map &map) ATTR_COLD;

	TIMER_CALLBACK_MEMBER(cassette_timer);

	void display_latch_w(uint8_t data);
	void icm7228_write_w(int state);
	void icm7228_mode_w(int state);

	emu_timer *m_cassette_timer = nullptr;

	uint8_t m_disp_latch;
	uint8_t m_icm7228_control;
	uint8_t m_icm7228_digit;
	int m_icm7228_write;
	int m_icm7228_mode;
	uint8_t m_keycol;
};


void mac3_state::mem_map(address_map &map)
{
	map(0x0000, 0x1fff).ram();
	map(0x7000, 0x7000).mirror(0x0fff).w(FUNC(mac3_state::display_latch_w));
	map(0x8000, 0x800f).mirror(0x0ff0).rw("duart", FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0x9000, 0x900f).mirror(0x0ff0).m("via", FUNC(via6522_device::map));
	map(0xa000, 0xbfff).rom().region("user", 0);
	map(0xc000, 0xffff).rom().region("monitor", 0);
}


static INPUT_PORTS_START( mac3 )
	PORT_START("X0")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')

	PORT_START("X1")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')

	PORT_START("X2")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')

	PORT_START("X3")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')

	PORT_START("X4")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')

	PORT_START("X5")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')

	PORT_START("X6")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR('^')

	PORT_START("X7")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR('V')
INPUT_PORTS_END


TIMER_CALLBACK_MEMBER(mac3_state::cassette_timer)
{
	m_duart->ip6_w((m_cassette->input() > 0.0) ? 1 : 0);
}


void mac3_state::display_latch_w(uint8_t data)
{
	LOG("%s display_latch_w: %02x\n", machine().describe_context(), data);
	m_disp_latch = data;

	if (data == 0xfe)
		m_keycol = 0;
	else
		m_keycol = (m_keycol + 1) & 7;

	m_duart->ip3_w(BIT(m_keypad[m_keycol]->read(), 3));
	m_duart->ip4_w(BIT(m_keypad[m_keycol]->read(), 4));
	m_duart->ip5_w(BIT(m_keypad[m_keycol]->read(), 5));
}


void mac3_state::icm7228_write_w(int state)
{
	if (state && !m_icm7228_write)
	{
		switch (m_icm7228_mode)
		{
		case 0:
			if (BIT(m_icm7228_control, 7)) // data incoming
			{
				LOG("%s icm7228_display_w: digit %d seg %02x\n", machine().describe_context(), m_icm7228_digit, m_disp_latch);
				if (m_icm7228_digit < 8)
					m_digits[m_icm7228_digit++] = m_disp_latch ^ 0x80; // invert decimal point bit
			}
			break;

		case 1:
			// b4  0 - Shutdown,         1 - Normal Operation,
			// b5  0 - Decode,           1 - No Decode
			// b6  0 - Code B Decoding,  1 - Hexadecimal Decoding
			// b7  0 - No Data Incoming, 1 - Data Incoming
			m_icm7228_control = m_disp_latch;
			LOG("%s icm7228_control_w: %02x\n", machine().describe_context(), m_icm7228_control);
			if (BIT(m_icm7228_control, 7))
				m_icm7228_digit = 0;
			break;
		}
	}

	m_icm7228_write = state;
}

void mac3_state::icm7228_mode_w(int state)
{
	m_icm7228_mode = state;
}


void mac3_state::machine_start()
{
	m_digits.resolve();

	m_cassette_timer = timer_alloc(FUNC(mac3_state::cassette_timer), this);
	m_cassette_timer->adjust(attotime::from_hz(44100), 0, attotime::from_hz(44100));

	save_item(NAME(m_icm7228_control));
	save_item(NAME(m_icm7228_digit));
	save_item(NAME(m_keycol));
}

void mac3_state::machine_reset()
{
	m_icm7228_control = 0;
	m_icm7228_digit = 0;
	m_keycol = 0;
}


static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END


void mac3_state::mac3(machine_config &config)
{
	M6502(config, m_maincpu, 1_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mac3_state::mem_map);

	INPUT_MERGER_ANY_HIGH(config, "irqs").output_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	config.set_default_layout(layout_mac3);

	via6522_device &via(MOS6522(config, "via", 1_MHz_XTAL));
	via.irq_handler().set("irqs", FUNC(input_merger_device::in_w<0>));

	// TODO: ICM7228A device.

	scn2681_device &duart(SCN2681(config, "duart", 3.6864_MHz_XTAL));
	duart.irq_cb().set("irqs", FUNC(input_merger_device::in_w<1>));
	duart.a_tx_cb().set("rs232a", FUNC(rs232_port_device::write_txd));
	duart.b_tx_cb().set("rs232b", FUNC(rs232_port_device::write_txd));
	duart.outport_cb().set([this] (uint8_t data) { LOG("%s outport_cb %02x\n", machine().describe_context(), data); });
	duart.outport_cb().append("rs232a", FUNC(rs232_port_device::write_rts)).bit(0);
	duart.outport_cb().append("rs232b", FUNC(rs232_port_device::write_rts)).bit(1);
	duart.outport_cb().append(FUNC(mac3_state::icm7228_write_w)).bit(4);
	duart.outport_cb().append(FUNC(mac3_state::icm7228_mode_w)).bit(5);
	duart.outport_cb().append([this](int state) { m_cassette->output(state ? +1.0 : -1.0); }).bit(7);

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, nullptr));
	rs232a.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));
	rs232a.rxd_handler().set("duart", FUNC(scn2681_device::rx_a_w));
	rs232a.cts_handler().set("duart", FUNC(scn2681_device::ip0_w));
	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set("duart", FUNC(scn2681_device::rx_b_w));
	rs232b.cts_handler().set("duart", FUNC(scn2681_device::ip1_w));

	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
}


ROM_START( mac3 )
	ROM_REGION(0x4000, "monitor", 0)
	ROM_SYSTEM_BIOS(0, "22ai", "V2.2 Applications, I/O") // supports DT35 Applications and DT34 Input/output devices modules
	ROMX_LOAD("mac3_6502_v2.2_ai.bin", 0x0000, 0x4000, CRC(439c30e3) SHA1(a0a0b23dd67167321bbe346659da3a6e0d52aee0), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "22a",  "V2.2 Applications")      // supports DT35 Applications module (has bug in Terminal mode)
	ROMX_LOAD("mac3_6502_v2.2_a.bin",  0x0000, 0x4000, CRC(bf8f777d) SHA1(c8b624ae93d0239c8893097bcf44664457a6b2fe), ROM_BIOS(1))

	ROM_REGION(0x2000, "user", ROMREGION_ERASE00)
ROM_END

} // anonymous namespace


//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT         COMPANY                    FULLNAME                      FLAGS
COMP( 1990, mac3,   0,      0,      mac3,    mac3,   mac3_state,   empty_init,  "L.J.Technical Systems",   "Mac III 6502 Microcomputer", MACHINE_NOT_WORKING )



macii.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Nathan Woods, Raphael Nabet, R. Belmont, O. Galibert
/****************************************************************************

    macii.cpp
    Macintosh II ("Becks, Cabernet, Ikki, Little Big Mac, Paris, Reno, Uzi, Milwaukee")
    Macintosh II FDHD (Codenames not known)
    Macintosh IIx ("Spock, Stratos")
    Macintosh IIcx ("Atlantic, Cobra, Aurora")
    Macintosh SE/30 ("Fafnir, Green Jade")

    Driver by R. Belmont and O. Galibert, based on work by Nathan Woods, Ernesto Corvi, and Raphael Nabet

****************************************************************************/

#include "emu.h"

#include "adbmodem.h"
#include "macadb.h"
#include "macrtc.h"
#include "macscsi.h"
#include "mactoolbox.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/devices.h"
#include "bus/macpds/macpds.h"
#include "bus/nubus/nubus.h"
#include "bus/nubus/cards.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68020.h"
#include "cpu/m68000/m68030.h"
#include "cpu/m68000/m68kmusashi.h"
#include "machine/6522via.h"
#include "machine/applefdintf.h"
#include "machine/iwm.h"
#include "machine/ncr5380.h"
#include "machine/nscsi_bus.h"
#include "machine/ram.h"
#include "machine/swim1.h"
#include "machine/timer.h"
#include "machine/z80scc.h"
#include "sound/asc.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/ap_dsk35.h"


namespace {

#define C15M (15.6672_MHz_XTAL)
#define C7M  (C15M/2)

// video parameters for classic Macs
constexpr int MAC_H_VIS   = 512;
constexpr int MAC_V_VIS   = 342;
constexpr int MAC_H_TOTAL = 704;  // (512+192)
constexpr int MAC_V_TOTAL = 370; // (342+28)

// Mac driver data

class macii_state:public driver_device
{
public:
	macii_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_via1(*this, "via6522_0"),
		m_via2(*this, "via6522_1"),
		m_asc(*this, "asc"),
		m_adbmodem(*this, "adbmodem"),
		m_macadb(*this, "macadb"),
		m_ram(*this, RAM_TAG),
		m_scc(*this, "scc"),
		m_ncr5380(*this, "scsi:7:ncr5380"),
		m_scsihelp(*this, "scsihelp"),
		m_fdc(*this, "fdc"),
		m_floppy(*this, "fdc:%d", 0U),
		m_rtc(*this, "rtc"),
		m_vram(*this,"vram"),
		m_screen(*this, "screen"),
		m_overlay(0),
		m_via2_vbl(0),
		m_se30_vbl_enable(0),
		m_nubus_irq_state(0),
		m_glue_ram_size(0),
		m_is_original_ii(false),
		m_adb_irq_pending(0),
		m_screen_buffer(0),
		m_scc_interrupt(false),
		m_via_interrupt(false),
		m_via2_interrupt(false),
		m_scsi_interrupt(false),
		m_last_taken_interrupt(0),
		m_via2_ca1_hack(0),
		m_rom_size(0),
		m_rom_ptr(nullptr),
		m_scanline_timer(nullptr),
		m_cur_floppy(nullptr)
	{
	}

	void macii(machine_config &config);
	void maciihmu(machine_config &config);
	void maciihd(machine_config &config);
	void maciix(machine_config &config);
	void maciicx(machine_config &config);
	void macse30(machine_config &config);

	void macii_init();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void set_memory_overlay(int overlay);
	void scc_mouse_irq(int x, int y);
	void nubus_slot_interrupt(u8 slot, u32 state);
	void set_scc_interrupt(int state);
	void set_via_interrupt(int value);
	void set_via2_interrupt(int value);
	void field_interrupts();
	void vblank_irq();
	void update_volume();

	void via_sync();
	u16 via_r(offs_t offset);
	void via_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	u16 via2_r(offs_t offset);
	void via2_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	u16 iwm_r(offs_t offset, u16 mem_mask = ~0);
	void iwm_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	u16 scc_r(offs_t offset);
	void scc_w(offs_t offset, u16 data);
	u16 scsi_r(offs_t offset, u16 mem_mask = ~0);
	void scsi_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	u32 scsi_drq_r(offs_t offset, u32 mem_mask = ~0);
	void scsi_drq_w(offs_t offset, u32 data, u32 mem_mask = ~0);
	void scsi_berr_w(u8 data);

	template <int Slot> void nubus_irq_w(int state);

	void scsi_irq(int state);
	void mac_asc_irq(int state);
	void adb_irq_w(int state) { m_adb_irq_pending = state; }

	void macii_map(address_map &map) ATTR_COLD;
	void macse30_map(address_map &map) ATTR_COLD;

	void phases_w(u8 phases);
	void devsel_w(u8 devsel);

	u32 screen_update_macse30(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	TIMER_CALLBACK_MEMBER(scanline_tick);
	u8 via_in_a();
	u8 iicx_via_in_a();
	u8 via_in_b();
	void via_out_a(u8 data);
	void via_out_b(u8 data);
	void se30_via_out_b(u8 data);
	u8 via2_in_a();
	u8 via2_in_b();
	u8 iix_via2_in_b();
	void via2_out_a(u8 data);
	void via2_out_b(u8 data);
	void hmmu_via2_out_b(u8 data);
	void state_load();
	void via_irq(int state);
	void via2_irq(int state);

	required_device<cpu_device> m_maincpu;
	required_device<via6522_device> m_via1;
	required_device<via6522_device> m_via2;
	required_device<asc_device> m_asc;
	optional_device<adbmodem_device> m_adbmodem;
	required_device<macadb_device> m_macadb;
	required_device<ram_device> m_ram;
	required_device<z80scc_device> m_scc;
	required_device<ncr53c80_device> m_ncr5380;
	required_device<mac_scsi_helper_device> m_scsihelp;
	required_device<applefdintf_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<rtc3430042_device> m_rtc;
	optional_shared_ptr<u32> m_vram;
	optional_device<screen_device> m_screen;

	u32 m_overlay;
	u32 m_via2_vbl;
	u32 m_se30_vbl_enable;
	u8 m_nubus_irq_state = 0x3f;
	u8 m_glue_ram_size;
	bool m_is_original_ii;

	int m_adb_irq_pending;
	int m_screen_buffer;

	// interrupts
	int m_scc_interrupt, m_via_interrupt, m_via2_interrupt, m_scsi_interrupt, m_last_taken_interrupt;

	int m_via2_ca1_hack;
	u32 m_rom_size;
	u32 *m_rom_ptr;

	emu_timer *m_scanline_timer;

	floppy_image_device *m_cur_floppy;
};

u32 macii_state::screen_update_macse30(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	u32 const video_base = (m_screen_buffer ? 0x8000 : 0) + (MAC_H_VIS / 8);
	u16 const *const video_ram = (const u16 *)&m_vram[video_base / 4];

	for (int y = 0; y < MAC_V_VIS; y++)
	{
		u32 *const line = &bitmap.pix(y);

		for (int x = 0; x < MAC_H_VIS; x += 16)
		{
			u16 const word = video_ram[((y * MAC_H_VIS) / 16) + ((x / 16) ^ 1)];
			for (int b = 0; b < 16; b++)
			{
				line[x + b] = ((word >> (15 - b)) & 0x0001) ? 0 : 0xffffff;
			}
		}
	}
	return 0;
}

void macii_state::field_interrupts()
{
	int take_interrupt = -1;

	if (m_scc_interrupt)
	{
		take_interrupt = 4;
	}
	else if (m_via2_interrupt)
	{
		take_interrupt = 2;
	}
	else if (m_via_interrupt)
	{
		take_interrupt = 1;
	}

	if (m_last_taken_interrupt > -1)
	{
		m_maincpu->set_input_line(m_last_taken_interrupt, CLEAR_LINE);
		m_last_taken_interrupt = -1;
	}

	if (take_interrupt > -1)
	{
		m_maincpu->set_input_line(take_interrupt, ASSERT_LINE);
		m_last_taken_interrupt = take_interrupt;
	}
}

void macii_state::set_scc_interrupt(int state)
{
	m_scc_interrupt = state;
	this->field_interrupts();
}

void macii_state::set_via_interrupt(int value)
{
	m_via_interrupt = value;
	this->field_interrupts();
}

void macii_state::set_via2_interrupt(int value)
{
	m_via2_interrupt = value;
	this->field_interrupts();
}

void macii_state::mac_asc_irq(int state)
{
	m_via2->write_cb1(state ^ 1);
}

void macii_state::set_memory_overlay(int overlay)
{
	address_space &space = m_maincpu->space(AS_PROGRAM);

	if (overlay != m_overlay)
	{
		m_overlay = overlay;
		if (overlay)
		{
			space.install_rom(0x00000000, m_rom_size - 1, (void *)m_rom_ptr);
		}
		else
		{
			via2_out_a(0x3f);
		}
	}
}

u16 macii_state::scsi_r(offs_t offset, u16 mem_mask)
{
	int reg = (offset >> 3) & 0xf;

	bool pseudo_dma = (reg == 6) && (offset == 0x130);

	return m_scsihelp->read_wrapper(pseudo_dma, reg) << 8;
}

u32 macii_state::scsi_drq_r(offs_t offset, u32 mem_mask)
{
	switch (mem_mask)
	{
	case 0xff000000:
		return m_scsihelp->read_wrapper(true, 6) << 24;

	case 0xffff0000:
		return (m_scsihelp->read_wrapper(true, 6) << 24) | (m_scsihelp->read_wrapper(true, 6) << 16);

	case 0xffffffff:
		return (m_scsihelp->read_wrapper(true, 6) << 24) | (m_scsihelp->read_wrapper(true, 6) << 16) | (m_scsihelp->read_wrapper(true, 6) << 8) | m_scsihelp->read_wrapper(true, 6);

	default:
		logerror("scsi_drq_r: unknown mem_mask %08x\n", mem_mask);
	}

	return 0;
}

void macii_state::scsi_drq_w(offs_t offset, u32 data, u32 mem_mask)
{
	switch (mem_mask)
	{
	case 0xff000000:
		m_scsihelp->write_wrapper(true, 0, data >> 24);
		break;

	case 0xffff0000:
		m_scsihelp->write_wrapper(true, 0, data >> 24);
		m_scsihelp->write_wrapper(true, 0, data >> 16);
		break;

	case 0xffffffff:
		m_scsihelp->write_wrapper(true, 0, data >> 24);
		m_scsihelp->write_wrapper(true, 0, data >> 16);
		m_scsihelp->write_wrapper(true, 0, data >> 8);
		m_scsihelp->write_wrapper(true, 0, data & 0xff);
		break;

	default:
		logerror("scsi_drq_w: unknown mem_mask %08x\n", mem_mask);
		break;
	}
}

void macii_state::scsi_w(offs_t offset, u16 data, u16 mem_mask)
{
	int reg = (offset >> 3) & 0xf;

	//  logerror("scsi_w: data %x offset %x mask %x (PC=%x)\n", data, offset, mem_mask, m_maincpu->pc());

	bool pseudo_dma = (reg == 0) && (offset == 0x100);

	m_scsihelp->write_wrapper(pseudo_dma, reg, data >> 8);
}

void macii_state::scsi_irq(int state)
{
}

void macii_state::scsi_berr_w(u8 data)
{
	m_maincpu->pulse_input_line(M68K_LINE_BUSERROR, attotime::zero);
}

u16 macii_state::scc_r(offs_t offset)
{
	u16 result = m_scc->dc_ab_r(offset);
	return (result << 8) | result;
}

void macii_state::scc_w(offs_t offset, u16 data)
{
	m_scc->dc_ab_w(offset, data >> 8);
}

u16 macii_state::iwm_r(offs_t offset, u16 mem_mask)
{
	u16 result = m_fdc->read(offset >> 8);

	if (!machine().side_effects_disabled())
		m_maincpu->adjust_icount(-5);

	return (result << 8) | result;
}

void macii_state::iwm_w(offs_t offset, u16 data, u16 mem_mask)
{
	if (ACCESSING_BITS_0_7)
		m_fdc->write((offset >> 8), data & 0xff);
	else
		m_fdc->write((offset >> 8), data >> 8);

	if (!machine().side_effects_disabled())
		m_maincpu->adjust_icount(-5);
}

u8 macii_state::via_in_a()
{
	return 0x81;
}

u8 macii_state::iicx_via_in_a()
{
	return 0x81 | 0x40; // bit 6 set for IIcx and SE/30
}

u8 macii_state::via_in_b()
{
	int val = 0;

	if (!m_adb_irq_pending)
	{
		val |= 0x08;
	}

	val |= m_rtc->data_r();
	return val;
}

void macii_state::via_out_a(u8 data)
{
	m_screen_buffer = BIT(data, 6);
	if (m_cur_floppy)
		m_cur_floppy->ss_w(BIT(data, 5));
	set_memory_overlay(BIT(data, 4));
}

void macii_state::via_out_b(u8 data)
{
	m_adbmodem->set_via_state((data & 0x30) >> 4);

	m_rtc->ce_w(BIT(data, 2));
	m_rtc->data_w(BIT(data, 0));
	m_rtc->clk_w(BIT(data, 1));
}

void macii_state::se30_via_out_b(u8 data)
{
	// 0x40 = 0 means enable vblank on SE/30
	m_se30_vbl_enable = BIT(data, 6) ^ 1;

	// clear the interrupt if we disabled it
	if (!m_se30_vbl_enable)
	{
		nubus_slot_interrupt(0xe, 0);
	}

	via_out_b(data);
}

void macii_state::via_irq(int state)
{
	/* interrupt the 68k (level 1) */
	set_via_interrupt(state);
}

void macii_state::via_sync()
{
	// The via runs at 783.36KHz while the main cpu runs at 15MHz or
	// more, so we need to sync the access with the via clock.  Plus
	// the whole access takes half a (via) cycle and ends when synced
	// with the main cpu again.

	// Get the main cpu time
	u64 cycle = m_maincpu->total_cycles();

	// Get the number of the cycle the via is in at that time
	u64 via_cycle = cycle * m_via1->clock() / m_maincpu->clock();

	// The access is going to start at via_cycle+1 and end at
	// via_cycle+1.5, compute what that means in maincpu cycles (the
	// +1 rounds up, since the clocks are too different to ever be
	// synced).
	u64 main_cycle = (via_cycle * 2 + 3) * m_maincpu->clock() / (2 * m_via1->clock()) + 1;

	// Finally adjust the main cpu icount as needed.
	m_maincpu->adjust_icount(-int(main_cycle - cycle));
}

u16 macii_state::via_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		via_sync();

	u16 data;

	offset >>= 8;
	offset &= 0x0f;

	data = m_via1->read(offset);

	return (data & 0xff) | (data << 8);
}

void macii_state::via_w(offs_t offset, u16 data, u16 mem_mask)
{
	via_sync();

	offset >>= 8;
	offset &= 0x0f;

	if (ACCESSING_BITS_0_7)
		m_via1->write(offset, data & 0xff);
	if (ACCESSING_BITS_8_15)
		m_via1->write(offset, (data >> 8) & 0xff);
}

void macii_state::via2_irq(int state)
{
	set_via2_interrupt(state);
}

u16 macii_state::via2_r(offs_t offset)
{
	int data;

	if (!machine().side_effects_disabled())
		via_sync();

	offset >>= 8;
	offset &= 0x0f;

	data = m_via2->read(offset);

	return (data & 0xff) | (data << 8);
}

void macii_state::via2_w(offs_t offset, u16 data, u16 mem_mask)
{
	via_sync();

	offset >>= 8;
	offset &= 0x0f;

	if (ACCESSING_BITS_0_7)
		m_via2->write(offset, data & 0xff);
	if (ACCESSING_BITS_8_15)
		m_via2->write(offset, (data >> 8) & 0xff);
}

u8 macii_state::via2_in_a()
{
	return m_glue_ram_size | m_nubus_irq_state;
}

u8 macii_state::via2_in_b()
{
	// PB6 set for SE/30 and IIcx
	return 0xcf;
}

u8 macii_state::iix_via2_in_b()
{
	return 0x87;
}

void macii_state::via2_out_a(u8 data)
{
	m_glue_ram_size = data & 0xc0;

	if (!m_overlay)
	{
		address_space &space = m_maincpu->space(AS_PROGRAM);
		const u8 *ram = m_ram->pointer();
		const u32 memsize = m_ram->size();
		u32 asize = (memsize >= 0x4000000) ? 0x4000000 : memsize;
		u32 bsize = (memsize >= 0x4000000) ? (memsize - 0x4000000) : 0;

		space.unmap_readwrite(0x00000000, 0x3fffffff);

		u32 blocation = 0;
		switch (m_glue_ram_size >> 6)
		{
			case 0: // bank B at 1 meg
				blocation = 0x00100000;
				break;

			case 1: // bank B at 2 megs
				blocation = 0x00200000;
				break;

			case 2: // bank B at 8 megs
				blocation = 0x00800000;
				break;

			case 3: // bank B at 32 megs
				blocation = 0x02000000;
				break;
		}

		bool bNoMirroring = false;
		bool bMirrorB = false;
		switch (memsize/0x100000)
		{
			case 1:
			case 16:
				bNoMirroring = true;
				break;

			case 2:
				asize = bsize = 0x100000;
				break;

			case 4:
				bNoMirroring = true;
				asize = bsize = 0x200000;
				break;

			case 5:
				asize = 0x400000;
				bsize = 0x100000;
				bMirrorB = true;
				break;

			case 8:
				asize = bsize = 0x400000;
				break;

			case 17:
				asize = 0x01000000;
				bsize = 0x00100000;
				bMirrorB = true;
				break;

			case 20:
				asize = 0x01000000;
				bsize = 0x00400000;
				bMirrorB = true;
				break;

			case 65:
				asize = 0x04000000;
				bsize = 0x00100000;
				bMirrorB = true;
				break;

			case 68:
				asize = 0x04000000;
				bsize = 0x00400000;
				bMirrorB = true;
				break;

			case 80:
				asize = 0x04000000;
				bsize = 0x01000000;
				bMirrorB = true;
				break;

			case 128:
				asize = 0x04000000;
				bsize = 0x04000000;
				break;
		}

		// Wait for a window small enough to satisfy the weird fragile ROM code in the original II
		if (blocation <= memsize)
		{
			space.install_ram(0x00000000, memsize - 1, (void *)ram);

			// Almost all supported sizes in the ROM need a bank A mirror afterward, but "non-power-of-2"
			// RAM sizes (ones where the size has more than 1 "1" bit, like 5 MiB or 68 MiB) need bank B
			// as the mirror instead.
			if (!bNoMirroring)
			{
				if (bMirrorB)
				{
					space.install_ram(memsize, memsize + bsize - 1, (void *)&ram[asize]);
				}
				else
				{
					space.install_ram(memsize, memsize + asize - 1, (void *)ram);
				}
			}

			// If our image is smaller than the window, plant bank B at its location to pass the initial test.
			if ((bsize > 0) && (blocation >= (memsize + asize)))
			{
				space.install_ram(blocation, blocation + bsize - 1, (void *)&ram[asize]);
			}
		}
		else
		{
			// The FDHD and later ROM system deaths if no RAM is present before running the memory
			// sizing checks, so always have 1 MiB valid at 0.
			space.install_ram(0x00000000, 0x000fffff, (void *)ram);
		}
	}
}

void macii_state::via2_out_b(u8 data)
{
	// chain 60.15 Hz to VIA1
	m_via1->write_ca1(data >> 7);
}

void macii_state::hmmu_via2_out_b(u8 data)
{
	m68000_musashi_device *m68k = downcast<m68000_musashi_device *>(m_maincpu.target());
	m68k->set_hmmu_enable((data & 0x8) ? M68K_HMMU_DISABLE : M68K_HMMU_ENABLE_II);
	via2_out_b(data);
}

void macii_state::machine_start()
{
	if (m_screen)
	{
		this->m_scanline_timer = timer_alloc(FUNC(macii_state::scanline_tick), this);
		this->m_scanline_timer->adjust(m_screen->time_until_pos(0, 0));
	}

	save_item(NAME(m_overlay));
	save_item(NAME(m_via2_vbl));
	save_item(NAME(m_se30_vbl_enable));
	save_item(NAME(m_nubus_irq_state));
	save_item(NAME(m_glue_ram_size));
	save_item(NAME(m_adb_irq_pending));
	save_item(NAME(m_screen_buffer));
	save_item(NAME(m_scc_interrupt));
	save_item(NAME(m_via_interrupt));
	save_item(NAME(m_via2_interrupt));
	save_item(NAME(m_scsi_interrupt));
	save_item(NAME(m_last_taken_interrupt));
	save_item(NAME(m_via2_ca1_hack));

	address_space &space = m_maincpu->space(AS_PROGRAM);
	const u32 rom_id = space.read_dword(0);

	if ((rom_id == 0x97851db6) || (rom_id == 0x9779d2c4))
	{
		m_is_original_ii = true;
	}
}

void macii_state::machine_reset()
{
	m_last_taken_interrupt = -1;

	/* setup the memory overlay */
	m_overlay = -1; // insure no match
	this->set_memory_overlay(1);

	/* setup videoram */
	this->m_screen_buffer = 1;

	m_via2_ca1_hack = 1;
	m_via2->write_ca1(1);
	m_via2->write_cb1(1);

	m_scsi_interrupt = 0;

	m_via2_vbl = 0;
	m_se30_vbl_enable = 0;
	m_nubus_irq_state = 0xff;
	m_last_taken_interrupt = 0;
}

void macii_state::state_load()
{
	int overlay = m_overlay;
	m_overlay = -1;
	set_memory_overlay(overlay);
}

void macii_state::macii_init()
{
	m_overlay = 1;
	m_scsi_interrupt = 0;
	m_scc_interrupt = 0;
	m_via_interrupt = 0;
	m_via2_interrupt = 0;

	m_rom_size = memregion("bootrom")->bytes();
	m_rom_ptr = reinterpret_cast<u32 *>(memregion("bootrom")->base());

	m_overlay = -1;
	set_memory_overlay(1);

	memset(m_ram->pointer(), 0, m_ram->size());

	/* save state stuff */
	machine().save().register_postload(save_prepost_delegate(FUNC(macii_state::state_load), this));
}

void macii_state::nubus_slot_interrupt(u8 slot, u32 state)
{
	static const u8 masks[8] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
	u8 mask = 0x3f;

	slot -= 9;

	if (state)
	{
		m_nubus_irq_state &= ~masks[slot];
	}
	else
	{
		m_nubus_irq_state |= masks[slot];
	}

	if ((m_nubus_irq_state & mask) != mask)
	{
		// HACK: sometimes we miss an ack (possible misbehavior in the VIA?)
		if (m_via2_ca1_hack == 0)
		{
			m_via2->write_ca1(1);
		}
		m_via2_ca1_hack = 0;
		m_via2->write_ca1(0);
	}
	else
	{
		m_via2_ca1_hack = 1;
		m_via2->write_ca1(1);
	}
}

void macii_state::vblank_irq()
{
	// handle SE/30 vblank IRQ
	if (m_se30_vbl_enable)
	{
		m_via2_vbl ^= 1;
		if (!m_via2_vbl)
		{
			this->nubus_slot_interrupt(0xe, 1);
		}
	}
}

TIMER_CALLBACK_MEMBER(macii_state::scanline_tick)
{
	const int scanline = m_screen->vpos();
	if (scanline == 0)
	{
		vblank_irq();
	}
	const int next_scanline = (scanline + 1) % MAC_V_TOTAL;
	m_scanline_timer->adjust(m_screen->time_until_pos(next_scanline), next_scanline);
}

template <int Slot> void macii_state::nubus_irq_w(int state)
{
	nubus_slot_interrupt(Slot, state);
}

template void macii_state::nubus_irq_w<9>(int state);
template void macii_state::nubus_irq_w<0xa>(int state);
template void macii_state::nubus_irq_w<0xb>(int state);
template void macii_state::nubus_irq_w<0xc>(int state);
template void macii_state::nubus_irq_w<0xd>(int state);
template void macii_state::nubus_irq_w<0xe>(int state);

void macii_state::phases_w(u8 phases)
{
	if (m_cur_floppy)
		m_cur_floppy->seek_phase_w(phases);
}

void macii_state::devsel_w(u8 devsel)
{
	if (devsel == 1)
		m_cur_floppy = m_floppy[0]->get_device();
	else if (devsel == 2)
		m_cur_floppy = m_floppy[1]->get_device();
	else
		m_cur_floppy = nullptr;
	m_fdc->set_floppy(m_cur_floppy);
	if (m_cur_floppy)
		m_cur_floppy->ss_w((m_via1->read_pa() & 0x20) >> 5);
}

/***************************************************************************
    ADDRESS MAPS
***************************************************************************/

void macii_state::macii_map(address_map &map)
{
	map(0x40000000, 0x4003ffff).rom().region("bootrom", 0).mirror(0x0ffc0000);

	// MMU remaps I/O without the F
	map(0x50000000, 0x50001fff).rw(FUNC(macii_state::via_r), FUNC(macii_state::via_w)).mirror(0x00f00000);
	map(0x50002000, 0x50003fff).rw(FUNC(macii_state::via2_r), FUNC(macii_state::via2_w)).mirror(0x00f00000);
	map(0x50004000, 0x50005fff).rw(FUNC(macii_state::scc_r), FUNC(macii_state::scc_w)).mirror(0x00f00000);
	map(0x50006000, 0x50006003).w(FUNC(macii_state::scsi_drq_w)).mirror(0x00f00000);
	map(0x50006060, 0x50006063).r(FUNC(macii_state::scsi_drq_r)).mirror(0x00f00000);
	map(0x50010000, 0x50011fff).rw(FUNC(macii_state::scsi_r), FUNC(macii_state::scsi_w)).mirror(0x00f00000);
	map(0x50012000, 0x50013fff).rw(FUNC(macii_state::scsi_drq_r), FUNC(macii_state::scsi_drq_w)).mirror(0x00f00000);
	map(0x50014000, 0x50015fff).rw(m_asc, FUNC(asc_device::read), FUNC(asc_device::write)).mirror(0x00f00000);
	map(0x50016000, 0x50017fff).rw(FUNC(macii_state::iwm_r), FUNC(macii_state::iwm_w)).mirror(0x00f00000);
	map(0x50040000, 0x50041fff).rw(FUNC(macii_state::via_r), FUNC(macii_state::via_w)).mirror(0x00f00000);
}

void macii_state::macse30_map(address_map &map)
{
	macii_map(map);

	map(0xfe000000, 0xfe00ffff).ram().share("vram");
	map(0xfee00000, 0xfee0ffff).ram().share("vram").mirror(0x000f0000);
	map(0xfeffe000, 0xfeffffff).rom().region("se30vrom", 0x0);
}

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

void macii_state::macii(machine_config &config)
{
	M68020PMMU(config, m_maincpu, C15M);
	m_maincpu->set_addrmap(AS_PROGRAM, &macii_state::macii_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	SPEAKER(config, "speaker", 2).front();
	ASC(config, m_asc, C15M, asc_device::asc_type::ASC);
	m_asc->irqf_callback().set(FUNC(macii_state::mac_asc_irq));
	m_asc->add_route(0, "speaker", 1.0, 0);
	m_asc->add_route(1, "speaker", 1.0, 1);

	RTC3430042(config, m_rtc, XTAL(32'768));
	m_rtc->cko_cb().set(m_via1, FUNC(via6522_device::write_ca2));

	IWM(config, m_fdc, C15M);
	m_fdc->devsel_cb().set(FUNC(macii_state::devsel_w));
	m_fdc->phases_cb().set(FUNC(macii_state::phases_w));
	applefdintf_device::add_35(config, m_floppy[0]);
	applefdintf_device::add_35_nc(config, m_floppy[1]);

	SCC85C30(config, m_scc, C7M);
	m_scc->configure_channels(3'686'400, 3'686'400, 3'686'400, 3'686'400);
	m_scc->out_int_callback().set(FUNC(macii_state::set_scc_interrupt));
	m_scc->out_txda_callback().set("printer", FUNC(rs232_port_device::write_txd));
	m_scc->out_txdb_callback().set("modem", FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232a(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_scc, FUNC(z80scc_device::rxa_w));
	rs232a.dcd_handler().set(m_scc, FUNC(z80scc_device::dcda_w));
	rs232a.cts_handler().set(m_scc, FUNC(z80scc_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "modem", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_scc, FUNC(z80scc_device::rxb_w));
	rs232b.dcd_handler().set(m_scc, FUNC(z80scc_device::dcdb_w));
	rs232b.cts_handler().set(m_scc, FUNC(z80scc_device::ctsb_w));

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:1", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3").option_set("cdrom", NSCSI_CDROM_APPLE).machine_config([](device_t *device)
																							{
			device->subdevice<cdda_device>("cdda")->add_route(0, "^^speaker", 1.0, 0);
			device->subdevice<cdda_device>("cdda")->add_route(1, "^^speaker", 1.0, 1); });
	NSCSI_CONNECTOR(config, "scsi:4", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", mac_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr5380", NCR53C80).machine_config([this](device_t *device)
																					 {
		ncr53c80_device &adapter = downcast<ncr53c80_device &>(*device);
		adapter.irq_handler().set(*this, FUNC(macii_state::scsi_irq));
		adapter.drq_handler().set(m_scsihelp, FUNC(mac_scsi_helper_device::drq_w)); });

	MAC_SCSI_HELPER(config, m_scsihelp);
	m_scsihelp->scsi_read_callback().set(m_ncr5380, FUNC(ncr53c80_device::read));
	m_scsihelp->scsi_write_callback().set(m_ncr5380, FUNC(ncr53c80_device::write));
	m_scsihelp->scsi_dma_read_callback().set(m_ncr5380, FUNC(ncr53c80_device::dma_r));
	m_scsihelp->scsi_dma_write_callback().set(m_ncr5380, FUNC(ncr53c80_device::dma_w));
	m_scsihelp->cpu_halt_callback().set_inputline(m_maincpu, INPUT_LINE_HALT);
	m_scsihelp->timeout_error_callback().set(FUNC(macii_state::scsi_berr_w));

	SOFTWARE_LIST(config, "hdd_list").set_original("mac_hdd").set_filter("MC68020");
	SOFTWARE_LIST(config, "cd_list").set_original("mac_cdrom").set_filter("MC68020");

	nubus_device &nubus(NUBUS(config, "nubus", 0));
	nubus.set_space(m_maincpu, AS_PROGRAM);
	nubus.out_irq9_callback().set(FUNC(macii_state::nubus_irq_w<9>));
	nubus.out_irqa_callback().set(FUNC(macii_state::nubus_irq_w<0xa>));
	nubus.out_irqb_callback().set(FUNC(macii_state::nubus_irq_w<0xb>));
	nubus.out_irqc_callback().set(FUNC(macii_state::nubus_irq_w<0xc>));
	nubus.out_irqd_callback().set(FUNC(macii_state::nubus_irq_w<0xd>));
	nubus.out_irqe_callback().set(FUNC(macii_state::nubus_irq_w<0xe>));
	NUBUS_SLOT(config, "nb9", "nubus", mac_nubus_cards, "mdc824");
	NUBUS_SLOT(config, "nba", "nubus", mac_nubus_cards, nullptr);
	NUBUS_SLOT(config, "nbb", "nubus", mac_nubus_cards, nullptr);
	NUBUS_SLOT(config, "nbc", "nubus", mac_nubus_cards, nullptr);
	NUBUS_SLOT(config, "nbd", "nubus", mac_nubus_cards, nullptr);
	NUBUS_SLOT(config, "nbe", "nubus", mac_nubus_cards, nullptr);

	R65NC22(config, m_via1, C7M/10);
	m_via1->readpa_handler().set(FUNC(macii_state::via_in_a));
	m_via1->readpb_handler().set(FUNC(macii_state::via_in_b));
	m_via1->writepa_handler().set(FUNC(macii_state::via_out_a));
	m_via1->writepb_handler().set(FUNC(macii_state::via_out_b));
	m_via1->irq_handler().set(FUNC(macii_state::via_irq));

	R65NC22(config, m_via2, C7M/10);
	m_via2->readpa_handler().set(FUNC(macii_state::via2_in_a));
	m_via2->readpb_handler().set(FUNC(macii_state::via2_in_b));
	m_via2->writepa_handler().set(FUNC(macii_state::via2_out_a));
	m_via2->writepb_handler().set(FUNC(macii_state::via2_out_b));
	m_via2->irq_handler().set(FUNC(macii_state::via2_irq));

	ADBMODEM(config, m_adbmodem, C7M);
	m_adbmodem->via_clock_callback().set(m_via1, FUNC(via6522_device::write_cb1));
	m_adbmodem->via_data_callback().set(m_via1, FUNC(via6522_device::write_cb2));
	m_adbmodem->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
	m_adbmodem->irq_callback().set(FUNC(macii_state::adb_irq_w));
	m_via1->cb2_handler().set(m_adbmodem, FUNC(adbmodem_device::set_via_data));
	config.set_perfect_quantum(m_maincpu);

	MACADB(config, m_macadb, C15M);
	m_macadb->adb_data_callback().set(m_adbmodem, FUNC(adbmodem_device::set_adb_line));

	RAM(config, m_ram);
	m_ram->set_default_size("2M");
	// The original Mac II will go off the rails if it sees RAM greater than 8MB.
	// This was fixed for the II FDHD/IIx/IIcx/SE30 ROM.
	m_ram->set_extra_options("1M,4M,5M,8M");

	SOFTWARE_LIST(config, "flop_mac35_orig").set_original("mac_flop_orig").set_filter("MC68020");
	SOFTWARE_LIST(config, "flop_mac35_clean").set_original("mac_flop_clcracked").set_filter("MC68020");
	SOFTWARE_LIST(config, "flop35_list").set_original("mac_flop").set_filter("MC68020");
}

void macii_state::maciihmu(machine_config &config)
{
	macii(config);

	M68020HMMU(config.replace(), m_maincpu, C15M);
	m_maincpu->set_addrmap(AS_PROGRAM, &macii_state::macii_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	m_via2->writepb_handler().set(FUNC(macii_state::hmmu_via2_out_b));
}

void macii_state::maciihd(machine_config &config)
{
	macii(config);

	// Mac II FDHD = Mac II with a SWIM1 instead of IWM
	SWIM1(config.replace(), m_fdc, C15M);
	m_fdc->phases_cb().set(FUNC(macii_state::phases_w));
	m_fdc->devsel_cb().set(FUNC(macii_state::devsel_w));

	applefdintf_device::add_35_hd(config, m_floppy[0]);
	applefdintf_device::add_35_hd(config, m_floppy[1]);
	SOFTWARE_LIST(config, "flop35hd_list").set_original("mac_hdflop").set_filter("MC68020");

	// The table of valid RAM sizes is at 0x4080366E in the 97221136 ROM (II FDHD, IIx, IIcx, SE/30).
	// Shift each byte left by 20 bits to get the size in bytes.  0x01 => 0x00100000 (1 MiB) and so on.
	m_ram->set_extra_options("1M,4M,5M,8M,16M,17M,20M,32M,64M,65M,68M,80M,128M");
}

void macii_state::maciix(machine_config &config)
{
	maciihd(config);

	// IIx = Mac II FDHD with a 68030 instead of the 020
	M68030(config.replace(), m_maincpu, C15M);
	m_maincpu->set_addrmap(AS_PROGRAM, &macii_state::macii_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	m_via2->readpb_handler().set(FUNC(macii_state::iix_via2_in_b));

	subdevice<software_list_device>("hdd_list")->set_filter("MC68030");
	subdevice<software_list_device>("cd_list")->set_filter("MC68030");
	subdevice<software_list_device>("flop_mac35_orig")->set_filter("MC68030");
	subdevice<software_list_device>("flop_mac35_clean")->set_filter("MC68030");
	subdevice<software_list_device>("flop35_list")->set_filter("MC68030");
}

void macii_state::maciicx(machine_config &config)
{
	maciix(config);

	// IIcx = IIx with 3 fewer slots
	config.device_remove("nbc");
	config.device_remove("nbd");
	config.device_remove("nbe");

	m_via1->readpa_handler().set(FUNC(macii_state::iicx_via_in_a));
	m_via2->readpb_handler().set(FUNC(macii_state::via2_in_b));
}

void macii_state::macse30(machine_config &config)
{
	maciicx(config);

	// SE/30 = IIx with no slots and built-in video
	m_maincpu->set_addrmap(AS_PROGRAM, &macii_state::macse30_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	m_via1->writepb_handler().set(FUNC(macii_state::se30_via_out_b));
	m_via1->readpa_handler().set(FUNC(macii_state::iicx_via_in_a));
	m_via2->readpb_handler().set(FUNC(macii_state::iix_via2_in_b));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_video_attributes(VIDEO_UPDATE_BEFORE_VBLANK);
	m_screen->set_raw(15.6672_MHz_XTAL, MAC_H_TOTAL, 0, MAC_H_VIS, MAC_V_TOTAL, 0, MAC_V_VIS);
	m_screen->set_screen_update(FUNC(macii_state::screen_update_macse30));

	config.device_remove("nb9");
	config.device_remove("nba");
	config.device_remove("nbb");
	config.device_remove("nubus");

	se30_pds_bus_device &se30bus(MACSE30_PDS_BUS(config, "pds", 0));
	se30bus.set_space(m_maincpu, AS_PROGRAM);
	se30bus.set_bus_mode(nubus_device::nubus_mode_t::SE30);
	se30bus.set_screen_tag("screen");
	se30bus.out_irq9_callback().set(FUNC(macii_state::nubus_irq_w<9>));
	se30bus.out_irqa_callback().set(FUNC(macii_state::nubus_irq_w<0xa>));
	se30bus.out_irqb_callback().set(FUNC(macii_state::nubus_irq_w<0xb>));
	se30bus.out_irqc_callback().set(FUNC(macii_state::nubus_irq_w<0xc>));
	se30bus.out_irqd_callback().set(FUNC(macii_state::nubus_irq_w<0xd>));
	se30bus.out_irqe_callback().set(FUNC(macii_state::nubus_irq_w<0xe>));
	NUBUS_SLOT(config, "pds030", "pds", mac_pds030_cards, nullptr);
}

static INPUT_PORTS_START( macadb )
INPUT_PORTS_END

ROM_START( macii )
	ROM_REGION32_BE(0x40000, "bootrom", 0)
	ROM_SYSTEM_BIOS(0, "default", "rev. B")
	ROMX_LOAD( "9779d2c4.rom", 0x000000, 0x040000, CRC(4df6d054) SHA1(db6b504744281369794e26ba71a6e385cf6227fa), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "original", "rev. A")
	ROMX_LOAD( "97851db6.rom", 0x000000, 0x040000, CRC(8c8b9d03) SHA1(5c264fe976f1e8495d364947c932a5e8309b4300), ROM_BIOS(1) )
ROM_END

ROM_START( maciihmu )
	ROM_REGION32_BE(0x40000, "bootrom", 0)
	ROM_SYSTEM_BIOS(0, "default", "rev. B")
	ROMX_LOAD( "9779d2c4.rom", 0x000000, 0x040000, CRC(4df6d054) SHA1(db6b504744281369794e26ba71a6e385cf6227fa), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "original", "rev. A")
	ROMX_LOAD( "97851db6.rom", 0x000000, 0x040000, CRC(8c8b9d03) SHA1(5c264fe976f1e8495d364947c932a5e8309b4300), ROM_BIOS(1) )
ROM_END

ROM_START( mac2fdhd )   // same ROM for II FDHD, IIx, IIcx, and SE/30
	ROM_REGION32_BE(0x40000, "bootrom", 0)
	ROM_LOAD( "97221136.rom", 0x000000, 0x040000, CRC(ce3b966f) SHA1(753b94351d94c369616c2c87b19d568dc5e2764e) )
ROM_END

ROM_START( maciix )
	ROM_REGION32_BE(0x40000, "bootrom", 0)
	ROM_LOAD( "97221136.rom", 0x000000, 0x040000, CRC(ce3b966f) SHA1(753b94351d94c369616c2c87b19d568dc5e2764e) )
ROM_END

ROM_START( maciicx )
	ROM_REGION32_BE(0x40000, "bootrom", 0)
	ROM_LOAD( "97221136.rom", 0x000000, 0x040000, CRC(ce3b966f) SHA1(753b94351d94c369616c2c87b19d568dc5e2764e) )
ROM_END

ROM_START( macse30 )
	ROM_REGION32_BE(0x40000, "bootrom", 0)
	ROM_LOAD( "97221136.rom", 0x000000, 0x040000, CRC(ce3b966f) SHA1(753b94351d94c369616c2c87b19d568dc5e2764e) )

	ROM_REGION32_BE(0x2000, "se30vrom", 0)
	ROM_LOAD( "se30vrom.uk6", 0x000000, 0x002000, CRC(b74c3463) SHA1(584201cc67d9452b2488f7aaaf91619ed8ce8f03) )
ROM_END

} // anonymous namespace

//    YEAR  NAME       PARENT    COMPAT  MACHINE   INPUT    CLASS        INIT        COMPANY           FULLNAME
COMP( 1987, macii,     0,        0,      macii,    macadb,  macii_state, macii_init, "Apple Computer", "Macintosh II",                 MACHINE_SUPPORTS_SAVE )
COMP( 1987, maciihmu,  macii,    0,      maciihmu, macadb,  macii_state, macii_init, "Apple Computer", "Macintosh II (w/o 68851 MMU)", MACHINE_SUPPORTS_SAVE )
COMP( 1988, mac2fdhd,  0,        0,      maciihd,  macadb,  macii_state, macii_init, "Apple Computer", "Macintosh II (FDHD)",          MACHINE_SUPPORTS_SAVE )
COMP( 1988, maciix,    mac2fdhd, 0,      maciix,   macadb,  macii_state, macii_init, "Apple Computer", "Macintosh IIx",                MACHINE_SUPPORTS_SAVE )
COMP( 1989, macse30,   mac2fdhd, 0,      macse30,  macadb,  macii_state, macii_init, "Apple Computer", "Macintosh SE/30",              MACHINE_SUPPORTS_SAVE )
COMP( 1989, maciicx,   mac2fdhd, 0,      maciicx,  macadb,  macii_state, macii_init, "Apple Computer", "Macintosh IIcx",               MACHINE_SUPPORTS_SAVE )



maciici.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/****************************************************************************

    maciici.cpp
    Mac IIci ("Cobra II, Aurora, Aurora25/16, Pacific, Atlantic")
    Mac IIsi ("Erickson, Rafiki, Hobie Cat")

    By R. Belmont

    These are the RBV/MDU (RAM Based Video/Memory Decode Unit) near-twins.
    IIci cost-reduced the IIcx and added on-board video.
    IIsi cost-reduced the IIci with a slower CPU and Egret ADB instead of
    the PIC ADB modem and Apple RTC/PRAM chip.

****************************************************************************/

#include "emu.h"

#include "adbmodem.h"
#include "egret.h"
#include "macadb.h"
#include "macrtc.h"
#include "macscsi.h"
#include "mactoolbox.h"
#include "rbv.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/devices.h"
#include "bus/nubus/nubus.h"
#include "bus/nubus/cards.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68030.h"
#include "machine/applefdintf.h"
#include "machine/ram.h"
#include "machine/swim1.h"
#include "machine/timer.h"
#include "machine/6522via.h"
#include "machine/ncr5380.h"
#include "machine/nscsi_bus.h"
#include "machine/z80scc.h"
#include "sound/asc.h"

#include "softlist_dev.h"
#include "speaker.h"

namespace {

static constexpr XTAL C15M = 31.3344_MHz_XTAL / 2;
static constexpr XTAL C7M = 31.3344_MHz_XTAL / 4;

class maciici_state : public driver_device
{
public:
	maciici_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_via1(*this, "via1"),
		m_rbv(*this, "rbv"),
		m_macadb(*this, "macadb"),
		m_ram(*this, RAM_TAG),
		m_adbmodem(*this, "adbmodem"),
		m_asc(*this, "asc"),
		m_scsibus1(*this, "scsi"),
		m_ncr5380(*this, "scsi:7:ncr5380"),
		m_scsihelp(*this, "scsihelp"),
		m_fdc(*this, "fdc"),
		m_floppy(*this, "fdc:%d", 0U),
		m_scc(*this, "scc"),
		m_rtc(*this, "rtc"),
		m_egret(*this, "egret"),
		m_config(*this, "config")
	{
	}

	void maciixi_base(machine_config &config);
	void maciici(machine_config &config);
	void maciisi(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<m68030_device> m_maincpu;
	required_device<via6522_device> m_via1;
	required_device<rbv_device> m_rbv;
	required_device<macadb_device> m_macadb;
	required_device<ram_device> m_ram;
	optional_device<adbmodem_device> m_adbmodem;
	required_device<asc_device> m_asc;
	required_device<nscsi_bus_device> m_scsibus1;
	required_device<ncr5380_device> m_ncr5380;
	required_device<mac_scsi_helper_device> m_scsihelp;
	required_device<applefdintf_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<z80scc_device> m_scc;
	optional_device<rtc3430042_device> m_rtc;
	optional_device<egret_device> m_egret;
	optional_ioport m_config;

	void set_via2_interrupt(int value);
	void field_interrupts();

	uint32_t m_overlay = 0;
	u32 *m_rom_ptr = nullptr;
	u32 m_rom_size = 0;
	int m_scc_interrupt = false, m_via_interrupt = false, m_via2_interrupt = false, m_last_taken_interrupt = false;
	int m_adb_irq_pending = 0;

	uint16_t via_r(offs_t offset);
	void via_w(offs_t offset, uint16_t data, uint16_t mem_mask);
	uint8_t via_in_a();
	uint8_t via_in_a_iisi();
	uint8_t via_in_b();
	uint8_t via_in_b_iisi();
	void via_out_a(uint8_t data);
	void via_out_b(uint8_t data);
	void via_out_b_iisi(uint8_t data);
	void via_sync();
	void via_irq(int state);
	void via_out_cb2(int state);
	void via_out_cb2_iisi(int state);
	void adb_irq_w(int state) { m_adb_irq_pending = state; }
	void scc_irq_w(int state);

	uint32_t rom_switch_r(offs_t offset);

	void maciici_map(address_map &map) ATTR_COLD;

	u16 scc_r(offs_t offset)
	{
		u16 result = m_scc->dc_ab_r(offset);
		return (result << 8) | result;
	}
	void scc_w(offs_t offset, u16 data)
	{
		m_scc->dc_ab_w(offset, data >> 8);
	}

	u16 scsi_r(offs_t offset, u16 mem_mask = ~0);
	void scsi_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	u32 scsi_drq_r(offs_t offset, u32 mem_mask = ~0);
	void scsi_drq_w(offs_t offset, u32 data, u32 mem_mask = ~0);

	void scsi_berr_w(u8 data)
	{
		m_maincpu->pulse_input_line(M68K_LINE_BUSERROR, attotime::zero);
	}

	void egret_reset_w(int state)
	{
		m_maincpu->set_input_line(INPUT_LINE_HALT, state);
		m_maincpu->set_input_line(INPUT_LINE_RESET, state);
	}

	floppy_image_device *m_cur_floppy = nullptr;
	int m_hdsel;
	void phases_w(uint8_t phases);
	void devsel_w(uint8_t devsel);

	uint16_t iwm_r(offs_t offset, u16 mem_mask)
	{
		if (!machine().side_effects_disabled())
		{
			m_maincpu->adjust_icount(-5);
		}

		u16 result = m_fdc->read((offset >> 8) & 0xf);
		return result << 8;
	}

	void iwm_w(offs_t offset, u16 data, u16 mem_mask)
	{
		if (ACCESSING_BITS_0_7)
			m_fdc->write((offset >> 8) & 0xf, data & 0xff);
		else
			m_fdc->write((offset >> 8) & 0xf, data >> 8);
	}
};

void maciici_state::machine_start()
{
	m_rbv->set_ram_info((u32 *)m_ram->pointer(), m_ram->size());

	m_rom_ptr = (u32 *)memregion("bootrom")->base();
	m_rom_size = memregion("bootrom")->bytes();

	m_last_taken_interrupt = -1;
}

void maciici_state::machine_reset()
{
	// main cpu shouldn't start until Egret wakes it up
	if (m_egret)
	{
		m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
	}

	if (m_config)
	{
		m_maincpu->set_fpu_enable(BIT(m_config->read(), 0));
	}

	// put ROM mirror at 0
	address_space &space = m_maincpu->space(AS_PROGRAM);
	const u32 memory_size = std::min((u32)0x3fffff, m_rom_size);
	const u32 memory_end = memory_size - 1;
	offs_t memory_mirror = memory_end & ~(memory_size - 1);

	space.unmap_write(0x00000000, memory_end);
	space.install_rom(0x00000000, memory_end & ~memory_mirror, memory_mirror, m_rom_ptr);
	m_overlay = true;
}

uint32_t maciici_state::rom_switch_r(offs_t offset)
{
	// disable the overlay
	if (m_overlay)
	{
		address_space &space = m_maincpu->space(AS_PROGRAM);
		const u32 memory_end = m_ram->size() - 1;
		void *memory_data = m_ram->pointer();
		offs_t memory_mirror = memory_end & ~memory_end;

		space.install_ram(0x00000000, memory_end & ~memory_mirror, memory_mirror, memory_data);
		m_overlay = false;
	}

	// printf("rom_switch_r: offset %08x ROM_size -1 = %08x, masked = %08x\n", offset, m_rom_size-1, offset & ((m_rom_size - 1)>>2));

	return m_rom_ptr[offset & ((m_rom_size - 1) >> 2)];
}

void maciici_state::field_interrupts()
{
	int take_interrupt = -1;

	if (m_scc_interrupt)
	{
		take_interrupt = 4;
	}
	else if (m_via2_interrupt)
	{
		take_interrupt = 2;
	}
	else if (m_via_interrupt)
	{
		take_interrupt = 1;
	}

	if (m_last_taken_interrupt > -1)
	{
		m_maincpu->set_input_line(m_last_taken_interrupt, CLEAR_LINE);
		m_last_taken_interrupt = -1;
	}

	if (take_interrupt > -1)
	{
		m_maincpu->set_input_line(take_interrupt, ASSERT_LINE);
		m_last_taken_interrupt = take_interrupt;
	}
}

void maciici_state::via_irq(int state)
{
	m_via_interrupt = state;
	field_interrupts();
}

void maciici_state::scc_irq_w(int state)
{
	m_scc_interrupt = state;
	field_interrupts();
}

void maciici_state::set_via2_interrupt(int value)
{
	m_via2_interrupt = value;
	field_interrupts();
}

void maciici_state::via_sync()
{
	// The via runs at 783.36KHz while the main cpu runs at 15MHz or
	// more, so we need to sync the access with the via clock.  Plus
	// the whole access takes half a (via) cycle and ends when synced
	// with the main cpu again.

	// Get the main cpu time
	u64 cycle = m_maincpu->total_cycles();

	// Get the number of the cycle the via is in at that time
	u64 via_cycle = cycle * m_via1->clock() / m_maincpu->clock();

	// The access is going to start at via_cycle+1 and end at
	// via_cycle+1.5, compute what that means in maincpu cycles (the
	// +1 rounds up, since the clocks are too different to ever be
	// synced).
	u64 main_cycle = (via_cycle * 2 + 3) * m_maincpu->clock() / (2 * m_via1->clock()) + 1;

	// Finally adjust the main cpu icount as needed.
	m_maincpu->adjust_icount(-int(main_cycle - cycle));
}

uint16_t maciici_state::via_r(offs_t offset)
{
	uint16_t data;

	offset >>= 8;
	offset &= 0x0f;

	if (!machine().side_effects_disabled())
		via_sync();

	data = m_via1->read(offset);

	return (data & 0xff) | (data << 8);
}

void maciici_state::via_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	offset >>= 8;
	offset &= 0x0f;

	via_sync();

	if (ACCESSING_BITS_0_7)
		m_via1->write(offset, data & 0xff);
	if (ACCESSING_BITS_8_15)
		m_via1->write(offset, (data >> 8) & 0xff);
}

uint8_t maciici_state::via_in_a()
{
	return 0xc7; // IIci: PA6 | PA2 | PA1
}

uint8_t maciici_state::via_in_a_iisi()
{
	return 0x97; // IIci: PA4 | PA2 | PA1
}

uint8_t maciici_state::via_in_b()
{
	u8 val = m_rtc->data_r();

	if (!m_adb_irq_pending)
	{
		val |= 0x08;
	}

	return val;
}

uint8_t maciici_state::via_in_b_iisi()
{
	return m_egret->get_xcvr_session() << 3;
}

void maciici_state::via_out_a(uint8_t data)
{
	int hdsel = BIT(data, 5);
	if (hdsel != m_hdsel)
	{
		if (m_cur_floppy)
		{
			m_cur_floppy->ss_w(hdsel);
		}
	}
	m_hdsel = hdsel;
}

void maciici_state::via_out_b(uint8_t data)
{
	//  printf("%s VIA1 OUT B: %02x\n", machine().describe_context().c_str(), data);
	m_adbmodem->set_via_state((data & 0x30) >> 4);

m_rtc->ce_w(BIT(data, 2));
	m_rtc->data_w(BIT(data, 0));
	m_rtc->clk_w(BIT(data, 1));
}

void maciici_state::via_out_b_iisi(uint8_t data)
{
	m_egret->set_via_full(BIT(data, 4));
	m_egret->set_sys_session(BIT(data, 5));
}

void maciici_state::via_out_cb2(int state)
{
//  m_macadb->adb_data_w(state);
}

void maciici_state::via_out_cb2_iisi(int state)
{
	m_egret->set_via_data(state & 1);
}

/***************************************************************************
    ADDRESS MAPS
***************************************************************************/

void maciici_state::maciici_map(address_map &map)
{
	map(0x40000000, 0x4007ffff).r(FUNC(maciici_state::rom_switch_r)).mirror(0x0ff80000);

	map(0x50000000, 0x50001fff).rw(FUNC(maciici_state::via_r), FUNC(maciici_state::via_w)).mirror(0x00f00000);
	map(0x50004000, 0x50005fff).rw(FUNC(maciici_state::scc_r), FUNC(maciici_state::scc_w)).mirror(0x00f00000);
	map(0x50006000, 0x50007fff).rw(FUNC(maciici_state::scsi_drq_r), FUNC(maciici_state::scsi_drq_w)).mirror(0x00f00000);
	map(0x50010000, 0x50011fff).rw(FUNC(maciici_state::scsi_r), FUNC(maciici_state::scsi_w)).mirror(0x00f00000);
	map(0x50012000, 0x50013fff).rw(FUNC(maciici_state::scsi_drq_r), FUNC(maciici_state::scsi_drq_w)).mirror(0x00f00000);
	map(0x50014000, 0x50015fff).rw(m_asc, FUNC(asc_device::read), FUNC(asc_device::write)).mirror(0x00f00000);
	map(0x50016000, 0x50017fff).rw(FUNC(maciici_state::iwm_r), FUNC(maciici_state::iwm_w)).mirror(0x00f00000);
	map(0x50024000, 0x50027fff).m(m_rbv, FUNC(rbv_device::map)).mirror(0x00f00000);
	map(0x50040000, 0x50041fff).rw(FUNC(maciici_state::via_r), FUNC(maciici_state::via_w)).mirror(0x00f00000);
}

u16 maciici_state::scsi_r(offs_t offset, u16 mem_mask)
{
	const int reg = (offset >> 3) & 0xf;
	const bool pseudo_dma = (reg == 6) && (offset == 0x130);

	return m_scsihelp->read_wrapper(pseudo_dma, reg) << 8;
}

void maciici_state::scsi_w(offs_t offset, u16 data, u16 mem_mask)
{
	const int reg = (offset >> 3) & 0xf;
	const bool pseudo_dma = (reg == 0) && (offset == 0x100);

	m_scsihelp->write_wrapper(pseudo_dma, reg, data >> 8);
}

u32 maciici_state::scsi_drq_r(offs_t offset, u32 mem_mask)
{
	switch (mem_mask)
	{
	case 0xff000000:
		return m_scsihelp->read_wrapper(true, 6) << 24;

	case 0xffff0000:
		return (m_scsihelp->read_wrapper(true, 6) << 24) | (m_scsihelp->read_wrapper(true, 6) << 16);

	case 0xffffffff:
		return (m_scsihelp->read_wrapper(true, 6) << 24) | (m_scsihelp->read_wrapper(true, 6) << 16) | (m_scsihelp->read_wrapper(true, 6) << 8) | m_scsihelp->read_wrapper(true, 6);

	default:
		logerror("scsi_drq_r: unknown mem_mask %08x\n", mem_mask);
	}

	return 0;
}

void maciici_state::scsi_drq_w(offs_t offset, u32 data, u32 mem_mask)
{
	switch (mem_mask)
	{
	case 0xff000000:
		m_scsihelp->write_wrapper(true, 0, data >> 24);
		break;

	case 0xffff0000:
		m_scsihelp->write_wrapper(true, 0, data >> 24);
		m_scsihelp->write_wrapper(true, 0, data >> 16);
		break;

	case 0xffffffff:
		m_scsihelp->write_wrapper(true, 0, data >> 24);
		m_scsihelp->write_wrapper(true, 0, data >> 16);
		m_scsihelp->write_wrapper(true, 0, data >> 8);
		m_scsihelp->write_wrapper(true, 0, data & 0xff);
		break;

	default:
		logerror("scsi_drq_w: unknown mem_mask %08x\n", mem_mask);
		break;
	}
}

void maciici_state::phases_w(uint8_t phases)
{
	if (m_cur_floppy)
		m_cur_floppy->seek_phase_w(phases);
}

void maciici_state::devsel_w(uint8_t devsel)
{
	if (devsel == 1)
		m_cur_floppy = m_floppy[0]->get_device();
	else if (devsel == 2)
		m_cur_floppy = m_floppy[1]->get_device();
	else
		m_cur_floppy = nullptr;

	m_fdc->set_floppy(m_cur_floppy);
	if (m_cur_floppy)
		m_cur_floppy->ss_w(m_hdsel);
}

static INPUT_PORTS_START(maciici)
INPUT_PORTS_END

static INPUT_PORTS_START(maciisi)
	PORT_START("config")
	PORT_CONFNAME(0x01, 0x00, "FPU")
	PORT_CONFSETTING(0x00, "No FPU")
	PORT_CONFSETTING(0x01, "FPU Present")
INPUT_PORTS_END

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/
void maciici_state::maciixi_base(machine_config &config)
{
	M68030(config, m_maincpu, 25000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &maciici_state::maciici_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	RTC3430042(config, m_rtc, XTAL(32'768));
	m_rtc->cko_cb().set(m_via1, FUNC(via6522_device::write_ca2));

	SWIM1(config, m_fdc, C15M);
	m_fdc->devsel_cb().set(FUNC(maciici_state::devsel_w));
	m_fdc->phases_cb().set(FUNC(maciici_state::phases_w));

	applefdintf_device::add_35_hd(config, m_floppy[0]);
	applefdintf_device::add_35_nc(config, m_floppy[1]);

	SOFTWARE_LIST(config, "flop35hd_list").set_original("mac_hdflop");

	SCC85C30(config, m_scc, C7M);
	m_scc->configure_channels(3'686'400, 3'686'400, 3'686'400, 3'686'400);
	m_scc->out_int_callback().set(FUNC(maciici_state::scc_irq_w));
	m_scc->out_txda_callback().set("printer", FUNC(rs232_port_device::write_txd));
	m_scc->out_txdb_callback().set("modem", FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232a(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_scc, FUNC(z80scc_device::rxa_w));
	rs232a.dcd_handler().set(m_scc, FUNC(z80scc_device::dcda_w));
	rs232a.cts_handler().set(m_scc, FUNC(z80scc_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "modem", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_scc, FUNC(z80scc_device::rxb_w));
	rs232b.dcd_handler().set(m_scc, FUNC(z80scc_device::dcdb_w));
	rs232b.cts_handler().set(m_scc, FUNC(z80scc_device::ctsb_w));

	SPEAKER(config, "speaker", 2).front();
	ASC(config, m_asc, C15M, asc_device::asc_type::ASC);
	m_asc->irqf_callback().set(m_rbv, FUNC(rbv_device::asc_irq_w));
	m_asc->add_route(0, "speaker", 1.0, 0);
	m_asc->add_route(1, "speaker", 1.0, 1);

	R65NC22(config, m_via1, C7M / 10);
	m_via1->readpa_handler().set(FUNC(maciici_state::via_in_a));
	m_via1->readpb_handler().set(FUNC(maciici_state::via_in_b));
	m_via1->writepa_handler().set(FUNC(maciici_state::via_out_a));
	m_via1->writepb_handler().set(FUNC(maciici_state::via_out_b));
	m_via1->cb2_handler().set(FUNC(maciici_state::via_out_cb2));
	m_via1->irq_handler().set(FUNC(maciici_state::via_irq));

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:1", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3").option_set("cdrom", NSCSI_CDROM_APPLE).machine_config(
		[](device_t *device)
		{
			device->subdevice<cdda_device>("cdda")->add_route(0, "^^speaker", 1.0, 0);
			device->subdevice<cdda_device>("cdda")->add_route(1, "^^speaker", 1.0, 1);
		});
	NSCSI_CONNECTOR(config, "scsi:4", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", mac_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr5380", NCR53C80).machine_config([this](device_t *device)
																					 {
		ncr53c80_device &adapter = downcast<ncr53c80_device &>(*device);
		adapter.drq_handler().set(m_scsihelp, FUNC(mac_scsi_helper_device::drq_w)); });

	MAC_SCSI_HELPER(config, m_scsihelp);
	m_scsihelp->scsi_read_callback().set(m_ncr5380, FUNC(ncr53c80_device::read));
	m_scsihelp->scsi_write_callback().set(m_ncr5380, FUNC(ncr53c80_device::write));
	m_scsihelp->scsi_dma_read_callback().set(m_ncr5380, FUNC(ncr53c80_device::dma_r));
	m_scsihelp->scsi_dma_write_callback().set(m_ncr5380, FUNC(ncr53c80_device::dma_w));
	m_scsihelp->cpu_halt_callback().set_inputline(m_maincpu, INPUT_LINE_HALT);
	m_scsihelp->timeout_error_callback().set(FUNC(maciici_state::scsi_berr_w));

	SOFTWARE_LIST(config, "hdd_list").set_original("mac_hdd");
	SOFTWARE_LIST(config, "cd_list").set_original("mac_cdrom").set_filter("MC68030,MC68030_32");

	RAM(config, m_ram);
	m_ram->set_default_size("2M");
	m_ram->set_extra_options("8M,32M,64M,96M,128M");

	SOFTWARE_LIST(config, "flop_mac35_orig").set_original("mac_flop_orig");
	SOFTWARE_LIST(config, "flop_mac35_clean").set_original("mac_flop_clcracked");
	SOFTWARE_LIST(config, "flop35_list").set_original("mac_flop");

	RBV(config, m_rbv, 31.3344_MHz_XTAL); // main clock input - additional 30.24MHz and 57.2832MHz pixel clock inputs
	m_rbv->via6015_callback().set(m_via1, FUNC(via6522_device::write_ca1));
	m_rbv->irq_callback().set(FUNC(maciici_state::set_via2_interrupt));

	/* internal ram */
	m_ram->set_default_size("2M");
	m_ram->set_extra_options("4M,8M,16M,32M,48M,64M,128M");

	nubus_device &nubus(NUBUS(config, "nubus", 0));
	nubus.set_space(m_maincpu, AS_PROGRAM);
	nubus.out_irq9_callback().set(m_rbv, FUNC(rbv_device::slot_irq_w<0x01>));
	nubus.out_irqa_callback().set(m_rbv, FUNC(rbv_device::slot_irq_w<0x02>));
	nubus.out_irqb_callback().set(m_rbv, FUNC(rbv_device::slot_irq_w<0x04>));
	nubus.out_irqc_callback().set(m_rbv, FUNC(rbv_device::slot_irq_w<0x08>));
	nubus.out_irqd_callback().set(m_rbv, FUNC(rbv_device::slot_irq_w<0x10>));
	nubus.out_irqe_callback().set(m_rbv, FUNC(rbv_device::slot_irq_w<0x20>));

	NUBUS_SLOT(config, "nbc", "nubus", mac_nubus_cards, nullptr);
	NUBUS_SLOT(config, "nbd", "nubus", mac_nubus_cards, nullptr);
	NUBUS_SLOT(config, "nbe", "nubus", mac_nubus_cards, nullptr);
}

void maciici_state::maciici(machine_config &config)
{
	maciixi_base(config);

	ADBMODEM(config, m_adbmodem, C7M);
	m_adbmodem->via_clock_callback().set(m_via1, FUNC(via6522_device::write_cb1));
	m_adbmodem->via_data_callback().set(m_via1, FUNC(via6522_device::write_cb2));
	m_adbmodem->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
	m_adbmodem->irq_callback().set(FUNC(maciici_state::adb_irq_w));
	m_via1->cb2_handler().set(m_adbmodem, FUNC(adbmodem_device::set_via_data));
	config.set_perfect_quantum(m_maincpu);

	MACADB(config, m_macadb, C15M);
	m_macadb->adb_data_callback().set(m_adbmodem, FUNC(adbmodem_device::set_adb_line));
}

void maciici_state::maciisi(machine_config &config)
{
	maciixi_base(config);

	M68030(config.replace(), m_maincpu, 20000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &maciici_state::maciici_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	m_via1->readpa_handler().set(FUNC(maciici_state::via_in_a_iisi));
	m_via1->readpb_handler().set(FUNC(maciici_state::via_in_b_iisi));
	m_via1->writepb_handler().set(FUNC(maciici_state::via_out_b_iisi));
	m_via1->cb2_handler().set(FUNC(maciici_state::via_out_cb2_iisi));

	MACADB(config, m_macadb, C15M);

	EGRET(config, m_egret, XTAL(32'768));
	m_egret->set_default_bios_tag("344s0100");
	m_egret->reset_callback().set(FUNC(maciici_state::egret_reset_w));
	m_egret->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
	m_egret->via_clock_callback().set(m_via1, FUNC(via6522_device::write_cb1));
	m_egret->via_data_callback().set(m_via1, FUNC(via6522_device::write_cb2));
	m_macadb->adb_data_callback().set(m_egret, FUNC(egret_device::set_adb_line));
	config.set_perfect_quantum(m_maincpu);

	config.device_remove("nbc");
	config.device_remove("nbd");
	config.device_remove("nbe");
	config.device_remove("nubus");

	// TODO: IIsi takes an adapter card that can accept either one SE/30 PDS card or one NuBus card
	nubus_device &nubus(NUBUS(config, "pds", 0));
	nubus.set_space(m_maincpu, AS_PROGRAM);
	nubus.out_irq9_callback().set(m_rbv, FUNC(rbv_device::slot_irq_w<0x01>));
	nubus.out_irqa_callback().set(m_rbv, FUNC(rbv_device::slot_irq_w<0x02>));
	nubus.out_irqb_callback().set(m_rbv, FUNC(rbv_device::slot_irq_w<0x04>));
	nubus.out_irqc_callback().set(m_rbv, FUNC(rbv_device::slot_irq_w<0x08>));
	nubus.out_irqd_callback().set(m_rbv, FUNC(rbv_device::slot_irq_w<0x10>));
	nubus.out_irqe_callback().set(m_rbv, FUNC(rbv_device::slot_irq_w<0x20>));
	NUBUS_SLOT(config, "siexp", "pds", mac_iisi_cards, nullptr);
}

ROM_START( maciici )
	ROM_REGION32_BE(0x80000, "bootrom", 0)
	ROM_LOAD32_BYTE( "341-0736.um12", 0x000000, 0x020000, CRC(7a1906e6) SHA1(3e39c80b52f40798502fcbdfc97b315545c4c4d3) )
	ROM_LOAD32_BYTE( "341-0735.um11", 0x000001, 0x020000, CRC(a8942189) SHA1(be9f653cab04c304d7ee8d4ec312c23ff5d47efc) )
	ROM_LOAD32_BYTE( "342-0734.um10", 0x000002, 0x020000, CRC(07f56402) SHA1(e11ca97181faf26cd0d05bd639d65998805c7822) )
	ROM_LOAD32_BYTE( "342-0733.um9",  0x000003, 0x020000, CRC(20c28451) SHA1(fecf849c9ac9717c18c13184e24a471888028e46) )
ROM_END

ROM_START( maciisi )
	ROM_REGION32_BE(0x80000, "bootrom", 0)
	ROM_LOAD( "36b7fb6c.rom", 0x000000, 0x080000, CRC(f304d973) SHA1(f923de4125aae810796527ff6e25364cf1d54eec) )
ROM_END

} // anonymous namespace

COMP(1989, maciici, 0, 0, maciici, maciici, maciici_state, empty_init, "Apple Computer", "Macintosh IIci", MACHINE_SUPPORTS_SAVE)
COMP(1990, maciisi, 0, 0, maciisi, maciisi, maciici_state, empty_init, "Apple Computer", "Macintosh IIsi", MACHINE_SUPPORTS_SAVE)



maciifx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/****************************************************************************

    maciifx.cpp
    Mac IIfx

    By R. Belmont

    This was the fastest 68030 Mac, with a 40 MHz clock speed and 2 65C02
    coprocessors plus DMA capability.  MacOS used almost none of the extra
    hardware so the machine never reached its full potential.  However, its
    DMA and I/O coprocessors were reused in the LaserWriter IIf/IIg and
    Quadra 900/950.

****************************************************************************/

#include "emu.h"

#include "macadb.h"
#include "macrtc.h"
#include "mactoolbox.h"
#include "scsidma.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/devices.h"
#include "bus/nubus/nubus.h"
#include "bus/nubus/cards.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68030.h"
#include "machine/6522via.h"
#include "machine/applefdintf.h"
#include "machine/applepic.h"
#include "machine/ncr5380.h"
#include "machine/nscsi_bus.h"
#include "machine/ram.h"
#include "machine/swim1.h"
#include "machine/timer.h"
#include "machine/z80scc.h"
#include "sound/asc.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"
#include "softlist_dev.h"

namespace {

#define C15M    (15.6672_MHz_XTAL)
#define C7M     (C15M/2)

class maciifx_state : public driver_device
{
public:
	maciifx_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_via1(*this, "via1"),
		m_macadb(*this, "macadb"),
		m_ram(*this, RAM_TAG),
		m_rtc(*this, "rtc"),
		m_scsidma(*this, "scsidma"),
		m_fdc(*this, "fdc"),
		m_floppy(*this, "fdc:%d", 0U),
		m_scc(*this, "scc"),
		m_asc(*this, "asc"),
		m_cur_floppy(nullptr),
		m_hdsel(0),
		m_last_taken_interrupt(-1),
		m_overlay(true),
		m_rom_ptr(nullptr),
		m_rom_size(0),
		m_adb_in(0)
	{
	}

	void maciifx(machine_config &config);
	void maciifx_map(address_map &map) ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<m68030_device> m_maincpu;
	required_device<via6522_device> m_via1;
	required_device<macadb_device> m_macadb;
	required_device<ram_device> m_ram;
	required_device<rtc3430042_device> m_rtc;
	required_device<scsidma_device> m_scsidma;
	required_device<applefdintf_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<z80scc_device> m_scc;
	required_device<asc_device> m_asc;

	floppy_image_device *m_cur_floppy = nullptr;
	int m_hdsel;

	uint8_t m_oss_regs[0x400]{};
	int m_last_taken_interrupt;
	emu_timer *m_6015_timer;

	bool m_overlay;
	u32 *m_rom_ptr;
	u32 m_rom_size;

	int m_adb_in;

	void phases_w(uint8_t phases);
	void devsel_w(uint8_t devsel);
	void fdc_hdsel(int state);

	uint32_t biu_r(offs_t offset, uint32_t mem_mask = ~0);
	void biu_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	template <int N>
	void oss_interrupt(int state);
	TIMER_CALLBACK_MEMBER(oss_6015_tick);
	uint8_t oss_r(offs_t offset);
	void oss_w(offs_t offset, uint8_t data);
	uint32_t buserror_r();

	uint16_t via_r(offs_t offset);
	void via_w(offs_t offset, uint16_t data, uint16_t mem_mask);
	uint8_t via_in_a();
	uint8_t via_in_b();
	void via_out_a(uint8_t data);
	void via_out_b(uint8_t data);
	void via_sync();

	uint32_t rom_switch_r(offs_t offset);

	void set_adb_line(int linestate) { m_adb_in = (linestate == ASSERT_LINE) ? true : false; }
	int adbin_r() { return m_adb_in; }
};

void maciifx_state::machine_start()
{
	save_item(NAME(m_hdsel));
	save_item(NAME(m_last_taken_interrupt));
	save_item(NAME(m_adb_in));

	m_6015_timer = timer_alloc(FUNC(maciifx_state::oss_6015_tick), this);
	m_6015_timer->adjust(attotime::never);

	m_rom_ptr = (u32 *)memregion("bootrom")->base();
	m_rom_size = memregion("bootrom")->bytes();

	m_last_taken_interrupt = -1;
	m_adb_in = 0;
}

void maciifx_state::machine_reset()
{
	// put ROM mirror at 0
	address_space& space = m_maincpu->space(AS_PROGRAM);
	const u32 memory_size = std::min((u32)0x3fffff, m_rom_size);
	const u32 memory_end = memory_size - 1;
	offs_t memory_mirror = memory_end & ~(memory_size - 1);

	space.unmap_write(0x00000000, memory_end);
	space.install_rom(0x00000000, memory_end & ~memory_mirror, memory_mirror, m_rom_ptr);
	m_overlay = true;

	m_6015_timer->adjust(attotime::from_hz(60.15), 0, attotime::from_hz(60.15));
}

uint32_t maciifx_state::rom_switch_r(offs_t offset)
{
	// disable the overlay
	if (m_overlay)
	{
		address_space &space = m_maincpu->space(AS_PROGRAM);
		const u32 memory_end = m_ram->size() - 1;
		void *memory_data = m_ram->pointer();
		offs_t memory_mirror = memory_end & ~memory_end;

		space.install_ram(0x00000000, memory_end & ~memory_mirror, memory_mirror, memory_data);
		m_overlay = false;
	}

	// printf("rom_switch_r: offset %08x ROM_size -1 = %08x, masked = %08x\n", offset, m_rom_size-1, offset & ((m_rom_size - 1)>>2));

	return m_rom_ptr[offset & ((m_rom_size - 1) >> 2)];
}

/***************************************************************************
    ADDRESS MAPS
***************************************************************************/
void maciifx_state::maciifx_map(address_map &map)
{
	map(0x40000000, 0x4007ffff).r(FUNC(maciifx_state::rom_switch_r)).mirror(0x0ff80000);

	map(0x50000000, 0x50001fff).rw(FUNC(maciifx_state::via_r), FUNC(maciifx_state::via_w)).mirror(0x00f00000);
	map(0x50004000, 0x50005fff).rw("sccpic", FUNC(applepic_device::host_r), FUNC(applepic_device::host_w)).mirror(0x00f00000).umask32(0xff00ff00);
	map(0x50004000, 0x50005fff).rw("sccpic", FUNC(applepic_device::host_r), FUNC(applepic_device::host_w)).mirror(0x00f00000).umask32(0x00ff00ff);
	map(0x50008000, 0x50009fff).m(m_scsidma, FUNC(scsidma_device::map)).mirror(0x0ff80000);
	map(0x50010000, 0x50011fff).rw(m_asc, FUNC(asc_device::read), FUNC(asc_device::write)).mirror(0x00f00000);
	map(0x50012000, 0x50013fff).rw("swimpic", FUNC(applepic_device::host_r), FUNC(applepic_device::host_w)).mirror(0x00f00000).umask32(0xff00ff00);
	map(0x50012000, 0x50013fff).rw("swimpic", FUNC(applepic_device::host_r), FUNC(applepic_device::host_w)).mirror(0x00f00000).umask32(0x00ff00ff);
	map(0x50018000, 0x50019fff).rw(FUNC(maciifx_state::biu_r), FUNC(maciifx_state::biu_w)).mirror(0x00f00000);
	map(0x5001a000, 0x5001bfff).rw(FUNC(maciifx_state::oss_r), FUNC(maciifx_state::oss_w)).mirror(0x00f00000);
	map(0x50024000, 0x50027fff).r(FUNC(maciifx_state::buserror_r)).mirror(0x00f00000); // must bus error on access here so ROM can determine we're an FMC
	map(0x50040000, 0x50041fff).rw(FUNC(maciifx_state::via_r), FUNC(maciifx_state::via_w)).mirror(0x00f00000);
}

void maciifx_state::via_sync()
{
	// The via runs at 783.36KHz while the main cpu runs at 15MHz or
	// more, so we need to sync the access with the via clock.  Plus
	// the whole access takes half a (via) cycle and ends when synced
	// with the main cpu again.

	// Get the main cpu time
	u64 cycle = m_maincpu->total_cycles();

	// Get the number of the cycle the via is in at that time
	u64 via_cycle = cycle * m_via1->clock() / m_maincpu->clock();

	// The access is going to start at via_cycle+1 and end at
	// via_cycle+1.5, compute what that means in maincpu cycles (the
	// +1 rounds up, since the clocks are too different to ever be
	// synced).
	u64 main_cycle = (via_cycle * 2 + 3) * m_maincpu->clock() / (2 * m_via1->clock()) + 1;

	// Finally adjust the main cpu icount as needed.
	m_maincpu->adjust_icount(-int(main_cycle - cycle));
}

uint16_t maciifx_state::via_r(offs_t offset)
{
	uint16_t data;

	offset >>= 8;
	offset &= 0x0f;

	if (!machine().side_effects_disabled())
		via_sync();

	data = m_via1->read(offset);

	return (data & 0xff) | (data << 8);
}

void maciifx_state::via_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	offset >>= 8;
	offset &= 0x0f;

	via_sync();

	if (ACCESSING_BITS_0_7)
		m_via1->write(offset, data & 0xff);
	if (ACCESSING_BITS_8_15)
		m_via1->write(offset, (data >> 8) & 0xff);
}

uint8_t maciifx_state::via_in_a()
{
	return 0xd3;    // PA6 | PA4 | PA1
}

uint8_t maciifx_state::via_in_b()
{
	return  m_rtc->data_r();
}

void maciifx_state::via_out_a(uint8_t data)
{
}

void maciifx_state::via_out_b(uint8_t data)
{
	m_rtc->ce_w((data & 0x04) >> 2);
	m_rtc->data_w(data & 0x01);
	m_rtc->clk_w((data >> 1) & 0x01);
}

void maciifx_state::phases_w(uint8_t phases)
{
	if (m_cur_floppy)
		m_cur_floppy->seek_phase_w(phases);
}

void maciifx_state::devsel_w(uint8_t devsel)
{
	if (devsel == 1)
	{
		m_cur_floppy = m_floppy[0]->get_device();
	}
	else if (devsel == 2)
	{
		m_cur_floppy = m_floppy[1]->get_device();
	}
	else
	{
		m_cur_floppy = nullptr;
	}

	m_fdc->set_floppy(m_cur_floppy);
	if (m_cur_floppy)
	{
		m_cur_floppy->ss_w(m_hdsel);
	}
}

void maciifx_state::fdc_hdsel(int state)
{
	if (state != m_hdsel)
	{
		if (m_cur_floppy)
		{
			m_cur_floppy->ss_w(state);
		}
	}
	m_hdsel = state;
}

uint32_t maciifx_state::biu_r(offs_t offset, uint32_t mem_mask)
{
	return 0;
}

void maciifx_state::biu_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
}

template <int N>
void maciifx_state::oss_interrupt(int state)
{
	if (state == ASSERT_LINE)
	{
		m_oss_regs[N >= 8 ? 0x202 : 0x203] |= 1 << (N & 7);
	}
	else
	{
		m_oss_regs[N >= 8 ? 0x202 : 0x203] &= ~(1 << (N & 7));
	}

	int take_interrupt = 0;
	for (int n = 0; n < 8; n++)
	{
		if (BIT(m_oss_regs[0x203], n) && take_interrupt < m_oss_regs[n])
		{
			take_interrupt = m_oss_regs[n];
		}
		if (BIT(m_oss_regs[0x202], n) && take_interrupt < m_oss_regs[8 + n])
		{
			take_interrupt = m_oss_regs[8 + n];
		}
	}

	if (m_last_taken_interrupt > -1)
	{
		m_maincpu->set_input_line(m_last_taken_interrupt, CLEAR_LINE);
		m_last_taken_interrupt = -1;
		m_oss_regs[0x200] &= 0x7f;
	}

	if (take_interrupt > 0)
	{
		m_maincpu->set_input_line(take_interrupt, ASSERT_LINE);
		m_last_taken_interrupt = take_interrupt;
		m_oss_regs[0x200] |= 0x80;
	}
}

TIMER_CALLBACK_MEMBER(maciifx_state::oss_6015_tick)
{
	m_via1->write_ca1(0);
	m_via1->write_ca1(1);
	oss_interrupt<10>(ASSERT_LINE);
}

uint8_t maciifx_state::oss_r(offs_t offset)
{
	if (offset < std::size(m_oss_regs))
	{
		return m_oss_regs[offset];
	}
	else
	{
		return 0;
	}
}

void maciifx_state::oss_w(offs_t offset, uint8_t data)
{
	if (offset == 0x207)
	{
		oss_interrupt<10>(CLEAR_LINE);
	}
	else if (offset < std::size(m_oss_regs))
	{
		m_oss_regs[offset] = data;
	}
}

uint32_t maciifx_state::buserror_r()
{
	m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
	m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
	return 0;
}

/***************************************************************************
    DEVICE CONFIG
***************************************************************************/

static INPUT_PORTS_START( maciifx )
INPUT_PORTS_END

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/
void maciifx_state::maciifx(machine_config &config)
{
	/* basic machine hardware */
	M68030(config, m_maincpu, 40000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &maciifx_state::maciifx_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	RTC3430042(config, m_rtc, XTAL(32'768));
	m_rtc->cko_cb().set(m_via1, FUNC(via6522_device::write_ca2));

	SWIM1(config, m_fdc, C15M);
	m_fdc->devsel_cb().set(FUNC(maciifx_state::devsel_w));
	m_fdc->phases_cb().set(FUNC(maciifx_state::phases_w));
	m_fdc->hdsel_cb().set(FUNC(maciifx_state::fdc_hdsel));

	applefdintf_device::add_35_hd(config, m_floppy[0]);
	applefdintf_device::add_35_nc(config, m_floppy[1]);

	SCC85C30(config, m_scc, C7M);
	m_scc->configure_channels(3'686'400, 3'686'400, 3'686'400, 3'686'400);
	m_scc->out_txda_callback().set("printer", FUNC(rs232_port_device::write_txd));
	m_scc->out_txdb_callback().set("modem", FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232a(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_scc, FUNC(z80scc_device::rxa_w));
	rs232a.dcd_handler().set(m_scc, FUNC(z80scc_device::dcda_w));
	rs232a.cts_handler().set(m_scc, FUNC(z80scc_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "modem", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_scc, FUNC(z80scc_device::rxb_w));
	rs232b.dcd_handler().set(m_scc, FUNC(z80scc_device::dcdb_w));
	rs232b.cts_handler().set(m_scc, FUNC(z80scc_device::ctsb_w));

	SCSIDMA(config, m_scsidma, C15M);
	m_scsidma->set_maincpu_tag("maincpu");
	m_scsidma->write_irq().set(FUNC(maciifx_state::oss_interrupt<9>));

	SPEAKER(config, "speaker", 2).front();
	ASC(config, m_asc, C15M, asc_device::asc_type::ASC);
	m_asc->add_route(0, "speaker", 1.0, 0);
	m_asc->add_route(1, "speaker", 1.0, 1);
	m_asc->irqf_callback().set(FUNC(maciifx_state::oss_interrupt<8>));

	R65NC22(config, m_via1, C7M / 10);
	m_via1->readpa_handler().set(FUNC(maciifx_state::via_in_a));
	m_via1->readpb_handler().set(FUNC(maciifx_state::via_in_b));
	m_via1->writepa_handler().set(FUNC(maciifx_state::via_out_a));
	m_via1->writepb_handler().set(FUNC(maciifx_state::via_out_b));
	m_via1->irq_handler().set(FUNC(maciifx_state::oss_interrupt<11>));

	MACADB(config, m_macadb, C15M);
	m_macadb->adb_data_callback().set(FUNC(maciifx_state::set_adb_line));

	applepic_device &sccpic(APPLEPIC(config, "sccpic", C15M));
	sccpic.prd_callback().set(m_scc, FUNC(z80scc_device::dc_ab_r));
	sccpic.pwr_callback().set(m_scc, FUNC(z80scc_device::dc_ab_w));
	sccpic.hint_callback().set(FUNC(maciifx_state::oss_interrupt<7>));

	m_scc->out_int_callback().set("sccpic", FUNC(applepic_device::pint_w));
	m_scc->out_wreqa_callback().set("sccpic", FUNC(applepic_device::reqa_w)).invert();
	m_scc->out_wreqb_callback().set("sccpic", FUNC(applepic_device::reqb_w)).invert();

	applepic_device &swimpic(APPLEPIC(config, "swimpic", C15M));
	swimpic.prd_callback().set(m_fdc, FUNC(applefdintf_device::read));
	swimpic.pwr_callback().set(m_fdc, FUNC(applefdintf_device::write));
	swimpic.hint_callback().set(FUNC(maciifx_state::oss_interrupt<6>));
	swimpic.gpout0_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w)).invert();
	swimpic.gpin_callback().set(FUNC(maciifx_state::adbin_r));

	m_fdc->dat1byte_cb().set("swimpic", FUNC(applepic_device::reqa_w));

	RAM(config, m_ram);
	m_ram->set_default_size("4M");
	m_ram->set_extra_options("8M,16M,32M,64M,96M,128M");

	SOFTWARE_LIST(config, "hdd_list").set_original("mac_hdd");
	SOFTWARE_LIST(config, "cd_list").set_original("mac_cdrom").set_filter("MC68030,MC68030_32");
	SOFTWARE_LIST(config, "flop35hd_list").set_original("mac_hdflop");
	SOFTWARE_LIST(config, "flop_mac35_orig").set_original("mac_flop_orig");
	SOFTWARE_LIST(config, "flop_mac35_clean").set_original("mac_flop_clcracked");
	SOFTWARE_LIST(config, "flop35_list").set_original("mac_flop");

	nubus_device &nubus(NUBUS(config, "nubus", 0));
	nubus.set_space(m_maincpu, AS_PROGRAM);
	nubus.out_irq9_callback().set(FUNC(maciifx_state::oss_interrupt<0>));
	nubus.out_irqa_callback().set(FUNC(maciifx_state::oss_interrupt<1>));
	nubus.out_irqb_callback().set(FUNC(maciifx_state::oss_interrupt<2>));
	nubus.out_irqc_callback().set(FUNC(maciifx_state::oss_interrupt<3>));
	nubus.out_irqd_callback().set(FUNC(maciifx_state::oss_interrupt<4>));
	nubus.out_irqe_callback().set(FUNC(maciifx_state::oss_interrupt<5>));

	NUBUS_SLOT(config, "nb9", "nubus", mac_nubus_cards, "mdc824");
	NUBUS_SLOT(config, "nba", "nubus", mac_nubus_cards, nullptr);
	NUBUS_SLOT(config, "nbb", "nubus", mac_nubus_cards, nullptr);
	NUBUS_SLOT(config, "nbc", "nubus", mac_nubus_cards, nullptr);
	NUBUS_SLOT(config, "nbd", "nubus", mac_nubus_cards, nullptr);
	NUBUS_SLOT(config, "nbe", "nubus", mac_nubus_cards, nullptr);
}

ROM_START(maciifx)
	ROM_REGION32_BE(0x80000, "bootrom", 0)
	ROM_LOAD("4147dd77.rom", 0x000000, 0x080000, CRC(ef441bbd) SHA1(9fba3d4f672a630745d65788b1d1119afa2c6728))
ROM_END

}   // anonymous namespace

COMP(1990, maciifx, 0, 0, maciifx, maciifx, maciifx_state, empty_init, "Apple Computer", "Macintosh IIfx", MACHINE_SUPPORTS_SAVE)



maciivx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/****************************************************************************

    maciivx.cpp
    Mac IIvx
    Mac IIvi

    By R. Belmont

    These 68030 machines were the last Mac IIs, and had development rushed
    after then-CEO John Sculley told MacWorld Tokyo that Apple would soon
    ship machines with a built-in CD-ROM drive.

    They run on the "VASP" system ASIC, which is basically V8 with slightly
    different video and the RAM size limit lifted to 68 MB.

****************************************************************************/

#include "emu.h"

#include "dfac.h"
#include "egret.h"
#include "macadb.h"
#include "macscsi.h"
#include "mactoolbox.h"
#include "vasp.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/devices.h"
#include "bus/nubus/nubus.h"
#include "bus/nubus/cards.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68030.h"
#include "machine/applefdintf.h"
#include "machine/ncr5380.h"
#include "machine/nscsi_bus.h"
#include "machine/ram.h"
#include "machine/swim1.h"
#include "machine/timer.h"
#include "machine/z80scc.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

namespace {

#define C32M    (31.3344_MHz_XTAL)
#define C15M    (C32M/2)
#define C7M     (C32M/4)

class maciivx_state : public driver_device
{
public:
	maciivx_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_macadb(*this, "macadb"),
		m_ram(*this, RAM_TAG),
		m_vasp(*this, "vasp"),
		m_dfac(*this, "dfac"),
		m_fdc(*this, "fdc"),
		m_floppy(*this, "fdc:%d", 0U),
		m_scsibus1(*this, "scsi"),
		m_ncr5380(*this, "scsi:7:ncr5380"),
		m_scsihelp(*this, "scsihelp"),
		m_scc(*this, "scc"),
		m_egret(*this, "egret"),
		m_config(*this, "config"),
		m_cur_floppy(nullptr),
		m_hdsel(0)
	{
	}

	void maciiv_base(machine_config &config);
	void maciivx(machine_config &config);
	void maciivi(machine_config &config);
	void base_map(address_map &map) ATTR_COLD;
	void maciivx_map(address_map &map) ATTR_COLD;
	void maciivi_map(address_map &map) ATTR_COLD;

private:
	required_device<m68030_device> m_maincpu;
	required_device<macadb_device> m_macadb;
	required_device<ram_device> m_ram;
	required_device<vasp_device> m_vasp;
	required_device<dfac_device> m_dfac;
	required_device<applefdintf_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<nscsi_bus_device> m_scsibus1;
	required_device<ncr5380_device> m_ncr5380;
	required_device<mac_scsi_helper_device> m_scsihelp;
	required_device<z80scc_device> m_scc;
	required_device<egret_device> m_egret;
	optional_ioport m_config;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	u16 scc_r(offs_t offset)
	{
		u16 result = m_scc->dc_ab_r(offset);
		return (result << 8) | result;
	}
	void scc_w(offs_t offset, u16 data)
	{
		m_scc->dc_ab_w(offset, data >> 8);
	}

	u16 scsi_r(offs_t offset, u16 mem_mask = ~0);
	void scsi_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	u32 scsi_drq_r(offs_t offset, u32 mem_mask = ~0);
	void scsi_drq_w(offs_t offset, u32 data, u32 mem_mask = ~0);

	void scsi_berr_w(u8 data)
	{
		m_maincpu->pulse_input_line(M68K_LINE_BUSERROR, attotime::zero);
	}

	void egret_reset_w(int state)
	{
		m_maincpu->set_input_line(INPUT_LINE_HALT, state);
		m_maincpu->set_input_line(INPUT_LINE_RESET, state);
	}

	floppy_image_device *m_cur_floppy = nullptr;
	int m_hdsel;

	void phases_w(uint8_t phases);
	void devsel_w(uint8_t devsel);
	uint16_t swim_r(offs_t offset, u16 mem_mask);
	void swim_w(offs_t offset, u16 data, u16 mem_mask);
	void hdsel_w(int state);
};

void maciivx_state::machine_start()
{
	m_vasp->set_ram_info((u32 *) m_ram->pointer(), m_ram->size());

	save_item(NAME(m_hdsel));
}

void maciivx_state::machine_reset()
{
	if (m_config)
	{
		m_maincpu->set_fpu_enable(BIT(m_config->read(), 0));
	}
}

/***************************************************************************
    ADDRESS MAPS
***************************************************************************/
void maciivx_state::base_map(address_map &map)
{
	// RAM, ROM, and base I/O mappings come from VASP
	map(0x40000000, 0x600fffff).m(m_vasp, FUNC(vasp_device::map));

	map(0x50004000, 0x50005fff).rw(FUNC(maciivx_state::scc_r), FUNC(maciivx_state::scc_w)).mirror(0x00f00000);
	map(0x50006000, 0x50007fff).rw(FUNC(maciivx_state::scsi_drq_r), FUNC(maciivx_state::scsi_drq_w)).mirror(0x00f00000);
	map(0x50010000, 0x50011fff).rw(FUNC(maciivx_state::scsi_r), FUNC(maciivx_state::scsi_w)).mirror(0x00f00000);
	map(0x50012000, 0x50013fff).rw(FUNC(maciivx_state::scsi_drq_r), FUNC(maciivx_state::scsi_drq_w)).mirror(0x00f00000);
	map(0x50016000, 0x50017fff).rw(FUNC(maciivx_state::swim_r), FUNC(maciivx_state::swim_w)).mirror(0x00f00000);
}

void maciivx_state::maciivx_map(address_map &map)
{
	base_map(map);
	map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a2015; }));
}

void maciivx_state::maciivi_map(address_map &map)
{
	base_map(map);
	map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a2016; }));
}

u16 maciivx_state::scsi_r(offs_t offset, u16 mem_mask)
{
	const int reg = (offset >> 3) & 0xf;
	const bool pseudo_dma = (reg == 6) && (offset == 0x130);

	return m_scsihelp->read_wrapper(pseudo_dma, reg) << 8;
}

void maciivx_state::scsi_w(offs_t offset, u16 data, u16 mem_mask)
{
	const int reg = (offset >> 3) & 0xf;
	const bool pseudo_dma = (reg == 0) && (offset == 0x100);

	m_scsihelp->write_wrapper(pseudo_dma, reg, data >> 8);
}

u32 maciivx_state::scsi_drq_r(offs_t offset, u32 mem_mask)
{
	switch (mem_mask)
	{
	case 0xff000000:
		return m_scsihelp->read_wrapper(true, 6) << 24;

	case 0xffff0000:
		return (m_scsihelp->read_wrapper(true, 6) << 24) | (m_scsihelp->read_wrapper(true, 6) << 16);

	case 0xffffffff:
		return (m_scsihelp->read_wrapper(true, 6) << 24) | (m_scsihelp->read_wrapper(true, 6) << 16) | (m_scsihelp->read_wrapper(true, 6) << 8) | m_scsihelp->read_wrapper(true, 6);

	default:
		logerror("scsi_drq_r: unknown mem_mask %08x\n", mem_mask);
	}

	return 0;
}

void maciivx_state::scsi_drq_w(offs_t offset, u32 data, u32 mem_mask)
{
	switch (mem_mask)
	{
	case 0xff000000:
		m_scsihelp->write_wrapper(true, 0, data >> 24);
		break;

	case 0xffff0000:
		m_scsihelp->write_wrapper(true, 0, data >> 24);
		m_scsihelp->write_wrapper(true, 0, data >> 16);
		break;

	case 0xffffffff:
		m_scsihelp->write_wrapper(true, 0, data >> 24);
		m_scsihelp->write_wrapper(true, 0, data >> 16);
		m_scsihelp->write_wrapper(true, 0, data >> 8);
		m_scsihelp->write_wrapper(true, 0, data & 0xff);
		break;

	default:
		logerror("scsi_drq_w: unknown mem_mask %08x\n", mem_mask);
		break;
	}
}

uint16_t maciivx_state::swim_r(offs_t offset, u16 mem_mask)
{
	if (!machine().side_effects_disabled())
	{
		m_maincpu->adjust_icount(-5);
	}

	u16 result = m_fdc->read((offset >> 8) & 0xf);
	return result << 8;
}
void maciivx_state::swim_w(offs_t offset, u16 data, u16 mem_mask)
{
	if (ACCESSING_BITS_0_7)
		m_fdc->write((offset >> 8) & 0xf, data & 0xff);
	else
		m_fdc->write((offset >> 8) & 0xf, data >> 8);
}

void maciivx_state::phases_w(uint8_t phases)
{
	if (m_cur_floppy)
		m_cur_floppy->seek_phase_w(phases);
}

void maciivx_state::devsel_w(uint8_t devsel)
{
	if (devsel == 1)
		m_cur_floppy = m_floppy[0]->get_device();
	else if (devsel == 2)
		m_cur_floppy = m_floppy[1]->get_device();
	else
		m_cur_floppy = nullptr;

	m_fdc->set_floppy(m_cur_floppy);
	if (m_cur_floppy)
		m_cur_floppy->ss_w(m_hdsel);
}

void maciivx_state::hdsel_w(int state)
{
	if (state != m_hdsel)
	{
		if (m_cur_floppy)
		{
			m_cur_floppy->ss_w(state);
		}
	}
	m_hdsel = state;
}

/***************************************************************************
    DEVICE CONFIG
***************************************************************************/

static INPUT_PORTS_START( maciivx )
INPUT_PORTS_END

static INPUT_PORTS_START( maciivi )
	PORT_START("config")
	PORT_CONFNAME(0x01, 0x00, "FPU")
	PORT_CONFSETTING(0x00, "No FPU")
	PORT_CONFSETTING(0x01, "FPU Present")
INPUT_PORTS_END

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

void maciivx_state::maciiv_base(machine_config &config)
{
	RAM(config, m_ram);
	m_ram->set_default_size("4M");
	m_ram->set_extra_options("8M,16M,32M,36M,48M,64M,68M");

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:1", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3").option_set("cdrom", NSCSI_CDROM_APPLE).machine_config(
		[](device_t *device)
		{
			device->subdevice<cdda_device>("cdda")->add_route(0, "^^speaker", 1.0, 0);
			device->subdevice<cdda_device>("cdda")->add_route(1, "^^speaker", 1.0, 1);
		});
	NSCSI_CONNECTOR(config, "scsi:4", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", mac_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr5380", NCR53C80).machine_config([this](device_t *device)
	{
		ncr53c80_device &adapter = downcast<ncr53c80_device &>(*device);
		adapter.drq_handler().set(m_scsihelp, FUNC(mac_scsi_helper_device::drq_w));
	});

	MAC_SCSI_HELPER(config, m_scsihelp);
	m_scsihelp->scsi_read_callback().set(m_ncr5380, FUNC(ncr53c80_device::read));
	m_scsihelp->scsi_write_callback().set(m_ncr5380, FUNC(ncr53c80_device::write));
	m_scsihelp->scsi_dma_read_callback().set(m_ncr5380, FUNC(ncr53c80_device::dma_r));
	m_scsihelp->scsi_dma_write_callback().set(m_ncr5380, FUNC(ncr53c80_device::dma_w));
	m_scsihelp->cpu_halt_callback().set_inputline(m_maincpu, INPUT_LINE_HALT);
	m_scsihelp->timeout_error_callback().set(FUNC(maciivx_state::scsi_berr_w));

	SOFTWARE_LIST(config, "hdd_list").set_original("mac_hdd");
	SOFTWARE_LIST(config, "cd_list").set_original("mac_cdrom").set_filter("MC68030,MC68030_32");
	SOFTWARE_LIST(config, "flop35hd_list").set_original("mac_hdflop");

	SCC85C30(config, m_scc, C7M);
	m_scc->configure_channels(3'686'400, 3'686'400, 3'686'400, 3'686'400);
	m_scc->out_int_callback().set(m_vasp, FUNC(vasp_device::scc_irq_w));
	m_scc->out_txda_callback().set("printer", FUNC(rs232_port_device::write_txd));
	m_scc->out_txdb_callback().set("modem", FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232a(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_scc, FUNC(z80scc_device::rxa_w));
	rs232a.dcd_handler().set(m_scc, FUNC(z80scc_device::dcda_w));
	rs232a.cts_handler().set(m_scc, FUNC(z80scc_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "modem", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_scc, FUNC(z80scc_device::rxb_w));
	rs232b.dcd_handler().set(m_scc, FUNC(z80scc_device::dcdb_w));
	rs232b.cts_handler().set(m_scc, FUNC(z80scc_device::ctsb_w));

	SPEAKER(config, "speaker", 2).front();

	APPLE_DFAC(config, m_dfac, 22257);
	m_dfac->add_route(0, "speaker", 1.0, 0);
	m_dfac->add_route(1, "speaker", 1.0, 1);

	VASP(config, m_vasp, C15M);
	m_vasp->set_maincpu_tag("maincpu");
	m_vasp->set_rom_tag("bootrom");
	m_vasp->hdsel_callback().set(FUNC(maciivx_state::hdsel_w));
	m_vasp->add_route(0, m_dfac, 1.0, 0);
	m_vasp->add_route(1, m_dfac, 1.0, 1);

	MACADB(config, m_macadb, C15M);

	nubus_device &nubus(NUBUS(config, "nubus", 0));
	nubus.set_space(m_maincpu, AS_PROGRAM);
	nubus.out_irqc_callback().set(m_vasp, FUNC(vasp_device::slot0_irq_w));
	nubus.out_irqd_callback().set(m_vasp, FUNC(vasp_device::slot1_irq_w));
	nubus.out_irqe_callback().set(m_vasp, FUNC(vasp_device::slot2_irq_w));

	NUBUS_SLOT(config, "nbc", "nubus", mac_nubus_cards, nullptr);
	NUBUS_SLOT(config, "nbd", "nubus", mac_nubus_cards, nullptr);
	NUBUS_SLOT(config, "nbe", "nubus", mac_nubus_cards, nullptr);

	SWIM1(config, m_fdc, C15M);
	m_fdc->devsel_cb().set(FUNC(maciivx_state::devsel_w));
	m_fdc->phases_cb().set(FUNC(maciivx_state::phases_w));

	applefdintf_device::add_35_hd(config, m_floppy[0]);
	applefdintf_device::add_35_nc(config, m_floppy[1]);
}

void maciivx_state::maciivx(machine_config &config)
{
	M68030(config, m_maincpu, C32M);
	m_maincpu->set_addrmap(AS_PROGRAM, &maciivx_state::maciivx_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	maciiv_base(config);

	EGRET(config, m_egret, XTAL(32'768));
	m_egret->set_default_bios_tag("341s0851");
	m_egret->reset_callback().set(FUNC(maciivx_state::egret_reset_w));
	m_egret->dfac_scl_callback().set(m_dfac, FUNC(dfac_device::clock_write));
	m_egret->dfac_sda_callback().set(m_dfac, FUNC(dfac_device::data_write));
	m_egret->dfac_latch_callback().set(m_dfac, FUNC(dfac_device::latch_write));
	m_egret->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
	m_egret->via_clock_callback().set(m_vasp, FUNC(vasp_device::cb1_w));
	m_egret->via_data_callback().set(m_vasp, FUNC(vasp_device::cb2_w));
	m_macadb->adb_data_callback().set(m_egret, FUNC(egret_device::set_adb_line));
	config.set_perfect_quantum(m_maincpu);

	m_vasp->pb3_callback().set(m_egret, FUNC(egret_device::get_xcvr_session));
	m_vasp->pb4_callback().set(m_egret, FUNC(egret_device::set_via_full));
	m_vasp->pb5_callback().set(m_egret, FUNC(egret_device::set_sys_session));
	m_vasp->cb2_callback().set(m_egret, FUNC(egret_device::set_via_data));
}

void maciivx_state::maciivi(machine_config &config)
{
	maciivx(config);

	M68030(config.replace(), m_maincpu, C15M);
	m_maincpu->set_addrmap(AS_PROGRAM, &maciivx_state::maciivi_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");
}

ROM_START(maciivx)
	ROM_REGION32_BE(0x100000, "bootrom", 0)
	ROM_LOAD("4957eb49.rom", 0x000000, 0x100000, CRC(61be06e5) SHA1(560ce203d65178657ad09d03f532f86fa512bb40))
ROM_END

ROM_START(maciivi)
	ROM_REGION32_BE(0x100000, "bootrom", 0)
	ROM_LOAD("4957eb49.rom", 0x000000, 0x100000, CRC(61be06e5) SHA1(560ce203d65178657ad09d03f532f86fa512bb40))
ROM_END

}   // anonymous namespace

COMP(1993, maciivx, 0,       0, maciivx, maciivx, maciivx_state, empty_init, "Apple Computer", "Macintosh IIvx", MACHINE_SUPPORTS_SAVE)
COMP(1993, maciivi, maciivx, 0, maciivi, maciivi, maciivx_state, empty_init, "Apple Computer", "Macintosh IIvi", MACHINE_SUPPORTS_SAVE)



maclc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/****************************************************************************

    maclc.cpp
    Mac LC, LC II, Classic II, Color Classic, Macintosh TV
    By R. Belmont

    These are all lower-end machines based on versions of the "V8" system
    controller, which has a 10 MB hard limit on RAM (8MB in the Mac TV).

    Mac TV video input chips:
    TEA6330T - Sound fader control unit for car stereos
        I2C: address 1000000x
    TDA8708BT - Video analog input interface
    SAA7197 T - Clock signal generator circuit for desktop video systems
    SAA7191 WP - Digital multistandard colour decoder
        I2C: address 1000101x
    SAA7186 H - Digital video scaler
        I2C: address 1011100x

****************************************************************************/

#include "emu.h"

#include "cuda.h"
#include "dfac.h"
#include "dfac2.h"
#include "egret.h"
#include "macadb.h"
#include "macscsi.h"
#include "mactoolbox.h"
#include "v8.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/devices.h"
#include "bus/nubus/cards.h"
#include "bus/nubus/nubus.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68020.h"
#include "cpu/m68000/m68030.h"
#include "machine/applefdintf.h"
#include "machine/ncr5380.h"
#include "machine/nscsi_bus.h"
#include "machine/ram.h"
#include "machine/swim1.h"
#include "machine/timer.h"
#include "machine/z80scc.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
namespace {

#define C32M    (31.3344_MHz_XTAL)
#define C15M    (C32M/2)
#define C7M     (C32M/4)

class maclc_state : public driver_device
{
public:
	maclc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_macadb(*this, "macadb"),
		m_ram(*this, RAM_TAG),
		m_v8(*this, "v8"),
		m_dfac(*this, "dfac"),
		m_dfac2(*this, "dfac2"),
		m_fdc(*this, "fdc"),
		m_floppy(*this, "fdc:%d", 0U),
		m_scsibus1(*this, "scsi"),
		m_ncr5380(*this, "scsi:7:ncr5380"),
		m_scsihelp(*this, "scsihelp"),
		m_scc(*this, "scc"),
		m_egret(*this, "egret"),
		m_cuda(*this, "cuda"),
		m_config(*this, "config"),
		m_cur_floppy(nullptr),
		m_hdsel(0)
	{
	}

	void maclc_base(machine_config &config);
	void maclc(machine_config &config);
	void maclc2(machine_config &config);
	void macclas2(machine_config &config);
	void maccclas(machine_config &config);
	void mactv(machine_config &config);
	void maclc_map(address_map &map) ATTR_COLD;
	void maccclassic_map(address_map &map) ATTR_COLD;

private:
	required_device<m68000_musashi_device> m_maincpu;
	required_device<macadb_device> m_macadb;
	required_device<ram_device> m_ram;
	required_device<v8_device> m_v8;
	optional_device<dfac_device> m_dfac;
	optional_device<dfac2_device> m_dfac2;
	optional_device<applefdintf_device> m_fdc;
	optional_device_array<floppy_connector, 2> m_floppy;
	required_device<nscsi_bus_device> m_scsibus1;
	required_device<ncr5380_device> m_ncr5380;
	required_device<mac_scsi_helper_device> m_scsihelp;
	required_device<z80scc_device> m_scc;
	optional_device<egret_device> m_egret;
	optional_device<cuda_device> m_cuda;
	optional_ioport m_config;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	u16 scc_r(offs_t offset)
	{
		u16 result = m_scc->dc_ab_r(offset);
		return (result << 8) | result;
	}
	void scc_w(offs_t offset, u16 data)
	{
		m_scc->dc_ab_w(offset, data >> 8);
	}

	u16 scsi_r(offs_t offset, u16 mem_mask = ~0);
	void scsi_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	u32 scsi_drq_r(offs_t offset, u32 mem_mask = ~0);
	void scsi_drq_w(offs_t offset, u32 data, u32 mem_mask = ~0);

	void scsi_berr_w(u8 data)
	{
		m_maincpu->pulse_input_line(M68K_LINE_BUSERROR, attotime::zero);
	}

	void cuda_reset_w(int state)
	{
		m_maincpu->set_input_line(INPUT_LINE_HALT, state);
		m_maincpu->set_input_line(INPUT_LINE_RESET, state);
	}

	floppy_image_device *m_cur_floppy = nullptr;
	int m_hdsel;

	void phases_w(uint8_t phases);
	void devsel_w(uint8_t devsel);
	uint16_t swim_r(offs_t offset, u16 mem_mask);
	void swim_w(offs_t offset, u16 data, u16 mem_mask);
	void hdsel_w(int state);

	void egret_reset_w(int state)
	{
		m_maincpu->set_input_line(INPUT_LINE_HALT, state);
		m_maincpu->set_input_line(INPUT_LINE_RESET, state);
	}

	void set_hmmu(int state)
	{
		m_maincpu->set_hmmu_enable((state == ASSERT_LINE) ? M68K_HMMU_DISABLE : M68K_HMMU_ENABLE_LC);
	}
};

void maclc_state::machine_start()
{
	m_v8->set_ram_info((u32 *) m_ram->pointer(), m_ram->size());

	save_item(NAME(m_hdsel));
}

void maclc_state::machine_reset()
{
	if (m_config)
	{
		m_maincpu->set_fpu_enable(BIT(m_config->read(), 0));
	}
}

/***************************************************************************
    ADDRESS MAPS
***************************************************************************/
void maclc_state::maclc_map(address_map &map)
{
	map.global_mask(0x80ffffff); // V8 uses bit 31 and 23-0 for address decoding only

	// RAM, ROM, and base I/O mappings come from V8
	map(0xa00000, 0xffffff).m(m_v8, FUNC(v8_device::map));

	map(0xf04000, 0xf05fff).rw(FUNC(maclc_state::scc_r), FUNC(maclc_state::scc_w));
	map(0xf06000, 0xf07fff).rw(FUNC(maclc_state::scsi_drq_r), FUNC(maclc_state::scsi_drq_w));
	map(0xf10000, 0xf11fff).rw(FUNC(maclc_state::scsi_r), FUNC(maclc_state::scsi_w));
	map(0xf12000, 0xf13fff).rw(FUNC(maclc_state::scsi_drq_r), FUNC(maclc_state::scsi_drq_w));
	map(0xf16000, 0xf17fff).rw(FUNC(maclc_state::swim_r), FUNC(maclc_state::swim_w));
}

void maclc_state::maccclassic_map(address_map &map)
{
	map.global_mask(0x80ffffff); // V8 uses bit 31 and 23-0 for address decoding only

	// RAM, ROM, and base I/O mappings come from V8
	map(0xa00000, 0xffffff).m(m_v8, FUNC(v8_device::map));

	map(0xf04000, 0xf05fff).rw(FUNC(maclc_state::scc_r), FUNC(maclc_state::scc_w));
	map(0xf06000, 0xf07fff).rw(FUNC(maclc_state::scsi_drq_r), FUNC(maclc_state::scsi_drq_w));
	map(0xf10000, 0xf11fff).rw(FUNC(maclc_state::scsi_r), FUNC(maclc_state::scsi_w));
	map(0xf12000, 0xf13fff).rw(FUNC(maclc_state::scsi_drq_r), FUNC(maclc_state::scsi_drq_w));
}

u16 maclc_state::scsi_r(offs_t offset, u16 mem_mask)
{
	const int reg = (offset >> 3) & 0xf;
	const bool pseudo_dma = (reg == 6) && (offset == 0x130);

	return m_scsihelp->read_wrapper(pseudo_dma, reg) << 8;
}

void maclc_state::scsi_w(offs_t offset, u16 data, u16 mem_mask)
{
	const int reg = (offset >> 3) & 0xf;
	const bool pseudo_dma = (reg == 0) && (offset == 0x100);

	m_scsihelp->write_wrapper(pseudo_dma, reg, data >> 8);
}

u32 maclc_state::scsi_drq_r(offs_t offset, u32 mem_mask)
{
	switch (mem_mask)
	{
	case 0xff000000:
		return m_scsihelp->read_wrapper(true, 6) << 24;

	case 0xffff0000:
		return (m_scsihelp->read_wrapper(true, 6) << 24) | (m_scsihelp->read_wrapper(true, 6) << 16);

	case 0xffffffff:
		return (m_scsihelp->read_wrapper(true, 6) << 24) | (m_scsihelp->read_wrapper(true, 6) << 16) | (m_scsihelp->read_wrapper(true, 6) << 8) | m_scsihelp->read_wrapper(true, 6);

	default:
		logerror("scsi_drq_r: unknown mem_mask %08x\n", mem_mask);
	}

	return 0;
}

void maclc_state::scsi_drq_w(offs_t offset, u32 data, u32 mem_mask)
{
	switch (mem_mask)
	{
	case 0xff000000:
		m_scsihelp->write_wrapper(true, 0, data >> 24);
		break;

	case 0xffff0000:
		m_scsihelp->write_wrapper(true, 0, data >> 24);
		m_scsihelp->write_wrapper(true, 0, data >> 16);
		break;

	case 0xffffffff:
		m_scsihelp->write_wrapper(true, 0, data >> 24);
		m_scsihelp->write_wrapper(true, 0, data >> 16);
		m_scsihelp->write_wrapper(true, 0, data >> 8);
		m_scsihelp->write_wrapper(true, 0, data & 0xff);
		break;

	default:
		logerror("scsi_drq_w: unknown mem_mask %08x\n", mem_mask);
		break;
	}
}

uint16_t maclc_state::swim_r(offs_t offset, u16 mem_mask)
{
	if (!machine().side_effects_disabled())
	{
		m_maincpu->adjust_icount(-5);
	}

	u16 result = m_fdc->read((offset >> 8) & 0xf);
	return result << 8;
}
void maclc_state::swim_w(offs_t offset, u16 data, u16 mem_mask)
{
	if (ACCESSING_BITS_0_7)
		m_fdc->write((offset >> 8) & 0xf, data & 0xff);
	else
		m_fdc->write((offset >> 8) & 0xf, data >> 8);
}

void maclc_state::phases_w(uint8_t phases)
{
	if (m_cur_floppy)
		m_cur_floppy->seek_phase_w(phases);
}

void maclc_state::devsel_w(uint8_t devsel)
{
	if (devsel == 1)
		m_cur_floppy = m_floppy[0]->get_device();
	else if (devsel == 2)
		m_cur_floppy = m_floppy[1]->get_device();
	else
		m_cur_floppy = nullptr;

	m_fdc->set_floppy(m_cur_floppy);
	if (m_cur_floppy)
		m_cur_floppy->ss_w(m_hdsel);
}

void maclc_state::hdsel_w(int state)
{
	if (state != m_hdsel)
	{
		if (m_cur_floppy)
		{
			m_cur_floppy->ss_w(state);
		}
	}
	m_hdsel = state;
}

/***************************************************************************
    DEVICE CONFIG
***************************************************************************/

static INPUT_PORTS_START( maclc )
	PORT_START("config")
	PORT_CONFNAME(0x01, 0x00, "FPU")
	PORT_CONFSETTING(0x00, "No FPU")
	PORT_CONFSETTING(0x01, "FPU Present")
INPUT_PORTS_END

// mactv doesn't have a way to add an FPU
static INPUT_PORTS_START( mactv )
INPUT_PORTS_END

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

void maclc_state::maclc_base(machine_config &config)
{
	M68020HMMU(config, m_maincpu, C15M);
	m_maincpu->set_addrmap(AS_PROGRAM, &maclc_state::maclc_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	RAM(config, m_ram);

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:1", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3").option_set("cdrom", NSCSI_CDROM_APPLE).machine_config(
		[](device_t *device)
		{
			device->subdevice<cdda_device>("cdda")->add_route(0, "^^speaker", 1.0, 0);
			device->subdevice<cdda_device>("cdda")->add_route(1, "^^speaker", 1.0, 1);
		});
	NSCSI_CONNECTOR(config, "scsi:4", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", mac_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr5380", NCR53C80).machine_config([this](device_t *device)
	{
		ncr53c80_device &adapter = downcast<ncr53c80_device &>(*device);
		adapter.drq_handler().set(m_scsihelp, FUNC(mac_scsi_helper_device::drq_w));
	});

	MAC_SCSI_HELPER(config, m_scsihelp);
	m_scsihelp->scsi_read_callback().set(m_ncr5380, FUNC(ncr53c80_device::read));
	m_scsihelp->scsi_write_callback().set(m_ncr5380, FUNC(ncr53c80_device::write));
	m_scsihelp->scsi_dma_read_callback().set(m_ncr5380, FUNC(ncr53c80_device::dma_r));
	m_scsihelp->scsi_dma_write_callback().set(m_ncr5380, FUNC(ncr53c80_device::dma_w));
	m_scsihelp->cpu_halt_callback().set_inputline(m_maincpu, INPUT_LINE_HALT);
	m_scsihelp->timeout_error_callback().set(FUNC(maclc_state::scsi_berr_w));

	SOFTWARE_LIST(config, "hdd_list").set_original("mac_hdd");
	SOFTWARE_LIST(config, "cd_list").set_original("mac_cdrom").set_filter("MC68020,MC68020_32");
	SOFTWARE_LIST(config, "flop35hd_list").set_original("mac_hdflop");

	SCC85C30(config, m_scc, C7M);
	m_scc->configure_channels(3'686'400, 3'686'400, 3'686'400, 3'686'400);
	m_scc->out_int_callback().set(m_v8, FUNC(v8_device::scc_irq_w));
	m_scc->out_txda_callback().set("printer", FUNC(rs232_port_device::write_txd));
	m_scc->out_txdb_callback().set("modem", FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232a(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_scc, FUNC(z80scc_device::rxa_w));
	rs232a.dcd_handler().set(m_scc, FUNC(z80scc_device::dcda_w));
	rs232a.cts_handler().set(m_scc, FUNC(z80scc_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "modem", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_scc, FUNC(z80scc_device::rxb_w));
	rs232b.dcd_handler().set(m_scc, FUNC(z80scc_device::dcdb_w));
	rs232b.cts_handler().set(m_scc, FUNC(z80scc_device::ctsb_w));

	SPEAKER(config, "speaker", 2).front();

	APPLE_DFAC(config, m_dfac, 22257);
	m_dfac->add_route(0, "speaker", 1.0, 0);
	m_dfac->add_route(1, "speaker", 1.0, 1);

	V8(config, m_v8, C15M);
	m_v8->set_maincpu_tag("maincpu");
	m_v8->set_rom_tag("bootrom");
	m_v8->hdsel_callback().set(FUNC(maclc_state::hdsel_w));
	m_v8->hmmu_enable_callback().set(FUNC(maclc_state::set_hmmu));
	m_v8->add_route(0, m_dfac, 1.0, 0);
	m_v8->add_route(1, m_dfac, 1.0, 1);

	nubus_device &nubus(NUBUS(config, "pds", 0));
	nubus.set_space(m_maincpu, AS_PROGRAM);
	nubus.set_address_mask(0x80ffffff);
	nubus.set_bus_mode(nubus_device::nubus_mode_t::LC_PDS);
	// V8 supports interrupts for slots $C, $D, and $E, but the LC, LC II, and Color Classic
	// only hook the slot $E IRQ up to the PDS slot.  ($C/$D/$E are 0/1/2 on the schematics).
	nubus.out_irqe_callback().set(m_v8, FUNC(v8_device::slot2_irq_w));

	MACADB(config, m_macadb, C15M);

	EGRET(config, m_egret, XTAL(32'768));
	m_egret->set_default_bios_tag("341s0850");
	m_egret->reset_callback().set(FUNC(maclc_state::egret_reset_w));
	m_egret->dfac_scl_callback().set(m_dfac, FUNC(dfac_device::clock_write));
	m_egret->dfac_sda_callback().set(m_dfac, FUNC(dfac_device::data_write));
	m_egret->dfac_latch_callback().set(m_dfac, FUNC(dfac_device::latch_write));
	m_egret->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
	m_egret->via_clock_callback().set(m_v8, FUNC(v8_device::cb1_w));
	m_egret->via_data_callback().set(m_v8, FUNC(v8_device::cb2_w));
	m_macadb->adb_data_callback().set(m_egret, FUNC(egret_device::set_adb_line));
	config.set_perfect_quantum(m_maincpu);

	m_v8->pb3_callback().set(m_egret, FUNC(egret_device::get_xcvr_session));
	m_v8->pb4_callback().set(m_egret, FUNC(egret_device::set_via_full));
	m_v8->pb5_callback().set(m_egret, FUNC(egret_device::set_sys_session));
	m_v8->cb2_callback().set(m_egret, FUNC(egret_device::set_via_data));

	SWIM1(config, m_fdc, C15M);
	m_fdc->devsel_cb().set(FUNC(maclc_state::devsel_w));
	m_fdc->phases_cb().set(FUNC(maclc_state::phases_w));

	applefdintf_device::add_35_hd(config, m_floppy[0]);
	applefdintf_device::add_35_nc(config, m_floppy[1]);
}

void maclc_state::maclc(machine_config &config)
{
	maclc_base(config);

	NUBUS_SLOT(config, "lcpds", "pds", mac_pdslc_orig_cards, nullptr);

	m_ram->set_default_size("2M");
	m_ram->set_extra_options("4M,6M,10M");
	m_v8->set_baseram_is_4M(false);
}

void maclc_state::maclc2(machine_config &config)
{
	maclc_base(config);

	NUBUS_SLOT(config, "lcpds", "pds", mac_pdslc_cards, nullptr);

	M68030(config.replace(), m_maincpu, C15M);
	m_maincpu->set_addrmap(AS_PROGRAM, &maclc_state::maclc_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	m_ram->set_default_size("4M");
	m_ram->set_extra_options("6M,8M,10M");
	m_v8->set_baseram_is_4M(true);

	SOFTWARE_LIST(config.replace(), "cd_list").set_original("mac_cdrom").set_filter("MC68030,MC68030_32");
}

void maclc_state::maccclas(machine_config &config)
{
	maclc_base(config);

	M68030(config.replace(), m_maincpu, C15M);
	m_maincpu->set_addrmap(AS_PROGRAM, &maclc_state::maccclassic_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	config.device_remove("egret");
	config.device_remove("fdc");

	CUDA_V2XX(config, m_cuda, XTAL(32'768));
	m_cuda->set_default_bios_tag("341s0417");
	m_cuda->reset_callback().set(FUNC(maclc_state::egret_reset_w));
	m_cuda->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
	m_cuda->via_clock_callback().set(m_v8, FUNC(v8_device::cb1_w));
	m_cuda->via_data_callback().set(m_v8, FUNC(v8_device::cb2_w));
	m_cuda->nmi_callback().set_inputline(m_maincpu, M68K_IRQ_7);
	m_macadb->adb_data_callback().set(m_cuda, FUNC(cuda_device::set_adb_line));
	m_macadb->adb_power_callback().set(m_cuda, FUNC(cuda_device::set_adb_power));
	config.set_perfect_quantum(m_maincpu);

	SPICE(config.replace(), m_v8, C15M);
	m_v8->set_maincpu_tag("maincpu");
	m_v8->set_rom_tag("bootrom");
	m_v8->hdsel_callback().set(FUNC(maclc_state::hdsel_w));
	m_v8->pb3_callback().set(m_cuda, FUNC(cuda_device::get_treq));
	m_v8->pb4_callback().set(m_cuda, FUNC(cuda_device::set_byteack));
	m_v8->pb5_callback().set(m_cuda, FUNC(cuda_device::set_tip));
	m_v8->cb2_callback().set(m_cuda, FUNC(cuda_device::set_via_data));
	m_v8->add_route(0, "speaker", 1.0, 0);
	m_v8->add_route(1, "speaker", 1.0, 1);

	config.device_remove("dfac");
	APPLE_DFAC2(config, m_dfac2, 22257);
	m_dfac2->sda_callback().set(m_cuda, FUNC(cuda_device::set_iic_sda));
	m_cuda->iic_scl_callback().set(m_dfac2, FUNC(dfac2_device::scl_write));
	m_cuda->iic_sda_callback().set(m_dfac2, FUNC(dfac2_device::sda_write));

	NUBUS_SLOT(config, "lcpds", "pds", mac_pdslc_cards, nullptr);

	m_ram->set_default_size("4M");
	m_ram->set_extra_options("6M,8M,10M");
	m_v8->set_baseram_is_4M(true);

	SOFTWARE_LIST(config.replace(), "cd_list").set_original("mac_cdrom").set_filter("MC68030,MC68030_32");
}

void maclc_state::mactv(machine_config &config)
{
	maclc_base(config);

	M68030(config.replace(), m_maincpu, C32M);
	m_maincpu->set_fpu_enable(false);   // this machine has no FPU and no ability to add one
	m_maincpu->set_addrmap(AS_PROGRAM, &maclc_state::maccclassic_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	config.device_remove("egret");
	config.device_remove("fdc");

	CUDA_V2XX(config, m_cuda, XTAL(32'768));
	m_cuda->set_default_bios_tag("341s0789");
	m_cuda->reset_callback().set(FUNC(maclc_state::egret_reset_w));
	m_cuda->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
	m_cuda->via_clock_callback().set(m_v8, FUNC(v8_device::cb1_w));
	m_cuda->via_data_callback().set(m_v8, FUNC(v8_device::cb2_w));
	m_cuda->nmi_callback().set_inputline(m_maincpu, M68K_IRQ_7);
	m_macadb->adb_data_callback().set(m_cuda, FUNC(cuda_device::set_adb_line));
	m_macadb->adb_power_callback().set(m_cuda, FUNC(cuda_device::set_adb_power));
	config.set_perfect_quantum(m_maincpu);

	TINKERBELL(config.replace(), m_v8, C15M);
	m_v8->set_maincpu_tag("maincpu");
	m_v8->set_rom_tag("bootrom");
	m_v8->hdsel_callback().set(FUNC(maclc_state::hdsel_w));
	m_v8->pb3_callback().set(m_cuda, FUNC(cuda_device::get_treq));
	m_v8->pb4_callback().set(m_cuda, FUNC(cuda_device::set_byteack));
	m_v8->pb5_callback().set(m_cuda, FUNC(cuda_device::set_tip));
	m_v8->cb2_callback().set(m_cuda, FUNC(cuda_device::set_via_data));
	m_v8->add_route(0, "speaker", 1.0, 0);
	m_v8->add_route(1, "speaker", 1.0, 1);

	config.device_remove("dfac");

	// Mac TV doesn't have an LC PDS
	config.device_remove("pds");

	m_ram->set_default_size("4M");
	m_ram->set_extra_options("5M,6M,8M");
	m_v8->set_baseram_is_4M(true);

	SOFTWARE_LIST(config.replace(), "cd_list").set_original("mac_cdrom").set_filter("MC68030,MC68030_32");
}

void maclc_state::macclas2(machine_config &config)
{
	maclc_base(config);

	M68030(config.replace(), m_maincpu, C15M);
	m_maincpu->set_addrmap(AS_PROGRAM, &maclc_state::maclc_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	EAGLE(config.replace(), m_v8, C15M);
	m_v8->set_maincpu_tag("maincpu");
	m_v8->set_rom_tag("bootrom");
	m_v8->hdsel_callback().set(FUNC(maclc_state::hdsel_w));
	m_v8->pb3_callback().set(m_egret, FUNC(egret_device::get_xcvr_session));
	m_v8->pb4_callback().set(m_egret, FUNC(egret_device::set_via_full));
	m_v8->pb5_callback().set(m_egret, FUNC(egret_device::set_sys_session));
	m_v8->cb2_callback().set(m_egret, FUNC(egret_device::set_via_data));
	m_v8->add_route(0, m_dfac, 1.0, 0);
	m_v8->add_route(1, m_dfac, 1.0, 1);

	// Classic II doesn't have an LC PDS slot (and its ROM has the Slot Manager disabled)
	config.device_remove("pds");

	m_ram->set_default_size("4M");
	m_ram->set_extra_options("6M,8M,10M");
	m_v8->set_baseram_is_4M(true);

	SOFTWARE_LIST(config.replace(), "cd_list").set_original("mac_cdrom").set_filter("MC68030,MC68030_32");
}

ROM_START(maclc)
	ROM_REGION32_BE(0x100000, "bootrom", 0)
	ROM_LOAD("350eacf0.rom", 0x000000, 0x080000, CRC(71681726) SHA1(6bef5853ae736f3f06c2b4e79772f65910c3b7d4))
ROM_END

ROM_START( maclc2 )
	ROM_REGION32_BE(0x100000, "bootrom", 0)
	ROM_LOAD32_BYTE( "341-0476_ue2-hh.bin", 0x000000, 0x020000, CRC(0c3b0ce4) SHA1(e4e8c883d7f2e002a3f7b7aefaa3840991e57025) )
	ROM_LOAD32_BYTE( "341-0475_ud2-mh.bin", 0x000001, 0x020000, CRC(7b013595) SHA1(0b82d8fac570270db9774f6254017d28611ae756) )
	ROM_LOAD32_BYTE( "341-0474_uc2-ml.bin", 0x000002, 0x020000, CRC(2ff2f52b) SHA1(876850df61d0233c1dd3c00d48d8d6690186b164) )
	ROM_LOAD32_BYTE( "341-0473_ub2-ll.bin", 0x000003, 0x020000, CRC(8843c37c) SHA1(bb5104110507ca543d106f11c6061245fd90c1a7) )
ROM_END

ROM_START( macclas2 )
	ROM_REGION32_BE(0x100000, "bootrom", 0) // 3193670e
	ROM_LOAD32_BYTE( "341-0867__ba16__=c=apple_91.romhh.27c010.u25", 0x000000, 0x020000, CRC(88230887) SHA1(8f45f6d7eb6a8ec9242a46db4773af1d154409c6) )
	ROM_LOAD32_BYTE( "341-0866__5be9__=c=apple_91.rommh.27c010.u24", 0x000001, 0x020000, CRC(eae68c36) SHA1(e6ce79647dfe7e66590a012836d0b6e985ff672b) )
	ROM_LOAD32_BYTE( "341-0865__821e__=c=apple_91.romml.27c010.u23", 0x000002, 0x020000, CRC(cb306c01) SHA1(4d6e409995fd9a4aa9afda0fd790a5b09b1c2aca) )
	ROM_LOAD32_BYTE( "341-0864__6fc6__=c=apple_91.romll.27c010.u22", 0x000003, 0x020000, CRC(21a51e72) SHA1(bb513c1a5b8a41c7534d66aeacaeea47f58dae92) )

	/*
	    There is a genuine bug in this ROM where a JMP through a table uses an index past the size of the
	    table and the processor ends up in the middle of another instruction.  On a real 68030, the resulting
	    illegal instruction does some still-not-100%-understood things to the registers that cause things to
	    work by accident.  While that is still being investigated, we can at least patch the ROM so that
	    booting when the system is in 32-bit mode works properly.

	    This first patch changes the bad table JMP to an RTS; the ROM in the IIvx and IIvi has the correct
	    number of table entries for the Classic II's boxflag and shows that the desired result is to do
	    nothing.
	*/
	ROM_FILL(0x43b6e, 1, 0x4e)
	ROM_FILL(0x43b6f, 1, 0x75)

	// This second patch fixes the ROM checksum to compensate for the patch we just made.
	ROM_FILL(0x00002, 1, 0x66)
	ROM_FILL(0x00003, 1, 0x88)
ROM_END

ROM_START(maccclas)
	ROM_REGION32_BE(0x100000, "bootrom", 0)
	ROM_LOAD("ecd99dc0.rom", 0x000000, 0x100000, CRC(c84c3aa5) SHA1(fd9e852e2d77fe17287ba678709b9334d4d74f1e))
ROM_END

ROM_START(mactv)
	ROM_REGION32_BE(0x100000, "bootrom", 0)
	ROM_LOAD("eaf1678d.bin", 0x000000, 0x100000, CRC(0644f05b) SHA1(74975c60d3a560fac9ad63125bb65a750fceaede))
ROM_END

} // anonymous namespace

COMP(1990, maclc,  0, 0, maclc,  maclc, maclc_state, empty_init, "Apple Computer", "Macintosh LC", MACHINE_SUPPORTS_SAVE)
COMP(1991, maclc2, 0, 0, maclc2, maclc, maclc_state, empty_init, "Apple Computer", "Macintosh LC II", MACHINE_SUPPORTS_SAVE)
COMP(1991, macclas2, 0, 0, macclas2, maclc, maclc_state, empty_init, "Apple Computer", "Macintosh Classic II", MACHINE_SUPPORTS_SAVE)
COMP(1993, maccclas, 0, 0, maccclas, maclc, maclc_state, empty_init, "Apple Computer", "Macintosh Color Classic", MACHINE_SUPPORTS_SAVE)
COMP(1994, mactv, 0, 0, mactv, mactv, maclc_state, empty_init, "Apple Computer", "Macintosh TV", MACHINE_SUPPORTS_SAVE)



maclc3.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/****************************************************************************

    drivers/maclc3.cpp
    Mac LC III ("Vail")
    Mac LC 520 ("Hook")

    By R. Belmont

    These are basically identical '030 machines, except the LC III is a pizzabox
    while the LC 520 is an all-in-one with a 14" Sony Trinitron built-in.
    LC III uses the Egret ADB microcontroller while LC 520 uses the later Cuda.
    Everything else is the same.

****************************************************************************/

#include "emu.h"

#include "cuda.h"
#include "dfac.h"
#include "dfac2.h"
#include "egret.h"
#include "macadb.h"
#include "macscsi.h"
#include "mactoolbox.h"
#include "omega.h"
#include "sonora.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/devices.h"
#include "bus/nubus/cards.h"
#include "bus/nubus/nubus.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68030.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "machine/z80scc.h"
#include "machine/nscsi_bus.h"
#include "machine/ncr5380.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"
#include "softlist_dev.h"

namespace {

static constexpr u32 C7M = 7833600;
static constexpr u32 C15M = (C7M * 2);

class macvail_state : public driver_device
{
public:
	macvail_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_macadb(*this, "macadb"),
		m_ram(*this, RAM_TAG),
		m_sonora(*this, "sonora"),
		m_dfac(*this, "dfac"),
		m_dfac2(*this, "dfac2"),
		m_omega(*this, "omega"),
		m_scsibus1(*this, "scsi"),
		m_ncr5380(*this, "scsi:7:ncr5380"),
		m_scsihelp(*this, "scsihelp"),
		m_scc(*this, "scc"),
		m_egret(*this, "egret"),
		m_cuda(*this, "cuda"),
		m_config(*this, "config")
	{
	}

	void maclc3_base(machine_config &config);
	void maclc3(machine_config &config);
	void maclc3p(machine_config &config);
	void maclc520(machine_config &config);
	void maclc550(machine_config &config);
	void base_map(address_map &map) ATTR_COLD;
	void maclc3_map(address_map &map) ATTR_COLD;
	void maclc3p_map(address_map &map) ATTR_COLD;
	void maclc520_map(address_map &map) ATTR_COLD;
	void maclc550_map(address_map &map) ATTR_COLD;

private:
	required_device<m68030_device> m_maincpu;
	optional_device<macadb_device> m_macadb;
	required_device<ram_device> m_ram;
	required_device<sonora_device> m_sonora;
	optional_device<dfac_device> m_dfac;
	optional_device<dfac2_device> m_dfac2;
	required_device<omega_device> m_omega;
	required_device<nscsi_bus_device> m_scsibus1;
	required_device<ncr5380_device> m_ncr5380;
	required_device<mac_scsi_helper_device> m_scsihelp;
	required_device<z80scc_device> m_scc;
	optional_device<egret_device> m_egret;
	optional_device<cuda_device> m_cuda;
	required_ioport m_config;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	u16 scc_r(offs_t offset)
	{
		u16 result = m_scc->dc_ab_r(offset);
		return (result << 8) | result;
	}
	void scc_w(offs_t offset, u16 data)
	{
		m_scc->dc_ab_w(offset, data >> 8);
	}

	u16 scsi_r(offs_t offset, u16 mem_mask = ~0);
	void scsi_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	u32 scsi_drq_r(offs_t offset, u32 mem_mask = ~0);
	void scsi_drq_w(offs_t offset, u32 data, u32 mem_mask = ~0);

	void scsi_berr_w(u8 data)
	{
		m_maincpu->pulse_input_line(M68K_LINE_BUSERROR, attotime::zero);
	}

	void cuda_reset_w(int state)
	{
		m_maincpu->set_input_line(INPUT_LINE_HALT, state);
		m_maincpu->set_input_line(INPUT_LINE_RESET, state);
	}
};

void macvail_state::machine_start()
{
	m_sonora->set_ram_info((u32 *) m_ram->pointer(), m_ram->size());
}

void macvail_state::machine_reset()
{
	if (m_config)
	{
		m_maincpu->set_fpu_enable(BIT(m_config->read(), 0));
	}
}

/***************************************************************************
    ADDRESS MAPS
***************************************************************************/
void macvail_state::base_map(address_map &map)
{
	// RAM, ROM, and base I/O mappings come from Sonora (LCIII) / Ardbeg (LC520)
	map(0x40000000, 0x600fffff).m(m_sonora, FUNC(sonora_device::map));

	map(0x50004000, 0x50005fff).rw(FUNC(macvail_state::scc_r), FUNC(macvail_state::scc_w)).mirror(0x00f00000);
	map(0x50006000, 0x50007fff).rw(FUNC(macvail_state::scsi_drq_r), FUNC(macvail_state::scsi_drq_w)).mirror(0x00f00000);
	map(0x50010000, 0x50011fff).rw(FUNC(macvail_state::scsi_r), FUNC(macvail_state::scsi_w)).mirror(0x00f00000);
	map(0x50012000, 0x50013fff).rw(FUNC(macvail_state::scsi_drq_r), FUNC(macvail_state::scsi_drq_w)).mirror(0x00f00000);
}

void macvail_state::maclc3_map(address_map &map)
{
	base_map(map);
	map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a0001; }));
}

void macvail_state::maclc3p_map(address_map &map)
{
	base_map(map);
	map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a0003; }));
}

void macvail_state::maclc520_map(address_map &map)
{
	base_map(map);
	map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a0100; }));
}

void macvail_state::maclc550_map(address_map &map)
{
	base_map(map);
	map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a0101; }));
}

u16 macvail_state::scsi_r(offs_t offset, u16 mem_mask)
{
	const int reg = (offset >> 3) & 0xf;
	const bool pseudo_dma = (reg == 6) && (offset == 0x130);

	return m_scsihelp->read_wrapper(pseudo_dma, reg) << 8;
}

void macvail_state::scsi_w(offs_t offset, u16 data, u16 mem_mask)
{
	const int reg = (offset >> 3) & 0xf;
	const bool pseudo_dma = (reg == 0) && (offset == 0x100);

	m_scsihelp->write_wrapper(pseudo_dma, reg, data >> 8);
}

u32 macvail_state::scsi_drq_r(offs_t offset, u32 mem_mask)
{
	switch (mem_mask)
	{
	case 0xff000000:
		return m_scsihelp->read_wrapper(true, 6) << 24;

	case 0xffff0000:
		return (m_scsihelp->read_wrapper(true, 6) << 24) | (m_scsihelp->read_wrapper(true, 6) << 16);

	case 0xffffffff:
		return (m_scsihelp->read_wrapper(true, 6) << 24) | (m_scsihelp->read_wrapper(true, 6) << 16) | (m_scsihelp->read_wrapper(true, 6) << 8) | m_scsihelp->read_wrapper(true, 6);

	default:
		logerror("scsi_drq_r: unknown mem_mask %08x\n", mem_mask);
	}

	return 0;
}

void macvail_state::scsi_drq_w(offs_t offset, u32 data, u32 mem_mask)
{
	switch (mem_mask)
	{
	case 0xff000000:
		m_scsihelp->write_wrapper(true, 0, data >> 24);
		break;

	case 0xffff0000:
		m_scsihelp->write_wrapper(true, 0, data >> 24);
		m_scsihelp->write_wrapper(true, 0, data >> 16);
		break;

	case 0xffffffff:
		m_scsihelp->write_wrapper(true, 0, data >> 24);
		m_scsihelp->write_wrapper(true, 0, data >> 16);
		m_scsihelp->write_wrapper(true, 0, data >> 8);
		m_scsihelp->write_wrapper(true, 0, data & 0xff);
		break;

	default:
		logerror("scsi_drq_w: unknown mem_mask %08x\n", mem_mask);
		break;
	}
}

/***************************************************************************
    DEVICE CONFIG
***************************************************************************/

static INPUT_PORTS_START( macadb )
	PORT_START("config")
	PORT_CONFNAME(0x01, 0x00, "FPU")
	PORT_CONFSETTING(0x00, "No FPU")
	PORT_CONFSETTING(0x01, "FPU Present")
INPUT_PORTS_END

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

void macvail_state::maclc3_base(machine_config &config)
{
	M68030(config, m_maincpu, 25_MHz_XTAL);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	RAM(config, m_ram);
	m_ram->set_default_size("4M");
	m_ram->set_extra_options("8M,16M,32M,48M,64M,80M");

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", mac_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:1", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3").option_set("cdrom", NSCSI_CDROM_APPLE).machine_config(
		[](device_t *device)
		{
			device->subdevice<cdda_device>("cdda")->add_route(0, "^^speaker", 1.0, 0);
			device->subdevice<cdda_device>("cdda")->add_route(1, "^^speaker", 1.0, 1);
		});
	NSCSI_CONNECTOR(config, "scsi:4", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr5380", NCR53C80).machine_config([this](device_t *device)
	{
		ncr53c80_device &adapter = downcast<ncr53c80_device &>(*device);
		adapter.drq_handler().set(m_scsihelp, FUNC(mac_scsi_helper_device::drq_w));
	});

	MAC_SCSI_HELPER(config, m_scsihelp);
	m_scsihelp->scsi_read_callback().set(m_ncr5380, FUNC(ncr53c80_device::read));
	m_scsihelp->scsi_write_callback().set(m_ncr5380, FUNC(ncr53c80_device::write));
	m_scsihelp->scsi_dma_read_callback().set(m_ncr5380, FUNC(ncr53c80_device::dma_r));
	m_scsihelp->scsi_dma_write_callback().set(m_ncr5380, FUNC(ncr53c80_device::dma_w));
	m_scsihelp->cpu_halt_callback().set_inputline(m_maincpu, INPUT_LINE_HALT);
	m_scsihelp->timeout_error_callback().set(FUNC(macvail_state::scsi_berr_w));

	SOFTWARE_LIST(config, "hdd_list").set_original("mac_hdd");
	SOFTWARE_LIST(config, "cd_list").set_original("mac_cdrom").set_filter("MC68030,MC68030_32");
	SOFTWARE_LIST(config, "flop35hd_list").set_original("mac_hdflop");

	SCC85C30(config, m_scc, C7M);
	m_scc->configure_channels(3'686'400, 3'686'400, 3'686'400, 3'686'400);
	m_scc->out_int_callback().set(m_sonora, FUNC(sonora_device::scc_irq_w));
	m_scc->out_txda_callback().set("printer", FUNC(rs232_port_device::write_txd));
	m_scc->out_txdb_callback().set("modem", FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232a(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_scc, FUNC(z80scc_device::rxa_w));
	rs232a.dcd_handler().set(m_scc, FUNC(z80scc_device::dcda_w));
	rs232a.cts_handler().set(m_scc, FUNC(z80scc_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "modem", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_scc, FUNC(z80scc_device::rxb_w));
	rs232b.dcd_handler().set(m_scc, FUNC(z80scc_device::dcdb_w));
	rs232b.cts_handler().set(m_scc, FUNC(z80scc_device::ctsb_w));

	SPEAKER(config, "speaker", 2).front();

	APPLE_OMEGA(config, m_omega, 31.3344_MHz_XTAL);
	m_omega->pclock_changed().set(m_sonora, FUNC(sonora_device::pixel_clock_w));

	SONORA(config, m_sonora, C15M);
	m_sonora->set_maincpu_tag("maincpu");
	m_sonora->set_rom_tag("bootrom");

	nubus_device &nubus(NUBUS(config, "pds", 0));
	nubus.set_space(m_maincpu, AS_PROGRAM);
	nubus.set_bus_mode(nubus_device::nubus_mode_t::LC32_PDS);
	// LC III style PDS cards have slot IRQs $C, $D, and $E connected
	nubus.out_irqc_callback().set(m_sonora, FUNC(sonora_device::slot0_irq_w));
	nubus.out_irqd_callback().set(m_sonora, FUNC(sonora_device::slot1_irq_w));
	nubus.out_irqe_callback().set(m_sonora, FUNC(sonora_device::slot2_irq_w));
	NUBUS_SLOT(config, "lcpds", "pds", mac_pdslc_cards, nullptr);

	MACADB(config, m_macadb, C15M);
}

void macvail_state::maclc3(machine_config &config)
{
	maclc3_base(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &macvail_state::maclc3_map);
	m_maincpu->set_fpu_enable(false); // this machine has no FPU

	EGRET(config, m_egret, XTAL(32'768));
	m_egret->set_default_bios_tag("341s0851");
	m_egret->reset_callback().set(FUNC(macvail_state::cuda_reset_w));
	m_egret->dfac_scl_callback().set(m_dfac, FUNC(dfac_device::clock_write));
	m_egret->dfac_scl_callback().append(m_omega, FUNC(omega_device::clock_write));
	m_egret->dfac_sda_callback().set(m_dfac, FUNC(dfac_device::data_write));
	m_egret->dfac_sda_callback().append(m_omega, FUNC(omega_device::data_write));
	m_egret->dfac_latch_callback().set(m_dfac, FUNC(dfac_device::latch_write));
	m_egret->dfac_latch_callback().append(m_omega, FUNC(omega_device::latch_write));
	m_egret->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
	m_egret->via_clock_callback().set(m_sonora, FUNC(sonora_device::cb1_w));
	m_egret->via_data_callback().set(m_sonora, FUNC(sonora_device::cb2_w));
	m_macadb->adb_data_callback().set(m_egret, FUNC(egret_device::set_adb_line));
	config.set_perfect_quantum(m_maincpu);

	m_sonora->pb3_callback().set(m_egret, FUNC(egret_device::get_xcvr_session));
	m_sonora->pb4_callback().set(m_egret, FUNC(egret_device::set_via_full));
	m_sonora->pb5_callback().set(m_egret, FUNC(egret_device::set_sys_session));
	m_sonora->cb2_callback().set(m_egret, FUNC(egret_device::set_via_data));

	APPLE_DFAC(config, m_dfac, 22257);
	m_dfac->add_route(0, "speaker", 1.0, 0);
	m_dfac->add_route(1, "speaker", 1.0, 1);
	m_sonora->add_route(0, m_dfac, 1.0, 0);
	m_sonora->add_route(1, m_dfac, 1.0, 1);
}

void macvail_state::maclc3p(machine_config &config)
{
	maclc3(config);
	M68030(config.replace(), m_maincpu, 33_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &macvail_state::maclc3p_map);
}

void macvail_state::maclc520(machine_config &config)
{
	maclc3_base(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &macvail_state::maclc520_map);

	CUDA_V2XX(config, m_cuda, XTAL(32'768));
	m_cuda->set_default_bios_tag("341s0060");
	m_cuda->reset_callback().set(FUNC(macvail_state::cuda_reset_w));
	m_cuda->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
	m_cuda->via_clock_callback().set(m_sonora, FUNC(sonora_device::cb1_w));
	m_cuda->via_data_callback().set(m_sonora, FUNC(sonora_device::cb2_w));
	m_cuda->iic_scl_callback().set(m_omega, FUNC(omega_device::clock_write));
	m_cuda->iic_sda_callback().set(m_omega, FUNC(omega_device::data_write));
	m_cuda->dfac_latch_callback().set(m_omega, FUNC(omega_device::latch_write));
	m_cuda->nmi_callback().set_inputline(m_maincpu, M68K_IRQ_7);
	m_macadb->adb_data_callback().set(m_cuda, FUNC(cuda_device::set_adb_line));
	m_macadb->adb_power_callback().set(m_cuda, FUNC(cuda_device::set_adb_power));
	config.set_perfect_quantum(m_maincpu);

	m_sonora->pb3_callback().set(m_cuda, FUNC(cuda_device::get_treq));
	m_sonora->pb4_callback().set(m_cuda, FUNC(cuda_device::set_byteack));
	m_sonora->pb5_callback().set(m_cuda, FUNC(cuda_device::set_tip));
	m_sonora->cb2_callback().set(m_cuda, FUNC(cuda_device::set_via_data));

	// DFAC is in LC III/LCIII+.  LC520/550 use DFAC2.
	m_sonora->reset_routes();
	m_sonora->add_route(0, "speaker", 1.0, 0);
	m_sonora->add_route(1, "speaker", 1.0, 1);

	APPLE_DFAC2(config, m_dfac2, 22257);
	m_dfac2->sda_callback().set(m_cuda, FUNC(cuda_device::set_iic_sda));
	m_cuda->iic_scl_callback().set(m_dfac2, FUNC(dfac2_device::scl_write));
	m_cuda->iic_sda_callback().set(m_dfac2, FUNC(dfac2_device::sda_write));
}

void macvail_state::maclc550(machine_config &config)
{
	maclc520(config);
	M68030(config.replace(), m_maincpu, 33_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &macvail_state::maclc550_map);
}

ROM_START( maclc3 )
	ROM_REGION32_BE(0x100000, "bootrom", 0)
	ROM_LOAD( "ecbbc41c.rom", 0x000000, 0x100000, CRC(e578f5f3) SHA1(c77df3220c861f37a2c553b6ee9241b202dfdffc) )
ROM_END

ROM_START( maclc520 )
	ROM_REGION32_BE(0x100000, "bootrom", 0)
	ROM_LOAD( "ede66cbd.rom", 0x000000, 0x100000, CRC(a893cb0f) SHA1(c54ee2f45020a4adeb7451adce04cd6e5fb69790) )
ROM_END

#define rom_maclc3p rom_maclc3
#define rom_maclc550 rom_maclc520

} // anonymous namespace

COMP(1993, maclc3, 0, 0, maclc3, macadb, macvail_state, empty_init, "Apple Computer", "Macintosh LC III", MACHINE_SUPPORTS_SAVE )
COMP(1993, maclc3p, maclc3, 0, maclc3p, macadb, macvail_state, empty_init, "Apple Computer", "Macintosh LC III+", MACHINE_SUPPORTS_SAVE )
COMP(1993, maclc520, 0, 0, maclc520, macadb, macvail_state, empty_init, "Apple Computer", "Macintosh LC 520", MACHINE_SUPPORTS_SAVE )
COMP(1994, maclc550, maclc520, 0, maclc550, macadb, macvail_state, empty_init, "Apple Computer", "Macintosh LC 550", MACHINE_SUPPORTS_SAVE )



macpdm.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont, O. Galibert

#include "emu.h"

#include "cuda.h"
#include "macadb.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/devices.h"
#include "bus/nubus/nubus.h"
#include "bus/nubus/cards.h"
#include "bus/rs232/rs232.h"
#include "cpu/powerpc/ppc.h"
#include "machine/6522via.h"
#include "machine/mv_sonora.h"
#include "machine/ncr53c90.h"
#include "machine/ram.h"
#include "machine/swim3.h"
#include "machine/timer.h"
#include "machine/z80scc.h"
#include "sound/awacs.h"

#include "softlist_dev.h"
#include "speaker.h"


namespace {

static constexpr auto IO_CLOCK = 31.3344_MHz_XTAL;
static constexpr auto ENET_CLOCK = 20_MHz_XTAL;
static constexpr auto SOUND_CLOCK = 45.1584_MHz_XTAL;

static constexpr u8 DMA2_IRQ_SND_IN             = 0x01;
static constexpr u8 DMA2_IRQ_SND_OUT            = 0x02;

class macpdm_state : public driver_device
{
public:
	macpdm_state(const machine_config &mconfig, device_type type, const char *tag);

	void macpdm(machine_config &config);

	void driver_init();
	virtual void driver_reset() override;

private:
	required_device<ppc_device> m_maincpu;
	required_device<via6522_device> m_via1;
	required_device<awacs_device> m_awacs;
	required_device<cuda_device> m_cuda;
	required_device<macadb_device> m_macadb;
	required_device<ram_device> m_ram;
	required_device<z80scc_device> m_scc;
	required_device<nscsi_bus_device> m_scsibus;
	required_device<ncr53c94_device> m_ncr53c94;
	required_device<swim3_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<mac_video_sonora_device> m_video;

	floppy_image_device *m_cur_floppy = nullptr;

	uint32_t m_model_id = 0;
	uint64_t m_hmc_reg = 0, m_hmc_buffer = 0;
	uint8_t m_hmc_bit = 0;

	uint8_t m_irq_control = 0;

	uint8_t m_dma_irq_1, m_dma_irq_2;

	uint8_t m_via2_ier = 0, m_via2_ifr = 0, m_via2_sier = 0, m_via2_sifr = 0;

	uint64_t m_dma_scsi_buffer = 0;

	uint32_t m_dma_badr = 0, m_dma_floppy_adr = 0;
	uint16_t m_dma_floppy_byte_count = 0, m_dma_floppy_offset = 0;

	uint16_t m_dma_berr_en = 0, m_dma_berr_flag = 0;

	uint32_t m_dma_scsi_a_base_adr = 0, m_dma_scsi_b_base_adr = 0;
	uint32_t m_dma_scsi_a_cur_offset = 0, m_dma_scsi_b_cur_offset = 0;

	uint8_t m_dma_scsi_a_ctrl = 0, m_dma_scsi_b_ctrl = 0, m_dma_floppy_ctrl = 0;
	uint8_t m_dma_scsi_buffer_word_count = 0;

	uint8_t m_dma_scc_txa_ctrl = 0, m_dma_scc_rxa_ctrl = 0, m_dma_scc_txb_ctrl = 0, m_dma_scc_rxb_ctrl = 0;
	uint8_t m_dma_enet_rx_ctrl = 0, m_dma_enet_tx_ctrl = 0;

	bool m_dma_scsi_a_in_step = false, m_dma_floppy_in_step = false, m_floppy_drq = false;

	void pdm_map(address_map &map) ATTR_COLD;

	void nmi_irq(int state);
	void dma_irq(int state);
	void enet_irq(int state);
	void scc_irq(int state);
	void via1_irq(int state);

	void bus_err_irq(int state);
	void fdc_dma_irq(int state);
	void etx_dma_irq(int state);
	void erx_dma_irq(int state);
	void txa_dma_irq(int state);
	void rxa_dma_irq(int state);
	void txb_dma_irq(int state);
	void rxb_dma_irq(int state);

	void sndo_dma_irq(int state);
	void sndi_dma_irq(int state);

	void fdc_err_irq(int state);
	void etx_err_irq(int state);
	void erx_err_irq(int state);
	void txa_err_irq(int state);
	void rxa_err_irq(int state);
	void txb_err_irq(int state);
	void rxb_err_irq(int state);

	void scsi_err_irq(int state);
	void sndo_err_irq(int state);
	void sndi_err_irq(int state);

	void vblank_irq(int state);
	void slot2_irq_w(int state);
	void slot1_irq_w(int state);
	void slot0_irq_w(int state);

	void fdc_irq(int state);
	void fdc_drq(int state);
	void recalc_dma_irqs();
	void scsi_irq(int state);
	void scsi_drq(int state);

	void phases_w(uint8_t phases);
	void sel35_w(int sel35);
	void devsel_w(uint8_t devsel);
	void hdsel_w(int hdsel);

	uint8_t via1_in_a();
	uint8_t via1_in_b();
	void via1_out_a(uint8_t data);
	void via1_out_b(uint8_t data);
	TIMER_DEVICE_CALLBACK_MEMBER(via1_60_15_timer);
	void via1_out_cb2(int state);

	void cuda_reset_w(int state);

	uint8_t via1_r(offs_t offset);
	void via1_w(offs_t offset, uint8_t data);

	uint8_t via2_ier_r();
	void via2_ier_w(uint8_t data);
	uint8_t via2_ifr_r();
	uint8_t via2_sier_r();
	void via2_sier_w(uint8_t data);
	uint8_t via2_sifr_r();
	void via2_sifr_w(uint8_t data);

	uint8_t fdc_r(offs_t offset);
	void fdc_w(offs_t offset, uint8_t data);

	uint8_t scsi_r(offs_t offset);
	void scsi_w(offs_t offset, uint8_t data);

	uint8_t hmc_r(offs_t offset);
	void hmc_w(offs_t offset, uint8_t data);

	void ram_size();

	uint32_t id_r();

	uint8_t diag_r(offs_t offset);

	uint8_t irq_control_r(offs_t offset);
	void irq_control_w(offs_t offset, uint8_t data);
	void irq_main_set(uint8_t mask, int state);
	void via2_irq_main_set(uint8_t mask, int state);
	void via2_irq_slot_set(uint8_t mask, int state);

	uint32_t dma_badr_r();
	void dma_badr_w(offs_t, uint32_t data, uint32_t mem_mask);
	uint16_t dma_berr_en_r();
	void dma_berr_en_w(offs_t, uint16_t data, uint16_t mem_mask);
	uint16_t dma_berr_flag_r();
	void dma_berr_flag_w(offs_t, uint16_t data, uint16_t mem_mask);

	void dma_scsi_a_step();
	uint32_t dma_scsi_a_base_adr_r();
	void dma_scsi_a_base_adr_w(offs_t, uint32_t data, uint32_t mem_mask);
	uint32_t dma_scsi_b_base_adr_r();
	void dma_scsi_b_base_adr_w(offs_t, uint32_t data, uint32_t mem_mask);
	uint8_t dma_scsi_a_ctrl_r();
	void dma_scsi_a_ctrl_w(uint8_t data);
	uint8_t dma_scsi_b_ctrl_r();
	void dma_scsi_b_ctrl_w(uint8_t data);
	uint32_t dma_scsi_a_cur_adr_r();
	uint32_t dma_scsi_b_cur_adr_r();

	void dma_floppy_step();
	uint8_t dma_floppy_ctrl_r();
	void dma_floppy_ctrl_w(uint8_t data);
	uint32_t dma_floppy_adr_r();
	void dma_floppy_adr_w(offs_t, uint32_t data, uint32_t mem_mask);
	uint16_t dma_floppy_byte_count_r();
	void dma_floppy_byte_count_w(offs_t, uint16_t data, uint16_t mem_mask);

	uint8_t dma_scc_txa_ctrl_r();
	void dma_scc_txa_ctrl_w(uint8_t data);
	uint8_t dma_scc_rxa_ctrl_r();
	void dma_scc_rxa_ctrl_w(uint8_t data);
	uint8_t dma_scc_txb_ctrl_r();
	void dma_scc_txb_ctrl_w(uint8_t data);
	uint8_t dma_scc_rxb_ctrl_r();
	void dma_scc_rxb_ctrl_w(uint8_t data);

	uint8_t dma_enet_rx_ctrl_r();
	void dma_enet_rx_ctrl_w(uint8_t data);
	uint8_t dma_enet_tx_ctrl_r();
	void dma_enet_tx_ctrl_w(uint8_t data);

	uint32_t sound_dma_output(offs_t offset);
	void sound_dma_input(offs_t offset, uint32_t value);

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};

macpdm_state::macpdm_state(const machine_config &mconfig, device_type type, const char *tag) :
	driver_device(mconfig, type, tag),
	m_maincpu(*this, "maincpu"),
	m_via1(*this, "via6522_1"),
	m_awacs(*this, "awacs"),
	m_cuda(*this, "cuda"),
	m_macadb(*this, "macadb"),
	m_ram(*this, RAM_TAG),
	m_scc(*this, "scc"),
	m_scsibus(*this, "scsi"),
	m_ncr53c94(*this, "scsi:7:ncr53c94"),
	m_fdc(*this, "fdc"),
	m_floppy(*this, "fdc:%d", 0U),
	m_video(*this, "video"),
	m_dma_irq_1(0),
	m_dma_irq_2(0)
{
	m_cur_floppy = nullptr;
}

void macpdm_state::driver_init()
{
	m_maincpu->space().install_ram(0, m_ram->mask(), 0, m_ram->pointer());

	m_model_id = 0xa55a3011;
	// 7100 = a55a3012
	// 8100 = a55a3013

	save_item(NAME(m_hmc_reg));
	save_item(NAME(m_hmc_buffer));
	save_item(NAME(m_hmc_bit));

	save_item(NAME(m_via2_ier));
	save_item(NAME(m_via2_ifr));
	save_item(NAME(m_via2_sier));
	save_item(NAME(m_via2_sifr));

	save_item(NAME(m_irq_control));

	save_item(NAME(m_dma_badr));
	save_item(NAME(m_dma_berr_en));
	save_item(NAME(m_dma_berr_flag));
	save_item(NAME(m_dma_scsi_buffer));
	save_item(NAME(m_dma_scsi_buffer_word_count));
	save_item(NAME(m_dma_scsi_a_in_step));
	save_item(NAME(m_dma_scsi_a_base_adr));
	save_item(NAME(m_dma_scsi_b_base_adr));
	save_item(NAME(m_dma_scsi_a_ctrl));
	save_item(NAME(m_dma_scsi_b_ctrl));
	save_item(NAME(m_dma_scsi_a_cur_offset));
	save_item(NAME(m_dma_scsi_b_cur_offset));
	save_item(NAME(m_dma_floppy_ctrl));
	save_item(NAME(m_dma_floppy_in_step));
	save_item(NAME(m_dma_scc_txa_ctrl));
	save_item(NAME(m_dma_scc_rxa_ctrl));
	save_item(NAME(m_dma_scc_txb_ctrl));
	save_item(NAME(m_dma_scc_rxb_ctrl));
	save_item(NAME(m_dma_enet_rx_ctrl));
	save_item(NAME(m_dma_enet_tx_ctrl));

	save_item(NAME(m_dma_floppy_adr));
	save_item(NAME(m_dma_floppy_offset));
	save_item(NAME(m_dma_floppy_byte_count));
	save_item(NAME(m_floppy_drq));
}

void macpdm_state::driver_reset()
{
	m_hmc_reg = 0;
	m_hmc_buffer = 0;
	m_hmc_bit = 0;

	m_via2_ier = 0x00;
	m_via2_ifr = 0x00;
	m_via2_sier = 0x00;
	m_via2_sifr = 0x7f;

	m_irq_control = 0;

	m_dma_badr = 0;
	m_dma_berr_en = 0;
	m_dma_berr_flag = 0;
	m_dma_scsi_buffer = 0;
	m_dma_scsi_buffer_word_count = 0;
	m_dma_scsi_a_in_step = false;
	m_dma_scsi_a_base_adr = 0;
	m_dma_scsi_b_base_adr = 0;
	m_dma_scsi_a_ctrl = 0;
	m_dma_scsi_b_ctrl = 0;
	m_dma_scsi_a_cur_offset = 0;
	m_dma_scsi_b_cur_offset = 0;
	m_dma_floppy_ctrl = 0;
	m_dma_scc_txa_ctrl = 0;
	m_dma_scc_rxa_ctrl = 0;
	m_dma_scc_txb_ctrl = 0;
	m_dma_scc_rxb_ctrl = 0;
	m_dma_enet_rx_ctrl = 0;
	m_dma_enet_tx_ctrl = 0;

	m_dma_floppy_adr = 0x15000;
	m_dma_floppy_offset = 0;
	m_dma_floppy_byte_count = 0;
	m_floppy_drq = false;

	m_video->set_vram_base((const u64 *)m_ram->pointer());
	m_video->set_vram_offset(0);

	m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
}

uint8_t macpdm_state::irq_control_r(offs_t offset)
{
	switch (offset)
	{
		case 0x0:
			return m_irq_control;

		case 0x8:
			return m_dma_irq_1;

		case 0xa:
			return m_dma_irq_2;
	}

	return 0;
}

void macpdm_state::irq_control_w(offs_t offset, uint8_t data)
{
	if (offset == 0)
	{
		if((m_irq_control ^ data) & 0x40) {
			m_irq_control = (m_irq_control & ~0xc0) | (data & 0x40);
			m_maincpu->set_input_line(PPC_IRQ, CLEAR_LINE);
		}
		if((data & 0xc0) == 0xc0 && (m_irq_control & 0x80)) {
			m_irq_control &= 0x7f;
			m_maincpu->set_input_line(PPC_IRQ, CLEAR_LINE);
		}
	}
}

void macpdm_state::irq_main_set(uint8_t mask, int state)
{
	if(((m_irq_control & mask) != 0) == state)
		return;

	m_irq_control ^= mask;

	if(m_irq_control & 0x40) {
		m_irq_control |= 0x80;
		m_maincpu->set_input_line(PPC_IRQ, ASSERT_LINE);
	} else {
		if(m_irq_control & 0x3f) {
			m_irq_control |= 0x80;
			m_maincpu->set_input_line(PPC_IRQ, ASSERT_LINE);
		} else {
			m_irq_control &= 0x7f;
			m_maincpu->set_input_line(PPC_IRQ, CLEAR_LINE);
		}
	}

	//  logerror("irq control %02x\n", m_irq_control);
}

void macpdm_state::via2_irq_main_set(uint8_t mask, int state)
{
	if(((m_via2_ifr & mask) != 0) == state)
		return;

	m_via2_ifr ^= mask;
	//  logerror("via2 main %02x / %02x -> %02x\n", m_via2_ifr, m_via2_ier, m_via2_ifr & m_via2_ier);

	irq_main_set(0x02, (m_via2_ifr & m_via2_ier) != 0);
}

void macpdm_state::via2_irq_slot_set(uint8_t mask, int state)
{
	if(((m_via2_sifr & mask) == 0) == state)
		return;

	m_via2_sifr ^= mask;
	via2_irq_main_set(0x02, ((~m_via2_sifr) & m_via2_sier) != 0);
}




// bit 7 = out - scc wait/request
// bit 5 = out - head select, unconnected
// bit 3 = ?   - sync modem (?)
uint8_t macpdm_state::via1_in_a()
{
	return 0x00;
}

void macpdm_state::via1_out_a(uint8_t data)
{
}

// bit 7 = ?   - snd res (?)
// bit 5 = out - sys sess/tip
// bit 4 = out - via full/byte ack
// bit 3 = in  - xcvr sess/treq

uint8_t macpdm_state::via1_in_b()
{
	uint8_t val = 0;

	val |= m_cuda->get_treq() << 3;

	return val;
}

void macpdm_state::via1_out_b(uint8_t data)
{
	m_cuda->set_byteack(BIT(data, 4));
	m_cuda->set_tip(BIT(data, 5));
}

TIMER_DEVICE_CALLBACK_MEMBER(macpdm_state::via1_60_15_timer)
{
	m_via1->write_ca1(1);
	m_via1->write_ca1(0);
}

void macpdm_state::via1_out_cb2(int state)
{
	m_cuda->set_via_data(state & 1);
}


void macpdm_state::cuda_reset_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state);
	m_maincpu->set_input_line(INPUT_LINE_RESET, state);
}


uint8_t macpdm_state::via1_r(offs_t offset)
{
	return m_via1->read(offset >> 9);
}

void macpdm_state::via1_w(offs_t offset, uint8_t data)
{
	m_via1->write(offset >> 9, data);
}


uint8_t macpdm_state::via2_ier_r()
{
	return m_via2_ier;
}

void macpdm_state::via2_ier_w(uint8_t data)
{
	if(data & 0x80)
		m_via2_ier |= data & 0x3b;
	else
		m_via2_ier &= ~data;

	logerror("via2 ier %s %s %s %s\n",
			 m_via2_ier & 0x20 ? "fdc" : "-",
			 m_via2_ier & 0x10 ? "sound" : "-",
			 m_via2_ier & 0x08 ? "scsi" : "-",
			 m_via2_ier & 0x02 ? "slot" : "-",
			 m_via2_ier & 0x01 ? "scsidrq" : "-");

	irq_main_set(0x02, (m_via2_ifr & m_via2_ier) != 0);
}

uint8_t macpdm_state::via2_ifr_r()
{
	return m_via2_ifr;
}

uint8_t macpdm_state::via2_sier_r()
{
	return m_via2_sier;
}

void macpdm_state::via2_sier_w(uint8_t data)
{
	if(data & 0x80)
		m_via2_sier |= data & 0x78;
	else
		m_via2_sier &= ~data;

	logerror("via2 sier %s %s %s %s\n",
			 m_via2_sier & 0x40 ? "vbl" : "-",
			 m_via2_sier & 0x20 ? "slot2" : "-",
			 m_via2_sier & 0x10 ? "slot1" : "-",
			 m_via2_sier & 0x08 ? "slot0" : "-");

	via2_irq_main_set(0x02, ((~m_via2_sifr) & m_via2_sier) != 0);
}

uint8_t macpdm_state::via2_sifr_r()
{
	return m_via2_sifr;
}

void macpdm_state::via2_sifr_w(uint8_t data)
{
	if(data & (~m_via2_sifr) & 0x40) {
		m_via2_sifr |= 0x40;
		via2_irq_main_set(0x02, ((~m_via2_sifr) & m_via2_sier) != 0);
	}
}


uint8_t macpdm_state::fdc_r(offs_t offset)
{
	return m_fdc->read(offset >> 9);
}

void macpdm_state::fdc_w(offs_t offset, uint8_t data)
{
	m_fdc->write(offset >> 9, data);
}

uint8_t macpdm_state::scsi_r(offs_t offset)
{
	return m_ncr53c94->read(offset >> 4);
}

void macpdm_state::scsi_w(offs_t offset, uint8_t data)
{
	m_ncr53c94->write(offset >> 4, data);
}

uint8_t macpdm_state::hmc_r(offs_t offset)
{
	const uint8_t rv = (m_hmc_reg >> m_hmc_bit) & 1;
	m_hmc_bit++;
	return rv;
}

void macpdm_state::hmc_w(offs_t offset, uint8_t data)
{
	if(offset & 8)
		m_hmc_bit = 0;
	else {
		if(data & 1)
			m_hmc_buffer |= u64(1) << m_hmc_bit;
		else
			m_hmc_buffer &= ~(u64(1) << m_hmc_bit);
		m_hmc_bit ++;
		if(m_hmc_bit == 35) {
			m_hmc_reg = m_hmc_buffer & ~3; // csiz is readonly, we pretend there isn't a l2 cache
			m_video->set_vram_offset(m_hmc_reg & 0x200000000 ? 0 : 0x100000);
			ram_size();
			logerror("HMC l2=%c%c%c%c%c vbase=%c%s mbram=%cM size=%x%s romd=%d refresh=%02x w=%c%c%c%c ras=%d%d%d%d\n",
					 m_hmc_reg & 0x008000000 ? '+' : '-',      // l2_en
					 m_hmc_reg & 0x400000000 ? '3' : '2',      // l2_init
					 m_hmc_reg & 0x004000000 ? '1' : '2',      // l2_brst
					 m_hmc_reg & 0x010000000 ? 'I' : 'U',      // l2_inst
					 m_hmc_reg & 0x002000000 ? 'w' : '.',      // l2romw
					 m_hmc_reg & 0x200000000 ? '1' : '0',      // vbase
					 m_hmc_reg & 0x100000000 ? " vtst" : "",   // vtst
					 m_hmc_reg & 0x080000000 ? '8' : '4',      // mb_ram
					 (uint32_t)((m_hmc_reg >> 29) & 3),        // size
					 m_hmc_reg & 0x001000000 ? " nblrom" : "", // nblrom
					 (uint32_t)(12 - 2*((m_hmc_reg >> 22) & 3)), // romd
					 (uint32_t)((m_hmc_reg >> 16) & 0x3f),     // rfsh
					 m_hmc_reg & 0x000000008 ? '3' : '2',      // winit
					 m_hmc_reg & 0x000000004 ? '3' : '2',      // wbrst
					 m_hmc_reg & 0x000008000 ? '1' : '2',      // wcasp
					 m_hmc_reg & 0x000004000 ? '1' : '2',      // wcasd
					 (uint32_t)(3 - ((m_hmc_reg >> 12) & 3)),  // rdac
					 (uint32_t)(6 - ((m_hmc_reg >> 8) & 3)),   // rasd
					 (uint32_t)(5 - ((m_hmc_reg >> 6) & 3)),   // rasp
					 (uint32_t)(4 - ((m_hmc_reg >> 4) & 3)));  // rcasd
		}
	}
}

/*
    PDM uses a variant on the V8/Sonora style memory controller.
    - Motherboard RAM can be 4 or 8 MiB
    - The hardware officially limits SIMMs to 2, 8, or 32 MiB, and SIMMs must be installed in pairs
    - In reality, 128 MiB SIMMs will also work.
    - 6100 has only 2 SIMM slots, so valid sizes are 8MiB (no SIMMs), 12MiB (2 MiB SIMM x2),
      24MiB (8 MiB SIMM x2), 72MiB (32 MiB SIMM x2), and 264MiB (128 MiB SIMM x2)
    - 7100 has 4 SIMM slots, allowing RAM up to 520MiB (128 MiB SIMM x4)
    - 8100 has 8 SIMM slots, which add valid sizes up to 264 MiB (32 MiB SIMM x8)
*/
void macpdm_state::ram_size(){
	static const uint32_t sizes[4] = { 128*1024*1024, 2*1024*1024, 8*1024*1024, 32*1024*1024 };
	const u8 config = (m_hmc_reg >> 29) & 3;        // 0 = 128MiB, 1 = 2MiB, 2 = 8MiB, 3 = 32MiB
	const u8 mb_size = (m_hmc_reg & 0x00800000) ? 1 : 0;
	address_space &space = m_maincpu->space(AS_PROGRAM);
	const u32 total_ram = m_ram->size();
	const u32 mb_ram_size = (mb_size ? 8*1024*1024 : 4*1024*1024);

	// SIMMs must be in identical pairs, so any leftover RAM after the motherboard 8MiB is
	// the number of slots times the SIMM size.
	const u32 simm_size = (total_ram - mb_ram_size) / 2;

	u8 *mb_ram = (u8 *)m_ram->pointer();
	u8 *ram_a = mb_ram + mb_ram_size;
	u8 *ram_b = ram_a + simm_size;

	// unmap the first GB of address space (reserved for RAM)
	space.unmap_readwrite(0x00000000, 0x3fffffff);

	// map the motherboard 8MB
	space.install_ram(0x00000000, 0x007fffff, 0, (void *)mb_ram);

	// install RAM A
	if (simm_size > 0)
	{
		if (simm_size <= 8*1024*1024)
		{
			space.install_ram(mb_ram_size, mb_ram_size + simm_size - 1, 0, (void *)ram_a);
		}
		else
		{
			space.install_ram(mb_ram_size, sizes[config] - 1, 0, (void *)(ram_a + mb_ram_size));
		}

		// install a complete image of RAM A in the slot above the top of memory (which is actually where the ROM code looks for it)
		const u32 alias_base = (sizes[config] * 2);
		space.install_ram(alias_base, alias_base + simm_size - 1, 0, (void *)ram_a);

		// install RAM B
		u32 b_base = sizes[config];

		if (simm_size < (128*1024*1024))
		{
			b_base += mb_ram_size;
		}

		space.install_ram(b_base, b_base + simm_size - 1, 0, (void *)ram_b);
	}
}

uint8_t macpdm_state::diag_r(offs_t offset)
{
	// returning 0 at address 0 gives the 'car crash' sound after the boot bong
	logerror("diag_r %x\n", offset);
	return offset ? 0 : 1;
}

void macpdm_state::phases_w(uint8_t phases)
{
	if(m_cur_floppy)
		m_cur_floppy->seek_phase_w(phases);
}

void macpdm_state::sel35_w(int sel35)
{
	logerror("fdc mac sel35 %d\n", sel35);
}

void macpdm_state::devsel_w(uint8_t devsel)
{
	if(devsel == 1)
		m_cur_floppy = m_floppy[0]->get_device();
	else if(devsel == 2)
		m_cur_floppy = m_floppy[1]->get_device();
	else
		m_cur_floppy = nullptr;
	m_fdc->set_floppy(m_cur_floppy);
}

void macpdm_state::hdsel_w(int hdsel)
{
	if(m_cur_floppy)
		m_cur_floppy->ss_w(hdsel);
}

uint32_t macpdm_state::id_r()
{
	return m_model_id;
}

void macpdm_state::scc_irq(int state)
{
	logerror("scc irq %d\n", state);
}

void macpdm_state::via1_irq(int state)
{
	irq_main_set(0x01, state);
}

void macpdm_state::nmi_irq(int state)
{
	irq_main_set(0x20, state);
}

void macpdm_state::recalc_dma_irqs()
{
	if ((m_dma_irq_1 | m_dma_irq_2) != 0)
	{
		irq_main_set(0x10, ASSERT_LINE);
	}
	else
	{
		irq_main_set(0x10, CLEAR_LINE);
	}
}

void macpdm_state::vblank_irq(int state)
{
	via2_irq_slot_set(0x40, state);
}

void macpdm_state::slot2_irq_w(int state)
{
	via2_irq_slot_set(0x20, state);
}

void macpdm_state::slot1_irq_w(int state)
{
	via2_irq_slot_set(0x10, state);
}

void macpdm_state::slot0_irq_w(int state)
{
	via2_irq_slot_set(0x08, state);
}

void macpdm_state::sndo_dma_irq(int state)
{
	m_dma_irq_2 &= ~DMA2_IRQ_SND_OUT;
	m_dma_irq_2 |= state ? DMA2_IRQ_SND_OUT : 0;
	recalc_dma_irqs();
}

void macpdm_state::sndi_dma_irq(int state)
{
	m_dma_irq_2 &= ~DMA2_IRQ_SND_IN;
	m_dma_irq_2 |= state ? DMA2_IRQ_SND_IN : 0;
	recalc_dma_irqs();
}

uint32_t macpdm_state::dma_badr_r()
{
	return m_dma_badr;
}

void macpdm_state::dma_badr_w(offs_t, uint32_t data, uint32_t mem_mask)
{
	COMBINE_DATA(&m_dma_badr);
	m_dma_badr &= 0xfffc0000;

	logerror("dma base address %08x\n", m_dma_badr);

	m_dma_floppy_adr = (m_dma_badr | 0x10000) + (m_dma_floppy_adr & 0xffff);
}

uint16_t macpdm_state::dma_berr_en_r()
{
	return m_dma_berr_en;
}

void macpdm_state::dma_berr_en_w(offs_t, uint16_t data, uint16_t mem_mask)
{
	COMBINE_DATA(&m_dma_berr_en);
	logerror("dma bus error enable %08x\n", m_dma_berr_en);
}

uint16_t macpdm_state::dma_berr_flag_r()
{
	return m_dma_berr_flag;
}

void macpdm_state::dma_berr_flag_w(offs_t, uint16_t data, uint16_t mem_mask)
{
	COMBINE_DATA(&m_dma_berr_flag);
	logerror("dma bus error flag %08x\n", m_dma_berr_flag);
}


// SCSI management

void macpdm_state::dma_scsi_a_step()
{
	m_dma_scsi_a_in_step = true;

	if(m_dma_scsi_a_ctrl & 0x40) {
		while(m_via2_ifr & 0x01) {
			if(m_dma_scsi_buffer_word_count == 0) {
				m_dma_scsi_buffer_word_count = 4;
				m_dma_scsi_buffer = m_maincpu->space().read_qword(m_dma_scsi_a_base_adr + m_dma_scsi_a_cur_offset);
				m_dma_scsi_a_cur_offset += 8;
			}
			m_dma_scsi_buffer_word_count --;
			m_ncr53c94->dma16_swap_w(m_dma_scsi_buffer >> (16*m_dma_scsi_buffer_word_count));
		}

	} else {
		while(m_via2_ifr & 0x01) {
			uint16_t w = m_ncr53c94->dma16_swap_r();
			m_dma_scsi_buffer = (m_dma_scsi_buffer & ~(u64(0xffff) << (48 - 16*m_dma_scsi_buffer_word_count))) | (u64(w) << (48 - 16*m_dma_scsi_buffer_word_count));
			m_dma_scsi_buffer_word_count ++;
			if(m_dma_scsi_buffer_word_count == 4) {
				m_dma_scsi_buffer_word_count = 0;
				m_maincpu->space().write_qword(m_dma_scsi_a_base_adr + m_dma_scsi_a_cur_offset, m_dma_scsi_buffer);
				m_dma_scsi_a_cur_offset += 8;
			}
		}
	}

	m_dma_scsi_a_in_step = false;
}

void macpdm_state::scsi_irq(int state)
{
	via2_irq_main_set(0x08, state);
}

void macpdm_state::scsi_drq(int state)
{
	via2_irq_main_set(0x01, state);
	if((m_dma_scsi_a_ctrl & 0x02) && (m_via2_ifr & 0x01) && !m_dma_scsi_a_in_step)
		dma_scsi_a_step();
}

uint32_t macpdm_state::dma_scsi_a_base_adr_r()
{
	return m_dma_scsi_a_base_adr;
}

void macpdm_state::dma_scsi_a_base_adr_w(offs_t, uint32_t data, uint32_t mem_mask)
{
	COMBINE_DATA(&m_dma_scsi_a_base_adr);
	m_dma_scsi_a_base_adr &= ~7;
	m_dma_scsi_a_cur_offset = 0;
	m_dma_scsi_buffer_word_count = 0;
	logerror("dma_scsi_a_base_adr_w %08x\n", m_dma_scsi_a_base_adr);
}

uint32_t macpdm_state::dma_scsi_b_base_adr_r()
{
	return m_dma_scsi_b_base_adr;
}

void macpdm_state::dma_scsi_b_base_adr_w(offs_t, uint32_t data, uint32_t mem_mask)
{
	COMBINE_DATA(&m_dma_scsi_b_base_adr);
	m_dma_scsi_b_base_adr &= ~7;
	m_dma_scsi_a_cur_offset = 0;
	logerror("dma_scsi_b_base_adr_w %08x\n", m_dma_scsi_b_base_adr);
}

uint8_t macpdm_state::dma_scsi_a_ctrl_r()
{
	return m_dma_scsi_a_ctrl;
}

void macpdm_state::dma_scsi_a_ctrl_w(uint8_t data)
{
	m_dma_scsi_a_ctrl = data & 0x42;
	if(data & 1) {
		m_dma_scsi_a_ctrl &= 0x40;
		m_dma_scsi_a_cur_offset = 0;
		m_dma_scsi_buffer_word_count = 0;
	}
	if(data & 0x10) {
		while(m_via2_ifr & 0x01) {
			uint16_t w = m_ncr53c94->dma16_swap_r();
			m_dma_scsi_buffer = (m_dma_scsi_buffer & ~(u64(0xffff) << (48 - 16*m_dma_scsi_buffer_word_count))) | (u64(w) << (48 - 16*m_dma_scsi_buffer_word_count));
			m_dma_scsi_buffer_word_count ++;
			if(m_dma_scsi_buffer_word_count == 4) {
				m_dma_scsi_buffer_word_count = 0;
				m_maincpu->space().write_qword(m_dma_scsi_a_base_adr + m_dma_scsi_a_cur_offset, m_dma_scsi_buffer);
				m_dma_scsi_a_cur_offset += 8;
			}
		}
		if(m_dma_scsi_buffer_word_count) {
			m_maincpu->space().write_qword(m_dma_scsi_a_base_adr + m_dma_scsi_a_cur_offset, m_dma_scsi_buffer);
			m_dma_scsi_buffer_word_count = 0;
		}
	}

	if((m_dma_scsi_a_ctrl & 0x02) && (m_via2_ifr & 0x01) && !m_dma_scsi_a_in_step)
		dma_scsi_a_step();

	logerror("dma_scsi_a_ctrl_w %02x\n", m_dma_scsi_a_ctrl);
}

uint8_t macpdm_state::dma_scsi_b_ctrl_r()
{
	return m_dma_scsi_b_ctrl;
}

void macpdm_state::dma_scsi_b_ctrl_w(uint8_t data)
{
	// Channel B is not actually connected to anything
	m_dma_scsi_b_ctrl = data & 0x42;
	if(data & 1) {
		m_dma_scsi_b_ctrl &= 0x40;
		m_dma_scsi_b_cur_offset = 0;
	}
	logerror("dma_scsi_b_ctrl_w %02x\n", m_dma_scsi_b_ctrl);
}

uint32_t macpdm_state::dma_scsi_a_cur_adr_r()
{
	return m_dma_scsi_a_base_adr + m_dma_scsi_a_cur_offset;
}

uint32_t macpdm_state::dma_scsi_b_cur_adr_r()
{
	return m_dma_scsi_b_base_adr + m_dma_scsi_b_cur_offset;
}


// Floppy management

uint8_t macpdm_state::dma_floppy_ctrl_r()
{
	return m_dma_floppy_ctrl;
}

void macpdm_state::dma_floppy_ctrl_w(uint8_t data)
{
	m_dma_floppy_ctrl = (m_dma_floppy_ctrl & 0x80) | (data & 0x4a);
	if(data & 0x01) {
		m_dma_floppy_ctrl &= 0x7f;
		m_dma_floppy_offset = 0;
	}

	if(data & 0x80)
		m_dma_floppy_ctrl &= 0x7f;

	logerror("dma floppy ctrl %02x\n", m_dma_floppy_ctrl);
}

uint16_t macpdm_state::dma_floppy_byte_count_r()
{
	return m_dma_floppy_byte_count;
}

void macpdm_state::dma_floppy_byte_count_w(offs_t, uint16_t data, uint16_t mem_mask)
{
	COMBINE_DATA(&m_dma_floppy_byte_count);
	logerror("dma floppy count %04x\n", m_dma_floppy_byte_count);
}

uint32_t macpdm_state::dma_floppy_adr_r()
{
	return m_dma_floppy_adr;
}

void macpdm_state::dma_floppy_adr_w(offs_t, uint32_t data, uint32_t mem_mask)
{
	COMBINE_DATA(&m_dma_floppy_adr);
	m_dma_floppy_adr = (m_dma_badr | 0x10000) + (m_dma_floppy_adr & 0xffff);
	m_dma_floppy_offset = 0;
	logerror("dma floppy adr %08x\n", m_dma_floppy_adr);
}

void macpdm_state::fdc_irq(int state)
{
	via2_irq_main_set(0x20, state);
}

void macpdm_state::dma_floppy_step()
{
	m_dma_floppy_in_step = true;

	if(m_dma_floppy_ctrl & 0x40) {
		fatalerror("floppy dma write\n");

	} else {
		while(m_floppy_drq) {
			u8 r = m_fdc->dma_r();
			m_maincpu->space().write_byte(m_dma_floppy_adr + m_dma_floppy_offset, r);
			m_dma_floppy_offset ++;
			m_dma_floppy_byte_count --;
			//          logerror("dma_w %03x, %02x\n", m_dma_floppy_offset, r);
			if(m_dma_floppy_byte_count == 0) {
				m_dma_floppy_ctrl &= ~0x02;
				m_dma_floppy_ctrl |= 0x80;
				logerror("dma floppy done\n");
				// todo irq dma
				break;
			}
		}
	}

	m_dma_floppy_in_step = false;
}

void macpdm_state::fdc_drq(int state)
{
	m_floppy_drq = state;
	if((m_dma_floppy_ctrl & 0x02) && m_floppy_drq && !m_dma_floppy_in_step)
		dma_floppy_step();
}



// SCC management

uint8_t macpdm_state::dma_scc_txa_ctrl_r()
{
	return m_dma_scc_txa_ctrl;
}

void macpdm_state::dma_scc_txa_ctrl_w(uint8_t data)
{
	m_dma_scc_txa_ctrl = data;
	logerror("dma_scc_txa_ctrl_w %02x\n", m_dma_scc_txa_ctrl);
}

uint8_t macpdm_state::dma_scc_rxa_ctrl_r()
{
	return m_dma_scc_rxa_ctrl;
}

void macpdm_state::dma_scc_rxa_ctrl_w(uint8_t data)
{
	m_dma_scc_rxa_ctrl = data;
	logerror("dma_scc_rxa_ctrl_w %02x\n", m_dma_scc_rxa_ctrl);
}

uint8_t macpdm_state::dma_scc_txb_ctrl_r()
{
	return m_dma_scc_txb_ctrl;
}

void macpdm_state::dma_scc_txb_ctrl_w(uint8_t data)
{
	m_dma_scc_txb_ctrl = data;
	logerror("dma_scc_txb_ctrl_w %02x\n", m_dma_scc_txb_ctrl);
}

uint8_t macpdm_state::dma_scc_rxb_ctrl_r()
{
	return m_dma_scc_rxb_ctrl;
}

void macpdm_state::dma_scc_rxb_ctrl_w(uint8_t data)
{
	m_dma_scc_rxb_ctrl = data;
	logerror("dma_scc_rxb_ctrl_w %02x\n", m_dma_scc_rxb_ctrl);
}

uint8_t macpdm_state::dma_enet_rx_ctrl_r()
{
	return m_dma_enet_rx_ctrl;
}

void macpdm_state::dma_enet_rx_ctrl_w(uint8_t data)
{
	m_dma_enet_rx_ctrl = data;
	logerror("dma_enet_rx_ctrl_w %02x\n", m_dma_enet_rx_ctrl);
}

uint8_t macpdm_state::dma_enet_tx_ctrl_r()
{
	return m_dma_enet_tx_ctrl;
}

void macpdm_state::dma_enet_tx_ctrl_w(uint8_t data)
{
	m_dma_enet_tx_ctrl = data;
	logerror("dma_enet_tx_ctrl_w %02x\n", m_dma_enet_tx_ctrl);
}

uint32_t macpdm_state::sound_dma_output(offs_t offset)
{
	offs_t adr = m_dma_badr + (offset & 0x10000 ? 0x12000 : 0x10000) + 4*(offset & 0x7ff);
	return m_maincpu->space().read_dword(adr);
}

void macpdm_state::sound_dma_input(offs_t offset, uint32_t value)
{
	offs_t adr = m_dma_badr + (offset & 0x10000 ? 0x0e000 : 0x0c000) + 4*(offset & 0x7ff);
	m_maincpu->space().write_dword(adr, value);
}


void macpdm_state::pdm_map(address_map &map)
{
	map(0x40000000, 0x403fffff).rom().region("bootrom", 0).mirror(0x0fc00000);

	map(0x50f00000, 0x50f00000).rw(FUNC(macpdm_state::via1_r), FUNC(macpdm_state::via1_w)).select(0x1e00);
	map(0x50f04000, 0x50f04007).rw(m_scc, FUNC(z80scc_device::dc_ab_r), FUNC(z80scc_device::dc_ab_w)).umask64(0xff00ff00ff00ff00);
	// 50f08000 = ethernet ID PROM
	// 50f0a000 = MACE ethernet controller
	map(0x50f10000, 0x50f10000).rw(FUNC(macpdm_state::scsi_r), FUNC(macpdm_state::scsi_w)).select(0xf0);
	map(0x50f10100, 0x50f10101).rw(m_ncr53c94, FUNC(ncr53c94_device::dma16_swap_r), FUNC(ncr53c94_device::dma16_swap_w));
	map(0x50f14000, 0x50f1401f).rw(m_awacs, FUNC(awacs_device::read), FUNC(awacs_device::write));
	map(0x50f16000, 0x50f16000).rw(FUNC(macpdm_state::fdc_r), FUNC(macpdm_state::fdc_w)).select(0x1e00);

	map(0x50f24000, 0x50f24003).rw(m_video, FUNC(mac_video_sonora_device::dac_r), FUNC(mac_video_sonora_device::dac_w));

	map(0x50f26002, 0x50f26002).rw(FUNC(macpdm_state::via2_sifr_r), FUNC(macpdm_state::via2_sifr_w)).mirror(0x1fe0);
	map(0x50f26003, 0x50f26003).r(FUNC(macpdm_state::via2_ifr_r)).mirror(0x1fe0);
	map(0x50f26012, 0x50f26012).rw(FUNC(macpdm_state::via2_sier_r), FUNC(macpdm_state::via2_sier_w)).mirror(0x1fe0);
	map(0x50f26013, 0x50f26013).rw(FUNC(macpdm_state::via2_ier_r), FUNC(macpdm_state::via2_ier_w)).mirror(0x1fe0);

	map(0x50f28000, 0x50f28007).rw(m_video, FUNC(mac_video_sonora_device::vctrl_r), FUNC(mac_video_sonora_device::vctrl_w));

	map(0x50f2a000, 0x50f2a00f).rw(FUNC(macpdm_state::irq_control_r), FUNC(macpdm_state::irq_control_w));

	map(0x50f2c000, 0x50f2dfff).r(FUNC(macpdm_state::diag_r));

	map(0x50f31000, 0x50f31003).rw(FUNC(macpdm_state::dma_badr_r), FUNC(macpdm_state::dma_badr_w));
	map(0x50f31c20, 0x50f31c20).rw(FUNC(macpdm_state::dma_enet_tx_ctrl_r), FUNC(macpdm_state::dma_enet_tx_ctrl_w));

	map(0x50f32000, 0x50f32003).rw(FUNC(macpdm_state::dma_scsi_a_base_adr_r), FUNC(macpdm_state::dma_scsi_a_base_adr_w));
	map(0x50f32004, 0x50f32007).rw(FUNC(macpdm_state::dma_scsi_b_base_adr_r), FUNC(macpdm_state::dma_scsi_b_base_adr_w));
	map(0x50f32008, 0x50f32008).rw(FUNC(macpdm_state::dma_scsi_a_ctrl_r), FUNC(macpdm_state::dma_scsi_a_ctrl_w));
	map(0x50f32009, 0x50f32009).rw(FUNC(macpdm_state::dma_scsi_b_ctrl_r), FUNC(macpdm_state::dma_scsi_b_ctrl_w));
	map(0x50f32010, 0x50f32013).r(FUNC(macpdm_state::dma_scsi_a_cur_adr_r));
	map(0x50f32014, 0x50f32017).r(FUNC(macpdm_state::dma_scsi_b_cur_adr_r));

	map(0x50f32028, 0x50f32028).rw(FUNC(macpdm_state::dma_enet_rx_ctrl_r), FUNC(macpdm_state::dma_enet_rx_ctrl_w));

	map(0x50f32060, 0x50f32063).rw(FUNC(macpdm_state::dma_floppy_adr_r), FUNC(macpdm_state::dma_floppy_adr_w));
	map(0x50f32064, 0x50f32065).rw(FUNC(macpdm_state::dma_floppy_byte_count_r), FUNC(macpdm_state::dma_floppy_byte_count_w));
	map(0x50f32068, 0x50f32068).rw(FUNC(macpdm_state::dma_floppy_ctrl_r), FUNC(macpdm_state::dma_floppy_ctrl_w));

	map(0x50f32088, 0x50f32088).rw(FUNC(macpdm_state::dma_scc_txa_ctrl_r), FUNC(macpdm_state::dma_scc_txa_ctrl_w));
	map(0x50f32098, 0x50f32098).rw(FUNC(macpdm_state::dma_scc_rxa_ctrl_r), FUNC(macpdm_state::dma_scc_rxa_ctrl_w));
	map(0x50f320a8, 0x50f320a8).rw(FUNC(macpdm_state::dma_scc_txb_ctrl_r), FUNC(macpdm_state::dma_scc_txb_ctrl_w));
	map(0x50f320b8, 0x50f320b8).rw(FUNC(macpdm_state::dma_scc_rxb_ctrl_r), FUNC(macpdm_state::dma_scc_rxb_ctrl_w));

	map(0x50f32100, 0x50f32101).rw(FUNC(macpdm_state::dma_berr_en_r), FUNC(macpdm_state::dma_berr_en_w));
	map(0x50f32102, 0x50f32103).rw(FUNC(macpdm_state::dma_berr_flag_r), FUNC(macpdm_state::dma_berr_flag_w));

	map(0x50f40000, 0x50f4000f).rw(FUNC(macpdm_state::hmc_r), FUNC(macpdm_state::hmc_w));
	map(0x5ffffff8, 0x5fffffff).r(FUNC(macpdm_state::id_r));

	map(0xffc00000, 0xffffffff).rom().region("bootrom", 0);
}

void macpdm_state::macpdm(machine_config &config)
{
	PPC601(config, m_maincpu, 60000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &macpdm_state::pdm_map);
	m_maincpu->ppcdrc_set_options(PPCDRC_COMPATIBLE_OPTIONS);

	MAC_VIDEO_SONORA(config, m_video);
	m_video->set_PDM();
	m_video->screen_vblank().set(FUNC(macpdm_state::vblank_irq));

	SPEAKER(config, "speaker", 2).front();

	AWACS(config, m_awacs, SOUND_CLOCK/2);
	m_awacs->irq_out_cb().set(FUNC(macpdm_state::sndo_dma_irq));
	m_awacs->irq_in_cb().set(FUNC(macpdm_state::sndi_dma_irq));
	m_awacs->dma_output().set(FUNC(macpdm_state::sound_dma_output));
	m_awacs->dma_input().set(FUNC(macpdm_state::sound_dma_input));

	m_awacs->add_route(0, "speaker", 1.0, 0);
	m_awacs->add_route(1, "speaker", 1.0, 1);

	NSCSI_BUS(config, m_scsibus);
	NSCSI_CONNECTOR(config, "scsi:0", default_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:1", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3").option_set("cdrom", NSCSI_CDROM_APPLE).machine_config(
		[](device_t *device)
		{
			device->subdevice<cdda_device>("cdda")->add_route(0, "^^speaker", 1.0, 0);
			device->subdevice<cdda_device>("cdda")->add_route(1, "^^speaker", 1.0, 1);
		});
	NSCSI_CONNECTOR(config, "scsi:4", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr53c94", NCR53C94).machine_config(
		[this] (device_t *device)
		{
			auto &ctrl = downcast<ncr53c94_device &>(*device);
			ctrl.set_clock(ENET_CLOCK/2);
			ctrl.set_busmd(ncr53c94_device::BUSMD_3);
			ctrl.drq_handler_cb().set(*this, FUNC(macpdm_state::scsi_drq));
			ctrl.irq_handler_cb().set(*this, FUNC(macpdm_state::scsi_irq));
		});

	SOFTWARE_LIST(config, "flop_mac35_orig").set_original("mac_flop_orig");
	SOFTWARE_LIST(config, "flop_mac35_clean").set_original("mac_flop_clcracked");
	SOFTWARE_LIST(config, "flop35_list").set_original("mac_flop");
	SOFTWARE_LIST(config, "flop35hd_list").set_original("mac_hdflop");
	SOFTWARE_LIST(config, "hdd_list").set_original("mac_hdd");
	SOFTWARE_LIST(config, "cd_list").set_original("mac_cdrom").set_filter("PPC601");

	SWIM3(config, m_fdc, IO_CLOCK);
	m_fdc->irq_cb().set(FUNC(macpdm_state::fdc_irq));
	m_fdc->drq_cb().set(FUNC(macpdm_state::fdc_drq));
	m_fdc->hdsel_cb().set(FUNC(macpdm_state::hdsel_w));
	m_fdc->devsel_cb().set(FUNC(macpdm_state::devsel_w));
	m_fdc->phases_cb().set(FUNC(macpdm_state::phases_w));
	m_fdc->sel35_cb().set(FUNC(macpdm_state::sel35_w));
	applefdintf_device::add_35_hd(config, m_floppy[0]);
	applefdintf_device::add_35_nc(config, m_floppy[1]);

	// pclk is maincpu:60MHz/4, RTxCA is IO_CLOCK*2/17 or GPI input, RTxCB is IO_CLOCK*2/17
	// IO_CLOCK*2/17 is 3'686'400
	SCC85C30(config, m_scc, 60000000/4);
	m_scc->configure_channels(3'686'400, 3'686'400, 3'686'400, 3'686'400);
	m_scc->out_int_callback().set(FUNC(macpdm_state::scc_irq));
	m_scc->out_txda_callback().set("printer", FUNC(rs232_port_device::write_txd));
	m_scc->out_txdb_callback().set("modem", FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232a(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_scc, FUNC(z80scc_device::rxa_w));
	rs232a.dcd_handler().set(m_scc, FUNC(z80scc_device::dcda_w));
	rs232a.cts_handler().set(m_scc, FUNC(z80scc_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "modem", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_scc, FUNC(z80scc_device::rxb_w));
	rs232b.dcd_handler().set(m_scc, FUNC(z80scc_device::dcdb_w));
	rs232b.cts_handler().set(m_scc, FUNC(z80scc_device::ctsb_w));

	R65NC22(config, m_via1, IO_CLOCK/40);
	m_via1->readpa_handler().set(FUNC(macpdm_state::via1_in_a));
	m_via1->readpb_handler().set(FUNC(macpdm_state::via1_in_b));
	m_via1->writepa_handler().set(FUNC(macpdm_state::via1_out_a));
	m_via1->writepb_handler().set(FUNC(macpdm_state::via1_out_b));
	m_via1->cb2_handler().set(FUNC(macpdm_state::via1_out_cb2));
	m_via1->irq_handler().set(FUNC(macpdm_state::via1_irq));

	RAM(config, m_ram);
	m_ram->set_default_size("8M");
	m_ram->set_extra_options("12M,24M,72M,264M");

	nubus_device &nubus(NUBUS(config, "nubus", 0));
	nubus.set_space(m_maincpu, AS_PROGRAM);
	nubus.out_irqc_callback().set(FUNC(macpdm_state::slot0_irq_w));
	nubus.out_irqd_callback().set(FUNC(macpdm_state::slot1_irq_w));
	nubus.out_irqe_callback().set(FUNC(macpdm_state::slot2_irq_w));

	// 6100 with the NuBus adapter has one slot, slot $E
	NUBUS_SLOT(config, "nbe", "nubus", powermac_nubus_cards, nullptr);

	MACADB(config, m_macadb, IO_CLOCK/2);
	CUDA_V2XX(config, m_cuda, XTAL(32'768));
	m_cuda->set_default_bios_tag("341s0060");
	m_cuda->reset_callback().set(FUNC(macpdm_state::cuda_reset_w));
	m_cuda->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
	m_cuda->via_clock_callback().set(m_via1, FUNC(via6522_device::write_cb1));
	m_cuda->via_data_callback().set(m_via1, FUNC(via6522_device::write_cb2));
	m_cuda->nmi_callback().set(FUNC(macpdm_state::nmi_irq));
	m_macadb->adb_data_callback().set(m_cuda, FUNC(cuda_device::set_adb_line));
	m_macadb->adb_power_callback().set(m_cuda, FUNC(cuda_device::set_adb_power));
	config.set_perfect_quantum(m_maincpu);

	TIMER(config, "beat_60_15").configure_periodic(FUNC(macpdm_state::via1_60_15_timer), attotime::from_double(1/60.15));
}

static INPUT_PORTS_START( macpdm )
INPUT_PORTS_END

ROM_START( pmac6100 )
	ROM_REGION64_BE(0x400000, "bootrom", 0)
	ROM_LOAD( "9feb69b3.rom", 0x000000, 0x400000, CRC(a43fadbc) SHA1(6fac1c4e920a077c077b03902fef9199d5e8f2c3) )
ROM_END

} // anonymous namespace


COMP( 1994, pmac6100,  0, 0, macpdm, macpdm, macpdm_state, driver_init, "Apple Computer", "Power Macintosh 6100/60",  MACHINE_NOT_WORKING )



macprtb.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/****************************************************************************

    macprtb.cpp
    Mac Portable / PowerBook 100 emulation
    By R. Belmont

    These are electrically identical 68000 machines in very different form factors.
    The Mac Portable came in a large, heavy, "luggable" form factor and didn't enjoy
    much success.

    The PowerBook 100 was much smaller and lighter, the result of
    Apple partnering with Sony to redesign the Portable motherboard to fit into
    a much smaller case.  The PowerBook 100 pioneered the modern convention of
    having the pointing device nearest the user with the keyboard pushed back
    towards the hinge.

    These are sort of an intermediate step between the SE and Mac II in terms
    of functional layout: ASC and SWIM are present, but there's only 1 VIA
    (CMDμ G65SC22PE-2, not the "6523" variant normally used in ADB Macs) and an
    M50753 microcontroller "PMU" handles power management, ADB, and clock/PRAM.

    These machines didn't have a power switch, so you can press any key after
    shutting them down and they'll reboot.  No other Apple portables did that.

    VIA connections:
    Port A: 8-bit bidirectional data bus to the PMU
    Port B: 0: PMU REQ
            1: PMU ACK
            2: VIA_TEST
            3: MODEM
            4: N/C
            5: HDSEL (floppy head select)
            6: STEREO
            7: SCC REQ

    CA1: 60 Hz clock
    CA2: 1 second clock from PMU
    CB1: PMU IRQ
    CB2: SCSI IRQ

    PMU (M50753) connections:
    IN port: 0: PMGR_IN0
             1: A/D FILTER
             2: SOUND LATCH
             3: OFF HOOK
             4: ?
             5: Target Disk Mode flag (0 = TDM, 1 for normal boot)
             6: ?
             7: ?

    Port 0: 0: IWM_CNTRL
            1: N/C
            2: HD PWR/
            3: MODEM PWR/
            4: SERIAL PWR/
            5: SOUND PWR/
            6: -5 EN
            7: SYS_PWR/

    Port 1: 0: N/C
            1: Any Key Down
            2: STOP CLK
            3: CHRG ON/
            4: KBD RST/ (resets keyboard M50740)
            5: HICHG
            6: RING DETECT
            7: N/C

    Port 2: bi-directional data bus, connected to VIA port A

    Port 3: 0: RESET/
            1: SYS_RST/
            2: VIA_TEST
            3: SOUND OFF
            4: 1 SEC/
            5: PMINT/
            6: PMACK/
            7: PMREQ/

    Port 4: 0: PMGR_ADB (ADB out)
            1: ADB (ADB in)
            2: DISP BLANK/
            3: MODEM_INS/

    INT1: 60.15 Hz clock
    INT2: RESET

****************************************************************************/

#include "emu.h"

#include "macadb.h"
#include "macscsi.h"
#include "mactoolbox.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/devices.h"
#include "cpu/m68000/m68000.h"
#include "cpu/m6502/m5074x.h"
#include "formats/ap_dsk35.h"
#include "machine/6522via.h"
#include "machine/ram.h"
#include "machine/applefdintf.h"
#include "machine/macseconds.h"
#include "machine/swim1.h"
#include "machine/timer.h"
#include "machine/z80scc.h"
#include "machine/ncr5380.h"
#include "machine/nscsi_bus.h"
#include "sound/asc.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

namespace {
class macportable_state : public driver_device, public device_nvram_interface, public macseconds_interface
{
public:
	macportable_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		device_nvram_interface(mconfig, *this),
		macseconds_interface(),
		m_maincpu(*this, "maincpu"),
		m_pmu(*this, "pmu"),
		m_via1(*this, "via1"),
		m_macadb(*this, "macadb"),
		m_ncr5380(*this, "scsi:7:ncr5380"),
		m_scsihelp(*this, "scsihelp"),
		m_ram(*this, RAM_TAG),
		m_swim(*this, "fdc"),
		m_floppy(*this, "fdc:%d", 0U),
		m_screen(*this, "screen"),
		m_asc(*this, "asc"),
		m_scc(*this, "scc"),
		m_vram(*this, "vram"),
		m_cur_floppy(nullptr),
		m_hdsel(0),
		m_ram_ptr(nullptr),
		m_rom_ptr(nullptr),
		m_ram_mask(0),
		m_ram_size(0),
		m_rom_size(0),
		m_6015_timer(nullptr), m_6015_deassert_timer(nullptr),
		m_via_cycles(0),
		m_via_interrupt(0),
		m_scc_interrupt(0),
		m_asc_interrupt(0),
		m_last_taken_interrupt(-1),
		m_ca1_data(0),
		m_overlay(false),
		m_pmu_blank_display(true),
		m_pmu_to_via(0),
		m_pmu_from_via(0),
		m_pmu_ack(0),
		m_pmu_req(0),
		m_pmu_p0(0x80),
		m_adb_line(1),
		m_adb_akd(0)
	{
	}

	void macprtb(machine_config &config);
	void macprtb_map(address_map &map) ATTR_COLD;

	void init_macprtb();

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual void nvram_default() override;
	virtual bool nvram_read(util::read_stream &file) override;
	virtual bool nvram_write(util::write_stream &file) override;

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	u16 via_r(offs_t offset);
	void via_w(offs_t offset, u16 data, u16 mem_mask);
	u8 via_in_a();
	u8 via_in_b();
	void via_out_a(u8 data);
	void via_out_b(u8 data);
	void field_interrupts();
	void via_irq_w(int state);
	TIMER_CALLBACK_MEMBER(mac_6015_tick);
	TIMER_CALLBACK_MEMBER(mac_6015_untick);

	void phases_w(u8 phases);
	void devsel_w(u8 devsel);

	u16 rom_switch_r(offs_t offset);

	u16 scsi_r(offs_t offset, u16 mem_mask);
	void scsi_w(offs_t offset, u16 data, u16 mem_mask);
	void scsi_berr_w(u8 data);
	u16 scc_r(offs_t offset);
	void scc_w(offs_t offset, u16 data);
	u16 iwm_r(offs_t offset, u16 mem_mask);
	void iwm_w(offs_t offset, u16 data, u16 mem_mask);
	u16 autovector_r(offs_t offset);
	void autovector_w(offs_t offset, u16 data);
	void pmu_p0_w(u8 data);
	u8 pmu_p1_r();
	u8 pmu_data_r();
	void pmu_data_w(u8 data);
	u8 pmu_comms_r();
	void pmu_comms_w(u8 data);
	void set_adb_line(int state);
	void set_adb_anykeydown(int state);
	u8 pmu_adb_r();
	void pmu_adb_w(u8 data);
	u8 pmu_in_r();
	u8 ad_in_r();
	void asc_irq_w(int state);
	void scc_irq_w(int state);
	u16 config_r();

	required_device<m68000_device> m_maincpu;
	required_device<m50753_device> m_pmu;
	required_device<via6522_device> m_via1;
	required_device<macadb_device> m_macadb;
	required_device<ncr53c80_device> m_ncr5380;
	required_device<mac_scsi_helper_device> m_scsihelp;
	required_device<ram_device> m_ram;
	required_device<applefdintf_device> m_swim;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<screen_device> m_screen;
	required_device<asc_device> m_asc;
	required_device<z80scc_device> m_scc;
	required_shared_ptr<u16> m_vram;

	floppy_image_device *m_cur_floppy;
	int m_hdsel;

	u16 *m_ram_ptr, *m_rom_ptr;
	u32 m_ram_mask, m_ram_size, m_rom_size;

	emu_timer *m_6015_timer, *m_6015_deassert_timer;

	s32 m_via_cycles, m_via_interrupt, m_scc_interrupt, m_asc_interrupt, m_last_taken_interrupt;
	s32 m_ca1_data;

	bool m_overlay, m_pmu_blank_display;

	u8 m_pmu_to_via, m_pmu_from_via, m_pmu_ack, m_pmu_req, m_pmu_p0;
	s32 m_adb_line, m_adb_akd;
};

void macportable_state::nvram_default()
{
}

bool macportable_state::nvram_read(util::read_stream &file)
{
	// Unlike Egret and Cuda, this PMU doesn't assume RAM contents
	// are bad on boot.  It checksums the PRAM and RTC and if the checksum
	// passes it doesn't re-initialize.
	u8 nvram[0xc0];
	auto const [err, actual] = read(file, nvram, 0xc0);
	if (!err && (actual == 0xc0))
	{
		for (int i = 0; i < 0xc0; i++)
		{
			m_pmu->space(AS_PROGRAM).write_byte(i, nvram[i]);
		}
		return true;
	}
	return false;
}

bool macportable_state::nvram_write(util::write_stream &file)
{
	u8 nvram[0xc0];
	for (int i = 0; i < 0xc0; i++)
	{
		nvram[i] = m_pmu->space(AS_PROGRAM).read_byte(i);
	}

	auto const [err, actual] = write(file, nvram, 0xc0);
	return !err;
}

u16 macportable_state::scc_r(offs_t offset)
{
	u16 result = m_scc->dc_ab_r(offset);
	return (result << 8) | result;
}

void macportable_state::scc_w(offs_t offset, u16 data)
{
	m_scc->dc_ab_w(offset, data >> 8);
}

u16 macportable_state::iwm_r(offs_t offset, u16 mem_mask)
{
	u16 result = m_swim->read((offset >> 8) & 0xf);
	return (result << 8) | result;
}

void macportable_state::iwm_w(offs_t offset, u16 data, u16 mem_mask)
{
	if (ACCESSING_BITS_0_7)
		m_swim->write((offset >> 8) & 0xf, data & 0xff);
	else
		m_swim->write((offset >> 8) & 0xf, data >> 8);
}

u16 macportable_state::autovector_r(offs_t offset)
{
	return 0;
}

void macportable_state::autovector_w(offs_t offset, u16 data)
{
};

// returns nonzero if no PDS RAM expansion, 0 if present
u16 macportable_state::config_r()
{
	if (m_ram->size() < (6*1024*1024))
	{
		return 0xffff;
	}

	return 0;
}

void macportable_state::asc_irq_w(int state)
{
	m_asc_interrupt = state;
	field_interrupts();
}

void macportable_state::scc_irq_w(int state)
{
	m_scc_interrupt = state;
	field_interrupts();
}

void macportable_state::pmu_p0_w(u8 data)
{
	if ((!BIT(data, 7)) && (BIT(m_pmu_p0, 7)))
	{
		system_time systime;
		machine().current_datetime(systime);
		u32 seconds = get_local_seconds(systime);

		m_pmu->space(AS_PROGRAM).write_byte(0x28, seconds & 0xff);
		m_pmu->space(AS_PROGRAM).write_byte(0x27, (seconds >> 8) & 0xff);
		m_pmu->space(AS_PROGRAM).write_byte(0x26, (seconds >> 16) & 0xff);
		m_pmu->space(AS_PROGRAM).write_byte(0x25, (seconds >> 24) & 0xff);
	}

	m_pmu_p0 = data;
}

u8 macportable_state::pmu_p1_r()
{
	return 0x08 | (m_adb_akd << 1);        // indicate on charger power
}

u8 macportable_state::pmu_data_r()
{
	return m_pmu_from_via;
}

void macportable_state::pmu_data_w(u8 data)
{
	m_pmu_to_via = data;
}

u8 macportable_state::pmu_comms_r()
{
	return (m_pmu_req << 7);
}

void macportable_state::pmu_comms_w(u8 data)
{
	if (!BIT(data, 1))
	{
		address_space &space = m_maincpu->space(AS_PROGRAM);
		const u32 memory_size = std::min((u32)0x3fffff, m_rom_size);
		const u32 memory_end = memory_size - 1;
		offs_t memory_mirror = memory_end & ~(memory_size - 1);
		space.unmap_readwrite(0x00000000, memory_end);
		space.install_rom(0x00000000, memory_end & ~memory_mirror, memory_mirror, m_rom_ptr);
		m_overlay = true;
	}

	m_maincpu->set_input_line(INPUT_LINE_RESET, BIT(data, 1) ? CLEAR_LINE : ASSERT_LINE);

	m_via1->write_ca2(BIT(data, 4)); // 1 second interrupt
	m_via1->write_cb1(BIT(data, 5)); // PMINT/
	m_pmu_ack = BIT(data, 6);
}

void macportable_state::set_adb_line(int state)
{
	m_adb_line = state;
}

void macportable_state::set_adb_anykeydown(int state)
{
	m_adb_akd = state;
}

u8 macportable_state::pmu_adb_r()
{
	return (m_adb_line << 1);
}

void macportable_state::pmu_adb_w(u8 data)
{
	m_macadb->adb_linechange_w((data & 1) ^ 1);

	m_pmu_blank_display = BIT(data, 2) ^ 1;
}

u8 macportable_state::pmu_in_r()
{
	return 0x20;
} // bit 5 is 0 if the Target Disk Mode should be enabled on the PB100

u8 macportable_state::ad_in_r()
{
	return 0xff;
}

void macportable_state::field_interrupts()
{
	int take_interrupt = -1;

	if ((m_scc_interrupt) || (m_asc_interrupt))
	{
		take_interrupt = 2;
	}
	else if (m_via_interrupt)
	{
		take_interrupt = 1;
	}

	if (m_last_taken_interrupt > -1)
	{
		m_maincpu->set_input_line(m_last_taken_interrupt, CLEAR_LINE);
		m_last_taken_interrupt = -1;
	}

	if (take_interrupt > -1)
	{
		m_maincpu->set_input_line(take_interrupt, ASSERT_LINE);
		m_last_taken_interrupt = take_interrupt;
	}
}

void macportable_state::machine_start()
{
	m_ram_ptr = (u16*)m_ram->pointer();
	m_ram_size = m_ram->size()>>1;
	m_ram_mask = m_ram_size - 1;
	m_rom_ptr = (u16*)memregion("bootrom")->base();
	m_rom_size = memregion("bootrom")->bytes();
	m_via_cycles = -50;

	save_item(NAME(m_via_cycles));
	save_item(NAME(m_via_interrupt));
	save_item(NAME(m_scc_interrupt));
	save_item(NAME(m_asc_interrupt));
	save_item(NAME(m_last_taken_interrupt));
	save_item(NAME(m_ca1_data));
	save_item(NAME(m_overlay));
	save_item(NAME(m_pmu_blank_display));
	save_item(NAME(m_pmu_to_via));
	save_item(NAME(m_pmu_from_via));
	save_item(NAME(m_pmu_ack));
	save_item(NAME(m_pmu_req));
	save_item(NAME(m_pmu_p0));
	save_item(NAME(m_adb_line));
	save_item(NAME(m_adb_akd));

	m_6015_timer = timer_alloc(FUNC(macportable_state::mac_6015_tick), this);
	m_6015_deassert_timer = timer_alloc(FUNC(macportable_state::mac_6015_untick), this);
}

void macportable_state::machine_reset()
{
	m_overlay = true;
	m_via_interrupt = m_scc_interrupt = 0;
	m_last_taken_interrupt = -1;
	m_ca1_data = 0;

	m_pmu_ack = 1;

	// start 60.15 Hz timer
	m_6015_timer->adjust(attotime::from_hz(60.15), 0, attotime::from_hz(60.15));

	m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
}

void macportable_state::init_macprtb()
{
}

u32 macportable_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	// is the display enabled?
	if (m_pmu_blank_display)
	{
		bitmap.fill(0, cliprect);
		return 0;
	}

	u16 const *const video_ram = (const u16 *) m_vram.target();

	for (int y = 0; y < 400; y++)
	{
		u32 *const line = &bitmap.pix(y);

		for (int x = 0; x < 640; x += 16)
		{
			u16 const word = video_ram[((y * 640)/16) + ((x/16))];
			for (int b = 0; b < 16; b++)
			{
				line[x + b] = ((word >> (15 - b)) & 0x0001) ? 0 : 0xffffffff;
			}
		}
	}
	return 0;
}

u16 macportable_state::via_r(offs_t offset)
{
	u16 data;

	offset >>= 8;
	offset &= 0x0f;

	data = m_via1->read(offset);

	m_maincpu->adjust_icount(m_via_cycles);

	return (data & 0xff) | (data << 8);
}

void macportable_state::via_w(offs_t offset, u16 data, u16 mem_mask)
{
	offset >>= 8;
	offset &= 0x0f;

	if (ACCESSING_BITS_0_7)
		m_via1->write(offset, data & 0xff);
	if (ACCESSING_BITS_8_15)
		m_via1->write(offset, (data >> 8) & 0xff);

	m_maincpu->adjust_icount(m_via_cycles);
}

void macportable_state::via_irq_w(int state)
{
	m_via_interrupt = state;
	field_interrupts();
}

u16 macportable_state::rom_switch_r(offs_t offset)
{
	// disable the overlay
	if (m_overlay && !machine().side_effects_disabled())
	{
		address_space &space = m_maincpu->space(AS_PROGRAM);
		const u32 memory_end = m_ram->size() - 1;
		void *memory_data = m_ram->pointer();
		offs_t memory_mirror = memory_end & ~memory_end;

		space.install_ram(0x00000000, memory_end & ~memory_mirror, memory_mirror, memory_data);
		m_overlay = false;
	}

	return m_rom_ptr[offset & ((m_rom_size - 1)>>1)];
}

TIMER_CALLBACK_MEMBER(macportable_state::mac_6015_tick)
{
	/* signal VBlank on CA1 input on the VIA */
	m_ca1_data ^= 1;
	m_via1->write_ca1(m_ca1_data);

	m_pmu->set_input_line(m50753_device::M50753_INT1_LINE, ASSERT_LINE);
	m_macadb->portable_update_keyboard();

	m_6015_deassert_timer->adjust(attotime::from_hz(60.15*525), 0);
}

TIMER_CALLBACK_MEMBER(macportable_state::mac_6015_untick)
{
	m_ca1_data ^= 1;
	m_via1->write_ca1(m_ca1_data);
}

u16 macportable_state::scsi_r(offs_t offset, u16 mem_mask)
{
	const int reg = (offset >> 3) & 0xf;
	const bool pseudo_dma = (reg == 6) && (offset >= 0x130);

	return m_scsihelp->read_wrapper(pseudo_dma, reg) << 8;
}

void macportable_state::scsi_w(offs_t offset, u16 data, u16 mem_mask)
{
	const int reg = (offset >> 3) & 0xf;
	const bool pseudo_dma = (reg == 0) && (offset >= 0x100);

	m_scsihelp->write_wrapper(pseudo_dma, reg, data);
}

void macportable_state::scsi_berr_w(u8 data)
{
	m_maincpu->trigger_bus_error();
}

void macportable_state::macprtb_map(address_map &map)
{
	map(0x900000, 0x93ffff).r(FUNC(macportable_state::rom_switch_r)).mirror(0x0c0000);
	map(0xf60000, 0xf6ffff).rw(FUNC(macportable_state::iwm_r), FUNC(macportable_state::iwm_w));
	map(0xf70000, 0xf7ffff).rw(FUNC(macportable_state::via_r), FUNC(macportable_state::via_w));
	map(0xf90000, 0xf9ffff).rw(FUNC(macportable_state::scsi_r), FUNC(macportable_state::scsi_w));
	map(0xfa8000, 0xfaffff).ram().share("vram"); // VRAM
	map(0xfb0000, 0xfbffff).rw(m_asc, FUNC(asc_device::read), FUNC(asc_device::write));
	map(0xfc0000, 0xfcffff).r(FUNC(macportable_state::config_r));
	map(0xfd0000, 0xfdffff).rw(FUNC(macportable_state::scc_r), FUNC(macportable_state::scc_w));
	map(0xfe0000, 0xfe0001).noprw();
	map(0xfffff0, 0xffffff).rw(FUNC(macportable_state::autovector_r), FUNC(macportable_state::autovector_w));
}

u8 macportable_state::via_in_a()
{
	return m_pmu_to_via;
}

u8 macportable_state::via_in_b()
{
	return 0x80 | 0x04 | ((m_pmu_ack & 1)<<1) | m_pmu_req;
}

void macportable_state::via_out_a(u8 data)
{
	m_pmu_from_via = data;
}

void macportable_state::via_out_b(u8 data)
{
	int hdsel = BIT(data, 5);
	if (hdsel != m_hdsel)
	{
		if (m_cur_floppy)
		{
			m_cur_floppy->ss_w(hdsel);
		}
		m_hdsel = hdsel;
	}
	m_pmu_req = (data & 1);
}

void macportable_state::phases_w(u8 phases)
{
	if (m_cur_floppy)
	{
		m_cur_floppy->seek_phase_w(phases);
	}
}

void macportable_state::devsel_w(u8 devsel)
{
	if (devsel == 1)
	{
		m_cur_floppy = m_floppy[0]->get_device();
	}
	else if (devsel == 2)
	{
		m_cur_floppy = m_floppy[1]->get_device();
	}
	else
	{
		m_cur_floppy = nullptr;
	}
	m_swim->set_floppy(m_cur_floppy);
	if (m_cur_floppy)
	{
		m_cur_floppy->ss_w(m_hdsel);
	}
}

static INPUT_PORTS_START( macadb )
INPUT_PORTS_END

void macportable_state::macprtb(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, 15.6672_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &macportable_state::macprtb_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	M50753(config, m_pmu, 3.93216_MHz_XTAL);
	m_pmu->write_p<0>().set(FUNC(macportable_state::pmu_p0_w));
	m_pmu->read_p<1>().set(FUNC(macportable_state::pmu_p1_r));
	m_pmu->read_p<2>().set(FUNC(macportable_state::pmu_data_r));
	m_pmu->write_p<2>().set(FUNC(macportable_state::pmu_data_w));
	m_pmu->set_pullups<2>(0xff); // internal pullup option?
	m_pmu->read_p<3>().set(FUNC(macportable_state::pmu_comms_r));
	m_pmu->write_p<3>().set(FUNC(macportable_state::pmu_comms_w));
	m_pmu->read_p<4>().set(FUNC(macportable_state::pmu_adb_r));
	m_pmu->write_p<4>().set(FUNC(macportable_state::pmu_adb_w));
	m_pmu->set_pullups<4>(0x0f); // external pullups
	m_pmu->read_in_p().set(FUNC(macportable_state::pmu_in_r));
	m_pmu->ad_in<0>().set(FUNC(macportable_state::ad_in_r));
	m_pmu->ad_in<1>().set(FUNC(macportable_state::ad_in_r));

	M50740(config, "kybd", 3.93216_MHz_XTAL).set_disable();

	config.set_perfect_quantum(m_maincpu);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60.15);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(1260));
	m_screen->set_video_attributes(VIDEO_UPDATE_BEFORE_VBLANK);
	m_screen->set_size(700, 480);
	m_screen->set_visarea(0, 639, 0, 399);
	m_screen->set_screen_update(FUNC(macportable_state::screen_update));

	MACADB(config, m_macadb, 15.6672_MHz_XTAL);
	m_macadb->adb_data_callback().set(FUNC(macportable_state::set_adb_line));
	m_macadb->adb_akd_callback().set(FUNC(macportable_state::set_adb_anykeydown));

	SWIM1(config, m_swim, 15.6672_MHz_XTAL);
	m_swim->phases_cb().set(FUNC(macportable_state::phases_w));
	m_swim->devsel_cb().set(FUNC(macportable_state::devsel_w));

	applefdintf_device::add_35_hd(config, m_floppy[0]);
	applefdintf_device::add_35_nc(config, m_floppy[1]);

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:1", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3").option_set("cdrom", NSCSI_CDROM_APPLE).machine_config(
		[](device_t *device)
		{
			device->subdevice<cdda_device>("cdda")->add_route(0, "^^speaker", 1.0, 0);
			device->subdevice<cdda_device>("cdda")->add_route(1, "^^speaker", 1.0, 1);
		});
	NSCSI_CONNECTOR(config, "scsi:4", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", mac_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr5380", NCR53C80).machine_config([this](device_t *device) {
		ncr53c80_device &adapter = downcast<ncr53c80_device &>(*device);
		adapter.irq_handler().set(m_via1, FUNC(r65c22_device::write_cb2));
		adapter.drq_handler().set(m_scsihelp, FUNC(mac_scsi_helper_device::drq_w));
		adapter.drq_handler().append(m_via1, FUNC(r65c22_device::write_ca1));
	});

	MAC_SCSI_HELPER(config, m_scsihelp);
	m_scsihelp->scsi_read_callback().set(m_ncr5380, FUNC(ncr53c80_device::read));
	m_scsihelp->scsi_write_callback().set(m_ncr5380, FUNC(ncr53c80_device::write));
	m_scsihelp->scsi_dma_read_callback().set(m_ncr5380, FUNC(ncr53c80_device::dma_r));
	m_scsihelp->scsi_dma_write_callback().set(m_ncr5380, FUNC(ncr53c80_device::dma_w));
	m_scsihelp->cpu_halt_callback().set_inputline(m_maincpu, INPUT_LINE_HALT);
	m_scsihelp->timeout_error_callback().set(FUNC(macportable_state::scsi_berr_w));

	SCC85C30(config, m_scc, 15.6672_MHz_XTAL /2 );
	m_scc->out_int_callback().set(FUNC(macportable_state::scc_irq_w));

	R65C22(config, m_via1, 15.6672_MHz_XTAL / 20);
	m_via1->readpa_handler().set(FUNC(macportable_state::via_in_a));
	m_via1->readpb_handler().set(FUNC(macportable_state::via_in_b));
	m_via1->writepa_handler().set(FUNC(macportable_state::via_out_a));
	m_via1->writepb_handler().set(FUNC(macportable_state::via_out_b));
	m_via1->irq_handler().set(FUNC(macportable_state::via_irq_w));

	SPEAKER(config, "speaker", 2).front();
	ASC(config, m_asc, 15.6672_MHz_XTAL, asc_device::asc_type::ASC);
	m_asc->irqf_callback().set(FUNC(macportable_state::asc_irq_w));
	m_asc->add_route(0, "speaker", 1.0, 0);
	m_asc->add_route(1, "speaker", 1.0, 1);

	RAM(config, m_ram);
	m_ram->set_default_size("1M");
	m_ram->set_extra_options("2M,4M,5M,6M,7M,8M,9M");

	SOFTWARE_LIST(config, "hdd_list").set_original("mac_hdd");
	SOFTWARE_LIST(config, "cd_list").set_original("mac_cdrom").set_filter("MC68000");
	SOFTWARE_LIST(config, "flop_mac35_orig").set_original("mac_flop_orig");
	SOFTWARE_LIST(config, "flop_mac35_clean").set_original("mac_flop_clcracked");
	SOFTWARE_LIST(config, "flop35_list").set_original("mac_flop");
	SOFTWARE_LIST(config, "flop35hd_list").set_original("mac_hdflop");
}

ROM_START(macprtb)
	ROM_REGION16_BE(0x40000, "bootrom", 0)
	ROM_LOAD16_WORD("93ca3846.rom", 0x000000, 0x040000, CRC(497348f8) SHA1(79b468b33fc53f11e87e2e4b195aac981bf0c0a6))

	ROM_REGION(0x1800, "pmu", 0)
	ROM_LOAD("pmuv1.bin", 0x000000, 0x001800, CRC(01dae148) SHA1(29d2fca7426c31f2b9334832ed3d257974a61bb1))

	ROM_REGION(0xc00, "kybd", 0)
	ROM_LOAD("342s0740-2.12l", 0x000, 0xc00, NO_DUMP)
ROM_END

ROM_START(macpb100)
	ROM_REGION16_BE(0x40000, "bootrom", 0)
	ROM_LOAD16_WORD("96645f9c.rom", 0x000000, 0x040000, CRC(29ac7ee9) SHA1(7f3acf40b1f63612de2314a2e9fcfeafca0711fc))

	ROM_REGION(0x1800, "pmu", 0)
	ROM_LOAD("pmuv1.bin", 0x000000, 0x001800, CRC(01dae148) SHA1(29d2fca7426c31f2b9334832ed3d257974a61bb1))

	ROM_REGION(0xc00, "kybd", 0)
	ROM_LOAD("342s0743-1.u29", 0x000, 0xc00, NO_DUMP)
ROM_END

} // anonymous namespace


COMP(1989, macprtb,  0, 0, macprtb, macadb, macportable_state, init_macprtb, "Apple Computer", "Macintosh Portable", MACHINE_SUPPORTS_SAVE )
COMP(1991, macpb100, 0, 0, macprtb, macadb, macportable_state, init_macprtb, "Apple Computer", "Macintosh PowerBook 100", MACHINE_SUPPORTS_SAVE )



macpwrbk030.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/****************************************************************************

    macpwrbk030.cpp
    Mac PowerBooks with a 68030 CPU and M50753 PMU
    By R. Belmont

    These are basically late-period Mac IIs without NuBus and with
    Egret/Cuda replaced by the PMU.

    Generation 1:
    PowerBook 140: 16 MHz 68030, 2 MiB RAM, no FPU, passive-matrix screen
    PowerBook 145: 140 with 25 MHz 68030
    PowerBook 145B: 140 with 25 MHz 68030 and 4 MiB of RAM standard
    PowerBook 170: 140, with 25 MHz 68030, 68881 FPU, active-matrix screen

    Generation 2: (all models include external monitor support)
    PowerBook 160: 25 MHz 68030, no FPU, passive-matrix screen with 2 bits per pixel grayscale
    PowerBook 180: 33 MHz 68030, 68881 FPU, active-matrix screen with 2 bits per pixel grayscale
    PowerBook 165: 160 with 33 MHz 68030
    PowerBook 165c: 160 with 33 MHz 68030, FPU, color 640x400 display
    PowerBook 180c: 165c with color 640x480 display

    Driver features:
    - Display, audio, floppy, SCSI, and ADB all work.  You can boot compatible System versions
      and run arbitrary software.
    - FPU presence and Jaws/Niagra CPU speed readback are supported so that Gestalt properly
      identifies all models (except the 145B is shown as a 145; Apple documents this as also
      occuring on hardware).
    - 165c/180c use of a VGA GPIO feature bit to determine the correct model is supported.
    - Sleep/suspend and wake-up works on all models.

    Driver TODOs:
    - External video interface on 160/165/165c/180/180c.  Need to make this a slot interface
      because MAME doesn't otherwise support optionally adding a screen.

    ============================================================================
    Technical info

    VIA 1 connections: (PowerBook 140/170, 160/180/180C are similar)
    Port A: 0: VIA_TEST
            1: CPU_ID0
            2: CPU_ID1
            3: MODEM
            4: CPU_ID2
            5: HDSEL (floppy head select)
            6: CPU_ID3
            7: SCC REQ

    Port B: 0: RTC DATA
            1: RTC CLOCK
            2: RTC
            3: LINK SEL
            4: N/C
            5: N/C
            6: N/C
            7: N/C

    CA1: 60 Hz clock
    CA2: 1 second clock
    CB1: PMU IRQ
    CB2: MODEM SND EN

    VIA 2 connections: (VIA2 is a pseudo-VIA inside the "Peripheral Glue" ASIC)
    Port A: bi-directional PMU data bus

    Port B: 0: SV1
            1: PMACK
            2: PMREQ
            3: SV0
            4: SV2
            5: HMMU
            6: N/C
            7: MODEM RESET

    PMU (M50753) connections:
    IN port: 0: BRITE SENSE
             1: A/D BATT     (battery charge level)
             2: SND CNTL
             3: MODEM BUSY
             4: PWR ON       (input, 1 = system power on)
             5: TEMP A/D     (battery temperature)
             6: NICAD SLA
             7: TABLE SEL    (A/D input, some kind of battery state)

    Port 0: 0: SWIM CNTL
            1: SCC CNTL
            2: HD PWR
            3: MODEM PWR
            4: N/C
            5: SOUND PWR
            6: MODEM PWROUT
            7: SYS_PWR

    Port 1: 0: CCFL PWR CNTL
            1: AKD              (input, works like the high bit of $C000 on the Apple II, except includes the modifiers)
            2: STOP CLK
            3: CHRG ON          (input, 1 = charger is on, 7.1.1 Battery applet shows charging symbol)
            4: KBD RST          (output, resets keyboard M50740)
            5: HICHG            (output)
            6: RING DETECT
            7: CHG OFF          (output)

    Port 2: bi-directional data bus, connected to VIA port A

    Port 3: 0: PWR OFF
            1: SYS RST
            2: VIA TEST
            3: SOUND OFF
            4: 1 SEC        (input from Mac Plus RTC/PRAM chip, never read by the PMU program)
            5: PMINT
            6: PMACK
            7: PMREQ

    Port 4: 0: PMGR_ADB (ADB out)
            1: ADB (ADB in)
            2: DISP BLANK
            3: MODEM_INS

    INT1: 60 Hz clock
    INT2: INT2 PULLUP (pulled up and otherwise N/C)

****************************************************************************/

#include "emu.h"

#include "dfac.h"
#include "gsc.h"
#include "macadb.h"
#include "macrtc.h"
#include "macscsi.h"
#include "mactoolbox.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/devices.h"
#include "cpu/m68000/m68030.h"
#include "cpu/m6502/m5074x.h"
#include "machine/6522via.h"
#include "machine/ram.h"
#include "machine/applefdintf.h"
#include "machine/swim1.h"
#include "machine/timer.h"
#include "machine/z80scc.h"
#include "machine/ncr5380.h"
#include "machine/nscsi_bus.h"
#include "machine/pseudovia.h"
#include "bus/nscsi/devices.h"
#include "sound/asc.h"
#include "video/wd90c26.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/ap_dsk35.h"

namespace {
class macpb030_state : public driver_device
{
public:
	macpb030_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_pmu(*this, "pmu"),
		m_via1(*this, "via1"),
		m_pseudovia(*this, "via2"),
		m_macadb(*this, "macadb"),
		m_ncr5380(*this, "scsi:7:ncr5380"),
		m_scsihelp(*this, "scsihelp"),
		m_ram(*this, RAM_TAG),
		m_swim(*this, "fdc"),
		m_floppy(*this, "fdc:%d", 0U),
		m_rtc(*this, "rtc"),
		m_gsc(*this, "gsc"),
		m_screen(*this, "screen"),
		m_palette(*this, "palette"),
		m_asc(*this, "asc"),
		m_dfac(*this, "dfac"),
		m_scc(*this, "scc"),
		m_vram(*this, "vram"),
		m_ext_vram(*this, "ext_vram"),
		m_vga(*this, "vga"),
		m_ram_ptr(nullptr),
		m_rom_ptr(nullptr),
		m_ram_mask(0),
		m_ram_size(0),
		m_rom_size(0),
		m_6015_timer(nullptr), m_6015_deassert_timer(nullptr),
		m_via_interrupt(0),
		m_via2_interrupt(0),
		m_scc_interrupt(0),
		m_last_taken_interrupt(0),
		m_ca1_data(0),
		m_adb_line(0),
		m_overlay(false),
		m_cur_floppy(nullptr),
		m_hdsel(0),
		m_pmu_blank_display(false),
		m_pmu_from_via(0),
		m_pmu_to_via(0),
		m_pmu_ack(0),
		m_pmu_req(0),
		m_pangola_data(0x1e),
		m_ponti_modem_ctl(0),
		m_ponti_snd_ctl(0),
		m_ponti_SPI_SR(0),
		m_ponti_backlight_ctl(0)
	{
	}

	void macpb140(machine_config &config);
	void macpb145(machine_config &config);
	void macpb145b(machine_config &config);
	void macpb160(machine_config &config);
	void macpb165(machine_config &config);
	void macpb165c(machine_config &config);
	void macpb170(machine_config &config);
	void macpb180(machine_config &config);
	void macpb180c(machine_config &config);
	void macpb140_map(address_map &map) ATTR_COLD;
	void macpb160_map(address_map &map) ATTR_COLD;
	void macpb165c_map(address_map &map) ATTR_COLD;

private:
	required_device<m68030_device> m_maincpu;
	required_device<m50753_device> m_pmu;
	required_device<via6522_device> m_via1;
	required_device<pseudovia_device> m_pseudovia;
	required_device<macadb_device> m_macadb;
	required_device<ncr53c80_device> m_ncr5380;
	required_device<mac_scsi_helper_device> m_scsihelp;
	required_device<ram_device> m_ram;
	required_device<applefdintf_device> m_swim;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<rtc3430042_device> m_rtc;
	optional_device<gsc_device> m_gsc;
	optional_device<screen_device> m_screen;
	optional_device<palette_device> m_palette;
	required_device<asc_device> m_asc;
	required_device<dfac_device> m_dfac;
	required_device<z80scc_device> m_scc;
	optional_shared_ptr<u32> m_vram, m_ext_vram;
	optional_device<wd90c26_vga_device> m_vga;

	u32 *m_ram_ptr, *m_rom_ptr;
	u32 m_ram_mask, m_ram_size, m_rom_size;

	emu_timer *m_6015_timer, *m_6015_deassert_timer;

	int m_via_interrupt, m_via2_interrupt, m_scc_interrupt, m_last_taken_interrupt;
	int m_ca1_data;
	int m_adb_line, m_adb_akd;

	bool m_overlay;

	floppy_image_device *m_cur_floppy;
	int m_hdsel;

	bool m_pmu_blank_display;
	u8 m_pmu_from_via, m_pmu_to_via, m_pmu_ack, m_pmu_req;

	u16 m_pangola_data;

	u8 m_ponti_modem_ctl, m_ponti_snd_ctl, m_ponti_SPI_SR, m_ponti_backlight_ctl;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	u32 screen_update_ddc(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	u32 screen_update_vga(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	u16 via_r(offs_t offset);
	void via_w(offs_t offset, u16 data, u16 mem_mask);
	u8 via_in_a();
	u8 via_in_b();
	void via_out_a(u8 data);
	void via_out_b(u8 data);
	void field_interrupts();
	void via_sync();
	void scc_irq_w(int state);
	void via_irq_w(int state);
	u8 via2_r(offs_t offset);
	void via2_w(offs_t offset, u8 data);
	u8 via2_in_a();
	u8 via2_in_b();
	void via2_out_a(u8 data);
	void via2_out_b(u8 data);
	void via2_irq_w(int state);
	TIMER_CALLBACK_MEMBER(mac_6015_tick);
	TIMER_CALLBACK_MEMBER(mac_6015_untick);

	u32 rom_switch_r(offs_t offset);
	u16 scsi_r(offs_t offset, u16 mem_mask);
	void scsi_w(offs_t offset, u16 data, u16 mem_mask);
	u32 scsi_drq_r(offs_t offset, u32 mem_mask = ~0);
	void scsi_drq_w(offs_t offset, u32 data, u32 mem_mask = ~0);
	void scsi_berr_w(u8 data);
	u16 scc_r(offs_t offset);
	void scc_w(offs_t offset, u16 data);
	void phases_w(u8 phases);
	void devsel_w(u8 devsel);
	u16 swim_r(offs_t offset, u16 mem_mask);
	void swim_w(offs_t offset, u16 data, u16 mem_mask);
	u32 buserror_r();

	u32 jaws_r(offs_t offset, u32 mem_mask);
	void jaws_w(offs_t offset, u32 data, u32 mem_mask);
	u32 niagra_r(offs_t offset, u32 mem_mask);
	void niagra_w(offs_t offset, u32 data, u32 mem_mask);

	u16 pangola_r();
	void pangola_w(u16 data);
	u8 pangola_vram_r(offs_t offset);
	void pangola_vram_w(offs_t offset, u8 data);

	u8 ext_video_r(offs_t offset);
	void ext_video_w(offs_t offset, u8 data);

	u8 pmu_p1_r();
	u8 pmu_data_r();
	void pmu_data_w(u8 data);
	u8 pmu_comms_r();
	void pmu_comms_w(u8 data);
	void set_adb_line(int state);
	void set_adb_anykeydown(int state);
	u8 pmu_p4_r();
	void pmu_p4_w(u8 data);
	u8 pmu_in_r();
	u8 battery_r();
	u8 battery2_r();
	u8 battery3_r();
	u8 brightness_r();
};

void macpb030_state::machine_start()
{
	m_ram_ptr = (u32 *)m_ram->pointer();
	m_ram_size = m_ram->size() >> 1;
	m_ram_mask = m_ram_size - 1;
	m_rom_ptr = (u32 *)memregion("bootrom")->base();
	m_rom_size = memregion("bootrom")->bytes();
	m_via_interrupt = m_via2_interrupt = m_scc_interrupt = 0;
	m_last_taken_interrupt = -1;
	m_ca1_data = 0;

	m_6015_timer = timer_alloc(FUNC(macpb030_state::mac_6015_tick), this);
	m_6015_deassert_timer = timer_alloc(FUNC(macpb030_state::mac_6015_untick), this);

	/*
	   HACK-ish: There is an uninitialized variable in the PMU code that can
	   cause the charger connected state to not be checked until after
	   the 68K startup code reads it and makes decisions.  On hardware this
	   likely is ameliorated by the PMU always having power unless the
	   battery goes 100% dead.

	   In normal operation this location counts down and when it reaches zero,
	   the charger and battery states are refreshed.  The variable is then set to
	   either 0x3c or 0x5b depending on battery state.  The important part is that
	   in normal operation it never is allowed to wrap to 0xff, which was happening
	   in MAME due to RAM being initialized to zero.
	*/
	m_pmu->space(AS_PROGRAM).write_byte(0x25, 0x5b);

	m_maincpu->space(AS_PROGRAM).install_write_tap(0x50f14000, 0x50f15fff, "snd_latch_mon", [this](offs_t offset, u32 &data, u32 mem_mask)                                         {
		if (!machine().side_effects_disabled())
		{
			this->m_ponti_snd_ctl |= 0x08;          // indicate sound chip write so power management knows not to sleep
		}
	});

	save_item(NAME(m_via_interrupt));
	save_item(NAME(m_via2_interrupt));
	save_item(NAME(m_scc_interrupt));
	save_item(NAME(m_last_taken_interrupt));
	save_item(NAME(m_ca1_data));
	save_item(NAME(m_adb_line));
	save_item(NAME(m_adb_akd));
	save_item(NAME(m_overlay));
	save_item(NAME(m_hdsel));
	save_item(NAME(m_pmu_blank_display));
	save_item(NAME(m_pmu_from_via));
	save_item(NAME(m_pmu_to_via));
	save_item(NAME(m_pmu_ack));
	save_item(NAME(m_pmu_req));
	save_item(NAME(m_pangola_data));
	save_item(NAME(m_ponti_modem_ctl));
	save_item(NAME(m_ponti_snd_ctl));
	save_item(NAME(m_ponti_SPI_SR));
	save_item(NAME(m_ponti_backlight_ctl));
}

void macpb030_state::machine_reset()
{
	m_overlay = true;
	m_via_interrupt = m_via2_interrupt = m_scc_interrupt = 0;
	m_last_taken_interrupt = -1;
	m_ca1_data = 0;

	m_cur_floppy = nullptr;
	m_hdsel = 0;

	// put ROM mirror at 0
	address_space &space = m_maincpu->space(AS_PROGRAM);
	const u32 memory_size = std::min((u32)0x3fffff, m_rom_size);
	const u32 memory_end = memory_size - 1;
	offs_t memory_mirror = memory_end & ~(memory_size - 1);

	space.unmap_write(0x00000000, memory_end);
	space.install_rom(0x00000000, memory_end & ~memory_mirror, memory_mirror, m_rom_ptr);

	// start 60.15 Hz timer
	m_6015_timer->adjust(attotime::from_hz(60.15), 0, attotime::from_hz(60.15));

	// main cpu shouldn't start until PMU wakes it up
	m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
}

// Jaws memory controller for the PowerBook 140/170 and 145/145B.
u32 macpb030_state::jaws_r(offs_t offset, u32 mem_mask)
{
	switch (offset >> 10)
	{
		case 0x00:      // RAM wait state control
		case 0x04:      // Econo-Mode register
		case 0x06:      // ROM wait state control
		case 0x10:      // RAM bank A config
		case 0x12:      // RAM bank B config
		case 0x14:      // RAM bank C config
		case 0x20:      // CPU power off control
		case 0x22:      // Set CPU clock
		case 0x30:      // Select plain SCC vs. 85C80 "Combo" SCC + 53C80 SCSI
		case 0x32:      // Puts RAM into self-refresh mode
			break;

		case 0x34: // Get CPU clock
			if (m_maincpu->clock() == 25'000'000)
			{
				return 1<<24;
			}
			break;
	}
	return 0;
}

void macpb030_state::jaws_w(offs_t offset, u32 data, u32 mem_mask)
{
	switch (offset >> 10)
	{
		case 0x00:      // RAM wait state control
		case 0x04:      // Econo-Mode register
		case 0x06:      // ROM wait state control
		case 0x10:      // RAM bank A config
		case 0x12:      // RAM bank B config
		case 0x14:      // RAM bank C config
		case 0x22:      // Set CPU clock
		case 0x30:      // Select plain SCC vs. 85C80 "Combo" SCC + 53C80 SCSI
		case 0x32:      // Puts RAM into self-refresh mode
		case 0x34:      // Get CPU clock
			break;

		case 0x20:      // CPU power down control, expects the CPU to reset after a short delay to resume
						// We make that no delay, since we don't care about saving power.
			m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
			m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
			break;
	}
}

// Niagra (Jaws derivative) memory controller for the PowerBook 160/180 and 165c/180c.
u32 macpb030_state::niagra_r(offs_t offset, u32 mem_mask)
{
	switch (offset >> 10)
	{
		case 0x02:      // Video count options
		case 0x22:      // Video accesses through 64
		case 0x24:      // Video accesses through 512
		case 0x26:      // Video accesses through 2K
		case 0x30:      // Enable flash through Niagra
			break;

		case 0x34: // FPU access detected
			return 0xff000000;

		case 0x36: // CPU speed register
			if (m_maincpu->clock() == 25'000'000)
			{
				return 2<<24;
			}
			else if (m_maincpu->clock() == 33'000'000)
			{
				return 3<<24;
			}
			return 0;

		case 0x16:      // "Ponti registers"
			switch ((offset >> 8) & 3)
			{
				case 0:     // SPI modem control
					return m_ponti_modem_ctl << 24;

				case 1:     // Sound control
					return m_ponti_snd_ctl << 24;

				case 2:     // SPI shift register
					return m_ponti_SPI_SR << 24;

				case 3:     // Backlight control
					return m_ponti_backlight_ctl << 24;
			}
			break;

		default:
			return jaws_r(offset, mem_mask);
	}

	return 0;
}

void macpb030_state::niagra_w(offs_t offset, u32 data, u32 mem_mask)
{
	switch (offset >> 10)
	{
		case 0x02:      // Video count options
		case 0x22:      // Video accesses through 64
		case 0x24:      // Video accesses through 512
		case 0x26:      // Video accesses through 2K
		case 0x30:      // Enable flash through Niagra
		case 0x34:      // FPU access detected
		case 0x36:      // CPU speed register
			break;

		case 0x16: // "Ponti registers"
			data >>= 24;
			switch ((offset >> 8) & 3)
			{
			case 0: // SPI modem control
				m_ponti_modem_ctl = data;
				break;

			case 1: // Sound control
				// if the sound latch clear is asserted, clear the latch
				if (BIT(data, 2) && !BIT(m_ponti_snd_ctl, 2))
				{
					m_ponti_snd_ctl &= ~0x08;
				}

				// preserve the value of the sound latch output
				m_ponti_snd_ctl &= 0x08;
				m_ponti_snd_ctl |= data & ~0x08;
				break;

			case 2: // SPI shift register
				m_ponti_SPI_SR = data;
				break;

			case 3: // Backlight control
				m_ponti_backlight_ctl = data;
				break;
			}
			break;

		default:
			jaws_w(offset, data, mem_mask);
			break;
	}
}

/*
    Color PowerBooks used a stock Western Digital SVGA chipset that could drive LCDs,
    but in order to maintain compatibility with all Mac video depths, the Pangola chip was
    inserted in between.  The SVGA chip is always run at 8 bits per pixel, while Pangola
    sits in between and does the necessary conversion between 1, 2, and 4 bpp on the Mac
    end and 8 bpp on QuickDraw's end.  In 8bpp mode it's just a passthrough.
*/
u16 macpb030_state::pangola_r()
{
	return m_pangola_data;
	// TODO: trace pins, 0x13 -> 0x17 -> 0x16 sequence written before waking up VGA core
}

void macpb030_state::pangola_w(u16 data)
{
	m_pangola_data = data;
}

u8 macpb030_state::pangola_vram_r(offs_t offset)
{
	switch ((m_pangola_data >> 5) & 3)
	{
		case 0: // 8 bpp, do the default thing
			break;

		case 1: // 4bpp
			offset <<= 1;
			return (m_vga->mem_linear_r(offset) << 4) | (m_vga->mem_linear_r(offset+1) & 0xf);

		case 2: // 2bpp
			offset <<= 2;
			return (m_vga->mem_linear_r(offset) << 6) |
				   (m_vga->mem_linear_r(offset + 1) & 0x3) << 4 |
				   (m_vga->mem_linear_r(offset + 2) & 0x3) << 2 |
				   (m_vga->mem_linear_r(offset + 3) & 0x3);

		case 3: // 1bpp
			offset <<= 3;
			return (m_vga->mem_linear_r(offset) << 7) |
				   (m_vga->mem_linear_r(offset + 1) & 0x1) << 6 |
				   (m_vga->mem_linear_r(offset + 2) & 0x1) << 5 |
				   (m_vga->mem_linear_r(offset + 3) & 0x1) << 4 |
				   (m_vga->mem_linear_r(offset + 4) & 0x1) << 3 |
				   (m_vga->mem_linear_r(offset + 5) & 0x1) << 2 |
				   (m_vga->mem_linear_r(offset + 6) & 0x1) << 1 |
				   (m_vga->mem_linear_r(offset + 7) & 0x1);
	}

	return m_vga->mem_linear_r(offset);
}

void macpb030_state::pangola_vram_w(offs_t offset, u8 data)
{
	switch ((m_pangola_data >> 5) & 3)
	{
		case 0: // 8 bpp, passthrough
			m_vga->mem_linear_w(offset, data);
			break;

		case 1: // 4bpp
			offset <<= 1;
			m_vga->mem_linear_w(offset, data>>4);
			m_vga->mem_linear_w(offset+1, data & 0xf);
			break;

		case 2: // 2bpp
			offset <<= 2;
			m_vga->mem_linear_w(offset, data >> 6);
			m_vga->mem_linear_w(offset + 1, (data >> 4) & 0x3);
			m_vga->mem_linear_w(offset + 2, (data >> 2) & 0x3);
			m_vga->mem_linear_w(offset + 3, data & 0x3);
			break;

		case 3: // 1bpp
			offset <<= 3;
			m_vga->mem_linear_w(offset, data >> 7);
			m_vga->mem_linear_w(offset + 1, (data >> 6) & 0x1);
			m_vga->mem_linear_w(offset + 2, (data >> 5) & 0x1);
			m_vga->mem_linear_w(offset + 3, (data >> 4) & 0x1);
			m_vga->mem_linear_w(offset + 4, (data >> 3) & 0x1);
			m_vga->mem_linear_w(offset + 5, (data >> 2) & 0x1);
			m_vga->mem_linear_w(offset + 6, (data >> 1) & 0x1);
			m_vga->mem_linear_w(offset + 7, data & 0x1);
			break;
	}
}

/*
    PB160/180 external video (stub for now)
*/
u8 macpb030_state::ext_video_r(offs_t offset)
{
	switch (offset)
	{
		case 4:             // Monitor ID in bits 4-6.  Return 7 (no connection) for now.
			return 0x70;    // No monitor for now
	}

	return 0;
}

void macpb030_state::ext_video_w(offs_t offset, u8 data)
{
	// 0 = DAC color number
	// 1 = DAC color write (write R, then G, then B, like usual)
	// 8 = depth (0=1bpp, 1=2bpp, 2=4bpp, 3=8bpp, 4=16bpp)
	// 60+61 = visible vertical area (LSB in 60, MSB in 61)
}

u8 macpb030_state::pmu_in_r()
{
	// power on, no target disk mode
	return 0x30;
}

u8 macpb030_state::brightness_r()
{
	return 0x7f;
}

// ADC 1 - battery level
u8 macpb030_state::battery_r()
{
	return 0xff;
}

// ADC 5 - battery temperature
u8 macpb030_state::battery2_r()
{
	return 0x40;
}

// ADC 7 - "TABLE SEL" on the schematic
u8 macpb030_state::battery3_r()
{
	return 0x10;
}

void macpb030_state::set_adb_line(int state)
{
	m_adb_line = state;
}

void macpb030_state::set_adb_anykeydown(int state)
{
	m_adb_akd = state;
}

u8 macpb030_state::pmu_p1_r()
{
	if (m_adb_akd)
	{
		return 0x88 | 0x02;
	}

	return 0x88;
}

u8 macpb030_state::pmu_data_r()
{
	return m_pmu_from_via;
}

void macpb030_state::pmu_data_w(u8 data)
{
	m_pmu_to_via = data;
}

u8 macpb030_state::pmu_comms_r()
{
	return (m_pmu_req << 7);
}

void macpb030_state::pmu_comms_w(u8 data)
{
	if (!BIT(data, 1))
	{
		address_space &space = m_maincpu->space(AS_PROGRAM);
		const u32 memory_size = std::min((u32)0x3fffff, m_rom_size);
		const u32 memory_end = memory_size - 1;
		offs_t memory_mirror = memory_end & ~(memory_size - 1);

		space.unmap_write(0x00000000, memory_end);
		space.install_rom(0x00000000, memory_end & ~memory_mirror, memory_mirror, m_rom_ptr);
		m_overlay = true;
	}

	m_maincpu->set_input_line(INPUT_LINE_HALT, BIT(data, 0) ? CLEAR_LINE : ASSERT_LINE);
	m_maincpu->set_input_line(INPUT_LINE_RESET, BIT(data, 1) ? CLEAR_LINE : ASSERT_LINE);

	m_via1->write_cb1(BIT(data, 5) ^ 1);
	if (m_pmu_ack != BIT(data, 6))
	{
		m_pmu_ack = BIT(data, 6);
		machine().scheduler().synchronize();
	}
}

u8 macpb030_state::pmu_p4_r()
{
	return (m_adb_line << 1);
}

void macpb030_state::pmu_p4_w(u8 data)
{
	m_macadb->adb_linechange_w((data & 1) ^ 1);
	m_pmu_blank_display = BIT(data, 2) ^ 1;
	if (m_gsc)
	{
		m_gsc->set_pmu_blank(m_pmu_blank_display);
	}
}

u32 macpb030_state::buserror_r()
{
	m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
	m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
	return 0;
}

u16 macpb030_state::swim_r(offs_t offset, u16 mem_mask)
{
	if (!machine().side_effects_disabled())
	{
		m_maincpu->adjust_icount(-5);
	}

	u16 result = m_swim->read((offset >> 8) & 0xf);
	return result << 8;
}

void macpb030_state::swim_w(offs_t offset, u16 data, u16 mem_mask)
{
	if (ACCESSING_BITS_0_7)
		m_swim->write((offset >> 8) & 0xf, data & 0xff);
	else
		m_swim->write((offset >> 8) & 0xf, data >> 8);
}

u16 macpb030_state::scc_r(offs_t offset)
{
	via_sync();
	const u16 result = m_scc->dc_ab_r(offset);
	return (result << 8) | result;
}

void macpb030_state::scc_w(offs_t offset, u16 data)
{
	m_scc->dc_ab_w(offset, data >> 8);
}

void macpb030_state::field_interrupts()
{
	int take_interrupt = -1;

	if (m_scc_interrupt)
	{
		take_interrupt = 4;
	}
	else if (m_via2_interrupt)
	{
		take_interrupt = 2;
	}
	else if (m_via_interrupt)
	{
		take_interrupt = 1;
	}

	if (m_last_taken_interrupt > -1)
	{
		m_maincpu->set_input_line(m_last_taken_interrupt, CLEAR_LINE);
		m_last_taken_interrupt = -1;
	}

	if (take_interrupt > -1)
	{
		m_maincpu->set_input_line(take_interrupt, ASSERT_LINE);
		m_last_taken_interrupt = take_interrupt;
	}
}

void macpb030_state::via_sync()
{
	// The via runs at 783.36KHz while the main cpu runs at 15MHz or
	// more, so we need to sync the access with the via clock.  Plus
	// the whole access takes half a (via) cycle and ends when synced
	// with the main cpu again.

	// Get the main cpu time
	u64 cycle = m_maincpu->total_cycles();

	// Get the number of the cycle the via is in at that time
	u64 via_cycle = cycle * m_via1->clock() / m_maincpu->clock();

	// The access is going to start at via_cycle+1 and end at
	// via_cycle+1.5, compute what that means in maincpu cycles (the
	// +1 rounds up, since the clocks are too different to ever be
	// synced).
	u64 main_cycle = (via_cycle * 2 + 3) * m_maincpu->clock() / (2 * m_via1->clock()) + 1;

	// Finally adjust the main cpu icount as needed.
	m_maincpu->adjust_icount(-int(main_cycle - cycle));
}

void macpb030_state::phases_w(u8 phases)
{
	if (m_cur_floppy)
		m_cur_floppy->seek_phase_w(phases);
}

void macpb030_state::devsel_w(u8 devsel)
{
	if (devsel == 1)
	{
		m_cur_floppy = m_floppy[0]->get_device();
	}
	else if (devsel == 2)
	{
		m_cur_floppy = m_floppy[1]->get_device();
	}
	else
	{
		m_cur_floppy = nullptr;
	}
	m_swim->set_floppy(m_cur_floppy);
	if (m_cur_floppy)
	{
		m_cur_floppy->ss_w(m_hdsel);
	}
}

u32 macpb030_state::screen_update_ddc(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	// is the display enabled?
	if (m_pmu_blank_display)
	{
		bitmap.fill(1, cliprect);
		return 0;
	}

	u16 const *const video_ram = (const u16 *)m_vram.target();

	for (int y = 0; y < 400; y++)
	{
		u16 *const line = &bitmap.pix(y);

		for (int x = 0; x < 640; x += 16)
		{
			u16 const word = video_ram[((y * 640) / 16) + ((x / 16) ^ 1)];
			for (int b = 0; b < 16; b++)
			{
				line[x + b] = (word >> (15 - b)) & 0x0001;
			}
		}
	}
	return 0;
}

u32 macpb030_state::screen_update_vga(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if (m_pmu_blank_display)
	{
		bitmap.fill(0, cliprect);
		return 0;
	}

	return m_vga->screen_update(screen, bitmap, cliprect);
}

u16 macpb030_state::via_r(offs_t offset)
{
	u16 data;

	offset >>= 8;
	offset &= 0x0f;

	data = m_via1->read(offset);

	if (!machine().side_effects_disabled())
		via_sync();

	return (data & 0xff) | (data << 8);
}

void macpb030_state::via_w(offs_t offset, u16 data, u16 mem_mask)
{
	offset >>= 8;
	offset &= 0x0f;

	if (ACCESSING_BITS_0_7)
		m_via1->write(offset, data & 0xff);
	if (ACCESSING_BITS_8_15)
		m_via1->write(offset, (data >> 8) & 0xff);

	via_sync();
}

void macpb030_state::scc_irq_w(int state)
{
	m_scc_interrupt = state;
	field_interrupts();
}

void macpb030_state::via_irq_w(int state)
{
	m_via_interrupt = state;
	field_interrupts();
}

u8 macpb030_state::via2_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
	{
		via_sync();
	}

	return m_pseudovia->read(offset);
}

void macpb030_state::via2_w(offs_t offset, u8 data)
{
	via_sync();
	m_pseudovia->write(offset, data);
}

void macpb030_state::via2_irq_w(int state)
{
	m_via2_interrupt = state;
	field_interrupts();
}

u32 macpb030_state::rom_switch_r(offs_t offset)
{
	if (m_overlay && !machine().side_effects_disabled())
	{
		address_space& space = m_maincpu->space(AS_PROGRAM);
		const u32 memory_end = m_ram->size() - 1;
		void *memory_data = m_ram->pointer();
		offs_t memory_mirror = memory_end & ~memory_end;

		space.install_ram(0x00000000, memory_end & ~memory_mirror, memory_mirror, memory_data);
		m_overlay = false;
	}

	return m_rom_ptr[offset & ((m_rom_size - 1)>>2)];
}

TIMER_CALLBACK_MEMBER(macpb030_state::mac_6015_tick)
{
	/* signal VBlank on CA1 input on the VIA */
	m_ca1_data ^= 1;
	m_via1->write_ca1(m_ca1_data);

	m_pmu->set_input_line(m50753_device::M50753_INT1_LINE, ASSERT_LINE);
	m_macadb->portable_update_keyboard();

	m_6015_deassert_timer->adjust(attotime::from_hz(60.15 * 525), 0);
}

TIMER_CALLBACK_MEMBER(macpb030_state::mac_6015_untick)
{
	m_ca1_data ^= 1;
	m_via1->write_ca1(m_ca1_data);
}

u16 macpb030_state::scsi_r(offs_t offset, u16 mem_mask)
{
	const int reg = (offset >> 3) & 0xf;
	const bool pseudo_dma = (reg == 6) && (offset >= 0x130);

	return m_scsihelp->read_wrapper(pseudo_dma, reg) << 8;
}

void macpb030_state::scsi_w(offs_t offset, u16 data, u16 mem_mask)
{
	const int reg = (offset >> 3) & 0xf;
	const bool pseudo_dma = (reg == 0) && (offset >= 0x100);

	m_scsihelp->write_wrapper(pseudo_dma, reg, data>>8);
}

u32 macpb030_state::scsi_drq_r(offs_t offset, u32 mem_mask)
{
	switch (mem_mask)
	{
		case 0xff000000:
			return m_scsihelp->read_wrapper(true, 6)<<24;

		case 0xffff0000:
			return (m_scsihelp->read_wrapper(true, 6)<<24) | (m_scsihelp->read_wrapper(true, 6)<<16);

		case 0xffffffff:
			return (m_scsihelp->read_wrapper(true, 6)<<24) | (m_scsihelp->read_wrapper(true, 6)<<16) | (m_scsihelp->read_wrapper(true, 6)<<8) | m_scsihelp->read_wrapper(true, 6);

		default:
			logerror("scsi_drq_r: unknown mem_mask %08x\n", mem_mask);
	}

	return 0;
}

void macpb030_state::scsi_drq_w(offs_t offset, u32 data, u32 mem_mask)
{
	switch (mem_mask)
	{
		case 0xff000000:
			m_scsihelp->write_wrapper(true, 0, data>>24);
			break;

		case 0xffff0000:
			m_scsihelp->write_wrapper(true, 0, data>>24);
			m_scsihelp->write_wrapper(true, 0, data>>16);
			break;

		case 0xffffffff:
			m_scsihelp->write_wrapper(true, 0, data>>24);
			m_scsihelp->write_wrapper(true, 0, data>>16);
			m_scsihelp->write_wrapper(true, 0, data>>8);
			m_scsihelp->write_wrapper(true, 0, data&0xff);
			break;

		default:
			logerror("scsi_drq_w: unknown mem_mask %08x\n", mem_mask);
			break;
	}
}

void macpb030_state::scsi_berr_w(u8 data)
{
	m_maincpu->pulse_input_line(M68K_LINE_BUSERROR, attotime::zero);
}

/***************************************************************************
    ADDRESS MAPS
****************************************************************************/

// ROM detects the "Jaws" ASIC by checking for I/O space mirrored at 0x01000000 boundries
void macpb030_state::macpb140_map(address_map &map)
{
	map(0x40000000, 0x400fffff).r(FUNC(macpb030_state::rom_switch_r)).mirror(0x0ff00000);

	map(0x50000000, 0x50001fff).rw(FUNC(macpb030_state::via_r), FUNC(macpb030_state::via_w)).mirror(0x01f00000);
	map(0x50002000, 0x50003fff).rw(FUNC(macpb030_state::via2_r), FUNC(macpb030_state::via2_w)).mirror(0x01f00000);
	map(0x50004000, 0x50005fff).rw(FUNC(macpb030_state::scc_r), FUNC(macpb030_state::scc_w)).mirror(0x01f00000);
	map(0x50006000, 0x50007fff).rw(FUNC(macpb030_state::scsi_drq_r), FUNC(macpb030_state::scsi_drq_w)).mirror(0x01f00000);
	map(0x50010000, 0x50011fff).rw(FUNC(macpb030_state::scsi_r), FUNC(macpb030_state::scsi_w)).mirror(0x01f00000);
	map(0x50012060, 0x50012063).r(FUNC(macpb030_state::scsi_drq_r)).mirror(0x01f00000);
	map(0x50014000, 0x50015fff).rw(m_asc, FUNC(asc_device::read), FUNC(asc_device::write)).mirror(0x01f00000);
	map(0x50016000, 0x50017fff).rw(FUNC(macpb030_state::swim_r), FUNC(macpb030_state::swim_w)).mirror(0x01f00000);
	map(0x50024000, 0x50027fff).r(FUNC(macpb030_state::buserror_r)).mirror(0x01f00000); // bus error here to make sure we aren't mistaken for another decoder
	map(0x50080000, 0x500bffff).rw(FUNC(macpb030_state::jaws_r), FUNC(macpb030_state::jaws_w)).mirror(0x01f00000);

	// Video uses the mirror at fee08000, but the Power Manager stashes some sleep data in the
	// lower 32K, so this *must* be mirrored
	map(0xfee00000, 0xfee07fff).ram().share("vram").mirror(0x00008000);
}

void macpb030_state::macpb160_map(address_map &map)
{
	map(0x40000000, 0x400fffff).r(FUNC(macpb030_state::rom_switch_r)).mirror(0x0ff00000);

	map(0x50000000, 0x6fffffff).m(m_gsc, FUNC(gsc_device::map));

	map(0x50f00000, 0x50f01fff).rw(FUNC(macpb030_state::via_r), FUNC(macpb030_state::via_w));
	map(0x50f02000, 0x50f03fff).rw(FUNC(macpb030_state::via2_r), FUNC(macpb030_state::via2_w));
	map(0x50f04000, 0x50f05fff).rw(FUNC(macpb030_state::scc_r), FUNC(macpb030_state::scc_w));
	map(0x50f06000, 0x50f07fff).rw(FUNC(macpb030_state::scsi_drq_r), FUNC(macpb030_state::scsi_drq_w));
	map(0x50f10000, 0x50f11fff).rw(FUNC(macpb030_state::scsi_r), FUNC(macpb030_state::scsi_w));
	map(0x50f12060, 0x50f12063).r(FUNC(macpb030_state::scsi_drq_r));
	map(0x50f14000, 0x50f15fff).rw(m_asc, FUNC(asc_device::read), FUNC(asc_device::write));
	map(0x50f16000, 0x50f17fff).rw(FUNC(macpb030_state::swim_r), FUNC(macpb030_state::swim_w));
	map(0x50f24000, 0x50f27fff).r(FUNC(macpb030_state::buserror_r)); // bus error here to make sure we aren't mistaken for another decoder
	map(0x50f80000, 0x50fbffff).rw(FUNC(macpb030_state::niagra_r), FUNC(macpb030_state::niagra_w));

	// external video on 160/180
	map(0xfe0fe000, 0xfe0fe0ff).rw(FUNC(macpb030_state::ext_video_r), FUNC(macpb030_state::ext_video_w));
	map(0xfe100000, 0xfe17ffff).ram().share("ext_vram");
}

void macpb030_state::macpb165c_map(address_map &map)
{
	map(0x40000000, 0x400fffff).r(FUNC(macpb030_state::rom_switch_r)).mirror(0x0ff00000);

	map(0x50f00000, 0x50f01fff).rw(FUNC(macpb030_state::via_r), FUNC(macpb030_state::via_w));
	map(0x50f02000, 0x50f03fff).rw(FUNC(macpb030_state::via2_r), FUNC(macpb030_state::via2_w));
	map(0x50f04000, 0x50f05fff).rw(FUNC(macpb030_state::scc_r), FUNC(macpb030_state::scc_w));
	map(0x50f06000, 0x50f07fff).rw(FUNC(macpb030_state::scsi_drq_r), FUNC(macpb030_state::scsi_drq_w));
	map(0x50f10000, 0x50f11fff).rw(FUNC(macpb030_state::scsi_r), FUNC(macpb030_state::scsi_w));
	map(0x50f12060, 0x50f12063).r(FUNC(macpb030_state::scsi_drq_r));
	map(0x50f14000, 0x50f15fff).rw(m_asc, FUNC(asc_device::read), FUNC(asc_device::write));
	map(0x50f16000, 0x50f17fff).rw(FUNC(macpb030_state::swim_r), FUNC(macpb030_state::swim_w));
	map(0x50f20000, 0x50f21fff).r(FUNC(macpb030_state::buserror_r)); // bus error here to detect we're not the grayscale 160/165/180
	map(0x50f24000, 0x50f27fff).r(FUNC(macpb030_state::buserror_r)); // bus error here to make sure we aren't mistaken for another decoder
	map(0x50f80000, 0x50fbffff).rw(FUNC(macpb030_state::niagra_r), FUNC(macpb030_state::niagra_w));

	// on-board color video on 165c/180c, presumably under ISA bus
	map(0xfc000000, 0xfc07ffff).rw(FUNC(macpb030_state::pangola_vram_r), FUNC(macpb030_state::pangola_vram_w)).mirror(0x00380000);
	//map(0xfc400102, 0xfc400102).w(wd90c26_vga_device::wakeup_w));
	map(0xfc4003b0, 0xfc4003df).m(m_vga, FUNC(wd90c26_vga_device::io_map));
	// TODO: trace $3d0 writes (doesn't belong to WD90C26 core, RAMDAC overlay?)
	map(0xfc4046e8, 0xfc4046e8).mirror(0x3000).w(m_vga, FUNC(wd90c26_vga_device::mode_setup_w));

	map(0xfc800000, 0xfc800003).rw(FUNC(macpb030_state::pangola_r), FUNC(macpb030_state::pangola_w));
	map(0xfcff8000, 0xfcffffff).rom().region("vrom", 0x0000);

	// external video on 165c/180c
	map(0xfe0fe000, 0xfe0fe0ff).rw(FUNC(macpb030_state::ext_video_r), FUNC(macpb030_state::ext_video_w));
	map(0xfe100000, 0xfe17ffff).ram().share("ext_vram");
}

u8 macpb030_state::via_in_a()
{
	return 0x81 | 0x12; // ID for 140/160
}

u8 macpb030_state::via_in_b()
{
	return 0x08 | m_rtc->data_r();    // flag indicating no Target Disk Mode
}

void macpb030_state::via_out_a(u8 data)
{
	int hdsel = BIT(data, 5);
	if (hdsel != m_hdsel)
	{
		if (m_cur_floppy)
		{
			m_cur_floppy->ss_w(hdsel);
		}
	}
	m_hdsel = hdsel;
}

void macpb030_state::via_out_b(u8 data)
{
	m_rtc->ce_w(BIT(data, 2));
	m_rtc->data_w(BIT(data, 0));
	m_rtc->clk_w(BIT(data, 1));
}

u8 macpb030_state::via2_in_a()
{
	return m_pmu_to_via;
}

u8 macpb030_state::via2_in_b()
{
	// Must also return the pmu_req state here or bset/bclr operations on other
	// bits in this port will accidentally clear pmu_req and cause CPU/PMU comms
	// problems!  The ROM code for sleeping on all of these machines does that.
	return ((m_pmu_ack & 1) << 1) | (m_pmu_req << 2);
}

void macpb030_state::via2_out_a(u8 data)
{
	m_pmu_from_via = data;
}

void macpb030_state::via2_out_b(u8 data)
{
	if (m_pmu_req != BIT(data, 2))
	{
		m_pmu_req = BIT(data, 2);
		machine().scheduler().synchronize();
	}

	m_dfac->data_write(BIT(data, 3));
	m_dfac->clock_write(BIT(data, 4));
	m_dfac->latch_write(BIT(data, 0));
}

static INPUT_PORTS_START( macadb )
INPUT_PORTS_END

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

void macpb030_state::macpb140(machine_config &config)
{
	M68030(config, m_maincpu, 31.3344_MHz_XTAL/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &macpb030_state::macpb140_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");
	m_maincpu->set_fpu_enable(false);

	M50753(config, m_pmu, 3.93216_MHz_XTAL);
	m_pmu->read_p<1>().set(FUNC(macpb030_state::pmu_p1_r));
	m_pmu->read_p<2>().set(FUNC(macpb030_state::pmu_data_r));
	m_pmu->write_p<2>().set(FUNC(macpb030_state::pmu_data_w));
	m_pmu->read_p<3>().set(FUNC(macpb030_state::pmu_comms_r));
	m_pmu->write_p<3>().set(FUNC(macpb030_state::pmu_comms_w));
	m_pmu->read_p<4>().set(FUNC(macpb030_state::pmu_p4_r));
	m_pmu->write_p<4>().set(FUNC(macpb030_state::pmu_p4_w));
	m_pmu->read_in_p().set(FUNC(macpb030_state::pmu_in_r));
	m_pmu->ad_in<0>().set(FUNC(macpb030_state::brightness_r));
	m_pmu->ad_in<1>().set(FUNC(macpb030_state::battery_r));
	m_pmu->ad_in<5>().set(FUNC(macpb030_state::battery2_r));
	m_pmu->ad_in<7>().set(FUNC(macpb030_state::battery3_r));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60.15);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(1260));
	m_screen->set_video_attributes(VIDEO_UPDATE_BEFORE_VBLANK);
	m_screen->set_size(700, 480);
	m_screen->set_visarea(0, 639, 0, 399);
	m_screen->set_palette(m_palette);
	m_screen->set_screen_update(FUNC(macpb030_state::screen_update_ddc));

	PALETTE(config, m_palette, palette_device::MONOCHROME_INVERTED);

	MACADB(config, m_macadb, 31.3344_MHz_XTAL/2);
	m_macadb->adb_data_callback().set(FUNC(macpb030_state::set_adb_line));
	m_macadb->adb_akd_callback().set(FUNC(macpb030_state::set_adb_anykeydown));

	RTC3430042(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->cko_cb().set(m_via1, FUNC(via6522_device::write_ca2));

	SWIM1(config, m_swim, 31.3344_MHz_XTAL / 2);
	m_swim->phases_cb().set(FUNC(macpb030_state::phases_w));
	m_swim->devsel_cb().set(FUNC(macpb030_state::devsel_w));

	applefdintf_device::add_35_hd(config, m_floppy[0]);
	applefdintf_device::add_35_nc(config, m_floppy[1]);

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", mac_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:1", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3").option_set("cdrom", NSCSI_CDROM_APPLE).machine_config(
		[](device_t *device)
		{
			device->subdevice<cdda_device>("cdda")->add_route(0, "^^speaker", 1.0, 0);
			device->subdevice<cdda_device>("cdda")->add_route(1, "^^speaker", 1.0, 1);
		});
	NSCSI_CONNECTOR(config, "scsi:4", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr5380", NCR53C80).machine_config([this](device_t *device) {
		ncr53c80_device &adapter = downcast<ncr53c80_device &>(*device);
		adapter.irq_handler().set(m_pseudovia, FUNC(pseudovia_device::scsi_irq_w));
		adapter.drq_handler().set(m_scsihelp, FUNC(mac_scsi_helper_device::drq_w));
	});

	MAC_SCSI_HELPER(config, m_scsihelp);
	m_scsihelp->scsi_read_callback().set(m_ncr5380, FUNC(ncr53c80_device::read));
	m_scsihelp->scsi_write_callback().set(m_ncr5380, FUNC(ncr53c80_device::write));
	m_scsihelp->scsi_dma_read_callback().set(m_ncr5380, FUNC(ncr53c80_device::dma_r));
	m_scsihelp->scsi_dma_write_callback().set(m_ncr5380, FUNC(ncr53c80_device::dma_w));
	m_scsihelp->cpu_halt_callback().set_inputline(m_maincpu, INPUT_LINE_HALT);
	m_scsihelp->timeout_error_callback().set(FUNC(macpb030_state::scsi_berr_w));

	SCC85C30(config, m_scc, 31.3344_MHz_XTAL / 4);
	m_scc->out_int_callback().set(FUNC(macpb030_state::scc_irq_w));

	R65C22(config, m_via1, 31.3344_MHz_XTAL / 20);
	m_via1->readpa_handler().set(FUNC(macpb030_state::via_in_a));
	m_via1->readpb_handler().set(FUNC(macpb030_state::via_in_b));
	m_via1->writepa_handler().set(FUNC(macpb030_state::via_out_a));
	m_via1->writepb_handler().set(FUNC(macpb030_state::via_out_b));
	m_via1->irq_handler().set(FUNC(macpb030_state::via_irq_w));

	APPLE_PSEUDOVIA(config, m_pseudovia, 31.3344_MHz_XTAL / 20);
	m_pseudovia->readpa_handler().set(FUNC(macpb030_state::via2_in_a));
	m_pseudovia->readpb_handler().set(FUNC(macpb030_state::via2_in_b));
	m_pseudovia->writepa_handler().set(FUNC(macpb030_state::via2_out_a));
	m_pseudovia->writepb_handler().set(FUNC(macpb030_state::via2_out_b));
	m_pseudovia->irq_callback().set(FUNC(macpb030_state::via2_irq_w));

	// Like the Quadra 700, DFAC is only for audio input on these machines
	APPLE_DFAC(config, m_dfac, 22257);

	SPEAKER(config, "speaker", 2).front();
	ASC(config, m_asc, 22.5792_MHz_XTAL, asc_device::asc_type::EASC);
	m_asc->irqf_callback().set(m_pseudovia, FUNC(pseudovia_device::asc_irq_w));
	m_asc->add_route(0, "speaker", 1.0, 0);
	m_asc->add_route(1, "speaker", 1.0, 1);

	RAM(config, m_ram);
	m_ram->set_default_size("2M");
	m_ram->set_extra_options("4M,6M,8M");

	SOFTWARE_LIST(config, "hdd_list").set_original("mac_hdd");
	SOFTWARE_LIST(config, "cd_list").set_original("mac_cdrom").set_filter("MC68030,MC68030_32");
	SOFTWARE_LIST(config, "flop_mac35_orig").set_original("mac_flop_orig");
	SOFTWARE_LIST(config, "flop_mac35_clean").set_original("mac_flop_clcracked");
	SOFTWARE_LIST(config, "flop35_list").set_original("mac_flop");
	SOFTWARE_LIST(config, "flop35hd_list").set_original("mac_hdflop");
}

// PowerBook 145 = 140 @ 25 MHz (still 2MB RAM)
void macpb030_state::macpb145(machine_config &config)
{
	macpb140(config);
	m_maincpu->set_clock(25_MHz_XTAL);
}

// PowerBook 145B = 140 @ 25 MHz with 4MB RAM
void macpb030_state::macpb145b(machine_config &config)
{
	macpb145(config);
	m_ram->set_default_size("4M");
	m_ram->set_extra_options("6M,8M");
}

// PowerBook 170 = 140 @ 25 MHz with an active-matrix LCD (140/145/145B were passive)
void macpb030_state::macpb170(machine_config &config)
{
	macpb140(config);
	m_maincpu->set_clock(25_MHz_XTAL);
	m_maincpu->set_fpu_enable(true);

	m_ram->set_default_size("4M");
	m_ram->set_extra_options("6M,8M");
}

void macpb030_state::macpb160(machine_config &config)
{
	macpb140(config);
	m_maincpu->set_clock(25_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &macpb030_state::macpb160_map);
	m_maincpu->set_fpu_enable(false);

	config.device_remove("screen");
	config.device_remove("palette");

	GSC(config, m_gsc, 31.3344_MHz_XTAL);
	m_gsc->set_panel_id(5);

	m_ram->set_extra_options("4M,6M,8M,12M,14M");
}

void macpb030_state::macpb165(machine_config &config)
{
	macpb160(config);
	m_maincpu->set_clock(33_MHz_XTAL);
}

void macpb030_state::macpb180(machine_config &config)
{
	macpb160(config);
	m_maincpu->set_clock(33_MHz_XTAL);
	m_maincpu->set_fpu_enable(true);
}

void macpb030_state::macpb165c(machine_config &config)
{
	macpb140(config);
	m_maincpu->set_clock(33_MHz_XTAL);
	m_maincpu->set_fpu_enable(true);
	m_maincpu->set_addrmap(AS_PROGRAM, &macpb030_state::macpb165c_map);

	m_screen->set_raw(25.175_MHz_XTAL, 800, 0, 640, 524, 0, 480);
	m_screen->set_screen_update(FUNC(macpb030_state::screen_update_vga));
	m_screen->set_no_palette();

	WD90C26(config, m_vga, 0);
	m_vga->set_screen(m_screen);
	// 512KB
	m_vga->set_vram_size(0x80000);
	// model ID: 0 = 180c, 1 = 165c
	m_vga->read_cnf15_callback().set_constant(1);
}

void macpb030_state::macpb180c(machine_config &config)
{
	macpb165c(config);
	m_vga->read_cnf15_callback().set_constant(0);
}

ROM_START(macpb140)
	ROM_REGION32_BE(0x100000, "bootrom", 0)
	ROM_LOAD("420dbff3.rom", 0x000000, 0x100000, CRC(88ea2081) SHA1(7a8ee468d16e64f2ad10cb8d1a45e6f07cc9e212))

	ROM_REGION(0x1800, "pmu", 0)
	ROM_LOAD("pmuv2.bin", 0x000000, 0x001800, CRC(1a32b5e5) SHA1(7c096324763cfc8d2024893b3e8493b7729b3a92))
ROM_END

#define rom_macpb145 rom_macpb140
#define rom_macpb145b rom_macpb140
#define rom_macpb170 rom_macpb140

ROM_START(macpb160)
	ROM_REGION32_BE(0x100000, "bootrom", 0)
	ROM_LOAD("e33b2724.rom", 0x000000, 0x100000, CRC(536c60f4) SHA1(c0510682ae6d973652d7e17f3c3b27629c47afac))

	ROM_REGION(0x1800, "pmu", 0)
	ROM_LOAD("pmuv3.bin", 0x000000, 0x001800, CRC(f2df696c) SHA1(fc312cbfd407c6f0248c6463910e41ad6b5b0daa))
ROM_END

#define rom_macpb165 rom_macpb160
#define rom_macpb180 rom_macpb160

ROM_START(macpb180c)
	ROM_REGION32_BE(0x100000, "bootrom", 0)
	ROM_LOAD("e33b2724.rom", 0x000000, 0x100000, CRC(536c60f4) SHA1(c0510682ae6d973652d7e17f3c3b27629c47afac))

	ROM_REGION32_BE(0x8000, "vrom", 0)
	ROM_LOAD("pb180cvrom.bin", 0x0000, 0x8000, CRC(810c75ad) SHA1(3a936e97dee5ceeb25e50197ef504e514ae689a4))

	ROM_REGION(0x1800, "pmu", 0)
	ROM_LOAD("pmuv3.bin", 0x000000, 0x001800, CRC(f2df696c) SHA1(fc312cbfd407c6f0248c6463910e41ad6b5b0daa))
ROM_END

#define rom_macpb165c rom_macpb180c

} // anonymous namespace


COMP(1991, macpb140, 0, 0, macpb140, macadb, macpb030_state, empty_init, "Apple Computer", "Macintosh PowerBook 140", MACHINE_SUPPORTS_SAVE)
COMP(1991, macpb170, macpb140, 0, macpb170, macadb, macpb030_state, empty_init, "Apple Computer", "Macintosh PowerBook 170", MACHINE_SUPPORTS_SAVE)
COMP(1992, macpb145, macpb140, 0, macpb145, macadb, macpb030_state, empty_init, "Apple Computer", "Macintosh PowerBook 145", MACHINE_SUPPORTS_SAVE)
COMP(1993, macpb145b, macpb140, 0, macpb145b, macadb, macpb030_state, empty_init, "Apple Computer", "Macintosh PowerBook 145B", MACHINE_SUPPORTS_SAVE)
COMP(1992, macpb160, 0, 0, macpb160, macadb, macpb030_state, empty_init, "Apple Computer", "Macintosh PowerBook 160", MACHINE_SUPPORTS_SAVE)
COMP(1993, macpb165, macpb160, 0, macpb165, macadb, macpb030_state, empty_init, "Apple Computer", "Macintosh PowerBook 165", MACHINE_SUPPORTS_SAVE)
COMP(1993, macpb165c, macpb180c, 0, macpb165c, macadb, macpb030_state, empty_init, "Apple Computer", "Macintosh PowerBook 165c", MACHINE_SUPPORTS_SAVE)
COMP(1992, macpb180, macpb160, 0, macpb180, macadb, macpb030_state, empty_init, "Apple Computer", "Macintosh PowerBook 180", MACHINE_SUPPORTS_SAVE)
COMP(1993, macpb180c, 0, 0, macpb180c, macadb, macpb030_state, empty_init, "Apple Computer", "Macintosh PowerBook 180c", MACHINE_SUPPORTS_SAVE)



macpwrbkmsc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/****************************************************************************

    macpwrbkmsc.cpp
    68K Mac PowerBooks based on the MSC/MSC II system ASICs and the PG&E power manager
    By R. Belmont

    Supported machines:
    PowerBook Duo 210:  68030 @ 25 MHz, 640x400 passive-matrix 4bpp grayscale screen, 4 MiB RAM (24 max)
    PowerBook Duo 230:  68030 @ 33 MHz, 640x400 passive-matrix 4bpp grayscale screen, 4 MiB RAM (24 max)
    PowerBook Duo 250:  68030 @ 33 MHz, 640x400 active-matrix 4bpp grayscale screen, 4 MiB RAM (24 max)
    PowerBook Duo 270c: 68030 @ 33 MHz, FPU, 640x480 active-matrix 16bpp color screen, 4 MiB RAM (32 max)
    PowerBook Duo 280:  68040 @ 33 MHz, 640x480 active-matrix 4bpp grayscale screen, 4 MiB RAM (40 max)
    PowerBook Duo 280c: 68040 @ 33 MHz, 640x480 active-matrix 16bpp color screen, 4 MiB RAM (40 max)

    Future:
    PowerBook 150: '030 @ 33 MHz, 640x480 grayscale screen, 4 MiB RAM (40 max), IDE HDD, ADB trackpad, PG&E matrix keyboard

    ============================================================================
    Technical info

    Pseudo-VIA2 Port B bits 1 and 2 are /PMU_ACK and /PMU_REQ, respectively.
    Main PMU comms are through the VIA shifter, but using a hardware SPI block
    on the PG&E end instead of the 68HC05 losing cycles doing bit-banging.

    Brightness: PLM 1 7F (all the way down) to 26 (all the way up)
                PLM 2 01  "   "   "   "     to 5A
                Total of the 2 PLM timers is always 0x80.  Timer 1 is off period, timer 2 is on period.

    PWM A0 - charging current control
    PWM B0 - screen contrast, 0x33 to 0xc5 range

    Temperature sensors read on a non-linear scale, probably a commercial part.
    Here are selected points from the lookup table the 68HC05 uses to convert it.

    Sensor val  Temperature (Celsius)
    ---------------------------------
    0 - 9:      invalid (high)
    10          115
    20          92
    30          78
    40          68
    50          60
    60          54
    70          49
    80          44
    90          39
    100         35
    110         31
    120         28
    130         24
    140         21
    150         17
    160         14
    170         10
    180         6
    190         2
    195 & 196   0
    200         -2
    210         -7
    220         -12
    230         -19
    240         -28
    250         -48
    252         -55
    253+        invalid (low)
****************************************************************************/

#include "emu.h"

#include "csc.h"
#include "dfac.h"
#include "gsc.h"
#include "macscsi.h"
#include "mactoolbox.h"
#include "msc.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/devices.h"
#include "bus/nubus/nubus.h"
#include "bus/nubus/pwrbkduo/pwrbkduo.h"
#include "bus/nubus/pwrbkduo/cards.h"
#include "cpu/m6805/m68hc05pge.h"
#include "cpu/m68000/m68030.h"
#include "cpu/m68000/m68040.h"
#include "machine/ds2401.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "machine/z80scc.h"
#include "machine/ncr5380.h"
#include "machine/nscsi_bus.h"
#include "bus/nscsi/devices.h"

#include "softlist_dev.h"
#include "speaker.h"
#include "utf8.h"

namespace {
class macpbmsc_state : public driver_device
{
public:
	macpbmsc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_pmu(*this, "pge"),
		m_msc(*this, "msc"),
		m_dfac(*this, "dfac"),
		m_ncr5380(*this, "scsi:7:ncr5380"),
		m_scsihelp(*this, "scsihelp"),
		m_ram(*this, RAM_TAG),
		m_gsc(*this, "gsc"),
		m_csc(*this, "csc"),
		m_scc(*this, "scc"),
		m_dockslot(*this, "duobus"),
		m_battserial(*this, "ds2400"),
		m_mouse0(*this, "MOUSE0"),
		m_mouse1(*this, "MOUSE1"),
		m_mouse2(*this, "MOUSE2"),
		m_keys(*this, "Y%u", 0),
		m_kbspecial(*this, "keyb_special"),
		m_ca1_data(0),
		m_cb1_data(0),
		m_pmu_blank_display(true),
		m_portc(0),
		m_last_porte(0xff),
		m_last_portf(0xff),
		m_last_portg(0xff),
		m_last_porth(0x00), // bit 0 must start as 0 for the PG&E bootrom to configure the DFAC
		m_last_portl(0xff),
		m_lastmousex(0), m_lastmousey(0), m_lastbutton(0),
		m_mouseX(0), m_mouseY(0),
		m_matrix_row(0)
	{
	}

	void macpd2xx_base_map(address_map &map) ATTR_COLD;
	void macpd210(machine_config &config);
	void macpd210_map(address_map &map) ATTR_COLD;
	void macpd230(machine_config &config);
	void macpd230_map(address_map &map) ATTR_COLD;
	void macpd250(machine_config &config);
	void macpd250_map(address_map &map) ATTR_COLD;
	void macpd270c(machine_config &config);
	void macpd270c_map(address_map &map) ATTR_COLD;
	void macpd280(machine_config &config);
	void macpd280c(machine_config &config);
	void macpd280_map(address_map &map) ATTR_COLD;

private:
	required_device<m68000_musashi_device> m_maincpu;
	required_device<m68hc05pge_device> m_pmu;
	required_device<msc_device> m_msc;
	required_device<dfac_device> m_dfac;
	required_device<ncr53c80_device> m_ncr5380;
	required_device<mac_scsi_helper_device> m_scsihelp;
	required_device<ram_device> m_ram;
	optional_device<gsc_device> m_gsc;
	optional_device<csc_device> m_csc;
	required_device<z80scc_device> m_scc;
	required_device<pwrbkduo_device> m_dockslot;
	required_device<ds2401_device> m_battserial;
	required_ioport m_mouse0, m_mouse1, m_mouse2;
	required_ioport_array<8> m_keys;
	required_ioport m_kbspecial;
	int m_ca1_data;
	int m_cb1_data;

	bool m_pmu_blank_display;

	u8 m_portc, m_last_porte, m_last_portf, m_last_portg, m_last_porth, m_last_portl;

	s32 m_lastmousex, m_lastmousey, m_lastbutton;
	u8 m_mouseX, m_mouseY;
	u8 m_matrix_row;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void scc_irq_w(int state);
	void via_irq_w(int state);

	u16 scsi_r(offs_t offset, u16 mem_mask);
	void scsi_w(offs_t offset, u16 data, u16 mem_mask);
	u32 scsi_drq_r(offs_t offset, u32 mem_mask = ~0);
	void scsi_drq_w(offs_t offset, u32 data, u32 mem_mask = ~0);
	void scsi_berr_w(u8 data);
	u16 scc_r(offs_t offset);
	void scc_w(offs_t offset, u16 data);
	void vbl_w(int state);

	u8 pmu_porta_r();
	u8 pmu_portb_r();
	u8 pmu_portc_r();
	void pmu_portc_w(u8 data);
	u8 pmu_portd_r();
	u8 pmu_porte_r();
	void pmu_porte_w(u8 data);
	u8 pmu_portf_r();
	void pmu_portf_w(u8 data);
	u8 pmu_portg_r();
	void pmu_portg_w(u8 data);
	u8 pmu_porth_r();
	void pmu_porth_w(u8 data);
	void pmu_portj_w(u8 data);
	void pmu_portl_w(u8 data);
	u8 pmu_read_mouseX();
	u8 pmu_read_mouseY();
	int pmu_read_mouseButton();
	u8 pmu_bat_low();
	u8 pmu_bat_high();
	u8 pmu_bat_current();
	u8 pmu_bat_temp();
	u8 pmu_ambient_temp();
};

void macpbmsc_state::machine_start()
{
	m_msc->set_ram_info((u32 *)m_ram->pointer(), m_ram->size());

	m_ca1_data = 0;

	save_item(NAME(m_ca1_data));
	save_item(NAME(m_cb1_data));
	save_item(NAME(m_pmu_blank_display));
	save_item(NAME(m_portc));
	save_item(NAME(m_last_porte));
	save_item(NAME(m_last_portf));
	save_item(NAME(m_last_portg));
	save_item(NAME(m_last_porth));
	save_item(NAME(m_last_portl));
	save_item(NAME(m_lastmousex));
	save_item(NAME(m_lastmousey));
	save_item(NAME(m_lastbutton));
	save_item(NAME(m_mouseX));
	save_item(NAME(m_mouseY));
	save_item(NAME(m_matrix_row));
}

void macpbmsc_state::machine_reset()
{
	m_ca1_data = 0;
	m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
}

u16 macpbmsc_state::scc_r(offs_t offset)
{
	m_msc->via_sync();
	const u16 result = m_scc->dc_ab_r(offset);
	return (result << 8) | result;
}

void macpbmsc_state::scc_w(offs_t offset, u16 data)
{
	m_scc->dc_ab_w(offset, data >> 8);
}

void macpbmsc_state::vbl_w(int state)
{
	int MouseCountX = 0, MouseCountY = 0;
	int NewX, NewY;

	NewX = m_mouse1->read();
	NewY = m_mouse2->read();

	//  printf("pollmouse: X %d Y %d\n", NewX, NewY);

	/* see if it moved in the x coord */
	if (NewX != m_lastmousex)
	{
		int diff = (NewX - m_lastmousex);

		/* check for wrap */
		if (diff > 0x80)
			diff = 0x100 - diff;
		if (diff < -0x80)
			diff = -0x100 - diff;

		MouseCountX += diff;
		m_lastmousex = NewX;
	}

	/* see if it moved in the y coord */
	if (NewY != m_lastmousey)
	{
		int diff = (NewY - m_lastmousey);

		/* check for wrap */
		if (diff > 0x80)
			diff = 0x100 - diff;
		if (diff < -0x80)
			diff = -0x100 - diff;

		MouseCountY += diff;
		m_lastmousey = NewY;
	}

	m_lastbutton = m_mouse0->read() & 0x01;
	m_mouseX = MouseCountX;
	m_mouseY = MouseCountY;
//  printf("X %02x Y %02x\n", m_mouseX, m_mouseY);
}

u16 macpbmsc_state::scsi_r(offs_t offset, u16 mem_mask)
{
	const int reg = (offset >> 3) & 0xf;
	const bool pseudo_dma = (reg == 6) && (offset >= 0x130);

	return m_scsihelp->read_wrapper(pseudo_dma, reg) << 8;
}

void macpbmsc_state::scsi_w(offs_t offset, u16 data, u16 mem_mask)
{
	const int reg = (offset >> 3) & 0xf;
	const bool pseudo_dma = (reg == 0) && (offset >= 0x100);

	m_scsihelp->write_wrapper(pseudo_dma, reg, data>>8);
}

u32 macpbmsc_state::scsi_drq_r(offs_t offset, u32 mem_mask)
{
	switch (mem_mask)
	{
		case 0xff000000:
			return m_scsihelp->read_wrapper(true, 6)<<24;

		case 0xffff0000:
			return (m_scsihelp->read_wrapper(true, 6)<<24) | (m_scsihelp->read_wrapper(true, 6)<<16);

		case 0xffffffff:
			return (m_scsihelp->read_wrapper(true, 6)<<24) | (m_scsihelp->read_wrapper(true, 6)<<16) | (m_scsihelp->read_wrapper(true, 6)<<8) | m_scsihelp->read_wrapper(true, 6);

		default:
			logerror("scsi_drq_r: unknown mem_mask %08x\n", mem_mask);
	}

	return 0;
}

void macpbmsc_state::scsi_drq_w(offs_t offset, u32 data, u32 mem_mask)
{
	switch (mem_mask)
	{
		case 0xff000000:
			m_scsihelp->write_wrapper(true, 0, data>>24);
			break;

		case 0xffff0000:
			m_scsihelp->write_wrapper(true, 0, data>>24);
			m_scsihelp->write_wrapper(true, 0, data>>16);
			break;

		case 0xffffffff:
			m_scsihelp->write_wrapper(true, 0, data>>24);
			m_scsihelp->write_wrapper(true, 0, data>>16);
			m_scsihelp->write_wrapper(true, 0, data>>8);
			m_scsihelp->write_wrapper(true, 0, data&0xff);
			break;

		default:
			logerror("scsi_drq_w: unknown mem_mask %08x\n", mem_mask);
			break;
	}
}

void macpbmsc_state::scsi_berr_w(u8 data)
{
	m_maincpu->pulse_input_line(M68K_LINE_BUSERROR, attotime::zero);
}

u8 macpbmsc_state::pmu_porta_r()
{
	if (m_portc == 0)   // power key
	{
		return 0xdf | ((m_kbspecial->read() & 1) << 5);
	}

	// matrix X0-X7 (bits 0-7)
	return m_keys[m_matrix_row]->read() & 0xff;
}

u8 macpbmsc_state::pmu_portb_r()
{
	// matrix X8-X10 (bits 0-2), modifiers (bits 3-7)
	return (m_kbspecial->read() & 0xf8) | ((m_keys[m_matrix_row]->read() >> 8) & 7);
}

u8 macpbmsc_state::pmu_portc_r()
{
	return m_portc ^ 0xff;
}

void macpbmsc_state::pmu_portc_w(u8 data)
{
	m_portc = data ^ 0xff;

	// matrix row select
	m_matrix_row = 0;
	for (u8 i = 0; i < 8; i++)
	{
		if (BIT(m_portc, i))
		{
			m_matrix_row = i;
			return;
		}
	}
}

// bit 4 = 1 for US keyboard, 0 for ISO
// bit 5 = 1 for sound power off
// bit 6 = 1 for docking station NOT present
// bit 7 = 1 for second mouse button NOT pressed
u8 macpbmsc_state::pmu_portd_r()
{
	return (1 << 7) | (m_dockslot->is_slot_empty() ? (1 << 6) : 0) | (1 << 4);   // US keyboard, get dock presence from slot
}

// bit 1 = screen power on/off
// bit 2 = MSC /reset
// bit 7 = data line for 1-wire Dallas comms with the battery
u8 macpbmsc_state::pmu_porte_r()
{
	if (!machine().side_effects_disabled())
	{
		return (m_last_porte & 0x7f) | (m_battserial->read() << 7);
	}

	return m_last_porte;
}

void macpbmsc_state::pmu_porte_w(u8 data)
{
	if (BIT(data, 2) != BIT(m_last_porte, 2))
	{
		if (BIT(data, 2))
		{
			m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
		}
		m_msc->pmu_reset_w(BIT(data, 2) ^ 1);
	}
	m_maincpu->set_input_line(INPUT_LINE_RESET, BIT(data, 2) ? CLEAR_LINE : ASSERT_LINE);
	if (BIT(data, 1) != BIT(m_last_porte, 1))
	{
		m_pmu_blank_display = BIT(data, 1);
		if (m_gsc)
		{
			m_gsc->set_pmu_blank(m_pmu_blank_display);
		}
		else if (m_gsc)
		{
			m_csc->set_pmu_blank(m_pmu_blank_display);
		}
	}
	if (BIT(data, 7) != BIT(m_last_porte, 7))
	{
		m_battserial->write(BIT(data, 7));
	}
	m_last_porte = data;
}

// bit 1 = Power (1 = off, 0 = on)
// bit 2 = 1 for +5V present when input, cause level 1 interrupt when output (VIA CB2?)
// bit 3 = clamshell open (1) or closed (0)
// bit 6 = /PMREQ
u8 macpbmsc_state::pmu_portf_r()
{
	u8 retval = (1 << 1) | (1 << 2);       // indicate +5V present and PFW rail powered up
	retval |= (1 << 3);         // indicate clamshell open
	retval |= (m_msc->get_pmu_req() << 6);
	return retval;
}

u8 macpbmsc_state::pmu_bat_low()
{
	return 0xff;
}

u8 macpbmsc_state::pmu_bat_high()
{
	return 0x7f;
}

u8 macpbmsc_state::pmu_bat_current()
{
	return 0x40;
}

u8 macpbmsc_state::pmu_bat_temp()
{
	return 131; // ~24 degrees C
}

u8 macpbmsc_state::pmu_ambient_temp()
{
	return 131; // ~24 degrees C
}

void macpbmsc_state::pmu_portf_w(u8 data)
{
	if (!BIT(data, 2) && BIT(m_last_portf, 2))
	{
		m_msc->pmu_int(ASSERT_LINE);
	}
	else if (BIT(data, 2) && !BIT(m_last_portf, 2))
	{
		m_msc->pmu_int(CLEAR_LINE);
	}

	m_last_portf = data;
}

// bit 3 = 1 for docking station powered up
// bit 4 = caps lock LED
// bit 5 = sleep LED
// bit 6 = charger present (1 = present)
u8 macpbmsc_state::pmu_portg_r()
{
	return (1 << 6) | (1 << 3); // indicate we're on a charger and dock is powered up
}

// bit 1 set turns on the main battery power
// bit 5 is sleep: 0 = normal operation, 1 = turn off 31.whatever MHz master clock
void macpbmsc_state::pmu_portg_w(u8 data)
{
	if (BIT(data, 5) != BIT(m_last_portg, 5))
	{
		if (!BIT(data, 5))
		{
			m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
			m_msc->pmu_reset_w(ASSERT_LINE);
			m_msc->pmu_reset_w(CLEAR_LINE);
		}
	}

	m_last_portg = data;
}

// bit 0 = DFAC reset
// bit 1 = sleep LED (270c, maybe 280/280c also?)
// bit 2 = NMI
// bit 5 = DFAC latch
// bit 6 = /PMACK
u8 macpbmsc_state::pmu_porth_r()
{
	return m_last_porth;
}

void macpbmsc_state::pmu_porth_w(u8 data)
{
	m_dfac->latch_write(BIT(data, 5));
	m_msc->pmu_ack_w(BIT(data, 6));
	m_last_porth = data;
}

// bit 6 = DFAC clock
// bit 7 = DFAC data
void macpbmsc_state::pmu_portj_w(u8 data)
{
	m_dfac->clock_write(BIT(data, 6));
	m_dfac->data_write(BIT(data, 7));
}

// bit 1 = main power to the CPU (1 = off, 0 = on)
void macpbmsc_state::pmu_portl_w(u8 data)
{
	m_last_portl = data;
}

u8 macpbmsc_state::pmu_read_mouseX()
{
	return m_mouseX;
}

u8 macpbmsc_state::pmu_read_mouseY()
{
	return m_mouseY;
}

int macpbmsc_state::pmu_read_mouseButton()
{
	return m_lastbutton;
}

/***************************************************************************
    ADDRESS MAPS
****************************************************************************/

void macpbmsc_state::macpd2xx_base_map(address_map &map)
{
	map(0x40000000, 0x600fffff).m(m_msc, FUNC(msc_device::map));

	map(0x50f04000, 0x50f05fff).rw(FUNC(macpbmsc_state::scc_r), FUNC(macpbmsc_state::scc_w));
	map(0x50f06000, 0x50f07fff).rw(FUNC(macpbmsc_state::scsi_drq_r), FUNC(macpbmsc_state::scsi_drq_w));
	map(0x50f10000, 0x50f11fff).rw(FUNC(macpbmsc_state::scsi_r), FUNC(macpbmsc_state::scsi_w));
	map(0x50f12000, 0x50f13fff).rw(FUNC(macpbmsc_state::scsi_drq_r), FUNC(macpbmsc_state::scsi_drq_w));
}

void macpbmsc_state::macpd210_map(address_map &map)
{
	macpd2xx_base_map(map);

	map(0x50000000, 0x6fffffff).m(m_gsc, FUNC(gsc_device::map));

	map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a1004; }));
}

void macpbmsc_state::macpd230_map(address_map &map)
{
	macpd210_map(map);

	map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a1005; }));
}

void macpbmsc_state::macpd250_map(address_map &map)
{
	macpd210_map(map);

	map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a1006; }));
}

void macpbmsc_state::macpd270c_map(address_map &map)
{
	macpd2xx_base_map(map);

	map(0x50000000, 0x6fffffff).m(m_csc, FUNC(csc_device::map));

	map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a1002; }));
}

void macpbmsc_state::macpd280_map(address_map &map)
{
	macpd270c_map(map);

	map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a1000; }));
}

static INPUT_PORTS_START( dblite )
	PORT_START("MOUSE0") /* Mouse - button */
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("Mouse Button") PORT_CODE(MOUSECODE_BUTTON1)

	PORT_START("MOUSE1") /* Mouse - X AXIS */
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1)

	PORT_START("MOUSE2") /* Mouse - Y AXIS */
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1)

	PORT_START("Y0")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)          PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)          PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("Y1")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)  PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)  PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)  PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)  PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)  PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)  PORT_CHAR('\'') PORT_CHAR('\"')
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN)      PORT_CODE(KEYCODE_DOWN) PORT_CHAR(10)
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("Y2")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc")             PORT_CODE(KEYCODE_ESC)      PORT_CHAR(27)
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Brightness Up")   PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Brightness Down") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)         PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)          PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD)  PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("Y3")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Contrast Down") PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)       PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Contrast Up")   PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)       PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)       PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)   PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("Y4")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)      PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)      PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)      PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)      PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)      PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)  PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)   PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)  PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT)     PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("Y5")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)      PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)      PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)      PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)      PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)      PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)      PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("Y6")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('`') PORT_CHAR('~')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)          PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)          PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)          PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('#') PORT_CHAR(U'^') // (actually to the left of the return key on the ASDF row)
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("Y7")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)      PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)      PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)      PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)      PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)      PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)  PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Delete") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("keyb_special")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Power")         PORT_CODE(KEYCODE_F12)

	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Command")       PORT_CODE(KEYCODE_LALT)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Control")       PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift")         PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Option")        PORT_CODE(KEYCODE_RALT)
	PORT_BIT( 0x80, IP_ACTIVE_LOW,  IPT_KEYBOARD) PORT_NAME("Caps Lock")    PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
INPUT_PORTS_END

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

void macpbmsc_state::macpd210(machine_config &config)
{
	M68030(config, m_maincpu, 25_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &macpbmsc_state::macpd210_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");
	m_maincpu->set_fpu_enable(false);

	M68HC05PGE(config, m_pmu, 4.194304_MHz_XTAL);
	m_pmu->read_p<m68hc05pge_device::PGE_PORTA>().set(FUNC(macpbmsc_state::pmu_porta_r));
	m_pmu->read_p<m68hc05pge_device::PGE_PORTB>().set(FUNC(macpbmsc_state::pmu_portb_r));
	m_pmu->read_p<m68hc05pge_device::PGE_PORTC>().set(FUNC(macpbmsc_state::pmu_portc_r));
	m_pmu->write_p<m68hc05pge_device::PGE_PORTC>().set(FUNC(macpbmsc_state::pmu_portc_w));
	m_pmu->read_p<m68hc05pge_device::PGE_PORTD>().set(FUNC(macpbmsc_state::pmu_portd_r));
	m_pmu->read_p<m68hc05pge_device::PGE_PORTE>().set(FUNC(macpbmsc_state::pmu_porte_r));
	m_pmu->write_p<m68hc05pge_device::PGE_PORTE>().set(FUNC(macpbmsc_state::pmu_porte_w));
	m_pmu->read_p<m68hc05pge_device::PGE_PORTF>().set(FUNC(macpbmsc_state::pmu_portf_r));
	m_pmu->write_p<m68hc05pge_device::PGE_PORTF>().set(FUNC(macpbmsc_state::pmu_portf_w));
	m_pmu->read_p<m68hc05pge_device::PGE_PORTG>().set(FUNC(macpbmsc_state::pmu_portg_r));
	m_pmu->write_p<m68hc05pge_device::PGE_PORTG>().set(FUNC(macpbmsc_state::pmu_portg_w));
	m_pmu->read_p<m68hc05pge_device::PGE_PORTH>().set(FUNC(macpbmsc_state::pmu_porth_r));
	m_pmu->write_p<m68hc05pge_device::PGE_PORTH>().set(FUNC(macpbmsc_state::pmu_porth_w));
	m_pmu->write_p<m68hc05pge_device::PGE_PORTJ>().set(FUNC(macpbmsc_state::pmu_portj_w));
	m_pmu->write_p<m68hc05pge_device::PGE_PORTL>().set(FUNC(macpbmsc_state::pmu_portl_w));
	m_pmu->set_pullups<m68hc05pge_device::PGE_PORTC>(0xff);
	m_pmu->set_pullups<m68hc05pge_device::PGE_PORTE>(0x80);     // bit 7 of port E is the 1-Wire bus
	m_pmu->ad_in<0>().set(FUNC(macpbmsc_state::pmu_bat_low));
	m_pmu->ad_in<1>().set(FUNC(macpbmsc_state::pmu_bat_high));
	m_pmu->ad_in<2>().set(FUNC(macpbmsc_state::pmu_bat_current));
	m_pmu->ad_in<3>().set(FUNC(macpbmsc_state::pmu_bat_temp));
	m_pmu->ad_in<4>().set(FUNC(macpbmsc_state::pmu_ambient_temp));
	m_pmu->spi_clock_callback().set(m_msc, FUNC(msc_device::cb1_w));
	m_pmu->spi_mosi_callback().set(m_msc, FUNC(msc_device::cb2_w));
	m_pmu->read_tbB().set(FUNC(macpbmsc_state::pmu_read_mouseButton));
	m_pmu->read_tbX().set(FUNC(macpbmsc_state::pmu_read_mouseX));
	m_pmu->read_tbY().set(FUNC(macpbmsc_state::pmu_read_mouseY));

	MSC(config, m_msc, 31.3344_MHz_XTAL);
	m_msc->set_maincpu_tag("maincpu");
	m_msc->set_pmu_tag("pge");
	m_msc->set_rom_tag("bootrom");
	m_msc->set_cpu_clock(25_MHz_XTAL);
	m_msc->add_route(0, m_dfac, 1.0, 0);
	m_msc->add_route(1, m_dfac, 1.0, 1);
	m_msc->cb2_callback().set(m_pmu, FUNC(m68hc05pge_device::spi_miso_w));
	m_msc->vbl_callback().set(FUNC(macpbmsc_state::vbl_w));

	APPLE_DFAC(config, m_dfac, 22257);
	m_dfac->add_route(0, "speaker", 1.0, 0);
	m_dfac->add_route(1, "speaker", 1.0, 1);

	GSC(config, m_gsc, 31.3344_MHz_XTAL);
	m_gsc->set_panel_id(6);

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", mac_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:1", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3").option_set("cdrom", NSCSI_CDROM_APPLE).machine_config(
		[](device_t *device)
		{
			device->subdevice<cdda_device>("cdda")->add_route(0, "^^speaker", 1.0, 0);
			device->subdevice<cdda_device>("cdda")->add_route(1, "^^speaker", 1.0, 1);
		});
	NSCSI_CONNECTOR(config, "scsi:4", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr5380", NCR53C80).machine_config([this](device_t *device) {
		ncr53c80_device &adapter = downcast<ncr53c80_device &>(*device);
		adapter.irq_handler().set(m_msc, FUNC(msc_device::scsi_irq_w));
		adapter.drq_handler().set(m_scsihelp, FUNC(mac_scsi_helper_device::drq_w));
	});

	MAC_SCSI_HELPER(config, m_scsihelp);
	m_scsihelp->scsi_read_callback().set(m_ncr5380, FUNC(ncr53c80_device::read));
	m_scsihelp->scsi_write_callback().set(m_ncr5380, FUNC(ncr53c80_device::write));
	m_scsihelp->scsi_dma_read_callback().set(m_ncr5380, FUNC(ncr53c80_device::dma_r));
	m_scsihelp->scsi_dma_write_callback().set(m_ncr5380, FUNC(ncr53c80_device::dma_w));
	m_scsihelp->cpu_halt_callback().set_inputline(m_maincpu, INPUT_LINE_HALT);
	m_scsihelp->timeout_error_callback().set(FUNC(macpbmsc_state::scsi_berr_w));

	SCC85C30(config, m_scc, 31.3344_MHz_XTAL / 4);
	m_scc->out_int_callback().set(m_msc, FUNC(msc_device::scc_irq_w));

	DS2401(config, m_battserial, 0); // actually DS2400, but 2400/2401 are compatible

	PWRBKDUO(config, m_dockslot, 0);
	m_dockslot->set_space(m_maincpu, AS_PROGRAM);
	m_dockslot->set_maincpu_tag("maincpu");
	m_dockslot->set_screen_tag(":gsc:screen");
	m_dockslot->irq_callback().set(m_msc, FUNC(msc_device::slot2_irq_w));
	PWRBKDUO_SLOT(config, "dock", "duobus", pwrbkduo_cards, nullptr);

	SPEAKER(config, "speaker", 2).front();

	RAM(config, m_ram);
	m_ram->set_default_size("4M");
	m_ram->set_extra_options("8M,12M,16M,24M");

	SOFTWARE_LIST(config, "hdd_list").set_original("mac_hdd");
	SOFTWARE_LIST(config, "cd_list").set_original("mac_cdrom").set_filter("MC68030,MC68030_32");
}

void macpbmsc_state::macpd230(machine_config &config)
{
	macpd210(config);
	m_maincpu->set_clock(33_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &macpbmsc_state::macpd230_map);
	m_msc->set_cpu_clock(33_MHz_XTAL);
}

void macpbmsc_state::macpd250(machine_config &config)
{
	macpd230(config);
	m_maincpu->set_fpu_enable(true);
	m_maincpu->set_addrmap(AS_PROGRAM, &macpbmsc_state::macpd250_map);
}

void macpbmsc_state::macpd270c(machine_config &config)
{
	macpd230(config);
	config.device_remove("gsc");

	CSC(config, m_csc, 31.3344_MHz_XTAL);
	m_csc->write_irq().set(m_msc, FUNC(msc_device::lcd_irq_w));
	m_csc->set_panel_id(0);

	m_dockslot->set_screen_tag(":csc:screen");

	m_maincpu->set_fpu_enable(true);
	m_maincpu->set_addrmap(AS_PROGRAM, &macpbmsc_state::macpd270c_map);
}

void macpbmsc_state::macpd280(machine_config &config)
{
	macpd270c(config);

	m_csc->set_panel_id(4);

	M68040(config.replace(), m_maincpu, 33_MHz_XTAL/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &macpbmsc_state::macpd280_map);
}

void macpbmsc_state::macpd280c(machine_config &config)
{
	macpd280(config);

	m_csc->set_panel_id(0);
}

ROM_START(macpd210)
	ROM_REGION32_BE(0x100000, "bootrom", 0)
	ROM_LOAD("ecfa989b.rom", 0x000000, 0x100000, CRC(b86ed854) SHA1(ed1371c97117a5884da4a6605ecfc5abed48ae5a))

	// battery serial number, read from an embedded Dallas DS2400
	ROM_REGION(0x8, "ds2400", ROMREGION_ERASE00)
	ROM_LOAD( "duobatid.bin", 0x000000, 0x000008, CRC(7545c341) SHA1(61b094ee5b398077f70eaa1887921c8366f7abfe) )
ROM_END

ROM_START(macpd270c)
	ROM_REGION32_BE(0x100000, "bootrom", 0)
	ROM_LOAD( "0024d346.rom", 0x000000, 0x100000, CRC(94c4d04a) SHA1(be7bd9637203e4513b896146ddfc85c37817d131) )

	ROM_REGION(0x8, "ds2400", ROMREGION_ERASE00)
	ROM_LOAD( "duobatid.bin", 0x000000, 0x000008, CRC(7545c341) SHA1(61b094ee5b398077f70eaa1887921c8366f7abfe) )
ROM_END

ROM_START(macpd280)
	ROM_REGION32_BE(0x100000, "bootrom", 0)
	ROM_LOAD("015621d7.rom", 0x000000, 0x100000, CRC(568d28eb) SHA1(d49dd69cf038784b8849793ad3c0e62c2d11f653))

	ROM_REGION(0x8, "ds2400", ROMREGION_ERASE00)
	ROM_LOAD( "duobatid.bin", 0x000000, 0x000008, CRC(7545c341) SHA1(61b094ee5b398077f70eaa1887921c8366f7abfe) )
ROM_END

#define rom_macpd230 rom_macpd210
#define rom_macpd250 rom_macpd210
#define rom_macpd280c rom_macpd280

} // anonymous namespace

COMP(1992, macpd210, 0, 0, macpd210, dblite, macpbmsc_state, empty_init, "Apple Computer", "Macintosh PowerBook Duo 210", MACHINE_SUPPORTS_SAVE)
COMP(1992, macpd230, macpd210, 0, macpd230, dblite, macpbmsc_state, empty_init, "Apple Computer", "Macintosh PowerBook Duo 230", MACHINE_SUPPORTS_SAVE)
COMP(1993, macpd250, macpd210, 0, macpd250, dblite, macpbmsc_state, empty_init, "Apple Computer", "Macintosh PowerBook Duo 250", MACHINE_SUPPORTS_SAVE)
COMP(1993, macpd270c, 0, 0, macpd270c, dblite, macpbmsc_state, empty_init, "Apple Computer", "Macintosh PowerBook Duo 270c", MACHINE_SUPPORTS_SAVE)
COMP(1994, macpd280, 0, 0, macpd280, dblite, macpbmsc_state, empty_init, "Apple Computer", "Macintosh PowerBook Duo 280", MACHINE_SUPPORTS_SAVE)
COMP(1994, macpd280c, macpd280, 0, macpd280c, dblite, macpbmsc_state, empty_init, "Apple Computer", "Macintosh PowerBook Duo 280c", MACHINE_SUPPORTS_SAVE)



macquadra605.cpp
<---------------------------------------------------------------------->
// copyright-holders:R. Belmont
// license:BSD-3-Clause
/****************************************************************************

    drivers/macquadra605.cpp
    Mac Quadra 605 ("Aladdin", "Primus")
    Mac LC 475
    Mac LC 575 ("Optimus")

    By R. Belmont

    These machines were cost-reduced versions of the "Wombat" (Quadra/Centris 610/650/800)
    machines.  djMEMC was replaced with a cost-reduced version called MEMCjr,
    and IOSB was replaced with PrimeTime, which is mostly compatible.

****************************************************************************/

#include "emu.h"

#include "cuda.h"
#include "dfac2.h"
#include "djmemc.h"
#include "iosb.h"
#include "macadb.h"
#include "mactoolbox.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/devices.h"
#include "bus/nubus/cards.h"
#include "bus/nubus/nubus.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68040.h"
#include "machine/ncr53c90.h"
#include "machine/nscsi_bus.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "machine/z80scc.h"

#include "softlist_dev.h"

#define C32M 31.3344_MHz_XTAL
#define C15M (C32M/2)
#define C7M (C32M/4)

namespace {

class quadra605_state : public driver_device
{
public:
	quadra605_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_memcjr(*this, "memcjr"),
		m_primetime(*this, "primetime"),
		m_macadb(*this, "macadb"),
		m_cuda(*this, "cuda"),
		m_dfac2(*this, "dfac2"),
		m_scc(*this, "scc"),
		m_ram(*this, RAM_TAG),
		m_scsibus(*this, "scsi"),
		m_ncr1(*this, "scsi:7:ncr53c96")
	{
	}

	void macqd605(machine_config &config);
	void maclc475(machine_config &config);
	void maclc575(machine_config &config);

	void quadra605_map(address_map &map) ATTR_COLD;
	void lc475_map(address_map &map) ATTR_COLD;
	void lc575_map(address_map &map) ATTR_COLD;

	void init_macqd605();

private:
	required_device<m68040_device> m_maincpu;
	required_device<memcjr_device> m_memcjr;
	required_device<primetime_device> m_primetime;
	required_device<macadb_device> m_macadb;
	required_device<cuda_device> m_cuda;
	required_device<dfac2_device> m_dfac2;
	required_device<z80scc_device> m_scc;
	required_device<ram_device> m_ram;
	required_device<nscsi_bus_device> m_scsibus;
	required_device<ncr53c96_device> m_ncr1;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	u16 mac_scc_r(offs_t offset)
	{
		m_primetime->via_sync();
		u16 result = m_scc->dc_ab_r(offset);
		return (result << 8) | result;
	}
	void mac_scc_2_w(offs_t offset, u16 data) { m_primetime->via_sync();
		m_scc->dc_ab_w(offset, data >> 8); }

	void cuda_reset_w(int state)
	{
		m_maincpu->set_input_line(INPUT_LINE_HALT, state);
		m_maincpu->set_input_line(INPUT_LINE_RESET, state);
	}
};

void quadra605_state::machine_start()
{
	m_memcjr->set_ram_info((u32 *) m_ram->pointer(), m_ram->size());
}

void quadra605_state::machine_reset()
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
}

void quadra605_state::init_macqd605()
{
}

/***************************************************************************
    ADDRESS MAPS
***************************************************************************/
void quadra605_state::quadra605_map(address_map &map)
{
	map(0x00000000, 0xffffffff).m(m_memcjr, FUNC(memcjr_device::map));
	map(0x50000000, 0x5fffffff).m(m_primetime, FUNC(primetime_device::map));

	map(0x5000c000, 0x5000dfff).rw(FUNC(quadra605_state::mac_scc_r), FUNC(quadra605_state::mac_scc_2_w)).mirror(0x00fc0000);

	map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a2225; }));
}

void quadra605_state::lc475_map(address_map &map)
{
	quadra605_map(map);
	map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a2221; }));
}

void quadra605_state::lc575_map(address_map &map)
{
	quadra605_map(map);
	map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a222e; }));
}

/***************************************************************************
    DEVICE CONFIG
***************************************************************************/

static INPUT_PORTS_START( macadb )
INPUT_PORTS_END

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

void quadra605_state::macqd605(machine_config &config)
{
	M68040(config, m_maincpu, 25_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &quadra605_state::quadra605_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	MEMCJR(config, m_memcjr, 25_MHz_XTAL);
	m_memcjr->set_maincpu_tag("maincpu");
	m_memcjr->set_rom_tag("bootrom");
	m_memcjr->write_irq().set(m_primetime, FUNC(primetime_device::via2_irq_w<0x40>));

	PRIMETIME(config, m_primetime, 25_MHz_XTAL);
	m_primetime->set_maincpu_tag("maincpu");
	m_primetime->set_scsi_tag("scsi:7:ncr53c96");

	SCC85C30(config, m_scc, C7M);
	m_scc->configure_channels(3'686'400, 3'686'400, 3'686'400, 3'686'400);
	m_scc->out_int_callback().set(m_primetime, FUNC(primetime_device::scc_irq_w));
	m_scc->out_txda_callback().set("printer", FUNC(rs232_port_device::write_txd));
	m_scc->out_txdb_callback().set("modem", FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232a(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_scc, FUNC(z80scc_device::rxa_w));
	rs232a.dcd_handler().set(m_scc, FUNC(z80scc_device::dcda_w));
	rs232a.cts_handler().set(m_scc, FUNC(z80scc_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "modem", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_scc, FUNC(z80scc_device::rxb_w));
	rs232b.dcd_handler().set(m_scc, FUNC(z80scc_device::dcdb_w));
	rs232b.cts_handler().set(m_scc, FUNC(z80scc_device::ctsb_w));

	// SCSI bus and devices
	NSCSI_BUS(config, m_scsibus);
	NSCSI_CONNECTOR(config, "scsi:0", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:1", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3").option_set("cdrom", NSCSI_CDROM_APPLE).machine_config(
		[](device_t *device)
		{
			device->subdevice<cdda_device>("cdda")->add_route(0, "^^primetime:speaker", 1.0, 0);
			device->subdevice<cdda_device>("cdda")->add_route(1, "^^primetime:speaker", 1.0, 1);
		});
	NSCSI_CONNECTOR(config, "scsi:4", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", mac_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr53c96", NCR53C96).clock(40_MHz_XTAL).machine_config(
		[this] (device_t *device)
		{
			ncr53c96_device &adapter = downcast<ncr53c96_device &>(*device);

			adapter.set_busmd(ncr53c96_device::BUSMD_1);
			adapter.irq_handler_cb().set(m_primetime, FUNC(primetime_device::scsi_irq_w));
			adapter.drq_handler_cb().set(m_primetime, FUNC(primetime_device::scsi_drq_w));
		});

	MACADB(config, m_macadb, C15M);

	CUDA_V2XX(config, m_cuda, XTAL(32'768));
	m_cuda->set_default_bios_tag("341s0788");
	m_cuda->reset_callback().set(FUNC(quadra605_state::cuda_reset_w));
	m_cuda->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
	m_cuda->via_clock_callback().set(m_primetime, FUNC(primetime_device::cb1_w));
	m_cuda->via_data_callback().set(m_primetime, FUNC(primetime_device::cb2_w));
	m_cuda->nmi_callback().set_inputline(m_maincpu, M68K_IRQ_7);
	m_macadb->adb_data_callback().set(m_cuda, FUNC(cuda_device::set_adb_line));
	m_macadb->adb_power_callback().set(m_cuda, FUNC(cuda_device::set_adb_power));
	config.set_perfect_quantum(m_maincpu);

	APPLE_DFAC2(config, m_dfac2, 22257);
	m_dfac2->sda_callback().set(m_cuda, FUNC(cuda_device::set_iic_sda));
	m_cuda->iic_scl_callback().set(m_dfac2, FUNC(dfac2_device::scl_write));
	m_cuda->iic_sda_callback().set(m_dfac2, FUNC(dfac2_device::sda_write));

	m_primetime->pb3_callback().set(m_cuda, FUNC(cuda_device::get_treq));
	m_primetime->pb4_callback().set(m_cuda, FUNC(cuda_device::set_byteack));
	m_primetime->pb5_callback().set(m_cuda, FUNC(cuda_device::set_tip));
	m_primetime->write_cb2().set(m_cuda, FUNC(cuda_device::set_via_data));

	nubus_device &nubus(NUBUS(config, "pds", 0));
	nubus.set_space(m_maincpu, AS_PROGRAM);
	nubus.set_bus_mode(nubus_device::nubus_mode_t::QUADRA_DAFB);
	nubus.out_irqe_callback().set(m_primetime, FUNC(primetime_device::via2_irq_w<0x20>));
	NUBUS_SLOT(config, "lcpds", "pds", mac_pdslc_cards, nullptr);

	/* internal ram */
	RAM(config, m_ram);
	m_ram->set_default_size("4M");
	m_ram->set_extra_options("8M,16M,32M,64M,96M,128M,192M,256M,320M,384M,512M,640M");

	SOFTWARE_LIST(config, "hdd_list").set_original("mac_hdd");
	SOFTWARE_LIST(config, "cd_list").set_original("mac_cdrom").set_filter("MC68040");
}

void quadra605_state::maclc475(machine_config &config)
{
	macqd605(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &quadra605_state::lc475_map);
}

void quadra605_state::maclc575(machine_config &config)
{
	macqd605(config);

	M68040(config.replace(), m_maincpu, 33_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &quadra605_state::lc575_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	m_ram->set_default_size("5M");
}

ROM_START( macqd605 )
	ROM_REGION32_BE(0x100000, "bootrom", 0)
	ROM_LOAD( "ff7439ee.bin", 0x000000, 0x100000, CRC(b8514689) SHA1(1d833125adf553a50f5994746c2c01aa5a1dbbf2) )
ROM_END

#define rom_maclc475 rom_macqd605
#define rom_maclc575 rom_macqd605

} // anonymous namespace

COMP( 1993, macqd605, 0, 0, macqd605, macadb, quadra605_state, init_macqd605,  "Apple Computer", "Macintosh Quadra 605", MACHINE_SUPPORTS_SAVE)
COMP( 1993, maclc475, macqd605, 0, maclc475, macadb, quadra605_state, init_macqd605,  "Apple Computer", "Macintosh LC/Performa 475", MACHINE_SUPPORTS_SAVE)
COMP( 1994, maclc575, macqd605, 0, maclc575, macadb, quadra605_state, init_macqd605,  "Apple Computer", "Macintosh LC/Performa 575", MACHINE_SUPPORTS_SAVE)



macquadra630.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/****************************************************************************

    drivers/macquadra630.cpp
    Mac Quadra 630 ("Show and Tell")
    Mac LC 580 ("Dragonkid")

    By R. Belmont

    These machines took the cost-reduced but still decent Quadra 605/LC 575
    and made them even cheaper by replacing the full-featured DAFB video chip
    with "Valkyrie".  It appears to offer only a few pre-programmed video
    mode timings.

    Further cost reduction occured by replacing the hard disk with an ATA/IDE
    model instead of the by-then traditional Mac SCSI drive.  The ATA interface
    was wedged into the chipset in a somewhat odd manner that impacts VIA2
    interrupt handling (including video VBL IRQs).

    Known problems:
    - If you don't boot an OS, the mouse pointer will stop updating when the
      question-mark disk appears.  If you do boot an OS, everything's fine.

    - The later version boot ROM for the LC 580 can't boot a SCSI CD-ROM.  It
      reads 512 bytes of a 2048 byte sector and expects CyclePhase_96 to read
      and discard the rest of the sector from the drive.  But it sees a (pseudo)
      DMA command was active and waits for DRQ, which doesn't happen because
      the 53C96's transfer count is zero.  The earlier ROM has the same logic as
      previous (and later!) 53C96 machines and works fine.

      Video in chips


****************************************************************************/

#include "emu.h"

#include "cuda.h"
#include "dfac2.h"
#include "f108.h"
#include "iosb.h"
#include "macadb.h"
#include "mactoolbox.h"
#include "valkyrie.h"

#include "bus/nubus/cards.h"
#include "bus/nubus/nubus.h"
#include "cpu/m68000/m68040.h"
#include "machine/input_merger.h"
#include "machine/ram.h"
#include "machine/timer.h"

#include "softlist_dev.h"


#define C32M 31.3344_MHz_XTAL
#define C15M (C32M/2)
#define C7M (C32M/4)

namespace {

class quadra630_state : public driver_device
{
public:
	quadra630_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_f108(*this, "f108"),
		m_primetimeii(*this, "primetimeii"),
		m_dfac2(*this, "dfac2"),
		m_video(*this, "valkyrie"),
		m_macadb(*this, "macadb"),
		m_cuda(*this, "cuda"),
		m_ram(*this, RAM_TAG)
	{
	}

	void macqd630(machine_config &config);
	void maclc580(machine_config &config);

	void quadra630_map(address_map &map) ATTR_COLD;
	void lc580_map(address_map &map) ATTR_COLD;

	void init_macqd630();

private:
	required_device<m68040_device> m_maincpu;
	required_device<f108_device> m_f108;
	required_device<primetimeii_device> m_primetimeii;
	required_device<dfac2_device> m_dfac2;
	required_device<valkyrie_device> m_video;
	required_device<macadb_device> m_macadb;
	required_device<cuda_device> m_cuda;
	required_device<ram_device> m_ram;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void cuda_reset_w(int state)
	{
		m_maincpu->set_input_line(INPUT_LINE_HALT, state);
		m_maincpu->set_input_line(INPUT_LINE_RESET, state);
	}
};

void quadra630_state::machine_start()
{
	m_f108->set_ram_info((u32 *) m_ram->pointer(), m_ram->size());
}

void quadra630_state::machine_reset()
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
}

void quadra630_state::init_macqd630()
{
}

/***************************************************************************
    ADDRESS MAPS
***************************************************************************/
void quadra630_state::quadra630_map(address_map &map)
{
	map(0x00000000, 0xffffffff).m(m_f108, FUNC(f108_device::map));
	map(0x00000000, 0xffffffff).m(m_video, FUNC(valkyrie_device::map));
	map(0x50000000, 0x5fffffff).m(m_primetimeii, FUNC(primetime_device::map));

	// 5000a000 = SONIC if comm slot card is installed
	map(0x5000a000, 0x5000bfff).noprw().mirror(0x00fc0000);

	// 2252 = Q630, 225a = LC580
	map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a2252; }));
}

void quadra630_state::lc580_map(address_map &map)
{
	quadra630_map(map);

	map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a225a; }));
}

/***************************************************************************
    DEVICE CONFIG
***************************************************************************/

static INPUT_PORTS_START( macadb )
INPUT_PORTS_END

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

void quadra630_state::macqd630(machine_config &config)
{
	M68040(config, m_maincpu, 33_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &quadra630_state::quadra630_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	F108(config, m_f108, 33_MHz_XTAL);
	m_f108->set_maincpu_tag("maincpu");
	m_f108->set_primetimeii_tag("primetimeii");
	m_f108->set_rom_tag("bootrom");
	m_f108->write_ata_irq().set(m_primetimeii, FUNC(primetimeii_device::ata_irq_w));

	PRIMETIMEII(config, m_primetimeii, 33_MHz_XTAL);
	m_primetimeii->set_maincpu_tag("maincpu");
	m_primetimeii->set_scsi_tag("f108:scsi:7:ncr53c96");

	VALKYRIE(config, m_video, C32M);
	m_video->write_irq().set(m_primetimeii, FUNC(primetime_device::via2_irq_w<0x40>));

	MACADB(config, m_macadb, C15M);

	CUDA_V2XX(config, m_cuda, XTAL(32'768));
	m_cuda->set_default_bios_tag("341s0060");
	m_cuda->reset_callback().set(FUNC(quadra630_state::cuda_reset_w));
	m_cuda->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
	m_cuda->via_clock_callback().set(m_primetimeii, FUNC(primetime_device::cb1_w));
	m_cuda->via_data_callback().set(m_primetimeii, FUNC(primetime_device::cb2_w));
	m_cuda->nmi_callback().set_inputline(m_maincpu, M68K_IRQ_7);
	m_macadb->adb_data_callback().set(m_cuda, FUNC(cuda_device::set_adb_line));
	m_macadb->adb_power_callback().set(m_cuda, FUNC(cuda_device::set_adb_power));
	config.set_perfect_quantum(m_maincpu);

	input_merger_device &sda_merger(INPUT_MERGER_ALL_HIGH(config, "sda"));
	sda_merger.output_handler().append(m_cuda, FUNC(cuda_device::set_iic_sda));

	m_cuda->iic_sda_callback().set(sda_merger, FUNC(input_merger_device::in_w<0>));
	m_cuda->iic_sda_callback().append(m_video, FUNC(valkyrie_device::sda_write));
	m_cuda->iic_scl_callback().set(m_video, FUNC(valkyrie_device::scl_write));

	m_video->sda_callback().set(sda_merger, FUNC(input_merger_device::in_w<1>));

	APPLE_DFAC2(config, m_dfac2, 22257);
	m_dfac2->sda_callback().set(sda_merger, FUNC(input_merger_device::in_w<2>));
	m_cuda->iic_scl_callback().append(m_dfac2, FUNC(dfac2_device::scl_write));
	m_cuda->iic_sda_callback().append(m_dfac2, FUNC(dfac2_device::sda_write));

	m_primetimeii->pb3_callback().set(m_cuda, FUNC(cuda_device::get_treq));
	m_primetimeii->pb4_callback().set(m_cuda, FUNC(cuda_device::set_byteack));
	m_primetimeii->pb5_callback().set(m_cuda, FUNC(cuda_device::set_tip));
	m_primetimeii->write_cb2().set(m_cuda, FUNC(cuda_device::set_via_data));

	nubus_device &nubus(NUBUS(config, "pds", 0));
	nubus.set_space(m_maincpu, AS_PROGRAM);
	nubus.set_bus_mode(nubus_device::nubus_mode_t::QUADRA_DAFB);
	nubus.out_irqe_callback().set(m_primetimeii, FUNC(primetime_device::via2_irq_w<0x20>));
	NUBUS_SLOT(config, "lcpds", "pds", mac_pdslc_cards, nullptr);

	/* internal ram */
	RAM(config, m_ram);
	m_ram->set_default_size("4M");
	m_ram->set_extra_options("8M,16M,32M");
}

void quadra630_state::maclc580(machine_config &config)
{
	macqd630(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &quadra630_state::lc580_map);
}

ROM_START( macqd630 )
	ROM_REGION32_BE(0x100000, "bootrom", 0)
	ROM_LOAD( "06684214.bin", 0x000000, 0x100000, CRC(1735e7a5) SHA1(47cd505b6a7c46e5c0ffa29f0d5037c83e94a02f) )
ROM_END

ROM_START( maclc580 )
	ROM_REGION32_BE(0x100000, "bootrom", 0)
	ROM_SYSTEM_BIOS(0, "older", "Version 32F1")
	ROMX_LOAD("06684214.bin", 0x000000, 0x100000, CRC(1735e7a5) SHA1(47cd505b6a7c46e5c0ffa29f0d5037c83e94a02f), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "later", "Version 32F2 (bug: can't boot CD-ROM)")
	ROMX_LOAD( "064dc91d.bin", 0x000000, 0x100000, CRC(59e6960f) SHA1(f48a8adf06bce50beee033d0d814da0e5e916d08), ROM_BIOS(1))
ROM_END

} // anonymous namespace

COMP( 1994, macqd630, 0, 0, macqd630, macadb, quadra630_state, init_macqd630,  "Apple Computer", "Macintosh Quadra 630", MACHINE_SUPPORTS_SAVE)
COMP( 1995, maclc580, macqd630, 0, maclc580, macadb, quadra630_state, init_macqd630,  "Apple Computer", "Macintosh LC/Performa 580", MACHINE_SUPPORTS_SAVE)



macquadra700.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/****************************************************************************

    drivers/macquadra700.cpp
    Mac Quadra 700 ("Spike"), 900 ("Eclipse"), and 950 ("Zydeco") emulation

    Emulation by R. Belmont

    These machines were sort of a "workstation class Mac II", with fast
    built-in video, built-in Ethernet, and enhanced NuBus '90 slots that
    were backwards compatible but also had some new tricks.

    The Quadra 900 replaced the real-time clock with a compatible variant
    of Egret, and offloaded ADB, serial, LocalTalk, and floppy processing
    to the same pair of 65C02-based IOPs (I/O Processors) from the Mac IIfx.
    This setup never appeared again, but its features, including doing real
    DMA to and from the floppy drive, did.

****************************************************************************/

#include "emu.h"

#include "adbmodem.h"
#include "dafb.h"
#include "dfac.h"
#include "egret.h"
#include "macadb.h"
#include "macrtc.h"
#include "mactoolbox.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/devices.h"
#include "bus/nubus/cards.h"
#include "bus/nubus/nubus.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68040.h"
#include "machine/6522via.h"
#include "machine/applefdintf.h"
#include "machine/applepic.h"
#include "machine/dp83932c.h"
#include "machine/ncr53c90.h"
#include "machine/nscsi_bus.h"
#include "machine/ram.h"
#include "machine/swim1.h"
#include "machine/timer.h"
#include "machine/z80scc.h"
#include "sound/asc.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "formats/ap_dsk35.h"

#define C32M 31.3344_MHz_XTAL
#define C15M (C32M/2)
#define C7M (C32M/4)

namespace {
class quadrax00_state : public driver_device
{
public:
	quadrax00_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_via1(*this, "via1"),
		m_via2(*this, "via2"),
		m_macadb(*this, "macadb"),
		m_ram(*this, RAM_TAG),
		m_swim(*this, "fdc"),
		m_floppy(*this, "fdc:%d", 0U),
		m_scsibus1(*this, "scsi"),
		m_ncr1(*this, "scsi:7:ncr53c96"),
		m_sonic(*this, "sonic"),
		m_dafb(*this, "dafb"),
		m_easc(*this, "easc"),
		m_dfac(*this, "dfac"),
		m_scc(*this, "scc"),
		m_cur_floppy(nullptr),
		m_hdsel(0),
		m_adb_irq_pending(0),
		m_ram_ptr(nullptr), m_rom_ptr(nullptr),
		m_ram_mask(0), m_ram_size(0), m_rom_size(0),
		m_overlay(0),
		m_via2_ca1_hack(0),
		m_nubus_irq_state(0),
		m_via_interrupt(0), m_via2_interrupt(0), m_scc_interrupt(0), m_last_taken_interrupt(0)
	{
	}

	void quadra_base(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	u32 rom_switch_r(offs_t offset);
	u16 via_r(offs_t offset);
	void via_w(offs_t offset, u16 data, u16 mem_mask);
	u16 via2_r(offs_t offset);
	void via2_w(offs_t offset, u16 data, u16 mem_mask);
	u8 ethernet_mac_r(offs_t offset);
	u16 scc_r(offs_t offset);
	void scc_w(offs_t offset, u16 data);
	u16 swim_r(offs_t offset, u16 mem_mask);
	void swim_w(offs_t offset, u16 data, u16 mem_mask);
	void adb_irq_w(int state) { m_adb_irq_pending = state; }
	void nubus_slot_interrupt(u8 slot, u32 state);
	void dafb_irq_w(int state) { nubus_slot_interrupt(0xf, state); }

	required_device<m68040_device> m_maincpu;
	required_device<via6522_device> m_via1, m_via2;
	required_device<macadb_device> m_macadb;
	required_device<ram_device> m_ram;
	required_device<applefdintf_device> m_swim;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<nscsi_bus_device> m_scsibus1;
	required_device<ncr53c96_device> m_ncr1;
	required_device<dp83932c_device> m_sonic;
	required_device<dafb_device> m_dafb;
	required_device<asc_device> m_easc;
	required_device<dfac_device> m_dfac;
	required_device<z80scc_device> m_scc;

	floppy_image_device *m_cur_floppy;
	int m_hdsel;
	int m_adb_irq_pending;
	u32 *m_ram_ptr, *m_rom_ptr;
	u32 m_ram_mask, m_ram_size, m_rom_size;
	bool m_overlay;

	void scc_irq_w(int state);

private:
	void nubus_irq_9_w(int state);
	void nubus_irq_a_w(int state);
	void nubus_irq_b_w(int state);
	void nubus_irq_c_w(int state);
	void nubus_irq_d_w(int state);
	void nubus_irq_e_w(int state);

	u8 via2_in_a();
	u8 via2_in_b();
	void via2_out_a(u8 data);
	void via2_out_b(u8 data);
	void via_sync();
	void field_interrupts();
	void via_irq(int state);
	void via2_irq(int state);
	void phases_w(u8 phases);
	void devsel_w(u8 devsel);

	u8 m_mac[6];
	int m_via2_ca1_hack, m_nubus_irq_state;
	int m_via_interrupt, m_via2_interrupt, m_scc_interrupt, m_last_taken_interrupt;
};

class spike_state : public quadrax00_state
{
public:
	spike_state(const machine_config &mconfig, device_type type, const char *tag) :
		quadrax00_state(mconfig, type, tag),
		m_adbmodem(*this, "adbmodem"),
		m_rtc(*this,"rtc")
	{
	}

	void quadra700_map(address_map &map) ATTR_COLD;
	void macqd700(machine_config &config);

private:
	u8 via_in_a();
	u8 via_in_b();
	void via_out_a(u8 data);
	void via_out_b(u8 data);

	required_device<adbmodem_device> m_adbmodem;
	required_device<rtc3430042_device> m_rtc;
};

class eclipse_state : public quadrax00_state
{
public:
	eclipse_state(const machine_config &mconfig, device_type type, const char *tag) :
		quadrax00_state(mconfig, type, tag),
		m_sccpic(*this, "sccpic"),
		m_swimpic(*this, "swimpic"),
		m_egret(*this, "egret"),
		m_scsibus2(*this, "scsi2"),
		m_ncr2(*this, "scsi2:7:ncr53c96"),
		m_adb_in(0)
	{
	}

	void quadra900_map(address_map &map) ATTR_COLD;
	void macqd900(machine_config &config);
	void macqd950(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void egret_reset_w(int state);
	void fdc_hdsel(int state);

	void set_adb_line(int linestate) { m_adb_in = (linestate == ASSERT_LINE) ? true : false; }
	int adbin_r() { return m_adb_in; }

	required_device<applepic_device> m_sccpic, m_swimpic;
	required_device<egret_device> m_egret;
	required_device<nscsi_bus_device> m_scsibus2;
	required_device<ncr53c96_device> m_ncr2;

	int m_adb_in;

private:
	u8 via_in_a();
	u8 via_in_a_q950();
	u8 via_in_b();
	void via_out_a(u8 data);
	void via_out_b(u8 data);
	void via2_out_b_q900(u8 data);
};

void quadrax00_state::field_interrupts()
{
	int take_interrupt = -1;

	if (m_scc_interrupt)
	{
		take_interrupt = 4;
	}
	else if (m_via2_interrupt)
	{
		take_interrupt = 2;
	}
	else if (m_via_interrupt)
	{
		take_interrupt = 1;
	}

	if (m_last_taken_interrupt > -1)
	{
		m_maincpu->set_input_line(m_last_taken_interrupt, CLEAR_LINE);
		m_last_taken_interrupt = -1;
	}

	if (take_interrupt > -1)
	{
		m_maincpu->set_input_line(take_interrupt, ASSERT_LINE);
		m_last_taken_interrupt = take_interrupt;
	}
}

void quadrax00_state::machine_start()
{
	m_dafb->set_turboscsi1_device(m_ncr1);
	m_dafb->set_turboscsi2_device(nullptr);

	// MAC PROM is stored with a bit swizzle and must match one of 2
	// Apple-assigned OUI blocks 00:05:02 or 08:00:07
	const std::array<u8, 6> &MAC = m_sonic->get_mac();
	m_mac[0] = bitswap<8>(0x00, 0, 1, 2, 3, 7, 6, 5, 4);
	m_mac[1] = bitswap<8>(0x05, 0, 1, 2, 3, 7, 6, 5, 4);
	m_mac[2] = bitswap<8>(0x02, 0, 1, 2, 3, 7, 6, 5, 4);
	m_mac[3] = bitswap<8>(MAC[3], 0, 1, 2, 3, 7, 6, 5, 4);
	m_mac[4] = bitswap<8>(MAC[4], 0, 1, 2, 3, 7, 6, 5, 4);
	m_mac[5] = bitswap<8>(MAC[5], 0, 1, 2, 3, 7, 6, 5, 4);
	m_sonic->set_mac(&m_mac[0]);

	m_ram_ptr = (u32*)m_ram->pointer();
	m_ram_size = m_ram->size()>>1;
	m_ram_mask = m_ram_size - 1;
	m_rom_ptr = (u32*)memregion("bootrom")->base();
	m_rom_size = memregion("bootrom")->bytes();
	m_via_interrupt = m_via2_interrupt = m_scc_interrupt = 0;
	m_last_taken_interrupt = -1;

	save_item(NAME(m_via2_ca1_hack));
	save_item(NAME(m_nubus_irq_state));
	save_item(NAME(m_adb_irq_pending));
	save_item(NAME(m_hdsel));
	save_item(NAME(m_via_interrupt));
	save_item(NAME(m_via2_interrupt));
	save_item(NAME(m_scc_interrupt));
	save_item(NAME(m_last_taken_interrupt));
	save_item(NAME(m_overlay));
}

void quadrax00_state::machine_reset()
{
	m_nubus_irq_state = 0xff;
	m_via2_ca1_hack = 1;
	m_via2->write_ca1(1);
	m_via2->write_cb1(1);
	m_overlay = true;
	m_via_interrupt = m_via2_interrupt = m_scc_interrupt = 0;
	m_last_taken_interrupt = -1;

	// put ROM mirror at 0
	address_space& space = m_maincpu->space(AS_PROGRAM);
	const u32 memory_size = std::min((u32)0x3fffff, m_rom_size);
	const u32 memory_end = memory_size - 1;
	offs_t memory_mirror = memory_end & ~(memory_size - 1);

	space.unmap_write(0x00000000, memory_end);
	space.install_rom(0x00000000, memory_end & ~memory_mirror, memory_mirror, m_rom_ptr);
}

void eclipse_state::machine_start()
{
	quadrax00_state::machine_start();

	m_dafb->set_turboscsi2_device(m_ncr2);

	save_item(NAME(m_adb_in));
}

void eclipse_state::machine_reset()
{
	quadrax00_state::machine_reset();

	// halt 680x0 until Caboose wakes us up
	m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
}

void quadrax00_state::nubus_slot_interrupt(u8 slot, u32 state)
{
	static const u8 masks[8] = { 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 };
	u8 mask = 0xff;

	slot -= 9;

	if (state)
	{
		m_nubus_irq_state &= ~masks[slot];
	}
	else
	{
		m_nubus_irq_state |= masks[slot];
	}

	if ((m_nubus_irq_state & mask) != mask)
	{
		// HACK: sometimes we miss an ack (possible misbehavior in the VIA?)
		if (m_via2_ca1_hack == 0)
		{
			m_via2->write_ca1(1);
		}
		m_via2_ca1_hack = 0;
		m_via2->write_ca1(0);
	}
	else
	{
		m_via2_ca1_hack = 1;
		m_via2->write_ca1(1);
	}
}

void quadrax00_state::nubus_irq_9_w(int state) { nubus_slot_interrupt(9, state); }
void quadrax00_state::nubus_irq_a_w(int state) { nubus_slot_interrupt(0xa, state); }
void quadrax00_state::nubus_irq_b_w(int state) { nubus_slot_interrupt(0xb, state); }
void quadrax00_state::nubus_irq_c_w(int state) { nubus_slot_interrupt(0xc, state); }
void quadrax00_state::nubus_irq_d_w(int state) { nubus_slot_interrupt(0xd, state); }
void quadrax00_state::nubus_irq_e_w(int state) { nubus_slot_interrupt(0xe, state); }

void quadrax00_state::scc_irq_w(int state)
{
	m_scc_interrupt = state;
	field_interrupts();
}

u16 quadrax00_state::scc_r(offs_t offset)
{
	via_sync();
	return m_scc->dc_ab_r(offset) << 8;
}

void quadrax00_state::scc_w(offs_t offset, u16 data)
{
	via_sync();
	m_scc->dc_ab_w(offset, data >> 8);
}

u16 quadrax00_state::swim_r(offs_t offset, u16 mem_mask)
{
	if (!machine().side_effects_disabled())
	{
		m_maincpu->adjust_icount(-5);
	}

	u16 result = m_swim->read((offset >> 8) & 0xf);
	return result << 8;
}

void quadrax00_state::swim_w(offs_t offset, u16 data, u16 mem_mask)
{
	m_swim->write((offset >> 8) & 0xf, data >> 8);

	if (!machine().side_effects_disabled())
		m_maincpu->adjust_icount(-5);
}

void eclipse_state::fdc_hdsel(int state)
{
	if (state != m_hdsel)
	{
		if (m_cur_floppy)
		{
			m_cur_floppy->ss_w(state);
		}
	}
	m_hdsel = state;
}

void eclipse_state::egret_reset_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state);
	m_maincpu->set_input_line(INPUT_LINE_RESET, state);
}

u16 quadrax00_state::via_r(offs_t offset)
{
	u16 data;

	offset >>= 8;
	offset &= 0x0f;

	if (!machine().side_effects_disabled())
		via_sync();

	data = m_via1->read(offset);

	return (data & 0xff) | (data << 8);
}

void quadrax00_state::via_w(offs_t offset, u16 data, u16 mem_mask)
{
	offset >>= 8;
	offset &= 0x0f;

	via_sync();

	if (ACCESSING_BITS_0_7)
		m_via1->write(offset, data & 0xff);
	if (ACCESSING_BITS_8_15)
		m_via1->write(offset, (data >> 8) & 0xff);
}

void quadrax00_state::via_irq(int state)
{
	m_via_interrupt = state;
	field_interrupts();
}

void quadrax00_state::via2_irq(int state)
{
	m_via2_interrupt = state;
	field_interrupts();
}

u16 quadrax00_state::via2_r(offs_t offset)
{
	int data;

	offset >>= 8;
	offset &= 0x0f;

	if (!machine().side_effects_disabled())
		via_sync();

	data = m_via2->read(offset);
	return (data & 0xff) | (data << 8);
}

void quadrax00_state::via2_w(offs_t offset, u16 data, u16 mem_mask)
{
	offset >>= 8;
	offset &= 0x0f;

	via_sync();

	if (ACCESSING_BITS_0_7)
		m_via2->write(offset, data & 0xff);
	if (ACCESSING_BITS_8_15)
		m_via2->write(offset, (data >> 8) & 0xff);
}

void quadrax00_state::via_sync()
{
	// The via runs at 783.36KHz while the main cpu runs at 15MHz or
	// more, so we need to sync the access with the via clock.  Plus
	// the whole access takes half a (via) cycle and ends when synced
	// with the main cpu again.

	// Get the main cpu time
	u64 cycle = m_maincpu->total_cycles();

	// Get the number of the cycle the via is in at that time
	u64 via_cycle = cycle * m_via1->clock() / m_maincpu->clock();

	// The access is going to start at via_cycle+1 and end at
	// via_cycle+1.5, compute what that means in maincpu cycles (the
	// +1 rounds up, since the clocks are too different to ever be
	// synced).
	u64 main_cycle = (via_cycle * 2 + 3) * m_maincpu->clock() / (2 * m_via1->clock()) + 1;

	// Finally adjust the main cpu icount as needed.
	m_maincpu->adjust_icount(-int(main_cycle - cycle));
}

u32 quadrax00_state::rom_switch_r(offs_t offset)
{
	// disable the overlay
	if (m_overlay && !machine().side_effects_disabled())
	{
		address_space& space = m_maincpu->space(AS_PROGRAM);
		const u32 memory_end = m_ram->size() - 1;
		void *memory_data = m_ram->pointer();
		offs_t memory_mirror = memory_end & ~memory_end;

		space.install_ram(0x00000000, memory_end & ~memory_mirror, memory_mirror, memory_data);
		m_overlay = false;
	}

	return m_rom_ptr[offset & ((m_rom_size - 1)>>2)];
}

u8 quadrax00_state::ethernet_mac_r(offs_t offset)
{
	if (offset < 6)
	{
		return m_mac[offset];
	}
	else if (offset == 7)
	{
		u8 xor_total = 0;

		for (int i = 0; i < 6; i++)
		{
			xor_total ^= (u8)m_mac[i];
		}

		return xor_total ^ 0xff;
	}

	return 0;
}

/***************************************************************************
    ADDRESS MAPS
***************************************************************************/
void spike_state::quadra700_map(address_map &map)
{
	map(0x40000000, 0x400fffff).r(FUNC(spike_state::rom_switch_r)).mirror(0x0ff00000);

	map(0x50000000, 0x50001fff).rw(FUNC(spike_state::via_r), FUNC(spike_state::via_w)).mirror(0x00fc0000);
	map(0x50002000, 0x50003fff).rw(FUNC(spike_state::via2_r), FUNC(spike_state::via2_w)).mirror(0x00fc0000);
	map(0x50008000, 0x50008007).r(FUNC(spike_state::ethernet_mac_r)).mirror(0x00fc0000);
	map(0x5000a000, 0x5000b0ff).m(m_sonic, FUNC(dp83932c_device::map)).umask32(0x0000ffff).mirror(0x00fc0000);
	// 5000e000 = Orwell controls
	map(0x5000f000, 0x5000f0ff).rw(m_dafb, FUNC(dafb_device::turboscsi_r<0>), FUNC(dafb_device::turboscsi_w<0>)).mirror(0x00fc0000);
	map(0x5000f100, 0x5000f101).rw(m_dafb, FUNC(dafb_device::turboscsi_dma_r<0>), FUNC(dafb_device::turboscsi_dma_w<0>)).select(0x00fc0000);
	map(0x5000c000, 0x5000dfff).rw(FUNC(spike_state::scc_r), FUNC(spike_state::scc_w)).mirror(0x00fc0000);
	map(0x50014000, 0x50015fff).rw(m_easc, FUNC(asc_device::read), FUNC(asc_device::write)).mirror(0x00fc0000);
	map(0x5001e000, 0x5001ffff).rw(FUNC(spike_state::swim_r), FUNC(spike_state::swim_w)).mirror(0x00fc0000);

	map(0xf9000000, 0xf91fffff).rw(m_dafb, FUNC(dafb_device::vram_r), FUNC(dafb_device::vram_w));
	map(0xf9800000, 0xf98003ff).m(m_dafb, FUNC(dafb_device::map));
}

void eclipse_state::quadra900_map(address_map &map)
{
	map(0x40000000, 0x400fffff).r(FUNC(eclipse_state::rom_switch_r)).mirror(0x0ff00000);

	map(0x50000000, 0x50001fff).rw(FUNC(eclipse_state::via_r), FUNC(eclipse_state::via_w)).mirror(0x00fc0000);
	map(0x50002000, 0x50003fff).rw(FUNC(eclipse_state::via2_r), FUNC(eclipse_state::via2_w)).mirror(0x00fc0000);
	map(0x50008000, 0x50008007).r(FUNC(eclipse_state::ethernet_mac_r)).mirror(0x00fc0000);
	map(0x5000a000, 0x5000b0ff).m(m_sonic, FUNC(dp83932c_device::map)).umask32(0x0000ffff).mirror(0x00fc0000);
	map(0x5000c000, 0x5000cfff).rw(m_sccpic, FUNC(applepic_device::host_r), FUNC(applepic_device::host_w)).mirror(0x00f00000).umask32(0xff00ff00);
	map(0x5000c000, 0x5000cfff).rw(m_sccpic, FUNC(applepic_device::host_r), FUNC(applepic_device::host_w)).mirror(0x00f00000).umask32(0x00ff00ff);
	map(0x5000f000, 0x5000f0ff).rw(m_dafb, FUNC(dafb_device::turboscsi_r<0>), FUNC(dafb_device::turboscsi_w<0>)).mirror(0x00fc0000);
	map(0x5000f100, 0x5000f101).rw(m_dafb, FUNC(dafb_device::turboscsi_dma_r<0>), FUNC(dafb_device::turboscsi_dma_w<0>)).select(0x00fc0000);
	map(0x5000f400, 0x5000f4ff).rw(m_dafb, FUNC(dafb_device::turboscsi_r<1>), FUNC(dafb_device::turboscsi_w<1>)).mirror(0x00fc0000);
	map(0x5000f502, 0x5000f503).rw(m_dafb, FUNC(dafb_device::turboscsi_dma_r<1>), FUNC(dafb_device::turboscsi_dma_w<1>)).select(0x00fc0000);

	map(0x50014000, 0x50015fff).rw(m_easc, FUNC(asc_device::read), FUNC(asc_device::write)).mirror(0x00fc0000);
	map(0x5001e000, 0x5001efff).rw(m_swimpic, FUNC(applepic_device::host_r), FUNC(applepic_device::host_w)).mirror(0x00f00000).umask32(0xff00ff00);
	map(0x5001e000, 0x5001efff).rw(m_swimpic, FUNC(applepic_device::host_r), FUNC(applepic_device::host_w)).mirror(0x00f00000).umask32(0x00ff00ff);

	map(0xf9000000, 0xf91fffff).rw(m_dafb, FUNC(dafb_device::vram_r), FUNC(dafb_device::vram_w));
	map(0xf9800000, 0xf98003ff).m(m_dafb, FUNC(dafb_device::map));
}

u8 spike_state::via_in_a()
{
	return 0xc1;
}

u8 spike_state::via_in_b()
{
	u8 val = m_rtc->data_r();

	if (!m_adb_irq_pending)
	{
		val |= 0x08;
	}

	return val;
}

void spike_state::via_out_a(u8 data)
{
	int hdsel = BIT(data, 5);
	if (hdsel != m_hdsel)
	{
		if (m_cur_floppy)
		{
			m_cur_floppy->ss_w(hdsel);
		}
	}
	m_hdsel = hdsel;
}

void spike_state::via_out_b(u8 data)
{
	m_adbmodem->set_via_state((data & 0x30) >> 4);

	m_rtc->ce_w((data & 0x04)>>2);
	m_rtc->data_w(data & 0x01);
	m_rtc->clk_w((data >> 1) & 0x01);
}

u8 quadrax00_state::via2_in_a()
{
	return 0x80 | m_nubus_irq_state;
}

u8 quadrax00_state::via2_in_b()
{
	return 0xcf;        // indicate no NuBus transaction error
}

void quadrax00_state::via2_out_a(u8 data)
{
}

void quadrax00_state::via2_out_b(u8 data)
{
	// chain 60.15 Hz to VIA1
	m_via1->write_ca1(data>>7);

	m_dfac->data_write(BIT(data, 3));
	m_dfac->clock_write(BIT(data, 4));
	m_dfac->latch_write(BIT(data, 0));
}

void eclipse_state::via2_out_b_q900(u8 data)
{
	// chain 60.15 Hz to VIA1
	m_via1->write_ca1(data >> 7);
}

	u8 eclipse_state::via_in_a()
	{
		return 0xd1;
	}

	u8 eclipse_state::via_in_a_q950()
	{
		return 0x91;
	}

	u8 eclipse_state::via_in_b()
	{
		return m_egret->get_xcvr_session() << 3;
	}

	void eclipse_state::via_out_a(u8 data)
	{
	}

	void eclipse_state::via_out_b(u8 data)
	{
		m_egret->set_via_full(BIT(data, 4));
		m_egret->set_sys_session(BIT(data, 5));
	}

	void quadrax00_state::phases_w(u8 phases)
	{
		if (m_cur_floppy)
		{
			m_cur_floppy->seek_phase_w(phases);
		}
	}

	void quadrax00_state::devsel_w(u8 devsel)
	{
		if (devsel == 1)
		{
			m_cur_floppy = m_floppy[0]->get_device();
		}
		else if (devsel == 2)
		{
			m_cur_floppy = m_floppy[1]->get_device();
		}
		else
		{
			m_cur_floppy = nullptr;
		}
		m_swim->set_floppy(m_cur_floppy);
		if (m_cur_floppy)
		{
			m_cur_floppy->ss_w(m_hdsel);
		}
	}

	/***************************************************************************
	    DEVICE CONFIG
	***************************************************************************/

	static INPUT_PORTS_START(macadb)
		INPUT_PORTS_END

		/***************************************************************************
		    MACHINE DRIVERS
		***************************************************************************/

		void
		quadrax00_state::quadra_base(machine_config & config)
	{
		DAFB(config, m_dafb, 50_MHz_XTAL / 2);
		m_dafb->set_maincpu_tag("maincpu");
		m_dafb->dafb_irq().set(FUNC(quadrax00_state::dafb_irq_w));

		SWIM1(config, m_swim, C15M);
		m_swim->phases_cb().set(FUNC(quadrax00_state::phases_w));
		m_swim->devsel_cb().set(FUNC(quadrax00_state::devsel_w));

		applefdintf_device::add_35_hd(config, m_floppy[0]);
		applefdintf_device::add_35_nc(config, m_floppy[1]);

		SCC8530(config, m_scc, C7M);
		m_scc->configure_channels(3'686'400, 3'686'400, 3'686'400, 3'686'400);
		m_scc->out_txda_callback().set("printer", FUNC(rs232_port_device::write_txd));
		m_scc->out_txdb_callback().set("modem", FUNC(rs232_port_device::write_txd));

		rs232_port_device &rs232a(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
		rs232a.rxd_handler().set(m_scc, FUNC(z80scc_device::rxa_w));
		rs232a.dcd_handler().set(m_scc, FUNC(z80scc_device::dcda_w));
		rs232a.cts_handler().set(m_scc, FUNC(z80scc_device::ctsa_w));

		rs232_port_device &rs232b(RS232_PORT(config, "modem", default_rs232_devices, nullptr));
		rs232b.rxd_handler().set(m_scc, FUNC(z80scc_device::rxb_w));
		rs232b.dcd_handler().set(m_scc, FUNC(z80scc_device::dcdb_w));
		rs232b.cts_handler().set(m_scc, FUNC(z80scc_device::ctsb_w));

		// SCSI bus and devices
		NSCSI_BUS(config, m_scsibus1);
		NSCSI_CONNECTOR(config, "scsi:0", mac_scsi_devices, nullptr);
		NSCSI_CONNECTOR(config, "scsi:1", mac_scsi_devices, nullptr);
		NSCSI_CONNECTOR(config, "scsi:2", mac_scsi_devices, nullptr);
		NSCSI_CONNECTOR(config, "scsi:3").option_set("cdrom", NSCSI_CDROM_APPLE).machine_config([](device_t *device)
																								 {
			device->subdevice<cdda_device>("cdda")->add_route(0, "^^speaker", 1.0, 0);
			device->subdevice<cdda_device>("cdda")->add_route(1, "^^speaker", 1.0, 1); });
		NSCSI_CONNECTOR(config, "scsi:4", mac_scsi_devices, nullptr);
		NSCSI_CONNECTOR(config, "scsi:5", mac_scsi_devices, nullptr);
		NSCSI_CONNECTOR(config, "scsi:6", mac_scsi_devices, "harddisk");
		NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr53c96", NCR53C96).clock(50_MHz_XTAL / 2).machine_config([this](device_t *device)
																												  {
			ncr53c96_device &adapter = downcast<ncr53c96_device &>(*device);

			adapter.set_busmd(ncr53c96_device::BUSMD_1);
			adapter.irq_handler_cb().set(m_via2, FUNC(via6522_device::write_cb2)).invert();
			adapter.drq_handler_cb().set(m_dafb, FUNC(dafb_device::turboscsi_drq_w<0>)); });

		DP83932C(config, m_sonic, 40_MHz_XTAL / 2); // clock is C20M on the schematics
		m_sonic->set_bus(m_maincpu, 0);
		m_sonic->out_int_cb().set(m_via2, FUNC(via6522_device::write_pa0)).invert(); // IRQ is active low

		nubus_device &nubus(NUBUS(config, "nubus", 40_MHz_XTAL / 4));
		nubus.set_space(m_maincpu, AS_PROGRAM);
		nubus.set_bus_mode(nubus_device::nubus_mode_t::QUADRA_DAFB);
		nubus.out_irq9_callback().set(FUNC(quadrax00_state::nubus_irq_9_w));
		nubus.out_irqa_callback().set(FUNC(quadrax00_state::nubus_irq_a_w));
		nubus.out_irqb_callback().set(FUNC(quadrax00_state::nubus_irq_b_w));
		nubus.out_irqc_callback().set(FUNC(quadrax00_state::nubus_irq_c_w));
		nubus.out_irqd_callback().set(FUNC(quadrax00_state::nubus_irq_d_w));
		nubus.out_irqe_callback().set(FUNC(quadrax00_state::nubus_irq_e_w));
		NUBUS_SLOT(config, "nbd", "nubus", mac_nubus_cards, nullptr);
		NUBUS_SLOT(config, "nbe", "nubus", mac_nubus_cards, nullptr);

		R65NC22(config, m_via1, C7M / 10);
		m_via1->irq_handler().set(FUNC(quadrax00_state::via_irq));

		R65NC22(config, m_via2, C7M / 10);
		m_via2->readpa_handler().set(FUNC(quadrax00_state::via2_in_a));
		m_via2->readpb_handler().set(FUNC(quadrax00_state::via2_in_b));
		m_via2->writepa_handler().set(FUNC(quadrax00_state::via2_out_a));
		m_via2->writepb_handler().set(FUNC(quadrax00_state::via2_out_b));
		m_via2->irq_handler().set(FUNC(quadrax00_state::via2_irq));

		MACADB(config, m_macadb, C15M);

		SPEAKER(config, "speaker", 2).front();
		ASC(config, m_easc, 22.5792_MHz_XTAL, asc_device::asc_type::EASC);
		m_easc->irqf_callback().set(m_via2, FUNC(via6522_device::write_cb1)).invert();
		m_easc->add_route(0, "speaker", 1.0, 0);
		m_easc->add_route(1, "speaker", 1.0, 1);

		// DFAC is only for audio input on Q700/Q800
		APPLE_DFAC(config, m_dfac, 22257);

		/* internal ram */
		RAM(config, m_ram);

		SOFTWARE_LIST(config, "hdd_list").set_original("mac_hdd");
		SOFTWARE_LIST(config, "cd_list").set_original("mac_cdrom").set_filter("MC68040");
		//SOFTWARE_LIST(config, "cd_apple_dev").set_original("apple_devcd");
		SOFTWARE_LIST(config, "flop_mac35_orig").set_original("mac_flop_orig");
		SOFTWARE_LIST(config, "flop_mac35_clean").set_original("mac_flop_clcracked");
		SOFTWARE_LIST(config, "flop35_list").set_original("mac_flop");
		SOFTWARE_LIST(config, "flop35hd_list").set_original("mac_hdflop");
	}

	void spike_state::macqd700(machine_config & config)
	{
		quadra_base(config);

		M68040(config, m_maincpu, 50_MHz_XTAL / 2);
		m_maincpu->set_addrmap(AS_PROGRAM, &spike_state::quadra700_map);
		m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

		RTC3430042(config, m_rtc, XTAL(32'768));
		m_rtc->cko_cb().set(m_via1, FUNC(via6522_device::write_ca2));

		ADBMODEM(config, m_adbmodem, C7M);
		m_adbmodem->via_clock_callback().set(m_via1, FUNC(via6522_device::write_cb1));
		m_adbmodem->via_data_callback().set(m_via1, FUNC(via6522_device::write_cb2));
		m_adbmodem->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
		m_adbmodem->irq_callback().set(FUNC(spike_state::adb_irq_w));
		m_via1->cb2_handler().set(m_adbmodem, FUNC(adbmodem_device::set_via_data));
		m_macadb->adb_data_callback().set(m_adbmodem, FUNC(adbmodem_device::set_adb_line));
		config.set_perfect_quantum(m_maincpu);

		m_via1->readpa_handler().set(FUNC(spike_state::via_in_a));
		m_via1->readpb_handler().set(FUNC(spike_state::via_in_b));
		m_via1->writepa_handler().set(FUNC(spike_state::via_out_a));
		m_via1->writepb_handler().set(FUNC(spike_state::via_out_b));

		// Q700 has 4 MB soldered in and 4 SIMM slots which can accept 1, 4, 8, or 16 MB SIMMs
		m_ram->set_default_size("4M");
		m_ram->set_extra_options("8M,20M,36M,68M"); // 4M + (4x1M SIMMs), (4x4M), (4x8M), (4x16M)
	}

	void eclipse_state::macqd900(machine_config & config)
	{
		quadra_base(config);

		M68040(config, m_maincpu, 50_MHz_XTAL / 2);
		m_maincpu->set_addrmap(AS_PROGRAM, &eclipse_state::quadra900_map);
		m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

		APPLEPIC(config, m_sccpic, C15M);
		m_sccpic->prd_callback().set(m_scc, FUNC(z80scc_device::dc_ab_r));
		m_sccpic->pwr_callback().set(m_scc, FUNC(z80scc_device::dc_ab_w));
		m_sccpic->hint_callback().set(FUNC(eclipse_state::scc_irq_w));

		m_scc->out_int_callback().set(m_sccpic, FUNC(applepic_device::pint_w));
		m_scc->out_wreqa_callback().set(m_sccpic, FUNC(applepic_device::reqa_w)).invert();
		m_scc->out_wreqb_callback().set(m_sccpic, FUNC(applepic_device::reqb_w)).invert();

		APPLEPIC(config, m_swimpic, C15M);
		m_swimpic->prd_callback().set(m_swim, FUNC(applefdintf_device::read));
		m_swimpic->pwr_callback().set(m_swim, FUNC(applefdintf_device::write));
		m_swimpic->hint_callback().set(m_via2, FUNC(via6522_device::write_ca2)).invert();
		m_swimpic->gpout0_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w)).invert();
		m_swimpic->gpin_callback().set(FUNC(eclipse_state::adbin_r));

		m_swim->dat1byte_cb().set(m_swimpic, FUNC(applepic_device::reqa_w));
		m_swim->dat1byte_cb().append(m_swimpic, FUNC(applepic_device::reqb_w));
		m_swim->hdsel_cb().set(FUNC(eclipse_state::fdc_hdsel));

		// TODO: this is supposed to use a special version of Egret called "Caboose",
		// but currently that version of the program boots up and refuses to listen
		// to commands from the 68040.  Stock Egret works fine until that gets figured out.
		EGRET(config, m_egret, XTAL(32'768));
		m_egret->set_default_bios_tag("341s0851");
		m_egret->reset_callback().set(FUNC(eclipse_state::egret_reset_w));
		m_egret->via_clock_callback().set(m_via1, FUNC(via6522_device::write_cb1));
		m_egret->via_data_callback().set(m_via1, FUNC(via6522_device::write_cb2));
		m_egret->dfac_scl_callback().set(m_dfac, FUNC(dfac_device::clock_write));
		m_egret->dfac_sda_callback().set(m_dfac, FUNC(dfac_device::data_write));
		m_egret->dfac_latch_callback().set(m_dfac, FUNC(dfac_device::latch_write));
		m_via1->cb2_handler().set(m_egret, FUNC(egret_device::set_via_data));
		config.set_perfect_quantum(m_maincpu);

		NSCSI_BUS(config, m_scsibus2);
		NSCSI_CONNECTOR(config, "scsi2:0", mac_scsi_devices, nullptr);
		NSCSI_CONNECTOR(config, "scsi2:1", mac_scsi_devices, nullptr);
		NSCSI_CONNECTOR(config, "scsi2:2", mac_scsi_devices, nullptr);
		NSCSI_CONNECTOR(config, "scsi2:3", mac_scsi_devices, nullptr);
		NSCSI_CONNECTOR(config, "scsi2:4", mac_scsi_devices, nullptr);
		NSCSI_CONNECTOR(config, "scsi2:5", mac_scsi_devices, nullptr);
		NSCSI_CONNECTOR(config, "scsi2:6", mac_scsi_devices, nullptr);
		NSCSI_CONNECTOR(config, "scsi2:7").option_set("ncr53c96", NCR53C96).clock(50_MHz_XTAL / 2).machine_config([this](device_t *device)
																												  {
		ncr53c96_device &adapter = downcast<ncr53c96_device &>(*device);

		adapter.set_busmd(ncr53c96_device::BUSMD_1);
		adapter.irq_handler_cb().append(m_via2, FUNC(via6522_device::write_cb2)).invert();
		adapter.drq_handler_cb().set(m_dafb, FUNC(dafb_device::turboscsi_drq_w<1>)); });

		// 900 and 950 are 5-slot machines, so add the other 3
		NUBUS_SLOT(config, "nba", "nubus", mac_nubus_cards, nullptr);
		NUBUS_SLOT(config, "nbb", "nubus", mac_nubus_cards, nullptr);
		NUBUS_SLOT(config, "nbc", "nubus", mac_nubus_cards, nullptr);

		m_macadb->adb_data_callback().set(FUNC(eclipse_state::set_adb_line));
		m_macadb->adb_data_callback().append(m_egret, FUNC(egret_device::set_adb_line));

		m_via1->readpa_handler().set(FUNC(eclipse_state::via_in_a));
		m_via1->readpb_handler().set(FUNC(eclipse_state::via_in_b));
		m_via1->writepa_handler().set(FUNC(eclipse_state::via_out_a));
		m_via1->writepb_handler().set(FUNC(eclipse_state::via_out_b));
		m_via2->writepb_handler().set(FUNC(eclipse_state::via2_out_b_q900));

		// Q900/Q950 have no soldered RAM and 16 SIMM slots which can accept 1, 4, 8, or 16 MB SIMMs 4 at a time
		m_ram->set_default_size("4M");
		m_ram->set_extra_options("8M,16M,32M,64M,80M,96M,128M,192M,256M");
	}

	void eclipse_state::macqd950(machine_config & config)
	{
		macqd900(config);

		m_maincpu->set_clock(33.333_MHz_XTAL);

		DAFB_Q950(config.replace(), m_dafb, 50_MHz_XTAL / 2);
		m_dafb->set_maincpu_tag("maincpu");
		m_dafb->dafb_irq().set(FUNC(eclipse_state::dafb_irq_w));

		m_via1->readpa_handler().set(FUNC(eclipse_state::via_in_a_q950));
	}

	ROM_START(macqd700)
	ROM_REGION32_BE(0x100000, "bootrom", 0)
	ROM_LOAD( "420dbff3.rom", 0x000000, 0x100000, CRC(88ea2081) SHA1(7a8ee468d16e64f2ad10cb8d1a45e6f07cc9e212) )
ROM_END

ROM_START( macqd950 )
	ROM_REGION32_BE(0x100000, "bootrom", 0)
	ROM_LOAD("3dc27823.rom", 0x000000, 0x100000, CRC(0e11206e) SHA1(d61dba4a2d2cf9048244b713eaa294100063658d))
ROM_END

#define rom_macqd900 rom_macqd700

} // anonymous namespace

COMP( 1991, macqd700, 0, 0, macqd700, macadb, spike_state, empty_init, "Apple Computer", "Macintosh Quadra 700", MACHINE_SUPPORTS_SAVE)
COMP( 1991, macqd900, 0, 0, macqd900, macadb, eclipse_state, empty_init, "Apple Computer", "Macintosh Quadra 900", MACHINE_SUPPORTS_SAVE)
COMP( 1992, macqd950, 0, 0, macqd950, macadb, eclipse_state, empty_init, "Apple Computer", "Macintosh Quadra 950", MACHINE_SUPPORTS_SAVE)



macquadra800.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/****************************************************************************

    Mac Centris 610 ("WLCD")
    Mac Centris 650 ("Wombat 25")
    Mac Quadra 610 ("Speedbump 610")
    Mac Quadra 650 ("Speedbump 650")
    Mac Quadra 800 ("Wombat 33")

    By R. Belmont

    These second-generation 68040 machines shrunk the huge mass of separate
    chips found in the Quadra 700 down to a pair of ASICs, djMEMC (memory controller
    plus revised DAFB video) and IOSB (I/O bus adaptor with integrated VIAs,
    audio, "Turbo SCSI", and SWIM2 floppy).

****************************************************************************/

#include "emu.h"

#include "adbmodem.h"
#include "dfac.h"
#include "djmemc.h"
#include "iosb.h"
#include "macadb.h"
#include "mactoolbox.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/devices.h"
#include "bus/nubus/cards.h"
#include "bus/nubus/nubus.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68040.h"
#include "machine/dp83932c.h"
#include "machine/ncr53c90.h"
#include "machine/nscsi_bus.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "machine/z80scc.h"

#include "softlist_dev.h"

#define C32M 31.3344_MHz_XTAL
#define C15M (C32M/2)
#define C7M (C32M/4)

namespace {

class quadra800_state : public driver_device
{
public:
	quadra800_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_djmemc(*this, "djmemc"),
		m_iosb(*this, "iosb"),
		m_dfac(*this, "dfac"),
		m_macadb(*this, "macadb"),
		m_adbmodem(*this, "adbmodem"),
		m_scc(*this, "scc"),
		m_ram(*this, RAM_TAG),
		m_scsibus(*this, "scsi"),
		m_ncr1(*this, "scsi:7:ncr53c96"),
		m_sonic(*this, "sonic")
	{
	}

	void macqd800(machine_config &config);
	void macct610(machine_config &config);
	void macct650(machine_config &config);
	void macqd610(machine_config &config);
	void macqd650(machine_config &config);

	void quadra800_map(address_map &map) ATTR_COLD;

	void init_macqd800();

private:
	required_device<m68040_device> m_maincpu;
	required_device<djmemc_device> m_djmemc;
	required_device<iosb_device> m_iosb;
	required_device<dfac_device> m_dfac;
	required_device<macadb_device> m_macadb;
	required_device<adbmodem_device> m_adbmodem;
	required_device<z80scc_device> m_scc;
	required_device<ram_device> m_ram;
	required_device<nscsi_bus_device> m_scsibus;
	required_device<ncr53c96_device> m_ncr1;
	required_device<dp83932c_device> m_sonic;

	u8 m_mac[6];

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	u16 mac_scc_r(offs_t offset)
	{
		m_iosb->via_sync();
		u16 result = m_scc->dc_ab_r(offset);
		return (result << 8) | result;
	}
	void mac_scc_2_w(offs_t offset, u16 data) { m_iosb->via_sync(); m_scc->dc_ab_w(offset, data >> 8); }

	u8 ethernet_mac_r(offs_t offset);
};

void quadra800_state::machine_start()
{
	m_djmemc->set_ram_info((u32 *) m_ram->pointer(), m_ram->size());

	// MAC PROM is stored with a bit swizzle and must match one of 2
	// Apple-assigned OUI blocks 00:05:02 or 08:00:07
	const std::array<u8, 6> &MAC = m_sonic->get_mac();
	m_mac[0] = bitswap<8>(0x08, 0, 1, 2, 3, 7, 6, 5, 4);
	m_mac[1] = bitswap<8>(0x00, 0, 1, 2, 3, 7, 6, 5, 4);
	m_mac[2] = bitswap<8>(0x07, 0, 1, 2, 3, 7, 6, 5, 4);
	m_mac[3] = bitswap<8>(MAC[3], 0, 1, 2, 3, 7, 6, 5, 4);
	m_mac[4] = bitswap<8>(MAC[4], 0, 1, 2, 3, 7, 6, 5, 4);
	m_mac[5] = bitswap<8>(MAC[5], 0, 1, 2, 3, 7, 6, 5, 4);
	m_sonic->set_mac(&m_mac[0]);
}

void quadra800_state::machine_reset()
{
}

void quadra800_state::init_macqd800()
{
}

u8 quadra800_state::ethernet_mac_r(offs_t offset)
{
	if (offset < 6)
	{
		return m_mac[offset];
	}
	else if (offset == 7)
	{
		u8 xor_total = 0;

		for (int i = 0; i < 6; i++)
		{
			xor_total ^= (u8)m_mac[i];
		}

		return xor_total ^ 0xff;
	}

	return 0;
}

/***************************************************************************
    ADDRESS MAPS
***************************************************************************/
void quadra800_state::quadra800_map(address_map &map)
{
	map(0x00000000, 0xffffffff).m(m_djmemc, FUNC(djmemc_device::map));
	map(0x50000000, 0x5fffffff).m(m_iosb, FUNC(iosb_device::map));

	map(0x50008000, 0x50008007).r(FUNC(quadra800_state::ethernet_mac_r)).mirror(0x00fc0000);
	map(0x5000a000, 0x5000b0ff).m(m_sonic, FUNC(dp83932c_device::map)).umask32(0x0000ffff).mirror(0x00fc0000);
	map(0x5000c000, 0x5000dfff).rw(FUNC(quadra800_state::mac_scc_r), FUNC(quadra800_state::mac_scc_2_w)).mirror(0x00fc0000);
}

/***************************************************************************
    DEVICE CONFIG
***************************************************************************/

static INPUT_PORTS_START( macadb )
INPUT_PORTS_END

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

void quadra800_state::macqd800(machine_config &config)
{
	M68040(config, m_maincpu, 33_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &quadra800_state::quadra800_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	DJMEMC(config, m_djmemc, 33_MHz_XTAL);
	m_djmemc->set_maincpu_tag("maincpu");
	m_djmemc->set_rom_tag("bootrom");
	m_djmemc->write_irq().set(m_iosb, FUNC(iosb_device::via2_irq_w<0x40>));

	IOSB(config, m_iosb, 33_MHz_XTAL);
	m_iosb->set_maincpu_tag("maincpu");
	m_iosb->set_scsi_tag("scsi:7:ncr53c96");
	m_iosb->write_adb_st().set(m_adbmodem, FUNC(adbmodem_device::set_via_state));

	// Quadra 800 ID is 0x12
	m_iosb->read_pa1().set_constant(1);
	m_iosb->read_pa2().set_constant(0);
	m_iosb->read_pa4().set_constant(1);
	m_iosb->read_pa6().set_constant(0);

	APPLE_DFAC(config, m_dfac, 22257);
	m_iosb->write_dfac_clock().set(m_dfac, FUNC(dfac_device::clock_write));
	m_iosb->write_dfac_data().set(m_dfac, FUNC(dfac_device::data_write));
	m_iosb->write_dfac_latch().set(m_dfac, FUNC(dfac_device::latch_write));

	SCC85C30(config, m_scc, C7M);
	m_scc->configure_channels(3'686'400, 3'686'400, 3'686'400, 3'686'400);
	m_scc->out_int_callback().set(m_iosb, FUNC(iosb_device::scc_irq_w));
	m_scc->out_txda_callback().set("printer", FUNC(rs232_port_device::write_txd));
	m_scc->out_txdb_callback().set("modem", FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232a(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_scc, FUNC(z80scc_device::rxa_w));
	rs232a.dcd_handler().set(m_scc, FUNC(z80scc_device::dcda_w));
	rs232a.cts_handler().set(m_scc, FUNC(z80scc_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "modem", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_scc, FUNC(z80scc_device::rxb_w));
	rs232b.dcd_handler().set(m_scc, FUNC(z80scc_device::dcdb_w));
	rs232b.cts_handler().set(m_scc, FUNC(z80scc_device::ctsb_w));

	// SCSI bus and devices
	NSCSI_BUS(config, m_scsibus);
	NSCSI_CONNECTOR(config, "scsi:0", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:1", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3").option_set("cdrom", NSCSI_CDROM_APPLE).machine_config(
		[](device_t *device)
		{
			device->subdevice<cdda_device>("cdda")->add_route(0, "^^iosb:speaker", 1.0, 0);
			device->subdevice<cdda_device>("cdda")->add_route(1, "^^iosb:speaker", 1.0, 1);
		});
	NSCSI_CONNECTOR(config, "scsi:4", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", mac_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", mac_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr53c96", NCR53C96).clock(40_MHz_XTAL).machine_config(
		[this] (device_t *device)
		{
			ncr53c96_device &adapter = downcast<ncr53c96_device &>(*device);

			adapter.set_busmd(ncr53c96_device::BUSMD_1);
			adapter.irq_handler_cb().set(m_iosb, FUNC(iosb_device::scsi_irq_w));
			adapter.drq_handler_cb().set(m_iosb, FUNC(iosb_device::scsi_drq_w));
		});

	DP83932C(config, m_sonic, 40_MHz_XTAL / 2); // clock is C20M on the schematics
	m_sonic->set_bus(m_maincpu, 0);
	m_sonic->out_int_cb().set(m_iosb, FUNC(iosb_device::via2_irq_w<0x01>));

	nubus_device &nubus(NUBUS(config, "nubus", 40_MHz_XTAL / 4));
	nubus.set_space(m_maincpu, AS_PROGRAM);
	nubus.set_bus_mode(nubus_device::nubus_mode_t::QUADRA_DAFB);
	nubus.out_irqc_callback().set(m_iosb, FUNC(iosb_device::via2_irq_w<0x08>));
	nubus.out_irqd_callback().set(m_iosb, FUNC(iosb_device::via2_irq_w<0x10>));
	nubus.out_irqe_callback().set(m_iosb, FUNC(iosb_device::via2_irq_w<0x20>));
	NUBUS_SLOT(config, "nbc", "nubus", mac_nubus_cards, nullptr);
	NUBUS_SLOT(config, "nbd", "nubus", mac_nubus_cards, nullptr);
	NUBUS_SLOT(config, "nbe", "nubus", mac_nubus_cards, nullptr);

	ADBMODEM(config, m_adbmodem, C7M);
	m_adbmodem->via_clock_callback().set(m_iosb, FUNC(iosb_device::cb1_w));
	m_adbmodem->via_data_callback().set(m_iosb, FUNC(iosb_device::cb2_w));
	m_adbmodem->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
	m_adbmodem->irq_callback().set(m_iosb, FUNC(iosb_device::pb3_w));
	m_iosb->write_cb2().set(m_adbmodem, FUNC(adbmodem_device::set_via_data));
	config.set_perfect_quantum(m_maincpu);

	MACADB(config, m_macadb, C15M);
	m_macadb->adb_data_callback().set(m_adbmodem, FUNC(adbmodem_device::set_adb_line));

	/* internal ram */
	RAM(config, m_ram);
	m_ram->set_default_size("8M");
	m_ram->set_extra_options("16M,32M,64M,96M,128M,192M,256M,320M,384M,512M,640M");

	SOFTWARE_LIST(config, "hdd_list").set_original("mac_hdd");
	SOFTWARE_LIST(config, "cd_list").set_original("mac_cdrom").set_filter("MC68040");
	SOFTWARE_LIST(config, "flop_mac35_orig").set_original("mac_flop_orig");
	SOFTWARE_LIST(config, "flop_mac35_clean").set_original("mac_flop_clcracked");
	SOFTWARE_LIST(config, "flop35_list").set_original("mac_flop");
	SOFTWARE_LIST(config, "flop35hd_list").set_original("mac_hdflop");
}

void quadra800_state::macct610(machine_config &config)
{
	macqd800(config);

	M68040(config.replace(), m_maincpu, 20_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &quadra800_state::quadra800_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	// Centris 610 ID is 0x40
	m_iosb->read_pa1().set_constant(0);
	m_iosb->read_pa2().set_constant(0);
	m_iosb->read_pa4().set_constant(0);
	m_iosb->read_pa6().set_constant(1);
}

void quadra800_state::macct650(machine_config &config)
{
	macqd800(config);

	M68040(config.replace(), m_maincpu, 25_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &quadra800_state::quadra800_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	// Centris 650 ID is 0x46
	m_iosb->read_pa1().set_constant(1);
	m_iosb->read_pa2().set_constant(1);
	m_iosb->read_pa4().set_constant(0);
	m_iosb->read_pa6().set_constant(1);
}

void quadra800_state::macqd610(machine_config &config)
{
	macqd800(config);

	M68040(config.replace(), m_maincpu, 25_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &quadra800_state::quadra800_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	// Quadra 610 ID is 0x44
	m_iosb->read_pa1().set_constant(0);
	m_iosb->read_pa2().set_constant(1);
	m_iosb->read_pa4().set_constant(0);
	m_iosb->read_pa6().set_constant(1);
}

void quadra800_state::macqd650(machine_config &config)
{
	macqd800(config);

	M68040(config.replace(), m_maincpu, 33_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &quadra800_state::quadra800_map);
	m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");

	// Quadra 650 ID is 0x52
	m_iosb->read_pa1().set_constant(1);
	m_iosb->read_pa2().set_constant(0);
	m_iosb->read_pa4().set_constant(1);
	m_iosb->read_pa6().set_constant(1);
}

ROM_START( macqd800 )
	ROM_REGION32_BE(0x100000, "bootrom", 0)
	ROM_SYSTEM_BIOS(0, "default", "Version 23F2")
	ROMX_LOAD( "f1acad13.rom", 0x000000, 0x100000, CRC(4e70e3c0) SHA1(f2a9ce387019bf272c6e3459d961b30f28942ac5), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "original", "Version 23F1")
	ROMX_LOAD( "f1a6f343.rom", 0x000000, 0x100000, CRC(3318a935) SHA1(031f13bfd726a70cfe4c73c5967861bc77297a79), ROM_BIOS(1) )
ROM_END

#define rom_macct610 rom_macqd800
#define rom_macct650 rom_macqd800
#define rom_macqd610 rom_macqd800
#define rom_macqd650 rom_macqd800

} // anonymous namespace

COMP( 1993, macqd800, 0, 0, macqd800, macadb, quadra800_state, init_macqd800,  "Apple Computer", "Macintosh Quadra 800", MACHINE_SUPPORTS_SAVE)
COMP( 1993, macct610, macqd800, 0, macct610, macadb, quadra800_state, init_macqd800,  "Apple Computer", "Macintosh Centris 610", MACHINE_SUPPORTS_SAVE)
COMP( 1993, macct650, macqd800, 0, macct650, macadb, quadra800_state, init_macqd800,  "Apple Computer", "Macintosh Centris 650", MACHINE_SUPPORTS_SAVE)
COMP( 1993, macqd610, macqd800, 0, macqd610, macadb, quadra800_state, init_macqd800,  "Apple Computer", "Macintosh Quadra 610", MACHINE_SUPPORTS_SAVE)
COMP( 1993, macqd650, macqd800, 0, macqd650, macadb, quadra800_state, init_macqd800,  "Apple Computer", "Macintosh Quadra 650", MACHINE_SUPPORTS_SAVE)



magibook.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:

/*********************************************************************************

    Skeleton driver for VTech MagiBook / LeapFrog LeapStart interactive books.
    Video from the real hardware (VTech): https://youtube.com/shorts/OcZ1ADZ_rTo

*********************************************************************************/

#include "emu.h"

#include "cpu/arm7/arm7.h"

#include "screen.h"
#include "speaker.h"


namespace {


class magibook_state : public driver_device
{
public:
	magibook_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu")
	{ }

	void magibook(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};

void magibook_state::machine_start()
{
}

void magibook_state::machine_reset()
{
}

// Wired touch pen plus "+", "-" and power buttons.
static INPUT_PORTS_START( magibook )
INPUT_PORTS_END

void magibook_state::magibook(machine_config &config)
{
	ARM9(config, m_maincpu, 24'000'000); // GeneralPlus GP326813, unknown frequency

	// Screenless

	SPEAKER(config, "mono").front_left();
}

// Spanish machine on VTech 6021 hardware, may be different between regions.
ROM_START( magibooksp )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "internal.u200", 0x00000, 0x10000, NO_DUMP ) // Unknown internal ROM size, if any

	ROM_REGION( 0x11000000, "program", 0 )
	ROM_LOAD( "vtech_magibook_tc58nvg1s3hta00.u302", 0x00000000, 0x11000000, CRC(035aa5ba) SHA1(b118954b764daab217d8b6b6785b0cedfcb88780) )
ROM_END

} // anonymous namespace


CONS( 2016, magibooksp, 0, 0, magibook, magibook, magibook_state, empty_init, "VTech", "MagiBook (Spanish)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_REQUIRES_ARTWORK )



magiceyes_pollux_vr3520f.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/******************************************************************************

    https://elinux.org/Pollux

    "The Pollux is a System on a Chip (SoC) that was manufactured by MagicEyes,
    whose intellectual property is now owned by Core Logic. Originally designed
    for LeapFrog as the LF1000, the 533MHz ARM926EJ core VR3520F has now made
    its way into several products running WinCE and Linux."

    (there are also links to datasheets etc.)

    used by
    Leapfrog Didj (as LF1000)
    GP2X Wiz (as VR3520F)
    GP2X Caanoo (as VR3520F)
     + more?

*******************************************************************************/

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/arm7/arm7.h"

#include "softlist_dev.h"
#include "speaker.h"
#include "screen.h"


namespace {

class magiceyes_vr3520f_game_state : public driver_device
{
public:
	magiceyes_vr3520f_game_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "arm9")
		, m_cart(*this, "cartslot")
	{ }

	void leapfrog_didj(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	void didj_arm9_map(address_map &map) ATTR_COLD;

	required_device<arm9_cpu_device> m_maincpu;
	optional_device<generic_slot_device> m_cart;
};

void magiceyes_vr3520f_game_state::didj_arm9_map(address_map &map)
{
}

void magiceyes_vr3520f_game_state::machine_start()
{
}

void magiceyes_vr3520f_game_state::machine_reset()
{
}

DEVICE_IMAGE_LOAD_MEMBER(magiceyes_vr3520f_game_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

static INPUT_PORTS_START( leapfrog_didj )
INPUT_PORTS_END


uint32_t magiceyes_vr3520f_game_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void magiceyes_vr3520f_game_state::leapfrog_didj(machine_config &config)
{
	ARM9(config, m_maincpu, 533000000); // 533 MHz ARM926TEJ (clocked at @ 393 MHz for the Didj?)
	m_maincpu->set_addrmap(AS_PROGRAM, &magiceyes_vr3520f_game_state::didj_arm9_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(0));
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	screen.set_screen_update(FUNC(magiceyes_vr3520f_game_state::screen_update));

	SPEAKER(config, "speaker", 2).front();

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "leapfrog_didj_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(magiceyes_vr3520f_game_state::cart_load));
	m_cart->set_must_be_loaded(true);

	SOFTWARE_LIST(config, "cart_list").set_original("leapfrog_didj_cart");
}

ROM_START( didj )
	ROM_REGION32_BE( 0x10800000, "bios", 0 ) // external NAND
	ROM_LOAD16_WORD_SWAP( "didj_29f2g08aac_2cda.bin", 0x000000, 0x10800000, CRC(3df8c3ee) SHA1(6dc4044be10da48b6dd37e40f8a112fc4314c87d) )

	// is there an internal bootloader beyond copying code from NAND into RAM?
ROM_END

} // anonymous namespace


CONS( 2008, didj,      0,       0,      leapfrog_didj, leapfrog_didj, magiceyes_vr3520f_game_state, empty_init, "LeapFrog", "Didj", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



magnum.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Carl

// Dulmont Magnum
// Additional info https://www.youtube.com/watch?v=st7H_vqSaQc and
// http://www.eevblog.com/forum/blog/eevblog-949-vintage-australian-made-laptop-teardown/msg1080508/#msg1080508
// TODO: cartridge dumps

#include "emu.h"
#include "cpu/i86/i186.h"
#include "machine/cdp1879.h"
#include "machine/wd_fdc.h"
#include "video/hd61830.h"
#include "video/i8275.h"
#include "sound/beep.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class magnum_state : public driver_device
{
public:
	magnum_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_beep(*this, "beep")
		, m_shift(*this, "SHIFT")
	{
	}
	void magnum(machine_config &config);
	DECLARE_INPUT_CHANGED_MEMBER(keypress);
protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	void beep_w(u8 data);
	u8 sysctl_r(offs_t offset);
	void sysctl_w(offs_t offset, u8 data);
	u16 irqstat_r();
	void port50_w(u16 data);
	void rtcirq_w(int state);

	void check_irq();
	void magnum_io(address_map &map) ATTR_COLD;
	void magnum_map(address_map &map) ATTR_COLD;
	void magnum_lcdc(address_map &map) ATTR_COLD;

	required_device<i80186_cpu_device> m_maincpu;
	required_device<beep_device> m_beep;
	required_ioport m_shift;

	u8 m_key;
	bool m_wake, m_rtcirq, m_keybirq;
};

void magnum_state::check_irq()
{
	if(m_rtcirq || m_keybirq)
	{
		m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
		m_maincpu->int0_w(ASSERT_LINE);
		m_wake = true;
	}
	else
		m_maincpu->int0_w(CLEAR_LINE);
}

INPUT_CHANGED_MEMBER(magnum_state::keypress)
{
	if(newval != oldval)
	{
		m_key = (uint8_t)(param & 0xff) | (m_shift->read() & 0xc ? 0 : 0x80);
		m_keybirq = true;
		check_irq();
	}
}

static INPUT_PORTS_START(magnum)
	PORT_START("ROW.0")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 0)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 1)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 4)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 5)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 8)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 9)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 12)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 13)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 2) // all 0x4 bits in every nibble also repeated in row 4+
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 3) // all 0x8 bits in every nibble also repeated in row 4+
	PORT_START("ROW.1")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 16)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR(9) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 17)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 20)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 21)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 24)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 25)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 28)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 29)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 18)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 19)
	PORT_START("ROW.2")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 32)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 33)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 36)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 37)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 40)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 41)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 44)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 45)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 34)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 35)
	PORT_START("ROW.3")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 48)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 49)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Help") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 52)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 53)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 56)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 57)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('`') PORT_CHAR('~') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 60)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 61)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 50)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 51)
	PORT_START("ROW.4")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 64)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 65)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 68)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 69)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 72)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 73)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 76)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 77)
	PORT_START("ROW.5")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 80)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 81)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F12) PORT_CHAR(UCHAR_MAMEKEY(F12)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 84)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 85)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 88)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 89)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 92)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 93)
	PORT_START("ROW.6")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 96)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_UNKNOWN) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 97)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Line Feed") PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 100)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 101)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 104)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 105)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 108)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 109)
	PORT_START("ROW.7")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 112)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 113)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 116)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 117)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 120)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 121)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 124)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(magnum_state::keypress), 125)
	PORT_START("SHIFT")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))
INPUT_PORTS_END

void magnum_state::machine_start()
{
}

void magnum_state::machine_reset()
{
	m_wake = false;
	m_rtcirq = false;
	m_keybirq = false;
}

void magnum_state::beep_w(u8 data)
{
	if (data & ~1) logerror("beep_w unmapped bits %02x\n", data);
	m_beep->set_state(BIT(data, 0));
}

void magnum_state::magnum_map(address_map &map)
{
	map(0x00000, 0x3ffff).ram(); // fixed 256k for now
	map(0xe0000, 0xfffff).rom().region("bios", 0);
}

u8 magnum_state::sysctl_r(offs_t offset)
{
	switch(offset)
	{
		case 0:
			return m_key;
		case 1:
		{
			u8 ret = m_keybirq ? 1 : 0;
			m_keybirq = false;
			check_irq();
			return ret | (m_shift->read() & 3 ? 0 : 4);
		}
	}
	return 0;
}

void magnum_state::sysctl_w(offs_t offset, u8 data)
{
	switch(offset)
	{
		case 0:
			break;
		case 2:
			m_maincpu->set_input_line(INPUT_LINE_RESET, data ? CLEAR_LINE : ASSERT_LINE);
			break;
		case 4:
			break;
		case 6:
			break;
		case 12:
			break;
	}
}

/* IRQs
 * bit  0 -
 *      1 -
 *      2 -
 *      3 -
 *      4 -
 *      5 - Keyboard?
 *      6 - RTC?
 *      7 -
 *      8 - UART1?
 *      9 - UART2?
 *      10- Reset?
 *      11-14 ...
 *      15- CRTC?
 */

u16 magnum_state::irqstat_r()
{
	u16 ret = m_wake ? 0 : 0x400;
	ret |= m_rtcirq ? 0x40 : 0;
	ret |= m_keybirq ? 0x20 : 0;
	m_wake = false;
	return ret;
}

void magnum_state::port50_w(u16 data)
{
}

void magnum_state::rtcirq_w(int state)
{
	m_rtcirq = state == ASSERT_LINE;
	check_irq();
}

void magnum_state::magnum_io(address_map &map)
{
	map.unmap_value_high();
	//map(0x000a, 0x000b) cdp1854 1
	//map(0x000e, 0x000f) cpd1854 2
	//map(0x0014, 0x0017).rw("crtc", FUNC(i8275_device::read), FUNC(i8275_device::write)).umask16(0x00ff);
	map(0x0018, 0x0019).rw("lcdc2", FUNC(hd61830_device::data_r), FUNC(hd61830_device::data_w)).umask16(0x00ff);
	map(0x001a, 0x001b).rw("lcdc2", FUNC(hd61830_device::status_r), FUNC(hd61830_device::control_w)).umask16(0x00ff);
	map(0x001c, 0x001d).rw("lcdc1", FUNC(hd61830_device::data_r), FUNC(hd61830_device::data_w)).umask16(0x00ff);
	map(0x001e, 0x001f).rw("lcdc1", FUNC(hd61830_device::status_r), FUNC(hd61830_device::control_w)).umask16(0x00ff);
	map(0x0040, 0x004f).rw(FUNC(magnum_state::sysctl_r), FUNC(magnum_state::sysctl_w));
	map(0x0050, 0x0051).rw(FUNC(magnum_state::irqstat_r), FUNC(magnum_state::port50_w));
	map(0x0056, 0x0056).w(FUNC(magnum_state::beep_w));
	map(0x0080, 0x008f).rw("rtc", FUNC(cdp1879_device::read), FUNC(cdp1879_device::write)).umask16(0x00ff);
	//map(0x0100, 0x0107).rw("fdc", FUNC(wd1793_device::read), FUNC(wd1793_device::write)).umask16(0x00ff);
}

void magnum_state::magnum_lcdc(address_map &map)
{
	map(0x0000, 0x027f).ram();
}

void magnum_state::magnum(machine_config &config)
{
	I80186(config, m_maincpu, XTAL(12'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &magnum_state::magnum_map);
	m_maincpu->set_addrmap(AS_IO, &magnum_state::magnum_io);

	CDP1879(config, "rtc", XTAL(32'768)).irq_callback().set(FUNC(magnum_state::rtcirq_w));

	screen_device &screen1(SCREEN(config, "screen1", SCREEN_TYPE_LCD));
	screen1.set_refresh_hz(50);
	screen1.set_screen_update("lcdc1", FUNC(hd61830_device::screen_update));
	screen1.set_size(6*40, 9*16);
	screen1.set_visarea(0, 6*40-1, 0, 8*16-1);
	screen1.set_palette("palette");

	screen_device &screen2(SCREEN(config, "screen2", SCREEN_TYPE_LCD));
	screen2.set_refresh_hz(50);
	screen2.set_screen_update("lcdc2", FUNC(hd61830_device::screen_update));
	screen2.set_size(6*40, 9*16);
	screen2.set_visarea(0, 6*40-1, 0, 8*16-1);
	screen2.set_palette("palette");

	hd61830_device &lcdc1(HD61830(config, "lcdc1", 1000000)); // unknown clock
	lcdc1.set_addrmap(0, &magnum_state::magnum_lcdc);
	lcdc1.set_screen("screen1");

	hd61830_device &lcdc2(HD61830(config, "lcdc2", 1000000)); // unknown clock
	lcdc2.set_addrmap(0, &magnum_state::magnum_lcdc);
	lcdc2.set_screen("screen2");

	//I8275(config, "crtc", 3000000); // unknown clock

	//WD1793(config, "fdc", 1000000); // nothing known, type or if any disks even exist, port 0x44 is possibly motor control

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	SPEAKER(config, "speaker").front_center();
	BEEP(config, m_beep, 500).add_route(ALL_OUTPUTS, "speaker", 0.50); // frequency is guessed
}

ROM_START( magnum )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE("a1.7.88.bin", 0x00000, 0x4000, CRC(57882427) SHA1(97637b65ca43eb9d3bba546fb8ca701ba25ade8d))
	ROM_LOAD16_BYTE("a1.7.81.bin", 0x00001, 0x4000, CRC(949f53a8) SHA1(b339f1495d9af7dfff0c3a2c24789631f9d1265b))
	ROM_LOAD16_BYTE("a1.7.87.bin", 0x08000, 0x4000, CRC(25036dda) SHA1(20bc3782a66855b20cb0abe1051fa2eb50c7a860))
	ROM_LOAD16_BYTE("a1.7.82.bin", 0x08001, 0x4000, CRC(ecf387d8) SHA1(8b42f6ab030afb51f21f4a56c62e5acf7d074066))
	ROM_LOAD16_BYTE("a1.7.86.bin", 0x10000, 0x4000, CRC(c80b3a6b) SHA1(0f0d2cb653bbeff8f3bab6d20dc30c220a67a315))
	ROM_LOAD16_BYTE("a1.7.83.bin", 0x10001, 0x4000, CRC(51f56d78) SHA1(df717eada5e6439b1c01d91bd0ea009cd0f8ddfa))
	ROM_LOAD16_BYTE("a1.7.85.bin", 0x18000, 0x4000, CRC(f5dd5407) SHA1(af2edf7a658bcf648acb8be9f13849f838d96214))
	ROM_LOAD16_BYTE("a1.7.84.bin", 0x18001, 0x4000, CRC(b3434bb0) SHA1(8000a7aca8fc505b136a618d9eb210c50393eff1))

	ROM_REGION(0x1000, "char", 0)
	ROM_LOAD("dulmontcharrom.bin", 0x0000, 0x1000, CRC(9dff89bf) SHA1(d359aeba7f0b0c81accf3bca25e7da636c033721))
ROM_END

} // anonymous namespace


COMP( 1983, magnum, 0, 0, magnum, magnum, magnum_state, empty_init, "Dulmont", "Magnum", MACHINE_IMPERFECT_SOUND)



mark5.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Chess Champion: Mark V / Mark VI (aka MK V / MK VI)

Released in late 1981, the chess engine was initially written by David Broughton
for a Z80 CPU and used in a prototype. It was ported to 6502 by Mark Taylor,
I/O by Mike Johnson. Support from David Levy and Kevin O'Connell, hardware
by Nick Toop. These credits are in the ROM data.

Mark VI/Philidor was released a year later, it was a plug-in module for the Mark V.
It's not much stronger than Mark V(retroactively called Mark V/Travemunde).

Hardware notes:
- SY6502A @ ~2MHz (19.6608MHz XTAL, bunch of 74113 dividers)
- 16KB RAM (8*HM4716AP-4N)
- 256x4 battery-backed RAM (TC5501P)
- HLCD0538, 2*HLCD0539, LCD screen with chessboard

3 slots:
- ROM module at the bottom, mandatory (4*8KB)
- unused module slot next to the LCD
- sensory board at left edge, only for MK VI

Chess Champion Sensory Board:
- PCB label: SciSys 502-00
- 4KB ROM (NEC D2732), TTL
- magnet sensors, 64 leds

The slots were designed to support anything, but the only released peripheral
was the Chess Champion Sensory Board. A piece-recognition chessboard was also
announced but not released. Maybe it existed as prototype, see patent GB2103943A.

TODO:
- /2 CPU divider when accessing 0x5000 (the nvram)
- reading from 0x4400 will write to the LCD too, open bus? it wouldn't make
  sense to use it (and as expected, it never is used)
- what are the 1M/3M/4M diodes for? CPU speed? the only noticeable difference
  is beeper pitch

*******************************************************************************/

#include "emu.h"
#include "cpu/m6502/m6502.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/hlcd0538.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "saitek_mark5.lh"
#include "saitek_mark6.lh"


namespace {

class mark5_state : public driver_device
{
public:
	mark5_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_cb_rom(*this, "chessboard"),
		m_display(*this, "display%u", 0),
		m_lcd(*this, "lcd%u", 0),
		m_dac(*this, "dac"),
		m_nvram(*this, "nvram"),
		m_inputs(*this, "IN.%u", 0),
		m_out_x(*this, "%u.%u.%u", 0U, 0U, 0U)
	{ }

	// machine configs
	void mark5(machine_config &config);
	void mark6(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(cb_enable) { if (!newval) m_display[3]->clear(); }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	optional_device<sensorboard_device> m_board;
	optional_region_ptr<u8> m_cb_rom;
	optional_device_array<pwm_display_device, 3+1> m_display;
	required_device_array<hlcd0538_device, 3> m_lcd;
	required_device<dac_1bit_device> m_dac;
	required_shared_ptr<u8> m_nvram;
	required_ioport_array<7+2> m_inputs;
	output_finder<3, 8, 34> m_out_x;

	u8 m_dac_data = 0;
	u8 m_lcd_lcd = 0;
	u8 m_lcd_rowsel = 0;
	u8 m_cb_mux = 0;

	attotime m_board_init_time;
	emu_timer *m_irqtimer;

	void init_board(u8 data);
	bool board_active() { return machine().time() > m_board_init_time; }

	// address maps
	void mark5_map(address_map &map) ATTR_COLD;
	void mark6_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void nvram_w(offs_t offset, u8 data);
	u8 nvram_r(offs_t offset);
	void lcd_data_w(u8 data);
	void sound_w(u8 data);
	u8 sound_r();
	void reset_irq_w(u8 data);
	u8 reset_irq_r();
	u8 input_r(offs_t offset);
	u8 cb_rom_r(offs_t offset);
	void cb_w(u8 data);
	u8 cb_r();

	template<int N> void pwm_output_w(offs_t offset, u8 data);
	template<int N> void lcd_output_w(u64 data);

	TIMER_CALLBACK_MEMBER(interrupt);
	void write_lcd(int state);
};



/*******************************************************************************
    Initialization
*******************************************************************************/

void mark5_state::machine_start()
{
	m_out_x.resolve();
	m_irqtimer = timer_alloc(FUNC(mark5_state::interrupt), this);

	// register for savestates
	save_item(NAME(m_dac_data));
	save_item(NAME(m_lcd_lcd));
	save_item(NAME(m_lcd_rowsel));
	save_item(NAME(m_cb_mux));
	save_item(NAME(m_board_init_time));
}

void mark5_state::machine_reset()
{
	reset_irq_w(0);
}

void mark5_state::init_board(u8 data)
{
	// ccmk6 expects an empty chessboard after a cold boot
	if (~data & 1)
		m_board_init_time = machine().time() + attotime::from_msec(1500);
}



/*******************************************************************************
    I/O
*******************************************************************************/

void mark5_state::nvram_w(offs_t offset, u8 data)
{
	// nvram is only d0-d3
	m_nvram[offset] = data & 0xf;
}

u8 mark5_state::nvram_r(offs_t offset)
{
	return m_nvram[offset] & 0xf;
}

template<int N>
void mark5_state::pwm_output_w(offs_t offset, u8 data)
{
	m_out_x[N][offset & 0x3f][offset >> 6] = data;
}

template<int N>
void mark5_state::lcd_output_w(u64 data)
{
	if constexpr (N == 0)
	{
		// HLCD0538 R pins
		m_lcd_rowsel = data & 0xff;
		data >>= 8;
	}

	m_display[N]->matrix(m_lcd_rowsel, data);
}

void mark5_state::write_lcd(int state)
{
	for (int i = 0; i < 3; i++)
		m_lcd[i]->lcd_w(state);

	m_lcd_lcd = state;
}

TIMER_CALLBACK_MEMBER(mark5_state::interrupt)
{
	// master clock to MC14020, Q12 to IRQ @ 480Hz
	// irq active ~34.61us (470pF, 100K to GND)
	m_irqtimer->adjust(attotime::from_hz(19.6608_MHz_XTAL / 10 / 0x1000));
	m_maincpu->pulse_input_line(0, attotime::from_nsec(34610));

	// MC14020 Q13(1 stage further than IRQ) goes to LCD "LCD" pins
	write_lcd(m_lcd_lcd ^ 1);
}

void mark5_state::reset_irq_w(u8 data)
{
	// MC14020 R
	m_irqtimer->adjust(attotime::from_hz((19.6608_MHz_XTAL / 10 / 0x1000) * 2));
	write_lcd(0);
}

u8 mark5_state::reset_irq_r()
{
	if (!machine().side_effects_disabled())
		reset_irq_w(0);

	return 0xff;
}

void mark5_state::sound_w(u8 data)
{
	// 7474 to speaker out
	m_dac_data ^= 1;
	m_dac->write(m_dac_data & m_inputs[7]->read());
}

u8 mark5_state::sound_r()
{
	if (!machine().side_effects_disabled())
		sound_w(0);

	return 0xff;
}

void mark5_state::lcd_data_w(u8 data)
{
	// d0,d2,d4: LCD data
	for (int i = 0; i < 3; i++)
	{
		m_lcd[i]->data_w(BIT(data, i*2));

		m_lcd[i]->clk_w(1);
		m_lcd[i]->clk_w(0);
	}
}

u8 mark5_state::input_r(offs_t offset)
{
	u8 data = 0;

	// _a6: configuration diodes
	// a0-a5: multiplexed inputs
	for (int i = 0; i < 7; i++)
		if (BIT(offset ^ 0x40, i))
			data |= m_inputs[i]->read();

	return ~data;
}

void mark5_state::cb_w(u8 data)
{
	if (~m_inputs[6]->read() & 0x20)
		return;

	// d0-d2: chessboard led mux 1/input mux
	// d3-d6: chessboard led mux 2
	m_display[3]->matrix(1 << (data & 7), 1 << (data >> 3 & 0xf));
	m_cb_mux = data;
}

u8 mark5_state::cb_r()
{
	if (~m_inputs[6]->read() & 0x20 || !board_active())
		return 0xff;

	// read chessboard sensors
	return ~m_board->read_file(m_cb_mux & 7);
}

u8 mark5_state::cb_rom_r(offs_t offset)
{
	return (m_inputs[6]->read() & 0x20) ? m_cb_rom[offset] : 0xff;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void mark5_state::mark5_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).ram();
	map(0x4000, 0x407f).mirror(0x0380).r(FUNC(mark5_state::input_r));
	map(0x4400, 0x4400).mirror(0x03ff).w(FUNC(mark5_state::lcd_data_w));
	map(0x4800, 0x4800).mirror(0x03ff).rw(FUNC(mark5_state::sound_r), FUNC(mark5_state::sound_w));
	map(0x4c00, 0x4c00).mirror(0x03ff).rw(FUNC(mark5_state::reset_irq_r), FUNC(mark5_state::reset_irq_w));
	map(0x5000, 0x50ff).mirror(0x0f00).ram().rw(FUNC(mark5_state::nvram_r), FUNC(mark5_state::nvram_w)).share("nvram");
	map(0x8000, 0xffff).rom();
}

void mark5_state::mark6_map(address_map &map)
{
	mark5_map(map);

	map(0x7000, 0x77ff).r(FUNC(mark5_state::cb_rom_r));
	map(0x7800, 0x7800).mirror(0x07ff).rw(FUNC(mark5_state::cb_r), FUNC(mark5_state::cb_w));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( mark5 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("Enter Position")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("Draw")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Peripheral")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Next Simult")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Swap")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Next Best")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Auto")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Comment")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Analysis")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_SLASH) PORT_NAME("Clock")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_STOP) PORT_NAME("Stop Clock")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("No / 0 / CS")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_A) PORT_NAME("A / 1 / Pawn")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_B) PORT_NAME("B / 2 / Knight")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_C) PORT_NAME("C / 3 / Bishop")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_D) PORT_NAME("D / 4 / Rook")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_E) PORT_NAME("E / 5 / Queen")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_F) PORT_NAME("F / 6 / King")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_G) PORT_NAME("G / 7 / White")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_H) PORT_NAME("H / 8 / Black")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_EQUALS) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("Yes / 9 / CB")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_PGDN) PORT_NAME("Backward")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_PGUP) PORT_NAME("Forward")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Replay")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Go")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Mode")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_COMMA) PORT_NAME("Start Clock")

	PORT_START("IN.5") // d-pad reads here
	PORT_BIT(0x01, 0x01, IPT_CUSTOM) PORT_CONDITION("IN.8", 0x31, NOTEQUALS, 0x00)
	PORT_BIT(0x02, 0x02, IPT_CUSTOM) PORT_CONDITION("IN.8", 0xc2, NOTEQUALS, 0x00)
	PORT_BIT(0x04, 0x04, IPT_CUSTOM) PORT_CONDITION("IN.8", 0xa4, NOTEQUALS, 0x00)
	PORT_BIT(0x08, 0x08, IPT_CUSTOM) PORT_CONDITION("IN.8", 0x58, NOTEQUALS, 0x00)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.6") // diodes
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_CUSTOM) // 1M/3M/4M
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_CUSTOM) // "
	PORT_CONFNAME( 0x0c, 0x00, DEF_STR( Language ) )
	PORT_CONFSETTING(    0x00, DEF_STR( English ) )
	PORT_CONFSETTING(    0x04, DEF_STR( German ) )
	PORT_CONFSETTING(    0x08, DEF_STR( French ) )
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_CUSTOM) // $6000 edge connector
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_CUSTOM) // $7000 "

	PORT_START("IN.7") // switches
	PORT_CONFNAME( 0x01, 0x01, "Sound" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
	PORT_CONFNAME( 0x02, 0x02, "LCD Light" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x02, DEF_STR( On ) )

	PORT_START("IN.8") // square 'd-pad' (8-way, so define joystick)
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP) PORT_CODE(KEYCODE_UP) PORT_NAME("Cursor Up")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Cursor Down")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Cursor Right")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Cursor Left")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) // ul
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) // ur
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) // dl
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) // dr
INPUT_PORTS_END

static INPUT_PORTS_START( mark6 )
	PORT_INCLUDE( mark5 )

	PORT_MODIFY("IN.6")
	PORT_CONFNAME( 0x20, 0x20, "Sensory Board" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mark5_state::cb_enable), 0)
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x20, DEF_STR( On ) )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void mark5_state::mark5(machine_config &config)
{
	// basic machine hardware
	M6502(config, m_maincpu, 19.6608_MHz_XTAL / 10);
	m_maincpu->set_addrmap(AS_PROGRAM, &mark5_state::mark5_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	// video hardware
	HLCD0538(config, m_lcd[0]).write_cols().set(FUNC(mark5_state::lcd_output_w<0>));
	PWM_DISPLAY(config, m_display[0]).set_size(8, 26);
	m_display[0]->output_x().set(FUNC(mark5_state::pwm_output_w<0>));

	HLCD0539(config, m_lcd[1]).write_cols().set(FUNC(mark5_state::lcd_output_w<1>));
	PWM_DISPLAY(config, m_display[1]).set_size(8, 34);
	m_display[1]->output_x().set(FUNC(mark5_state::pwm_output_w<1>));

	HLCD0539(config, m_lcd[2]).write_cols().set(FUNC(mark5_state::lcd_output_w<2>));
	PWM_DISPLAY(config, m_display[2]).set_size(8, 34);
	m_display[2]->output_x().set(FUNC(mark5_state::pwm_output_w<2>));

	for (int i = 0; i < 3; i++)
		m_display[i]->set_bri_maximum(0.1);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(942/1.5, 1080/1.5);
	screen.set_visarea_full();

	config.set_default_layout(layout_saitek_mark5);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void mark5_state::mark6(machine_config &config)
{
	mark5(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &mark5_state::mark6_map);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->init_cb().append(FUNC(mark5_state::init_board));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	PWM_DISPLAY(config, m_display[3]).set_size(8, 8);
	m_display[3]->set_interpolation(0.3);
	config.set_default_layout(layout_saitek_mark6);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( ccmk5 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("c47024_syp_2364-3-y51", 0x8000, 0x2000, CRC(c210f530) SHA1(60ba3809ed3054024508344f654a6846061fafd5) ) // 2364
	ROM_LOAD("c47025_syp_2364-3-y5a", 0xa000, 0x2000, CRC(3239c96b) SHA1(6a23713b30c48546d993a0de8998c8de9044e48c) ) // "
	ROM_LOAD("c47026_syp_2364-3-y5c", 0xc000, 0x2000, CRC(1754ccab) SHA1(d246b6aa2e2a1858dd6608a4dbf496778f79b22e) ) // "
	ROM_LOAD("c47027_syp_2364-3-y5d", 0xe000, 0x2000, CRC(7c0f7bd8) SHA1(68b4566f0501005f6b1739bb24a4bec990421a6f) ) // "

	ROM_REGION( 1887415, "screen", 0)
	ROM_LOAD("ccmk5.svg", 0, 1887415, CRC(656a2263) SHA1(4557979c62b1240f7a0d813ec5f4d54b8a27218e) )
ROM_END

ROM_START( ccmk6 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("y6_80", 0x8000, 0x2000, CRC(8144dd71) SHA1(8d6fbb4aa9757149c81d2bf533085dc5203f0751) ) // 2764
	ROM_LOAD("y6_a0", 0xa000, 0x2000, CRC(dd77dd90) SHA1(844aee56e1941f05bdf046d95c5ae687707a2c95) ) // "
	ROM_LOAD("y6_c0", 0xc000, 0x2000, CRC(705e5718) SHA1(513bba3e7344194efaaf022a7934d32d8cba3cb5) ) // "
	ROM_LOAD("y6_e0", 0xe000, 0x2000, CRC(b92c3eb3) SHA1(99a20f5e971b8c4228e0eda0a4c05750d46b95f6) ) // "

	ROM_REGION( 0x1000, "chessboard", 0 )
	ROM_LOAD("d2732c-e.u1", 0x0000, 0x1000, CRC(93221b4c) SHA1(8561b52c80cab7c04d30eaa14f9520a362d7f822) ) // no label, identical halves

	ROM_REGION( 1887415, "screen", 0)
	ROM_LOAD("ccmk5.svg", 0, 1887415, CRC(656a2263) SHA1(4557979c62b1240f7a0d813ec5f4d54b8a27218e) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY, FULLNAME, FLAGS
SYST( 1981, ccmk5, 0,      0,      mark5,   mark5, mark5_state, empty_init, "SciSys / Philidor Software", "Chess Champion: Mark V", MACHINE_SUPPORTS_SAVE )
SYST( 1982, ccmk6, ccmk5,  0,      mark6,   mark6, mark5_state, empty_init, "SciSys / Philidor Software", "Chess Champion: Mark VI/Philidor", MACHINE_SUPPORTS_SAVE )



marsvending.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:

/*
  Skeleton driver for MCS51-based Mars Electronics vending machines.

  Hardware for models 11x:
  ________________________________________________
 |        ___   ·········  ····    ::::::::      |
 |       |  |                                    |
 |   ___   ________   ________   ________   ___  |
 |· |2061 |74HC238N  |UDN2595A  |74HC03AN  555CN |
 |·  ________   ________         ______________  |
 |· |ULN2064B  |74HC32AN        | HY6116AP-10 |  |
 |·                             |_____________|  |
 |   ________   ________        _______________  |
 |· |ULN2064B  |74HC572N       | EPROM        |  |
 |·                            |______________|  |
 |·             ________                         |
 |·            |CA3081_|         ________       ·|
 |·                             |74HC573N       ·|
 |·                       _____________________  |
 |·                      | Intel P80C31BH     | ·|
 |·                      |____________________| ·|
 |             ________                 Xtal     |
 |            |74HC572N               11.0 MHz   |
 |                    ________   ________        |
 |  _____            74HC4052N  |74HC541N        |
 | LM317T                        ________        |
 |·                             |74HC573N        |
 |·                 ________     ____            |
 |·                |LM339N_|    DS1232   SWITCH  |
 |·         ____                                 |
 |·        555CN              ____    SPEAKER   ·|
 |·                         L9130H              ·|
 |                    ________      ________    ·|
 |·                  |74HC14N|     |UDN2595A    ·|
 |                     ____          ____       ·|
 |                    X24C04P       PCF8583P    ·|
 |  _____             ________     Xtal          |
 | LM2940CT          |DS14C88N                  ·|
 |                           3.6V BATTERY       :|
 |_______________________________________________|

 Plus a display PCB with 10 digits, 16 segments per digit (character with decimal
 point and comma tail), controlled with a Rockwell 10957P-40 or compatible (Micrel, etc.).

*/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/i2cmem.h"
#include "machine/pcf8583.h"
#include "machine/roc10937.h"
#include "speaker.h"

namespace {

class marsvending_state : public driver_device
{
public:
	marsvending_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu")
	{ }

	void marsvending(machine_config &config);
	void zunknecta(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};

void marsvending_state::machine_start()
{
}

void marsvending_state::machine_reset()
{
}

static INPUT_PORTS_START( marsvending )
INPUT_PORTS_END

void marsvending_state::marsvending(machine_config &config)
{
	I80C31(config, m_maincpu, 11_MHz_XTAL); // Intel P80C31BH

	PCF8583(config, "clock", 32.768_kHz_XTAL); // PCF8583P

	I2C_24C04(config, "i2cmem", 0); // X24C04P

	ROC10957(config, "display", 0); // Rockwell 10957P-40 or compatible, 10 digits, 16 segments per digit (character with decimal point and comma tail)

	SPEAKER(config, "mono").front_center();
}

ROM_START( apvm110 )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD( "31058i.bin",     0x0000, 0x8000, CRC(b83a7b19) SHA1(8e7dffd2c1040017151a2da5bd72a16eda542fc5) )
ROM_END

ROM_START( apvm110a )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD( "mcb110-005.bin", 0x0000, 0x8000, CRC(94e0e2a2) SHA1(cfa5d41d5ff9dfdd0a042be7e1a321cf4b0253c2) )
ROM_END


} // anonymous namespace


SYST( 1990, apvm110,  0,       0, marsvending, marsvending, marsvending_state, empty_init, "Mars Electronics", "Automatic Products Vending Machine model 110 (set 1)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
SYST( 1990, apvm110a, apvm110, 0, marsvending, marsvending, marsvending_state, empty_init, "Mars Electronics", "Automatic Products Vending Machine model 110 (set 2)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



master.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Chess King Master (yes, it's plainly named "Master")

Chess King was a UK business formed by Intelligent Software Ltd (later known as
Intelligent Chess Software Ltd, after the fiasco with the Enterprise home computer),
so logically, all the programs were by them. According to the manual, the chess
engine of Master is Cyrus (by Richard Lang).

To start a new game, press CHANGE POSITION, NEW GAME, and CHANGE POSITION again.

Hardware notes:
- PCB label: COPYRIGHT CHESS KING
- Z80 CPU(NEC D780C-1) @ 4MHz(8MHz XTAL), IRQ from 555 timer
- 8KB ROM(NEC D2764C-3), 2KB RAM(NEC D4016C), ROM is scrambled for easy PCB placement
- simple I/O via 2*74373 and a 74145
- 8*8 chessboard buttons, 32+1 border leds, piezo

*******************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/bankdev.h"
#include "machine/clock.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "cking_master.lh"


namespace {

class master_state : public driver_device
{
public:
	master_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_board(*this, "board"),
		m_dac(*this, "dac"),
		m_mainmap(*this, "mainmap"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void master(machine_config &config);

	void init_master();

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<z80_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<sensorboard_device> m_board;
	required_device<dac_2bit_ones_complement_device> m_dac;
	required_device<address_map_bank_device> m_mainmap;
	required_ioport_array<2> m_inputs;

	u8 m_inp_mux = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;
	void main_trampoline(address_map &map) ATTR_COLD;
	u8 main_trampoline_r(offs_t offset);
	void main_trampoline_w(offs_t offset, u8 data);

	// I/O handlers
	u8 input_r();
	void control_w(u8 data);
};



/*******************************************************************************
    Initialization
*******************************************************************************/

void master_state::init_master()
{
	u8 *rom = memregion("maincpu")->base();
	const u32 len = memregion("maincpu")->bytes();

	// descramble data lines
	for (int i = 0; i < len; i++)
		rom[i] = bitswap<8>(rom[i], 4,5,0,7,6,1,3,2);

	// descramble address lines
	std::vector<u8> buf(len);
	memcpy(&buf[0], rom, len);
	for (int i = 0; i < len; i++)
		rom[i] = buf[bitswap<16>(i, 15,14,13,12,11,3,7,9, 10,8,6,5,4,2,1,0)];
}

void master_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void master_state::control_w(u8 data)
{
	// d0-d3: 74145 A-D
	// 74145 0-9: input mux, led select
	m_inp_mux = data & 0xf;
	u16 sel = 1 << m_inp_mux & 0x3ff;

	// d4,d5: led data
	m_display->matrix(sel & 0x1ff, data >> 4 & 3);

	// d6,d7: speaker +/-
	m_dac->write(data >> 6 & 3);
}

u8 master_state::input_r()
{
	u8 data = 0;

	// d0-d7: multiplexed inputs
	// read chessboard sensors
	if (m_inp_mux < 8)
		data = m_board->read_file(m_inp_mux, true);

	// read other buttons
	else if (m_inp_mux < 10)
		data = m_inputs[m_inp_mux - 8]->read();

	return ~data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void master_state::main_map(address_map &map)
{
	map(0x0000, 0x1fff).mirror(0x6000).rom().region("maincpu", 0); // _A15
	map(0xa000, 0xa000).mirror(0x1fff).rw(FUNC(master_state::input_r), FUNC(master_state::control_w)); // A13
	map(0xc000, 0xc7ff).mirror(0x3800).ram(); // A14
}

// PCB design is prone to bus conflicts, but should be fine if software obeys
void master_state::main_trampoline_w(offs_t offset, u8 data)
{
	if (offset & 0x2000)
		m_mainmap->write8((offset & 0x3fff) | 0x8000, data);
	if (offset & 0x4000)
		m_mainmap->write8((offset & 0x7fff) | 0x8000, data);
}

u8 master_state::main_trampoline_r(offs_t offset)
{
	u8 data = 0xff;
	if (~offset & 0x8000)
		data &= m_mainmap->read8(offset);
	if (offset & 0x2000)
		data &= m_mainmap->read8((offset & 0x3fff) | 0x8000);
	if (offset & 0x4000)
		data &= m_mainmap->read8((offset & 0x7fff) | 0x8000);

	return data;
}

void master_state::main_trampoline(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(master_state::main_trampoline_r), FUNC(master_state::main_trampoline_w));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( master )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Change Position")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Clear Board")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("King")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Queen")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Rook")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("White")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("Black")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Move")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Sound")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void master_state::master(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 8_MHz_XTAL/2);
	m_maincpu->z80_set_m1_cycles(4+1); // 1 WAIT CLK per M1
	m_maincpu->set_addrmap(AS_PROGRAM, &master_state::main_trampoline);
	ADDRESS_MAP_BANK(config, m_mainmap).set_map(&master_state::main_map).set_options(ENDIANNESS_LITTLE, 8, 16);

	auto &irq_clock(CLOCK(config, "irq_clock", 418)); // 555 timer (22nF, 150K, 1K5), measured 418Hz
	irq_clock.set_pulse_width(attotime::from_nsec(22870)); // active for 22.87us
	irq_clock.signal_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 2);
	config.set_default_layout(layout_cking_master);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_2BIT_ONES_COMPLEMENT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.125);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( master )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("d2764c-3.ic2", 0x0000, 0x2000, CRC(59cbec9e) SHA1(2e0629e65778da62bed857406b91a334698d2fe8) ) // no custom label
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT         COMPANY, FULLNAME, FLAGS
SYST( 1983, master, 0,      0,      master,  master, master_state, init_master, "Chess King / Intelligent Software", "Master (Chess King)", MACHINE_SUPPORTS_SAVE )



max80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

A business-only clone of the TRS-80 Model III.

All controllers are built-in. Disk drives are required. It can
boot from any size floppy or from a hard disk. All the drives are
in external enclosures. External connections are:
- 5.25 floppy connector (up to 4 drives)
- 8 floppy connector (up to 4 drives)
- HD connector (1 only, gives total of 9 drives)
- Serial A
- Serial B
- Expansion I/O

All configuration comes from the floppy image. This includes setting up
the CRTC, the character generator, the SIO and the keyboard.

There was a Technical Manual, but I was unable to find a copy, and so
everything in here is based upon what could be gleaned from the schematic.

There's IMD floppy images available, but it's unknown what kind of drive
is needed. I was not able to boot anything.

Organisation of memory:
- There's 128K of RAM, split into 32k sections. Bits 6,7 of 7FC control the
  banking. According to the disassembly, 0x80 selects A1 and B1, while 0x40
  selects A1 and A2. I presume that the 64K banks are A and B, and their 32K
  halves are 1 and 2. It's unknown what combinations 0x00 and 0xC0 select.

- Overlaid on this is a special 4K-sized bank which holds the ROM, the video
  RAM and all the devices. This bank can be anywhere in memory on a 4K boundary
  chosen by writing to x7DC. Further, the 4K bank has some unassigned areas,
  allowing the underlying main ram to shine through. It's unknown what happens
  if you attempt to write to ROM. The bank is initially at address 0-FFF.

NOTE on character generation:
- There's no character generator ROM. All definitions are in a 6116 RAM at
  an unknown address within the 4K bank. Therefore nothing can be seen until
  a successful boot from disk, which contains the font data, and also does
  initialisation of the CRTC. Video RAM resides in another 6116, which is
  also in the 4K bank.

Booting:
- A sector is read into main RAM starting at 0x400. Then it is executed and
  the process of booting can begin. In TRS-80 compatible-mode (a disk program),
  the 4K bank is moved to 0x3000.

***************************************************************************


To Do: Almost everything.
Status: Beeps every so often. Unable to read the disk.


***************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
//#include "machine/ram.h"
#include "imagedev/floppy.h"
#include "imagedev/snapquik.h"
#include "machine/z80sio.h"
#include "machine/z80pio.h"
#include "machine/com8116.h"
#include "machine/msm5832.h"
//#include "bus/rs232/rs232.h"
#include "machine/wd_fdc.h"
#include "sound/beep.h"
#include "machine/timer.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "formats/imd_dsk.h"
//#include "formats/trs80_dsk.h"
//#include "formats/dmk_dsk.h"

#include "utf8.h"


namespace {

class max80_state : public driver_device
{
public:
	max80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
//      , m_p_chargen(*this, "chargen")
//      , m_vram(*this, "videoram")
		, m_palette(*this, "palette")
		, m_pio(*this, "pio")
		, m_crtc(*this, "crtc")
		, m_uart(*this, "uart")
		, m_brg(*this, "brg")
		, m_beep(*this, "beeper")
		, m_beep_timer(*this, "beep_timer")
		, m_rtc(*this, "rtc")
		, m_fdc(*this, "fdc")
		, m_floppy0(*this, "fdc:0")
		, m_floppy1(*this, "fdc:1")
		, m_floppy2(*this, "fdc:2")
		, m_floppy3(*this, "fdc:3")
		, m_io_keyboard(*this, "LINE%u", 0U)
//      , m_mainram(*this, RAM_TAG)
	{ }

	void max80(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	static void floppy_formats(format_registration &fr);
	[[maybe_unused]] u8 keyboard_r(offs_t offset);
	void beep_w(offs_t offset, u8 data);
	void mode_w(offs_t offset, u8 data);
	void drive_w(offs_t offset, u8 data);
	u8 fdc_status_r(offs_t offset);
	u8 pio_pa_r(offs_t offset);
	void pio_pa_w(offs_t offset, u8 data);
	void pio_pb_w(offs_t offset, u8 data);

	void intrq_w(int state);
	void drq_w(int state);
	MC6845_UPDATE_ROW(crtc_update_row);
	TIMER_DEVICE_CALLBACK_MEMBER(beep_timer);

	void mem_map(address_map &map) ATTR_COLD;

	u8 m_size_store = 0U;
	bool m_fdc_drq = 0;
	bool m_fdc_int = 0;
	bool m_allow_nmi = 0;
	u8 m_mode = 0U;
	floppy_image_device *m_floppy = 0;
	required_device<cpu_device> m_maincpu;
	//required_region_ptr<u8> m_p_chargen;
	//required_shared_ptr<u8> m_p_vram;
	required_device<palette_device> m_palette;
	required_device<z80pio_device> m_pio;
	required_device<mc6845_device> m_crtc;
	required_device<z80sio_device> m_uart;
	required_device<com8116_device> m_brg;
	required_device<beep_device> m_beep;
	required_device<timer_device> m_beep_timer;
	required_device<msm5832_device> m_rtc;
	required_device<mb8876_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	required_device<floppy_connector> m_floppy1;
	required_device<floppy_connector> m_floppy2;
	required_device<floppy_connector> m_floppy3;
	required_ioport_array<8> m_io_keyboard;
//  optional_device<ram_device>                 m_mainram;
};


void max80_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).ram();
	map(0x0000, 0x01ff).rom();
	//map(0x3800, 0x3bff).r(FUNC(max80_state::keyboard_r));
	//map(0x3c00, 0x3fff).ram().share(m_p_videoram);
	map(0x07d0, 0x07d3).w(m_brg, FUNC(com8116_device::str_w));  // W0
	map(0x07d4, 0x07d7).w(m_brg, FUNC(com8116_device::stt_w));  // W1
	map(0x07d8, 0x07db).w(FUNC(max80_state::drive_w));       // W2
	map(0x07dc, 0x07df).w(FUNC(max80_state::mode_w));   // 0x30 to move 4k area to 3000-3FFF   //W3
	map(0x07e0, 0x07e0).mirror(2).w(m_crtc, FUNC(mc6845_device::address_w));    // OUT 0
	map(0x07e1, 0x07e1).mirror(2).w(m_crtc, FUNC(mc6845_device::register_w));   // OUT 0
	map(0x07e4, 0x07e7).rw(m_uart, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));  // IN1,OUT1
	map(0x07ec, 0x07ef).rw(m_fdc, FUNC(mb8876_device::read), FUNC(mb8876_device::write));  // IN3,OUT3
	//map(0x07f0, 0x07f3).r   // udata
	map(0x07f4, 0x07f7).r(FUNC(max80_state::fdc_status_r));
	map(0x07f8, 0x07fb).portr("BOOT");   // IN6
	map(0x07f8, 0x07fb).w(FUNC(max80_state::beep_w));
	map(0x07fc, 0x07ff).rw(m_pio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt)); // IN7,OUT7
}

static INPUT_PORTS_START( max80 )
	PORT_START("LINE0")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_INSERT)     PORT_CHAR('@')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)          PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)          PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)          PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)          PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)          PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)          PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)          PORT_CHAR('g') PORT_CHAR('G')

	PORT_START("LINE1")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)          PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)          PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)          PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)          PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)          PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)          PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)          PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("LINE2")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)          PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)          PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)          PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)          PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)          PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)          PORT_CHAR('w') PORT_CHAR('W')

	PORT_START("LINE3")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)          PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)          PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("_") PORT_CODE(KEYCODE_DEL)        PORT_CHAR('_') PORT_CHAR(127)

	PORT_START("LINE4")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD)          PORT_CHAR('0')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)          PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)          PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("LINE5")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME(": *") PORT_CODE(KEYCODE_MINUS)        PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("; +") PORT_CODE(KEYCODE_COLON)        PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA)        PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("- =") PORT_CODE(KEYCODE_EQUALS)       PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD)    PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH)        PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("LINE6")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13)
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("Clear") PORT_CODE(KEYCODE_HOME)       PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_END)        PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)        PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_BACKSPACE)   PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')

	PORT_START("LINE7")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x40, 0x00, IPT_UNUSED)
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("CTL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)

	PORT_START("BOOT")   // lower 3 dips decide the boot device. Other 5 dips are unknown. Manual shows them all set low.
	PORT_DIPNAME( 0x07, 0x02, "Boot device")
	PORT_DIPSETTING(    0x00, "None")
	PORT_DIPSETTING(    0x01, "Floppy 5")
	PORT_DIPSETTING(    0x02, "Floppy 8")
	PORT_DIPSETTING(    0x03, "HD 5 UVC")
	PORT_DIPSETTING(    0x04, "HD 8 UVC")
	PORT_DIPSETTING(    0x05, "FD 5 UVC")
	PORT_DIPSETTING(    0x06, "FD 8 UVC")
	PORT_DIPSETTING(    0x07, "HD SASI")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNKNOWN )
INPUT_PORTS_END


/*************************************
 *
 *              Port handlers.
 *
 *************************************/


TIMER_DEVICE_CALLBACK_MEMBER(max80_state::beep_timer)
{
	m_beep->set_state(0);
}

void max80_state::drive_w(offs_t offset, u8 data)
{
	m_floppy = nullptr;

	if (BIT(data, 0)) m_floppy = m_floppy0->get_device();
	if (BIT(data, 1)) m_floppy = m_floppy1->get_device();
	if (BIT(data, 2)) m_floppy = m_floppy2->get_device();
	if (BIT(data, 3)) m_floppy = m_floppy3->get_device();

	m_fdc->set_floppy(m_floppy);

	if (m_floppy)
	{
		m_floppy->mon_w(0);
		m_floppy->ss_w(BIT(data, 4));
	}

	m_fdc->dden_w(!BIT(data, 6));
	m_fdc->set_unscaled_clock(BIT(data, 5) ? 2000000 : 1000000);
	m_allow_nmi = BIT(data, 7);
}


void max80_state::intrq_w(int state)
{
	m_fdc_int = state;
}

void max80_state::drq_w(int state)
{
	m_fdc_drq = state;
	m_maincpu->set_input_line(INPUT_LINE_NMI, (state && m_allow_nmi) ? ASSERT_LINE : CLEAR_LINE);
}

u8 max80_state::keyboard_r(offs_t offset)
{
	u8 i, result = 0;

	for (i = 0; i < 8; i++)
		if (BIT(offset, i))
			result |= m_io_keyboard[i]->read();

	return result;
}

u8 max80_state::fdc_status_r(offs_t offset)
{
	u8 data = 0xfc | int(m_fdc_drq) | (m_fdc_int << 1);
	return data;
}

void max80_state::beep_w(offs_t offset, u8 data)
{
	m_beep->set_state(1);
	m_beep_timer->adjust(attotime::from_msec(150));
}

void max80_state::mode_w(offs_t offset, u8 data)
{
	// bit 0, disable rom
	// bit 1, disable i/o
	// bit 2, enable video access from cpu
	// bit 3, enable wide characters
	// bits 4-7, move rom and i/o to the block starting with x000, where x = BIT(data,4,4)
	m_mode = data;
}

u8 max80_state::pio_pa_r(offs_t offset)
{
	return m_rtc->data_r();
}

void max80_state::pio_pa_w(offs_t offset, u8 data)
{
	m_rtc->data_w(data & 15);
	// Memory banking of the 64k rams
	// if subsequent memory address of >=0x8000, bits 6/7 are used, else bits 4/5.
	// High bit switches 64k banks. Low bit does something with 32k, but not sure what.
}

void max80_state::pio_pb_w(offs_t offset, u8 data)
{
	m_rtc->address_w(data & 15);
	m_rtc->write_w(BIT(data, 4));
	m_rtc->read_w(BIT(data, 5));
	m_rtc->hold_w(BIT(data, 6));
}

/*************************************
 *  Machine              *
 *************************************/

void max80_state::machine_start()
{
//  save_item(NAME(m_irq));
//  save_item(NAME(m_size_store));
//  save_item(NAME(m_drq_off));
//  save_item(NAME(m_intrq_off));

}

void max80_state::machine_reset()
{
	m_size_store = 0xff;
	m_fdc_drq = false;
	m_fdc_int = false;
	m_floppy = nullptr;
}

MC6845_UPDATE_ROW( max80_state::crtc_update_row )
{
#if 0
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u32 *p = &bitmap.pix(y);

	for (u16 x = 0; x < x_count; x++)
	{
		u16 mem = (ma + x) & 0x7ff;
		u8 chr = m_vram[mem];
		u8 gfx = m_p_chargen[(chr<<4) | ra] ^ ((x == cursor_x) ? 0xff : 0);

		/* Display a scanline of a character (8 pixels) */
		*p++ = palette[BIT(gfx, 7)];
		*p++ = palette[BIT(gfx, 6)];
		*p++ = palette[BIT(gfx, 5)];
		*p++ = palette[BIT(gfx, 4)];
		*p++ = palette[BIT(gfx, 3)];
		*p++ = palette[BIT(gfx, 2)];
		*p++ = palette[BIT(gfx, 1)];
		*p++ = palette[BIT(gfx, 0)];
	}
#endif
}


void max80_state::floppy_formats(format_registration &fr)
{
	fr.add(FLOPPY_IMD_FORMAT);
	//fr.add(FLOPPY_JV3_FORMAT);
	//fr.add(FLOPPY_DMK_FORMAT);
	//fr.add(FLOPPY_JV1_FORMAT);
}

static void max80_floppies(device_slot_interface &device)
{
	// Available images get rejected with 40-track drives
	//device.option_add("40t_sd", FLOPPY_525_SSSD);
	//device.option_add("40t_dd", FLOPPY_525_DD);
	device.option_add("80t_qd", FLOPPY_525_QD);
	device.option_add("8ssdd", FLOPPY_8_SSDD);
	device.option_add("8dsdd", FLOPPY_8_DSDD);
}


void max80_state::max80(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 15'200'000 / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &max80_state::mem_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2000)); // not accurate
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	screen.set_size(640, 480);
	screen.set_visarea(0, 639, 0, 479);

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	HD6845S(config, m_crtc, 15'200'000 / 8);   // HD46505
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(max80_state::crtc_update_row));

	// devices
	MB8876(config, m_fdc, 8_MHz_XTAL / 4);
	m_fdc->intrq_wr_callback().set(FUNC(max80_state::intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(max80_state::drq_w));
	FLOPPY_CONNECTOR(config, "fdc:0", max80_floppies, "8ssdd", max80_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", max80_floppies, nullptr, max80_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:2", max80_floppies, nullptr, max80_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:3", max80_floppies, nullptr, max80_state::floppy_formats).enable_sound(true);

	Z80PIO(config, m_pio, 15'200'000 / 3);
	m_pio->in_pa_callback().set(FUNC(max80_state::pio_pa_r));
	m_pio->out_pa_callback().set(FUNC(max80_state::pio_pa_w));
	m_pio->out_pb_callback().set(FUNC(max80_state::pio_pb_w));

	COM8116(config, m_brg, 5'068'800);   // A few gates wired up as an oscillator. Frequency guessed.
	m_brg->fr_handler().set(m_uart, FUNC(z80sio_device::rxca_w));
	m_brg->ft_handler().set(m_uart, FUNC(z80sio_device::rxcb_w));

	MSM5832(config, m_rtc, 32.768_kHz_XTAL);

	Z80SIO(config, m_uart, 15'200'000 / 3);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 800).add_route(ALL_OUTPUTS, "mono", 0.50);
	TIMER(config, m_beep_timer).configure_generic(FUNC(max80_state::beep_timer));
}


/***************************************************************************

  Game driver(s)

***************************************************************************/


ROM_START(max80)
	ROM_REGION(0x0200, "maincpu",0)
	ROM_LOAD("max80.e12", 0x0000, 0x0200, CRC(cf316f25) SHA1(78663711c6100a67ef18382284565feda2bbbf77) )
ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT    COMPAT    MACHINE   INPUT     CLASS          INIT             COMPANY          FULLNAME               FLAGS
COMP( 1982, max80,    0,        trs80l2,  max80,    max80,    max80_state, empty_init,    "Lobo Systems",      "MAX-80",        MACHINE_NOT_WORKING ) //| MACHINE_SUPPORTS_SAVE )



mbc020.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Preliminary driver for single-board computers by Synertek/Sym Systems.

    The bottom edge connector is compatible with Motorola's EXORciser/
    Micromodule bus. One of MBC020's other connectors is claimed to allow
    "Direct Attachment to a CRT Monitor"; this was omitted in MBC010.

    MBC010 and MBC020 were available with either a SY6512 CPU (-65) or
    MC6800 CPU (-68), running at either 1 or 2 MHz.

    Though the timing circuit includes dynamic RAM refresh control, the
    onboard RAM is entirely static (6 SY2114s or equivalent, plus an optional
    socketed 6116).

    "Sym Systems Corp." may have been a short-lived spinoff or subsidiary of
    Synertek, named in reference to their SYM-1 SBC. Synertek clearly did not
    insist on in-house sourcing of the 6500-series peripherals, since their
    catalog photo of the MBC020 includes a Motorola-branded MC6845 CRTC, and
    the dumped board has a Rockwell VIA and AMI ACIA.

****************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/m6502/m6502.h"
#include "machine/6522via.h"
#include "machine/mos6551.h"
#include "video/mc6845.h"
#include "screen.h"


namespace {

class mbc020_state : public driver_device
{
public:
	mbc020_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_videoram(*this, "videoram")
		, m_chargen(*this, "chargen")
	{
	}

	void mbc020(machine_config &config);

private:
	MC6845_UPDATE_ROW(update_row);
	MC6845_ON_UPDATE_ADDR_CHANGED(update_cb);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_shared_ptr<u8> m_videoram;
	required_region_ptr<u8> m_chargen;
};

MC6845_UPDATE_ROW(mbc020_state::update_row)
{
	u32 *pix = &bitmap.pix(y);

	for (int x = 0; x < x_count; x++)
	{
		u8 data = m_videoram[(ma + x) & 0x7ff];

		// No XOR logic evident on PCB
		u8 dots = x == cursor_x ? 0xff : m_chargen[(data & 0x7f) | (ra & 15) << 7];

		// Guess as to how "Dual Intensity Video Levels" works
		rgb_t fg = BIT(data, 7) ? rgb_t(0xc0, 0xc0, 0xc0) : rgb_t::white();
		for (int n = 7; n >= 0; n--)
			*pix++ = BIT(dots, n) ? fg : rgb_t::black();
	}
}

MC6845_ON_UPDATE_ADDR_CHANGED(mbc020_state::update_cb)
{
}


void mbc020_state::mem_map(address_map &map)
{
	map(0x0000, 0x03ff).ram();
	map(0x8000, 0x8fff).rom().region("monitor", 0x2000);
	map(0x9000, 0x900f).m("extvia", FUNC(via6522_device::map));
	map(0x9900, 0x990f).m("via", FUNC(via6522_device::map));
	map(0x9a00, 0x9a03).rw("acia", FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x9c00, 0x9c00).rw("crtc", FUNC(sy6545_1_device::status_r), FUNC(sy6545_1_device::address_w));
	map(0x9c01, 0x9c01).rw("crtc", FUNC(sy6545_1_device::register_r), FUNC(sy6545_1_device::register_w));
	map(0xa000, 0xa7ff).ram();
	map(0xa800, 0xafff).ram().share("videoram");
	map(0xb000, 0xbfff).rom().region("monitor", 0);
	map(0xe000, 0xefff).rom().region("monitor", 0x1000);
	map(0xf800, 0xffff).rom().region("monitor", 0x2800);
}

static INPUT_PORTS_START(mbc020)
	PORT_START("PA")
	PORT_CONFNAME(0x38, 0x30, "Baud Rate")
	PORT_CONFSETTING(0x00, "110")
	PORT_CONFSETTING(0x08, "300")
	PORT_CONFSETTING(0x10, "600")
	PORT_CONFSETTING(0x18, "1200")
	PORT_CONFSETTING(0x20, "2400")
	PORT_CONFSETTING(0x28, "4800")
	PORT_CONFSETTING(0x30, "9600")
	PORT_CONFSETTING(0x38, "19200")
	PORT_BIT(0xc3, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

static DEVICE_INPUT_DEFAULTS_START( keyboard )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END

void mbc020_state::mbc020(machine_config &config)
{
	M6512(config, m_maincpu, 16_MHz_XTAL / 8); // SYU6512A
	m_maincpu->set_addrmap(AS_PROGRAM, &mbc020_state::mem_map);

	MOS6522(config, "via", 16_MHz_XTAL / 8); // R6522AP

	via6522_device &extvia(MOS6522(config, "extvia", 16_MHz_XTAL / 8)); // not on main board
	extvia.readpa_handler().set_ioport("PA");

	mos6551_device &acia(MOS6551(config, "acia", 16_MHz_XTAL / 8)); // S6551AP
	acia.set_xtal(1.8432_MHz_XTAL);
	acia.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	acia.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	acia.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(16_MHz_XTAL, 1016, 0, 640, 263, 0, 225);
	screen.set_screen_update("crtc", FUNC(sy6545_1_device::screen_update));

	sy6545_1_device &crtc(SY6545_1(config, "crtc", 16_MHz_XTAL / 8)); // SY6545-1
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(8);
	crtc.set_update_row_callback(FUNC(mbc020_state::update_row));
	crtc.set_on_update_addr_change_callback(FUNC(mbc020_state::update_cb));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "keyboard"));
	rs232.set_option_device_input_defaults("keyboard", DEVICE_INPUT_DEFAULTS_NAME(keyboard));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));
	rs232.rxd_handler().set("acia", FUNC(mos6551_device::write_rxd));
	rs232.dsr_handler().set("acia", FUNC(mos6551_device::write_dsr));
	rs232.cts_handler().set("acia", FUNC(mos6551_device::write_cts));
}

ROM_START(mbc020) // Silkscreened on PCB: "© 1980 by SYM Systems Corp."
	ROM_REGION(0x3000, "monitor", 0) // "SERVOMON VER 4.0 COPYRIGHT JUL-1983 TORQUE SYSTEMS INC."
	ROM_LOAD("20013-4.u13", 0x0000, 0x1000, CRC(53cbfc68) SHA1(72834ac1d8e8feed1941c7b7d53b264d8333a496)) // TMS2532JL-35
	ROM_LOAD("20014-4.u14", 0x1000, 0x1000, CRC(f7ed5508) SHA1(a6d644f07c889c24291fe6d64f9ef90ef34324ba)) // TMS2532JL-35
	ROM_LOAD("20015-4.u15", 0x2000, 0x1000, CRC(17485482) SHA1(57a6f684dd5111f2499b655f27794116aef354d7)) // TMS2532JL-35

	ROM_REGION(0x800, "chargen", 0)
	ROM_LOAD("02-0054a.u5", 0x000, 0x800, CRC(3ed97af7) SHA1(26d5a1c96b9896336e7ccf9e66dbeb2733ab4593))

	ROM_REGION(0x20, "mmap", 0) // Memory mapping PROM (decodes A11–A15)
	ROM_LOAD("n82s123n.u17", 0x00, 0x20, CRC(f219550d) SHA1(b149f9872bc9091b28b0da65f0206ce9893083be))
ROM_END

} // anonymous namespace


COMP(1983, mbc020, 0, 0, mbc020, mbc020, mbc020_state, empty_init, "Sym Systems / Torque Systems", "MBC020-65 CPU/Video Board (Torque Systems OEM)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)



mbc200.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************

Sanyo MBC-200

Machine MBC-1200 is identical but sold outside of Japan

16 x HM6116P-3 2K x 8 SRAM soldered onboard (so 32k ram)
4 x HM6116P-3 2K x 8 SRAM socketed (so 8k ram)
4 x MB83256 32K x 8 socketed (128k rom)
Floppy = 5.25"
MBC1200 has one floppy while MBC1250 has 2. The systems are otherwise identical.

Keyboard communicates serially to UART at E0,E1. The keyboard uses an undumped
8748 microcontroller with a 12*8 key matrix. The input codes are not ASCII, so
using custom code until the required details become available.

On back side:
- keyboard DIN connector
- Centronics printer port
- RS-232C 25pin connector

SBASIC:
Running programs: the file names used within SBASIC must be in
uppercase. For example, run "DEMO" .
You can also run a basic program from CP/M: sbasic "GRAPHICS" .
To Break, press either ^N or ^O (display freezes), then ^C .
Some control keys: 0x14 = Home; 0x8 = Left/BS; 0xA = Down; 0xB = Up; 0xC = Right.
GAIJI.BAS doesn't work because GAIJI.FNT is missing.

TODO:
- Less expensive synchronisation between CPUs
- UART connections
- PPI 9F PA0 and PA1 are connected to jumpers
- Any other devices?

****************************************************************************/

#include "emu.h"

#include "bus/centronics/ctronics.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/keyboard.h"
#include "machine/output_latch.h"
#include "machine/wd_fdc.h"
#include "sound/spkrdev.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class mbc200_state : public driver_device
{
public:
	mbc200_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_palette(*this, "palette")
		, m_crtc(*this, "crtc")
		, m_ppi_m(*this, "ppi_m")
		, m_ppi_s(*this, "ppi_s")
		, m_vram(*this, "vram")
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_speaker(*this, "speaker")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0)
		, m_printer(*this, "printer")
	{ }

	void mbc200(machine_config &config) ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u8 boot_m1_r(offs_t offset);
	u8 sub_io_r(offs_t offset);
	void sub_io_w(offs_t offset, u8 data);
	u8 ps_porta_r();
	template <unsigned Bit> void ps_porta_w(int state);
	void ps_portc_w(u8 data);
	u8 pm_porta_r();
	u8 pm_portc_r();
	void pm_portb_w(u8 data);
	void pm_portc_w(u8 data);
	u8 keyboard_r(offs_t offset);
	void kbd_put(u8 data);
	MC6845_UPDATE_ROW(update_row);

	void main_mem(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;
	void main_opcodes(address_map &map) ATTR_COLD;
	void sub_mem(address_map &map) ATTR_COLD;
	void sub_io(address_map &map) ATTR_COLD;

	required_device<palette_device> m_palette;
	required_device<mc6845_device> m_crtc;
	required_device<i8255_device> m_ppi_m;
	required_device<i8255_device> m_ppi_s;
	required_shared_ptr<u8> m_vram;
	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_device<speaker_sound_device> m_speaker;
	required_device<mb8876_device> m_fdc;
	required_device_array<floppy_connector, 4> m_floppy;
	required_device<centronics_device> m_printer;

	u8 m_cpu_m_sound = 0U;
	u8 m_cpu_s_sound = 0U;
	u8 m_pm_portc = 0xffU;
	u8 m_ps_porta = 0xffU;
	u8 m_comm_data = 0xffU;
	u8 m_term_data = 0U;
};


void mbc200_state::main_mem(address_map &map)
{
	map(0x0000, 0xffff).ram().share(m_ram);
}

void mbc200_state::main_opcodes(address_map &map)
{
	// set up on reset
}

void mbc200_state::main_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);

	//map(0xe0, 0xe1).rw("uart1", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xe0, 0xe1).mirror(0x02).r(FUNC(mbc200_state::keyboard_r)).nopw();
	map(0xe4, 0xe7).rw(m_fdc, FUNC(mb8876_device::read), FUNC(mb8876_device::write));
	map(0xe8, 0xeb).rw(m_ppi_m, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xec, 0xed).mirror(0x02).rw("uart2", FUNC(i8251_device::read), FUNC(i8251_device::write));
}


void mbc200_state::sub_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom();
	map(0x2000, 0x3fff).ram();
	//map(0x4000, 0x7fff).bankr(...); missing dumps of MB83256 ROMs at 13J, 11J, 10J and 12J
	map(0x8000, 0xffff).ram().share("vram");
}

void mbc200_state::sub_io(address_map &map)
{
	// I/O decoding is sloppy:
	// A7 low selects PPI 9F (read/write)
	// A6 low selects CRTC 11D (write only)
	// A5 low selects main CPU communication (read/write)
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0xff).rw(FUNC(mbc200_state::sub_io_r), FUNC(mbc200_state::sub_io_w));
}



u8 mbc200_state::boot_m1_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
	{
		m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0xffff, &m_ram[0]);
		m_maincpu->space(AS_OPCODES).install_rom(0x0000, 0xffff, &m_ram[0]);
	}
	return m_ram[0x8000 | offset];
}


u8 mbc200_state::sub_io_r(offs_t offset)
{
	u8 result = 0xff;

	if (!BIT(offset, 7))
		result &= m_ppi_s->read(offset & 0x03);

	if (!BIT(offset, 5))
		result &= m_ppi_m->acka_r();

	return result;
}

void mbc200_state::sub_io_w(offs_t offset, u8 data)
{
	if (!BIT(offset, 7))
		m_ppi_s->write(offset & 0x03, data);

	if (!BIT(offset, 6))
	{
		if (BIT(offset, 0))
			m_crtc->register_w(data);
		else
			m_crtc->address_w(data);
	}

	if (!BIT(offset, 5))
	{
		m_comm_data = data;
		m_ppi_m->pc4_w(0);
		m_ppi_m->pc4_w(1);
	}
}


u8 mbc200_state::ps_porta_r()
{
	return m_ps_porta;
}

template <unsigned Bit>
void mbc200_state::ps_porta_w(int state)
{
	if (state)
		m_ps_porta |= u8(1) << Bit;
	else
		m_ps_porta &= ~(u8(1) << Bit);
}

void mbc200_state::ps_portc_w(u8 data)
{
	// FIXME: PC0, PC1, PC2 select ROM bank

	m_pm_portc = BIT(data, 3) ? 0xff : 0xfe;

	m_cpu_s_sound = BIT(data, 4); // used by beep command in basic
	m_speaker->level_w(m_cpu_m_sound + m_cpu_s_sound);

	m_printer->write_init(BIT(data, 6));
	m_printer->write_strobe(BIT(~data, 7));
}

u8 mbc200_state::pm_porta_r()
{
	// Gets whatever happens to be on the slave CPU's data bus.
	// However, we know this only happens on I/O writes with A5 low.
	return m_comm_data;
}

u8 mbc200_state::pm_portc_r()
{
	return m_pm_portc;
}

// Writing to PPI port B ($E9).  Being programmed for output, read operations will get the current value.
void mbc200_state::pm_portb_w(u8 data)
{
	ps_porta_w<5>(BIT(data, 0));

	m_cpu_m_sound = BIT(data, 1); // key-click
	m_speaker->level_w(m_cpu_m_sound + m_cpu_s_sound);

	// The BIOS supports up to 4 drives, (2 internal + 2 external)
	// E: and F: are virtual swaps of A: and B:
	u8 const drivenum = (data & 0x70) >> 4;
	floppy_image_device *floppy = nullptr;
	if (drivenum < 4)
		floppy = m_floppy[drivenum]->get_device();

	m_fdc->set_floppy(floppy);

	if (floppy)
	{
		floppy->mon_w(0);
		floppy->ss_w(BIT(data, 7));
	}
}

void mbc200_state::pm_portc_w(u8 data)
{
	ps_porta_w<6>(BIT(data, 5)); // IBF
	ps_porta_w<7>(BIT(data, 7)); // /OBF
}


/* Input ports */
static INPUT_PORTS_START( mbc200 )
INPUT_PORTS_END

u8 mbc200_state::keyboard_r(offs_t offset)
{
	u8 data = 0;
	if (offset)
	{
		if (m_term_data)
		{
			data = 2;
			// handle CTRL key pressed
			if (m_term_data < 0x20)
			{
				data |= 8;
				m_term_data |= 0x40;
			}
		}
	}
	else
	{
		data = m_term_data;
		m_term_data = 0;
	}

	return data;
}

// convert standard control keys to expected code;
void mbc200_state::kbd_put(u8 data)
{
	switch (data)
	{
		case 0x0e:
			m_term_data = 0xe2;
			break;
		case 0x0f:
			m_term_data = 0xe3;
			break;
		case 0x08:
			m_term_data = 0xe4;
			break;
		case 0x09:
			m_term_data = 0xe5;
			break;
		case 0x0a:
			m_term_data = 0xe6;
			break;
		case 0x0d:
			m_term_data = 0xe7;
			break;
		case 0x1b:
			m_term_data = 0xe8;
			break;
		default:
			m_term_data = data;
	}
}

void mbc200_state::machine_start()
{
	save_item(NAME(m_cpu_m_sound));
	save_item(NAME(m_cpu_s_sound));
	save_item(NAME(m_pm_portc));
	save_item(NAME(m_ps_porta));
	save_item(NAME(m_comm_data));
	save_item(NAME(m_term_data));
}

void mbc200_state::machine_reset()
{
	m_maincpu->space(AS_PROGRAM).unmap_readwrite(0x0000, 0x7fff);
	m_maincpu->space(AS_PROGRAM).install_rom(0x0000, 0x0fff, 0x7000, &m_rom[0]);
	m_maincpu->space(AS_OPCODES).install_rom(0x0000, 0x0fff, 0x7000, &m_rom[0]);
	m_maincpu->space(AS_OPCODES).install_read_handler(0x8000, 0xffff, emu::rw_delegate(*this, FUNC(mbc200_state::boot_m1_r)));
}

static void mbc200_floppies(device_slot_interface &device)
{
	device.option_add("qd", FLOPPY_525_QD);
}

MC6845_UPDATE_ROW( mbc200_state::update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u32 *p = &bitmap.pix(y);

	for (u16 x = 0; x < x_count; x++)
	{
		u16 mem = (ma+x)*4+ra;
		u8 gfx = m_vram[mem & 0x7fff];
		*p++ = palette[BIT(gfx, 7)];
		*p++ = palette[BIT(gfx, 6)];
		*p++ = palette[BIT(gfx, 5)];
		*p++ = palette[BIT(gfx, 4)];
		*p++ = palette[BIT(gfx, 3)];
		*p++ = palette[BIT(gfx, 2)];
		*p++ = palette[BIT(gfx, 1)];
		*p++ = palette[BIT(gfx, 0)];
	}
}

static const gfx_layout charlayout =
{
	8,8,
	256,
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};

static GFXDECODE_START( gfx_mbc200 )
	GFXDECODE_ENTRY( "subcpu", 0x1800, charlayout, 0, 1 )
GFXDECODE_END


void mbc200_state::mbc200(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 16_MHz_XTAL / 4); // NEC D780C-1
	m_maincpu->set_addrmap(AS_PROGRAM, &mbc200_state::main_mem);
	m_maincpu->set_addrmap(AS_IO, &mbc200_state::main_io);
	m_maincpu->set_addrmap(AS_OPCODES, &mbc200_state::main_opcodes);

	z80_device &subcpu(Z80(config, "subcpu", 16_MHz_XTAL / 4)); // NEC D780C-1
	subcpu.set_addrmap(AS_PROGRAM, &mbc200_state::sub_mem);
	subcpu.set_addrmap(AS_IO, &mbc200_state::sub_io);

	config.set_perfect_quantum(m_maincpu); // lazy way to keep CPUs in sync

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(16_MHz_XTAL, 816, 0, 640, 420, 0, 400);
	screen.set_screen_update(m_crtc, FUNC(hd6845s_device::screen_update));
	screen.set_color(rgb_t::green());
	GFXDECODE(config, "gfxdecode", m_palette, gfx_mbc200);
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	HD6845S(config, m_crtc, 16_MHz_XTAL / 8); // HD46505SP
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(mbc200_state::update_row));

	// sound
	SPEAKER(config, "mono").front_center();
	static const double speaker_levels[4] = { 0.0, 0.6, 1.0 };
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
	m_speaker->set_levels(3, speaker_levels);

	I8255(config, m_ppi_m);
	m_ppi_m->in_pa_callback().set(FUNC(mbc200_state::pm_porta_r));
	m_ppi_m->in_pc_callback().set(FUNC(mbc200_state::pm_portc_r));
	m_ppi_m->out_pb_callback().set(FUNC(mbc200_state::pm_portb_w));
	m_ppi_m->out_pc_callback().set(FUNC(mbc200_state::pm_portc_w));

	I8255(config, m_ppi_s).out_pc_callback();
	m_ppi_s->in_pa_callback().set(FUNC(mbc200_state::ps_porta_r));
	m_ppi_s->out_pb_callback().set("printdata", FUNC(output_latch_device::write));
	m_ppi_s->out_pc_callback().set(FUNC(mbc200_state::ps_portc_w));

	i8251_device &uart1(I8251(config, "uart1", 0)); // INS8251N
	//uart1.txd_handler().set(...); to keyboard
	uart1.rts_handler().set("uart1", FUNC(i8251_device::write_cts));

	i8251_device &uart2(I8251(config, "uart2", 0)); // INS8251A
	uart2.txd_handler().set("rs232c", FUNC(rs232_port_device::write_txd));
	uart2.rts_handler().set("rs232c", FUNC(rs232_port_device::write_rts));
	uart2.dtr_handler().set("rs232c", FUNC(rs232_port_device::write_dtr));

	MB8876(config, m_fdc, 16_MHz_XTAL / 16);

	FLOPPY_CONNECTOR(config,  m_floppy[0], mbc200_floppies, "qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config,  m_floppy[1], mbc200_floppies, "qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config,  m_floppy[2], mbc200_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config,  m_floppy[3], mbc200_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	// printer
	CENTRONICS(config, m_printer, centronics_devices, "printer");
	m_printer->set_output_latch(OUTPUT_LATCH(config, "printdata"));
	m_printer->busy_handler().set(FUNC(mbc200_state::ps_porta_w<2>)).invert();
	m_printer->perror_handler().set(FUNC(mbc200_state::ps_porta_w<3>));
	m_printer->select_handler().set(FUNC(mbc200_state::ps_porta_w<4>));

	// RS-232C
	rs232_port_device &rs232c(RS232_PORT(config, "rs232c", default_rs232_devices, nullptr));
	rs232c.rxd_handler().set("uart2", FUNC(i8251_device::write_rxd));
	rs232c.cts_handler().set("uart2", FUNC(i8251_device::write_cts));
	rs232c.dsr_handler().set("uart2", FUNC(i8251_device::write_dsr));

	// keyboard
	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(mbc200_state::kbd_put));

	// software lists
	SOFTWARE_LIST(config, "flop_list").set_original("mbc200");
}

/* ROM definition */
ROM_START( mbc200 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "d2732a.bin",  0x0000, 0x1000, CRC(bf364ce8) SHA1(baa3a20a5b01745a390ef16628dc18f8d682d63b))

	ROM_REGION( 0x2000, "subcpu", ROMREGION_ERASEFF )
	ROM_LOAD( "m5l2764.bin", 0x0000, 0x2000, CRC(377300a2) SHA1(8563172f9e7f84330378a8d179f4138be5fda099))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY  FULLNAME   FLAGS
COMP( 1982, mbc200, 0,      0,      mbc200,  mbc200, mbc200_state, empty_init, "Sanyo", "MBC-200", MACHINE_SUPPORTS_SAVE )



mbc55x.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Phill Harvey-Smith
/*
    drivers/mbc55x.cpp

    Machine driver for the Sanyo MBC-550 and MBC-555.

    Phill Harvey-Smith
    2011-01-29.

*/


#include "emu.h"
#include "mbc55x.h"
#include "bus/isa/isa.h"
#include "bus/isa/isa_cards.h"
#include "bus/rs232/rs232.h"
#include "machine/clock.h"
#include "machine/i8087.h"
#include "machine/input_merger.h"
#include "mbc55x_kbd.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


void mbc55x_state::mbc55x_mem(address_map &map)
{
	map(0x00000, 0x0FFFF).bankrw(RAM_BANK00_TAG);
	map(0x10000, 0x1FFFF).bankrw(RAM_BANK01_TAG);
	map(0x20000, 0x2FFFF).bankrw(RAM_BANK02_TAG);
	map(0x30000, 0x3FFFF).bankrw(RAM_BANK03_TAG);
	map(0x40000, 0x4FFFF).bankrw(RAM_BANK04_TAG);
	map(0x50000, 0x5FFFF).bankrw(RAM_BANK05_TAG);
	map(0x60000, 0x6FFFF).bankrw(RAM_BANK06_TAG);
	map(0x70000, 0x7FFFF).bankrw(RAM_BANK07_TAG);
	map(0x80000, 0x8FFFF).bankrw(RAM_BANK08_TAG);
	map(0x90000, 0x9FFFF).bankrw(RAM_BANK09_TAG);
	map(0xA0000, 0xAFFFF).bankrw(RAM_BANK0A_TAG);
	map(0xB0000, 0xBFFFF).bankrw(RAM_BANK0B_TAG);
	map(0xC0000, 0xCFFFF).bankrw(RAM_BANK0C_TAG);
	map(0xD0000, 0xDFFFF).bankrw(RAM_BANK0D_TAG);
	map(0xE0000, 0xEFFFF).bankrw(RAM_BANK0E_TAG);
	map(0xF0000, 0xF3FFF).bankrw(RED_PLANE_TAG);
	map(0xF4000, 0xF7FFF).bankrw(BLUE_PLANE_TAG);
	map(0xF8000, 0xFBFFF).noprw();
	map(0xFC000, 0xFDFFF).rom().nopw().region(MAINCPU_TAG, 0x0000).mirror(0x002000);
}

void mbc55x_state::mbc55x_io(address_map &map)
{
	map(0x0000, 0x0000).select(0x003e).rw(FUNC(mbc55x_state::iodecode_r), FUNC(mbc55x_state::iodecode_w));
}

void mbc55x_state::mbc55x_iodecode(address_map &map)
{
	map(0x00, 0x01).mirror(0x02).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x04, 0x07).rw(m_fdc, FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	map(0x08, 0x08).mirror(0x03).rw(FUNC(mbc55x_state::vram_page_r), FUNC(mbc55x_state::vram_page_w));
	map(0x0c, 0x0f).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x10, 0x13).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x14, 0x15).mirror(0x02).rw("sio", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x18, 0x18).mirror(0x02).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x19, 0x19).mirror(0x02).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x1c, 0x1d).mirror(0x02).rw(m_kb_uart, FUNC(i8251_device::read), FUNC(i8251_device::write));
}

uint8_t mbc55x_state::iodecode_r(offs_t offset)
{
	return m_iodecode->read8(offset >> 1);
}

void mbc55x_state::iodecode_w(offs_t offset, uint8_t data)
{
	m_iodecode->write8(offset >> 1, data);
}

/* 8255 Configuration */

uint8_t mbc55x_state::game_io_r()
{
	u8 result = m_gameio->sw3_r();
	result |= m_gameio->sw2_r() << 1;
	result |= m_gameio->sw1_r() << 2;
	result |= m_gameio->sw0_r() << 3;

	for (int i = 0; i < 4; i++)
		if (machine().time().as_double() < m_ls123_clear_time[i])
			result |= 1 << (4 + i);

	return result;
}

uint8_t mbc55x_state::printer_status_r()
{
	return m_printer_status;
}

void mbc55x_state::printer_data_w(uint8_t data)
{
	m_printer->write_data7(!BIT(data, 7));
	m_printer->write_data6(!BIT(data, 6));
	m_printer->write_data5(!BIT(data, 5));
	m_printer->write_data4(!BIT(data, 4));
	m_printer->write_data3(!BIT(data, 3));
	m_printer->write_data2(!BIT(data, 2));
	m_printer->write_data1(!BIT(data, 1));
	m_printer->write_data0(!BIT(data, 0));

	m_gameio->an0_w(!BIT(data, 0));
	m_gameio->an1_w(!BIT(data, 1));
	m_gameio->an2_w(!BIT(data, 2));
	m_gameio->an3_w(!BIT(data, 3));
	m_gameio->an4_w(!BIT(data, 4));
	m_gameio->strobe_w(!BIT(data, 5));

	if (m_ls123_strobe != BIT(data, 7))
	{
		if (BIT(data, 7))
		{
			m_ls123_clear_time[0] = machine().time().as_double() + m_x_calibration * m_gameio->pdl0_r();
			m_ls123_clear_time[1] = machine().time().as_double() + m_y_calibration * m_gameio->pdl1_r();
			m_ls123_clear_time[2] = machine().time().as_double() + m_x_calibration * m_gameio->pdl2_r();
			m_ls123_clear_time[3] = machine().time().as_double() + m_y_calibration * m_gameio->pdl3_r();
		}

		m_ls123_strobe = BIT(data, 7);
	}
}

void mbc55x_state::disk_select_w(uint8_t data)
{
	floppy_image_device *floppy = nullptr;

	switch (data & 0x03)
	{
	case 0: floppy = m_floppy[0]->get_device(); break;
	case 1: floppy = m_floppy[1]->get_device(); break;
	case 2: floppy = m_floppy[2]->get_device(); break;
	case 3: floppy = m_floppy[3]->get_device(); break;
	}

	m_fdc->set_floppy(floppy);

	if (floppy)
	{
		floppy->mon_w(0);
		floppy->ss_w(BIT(data, 2));
	}

	m_printer->write_strobe(!BIT(data, 3));
}

void mbc55x_state::printer_busy_w(int state)
{
	m_printer_status = (m_printer_status & 0xef) | (state ? 0x10 : 0x00);
}

void mbc55x_state::printer_paper_end_w(int state)
{
	m_printer_status = (m_printer_status & 0xdf) | (state ? 0x20 : 0x00);
}

void mbc55x_state::printer_select_w(int state)
{
	m_printer_status = (m_printer_status & 0xbf) | (state ? 0x40 : 0x00);
}


void mbc55x_state::set_ram_size()
{
	address_space   &space      = m_maincpu->space( AS_PROGRAM );
	int             ramsize     = m_ram->size();
	int             nobanks     = ramsize / RAM_BANK_SIZE;
	char            bank[10];
	int             bankno;
	uint8_t           *ram        = &m_ram->pointer()[0];
	uint8_t           *map_base;
	int             bank_base;


	logerror("Ramsize is %d bytes\n",ramsize);
	logerror("RAM_BANK_SIZE=%d, nobanks=%d\n",RAM_BANK_SIZE,nobanks);

	// Main memory mapping

	for(bankno=0; bankno<RAM_BANK_COUNT; bankno++)
	{
		sprintf(bank,"bank%x",bankno);
		bank_base=bankno*RAM_BANK_SIZE;
		map_base=&ram[bank_base];

		if(bankno<nobanks)
		{
			membank(bank)->set_base(map_base);
			space.install_readwrite_bank(bank_base, bank_base+(RAM_BANK_SIZE-1), membank(bank));
			logerror("Mapping bank %d at %05X to RAM\n",bankno,bank_base);
		}
		else
		{
			space.nop_readwrite(bank_base, bank_base+(RAM_BANK_SIZE-1));
			logerror("Mapping bank %d at %05X to NOP\n",bankno,bank_base);
		}
	}

	// Graphics red and blue plane memory mapping, green is in main memory
	membank(RED_PLANE_TAG)->set_base(&m_video_mem[RED_PLANE_OFFSET]);
	space.install_readwrite_bank(RED_PLANE_MEMBASE, RED_PLANE_MEMBASE+(COLOUR_PLANE_SIZE-1), membank(RED_PLANE_TAG));
	membank(BLUE_PLANE_TAG)->set_base(&m_video_mem[BLUE_PLANE_OFFSET]);
	space.install_readwrite_bank(BLUE_PLANE_MEMBASE, BLUE_PLANE_MEMBASE+(COLOUR_PLANE_SIZE-1), membank(BLUE_PLANE_TAG));
}

void mbc55x_state::machine_reset()
{
	set_ram_size();
}

void mbc55x_state::machine_start()
{
	// FIXME: values copied from apple2.cpp
	m_x_calibration = attotime::from_nsec(10800).as_double();
	m_y_calibration = attotime::from_nsec(10800).as_double();

	m_printer_status = 0xff;

	m_ls123_strobe = true;
	std::fill(std::begin(m_ls123_clear_time), std::end(m_ls123_clear_time), 0.0);

	m_kb_uart->write_cts(0);
}


static INPUT_PORTS_START( mbc55x )
INPUT_PORTS_END


// MBC-550 : 1 x 5.25" disk-drive (160 KB)
// MBC-555 : 2 x 5.25" disk-drive (160 KB)
// MBC-555-2 : 2 x 5.25" disk-drive (360 KB)
// MBC-555-3 : 2 x 5.25" disk-drive (720 KB)

static void mbc55x_floppies(device_slot_interface &device)
{
	device.option_add("ssdd", FLOPPY_525_SSDD);
	device.option_add("dd", FLOPPY_525_DD);
	device.option_add("qd", FLOPPY_525_QD);
}


void mbc55x_state::mbc55x(machine_config &config)
{
	/* basic machine hardware */
	I8088(config, m_maincpu, 14.318181_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &mbc55x_state::mbc55x_mem);
	m_maincpu->set_addrmap(AS_IO, &mbc55x_state::mbc55x_io);
	m_maincpu->set_irq_acknowledge_callback(PIC8259_TAG, FUNC(pic8259_device::inta_cb));
	m_maincpu->esc_opcode_handler().set("coproc", FUNC(i8087_device::insn_w));
	m_maincpu->esc_data_handler().set("coproc", FUNC(i8087_device::addr_w));

	i8087_device &i8087(I8087(config, "coproc", 14.318181_MHz_XTAL / 4));
	i8087.set_space_88(m_maincpu, AS_PROGRAM);
	i8087.irq().set(m_pic, FUNC(pic8259_device::ir6_w));
	i8087.busy().set_inputline("maincpu", INPUT_LINE_TEST);

	ADDRESS_MAP_BANK(config, m_iodecode);
	m_iodecode->endianness(ENDIANNESS_LITTLE);
	m_iodecode->data_width(8);
	m_iodecode->addr_width(5);
	m_iodecode->set_addrmap(0, &mbc55x_state::mbc55x_iodecode);

	mbc55x_keyboard_device &keyboard(MBC55X_KEYBOARD(config, "keyboard"));
	keyboard.txd_callback().set(m_kb_uart, FUNC(i8251_device::write_rxd));

	/* video hardware */
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_raw(14.318181_MHz_XTAL, 896, 0, 640, 262, 0, 200);
	screen.set_screen_update(VID_MC6845_NAME, FUNC(mc6845_device::screen_update));

	PALETTE(config, m_palette, FUNC(mbc55x_state::mbc55x_palette), SCREEN_NO_COLOURS * 3);

	RAM(config, RAM_TAG).set_default_size("128K").set_extra_options("128K,192K,256K,320K,384K,448K,512K,576K,640K");

	/* sound hardware */
	SPEAKER(config, MONO_TAG).front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, MONO_TAG, 0.75);

	/* Devices */
	I8251(config, m_kb_uart, 14.318181_MHz_XTAL / 8);
	m_kb_uart->txd_handler().set("speaker", FUNC(speaker_sound_device::level_w)).invert();
	m_kb_uart->rts_handler().set(m_printer, FUNC(centronics_device::write_init)).invert();
	m_kb_uart->rxrdy_handler().set(m_pic, FUNC(pic8259_device::ir3_w));

	PIT8253(config, m_pit);
	m_pit->out_handler<0>().set(m_pic, FUNC(pic8259_device::ir0_w));
	m_pit->out_handler<0>().append(m_pit, FUNC(pit8253_device::write_clk1));
	m_pit->out_handler<1>().set(m_pic, FUNC(pic8259_device::ir1_w));
	m_pit->set_clk<2>(14.318181_MHz_XTAL / 8);
	m_pit->out_handler<2>().set("sio", FUNC(i8251_device::write_txc));
	m_pit->out_handler<2>().append("sio", FUNC(i8251_device::write_rxc));
	m_pit->out_handler<2>().append("line", FUNC(rs232_port_device::write_etc));

	clock_device &clk_78_6khz(CLOCK(config, "clk_78.6khz", 14.318181_MHz_XTAL / 14 / 13));
	clk_78_6khz.signal_handler().set(m_pit, FUNC(pit8253_device::write_clk0));
	clk_78_6khz.signal_handler().append(m_kb_uart, FUNC(i8251_device::write_txc));
	clk_78_6khz.signal_handler().append(m_kb_uart, FUNC(i8251_device::write_rxc));

	PIC8259(config, m_pic);
	m_pic->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	I8255(config, m_ppi);
	m_ppi->in_pa_callback().set(FUNC(mbc55x_state::game_io_r));
	m_ppi->out_pb_callback().set(FUNC(mbc55x_state::printer_data_w));
	m_ppi->in_pc_callback().set(FUNC(mbc55x_state::printer_status_r));
	m_ppi->out_pc_callback().set(FUNC(mbc55x_state::disk_select_w));

	HD6845S(config, m_crtc, 14.318181_MHz_XTAL / 8); // HD46505SP-1
	m_crtc->set_screen(SCREEN_TAG);
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(mbc55x_state::crtc_update_row));
	m_crtc->out_vsync_callback().set(FUNC(mbc55x_state::vid_vsync_changed));
	m_crtc->out_hsync_callback().set(FUNC(mbc55x_state::vid_hsync_changed));

	/* Backing storage */
	FD1793(config, m_fdc, 14.318181_MHz_XTAL / 14); // M5W1793-02P (clock is nominally 1 MHz)
	m_fdc->intrq_wr_callback().set(m_pic, FUNC(pic8259_device::ir5_w));

	FLOPPY_CONNECTOR(config, m_floppy[0], mbc55x_floppies, "qd", floppy_image_device::default_pc_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[1], mbc55x_floppies, "qd", floppy_image_device::default_pc_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[2], mbc55x_floppies, nullptr, floppy_image_device::default_pc_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[3], mbc55x_floppies, nullptr, floppy_image_device::default_pc_floppy_formats);

	/* Software list */
	SOFTWARE_LIST(config, "disk_list").set_original("mbc55x");

	isa8_device &isa(ISA8(config, "isa", 14.318181_MHz_XTAL / 4));
	isa.set_memspace(m_maincpu, AS_PROGRAM);
	isa.set_iospace(m_maincpu, AS_IO);
	isa.irq7_callback().set(m_pic, FUNC(pic8259_device::ir7_w)); // all other IRQ and DRQ lines are NC
	isa.iochck_callback().set_inputline(m_maincpu, INPUT_LINE_NMI).invert();

	ISA8_SLOT(config, "external", 0, "isa", pc_isa8_cards, nullptr, false);

	i8251_device &sio(I8251(config, "sio", 14.318181_MHz_XTAL / 8)); // on separate board, through 20-pin header
	sio.dtr_handler().set("line", FUNC(rs232_port_device::write_dtr));
	sio.txd_handler().set("line", FUNC(rs232_port_device::write_txd));
	sio.rts_handler().set("line", FUNC(rs232_port_device::write_rts));
	sio.rxrdy_handler().set("sioint", FUNC(input_merger_device::in_w<0>));
	sio.txrdy_handler().set("sioint", FUNC(input_merger_device::in_w<1>));

	rs232_port_device &serial(RS232_PORT(config, "line", default_rs232_devices, nullptr));
	serial.rxd_handler().set("sio", FUNC(i8251_device::write_rxd));
	serial.dsr_handler().set("sio", FUNC(i8251_device::write_dsr));
	serial.cts_handler().set("sio", FUNC(i8251_device::write_cts));

	INPUT_MERGER_ANY_HIGH(config, "sioint").output_handler().set(m_pic, FUNC(pic8259_device::ir2_w));

	APPLE2_GAMEIO(config, m_gameio, apple2_gameio_device::default_options, nullptr);
	m_gameio->set_sw_pullups(true); // 3300 ohm pullups to 5.0V on pins 2-4 and 16

	CENTRONICS(config, m_printer, centronics_devices, nullptr);
	m_printer->busy_handler().set(FUNC(mbc55x_state::printer_busy_w)).invert(); // LS14 Schmitt trigger
	m_printer->busy_handler().append(m_pic, FUNC(pic8259_device::ir4_w)).invert();
	m_printer->perror_handler().set(FUNC(mbc55x_state::printer_paper_end_w));
	m_printer->select_handler().set(FUNC(mbc55x_state::printer_select_w));
}


ROM_START( mbc55x )
	ROM_REGION(0x4000, MAINCPU_TAG, 0)

	ROM_SYSTEM_BIOS(0, "v120", "mbc55x BIOS v1.20 (1983)")
	ROMX_LOAD("mbc55x-v120.rom", 0x0000, 0x2000, CRC(b439b4b8) SHA1(6e8df0f3868e3fd0229a5c2720d6c01e46815cab), ROM_BIOS(0))
ROM_END

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY  FULLNAME   FLAGS
COMP( 1983, mbc55x, 0,      0,      mbc55x,  mbc55x, mbc55x_state, empty_init, "Sanyo", "MBC-55x", 0 )



mbee.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Driver completely rewritten by Robbbert, in a process begun on 2009-02-24.
Assistance/advice was gratefully received from:
    E.J.Wordsworth (owner of Microbee Systems), nama, ChickenMan,
    and the author of the "ubee512" emulator.

Previous driver was written by Juergen Buchmueller, Jan 2000 with assistance
from Brett Selwood and Andrew Davies.


    Keyboard notes are in video/microbee.cpp

    256tc: The 1.31 rom version which appears to fit the 256TC is actually
    part of the Z80 emulation in the Matilda model. If you fit this rom into a real
    256TC, the floppy disk will not be detected.

    The unemulated Matilda is a IBM XT clone fitted with a NEC V40, and has the
    ability to emulate the 256TC as mentioned above.

    The Premium Plus was a limited-edition kit from Microbee Systems, but we don't
    have any technical info or schematic as yet. It starts up, keyboard works, disks
    work much the same as a 128k or 256tc. It has 1024k of RAM.
    The kit itself has an extra custom FPGA CPU board with memory-card slot, but there's
    no info on it yet. We just emulate the Z80 portion.

    ICs on schematics, pcbs, and manuals which never made it into production machines:
    - Z80SCC;
    - SN76489A;
    - 2651A;
    - B & C roms on disk-based machines.

    Floppy formats:
    - All disks are the standard CPCEMU 'dsk' format.
    - Types are 9/13cm 40/80 track (single or double sided)
    - 13cm has been specified as QD to prevent a nasty crash if an 80-track disk was mounted
    - The tracks/sector layout is the same regardless of size
    - Although certain models came with particular drives as standard, users could add
      the other size if they wished. We support both sizes on any model.

    Early machines have 'standard' video (128 hires characters).
    Later machines had the option of 'premium' video which
    provides thousands of hires characters, enough to simulate
    bit-mapped graphics.

    Commands to call up built-in roms (depends on the model):
    NET - Jump to E000, usually the Telcom communications program.
          This rom can be replaced with the Dreamdisk Chip-8 rom.
        Note that Telcom 3.21 is 8k, it uses a rombank switch
        (by reading port 0A) to swap between the two halves.
        See Telcom notes below.

    MEM - same as NET.

    EDASM - Jump to C000, usually the Editor/Assembler package.

    MENU - Do a rombank switch to bank 5 and jump to C000 to start the Shell

    PAK n - Do a rombank switch (write to port 0A) to bank "n" and jump to C000.

    These early colour computers have a PROM to create the foreground palette.

    Notes about the printer:
    - Older models default to a 1200 baud serial printer, which we do not support.
    - You need to change it to parallel by entering OUTL#1 while in Basic.
    - After you mount/create a printfile, you can LPRINT and LLIST in Basic,
      or by using the printing option in other apps.

    Notes about Telcom:
    - On the older models, Telcom is called up by entering NET from within Basic. Models
      from the pc85 onwards have it as a menu option.
    - To exit, press Enter without any input. Disk versions, enter CPM or press ^C.
    - After being used, version 3 and up will enable the use of OUT#7 in Basic, which
      changes the screen to 80x24. Enter OUT#0 to revert to normal.
    - Most versions of Telcom can have their parameters adjusted directly from Basic,
      without needing to enter the Telcom program.
    - Most versions of Telcom have an optional clock. In older models firstly select VS
      from the MAME config menu, then enter NET CLOCK to enable it. NET TIME hhmm to set
      the time (24hour format). NET CLOCKD is supposed to remove the status line, but it
      doesn't, although the clock stops updating. NET CLOCK and NET CLOCKD are toggles.
    - Telcom 1.2 (used in mbeeic) has a bug. If you enter NET CLOCK, the status line is
      filled with inverse K. You can fix this from Basic by doing NET CLOCK 3 times.

    Notes about Disk System
    - Ports 44 to 47 are for standard connection to FD2793.
    - Port 48 is used for drive/side/density select on write, and intrq/drq on read.
      intrq and drq are OR'd together, then gated to bit 7 of the data bus whenever
      port 48 is activated on read. There are no interrupts used.
    - The 256TC can boot from the B drive if you hold down Shift and hit F1 at the menu.

    How to use the config switch for PIO B7:
    - Teleterm: Must use RTC. Anything else makes teleterm go crazy.
    - 256TC, 128, 128p: Doesn't seem to matter, leave at the default.
    - Standard: Has no effect, best left at "Tied High"
    - Other rom-based models: "VS" to make the Telcom clock work, or "Tied high".
    - 56k: not sure yet, leave as "Tied high" until more is known.

***************************************************************************

    TODO/not working:

    Old CRTC-based keyboard:
    - Paste drops many characters.
    - Typing can drop the occasional character.
    - mbee -bios 1 is unusable due to keyboard issues.

    FDC:   (TODO: see if these bugs still exist)
    - B drive doesn't work with most disks.
    - some disks cause MAME to freeze.

    - 128k: Simply Write has no keyboard.

    - 256tc: the Intro disk doesn't work

    - 256tc, Teleterm: Keyboard CPU inbuilt ROM needs to be dumped.
    - 128k, 64k: PALs need to be dumped for the bankswitching.

    - Mouse: a few programs support the use of a serial mouse which interfaced
             directly to the Z80PIO. However there's little info to be found.
             PIO B3 to ground activates the mouse pointer in Shell v3.01.

    - Hard drive (10MB) & controller

*******************************************************************************/

#include "emu.h"
#include "mbee.h"
#include "formats/mbee_cas.h"
#include "sound/sn76496.h"
#include "softlist_dev.h"
#include "speaker.h"

void mbee_state::mbee_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).ram();
	map(0x8000, 0xbfff).rom().region("maincpu",0);
	map(0xc000, 0xdfff).r(FUNC(mbee_state::pak_r));
	map(0xe000, 0xefff).r(FUNC(mbee_state::net_r));
	map(0xf000, 0xf7ff).rw(FUNC(mbee_state::video_low_r), FUNC(mbee_state::video_low_w));
	map(0xf800, 0xffff).rw(FUNC(mbee_state::video_high_r), FUNC(mbee_state::video_high_w));
}

void mbee_state::mbeeic_mem(address_map &map)
{
	map(0x0000, 0x7fff).ram();
	map(0x8000, 0xbfff).rom().region("maincpu",0);
	map(0xc000, 0xdfff).r(FUNC(mbee_state::pak_r));
	map(0xe000, 0xefff).r(FUNC(mbee_state::net_r));
	map(0xf000, 0xf7ff).rw(FUNC(mbee_state::video_low_r), FUNC(mbee_state::video_low_w));
	map(0xf800, 0xffff).rw(FUNC(mbee_state::video_high_r), FUNC(mbee_state::video_high_w));
}

void mbee_state::mbeeppc_mem(address_map &map)
{
	map(0x0000, 0x7fff).ram();
	map(0x8000, 0x9fff).bankr("basic");
	map(0xa000, 0xbfff).rom().region("maincpu",0);
	map(0xc000, 0xdfff).r(FUNC(mbee_state::pak_r));
	map(0xe000, 0xefff).r(FUNC(mbee_state::net_r));
	map(0xf000, 0xf7ff).rw(FUNC(mbee_state::video_low_r), FUNC(mbee_state::video_low_w));
	map(0xf800, 0xffff).rw(FUNC(mbee_state::video_high_r), FUNC(mbee_state::video_high_w));
}

void mbee_state::mbeett_mem(address_map &map)
{
	map(0x0000, 0x7fff).ram();
	map(0x8000, 0x9fff).rom().region("maincpu",0);
	map(0xa000, 0xbfff).ram();
	map(0xc000, 0xdfff).r(FUNC(mbee_state::pak_r));
	map(0xe000, 0xefff).r(FUNC(mbee_state::net_r));
	map(0xf000, 0xf7ff).rw(FUNC(mbee_state::video_low_r), FUNC(mbee_state::video_low_w));
	map(0xf800, 0xffff).rw(FUNC(mbee_state::video_high_r), FUNC(mbee_state::video_high_w));
}

void mbee_state::mbee56_mem(address_map &map)
{
	map(0x0000, 0xdfff).ram();
	map(0xe000, 0xefff).rom().region("maincpu",0);
	map(0xf000, 0xf7ff).rw(FUNC(mbee_state::video_low_r), FUNC(mbee_state::video_low_w));
	map(0xf800, 0xffff).rw(FUNC(mbee_state::video_high_r), FUNC(mbee_state::video_high_w));
}

void mbee_state::mbee256_mem(address_map &map)
{
	map(0x0000, 0x0fff).bankr("bankr0").bankw("bankw0");
	map(0x1000, 0x1fff).bankr("bankr1").bankw("bankw1");
	map(0x2000, 0x2fff).bankr("bankr2").bankw("bankw2");
	map(0x3000, 0x3fff).bankr("bankr3").bankw("bankw3");
	map(0x4000, 0x4fff).bankr("bankr4").bankw("bankw4");
	map(0x5000, 0x5fff).bankr("bankr5").bankw("bankw5");
	map(0x6000, 0x6fff).bankr("bankr6").bankw("bankw6");
	map(0x7000, 0x7fff).bankr("bankr7").bankw("bankw7");
	map(0x8000, 0x8fff).bankr("bankr8").bankw("bankw8");
	map(0x9000, 0x9fff).bankr("bankr9").bankw("bankw9");
	map(0xa000, 0xafff).bankr("bankr10").bankw("bankw10");
	map(0xb000, 0xbfff).bankr("bankr11").bankw("bankw11");
	map(0xc000, 0xcfff).bankr("bankr12").bankw("bankw12");
	map(0xd000, 0xdfff).bankr("bankr13").bankw("bankw13");
	map(0xe000, 0xefff).bankr("bankr14").bankw("bankw14");
	map(0xf000, 0xffff).bankr("bankr15").bankw("bankw15");
}

void mbee_state::mbee_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x03).mirror(0x10).rw(m_pio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x0b, 0x0b).mirror(0x10).w(FUNC(mbee_state::port0b_w));
	map(0x0c, 0x0c).mirror(0x10).r(m_crtc, FUNC(mc6845_device::status_r)).w(FUNC(mbee_state::m6545_index_w));
	map(0x0d, 0x0d).mirror(0x10).r(m_crtc, FUNC(mc6845_device::register_r)).w(FUNC(mbee_state::m6545_data_w));
}

void mbee_state::mbeeic_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0003).mirror(0xff10).rw(m_pio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x0008, 0x0008).mirror(0xff10).rw(FUNC(mbee_state::port08_r), FUNC(mbee_state::port08_w));
	map(0x0009, 0x0009).mirror(0xff00).nopw();
	map(0x000a, 0x000a).select(0xff10).rw(FUNC(mbee_state::telcom_r), FUNC(mbee_state::port0a_w));
	map(0x000b, 0x000b).mirror(0xff10).w(FUNC(mbee_state::port0b_w));
	map(0x000c, 0x000c).mirror(0xff10).r(m_crtc, FUNC(mc6845_device::status_r)).w(FUNC(mbee_state::m6545_index_w));
	map(0x000d, 0x000d).mirror(0xff10).r(m_crtc, FUNC(mc6845_device::register_r)).w(FUNC(mbee_state::m6545_data_w));
}

void mbee_state::mbeeppc_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0003).mirror(0xff10).rw(m_pio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x0008, 0x0008).mirror(0xff10).rw(FUNC(mbee_state::port08_r), FUNC(mbee_state::port08_w));
	map(0x0009, 0x0009).mirror(0xff00).nopw();
	map(0x000a, 0x000a).select(0xff10).rw(FUNC(mbee_state::telcom_r), FUNC(mbee_state::port0a_w));
	map(0x000b, 0x000b).mirror(0xff10).w(FUNC(mbee_state::port0b_w));
	map(0x000c, 0x000c).mirror(0xff00).r(m_crtc, FUNC(mc6845_device::status_r)).w(FUNC(mbee_state::m6545_index_w));
	map(0x000d, 0x000d).mirror(0xff10).r(m_crtc, FUNC(mc6845_device::register_r)).w(FUNC(mbee_state::m6545_data_w));
	map(0x001c, 0x001c).mirror(0xff00).rw(FUNC(mbee_state::port1c_r), FUNC(mbee_state::port1c_w));
}

void mbee_state::mbeett_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0003).mirror(0xff00).rw(m_pio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x0004, 0x0004).mirror(0xff00).w(m_rtc, FUNC(mc146818_device::address_w));
	map(0x0006, 0x0006).mirror(0xff00).w(m_rtc, FUNC(mc146818_device::data_w));
	map(0x0007, 0x0007).mirror(0xff00).r(m_rtc, FUNC(mc146818_device::data_r));
	map(0x0008, 0x0008).mirror(0xff00).rw(FUNC(mbee_state::port08_r), FUNC(mbee_state::port08_w));
	map(0x000a, 0x000a).select(0xff10).rw(FUNC(mbee_state::telcom_r), FUNC(mbee_state::port0a_w));
	map(0x000b, 0x000b).mirror(0xff00).w(FUNC(mbee_state::port0b_w));
	map(0x000c, 0x000c).mirror(0xff00).r(m_crtc, FUNC(mc6845_device::status_r)).w(FUNC(mbee_state::m6545_index_w));
	map(0x000d, 0x000d).mirror(0xff00).r(m_crtc, FUNC(mc6845_device::register_r)).w(FUNC(mbee_state::m6545_data_w));
	map(0x0018, 0x001b).mirror(0xff00).r(FUNC(mbee_state::port18_r));
	map(0x001c, 0x001f).mirror(0xff00).rw(FUNC(mbee_state::port1c_r), FUNC(mbee_state::port1c_w));
	map(0x0009, 0x0009).select(0xff00).r(FUNC(mbee_state::speed_r));
	map(0x0068, 0x006f).mirror(0xff00).noprw();    // swallow i/o to SCC which was never fitted to production machines
}

void mbee_state::mbee56_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x03).rw(m_pio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x08, 0x08).rw(FUNC(mbee_state::port08_r), FUNC(mbee_state::port08_w));
	map(0x09, 0x09).nopw();
	map(0x0b, 0x0b).w(FUNC(mbee_state::port0b_w));
	map(0x0c, 0x0c).r(m_crtc, FUNC(mc6845_device::status_r)).w(FUNC(mbee_state::m6545_index_w));
	map(0x0d, 0x0d).r(m_crtc, FUNC(mc6845_device::register_r)).w(FUNC(mbee_state::m6545_data_w));
	map(0x44, 0x47).rw(m_fdc, FUNC(wd2793_device::read), FUNC(wd2793_device::write));
	map(0x48, 0x4f).rw(FUNC(mbee_state::fdc_status_r), FUNC(mbee_state::fdc_motor_w));
}

void mbee_state::mbee128_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x03).rw(m_pio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x04, 0x04).w(m_rtc, FUNC(mc146818_device::address_w));
	map(0x06, 0x06).w(m_rtc, FUNC(mc146818_device::data_w));
	map(0x07, 0x07).r(m_rtc, FUNC(mc146818_device::data_r));
	map(0x08, 0x08).rw(FUNC(mbee_state::port08_r), FUNC(mbee_state::port08_w));
	map(0x09, 0x09).nopw();
	map(0x0b, 0x0b).w(FUNC(mbee_state::port0b_w));
	map(0x0c, 0x0c).r(m_crtc, FUNC(mc6845_device::status_r)).w(FUNC(mbee_state::m6545_index_w));
	map(0x0d, 0x0d).r(m_crtc, FUNC(mc6845_device::register_r)).w(FUNC(mbee_state::m6545_data_w));
	map(0x1c, 0x1f).rw(FUNC(mbee_state::port1c_r), FUNC(mbee_state::port1c_w));
	map(0x44, 0x47).rw(m_fdc, FUNC(wd2793_device::read), FUNC(wd2793_device::write));
	map(0x48, 0x4f).rw(FUNC(mbee_state::fdc_status_r), FUNC(mbee_state::fdc_motor_w));
	map(0x50, 0x57).w(FUNC(mbee_state::port50_w));
}

void mbee_state::mbee128p_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	mbee128_io(map);
	map(0x10, 0x13).w("sn1", FUNC(sn76489a_device::write));
}

void mbee_state::mbee256_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0003).mirror(0xff00).rw(m_pio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x0004, 0x0004).mirror(0xff00).w(m_rtc, FUNC(mc146818_device::address_w));
	map(0x0006, 0x0006).mirror(0xff00).w(m_rtc, FUNC(mc146818_device::data_w));
	map(0x0007, 0x0007).mirror(0xff00).r(m_rtc, FUNC(mc146818_device::data_r));
	map(0x0008, 0x0008).mirror(0xff00).rw(FUNC(mbee_state::port08_r), FUNC(mbee_state::port08_w));
	map(0x0009, 0x0009).select(0xff00).r(FUNC(mbee_state::speed_r));
	map(0x0009, 0x0009).mirror(0xff00).nopw();
	map(0x000b, 0x000b).mirror(0xff00).w(FUNC(mbee_state::port0b_w));
	map(0x000c, 0x000c).mirror(0xff00).r(m_crtc, FUNC(mc6845_device::status_r)).w(FUNC(mbee_state::m6545_index_w));
	map(0x000d, 0x000d).mirror(0xff00).r(m_crtc, FUNC(mc6845_device::register_r)).w(FUNC(mbee_state::m6545_data_w));
	map(0x0010, 0x0013).mirror(0xff00).w("sn1", FUNC(sn76489a_device::write));
	map(0x0018, 0x001b).mirror(0xff00).r(FUNC(mbee_state::port18_r));
	map(0x001c, 0x001f).mirror(0xff00).rw(FUNC(mbee_state::port1c_r), FUNC(mbee_state::port1c_w));
	map(0x0044, 0x0047).mirror(0xff00).rw(m_fdc, FUNC(wd2793_device::read), FUNC(wd2793_device::write));
	map(0x0048, 0x004f).mirror(0xff00).rw(FUNC(mbee_state::fdc_status_r), FUNC(mbee_state::fdc_motor_w));
	map(0x0050, 0x0057).mirror(0xff00).w(FUNC(mbee_state::port50_w));
	// map(0x0058, 0x005f).mirror(0xff00); External options: floppy drive, hard drive and keyboard
	// map(0x0060, 0x0067).mirror(0xff00); Reserved for file server selection (unused)
	// map(0x0068, 0x006f).mirror(0xff00); Reserved for 8530 SCC (never used)
}

static INPUT_PORTS_START( oldkb )
	PORT_START("X.0") /* IN0 KEY ROW 0 [000] */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("@") PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR(0x01)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR(0x02)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR(0x03)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR(0x04)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(0x05)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR(0x06)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR(0x07)

	PORT_START("X.1") /* IN1 KEY ROW 1 [080] */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') PORT_CHAR(0x08)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') PORT_CHAR(0x09)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') PORT_CHAR(0x0a)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR(0x0b)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR(0x0c)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') PORT_CHAR(0x0d)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(0x0e)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(0x0f)

	PORT_START("X.2") /* IN2 KEY ROW 2 [100] */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR(0x10)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') PORT_CHAR(0x11)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR(0x12)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR(0x13)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(0x14)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(0x15)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR(0x16)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR(0x17)

	PORT_START("X.3") /* IN3 KEY ROW 3 [180] */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(0x18)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR(0x19)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(0x1a)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{') PORT_CHAR(0x1b)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|') PORT_CHAR(0x1c)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("]") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}') PORT_CHAR(0x1d)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("^") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('^') PORT_CHAR('~') PORT_CHAR(0x1e)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Delete") PORT_CODE(KEYCODE_DEL) PORT_CHAR(8) PORT_CHAR(0x5f) PORT_CHAR(0x1f)  // port_char not working - hijacked

	PORT_START("X.4") /* IN4 KEY ROW 4 [200] */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("1 !") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("2 \"") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("3 #") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("4 $") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("5 %") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("6 &") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("7 '") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("X.5") /* IN5 KEY ROW 5 [280] */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("8 (") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("9 )") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(": *") PORT_CODE(KEYCODE_COLON) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("; +") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("- =") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("X.6") /* IN6 KEY ROW 6 [300] */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Escape") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Linefeed") PORT_CODE(KEYCODE_HOME) PORT_CHAR(10)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Break") PORT_CODE(KEYCODE_END) PORT_CHAR(3)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("X.7") /* IN7 KEY ROW 7 [380] */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("(Up)") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("(Down)") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("(Left)") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("(Right)") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
INPUT_PORTS_END

static INPUT_PORTS_START( mbee )

	PORT_INCLUDE( oldkb )

	// Autorun on quickload
	PORT_START("CONFIG")
	PORT_CONFNAME( 0x01, 0x01, "Autorun on Quickload")
	PORT_CONFSETTING(    0x00, DEF_STR(No))
	PORT_CONFSETTING(    0x01, DEF_STR(Yes))
	// monochrome monitor could be used
	PORT_CONFNAME( 0x30, 0x00, "Monitor type")
	PORT_CONFSETTING(    0x00, "Colour")
	PORT_CONFSETTING(    0x10, "Green")
	PORT_CONFSETTING(    0x20, "Amber")
	PORT_CONFSETTING(    0x30, "White")
	// Wire links on motherboard
	PORT_CONFNAME( 0xc0, 0x00, "PIO B7")
	PORT_CONFSETTING(    0x00, "VS") // sync pulse to enable telcom clock
	PORT_CONFSETTING(    0x40, "RTC") // optional board usually not fitted
	PORT_CONFSETTING(    0x80, "Tied high") // default resistor to vcc
	PORT_CONFSETTING(    0xc0, "Reserved for net")
INPUT_PORTS_END

static INPUT_PORTS_START( mbee128 )

	PORT_INCLUDE( oldkb )

	// Autorun on quickload
	PORT_START("CONFIG")
	PORT_CONFNAME( 0x01, 0x01, "Autorun on Quickload")
	PORT_CONFSETTING(    0x00, DEF_STR(No))
	PORT_CONFSETTING(    0x01, DEF_STR(Yes))
	// monochrome monitor could be used
	PORT_CONFNAME( 0x30, 0x00, "Monitor type")
	PORT_CONFSETTING(    0x00, "Colour")
	PORT_CONFSETTING(    0x10, "Green")
	PORT_CONFSETTING(    0x20, "Amber")
	PORT_CONFSETTING(    0x30, "White")
	// Wire links on motherboard
	PORT_CONFNAME( 0xc0, 0x80, "PIO B7")
	PORT_CONFSETTING(    0x00, "VS") // sync pulse to enable telcom clock
	PORT_CONFSETTING(    0x40, "RTC") // RTC IRQ for clock
	PORT_CONFSETTING(    0x80, "Tied high") // default resistor to vcc
	PORT_CONFSETTING(    0xc0, "Reserved for net")
INPUT_PORTS_END

static INPUT_PORTS_START( mbee256 )
	PORT_START("Y.0") /* IN0 KEY ROW 0 [+00] */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Escape") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Break") PORT_CODE(KEYCODE_END) PORT_CHAR(3)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("0 (num)") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DEL (num)") PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("Y.1") /* IN1 KEY ROW 1 [+08] */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("1 !") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') PORT_CHAR(0x11)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR(0x01)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Linefeed") PORT_CODE(KEYCODE_HOME) PORT_CHAR(10)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Insert") PORT_CODE(KEYCODE_INSERT)

	PORT_START("Y.2") /* IN2 KEY ROW 2 [+10] */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F3") PORT_CODE(KEYCODE_F3)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("2 @") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR(0x17)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR(0x13)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("+ (num)") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("2 (num)") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("3 (num)") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(0x1a)

	PORT_START("Y.3") /* IN3 KEY ROW 3 [+18] */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F4") PORT_CODE(KEYCODE_F4)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("3 #") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(0x05)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR(0x04)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("- (num)") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("5 (num)") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("6 (num)") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(0x18)

	PORT_START("Y.4") /* IN4 KEY ROW 4 [+20] */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F5") PORT_CODE(KEYCODE_F5)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("4 $") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR(0x12)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR(0x06)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("* (num)") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("8 (num)") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("9 (num)") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR(0x03)

	PORT_START("Y.5") /* IN5 KEY ROW 5 [+28] */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F6") PORT_CODE(KEYCODE_F6)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("5 %") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(0x14)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR(0x07)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("7 (num)") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("1 (num)") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("4 (num)") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR(0x16)

	PORT_START("Y.6") /* IN6 KEY ROW 6 [+30] */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F7") PORT_CODE(KEYCODE_F7)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("6 &") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR(0x19)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') PORT_CHAR(0x08)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("/ (num)") PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("(Down)") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("(Right)") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR(0x02)

	PORT_START("Y.7") /* IN7 KEY ROW 7 [+38] */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F8") PORT_CODE(KEYCODE_F8)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("7 &") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(0x15)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') PORT_CHAR(0x0a)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("(Left)") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(0x0e)

	PORT_START("Y.8") /* IN0 KEY ROW 0 [+40] */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F9") PORT_CODE(KEYCODE_F9)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("8 *") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') PORT_CHAR(0x09)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR(0x0b)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("(Up)") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') PORT_CHAR(0x0d)

	PORT_START("Y.9") /* IN1 KEY ROW 1 [+48] */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F10") PORT_CODE(KEYCODE_F10)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("9 (") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(0x0f)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR(0x0c)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("Y.10") /* IN2 KEY ROW 2 [+50] */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F11") PORT_CODE(KEYCODE_F11)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("0 )") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR(0x10)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("; :") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Delete") PORT_CODE(KEYCODE_DEL) PORT_CHAR(127)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("` ~") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('`') PORT_CHAR('~')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("\\ |") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|') PORT_CHAR(0x1c)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')

	PORT_START("Y.11") /* IN3 KEY ROW 3 [+58] */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F12") PORT_CODE(KEYCODE_F12)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("[ {") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{') PORT_CHAR(0x1b)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("\' \"") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('\"')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("= +") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("] }") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}') PORT_CHAR(0x1d)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("Y.12") /* IN4 KEY ROW 4 [+60] */
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("Y.13") /* IN5 KEY ROW 5 [+68] */
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)

	PORT_START("Y.14") /* IN6 KEY ROW 6 [+70] */
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Alt") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)

	// Autorun on quickload
	PORT_START("CONFIG")
	PORT_CONFNAME( 0x01, 0x01, "Autorun on Quickload")
	PORT_CONFSETTING(    0x00, DEF_STR(No))
	PORT_CONFSETTING(    0x01, DEF_STR(Yes))
	// Wire links on motherboard
	PORT_CONFNAME( 0xc0, 0x80, "PIO B7")
	PORT_CONFSETTING(    0x00, "VS") // sync pulse to enable telcom clock
	PORT_CONFSETTING(    0x40, "RTC") // RTC IRQ must be used on teleterm
	PORT_CONFSETTING(    0x80, "Tied high") // default resistor to vcc
	PORT_CONFSETTING(    0xc0, "Reserved for net")
INPUT_PORTS_END

static const z80_daisy_config mbee_daisy_chain[] =
{
	{ "z80pio" },
	{ nullptr }
};

/**************************** F4 CHARACTER DISPLAYER */
static const gfx_layout charlayout =
{
	8,16,                   /* 8 x 16 characters */
	RGN_FRAC(1,1),
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{  0*8,  1*8,  2*8,  3*8,  4*8,  5*8,  6*8,  7*8, 8*8,  9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_mono )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 96, 1 )
GFXDECODE_END

static GFXDECODE_START( gfx_standard )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 48 )
GFXDECODE_END

static GFXDECODE_START( gfx_premium )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 8 )
GFXDECODE_END

static void mbee_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
	device.option_add("525qd", FLOPPY_525_QD);
}


void mbee_state::remove_carts(machine_config &config)
{
	config.device_remove("cart_list");
	config.device_remove("optrom1");
	config.device_remove("optrom2");
	config.device_remove("optrom3");
	config.device_remove("optrom4");
	config.device_remove("optrom5");
	config.device_remove("optrom6");
	config.device_remove("optrom7");
}

void mbee_state::remove_quick(machine_config &config)
{
	config.device_remove("cass_list");
	config.device_remove("quickload");
	config.device_remove("quik_list");
}

void mbee_state::mbee(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 12_MHz_XTAL / 6);         /* 2 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &mbee_state::mbee_mem);
	m_maincpu->set_addrmap(AS_IO, &mbee_state::mbee_io);
	m_maincpu->set_daisy_config(mbee_daisy_chain);

	Z80PIO(config, m_pio, 12_MHz_XTAL / 6);
	m_pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio->out_pa_callback().set("cent_data_out", FUNC(output_latch_device::write));
	m_pio->out_ardy_callback().set(FUNC(mbee_state::pio_ardy));
	m_pio->in_pb_callback().set(FUNC(mbee_state::pio_port_b_r));
	m_pio->out_pb_callback().set(FUNC(mbee_state::pio_port_b_w));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(250)); /* not accurate */
	m_screen->set_size(64*8, 19*16);           /* need at least 17 lines for NET */
	m_screen->set_visarea(0*8, 64*8-1, 0, 19*16-1);
	m_screen->set_screen_update(FUNC(mbee_state::screen_update_mbee));

	GFXDECODE(config, "gfxdecode", m_palette, gfx_mono);

	PALETTE(config, m_palette, FUNC(mbee_state::standard_palette), 100);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	/* devices */
	SY6545_1(config, m_crtc, 12_MHz_XTAL / 8);
	m_crtc->set_screen(m_screen);
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(mbee_state::crtc_update_row));
	m_crtc->set_on_update_addr_change_callback(FUNC(mbee_state::crtc_update_addr));
	m_crtc->out_vsync_callback().set(FUNC(mbee_state::crtc_vs));

	quickload_image_device &quickload(QUICKLOAD(config, "quickload", "mwb,com,bee,bin", attotime::from_seconds(3)));
	quickload.set_load_callback(FUNC(mbee_state::quickload_cb));
	quickload.set_interface("mbee_quik");

	CENTRONICS(config, m_centronics, centronics_devices, nullptr);
	m_centronics->ack_handler().set(m_pio, FUNC(z80pio_device::strobe_a));

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(mbee_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("mbee_cass");

	generic_slot_device &optrom1(GENERIC_SOCKET(config, "optrom1", generic_plain_slot, "mbee_net", "mbn")); // net
	optrom1.set_device_load(FUNC(mbee_state::pak_load<1U>));
	optrom1.set_device_unload(FUNC(mbee_state::pak_unload<1U>));
	generic_slot_device &optrom2(GENERIC_SOCKET(config, "optrom2", generic_plain_slot, "mbee_pak", "mbp")); // edasm
	optrom2.set_device_load(FUNC(mbee_state::pak_load<2U>));
	optrom2.set_device_unload(FUNC(mbee_state::pak_unload<2U>));

	SOFTWARE_LIST(config, "cass_list").set_original("mbee_cass").set_filter("1");
	SOFTWARE_LIST(config, "quik_list").set_original("mbee_quik").set_filter("1");
	SOFTWARE_LIST(config, "cart_list").set_original("mbee_cart").set_filter("1");
}


void mbee_state::mbeeic(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 13.5_MHz_XTAL / 4);         /* 3.37500 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &mbee_state::mbeeic_mem);
	m_maincpu->set_addrmap(AS_IO, &mbee_state::mbeeic_io);
	m_maincpu->set_daisy_config(mbee_daisy_chain);

	Z80PIO(config, m_pio, 13.5_MHz_XTAL / 4);
	m_pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio->out_pa_callback().set("cent_data_out", FUNC(output_latch_device::write));
	m_pio->out_ardy_callback().set(FUNC(mbee_state::pio_ardy));
	m_pio->in_pb_callback().set(FUNC(mbee_state::pio_port_b_r));
	m_pio->out_pb_callback().set(FUNC(mbee_state::pio_port_b_w));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(250)); /* not accurate */
	m_screen->set_size(80*8, 310);
	m_screen->set_visarea(0, 80*8-1, 0, 19*16-1);
	m_screen->set_screen_update(FUNC(mbee_state::screen_update_mbee));

	GFXDECODE(config, "gfxdecode", m_palette, gfx_standard);

	PALETTE(config, m_palette, FUNC(mbee_state::standard_palette), 100);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	/* devices */
	SY6545_1(config, m_crtc, 13.5_MHz_XTAL / 8);
	m_crtc->set_screen(m_screen);
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(mbee_state::crtc_update_row));
	m_crtc->set_on_update_addr_change_callback(FUNC(mbee_state::crtc_update_addr));
	m_crtc->out_vsync_callback().set(FUNC(mbee_state::crtc_vs));

	quickload_image_device &quickload(QUICKLOAD(config, "quickload", "mwb,com,bee,bin", attotime::from_seconds(3)));
	quickload.set_load_callback(FUNC(mbee_state::quickload_cb));
	quickload.set_interface("mbee_quik");

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set(m_pio, FUNC(z80pio_device::strobe_a));

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(mbee_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("mbee_cass");

	generic_slot_device &optrom1(GENERIC_SOCKET(config, "optrom1", generic_plain_slot, "mbee_net", "mbn")); // net
	optrom1.set_device_load(FUNC(mbee_state::pak_load<1U>));
	optrom1.set_device_unload(FUNC(mbee_state::pak_unload<1U>));
	generic_slot_device &optrom2(GENERIC_SOCKET(config, "optrom2", generic_plain_slot, "mbee_pak", "mbp")); // pak0
	optrom2.set_device_load(FUNC(mbee_state::pak_load<2U>));
	optrom2.set_device_unload(FUNC(mbee_state::pak_unload<2U>));
	generic_slot_device &optrom3(GENERIC_SOCKET(config, "optrom3", generic_plain_slot, "mbee_pak", "mbp")); // pak1
	optrom3.set_device_load(FUNC(mbee_state::pak_load<3U>));
	optrom3.set_device_unload(FUNC(mbee_state::pak_unload<3U>));
	generic_slot_device &optrom4(GENERIC_SOCKET(config, "optrom4", generic_plain_slot, "mbee_pak", "mbp")); // pak2
	optrom4.set_device_load(FUNC(mbee_state::pak_load<4U>));
	optrom4.set_device_unload(FUNC(mbee_state::pak_unload<4U>));
	generic_slot_device &optrom5(GENERIC_SOCKET(config, "optrom5", generic_plain_slot, "mbee_pak", "mbp")); // pak3
	optrom5.set_device_load(FUNC(mbee_state::pak_load<5U>));
	optrom5.set_device_unload(FUNC(mbee_state::pak_unload<5U>));
	generic_slot_device &optrom6(GENERIC_SOCKET(config, "optrom6", generic_plain_slot, "mbee_pak", "mbp")); // pak4
	optrom6.set_device_load(FUNC(mbee_state::pak_load<6U>));
	optrom6.set_device_unload(FUNC(mbee_state::pak_unload<6U>));
	generic_slot_device &optrom7(GENERIC_SOCKET(config, "optrom7", generic_plain_slot, "mbee_pak", "mbp")); // pak5
	optrom7.set_device_load(FUNC(mbee_state::pak_load<7U>));
	optrom7.set_device_unload(FUNC(mbee_state::pak_unload<7U>));

	SOFTWARE_LIST(config, "cass_list").set_original("mbee_cass").set_filter("2");
	SOFTWARE_LIST(config, "quik_list").set_original("mbee_quik").set_filter("2");
	SOFTWARE_LIST(config, "cart_list").set_original("mbee_cart").set_filter("2");
}

void mbee_state::mbeepc85(machine_config &config)
{
	mbeeic(config);
	SOFTWARE_LIST(config.replace(), "cart_list").set_original("mbee_cart").set_filter("3");
}

void mbee_state::mbeeppc(machine_config &config)
{
	mbeepc85(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &mbee_state::mbeeppc_mem);
	m_maincpu->set_addrmap(AS_IO, &mbee_state::mbeeppc_io);
	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_premium);
	m_palette->set_init(FUNC(mbee_state::premium_palette));

	MC146818(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->irq().set(FUNC(mbee_state::rtc_irq_w));
}

void mbee_state::mbee56(machine_config &config)
{
	mbeeic(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &mbee_state::mbee56_mem);
	m_maincpu->set_addrmap(AS_IO, &mbee_state::mbee56_io);

	WD2793(config, m_fdc, 4_MHz_XTAL / 2);
	m_fdc->intrq_wr_callback().set(FUNC(mbee_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(mbee_state::fdc_drq_w));
	m_fdc->enmf_rd_callback().set_constant(0);
	FLOPPY_CONNECTOR(config, m_floppy0, mbee_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy1, mbee_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	SOFTWARE_LIST(config, "flop_list").set_original("mbee_flop").set_filter("1");
	remove_carts(config);
	remove_quick(config);
}

void mbee_state::mbee128(machine_config &config)
{
	mbee56(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &mbee_state::mbee256_mem);
	m_maincpu->set_addrmap(AS_IO, &mbee_state::mbee128_io);

	MC146818(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->irq().set(FUNC(mbee_state::rtc_irq_w));

	SOFTWARE_LIST(config.replace(), "flop_list").set_original("mbee_flop").set_filter("2");
}

void mbee_state::mbee128p(machine_config &config)
{
	mbeeppc(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &mbee_state::mbee256_mem);
	m_maincpu->set_addrmap(AS_IO, &mbee_state::mbee128p_io);

	WD2793(config, m_fdc, 4_MHz_XTAL / 2);
	m_fdc->intrq_wr_callback().set(FUNC(mbee_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(mbee_state::fdc_drq_w));
	m_fdc->enmf_rd_callback().set_constant(0);
	FLOPPY_CONNECTOR(config, m_floppy0, mbee_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy1, mbee_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	SN76489A(config, "sn1", 13.5_MHz_XTAL / 4).add_route(ALL_OUTPUTS, "mono", 0.50);

	SOFTWARE_LIST(config, "flop_list").set_original("mbee_flop").set_filter("3");
	remove_carts(config);
	remove_quick(config);
}

void mbee_state::mbeepp(machine_config &config)
{
	mbee128p(config);
	SOFTWARE_LIST(config.replace(), "flop_list").set_original("mbee_flop").set_filter("2,3");
}

void mbee_state::mbee256(machine_config &config)
{
	mbee128p(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &mbee_state::mbee256_mem);
	m_maincpu->set_addrmap(AS_IO, &mbee_state::mbee256_io);

	TIMER(config, "newkb_timer").configure_periodic(FUNC(mbee_state::newkb_timer), attotime::from_hz(50));

	SOFTWARE_LIST(config.replace(), "flop_list").set_original("mbee_flop").set_filter("4");
}

void mbee_state::mbeett(machine_config &config)
{
	mbeeppc(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &mbee_state::mbeett_mem);
	m_maincpu->set_addrmap(AS_IO, &mbee_state::mbeett_io);
	TIMER(config, "newkb_timer").configure_periodic(FUNC(mbee_state::newkb_timer), attotime::from_hz(50));
	remove_quick(config);
	config.device_remove("optrom3");
	config.device_remove("optrom4");
	config.device_remove("optrom5");
	config.device_remove("optrom6");
	config.device_remove("optrom7");
	SOFTWARE_LIST(config.replace(), "cart_list").set_original("mbee_cart").set_filter("TT");
}

// This represents the Series 1: Kit computer, 16K, 32K, 16K Plus, and 32K plus.
ROM_START( mbee )
	ROM_REGION( 0x6000, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "510", "Basic 5.10" )
	ROMX_LOAD("bas510a.ic25",  0x0000, 0x1000, CRC(2ca47c36) SHA1(f36fd0afb3f1df26edc67919e78000b762b6cbcb), ROM_BIOS(0) )
	ROMX_LOAD("bas510b.ic27",  0x1000, 0x1000, CRC(a07a0c51) SHA1(dcbdd9df78b4b6b2972de2e4050dabb8ae9c3f5a), ROM_BIOS(0) )
	ROMX_LOAD("bas510c.ic28",  0x2000, 0x1000, CRC(906ac00f) SHA1(9b46458e5755e2c16cdb191a6a70df6de9fe0271), ROM_BIOS(0) )
	ROMX_LOAD("bas510d.ic30",  0x3000, 0x1000, CRC(61727323) SHA1(c0fea9fd0e25beb9faa7424db8efd07cf8d26c1b), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "500", "Basic 5.00" )
	ROMX_LOAD("bas500a.ic25",  0x0000, 0x1000, CRC(5fabd317) SHA1(a91bb6a802da17bc02e720e68b8bdd80261b5039), ROM_BIOS(1) )
	ROMX_LOAD("bas500b.ic27",  0x1000, 0x1000, CRC(0aaf5d04) SHA1(f871a2991d4a487596ec637e237fc0d9656f4a64), ROM_BIOS(1) )
	ROMX_LOAD("bas500c.ic28",  0x2000, 0x1000, CRC(ab7d79b8) SHA1(75f71b63dcd81946afce7e2c4d83c6f3ba5c2bc3), ROM_BIOS(1) )
	ROMX_LOAD("bas500d.ic30",  0x3000, 0x1000, CRC(19f83c4e) SHA1(3ca985bb6b31d39e9cd42ea0a4baeb266c1b5e86), ROM_BIOS(1) )

	// first 0x800 for normal chars, 2nd 0x800 for small chars. Some roms don't have small chars so normal ones loaded twice.
	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD("charrom.ic13",   0x0000, 0x0800, CRC(b149737b) SHA1(a3cd4f5d0d3c71137cd1f0f650db83333a2e3597) )
	ROM_RELOAD( 0x0800, 0x0800 )

	ROM_REGION( 0x0020, "proms", 0 )
	ROM_LOAD_OPTIONAL( "82s123.ic16", 0x0000, 0x0020, CRC(79fa1e9d) SHA1(0454051697b23e4561744466fb31e7a133d02246) ) // video switching prom, not needed for emulation purposes
ROM_END

// This represents the Series 1: IC (Integrated Computer), and the Series 2: Experimenter, the Educator, and the Personal Communicator
ROM_START( mbeeic )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "522e", "Basic 5.22e" )
	ROMX_LOAD("bas522e.ic5",           0x0000,  0x2000, CRC(7896a696) SHA1(a158f7803296766160e1f258dfc46134735a9477), ROM_BIOS(0) )
	ROMX_LOAD("bas522e.ic10",          0x2000,  0x2000, CRC(b21d9679) SHA1(332844433763331e9483409cd7da3f90ac58259d), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "522d", "Basic 5.22d" )
	ROMX_LOAD("bas522d.ic5",           0x0000,  0x2000, CRC(7896a696) SHA1(a158f7803296766160e1f258dfc46134735a9477), ROM_BIOS(1) )
	ROMX_LOAD("bas522d.ic10",          0x2000,  0x2000, CRC(523a38ff) SHA1(a5383067bc712123849710d8b69cbd879d17a61f), ROM_BIOS(1) )

	ROM_REGION( 0x1000, "netdef", ROMREGION_ERASEFF )
	ROM_LOAD_OPTIONAL( "telcom10.mbn", 0x0000,  0x1000, CRC(d1617e4f) SHA1(c73dc4dcf4c69419842fa4b52aa92e86924a2e2b) ) // net

	/* PAK option roms */
	ROM_REGION( 0x20000, "pakdef", ROMREGION_ERASEFF )
	ROM_LOAD_OPTIONAL("wbee12.mbp",   0x0000,  0x2000, CRC(0fc21cb5) SHA1(33b3995988fc51ddef1568e160dfe699867adbd5) ) // pak0
	ROM_LOAD_OPTIONAL("help1.mbp",    0x2000,  0x2000, CRC(d34fae54) SHA1(5ed30636f48e9d208ce2da367ba4425782a5bce3) ) // pak1

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD("charrom.bin",           0x0000,  0x1000, CRC(1f9fcee4) SHA1(e57ac94e03638075dde68a0a8c834a4f84ba47b0) )

	ROM_REGION( 0x0040, "proms", 0 )
	ROM_LOAD( "82s123.ic7",           0x0000,  0x0020, CRC(61b9c16c) SHA1(0ee72377831c21339360c376f7248861d476dc20) )
	ROM_LOAD_OPTIONAL( "82s123.ic16", 0x0020,  0x0020, CRC(79fa1e9d) SHA1(0454051697b23e4561744466fb31e7a133d02246) ) // video switching prom, not needed for emulation purposes
ROM_END

// This represents the Series 3: 16K Educator, and the 32K Communicator
ROM_START( mbeepc )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "522e", "Basic 5.22e" )
	ROMX_LOAD("bas522e.ic5",           0x0000,  0x2000, CRC(7896a696) SHA1(a158f7803296766160e1f258dfc46134735a9477), ROM_BIOS(0) )
	ROMX_LOAD("bas522e.ic10",          0x2000,  0x2000, CRC(b21d9679) SHA1(332844433763331e9483409cd7da3f90ac58259d), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "524e", "Basic 5.24e" )
	ROMX_LOAD("bas524e.ic5",           0x0000,  0x2000, CRC(ec9c7a60) SHA1(a4021bcedc8da8c0eb0bda036a1d457619a175b0), ROM_BIOS(1) )
	ROMX_LOAD("bas524e.ic10",          0x2000,  0x2000, CRC(9621cfc8) SHA1(81ab332d366466ae84cff2e8b8596dd86c6b6f63), ROM_BIOS(1) )

	ROM_REGION( 0x1000, "netdef", ROMREGION_ERASEFF )
	ROM_LOAD_OPTIONAL( "telcom10.mbn", 0x0000, 0x1000, CRC(d1617e4f) SHA1(c73dc4dcf4c69419842fa4b52aa92e86924a2e2b) ) // net

	/* PAK option roms */
	ROM_REGION( 0x20000, "pakdef", ROMREGION_ERASEFF )
	ROM_LOAD_OPTIONAL("wbee12.mbp",   0x0000,  0x2000, CRC(0fc21cb5) SHA1(33b3995988fc51ddef1568e160dfe699867adbd5) ) // pak0
	ROM_LOAD_OPTIONAL("help1.mbp",    0x2000,  0x2000, CRC(d34fae54) SHA1(5ed30636f48e9d208ce2da367ba4425782a5bce3) ) // pak1

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD("charrom.bin",           0x0000,  0x1000, CRC(1f9fcee4) SHA1(e57ac94e03638075dde68a0a8c834a4f84ba47b0) )

	ROM_REGION( 0x0040, "proms", 0 )
	ROM_LOAD( "82s123.ic7",           0x0000,  0x0020, CRC(61b9c16c) SHA1(0ee72377831c21339360c376f7248861d476dc20) )
	ROM_LOAD_OPTIONAL( "82s123.ic16", 0x0020,  0x0020, CRC(79fa1e9d) SHA1(0454051697b23e4561744466fb31e7a133d02246) ) // video switching prom, not needed for emulation purposes
ROM_END

ROM_START( mbeepc85 )  // The PC85 with the earlier menu
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("bas525a.rom",           0x0000,  0x2000, CRC(a6e02afe) SHA1(0495308c7e1d84b5989a3af6d3b881f4580b2641) )
	ROM_LOAD("bas525b.rom",           0x2000,  0x2000, CRC(245dd36b) SHA1(dd288f3e6737627f50d3d2a49df3e57c423d3118) )

	ROM_REGION( 0x2000, "netdef", ROMREGION_ERASEFF )
	ROM_LOAD_OPTIONAL("telcom321a.mbn", 0x0000,  0x2000, CRC(36852a11) SHA1(c45b8d03629e86231c6b256a7435abd87d8872a4) )

	// PAK option roms - Wordbee must be in slot 0 and Shell must be in slot 5.
	ROM_REGION( 0x20000, "pakdef", ROMREGION_ERASEFF )
	ROM_LOAD("wbee13r3.mbp",          0x0000,  0x2000, CRC(d7c58b7b) SHA1(5af1b8d21a0f21534ed1833ae919dbbc6ca973e2) ) // 0
	ROM_LOAD("help2.mbp",             0x2000,  0x2000, CRC(a4f1fa90) SHA1(1456abc6ed0501a3b15a99b4302750843293ae5f) ) // 1
	ROM_LOAD("shell.mbp",             0xa000,  0x2000, CRC(5a2c7cd6) SHA1(8edc086710cb558f2146d660eddc8a18ba6a141c) ) // 5

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD("charrom.bin",           0x0000,  0x1000, CRC(1f9fcee4) SHA1(e57ac94e03638075dde68a0a8c834a4f84ba47b0) )

	ROM_REGION( 0x0040, "proms", 0 )
	ROM_LOAD( "82s123.ic7",           0x0000,  0x0020, CRC(61b9c16c) SHA1(0ee72377831c21339360c376f7248861d476dc20) )
	ROM_LOAD_OPTIONAL( "82s123.ic16", 0x0020,  0x0020, CRC(79fa1e9d) SHA1(0454051697b23e4561744466fb31e7a133d02246) ) // video switching prom, not needed for emulation purposes
ROM_END

ROM_START( mbeepc85b ) // The PC85 with the later menu
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("bas525a.rom",           0x0000,  0x2000, CRC(a6e02afe) SHA1(0495308c7e1d84b5989a3af6d3b881f4580b2641) )
	ROM_LOAD("bas525b.rom",           0x2000,  0x2000, CRC(245dd36b) SHA1(dd288f3e6737627f50d3d2a49df3e57c423d3118) )

	ROM_REGION( 0x2000, "netdef", ROMREGION_ERASEFF )
	ROM_LOAD_OPTIONAL("telcom321a.mbn", 0x0000,  0x2000, CRC(36852a11) SHA1(c45b8d03629e86231c6b256a7435abd87d8872a4) )

	// PAK option roms - Wordbee must be in slot 0 and Shell must be in slot 5.
	ROM_REGION( 0x20000, "pakdef", ROMREGION_ERASEFF )
	ROM_LOAD("wbee13r3.mbp",          0x0000,  0x2000, CRC(d7c58b7b) SHA1(5af1b8d21a0f21534ed1833ae919dbbc6ca973e2) ) // 0
	ROM_LOAD("help2.mbp",             0x2000,  0x2000, CRC(a4f1fa90) SHA1(1456abc6ed0501a3b15a99b4302750843293ae5f) ) // 1
	ROM_LOAD("busy.mbp",              0x4000,  0x2000, CRC(56255f60) SHA1(fd2e37209fd49290be6875bc460cfc05392938ba) ) // 2
	ROM_CONTINUE( 0x14000, 0x2000 )
	ROM_LOAD("graphics.mbp",          0x6000,  0x2000, CRC(9e9d327c) SHA1(aebf60ed153004380b9f271f2212376910a6cef9) ) // 3
	ROM_CONTINUE( 0x16000, 0x2000 )
	ROM_LOAD("viatel23.mbp",          0x8000,  0x2000, CRC(2da2411f) SHA1(d3cfa978165feef0a96e28197f6a762aa6604799) ) // 4
	ROM_LOAD("shell-b.mbp",           0xa000,  0x2000, CRC(17bf2d58) SHA1(ae22a5fc5783f37066ba5555497e40945272ca3d) ) // 5

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD("charrom.bin",           0x0000,  0x1000, CRC(1f9fcee4) SHA1(e57ac94e03638075dde68a0a8c834a4f84ba47b0) )

	ROM_REGION( 0x0040, "proms", 0 )
	ROM_LOAD( "82s123.ic7",           0x0000,  0x0020, CRC(61b9c16c) SHA1(0ee72377831c21339360c376f7248861d476dc20) )
	ROM_LOAD_OPTIONAL( "82s123.ic16", 0x0020,  0x0020, CRC(79fa1e9d) SHA1(0454051697b23e4561744466fb31e7a133d02246) ) // video switching prom, not needed for emulation purposes
ROM_END

ROM_START( mbeepc85s ) // The Swedish version of the PC85, with custom localised software paks
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("bas524a.rom",           0x0000,  0x2000, CRC(ec9c7a60) SHA1(a4021bcedc8da8c0eb0bda036a1d457619a175b0) )
	ROM_LOAD("bas524b.rom",           0x2000,  0x2000, CRC(17d3eac7) SHA1(d40d376cc5e751d257d951909a34445e70506c7b) )

	ROM_REGION( 0x2000, "netdef", ROMREGION_ERASEFF )
	ROM_LOAD_OPTIONAL("telcom321s.mbp", 0x0000, 0x2000, CRC(00f8fde1) SHA1(eb881bbab90c85fd6e29540decd25e884c67f738) )

	// PAK roms - These are not optional and will only work in the correct slots.
	ROM_REGION( 0x20000, "pakdef", ROMREGION_ERASEFF )
	ROM_LOAD("wbee20s.mbp",           0x0000,  0x2000, CRC(6a0fe57f) SHA1(a101b588b1872e19382b9e9ea50fabb0fd060aa6) ) // 0
	ROM_LOAD("db-s.mbp",              0x2000,  0x2000, CRC(e2094771) SHA1(62d7fb66c91d2bd24523bc84e4f005cf2c4480bb) ) // 1
	ROM_LOAD("kalk-s.mbp",            0x4000,  0x2000, CRC(08dd71ee) SHA1(c9d506d8bb56f602c3481b253d4cac226f545d98) ) // 2
	ROM_LOAD("bg-s.mbp",              0x6000,  0x2000, CRC(5aa4813e) SHA1(a8638e9046bfb9d5a98c878322295ce408bd879d) ) // 3
	ROM_LOAD("vtex11s.mbp",           0x8000,  0x2000, CRC(67592b3f) SHA1(7f1d23ded34781ccda5f36b4a4fa118a8c0e44ec) ) // 4
	ROM_LOAD("shell-s.mbp",           0xa000,  0x2000, CRC(bdf1768f) SHA1(4385351d07288cf94947ac63131eeed98572caa1) ) // 5

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD("charrom-s.bin",         0x0000,  0x1000, CRC(1bcbf083) SHA1(6438649b8b5fc20dd772ec7195e69a5bbe016b09) )

	ROM_REGION( 0x0040, "proms", 0 )
	ROM_LOAD( "82s123.ic7",           0x0000,  0x0020, CRC(61b9c16c) SHA1(0ee72377831c21339360c376f7248861d476dc20) )
	ROM_LOAD_OPTIONAL( "82s123.ic16", 0x0020,  0x0020, CRC(79fa1e9d) SHA1(0454051697b23e4561744466fb31e7a133d02246) ) // video switching prom, not needed for emulation purposes
ROM_END

ROM_START( mbeett ) // The Teleterm
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD("kernel_106.rom",        0x0000,  0x2000, CRC(5ab9cb1d) SHA1(a1fb971622f85c4d866b91cb4bec6d75757e8c5f) )

	ROM_REGION( 0x2000, "netdef", ROMREGION_ERASEFF )
	ROM_LOAD("wm_106.mbn",            0x0000,  0x2000, CRC(77e0b355) SHA1(1db6769cd6b12e1c335c83f17f8c139986c87758) )

	ROM_REGION( 0x20000, "pakdef", ROMREGION_ERASEFF )
	ROM_LOAD("tv_470311.mbp",         0x2000,  0x2000, CRC(2c4c2dcb) SHA1(77cd75166a389cb2d1d8abf00b1ddd077ce98354) ) // 1
	ROM_CONTINUE( 0x12000, 0x2000 )
	ROM_LOAD("tw_103.mbp",            0x4000,  0x2000, CRC(881edb4b) SHA1(f6e30a12b1537bd55b69d1319799b150e80a471b) ) // 2
	ROM_CONTINUE( 0x14000, 0x2000 )
	ROM_LOAD("oside_107.mbp",         0x6000,  0x2000, CRC(05d99aba) SHA1(4f88d63025f99bcc54d6f2abc20a699c97384f68) ) // 3
	ROM_CONTINUE( 0x16000, 0x1000 )
	ROM_LOAD("test_105.mbp",          0x8000,  0x2000, CRC(b69aa618) SHA1(49de8cbad59f549c7ad9f8efc9beee0cfcd901fe) ) // 4

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD("charrom.bin",           0x0000,  0x1000, CRC(1f9fcee4) SHA1(e57ac94e03638075dde68a0a8c834a4f84ba47b0) )
ROM_END

ROM_START( mbeeppc ) // The Premium PC85
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD("bas529b.rom",           0x0000,  0x2000, CRC(a1bd986b) SHA1(5d79f210c9042db5aefc85a0bdf45210cb9e9899) )

	ROM_REGION( 0x4000, "basicrom", 0 )
	ROM_LOAD("bas529a.rom",           0x0000,  0x4000, CRC(fe8242e1) SHA1(ff790edf4fcc7a134d451dbad7779157b07f6abf) )

	ROM_REGION( 0x2000, "netdef", ROMREGION_ERASEFF )
	ROM_LOAD_OPTIONAL("telco321.rom", 0x0000,  0x2000, CRC(36852a11) SHA1(c45b8d03629e86231c6b256a7435abd87d8872a4) )

	/* PAK option roms - Wordbee must be in slot 0 and Shell must be in slot 5. */
	ROM_REGION( 0x20000, "pakdef", ROMREGION_ERASEFF )
	ROM_LOAD("wbee13r3.mbp",          0x0000,  0x2000, CRC(d7c58b7b) SHA1(5af1b8d21a0f21534ed1833ae919dbbc6ca973e2) ) // 0
	ROM_LOAD("help2.mbp",             0x2000,  0x2000, CRC(a4f1fa90) SHA1(1456abc6ed0501a3b15a99b4302750843293ae5f) ) // 1
	ROM_LOAD("busy-p.mbp",            0x4000,  0x2000, CRC(f2897427) SHA1(b4c351bdac72d89589980be6d654f9b931bcba6b) ) // 2
	ROM_CONTINUE( 0x14000, 0x2000 )
	ROM_LOAD("graphics.mbp",          0x6000,  0x2000, CRC(9e9d327c) SHA1(aebf60ed153004380b9f271f2212376910a6cef9) ) // 3
	ROM_CONTINUE( 0x16000, 0x2000 )
	ROM_LOAD("vtex235.mbp",           0x8000,  0x2000, CRC(8c30ecb2) SHA1(cf068462d7def885bdb5d3a265851b88c727c0d7) ) // 4
	ROM_LOAD("ppcshell.mbp",          0xa000,  0x2000, CRC(1e793555) SHA1(ddeaa081ec4408e80e3fb192865d87daa035c701) ) // 5

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD("charrom.bin",           0x0000,  0x1000, CRC(1f9fcee4) SHA1(e57ac94e03638075dde68a0a8c834a4f84ba47b0) )
ROM_END

// This represents the Series 1: 64K, and 64K Plus
ROM_START( mbee56 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("56kb.rom",              0x0000,  0x1000, CRC(28211224) SHA1(b6056339402a6b2677b0e6c57bd9b78a62d20e4f) )

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD("charrom.bin",           0x0000,  0x1000, CRC(1f9fcee4) SHA1(e57ac94e03638075dde68a0a8c834a4f84ba47b0) )

	ROM_REGION( 0x0040, "proms", 0 )
	ROM_LOAD( "82s123.ic7",           0x0000,  0x0020, CRC(61b9c16c) SHA1(0ee72377831c21339360c376f7248861d476dc20) )
	ROM_LOAD_OPTIONAL( "82s123.ic16", 0x0020,  0x0020, CRC(79fa1e9d) SHA1(0454051697b23e4561744466fb31e7a133d02246) ) // video switching prom, not needed for emulation purposes
ROM_END

// This represents the Series 3: 64K CIAB (Computer-in-a-Book), and the standard 128K Small Business Computer
ROM_START( mbee128 )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD("bn54.bin",              0x0000,  0x2000, CRC(995c53db) SHA1(46e1a5cfd5795b8cf528bacf9dc79398ff7d64af) )
	//ROM_SYSTEM_BIOS( 1, "bn55", "bn55" )
	//ROMX_LOAD("bn55.rom",           0x0000, 0x2000, CRC(ca2c1073) SHA1(355d90d181de899cc7af892df96305fead9c81b4), ROM_BIOS(1) )  // not working - meant for standard CIAB

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD("charrom.bin",           0x0000,  0x1000, CRC(1f9fcee4) SHA1(e57ac94e03638075dde68a0a8c834a4f84ba47b0) )

	ROM_REGION( 0x4000, "pals", 0 ) // undumped; using prom from 256tc for now
	ROM_LOAD( "silver.u39", 0x0000, 0x4000, BAD_DUMP CRC(c34aab64) SHA1(781fe648488dec90185760f8e081e488b73b68bf) )

	ROM_REGION( 0x0040, "proms", 0 )
	ROM_LOAD( "82s123.ic7",           0x0000,  0x0020, CRC(61b9c16c) SHA1(0ee72377831c21339360c376f7248861d476dc20) )
	ROM_LOAD_OPTIONAL( "82s123.ic16", 0x0020,  0x0020, CRC(79fa1e9d) SHA1(0454051697b23e4561744466fb31e7a133d02246) ) // video switching prom, not needed for emulation purposes
ROM_END

// This represents the 64K Premium CIAB, the 128K Premium Small Business Computer, and the 128K Overdrive
ROM_START( mbee128p )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "bn60", "Version 2.03" )
	ROMX_LOAD("bn60.rom",           0x0000, 0x2000, CRC(ed15d4ee) SHA1(3ea42b63d42b9a4c5402676dee8912ad1f906bda), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "bn59", "Version 2.02" )
	ROMX_LOAD("bn59.rom",           0x0000, 0x2000, CRC(97384116) SHA1(87f2c4ab1a1f2964ba4f2bb60e62dc9c163831ba), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "bn58", "Version 2.01" )
	ROMX_LOAD("bn58.rom",           0x0000, 0x2000, CRC(2f3757a6) SHA1(37158da0e8609fea50382f6b941fe473eaaf20cf), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "bn56", "bn56" )
	ROMX_LOAD("bn56.rom",           0x0000, 0x2000, CRC(3f76769d) SHA1(cfae2069d739c26fe39f734d9f705a3c965d1e6f), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "bn54", "bn54" )
	ROMX_LOAD("bn54.rom",           0x0000, 0x2000, CRC(995c53db) SHA1(46e1a5cfd5795b8cf528bacf9dc79398ff7d64af), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "bn54s", "bn54_Swedish" )
	ROMX_LOAD("bn54_swedish.rom",   0x0000, 0x2000, CRC(694179a6) SHA1(1d465330845cd7878c236a0c84a85b5512ccfd65), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6, "bn56s", "bn56_Swedish" )
	ROMX_LOAD("bn56_swedish.rom",   0x0000, 0x2000, CRC(dad4a515) SHA1(9c1e0faaccd8d2062bb3b99c8600b515ed460479), ROM_BIOS(6) )
	ROM_SYSTEM_BIOS( 7, "hd18", "Hard Disk System" )
	ROMX_LOAD("hd18.rom",           0x0000, 0x2000, CRC(ed53ace7) SHA1(534e2e00cc527197c76b3c106b3c9ff7f1328487), ROM_BIOS(7) )

	ROM_REGION( 0x4000, "pals", 0 ) // undumped; using prom from 256tc for now
	ROM_LOAD( "silver.u39", 0x0000, 0x4000, BAD_DUMP CRC(c34aab64) SHA1(781fe648488dec90185760f8e081e488b73b68bf) )

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD("charrom.bin",           0x0000,  0x1000, CRC(1f9fcee4) SHA1(e57ac94e03638075dde68a0a8c834a4f84ba47b0) )
ROM_END

ROM_START( mbee256 ) // The 256K Telecomputer
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "1.20", "Version 1.20" )
	ROMX_LOAD("256tc_boot_1.20.u38", 0x0000, 0x4000, CRC(fe8d6a84) SHA1(a037a1b90b18a2180e9f5f216b829fcd480449a4), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "1.15", "Version 1.15" )
	ROMX_LOAD("256tc_boot_1.15.u38", 0x0000, 0x4000, CRC(1902062d) SHA1(e4a1c0b3f4996e313da0bac0edb6d34e3270723e), ROM_BIOS(1) )

	ROM_REGION( 0x4000, "pals", 0 )
	ROM_LOAD( "silver.u39", 0x0000, 0x4000, CRC(c34aab64) SHA1(781fe648488dec90185760f8e081e488b73b68bf) )

	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD("char256.u53", 0x0000, 0x2000, CRC(9372af3c) SHA1(a63591822c0504de2fed52e88d64e1dbd6124b74) )
ROM_END

// Premium Plus - Note: The bios rom is the only one confirmed to be in the machine. IC position numbers are unknown.
// No technical information has been released.
ROM_START( mbeepp ) // Premium Plus
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "pp.bin", 0x0000, 0x4000, CRC(33292300) SHA1(8ba32123ef1b3beffa797855a1de0ea2078d652a) ) // ver 1.0

	ROM_REGION( 0x4000, "pals", 0 )
	ROM_LOAD( "silver.u39", 0x0000, 0x4000, BAD_DUMP CRC(c34aab64) SHA1(781fe648488dec90185760f8e081e488b73b68bf) )

	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD("char256.u53", 0x0000, 0x2000, BAD_DUMP CRC(9372af3c) SHA1(a63591822c0504de2fed52e88d64e1dbd6124b74) )
ROM_END

/***************************************************************************

  Game driver(s)

***************************************************************************/

//    YEAR  NAME       PARENT  COMPAT  MACHINE   INPUT    CLASS       INIT           COMPANY               FULLNAME
COMP( 1982, mbee,      0,      0,      mbee,     mbee,    mbee_state, init_mbee,     "Applied Technology", "Microbee 16k Standard",       MACHINE_SUPPORTS_SAVE )
COMP( 1983, mbeeic,    mbee,   0,      mbeeic,   mbee,    mbee_state, init_mbeeic,   "Applied Technology", "Microbee 32k IC",             MACHINE_SUPPORTS_SAVE )
COMP( 1983, mbee56,    mbee,   0,      mbee56,   mbee,    mbee_state, init_mbee56,   "Applied Technology", "Microbee 64k",                MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1984, mbeepc,    mbee,   0,      mbeeic,   mbee,    mbee_state, init_mbeeic,   "Applied Technology", "Microbee 32k Communicator",   MACHINE_SUPPORTS_SAVE )
COMP( 1984, mbee128,   mbee,   0,      mbee128,  mbee128, mbee_state, init_mbee128,  "Applied Technology", "Microbee 128k Standard",      MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1985, mbeepc85,  mbee,   0,      mbeepc85, mbee,    mbee_state, init_mbeeic,   "Applied Technology", "Microbee PC85",               MACHINE_SUPPORTS_SAVE )
COMP( 1985, mbeepc85s, mbee,   0,      mbeepc85, mbee,    mbee_state, init_mbeeic,   "Applied Technology", "Microbee PC85 (Swedish)",     MACHINE_SUPPORTS_SAVE )
COMP( 1985, mbeepc85b, mbee,   0,      mbeepc85, mbee,    mbee_state, init_mbeeic,   "Microbee Systems",   "Microbee PC85 (New version)", MACHINE_SUPPORTS_SAVE )
COMP( 1986, mbeeppc,   mbee,   0,      mbeeppc,  mbee,    mbee_state, init_mbeeppc,  "Microbee Systems",   "Microbee Premium PC85",       MACHINE_SUPPORTS_SAVE )
COMP( 1986, mbeett,    mbee,   0,      mbeett,   mbee256, mbee_state, init_mbeett,   "Microbee Systems",   "Microbee Teleterm",           MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1986, mbee128p,  mbee,   0,      mbee128p, mbee128, mbee_state, init_mbee128p, "Microbee Systems",   "Microbee 128k Premium",       MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1987, mbee256,   mbee,   0,      mbee256,  mbee256, mbee_state, init_mbee256,  "Microbee Systems",   "Microbee 256TC",              MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 2012, mbeepp,    mbee,   0,      mbeepp,   mbee128, mbee_state, init_mbeepp,   "Microbee Systems",   "Microbee Premium Plus+",      MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



mbs1.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Angelo Salese
/**************************************************************************************************

MB-S1 (c) 1984 Hitachi

TODO:
- Cassette won't load, not even in Level 3 mode;
- Many features missing;

**************************************************************************************************/

#include "emu.h"

#include "bml3.h"
#include "mbs1_mmu.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class mbs1_state : public bml3mk5_state
{
public:
	mbs1_state(const machine_config &mconfig, device_type type, const char *tag)
		: bml3mk5_state(mconfig, type, tag)
		, m_mmu(*this, "mmu")
		, m_vtram(*this, "vtram")
		, m_system_mode(*this, "SYSTEM_MODE")
	{ }

	void mbs1(machine_config &config) ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

//  virtual void main_map(address_map &map) override ATTR_COLD;
	virtual void system_io(address_map &map) override ATTR_COLD;
	void s1_map(address_map &map) ATTR_COLD;
	void s1_mmu_map(address_map &map) ATTR_COLD;
	void s1_ext_io(address_map &map) ATTR_COLD;

	virtual MC6845_UPDATE_ROW(crtc_update_row) override;

private:
	required_device<mbs1_mmu_device> m_mmu;
	required_shared_ptr<u8> m_vtram;
	required_ioport m_system_mode;

	bool m_su_mode = 0;
	u8 m_system_clock = 0;
	bool m_system_type = false;
};

MC6845_UPDATE_ROW( mbs1_state::crtc_update_row )
{
	if (!m_system_type)
	{
		bml3mk5_state::crtc_update_row(bitmap, cliprect, ma, ra, y, x_count, cursor_x, de, hbp, vbp);
		return;
	}
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u8 const bgcolor = 0; //m_hres_reg & 7;

	// TODO: WIDTH80, pinpoint bit used
	for (u8 x = 0; x < x_count; x += 2)
	{
		u16 mem = ((ma + x) >> 1) & 0x7ff;

		//u8 const attr = m_aram[mem];
		u8 const rawbits = m_vtram[mem];
		int const tile = rawbits & 0x7f;
		int const tile_bank = BIT(rawbits, 7);
		u8 const color = 7; //attr & 7;
		bool const reverse = (x == cursor_x); // ^BIT(attr, 3);

		u8 gfx_data = m_p_chargen[(tile<<4)|(ra<<1)|tile_bank];

		for (u8 xi = 0; xi < 8; xi++)
		{
			u8 const pen = BIT(reverse ? ~gfx_data : gfx_data, 7 - xi) ? color : bgcolor;
			int const res_x = x * 8 + xi * 2;
			bitmap.pix(y, res_x + 0) = palette[pen];
			bitmap.pix(y, res_x + 1) = palette[pen];
		}
	}

}


void mbs1_state::s1_map(address_map &map)
{
	map(0x0000, 0xffff).rw(m_mmu, FUNC(mbs1_mmu_device::read), FUNC(mbs1_mmu_device::write));
}

// TODO: sloppily enough, a memory_view can't see that is actually on higher base
// so using just map(0xf0000, 0xfffff).m(*this, FUNC(mbs1_state::main_map));
// will just punt with "A memory_view must be installed at its configuration address."
// We also eventually need to add some memory sharing here, for the work RAM so for now
// lets just repeat lv3 memory map.
void mbs1_state::s1_mmu_map(address_map &map)
{
	map.unmap_value_high();
//  map(0x00000, 0x7ffff) extended RAM

//  map(0x84000, 0x8ffff) mirror of main work RAM +$4000
	map(0x84000, 0x8ffff).ram();
//  map(0xb0000, 0xb3fff) GVRAM
//  map(0xb4000, 0xbbfff) extra GVRAM banks
	map(0xbc000, 0xbc7ff).mirror(0x800).ram().share("vtram");
//  map(0xc0000, 0xc6fff) ROM for MPC-CM01 (card?)
//  map(0xc7000, 0xc7fff) EEPROM for MPC-CM01
	map(0xd0000, 0xd7fff).rom().region("dictionary", 0);
	map(0xe0000, 0xe7fff).rom().region("s1basic", 0);
//  map(0xe2000, 0xe2fff) view overlay for MPC-CM01
	map(0xe8000, 0xeffff).rom().region("s1boot", 0);
	map(0xefe00, 0xefeff).m(*this, FUNC(mbs1_state::s1_ext_io));
	map(0xeff00, 0xeffff).m(*this, FUNC(mbs1_state::system_io));

	map(0xf0000, 0xf03ff).ram();
	map(0xf0400, 0xf43ff).rw(FUNC(mbs1_state::vram_r), FUNC(mbs1_state::vram_w));
	map(0xf4400, 0xf7fff).ram();
	map(0xf8000, 0xf9fff).ram();
	map(0xfa000, 0xfbfff).view(m_banka);
	m_banka[0](0xfa000, 0xfbfff).readonly().share("rama");
	map(0xfa000, 0xfbfff).writeonly().share("rama");
	map(0xfc000, 0xfdfff).view(m_bankc);
	m_bankc[0](0xfc000, 0xfdfff).readonly().share("ramc");
	map(0xfc000, 0xfdfff).writeonly().share("ramc");
	map(0xfe000, 0xfefff).view(m_banke);
	m_banke[0](0xfe000, 0xfefff).readonly().share("rame");
	map(0xfe000, 0xfefff).writeonly().share("rame");
	map(0xff000, 0xffeff).view(m_bankf);
	m_bankf[0](0xff000, 0xffeff).readonly().share("ramf");
	map(0xff000, 0xffeff).writeonly().share("ramf");
	map(0xffff0, 0xfffff).view(m_bankg);
	m_bankg[0](0xffff0, 0xfffff).readonly().share("ramg");
	map(0xffff0, 0xfffff).writeonly().share("ramg");
	map(0xf8000, 0xfffff).view(m_rom_view);
	m_rom_view[0](0xfa000, 0xffeff).rom().region("maincpu", 0xa000);
	m_rom_view[0](0xffff0, 0xfffff).rom().region("maincpu", 0xfff0);
	map(0xfff00, 0xfffef).m(*this, FUNC(mbs1_state::system_io));
	map(0xfa000, 0xfa7ff).view(m_ig_view);
	m_ig_view[0](0xfa000, 0xfa7ff).writeonly().w(FUNC(mbs1_state::ig_ram_w));

}

// relative to $efe00
void mbs1_state::s1_ext_io(address_map &map)
{
	map(0x00, 0x0f).rw(m_mmu, FUNC(mbs1_mmu_device::bank_r), FUNC(mbs1_mmu_device::bank_w));
//  map(0x10, 0x10) FUSE (w/o)
//  map(0x11, 0x11) OS-9 address segment
//  map(0x18, 0x18) TRAP (r/o)
//  map(0x19, 0x19) BUS control
//  map(0x1a, 0x1b) Pro-control / Acc-control for MPC-68008
//  map(0x20, 0x20) BMSK color (w/o)
//  map(0x21, 0x21) Active graphic plane
//  map(0x23, 0x23) Display page
//  map(0x24, 0x24) SCRN mode
//  map(0x25, 0x27) BRG graphic color
//  map(0x28, 0x2f) palette
//  map(0x40, 0x41) PIA 1 (DE-9)
//  map(0x42, 0x43) PIA 2 (Printer, mirror of 0xffc2-c3)
}

void mbs1_state::system_io(address_map &map)
{
	bml3mk5_state::system_io(map);
	map(0xeffeb, 0xeffeb).lrw8(
		NAME([this]() {
			return (m_system_type) | (m_system_clock << 1) | (m_su_mode << 2);
		}),
		NAME([this] (u8 data) {
			logerror("$effeb system clock write %02x\n", data);
			m_system_clock = BIT(data, 1);
			m_maincpu->set_unscaled_clock(CPU_EXT_CLOCK * (m_system_clock + 1));
		})
	);
}

// TODO: duplicated from base bml3, needs keyboard converted into slot device
static INPUT_PORTS_START( mbs1 )
	PORT_START("DIPSW")
	PORT_BIT( 0x03, IP_ACTIVE_LOW, IPT_UNUSED )
	// TODO: seems to apply for Level 3 mode only
	// May have been removed and hardwired to '1' here.
	PORT_DIPNAME( 0x04, 0x04, "40-/80-column")
	PORT_DIPSETTING(0x00, "40 chars/line")
	PORT_DIPSETTING(0x04, "80 chars/line")
	PORT_BIT( 0xf8, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("SYSTEM_MODE")
	PORT_DIPNAME( 0x01, 0x01, "System Mode")
	PORT_DIPSETTING(0x00, "Level 3 mode")
	PORT_DIPSETTING(0x01, "S1 mode")

	PORT_START("X0") //0x00-0x1f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("? PAD")
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Control") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("X2")  // ???
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Caps Lock") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Kana Lock") PORT_CODE(KEYCODE_NUMLOCK)
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Kana Shift")  PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("8 PAD") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("9 PAD") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("*") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR(39)
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('^')
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Delete") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("7 PAD") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Delete PAD") //backspace
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(u8"¥") PORT_CODE(KEYCODE_TAB)

	PORT_START("X1") //0x20-0x3f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('@')
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0 PAD") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(". PAD")  PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("HOME") PORT_CODE(KEYCODE_HOME) //or cls?
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("4 PAD") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("5 PAD") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("6 PAD") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("- PAD") PORT_CODE(KEYCODE_MINUS_PAD)

	PORT_START("X2") //0x40-0x5f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("/ PAD") PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("_") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('_')
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1 PAD") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2 PAD") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3 PAD") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("+") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF5") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT(0xffe00000,IP_ACTIVE_HIGH,IPT_UNKNOWN)

	PORT_START("X3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(bml3_state::nmi_button), 0)
INPUT_PORTS_END

void mbs1_state::machine_start()
{
	bml3mk5_state::machine_start();
	save_item(NAME(m_system_clock));
	save_item(NAME(m_su_mode));
}


void mbs1_state::machine_reset()
{
	bml3mk5_state::machine_reset();
	m_system_type = bool(BIT(m_system_mode->read(), 0));
	m_mmu->init_banks(m_system_type, m_su_mode);
}


void mbs1_state::mbs1(machine_config &config)
{
	bml3mk5_state::bml3mk5(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &mbs1_state::s1_map);

	MBS1_MMU(config, m_mmu, 0);
	m_mmu->set_map(&mbs1_state::s1_mmu_map);
}

ROM_START( mbs1 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD("rom1.rom", 0xa000, 0x5f00, CRC(b23b0530) SHA1(cb9a8352d2ba0fc0085e4219cd07c91c8447ab75) )
	ROM_LOAD("rom2.rom", 0xfff0, 0x0010, CRC(63b9adb2) SHA1(62b950671b533d18bca36eae362f2bc26638b587) )

	ROM_REGION( 0x8000, "s1boot", ROMREGION_ERASEFF )
	ROM_LOAD("s1rom2.rom", 0x0000, 0x7e00, CRC(e266108c) SHA1(18af24fa8f123960562fbe1c58e209d06d62c205) )
	ROM_LOAD("s1romi.rom", 0x7ff0, 0x0010, CRC(81cc0e4d) SHA1(7e52264e0fdb5cb9a3489c70842f4d12146060ba) )

	ROM_REGION( 0x8000, "s1basic", ROMREGION_ERASEFF )
	ROM_LOAD("s1bas1.rom", 0x0000, 0x8000, CRC(efb80721) SHA1(81c926f9fa658012cd04b30176aad800db46594e) )

	ROM_REGION( 0x8000, "dictionary", ROMREGION_ERASEFF )
	ROM_LOAD("s1dic.rom", 0x0000, 0x8000, NO_DUMP )

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD("font.rom", 0x0000, 0x1000, CRC(d1f27c5a) SHA1(a3abbdea9f6656bd795fd35ee806a54d7be35de0) )
ROM_END

} // anonymous namespace


COMP( 1984, mbs1,    0,     0,      mbs1,    mbs1,  mbs1_state,    empty_init, "Hitachi", "MB-S1", MACHINE_NOT_WORKING )



mc10.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nathan Woods, Dirk Best, tim lindner
/***************************************************************************

    TRS-80 Radio Shack MicroColor Computer

***************************************************************************/

#include "emu.h"

#include "cpu/m6800/m6801.h"
#include "imagedev/cassette.h"
#include "imagedev/printer.h"
#include "bus/mc10/mc10_cart.h"
#include "bus/rs232/rs232.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "sound/dac.h"
#include "video/ef9345.h"
#include "video/mc6847.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "formats/coco_cas.h"

namespace
{

/***************************************************************************
    TYPE DEFINITIONS
***************************************************************************/

class mc10_state : public driver_device
{
public:
	mc10_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_mc10cart(*this, "ext")
		, m_mc6847(*this, "mc6847")
		, m_dac(*this, "dac")
		, m_cassette(*this, "cassette")
		, m_rs232(*this, "rs232")
		, m_pb(*this, "pb%u", 0U)
	{}

	void mc10_base(machine_config &config);
	void mc10_video(machine_config &config);
	void mc10(machine_config &config);
	void alice(machine_config &config);
	void mc10_bfff_w_dac(uint8_t data);
	uint8_t mc10_bfff_r();

protected:
	void mc10_bfff_w(uint8_t data);

	uint8_t mc10_port1_r();
	void mc10_port1_w(uint8_t data);
	uint8_t mc10_port2_r();
	void mc10_port2_w(uint8_t data);

	uint8_t mc6847_videoram_r(offs_t offset);

	// device-level overrides
	virtual void driver_start() override;
	virtual void driver_reset() override;

	required_device<m6803_cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	optional_device<mc10cart_slot_device> m_mc10cart;

	uint8_t m_keyboard_strobe = 0;
	uint8_t m_port2 = 0;

	uint8_t read_keyboard_strobe(bool single_line);

	mc10cart_slot_device &mc10cart() { return *m_mc10cart; }

private:
	void mc10_mem(address_map &map) ATTR_COLD;

	optional_device<mc6847_base_device> m_mc6847;
	required_device<dac_bit_interface> m_dac;
	required_device<cassette_image_device> m_cassette;
	required_device<rs232_port_device> m_rs232;
	required_ioport_array<8> m_pb;
};

class alice32_state : public mc10_state
{
public:
	alice32_state(const machine_config &mconfig, device_type type, const char *tag)
		: mc10_state(mconfig, type, tag)
		, m_ef9345(*this, "ef9345")
	{}

	void alice32(machine_config &config);
	void alice90(machine_config &config);
	void alice32_bfff_w(uint8_t data);
	uint8_t alice90_bfff_r();

protected:
	// device-level overrides
	virtual void driver_start() override;

	TIMER_DEVICE_CALLBACK_MEMBER(alice32_scanline);
	required_device<ef9345_device> m_ef9345;

private:
	void alice32_mem(address_map &map) ATTR_COLD;
	void alice90_mem(address_map &map) ATTR_COLD;
};

/***************************************************************************
    MEMORY MAPPED I/O
***************************************************************************/

uint8_t mc10_state::read_keyboard_strobe(bool single_line)
{
	bool read = false;
	uint8_t result = 0xff;

	for (int i = m_pb.size() - 1; (i >= 0) && (!read || !single_line); i--)
	{
		if (!BIT(m_keyboard_strobe, i))
		{
			result &= m_pb[i]->read();
			read = true;
		}
	}

	return result;
}

uint8_t mc10_state::mc10_bfff_r()
{
	return read_keyboard_strobe(false);
}

uint8_t alice32_state::alice90_bfff_r()
{
	return read_keyboard_strobe(true);
}

void mc10_state::mc10_bfff_w(uint8_t data)
{
	// bit 2 to 6, mc6847 mode lines
	m_mc6847->gm2_w(BIT(data, 2));
	m_mc6847->intext_w(BIT(data, 2));
	m_mc6847->gm1_w(BIT(data, 3));
	m_mc6847->gm0_w(BIT(data, 4));
	m_mc6847->ag_w(BIT(data, 5));
	m_mc6847->css_w(BIT(data, 6));

	mc10_bfff_w_dac(data);
}

void mc10_state::mc10_bfff_w_dac(uint8_t data)
{
	// bit 7, dac output
	m_dac->write(BIT(data, 7));
}

void alice32_state::alice32_bfff_w(uint8_t data)
{
	mc10_bfff_w_dac(data);
}

/***************************************************************************
    MC6803 I/O
***************************************************************************/

uint8_t mc10_state::mc10_port1_r()
{
	return m_keyboard_strobe;
}

void mc10_state::mc10_port1_w(uint8_t data)
{
	m_keyboard_strobe = data;
}

uint8_t mc10_state::mc10_port2_r()
{
	uint8_t result = 0xeb;

	// bit 1, keyboard line pa6
	if (!BIT(m_keyboard_strobe, 0)) result &= m_pb[0]->read() >> 5;
	if (!BIT(m_keyboard_strobe, 2)) result &= m_pb[2]->read() >> 5;
	if (!BIT(m_keyboard_strobe, 7)) result &= m_pb[7]->read() >> 5;

	// bit 2, rs232 input
	result |= (m_rs232->rxd_r() ? 1 : 0) << 2;

	// bit 3, printer ots input
	result |= (m_rs232->cts_r() ? 1 : 0) << 3;

	// bit 4, cassette input
	result |= (m_cassette->input() >= 0 ? 1 : 0) << 4;

	return result;
}

void mc10_state::mc10_port2_w(uint8_t data)
{
	// bit 0, cassette & printer output
	m_cassette->output( BIT(data, 0) ? +1.0 : -1.0);
	m_rs232->write_txd(BIT(data, 0) ? 1 : 0);

	m_port2 = data;
}


/***************************************************************************
    VIDEO EMULATION
***************************************************************************/

uint8_t mc10_state::mc6847_videoram_r(offs_t offset)
{
	if (offset == ~0) return 0xff;

	uint8_t result = m_ram->read(offset);
	m_mc6847->inv_w(BIT(result, 6));
	m_mc6847->as_w(BIT(result, 7));
	return result;
}

TIMER_DEVICE_CALLBACK_MEMBER(alice32_state::alice32_scanline)
{
	m_ef9345->update_scanline((uint16_t)param);
}

/***************************************************************************
    DRIVER INIT
***************************************************************************/

void mc10_state::driver_reset()
{
	m_keyboard_strobe = 0x00;
}

void mc10_state::driver_start()
{
	save_item(NAME(m_keyboard_strobe));

	address_space &space = m_maincpu->space(AS_PROGRAM);
	u32 ram_size = m_ram->size();

	// don't step on hardware page at 0xbf00
	if (ram_size == 0x8000)
		ram_size -= 0x100;

	space.install_ram(0x4000, 0x4000+ram_size-1, m_ram->pointer());
}

void alice32_state::driver_start()
{
	save_item(NAME(m_keyboard_strobe));

	address_space &space = m_maincpu->space(AS_PROGRAM);
	u32 ram_size = m_ram->size();

	space.install_ram(0x3000, 0x3000+ram_size-1, m_ram->pointer());
}

/***************************************************************************
    ADDRESS MAPS
***************************************************************************/

void mc10_state::mc10_mem(address_map &map)
{
	// mc10 / alice: RAM start at 0x4000, installed in driver_start
	map(0xbfff, 0xbfff).rw(FUNC(mc10_state::mc10_bfff_r), FUNC(mc10_state::mc10_bfff_w));
	map(0xe000, 0xffff).rom().region("maincpu", 0x0000);
}

void alice32_state::alice32_mem(address_map &map)
{
	// alice32: RAM start at 0x3000, installed in driver_start
	map(0xbf20, 0xbf29).rw(m_ef9345, FUNC(ef9345_device::data_r), FUNC(ef9345_device::data_w));
	map(0xbfff, 0xbfff).rw(FUNC(mc10_state::mc10_bfff_r), FUNC(alice32_state::alice32_bfff_w));
	map(0xc000, 0xffff).rom().region("maincpu", 0x0000);
}

void alice32_state::alice90_mem(address_map &map)
{
	// alice90: RAM start at 0x3000, installed in driver_start
	map(0xbf20, 0xbf29).rw(m_ef9345, FUNC(ef9345_device::data_r), FUNC(ef9345_device::data_w));
	map(0xbfff, 0xbfff).rw(FUNC(alice32_state::alice90_bfff_r), FUNC(alice32_state::alice32_bfff_w));
	map(0xc000, 0xffff).rom().region("maincpu", 0x0000);
}



/***************************************************************************
    INPUT PORTS
***************************************************************************/

/* MC-10 keyboard

       PB0 PB1 PB2 PB3 PB4 PB5 PB6 PB7
  PA6: Ctl N/c Brk N/c N/c N/c N/c Shift
  PA5: 8   9   :   ;   ,   -   .   /
  PA4: 0   1   2   3   4   5   6   7
  PA3: X   Y   Z   N/c N/c N/c Ent Space
  PA2: P   Q   R   S   T   U   V   W
  PA1: H   I   J   K   L   M   N   O
  PA0: @   A   B   C   D   E   F   G
 */

/*  Port                                        Key description                 Emulated key                  Natural key     Shift 1         Shift 2 (Ctrl) */
INPUT_PORTS_START( mc10 )
	PORT_START("pb0") /* KEY ROW 0 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@     INPUT")        PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H     THEN")         PORT_CODE(KEYCODE_H)          PORT_CHAR('H')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P     INKEY$")       PORT_CODE(KEYCODE_P)          PORT_CHAR('P')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X     SQN")          PORT_CODE(KEYCODE_X)          PORT_CHAR('X')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0")                  PORT_CODE(KEYCODE_0)          PORT_CHAR('0')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8  (  CLS")          PORT_CODE(KEYCODE_8)          PORT_CHAR('8')  PORT_CHAR('(')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CONTROL")            PORT_CODE(KEYCODE_LSHIFT)     PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("pb1") /* KEY ROW 1 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A     \xE2\x86\x90") PORT_CODE(KEYCODE_A)          PORT_CHAR('A')  PORT_CHAR('~')  PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I     NEXT")         PORT_CODE(KEYCODE_I)          PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q     L.DEL")        PORT_CODE(KEYCODE_Q)          PORT_CHAR('Q')  PORT_CHAR('~')  PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y     RESTORE")      PORT_CODE(KEYCODE_Y)          PORT_CHAR('Y')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1  !  RUN")          PORT_CODE(KEYCODE_1)          PORT_CHAR('1')  PORT_CHAR('!')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9  )  PRINT")        PORT_CODE(KEYCODE_9)          PORT_CHAR('9')  PORT_CHAR(')')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("pb2") /* KEY ROW 2 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B     ABS")          PORT_CODE(KEYCODE_B)          PORT_CHAR('B')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J     GOTO")         PORT_CODE(KEYCODE_J)          PORT_CHAR('J')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R     RESET")        PORT_CODE(KEYCODE_R)          PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z     \xE2\x86\x93") PORT_CODE(KEYCODE_Z)          PORT_CHAR('Z')  PORT_CHAR('~')  PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2  \"  CONT")        PORT_CODE(KEYCODE_2)          PORT_CHAR('2')  PORT_CHAR('\"')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(":  *  END")          PORT_CODE(KEYCODE_MINUS)      PORT_CHAR(':')  PORT_CHAR('*')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BREAK")              PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(UCHAR_MAMEKEY(CANCEL))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("pb3") /* KEY ROW 3 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C     INT")          PORT_CODE(KEYCODE_C)          PORT_CHAR('C')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K     SOUND")        PORT_CODE(KEYCODE_K)          PORT_CHAR('K')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S     \xE2\x86\x92") PORT_CODE(KEYCODE_S)          PORT_CHAR('S')  PORT_CHAR('~')  PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3  #  CSAVE")        PORT_CODE(KEYCODE_3)          PORT_CHAR('3')  PORT_CHAR('#')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";  +  POKE")         PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';')  PORT_CHAR('+')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("pb4") /* KEY ROW 4 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D     GOSUB")        PORT_CODE(KEYCODE_D)          PORT_CHAR('D')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L     PEEK")         PORT_CODE(KEYCODE_L)          PORT_CHAR('L')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T     READ")         PORT_CODE(KEYCODE_T)          PORT_CHAR('T')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4  $  CLOAD")        PORT_CODE(KEYCODE_4)          PORT_CHAR('4')  PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",  <  TAN")          PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')  PORT_CHAR('<')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("pb5") /* KEY ROW 5 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E     SET")          PORT_CODE(KEYCODE_E)          PORT_CHAR('E')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M     COS")          PORT_CODE(KEYCODE_M)          PORT_CHAR('M')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U     FOR")          PORT_CODE(KEYCODE_U)          PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5  %  NEW")          PORT_CODE(KEYCODE_5)          PORT_CHAR('5')  PORT_CHAR('%')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-  =  STOP")         PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('-')  PORT_CHAR('=')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("pb6") /* KEY ROW 6 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F     RETURN")       PORT_CODE(KEYCODE_F)          PORT_CHAR('F')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N     SIN")          PORT_CODE(KEYCODE_N)          PORT_CHAR('N')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V     RND")          PORT_CODE(KEYCODE_V)          PORT_CHAR('V')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER")              PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6  &  LIST")         PORT_CODE(KEYCODE_6)          PORT_CHAR('6')  PORT_CHAR('&')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".  >  LOG")          PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')  PORT_CHAR('>')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("pb7") /* KEY ROW 7 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G     IF")           PORT_CODE(KEYCODE_G)          PORT_CHAR('G')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O     STEP")         PORT_CODE(KEYCODE_O)          PORT_CHAR('O')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W     \xE2\x86\x91") PORT_CODE(KEYCODE_W)          PORT_CHAR('W')  PORT_CHAR('~')  PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE")              PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7  '  CLEAR")        PORT_CODE(KEYCODE_7)          PORT_CHAR('7')  PORT_CHAR('\'')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/  ?  SQR")          PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/')  PORT_CHAR('?')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT")              PORT_CODE(KEYCODE_RSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

/* Alice uses an AZERTY keyboard */
/*  Port                                        Key description                 Emulated key                  Natural key     Shift 1         Shift 2 (Ctrl) */
INPUT_PORTS_START( alice )
	PORT_START("pb0") /* KEY ROW 0 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@     INPUT")        PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H     THEN")         PORT_CODE(KEYCODE_H)          PORT_CHAR('H')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P     INKEY$")       PORT_CODE(KEYCODE_P)          PORT_CHAR('P')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X     SQN")          PORT_CODE(KEYCODE_X)          PORT_CHAR('X')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0")                  PORT_CODE(KEYCODE_0)          PORT_CHAR('0')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8  (  CLS")          PORT_CODE(KEYCODE_8)          PORT_CHAR('8')  PORT_CHAR('(')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CONTROL")            PORT_CODE(KEYCODE_LSHIFT)     PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("pb1") /* KEY ROW 1 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q     L.DEL")        PORT_CODE(KEYCODE_Q)          PORT_CHAR('Q')  PORT_CHAR('~')  PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I     NEXT")         PORT_CODE(KEYCODE_I)          PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A     \xE2\x86\x90") PORT_CODE(KEYCODE_A)          PORT_CHAR('A')  PORT_CHAR('~')  PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y     RESTORE")      PORT_CODE(KEYCODE_Y)          PORT_CHAR('Y')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1  !  RUN")          PORT_CODE(KEYCODE_1)          PORT_CHAR('1')  PORT_CHAR('!')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9  )  PRINT")        PORT_CODE(KEYCODE_9)          PORT_CHAR('9')  PORT_CHAR(')')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("pb2") /* KEY ROW 2 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B     ABS")          PORT_CODE(KEYCODE_B)          PORT_CHAR('B')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J     GOTO")         PORT_CODE(KEYCODE_J)          PORT_CHAR('J')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R     RESET")        PORT_CODE(KEYCODE_R)          PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W     \xE2\x86\x91") PORT_CODE(KEYCODE_W)          PORT_CHAR('W')  PORT_CHAR('~')  PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2  \"  CONT")        PORT_CODE(KEYCODE_2)          PORT_CHAR('2')  PORT_CHAR('\"')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(":  *  END")          PORT_CODE(KEYCODE_MINUS)      PORT_CHAR(':')  PORT_CHAR('*')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BREAK")              PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(UCHAR_MAMEKEY(CANCEL))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("pb3") /* KEY ROW 3 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C     INT")          PORT_CODE(KEYCODE_C)          PORT_CHAR('C')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K     SOUND")        PORT_CODE(KEYCODE_K)          PORT_CHAR('K')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S     \xE2\x86\x92") PORT_CODE(KEYCODE_S)          PORT_CHAR('S')  PORT_CHAR('~')  PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3  #  CSAVE")        PORT_CODE(KEYCODE_3)          PORT_CHAR('3')  PORT_CHAR('#')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M     COS")          PORT_CODE(KEYCODE_M)          PORT_CHAR('M')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("pb4") /* KEY ROW 4 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D     GOSUB")        PORT_CODE(KEYCODE_D)          PORT_CHAR('D')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L     PEEK")         PORT_CODE(KEYCODE_L)          PORT_CHAR('L')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T     READ")         PORT_CODE(KEYCODE_T)          PORT_CHAR('T')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4  $  CLOAD")        PORT_CODE(KEYCODE_4)          PORT_CHAR('4')  PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",  <  TAN")          PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')  PORT_CHAR('<')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("pb5") /* KEY ROW 5 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E     SET")          PORT_CODE(KEYCODE_E)          PORT_CHAR('E')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/  ?  SQR")          PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/')  PORT_CHAR('?')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U     FOR")          PORT_CODE(KEYCODE_U)          PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5  %  NEW")          PORT_CODE(KEYCODE_5)          PORT_CHAR('5')  PORT_CHAR('%')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-  =  STOP")         PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('-')  PORT_CHAR('=')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("pb6") /* KEY ROW 6 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F     RETURN")       PORT_CODE(KEYCODE_F)          PORT_CHAR('F')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N     SIN")          PORT_CODE(KEYCODE_N)          PORT_CHAR('N')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V     RND")          PORT_CODE(KEYCODE_V)          PORT_CHAR('V')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER")              PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6  &  LIST")         PORT_CODE(KEYCODE_6)          PORT_CHAR('6')  PORT_CHAR('&')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".  >  LOG")          PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')  PORT_CHAR('>')
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("pb7") /* KEY ROW 7 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G     IF")           PORT_CODE(KEYCODE_G)          PORT_CHAR('G')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O     STEP")         PORT_CODE(KEYCODE_O)          PORT_CHAR('O')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z     \xE2\x86\x93") PORT_CODE(KEYCODE_Z)          PORT_CHAR('Z')  PORT_CHAR('~')  PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE")              PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7  '  CLEAR")        PORT_CODE(KEYCODE_7)          PORT_CHAR('7')  PORT_CHAR('\'')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";  +  POKE")         PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';')  PORT_CHAR('+')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT")              PORT_CODE(KEYCODE_RSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

static DEVICE_INPUT_DEFAULTS_START( printer )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

void mc10_state::mc10_base(machine_config &config)
{
	/* basic machine hardware */
	M6803(config, m_maincpu, XTAL(3'579'545));  /* 0,894886 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &mc10_state::mc10_mem);
	m_maincpu->in_p1_cb().set(FUNC(mc10_state::mc10_port1_r));
	m_maincpu->out_p1_cb().set(FUNC(mc10_state::mc10_port1_w));
	m_maincpu->in_p2_cb().set(FUNC(mc10_state::mc10_port2_r));
	m_maincpu->out_p2_cb().set(FUNC(mc10_state::mc10_port2_w));

	/* sound hardware */
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.0625);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(alice32_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "speaker", 0.05);
	m_cassette->set_interface("mc10_cass");

	/* printer */
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "rs_printer"));
	rs232.set_option_device_input_defaults("rs_printer", DEVICE_INPUT_DEFAULTS_NAME(printer));
}

void mc10_state::mc10_video(machine_config &config)
{
	/* internal ram */
	RAM(config, m_ram).set_default_size("4K").set_extra_options("8K,20K,32K");

	/* video hardware */
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	mc6847_device &vdg(MC6847(config, "mc6847", XTAL(3'579'545)));
	vdg.set_screen("screen");
	vdg.input_callback().set(FUNC(mc10_state::mc6847_videoram_r));
}

void mc10_state::mc10(machine_config &config)
{
	mc10_base(config);
	mc10_video(config);

	/* expansion port hardware */
	mc10cart_slot_device &cartslot(MC10CART_SLOT(config, "ext", DERIVED_CLOCK(1, 1), mc10_cart_add_basic_devices, nullptr));
	cartslot.set_memspace(m_maincpu, AS_PROGRAM);
	cartslot.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);

	/* Software lists */
	SOFTWARE_LIST(config, "cass_list").set_original("mc10");
}

void mc10_state::alice(machine_config &config)
{
	mc10_base(config);
	mc10_video(config);

	/* expansion port hardware */
	mc10cart_slot_device &cartslot(MC10CART_SLOT(config, "ext", DERIVED_CLOCK(1, 1), alice_cart_add_basic_devices, nullptr));
	cartslot.set_memspace(m_maincpu, AS_PROGRAM);
	cartslot.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);

	/* Software lists */
	SOFTWARE_LIST(config, "cass_list").set_original("mc10");
}

void alice32_state::alice32(machine_config &config)
{
	mc10_base(config);

	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &alice32_state::alice32_mem);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_screen_update("ef9345", FUNC(ef9345_device::screen_update));
	screen.set_size(336, 270);
	screen.set_visarea(00, 336-1, 00, 270-1);
	PALETTE(config, "palette").set_entries(8);

	EF9345(config, m_ef9345, 0);
	m_ef9345->set_screen("screen");
	m_ef9345->set_palette_tag("palette");
	TIMER(config, "alice32_sl").configure_scanline(FUNC(alice32_state::alice32_scanline), "screen", 0, 10);

	/* expansion port hardware */
	mc10cart_slot_device &cartslot(MC10CART_SLOT(config, "ext", DERIVED_CLOCK(1, 1), alice32_cart_add_basic_devices, nullptr));
	cartslot.set_memspace(m_maincpu, AS_PROGRAM);
	cartslot.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	/* internal ram */
	RAM(config, m_ram).set_default_size("8K").set_extra_options("24K");

	/* Software lists */
	SOFTWARE_LIST(config, "cass_list").set_original("alice32");
	SOFTWARE_LIST(config, "mc10_cass").set_compatible("mc10");
}

void alice32_state::alice90(machine_config &config)
{
	alice32(config);

	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &alice32_state::alice90_mem);

	/* internal ram */
	m_ram->set_default_size("32K");

	/* Software lists */
	subdevice<software_list_device>("cass_list")->set_original("alice90");
	SOFTWARE_LIST(config, "alice32_cass").set_compatible("alice32");
	config.device_remove("mc10_cass");
}

/***************************************************************************
    ROM DEFINITIONS
***************************************************************************/

ROM_START( mc10 )
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("mc10.rom", 0x0000, 0x2000, CRC(11fda97e) SHA1(4afff2b4c120334481aab7b02c3552bf76f1bc43))
ROM_END

ROM_START( alice )
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("alice.rom", 0x0000, 0x2000, CRC(f876abe9) SHA1(c2166b91e6396a311f486832012aa43e0d2b19f8))
ROM_END

ROM_START( alice32 )
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_LOAD("alice32.rom", 0x0000, 0x4000, CRC(c3854ddf) SHA1(f34e61c3cf711fb59ff4f1d4c0d2863dab0ab5d1))

	ROM_REGION( 0x2000, "ef9345", 0 )
	ROM_LOAD( "charset.rom", 0x0000, 0x2000, BAD_DUMP CRC(b2f49eb3) SHA1(d0ef530be33bfc296314e7152302d95fdf9520fc) )            // from dcvg5k
ROM_END

#define rom_alice90 rom_alice32

}

/***************************************************************************
    GAME DRIVERS
***************************************************************************/

//    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT  CLASS          INIT        COMPANY              FULLNAME     FLAGS
COMP( 1983, mc10,    0,       0,      mc10,    mc10,  mc10_state,    empty_init, "Tandy Radio Shack", "MC-10",     MACHINE_SUPPORTS_SAVE )
COMP( 1983, alice,   mc10,    0,      alice,   alice, mc10_state,    empty_init, "Matra & Hachette",  "Alice",     MACHINE_SUPPORTS_SAVE )
COMP( 1984, alice32, 0,       0,      alice32, alice, alice32_state, empty_init, "Matra & Hachette",  "Alice 32",  MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
COMP( 1985, alice90, alice32, 0,      alice90, alice, alice32_state, empty_init, "Matra & Hachette",  "Alice 90",  MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )



mc1000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/***************************************************************************

    MC 1000

    12/05/2009 Skeleton driver.

    http://ensjo.wikispaces.com/MC-1000+on+JEMU
    http://ensjo.blogspot.com/2006/11/color-artifacting-no-mc-1000.html

****************************************************************************/

/*

    TODO:

    - xtal frequency?
    - Z80 wait at 0x0000-0x1fff when !hsync & !vsync
    - 80-column card (MC6845) character generator ROM
    - Charlemagne / GEM-1000 / Junior Computer ROMs

*/

#include "emu.h"

#include "bus/centronics/ctronics.h"
#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "machine/ram.h"
#include "machine/rescap.h"
#include "machine/timer.h"
#include "sound/ay8910.h"
#include "video/mc6845.h"
#include "video/mc6847.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

#define SCREEN_TAG      "screen"
#define Z80_TAG         "u13"
#define AY8910_TAG      "u21"
#define MC6845_TAG      "mc6845"
#define MC6847_TAG      "u19"
#define CENTRONICS_TAG  "centronics"

#define MC1000_MC6845_VIDEORAM_SIZE     0x800
#define MC1000_MC6847_VIDEORAM_SIZE     0x1800

class mc1000_state : public driver_device
{
public:
	mc1000_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, Z80_TAG),
		m_vdg(*this, MC6847_TAG),
		m_crtc(*this, MC6845_TAG),
		m_centronics(*this, CENTRONICS_TAG),
		m_cassette(*this, "cassette"),
		m_ram(*this, RAM_TAG),
		m_rom(*this, Z80_TAG),
		m_mc6845_video_ram(*this, "mc6845_vram"),
		m_mc6847_video_ram(*this, "mc6847_vram"),
		m_y(*this, "Y%u", 0),
		m_joy(*this, "JOY%u", 0),
		m_modifiers(*this, "MODIFIERS"),
		m_joykeymap(*this, "JOYKEYMAP%u", 0),
		m_banks(*this, "bank%u", 1U)
	{ }

	void mc1000(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	required_device<mc6847_base_device> m_vdg;
	optional_device<mc6845_device> m_crtc;
	required_device<centronics_device> m_centronics;
	required_device<cassette_image_device> m_cassette;
	required_device<ram_device> m_ram;
	required_memory_region m_rom;
	required_shared_ptr<uint8_t> m_mc6845_video_ram;
	required_shared_ptr<uint8_t> m_mc6847_video_ram;
	required_ioport_array<8> m_y;
	required_ioport_array<2> m_joy;
	required_ioport m_modifiers;
	required_ioport_array<2> m_joykeymap;
	required_memory_bank_array<5> m_banks;

	std::unique_ptr<uint8_t[]> m_banked_ram;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint8_t printer_r();
	void printer_w(uint8_t data);
	void mc6845_ctrl_w(uint8_t data);
	void mc6847_attr_w(uint8_t data);
	void fs_w(int state);
	void hs_w(int state);
	uint8_t videoram_r(offs_t offset);
	void keylatch_w(uint8_t data);
	uint8_t keydata_r();
	uint8_t rom_banking_r(offs_t offset);

	void bankswitch();

	/* cpu state */
	int m_ne555_int;

	/* memory state */
	int m_rom0000;
	uint8_t m_mc6845_bank;
	uint8_t m_mc6847_bank;

	/* keyboard state */
	int m_keylatch;

	/* video state */
	int m_hsync;
	int m_vsync;
	uint8_t m_mc6847_attr;

	void write_centronics_busy(int state);
	int m_centronics_busy;

	void init_mc1000();
	TIMER_DEVICE_CALLBACK_MEMBER(ne555_tick);
	void mc1000_banking_mem(address_map &map) ATTR_COLD;
	void mc1000_io(address_map &map) ATTR_COLD;
	void mc1000_mem(address_map &map) ATTR_COLD;
};

/* Memory Banking */

void mc1000_state::bankswitch()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	/* MC6845 video RAM */
	m_banks[1]->set_entry(m_mc6845_bank);

	/* extended RAM */
	if (m_ram->size() > 16*1024)
	{
		program.install_readwrite_bank(0x4000, 0x7fff, m_banks[2]);
	}
	else
	{
		program.unmap_readwrite(0x4000, 0x7fff);
	}

	/* MC6847 video RAM */
	if (m_mc6847_bank)
	{
		if (m_ram->size() > 16*1024)
		{
			program.install_readwrite_bank(0x8000, 0x97ff, m_banks[3]);
		}
		else
		{
			program.unmap_readwrite(0x8000, 0x97ff);
		}
	}
	else
	{
		program.install_readwrite_bank(0x8000, 0x97ff, m_banks[3]);
	}

	m_banks[3]->set_entry(m_mc6847_bank);

	/* extended RAM */
	if (m_ram->size() > 16*1024)
	{
		program.install_readwrite_bank(0x9800, 0xbfff, m_banks[4]);
	}
	else
	{
		program.unmap_readwrite(0x9800, 0xbfff);
	}
}

/* Read/Write Handlers */

void mc1000_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

uint8_t mc1000_state::printer_r()
{
	return m_centronics_busy;
}

void mc1000_state::printer_w(uint8_t data)
{
	m_centronics->write_strobe(BIT(data, 0));
}

void mc1000_state::mc6845_ctrl_w(uint8_t data)
{
	m_mc6845_bank = BIT(data, 0);

	bankswitch();
}

void mc1000_state::mc6847_attr_w(uint8_t data)
{
	/*

	    bit     description

	    0       enable CPU video RAM access
	    1       CSS
	    2       GM0
	    3       GM1
	    4       GM2
	    5       _INT/EXT
	    6       _A/S
	    7       _A/G

	*/

	m_mc6847_bank = BIT(data, 0);
	m_vdg->css_w(BIT(data, 1));
	m_vdg->gm0_w(BIT(data, 2));
	m_vdg->gm1_w(BIT(data, 3));
	m_vdg->gm2_w(BIT(data, 4));
	m_vdg->intext_w(BIT(data, 5));
	m_vdg->as_w(BIT(data, 6));
	m_vdg->ag_w(BIT(data, 7));

	bankswitch();
}

/* Memory Maps */

void mc1000_state::mc1000_mem(address_map &map)
{
	map(0x0000, 0x1fff).bankrw("bank1");
	map(0x2000, 0x27ff).bankrw("bank2").share("mc6845_vram");
	map(0x2800, 0x3fff).ram().share("ram2800");
	map(0x4000, 0x7fff).bankrw("bank3");
	map(0x8000, 0x97ff).bankrw("bank4").share("mc6847_vram");
	map(0x9800, 0xbfff).bankrw("bank5");
	map(0xc000, 0xffff).rom().region(Z80_TAG, 0);
}

void mc1000_state::mc1000_banking_mem(address_map &map)
{
	map(0x0000, 0x1fff).bankrw("bank1");
	map(0x2000, 0x27ff).bankrw("bank2").share("mc6845_vram");
	map(0x2800, 0x3fff).ram().share("ram2800");
	map(0x4000, 0x7fff).bankrw("bank3");
	map(0x8000, 0x97ff).bankrw("bank4").share("mc6847_vram");
	map(0x9800, 0xbfff).bankrw("bank5");
	map(0xc000, 0xffff).r(FUNC(mc1000_state::rom_banking_r));
}

void mc1000_state::mc1000_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x04, 0x04).rw(FUNC(mc1000_state::printer_r), FUNC(mc1000_state::printer_w));
	map(0x05, 0x05).w("cent_data_out", FUNC(output_latch_device::write));
//  map(0x10, 0x10).w(m_crtc, FUNC(mc6845_device::address_w));
//  map(0x11, 0x11).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x12, 0x12).w(FUNC(mc1000_state::mc6845_ctrl_w));
	map(0x20, 0x20).w(AY8910_TAG, FUNC(ay8910_device::address_w));
	map(0x40, 0x40).r(AY8910_TAG, FUNC(ay8910_device::data_r));
	map(0x60, 0x60).w(AY8910_TAG, FUNC(ay8910_device::data_w));
	map(0x80, 0x80).w(FUNC(mc1000_state::mc6847_attr_w));
}

/* Input Ports */

static INPUT_PORTS_START( mc1000 )
	PORT_START("JOY0") /* Player 1 */
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )    /* = 'I' */
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )  /* = 'Q' */
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )  /* = 'Y' */
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) /* = '1' */
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1 )        /* = '9' */
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("JOY1") /* Player 2 */
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)        /* = '@' */
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)    /* = 'H' */
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)  /* = 'P' */
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)  /* = 'X' */
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) /* = '0' */
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('@')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RUBOUT") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('^')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RESET")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("MODIFIERS")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))

	PORT_START("JOYKEYMAP0")
	PORT_CONFNAME( 0x01, 0x00, "JOYSTICK A (P1) keyboard mapping" )
	PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
	PORT_CONFSETTING( 0x01, DEF_STR( On ) )

	PORT_START("JOYKEYMAP1")
	PORT_CONFNAME( 0x01, 0x00, "JOYSTICK B (P2) keyboard mapping" )
	PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
	PORT_CONFSETTING( 0x01, DEF_STR( On ) )
INPUT_PORTS_END

/* Video */

void mc1000_state::fs_w(int state)
{
	m_vsync = state;
}

void mc1000_state::hs_w(int state)
{
	m_hsync = state;
}

uint8_t mc1000_state::videoram_r(offs_t offset)
{
	if (offset == ~0) return 0xff;

	m_vdg->inv_w(BIT(m_mc6847_video_ram[offset], 7));

	return m_mc6847_video_ram[offset];
}

/* AY-3-8910 Interface */

void mc1000_state::keylatch_w(uint8_t data)
{
	m_keylatch = data;

	m_cassette->output(BIT(data, 7) ? -1.0 : +1.0);
}

uint8_t mc1000_state::keydata_r()
{
	uint8_t data = 0xff;

	if (!BIT(m_keylatch, 0))
	{
		data &= m_y[0]->read();
		if (m_joykeymap[1]->read()) data &= m_joy[1]->read();
	}
	if (!BIT(m_keylatch, 1))
	{
		data &= m_y[1]->read();
		if (m_joykeymap[0]->read()) data &= m_joy[0]->read();
	}
	if (!BIT(m_keylatch, 2)) data &= m_y[2]->read();
	if (!BIT(m_keylatch, 3)) data &= m_y[3]->read();
	if (!BIT(m_keylatch, 4)) data &= m_y[4]->read();
	if (!BIT(m_keylatch, 5)) data &= m_y[5]->read();
	if (!BIT(m_keylatch, 6)) data &= m_y[6]->read();
	if (!BIT(m_keylatch, 7)) data &= m_y[7]->read();

	data = (m_modifiers->read() & 0xc0) | (data & 0x3f);

	if ((m_cassette->get_state() & CASSETTE_MASK_UISTATE) == CASSETTE_PLAY)
	{
		if (m_cassette->input() >= +0.0) data &= 0x7f;
	}

	return data;
}


uint8_t mc1000_state::rom_banking_r(offs_t offset)
{
	m_banks[0]->set_entry(0);
	m_rom0000 = 0;
	return m_rom->base()[offset];
}

/* Machine Initialization */

void mc1000_state::machine_start()
{
	/* setup memory banking */
	m_banked_ram = make_unique_clear<uint8_t[]>(0xc000);

	m_banks[0]->configure_entry(0, m_banked_ram.get());
	m_banks[0]->configure_entry(1, m_rom->base());
	m_banks[0]->set_entry(1);

	m_rom0000 = 1;
	m_mc6845_bank = 0;
	m_mc6847_bank = 0;

	m_banks[1]->configure_entry(0, m_banked_ram.get() + 0x2000);
	m_banks[1]->configure_entry(1, m_mc6845_video_ram);
	m_banks[1]->set_entry(0);

	m_banks[2]->configure_entry(0, m_banked_ram.get() + 0x4000);
	m_banks[2]->set_entry(0);

	m_banks[3]->configure_entry(0, m_mc6847_video_ram);
	m_banks[3]->configure_entry(1, m_banked_ram.get() + 0x8000);
	m_banks[3]->set_entry(0);

	m_banks[4]->configure_entry(0, m_banked_ram.get() + 0x9800);
	m_banks[4]->set_entry(0);

	bankswitch();

	/* register for state saving */
	save_pointer(NAME(m_banked_ram), 0xc000);
	save_item(NAME(m_rom0000));
	save_item(NAME(m_mc6845_bank));
	save_item(NAME(m_mc6847_bank));
	save_item(NAME(m_keylatch));
	save_item(NAME(m_hsync));
	save_item(NAME(m_vsync));
}

void mc1000_state::machine_reset()
{
	m_banks[0]->set_entry(1);

	m_rom0000 = 1;
}

/* Machine Driver */

/*

 Interrupt generator:
 NE555 chip in astable circuit.

  +---------*---*---o V+
  |         |   |
 +-+        |   |
 | |390K    |   |
 | |R17     |8  |4
 +-+      +-------+
  |      7|       |3
  *-------|       |-------> /INT (Z80)
  |       |       |
  |       |       |
 +-+R16  2| IC 28 |
 | |1K +--|       |
 | |   |  |  555  |
 +-+   |  |       |
  |    | 6|       |5
  *----*--|       |---+
  |       |       |   |
 ---C30   +-------+  ---C29
 ---10n       |1     ---10n
 _|_         _|_     _|_
 ///         ///     ///

 Calculated properties:

 * 99.74489795918367 Duty Cycle Percentage
 * 367.3469387755102 Frequency in Hertz
 * 0.00000693 Seconds Low
 * 0.00270963 Seconds High

 */

#define MC1000_NE555_FREQ       (367) /* Hz */
#define MC1000_NE555_DUTY_CYCLE (99.745) /* % */

TIMER_DEVICE_CALLBACK_MEMBER(mc1000_state::ne555_tick)
{
	// (m_ne555_int not needed anymore and can be done with?)
	m_ne555_int = param;

	m_maincpu->set_input_line(INPUT_LINE_IRQ0, param);
}

void mc1000_state::mc1000(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 3579545);
	m_maincpu->set_addrmap(AS_PROGRAM, &mc1000_state::mc1000_mem);
	m_maincpu->set_addrmap(AS_OPCODES, &mc1000_state::mc1000_banking_mem);
	m_maincpu->set_addrmap(AS_IO, &mc1000_state::mc1000_io);

	/* timers */
	timer_device &ne555clear(TIMER(config, "ne555clear"));
	ne555clear.configure_periodic(FUNC(mc1000_state::ne555_tick), attotime::from_hz(MC1000_NE555_FREQ));
	ne555clear.config_param(CLEAR_LINE);

	timer_device &ne555assert(TIMER(config, "ne555assert"));
	ne555assert.configure_periodic(FUNC(mc1000_state::ne555_tick), attotime::from_hz(MC1000_NE555_FREQ));
	ne555assert.set_start_delay(attotime::from_hz(MC1000_NE555_FREQ * 100 / MC1000_NE555_DUTY_CYCLE));
	ne555assert.config_param(ASSERT_LINE);

	/* video hardware */
	SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER);

	MC6847(config, m_vdg, XTAL(3'579'545));
	m_vdg->hsync_wr_callback().set(FUNC(mc1000_state::hs_w));
	m_vdg->fsync_wr_callback().set(FUNC(mc1000_state::fs_w));
	m_vdg->input_callback().set(FUNC(mc1000_state::videoram_r));
	m_vdg->set_screen(SCREEN_TAG);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	ay8910_device &ay8910(AY8910(config, AY8910_TAG, 3579545/2));
	ay8910.set_flags(AY8910_SINGLE_OUTPUT);
	ay8910.set_resistors_load(RES_K(2.2), 0, 0);
	ay8910.port_b_read_callback().set(FUNC(mc1000_state::keydata_r));
	ay8910.port_a_write_callback().set(FUNC(mc1000_state::keylatch_w));
	ay8910.add_route(ALL_OUTPUTS, "mono", 0.25);

	/* devices */
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("mc1000_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("mc1000_cass");

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(mc1000_state::write_centronics_busy));

	output_latch_device &latch(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(latch);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("16K").set_extra_options("48K");
}

/* ROMs */

ROM_START( mc1000 )
	ROM_REGION( 0x4000, Z80_TAG, 0 )
	ROM_LOAD( "mc1000.ic17", 0x0000, 0x2000, CRC(8e78d80d) SHA1(9480270e67a5db2e7de8bc5c8b9e0bb210d4142b) )
	ROM_LOAD( "mc1000.ic12", 0x2000, 0x2000, CRC(750c95f0) SHA1(fd766f5ea4481ef7fd4df92cf7d8397cc2b5a6c4) )
ROM_END

} // Anonymous namespace


/* System Drivers */

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY  FULLNAME   FLAGS */
COMP( 1985, mc1000, 0,      0,      mc1000,  mc1000, mc1000_state, empty_init, "CCE",   "MC-1000", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )



mc1502.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************

    drivers/mc1502.cpp

    Driver file for Elektronika MS 1502

    To do:
    - fix video errors caused by 465caf8038a120b4c1ffad9df67a1dc7474e5bb1
      "cga: treat as fixed sync monitor (nw)"
    - debug video init in BIOS 7.2
    - pk88 (video, keyboard, etc.)

***************************************************************************/

#include "emu.h"
#include "kb_7007_3.h"

#include "bus/centronics/ctronics.h"
#include "bus/isa/isa.h"
#include "bus/isa/mc1502_fdc.h"
#include "bus/isa/xsu_cards.h"
#include "bus/rs232/rs232.h"
#include "cpu/i86/i86.h"
#include "imagedev/cassette.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/ram.h"
#include "sound/spkrdev.h"

#include "softlist_dev.h"
#include "speaker.h"


#define LOG_KEYBOARD  (1U << 1)
#define LOG_PPI       (1U << 2)

//#define VERBOSE (LOG_GENERAL | LOG_VRAM)
//#define LOG_OUTPUT_STREAM std::cout

#include "logmacro.h"

#define LOGKBD(...) LOGMASKED(LOG_KEYBOARD, __VA_ARGS__)
#define LOGPPI(...) LOGMASKED(LOG_PPI, __VA_ARGS__)


namespace {

class mc1502_state : public driver_device
{
public:
	mc1502_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_upd8251(*this, "upd8251")
		, m_pic8259(*this, "pic8259")
		, m_pit8253(*this, "pit8253")
		, m_ppi8255n1(*this, "ppi8255n1")
		, m_ppi8255n2(*this, "ppi8255n2")
		, m_isabus(*this, "isa")
		, m_speaker(*this, "speaker")
		, m_cassette(*this, "cassette")
		, m_centronics(*this, "centronics")
		, m_ram(*this, RAM_TAG)
		, m_kbdio(*this, "Y%u", 1)
	{ }

	void mc1502(machine_config &config);

	void init_mc1502();

	void fdc_config(device_t *device);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device>  m_maincpu;
	required_device<i8251_device> m_upd8251;
	required_device<pic8259_device>  m_pic8259;
	required_device<pit8253_device>  m_pit8253;
	required_device<i8255_device>  m_ppi8255n1;
	required_device<i8255_device>  m_ppi8255n2;
	required_device<isa8_device>  m_isabus;
	required_device<speaker_sound_device>  m_speaker;
	required_device<cassette_image_device>  m_cassette;
	required_device<centronics_device> m_centronics;
	required_device<ram_device> m_ram;
	required_ioport_array<12> m_kbdio;

	TIMER_CALLBACK_MEMBER(keyb_signal_callback);

	struct {
		uint8_t       pulsing = 0;
		uint16_t      mask = 0;       /* input lines */
		emu_timer   *keyb_signal_timer = nullptr;
	} m_kbd;

	uint8_t m_ppi_portb = 0;
	uint8_t m_ppi_portc = 0;
	uint8_t m_spkrdata = 0;

	void mc1502_pit8253_out1_changed(int state);
	void mc1502_pit8253_out2_changed(int state);
	void mc1502_speaker_set_spkrdata(int state);
	void mc1502_i8251_syndet(int state);

	void mc1502_ppi_portb_w(uint8_t data);
	void mc1502_ppi_portc_w(uint8_t data);
	uint8_t mc1502_ppi_portc_r();
	uint8_t mc1502_kppi_porta_r();
	void mc1502_kppi_portb_w(uint8_t data);
	void mc1502_kppi_portc_w(uint8_t data);

	void mc1502_io(address_map &map) ATTR_COLD;
	void mc1502_map(address_map &map) ATTR_COLD;

	int m_pit_out2 = 0;
};


/*
 * onboard devices:
 */

// Timer

/* check if any keys are pressed, raise IRQ1 if so */

TIMER_CALLBACK_MEMBER(mc1502_state::keyb_signal_callback)
{
	uint8_t key = 0;

	for (int i = 0; i < 12; i++)
	{
		key |= m_kbdio[i]->read();
	}

	LOGKBD("k_s_c = %02X (%d) %s\n", key, m_kbd.pulsing, (key || m_kbd.pulsing) ? " will IRQ" : "");

	/*
	   If a key is pressed and we're not pulsing yet, start pulsing the IRQ1;
	   keep pulsing while any key is pressed, and pulse one time after all keys
	   are released.
	 */
	if (key)
	{
		if (m_kbd.pulsing < 2)
		{
			m_kbd.pulsing += 2;
		}
	}

	if (m_kbd.pulsing)
	{
		m_pic8259->ir1_w(m_kbd.pulsing & 1);
		m_kbd.pulsing--;
	}
}

void mc1502_state::mc1502_ppi_portb_w(uint8_t data)
{
	m_ppi_portb = data;
	m_pit8253->write_gate2(BIT(data, 0));
	mc1502_speaker_set_spkrdata(BIT(data, 1));
	m_centronics->write_strobe(BIT(data, 2));
	m_centronics->write_autofd(BIT(data, 3));
	m_centronics->write_init(BIT(data, 4));
}

// bit 0: parallel port data transfer direction (default = 0 = out)
// bits 1-2: CGA_FONT (default = 01)
// bit 3: i8251 SYNDET pin triggers NMI (default = 1 = no)
void mc1502_state::mc1502_ppi_portc_w(uint8_t data)
{
	m_ppi_portc = data & 15;
}

// 0x80 -- serial RxD (not emulated)
// 0x40 -- CASS IN, also loops back T2OUT (gated by CASWR)
// 0x20 -- T2OUT
// 0x10 -- SNDOUT
uint8_t mc1502_state::mc1502_ppi_portc_r()
{
	int data = 0xff;
	double tap_val = m_cassette->input();

	data = (data & ~0x40) | (tap_val < 0 ? 0x40 : 0x00) | ((BIT(m_ppi_portb, 7) && m_pit_out2) ? 0x40 : 0x00);
	data = (data & ~0x20) | (m_pit_out2 ? 0x20 : 0x00);
	data = (data & ~0x10) | ((BIT(m_ppi_portb, 1) && m_pit_out2) ? 0x10 : 0x00);

	LOGPPI("mc1502_ppi_portc_r = %02X (tap_val %f t2out %d) at %s\n",
		data, tap_val, m_pit_out2, machine().describe_context());
	return data;
}

uint8_t mc1502_state::mc1502_kppi_porta_r()
{
	uint8_t key = 0;

	for (int i = 0; i < 12; i++)
	{
		if (BIT(m_kbd.mask, i))
		{
			key |= m_kbdio[i]->read();
		}
	}

	key ^= 0xff;
	LOGPPI("mc1502_kppi_porta_r = %02X\n", key);
	return key;
}

void mc1502_state::mc1502_kppi_portb_w(uint8_t data)
{
	m_kbd.mask &= ~255;
	m_kbd.mask |= data ^ 255;
	if (!BIT(data, 0))
		m_kbd.mask |= 1 << 11;
	else
		m_kbd.mask &= ~(1 << 11);
	LOGPPI("mc1502_kppi_portb_w ( %02X -> %04X )\n", data, m_kbd.mask);
}

void mc1502_state::mc1502_kppi_portc_w(uint8_t data)
{
	m_kbd.mask &= ~(7 << 8);
	m_kbd.mask |= ((data ^ 7) & 7) << 8;
	LOGPPI("mc1502_kppi_portc_w ( %02X -> %04X )\n", data, m_kbd.mask);
}

void mc1502_state::mc1502_i8251_syndet(int state)
{
	if (!BIT(m_ppi_portc, 3))
		m_maincpu->set_input_line(INPUT_LINE_NMI, state ? ASSERT_LINE : CLEAR_LINE);
}

void mc1502_state::mc1502_pit8253_out1_changed(int state)
{
	m_upd8251->write_txc(state);
	m_upd8251->write_rxc(state);
}

void mc1502_state::mc1502_pit8253_out2_changed(int state)
{
	m_pit_out2 = state;
	m_speaker->level_w(m_spkrdata & m_pit_out2);
	m_cassette->output(state ? 1 : -1);
}

void mc1502_state::mc1502_speaker_set_spkrdata(int state)
{
	m_spkrdata = state ? 1 : 0;
	m_speaker->level_w(m_spkrdata & m_pit_out2);
}

void mc1502_state::init_mc1502()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	program.install_ram(0, m_ram->size() - 1, m_ram->pointer());
}

void mc1502_state::machine_start()
{
	/*
	       Keyboard polling circuit holds IRQ1 high until a key is
	       pressed, then it starts a timer that pulses IRQ1 low each
	       40ms (check) for 20ms (check) until all keys are released.
	       Last pulse causes BIOS to write a 'break' scancode into port 60h.
	 */
	m_pic8259->ir1_w(1);
	m_kbd.pulsing = 0;
	m_kbd.mask = 0;
	m_kbd.keyb_signal_timer = timer_alloc(FUNC(mc1502_state::keyb_signal_callback), this);
	m_kbd.keyb_signal_timer->adjust(attotime::from_msec(20), 0, attotime::from_msec(20));
}

void mc1502_state::machine_reset()
{
	m_spkrdata = 0;
	m_pit_out2 = 1;
	m_ppi_portb = 0;
	m_ppi_portc = 0;
	m_speaker->level_w(0);
}

/*
 * macros
 */

void mc1502_state::fdc_config(device_t *device)
{
	mc1502_fdc_device &fdc = *downcast<mc1502_fdc_device *>(device);
	fdc.set_cpu(m_maincpu);
}

void mc1502_state::mc1502_map(address_map &map)
{
	map.unmap_value_high();
	map(0xf0000, 0xfffff).rom().region("bios", 0);
}

void mc1502_state::mc1502_io(address_map &map)
{
	map(0x0020, 0x0021).rw(m_pic8259, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x0028, 0x0029).rw(m_upd8251, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x0040, 0x0043).rw(m_pit8253, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x0060, 0x0063).rw(m_ppi8255n1, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x0068, 0x006B).rw(m_ppi8255n2, FUNC(i8255_device::read), FUNC(i8255_device::write)); // keyboard poll
}

static INPUT_PORTS_START( mc1502 )
	PORT_INCLUDE( mc7007_3_keyboard )
INPUT_PORTS_END

void mc1502_state::mc1502(machine_config &config)
{
	I8088(config, m_maincpu, XTAL(16'000'000) / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &mc1502_state::mc1502_map);
	m_maincpu->set_addrmap(AS_IO, &mc1502_state::mc1502_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259", FUNC(pic8259_device::inta_cb));

	PIT8253(config, m_pit8253);
	m_pit8253->set_clk<0>(XTAL(16'000'000) / 12); /* heartbeat IRQ */
	m_pit8253->out_handler<0>().set(m_pic8259, FUNC(pic8259_device::ir0_w));
	m_pit8253->set_clk<1>(XTAL(16'000'000) / 12); /* serial port */
	m_pit8253->out_handler<1>().set(FUNC(mc1502_state::mc1502_pit8253_out1_changed));
	m_pit8253->set_clk<2>(XTAL(16'000'000) / 12); /* pio port c pin 4, and speaker polling enough */
	m_pit8253->out_handler<2>().set(FUNC(mc1502_state::mc1502_pit8253_out2_changed));

	PIC8259(config, m_pic8259);
	m_pic8259->out_int_callback().set_inputline(m_maincpu, 0);

	I8255(config, m_ppi8255n1);
	m_ppi8255n1->out_pa_callback().set("cent_data_out", FUNC(output_latch_device::write));
	m_ppi8255n1->out_pb_callback().set(FUNC(mc1502_state::mc1502_ppi_portb_w));
	m_ppi8255n1->in_pc_callback().set(FUNC(mc1502_state::mc1502_ppi_portc_r));
	m_ppi8255n1->out_pc_callback().set(FUNC(mc1502_state::mc1502_ppi_portc_w));

	I8255(config, m_ppi8255n2);
	m_ppi8255n2->in_pa_callback().set(FUNC(mc1502_state::mc1502_kppi_porta_r));
	m_ppi8255n2->out_pb_callback().set(FUNC(mc1502_state::mc1502_kppi_portb_w));
	m_ppi8255n2->in_pc_callback().set("cent_status_in", FUNC(input_buffer_device::read));
	m_ppi8255n2->out_pc_callback().set(FUNC(mc1502_state::mc1502_kppi_portc_w));

	I8251(config, m_upd8251, 0);
	m_upd8251->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_upd8251->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	m_upd8251->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_upd8251->rxrdy_handler().set(m_pic8259, FUNC(pic8259_device::ir7_w)); /* default handler does nothing */
	m_upd8251->txrdy_handler().set(m_pic8259, FUNC(pic8259_device::ir7_w));
	m_upd8251->syndet_handler().set(FUNC(mc1502_state::mc1502_i8251_syndet));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_upd8251, FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set(m_upd8251, FUNC(i8251_device::write_dsr));
	rs232.cts_handler().set(m_upd8251, FUNC(i8251_device::write_cts));

	isa8_device &isa(ISA8(config, "isa", 0));
	isa.set_memspace("maincpu", AS_PROGRAM);
	isa.set_iospace("maincpu", AS_IO);
	isa.irq2_callback().set(m_pic8259, FUNC(pic8259_device::ir2_w));
	isa.irq3_callback().set(m_pic8259, FUNC(pic8259_device::ir3_w));
	isa.irq4_callback().set(m_pic8259, FUNC(pic8259_device::ir4_w));
	isa.irq5_callback().set(m_pic8259, FUNC(pic8259_device::ir5_w));
	isa.irq6_callback().set(m_pic8259, FUNC(pic8259_device::ir6_w));
	isa.irq7_callback().set(m_pic8259, FUNC(pic8259_device::ir7_w));
	isa.iochrdy_callback().set_inputline(m_maincpu, INPUT_LINE_HALT);

	ISA8_SLOT(config, "board0", 0, "isa", mc1502_isa8_cards, "cga_mc1502", true); // FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa1", 0, "isa", mc1502_isa8_cards, "fdc", false).set_option_machine_config("fdc", [this](device_t *device) { fdc_config(device); });
	ISA8_SLOT(config, "isa2", 0, "isa", mc1502_isa8_cards, "rom", false).set_option_machine_config("fdc", [this](device_t* device) { fdc_config(device); });

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.80);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set("cent_status_in", FUNC(input_buffer_device::write_bit6));
	m_centronics->busy_handler().set("cent_status_in", FUNC(input_buffer_device::write_bit7));
	m_centronics->fault_handler().set("cent_status_in", FUNC(input_buffer_device::write_bit4));
	m_centronics->perror_handler().set("cent_status_in", FUNC(input_buffer_device::write_bit5));

	INPUT_BUFFER(config, "cent_status_in");

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	SOFTWARE_LIST(config, "flop_list").set_original("mc1502_flop");
//  SOFTWARE_LIST(config, "cass_list").set_original("mc1502_cass");

	RAM(config, RAM_TAG).set_default_size("608K").set_extra_options("96K"); /* 96 base + 512 on expansion card */
}


/*
        Apparently there was a hardware revision with built-in floppy
        controller mapped to alternate set of ports; v531 and v533
        support this revision. v533 is possibly not an original BIOS, it
        supports autoboot which none of others do. v521h is a version
        with support for 3rd party hard disk controller (not emulated).
        v51 is designed for a different keyboard layout (JCUKEN, not
        QWERTY).
*/
ROM_START( mc1502 )
	ROM_REGION(0x10000,"bios", 0)

	ROM_DEFAULT_BIOS("v52")
	ROM_SYSTEM_BIOS(0, "v50", "v5.0 10/05/89")
	ROMX_LOAD("monitor_5_0.rom",  0xc000, 0x4000, CRC(9e97c6a0) SHA1(16a304e8de69ec4d8b92acda6bf28454c361a24f),ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v52", "v5.2 22/03/91")
	ROMX_LOAD("monitor_5_2.rom",  0xc000, 0x4000, CRC(0e65491e) SHA1(8a4d556473b5e0e59b05fab77c79c29f4d562412),ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v521", "v5.21 12/10/92")
	ROMX_LOAD("monitor_5_21.rom", 0xc000, 0x4000, CRC(28c8f653) SHA1(04b0b09e0b86d9648a83352cc1590eb8963833e0),ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v531", "v5.31 12/10/92")
	ROMX_LOAD("monitor_5_31.rom", 0xc000, 0x4000, CRC(a48295d5) SHA1(6f38977c22f9cc6c2bc6f6e53edc4048ca6b6721),ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "v533", "v5.33 01/08/93")
	ROMX_LOAD("0,cbc0.bin", 0xc000, 0x2000, CRC(9a55bc4f) SHA1(81da44eec2e52cf04b1fc7053502270f51270590),ROM_BIOS(4))
	ROMX_LOAD("1,dfe2.bin", 0xe000, 0x2000, CRC(8dec077a) SHA1(d6f6d7cc2183abc77fbd9cd59132de5766f7c458),ROM_BIOS(4))

	// 5.21 + 3rd party HDC support. fails checksum test so marked BAD_DUMP.
	ROM_SYSTEM_BIOS(5, "v521h", "v5.21h 22/09/93")
	ROMX_LOAD("mshbios0.pgm", 0xc000, 0x2000, BAD_DUMP CRC(be447261) SHA1(b93c597c17dfa4b678f72c20a3f7119b73e6ba1c),ROM_BIOS(5))
	ROMX_LOAD("mshbios1.pgm", 0xe000, 0x2000, BAD_DUMP CRC(89e2eaf2) SHA1(37d6b225b5e35574fdac81219589407d925225be),ROM_BIOS(5))

	// 5.3
	ROM_SYSTEM_BIOS(6, "v53", "v5.3 10/11/91")
	ROMX_LOAD("1502-3b0.pgm", 0xc000, 0x2000, CRC(dc148763) SHA1(7a5e66438007b2de328ac680614f9c4ff60f6a75),ROM_BIOS(6))
	ROMX_LOAD("1502-3b1.pgm", 0xe000, 0x2000, CRC(17fc2af2) SHA1(a060d7b7302dfa639025f025106b50412cf26953),ROM_BIOS(6))
	// 5.1 -- JCUKEN keyboard
	ROM_SYSTEM_BIOS(7, "v51", "v5.1 10/12/90")
	ROMX_LOAD("ms1502b0.pgm", 0xc000, 0x2000, CRC(92fcc29a) SHA1(930a4cffcd6ec6110dd9a18bd389b78f0ccb110a),ROM_BIOS(7))
	ROMX_LOAD("ms1502b1.pgm", 0xe000, 0x2000, CRC(fe355a58) SHA1(b4ef7775045c6f2095e2b487fe19824986a4892c),ROM_BIOS(7))
	// 5.31
	ROM_SYSTEM_BIOS(8, "v531_93", "v5.31 21/01/93")
	ROMX_LOAD("ms531b0.pgm", 0xc000, 0x2000, CRC(d97157d1) SHA1(cb1a1e0e2d9a0fcc78f9b09bfb4814d408ee4fae),ROM_BIOS(8))
	ROMX_LOAD("ms531b1.pgm", 0xe000, 0x2000, CRC(b1368e1a) SHA1(286496d25dc0ac2d8fe1802caffc6c37b236d105),ROM_BIOS(8))
	// 5.2
	ROM_SYSTEM_BIOS(9, "v52_91", "v5.2 10/11/91")
	ROMX_LOAD("msv5-2b0.pgm", 0xc000, 0x2000, CRC(f7f370e9) SHA1(e069a35005581a02856853b57dd511ab8e10054b),ROM_BIOS(9))
	ROMX_LOAD("msv5-2b1.pgm", 0xe000, 0x2000, CRC(d50e1c43) SHA1(22724dec0052ee9e52f44f5914f2f5f3fae14612),ROM_BIOS(9))

	// 7.2
	ROM_SYSTEM_BIOS(10, "v72", "v7.2 01/21/96")
	ROMX_LOAD("7.2_1.bin", 0xe000, 0x2000, CRC(80912ad4) SHA1(cc54b77b2db4cc5d614efafd04367d2f06400fc8),ROM_BIOS(10))

	ROM_REGION(0x2000,"gfx1", ROMREGION_ERASE00)
	ROM_LOAD("symgen.rom", 0x0000, 0x2000, CRC(b2747a52) SHA1(6766d275467672436e91ac2997ac6b77700eba1e))
ROM_END

/*
        Predecessor of MC1502, same keyboard attachment but
        different video subsystem (not emulated).
*/
ROM_START( pk88 )
	ROM_REGION(0x10000, "bios", 0)

	// datecode 07.23.87
	ROM_LOAD( "b0.064", 0x0000, 0x2000, CRC(80d3cf5d) SHA1(64769b7a8b60ffeefa04e4afbec778069a2840c9))
	ROM_LOAD( "b1.064", 0x2000, 0x2000, CRC(673a4acc) SHA1(082ae803994048e225150f771794ca305f73d731))
	ROM_LOAD( "b2.064", 0x4000, 0x2000, CRC(1ee66152) SHA1(7ed8c4c6c582487e802beabeca5b86702e5083e8))
	ROM_LOAD( "b3.064", 0x6000, 0x2000, CRC(3062b3fc) SHA1(5134dd64721cbf093d059ee5d3fd09c7f86604c7))
	ROM_LOAD( "pk88-0.064", 0xc000, 0x2000, CRC(1e4666cf) SHA1(6364c5241f2792909ff318194161eb2c29737546))
	ROM_LOAD( "pk88-1.064", 0xe000, 0x2000, CRC(6fa7e7ef) SHA1(d68bc273baa46ba733ac6ad4df7569dd70cf60dd))
ROM_END

} // anonymous namespace


/***************************************************************************

  Game driver(s)

***************************************************************************/

//     YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT    CLASS         INIT         COMPANY               FULLNAME               FLAGS
COMP ( 1989, mc1502, 0,      0,      mc1502,  mc1502,  mc1502_state, init_mc1502, "NPO Microprocessor", "Elektronika MS 1502", MACHINE_IMPERFECT_GRAPHICS )
COMP ( 1988, pk88,   0,      0,      mc1502,  mc1502,  mc1502_state, init_mc1502, "NPO Microprocessor", "Elektronika PK-88",   MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



mc400.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/******************************************************************************

    Psion MC400/MC200 Series

******************************************************************************/

#include "emu.h"

#include "bus/psion/module/slot.h"
#include "cpu/i86/i86.h"
#include "machine/nvram.h"
#include "machine/psion_asic1.h"
#include "machine/psion_asic2.h"
#include "machine/psion_asic3.h"
#include "machine/psion_ssd.h"
#include "machine/ram.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class psionmc_state : public driver_device
{
public:
	psionmc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_asic1(*this, "asic1")
		, m_asic2(*this, "asic2")
		, m_asic3(*this, "asic3")
		, m_ram(*this, "ram")
		, m_nvram(*this, "nvram")
		, m_palette(*this, "palette")
		, m_keyboard(*this, "COL%u", 0U)
		, m_speaker(*this, "speaker")
		, m_ssd(*this, "ssd%u", 1U)
		, m_exp(*this, "exp%u", 1U)
		, m_penup_timer(nullptr)
	{ }

	void psionmc(machine_config &config);
	void mc200(machine_config &config);
	void mc400(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(key_on);
	//DECLARE_INPUT_CHANGED_MEMBER(reset);
	DECLARE_INPUT_CHANGED_MEMBER(digitiser_changed);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	optional_device<psion_asic1_device> m_asic1;
	required_device<psion_asic2_device> m_asic2;
	required_device<psion_asic3_device> m_asic3;
	required_device<ram_device> m_ram;
	required_device<nvram_device> m_nvram;
	required_device<palette_device> m_palette;
	required_ioport_array<10> m_keyboard;
	required_device<speaker_sound_device> m_speaker;
	required_device_array<psion_ssd_device, 4> m_ssd;
	required_device_array<psion_module_slot_device, 2> m_exp;

	emu_timer *m_penup_timer;

	TIMER_CALLBACK_MEMBER(digitiser_penup);

	void palette_init(palette_device &palette);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void asic1_map(address_map &map) ATTR_COLD;

	int m_dr = 0;
	uint16_t m_digitiser[2] = { 0, 0 };
};


void psionmc_state::machine_start()
{
	m_asic1->space(0).install_ram(0, m_ram->mask(), m_ram->pointer());
	m_nvram->set_base(m_ram->pointer(), m_ram->size());

	m_penup_timer = timer_alloc(FUNC(psionmc_state::digitiser_penup), this);
	m_penup_timer->adjust(attotime::never);
}


void psionmc_state::mem_map(address_map &map)
{
	map(0x00000, 0xfffff).rw(m_asic1, FUNC(psion_asic1_device::mem_r), FUNC(psion_asic1_device::mem_w));
}

void psionmc_state::io_map(address_map &map)
{
	map(0x0000, 0x001f).rw(m_asic1, FUNC(psion_asic1_device::io_r), FUNC(psion_asic1_device::io_w));
	map(0x0080, 0x008f).rw(m_asic2, FUNC(psion_asic2_device::io_r), FUNC(psion_asic2_device::io_w)).umask16(0x00ff);
	map(0x0100, 0x01ff).rw(m_exp[0], FUNC(psion_module_slot_device::io_r), FUNC(psion_module_slot_device::io_w));
	map(0x0200, 0x02ff).rw(m_exp[1], FUNC(psion_module_slot_device::io_r), FUNC(psion_module_slot_device::io_w));
}

void psionmc_state::asic1_map(address_map &map)
{
	map(0x00000, 0xb7fff).noprw();
	map(0xb8000, 0xbffff).ram(); // 32K video RAM
	map(0xc0000, 0xfffff).rom().region("flash", 0);
}


static INPUT_PORTS_START( psionmc )
	PORT_START("COL0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))  PORT_NAME("Control")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)            PORT_NAME("Shift (L)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT)       PORT_CHAR(UCHAR_MAMEKEY(LALT))      PORT_NAME("Psion")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK)   PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))  PORT_NAME("Caps Lock")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)            PORT_NAME("Shift (R)")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))      PORT_NAME(u8"\u2190") // U+2190 = ←
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))      PORT_NAME(u8"\u2193") // U+2193 = ↓

	PORT_START("COL1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)          PORT_CHAR('1')  PORT_CHAR('!')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)          PORT_CHAR('2')  PORT_CHAR('"')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q')  PORT_CHAR('Q')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)          PORT_CHAR('w')  PORT_CHAR('W')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('a')  PORT_CHAR('A')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)          PORT_CHAR('s')  PORT_CHAR('S')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z')  PORT_CHAR('Z')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)          PORT_CHAR('x')  PORT_CHAR('X')

	PORT_START("COL2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)          PORT_CHAR('3')  PORT_CHAR(0xa3)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)          PORT_CHAR('4')  PORT_CHAR('$')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)          PORT_CHAR('e')  PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)          PORT_CHAR('r')  PORT_CHAR('R')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)          PORT_CHAR('d')  PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)          PORT_CHAR('f')  PORT_CHAR('F')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)          PORT_CHAR('c')  PORT_CHAR('C')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)          PORT_CHAR('v')  PORT_CHAR('V')

	PORT_START("COL3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)          PORT_CHAR('5')  PORT_CHAR('%')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_CHAR('6')  PORT_CHAR('^')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)          PORT_CHAR('t')  PORT_CHAR('T')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y')  PORT_CHAR('Y')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)          PORT_CHAR('g')  PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)          PORT_CHAR('h')  PORT_CHAR('H')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)          PORT_CHAR('b')  PORT_CHAR('B')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)          PORT_CHAR('n')  PORT_CHAR('N')

	PORT_START("COL4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7')  PORT_CHAR('&')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8')  PORT_CHAR('*')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)          PORT_CHAR('u')  PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)          PORT_CHAR('i')  PORT_CHAR('I')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)          PORT_CHAR('j')  PORT_CHAR('J')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)          PORT_CHAR('k')  PORT_CHAR('K')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)          PORT_CHAR('m')  PORT_CHAR('M')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')  PORT_CHAR('<')

	PORT_START("COL5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_CHAR('9')  PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_CHAR('0')  PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)          PORT_CHAR('o')  PORT_CHAR('O')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)          PORT_CHAR('p')  PORT_CHAR('P')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)          PORT_CHAR('l')  PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';')  PORT_CHAR(':')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')  PORT_CHAR('>')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/')  PORT_CHAR('?')

	PORT_START("COL6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-')  PORT_CHAR('_')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('=')  PORT_CHAR('+')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[')  PORT_CHAR('{')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')  PORT_CHAR('}')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR('\'') PORT_CHAR('@')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('#')  PORT_CHAR('~')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))     PORT_NAME(u8"\u2191") // U+2191 = ↑
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)                    PORT_NAME("Enter")

	PORT_START("COL7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(0x08)                  PORT_NAME("Backspace")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)        PORT_CHAR(0x09)                  PORT_NAME("Tab")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')                   PORT_NAME("Space")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))  PORT_NAME(u8"\u2192") // U+2192 = →

	PORT_START("COL8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT)     PORT_CHAR(UCHAR_MAMEKEY(INSERT)) PORT_NAME("Task")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME)       PORT_CHAR(UCHAR_MAMEKEY(HOME))   PORT_NAME("Home")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP)       PORT_CHAR(UCHAR_MAMEKEY(PGUP))   PORT_NAME("Page Up")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                                                PORT_NAME("LCD")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON1)  PORT_CODE(MOUSECODE_BUTTON1)                                   PORT_NAME("Touchpad Button")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)        PORT_CHAR(UCHAR_MAMEKEY(DEL))    PORT_NAME("Delete")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_END)        PORT_CHAR(UCHAR_MAMEKEY(END))    PORT_NAME("End")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN)       PORT_CHAR(UCHAR_MAMEKEY(PGDN))   PORT_NAME("Page Down")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                                                PORT_NAME("Record")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("ESC")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC)) PORT_NAME("On Esc") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psionmc_state::key_on), 0)

	PORT_START("DIGIT0")
	PORT_BIT(0x7ff, 0x300, IPT_AD_STICK_X) PORT_NAME("Touchpad X-Axis") PORT_MINMAX(0x0c0, 0x7c0) PORT_SENSITIVITY(20) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psionmc_state::digitiser_changed), 0)

	PORT_START("DIGIT1")
	PORT_BIT(0x7ff, 0x300, IPT_AD_STICK_Y) PORT_NAME("Touchpad Y-Axis") PORT_MINMAX(0x100, 0x640) PORT_SENSITIVITY(20) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psionmc_state::digitiser_changed), 1) PORT_REVERSE

	//PORT_START("RESET")
	//PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_NAME("Reset") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psionmc_state::reset), 0)
INPUT_PORTS_END

static INPUT_PORTS_START(psionmc_de)
	PORT_INCLUDE(psionmc)

	PORT_MODIFY("COL1")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q')  PORT_CHAR('Q')  PORT_CHAR('@')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_CHAR('y')  PORT_CHAR('Y')

	PORT_MODIFY("COL2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)          PORT_CHAR('3')  PORT_CHAR(0xa7)

	PORT_MODIFY("COL3")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_CHAR('6')  PORT_CHAR('&')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('z')  PORT_CHAR('Z')

	PORT_MODIFY("COL4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7')  PORT_CHAR('/')  PORT_CHAR('{')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8')  PORT_CHAR('(')  PORT_CHAR('[')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')  PORT_CHAR(';')

	PORT_MODIFY("COL5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_CHAR('9')  PORT_CHAR(')')  PORT_CHAR(']')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_CHAR('0')  PORT_CHAR('=')  PORT_CHAR('}')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(0xf6) PORT_CHAR(0xd6)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')  PORT_CHAR(':')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('-')  PORT_CHAR('_')

	PORT_MODIFY("COL6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR(0xdf) PORT_CHAR('?')  PORT_CHAR('\\')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('\'') PORT_CHAR('`')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR(0xfc) PORT_CHAR(0xdc)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('+')  PORT_CHAR('*')  PORT_CHAR('~')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(0xe4) PORT_CHAR(0xc4)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('<')  PORT_CHAR('>')  PORT_CHAR('|')

	PORT_MODIFY("COL7")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('#')  PORT_CHAR('^')
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(psionmc_state::key_on)
{
	if (newval)
	{
		m_asic2->on_clr_w(newval);
	}
}

//INPUT_CHANGED_MEMBER(psionmc_state::reset)
//{
//  if (newval)
//  {
//      m_asic2->reset_w(0);
//  }
//}

INPUT_CHANGED_MEMBER(psionmc_state::digitiser_changed)
{
	m_digitiser[param] = newval;

	m_penup_timer->adjust(attotime::from_msec(200), 0);
}

TIMER_CALLBACK_MEMBER(psionmc_state::digitiser_penup)
{
	m_digitiser[0] |= 0x800;
	m_digitiser[1] |= 0x800;
}


void psionmc_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(131, 136, 139));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}


void psionmc_state::psionmc(machine_config &config)
{
	I8086(config, m_maincpu, 15.36_MHz_XTAL / 2); // M80C86A
	m_maincpu->set_addrmap(AS_PROGRAM, &psionmc_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &psionmc_state::io_map);
	m_maincpu->set_irq_acknowledge_callback(m_asic1, FUNC(psion_asic1_device::inta_cb));

	PSION_ASIC1(config, m_asic1, 15.36_MHz_XTAL);
	m_asic1->set_screen("screen");
	m_asic1->set_laptop_mode(true);
	m_asic1->set_addrmap(0, &psionmc_state::asic1_map);
	m_asic1->int_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_asic1->nmi_cb().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_asic1->frcovl_cb().set(m_asic2, FUNC(psion_asic2_device::frcovl_w));

	PSION_ASIC2(config, m_asic2, 15.36_MHz_XTAL);
	m_asic2->int_cb().set(m_asic1, FUNC(psion_asic1_device::eint3_w));
	m_asic2->nmi_cb().set(m_asic1, FUNC(psion_asic1_device::enmi_w));
	m_asic2->cbusy_cb().set_inputline(m_maincpu, INPUT_LINE_TEST);
	m_asic2->buz_cb().set(m_speaker, FUNC(speaker_sound_device::level_w));
	m_asic2->buzvol_cb().set([this](int state) { m_speaker->set_output_gain(ALL_OUTPUTS, state ? 1.0 : 0.25); });
	m_asic2->dr_cb().set([this](int state) { m_dr = state; });
	m_asic2->col_cb().set([this](uint8_t data) { return m_keyboard[data]->read(); });
	m_asic2->data_r<0>().set(m_asic3, FUNC(psion_asic3_device::data_r));        // Power supply (ASIC3)
	m_asic2->data_w<0>().set(m_asic3, FUNC(psion_asic3_device::data_w));
	m_asic2->data_r<1>().set(m_ssd[0], FUNC(psion_ssd_device::data_r));         // SSD Pack 1
	m_asic2->data_w<1>().set(m_ssd[0], FUNC(psion_ssd_device::data_w));
	m_asic2->data_r<2>().set(m_ssd[1], FUNC(psion_ssd_device::data_r));         // SSD Pack 2
	m_asic2->data_w<2>().set(m_ssd[1], FUNC(psion_ssd_device::data_w));
	m_asic2->data_r<3>().set(m_ssd[2], FUNC(psion_ssd_device::data_r));         // SSD Pack 3
	m_asic2->data_w<3>().set(m_ssd[2], FUNC(psion_ssd_device::data_w));
	m_asic2->data_r<4>().set(m_ssd[3], FUNC(psion_ssd_device::data_r));         // SSD Pack 4
	m_asic2->data_w<4>().set(m_ssd[3], FUNC(psion_ssd_device::data_w));
	m_asic2->data_r<5>().set(m_exp[1], FUNC(psion_module_slot_device::data_r)); // Expansion port B
	m_asic2->data_w<5>().set(m_exp[1], FUNC(psion_module_slot_device::data_w));
	m_asic2->data_r<6>().set(m_exp[0], FUNC(psion_module_slot_device::data_r)); // Expansion port A
	m_asic2->data_w<6>().set(m_exp[0], FUNC(psion_module_slot_device::data_w));
	//m_asic2->data_r<7>().set(m_asic5, FUNC(psion_asic5_device::data_r));        // High speed link
	//m_asic2->data_w<7>().set(m_asic5, FUNC(psion_asic5_device::data_w));

	PSION_PSU_ASIC5(config, m_asic3);
	m_asic3->adin_cb().set([this]() { return m_digitiser[m_dr]; });

	RAM(config, m_ram);
	NVRAM(config, "nvram", nvram_device::DEFAULT_NONE);

	PALETTE(config, "palette", FUNC(psionmc_state::palette_init), 2);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00); // Piezo buzzer

	PSION_SSD(config, m_ssd[0]);
	m_ssd[0]->door_cb().set(m_asic2, FUNC(psion_asic2_device::dnmi_w));
	PSION_SSD(config, m_ssd[1]);
	m_ssd[1]->door_cb().set(m_asic2, FUNC(psion_asic2_device::dnmi_w));
	PSION_SSD(config, m_ssd[2]);
	m_ssd[2]->door_cb().set(m_asic2, FUNC(psion_asic2_device::dnmi_w));
	PSION_SSD(config, m_ssd[3]);
	m_ssd[3]->door_cb().set(m_asic2, FUNC(psion_asic2_device::dnmi_w));

	PSION_MODULE_SLOT(config, m_exp[0], psion_mcmodule_devices, "serpar"); // RS232/Parallel
	m_exp[0]->intr_cb().set(m_asic1, FUNC(psion_asic1_device::eint2_w));
	PSION_MODULE_SLOT(config, m_exp[1], psion_mcmodule_devices, nullptr);
	m_exp[1]->intr_cb().set(m_asic1, FUNC(psion_asic1_device::eint1_w));

	SOFTWARE_LIST(config, "ssd_list").set_original("psion_ssd").set_filter("MC");
}

void psionmc_state::mc200(machine_config &config)
{
	psionmc(config);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_size(640, 200);
	screen.set_visarea_full();
	screen.set_refresh_hz(66);
	screen.set_screen_update(m_asic1, FUNC(psion_asic1_device::screen_update_single));
	screen.set_palette(m_palette);

	m_ram->set_default_size("128K");
}

void psionmc_state::mc400(machine_config &config)
{
	psionmc(config);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_size(640, 400);
	screen.set_visarea_full();
	screen.set_refresh_hz(66);
	screen.set_screen_update(m_asic1, FUNC(psion_asic1_device::screen_update_dual));
	screen.set_palette(m_palette);

	m_ram->set_default_size("256K");
}


ROM_START( mc200 )
	ROM_REGION16_LE(0x40000, "flash", ROMREGION_ERASEFF)
	// 2 x 28F010 128k flash chips, V2.20F also known to exist
	ROM_SYSTEM_BIOS(0, "212f", "V2.12F 081090")
	ROMX_LOAD("v212f_0.bin", 0x00000, 0x20000, CRC(ff346271) SHA1(50a01bbd653bbdffb59e37753a3ddd8c1f677ca5), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("v212f_1.bin", 0x00001, 0x20000, CRC(4f266410) SHA1(3d31b36daa239b0b80ec74855ecfa59fb05c9aaa), ROM_BIOS(0) | ROM_SKIP(1))

	ROM_REGION(0x20000, "ssd3", 0)
	ROM_LOAD("mc200_system_disk.bin", 0x00000, 0x20000, CRC(8d7dab78) SHA1(a4ae14bfb30525033e681558e0aa5e4c5b21c62e))
ROM_END

ROM_START( mc400 )
	ROM_REGION16_LE(0x40000, "flash", ROMREGION_ERASEFF)
	// 2 x 28F010 128k flash chips
	ROM_SYSTEM_BIOS(0, "212f", "V2.12F 081090")
	ROMX_LOAD("v212f_0.bin", 0x00000, 0x20000, CRC(ff346271) SHA1(50a01bbd653bbdffb59e37753a3ddd8c1f677ca5), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("v212f_1.bin", 0x00001, 0x20000, CRC(4f266410) SHA1(3d31b36daa239b0b80ec74855ecfa59fb05c9aaa), ROM_BIOS(0) | ROM_SKIP(1))
	ROM_SYSTEM_BIOS(1, "126f", "V1.26F 020290")
	ROMX_LOAD("v126f_0.bin", 0x00000, 0x20000, CRC(2c569929) SHA1(8a89a3aa1811e3e41da0e3849c9d98ad61362b33), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("v126f_1.bin", 0x00001, 0x20000, CRC(e5a63fae) SHA1(dd630d5905601217dfbae183d3df621f98a82fd3), ROM_BIOS(1) | ROM_SKIP(1))

	// Psion test ROM master images
	ROM_SYSTEM_BIOS(2, "110ftst", "V1.10F Serial/Parallel Test")
	ROMX_LOAD("v110ftst.bin", 0x00000, 0x40000, CRC(178e8a7e) SHA1(a6e3e14e63c83473292eea87526c66873930b275), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "101ftst", "V1.01F Serial/Parallel Test")
	ROMX_LOAD("v101ftst.bin", 0x00000, 0x40000, CRC(8b8ec6b2) SHA1(fc9f914322bd1b1dbe60e84f317549fda9b80aad), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "062atst", "V0.62A/1 MC Final Test")
	ROMX_LOAD("v062atst.bin", 0x00000, 0x40000, CRC(72e026a5) SHA1(e7cbdecdad0574bd4804393189c8c768e934b6f5), ROM_BIOS(4))

	ROM_REGION(0x20000, "ssd3", 0)
	ROM_LOAD("mc400_system_disk.bin", 0x00000, 0x20000, CRC(2ee09e83) SHA1(2ba5dcdb07986f53ac97ca2e397ded0fc839a70a))
ROM_END

ROM_START( mcword )
	ROM_REGION16_LE(0x40000, "flash", ROMREGION_ERASEFF)
	// 2 x 28F010 128k flash chips
	ROM_SYSTEM_BIOS(0, "260f", "V2.60F/ENG 060892")
	ROMX_LOAD("v260f_0.bin", 0x00000, 0x20000, CRC(b4f2e560) SHA1(18bed2f8dfde458953eca2d0c21c4a94ba83bdf1), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("v260f_1.bin", 0x00001, 0x20000, CRC(abe679da) SHA1(751d9efd44c4bedaad77ef2ef8a16605eb88787c), ROM_BIOS(0) | ROM_SKIP(1))

	ROM_REGION(0x40000, "ssd3", 0)
	ROM_LOAD("mcword_system_disk.bin", 0, 0x40000, CRC(c7f8ad21) SHA1(5bc99c5430ad631dd094a0f99c2963a8c32b0556))
ROM_END

ROM_START( mcword_de )
	ROM_REGION16_LE(0x40000, "flash", ROMREGION_ERASEFF)
	// 2 x 28F010 128k flash chips
	ROM_SYSTEM_BIOS(0, "260f", "V2.60F/ENG 260892")
	ROMX_LOAD("v260f_0_de.bin", 0x00000, 0x20000, CRC(5a92ee9a) SHA1(b84de4c30c78aa6cb9f453ebbe54bf53ae00a842), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("v260f_1_de.bin", 0x00001, 0x20000, CRC(088bc86d) SHA1(9aa665e4e6bc111ffb4bd24c0eeb7da91962e902), ROM_BIOS(0) | ROM_SKIP(1))

	ROM_REGION(0x40000, "ssd3", 0)
	ROM_LOAD("mcword_system_disk.bin", 0x00000, 0x40000, CRC(c7f8ad21) SHA1(5bc99c5430ad631dd094a0f99c2963a8c32b0556))
ROM_END

} // anonymous namespace


//    YEAR  NAME        PARENT     COMPAT  MACHINE    INPUT        CLASS           INIT         COMPANY   FULLNAME             FLAGS
COMP( 1989, mc200,      mc400,     0,      mc200,     psionmc,     psionmc_state,  empty_init,  "Psion",  "MC 200",            MACHINE_SUPPORTS_SAVE )
COMP( 1989, mc400,      0,         0,      mc400,     psionmc,     psionmc_state,  empty_init,  "Psion",  "MC 400",            MACHINE_SUPPORTS_SAVE )
COMP( 1992, mcword,     mc400,     0,      mc400,     psionmc,     psionmc_state,  empty_init,  "Psion",  "MC Word",           MACHINE_SUPPORTS_SAVE )
COMP( 1992, mcword_de,  mc400,     0,      mc400,     psionmc_de,  psionmc_state,  empty_init,  "Psion",  "MC Word (German)",  MACHINE_SUPPORTS_SAVE )



mc68000.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/****************************************************************************

    mc-68000-Computer

    Hardware:
    - MC68000
    - 16 MHz XTAL
    - 128k or 512k RAM
    - 2x 6522 VIA
    - 6845 CRTC
    - 8 expansion slots

    TODO:
    - Cassette
    - Color/brightness levels
    - Sound
    - Joysticks

    Notes:
    - This computer was described in the "mc" magazine by Franzis Verlag.
      You could build it yourself or order it already assembled.
    - Press ESC at the boot prompt to enter the monitor
    - A later version is called "System II" and features a built-in floppy
      and SASI controller

****************************************************************************/

#include "emu.h"
#include "bus/centronics/ctronics.h"
#include "bus/mc68000/sysbus.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
#include "machine/6522via.h"
#include "machine/input_merger.h"
#include "machine/keyboard.h"
#include "machine/ram.h"
#include "video/mc6845.h"
#include "screen.h"


#define LOG_IO_READ  (1U << 1)
#define LOG_IO_WRITE (1U << 2)

//#define VERBOSE (LOG_GENERAL | LOG_IO_WRITE | LOG_IO_READ)
#include "logmacro.h"


namespace {


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class mc68000_state : public driver_device
{
public:
	mc68000_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_irq3(*this, "irq3"),
		m_ram(*this, RAM_TAG),
		m_crtc(*this, "crtc"),
		m_via(*this, "via%u", 0U),
		m_sysbus(*this, "sysbus"),
		m_centronics_latch(*this, "centronics_latch"),
		m_centronics_error(*this, "centronics_error"),
		m_centronics(*this, "centronics"),
		m_serial(*this, "serial"),
		m_apm_view(*this, "apm"),
		m_eprom(*this, "eprom%u", 0U),
		m_switches(*this, "switches")
	{ }

	void mc68000(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<m68000_device> m_maincpu;
	required_device<input_merger_device> m_irq3;
	required_device<ram_device> m_ram;
	required_device<mc6845_device> m_crtc;
	required_device_array<via6522_device, 2> m_via;
	required_device<mc68000_sysbus_device> m_sysbus;
	required_device<output_latch_device> m_centronics_latch;
	required_device<input_merger_device> m_centronics_error;
	required_device<centronics_device> m_centronics;
	required_device<rs232_port_device> m_serial;
	memory_view m_apm_view;
	required_memory_region_array<2> m_eprom;
	required_ioport m_switches;

	uint16_t *m_ram_base;
	uint32_t m_ram_mask;

	std::unique_ptr<uint8_t[]> m_addr_decode;

	uint8_t m_uvia_porta;
	uint8_t m_key;
	bool m_ibmkbd_clock;
	bool m_ibmkbd_data;
	uint8_t m_ibmkbd_bits;

	void mem_map(address_map &map) ATTR_COLD;
	void vector_map(address_map &map) ATTR_COLD;
	uint16_t memory_r(offs_t offset, uint16_t mem_mask = ~0);
	void memory_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

	void ibmkbd_clock_w(int state);
	void ibmkbd_data_w(int state);

	MC6845_UPDATE_ROW(crtc_update_row);

	void lvia_porta_w(uint8_t data);
	void uvia_porta_w(uint8_t data);
	void addr_decode_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void mc68000_state::mem_map(address_map &map)
{
	map(0x000000, 0xffffff).view(m_apm_view);
	m_apm_view[0](0x000000, 0x007fff).mirror(0xff8000).rom().region("eprom0", 0);
	m_apm_view[0](0x000000, 0xffffff).w(FUNC(mc68000_state::addr_decode_w));
	m_apm_view[1](0x000000, 0xffffff).rw(FUNC(mc68000_state::memory_r), FUNC(mc68000_state::memory_w));
}

void mc68000_state::vector_map(address_map &map)
{
	// vector number fetched from memory at 0xfffff0 to 0xffffff
	map(0xfffff0, 0xffffff).lr16(NAME([this](offs_t offset) -> uint16_t { return memory_r((0xfffff0 >> 1) | offset); }));
}

uint16_t mc68000_state::memory_r(offs_t offset, uint16_t mem_mask)
{
	uint8_t code = m_addr_decode[offset >> 13];
	uint16_t data = 0x0000;

	switch (code)
	{
	// ram
	case 0x0:
		data = m_ram_base[offset & m_ram_mask];
		break;

	// io
	case 0x1:
		if (machine().side_effects_disabled())
			break;

		LOGMASKED(LOG_IO_READ, "Read from IO: %06x = %04x & %04x\n", offset << 1, data, mem_mask);

		// ic45, 74ls139
		switch ((offset >> 11) & 0x03)
		{
		case 0:
			if (ACCESSING_BITS_8_15 && (BIT(offset, 0) == 1))
				data = m_crtc->register_r() << 8;
			break;

		case 1:
			if (ACCESSING_BITS_0_7)
				data |= m_via[0]->read(offset) << 0;
			if (ACCESSING_BITS_8_15)
				data |= m_via[1]->read(offset) << 8;
			break;

		case 2:
			data = m_sysbus->floppy_r(offset, mem_mask);
			break;

		case 3:
			if (ACCESSING_BITS_8_15 && (BIT(offset, 0) == 0))
			{
				data = m_key << 8;

				// a read here also selects switch 1 to be read from cb1
				m_via[1]->write_cb1(1);
				m_via[1]->write_cb1(BIT(m_switches->read(), 1));
			}
			if (ACCESSING_BITS_8_15 && (BIT(offset, 0) == 1))
				m_apm_view.select(0);
			break;
		}

		LOGMASKED(LOG_IO_READ, "IO data = %04x\n", data);

		break;

	// second eprom slot
	case 0x2:
		data = m_eprom[1]->as_u16(offset & 0x3fff);
		break;

	// monitor rom
	case 0x3:
		data = m_eprom[0]->as_u16(offset & 0x3fff);
		break;

	// bus error
	case 0x6:
		if (!machine().side_effects_disabled())
			m_maincpu->trigger_bus_error();
		break;

	// expansion slots
	case 0x8:
	case 0x9:
	case 0xa:
	case 0xb:
	case 0xc:
	case 0xd:
	case 0xe:
	case 0xf:
		data = m_sysbus->slot_r(code - 0x8, offset, mem_mask);
		break;

	default:
		LOG("Unhandled code %x: %06x = %04x & %04x\n", code, offset, data, mem_mask);
	}

	return data & mem_mask;
}

void mc68000_state::memory_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	uint8_t code = m_addr_decode[offset >> 13];

	switch (code)
	{
	// ram
	case 0x0:
		COMBINE_DATA(&m_ram_base[offset & m_ram_mask]);
		break;

	// io
	case 0x1:
		LOGMASKED(LOG_IO_WRITE, "Write to IO: %06x = %04x & %04x\n", offset << 1, data, mem_mask);

		// ic45, 74ls139
		switch ((offset >> 11) & 0x03)
		{
		case 0:
			if (ACCESSING_BITS_8_15 && (BIT(offset, 0) == 0))
				m_crtc->address_w(data >> 8);
			if (ACCESSING_BITS_8_15 && (BIT(offset, 0) == 1))
				m_crtc->register_w(data >> 8);
			break;

		case 1:
			if (ACCESSING_BITS_0_7)
				m_via[0]->write(offset, data >> 0);
			if (ACCESSING_BITS_8_15)
				m_via[1]->write(offset, data >> 8);
			break;

		case 2:
			m_sysbus->floppy_w(offset, data, mem_mask);
			break;

		case 3:
			if (ACCESSING_BITS_8_15 && (BIT(offset, 0) == 0))
			{
				m_centronics_latch->write(data >> 8);

				// a write here also selects switch 0 to be read from cb1
				m_via[1]->write_cb1(1);
				m_via[1]->write_cb1(BIT(m_switches->read(), 0));
			}
			if (ACCESSING_BITS_8_15 && (BIT(offset, 0) == 1))
				LOGMASKED(LOG_IO_WRITE, "Unhandled volume latch write\n");
			break;
		}

		break;

	// second eprom slot
	case 0x2:
		LOG("Write to second EPROM: %06x = %04x & %04x\n", offset, data, mem_mask);
		break;

	// monitor eprom
	case 0x3:
		LOG("Write to monitor EPROM: %06x = %04x & %04x\n", offset, data, mem_mask);
		break;

	// bus error
	case 0x6:
		m_maincpu->trigger_bus_error();
		break;

	// expansion slots
	case 0x8:
	case 0x9:
	case 0xa:
	case 0xb:
	case 0xc:
	case 0xd:
	case 0xe:
	case 0xf:
		m_sysbus->slot_w(code - 0x8, offset, data, mem_mask);
		break;

	default:
		LOG("Unhandled code %x: %06x = %04x & %04x\n", code, offset, data, mem_mask);
	}
}


//**************************************************************************
//  INPUT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( mc68000 )
	PORT_START("switches")
	PORT_DIPNAME(0x01, 0x01, "IO Mode")
	PORT_DIPLOCATION("DIL:1")
	PORT_DIPSETTING(   0x00, "Terminal")
	PORT_DIPSETTING(   0x01, "Internal")
	PORT_DIPNAME(0x02, 0x02, "Columns")
	PORT_DIPLOCATION("DIL:2")
	PORT_DIPSETTING(   0x00, "40")
	PORT_DIPSETTING(   0x02, "80")
	// DIL:3 and DIL:4 select parallel keyboard strobe polarity
INPUT_PORTS_END

void mc68000_state::ibmkbd_clock_w(int state)
{
	if (state && !m_ibmkbd_clock)
	{
		if (m_ibmkbd_bits >= 1 && m_ibmkbd_bits <= 8)
		{
			m_key <<= 1;
			m_key |= m_ibmkbd_data ? 0x01 : 0x00;
		}

		if (m_ibmkbd_bits == 9)
		{
			m_ibmkbd_bits = 0;

			m_via[0]->write_ca2(1);
			m_via[0]->write_ca2(0);
		}
		else
		{
			m_ibmkbd_bits++;
		}
	}

	m_ibmkbd_clock = bool(state);
}

void mc68000_state::ibmkbd_data_w(int state)
{
	m_ibmkbd_data = bool(state);
}


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

MC6845_UPDATE_ROW( mc68000_state::crtc_update_row )
{
	// page select
	offs_t offset = (m_uvia_porta & 0x07 & (m_ram_mask >> 15)) << 16;

	// offset into page
	offset |= ((ma & 0x3000) << 2) | ((ra & 0x07) << 11) | (ma & 0x7fe);

	rgb_t fg = rgb_t::white();
	rgb_t bg = rgb_t::black();

	for (int i = 0; i < x_count / 2; i++)
	{
		uint16_t data = m_ram_base[(offset >> 1) + i];

		// cursor
		if (cursor_x == (i * 2) + 0)
			data |= 0xff00;
		else if  (cursor_x == (i * 2) + 1)
			data |= 0x00ff;

		// lines 8 and 9 are blank
		if (ra > 7)
			data = 0x0000;

		// draw 16 pixels of the cell
		for (int x = 0; x < 16; x++)
			bitmap.pix(y, x + i * 16) = BIT(data, 15 - x) ? fg : bg;
	}
}


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void mc68000_state::lvia_porta_w(uint8_t data)
{
	// 7-------  serial cts (read)
	// -6------  40/80 char mode
	// --5-----  serial tx
	// ---4----  serial dtr
	// ----3---  ibm/pc keyboard (read)
	// -----2--  serial rx (read)
	// ------1-  cassette write
	// -------0  cassette read (read)

	m_serial->write_dtr(BIT(data, 4));
	m_serial->write_txd(BIT(~data, 5));
}

void mc68000_state::uvia_porta_w(uint8_t data)
{
	// 7-------  centronics error (read)
	// -6------  centronics strobe
	// --5-----  joystick control
	// ---43---  color helper bits
	// -----210  video base address

	m_uvia_porta = data;
	m_centronics->write_strobe(BIT(data, 6));
}

void mc68000_state::addr_decode_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
		m_addr_decode[offset >> 13] = data & 0x0f;

	// a write here deselects the address decoder
	if (ACCESSING_BITS_8_15)
		m_apm_view.select(1);
}

void mc68000_state::machine_start()
{
	// allocate space for address decoder ram
	m_addr_decode = std::make_unique<uint8_t[]>(0x400);

	// base ram
	m_ram_base = reinterpret_cast<uint16_t *>(m_ram->pointer());
	m_ram_mask = m_ram->mask() >> 1;

	// register for save states
	save_pointer(NAME(m_addr_decode), 0x400);
	save_item(NAME(m_uvia_porta));
	save_item(NAME(m_key));
	save_item(NAME(m_ibmkbd_clock));
	save_item(NAME(m_ibmkbd_data));
	save_item(NAME(m_ibmkbd_bits));
}

void mc68000_state::machine_reset()
{
	m_apm_view.select(0);

	m_key = 0x00;
	m_ibmkbd_bits = 0;

	// enable ibm pc keyboard mode
	m_via[0]->write_pa3(0);
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void mc68000_state::mc68000(machine_config &config)
{
	M68000(config, m_maincpu, 16_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &mc68000_state::mem_map);
	m_maincpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &mc68000_state::vector_map);

	INPUT_MERGER_ANY_HIGH(config, m_irq3);
	m_irq3->output_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ3);

	RAM(config, RAM_TAG).set_default_size("128K").set_extra_options("512K");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(16_MHz_XTAL, 1024, 0, 640, 312, 0, 250);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	MC6845(config, m_crtc, 16_MHz_XTAL / 8);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(mc68000_state::crtc_update_row));
	m_crtc->out_vsync_callback().set(m_via[1], FUNC(via6522_device::write_ca1));

	MOS6522(config, m_via[0], 16_MHz_XTAL / 2 / 10); // ic55
	m_via[0]->irq_handler().set(m_irq3, FUNC(input_merger_device::in_w<0>));
	m_via[0]->writepa_handler().set(FUNC(mc68000_state::lvia_porta_w));

	MOS6522(config, m_via[1], 16_MHz_XTAL / 2 / 10); // ic56
	m_via[1]->irq_handler().set(m_irq3, FUNC(input_merger_device::in_w<1>));
	m_via[1]->writepa_handler().set(FUNC(mc68000_state::uvia_porta_w));

	MC68000_SYSBUS(config, m_sysbus, 16_MHz_XTAL);
	m_sysbus->irq1_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ1);
	m_sysbus->irq2_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ2);
	m_sysbus->irq3_cb().set(m_irq3, FUNC(input_merger_device::in_w<2>));
	m_sysbus->irq4_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ4);
	m_sysbus->irq5_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ5);
	m_sysbus->irq6_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ6);
	m_sysbus->irq7_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ7);
	MC68000_SYSBUS_SLOT(config, "sysbus:0", mc68000_sysbus_cards, nullptr);
	MC68000_SYSBUS_SLOT(config, "sysbus:1", mc68000_sysbus_cards, nullptr);
	MC68000_SYSBUS_SLOT(config, "sysbus:2", mc68000_sysbus_cards, nullptr);
	MC68000_SYSBUS_SLOT(config, "sysbus:3", mc68000_sysbus_cards, nullptr);
	MC68000_SYSBUS_SLOT(config, "sysbus:4", mc68000_sysbus_cards, nullptr);
	MC68000_SYSBUS_SLOT(config, "sysbus:5", mc68000_sysbus_cards, nullptr);
	MC68000_SYSBUS_SLOT(config, "sysbus:6", mc68000_sysbus_cards, nullptr);
	MC68000_SYSBUS_SLOT(config, "sysbus:7", mc68000_sysbus_cards, nullptr);

	OUTPUT_LATCH(config, m_centronics_latch); // ic85

	INPUT_MERGER_ANY_LOW(config, m_centronics_error);
	m_centronics_error->output_handler().set(m_via[1], FUNC(via6522_device::write_pa7)).invert();

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->set_output_latch(*m_centronics_latch);
	m_centronics->ack_handler().set(m_via[1], FUNC(via6522_device::write_ca2)); // alternatively connected to busy
	m_centronics->fault_handler().set(m_centronics_error, FUNC(input_merger_device::in_w<0>));
	m_centronics->perror_handler().set(m_centronics_error, FUNC(input_merger_device::in_w<1>)).invert();

	RS232_PORT(config, m_serial, default_rs232_devices, nullptr);
	m_serial->rxd_handler().set(m_via[0], FUNC(via6522_device::write_ca1));
	m_serial->rxd_handler().append(m_via[0], FUNC(via6522_device::write_pa2));
	m_serial->cts_handler().set(m_via[0], FUNC(via6522_device::write_pa7));

	pc_kbdc_device &ibmkbd(PC_KBDC(config, "ibmkbd", pc_xt_keyboards, STR_KBD_IBM_PC_XT_83));
	ibmkbd.out_clock_cb().set(FUNC(mc68000_state::ibmkbd_clock_w));
	ibmkbd.out_data_cb().set(FUNC(mc68000_state::ibmkbd_data_w));
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( mc68000 )
	ROM_REGION16_BE(0x8000, "eprom0", 0)
	ROM_DEFAULT_BIOS("v143")
	ROM_SYSTEM_BIOS(0, "v141",  "V1.41")
	ROMX_LOAD("mc68000_system_1.41_upper.bin", 0x00000, 0x4000, CRC(e57f246f) SHA1(89e59e307ced22f243c4f6619dc07948e714fe71), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("mc68000_system_1.41_lower.bin", 0x00001, 0x4000, CRC(9183494d) SHA1(3be47d956d03e4f89c7ff17e027cd2fe8334d64a), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v143",  "V1.43")
	ROMX_LOAD("mc68000_system_1.43_upper.bin", 0x00000, 0x4000, CRC(5d73ae54) SHA1(38a594de605d63ba13ee963b004d3175a98e669a), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("mc68000_system_1.43_lower.bin", 0x00001, 0x4000, CRC(296434e5) SHA1(74416aebf3ccfa9b57d8d778b3689971f354d87c), ROM_SKIP(1) | ROM_BIOS(1))

	ROM_REGION16_BE(0x8000, "eprom1", 0)
	ROM_LOAD16_BYTE("mc68000_cpm_2.0_upper.bin", 0x00000, 0x4000, CRC(9dbe197a) SHA1(a26dcbbf567cdccf026ce5cebb248e52b5a8b24a))
	ROM_LOAD16_BYTE("mc68000_cpm_2.0_lower.bin", 0x00001, 0x4000, CRC(064dcc3b) SHA1(2fa3d1ddf1485bc46ddc90e2a68d9cc654157c27))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY                FULLNAME             FLAGS
COMP( 1984, mc68000, 0,      0,      mc68000, mc68000, mc68000_state, empty_init, "mc / Franzis Verlag", "mc-68000-Computer", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



mc8020.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************

MC-80.20 driver by Miodrag Milanovic

2009-05-12 Skeleton driver.
2009-05-15 Initial implementation
2011-09-01 Modernised, added a keyboard

ToDo:
- Find out if it has sound hardware, add if so
- What port B on PIO is for
- Find out the correct frequencies and connections for the CTC

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/z80daisy.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "emupal.h"
#include "screen.h"


namespace {

class mc8020_state : public driver_device
{
public:
	mc8020_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_p_videoram(*this, "videoram")
		, m_maincpu(*this, "maincpu")
		, m_keyboard(*this, "X%u", 0U)
	{ }

	void mc8020(machine_config &config);

private:
	u8 port_b_r();
	void port_a_w(u8 data);
	void port_b_w(u8 data);
	uint32_t screen_update_mc8020(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	u8 m_row = 0U;
	virtual void machine_start() override ATTR_COLD;
	required_shared_ptr<u8> m_p_videoram;
	required_device<z80_device> m_maincpu;
	required_ioport_array<7> m_keyboard;
};

void mc8020_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0bff).rom();
	map(0x0c00, 0x0fff).ram().share("videoram");// 1KB RAM ZRE
	map(0x2000, 0x5fff).rom();
	map(0x6000, 0xffff).ram();
}

void mc8020_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0xf0, 0xf3).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0xf4, 0xf7).rw("pio", FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
}


/* Input ports */
static INPUT_PORTS_START( mc8020 )
	PORT_START("X0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(34)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("X1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('+') PORT_CHAR('*')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('^')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('/')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')

	PORT_START("X2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')

	PORT_START("X3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')

	PORT_START("X4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')

	PORT_START("X5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('?')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR(']')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)

	PORT_START("X6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
INPUT_PORTS_END


u8 mc8020_state::port_b_r()
{
	if (m_row == 0x40)
		return m_keyboard[0]->read();
	else
	if (m_row == 0x20)
		return m_keyboard[1]->read();
	else
	if (m_row == 0x10)
		return m_keyboard[2]->read();
	else
	if (m_row == 0x08)
		return m_keyboard[3]->read();
	else
	if (m_row == 0x04)
		return m_keyboard[4]->read();
	else
	if (m_row == 0x02)
		return m_keyboard[5]->read();
	else
	if (m_row == 0x01)
		return m_keyboard[6]->read();
	else
		return 0;
}

void mc8020_state::port_a_w(u8 data)
{
	m_row = data;
}

void mc8020_state::port_b_w(u8 data)
{
}

void mc8020_state::machine_start()
{
	save_item(NAME(m_row));
}


// This is not a content of U402 510
// but order is fine
static const uint8_t prom[] = {
	0x0e,0x11,0x13,0x15,0x17,0x10,0x0e,0x00, // @
	0x04,0x0a,0x11,0x11,0x1f,0x11,0x11,0x00, // A
	0x1e,0x11,0x11,0x1e,0x11,0x11,0x1e,0x00, // B
	0x0e,0x11,0x10,0x10,0x10,0x11,0x0e,0x00, // C
	0x1e,0x09,0x09,0x09,0x09,0x09,0x1e,0x00, // D
	0x1f,0x10,0x10,0x1e,0x10,0x10,0x1f,0x00, // E
	0x1f,0x10,0x10,0x1e,0x10,0x10,0x10,0x00, // F
	0x0e,0x11,0x10,0x10,0x13,0x11,0x0f,0x00, // G

	0x11,0x11,0x11,0x1f,0x11,0x11,0x11,0x00, // H
	0x0e,0x04,0x04,0x04,0x04,0x04,0x0e,0x00, // I
	0x01,0x01,0x01,0x01,0x11,0x11,0x0e,0x00, // J
	0x11,0x12,0x14,0x18,0x14,0x12,0x11,0x00, // K
	0x10,0x10,0x10,0x10,0x10,0x10,0x1f,0x00, // L
	0x11,0x1b,0x15,0x15,0x11,0x11,0x11,0x00, // M
	0x11,0x11,0x19,0x15,0x13,0x11,0x11,0x00, // N
	0x0e,0x11,0x11,0x11,0x11,0x11,0x0e,0x00, // O

	0x1e,0x11,0x11,0x1e,0x10,0x10,0x10,0x00, // P
	0x0e,0x11,0x11,0x11,0x15,0x12,0x0d,0x00, // Q
	0x1e,0x11,0x11,0x1e,0x14,0x12,0x11,0x00, // R
	0x0e,0x11,0x10,0x0e,0x01,0x11,0x0e,0x00, // S
	0x1f,0x04,0x04,0x04,0x04,0x04,0x04,0x00, // T
	0x11,0x11,0x11,0x11,0x11,0x11,0x0e,0x00, // U
	0x11,0x11,0x11,0x0a,0x0a,0x04,0x04,0x00, // V
	0x11,0x11,0x11,0x15,0x15,0x15,0x0a,0x00, // W

	0x11,0x11,0x0a,0x04,0x0a,0x11,0x11,0x00, // X
	0x11,0x11,0x0a,0x04,0x04,0x04,0x04,0x00, // Y
	0x1f,0x01,0x02,0x04,0x08,0x10,0x1f,0x00, // Z
	0x1c,0x10,0x10,0x10,0x10,0x10,0x1c,0x00, // [
	0x00,0x10,0x08,0x04,0x02,0x01,0x00,0x00, // backslash
	0x07,0x01,0x01,0x01,0x01,0x01,0x07,0x00, // ]
	0x0e,0x11,0x00,0x00,0x00,0x00,0x00,0x00, // ^
	0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x00, // _

	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //
	0x04,0x04,0x04,0x04,0x04,0x00,0x04,0x00, // !
	0x0a,0x0a,0x0a,0x00,0x00,0x00,0x00,0x00, // "
	0x0a,0x0a,0x1f,0x0a,0x1f,0x0a,0x0a,0x00, // #
	0x00,0x11,0x0e,0x0a,0x0e,0x11,0x00,0x00, // []
	0x18,0x19,0x02,0x04,0x08,0x13,0x03,0x00, // %
	0x04,0x0a,0x0a,0x0c,0x15,0x12,0x0d,0x00, // &
	0x04,0x04,0x08,0x00,0x00,0x00,0x00,0x00, // '

	0x02,0x04,0x08,0x08,0x08,0x04,0x02,0x00, // (
	0x08,0x04,0x02,0x02,0x02,0x04,0x08,0x00, // )
	0x00,0x04,0x15,0x0e,0x15,0x04,0x00,0x00, // *
	0x00,0x04,0x04,0x1f,0x04,0x04,0x00,0x00, // +
	0x00,0x00,0x00,0x00,0x08,0x08,0x10,0x00, // ,
	0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x00, // -
	0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00, // .
	0x00,0x01,0x02,0x04,0x08,0x10,0x00,0x00, // /

	0x0e,0x11,0x13,0x15,0x19,0x11,0x0e,0x00, // 0
	0x04,0x0c,0x04,0x04,0x04,0x04,0x0e,0x00, // 1
	0x0e,0x11,0x01,0x06,0x08,0x10,0x1f,0x00, // 2
	0x1f,0x01,0x02,0x06,0x01,0x11,0x0e,0x00, // 3
	0x02,0x06,0x0a,0x12,0x1f,0x02,0x02,0x00, // 4
	0x1f,0x10,0x1e,0x01,0x01,0x11,0x0e,0x00, // 5
	0x07,0x08,0x10,0x1e,0x11,0x11,0x0e,0x00, // 6
	0x1f,0x01,0x02,0x04,0x08,0x08,0x08,0x00, // 7

	0x0e,0x11,0x11,0x0e,0x11,0x11,0x0e,0x00, // 8
	0x0e,0x11,0x11,0x0f,0x01,0x02,0x1c,0x00, // 9
	0x00,0x00,0x00,0x00,0x08,0x00,0x08,0x00, // :
	0x00,0x00,0x04,0x00,0x04,0x04,0x08,0x00, // ;
	0x02,0x04,0x08,0x10,0x08,0x04,0x02,0x00, // <
	0x00,0x00,0x1f,0x00,0x1f,0x00,0x00,0x00, // =
	0x08,0x04,0x02,0x01,0x02,0x04,0x08,0x00, // >
	0x0e,0x11,0x01,0x02,0x04,0x00,0x04,0x00  // ?
};


uint32_t mc8020_state::screen_update_mc8020(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 sy=0,ma=0;

	for (u8 y = 0; y < 8; y++ )
	{
		for (u8 ra = 0; ra < 16; ra++)
		{
			u16 *p = &bitmap.pix(sy++);

			for (u16 x = ma; x < ma + 32; x++)
			{
				u8 gfx;
				if (ra > 3 && ra < 12)
				{
					u8 chr = m_p_videoram[x];
					gfx = prom[(chr<<3) | (ra-4)];
				}
				else
					gfx = 0;

				/* Display a scanline of a character */
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma+=32;
	}
	return 0;
}

static const z80_daisy_config daisy_chain[] =
{
	{ "ctc" },
	{ nullptr }
};

void mc8020_state::mc8020(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(2'457'600));
	m_maincpu->set_addrmap(AS_PROGRAM, &mc8020_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &mc8020_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(32*6, 16*8);
	screen.set_visarea(0, 32*6-1, 0, 16*8-1);
	screen.set_screen_update(FUNC(mc8020_state::screen_update_mc8020));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* devices */
	z80pio_device& pio(Z80PIO(config, "pio", XTAL(2'457'600)));
	pio.out_pa_callback().set(FUNC(mc8020_state::port_a_w));
	pio.in_pb_callback().set(FUNC(mc8020_state::port_b_r));
	pio.out_pb_callback().set(FUNC(mc8020_state::port_b_w));

	z80ctc_device &ctc(Z80CTC(config, "ctc", XTAL(2'457'600)));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	ctc.set_clk<2>(XTAL(2'457'600) / 64); // guess
	ctc.zc_callback<2>().set("ctc", FUNC(z80ctc_device::trg1));
	ctc.zc_callback<2>().append("ctc", FUNC(z80ctc_device::trg0));
}


/* ROM definition */
ROM_START( mc8020 )
	ROM_REGION( 0x6000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "ver1", "Version 1")
	ROMX_LOAD( "s01.rom",     0x0000, 0x0400, CRC(0f1c1a62) SHA1(270c0a9e8e165658f3b09d40a3e8bb3dc1b88184), ROM_BIOS(0))
	ROMX_LOAD( "s02.rom",     0x0400, 0x0400, CRC(93b5811c) SHA1(8559d24072c9b5908a2627ff986d818308f51d59), ROM_BIOS(0))
	ROMX_LOAD( "s03.rom",     0x0800, 0x0400, CRC(3d32c334) SHA1(56d3012595540d03054ad3c6795ed5d929581a04), ROM_BIOS(0))
	ROMX_LOAD( "mo01_v2.rom", 0x2000, 0x0400, CRC(7e47201c) SHA1(db49afdc5c1fe4065a979c56cbdbd3c58f5d942f), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "ver2", "Version 2")
	ROMX_LOAD( "s01.rom",    0x0000, 0x0400, CRC(0f1c1a62) SHA1(270c0a9e8e165658f3b09d40a3e8bb3dc1b88184), ROM_BIOS(1))
	ROMX_LOAD( "s02_v2.rom", 0x0400, 0x0400, CRC(dd26c90a) SHA1(1108c11362fa63d21110a3b17868c1854a318c09), ROM_BIOS(1))
	ROMX_LOAD( "s03_v2.rom", 0x0800, 0x0400, CRC(5b64ee7b) SHA1(3b4cbfcb8e2dedcfd4a3680c81fe6ceb2211b275), ROM_BIOS(1))
	ROMX_LOAD( "mo01.rom",   0x2000, 0x0400, CRC(c65a470f) SHA1(71325fed1a342149b5efc2234ecfc8adfff0a42d), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "ver3", "Version 3")
	ROMX_LOAD( "s01.rom",    0x0000, 0x0400, CRC(0f1c1a62) SHA1(270c0a9e8e165658f3b09d40a3e8bb3dc1b88184), ROM_BIOS(2))
	ROMX_LOAD( "s02_v3.rom", 0x0400, 0x0400, CRC(40c7a694) SHA1(bcdf382e8dad9bb6e06d23ec018e9df55c8d8d0c), ROM_BIOS(2))
	ROMX_LOAD( "s03.rom",    0x0800, 0x0400, CRC(3d32c334) SHA1(56d3012595540d03054ad3c6795ed5d929581a04), ROM_BIOS(2))
	ROMX_LOAD( "mo01_v2.rom",0x2000, 0x0400, CRC(7e47201c) SHA1(db49afdc5c1fe4065a979c56cbdbd3c58f5d942f), ROM_BIOS(2))

	// m02 doesn't exist on board
	ROM_LOAD( "mo03.rom", 0x2800, 0x0400, CRC(29685056) SHA1(39e77658fb7af5a28112341f0893e007d73c1b7a))
	ROM_LOAD( "mo04.rom", 0x2c00, 0x0400, CRC(fd315b73) SHA1(cfb943ec8c884a9b92562d05f92faf06fe42ad68))
	ROM_LOAD( "mo05.rom", 0x3000, 0x0400, CRC(453d6370) SHA1(d96f0849a2da958d7e92a31667178ad140719477))
	ROM_LOAD( "mo06.rom", 0x3400, 0x0400, CRC(6357aba5) SHA1(a4867766f6e14e9fe66f22a6839f17c01058c8af))
	ROM_LOAD( "mo07.rom", 0x3800, 0x0400, CRC(a1eb6021) SHA1(b05b63f02de89f065f337bbe54c5b48244e3a4ba))
	ROM_LOAD( "mo08.rom", 0x3c00, 0x0400, CRC(8221cc32) SHA1(65e0ee4241d39d138205c88374b3bcd127e21511))
	ROM_LOAD( "mo09.rom", 0x4000, 0x0400, CRC(7ad5944d) SHA1(ef2781b114277a09ce0cf2e7decfdb7c48a693e3))
	ROM_LOAD( "mo10.rom", 0x4400, 0x0400, CRC(11de8c76) SHA1(b384d22506ff7e3e28bd2dcc33b7a69617eeb52a))
	ROM_LOAD( "mo11.rom", 0x4800, 0x0400, CRC(370cc672) SHA1(133f6e8bfd4e1ca2e9e0a8e2342084419f895e3c))
	ROM_LOAD( "mo12.rom", 0x4c00, 0x0400, CRC(a3838f2b) SHA1(e3602943700bf5068117946bf86f052f5c587169))
	ROM_LOAD( "mo13.rom", 0x5000, 0x0400, CRC(88b61d12) SHA1(00dd4452b4d4191e589ab58ba924ed21b10f323b))
	ROM_LOAD( "mo14.rom", 0x5400, 0x0400, CRC(2168da19) SHA1(c1ce1263167067d8be0a90604d9c29b7379a0545))
	ROM_LOAD( "mo15.rom", 0x5800, 0x0400, CRC(e32f54c4) SHA1(c3d9ca2204e7adbc625cf96031acb8c1df0447c7))
	ROM_LOAD( "mo16.rom", 0x5c00, 0x0400, CRC(403be935) SHA1(4e74355a78ab090ce180437156fed8e4a1d1c787))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY                FULLNAME       FLAGS
COMP( 198?, mc8020, 0,      0,      mc8020,  mc8020, mc8020_state, empty_init, "VEB Elektronik Gera", "MC-80.21/22", MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



mc8030.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

MC-80.3x driver by Miodrag Milanovic

2009-05-12 Skeleton driver.
2009-05-15 Initial implementation
2011-09-01 Modernised

mc80.3x: http://www.ycdt.net/mc80.3x/

mc8030: very little info available. The area from FFD8-FFFF is meant for
interrupt vectors and so on, but most of it is zeroes. Appears the keyboard
is an ascii keyboard with built-in beeper. It communicates via the SIO.
The asp ctc needs at least 2 triggers. The purpose of the zve pio is unknown.

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/z80daisy.h"
#include "emupal.h"
#include "screen.h"
#include "machine/clock.h"
#include "bus/rs232/rs232.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"


namespace {

class mc8030_state : public driver_device
{
public:
	mc8030_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void mc8030(machine_config &config);

private:
	void zve_write_protect_w(uint8_t data);
	void vis_w(offs_t offset, uint8_t data);
	void eprom_prog_w(uint8_t data);
	uint8_t zve_port_a_r();
	uint8_t zve_port_b_r();
	void zve_port_a_w(uint8_t data);
	void zve_port_b_w(uint8_t data);
	uint8_t asp_port_a_r();
	uint8_t asp_port_b_r();
	void asp_port_a_w(uint8_t data);
	void asp_port_b_w(uint8_t data);
	void machine_start() override ATTR_COLD;
	uint32_t screen_update_mc8030(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	std::unique_ptr<u8[]> m_vram;
	required_device<z80_device> m_maincpu;
};


void mc8030_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	//  ZRE 4 * 2KB
	map(0x0000, 0x1fff).rom(); // ZRE ROM's 4 * 2716
	map(0x2000, 0x27ff).rom(); // SPE ROM's 2 * 2708
	map(0x2800, 0x3fff).rom(); // For extension
	map(0x4000, 0xbfff).ram(); // SPE RAM
	map(0xc000, 0xffff).ram(); // ZRE RAM
}

void mc8030_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x30, 0x3f).mirror(0xff00).noprw(); //"mass storage"
	map(0x80, 0x83).mirror(0xff00).rw("zve_ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write)); // user CTC
	map(0x84, 0x87).mirror(0xff00).rw("zve_pio", FUNC(z80pio_device::read), FUNC(z80pio_device::write)); // PIO unknown usage
	map(0x88, 0x8f).mirror(0xff00).w(FUNC(mc8030_state::zve_write_protect_w));
	map(0xc0, 0xcf).select(0xff00).w(FUNC(mc8030_state::vis_w));
	map(0xd0, 0xd3).mirror(0xff00).rw("asp_sio", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w)); // keyboard & IFSS?
	map(0xd4, 0xd7).mirror(0xff00).rw("asp_ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write)); // sio bauds, KMBG? and kbd
	map(0xd8, 0xdb).mirror(0xff00).rw("asp_pio", FUNC(z80pio_device::read), FUNC(z80pio_device::write)); // external bus
	map(0xe0, 0xef).mirror(0xff00).w(FUNC(mc8030_state::eprom_prog_w));
}

/* Input ports */
static INPUT_PORTS_START( mc8030 )
INPUT_PORTS_END


void  mc8030_state::zve_write_protect_w(uint8_t data)
{
}

void mc8030_state::vis_w(offs_t offset, uint8_t data)
{
	// reg C
	// 7 6 5 4 -- module
	//         3 - 0 left half, 1 right half
	//           2 1 0
	//           =====
	//           0 0 0 - dark
	//           0 0 1 - light
	//           0 1 0 - in reg pixel
	//           0 1 1 - negate in reg pixel
	//           1 0 x - operation code in B reg
	// reg B
	//
	uint16_t addr = ((offset & 0xff00) >> 2) | ((offset & 0x08) << 2) | (data >> 3);
	uint8_t c = 1 << (data & 7);
	if (BIT(offset, 0))
		m_vram[addr] |= c;
	else
		m_vram[addr] &= ~c;
}

void mc8030_state::eprom_prog_w(uint8_t data)
{
}

uint8_t mc8030_state::zve_port_a_r()
{
	return 0xff;
}

uint8_t mc8030_state::zve_port_b_r()
{
	return 0xff;
}

uint8_t mc8030_state::asp_port_a_r()
{
	return 0xff;
}

uint8_t mc8030_state::asp_port_b_r()
{
	return 0xff;
}


uint32_t mc8030_state::screen_update_mc8030(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint16_t ma=0;

	for(uint16_t y = 0; y < 256; y++ )
	{
		uint16_t *p = &bitmap.pix(y);
		for (uint16_t x = ma; x < ma + 64; x++)
		{
			uint8_t const gfx = m_vram[x^0x3fff];

			/* Display a scanline of a character */
			*p++ = BIT(gfx, 7);
			*p++ = BIT(gfx, 6);
			*p++ = BIT(gfx, 5);
			*p++ = BIT(gfx, 4);
			*p++ = BIT(gfx, 3);
			*p++ = BIT(gfx, 2);
			*p++ = BIT(gfx, 1);
			*p++ = BIT(gfx, 0);
		}
		ma+=64;
	}
	return 0;
}

// this is a guess there is no information available
static const z80_daisy_config daisy_chain[] =
{
	{ "asp_ctc" },      /* System ctc */
	{ "asp_pio" },      /* System pio */
	{ "asp_sio" },      /* sio */
	{ "zve_pio" },      /* User pio */
	{ "zve_ctc" },      /* User ctc */
	{ nullptr }
};

void mc8030_state::machine_start()
{
	m_vram = make_unique_clear<u8[]>(0x4000);
	save_pointer(NAME(m_vram), 0x4000);
}

void mc8030_state::mc8030(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(2'457'600));
	m_maincpu->set_addrmap(AS_PROGRAM, &mc8030_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &mc8030_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(512, 256);
	screen.set_visarea(0, 512-1, 0, 256-1);
	screen.set_screen_update(FUNC(mc8030_state::screen_update_mc8030));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* Devices */
	z80pio_device& zve_pio(Z80PIO(config, "zve_pio", XTAL(2'457'600)));
	zve_pio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	zve_pio.in_pa_callback().set(FUNC(mc8030_state::zve_port_a_r));
	//zve_pio.out_pa_callback().set(FUNC(mc8030_state::zve_port_a_w));
	zve_pio.in_pb_callback().set(FUNC(mc8030_state::zve_port_b_r));
	//zve_pio.out_pb_callback().set(FUNC(mc8030_state::zve_port_b_w));

	z80ctc_device& zve_ctc(Z80CTC(config, "zve_ctc", XTAL(2'457'600)));
	zve_ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	// ZC0, ZC1, ZC2 for user

	z80pio_device& asp_pio(Z80PIO(config, "asp_pio", XTAL(2'457'600)));
	asp_pio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	asp_pio.in_pa_callback().set(FUNC(mc8030_state::asp_port_a_r));
	//asp_pio.out_pa_callback().set(FUNC(mc8030_state::asp_port_a_w));
	asp_pio.in_pb_callback().set(FUNC(mc8030_state::asp_port_b_r));
	//asp_pio.out_pb_callback().set(FUNC(mc8030_state::asp_port_b_w));

	z80ctc_device& asp_ctc(Z80CTC(config, "asp_ctc", XTAL(2'457'600)));
	asp_ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	// ZC0: to SIO CLK CH A
	// ZC1: to SIO CLK CH B
	// ZC2: KMBG (??)

	clock_device &uart_clock(CLOCK(config, "uart_clock", 153600));
	uart_clock.signal_handler().set("asp_sio", FUNC(z80sio_device::txca_w));
	uart_clock.signal_handler().append("asp_sio", FUNC(z80sio_device::rxca_w));

	z80sio_device& sio(Z80SIO(config, "asp_sio", 4800));
	// SIO CH A in = keyboard; out = beeper; CH B = IFSS (??)
	sio.out_txda_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	sio.out_dtra_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	sio.out_rtsa_callback().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "keyboard"));
	rs232.rxd_handler().set("asp_sio", FUNC(z80sio_device::rxa_w));
	rs232.cts_handler().set("asp_sio", FUNC(z80sio_device::ctsa_w));
}

/* ROM definition */
ROM_START( mc8030 )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "zve_1.rom", 0x0000, 0x0800, CRC(31ec0159) SHA1(a97ea9eb733c462e77d625a7942134e45d911c0a))
	ROM_LOAD( "zve_2.rom", 0x0800, 0x0800, CRC(5104983d) SHA1(7516274904042f4fc6813aa8b2a75c0a64f9b937))
	ROM_LOAD( "zve_3.rom", 0x1000, 0x0800, CRC(4bcfd727) SHA1(d296e587098e70270ad60db8edaa685af368b849))
	ROM_LOAD( "zve_4.rom", 0x1800, 0x0800, CRC(f949ae43) SHA1(68c324cf5578497db7ae65da5695fcb30493f612))
	ROM_LOAD( "spe_1.rom", 0x2000, 0x0400, CRC(826f609c) SHA1(e77ff6c180f5a6d7756d076173ae264a0e26f066))
	ROM_LOAD( "spe_2.rom", 0x2400, 0x0400, CRC(98320040) SHA1(6baf87e196f1ccdf44912deafa6042becbfb0679))

	ROM_REGION( 0x4000, "user1", 0 )
	// marked as "80.3x"
	ROM_LOAD( "mc80.3-x-2c00-c63c.bin", 0x2c00, 0x0400, CRC(469be754) SHA1(a7fea257a1c0970349f75504c0870a2649b50303) )
	ROM_LOAD( "mc80.3-ccd-3000-f10a.bin", 0x3000, 0x0400, CRC(7d220128) SHA1(bb6070c9d460ec7ea1a1b46b19ca9520d55e127c) ) // 80.3x ccd test system
	ROM_LOAD( "mc80.3-x-3800-7280.bin", 0x3800, 0x0400, CRC(09976efb) SHA1(1a708adbf1cd68d450a9bfccafe1f82e755e5885) )
	// marked as "80.3x rk"
	ROM_LOAD( "mc80.3-rk-3000-5642.bin", 0x3000, 0x0400, CRC(280b2211) SHA1(d2c05ff7f7ea534776bf7e92263f1c10192e5385) )
	ROM_LOAD( "mc80.3-rk-3400-c6a7.bin", 0x3400, 0x0400, CRC(fc5656f3) SHA1(0ad5abb6536665719693063bf8da2993238c84dd) )
	ROM_LOAD( "mc80.3-rk-3800-1678.bin", 0x3800, 0x0400, CRC(34d7e1cf) SHA1(14d3e49f34e0c2a95967613538b33a671998e7a8) )
	// marked as "80.30e v1"
	ROM_LOAD( "mc80.30e-0000.bin", 0x0000, 0x0800, CRC(ebdf766f) SHA1(d06f1e4467104f59554168d17cd15b98d107375e) )
	// marked as "80.30e v2"
	ROM_LOAD( "mc80.30e-0000v2.bin", 0x0000, 0x0800, CRC(259b55e9) SHA1(6e8fd84f1b225f33bc0fd30ecc6e30b8063eaeed) )
	ROM_LOAD( "mc80.30e-0800v2.bin", 0x0800, 0x0800, CRC(fe7a01a7) SHA1(6531cde5b9dea2a15a813598937aa3d9540a8066) )
	// marked as "80.31e"
	ROM_LOAD( "mc80.31e-2400-d0d7.bin", 0x2400, 0x0400, CRC(43c22046) SHA1(a179fe83b5cbbbc5f92a4b2ef1012099ccb333d9) )
	ROM_LOAD( "mc80.31e-3000-0d15.bin", 0x3000, 0x0400, CRC(cf6f090b) SHA1(0bc352ca42f41cfe7e28052c099dcd020b776dd5) )
	ROM_LOAD( "mc80.31e-3400-9993.bin", 0x3400, 0x0400, CRC(918d2b55) SHA1(b6dec17e996c464cf189a699d24d270494540b49) )
	ROM_LOAD( "mc80.31e-3800-7a4c.bin", 0x3800, 0x0400, CRC(62d9f989) SHA1(a20b731daed51270d86f486751302055eb93dd1c) )
	// marked as "80.31e?"
	ROM_LOAD( "mc80.3s-2000.bin", 0x2000, 0x0400, CRC(28f1df56) SHA1(9752c9eab3d9f72c23b5f5618a5db1a038953e29) )
	ROM_LOAD( "mc80.3s-2400.bin", 0x2400, 0x0400, CRC(43c22046) SHA1(a179fe83b5cbbbc5f92a4b2ef1012099ccb333d9) )
	ROM_LOAD( "mc80.3s-2800.bin", 0x2800, 0x0400, CRC(4b52deb1) SHA1(f8a9ddb4363f8389990fd263985e882a73265c5d) )
	ROM_LOAD( "mc80.3s-2c00.bin", 0x2c00, 0x0400, CRC(a13d8302) SHA1(1fcdcd6b7af8ef4b18a0658a1a50d0db26b7f214) )
	ROM_LOAD( "mc80.3s-3000.bin", 0x3000, 0x0400, CRC(cf6f090b) SHA1(0bc352ca42f41cfe7e28052c099dcd020b776dd5) )
	ROM_LOAD( "mc80.3s-3400.bin", 0x3400, 0x0400, CRC(918d2b55) SHA1(b6dec17e996c464cf189a699d24d270494540b49) )
	ROM_LOAD( "mc80.3s-3800.bin", 0x3800, 0x0400, CRC(6104646b) SHA1(630f7c57e928db0eb4070139a66f2d313a6314b4) )
	ROM_LOAD( "mc80.3s-3c00.bin", 0x3c00, 0x0400, CRC(2f82d032) SHA1(fe8f642b94a0ba8852ec56d8cbb7a52bb7e5d55a) )
	// marked as "80.33 original"
	ROM_LOAD( "mc80.33-1000.bin", 0x1000, 0x0800, CRC(c7e062b1) SHA1(81b999655b32d9b39287a08896a274278a2f739c) )
	// random set, zve_1 - 4 same as main set
	ROM_LOAD( "spe_1a.rom",    0x2000, 0x000800, CRC(37c71c68) SHA1(951650698b00f65facf5ccfbd8dd13628a93425d) )
	ROM_LOAD( "spe_2a.rom",    0x2800, 0x000400, CRC(9ec8f287) SHA1(cdf5a9583d898814ba480ffbc8d906a642c6dc81) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY                FULLNAME       FLAGS
COMP( 198?, mc8030, 0,      0,      mc8030,  mc8030, mc8030_state, empty_init, "VEB Elektronik Gera", "MC-80.30/31", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | ORIENTATION_FLIP_X | MACHINE_SUPPORTS_SAVE )



mcb216.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

2013-12-01 Driver for Cromemco MCB-216 SCC (Single Card Computer),
and also the earlier CB-308.


Memory allocation
- 0000 to 0FFF - standard roms
- 1000 to 1FFF - optional roms or ram (expect roms)
- 2000 to 23FF - standard ram
- 2400 to FFFF - optional whatever the user wants (expect ram)

All commands to be in uppercase.

MCB-216:
Press Enter twice. You will see the Basic OK prompt. To get into the
monitor, use the QUIT command, and to return use the B command.

The mcb216 can use an optional floppy-disk-drive unit. The only other
storage is paper-tape, which is expected to be attached to the terminal.

CB-308:
Press Enter twice. You will see the Monitor logo. To get into the BASIC,
enter GE400. To return to the monitor, use the QUIT command followed by
pressing Enter twice. All monitor commands must be in uppercase. The
only storage is paper-tape.

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/tms5501.h"
#include "bus/rs232/rs232.h"


namespace {

class mcb216_state : public driver_device
{
public:
	mcb216_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_tms5501(*this, "tms5501")
	{ }

	void mcb216(machine_config &config);

protected:
	u8 tms5501_status_r();
	IRQ_CALLBACK_MEMBER(irq_callback);
	void mcb216_io(address_map &map) ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_device<tms5501_device> m_tms5501;

private:
	void mcb216_mem(address_map &map) ATTR_COLD;
};

class cb308_state : public mcb216_state
{
public:
	using mcb216_state::mcb216_state;
	void cb308(machine_config &config);

private:
	void cb308_mem(address_map &map) ATTR_COLD;
	void machine_reset() override ATTR_COLD;
};

u8 mcb216_state::tms5501_status_r()
{
	// D7  D6  D5  D4  D3  D2  D1  D0
	// TBE RDA IPG TBE RDA SRV ORE FME
	return bitswap<8>(m_tms5501->sta_r(), 4, 3, 5, 4, 3, 2, 1, 0);
}

void mcb216_state::mcb216_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom().region("roms", 0);  // SCC board has space for 2k of roms
	map(0x2000, 0x23ff).ram();
	map(0x2400, 0xffff).ram();
}

void mcb216_state::mcb216_io(address_map &map)
{
	map.global_mask(0xff);
	// 74904 PROM provides custom remapping for TMS5501 registers
	map(0x00, 0x00).r(FUNC(mcb216_state::tms5501_status_r)).w(m_tms5501, FUNC(tms5501_device::rr_w));
	map(0x01, 0x01).rw(m_tms5501, FUNC(tms5501_device::rb_r), FUNC(tms5501_device::tb_w));
	map(0x02, 0x02).w(m_tms5501, FUNC(tms5501_device::cmd_w));
	map(0x03, 0x03).rw(m_tms5501, FUNC(tms5501_device::rst_r), FUNC(tms5501_device::mr_w));
	map(0x04, 0x04).rw(m_tms5501, FUNC(tms5501_device::xi_r), FUNC(tms5501_device::xo_w));
	map(0x05, 0x09).w(m_tms5501, FUNC(tms5501_device::tmr_w));
}

void cb308_state::cb308_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).ram();
	map(0xe000, 0xefff).rom().region("roms", 0);
}


/* Input ports */
static INPUT_PORTS_START( mcb216 )
INPUT_PORTS_END


IRQ_CALLBACK_MEMBER(mcb216_state::irq_callback)
{
	return m_tms5501->get_vector();
}

void cb308_state::machine_reset()
{
	m_maincpu->set_state_int(Z80_PC, 0xe000);
}

void mcb216_state::mcb216(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 8_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &mcb216_state::mcb216_mem);
	m_maincpu->set_addrmap(AS_IO, &mcb216_state::mcb216_io);
	m_maincpu->set_irq_acknowledge_callback(FUNC(mcb216_state::irq_callback));

	TMS5501(config, m_tms5501, 8_MHz_XTAL / 4);
	m_tms5501->xmt_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	m_tms5501->int_callback().set_inputline(m_maincpu, 0);

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_tms5501, FUNC(tms5501_device::rcv_w));
}

void cb308_state::cb308(machine_config &config)
{
	mcb216(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &cb308_state::cb308_mem);
}

/* ROM definition */
ROM_START( mcb216 )
	ROM_REGION(0x2000, "roms", 0)
	ROM_LOAD( "mcb216r0", 0x0000, 0x0800, CRC(86d20cea) SHA1(9fb8fdbcb8d31bd3304a0b3339c7f423188e9d37) )
	ROM_LOAD( "mcb216r1", 0x0800, 0x0800, CRC(68a25b2c) SHA1(3eadd4a5d65726f767742deb4b51a97df813f37d) )

	ROM_REGION(0x20, "prom", 0)
	ROM_LOAD( "74904.ic25", 0x00, 0x20, NO_DUMP ) // TBP18S030 or equivalent
ROM_END

ROM_START( cb308 )
	ROM_REGION(0x2000, "roms", 0)
	ROM_LOAD( "cb308r0",  0x0000, 0x0400, CRC(62f50531) SHA1(3071e2ab7fc6b2ca889e4fb5cf7cc9ee8fbe53d3) )
	ROM_LOAD( "cb308r1",  0x0400, 0x0400, CRC(03191ac1) SHA1(84665dfc797c9f51bb659291b18399986ed846fb) )
	ROM_LOAD( "cb308r2",  0x0800, 0x0400, CRC(695ea521) SHA1(efe36a712e2a038ee804e556c5ebe05443cf798e) )
	ROM_LOAD( "cb308r3",  0x0c00, 0x0400, CRC(e3e4a778) SHA1(a7c14458f8636d860ae25b10387fa6f7f2ef6ef9) )

	ROM_REGION(0x20, "prom", 0)
	ROM_LOAD( "74904.ic25", 0x00, 0x20, NO_DUMP ) // TBP18S030 or equivalent
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY     FULLNAME   FLAGS */
COMP( 1979, mcb216, 0,      0,      mcb216,  mcb216, mcb216_state, empty_init, "Cromemco", "MCB-216", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1977, cb308,  mcb216, 0,      cb308,   mcb216, cb308_state,  empty_init, "Cromemco", "CB-308",  MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



mccpm.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

mc-CP/M-Computer

2010-08-31 Skeleton driver.
2010-11-18 Connected to a terminal
2011-09-28 Added more bioses

Some Monitor commands (varies between versions):

B - lock keyboard (^N to regain control)
E - prints a number
I - Select boot drive/set parameters - then it attempts to boot
K,O - display version header
N - newline
Z - print 'EFFF'

URL for v3.4: http://www.hanshehl.de/mc-prog.htm (German language)

Although the manual specifies ports 40-44 for the FDC, all bios versions
support it at 30-34 as well.

No software to test with, so we'll never know if the FDC works.

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"
#include "machine/f4702.h"
#include "machine/wd_fdc.h"
#include "bus/rs232/rs232.h"
#include "imagedev/floppy.h"


namespace {

class mccpm_state : public driver_device
{
public:
	mccpm_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_fdc(*this, "fdc")
		, m_fdd(*this, "fdc:%u", 0U)
		, m_brg(*this, "brg%u", 1U)
		, m_view(*this, "view")
	{ }

	void mccpm(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	void port44_w(u8);
	u8 port44_r();
	void fdc_irq(bool);
	template <int N> void bd_q_w(offs_t offset, u8 data);

	u8 m_fdc_status = 0U;
	floppy_image_device *m_floppy = 0;

	required_device<cpu_device> m_maincpu;
	required_device<fd1797_device> m_fdc;
	required_device_array<floppy_connector, 2> m_fdd;
	required_device_array<f4702_device, 2> m_brg;
	memory_view m_view;
};



void mccpm_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).view(m_view);
	m_view[0](0x0000, 0x3fff).rom().region("maincpu", 0);
	m_view[0](0x4000, 0x7fff).lr8(NAME([this] () { if (!machine().side_effects_disabled()) m_view.select(1); return 0xff; }));
	m_view[1](0x0000, 0x7fff).ram();
	map(0x8000, 0xffff).ram();
}

void mccpm_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x40, 0x43).rw(m_fdc, FUNC(fd1797_device::read), FUNC(fd1797_device::write));
	map(0x44, 0x44).rw(FUNC(mccpm_state::port44_r), FUNC(mccpm_state::port44_w));
	map(0xf0, 0xf3).rw("sio", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0xf4, 0xf7).rw("pio", FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
}

/* Input ports */
static INPUT_PORTS_START( mccpm )
	PORT_START("BAUD1")
	PORT_DIPNAME(0xf, 0x8, "Baud Rate B (Printer)") PORT_DIPLOCATION("S:5,6,7,8")
	PORT_DIPSETTING(0x2, "50")
	PORT_DIPSETTING(0x3, "75")
	PORT_DIPSETTING(0xf, "110")
	PORT_DIPSETTING(0x4, "134.5")
	PORT_DIPSETTING(0xe, "150")
	PORT_DIPSETTING(0x5, "200")
	PORT_DIPSETTING(0xd, "300")
	PORT_DIPSETTING(0x6, "600")
	PORT_DIPSETTING(0xb, "1200")
	PORT_DIPSETTING(0xa, "1800")
	PORT_DIPSETTING(0x7, "2400")
	PORT_DIPSETTING(0x9, "4800")
	PORT_DIPSETTING(0x8, "9600")
	PORT_DIPSETTING(0x0, "19200")

	PORT_START("BAUD2")
	PORT_DIPNAME(0xf, 0x8, "Baud Rate A (Terminal)") PORT_DIPLOCATION("S:1,2,3,4")
	PORT_DIPSETTING(0x2, "50")
	PORT_DIPSETTING(0x3, "75")
	PORT_DIPSETTING(0xf, "110")
	PORT_DIPSETTING(0x4, "134.5")
	PORT_DIPSETTING(0xe, "150")
	PORT_DIPSETTING(0x5, "200")
	PORT_DIPSETTING(0xd, "300")
	PORT_DIPSETTING(0x6, "600")
	PORT_DIPSETTING(0xb, "1200")
	PORT_DIPSETTING(0xa, "1800")
	PORT_DIPSETTING(0x7, "2400")
	PORT_DIPSETTING(0x9, "4800")
	PORT_DIPSETTING(0x8, "9600")
	PORT_DIPSETTING(0x0, "19200")
INPUT_PORTS_END

void mccpm_state::port44_w(u8 data)
{
	m_floppy = nullptr;
	if (BIT(data, 1))
		m_floppy = m_fdd[1]->get_device();
	else
	if (BIT(data, 0))
		m_floppy = m_fdd[0]->get_device();

	m_fdc->set_floppy(m_floppy);

	if (m_floppy)
	{
		m_floppy->mon_w(0);
		m_fdc->dden_w(!BIT(data, 4));   // 0 = FM; 1 = MFM
	}
	// side select comes from fdc pin 25
	m_fdc->set_unscaled_clock(BIT(data, 5) ? 1e6 : 2e6);  // 13 or 20cm clock select
	m_maincpu->set_input_line_vector(0, 0xD7 ); // Z80 - jump to 0x0010 upon interrupt acknowledge IM 0 (or should it say 0x10?)
}

u8 mccpm_state::port44_r()
{
	// bit 4 is floppy hld_r, not yet emulated.
	// So we assume the head is loaded if the drive is selected.
	if (m_floppy)
		return m_fdc_status | 4;
	else
		return m_fdc_status;
}

void mccpm_state::fdc_irq(bool state)
{
	m_fdc_status = (m_fdc_status & 0xfd) | (state ? 2 : 0);
	m_maincpu->set_input_line(0, state ? ASSERT_LINE : CLEAR_LINE);
}

template <int N> void mccpm_state::bd_q_w(offs_t offset, u8 data)
{
	// "19200 extern" obtained by connecting Q2 to Im
	m_brg[N]->im_w(BIT(offset, 2));
}

void mccpm_state::machine_reset()
{
	m_view.select(0);
	m_fdc_status = 0xfb;
	m_floppy = nullptr;
}

void mccpm_state::machine_start()
{
	save_item(NAME(m_fdc_status));
}

static void flop_types(device_slot_interface &device)
{
	device.option_add("flop", FLOPPY_525_QD);
}

void mccpm_state::mccpm(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &mccpm_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &mccpm_state::io_map);

	/* Devices */
	// clock supplied by pair of HD4702 baud rate generators
	F4702(config, m_brg[0], 2.4576_MHz_XTAL); // XTAL connected to Ix/Ox
	m_brg[0]->s_callback().set_ioport("BAUD1");
	m_brg[0]->z_callback().set("sio", FUNC(z80sio_device::rxtxcb_w));
	m_brg[0]->z_callback().append(FUNC(mccpm_state::bd_q_w<0>));

	F4702(config, m_brg[1], 2.4576_MHz_XTAL); // Cp connected to first BRG's CO
	m_brg[1]->s_callback().set_ioport("BAUD2");
	m_brg[1]->z_callback().set("sio", FUNC(z80sio_device::txca_w));
	m_brg[1]->z_callback().append("sio", FUNC(z80sio_device::rxca_w));
	m_brg[1]->z_callback().append(FUNC(mccpm_state::bd_q_w<1>));

	// Ch A: terminal; Ch B: printer
	z80sio_device& sio(Z80SIO(config, "sio", XTAL(4'000'000)));
	sio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	sio.out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
	sio.out_dtra_callback().set("rs232a", FUNC(rs232_port_device::write_dtr));
	sio.out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts));
	sio.out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
	sio.out_dtrb_callback().set("rs232b", FUNC(rs232_port_device::write_dtr));
	sio.out_rtsb_callback().set("rs232b", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, "terminal"));
	rs232a.rxd_handler().set("sio", FUNC(z80sio_device::rxa_w));
	rs232a.cts_handler().set("sio", FUNC(z80sio_device::ctsa_w));
	rs232a.dcd_handler().set("sio", FUNC(z80sio_device::dcda_w));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set("sio", FUNC(z80sio_device::rxb_w));
	rs232b.cts_handler().set("sio", FUNC(z80sio_device::ctsb_w));
	rs232b.dcd_handler().set("sio", FUNC(z80sio_device::dcdb_w));

	Z80PIO(config, "pio", XTAL(4'000'000));

	FD1797(config, m_fdc, 8_MHz_XTAL / 8);
	m_fdc->intrq_wr_callback().set([this] (bool state) { mccpm_state::fdc_irq(state); });
	m_fdc->drq_wr_callback().set([this] (u8 state) { m_fdc_status = (m_fdc_status & 0xfe) | (state ? 1 : 0); });
	FLOPPY_CONNECTOR(config, "fdc:0", flop_types, "flop", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", flop_types, "flop", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
}

/* ROM definition */
ROM_START( mccpm )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "v36", "V3.6")
	ROMX_LOAD("mon36.j15",   0x0000, 0x1000, CRC(9c441537) SHA1(f95bad52d9392b8fc9d9b8779b7b861672a0022b), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v34", "V3.4")
	ROMX_LOAD("monhemc.bin", 0x0000, 0x1000, CRC(cae7b56e) SHA1(1f40be9491a595e6705099a452743cc0d49bfce8), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v34a", "V3.4 (alt)")
	ROMX_LOAD("mc01mon.bin", 0x0000, 0x0d00, CRC(d1c89043) SHA1(f52a0ed3793dde0de74596be7339233b6a1770af), ROM_BIOS(2))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY                         FULLNAME            FLAGS
COMP( 1981, mccpm, 0,      0,      mccpm,   mccpm, mccpm_state, empty_init, "GRAF Elektronik Systeme GmbH", "mc-CP/M-Computer", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



mcg85.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/******************************************************************************

    Atlantis Computers MCG-85

    After RESET press SPACE on terminal for Expeditor to detect baud rate (50-19.2K).

******************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8155.h"


namespace {

class mcg85_state : public driver_device
{
public:
	mcg85_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void mcg85(machine_config &config);

private:
	required_device<i8085a_cpu_device> m_maincpu;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
};


void mcg85_state::mem_map(address_map &map)
{
	map(0x0000, 0x0fff).rom();
	map(0x1000, 0x1fff).ram(); // optional RAM
	map(0x2000, 0x20ff).mirror(0x700).rw("i8155", FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w));
}

void mcg85_state::io_map(address_map &map)
{
	map(0x20, 0x27).rw("i8155", FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
}


static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END


void mcg85_state::mcg85(machine_config &config)
{
	I8085A(config, m_maincpu, 6.144_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mcg85_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &mcg85_state::io_map);
	m_maincpu->in_sid_func().set("rs232", FUNC(rs232_port_device::rxd_r));
	m_maincpu->out_sod_func().set("rs232", FUNC(rs232_port_device::write_txd));

	I8155(config, "i8155", 6.144_MHz_XTAL / 2);

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));
}


ROM_START( mcg85 )
	ROM_REGION(0x1000, "maincpu", ROMREGION_ERASE00)
	ROM_SYSTEM_BIOS( 0, "24", "Expeditor Monitor V2.4" )
	ROMX_LOAD("expdr_monitor_2.4.bin", 0x0000, 0x0800, CRC(54a75a26) SHA1(f19294599e8b89a7036ffd1b7a97026d71837c5a), ROM_BIOS(0))
ROM_END

} // anonymous namespace


//    YEAR  NAME    PARENT  COMPAT  MACHINE   INPUT   CLASS         INIT         COMPANY                FULLNAME   FLAGS
COMP( 1981, mcg85,  0,      0,      mcg85,    0,      mcg85_state,  empty_init,  "Atlantis Computers",  "MCG-85",  MACHINE_NO_SOUND_HW )



mcm70.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/*******************************************************************************************************

    MCM/70

    TODO:
    - at power on need to press START to use (need schematic to know what's happening)
    - cassette (x2) handling, digital with pre-recorded clock track.
    - MCP-132 printer/plotter device (Diablo HyType I).
    - serial communications interface unit (terminal and modem ports).
    - artwork to show keyboard layout.

******************************************************************************************************/

#include "emu.h"
#include "cpu/i8008/i8008.h"

#include "emupal.h"
#include "screen.h"

#define LOG_DEVICE (1U << 1)

#define VERBOSE (LOG_DEVICE)
#include "logmacro.h"


namespace {

class mcm70_state : public driver_device
{
public:
	mcm70_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rombank(*this, "rombank")
		, m_ram(*this, "ram")
		, m_kbd(*this, "COL%u", 0U)
		, m_kbd_mod(*this, "MODIFIERS")
		, m_palette(*this, "palette")
		, m_device_select(0)
	{ }

	void mcm70(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(start);

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

private:
	uint8_t status_r(offs_t offset);
	void devsel_w(offs_t offset, uint8_t data);
	void device_w(offs_t offset, uint8_t data);
	uint8_t device_r(offs_t offset);

	uint8_t keyboard_r(offs_t offset);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	void mcm70_palette(palette_device &palette) const;
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	required_device<cpu_device> m_maincpu;
	required_memory_bank m_rombank;
	required_shared_ptr<uint8_t> m_ram;
	required_ioport_array<8> m_kbd;
	required_ioport m_kbd_mod;
	required_device<palette_device> m_palette;

	uint8_t m_device_select;
};


void mcm70_state::mem_map(address_map &map)
{
	map(0x0000, 0x17ff).rom().region("maincpu", 0);
	map(0x1800, 0x1fff).bankr("rombank");
	map(0x2000, 0x3fff).ram().share("ram");
}

void mcm70_state::io_map(address_map &map)
{
	map(0x00, 0x00).select(0xffe0).r(FUNC(mcm70_state::keyboard_r));
	map(0x01, 0x01).select(0xffe0).r(FUNC(mcm70_state::status_r));
	map(0x02, 0x03).mirror(0xffe0).r(FUNC(mcm70_state::device_r));
	map(0x08, 0x08).mirror(0xffe0).lw8(NAME([this](uint8_t data) { m_rombank->set_entry(data >> 4); }));
	map(0x09, 0x09).select(0xffe0).w(FUNC(mcm70_state::devsel_w));
	map(0x0a, 0x0b).mirror(0xffe0).w(FUNC(mcm70_state::device_w));
	map(0x1f, 0x1f).mirror(0xffe0).nopw(); // output single column from display
}


uint8_t mcm70_state::status_r(offs_t offset)
{
	return 0x00;
}


void mcm70_state::devsel_w(offs_t offset, uint8_t data)
{
	m_device_select = offset >> 8;
	std::string device_selected;

	switch (m_device_select)
	{
	case 0x00: // None
		device_selected = "None";
		break;

	case 0x01: // Printer (MCP-132)
		device_selected = "Printer";
		break;

	case 0xc8: // Cassette 1
		device_selected = "Cassette 1";
		break;

	case 0xc9: // Cassette 2
		device_selected = "Cassette 2";
		break;

	default:
		device_selected = "Unknown";
		break;
	}

	LOGMASKED(LOG_DEVICE, "devsel_w: %02x %s\n", data, device_selected);
}

uint8_t mcm70_state::device_r(offs_t offset)
{
	uint8_t data = 0x00;

	switch (m_device_select)
	{
	case 0x01: // Printer
		switch (offset & 1)
		{
		case 0: // status
			// bit 0 Printer powered and ready
			// bit 1 Check condition (printer crashed)
			// bit 2 Not used
			// bit 3 Not used
			// bit 4 Ribbon up
			// bit 5 Character print ready
			// bit 6 Carriage ready
			// bit 7 Paper feed ready
			break;

		case 1: // data
			break;
		}
		break;

	case 0xc8: // Cassette 1
		break;

	case 0xc9: // Cassette 2
		break;
	}

	LOGMASKED(LOG_DEVICE, "device_r: %s %02x\n", (offset & 1) ? "data" : "status", data);

	return data;
}

void mcm70_state::device_w(offs_t offset, uint8_t data)
{
	switch (m_device_select)
	{
	case 0x01: // Printer
		break;

	case 0xc8: // Cassette 1
		break;

	case 0xc9: // Cassette 2
		break;
	}

	LOGMASKED(LOG_DEVICE, "device_w: %s %02x\n", (offset & 1) ? "data" : "control", data);
}


static INPUT_PORTS_START( mcm70 )
	PORT_START("COL0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)          PORT_CHAR('+') PORT_CHAR('-')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)      PORT_CHAR(8)   PORT_NAME("BACK SPACE")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)              PORT_CHAR('9') PORT_CHAR(0x2228)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)              PORT_CHAR('7') PORT_CHAR('>')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)              PORT_CHAR('5') PORT_CHAR('=')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)              PORT_CHAR('3') PORT_CHAR('<')

	PORT_START("COL1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)          PORT_CHAR(13)  PORT_CHAR(13) PORT_NAME("RETURN")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)              PORT_CHAR('P') PORT_CHAR('*')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)              PORT_CHAR('I') PORT_CHAR(0x2373)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)              PORT_CHAR('Y') PORT_CHAR(0x2191)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)              PORT_CHAR('R') PORT_CHAR(0x2374)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)              PORT_CHAR('W') PORT_CHAR(0x2375)

	PORT_START("COL2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)          PORT_CHAR(']') PORT_CHAR(')')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)              PORT_CHAR('L') PORT_CHAR(0x2395)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)              PORT_CHAR('J') PORT_CHAR(0x2218)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)              PORT_CHAR('G') PORT_CHAR(0x2207)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)              PORT_CHAR('D') PORT_CHAR(0x230a)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)              PORT_CHAR('A') PORT_CHAR(0x237a)

	PORT_START("COL3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)          PORT_CHAR('/') PORT_CHAR('\\')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)          PORT_CHAR(',') PORT_CHAR(';')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)              PORT_CHAR('N') PORT_CHAR(0x22a4)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)              PORT_CHAR('V') PORT_CHAR(0x222a)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)              PORT_CHAR('X') PORT_CHAR(0x2283)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)          PORT_CHAR(' ') PORT_NAME("SPACE")

	PORT_START("COL4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)          PORT_CHAR('.') PORT_CHAR(':')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)             PORT_CHAR('M') PORT_CHAR('|')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)             PORT_CHAR('B') PORT_CHAR(0x22a5)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)             PORT_CHAR('C') PORT_CHAR(0x2229)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)             PORT_CHAR('Z') PORT_CHAR(0x2282)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)             PORT_CHAR('Q') PORT_CHAR('?')

	PORT_START("COL5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)         PORT_CHAR('[') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)             PORT_CHAR('K') PORT_CHAR('\'')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)             PORT_CHAR('H') PORT_CHAR(0x2206)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)             PORT_CHAR('F') PORT_CHAR('_')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)             PORT_CHAR('S') PORT_CHAR(0x2308)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)             PORT_CHAR('2') PORT_CHAR(0x203e)

	PORT_START("COL6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)     PORT_CHAR(0x2190) PORT_CHAR(0x2192)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)             PORT_CHAR('O') PORT_CHAR(0x25cb)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)             PORT_CHAR('U') PORT_CHAR(0x2193)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)             PORT_CHAR('T') PORT_CHAR('~')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)             PORT_CHAR('E') PORT_CHAR(0x2208)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)             PORT_CHAR('1') PORT_CHAR(0xa8)

	PORT_START("COL7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)        PORT_CHAR('x') PORT_CHAR(0xf7)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)             PORT_CHAR('0') PORT_CHAR(0x2227)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)             PORT_CHAR('8') PORT_CHAR(0x2260)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)             PORT_CHAR('6') PORT_CHAR(0x2265)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)             PORT_CHAR('4') PORT_CHAR(0x2264)

	PORT_START("MODIFIERS")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_NAME("CTRL")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)   PORT_CODE(KEYCODE_RSHIFT)   PORT_NAME("SHIFT")

	PORT_START("START")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_NAME("START") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mcm70_state::start), 0)
INPUT_PORTS_END


INPUT_CHANGED_MEMBER(mcm70_state::start)
{
}


uint8_t mcm70_state::keyboard_r(offs_t offset)
{
	uint8_t data = m_kbd_mod->read();

	switch (offset >> 8)
	{
	case 0x01: data |= m_kbd[0]->read(); break;
	case 0x02: data |= m_kbd[1]->read(); break;
	case 0x04: data |= m_kbd[2]->read(); break;
	case 0x08: data |= m_kbd[3]->read(); break;
	case 0x10: data |= m_kbd[4]->read(); break;
	case 0x20: data |= m_kbd[5]->read(); break;
	case 0x40: data |= m_kbd[6]->read(); break;
	case 0x80: data |= m_kbd[7]->read(); break;
	}

	return data;
}


void mcm70_state::machine_start()
{
	m_rombank->configure_entries(0, 16, memregion("banked")->base(), 0x800);
	m_rombank->set_entry(0);

	save_item(NAME(m_device_select));
}

void mcm70_state::machine_reset()
{
	m_device_select = 0x00;
}


uint32_t mcm70_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	// Burroughs Self-Scan Panel Display C4047
	// The panel displays 222 columns of 7 dots, 32 characters of 5x7 with a blank column between each.
	// Each column of the display is controlled by a byte of video RAM

	pen_t const *const pen = m_palette->pens();

	for (int x = 0; x < 222; x++)
	{
		uint8_t data = m_ram[0x0021 + x];

		for (int y = 0; y < 7; y++)
		{
			int32_t xl = x * 4;
			int32_t yl = y * 4;

			// pixel
			if (BIT(data, y)) // on
			{
				bitmap.pix(yl,     xl)     = pen[1];
				bitmap.pix(yl,     xl + 1) = pen[2];
				bitmap.pix(yl,     xl + 2) = pen[1];
				bitmap.pix(yl + 1, xl)     = pen[2];
				bitmap.pix(yl + 1, xl + 1) = pen[3];
				bitmap.pix(yl + 1, xl + 2) = pen[2];
				bitmap.pix(yl + 2, xl)     = pen[1];
				bitmap.pix(yl + 2, xl + 1) = pen[2];
				bitmap.pix(yl + 2, xl + 2) = pen[1];
			}
			else // off
			{
				bitmap.pix(yl,     xl)     = pen[4];
				bitmap.pix(yl,     xl + 1) = pen[4];
				bitmap.pix(yl,     xl + 2) = pen[4];
				bitmap.pix(yl + 1, xl)     = pen[4];
				bitmap.pix(yl + 1, xl + 1) = pen[4];
				bitmap.pix(yl + 1, xl + 2) = pen[4];
				bitmap.pix(yl + 2, xl)     = pen[4];
				bitmap.pix(yl + 2, xl + 1) = pen[4];
				bitmap.pix(yl + 2, xl + 2) = pen[4];
			}

			// gap around pixel
			if (x < 221)
			{
				bitmap.pix(yl,     xl + 3) = pen[0];
				bitmap.pix(yl + 1, xl + 3) = pen[0];
				bitmap.pix(yl + 2, xl + 3) = pen[0];
			}
			if (y < 6)
			{
				bitmap.pix(yl + 3, xl    ) = pen[0];
				bitmap.pix(yl + 3, xl + 1) = pen[0];
				bitmap.pix(yl + 3, xl + 2) = pen[0];
			}
			if (x < 221 && y < 6)
			{
				bitmap.pix(yl + 3, xl + 3) = pen[0];
			}
		}
	}

	return 0;
}

void mcm70_state::mcm70_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(41, 24, 26));    // background
	palette.set_pen_color(1, rgb_t(174, 35, 35));   // cell on outer corner
	palette.set_pen_color(2, rgb_t(237, 79, 80));   // cell on outer center
	palette.set_pen_color(3, rgb_t(255, 228, 238)); // cell on center
	palette.set_pen_color(4, rgb_t(79, 44, 40));    // cell off
}


void mcm70_state::mcm70(machine_config &config)
{
	I8008(config, m_maincpu, 400000); // TODO: verify clock
	m_maincpu->set_addrmap(AS_PROGRAM, &mcm70_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &mcm70_state::io_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_screen_update(FUNC(mcm70_state::screen_update));
	screen.set_size(887, 27);
	screen.set_visarea_full();
	PALETTE(config, m_palette, FUNC(mcm70_state::mcm70_palette), 5);
}


ROM_START( mcm70 )
	ROM_REGION(0x1800, "maincpu", 0)
	ROM_LOAD( "mcm70_m1.bin", 0x0000, 0x0800, CRC(e16ea421) SHA1(c3b8608100689fca56dfc43703b6f2e1c5dc5425))
	ROM_LOAD( "mcm70_m2.bin", 0x0800, 0x0800, CRC(dde53a75) SHA1(f9d0946bea7dc2f6c6697636061a6308236e0436))
	ROM_LOAD( "mcm70_m3.bin", 0x1000, 0x0800, CRC(319516f4) SHA1(d63f23173bc86a872d9b8a908e91f73e61b51ed8))

	ROM_REGION(0x8000, "banked", ROMREGION_ERASEFF)
	ROM_LOAD( "mcm70_b0.bin", 0x0000, 0x0800, CRC(71f0f656) SHA1(48d444b389135cb63c71b8f8f3d35433fa2d4424))
	ROM_LOAD( "mcm70_b1.bin", 0x0800, 0x0800, CRC(9167509e) SHA1(ece999709f6493c23e79f080f565fb89b87f71aa))
	ROM_LOAD( "mcm70_b2.bin", 0x1000, 0x0800, CRC(b9b60da0) SHA1(862f8c9f48f80bfade1be0adf8e53f109cb0b5b3))
	ROM_LOAD( "mcm70_b3.bin", 0x1800, 0x0800, CRC(cfa0cffc) SHA1(dbeda9396422122b47321723a6425815dd8d5ac5))
	ROM_LOAD( "mcm70_b4.bin", 0x2000, 0x0800, CRC(9f67282c) SHA1(23707721e330d10f3c157bb95fb6329fb857b05c))
	ROM_LOAD( "mcm70_b5.bin", 0x2800, 0x0800, CRC(766c3646) SHA1(b96819eff1b711a677a97f650f12467e23107406))
	ROM_LOAD( "mcm70_b6.bin", 0x3000, 0x0800, CRC(c8f1eb05) SHA1(92aaca4f15ce971e5b45dd5fa3ff60206dc59015))
	ROM_LOAD( "mcm70_b7.bin", 0x3800, 0x0800, CRC(520e8238) SHA1(132b5ae1666da7cad7af386e3971c2dcb92a5316))
	ROM_LOAD( "mcm70_b8.bin", 0x4000, 0x0800, CRC(1128f1ed) SHA1(4d667cd28d5a4a424b827fc7f75b8bfe62f1c162))
	ROM_LOAD( "mcm70_b9.bin", 0x4800, 0x0800, CRC(11ac4225) SHA1(b37610479b48027c9bdf9e5e72556404a28c2987))
	ROM_LOAD( "mcm70_ba.bin", 0x5000, 0x0800, CRC(14eb2a1f) SHA1(9d7959b918f148b70f4fcb74222a4287a8721c63))
	ROM_LOAD( "mcm70_bb.bin", 0x5800, 0x0800, CRC(0685f8b0) SHA1(4fd6abc6f9ff4e02690e58a349a398a20534c788))
	ROM_LOAD( "mcm70_bc.bin", 0x6000, 0x0800, CRC(1ae19741) SHA1(c22dd49180999d70e6fd0c7d89a7ff6cc5eed974))
ROM_END

} // anonymous namespace

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS        INIT        COMPANY                    FULLNAME   FLAGS
COMP( 1974, mcm70,  0,      0,      mcm70,   mcm70,  mcm70_state, empty_init, "Micro Computer Machines", "MCM/70",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



mdconsole.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

#include "emu.h"
#include "mdconsole.h"

#include "bus/generic/carts.h"
#include "bus/sms_ctrl/controllers.h"
#include "bus/generic/slot.h"
#include "imagedev/cdromimg.h"
#include "sound/sn76496.h"

#include "softlist.h"

#include "multibyte.h"


/*************************************
 *
 *  Input handlers
 *
 *************************************/

void md_cons_state::md_ctrl_ports(machine_config &config)
{
	SMS_CONTROL_PORT(config, m_ctrl_ports[0], sms_control_port_devices, SMS_CTRL_OPTION_MD_PAD);
	m_ctrl_ports[0]->th_handler().set(m_ioports[0], FUNC(megadrive_io_port_device::th_w));
	m_ctrl_ports[0]->set_screen(m_screen);

	m_ioports[0]->set_in_handler(m_ctrl_ports[0], FUNC(sms_control_port_device::in_r));
	m_ioports[0]->set_out_handler(m_ctrl_ports[0], FUNC(sms_control_port_device::out_w));

	SMS_CONTROL_PORT(config, m_ctrl_ports[1], sms_control_port_devices, SMS_CTRL_OPTION_MD_PAD);
	m_ctrl_ports[1]->th_handler().set(m_ioports[1], FUNC(megadrive_io_port_device::th_w));
	m_ctrl_ports[1]->set_screen(m_screen);

	m_ioports[1]->set_in_handler(m_ctrl_ports[1], FUNC(sms_control_port_device::in_r));
	m_ioports[1]->set_out_handler(m_ctrl_ports[1], FUNC(sms_control_port_device::out_w));
}

void md_cons_state::md_exp_port(machine_config &config)
{
	SMS_CONTROL_PORT(config, m_ctrl_ports[2], sms_control_port_devices, nullptr);
	m_ctrl_ports[2]->th_handler().set(m_ioports[2], FUNC(megadrive_io_port_device::th_w));
	m_ctrl_ports[2]->set_screen(m_screen);

	m_ioports[2]->set_in_handler(m_ctrl_ports[2], FUNC(sms_control_port_device::in_r));
	m_ioports[2]->set_out_handler(m_ctrl_ports[2], FUNC(sms_control_port_device::out_w));
}


/*************************************
 *
 *  Input ports
 *
 *************************************/


static INPUT_PORTS_START( md )
	PORT_START("RESET")     // Buttons on Mega Drive console
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_NAME("Reset Button") PORT_IMPULSE(1) // reset, resets 68k (and..?)
INPUT_PORTS_END

static INPUT_PORTS_START( gen_nomd )
	PORT_START("RESET")     // No reset button
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


/*************************************
 *
 *  Machine driver
 *
 *************************************/

void md_cons_state::machine_start()
{
	m_vdp->stop_timers();

	if (m_cart)
		m_cart->save_nvram();

	m_genz80.z80_run_timer = timer_alloc(FUNC(md_cons_state::megadriv_z80_run_state), this);
}

void md_cons_state::install_cartslot()
{
	if (m_cart)
	{
		// for now m_cartslot is only in MD and not 32x and SegaCD
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x000000, 0x7fffff, read16sm_delegate(*m_cart, FUNC(base_md_cart_slot_device::read)));
		m_maincpu->space(AS_PROGRAM).install_write_handler(0x000000, 0x7fffff, write16s_delegate(*m_cart, FUNC(base_md_cart_slot_device::write)));
		m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0xa13000, 0xa130ff, read16sm_delegate(*m_cart, FUNC(base_md_cart_slot_device::read_a13)), write16sm_delegate(*m_cart, FUNC(base_md_cart_slot_device::write_a13)));
		m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0xa15000, 0xa150ff, read16sm_delegate(*m_cart, FUNC(base_md_cart_slot_device::read_a15)), write16sm_delegate(*m_cart, FUNC(base_md_cart_slot_device::write_a15)));
//      m_maincpu->space(AS_PROGRAM).install_write_handler(0xa14000, 0xa14003, write16sm_delegate(*m_cart, FUNC(base_md_cart_slot_device::write_tmss_bank)));
	}
}

uint16_t md_cons_state::tmss_r(offs_t offset)
{
	if (offset < 0x4000 / 2)
		return m_tmss[offset];

	return 0xffff;
}

void md_cons_state::tmss_swap_w(uint16_t data)
{
	if (data & 0x0001)
	{
		install_cartslot();
		m_maincpu->space(AS_PROGRAM).install_write_handler(0xa14100, 0xa14101, write16smo_delegate(*this, FUNC(md_cons_state::tmss_swap_w)));
	}
	else
	{
		install_tmss();
	}
}

void md_cons_state::install_tmss()
{
	m_maincpu->space(AS_PROGRAM).unmap_readwrite(0x000000, 0x7fffff);
	m_maincpu->space(AS_PROGRAM).install_read_handler(0x000000, 0x7fffff, read16sm_delegate(*this, FUNC(md_cons_state::tmss_r)));

	m_maincpu->space(AS_PROGRAM).install_write_handler(0xa14100, 0xa14101, write16smo_delegate(*this, FUNC(md_cons_state::tmss_swap_w)));

}


void md_cons_slot_state::machine_start()
{
	md_cons_state::machine_start();

	// the SVP introduces some kind of DMA 'lag', which we have to compensate for, this is obvious even on gfx DMAd from ROM (the Speedometer)
	if (m_cart && (m_cart->get_type() == SEGA_SVP))
		m_vdp->set_dma_delay(2);

	if (m_tmss)
	{
		install_tmss();
	}
	else
	{
		install_cartslot();
	}
}

void md_cons_state::machine_reset()
{
	md_base_state::machine_reset();

	// if the system has a 32x, pause the extra CPUs until they are actually turned on
	if (m_32x)
		m_32x->pause_cpu();
}

void md_cons_cd_state::machine_start()
{
	md_cons_state::machine_start();

	// the segaCD introduces some kind of DMA 'lag', which we have to compensate for,
	// at least when reading wordram? we might need to check what mode we're in the DMA...
	m_vdp->set_dma_delay(2);
}

// same as screen_eof_megadriv but with addition of 32x and SegaCD/MegaCD pieces
void md_cons_state::screen_vblank_console(int state)
{
	if (m_io_reset.read_safe(0) & 0x01)
		m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);

	// rising edge
	if (state)
	{
		if (!m_vdp->m_use_alt_timing)
		{
			bool mode3 = (m_vdp->get_imode() == 3);
			m_vdp->vdp_handle_eof();
			m_vdp->m_megadriv_scanline_timer->adjust(attotime::zero);

			if (m_32x)
				m_32x->screen_eof(mode3);

			if (m_segacd)
				m_segacd->update_total_scanlines(mode3);
		}
	}
}

void md_cons_slot_state::ms_megadriv(machine_config &config)
{
	md_ntsc(config);

	m_screen->screen_vblank().set(FUNC(md_cons_slot_state::screen_vblank_console));

	md_ctrl_ports(config);
	md_exp_port(config);

	MD_CART_SLOT(config, m_cart, md_cart, nullptr).set_must_be_loaded(true);
	SOFTWARE_LIST(config, "cart_list").set_original("megadriv").set_filter("NTSC-U");
}

void md_cons_slot_state::ms_megadrivj(machine_config &config)
{
	ms_megadriv(config);

	subdevice<software_list_device>("cart_list")->set_filter("NTSC-J");
}

void md_cons_slot_state::ms_megadpal(machine_config &config)
{
	md_pal(config);

	m_screen->screen_vblank().set(FUNC(md_cons_slot_state::screen_vblank_console));

	md_ctrl_ports(config);
	md_exp_port(config);

	MD_CART_SLOT(config, m_cart, md_cart, nullptr).set_must_be_loaded(true);
	SOFTWARE_LIST(config, "cart_list").set_original("megadriv").set_filter("PAL");
}

void md_cons_slot_state::ms_megadriv2(machine_config &config)
{
	md2_ntsc(config);

	m_screen->screen_vblank().set(FUNC(md_cons_slot_state::screen_vblank_console));

	md_ctrl_ports(config);

	MD_CART_SLOT(config, m_cart, md_cart, nullptr).set_must_be_loaded(true);
	SOFTWARE_LIST(config, "cart_list").set_original("megadriv").set_filter("NTSC-U");
}

void md_cons_slot_state::ms_nomad(machine_config &config)
{
	ms_megadriv2(config);

	// P1 controller is integrated
	m_ctrl_ports[0]->set_default_option(SMS_CTRL_OPTION_MD_6BUTTON);
	m_ctrl_ports[0]->set_fixed(true);
}

void md_cons_slot_state::ms_megajet(machine_config &config)
{
	ms_nomad(config);

	subdevice<software_list_device>("cart_list")->set_filter("NTSC-J");
}

void md_cons_slot_state::genesis_tmss(machine_config &config)
{
	ms_megadriv(config);

	subdevice<software_list_device>("cart_list")->set_filter("NTSC-U,TMSS");
}

/*************************************
 *
 *  ROM definition(s)
 *
 *************************************/


/* we don't use the BIOS ROM (it's not needed and only provides security on early models) */

ROM_START(genesis)
	ROM_REGION(MD_CPU_REGION_SIZE, "maincpu", ROMREGION_ERASEFF)
	ROM_REGION( 0x10000, "soundcpu", ROMREGION_ERASEFF)
ROM_END

ROM_START(megadriv)
	ROM_REGION(MD_CPU_REGION_SIZE, "maincpu", ROMREGION_ERASEFF)
	ROM_REGION( 0x10000, "soundcpu", ROMREGION_ERASEFF)
ROM_END

ROM_START(megadrij)
	ROM_REGION(MD_CPU_REGION_SIZE, "maincpu", ROMREGION_ERASEFF)
	ROM_REGION( 0x10000, "soundcpu", ROMREGION_ERASEFF)
ROM_END

ROM_START(genesis_tmss)
	ROM_REGION(MD_CPU_REGION_SIZE, "maincpu", ROMREGION_ERASEFF)
	ROM_REGION( 0x10000, "soundcpu", ROMREGION_ERASEFF)

	ROM_REGION16_BE(0x4000, "tmss", ROMREGION_ERASEFF)
	ROM_LOAD( "tmss_usa.bin", 0x0000,  0x4000, CRC(5f5e64eb) SHA1(453fca4e1db6fae4a10657c4451bccbb71955628) )
ROM_END


ROM_START(megajet)
	ROM_REGION(MD_CPU_REGION_SIZE, "maincpu", ROMREGION_ERASEFF)
	ROM_REGION( 0x10000, "soundcpu", ROMREGION_ERASEFF)
ROM_END

ROM_START(gen_nomd)
	ROM_REGION(MD_CPU_REGION_SIZE, "maincpu", ROMREGION_ERASEFF)
	ROM_REGION( 0x10000, "soundcpu", ROMREGION_ERASEFF)
ROM_END

/*************************************
 *
 *  Driver initialization
 *
 *************************************/

void md_cons_state::init_genesis()
{
	init_megadriv();

	if (m_32x)
	{
		m_32x->set_32x_pal(false);
		m_32x->set_framerate(60);
		m_32x->set_total_scanlines(262);
	}
	if (m_segacd)
	{
		m_segacd->set_framerate(60);
		m_segacd->set_total_scanlines(262);
	}

	m_version_hi_nibble = 0x80; // Export NTSC
	if (!m_segacd)
		m_version_hi_nibble |= 0x20;
}

void md_cons_state::init_genesis_tmss()
{
	init_genesis();
	m_version_lo_nibble = 1;
}

void md_cons_state::init_md_eur()
{
	init_megadrie();

	if (m_32x)
	{
		m_32x->set_32x_pal(true);
		m_32x->set_framerate(50);
		m_32x->set_total_scanlines(313);
	}
	if (m_segacd)
	{
		m_segacd->set_framerate(50);
		m_segacd->set_total_scanlines(313);
	}

	m_version_hi_nibble = 0xc0; // Export PAL
	if (!m_segacd)
		m_version_hi_nibble |= 0x20;
}

void md_cons_state::init_md_jpn()
{
	init_megadrij();

	if (m_32x)
	{
		m_32x->set_32x_pal(false);
		m_32x->set_framerate(60);
		m_32x->set_total_scanlines(262);
	}
	if (m_segacd)
	{
		m_segacd->set_framerate(60);
		m_segacd->set_total_scanlines(262);
	}

	m_version_hi_nibble = 0x00; // JPN NTSC
	if (!m_segacd)
		m_version_hi_nibble |= 0x20;
}

/****************************************** 32X emulation ****************************************/

DEVICE_IMAGE_LOAD_MEMBER( md_cons_state::_32x_cart )
{
	uint32_t length;
	std::vector<uint8_t> temp_copy;
	uint16_t *ROM16;
	uint32_t *ROM32;

	if (!image.loaded_through_softlist())
	{
		length = image.length();
		temp_copy.resize(length);
		image.fread(&temp_copy[0], length);
	}
	else
	{
		length = image.get_software_region_length("rom");
		temp_copy.resize(length);
		memcpy(&temp_copy[0], image.get_software_region("rom"), length);
	}

	// Copy the cart image in the locations the driver expects
	// Notice that, by using get_uXXbe, we are sure the code works on both LE and BE machines
	ROM16 = (uint16_t *) memregion("gamecart")->base();
	for (int i = 0; i < length; i += 2)
		ROM16[i / 2] = get_u16be(&temp_copy[i]);

	ROM32 = (uint32_t *) memregion("gamecart_sh2")->base();
	for (int i = 0; i < length; i += 4)
		ROM32[i / 4] = get_u32be(&temp_copy[i]);

	ROM16 = (uint16_t *) memregion("maincpu")->base();
	for (int i = 0x00; i < length; i += 2)
		ROM16[i / 2] = get_u16be(&temp_copy[i]);

	return std::make_pair(std::error_condition(), std::string());
}


void md_cons_state::_32x_scanline_callback(int x, uint32_t priority, uint32_t &lineptr)
{
	if (m_32x)
		m_32x->render_videobuffer_to_screenbuffer(x, priority, lineptr);
}

void md_cons_state::_32x_interrupt_callback(int scanline, int irq6)
{
	if (m_32x)
		m_32x->interrupt_cb(scanline, irq6);
}

void md_cons_state::_32x_scanline_helper_callback(int scanline)
{
	if (m_32x)
		m_32x->render_videobuffer_to_screenbuffer_helper(scanline);
}

void md_cons_state::genesis_32x(machine_config &config)
{
	md_ntsc(config);

	m_vdp->set_md_32x_scanline(FUNC(md_cons_state::_32x_scanline_callback));
	m_vdp->set_md_32x_scanline_helper(FUNC(md_cons_state::_32x_scanline_helper_callback));
	m_vdp->set_md_32x_interrupt(FUNC(md_cons_state::_32x_interrupt_callback));
	m_vdp->reset_routes();
	m_vdp->add_route(ALL_OUTPUTS, "speaker", 0.50, 0);
	m_vdp->add_route(ALL_OUTPUTS, "speaker", 0.50, 1);

	SEGA_32X_NTSC(config, m_32x, (MASTER_CLOCK_NTSC * 3) / 7, m_maincpu, m_scan_timer);
	m_32x->set_screen("megadriv");
	m_32x->add_route(0, "speaker", 1.00, 0);
	m_32x->add_route(1, "speaker", 1.00, 1);

	m_screen->screen_vblank().set(FUNC(md_cons_state::screen_vblank_console));

	// we need to remove and re-add the YM because the balance is different
	// due to MAME having severe issues if the dac output is > 0.40? (sound is corrupted even if DAC is silent?!)
	m_ymsnd->reset_routes();
	m_ymsnd->add_route(0, "speaker", (0.50)/2, 0);
	m_ymsnd->add_route(1, "speaker", (0.50)/2, 1);

	md_ctrl_ports(config);
	md_exp_port(config);

	generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "_32x_cart", "32x,bin"));
	cartslot.set_must_be_loaded(true);
	cartslot.set_device_load(FUNC(md_cons_state::_32x_cart));

	SOFTWARE_LIST(config, "cart_list").set_original("32x").set_filter("NTSC-U");
}


void md_cons_state::mdj_32x(machine_config &config)
{
	md_ntsc(config);

	m_vdp->set_md_32x_scanline(FUNC(md_cons_state::_32x_scanline_callback));
	m_vdp->set_md_32x_scanline_helper(FUNC(md_cons_state::_32x_scanline_helper_callback));
	m_vdp->set_md_32x_interrupt(FUNC(md_cons_state::_32x_interrupt_callback));
	m_vdp->reset_routes();
	m_vdp->add_route(ALL_OUTPUTS, "speaker", 0.50, 0);
	m_vdp->add_route(ALL_OUTPUTS, "speaker", 0.50, 1);

	SEGA_32X_NTSC(config, m_32x, (MASTER_CLOCK_NTSC * 3) / 7, m_maincpu, m_scan_timer);
	m_32x->set_screen("megadriv");
	m_32x->add_route(0, "speaker", 1.00, 0);
	m_32x->add_route(1, "speaker", 1.00, 1);

	m_screen->screen_vblank().set(FUNC(md_cons_state::screen_vblank_console));

	// we need to remove and re-add the sound system because the balance is different
	// due to MAME having severe issues if the dac output is > 0.40? (sound is corrupted even if DAC is silent?!)
	m_ymsnd->reset_routes();
	m_ymsnd->add_route(0, "speaker", (0.50)/2, 0);
	m_ymsnd->add_route(1, "speaker", (0.50)/2, 1);

	md_ctrl_ports(config);
	md_exp_port(config);

	generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "_32x_cart", "32x,bin"));
	cartslot.set_must_be_loaded(true);
	cartslot.set_device_load(FUNC(md_cons_state::_32x_cart));

	SOFTWARE_LIST(config, "cart_list").set_original("32x").set_filter("NTSC-J");
}


void md_cons_state::md_32x(machine_config &config)
{
	md_pal(config);

	m_vdp->set_md_32x_scanline(FUNC(md_cons_state::_32x_scanline_callback));
	m_vdp->set_md_32x_scanline_helper(FUNC(md_cons_state::_32x_scanline_helper_callback));
	m_vdp->set_md_32x_interrupt(FUNC(md_cons_state::_32x_interrupt_callback));
	m_vdp->reset_routes();
	m_vdp->add_route(ALL_OUTPUTS, "speaker", 0.50, 0);
	m_vdp->add_route(ALL_OUTPUTS, "speaker", 0.50, 1);

	SEGA_32X_PAL(config, m_32x, (MASTER_CLOCK_PAL * 3) / 7, m_maincpu, m_scan_timer);
	m_32x->set_screen("megadriv");
	m_32x->add_route(0, "speaker", 1.00, 0);
	m_32x->add_route(1, "speaker", 1.00, 1);

	m_screen->screen_vblank().set(FUNC(md_cons_state::screen_vblank_console));

	// we need to remove and re-add the sound system because the balance is different
	// due to MAME having severe issues if the dac output is > 0.40? (sound is corrupted even if DAC is silent?!)
	m_ymsnd->reset_routes();
	m_ymsnd->add_route(0, "speaker", (0.50)/2, 0);
	m_ymsnd->add_route(1, "speaker", (0.50)/2, 1);

	md_ctrl_ports(config);
	md_exp_port(config);

	generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "_32x_cart", "32x,bin"));
	cartslot.set_must_be_loaded(true);
	cartslot.set_device_load(FUNC(md_cons_state::_32x_cart));

	SOFTWARE_LIST(config, "cart_list").set_original("32x").set_filter("PAL");
}



#define _32X_ROMS \
	ROM_REGION16_BE( 0x400000, "gamecart", ROMREGION_ERASE00 ) /* 68000 Code */ \
	ROM_REGION32_BE( 0x400000, "gamecart_sh2", ROMREGION_ERASE00 ) /* Copy for the SH2 */ \
	ROM_REGION16_BE( 0x400000, "32x_68k_bios", 0 ) /* 68000 Code */ \
	ROM_LOAD( "32x_g_bios.bin", 0x000000,  0x000100, CRC(5c12eae8) SHA1(dbebd76a448447cb6e524ac3cb0fd19fc065d944) ) \
	ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 ) \
	/* temp, rom should only be visible here when one of the regs is set, tempo needs it */ \
	ROM_COPY( "32x_68k_bios", 0x0, 0x0, 0x100) \
	ROM_REGION32_BE( 0x400000, "master", 0 ) /* SH2 Code */ \
	ROM_SYSTEM_BIOS( 0, "retail", "Mars Version 1.0 (retail)" ) \
	ROMX_LOAD( "32x_m_bios.bin", 0x000000,  0x000800, CRC(dd9c46b8) SHA1(1e5b0b2441a4979b6966d942b20cc76c413b8c5e), ROM_BIOS(0) ) \
	ROM_SYSTEM_BIOS( 1, "sdk", "Mars Version 1.0 (early sdk)" ) \
	ROMX_LOAD( "32x_m_bios_sdk.bin", 0x000000,  0x000800, BAD_DUMP CRC(c7102c53) SHA1(ed73a47f186b373b8eff765f84ef26c3d9ef6cb0), ROM_BIOS(1) ) \
	ROM_REGION32_BE( 0x400000, "slave", 0 ) /* SH2 Code */ \
	ROM_LOAD( "32x_s_bios.bin", 0x000000,  0x000400, CRC(bfda1fe5) SHA1(4103668c1bbd66c5e24558e73d4f3f92061a109a) )

ROM_START( 32x )
	_32X_ROMS
ROM_END

ROM_START( 32xe )
	_32X_ROMS
ROM_END

ROM_START( 32xj )
	_32X_ROMS
ROM_END


/****************************************** SegaCD emulation ****************************************/

void md_cons_cd_state::genesis_scd(machine_config &config)
{
	md_ntsc(config);

	m_screen->screen_vblank().set(FUNC(md_cons_cd_state::screen_vblank_console));

	SEGA_SEGACD_US(config, m_segacd, 0);
	m_segacd->set_palette("gen_vdp:gfx_palette");
	m_segacd->set_hostcpu(m_maincpu);
	m_segacd->set_screen("megadriv");

	config.set_perfect_quantum("segacd:segacd_68k"); // perfect sync to the fastest cpu

	md_ctrl_ports(config);
	md_exp_port(config);

	CDROM(config, "cdrom").set_interface("cdrom");

	SOFTWARE_LIST(config, "cd_list").set_original("megacd").set_filter("NTSC-U");
}

void md_cons_cd_state::genesis2_scd(machine_config &config)
{
	md2_ntsc(config);

	m_screen->screen_vblank().set(FUNC(md_cons_cd_state::screen_vblank_console));

	SEGA_SEGACD_US(config, m_segacd, 0);
	m_segacd->set_palette("gen_vdp:gfx_palette");
	m_segacd->set_hostcpu(m_maincpu);
	m_segacd->set_screen("megadriv");

	config.set_perfect_quantum("segacd:segacd_68k"); // perfect sync to the fastest cpu

	md_ctrl_ports(config);

	CDROM(config, "cdrom").set_interface("cdrom");

	SOFTWARE_LIST(config, "cd_list").set_original("megacd").set_filter("NTSC-U");
}

void md_cons_cd_state::md_scd(machine_config &config)
{
	md_pal(config);

	m_screen->screen_vblank().set(FUNC(md_cons_cd_state::screen_vblank_console));

	SEGA_SEGACD_EUROPE(config, m_segacd, 0);
	m_segacd->set_palette("gen_vdp:gfx_palette");
	m_segacd->set_hostcpu(m_maincpu);
	m_segacd->set_screen("megadriv");

	config.set_perfect_quantum("segacd:segacd_68k"); // perfect sync to the fastest cpu

	md_ctrl_ports(config);
	md_exp_port(config);

	CDROM(config, "cdrom").set_interface("cdrom");

	SOFTWARE_LIST(config, "cd_list").set_original("megacd").set_filter("PAL");
}

void md_cons_cd_state::md2_scd(machine_config &config)
{
	md2_pal(config);

	m_screen->screen_vblank().set(FUNC(md_cons_cd_state::screen_vblank_console));

	SEGA_SEGACD_EUROPE(config, m_segacd, 0);
	m_segacd->set_palette("gen_vdp:gfx_palette");
	m_segacd->set_hostcpu(m_maincpu);
	m_segacd->set_screen("megadriv");

	config.set_perfect_quantum("segacd:segacd_68k"); // perfect sync to the fastest cpu

	md_ctrl_ports(config);

	CDROM(config, "cdrom").set_interface("cdrom");

	SOFTWARE_LIST(config, "cd_list").set_original("megacd").set_filter("PAL");
}

void md_cons_cd_state::mdj_scd(machine_config &config)
{
	md_ntsc(config);

	m_screen->screen_vblank().set(FUNC(md_cons_cd_state::screen_vblank_console));

	SEGA_SEGACD_JAPAN(config, m_segacd, 0);
	m_segacd->set_palette("gen_vdp:gfx_palette");
	m_segacd->set_hostcpu(m_maincpu);
	m_segacd->set_screen("megadriv");

	config.set_perfect_quantum("segacd:segacd_68k"); // perfect sync to the fastest cpu

	md_ctrl_ports(config);
	md_exp_port(config);

	CDROM(config, "cdrom").set_interface("cdrom");

	SOFTWARE_LIST(config, "cd_list").set_original("megacd").set_filter("NTSC-J");
}

void md_cons_cd_state::md2j_scd(machine_config &config)
{
	md2_ntsc(config);

	m_screen->screen_vblank().set(FUNC(md_cons_cd_state::screen_vblank_console));

	SEGA_SEGACD_JAPAN(config, m_segacd, 0);
	m_segacd->set_palette("gen_vdp:gfx_palette");
	m_segacd->set_hostcpu(m_maincpu);
	m_segacd->set_screen("megadriv");

	config.set_perfect_quantum("segacd:segacd_68k"); // perfect sync to the fastest cpu

	md_ctrl_ports(config);

	CDROM(config, "cdrom").set_interface("cdrom");

	SOFTWARE_LIST(config, "cd_list").set_original("megacd").set_filter("NTSC-J");
}

/******************SEGA CD + 32X****************************/

void md_cons_cd_state::genesis_32x_scd(machine_config &config)
{
	genesis_32x(config);

	SEGA_SEGACD_US(config, m_segacd, 0);
	m_segacd->set_palette("gen_vdp:gfx_palette");
	m_segacd->set_hostcpu(m_maincpu);
	m_segacd->set_screen("megadriv");

	config.set_perfect_quantum("segacd:segacd_68k"); // perfect sync to the fastest cpu

	CDROM(config, "cdrom").set_interface("cdrom");

	config.device_remove("cartslot");
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "_32x_cart", "32x,bin").set_device_load(FUNC(md_cons_cd_state::_32x_cart));

	//config.m_perfect_cpu_quantum = subtag("32x_master_sh2");
	SOFTWARE_LIST(config, "cd_list").set_original("megacd").set_filter("NTSC-U");
}

void md_cons_cd_state::md_32x_scd(machine_config &config)
{
	md_32x(config);

	SEGA_SEGACD_EUROPE(config, m_segacd, 0);
	m_segacd->set_palette("gen_vdp:gfx_palette");
	m_segacd->set_hostcpu(m_maincpu);
	m_segacd->set_screen("megadriv");

	config.set_perfect_quantum("segacd:segacd_68k"); // perfect sync to the fastest cpu

	CDROM(config, "cdrom").set_interface("cdrom");

	config.device_remove("cartslot");
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "_32x_cart", "32x,bin").set_device_load(FUNC(md_cons_cd_state::_32x_cart));

	//config.m_perfect_cpu_quantum = subtag("32x_master_sh2");
	SOFTWARE_LIST(config, "cd_list").set_original("megacd").set_filter("PAL");
}

void md_cons_cd_state::mdj_32x_scd(machine_config &config)
{
	mdj_32x(config);

	SEGA_SEGACD_JAPAN(config, m_segacd, 0);
	m_segacd->set_palette("gen_vdp:gfx_palette");
	m_segacd->set_hostcpu(m_maincpu);
	m_segacd->set_screen("megadriv");

	config.set_perfect_quantum("segacd:segacd_68k"); // perfect sync to the fastest cpu

	CDROM(config, "cdrom").set_interface("cdrom");

	config.device_remove("cartslot");
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "_32x_cart", "32x,bin").set_device_load(FUNC(md_cons_cd_state::_32x_cart));

	//config.m_perfect_cpu_quantum = subtag("32x_master_sh2");
	SOFTWARE_LIST(config, "cd_list").set_original("megacd").set_filter("NTSC-J");
}

/* We need proper names for most of these BIOS ROMs! */
ROM_START( segacd )
	ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 )
	/* v1.10 confirmed dump by dead_screem */
	ROM_LOAD( "mpr-15045b.bin", 0x000000,  0x020000, CRC(c6d10268) SHA1(f4f315adcef9b8feb0364c21ab7f0eaf5457f3ed) )
ROM_END

ROM_START( megacd )
	ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 )
	/* v1.00, confirmed good dump */
	ROM_LOAD( "megacd_model1_bios_1_00_e.bin", 0x000000,  0x020000, CRC(529ac15a) SHA1(f891e0ea651e2232af0c5c4cb46a0cae2ee8f356) )
ROM_END

ROM_START( megacdj )
	ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_DEFAULT_BIOS("v100g")   // this seems the only revision where the cursor in CD menu works, allowing to boot games
	/* Confirmed by ElBarto */
	ROM_SYSTEM_BIOS(0, "v100s", "v1.00S")
	ROMX_LOAD( "mpr-14088h.bin", 0x000000,  0x020000, CRC(3773d5aa) SHA1(bbf729a1aaa1667b783749299e1ad932aaf5f253), ROM_BIOS(0) | ROM_GROUPWORD | ROM_REVERSE)
	/* Confirmed by ElBarto */
	ROM_SYSTEM_BIOS(1, "v100g", "v1.00G")
	ROMX_LOAD( "epr-14088b.bin", 0x000000,  0x020000, CRC(69ed6ccd) SHA1(27d11c3836506f01ee81cd142c0cd8b51abebbd2), ROM_BIOS(1) | ROM_GROUPWORD | ROM_REVERSE)
	/* Confirmed by ElBarto */
	ROM_SYSTEM_BIOS(2, "v100l", "v1.00L")
	ROMX_LOAD( "mpr-14088c.bin", 0x000000,  0x020000, CRC(03134289) SHA1(d60cb5a53f26d6b13e354bc149217587f2301718), ROM_BIOS(2) | ROM_GROUPWORD | ROM_REVERSE)
	/* Confirmed by ElBarto */
	ROM_SYSTEM_BIOS(3, "v100o", "v1.00O")
	ROMX_LOAD( "epr-14088d.bin", 0x000000,  0x020000, CRC(dfa95ee9) SHA1(e13666c76fa0a2e94e2f651b26b0fd625bf55f07), ROM_BIOS(3) | ROM_GROUPWORD | ROM_REVERSE)
	ROM_SYSTEM_BIOS(4, "v100p", "v1.00P")   // CRC: e2e70bc8 when byteswapped
	ROMX_LOAD( "epr-14088e.bin", 0x000000,  0x020000, CRC(9d2da8f2) SHA1(4846f448160059a7da0215a5df12ca160f26dd69), ROM_BIOS(4) )
	// EEPROM had no SEGA's label, might be 14088(no rev) or 14088A
	ROM_SYSTEM_BIOS(5, "v100c", "v1.00C")   // CRC: c3b60c13 when byteswapped
	ROMX_LOAD( "100c.bin",       0x000000,  0x020000, CRC(41af44c4) SHA1(f30d109d1c2f7c9feaf38600c65834261db73d1f), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS(6, "v111", "v1.11")   // CRC: e0a6179b when byteswapped
	ROMX_LOAD( "mpr-14837.bin",  0x000000,  0x020000, CRC(4be18ff6) SHA1(204758d5a64c24e96e1a9fe6bd82e1878fef7ade), ROM_BIOS(6) )
ROM_END

/* Asia bios, when run in USA region will show :
ERROR!
THIS IS A PAL-COMPATIBLE MEGA CD
FOR EXCLUSIVE USE IN SOUTHEAST ASIA.

Confirmed by Jakovasaur
*/
ROM_START( megacda )
	ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "epr-14536h.bin", 0x000000,  0x020000, CRC(550f30bb) SHA1(e4193c6ae44c3cea002707d2a88f1fbcced664de))
ROM_END

ROM_START( segacd2 )
	ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_SYSTEM_BIOS(0, "v211x", "Model 2 v2.11X")
	ROMX_LOAD( "mpr-15764-t.bin", 0x000000,  0x020000, CRC(2e49d72c) SHA1(328a3228c29fba244b9db2055adc1ec4f7a87e6b), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "v200", "Model 2 v2.00") /* verified dump */
	ROMX_LOAD( "us_scd2_930314.bin", 0x000000,  0x020000, CRC(8af65f58) SHA1(5a8c4b91d3034c1448aac4b5dc9a6484fce51636), ROM_BIOS(1) )
	/* this is reportedly a bad dump, it has many differences from the verified dump and does not boot in Kega */
	/* ROMX_LOAD( "segacd_model2_bios_2_00_u.bin", 0x000000,  0x020000, CRC(340b4be4) SHA1(bd3ee0c8ab732468748bf98953603ce772612704), ROM_BIOS(1) ) */
	ROM_SYSTEM_BIOS(2, "v200w", "Model 2 v2.00W")
	ROMX_LOAD( "segacd_model2_bios_2_00w_u.bin", 0x000000,  0x020000, CRC(9f6f6276) SHA1(5adb6c3af218c60868e6b723ec47e36bbdf5e6f0), ROM_BIOS(2) )
ROM_END

/* v2.00, v2.00w confirmed good dumps by ElBarto */
/* v2.11x confirmed good dump by TwistedTom */
ROM_START( megacd2 )
	ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_SYSTEM_BIOS(0, "v211x", "v2.11X")   // MK-4102A-50 Sony pcb
	ROMX_LOAD( "mpr-15811-t.bin", 0x000000,  0x020000, CRC(391a80d2) SHA1(aa4bb1803b06714fbc04d8a209161876796dd511), ROM_BIOS(0) | ROM_GROUPWORD | ROM_REVERSE )
	ROM_SYSTEM_BIOS(1, "v200w", "v2.00W")   // MK-4102-50 Funai pcb
	ROMX_LOAD( "mpr-15512a.bin", 0x000000,  0x020000, CRC(53f1757c) SHA1(67bf3970ca5a05fd5ce3d6c446789c5d971b98a4), ROM_BIOS(1) | ROM_GROUPWORD | ROM_REVERSE )
	ROM_SYSTEM_BIOS(2, "v200", "v2.00")     // ?
	ROMX_LOAD( "mpr-15512.bin", 0x000000,  0x020000, CRC(cb76f114) SHA1(939f173cadc41e996a3c34498da1bf55e7e18ff8), ROM_BIOS(2) | ROM_GROUPWORD | ROM_REVERSE )
ROM_END

/* Confirmed good dump by ElBarto */
ROM_START( megacd2j )
	ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_SYSTEM_BIOS(0, "v200c", "v2.00C")
	ROMX_LOAD( "mpr-15398.bin", 0x000000,  0x020000, CRC(1e4344e6) SHA1(4d1251a6973d932e734ae5e8c6b9b55eb40e4143), ROM_BIOS(0) | ROM_GROUPWORD | ROM_REVERSE )
ROM_END

ROM_START( aiwamcd )
	ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 )
	/* v2.11 */
	ROM_LOAD( "mpr-15768-t.bin", 0x000000,  0x020000, CRC(8052c7a0) SHA1(219d284dcf63ce366a4dc6d1ff767a0d2eea283d) )
ROM_END

ROM_START( laseract )
	ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_SYSTEM_BIOS(0, "v104", "v1.04")
	ROMX_LOAD( "laseractive_bios_1_04_u.bin", 0x000000,  0x020000, CRC(50cd3d23) SHA1(aa811861f8874775075bd3f53008c8aaf59b07db), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "v102", "v1.02")
	ROMX_LOAD( "laseractive_bios_1_02_u.bin", 0x000000,  0x020000, CRC(3b10cf41) SHA1(8af162223bb12fc19b414f126022910372790103), ROM_BIOS(1) )
ROM_END

ROM_START( laseractj )
	ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_SYSTEM_BIOS(0, "v105", "v1.05")
	ROMX_LOAD( "mega-ld 1.05 bios.bin", 0x000000,  0x020000, CRC(474aaa44) SHA1(b3b1d880e288b6dc79eec0ff1b0480c229ec141d), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "v102", "v1.02")
	ROMX_LOAD( "laseractive_bios_1_02_j.bin", 0x000000,  0x020000, CRC(00eedb3a) SHA1(26237b333db4a4c6770297fa5e655ea95840d5d9), ROM_BIOS(1) )
ROM_END

ROM_START( xeye )
	ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 )
	/* v2.00 (US), confirmed good with a chip dump */
	ROM_LOAD( "g304.bin", 0x000000,  0x020000, CRC(290f8e33) SHA1(651f14d5a5e0ecb974a60c0f43b1d2006323fb09) )
ROM_END

ROM_START( wmega )
	ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 )
	/* v1.00 (Japan NTSC) Sega BIOS, chip-dumped */
	ROM_LOAD( "g301.bin", 0x000000,  0x020000, CRC(d21fe71d) SHA1(3fc9358072f74bd24e3e297ea11b2bf15a7af891) )
ROM_END

ROM_START( wmegam2 )
	ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 )
	/* v2.00 */
	ROM_LOAD( "wondermega_m2_bios_2_00_j.bin", 0x000000,  0x020000, CRC(2b19972f) SHA1(b3f32e409bd5508c89ed8be33d41a58d791d0e5d) )
ROM_END

ROM_START( cdx )
	ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 )
	/* v2.21X */
	ROM_LOAD( "segacdx_bios_2_21_u.bin", 0x000000,  0x020000, CRC(d48c44b5) SHA1(2b125c0545afa089b617f2558e686ea723bdc06e) )
ROM_END

ROM_START( multmega )
	ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 )
	/* v2.21X */
	ROM_LOAD( "opr-16140.bin", 0x000000,  0x020000, CRC(aacb851e) SHA1(75548ac9aaa6e81224499f9a1403b2b42433f5b7) )
	/* the below was marked "confirmed good dump", but 0x72 and 0x73 are 0xFF, indicating a bad dump made from memory */
	/* ROM_LOAD( "multimega_bios_2_21_e.bin", 0x000000,  0x020000, CRC(34d3cce1) SHA1(73fc9c014ad803e9e7d8076b3642a8a5224b3e51) ) */
ROM_END

/* some games use the 32x and SegaCD together to give better quality FMV */
ROM_START( 32x_scd )
	ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 )

	ROM_REGION16_BE( 0x400000, "gamecart", 0 ) /* 68000 Code */
	ROM_LOAD( "mpr-15764-t.bin", 0x000000,  0x020000, CRC(2e49d72c) SHA1(328a3228c29fba244b9db2055adc1ec4f7a87e6b) )

	ROM_REGION32_BE( 0x400000, "gamecart_sh2", 0 ) /* Copy for the SH2 */
	ROM_COPY( "gamecart", 0x000000, 0x0, 0x400000)

	ROM_REGION16_BE( 0x400000, "32x_68k_bios", 0 ) /* 68000 Code */
	ROM_LOAD( "32x_g_bios.bin", 0x000000,  0x000100, CRC(5c12eae8) SHA1(dbebd76a448447cb6e524ac3cb0fd19fc065d944) )

	ROM_REGION32_BE( 0x400000, "master", 0 ) /* SH2 Code */
	ROM_LOAD( "32x_m_bios.bin", 0x000000,  0x000800, CRC(dd9c46b8) SHA1(1e5b0b2441a4979b6966d942b20cc76c413b8c5e) )

	ROM_REGION32_BE( 0x400000, "slave", 0 ) /* SH2 Code */
	ROM_LOAD( "32x_s_bios.bin", 0x000000,  0x000400, CRC(bfda1fe5) SHA1(4103668c1bbd66c5e24558e73d4f3f92061a109a) )
ROM_END

ROM_START( 32x_mcd )
	ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 )

	ROM_REGION16_BE( 0x400000, "gamecart", 0 ) /* 68000 Code */
	ROM_LOAD( "megacd_model1_bios_1_00_e.bin", 0x000000,  0x020000, CRC(529ac15a) SHA1(f891e0ea651e2232af0c5c4cb46a0cae2ee8f356) )

	ROM_REGION32_BE( 0x400000, "gamecart_sh2", 0 ) /* Copy for the SH2 */
	ROM_COPY( "gamecart", 0x000000, 0x0, 0x400000)

	ROM_REGION16_BE( 0x400000, "32x_68k_bios", 0 ) /* 68000 Code */
	ROM_LOAD( "32x_g_bios.bin", 0x000000,  0x000100, CRC(5c12eae8) SHA1(dbebd76a448447cb6e524ac3cb0fd19fc065d944) )

	ROM_REGION32_BE( 0x400000, "master", 0 ) /* SH2 Code */
	ROM_LOAD( "32x_m_bios.bin", 0x000000,  0x000800, CRC(dd9c46b8) SHA1(1e5b0b2441a4979b6966d942b20cc76c413b8c5e) )

	ROM_REGION32_BE( 0x400000, "slave", 0 ) /* SH2 Code */
	ROM_LOAD( "32x_s_bios.bin", 0x000000,  0x000400, CRC(bfda1fe5) SHA1(4103668c1bbd66c5e24558e73d4f3f92061a109a) )
ROM_END

ROM_START( 32x_mcdj )
	ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 )

	ROM_REGION16_BE( 0x400000, "gamecart", 0 ) /* 68000 Code */
	ROM_DEFAULT_BIOS("v100g")   // this seems the only revision where the cursor in CD menu works, allowing to boot games
	/* Confirmed by ElBarto */
	ROM_SYSTEM_BIOS(0, "v100s", "v1.00S")
	ROMX_LOAD( "mpr-14088h.bin", 0x000000,  0x020000, CRC(3773d5aa) SHA1(bbf729a1aaa1667b783749299e1ad932aaf5f253), ROM_BIOS(0) | ROM_GROUPWORD | ROM_REVERSE)
	/* Confirmed by ElBarto */
	ROM_SYSTEM_BIOS(1, "v100g", "v1.00G")
	ROMX_LOAD( "epr-14088b.bin", 0x000000,  0x020000, CRC(69ed6ccd) SHA1(27d11c3836506f01ee81cd142c0cd8b51abebbd2), ROM_BIOS(1) | ROM_GROUPWORD | ROM_REVERSE)
	/* Confirmed by ElBarto */
	ROM_SYSTEM_BIOS(2, "v100l", "v1.00L")
	ROMX_LOAD( "mpr-14088c.bin", 0x000000,  0x020000, CRC(03134289) SHA1(d60cb5a53f26d6b13e354bc149217587f2301718), ROM_BIOS(2) | ROM_GROUPWORD | ROM_REVERSE)
	/* Confirmed by ElBarto */
	ROM_SYSTEM_BIOS(3, "v100o", "v1.00O")
	ROMX_LOAD( "epr-14088d.bin", 0x000000,  0x020000, CRC(dfa95ee9) SHA1(e13666c76fa0a2e94e2f651b26b0fd625bf55f07), ROM_BIOS(3) | ROM_GROUPWORD | ROM_REVERSE)
	ROM_SYSTEM_BIOS(4, "v100p", "v1.00P")   // CRC: e2e70bc8 when byteswapped
	ROMX_LOAD( "epr-14088e.bin", 0x000000,  0x020000, CRC(9d2da8f2) SHA1(4846f448160059a7da0215a5df12ca160f26dd69), ROM_BIOS(4) )

	ROM_REGION32_BE( 0x400000, "gamecart_sh2", 0 ) /* Copy for the SH2 */
	ROM_COPY( "gamecart", 0x000000, 0x0, 0x400000)

	ROM_REGION16_BE( 0x400000, "32x_68k_bios", 0 ) /* 68000 Code */
	ROM_LOAD( "32x_g_bios.bin", 0x000000,  0x000100, CRC(5c12eae8) SHA1(dbebd76a448447cb6e524ac3cb0fd19fc065d944) )

	ROM_REGION32_BE( 0x400000, "master", 0 ) /* SH2 Code */
	ROM_LOAD( "32x_m_bios.bin", 0x000000,  0x000800, CRC(dd9c46b8) SHA1(1e5b0b2441a4979b6966d942b20cc76c413b8c5e) )

	ROM_REGION32_BE( 0x400000, "slave", 0 ) /* SH2 Code */
	ROM_LOAD( "32x_s_bios.bin", 0x000000,  0x000400, CRC(bfda1fe5) SHA1(4103668c1bbd66c5e24558e73d4f3f92061a109a) )
ROM_END



/***************************************************************************

  Game driver(s)

***************************************************************************/

/*    YEAR  NAME          PARENT    COMPAT  MACHINE          INPUT     CLASS          INIT          COMPANY   FULLNAME */
CONS( 1989, genesis,      0,        0,      ms_megadriv,     md,       md_cons_slot_state, init_genesis, "Sega",   "Genesis (USA, NTSC)",  MACHINE_SUPPORTS_SAVE )
CONS( 1990, megadriv,     genesis,  0,      ms_megadpal,     md,       md_cons_slot_state, init_md_eur,  "Sega",   "Mega Drive (Europe, PAL)", MACHINE_SUPPORTS_SAVE )
CONS( 1988, megadrij,     genesis,  0,      ms_megadrivj,    md,       md_cons_slot_state, init_md_jpn,  "Sega",   "Mega Drive (Japan, NTSC)", MACHINE_SUPPORTS_SAVE )

// 1990+ models had the TMSS security chip, leave this as a clone, it reduces compatibility and nothing more.
CONS( 1990, genesis_tmss, genesis,  0,      genesis_tmss,    md,       md_cons_slot_state, init_genesis_tmss, "Sega",   "Genesis (USA, NTSC, with TMSS chip)",  MACHINE_SUPPORTS_SAVE )

// the 32X plugged in the cart slot, games plugged into the 32x.  Maybe it should be handled as an expansion device?
CONS( 1994, 32x,          0,        0,      genesis_32x,     md,       md_cons_state, init_genesis, "Sega",   "Genesis with 32X (USA, NTSC)", MACHINE_NOT_WORKING )
CONS( 1994, 32xe,         32x,      0,      md_32x,          md,       md_cons_state, init_md_eur,  "Sega",   "Mega Drive with 32X (Europe, PAL)", MACHINE_NOT_WORKING )
CONS( 1994, 32xj,         32x,      0,      mdj_32x,         md,       md_cons_state, init_md_jpn,  "Sega",   "Mega Drive with 32X (Japan, NTSC)", MACHINE_NOT_WORKING )

// the SegaCD plugged into the expansion port..
CONS( 1992, segacd,       0,        0,      genesis_scd,     md,       md_cons_cd_state, init_genesis, "Sega",   "Sega CD (USA, NTSC)", MACHINE_NOT_WORKING )
CONS( 1993, megacd,       segacd,   0,      md_scd,          md,       md_cons_cd_state, init_md_eur,  "Sega",   "Mega-CD (Europe, PAL)", MACHINE_NOT_WORKING )
CONS( 1991, megacdj,      segacd,   0,      mdj_scd,         md,       md_cons_cd_state, init_md_jpn,  "Sega",   "Mega-CD (Japan, NTSC)", MACHINE_NOT_WORKING ) // this bios doesn't work with our ram interleave needed by a few games?!
CONS( 1991, megacda,      segacd,   0,      md_scd,          md,       md_cons_cd_state, init_md_eur,  "Sega",   "Mega-CD (Asia, PAL)", MACHINE_NOT_WORKING )
CONS( 1993, segacd2,      0,        0,      genesis_scd,     md,       md_cons_cd_state, init_genesis, "Sega",   "Sega CD 2 (USA, NTSC)", MACHINE_NOT_WORKING )
CONS( 1993, megacd2,      segacd2,  0,      md_scd,          md,       md_cons_cd_state, init_md_eur,  "Sega",   "Mega-CD 2 (Europe, PAL)", MACHINE_NOT_WORKING )
CONS( 1993, megacd2j,     segacd2,  0,      mdj_scd,         md,       md_cons_cd_state, init_md_jpn,  "Sega",   "Mega-CD 2 (Japan, NTSC)", MACHINE_NOT_WORKING )
CONS( 1994, aiwamcd,      segacd2,  0,      mdj_scd,         md,       md_cons_cd_state, init_md_jpn,  "AIWA",   "Mega-CD CSD-G1M (Japan, NTSC)", MACHINE_NOT_WORKING )
CONS( 1993, xeye,         0,        0,      genesis2_scd,    md,       md_cons_cd_state, init_genesis, "JVC",    "X'eye (USA, NTSC)", MACHINE_NOT_WORKING )
CONS( 1992, wmega,        xeye,     0,      mdj_scd,         md,       md_cons_cd_state, init_md_jpn,  "Sega",   "Wondermega (Japan, NTSC)", MACHINE_NOT_WORKING )
CONS( 1993, wmegam2,      xeye,     0,      md2j_scd,        md,       md_cons_cd_state, init_md_jpn,  "Victor", "Wondermega M2 (Japan, NTSC)", MACHINE_NOT_WORKING )
CONS( 1994, cdx,          0,        0,      genesis2_scd,    md,       md_cons_cd_state, init_genesis, "Sega",   "CDX (USA, NTSC)", MACHINE_NOT_WORKING )
CONS( 1994, multmega,     cdx,      0,      md2_scd,         md,       md_cons_cd_state, init_md_eur,  "Sega",   "Multi-Mega (Europe, PAL)", MACHINE_NOT_WORKING )

//32X plugged in the cart slot + SegaCD plugged into the expansion port..
CONS( 1994, 32x_scd,      0,        0,      genesis_32x_scd, md,       md_cons_cd_state, init_genesis, "Sega",   "Sega CD with 32X (USA, NTSC)", MACHINE_NOT_WORKING )
CONS( 1995, 32x_mcd,      32x_scd,  0,      md_32x_scd,      md,       md_cons_cd_state, init_md_eur,  "Sega",   "Mega-CD with 32X (Europe, PAL)", MACHINE_NOT_WORKING )
CONS( 1994, 32x_mcdj,     32x_scd,  0,      mdj_32x_scd,     md,       md_cons_cd_state, init_md_jpn,  "Sega",   "Mega-CD with 32X (Japan, NTSC)", MACHINE_NOT_WORKING )

// handheld hardware
CONS( 1995, gen_nomd,     0,        0,      ms_nomad,        gen_nomd, md_cons_slot_state, init_genesis, "Sega",   "Genesis Nomad (USA Genesis handheld)",  MACHINE_SUPPORTS_SAVE )

// handheld without LCD
CONS( 1993, megajet,      gen_nomd, 0,      ms_megajet,      md,       md_cons_slot_state, init_md_jpn,  "Sega",   "Mega Jet (Japan Mega Drive handheld)",  MACHINE_SUPPORTS_SAVE )

// LaserActive (Laserdisc Player(ex: CLD-A100) with 'Control Pack' Addon slot)
// Mega Drive Pack(PAC-S1)/Genesis Pack(PAC-S10) plugged into the Control Pack slot, for plays Mega Drive/Genesis Cartridge, Mega-CD/Sega CD, Mega-LD stuffs
CONS( 1993, laseract,     0,        0,      genesis_scd,     md,       md_cons_cd_state, init_genesis, "Pioneer / Sega","LaserActive with Genesis Pack PAC-S10 (USA, NTSC)", MACHINE_NOT_WORKING )
CONS( 1993, laseractj,    laseract, 0,      mdj_scd,         md,       md_cons_cd_state, init_md_jpn,  "Pioneer / Sega","LaserActive with Mega Drive Pack PAC-S1 (Japan, NTSC)", MACHINE_NOT_WORKING )
//TODO: it has also PC Engine Pack(PAC-N1)/TG16 Pack(PAC-N10) for plays PC Engine/TG16 Cartridge, (Super/Arcade) CD-ROM2/TurboGrafx-CD, LD-ROM2 stuffs, but not emulated.



mdisk.cpp
<---------------------------------------------------------------------->
// license: GPL-2.0+
// copyright-holders: Dirk Best
/***************************************************************************

    mupid M-Disk Comp.-A
    Grundig FL-100

    Floppy disk station, usually connected to a mupid C2D2/C2A2 or the
    Grundig PTC-100.

    Everything here is guessed based on the software and a PCB image. You
    can see garbled output when you connect the builtin terminal to the
    first serial port ('ser'). It does boot from floppy too, so presumely
    only needs an emulated main system to work.

***************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/i8271.h"
#include "machine/ram.h"
#include "bus/rs232/rs232.h"
#include "imagedev/floppy.h"


namespace {

//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class mdisk_state : public driver_device
{
public:
	mdisk_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_cpu(*this, "maincpu"),
		m_ram(*this, "ram"),
		m_fdc(*this, "fdc"),
		m_floppy(*this, "fdc:%u", 0),
		m_rom_timer(nullptr),
		m_uart1_rxrdy(0), m_uart1_txrdy(0),
		m_fdc_irq(0)
		{ }

		void mdisk(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	TIMER_CALLBACK_MEMBER(rom_timer_callback);
	void uart1_rxrdy_w(int state);
	void uart1_txrdy_w(int state);
	void fdc_irq_w(int state);
	void fdc_motor_w(int state);
	void fdc_side_w(uint8_t data);

	void update_irq(uint8_t vector);

	required_device<z80_device> m_cpu;
	required_device<ram_device> m_ram;
	required_device<i8271_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;

	void mdisk_mem(address_map &map) ATTR_COLD;
	void mdisk_io(address_map &map) ATTR_COLD;

	emu_timer *m_rom_timer;

	int m_uart1_rxrdy, m_uart1_txrdy, m_fdc_irq;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void mdisk_state::mdisk_mem(address_map &map)
{
	map(0x0000, 0x1fff).bankr("bank1r").bankw("bank1w");
	map(0x2000, 0xffff).bankrw("bank2");
}

void mdisk_state::mdisk_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x20, 0x20).w(FUNC(mdisk_state::fdc_side_w));
	map(0x40, 0x40).rw("fdc", FUNC(i8271_device::data_r), FUNC(i8271_device::data_w));
	map(0x80, 0x81).rw("uart1", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x84, 0x85).rw("uart2", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x9c, 0x9e).m(m_fdc, FUNC(i8271_device::map));
}


//**************************************************************************
//  INPUTS
//**************************************************************************

INPUT_PORTS_START( mdisk )
	PORT_START("front")
	PORT_DIPNAME(0x01, 0x01, "Baudrate")
	PORT_DIPSETTING(0x00, "4800")
	PORT_DIPSETTING(0x01, "19200")
INPUT_PORTS_END


//**************************************************************************
//  FLOPPY
//**************************************************************************

void mdisk_state::fdc_motor_w(int state)
{
	if (m_floppy[0]->get_device()) m_floppy[0]->get_device()->mon_w(!state);
	if (m_floppy[1]->get_device()) m_floppy[1]->get_device()->mon_w(!state);
}

void mdisk_state::fdc_side_w(uint8_t data)
{
	if (m_floppy[0]->get_device()) m_floppy[0]->get_device()->ss_w(BIT(data, 0));
	if (m_floppy[1]->get_device()) m_floppy[1]->get_device()->ss_w(BIT(data, 0));
}


//**************************************************************************
//  MACHINE
//**************************************************************************

void mdisk_state::machine_start()
{
	// timer to switch rom to ram
	m_rom_timer = timer_alloc(FUNC(mdisk_state::rom_timer_callback), this);

	// register for save states
	save_item(NAME(m_uart1_rxrdy));
	save_item(NAME(m_uart1_txrdy));
	save_item(NAME(m_fdc_irq));
}

void mdisk_state::machine_reset()
{
	// read from rom, write to ram
	membank("bank1r")->set_base(memregion("firmware")->base());
	membank("bank1w")->set_base(m_ram->pointer());
	membank("bank2")->set_base(m_ram->pointer() + 0x2000);

	// timing unknown
	m_rom_timer->adjust(attotime::from_msec(50));

	m_uart1_rxrdy = 0;
	m_uart1_txrdy = 0;
	m_fdc_irq = 0;
}

TIMER_CALLBACK_MEMBER(mdisk_state::rom_timer_callback)
{
	// switch in ram
	logerror("Disabling ROM\n");
	membank("bank1r")->set_base(m_ram->pointer());
}

void mdisk_state::update_irq(uint8_t vector)
{
	if (m_uart1_rxrdy || m_uart1_txrdy || m_fdc_irq)
		m_cpu->set_input_line_and_vector(INPUT_LINE_IRQ0, ASSERT_LINE, vector); // Z80
	else
		m_cpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
}

void mdisk_state::fdc_irq_w(int state)
{
	m_fdc_irq = state;
	update_irq(0x00);
}

void mdisk_state::uart1_rxrdy_w(int state)
{
	m_uart1_rxrdy = state;
	update_irq(0x18);
}

void mdisk_state::uart1_txrdy_w(int state)
{
	m_uart1_txrdy = state;
	update_irq(0x1c);
}


//**************************************************************************
//  MACHINE DEFINITIONS
//**************************************************************************

static void mdisk_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

void mdisk_state::mdisk(machine_config &config)
{
	Z80(config, m_cpu, 4_MHz_XTAL);
	m_cpu->set_addrmap(AS_PROGRAM, &mdisk_state::mdisk_mem);
	m_cpu->set_addrmap(AS_IO, &mdisk_state::mdisk_io);

	// 64k internal ram
	RAM(config, m_ram).set_default_size("64K");

	// uart 1
	i8251_device &uart1(I8251(config, "uart1", 4_MHz_XTAL));
	uart1.rxrdy_handler().set(FUNC(mdisk_state::uart1_rxrdy_w));
	uart1.txrdy_handler().set(FUNC(mdisk_state::uart1_txrdy_w));
	uart1.txd_handler().set("ser", FUNC(rs232_port_device::write_txd));
	uart1.rts_handler().set("ser", FUNC(rs232_port_device::write_rts));
	uart1.dtr_handler().set("ser", FUNC(rs232_port_device::write_dtr));

	rs232_port_device &rs232_1(RS232_PORT(config, "ser", default_rs232_devices, nullptr));
	rs232_1.rxd_handler().set("uart1", FUNC(i8251_device::write_rxd));
	rs232_1.cts_handler().set("uart1", FUNC(i8251_device::write_cts));
	rs232_1.dsr_handler().set("uart1", FUNC(i8251_device::write_dsr));

	// uart 2
	i8251_device &uart2(I8251(config, "uart2", 4_MHz_XTAL));
	uart2.txd_handler().set("aux", FUNC(rs232_port_device::write_txd));
	uart2.rts_handler().set("aux", FUNC(rs232_port_device::write_rts));
	uart2.dtr_handler().set("aux", FUNC(rs232_port_device::write_dtr));

	rs232_port_device &rs232_2(RS232_PORT(config, "aux", default_rs232_devices, nullptr));
	rs232_2.rxd_handler().set("uart2", FUNC(i8251_device::write_rxd));
	rs232_2.cts_handler().set("uart2", FUNC(i8251_device::write_cts));
	rs232_2.dsr_handler().set("uart2", FUNC(i8251_device::write_dsr));

	// selectable 4800 or 19200
	clock_device &uart_clock(CLOCK(config, "uart_clock", 4.9152_MHz_XTAL / 32)); // just 9600 for now
	uart_clock.signal_handler().set("uart1", FUNC(i8251_device::write_txc));
	uart_clock.signal_handler().append("uart1", FUNC(i8251_device::write_rxc));
	uart_clock.signal_handler().append("uart2", FUNC(i8251_device::write_txc));
	uart_clock.signal_handler().append("uart2", FUNC(i8251_device::write_rxc));

	// floppy
	I8271(config, m_fdc, 4_MHz_XTAL);
	m_fdc->set_ready_line_connected(true);
	m_fdc->intrq_wr_callback().set(FUNC(mdisk_state::fdc_irq_w));
	m_fdc->drq_wr_callback().set_inputline(m_cpu, INPUT_LINE_NMI);
	m_fdc->hdl_wr_callback().set(FUNC(mdisk_state::fdc_motor_w));
	FLOPPY_CONNECTOR(config, m_floppy[0], mdisk_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[1], mdisk_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( mdisk )
	ROM_REGION(0x2000, "firmware", 0)
	ROM_LOAD("motronic_0090_6815_16.00.bin", 0x0000, 0x2000, CRC(931b3410) SHA1(4593708268d5ef7ffcb330f91218fc0c845abf5e))
ROM_END

ROM_START( fl100 )
	ROM_REGION(0x2000, "firmware", 0)
	ROM_LOAD("fl100.bin", 0x0000, 0x2000, CRC(68800982) SHA1(501c8877a18cef091476b780de605b2bea3853fb))
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY    FULLNAME          FLAGS
COMP( 198?, mdisk, 0,      0,      mdisk,   mdisk, mdisk_state, empty_init, "mupid",   "M-Disk Comp.-A", MACHINE_NOT_WORKING )
COMP( 198?, fl100, mdisk,  0,      mdisk,   mdisk, mdisk_state, empty_init, "Grundig", "FL-100",         MACHINE_NOT_WORKING )



mdt60.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/**************************************************************************

    MDT 60 was designed by Zenith for use with Morrow's Micro-Decision
    computers as a low-cost terminal with limited features. It appears
    to be a stripped-down version of some previously designed terminal,
    with the auxiliary port omitted from the Morrow version even though
    the schematics published in its service manual include it. These
    schematics also include the same keyboard used by the Z-29, though
    the actual keyboard found with one unit was different.

**************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "bus/z29_kbd/keyboard.h"
#include "cpu/m6502/m6502.h"
#include "machine/i8251.h"
#include "video/mc6845.h"
#include "screen.h"


namespace {

class mdt60_state : public driver_device
{
public:
	mdt60_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_keyboard(*this, "keyboard")
		, m_crtc(*this, "crtc")
		, m_uart(*this, "uart%u", 0U)
		, m_charram(*this, "charram")
		, m_charrom(*this, "charrom")
		, m_attrram(*this, "attrram")
		, m_dip0(*this, "DIP0")
		, m_baud_timer(nullptr)
		, m_keyin(false)
		, m_output_reg(0x3f)
		, m_timer_output(false)
	{
	}

	void mdt60(machine_config &mconfig);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	MC6845_UPDATE_ROW(update_row);
	MC6845_ON_UPDATE_ADDR_CHANGED(update_cb);
	TIMER_CALLBACK_MEMBER(baud_timer);

	void keyin_w(int state);
	u8 dip0_r(offs_t offset);
	void reg_w(u8 data);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<z29_keyboard_port_device> m_keyboard;
	required_device<r6545_1_device> m_crtc;
	optional_device_array<i8251_device, 2> m_uart;
	required_shared_ptr<u8> m_charram;
	required_region_ptr<u8> m_charrom;
	required_shared_ptr<u8> m_attrram;
	required_ioport m_dip0;

	emu_timer *m_baud_timer;

	bool m_keyin;
	u8 m_output_reg;
	bool m_timer_output;
};

void mdt60_state::machine_start()
{
	m_baud_timer = timer_alloc(FUNC(mdt60_state::baud_timer), this);
	m_baud_timer->adjust(attotime::zero);

	save_item(NAME(m_keyin));
	save_item(NAME(m_output_reg));
	save_item(NAME(m_timer_output));
}

void mdt60_state::machine_reset()
{
	reg_w(0);
}


TIMER_CALLBACK_MEMBER(mdt60_state::baud_timer)
{
	m_timer_output = !m_timer_output;

	// LS352 multiplexer has inverting outputs
	m_uart[0]->write_rxc(!m_timer_output);
	m_uart[0]->write_txc(!m_timer_output);

	constexpr auto time_base = 16.5888_MHz_XTAL / 9;
	if ((m_output_reg & 0x06) == 0x00)
		m_baud_timer->adjust(attotime::from_ticks(m_timer_output ? 2 : 4, time_base));
	else if ((m_output_reg & 0x06) == 0x06)
		m_baud_timer->adjust(attotime::from_ticks(48, time_base));
	else
		m_baud_timer->adjust(attotime::from_ticks(3 * (m_output_reg & 0x06), time_base));
}

MC6845_UPDATE_ROW(mdt60_state::update_row)
{
	u32 *pix = &bitmap.pix(y);

	for (unsigned x = 0; x < x_count; x++)
	{
		u8 cdata = m_charram[(ma + x) & 0x7ff];
		u8 adata = m_attrram[(ma + x) & 0x3ff] >> (BIT(ma + x, 10) ? 4 : 0);
		u16 dots = m_charrom[cdata << 4 | ra] << 1 | BIT(adata, 3);

		if (x == cursor_x)
			dots = ~dots;
		if (BIT(adata, 2))
			dots = ~dots;
		if (BIT(adata, 0) && (ra & 0xb) == 0xa)
			dots = ~dots;

		rgb_t fg = BIT(adata, 1) ? rgb_t::white() : rgb_t(0xc0, 0xc0, 0xc0);
		rgb_t bg = rgb_t::black();
		if (BIT(m_output_reg, 5))
			std::swap(fg, bg);

		for (int n = 8; n >= 0; n--)
			*pix++ = BIT(dots, n) ? fg : bg;
	}
}

MC6845_ON_UPDATE_ADDR_CHANGED(mdt60_state::update_cb)
{
}


void mdt60_state::keyin_w(int state)
{
	m_keyin = state;
	m_maincpu->set_input_line(m6502_device::IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
}

u8 mdt60_state::dip0_r(offs_t offset)
{
	u8 buffer = 0xff;
	if (!BIT(offset, 0))
		buffer &= m_dip0->read() >> 1 | 0x80;
	if (!BIT(offset, 1))
		buffer &= m_dip0->read() | 0xfe;
	if (!m_keyin)
		buffer &= 0x7f;
	return buffer;
}

void mdt60_state::reg_w(u8 data)
{
	if (BIT(data, 0) != BIT(m_output_reg, 0))
		m_keyboard->keyout_w(BIT(data, 0));

	// Bits 1–2 & 3–4 select 1 of 4 baud rates for each UART; bit 5 selects inverse video
	m_output_reg = data & 0x3f;
}

void mdt60_state::mem_map(address_map &map)
{
	map(0x0000, 0x03ff).mirror(0x1800).ram();
	map(0x0400, 0x07ff).mirror(0x1800).ram().share("attrram");
	map(0x2000, 0x27ff).mirror(0x1800).ram().share("charram");
	map(0x4000, 0x4000).mirror(0x1ffe).rw(m_crtc, FUNC(r6545_1_device::status_r), FUNC(r6545_1_device::address_w));
	map(0x4001, 0x4001).mirror(0x1ffe).rw(m_crtc, FUNC(r6545_1_device::register_r), FUNC(r6545_1_device::register_w));
	map(0x6000, 0x6001).mirror(0x1ffe).rw(m_uart[0], FUNC(i8251_device::read), FUNC(i8251_device::write));
	//map(0x8000, 0x8001).mirror(0x1ffe).rw(m_uart[1], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xa000, 0xa003).mirror(0x1ffc).r(FUNC(mdt60_state::dip0_r));
	map(0xa000, 0xa000).mirror(0x1fff).w(FUNC(mdt60_state::reg_w));
	//map(0xc000, 0xc000).mirror(0x1fff).portr("DIP1");
	map(0xe000, 0xefff).mirror(0x1000).rom().region("coderom", 0);
}


static INPUT_PORTS_START(mdt60)
	PORT_START("DIP0")
	PORT_DIPNAME(0x03, 0x01, "Baud Rate") PORT_DIPLOCATION("SW1:1,2")
	PORT_DIPSETTING(0x03, "300")
	PORT_DIPSETTING(0x02, "1200")
	PORT_DIPSETTING(0x01, "9600")
	PORT_DIPSETTING(0x00, "19200")
	PORT_DIPNAME(0x0c, 0x00, "Parity") PORT_DIPLOCATION("SW1:3,4")
	PORT_DIPSETTING(0x04, "Odd")
	PORT_DIPSETTING(0x0c, "Even")
	PORT_DIPSETTING(0x00, "Mark")
	PORT_DIPSETTING(0x08, "Space")
	PORT_DIPNAME(0x70, 0x00, "Character Set") PORT_DIPLOCATION("SW1:5,6,7")
	PORT_DIPSETTING(0x00, "U.S.")
	PORT_DIPSETTING(0x10, "U.K.")
	PORT_DIPSETTING(0x20, "French")
	PORT_DIPSETTING(0x30, "German")
	PORT_DIPSETTING(0x40, "Spanish")
	PORT_DIPSETTING(0x50, "Danish/Norwegian")
	PORT_DIPSETTING(0x60, "Swedish/Finnish")
	PORT_DIPSETTING(0x70, "Italian")
	PORT_DIPNAME(0x80, 0x80, "Function Key Sequence Type") PORT_DIPLOCATION("SW1:8")
	PORT_DIPSETTING(0x80, "Morrow (FS)")
	PORT_DIPSETTING(0x00, "TeleVideo (SOH...CR)")

	PORT_START("DIP1")
	PORT_BIT(0xff, 0xff, IPT_UNUSED) // SW2 is unpopulated (as is LS244 at U20)
INPUT_PORTS_END

// XTAL frequency is specified as 16.589 MHz on actual parts as well as in MDT 60 schematics.
// This has been assumed to be a lower-precision specification of the common 16.5888 MHz value.

void mdt60_state::mdt60(machine_config &config)
{
	M6512(config, m_maincpu, 16.5888_MHz_XTAL / 9); // R6512AP
	m_maincpu->set_addrmap(AS_PROGRAM, &mdt60_state::mem_map);

	I8251(config, m_uart[0], 16.5888_MHz_XTAL / 9); // TMP8251AP (U19)
	m_uart[0]->rxrdy_handler().set_inputline(m_maincpu, m6512_device::NMI_LINE);
	m_uart[0]->txd_handler().set("comm", FUNC(rs232_port_device::write_txd));
	m_uart[0]->dtr_handler().set("comm", FUNC(rs232_port_device::write_dtr));
	m_uart[0]->rts_handler().set("comm", FUNC(rs232_port_device::write_rts));

	// UART 1 (at U28) is unpopulated and not used by code

	Z29_KEYBOARD(config, m_keyboard, z29_keyboards, "md");
	m_keyboard->keyin_callback().set(FUNC(mdt60_state::keyin_w)).invert();
	// Reset output (pin 2) is not connected

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::amber());
	screen.set_raw(16.5888_MHz_XTAL, 918, 0, 720, 301, 0, 288);
	screen.set_screen_update(m_crtc, FUNC(r6545_1_device::screen_update));

	R6545_1(config, m_crtc, 16.5888_MHz_XTAL / 9); // R6545-1AP
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(9);
	m_crtc->set_update_row_callback(FUNC(mdt60_state::update_row));
	m_crtc->set_on_update_addr_change_callback(FUNC(mdt60_state::update_cb));
	// LPEN is tied to GND (code uses this to determine CRTC type)

	rs232_port_device &comm(RS232_PORT(config, "comm", default_rs232_devices, "loopback"));
	// Service Manual suggests shorting pins with a bent paper clip if no DB-25 loopback connector is available ;-)
	comm.rxd_handler().set(m_uart[0], FUNC(i8251_device::write_rxd));
	comm.dsr_handler().set(m_uart[0], FUNC(i8251_device::write_dsr));
	comm.cts_handler().set(m_uart[0], FUNC(i8251_device::write_cts));
}


ROM_START(mdt60)
	ROM_REGION(0x1000, "coderom", 0)
	// "ROM REV. 1.7 -- Copyright (C) 1983 by Morrow Designs, Inc."
	ROM_LOAD("vb-rom_1.7_bcfd.u9", 0x0000, 0x1000, CRC(3902f7b3) SHA1(ee0ce7f68efe20efe1ab8a44888e276f08fe2d07))

	ROM_REGION(0x1000, "charrom", 0)
	ROM_LOAD("char_gen_b207.u6", 0x0000, 0x1000, CRC(b19432da) SHA1(fa73641c08b778f19a17676a7f4074f69b7b55dd))
ROM_END

} // anonymous namespace


SYST(1983, mdt60, 0, 0, mdt60, mdt60, mdt60_state, empty_init, "Morrow Designs", "MDT 60 Video Display Terminal", MACHINE_SUPPORTS_SAVE)



megadriv_firecore.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

/*
    Firecore / RedKid 'Mega Drive' and 'Genesis' bootleg/clone based systems.

    Firecore offers some extended video modes over a regular Mega Drive
    The YM/FM implementation in the chip has a number of flaws, MAME's older
    YM/FM core was closer, but still 'too good' in many situations.  Some
    software written for Firecore relies on these flaws to sound correct.

*/

#include "emu.h"
#include "megadriv.h"


namespace {


class megadriv_firecore_state : public md_ctrl_state
{
public:
	megadriv_firecore_state(const machine_config& mconfig, device_type type, const char* tag) :
		md_ctrl_state(mconfig, type, tag),
		m_bank(0),
		m_externalbank(0),
		m_romsize(0x400000),
		m_rom(*this, "maincpu")
	{ }

	void megadriv_firecore_3button_ntsc(machine_config &config) ATTR_COLD;
	void megadriv_firecore_3button_pal(machine_config &config) ATTR_COLD;
	void megadriv_firecore_6button_ntsc(machine_config &config) ATTR_COLD;

	void init_atgame40() ATTR_COLD;
	void init_dcat() ATTR_COLD;
	void init_mdhh100() ATTR_COLD;
	void init_sarc110() ATTR_COLD;
	void init_reactmd() ATTR_COLD;

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint16_t read(offs_t offset);

	void megadriv_firecore_map(address_map &map) ATTR_COLD;

	void bank_high_w(offs_t offset, uint16_t data, uint16_t mem_mask);
	void bank_low_w(offs_t offset, uint16_t data, uint16_t mem_mask);
	void bank_upper_w(offs_t offset, uint16_t data, uint16_t mem_mask);

	void b01036_w(offs_t offset, uint16_t data, uint16_t mem_mask);

	int m_bank;
	int m_externalbank;
	int m_romsize;

	required_region_ptr<uint16_t> m_rom;
};

uint16_t megadriv_firecore_state::read(offs_t offset)
{
	return m_rom[(((m_externalbank | m_bank) >> 1) + offset) & (m_romsize - 1)];
}

void megadriv_firecore_state::bank_high_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	data &= 0x7f;
	mem_mask &= 0x7f;

	m_bank = (m_bank & 0xff80ffff) | (data & mem_mask) << 16;
	logerror("%s: bank_high_w bank is now %08x\n", machine().describe_context(), m_bank);
}

void megadriv_firecore_state::bank_low_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	m_bank = (m_bank & 0xffff0000) | (data & mem_mask);
	logerror("%s: bank_low_w bank is now %08x\n", machine().describe_context(), m_bank);
}

void megadriv_firecore_state::bank_upper_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	// this is handled differently to the other writes, probably some external logic
	// rather than the same banking
	// written before bank_high and bank_low
	m_bank |= 0x800000;
	logerror("%s: bank_upper_w (%04x %04x) bank is now %08x\n", machine().describe_context(), data, mem_mask, m_bank);
}

void megadriv_firecore_state::b01036_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	// all games in atgame40 that fail to display anything write 0x0001 here
	// could be coincidence, but could also be enabling the alt display mode?
	logerror("%s: b01036_w %04x %04x (for games with no display?)\n", machine().describe_context(), data, mem_mask);
}

void megadriv_firecore_state::megadriv_firecore_map(address_map &map)
{
	megadriv_68k_base_map(map);

	map(0x000000, 0x3fffff).r(FUNC(megadriv_firecore_state::read)); // Cartridge Program ROM

	map(0xa10104, 0xa10105).w(FUNC(megadriv_firecore_state::bank_upper_w)); // read and written

	map(0xb01028, 0xb01029).w(FUNC(megadriv_firecore_state::bank_low_w));
	map(0xb0102a, 0xb0102b).w(FUNC(megadriv_firecore_state::bank_high_w));

	map(0xb01036, 0xb01037).w(FUNC(megadriv_firecore_state::b01036_w));
}

// controller is wired directly into unit, no controller slots
static INPUT_PORTS_START( firecore_3button )
	PORT_INCLUDE( md_common )

	// TODO: how do the MENU buttons on the two controllers work?
INPUT_PORTS_END


INPUT_PORTS_START( mympac )
	PORT_START("PAD1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )    PORT_8WAY
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )  PORT_8WAY
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )  PORT_8WAY
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Coin Button 1") // not coin slots
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Coin Button 2")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_START2 )
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x0f00, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PAD2")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( firecore_6button )
	PORT_INCLUDE( md_common )

	PORT_MODIFY("PAD1") // Extra buttons for Joypad 1 (6 button + start + mode)
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(1) PORT_NAME("%p Z")
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(1) PORT_NAME("%p Y")
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("%p X")
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_SELECT )  PORT_PLAYER(1) PORT_NAME("%p Mode")

	PORT_MODIFY("PAD2") // Extra buttons for Joypad 2 (6 button + start + mode)
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(2) PORT_NAME("%p Z")
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(2) PORT_NAME("%p Y")
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("%p X")
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_SELECT )  PORT_PLAYER(2) PORT_NAME("%p Mode")
INPUT_PORTS_END

static INPUT_PORTS_START( msi_6button )
	PORT_INCLUDE( firecore_6button )

	PORT_MODIFY("PAD2") // no 2nd pad
	PORT_BIT( 0x0fff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("RESET") // RESET button on controller to the left of START
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1) PORT_NAME("Reset")
INPUT_PORTS_END

void megadriv_firecore_state::machine_start()
{
	md_ctrl_state::machine_start();
	m_vdp->stop_timers();
	save_item(NAME(m_bank));
	save_item(NAME(m_externalbank));
	save_item(NAME(m_romsize));
}

void megadriv_firecore_state::machine_reset()
{
	m_bank = 0;
	md_ctrl_state::machine_reset();
}

void megadriv_firecore_state::megadriv_firecore_3button_ntsc(machine_config &config)
{
	md_ntsc(config);

	ctrl1_3button(config);
	ctrl2_3button(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &megadriv_firecore_state::megadriv_firecore_map);
}

void megadriv_firecore_state::megadriv_firecore_3button_pal(machine_config &config)
{
	md_pal(config);

	ctrl1_3button(config);
	ctrl2_3button(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &megadriv_firecore_state::megadriv_firecore_map);
}

void megadriv_firecore_state::megadriv_firecore_6button_ntsc(machine_config &config)
{
	megadriv_firecore_3button_ntsc(config);

	ctrl1_6button(config);
	ctrl2_6button(config);
}


ROM_START( matet )
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "tetrismyarcade_s29gl032n90tfi04_0001227e.bin", 0x000000, 0x400000, CRC(09b5af89) SHA1(85e506923fd803f05cc8f579f37331b608fea744) )
	ROM_IGNORE(0x100)
ROM_END

ROM_START( mateta )
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "testris_s29gl032m90tfir4_0001227e.bin", 0x000000, 0x400000, CRC(656ffc77) SHA1(da7ca2d4c2bff3e583f5ad30aa4fe722691a03d9) )
	ROM_IGNORE(0x100)
ROM_END


ROM_START( mypac )
	ROM_REGION( 0x800000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "en29lb160bb.bin", 0x000000, 0x200000, CRC(d741a601) SHA1(a8d89034458b14c5cea83980be5400b82081b274) )
ROM_END

ROM_START( mypaca )
	ROM_REGION( 0x800000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "pacmanarcade_s29gl064n90tfi04_0001227e.bin", 0x000000, 0x800000, CRC(41495033) SHA1(219f0bd38b8a646ca43c9679aeed02c121467cd7) )
	ROM_IGNORE(0x100)
ROM_END

ROM_START( mympac )
	ROM_REGION( 0x800000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "m29w640ft.bin", 0x000000, 0x800000, CRC(d6ceda9e) SHA1(c897f8d5661fea0c030daf9c5e92524eb4e71d52) )
ROM_END

ROM_START( mygalag )
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "galaga_standup_s29al016d70tfi02_00012249.bin", 0x000000, 0x200000, CRC(8f3d2e05) SHA1(8f6a54e5a8ee55e7a6cae3e72b8e70c4eee2c1ef) )
ROM_END

ROM_START( mygalaga )
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "galaga_s29jl032h70tfi01_0001227e.bin", 0x000000, 0x400000, CRC(e775089a) SHA1(0938afa8e92a8c77b4fb86e0ec044fbb2b572570) )
ROM_END

ROM_START( mysinv )
	ROM_REGION( 0x800000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "spaceinvaders_s29gl0640n90tfi04_0001227e.bin", 0x000000, 0x800000, CRC(55e001d1) SHA1(1eaa377bf78a0f1f492565a9f38b2f7d60d0e440) )
	ROM_IGNORE(0x100)
ROM_END



/*

As the atgame40 is not running on standard MegaDrive hardware quite a few of these games rely
on an unsupported video mode (they still play sounds and resond to inputs / do colour fades)

00 Menu                             located at 00800000  WORKS
--
01 Air Hockey                       located at 0000c800  WORKS
02 Black Sheep                      located at 0002935a  BOOTS - NO DISPLAY
03 Bomber                           located at 000b6f5a  WORKS
04 Bottle Taps Race                 located at 000d6f5a  WORKS
05 Brain Switch                     located at 000ec760  BOOTS - NO DISPLAY
06 Bulls and Cows                   located at 00800000  BOOTS - NO DISPLAY
07 Cannon                           located at 0013e360  WORKS
08 Checker                          located at 0016301e  WORKS
09 Chess                            located at 001aa232  WORKS
10 Colour Puzzle                    located at 001f0a32  BOOTS - NO DISPLAY
11 Cross The Road                   located at 008a2800  BOOTS - NO DISPLAY
12 Curling 2010                     located at 00953486  BOOTS - NO DISPLAY
13 Fight or Lose                    located at 009f002e  WORKS
14 Fire Fly Glow                    located at 00a09ede  BOOTS - NO DISPLAY
15 Fish Story                       located at 00aa2ede  BOOTS - NO DISPLAY
16 Flash Memory                     located at 00285632  BOOTS - NO DISPLAY
17 Formula Challenge                located at 00310232  BOOTS - NO DISPLAY
18 Hexagons                         located at 00394a32  WORKS
19 Jacks Pea                        located at 003b4a32  BOOTS - NO DISPLAY
20 Jewel Magic                      located at 00b4a874  BOOTS - NO DISPLAY
21 Logic Dial                       located at 0040b4ac  BOOTS - NO DISPLAY
22 Table Magic                      located at 00be8f8c  BOOTS - NO DISPLAY
23 Mahjong                          located at 0049e8ac  WORKS
24 Match Eleven                     located at 004ae8ac  BOOTS - NO DISPLAY
25 Mega Brain Switch                located at 005390ac  BOOTS - NO DISPLAY
26 Memory                           located at 0058a0ac  WORKS
27 Memory Match                     located at 00c8538c  BOOTS - NO DISPLAY
28 Mirror Mirror                    located at 00d2178c  BOOTS - NO DISPLAY
29 Mr Balls                         located at 0059a0ac  WORKS
30 Navel Power                      located at 005c0a08  WORKS
31 Panic Lift                       located at 00d86f8c  BOOTS - NO DISPLAY
32 Reaction Match                   located at 00e2038c  BOOTS - NO DISPLAY
33 Snake                            located at 005dbbd4  WORKS
34 Space Hunter                     located at 005ebbd4  BOOTS - NO DISPLAY
35 Spider                           located at 006817d4  WORKS
36 Sudoku Quiz                      located at 0069efb4  BOOTS - NO DISPLAY
37 Treasure Hunt                    located at 00eb9b8c  BOOTS - NO DISPLAY
38 UFO Sighting                     located at 00f5938c  BOOTS - NO DISPLAY
39 Warehouse Keeper                 located at 00730fb4  WORKS
40 Whack a Wolf                     located at 00740fb4  BOOTS - NO DISPLAY
*/

ROM_START( atgame40 )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "40bonusgamesin1.bin", 0x800000, 0x800000, CRC(4eba6e83) SHA1(b8edf1b6ecb70a136b551f1454ba8afa45bd8bc1) )
	ROM_CONTINUE(0x000000, 0x800000)
ROM_END


ROM_START(dcat16)
	ROM_REGION(0x1000000, "maincpu", ROMREGION_ERASEFF)
	ROM_LOAD16_WORD_SWAP( "mg6025.u1", 0x0000,  0x800000, CRC(5453d673) SHA1(b9f8d849cbed81fe73525229f4897ccaeeb7a833) )
	ROM_RELOAD(0x800000,0x800000)
ROM_END


ROM_START(mahg156)
	ROM_REGION(0x8000000, "maincpu", ROMREGION_ERASEFF)
	ROM_LOAD16_WORD_SWAP( "md156.u3", 0x0000,  0x8000000, CRC(665fc68c) SHA1(6b765f96716c4a0abf3d27252ec82be6b0d9a985) )
//  the Megadrive ROMs for the most part appear to be hacked versions of the games / old scene dumps
//  some are region locked to differing regions (not all games present in ROM appear on the menu)
//  ROM_COPY( "maincpu", 0x0000000, 0, 0x080000) // FORGOTTEN WORLDS
//  ROM_COPY( "maincpu", 0x0080000, 0, 0x080000) // FIRE PRO WRESTLING
//  ROM_COPY( "maincpu", 0x0100000, 0, 0x080000) // GHOST BUSTERS
//  ROM_COPY( "maincpu", 0x0180000, 0, 0x080000) // DICK TRACY
//  ROM_COPY( "maincpu", 0x0200000, 0, 0x080000) // DEVIL CRASH
//  ROM_COPY( "maincpu", 0x0280000, 0, 0x080000) // DECAP ATTACK
//  ROM_COPY( "maincpu", 0x0300000, 0, 0x080000) // DARWIN 4081
//  ROM_COPY( "maincpu", 0x0380000, 0, 0x080000) // CRACK DOWN
//  ROM_COPY( "maincpu", 0x0400000, 0, 0x080000) // CAPTAIN PLANET
//  ROM_COPY( "maincpu", 0x0480000, 0, 0x080000) // CALIFORNIA GAMES
//  ROM_COPY( "maincpu", 0x0500000, 0, 0x080000) // CADASH
//  ROM_COPY( "maincpu", 0x0580000, 0, 0x080000) // BOOGIE WOOGIE BOWLING
//  ROM_COPY( "maincpu", 0x0600000, 0, 0x080000) // BIMINI RUN
//  ROM_COPY( "maincpu", 0x0700000, 0, 0x080000) // BATTLE TOADS
//  ROM_COPY( "maincpu", 0x0780000, 0, 0x080000) // TROUBLE SHOOTER
//  ROM_COPY( "maincpu", 0x0800000, 0, 0x080000) // BURNING FORCE
//  ROM_COPY( "maincpu", 0x0880000, 0, 0x080000) // FAERY TALE ADVENTURE
//  ROM_COPY( "maincpu", 0x0900000, 0, 0x080000) // E-SWAT
//  ROM_COPY( "maincpu", 0x0980000, 0, 0x080000) // ELEMENTAL MASTER
//  ROM_COPY( "maincpu", 0x0a00000, 0, 0x080000) // EA HOCKEY
//  ROM_COPY( "maincpu", 0x0a80000, 0, 0x080000) // DARK CASTLE
//  ROM_COPY( "maincpu", 0x0b00000, 0, 0x080000) // CYBORG JUSTICE (CENSOR)
//  ROM_COPY( "maincpu", 0x0b80000, 0, 0x080000) // LITTLE MERMAID
//  ROM_COPY( "maincpu", 0x0c00000, 0, 0x080000) // DORAEMON
//  ROM_COPY( "maincpu", 0x0c80000, 0, 0x080000) // SONIC
//  ROM_COPY( "maincpu", 0x0d00000, 0, 0x080000) // WANI WANI WORLD
//  ROM_COPY( "maincpu", 0x0d80000, 0, 0x080000) // GOLDEN AXE 2
//  etc.
ROM_END

ROM_START(mdhh100)
	ROM_REGION(0x8000000, "maincpu", ROMREGION_ERASEFF)
	ROM_LOAD16_WORD_SWAP( "s29gl01gp11tfir1.u13", 0x0000,  0x8000000, CRC(564ab33a) SHA1(e455aaa9ed6f302d1ebe55b5202f983af612c415) )
ROM_END

ROM_START( msi_sf2 )
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "29lv320.bin", 0x000000, 0x400000, CRC(465b12f0) SHA1(7a058f6feb4f08f56ae0f7369c2ca9a9fe2ed40e) )
ROM_END

ROM_START( reactmd )
	ROM_REGION( 0x2000000, "maincpu", ROMREGION_ERASE00 ) // this contains the MD games and main boot menu
	ROM_LOAD16_WORD_SWAP( "reactormd.bin", 0x0000, 0x2000000, CRC(fe9664a4) SHA1(d475b524f576c9d1d90aed20c7467cc652396baf) )

	ROM_REGION( 0x4000000, "sunplus", ROMREGION_ERASE00 ) // this contains the SunPlus games
	ROM_LOAD16_WORD_SWAP( "reactor_md_sunplus-full.bin", 0x0000, 0x4000000, CRC(843aa58c) SHA1(07cdc6d4aa0057939c145ece01a9aca73c7f1f2b) )
	ROM_IGNORE(0x4000000) // the 2nd half of the ROM can't be accessed by the PCB (address line tied low) (contains garbage? data)
ROM_END

ROM_START( sarc110 )
	ROM_REGION( 0x1000000, "maincpu", 0 ) // Mega Drive part
	ROM_LOAD16_WORD_SWAP( "superarcade.bin", 0x000000, 0x1000000, CRC(be732867) SHA1(3857b2fbddd6a548c81caf64122e47a0df079be5) )

	ROM_REGION( 0x400000, "mainrom", 0 ) // VT02/03 part
	ROM_LOAD( "ic1.prg", 0x00000, 0x400000, CRC(de76f71f) SHA1(ff6b37a76c6463af7ae901918fc008b4a2863951) )
ROM_END

ROM_START( sarc110a )
	ROM_REGION( 0x1000000, "maincpu", 0 ) // Mega Drive part
	ROM_LOAD16_WORD_SWAP( "superarcade.bin", 0x000000, 0x1000000, CRC(be732867) SHA1(3857b2fbddd6a548c81caf64122e47a0df079be5) )

	ROM_REGION( 0x400000, "mainrom", 0 ) // VT02/03 part
	ROM_LOAD( "ic1_ver2.prg", 0x00000, 0x400000, CRC(b97a0dc7) SHA1(bace32d73184df914113de5336e29a7a6f4c03fa) )
ROM_END

void megadriv_firecore_state::init_atgame40()
{
	m_romsize = memregion("maincpu")->bytes();
	init_megadrie();
}

void megadriv_firecore_state::init_sarc110()
{
	m_romsize = memregion("maincpu")->bytes();
	m_externalbank = 0x800000;
	init_megadrie();
}

void megadriv_firecore_state::init_reactmd()
{
	m_romsize = memregion("maincpu")->bytes();
	m_externalbank = 0x1800000;
	init_megadrie();

	// decryption of the SunPlus part
	uint16_t *ROM = (uint16_t*)memregion("sunplus")->base();
	int size = memregion("sunplus")->bytes();

	for (int i = 0; i < size/2; i++)
	{
		ROM[i] = bitswap<16>(ROM[i], 15, 13, 14, 12,  7,  6,  5,  4,
									 11, 10, 9,  8,   3,  1,  2,  0);

		ROM[i] = ROM[i] ^ 0xa5a5;
	}
}

void megadriv_firecore_state::init_dcat()
{
	m_romsize = memregion("maincpu")->bytes();
	init_megadriv();
}

void megadriv_firecore_state::init_mdhh100()
{
	m_romsize = memregion("maincpu")->bytes();
	m_externalbank = 0x7800000;
	init_megadriv();
}


} // anonymous namespace

// Games below have a device at b0102x which appears to either be able to select ROM base on a byte boundary
// OR maybe are running from RAM instead of ROM (with an auto-copy at the start?) with that being a DMA operation.

// Technically this is a MD type cartridge, but it doesn't seem to be designed for use with a standard MD as it contains
// nothing but the 16Mbyte ROM and a 5v to 3.3v converter yet the code clearly requires some extensive banking logic.
// Testing it on a real MD shows nothing, not even the menu.
//
// We don't seem to emulate the system it's designed for, so for now just treat it as its own thing (which may become
// the basis of a driver for that console)
//
// due to differences in the SoC compared to real MD hardware (including sound + new video modes) these have been left
// as NOT WORKING for now although some games run to a degree

CONS( 2021, mypac,     0,        0, megadriv_firecore_3button_ntsc,  mympac, megadriv_firecore_state, init_megadriv,           "dreamGEAR", "My Arcade Pac-Man (DGUNL-4198, Pocket Player Pro)", MACHINE_NOT_WORKING | ROT270 )
CONS( 2021, mypaca,    mypac,    0, megadriv_firecore_3button_ntsc,  mympac, megadriv_firecore_state, init_megadriv,           "dreamGEAR", "My Arcade Pac-Man (DGUNL-4194, Micro Player Pro)", MACHINE_NOT_WORKING | ROT270 )

CONS( 2021, mympac,    0,        0, megadriv_firecore_3button_ntsc,  mympac, megadriv_firecore_state, init_megadriv,           "dreamGEAR", "My Arcade Ms. Pac-Man (DGUNL-7010, Pocket Player Pro)", MACHINE_NOT_WORKING | ROT270 )

// menu uses unsupported extended mode
CONS( 2021, mygalag,   0,        0, megadriv_firecore_3button_ntsc,  mympac, megadriv_firecore_state, init_megadriv,           "dreamGEAR", "My Arcade Galaga (DGUNL-4195, Micro Player Pro)", MACHINE_NOT_WORKING | ROT270 )
CONS( 2021, mygalaga,  mygalag,  0, megadriv_firecore_3button_ntsc,  mympac, megadriv_firecore_state, init_megadriv,           "dreamGEAR", "My Arcade Galaga (DGUNL-4199, Pocket Player Pro)", MACHINE_NOT_WORKING | ROT270 )

CONS( 2021, mysinv,    0,        0, megadriv_firecore_3button_ntsc,  mympac, megadriv_firecore_state, init_megadriv,           "dreamGEAR", "My Arcade Space Invaders (DGUNL-7006, Pocket Player Pro)", MACHINE_NOT_WORKING | ROT90 )

CONS( 2012, atgame40,  0,        0, megadriv_firecore_3button_pal,   firecore_3button, megadriv_firecore_state, init_atgame40, "AtGames",   "40 Bonus Games in 1 (AtGames)", MACHINE_NOT_WORKING)

CONS( 2021, matet,     0,        0, megadriv_firecore_3button_ntsc,  firecore_3button, megadriv_firecore_state, init_megadriv, "dreamGEAR", "My Arcade Tetris (DGUNL-7028, Pocket Player Pro)", MACHINE_NOT_WORKING)
CONS( 2021, mateta,    matet,    0, megadriv_firecore_3button_ntsc,  firecore_3button, megadriv_firecore_state, init_megadriv, "dreamGEAR", "My Arcade Tetris (DGUNL-7025, Micro Player Pro)", MACHINE_NOT_WORKING)

// has an SD card slot?
CONS( 200?, dcat16,    0,        0, megadriv_firecore_3button_ntsc,  firecore_3button, megadriv_firecore_state, init_dcat,     "Firecore",  "D-CAT16 (Mega Drive handheld)",  MACHINE_NOT_WORKING )
// seems to be based on the AT games units, requires custom mode for menu?
CONS( 201?, mahg156,   0,        0, megadriv_firecore_3button_ntsc,  firecore_3button, megadriv_firecore_state, init_mdhh100,  "<unknown>", "Mini Arcade Handheld Game Console 2.8 Inch Screen Built in 156 Retro Games (Mega Drive handheld)",  MACHINE_NOT_WORKING )
// game-boy like handheld, pink in colour, 6 button controller (+ home select, start, vol buttons)
CONS( 201?, mdhh100,   0,        0, megadriv_firecore_3button_ntsc,  firecore_3button, megadriv_firecore_state, init_mdhh100,  "<unknown>", "unknown 100-in-1 handheld (Mega Drive based)",  MACHINE_NOT_WORKING )

// From a European unit but NTSC? - code is hacked from original USA Genesis game with region check still intact? (does the clone hardware always identify as such? or does the bypassed boot code skip the check?)
CONS( 2018, msi_sf2,   0,        0, megadriv_firecore_6button_ntsc,  msi_6button,      megadriv_firecore_state, init_megadriv, "MSI / Capcom / Sega", "Street Fighter II: Special Champion Edition (MSI Plug & Play) (Europe)", 0)

// Two systems in one unit - Firecore Genesis and SunPlus
CONS( 2009, reactmd,   0,        0, megadriv_firecore_3button_pal,   firecore_3button, megadriv_firecore_state, init_reactmd,  "AtGames / Sega / Waixing", "Reactor MD (Firecore/SunPlus hybrid, PAL)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// Two systems in one unit - Firecore Genesis and VT02/VT03
CONS( 200?, sarc110,   0,        0, megadriv_firecore_3button_pal,   firecore_3button, megadriv_firecore_state, init_sarc110,  "<unknown>", "Super Arcade 101-in-1 (Firecore/VT hybrid, set 1)", MACHINE_NOT_WORKING)
CONS( 200?, sarc110a,  sarc110,  0, megadriv_firecore_3button_pal,   firecore_3button, megadriv_firecore_state, init_sarc110,  "<unknown>", "Super Arcade 101-in-1 (Firecore/VT hybrid, set 2)", MACHINE_NOT_WORKING)



megadriv_rad.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

/*
    Radica 'Mega Drive' and 'Genesis' clones
    these were mini battery operated "TV Game" consoles with wired in controller and no cartslot
    fully licensed by Sega

    reproduction 'System on a Chip' hardware, not perfect, flaws will need emulating eventually.

*/

#include "emu.h"
#include "megadriv.h"


namespace {

class megadriv_radica_state_base : public md_ctrl_state
{
public:
	megadriv_radica_state_base(const machine_config &mconfig, device_type type, const char *tag) :
		md_ctrl_state(mconfig, type, tag),
		m_bank(0),
		m_romsize(0x400000),
		m_rom(*this, "maincpu")
	{ }

protected:
	virtual uint16_t read(offs_t offset);
	uint16_t read_a13(offs_t offset);

	virtual void megadriv_radica_map(address_map &map) ATTR_COLD;

	void radica_base_map(address_map &map) ATTR_COLD;

	int m_bank;
	int m_romsize;

	required_region_ptr<uint16_t> m_rom;
};



class megadriv_radica_state : public megadriv_radica_state_base
{
public:
	megadriv_radica_state(const machine_config& mconfig, device_type type, const char* tag) :
		megadriv_radica_state_base(mconfig, type, tag)
	{ }

	void megadriv_radica_3button_ntsc(machine_config &config);
	void megadriv_radica_3button_pal(machine_config &config);

	void megadriv_radica_6button_ntsc(machine_config &config);
	void megadriv_radica_6button_pal(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
};


void megadriv_radica_state_base::radica_base_map(address_map &map)
{
	megadriv_68k_base_map(map);

	map(0x000000, 0x3fffff).r(FUNC(megadriv_radica_state_base::read)); // Cartridge Program ROM
}

void megadriv_radica_state_base::megadriv_radica_map(address_map &map)
{
	radica_base_map(map);

	map(0xa13000, 0xa130ff).r(FUNC(megadriv_radica_state_base::read_a13));
}

uint16_t megadriv_radica_state_base::read(offs_t offset)
{
	return m_rom[(((m_bank * 0x10000) + (offset << 1)) & (m_romsize - 1))/2];
}

uint16_t megadriv_radica_state_base::read_a13(offs_t offset)
{
	if (offset < 0x80)
		m_bank = offset & 0x3f;

	// low bit gets set when selecting cannon fodder or mega lo mania in the rad_ssoc set, pointing to the wrong area, but rad_gen1 needs it for the menu
	// as they're standalones it could just be different logic
	if (m_bank != 0x3f)
		m_bank &= 0x3e;

	return 0;
}

// controller is wired directly into unit, no controller slots
static INPUT_PORTS_START( radica_3button )
	PORT_INCLUDE( md_common )

	// TODO: how do the MENU buttons on the two controllers work?
INPUT_PORTS_END

// the 6-in-1 and Sonic Gold units really only have a single wired controller, and no way to connect a 2nd one, despite having some 2 player games!
static INPUT_PORTS_START( radica_3button_1player )
	PORT_INCLUDE( md_common )

	PORT_MODIFY("PAD2")
	PORT_BIT( 0x0fff, IP_ACTIVE_LOW, IPT_UNUSED )

	// TODO: how does the MENU button on the controller work?
INPUT_PORTS_END

static INPUT_PORTS_START( radica_6button )
	PORT_INCLUDE( md_common )

	PORT_MODIFY("PAD1") // Extra buttons for Joypad 1 (6 button + start + mode)
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(1) PORT_NAME("%p Z")
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(1) PORT_NAME("%p Y")
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("%p X")
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_SELECT )  PORT_PLAYER(1) PORT_NAME("%p Mode")

	PORT_MODIFY("PAD2") // Extra buttons for Joypad 2 (6 button + start + mode)
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(2) PORT_NAME("%p Z")
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(2) PORT_NAME("%p Y")
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("%p X")
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_SELECT )  PORT_PLAYER(2) PORT_NAME("%p Mode")
INPUT_PORTS_END


void megadriv_radica_state::machine_start()
{
	megadriv_radica_state_base::machine_start();

	m_vdp->stop_timers();

	save_item(NAME(m_bank));
}

void megadriv_radica_state::machine_reset()
{
	m_bank = 0;
	megadriv_radica_state_base::machine_reset();
}

void megadriv_radica_state::megadriv_radica_3button_ntsc(machine_config &config)
{
	md_ntsc(config);

	ctrl1_3button(config);
	ctrl2_3button(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &megadriv_radica_state::megadriv_radica_map);
}

void megadriv_radica_state::megadriv_radica_3button_pal(machine_config &config)
{
	md_pal(config);

	ctrl1_3button(config);
	ctrl2_3button(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &megadriv_radica_state::megadriv_radica_map);
}

void megadriv_radica_state::megadriv_radica_6button_pal(machine_config &config)
{
	megadriv_radica_3button_pal(config);

	ctrl1_6button(config);
	ctrl2_6button(config);
}

void megadriv_radica_state::megadriv_radica_6button_ntsc(machine_config &config)
{
	megadriv_radica_3button_ntsc(config);

	ctrl1_6button(config);
	ctrl2_6button(config);
}

ROM_START( rad_sf2 )
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "radica_megadrive_streetfighter2_usa.bin", 0x000000, 0x400000, CRC(a4426df8) SHA1(091f2a95ebd091141de5bcb83562c6087708cb32) )
ROM_END

ROM_START( rad_sf2uk )
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "radica_megadrive_streetfighter2_uk.bin", 0x000000, 0x400000,  CRC(868afb44) SHA1(f4339e36272c18b1d49aa4095127ed18e0961df6) )
ROM_END

ROM_START( mdtvp3j )
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "playtv_vol3.bin", 0x000000, 0x400000,  CRC(d2daf376) SHA1(147b88d7aff834146c649077b43312c71b973298) )
ROM_END

ROM_START( rad_gen1 )
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "radica_megadrive_vol1_blue_usa.bin", 0x000000, 0x400000,  CRC(3b4c8438) SHA1(5ed9c053f9ebc8d4bf571d57e562cf347585d158) )
ROM_END

ROM_START( rad_md1 )
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "radica_megadrive_vol1_blue_europe.bin", 0x000000, 0x400000, CRC(85867db1) SHA1(ddc596e2e68dc872bc0679a2de7a295b4c6d6b8e) )
ROM_END

ROM_START( rad_md1uk )
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "radicauk.u2", 0x000000, 0x400000, CRC(03a6734b) SHA1(255048d46b593bc975b3a6c44e8b8e35917511c7) )
ROM_END

ROM_START( mdtvp1j )
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "l08y6_i_32m.u2", 0x000000, 0x400000, CRC(740a8859) SHA1(cf1212ef28e75e2cea752cf10a06ea715a30ae07) ) // 04-07-23 date sticker (23 July 2004)
ROM_END

ROM_START( rad_gen2 )
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "radica_genesis_vol2_red_usa.bin", 0x000000, 0x400000, CRC(7c1a0f0e) SHA1(a6441f75a4cd48f1563aeafdfbdde00202d4067c) )
ROM_END

ROM_START( rad_md2uk )
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "radica_megadrive_vol2_red_uk.bin", 0x000000, 0x400000, CRC(b68fd025) SHA1(b8f9c505653d6dd2b62840f078f828360faf8abc) )
ROM_END

ROM_START( mdtvp2j )
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "playtv_vol2.bin", 0x000000, 0x400000, CRC(4d887d12) SHA1(b7f70abd12c3a3c68d1ad127a1475b704e898f51) )
ROM_END

ROM_START( rad_ssoc )
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_LOAD( "radica_sensiblesoccer_uk.bin", 0x000000, 0x400000,  CRC(b8745ab3) SHA1(0ab3f26e5ffd288e5a3a5db676951b9095299eb0) ) // should be byteswapped?
ROM_END

ROM_START( rad_sonic )
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "radica_supersonicgold_usa.bin", 0x000000, 0x400000, CRC(853c9140) SHA1(cf70a9cdd3be4d8d1b6195698db3a941f4908791) )
ROM_END

ROM_START( rad_sonicuk )
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "radica_supersonicgold_uk.bin", 0x000000, 0x400000, CRC(ed774018) SHA1(cc2f7183e128c947463e3a43a0184b835ea16db8) )
ROM_END

// once byteswapped this matches "outrun 2019 (usa) (beta).bin  megadriv:outr2019up Out Run 2019 (USA, Prototype)"
// this was dumped from a PAL/UK unit, so maybe that 'beta' is really an alt Euro release, or was simply dumped from one of these Radica units and mislabeled?
ROM_START( rad_orun )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "outrun.bin", 0x000000, 0x100000, CRC(4fd6d653) SHA1(57f0e4550ff883e4bb7857caef2c893c21f80b42) )
ROM_END

ROM_START( rad_mncr )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASEFF )
	// radica_menacer_byteswapped.bin = mpr-15075-f.u1        megadriv:menacer  Menacer 6-Game Cartridge (Europe, USA)
	ROM_LOAD16_WORD_SWAP( "radica_menacer.bin", 0x000000, 0x100000, CRC(5f9ef4a4) SHA1(f28350e7325cb7469d760d97ee452a9d846eb3d4) )
ROM_END

ROM_START( banmrid )
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "b75909a.u10", 0x000000, 0x400000, CRC(b439e06e) SHA1(3a87fc16186b7042dd92c7cf68c4284cd86f9175) )
ROM_END


} // anonymous namespace


// US versions show 'Genesis' on the menu,    show a www.radicagames.com splash screen, and use NTSC versions of the ROMs, sometimes region locked
// EU versions show 'Mega Drive' on the menu, show a www.radicagames.com splash screen, and use PAL versions of the ROMs, sometimes region locked
// UK versions show "Mega Drive' on the menu, show a www.radicauk.com splash screen,    and use PAL versions of the ROMs, sometimes region locked

CONS( 2004, rad_gen1,  0,        0, megadriv_radica_3button_ntsc, radica_3button_1player, megadriv_radica_state, init_megadriv, "Radica / Sega",                     "Genesis Collection Volume 1 (Radica, Arcade Legends) (USA)", 0)
CONS( 2004, rad_md1,   rad_gen1, 0, megadriv_radica_3button_pal,  radica_3button_1player, megadriv_radica_state, init_megadrie, "Radica / Sega",                     "Mega Drive Collection Volume 1 (Radica, Arcade Legends) (Europe)", 0)
CONS( 2004, rad_md1uk, rad_gen1, 0, megadriv_radica_3button_pal,  radica_3button_1player, megadriv_radica_state, init_megadrie, "Radica / Sega",                     "Mega Drive Collection Volume 1 (Radica, Arcade Legends) (UK)", 0)
CONS( 2004, mdtvp1j,   rad_gen1, 0, megadriv_radica_3button_ntsc, radica_3button_1player, megadriv_radica_state, init_megadriv, "Sega Toys",                         "Mega Drive Play TV 1 (Japan)", 0) // expects US region despite being a Japanese unit (Bean Machine is region locked)

CONS( 2004, rad_gen2,  0,        0, megadriv_radica_3button_ntsc, radica_3button_1player, megadriv_radica_state, init_megadriv, "Radica / Sega",                     "Genesis Collection Volume 2 (Radica, Arcade Legends) (USA)", 0)
CONS( 2004, rad_md2uk, rad_gen2, 0, megadriv_radica_3button_pal,  radica_3button_1player, megadriv_radica_state, init_megadrie, "Radica / Sega",                     "Mega Drive Collection Volume 2 (Radica, Arcade Legends) (UK)", 0)
// is there a Europe version with Radica Games boot screen and Mega Drive text?
CONS( 2004, mdtvp2j,   rad_gen2, 0, megadriv_radica_3button_ntsc, radica_3button_1player, megadriv_radica_state, init_megadriv, "Sega Toys",                         "Mega Drive Play TV 2 (Japan)", 0)

// box calls this Volume 3
CONS( 2004, rad_sonic,  0,        0, megadriv_radica_3button_ntsc, radica_3button_1player, megadriv_radica_state, init_megadriv, "Radica / Sega",                     "Super Sonic Gold (Radica Plug & Play) (USA)", 0)
CONS( 2004, rad_sonicuk,rad_sonic,0, megadriv_radica_3button_pal,  radica_3button_1player, megadriv_radica_state, init_megadrie, "Radica / Sega",                     "Super Sonic Gold (Radica Plug & Play) (UK)", 0)
// is there a Europe version with Radica Games boot screen and Mega Drive text?

CONS( 2004, rad_sf2,   0,        0, megadriv_radica_6button_ntsc, radica_6button,         megadriv_radica_state, init_megadriv, "Radica / Capcom / Sega",            "Street Fighter II: Special Champion Edition [Ghouls'n Ghosts] (Radica, Arcade Legends) (USA)", 0)
CONS( 2004, rad_sf2uk, rad_sf2,  0, megadriv_radica_6button_pal,  radica_6button,         megadriv_radica_state, init_megadrie, "Radica / Capcom / Sega",            "Street Fighter II: Special Champion Edition [Ghouls'n Ghosts] (Radica, Arcade Legends) (UK)", 0)
// is there a Europe version with Radica Games boot screen and Mega Drive text?
CONS( 2004, mdtvp3j,   rad_sf2,  0, megadriv_radica_6button_ntsc, radica_6button,         megadriv_radica_state, init_megadriv, "Sega Toys",                         "Mega Drive Play TV 3 (Japan)", 0) // This one does contain the Japanese ROM for SF2 (but the World release of GnG) so SF2 runs in Japanese, but GnG runs in English

// still branded as Arcade Legends even if none of these were ever arcade games, European exclusive
CONS( 2004, rad_ssoc,  0,        0, megadriv_radica_3button_pal,  radica_3button,         megadriv_radica_state, init_megadrie, "Radica / Sensible Software / Sega", "Sensible Soccer plus [Cannon Fodder, Mega lo Mania] (Radica, Arcade Legends) (UK)", 0)
// is there a Europe version with Radica Games boot screen and Mega Drive text?

// not region locked, no Radica logos, uncertain if other regions would differ
CONS( 2004, rad_orun,  0,        0, megadriv_radica_3button_pal,  radica_3button_1player, megadriv_radica_state, init_megadrie, "Radica / Sega",                     "Out Run 2019 (Radica Plug & Play, UK)", 0)

// this has been verified as identical to the 6-in-1 cartridge that came with the Menacer gun for the MD
CONS( 2004, rad_mncr,  0,        0, megadriv_radica_3button_ntsc, radica_3button_1player, megadriv_radica_state, init_megadriv, "Radica / Sega",                     "Menacer (Radica Plug & Play)", MACHINE_NOT_WORKING )

// 仮面ライダー龍騎 サバイバルファイト
CONS( 2002, banmrid,    0,        0, megadriv_radica_3button_ntsc, radica_3button_1player, megadriv_radica_state, init_megadriv, "Bandai",                     "Kamen Rider Ryuki: Survival Fight (Japan)", MACHINE_NOT_WORKING )



megadriv_unksoc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

/*
    Are these (dgunl3227, ra145) actually emulation based? there is a block of 0x40000 bytes at the start of the ROM that doesn't
    appear to be used, very similar in both units.  Banking also seems entirely illogical unless something else is managing it.
    The menu code in both seems to have the same origin, containing a bunch of unused pirate versions of MD games.
    The version of SF2 in the 'ra145' unit is the same as the one in the MSI unit, and expects region to report US even
    when some of the units run at PAL speed?
    It is also confirmed from real hardware videos that these units do not have the usual sprite limits (so masking effect on Sonic title screen fails)
*/

#include "emu.h"
#include "megadriv.h"

namespace {

class megadriv_dgunl_state : public md_ctrl_state
{
public:
	megadriv_dgunl_state(const machine_config& mconfig, device_type type, const char* tag) :
		md_ctrl_state(mconfig, type, tag),
		m_bank(0),
		m_romsize(0x400000),
		m_rom(*this, "maincpu")
	{ }

	void megadriv_dgunl_ntsc(machine_config &config) ATTR_COLD;

	void init_dgunl3227() ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual uint16_t read(offs_t offset);

	uint16_t m_a1630a = 0;
	uint32_t m_bank;
	uint32_t m_romsize;

private:
	uint16_t read_a16300(offs_t offset, uint16_t mem_mask);
	uint16_t read_a16302(offs_t offset, uint16_t mem_mask);
	virtual void write_a1630a(offs_t offset, uint16_t data, uint16_t mem_mask);

	void megadriv_unksoc_map(address_map &map) ATTR_COLD;

	required_region_ptr<uint16_t> m_rom;
};


class megadriv_ra145_state : public megadriv_dgunl_state
{
public:
	megadriv_ra145_state(const machine_config& mconfig, device_type type, const char* tag) :
		megadriv_dgunl_state(mconfig, type, tag)
	{ }

	void megadriv_ra145_ntsc(machine_config &config);

	void init_ra145() ATTR_COLD;

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	virtual void write_a1630a(offs_t offset, uint16_t data, uint16_t mem_mask) override;
};


uint16_t megadriv_dgunl_state::read_a16300(offs_t offset, uint16_t mem_mask)
{
	return 0x5a5a;
}

uint16_t megadriv_dgunl_state::read_a16302(offs_t offset, uint16_t mem_mask)
{
	return m_a1630a;
}

void megadriv_dgunl_state::write_a1630a(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	m_a1630a = data;
	m_bank = (data & 0x07) * 8;
}


void megadriv_ra145_state::write_a1630a(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	// the banking must know how big each game is to get the base location, as it only writes a game number?!
	// it must also know to skip the unused copy of Golden Axe
	// there must be a table somewhere, or, if this is actually an emulation based system, it must be scanning
	// for headers somehow?

	m_a1630a = data;

	switch (data & 0xff)
	{


	//                  0x0000000           = Block of unknown data/code
	//                  0x0040000           = 145 in 1 menu
	//                  (includes unused Columns, Fatal Labyrinth, Blockout, Flicky, Shove It, Space Invaders 90 as part of menu data)
	case 0x00: m_bank = 0x02c0000; break;// = Fantasia                               00
	case 0x01: m_bank = 0x0340000; break;// = Fire Shark                             01
	case 0x02: m_bank = 0x03c0000; break;// = James Bond 007 - The Duel              02  (BAD ROM)
	case 0x03: m_bank = 0x0440000; break;// = Sunset Riders                          03
	case 0x04: m_bank = 0x04c0000; break;// = Robocop 3                              04
	case 0x05: m_bank = 0x0540000; break;// = Hokuto no Ken                          05  (BAD ROM)
	case 0x06: m_bank = 0x05c0000; break;// = Alien 3                                06
	case 0x07: m_bank = 0x0640000; break;// = Batman                                 07  (BAD ROM)
	case 0x08: m_bank = 0x06c0000; break;// = Cat                                    08
	case 0x09: m_bank = 0x0740000; break;// = DJ Boy                                 09
	case 0x0a: m_bank = 0x07c0000; break;// = Rambo 3                                0a  (BAD ROM)
	case 0x0b: m_bank = 0x0840000; break;// = Wardner                                0b
	case 0x0c: m_bank = 0x08c0000; break;// = Paperboy                               0c
	case 0x0d: m_bank = 0x0940000; break;// = Streets of Rage                        0d
	case 0x0e: m_bank = 0x09c0000; break;// = Tiny Toon Adventures                   0e
	case 0x0f: m_bank = 0x0a40000; break;// = Super Battleship                       0f
	case 0x10: m_bank = 0x0ac0000; break;// = Burning Force                          10
	case 0x11: m_bank = 0x0b40000; break;// = Cadash                                 11
	case 0x12: m_bank = 0x0bc0000; break;// = Caesars Palace                         12
	case 0x13: m_bank = 0x0c40000; break;// = chase HQ 2                             13
	case 0x14: m_bank = 0x0cc0000; break;// = Wonderboy 3                            14
	case 0x15: m_bank = 0x0d40000; break;// = Fighting Master                        15
	case 0x16: m_bank = 0x0dc0000; break;// = The Flintstones                        16
	case 0x17: m_bank = 0x0e40000; break;// = Ariel the Little Mermaid               17
	case 0x18: m_bank = 0x0ec0000; break;// = Hellfire                               18
	case 0x19: m_bank = 0x0f40000; break;// = Arrow Flash                            19
	case 0x1a: m_bank = 0x0fc0000; break;// = Shove It                               1a
	case 0x1b: m_bank = 0x1040000; break;// = Donkey Kong 99                         1b
	case 0x1c: m_bank = 0x1240000; break;// = Turtles Tournament                     1c
	case 0x1d: m_bank = 0x1440000; break;// = Thunder Force 2                        1d  (This is meant to be Revenge of Shinobi according to the menu, but incorrect game was in the ROM)
	case 0x1e: m_bank = 0x14c0000; break;// = Wings of Wor                           1e
	case 0x1f: m_bank = 0x1540000; break;// = Wrestle War                            1f
	case 0x20: m_bank = 0x15c0000; break;// = Afterburner 2                          20
	case 0x21: m_bank = 0x1640000; break;// = Altered Beast                          21
	case 0x22: m_bank = 0x16c0000; break;// = Captain Planet                         22
	case 0x23: m_bank = 0x1740000; break;// = Bimimi Run                             23
	case 0x24: m_bank = 0x17c0000; break;// = Osomatsu                               24
	case 0x25: m_bank = 0x1840000; break;// = Castle of Illusion                     25
	case 0x26: m_bank = 0x18c0000; break;// = Crackdown                              26
	case 0x27: m_bank = 0x1940000; break;// = Crossfire                              27
	case 0x28: m_bank = 0x19c0000; break;// = Curse                                  28
	case 0x29: m_bank = 0x1a40000; break;// = Dangerous Seed                         29
	case 0x2a: m_bank = 0x1ac0000; break;// = Dark Castle                            2a
	case 0x2b: m_bank = 0x1b40000; break;// = Darwin                                 2b
	case 0x2c: m_bank = 0x1bc0000; break;// = Thunder Force 2 (duplicate?)           2c
	case 0x2d: m_bank = 0x1c40000; break;// = Dynamite Duke                          2d
	case 0x2e: m_bank = 0x1cc0000; break;// = EA Hockey                              2e
	case 0x2f: m_bank = 0x1d40000; break;// = Elemenal Master                        2f
	case 0x30: m_bank = 0x1dc0000; break;// = Super Thunder Blade                    30
	case 0x31: m_bank = 0x1e40000; break;// = Target Earth                           31
	case 0x32: m_bank = 0x1ec0000; break;// = Rastan Saga 2                          32
	case 0x33: m_bank = 0x1f40000; break;// = Ghostbusters                           33
	case 0x34: m_bank = 0x1fc0000; break;// = Mahjong cop                            34
	case 0x35: m_bank = 0x2040000; break;// = High School Soccer                     35
	case 0x36: m_bank = 0x20c0000; break;// = Insector X                             36
	case 0x37: m_bank = 0x2140000; break;// = Pheilos                                37
	case 0x38: m_bank = 0x21c0000; break;// = Runark                                 38
	case 0x39: m_bank = 0x2240000; break;// = Saint Sword                            39
	case 0x3a: m_bank = 0x22c0000; break;// = Shadow Dancer                          3a
	case 0x3b: m_bank = 0x2340000; break;// = Shiten Myooh                           3b
	case 0x3c: m_bank = 0x23c0000; break;// = Wani Wani World                        3c
	case 0x3d: m_bank = 0x2440000; break;// = Street Mmart                           3d
	case 0x3e: m_bank = 0x24c0000; break;// = Toki                                   3e
	case 0x3f: m_bank = 0x2540000; break;// = Trouble Shooter                        3f
	case 0x40: m_bank = 0x25c0000; break;// = Truxton                                40
	case 0x41: m_bank = 0x2640000; break;// = James Pond 2                           41
	case 0x42: m_bank = 0x26c0000; break;// = Twin Hawk                              42
	case 0x43: m_bank = 0x2740000; break;// = Syd of Valis                           43
	case 0x44: m_bank = 0x27c0000; break;// = Zoom                                   44
	case 0x45: m_bank = 0x2840000; break;// = Streets of Rage 2                      45
	case 0x46: m_bank = 0x2a40000; break;// = Sonic & Knuckles                       46
	case 0x47: m_bank = 0x2c40000; break;// = Comix Zone                             47
	case 0x48: m_bank = 0x2e40000; break;// = Rolling Thunder 2                      48
	case 0x49: m_bank = 0x2f40000; break;// = Bubble & Squeek                        49
	case 0x4a: m_bank = 0x2fc0000; break;// = Alex Kidd                              4a
	case 0x4b: m_bank = 0x3040000; break;// = Super Mario 2                          4b
	case 0x4c: m_bank = 0x3240000; break;// = NBA All Star                           4c
	case 0x4d: m_bank = 0x3340000; break;// = Bio Hazard Battle                      4d
	case 0x4e: m_bank = 0x3440000; break;// = Prince of Persia                       4e
	case 0x4f: m_bank = 0x3540000; break;// = Champions World Soccer                 4f
	case 0x50: m_bank = 0x3640000; break;// = Lotus 2                                50
	case 0x51: m_bank = 0x3740000; break;// = Grandia                                51
	case 0x52: m_bank = 0x37c0000; break;// = World Cup Soccer                       52
	case 0x53: m_bank = 0x3840000; break;// = Ultimate Mortal Kombat 3               53
	case 0x54: m_bank = 0x3c40000; break;// = Street Fighter II (hacked MSI version) 54
	case 0x55: m_bank = 0x3f40000; break;// = Verytex                                55
	case 0x56: m_bank = 0x3fc0000; break;// = Space Invaders 90                      56

	//This 32MBytes of the ROM has its own unused '666666 in 1 menu' and only references games
	//within this block; it was probably released as a standalone using this 32Mbytes of ROM
	//                  0x4000000 = Block of unknown data/code
	//                  0x4040000 = 666666 in 1 menu
	//                  (includes unused Columns, Fatal Labyrinth, Blockout, Flicky, Shove It, Space Invaders 90 as part of menu data)
	//                  0x42c0000 - Golden Axe                             -- (not used by 145-in-1 menu, no assigned number either?!)
	case 0x57: m_bank = 0x4340000; break;// - Mega Bomberman                         57
	case 0x58: m_bank = 0x4440000; break;// - Dragon The Bruce Lee Story             58
	case 0x59: m_bank = 0x4640000; break;// - Twinkle Tale                           59
	case 0x5a: m_bank = 0x4740000; break;// - Jewel Master                           5a
	case 0x5b: m_bank = 0x47c0000; break;// - Pacmania                               5b
	case 0x5c: m_bank = 0x4840000; break;// - X-Men 2                                5c
	case 0x5d: m_bank = 0x4a40000; break;// - Top Gear 2                             5d
	case 0x5e: m_bank = 0x4b40000; break;// - Brian Lara Cricket                     5e
	case 0x5f: m_bank = 0x4c40000; break;// - Garfield                               5f
	case 0x60: m_bank = 0x4e40000; break;// - Raiden Trad                            60
	case 0x61: m_bank = 0x4f40000; break;// - James Pond                             61
	case 0x62: m_bank = 0x4fc0000; break;// - Mega Panel                             62
	case 0x63: m_bank = 0x5040000; break;// - James Pond 3                           63
	case 0x64: m_bank = 0x5240000; break;// - Contra Hardcorps                       64
	case 0x65: m_bank = 0x5440000; break;// - International Superstar Soccer Deluxe  65
	case 0x66: m_bank = 0x5640000; break;// - Double Dragon 3 (with Trainer)         66
	case 0x67: m_bank = 0x5740000; break;// - Micro Machines                         67
	case 0x68: m_bank = 0x57c0000; break;// - Hard Drivin'                           68
	case 0x69: m_bank = 0x5840000; break;// - Dragon Ball Z                          69
	case 0x6a: m_bank = 0x5a40000; break;// - F1 World Championship Edition          6a
	case 0x6b: m_bank = 0x5c40000; break;// - Megaman / Rockman                      -- (6b, but unused? probably due to save feature?)
	case 0x6c: m_bank = 0x5e40000; break;// - Operation Vapor Trail                  6c
	case 0x6d: m_bank = 0x5f40000; break;// - Roadblasters                           6d
	case 0x6e: m_bank = 0x5fc0000; break;// - Super Volleyball                       6e

	//The final 32MBytes of the ROM has its own unused '888888 in 1 menu' and only references games
	//within this block; it was probably released as a standalone using this 32Mbytes of ROM
	//                  0x6000000 = Block of unknown data/code
	//                  0x6040000 = 888888 in 1 menu
	//                  (includes unused Columns, Fatal Labyrinth, Blockout, Flicky, Shove It, Space Invaders 90 as part of menu data)
	case 0x6f: m_bank = 0x62c0000; break;// - Last Battle                            6f
	case 0x70: m_bank = 0x6340000; break;// - Shinobi III                            70
	case 0x71: m_bank = 0x6440000; break;// - Mickey Mania                           71
	case 0x72: m_bank = 0x6640000; break;// - Snow Bros                              72
	case 0x73: m_bank = 0x6740000; break;// - Space Harrier II                       73
	case 0x74: m_bank = 0x67c0000; break;// - Volfied                                74
	case 0x75: m_bank = 0x6840000; break;// - Goofy's Hysterical History Tour        75
	case 0x76: m_bank = 0x6940000; break;// - Mighty Max                             76
	case 0x77: m_bank = 0x6a40000; break;// - Mr Nutz                                77
	case 0x78: m_bank = 0x6b40000; break;// - Sonic 2                                78
	case 0x79: m_bank = 0x6c40000; break;// - Ecco Jr.                               79
	case 0x7a: m_bank = 0x6d40000; break;// - Desert Demolition                      7a
	case 0x7b: m_bank = 0x6e40000; break;// - Midnight Resistance                    7b
	case 0x7c: m_bank = 0x6f40000; break;// - Spiderman vs The Kingpin               7c
	case 0x7d: m_bank = 0x6fc0000; break;// - Trampoline Terror                      7d
	case 0x7e: m_bank = 0x7040000; break;// - MUSHA                                  7e
	case 0x7f: m_bank = 0x70c0000; break;// - Sonic                                  7f
	case 0x80: m_bank = 0x7140000; break;// - Thunder Force III                      80
	case 0x81: m_bank = 0x71c0000; break;// - Golden Axe (again)                     81
	//                  0x7240000; break;// - Controller Test Menu                   -- (unused)
	//                  0x7340000; break;// - Controller Test Menu (again)           -- (unused)
	case 0x82: m_bank = 0x7440000; break;// - Wacky Worlds                           82 (writes 93, incorrectly swapped in menu with Wacky Worlds)
	case 0x83: m_bank = 0x7540000; break;// - Bonkers                                83
	case 0x84: m_bank = 0x7640000; break;// - Alisia Dragoon                         84
	case 0x85: m_bank = 0x7740000; break;// - Super Hang On                          85
	case 0x86: m_bank = 0x77c0000; break;// - Dragon's Eye Shanghai 3                86
	case 0x87: m_bank = 0x7840000; break;// - Jimmy White's Whirlwind Snooker        87
	case 0x88: m_bank = 0x78c0000; break;// - Home Alone 2                           88
	case 0x89: m_bank = 0x7940000; break;// - Xenon 2 (with trainer)                 89
	case 0x8a: m_bank = 0x79c0000; break;// - Hit the Ice                            8a
	case 0x8b: m_bank = 0x7a40000; break;// - Tale Spin                              8b
	case 0x8c: m_bank = 0x7ac0000; break;// - Puyo Puyo                              8c
	case 0x8d: m_bank = 0x7b40000; break;// - Rampart (with trainer)                 8d
	case 0x8e: m_bank = 0x7bc0000; break;// - Crue Ball                              8e
	case 0x8f: m_bank = 0x7c40000; break;// - Marble Madness                         8f
	case 0x90: m_bank = 0x7cc0000; break;// - Patlabor                               90
	case 0x91: m_bank = 0x7d40000; break;// - The Aquatic Games                      91
	case 0x92: m_bank = 0x7dc0000; break;// - King Salmon                            92
	case 0x93: m_bank = 0x7e40000; break;// - Fun n Games                            93 (writes 82, incorrectly swapped in menu with Fun n Games)
	case 0x94: m_bank = 0x7f40000; break;// - Gynoug                                 94
	case 0x95: m_bank = 0x7fc0000; break;// - Ms. Pac-Man                            95
	default:   m_bank = 0x0040000; break;
	}

	m_bank = m_bank / 0x10000;
}

void megadriv_dgunl_state::megadriv_unksoc_map(address_map &map)
{
	megadriv_68k_base_map(map);

	map(0x000000, 0x3fffff).r(FUNC(megadriv_dgunl_state::read)); // Cartridge Program ROM

	map(0xa16300, 0xa16301).r(FUNC(megadriv_dgunl_state::read_a16300));
	map(0xa16302, 0xa16303).r(FUNC(megadriv_dgunl_state::read_a16302));

	map(0xa1630a, 0xa1630b).w(FUNC(megadriv_dgunl_state::write_a1630a));
}

uint16_t megadriv_dgunl_state::read(offs_t offset)
{
	return m_rom[(((m_bank * 0x10000) + (offset << 1)) & (m_romsize - 1))/2];
}


static INPUT_PORTS_START( unksoc_6button )
	PORT_INCLUDE( md_common )

	PORT_MODIFY("PAD1") // Extra buttons for Joypad 1 (6 button + start + mode)
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(1) PORT_NAME("%p Z")
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(1) PORT_NAME("%p Y")
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("%p X")
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_SELECT )  PORT_PLAYER(1) PORT_NAME("%p Mode")

	PORT_MODIFY("PAD2") // Extra buttons for Joypad 2 (6 button + start + mode)
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(2) PORT_NAME("%p Z")
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(2) PORT_NAME("%p Y")
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("%p X")
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_SELECT )  PORT_PLAYER(2) PORT_NAME("%p Mode")
INPUT_PORTS_END

static INPUT_PORTS_START( ra145_6button )
	PORT_INCLUDE( unksoc_6button )

	PORT_MODIFY("PAD2") // no 2nd pad
	PORT_BIT( 0x0fff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("RESET") // RESET button on controller to the left of START
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1) PORT_NAME("Reset")
INPUT_PORTS_END

static INPUT_PORTS_START( dgunl_1player )
	PORT_INCLUDE( md_common )

	PORT_MODIFY("PAD1")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_UNUSED  )                                  PORT_CONDITION("DEBUG", 0x01, EQUALS, 0x00)
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("%p C") PORT_CONDITION("DEBUG", 0x01, EQUALS, 0x01)

	PORT_MODIFY("PAD2") // no 2nd pad
	PORT_BIT( 0x0fff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("RESET") // RESET button to the left of START
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1) PORT_NAME("Reset")

	// the unit only has 2 buttons, A and B, strings are changed to remove references to C, even if behavior in Pac-Mania still exists and differs between them
	// however, Pac-Man still has a test mode which requires holding A+C on startup
	PORT_START("DEBUG")
	PORT_CONFNAME( 0x01, 0x00, "Enable Button C" )
	PORT_CONFSETTING(    0x00, DEF_STR( No ) )
	PORT_CONFSETTING(    0x01, DEF_STR( Yes ) )
INPUT_PORTS_END


void megadriv_dgunl_state::machine_start()
{
	md_ctrl_state::machine_start();
	m_vdp->stop_timers();
	m_a1630a = 0;

	save_item(NAME(m_bank));
	save_item(NAME(m_a1630a));
}

void megadriv_dgunl_state::machine_reset()
{
	m_bank = 0;
	md_ctrl_state::machine_reset();
}

void megadriv_ra145_state::machine_reset()
{
	m_bank = 4;
	md_ctrl_state::machine_reset();
}

void megadriv_dgunl_state::megadriv_dgunl_ntsc(machine_config &config)
{
	md_ntsc(config);

	ctrl1_3button(config);
	ctrl2_3button(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &megadriv_dgunl_state::megadriv_unksoc_map);
}

void megadriv_ra145_state::megadriv_ra145_ntsc(machine_config &config)
{
	megadriv_dgunl_ntsc(config);

	ctrl1_6button(config);
	ctrl2_6button(config);
}


ROM_START( dgunl3227 )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	// populated in init function

	ROM_REGION( 0x400000, "rom", 0 )
	ROM_LOAD16_WORD_SWAP( "pacmantc58fvm5t2a.bin", 0x000000, 0x400000, CRC(b09fa599) SHA1(3cc50bee7ef91608848fb34185a0723d2b82b46f) )
ROM_END

ROM_START( dgunl3227a )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	// populated in init function

	ROM_REGION( 0x400000, "rom", 0 )
	ROM_LOAD16_WORD_SWAP( "myarcadepacman_s99jl032hbt1_9991227e_as_s29jl032h55tai01.bin", 0x000000, 0x400000, CRC(ecead966) SHA1(971e8da6eb720f670f4148c7e07922e4f24eb609) )
ROM_END

ROM_START( ra145 )
	/*
	Data for the following games is corrupt (ranges approximate, based on areas of inconsistent readout)

	3c0000 - 43ffff - James Bond 007 - The Duel   (400171 - 41c600 is corrupt)
	4c0000 - 53ffff - Robocop 3                   (507bd4 - 50ad43 is corrupt)
	540000 - 5bffff - Hokuto no Ken               (540006 - 55fff2 is corrupt)
	640000 - 6bffff - Batman                      (640042 - 65ffaf is corrupt)
	7c0000 - 7fffff - Rambo 3                     (7e031a - 7fffb9 is corrupt)

	Unfortunately as many of the games in this unit have been hacked, or are using pirate versions of the games
	from the mid 90s (in a few cases, complete with trainers) that seem to have dropped out of circulation it
	is not possible to repair the data in dump from the damaged unit.

	The unit also includes a duplicate copy of Thunder Force II instead of Revenge of Shinobi, this however
	is not a dump issue, nor is Wacky Worlds being swapped with Fun and Games in the menu
	*/

	ROM_REGION( 0x8000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "ra145.bin", 0x000000, 0x8000000, BAD_DUMP CRC(30583950) SHA1(855eae232e3830a505f9bc1a26edb3a7d15ce4d1) )
ROM_END

void megadriv_dgunl_state::init_dgunl3227()
{
	uint8_t* rom = memregion("rom")->base();
	uint8_t* dst = memregion("maincpu")->base();
	size_t len = memregion("rom")->bytes();

	std::vector<u8> buffer(len);

	for (int i = 0; i < len; i++)
		buffer[i] = rom[i ^ 3];

	std::copy(buffer.begin(), buffer.end(), &rom[0]);

	int baseaddr, size, dstaddr;
	//baseaddr = 0x200000; size = 0x40000; // unknown data (unused menu data maybe?)
	//baseaddr = 0x240000; size = 0x20000; // 'sample' program with UWOL header later too (lower part of menu program)
	//baseaddr = 0x260000; size = 0x20000; // pirate version of Columns with Sega text removed
	//baseaddr = 0x280000; size = 0x20000; // Fatal Labyrinth
	//baseaddr = 0x2a0000; size = 0x20000; // pirate version of Block Out with EA logo and text removed
	//baseaddr = 0x2c0000; size = 0x20000; // Flicky
	//baseaddr = 0x2e0000; size = 0x20000; // Shove It
	//baseaddr = 0x300000; size = 0x40000; // pirate version of Space Invaders 90 with Taito logos and copyright removed (also upper part of menu program - has extra header + bits of code for '202 in 1' menu which has been hacked to run the 3-in-1 menu)

	// the following 3 games are available to select from the menu on this system
	//baseaddr = 0x340000; size = 0x40000; // Pac-Attack / Pac-Panic (used by this unit)
	//baseaddr = 0x380000; size = 0x40000; // Pac-Mania (used by this unit)
	//baseaddr = 0x3c0000; size = 0x40000; // Pac-Man (used by this unit) (2nd copy of header about halfway through?)

	// copy 1st part of menu code
	baseaddr = 0x240000;
	size     = 0x020000;
	dstaddr  = 0x000000;
	for (int i = 0; i < size; i++)
	{
		dst[i + dstaddr] = rom[baseaddr + i];
	}

	// copy 2nd part of menu code
	baseaddr = 0x300000;
	size =     0x040000;
	dstaddr =  0x0c0000;
	for (int i = 0; i < size; i++)
	{
		dst[i + dstaddr] = rom[baseaddr + i];
	}

	// copy pac-panic to first bank
	baseaddr = 0x340000;
	size =     0x040000;
	dstaddr =  0x100000;
	for (int i = 0; i < size; i++)
	{
		dst[i + dstaddr] = rom[baseaddr + i];
	}

	// copy pac-mania to 2nd bank
	baseaddr = 0x380000;
	size =     0x040000;
	dstaddr =  0x180000;
	for (int i = 0; i < size; i++)
	{
		dst[i + dstaddr] = rom[baseaddr + i];
	}

	// copy pac-man to 3nd bank
	baseaddr = 0x3c0000;
	size =     0x040000;
	dstaddr =  0x200000;
	for (int i = 0; i < size; i++)
	{
		dst[i + dstaddr] = rom[baseaddr + i];
	}

	// other data isn't copied because it's never referenced, therefore we don't know how it gets accessed

	init_megadriv();
}

void megadriv_ra145_state::init_ra145()
{
	m_romsize = memregion("maincpu")->bytes();
	init_megadriv();
}

} // anonymous namespace

// the parent set has updated software explaining how to insert coins in Pac-Man as well as an updated copyright string
CONS( 2018, dgunl3227,  0,        0, megadriv_dgunl_ntsc, dgunl_1player,         megadriv_dgunl_state, init_dgunl3227,    "dreamGEAR",            "My Arcade Pac-Man Pocket Player (DGUNL-3227)", 0 )
CONS( 2018, dgunl3227a, dgunl3227,0, megadriv_dgunl_ntsc, dgunl_1player,         megadriv_dgunl_state, init_dgunl3227,    "dreamGEAR",            "My Arcade Pac-Man Pocket Player (DGUNL-3227, older)", 0 )

CONS( 2018, ra145,      0,        0, megadriv_ra145_ntsc, ra145_6button,         megadriv_ra145_state, init_ra145,        "<unknown>",            "Retro Arcade 16 Bits Classic Edition Mini TV Game Console - 145 Classic Games - TV Arcade Plug and Play (Mega Drive bootlegs)", MACHINE_NOT_WORKING )



megapc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Barry Rodewald
/**************************************************************************************************

Amstrad MegaPC

TODO:
- MD ISA8 portion (including sharing of OPN as adlib compatible, needs PAL mods for YM7101);
- chipset not extensively tested (assume incomplete as per pc/teradrive.cpp);
- megapcpl: floppy boot loading fails even if CMOS is setup properly;
- front panel slide (MD on left, PC on right. Determines what is driving the monitor);

**************************************************************************************************/

#include "emu.h"

#include "bus/isa/isa.h"
#include "bus/isa/isa_cards.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "cpu/i386/i386.h"
#include "machine/ram.h"
#include "machine/wd7600.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "softlist_dev.h"
#include "speaker.h"

namespace {

class megapc_state : public driver_device
{
public:
	megapc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ram(*this, RAM_TAG),
		m_wd7600(*this, "wd7600"),
		m_isabus(*this, "isabus"),
		m_speaker(*this, "speaker")
	{ }

	void megapcpl(machine_config &config);
	void megapc(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<wd7600_device> m_wd7600;
	required_device<isa16_device> m_isabus;
	required_device<speaker_sound_device> m_speaker;

	uint16_t wd7600_ior(offs_t offset);
	void wd7600_iow(offs_t offset, uint16_t data);
	void wd7600_hold(int state);
	void wd7600_tc(offs_t offset, uint8_t data) { m_isabus->eop_w(offset, data); }
	void wd7600_spkr(int state) { m_speaker->level_w(state); }
	void megapc_io(address_map &map) ATTR_COLD;
	void megapc_map(address_map &map) ATTR_COLD;
};

uint16_t megapc_state::wd7600_ior(offs_t offset)
{
	if (offset < 4)
		return m_isabus->dack_r(offset);
	else
		return m_isabus->dack16_r(offset);
}

void megapc_state::wd7600_iow(offs_t offset, uint16_t data)
{
	if (offset < 4)
		m_isabus->dack_w(offset, data);
	else
		m_isabus->dack16_w(offset, data);
}

void megapc_state::wd7600_hold(int state)
{
	// halt cpu
	m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);

	// and acknowledge hold
	m_wd7600->hlda_w(state);
}

void megapc_state::megapc_map(address_map &map)
{
	map.unmap_value_high();
}

void megapc_state::megapc_io(address_map &map)
{
	map.unmap_value_high();
}

void megapc_state::megapc(machine_config &config)
{
	i386sx_device &maincpu(I386SX(config, m_maincpu, 50_MHz_XTAL / 2));
	maincpu.set_addrmap(AS_PROGRAM, &megapc_state::megapc_map);
	maincpu.set_addrmap(AS_IO, &megapc_state::megapc_io);
	maincpu.set_irq_acknowledge_callback("wd7600", FUNC(wd7600_device::intack_cb));

	WD7600(config, m_wd7600, 50_MHz_XTAL / 2);
	m_wd7600->set_cputag(m_maincpu);
	m_wd7600->set_ramtag(m_ram);
	m_wd7600->set_biostag("bios");
	m_wd7600->set_keybctag("keybc");
	m_wd7600->hold_callback().set(FUNC(megapc_state::wd7600_hold));
	m_wd7600->nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_wd7600->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_wd7600->cpureset_callback().set_inputline(m_maincpu, INPUT_LINE_RESET);
	m_wd7600->a20m_callback().set_inputline(m_maincpu, INPUT_LINE_A20);
	// isa dma
	m_wd7600->ior_callback().set(FUNC(megapc_state::wd7600_ior));
	m_wd7600->iow_callback().set(FUNC(megapc_state::wd7600_iow));
	m_wd7600->tc_callback().set(FUNC(megapc_state::wd7600_tc));
	// speaker
	m_wd7600->spkr_callback().set(FUNC(megapc_state::wd7600_spkr));

	// on board devices
	ISA16(config, m_isabus, 0);
	m_isabus->set_memspace(m_maincpu, AS_PROGRAM);
	m_isabus->set_iospace(m_maincpu, AS_IO);
	m_isabus->iochck_callback().set(m_wd7600, FUNC(wd7600_device::iochck_w));
	m_isabus->irq2_callback().set(m_wd7600, FUNC(wd7600_device::irq09_w));
	m_isabus->irq3_callback().set(m_wd7600, FUNC(wd7600_device::irq03_w));
	m_isabus->irq4_callback().set(m_wd7600, FUNC(wd7600_device::irq04_w));
	m_isabus->irq5_callback().set(m_wd7600, FUNC(wd7600_device::irq05_w));
	m_isabus->irq6_callback().set(m_wd7600, FUNC(wd7600_device::irq06_w));
	m_isabus->irq7_callback().set(m_wd7600, FUNC(wd7600_device::irq07_w));
	m_isabus->irq10_callback().set(m_wd7600, FUNC(wd7600_device::irq10_w));
	m_isabus->irq11_callback().set(m_wd7600, FUNC(wd7600_device::irq11_w));
	m_isabus->irq12_callback().set(m_wd7600, FUNC(wd7600_device::irq12_w));
	m_isabus->irq14_callback().set(m_wd7600, FUNC(wd7600_device::irq14_w));
	m_isabus->irq15_callback().set(m_wd7600, FUNC(wd7600_device::irq15_w));
	m_isabus->drq0_callback().set(m_wd7600, FUNC(wd7600_device::dreq0_w));
	m_isabus->drq1_callback().set(m_wd7600, FUNC(wd7600_device::dreq1_w));
	m_isabus->drq2_callback().set(m_wd7600, FUNC(wd7600_device::dreq2_w));
	m_isabus->drq3_callback().set(m_wd7600, FUNC(wd7600_device::dreq3_w));
	m_isabus->drq5_callback().set(m_wd7600, FUNC(wd7600_device::dreq5_w));
	m_isabus->drq6_callback().set(m_wd7600, FUNC(wd7600_device::dreq6_w));
	m_isabus->drq7_callback().set(m_wd7600, FUNC(wd7600_device::dreq7_w));

	// FIXME: determine ISA bus clock
	ISA16_SLOT(config, "board1", 0, "isabus", pc_isa16_cards, "fdc_smc", true);
	ISA16_SLOT(config, "board2", 0, "isabus", pc_isa16_cards, "comat", true);
	ISA16_SLOT(config, "board3", 0, "isabus", pc_isa16_cards, "ide", true);
	ISA16_SLOT(config, "board4", 0, "isabus", pc_isa16_cards, "lpt", true);
	// WD90C11A-LR
	ISA16_SLOT(config, "board5", 0, "isabus", pc_isa16_cards, "wd90c11_lr", true);
	// TODO: motherboard ISA resource for MD portion (8-bit slot?)
	// (doesn't share anything with base except drawing power)
	// TODO: reuses MD sound chip as Adlib-compatible sound card

	ISA16_SLOT(config, "isa1", 0, "isabus", pc_isa16_cards, nullptr, false);

	ps2_keyboard_controller_device &keybc(PS2_KEYBOARD_CONTROLLER(config, "keybc", 12_MHz_XTAL));
	keybc.hot_res().set("wd7600", FUNC(wd7600_device::kbrst_w));
	keybc.gate_a20().set("wd7600", FUNC(wd7600_device::gatea20_w));
	keybc.kbd_irq().set("wd7600", FUNC(wd7600_device::irq01_w));
	keybc.kbd_clk().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	keybc.kbd_data().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));

	// NOTE: wants an IBM keyboard
	pc_kbdc_device &pc_kbdc(PC_KBDC(config, "kbd", pc_at_keyboards, STR_KBD_IBM_PC_AT_84));
	pc_kbdc.out_clock_cb().set("keybc", FUNC(ps2_keyboard_controller_device::kbd_clk_w));
	pc_kbdc.out_data_cb().set("keybc", FUNC(ps2_keyboard_controller_device::kbd_data_w));

	// TODO: mouse port

	/* internal ram */
	RAM(config, m_ram).set_default_size("4M").set_extra_options("1M,2M,8M,15M,16M");

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	// video hardware
	PALETTE(config, "palette").set_entries(256); // todo: really needed?

	/* software lists */
	SOFTWARE_LIST(config, "disk_list").set_original("megapc");
}

void megapc_state::megapcpl(machine_config &config)
{
	megapc(config);
	i486_device &maincpu(I486(config.replace(), m_maincpu, 66'000'000 / 2));
	maincpu.set_addrmap(AS_PROGRAM, &megapc_state::megapc_map);
	maincpu.set_addrmap(AS_IO, &megapc_state::megapc_io);
	maincpu.set_irq_acknowledge_callback("wd7600", FUNC(wd7600_device::intack_cb));
}

// Amstrad MegaPC
ROM_START( megapc )
	ROM_REGION(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "41651-bios lo.u18",  0x00000, 0x10000, CRC(1e9bd3b7) SHA1(14fd39ec12df7fae99ccdb0484ee097d93bf8d95))
	ROM_LOAD16_BYTE( "211253-bios hi.u19", 0x00001, 0x10000, CRC(6acb573f) SHA1(376d483db2bd1c775d46424e1176b24779591525))
ROM_END

// Amstrad MegaPC Plus
ROM_START( megapcpl )
	ROM_REGION(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "41652.u18",  0x00000, 0x10000, CRC(6f5b9a1c) SHA1(cae981a35a01234fcec99a96cb38075d7bf23474))
	ROM_LOAD16_BYTE( "486slc.u19", 0x00001, 0x10000, CRC(6fb7e3e9) SHA1(c439cb5a0d83176ceb2a3555e295dc1f84d85103))
ROM_END

} // anonymous namespace


COMP( 1993, megapc,    0,       0,       megapc,    0,     megapc_state, empty_init,  "Amstrad plc", "MegaPC", MACHINE_NOT_WORKING )
COMP( 199?, megapcpl,  megapc,  0,       megapcpl,  0,     megapc_state, empty_init,  "Amstrad plc", "MegaPC Plus", MACHINE_NOT_WORKING )
// TODO: megapcpla here



mekd1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:68bit
/***************************************************************************

Motorola Evaluation Kit 6800 D1 - MEK6800D1

The monitor is supplied in the MCM6830L7 ROM and named MIKBUG rev 9. MIKBUG is
a 512 byte monitor. The MIKBUG commands are:

M aaaa - memory examine and change at address 'aaaa'.
  <space>hh - modify content with hex value 'hh' and increase address. If the
              memory can not be changed then it prints '?' and exits the
              memory examine.
  <space> and any other key exits the memory examine. The documentation
          suggests using a carriage return.
  Any other key increases the address.

R - Register display: CC B A X PC SP. The registers can be modified at:
  0xa043  CC
  0xa044  B
  0xa045  A
  0xa046  X high
  0xa047  X low
  0xa048  PC high
  0xa049  PC low
  0xa008  SP high
  0xa009  SP low

L - Loads data, in S19 format, as generated by the assembler. If there is a
  checksum failure then it prints '?' and exits the load. An 'S9' record
  exits the load.  Note that the MIKBUG punch command 'P' does not write a
  'S9' record so it is necessary to manually enter a 'S9' sequence at the
  end of the load when loading a MIKBUG generated tape!

P - Print or Punch memory dump. The start address is loaded from 0xa002 to
  0xa003, and the end address is loaded from 0xa004 to 0xa005. The output is
  in S19 format and can be loaded with the 'L' command, but the end-of-file
  'S9' record is not emitted.

G - Go to user code. The registers are loaded from the addresses noted above
  0xa043 to 0xa049 and 0xa008 to 0xa009 for the stack pointer. Breakpoints
  can be manually set by inserting a SWI (0x3f) instruction, and when these
  are encountered control returns to MIKBUG and the registers are save and
  printed.

An IRQ handler can be defined at 0xa000 to 0xa001.
An NMI handler can be defined at 0xa006 to 0xa007.

MIKBUG was designed to work with the ASR33 Teletypewriter and drives the
'reader control' line high during a 'load' operation intended for a reader
relay control which was a common modification. There is no signal line driven
for a 'punch' operation. TODO might be able to at least use this 'reader
control' line to switch to input from a tape.

MIKBUG was designed to work with the Texas Instruments 733 ASR twin tape
cassette terminal with automatic device control and issues the following
control codes when loading and 'punching' a tape. TODO perhaps these could
be detected and serial I/O diverted to a cassette.
 Code 0x11 DC1 - Tape playback on
 Code 0x12 DC2 - Tape record on.
 Code 0x13 DC3 - Tape playback off.
 Code 0x14 DC4 - Tape record off.

The serial bit rate is controlled by a MC14536 timer and is variable. Common
rates are 110 baud and 300 baud, for the ASR33 and 733 ASR terminals
respectively, but is was practical to run at higher rates up to about 4800
baud.

Memory is expandable off-board with some modifications, to at least 8K. There
were versions of the resident editor and assembler for use with MIKBUG, that
required the expanded RAM.

The board hosts a MC6850 ACIA but this is not used by MIKBUG so it is a user
ACIA. The clock is supplied off board and the documentation offers a design
suggestion using the MC14411 and this is emulated here as documented.

TODO could add a reset button, and an NMI button, and an LED for the reader
relay line. Might want to default the terminal to echo characters.

References:
1. Assembly Instructions for the Motorola M6800 Design Evaluation Kit, 1975.
2. Engineering Note 100, MCM6830L7 MIKBUG/MINIBUG ROM.

****************************************************************************/

#include "emu.h"
#include "cpu/m6800/m6800.h"
#include "machine/6821pia.h"
#include "machine/6850acia.h"
#include "machine/mc14411.h"
#include "machine/input_merger.h"
#include "machine/timer.h"
#include "bus/rs232/rs232.h"
#include "machine/terminal.h"


namespace {

class mekd1_state : public driver_device
{
public:
	mekd1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pia0(*this, "pia0")
		, m_pia1(*this, "pia1")
		, m_acia(*this, "acia")
		, m_brg(*this, "brg")
		, m_rs232(*this, "rs232")
		, m_baud_rate(*this, "BAUD_RATE")
		, m_stop_bits(*this, "STOP_BITS")
		, m_acia_baud_rate(*this, "ACIA_BAUD_RATE")
	{ }

	void mekd1(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<pia6821_device> m_pia0;
	required_device<pia6821_device> m_pia1;
	required_device<acia6850_device> m_acia;
	required_device<mc14411_device> m_brg;
	required_device<rs232_port_device> m_rs232;
	required_ioport m_baud_rate;
	required_ioport m_stop_bits;
	required_ioport m_acia_baud_rate;

	uint8_t pia0_pa_r();
	uint8_t pia0_pb_r();
	void pia0_pa_w(uint8_t data);
	void pia0_pb_w(uint8_t data);
	void pia0_cb2_w(int state);

	// Clocks
	void write_f1_clock(int state);
	void write_f2_clock(int state);
	void write_f4_clock(int state);
	void write_f5_clock(int state);
	void write_f7_clock(int state);
	void write_f8_clock(int state);
	void write_f9_clock(int state);
	void write_f11_clock(int state);
	void write_f13_clock(int state);

	TIMER_CALLBACK_MEMBER(bit_rate);
	TIMER_CALLBACK_MEMBER(bit_rate_half);

	emu_timer *m_bit_rate_timer;
	emu_timer *m_bit_rate_half_timer;

	bool m_bit_rate_out;
	bool m_bit_rate_half_out;
	bool m_bit_rate_reset;
	bool m_bit_rate_select;
};

void mekd1_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x027f).mirror(0x7c00).ram();

	// The emulated addressing for these is not exact. The hardware uses
	// A3, A4, and A5 to select between these respectively. For example
	// the first PIA is selected when A3 is high, but it ignores A4 and
	// A5, etc. So it is possible that more than one are selected at once
	// which is not emulated here. Here it is assumed that A3, A4, and A5
	// are completely decoded and that only one device is selected at a
	// time.
	map(0x8004, 0x8007).mirror(0x5fe0).rw(m_pia0, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x8008, 0x800b).mirror(0x5fe0).rw(m_pia1, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x8010, 0x8011).mirror(0x5fe2).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write));

	map(0xa000, 0xa07f).mirror(0x0f80).ram();

	// Although the ROM is 1k, MIKBUG uses only the first 512 bytes and
	// when using MIKBUG the A9 ROM input is connected to 0V.
	map(0xe000, 0xe1ff).mirror(0x1e00).rom().region("mcm6830l7", 0);
}

static INPUT_PORTS_START( mekd1 )

	PORT_START("BAUD_RATE")
	PORT_CONFNAME(0x3fff, 416, "RS232 Baud Rate")
	PORT_CONFSETTING(9091, "110")
	PORT_CONFSETTING(3333, "300")
	PORT_CONFSETTING(1667, "600")
	PORT_CONFSETTING( 833, "1200")
	PORT_CONFSETTING( 416, "2400")
	PORT_CONFSETTING( 208, "4800")
	PORT_CONFSETTING( 139, "7200")
	PORT_CONFSETTING( 104, "9600")

	PORT_START("STOP_BITS")
	PORT_CONFNAME(0x01, 0, "Stop bits")
	PORT_CONFSETTING(0x00, "1")
	PORT_CONFSETTING(0x01, "2")

	PORT_START("ACIA_BAUD_RATE")
	PORT_CONFNAME(0xf, 1, "ACIA Baud Rate")
	PORT_CONFSETTING(13, "110")
	PORT_CONFSETTING(11, "150")
	PORT_CONFSETTING(9, "300")
	PORT_CONFSETTING(8, "600")
	PORT_CONFSETTING(7, "1200")
	PORT_CONFSETTING(5, "2400")
	PORT_CONFSETTING(4, "3600")
	PORT_CONFSETTING(2, "4800")
	PORT_CONFSETTING(1, "9600")

INPUT_PORTS_END



uint8_t mekd1_state::pia0_pa_r()
{
	return m_rs232->rxd_r() << 7;
}

uint8_t mekd1_state::pia0_pb_r()
{
	bool timer_out;
	uint8_t stop_bits = m_stop_bits->read();

	if (m_bit_rate_select)
		timer_out = m_bit_rate_out;
	else
		timer_out = m_bit_rate_half_out;

	return (timer_out << 7) | (stop_bits << 6);
}

void mekd1_state::pia0_pa_w(uint8_t data)
{
	m_rs232->write_txd(BIT(data, 0));
}

void mekd1_state::pia0_pb_w(uint8_t data)
{
	m_bit_rate_select = BIT(data, 2);

	if (BIT(data, 0) == 1)
	{
		// Reset
		m_bit_rate_timer->reset();
		m_bit_rate_half_timer->reset();
		m_bit_rate_out = 0;
		m_bit_rate_half_out = 0;
		m_bit_rate_reset = 1;
		return;
	}

	if (m_bit_rate_reset)
	{
		// Timer has just been taken out of reset.
		m_bit_rate_reset = 0;
		uint16_t delay = m_baud_rate->read();
		// An adjustment is subtracted from the delay for the CPU
		// instruction processing paths as the CPU polls the timer
		// output, and this would vary with the CPU clock. In
		// hardware, variable resisters needed to be tuned to achieve
		// the correct timing which was not hard at the lower baud
		// rates.
		m_bit_rate_timer->adjust(attotime::from_usec(delay - 31));
		m_bit_rate_half_timer->adjust(attotime::from_usec((delay / 2 ) - 31));
	}
}

void mekd1_state::pia0_cb2_w(int state)
{
	// This is a tape reader control line.
}

TIMER_CALLBACK_MEMBER(mekd1_state::bit_rate)
{
	m_bit_rate_out = 1;
}

TIMER_CALLBACK_MEMBER(mekd1_state::bit_rate_half)
{
	m_bit_rate_half_out = 1;
}

void mekd1_state::write_f1_clock(int state)
{
	if (m_acia_baud_rate->read() == 1)
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}

void mekd1_state::write_f2_clock(int state)
{
	if (m_acia_baud_rate->read() == 2)
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}

void mekd1_state::write_f4_clock(int state)
{
	if (m_acia_baud_rate->read() == 4)
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}

void mekd1_state::write_f5_clock(int state)
{
	if (m_acia_baud_rate->read() == 5)
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}

void mekd1_state::write_f7_clock(int state)
{
	if (m_acia_baud_rate->read() == 7)
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}

void mekd1_state::write_f8_clock(int state)
{
	if (m_acia_baud_rate->read() == 8)
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}

void mekd1_state::write_f9_clock(int state)
{
	if (m_acia_baud_rate->read() == 9)
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}

void mekd1_state::write_f11_clock(int state)
{
	if (m_acia_baud_rate->read() == 11)
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}

void mekd1_state::write_f13_clock(int state)
{
	if (m_acia_baud_rate->read() == 13)
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}


void mekd1_state::machine_reset()
{
	m_pia0->reset();
	m_pia1->reset();
	m_brg->rsa_w(CLEAR_LINE);
	m_brg->rsb_w(ASSERT_LINE);
	m_bit_rate_timer->reset();
	m_bit_rate_half_timer->reset();
	m_bit_rate_out = 0;
	m_bit_rate_half_out = 0;
	m_bit_rate_select = 0;
	m_bit_rate_reset = 1;
}

void mekd1_state::machine_start()
{
	// Allocate timers
	m_bit_rate_timer = timer_alloc(FUNC(mekd1_state::bit_rate), this);
	m_bit_rate_half_timer = timer_alloc(FUNC(mekd1_state::bit_rate_half), this);

	save_item(NAME(m_bit_rate_out));
	save_item(NAME(m_bit_rate_half_out));
	save_item(NAME(m_bit_rate_reset));
	save_item(NAME(m_bit_rate_select));
}

static DEVICE_INPUT_DEFAULTS_START(terminal)
	DEVICE_INPUT_DEFAULTS("RS232_RXBAUD", 0xff, RS232_BAUD_2400)
	DEVICE_INPUT_DEFAULTS("RS232_TXBAUD", 0xff, RS232_BAUD_2400)
	DEVICE_INPUT_DEFAULTS("RS232_DATABITS", 0xff, RS232_DATABITS_7)
	DEVICE_INPUT_DEFAULTS("RS232_PARITY", 0xff, RS232_PARITY_SPACE)
	DEVICE_INPUT_DEFAULTS("RS232_STOPBITS", 0xff, RS232_STOPBITS_1)
DEVICE_INPUT_DEFAULTS_END

void mekd1_state::mekd1(machine_config &config)
{
	// The clock rate was adjustable from 100KHz to 1MHz.
	M6800(config, m_maincpu, 2_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &mekd1_state::mem_map);

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, M6800_IRQ_LINE);

	// Terminal I/O.
	// The design uses a 6820 which is slightly different to the emulated 6821.
	// PA0 serial output.
	// PA7 serial input.
	// PB0 resets the rite rate timer when high.
	// PB2 selects the bit rate timer output: 0 half time, 1 full time.
	// PB6 jumper input: 0 for 1 stop bit, 1 for 2 stop bits.
	// PB7 input from the bit rate timer that goes high after the timer period elapses.
	// CB2 "reader control" output
	// IRQA and IRQB are NC.
	PIA6821(config, m_pia0);
	m_pia0->readpa_handler().set(FUNC(mekd1_state::pia0_pa_r));
	m_pia0->readpb_handler().set(FUNC(mekd1_state::pia0_pb_r));
	m_pia0->writepa_handler().set(FUNC(mekd1_state::pia0_pa_w));
	m_pia0->writepb_handler().set(FUNC(mekd1_state::pia0_pb_w));
	m_pia0->cb2_handler().set(FUNC(mekd1_state::pia0_cb2_w));

	// User PIA. All the I/O lines are available at P2.
	PIA6821(config, m_pia1);
	m_pia1->irqa_handler().set("mainirq", FUNC(input_merger_device::in_w<0>));
	m_pia1->irqb_handler().set("mainirq", FUNC(input_merger_device::in_w<1>));

	// User ACIA. Available at P2.
	// /CTS is pulled low, but may be driven.
	// /DCD is pulled low, but may be driven.
	ACIA6850(config, m_acia, 0);
	m_acia->irq_handler().set("mainirq", FUNC(input_merger_device::in_w<2>));

	// Off-board clock for the on-board MC6850.
	MC14411(config, m_brg, XTAL(1'843'200));
	m_brg->out_f<1>().set(FUNC(mekd1_state::write_f1_clock));
	m_brg->out_f<2>().set(FUNC(mekd1_state::write_f2_clock));
	m_brg->out_f<4>().set(FUNC(mekd1_state::write_f4_clock));
	m_brg->out_f<4>().set(FUNC(mekd1_state::write_f5_clock));
	m_brg->out_f<7>().set(FUNC(mekd1_state::write_f7_clock));
	m_brg->out_f<8>().set(FUNC(mekd1_state::write_f8_clock));
	m_brg->out_f<9>().set(FUNC(mekd1_state::write_f9_clock));
	m_brg->out_f<11>().set(FUNC(mekd1_state::write_f11_clock));
	m_brg->out_f<13>().set(FUNC(mekd1_state::write_f13_clock));

	RS232_PORT(config, m_rs232, default_rs232_devices, "terminal");
	m_rs232->set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));
}


/* ROM definition */
ROM_START( mekd1 )
	ROM_REGION( 0x0200, "mcm6830l7", 0 )
	ROM_LOAD("mikbugv9.bin", 0x0000, 0x0200, CRC(f5ff896f) SHA1(32990115ad9eebe7a1a5a03b4b1ea83360b1820f))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE   INPUT  CLASS        INIT        COMPANY     FULLNAME      FLAGS
COMP( 1975, mekd1, 0,  0,      mekd1,  mekd1, mekd1_state, empty_init, "Motorola", "MEK6800D1", MACHINE_NO_SOUND_HW )



mekd2.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Juergen Buchmueller, Robbbert
/******************************************************************************
    Motorola Evaluation Kit 6800 D2
    MEK6800D2

    system driver

    Juergen Buchmueller, Jan 2000
    2013-06-16 Working driver [Robbbert]

    memory map

    range       short   description
    0000-00ff   RAM     256 bytes RAM
    0100-01ff   RAM     optional 256 bytes RAM
    6000-63ff   PROM    optional PROM
    or
    6000-67ff   ROM     optional ROM
    8004-8007   PIA     expansion port
    8008-8009   ACIA    cassette interface
    8020-8023   PIA     keyboard interface
    a000-a07f   RAM     128 bytes RAM (JBUG scratch)
    c000-c3ff   PROM    optional PROM
    or
    c000-c7ff   ROM     optional ROM
    e000-e3ff   ROM     JBUG monitor program
    e400-ffff   -/-     mirrors of monitor rom


Enter the 4 digit address then the command key:

  - M : Examine and Change Memory (example: E000M, then G to skip to next, ESC to exit)
  - E : Escape (abort) operation (ESC key in our emulation)
  - R : Examine Registers
  - G : Begin execution at specified address
  - P : Punch data from memory to magnetic tape
  - L : Load memory from magnetic tape
  - N : Trace one instruction
  - V : Set (and remove) breakpoints

The keys are laid out as:

  P L N V

  7 8 9 A  M
  4 5 6 B  E
  1 2 3 C  R
  0 F E D  G


Pasting:
        0-F : as is
        NEXT : ^
        MEM : =
        GO : ^

Test Paste:
        HA030=11^22^33^44^55^66^77^88^99^HA030=
        Now press up-arrow to confirm the data has been entered.

        If you wish to follow the tutorial in the manual, here is the test
        program that you need to enter in step 1:
        H0020=8E^00^FF^4F^C6^04^CE^00^10^AB^00^08^5A^26^FA^97^15^3F^H

        Save the above program to tape:
        HA002=00^20^00^32^HP (A002 has start address, A004 has end address, big endian)

TODO
        Display should go blank during cassette operations


******************************************************************************/

#include "emu.h"
#include "cpu/m6800/m6800.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "machine/6821pia.h"
#include "machine/6850acia.h"
#include "machine/clock.h"
#include "machine/timer.h"
#include "speaker.h"

#include "mekd2.lh"


namespace {

#define XTAL_MEKD2 1228800

class mekd2_state : public driver_device
{
public:
	mekd2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pia_s(*this, "pia_s")
		, m_pia_u(*this, "pia_u")
		, m_acia(*this, "acia")
		, m_cass(*this, "cassette")
		, m_trace_timer(*this, "trace_timer")
		, m_digits(*this, "digit%u", 0U)
		, m_keyboard(*this, "X%d", 0U)
	{ }

	void mekd2(machine_config &config);

private:
	int key40_r();
	uint8_t key_r();
	void nmi_w(int state);
	void digit_w(uint8_t data);
	void segment_w(uint8_t data);
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_w);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);
	TIMER_DEVICE_CALLBACK_MEMBER(trace_timer);

	void mem_map(address_map &map) ATTR_COLD;

	uint8_t m_cass_data[4]{};
	uint8_t m_segment = 0U;
	uint8_t m_digit = 0U;
	uint8_t m_keydata = 0U;
	bool m_cassbit = 0;
	bool m_cassold = 0;
	virtual void machine_start() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_device<pia6821_device> m_pia_s;
	required_device<pia6821_device> m_pia_u;
	required_device<acia6850_device> m_acia;
	required_device<cassette_image_device> m_cass;
	required_device<timer_device> m_trace_timer;
	output_finder<6> m_digits;
	required_ioport_array<6> m_keyboard;
};



/***********************************************************

    Address Map

************************************************************/

void mekd2_state::mem_map(address_map &map)
{
	map(0x0000, 0x00ff).ram(); // user ram
	map(0x8004, 0x8007).rw(m_pia_u, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x8008, 0x8009).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0x8020, 0x8023).rw(m_pia_s, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xa000, 0xa07f).ram(); // system ram
	map(0xe000, 0xe3ff).rom().mirror(0x1c00).region("maincpu",0);   /* JBUG ROM */
}

/***********************************************************

    Keys

************************************************************/

static INPUT_PORTS_START( mekd2 )
	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E (hex)") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')

	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')

	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')

	PORT_START("X4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('P') // save tape
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('L') // load tape
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('N') // trace (step)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('V') // breakpoint

	PORT_START("X5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('=') // memory
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E (escape)") PORT_CODE(KEYCODE_ESC) PORT_CHAR('H')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R') // regs
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_UP) PORT_CHAR('^') // go, next
INPUT_PORTS_END


/***********************************************************

    Trace hardware (what happens when N is pressed)

************************************************************/

TIMER_DEVICE_CALLBACK_MEMBER(mekd2_state::trace_timer)
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
}


void mekd2_state::nmi_w(int state)
{
	if (state)
		m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
	else
		m_trace_timer->adjust(attotime::from_usec(18));
}



/***********************************************************

    Keyboard

************************************************************/

int mekd2_state::key40_r()
{
	return BIT(m_keydata, 6);
}

uint8_t mekd2_state::key_r()
{
	uint8_t i;
	m_keydata = 0xff;

	for (i = 0; i < 6; i++)
	{
		if (BIT(m_digit, i))
		{
			m_keydata &= m_keyboard[i]->read();
		}
	}

	i = 0x80;
	if (m_digit < 0x40)
		i = BIT(m_keydata, 0) ? 0x80 : 0;
	else
	if (m_digit < 0x80)
		i = BIT(m_keydata, 1) ? 0x80 : 0;
	else
	if (m_digit < 0xc0)
		i = BIT(m_keydata, 2) ? 0x80 : 0;
	else
		i = BIT(m_keydata, 3) ? 0x80 : 0;

	return i | m_segment;
}



/***********************************************************

    LED display

************************************************************/

void mekd2_state::segment_w(uint8_t data)
{
	m_segment = data & 0x7f;
}

void  mekd2_state::digit_w(uint8_t data)
{
	if (data < 0x3f)
	{
		for (uint8_t i = 0; i < 6; i++)
		{
			if (BIT(data, i))
				m_digits[i] = ~m_segment & 0x7f;
		}
	}
	m_digit = data;
}



/***********************************************************

    Interfaces

************************************************************/

QUICKLOAD_LOAD_MEMBER(mekd2_state::quickload_cb)
{
	static const char magic[] = "MEK6800D2";
	char buff[9];
	uint16_t addr, size;
	uint8_t ident, *RAM = memregion("maincpu")->base();

	image.fread(buff, sizeof(buff));
	if (memcmp(buff, magic, sizeof(buff)))
	{
		return std::make_pair(
				image_error::INVALIDIMAGE,
				util::string_format("Magic '%s' not found", magic));
	}
	image.fread(&addr, 2);
	addr = little_endianize_int16(addr);
	image.fread(&size, 2);
	size = little_endianize_int16(size);
	image.fread(&ident, 1);
	logerror("mekd2 rom load: $%04X $%04X $%02X\n", addr, size, ident);
	while (size-- > 0)
		image.fread(&RAM[addr++], 1);

	return std::make_pair(std::error_condition(), std::string());
}

TIMER_DEVICE_CALLBACK_MEMBER(mekd2_state::kansas_w)
{
	m_cass_data[3]++;

	if (m_cassbit != m_cassold)
	{
		m_cass_data[3] = 0;
		m_cassold = m_cassbit;
	}

	if (m_cassbit)
		m_cass->output(BIT(m_cass_data[3], 0) ? -1.0 : +1.0); // 2400Hz
	else
		m_cass->output(BIT(m_cass_data[3], 1) ? -1.0 : +1.0); // 1200Hz
}

TIMER_DEVICE_CALLBACK_MEMBER(mekd2_state::kansas_r)
{
	/* cassette - turn 1200/2400Hz to a bit */
	m_cass_data[1]++;
	uint8_t cass_ws = (m_cass->input() > +0.03) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		m_acia->write_rxd((m_cass_data[1] < 12) ? 1 : 0);
		m_cass_data[1] = 0;
	}
}

void mekd2_state::machine_start()
{
	m_digits.resolve();
}

/***********************************************************

    Machine

************************************************************/

void mekd2_state::mekd2(machine_config &config)
{
	/* basic machine hardware */
	M6800(config, m_maincpu, XTAL_MEKD2 / 2);        /* 614.4 kHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &mekd2_state::mem_map);

	config.set_default_layout(layout_mekd2);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cass);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	/* Devices */
	PIA6821(config, m_pia_s);
	m_pia_s->readpa_handler().set(FUNC(mekd2_state::key_r));
	m_pia_s->readcb1_handler().set(FUNC(mekd2_state::key40_r));
	m_pia_s->writepa_handler().set(FUNC(mekd2_state::segment_w));
	m_pia_s->writepb_handler().set(FUNC(mekd2_state::digit_w));
	m_pia_s->ca2_handler().set(FUNC(mekd2_state::nmi_w));
	m_pia_s->irqa_handler().set_inputline("maincpu", INPUT_LINE_NMI);
	m_pia_s->irqb_handler().set_inputline("maincpu", INPUT_LINE_NMI);

	PIA6821(config, m_pia_u);
	m_pia_u->irqa_handler().set_inputline("maincpu", M6800_IRQ_LINE);
	m_pia_u->irqb_handler().set_inputline("maincpu", M6800_IRQ_LINE);

	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set([this] (bool state) { m_cassbit = state; });

	clock_device &acia_tx_clock(CLOCK(config, "acia_tx_clock", XTAL_MEKD2 / 256)); // 4800Hz
	acia_tx_clock.signal_handler().set(m_acia, FUNC(acia6850_device::write_txc));

	clock_device &acia_rx_clock(CLOCK(config, "acia_rx_clock", 300)); // toggled by cassette circuit
	acia_rx_clock.signal_handler().set(m_acia, FUNC(acia6850_device::write_rxc));

	TIMER(config, "kansas_w").configure_periodic(FUNC(mekd2_state::kansas_w), attotime::from_hz(4800));
	TIMER(config, "kansas_r").configure_periodic(FUNC(mekd2_state::kansas_r), attotime::from_hz(40000));
	TIMER(config, m_trace_timer).configure_generic(FUNC(mekd2_state::trace_timer));

	QUICKLOAD(config, "quickload", "d2", attotime::from_seconds(1)).set_load_callback(FUNC(mekd2_state::quickload_cb));
}

/***********************************************************

    ROMS

************************************************************/

ROM_START(mekd2)
	ROM_REGION(0x0400,"maincpu",0)
	ROM_LOAD("jbug.rom", 0x0000, 0x0400, CRC(5ed08792) SHA1(b06e74652a4c4e67c4a12ddc191ffb8c07f3332e) )
ROM_END

} // anonymous namespace


/***************************************************************************

  Game driver(s)

***************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE   INPUT  CLASS        INIT        COMPANY     FULLNAME      FLAGS
COMP( 1977, mekd2,  0,      0,      mekd2,    mekd2, mekd2_state, empty_init, "Motorola", "MEK6800D2" , MACHINE_SUPPORTS_SAVE )



mekd3.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: 68bit
/******************************************************************************

Motorola Evaluation Kit 6802 D3 - MEK6802D3

Keypad commands:

RS (Reset)  - Reset, wired to the CPU reset line
EX (Escape) - Typically aborts user program, or exits command.
M  - Memory display/change
  Digits 5 and 6 show the entered data, and are blank unless it differs.
  Digits 7 and 8 show the actual data at the address.
  GO - increase the address.
  M  - decreases the address.
  FS - Offset calculation. Enter address, then press 'GO'.
  EX - exits memory display.
RD - Register display/alter
  RD  - advance to next register.
  T/B - trace a single instruction
  GO  - continue user code.
  EX  - exits register display.
GO - Go to user program.
  If no address is entered then it uses the pseudo PC, it continues.
  Enter the address and press 'Go' to use that entered address.
  It firstly checks that there is RAM at the stack pointer. ???
T/B - Trace one instruction.
FS T/B - Breakpoint editor
  GO - advance to next breakpoing, up to 8, then loops.
  FS - insert a breakpoint
  FC - deactivate breakpoint
  EX - exits breakpoing editor.
P/L - Punch tape
  The 300/1200 baud switch needs to be set, and the same rate selected.
  FS - Set 300 baud mode, J-BUG compatible.
  FC - Set 1200 baud mode, has checksum.
  At the 'b' prompt enter the beginning address of the data, then 'GO'.
  At the 'E' prompt enter the last address of the data, the 'GO' to
  start writing to the tape. There is a 30 second leader of $ff.
  M - Skip to Verify tape.
FS P/L - Load from tape
  FS - Set 300 baud mode, J-BUG compatible.
  FC - Set 1200 baud mode, has checksum.
  GO - Start load.
  M - Skip to Verify tape.
FS 0 to F
  One of 16 user defined functions. Press FS then one number key 0 to F.
  A pointer to a table of 16 function addresses should be set at 0x8102.
FC - Clears the 'FS' flag.


ASCII terminal commands:
B        - Breakpoing display, up to eight.
C        - Continue user program
D        - Delete all breakpoints
E <addr> - Examine a block of memory, with ASCII chars.
  Carriage Return - exit examine memory.
G <addr> - Go to the user program at <addr>.
  Keypad Escape - breaks execution.
L        - Load from audio tape.
           The 300/1200 baud switch needs to be set, and the same rate selected.
           The 300 baud rate is J-BUG compatible. The 1200 includes a checksum.
M <addr> - Memory display at <addr>
  Linefeed - Advance to next address.
  M or ^ - Previous address.
  Space  - Redisplay data.
  Carriage Return - exit memory display.
  O <addr> - Offset calculation.
N        - Trace one instruction.
O        - Offset load from tape.
P        - Punch tape.
R        - Register display and edit.
  Space  - Advance to next register.
  Carriage Return - exit register display.
  <nn>   - Change register value.
S <addr> - Set a breakpoint at the <addr>.
T <nn>   - Trace <nn> instructions.
U <addr> - Delete the breakpoint at <addr>
V        - Verify tape.
Ctrl-E   - Clear screen.
!        - Execute user function stored in user register 2, aka $816a
"        - Execute user function stored in user register 3, aka $816c
#        - Execute user function stored in user register 4, aka $816e

$FDFD    - Remove breakpoints. Documentation suggests assigning this address
           to a user function when debugging, to clean up breakpoints.


TODO

There are references to a MEK6802EA product, probably an Editor Assembler
that might have been for this system. Perhaps it was ROMable and that might
be nice to add here.

There was a color graphics evaluation board, the MEK68VDG, using the
MC6847. It must have had it's own expansion ROM as the D3DUG2 ROM for the
MEK68R2 is specific to the R2 and has no support for the 6847. This might be
a nice addition if documentation and/or a ROM can be found.

The RAM paging might need revising.

The ROM support is still TODO.

******************************************************************************/

#include "emu.h"
#include "cpu/m6800/m6800.h"
#include "machine/input_merger.h"
#include "machine/ram.h"
#include "machine/6821pia.h"
#include "machine/6850acia.h"
#include "machine/mc14411.h"
#include "machine/mc6846.h"
#include "machine/clock.h"
#include "machine/timer.h"
#include "video/pwm.h"
#include "bus/rs232/rs232.h"
#include "imagedev/cassette.h"
#include "speaker.h"
#include "mekd3.lh"

// MEK68R2
#include "machine/terminal.h"
#include "video/mc6845.h"
#include "emupal.h"
#include "screen.h"
#include "render.h"


namespace {

#define XTAL_MEKD3 3.579545_MHz_XTAL

class mekd3_state : public driver_device
{
public:
	mekd3_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_ram_bank(*this, "ram_bank")
		, m_mainirq(*this, "mainirq")
		, m_mainnmi(*this, "mainnmi")
		, m_mc6846(*this, "mc6846")
		, m_kpd_pia(*this, "kpd_pia")
		, m_display(*this, "display")
		, m_keypad_columns(*this, "COL%u", 0)
		  // MEK68IO
		, m_pia_io1(*this, "pia_io1")
		, m_pia_io2(*this, "pia_io2")
		, m_acia_io1(*this, "acia_io1")
		, m_acia_cas(*this, "acia_cas")
		, m_brg(*this, "brg")
		, m_cass(*this, "cassette")
		, m_console_enable(*this, "CONSOLE_ENABLE")
		, m_rs232_tx_baud(*this, "RS232_TX_BAUD")
		, m_rs232_rx_baud(*this, "RS232_RX_BAUD")
		, m_rs232_cts_route(*this, "RS232_CTS_ROUTE")
		, m_rs232_dcd_route(*this, "RS232_DCD_ROUTE")
		, m_cas_baud(*this, "CAS_BAUD")
		  // MEK68R2
		, m_mc6845(*this, "mc6845")
		, m_palette(*this, "palette")
		, m_screen(*this, "screen")
		, m_p_chargen(*this, "chargen")
		, m_video_ram(*this, "videoram")
		, m_r2_pia(*this, "r2_pia")
		, m_r2_mode(*this, "R2_MODE")
		, m_r2_display_nationality(*this, "R2_DISPLAY_NATIONALITY")
		, m_r2_display_format(*this, "R2_DISPLAY_FORMAT")
	{ }

	void mekd3(machine_config &config);
	void init_mekd3();

	void reset_key_w(int state);
	DECLARE_INPUT_CHANGED_MEMBER(keypad_changed);
	DECLARE_INPUT_CHANGED_MEMBER(rs232_cts_route_change);
	DECLARE_INPUT_CHANGED_MEMBER(rs232_dcd_route_change);

private:
	int keypad_cb1_r();
	uint8_t keypad_key_r();
	void led_digit_w(uint8_t data);
	void led_segment_w(uint8_t data);
	TIMER_DEVICE_CALLBACK_MEMBER(led_update);
	void page_w(uint8_t data);

	required_device<m6802_cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_memory_bank m_ram_bank;
	required_device<input_merger_device> m_mainirq;
	required_device<input_merger_device> m_mainnmi;
	required_device<mc6846_device> m_mc6846;
	required_device<pia6821_device> m_kpd_pia;
	required_device<pwm_display_device> m_display;
	required_ioport_array<4> m_keypad_columns;

	uint8_t m_rom_page;
	uint8_t m_ram_page;
	uint8_t m_segment;
	uint8_t m_digit;

	bool keypad_key_pressed();

	void mekd3_mem(address_map &map) ATTR_COLD;
	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);

	// MEK68IO

	void rs232_route_cts(int state);
	void rs232_route_dcd(int state);
	int m_cts;
	int m_dcd;

	// Clocks
	void write_f1_clock(int state);
	void write_f2_clock(int state);
	void write_f4_clock(int state);
	void write_f5_clock(int state);
	void write_f7_clock(int state);
	void write_f8_clock(int state);
	void write_f9_clock(int state);
	void write_f13_clock(int state);

	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);
	void acia_cas_clock300_w(int state);
	void acia_cas_clock1200_w(int state);
	uint8_t pia_io2a_r();

	required_device<pia6821_device> m_pia_io1;
	required_device<pia6821_device> m_pia_io2;
	required_device<acia6850_device> m_acia_io1;
	required_device<acia6850_device> m_acia_cas;
	required_device<mc14411_device> m_brg;
	required_device<cassette_image_device> m_cass;
	required_ioport m_console_enable;
	required_ioport m_rs232_tx_baud;
	required_ioport m_rs232_rx_baud;
	required_ioport m_rs232_cts_route;
	required_ioport m_rs232_dcd_route;
	required_ioport m_cas_baud;
	uint8_t m_cass_rx_period, m_cass_txcount;
	bool m_cass_in, m_cass_inbit, m_cass_txbit, m_cass_last_txbit;

	// MEK68R2
	MC6845_UPDATE_ROW(update_row);
	uint8_t r2_pia_pa_r();
	uint8_t r2_pia_pb_r();
	optional_device<mc6845_device> m_mc6845;
	optional_device<palette_device> m_palette;
	optional_device<screen_device> m_screen;
	optional_region_ptr<uint8_t> m_p_chargen;
	optional_shared_ptr<uint8_t> m_video_ram;
	optional_device<pia6821_device> m_r2_pia;
	optional_ioport m_r2_mode;
	optional_ioport m_r2_display_nationality;
	optional_ioport m_r2_display_format;
	void kbd_put(uint8_t data);
	uint8_t m_term_data;
};



/***********************************************************

    Address Map

************************************************************/

void mekd3_state::mekd3_mem(address_map &map)
{
	// User RAM banks
	map(0x0000, 0x7fff).bankrw("ram_bank");

	// MEK68IO User PIA
	map(0x8000, 0x8003).rw(m_pia_io1, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	// MEK68IO D3BUG cassette PIA
	map(0x8004, 0x8007).rw(m_pia_io2, FUNC(pia6821_device::read), FUNC(pia6821_device::write));

	// MEK68IO RS232 ACIA
	map(0x8008, 0x8009).rw(m_acia_io1, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	// MEK68IO cassette ACIA.
	map(0x800a, 0x800b).rw(m_acia_cas, FUNC(acia6850_device::read), FUNC(acia6850_device::write));

	// 8040-8041 MEK68VG VDG Scroll register

	// 8042-8043 MEK68R2 CRT register.
	map(0x8042, 0x8042).w(m_mc6845, FUNC(mc6845_device::address_w));
	map(0x8043, 0x8043).rw(m_mc6845, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));

	// MEK68R2 PIA (Keyboard)
	map(0x8044, 0x8047).rw(m_r2_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	// GPIA IEEE-488 8048-804F

	// 8050-8057 MEK68EP PROM Programmer

	// MEK6802D3 timer and I/O.
	map(0x8080, 0x8087).rw(m_mc6846, FUNC(mc6846_device::read), FUNC(mc6846_device::write));
	// MEK6802D3 display/keyboard PIA
	map(0x8088, 0x808b).rw(m_kpd_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));

	// MEK6802D3 MCM6810 RAM
	map(0x8100, 0x81ff).ram(); // system ram

	// 8200 - 87ff  User defined RAM

	// 8800 - 8fff  MEK68VDG RAM

	// 9000-9fff MEK68R2 and MEK68VDG Video RAM
	map(0x9000, 0x9fff).ram().share(m_video_ram);

	// a000-efff ROM banks.

	// D3BUG2 ROM
	map(0xf000, 0xf7ff).rom();

	// MEK6802D3 D3BUG ROM, MC6846 ROM
	map(0xf800, 0xffff).rom();
}

/***********************************************************

    Keys

************************************************************/

static INPUT_PORTS_START(mekd3)
	// RESET is not wired to the key matrix.
	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("RS") PORT_WRITE_LINE_MEMBER(FUNC(mekd3_state::reset_key_w))

	// PORT_CODEs are not assigned to the keypad to allow it on screen at
	// the same time as the terminal or CRT console which also receive
	// keyboard inputs. When a keyboard is available the keypad is of
	// limited use, but still useful to interrupt code or reset the
	// machine. If MAME someday allows the keyboard input focus to be
	// switched then this might be redesigned.
	PORT_START("COL0")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("M")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("FS")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("7")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("4")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("0")

	PORT_START("COL1")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("EX")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("FC")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("8")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("5")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("F")

	PORT_START("COL2")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("RD")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("P/L")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("9")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("6")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("E")

	PORT_START("COL3")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("GO")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("T/B")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("A")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("B")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("C")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::keypad_changed), 0) PORT_NAME("D")


	// MEK68IO

	PORT_START("CONSOLE_ENABLE")
	PORT_DIPNAME(0x01, 0x00, "RS-232 console")
	PORT_DIPSETTING(0x01, DEF_STR(On))
	PORT_DIPSETTING(0x00, DEF_STR(Off))

	// RS232 baud rates available for use at socket 1.
	PORT_START("RS232_TX_BAUD")
	PORT_CONFNAME(0x3f, 1, "RS232 TX Baud Rate")
	PORT_CONFSETTING(0x80, "110")
	PORT_CONFSETTING(0x40, "300")
	PORT_CONFSETTING(0x20, "600")
	PORT_CONFSETTING(0x10, "1200")
	PORT_CONFSETTING(0x08, "2400")
	PORT_CONFSETTING(0x04, "3600")
	PORT_CONFSETTING(0x02, "4800")
	PORT_CONFSETTING(0x01, "9600")

	PORT_START("RS232_RX_BAUD")
	PORT_CONFNAME(0x3f, 1, "RS232 RX Baud Rate")
	PORT_CONFSETTING(0x80, "110")
	PORT_CONFSETTING(0x40, "300")
	PORT_CONFSETTING(0x20, "600")
	PORT_CONFSETTING(0x10, "1200")
	PORT_CONFSETTING(0x08, "2400")
	PORT_CONFSETTING(0x04, "3600")
	PORT_CONFSETTING(0x02, "4800")
	PORT_CONFSETTING(0x01, "9600")

	// RS232 CTS and DCD routing at socket 3. These need to be
	// jumpered low if not driven by the RS232 device.
	PORT_START("RS232_CTS_ROUTE")
	PORT_CONFNAME(0x1, 0, "RS232 CTS") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::rs232_cts_route_change), 0)
	PORT_CONFSETTING(0, "Jumper Low")
	PORT_CONFSETTING(1, "Pass Through")
	PORT_START("RS232_DCD_ROUTE")
	PORT_CONFNAME(0x1, 0, "RS232 DCD") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd3_state::rs232_dcd_route_change), 0)
	PORT_CONFSETTING(0, "Jumper Low")
	PORT_CONFSETTING(1, "Pass Through")

	// A mechanical switch at the edge of the board set the cassette baud
	// rate. The software could not control or read this rate setting and
	// since D3BUG used a different format for 1200 baud it had to promote
	// for the rate too.
	PORT_START("CAS_BAUD")
	PORT_CONFNAME(0x01, 0, "Cassette Baud Rate")
	PORT_CONFSETTING(0x1, "1200")
	PORT_CONFSETTING(0x0, "300")


	// MEK68R2

	PORT_START("R2_MODE")
	PORT_DIPNAME(0x1, 0, "R2 Mode")
	PORT_DIPSETTING(0, "Normal")
	PORT_DIPSETTING(1, "Dumb terminal")

	PORT_START("R2_DISPLAY_NATIONALITY")
	PORT_DIPNAME(0x1, 1, "Display nationality")
	PORT_DIPSETTING(0, "US")
	PORT_DIPSETTING(1, "Europe")

	PORT_START("R2_DISPLAY_FORMAT")
	PORT_DIPNAME(0x0003, 2, "Display format")
	PORT_DIPSETTING(0, "16 lines of 32 characters")
	PORT_DIPSETTING(1, "16 lines of 64 characters")
	PORT_DIPSETTING(2, "20 lines of 80 characters")
	PORT_DIPSETTING(3, "User defined")

INPUT_PORTS_END


/***********************************************************

 RAM and ROM paging

************************************************************/

void mekd3_state::page_w(uint8_t data)
{
	m_rom_page = data & 0x07;
	// TODO switch the ROM bank entry.
	m_ram_page = (data >> 3) & 0x07;
	m_ram_bank->set_entry(m_ram_page);
}

/***********************************************************

    Keypad

************************************************************/

void mekd3_state::reset_key_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? CLEAR_LINE : ASSERT_LINE);

	if (!state)
	{
		m_mc6846->reset();
		m_kpd_pia->reset();
		m_kpd_pia->write(1, 2);
		// MEK68IO
		m_pia_io1->reset();
		m_pia_io2->reset();
		m_acia_io1->reset();
		m_acia_cas->reset();
		m_cass_rx_period = 0;
		m_cass_txcount = 0;
		m_cass_in = 0;
		m_cass_inbit = 0;
		// MEK68R2
		m_mc6845->reset();
		m_r2_pia->reset();
	}
}

bool mekd3_state::keypad_key_pressed()
{
	return (m_keypad_columns[0]->read() & m_digit) ||
		(m_keypad_columns[1]->read() & m_digit) ||
		(m_keypad_columns[2]->read() & m_digit) ||
		(m_keypad_columns[3]->read() & m_digit);
}

INPUT_CHANGED_MEMBER(mekd3_state::keypad_changed)
{
	m_kpd_pia->cb1_w(mekd3_state::keypad_key_pressed());
}

int mekd3_state::keypad_cb1_r()
{
	return mekd3_state::keypad_key_pressed();
}

uint8_t mekd3_state::keypad_key_r()
{
	uint8_t mux = (m_digit & 0xc0) >> 6;
	uint8_t i = (m_keypad_columns[mux]->read() & m_digit) ? 0 : 0x80;

	return i | m_segment;
}

/***********************************************************

    Seven segment LED display

************************************************************/

// PA
void mekd3_state::led_segment_w(uint8_t data)
{
	m_segment = data & 0x7f;
	m_display->matrix(m_digit, m_segment);
}

// PB
void mekd3_state::led_digit_w(uint8_t data)
{
	m_digit = data;
	m_display->matrix(m_digit, m_segment);
	// Update the keypad pressed output which depends on m_digit.
	m_kpd_pia->cb1_w(mekd3_state::keypad_key_pressed());
}

/***********************************************************

  MEK68IO

************************************************************/

void mekd3_state::rs232_route_cts(int state)
{
	if (m_rs232_cts_route->read())
		m_acia_io1->write_cts(state);

	// Cache the state, in case the ioport setting changes.
	m_cts = state;
}

void mekd3_state::rs232_route_dcd(int state)
{
	if (m_rs232_dcd_route->read())
		m_acia_io1->write_dcd(state);

	// Cache the state, in case the ioport setting changes.
	m_dcd = state;
}

INPUT_CHANGED_MEMBER(mekd3_state::rs232_cts_route_change)
{
	if (newval)
		m_acia_io1->write_cts(m_cts);
	else
		m_acia_io1->write_cts(0);
}

INPUT_CHANGED_MEMBER(mekd3_state::rs232_dcd_route_change)
{
	if (newval)
		m_acia_io1->write_dcd(m_dcd);
	else
		m_acia_io1->write_dcd(0);
}

void mekd3_state::write_f1_clock(int state)
{
	if (BIT(m_rs232_tx_baud->read(), 0))
		m_acia_io1->write_txc(state);
	if (BIT(m_rs232_rx_baud->read(), 0))
		m_acia_io1->write_rxc(state);
}

void mekd3_state::write_f2_clock(int state)
{
	if (BIT(m_rs232_tx_baud->read(), 1))
		m_acia_io1->write_txc(state);
	if (BIT(m_rs232_rx_baud->read(), 1))
		m_acia_io1->write_rxc(state);
}

void mekd3_state::write_f4_clock(int state)
{
	if (BIT(m_rs232_tx_baud->read(), 2))
		m_acia_io1->write_txc(state);
	if (BIT(m_rs232_rx_baud->read(), 2))
		m_acia_io1->write_rxc(state);
}

void mekd3_state::write_f5_clock(int state)
{
	if (BIT(m_rs232_tx_baud->read(), 3))
		m_acia_io1->write_txc(state);
	if (BIT(m_rs232_rx_baud->read(), 3))
		m_acia_io1->write_rxc(state);
}

void mekd3_state::write_f7_clock(int state)
{
	if (BIT(m_rs232_tx_baud->read(), 4))
		m_acia_io1->write_txc(state);
	if (BIT(m_rs232_rx_baud->read(), 4))
		m_acia_io1->write_rxc(state);

	// 1200 baud also drives the cassette ACIA
	if (m_cas_baud->read() == 1)
		acia_cas_clock1200_w(state);
}

void mekd3_state::write_f8_clock(int state)
{
	if (BIT(m_rs232_tx_baud->read(), 5))
		m_acia_io1->write_txc(state);
	if (BIT(m_rs232_rx_baud->read(), 5))
		m_acia_io1->write_rxc(state);
}

void mekd3_state::write_f9_clock(int state)
{
	if (BIT(m_rs232_tx_baud->read(), 6))
		m_acia_io1->write_txc(state);
	if (BIT(m_rs232_rx_baud->read(), 6))
		m_acia_io1->write_rxc(state);

	// 300 baud also drives the cassette ACIA
	if (m_cas_baud->read() == 0)
		acia_cas_clock300_w(state);
}

void mekd3_state::write_f13_clock(int state)
{
	if (BIT(m_rs232_tx_baud->read(), 7))
		m_acia_io1->write_txc(state);
	if (BIT(m_rs232_rx_baud->read(), 7))
		m_acia_io1->write_rxc(state);
}


TIMER_DEVICE_CALLBACK_MEMBER(mekd3_state::kansas_r)
{
	m_cass_rx_period++;

	// Turn 1200/2400Hz to a bit
	uint8_t cassin = (m_cass->input() > +0.04) ? 1 : 0;
	uint8_t inbit = m_cass_inbit;

	if (cassin != m_cass_in)
	{
		// Transition, now check the period.
		inbit = (m_cass_rx_period < 12) ? 1 : 0;
		m_cass_in = cassin;
		m_cass_rx_period = 0;
	}
	else if (m_cass_rx_period > 32)
	{
		// Idle the ACIA if there is no data.
		m_cass_rx_period = 32;
		inbit = 1;
	}

	if (inbit != m_cass_inbit)
	{
		m_acia_cas->write_rxd(inbit);
		m_cass_inbit = inbit;
	}
}

void mekd3_state::acia_cas_clock300_w(int state)
{
	// The Kansas City cassette format encodes a '0' bit by four cycles of
	// a 1200 Hz sine wave, and a '1' bit as eight cycles of 2400 Hz,
	// giving a 300 baud rate.
	//
	// The clock rate to the ACIA is 16x the baud rate and is divided by 2
	// to get the 2400 Hz rate, or divided by 4 to get the 1200 Hz rate.

	// Sync the period phase on TX bit transitions.
	if (m_cass_txbit != m_cass_last_txbit)
	{
		m_cass_txcount = 0;
		m_cass_last_txbit = m_cass_txbit;
	}

	if (m_cass_txbit)
		m_cass->output(BIT(m_cass_txcount, 1) ? +1.0 : -1.0); // 2400Hz
	else
		m_cass->output(BIT(m_cass_txcount, 2) ? +1.0 : -1.0); // 1200Hz

	m_cass_txcount++;
	m_acia_cas->write_txc(state);
	m_acia_cas->write_rxc(state);
}

void mekd3_state::acia_cas_clock1200_w(int state)
{
	// For the 1200 baud rate the number of cycles in reduced to just one
	// cycle at 1200 Hz and two at 2400 Hz.

	// Sync the period phase on TX bit transitions.
	if (m_cass_txbit != m_cass_last_txbit)
	{
		m_cass_txcount = 0;
		m_cass_last_txbit = m_cass_txbit;
	}

	if (m_cass_txbit)
		m_cass->output(BIT(m_cass_txcount, 3) ? +1.0 : -1.0); // 2400Hz
	else
		m_cass->output(BIT(m_cass_txcount, 4) ? +1.0 : -1.0); // 1200Hz

	m_cass_txcount++;
	m_acia_cas->write_txc(state);
	m_acia_cas->write_rxc(state);
}

uint8_t mekd3_state::pia_io2a_r()
{
	uint32_t console_enable = m_console_enable->read();

	// bit 1 (0x02) is a jumper to set RS232 terminal mode when set.

	return console_enable ? 0x02 : 0x00;
}


/***********************************************************

  MEK68R2

  This might in future be moved to a slot device, on a bus.

************************************************************/

// Delivery of keyboard inputs to the MEK68R2 keyboard is disabled when on
// views with the RS232 terminal, assuming that this keyboard is not present.
// Also disable delivery when the 'MEK68R2 present' jumper indicates it is
// disabled, assuming that this keyboard is not present.
void mekd3_state::kbd_put(uint8_t data)
{
	uint8_t view = machine().render().first_target()->view();
	if (view == 0)
		return;

	m_term_data = data;
	// Triggers on the falling edge.
	m_r2_pia->ca1_w(ASSERT_LINE);
	m_r2_pia->ca1_w(CLEAR_LINE);
	m_r2_pia->ca1_w(ASSERT_LINE);
}

// PA0 to PA6 - Keyboard data.
// PA7 - Display nationality, 0 USA, 1 Europe.
uint8_t mekd3_state::r2_pia_pa_r()
{
	uint8_t ret = m_term_data;
	int8_t display_nationality = m_r2_display_nationality->read();
	m_term_data = 0;
	return ret | (display_nationality << 7);
}

// PB0 - Mode: 0 normal, 1 dumb terminal.
// PB1,2,3 - N/C
// PB4 - User defined
// PB5 - Light pen control.
// PB7, PB6 - Display format.
//       00 - 16 lines of 32 characters.
//       01 - 16 lines of 64 characters.
//       10 - 20 lines of 80 characters.
//       11 - User defined.
uint8_t mekd3_state::r2_pia_pb_r()
{
	int8_t display_format = m_r2_display_format->read();
	int8_t mode = m_r2_mode->read();
	return (display_format << 6) | mode;
}

MC6845_UPDATE_ROW(mekd3_state::update_row)
{
	const pen_t *pen = m_palette->pens();

	int x = 0;

	for (int column = 0; column < x_count; column++)
	{
		uint8_t code = m_video_ram[(ma + column) & 0xfff];
		int dcursor = (column == cursor_x);

		if (BIT(code, 7))
		{
			/* Lores 6 pixel character.
			     -----------
			     | D1 | D0 |
			     | D3 | D2 |
			     | D5 | D4 |
			     -----------
			     D6 - 1 Grey tone, 0 brightness.
			*/
			int pixel = ((ra & 0x0c) >> 1) + 1;
			int dout = BIT(code, pixel);
			int grey = BIT(code, 6);
			int color = ((dcursor ^ dout) && de) << (grey ^ 1);
			bitmap.pix(y, x++) = pen[color];
			bitmap.pix(y, x++) = pen[color];
			bitmap.pix(y, x++) = pen[color];
			bitmap.pix(y, x++) = pen[color];
			pixel--;
			dout = BIT(code, pixel);
			color = ((dcursor ^ dout) && de) << (grey ^ 1);
			bitmap.pix(y, x++) = pen[color];
			bitmap.pix(y, x++) = pen[color];
			bitmap.pix(y, x++) = pen[color];
			bitmap.pix(y, x++) = pen[color];
		}
		else
		{
			offs_t address = ra < 8 ? ((code & 0x7f) << 3) | (ra & 0x07) : 0;
			uint8_t data = m_p_chargen[address];

			for (int bit = 0; bit < 8; bit++)
			{
				int dout = BIT(data, 7);
				int color = ((dcursor ^ dout) && de) << 1;

				bitmap.pix(y, x++) = pen[color];

				data <<= 1;
			}
		}
	}
}

/***********************************************************

************************************************************/

void mekd3_state::init_mekd3()
{
	uint8_t* ROM = memregion("maincpu")->base();

	// Hack to the trace timer delay, which is 0x000e by default, but
	// that did not produce proper timing for tracing.
	// 15 half works sometimes??
	// 16 to 19 half works, gets to user code but not to the next instruction!
	// 20 will step a nop but not a branch.
	// 21 also steps a branch.
	// 22 can overstep!
	ROM[0xf80c] = 21;
}

void mekd3_state::machine_start()
{
	uint8_t* RAM = m_ram->pointer();
	m_ram_bank->configure_entries(0, 8, RAM, 0x8000);

	save_item(NAME(m_rom_page));
	save_item(NAME(m_ram_page));
	save_item(NAME(m_segment));
	save_item(NAME(m_digit));
	save_item(NAME(m_cts));
	save_item(NAME(m_dcd));
	save_item(NAME(m_cass_rx_period));
	save_item(NAME(m_cass_txcount));
	save_item(NAME(m_cass_in));
	save_item(NAME(m_cass_inbit));
	save_item(NAME(m_cass_txbit));
	save_item(NAME(m_cass_last_txbit));
	save_item(NAME(m_term_data));
}

void mekd3_state::machine_reset()
{
	m_rom_page = 0;
	m_ram_page = 0;
	m_ram_bank->set_entry(0);

	// Avoid triggering an early interrupt when CA1 lowered. The mc6821
	// driver resets CA1 high and to trigger on a high to low
	// transition. The mekd3 programs CA1 to trigger on a low to high
	// transition and configuring this earlier here is adequate.
	m_kpd_pia->write(1, 2);

	m_brg->rsa_w(CLEAR_LINE);
	m_brg->rsb_w(ASSERT_LINE);

	m_cass_rx_period = 0;
	m_cass_txcount = 0;
	m_cass_in = 0;
	m_cass_inbit = 0;

	// Write low here if jumpered low.
	if (!m_rs232_cts_route->read())
		m_acia_io1->write_cts(0);
	if (!m_rs232_dcd_route->read())
		m_acia_io1->write_dcd(0);

	// MEK68R2
	m_r2_pia->ca1_w(ASSERT_LINE);
	m_r2_pia->ca2_w(ASSERT_LINE);
	m_r2_pia->cb1_w(0);
	m_r2_pia->cb2_w(0);
}

/***********************************************************

    Machine

************************************************************/

static DEVICE_INPUT_DEFAULTS_START(terminal)
	DEVICE_INPUT_DEFAULTS("RS232_RXBAUD", 0xff, RS232_BAUD_9600)
	DEVICE_INPUT_DEFAULTS("RS232_TXBAUD", 0xff, RS232_BAUD_9600)
	DEVICE_INPUT_DEFAULTS("RS232_DATABITS", 0xff, RS232_DATABITS_8)
	DEVICE_INPUT_DEFAULTS("RS232_PARITY", 0xff, RS232_PARITY_NONE)
	DEVICE_INPUT_DEFAULTS("RS232_STOPBITS", 0xff, RS232_STOPBITS_1)
DEVICE_INPUT_DEFAULTS_END

void mekd3_state::mekd3(machine_config &config)
{
	M6802(config, m_maincpu, XTAL_MEKD3);        // 894.8 kHz clock
	m_maincpu->set_ram_enable(false);
	m_maincpu->set_addrmap(AS_PROGRAM, &mekd3_state::mekd3_mem);

	RAM(config, m_ram).set_default_size("256K").set_default_value(0);

	INPUT_MERGER_ANY_HIGH(config, m_mainirq).output_handler().set_inputline(m_maincpu, M6802_IRQ_LINE);
	INPUT_MERGER_ANY_HIGH(config, m_mainnmi).output_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);

	// LED display
	PWM_DISPLAY(config, m_display).set_size(8, 7);
	m_display->set_segmask(0xff, 0x7f);

	config.set_default_layout(layout_mekd3);

	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	// Keypad and display PIA. CA2 and CB2 are NC.
	PIA6821(config, m_kpd_pia);
	m_kpd_pia->readpa_handler().set(FUNC(mekd3_state::keypad_key_r));
	m_kpd_pia->readcb1_handler().set(FUNC(mekd3_state::keypad_cb1_r));
	m_kpd_pia->writepa_handler().set(FUNC(mekd3_state::led_segment_w));
	m_kpd_pia->writepb_handler().set(FUNC(mekd3_state::led_digit_w));
	m_kpd_pia->ca1_w(0);
	m_kpd_pia->irqa_handler().set(m_mainnmi, FUNC(input_merger_device::in_w<0>));
	m_kpd_pia->irqb_handler().set(m_mainnmi, FUNC(input_merger_device::in_w<1>));

	// CP1, CP2, /CTG, /CTC are available at SK1, and not used here.
	MC6846(config, m_mc6846, XTAL_MEKD3 / 4);  // Same as the cpu clock
	m_mc6846->out_port().set(FUNC(mekd3_state::page_w));
	m_mc6846->cto().set(m_kpd_pia, FUNC(pia6821_device::ca1_w)); // trace timer
	m_mc6846->irq().set(m_mainirq, FUNC(input_merger_device::in_w<0>));

	// MEK68IO

	// A 'user' PIA, I/O available at SK6.
	PIA6821(config, m_pia_io1);
	m_pia_io1->irqa_handler().set(m_mainnmi, FUNC(input_merger_device::in_w<2>));
	m_pia_io1->irqb_handler().set(m_mainirq, FUNC(input_merger_device::in_w<1>));

	// Largely a 'user' PIA, I/O available at SK5.
	// PA0 can optionally be an audio bit input, at TP1.
	// PA1 is a jumper mode input, and low by default.
	// PA2 can optionally be an audio bit output, at TP2.
	PIA6821(config, m_pia_io2);
	m_pia_io2->readpa_handler().set(FUNC(mekd3_state::pia_io2a_r));
	m_pia_io2->irqa_handler().set(m_mainirq, FUNC(input_merger_device::in_w<2>));
	m_pia_io2->irqb_handler().set(m_mainirq, FUNC(input_merger_device::in_w<3>));

	// RS232 ACIA
	// /RTS, /CTS and /DCD are available at SK3.
	ACIA6850(config, m_acia_io1, 0);
	m_acia_io1->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_acia_io1->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_acia_io1->irq_handler().set(m_mainirq, FUNC(input_merger_device::in_w<4>));

	MC14411(config, m_brg, XTAL(1'843'200));
	m_brg->out_f<1>().set(FUNC(mekd3_state::write_f1_clock));
	m_brg->out_f<2>().set(FUNC(mekd3_state::write_f2_clock));
	m_brg->out_f<4>().set(FUNC(mekd3_state::write_f4_clock));
	m_brg->out_f<4>().set(FUNC(mekd3_state::write_f5_clock));
	m_brg->out_f<7>().set(FUNC(mekd3_state::write_f7_clock));
	m_brg->out_f<8>().set(FUNC(mekd3_state::write_f8_clock));
	m_brg->out_f<9>().set(FUNC(mekd3_state::write_f9_clock));
	m_brg->out_f<13>().set(FUNC(mekd3_state::write_f13_clock));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_acia_io1, FUNC(acia6850_device::write_rxd));
	rs232.cts_handler().set(FUNC(mekd3_state::rs232_route_cts));
	rs232.dcd_handler().set(FUNC(mekd3_state::rs232_route_dcd));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	// /RTS is available at SK2.
	// /CTS and /DCD are available at SK2, or can be jumpered low.
	ACIA6850(config, m_acia_cas, 0);
	m_acia_cas->txd_handler().set([this] (bool state) { m_cass_txbit = state; });
	m_acia_cas->irq_handler().set(m_mainirq, FUNC(input_merger_device::in_w<5>));

	TIMER(config, "kansas_r").configure_periodic(FUNC(mekd3_state::kansas_r), attotime::from_hz(40000));

	// MEK68R2

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);

	m_screen->set_refresh_hz(50);
	m_screen->set_size(80 * 8 + 80 * 10, 20 * 12 + 100);
	m_screen->set_visarea(0, 80 * 8 + 80 * 10 - 1, 0, 20 * 12 + 100 - 1);
	m_screen->set_screen_update("mc6845", FUNC(mc6845_device::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	MC6845(config, m_mc6845, XTAL(14'318'181)/8);
	m_mc6845->set_screen(m_screen);
	//
	m_mc6845->set_show_border_area(false);
	m_mc6845->set_char_width(8);
	m_mc6845->set_update_row_callback(FUNC(mekd3_state::update_row));
	m_mc6845->out_hsync_callback().set(m_r2_pia, FUNC(pia6821_device::cb2_w));
	m_mc6845->out_vsync_callback().set(m_r2_pia, FUNC(pia6821_device::cb1_w));

	// PA is the keyboard data and a mode flag.
	// CA1 is keyboard strobe.
	// CA2 light pen input.
	// PB0 is mode flags and light pen control.
	// CB1 is VSYNC, and CB2 is HSYNC.
	PIA6821(config, m_r2_pia);
	m_r2_pia->readpa_handler().set(FUNC(mekd3_state::r2_pia_pa_r));
	m_r2_pia->readpb_handler().set(FUNC(mekd3_state::r2_pia_pb_r));
	m_r2_pia->irqa_handler().set(m_mainirq, FUNC(input_merger_device::in_w<6>));
	m_r2_pia->irqb_handler().set(m_mainirq, FUNC(input_merger_device::in_w<7>));

	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(mekd3_state::kbd_put));
}

/***********************************************************

    ROMS

************************************************************/

ROM_START(mekd3)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("d3bug.rom", 0xf800, 0x0800, CRC(57863614) SHA1(b31679df86367d1e48e12f01a22cd0f008e74df4))
	ROM_LOAD("d3bug2.rom", 0xf000, 0x0800, CRC(bf3640b0) SHA1(374362c4464ab3986af2f08395bf254d1ce7a52f))
	ROM_REGION(0x0400, "chargen",0)
	ROM_LOAD("mcm6674p.chr", 0x0000, 0x0400, CRC(1c22088a) SHA1(b5f0bd0cfdec0cd5c1cb764506bef3c17d6af0eb))
ROM_END

} // anonymous namespace


/***************************************************************************

  Game driver(s)

***************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE   INPUT  CLASS        INIT        COMPANY     FULLNAME      FLAGS
COMP( 1978, mekd3,  0,      0,      mekd3,    mekd3, mekd3_state, init_mekd3, "Motorola", "MEK6802D3" , MACHINE_NO_SOUND )



mekd4.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: 68bit
/******************************************************************************

Motorola Evaluation Kit 6809 D4 - MEK6809D4

Memory map

Range    Short  Description

0000-0fff RAM   User RAM, 4K, default location. It can be remapped to any 1K
                address boundary and on any RAM page, or disabled.

0000-7fff RAM   Off board RAM typically maps in this range, on any RAM page.

9000-9fff RAM   MEK68R2 Screen RAM

e000-e07f I/O   I/O1 Select Area
e080-e0ff I/O   I/O2 Select Area, includes system I/O below.

e0f1-e0ff SYSIO Onboard system I/O is always mapped to these addresses
                irrespective of the RAM or ROM pages.
e0f1-e0f1 Cfg   Read: 4-bit Config jumper; Write: RAM/ROM page latch
e0f2-e0f3 ACIA  Console RS-232
e0f4-e0f7 PIA   Stop on address comparator PIA. A0 and A1 are reversed.
e0f8-e0fb PIA   Keypad and LED display
e0fc-e0ff PIA   User PIA on Keypad-display board

e400-e7ff RAM   Onboad 1K stack RAM. Always mapped to these addresses
                irrespective of the RAM or ROM page.

e800-efff ROM   ROMG U61, normal location.
f000-ffff ROM   ROMH U62, normal location.


The onboard 4K user RAM can be placed at any 1K address boundary on any RAM
page and on multiple RAM pages. The default is 0x0000 to 0x0fff on all RAM
pages, but jumpers may be added at connector J2 to modify this selection. If a
jumper is present across pins 1-2, or 3-4, or 5-6, or 7-8, then address lines
A15 to A12 respectively must be high (rather than low) for the user RAM to be
selected. By default, with no jumpers installed, the RAM page address lines
RAP0 to RAP2 are 'do not care', but J2 pin pairs can be jumpered to select the
user RAM only when these are high or low - pins 9-10 select RAP0 being high,
pins 11-12 select RAP0 being low, pins 13-14 select RAP1 being high, etc. The
user RAM can also be disabled by jumpering any of pins 10, 12, 14, 16, 18, 20
low which can be done by jumpering any of these to any of pins 2, 4, 6, or 8.
The user RAM data bus is buffered and so long as it is decoded then the data
lines are driven so removing this RAM does not free the decoded address range,
and the onboard user RAM takes precedence over off board addressing. It would
cause a data bus conflict if the user RAM were decoded in the same address
range as the onboard stack RAM, ROM, or the system I/O. This user RAM can also
be write protected by removing the jumper between pins 11-12 at J1.

TODO implement the 4K user RAM mapping.

There are eight onboard ROM sockets, labeled ROM/A to ROM/H. ROM/A to ROM/D
have addressing for A0 to A12 so support 8K devices, however collectively
their A11 and/or A12 lines can be jumpered low or high to support smaller
devices. The ROM/E and ROM/F have addressing for A0 to A11 so support 4K
devices, but can be collectively jumpered for 1K, 2K or 4K operation. ROM/G
and ROM/H are used for the monitor ROMS and support 2K or 4K devices
individually, and the standard ROM sizes are 2K and 4K respectively. The
onboard ROM data bus is buffered and so long as it is decoded then the data
lines are driven so removing a ROM does not free the decoded address range,
and the onboard ROM takes precedence over off board addressing. It would cause
a data bus conflict if this ROM were decoded in the same address range as
the onboard stack RAM, the user RAM, or the system I/O. The data bus buffer
for the ROMs is not bi-directional, it would appear to not be possible to
place RAM or I/O in these sockets, and it appears that it would cause a bus
conflict if there were a valid write to these ROMs, and the W/R line is wired
to the ROM decoder to support that - not sure what was intended here?

The onboard ROM selection is handled by a 1K mapping ROM in U31. The outputs
D0 to D7 selecting ROM/A to ROM/H respectively when low. It was up to the
programming of this ROM to avoid address conflicts, to select only one ROM at
a time. The ROM address inputs A9 to A0 are respectively: A15, A14, A13, A12,
A11, A10, ROP2, ROP1, ROP0, R/W. This allows mapping ROMS on 1K address map
boundaries. The default mapping ROM was label "D4MAP 00", a MCM68A316, and did
not select any ROM 0x4000.

The mapping ROM pins 18 and 20 and wired to 0V, pin 21 is wired to +5V, pin
19 can be jumpered to 0V or 5V, defaulting to 0V. So could a single supply
EPROM could be substituted. Pin 19 is A10 on a 2716 EPROM the lower half of a
2K EPROM could be used too.

TODO implement the onboard ROMs and their mapping.

There was a resident editor assembler product available for this system, the
"MEK6809EAC Editor/Assembler V1.0 2/80". It was supplied on a tape and side
one had a version to load into RAM at 0100 to 2fff, and side two had a version
that was ROMable at a000 to cfff. It supported the MEK68R2 CRT display or a
terminal and object code could be placed in memory or saved on tape.

TODO This editor/assembler ROM might be a nice addition if it can be found.

The CPU clock is either generated from the onboard 3.579545 MHz XTAL or
externally as set via J3.

TODO could support 1MHz and 2MHz CPU operation.

The board includes a hardware 'stop' address comparator which compares the
A0-A15 address lines to the output of the PIA at e0f4-e0f7, 'stop_pia' here,
and asserts that PIA's CA1 input when they match. The monitor software uses
this to trigger an interrupt to implement code tracing. The PIA port B outputs
B0 to B7 are compared to the address lines A0 to A7 respectively, and the port
A outputs A0 to A7 are compared to the address lines A8 to A15
respectively. Notably the comparitor does not compare the RAM or ROM bank
lines, so this might be frustrating when used with code making use of
banking. It appears that the address comparator does not distinguish between
code versus data, so it would appear to be able to trigger on a data access
too, and although the monitor documenation does not mention such a use this
use is possible with the monitor. The trigger is also available at TP1 and
this could be informative in some hardware development, with the address set
and the interrupt disabled. The CA2 input can also be toggled low to generate
an interrupt as a manually 'abort'.


J1
1-2  RS-232 console
3-4  R2 board present
7-8  Keypad present
5-6  300/1200 cassette baud rate
11-12 Write protect 4K user ram.


ASCII terminal commands:
<hex addr><space> - Memory change
  <hex byte>      - enter data, write to memory.
  <space>         - increase address, same line
  <linefeed>      - increase address, new line
  '-' or '^'      - decrease address, new line
  ';'<hex addr>   - calculate branch offset to addr, 8 bit or 16 bit offset.
  <carriage return> - return from memory change
'R'               - Register editor
  'P'             - Program counter
  'A'             - A-Accumulator
  'B'             - B-Accumulator
  'X'             - X-Index
  'Y'             - Y-Index
  'C'             - Condition Codes
  'H'             - RAM/ROM pages select latch, high nibble is ROM page, low RAM page.
  'D'             - Direct Page
  'U'             - U-Stack
  'S'             - S-Stack
  '1','2','3','4' - Definable 16-bit registers (memory locations)
  <cr>            - exit register editor
  <linefeed>      - display update
  <space>         - next register
  'T'             - trace one instruction
  'L'             - trace one line (subroutine), using single stepping
  'R'             - trace one line (subroutine), using hardware (real time) approach.
'Q'               - Enter breakpoint editor
  'I'<addr>       - insert breakpoint at <addr>, to stop after one times.
  'I'<addr>;<n>   - insert breakpoint at <addr>, to stop after <n> times.
  'R'<addr>       - remove breakpoing at <addr>
  'S'<addr>       - set stop address, to stop after one times.
  'S'<addr>;<n>   - set stop address, to stop after <n> times.
  'K'             - clear all breakpoints and deactivate the stop address.
  <cr>            - exit breakpoint editor.
'G'               - Continue at the pseudo program counter.
<addr>'G'         - Go to user program at <addr>
'M'               - Memory dump
'P'               - Punch. Store information from memory to cassette.
<addr>'P'         - Punch with offset, as if starting from <addr>.
'L'               - Load. Read information to memory from cassette.
<addr>'L'         - Load with offset.
'V'               - Verify cassette data against memory.
<addr>'V'         - Verify with offset.
<hex>=            - Convert hex to decimal.
#<decimal>$       - Convert decimal to hex.
'U'               - Switch R2 to User screen page.
'S'               - Switch R2 to System screen page.
'X'               - Enter special functions.
  'M'             - Move memory.
  'F'             - Fill memory.
  'S'             - Search memory. Escape to pause.
  'A'             - ASCII Entry.
    '@'           - End of message.
    <escape>      - Exit ASCII entry.
    <delete>      - Delete last character.
    <backspace>   - When using R2D, back up.


MEK68KPD commands:

RS (Reset)  Reset, wired to the CPU reset line
EX (Escape) Typically aborts user program. Switch from CRT to KPD.
M (Memory display/change)
  Digits 5 and 6 show the entered data, and are blank unless it differs.
  Digits 7 and 8 show the actual data at the address.
  G  - increase the address.
  M  - decreases the address.
  FS - Offset calculation. Enter address, then press 'GO'.
    Last digit is S for a short offset and L for a long 16 bit offset.
    FS - stores the offset and returns to memory display and increased the address.
    FC - return to memory display at the same address.
  EX - exits memory display.
RD (Register display/alter)
  The 'HP' register is the hardware page register, upper nibble the ROM page,
  lower the RAM page, 8 pages each.
  The 'SA' register is the hardware stop address.
    FS - at register 'SA', to change the 'number of times'.
    FC - at register 'SA', to change the stop address.
  G   - advance to next register.
  M   - previous register.
  T/B - trace a single instruction
  P/L - trace user line, an entire subroutine if next, slow software version.
  RD  - trace user line, using the stop address hardware.
  EX  - exits register display.
GO to user program.
  If no address if entered then it uses the pseudo PC, it continues.
  Enter the address and press 'Go' to use that entered address.
  It firstly checks that there is RAM at the stack pointer.
FS GO - enter the address, then FS, then GO and it runs as a sub of the LED
  display code allowing use of the LED display. The sub should be quick
  relative to the 1ms display update period.
FS T/B - Breakpoint editor
  GO - advance to next breakpoing, up to 8, then loops.
  FC - deactivate breakpoint
  FS - edit the 'number of times before stopping' for the current entry.
  FS - enter an address then press FS to enter that as a breakpoint address
       and then edit the 'number of times before stopping'.
  EX - exits breakpoing editor.
P/L (Punch tape)
  Enter an address before P/L to set the apparent beginning address.
  At the 'b' prompt enter the beginning address of the data, then 'GO'.
  At the 'E' prompt enter the last address of the data.
  Start the tape and press GO. There is a 30 second leader of $ff.
FS P/L (Load from tape)
  Enter an address before FS for an offset load or verify??
FS 0 to F
  One of 16 user defined functions. Press FS then one number key 0 to F.
  Numeric data may be entered before pressing FS.
  A pointer to a table of 16 function address should be set at 0xe72e.
  There is a reservation at 0xe730-0xe74f for this table.
  The function address is stored in 0xe700 and called by the LED display
  update function PUT every 1msec.

******************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/m6809/m6809.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "machine/6821pia.h"
#include "machine/6850acia.h"
#include "machine/bankdev.h"
#include "machine/clock.h"
#include "machine/input_merger.h"
#include "machine/mc14411.h"
#include "machine/timer.h"
#include "video/pwm.h"

// MEK68R2
#include "machine/terminal.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "render.h"
#include "screen.h"
#include "speaker.h"

#include "mekd4.lh"


namespace {

class mekd4_state : public driver_device
{
public:
	mekd4_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_bankdev(*this, "bankdev")
		, m_stop_pia(*this, "stop_pia")
		, m_kpd_pia(*this, "kpd_pia")
		, m_display(*this, "display")
		, m_user_pia(*this, "user_pia")
		, m_brg(*this, "brg")
		, m_rs232_tx_baud(*this, "RS232_TX_BAUD")
		, m_rs232_rx_baud(*this, "RS232_RX_BAUD")
		, m_rs232_cts_route(*this, "RS232_CTS_ROUTE")
		, m_rs232_dcd_route(*this, "RS232_DCD_ROUTE")
		, m_acia(*this, "acia")
		, m_cass(*this, "cassette")
		, m_jumper1(*this, "JUMPER1")
		, m_keypad_columns(*this, "COL%u", 0)
		  // MEK68R2
		, m_mc6845(*this, "mc6845")
		, m_palette(*this, "palette")
		, m_screen(*this, "screen")
		, m_p_chargen(*this, "chargen")
		, m_video_ram(*this, "videoram")
		, m_r2_pia(*this, "r2_pia")
		, m_r2_mode(*this, "R2_MODE")
		, m_r2_display_nationality(*this, "R2_DISPLAY_NATIONALITY")
		, m_r2_display_format(*this, "R2_DISPLAY_FORMAT")
	{ }

	void mekd4(machine_config &config);
	void init_mekd4();

	void reset_key_w(int state);
	DECLARE_INPUT_CHANGED_MEMBER(keypad_changed);
	DECLARE_INPUT_CHANGED_MEMBER(rs232_cts_route_change);
	DECLARE_INPUT_CHANGED_MEMBER(rs232_dcd_route_change);

private:
	uint8_t main_r(offs_t offset);
	void main_w(offs_t offset, uint8_t data);
	uint8_t config_r();
	void page_w(uint8_t data);
	uint8_t stop_pia_r(offs_t offset);
	void stop_pia_w(offs_t offset, uint8_t data);
	void stop_pia_pa_w(uint8_t data);
	void stop_pia_pb_w(uint8_t data);
	uint16_t m_stop_address;

	void rs232_route_cts(int state);
	void rs232_route_dcd(int state);

	// Clocks
	void write_f1_clock(int state);
	void write_f3_clock(int state);
	void write_f7_clock(int state);
	void write_f8_clock(int state);
	void write_f9_clock(int state);
	void write_f13_clock(int state);

	int keypad_cb1_r();
	uint8_t keypad_key_r();
	void led_digit_w(uint8_t data);
	void led_segment_w(uint8_t data);

	int stop_pia_cb1_r();
	void stop_pia_cb2_w(int state);

	void mekd4_stop_mem(address_map &map) ATTR_COLD;
	void mekd4_mem(address_map &map) ATTR_COLD;

	address_space *m_banked_space;

	bool keypad_key_pressed();

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	uint8_t m_rom_page; // aka ROP0, ROP1, ROP2
	uint8_t m_ram_page; // aka RAP0, RAP1, RAP2
	uint8_t m_segment;
	uint8_t m_digit;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_device<address_map_bank_device> m_bankdev;
	required_device<pia6821_device> m_stop_pia;
	required_device<pia6821_device> m_kpd_pia;
	required_device<pwm_display_device> m_display;
	required_device<pia6821_device> m_user_pia;
	required_device<mc14411_device> m_brg;
	required_ioport m_rs232_tx_baud;
	required_ioport m_rs232_rx_baud;
	required_ioport m_rs232_cts_route;
	required_ioport m_rs232_dcd_route;
	required_device<acia6850_device> m_acia;
	required_device<cassette_image_device> m_cass;
	required_ioport m_jumper1;
	required_ioport_array<4> m_keypad_columns;
	int m_cts;
	int m_dcd;

	// MEK68R2
	MC6845_UPDATE_ROW(update_row);
	uint8_t r2_pia_pa_r();
	uint8_t r2_pia_pb_r();
	optional_device<mc6845_device> m_mc6845;
	optional_device<palette_device> m_palette;
	optional_device<screen_device> m_screen;
	optional_region_ptr<uint8_t> m_p_chargen;
	optional_shared_ptr<uint8_t> m_video_ram;
	optional_device<pia6821_device> m_r2_pia;
	optional_ioport m_r2_mode;
	optional_ioport m_r2_display_nationality;
	optional_ioport m_r2_display_format;
	void kbd_put(uint8_t data);
	uint8_t m_term_data;
};



/***********************************************************

    Address Map

************************************************************/

void mekd4_state::mekd4_stop_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(mekd4_state::main_r), FUNC(mekd4_state::main_w));
}

void mekd4_state::mekd4_mem(address_map &map)
{
	map(0x0000, 0x7fff).ram();

	/* MEK68R2 Video RAM 9000-9fff */
	map(0x9000, 0x9fff).ram().share(m_video_ram);

	/* MEK68VG VDG Scroll register f040-f041 */

	/* MEK68R2 CRT register f042-f043 */
	map(0xe042, 0xe042).w(m_mc6845, FUNC(mc6845_device::address_w));
	map(0xe043, 0xe043).rw(m_mc6845, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));

	/* MEK68R2 PIA (Keyboard) */
	map(0xe044, 0xe047).rw(m_r2_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));

	map(0xe0f1, 0xe0f1).rw(FUNC(mekd4_state::config_r), FUNC(mekd4_state::page_w));
	map(0xe0f2, 0xe0f3).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xe0f4, 0xe0f7).rw(FUNC(mekd4_state::stop_pia_r), FUNC(mekd4_state::stop_pia_w));
	map(0xe0f8, 0xe0fb).rw(m_kpd_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xe0fc, 0xe0ff).rw(m_user_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));

	map(0xe400, 0xe7ff).ram();

	map(0xe800, 0xefff).rom();
	map(0xf000, 0xffff).rom();
}

/***********************************************************

    Keys

************************************************************/

static INPUT_PORTS_START(mekd4)

	PORT_START("JUMPER1")
	PORT_DIPNAME(0x01, 0x00, "RS-232 console (D4B)")
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPSETTING(0x01, DEF_STR(Off))
	PORT_DIPNAME(0x02, 0x00, "MEK68R2 present (D4C)")
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPSETTING(0x02, DEF_STR(Off))
	PORT_DIPNAME(0x04, 0x00, "Cassette baud rate")
	PORT_DIPSETTING(0x00, "1200")
	PORT_DIPSETTING(0x04, "300")
	PORT_DIPNAME(0x08, 0x00, "Keypad and display present (D4A)")
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPSETTING(0x08, DEF_STR(Off))

	PORT_START("RS232_TX_BAUD")
	PORT_CONFNAME(0x3f, 1, "RS232 TX Baud Rate")
	PORT_CONFSETTING(0x20, "110")
	PORT_CONFSETTING(0x10, "300")
	PORT_CONFSETTING(0x08, "600")
	PORT_CONFSETTING(0x04, "1200")
	PORT_CONFSETTING(0x02, "4800")
	PORT_CONFSETTING(0x01, "9600")

	PORT_START("RS232_RX_BAUD")
	PORT_CONFNAME(0x3f, 1, "RS232 RX Baud Rate")
	PORT_CONFSETTING(0x20, "110")
	PORT_CONFSETTING(0x10, "300")
	PORT_CONFSETTING(0x08, "600")
	PORT_CONFSETTING(0x04, "1200")
	PORT_CONFSETTING(0x02, "4800")
	PORT_CONFSETTING(0x01, "9600")

	// RS232 CTS and DCD routing at the RS232 Conn. These need to be
	// jumpered to logical low if not driven by the RS232 device. There is
	// +12 and -12V available at this connector for this purpose.
	PORT_START("RS232_CTS_ROUTE")
	PORT_CONFNAME(0x1, 0, "RS232 CTS") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::rs232_cts_route_change), 0)
	PORT_CONFSETTING(0, "Jumper low")
	PORT_CONFSETTING(1, "Pass through")
	PORT_START("RS232_DCD_ROUTE")
	PORT_CONFNAME(0x1, 0, "RS232 DCD") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::rs232_dcd_route_change), 0)
	PORT_CONFSETTING(0, "Jumper low")
	PORT_CONFSETTING(1, "Pass through")

	// RESET is not wired to the key matrix.
	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("RS") PORT_WRITE_LINE_MEMBER(FUNC(mekd4_state::reset_key_w))

	// PORT_CODEs are not assigned to the keypad to allow it on screen at
	// the same time as the terminal or CRT console which also receive
	// keyboard inputs. When a keyboard is available the keypad is of
	// limited use, but still useful to interrupt code or reset the
	// machine. If MAME someday allows the keyboard input focus to be
	// switched then this might be redesigned.
	PORT_START("COL0")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("M")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("FS")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("7")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("4")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("0")

	PORT_START("COL1")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("EX")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("FC")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("8")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("5")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("F")

	PORT_START("COL2")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("RD")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("P/L")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("9")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("6")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("E")

	PORT_START("COL3")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("GO")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("T/B")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("A")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("B")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("C")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd4_state::keypad_changed), 0) PORT_NAME("D")

	// MEK68R2

	PORT_START("R2_MODE")
	PORT_DIPNAME(0x1, 0, "R2 Mode")
	PORT_DIPSETTING(0, "Normal")
	PORT_DIPSETTING(1, "Dumb terminal")

	PORT_START("R2_DISPLAY_NATIONALITY")
	PORT_DIPNAME(0x1, 1, "Display nationality")
	PORT_DIPSETTING(0, "US")
	PORT_DIPSETTING(1, "Europe")

	PORT_START("R2_DISPLAY_FORMAT")
	PORT_DIPNAME(0x0003, 2, "Display format")
	PORT_DIPSETTING(0, "16 lines of 32 characters")
	PORT_DIPSETTING(1, "16 lines of 64 characters")
	PORT_DIPSETTING(2, "20 lines of 80 characters")
	PORT_DIPSETTING(3, "User defined")

INPUT_PORTS_END


/***********************************************************

 Stop comparitor.

************************************************************/

uint8_t mekd4_state::main_r(offs_t offset)
{
	if (offset == m_stop_address && !machine().side_effects_disabled())
	{
		m_stop_pia->ca1_w(CLEAR_LINE);
		m_stop_pia->ca1_w(ASSERT_LINE);
		m_stop_pia->ca1_w(CLEAR_LINE);
	}
	return m_banked_space->read_byte(offset);
}

void mekd4_state::main_w(offs_t offset, uint8_t data)
{
	if (offset == m_stop_address && !machine().side_effects_disabled())
	{
		m_stop_pia->ca1_w(CLEAR_LINE);
		m_stop_pia->ca1_w(ASSERT_LINE);
		m_stop_pia->ca1_w(CLEAR_LINE);
	}
	m_banked_space->write_byte(offset, data);
}

uint8_t mekd4_state::config_r()
{
	return 0xf0 | m_jumper1->read();
}

// The design reversed the A0 and A1 lines so that
// a 16 bit write could write both data addresses.
uint8_t mekd4_state::stop_pia_r(offs_t offset)
{
	// Reverse the A0 and A1 address lines;
	int8_t reversed = BIT(offset, 0) << 1 | BIT(offset, 1);
	return m_stop_pia->read(reversed);
}

void mekd4_state::stop_pia_w(offs_t offset, uint8_t data)
{
	// Reverse the A0 and A1 address lines;
	int8_t reversed = BIT(offset, 0) << 1 | BIT(offset, 1);
	m_stop_pia->write(reversed, data);
}

void mekd4_state::stop_pia_pa_w(uint8_t data)
{
	m_stop_address = (m_stop_address & 0x00ff) | (data << 8);
}

void mekd4_state::stop_pia_pb_w(uint8_t data)
{
	m_stop_address = (m_stop_address & 0xff00) | data;
}

/***********************************************************

 RAM and ROM paging

************************************************************/

void mekd4_state::page_w(uint8_t data)
{
	m_rom_page = data & 0x07;
	m_ram_page = (data >> 4) & 0x07;
}

/***********************************************************

    Keypad

************************************************************/

void mekd4_state::reset_key_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? CLEAR_LINE : ASSERT_LINE);

	// TODO reset other devices.
}

bool mekd4_state::keypad_key_pressed()
{
	return (m_keypad_columns[0]->read() & m_digit) ||
		(m_keypad_columns[1]->read() & m_digit) ||
		(m_keypad_columns[2]->read() & m_digit) ||
		(m_keypad_columns[3]->read() & m_digit);
}

INPUT_CHANGED_MEMBER(mekd4_state::keypad_changed)
{
	m_kpd_pia->cb1_w(mekd4_state::keypad_key_pressed());
}

int mekd4_state::keypad_cb1_r()
{
	return mekd4_state::keypad_key_pressed();
}

uint8_t mekd4_state::keypad_key_r()
{
	uint8_t mux = (m_digit & 0xc0) >> 6;
	uint8_t i = (m_keypad_columns[mux]->read() & m_digit) ? 0 : 0x80;

	return i | m_segment;
}

/***********************************************************

    Seven segment LED display

************************************************************/

// PA
void mekd4_state::led_segment_w(uint8_t data)
{
	m_segment = data & 0x7f;
	m_display->matrix(m_digit, ~m_segment);
}

// PB
void mekd4_state::led_digit_w(uint8_t data)
{
	m_digit = data;
	m_display->matrix(m_digit, ~m_segment);
	// Update the keypad pressed output which depends on m_digit.
	m_kpd_pia->cb1_w(mekd4_state::keypad_key_pressed());
}


/***********************************************************

  Cassette

************************************************************/

int mekd4_state::stop_pia_cb1_r()
{
	uint8_t state = m_cass->input() > +0.0;
	return state;
}

void mekd4_state::stop_pia_cb2_w(int state)
{
	m_cass->output(state ? -1.0 : +1.0);
}

/***********************************************************

  ACIA

************************************************************/

void mekd4_state::rs232_route_cts(int state)
{
	if (m_rs232_cts_route->read())
		m_acia->write_cts(state);

	// Cache the state, in case the ioport setting changes.
	m_cts = state;
}

void mekd4_state::rs232_route_dcd(int state)
{
	if (m_rs232_dcd_route->read())
		m_acia->write_dcd(state);

	// Cache the state, in case the ioport setting changes.
	m_dcd = state;
}

INPUT_CHANGED_MEMBER(mekd4_state::rs232_cts_route_change)
{
	if (newval)
		m_acia->write_cts(m_cts);
	else
		m_acia->write_cts(0);
}

INPUT_CHANGED_MEMBER(mekd4_state::rs232_dcd_route_change)
{
	if (newval)
		m_acia->write_dcd(m_dcd);
	else
		m_acia->write_dcd(0);
}

void mekd4_state::write_f1_clock(int state)
{
	if (BIT(m_rs232_tx_baud->read(), 0))
		m_acia->write_txc(state);
	if (BIT(m_rs232_rx_baud->read(), 0))
		m_acia->write_rxc(state);
}

void mekd4_state::write_f3_clock(int state)
{
	if (BIT(m_rs232_tx_baud->read(), 1))
		m_acia->write_txc(state);
	if (BIT(m_rs232_rx_baud->read(), 1))
		m_acia->write_rxc(state);
}

void mekd4_state::write_f7_clock(int state)
{
	if (BIT(m_rs232_tx_baud->read(), 2))
		m_acia->write_txc(state);
	if (BIT(m_rs232_rx_baud->read(), 2))
		m_acia->write_rxc(state);
}

void mekd4_state::write_f8_clock(int state)
{
	if (BIT(m_rs232_tx_baud->read(), 3))
		m_acia->write_txc(state);
	if (BIT(m_rs232_rx_baud->read(), 3))
		m_acia->write_rxc(state);
}

void mekd4_state::write_f9_clock(int state)
{
	if (BIT(m_rs232_tx_baud->read(), 4))
		m_acia->write_txc(state);
	if (BIT(m_rs232_rx_baud->read(), 4))
		m_acia->write_rxc(state);
}

void mekd4_state::write_f13_clock(int state)
{
	if (BIT(m_rs232_tx_baud->read(), 5))
		m_acia->write_txc(state);
	if (BIT(m_rs232_rx_baud->read(), 5))
		m_acia->write_rxc(state);
}

/***********************************************************

  MEK68R2

  This might in future be moved to a slot device, on a bus.

************************************************************/

// Delivery of keyboard inputs to the MEK68R2 keyboard is disabled when on
// views with the RS232 terminal, assuming that this keyboard is not present.
// Also disable delivery when the 'MEK68R2 present' jumper indicates it is
// disabled, assuming that this keyboard is not present.
void mekd4_state::kbd_put(uint8_t data)
{
	uint8_t view = machine().render().first_target()->view();
	if (view == 0)
		return;

	if (BIT(m_jumper1->read(), 1))
		return;

	m_term_data = data;
	// Triggers on the falling edge.
	m_r2_pia->ca1_w(ASSERT_LINE);
	m_r2_pia->ca1_w(CLEAR_LINE);
	m_r2_pia->ca1_w(ASSERT_LINE);
}

// PA0 to PA6 - Keyboard data.
// PA7 - Display nationality, 0 USA, 1 Europe.
uint8_t mekd4_state::r2_pia_pa_r()
{
	uint8_t ret = m_term_data;
	int8_t display_nationality = m_r2_display_nationality->read();
	m_term_data = 0;
	return ret | (display_nationality << 7);
}

// PB0 - Mode: 0 normal, 1 dumb terminal.
// PB1,2,3 - N/C
// PB4 - User defined
// PB5 - Light pen control.
// PB7, PB6 - Display format.
//       00 - 16 lines of 32 characters.
//       01 - 16 lines of 64 characters.
//       10 - 20 lines of 80 characters.
//       11 - User defined.
uint8_t mekd4_state::r2_pia_pb_r()
{
	int8_t display_format = m_r2_display_format->read();
	int8_t mode = m_r2_mode->read();
	return (display_format << 6) | mode;
}

MC6845_UPDATE_ROW(mekd4_state::update_row)
{
	const pen_t *pen = m_palette->pens();

	int x = 0;

	for (int column = 0; column < x_count; column++)
	{
		uint8_t code = m_video_ram[(ma + column) & 0xfff];
		int dcursor = (column == cursor_x);

		if (BIT(code, 7)) {
			/* Lores 6 pixel character.
			     -----------
			     | D1 | D0 |
			     | D3 | D2 |
			     | D5 | D4 |
			     -----------
			     D6 - 1 Grey tone, 0 brightness.
			*/
			int pixel = ((ra & 0x0c) >> 1) + 1;
			int dout = BIT(code, pixel);
			int grey = BIT(code, 6);
			int color = ((dcursor ^ dout) && de) << (grey ^ 1);
			bitmap.pix(y, x++) = pen[color];
			bitmap.pix(y, x++) = pen[color];
			bitmap.pix(y, x++) = pen[color];
			bitmap.pix(y, x++) = pen[color];
			pixel--;
			dout = BIT(code, pixel);
			color = ((dcursor ^ dout) && de) << (grey ^ 1);
			bitmap.pix(y, x++) = pen[color];
			bitmap.pix(y, x++) = pen[color];
			bitmap.pix(y, x++) = pen[color];
			bitmap.pix(y, x++) = pen[color];
		} else {
			offs_t address = ra < 8 ? ((code & 0x7f) << 3) | (ra & 0x07) : 0;
			uint8_t data = m_p_chargen[address];

			for (int bit = 0; bit < 8; bit++)
			{
				int dout = BIT(data, 7);
				int color = ((dcursor ^ dout) && de) << 1;

				bitmap.pix(y, x++) = pen[color];

				data <<= 1;
			}
		}
	}
}


/***********************************************************

************************************************************/

void mekd4_state::init_mekd4()
{
}

void mekd4_state::machine_start()
{
	m_banked_space = &subdevice<address_map_bank_device>("bankdev")->space(AS_PROGRAM);

	save_item(NAME(m_stop_address));
	save_item(NAME(m_rom_page));
	save_item(NAME(m_ram_page));
	save_item(NAME(m_segment));
	save_item(NAME(m_digit));
	save_item(NAME(m_cts));
	save_item(NAME(m_dcd));
	save_item(NAME(m_term_data));
}

void mekd4_state::machine_reset()
{
	m_rom_page = 0;
	m_ram_page = 0;
	m_stop_address = 0x0000;

	// Avoid triggering an early interrupt when CB1 lowered. The mc6821
	// driver resets CB1 high and to trigger on a high to low
	// transition. The mekd4 programs CB1 to trigger on a low to high
	// transition and configuring this earlier here is adequate.
	m_kpd_pia->write(1, 2);
	m_kpd_pia->cb2_w(ASSERT_LINE);  // Pulled high.

	m_brg->rsa_w(CLEAR_LINE);
	m_brg->rsb_w(ASSERT_LINE);

	// Write low here if jumpered low.
	if (!m_rs232_cts_route->read())
		m_acia->write_cts(0);
	if (!m_rs232_dcd_route->read())
		m_acia->write_dcd(0);

	// MEK68R2
	m_r2_pia->ca1_w(ASSERT_LINE);
	m_r2_pia->ca2_w(ASSERT_LINE);
	m_r2_pia->cb1_w(0);
	m_r2_pia->cb2_w(0);
}

/***********************************************************

    Machine

************************************************************/

static DEVICE_INPUT_DEFAULTS_START(terminal)
	DEVICE_INPUT_DEFAULTS("RS232_RXBAUD", 0xff, RS232_BAUD_9600)
	DEVICE_INPUT_DEFAULTS("RS232_TXBAUD", 0xff, RS232_BAUD_9600)
	DEVICE_INPUT_DEFAULTS("RS232_DATABITS", 0xff, RS232_DATABITS_8)
	DEVICE_INPUT_DEFAULTS("RS232_PARITY", 0xff, RS232_PARITY_NONE)
	DEVICE_INPUT_DEFAULTS("RS232_STOPBITS", 0xff, RS232_STOPBITS_1)
DEVICE_INPUT_DEFAULTS_END

void mekd4_state::mekd4(machine_config &config)
{
	MC6809(config, m_maincpu, 3.579545_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mekd4_state::mekd4_stop_mem);

	ADDRESS_MAP_BANK(config, m_bankdev, 0);
	m_bankdev->set_endianness(ENDIANNESS_BIG);
	m_bankdev->set_data_width(8);
	m_bankdev->set_addr_width(20);
	m_bankdev->set_addrmap(AS_PROGRAM, &mekd4_state::mekd4_mem);

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, M6809_IRQ_LINE);
	INPUT_MERGER_ANY_HIGH(config, "mainnmi").output_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);

	/* LED display */
	PWM_DISPLAY(config, m_display).set_size(8, 7);
	m_display->set_segmask(0xff, 0x7f);

	config.set_default_layout(layout_mekd4);

	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	// IRQ is not connected. RTS, CTS, and DCD are available.
	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_acia->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	MC14411(config, m_brg, XTAL(1'843'200));
	m_brg->out_f<1>().set(FUNC(mekd4_state::write_f1_clock));
	m_brg->out_f<3>().set(FUNC(mekd4_state::write_f3_clock));
	m_brg->out_f<7>().set(FUNC(mekd4_state::write_f7_clock));
	m_brg->out_f<8>().set(FUNC(mekd4_state::write_f8_clock));
	m_brg->out_f<9>().set(FUNC(mekd4_state::write_f9_clock));
	m_brg->out_f<13>().set(FUNC(mekd4_state::write_f13_clock));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_acia, FUNC(acia6850_device::write_rxd));
	rs232.cts_handler().set(FUNC(mekd4_state::rs232_route_cts));
	rs232.dcd_handler().set(FUNC(mekd4_state::rs232_route_dcd));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	// Stop PIA. IRQB is NC.
	PIA6821(config, m_stop_pia);
	m_stop_pia->writepa_handler().set(FUNC(mekd4_state::stop_pia_pa_w));
	m_stop_pia->writepb_handler().set(FUNC(mekd4_state::stop_pia_pb_w));
	m_stop_pia->ca2_w(1); // Connected to 'abort' TP2. Can be toggled low to and abort user code.
	m_stop_pia->readcb1_handler().set(FUNC(mekd4_state::stop_pia_cb1_r));
	m_stop_pia->cb2_handler().set(FUNC(mekd4_state::stop_pia_cb2_w));
	m_stop_pia->irqa_handler().set("mainnmi", FUNC(input_merger_device::in_w<0>));

	// Keypad and display PIA. CA1, CA2, IRQA are NC. CB2 is pulled high.
	PIA6821(config, m_kpd_pia);
	m_kpd_pia->readpa_handler().set(FUNC(mekd4_state::keypad_key_r));
	m_kpd_pia->readcb1_handler().set(FUNC(mekd4_state::keypad_cb1_r));
	m_kpd_pia->writepa_handler().set(FUNC(mekd4_state::led_segment_w));
	m_kpd_pia->writepb_handler().set(FUNC(mekd4_state::led_digit_w));
	m_kpd_pia->irqb_handler().set("mainnmi", FUNC(input_merger_device::in_w<1>));

	// Keypad and display board User PIA.
	PIA6821(config, m_user_pia);
	m_user_pia->irqa_handler().set("mainirq", FUNC(input_merger_device::in_w<0>));
	m_user_pia->irqb_handler().set("mainirq", FUNC(input_merger_device::in_w<1>));

	// MEK68R2

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);

	m_screen->set_refresh_hz(50);
	m_screen->set_size(80 * 8 + 80 * 10, 20 * 12 + 100);
	m_screen->set_visarea(0, 80 * 8 + 80 * 10 - 1, 0, 20 * 12 + 100 - 1);
	m_screen->set_screen_update("mc6845", FUNC(mc6845_device::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	MC6845(config, m_mc6845, XTAL(14'318'181)/8);
	m_mc6845->set_screen(m_screen);
	m_mc6845->set_show_border_area(false);
	m_mc6845->set_char_width(8);
	m_mc6845->set_update_row_callback(FUNC(mekd4_state::update_row));
	m_mc6845->out_hsync_callback().set(m_r2_pia, FUNC(pia6821_device::cb2_w));
	m_mc6845->out_vsync_callback().set(m_r2_pia, FUNC(pia6821_device::cb1_w));

	// PA is the keyboard data and a mode flag.
	// CA1 is keyboard strobe.
	// CA2 light pen input.
	// PB0 is mode flags and light pen control.
	// CB1 is VSYNC, and CB2 is HSYNC.
	PIA6821(config, m_r2_pia);
	m_r2_pia->readpa_handler().set(FUNC(mekd4_state::r2_pia_pa_r));
	m_r2_pia->readpb_handler().set(FUNC(mekd4_state::r2_pia_pb_r));
	m_r2_pia->irqa_handler().set("mainirq", FUNC(input_merger_device::in_w<2>));
	m_r2_pia->irqb_handler().set("mainirq", FUNC(input_merger_device::in_w<3>));

	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(mekd4_state::kbd_put));
}

/***********************************************************

    ROMS

************************************************************/

ROM_START(mekd4)
	ROM_REGION(0x10000,"bankdev",0)
	ROM_LOAD("d4bugr2.rom", 0xe800, 0x0800, CRC(0b80a67d) SHA1(20d980767a7a667fe0f8e377bb2c29e297e6c635))
	ROM_LOAD("d4bugkpd.rom", 0xf000, 0x1000, CRC(1fdf414a) SHA1(3c8883a6ee0ae89398d9be5a5843db4c3b20f7fd))
	ROM_REGION(0x0400, "chargen",0)
	ROM_LOAD("mcm6674p.chr", 0x0000, 0x0400, CRC(1c22088a) SHA1(b5f0bd0cfdec0cd5c1cb764506bef3c17d6af0eb))
	ROM_REGION(0x0400, "rommap",0)
	ROM_LOAD("d4map00.rom", 0x0000, 0x0400, CRC(7e676444) SHA1(4f8a7443da509561be958786f9bd72eac3969a89))
ROM_END

} // anonymous namespace


/***************************************************************************

  Game driver(s)

***************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE   INPUT  CLASS        INIT        COMPANY     FULLNAME      FLAGS
COMP(1980, mekd4,  0,      0,      mekd4,    mekd4, mekd4_state, init_mekd4, "Motorola", "MEK6802D4" , MACHINE_NO_SOUND)



mekd5.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: 68bit
/******************************************************************************

Motorola Evaluation Kit 6802 D5 - MEK6802D5

Memory map

Range    Short  Description

0000-dfff RAM   Either 128 bytes on board, or external

e000-e3ff RAM   Static RAM, 1K.
e400-e47f RAM   System RAM
e480-e483 PIA   User PIA
e484-e487 PIA   System PIA

e700-e701 ACIA  System ACIA.

e800-efff ROM   Optional user ROM
f000-f7ff ROM   D5BUG monitor ROM
f800-ffff ROM   D5BUG (mirror), or optional user ROM.


A 1K or 2K optional user ROM or EPROM can be installed and mapped to either
0xe800-0xefff, or to 0xf800-0xffff, set via jumper 3.
TODO implement this user ROM.

The board has provision for an ACIA, and the documentation mentions that it is
not used when there is a keypad, and the keypad is removable. However the
D5BUG monitor has no support for this ACIA. Was there an alternative official
monitor that used this ACIA?


Keypad commands:

RS (Reset)  Reset, wired to the CPU reset line
EX (Escape) Typically aborts user program.
M (Memory display/change)
  Digits 5 and 6 show the actual data at the address.
  G  - increase the address.
  M  - decreases the address.
  FS - Offset calculation. Enter address, then press 'GO'.
    GO - stores the offset and returns to memory display and increased the address.
    FC - return to memory display, without storing the offset.
    M  - return to memory display, after a BAD offset.
  EX - exits memory display.
RD (Register display/alter)
  G   - advance to next register.
  M   - previous register.
  T/B - trace a single instruction
  EX  - exits register display.
GO to user program.
  If no address if entered then it uses the pseudo PC, it continues.
  Enter the address and press 'Go' to use that entered address.
  It firstly checks that there is RAM at the stack pointer.
FS T/B - Breakpoint editor
  GO - advance to next breakpoing, up to 8, then loops.
  FS - insert a breakpoint
  FC - deactivate breakpoint
  EX - exits breakpoing editor.
P/L (Punch tape)
  At the 'bb' prompt enter the beginning address of the data, then 'GO'.
  At the 'EE' prompt enter the last address of the data.
  Start the tape and press GO. There is a 30 second leader of $ff.
FS P/L (Load from tape)
FS RD (Verify from tape)
FS 0 to F
  One of 16 user defined functions. Press FS then one number key 0 to F.
  A pointer to a table of 16 function addresses should be set at 0xe43f.

******************************************************************************/

#include "emu.h"
#include "cpu/m6800/m6800.h"
#include "machine/input_merger.h"
#include "machine/6821pia.h"
#include "machine/6850acia.h"
#include "machine/mc14411.h"
#include "machine/clock.h"
#include "machine/timer.h"
#include "video/pwm.h"
#include "speaker.h"
#include "bus/rs232/rs232.h"
#include "machine/terminal.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "render.h"
#include "mekd5.lh"


namespace {

#define XTAL_MEKD5 3.579545_MHz_XTAL

class mekd5_state : public driver_device
{
public:
	mekd5_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_kpd_pia(*this, "kpd_pia")
		, m_user_pia(*this, "user_pia")
		, m_display(*this, "display")
		, m_brg(*this, "brg")
		, m_baud_rate(*this, "BAUD_RATE")
		, m_acia(*this, "acia")
		, m_cass(*this, "cassette")
		, m_keypad_columns(*this, "COL%u", 0)
	{ }

	void mekd5(machine_config &config);

	void reset_key_w(int state);
	DECLARE_INPUT_CHANGED_MEMBER(keypad_changed);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void trace_timer_clear_w(int state);

	int keypad_cb1_r();
	uint8_t keypad_key_r();
	void led_digit_w(uint8_t data);
	void led_segment_w(uint8_t data);
	int kansas_r();

	// Clocks
	void write_f1_clock(int state);
	void write_f3_clock(int state);
	void write_f5_clock(int state);
	void write_f7_clock(int state);
	void write_f9_clock(int state);
	void write_f13_clock(int state);

	void mekd5_mem(address_map &map) ATTR_COLD;

	bool keypad_key_pressed();

	TIMER_CALLBACK_MEMBER(trace_tick);

	emu_timer *m_trace_timer = nullptr;
	uint8_t m_segment;
	uint8_t m_digit;

	required_device<m6802_cpu_device> m_maincpu;
	required_device<pia6821_device> m_kpd_pia;
	required_device<pia6821_device> m_user_pia;
	required_device<pwm_display_device> m_display;
	required_device<mc14411_device> m_brg;
	required_ioport m_baud_rate;
	required_device<acia6850_device> m_acia;
	required_device<cassette_image_device> m_cass;
	required_ioport_array<4> m_keypad_columns;
};



/***********************************************************

    Address Map

************************************************************/

void mekd5_state::mekd5_mem(address_map &map)
{
	map(0x0000, 0xdfff).ram();
	map(0xe000, 0xe3ff).ram();
	map(0xe400, 0xe47f).ram();

	map(0xe480, 0xe483).mirror(0x0378).rw(m_user_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xe484, 0xe487).mirror(0x0378).rw(m_kpd_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));

	map(0xe700, 0xe701).mirror(0x003e).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write));

	/* D5BUG ROM */
	map(0xf000, 0xf7ff).rom().mirror(0x0800);
}

/***********************************************************

    Keys

************************************************************/

static INPUT_PORTS_START(mekd5)

	// RESET is not wired to the key matrix.
	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("RS") PORT_WRITE_LINE_MEMBER(FUNC(mekd5_state::reset_key_w))

	PORT_START("COL0")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("M") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("FS") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("7") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("0") PORT_CODE(KEYCODE_0)

	PORT_START("COL1")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("EX") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("FC") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("8") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("F") PORT_CODE(KEYCODE_F)

	PORT_START("COL2")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("RD") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("P/L") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("9") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("E") PORT_CODE(KEYCODE_E)

	PORT_START("COL3")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("GO") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("T/B") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("A") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("B") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("C") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mekd5_state::keypad_changed), 0) PORT_NAME("D") PORT_CODE(KEYCODE_D)

	/* RS232 baud rates available via J5. */
	PORT_START("BAUD_RATE")
	PORT_CONFNAME(0x3f, 1, "RS232 Baud Rate")
	PORT_CONFSETTING(0x20, "110")
	PORT_CONFSETTING(0x10, "300")
	PORT_CONFSETTING(0x08, "1200")
	PORT_CONFSETTING(0x04, "2400")
	PORT_CONFSETTING(0x02, "4800")
	PORT_CONFSETTING(0x01, "9600")

INPUT_PORTS_END

/***********************************************************

    Trace timer

************************************************************/

TIMER_CALLBACK_MEMBER(mekd5_state::trace_tick)
{
	// CB2 is programmed to trigger on the falling edge, so after
	// a count of 16. CB2 input comes from a counter, so the duty
	// cycle should be 50/50, but it makes no difference to rise
	// and fall here.
	m_kpd_pia->cb2_w(1);
	m_kpd_pia->cb2_w(0);
}


// Expect a delay of 16 cycles.  However the 6800 cycle model appears to
// account for the store that writes here as occuring at the start of that
// instruction adding 5 cycles to give an effective 21 cycles. TODO adjust
// this back to 16 cycles when the 6800 cycle timing becomes more accurate.
void mekd5_state::trace_timer_clear_w(int state)
{
	if (state)
		m_kpd_pia->cb2_w(0);
	else
		m_trace_timer->adjust(attotime::from_ticks(21, XTAL_MEKD5 / 4));
}

/***********************************************************

    Keypad

************************************************************/

// Keypad input is disable on views with the RS232 input.

void mekd5_state::reset_key_w(int state)
{
	uint8_t view = machine().render().first_target()->view();
	if (view > 1) return;

	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? CLEAR_LINE : ASSERT_LINE);

	// TODO reset other devices.
}


bool mekd5_state::keypad_key_pressed()
{
	uint8_t view = machine().render().first_target()->view();
	if (view > 1) return 0;

	return (m_keypad_columns[0]->read() & m_digit) ||
		(m_keypad_columns[1]->read() & m_digit) ||
		(m_keypad_columns[2]->read() & m_digit) ||
		(m_keypad_columns[3]->read() & m_digit);
}

INPUT_CHANGED_MEMBER(mekd5_state::keypad_changed)
{
	m_kpd_pia->cb1_w(mekd5_state::keypad_key_pressed());
}

int mekd5_state::keypad_cb1_r()
{
	return mekd5_state::keypad_key_pressed();
}

uint8_t mekd5_state::keypad_key_r()
{
	uint8_t view = machine().render().first_target()->view();
	if (view > 1) return m_segment;

	uint8_t mux = (m_digit & 0xc0) >> 6;
	uint8_t i = (m_keypad_columns[mux]->read() & m_digit) ? 0 : 0x80;

	return i | m_segment;
}

/***********************************************************

    Seven segment LED display, and cassette

************************************************************/

// PA
void mekd5_state::led_segment_w(uint8_t data)
{
	m_segment = data & 0x7f;
	m_display->matrix(m_digit & 0x3f, ~m_segment);
}

// PB
void mekd5_state::led_digit_w(uint8_t data)
{
	m_digit = data;
	m_display->matrix(m_digit & 0x3f, ~m_segment);
	// PB7 also drives the cassette output.
	m_cass->output(BIT(data, 7) ? -1.0 : +1.0);
	// Update the keypad pressed output which depends on m_digit.
	m_kpd_pia->cb1_w(mekd5_state::keypad_key_pressed());
}

int mekd5_state::kansas_r()
{
	uint8_t data = m_cass->input() > +0.0;
	return data;
}


/***********************************************************

  ACIA clocks

************************************************************/

void mekd5_state::write_f1_clock(int state)
{
	if (BIT(m_baud_rate->read(), 0))
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}

void mekd5_state::write_f3_clock(int state)
{
	if (BIT(m_baud_rate->read(), 1))
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}

void mekd5_state::write_f5_clock(int state)
{
	if (BIT(m_baud_rate->read(), 2))
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}

void mekd5_state::write_f7_clock(int state)
{
	if (BIT(m_baud_rate->read(), 3))
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}

void mekd5_state::write_f9_clock(int state)
{
	if (BIT(m_baud_rate->read(), 4))
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}

void mekd5_state::write_f13_clock(int state)
{
	if (BIT(m_baud_rate->read(), 5))
	{
		m_acia->write_txc(state);
		m_acia->write_rxc(state);
	}
}


/***********************************************************

************************************************************/

void mekd5_state::machine_start()
{
	save_item(NAME(m_segment));
	save_item(NAME(m_digit));

	m_trace_timer = timer_alloc(FUNC(mekd5_state::trace_tick), this);
}

void mekd5_state::machine_reset()
{
	// Trace timer out low.
	m_kpd_pia->cb2_w(0);

	m_brg->rsa_w(CLEAR_LINE);
	m_brg->rsb_w(ASSERT_LINE);

	// /DCD and /CTS are wired low.
	m_acia->write_dcd(CLEAR_LINE);
	m_acia->write_cts(CLEAR_LINE);
}

/***********************************************************

    Machine

************************************************************/

static DEVICE_INPUT_DEFAULTS_START(terminal)
	DEVICE_INPUT_DEFAULTS("RS232_RXBAUD", 0xff, RS232_BAUD_9600)
	DEVICE_INPUT_DEFAULTS("RS232_TXBAUD", 0xff, RS232_BAUD_9600)
	DEVICE_INPUT_DEFAULTS("RS232_DATABITS", 0xff, RS232_DATABITS_8)
	DEVICE_INPUT_DEFAULTS("RS232_PARITY", 0xff, RS232_PARITY_NONE)
	DEVICE_INPUT_DEFAULTS("RS232_STOPBITS", 0xff, RS232_STOPBITS_1)
DEVICE_INPUT_DEFAULTS_END

void mekd5_state::mekd5(machine_config &config)
{
	M6802(config, m_maincpu, XTAL_MEKD5);        /* 894.8 kHz clock */
	m_maincpu->set_ram_enable(false);
	m_maincpu->set_addrmap(AS_PROGRAM, &mekd5_state::mekd5_mem);

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, M6802_IRQ_LINE);
	INPUT_MERGER_ANY_HIGH(config, "mainnmi").output_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);

	// LED display
	PWM_DISPLAY(config, m_display).set_size(6, 7);
	m_display->set_segmask(0x3f, 0x7f);

	config.set_default_layout(layout_mekd5);

	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	// Keypad and display PIA (U23). IRQA is NC. CB2 is trace timer input.
	PIA6821(config, m_kpd_pia);
	m_kpd_pia->readpa_handler().set(FUNC(mekd5_state::keypad_key_r));
	m_kpd_pia->writepa_handler().set(FUNC(mekd5_state::led_segment_w));
	m_kpd_pia->writepb_handler().set(FUNC(mekd5_state::led_digit_w));
	m_kpd_pia->readca1_handler().set(FUNC(mekd5_state::kansas_r));
	m_kpd_pia->ca2_handler().set(FUNC(mekd5_state::trace_timer_clear_w));
	m_kpd_pia->readcb1_handler().set(FUNC(mekd5_state::keypad_cb1_r));
	m_kpd_pia->irqb_handler().set("mainnmi", FUNC(input_merger_device::in_w<1>));

	// User PIA (U9).
	// IRQA and IRQB can be independently jumpered to IRQ or NMI via J1.
	// All the I/O lines are available at the User I/O connector.
	PIA6821(config, m_user_pia);

	// IRQ is NC. RX and TX clk are wired together. RTS is available.
	// /DCD and /CTS and wired low.
	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));

	MC14411(config, m_brg, XTAL(1'843'200));
	m_brg->out_f<1>().set(FUNC(mekd5_state::write_f1_clock));
	m_brg->out_f<3>().set(FUNC(mekd5_state::write_f3_clock));
	m_brg->out_f<5>().set(FUNC(mekd5_state::write_f5_clock));
	m_brg->out_f<7>().set(FUNC(mekd5_state::write_f7_clock));
	m_brg->out_f<9>().set(FUNC(mekd5_state::write_f9_clock));
	m_brg->out_f<13>().set(FUNC(mekd5_state::write_f13_clock));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_acia, FUNC(acia6850_device::write_rxd));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));
}

/***********************************************************

    ROMS

************************************************************/

ROM_START(mekd5)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("d5bug.rom", 0xf000, 0x0800, CRC(67c00a2c) SHA1(ae321dbca0baf4b67d62bfec77266d9132b973bf))
ROM_END

} // anonymous namespace


/***************************************************************************

  Game driver(s)

***************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE   INPUT  CLASS        INIT        COMPANY     FULLNAME      FLAGS
COMP( 1980, mekd5,  0,      0,      mekd5,    mekd5, mekd5_state, empty_init, "Motorola", "MEK6802D5" , MACHINE_NO_SOUND )



memorymoog.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:m1macrophage

/*
The memorymoog is a CPU-controlled, 6-voice, polyphonic analog synthesizer.

The architecture is typical of polyphonic analog synthesizers of the time. The
firmware:
* Scans the keyboard and buttons.
* Scans the value of potentiometers. This synthesizer lacks an ADC, so the
  conversion is done by outputing a voltage to the DAC, and comparing that
  to the voltage generated by the pot, using successive approximation (binary
  search).
* Sets control voltages.
* Routes audio and modulation by controlling analog switches.
* Controls LEDs, displays and cassette I/O.

Each voice receives its own pitch and triggers, but all other parameters are
shared across voices. The modulation sources (envelope generators, LFOs) are
analog.

This driver is based on the memorymoog service manual and schematics. It is
intended as an educational tool. Even though it is marked as a skeleton,
emulation of the digital and digital-analog interface is pretty far along.
TODOs throughout the code call out the missing pieces. There is no attempt to
emulate the analog audio circuits.

There is no layout yet. You can run mame with `-output console`, in order to
observe internal stage changes.

The strings used in output and port names, enums, read and write handlers, etc.,
reflect those in the schematics.

PCBoards:
1 - 6 voice boards (1A-1F)
2 - Common Analog
3 - Contour & Glide
4 - Digital
5 - DMUX
6 - Right-side control (RSC)
7 - Left-side control (LSC)
8 - Display
9 - Jack interface
10 - Lehd-hand control (LHC)
11 - AUX1
12 - AUX2
13 - Power Supply

The boards most pertinent to this driver are 4 and 5 (digital logic and
digital-to-analog conversion) and 6, 7, 8 and 10 (user interface). But other
boards are ocassionally referenced as well.

TODO:
- Interactive layout.
- Cassette input/output.
- Enough analog emulation for autotune to work.
*/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/nvram.h"
#include "machine/output_latch.h"
#include "machine/z80ctc.h"
#include "machine/z80daisy.h"
#include "video/pwm.h"

#define LOG_KEYPRESS (1U << 1)
#define LOG_CV       (1U << 2)
#define LOG_ADC      (1U << 3)

#define VERBOSE (LOG_GENERAL | LOG_CV)
//#define LOG_OUTPUT_FUNC osd_printf_info

#include "logmacro.h"

namespace {

constexpr const char MAINCPU_TAG[] = "z80";
constexpr const char CTC_TAG[] = "ctc";
constexpr const char NVRAM_TAG[] = "nvram";

class memorymoog_state : public driver_device
{
public:
	memorymoog_state(const machine_config &mconfig, device_type type, const char *tag) ATTR_COLD
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, MAINCPU_TAG)
		, m_ctc(*this, CTC_TAG)
		, m_keyboard_io(*this, "keyboard_column_%d", 0U)
		, m_switch_a_io(*this, "switch_a_row_%d", 0U)
		, m_switch_b_io(*this, "switch_b_row_%d", 0U)
		, m_pot_io(*this, "pot_%d", 0U)
		, m_octave_io(*this, "octave_buttons")
		, m_octave_minus_1_led(*this, "oct_m1")
		, m_octave_0_led(*this, "oct_0")
		, m_digit_device(*this, "pwm_digit_device")
		, m_digits(*this, "digit_%d", 1U)
		, m_char_device(*this, "pwm_char_device")
		, m_chars(*this, "char_%d", 1U)
		, m_led_matrix_device(*this, "pwm_led_matrix_device")
		, m_leds(8)
		, m_cv(NUM_CVS, -1)
	{
		for (int i = 0; i < 8; ++i)
		{
			for (int j = 0; j < 4; ++j)
			{
				m_leds[i].push_back(
					output_finder<>(
						*this, std::string("led_") + BOARD_6_LED_NAMES[i][j]));
			}
			for (int j = 0; j < 4; ++j)
			{
				m_leds[i].push_back(
					output_finder<>(
						*this, std::string("led_") + BOARD_7_LED_NAMES[i][j]));
			}
		}
	}

	void memorymoog(machine_config &config) ATTR_COLD;

	DECLARE_INPUT_CHANGED_MEMBER(octave_button_pressed);

protected:
	void machine_start() override ATTR_COLD;

private:
	float get_cv() const;
	bool adc_comparator_on() const;

	u8 u26_low4_r();

	template<int N> u8 key_matrix_r(
		const required_ioport_array<N> &input, u8 input_mask, u8 selection,
		const char *name);
	u8 keyboard_r();
	u8 switches_a_r();
	u8 switches_b_r();

	void update_sh();
	void keyboard_w(u8 data);
	void switches_w(u8 data);
	void tape_relay_w(u8 data);
	void dac_low_w(u8 data);
	void cv_mux_control_w(offs_t offset, u8 data);
	void led_drive_w(u8 data);
	void led_latch_w(u8 data);
	void led_update_w(offs_t offset, u8 data);
	void digit_latch_w(u8 data);
	void digit_update_w(offs_t offset, u8 data);
	void char_latch_a_w(u8 data);
	void char_latch_b_w(u8 data);
	void char_update_w(offs_t offset, u8 data);

	void memory_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_device<z80ctc_device> m_ctc;
	required_ioport_array<8> m_keyboard_io;
	required_ioport_array<8> m_switch_a_io;
	required_ioport_array<6> m_switch_b_io;
	required_ioport_array<31> m_pot_io;

	u8 m_keyboard_columns = 0xff;  // U28 (board 4).
	u8 m_switch_rows = 0xff;  // U6 (board 7).
	u16 m_dac_latch = 0;  // 12-bit DAC. U2: 8 MSbits, U1: 4LSbits (board 5).
	s16 m_selected_sh = -1;  // S&H DMUX. 0-63, -1 for "none selected".
	s16 m_selected_pot = 0;  // Potentiomenter MUX. 0-30.
	bool m_positive_hysteresis = true;
	bool m_negative_hysteresis = false;

	required_ioport m_octave_io;
	output_finder<> m_octave_minus_1_led;  // LED 2, board 10.
	output_finder<> m_octave_0_led;  // LED 1, board 10.
	bool m_octave_low = false;

	// MAN6610. U3, board 7.
	required_device<pwm_display_device> m_digit_device;
	output_finder<2> m_digits;

	// LT-1604. U4, board 7.
	required_device<pwm_display_device> m_char_device;
	output_finder<8> m_chars;
	u16 m_char_led_source = 0x3fff;

	required_device<pwm_display_device> m_led_matrix_device;
	std::vector<std::vector<output_finder<>>> m_leds;

	std::vector<float> m_cv; // Control voltages. See CV_NAMES below.

	// When these strings get converted to output names, they will include
	// the "led_" prefix.
	static constexpr const char *BOARD_6_LED_NAMES[8][4] =
	{
		{"osc1_2'", "osc1_4'", "osc1_8'", "osc1_16'"},
		{"osc2_2'", "osc2_4'", "osc2_8'", "osc2_16'"},
		{"osc3_2'", "osc3_4'", "osc3_8'", "osc3_16'"},
		{"kybd_control", "sync_2to1", "NOT_CONNECTED_1", "low"},
		{"osc3_ramp", "osc3_pulse", "osc3_tri", "release"},
		{"osc2_ramp", "osc2_pulse", "osc2_tri", "uncond_cont"},
		{"osc1_ramp", "osc1_pulse", "osc1_tri", "return_to_zero"},
		{"2/3_kybd_trk", "NOT_CONNECTED_2", "1/3_kybd_trk", "kybd_follow"},
	};
	static constexpr const char *BOARD_7_LED_NAMES[8][4] =
	{
		{"saw_lfo", "osc2_freq_lfo", "hold", "kybd_mode"},
		{"tri_lfo", "osc1_freq_lfo", "kybd_out", "mono"},
		{"ramp_lfo", "osc3_freq_lfo", "arpeggiator", "glide"},
		{"square_lfo", "pw1_lfo", "NOT_CONNECTED_3", "mult_trig"},
		{"filter_lfo", "pw3_lfo", "fp2_osc2", "vm_freq1"},
		{"sh_lfo", "pw2_lfo", "fp1_filter", "fp2_mod"},
		{"osc3_amt", "fp1_pitch", "vm_pw2", "vm_filter"},
		{"invert", "fp1_volume", "vm_pw1", "vm_freq2"},
	};

	static constexpr const int NUM_CVS = 64;
	static constexpr const char *CV_NAMES[NUM_CVS] =
	{
		// U10
		"GLIDE",
		"GLIDE_MONO",
		"PITCH_BEND_AMT",
		"MOD_AMT",
		"FOOT_PEDAL_1_AMT",
		"FOOT_PEDAL_2_AMT",
		"MOD_RATE",
		"MOD_RATE_SEQ",

		// U11
		"VOICE_MOD_OSC3_AMT",
		"VOICE_MOD_FILT_ENV",
		"OSC_1_PW",
		"OSC_2_FREQ",
		"OSC_2_PW",
		"OSC_3_FREQ",
		"OSC_3_PW",
		"OSC_1_AMT",

		// U12
		"OSC_2_AMT",
		"OSC_3_AMT",
		"NOISE_AMT",
		"VCF_FREQ",
		"EMPHASIS",
		"VCF_CONTOUR_AMT",
		"VCF_ATTACK",
		"VCF_DECAY",

		// U13
		"VCF_SUSTAIN",
		"VCF_RELEASE",
		"LOUD_ATTACK",
		"LOUD_DECAY",
		"LOUD_SUSTAIN",
		"LOUD_RELEASE",
		"PROGRAMMABLE_VOL",
		"NOT_CONNECTED_A",

		// U14
		"OSC_1_OCT",
		"OSC_2_OCT",
		"OSC_3_OCT",
		"NOT_CONNECTED_B",
		"NOT_CONNECTED_C",
		"NOT_CONNECTED_D",
		"TRANSPOSE_SCALE",
		"MONO_KYBD_CV",

		// U15
		"RAW_KYBD_CV_A",
		"RAW_KYBD_CV_B",
		"RAW_KYBD_CV_C",
		"RAW_KYBD_CV_D",
		"RAW_KYBD_CV_E",
		"RAW_KYBD_CV_F",
		"AUTOTUNE_A1",
		"AUTOTUNE_A2",

		// U16
		"AUTOTUNE_A3",
		"AUTOTUNE_B1",
		"AUTOTUNE_B2",
		"AUTOTUNE_B3",
		"AUTOTUNE_C1",
		"AUTOTUNE_C2",
		"AUTOTUNE_C3",
		"AUTOTUNE_D1",

		// U17
		"AUTOTUNE_D2",
		"AUTOTUNE_D3",
		"AUTOTUNE_E1",
		"AUTOTUNE_E2",
		"AUTOTUNE_E3",
		"AUTOTUNE_F1",
		"AUTOTUNE_F2",
		"AUTOTUNE_F3",
	};

	static constexpr const float V_REF = 10;
};

float memorymoog_state::get_cv() const
{
	// According to the Technical Service Info manual, the DAC is calibrated to
	// output 10V (V_REF) when the upper 8 bits are all on and the lower 4 are
	// all off.
	return V_REF * m_dac_latch / 0xff0;
}

bool memorymoog_state::adc_comparator_on() const
{
	if (m_selected_pot == 31)
	{
		LOG("Firmware error: addressed unconnected mux input\n");
		return true;
	}

	const float pot_v = m_pot_io[m_selected_pot]->read() * V_REF / 100;

	// For details on how hysteresis is applied, see Technical Service Info on
	// A/D circuitry.
	// If both the positive and negative hysteresis circuits are enabled, the
	// total will work out to ~0.
	float hysteresis = 0;
	if (m_positive_hysteresis)
		hysteresis += 0.05;
	if (m_negative_hysteresis)
		hysteresis -= 0.05;

	const float v = pot_v + hysteresis;
	const float dac_v = get_cv();
	const bool comp_on = v > dac_v;
	if (m_selected_pot == 0)
	{
		LOGMASKED(LOG_ADC, "Comparator: %f %f %d %04x\n", v, dac_v, comp_on,
				  m_dac_latch);
	}
	return comp_on;
}

u8 memorymoog_state::u26_low4_r()
{
	// Component designations refer to Board 4 (digital), unless otherwise
	// noted.

	// The 2 MSbits of U26 are mapped to a different address. See keyboard_r().

	// D0 <- Comparator (LM393, U23B) <- tape in (labelled "TO TAPE", J16.
	// Maybe mixed up with "FROM TAPE"?). When not receiving tape input,
	// the comparator will be comparing 2.5V against 2.5V, with some hysteresis.
	// So it will settle to 0 or 1, arbitrarily, depending on the biasing
	// resistor (R41-R44) tolerances.
	// TODO: Emulate cassette.
	const u8 d0 = 1;

	// D1 <- Inverted by Q2 <- clock In (J11). J11 is is normalled to connection
	// "DMUX P517-4" <- Common Analog board S21-3 <- Square LFO.
	// D1 will either track the square LFO, or an external clock (if connected).
	// TODO: Emulate LFO.
	const u8 d1 = 1;

	// D2 <- Approximation Comparator. Will be 1 when the scanned pot voltage is
	// greater than the DAC output voltage.
	const u8 d2 = adc_comparator_on() ? 1 : 0;

	// D3 <- A connector labelled "N/C" (P47-2). Pulled high.
	const u8 d3 = 1;

	return 0xf0 | (d3 << 3) | (d2 << 2) | (d1 << 1) | d0;
}

template<int N> u8 memorymoog_state::key_matrix_r(
	const required_ioport_array<N> &input, u8 input_mask, u8 selection,
	const char *name)
{
	static_assert(N > 0 && N <= 8);

	u8 pressed = 0xff;
	for (int i = 0; i < N; ++i)
		if (!BIT(selection, i))  // `selection` is active-low.
			pressed &= input[i]->read();
	pressed |= ~input_mask;

	if (pressed != 0xff)
		LOGMASKED(LOG_KEYPRESS, "Pressed %s %02X: %02X\n", name, selection,
				  pressed);

	return pressed;  // Returned value is active-low.
}

u8 memorymoog_state::keyboard_r()
{
	return key_matrix_r<8>(m_keyboard_io, 0xff, m_keyboard_columns, "Keyboard");
}

u8 memorymoog_state::switches_a_r()
{
	return key_matrix_r<8>(m_switch_a_io, 0x3f, m_switch_rows, "Switches A");
}

u8 memorymoog_state::switches_b_r()
{
	return key_matrix_r<6>(m_switch_b_io, 0x3f, m_switch_rows, "Switches B");
}

void memorymoog_state::update_sh()
{
	if (m_selected_sh < 0)
		return;

	const float cv = get_cv();
	if (m_cv[m_selected_sh] == cv)
		return;

	m_cv[m_selected_sh] = cv;
	// TODO: all autotune CVs are divided by a 115K-10K divider.
	LOGMASKED(LOG_CV, "CV: %02d %-20s %04X - %f\n", m_selected_sh,
			  CV_NAMES[m_selected_sh], m_dac_latch, cv);
}

void memorymoog_state::keyboard_w(u8 data)
{
	m_keyboard_columns = data;
	// TODO: D0 also connected to tape out J14 (labeled "FROM TAPE". Should it
	// be "TO TAPE"?). See u26_low4_r().
}

void memorymoog_state::switches_w(u8 data)
{
	m_switch_rows = data;
}

void memorymoog_state::tape_relay_w(u8 data)
{
	// U33, D0.
	// TODO: Implement.
}

void memorymoog_state::dac_low_w(u8 data)
{
	// Updates the 4 least significant bits of the 12-bit DAC.
	m_dac_latch = (m_dac_latch & 0x0ff0) | (data & 0x0f);
	m_positive_hysteresis = !BIT(data, 4);  // Active low.
	m_negative_hysteresis = BIT(data, 5);   // Active high.
	update_sh();
}

void memorymoog_state::cv_mux_control_w(offs_t offset, u8 data)
{
	// Writing to this port does multiple things:
	// - Latches MSB (8 bits) for the 12-bit DAC.
	// - Selects the CV to be written.
	// - Generates wait states.
	// - Selects which potentiometer's voltage is being compared to the DAC.

	// Generate wait states.
	if (!machine().side_effects_disabled())
	{
		// U31 (74LS393) and U12 (74LS02) create a 16-cycle wait state.
		// U31, U33 (74LS74), U21 and U14 (74LS04) create a 4-cycle delay to
		// allow the DAC to settle. The S&H MUX is then enabled, and there's
		// another 12 cycles of waiting for the S&H capacitor to (dis)charge.
		m_maincpu->adjust_icount(-(4 + 12));
	}

	// D0-D7 are latched onto the 8 most significant bits of the 12-bit DAC.
	m_dac_latch = (u16(data) << 4) | (m_dac_latch & 0x000f);

	// An S&H mux is selected and a CV updated only if A6 = 0.
	if (!BIT(offset, 6))
	{
		// A3-A5 control which of the 8 DMUXes is enabled.
		// A0-A2 control which of the 8 paths within each DMUX is enabled.
		m_selected_sh = offset & 0x3f;
		update_sh();
	}
	else
	{
		m_selected_sh = -1;  // No DMUX selected.
	}

	// The potentiometer whose voltage is being measured will always be
	// selected, regardless of the value of A6. For that purpose:
	// - A5 is ignored.
	// - A3-A4 control which of the 4 MUXes is enabled.
	// - A0-A2 control which of the 8 paths within each MUX is enabled.
	m_selected_pot = offset & 0x1f;
}

void memorymoog_state::led_drive_w(u8 data)
{
	// Latched by 74LS273 (U1, board 7), buffered and inverted by
	// ULN2074 (U2, U3, board 7), and connected to LED cathodes.
	m_led_matrix_device->write_my(data);
	m_char_device->write_my(data);
}

void memorymoog_state::led_latch_w(u8 data)
{
	// Latched by 2x74LS378. D0-D3 by U2, board 6. D4-D7 by U8, board 7.
	// Active low. When 0, a PNP transistor switches 6V to the LED anodes.
	// Inverting because pwm_display_device expects inputs as active-high.
	m_led_matrix_device->write_mx(~data);
}

void memorymoog_state::led_update_w(offs_t offset, u8 data)
{
	// This is a callback from the pwm_display_device for LEDs.
	// The offset should be decoded as: x = offset >> 6, y = offset & 0x3f.
	m_leds[offset & 0x3f][offset >> 6] = data;
}

void memorymoog_state::digit_latch_w(u8 data)
{
	// TODO: Verify polarity.
	static constexpr const u8 PATTERNS[16] = // 7447 (U2, board 7).
	{
		0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7c, 0x07,
		0x7f, 0x67, 0x58, 0x4c, 0x62, 0x69, 0x78, 0x00,
	};

	// Inverting `data` because digit selection is active low.
	const u8 selection_mask = (~data >> 4) & 0x03;
	m_digit_device->matrix(selection_mask, PATTERNS[data & 0x0f]);
}

void memorymoog_state::digit_update_w(offs_t offset, u8 data)
{
	// Digits are ordered from left to right. So offset 0 (corresponding to
	// digit_1) is the most significant digit.
	m_digits[offset] = data;
}

void memorymoog_state::char_latch_a_w(u8 data)
{
	// 74LS377, U4, board 7. Q7 not connected.
	// D0-D6 -> A, B, C, D, E, F, G1 on LT-1604.
	// These are the low order 7 bits of LT-1604.
	// Bits are active low. When low, they connect corresponding LT-1604 inputs
	// to 6V.
	m_char_led_source = (m_char_led_source & 0x3f80) | (data & 0x7f);
	// Inverting because pwm_display_device expects inputs as active-high.
	m_char_device->write_mx(~m_char_led_source);
}

void memorymoog_state::char_latch_b_w(u8 data)
{
	// 74LS377, U5, board 7. Q7 not connected.
	// D0-D6 -> G2, H, J, K, L, M, N on LT-1604.
	// These are the high order 7 bits of LT-1604.
	// Bits are active low. When low, they connect corresponding LT-1604 inputs
	// to 6V.
	m_char_led_source = (u16(data & 0x7f) << 7) | (m_char_led_source & 0x7f);
	// Inverting because pwm_display_device expects inputs as active-high.
	m_char_device->write_mx(~m_char_led_source);
}

void memorymoog_state::char_update_w(offs_t offset, u8 data)
{
	// Characters are ordered from left to right.
	m_chars[offset] = data;
}

void memorymoog_state::memory_map(address_map &map)
{
	// Memory decoding done by 3 x 74LS138 (U9, U11, U17).
	// "S4X" below refers to the expansion connector. This was unused in the
	// initial revisions of the memorymoog, and AFAIK was only used for the
	// memorymoog Plus upgrade (not yet emulated).
	// U9 - Active when 0 = A14 = A15 = /MREQ = /RD (leverages U12 NOR)
	//      A0 <- A12, A1 <- A13, A2 <- 0.
	//      Outputs: 0 -> ROM U2, 1 -> ROM U3, 2 -> ROM U4, 3 -> S4X:/ROM.
	//               4-7 -> N.C.
	// U11 - Active when /MREQ = 0, A14 = 1, A15 = 0.
	//       A0 <- A11, A1 <- A12, A2 <- A13.
	//       Outputs: 0 -> RAM U5, 1 -> RAM U6, 2 -> RAM U7, 3 -> RAM U8
	//                4-7 -> /RAM 1-4 on S4X connector.
	//       U8 RAM was not populated in initial production models.
	//       RAM /CS decoding also includes 74LS04 and 72LS26 to ensure /CS
	//       is only active when there is no SHUTDOWN signal detected.
	// U17 - Active when A14 = 1, A15 = 1,  /MREQ = 0.
	//       A0 <- A11, A1 <- A12, A2 <- A13.
	//       Outputs: 0 -> U18 /EN2, 1 -> U19 /EN2, 2 -> U20 /EN2.
	//                3-6 -> N.C., 7 -> S4X: SEQ.PORT + TRANCEIVER BUS U29.
	// U18 - Active on /MREQ=0 A_high = 0xC0-0xC7.
	// U19 - Active on /MREQ=0 A_high = 0xC8-0xCF.
	// U20 - Active on /MREQ=0 A_high = 0xD0-0x7F
	// 0xd800-0xf7ff -> N.C.
	// 0xf800-0xffff -> S4X SEQ PORT (also disables traceiver U29).

	map(0x0000, 0x2fff).rom();  // U2-U4, 2532.
	// map(0x3000, 0x3fff)  // S4X: ROM
	map(0x4000, 0x57ff).ram().share(NVRAM_TAG);  // U5-U7 6116
	// map(0x5800, 0x5fff)  // U8 6116, but slot was not populated until
	// the Memorymoog Plus upgrade, which is not yet emulated.
	// map(0x6000, 0x7fff)  // S4X: RAM1-4

	map(0xc000, 0xc000).mirror(0x00ff).r(FUNC(memorymoog_state::u26_low4_r));
	map(0xc100, 0xc100).mirror(0x00ff).r(FUNC(memorymoog_state::keyboard_r));
	map(0xc100, 0xc100).mirror(0x00ff).w(FUNC(memorymoog_state::keyboard_w));
	map(0xc200, 0xc200).mirror(0x00ff).portr("rear_panel_inputs");
	map(0xc300, 0xc300).mirror(0x00ff).w(FUNC(memorymoog_state::tape_relay_w));
	// Unused: map(0xc400, 0xc4ff)
	map(0xc500, 0xc500).mirror(0x00ff).w(FUNC(memorymoog_state::dac_low_w));
	map(0xc600, 0xc600).mirror(0x00ff).w("latch_u54", FUNC(output_latch_device::write));
	map(0xc700, 0xc700).mirror(0x00ff).w("latch_u55", FUNC(output_latch_device::write));

	map(0xc800, 0xc800).mirror(0x00ff).w("latch_u56", FUNC(output_latch_device::write));
	map(0xc900, 0xc900).mirror(0x00ff).w("latch_u57", FUNC(output_latch_device::write));
	map(0xca00, 0xca00).mirror(0x00ff).w("latch_u58", FUNC(output_latch_device::write));
	map(0xcb00, 0xcb00).mirror(0x00ff).w("latch_u59", FUNC(output_latch_device::write));
	map(0xcc00, 0xcc00).mirror(0x00ff).w("latch_u60", FUNC(output_latch_device::write));
	map(0xcd00, 0xcd00).mirror(0x00ff).w("latch_u61", FUNC(output_latch_device::write));
	map(0xce00, 0xce00).mirror(0x00ff).w("latch_u51", FUNC(output_latch_device::write));
	map(0xcf00, 0xcf00).mirror(0x00ff).w("trigger_latch_u52", FUNC(output_latch_device::write));

	map(0xd000, 0xd000).mirror(0x00ff).w(FUNC(memorymoog_state::led_drive_w));
	map(0xd100, 0xd100).mirror(0x00ff).w(FUNC(memorymoog_state::led_latch_w));
	map(0xd200, 0xd200).mirror(0x00ff).w(FUNC(memorymoog_state::char_latch_a_w));
	map(0xd300, 0xd300).mirror(0x00ff).w(FUNC(memorymoog_state::char_latch_b_w));
	map(0xd400, 0xd400).mirror(0x00ff).w(FUNC(memorymoog_state::digit_latch_w));
	// TODO: map(0xd500, 0xd5ff) not labelled. Connected to pin 3 of connector
	// S44 to board 7.
	map(0xd600, 0xd600).mirror(0x00ff).r(FUNC(memorymoog_state::switches_b_r));
	map(0xd700, 0xd700).mirror(0x00ff).r(FUNC(memorymoog_state::switches_a_r));
	map(0xd700, 0xd700).mirror(0x00ff).w(FUNC(memorymoog_state::switches_w));
}

void memorymoog_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x7f).w(FUNC(memorymoog_state::cv_mux_control_w));
	map(0x80, 0x83).mirror(0x7c).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
}

void memorymoog_state::machine_start()
{
	m_digits.resolve();
	m_chars.resolve();
	m_octave_minus_1_led.resolve();
	m_octave_0_led.resolve();
	for (std::vector<output_finder<>> &led_row : m_leds)
		for (output_finder<> &led_output : led_row)
			led_output.resolve();

	save_item(NAME(m_keyboard_columns));
	save_item(NAME(m_switch_rows));
	save_item(NAME(m_dac_latch));
	save_item(NAME(m_selected_sh));
	save_item(NAME(m_selected_pot));
	save_item(NAME(m_positive_hysteresis));
	save_item(NAME(m_negative_hysteresis));
	save_item(NAME(m_octave_low));
	save_item(NAME(m_char_led_source));
	save_item(NAME(m_cv));
}

const z80_daisy_config memorymoog_daisy_chain[] =
{
	{ CTC_TAG },
	{ nullptr }
};

void memorymoog_state::memorymoog(machine_config &config)
{
	Z80(config, m_maincpu, 4_MHz_XTAL / 2);  // Division done by U16.
	m_maincpu->set_addrmap(AS_PROGRAM, &memorymoog_state::memory_map);
	m_maincpu->set_addrmap(AS_IO, &memorymoog_state::io_map);
	m_maincpu->set_daisy_config(memorymoog_daisy_chain);
	// /NMI pulled high.

	Z80CTC(config, m_ctc, 4_MHz_XTAL / 2);  // Same clock line as CPU.
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	// TODO: Implement the rest of the connections:
	// CLK/TRG 0,1 and ZC/TO 0,1 connected to S4X.
	// CLK/TRG 2, 3 used for autotune.
	// ZC/TO 2 N.C.
	// IEI pulled up.
	// IEO N.C.

	NVRAM(config, NVRAM_TAG, nvram_device::DEFAULT_ALL_0);  // 3x6116LP: U5,6,7.

	PWM_DISPLAY(config, m_digit_device).set_size(2, 7);
	m_digit_device->set_segmask(0x03, 0x7f);
	m_digit_device->output_digit().set(FUNC(memorymoog_state::digit_update_w));

	PWM_DISPLAY(config, m_char_device).set_size(8, 14);
	m_char_device->set_segmask(0xff, 0x3fff);
	m_char_device->output_digit().set(FUNC(memorymoog_state::char_update_w));

	PWM_DISPLAY(config, m_led_matrix_device).set_size(8, 8);
	m_led_matrix_device->output_x().set(FUNC(memorymoog_state::led_update_w));

	// All latches below are on board 5 (DMUX board).

	// U54-U58 are connected on the +-7.5V inverted data bus.

	// Bits 0, 1 and 3 are not connected.
	output_latch_device &u54(OUTPUT_LATCH(config, "latch_u54"));
	u54.bit_handler<2>().set_output("CONTOUR_KBYD_TRK").invert();
	u54.bit_handler<4>().set_output("VOICE_MOD_FILT").invert();
	u54.bit_handler<5>().set_output("VOICE_MOD_INVERT_ENABLE").invert();

	// Bit 0 translated from -+7.5V to ~ 0.7/15V via Zener CR4 (IN5237A,
	// Vz ~= 8.2V) and R164 (10K, connected to +15V).
	output_latch_device &u55(OUTPUT_LATCH(config, "latch_u55"));
	u55.bit_handler<0>().set_output("CONTOURED_OSC_3_AMT").invert();
	u55.bit_handler<1>().set_output("VOICE_MOD_PW_1").invert();
	u55.bit_handler<2>().set_output("VOIDE_MOD_PW_2").invert();
	u55.bit_handler<3>().set_output("VOICE_MOD_FREQ_2").invert();
	u55.bit_handler<4>().set_output("VOICE_MOD_FREQ_1").invert();
	u55.bit_handler<5>().set_output("TRANSPOSE_ENABLE").invert();

	output_latch_device &u56(OUTPUT_LATCH(config, "latch_u56"));
	u56.bit_handler<0>().set_output("SAW_SW_ENABLE").invert();
	u56.bit_handler<1>().set_output("RAMP_SW_ENABLE").invert();
	u56.bit_handler<2>().set_output("SQUARE_SW_ENABLE").invert();
	u56.bit_handler<3>().set_output("TRIANGLE_SW_ENABLE").invert();
	u56.bit_handler<4>().set_output("SH_SW_ENABLE").invert();
	u56.bit_handler<5>().set_output("MOD_VCF").invert();

	output_latch_device &u57(OUTPUT_LATCH(config, "latch_u57"));
	u57.bit_handler<0>().set_output("MOD_PW_2").invert();
	u57.bit_handler<1>().set_output("MOD_PW_3").invert();
	u57.bit_handler<2>().set_output("MOD_PW_1").invert();
	u57.bit_handler<3>().set_output("MOD_FREQ_3").invert();
	u57.bit_handler<4>().set_output("MOD_FREQ_1").invert();
	u57.bit_handler<5>().set_output("MOD_FREQ_2").invert();

	output_latch_device &u58(OUTPUT_LATCH(config, "latch_u58"));
	u58.bit_handler<0>().set_output("TUNE").invert();
	u58.bit_handler<1>().set_output("FP_1_VOL").invert();
	u58.bit_handler<2>().set_output("FP_2_OSC_2").invert();
	u58.bit_handler<3>().set_output("FP_1_FILT").invert();
	u58.bit_handler<4>().set_output("FP_2_MOD").invert();
	u58.bit_handler<5>().set_output("FP_1_PITCH").invert();

	// U59-U61 are on the 0/15V data bus.

	output_latch_device &u59(OUTPUT_LATCH(config, "latch_u59"));
	u59.bit_handler<0>().set_output("OSC_1_TRI");
	u59.bit_handler<1>().set_output("OSC_1_SAW");
	u59.bit_handler<2>().set_output("OSC_1_PULSE");
	u59.bit_handler<3>().set_output("OSC_2_TRI");
	u59.bit_handler<4>().set_output("OSC_2_SAW");
	u59.bit_handler<5>().set_output("OSC_2_PULSE");

	output_latch_device &u60(OUTPUT_LATCH(config, "latch_u60"));
	u60.bit_handler<0>().set_output("OSC_3_TRI");
	u60.bit_handler<1>().set_output("OSC_3_SAW");
	u60.bit_handler<2>().set_output("OSC_3_PULSE");
	u60.bit_handler<3>().set_output("OSC_3_KYBD_TRK");
	u60.bit_handler<4>().set_output("1/3-FILT");
	u60.bit_handler<5>().set_output("2/3-FILT");

	// Bits 2-5 not connected.
	output_latch_device &u61(OUTPUT_LATCH(config, "latch_u61"));
	u61.bit_handler<0>().set_output("SYNC_ENABLE");

	// U51-U52 are on the 0/5V data bus.

	output_latch_device &u51(OUTPUT_LATCH(config, "latch_u51"));
	u51.bit_handler<0>().set_output("MOD_OSC_RESET");
	u51.bit_handler<1>().set_output("UNCOND_ATTACK");
	u51.bit_handler<2>().set_output("RETURN_TO_ZERO");
	u51.bit_handler<3>().set_output("OSC_3_LOW_FREQ");
	// Bit 4 not connected.
	u51.bit_handler<5>().set_output("S-TRIG");  // Converted to 0-15V.
	u51.bit_handler<5>().append_output("Y-TRIG").invert();  // 0-15V, inverted.

	output_latch_device &u52(OUTPUT_LATCH(config, "trigger_latch_u52"));
	u52.bit_handler<0>().set_output("TRIG_A");
	u52.bit_handler<1>().set_output("TRIG_B");
	u52.bit_handler<2>().set_output("TRIG_C");
	u52.bit_handler<3>().set_output("TRIG_D");
	u52.bit_handler<4>().set_output("TRIG_E");
	u52.bit_handler<5>().set_output("TRIG_F");
}

DECLARE_INPUT_CHANGED_MEMBER(memorymoog_state::octave_button_pressed)
{
	// All components are on Board 10.
	// Comparators U1A and U1B along with surrounding components form an
	// SR flip-flop, triggered by SW1 and SW2.

	// Octave buttons will affect pich directly, without the firmware knowing
	// about it (they are not connected to the CPU).

	// Inputs are active low.
	const u8 input = m_octave_io->read();
	const bool oct_minus_1 = (input & 0x01) == 0;  // SW1
	const bool oct_0 = (input & 0x02) == 0;  // SW2

	if (oct_minus_1 && !oct_0)
	{
		m_octave_low = true;
	}
	else if (!oct_minus_1 && oct_0)
	{
		m_octave_low = false;
	}
	else if (oct_minus_1 && oct_0)
	{
		// The selected octave is undefined in this case, so it isn't updated.
		// An octave will be selected once one of the two buttons is released.
	}
	else
	{
		// No buttons pressed. No change in selected octave.
	}

	m_octave_minus_1_led = m_octave_low ? 1 : 0;
	m_octave_0_led = m_octave_low ? 0 : 1;
}

// All strings in PORT_NAME(...) match those in the schematic.
INPUT_PORTS_START(memorymoog)
	PORT_START("keyboard_column_0")  // C0 - G0 in schematic.
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_C2 PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_CS2
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_D2
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_DS2
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_E2
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_F2
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_FS2
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_G2

	PORT_START("keyboard_column_1")  // G#0 - D#1
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_GS2
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_A2
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_AS2
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_B2 PORT_CODE(KEYCODE_X)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_C3
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_CS3
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_D3
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_DS3

	PORT_START("keyboard_column_2")  // E1 - B1
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_E3
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_F3
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_FS3
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_G3
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_GS3
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_A3
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_AS3
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_B3

	PORT_START("keyboard_column_3")  // C2 - G2
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_C4
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_CS4
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_D4
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_DS4
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_E4
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_F4
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_FS4
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_G4

	PORT_START("keyboard_column_4")  // G#2 - D#3
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_GS4
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_A4
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_AS4
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_B4
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_C5
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_CS5
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_D5
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_DS5

	PORT_START("keyboard_column_5")  // E3 - B3
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_E5
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_F5
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_FS5
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_G5
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_GS5
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_A5
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_AS5
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_B5

	PORT_START("keyboard_column_6")  // C4 - G4
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_C6
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_CS6
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_D6
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_DS6
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_E6
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_F6
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_FS6
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_G6 PORT_CODE(KEYCODE_C)

	PORT_START("keyboard_column_7")  // G#4 - C5
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_GS6
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_A6
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_AS6
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_B6
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_C7 PORT_CODE(KEYCODE_V)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)  // NC
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)  // NC
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)  // NC

	PORT_START("switch_a_row_0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("B")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC2_FREQ_LFO")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SAW_LFO")

	PORT_START("switch_a_row_1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("3")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("6")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("9") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("ENTER")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC1_FREQ_LFO")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("TRI_LFO")

	PORT_START("switch_a_row_2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("2")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("5")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("8")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("0")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC3_FREQ_LFO")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SAW_LFO")

	PORT_START("switch_a_row_3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("4")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("7")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("RECORD_INTERLOCK")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PW1_LFO")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PULSE_LFO")

	PORT_START("switch_a_row_4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("MONO")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("HOLD")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("GLIDE")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("FP2_OSC2")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PW3_LFO")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("FILTER_LFO")

	PORT_START("switch_a_row_5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("MULT_TRIG")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("KYBD_MODE")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("VM_PW1")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("FP2_MOD")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PW2_LFO")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SH_LFO")

	PORT_START("switch_a_row_6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("FP1_FILTER")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("ARPEGIATOR")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("VM_FILTER")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("VM_OSC1_FREQ")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("FP1_VOLUME")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("CONTR_OSC3_AMT")

	PORT_START("switch_a_row_7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("KB_OUT")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("AUTO_TUNE")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("VM_PW2")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("VM_OSC2_FREQ")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("FP1_PITCH")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("INVERT")

	PORT_START("switch_b_row_0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC1-2'") PORT_CODE(KEYCODE_O)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC1-4'")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC1-8'")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC1-16'")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)  // NC
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)  // NC

	PORT_START("switch_b_row_1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC2-2'")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC2-4'")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC2-8'")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC2-16'")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)  // NC
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)  // NC

	PORT_START("switch_b_row_2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC3-2'")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC3-4'")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC3-8'")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC3-16'")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("LOW")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("KYBD_CONTROL")

	PORT_START("switch_b_row_3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC1_PULSE")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC1_RAMP")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC1_TRI") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SYNC_2_TO_1")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("1/3_KYBD_TRACK")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("2/3_KYBD_TRACK")

	PORT_START("switch_b_row_4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC3_PULSE")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC3_RAMP")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC3_TRI")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)  // NC
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("RELEASE")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("KYBD_FOLLOW")

	PORT_START("switch_b_row_5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC2_PULSE")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC2_RAMP") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC2_TRI")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)  // NC
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("RETURN_TO_ZERO")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("UNCOND_CONT")

	PORT_START("octave_buttons")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Octave -1") PORT_CODE(KEYCODE_1)
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(memorymoog_state::octave_button_pressed), 0x01)  // SW1 (Board 10).
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Octave 0") PORT_CODE(KEYCODE_0)
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(memorymoog_state::octave_button_pressed), 0x02)  // SW2 (Board 10).

	// 4051 MUX U9 (board 7)
	PORT_START("pot_0")
	PORT_ADJUSTER(50, "GLIDE")
	PORT_START("pot_1")
	PORT_ADJUSTER(50, "BEND AMT")
	PORT_START("pot_2")
	PORT_ADJUSTER(50, "MOD AMT")
	PORT_START("pot_3")
	PORT_ADJUSTER(50, "F.P. 1 AMT")
	PORT_START("pot_4")
	PORT_ADJUSTER(50, "F.P. 2 AMT")
	PORT_START("pot_5")
	PORT_ADJUSTER(50, "MOD RATE")
	PORT_START("pot_6")
	PORT_ADJUSTER(50, "V.M. OSC 3")
	PORT_START("pot_7")
	PORT_ADJUSTER(50, "V.M. FILTER CONTR.")

	// 4051 MUX U3 (board 6)
	PORT_START("pot_8")
	PORT_ADJUSTER(50, "POLY MOD OSC 3")  // TODO: maybe not a pot?
	PORT_START("pot_9")
	PORT_ADJUSTER(50, "POLY MOD FLT ENV.")  // TODO: maybe not a pot?
	PORT_START("pot_10")
	PORT_ADJUSTER(50, "PW1")
	PORT_START("pot_11")
	PORT_ADJUSTER(50, "FREQ 2")
	PORT_START("pot_12")
	PORT_ADJUSTER(50, "PW2")
	PORT_START("pot_13")
	PORT_ADJUSTER(50, "FREQ 3")
	PORT_START("pot_14")
	PORT_ADJUSTER(50, "PW3")
	PORT_START("pot_15")
	PORT_ADJUSTER(50, "MIX OSC LEVEL 1")

	// 4051 MUX U4 (board 6)
	PORT_START("pot_16")
	PORT_ADJUSTER(50, "MIX OSC 2 LEVEL")
	PORT_START("pot_17")
	PORT_ADJUSTER(50, "MIX OSC 3 LEVEL")
	PORT_START("pot_18")
	PORT_ADJUSTER(50, "NOISE LEVEL")
	PORT_START("pot_19")
	PORT_ADJUSTER(50, "CUTOFF")
	PORT_START("pot_20")
	PORT_ADJUSTER(50, "EMPH")
	PORT_START("pot_21")
	PORT_ADJUSTER(50, "CNTR AMT")
	PORT_START("pot_22")
	PORT_ADJUSTER(50, "FLT ATTACK")
	PORT_START("pot_23")
	PORT_ADJUSTER(50, "FLT DECAY")

	// 4051 MUX U4 (board 6)
	PORT_START("pot_24")
	PORT_ADJUSTER(50, "FLT SUSTAIN")
	PORT_START("pot_25")
	PORT_ADJUSTER(50, "FLT RELEASE")
	PORT_START("pot_26")
	PORT_ADJUSTER(50, "LOUD ATTACK")
	PORT_START("pot_27")
	PORT_ADJUSTER(50, "LOUD DECAY")
	PORT_START("pot_28")
	PORT_ADJUSTER(50, "LOUD SUSTAIN")
	PORT_START("pot_29")
	PORT_ADJUSTER(50, "LOUD RELEASE")
	PORT_START("pot_30")
	PORT_ADJUSTER(50, "PRO VOLUME")
	// I/O 7 not connected.

	PORT_START("rear_panel_inputs")  // U30 6-bit buffer.
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Release")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Hold")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Program Advance")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Program Backstep")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Glide")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Foot Pedal In (1 or 2)")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

// (Firmware version history below is based on
// https://forum.moogmusic.com/viewtopic.php?t=27119, and on Service Bulletins).
// The memorymoog was released with firwmare revision 1.4. Revisions 1.5 and
// 1.6 followed shortly after. Rev 1.6 is, apparently, the most common firmware
// for the non autotune-updated (see below) memorymoog.
// Rev 2.2 was short-lived, and quickly replaced with Rev 2.4, along with
// the "autotune update": a hardware update for more reliable autotuning (
// service bulletin 840A, date 10/6/83, for serial numbers below 2723).
// Version 2.5 was released shortly after, and is, apparently, the most common
// firmware for non-"plus" memory moogs with the autotune update.
// The "plus" hardware upgrade (not yet emulated) added MIDI and sequencer
// support, and was accompanied by firmware Rev 4.0. This was followed by
// Rev 4.1, which was the last official firmware update.
ROM_START(memorymoog)
	ROM_REGION(0x3000, MAINCPU_TAG, 0)
	ROM_DEFAULT_BIOS("r2.4")

	ROM_SYSTEM_BIOS(0, "r2.4", "Rev 2.4, October 1983")
	ROMX_LOAD("v2p4.u2", 0x000000, 0x001000, CRC(bb70cc39) SHA1(c0073d430ea4d3dd823c0678f482a6b3c49d926c), ROM_BIOS(0))
	ROMX_LOAD("v2p4.u3", 0x001000, 0x001000, CRC(088e1a3b) SHA1(4b9f568279fc7bf1a11ec2fb8f744dc261b0fcc4), ROM_BIOS(0))
	ROMX_LOAD("v2p4.u4", 0x002000, 0x001000, CRC(580db768) SHA1(98cd285342758abf7736002e0fbd30f4f7ecb96d), ROM_BIOS(0))
ROM_END

}  // anonymous namespace

// In production from 1982 to 1985.
SYST(1982, memorymoog, 0, 0, memorymoog, memorymoog, memorymoog_state, empty_init, "Moog Music", "Memorymoog", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE)



mentor16.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Novag Mentor 16 (model 892)

NOTE: Turn the power switch off before exiting MAME, otherwise NVRAM won't save
properly.

Hardware notes:
- PCB label: 100103
- Hitachi HD6301Y0P @ 8MHz
- 2*4-digit LCD panels
- piezo, 16+4 LEDs, 8*8 chessboard buttons

TODO:
- if/when MAME supports an exit callback, hook up power-off switch to that
- is Novag Amigo the same ROM? MCU label is also "892A", but QFP, ROM serial M44

BTANB:
- piezo sounds glitchy/fast (which is weird when compared against other Novag
  chesscomputers), verified with 2 videos

*******************************************************************************/

#include "emu.h"

#include "cpu/m6800/m6801.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "novag_mentor16.lh"


namespace {

class mentor16_state : public driver_device
{
public:
	mentor16_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_led_pwm(*this, "led_pwm"),
		m_lcd_pwm(*this, "lcd_pwm"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_out_lcd(*this, "s%u.%u", 0U, 0U),
		m_out_digit(*this, "digit%u", 0U)
	{ }

	void mentor16(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(power_off) { if (newval) m_power = false; }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD { m_power = true; }

private:
	// devices/pointers
	required_device<hd6301y0_cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_led_pwm;
	required_device<pwm_display_device> m_lcd_pwm;
	required_device<dac_2bit_ones_complement_device> m_dac;
	required_ioport_array<2> m_inputs;
	output_finder<4, 16> m_out_lcd;
	output_finder<8> m_out_digit;

	bool m_power = false;
	u8 m_inp_mux = 0;
	u16 m_lcd_segs = 0;
	u8 m_lcd_com = 0;
	emu_timer *m_piezo_delay;
	u8 m_piezo_data = 0;

	// I/O handlers
	void standby(int state);

	void lcd_pwm_w(offs_t offset, u8 data);
	void update_lcd();
	template <int N> void lcd_segs_w(u8 data);
	void lcd_com_w(u8 data);

	void update_piezo(s32 param);
	void p2_w(u8 data);
	u8 p5_r();
	u8 p6_r();
	void p6_w(u8 data);
};

void mentor16_state::machine_start()
{
	m_piezo_delay = timer_alloc(FUNC(mentor16_state::update_piezo), this);

	m_out_lcd.resolve();
	m_out_digit.resolve();

	// register for savestates
	save_item(NAME(m_power));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_lcd_segs));
	save_item(NAME(m_lcd_com));
	save_item(NAME(m_piezo_data));
}



/*******************************************************************************
    I/O
*******************************************************************************/

// LCD

void mentor16_state::lcd_pwm_w(offs_t offset, u8 data)
{
	// output raw segment data
	m_out_lcd[offset & 0x3f][offset >> 6] = data;

	// convert to 7seg digit
	int d = offset >> 6 & 0xe;
	m_out_digit[d >> 1] =
			m_out_lcd[0][d | 1] << 0 |
			m_out_lcd[1][d | 1] << 1 |
			m_out_lcd[2][d | 1] << 2 |
			m_out_lcd[3][d | 0] << 3 |
			m_out_lcd[2][d | 0] << 4 |
			m_out_lcd[0][d | 0] << 5 |
			m_out_lcd[1][d | 0] << 6;
}

void mentor16_state::update_lcd()
{
	m_lcd_pwm->matrix(m_lcd_com >> 4, (m_lcd_com & 1) ? ~m_lcd_segs : m_lcd_segs);
}

template <int N>
void mentor16_state::lcd_segs_w(u8 data)
{
	// P1x, P4x: LCD segments
	const u8 shift = 8 * N;
	m_lcd_segs = (m_lcd_segs & ~(0xff << shift)) | (data << shift);
	update_lcd();
}

void mentor16_state::lcd_com_w(u8 data)
{
	// P30: 4066 Y0-Y3, P34-P37: 4066 E0-E3
	// 4066 Z0-Z3: LCD commons
	m_lcd_com = data;
	update_lcd();
}


// misc

void mentor16_state::standby(int state)
{
	// clear display
	if (state)
	{
		m_led_pwm->clear();
		m_lcd_pwm->clear();
	}
}

void mentor16_state::update_piezo(s32 param)
{
	m_piezo_data = param & 3;
	m_dac->write(m_piezo_data);
}

void mentor16_state::p2_w(u8 data)
{
	// P20-P27: input mux, led data
	m_inp_mux = ~data;
	m_led_pwm->write_mx(~data);
}

u8 mentor16_state::p5_r()
{
	u8 data = 0;

	// P50-P57: read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_rank(i ^ 7);

	return ~data;
}

u8 mentor16_state::p6_r()
{
	u8 data = 0;

	// P60,P61: read buttons
	for (int i = 0; i < 2; i++)
		if (m_inp_mux & m_inputs[i]->read())
			data |= 1 << i;

	// P62,P63: piezo
	data |= m_piezo_data << 2;

	// P67: power state
	data |= (m_power) ? 0x80 : 0x00;
	return data ^ 0xf3;
}

void mentor16_state::p6_w(u8 data)
{
	// P62,P63: piezo (relies on short delay at rising edge)
	update_piezo(data >> 2 & m_piezo_data);
	m_piezo_delay->adjust(attotime::from_usec(5), data >> 2 & 3);

	// P64-P66: led select
	m_led_pwm->write_my(~data >> 4 & 7);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( mentor16 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Player/Player / King")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Random / Queen")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Sound / Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Solve Mate / Knight")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Depth Search / Rook")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Autoplay / Pawn")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Set Level")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Take Back")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Set Up / Verify")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Change Color")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Clear Board")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Acc. Time")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Move Time")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Hint / Show Moves")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Go")

	PORT_START("POWER") // needs to be triggered for nvram to work
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mentor16_state::power_off), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void mentor16_state::mentor16(machine_config &config)
{
	// basic machine hardware
	HD6301Y0(config, m_maincpu, 8_MHz_XTAL);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(hd6301y0_cpu_device::nvram_set_battery));
	m_maincpu->standby_cb().set(FUNC(mentor16_state::standby));
	m_maincpu->out_p1_cb().set(FUNC(mentor16_state::lcd_segs_w<0>));
	m_maincpu->out_p2_cb().set(FUNC(mentor16_state::p2_w));
	m_maincpu->out_p3_cb().set(FUNC(mentor16_state::lcd_com_w));
	m_maincpu->out_p4_cb().set(FUNC(mentor16_state::lcd_segs_w<1>));
	m_maincpu->in_p5_cb().set(FUNC(mentor16_state::p5_r));
	m_maincpu->in_p6_cb().set(FUNC(mentor16_state::p6_r));
	m_maincpu->in_p6_override_mask(0x0c); // reads back from live piezo
	m_maincpu->out_p6_cb().set(FUNC(mentor16_state::p6_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_lcd_pwm).set_size(4, 16);
	m_lcd_pwm->output_x().set(FUNC(mentor16_state::lcd_pwm_w));

	PWM_DISPLAY(config, m_led_pwm).set_size(3, 8);
	config.set_default_layout(layout_novag_mentor16);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_2BIT_ONES_COMPLEMENT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.125);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( mentor16 )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("novag_892a_31y0rk62p.u1", 0x0000, 0x4000, CRC(78176c7b) SHA1(fbd1b29efa5d80411754bf7cd6b80f388e065321) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1989, mentor16, 0,      0,      mentor16, mentor16, mentor16_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Mentor 16", MACHINE_SUPPORTS_SAVE )



meritum.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Mera-Elzab Meritum (Poland)

Meritum I was a basic unit with no expansion, expensive, and so very rare.
Meritum II had all the standard TRS80 expansions, but the i/o is different
and thus not compatible.
Meritum III hires graphics mode added; only pre-production units were made.

Split from trs80.cpp on 2018-07-15

It is quite similar to the TRS80 Model 1 Level II, however instead of the
external interface, Intel peripheral chips were used (i8251, i8253, i8255),
and 2KB of ROM with new subroutines.

Model II has an additional 8255 to act as a floppy interface, plus it has more
RAM (32 or 48 KB).

Many variants of ROM existed:
- initial one without Polish characters
- with Polish characters in char table
- translated MEMORY SIZE to ROZMIAR PAO
- experimental network version made by Silesian University of Technology
- 8 KiB version with bootstrap code to load program from floppy
and others.

There's no lowercase, so the shift key will select Polish characters if
available, as well as the usual standard punctuation.
The control key appears to do nothing.
There's also a Reset key, a NMI key, and 2 blank ones.

Serial printer (@1200bps) is supported by default after power-up by all variants.
If parallel printer (centronics) is to be used press P while system starts (e.g. P and F3 to reset).
This does not work for model 1 (no parallel printer support in ROM).

Status:
- Starts up, runs Basic. Cassette works. Quickload mostly works.
- Some quickloads have corrupt text due to no lowercase.
- Some quickloads don't run at all.
- Intel chips need adding, along with the peripherals they control.
- A speaker has been included (which works), but real machine might not have
    one at that address. To be checked.
- On meritum1, type SYSTEM then /12288 to enter the Monitor.
- On meritum_net, type NET to activate the networking features.
- Add Reset key and 2 blank keys.
- Need software specific to test the hardware.
- Need boot disks (MER-DOS, CP/M 2.2)
- Due to faster CPU clock, no TRS-80 cassettes can be loaded.

For Model III:
- Add 4-colour mode, 4 shades of grey mode, and 512x192 monochrome.

****************************************************************************/

#include "emu.h"
#include "trs80.h"

#include "trs80_quik.h"

#include "bus/centronics/ctronics.h"
#include "machine/input_merger.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/pit8253.h"

#include "emupal.h"
#include "softlist_dev.h"

#include "utf8.h"


namespace {

class meritum_state : public trs80_state
{
public:
	meritum_state(const machine_config &mconfig, device_type type, const char *tag)
		: trs80_state(mconfig, type, tag)
		, m_screen(*this, "screen")
		, m_centronics(*this, "centronics")
		, m_nmigate(*this, "nmigate")
	{ }

	void meritum1(machine_config &config);
	void meritum2(machine_config &config);
	void meritumn(machine_config &config);

private:

	u32 screen_update_meritum1(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	u32 screen_update_meritum2(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void mem_map2(address_map &map) ATTR_COLD;
	void io_map2(address_map &map) ATTR_COLD;
	void mainppi_portb_w(u8);
	void mainppi_portc_w(u8);

	required_device<screen_device> m_screen;
	optional_device<centronics_device> m_centronics;
	required_device<input_merger_device> m_nmigate;
};

void meritum_state::mainppi_portc_w(u8 data)
{
	m_nmigate->in_w<0>(!BIT(data, 7)); // negated PC7 => NMI
	m_centronics->write_strobe(BIT(data, 1)); // PC1 = STROBE (centronics)
}

void meritum_state::mainppi_portb_w(u8 data)
{
	m_centronics->write_data0(BIT(data, 0));
	m_centronics->write_data1(BIT(data, 1));
	m_centronics->write_data2(BIT(data, 2));
	m_centronics->write_data3(BIT(data, 3));
	m_centronics->write_data4(BIT(data, 4));
	m_centronics->write_data5(BIT(data, 5));
	m_centronics->write_data6(BIT(data, 6));
	m_centronics->write_data7(BIT(data, 7));
}

void meritum_state::mem_map(address_map &map)
{
	map(0x0000, 0x37ff).rom();
	map(0x3800, 0x38ff).r(FUNC(meritum_state::keyboard_r));
	map(0x3c00, 0x3fff).ram().share(m_p_videoram);
	map(0x4000, 0x7fff).ram();
}

void meritum_state::mem_map2(address_map &map)
{
	mem_map(map);
	map(0x4000, 0xffff).ram();
}

void meritum_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x03).rw("audiopit", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xf4, 0xf7).rw("mainppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xf8, 0xfb).rw("mainpit", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xfc, 0xfd).rw("usart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	// map(0xfe, 0xfe) audio interface
	map(0xff, 0xff).rw(FUNC(meritum_state::port_ff_r), FUNC(meritum_state::port_ff_w));
}

void meritum_state::io_map2(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	io_map(map);
	map(0xf0, 0xf3).rw("flopppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
}


static INPUT_PORTS_START( meritum )
	PORT_START("LINE0")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR('@')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('A', 'a') PORT_CHAR(0x0104, 0x0105)
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)          PORT_CHAR('B', 'b')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)          PORT_CHAR('C', 'c') PORT_CHAR(0x0106, 0x0107)
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)          PORT_CHAR('D', 'd')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)          PORT_CHAR('E', 'e') PORT_CHAR(0x0118, 0x0119)
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)          PORT_CHAR('F', 'f')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)          PORT_CHAR('G', 'g')

	PORT_START("LINE1")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)          PORT_CHAR('H', 'h')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)          PORT_CHAR('I', 'i')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)          PORT_CHAR('J', 'j')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)          PORT_CHAR('K', 'k')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)          PORT_CHAR('L', 'l') PORT_CHAR(0x0141, 0x0142)
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)          PORT_CHAR('M', 'm')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)          PORT_CHAR('N', 'n') PORT_CHAR(0x0143, 0x0144)
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)          PORT_CHAR('O', 'o') PORT_CHAR(0x00d3, 0x00f3)

	PORT_START("LINE2")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)          PORT_CHAR('P', 'p')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)          PORT_CHAR('Q', 'q')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)          PORT_CHAR('R', 'r')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)          PORT_CHAR('S', 's') PORT_CHAR(0x015a, 0x015b)
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)          PORT_CHAR('T', 't')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)          PORT_CHAR('U', 'u')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)          PORT_CHAR('V', 'v')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)          PORT_CHAR('W', 'w')

	PORT_START("LINE3")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)          PORT_CHAR('X', 'x') PORT_CHAR(0x0179, 0x017a)
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('Y', 'y')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_CHAR('Z', 'z') PORT_CHAR(0x017b, 0x017c)
	PORT_BIT(0xF8, 0x00, IPT_UNUSED)

	PORT_START("LINE4")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_CHAR('0')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("LINE5")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("LINE6")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER)     PORT_CHAR(13)
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("Clear") PORT_CODE(KEYCODE_HOME)      PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_END)       PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_TAB)       PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP), '[')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_OPENBRACE)  PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT), 8)
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_QUOTE)     PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT), 9)
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)     PORT_CHAR(' ')

	PORT_START("LINE7")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)               PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0xFC, 0x00, IPT_UNUSED)

	PORT_START("NMI")
	PORT_BIT(0x01, 0x01, IPT_KEYBOARD) PORT_NAME("NMI") PORT_CODE(KEYCODE_F1) PORT_WRITE_LINE_DEVICE_MEMBER("nmigate", FUNC(input_merger_device::in_w<1>))
INPUT_PORTS_END

u32 meritum_state::screen_update_meritum1(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	// lores characters are in the character generator. Each character is 6x12 (basic characters are 6x7 excluding descenders/ascenders).
	u16 sy=0,ma=0;
	const u8 cols = m_cpl ? 32 : 64;
	const u8 skip = m_cpl ? 2 : 1;

	if (cols != m_cols)
	{
		m_cols = cols;
		screen.set_visible_area(0, cols*6-1, 0, 16*12-1);
	}

	for (u8 y = 0; y < 16; y++)
	{
		for (u8 ra = 0; ra < 12; ra++)
		{
			u16 *p = &bitmap.pix(sy++);

			for (u16 x = ma; x < ma + 64; x+=skip)
			{
				const u8 chr = m_p_videoram[x] & 0xbf;
				u8 gfx;

				// shift down comma and semicolon
				// not sure Meritum I got the circuit for this (like TRS80)
				// but position of ';' suggests most likely yes
				if ((chr == 0x2c) && (ra >= 2))
					gfx = m_p_chargen[0x2be + ra];
				else if ((chr == 0x3b) && (ra >= 1))
					gfx = m_p_chargen[0x3af + ra];
				else
					gfx = m_p_chargen[(chr<<4) | ra];

				// Display a scanline of a character (6 pixels)
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma+=64;
	}
	return 0;
}

u32 meritum_state::screen_update_meritum2(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 sy=0,ma=0;
	const u8 cols = m_cpl ? 32 : 64;
	const u8 skip = m_cpl ? 2 : 1;

	if (cols != m_cols)
	{
		m_cols = cols;
		screen.set_visible_area(0, cols*6-1, 0, 16*12-1);
	}

	for (u8 y = 0; y < 16; y++)
	{
		for (u8 ra = 0; ra < 12; ra++)
		{
			u16 *p = &bitmap.pix(sy++);

			for (u16 x = ma; x < ma + 64; x+=skip)
			{
				const u8 chr = m_p_videoram[x];

				// get pattern of pixels for that character scanline
				const u8 gfx = m_p_chargen[(chr<<4) | ra];

				// Display a scanline of a character (6 pixels)
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma+=64;
	}
	return 0;
}


/**************************** F4 CHARACTER DISPLAYER ***********************************************************/
static const gfx_layout charlayout =
{
	6, 12,          /* 6 x 12 characters */
	256,            /* 256 characters */
	1,          /* 1 bits per pixel */
	{ 0 },          /* no bitplanes */
	/* x offsets */
	{ 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{  0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8*16           /* every char takes 16 bytes (unused scanlines are blank) */
};

static GFXDECODE_START(gfx_meritum)
	GFXDECODE_ENTRY( "chargen", 0, charlayout, 0, 1 )
GFXDECODE_END



void meritum_state::meritum1(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 10_MHz_XTAL / 4); // U880D @ 2.5 MHz or 1.67 MHz by jumper selection
	m_maincpu->set_addrmap(AS_PROGRAM, &meritum_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &meritum_state::io_map);

	i8251_device &usart(I8251(config, "usart", 10_MHz_XTAL / 4)); // same as CPU clock
	usart.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set("usart", FUNC(i8251_device::write_rxd));
	rs232.cts_handler().set("usart", FUNC(i8251_device::write_cts));

	INPUT_MERGER_ALL_HIGH(config, "nmigate").output_handler().set("mainpit", FUNC(pit8253_device::write_gate2)).invert();

	pit8253_device &pit(PIT8253(config, "mainpit", 0));
	pit.set_clk<0>(10_MHz_XTAL / 5); // 2 MHz
	pit.set_clk<1>(10_MHz_XTAL / 10); // 1 MHz
	pit.set_clk<2>(10_MHz_XTAL / 4); // same as CPU clock
	pit.out_handler<0>().set("usart", FUNC(i8251_device::write_txc));
	pit.out_handler<0>().append("usart", FUNC(i8251_device::write_rxc));
	// Channel 1 generates INT pulse through 123 monostable
	pit.out_handler<2>().set_inputline(m_maincpu, INPUT_LINE_NMI);

	i8255_device &mainppi(I8255(config, "mainppi")); // parallel interface
	mainppi.out_pc_callback().set(FUNC(meritum_state::mainppi_portc_w));
	mainppi.out_pb_callback().set(FUNC(meritum_state::mainppi_portb_w));

	// printer
	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->set_data_input_buffer("cent_data_in");
	m_centronics->ack_handler().set("mainppi", FUNC(i8255_device::pc2_w));

	INPUT_BUFFER(config, "cent_data_in");
	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	PIT8253(config, "audiopit", 0); // optional audio interface

	// video
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(10_MHz_XTAL, 107 * 6, 0, 64 * 6, 312, 0, 192);
	m_screen->set_screen_update(FUNC(meritum_state::screen_update_meritum1));
	m_screen->set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_meritum);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	// sound
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	// media
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_PLAY);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	TRS80_QUICKLOAD(config, "quickload", m_maincpu, attotime::from_seconds(1));
	SOFTWARE_LIST(config, "quik_list").set_original("trs80_quik").set_filter("M1");
}

void meritum_state::meritum2(machine_config &config)
{
	meritum1(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &meritum_state::mem_map2);
	m_maincpu->set_addrmap(AS_IO, &meritum_state::io_map2);
	I8255(config, "flopppi", 0); // floppy disk interface
	m_screen->set_screen_update(FUNC(meritum_state::screen_update_meritum2));
	SOFTWARE_LIST(config.replace(), "quik_list").set_original("trs80_quik").set_filter("M2");
}

void meritum_state::meritumn(machine_config &config)
{
	meritum2(config);
	SOFTWARE_LIST(config.replace(), "quik_list").set_original("trs80_quik").set_filter("MN");
}


/***************************************************************************

  Game driver(s)

***************************************************************************/


ROM_START( meritum1 )
	ROM_REGION(0x3800, "maincpu",0)
	ROM_LOAD( "rom_0.ic7",  0x0000, 0x0800, CRC(1ecf7205) SHA1(e91cedfe2ce7636d37d5b765e5bbc8168deaba77))
	ROM_LOAD( "rom_1.ic8",  0x0800, 0x0800, CRC(ac297d99) SHA1(ccf31d3f9d02c3b68a0ee3be4984424df0e83ab0))
	ROM_LOAD( "rom_2.ic9",  0x1000, 0x0800, CRC(a21d0d62) SHA1(6dfdf3806ed2b6502e09a1b6922f21494134cc05))
	ROM_LOAD( "rom_3.ic10", 0x1800, 0x0800, CRC(3a5ea239) SHA1(8c489670977892d7f2bfb098f5df0b4dfa8fbba6))
	ROM_LOAD( "rom_4.ic11", 0x2000, 0x0800, CRC(2ba025d7) SHA1(232efbe23c3f5c2c6655466ebc0a51cf3697be9b))
	ROM_LOAD( "rom_5.ic12", 0x2800, 0x0800, CRC(ed547445) SHA1(20102de89a3ee4a65366bc2d62be94da984a156b))
	ROM_LOAD( "rom_6.ic13", 0x3000, 0x0800, CRC(650c0f47) SHA1(05f67fed3c3f69ad210823460bacf40166cbf06e))

	ROM_REGION(0x1000, "chargen", ROMREGION_INVERT)
	ROM_LOAD( "char_gen.ic72", 0x0000, 0x0400, CRC(626fb8b1) SHA1(1274d14efad46e5397bd9952e1277ebee44e0491))
	ROM_CONTINUE( 0x0800, 0x0400)
	ROM_RELOAD(   0x0400, 0x0400)
	ROM_CONTINUE( 0x0c00, 0x0400)
ROM_END

ROM_START( meritum2)
	ROM_REGION(0x3800, "maincpu",0)
	ROM_LOAD( "01.ic7",  0x0000, 0x0800, CRC(ed705a47) SHA1(dae8b14eb2ddb2a8b4458215180ebc0fb781816a))
	ROM_LOAD( "02.ic8",  0x0800, 0x0800, CRC(ac297d99) SHA1(ccf31d3f9d02c3b68a0ee3be4984424df0e83ab0))
	ROM_LOAD( "03.ic9",  0x1000, 0x0800, CRC(a21d0d62) SHA1(6dfdf3806ed2b6502e09a1b6922f21494134cc05))
	ROM_LOAD( "04.ic10", 0x1800, 0x0800, CRC(3610bdda) SHA1(602f0ba1e1267f24620f993acac019ac6342a594))
	ROM_LOAD( "05.ic11", 0x2000, 0x0800, CRC(461fbf0d) SHA1(bd19187dd992168af43bd68055343d515f152624))
	ROM_LOAD( "06.ic12", 0x2800, 0x0800, CRC(ed547445) SHA1(20102de89a3ee4a65366bc2d62be94da984a156b))
	ROM_LOAD( "07.ic13", 0x3000, 0x0800, CRC(044b1459) SHA1(faace7353ffbef6587b1b9e7f8b312e0892e3427))

	ROM_REGION(0x1000, "chargen", ROMREGION_INVERT)
	ROM_LOAD( "chargen.ic72", 0x0000, 0x1000, CRC(3dfc6439) SHA1(6e45a27f68c3491c403b4eafe45a108f348dd2fd))
ROM_END

ROM_START( meritum_net )
	ROM_REGION(0x3800, "maincpu",0)
	ROM_LOAD( "01_447_m07_015m.ic7",  0x0000, 0x0800, CRC(6d30cb49) SHA1(558241340a84eebcbbf8d92540e028e9164b6f8a))
	ROM_LOAD( "02_440_m08_01.ic8",    0x0800, 0x0800, CRC(ac297d99) SHA1(ccf31d3f9d02c3b68a0ee3be4984424df0e83ab0))
	ROM_LOAD( "03_440_m09_015m.ic9",  0x1000, 0x0800, CRC(88e267da) SHA1(9cb8626801f8e969f35291de43c1b643c809a3c3))
	ROM_LOAD( "04_447_m10_015m.ic10", 0x1800, 0x0800, CRC(e51991e4) SHA1(a7d42436da1af405970f9f99ab34b6d9abd05adf))
	ROM_LOAD( "05_440_m11_02.ic11",   0x2000, 0x0800, CRC(461fbf0d) SHA1(bd19187dd992168af43bd68055343d515f152624))
	ROM_LOAD( "06_440_m12_01.ic12",   0x2800, 0x0800, CRC(ed547445) SHA1(20102de89a3ee4a65366bc2d62be94da984a156b))
	ROM_LOAD( "07_447_m13_015m.ic13", 0x3000, 0x0800, CRC(789f6964) SHA1(9b2231ca7ffd82bbca1f53988a7df833290ddbf2))

	ROM_REGION(0x1000, "chargen", ROMREGION_INVERT)
	ROM_LOAD( "char.ic72", 0x0000, 0x1000, CRC(2c09a5a7) SHA1(146891b3ddfc2de95e6a5371536394a657880054))
ROM_END

} // anonymous namespace


//    YEAR  NAME         PARENT    COMPAT    MACHINE   INPUT      CLASS          INIT             COMPANY              FULLNAME                FLAGS
COMP( 1983, meritum1,    0,        trs80l2,  meritum1, meritum,   meritum_state, empty_init,  "Mera-Elzab", "Meritum I (Model 1)",           MACHINE_SUPPORTS_SAVE )
COMP( 1985, meritum2,    meritum1, 0,        meritum2, meritum,   meritum_state, empty_init,  "Mera-Elzab", "Meritum I (Model 2)",           MACHINE_SUPPORTS_SAVE )
COMP( 1985, meritum_net, meritum1, 0,        meritumn, meritum,   meritum_state, empty_init,  "Mera-Elzab", "Meritum I (Model 2) (network)", MACHINE_SUPPORTS_SAVE )



mes.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Schleicher MES

2010-08-30 Skeleton driver

****************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"
#include "machine/keyboard.h"

#include "emupal.h"
#include "screen.h"


namespace {

class mes_state : public driver_device
{
public:
	mes_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_p_videoram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
	{ }

	void mes(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void kbd_put(u8 data);
	u8 port00_r();
	u8 port08_r();

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	u8 m_term_data = 0U;
	u8 m_port08 = 0U;
	required_device<cpu_device> m_maincpu;
	required_shared_ptr<u8> m_p_videoram;
	required_region_ptr<u8> m_p_chargen;
};


u8 mes_state::port00_r()
{
	u8 ret = m_term_data;
	m_term_data = 0;
	return ret;
}

u8 mes_state::port08_r()
{
	return m_port08 | (m_term_data ? 0x80 : 0);
}

void mes_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).rom();
	map(0x1000, 0xefff).ram();
	map(0xf000, 0xffff).ram().share("videoram");
}

void mes_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x00).r(FUNC(mes_state::port00_r));
	map(0x08, 0x08).r(FUNC(mes_state::port08_r));
	map(0x0c, 0x0f).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x10, 0x13).rw("sio", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
	map(0x18, 0x1b).rw("pio", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
}

/* Input ports */
static INPUT_PORTS_START( mes )
INPUT_PORTS_END

void mes_state::machine_start()
{
	save_item(NAME(m_term_data));
	save_item(NAME(m_port08));
}

void mes_state::machine_reset()
{
	m_port08 = 0;
	m_term_data = 0;
}

/* This system appears to have 2 screens. Not implemented.
    Also the screen dimensions are a guess. */
uint32_t mes_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 sy = 0, ma = 0;

	for (u8 y = 0; y < 25; y++)
	{
		for (u8 ra = 0; ra < 10; ra++)
		{
			u16 *p = &bitmap.pix(sy++);

			for (u16 x = ma; x < ma + 80; x++)
			{
				u8 gfx = 0;
				if (ra < 9)
				{
					const u8 chr = m_p_videoram[x];
					gfx = m_p_chargen[(chr<<4) | ra ];
				}

				/* Display a scanline of a character */
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma += 80;
	}
	return 0;
}

void mes_state::kbd_put(u8 data)
{
	m_term_data = data;
}

void mes_state::mes(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(16'000'000) / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &mes_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &mes_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(mes_state::screen_update));
	screen.set_size(640, 250);
	screen.set_visarea(0, 639, 0, 249);
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	Z80CTC(config, "ctc", 0);
	Z80PIO(config, "pio", 0);
	Z80SIO(config, "sio", 0);

	generic_keyboard_device &keybd(GENERIC_KEYBOARD(config, "keybd", 0));
	keybd.set_keyboard_callback(FUNC(mes_state::kbd_put));
}


/* ROM definition */
ROM_START( mes )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mescpu.bin",   0x0000, 0x1000, CRC(b6d90cf4) SHA1(19e608af5bdaabb00a134e1106b151b00e2a0b04))

	ROM_REGION( 0x2000, "xebec", 0 )
	ROM_LOAD( "mesxebec.bin", 0x0000, 0x2000, CRC(061b7212) SHA1(c5d600116fb7563c69ebd909eb9613269b2ada0f))

	/* character generator not dumped, using the one from 'c10' for now */
	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "c10_char.bin", 0x0000, 0x2000, BAD_DUMP CRC(cb530b6f) SHA1(95590bbb433db9c4317f535723b29516b9b9fcbf))
ROM_END

} // anonymous namespace


/* Driver */

//   YEAR   NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY       FULLNAME  FLAGS
COMP( 198?, mes,  0,      0,      mes,     mes,   mes_state, empty_init, "Schleicher", "MES",    MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



mex68kecb.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Chris Hanson
/*
 * mex68kecb.cpp - Motorola MEX68KECB
 *
 * Documentation:
 *   http://www.bitsavers.org/components/motorola/68000/MEX68KECB/MEX68KECB_D2_EduCompBd_Jul82.pdf
 *
 * The Motorola MC68000 Educational Computer Board is a single-board computer with
 * a 4MHz 68000 CPU, 32KB RAM, 16KB ROM, host and terminal serial ports, a
 * parallel interface/timer, a cassette interface, and a prototyping area with
 * full access to the 68000 bus. The ROM contains TUTOR, a debug and bootstrap
 * system that was the predecessor of MACSBUG.
 *
 * Specifications:
 * - 4MHz MC68000L4 CPU
 * - MC6850 ACIA x 2
 * - MC68230 PIT
 *
 * TODO:
 * - Cassette I/O
 *
 */

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
#include "machine/68230pit.h"
#include "machine/6850acia.h"
#include "machine/mc14411.h"


namespace {

class mex68kecb_state : public driver_device
{
public:
	static constexpr feature_type unemulated_features() { return feature::TAPE; }

	mex68kecb_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_bootvect(*this, "bootvect")
		, m_sysram(*this, "ram")
		, m_maincpu(*this, "maincpu")
		, m_pit(*this, "pit")
		, m_brg(*this, "brg")
		, m_acia1(*this, "acia1")
		, m_acia2(*this, "acia2")
		, m_acia1_baud(*this, "ACIA1_BAUD")
		, m_acia2_baud(*this, "ACIA2_BAUD")
		, m_terminal(*this, "terminal")
		, m_host(*this, "host")
	{ }

	void mex68kecb(machine_config &config) ATTR_COLD;

	DECLARE_INPUT_CHANGED_MEMBER(abort_button);

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	void mem_map(address_map &map) ATTR_COLD;

	// Clocks from Baud Rate Generator
	template <uint8_t Bit> void write_acia_clock(int state);

	void bootvect_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

	memory_view m_bootvect;
	required_shared_ptr<uint16_t> m_sysram; // Pointer to System RAM needed by bootvect_w and masking RAM buffer for post reset accesses

	required_device<cpu_device> m_maincpu;
	required_device<pit68230_device> m_pit;
	required_device<mc14411_device> m_brg;
	required_device<acia6850_device> m_acia1;
	required_device<acia6850_device> m_acia2;
	required_ioport m_acia1_baud;
	required_ioport m_acia2_baud;

	required_device<rs232_port_device> m_terminal;
	required_device<rs232_port_device> m_host;
};


/* Input ports */
static INPUT_PORTS_START( mex68kecb )
	PORT_START("ACIA1_BAUD")
	PORT_DIPNAME(0xff, 0x80, "Terminal Baud Rate") PORT_DIPLOCATION("J10:8,7,6,5,4,3,2,1")
	PORT_DIPSETTING(0x80, "9600")
	PORT_DIPSETTING(0x40, "4800")
	PORT_DIPSETTING(0x20, "2400")
	PORT_DIPSETTING(0x10, "1200")
	PORT_DIPSETTING(0x08,  "600")
	PORT_DIPSETTING(0x04,  "300")
	PORT_DIPSETTING(0x02,  "150")
	PORT_DIPSETTING(0x01,  "110")

	PORT_START("ACIA2_BAUD")
	PORT_DIPNAME(0xff, 0x80, "Host Baud Rate") PORT_DIPLOCATION("J9:8,7,6,5,4,3,2,1")
	PORT_DIPSETTING(0x80, "9600")
	PORT_DIPSETTING(0x40, "4800")
	PORT_DIPSETTING(0x20, "2400")
	PORT_DIPSETTING(0x10, "1200")
	PORT_DIPSETTING(0x08,  "600")
	PORT_DIPSETTING(0x04,  "300")
	PORT_DIPSETTING(0x02,  "150")
	PORT_DIPSETTING(0x01,  "110")

	PORT_START("ABORT")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("Abort button") PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mex68kecb_state::abort_button), 0)
INPUT_PORTS_END


void mex68kecb_state::mex68kecb(machine_config &config)
{
	M68000(config, m_maincpu, 8_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &mex68kecb_state::mem_map);

	// Set up BRG.

	MC14411(config, m_brg, 1.8432_MHz_XTAL);
	m_brg->out_f<1>().set(FUNC(mex68kecb_state::write_acia_clock<7>));  // 9600bps
	m_brg->out_f<3>().set(FUNC(mex68kecb_state::write_acia_clock<6>));  // 4800bps
	m_brg->out_f<5>().set(FUNC(mex68kecb_state::write_acia_clock<5>));  // 2400bps
	m_brg->out_f<7>().set(FUNC(mex68kecb_state::write_acia_clock<4>));  // 1200bps
	m_brg->out_f<8>().set(FUNC(mex68kecb_state::write_acia_clock<3>));  //  600bps
	m_brg->out_f<9>().set(FUNC(mex68kecb_state::write_acia_clock<2>));  //  300bps
	m_brg->out_f<11>().set(FUNC(mex68kecb_state::write_acia_clock<1>)); //  150bps
	m_brg->out_f<13>().set(FUNC(mex68kecb_state::write_acia_clock<0>)); //  110bps

	// Set up PIT and ACIAs.

	PIT68230(config, m_pit, 8_MHz_XTAL / 2);
	ACIA6850(config, m_acia1);
	ACIA6850(config, m_acia2);

	// Set up interrupts.

	// Nothing at IRQ1
	m_pit->timer_irq_callback().set_inputline(m_maincpu, M68K_IRQ_2);
	m_pit->port_irq_callback().set_inputline(m_maincpu, M68K_IRQ_3);
	// Optional 6800 peripherals at IRQ4
	m_acia1->irq_handler().set_inputline(m_maincpu, M68K_IRQ_5);
	m_acia2->irq_handler().set_inputline(m_maincpu, M68K_IRQ_6);
	// Abort button at IRQ7, see abort_button()

	// Set up terminal RS-232.

	RS232_PORT(config, m_terminal, default_rs232_devices, "terminal");
	m_terminal->rxd_handler().set(m_acia1, FUNC(acia6850_device::write_rxd));
	m_terminal->cts_handler().set(m_acia1, FUNC(acia6850_device::write_cts));
	m_terminal->dcd_handler().set(m_acia1, FUNC(acia6850_device::write_dcd));
	m_acia1->txd_handler().set(m_terminal, FUNC(rs232_port_device::write_txd));
	m_acia1->rts_handler().set(m_terminal, FUNC(rs232_port_device::write_rts));

	// Set up host RS-232.

	RS232_PORT(config, m_host, default_rs232_devices, nullptr);
	m_host->rxd_handler().set(m_acia2, FUNC(acia6850_device::write_rxd));
	m_host->cts_handler().set(m_acia2, FUNC(acia6850_device::write_cts));
	m_host->dcd_handler().set(m_acia2, FUNC(acia6850_device::write_dcd));
	m_acia2->txd_handler().set(m_host, FUNC(rs232_port_device::write_txd));
	m_acia2->rts_handler().set(m_host, FUNC(rs232_port_device::write_rts));
}

void mex68kecb_state::mem_map(address_map &map)
{
	map.unmap_value_high();

	map(0x000000, 0x007fff).ram().share("ram"); // 32KB RAM
	map(0x008000, 0x00bfff).rom().region("roms", 0); // 16KB ROM
	map(0x010000, 0x01003f).rw(m_pit, FUNC(pit68230_device::read), FUNC(pit68230_device::write)).umask16(0x00ff);
	map(0x010040, 0x010043).rw(m_acia1, FUNC(acia6850_device::read), FUNC(acia6850_device::write)).umask16(0xff00);
	map(0x010040, 0x010043).rw(m_acia2, FUNC(acia6850_device::read), FUNC(acia6850_device::write)).umask16(0x00ff);

	map(0x000000, 0x000007).view(m_bootvect);
	m_bootvect[0](0x000000, 0x000007).rom().region("roms", 0);              // After first write we act as RAM
	m_bootvect[0](0x000000, 0x000007).w(FUNC(mex68kecb_state::bootvect_w)); // ROM mirror just during reset
}

void mex68kecb_state::machine_reset()
{
	// Reset BRG.
	m_brg->rsa_w(CLEAR_LINE);
	m_brg->rsb_w(ASSERT_LINE);

	// Reset pointer to bootvector in ROM for bootvector view
	m_bootvect.select(0);
}

template <uint8_t Bit>
void mex68kecb_state::write_acia_clock(int state)
{
	if (BIT(m_acia1_baud->read(), Bit)) {
		m_acia1->write_txc(state);
		m_acia1->write_rxc(state);
	}

	if (BIT(m_acia2_baud->read(), Bit)) {
		m_acia2->write_txc(state);
		m_acia2->write_rxc(state);
	}
}

// Abort button handler
INPUT_CHANGED_MEMBER(mex68kecb_state::abort_button)
{
	m_maincpu->set_input_line(M68K_IRQ_7, newval ? CLEAR_LINE : ASSERT_LINE); // active low
}

// Boot vector handler, the PCB hardwires the first 8 bytes from 0x008000 to 0x0 at reset.
void mex68kecb_state::bootvect_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	COMBINE_DATA(&m_sysram[offset]);
	m_bootvect.disable(); // redirect all upcoming accesses to masking RAM until reset.
}


/* ROM definition */
ROM_START( mex68kecb )
	ROM_REGION16_BE(0x4000, "roms", ROMREGION_ERASE00)
	ROM_DEFAULT_BIOS("tutor13")

	ROM_SYSTEM_BIOS(0, "tutor13", "Motorola TUTOR 1.3")
	ROMX_LOAD("tutor13u.bin", 0x000000, 0x002000, CRC(7d11a0e9) SHA1(18ec8899651e78301b406f4fe6d4141c853e9e30), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD("tutor13l.bin", 0x000001, 0x002000, CRC(2bb3a4e2) SHA1(3dac64ec5af4f46a367959ec80677103e3822f20), ROM_SKIP(1) | ROM_BIOS(0) )
ROM_END

} // anonymous namespace


// Driver
//    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY     FULLNAME                            FLAGS
COMP( 1981, mex68kecb, 0,      0,      mex68kecb, mex68kecb, mex68kecb_state, empty_init, "Motorola", "68000 Educational Computer Board", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



mfabfz.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:rfka01
/***************************************************************************

Mikrocomputer fuer Ausbildung
Berufsfoerdungszentrum Essen
Information found on Wikipedia:
- System is a backbone upon which all functions are available on plug-in cards
- 32k RAM, 32k ROM
- Serial is via CPU SID/SOD pins, but can be replaced by 8251 on RS232 card
- Protocol is COM1, 4800, 8N1
- Timer card uses 8253
- PIO card uses 8255 and has a printer interface
- Cassette interface uses RS232 card
- Optional floppy (both sizes); and a EPROM burner
- OS: MAT85

Manuals have no schematics and no mention of the 8253 or 8255. The bios
doesn't try communicating with them either.

Commands:
A     Assembler
B     Set Breakpoint
D     Disassembler
G     Go
H     Help
I     Inport
L     Load memory from tape
M     Print/Modify memory (A=ascii, B=bit, H=hex)
N     Turn on tracer & step to next instruction
O     Outport
P     Display memory contents in various formats
R     Set initial register contents
S     Save memory to tape
T     Trace interval

Pressing enter will change the prompt from KMD > to KMD+> and pressing
space will change it back.

mfabfz85 -bios 0, 3 and 4 work; others produce rubbish.

Cassette:
- Like many early designs, the interface is grossly over-complicated, using 12 chips.
- Similar to Kansas City, except that 1 = 3600Hz, 0 = 2400Hz
- The higher frequencies, only 50% apart, cause the interface to be less reliable
- Baud rates of 150, 300, 600, 1200 selected by a jumper. We emulate 1200 only,
  as the current code would be too unreliable with the lower rates.

****************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "imagedev/cassette.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "bus/rs232/rs232.h"
#include "speaker.h"


namespace {

class mfabfz_state : public driver_device
{
public:
	mfabfz_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cass(*this, "cassette")
		, m_uart(*this, "uart2")
	{ }

	void mfabfz85(machine_config &config);
	void mfabfz(machine_config &config);

private:
	void mfabfz85_io(address_map &map) ATTR_COLD;
	void mfabfz_io(address_map &map) ATTR_COLD;
	void mfabfz_mem(address_map &map) ATTR_COLD;
	void machine_reset() override ATTR_COLD;
	void machine_start() override ATTR_COLD;
	void kansas_r(int state);
	void kansas_w(int state);
	u8 m_cass_data[5]{};
	bool m_cassoutbit = false, m_cassbit = false, m_cassold = false;
	required_device<cpu_device> m_maincpu;
	required_device<cassette_image_device> m_cass;
	required_device<i8251_device> m_uart;
};


void mfabfz_state::mfabfz_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).rom().region("roms", 0);
	map(0x8000, 0xffff).ram();
}

void mfabfz_state::mfabfz_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0xbe, 0xbf).rw("uart1", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xfe, 0xff).rw("uart2", FUNC(i8251_device::read), FUNC(i8251_device::write));
}

void mfabfz_state::mfabfz85_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0xfe, 0xff).rw("uart2", FUNC(i8251_device::read), FUNC(i8251_device::write));
}

/* Input ports */
static INPUT_PORTS_START( mfabfz )
INPUT_PORTS_END

// Note: if the other baud rates are to be supported, then this function
//       will need to be redesigned.
void mfabfz_state::kansas_w(int state)
{
	if ((m_cass->get_state() & CASSETTE_MASK_UISTATE) == CASSETTE_RECORD)
	{
		if (state)
		{
			// incoming @76923Hz (1200), 38461.5 (600), 19231.77 (300), 9615.38 (150)
			u8 twobit = m_cass_data[3] & 63;
			static u8 cycles[3] = { 11, 10, 11 }; // 1200 baud

			if (twobit == 0)
			{
				m_cassold = m_cassoutbit;
				m_cass_data[2] = 0;
				m_cass_data[4] = 0;
			}

			if (m_cass_data[2] == 0)
			{
				m_cassbit ^= 1;
				m_cass->output(m_cassbit ? -1.0 : +1.0);

				if (m_cassold)
				{
					m_cass_data[4]++;
					if (m_cass_data[4] > 2)
						m_cass_data[4] = 0;
					m_cass_data[2] = cycles[m_cass_data[4]]; // 3600 Hz
				}
				else
					m_cass_data[2] = 16; // 2400 Hz
			}

			m_cass_data[2]--;
			m_cass_data[3]++;
		}
	}

	m_uart->write_txc(state);
}

void mfabfz_state::kansas_r(int state)
{
	// incoming @76923Hz
	if (state)
	{
		// no tape - set to idle
		m_cass_data[1]++;
		if (m_cass_data[1] > 32)
		{
			m_cass_data[1] = 32;
			m_uart->write_rxd(1);
		}

		if ((m_cass->get_state() & CASSETTE_MASK_UISTATE) != CASSETTE_PLAY)
			return;

		/* cassette - turn 2400/3600Hz to a bit */
		uint8_t cass_ws = (m_cass->input() > +0.04) ? 1 : 0;

		if (cass_ws != m_cass_data[0])
		{
			m_cass_data[0] = cass_ws;
			m_uart->write_rxd((m_cass_data[1] < 14) ? 1 : 0);
			m_cass_data[1] = 0;
		}
	}

	m_uart->write_rxc(state);
}

void mfabfz_state::machine_reset()
{
	m_cass_data[0] = m_cass_data[1] = m_cass_data[2] = m_cass_data[3] = m_cass_data[4] = 0;
	m_cassoutbit = m_cassold = m_cassbit = 1;
	m_uart->write_rxd(1);
	m_uart->write_cts(0);
}

void mfabfz_state::machine_start()
{
	save_item(NAME(m_cass_data));
	save_item(NAME(m_cassoutbit));
	save_item(NAME(m_cassbit));
	save_item(NAME(m_cassold));
}

void mfabfz_state::mfabfz(machine_config &config)
{
	/* basic machine hardware */
	I8085A(config, m_maincpu, 4_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &mfabfz_state::mfabfz_mem);
	m_maincpu->set_addrmap(AS_IO, &mfabfz_state::mfabfz_io);

	// uart1 - terminal
	clock_device &uart1_clock(CLOCK(config, "uart1_clock", 4_MHz_XTAL / 26));
	uart1_clock.signal_handler().set("uart1", FUNC(i8251_device::write_txc));
	uart1_clock.signal_handler().append("uart1", FUNC(i8251_device::write_rxc));

	i8251_device &uart1(I8251(config, "uart1", 0));
	uart1.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	uart1.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	uart1.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("uart1", FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set("uart1", FUNC(i8251_device::write_dsr));
	rs232.cts_handler().set("uart1", FUNC(i8251_device::write_cts));

	// uart2 - cassette - clock comes from 2MHz through a divider consisting of 4 chips and some jumpers.
	I8251(config, m_uart, 4_MHz_XTAL / 2);
	m_uart->txd_handler().set([this] (bool state) { m_cassoutbit = state; });

	clock_device &uart_clock(CLOCK(config, "uart_clock", 4_MHz_XTAL / 52));
	uart_clock.signal_handler().set(FUNC(mfabfz_state::kansas_w));
	uart_clock.signal_handler().append(FUNC(mfabfz_state::kansas_r));

	// cassette is connected to the uart
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	SPEAKER(config, "mono").front_center();
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
}

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_4800 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_4800 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END

void mfabfz_state::mfabfz85(machine_config &config)
{
	/* basic machine hardware */
	i8085a_cpu_device &maincpu(I8085A(config, m_maincpu, 4_MHz_XTAL / 2));
	maincpu.set_addrmap(AS_PROGRAM, &mfabfz_state::mfabfz_mem);
	maincpu.set_addrmap(AS_IO, &mfabfz_state::mfabfz85_io);
	maincpu.in_sid_func().set("rs232", FUNC(rs232_port_device::rxd_r));
	maincpu.out_sod_func().set("rs232", FUNC(rs232_port_device::write_txd)).invert();

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	I8251(config, m_uart, 4_MHz_XTAL / 2);
	m_uart->txd_handler().set([this] (bool state) { m_cassoutbit = state; });

	clock_device &uart_clock(CLOCK(config, "uart_clock", 4_MHz_XTAL / 52));
	uart_clock.signal_handler().set(FUNC(mfabfz_state::kansas_w));
	uart_clock.signal_handler().append(FUNC(mfabfz_state::kansas_r));

	// cassette is connected to the uart
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	SPEAKER(config, "mono").front_center();
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
}


/* ROM definition */
ROM_START( mfabfz )
	ROM_REGION( 0x8000, "roms", 0 ) // MAT32K, 1986, works
	ROM_LOAD( "mfa_mat32k_vers.1.8-t_ic0.bin", 0x0000, 0x8000, CRC(6cba989e) SHA1(81611b6250a5319e5d28af5ce3a1e261af8315ae) )
ROM_END

ROM_START( mfabfz85 )
	ROM_REGION( 0x8000, "roms", 0 )
	ROM_SYSTEM_BIOS( 0, "32k", "MAT32K v1.8s" ) // 1982, 4800, 8N2, txd-invert
	ROMX_LOAD( "mfa_mat32k_vers.1.8-s_ic0.bin", 0x0000, 0x8000, CRC(021d7dff) SHA1(aa34b3a8bac52fc7746d35f5ffc6328734788cc2), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "8k", "MAT85 8k" ) // 1982, not working
	ROMX_LOAD( "mfa_mat_1_0000.bin", 0x0000, 0x0800, CRC(73b588ea) SHA1(2b9570fe44c3c19d6aa7c7c11ecf390fa5d48998), ROM_BIOS(1) )
	ROMX_LOAD( "mfa_mat_2_0800.bin", 0x0800, 0x0800, CRC(13f5be91) SHA1(2b9d64600679bab319a37381fc84e874c3b2a877), ROM_BIOS(1) )
	ROMX_LOAD( "mfa_mat_3_1000.bin", 0x1000, 0x0800, CRC(c9b91bb4) SHA1(ef829964f507b1f6bbcf3c557c274fe728636efe), ROM_BIOS(1) )
	ROMX_LOAD( "mfa_mat_4_1800.bin", 0x1800, 0x0800, CRC(649cd7f0) SHA1(e92f29c053234b36f22d525fe92e61bf24476f14), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "16k_set1", "MAT85+ 16k set1" ) // not working
	ROMX_LOAD( "mfa_mat85_0x0000-0x07ff.bin", 0x0000, 0x0800, CRC(73b588ea) SHA1(2b9570fe44c3c19d6aa7c7c11ecf390fa5d48998), ROM_BIOS(2) )
	ROMX_LOAD( "mfa_mat85_0x0800-0x0fff.bin", 0x0800, 0x0800, CRC(13f5be91) SHA1(2b9d64600679bab319a37381fc84e874c3b2a877), ROM_BIOS(2) )
	ROMX_LOAD( "mfa_mat85_0x1000-0x17ff.bin", 0x1000, 0x0800, CRC(c9b91bb4) SHA1(ef829964f507b1f6bbcf3c557c274fe728636efe), ROM_BIOS(2) )
	ROMX_LOAD( "mfa_mat85_0x1800-0x1fff.bin", 0x1800, 0x0800, CRC(649cd7f0) SHA1(e92f29c053234b36f22d525fe92e61bf24476f14), ROM_BIOS(2) )
	ROMX_LOAD( "mfa_mat85_0x2000-0x27ff.bin", 0x2000, 0x0800, CRC(d3592915) SHA1(68daec6c5c63692bc147b1710b9c45ca780f2c7b), ROM_BIOS(2) )
	ROMX_LOAD( "mfa_mat85_0x2800-0x2fff.bin", 0x2800, 0x0800, CRC(9a6aafa9) SHA1(af897e91cc2ce5d6e49fa88c920ad85e1f0209bf), ROM_BIOS(2) )
	ROMX_LOAD( "mfa_mat85_0x3000-0x37ff.bin", 0x3000, 0x0800, CRC(eae4e3d5) SHA1(f7112965874417bbfc4a32f31f84e1db83249ab7), ROM_BIOS(2) )
	ROMX_LOAD( "mfa_mat85_0x3800-0x3fff.bin", 0x3800, 0x0800, CRC(536db0e3) SHA1(328ccc18455f710390c29c0fd0f4b0713a4a69ae), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "16k_set2", "MAT85+ 16k set2" ) // 2400, 7N2, txd-invert
	ROMX_LOAD( "mat85_1_1of8.bin", 0x0000, 0x0800, CRC(73b588ea) SHA1(2b9570fe44c3c19d6aa7c7c11ecf390fa5d48998), ROM_BIOS(3) )
	ROMX_LOAD( "mat85_2_2of8.bin", 0x0800, 0x0800, CRC(c97acc82) SHA1(eedb27c19a2d21b5ec5bca6cafeb25584e21e500), ROM_BIOS(3) )
	ROMX_LOAD( "mat85_3_3of8.bin", 0x1000, 0x0800, CRC(c9b91bb4) SHA1(ef829964f507b1f6bbcf3c557c274fe728636efe), ROM_BIOS(3) )
	ROMX_LOAD( "mat85_4_4of8.bin", 0x1800, 0x0800, CRC(649cd7f0) SHA1(e92f29c053234b36f22d525fe92e61bf24476f14), ROM_BIOS(3) )
	ROMX_LOAD( "soft_1_5of8.bin",  0x2000, 0x0800, CRC(98d9e86e) SHA1(af78b370fe97a6017b192dadec4059256ee4f4c7), ROM_BIOS(3) )
	ROMX_LOAD( "soft_2_6of8.bin",  0x2800, 0x0800, CRC(81fc3b24) SHA1(186dbd389fd700c5af1ef7c37948e11701ec596e), ROM_BIOS(3) )
	ROMX_LOAD( "soft_3_7of8.bin",  0x3000, 0x0800, CRC(eae4e3d5) SHA1(f7112965874417bbfc4a32f31f84e1db83249ab7), ROM_BIOS(3) )
	ROMX_LOAD( "soft_4_8of8.bin",  0x3800, 0x0800, CRC(536db0e3) SHA1(328ccc18455f710390c29c0fd0f4b0713a4a69ae), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS (4, "32k_dtp", "MAT32K dtp" ) // 2400, 7N2, txd-invert
	ROMX_LOAD( "mfa_mat85_sp1_ed_kpl_dtp_terminal.bin", 0x0000, 0x8000, CRC(ed432c19) SHA1(31cbc06d276dbb201d50967f4ddba26a42560753), ROM_BIOS(4) )
ROM_END

} // anonymous namespace


/*    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT   CLASS,        INIT        COMPANY                         FULLNAME                               FLAGS */
COMP( 1979, mfabfz,   0,      0,      mfabfz,   mfabfz, mfabfz_state, empty_init, "Berufsfoerdungszentrum Essen", "Mikrocomputer fuer Ausbildung",       MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1979, mfabfz85, mfabfz, 0,      mfabfz85, mfabfz, mfabfz_state, empty_init, "Berufsfoerdungszentrum Essen", "Mikrocomputer fuer Ausbildung MAT85", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



mg1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * Whitechapel Computer Works MG-1 (Milliard Gargantubrain)
 *
 * Sources:
 *   - http://www.cpu-ns32k.net/Whitechapel.html
 *
 * TODO:
 *   - mouse
 */
/*
 * WIP
 *  - boots 42nix from hard disk image
 *  - cursor sometimes "trapped"
 *  - keyboard intermittently outputs garbage
 */

#include "emu.h"

#include "kbd.h"

#include "bus/rs232/rs232.h"
#include "cpu/ns32000/ns32000.h"
#include "cpu/m6800/m6801.h"
#include "machine/am79c90.h"
#include "machine/am9516.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/input_merger.h"
#include "machine/mm58174.h"
#include "machine/ns32081.h"
#include "machine/ns32082.h"
#include "machine/ns32202.h"
#include "machine/nvram.h"
#include "machine/pit8253.h"
#include "machine/ram.h"
#include "machine/upd7261.h"
#include "machine/wd_fdc.h"
#include "sound/spkrdev.h"
#include "video/mc6845.h"

#include "imagedev/floppy.h"
#include "formats/applix_dsk.h"

#include "screen.h"
#include "speaker.h"

#include "mg1.lh"

#define VERBOSE 0
#include "logmacro.h"

namespace {

class mg1_state : public driver_device
{
public:
	mg1_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_fpu(*this, "fpu")
		, m_mmu(*this, "mmu")
		, m_icu(*this, "icu")
		, m_ram(*this, "ram")
		, m_sram(*this, "sram")
		, m_iop(*this, "iop")
		, m_iop_sram(*this, "iop_sram")
		, m_iop_ctc(*this, "iop_ctc")
		, m_dma(*this, "dma%u", 0U)
		, m_usart(*this, "usart")
		, m_serial(*this, "serial")
		, m_rtc(*this, "rtc")
		, m_fdc(*this, "fdc")
		, m_fdd(*this, "fdc:0:35dd")
		, m_net(*this, "net")
		, m_hdc(*this, "hdc")
		, m_ctc(*this, "ctc")
		, m_crtc(*this, "crtc")
		, m_screen(*this, "screen")
		, m_vmram(*this, "vmram")
		, m_kbd(*this, "kbd")
		, m_buzzer(*this, "buzzer")
		, m_buzzen(*this, "buzzer_enable")
		, m_mouse_buttons(*this, "MOUSE_B")
		, m_mouse_axis(*this, "MOUSE_%c", 'X')
		, m_led_err(*this, "led_err")
		, m_led_fdd(*this, "led_fdd")
	{
	}

	// machine config
	void mg1(machine_config &config);

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// address maps
	template <unsigned ST> void cpu_map(address_map &map) ATTR_COLD;
	void iop_map(address_map &map) ATTR_COLD;
	void dma_map(address_map &map) ATTR_COLD;

private:
	MC6845_UPDATE_ROW(update_row);
	MC6845_END_UPDATE(draw_cursor);

	template <unsigned Axis> u8 mouse_axis_r();

	// devices
	required_device<ns32016_device> m_cpu;
	required_device<ns32081_device> m_fpu;
	required_device<ns32082_device> m_mmu;
	required_device<ns32202_device> m_icu;
	required_device<ram_device> m_ram;
	required_device<nvram_device> m_sram;

	required_device<mc68121_device> m_iop;
	required_device<nvram_device> m_iop_sram;

	required_device<pit8253_device> m_iop_ctc;

	required_device_array<am9516_device, 2> m_dma;
	required_device<i8251_device> m_usart;
	required_device<rs232_port_device> m_serial;
	required_device<mm58174_device> m_rtc;
	required_device<wd1770_device> m_fdc;
	required_device<floppy_image_device> m_fdd;
	required_device<am7990_device> m_net;

	required_device<upd7261_device> m_hdc;
	required_device<pit8253_device> m_ctc;

	required_device<mc6845_device> m_crtc;
	required_device<screen_device> m_screen;
	required_shared_ptr<u16> m_vmram;

	required_device<mg1_kbd_device> m_kbd;

	required_device<speaker_sound_device> m_buzzer;
	required_device<input_merger_all_high_device> m_buzzen;

	required_ioport m_mouse_buttons;
	required_ioport_array<2> m_mouse_axis;

	output_finder<> m_led_err;
	output_finder<> m_led_fdd;

	u8 m_sem[6] = { 0xc0, 0x80, 0xc0, 0xc0, 0xc0, 0xc0 };
	u8 m_iop_p2;

	s16 m_mouse[2];

	struct cursor_cnt
	{
		bool partial;
		u16 data;
		s16 value;
	}
	m_cursor_cnt[2];

	u8 m_cursor_reg;  // cursor page number and x offset
	bool m_cursor_fn; // cursor function (1=nor, 0=xnor)
	bool m_dispen;    // display enable
};

void mg1_state::machine_start()
{
	m_led_err.resolve();
	m_led_fdd.resolve();

	save_item(NAME(m_sem));
	save_item(NAME(m_iop_p2));
	save_item(NAME(m_mouse));
	save_item(STRUCT_MEMBER(m_cursor_cnt, partial));
	save_item(STRUCT_MEMBER(m_cursor_cnt, data));
	save_item(STRUCT_MEMBER(m_cursor_cnt, value));
	save_item(NAME(m_cursor_reg));
	save_item(NAME(m_cursor_fn));
	save_item(NAME(m_dispen));
}

void mg1_state::machine_reset()
{
	m_fdc->set_floppy(m_fdd);
	m_iop_p2 = 0;

	m_mouse[0] = 0;
	m_mouse[1] = 0;

	for (cursor_cnt &c : m_cursor_cnt)
	{
		c.partial = false;
		c.data = 0;
		c.value = 0;
	}

	m_cursor_reg = 0;
	m_cursor_fn = false;
	m_dispen = false;

	// HACK: capture the counter 1 and 2 values which control the hardware cursor location
	m_iop->space(0).install_write_tap(0x20e1, 0x20e2, "cursor_cnt_w",
		[this](offs_t offset, u8 &data, u8 mem_mask)
		{
			cursor_cnt &c = m_cursor_cnt[(offset & 3) - 1];

			// data is written least-significant byte first
			c.data = u16(data) << 8 | (c.data >> 8);
			c.partial = !c.partial;

			// convert counter values to pixel coordinates after each update
			if (!c.partial)
			{
				switch (offset & 3)
				{
				case 1:
					// counter 1 holds cursor y position multiplied by number of characters
					// per line (20), plus an offset (0x4d); except for lines <1
					c.value = (c.data > 0x4c) ? (c.data - 0x4d) / 0x14 : (c.data - 0x4d);
					break;
				case 2:
					// counter 2 holds cursor x position in character columns (+1)
					c.value = (c.data - 1) * 64;
					break;
				}
			}
		});
}

template <unsigned ST> void mg1_state::cpu_map(address_map &map)
{
	// rom page
	map(0x000000, 0x003fff).mirror(0xef4000).rom().region("prom", 0);
	map(0x008000, 0x008fff).mirror(0xef3000).ram().share("sram");  // 2xTC5516AP 2048x8 SRAM
	map(0x00c000, 0x00cfff).mirror(0xef3000).ram().share("vmram"); // 2xD4016C-3 2048x8 SRAM

	// i/o page
	//map(0x308000, 0x3081ff).mirror(0xcf6000); // wcw reserved
	map(0x308200, 0x3083ff).mirror(0xcf6000).umask16(0x00ff).lw8(
		[this](u8 data)
		{
			if (BIT(data, 2))
			{
				// DRAM-ON
				m_cpu->space(0).unmap_readwrite(0x000000, 0xbfffff);
				m_cpu->space(0).install_ram(0, m_ram->mask(), m_ram->pointer());
			}
		}, "dma_reg_w");
	//map(0x308400, 0x3085ff).mirror(0xcf6000); // wcw reserved
	map(0x30862e, 0x30863b).mirror(0xcf6000).umask16(0x00ff).lrw8(
		[this](offs_t offset)
		{
			u8 const data = m_sem[offset];
			if (!BIT(data, 7))
				m_sem[offset] |= 0x80;

			return data;
		}, "cpu_sem_r",
		[this](offs_t offset, u8 data)
		{
			m_sem[offset] &= ~0x80;
		}, "cpu_sem_w");
	map(0x308700, 0x3087ff).mirror(0xcf6000).umask16(0x00ff).lrw8(
		[this](offs_t offset)
		{
			return m_iop->dpram_r(offset);
		}, "iop_ram_r",
		[this](offs_t offset, u8 data)
		{
			if (offset == 18 + m_iop->dpram_r(3))
				logerror("iop command %d\n", data);

			m_iop->dpram_w(offset, data);
		}, "iop_ram_w");
	//map(0x308800, 0x3089ff).mirror(0xcf6000); // ctc
	//map(0x308a00, 0x308bff).mirror(0xcf6000); // raster-op function ctl
	//map(0x308c00, 0x308dff).mirror(0xcf6000); // raster-op

	map(0x308e00, 0x308e01).mirror(0xcf6000).rw(m_dma[0], FUNC(am9516_device::data_r), FUNC(am9516_device::data_w));
	map(0x308e02, 0x308e03).mirror(0xcf6000).rw(m_dma[0], FUNC(am9516_device::addr_r), FUNC(am9516_device::addr_w));
	map(0x308e04, 0x308e05).mirror(0xcf6000).rw(m_dma[1], FUNC(am9516_device::data_r), FUNC(am9516_device::data_w));
	map(0x308e06, 0x308e07).mirror(0xcf6000).rw(m_dma[1], FUNC(am9516_device::addr_r), FUNC(am9516_device::addr_w));

	map(0x309000, 0x309003).mirror(0xcf6000).umask16(0x00ff).rw(m_usart, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x309200, 0x3093ff).mirror(0xcf6000).rw(m_net, FUNC(am7990_device::regs_r), FUNC(am7990_device::regs_w));
	map(0x309400, 0x3095ff).mirror(0xcf6000).umask16(0x00ff).rw(m_rtc, FUNC(mm58174_device::read), FUNC(mm58174_device::write));

	map(0x309600, 0x309603).mirror(0xcf6000).m(m_hdc, FUNC(upd7261_device::map)).umask16(0x00ff);

	map(0x309800, 0x309807).mirror(0xcf6000).umask16(0x00ff).rw(m_fdc, FUNC(wd1770_device::read), FUNC(wd1770_device::write));

	map(0x309a00, 0x309bff).mirror(0xcf6000).umask16(0x00ff).lw8(
		[this](u8 data)
		{
			m_fdd->ss_w(BIT(data, 0));
			m_fdc->dden_w(BIT(data, 1));
			m_led_fdd = !BIT(data, 2);
			m_led_err = BIT(data, 3);
			m_hdc->head_w(BIT(data, 4) ? 0x08 : 0x00);
		}, "fdc_reg_w");

	//map(0x309c00, 0x309dff).mirror(0xcf6000); // dma interrupt acknowledge
	map(0x309e00, 0x309e3f).mirror(0xcf6000).umask16(0x00ff).m(m_icu, FUNC(ns32202_device::map<BIT(ST, 1)>));
}

void mg1_state::iop_map(address_map &map)
{
	map(0x0017, 0x001c).lrw8(
		[this](offs_t offset)
		{
			u8 const data = m_sem[offset];
			if (!BIT(data, 7))
				m_sem[offset] |= 0xc0;

			return data;
		}, "iop_sem_r",
		[this](offs_t offset, u8 data)
		{
			m_sem[offset] &= ~0x80;
		}, "iop_sem_w");

	// i/o area
	map(0x2000, 0x201f).mirror(0x1e00).r(FUNC(mg1_state::mouse_axis_r<0>));
	map(0x2000, 0x201f).mirror(0x1e00).lw8([this](u8 data) { m_buzzen->in_w<1>(BIT(data, 0)); }, "buzzen_w");
	map(0x2020, 0x2020).mirror(0x1e00).lw8([this](offs_t offset, u8 data) { logerror("host reset %x,0x%02x\n", offset, data); }, "host_reset").select(0x0100);
	map(0x2040, 0x205f).mirror(0x1e00).lw8([this](u8 data) { m_cursor_reg = data; }, "cursor_reg_w");
	map(0x2060, 0x207f).mirror(0x1e00).lw8([this](u8 data) { m_icu->ir_w<10>(1); m_icu->ir_w<10>(0); }, "iopint");
	map(0x2080, 0x2080).mirror(0x1e00).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x2081, 0x2081).mirror(0x1e00).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x20a0, 0x20bf).mirror(0x1e00).lrw8(
		[this]() { return m_mouse_buttons->read(); }, "mouse_b_r",
		[this](u8 data) { m_cursor_fn = BIT(data, 0); }, "cursor_fn_w");
	map(0x20c0, 0x20df).mirror(0x1e00).r(FUNC(mg1_state::mouse_axis_r<1>));
	map(0x20c0, 0x20df).mirror(0x1e00).lw8([this](u8 data) { m_dispen = BIT(data, 0); }, "dispen_w");
	map(0x20e0, 0x20ff).mirror(0x1e00).rw(m_iop_ctc, FUNC(pit8253_device::read), FUNC(pit8253_device::write));

	map(0x4000, 0x47ff).mirror(0x3800).ram().share("iop_sram"); // D4016C-3 2048x8 SRAM
	map(0x8000, 0xbfff).mirror(0x4000).rom().region("iop_prom", 0);
}

void mg1_state::dma_map(address_map &map)
{
	// system memory buffers swap DMA byte lanes
	map(0x000000, 0xffffff).lrw16(
		[this](offs_t offset, u16 mem_mask) { return swapendian_int16(m_cpu->space(0).read_word(offset << 1, swapendian_int16(mem_mask))); }, "dma_r",
		[this](offs_t offset, u16 data, u16 mem_mask) { m_cpu->space(0).write_word(offset << 1, swapendian_int16(data), swapendian_int16(mem_mask)); }, "dma_w");
}

template <unsigned Axis> u8 mg1_state::mouse_axis_r()
{
	s16 const value = m_mouse_axis[Axis]->read();
	s8 const delta = std::clamp<s16>(value - m_mouse[Axis], -128, 127);

	m_mouse[Axis] = value;

	return delta;
}

static INPUT_PORTS_START(mg1)
	PORT_START("MOUSE_B")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("Mouse Left Button")      PORT_CODE(MOUSECODE_BUTTON1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("Mouse Middle Button")    PORT_CODE(MOUSECODE_BUTTON3)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON3) PORT_NAME("Mouse Right Button")     PORT_CODE(MOUSECODE_BUTTON2)

	PORT_START("MOUSE_X")
	PORT_BIT(0xffff, 0x0000, IPT_MOUSE_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(0)

	PORT_START("MOUSE_Y")
	PORT_BIT(0xffff, 0x0000, IPT_MOUSE_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(0)
INPUT_PORTS_END

MC6845_UPDATE_ROW(mg1_state::update_row)
{
	if (!m_dispen)
		return;

	// 16 columns x 50 rows of characters, each 64x16 pixels
	// 10 bits look up video mapping ram -> va6-21 (16 bits)
	// 6 bits give address of 64-bit character in page

	for (unsigned column = 0; column < x_count; column++)
	{
		u16 const vma = ((ma & 0x0ff0) << 4) | ((ra & 0x0f) << 4) | column;
		u32 const va = (u32(m_vmram[0x400 + (vma >> 6)]) << 6) | (vma & 0x3f);

		for (unsigned byte = 0; byte < 8; byte++)
		{
			u8 const data = m_ram->read((va << 3) | BYTE8_XOR_LE(byte));
			unsigned const x = column * 64 + byte * 8;

			bitmap.pix(y, x + 0) = BIT(data, 0) ? rgb_t::black() : rgb_t::white();
			bitmap.pix(y, x + 1) = BIT(data, 1) ? rgb_t::black() : rgb_t::white();
			bitmap.pix(y, x + 2) = BIT(data, 2) ? rgb_t::black() : rgb_t::white();
			bitmap.pix(y, x + 3) = BIT(data, 3) ? rgb_t::black() : rgb_t::white();
			bitmap.pix(y, x + 4) = BIT(data, 4) ? rgb_t::black() : rgb_t::white();
			bitmap.pix(y, x + 5) = BIT(data, 5) ? rgb_t::black() : rgb_t::white();
			bitmap.pix(y, x + 6) = BIT(data, 6) ? rgb_t::black() : rgb_t::white();
			bitmap.pix(y, x + 7) = BIT(data, 7) ? rgb_t::black() : rgb_t::white();
		}
	}
}

MC6845_END_UPDATE(mg1_state::draw_cursor)
{
	if (!m_dispen)
	{
		bitmap.fill(rgb_t::black(), cliprect);
		return;
	}

	s32 const cursor_y = m_cursor_cnt[0].value;
	s32 const cursor_x = m_cursor_cnt[1].value - BIT(m_cursor_reg, 2, 6);

	rectangle cursor(cursor_x, cursor_x + 63, cursor_y, cursor_y + 63);
	cursor &= cliprect;

	for (s32 y = cursor.top(); y <= cursor.bottom(); y++)
	{
		// vma0-5 from counter, vma6-7 from register, vma8-15 high, vma16 low
		u16 const vma = 0xff00 | BIT(m_cursor_reg, 0, 2) << 6;
		u32 const va = (u32(m_vmram[vma >> 6]) << 6) + y - cursor_y;

		// read first displayed byte of cursor data
		unsigned bit = cursor.left() - cursor_x;
		u8 data = m_ram->read((va << 3) | BYTE8_XOR_LE(bit >> 3));

		for (s32 x = cursor.left(); x <= cursor.right(); x++)
		{
			// apply cursor function
			if (BIT(data, bit++ & 7))
			{
				if (m_cursor_fn)
					bitmap.pix(y, x) = rgb_t::black();
				else
					bitmap.pix(y, x) ^= rgb_t::white();
			}

			// read next byte of cursor data
			if (!(bit & 7) && (bit < 64))
				data = m_ram->read((va << 3) | BYTE8_XOR_LE(bit >> 3));
		}
	}
}

static void floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();

	// TODO: unrelated to applix, but same logical format
	fr.add(FLOPPY_APPLIX_FORMAT);
}

void mg1_state::mg1(machine_config &config)
{
	NS32016(config, m_cpu, 16_MHz_XTAL / 2);
	m_cpu->set_addrmap(0, &mg1_state::cpu_map<0>);
	m_cpu->set_addrmap(6, &mg1_state::cpu_map<6>);
	m_cpu->set_fpu(m_fpu);
	m_cpu->set_mmu(m_mmu);

	NS32081(config, m_fpu, 16_MHz_XTAL / 2);

	NS32082(config, m_mmu, 16_MHz_XTAL / 2);

	NS32202(config, m_icu, 5_MHz_XTAL);
	m_icu->out_int().set_inputline(m_cpu, INPUT_LINE_IRQ0).invert();

	/*
	 *  0              edge    lo
	 *  1              level   hi
	 *  2  busint2     level   lo
	 *  3  winint      level   hi
	 *  4  dma2int     edge    lo
	 *  5  busint3     level   lo
	 *  6              level   lo
	 *  7  busint4     level   lo
	 *  8  fdcintr     level   hi
	 *  9  busint5     edge    lo
	 * 10  iopint      edge    lo
	 * 11              edge    hi
	 * 12  busint6     edge    lo
	 * 13  (not used)  edge    lo
	 * 14  (not used)  edge    lo
	 * 15  (not used)  edge    lo
	 *
	 * tpl  0x090a
	 * eltg 0x01ee
	 *
	 *
	 */

	RAM(config, m_ram).set_default_size("2M").set_extra_options("4M,6M,8M").set_default_value(0);

	NVRAM(config, m_sram); // 2xTC5516AP 2048x8 SRAM

	MC68121(config, m_iop, 8_MHz_XTAL / 8); // MC68121 (mode 2)
	m_iop->set_addrmap(0, &mg1_state::iop_map);
	m_iop->in_p2_cb().set([this]() { return m_iop_p2; });

	NVRAM(config, m_iop_sram); // 1xD4016C-3 2048x8 SRAM

	PIT8253(config, m_iop_ctc);
	m_iop_ctc->set_clk<0>(8_MHz_XTAL / 8);
	m_iop_ctc->out_handler<0>().set(m_buzzen, FUNC(input_merger_all_high_device::in_w<0>));

	INPUT_MERGER_ALL_HIGH(config, m_buzzen);
	m_buzzen->output_handler().set(m_buzzer, FUNC(speaker_sound_device::level_w));

	// channel 1 & 2 cursor position
	m_iop_ctc->set_clk<1>(60_MHz_XTAL / 64);
	m_iop_ctc->set_clk<2>(60_MHz_XTAL / 64);
	// out2 -> cout2 (triggered every line)
	// out1 -> cout1 (goes low when cursor required on this line)

	AM9516(config, m_dma[0], 8_MHz_XTAL / 2); // graphics (not used)

	AM9516(config, m_dma[1], 8_MHz_XTAL / 2); // general, ch1 hard disk, ch2 -> J3 or floppy
	m_dma[1]->out_int().set(m_icu, FUNC(ns32202_device::ir_w<4>));
	m_dma[1]->set_addrmap(am9516_device::SYSTEM_MEM, &mg1_state::dma_map);

	I8251(config, m_usart, m_iop->clock());
	m_icu->out_cout().set(m_usart, FUNC(i8251_device::rx_clock_w));
	m_icu->out_cout().append(m_usart, FUNC(i8251_device::tx_clock_w));
	m_usart->rxrdy_handler().set(m_icu, FUNC(ns32202_device::ir_w<1>));
	m_usart->txrdy_handler().set(m_icu, FUNC(ns32202_device::ir_w<11>));

	RS232_PORT(config, m_serial, default_rs232_devices, nullptr);
	m_serial->rxd_handler().set(m_usart, FUNC(i8251_device::write_rxd));
	m_serial->dsr_handler().set(m_usart, FUNC(i8251_device::write_dsr));
	m_serial->dcd_handler().set(m_usart, FUNC(i8251_device::write_cts));
	m_usart->txd_handler().set(m_serial, FUNC(rs232_port_device::write_txd));
	m_usart->dtr_handler().set(m_serial, FUNC(rs232_port_device::write_dtr));
	m_usart->rts_handler().set(m_serial, FUNC(rs232_port_device::write_rts));
	// TODO: tset/rset

	MM58174(config, m_rtc, 32.768_kHz_XTAL);

	HD6845S(config, m_crtc, 60_MHz_XTAL / 64);
	m_crtc->set_show_border_area(false);
	m_crtc->set_hpixels_per_column(64);
	m_crtc->set_update_row_callback(FUNC(mg1_state::update_row));
	m_crtc->set_end_update_callback(FUNC(mg1_state::draw_cursor));
	m_crtc->out_vsync_callback().set(m_iop_ctc, FUNC(pit8253_device::write_gate1));
	m_crtc->out_hsync_callback().set(m_iop_ctc, FUNC(pit8253_device::write_gate2));

	// black & white crt, 56Hz refresh, 46.877kHz line, line sync 1.066uS, frame sync 341.32uS
	// crtc sees it as 16 col x 50 rows, with 64x16 character cells
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(60_MHz_XTAL, 20*64, 1*64, 17*64, 51*16 + 4, 0, 50*16);
	m_screen->set_screen_update(m_crtc, FUNC(mc6845_device::screen_update));
	m_screen->screen_vblank().set_inputline(m_iop, M6801_TIN_LINE);

	AM7990(config, m_net);
	m_net->intr_out().set(m_icu, FUNC(ns32202_device::ir_w<6>));
	m_net->dma_in().set([this](offs_t offset) { return m_cpu->space(0).read_word(offset); });
	m_net->dma_out().set([this](offs_t offset, u16 data, u16 mem_mask) { m_cpu->space(0).write_word(offset, data, mem_mask); });

	UPD7261(config, m_hdc, 10_MHz_XTAL);
	m_hdc->out_dreq().set(m_dma[1], FUNC(am9516_device::dreq_w<0>)).invert();
	m_hdc->out_int().set(m_icu, FUNC(ns32202_device::ir_w<3>));

	HARDDISK(config, "hdc:0");
	HARDDISK(config, "hdc:1");

	WD1770(config, m_fdc, 8_MHz_XTAL);
	m_fdc->intrq_wr_callback().set(m_icu, FUNC(ns32202_device::ir_w<8>));
	m_fdc->drq_wr_callback().set(m_dma[1], FUNC(am9516_device::dreq_w<1>)).invert();

	// format is 80 tracks, 5 sectors/track, 1024 bytes/sector
	FLOPPY_CONNECTOR(config, "fdc:0", "35dd", FLOPPY_35_DD, true, floppy_formats).enable_sound(true);

	PIT8253(config, m_ctc);

	MG1_KBD(config, m_kbd);
	m_kbd->out_data().set(
		[this](int state)
		{
			if (state)
				m_iop_p2 |= 0x08;
			else
				m_iop_p2 &= ~0x08;
		});

	/*
	 * Documentation indicates the serial clock for the IOP should be driven by the keyboard, however the available
	 * keyboard firmware does not produce a compatible clock output. A hand-drawn sketch indicates the system front
	 * panel allows the serial clock to be generated by a 2.4576MHz crystal and a 4060 divider. The default strapping
	 * selects a 256 divisor, giving a 9600Hz clock which after the 8x divider in the IOP gives a 1200 baud data rate.
	 */
	clock_device &kbd_clk(CLOCK(config, "kbd_clock", 2.4576_MHz_XTAL / 256));
	kbd_clk.signal_handler().set([this](int state) { if (state) m_iop->clock_serial(); });

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_buzzer);
	m_buzzer->add_route(ALL_OUTPUTS, "mono", 0.50);

	//SOFTWARE_LIST(config, "flop_list").set_original("mg1_flop");
	//SOFTWARE_LIST(config, "hdd_list").set_original("mg1_hdd");

	config.set_default_layout(layout_mg1);
}

ROM_START(mg1)
	ROM_REGION16_LE(0x4000, "prom", 0)
	ROM_SYSTEM_BIOS(0, "260", "v2.60")
	ROMX_LOAD("sys_260_even.u291", 0x0000, 0x2000, CRC(24b45b73) SHA1(04d86587b104aa122ac395aa39eb92a1f4d68def), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("sys_260_odd.u292",  0x0001, 0x2000, CRC(a46ebbf8) SHA1(a2ab9fa3a9576d63d8d49730bfcd58a0f508b30f), ROM_BIOS(0) | ROM_SKIP(1))

	// floppy support seems to work better with this firmware version
	ROM_SYSTEM_BIOS(1, "251", "v2.51")
	ROMX_LOAD("even_2.51__sys__13.1_u291.u291", 0x0000, 0x2000, CRC(677cab3c) SHA1(d0197b45ddb1ddd8cd125727312b06dcae0f984a), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("odd_2.51__sys__13.1_u292.u292",  0x0001, 0x2000, CRC(b0134a98) SHA1(a81bd4987030b09799bad0c3bc758ea8aed8cd2f), ROM_BIOS(1) | ROM_SKIP(1))

	ROM_REGION(0x4000, "iop_prom", 0)
	ROM_LOAD("3.0__iop__24.7.87.u285", 0x0000, 0x2000, CRC(733cd089) SHA1(31ffdd85b4ae2ac35dcde292a0d42860baaba88d))
	ROM_RELOAD(                        0x2000, 0x2000)
ROM_END

} // anonymous namespace

/*   YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY                       FULLNAME  FLAGS */
COMP(1984, mg1,  0,      0,      mg1,     mg1,   mg1_state, empty_init, "Whitechapel Computer Works", "MG-1",   MACHINE_NOT_WORKING)



mice.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Miodrag Milanovic
/***************************************************************************

Microtek International Inc. MICE (Micro-In-Circuit Emulator)

2013-08-27 Skeleton driver.

This is a CPU emulator for development work.

Each emulated CPU has its own plugin "personality" card, containing the CPU
itself, an XTAL clock and enough latches, buffers and gates to allow total
control to be exercised over its bus activity.

In the original MICE system, the MCP-85 main control card could be reused
with different CPU cards after replacing the EPROMs. The more spaciously
designed MICE-II eliminated this as a separate board and moved its primary
components (8085/8255/8251/8155/6116/ROMs/6.144 XTAL) onto the CEP (Control
Emulation Processor) card for each CPU; however, the RTT (Real-time Trace)
board (providing extra I/O ports) and UEM (Universal Emulation Memory)
boards could be shared between CEP cards.

The connection to the outside world is via a RS-232C port to a terminal.
The serial protocol is configurable through a 6-position DIP switch.

There's a mistake in the boot rom: if the test of the 8155 or 8255 fail, it
attempts to write a suitable message to the screen, but as the 8251 hasn't
yet been initialised, it hangs.

****************************************************************************/


#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8155.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "bus/rs232/rs232.h"


namespace {

class mice_state : public driver_device
{
public:
	mice_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void mice2(machine_config &config);
	void mice(machine_config &config);

private:
	void mice2_io(address_map &map) ATTR_COLD;
	void mice2_mem(address_map &map) ATTR_COLD;
	void mice_io(address_map &map) ATTR_COLD;
	void mice_mem(address_map &map) ATTR_COLD;
	required_device<cpu_device> m_maincpu;
};


void mice_state::mice_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).rom().region("mcp", 0);
	map(0x4400, 0x47ff).ram(); //(U13)
	map(0x6000, 0x60ff).rw("rpt", FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w));
}

void mice_state::mice_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x50, 0x51).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x60, 0x67).rw("rpt", FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
	map(0x70, 0x73).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
}

void mice_state::mice2_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).rom().region("cep", 0);
	map(0x9000, 0x90ff).rw("rpt", FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w));
	map(0xb000, 0xb7ff).ram();
	map(0xe800, 0xe8ff).rw("rtt8155", FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w));
}

void mice_state::mice2_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x80, 0x81).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x90, 0x97).rw("rpt", FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
	map(0xa0, 0xa3).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xc0, 0xc3).rw("rttppi1", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xc8, 0xcb).rw("rttppi2", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xd0, 0xd3).rw("rttppi3", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xd8, 0xdb).rw("rttppi4", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xe0, 0xe3).rw("rttppi5", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xe8, 0xed).rw("rtt8155", FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
}

// Input ports
static INPUT_PORTS_START( mice )
	PORT_START("BAUD")
	PORT_DIPNAME(0x07, 0x02, "Baud Rate") PORT_DIPLOCATION("DSW7:1,2,3")
	PORT_DIPSETTING(0x07, "110")
	PORT_DIPSETTING(0x06, "150")
	PORT_DIPSETTING(0x05, "300")
	PORT_DIPSETTING(0x04, "600")
	PORT_DIPSETTING(0x03, "1200")
	PORT_DIPSETTING(0x02, "2400")
	PORT_DIPSETTING(0x01, "4800")
	PORT_DIPSETTING(0x00, "9600")
	PORT_DIPNAME(0x08, 0x00, "Data Bits") PORT_DIPLOCATION("DSW7:4")
	PORT_DIPSETTING(0x00, "7")
	PORT_DIPSETTING(0x08, "8")
	PORT_DIPNAME(0x30, 0x30, "Parity") PORT_DIPLOCATION("DSW7:5,6")
	PORT_DIPSETTING(0x00, DEF_STR(None))
	PORT_DIPSETTING(0x30, "Even")
	PORT_DIPSETTING(0x10, "Odd")
	// "The number of stop bits is permanently set to one; and the communication is full duplex." (manual, p. 6)
INPUT_PORTS_END

static INPUT_PORTS_START( micev3 )
	PORT_START("BAUD")
	PORT_DIPNAME(0x07, 0x02, "Baud Rate") PORT_DIPLOCATION("U17:1,2,3")
	PORT_DIPSETTING(0x06, "150")
	PORT_DIPSETTING(0x05, "300")
	PORT_DIPSETTING(0x04, "600")
	PORT_DIPSETTING(0x03, "1200")
	PORT_DIPSETTING(0x02, "2400")
	PORT_DIPSETTING(0x01, "4800")
	PORT_DIPSETTING(0x00, "9600")
	PORT_DIPSETTING(0x07, "19200") // 110 baud on older firmware versions
	PORT_DIPNAME(0x08, 0x00, "Data Bits") PORT_DIPLOCATION("U17:4")
	PORT_DIPSETTING(0x00, "7")
	PORT_DIPSETTING(0x08, "8")
	PORT_DIPNAME(0x30, 0x30, "Parity") PORT_DIPLOCATION("U17:5,6")
	PORT_DIPSETTING(0x00, DEF_STR(None))
	PORT_DIPSETTING(0x30, "Even")
	PORT_DIPSETTING(0x10, "Odd")
	// "The number of stop bits is permanently set at two; and the communication is full duplex." (manual, p. 2-7)
INPUT_PORTS_END

static DEVICE_INPUT_DEFAULTS_START( mice_terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_2400 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_2400 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

static DEVICE_INPUT_DEFAULTS_START( mice2_terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_2400 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_2400 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END


void mice_state::mice(machine_config &config)
{
	// basic machine hardware
	I8085A(config, m_maincpu, 6.144_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mice_state::mice_mem);
	m_maincpu->set_addrmap(AS_IO, &mice_state::mice_io);

	i8251_device &uart(I8251(config, "uart", 6.144_MHz_XTAL / 2));
	uart.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	uart.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	uart.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	uart.txrdy_handler().set_inputline("maincpu", I8085_RST65_LINE);
	uart.rxrdy_handler().set_inputline("maincpu", I8085_RST75_LINE);

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("uart", FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set("uart", FUNC(i8251_device::write_dsr));
	rs232.cts_handler().set("uart", FUNC(i8251_device::write_cts));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(mice_terminal));

	i8155_device &rpt(I8155(config, "rpt", 6.144_MHz_XTAL / 2));
	rpt.in_pc_callback().set_ioport("BAUD");
	rpt.out_to_callback().set("uart", FUNC(i8251_device::write_txc));
	rpt.out_to_callback().append("uart", FUNC(i8251_device::write_rxc));

	I8255(config, "ppi");
}

void mice_state::mice2(machine_config &config)
{
	mice(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &mice_state::mice2_mem);
	m_maincpu->set_addrmap(AS_IO, &mice_state::mice2_io);

	subdevice<rs232_port_device>("rs232")->set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(mice2_terminal));

	I8255(config, "rttppi1");
	I8255(config, "rttppi2");
	I8255(config, "rttppi3");
	I8255(config, "rttppi4");
	I8255(config, "rttppi5");
	I8155(config, "rtt8155", 0);
}

// ROM definitions
ROM_START( mice_6502 )
	ROM_REGION( 0x4000, "mcp", 0 )
	ROM_LOAD( "6502_u10_v.2.0", 0x2000, 0x1000, CRC(496c53a7) SHA1(f28cddef18ab3e0eca1fea125dd678a54817c9df) )
	ROM_LOAD( "6502_u11_v.2.0", 0x1000, 0x1000, CRC(8d655bd2) SHA1(94936553f1692ede0934e3c7b599f3ad6adb6aec) )
	ROM_LOAD( "6502_u12_v.2.0", 0x0000, 0x1000, CRC(cee810ee) SHA1(ab642cda73f4b3f715ddc2909ba2b48cbd474d4d) )
ROM_END

ROM_START( mice2_z80 )
	ROM_REGION( 0x8000, "cep", 0 )
	ROM_LOAD( "z80_u2_v.3.0",   0x4000, 0x2000, CRC(992b1b53) SHA1(f7b66c49ab26a9f97b2e6ebe45d162daa66d8a67) )
	ROM_LOAD( "z80_u3_v.3.0",   0x2000, 0x2000, CRC(48d0be9b) SHA1(602af21868b1b5e6d488706a831259d78fefad6f) )
	ROM_LOAD( "z80_u4_v.3.0",   0x0000, 0x2000, CRC(4fe2d08d) SHA1(902b98357b8f2e61f68dd171478368a3ac47af6e) )
ROM_END

ROM_START( mice2_6502 )
	ROM_REGION( 0x8000, "cep", 0 )
	ROM_LOAD( "6502_u1_v.3.2",  0x6000, 0x2000, CRC(0ba10943) SHA1(e7590e2c1d9d2b1ff8cca0f5da366650ea4d50e3) )
	ROM_LOAD( "6502_u2_v.3.2",  0x4000, 0x2000, CRC(f3169423) SHA1(a588a2e1894f523cf11c34d036beadbfe5b10538) )
	ROM_LOAD( "6502_u3_v.3.2",  0x2000, 0x2000, CRC(d5c77c3f) SHA1(71439735ed62db07bee713775ee2189120d1a1e7) )
	ROM_LOAD( "6502_u4_v.3.2",  0x0000, 0x2000, CRC(6acfc3a1) SHA1(3572a4798873c21a247a43da8419e7b9a181c67d) )
ROM_END

ROM_START( mice2_8085 )
	ROM_REGION( 0x8000, "cep", 0 )
	ROM_LOAD( "8085_u2_v.3.1",  0x4000, 0x2000, CRC(2fce00a5) SHA1(0611f928be663a9279781d9f496fc950fd4ee7e2) )
	ROM_LOAD( "8085_u3_v.3.1",  0x2000, 0x2000, CRC(16ee3018) SHA1(9e215504bcea2c5ebfb7578ecf371eec45cbe5d7) )
	ROM_LOAD( "8085_u4_v.3.1",  0x0000, 0x2000, CRC(5798f2b5) SHA1(e0fe9411394bded8a77bc6a0f71519aad7800125) )
ROM_END

ROM_START( mice2_6809 )
	ROM_REGION( 0x8000, "cep", 0 )
	ROM_LOAD( "6809_u1_v.3.4",  0x0000, 0x8000, CRC(b94d043d) SHA1(822697485f064286155f2a66cdbdcb0bd66ddb8c) )
ROM_END

} // anonymous namespace


// Driver

//    YEAR  NAME        PARENT     COMPAT  MACHINE  INPUT   CLASS       INIT        COMPANY     FULLNAME                   FLAGS
COMP( 1981, mice_6502,  0,         0,      mice,    mice,   mice_state, empty_init, "Microtek", "MICE 6502 (Rev-A)",       MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )
COMP( 1984, mice2_z80,  0,         0,      mice2,   micev3, mice_state, empty_init, "Microtek", "MICE-II Z80 (Rev-F)",     MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )
COMP( 1987, mice2_6502, mice2_z80, 0,      mice2,   micev3, mice_state, empty_init, "Microtek", "MICE-II 6502 (Rev-F)",    MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )
COMP( 1987, mice2_8085, mice2_z80, 0,      mice2,   micev3, mice_state, empty_init, "Microtek", "MICE-II 8085 (Rev-M)",    MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )
COMP( 1987, mice2_6809, mice2_z80, 0,      mice2,   micev3, mice_state, empty_init, "Microtek", "MICE-II 6809(E) (Rev-L)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



mice3s68k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:

/**********************************************************************************************

Skeleton driver for Microtek MICE IIIS 68000 in-circuit emulator.

Three hardware components.

1.- Main unit with three PCBs:

CPM PCB (CPU):
 - 4 x EPROM.
 - 1 x PROM.
 - 1 x Intel R80186 near a 16 MHz crystal.
 - 2 x Mitsubishi M5M5256BP-10L (RAM).
 - 1 x Zilog Z0853006PSC near a 3.3864 MHz crystal.
 - Small suboard with one Serial RS-232C (DCE) port (DB25) and one Parallel port (DB25).
 - 1 x Reset switch.

EPM-6800A PCB:
 - 4 x NEC D8255AC-2.
 - 2 x GAL16V8.
 - 1 x Cypress CY7C166-25PC (RAM).
 - 2 x Sony CXK5814P-35 (RAM).
 - 2 x BNC connector (start slave out, sync start in).
 - 1 LED (EP run).

HEMM PCB:
 - 4 x NEC D8255AC-2.
 - 33 x Mitsubishi M5M5256BP-70L (RAM).

LAM-IIS PCB:
 - 8 x NEC 71055L.
 - 2 x AMC MACH110-12JC.
 - 1 x 40 MHz crystal.
 - 11 x GAL16V8.
 - 8 x P4C198-15JC (RAM).
 - 3 x BNC connector (sync 1 output, sync 2 output, ext trigger input).
 - 4 x Cypress CY7C196-25VC (RAM).
 - 16 x IDT 71256 (RAM).
 - 1 x Trace Bits port (DB9).
 - 1 LED (+5V power).

2.- EPOD-68000, external box for connecting the main unit to the CPU pass-through apapter,
with connectors, one GAL16V8 and some logic.

3.- CPU pass-through apapter.

**********************************************************************************************/

#include "emu.h"
#include "cpu/i86/i186.h"


namespace {

class mice3s68k_state : public driver_device
{
public:
	mice3s68k_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }


	void mice3s68k(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
};


static INPUT_PORTS_START(mice3s68k)
INPUT_PORTS_END

void mice3s68k_state::mice3s68k(machine_config &config)
{
	I80186(config, m_maincpu, 16_MHz_XTAL); // Intel R80186
}


// FW V5.2
ROM_START(mice3s68k)
	ROM_REGION(0x30000, "maincpu", 0)
	ROM_LOAD( "iiis_68000_v5.2.u11",        0x00000, 0x08000, CRC(cd94dd5d) SHA1(3a609f2dca9bc22a764188704eaa8ca952d1637c) )
	ROM_LOAD( "iiis_68000_v5.2.u12",        0x00800, 0x08000, CRC(fb9c9185) SHA1(6463274989f5e25cba08dbcac485eda583c7c602) )
	ROM_LOAD( "iiis_68000_v5.2_27c512.u14", 0x01000, 0x10000, CRC(75f8841f) SHA1(3ac20ff642f36145f0310fe96791bae74305e7ec) )
	ROM_LOAD( "iiis_68000_v5.2_27c512.u15", 0x02000, 0x10000, CRC(d2dbf91f) SHA1(90ce54ba3950944bb077e95e96b1388370baec32) )

	ROM_REGION(0x00100, "prom", 0)
	ROM_LOAD( "x2212p.u5",                  0x00000, 0x00100, CRC(c4089451) SHA1(4997cd46ddeab498ced5243e84d4f7fbe5c4597e) )

	ROM_REGION(0x00117, "pld", 0)
	ROM_LOAD( "54983012_gal16v8.u24",       0x00000, 0x00117, NO_DUMP ) // EPOD-68000
	ROM_LOAD( "54982021_gal16v8.u58",       0x00000, 0x00117, NO_DUMP ) // EPM-68000A
	ROM_LOAD( "54982011_gal16v8.u46",       0x00000, 0x00117, NO_DUMP ) // EPM-68000A
	ROM_LOAD( "08309011_gal16v8.u3",        0x00000, 0x00117, NO_DUMP ) // HEMM
	ROM_LOAD( "55005121_mach110.u106",      0x00000, 0x00117, NO_DUMP ) // LAM-IIS
	ROM_LOAD( "55005131_mach110.u128",      0x00000, 0x00117, NO_DUMP ) // LAM-IIS
	ROM_LOAD( "55005062_gal16v8.u55",       0x00000, 0x00117, NO_DUMP ) // LAM-IIS
	ROM_LOAD( "55005071_gal16v8.u56",       0x00000, 0x00117, NO_DUMP ) // LAM-IIS
	ROM_LOAD( "55005081_gal16v8.u58",       0x00000, 0x00117, NO_DUMP ) // LAM-IIS
	ROM_LOAD( "55005091_gal16v8.u59",       0x00000, 0x00117, NO_DUMP ) // LAM-IIS
	ROM_LOAD( "55005031_gal16v8.u30",       0x00000, 0x00117, NO_DUMP ) // LAM-IIS
	ROM_LOAD( "55005041_gal16v8.u31",       0x00000, 0x00117, NO_DUMP ) // LAM-IIS
	ROM_LOAD( "55005051_gal16v8.u44",       0x00000, 0x00117, NO_DUMP ) // LAM-IIS
	ROM_LOAD( "55005011_gal16v8.u9",        0x00000, 0x00117, NO_DUMP ) // LAM-IIS
	ROM_LOAD( "55005021_gal16v8.u10",       0x00000, 0x00117, NO_DUMP ) // LAM-IIS
	ROM_LOAD( "55005101_gal16v8.u78",       0x00000, 0x00117, NO_DUMP ) // LAM-IIS
	ROM_LOAD( "55005111_gal16v8.u32",       0x00000, 0x00117, NO_DUMP ) // LAM-IIS
ROM_END

} // anonymous namespace

SYST( 1992, mice3s68k, 0, 0, mice3s68k, mice3s68k, mice3s68k_state, empty_init, "Microtek", "MICE-IIIS 68000", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



micom_mahjong.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
/*******************************************************************************

マイコン麻雀 - Micom Mahjong, mail-order Mahjong console.

Two models are known, a grey one and a white one, hardware is presumed to be
the same. It's not known who developed/manufactured it. Probably not the
mail order house, but they can be considered the publisher.

The options at the start of the game:
Option 1: 1: 3 minutes play time, 2: 5 minutes
Option 2: 1: advanced difficulty, 2: beginner

Hardware notes:
- PCB label: IFVC-3224A
- Zilog Z8400A or Sharp LH0080A, 11.0592MHz XTAL
- 16KB ROM (4*2732), 1KB RAM (2*MSM2114L)
- 2KB ROM (2716) for tiles, 1KB VRAM (2*MSM2114L), 1bpp video
- 1-bit sound

TODO:
- video timing, maybe 11059200 / 2 / (262*352)?

*******************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "sound/dac.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"
#include "tilemap.h"


namespace {

class mmahjong_state : public driver_device
{
public:
	mmahjong_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_vram(*this, "vram"),
		m_tilemap(*this, "tilemap"),
		m_screen(*this, "screen"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void mmahjong(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_shared_ptr<u8> m_vram;
	required_device<tilemap_device> m_tilemap;
	required_device<screen_device> m_screen;
	required_device<dac_bit_interface> m_dac;
	required_ioport_array<3> m_inputs;

	u8 m_inp_matrix = 0;

	TILE_GET_INFO_MEMBER(get_tile_info) { tileinfo.set(0, m_vram[tile_index], 0, 0); }
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void main_map(address_map &map) ATTR_COLD;

	void vram_w(offs_t offset, u8 data);
	void input_w(u8 data);
	u8 input_r();
	void sound_w(u8 data);
};

void mmahjong_state::machine_start()
{
	save_item(NAME(m_inp_matrix));
}



/*******************************************************************************
    Video
*******************************************************************************/

u32 mmahjong_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	m_tilemap->draw(screen, bitmap, cliprect);
	return 0;
}

static GFXDECODE_START( gfx_mmahjong )
	GFXDECODE_ENTRY( "tiles", 0, gfx_8x8x1, 0, 1 )
GFXDECODE_END



/*******************************************************************************
    I/O
*******************************************************************************/

void mmahjong_state::vram_w(offs_t offset, u8 data)
{
	m_vram[offset] = data;
	m_tilemap->mark_tile_dirty(offset);
}

void mmahjong_state::input_w(u8 data)
{
	// d0-d2: input matrix
	// d3 is also written, but unused
	m_inp_matrix = data;
}

u8 mmahjong_state::input_r()
{
	u8 data = 0xff;

	// read keypad
	for (int i = 0; i < 3; i++)
		if (!BIT(m_inp_matrix, i))
			data &= m_inputs[i]->read();

	return data;
}

void mmahjong_state::sound_w(u8 data)
{
	// d0: speaker out
	m_dac->write(data & 1);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void mmahjong_state::main_map(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	map(0x5000, 0x53ff).ram();
	map(0x6000, 0x63ff).w(FUNC(mmahjong_state::vram_w)).share(m_vram);
	map(0x7001, 0x7001).r(FUNC(mmahjong_state::input_r));
	map(0x7002, 0x7002).w(FUNC(mmahjong_state::input_w));
	map(0x7004, 0x7004).w(FUNC(mmahjong_state::sound_w));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( mmahjong )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_MAHJONG_A) // 1
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_MAHJONG_B) // 2
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_MAHJONG_C) // 3
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_MAHJONG_D) // 4
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_MAHJONG_E) // 5
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_MAHJONG_F) // 6
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_MAHJONG_G) // 7
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_MAHJONG_H) // 8
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_MAHJONG_I) // 9
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_MAHJONG_J) // 10
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_MAHJONG_K) // 11
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_MAHJONG_L) // 12
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_MAHJONG_M) // 13
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_MAHJONG_N) // 0 (Tsumo)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_MAHJONG_PON)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_MAHJONG_CHI)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_MAHJONG_KAN)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_MAHJONG_REACH)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_MAHJONG_RON)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void mmahjong_state::mmahjong(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 11.0592_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &mmahjong_state::main_map);

	// video hardware
	GFXDECODE(config, "gfxdecode", "palette", gfx_mmahjong);

	PALETTE(config, "palette", palette_device::MONOCHROME);

	TILEMAP(config, m_tilemap, "gfxdecode", 0, 8, 8, TILEMAP_SCAN_ROWS, 32, 32).set_info_callback(FUNC(mmahjong_state::get_tile_info));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(8*32, 8*32);
	m_screen->set_visarea(0, 8*32-1, 0, 8*24-1);
	m_screen->set_screen_update(FUNC(mmahjong_state::screen_update));
	m_screen->set_palette("palette");

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( mmahjong )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "ms_1.10", 0x0000, 0x1000, CRC(a1607ac8) SHA1(4e0d7a0482c7619ef25b12a7e02f5d03bea8ce6f) )
	ROM_LOAD( "ms_2.9",  0x1000, 0x1000, CRC(cb1cab68) SHA1(88dc4808126528a269edf742062fa12e902be324) )
	ROM_LOAD( "ms_3.8",  0x2000, 0x1000, CRC(2fdd4f55) SHA1(a9246239144c41fd38bd42015552b5afab40e55a) )
	ROM_LOAD( "ms_4.7",  0x3000, 0x1000, CRC(cc550e36) SHA1(d66750ce6ddf6e4db4e5bd46a639494d8335a590) )

	ROM_REGION( 0x800, "tiles", 0 )
	ROM_LOAD( "ms_a.2", 0x000, 0x800, CRC(d1dfe5c1) SHA1(5042b89555867db418f4aeef6b520619d8f533f2) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1982, mmahjong, 0,      0,      mmahjong, mmahjong, mmahjong_state, empty_init, "Nippon Mail Service", "Micom Mahjong", MACHINE_SUPPORTS_SAVE )



micral.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/********************************************************************************

Bull (Originally R2E) Micral 80-22G

2015-10-01 Skeleton [Robbbert]

http://www.ti99.com/exelvision/website/index.php?page=r2e-micral-8022-g

This expensive, futuristic-looking design featured a motherboard and slots,
much like an ancient PC. The known chip complement is:
Z80A, 4MHz; 64KB RAM, 2KB BIOS ROM, 256x4 prom (7611);
CRT8002, TMS9937 (=CRT5037), 4KB video RAM, 256x4 prom (7611);
2x 5.25 inch floppy drives, one ST506 5MB hard drive;
CDP6402 UART. Sound is a beeper.
The keyboard has a uPD780C (=Z80) and 1KB of ROM.

The FDC and HDC are unknown.
No manuals, schematic or circuit description have been found.

Commands must be in uppercase. Reboot to exit each command.
Bx[,x]: ??
Gxxxx : go (writes a jump @FFED then jumps to FFEB)
T     : test
*     : echo keystrokes
enter : ??

Using generic keyboard via the uart for now. It's unknown how the real keyboard
communicates with the main cpu.

FFF8/9 are used for sending instructions to the screen. FFF9 is command/status,
and FFF8 is data. The codes 0-D seem to be for the CRT5037, but the results don't
make much sense. Code A0 is to write a byte to the current cursor position, and
B0 is to get the status.

Screen needs:
- Scrolling
- Proper character generator
- To be properly understood
- According to the web, graphics are possible. A screenshot shows reverse video
  exists.

Other things...
- Beeper
- 2 floppy drives
- keyboard
- unknown ports

--------------------
Honeywell Bull Questar/M

http://www.histoireinform.com/Histoire/+infos6/chr6inf3.htm
https://www.esocop.org/docs/Questar.pdf

*********************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/ay31015.h"
#include "machine/clock.h"
//#include "sound/beep.h"
#include "video/tms9927.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class micral_state : public driver_device
{
public:
	micral_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		//, m_beep(*this, "beeper")
		, m_p_chargen(*this, "chargen")
		, m_uart(*this, "uart")
		, m_crtc(*this, "crtc")
	{ }

	void micral(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	u8 keyin_r();
	u8 status_r();
	u8 unk_r();
	u8 video_r(offs_t offset);
	void video_w(offs_t offset, u8 data);
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void io_kbd(address_map &map) ATTR_COLD;
	void mem_kbd(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	u16 s_curpos = 0U;
	u8 s_command = 0U;
	u8 s_data = 0U;
	std::unique_ptr<u8[]> m_vram;
	memory_passthrough_handler m_rom_shadow_tap;
	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	//required_device<beep_device> m_beep;
	required_region_ptr<u8> m_p_chargen;
	required_device<ay31015_device> m_uart;
	required_device<crt5037_device> m_crtc;
};

u8 micral_state::status_r()
{
	return m_uart->dav_r() | 4;
}

u8 micral_state::unk_r()
{
	return 0x96;
}

u8 micral_state::keyin_r()
{
	m_uart->write_rdav(0);
	u8 result = m_uart->receive();
	m_uart->write_rdav(1);
	return result;
}

u8 micral_state::video_r(offs_t offset)
{
	if (offset)
		return 0x07;
	else
		return m_vram[s_curpos];
}

void micral_state::video_w(offs_t offset, u8 data)
{
	if (offset)
	{
		s_command = data;
		if (s_command == 0x0c)
			s_curpos = (s_curpos & 0xff00) | s_data;
		else
		if (s_command == 0x0d)
			s_curpos = (s_curpos & 0xff) | ((s_data & 0x1f) << 8);
		else
		if (s_command == 0xa0)
			m_vram[s_curpos] = s_data;

		//if (s_command < 0x10)
			//m_crtc->write(s_command, s_data);
	}
	else
	{
		s_data = data;
	}
}


void micral_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffef).ram().share("mainram");
	map(0xf800, 0xfeff).rom().region("maincpu", 0);
	map(0xfff6, 0xfff7); // .nopw(); // unknown ports
	map(0xfff8, 0xfff9).rw(FUNC(micral_state::video_r), FUNC(micral_state::video_w));
	map(0xfffa, 0xfffa).r(FUNC(micral_state::keyin_r));
	map(0xfffb, 0xfffb).r(FUNC(micral_state::unk_r));
	map(0xfffc, 0xfffc).r(FUNC(micral_state::status_r));
	map(0xfffd, 0xffff); // more unknown ports
}

void micral_state::mem_kbd(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x03ff).rom();
	map(0x8000, 0x8000).ram(); // byte returned to main cpu after receiving irq
	map(0x8001, 0x8001).portr("X0");
	map(0x8002, 0x8002).portr("X1");
	map(0x8004, 0x8004).portr("X2");
	map(0x8008, 0x8008).portr("X3");
	map(0x8010, 0x8010).portr("X4");
	map(0x8020, 0x8020).portr("X5");
	map(0x8040, 0x8040).portr("X6");
	map(0x8080, 0x8080).portr("X7");
	map(0x8100, 0x8100).portr("X8");
	map(0x8200, 0x8200).portr("X9");
	map(0x8400, 0x8400).portr("X10");
	map(0x8800, 0x8800).portr("X11");
	map(0x9000, 0x9000).portr("X12");
	map(0xa000, 0xa000).portr("X13");
}

void micral_state::io_kbd(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x00).portr("X14");
}

/* Input ports */
static INPUT_PORTS_START( micral )
	PORT_START("X0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ESC)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 01
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 00
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 03
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 00

	PORT_START("X1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) // 2A
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) // 22
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) // 28
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) // 94
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) // 90
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 29

	PORT_START("X2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 3E, 3C
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 00
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)

	PORT_START("X3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // '^'
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 00
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 00
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)

	PORT_START("X4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) // 5B
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // OB
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)

	PORT_START("X5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) // 3F
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // ':','/'
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 08
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 06

	PORT_START("X6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TAB)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 00
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 02
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 00
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 00

	PORT_START("X7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) // 91
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) // 27
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) // '-'
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) // '_'
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) // 8E
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) // '+'

	PORT_START("X8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 00
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 00
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)

	PORT_START("X9")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // '@', '#'
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 00
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)

	PORT_START("X10")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 9C, '%'
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 05
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 7F
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)

	PORT_START("X11")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // ';','.'
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // '!','&'
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 0A
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 00

	PORT_START("X12")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 95,FE
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 97,FC
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 9D,'$'
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 96,'\'
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 99,84
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 9A,92

	PORT_START("X13")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 00
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 00
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 00
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 00
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 00
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // 00

	PORT_START("X14")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RCONTROL) // ?? don't look for a new keypress
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LCONTROL) // ??
INPUT_PORTS_END

uint32_t micral_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint16_t sy=0,ma=0;

	for (uint8_t y = 0; y < 24; y++)
	{
		for (uint8_t ra = 0; ra < 10; ra++)
		{
			uint16_t *p = &bitmap.pix(sy++);

			for (uint16_t x = 0; x < 80; x++)
			{
				uint8_t gfx = 0;
				if (ra < 9)
				{
					uint8_t chr = m_vram[x+ma];
					gfx = m_p_chargen[(chr<<4) | ra ];
					if (((s_curpos & 0xff)==x) && ((s_curpos >> 8)==y))
						gfx ^= 0xff;
				}
				/* Display a scanline of a character */
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma+=256;
	}
	return 0;
}

void micral_state::machine_reset()
{
	// no idea if these are hard-coded, or programmable
	m_uart->write_xr(0);
	m_uart->write_xr(1);
	m_uart->write_swe(0);
	m_uart->write_np(1);
	m_uart->write_tsb(0);
	m_uart->write_nb1(1);
	m_uart->write_nb2(1);
	m_uart->write_eps(1);
	m_uart->write_cs(1);
	m_uart->write_cs(0);

	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x0000, 0x07ff, m_rom);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0xf800, 0xffff,
			"rom_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// reinstall RAM over the ROM shadow
					m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x07ff, m_ram);
				}
			},
			&m_rom_shadow_tap);
}

void micral_state::machine_start()
{
	m_vram = make_unique_clear<u8[]>(0x2000);
	save_pointer(NAME(m_vram), 0x2000);
	save_item(NAME(s_curpos));
	save_item(NAME(s_command));
	save_item(NAME(s_data));
}

void micral_state::micral(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, XTAL(4'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &micral_state::mem_map);
	// no i/o ports on main cpu
	z80_device &keyboard(Z80(config, "keyboard", XTAL(1'000'000))); // freq unknown
	keyboard.set_addrmap(AS_PROGRAM, &micral_state::mem_kbd);
	keyboard.set_addrmap(AS_IO, &micral_state::io_kbd);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(250));
	screen.set_screen_update(FUNC(micral_state::screen_update));
	screen.set_size(640, 240);
	screen.set_visarea(0, 639, 0, 239);
	screen.set_palette("palette");
	PALETTE(config, "palette", palette_device::MONOCHROME);
	//GFXDECODE(config, "gfxdecode", "palette", gfx_micral);

	CRT5037(config, m_crtc, 4000000 / 8);  // xtal freq unknown
	m_crtc->set_char_width(8);  // unknown
	//m_crtc->vsyn_callback().set(TMS5501_TAG, FUNC(tms5501_device::sens_w));
	m_crtc->set_screen("screen");

	/* sound hardware */
	//SPEAKER(config, "mono").front_center();
	//BEEP(config, m_beep, 2000).add_route(ALL_OUTPUTS, "mono", 0.50);

	AY31015(config, m_uart); // CDP6402
	m_uart->read_si_callback().set("rs232", FUNC(rs232_port_device::rxd_r));
	m_uart->write_so_callback().set("rs232", FUNC(rs232_port_device::write_txd));

	clock_device &uart_clock(CLOCK(config, "uart_clock", 153600));
	uart_clock.signal_handler().set(m_uart, FUNC(ay31015_device::write_tcp));
	uart_clock.signal_handler().append(m_uart, FUNC(ay31015_device::write_rcp));

	RS232_PORT(config, "rs232", default_rs232_devices, "keyboard");
}

ROM_START( micral )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "8022g.rom",    0x0000, 0x0800, CRC(882732a9) SHA1(3f37b82c450a54aedec209bd46fcbcf131c86313) )

	ROM_REGION( 0x0400, "keyboard", 0 )
	ROM_LOAD( "2010221.rom",  0x0000, 0x0400, CRC(65123378) SHA1(401f0a648b78bf1662a1cd2546e83ba8e3cb7a42) )

	// Using the chargen from 'c10' for now.
	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "c10_char.bin", 0x0000, 0x2000, BAD_DUMP CRC(cb530b6f) SHA1(95590bbb433db9c4317f535723b29516b9b9fcbf))
ROM_END

ROM_START( questarm )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "qm_547_1.rom", 0x0000, 0x0800, CRC(8e6dc953) SHA1(b31375af8c6769578d2000fff3e751e94e7ae4d4) )

	// using the keyboard ROM from 'micral' for now
	ROM_REGION( 0x0400, "keyboard", 0 )
	ROM_LOAD( "2010221.rom",  0x0000, 0x0400, CRC(65123378) SHA1(401f0a648b78bf1662a1cd2546e83ba8e3cb7a42) )

	// Using the chargen from 'c10' for now.
	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "c10_char.bin", 0x0000, 0x2000, BAD_DUMP CRC(cb530b6f) SHA1(95590bbb433db9c4317f535723b29516b9b9fcbf))
ROM_END

} // anonymous namespace

/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT         COMPANY           FULLNAME         FLAGS
COMP( 1981, micral,   0,      0,      micral,  micral, micral_state, empty_init, "Bull R2E",       "Micral 80-22G", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1982, questarm, micral, 0,      micral,  micral, micral_state, empty_init, "Honeywell Bull", "Questar/M",     MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



micro.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Novag Micro Chess

It's a portable chesscomputer with sensory board. The MCU says "(C) CAL R & O3",
though the program is supposedly by David Kittinger? Unlikely, since his first
work with Novag was Super Sensor IV, a couple of months newer than Micro Chess.

Hardware notes:
- Mostek 3875/42 (4KB ROM, 64 bytes extra RAM)
- buzzer, button sensors chessboard, 16+4 leds

MCU interrupts are unused. MCU embedded extra RAM is battery-backed via MEM
switch tied to pin #4 (VSB: RAM standby power).

*******************************************************************************/

#include "emu.h"

#include "cpu/f8/f8.h"
#include "machine/f3853.h"
#include "machine/sensorboard.h"
#include "machine/nvram.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "novag_micro.lh"


namespace {

class micro_state : public driver_device
{
public:
	micro_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_board(*this, "board"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.0")
	{ }

	void micro(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<sensorboard_device> m_board;
	required_device<dac_1bit_device> m_dac;
	required_ioport m_inputs;

	u8 m_led_data = 0;
	u8 m_control = 0;
	u8 m_inp_mux = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_display();
	void input_w(u8 data);
	u8 input_r();
	void control_w(u8 data);
	u8 control_r();
	void led_w(u8 data);
};

void micro_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_led_data));
	save_item(NAME(m_control));
	save_item(NAME(m_inp_mux));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void micro_state::update_display()
{
	u8 sel = (m_control << 2 & 0x3c) | (m_led_data >> 6 & 3);
	m_display->matrix(sel, m_led_data & 0x3f);
}

void micro_state::input_w(u8 data)
{
	// P00-P01: MK3875 doesn't have these pins
	// P02-P07: input mux part
	m_inp_mux = data >> 2;
}

u8 micro_state::input_r()
{
	// P10-P17: multiplexed inputs
	u8 data = 0;

	// read chessboard
	u8 cb_mux = (m_inp_mux << 2) | (m_control >> 5 & 3);
	cb_mux = bitswap<8>(cb_mux,4,5,6,7,1,0,3,2);

	for (int i = 0; i < 8; i++)
		if (BIT(cb_mux, i))
			data |= bitswap<8>(m_board->read_file(i),4,5,6,7,3,2,1,0);

	// read buttons
	if (m_control & 0x10)
		data |= m_inputs->read();

	return data;
}

void micro_state::control_w(u8 data)
{
	// P40: led select part
	// P41: white led
	// P42: ?
	// P43: black led
	// P44-P46: input mux part
	m_control = data;
	update_display();

	// P47: speaker out
	m_dac->write(BIT(data, 7));
}

u8 micro_state::control_r()
{
	return m_control;
}

void micro_state::led_w(u8 data)
{
	// P50-P55: led data
	// P56,P57: led select part
	m_led_data = data;
	update_display();
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void micro_state::main_map(address_map &map)
{
	map.global_mask(0xfff);
	map(0x0000, 0x0fbf).rom();
	map(0x0fc0, 0x0fff).ram().share("nvram");
}

void micro_state::main_io(address_map &map)
{
	map(0x00, 0x00).w(FUNC(micro_state::input_w));
	map(0x01, 0x01).r(FUNC(micro_state::input_r)).nopw();
	map(0x04, 0x07).rw("psu", FUNC(f38t56_device::read), FUNC(f38t56_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( micro )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level / Bishop")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_S) PORT_NAME("Sound / Queen")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back / King")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_G) PORT_NAME("Go")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_B) PORT_NAME("B/W")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_V) PORT_NAME("Verify / Pawn")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_U) PORT_NAME("Set Up / Rook")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game / Knight")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void micro_state::micro(machine_config &config)
{
	// basic machine hardware
	F8(config, m_maincpu, 4'500'000/2); // matches video reference
	m_maincpu->set_addrmap(AS_PROGRAM, &micro_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &micro_state::main_io);

	f38t56_device &psu(F38T56(config, "psu", 4'500'000/2));
	psu.read_a().set(FUNC(micro_state::control_r));
	psu.write_a().set(FUNC(micro_state::control_w));
	psu.write_b().set(FUNC(micro_state::led_w));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(6, 6);
	config.set_default_layout(layout_novag_micro);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( nmicro )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("3875_42_mk17121n", 0x0000, 0x1000, CRC(f21189f7) SHA1(ba346177eaeddc87a03b1103f0299b5bcd4b6c27) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY, FULLNAME, FLAGS
SYST( 1981, nmicro, 0,      0,      micro,   micro, micro_state, empty_init, "Novag Industries", "Micro Chess", MACHINE_SUPPORTS_SAVE )



micro2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Novag Micro II (model 821)

This program was used in several Novag chesscomputers:
- Novag Micro II (1st use)
- Novag Micro III
- Novag Presto
- Novag Octo
- Novag Allegro (not Allegro 4)
- Novag Piccolo

The chess engine is by Julio Kaplan, not David Kittinger.

Hardware notes:

Micro II, Micro III(same pcb):
- Mitsubishi M5L8049-079P-6, 6MHz XTAL
- buzzer, 20 leds, 8*8 chessboard buttons

Presto:
- PCB label: 100023, 100024
- NEC D80C49C MCU(serial 186), OSC from LC circuit measured ~6MHz

Octo (high-speed):
- same PCB as Presto
- NEC D80C49HC MCU(serial 111), 12MHz OSC from LC circuit, this was advertised
  as 15MHz on the box, but measured ~12MHz (older Octo version is probably ~6MHz?)
- speaker circuit is a bit different, not sure why

Piccolo (high-speed):
- PCB label: NOVAG 100041
- same MCU serial as Octo, LC OSC is around 14MHz

Note that even though the MCUs are different, internal ROM contents was confirmed
to be identical for Micro II/III, Presto, Octo, Piccolo.

BTANB:
- controls are very sensitive (board sensors too): 6MHz: valid (single) button
  press registered between 307ms and 436ms, 15MHz: between 123ms and 174ms

*******************************************************************************/

#include "emu.h"

#include "cpu/mcs48/mcs48.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "novag_micro2.lh"


namespace {

class micro2_state : public driver_device
{
public:
	micro2_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_board(*this, "board"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.0")
	{ }

	// machine configs
	void micro2(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(change_cpu_freq) { set_cpu_freq(); }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD { set_cpu_freq(); }

private:
	// devices/pointers
	required_device<mcs48_cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<sensorboard_device> m_board;
	required_device<dac_1bit_device> m_dac;
	required_ioport m_inputs;

	bool m_kp_select = false;
	u8 m_inp_mux = 0;

	// I/O handlers
	void mux_w(u8 data);
	void control_w(u8 data);
	u8 input_r();

	void set_cpu_freq();
};



/*******************************************************************************
    Initialization
*******************************************************************************/

void micro2_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_kp_select));
	save_item(NAME(m_inp_mux));
}

void micro2_state::set_cpu_freq()
{
	// known CPU speeds: 6MHz(XTAL), 6MHz(LC), ~15MHz(may vary, LC)
	u32 freq = (ioport("CPU")->read() & 1) ? 15'000'000 : 6'000'000;
	m_maincpu->set_unscaled_clock(freq);

	m_board->set_delay(attotime::from_ticks(2'000'000, freq)); // see BTANB
}



/*******************************************************************************
    I/O
*******************************************************************************/

void micro2_state::mux_w(u8 data)
{
	// D0-D7: input mux, led data
	m_inp_mux = ~data;
	m_display->write_mx(m_inp_mux);
}

void micro2_state::control_w(u8 data)
{
	// P21: keypad select
	m_kp_select = bool(~data & 2);

	// P22,P23: speaker lead 1,2
	m_dac->write(BIT(data, 2) & BIT(~data, 3));

	// P24-P26: led select
	m_display->write_my(~data >> 4 & 7);
}

u8 micro2_state::input_r()
{
	// P10-P17: multiplexed inputs
	u8 data = 0;

	// read chessboard buttons
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_rank(i);

	// read sidepanel keypad
	if (m_kp_select)
		data |= m_inputs->read();

	return ~data;
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( micro2 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_B) PORT_NAME("B/W") // aka "Black/White" or "Change Color"
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_V) PORT_NAME("Verify / Pawn")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_U) PORT_NAME("Set Up / Rook")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Knight")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level / Bishop")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Queen")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back / King")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_G) PORT_NAME("Go")

	PORT_START("CPU")
	PORT_CONFNAME( 0x01, 0x00, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(micro2_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "6MHz (original)" )
	PORT_CONFSETTING(    0x01, "15MHz (newer)" )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void micro2_state::micro2(machine_config &config)
{
	// basic machine hardware
	I8049(config, m_maincpu, 6_MHz_XTAL); // see set_cpu_freq
	m_maincpu->p1_in_cb().set(FUNC(micro2_state::input_r));
	m_maincpu->p2_out_cb().set(FUNC(micro2_state::control_w));
	m_maincpu->bus_out_cb().set(FUNC(micro2_state::mux_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(3, 8);
	config.set_default_layout(layout_novag_micro2);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( nmicro2 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD("8049_8210.u1", 0x0000, 0x0800, CRC(29a0eb4c) SHA1(e058d6018e53ddcaa3b5ec25b33b8bff091b04db) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1982, nmicro2, 0,       0,      micro2,  micro2, micro2_state, empty_init, "Novag Industries / Heuristic Software", "Micro II (Novag)", MACHINE_SUPPORTS_SAVE )



micro20.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: R. Belmont
/****************************************************************************

    micro20.cpp
    GMX Micro 20 single-board computer

    68020 + 68881 FPU

    800a5e = end of initial 68020 torture test
****************************************************************************/

#include "emu.h"
#include "cpu/m68000/m68020.h"
#include "machine/mc68681.h"
#include "machine/msm58321.h"
#include "machine/timer.h"
#include "machine/wd_fdc.h"
#include "machine/68230pit.h"
#include "bus/rs232/rs232.h"
#include "softlist.h"


namespace {

#define MAINCPU_TAG "maincpu"
#define DUART_A_TAG "duarta"
#define DUART_B_TAG "duartb"
#define RTC_TAG     "rtc"
#define FDC_TAG     "fdc"
#define PIT_TAG     "pit"

class micro20_state : public driver_device
{
public:
	micro20_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, MAINCPU_TAG),
		m_rom(*this, "bootrom"),
		m_mainram(*this, "mainram"),
		m_pit(*this, PIT_TAG),
		m_rtc(*this, RTC_TAG)
	{
	}

	void micro20(machine_config &config);

private:
	required_device<m68020_device> m_maincpu;
	required_memory_region m_rom;
	required_shared_ptr<uint32_t> m_mainram;
	required_device<pit68230_device> m_pit;
	required_device<msm58321_device> m_rtc;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void m68k_reset_callback(int state);
	u32 buserror_r();

	TIMER_DEVICE_CALLBACK_MEMBER(micro20_timer);
	void h4_w(int state);
	void portb_w(u8 data);
	void portc_w(u8 data);

	void timerirq_w(int state)
	{
		m_maincpu->set_input_line(M68K_IRQ_4, state);
	}

	void micro20_map(address_map &map) ATTR_COLD;

	u8 m_tin;
	u8 m_h4;
};

void micro20_state::machine_start()
{
}

void micro20_state::machine_reset()
{
	u32 *pROM = (uint32_t *)m_rom->base();
	u32 *pRAM = (uint32_t *)m_mainram.target();

	pRAM[0] = pROM[2];
	pRAM[1] = pROM[3];
	m_maincpu->reset();

	m_tin = 0;
}

TIMER_DEVICE_CALLBACK_MEMBER(micro20_state::micro20_timer)
{
	m_pit->update_tin(m_tin ? ASSERT_LINE : CLEAR_LINE);
	if (!m_h4 && m_tin)
		m_maincpu->set_input_line(M68K_IRQ_6, HOLD_LINE);

	m_tin ^= 1;
}

void micro20_state::h4_w(int state)
{
	printf("h4_w: %d\n", state);
	m_h4 = state ^ 1;
}

void micro20_state::m68k_reset_callback(int state)
{
	// startup test explicitly checks if the m68k RESET opcode resets the 68230
	m_pit->reset();
}

void micro20_state::portb_w(u8 data)
{
	m_rtc->d0_w((data & 1) ? ASSERT_LINE : CLEAR_LINE);
	m_rtc->d1_w((data & 2) ? ASSERT_LINE : CLEAR_LINE);
	m_rtc->d2_w((data & 4) ? ASSERT_LINE : CLEAR_LINE);
	m_rtc->d3_w((data & 8) ? ASSERT_LINE : CLEAR_LINE);
}

void micro20_state::portc_w(u8 data)
{
	// MSM58321 CS1 and CS2 are tied to /RST, inverted RESET.
	// So they're always high when the system is not reset.
	m_rtc->cs1_w(ASSERT_LINE);
	m_rtc->cs2_w(ASSERT_LINE);
	m_rtc->stop_w((data & 1) ? ASSERT_LINE : CLEAR_LINE);
	m_rtc->write_w((data & 2) ? ASSERT_LINE : CLEAR_LINE);
	m_rtc->read_w((data & 0x10) ? ASSERT_LINE : CLEAR_LINE);
	m_rtc->address_write_w((data & 0x40) ? ASSERT_LINE : CLEAR_LINE);
	m_rtc->test_w((data & 0x80) ? ASSERT_LINE : CLEAR_LINE);
}

u32 micro20_state::buserror_r()
{
	m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
	m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
	return 0xffff;
}
/***************************************************************************
    ADDRESS MAPS
***************************************************************************/

void micro20_state::micro20_map(address_map &map)
{
	map(0x00000000, 0x001fffff).ram().share("mainram");
	map(0x00200000, 0x002fffff).r(FUNC(micro20_state::buserror_r));
	map(0x00800000, 0x0083ffff).rom().region("bootrom", 0);
	map(0xffff8000, 0xffff8000).rw(FDC_TAG, FUNC(wd1772_device::status_r), FUNC(wd1772_device::cmd_w));
	map(0xffff8001, 0xffff8001).rw(FDC_TAG, FUNC(wd1772_device::track_r), FUNC(wd1772_device::track_w));
	map(0xffff8002, 0xffff8002).rw(FDC_TAG, FUNC(wd1772_device::sector_r), FUNC(wd1772_device::sector_w));
	map(0xffff8003, 0xffff8003).rw(FDC_TAG, FUNC(wd1772_device::data_r), FUNC(wd1772_device::data_w));
	map(0xffff8080, 0xffff808f).rw(DUART_A_TAG, FUNC(mc68681_device::read), FUNC(mc68681_device::write));
	map(0xffff80a0, 0xffff80af).rw(DUART_B_TAG, FUNC(mc68681_device::read), FUNC(mc68681_device::write));
	map(0xffff80c0, 0xffff80df).rw(m_pit, FUNC(pit68230_device::read), FUNC(pit68230_device::write));
}

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_19200 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_19200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void micro20_state::micro20(machine_config &config)
{
	/* basic machine hardware */
	M68020(config, m_maincpu, 16.67_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &micro20_state::micro20_map);
	m_maincpu->reset_cb().set(FUNC(micro20_state::m68k_reset_callback));

	mc68681_device &duart_a(MC68681(config, DUART_A_TAG, 3.6864_MHz_XTAL));
	duart_a.a_tx_cb().set("rs232", FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(DUART_A_TAG, FUNC(mc68681_device::rx_a_w));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	MC68681(config, DUART_B_TAG, 3.6864_MHz_XTAL);

	WD1772(config, FDC_TAG, 16.67_MHz_XTAL / 2);

	PIT68230(config, m_pit, 16.67_MHz_XTAL / 2);
	m_pit->timer_irq_callback().set(FUNC(micro20_state::timerirq_w));
	m_pit->h4_out_callback().set(FUNC(micro20_state::h4_w));
	m_pit->pb_out_callback().set(FUNC(micro20_state::portb_w));
	m_pit->pc_out_callback().set(FUNC(micro20_state::portc_w));

	MSM58321(config, m_rtc, 32768_Hz_XTAL);
	m_rtc->set_default_24h(false);
	m_rtc->d0_handler().set(m_pit, FUNC(pit68230_device::pb0_w));
	m_rtc->d1_handler().set(m_pit, FUNC(pit68230_device::pb1_w));
	m_rtc->d2_handler().set(m_pit, FUNC(pit68230_device::pb2_w));
	m_rtc->d3_handler().set(m_pit, FUNC(pit68230_device::pb3_w));
	m_rtc->busy_handler().set(m_pit, FUNC(pit68230_device::pb7_w));

	TIMER(config, "timer").configure_periodic(FUNC(micro20_state::micro20_timer), attotime::from_hz(200));
}

static INPUT_PORTS_START( micro20 )
INPUT_PORTS_END

/***************************************************************************

  Machine driver(s)

***************************************************************************/


ROM_START( micro20 )
	ROM_REGION32_BE(0x40000, "bootrom", 0)
	ROM_LOAD32_BYTE( "d00-07_u6_6791.bin",  0x000003, 0x010000, CRC(63d66ea1) SHA1(c5dfbc4d81920e1d2e981c52c1af3d486d382a35) )
	ROM_LOAD32_BYTE( "d08-15_u8_0dc6.bin",  0x000002, 0x010000, CRC(d62ef21f) SHA1(2779d430b1a0b835807627e707d46547b29ef579) )
	ROM_LOAD32_BYTE( "d16-23_u10_e5b0.bin", 0x000001, 0x010000, CRC(cd7acf86) SHA1(db994ed714a1079fbb66616355e8f18d2d1a2005) )
	ROM_LOAD32_BYTE( "d24-31_u13_d115.bin", 0x000000, 0x010000, CRC(3646d943) SHA1(97ee54063e2fe49fef2ff68d0f2e39345a75eac5) )
ROM_END

} // anonymous namespace


COMP( 1984, micro20, 0, 0, micro20, micro20, micro20_state, empty_init, "GMX", "Micro 20", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



microb.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert, AJR
/***************************************************************************

BEEHIVE Micro B Series

2009-05-25 Skeleton driver [Robbbert]
2011-04-25 Added partial keyboard.
2011-06-26 Added modifier keys.

This is a series of conventional computer terminals using a serial link.
DM3270 is a clone of the IBM3276-2.

The character gen rom is not dumped. Using the one from 'c10'
 for the moment.

System beeps if ^G or ^Z pressed. Pressing ^Q is the same as Enter.

****************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/input_merger.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/i8257.h"
#include "machine/pit8253.h"
#include "video/i8275.h"
#include "bus/rs232/rs232.h"
#include "sound/beep.h"
#include "screen.h"
#include "speaker.h"

namespace {

class microb_state : public driver_device
{
public:
	microb_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_dmac(*this, "dmac")
		, m_p_chargen(*this, "chargen")
		, m_beep(*this, "beep")
		, m_usart(*this, "usart%u", 1U)
		, m_rs232(*this, "rs232%c", 'a')
		, m_io_keyboard(*this, "X%u", 0U)
	{ }

	void microb(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void dmac_hrq_w(int state);
	u8 dmac_mem_r(offs_t offset);
	void dmac_mem_w(offs_t offset, u8 data);
	I8275_DRAW_CHARACTER_MEMBER(draw_character);

	u8 ppi2_pa_r();
	void ppi2_pc_w(u8 data);

	void microb_io(address_map &map) ATTR_COLD;
	void microb_mem(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<i8257_device> m_dmac;
	required_region_ptr<u8> m_p_chargen;
	required_device<beep_device> m_beep;
	required_device_array<i8251_device, 2> m_usart;
	required_device_array<rs232_port_device, 2> m_rs232;
	required_ioport_array<16> m_io_keyboard;

	u8 m_keyline = 0U;
};

void microb_state::dmac_hrq_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);
	m_dmac->hlda_w(state);
}

u8 microb_state::dmac_mem_r(offs_t offset)
{
	return m_maincpu->space(AS_PROGRAM).read_byte(offset);
}

void microb_state::dmac_mem_w(offs_t offset, u8 data)
{
	return m_maincpu->space(AS_PROGRAM).write_byte(offset, data);
}

u8 microb_state::ppi2_pa_r()
{
	return m_io_keyboard[m_keyline & 15]->read();
}

void microb_state::ppi2_pc_w(u8 data)
{
	m_keyline = data;
	m_beep->set_state(!BIT(data, 4));
}

void microb_state::microb_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x17ff).rom();
	map(0x8000, 0x8fff).ram();
}

void microb_state::microb_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x09).rw(m_dmac, FUNC(i8257_device::read), FUNC(i8257_device::write));
	map(0x10, 0x13).rw("ppi1", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x20, 0x21).rw(m_usart[0], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x30, 0x31).rw(m_usart[1], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x50, 0x51).rw("crtc", FUNC(i8275_device::read), FUNC(i8275_device::write));
	map(0x60, 0x63).rw("ppi2", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xe0, 0xe3).rw("pit1", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xf0, 0xf3).rw("pit2", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
}

/* Input ports */
static INPUT_PORTS_START( microb )
	PORT_START("X0")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q")  PORT_CODE(KEYCODE_Q)    PORT_CHAR('q') PORT_CHAR('Q')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W")  PORT_CODE(KEYCODE_W)    PORT_CHAR('w') PORT_CHAR('W')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E")  PORT_CODE(KEYCODE_E)    PORT_CHAR('e') PORT_CHAR('E')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R")  PORT_CODE(KEYCODE_R)    PORT_CHAR('r') PORT_CHAR('R')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T")  PORT_CODE(KEYCODE_T)    PORT_CHAR('t') PORT_CHAR('T')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y")  PORT_CODE(KEYCODE_Y)    PORT_CHAR('y') PORT_CHAR('Y')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U")  PORT_CODE(KEYCODE_U)    PORT_CHAR('u') PORT_CHAR('U')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I")  PORT_CODE(KEYCODE_I)    PORT_CHAR('i') PORT_CHAR('I')

	PORT_START("X1")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O")  PORT_CODE(KEYCODE_O)    PORT_CHAR('o') PORT_CHAR('O')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P")  PORT_CODE(KEYCODE_P)    PORT_CHAR('p') PORT_CHAR('P')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A")  PORT_CODE(KEYCODE_A)    PORT_CHAR('a') PORT_CHAR('A')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S")  PORT_CODE(KEYCODE_S)    PORT_CHAR('s') PORT_CHAR('S')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D")  PORT_CODE(KEYCODE_D)    PORT_CHAR('d') PORT_CHAR('D')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F")  PORT_CODE(KEYCODE_F)    PORT_CHAR('f') PORT_CHAR('F')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G")  PORT_CODE(KEYCODE_G)    PORT_CHAR('g') PORT_CHAR('G')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H")  PORT_CODE(KEYCODE_H)    PORT_CHAR('h') PORT_CHAR('H')

	PORT_START("X2")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J")  PORT_CODE(KEYCODE_J)    PORT_CHAR('j') PORT_CHAR('J')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K")  PORT_CODE(KEYCODE_K)    PORT_CHAR('k') PORT_CHAR('K')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L")  PORT_CODE(KEYCODE_L)    PORT_CHAR('l') PORT_CHAR('L')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z")  PORT_CODE(KEYCODE_Z)    PORT_CHAR('z') PORT_CHAR('Z')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X")  PORT_CODE(KEYCODE_X)    PORT_CHAR('x') PORT_CHAR('X')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C")  PORT_CODE(KEYCODE_C)    PORT_CHAR('c') PORT_CHAR('C')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V")  PORT_CODE(KEYCODE_V)    PORT_CHAR('v') PORT_CHAR('V')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B")  PORT_CODE(KEYCODE_B)    PORT_CHAR('b') PORT_CHAR('B')

	PORT_START("X3")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N")  PORT_CODE(KEYCODE_N)    PORT_CHAR('n') PORT_CHAR('N')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M")  PORT_CODE(KEYCODE_M)    PORT_CHAR('m') PORT_CHAR('M')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("`")  PORT_CODE(KEYCODE_TILDE) PORT_CHAR('`') PORT_CHAR('~')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[")  PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR(']')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";")  PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("'")  PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("{")  PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('{') PORT_CHAR('}')

	PORT_START("X4")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("<")  PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('<') PORT_CHAR('>')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",")  PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".")  PORT_CODE(KEYCODE_STOP) PORT_CHAR('.')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/")  PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1")  PORT_CODE(KEYCODE_1)    PORT_CHAR('1') PORT_CHAR('!')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2")  PORT_CODE(KEYCODE_2)    PORT_CHAR('2') PORT_CHAR('@')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3")  PORT_CODE(KEYCODE_3)    PORT_CHAR('3') PORT_CHAR('#')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4")  PORT_CODE(KEYCODE_4)    PORT_CHAR('4') PORT_CHAR('$')

	PORT_START("X5")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5")  PORT_CODE(KEYCODE_5)    PORT_CHAR('5') PORT_CHAR('%')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6")  PORT_CODE(KEYCODE_6)    PORT_CHAR('6') PORT_CHAR('^')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7")  PORT_CODE(KEYCODE_7)    PORT_CHAR('7') PORT_CHAR('&')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8")  PORT_CODE(KEYCODE_8)    PORT_CHAR('8') PORT_CHAR('*')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9")  PORT_CODE(KEYCODE_9)    PORT_CHAR('9') PORT_CHAR('(')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0")  PORT_CODE(KEYCODE_0)    PORT_CHAR('0') PORT_CHAR(')')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-")  PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("=")  PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')

	PORT_START("X6")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7pad")   PORT_CODE(KEYCODE_7_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8pad")   PORT_CODE(KEYCODE_8_PAD)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9pad")   PORT_CODE(KEYCODE_9_PAD)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X7")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED) // Does a HOME
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Home")   PORT_CODE(KEYCODE_HOME)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4pad")   PORT_CODE(KEYCODE_4_PAD)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5pad")   PORT_CODE(KEYCODE_5_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6pad")   PORT_CODE(KEYCODE_6_PAD)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Backspace")  PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X8")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1pad")   PORT_CODE(KEYCODE_1_PAD)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2pad")   PORT_CODE(KEYCODE_2_PAD)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3pad")   PORT_CODE(KEYCODE_3_PAD)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc")    PORT_CODE(KEYCODE_ESC)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Show/Hide Status")
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down")   PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0pad")   PORT_CODE(KEYCODE_0_PAD)

	PORT_START("X9")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".pad")   PORT_CODE(KEYCODE_DEL_PAD)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-pad")   PORT_CODE(KEYCODE_MINUS_PAD)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cursor Blink On/Off")
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED) // carriage return
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left")   PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))

	PORT_START("X10")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right")  PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space")  PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("XMIT")   PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	// This row is scanned but nothing happens
	PORT_START("X11")
		PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	// These probably not exist
	PORT_START("X12")
		PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("X13")
		PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("X14")
		PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("X15")
		PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("MODIFIERS")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Capslock") PORT_CODE(KEYCODE_CAPSLOCK)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LCtrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RCtrl") PORT_CODE(KEYCODE_RCONTROL)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LShift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RShift") PORT_CODE(KEYCODE_RSHIFT)
		PORT_BIT(0x83, IP_ACTIVE_LOW, IPT_UNUSED)

	// assumed to be dipswitches, purpose unknown, see code from 12D
	PORT_START("DIPS")
	PORT_DIPNAME( 0x01, 0x01, "Switch A") PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(    0x01, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x02, 0x02, "Switch B") PORT_DIPLOCATION("SW1:2")
	PORT_DIPSETTING(    0x02, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_BIT(0x3c, 0x2c, IPT_UNUSED) // this is required to sync keyboard and A-LOCK indicator
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED) // unused
	PORT_DIPNAME( 0x80, 0x80, "Switch C") PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(    0x80, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
INPUT_PORTS_END


void microb_state::machine_start()
{
	save_item(NAME(m_keyline));
}

I8275_DRAW_CHARACTER_MEMBER(microb_state::draw_character)
{
	using namespace i8275_attributes;

	u8 dots = BIT(attrcode, LTEN) ? 0xff : (BIT(attrcode, VSP) || linecount == 9) ? 0 : m_p_chargen[(charcode << 4) | linecount];
	if (BIT(attrcode, RVV))
		dots ^= 0xff;

	// HLGT is active on status line
	rgb_t const fg = BIT(attrcode, HLGT) ? rgb_t(0xc0, 0xc0, 0xc0) : rgb_t::white();

	u32 *pix = &bitmap.pix(y, x);
	for (int i = 0; i < 8; i++)
	{
		*pix++ = BIT(dots, 7) ? fg : rgb_t::black();
		dots <<= 1;
	}
}

void microb_state::microb(machine_config &config)
{
	/* basic machine hardware */
	I8085A(config, m_maincpu, XTAL(4'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &microb_state::microb_mem);
	m_maincpu->set_addrmap(AS_IO, &microb_state::microb_io);

	INPUT_MERGER_ANY_HIGH(config, "usartint").output_handler().set_inputline(m_maincpu, I8085_RST55_LINE);

	I8257(config, m_dmac, 2'000'000);
	m_dmac->out_hrq_cb().set(FUNC(microb_state::dmac_hrq_w));
	m_dmac->in_memr_cb().set(FUNC(microb_state::dmac_mem_r));
	m_dmac->out_memw_cb().set(FUNC(microb_state::dmac_mem_w));
	m_dmac->out_iow_cb<2>().set("crtc", FUNC(i8275_device::dack_w));
	m_dmac->out_tc_cb().set_inputline(m_maincpu, I8085_RST75_LINE);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_raw(1'620'000 * 8, 800, 0, 640, 324, 0, 300);
	//screen.set_raw(1'620'000 * 8, 800, 0, 640, 270, 0, 250);
	screen.set_screen_update("crtc", FUNC(i8275_device::screen_update));

	i8275_device &crtc(I8275(config, "crtc", 1'620'000));
	crtc.set_screen("screen");
	crtc.set_character_width(8);
	crtc.set_display_callback(FUNC(microb_state::draw_character));
	crtc.irq_wr_callback().set_inputline(m_maincpu, I8085_RST65_LINE);
	crtc.drq_wr_callback().set(m_dmac, FUNC(i8257_device::dreq2_w));

	i8255_device &ppi1(I8255(config, "ppi1"));
	ppi1.in_pb_callback().set_ioport("DIPS");

	i8255_device &ppi2(I8255(config, "ppi2"));
	ppi2.in_pa_callback().set(FUNC(microb_state::ppi2_pa_r));
	ppi2.in_pb_callback().set_ioport("MODIFIERS");
	ppi2.out_pc_callback().set(FUNC(microb_state::ppi2_pc_w));

	pit8253_device &pit1(PIT8253(config, "pit1"));
	pit1.set_clk<2>(1'536'000);
	pit1.out_handler<2>().set(m_usart[1], FUNC(i8251_device::write_rxc));

	pit8253_device &pit2(PIT8253(config, "pit2"));
	pit2.set_clk<0>(1'536'000);
	pit2.set_clk<1>(1'536'000);
	pit2.set_clk<2>(1'536'000);
	pit2.out_handler<0>().set(m_usart[0], FUNC(i8251_device::write_txc));
	pit2.out_handler<1>().set(m_usart[0], FUNC(i8251_device::write_rxc));
	pit2.out_handler<2>().set(m_usart[1], FUNC(i8251_device::write_txc));

	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 1000).add_route(ALL_OUTPUTS, "mono", 0.5);

	I8251(config, m_usart[0], 0);
	m_usart[0]->txd_handler().set(m_rs232[0], FUNC(rs232_port_device::write_txd));
	m_usart[0]->dtr_handler().set(m_rs232[0], FUNC(rs232_port_device::write_dtr));
	m_usart[0]->rts_handler().set(m_rs232[0], FUNC(rs232_port_device::write_rts));
	m_usart[0]->rxrdy_handler().set("usartint", FUNC(input_merger_device::in_w<0>));
	m_usart[0]->txrdy_handler().set("usartint", FUNC(input_merger_device::in_w<1>));

	RS232_PORT(config, m_rs232[0], default_rs232_devices, nullptr);
	m_rs232[0]->rxd_handler().set(m_usart[0], FUNC(i8251_device::write_rxd));
	m_rs232[0]->dsr_handler().set(m_usart[0], FUNC(i8251_device::write_dsr));
	m_rs232[0]->cts_handler().set(m_usart[0], FUNC(i8251_device::write_cts));

	I8251(config, m_usart[1], 0);
	m_usart[1]->txd_handler().set(m_rs232[1], FUNC(rs232_port_device::write_txd));
	m_usart[1]->dtr_handler().set(m_rs232[1], FUNC(rs232_port_device::write_dtr));
	m_usart[1]->rts_handler().set(m_rs232[1], FUNC(rs232_port_device::write_rts));
	m_usart[1]->rxrdy_handler().set("usartint", FUNC(input_merger_device::in_w<2>));
	m_usart[1]->txrdy_handler().set("usartint", FUNC(input_merger_device::in_w<3>));

	RS232_PORT(config, m_rs232[1], default_rs232_devices, nullptr);
	m_rs232[1]->rxd_handler().set(m_usart[1], FUNC(i8251_device::write_rxd));
	m_rs232[1]->dsr_handler().set(m_usart[1], FUNC(i8251_device::write_dsr));
	m_rs232[1]->cts_handler().set(m_usart[1], FUNC(i8251_device::write_cts));
}

/* ROM definition */
ROM_START( dm3270 )
	ROM_REGION( 0x1800, "maincpu", 0 )
	ROM_LOAD( "dm3270-1.rom", 0x0000, 0x0800, CRC(781bde32) SHA1(a3fe25baadd2bfc2b1791f509bb0f4960281ee32) )
	ROM_LOAD( "dm3270-2.rom", 0x0800, 0x0800, CRC(4d3476b7) SHA1(627ad42029ca6c8574cda8134d047d20515baf53) )
	ROM_LOAD( "dm3270-3.rom", 0x1000, 0x0800, CRC(dbf15833) SHA1(ae93117260a259236c50885c5cecead2aad9b3c4) )

	/* character generator not dumped, using the one from 'c10' for now */
	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "c10_char.bin", 0x0000, 0x2000, BAD_DUMP CRC(cb530b6f) SHA1(95590bbb433db9c4317f535723b29516b9b9fcbf))
ROM_END

} // Anonymous namespace

//    YEAR  NAME    PARENT  COMPAT  MACHINE INPUT   CLASS         INIT        COMPANY                  FULLNAME                               FLAGS
COMP( 1982, dm3270, 0,      0,      microb, microb, microb_state, empty_init, "Beehive International", "DM3270 Control Unit Display Station", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )



microbox2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/***************************************************************************

    Micro Concepts Microbox II

    Usage:
      Insert the FLEX disk in drive 0 and the Distribution disk in drive 1,
      then type BO to boot FLEX.
      Type DEMO.1 to run the demo from drive 1.

    TODO:
    - implement WD2123 device (dual channel 8251A)
    - improve UPD7220A to handle scrolling

***************************************************************************/

#include "emu.h"

#include "bus/centronics/ctronics.h"
#include "bus/rs232/rs232.h"
#include "cpu/m6809/m6809.h"
#include "formats/flex_dsk.h"
#include "imagedev/floppy.h"
#include "machine/6821pia.h"
#include "machine/6883sam.h"
#include "machine/i8255.h"
#include "machine/keyboard.h"
#include "machine/mc146818.h"
#include "machine/wd_fdc.h"
//#include "machine/wd2123.h"
#include "sound/beep.h"
#include "video/upd7220.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class microbx2_state : public driver_device
{
public:
	microbx2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_epromdisc(*this, "epromdisc")
		, m_ram(*this, "ram", 0x10000, ENDIANNESS_BIG)
		, m_map_view(*this, "map_view")
		, m_pia1(*this, "pia1")
		, m_video_ram(*this, "video_ram")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0)
		, m_beeper(*this, "beeper")
		, m_sw1(*this, "SW1")
	{}

	static constexpr feature_type unemulated_features() { return feature::COMMS; }

	void microbx2(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_region_ptr<uint8_t> m_epromdisc;
	memory_share_creator<uint8_t> m_ram;
	memory_view m_map_view;
	required_device<pia6821_device> m_pia1;
	required_shared_ptr<uint16_t> m_video_ram;
	required_device<wd1770_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<beep_device> m_beeper;
	required_ioport m_sw1;

	void upd7220_map(address_map &map) ATTR_COLD;

	UPD7220_DISPLAY_PIXELS_MEMBER(display_pixels);

	void mem_map(address_map &map) ATTR_COLD;
	void ram_map(address_map &map) ATTR_COLD;
	void rom0_map(address_map &map) ATTR_COLD;
	void rom1_map(address_map &map) ATTR_COLD;
	void rom2_map(address_map &map) ATTR_COLD;
	void io0_map(address_map &map) ATTR_COLD;
	void io1_map(address_map &map) ATTR_COLD;
	void io2_map(address_map &map) ATTR_COLD;
	void boot_map(address_map &map) ATTR_COLD;

	static void floppy_formats(format_registration &fr);

	void kbd_w(uint8_t data);
	void pia1_pb_w(uint8_t data);
	uint8_t pia2_pa_r();
	void pia2_pb_w(uint8_t data);
	void pia2_pc_w(uint8_t data);

	uint8_t m_keydata;
	uint16_t m_eprom_addr;
	int m_ls393_input;
};


void microbx2_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).rw("sam", FUNC(sam6883_device::read), FUNC(sam6883_device::write));
}

void microbx2_state::ram_map(address_map &map)
{
	// $0000-$FEFF
	map(0x0000, 0xffff).ram().share("ram");
	map(0xe000, 0xefff).view(m_map_view);
	m_map_view[0](0xe000, 0xefff).rom().region("maincpu", 0x0000);
	map(0xf000, 0xffff).rom().region("maincpu", 0x1000);
}

void microbx2_state::rom0_map(address_map &map)
{
	// $8000-$9FFF
	map(0x0000, 0x1fff).rom().region("maincpu", 0);
	map(0x0000, 0x1fff).lw8(NAME([this](offs_t offset, uint8_t data) { m_ram[offset + 0x8000] = data; }));
}

void microbx2_state::rom1_map(address_map &map)
{
	// $A000-$BFFF
	map(0x0000, 0x1fff).rom().region("maincpu", 0);
	map(0x0000, 0x1fff).lw8(NAME([this](offs_t offset, uint8_t data) { m_ram[offset + 0xa000] = data; }));
}

void microbx2_state::rom2_map(address_map &map)
{
	// $C000-$FEFF
	map(0x0000, 0x1fff).mirror(0x2000).rom().region("maincpu", 0);
	map(0x0000, 0x3eff).lw8(NAME([this](offs_t offset, uint8_t data) { m_ram[offset + 0xc000] = data; }));
}

void microbx2_state::io0_map(address_map &map)
{
	// $FF00-$FF1F
	map(0x00, 0x03).rw("pia1", FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	//map(0x04, 0x05).rw("deuce", FUNC(wd2123_device::read_b), FUNC(wd2123_device::write_b));
	//map(0x08, 0x09).rw("deuce", FUNC(wd2123_device::read_a), FUNC(wd2123_device::write_a));
	//map(0x0c, 0x0d).w("deuce", FUNC(wd2123_device::baud));
	map(0x10, 0x13).rw("fdc", FUNC(wd1770_device::read), FUNC(wd1770_device::write));
	map(0x14, 0x15).rw("gdc", FUNC(upd7220a_device::read), FUNC(upd7220a_device::write));
	map(0x18, 0x18).w("rtc", FUNC(mc146818_device::address_w));
	map(0x19, 0x19).rw("rtc", FUNC(mc146818_device::data_r), FUNC(mc146818_device::data_w));
	map(0x1c, 0x1f).rw("pia2", FUNC(i8255_device::read), FUNC(i8255_device::write));
}

void microbx2_state::io1_map(address_map &map)
{
	// $FF20-$FF3F
}

void microbx2_state::io2_map(address_map &map)
{
	// $FF40-$FF5F
}

void microbx2_state::boot_map(address_map &map)
{
	// $FF60-$FFEF
	map(0x60, 0x7f).nopw(); // SAM Registers
}

void microbx2_state::upd7220_map(address_map &map)
{
	map(0x00000, 0x3ffff).ram().share("video_ram");
}


void microbx2_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_FLEX_FORMAT);
}


static INPUT_PORTS_START( microbx2 )
	PORT_START("SW1")
	PORT_DIPNAME(0x10, 0x10, "Initial Input") PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(   0x00, "Serial Keyboard")
	PORT_DIPSETTING(   0x10, "Parallel Keyboard")
	PORT_DIPNAME(0x20, 0x20, "Initial Output") PORT_DIPLOCATION("SW1:2")
	PORT_DIPSETTING(   0x00, "Serial Terminal")
	PORT_DIPSETTING(   0x20, "Video Monitor")
	PORT_DIPNAME(0x40, 0x40, "Step Rate") PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(   0x00, "30ms")
	PORT_DIPSETTING(   0x40, "6ms")
	PORT_DIPNAME(0x80, 0x80, "Auto Boot") PORT_DIPLOCATION("SW1:4")
	PORT_DIPSETTING(   0x00, "FLEX")
	PORT_DIPSETTING(   0x80, "Monitor")
INPUT_PORTS_END


UPD7220_DISPLAY_PIXELS_MEMBER(microbx2_state::display_pixels)
{
	uint16_t const gfx = m_video_ram[address & 0x3ffff];

	for (int i = 0; i < 16; i++)
	{
		bitmap.pix(y, x + i) = BIT(gfx, i) ? rgb_t::white() : rgb_t::black();
	}
}


void microbx2_state::machine_start()
{
	m_ls393_input = 1;

	save_item(NAME(m_eprom_addr));
}


void microbx2_state::kbd_w(uint8_t data)
{
	m_keydata = data;

	m_pia1->ca1_w(0);
	m_pia1->ca1_w(1);
}


void microbx2_state::pia1_pb_w(uint8_t data)
{
	// bit 0: drv
	floppy_image_device *floppy = m_floppy[BIT(data, 0)]->get_device();
	m_fdc->set_floppy(floppy);

	// bit 1: dden
	m_fdc->dden_w(BIT(data, 1));

	// bit 2: map
	if (BIT(data, 2))
		m_map_view.select(0);
	else
		m_map_view.disable();

	// bit 3: sounder
	m_beeper->set_state(BIT(data, 3));

	// bit 6: side
	if (floppy)
		floppy->ss_w(!BIT(data, 6));
}


uint8_t microbx2_state::pia2_pa_r()
{
	// D0-7
	return m_epromdisc[m_eprom_addr];
}

void microbx2_state::pia2_pb_w(uint8_t data)
{
	// A8-15
	m_eprom_addr = (m_eprom_addr & 0xc0ff) | ((data & 0x3f) << 8);
}


void microbx2_state::pia2_pc_w(uint8_t data)
{
	// CE lines (27128 EPROMs)
	m_eprom_addr &= 0x3fff;
	switch (~data & 0x0f)
	{
	case 0x01: m_eprom_addr |= 0x0000; break;
	case 0x02: m_eprom_addr |= 0x4000; break;
	case 0x04: m_eprom_addr |= 0x8000; break;
	case 0x08: m_eprom_addr |= 0xc000; break;
	}

	// LS393 input
	if (!BIT(data, 4) && m_ls393_input)
		m_eprom_addr++;

	m_ls393_input = BIT(data, 4);

	// LS393 clear
	if (BIT(data, 5))
		m_eprom_addr &= 0xff00;
}


static DEVICE_INPUT_DEFAULTS_START(terminal)
	DEVICE_INPUT_DEFAULTS("RS232_TXBAUD",   0xff, RS232_BAUD_9600)
	DEVICE_INPUT_DEFAULTS("RS232_RXBAUD",   0xff, RS232_BAUD_9600)
	DEVICE_INPUT_DEFAULTS("RS232_DATABITS", 0xff, RS232_DATABITS_8)
	DEVICE_INPUT_DEFAULTS("RS232_PARITY",   0xff, RS232_PARITY_NONE)
	DEVICE_INPUT_DEFAULTS("RS232_STOPBITS", 0xff, RS232_STOPBITS_1)
DEVICE_INPUT_DEFAULTS_END


void microbx2_state::microbx2(machine_config &config)
{
	MC6809E(config, m_maincpu, 16_MHz_XTAL / 8);
	m_maincpu->set_addrmap(AS_PROGRAM, &microbx2_state::mem_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(16_MHz_XTAL / 3, 1024, 0, 768, 674, 31, 607);
	screen.set_screen_update("gdc", FUNC(upd7220a_device::screen_update));

	upd7220a_device &gdc(UPD7220A(config, "gdc", 16_MHz_XTAL / 3)); // unverified clock, hand tuned for ~60 Hz
	gdc.set_addrmap(0, &microbx2_state::upd7220_map);
	gdc.set_display_pixels(FUNC(microbx2_state::display_pixels));
	gdc.set_screen("screen");

	sam6883_device &sam(SAM6883(config, "sam", 16_MHz_XTAL, m_maincpu));
	sam.set_addrmap(0, &microbx2_state::ram_map);  // RAM at $0000
	sam.set_addrmap(1, &microbx2_state::rom0_map); // RAM at $8000
	sam.set_addrmap(2, &microbx2_state::rom1_map); // RAM at $A000
	sam.set_addrmap(3, &microbx2_state::rom2_map); // RAM at $C000
	sam.set_addrmap(4, &microbx2_state::io0_map);  // IO0 at $FF00
	sam.set_addrmap(5, &microbx2_state::io1_map);  // IO1 at $FF20
	sam.set_addrmap(6, &microbx2_state::io2_map);  // IO2 at $FF40
	sam.set_addrmap(7, &microbx2_state::boot_map); // BOOT at $FF60

	PIA6821(config, m_pia1);
	m_pia1->readpa_handler().set([this]() { return m_keydata; });
	m_pia1->writepa_handler().set("cent_data_out", FUNC(output_latch_device::write));
	m_pia1->readpb_handler().set_ioport("SW1");
	m_pia1->writepb_handler().set(FUNC(microbx2_state::pia1_pb_w));
	m_pia1->cb2_handler().set("centronics", FUNC(centronics_device::write_strobe));

	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(microbx2_state::kbd_w));

	centronics_device &centronics(CENTRONICS(config, "centronics", centronics_devices, "printer"));
	centronics.ack_handler().set("pia1", FUNC(pia6821_device::cb1_w));
	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	centronics.set_output_latch(cent_data_out);

	i8255_device &pia2(I8255(config, "pia2"));
	pia2.in_pa_callback().set(FUNC(microbx2_state::pia2_pa_r));
	pia2.out_pb_callback().set(FUNC(microbx2_state::pia2_pb_w));
	pia2.out_pc_callback().set(FUNC(microbx2_state::pia2_pc_w));

	mc146818_device &rtc(MC146818(config, "rtc", 32.768_kHz_XTAL));
	rtc.set_binary(true);

	//wd2123_device &deuce(WD2123(config, "deuce", 1.8432_MHz_XTAL));
	//deuce.txd_a_handler().set("rs232a", FUNC(rs232_port_device::write_txd));
	//deuce.rts_a_handler().set("rs232a", FUNC(rs232_port_device::write_rts));
	//deuce.txd_b_handler().set("rs232b", FUNC(rs232_port_device::write_txd));
	//deuce.rts_b_handler().set("rs232b", FUNC(rs232_port_device::write_rts));

	//rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, nullptr));
	//rs232a.rxd_handler().set("deuce", FUNC(wd2123_device::write_rxd_a));
	//rs232a.cts_handler().set("deuce", FUNC(wd2123_device::write_cts_a));
	//rs232a.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	//rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	//rs232b.rxd_handler().set("deuce", FUNC(wd2123_device::write_rxd_b));
	//rs232b.cts_handler().set("deuce", FUNC(wd2123_device::write_cts_b));

	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 1200).add_route(ALL_OUTPUTS, "mono", 0.50); // TODO: unknown frequency

	WD1770(config, m_fdc, 16_MHz_XTAL / 2);

	FLOPPY_CONNECTOR(config, "fdc:0", "525qd", FLOPPY_525_QD, true, microbx2_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", "525qd", FLOPPY_525_QD, true, microbx2_state::floppy_formats).enable_sound(true);

	SOFTWARE_LIST(config, "flop_list").set_original("microbox2_flop");
}


ROM_START(microbx2)
	ROM_REGION(0x2000,"maincpu", 0)
	ROM_SYSTEM_BIOS(0, "450", "Mon09 Ver 4.5")
	ROMX_LOAD("mon09_4.5.bin",  0x0000, 0x2000, CRC(4ccf92bf) SHA1(a0e778ed7498afcbca17082ca64828ed7967b3c3), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "422", "Mon09 Ver 4.22")
	ROMX_LOAD("mon09_4.22.bin", 0x0000, 0x2000, CRC(f6ea63ca) SHA1(a9c77df9f959ac26f21472d53aad8f9f6045c054), ROM_BIOS(1))

	ROM_REGION(0x10000,"epromdisc",0)
	ROM_LOAD("eprom_disc_1.bin", 0x0000, 0x4000, CRC(5132c397) SHA1(cb882af5cd6632503b9f8ec98e863c8217f0022a))
	ROM_LOAD("eprom_disc_2.bin", 0x4000, 0x4000, CRC(dcc3862a) SHA1(a78df097b9e5c8c6cf75a1af0d081bd6cf0cb39a))
	ROM_LOAD("eprom_disc_3.bin", 0x8000, 0x4000, CRC(64de116d) SHA1(dd8001aae11fa6f2d0b2061a64e1f1ef59cb2bc3))
	ROM_LOAD("eprom_disc_4.bin", 0xc000, 0x4000, CRC(4533d8d9) SHA1(56f0d2fed44841cdfb019e49632c5ceca67c92b2))
ROM_END

} // anonymous namespace


//    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT         COMPANY            FULLNAME        FLAGS
COMP( 1984, microbx2,  0,      0,      microbx2,  microbx2,  microbx2_state,  empty_init,  "Micro Concepts",  "Microbox II",  MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )



microdec.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/**********************************************************************************************

Morrow Designs Micro Decision

2009-12-10 Skeleton driver.

Although it looks like an ordinary CP/M computer, the monitor and keyboard are actually a
dumb terminal plugged into the base unit. Therefore the roms and details of the terminal are
needed.

Board design changes depending on bios version of base unit:
In earliest design, F7 sets up VFO and F8 selects motor on, while fdc does drive select.
Later version gets rid of these, and F7 now does motor on and drive select; also addition of
i8253 timer, a centronics port, and a diagnostic jumper. F8 is unused.

Currently (as at 2016-07-17), memory test works, rom banking works, disk does NOT boot.
Ver 1 roms say "Not found", Ver 2 roms hang after pressing enter, Ver 3 rom hangs after memory test.

ToDo:
- Make the floppy boot
- Add i8253 timer chip
- Add centronics parts
- Different hardware for different bios versions (only earliest design is coded)

***********************************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/upd765.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "bus/rs232/rs232.h"
#include "softlist_dev.h"


namespace {

class microdec_state : public driver_device
{
public:
	microdec_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_fdc(*this, "fdc")
		, m_floppy0(*this, "fdc:0")
		, m_floppy(nullptr)
		, m_rom_view(*this, "rom")
	{ }

	void microdec(machine_config &config);

	void init_microdec();

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	uint8_t portf5_r();
	uint8_t portf6_r();
	void portf6_w(uint8_t data);
	uint8_t portf7_r();
	void portf7_w(uint8_t data);
	void portf8_w(uint8_t data);

	void microdec_io(address_map &map) ATTR_COLD;
	void microdec_mem(address_map &map) ATTR_COLD;

	uint8_t m_portf8 = 0U;
	bool m_fdc_rdy = 0;

	required_device<cpu_device> m_maincpu;
	required_device<upd765a_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	floppy_image_device *m_floppy;
	memory_view m_rom_view;
};


/*
d0-2 : motor on signals from f8
d3   : ack (cent)
d4   : ready (fdd)
d5   : diag jumper (md3 only) */
uint8_t microdec_state::portf5_r()
{
	if (!machine().side_effects_disabled())
		m_fdc->set_ready_line_connected(m_fdc_rdy);

	uint8_t data = m_portf8 | ioport("DIAG")->read() | 0xc0;
	return data;
}

// disable eprom
uint8_t microdec_state::portf6_r()
{
	if (!machine().side_effects_disabled())
		m_rom_view.disable(); // point at ram
	return 0xff;
}

// TC pin on fdc
uint8_t microdec_state::portf7_r()
{
	if (!machine().side_effects_disabled())
	{
		m_fdc->tc_w(1);
		m_fdc->tc_w(0);
	}
	return 0xff;
}

// enable eprom
void microdec_state::portf6_w(uint8_t data)
{
	m_rom_view.select(0); // point at rom
}

// sets up VFO stuff
void microdec_state::portf7_w(uint8_t data)
{
	m_fdc_rdy = BIT(data,2);
}

/*
d0-2 : motor on for drive sockets
d3   : precomp */
void microdec_state::portf8_w(uint8_t data)
{
	m_portf8 = data & 7;
	/* code for motor on per drive goes here */
	m_floppy = m_floppy0->get_device();
	m_fdc->set_floppy(m_floppy);

	if (m_floppy)
		m_floppy->mon_w(0);
}

void microdec_state::microdec_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).ram();
	map(0x0000, 0x0fff).view(m_rom_view);
	m_rom_view[0](0x0000, 0x0fff).rom().region("maincpu", 0);
}

void microdec_state::microdec_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0xf5, 0xf5).r(FUNC(microdec_state::portf5_r));
	map(0xf6, 0xf6).rw(FUNC(microdec_state::portf6_r), FUNC(microdec_state::portf6_w));
	map(0xf7, 0xf7).rw(FUNC(microdec_state::portf7_r), FUNC(microdec_state::portf7_w));
	map(0xf8, 0xf8).w(FUNC(microdec_state::portf8_w));
	map(0xfa, 0xfb).m(m_fdc, FUNC(upd765a_device::map));
	map(0xfc, 0xfd).rw("uart1", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xfe, 0xff).rw("uart2", FUNC(i8251_device::read), FUNC(i8251_device::write));
	// map(0xf0, 0xf3) 8253 PIT (md3 only) used as a baud rate generator for serial ports
	// map(0xf4, 0xf4) Centronics data
	// map(0xf5, 0xf5) motor check (md1/2)
	// map(0xf5, 0xf5) Centronics status (md3) read bit 3 (ack=1); read bit 4 (busy=1); write bit 7 (stb=0)
	// map(0xf6, 0xf6) rom enable (w=enable; r=disable)
	// map(0xf7, 0xf7) VFO Count set
	// map(0xf8, 0xf8) Motor and Shift control
	// map(0xfa, 0xfb) uPD765C fdc FA=status; FB=data
	// map(0xfc, 0xfd) Serial Port 1 (terminal) FC=data FD=status
	// map(0xfe, 0xff) Serial Port 2 (printer) FE=data FF=status
}

/* Input ports */
static INPUT_PORTS_START( microdec )
	PORT_START("DIAG")
	PORT_DIPNAME( 0x20, 0x20, "Diagnostics" )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

void microdec_state::machine_start()
{
	save_item(NAME(m_portf8));
	save_item(NAME(m_fdc_rdy));
}

void microdec_state::machine_reset()
{
	m_rom_view.select(0); // point at rom
}

static void microdec_floppies(device_slot_interface &device)
{
	device.option_add("525hd", FLOPPY_525_HD);
}

void microdec_state::init_microdec()
{
	m_fdc->set_ready_line_connected(1);
}

void microdec_state::microdec(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &microdec_state::microdec_mem);
	m_maincpu->set_addrmap(AS_IO, &microdec_state::microdec_io);
	m_maincpu->set_irq_acknowledge_callback(NAME([](device_t &, int) -> int { return 0x7f; })); // 7407 drives D7 low

	/* video hardware */
	clock_device &uart_clock(CLOCK(config, "uart_clock", 16_MHz_XTAL / 4 / 13 / 2)); // TODO: rate configured by SW1/SW1 or PIT
	uart_clock.signal_handler().set("uart1", FUNC(i8251_device::write_txc));
	uart_clock.signal_handler().append("uart1", FUNC(i8251_device::write_rxc));
	uart_clock.signal_handler().append("uart2", FUNC(i8251_device::write_txc));
	uart_clock.signal_handler().append("uart2", FUNC(i8251_device::write_rxc));

	i8251_device &uart1(I8251(config, "uart1", 16_MHz_XTAL / 8));
	uart1.txd_handler().set("rs232a", FUNC(rs232_port_device::write_txd));
	uart1.dtr_handler().set("rs232a", FUNC(rs232_port_device::write_dtr));
	uart1.rts_handler().set("rs232a", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, "terminal"));
	rs232a.rxd_handler().set("uart1", FUNC(i8251_device::write_rxd));
	rs232a.dsr_handler().set("uart1", FUNC(i8251_device::write_dsr));
	rs232a.cts_handler().set("uart1", FUNC(i8251_device::write_cts));

	i8251_device &uart2(I8251(config, "uart2", 16_MHz_XTAL / 8));
	uart2.txd_handler().set("rs232b", FUNC(rs232_port_device::write_txd));
	uart2.dtr_handler().set("rs232b", FUNC(rs232_port_device::write_dtr));
	uart2.rts_handler().set("rs232b", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set("uart2", FUNC(i8251_device::write_rxd));
	rs232b.dsr_handler().set("uart2", FUNC(i8251_device::write_dsr));
	rs232b.cts_handler().set("uart2", FUNC(i8251_device::write_cts));

	UPD765A(config, m_fdc, 16_MHz_XTAL / 4, true, true);
	m_fdc->intrq_wr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	FLOPPY_CONNECTOR(config, "fdc:0", microdec_floppies, "525hd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	//FLOPPY_CONNECTOR(config, "fdc:1", microdec_floppies, "525hd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	// software lists
	SOFTWARE_LIST(config, "flop_list").set_original("md2_flop");
}

/* ROM definition */
ROM_START( md2 )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v13", "v1.3" )
	ROMX_LOAD("md2-13.bin",  0x0000, 0x0800, CRC(43f4c9ab) SHA1(48a35cbee4f341310e9cba5178c3fd6e74ef9748), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v13a", "v1.3a" )
	ROMX_LOAD("md2-13a.bin", 0x0000, 0x0800, CRC(d7fcddfd) SHA1(cae29232b737ebb36a27b8ad17bc69e9968f1309), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "v13b", "v1.3b" )
	ROMX_LOAD("md2-13b.bin", 0x0000, 0x1000, CRC(a8b96835) SHA1(c6b111939aa7e725da507da1915604656540b24e), ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "v20", "v2.0" )
	ROMX_LOAD("md2-20.bin",  0x0000, 0x1000, CRC(a604735c) SHA1(db6e6e82a803f5cbf4f628f5778a93ae3e211fe1), ROM_BIOS(3))
	ROM_SYSTEM_BIOS( 4, "v23", "v2.3" )
	ROMX_LOAD("md2-23.bin",  0x0000, 0x1000, CRC(49bae273) SHA1(00381a226fe250aa3636b0b740df0af63efb0d18), ROM_BIOS(4))
ROM_END

ROM_START( md3 )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v23a", "v2.3a" )
	ROMX_LOAD("md3-23a.bin", 0x0000, 0x1000, CRC(95d59980) SHA1(ae65a8e8e2823cf4cf6b1d74c0996248e290e9f1), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v25", "v2.5" )
	ROMX_LOAD("md3-25.bin",  0x0000, 0x1000, CRC(14f86bc5) SHA1(82fe022c85f678744bb0340ca3f88b18901fdfcb), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "v31", "v3.1" )
	ROMX_LOAD("md3-31.bin",  0x0000, 0x1000, CRC(bd4014f6) SHA1(5b33220af34c64676756177db4915f97840b2996), ROM_BIOS(2))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME  PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT           COMPANY           FULLNAME               FLAGS
COMP( 1982, md2,  0,      0,      microdec, microdec, microdec_state, init_microdec, "Morrow Designs", "Micro Decision MD-2", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1982, md3,  md2,    0,      microdec, microdec, microdec_state, init_microdec, "Morrow Designs", "Micro Decision MD-3", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



microkit.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder, Robbbert
/*

    RCA COSMAC Microkit

    http://www.vintagecomputer.net/browse_thread.cfm?id=511

    Press CR or LF to get the * prompt.
    Commands (must be in UPPERcase):
    $Pxxxx - Jump to address xxxx
    ?Mxxxx yyyy - Dump memory starting at xxxx for yyyy bytes
    !Mxxxx yyzz... - Write data (yy etc) to memory xxxx onwards.

    There's no sound or storage facilities, therefore no software.

    The computer looks like a rack-mount metal box with a rudimentary front panel.
    - Buttons are: Reset; Load; Run program; Run Utility
    - There is a RUN LED.
    - It also has a power switch and lamp, and a fuse.
    - According to the schematic there are LEDs on every data, address and status line.

*****************************************************************************************/

#include "emu.h"
#include "cpu/cosmac/cosmac.h"
#include "bus/rs232/rs232.h"
#include "machine/cdp1852.h"


namespace {

class microkit_state : public driver_device
{
public:
	microkit_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_rs232(*this, "rs232")
	{ }

	void microkit(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(reset_button);
	DECLARE_INPUT_CHANGED_MEMBER(runp_button);
	DECLARE_INPUT_CHANGED_MEMBER(runu_button);

private:
	int clear_r();
	void ram_w(offs_t offset, uint8_t data);
	uint8_t ram_r(offs_t offset);

	void microkit_io(address_map &map) ATTR_COLD;
	void microkit_mem(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	std::unique_ptr<uint8_t[]> m_ram;
	uint8_t m_resetcnt = 0U;
	bool m_a15 = 1;
	required_device<cosmac_device> m_maincpu;
	required_memory_region m_rom;
	required_device<rs232_port_device> m_rs232;
};

void microkit_state::microkit_mem(address_map &map)
{
	map(0x0000, 0x01ff).rw(FUNC(microkit_state::ram_r),FUNC(microkit_state::ram_w));
	map(0x0200, 0x0fff).ram();
	map(0x8000, 0x81ff).rom().region("maincpu", 0);  // CDP1832.U2
	map(0x8c00, 0x8c1f).ram();   // CDP1824.U3 "Register Storage"
}

void microkit_state::microkit_io(address_map &map)
{
	map(0x05, 0x05).r("u4", FUNC(cdp1852_device::read)).w("u4", FUNC(cdp1852_device::write));   // user output port
	map(0x06, 0x06).r("u5", FUNC(cdp1852_device::read)).w("u5", FUNC(cdp1852_device::write));   // user input port
	map(0x07, 0x07).nopw(); // writes lots of zeros here
}

static INPUT_PORTS_START( microkit )
	PORT_START("RESET")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("RESET") PORT_CODE(KEYCODE_F3) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(microkit_state::reset_button), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("RUN P") PORT_CODE(KEYCODE_F2) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(microkit_state::runp_button), 0)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("RUN U") PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(microkit_state::runu_button), 0)
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(microkit_state::reset_button)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
	m_resetcnt = 0;
}

INPUT_CHANGED_MEMBER(microkit_state::runp_button)
{
	m_a15 = 0;
	m_resetcnt = 0;
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}

INPUT_CHANGED_MEMBER(microkit_state::runu_button)
{
	m_a15 = 1;
	m_resetcnt = 0;
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}

int microkit_state::clear_r()
{
	if (m_resetcnt < 0x20)
		m_resetcnt++;

	if (m_resetcnt < 0x10)
		return 0;
	else
	if (m_resetcnt == 0x1c)
		m_a15 = 0;

	return 1;
}

uint8_t microkit_state::ram_r(offs_t offset)
{
	if (m_a15)
		return m_rom->base()[offset];
	else
		return m_ram[offset];
}

void microkit_state::ram_w(offs_t offset, uint8_t data)
{
	m_ram[offset] = data;
}

void microkit_state::machine_reset()
{
	m_resetcnt = 0;
	m_maincpu->reset();
}

void microkit_state::machine_start()
{
	// Register save state
	save_item(NAME(m_resetcnt));
	save_item(NAME(m_a15));
	m_ram = make_unique_clear<uint8_t[]>(0x200);
	save_pointer(NAME(m_ram), 0x200);
}

static DEVICE_INPUT_DEFAULTS_START( serial_keyb )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_300 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_300 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_MARK )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END


void microkit_state::microkit(machine_config &config)
{
	// basic machine hardware
	CDP1802(config, m_maincpu, 2_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &microkit_state::microkit_mem);
	m_maincpu->set_addrmap(AS_IO, &microkit_state::microkit_io);
	m_maincpu->wait_cb().set_constant(1);
	m_maincpu->clear_cb().set(FUNC(microkit_state::clear_r));
	m_maincpu->q_cb().set(m_rs232, FUNC(rs232_port_device::write_txd)).invert();

	/* video hardware */
	RS232_PORT(config, m_rs232, default_rs232_devices, "terminal");
	m_rs232->rxd_handler().set_inputline(m_maincpu, COSMAC_INPUT_LINE_EF4);
	m_rs232->set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(serial_keyb));

	cdp1852_device &u4(CDP1852(config, "u4"));
	u4.mode_cb().set_constant(1); // output

	cdp1852_device &u5(CDP1852(config, "u5"));
	u5.mode_cb().set_constant(0); // input
}

ROM_START( microkit )
	ROM_REGION( 0x200, "maincpu", 0 )
	ROM_LOAD( "ut4.u2", 0x0000, 0x0200, CRC(ba642f8e) SHA1(8975a4c6a0bb699b53c753946aac54860054246a) )

	// These are bad, with heaps of bugs
	//ROM_REGION( 0x200, "maincpu", ROMREGION_INVERT )
	//ROM_LOAD( "3.2b", 0x000, 0x100, CRC(6799357e) SHA1(c46e3322b8b1b6534a7da04806be29fa265951b7) )
	//ROM_LOAD( "4.2a", 0x100, 0x100, CRC(27267bad) SHA1(838df9be2dc175584a1a6ee1770039118e49482e) )

ROM_END

} // anonymous namespace


COMP( 1975, microkit, 0, 0, microkit, microkit, microkit_state, empty_init, "RCA", "COSMAC Microkit", MACHINE_SUPPORTS_SAVE )



microkorg.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Korg microKORG compact synthesizer/vocoder.

    The MS2000 Analog Modeling Synthesizer runs on similar hardware.

****************************************************************************/

#include "emu.h"
#include "cpu/h8/h8s2329.h"
#include "machine/intelfsh.h"


namespace {

class microkorg_state : public driver_device
{
public:
	microkorg_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void microkorg(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<h8s2320_device> m_maincpu;
};


void microkorg_state::mem_map(address_map &map)
{
	map(0x000000, 0x0fffff).rw("flash", FUNC(intelfsh16_device::read), FUNC(intelfsh16_device::write));
	map(0x400000, 0x47ffff).ram();
}


static INPUT_PORTS_START(microkorg)
INPUT_PORTS_END

void microkorg_state::microkorg(machine_config &config)
{
	H8S2320(config, m_maincpu, 10_MHz_XTAL); // HD6412320VF25 (or HD6412324SVF25)
	m_maincpu->set_addrmap(AS_PROGRAM, &microkorg_state::mem_map);
	// TODO: serial channel 0 is SPI link to DSP; serial channel 1 is MIDI

	FUJITSU_29LV800B(config, "flash"); // MBM29LV800BA-90PFTN

	//DSP56362(config, "dsp", 12.288_MHz_XTAL / 4); // DSPB56362PV100

	//AK4522(config, "codec", 12.288_MHz_XTAL); // AK4522VF
}

ROM_START(microkorg)
	ROM_REGION16_LE(0x100000, "flash", 0)
	ROM_LOAD("korg_microkorg_v1.03_29lv800b.ic20", 0x000000, 0x100000, CRC(607ada7e) SHA1(4a6e2f4068cac7493484af2a8c1d1db7d8bd7a17))
ROM_END

} // anonymous namespace


SYST(2002, microkorg, 0, 0, microkorg, microkorg, microkorg_state, empty_init, "Korg", "microKORG Synthesizer/Vocoder", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



micromon.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/*

Micromon 7141 ECG unit for hospitals.

No manuals or schematic found.

From the photos, we can see a CDP1802ACE CPU, 2.4576 crystal,
a GM76C28A-10 2Kx8 static RAM (like 6116), 2 ROMS and a bunch of small chips.
There's also 4 dipswitches in a single package. 1=on,2=off,3=off,4=on.
The unit has an audible alarm, presumably a speaker, but it doesn't appear in any photo.

The front panel has a CRT display (looks like cyan on black), some buttons with unknown
symbols, a 5-position rotary switch, a plug for the ECG device, a socket called "Sync"
and some LEDs.

Date of manufacture unknown, however the chips have date codes of 1994 and 1995.

*****************************************************************************************/

#include "emu.h"
#include "cpu/cosmac/cosmac.h"


namespace {

class micromon_state : public driver_device
{
public:
	micromon_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void micromon(machine_config &config);

private:
	int clear_r();

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	virtual void machine_reset() override ATTR_COLD;
	uint8_t m_resetcnt = 0U;
	required_device<cosmac_device> m_maincpu;
};

void micromon_state::mem_map(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x2000, 0x3fff).ram();
}

void micromon_state::io_map(address_map &map)
{
}

static INPUT_PORTS_START( micromon )
INPUT_PORTS_END

int micromon_state::clear_r()
{
	if (m_resetcnt < 0x10)
		m_maincpu->set_state_int(cosmac_device::COSMAC_R0, 0x0000);
	if (m_resetcnt < 0x20)
		m_resetcnt++;
	// set reset pin to normal
	return 1;
}

void micromon_state::machine_reset()
{
	m_resetcnt = 0;
}


void micromon_state::micromon(machine_config &config)
{
	// basic machine hardware
	CDP1802(config, m_maincpu, 2457600);
	m_maincpu->set_addrmap(AS_PROGRAM, &micromon_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &micromon_state::io_map);
	m_maincpu->wait_cb().set_constant(1);
	m_maincpu->clear_cb().set(FUNC(micromon_state::clear_r));

	// video hardware

	// sound
}

ROM_START( micromon7141 )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "721421_rev4.0_25_7_95.ic2",  0x0000, 0x1000, CRC(b2a26439) SHA1(66a65d19b3cff185e82b10fc7ecb965c51751b7c) )
	ROM_LOAD( "702423_rev4.0_25_7_95.ic41", 0x1000, 0x1000, CRC(5efe6b4b) SHA1(b3670c53e2527e824cc22e4a54db9abf5a07239f) )
ROM_END

} // anonymous namespace


SYST( 1995?, micromon7141, 0, 0, micromon, micromon, micromon_state, empty_init, "Kontron Instruments",  "Micromon 7141 ECG unit",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



micronic.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

    Micronic 1000

    This is a small handheld CP/M computer with a 4x8 keypad and a 128x64
    LCD display. It was produced by Victor Micronic Ltd, a UK subsidiary
    of Datronic AB.

    06/2010 (Sandro Ronco)
    - ROM/RAM banking
    - keypad input
    - Periodic IRQ (RTC-146818)
    - NVRAM

    TODO:
    - IR I/O port
    - LCD contrast and backlight

    NOTE:
    The display shows "TESTING..." for about 2 min before showing the information screen

    More info:
    http://web.archive.org/web/20180104024745/http://www.philpem.me.uk/elec/micronic/
    http://web.archive.org/web/20080411235036/http://members.lycos.co.uk:80/leeedavison/z80/micronic/index.html
    https://geocities.restorativland.org/SiliconValley/Port/8052/
    https://www.cbronline.com/news/victor_micronic_has_the_m1000_handheld_computer_terminal/

****************************************************************************/

/*

    KBD_R:  EQU 00h     ; key matrix read port
    KBD_W:  EQU 02h     ; key matrix write port
    LCD_D:  EQU 03h     ; LCD data port
    Port_04:    EQU 04h     ; IRQ hardware mask
                        ; .... ...0 = keyboard interrupt enable
                        ; .... ..0. = RTC interrupt enable
                        ; .... .0.. = IR port interrupt enable
                        ; .... 0... = main battery interrupt enable
                        ; ...0 .... = backup battery interrupt enable
    Port_05:    EQU 05h     ; interrupt flag byte
                        ; .... ...1 = keyboard interrupt
                        ; .... ..1. = RTC interrupt
                        ; .... .1.. = IR port interrupt ??
                        ; .... 1... = main battery interrupt
                        ; ...1 .... = backup battery interrupt
    Port_07:    EQU 07h     ;
                        ; .... ...x
                        ; .... ..x.
    RTC_A:  EQU 08h     ; RTC address port
    LCD_C:  EQU 23h     ; LCD command port
    RTC_D:  EQU 28h     ; RTC data port
    Port_2A:    EQU 2Ah     ;
                        ; .... ...x
                        ; .... ..x.
                        ; ...x ....
                        ; ..x. ....
    Port_2B:    EQU 2Bh     ; .... xxxx = beep tone
                        ; .... 0000 = off
                        ; .... 0001 = 0.25mS = 4.000kHz
                        ; .... 0010 = 0.50mS = 2.000kHz
                        ; .... 0011 = 0.75mS = 1.333kHz
                        ; .... 0100 = 1.00mS = 1.000kHz
                        ; .... 0101 = 1.25mS = 0.800kHz
                        ; .... 0110 = 1.50mS = 0.667kHz
                        ; .... 0111 = 1.75mS = 0.571kHz
                        ; .... 1000 = 2.00mS = 0.500kHz
                        ; .... 1001 = 2.25mS = 0.444kHz
                        ; .... 1010 = 2.50mS = 0.400kHz
                        ; .... 1011 = 2.75mS = 0.364kHz
                        ; .... 1100 = 3.00mS = 0.333kHz
                        ; .... 1101 = 3.25mS = 0.308kHz
                        ; .... 1110 = 3.50mS = 0.286kHz
                        ; .... 1111 = 3.75mS = 0.267kHz
    Port_2C:    EQU 2Ch     ;
                        ; .... ...x V24_ADAPTER IR port clock
                        ; .... ..x. V24_ADAPTER IR port data
                        ; ...1 .... = backlight on
                        ; ..xx ..xx
    Port_2D:    EQU 2Dh     ;
                        ; .... ...x
                        ; .... ..x.
    Port_33:    EQU 33h     ;
    Port_46:    EQU 46h     ; LCD contrast port
    MEM_P:  EQU 47h     ; memory page
    Port_48:    EQU 48h     ;
                        ; .... ...x
                        ; .... ..x.
    Port_49:    EQU 49h     ;
                        ; .... ...x
                        ; .... ..x.
    Port_4A:    EQU 4Ah     ; end IR port output
                        ; .... ...x
                        ; .... ..x.
                        ; ...x ....
                        ; ..x. ....
                        ; .x.. ....
                        ; x... ....
    Port_4B:    EQU 4Bh     ; IR port status byte
                        ; .... ...1 RX buffer full
                        ; ...x ....
                        ; .x.. ....
                        ; 1... .... TX buffer empty
    Port_4C:    EQU 4Ch     ;
                        ; .... ...x
                        ; x... ....
    Port_4D:    EQU 4Dh     ; IR transmit byte
    Port_4E:    EQU 4Eh     ; IR receive byte
    Port_4F:    EQU 4Fh     ;
                        ; .... ...x
                        ; .... ..x.
                        ; .... .x..
                        ; .... x...
                        ; ...x ....

*/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "video/hd61830.h"
#include "machine/mc146818.h"
#include "machine/ram.h"
#include "machine/nvram.h"
#include "sound/beep.h"
#include "imagedev/cassette.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

namespace {

#define SCREEN_TAG      "screen"
#define Z80_TAG         "z80"
#define MC146818_TAG    "mc146818"
#define HD61830_TAG     "hd61830"

class micronic_state : public driver_device
{
public:
	micronic_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, Z80_TAG),
		m_lcdc(*this, HD61830_TAG),
		m_beep(*this, "beeper"),
		m_rtc(*this, MC146818_TAG),
		m_nvram1(*this, "nvram1"),
		m_nvram2(*this, "nvram2"),
		m_ram(*this, RAM_TAG),
		m_ram_base(*this, "ram_base"),
		m_status_flag(1),
		m_bank1(*this, "bank1"),
		m_bit0(*this, "BIT0"),
		m_bit1(*this, "BIT1"),
		m_bit2(*this, "BIT2"),
		m_bit3(*this, "BIT3"),
		m_bit4(*this, "BIT4"),
		m_bit5(*this, "BIT5"),
		m_backbattery(*this, "BACKBATTERY"),
		m_mainbattery(*this, "MAINBATTERY"),
		m_cassette(*this, "cassette")
	{ }

	void micronic(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void nvram_init(nvram_device &nvram, void *data, size_t size);

	uint8_t keypad_r();
	uint8_t status_flag_r();
	void status_flag_w(uint8_t data);
	void kp_matrix_w(uint8_t data);
	void beep_w(uint8_t data);
	uint8_t irq_flag_r();
	void port_2c_w(uint8_t data);
	void bank_select_w(uint8_t data);
	void lcd_contrast_w(uint8_t data);
	void mc146818_irq(int state);

	void micronic_palette(palette_device &palette) const;

	void micronic_io(address_map &map) ATTR_COLD;
	void micronic_mem(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<hd61830_device> m_lcdc;
	required_device<beep_device> m_beep;
	required_device<mc146818_device> m_rtc;
	required_device<nvram_device> m_nvram1;
	required_device<nvram_device> m_nvram2;
	required_device<ram_device> m_ram;

	required_shared_ptr<uint8_t> m_ram_base;
	uint8_t m_banks_num = 0;
	uint8_t m_kp_matrix = 0;
	uint8_t m_lcd_contrast = 0;
	int m_lcd_backlight = 0;
	uint8_t m_status_flag = 0;

	required_memory_bank m_bank1;
	required_ioport m_bit0;
	required_ioport m_bit1;
	required_ioport m_bit2;
	required_ioport m_bit3;
	required_ioport m_bit4;
	required_ioport m_bit5;
	required_ioport m_backbattery;
	required_ioport m_mainbattery;
	optional_device<cassette_image_device> m_cassette;
};

uint8_t micronic_state::keypad_r()
{
	uint8_t data = 0;

	for (uint8_t bit = 0; bit < 8; bit++)
	{
		if (m_kp_matrix & (1 << bit))
		{
			data |= m_bit0->read() & (0x01 << bit) ? 0x01 : 0x00;
			data |= m_bit1->read() & (0x01 << bit) ? 0x02 : 0x00;
			data |= m_bit2->read() & (0x01 << bit) ? 0x04 : 0x00;
			data |= m_bit3->read() & (0x01 << bit) ? 0x08 : 0x00;
			data |= m_bit4->read() & (0x01 << bit) ? 0x10 : 0x00;
			data |= m_bit5->read() & (0x01 << bit) ? 0x20 : 0x00;
		}
	}
	return data;
}

uint8_t micronic_state::status_flag_r()
{
	return m_status_flag;
}

void micronic_state::status_flag_w(uint8_t data)
{
	m_status_flag = data;
}

void micronic_state::kp_matrix_w(uint8_t data)
{
	m_kp_matrix = data;
}

void micronic_state::beep_w(uint8_t data)
{
	uint16_t frequency[16] =
	{
			0, 4000, 2000, 1333, 1000, 800, 667, 571,
		500,  444,  400,  364,  333, 308, 286, 267
	};

	m_beep->set_clock(frequency[data & 0x0f]);
	m_beep->set_state((data & 0x0f) ? 1 : 0);
}

uint8_t micronic_state::irq_flag_r()
{
	return (m_backbattery->read()<<4) | (m_mainbattery->read()<<3) | (keypad_r() ? 0 : 1);
}

void micronic_state::bank_select_w(uint8_t data)
{
	if (data < 2)
	{
		m_bank1->set_entry(data);
		m_maincpu->space(AS_PROGRAM).unmap_write(0x0000, 0x7fff);
	}
	else
	{
		m_bank1->set_entry((data <= m_banks_num) ? data : m_banks_num);
		m_maincpu->space(AS_PROGRAM).install_write_bank(0x0000, 0x7fff, membank("bank1"));
	}
}

void micronic_state::lcd_contrast_w(uint8_t data)
{
	m_lcd_contrast = data;
}

void micronic_state::port_2c_w(uint8_t data)
{
	m_lcd_backlight = BIT(data, 4);
}


/***************************************************************************
    Machine
***************************************************************************/

void micronic_state::micronic_mem(address_map &map)
{
	map(0x0000, 0x7fff).bankrw("bank1");
	map(0x8000, 0xffff).ram().share("ram_base");
}

void micronic_state::micronic_io(address_map &map)
{
	map.global_mask(0xff);

	/* keypad */
	map(0x00, 0x00).r(FUNC(micronic_state::keypad_r));
	map(0x02, 0x02).w(FUNC(micronic_state::kp_matrix_w));

	/* hd61830 */
	map(0x03, 0x03).rw(m_lcdc, FUNC(hd61830_device::data_r), FUNC(hd61830_device::data_w));
	map(0x23, 0x23).rw(m_lcdc, FUNC(hd61830_device::status_r), FUNC(hd61830_device::control_w));

	/* rtc-146818 */
	map(0x08, 0x08).w(m_rtc, FUNC(mc146818_device::address_w));
	map(0x28, 0x28).rw(m_rtc, FUNC(mc146818_device::data_r), FUNC(mc146818_device::data_w));

	/* sound */
	map(0x2b, 0x2b).w(FUNC(micronic_state::beep_w));

	/* basic machine */
	map(0x05, 0x05).r(FUNC(micronic_state::irq_flag_r));
	map(0x2c, 0x2c).w(FUNC(micronic_state::port_2c_w));
	map(0x47, 0x47).w(FUNC(micronic_state::bank_select_w));
	map(0x46, 0x46).w(FUNC(micronic_state::lcd_contrast_w));
	map(0x48, 0x48).w(FUNC(micronic_state::status_flag_w));
	map(0x49, 0x49).r(FUNC(micronic_state::status_flag_r));
}

/* Input ports */
static INPUT_PORTS_START( micronic )
	PORT_START("MAINBATTERY")
		PORT_CONFNAME( 0x01, 0x01, "Main Battery Status" )
		PORT_CONFSETTING( 0x01, DEF_STR( Normal ) )
		PORT_CONFSETTING( 0x00, "Low Battery" )
	PORT_START("BACKBATTERY")
		PORT_CONFNAME( 0x01, 0x01, "Backup Battery Status" )
		PORT_CONFSETTING( 0x01, DEF_STR( Normal ) )
		PORT_CONFSETTING( 0x00, "Low Battery" )

	PORT_START("BIT0")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("MODE") PORT_CODE(KEYCODE_EQUALS)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("A (") PORT_CODE(KEYCODE_A)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("B )") PORT_CODE(KEYCODE_B)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("2nd") PORT_CODE(KEYCODE_LSHIFT)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("U 1") PORT_CODE(KEYCODE_1)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("BACKSPACE") PORT_CODE(KEYCODE_BACKSPACE)
	PORT_START("BIT1")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("D DEL") PORT_CODE(KEYCODE_D)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("E #") PORT_CODE(KEYCODE_E)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("F &") PORT_CODE(KEYCODE_F)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("V 2") PORT_CODE(KEYCODE_2)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("SPACE 0") PORT_CODE(KEYCODE_0)
	PORT_START("BIT2")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("G +") PORT_CODE(KEYCODE_G)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("H /") PORT_CODE(KEYCODE_H)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("I ,") PORT_CODE(KEYCODE_I)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("J ?") PORT_CODE(KEYCODE_J)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("W 3") PORT_CODE(KEYCODE_3)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("NO") PORT_CODE(KEYCODE_PGUP)
	PORT_START("BIT3")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("K -") PORT_CODE(KEYCODE_K)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("L *") PORT_CODE(KEYCODE_L)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("M .") PORT_CODE(KEYCODE_M)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("N Z") PORT_CODE(KEYCODE_Z)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("YES") PORT_CODE(KEYCODE_PGDN)
	PORT_START("BIT4")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("O 7") PORT_CODE(KEYCODE_7)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("P 8") PORT_CODE(KEYCODE_8)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Q 9") PORT_CODE(KEYCODE_9)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("DEPT") PORT_CODE(KEYCODE_R)
	PORT_START("BIT5")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("R 4") PORT_CODE(KEYCODE_4)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("S 5") PORT_CODE(KEYCODE_5)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("T 6") PORT_CODE(KEYCODE_6)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("END") PORT_CODE(KEYCODE_END)
INPUT_PORTS_END


void micronic_state::nvram_init(nvram_device &nvram, void *data, size_t size)
{
	m_status_flag = 0;
}


void micronic_state::micronic_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t( 92,  83,  88));
}

void micronic_state::machine_start()
{
	/* ROM banks */
	m_bank1->configure_entries(0x00, 0x02, memregion(Z80_TAG)->base(), 0x10000);

	/* RAM banks */
	m_banks_num = (m_ram->size() >> 15) + 1;
	m_bank1->configure_entries(0x02, m_banks_num - 1, m_ram->pointer(), 0x8000);

	m_nvram1->set_base(m_ram_base, 0x8000);
	m_nvram2->set_base(m_ram->pointer(), m_ram->size());

	/* register for state saving */
	save_item(NAME(m_banks_num));
	save_item(NAME(m_kp_matrix));
	save_item(NAME(m_lcd_contrast));
	save_item(NAME(m_lcd_backlight));
	save_item(NAME(m_status_flag));
	// TODO: restore RAM bank at state load...
}

void micronic_state::machine_reset()
{
	m_bank1->set_entry(0);
	m_maincpu->space(AS_PROGRAM).unmap_write(0x0000, 0x7fff);
}


void micronic_state::mc146818_irq(int state)
{
	m_maincpu->set_input_line(0, state ? HOLD_LINE : CLEAR_LINE);
}


void micronic_state::micronic(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 3.579545_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &micronic_state::micronic_mem);
	m_maincpu->set_addrmap(AS_IO, &micronic_state::micronic_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_LCD));
	screen.set_refresh_hz(80);
	screen.set_screen_update(HD61830_TAG, FUNC(hd61830_device::screen_update));
	screen.set_size(120, 64);   //6x20, 8x8
	screen.set_visarea(0, 120-1, 0, 64-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(micronic_state::micronic_palette), 2);

	HD61830(config, m_lcdc, 4.9152_MHz_XTAL / 2 / 2);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 0).add_route(ALL_OUTPUTS, "mono", 1.00);

	/* ram banks */
	RAM(config, RAM_TAG).set_default_size("224K");

	NVRAM(config, "nvram1").set_custom_handler(FUNC(micronic_state::nvram_init));  // base RAM
	NVRAM(config, "nvram2").set_custom_handler(FUNC(micronic_state::nvram_init));  // additional RAM banks

	MC146818(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->irq().set(FUNC(micronic_state::mc146818_irq));
}

/* ROM definition */
ROM_START( micronic )
	ROM_REGION( 0x18000, Z80_TAG, 0 )
	ROM_SYSTEM_BIOS(0, "v228", "Micronic 1000")
	ROMX_LOAD("micron1.bin", 0x0000, 0x8000, CRC(5632c8b7) SHA1(d1c9cf691848e9125f9ea352e4ffa41c288f3e29), ROM_BIOS(0))
	ROMX_LOAD("micron2.bin", 0x10000, 0x8000, CRC(dc8e7341) SHA1(927dddb3914a50bb051256d126a047a29eff7c65), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "test", "Micronic 1000 LCD monitor")
	ROMX_LOAD("monitor2.bin", 0x0000, 0x8000, CRC(c6ae2bbf) SHA1(1f2e3a3d4720a8e1bb38b37f4ab9e0e32676d030), ROM_BIOS(1))
ROM_END

} // anonymous namespace

/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY            FULLNAME         FLAGS
COMP( 1987, micronic, 0,      0,      micronic, micronic, micronic_state, empty_init, "Victor Micronic", "Micronic 1000", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



microtan.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Juergen Buchmueller,Nigel Barnes
/******************************************************************************
 *  Microtan 65
 *
 *  Thanks go to Geoff Macdonald <mail@geoff.org.uk>
 *  for his site http://www.geoff.org.uk/microtan/index.htm
 *  and to Fabrice Frances <frances@ensica.fr>
 *  for his site http://oric.free.fr/microtan.html
 *
 *  Microtan65 memory map
 *
 *  range     short     description
 *  0000-01ff SYSRAM    system ram
 *                      0000-003f variables
 *                      0040-00ff basic
 *                      0100-01ff stack
 *  0200-03ff VIDEORAM  display
 *  0400-afff RAM       main memory
 *  bc00-bc01 AY8912-0  sound chip #0
 *  bc02-bc03 AY8912-1  sound chip #1
 *  bc04      SPACEINV  space invasion sound (?)
 *  bfc0-bfcf VIA6522-0 VIA 6522 #0
 *  bfd0-bfd3 SIO       serial i/o
 *  bfe0-bfef VIA6522-1 VIA 6522 #1
 *  bff0      GFX_KBD   R: chunky graphics on W: reset KBD interrupt
 *  bff1      NMI       W: start delayed NMI
 *  bff2      HEX       W: hex. keypad column
 *  bff3      KBD_GFX   R: ASCII KBD / hex. keypad row W: chunky graphics off
 *  c000-e7ff BASIC     BASIC Interpreter ROM
 *  f000-f7ff XBUG      XBUG ROM
 *  f800-ffff TANBUG    TANBUG ROM
 *
 *  Tanbug commands:
 *  B         Set breakpoint
 *  C         copy (move) memory block
 *  G         Go
 *  L         Hex dump
 *  M         Modify memory
 *  N         Exit single-step mode
 *  O         Hex calculator
 *  P         Step once
 *  R         Register examine/modify
 *  S         Enter single-step mode
 *  BAS       Start BASIC
 *  WAR       Re-enter BASIC (warm start)
 *
 *  Ralbug commnds:
 *  COP       Copy
 *  MEM       Modify memory
 *  SYW       Warm start to DOS system
 *  SYC       Cold start to DOS system
 *  SYB       Boot DOS system
 *  LIS       List a line of memory
 *  JMP       Go to address that follows
 *  JSR       JSR to following address
 *  JMI       Jump indirect
 *  JSI       JSR indirect
 *  TES       Memory test
 *  CLR       Clear screen
 *  COL       Colour change
 *  HEL       Help display menu
 *  VEC       List vector jump table
 *  FIL       Fill Memory
 *  CUR       Cursor move
 *  ASC       ASCII table display
 *  SYN       Command syntax
 *  HOM       Home cursor
 *  WAR       Warm start $0003
 *  STA       Start $0000
 *  EXP       Expand monitor
 *  BLE       Keyboard bleep
 *  KIL       Kill off the cache
 *
 *****************************************************************************/

#include "emu.h"
#include "microtan.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"

#include "utf8.h"


void microtan_state::mt65_map(address_map &map)
{
	map(0x0000, 0xffff).rw(m_tanbus, FUNC(tanbus_device::read), FUNC(tanbus_device::write));
	map(0x0000, 0x01ff).ram();
	map(0x0200, 0x03ff).ram().w(FUNC(microtan_state::videoram_w)).share(m_videoram);
	map(0xbff0, 0xbfff).rw(FUNC(microtan_state::bffx_r), FUNC(microtan_state::bffx_w));
	map(0xf800, 0xffff).rom().region("maincpu", 0);
}

void microtan_state::spinv_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x01ff).ram();
	map(0x0200, 0x03ff).ram().w(FUNC(microtan_state::videoram_w)).share(m_videoram);
	map(0xbff0, 0xbfff).rw(FUNC(microtan_state::bffx_r), FUNC(microtan_state::bffx_w));
	map(0xf800, 0xffff).rom().region("maincpu", 0);
}

void mt6809_state::mt6809_map(address_map &map)
{
	map(0x0000, 0xffff).rw(m_tanbus, FUNC(tanbus_device::read), FUNC(tanbus_device::write));
	map(0x0000, 0x03ff).ram();
	map(0xbff0, 0xbfff).rw(FUNC(mt6809_state::bffx_r), FUNC(mt6809_state::bffx_w));
	map(0xf800, 0xffff).rom().region("maincpu", 0);
}


void microtan_state::trigger_reset(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? ASSERT_LINE : CLEAR_LINE);

	if (state)
	{
		machine().schedule_soft_reset();
	}
}

void mt6809_state::trigger_reset(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? ASSERT_LINE : CLEAR_LINE);

	if (state)
	{
		machine().schedule_soft_reset();
	}
}


static const gfx_layout char_layout =
{
	8, 16,      /* 8 x 16 graphics */
	256,        /* 256 codes */
	1,          /* 1 bit per pixel */
	{ 0 },      /* no bitplanes */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8 * 16      /* code takes 8 times 16 bits */
};

static GFXDECODE_START( gfx_microtan )
	GFXDECODE_ENTRY( "gfx1", 0, char_layout, 0, 1 )
	GFXDECODE_ENTRY( "gfx2", 0, char_layout, 0, 1 )
GFXDECODE_END


void microtan_state::mt65(machine_config &config)
{
	/* Microtan 65 CPU board */
	M6502(config, m_maincpu, 6_MHz_XTAL / 8);
	m_maincpu->set_addrmap(AS_PROGRAM, &microtan_state::mt65_map);

	INPUT_MERGER_ANY_HIGH(config, m_irq_line).output_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	MICROTAN_KBD_SLOT(config, m_keyboard, microtan_kbd_devices, "mt009");
	m_keyboard->strobe_handler().set(FUNC(microtan_state::kbd_int));
	m_keyboard->reset_handler().set(FUNC(microtan_state::trigger_reset));

	/* video hardware - include overscan */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(6_MHz_XTAL, 384, 0, 32*8, 312, 0, 16*16);
	screen.set_screen_update(FUNC(microtan_state::screen_update));
	screen.set_palette("palette");

	GFXDECODE(config, m_gfxdecode, "palette", gfx_microtan);

	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* Microtan Motherboard */
	TANBUS(config, m_tanbus, 6_MHz_XTAL);
	m_tanbus->out_irq_callback().set(m_irq_line, FUNC(input_merger_device::in_w<IRQ_TANBUS>));
	m_tanbus->out_so_callback().set_inputline(m_maincpu, M6502_SET_OVERFLOW);
	m_tanbus->out_nmi_callback().set_inputline(m_maincpu, M6502_NMI_LINE);
	m_tanbus->out_pgm_callback().set(FUNC(microtan_state::pgm_chargen_w));

	TANBUS_SLOT(config, "tanbus:tanex", -1, tanex_devices, "tanex");
	TANBUS_SLOT(config, "tanbus:0", 0, tanbus_devices, "tanram");
	TANBUS_SLOT(config, "tanbus:1", 1, tanbus_devices, nullptr);
	TANBUS_SLOT(config, "tanbus:2", 2, tanbus_devices, "bullsnd");
	TANBUS_SLOT(config, "tanbus:3", 3, tanbus_devices, nullptr);
	TANBUS_SLOT(config, "tanbus:4", 4, tanbus_devices, nullptr);
	TANBUS_SLOT(config, "tanbus:5", 5, tanbus_devices, nullptr);
	TANBUS_SLOT(config, "tanbus:6", 6, tanbus_devices, nullptr);
	TANBUS_SLOT(config, "tanbus:7", 7, tanbus_devices, nullptr);
	TANBUS_SLOT(config, "tanbus:exp", 8, tanbus_devices, nullptr);

	/* snapshot/quickload */
	snapshot_image_device &snapshot(SNAPSHOT(config, "snapshot", "dmp,m65"));
	snapshot.set_load_callback(FUNC(microtan_state::snapshot_cb));
	snapshot.set_interface("mt65_snap");
	QUICKLOAD(config, "quickload", "hex").set_load_callback(FUNC(microtan_state::quickload_cb));

	/* software lists */
	SOFTWARE_LIST(config, "rom_list").set_original("mt65_rom");
	SOFTWARE_LIST(config, "cass_list").set_original("mt65_cass");
	//SOFTWARE_LIST(config, "flop_list").set_original("mt65_flop").set_filter("6502");
	SOFTWARE_LIST(config, "snap_list").set_original("mt65_snap");
}

void microtan_state::micron(machine_config &config)
{
	mt65(config);

	/* Microtan Mini-Motherboard connects MT65 CPU card and TANEX, no extra slots */
	config.device_remove("tanbus:0");
	config.device_remove("tanbus:1");
	config.device_remove("tanbus:2");
	config.device_remove("tanbus:3");
	config.device_remove("tanbus:4");
	config.device_remove("tanbus:5");
	config.device_remove("tanbus:6");
	config.device_remove("tanbus:7");
	config.device_remove("tanbus:exp");
}

void microtan_state::spinveti(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, 6_MHz_XTAL / 8);
	m_maincpu->set_addrmap(AS_PROGRAM, &microtan_state::spinv_map);

	INPUT_MERGER_ANY_HIGH(config, m_irq_line).output_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	MICROTAN_KBD_SLOT(config, m_keyboard, microtan_kbd_devices, "spinveti");
	m_keyboard->strobe_handler().set(FUNC(microtan_state::kbd_int));
	m_keyboard->reset_handler().set(FUNC(microtan_state::trigger_reset));

	/* video hardware - include overscan */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(6_MHz_XTAL, 384, 0, 32*8, 312, 0, 16*16);
	screen.set_screen_update(FUNC(microtan_state::screen_update));
	screen.set_palette("palette");

	GFXDECODE(config, m_gfxdecode, "palette", gfx_microtan);

	PALETTE(config, "palette", palette_device::MONOCHROME);
}


static DEVICE_INPUT_DEFAULTS_START(ra32k_def_ram0000)
	/* 32K RAM configured 0x0000-0x7fff */
	DEVICE_INPUT_DEFAULTS("DSW1", 0x01, 0x01) // all blocks enabled
	DEVICE_INPUT_DEFAULTS("DSW1", 0x02, 0x02)
	DEVICE_INPUT_DEFAULTS("DSW1", 0x04, 0x04)
	DEVICE_INPUT_DEFAULTS("DSW1", 0x08, 0x08)
	DEVICE_INPUT_DEFAULTS("DSW1", 0x10, 0x10)
	DEVICE_INPUT_DEFAULTS("DSW1", 0x20, 0x20)
	DEVICE_INPUT_DEFAULTS("DSW1", 0x40, 0x40)
	DEVICE_INPUT_DEFAULTS("DSW1", 0x80, 0x80)
	DEVICE_INPUT_DEFAULTS("DSW2", 0x01, 0x01) // all blocks enabled
	DEVICE_INPUT_DEFAULTS("DSW2", 0x02, 0x02)
	DEVICE_INPUT_DEFAULTS("DSW2", 0x04, 0x04)
	DEVICE_INPUT_DEFAULTS("DSW2", 0x08, 0x08)
	DEVICE_INPUT_DEFAULTS("DSW2", 0x10, 0x10)
	DEVICE_INPUT_DEFAULTS("DSW2", 0x20, 0x20)
	DEVICE_INPUT_DEFAULTS("DSW2", 0x40, 0x40)
	DEVICE_INPUT_DEFAULTS("DSW2", 0x80, 0x80)
	DEVICE_INPUT_DEFAULTS("DSW3", 0x0f, 0x0f) // start address $0000
	DEVICE_INPUT_DEFAULTS("LNK1", 0x01, 0x01) // paged
DEVICE_INPUT_DEFAULTS_END

static DEVICE_INPUT_DEFAULTS_START(ra32k_def_ram8000)
	/* 32K RAM configured 0x8000-0xffff */
	DEVICE_INPUT_DEFAULTS("DSW1", 0x01, 0x01)
	DEVICE_INPUT_DEFAULTS("DSW1", 0x02, 0x02)
	DEVICE_INPUT_DEFAULTS("DSW1", 0x04, 0x04)
	DEVICE_INPUT_DEFAULTS("DSW1", 0x08, 0x08)
	DEVICE_INPUT_DEFAULTS("DSW1", 0x10, 0x10)
	DEVICE_INPUT_DEFAULTS("DSW1", 0x20, 0x20)
	DEVICE_INPUT_DEFAULTS("DSW1", 0x40, 0x40)
	DEVICE_INPUT_DEFAULTS("DSW1", 0x80, 0x00) // disable block $F800
	DEVICE_INPUT_DEFAULTS("DSW2", 0x01, 0x01) // all blocks enabled
	DEVICE_INPUT_DEFAULTS("DSW2", 0x02, 0x02)
	DEVICE_INPUT_DEFAULTS("DSW2", 0x04, 0x04)
	DEVICE_INPUT_DEFAULTS("DSW2", 0x08, 0x08)
	DEVICE_INPUT_DEFAULTS("DSW2", 0x10, 0x10)
	DEVICE_INPUT_DEFAULTS("DSW2", 0x20, 0x20)
	DEVICE_INPUT_DEFAULTS("DSW2", 0x40, 0x40)
	DEVICE_INPUT_DEFAULTS("DSW2", 0x80, 0x80)
	DEVICE_INPUT_DEFAULTS("DSW3", 0x0f, 0x0d) // start address $8000
	DEVICE_INPUT_DEFAULTS("LNK1", 0x01, 0x00) // permanent
DEVICE_INPUT_DEFAULTS_END

static DEVICE_INPUT_DEFAULTS_START(ra32k_def_rom0000)
	/* 8K EPROM configured 0x2000-0x3fff */
	DEVICE_INPUT_DEFAULTS("DSW1", 0x01, 0x01) // all blocks enabled
	DEVICE_INPUT_DEFAULTS("DSW1", 0x02, 0x02)
	DEVICE_INPUT_DEFAULTS("DSW1", 0x04, 0x04)
	DEVICE_INPUT_DEFAULTS("DSW1", 0x08, 0x08)
	DEVICE_INPUT_DEFAULTS("DSW1", 0x10, 0x10)
	DEVICE_INPUT_DEFAULTS("DSW1", 0x20, 0x20)
	DEVICE_INPUT_DEFAULTS("DSW1", 0x40, 0x40)
	DEVICE_INPUT_DEFAULTS("DSW1", 0x80, 0x80)
	DEVICE_INPUT_DEFAULTS("DSW2", 0x01, 0x01) // all blocks enabled
	DEVICE_INPUT_DEFAULTS("DSW2", 0x02, 0x02)
	DEVICE_INPUT_DEFAULTS("DSW2", 0x04, 0x04)
	DEVICE_INPUT_DEFAULTS("DSW2", 0x08, 0x08)
	DEVICE_INPUT_DEFAULTS("DSW2", 0x10, 0x10)
	DEVICE_INPUT_DEFAULTS("DSW2", 0x20, 0x20)
	DEVICE_INPUT_DEFAULTS("DSW2", 0x40, 0x40)
	DEVICE_INPUT_DEFAULTS("DSW2", 0x80, 0x80)
	DEVICE_INPUT_DEFAULTS("DSW3", 0x0f, 0x0f) // start address $0000
	DEVICE_INPUT_DEFAULTS("LNK1", 0x01, 0x01) // paged
DEVICE_INPUT_DEFAULTS_END

void mt6809_state::mt6809(machine_config &config)
{
	/* Ralph Allen 6809 CPU Board */
	MC6809(config, m_maincpu, 6_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mt6809_state::mt6809_map);

	MICROTAN_KBD_SLOT(config, m_keyboard, microtan_kbd_devices, "mt009");
	m_keyboard->strobe_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_keyboard->reset_handler().set(FUNC(mt6809_state::trigger_reset));

	/* Microtan Motherboard */
	TANBUS(config, m_tanbus, 6_MHz_XTAL);
	m_tanbus->out_irq_callback().set_inputline(m_maincpu, M6809_IRQ_LINE);
	m_tanbus->out_nmi_callback().set_inputline(m_maincpu, M6809_FIRQ_LINE);

	TANBUS_SLOT(config, "tanbus:tanex", -1, tanex_devices, nullptr);
	TANBUS_SLOT(config, "tanbus:0", 0, tanbus6809_devices, "ra32kram").set_option_device_input_defaults("ra32kram", DEVICE_INPUT_DEFAULTS_NAME(ra32k_def_ram0000));
	TANBUS_SLOT(config, "tanbus:1", 1, tanbus6809_devices, "ra32kram").set_option_device_input_defaults("ra32kram", DEVICE_INPUT_DEFAULTS_NAME(ra32k_def_ram8000));
	TANBUS_SLOT(config, "tanbus:2", 2, tanbus6809_devices, "ravdu");
	TANBUS_SLOT(config, "tanbus:3", 3, tanbus6809_devices, nullptr);
	TANBUS_SLOT(config, "tanbus:4", 4, tanbus6809_devices, nullptr);
	TANBUS_SLOT(config, "tanbus:5", 5, tanbus6809_devices, "radisc");
	TANBUS_SLOT(config, "tanbus:6", 6, tanbus6809_devices, nullptr);
	TANBUS_SLOT(config, "tanbus:7", 7, tanbus6809_devices, "ra32krom").set_option_device_input_defaults("ra32krom", DEVICE_INPUT_DEFAULTS_NAME(ra32k_def_rom0000));
	TANBUS_SLOT(config, "tanbus:exp", 8, tanbus6809_devices, nullptr);

	/* software lists */
	//SOFTWARE_LIST(config, "flop_list").set_original("mt65_flop").set_filter("6809");
}


ROM_START( mt65 )
	ROM_REGION(0x0800, "maincpu", 0)
	ROM_DEFAULT_BIOS("tanbug23")
	/* Microtan 65 (MT0016 Iss 1) */
	ROM_SYSTEM_BIOS(0, "tanbug23", "TANBUG V2.3")
	ROMX_LOAD("tanbug23.k3", 0x0000, 0x0800, CRC(7f29845d) SHA1(de277e942eefa11a9a6defe3d7d03071d5100b68), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "tanbug23p", "TANBUG V2.3 (patched)")
	ROMX_LOAD("tanbug_2.rom", 0x0000, 0x0400, CRC(7e215313) SHA1(c8fb3d33ce2beaf624dc75ec57d34c216b086274), ROM_BIOS(1))
	ROMX_LOAD("tanbug.rom",   0x0400, 0x0400, CRC(c8221d9e) SHA1(c7fe4c174523aaaab30be7a8c9baf2bc08b33968), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "tanbug31", "TANBUG V3.1 (TANDOS)")
	ROMX_LOAD("tanbug31.k3", 0x0000, 0x0800, CRC(5943e427) SHA1(55fe645363cd5f2015a537be6f7a00de33d1bea5), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "tanbug3b", "TANBUG V.3B (MPVDU)")
	ROMX_LOAD("tanbug3b.k3", 0x0000, 0x0800, CRC(5b49f861) SHA1(b37cddf97b6324ab0647479b5b013a9b3d47ddda), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "tugbug11", "TUGBUG V1.1 (VID8082)")
	ROMX_LOAD("tugbug11.k3", 0x0000, 0x0800, CRC(23b02439) SHA1(9e56fbd6c07b1dc5c10e7b574a5ea26405781a3d), ROM_BIOS(4))

	/* DM8678BWF (upper case) and DM8678CAE (lower case) */
	ROM_REGION(0x1000, "gfx1", 0)
	ROM_LOAD("charset.rom", 0x0000, 0x0800, CRC(3b3c5360) SHA1(a3a2f74149107f8b8f35b15069c71f3aa843d12f))
	ROM_RELOAD(0x0800, 0x0800)

	ROM_REGION(0x1000, "gfx2", ROMREGION_ERASEFF)
ROM_END

ROM_START( micron )
	ROM_REGION(0x0800, "maincpu", 0)
	ROM_DEFAULT_BIOS("tanbug1")
	/* MT65 (Iss 0) */
	ROM_SYSTEM_BIOS(0, "tanbug1", "TANBUG V1")
	ROMX_LOAD("tanbug1.g2", 0x0000, 0x0400, CRC(6f45aaca) SHA1(c1a020d86830ee475ee75c847e98f7a02d467659), ROM_BIOS(0) | ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO)
	ROM_RELOAD(0x0400, 0x0400)
	ROMX_LOAD("tanbug1.h2", 0x0000, 0x0400, CRC(1ccd6b8f) SHA1(49784749599255458ba5513d8c2fc58c4df39515), ROM_BIOS(0) | ROM_NIBBLE | ROM_SHIFT_NIBBLE_HI)
	ROM_RELOAD(0x0400, 0x0400)

	/* DM8678BWF (upper case) and DM8678CAE (lower case) */
	ROM_REGION(0x1000, "gfx1", 0)
	ROM_LOAD("charset.rom", 0x0000, 0x0800, CRC(3b3c5360) SHA1(a3a2f74149107f8b8f35b15069c71f3aa843d12f))
	ROM_RELOAD(0x0800, 0x0800)

	ROM_REGION(0x1000, "gfx2", ROMREGION_ERASEFF)
ROM_END

ROM_START( spinveti )
	ROM_REGION(0x0800, "maincpu", 0)
	ROM_LOAD("spaceinv.ic20", 0x0000, 0x0800, CRC(c0b074e0) SHA1(464be4082bd60d825c0c1e7ae033d7ab095c8e70))

	/* DM8678BWF only */
	ROM_REGION(0x1000, "gfx1", 0)
	ROM_LOAD("charset.rom",  0x0000, 0x0800, CRC(3b3c5360) SHA1(a3a2f74149107f8b8f35b15069c71f3aa843d12f))
	ROM_RELOAD(0x0800, 0x0800)

	ROM_REGION(0x1000, "gfx2", ROMREGION_ERASEFF)
ROM_END

ROM_START( mt6809 )
	/* 6809 CPU Card (Ralph Allen Engineering) */
	ROM_REGION(0x0800, "maincpu", 0)
	ROM_SYSTEM_BIOS(0, "ralbug14", "RALBUG V1.4")
	ROMX_LOAD("ralbug14.rom", 0x0000, 0x0800, CRC(8bbc87d8) SHA1(3d53a6ffd4a8d7c8edf4f2e23946011ed29146ce), ROM_BIOS(0))
ROM_END


//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT           COMPANY          FULLNAME                  FLAGS
COMP( 1979, mt65,     0,      0,      mt65,     0,        microtan_state, empty_init,    "Tangerine",     "Microtan 65",            MACHINE_NO_SOUND_HW )
COMP( 1980, micron,   mt65,   0,      micron,   0,        microtan_state, empty_init,    "Tangerine",     "Micron",                 MACHINE_NO_SOUND_HW )
COMP( 1980, spinveti, 0,      0,      spinveti, 0,        microtan_state, empty_init,    "Tangerine/ETI", "Space Invasion (ETI)",   MACHINE_NO_SOUND )
COMP( 1984, mt6809,   mt65,   0,      mt6809,   0,        mt6809_state,   empty_init,    "Tangerine",     "Microtan 6809 System",   MACHINE_NO_SOUND_HW )



microterm_f8.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Driver for Micro-Term ACT-5A and other F8-based terminals.

*******************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/f8/f8.h"
#include "machine/ay31015.h"
#include "machine/f3853.h"
#include "machine/input_merger.h"
#include "machine/ripple_counter.h"
#include "sound/beep.h"
#include "screen.h"
#include "speaker.h"


namespace {

class microterm_f8_state : public driver_device
{
public:
	microterm_f8_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_uart(*this, "uart")
		, m_io(*this, "io")
		, m_screen(*this, "screen")
		, m_bell(*this, "bell")
		, m_blinkcount(*this, "blinkcount")
		, m_kbdecode(*this, "kbdecode")
		, m_chargen(*this, "chargen")
		, m_keys(*this, "KEY%u", 0U)
		, m_modifiers(*this, "MODIFIERS")
		, m_special(*this, "SPECIAL")
		, m_dsw(*this, "DSW%u", 1U)
		, m_jumpers(*this, "JUMPERS")
	{ }

	void act5a(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	TIMER_CALLBACK_MEMBER(baud_clock);

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void vblank_w(int state);

	u8 bell_r();
	void scroll_w(u8 data);
	u8 vram_r(offs_t offset);
	void vram_w(offs_t offset, u8 data);
	void uart_transmit_w(u8 data);
	bool poll_keyboard();
	u8 key_r();
	u8 port00_r();
	void port00_w(u8 data);
	u8 port01_r();

	void f8_mem(address_map &map) ATTR_COLD;
	void f8_io(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<ay51013_device> m_uart;
	required_device<rs232_port_device> m_io;
	//required_device<rs232_port_device> m_aux;
	required_device<screen_device> m_screen;
	required_device<beep_device> m_bell;
	required_device<ripple_counter_device> m_blinkcount;
	required_region_ptr<u8> m_kbdecode;
	required_region_ptr<u8> m_chargen;
	required_ioport_array<11> m_keys;
	required_ioport m_modifiers;
	required_ioport m_special;
	required_ioport_array<3> m_dsw;
	required_ioport m_jumpers;

	u8 m_port00 = 0;
	u8 m_keylatch = 0;
	u8 m_attrlatch = 0;
	u8 m_scroll = 0;
	std::unique_ptr<u16[]> m_vram;

	emu_timer *m_baud_clock = nullptr;
};

void microterm_f8_state::machine_start()
{
	m_keylatch = 0;
	m_attrlatch = 0;
	m_vram = make_unique_clear<u16[]>(0x800); // 6x MM2114 with weird addressing

	m_baud_clock = timer_alloc(FUNC(microterm_f8_state::baud_clock), this);
	m_baud_clock->adjust(attotime::zero, 0);

	save_item(NAME(m_port00));
	save_item(NAME(m_keylatch));
	save_item(NAME(m_attrlatch));
	save_item(NAME(m_scroll));
	save_pointer(NAME(m_vram), 0x800);
}

void microterm_f8_state::machine_reset()
{
	m_scroll = 0;
	m_port00 = 0xff;

	// UART parameters
	ioport_value dsw3 = m_dsw[2]->read();
	m_uart->write_np(BIT(dsw3, 9));
	m_uart->write_tsb(BIT(dsw3, 8));
	m_uart->write_eps(BIT(dsw3, 7));
	m_uart->write_nb1(BIT(dsw3, 6));
	m_uart->write_nb2(BIT(dsw3, 5));

	m_uart->write_cs(1);
	m_uart->write_swe(0);
}

TIMER_CALLBACK_MEMBER(microterm_f8_state::baud_clock)
{
	m_uart->write_tcp(param);
	m_uart->write_rcp(param);

	ioport_value rate = m_dsw[1]->read() ^ 0xff;
	if (BIT(rate, 7))
		m_baud_clock->adjust(attotime::from_hz(110 * 32), !param);
	else
		m_baud_clock->adjust(attotime::from_hz(19200 * 32 / rate), !param);
}

u32 microterm_f8_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if (BIT(m_port00, 2))
	{
		// Display blanked?
		bitmap.fill(rgb_t::black(), cliprect);
		return 0;
	}

	const ioport_value jumpers = m_jumpers->read();
	unsigned y = cliprect.top();
	offs_t rowbase = (((y / 12) + m_scroll) % 24) * 80;
	unsigned line = y % 12;
	if (line >= 6)
		line += 2;

	const bool cursor_on = (m_blinkcount->count() & (~jumpers << 1) & 0x38) == 0;
	const bool blink_on = (m_blinkcount->count() & (~jumpers >> 3) & 0x38) == 0;

	while (y <= cliprect.bottom())
	{
		const bool allow_underline = (line == 13) || (line == 12 && !BIT(jumpers, 1));

		for (unsigned x = cliprect.left(); x <= cliprect.right(); x++)
		{
			u16 ch = m_vram[rowbase + (x / 9)];
			u8 chdata = m_chargen[(line << 7) | (ch & 0x7f)];
			bool dot = (!BIT(ch, 8) && allow_underline) || ((BIT(ch, 10) || blink_on) && BIT(chdata, 8 - (x % 9)));
			if ((BIT(ch, 7) && cursor_on) == BIT(ch, 9))
				dot = !dot;
			bitmap.pix(y, x) = dot ? rgb_t::white() : rgb_t::black();
		}

		y++;
		line++;
		if ((line & 7) >= 6)
		{
			line = (line + 2) & 15;
			if (line == 0)
			{
				rowbase += 80;
				if (rowbase == 24 * 80)
					rowbase = 0;
			}
		}
	}

	return 0;
}

void microterm_f8_state::vblank_w(int state)
{
	if (state)
		m_bell->set_state(0);
}

u8 microterm_f8_state::bell_r()
{
	if (!machine().side_effects_disabled())
		m_bell->set_state(1);
	return 0;
}

void microterm_f8_state::scroll_w(u8 data)
{
	m_scroll++;
	if (m_scroll == 24)
		m_scroll = 0;
}

u8 microterm_f8_state::vram_r(offs_t offset)
{
	offs_t vaddr = (offset >> 8) * 80 + (offset & 0x007f);
	assert(vaddr < 0x800);

	u16 vdata = m_vram[vaddr];
	if (!machine().side_effects_disabled())
		m_attrlatch = (vdata & 0xf00) >> 4;

	// Bit 7 indicates protected attribute
	if ((~vdata & (~m_jumpers->read() >> 1) & 0xf00) != 0)
		return vdata | 0x80;
	else
		return vdata & 0x7f;
}

void microterm_f8_state::vram_w(offs_t offset, u8 data)
{
	offs_t vaddr = (offset >> 8) * 80 + (offset & 0x007f);
	assert(vaddr < 0x800);

	m_vram[vaddr] = data | u16(m_port00 & 0xf0) << 4;
	if (BIT(m_port00, 0))
		m_vram[vaddr] |= u16(m_attrlatch) << 4;
}

void microterm_f8_state::uart_transmit_w(u8 data)
{
	m_uart->transmit((data & 0x7f) | (m_dsw[2]->read() & 0x10) << 3);
}

bool microterm_f8_state::poll_keyboard()
{
	for (int row = 0; row < 11; row++)
	{
		u8 polled = m_keys[row]->read();
		if (polled == 0xff)
			continue;

		offs_t keyaddr = (m_modifiers->read() << 7) | (row << 3);
		while (BIT(polled, 0))
		{
			polled >>= 1;
			keyaddr++;
		}
		m_keylatch = m_kbdecode[keyaddr];
		return true;
	}

	return false;
}

u8 microterm_f8_state::key_r()
{
	// Cursor is supposed to stop blinking temporarily when keys are depressed
	// This implementation suspends cursor blinking when keys are actually read
	// It also suspends blinking text, which perhaps is supposed to happen
	if (!machine().side_effects_disabled())
	{
		m_blinkcount->reset_w(1);
		m_blinkcount->reset_w(0);
	}

	return m_keylatch;
}

u8 microterm_f8_state::port00_r()
{
	u8 flags = m_port00;

	// Full duplex switch
	if (!BIT(m_dsw[2]->read(), 0))
		flags |= 0x02;

	if (BIT(m_port00, 0))
		flags |= m_attrlatch;

	return flags;
}

void microterm_f8_state::port00_w(u8 data)
{
	m_port00 = data;
}

u8 microterm_f8_state::port01_r()
{
	u8 flags = 0;

	// Some timing flag (not necessarily HBLANK?)
	if (!m_screen->hblank())
		flags |= 0x80;

	// Keyboard polling
	if (poll_keyboard())
		flags |= 0x40;

	// ???
	if (!m_screen->vblank())
		flags |= 0x20;

	// Local mode switch
	if (!BIT(m_special->read(), 1))
		flags |= 0x10;

	// Protected field setting
	flags |= ~m_dsw[2]->read() & 0x06;

	return flags;
}

void microterm_f8_state::f8_mem(address_map &map)
{
	map(0x0000, 0x0bff).rom().region("maincpu", 0);
	map(0x0c00, 0x0c00).r(FUNC(microterm_f8_state::bell_r));
	map(0x2000, 0x2000).mirror(0x1fff).w(FUNC(microterm_f8_state::scroll_w));
	map(0x4000, 0x407f).select(0x1f00).rw(FUNC(microterm_f8_state::vram_r), FUNC(microterm_f8_state::vram_w));
	map(0x5800, 0x5fff).unmaprw();
	map(0x8000, 0x8000).r(m_uart, FUNC(ay51013_device::receive)).w(FUNC(microterm_f8_state::uart_transmit_w));
	map(0xf000, 0xf000).r(FUNC(microterm_f8_state::key_r));
}

void microterm_f8_state::f8_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x00).rw(FUNC(microterm_f8_state::port00_r), FUNC(microterm_f8_state::port00_w));
	map(0x01, 0x01).r(FUNC(microterm_f8_state::port01_r)).nopw();
	map(0x0c, 0x0f).rw("smi", FUNC(f3853_device::read), FUNC(f3853_device::write));
}

static INPUT_PORTS_START(act5a)
	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(' ') PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(0x0e) PORT_CODE(KEYCODE_N)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR(0x02) PORT_CODE(KEYCODE_B)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR(0x16) PORT_CODE(KEYCODE_V)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR(0x03) PORT_CODE(KEYCODE_C)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(0x18) PORT_CODE(KEYCODE_X)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(0x1a) PORT_CODE(KEYCODE_Z)

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD)) PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CHAR('h') PORT_CHAR('H') PORT_CHAR(0x08) PORT_CODE(KEYCODE_H)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR(0x07) PORT_CODE(KEYCODE_G)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR(0x06) PORT_CODE(KEYCODE_F)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR(0x04) PORT_CODE(KEYCODE_D)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR(0x13) PORT_CODE(KEYCODE_S)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR(0x01) PORT_CODE(KEYCODE_A)

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8  *  -") PORT_CHAR('8') PORT_CHAR('*') PORT_CODE(KEYCODE_8)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR(0x19) PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(0x14) PORT_CODE(KEYCODE_T)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR(0x12) PORT_CODE(KEYCODE_R)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(0x05) PORT_CODE(KEYCODE_E)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR(0x17) PORT_CODE(KEYCODE_W)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CHAR('q') PORT_CHAR('Q') PORT_CHAR(0x11) PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x09) PORT_CODE(KEYCODE_TAB)

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR('&') PORT_CODE(KEYCODE_7)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR('^') PORT_CODE(KEYCODE_6)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR('$') PORT_CODE(KEYCODE_4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR('@') PORT_CODE(KEYCODE_2)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x1b) PORT_CODE(KEYCODE_ESC)

	PORT_START("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]  }") PORT_CHAR(']') PORT_CHAR('}') PORT_CHAR(0x1d) PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('-') PORT_CHAR('_') PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[  {") PORT_CHAR('[') PORT_CHAR('{') PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR(0x10) PORT_CODE(KEYCODE_P)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0') PORT_CHAR(')') PORT_CODE(KEYCODE_0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\  |") PORT_CHAR('\\') PORT_CHAR('|') PORT_CHAR(0x1c) PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('`') PORT_CHAR('~') PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('=') PORT_CHAR('+') PORT_CODE(KEYCODE_EQUALS)

	PORT_START("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\'') PORT_CHAR('"') PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHAR(':') PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('/') PORT_CHAR('?') PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".  >  9") PORT_CHAR('.') PORT_CHAR('>') PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",  <  8") PORT_CHAR(',') PORT_CHAR('<') PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M  7") PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_M)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L  6") PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR(0x0c) PORT_CODE(KEYCODE_L)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K  5") PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR(0x0b) PORT_CODE(KEYCODE_K)

	PORT_START("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J  4") PORT_CHAR('j') PORT_CHAR('J') PORT_CODE(KEYCODE_J)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O  3") PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(0x0f) PORT_CODE(KEYCODE_O)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I  2") PORT_CHAR('i') PORT_CHAR('I') PORT_CODE(KEYCODE_I)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U  1") PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(0x15) PORT_CODE(KEYCODE_U)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9  (  0") PORT_CHAR('9') PORT_CHAR('(') PORT_CODE(KEYCODE_9)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Line Feed") PORT_CHAR(0x0a) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Delete") PORT_CHAR(0x7f) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CHAR(0x0d) PORT_CODE(KEYCODE_ENTER)

	PORT_START("KEY7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD)) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD)) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD)) PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD)) PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD)) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) PORT_CODE(KEYCODE_8_PAD)

	PORT_START("KEY8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD)) PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD)) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CODE(KEYCODE_LEFT)

	PORT_START("KEY9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B/W  Blink") PORT_CODE(KEYCODE_F8)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Intens  Undl") PORT_CODE(KEYCODE_F7)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Format On/Off") PORT_CODE(KEYCODE_F6)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Split On/Off") PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Clear Home") PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Clear EOL/EOF") PORT_CHAR(UCHAR_MAMEKEY(END)) PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ins Char/Line") PORT_CHAR(UCHAR_MAMEKEY(INSERT)) PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del Char/Line") PORT_CHAR(UCHAR_MAMEKEY(DEL)) PORT_CODE(KEYCODE_F1)

	PORT_START("KEY10")
	PORT_BIT(0x3f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Send Line/Page") PORT_CODE(KEYCODE_F11)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Print Line/Page") PORT_CHAR(UCHAR_MAMEKEY(PRTSCR)) PORT_CODE(KEYCODE_F12)

	PORT_START("MODIFIERS")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CHAR(UCHAR_SHIFT_1) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num") PORT_CHAR(UCHAR_MAMEKEY(NUMLOCK)) PORT_CODE(KEYCODE_LALT) PORT_TOGGLE
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CHAR(UCHAR_SHIFT_2) PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Caps Lock") PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE

	PORT_START("SPECIAL")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_F9) PORT_WRITE_LINE_DEVICE_MEMBER("txd", FUNC(input_merger_device::in_w<1>))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Line/Loc") PORT_CODE(KEYCODE_F10) PORT_TOGGLE

	PORT_START("DSW1")
	PORT_DIPNAME(0xff, 0xfd, "Printer Data Rate") PORT_DIPLOCATION("S1:8,7,6,5,4,3,2,1")
	PORT_DIPSETTING(0x7f, "110")
	PORT_DIPSETTING(0xbf, "300")
	PORT_DIPSETTING(0xdf, "600")
	PORT_DIPSETTING(0xef, "1,200")
	PORT_DIPSETTING(0xf7, "2,400")
	PORT_DIPSETTING(0xfb, "4,800")
	PORT_DIPSETTING(0xfd, "9,600")
	PORT_DIPSETTING(0xfe, "19,200")

	PORT_START("DSW2")
	PORT_DIPNAME(0xff, 0xfd, "I/O Data Rate") PORT_DIPLOCATION("S2:1,2,3,4,5,6,7,8")
	PORT_DIPSETTING(0x7f, "110")
	PORT_DIPSETTING(0xbf, "300")
	PORT_DIPSETTING(0xdf, "600")
	PORT_DIPSETTING(0xef, "1,200")
	PORT_DIPSETTING(0xf7, "2,400")
	PORT_DIPSETTING(0xfb, "4,800")
	PORT_DIPSETTING(0xfd, "9,600")
	PORT_DIPSETTING(0xfe, "19,200")

	PORT_START("DSW3")
	PORT_DIPNAME(0x001, 0x000, "Conversation Mode") PORT_DIPLOCATION("S3:1")
	PORT_DIPSETTING(0x001, "Half Duplex")
	PORT_DIPSETTING(0x000, "Full Duplex")
	PORT_DIPNAME(0x006, 0x006, "Protected Field Attribute") PORT_DIPLOCATION("S3:2,3")
	PORT_DIPSETTING(0x006, "Reduced Intensity")
	PORT_DIPSETTING(0x002, "Blinking")
	PORT_DIPSETTING(0x004, "Reverse Video")
	PORT_DIPSETTING(0x000, "Underline")
	PORT_DIPNAME(0x008, 0x008, "Display Null Character") PORT_DIPLOCATION("S3:4")
	PORT_DIPSETTING(0x000, DEF_STR(Off))
	PORT_DIPSETTING(0x008, DEF_STR(On))
	PORT_DIPNAME(0x010, 0x010, "8th Bit Transmit") PORT_DIPLOCATION("S3:5")
	PORT_DIPSETTING(0x000, "0 (Space)")
	PORT_DIPSETTING(0x010, "1 (Mark)")
	PORT_DIPNAME(0x060, 0x060, "Word Length") PORT_DIPLOCATION("S3:6,7")
	PORT_DIPSETTING(0x000, "5 Bits")
	PORT_DIPSETTING(0x040, "6 Bits")
	PORT_DIPSETTING(0x020, "7 Bits")
	PORT_DIPSETTING(0x060, "8 Bits")
	PORT_DIPNAME(0x280, 0x280, "Parity") PORT_DIPLOCATION("S3:8,10")
	PORT_DIPSETTING(0x280, "None")
	PORT_DIPSETTING(0x080, "Even")
	PORT_DIPSETTING(0x000, "Odd")
	PORT_DIPNAME(0x100, 0x100, "Number of Stop Bits") PORT_DIPLOCATION("S3:9")
	PORT_DIPSETTING(0x000, "1")
	PORT_DIPSETTING(0x100, "2")

	PORT_START("JUMPERS")
	PORT_DIPNAME(0x0003, 0x0002, "Underline") PORT_DIPLOCATION("W1:1,2")
	PORT_DIPSETTING(0x0002, "Single")
	PORT_DIPSETTING(0x0001, "Double")
	PORT_DIPNAME(0x003c, 0x0034, "Cursor Rate") PORT_DIPLOCATION("W2:1,2,3,4")
	PORT_DIPSETTING(0x001c, "0 Hz")
	PORT_DIPSETTING(0x002c, "1 Hz")
	PORT_DIPSETTING(0x0034, "2 Hz")
	PORT_DIPSETTING(0x0038, "4 Hz")
	PORT_DIPNAME(0x01c0, 0x00c0, "Blinking Rate") PORT_DIPLOCATION("W3:1,2,3")
	PORT_DIPSETTING(0x00c0, "1 Hz")
	PORT_DIPSETTING(0x0140, "2 Hz")
	PORT_DIPSETTING(0x0180, "4 Hz")
	PORT_DIPNAME(0x1e00, 0x0e00, "Protected Video Attribute") PORT_DIPLOCATION("W4:4,3,2,1")
	PORT_DIPSETTING(0x0e00, "Reduced Intensity")
	PORT_DIPSETTING(0x1600, "Blinking")
	PORT_DIPSETTING(0x1a00, "Reverse Video")
	PORT_DIPSETTING(0x1c00, "Underline")
	PORT_DIPNAME(0xe000, 0x6000, "Keyboard Auto Repeat Rate") PORT_DIPLOCATION("W5:1,2,3")
	PORT_DIPSETTING(0x6000, "7.5 cps")
	PORT_DIPSETTING(0xa000, "15 cps")
	PORT_DIPSETTING(0xc000, "30 cps")
INPUT_PORTS_END

void microterm_f8_state::act5a(machine_config &config)
{
	cpu_device &maincpu(F8(config, "maincpu", 2_MHz_XTAL));
	maincpu.set_addrmap(AS_PROGRAM, &microterm_f8_state::f8_mem);
	maincpu.set_addrmap(AS_IO, &microterm_f8_state::f8_io);
	maincpu.set_irq_acknowledge_callback("smi", FUNC(f3853_device::int_acknowledge));

	f3853_device &smi(F3853(config, "smi", 2_MHz_XTAL));
	smi.int_req_callback().set_inputline("maincpu", F8_INPUT_LINE_INT_REQ);

	AY51013(config, m_uart);
	m_uart->read_si_callback().set(m_io, FUNC(rs232_port_device::rxd_r));
	m_uart->write_so_callback().set("txd", FUNC(input_merger_device::in_w<0>));
	m_uart->write_dav_callback().set("smi", FUNC(f3853_device::ext_int_w));
	m_uart->set_auto_rdav(true);

	RS232_PORT(config, m_io, default_rs232_devices, nullptr);

	INPUT_MERGER_ALL_HIGH(config, "txd").output_handler().set(m_io, FUNC(rs232_port_device::write_txd));

	//RS232_PORT(config, m_aux, default_rs232_devices, nullptr);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(16.572_MHz_XTAL, 918, 0, 720, 301, 0, 288); // more or less guessed
	screen.set_screen_update(FUNC(microterm_f8_state::screen_update));
	screen.screen_vblank().set(FUNC(microterm_f8_state::vblank_w));
	screen.screen_vblank().append(m_blinkcount, FUNC(ripple_counter_device::clock_w)).invert();

	RIPPLE_COUNTER(config, m_blinkcount).set_stages(6);

	SPEAKER(config, "mono").front_center();
	BEEP(config, m_bell, 1760);
	m_bell->add_route(ALL_OUTPUTS, "mono", 0.50);
}

ROM_START(act5a)
	ROM_REGION(0x0c00, "maincpu", 0) // xtals 16.572MHz, 2.000MHz
	ROM_LOAD("act5a_2708.u1",  0x0000, 0x0400, CRC(ad3bfa5a) SHA1(723c7bfefbf96177171b8e58a8e20ee69daa27f0))
	ROM_LOAD("act5a_2708.u12", 0x0400, 0x0400, CRC(be4a148d) SHA1(69bf838ed9fccdf7f225bc380bcce8e7e0bd88bc))
	ROM_LOAD("act5a_2708.u22", 0x0800, 0x0400, CRC(f0ec3b9f) SHA1(7785eba9993c23a767b84fff2c44d5cb6210ad80))

	ROM_REGION(0x2000, "kbdecode", 0)
	ROM_LOAD("act5a_9316.u39", 0x0000, 0x2000, CRC(6f9eac71) SHA1(0488bdd19a6bff4e869a3480c7e9d8c5ca9938eb))

	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD("act5a_9316.u55", 0x0000, 0x2000, CRC(8f96b7c8) SHA1(652d420ab5be9412cae322cd1799f8a9e3959c44))
ROM_END

} // anonymous namespace


//COMP(1976, act4, 0, 0, act5a, act5a, microterm_f8_state, empty_init, "Micro-Term", "ACT-IV", MACHINE_NOT_WORKING)
//COMP(1978, act5, 0, 0, act5a, act5a, microterm_f8_state, empty_init, "Micro-Term", "ACT-V", MACHINE_NOT_WORKING)
COMP(1980, act5a, 0, 0, act5a, act5a, microterm_f8_state, empty_init, "Micro-Term", "ACT-5A", MACHINE_NOT_WORKING)



microvsn.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol, hap
// thanks-to:Dan Boris, Kevin Horton, Sean Riddle
/*******************************************************************************

Milton Bradley Microvision, handheld game console

Hardware notes:
- SCUS0488(Hughes HLCD0488) LCD, 16*16 screen
- piezo, 12 buttons under membrane + analog paddle(MB calls it the Control Knob)
- no CPU on console, it is on the cartridge

The LCD motion blur is normal. To decrease it, simply increase the screen contrast
in MAME, this makes it similar to repro LCD replacements. It's also advised to
disable screen filtering, eg. with -prescale or -nofilter.

12 games were released, all of them have a TMS1100 MCU. The first couple of
games had an Intel 8021 MCU at first, but Milton Bradley switched to TMS1100.
See the softwarelist XML for details.

Each game had a screen- and button overlay attached to it, MAME external artwork
is recommended.

TODO:
- dump/add remaining 8021 cartridges, which games have 8021 versions? An online
  FAQ mentions at least Block Buster, Connect Four, Bowling.

*******************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/mcs48/mcs48.h"
#include "cpu/tms1000/tms1100.h"
#include "sound/dac.h"
#include "video/hlcd0488.h"
#include "video/pwm.h"

#include "emupal.h"
#include "softlist_dev.h"
#include "screen.h"
#include "speaker.h"

#include "microvision.lh"


namespace {

class microvision_state : public driver_device
{
public:
	microvision_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_tms1100( *this, "tms1100_cpu" ),
		m_i8021( *this, "i8021_cpu" ),
		m_lcd(*this, "lcd"),
		m_lcd_pwm(*this, "lcd_pwm"),
		m_dac( *this, "dac" ),
		m_cart(*this, "cartslot"),
		m_inputs(*this, "COL%u", 0),
		m_paddle(*this, "PADDLE"),
		m_conf(*this, "CONF")
	{ }

	void microvision(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(conf_changed) { apply_settings(); }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override { apply_settings(); }
	virtual void device_post_load() override { apply_settings(); }

private:
	optional_device<tms1100_cpu_device> m_tms1100;
	optional_device<i8021_device> m_i8021;
	required_device<hlcd0488_device> m_lcd;
	required_device<pwm_display_device> m_lcd_pwm;
	required_device<dac_byte_interface> m_dac;
	required_device<generic_slot_device> m_cart;
	required_ioport_array<3> m_inputs;
	required_ioport m_paddle;
	required_ioport m_conf;

	u32 tms1100_micro_pla(offs_t offset);
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
	void apply_settings(void);

	u8 m_pla_auto = 0;
	u16 m_butmask_auto = 0;
	u16 m_button_mask = 0;
	bool m_paddle_auto = false;
	bool m_paddle_on = false;
	attotime m_paddle_delay;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void lcd_output_w(offs_t offset, u16 data) { m_lcd_pwm->matrix(offset, data); }

	// TMS1100 interface
	u8 tms1100_k_r();
	void tms1100_o_w(u16 data);
	void tms1100_r_w(u32 data);

	u32 m_r = 0;

	// Intel 8021 interface
	u8 i8021_p0_r();
	void i8021_p0_w(u8 data);
	void i8021_p1_w(u8 data);
	void i8021_p2_w(u8 data);
	int i8021_t1_r();

	u8 m_p0 = 0xff;
	u8 m_p2 = 0xff;
};

void microvision_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_paddle_delay));
	save_item(NAME(m_r));
	save_item(NAME(m_p0));
	save_item(NAME(m_p2));

	// don't save: m_pla_auto, m_butmask_auto, m_paddle_auto,
	// m_button_mask, m_paddle_on
}



/*******************************************************************************
    Cartridge Init
*******************************************************************************/

static const u16 tms1100_output_pla[2][0x20] =
{
	// default TMS1100 O output PLA
	// verified for: blckbstr, pinball
	{
		0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06, 0x0e,
		0x01, 0x09, 0x05, 0x0d, 0x03, 0x0b, 0x07, 0x0f,
		0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06, 0x0e,
		0x01, 0x09, 0x05, 0x0d, 0x03, 0x0b, 0x07, 0x0f
	},

	// reversed bit order
	// verified for: bowling, vegasslt
	{
		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
	}
};

u32 microvision_state::tms1100_micro_pla(offs_t offset)
{
	// default TMS1100 microinstructions PLA - this should work for all games
	// verified for: blckbstr, bowling, pinball, vegasslt

	// TCY, YNEC, TCMIY, AxAAC
	static const u16 micro1[4] = { 0x0108, 0x9080, 0x8068, 0x0136 };

	// 0x00, 0x20, 0x30
	static const u16 micro2[0x30] =
	{
		0x1402, 0x0c30, 0xd002, 0x2404, 0x8019, 0x8038, 0x0416, 0x0415,
		0x0104, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1100, 0x0000,

		0x000a, 0x0404, 0x0408, 0x8004, 0xa019, 0xa038, 0x2004, 0x2000,
		0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,

		0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
		0x1580, 0x1580, 0x1580, 0x1580, 0x0c34, 0x0834, 0x0434, 0x1400
	};

	static const int micro2h[4] = { 0x00, -1, 0x10, 0x20 };

	u16 data = 0;

	if (offset >= 0x40 && offset < 0x80)
	{
		data = micro1[offset >> 4 & 3];
		if (offset == 0x7f) data ^= 2; // CLA
	}
	else if (offset < 0x40 && (offset & 0xf0) != 0x10)
		data = micro2[micro2h[offset >> 4] | (offset & 0xf)];

	return (data == 0) ? 0x8fa3 : data;
}

DEVICE_IMAGE_LOAD_MEMBER(microvision_state::cart_load)
{
	u32 size = m_cart->common_get_size("rom");

	if (size != 0x400 && size != 0x800)
		return std::make_pair(image_error::INVALIDLENGTH, "Invalid ROM file size (must be 1K or 2K)");

	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	// set default settings
	u32 clock = (size == 0x400) ? 3000000 : 500000;
	m_pla_auto = 0;
	m_butmask_auto = 0xfff;
	m_paddle_auto = false;

	if (image.loaded_through_softlist())
	{
		const char *cclock = image.get_feature("clock");
		u32 sclock = cclock ? strtoul(cclock, nullptr, 0) : 0;
		if (sclock != 0)
			clock = sclock;

		const char *butmask = image.get_feature("butmask");
		m_butmask_auto = butmask ? strtoul(butmask, nullptr, 0) : 0;
		m_butmask_auto = ~m_butmask_auto & 0xfff;

		const char *pla = image.get_feature("pla");
		m_pla_auto = pla && strtoul(pla, nullptr, 0) ? 1 : 0;

		const char *paddle = image.get_feature("paddle");
		m_paddle_auto = paddle && strtoul(paddle, nullptr, 0);
	}

	// detect MCU on file size
	if (size == 0x400)
	{
		// 8021 MCU
		memcpy(memregion("i8021_cpu")->base(), m_cart->get_rom_base(), size);
		m_i8021->set_clock(clock);
	}
	else
	{
		// TMS1100 MCU
		memcpy(memregion("tms1100_cpu")->base(), m_cart->get_rom_base(), size);
		m_tms1100->set_clock(clock);
	}

	return std::make_pair(std::error_condition(), std::string());
}

void microvision_state::apply_settings()
{
	u8 conf = m_conf->read();

	// cartridge physically restricts button panel (some glitches otherwise)
	m_button_mask = (conf & 1) ? m_butmask_auto : 0xfff;

	u8 pla = ((conf & 0x18) == 0x10) ? m_pla_auto : (conf >> 3 & 1);
	m_tms1100->set_output_pla(tms1100_output_pla[pla]);

	m_paddle_on = ((conf & 6) == 4) ? m_paddle_auto : bool(conf & 2);
}



/*******************************************************************************
    Video
*******************************************************************************/

uint32_t microvision_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	for (int y = 0; y < 16; y++)
	{
		for (int x = 0; x < 16; x++)
		{
			// simulate LCD persistence
			int p = m_lcd_pwm->read_element_bri(y ^ 15, x ^ 15) * 8500;
			p = (p > 255) ? 0 : p ^ 255;

			if (cliprect.contains(x, y))
				bitmap.pix(y, x) = p << 16 | p << 8 | p;
		}
	}

	return 0;
}



/*******************************************************************************
    I/O
*******************************************************************************/

// TMS1100 interface

u8 microvision_state::tms1100_k_r()
{
	u8 data = 0;

	// multiplexed inputs
	for (int i = 0; i < 3; i++)
		if (BIT(m_r, i + 8))
			data |= m_inputs[i]->read() & (m_button_mask >> (i * 4) & 0xf);

	// K8: paddle capacitor
	if (m_paddle_on && m_paddle_delay < machine().time())
		data |= BIT(m_r, 2) << 3;

	return data;
}

void microvision_state::tms1100_o_w(u16 data)
{
	// O0-O3: LCD data
	m_lcd->data_w(data & 0xf);
}

void microvision_state::tms1100_r_w(u32 data)
{
	// R2: charge paddle capacitor when high
	if (~m_r & data & 4 && m_paddle_on)
	{
		// note that the games don't use the whole range, so there's a deadzone around the edges
		const float step = (2000 - 500) / 255.0f; // approximation
		m_paddle_delay = machine().time() + attotime::from_usec(500 + m_paddle->read() * step);
	}

	// R0: speaker lead 2
	// R1: speaker lead 1 (GND on some carts)
	m_dac->write((BIT(data, 0) << 1) | BIT(data, 1));

	// R6: LCD latch pulse
	// R7: LCD data clock
	m_lcd->latch_pulse_w(BIT(data, 6));
	m_lcd->data_clk_w(BIT(data, 7));

	// R8-R10: input mux
	m_r = data;
}


// Intel 8021 interface

u8 microvision_state::i8021_p0_r()
{
	u8 in[3];
	for (int i = 0; i < 3; i++)
		in[i] = m_inputs[i]->read() & (m_button_mask >> (i * 4) & 0xf);

	u8 data = 0;

	// P00-P02: multiplexed inputs from P04-P07
	for (int i = 0; i < 3; i++)
		if (~(m_p0 >> 4) & in[i])
			data |= 1 << i;

	// P04-P07: multiplexed inputs from P00-P02
	for (int i = 0; i < 3; i++)
		if (BIT(~m_p0, i))
			data |= in[i] << 4;

	return ~data & m_p0;
}

void microvision_state::i8021_p0_w(u8 data)
{
	// P0-P2, P4-P7: input mux
	m_p0 = data;
}

void microvision_state::i8021_p1_w(u8 data)
{
	// P14-P17: lcd data
	m_lcd->data_w(data >> 4 & 0xf);

	// P10: lcd latch pulse
	// P11: lcd data clk
	m_lcd->latch_pulse_w(BIT(data, 0));
	m_lcd->data_clk_w(BIT(data, 1));
}

void microvision_state::i8021_p2_w(u8 data)
{
	// P20: speaker lead 1
	// P21: speaker lead 2
	m_dac->write(data & 3);

	// P22,P23: charge paddle capacitor when low
	if (m_p2 & 0xc && (data & 0xc) == 0 && m_paddle_on)
	{
		const float step = (1000 - 10) / 255.0f; // approximation
		m_paddle_delay = machine().time() + attotime::from_usec(10 + (m_paddle->read() ^ 0xff) * step);
	}

	m_p2 = data;
}

int microvision_state::i8021_t1_r()
{
	// T1: paddle capacitor (active low)
	if (m_paddle_on)
		return (m_p2 & 0xc || m_paddle_delay > machine().time()) ? 1 : 0;
	else
		return 1;
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( microvision )
	PORT_START("COL0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON12 ) PORT_CODE(KEYCODE_C)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON9 ) PORT_CODE(KEYCODE_D)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_CODE(KEYCODE_E)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CODE(KEYCODE_3)

	PORT_START("COL1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON11 ) PORT_CODE(KEYCODE_X)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON8 ) PORT_CODE(KEYCODE_S)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_CODE(KEYCODE_W)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CODE(KEYCODE_2)

	PORT_START("COL2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON10 ) PORT_CODE(KEYCODE_Z)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_CODE(KEYCODE_A)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_CODE(KEYCODE_Q)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CODE(KEYCODE_1)

	PORT_START("PADDLE")
	PORT_BIT( 0xff, 0x80, IPT_PADDLE ) PORT_SENSITIVITY(15) PORT_KEYDELTA(15) PORT_CENTERDELTA(0)

	PORT_START("CONF")
	PORT_CONFNAME( 0x01, 0x01, "Restrict Buttons" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(microvision_state::conf_changed), 0)
	PORT_CONFSETTING(    0x00, DEF_STR( No ) )
	PORT_CONFSETTING(    0x01, "Auto" )
	PORT_CONFNAME( 0x06, 0x04, "Paddle Hardware" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(microvision_state::conf_changed), 0)
	PORT_CONFSETTING(    0x00, DEF_STR( No ) ) // no circuitry on cartridge PCB
	PORT_CONFSETTING(    0x02, DEF_STR( Yes ) )
	PORT_CONFSETTING(    0x04, "Auto" )
	PORT_CONFNAME( 0x18, 0x10, "TMS1100 PLA Type" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(microvision_state::conf_changed), 0)
	PORT_CONFSETTING(    0x00, "0" )
	PORT_CONFSETTING(    0x08, "1" )
	PORT_CONFSETTING(    0x10, "Auto" )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void microvision_state::microvision(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_tms1100, 0);
	m_tms1100->set_output_pla(tms1100_output_pla[0]);
	m_tms1100->set_decode_micro().set(FUNC(microvision_state::tms1100_micro_pla));
	m_tms1100->read_k().set(FUNC(microvision_state::tms1100_k_r));
	m_tms1100->write_o().set(FUNC(microvision_state::tms1100_o_w));
	m_tms1100->write_r().set(FUNC(microvision_state::tms1100_r_w));

	I8021(config, m_i8021, 0);
	m_i8021->bus_in_cb().set(FUNC(microvision_state::i8021_p0_r));
	m_i8021->bus_out_cb().set(FUNC(microvision_state::i8021_p0_w));
	m_i8021->p1_out_cb().set(FUNC(microvision_state::i8021_p1_w));
	m_i8021->p2_out_cb().set(FUNC(microvision_state::i8021_p2_w));
	m_i8021->t1_in_cb().set(FUNC(microvision_state::i8021_t1_r));

	// video hardware
	HLCD0488(config, m_lcd);
	m_lcd->write_cols().set(FUNC(microvision_state::lcd_output_w));

	PWM_DISPLAY(config, m_lcd_pwm).set_size(16, 16);
	m_lcd_pwm->set_interpolation(0.25);
	config.set_default_layout(layout_microvision);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(0);
	screen.set_screen_update(FUNC(microvision_state::screen_update));
	screen.set_size(16, 16);
	screen.set_visarea_full();

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_2BIT_ONES_COMPLEMENT(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.25);

	// cartridge
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "microvision_cart");
	m_cart->set_must_be_loaded(true);
	m_cart->set_device_load(FUNC(microvision_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("microvision");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( microvsn )
	// nothing here yet, ROM is on the cartridge
	ROM_REGION( 0x400, "i8021_cpu", ROMREGION_ERASE00 )
	ROM_REGION( 0x800, "tms1100_cpu", ROMREGION_ERASE00 )
	ROM_REGION( 867, "tms1100_cpu:mpla", ROMREGION_ERASE00 )
	ROM_REGION( 365, "tms1100_cpu:opla", ROMREGION_ERASE00 )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE      INPUT        CLASS              INIT        COMPANY, FULLNAME, FLAGS
SYST( 1979, microvsn, 0,      0,      microvision, microvision, microvision_state, empty_init, "Milton Bradley", "Microvision", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )



midi2cv8.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:m1macrophage

/*
The PAiA midi2cv8 is an 8-channel MIDI-to-CV converter. It is a module for
the PAiA 9700 Series modular system, but can be used as a standalone MIDI-to-CV
converter.

DIP switches control the MIDI channel to listen to, and the operating mode.
Depending on that mode, each of the 8 outputs produces control voltage (CV),
trigger, or gate signals in response to MIDI messages.

The firmware is running on an 80C31. It listens for MIDI messages and sets the
voltage for the 8 outputs by time-multiplexing 8 sample & hold (S&H) circuits.
Specifically, the firmware will write a value to the 8-bit DAC, and after some
delay for the DAC to settle, it will enable the S&H for one output. After some
additional delay for the S&H capacitor to (dis)charge to the target value, the
firmware will disable that S&H and move on to the next one.

The midi2cv8 comes in two configurations: Volt-per-octave (V/Oct) and
Volt-per-Hertz (V/Hz).

In the V/Oct configuration, the output of the DAC (a current) is converted to a
voltage in the range 0-10V.

The V/Hz configuration adds a daughterboard and changes some of the components.
R32 (2.7Kohm) is added, and R28 is changed from 5.6Kohm to 2.7Kohm. These change
the DAC output range to 5-10V. The daughterboard is then used to divide that by
1, 2, 4, 8 or 16 (controlled by the MCU). This is a neat trick to get
exponential voltage output for 5 octaves, while only using an 8-bit DAC.

The firmware for the two configurations is the same. P1.7 of the MCU is read at
startup to detect whether the daughterboard is installed. If it is, the firmware
will switch to V/Hz mode. See the two variants of is_volts_per_hz_r().

This driver is based on the schematics, user manual and documentation provided
by the manufacturer on their website. Both the V/Oct and V/Hz configurations are
emulated. This driver cannot replace the real device. MAME does not output
physical voltages. This is just an educational tool.

Usage:

The provided layout will display the generated CVs/triggers/gates next to the
outputs.

The default dipswitch setting for "Mode" ("Mode 8 - self tests") does not
require MIDI. Just run the driver and see voltages come to life on the screen.

The other modes require supplying a MIDI input source to MAME.
Example:
./mame -listmidi  # List MIDI devices, physical or virtual (e.g. DAWs).
./mame -window midi2cv8 -midiin "{midi device}"

Keep in mind that dipswitch changes don't take effect until a restart or
reset (F3).
*/

#include "emu.h"

#include "cpu/mcs51/mcs51.h"
#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "machine/rescap.h"
#include "video/pwm.h"

#include "paia_midi2cv8.lh"

#define LOG_DAC         (1U << 1)
#define LOG_CVS         (1U << 2)
#define LOG_CALIBRATION (1U << 3)

#define VERBOSE (LOG_GENERAL | LOG_CALIBRATION)
//#define LOG_OUTPUT_FUNC osd_printf_info

#include "logmacro.h"

namespace {

constexpr const char MAINCPU_TAG[] = "80c31";

class midi2cv8_state : public driver_device
{
public:
	midi2cv8_state(const machine_config &mconfig, device_type type, const char *tag) ATTR_COLD;

	void midi2cv8(machine_config &config) ATTR_COLD;

	DECLARE_INPUT_CHANGED_MEMBER(dac_trimmer_changed);

protected:
	static inline constexpr double VCC = 5.0;

	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

	void update_active_cv();
	virtual bool compute_cv(double *cv) const;
	virtual int is_volts_per_hz_r() const;
	virtual double r28() const;

	double get_dac_i_out() const { return m_dac_i_fs * m_dac_value / 255.0; }
	mcs51_cpu_device &get_maincpu() { return *m_maincpu; }

private:
	void midi_rxd_w(int state);
	int midi_rxd_r() const;

	void dac_w(u8 data);
	void output_mux_select_w(u8 data);
	void update_dac_reference();

	void program_map(address_map &map) ATTR_COLD;
	void external_memory_map(address_map &map) ATTR_COLD;

	required_device<mcs51_cpu_device> m_maincpu;
	required_device<pwm_display_device> m_midi_pwm_led;
	required_ioport m_dac_trimmer;
	output_finder<8> m_cv_display_integer;
	output_finder<8> m_cv_display_fractional;

	bool m_inhibit_output_mux = false;
	u8 m_selected_output_mux = 0;
	u8 m_dac_value = 0;
	double m_dac_i_fs = 0;
	u8 m_midi_rxd_bit = 1; // Initial value needs to be 1, for serial "idle".
	std::array<double, 8> m_cv;
};

// MIDI2CV8 with the V/Hz daughterboard.
class midi2cv8_vhz_state : public midi2cv8_state
{
public:
	midi2cv8_vhz_state(const machine_config &mconfig, device_type type, const char *tag) ATTR_COLD;

	void midi2cv8_vhz(machine_config &config) ATTR_COLD;

	DECLARE_INPUT_CHANGED_MEMBER(octave_trimmer_changed);

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

	bool compute_cv(double *cv) const override;
	int is_volts_per_hz_r() const override;
	double r28() const override;

private:
	void octave_mux_select_w(u8 data);
	void update_octave_calibration();

	required_ioport_array<4> m_octave_trimmers;
	std::array<double, 5> m_octave_scalers = {1, 1, 1, 1, 1};
	u8 m_selected_octave_mux = 0;
};

// The implementations of midi2cv8_state and midi2cv8_vhz_state below are
// interleaved, to better demonstrate the difference in behavior between them.

midi2cv8_state::midi2cv8_state(const machine_config &mconfig, device_type type, const char *tag)
	: driver_device(mconfig, type, tag)
	, m_maincpu(*this, MAINCPU_TAG)
	, m_midi_pwm_led(*this, "midi_pwm_led")
	, m_dac_trimmer(*this, "dac_trimmer")
	, m_cv_display_integer(*this, "cv_%d_integer", 1U)
	, m_cv_display_fractional(*this, "cv_%d_fractional", 1U)
{
	std::fill(m_cv.begin(), m_cv.end(), -1);
}

midi2cv8_vhz_state::midi2cv8_vhz_state(const machine_config &mconfig, device_type type, const char *tag)
	: midi2cv8_state(mconfig, type, tag)
	, m_octave_trimmers(*this, "octave_%u_trimmer", 1U)
{
}

void midi2cv8_state::update_active_cv()
{
	// Mapping from CV MUX output to the output on the panel from top to bottom.
	// 0 is at the top, 7 at the bottom.
	static constexpr const int OUTPUT_MAPPING[8] = {0, 4, 1, 5, 2, 6, 3, 7};

	if (m_inhibit_output_mux)
		return;

	double cv = 0;
	if (!compute_cv(&cv))
		return;

	const int physical_output = OUTPUT_MAPPING[m_selected_output_mux];
	if (cv == m_cv[physical_output])
		return;

	m_cv[physical_output] = cv;
	const s32 cv_millis = s32(round(1000 * cv));
	m_cv_display_integer[physical_output] = cv_millis / 1000;
	m_cv_display_fractional[physical_output] = cv_millis % 1000;

	LOGMASKED(LOG_CVS, "CV %d - %d: %f - %d @ %f\n",
			physical_output + 1, m_selected_output_mux, cv, cv_millis,
			machine().time().as_double());
}

bool midi2cv8_state::compute_cv(double *cv) const
{
	// IC10:B (LM324 op-amp) and R28 convert the DAC output current to a voltage.
	*cv = get_dac_i_out() * r28();
	return true;
}

bool midi2cv8_vhz_state::compute_cv(double *cv) const
{
	// -1 means that MUX input is not connected.
	constexpr int SCALER_INDEX[8] = {-1, 4, 3, 2, 1, 0, -1, -1};
	assert(m_selected_octave_mux >= 0 && m_selected_octave_mux < 8);
	const int scaler_index = SCALER_INDEX[m_selected_octave_mux];
	if (scaler_index < 0)
		return false;

	// IC10:A (LM324 op-amp), R39 and R40 invert VCC to produce -5V, which is
	// then summed with the DAC output via R32. IC10:B converts the current sum
	// to a voltage, while inverting it. This results in a nominal voltage range
	// of 5V-10V. That voltage is then optionally scaled down by the voltage
	// divider network selected by the octave MUX.
	constexpr double R39 = RES_K(33);
	constexpr double R40 = RES_K(33);
	constexpr double R32 = RES_R(2700);
	constexpr double I_OFFSET = (R40 / R39) * VCC / R32;
	*cv = (I_OFFSET + get_dac_i_out()) * r28() * m_octave_scalers[scaler_index];
	return true;
}

int midi2cv8_state::is_volts_per_hz_r() const
{
	// P1.7 pulled up by R54, but connected to GND when the V/Hz option is not
	// installed. This results in P1.7 reading as 0.
	return 0;
}

double midi2cv8_state::r28() const
{
	return RES_R(5600);
}

double midi2cv8_vhz_state::r28() const
{
	return RES_R(2700);
}

int midi2cv8_vhz_state::is_volts_per_hz_r() const
{
	// P1.7 pulled up by R54, and connected to R5 in the V/Hz board, when
	// that board is installed. This results in P1.7 reading as 1.
	return 1;
}

void midi2cv8_state::midi_rxd_w(int state)
{
	m_midi_rxd_bit = state;

	// MIDI IN state is inverted twice (IC6:A and IC6:C) and connected to the
	// cathode of LED D2. So the LED will be on when MIDI IN is low.
	m_midi_pwm_led->write_element(0, 0, state ? 0 : 1);
}

int midi2cv8_state::midi_rxd_r() const
{
	return m_midi_rxd_bit;
}

void midi2cv8_state::dac_w(u8 data)
{
	if (m_dac_value == data)
		return;
	m_dac_value = data;
	update_active_cv();
	LOGMASKED(LOG_DAC, "DAC value: %02x\n", m_dac_value);
}

void midi2cv8_state::output_mux_select_w(u8 data)
{
	// MUX (IC13) is a 4051.
	// P3.1 -> MUX INH
	// P3.2 -> MUX B
	// P3.3 -> MUX C
	// P3.4 -> MUX A
	// All signals above are inverted and level-shifted by Q4-Q7.

	data = ~data & 0x1e;
	const bool inhibit = BIT(data, 1);
	const u8 selection = bitswap<3>(data, 3, 2, 4);
	if (inhibit == m_inhibit_output_mux && selection == m_selected_output_mux)
		return;
	m_inhibit_output_mux = inhibit;
	m_selected_output_mux = selection;
	update_active_cv();
}

void midi2cv8_state::update_dac_reference()
{
	constexpr double R29_POT_MAX = RES_R(1000);
	constexpr double R31 = RES_R(2200);

	const double r29 = R29_POT_MAX * (100 - m_dac_trimmer->read()) / 100.0;
	const double i_ref = VCC / (r29 + R31);

	// Compute max ("full scale") output current using the formula in the DAC08
	// datasheet.
	m_dac_i_fs = i_ref * 255.0 / 256.0;
	update_active_cv();

	LOGMASKED(LOG_CALIBRATION, "DAC iFS updated: %f\n", m_dac_i_fs);
}

void midi2cv8_vhz_state::octave_mux_select_w(u8 data)
{
	// Octave MUX (IC4) is a 4051. X0, X6 and X7 are not connected.
	// MUX INH is tied low, so it is always enabled.
	// P1.5 -> OCT A.
	// P1.6 -> OCT B.
	// P1.7 -> OCT C.
	// All signals above are inverted and level-shifted by Q1-Q3.

	const u8 selection = (~data & 0xe0) >> 5;  // Bits 5-7.
	if (m_selected_octave_mux == selection)
		return;
	m_selected_octave_mux = selection;
	update_active_cv();
}

// Returns the voltage proportion at different points on the resistive divider
// ladder. This ladder is used for scaling down the CV to lower octaves.
constexpr std::array<double, 8> octave_resistor_ladder_v()
{
	constexpr double RESISTOR_LADDER[9] =
	{
		RES_R(47),  // R47
		RES_2_PARALLEL(RES_R(22), RES_K(1)),  // R26, R20 pot
		RES_R(22),  // R21
		RES_2_PARALLEL(RES_R(56), RES_K(1)),  // R18, R14 pot
		RES_R(47),  // R17
		RES_2_PARALLEL(RES_R(120), RES_K(1)),  // R13, R11 pot
		RES_R(100),  // R12
		RES_2_PARALLEL(RES_R(270), RES_K(1)),  // R10, R8 pot
		RES_R(390),  // R9
	};

	// Compute cumulative resistance in both directions.
	std::array<double, 8> r_lower{};
	std::array<double, 8> r_upper{};
	r_lower[0] = RESISTOR_LADDER[0];
	r_upper[7] = RESISTOR_LADDER[8];
	for (int i = 1; i < 8; ++i)
	{
		r_lower[i] = r_lower[i - 1] + RESISTOR_LADDER[i];
		r_upper[7 - i] = r_upper[8 - i] + RESISTOR_LADDER[8 - i];
	}

	std::array<double, 8> ladder_v{};
	for (int i = 0; i < 8; ++i)
		ladder_v[i] = RES_VOLTAGE_DIVIDER(r_upper[i], r_lower[i]);
	return ladder_v;
}

void midi2cv8_vhz_state::update_octave_calibration()
{
	constexpr std::array<double, 8> ladder_v = octave_resistor_ladder_v();
	for (int octave = 0; octave < 4; ++octave)
	{
		const double v_low = ladder_v[2 * octave];
		const double v_high = ladder_v[2 * octave + 1];
		const double trimmer = m_octave_trimmers[octave]->read() / 100.0;
		m_octave_scalers[octave] = v_low + trimmer * (v_high - v_low);
	}
	m_octave_scalers[4] = 1.0;
	update_active_cv();

	LOGMASKED(LOG_CALIBRATION, "Octave calibration updated: ");
	for (int i = 0; i < m_octave_scalers.size(); ++i)
		LOGMASKED(LOG_CALIBRATION, "%d: %f  ", i, m_octave_scalers[i]);
	LOGMASKED(LOG_CALIBRATION, "\n");
}

void midi2cv8_state::program_map(address_map &map)
{
	// A13-A15 are not connected.
	map(0x0000, 0x1fff).mirror(0xe000).rom();
}

void midi2cv8_state::external_memory_map(address_map &map)
{
	// Address lines ignored on external memory writes.
	map(0x0000, 0x0000).mirror(0xffff).w(FUNC(midi2cv8_state::dac_w));
}

void midi2cv8_state::machine_start()
{
	m_cv_display_integer.resolve();
	m_cv_display_fractional.resolve();

	save_item(NAME(m_inhibit_output_mux));
	save_item(NAME(m_selected_output_mux));
	save_item(NAME(m_dac_value));
	save_item(NAME(m_dac_i_fs));
	save_item(NAME(m_midi_rxd_bit));
	save_item(NAME(m_cv));
}

void midi2cv8_vhz_state::machine_start()
{
	midi2cv8_state::machine_start();
	save_item(NAME(m_octave_scalers));
	save_item(NAME(m_selected_octave_mux));
}

void midi2cv8_state::machine_reset()
{
	update_dac_reference();
}

void midi2cv8_vhz_state::machine_reset()
{
	midi2cv8_state::machine_reset();
	update_octave_calibration();
}

void midi2cv8_state::midi2cv8(machine_config &config)
{
	I80C31(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &midi2cv8_state::program_map);
	m_maincpu->set_addrmap(AS_IO, &midi2cv8_state::external_memory_map);

	m_maincpu->port_in_cb<1>().set_ioport("dsw").mask(0x1f);  // P1.0-P1.4
	m_maincpu->port_in_cb<1>().append(FUNC(midi2cv8_state::is_volts_per_hz_r)).lshift(7).mask(0x80);  // P1.7

	m_maincpu->port_in_cb<3>().set(FUNC(midi2cv8_state::midi_rxd_r)).mask(0x01);  // P3.0
	m_maincpu->port_in_cb<3>().append_ioport("dsw").mask(0x20);  // P3.5 <- DSW BIT 5
	m_maincpu->port_in_cb<3>().append_ioport("dsw").lshift(1).mask(0x80);  //  P3.7 <- DSW BIT 6
	m_maincpu->port_out_cb<3>().set(FUNC(midi2cv8_state::output_mux_select_w));

	midi_port_device &midi_in(MIDI_PORT(config, "mdin", midiin_slot, "midiin"));
	MIDI_PORT(config, "mdthru", midiout_slot, "midiout");
	midi_in.rxd_handler().set(FUNC(midi2cv8_state::midi_rxd_w));
	midi_in.rxd_handler().append("mdthru", FUNC(midi_port_device::write_txd));

	PWM_DISPLAY(config, m_midi_pwm_led).set_size(1, 1);
	m_midi_pwm_led->output_x().set_output("midi_led");
	// These values make the MIDI LED in the default layout functional,
	// without being too annoying.
	m_midi_pwm_led->set_interpolation(0.2);
	m_midi_pwm_led->set_bri_levels(0.0001);
	m_midi_pwm_led->set_refresh(attotime::from_hz(30));

	config.set_default_layout(layout_paia_midi2cv8);
}

void midi2cv8_vhz_state::midi2cv8_vhz(machine_config &config)
{
	midi2cv8(config);
	get_maincpu().port_out_cb<1>().set(FUNC(midi2cv8_vhz_state::octave_mux_select_w));
}

DECLARE_INPUT_CHANGED_MEMBER(midi2cv8_state::dac_trimmer_changed)
{
	update_dac_reference();
}

DECLARE_INPUT_CHANGED_MEMBER(midi2cv8_vhz_state::octave_trimmer_changed)
{
	update_octave_calibration();
}

INPUT_PORTS_START(midi2cv8)
	PORT_START("dsw")
	PORT_DIPNAME(0x0f, 0x00, "MIDI Channel") PORT_DIPLOCATION("SW1:1,2,3,4")
	PORT_DIPSETTING(   0x00, "1")
	PORT_DIPSETTING(   0x01, "2")
	PORT_DIPSETTING(   0x02, "3")
	PORT_DIPSETTING(   0x03, "4")
	PORT_DIPSETTING(   0x04, "5")
	PORT_DIPSETTING(   0x05, "6")
	PORT_DIPSETTING(   0x06, "7")
	PORT_DIPSETTING(   0x07, "8")
	PORT_DIPSETTING(   0x08, "9")
	PORT_DIPSETTING(   0x09, "10")
	PORT_DIPSETTING(   0x0a, "11")
	PORT_DIPSETTING(   0x0b, "12")
	PORT_DIPSETTING(   0x0c, "13")
	PORT_DIPSETTING(   0x0d, "14")
	PORT_DIPSETTING(   0x0e, "15")
	PORT_DIPSETTING(   0x0f, "16")
	PORT_DIPNAME(0x70, 0x70, "Mode") PORT_DIPLOCATION("SW1:5,6,7")
	PORT_DIPSETTING(   0x00, "Mode 1 - 1 voice")
	PORT_DIPSETTING(   0x10, "Mode 2 - 2 voice")
	PORT_DIPSETTING(   0x20, "Mode 3 - 4 voice")
	PORT_DIPSETTING(   0x30, "Mode 4 - control change")
	PORT_DIPSETTING(   0x40, "Mode 5 - analog drum")
	PORT_DIPSETTING(   0x50, "Mode 6 - din sync")
	PORT_DIPSETTING(   0x60, "Mode 7 - [unused]")
	PORT_DIPSETTING(   0x70, "Mode 8 - self-tests")
	PORT_DIPNAME(0x80, 0x00, "Not Connected") PORT_DIPLOCATION("SW1:8")
	PORT_DIPSETTING(   0x00, DEF_STR(On))
	PORT_DIPSETTING(   0x80, DEF_STR(Off))

	PORT_START("dac_trimmer")
	PORT_ADJUSTER(50, "DAC tune") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(midi2cv8_state::dac_trimmer_changed), 0)
INPUT_PORTS_END

INPUT_PORTS_START(midi2cv8_vhz)
	PORT_INCLUDE(midi2cv8)

	// All octave trimmers are 1K potentiometers.
	// Default values are for a correct tuning relative to octave 5, with a
	// small error due to adjuster resolution.
	PORT_START("octave_1_trimmer")
	PORT_ADJUSTER(72, "Octave 1 trimmer (R20)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(midi2cv8_vhz_state::octave_trimmer_changed), 0)
	PORT_START("octave_2_trimmer")
	PORT_ADJUSTER(65, "Octave 2 trimmer (R14)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(midi2cv8_vhz_state::octave_trimmer_changed), 0)
	PORT_START("octave_3_trimmer")
	PORT_ADJUSTER(56, "Octave 3 trimmer (R11)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(midi2cv8_vhz_state::octave_trimmer_changed), 0)
	PORT_START("octave_4_trimmer")
	PORT_ADJUSTER(48, "Octave 4 trimmer (R8)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(midi2cv8_vhz_state::octave_trimmer_changed), 0)
INPUT_PORTS_END

#define ROMS_MIDI2CV8 \
	ROM_REGION(0x2000, MAINCPU_TAG, 0) \
	ROM_DEFAULT_BIOS("v201") \
	ROM_SYSTEM_BIOS(0, "v201", "v2.01 - December 1997") \
	ROMX_LOAD("midi2cv_v2.01.ic2", 0x000000, 0x002000, CRC(bae8c045) SHA1(a5db57e53831b73903a0fb171e0444e6956febc3), ROM_BIOS(0))

ROM_START(midi2cv8)
	ROMS_MIDI2CV8
ROM_END

ROM_START(midi2cv8_vhz)
	ROMS_MIDI2CV8
ROM_END

} // anonymous namespace

SYST(1997, midi2cv8, 0, 0, midi2cv8, midi2cv8, midi2cv8_state, empty_init, "PAiA Electronics", "midi2cv8", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE)
SYST(1997, midi2cv8_vhz, 0, 0, midi2cv8_vhz, midi2cv8_vhz, midi2cv8_vhz_state, empty_init, "PAiA Electronics", "midi2cv8 V/Hz", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE)



midiverb.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:m1macrophage

/*
The MIDIverb is a digital delay & reverb unit.

The computer portion of the device is very simple. The firmware runs on a
80C31 microcontroller. It reads the 4 buttons, drives the two 7-segment
displays, and listens to MIDI for program changes. It also controls which
program (effect) is running on the DSP.

The UI is very simple. The user can choose one of 63 effects by using the
"up" and "down" buttons on the unit. The effect can also be set via MIDI
program changes, and the MIDI channel is configurable ("channel" button). The
"defeat" button will run the 64th effect, which is just silence. That same
silence program is also enabled temporarily when switching between effects.
Finally, there is a wet/dry control knob. For more information on the audio
hardware, see midiverb_state::configure_audio().

An interesting aspect of the MIDIverb is its DSP, which is built out of discrete
logic components and runs custom microcode. Each microcode instruction consists
of a 2-bit opcode and 14-bit RAM delta offset. The effects program makes up the
top 6 bits of the microcode ROM address, and the DSP just loops over the 128
instructions of each program. There are also pre-determined program counter
addresses where specific functions are performed (e.g. ADC, DAC). For more info,
see midiverb_dsp::sound_stream_update().

This driver is based on https://www.youtube.com/watch?v=JNPpU08YZjk
and https://www.youtube.com/watch?v=5DYbirWuBaU, and is intended as an
educational tool.

Usage notes:

The driver comes with an interactive layout.

MIDI is optional, and can be configured as follows:
./mame -listmidi  # List MIDI devices, physical or virtual (e.g. DAWs).
./mame -window midiverb -midiin "{midi device}"

Audio inputs are emulated using MAME's audio input capabilities.

- Select your input through the audio mixer menu.  You can adjust level there too.

- Use the "DRY/WET MIX" Slider Control to adjust the wet/dry ratio.
*/

#include "emu.h"

#include "cpu/mcs51/mcs51.h"
#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "machine/rescap.h"
#include "sound/flt_biquad.h"
#include "sound/flt_rc.h"
#include "sound/mixer.h"
#include "video/pwm.h"
#include "speaker.h"

#include "alesis_midiverb.lh"

#define LOG_PROGRAM_CHANGE (1U << 1)
#define LOG_DSP_EXECUTION  (1U << 2)

#define VERBOSE (LOG_GENERAL)
//#define LOG_OUTPUT_FUNC osd_printf_info

#include "logmacro.h"

// Emulation of the MIDIverb DSP circuit, built out of discrete logic
// components.
class midiverb_dsp_device : public device_t, public device_sound_interface
{
public:
	midiverb_dsp_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0) ATTR_COLD;

	void program_select_w(u8 data);

protected:
	void device_start() override ATTR_COLD;
	void sound_stream_update(sound_stream &stream) override;

private:
	u16 analog_to_digital(float sample) const;
	float digital_to_analog(u16 sample) const;

	required_memory_region m_microcode;
	sound_stream *m_stream = nullptr;

	// State
	u8 m_program = 0;
	u16 m_accum = 0;
	u16 m_reg = 0;
	u16 m_ram_offset = 0;
	std::vector<u16> m_ram;  // 4 x TMS4416-15 (16K x 4bit). U14, U19, U22, U25.

	static constexpr const int CLOCKS_PER_INSTRUCTION = 2;
	static constexpr const int INSTRUCTIONS_PER_SAMPLE = 128;
	static constexpr const float DAC_MAX_V = 4.8F;
};

DEFINE_DEVICE_TYPE(MIDIVERB_DSP, midiverb_dsp_device, "midiverb_dsp", "MIDIverb discrete DSP");

midiverb_dsp_device::midiverb_dsp_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, MIDIVERB_DSP, tag, owner, clock)
	, device_sound_interface(mconfig, *this)
	, m_microcode(*this, ":dsp_microcode")
	, m_ram(0x4000, 0)  // 16K
{
}

void midiverb_dsp_device::program_select_w(u8 data)
{
	const u8 new_program = data & 0x3f;
	if (m_program == new_program)
		return;

	m_stream->update();
	m_program = new_program;
	LOGMASKED(LOG_PROGRAM_CHANGE, "DSP: Program changed to: %d\n", m_program);
}

void midiverb_dsp_device::device_start()
{
	// The actual sample rate works out to 23,437.5 KHz. But stream_alloc takes
	// a u32, and .value() will round it down to 23,437 KHz.
	const XTAL sample_clock = 6_MHz_XTAL / CLOCKS_PER_INSTRUCTION / INSTRUCTIONS_PER_SAMPLE;
	m_stream = stream_alloc(1, 2, sample_clock.value());

	save_item(NAME(m_program));
	save_item(NAME(m_accum));
	save_item(NAME(m_reg));
	save_item(NAME(m_ram_offset));
	save_item(NAME(m_ram));
}

#define LOG_DSP(...) \
		do { \
			if (sample_i < DEBUG_SAMPLES) \
				LOGMASKED(LOG_DSP_EXECUTION, __VA_ARGS__); \
		} while(0)

void midiverb_dsp_device::sound_stream_update(sound_stream &stream)
{
	static constexpr const u8 MAX_PC = 0x7f;
	static constexpr const int DEBUG_SAMPLES = 2;
	static constexpr const char* const OP_NAME[4] =
	{ "ADDHF", "LDHF ", "STPOS", "STNEG" };

	const int n = stream.samples();
	const u16 rom_base = u16(m_program) << 8;

	for (int sample_i = 0; sample_i < n; ++sample_i)
	{
		u16 rom_address = rom_base + 2 * (MAX_PC - 1);
		for (u8 pc = 0; pc <= MAX_PC; ++pc)
		{
			// Each microcode instruction consists of two bytes. A 2-bit opcode
			// and a 14-bit RAM offset. Microcode execution is pipelined:
			// - The RAM offset in each instruction gets added to the current
			//   RAM address *after* the execution of the instruction, so it
			//   affects the RAM access of the next instruction.
			// - The 2-bit opcode being executed comes from the *previous*
			//   instruction in ROM.
			// - The "program counter" (whose value is used for some control
			//   signals, see below) leads the instruction currently being
			//   executed.

			const u8 op = m_microcode->as_u8(rom_address + 1) >> 6;
			rom_address = rom_base + 2 * ((pc + MAX_PC) & MAX_PC);
			const u8 ram_row = m_microcode->as_u8(rom_address);
			const u8 ram_col = m_microcode->as_u8(rom_address + 1) & 0x3f;
			const u16 ram_offset_delta = (u16(ram_col) << 8) | ram_row;

			// Control signals. Names match those in:
			// https://www.youtube.com/watch?v=JNPpU08YZjk.
			const bool mode_rc0 = (pc == 0);  // Read from ADC.
			const bool ld_dac = (pc == 0x60 || pc == 0x70);  // Write to DAC.
			const bool dac_left = (pc == 0x70);  // Route DAC to left (instead of right) channel.
			const bool ld_dsp = !mode_rc0 && !ld_dac;  // Write to register.
			const bool clear_acc = BIT(op, 0);  // Clear the accumulator.
			const bool rd_r0 = (op == 0x02);  // Place register contents on the bus.
			const bool rd_r1 = (op == 0x03);  // Place negated register contents on the bus.
			const bool dram_w = BIT(op, 1) || mode_rc0;  // Write (instead of read) RAM.

			u16 bus_value = 0;
			int num_bus_writes = 0;
			if (mode_rc0)
			{
				bus_value = analog_to_digital(stream.get(0, sample_i));
				++num_bus_writes;
			}
			if (rd_r0)
			{
				bus_value = m_reg;
				++num_bus_writes;
			}
			if (rd_r1)
			{
				bus_value = ~m_reg;
				++num_bus_writes;
			}
			if (!dram_w)
			{
				bus_value = m_ram[m_ram_offset];
				++num_bus_writes;
			}
			if (num_bus_writes == 0)
				LOG("DSP microcode error: floating bus\n");
			else if (num_bus_writes > 1)
				LOG("DSP microcode error: bus conflict %d %d %d %d\n", mode_rc0, rd_r0, rd_r1, !dram_w);

			if (dram_w)
				m_ram[m_ram_offset] = bus_value;

			if (ld_dac)
			{
				if (dac_left)
					stream.put(0, sample_i, digital_to_analog(bus_value));
				else
					stream.put(1, sample_i, digital_to_analog(bus_value));
			}

			if (clear_acc)
				m_accum = 0;

			if (ld_dsp)
			{
				const u16 sign = BIT(bus_value, 15);
				m_accum += ((sign << 15) | (bus_value >> 1)) + sign;
				m_reg = m_accum;
			}

			LOG_DSP("%04X %02x - DSP OP: %d %s (%04x), A: %6d, R: %6d, bus: %6d, ram: %6d @ %04x",
					rom_address, pc, op, OP_NAME[op], ram_offset_delta, m_accum,
					m_reg, bus_value, m_ram[m_ram_offset], m_ram_offset);
			if (mode_rc0)
				LOG_DSP(" [ADC]");
			if (ld_dac)
			{
				if (dac_left)
					LOG_DSP(" [DAC LEFT]");
				else
					LOG_DSP(" [DAC RIGHT]");
			}
			LOG_DSP("\n");

			m_ram_offset = (m_ram_offset + ram_offset_delta) & (m_ram.size() - 1);
		}
		LOG_DSP("\n");
	}
	LOGMASKED(LOG_DSP_EXECUTION, "\n");
}

u16 midiverb_dsp_device::analog_to_digital(float sample) const
{
	// Analog-to-digital conversion is done with a 12-bit DAC+SAR.
	// Note that samples in the stream are treated as voltages (see
	// configure_audio()). Convert the voltage to the range: -/+1.
	const float transformed = std::clamp(sample, -DAC_MAX_V, DAC_MAX_V) / DAC_MAX_V;

	// Quantize to 12 bits, keeping in mind that the range is -1 - 1 (reason
	// for "/ 2"). Then convert to 13 bits ("* 2"). Bit 0 is always set ("+ 1).
	const s16 quantized = floorf(transformed * ((1 << 12) / 2 - 1)) * 2 + 1;
	assert(quantized > -4096 && quantized < 4096);

	return static_cast<u16>(quantized);
}

float midiverb_dsp_device::digital_to_analog(u16 sample) const
{
	// Digital-to-analog conversion uses the 12-bit DAC and 1 extra bit
	// (LSB), for a total of 13 bits. The extra bit is implemented by
	// conditionally injecting extra current into the current-to-voltage
	// converter that follows the DAC. There is also saturation logic to detect
	// overflows and underflows, and set the DAC to the max or min value
	// respectively.
	const s16 saturated = std::clamp<s16>(static_cast<s16>(sample), -4095, 4095);
	return DAC_MAX_V * float(saturated) / ((1 << 13) / 2 - 1);
}

namespace {

constexpr const char MAINCPU_TAG[] = "80c31";

class midiverb_state : public driver_device
{
public:
	midiverb_state(const machine_config &mconfig, device_type type, const char *tag) ATTR_COLD
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, MAINCPU_TAG)
		, m_digit_device(*this, "pwm_digit_device")
		, m_digit_out(*this, "digit_%d", 1U)
		, m_mix(*this, "mix")
		, m_audio_in(*this, "audio_input")
		, m_dsp(*this, "discrete_dsp")
		, m_left_out(*this, "left_mixer_out")
		, m_right_out(*this, "right_mixer_out")
	{
	}

	void midiverb(machine_config &config) ATTR_COLD;

	DECLARE_INPUT_CHANGED_MEMBER(mix_changed);

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

private:
	u8 midi_rxd_r() const;
	void midi_rxd_w(int state);
	void digit_select_w(u8 data);
	void digit_latch_w(u8 data);
	void digit_out_update_w(offs_t offset, u8 data);

	void update_mix();

	void program_map(address_map &map) ATTR_COLD;
	void external_memory_map(address_map &map) ATTR_COLD;
	void configure_audio(machine_config &config) ATTR_COLD;

	required_device<mcs51_cpu_device> m_maincpu;
	required_device<pwm_display_device> m_digit_device;
	output_finder<2> m_digit_out;  // 2 x MAN4710A (7-seg display), DS1 & DS2.
	required_ioport m_mix;
	required_device<microphone_device> m_audio_in;
	required_device<midiverb_dsp_device> m_dsp;
	required_device<mixer_device> m_left_out;
	required_device<mixer_device> m_right_out;

	bool m_midi_rxd_bit = true; // Start high for serial idle.

	enum
	{
		LEFT_CHANNEL = 0,
		RIGHT_CHANNEL
	};
};

u8 midiverb_state::midi_rxd_r() const
{
	return m_midi_rxd_bit ? 1 : 0;
}

void midiverb_state::midi_rxd_w(int state)
{
	m_midi_rxd_bit = state;
}

void midiverb_state::digit_select_w(u8 data)
{
	// The digit select signals (bit 0 and 1) are active-low. They connect to
	// the base of PNP transistors (2N4403, Q4 and Q3 for DS1 and DS2
	// respectively). When low, power is connected to the MAN4710 anode inputs.
	m_digit_device->write_my(~data & 0x03);
}

void midiverb_state::digit_latch_w(u8 data)
{
	// The Data bus is connected to the latch in an unintuitive way. Same goes
	// for the connections from the latch to the 7seg display. Presumably done
	// to save board space, which was limited.
	const u8 descrambled = bitswap<7>(data, 1, 6, 7, 4, 0, 2, 5);

	// Inverting because segment LEDs are active-low, but pwm_display_device
	// expects active-high.
	m_digit_device->write_mx(~descrambled & 0x7f);
}

void midiverb_state::digit_out_update_w(offs_t offset, u8 data)
{
	// Digits are ordered from left to right. So offset 0 (corresponding to
	// digit_1) is the most significant digit.
	m_digit_out[offset] = data;
}

void midiverb_state::update_mix()
{
	const float wet = m_mix->read() / 100.0F;
	const float dry = 1.0F - wet;

	m_left_out->set_input_gain(0, dry);
	m_left_out->set_input_gain(1, wet);

	m_right_out->set_input_gain(0, dry);
	m_right_out->set_input_gain(1, wet);
}

void midiverb_state::program_map(address_map &map)
{
	// 2764 ROM has A0-A11 connected to the MCU, and A12 tied high. ROM /OE
	// is tied to MCU A15, so it is only active when A15 is 0.
	map(0x0000, 0x0fff).mirror(0x7000).rom().region(MAINCPU_TAG, 0x1000);
}

void midiverb_state::external_memory_map(address_map &map)
{
	// Address lines ignored when writing to external memory.
	map(0x0000, 0x0000).mirror(0xffff).w(FUNC(midiverb_state::digit_latch_w));
}

void midiverb_state::configure_audio(machine_config &config)
{
	static constexpr const double SK_R3 = RES_M(999.99);
	static constexpr const double SK_R4 = RES_R(0.001);

	// Audio input.
	MICROPHONE(config, m_audio_in, 2).front();

	// According to the user manual, input levels can be up to +6 dBV peak when
	// a single input is connected, or 0 dBV when both are connected. 0 dBV
	// means the input voltage can peak at +/- 1.414V. The microphone device
	// returns samples in the range +/- 1. So we can just treat those as
	// voltages.

	// Each input goes through a highpass RC filter (~31Hz cutoff).
	filter_rc_device &left_rc_in = FILTER_RC(config, "left_rc_in");
	filter_rc_device &right_rc_in = FILTER_RC(config, "right_rc_in");
	left_rc_in.set_rc(filter_rc_device::HIGHPASS, RES_K(51), 0, 0, CAP_U(0.1));
	right_rc_in.set_rc(filter_rc_device::HIGHPASS, RES_K(51), 0, 0, CAP_U(0.1));
	m_audio_in->add_route(LEFT_CHANNEL, left_rc_in, 1.0);
	m_audio_in->add_route(RIGHT_CHANNEL, right_rc_in, 1.0);

	// Following the RC HPF, the signal is scaled using an opamp in the
	// non-inverting amplifier configuration.
	// Using a MIXER for this as a convenient way to apply gain. This stage is
	// not really a mixer.
	mixer_device &left_amp_in = MIXER(config, "left_amp_in");
	mixer_device &right_amp_in = MIXER(config, "right_amp_in");
	const double input_gain = 1 + RES_K(10) / RES_K(2.4);  // ~5.17x
	left_rc_in.add_route(0, left_amp_in, input_gain);
	right_rc_in.add_route(0, right_amp_in, input_gain);

	// The two channels are mixed in equal proportions to form a mono signal.
	mixer_device &lrmix = MIXER(config, "lrmix");
	left_amp_in.add_route(0, lrmix, 0.5);  // LEFT_CHANNEL.
	right_amp_in.add_route(0, lrmix, 0.5);  // RIGHT_CHANNEL.

	// The mono signal passes through a cascade of 3 Sallen-Key filters, which
	// combined form a 6-pole lowpass filter with a cutoff of ~12 Khz and a
	// boost peaking at 9.7 KHz ("preemphasis").

	// 2-pole LPF, ~6.5 Khz cutoff, flat.
	filter_biquad_device &sk_in1 = FILTER_BIQUAD(config, "sk_in1");
	const double sk_in1_r1 = RES_2_PARALLEL(RES_K(10), RES_K(10));
	sk_in1.opamp_sk_lowpass_setup(sk_in1_r1, RES_K(5.1), SK_R3, SK_R4, CAP_U(0.01), CAP_P(3300));
	lrmix.add_route(0, sk_in1, 1.0);

	// 2-pole LPF, ~13 KHz cutoff, 9-10 dB peak at ~8.4 KHz.
	filter_biquad_device &sk_in2 = FILTER_BIQUAD(config, "sk_in2");
	sk_in2.opamp_sk_lowpass_setup(RES_K(10), RES_K(10), SK_R3, SK_R4, CAP_U(0.01), CAP_P(330));
	sk_in1.add_route(0, sk_in2, 1.0);

	// 2-pole LPF, 16 KHz cutoff, 18 dB peak at ~10.5 KHz nominal.
	// NOTE: This is not a pure SK filter. It seems to be combined with a soft-
	// clipping (?) circuit. The circuit's behavior is not emulated.
	filter_biquad_device &sk_in3 = FILTER_BIQUAD(config, "sk_in3");
	sk_in3.opamp_sk_lowpass_setup(RES_K(4.7), RES_K(4.7), SK_R3, SK_R4, CAP_U(0.047), CAP_P(220));
	sk_in2.add_route(0, sk_in3, 1.0);

	// Next stage is the analog-to-digital conversion. This is done by using a
	// DAC (AM6012) together with a SAR (AM25L04 successive approximation
	// register) and a comparator. This setup does a binary search for the
	// correct digital representation. Since audio in MAME is digital, this
	// process is simplified (no need for a binary search). It is implemented
	// within the DSP.

	// The DSP will read the digital sample, process it, and output left and
	// right samples, using the DAC.
	// A mixer is added here to force a resampling, so that the DSP's low sample
	// rate does not propagate back to the SK filters.
	mixer_device &resampler = MIXER(config, "pre_dsp_resample");
	MIDIVERB_DSP(config, m_dsp);
	sk_in3.add_route(0, resampler, 1.0);
	resampler.add_route(0, m_dsp, 1.0);

	// The left and right DSP outputs are processed by a low-pass reconstruction
	// filter.

	// NOTE: While there is an RC filter followed by an SK filter, there is no
	// buffer between them. Therefore, it might not be very accurate to treat
	// them as distinct filters, but probably a decent approximation. The
	// alternative is to derive and implement a custom filter.

	// NOTE: The cutoff frequency of the RC filter is very high (~106 KHz).
	// Including it here might, in theory, decrease audio accuracy ("frequency
	// warping"), unless MAME is running with a very high sample rate.

	// LPF, 1-pole, ~106 KHz cutoff.
	filter_rc_device &left_rc_out = FILTER_RC(config, "left_rc_out");
	filter_rc_device &right_rc_out = FILTER_RC(config, "right_rc_out");
	left_rc_out.set_lowpass(RES_K(1), CAP_P(1500));
	right_rc_out.set_lowpass(RES_K(1), CAP_P(1500));
	m_dsp->add_route(LEFT_CHANNEL, left_rc_out, 1.0);
	m_dsp->add_route(RIGHT_CHANNEL, right_rc_out, 1.0);

	// LPF, 2-pole, ~11 KHz cutoff.
	filter_biquad_device &left_sk_out = FILTER_BIQUAD(config, "left_sk_out");
	filter_biquad_device &right_sk_out = FILTER_BIQUAD(config, "right_sk_out");
	left_sk_out.opamp_sk_lowpass_setup(RES_K(6.8), RES_K(6.8), SK_R3, SK_R4, CAP_P(3300), CAP_P(1500));
	right_sk_out.opamp_sk_lowpass_setup(RES_K(6.8), RES_K(6.8), SK_R3, SK_R4, CAP_P(3300), CAP_P(1500));
	left_rc_out.add_route(0, left_sk_out, 1.0);
	right_rc_out.add_route(0, right_sk_out, 1.0);

	// After reconstruction, each processed (wet) channel is mixed with the
	// corresponding original (dry) channel, based on the position of a
	// user-accessible, dual-gang potentiometer.
	MIXER(config, m_left_out);
	left_amp_in.add_route(0, m_left_out, 1.0, 0);
	left_sk_out.add_route(0, m_left_out, 1.0, 1);
	MIXER(config, m_right_out);
	right_amp_in.add_route(0, m_right_out, 1.0, 0);
	right_sk_out.add_route(0, m_right_out, 1.0, 1);

	// Finally, the signals are attenuated to line level, undoing the ~5x
	// amplification at the input.
	SPEAKER(config, "speaker", 2).front();
	const double output_gain = RES_VOLTAGE_DIVIDER(RES_K(2.4), RES_R(500));
	m_left_out->add_route(ALL_OUTPUTS, "speaker", output_gain, 0);
	m_right_out->add_route(ALL_OUTPUTS, "speaker", output_gain, 1);
}

void midiverb_state::machine_start()
{
	m_digit_out.resolve();
	save_item(NAME(m_midi_rxd_bit));
}

void midiverb_state::machine_reset()
{
	update_mix();
}

void midiverb_state::midiverb(machine_config &config)
{
	I80C31(config, m_maincpu, 6_MHz_XTAL);  // U55.
	m_maincpu->set_addrmap(AS_PROGRAM, &midiverb_state::program_map);
	m_maincpu->set_addrmap(AS_IO, &midiverb_state::external_memory_map);

	m_maincpu->port_out_cb<1>().set(FUNC(midiverb_state::digit_select_w)).mask(0x03);  // P1.0-P1.1
	m_maincpu->port_out_cb<1>().append(m_dsp, FUNC(midiverb_dsp_device::program_select_w)).rshift(2);  // P1.2-P1.7
	m_maincpu->port_in_cb<3>().set(FUNC(midiverb_state::midi_rxd_r)).mask(0x01);  // P3.0
	m_maincpu->port_in_cb<3>().append_ioport("buttons").lshift(2).mask(0x3c);  // P3.2-P3.5

	midi_port_device &midi_in = MIDI_PORT(config, "mdin", midiin_slot, "midiin");
	MIDI_PORT(config, "mdthru", midiout_slot, "midiout");
	midi_in.rxd_handler().set(FUNC(midiverb_state::midi_rxd_w));
	midi_in.rxd_handler().append("mdthru", FUNC(midi_port_device::write_txd));

	PWM_DISPLAY(config, m_digit_device).set_size(2, 7);  // 2 x MAN4710.
	m_digit_device->set_segmask(0x03, 0x7f);
	m_digit_device->output_digit().set(FUNC(midiverb_state::digit_out_update_w));

	config.set_default_layout(layout_alesis_midiverb);

	configure_audio(config);
}

DECLARE_INPUT_CHANGED_MEMBER(midiverb_state::mix_changed)
{
	update_mix();
}

INPUT_PORTS_START(midiverb)
	PORT_START("buttons")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("MIDI CHANNEL") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("UP") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("DOWN") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("DEFEAT") PORT_CODE(KEYCODE_D)

	PORT_START("mix")  // MIX potentiometer at the back of the unit.
	PORT_ADJUSTER(100, "DRY/WET MIX")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(midiverb_state::mix_changed), 0)
INPUT_PORTS_END

ROM_START(midiverb)
	ROM_REGION(0x2000, MAINCPU_TAG, 0)  // U54. 2764 ROM.
	ROM_LOAD("mvop_4-7-86.u54", 0x000000, 0x002000, CRC(14d6596d) SHA1(c6dc579d8086556b2dd4909c8deb3c7006293816))

	ROM_REGION(0x4000, "dsp_microcode", 0)  // U51, 27256, 32K ROM, but only 16K used.
	ROM_LOAD("mvobj_2-6-86.u51", 0x000000, 0x004000, CRC(1aa25250) SHA1(ebd7ca265540c3420f4259d0eaefecaf6d95a1bf))
	ROM_CONTINUE(0x000000, 0x004000)  // A14 (pin 27) tied to VCC. Only upper half used.
	// Seems to have also shipped with a 27128 ROM (16K), with the same
	// "MVOBJ 2-6-86" label.
ROM_END

}  // anonymous namespace

SYST(1986, midiverb, 0, 0, midiverb, midiverb, midiverb_state, empty_init, "Alesis", "MIDIverb", MACHINE_SUPPORTS_SAVE)



mightyframe.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************************

2017-11-03 Skeleton

Convergent Technologies 68k Mightyframe S80

Chips: CPU is a square MC68020RC12B. Also, 3x Z80SCC and 2 undumped proms labelled "7201087B"(@15D) and "7201089B"(@15F)
       The only photo shows just a part of the board, the only crystal is a tiny tubular one next to a OKI M58321 chip.

Manuals: http://mightyframe.blogspot.com.au/p/manuals.html

************************************************************************************************************************************/

#include "emu.h"
#include "cpu/m68000/m68000.h"


namespace {

class mightyframe_state : public driver_device
{
public:
	mightyframe_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		//      , m_maincpu(*this, "maincpu")
	{ }

	void mightyframe(machine_config &config);
private:
	void mem_map(address_map &map) ATTR_COLD;
	//  required_device<cpu_device> m_maincpu;
};

void mightyframe_state::mem_map(address_map &map)
{
	map(0x000000, 0x007fff).rom();
}

static INPUT_PORTS_START( mightyframe )
INPUT_PORTS_END

void mightyframe_state::mightyframe(machine_config &config)
{
	m68000_device &maincpu(M68000(config, "maincpu", XTAL(16'000'000))); // no idea of clock
	maincpu.set_addrmap(AS_PROGRAM, &mightyframe_state::mem_map);
}

ROM_START( mightyframe )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD( "72-01231.26c", 0x0000, 0x8000, CRC(41faf884) SHA1(d0c6f35394b4006bbe9a3f81b658ded37f41d86f) )
ROM_END

} // anonymous namespace


COMP( 1985?, mightyframe, 0, 0, mightyframe, mightyframe, mightyframe_state, empty_init, "Convergent Technologies", "Mightyframe", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



mikro80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/*****************************************************************************************

MIKRO80 driver by Miodrag Milanovic

2008-03-10 Preliminary driver.


Cassette:
* Mikro80: loads software items, but not its own saves
* Radio99: can load its own saves; can load radio86 tapes, can load ut88 tapes.
* Kristall2: can load its own saves; can load radio86 tapes; can load radio99 tapes.


ToDo:
- Cassette - need schematic of CMT.

*****************************************************************************************/


#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "formats/rk_cas.h"
#include "mikro80.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

/* Address maps */
void mikro80_state::mikro80_mem(address_map &map)
{
	map(0x0000, 0xdfff).ram().share("mainram");
	map(0xe000, 0xe7ff).ram().share("attrram");
	map(0xe800, 0xefff).ram().share("videoram");
	map(0xf000, 0xf7ff).ram();
	map(0xf800, 0xffff).rom().region("maincpu",0);
}

void mikro80_state::mikro80_io(address_map &map)
{
	map.unmap_value_high();
	map(0x01, 0x01).rw(FUNC(mikro80_state::tape_r), FUNC(mikro80_state::tape_w));
	map(0x04, 0x07).lr8(NAME([this] (offs_t offset) { return m_ppi->read(offset^3); })).lw8(NAME([this] (offs_t offset, u8 data) { m_ppi->write(offset^3, data); }));
}

void mikro80_state::kristall_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x03).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x02, 0x02).w(FUNC(mikro80_state::portc_w));
	// map(0x20, 0x23).  init byte 8B, so possibly another ppi with reversed offset like mikro80
}

void mikro80_state::radio99_io(address_map &map)
{
	map.unmap_value_high();
	map(0x01, 0x01).rw(FUNC(mikro80_state::tape_r), FUNC(mikro80_state::tape_w));
	// no init byte, so ppi has been replaced by ordinary latches
	map(0x04, 0x04).w(FUNC(mikro80_state::sound_w));
	//map(0x05, 0x05).rw(FUNC(mikro80_state::portc_r), FUNC(mikro80_state::portc_w));
	map(0x05, 0x05).r(FUNC(mikro80_state::portc_r)).nopw();
	map(0x06, 0x06).r(FUNC(mikro80_state::portb_r));
	map(0x07, 0x07).w(FUNC(mikro80_state::porta_w));
}

/* Input ports */
static INPUT_PORTS_START( mikro80 )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('@')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('~')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("<>") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('_')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Rus/Lat") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_mikro80 )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END

u32 mikro80_state::screen_update_mikro80(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 sy=0,ma=0;

	for (u8 y = 0; y < 32; y++)
	{
		for (u8 ra = 0; ra < 8; ra++)
		{
			u16 *p = &bitmap.pix(sy++);

			for (u16 x = ma; x < ma + 64; x++)
			{
				bool attr = BIT(m_aram[x+1], 7);
				u8 chr = m_vram[x];
				u8 gfx = m_p_chargen[(chr<<3) | ra ] ^ (attr ? 0xff : 0);

				/* Display a scanline of a character */
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma+=64;
	}
	return 0;
}


void mikro80_state::mikro80(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, 2000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &mikro80_state::mikro80_mem);
	m_maincpu->set_addrmap(AS_IO, &mikro80_state::mikro80_io);

	I8255(config, m_ppi);
	m_ppi->out_pa_callback().set(FUNC(mikro80_state::porta_w));
	m_ppi->in_pb_callback().set(FUNC(mikro80_state::portb_r));
	m_ppi->in_pc_callback().set(FUNC(mikro80_state::portc_r));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(64*8, 32*8);
	screen.set_visarea(0, 64*8-1, 0, 32*8-1);
	screen.set_screen_update(FUNC(mikro80_state::screen_update_mikro80));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_mikro80);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	SPEAKER(config, "speaker").front_center();

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(rk8_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "speaker", 0.05);
	m_cassette->set_interface("mikro80_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("mikro80");
}

void mikro80_state::radio99(machine_config &config)
{
	mikro80(config);

	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_IO, &mikro80_state::radio99_io);

	DAC_1BIT(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.50);
}

void mikro80_state::kristall(machine_config &config)
{
	mikro80(config);
	m_maincpu->set_addrmap(AS_IO, &mikro80_state::kristall_io);
	m_ppi->in_pc_callback().set(FUNC(mikro80_state::kristall2_portc_r));
}


/* ROM definition */

ROM_START( mikro80 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mikro80.rom", 0x0000, 0x0800, CRC(63a4b72a) SHA1(6bd3e396539a15e2ccffa7486cae06ef6ddd1d03))

	ROM_REGION(0x0800, "chargen",0)
	ROM_LOAD ("mikro80.fnt", 0x0000, 0x0800, CRC(43eb72bb) SHA1(761319cc6747661b33e84aa449cec83800543b5b) )
ROM_END

ROM_START( radio99 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "monrk88.bin", 0x0000, 0x0800, CRC(5415d847) SHA1(c8233c72548bc79846b9d998766a10df349c5bda))

	ROM_REGION(0x0800, "chargen",0)
	ROM_LOAD ("mikro80.fnt", 0x0000, 0x0800, CRC(43eb72bb) SHA1(761319cc6747661b33e84aa449cec83800543b5b) )
ROM_END

ROM_START( kristall2 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "kristall-2.rom", 0x0000, 0x0800, CRC(e1b5c60f) SHA1(8ce5158def7fca91ec7e11efbb10aa5d70b7c36d))

	ROM_REGION(0x0800, "chargen",0)
	ROM_LOAD( "kristall-2.fnt", 0x0000, 0x0800, CRC(9661c9f5) SHA1(830c38735dcb1c8a271fa0027f94b4e034848fc8))
ROM_END


/* Driver */
/*    YEAR  NAME       PARENT   COMPAT  MACHINE   INPUT    CLASS          INIT          COMPANY      FULLNAME       FLAGS */
COMP( 1983, mikro80,   0,       0,      mikro80,  mikro80, mikro80_state, init_mikro80, "<unknown>", "Mikro-80",    MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1993, radio99,   mikro80, 0,      radio99,  mikro80, mikro80_state, init_radio99, "<unknown>", "Radio-99DM",  MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1987, kristall2, mikro80, 0,      kristall, mikro80, mikro80_state, init_mikro80, "<unknown>", "Kristall-2",  MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



mikromik.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

    Nokia Elektroniikka pj

    Controller ILC 9534
    FDC-Interface ILC 9530

    Parts:

    6,144 MHz xtal (CPU clock)
    18,720 MHz xtal (pixel clock)
    16 MHz xtal (FDC clock)
    Intel I8085AP (CPU)
    Intel 8253-5P (PIT)
    Intel 8275P (CRTC)
    Intel 8212P (I/OP)
    Intel 8237A-5P (DMAC)
    NEC uPD7220C (GDC)
    NEC uPD7201P (MPSC=uart)
    NEC uPD765 (FDC)
    TMS4116-15 (16Kx4 DRAM)*4 = 32KB Video RAM for 7220
    2164-6P (64Kx1 DRAM)*8 = 64KB Work RAM

    DMA channels:

    0   CRT
    1   MPSC transmit
    2   MPSC receive
    3   FDC

    Interrupts:

    INTR    MPSC INT
    RST5.5  FDC IRQ
    RST6.5  8212 INT
    RST7.5  DMA EOP

    Models:

    M1: 1x 160KB floppy
    M2: 2x 160KB floppy
    M3: 1x 320KB floppy (96 tpi, single sided)
    M4: 2x 320KB floppy
    M5: 1x 640KB floppy (96 tpi, double sided)
    M6: 2x 640KB floppy
    M7: 1x 640KB floppy + 5MB hard disk
    M4G: 2x 320KB floppy + GDC
    M6G: 2x 640KB floppy + GDC
    M7G: 1x 640KB floppy + 5MB hard disk + GDC


    ./chdman createhd -chs 306,2,32 -ss 256 -o st406.chd

*/

/*

    TODO:

    - M7 boot floppy
    - accurate video timing
    - PCB layouts
    - NEC uPD7201 MPSC

*/

#include "emu.h"
#include "mikromik.h"
#include "softlist_dev.h"

//#define VERBOSE 1
#include "logmacro.h"


//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

#define MMU_IOEN    0x01
#define MMU_RAMEN   0x02
#define MMU_CE4     0x08
#define MMU_CE0     0x10
#define MMU_CE1     0x20
#define MMU_CE2     0x40
#define MMU_CE3     0x80



//**************************************************************************
//  MEMORY MANAGEMENT UNIT
//**************************************************************************

//-------------------------------------------------
//  read -
//-------------------------------------------------

uint8_t mm1_state::read(offs_t offset)
{
	uint8_t data = 0;
	uint8_t mmu = m_mmu_rom->base()[(m_a8 << 8) | (offset >> 8)];

	if (mmu & MMU_IOEN)
	{
		data = m_io->read8(offset & 0x7f);
	}
	else
	{
		if (mmu & MMU_RAMEN)
		{
			data = m_ram->pointer()[offset];
		}
		else if (!(mmu & MMU_CE0))
		{
			data = m_rom->base()[offset & 0x1fff];
		}
		else if (!(mmu & MMU_CE1))
		{
			data = m_rom->base()[0x2000 + (offset & 0x1fff)];
		}
	}

	return data;
}



//-------------------------------------------------
//  write -
//-------------------------------------------------

void mm1_state::write(offs_t offset, uint8_t data)
{
	uint8_t mmu = m_mmu_rom->base()[(m_a8 << 8) | (offset >> 8)];

	if (mmu & MMU_IOEN)
	{
		m_io->write8(offset & 0x7f, data);
	}
	else
	{
		if (mmu & MMU_RAMEN)
		{
			m_ram->pointer()[offset] = data;
		}
	}
}


//-------------------------------------------------
//  recall_w -
//-------------------------------------------------

void mm1_state::recall_w(int state)
{
	LOG("RECALL %u\n", state);
	m_recall = state;
	m_fdc->reset_w(state);

	if (m_recall)
	{
		m_dmac->dreq3_w(false);
	}
}


//-------------------------------------------------
//  motor_on_w -
//-------------------------------------------------

void mm1_state::motor_on_w(int state)
{
	LOG("MOTOR %u\n", state);

	m_floppy[0]->mon_w(!state);

	if (m_floppy[1])
	{
		m_floppy[1]->mon_w(!state);
	}
}


//-------------------------------------------------
//  switch_w -
//-------------------------------------------------

void mm1_state::switch_w(int state)
{
	LOG("SWITCH %u\n", state);

	m_switch = state;

	if (m_switch)
	{
		m_io->space().install_readwrite_handler(0x50, 0x50, read8sm_delegate(*this, FUNC(mm1_state::sasi_status_r)), write8sm_delegate(*this, FUNC(mm1_state::sasi_cmd_w)));
		m_io->space().install_readwrite_handler(0x51, 0x51, emu::rw_delegate(*this, FUNC(mm1_state::sasi_data_r)), write8sm_delegate(*this, FUNC(mm1_state::sasi_data_w)));
	}
	else
	{
		m_io->space().install_device(0x50, 0x51, *m_fdc, &upd765a_device::map);
	}

	m_floppy[0]->mon_w(state);
}

uint8_t mm1_state::sasi_status_r(offs_t offset)
{
	uint8_t data = 0;

	data |= m_sasi->bsy_r();
	data |= m_sasi->rst_r() << 1;
	data |= m_sasi->msg_r() << 2;
	data |= m_sasi->cd_r() << 3;
	data |= m_sasi->req_r() << 4;
	data |= m_sasi->io_r() << 5;
	data |= m_sasi->ack_r() << 7;

	//LOG("%s SASI STATUS %02x\n",machine().describe_context(),data);

	return data;
}

void mm1_state::sasi_cmd_w(offs_t offset, uint8_t data)
{
	LOG("%s SASI CMD %02x\n", machine().describe_context(), data);

	m_sasi->sel_w(BIT(data, 0));
	m_sasi->rst_w(BIT(data, 1));
	m_sasi->atn_w(BIT(data, 2));
}

uint8_t mm1_state::sasi_data_r(offs_t offset)
{
	uint8_t data = m_sasi->read();

	LOG("%s SASI DATA R %02x\n", machine().describe_context(), data);

	if (m_sasi->req_r())
	{
		m_sasi->ack_w(1);
	}

	return data;
}

void mm1_state::sasi_data_w(offs_t offset, uint8_t data)
{
	m_sasi_data = data;

	if (!m_sasi->io_r())
	{
		m_sasi->write(data);
	}

	LOG("%s SASI DATA W %02x\n", machine().describe_context(), data);

	if (m_sasi->req_r())
	{
		m_sasi->ack_w(1);
	}
}

uint8_t mm1_state::sasi_ior3_r(offs_t offset)
{
	uint8_t data = 0;

	if (m_switch)
	{
		data = sasi_data_r(0);
	}
	else
	{
		data = m_fdc->dma_r();
	}

	return data;
}

void mm1_state::sasi_iow3_w(offs_t offset, uint8_t data)
{
	if (m_switch)
	{
		sasi_data_w(0, data);
	}
	else
	{
		m_fdc->dma_w(data);
	}
}

void mm1_state::sasi_bsy_w(int state)
{
	if (state)
	{
		m_sasi->sel_w(0);
	}
}

void mm1_state::sasi_req_w(int state)
{
	if (!state)
	{
		m_sasi->ack_w(0);
	}

	m_dmac->dreq3_w(state);
}

void mm1_state::sasi_io_w(int state)
{
	if (state)
	{
		m_sasi->write(0);
	}
	else
	{
		m_sasi->write(m_sasi_data);
	}
}


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void mm1_state::mm1_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(mm1_state::read), FUNC(mm1_state::write));
}

void mm1_state::mmu_io_map(address_map &map)
{
	map(0x00, 0x0f).rw(m_dmac, FUNC(am9517a_device::read), FUNC(am9517a_device::write));
	map(0x10, 0x13).mirror(0x0c).rw(m_mpsc, FUNC(upd7201_device::cd_ba_r), FUNC(upd7201_device::cd_ba_w));
	map(0x20, 0x21).mirror(0x0e).rw(m_crtc, FUNC(i8275_device::read), FUNC(i8275_device::write));
	map(0x30, 0x33).mirror(0x0c).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x40, 0x40).mirror(0x0f).rw(m_iop, FUNC(i8212_device::read), FUNC(i8212_device::write));
	map(0x50, 0x51).mirror(0x0e).m(m_fdc, FUNC(upd765a_device::map));
	map(0x60, 0x67).mirror(0x08).w(m_outlatch, FUNC(ls259_device::write_d0));
}

void mm1_state::mm1g_mmu_io_map(address_map &map)
{
	mmu_io_map(map);
	map(0x70, 0x71).mirror(0x0e).rw(m_hgdc, FUNC(upd7220_device::read), FUNC(upd7220_device::write));
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( mm1 )
//-------------------------------------------------

static INPUT_PORTS_START( mm1 )
	// defined in machine/mm1kb.h

	PORT_START("T5")
	PORT_CONFNAME( 0x01, 0x00, "Floppy Drive Type")
	PORT_CONFSETTING( 0x00, "640 KB" )
	PORT_CONFSETTING( 0x01, "160/320 KB" )
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  I8237_INTERFACE( dmac_intf )
//-------------------------------------------------

void mm1_state::update_tc()
{
	int fdc_tc = m_tc && !m_dack3;

	if (m_fdc_tc != fdc_tc)
	{
		m_fdc_tc = fdc_tc;
		m_fdc->tc_w(m_fdc_tc);
	}
}

void mm1_state::dma_hrq_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);

	// Assert HLDA
	m_dmac->hack_w(state);
}

uint8_t mm1_state::mpsc_dack_r()
{
	// clear data request
	m_dmac->dreq2_w(CLEAR_LINE);

	return 1;//m_mpsc->dtra_r();
}

void mm1_state::mpsc_dack_w(uint8_t data)
{
	//m_mpsc->hai_w(data);

	// clear data request
	m_dmac->dreq1_w(CLEAR_LINE);
}

void mm1_state::dma_eop_w(int state)
{
	m_maincpu->set_input_line(I8085_RST75_LINE, state);

	m_tc = state;
	update_tc();
}

void mm1_state::dack3_w(int state)
{
	m_dack3 = state;
	update_tc();
}

void mm1_state::itxc_w(int state)
{
	if (!m_intc)
	{
		m_mpsc->txca_w(state);
	}
}

void mm1_state::irxc_w(int state)
{
	if (!m_intc)
	{
		m_mpsc->rxca_w(state);
	}
}

void mm1_state::auxc_w(int state)
{
	m_mpsc->txcb_w(state);
	m_mpsc->rxcb_w(state);
}

//-------------------------------------------------
//  UPD7201
//-------------------------------------------------

void mm1_state::drq2_w(int state)
{
	if (state)
	{
		m_dmac->dreq2_w(ASSERT_LINE);
	}
}

void mm1_state::drq1_w(int state)
{
	if (state)
	{
		m_dmac->dreq1_w(ASSERT_LINE);
	}
}

int mm1_state::dsra_r()
{
	return 1;
}


//-------------------------------------------------
//  upd765_interface fdc_intf
//-------------------------------------------------

void mm1_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_MM1_FORMAT);
}

static void mm1m4_floppies(device_slot_interface &device)
{
	device.option_add("525", FLOPPY_525_QD);
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( mm1 )
//-------------------------------------------------

void mm1_state::machine_start()
{
	// state saving
	save_item(NAME(m_a8));
	save_item(NAME(m_leen));
	save_item(NAME(m_intc));
	save_item(NAME(m_rx21));
	save_item(NAME(m_tx21));
	save_item(NAME(m_rcl));
	save_item(NAME(m_recall));
	save_item(NAME(m_dack3));
	save_item(NAME(m_tc));
	save_item(NAME(m_fdc_tc));
	save_item(NAME(m_switch));
	save_item(NAME(m_sasi_data));
}



//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

void mm1_state::common(machine_config &config)
{
	// basic system hardware
	I8085A(config, m_maincpu, 6.144_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mm1_state::mm1_map);
	m_maincpu->in_sid_func().set(FUNC(mm1_state::dsra_r));
	m_maincpu->out_sod_func().set(KB_TAG, FUNC(mm1_keyboard_device::bell_w)).invert();

	config.set_perfect_quantum(m_maincpu);

	// peripheral hardware
	ADDRESS_MAP_BANK(config, m_io);
	m_io->set_addrmap(0, &mm1_state::mmu_io_map);
	m_io->set_data_width(8);
	m_io->set_addr_width(7);

	I8212(config, m_iop, 0);
	m_iop->int_wr_callback().set_inputline(m_maincpu, I8085_RST65_LINE);
	m_iop->di_rd_callback().set(KB_TAG, FUNC(mm1_keyboard_device::read));

	LS259(config, m_outlatch);
	m_outlatch->q_out_cb<0>().set(FUNC(mm1_state::a8_w)); // IC24 A8
	m_outlatch->q_out_cb<1>().set(FUNC(mm1_state::recall_w)); // RECALL
	m_outlatch->q_out_cb<2>().set(FUNC(mm1_state::rx21_w)); // _RV28/RX21
	m_outlatch->q_out_cb<3>().set(FUNC(mm1_state::tx21_w)); // _TX21
	m_outlatch->q_out_cb<4>().set(FUNC(mm1_state::rcl_w)); // _RCL
	m_outlatch->q_out_cb<5>().set(FUNC(mm1_state::intc_w)); // _INTC
	m_outlatch->q_out_cb<6>().set(FUNC(mm1_state::leen_w)); // LEEN
	m_outlatch->q_out_cb<7>().set(FUNC(mm1_state::motor_on_w)); // MOTOR ON

	AM9517A(config, m_dmac, 6.144_MHz_XTAL/2);
	m_dmac->out_hreq_callback().set(FUNC(mm1_state::dma_hrq_w));
	m_dmac->out_eop_callback().set(FUNC(mm1_state::dma_eop_w));
	m_dmac->in_memr_callback().set(FUNC(mm1_state::read));
	m_dmac->out_memw_callback().set(FUNC(mm1_state::write));
	m_dmac->in_ior_callback<2>().set(FUNC(mm1_state::mpsc_dack_r));
	m_dmac->in_ior_callback<3>().set(m_fdc, FUNC(upd765_family_device::dma_r));
	m_dmac->out_iow_callback<0>().set(m_crtc, FUNC(i8275_device::dack_w));
	m_dmac->out_iow_callback<1>().set(FUNC(mm1_state::mpsc_dack_w));
	m_dmac->out_iow_callback<3>().set(m_fdc, FUNC(upd765_family_device::dma_w));
	m_dmac->out_dack_callback<3>().set(FUNC(mm1_state::dack3_w));

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(6.144_MHz_XTAL/2/2);
	m_pit->out_handler<0>().set(FUNC(mm1_state::itxc_w));
	m_pit->set_clk<1>(6.144_MHz_XTAL/2/2);
	m_pit->out_handler<1>().set(FUNC(mm1_state::irxc_w));
	m_pit->set_clk<2>(6.144_MHz_XTAL/2/2);
	m_pit->out_handler<2>().set(FUNC(mm1_state::auxc_w));

	UPD765A(config, m_fdc, 16_MHz_XTAL/2, true, true);
	m_fdc->intrq_wr_callback().set_inputline(m_maincpu, I8085_RST55_LINE);
	m_fdc->drq_wr_callback().set(m_dmac, FUNC(am9517a_device::dreq3_w));

	UPD7201(config, m_mpsc, 6.144_MHz_XTAL/2);
	m_mpsc->out_txda_callback().set(m_rs232a, FUNC(rs232_port_device::write_txd));
	m_mpsc->out_dtra_callback().set(m_rs232a, FUNC(rs232_port_device::write_dtr));
	m_mpsc->out_rtsa_callback().set(m_rs232a, FUNC(rs232_port_device::write_rts));
	m_mpsc->out_rxdrqa_callback().set(FUNC(mm1_state::drq2_w));
	m_mpsc->out_txdrqa_callback().set(FUNC(mm1_state::drq1_w));

	RS232_PORT(config, m_rs232a, default_rs232_devices, nullptr);
	m_rs232a->cts_handler().set(m_mpsc, FUNC(upd7201_device::rxa_w));
	RS232_PORT(config, m_rs232b, default_rs232_devices, nullptr);
	RS232_PORT(config, m_rs232c, default_rs232_devices, nullptr);
	m_rs232c->cts_handler().set(m_mpsc, FUNC(upd7201_device::ctsb_w));

	mm1_keyboard_device &kb(MM1_KEYBOARD(config, KB_TAG, 2500)); // actual KBCLK is 6.144_MHz_XTAL/2/16
	kb.kbst_wr_callback().set(m_iop, FUNC(i8212_device::stb_w));

	// internal ram
	RAM(config, RAM_TAG).set_default_size("64K");

	// software lists
	SOFTWARE_LIST(config, "flop_list").set_original("mm1_flop");
}

void mm1_state::mm1(machine_config &config)
{
	common(config);
	mm1_video(config);
}

void mm1_state::mm1g(machine_config &config)
{
	common(config);
	mm1g_video(config);

	m_io->set_addrmap(0, &mm1_state::mm1g_mmu_io_map);
}

void mm1_state::mm1_320k_dual(machine_config &config)
{
	FLOPPY_CONNECTOR(config, UPD765_TAG ":0", mm1m4_floppies, "525", mm1_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, UPD765_TAG ":1", mm1m4_floppies, "525", mm1_state::floppy_formats).enable_sound(true);
}

void mm1_state::mm1m4(machine_config &config)
{
	mm1(config);
	mm1_320k_dual(config);
}

void mm1_state::mm1m4g(machine_config &config)
{
	mm1g(config);
	mm1_320k_dual(config);
}

void mm1_state::mm1_640k_dual(machine_config &config)
{
	FLOPPY_CONNECTOR(config, UPD765_TAG ":0", mm1m4_floppies, "525", mm1_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, UPD765_TAG ":1", mm1m4_floppies, "525", mm1_state::floppy_formats).enable_sound(true);
}

void mm1_state::mm1m6(machine_config &config)
{
	mm1(config);
	mm1_640k_dual(config);
}

void mm1_state::mm1m6g(machine_config &config)
{
	mm1g(config);
	mm1_640k_dual(config);
}

void mm1_state::mm1_640k_winchester(machine_config &config)
{
	FLOPPY_CONNECTOR(config, UPD765_TAG ":0", mm1m4_floppies, "525", mm1_state::floppy_formats).enable_sound(true);

	NSCSI_BUS(config, "sasi");
	NSCSI_CONNECTOR(config, "sasi:0", default_scsi_devices, "s1410");
	NSCSI_CONNECTOR(config, "sasi:7", default_scsi_devices, "scsicb", true)
		.option_add_internal("scsicb", NSCSI_CB)
		.machine_config([this](device_t* device) {
			downcast<nscsi_callback_device&>(*device).bsy_callback().set(*this, FUNC(mm1_state::sasi_bsy_w));
			downcast<nscsi_callback_device&>(*device).req_callback().set(*this, FUNC(mm1_state::sasi_req_w));
			downcast<nscsi_callback_device&>(*device).io_callback().set(*this, FUNC(mm1_state::sasi_io_w));
		});

	m_outlatch->q_out_cb<7>().set(FUNC(mm1_state::switch_w));

	m_dmac->in_ior_callback<3>().set(*this, FUNC(mm1_state::sasi_ior3_r));
	m_dmac->out_iow_callback<3>().set(*this, FUNC(mm1_state::sasi_iow3_w));
}

void mm1_state::mm1m7(machine_config &config)
{
	mm1(config);
	mm1_640k_winchester(config);
}

void mm1_state::mm1m7g(machine_config &config)
{
	mm1g(config);
	mm1_640k_winchester(config);
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( mm1m4 )
//-------------------------------------------------

ROM_START( mm1m4 )
	ROM_REGION( 0x4000, I8085A_TAG, 0 ) // BIOS
	ROM_LOAD( "9081b.ic43", 0x0000, 0x2000, CRC(60841940) SHA1(d755e9be53f27f41d1e93b4c1793f9ea6a3e1229) )

	ROM_REGION( 0x200, "address", 0 ) // address decoder
	ROM_LOAD( "720793a.ic24", 0x000, 0x200, CRC(deea87a6) SHA1(8f19e43252c9a0b1befd02fc9d34fe1437477f3a) )

	ROM_REGION( 0x1000, "chargen", 0 ) // character generator
	ROM_LOAD( "6807b.ic61", 0x0000, 0x1000, CRC(32b36220) SHA1(8fe7a181badea3f7e656dfaea21ee9e4c9baf0f1) )
ROM_END

#define rom_mm1m4g rom_mm1m4


//-------------------------------------------------
//  ROM( mm1m6 )
//-------------------------------------------------

ROM_START( mm1m6 )
	ROM_REGION( 0x4000, I8085A_TAG, 0 ) // BIOS
	ROM_LOAD( "9081b.ic2", 0x0000, 0x2000, CRC(2955feb3) SHA1(946a6b0b8fb898be3f480c04da33d7aaa781152b) )

	ROM_REGION( 0x200, "address", 0 ) // address decoder
	ROM_LOAD( "720793a.ic24", 0x000, 0x200, CRC(deea87a6) SHA1(8f19e43252c9a0b1befd02fc9d34fe1437477f3a) )

	ROM_REGION( 0x1000, "chargen", 0 ) // character generator
	ROM_LOAD( "6807b.ic61", 0x0000, 0x1000, CRC(32b36220) SHA1(8fe7a181badea3f7e656dfaea21ee9e4c9baf0f1) )
ROM_END

#define rom_mm1m6g rom_mm1m6


//-------------------------------------------------
//  ROM( mm1m7 )
//-------------------------------------------------

ROM_START( mm1m7 )
	ROM_REGION( 0x4000, I8085A_TAG, 0 ) // BIOS
	ROM_LOAD( "9057c.ic2", 0x0000, 0x2000, CRC(89bbc042) SHA1(7e8800c94934b81ce08b7af862e1159e0517684d) )

	ROM_REGION( 0x200, "address", 0 ) // address decoder
	ROM_LOAD( "726972b.ic24", 0x000, 0x200, CRC(2487d4ca) SHA1(e883a2e9540c31abba3d7f3bc23a48941f655ea0) )

	ROM_REGION( 0x1000, "chargen", 0 ) // character generator
	ROM_LOAD( "6807b.ic61", 0x0000, 0x1000, CRC(32b36220) SHA1(8fe7a181badea3f7e656dfaea21ee9e4c9baf0f1) )
ROM_END

#define rom_mm1m7g rom_mm1m7



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME    PARENT COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY       FULLNAME            FLAGS
COMP( 1981, mm1m4,  0,     0,      mm1m4,   mm1,   mm1_state, empty_init, "Nokia Data", "MikroMikko 1 M4",  MACHINE_SUPPORTS_SAVE )
COMP( 1981, mm1m4g, mm1m4, 0,      mm1m4g,  mm1,   mm1_state, empty_init, "Nokia Data", "MikroMikko 1 M4G", MACHINE_SUPPORTS_SAVE )
COMP( 1981, mm1m6,  0,     0,      mm1m6,   mm1,   mm1_state, empty_init, "Nokia Data", "MikroMikko 1 M6",  MACHINE_SUPPORTS_SAVE )
COMP( 1981, mm1m6g, mm1m6, 0,      mm1m6g,  mm1,   mm1_state, empty_init, "Nokia Data", "MikroMikko 1 M6G", MACHINE_SUPPORTS_SAVE )
COMP( 1981, mm1m7,  0,     0,      mm1m7,   mm1,   mm1_state, empty_init, "Nokia Data", "MikroMikko 1 M7",  MACHINE_SUPPORTS_SAVE )
COMP( 1981, mm1m7g, mm1m7, 0,      mm1m7g,  mm1,   mm1_state, empty_init, "Nokia Data", "MikroMikko 1 M7G", MACHINE_SUPPORTS_SAVE )



mikromikko2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

    Models:

    M13A: Floppy drive 5.25", 320 KB + memory 128 KB
    M13B: Floppy drive 5.25", 320 KB + memory 256 KB
    M13C: Floppy drive 5.25", 320 KB + memory 512 KB
    M13D: Floppy drive 5.25", 320 KB + memory 768 KB
    M14A: 2 floppy drives 5.25", 320 KB + memory 128 KB
    M14B: 2 floppy drives 5.25", 320 KB + memory 256 KB
    M14C: 2 floppy drives 5.25", 320 KB + memory 512 KB
    M14D: 2 floppy drives 5.25", 320 KB + memory 768 KB
    M15A: Floppy drive 5.25", 640 KB + memory 128 KB
    M15B: Floppy drive 5.25", 640 KB + memory 256 KB
    M15C: Floppy drive 5.25", 640 KB + memory 512 KB
    M15D: Floppy drive 5.25", 640 KB + memory 768 KB
    M16A: 2 floppy drives 5.25", 640 KB + memory 128 KB
    M16B: 2 floppy drives 5.25", 640 KB + memory 256 KB
    M16C: 2 floppy drives 5.25", 640 KB + memory 512 KB
    M16D: 2 floppy drives 5.25", 640 KB + memory 768 KB
    M25B: Floppy drive 5.25", 640 KB + hard disk 5.25" Winchester, 5 MB + memory 256 KB
    M25C: Floppy drive 5.25", 640 KB + hard disk 5.25" Winchester, 5 MB + memory 512 KB
    M25D: Floppy drive 5.25", 640 KB + hard disk 5.25" Winchester, 5 MB + memory 768 KB
    M35B: Floppy drive 5.25", 640 KB + hard disk 5.25" Winchester, 15 MB + memory 256 KB
    M35C: Floppy drive 5.25", 640 KB + hard disk 5.25" Winchester, 15 MB + memory 512 KB
    M35D: Floppy drive 5.25", 640 KB + hard disk 5.25" Winchester, 15 MB + memory 768 KB

    ./chdman createhd -chs 306,2,17 -ss 512 -o st406.chd
    ./chdman createhd -chs 306,6,17 -ss 512 -o st412.chd

*/

/*

    TODO:

    - DMA
    - floppy
    - SASI
    - video
    - keyboard

*/

#include "emu.h"
#include "mikromikko2.h"
#include "softlist_dev.h"

void mm2_state::novram_store(offs_t offset, uint8_t data)
{
	m_novram->store(1);
	m_novram->store(0);
}

void mm2_state::novram_recall(offs_t offset, uint8_t data)
{
	m_novram->recall(!BIT(data, 0));
}

uint8_t mm2_state::videoram_r(offs_t offset)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	uint16_t data = program.read_word(0xd0000 | (offset << 1));

	// character
	m_drb0->write(data & 0xff);

	// attributes
	m_drb1->write(data >> 8);

	return data & 0xff;
}

uint8_t mm2_state::status_r(offs_t offset)
{
	uint8_t data = 0x80;

	data |= !m_rs232a->dsr_r() << 4;

	return data;
}

uint8_t mm2_state::sasi_status_r(offs_t offset)
{
	uint8_t data = 0;

	data |= m_sasi->bsy_r();
	data |= m_sasi->rst_r() << 1;
	data |= m_sasi->msg_r() << 2;
	data |= m_sasi->cd_r() << 3;
	data |= m_sasi->req_r() << 4;
	data |= m_sasi->io_r() << 5;
	data |= m_sasi->ack_r() << 7;

	return data;
}

void mm2_state::sasi_cmd_w(offs_t offset, uint8_t data)
{
	m_sasi->sel_w(BIT(data, 0));
	m_sasi->rst_w(BIT(data, 1));
	m_sasi->atn_w(BIT(data, 2));
}

uint8_t mm2_state::sasi_data_r(offs_t offset)
{
	uint8_t data = m_sasi->read();

	if (m_sasi->req_r())
	{
		m_sasi->ack_w(1);
	}

	return data;
}

void mm2_state::sasi_data_w(offs_t offset, uint8_t data)
{
	m_sasi_data = data;

	if (!m_sasi->io_r())
	{
		m_sasi->write(data);
	}

	if (m_sasi->req_r())
	{
		m_sasi->ack_w(1);
	}
}

void mm2_state::sasi_bsy_w(int state)
{
	if (state)
	{
		m_sasi->sel_w(0);
	}
}

void mm2_state::sasi_req_w(int state)
{
	if (!state)
	{
		m_sasi->ack_w(0);
	}

	m_dmac->dreq3_w(state);
}

void mm2_state::sasi_io_w(int state)
{
	if (state)
	{
		m_sasi->write(0);
	}
	else
	{
		m_sasi->write(m_sasi_data);
	}
}

uint8_t mm2_state::dmac_mem_r(offs_t offset)
{
	uint16_t *mem = (uint16_t *)m_maincpu->space(AS_PROGRAM).get_read_ptr(m_dma_hi << 15);

	if (WORD_ALIGNED(offset))
	{
		return mem[offset >> 1] & 0xff;
	}
	else
	{
		return mem[offset >> 1] >> 8;
	}
}

void mm2_state::dmac_mem_w(offs_t offset, uint8_t data)
{
	uint16_t *mem = (uint16_t *)m_maincpu->space(AS_PROGRAM).get_write_ptr(m_dma_hi << 15);
	uint16_t value = mem[offset >> 1];

	if (WORD_ALIGNED(offset))
	{
		mem[offset >> 1] = (value & 0xff00) | data;
	}
	else
	{
		mem[offset >> 1] = data << 8 | (value & 0xff);
	}
}

void mm2_state::mm2_map(address_map &map)
{
	map(0x00000, 0x1ffff).ram(); // DRAM 128 KB (on SBC186)
	map(0x20000, 0x3ffff).ram(); // DRAM 128 KB (on SBC186)
	map(0x40000, 0x7ffff).ram(); // DRAM 256 KB (on SBC186 or MEME186)
	map(0x80000, 0xbffff).ram(); // DRAM 256 KB (on MEME186)
	map(0xd0000, 0xd1fff).ram(); // video RAM
	map(0xd8000, 0xd9fff).rom().region("chargen", 0);
	map(0xf0000, 0xf01ff).rw(m_novram, FUNC(x2212_device::read), FUNC(x2212_device::write)).umask16(0xff00);
	map(0xf0200, 0xfffff).rom().region(I80186_TAG, 0x200);
}

void mm2_state::mm2_io_map(address_map &map)
{
	// SBC16
	map(0xf800, 0xf803).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0xf880, 0xf887).rw(m_mpsc, FUNC(i8274_device::cd_ba_r), FUNC(i8274_device::cd_ba_w)).umask16(0x00ff);
	map(0xf884, 0xf885).r(FUNC(mm2_state::status_r)).umask16(0xff00);
	map(0xf900, 0xf901).w(FUNC(mm2_state::novram_store)).umask16(0x00ff);
	map(0xf97e, 0xf97f).w(FUNC(mm2_state::novram_recall)).umask16(0xff00);
	map(0xf930, 0xf937).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0xff00);
	map(0xf940, 0xf941).w(FUNC(mm2_state::tcl_w)).umask16(0xff00);
	map(0xf950, 0xf951).w(FUNC(mm2_state::diag_w)).umask16(0xff00);
	map(0xf960, 0xf961).w(FUNC(mm2_state::cls0_w)).umask16(0xff00);
	map(0xf962, 0xf963).w(FUNC(mm2_state::cls1_w)).umask16(0xff00);
	//map(0xf965, 0xf965) LOOPBACK LLBA
	//map(0xf967, 0xf967) LOOPBACK LLBB
	//map(0xf969, 0xf969) DATA CODING NRZI
	//map(0xf96b, 0xf96b) SIGNAL LEVELS V24
	//map(0xf96d, 0xf96d) SIGNAL LEVELS X27
	map(0xf96e, 0xf96f).w(FUNC(mm2_state::dtra_w)).umask16(0xff00);
	//map(0xf971, 0xf971) V24 SIGNAL TSTA
	//map(0xf973, 0xf973) V24 SIGNAL SRSA
	map(0xf974, 0xf975).w(FUNC(mm2_state::dtrb_w)).umask16(0xff00);

	// CRTC186
	map(0xf980, 0xf9ff).rw(m_vpac, FUNC(crt9007_device::read), FUNC(crt9007_device::write)).umask16(0x00ff);
	map(0xf980, 0xf981).rw(m_sio, FUNC(i8251_device::data_r), FUNC(i8251_device::data_w)).umask16(0xff00);
	map(0xf982, 0xf983).rw(m_sio, FUNC(i8251_device::status_r), FUNC(i8251_device::control_w)).umask16(0xff00);
	map(0xf9c0, 0xf9c1).w(FUNC(mm2_state::cpl_w));
	map(0xf9c2, 0xf9c3).w(FUNC(mm2_state::blc_w));
	map(0xf9c4, 0xf9c5).w(FUNC(mm2_state::mode_w));
	map(0xf9c6, 0xf9c7).w(FUNC(mm2_state::modeg_w));
	map(0xf9ca, 0xf9cb).w(FUNC(mm2_state::c70_50_w));
	map(0xf9cc, 0xf9cd).w(FUNC(mm2_state::crb_w));
	map(0xf9ce, 0xf9cf).w(FUNC(mm2_state::cru_w));

	// MMC186
	map(0xfa00, 0xfa1f).rw(m_dmac, FUNC(am9517a_device::read), FUNC(am9517a_device::write)).umask16(0x00ff);
	map(0xfa20, 0xfa21).rw(FUNC(mm2_state::sasi_status_r), FUNC(mm2_state::sasi_cmd_w)).umask16(0x00ff);
	map(0xfa22, 0xfa23).rw(FUNC(mm2_state::sasi_data_r), FUNC(mm2_state::sasi_data_w)).umask16(0x00ff);
	map(0xfa40, 0xfa41).r(m_fdc, FUNC(upd765a_device::msr_r)).umask16(0x00ff);
	map(0xfa42, 0xfa43).rw(m_fdc, FUNC(upd765a_device::fifo_r), FUNC(upd765a_device::fifo_w)).umask16(0x00ff);
	// map(0xfa60, 0xfa60) CONTROL SASI Select
	// map(0xfa62, 0xfa62) CONTROL SASI Interrupts Enable
	map(0xfa66, 0xfa67).w(FUNC(mm2_state::fdc_reset_w)).umask16(0x00ff);
	// map(0xfa6a, 0xfa6a) CONTROL -Mini/Std Select
	map(0xfa6c, 0xfa6d).w(FUNC(mm2_state::motor_on_w)).umask16(0x00ff);
	// map(0xfa6e, 0xfa6e) CONTROL Motor On (Std)
	map(0xfa70, 0xfa70).mirror(0xe).w(FUNC(mm2_state::dma_hi_w)).umask16(0x00ff);
}

void mm2_state::vpac_mem(address_map &map)
{
	map(0x0000, 0x3fff).r(FUNC(mm2_state::videoram_r));
}

static INPUT_PORTS_START( mm2 )
INPUT_PORTS_END

static const gfx_layout charlayout =
{
	8, 15,
	RGN_FRAC(1,1),
	1,
	{ RGN_FRAC(0,1) },
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	{  0*16,  1*16,  2*16,  3*16,  4*16,  5*16,  6*16,  7*16,
		8*16,  9*16, 10*16, 11*16, 12*16, 13*16, 14*16 },
	16*16
};

static const gfx_layout gfxlayout =
{
	8, 1,
	RGN_FRAC(1,1),
	1,
	{ RGN_FRAC(0,1) },
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	{ 15*16 },
	16*16
};

static GFXDECODE_START( gfx_mm2 )
	GFXDECODE_ENTRY( "chargen", 0, charlayout, 0, 1 )
	GFXDECODE_ENTRY( "chargen", 0, gfxlayout, 0, 1 )
GFXDECODE_END

void mm2_state::palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(0xff, 0xff, 0xff)); // white
	palette.set_pen_color(1, rgb_t(0x00, 0x00, 0x00)); // black (normal color)
	palette.set_pen_color(2, rgb_t(0x7f, 0x7f, 0x7f)); // grey ("highlight" mode color)
}

uint32_t mm2_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	if (!m_blc)
	{
		bitmap.fill(0, cliprect);
		return 0;
	}

	uint16_t *vram = (uint16_t *)m_maincpu->space(AS_PROGRAM).get_read_ptr(0xd1000);
	uint16_t *crom = (uint16_t *)m_maincpu->space(AS_PROGRAM).get_read_ptr(0xd8000);

	for (int sy = 0; sy < 24; sy++)
	{
		for (int y = 0; y < 12; y++)
		{
			for (int sx = 0; sx < 80; sx++)
			{
				uint16_t data = vram[(sy * 80) + sx];
				offs_t char_addr = ((data & 0x1ff) << 4) | y;
				uint16_t char_data = crom[char_addr];

				for (int bit = 0; bit < 8; bit++)
				{
					bool pixel = BIT(char_data, 0) ^ m_cpl;
					char_data >>= 1;

					bitmap.pix((sy * 12) + y, (sx * 8) + bit) = pixel;
				}
			}
		}
	}

	return 0;
}

void mm2_state::machine_start()
{
}

void mm2_state::machine_reset()
{
	m_cpl = 0;
	m_blc = 0;
	m_mode = 0;
	m_modeg = 0;
	m_c70_50 = 0;
	m_cru = 0;
	m_crb = 0;
}

void mm2_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
}

static void mm2_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

void mm2_state::mm2(machine_config &config)
{
	// SBC186
	I80186(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mm2_state::mm2_map);
	m_maincpu->set_addrmap(AS_IO, &mm2_state::mm2_io_map);
	m_maincpu->irmx_irq_cb().set(m_pic, FUNC(pic8259_device::ir7_w));
	m_maincpu->tmrout0_handler().set(FUNC(mm2_state::tmrout0_w));
	m_maincpu->tmrout1_handler().set(FUNC(mm2_state::tmrout1_w));

	PIC8259(config, m_pic);
	m_pic->out_int_callback().set(m_maincpu, FUNC(i80186_cpu_device::int0_w));

	PIT8253(config, m_pit);
	m_pit->set_clk<0>(16_MHz_XTAL/8);
	m_pit->set_clk<1>(16_MHz_XTAL/8);
	m_pit->set_clk<2>(16_MHz_XTAL/8);
	m_pit->out_handler<0>().set(FUNC(mm2_state::ir0_w));
	m_pit->out_handler<1>().set(m_mpsc, FUNC(i8274_device::rxtxcb_w));
	m_pit->out_handler<2>().set(m_speaker, FUNC(speaker_sound_device::level_w));

	I8274(config, m_mpsc, 16_MHz_XTAL/4);
	m_mpsc->out_txda_callback().set(m_rs232a, FUNC(rs232_port_device::write_txd));
	m_mpsc->out_rtsa_callback().set(m_rs232a, FUNC(rs232_port_device::write_rts));
	m_mpsc->out_txdb_callback().set(m_rs232b, FUNC(rs232_port_device::write_txd));
	m_mpsc->out_rtsb_callback().set(m_rs232b, FUNC(rs232_port_device::write_rts));
	m_mpsc->out_int_callback().set(m_pic, FUNC(pic8259_device::ir1_w));

	RS232_PORT(config, m_rs232a, default_rs232_devices, nullptr);
	m_rs232a->rxd_handler().set(m_mpsc, FUNC(z80dart_device::rxa_w));
	m_rs232a->dcd_handler().set(m_mpsc, FUNC(z80dart_device::dcda_w));
	m_rs232a->cts_handler().set(m_mpsc, FUNC(z80dart_device::ctsa_w));

	RS232_PORT(config, m_rs232b, default_rs232_devices, "terminal");
	m_rs232b->rxd_handler().set(m_mpsc, FUNC(z80dart_device::rxb_w));
	m_rs232b->cts_handler().set(m_mpsc, FUNC(z80dart_device::ctsb_w));

	X2212(config, m_novram);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker, 0).add_route(ALL_OUTPUTS, "mono", 1.00);

	// CRTC186
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(71);
	screen.set_screen_update(FUNC(mm2_state::screen_update));
	screen.set_size(640, 420);
	screen.set_visarea(0, 640-1, 0, 420-1);
	screen.set_palette(m_palette);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_mm2);
	PALETTE(config, m_palette, FUNC(mm2_state::palette), 3);

	CRT9007(config, m_vpac, 35.4525_MHz_XTAL/8);
	m_vpac->set_addrmap(0, &mm2_state::vpac_mem);
	m_vpac->set_character_width(10);
	m_vpac->int_callback().set(FUNC(mm2_state::vpac_int_w));
	m_vpac->set_screen(SCREEN_TAG);

	CRT9212(config, m_drb0, 0);

	CRT9212(config, m_drb1, 0);

	I8251(config, m_sio, 16_MHz_XTAL/4);
	m_sio->rxrdy_handler().set(FUNC(mm2_state::sio_rxrdy_w));
	m_sio->txrdy_handler().set(FUNC(mm2_state::sio_txrdy_w));

	// MMC186
	AM9517A(config, m_dmac, 16_MHz_XTAL/4);
	m_dmac->in_memr_callback().set(FUNC(mm2_state::dmac_mem_r));
	m_dmac->out_memw_callback().set(FUNC(mm2_state::dmac_mem_w));
	m_dmac->in_ior_callback<0>().set(FUNC(mm2_state::sasi_data_r));
	m_dmac->out_iow_callback<0>().set(FUNC(mm2_state::sasi_data_w));
	m_dmac->in_ior_callback<1>().set(m_fdc, FUNC(upd765_family_device::dma_r));
	m_dmac->out_iow_callback<1>().set(m_fdc, FUNC(upd765_family_device::dma_w));

	UPD765A(config, m_fdc, 16_MHz_XTAL/2, true, true);
	m_fdc->intrq_wr_callback().set(m_pic, FUNC(pic8259_device::ir4_w));
	m_fdc->drq_wr_callback().set(m_dmac, FUNC(am9517a_device::dreq1_w));

	FLOPPY_CONNECTOR(config, UPD765_TAG ":0", mm2_floppies, "525qd", mm2_state::floppy_formats).enable_sound(true);

	NSCSI_BUS(config, "sasi");
	NSCSI_CONNECTOR(config, "sasi:0", default_scsi_devices, "s1410");
	NSCSI_CONNECTOR(config, "sasi:7", default_scsi_devices, "scsicb", true)
		.option_add_internal("scsicb", NSCSI_CB)
		.machine_config([this](device_t* device) {
			downcast<nscsi_callback_device&>(*device).bsy_callback().set(*this, FUNC(mm2_state::sasi_bsy_w));
			downcast<nscsi_callback_device&>(*device).req_callback().set(*this, FUNC(mm2_state::sasi_req_w));
			downcast<nscsi_callback_device&>(*device).io_callback().set(*this, FUNC(mm2_state::sasi_io_w));
		});

	// software lists
	SOFTWARE_LIST(config, "flop_list").set_original("mm2_flop");
}

ROM_START( mm2m35d )
	ROM_REGION16_LE( 0x10000, I80186_TAG, 0 )
	ROM_DEFAULT_BIOS("c")
	ROM_SYSTEM_BIOS(0, "a", "A")
	ROMX_LOAD( "9488a.ic38", 0x0000, 0x4000, CRC(ae831b67) SHA1(d922f02dfac783d0c86ca9a09bc2ad345ee1e71a), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "9490a.ic52", 0x0001, 0x4000, CRC(3ca470d1) SHA1(4cc300544e4a81939c2eb87e22c3ea367a7ec62c), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "9489a.ic41", 0x8000, 0x4000, CRC(a0f19bf5) SHA1(6af91b2f798ddfa9430546e23f00bbeb5ead5a29), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "9491a.ic58", 0x8001, 0x4000, CRC(cf7f3e6d) SHA1(5bf24661f5535d40d1b6ef7f2599f424f6eb2a11), ROM_SKIP(1) | ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "c", "C")
	ROMX_LOAD( "9488c.ic38", 0x0000, 0x4000, CRC(cbd151f0) SHA1(16470d4c2cee7a515640894d7ff1b3662516082a), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "9490c.ic52", 0x0001, 0x4000, CRC(bfde706e) SHA1(8a154aa00d480684b00aa7c30be6d6a78dd9ddaa), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "9489c.ic41", 0x8000, 0x4000, CRC(b5086aac) SHA1(f8d7a936baa701dcc30949fe1241be2ab9b80201), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "9491c.ic58", 0x8001, 0x4000, CRC(32047735) SHA1(408f03bc2d89257488e4b3336500681bb168cdec), ROM_SKIP(1) | ROM_BIOS(1) )

	ROM_REGION16_LE( 0x4000, "chargen", 0 )
	ROMX_LOAD( "9067e.ic40", 0x0000, 0x2000, CRC(fa719d92) SHA1(af6cc03a8171b9c95e8548c5e0268816344d7367), ROM_SKIP(1) )

	ROM_REGION( 0x2000, "attr", 0 )
	ROM_LOAD( "9026a.ic26", 0x0000, 0x2000, CRC(fe1da600) SHA1(3a5512b08d8f7bb5a0ff3f50bcf33de649a0489d) )

	ROM_REGION( 0x100, "timing", 0 )
	ROM_LOAD( "739025b.ic8", 0x000, 0x100, CRC(c538b10a) SHA1(9810732a52ee6b8313d27462b27acc7e4d5badeb) )

	ROM_REGION( 0x400, "keyboard", 0 )
	ROM_LOAD( "keyboard", 0x000, 0x400, NO_DUMP )
ROM_END

COMP( 1983, mm2m35d,  0,     0,      mm2,   mm2,   mm2_state, empty_init, "Nokia Data", "MikroMikko 2 M35D", MACHINE_NOT_WORKING )



mikrosha.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

        Mikrosha driver by Miodrag Milanovic

        05/06/2008 Preliminary driver.

****************************************************************************/

#include "emu.h"
#include "radio86.h"

#include "cpu/i8085/i8085.h"
#include "machine/pit8253.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/rk_cas.h"


namespace {

class mikrosha_state : public radio86_state
{
public:
	mikrosha_state(const machine_config &mconfig, device_type type, const char *tag)
		: radio86_state(mconfig, type, tag)
	{ }

	void mikrosha(machine_config &config);

private:
	void mikrosha_8255_font_page_w(uint8_t data);
	void mikrosha_pit_out2(int state);
	I8275_DRAW_CHARACTER_MEMBER(display_pixels);
	void machine_reset() override ATTR_COLD;
	void machine_start() override ATTR_COLD;

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	uint8_t m_mikrosha_font_page = 0;
};

void mikrosha_state::machine_reset()
{
	if (m_cart->exists())
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x8000, 0x8000 + m_cart->get_rom_size() - 1, read8sm_delegate(*m_cart, FUNC(generic_slot_device::read_rom)));
	radio86_state::machine_reset();
	m_mikrosha_font_page = 0;
}

/* Address maps */
void mikrosha_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).ram().share("mainram"); // RAM
	map(0xc000, 0xc003).rw(m_ppi1, FUNC(i8255_device::read), FUNC(i8255_device::write)).mirror(0x07fc);
	map(0xc800, 0xc803).rw(m_ppi2, FUNC(i8255_device::read), FUNC(i8255_device::write)).mirror(0x07fc);
	map(0xd000, 0xd001).rw("crtc", FUNC(i8275_device::read), FUNC(i8275_device::write)).mirror(0x07fe); // video
	map(0xd800, 0xd803).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write)).mirror(0x07fc); // Timer
	map(0xe000, 0xf7ff).r(FUNC(mikrosha_state::radio_cpu_state_r)); // Not connected
	map(0xf800, 0xffff).rom().region("maincpu",0).w(m_dma, FUNC(i8257_device::write));    // DMA
}

void mikrosha_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0xff).rw(FUNC(mikrosha_state::radio_io_r), FUNC(mikrosha_state::radio_io_w));
}

/* Input ports */
static INPUT_PORTS_START( mikrosha )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('@')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del") PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')

	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PgUp") PORT_CODE(KEYCODE_PGUP)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR(0xA4)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\\')

	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ins") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')

	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Center") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('~')

	PORT_START("LINE7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PgDn") PORT_CODE(KEYCODE_PGDN)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Back") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)

	PORT_START("LINE8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Rus/Lat") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
INPUT_PORTS_END

void mikrosha_state::mikrosha_8255_font_page_w(uint8_t data)
{
	m_mikrosha_font_page = (data >> 7) & 1;
}

void mikrosha_state::mikrosha_pit_out2(int state)
{
}

void mikrosha_state::machine_start()
{
	save_item(NAME(m_tape_value));
	save_item(NAME(m_mikrosha_font_page));
}

I8275_DRAW_CHARACTER_MEMBER(mikrosha_state::display_pixels)
{
	using namespace i8275_attributes;

	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	uint8_t const *const charmap = &m_chargen[(m_mikrosha_font_page & 1) * 0x400];
	uint8_t pixels = charmap[(linecount & 7) + (charcode << 3)] ^ 0xff;
	if (BIT(attrcode, VSP))
		pixels = 0;

	if (BIT(attrcode, LTEN))
		pixels = 0xff;

	if (BIT(attrcode, RVV))
		pixels ^= 0xff;

	bool hlgt = BIT(attrcode, HLGT);
	for(int i=0;i<6;i++)
		bitmap.pix(y, x + i) = palette[(pixels >> (5-i)) & 1 ? (hlgt ? 2 : 1) : 0];
}

/* F4 Character Displayer */
static const gfx_layout mikrosha_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_mikrosha )
	GFXDECODE_ENTRY( "chargen", 0x0000, mikrosha_charlayout, 0, 1 )
GFXDECODE_END

void mikrosha_state::mikrosha(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, XTAL(16'000'000) / 9);
	m_maincpu->set_addrmap(AS_PROGRAM, &mikrosha_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &mikrosha_state::io_map);

	I8255(config, m_ppi1);
	m_ppi1->in_pa_callback().set(FUNC(mikrosha_state::radio86_8255_portb_r2));
	m_ppi1->out_pb_callback().set(FUNC(mikrosha_state::radio86_8255_porta_w2));
	m_ppi1->in_pc_callback().set(FUNC(mikrosha_state::radio86_8255_portc_r2));
	m_ppi1->out_pc_callback().set(FUNC(mikrosha_state::radio86_8255_portc_w2));

	I8255(config, m_ppi2);
	m_ppi2->out_pb_callback().set(FUNC(mikrosha_state::mikrosha_8255_font_page_w));
	m_ppi2->tri_pb_callback().set_constant(0);

	i8275_device &i8275(I8275(config, "crtc", XTAL(16'000'000) / 12));
	i8275.set_character_width(6);
	i8275.set_display_callback(FUNC(mikrosha_state::display_pixels));
	i8275.drq_wr_callback().set(m_dma, FUNC(i8257_device::dreq2_w));

	pit8253_device &pit(PIT8253(config, "pit", 0));
	pit.set_clk<0>(0);
	pit.set_clk<1>(0);
	pit.set_clk<2>(2000000);
	pit.out_handler<2>().set(FUNC(mikrosha_state::mikrosha_pit_out2));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_screen_update("crtc", FUNC(i8275_device::screen_update));
	screen.set_refresh_hz(50);
	screen.set_size(78*6, 30*10);
	screen.set_visarea(0, 78*6-1, 0, 30*10-1);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_mikrosha);
	PALETTE(config, m_palette, FUNC(mikrosha_state::radio86_palette), 3);

	SPEAKER(config, "mono").front_center();

	I8257(config, m_dma, XTAL(16'000'000) / 9);
	m_dma->out_hrq_cb().set(FUNC(mikrosha_state::hrq_w));
	m_dma->in_memr_cb().set(FUNC(mikrosha_state::memory_read_byte));
	m_dma->out_memw_cb().set(FUNC(mikrosha_state::memory_write_byte));
	m_dma->out_iow_cb<2>().set("crtc", FUNC(i8275_device::dack_w));
	m_dma->set_reverse_rw_mode(1);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(rkm_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("mikrosha_cass");

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "mikrosha_cart", "bin,rom");

	SOFTWARE_LIST(config, "cass_list").set_original("mikrosha_cass");
	SOFTWARE_LIST(config, "cart_list").set_original("mikrosha_cart");
}


/* ROM definition */
ROM_START( mikrosha )
	ROM_REGION( 0x0800, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "mikrosha.rom", 0x0000, 0x0800, CRC(86a83556) SHA1(94b1baad0a419145939a891ff51f4324e8e4ddd2))

	ROM_REGION(0x0800, "chargen",0)
	ROM_LOAD ("mikrosha.fnt", 0x0000, 0x0800, CRC(b315da1c) SHA1(b5bf9abc0fff75b1aba709a7f08b23d4a89bb04b))
ROM_END

ROM_START( m86rk )
	ROM_REGION( 0x0800, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "m86rk.bin", 0x0000, 0x0800, CRC(a898d77a) SHA1(c2497bf8434b5028fe0a9fc09be311465d5553a5))

	ROM_REGION(0x0800, "chargen",0)
	/* here should probably be different rom */
	ROM_LOAD ("mikrosha.fnt", 0x0000, 0x0800, CRC(b315da1c) SHA1(b5bf9abc0fff75b1aba709a7f08b23d4a89bb04b))
ROM_END

} // anonymous namespace


/* Driver */
//    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT     CLASS           INIT          COMPANY                                FULLNAME         FLAGS
COMP( 1987, mikrosha, radio86, 0,      mikrosha, mikrosha, mikrosha_state, init_radio86, "Lianozovo Electromechanical Factory", "Mikrosha",      MACHINE_SUPPORTS_SAVE )
COMP( 1987, m86rk,    radio86, 0,      mikrosha, mikrosha, mikrosha_state, init_radio86, "<unknown>",                           "Mikrosha-86RK", MACHINE_SUPPORTS_SAVE )



milano.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco, hap
// thanks-to:Berger
/*******************************************************************************

Mephisto Milano

Hardware notes:
- RP65C02G or W65C02P-8 @ 4.91MHz
- 8KB RAM(battery-backed), 64KB ROM
- HD44100H, HD44780, 2*16 chars LCD screen
- 8*8 chessboard buttons, 16 leds, piezo

Nigel Short is basically a Milano 2.00

*******************************************************************************/

#include "emu.h"

#include "mmdisplay2.h"

#include "cpu/m6502/r65c02.h"
#include "machine/74259.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "video/pwm.h"

// internal artwork
#include "mephisto_milano.lh"


namespace {

class milano_state : public driver_device
{
public:
	milano_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_led_pwm(*this, "led_pwm"),
		m_keys(*this, "KEY")
	{ }

	void milano(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<mephisto_display2_device> m_display;
	required_device<pwm_display_device> m_led_pwm;
	required_ioport m_keys;

	u8 m_board_mux = 0;
	u8 m_led_data = 0;

	void milano_mem(address_map &map) ATTR_COLD;

	void update_leds();
	void io_w(u8 data);
	void board_w(u8 data);
	u8 board_r();
	u8 keys_r(offs_t offset);
};

void milano_state::machine_start()
{
	save_item(NAME(m_board_mux));
	save_item(NAME(m_led_data));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void milano_state::update_leds()
{
	m_led_pwm->matrix(m_board_mux, m_led_data);
}

void milano_state::io_w(u8 data)
{
	// default display module
	m_display->io_w(data & 0x0f);

	// high bits go to board leds
	m_led_data = data >> 4;
	update_leds();
}

void milano_state::board_w(u8 data)
{
	m_board_mux = ~data;
	update_leds();
}

u8 milano_state::board_r()
{
	u8 data = 0;

	// read chessboard sensors
	for (int i = 0; i < 8; i++)
		if (BIT(m_board_mux, i))
			data |= m_board->read_rank(i);

	return data;
}

u8 milano_state::keys_r(offs_t offset)
{
	return ~(BIT(m_keys->read(), offset) << 7);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void milano_state::milano_mem(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("nvram");
	map(0x1fc0, 0x1fff).unmaprw();
	map(0x1fc0, 0x1fc0).w(m_display, FUNC(mephisto_display2_device::latch_w));
	map(0x1fd0, 0x1fd0).w(FUNC(milano_state::board_w));
	map(0x1fe0, 0x1fe0).r(FUNC(milano_state::board_r));
	map(0x1fe8, 0x1fef).w("outlatch", FUNC(hc259_device::write_d7)).nopr();
	map(0x1fd8, 0x1fdf).r(FUNC(milano_state::keys_r));
	map(0x1ff0, 0x1ff0).w(FUNC(milano_state::io_w));
	map(0x2000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( milano )
	PORT_START("KEY")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Training / Pawn")   PORT_CODE(KEYCODE_T)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Info / Knight")     PORT_CODE(KEYCODE_I)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Memory / Bishop")   PORT_CODE(KEYCODE_M)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Position / Rook")   PORT_CODE(KEYCODE_P)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Level / Queen")     PORT_CODE(KEYCODE_L)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Function / King")   PORT_CODE(KEYCODE_F)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Enter / New Game")  PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CODE(KEYCODE_F1) // combine for NEW GAME
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Clear / New Game")  PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_F1) // "
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void milano_state::milano(machine_config &config)
{
	// basic machine hardware
	R65C02(config, m_maincpu, 4.9152_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &milano_state::milano_mem);

	const attotime nmi_period = attotime::from_hz(4.9152_MHz_XTAL / 0x2000);
	m_maincpu->set_periodic_int(FUNC(milano_state::nmi_line_pulse), nmi_period);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	hc259_device &outlatch(HC259(config, "outlatch"));
	outlatch.q_out_cb<0>().set_output("led100");
	outlatch.q_out_cb<1>().set_output("led101");
	outlatch.q_out_cb<2>().set_output("led102");
	outlatch.q_out_cb<3>().set_output("led103");
	outlatch.q_out_cb<4>().set_output("led104");
	outlatch.q_out_cb<5>().set_output("led105");

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_led_pwm).set_size(8, 2);

	MEPHISTO_DISPLAY_MODULE2(config, m_display); // internal
	config.set_default_layout(layout_mephisto_milano);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( milano ) // 1.02
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("milano_b958", 0x0000, 0x10000, CRC(0e9c8fe1) SHA1(e9176f42d86fe57e382185c703c7eff7e63ca711) )
ROM_END

ROM_START( milanoa ) // 1.01
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("milano_4af8", 0x0000, 0x10000, CRC(22efc0be) SHA1(921607d6dacf72c0686b8970261c43e2e244dc9f) )
ROM_END

ROM_START( nshort ) // 2.00
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("milano_3c59_nigel_short_21-sep-93", 0x0000, 0x10000, CRC(4bd51e23) SHA1(3f55cc1c55dae8818b7e9384b6b8d43dc4f0a1af) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT   COMPAT  MACHINE   INPUT   CLASS          INIT       COMPANY, FULLNAME, FLAGS
SYST( 1991, milano,    0,       0,      milano,   milano, milano_state, empty_init, "Hegener + Glaser", "Mephisto Milano (v1.02)", MACHINE_SUPPORTS_SAVE )
SYST( 1991, milanoa,   milano,  0,      milano,   milano, milano_state, empty_init, "Hegener + Glaser", "Mephisto Milano (v1.01)", MACHINE_SUPPORTS_SAVE )

SYST( 1993, nshort,    0,       0,      milano,   milano, milano_state, empty_init, "Hegener + Glaser", "Mephisto Nigel Short", MACHINE_SUPPORTS_SAVE )



milton6805.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Milton Bradley Milton

This is the talking tabletop game, not the chess computer with the same name.

Game 1: Match beginning of a phrase(red button) with end of phrase(yellow button).
Game 2: Same as game 1, but all in one turn.
Game 3: Press phrase end buttons, memorize them, press Go and match them.

Hardware is an odd combination: MC6805P2 MCU, GI SP0250 speech + 2*TMC0430 GROM.
See patent 4326710 for detailed information, except MC6805 clocked from SP0250 3.12MHz
and GROM clocked by 3.12MHz/8=390kHz.

*******************************************************************************/

#include "emu.h"
#include "cpu/m6805/m68705.h"
#include "machine/clock.h"
#include "machine/tmc0430.h"
#include "sound/sp0250.h"
#include "speaker.h"

// internal artwork
#include "milton.lh"

class milton_filter_device;


class milton_state : public driver_device
{
public:
	milton_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_grom(*this, "grom%u", 0),
		m_speech(*this, "sp0250"),
		m_filter(*this, "filter"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void milton(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(volume_changed);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<m6805_hmos_device> m_maincpu;
	required_device_array<tmc0430_device, 2> m_grom;
	required_device<sp0250_device> m_speech;
	required_device<milton_filter_device> m_filter;
	required_ioport_array<5> m_inputs;

	u8 m_data = 0;
	u8 m_control = 0xff;

	void data_w(u8 data);
	u8 data_r();
	void control_w(u8 data);
	u8 control_r();
	u8 input_r();
};

void milton_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_data));
	save_item(NAME(m_control));
}



/*******************************************************************************
    LED Filter
*******************************************************************************/

class milton_filter_device : public device_t, public device_sound_interface
{
public:
	milton_filter_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);

protected:
	virtual void device_start() override ATTR_COLD;
	virtual void sound_stream_update(sound_stream &stream) override;

private:
	sound_stream *m_stream = nullptr;
	output_finder<> m_led_out;
};

DEFINE_DEVICE_TYPE(MILTON_LED_FILTER, milton_filter_device, "milton_led_filter", "Milton LED Filter")


milton_filter_device::milton_filter_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	device_t(mconfig, MILTON_LED_FILTER, tag, owner, clock),
	device_sound_interface(mconfig, *this),
	m_led_out(*this, "led")
{ }

void milton_filter_device::device_start()
{
	m_stream = stream_alloc(1, 1, machine().sample_rate());
	m_led_out.resolve();
}

void milton_filter_device::sound_stream_update(sound_stream &stream)
{
	sound_stream::sample_t level = 0;

	for (int i = 0; i < stream.samples(); i++)
		level += fabsf(stream.get(0, i));

	stream.copy(0, 0);

	if (stream.samples() > 0)
		level /= stream.samples();

	// 2 leds connected to the audio circuit
	const sound_stream::sample_t threshold = 1500.0 / 32768.0;
	m_led_out = (level > threshold) ? 1 : 0;
}



/*******************************************************************************
    I/O
*******************************************************************************/

void milton_state::data_w(u8 data)
{
	// TMC0430 + SP0250 data
	m_data = data;
}

u8 milton_state::data_r()
{
	if (machine().side_effects_disabled())
		return 0;

	// TMC0430 data
	u8 data = 0;
	m_grom[0]->readz(&data);
	m_grom[1]->readz(&data);
	return data;
}

void milton_state::control_w(u8 data)
{
	// d0-d4: input mux

	// d5: SP0250 data present
	if (~m_control & data & 0x20)
		m_speech->write(m_data);

	// d1: TMC0430 M
	// d3: TMC0430 MO
	// d7: TMC0430 GS
	for (int i = 0; i < 2; i++)
	{
		m_grom[i]->m_line(BIT(data, 1));
		m_grom[i]->mo_line(BIT(data, 3));
		m_grom[i]->gsq_line(BIT(~data, 7));
	}

	// write pending TMC0430 data
	if (m_control & ~data & 0x80 && ~data & 2)
	{
		m_grom[0]->write(m_data);
		m_grom[1]->write(m_data);
	}

	m_control = data;
}

u8 milton_state::control_r()
{
	if (machine().side_effects_disabled())
		return 0;

	// d6: SP0250 data request
	// other: no function (DDRB = 0xbf)
	return m_speech->drq_r() ? 0x40 : 0;
}

u8 milton_state::input_r()
{
	u8 data = 0;

	// d0-d3: multiplexed inputs
	for (int i = 0; i < 5; i++)
		if (BIT(~m_control, i))
			data |= m_inputs[i]->read();

	return ~data;
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( milton )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Red Button 7")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Red Button 6")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Red Button 5")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Red Button 4")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Red Button 3")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Red Button 2")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Red Button 1")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Purple Button 1")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Purple Button 2")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Purple Button 3")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("Go")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Score")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Reset")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Yellow Button 1") // starting at top, then clockwise
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Yellow Button 2")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Yellow Button 3")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Yellow Button 4")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("Yellow Button 5")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Yellow Button 6")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_J) PORT_NAME("Yellow Button 7")

	PORT_START("VOLUME")
	PORT_CONFNAME( 0x01, 0x00, "Volume" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(milton_state::volume_changed), 0)
	PORT_CONFSETTING(    0x01, "Low" )
	PORT_CONFSETTING(    0x00, "High" )
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(milton_state::volume_changed)
{
	m_filter->set_output_gain(0, newval ? 0.25 : 1.0);
}



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void milton_state::milton(machine_config &config)
{
	// basic machine hardware
	M6805P2(config, m_maincpu, 3.12_MHz_XTAL);
	m_maincpu->porta_w().set(FUNC(milton_state::data_w));
	m_maincpu->porta_r().set(FUNC(milton_state::data_r));
	m_maincpu->portb_w().set(FUNC(milton_state::control_w));
	m_maincpu->portb_r().set(FUNC(milton_state::control_r));
	m_maincpu->portc_r().set(FUNC(milton_state::input_r));

	TMC0430(config, m_grom[0], "groms", 0x0000, 0);
	TMC0430(config, m_grom[1], "groms", 0x2000, 1);

	clock_device &gromclock(CLOCK(config, "gromclock", 3.12_MHz_XTAL/8));
	gromclock.signal_handler().set(m_grom[0], FUNC(tmc0430_device::gclock_in));
	gromclock.signal_handler().append(m_grom[1], FUNC(tmc0430_device::gclock_in));

	config.set_default_layout(layout_milton);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	SP0250(config, m_speech, 3.12_MHz_XTAL).add_route(0, m_filter, 1.0, 0);
	MILTON_LED_FILTER(config, m_filter).add_route(0, "speaker", 1.0);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( milton )
	ROM_REGION( 0x800, "maincpu", 0 )
	ROM_LOAD("sc87008p_783-4043-001", 0x000, 0x800, CRC(b054dbea) SHA1(b5339c8170e773b68505c3d60dc75249a583d60a) )

	ROM_REGION( 0x4000, "groms", ROMREGION_ERASE00 )
	ROM_LOAD("4043-003", 0x0000, 0x1800, CRC(d95df757) SHA1(6723480866f6393d310e304ef3b61e3a319a7beb) )
	ROM_LOAD("4043-004", 0x2000, 0x1800, CRC(9ac929f7) SHA1(1a27d56fc49eb4e58ea3b5c58d7fbedc5a751592) )
ROM_END



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1980, milton, 0,      0,      milton,  milton, milton_state, empty_init, "Milton Bradley", "Electronic Milton", MACHINE_SUPPORTS_SAVE )



milwaukee.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: TODO
/***********************************************************************************************************************************

2017-11-20 Skeleton

Milwaukee Computer MC-1000 series (MC-1000/1100/1200/1300/1400) all the same except for disk options.

Chips: SY6502, 2x 6821, 2x MC6850P, 6852, INS8253
Other: 2x 7-position rotary "dips" to select baud rates on each 6850 (19.2K, 9600, 4800, 2400, 1200, 600, 300).


Status:
- When booted it asks for a test to perform. Valid answers are A,B,D,E,H,I,M,O,P,S,W. To exit a test, press E.
         M displays * then loops. E just asks the Test question again. P gets caught in a loop (fdc test).
         When W pressed, N is displayed.


************************************************************************************************************************************/

#include "emu.h"
#include "cpu/m6502/m6502.h"
#include "machine/6821pia.h"
#include "machine/6850acia.h"
#include "machine/pit8253.h"
#include "machine/mc6852.h"
#include "machine/clock.h"
#include "bus/rs232/rs232.h"


namespace {

class milwaukee_state : public driver_device
{
public:
	milwaukee_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void milwaukee(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;
	required_device<cpu_device> m_maincpu;
};
void milwaukee_state::mem_map(address_map &map)
{
	map(0x0000, 0xf7ff).ram();
	//map(0xf800, 0xf87f) expansion i/o
	map(0xf880, 0xf881).rw("acia1", FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // terminal
	map(0xf882, 0xf883).rw("acia2", FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // remote
	map(0xf884, 0xf887).rw("pia1", FUNC(pia6821_device::read), FUNC(pia6821_device::write)); // centronics
	map(0xf888, 0xf88b).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xf88c, 0xf88f).rw("pia2", FUNC(pia6821_device::read), FUNC(pia6821_device::write)); // disk controller
	map(0xf890, 0xf891).rw("ssda", FUNC(mc6852_device::read), FUNC(mc6852_device::write));
	map(0xf898, 0xffff).rom().region("roms", 0x0098);
}

static INPUT_PORTS_START( milwaukee )
INPUT_PORTS_END

void milwaukee_state::milwaukee(machine_config &config)
{
	M6502(config, m_maincpu, 16_MHz_XTAL / 16);
	m_maincpu->set_addrmap(AS_PROGRAM, &milwaukee_state::mem_map);

	pit8253_device &pit(PIT8253(config, "pit", 0));
	pit.set_clk<0>(16_MHz_XTAL / 16 / 4); // 250 kHz
	pit.out_handler<0>().set("pit", FUNC(pit8253_device::write_gate0)).invert();
	pit.set_clk<1>(16_MHz_XTAL / 2 / 13 / 2048 / 5); // 60.09 Hz?
	pit.out_handler<1>().set("pit", FUNC(pit8253_device::write_clk2)).invert();

	PIA6821(config, "pia1");
	PIA6821(config, "pia2");
	ACIA6850(config, "acia2", 0);
	MC6852(config, "ssda", 0);

	clock_device &acia_clock(CLOCK(config, "acia_clock", 16_MHz_XTAL / 2 / 13 / 4));
	acia_clock.signal_handler().set("acia1", FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append("acia1", FUNC(acia6850_device::write_rxc));

	acia6850_device &acia1(ACIA6850(config, "acia1", 0));
	acia1.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	acia1.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("acia1", FUNC(acia6850_device::write_rxd));
	rs232.cts_handler().set("acia1", FUNC(acia6850_device::write_cts));
}

ROM_START( mc1200 )
	ROM_REGION( 0x0c00, "roms", 0 )
	ROM_LOAD( "mfm_6-29-82_10_sector.u15", 0x0000, 0x0800, CRC(40b0af66) SHA1(c988e1f90c9abb93171c4e40a6585ce9cc3fd495) )
	ROM_LOAD( "2758.u14", 0x0800, 0x0400, CRC(b20e2345) SHA1(da498cc0c746897a85d6f2d1a5bd70a726c1e4ef) ) // big white sticker, but nothing on it
ROM_END

} // anonymous namespace


COMP( 1980, mc1200, 0, 0, milwaukee, milwaukee, milwaukee_state, empty_init, "Milwaukee Computers", "MC-1200", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



mindset.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert

#include "emu.h"
#include "cpu/i86/i186.h"
#include "cpu/mcs48/mcs48.h"
#include "imagedev/floppy.h"
#include "machine/upd765.h"
#include "sound/dac.h"
#include "machine/ins8250.h"
#include "bus/rs232/rs232.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "mindset.lh"

// Missing:
// - Correct video timings
//   * screen_device does not handle interlaced screens yet

// - Correct timing of the specific-video-position interrupt
//   * see previous (so that partial screen update is correct) plus there's no "interrupt at a (x, y) position" interface

// - Interrupt from modules
//   * no use example, serial maybe?  And may be going through the system mcu

// - HD support module
//   * can't find the software that uses it.  Hand-drawn schematic at http://bitsavers.org/pdf/mindset/video_input/video_input_module_schematic.jpg (it's the same)

// - Modem modules
//   * no information on them, probably no software to use them either

// - Cartridges
//   * no dump of the rom ones, no users for the nvram ones.  gwbasic could use the nvram ones maybe?

// - Graphics CoProcessor timings
//   * need to count the accesses and estimate the mean access duration

// - GCP right-to-left blitting
//   * need to find a user, otherwise no way to know if the implementation is correct

// - GCP collision detection
//   * need to find a user, or at least a way to distinguish between the mask and the comparison value

// - GCP interrupt
//   * need to find a user, especially since it interacts with the system mcu

// - Genlock
//   * no osd support for video input

// - Capture card
//   * no osd support for video input.  Could try with the picture image device maybe?

// - Digitizing tablet
//   * no osd support, and a relatively rare device in the real world which is also hard to simulate on something else

// - Power control
//   * the system mcu controls the power to the system (some bit of p2 is seems), rocker switch on the keyboard resets the keyboard mcu for "on" and pretends sending a byte for "off".  Can be annoying UI-wise, and pretty much requires autosave.

class mindset_module_interface: public device_t {
public:
	virtual void map(address_map &map) = 0;
	virtual void idmap(address_map &map) = 0;

	auto irq_cb() { return m_irq_cb.bind(); }
	bool irq_r() const;

protected:
	bool m_irq_state;

	void irq_w(int state);

	mindset_module_interface(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
	virtual void device_start() override ATTR_COLD;
	virtual void device_reset() override ATTR_COLD;

private:
	devcb_write_line m_irq_cb;
};

void mindset_module_interface::device_start()
{
	save_item(NAME(m_irq_state));
}

void mindset_module_interface::device_reset()
{
	m_irq_state = false;
	m_irq_cb(m_irq_state);
}

mindset_module_interface::mindset_module_interface(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
	device_t(mconfig, type, tag, owner, clock),
	m_irq_cb(*this)
{
}

void mindset_module_interface::irq_w(int state)
{
	m_irq_state = state;
	m_irq_cb(m_irq_state);
}

class mindset_module: public device_t,
					  public device_slot_interface
{
public:
	template <typename T>
	mindset_module(const machine_config &mconfig, const char *tag, device_t *owner, T &&opts, const char *dflt, bool fixed = false)
		: mindset_module(mconfig, tag, owner, 0)
	{
		option_reset();
		opts(*this);
		set_default_option(dflt);
		set_fixed(fixed);
	}
	mindset_module(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
	virtual ~mindset_module() = default;

	void map(address_space &space, offs_t base, bool id);

protected:
	virtual void device_validity_check(validity_checker &valid) const override;
	virtual void device_start() override ATTR_COLD;
};

DEFINE_DEVICE_TYPE(MINDSET_MODULE, mindset_module,  "mindset_module", "MINDSET module")

mindset_module::mindset_module(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	device_t(mconfig, MINDSET_MODULE, tag, owner, clock),
	device_slot_interface(mconfig, *this)
{
}

void mindset_module::device_validity_check(validity_checker &valid) const
{
	device_t *const carddev = get_card_device();
	if (carddev && !dynamic_cast<mindset_module_interface *>(carddev))
		osd_printf_error("Card device %s (%s) does not implement mindset_module_interface\n", carddev->tag(), carddev->name());
}

void mindset_module::device_start()
{
}

void mindset_module::map(address_space &space, offs_t base, bool id)
{
	mindset_module_interface *module = dynamic_cast<mindset_module_interface *>(get_card_device());
	if(module)
		space.install_device(base, base+0x3f, *module, id ? &mindset_module_interface::idmap : &mindset_module_interface::map);
}


class mindset_sound_module: public mindset_module_interface {
public:
	mindset_sound_module(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
	virtual ~mindset_sound_module() = default;

	virtual void map(address_map &map) override ATTR_COLD;
	virtual void idmap(address_map &map) override ATTR_COLD;

protected:
	virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD;
	virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
	virtual void device_start() override ATTR_COLD;
	virtual void device_reset() override ATTR_COLD;

private:
	u8 m_p1 = 0, m_p2 = 0;

	required_device<i8042_device> m_soundcpu;
	required_device<dac_byte_interface> m_dac;

	void p1_w(u8 data);
	void p2_w(u8 data);
	void update_dac();
};

DEFINE_DEVICE_TYPE(MINDSET_SOUND_MODULE, mindset_sound_module,  "mindset_sound_module", "MINDSET stereo sound module")

mindset_sound_module::mindset_sound_module(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	mindset_module_interface(mconfig, MINDSET_SOUND_MODULE, tag, owner, clock),
	m_soundcpu(*this, "soundcpu"),
	m_dac(*this, "dac")
{
}

void mindset_sound_module::device_start()
{
	mindset_module_interface::device_start();
	save_item(NAME(m_p1));
	save_item(NAME(m_p2));
}

void mindset_sound_module::device_reset()
{
	mindset_module_interface::device_reset();
	m_p1 = 0x80;
	m_p2 = 0;
	m_dac->write(0x80);
}

void mindset_sound_module::update_dac()
{
	// The p1 dac has the waveform (idle at 0x80), while the p2 one is used for the global volume (mute at 0x00, max at 0xff)
	m_dac->write((s8(m_p1-0x80)*m_p2/255 + 0x80) & 0xff);
}

void mindset_sound_module::p1_w(u8 data)
{
	m_p1 = data;
	update_dac();
}

void mindset_sound_module::p2_w(u8 data)
{
	m_p2 = data;
	update_dac();
}

void mindset_sound_module::map(address_map &map)
{
	map(0x00, 0x03).rw(m_soundcpu, FUNC(i8042_device::upi41_master_r), FUNC(i8042_device::upi41_master_w)).umask16(0x00ff).mirror(0x3c);
}

void mindset_sound_module::idmap(address_map &map)
{
	map(0x00, 0x3f).lr8(NAME([]() -> u8 { return 0x13; })).umask16(0x00ff);
}

ROM_START(mindset_sound_module)
	ROM_REGION(0x0800, "soundcpu", 0)
	ROM_LOAD("253006-001.u16", 0, 0x800, CRC(7bea5edd) SHA1(30cdc0dedaa5246f4952df452a99ca22e3cd0636))
ROM_END

const tiny_rom_entry *mindset_sound_module::device_rom_region() const
{
	return ROM_NAME(mindset_sound_module);
}

void mindset_sound_module::device_add_mconfig(machine_config &config)
{
	I8042(config, m_soundcpu, 12_MHz_XTAL/2);
	m_soundcpu->p1_out_cb().set(FUNC(mindset_sound_module::p1_w));
	m_soundcpu->p2_out_cb().set(FUNC(mindset_sound_module::p2_w));
	DAC_8BIT_R2R(config, m_dac, 0).add_route(ALL_OUTPUTS, ":speaker", 0.5, 1);
}


class mindset_rs232_module: public mindset_module_interface {
public:
	mindset_rs232_module(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
	virtual ~mindset_rs232_module() = default;

	virtual void map(address_map &map) override ATTR_COLD;
	virtual void idmap(address_map &map) override ATTR_COLD;

protected:
	virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;

private:
	required_device<ins8250_device> m_ins8250;
	required_device<rs232_port_device> m_rs232;
};

DEFINE_DEVICE_TYPE(MINDSET_RS232_MODULE, mindset_rs232_module,  "mindset_rs232_module", "MINDSET RS232 module")

mindset_rs232_module::mindset_rs232_module(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	mindset_module_interface(mconfig, MINDSET_RS232_MODULE, tag, owner, clock),
	m_ins8250(*this, "ins8250"),
	m_rs232(*this, "rs232")
{
}

void mindset_rs232_module::map(address_map &map)
{
	map(0x00, 0x0f).rw(m_ins8250, FUNC(ins8250_device::ins8250_r), FUNC(ins8250_device::ins8250_w)).umask16(0x00ff).mirror(0x30);
}

void mindset_rs232_module::idmap(address_map &map)
{
	map(0x00, 0x3f).lr8(NAME([this]() -> u8 { return 0x73 | (m_irq_state ? 0x80 : 0x00); })).umask16(0x00ff);
}

void mindset_rs232_module::device_add_mconfig(machine_config &config)
{
	INS8250(config, m_ins8250, 12_MHz_XTAL/2/4); // Weird since there's no divider in the module
	m_ins8250->out_tx_callback().set(m_rs232, FUNC(rs232_port_device::write_txd));
	m_ins8250->out_int_callback().set(FUNC(mindset_rs232_module::irq_w));

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set(m_ins8250, FUNC(ins8250_device::rx_w));
	m_rs232->dsr_handler().set(m_ins8250, FUNC(ins8250_device::dsr_w));
}



class mindset_state: public driver_device
{
public:
	mindset_state(const machine_config &mconfig, device_type type, const char *tag);
	virtual ~mindset_state() = default;

	void mindset(machine_config &config);

protected:
	required_device<i80186_cpu_device> m_maincpu;
	required_device<i8042_device> m_syscpu, m_soundcpu;
	required_device<i8749_device> m_kbdcpu;
	required_device<screen_device> m_screen;
	required_device<i8272a_device> m_fdc;
	required_device<floppy_connector> m_fdco[2];
	required_shared_ptr<u16> m_vram;
	required_ioport_array<11> m_kbd_row;
	required_ioport_array<2> m_mouse_axis;
	required_ioport m_mouse_btn, m_joystick;
	output_finder<2> m_floppy_leds;
	output_finder<> m_red_led;
	output_finder<> m_yellow_led;
	output_finder<> m_green_led;
	required_device<dac_byte_interface> m_dac;
	required_device_array<mindset_module, 6> m_modules;

	memory_access<20, 1, 0, ENDIANNESS_LITTLE>::cache m_gcps;

	floppy_image_device *m_floppy[2];
	u32 m_palette[16];
	bool m_genlock[16];
	u16 m_dispctrl, m_screenpos, m_intpos, m_intaddr, m_fdc_dma_count;
	u8 m_kbd_p1, m_kbd_p2, m_borderidx, m_snd_p1, m_snd_p2, m_sys_p2;
	u8 m_mouse_last_read[2], m_mouse_counter[2];
	bool m_fdc_intext, m_fdc_int, m_fdc_drq, m_trap_int, m_trap_drq;

	u16 m_trap_data[8];
	u32 m_trap_pos, m_trap_len;

	static u16 gcp_blend_0(u16, u16);
	static u16 gcp_blend_1(u16, u16);
	static u16 gcp_blend_2(u16, u16);
	static u16 gcp_blend_3(u16, u16);
	static u16 gcp_blend_4(u16, u16);
	static u16 gcp_blend_5(u16, u16);
	static u16 gcp_blend_6(u16, u16);
	static u16 gcp_blend_7(u16, u16);

	static u16 (*const gcp_blend[8])(u16, u16);

	static inline u16 msk(int bit) { return (1U << bit) - 1; }
	static inline u16 sw(u16 data) { return (data >> 8) | (data << 8); }

	void maincpu_mem(address_map &map) ATTR_COLD;
	void maincpu_io(address_map &map) ATTR_COLD;

	void display_mode();
	void blit(u16 packet_seg, u16 packet_adr);

	void gcp_w(u16);
	u16 dispctrl_r();
	void dispctrl_w(u16 data);
	u16 dispreg_r();
	void dispreg_w(u16 data);

	int sys_t0_r();
	int sys_t1_r();
	u8 sys_p1_r();
	u8 sys_p2_r();
	void sys_p1_w(u8 data);
	void sys_p2_w(u8 data);

	void kbd_p1_w(u8 data);
	void kbd_p2_w(u8 data);
	int kbd_t1_r();
	u8 kbd_d_r();

	void snd_p1_w(u8 data);
	void snd_p2_w(u8 data);

	void fdc_ctrl_w(u8 data);
	void fdc_int_w(int state);
	u16 fdc_clear_interrupt();
	void fdc_dma_count_w(u16 data);
	u8 fdc_dma_r();
	void fdc_dma_w(u8 data);

	u16 trap_dma_r(offs_t, u16);
	u16 trap_r(offs_t offset);
	void trap_w(offs_t offset, u16 data);
	u16 trap_clear_interrupt();

	template<int floppy> void floppy_led_cb(floppy_image_device *, int state);

	u16 keyscan();
	void update_dac();
	void map_modules();

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
};


mindset_state::mindset_state(const machine_config &mconfig, device_type type, const char *tag) :
	driver_device(mconfig, type, tag),
	m_maincpu(*this, "maincpu"),
	m_syscpu(*this, "syscpu"),
	m_soundcpu(*this, "soundcpu"),
	m_kbdcpu(*this, "kbdcpu"),
	m_screen(*this, "screen"),
	m_fdc(*this, "fdc"),
	m_fdco{{*this, "fdc:0"}, {*this, "fdc:1"}},
	m_vram(*this, "vram"),
	m_kbd_row(*this, "K%02u", 0U),
	m_mouse_axis(*this, "MOUSEAXIS%u", 0U),
	m_mouse_btn(*this, "MOUSEBTN"),
	m_joystick(*this, "JOYSTICK"),
	m_floppy_leds(*this, "drive%u_led", 0U),
	m_red_led(*this, "red_led"),
	m_yellow_led(*this, "yellow_led"),
	m_green_led(*this, "green_led"),
	m_dac(*this, "dac"),
	m_modules(*this, "m%d", 0U)
{
}

template<int floppy> void mindset_state::floppy_led_cb(floppy_image_device *, int state)
{
	m_floppy_leds[floppy] = state;
}

void mindset_state::machine_start()
{
	m_floppy_leds.resolve();
	m_red_led.resolve();
	m_yellow_led.resolve();
	m_green_led.resolve();

	m_maincpu->space(AS_PROGRAM).cache(m_gcps);
	for(int i=0; i<2; i++)
		m_floppy[i] = m_fdco[i]->get_device();

	if(m_floppy[0])
		m_floppy[0]->setup_led_cb(floppy_image_device::led_cb(&mindset_state::floppy_led_cb<0>, this));
	if(m_floppy[1])
		m_floppy[1]->setup_led_cb(floppy_image_device::led_cb(&mindset_state::floppy_led_cb<1>, this));

	save_item(NAME(m_fdc_intext));
	save_item(NAME(m_fdc_int));
	save_item(NAME(m_fdc_drq));
	save_item(NAME(m_trap_int));
	save_item(NAME(m_trap_drq));
	save_item(NAME(m_trap_pos));
	save_item(NAME(m_trap_len));
	save_item(NAME(m_trap_data));
	save_item(NAME(m_palette));
	save_item(NAME(m_genlock));
	save_item(NAME(m_dispctrl));
	save_item(NAME(m_screenpos));
	save_item(NAME(m_intpos));
	save_item(NAME(m_intaddr));
	save_item(NAME(m_fdc_dma_count));
	save_item(NAME(m_kbd_p1));
	save_item(NAME(m_kbd_p2));
	save_item(NAME(m_borderidx));
	save_item(NAME(m_mouse_last_read));
	save_item(NAME(m_mouse_counter));
	save_item(NAME(m_snd_p1));
	save_item(NAME(m_snd_p2));
	save_item(NAME(m_sys_p2));
}

void mindset_state::map_modules()
{
	auto &space = m_maincpu->space(AS_IO);
	bool id = !(m_sys_p2 & 0x40);
	space.unmap_readwrite(0x8080, 0x81ff);
	for(int i=0; i<6; i++)
		m_modules[i]->map(space, 0x8080 + 0x40*i, id);
}

void mindset_state::machine_reset()
{
	m_sys_p2 = 0;
	map_modules();
	m_fdc_intext = m_fdc_int = m_trap_int = m_fdc_drq = m_trap_drq = false;
	m_trap_pos = m_trap_len = 0;
	memset(m_trap_data, 0, sizeof(m_trap_data));
	memset(m_palette, 0, sizeof(m_palette));
	memset(m_genlock, 0, sizeof(m_genlock));
	m_dispctrl = m_screenpos = m_intpos = m_intaddr = m_fdc_dma_count = 0;
	m_kbd_p1 = m_kbd_p2 = m_borderidx = 0;
	memset(m_mouse_last_read, 0, sizeof(m_mouse_last_read));
	memset(m_mouse_counter, 0, sizeof(m_mouse_counter));
	m_snd_p1 = 0x80;
	m_snd_p2 = 0;
	m_dac->write(0x80);
}

int mindset_state::sys_t0_r()
{
	//  logerror("SYS: %d read t0 %d (%03x)\n", m_kbdcpu->total_cycles(), (m_kbd_p2 & 0x40) != 0, m_syscpu->pc());
	return (m_kbd_p2 & 0x40) != 0;
}

int mindset_state::sys_t1_r()
{
	logerror("SYS: read t1\n");
	return !m_fdc_int;
}

u8 mindset_state::sys_p1_r()
{
	//  logerror("SYS: read p1\n");
	return 0xff;
}

u8 mindset_state::sys_p2_r()
{
	//  logerror("SYS: read p2 (%03x)\n", m_syscpu->pc());
	return 0xff;
}

void mindset_state::sys_p1_w(u8 data)
{
	//  m_maincpu->int0_w(!((data & 0x40) || m_fdc->get_irq()));
	logerror("SYS: fdc write p1 %02x irq %d\n", data, !!(data & 0x40));
}

void mindset_state::sys_p2_w(u8 data)
{
	u8 old = m_sys_p2;
	m_sys_p2 = data;
	m_yellow_led = !BIT(data, 0);
	m_green_led = !BIT(data, 1);
	m_red_led = !BIT(data, 2);
	if((m_sys_p2 ^ old) & 0x40)
		map_modules();
	m_maincpu->int3_w(!(data & 0x80));
	//  logerror("SYS: write p2 %02x\n", data);
}

void mindset_state::kbd_p1_w(u8 data)
{
	m_kbd_p1 = data;
}

void mindset_state::kbd_p2_w(u8 data)
{
	//  if((m_kbd_p2 ^ data) & 0x40)
	//      logerror("KBD: %d output bit %d\n", m_kbdcpu->total_cycles(), (m_kbd_p2 & 0x40) != 0);
	m_kbd_p2 = data;
}

u8 mindset_state::kbd_d_r()
{
	return keyscan();
}

int mindset_state::kbd_t1_r()
{
	return keyscan() & 0x100;
}

void mindset_state::update_dac()
{
	// The p1 dac has the waveform (idle at 0x80), while the p2 one is used for the global volume (mute at 0x00, max at 0xff)
	m_dac->write((s8(m_snd_p1-0x80)*m_snd_p2/255 + 0x80) & 0xff);
}

void mindset_state::snd_p1_w(u8 data)
{
	m_snd_p1 = data;
	update_dac();
}

void mindset_state::snd_p2_w(u8 data)
{
	m_snd_p2 = data;
	update_dac();
}

u16 mindset_state::keyscan()
{
	u16 src = (m_kbd_p2 << 8) | m_kbd_p1;
	u16 res = 0x1ff;
	for(unsigned int i=0; i<11; i++)
		if(!(src & (1 << i)))
			res &= m_kbd_row[i]->read();

	if(!(src & 0x8000)) {
		int axis = (src >> 12) & 1;
		u8 aval = m_mouse_axis[axis]->read();
		m_mouse_counter[axis] += aval - m_mouse_last_read[axis];
		m_mouse_last_read[axis] = aval;

		u8 nib;
		if((src >> 11) & 1) {
			nib = m_mouse_counter[axis] & 0xf;
			m_mouse_counter[axis] &= 0xf0;
		} else {
			nib = m_mouse_counter[axis] >> 4;
			m_mouse_counter[axis] &= 0x0f;
		}

		res &= m_mouse_btn->read() & (nib | 0x1f0);
	}

	if(!(src & 0x2000))
		res &= m_joystick->read();

	return res;
}



u16 mindset_state::dispctrl_r()
{
	return m_dispctrl;
}

void mindset_state::dispctrl_w(u16 data)
{
	u16 chg = m_dispctrl ^ data;
	m_dispctrl = data;
	if(chg & 0xff88)
		logerror("display control %s bank=%c %s %s h=%d ppx=%d w=%s interlace=%d rreg=%d indicator=%s wreg=%d\n",
				 m_dispctrl & 0x8000 ? "?15" : "?!15",
				 m_dispctrl & 0x4000 ? '1' : '0',
				 m_dispctrl & 0x2000 ? "ibm" : "native",
				 m_dispctrl & 0x1000 ? "?12" : "?!12",
				 m_dispctrl & 0x0800 ? "400" : "200",
				 (m_dispctrl & 0x0600) >> 9,
				 m_dispctrl & 0x0100 ? "320" : "640",
				 m_dispctrl & 0x0080 ? "on" : "off",
				 (m_dispctrl & 0x0070) >> 4,
				 m_dispctrl & 0x0008 ? "on" : "off",
				 m_dispctrl & 7);
}

u16 mindset_state::dispreg_r()
{
	switch((m_dispctrl >> 4) & 7) {
	case 1: { // Read vram at interrupt position
		u16 v = m_vram[m_intaddr >> 1];
		if(m_intaddr & 1)
			v >>= 8;
		return sw(v << 4);
		break;
	}
	case 5: {
		// wants 0080 set to be able to upload the palette
		// may be a field indicator
		return 0x0080;
	}
	}

	logerror("dispreg read %x\n", (m_dispctrl >> 4) & 7);
	return 0;
}

void mindset_state::dispreg_w(u16 data)
{
	switch(m_dispctrl & 0x7) {
	case 0:
		m_screenpos = data;
		logerror("screen position (%d, %d)\n", (data >> 8) & 15, (data >> 12) & 15);
		break;
	case 1:
		m_borderidx = (data >> 8) & 0xf;
		logerror("border color %x\n", m_borderidx);
		break;
	case 2: {
		m_intpos = data;
		int intx = (159 - ((m_intpos >> 8) & 255)) * ((m_dispctrl & 0x100) ? 2 : 4);
		int inty = 199 - (m_intpos & 255);
		m_intaddr = 0;
		bool bank = m_dispctrl & 0x4000;
		bool ibm_mode = m_dispctrl & 0x2000;
		//      bool interleave = m_dispctrl & 0x0800;
		int pixels_per_byte_order = (m_dispctrl & 0x0600) >> 9;
		bool large_pixels = m_dispctrl & 0x0100;
		if(ibm_mode)
			m_intaddr = (inty & 1) * 0x2000 + (inty >> 1) * 80 + (intx >> (3 - large_pixels)) ;
		else {
			int stepy = 0;
			if(pixels_per_byte_order == 2) stepy = 40;
			if(pixels_per_byte_order == 1) stepy = 80;
			if(pixels_per_byte_order == 0) stepy = 160;

			m_intaddr = inty * stepy + (intx >> (pixels_per_byte_order - large_pixels + 2));
		}

		if(bank)
			m_intaddr += 16000;

		logerror("interrupt position (%3d, %3d) ramdac address %04x\n", intx, inty, m_intaddr);
		break;
	}
	case 4: {
		data = sw(data);
		u8 r = (0x49*(data & 7)) >> 1;
		u8 g = (0x49*((data & 0x38) >> 3)) >> 1;
		u8 b = (0x49*((data & 0x1c0) >> 6)) >> 1;

		m_palette[m_borderidx] = (r << 16) | (g << 8) | b;
		m_genlock[m_borderidx] = data & 0x0200;
		logerror("palette[%x] = %04x -> %06x.%d\n", m_borderidx, data, m_palette[m_borderidx], m_genlock[m_borderidx]);
		m_borderidx = (m_borderidx + 1) & 0xf;
		break;
	}
	case 5: {
		logerror("genlock %s%s, extra=%c\n", data & 0x0200 ? "on" : "off", data & 0x0100 ? " fixed" : "", data & 0x0400 ? '1' : '0');
		break;
	}

	default:
		logerror("display reg[%x] = %04x\n", m_dispctrl & 0xf, data);
	}
}

u32 mindset_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	// Temporary gross hack
	if(cliprect.max_y != 479)
		return 0;
	const u16 *bank = m_dispctrl & 0x4000 ? m_vram + 8000 : m_vram;
	bool ibm_mode = m_dispctrl & 0x2000;
	bool interleave = m_dispctrl & 0x0800;
	int pixels_per_byte_order = (m_dispctrl & 0x0600) >> 9;
	bool large_pixels = m_dispctrl & 0x0100;

	bitmap.fill(m_palette[m_borderidx]);

	int dx = ((m_screenpos >>  8) & 15) * (751 - 640) / 15;
	int dy = ((m_screenpos >> 12) & 15) * (480 - 400) / 15;

	if(ibm_mode) {
		if(large_pixels) {
			static int const palind[4] = { 0, 1, 4, 5 };
			for(int field=0; field<2; field++) {
				for(u32 yy=0; yy<2; yy++) {
					const u16 *src = bank + 4096*yy;
					for(u32 y=yy; y<200; y+=2) {
						u32 *dest = &bitmap.pix(2*y+field+dy, dx);
						for(u32 x=0; x<320; x+=8) {
							u16 sv = sw(*src++);
							for(u32 xx=0; xx<8; xx++) {
								u32 color = m_palette[palind[(sv >> (14-2*xx)) & 3]];
								*dest++ = color;
								*dest++ = color;
							}
						}
					}
				}
			}
			return 0;
		} else {
			static int const palind[2] = { 0, 4 };
			for(int field=0; field<2; field++) {
				for(u32 yy=0; yy<2; yy++) {
					const u16 *src = bank + 4096*yy;
					for(u32 y=yy; y<200; y+=2) {
						u32 *dest = &bitmap.pix(2*y+field+dy, dx);
						for(u32 x=0; x<640; x+=16) {
							u16 sv = sw(*src++);
							for(u32 xx=0; xx<16; xx++) {
								u32 color = m_palette[palind[(sv >> (15-xx)) & 1]];
								*dest++ = color;
							}
						}
					}
				}
			}
			return 0;
		}
	} else {
		if(large_pixels) {
			if(!interleave) {
				switch(pixels_per_byte_order) {
				case 0: {
					const u16 *src = bank;
					for(u32 y=0; y<200; y++) {
						u32 *dest0 = &bitmap.pix(2*y+dy, dx);
						u32 *dest1 = &bitmap.pix(2*y+1+dy, dx);
						for(u32 x=0; x<320; x+=4) {
							u16 sv = sw(*src++);
							for(u32 xx=0; xx<4; xx++) {
								u32 color = m_palette[(sv >> (12-4*xx)) & 15];
								*dest0++ = color;
								*dest0++ = color;
								*dest1++ = color;
								*dest1++ = color;
							}
						}
					}
					return 0;
				}
				case 1: {
					static int const palind[4] = { 0, 1, 4, 5 };
					const u16 *src = bank;
					for(u32 y=0; y<200; y++) {
						u32 *dest0 = &bitmap.pix(2*y+dy, dx);
						u32 *dest1 = &bitmap.pix(2*y+1+dy, dx);
						for(u32 x=0; x<320; x+=8) {
							u16 sv = sw(*src++);
							for(u32 xx=0; xx<8; xx++) {
								u32 color = m_palette[palind[(sv >> (14-2*xx)) & 3]];
								*dest0++ = color;
								*dest0++ = color;
								*dest1++ = color;
								*dest1++ = color;
							}
						}
					}
					return 0;
				}
				case 2: {
					const u16 *src = bank;
					for(u32 y=0; y<200; y++) {
						u32 *dest0 = &bitmap.pix(2*y+dy, dx);
						u32 *dest1 = &bitmap.pix(2*y+1+dy, dx);
						for(u32 x=0; x<320; x+=16) {
							u16 sv = sw(*src++);
							for(u32 xx=0; xx<16; xx++) {
								u32 color = m_palette[(sv >> (15-xx)) & 1];
								*dest0++ = color;
								*dest0++ = color;
								*dest1++ = color;
								*dest1++ = color;
							}
						}
					}
					return 0;
				}
				}
			} else {
				switch(pixels_per_byte_order) {
				case 0: {
					const u16 *src = bank;
					for(u32 y=0; y<400; y++) {
						u32 *dest = &bitmap.pix(y+dy, dx);
						for(u32 x=0; x<320; x+=4) {
							u16 sv = sw(*src++);
							for(u32 xx=0; xx<4; xx++) {
								u32 color = m_palette[(sv >> (12-4*xx)) & 15];
								*dest++ = color;
								*dest++ = color;
							}
						}
					}
					return 0;
				}
				case 1: {
					static int const palind[4] = { 0, 1, 4, 5 };
					const u16 *src = bank;
					for(u32 y=0; y<400; y++) {
						u32 *dest = &bitmap.pix(y+dy, dx);
						for(u32 x=0; x<320; x+=8) {
							u16 sv = sw(*src++);
							for(u32 xx=0; xx<8; xx++) {
								u32 color = m_palette[palind[(sv >> (14-2*xx)) & 3]];
								*dest++ = color;
								*dest++ = color;
							}
						}
					}
					return 0;
				}
				}
			}
		} else {
			if(!interleave) {
				switch(pixels_per_byte_order) {
				case 0: {
					static int const palind[4] = { 0, 4, 8, 12 };
					const u16 *src = bank;
					for(u32 y=0; y<200; y++) {
						u32 *dest0 = &bitmap.pix(2*y+dy, dx);
						u32 *dest1 = &bitmap.pix(2*y+1+dy, dx);
						for(u32 x=0; x<640; x+=8) {
							u16 sv = sw(*src++);
							for(u32 xx=0; xx<8; xx++) {
								u32 color = m_palette[palind[(sv >> (14-2*xx)) & 3]];
								*dest0++ = color;
								*dest1++ = color;
							}
						}
					}
					return 0;
				}
				case 1: {
					static int const palind[2] = { 0, 4 };
					const u16 *src = bank;
					for(u32 y=0; y<200; y++) {
						u32 *dest0 = &bitmap.pix(2*y+dy, dx);
						u32 *dest1 = &bitmap.pix(2*y+1+dy, dx);
						for(u32 x=0; x<640; x+=16) {
							u16 sv = sw(*src++);
							for(u32 xx=0; xx<16; xx++) {
								u32 color = m_palette[palind[(sv >> (15-xx)) & 1]];
								*dest0++ = color;
								*dest1++ = color;
							}
						}
					}
					return 0;
				}
				}
			} else {
				static int const palind[2] = { 0, 4 };
				const u16 *src = bank;
				for(u32 y=0; y<400; y++) {
					u32 *dest = &bitmap.pix(y+dy, dx);
					for(u32 x=0; x<640; x+=16) {
						u16 sv = sw(*src++);
						for(u32 xx=0; xx<16; xx++) {
							u32 color = m_palette[palind[(sv >> (15-xx)) & 1]];
							*dest++ = color;
						}
					}
				}
				return 0;
			}
		}

		logerror("Unimplemented native mode (%dx%d, ppb=%d)\n", large_pixels ? 320 : 640, interleave ? 400 : 200, 2 << pixels_per_byte_order);
	}

	bitmap.fill(0);

	return 0;
}

u16 mindset_state::gcp_blend_0(u16 src, u16)
{
	return src;
}

u16 mindset_state::gcp_blend_1(u16 src, u16 dst)
{
	return src & dst;
}

u16 mindset_state::gcp_blend_2(u16 src, u16 dst)
{
	return src | dst;
}

u16 mindset_state::gcp_blend_3(u16 src, u16 dst)
{
	return src ^ dst;
}

u16 mindset_state::gcp_blend_4(u16 src, u16)
{
	return ~src;
}

u16 mindset_state::gcp_blend_5(u16 src, u16 dst)
{
	return (~src) & dst;
}

u16 mindset_state::gcp_blend_6(u16 src, u16 dst)
{
	return (~src) | dst;
}

u16 mindset_state::gcp_blend_7(u16 src, u16 dst)
{
	return (~src) ^ dst;
}

u16 (*const mindset_state::gcp_blend[8])(u16, u16) = {
	gcp_blend_0,
	gcp_blend_1,
	gcp_blend_2,
	gcp_blend_3,
	gcp_blend_4,
	gcp_blend_5,
	gcp_blend_6,
	gcp_blend_7
};


void mindset_state::blit(u16 packet_seg, u16 packet_adr)
{
	u16 mode    = sw(m_gcps.read_word((packet_seg << 4) + ((packet_adr +  0) & 0xffff)));
	u16 src_adr = sw(m_gcps.read_word((packet_seg << 4) + ((packet_adr +  2) & 0xffff)));
	u16 src_sft = sw(m_gcps.read_word((packet_seg << 4) + ((packet_adr +  4) & 0xffff)));
	u16 dst_adr = sw(m_gcps.read_word((packet_seg << 4) + ((packet_adr +  6) & 0xffff)));
	u16 dst_sft = sw(m_gcps.read_word((packet_seg << 4) + ((packet_adr +  8) & 0xffff)));
	u16 width   = sw(m_gcps.read_word((packet_seg << 4) + ((packet_adr + 10) & 0xffff)));
	u16 height  = sw(m_gcps.read_word((packet_seg << 4) + ((packet_adr + 12) & 0xffff)));
	u16 sy      = sw(m_gcps.read_word((packet_seg << 4) + ((packet_adr + 14) & 0xffff)));
	u16 dy      = sw(m_gcps.read_word((packet_seg << 4) + ((packet_adr + 16) & 0xffff)));
	u16 rmask   = sw(m_gcps.read_word((packet_seg << 4) + ((packet_adr + 18) & 0xffff)));
	u16 src_seg = sw(m_gcps.read_word((packet_seg << 4) + ((packet_adr + 20) & 0xffff)));
	u16 dst_seg = sw(m_gcps.read_word((packet_seg << 4) + ((packet_adr + 22) & 0xffff)));
	u16 wmask   = sw(m_gcps.read_word((packet_seg << 4) + ((packet_adr + 24) & 0xffff)));
	u16 kmask   = sw(m_gcps.read_word((packet_seg << 4) + ((packet_adr + 26) & 0xffff)));

	// -f-w wnpi ktxm mm--
	// f = fast (pure word copy)
	// w = pixel width (1/2/4/8 bits)
	// n = invert collision flag (unimplemented)
	// p = pattern fill (used by fill_dest_buffer)
	// i/f = increment source / don't (used by blt_copy_word)
	// t/o = transparent/opaque
	// k = detect collision (unimplemented)
	// x = go right to left (unimplemented)
	// m = blending mode

	if(0)
	logerror("GCP: p src %04x:%04x.%x dst %04x:%04x.%x sz %xx%x step %x:%x mask %04x:%04x k %x:%x mode %c%d%c%c%c%c%c%c%d\n", src_seg, src_adr, src_sft, dst_seg, dst_adr, dst_sft, width, height, sy, dy, rmask, wmask, (kmask >> 8) & 15, kmask & 15,
			 mode & 0x4000 ? 'f' : '-',
			 (mode >> 11) & 3,
			 mode & 0x400 ? 'n' : '-',
			 mode & 0x200 ? 'p' : '-',
			 mode & 0x100 ? 'i' : 'f',
			 mode & 0x80 ? 'k' : '-',
			 mode & 0x40 ? 't' : 'o',
			 mode & 0x20 ? 'x' : '-',
			 (mode >> 2) & 7);

	if(mode & 0x200) {
		// pattern fill
		u16 src = m_gcps.read_word((src_seg << 4) + src_adr);
		for(u16 w=0; w != width/2; w++) {
			m_gcps.write_word((dst_seg << 4) + dst_adr, src);
			dst_adr += 2;
		}

	} else if(mode & 0x4000) {
		// fast mode
		u32 nw = (width+15) >> 4;
		for(u32 y=0; y<height; y++) {
			u16 src_cadr = src_adr;
			u16 dst_cadr = dst_adr;
			for(u32 w=0; w!=nw; w++) {
				m_gcps.write_word((dst_seg << 4) + dst_cadr, m_gcps.read_word((src_seg << 4) + src_cadr));
				src_cadr += 2;
				dst_cadr += 2;
			}
			src_adr += sy;
			dst_adr += dy;
		}

	} else {
		auto blend = gcp_blend[(mode >> 2) & 7];

		// Need to rotate rmask depending on the shifts too
		u16 awmask = ((wmask << 16) | wmask) >> (15 - dst_sft);
		u16 swmask, mwmask, ewmask;
		if(dst_sft >= width) {
			swmask = msk(dst_sft+1) & ~msk(dst_sft - width + 1);
			mwmask = 0xffff;
			ewmask = swmask;
		} else {
			swmask = msk(dst_sft+1);
			mwmask = 0xffff;
			ewmask = ~msk((dst_sft - width + 1) & 15);
		}

		swmask &= awmask;
		mwmask &= awmask;
		ewmask &= awmask;

		bool preload = dst_sft > src_sft;
		int src_do_sft = 15 - src_sft;
		int dst_do_sft = (preload ? 31 : 15) - dst_sft;

		u16 nw = ((width + (15 - dst_sft)) + 15) >> 4;

		for(u32 y=0; y<height; y++) {
			u16 src_cadr = src_adr;
			u16 dst_cadr = dst_adr;

			u16 cmask = swmask;
			u16 nw1 = nw;
			u32 srcs = 0;
			if(preload) {
				srcs = sw(m_gcps.read_word((src_seg << 4) + src_cadr)) << src_do_sft;
				if(mode & 0x100)
					src_cadr += 2;
			}
			do {
				srcs = (srcs << 16) | (sw(m_gcps.read_word((src_seg << 4) + src_cadr)) << src_do_sft);
				u16 src = (srcs >> dst_do_sft) & rmask;
				u16 dst = sw(m_gcps.read_word((dst_seg << 4) + dst_cadr));
				u16 res = blend(src, dst);
				if(mode & 0x40) {
					u16 tmask;
					switch((mode >> 11) & 3) {
					case 0:
					default:
						tmask = src;
						break;
					case 1:
						tmask = (src >> 1) | src;
						tmask = (tmask & 0x5555) * 0x3;
						break;
					case 2:
						tmask = (src >> 2) | src;
						tmask = (tmask >> 1) | tmask;
						tmask = (tmask & 0x1111) * 0xf;
						break;
					case 3:
						tmask = (src >> 4) | src;
						tmask = (tmask >> 2) | tmask;
						tmask = (tmask >> 1) | tmask;
						tmask = (tmask & 0x0101) * 0xff;
						break;
					}
					cmask &= tmask;
				}

				res = (dst & ~cmask) | (res & cmask);

				m_gcps.write_word((dst_seg << 4) + dst_cadr, sw(res));
				if(mode & 0x100)
					src_cadr += 2;
				dst_cadr += 2;

				nw1 --;

				cmask = nw1 == 1 ? ewmask : mwmask;
			} while(nw1);

			if(mode & 0x100)
				src_adr += sy;
			dst_adr += dy;
		}
	}
}

void mindset_state::gcp_w(u16)
{
	u16 packet_seg  = sw(m_gcps.read_word(0xbfd7a));
	u16 packet_adr  = sw(m_gcps.read_word(0xbfd78));
	u16 global_mode = sw(m_gcps.read_word(0xbfd76));

	if(0)
	logerror("GCP: start %04x:%04x mode %04x (%05x)\n", packet_seg, packet_adr, global_mode, m_maincpu->pc());

	switch(global_mode) {
	case 0x0005:
	case 0x0101:
		blit(packet_seg, packet_adr);
		break;
	}

	// 100 = done, 200 = done too???, 400 = collision?
	m_gcps.write_word(0xbfd74, m_gcps.read_word(0xbfd74) | 0x0700);

	// Can trigger an irq, on mode & 2 (or is it 200?) (0x40 on 8282, ack on 0x41, which means the system 8042...)
}

void mindset_state::fdc_ctrl_w(u8 data)
{
	logerror("fdc control %02x\n", data);
	if(data & 0x04)
		m_fdc->reset();
	m_floppy[data & 1]->mon_w(!(data & 2));
	m_floppy[(data & 1)^1]->mon_w(true);
}

void mindset_state::fdc_int_w(int state)
{
	if(!m_fdc_intext && state)
		m_fdc_int = true;
	m_fdc_intext = state;
	m_maincpu->int0_w(m_fdc_int || m_trap_int);
}

u16 mindset_state::fdc_clear_interrupt()
{
	m_fdc_int = false;
	m_maincpu->int0_w(m_fdc_int || m_trap_int);
	return 0x0000;
}

void mindset_state::fdc_dma_count_w(u16 data)
{
	m_fdc_dma_count = data;
	logerror("fdc dma count %x\n", m_fdc_dma_count);
}

u8 mindset_state::fdc_dma_r()
{
	u8 res = m_fdc->dma_r();
	if(!m_fdc_dma_count) {
		m_fdc->tc_w(1);
		m_fdc->tc_w(0);
	} else
		m_fdc_dma_count--;
	return res;
}

void mindset_state::fdc_dma_w(u8 data)
{
	m_fdc->dma_w(data);
	if(!m_fdc_dma_count) {
		m_fdc->tc_w(1);
		m_fdc->tc_w(0);
	} else
		m_fdc_dma_count--;
}

u16 mindset_state::trap_clear_interrupt()
{
	m_trap_int = false;
	m_maincpu->int0_w(m_fdc_int || m_trap_int);
	return 0x0000;
}

u16 mindset_state::trap_r(offs_t offset)
{
	//  machine().debug_break();
	logerror("trap_r %04x\n", offset << 1);
	m_trap_data[m_trap_len++] = (offset << 1) | 0x8000;
	m_trap_data[m_trap_len++] = 0;
	m_trap_drq = true;
	m_maincpu->drq1_w(m_fdc_drq || m_trap_drq);
	return 0;
}

void mindset_state::trap_w(offs_t offset, u16 data)
{
	//  machine().debug_break();
	logerror("trap_w %04x, %04x\n", offset << 1, data);
	m_trap_data[m_trap_len++] = offset << 1;
	m_trap_data[m_trap_len++] = data;
	m_trap_drq = true;
	m_maincpu->drq1_w(m_fdc_drq || m_trap_drq);
}

u16 mindset_state::trap_dma_r(offs_t, u16 mem_mask)
{
	u16 res = m_trap_pos < m_trap_len ? m_trap_data[m_trap_pos++] : 0;
	logerror("trap dma %04x @ %04x\n", res, mem_mask);
	if(m_trap_pos >= m_trap_len) {
		m_trap_drq = false;
		m_trap_int = true;
		m_maincpu->drq1_w(m_fdc_drq || m_trap_drq);
		m_maincpu->int0_w(m_fdc_int || m_trap_int);
		m_trap_pos = m_trap_len = 0;
	}
	return res;
}

void mindset_state::maincpu_mem(address_map &map)
{
	map(0x00000, 0x3ffff).ram();
	map(0xb8000, 0xbffff).ram().share("vram");
	map(0xf8000, 0xfffff).rom().region("maincpu", 0);
}

void mindset_state::maincpu_io(address_map &map)
{
	map(0x0000, 0x7fff).rw(FUNC(mindset_state::trap_r), FUNC(mindset_state::trap_w));

	map(0x8040, 0x8041).r(FUNC(mindset_state::trap_dma_r));
	map(0x8048, 0x8049).r(FUNC(mindset_state::trap_clear_interrupt));
	map(0x8050, 0x8050).w(FUNC(mindset_state::fdc_ctrl_w));
	map(0x8054, 0x8054).rw(FUNC(mindset_state::fdc_dma_r), FUNC(mindset_state::fdc_dma_w));
	map(0x8058, 0x8059).w(FUNC(mindset_state::fdc_dma_count_w));
	map(0x805c, 0x805d).r(FUNC(mindset_state::fdc_clear_interrupt));
	map(0x8060, 0x8060).r(m_fdc, FUNC(i8272a_device::msr_r));
	map(0x8062, 0x8062).rw(m_fdc, FUNC(i8272a_device::fifo_r), FUNC(i8272a_device::fifo_w));

#if 0
	map(0x8080, 0x8080).lr8("id13", []() -> u8 { return 0x13; }); // sound
	map(0x80c0, 0x80c0).lr8("id3f", []() -> u8 { return 0x3f; }); // serial type 1, maybe?
	map(0x8100, 0x8100).lr8("id5f", []() -> u8 { return 0x5f; }); // serial type 2
	map(0x8140, 0x8140).lr8("id70", []() -> u8 { return 0x70; }); // parallel printer, init writes 0x82 at +6
	map(0x8180, 0x8180).lr8("rs232-id", []() -> u8 { return 0x73; }); // rs232
#endif

	map(0x8280, 0x8283).rw(m_syscpu, FUNC(i8042_device::upi41_master_r), FUNC(i8042_device::upi41_master_w)).umask16(0x00ff);
	map(0x82a0, 0x82a3).rw(m_soundcpu, FUNC(i8042_device::upi41_master_r), FUNC(i8042_device::upi41_master_w)).umask16(0x00ff);
	map(0x8300, 0x8301).w(FUNC(mindset_state::gcp_w));
	map(0x8320, 0x8321).rw(FUNC(mindset_state::dispreg_r), FUNC(mindset_state::dispreg_w));
	map(0x8322, 0x8323).rw(FUNC(mindset_state::dispctrl_r), FUNC(mindset_state::dispctrl_w));
}

static void pc_dd_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

static void mindset_modules(device_slot_interface &device)
{
	device.option_add("stereo", MINDSET_SOUND_MODULE);
	device.option_add("rs232", MINDSET_RS232_MODULE);
}

void mindset_state::mindset(machine_config &config)
{
	config.set_perfect_quantum(m_syscpu);
	config.set_default_layout(layout_mindset);

	I80186(config, m_maincpu, 12_MHz_XTAL); // Divides internally by 2 to produce a clkout of 6MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &mindset_state::maincpu_mem);
	m_maincpu->set_addrmap(AS_IO,      &mindset_state::maincpu_io);

	I8042(config, m_syscpu, 14.318181_MHz_XTAL/2);
	m_syscpu->p1_in_cb().set(FUNC(mindset_state::sys_p1_r));
	m_syscpu->p2_in_cb().set(FUNC(mindset_state::sys_p2_r));
	m_syscpu->p1_out_cb().set(FUNC(mindset_state::sys_p1_w));
	m_syscpu->p2_out_cb().set(FUNC(mindset_state::sys_p2_w));
	m_syscpu->t0_in_cb().set(FUNC(mindset_state::sys_t0_r));
	m_syscpu->t1_in_cb().set(FUNC(mindset_state::sys_t1_r));

	I8042(config, m_soundcpu, 12_MHz_XTAL/2);
	m_soundcpu->p1_out_cb().set(FUNC(mindset_state::snd_p1_w));
	m_soundcpu->p2_out_cb().set(FUNC(mindset_state::snd_p2_w));

	I8749(config, m_kbdcpu, 6_MHz_XTAL);
	m_kbdcpu->p1_out_cb().set(FUNC(mindset_state::kbd_p1_w));
	m_kbdcpu->p2_out_cb().set(FUNC(mindset_state::kbd_p2_w));
	m_kbdcpu->bus_in_cb().set(FUNC(mindset_state::kbd_d_r));
	m_kbdcpu->t1_in_cb().set(FUNC(mindset_state::kbd_t1_r));

	// Should be NTSC actually... we'll see
	// Pretty sure the pixel clock is the 14.x one, the 12MHz one would only allow 630 pixels

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(100));
	m_screen->set_size(751, 480);
	m_screen->set_visarea(0, 750, 0, 479);
	m_screen->set_screen_update(FUNC(mindset_state::screen_update));
	// Should be at the position indicated by display reg 2
	m_screen->scanline().set([this](int scanline) { m_maincpu->int2_w(scanline == 398); });
	m_screen->screen_vblank().set(m_maincpu, FUNC(i80186_cpu_device::int1_w));

	I8272A(config, m_fdc, 16_MHz_XTAL/2, true);
	m_fdc->intrq_wr_callback().set(FUNC(mindset_state::fdc_int_w));
	m_fdc->drq_wr_callback().set([this](int state) { m_fdc_drq = state; m_maincpu->drq1_w(m_fdc_drq || m_trap_drq); });
	m_fdc->set_ready_line_connected(false);
	FLOPPY_CONNECTOR(config, m_fdco[0], pc_dd_floppies, "525dd", floppy_image_device::default_pc_floppy_formats);
	FLOPPY_CONNECTOR(config, m_fdco[1], pc_dd_floppies, "525dd", floppy_image_device::default_pc_floppy_formats);

	SPEAKER(config, "speaker", 2).front();
	DAC_8BIT_R2R(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.5, 0);

	MINDSET_MODULE(config, "m0", mindset_modules, "stereo", false);
	MINDSET_MODULE(config, "m1", mindset_modules, "rs232", false);
	MINDSET_MODULE(config, "m2", mindset_modules, nullptr, false);
	MINDSET_MODULE(config, "m3", mindset_modules, nullptr, false);
	MINDSET_MODULE(config, "m4", mindset_modules, nullptr, false);
	MINDSET_MODULE(config, "m5", mindset_modules, nullptr, false);

	SOFTWARE_LIST(config, "flop_list").set_original("mindset_flop");
}

static INPUT_PORTS_START(mindset)
	PORT_START("K00")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("K01")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9)         PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10)        PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD)                               PORT_NAME("Start")
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PAUSE)      PORT_NAME("Pause")
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11)        PORT_NAME("Sys config")
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F12)        PORT_NAME("Reset")
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("K02")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)         PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)         PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)         PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)         PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)         PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)         PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)         PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)         PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("K03")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('`') PORT_CHAR('~')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT)     PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP)       PORT_CHAR(UCHAR_MAMEKEY(PGUP))
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD)                               PORT_NAME("Break")
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("K04")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)        PORT_CHAR('\t')
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)          PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)          PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)          PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)          PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("K05")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)          PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(8)
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)        PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN)       PORT_CHAR(UCHAR_MAMEKEY(PGDN))
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK)   PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_NAME("Caps lock")

	PORT_START("K06")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_NAME("Control")
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)          PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)          PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)          PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)          PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)          PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("K07")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)          PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)          PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)          PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13) PORT_NAME("Return")
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME)       PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("K08")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_END)        PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT)       PORT_CHAR(UCHAR_MAMEKEY(LALT)) PORT_NAME("Alt")
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)     PORT_CHAR(UCHAR_SHIFT_1) PORT_NAME("Shift (Left)")
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)          PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)          PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)          PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)          PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("K09")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)          PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)          PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT)     PORT_CHAR(UCHAR_SHIFT_1) PORT_NAME("Shift (Right)")
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PRTSCR)     PORT_CHAR(UCHAR_MAMEKEY(PRTSCR)) PORT_NAME("Prt Scn")
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SCRLOCK)    PORT_CHAR(UCHAR_MAMEKEY(SCRLOCK))

	PORT_START("K10")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("MOUSEAXIS1")
	PORT_BIT(0xff, 0x00, IPT_MOUSE_X) PORT_SENSITIVITY(50) PORT_KEYDELTA(1) PORT_MINMAX(0, 255) PORT_PLAYER(1)

	PORT_START("MOUSEAXIS0")
	PORT_BIT(0xff, 0x00, IPT_MOUSE_Y) PORT_SENSITIVITY(50) PORT_KEYDELTA(1) PORT_MINMAX(0, 255) PORT_PLAYER(1)

	PORT_START("MOUSEBTN")
	PORT_BIT(0x1cf, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_PLAYER(1)
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_PLAYER(1)

	PORT_START("JOYSTICK")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)    PORT_PLAYER(2) PORT_8WAY
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)  PORT_PLAYER(2) PORT_8WAY
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)  PORT_PLAYER(2) PORT_8WAY
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_PLAYER(2)
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_PLAYER(2)
	PORT_BIT(0x1c0, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

ROM_START(mindset)
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD16_BYTE("1.7_lo.u60", 0, 0x4000, CRC(00474dc1) SHA1(676f30f170c14174dbff3b5cbf98d0f23472b7c4))
	ROM_LOAD16_BYTE("1.7_hi.u59", 1, 0x4000, CRC(1434af10) SHA1(39105eacdd7ddc13e449e2c32743e828bef33595))

	ROM_REGION(0x0800, "syscpu", 0)
	ROM_LOAD("253002-001.u17", 0, 0x800, CRC(69da82c9) SHA1(2f0bf5b134dc703cbc72e0c6df5b7beda1b39e70))

	ROM_REGION(0x0800, "soundcpu", 0)
	ROM_LOAD("253006-001.u16", 0, 0x800, CRC(7bea5edd) SHA1(30cdc0dedaa5246f4952df452a99ca22e3cd0636))

	ROM_REGION(0x0800, "kbdcpu", 0)
	ROM_LOAD("kbd_v3.0.bin", 0, 0x800, CRC(1c6aa433) SHA1(1d01dbda4730f26125ba2564a608c2f8ddfc05b3))
ROM_END

COMP( 1984, mindset, 0, 0, mindset, mindset, mindset_state, empty_init, "Mindset Corporation", "Mindset Personal Computer", MACHINE_SUPPORTS_SAVE)




mini2440.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Tim Schuerewegen
/*******************************************************************************

    FriendlyARM Mini2440

*******************************************************************************/

#include "emu.h"
#include "cpu/arm7/arm7.h"
#include "machine/nandflash.h"
#include "machine/s3c2440.h"
#include "sound/dac.h"
#include "screen.h"
#include "speaker.h"

#include <cstdarg>


namespace {

#define VERBOSE_LEVEL ( 0 )

class mini2440_state : public driver_device
{
public:
	mini2440_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_s3c2440(*this, "s3c2440"),
		m_nand(*this, "nand"),
		m_ldac(*this, "ldac"),
		m_rdac(*this, "rdac"),
		m_penx(*this, "PENX"),
		m_peny(*this, "PENY") { }

	void mini2440(machine_config &config);

	void init_mini2440();

	DECLARE_INPUT_CHANGED_MEMBER(mini2440_input_changed);

private:
	required_device<cpu_device> m_maincpu;
	required_device<s3c2440_device> m_s3c2440;
	required_device<samsung_k9f1g08u0b_device> m_nand;
	required_device<dac_word_interface> m_ldac;
	required_device<dac_word_interface> m_rdac;
	required_ioport m_penx;
	required_ioport m_peny;

	uint32_t m_port[9] = { };
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	inline void verboselog(int n_level, const char *s_fmt, ...) ATTR_PRINTF(3,4);
	uint32_t s3c2440_gpio_port_r(offs_t offset);
	void s3c2440_gpio_port_w(offs_t offset, uint32_t data);
	uint32_t s3c2440_core_pin_r(offs_t offset);
	void s3c2440_nand_command_w(uint8_t data);
	void s3c2440_nand_address_w(uint8_t data);
	uint8_t s3c2440_nand_data_r();
	void s3c2440_nand_data_w(uint8_t data);
	void s3c2440_i2s_data_w(offs_t offset, uint16_t data);
	uint32_t s3c2440_adc_data_r(offs_t offset);

	void mini2440_map(address_map &map) ATTR_COLD;
};

inline void mini2440_state::verboselog(int n_level, const char *s_fmt, ...)
{
	if (VERBOSE_LEVEL >= n_level)
	{
		va_list v;
		char buf[32768];
		va_start( v, s_fmt);
		vsprintf( buf, s_fmt, v);
		va_end( v);
		logerror( "%s: %s", machine().describe_context(), buf);
	}
}

/***************************************************************************
    MACHINE HARDWARE
***************************************************************************/

// GPIO

uint32_t mini2440_state::s3c2440_gpio_port_r(offs_t offset)
{
	uint32_t data = m_port[offset];
	switch (offset)
	{
		case S3C2440_GPIO_PORT_G :
		{
			data = (data & ~(1 << 13)) | (1 << 13); // [nand] 1 = 2048 byte page  (if ncon = 1)
			data = (data & ~(1 << 14)) | (1 << 14); // [nand] 1 = 5 address cycle (if ncon = 1)
			data = (data & ~(1 << 15)) | (0 << 15); // [nand] 0 = 8-bit bus width (if ncon = 1)
			data = data | 0x8E9; // for "load Image of Linux..."
		}
		break;
	}
	return data;
}

void mini2440_state::s3c2440_gpio_port_w(offs_t offset, uint32_t data)
{
	// tout2/gb2 -> uda1341ts l3mode
	// tout3/gb3 -> uda1341ts l3data
	// tclk0/gb4 -> uda1341ts l3clock

	m_port[offset] = data;
	switch (offset)
	{
		case S3C2440_GPIO_PORT_B :
		{
			verboselog(5,  "LED %d %d %d %d\n", BIT( data, 5), BIT( data, 6), BIT( data, 7), BIT( data, 8));
		}
		break;
	}
}

// CORE

/*

OM[1:0] = 00: Enable NAND flash memory boot

NCON: NAND flash memory selection (Normal / Advance)
0: Normal NAND flash (256Words/512Bytes page size, 3/4 address cycle)
1: Advance NAND flash (1KWords/2KBytes page size, 4/5 address cycle)

*/

uint32_t mini2440_state::s3c2440_core_pin_r(offs_t offset)
{
	int data = 0;
	switch (offset)
	{
		case S3C2440_CORE_PIN_NCON : data = 1; break;
		case S3C2440_CORE_PIN_OM0  : data = 0; break;
		case S3C2440_CORE_PIN_OM1  : data = 0; break;
	}
	return data;
}

// NAND

void mini2440_state::s3c2440_nand_command_w(uint8_t data)
{
	m_nand->command_w(data);
}

void mini2440_state::s3c2440_nand_address_w(uint8_t data)
{
	m_nand->address_w(data);
}

uint8_t mini2440_state::s3c2440_nand_data_r()
{
	return m_nand->data_r();
}

void mini2440_state::s3c2440_nand_data_w(uint8_t data)
{
	m_nand->data_w(data);
}

// I2S

void mini2440_state::s3c2440_i2s_data_w(offs_t offset, uint16_t data)
{
	if ( offset )
		m_ldac->write(data);
	else
		m_rdac->write(data);
}

// ADC

uint32_t mini2440_state::s3c2440_adc_data_r(offs_t offset)
{
	uint32_t data = 0;
	switch (offset)
	{
		case 2 + 0 : data = m_penx->read(); break;
		case 2 + 1 : data = 915 - m_peny->read() + 90; break;
	}
	verboselog(5,  "s3c2440_adc_data_r %08X\n", data);
	return data;
}

// TOUCH

INPUT_CHANGED_MEMBER(mini2440_state::mini2440_input_changed)
{
	m_s3c2440->s3c2440_touch_screen( (newval & 0x01) ? 1 : 0);
}

// ...

void mini2440_state::machine_start()
{
}

void mini2440_state::machine_reset()
{
	m_maincpu->reset();
	memset( m_port, 0, sizeof( m_port));
}

/***************************************************************************
    ADDRESS MAPS
***************************************************************************/

void mini2440_state::mini2440_map(address_map &map)
{
//  map(0x00000000, 0x001fffff).rom();
	map(0x30000000, 0x37ffffff).ram();
}

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

void mini2440_state::init_mini2440()
{
	// do nothing
}

void mini2440_state::mini2440(machine_config &config)
{
	ARM920T(config, m_maincpu, 400000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &mini2440_state::mini2440_map);

	PALETTE(config, "palette").set_entries(32768);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(1024, 768);
	screen.set_visarea(0, 239, 0, 319);
	screen.set_screen_update("s3c2440", FUNC(s3c2440_device::screen_update));

	SPEAKER(config, "speaker", 2).front();
	UDA1341TS(config, m_ldac, 0).add_route(ALL_OUTPUTS, "speaker", 1.0, 0); // uda1341ts.u12
	UDA1341TS(config, m_rdac, 0).add_route(ALL_OUTPUTS, "speaker", 1.0, 1); // uda1341ts.u12

	S3C2440(config, m_s3c2440, 12000000);
	m_s3c2440->set_palette_tag("palette");
	m_s3c2440->set_screen_tag("screen");
	m_s3c2440->core_pin_r_callback().set(FUNC(mini2440_state::s3c2440_core_pin_r));
	m_s3c2440->gpio_port_r_callback().set(FUNC(mini2440_state::s3c2440_gpio_port_r));
	m_s3c2440->gpio_port_w_callback().set(FUNC(mini2440_state::s3c2440_gpio_port_w));
	m_s3c2440->adc_data_r_callback().set(FUNC(mini2440_state::s3c2440_adc_data_r));
	m_s3c2440->i2s_data_w_callback().set(FUNC(mini2440_state::s3c2440_i2s_data_w));
	m_s3c2440->nand_command_w_callback().set(FUNC(mini2440_state::s3c2440_nand_command_w));
	m_s3c2440->nand_address_w_callback().set(FUNC(mini2440_state::s3c2440_nand_address_w));
	m_s3c2440->nand_data_r_callback().set(FUNC(mini2440_state::s3c2440_nand_data_r));
	m_s3c2440->nand_data_w_callback().set(FUNC(mini2440_state::s3c2440_nand_data_w));

	SAMSUNG_K9F1G08U0B(config, m_nand, 0);
	m_nand->rnb_wr_callback().set(m_s3c2440, FUNC(s3c2440_device::frnb_w));
}

static INPUT_PORTS_START( mini2440 )
	PORT_START( "PENB" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Pen Button") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mini2440_state::mini2440_input_changed), 0) PORT_PLAYER(1)
	PORT_START( "PENX" )
	PORT_BIT( 0x3ff, 0x200, IPT_LIGHTGUN_X ) PORT_NAME("Pen X") PORT_MINMAX(80, 950) PORT_SENSITIVITY(50) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_KEYDELTA(30) PORT_PLAYER(1)
	PORT_START( "PENY" )
	PORT_BIT( 0x3ff, 0x200, IPT_LIGHTGUN_Y ) PORT_NAME("Pen Y") PORT_MINMAX(90, 915) PORT_SENSITIVITY(50) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_KEYDELTA(30) PORT_PLAYER(1)
INPUT_PORTS_END

/***************************************************************************
    GAME DRIVERS
***************************************************************************/

ROM_START( mini2440 )
	ROM_REGION( 0x8400000, "nand", 0 )
	ROM_SYSTEM_BIOS( 0, "linux", "Linux 2.6.29.4-FriendlyARM + Qtopia 2.2.0 (2009/07/08)" )
	ROMX_LOAD( "linux.bin", 0, 0x8400000, CRC(7c98b249) SHA1(7c2e76edcbbcbfc3f3b0e53fb42d3e5c96e9a9fb), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "wince", "Windows Embedded CE 6.00 (2011/03/14)" )
	ROMX_LOAD( "wince.bin", 0, 0x8400000, CRC(6acd56b8) SHA1(d039968820348fb1169827fa12b38b94e80a076f), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "android", "Android 1.5 (2009/05/13)" )
	ROMX_LOAD( "android.bin", 0, 0x8400000, CRC(4721837d) SHA1(88fcf553b106d9fc624c9615d9c1da9c705ccb46), ROM_BIOS(2) )
ROM_END

} // anonymous namespace


COMP(2009, mini2440, 0, 0, mini2440, mini2440, mini2440_state, init_mini2440, "FriendlyARM", "Mini2440", 0)



minib.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/******************************************************************************

    http://www.sprow.co.uk/bbc/minib.htm

    The board comes with the following features:
    - 6502A processor, at 2MHz (slowed to 1MHz during i/o accesses)
    - 6522 versatile interface adapter for timers and interruptible latched i/o ports
    - Interface for a 20x4 LCD display
    - 32kbyte high speed SRAM
    - 128kbyte in circuit programmable flash ROM (16k for main operating system, 112k for other ROM based applications)
    - PS/2 keyboard interface
    - Real time clock (year 2000 compliant), on a shared I²C bus
    - Full implementation of the BBC micro's 1MHz expansion bus for add on boards
    - Power supply monitor/reset device

    TODO:
    - fix keyboard

******************************************************************************/

#include "emu.h"
#include "cpu/m6502/m6502.h"
#include "machine/6522via.h"
#include "machine/input_merger.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "bus/pc_kbd/keyboards.h"

// slot devices
#include "bus/bbc/1mhzbus/1mhzbus.h"
#include "bus/bbc/1mhzbus/24bbc.h"
#include "bus/bbc/1mhzbus/2ndserial.h"
#include "bus/bbc/1mhzbus/ide.h"
#include "bus/bbc/userport/userport.h"
#include "bus/bbc/userport/lcd.h"


namespace {

class minib_state : public driver_device
{
public:
	minib_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rombank(*this, "rombank")
		, m_kbd(*this, "kbd")
	{ }

	void minib(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<m6502_device> m_maincpu;
	required_memory_bank m_rombank;
	required_device<pc_kbdc_device> m_kbd;

	void mem_map(address_map &map) ATTR_COLD;

	//uint8_t pa_r();
	void pa_w(uint8_t data);
};


void minib_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).ram();
	map(0x8000, 0xbfff).bankr(m_rombank);
	map(0xc000, 0xffff).rom().region("flash", 0x1c000);
	map(0xfc00, 0xfcff).rw("1mhzbus", FUNC(bbc_1mhzbus_slot_device::fred_r), FUNC(bbc_1mhzbus_slot_device::fred_w));
	map(0xfd00, 0xfdff).rw("1mhzbus", FUNC(bbc_1mhzbus_slot_device::jim_r), FUNC(bbc_1mhzbus_slot_device::jim_w));
	map(0xfe30, 0xfe3f).lw8(NAME([this] (u8 data) { m_rombank->set_entry(data & 0x07); }));
	map(0xfe60, 0xfe6f).m("via", FUNC(via6522_device::map));
}


void minib_state::machine_start()
{
	m_rombank->configure_entries(0, 8, memregion("flash")->base(), 0x4000);
}

void minib_state::machine_reset()
{
	m_rombank->set_entry(0);
}


//uint8_t minib_state::pa_r()
//{
//  uint8_t data = 0x00;
//
//  data |= m_kbd->data_signal() << 1;
//
//  return data;
//}

void minib_state::pa_w(uint8_t data)
{
	// PA0 = SDA drive*
	// PA1 = PS/2 data sense
	// PA2 = SDA sense
	// PA3 = SCL
	// PA4 = PS/2 clock out*
	// PA5 = PS/2 data out*
	// CA1 = PS/2 clock sense
	// CA2, PA6, PA7, are spare and just go to a header

	logerror("pa_w: clk %d data %d\n", BIT(data, 4), BIT(data, 5));
	m_kbd->data_write_from_mb(!BIT(data, 4));
	m_kbd->clock_write_from_mb(!BIT(data, 5));
}


static void minib_1mhzbus_devices(device_slot_interface &device)
{
	device.option_add("24bbc", BBC_24BBC);         // Sprow 24bBC/RAM Disc
	device.option_add("2ndserial", BBC_2NDSERIAL); // Sprow 2nd Serial Port
	device.option_add("beebide", BBC_BEEBIDE);     // Sprow BeebIDE 16-bit
}

static void minib_userport_devices(device_slot_interface &device)
{
	device.option_add("lcd", BBC_LCD);             // Sprow LCD Display
}


void minib_state::minib(machine_config &config)
{
	M6502(config, m_maincpu, 16_MHz_XTAL / 8);
	m_maincpu->set_addrmap(AS_PROGRAM, &minib_state::mem_map);

	INPUT_MERGER_ANY_HIGH(config, "irqs").output_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	via6522_device &via(MOS6522(config, "via", 16_MHz_XTAL / 8));
	via.readpb_handler().set("userport", FUNC(bbc_userport_slot_device::pb_r));
	via.writepb_handler().set("userport", FUNC(bbc_userport_slot_device::pb_w));
	via.cb1_handler().set("userport", FUNC(bbc_userport_slot_device::write_cb1));
	via.cb2_handler().set("userport", FUNC(bbc_userport_slot_device::write_cb2));
	//via.readpa_handler().set(FUNC(minib_state::pa_r));
	via.writepa_handler().set(FUNC(minib_state::pa_w));
	via.irq_handler().set("irqs", FUNC(input_merger_device::in_w<0>));

	PC_KBDC(config, m_kbd, pc_at_keyboards, STR_KBD_MICROSOFT_NATURAL);
	m_kbd->out_data_cb().set("via", FUNC(via6522_device::write_pa1));
	m_kbd->out_clock_cb().set("via", FUNC(via6522_device::write_ca1));

	bbc_userport_slot_device &userport(BBC_USERPORT_SLOT(config, "userport", minib_userport_devices, "lcd"));
	userport.cb1_handler().set("via", FUNC(via6522_device::write_cb1));
	userport.cb2_handler().set("via", FUNC(via6522_device::write_cb2));

	bbc_1mhzbus_slot_device &bus(BBC_1MHZBUS_SLOT(config, "1mhzbus", 16_MHz_XTAL / 16, minib_1mhzbus_devices, nullptr));
	bus.irq_handler().set("irqs", FUNC(input_merger_device::in_w<1>));
	bus.nmi_handler().set_inputline(m_maincpu, M6502_NMI_LINE);
}


ROM_START(minib)
	ROM_REGION(0x20000, "flash", ROMREGION_ERASEFF)
	ROM_LOAD("os041.rom",  0x1d000, 0x3000, CRC(57a24945) SHA1(5e521850953be33b9b29995e5f043bbbb45c60e5)) // OS 0.41
	ROM_LOAD("gos015.rom", 0x18000, 0x4000, CRC(e35a93df) SHA1(c91286aec2a2e086cb55ca3e0af576132144831f)) // GOS 0.15
	ROM_LOAD("basic2.rom", 0x14000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281)) // BASIC 2
ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT         COMPANY        FULLNAME           FLAGS
COMP (2002, minib,    0,      0,      minib,   0,      minib_state,  empty_init,  "Sprow",       "MiniB Computer",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)



minichess.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

SciSys Mini Chess, pocket calculator style chesscomputer

It's the first chess program on HMCS40. The engine was written by Mark Taylor
with assistance from David Levy.

Hardware notes:
- Hitachi 44801A34 MCU @ ~400kHz
- 4-digit LCD screen

Excluding resellers with same title, this MCU was used in:
- SciSys Mini Chess - 1st use
- SciSys Junior Chess
- SciSys Graduate Chess
- SciSys Chess Partner 3000
- SciSys Chess Partner 4000

MCU clock is via a resistor, this less accurate than with an XTAL, so the speed
may vary. Graduate Chess appears to have a 62K resistor between the OSC pins,
which would make it around 500kHz?

On CP3000/4000 they added a level slider. This will oscillate the level switch
input pin, so the highest level setting is the same as level 2 on Mini Chess.
It works on the old A34 MCU because the game keeps reading D0 while computing.

*******************************************************************************/

#include "emu.h"

#include "cpu/hmcs40/hmcs40.h"
#include "machine/timer.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "saitek_minichess.lh"


namespace {

class mini_state : public driver_device
{
public:
	mini_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_comp_timer(*this, "comp_timer"),
		m_computing(*this, "computing"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void smchess(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<hmcs40_cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<timer_device> m_comp_timer;
	output_finder<> m_computing;
	required_ioport_array<5> m_inputs;

	u8 m_inp_mux = 0;
	u8 m_lcd_select = 0;
	u8 m_lcd_data = 0;

	TIMER_DEVICE_CALLBACK_MEMBER(computing) { m_computing = 1; }

	void update_lcd();
	template<int N> void seg_w(u8 data);
	void mux_w(u16 data);
	u16 input_r();
};

void mini_state::machine_start()
{
	m_computing.resolve();

	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_lcd_select));
	save_item(NAME(m_lcd_data));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void mini_state::update_lcd()
{
	u8 data = (m_lcd_select & 1) ? (m_lcd_data ^ 0xff) : m_lcd_data;
	data = bitswap<8>(data,2,4,6,7,5,1,0,3);
	m_display->matrix(m_lcd_select >> 2, data);
}

template<int N>
void mini_state::seg_w(u8 data)
{
	// R2x,R3x: LCD segment data
	const u8 shift = N * 4;
	m_lcd_data = (m_lcd_data & ~(0xf << shift)) | (data << shift);
	update_lcd();
}

void mini_state::mux_w(u16 data)
{
	// D9-D12: input mux
	m_inp_mux = ~data >> 9 & 0xf;

	// D3,D5-D8: CD4066 to LCD
	u8 sel = data >> 3 & 0x3f;

	// "computing" segment goes on when LCD isn't driven
	if (~m_lcd_select & sel & 1)
	{
		m_computing = 0;
		m_comp_timer->adjust(attotime::from_msec(100));
	}

	m_lcd_select = sel;
	update_lcd();
}

u16 mini_state::input_r()
{
	// D0,D2: switches
	u16 data = m_inputs[4]->read() & 5;

	// D13-D15: multiplexed inputs
	for (int i = 0; i < 4; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read() << 13;

	return ~data;
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( smchess )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("A 1 / Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("B 2 / Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("C 3 / Bishop")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("D 4 / Rook")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("E 5 / Queen")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("F 6 / King")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("G 7 / White")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("H 8 / Black")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("FP") // find position

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE") // clear entry
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_TOGGLE PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_TOGGLE PORT_CODE(KEYCODE_M) PORT_NAME("MM") // multi move
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void mini_state::smchess(machine_config &config)
{
	// basic machine hardware
	HD44801(config, m_maincpu, 400'000); // approximation
	m_maincpu->write_r<2>().set(FUNC(mini_state::seg_w<0>));
	m_maincpu->write_r<3>().set(FUNC(mini_state::seg_w<1>));
	m_maincpu->write_d().set(FUNC(mini_state::mux_w));
	m_maincpu->read_d().set(FUNC(mini_state::input_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 8);
	m_display->set_segmask(0xf, 0x7f);
	m_display->set_refresh(attotime::from_hz(30));

	config.set_default_layout(layout_saitek_minichess);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/2.5, 567/2.5);
	screen.set_visarea_full();

	TIMER(config, m_comp_timer).configure_generic(FUNC(mini_state::computing));
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( smchess )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD("44801a34_scisys-w_ltd_proj_t", 0x0000, 0x2000, CRC(be71f1c0) SHA1(6b4d5c8f8491c82bdec1938bd83c14e826ff3e30) )

	ROM_REGION( 48645, "screen", 0 )
	ROM_LOAD("smchess.svg", 0, 48645, CRC(19beaa99) SHA1(2d738bd6953dfd7a2c8c37814badd0aac2960c8c) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS       INIT        COMPANY, FULLNAME, FLAGS
SYST( 1981, smchess, 0,      0,      smchess, smchess, mini_state, empty_init, "SciSys / Philidor Software", "Mini Chess", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



minicom.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders: Felipe Sanches
/***************************************************************************

    Ultratec Minicom IV
    http://www.ultratec.com/ttys/non-printing/minicom.php

  Driver by Felipe Sanches

There messages are displayed in a 20 digit 14 segment VFD.

  ***********
 * *   *   * *
 *  *  *  *  *
 *   * * *   *
  ***** *****
 *   * * *   *
 *  *  *  *  *
 * *   *   * *
  ***********  *

Digits seem to be selected by a mux fed by a counter that is incremented by pulses in P1.2
There may be a bit connected to the counter reset signal...

Segment data is sent to each 14seg digit by first writing half of the data to port P0 and
 toggling T0 and then the other half of data is written to P0 again but toggling T1 afterwards.

  Changelog:

   2014 JUL 22 [Felipe Sanches]:
   * Fixing alignment of 14-seg display multiplexation

   2014 JUL 19 [Felipe Sanches]:
   * Got the display working except for a few glitches

   2014 JUL 16 [Felipe Sanches]:
   * Initial driver skeleton
*/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "minicom.lh"

#define LOG_IO_PORTS (1U << 1)

#define VERBOSE (0)
#include "logmacro.h"


namespace {

#define PRINTER_ATTACHED 1

class minicom_state : public driver_device
{
public:
	minicom_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_digits(*this, "digit%u", 0U)
	{ }

	void minicom(machine_config &config);

	void init_minicom();

private:
	void i87c52_p0_w(uint8_t data);
	void i87c52_p1_w(uint8_t data);
	void i87c52_p2_w(uint8_t data);
	void i87c52_p3_w(uint8_t data);
	uint8_t i87c52_p1_r();
	uint8_t i87c52_p2_r();

	uint8_t m_p[4]{};
	uint16_t m_display_data = 0;
	int m_digit_index = 0;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	required_device<i87c52_device> m_maincpu;
	output_finder<20> m_digits;
};

void minicom_state::machine_start()
{
	m_digits.resolve();
	// zerofill
	memset(m_p, 0, 4);
	m_digit_index = 0;
	m_display_data = 0;

	// register for savestates
	save_item(NAME(m_p));
	save_item(NAME(m_digit_index));
	save_item(NAME(m_display_data));
}

void minicom_state::machine_reset()
{
	m_digit_index = 19;
	m_display_data = 0;

	for (int i=0; i<20; i++)
		m_digits[i] = 0;
}

uint8_t minicom_state::i87c52_p1_r()
{
	//P1.3 seems to be an indicator of whether or not we have a printer device attached.
	// at address 0xABF the code checks this flag in order to decide which string to display:
	// "MINIPRINT IS RESET" or "MINICOM IS RESET"
	return PRINTER_ATTACHED << 3;
}

uint8_t minicom_state::i87c52_p2_r()
{
//          return 0; //para a palestra no Garoa... :-)
	return 1; //to skip the "NO POWER" warning. I'm not sure why.
}

static void printbits(uint8_t v, char *buf)
{
	for (int i = 7; i >= 0; i--)
		buf[7 - i] = '0' + BIT(v, i);
	buf[8] = '\0';
}

#define FALLING_EDGE(old_data, new_data, bit) (BIT(old_data ^ new_data, bit) && !BIT(new_data, bit))
#define RISING_EDGE(old_data, new_data, bit) (BIT(old_data ^ new_data, bit) && BIT(new_data, bit))

#define P1_UNKNOWN_BITS (0xFF & ~(1 << 2))
#define P2_UNKNOWN_BITS 0xFF
#define P3_UNKNOWN_BITS (0xFF & ~((1 << 4)|(1 << 5)))
void minicom_state::i87c52_p0_w(uint8_t data)
{
	if (data != m_p[0])
	{
		m_p[0]=data;

		//Bit P0.1 is the serial-input of a 20-bit shift register (made of a couple of chained UCN5810AF chips)
		//We are emulating the display based on the assumption that the firmware will multiplex it by defining one digit at a given time
		//It would be better (in terms of being closer to the actual hardware) to emulate the 20 bit shift register and update all digits
		//for which a bit is TTL high. It seems to me that in the real hardware that would result in dimmer brightness in the display and it
		//does not seem trivial to me to implement this using our current layout system. I'm leaving this note to whoever finds it exciting
				//to explore these possibilities (perhaps myself in the future?).
		if (BIT(data,1)){
			m_digit_index = 0;
		}
	}
}

void minicom_state::i87c52_p1_w(uint8_t data)
{
	if (data != m_p[1])
	{
		uint8_t changed = m_p[1] ^ data;
		if (changed ^ P1_UNKNOWN_BITS)
		{
			char bitbuf[9];
			printbits(changed, bitbuf);
			LOGMASKED(LOG_IO_PORTS, "Write to P1: %02X changed: (        ) (%s) (        ) (        )\n", data, bitbuf);
		}
		if (FALLING_EDGE(m_p[1], data, 2))
		{
			m_digit_index--;
			if (m_digit_index<0) m_digit_index = 19;
		}
		m_p[1] = data;
	}
}

void minicom_state::i87c52_p2_w(uint8_t data)
{
	if (data != m_p[2])
	{
		uint8_t changed = m_p[2] ^ data;
		if (changed ^ P2_UNKNOWN_BITS)
		{
			char bitbuf[9];
			printbits(changed, bitbuf);
			LOGMASKED(LOG_IO_PORTS, "Write to P2: %02X changed: (        ) (        ) (%s) (        )\n", data, bitbuf);
		}
		m_p[2] = data;
	}
}

void minicom_state::i87c52_p3_w(uint8_t data)
{
	if (data != m_p[3])
	{
		uint8_t changed = m_p[3] ^ data;
		if (changed ^ P3_UNKNOWN_BITS)
		{
			char bitbuf[9];
			printbits(changed, bitbuf);
			LOGMASKED(LOG_IO_PORTS, "Write to P3: %02X changed: (        ) (        ) (        ) (%s)\n", data, bitbuf);
		}

		if (FALLING_EDGE(m_p[3], data, 4)) //P3.4 = T0
		{
			m_display_data &= 0xFF00;
			m_display_data |= m_p[0];
		}

		if (FALLING_EDGE(m_p[3], data, 5)) //P3.5 = T1
		{
			m_display_data &= 0xFF;
			m_display_data |= (m_p[0] << 8);
		}

		if (BIT(changed,4) || BIT(changed,5))
		{
			m_digits[m_digit_index] = bitswap<16>(m_display_data,  9,  1,  3, 11, 12,  4,  2, 10, 14, 6,  7, 5,  0, 15,  13, 8) & 0x3FFF;
		}
		m_p[3] = data;
	}
}

void minicom_state::init_minicom()
{
}

void minicom_state::minicom(machine_config &config)
{
	/* basic machine hardware */
	I87C52(config, m_maincpu, XTAL(10'000'000)); /*FIX-ME: verify the correct clock frequency */
	m_maincpu->port_out_cb<0>().set(FUNC(minicom_state::i87c52_p0_w));
	m_maincpu->port_in_cb<1>().set(FUNC(minicom_state::i87c52_p1_r));
	m_maincpu->port_out_cb<1>().set(FUNC(minicom_state::i87c52_p1_w));
	m_maincpu->port_in_cb<2>().set(FUNC(minicom_state::i87c52_p2_r));
	m_maincpu->port_out_cb<2>().set(FUNC(minicom_state::i87c52_p2_w));
	m_maincpu->port_out_cb<3>().set(FUNC(minicom_state::i87c52_p3_w));

	/* video hardware */
	/* fluorescent 14-segment display forming a row of 20 characters */
	config.set_default_layout(layout_minicom);

	/* TODO: Map the keyboard rows/cols inputs (43-key, 4-row keyboard) */

	/* TODO: Treat the modem as a sound device. That may be an interesting challenge... :-) */
}

ROM_START( minicom )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "ultratec_minicom_iv.rom",  0x0000, 0x2000, CRC(22881366) SHA1(fc3faea5ecc1476e5bcb7999638f3150d06c9a81) )
ROM_END

ROM_START( mcom4_02 )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "ultratec_minicom_iv_20020419.rom",  0x0000, 0x2000, CRC(99b6cc35) SHA1(32577005bf02042f893c8880f8ce5b3d8a5f55f9) )
ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT  CLASS          INIT          COMPANY     FULLNAME                   FLAGS
COMP( 1997, minicom,  0,      0,      minicom, 0,     minicom_state, init_minicom, "Ultratec", "Minicom IV (1997-08-11)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND ) // fw release data: 11th Aug 1997
COMP( 2002, mcom4_02, 0,      0,      minicom, 0,     minicom_state, init_minicom, "Ultratec", "Minicom IV (2002-04-19)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND ) // fw release data: 19th Apr 2002



miniforce.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Joakim Larsson Edstrom
/***************************************************************************
 *
 *  Force miniForce 2P/32 driver
 *
 *  24/12/2016
 *
 * Thanks to Al Kossow and his site http://www.bitsavers.org/ I got the information
 * required to start the work with this driver.
 *
 * +=============================================================================================================================+
 * |CPU  |SRAM |     |     |     |     |     |     |WFC-1|     |  SYS68K/PWR-09A | SYS68K/WFMOD-50                               |
 * |-21  | -22 |     |     |     |     |     |     |     |     |                 |                                               |
 * | RST |     |     |     |     |     |     |     |O RUN|     |                 |                                               |
 * | ABT |     |     |     |     |     |     |     | R/L |     |O +5v            |+---------------------------------------------+|
 * |     |     |     |     |     |     |     |     |O LOC|     |O +12v           ||                                             ||
 * |O RUN|O RUN|     |     |     |     |     |     |O ERR|     |O -12v           ||                                             ||
 * |O HLT|     |     |     |     |     |     |     |O BSY|     |O ON             ||                                             ||
 * |O BM |     |     |     |     |     |     |     |     |     |                 ||                                             ||
 * |     |     |     |     |     |     |     |     |     |     |                 |+---------------------------------------------+|
 * |O FLM|O SL0|     |     |     |     |     |     |     |     |                 || FDD                                         ||
 * |O EPR|O SL1|     |     |     |     |     |     |     |     |                 ||                                             ||
 * |O 2WS|     |     |     |     |     |     |     |     |     |    +-------+    ||                                             ||
 * |O 4WS|     |     |     |     |     |     |     |     |     |    |   o   |PWR ||                                             ||
 * |O 6WS|     |     |     |     |     |     |     |     |     |    |       |    |+---------------------------------------------+|
 * |O 8WS|     |     |     |     |     |     |     |     |     |    +-------+    |                                               |
 * |O12WS|     |     |     |     |     |     |     |     |     |                 |                                               |
 * |O14WS|     |     |     |     |     |     |     |     |     |                 |+---------------------------------------------+|
 * |     |     |     |     |     |     |     |     |     |     |                 || HDD                                         ||
 * | CSH |     |     |     |     |     |     |     |     |     |                 ||                                             ||
 * | R/M |     |     |     |     |     |     |     |     |     |                 ||                                             ||
 * |     |     |     |     |     |     |     |     |     |     |                 ||                                             ||
 * |  o  |     |     |     |     |     |     |     |     |     |                 |+---------------------------------------------+|
 * |  o  |     |     |     |     |     |     |     |     |     |                 ||                                             ||
 * |  o  |     |     |     |     |     |     |     |     |     |                 ||                                             ||
 * |  o  |     |     |     |     |     |     |     |     |     |                 ||                                             ||
 * | RS232/422 |     |     |     |     |     |     |     |     |                 ||                                             ||
 * | P4  | P3  |     |     |     |     |     |     |     |     |                 |+---------------------------------------------+|
 * |     |     |     |     |     |     |     |     |     |     |                 |                                               |
 * |SLOT1|SLOT2|SLOT3|SLOT4|SLOT5|SLOT6|SLOT7|SLOT7|SLOT9|     |                 |                                               |
 * +=============================================================================================================================+
 *
 * History of Force Computers
 *---------------------------
 * See fccpu30.cpp
 *
 * Misc links about Force Computers and this board:
 *-------------------------------------------------
 * http://bitsavers.org/pdf/forceComputers/
 *
 * Description, from datasheets etc
 * --------------------------------
 * - Desktop station for 32 bit VMEbus environments
 * - Two 9 slot motherboards for A32/D32 wide VMEbus (Pl,P2)
 * - 280W power supply to drive VMEbus and mass storage memory
 * - 7HE 19 inch metal chassis including modules for drives,
 *   power supply and connectors (344mm x 520mm x 400mm).
 * - High modularity(!)
 * - One 1HE fan module including 3 fans for optimal cooling
 * - Flexible mounting and very little time expenditure for
 *   repairs through modularity
 * - Status indicators and switches of the VME boards are
 *   directly accessible on the front of the system
 * - One 5 1/4" full height space for the floppy drive
 * - One 5 1/4" full height space for the winchester drive
 * - Up to 6 free slots for system expansion
 *
 * Features per version
 * --------------------------------------------------------------------------
 *  Description             miniFORCE 2P21A  miniFORCE 2P21   miniFORCE 2P21S
 * --------------------------------------------------------------------------
 *  CPU 68020                20 MHz           16.7 MHz         12.5 MHz
 *  FPU 68881                20 MHz           16.7 MHz         12.5 MHz
 *  Memory SRAM              512KB            512KB            512KB
 *  Serial 68561 MPSC        2 RS232 ports    2 RS232 ports    2 RS232 ports
 *  Winchester HDD           51 MB            51 MB            20 MB
 *  Floppy                   1 MB             1 MB             1 MB
 *  Timer 68230 PIT          1                1                1
 *  RTOS                     PDOS             PDOS             PDOS
 * --------------------------------------------------------------------------
 *
 * Address Map from CPU-21 board perspective
 * --------------------------------------------------------------------------
 *  Range                   Decscription
 * --------------------------------------------------------------------------
 * 00000000-0007FFFF        Local 512KB SRAM CPU-21 CPU board
 * 00080000-000FFFFF        VME A32 512KB SRAM CPU-22 SRAM board (optional)
 * 00080000-FAFFFFFF        VME A32 Memory if no CPU-22 installed
 * 00100000-FAFFFFFF        VME A32 Memory if CPU-22 installed
 * FCB00000-FCB001FF        VME A24 First SIO-1 card (optional)
 * FCB01000-FCB0100F        VME A24 WFC-l card
 * FCB02000-FCB022FF        VME A24 ASCU-l/2 card (optional)
 * FF000000-FF07FFFF        EPROM Area 1
 * FF080000-FFFFFFFF        Local I/O devices
 * --------------------------------------------------------------------------
 */
#include "emu.h"
#include "bus/vme/vme.h"
#include "bus/vme/cp31.h"
#include "bus/vme/hcpu30.h"
#include "bus/vme/sys68k_cpu20.h"
#include "bus/vme/sys68k_iscsi.h"
#include "bus/vme/sys68k_isio.h"

#define VERBOSE (0) // (LOG_GENERAL)
//#define LOG_OUTPUT_FUNC printf
#include "logmacro.h"


#ifdef _MSC_VER
#define FUNCNAME __func__
#else
#define FUNCNAME __PRETTY_FUNCTION__
#endif


namespace {

class miniforce_state : public driver_device
{
public:
	miniforce_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
	{
	}

	void miniforce(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
};

/* Start it up */
void miniforce_state::machine_start()
{
	LOG("%s\n", FUNCNAME);
}

/* Start it up */
void miniforce_state::machine_reset()
{
	LOG("%s\n", FUNCNAME);
}

/* Input ports */
static INPUT_PORTS_START (miniforce)
INPUT_PORTS_END

static void miniforce_vme_cards(device_slot_interface &device)
{
	device.option_add("cp31",   VME_CP31);
	device.option_add("cpu21",  VME_SYS68K_CPU21);
	device.option_add("isio",   VME_SYS68K_ISIO1);
	device.option_add("iscsi",  VME_SYS68K_ISCSI1);
	device.option_add("hcpu30", VME_HCPU30);
}

/*
 * Machine configuration
 */
void miniforce_state::miniforce(machine_config &config)
{
//  ->set_addrmap(AS_PROGRAM, &miniforce_state::miniforce_mem);
	VME(config, "vme");
	VME_SLOT(config, "vme:slot1", miniforce_vme_cards, "cpu21");
	VME_SLOT(config, "vme:slot2", miniforce_vme_cards, nullptr);
	VME_SLOT(config, "vme:slot3", miniforce_vme_cards, nullptr);
	VME_SLOT(config, "vme:slot4", miniforce_vme_cards, nullptr);
	VME_SLOT(config, "vme:slot5", miniforce_vme_cards, nullptr);
	VME_SLOT(config, "vme:slot6", miniforce_vme_cards, nullptr);
	VME_SLOT(config, "vme:slot7", miniforce_vme_cards, nullptr);
	VME_SLOT(config, "vme:slot8", miniforce_vme_cards, nullptr);
	VME_SLOT(config, "vme:slot9", miniforce_vme_cards, nullptr);
}

ROM_START(miniforce)
ROM_END

} // anonymous namespace


/* Drivers TODO: setup distinct miniforce machine configurations */
/*    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY            FULLNAME     FLAGS */
COMP( 1987, miniforce, 0,      0,      miniforce, miniforce, miniforce_state, empty_init, "Force Computers", "miniFORCE", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



miniframe.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Dirk Best, R. Belmont
/***************************************************************************

    Convergent Miniframe

    Preliminary driver by R. Belmont based on unixpc.cpp by Dirk Best & R. Belmont

***************************************************************************/


#include "emu.h"
#include "cpu/m68000/m68010.h"
#include "imagedev/floppy.h"
#include "machine/ram.h"
#include "machine/wd_fdc.h"
#include "machine/bankdev.h"
#include "machine/pit8253.h"
#include "machine/pic8259.h"
#include "screen.h"


namespace {

/***************************************************************************
    DRIVER STATE
***************************************************************************/

class miniframe_state : public driver_device
{
public:
	miniframe_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_wd2797(*this, "wd2797")
		, m_floppy(*this, "wd2797:0:525dd")
		, m_ramrombank(*this, "ramrombank")
		, m_mapram(*this, "mapram")
	{ }

	void miniframe(machine_config &config);

private:
	required_device<m68010_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<wd2797_device> m_wd2797;
	required_device<floppy_image_device> m_floppy;
	required_device<address_map_bank_device> m_ramrombank;

	[[maybe_unused]] uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint16_t ram_mmu_r(offs_t offset);
	void ram_mmu_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

	void general_ctrl_w(uint16_t data);

	void wd2797_intrq_w(int state);
	void wd2797_drq_w(int state);

	required_shared_ptr<uint16_t> m_mapram;

	void miniframe_mem(address_map &map) ATTR_COLD;
	void ramrombank_map(address_map &map) ATTR_COLD;

	uint16_t *m_ramptr = nullptr;
	uint32_t m_ramsize = 0;
	uint16_t m_diskdmasize = 0;
	uint32_t m_diskdmaptr = 0;
	bool m_fdc_intrq = false;
};


/***************************************************************************
    MEMORY
***************************************************************************/

static constexpr unsigned MMU_MAX_PAGES = 1024;
static constexpr uint16_t MMU_WRITE_ENABLE = 0x8000;
static constexpr uint16_t MMU_STATUS_MASK  = 0x6000;
static constexpr uint16_t MMU_STATUS_NOT_PRESENT = 0x0000;
static constexpr uint16_t MMU_STATUS_PRESENT_NOT_ACCESSED = 0x2000;
static constexpr uint16_t MMU_STATUS_ACCESSED_NOT_WRITTEN = 0x4000;
static constexpr uint16_t MMU_STATUS_ACCESSED_WRITTEN = 0x6000;

uint16_t miniframe_state::ram_mmu_r(offs_t offset)
{
	uint8_t fc = m_maincpu->get_fc();
	uint16_t mapentry = m_mapram[(offset >> 11) & 0x7ff];

	if ((offset < ((512*1024)>>1)) && (fc != M68K_FC_SUPERVISOR_DATA) && (fc != M68K_FC_SUPERVISOR_PROGRAM))
	{
		fatalerror("mmu: user mode access to lower 512K, need to generate a fault\n");
	}

	if ((mapentry & MMU_STATUS_MASK) != MMU_STATUS_NOT_PRESENT)
	{
		uint32_t addr = (offset & 0x7ff) | ((mapentry & 0xfff) << 11);
		//printf("mmu_r: orig %x entry %04x xlate %x\n", offset, mapentry, addr);

		// indicate page has been read
		if ((mapentry & MMU_STATUS_MASK) == MMU_STATUS_PRESENT_NOT_ACCESSED)
		{
			m_mapram[(offset >> 11) & 0x7ff] &= ~MMU_STATUS_MASK;
			m_mapram[(offset >> 11) & 0x7ff] |= MMU_STATUS_ACCESSED_NOT_WRITTEN;
		}

		return m_ramptr[addr];
	}
	else
	{
		fatalerror("miniframe: invalid MMU page accessed, need to throw a fault\n");
	}
}

void miniframe_state::ram_mmu_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	uint8_t fc = m_maincpu->get_fc();
	uint16_t mapentry = m_mapram[(offset >> 11) & 0x7ff];

	if ((offset < ((512*1024)>>1)) && (fc != M68K_FC_SUPERVISOR_DATA) && (fc != M68K_FC_SUPERVISOR_PROGRAM))
	{
		fatalerror("mmu: user mode access to lower 512K, need to generate a fault\n");
	}

	if ((mapentry & MMU_STATUS_MASK) != MMU_STATUS_NOT_PRESENT)
	{
		uint32_t addr = (offset & 0x7ff) | ((mapentry & 0xfff) << 11);
		//printf("mmu_w: orig %x entry %04x xlate %x\n", offset, mapentry, addr);

		if (!(mapentry & MMU_WRITE_ENABLE) && (fc != M68K_FC_SUPERVISOR_PROGRAM) && (fc != M68K_FC_SUPERVISOR_DATA))
		{
			fatalerror("mmu: write protection violation, need to throw a fault\n");
		}

		// indicate page has been written
		// we know it's OK to just OR this
		m_mapram[(offset >> 11) & 0x7ff] |= MMU_STATUS_ACCESSED_WRITTEN;

		COMBINE_DATA(&m_ramptr[addr]);
	}
	else
	{
		fatalerror("miniframe: invalid MMU page accessed, need to throw a fault\n");
	}
}

void miniframe_state::general_ctrl_w(uint16_t data)
{
	if (data & 0x1000)  // ROM mirror at 0 if set
	{
		m_ramrombank->set_bank(1);
	}
	else
	{
		m_ramrombank->set_bank(0);
	}

	logerror("%x to general_ctrl_w\n", data);
}

void miniframe_state::machine_start()
{
	m_ramptr = (uint16_t *)m_ram->pointer();
	m_ramsize = m_ram->size();
}

void miniframe_state::machine_reset()
{
	// force ROM into lower mem on reset
	m_ramrombank->set_bank(0);

	// invalidate all pages by clearing all entries
	memset(m_mapram, 0, MMU_MAX_PAGES * sizeof(uint16_t));
}

/***************************************************************************
    VIDEO
***************************************************************************/

uint32_t miniframe_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}


/***************************************************************************
    ADDRESS MAPS
***************************************************************************/

void miniframe_state::miniframe_mem(address_map &map)
{
	map(0x000000, 0x3fffff).m(m_ramrombank, FUNC(address_map_bank_device::amap16));
	map(0x400000, 0x4007ff).ram().share("mapram");
	map(0x450000, 0x450001).w(FUNC(miniframe_state::general_ctrl_w));
	map(0x800000, 0x81ffff).rom().region("bootrom", 0);
	map(0xc00000, 0xc00007).rw("pit8253", FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0xc40000, 0xc40007).rw("baudgen", FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0xc90000, 0xc90003).rw("pic8259", FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
}

void miniframe_state::ramrombank_map(address_map &map)
{
	map(0x000000, 0x3fffff).rom().region("bootrom", 0);
	map(0x400000, 0x7fffff).rw(FUNC(miniframe_state::ram_mmu_r), FUNC(miniframe_state::ram_mmu_w));
}

/***************************************************************************
    INPUT PORTS
***************************************************************************/

static INPUT_PORTS_START( miniframe )
INPUT_PORTS_END


/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

static void miniframe_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

void miniframe_state::miniframe(machine_config &config)
{
	// basic machine hardware
	M68010(config, m_maincpu, XTAL(10'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &miniframe_state::miniframe_mem);

	// internal ram
	RAM(config, RAM_TAG).set_default_size("1M").set_extra_options("2M");

	// RAM/ROM bank
	ADDRESS_MAP_BANK(config, "ramrombank").set_map(&miniframe_state::ramrombank_map).set_options(ENDIANNESS_BIG, 16, 32, 0x400000);

	// floppy
	WD2797(config, m_wd2797, 1000000);
//  m_wd2797->intrq_wr_callback().set(FUNC(miniframe_state::wd2797_intrq_w));
//  m_wd2797->drq_wr_callback().set(FUNC(miniframe_state::wd2797_drq_w));
	FLOPPY_CONNECTOR(config, "wd2797:0", miniframe_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);

	// 8263s
	pit8253_device &pit8253(PIT8253(config, "pit8253", 0));
	pit8253.set_clk<0>(76800);
	pit8253.set_clk<1>(76800);
	pit8253.out_handler<0>().set("pic8259", FUNC(pic8259_device::ir4_w)); // FIXME: fighting for IR4 - error, or needs input merger?
	// chain clock 1 output into clock 2
	pit8253.out_handler<1>().set("pit8253", FUNC(pit8253_device::write_clk2));
	// and ir4 on the PIC
	pit8253.out_handler<1>().append("pic8259", FUNC(pic8259_device::ir4_w));

	pit8253_device &baudgen(PIT8253(config, "baudgen", 0));
	baudgen.set_clk<0>(1228800);
	baudgen.set_clk<1>(1228800);
	baudgen.set_clk<2>(1228800);

	// PIC8259s
	pic8259_device &pic8259(PIC8259(config, "pic8259", 0));
	pic8259.out_int_callback().set_inputline(m_maincpu, M68K_IRQ_4);
	pic8259.in_sp_callback().set_constant(1);
}


/***************************************************************************
    ROM DEFINITIONS
***************************************************************************/

ROM_START( minifram )
	ROM_REGION16_BE(0x400000, "bootrom", 0)
	ROM_LOAD16_BYTE("72-00357.bin", 0x000001, 0x002000, CRC(17c2749c) SHA1(972b5300b4d6ec65536910eab2b8550b9df9bb4d))
	ROM_LOAD16_BYTE("72-00356.bin", 0x000000, 0x002000, CRC(28b6c23a) SHA1(479e739a8154b6754e2e9b1fcfeb99d6ceaf9dbe))
ROM_END

} // anonymous namespace


/***************************************************************************
    GAME DRIVERS
***************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY       FULLNAME     FLAGS
COMP( 1985, minifram, 0,      0,      miniframe, miniframe, miniframe_state, empty_init, "Convergent", "Miniframe", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



minitel_2_rpic.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Jean-Francois DEL NERO
/***************************************************************************

    Minitel 2

    The Minitel is a small, on-line computer/Videotex terminal with multi-services that
    can be connected to any French telephone line. This terminal was widely used in France
    during the 80's and 90's.

    There are several models and versions. Most of them are based on 8051 compatible MCUs
    and EF9345 semi graphic video chip.

    The current implementation is a Minitel 2 from "La RADIOTECHNIQUE PORTENSEIGNE" / RPIC (Philips)
    More Minitel hardware related informations are available on this page :
    http://hxc2001.free.fr/minitel

    What is implemented and working :

    - Main MCU
    - Video output
    - Keyboard
    - 24C02 EPROM
    - Modem serial interface.
    - The rear serial port (prise péri-informatique).

    What is not yet implemented :

    - Sound output.
    - Screen should go blank when switched off

    The original firmware and the experimental demo ROM are currently both working.

    Please note the current special function keys assignation :

    F1 -> Suite
    F2 -> Retour
    F3 -> Envoi
    F4 -> Repetition
    F5 -> TEL
    F6 -> Guide
    F7 -> Sommaire
    F8 -> Connexion/Fin
    F10-> ON / OFF
    Alt Gr -> Fonction

    With the official ROM you need to press F10 to switch on the CRT.

    Modem and external port can be exported to the "modem" and "periinfo"
    interfaces.
    Example : Command line options to use to create a TCP socket linked to
    the modem port : "-modem null_modem -bitb socket.127.0.0.1:20000"
    Once mame started you can then send vdt files with netcat to this socket.

    Example 2 : Connecting the modem and periinfo ports to different TCP
    sockets : "-modem null_modem -bitb1 socket.127.0.0.1:20000
               -periinfo null_modem -bitb2 socket.127.0.0.1:20001"

****************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/clock.h"
#include "machine/i2cmem.h"
#include "machine/timer.h"
#include "video/ef9345.h"

#include "emupal.h"
#include "screen.h"
#include "softlist.h"

#include "logmacro.h"

namespace {

// 14174 Control register bits definition
enum
{
	CTRL_REG_MCBC      = 1 << 0,
	CTRL_REG_DTMF      = 1 << 1,
	CTRL_REG_CRTON     = 1 << 3,
	CTRL_REG_OPTO      = 1 << 4,
	CTRL_REG_LINERELAY = 1 << 5,
};

// 80C32 Port IO usage definitions
enum
{
	PORT_1_KBSERIN  = 1 << 0,
	PORT_1_MDM_DCDn = 1 << 1,
	PORT_1_MDM_PRD  = 1 << 2,
	PORT_1_MDM_TXD  = 1 << 3,
	PORT_1_MDM_RTS  = 1 << 4,
	PORT_1_KBLOAD   = 1 << 5,
	PORT_1_SCL      = 1 << 6,
	PORT_1_SDA      = 1 << 7,
};

enum
{
	PORT_3_SER_RXD = 1 << 0,
	PORT_3_SER_TXD = 1 << 1,
	PORT_3_SER_ZCO = 1 << 2,
	PORT_3_MDM_RXD = 1 << 3,
	PORT_3_SER_RDY = 1 << 5,
};

class minitel_state : public driver_device
{
public:
	minitel_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ts9347(*this, "ts9347")
		, m_palette(*this, "palette")
		, m_i2cmem(*this, "i2cmem")
		, m_modem(*this, "modem")
		, m_serport(*this, "periinfo")
		, m_io_kbd(*this, "Y%u", 0)
	{
	}

	void minitel2(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<i80c32_device> m_maincpu;
	required_device<ts9347_device> m_ts9347;
	required_device<palette_device> m_palette;
	required_device<i2cmem_device> m_i2cmem;
	required_device<rs232_port_device> m_modem;
	required_device<rs232_port_device> m_serport;

	required_ioport_array<16> m_io_kbd;

	uint8_t port1 = 0, port3 = 0;

	int keyboard_para_ser = 0;
	uint8_t keyboard_x_row_reg = 0;

	uint8_t last_ctrl_reg = 0;

	int lineconnected = 0;
	int tonedetect = 0;

	TIMER_DEVICE_CALLBACK_MEMBER(minitel_scanline);

	void update_modem_state();

	void port1_w(uint8_t data);
	void port3_w(uint8_t data);
	uint8_t port1_r();
	uint8_t port3_r();

	void serial_rxd(int state);

	void dev_ctrl_reg_w(offs_t offset, uint8_t data);
	uint8_t dev_keyb_ser_r(offs_t offset);

	uint8_t ts9347_io_r(offs_t offset);
	void ts9347_io_w(offs_t offset, uint8_t data);

	void mem_prg(address_map &map) ATTR_COLD;
	void mem_io(address_map &map) ATTR_COLD;
};

void minitel_state::machine_start()
{
	m_palette->set_pen_color(0, 0, 0, 0);
	m_palette->set_pen_color(1, 80, 80, 80);
	m_palette->set_pen_color(2, 160, 160, 160);
	m_palette->set_pen_color(3, 230, 230, 230);
	m_palette->set_pen_color(4, 40, 40, 40);
	m_palette->set_pen_color(5, 120, 120, 120);
	m_palette->set_pen_color(6, 200, 200, 200);
	m_palette->set_pen_color(7, 255, 255, 255);
}

void minitel_state::port1_w(uint8_t data)
{
	LOG("port_w: write %02X to PORT1\n", data);

	if( (port1 ^ data) & PORT_1_KBSERIN )
	{
		LOG("PORT_1_KBSERIN : %d \n", data & PORT_1_KBSERIN );
	}

	if( (port1 ^ data) & PORT_1_MDM_DCDn )
	{
		LOG("PORT_1_MDM_DCD : %d \n", data & PORT_1_MDM_DCDn );
	}

	if( (port1 ^ data) & PORT_1_MDM_PRD )
	{
		LOG("PORT_1_MDM_PRD : %d \n", data & PORT_1_MDM_PRD );
	}

	if( (port1 ^ data) & PORT_1_MDM_TXD )
	{
		LOG("PORT_1_MDM_TXD : %d \n", data & PORT_1_MDM_TXD );
	}

	if(lineconnected)
	{
		m_modem->write_txd(!!(data & PORT_1_MDM_TXD));
	}

	if( (port1 ^ data) & PORT_1_MDM_RTS )
	{
		LOG("PORT_1_MDM_RTS : %d \n", data & PORT_1_MDM_RTS );
	}

	if( (port1 ^ data) & PORT_1_KBLOAD )
	{
		LOG("PORT_1_KBLOAD : %d PC:0x%x\n", data & PORT_1_KBLOAD, m_maincpu->pc() );

		if(data & PORT_1_KBLOAD)
			keyboard_para_ser = 1;
		else
			keyboard_para_ser = 0;
	}

	if( (port1 ^ data) & PORT_1_SCL )
	{
		LOG("PORT_1_SCL : %d \n", data & PORT_1_SCL );
		m_i2cmem->write_scl( (data & PORT_1_SCL) ? 1 : 0);
	}

	if( (port1 ^ data) & PORT_1_SDA )
	{
		LOG("PORT_1_SDA : %d \n", data & PORT_1_SDA );
		m_i2cmem->write_sda( (data & PORT_1_SDA) ? 1 : 0);
	}

	port1 = data;
}

void minitel_state::port3_w(uint8_t data)
{
	LOG("port_w: write %02X to PORT3\n", data);

	m_serport->write_txd( !!(data & PORT_3_SER_TXD) );

	port3 = (port3 & PORT_3_SER_RXD) | (data & ~PORT_3_SER_RXD);
}

void minitel_state::serial_rxd(int state)
{
	if (state)
		port3 |= PORT_3_SER_RXD;
	else
		port3 &= ~PORT_3_SER_RXD;
}


void minitel_state::update_modem_state()
{
	// 1300 Hz tone detection :  PORT_1_MDM_RTS = 1, CTRL_REG_DTMF = 0, CTRL_REG_MCBC = 0
	if(  ( last_ctrl_reg & CTRL_REG_LINERELAY ) &&
		 ( port1 & PORT_1_MDM_RTS ) &&
		!( last_ctrl_reg & CTRL_REG_DTMF ) &&
		!( last_ctrl_reg & CTRL_REG_MCBC ) )
	{
		tonedetect = 1;
	}
	else
	{
		tonedetect = 0;
	}

	// Main transmission mode :  PORT_1_MDM_RTS = 0, CTRL_REG_DTMF = 1, CTRL_REG_MCBC = 0
	if( last_ctrl_reg & CTRL_REG_LINERELAY )
		lineconnected = 1;
	else
		lineconnected = 0;
};

uint8_t minitel_state::port1_r()
{
	uint8_t data;

	LOG("port_r: read %02X from PORT1 - Keyboard -> %x\n", port1,((keyboard_x_row_reg>>7)&1));

	data = ( ( (port1 & (0xFE & ~PORT_1_SDA) ) | ( (keyboard_x_row_reg>>7)&1) ) );
	data |= (m_i2cmem->read_sda() ? PORT_1_SDA : 0);

	update_modem_state();

	if( lineconnected )
		data &= ~PORT_1_MDM_DCDn;
	else
		data |=  PORT_1_MDM_DCDn;

	return data;
}

uint8_t minitel_state::port3_r()
{
	uint8_t data;

	LOG("port_r: read %02X from PORT3\n", port3);

	update_modem_state();

	data = (port3 & ~(PORT_3_SER_RDY)); // External port ready state

	return data;
}

void minitel_state::dev_ctrl_reg_w(offs_t offset, uint8_t data)
{
	if( last_ctrl_reg != data)
	{
		LOG("minitel_state::hw_ctrl_reg : %x %x\n",offset, data);

		if( (last_ctrl_reg ^ data) & CTRL_REG_DTMF )
		{
			LOG("CTRL_REG_DTMF : %d \n", data & CTRL_REG_DTMF );
		}

		if( (last_ctrl_reg ^ data) & CTRL_REG_MCBC )
		{
			LOG("CTRL_REG_MCBC : %d \n", data & CTRL_REG_MCBC );
		}

		if( (last_ctrl_reg ^ data) & CTRL_REG_OPTO )
		{
			LOG("CTRL_REG_OPTO : %d \n", data & CTRL_REG_OPTO );
		}

		if( (last_ctrl_reg ^ data) & CTRL_REG_LINERELAY )
		{
			LOG("CTRL_REG_RELAY : %d \n", data & CTRL_REG_LINERELAY );
		}

		if( (last_ctrl_reg ^ data) & CTRL_REG_CRTON )
		{
			LOG("CTRL_REG_CRTON : %d \n", data & CTRL_REG_CRTON );
		}
	}

	last_ctrl_reg = data;
}

uint8_t minitel_state::dev_keyb_ser_r(offs_t offset)
{
	LOG("minitel_state::keyb read : %x\n",offset);

	if ( keyboard_para_ser )
	{
		// load the 4014 with the keyboard row state
		keyboard_x_row_reg = m_io_kbd[(offset>>8)&0xF]->read();
		LOG("4014 Load : 0x%.2X 0x%.2X\n",(offset>>8)&0xF,keyboard_x_row_reg);
	}
	else
	{
		//shift the keyboard register...
		keyboard_x_row_reg = keyboard_x_row_reg << 1;
	}

	return 0xFF;
}

uint8_t minitel_state::ts9347_io_r(offs_t offset)
{
	return m_ts9347->data_r(offset);
}

void minitel_state::ts9347_io_w(offs_t offset, uint8_t data)
{
	LOG("minitel_state::ts9347_io_w : %x %x\n",offset, data);

	m_ts9347->data_w(offset, data);
}

TIMER_DEVICE_CALLBACK_MEMBER(minitel_state::minitel_scanline)
{
	m_ts9347->update_scanline((uint16_t)param);
}

void minitel_state::mem_prg(address_map &map)
{
	map(0x0000, 0x7fff).rom();
}

void minitel_state::mem_io(address_map &map)
{
	map(0x2000, 0x3fff).rw(FUNC(minitel_state::dev_keyb_ser_r), FUNC(minitel_state::dev_ctrl_reg_w));
	/* ts9347 */
	map(0x4000, 0x5ffF).rw(FUNC(minitel_state::ts9347_io_r), FUNC(minitel_state::ts9347_io_w));
}

/* Input ports */
static INPUT_PORTS_START( minitel2 )
	PORT_START("Y0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('-')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Suite") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Retour") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')

	PORT_START("Y1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Connexion/Fin") PORT_CODE(KEYCODE_F8)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Fonction") PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT) // Right maj
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) // Left maj

	PORT_START("Y2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')

	PORT_START("Y3")

	PORT_START("Y4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(':')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Envoi") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Repetition") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')

	PORT_START("Y5")

	PORT_START("Y6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('?')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tel") PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')

	PORT_START("Y7")

	PORT_START("Y8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(';')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(']')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BS") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BackS  Delete") PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')

	PORT_START("Y9")

	PORT_START("Y10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('*')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('#')

	PORT_START("Y11")

	PORT_START("Y12")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("On/Off") PORT_CODE(KEYCODE_F10)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')

	PORT_START("Y13")

	PORT_START("Y14")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\'')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('[')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Guide") PORT_CODE(KEYCODE_F6)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Sommaire") PORT_CODE(KEYCODE_F7)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')

	PORT_START("Y15")

INPUT_PORTS_END

static DEVICE_INPUT_DEFAULTS_START( m_modem )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_75 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_1200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

static DEVICE_INPUT_DEFAULTS_START( m_serport )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_1200 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_1200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void minitel_state::minitel2(machine_config &config)
{
	/* basic machine hardware */
	I80C32(config, m_maincpu, XTAL(14'318'181)); //verified on pcb
	m_maincpu->set_addrmap(AS_PROGRAM, &minitel_state::mem_prg);
	m_maincpu->set_addrmap(AS_IO, &minitel_state::mem_io);
	m_maincpu->port_in_cb<1>().set(FUNC(minitel_state::port1_r));
	m_maincpu->port_out_cb<1>().set(FUNC(minitel_state::port1_w));
	m_maincpu->port_in_cb<3>().set(FUNC(minitel_state::port3_r));
	m_maincpu->port_out_cb<3>().set(FUNC(minitel_state::port3_w));

	I2C_24C02(config, m_i2cmem);

	TS9347(config, m_ts9347, 0);
	m_ts9347->set_palette_tag(m_palette);

	TIMER(config, "minitel_sl", 0).configure_scanline(FUNC(minitel_state::minitel_scanline), "screen", 0, 10);

	RS232_PORT(config, m_modem, default_rs232_devices, nullptr);
	m_modem->rxd_handler().set_inputline(m_maincpu, MCS51_INT1_LINE).invert();
	m_modem->set_option_device_input_defaults("null_modem", DEVICE_INPUT_DEFAULTS_NAME(m_modem));
	m_modem->set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(m_modem));

	RS232_PORT(config, m_serport, default_rs232_devices, nullptr);
	m_serport->rxd_handler().set(FUNC(minitel_state::serial_rxd));
	m_serport->set_option_device_input_defaults("null_modem", DEVICE_INPUT_DEFAULTS_NAME(m_serport));
	m_serport->set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(m_serport));

	lineconnected = 0;

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_screen_update("ts9347", FUNC(ts9347_device::screen_update));
	screen.set_size(512, 312);
	screen.set_visarea(2, 512-10, 0, 278-1);

	PALETTE(config, m_palette).set_entries(8+1);

	// Send a fake 1300 Hz carrier (emulate the modem ZCO output)
	auto &fake_1300hz_clock(CLOCK(config, "fake_1300hz_clock", 1300));
	fake_1300hz_clock.set_pulse_width(attotime::from_usec(384));
	fake_1300hz_clock.signal_handler().set_inputline(m_maincpu, MCS51_INT0_LINE);
}

ROM_START( minitel2 )

	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS("ft_bv4")

	ROM_SYSTEM_BIOS(0, "ft_bv4", "Minitel 2 ROM BV4")
	ROMX_LOAD( "minitel2_bv4.bin",   0x0000, 0x8000, CRC(8844a0a7) SHA1(d3e9079b080dbcee27ad870ec6c39ac42e7deacf), ROM_BIOS(0) )

	ROM_SYSTEM_BIOS(1, "demov1", "Minitel 2 Demo")
	ROMX_LOAD( "demo_minitel.bin",   0x0000, 0x8000, CRC(607f2482) SHA1(7965edbef68e45d09dc67a4684da56003eff6328), ROM_BIOS(1) )

	ROM_SYSTEM_BIOS(2, "ft_bv9", "Minitel 2 ROM Bv9")
	ROMX_LOAD( "bv9.1402",           0x0000, 0x8000, CRC(ace5d65e) SHA1(c8d589f8af6bd7d339964fdece937a76db972115), ROM_BIOS(2) )

	ROM_REGION( 0x4000, "ts9347", 0 )
	ROM_LOAD( "charset.rom", 0x0000, 0x2000, BAD_DUMP CRC(b2f49eb3) SHA1(d0ef530be33bfc296314e7152302d95fdf9520fc) )            // from dcvg5k
ROM_END

} // anonymous namespace

COMP( 1989, minitel2, 0, 0, minitel2, minitel2, minitel_state, empty_init, "Philips", "Minitel 2", MACHINE_NO_SOUND )



mips.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * An emulation of systems designed and manufactured by MIPS Computer Systems,
 * all of which use MIPS R2000, R3000 or R6000 CPUs, and run the RISC/os
 * operating system.
 *
 * This driver is intended to eventually cover the following models:
 *
 *   Model       Board    CPU      Clock  Slots    Disk  Package       Other
 *   M/500       R2300    R2000     5MHz  VME      ESDI
 *   M/800       R2600    R2000     8MHz  VME      ESDI
 *   M/1000      R2800    R2000    10MHz  VME      ESDI
 *   M/120-3     R2400    R2000  12.5MHz  PC-AT    SCSI  Deskside      aka Intrepid
 *   M/120-5     R2400    R2000    16MHz  PC-AT    SCSI  Deskside
 *   M/180       R2400
 *   M/2000-6    R3200    R3000    20MHz  VMEx13   SMD   Rack Cabinet
 *   M/2000-8    R3200    R3000    25MHz  VMEx13   SMD   Rack Cabinet
 *   M/2000-?    RB3125   R3000    33MHz
 *   RC2030      I2000    R2000    16MHz           SCSI  Desktop       aka M/12, Jupiter
 *   RS2030      I2000    R2000    16MHz           SCSI  Desktop       aka M/12, Jupiter
 *   RC3230      R3030    R3000    25MHz  PC-ATx1  SCSI  Desktop       aka M/20, Pizazz
 *   RS3230      R3030    R3000    25MHz  PC-ATx1  SCSI  Desktop       aka M/20, Pizazz, Magnum 3000
 *   RC3240               R3000    25MHz  PC-ATx4  SCSI  Deskside      M/120 with CPU-board upgrade
 *   RC3330               R3000    33MHz  PC-AT    SCSI  Desktop
 *   RS3330               R3000    33MHz  PC-AT    SCSI  Desktop
 *   RC3260               R3000    25MHz  VMEx7    SCSI  Pedestal
 *   RC3360      RB3133   R3000    33MHz  VME      SCSI  Pedestal
 *   RC3370      RB3133
 *   RC6260      R6300    R6000    66MHz  VME      SCSI  Pedestal
 *   RC6280      R6300    R6000    66MHz  VMEx6    SMD   Data Center
 *   RC6380-100           R6000x1  66MHz  VME      SMD   Data Center
 *   RC6380-200           R6000x2  66MHz  VME      SMD   Data Center
 *   RC6380-400           R6000x4  66MHz  VME      SMD   Data Center
 *
 * Sources:
 *
 *   http://www.umips.net/
 *   http://www.geekdot.com/the-mips-rs2030/
 *   http://www.jp.netbsd.org/ports/mipsco/models.html
 *   http://www.prumpleffer.de/~miod/machineroom/machines/mips/magnum/index.html
 *   https://web.archive.org/web/20140518203135/http://no-l.org/pages/riscos.html
 *
 * TODO (rx3230)
 *   - verify/complete address maps
 *   - keyboard controller and interrupts
 *   - isa slot and colour graphics board
 *   - idprom
 *
 *   Ref   Part                      Function
 *
 * I2000 system board:
 *
 *         MIPS R2000A               Main CPU
 *         PACEMIPS PR2010A          Floating point unit
 *         33.3330 MHz crystal       CPU clock
 *         DL15CC200
 *         DDU7F-25                  Delay line (10 taps @ 2.5ns per tap)
 *         VLSI VL85C30-08PC         Serial port controller
 *         ST Z8038AB1 FIO           Parallel port controller
 *         1.8432 MHz crystal        Serial clock
 *         NEC D70216L-10            I/O processor
 *         Adaptec AIC-6250DL        SCSI controller
 *         AMD AM7990DCB/80          Ethernet controller
 *         20 MHz crystal
 *         2VP5U9                    DC/DC converter (Ethernet transceiver power?)
 *         WDC WD37C65BJW            Floppy controller
 *         Intel P8742AH             Keyboard controller
 *         Dallas DS1287             RTC and NVRAM
 *         buzzer                    Connected to keyboard controller?
 *
 *         P4C164-25PC               Cache RAM? (8Kx8, total 112KiB)
 *   U?-U?                           14 parts
 *
 *         27C512 64K EPROM          V50 IPL (64Kx8, total 256KiB)
 *   U139-U142                       4 parts
 *
 *         M5M4464                   V50 RAM (64Kx4, total 128KiB)
 *   U164-U167                       4 parts
 *
 *
 * Jupiter video board:
 *
 *         Idt75C458                 256x24 Color RAMDAC
 *         Bt438KC                   Clock generator
 *         108.180 MHz crystal
 *
 *         D41264V-12                Video RAM (64Kx4, total 1280KiB)
 *   U?-U?                           40 parts
 *
 *
 * R3030 system board (Assy. No. 03-00082- rev J):
 *
 *   N2B1  IDT 79R3000-25G           CPU
 *   L6B1  IDT 79R3010L-25OJ         FPU
 *   C3A2  50.0000 MHz crystal
 *   G2B8  MIPS 32-00039-000         RAMBO DMA/timer ASIC?
 *   H8B8  MIPS 32-00038-000         Cache control/write buffer ASIC?
 *   H8A3  MIPS 32-00038-000         Cache control/write buffer ASIC?
 *   E3H7  NCR 53C94                 SCSI controller
 *   C410  Intel N82072              Floppy controller
 *   B510  Z85C3010VSC               Serial controller
 *   C232  AMD AM7990JC/80           Ethernet controller
 *         AMD AM7992BDC             Ethernet serial interface
 *         M48T02                    RTC and NVRAM (labelled B6B93)
 *         MCS-48?                   Keyboard controller
 *   A7A7  DP8530V                   Clock generator
 *         AM27C1024                 IPL EPROM (128KiB, MSW)
 *         50-314-003
 *         3230 RIGHT
 *         CKSM / B098BB9
 *         TMS27C210                 IPL EPROM (128KiB, LSW)
 *         50-314-003
 *         3230 LEFT
 *         CKSM / 045A
 *
 *
 * Colour graphics board (assy. no. 03-00087- rev D):
 *
 *   UF4   Bt459KG110                256x24 Color RAMDAC
 *   UC4   Bt435KPJ                  Clock generator?
 *   OSC3  108.1800 MHz crystal      Pixel clock
 *
 *         ?                         Video RAM (total 1280KiB?)
 *   UJ11-UM11                       8 parts
 *   UJ13-UM13                       8 parts
 */
/*
 * Rx2030 WIP
 *
 *   - keyboard reset failure
 *
 * V50 internal peripherals:
 * base = 0xfe00
 * serial (sula): fe00
 *  timer (tula): fe08
 *    int (iula): fe10
 *    dma (dula): fe20
 *
 * V50 IPL diagnostic routines
 *   NVRAM       f8dc4
 *   Ethernet ID f8b58
 *   Parallel    f8c5c
 *   Keyboard    f4cbc
 *   SCC         ec0fc
 *   Floppy      ee3ea
 *   SCSI        f426e
 *   LANCE       f8f68
 *
 * V50 interrupts:
 *   intp1 <- SCU
 *   intp2 <- CPU interface
 *   intp3 <- SCC
 *   intp4 <- FIO
 *   intp5 <- LANCE
 *   intp6 <- floppy?
 *   intp7 <- SCSI
 *
 * R2000 interrupts:
 *   int0 <- ?
 *   int1 <- iop keyboard
 *   int2 <- ?
 *   int4 <- iop clock
 *   int5 <- vblank
 */
/*
 * Rx3230 WIP
 *
 *   status: boots RISC/os from network, panics during installation
 *
 * R3000 interrupts
 *  0 <- lance, scc, slot, keyboard
 *  1 <- scsi
 *  2 <- timer
 *  3 <- fpu
 *  4 <- fdc
 *  5 <- parity error
 *
 * Keyboard controller output port
 *  4: select 1M/4M SIMMs?
 *
 * rs3230 -window -nomax -ui_active -tty1 terminal -numscreens 2
 * PON failures
 *   instruction cache functionality (failed)
 *   instruction cache mats+ (skipped)
 *   data cache block refill (failed)
 *   instruction cache block refill (skipped)
 *   id prom (failed)
 *   tod - loop <1 second real time?
 *   color frame buffer (skipped)
 *   dma controller chip
 *   scsi controller chip
 *   tlb (skipped) - all pass except tlb_n (requires cpu data cache)
 *   exception (skipped)
 *   parity (failed)
 *   dma parity (skipped)
 *   at serial board (skipped)
 */

#include "emu.h"

// processors and memory
#include "cpu/mips/mips1.h"
#include "cpu/nec/v5x.h"
#include "machine/ram.h"

// i/o devices (common)
#include "machine/at_keybc.h"
#include "machine/z80scc.h"
#include "machine/upd765.h"
#include "machine/am79c90.h"

// i/o devices (rx2030)
#include "machine/mc146818.h"
#include "machine/z8038.h"
#include "machine/aic6250.h"

// i/o devices (rx3230)
#include "machine/timekpr.h"
#include "machine/ncr53c90.h"
#include "mips_rambo.h"

// busses and connectors
#include "machine/nscsi_bus.h"
#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"

#include "bus/pc_kbd/pc_kbdc.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/rs232/rs232.h"
#include "bus/rs232/hlemouse.h"

// video and audio
#include "screen.h"
#include "video/bt45x.h"
#include "video/bt459.h"
#include "sound/spkrdev.h"
#include "speaker.h"

#include "imagedev/floppy.h"

#define LOG_MMU     (1U << 1)
#define LOG_IOCB    (1U << 2)

#define VERBOSE 0
#include "logmacro.h"

namespace {

class rx2030_state : public driver_device
{
public:
	rx2030_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_iop(*this, "iop")
		, m_ram(*this, "ram")
		, m_rom(*this, "rx2030")
		, m_rtc(*this, "rtc")
		, m_fio(*this, "fio")
		, m_kbdc(*this, "kbdc")
		, m_kbd(*this, "kbd")
		, m_scc(*this, "scc")
		, m_tty(*this, "tty%u", 0U)
		, m_fdc(*this, "fdc")
		, m_scsibus(*this, "scsi")
		, m_scsi(*this, "scsi:7:aic6250")
		, m_net(*this, "net")
		, m_buzzer(*this, "buzzer")
		, m_screen(*this, "screen")
		, m_ramdac(*this, "ramdac")
		, m_vram(*this, "vram")
	{
	}

	// machine config
	void rx2030(machine_config &config);
	void rs2030(machine_config &config);
	void rc2030(machine_config &config);

	void rx2030_init();

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// address maps
	void rx2030_map(address_map &map) ATTR_COLD;
	void rs2030_map(address_map &map) ATTR_COLD;

	void iop_program_map(address_map &map) ATTR_COLD;
	void iop_io_map(address_map &map) ATTR_COLD;

	u16 mmu_r(offs_t offset, u16 mem_mask = 0xffff);
	void mmu_w(offs_t offset, u16 data, u16 mem_mask = 0xffff);

	u16 lance_r(offs_t offset, u16 mem_mask = 0xffff);
	void lance_w(offs_t offset, u16 data, u16 mem_mask = 0xffff);

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect);

private:
	enum iop_interface_status_mask : u8
	{
		FPU_ABSENT = 0x01, // clear if FPU installed
		DBG_ABSENT = 0x02, // clear if debug board installed
		IOP_IRQ    = 0x04, // set when CPU interrupts IOP
		VID_ABSENT = 0x10, // clear if video board installed
		IOP_IACK   = 0x40, // set when IOP acknowledges CPU interrupt
		IOP_NERR   = 0x80, // clear when IOP receives parity error
	};

	// processors and memory
	required_device<r2000a_device> m_cpu;
	required_device<v50_device> m_iop;
	required_device<ram_device> m_ram;
	required_region_ptr<u16> m_rom;

	// i/o devices
	required_device<mc146818_device> m_rtc;
	required_device<z8038_device> m_fio;
	required_device<at_keyboard_controller_device> m_kbdc;
	required_device<pc_kbdc_device> m_kbd;
	required_device<z80scc_device> m_scc;
	required_device_array<rs232_port_device, 2> m_tty;
	required_device<wd37c65c_device> m_fdc;
	required_device<nscsi_bus_device> m_scsibus;
	required_device<aic6250_device> m_scsi;
	required_device<am7990_device> m_net;
	required_device<speaker_sound_device> m_buzzer;

	// optional video board
	optional_device<screen_device> m_screen;
	optional_device<bt458_device> m_ramdac;
	optional_shared_ptr<u32> m_vram;

	// machine state
	u16 m_mmu[32]{};

	u8 m_iop_interface = 0;
};

class rx3230_state : public driver_device
{
public:
	rx3230_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_ram(*this, "ram")
		, m_rom(*this, "rx3230")
		, m_rambo(*this, "rambo")
		, m_scsibus(*this, "scsi")
		, m_scsi(*this, "scsi:7:ncr53c94")
		, m_net(*this, "net")
		, m_scc(*this, "scc")
		, m_tty(*this, "tty%u", 0U)
		, m_rtc(*this, "rtc")
		, m_fdc(*this, "fdc")
		, m_kbdc(*this, "kbdc")
		, m_kbd(*this, "kbd")
		, m_buzzer(*this, "buzzer")
		, m_screen(*this, "screen")
		, m_ramdac(*this, "ramdac")
		, m_vram(*this, "vram")
	{
	}

	// machine config
	void rx3230(machine_config &config);
	void rs3230(machine_config &config);
	void rc3230(machine_config &config);

	void rx3230_init();

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// address maps
	void rx3230_map(address_map &map) ATTR_COLD;
	void rs3230_map(address_map &map) ATTR_COLD;

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect);

	u16 lance_r(offs_t offset, u16 mem_mask = 0xffff);
	void lance_w(offs_t offset, u16 data, u16 mem_mask = 0xffff);

	template <u8 Source> void irq_w(int state);

private:
	// processors and memory
	required_device<r3000a_device> m_cpu;
	required_device<ram_device> m_ram;
	required_region_ptr<u32> m_rom;

	// i/o devices
	required_device<mips_rambo_device> m_rambo;
	required_device<nscsi_bus_device> m_scsibus;
	required_device<ncr53c94_device> m_scsi;
	required_device<am7990_device> m_net;
	required_device<z80scc_device> m_scc;
	required_device_array<rs232_port_device, 2> m_tty;
	required_device<m48t02_device> m_rtc;
	required_device<i82072_device> m_fdc;
	required_device<at_keyboard_controller_device> m_kbdc;
	required_device<pc_kbdc_device> m_kbd;
	required_device<speaker_sound_device> m_buzzer;

	// optional colour video board
	optional_device<screen_device> m_screen;
	optional_device<bt459_device> m_ramdac;
	optional_device<ram_device> m_vram;

	enum int_reg_mask : u8
	{
		INT_SLOT = 0x01, // expansion slot
		INT_KBD  = 0x02, // keyboard controller
		INT_SCC  = 0x04, // serial controller
		INT_SCSI = 0x08, // scsi controller
		INT_NET  = 0x10, // ethernet controller
		INT_DRS  = 0x20, // data rate select
		INT_DSR  = 0x40, // data set ready
		INT_CEB  = 0x80, // modem call indicator

		INT_CLR  = 0xff,
	};

	enum gfx_reg_mask : u8
	{
		GFX_H_BLANK   = 0x10,
		GFX_V_BLANK   = 0x20,
		GFX_COLOR_RSV = 0xce, // reserved
	};

	u8 m_int_reg = 0;
	int m_int0_state = 0;
	int m_int1_state = 0;
};

void rx2030_state::machine_start()
{
	save_item(NAME(m_mmu));
	save_item(NAME(m_iop_interface));
}

void rx2030_state::machine_reset()
{
	m_cpu->set_input_line(INPUT_LINE_RESET, 1);
}

void rx2030_state::rx2030_init()
{
	m_iop_interface = IOP_NERR | DBG_ABSENT;

	// map the configured ram and vram
	m_cpu->space(0).install_ram(0x00000000, m_ram->mask(), m_ram->pointer());

	// page zero of prom space is mapped to ram page zero
	m_cpu->space(0).install_rom(0x1fc00000, 0x1fc00fff, m_ram->pointer());

	if (!m_vram)
		m_iop_interface |= VID_ABSENT;

	/*
	 * HACK: the prom bfs code broadcasts to the network address (i.e. the
	 * host portion is "all zeroes"), instead of to the standard "all ones".
	 * This makes it very difficult to receive the bfs request in a modern host
	 * OS; the patch changes the code to broadcast to the standard broadcast
	 * address instead.
	 *
	 * Technique is identical to that described for the rx3230 below.
	 */
	switch (system_bios())
	{
	case 1:
		m_rom[0x1ab68 >> 1] = 0x0624;
		m_rom[0x1ab6a >> 1] = 0xffff;
		break;

	case 2:
		m_rom[0x1a7f8 >> 1] = 0x0624;
		m_rom[0x1a7fa >> 1] = 0xffff;
		break;
	}
}

u16 rx2030_state::mmu_r(offs_t offset, u16 mem_mask)
{
	offs_t const address = (m_mmu[(offset >> 11) & 0x1f] << 12) | ((offset << 1) & 0xfff);

	u16 const data = (m_ram->read(BYTE4_XOR_BE(address + 1)) << 8) | m_ram->read(BYTE4_XOR_BE(address + 0));

	LOGMASKED(LOG_MMU, "mmu_r offset 0x%06x reg %d page 0x%04x mapped 0x%06x data 0x%04x\n",
		(offset << 1), (offset >> 11) & 0x1f, m_mmu[(offset >> 11) & 0x1f], address, data);

	return data;
}

void rx2030_state::mmu_w(offs_t offset, u16 data, u16 mem_mask)
{
	offs_t const address = (m_mmu[(offset >> 11) & 0x1f] << 12) | ((offset << 1) & 0xfff);

	LOGMASKED(LOG_MMU, "mmu_w offset 0x%06x reg %d page 0x%04x mapped 0x%06x data 0x%04x (%s)\n",
		(offset << 1), (offset >> 11) & 0x1f, m_mmu[(offset >> 11) & 0x1f], address, data, machine().describe_context());

	if (ACCESSING_BITS_0_7)
		m_ram->write(BYTE4_XOR_BE(address + 0), data);

	if (ACCESSING_BITS_8_15)
		m_ram->write(BYTE4_XOR_BE(address + 1), data >> 8);
}

void rx2030_state::iop_program_map(address_map &map)
{
	// 00000:1ffff  128k ram (64kx4, 4 parts)
	// 20000:3ffff  128k shared (32x4k mapped pages, bits 0-11 offset, bits 12-16 mmu register)
	// 80000:fffff  512k eprom (256k x 2 copies)

	map(0x00000, 0x1ffff).ram();
	map(0x20000, 0x3ffff).rw(FUNC(rx2030_state::mmu_r), FUNC(rx2030_state::mmu_w));

	map(0x80000, 0xbffff).rom().region("rx2030", 0).mirror(0x40000);
}

void rx2030_state::iop_io_map(address_map &map)
{
	map(0x0000, 0x003f).lrw16(
		NAME([this] (offs_t offset, u16 mem_mask) { return m_mmu[offset]; }),
		NAME([this] (offs_t offset, u16 data, u16 mem_mask) { m_mmu[offset] = data; }));

	map(0x0040, 0x0043).m(m_fdc, FUNC(wd37c65c_device::map)).umask16(0xff);
	map(0x0044, 0x0045).w(m_fdc, FUNC(wd37c65c_device::dor_w)).umask16(0xff);
	map(0x0048, 0x0049).w(m_fdc, FUNC(wd37c65c_device::ccr_w)).umask16(0xff);
	//map(0x004c, 0x004d).r(m_fdc, FUNC(?)).umask16(0xff);

	map(0x0080, 0x0083).rw(m_scsi, FUNC(aic6250_device::read), FUNC(aic6250_device::write)).umask16(0xff);

	/*
	 * HACK: Substitute the keyboard "set defaults" command for the "reset"
	 * command to avoid an issue where the keyboard is still busy performing
	 * the reset and does not accept commands being sent to it to change the
	 * scan code set. Possibly caused by imperfect V50 timing and/or memory
	 * wait states that make the IOP code execute more slowly than emulated.
	 */
	map(0x00c0, 0x00c1).lrw8(NAME([this] () { return m_kbdc->data_r(); }), NAME([this] (u8 data) { m_kbdc->data_w(data == 0xff ? 0xf6 : data); })).umask16(0xff);
	map(0x00c4, 0x00c5).rw(m_kbdc, FUNC(at_keyboard_controller_device::status_r), FUNC(at_keyboard_controller_device::command_w)).umask16(0xff);

	map(0x0100, 0x0107).rw(m_scc, FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w)).umask16(0xff);

	map(0x0140, 0x0143).rw(m_net, FUNC(am7990_device::regs_r), FUNC(am7990_device::regs_w));

	map(0x0180, 0x018b).lr8(
			[] (offs_t offset)
			{
				// Ethernet MAC address (LSB first)
				static u8 const mac[] = { 0x00, 0x00, 0x6b, 0x12, 0x34, 0x56 };

				return mac[offset];
			}, "mac_r").umask16(0xff);

	// iop tests bits 0x04, 0x10 and 0x20
	map(0x01c0, 0x01c1).lr8(NAME([this] () { return m_iop_interface; })); // maybe?

	map(0x0200, 0x0201).rw(m_fio, FUNC(z8038_device::fifo_r<1>), FUNC(z8038_device::fifo_w<1>)).umask16(0xff);
	map(0x0202, 0x0203).rw(m_fio, FUNC(z8038_device::reg_r<1>), FUNC(z8038_device::reg_w<1>)).umask16(0xff);

	map(0x0240, 0x0241).w(m_rtc, FUNC(mc146818_device::address_w)).umask16(0xff00);
	map(0x0280, 0x0281).rw(m_rtc, FUNC(mc146818_device::data_r), FUNC(mc146818_device::data_w)).umask16(0xff00);

	map(0x02c0, 0x2c1).lw8([this](u8 data)
	{
		switch (data)
		{
		case 0: LOG("cpu interrupt 0 asserted\n"); m_cpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE); break;
		case 1: LOG("cpu interrupt 1 asserted\n"); m_cpu->set_input_line(INPUT_LINE_IRQ1, ASSERT_LINE); break;
		case 2: LOG("cpu interrupt 2 asserted\n"); m_cpu->set_input_line(INPUT_LINE_IRQ2, ASSERT_LINE); break;
		case 3: LOG("cpu interrupt 4 asserted\n"); m_cpu->set_input_line(INPUT_LINE_IRQ4, ASSERT_LINE); break;

		case 4:
			if (m_cpu->suspended())
			{
				LOG("cpu reset released\n");
				m_cpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
			}
			else
				m_iop->set_input_line(INPUT_LINE_IRQ2, CLEAR_LINE);
			m_iop_interface |= IOP_IACK;
			m_iop_interface &= ~IOP_IRQ;
			break;

		default:
			LOG("cpu interface command 0x%02x\n", data);
			break;

		//case 5: break; // unknown
		//case 6:
		//case 7:
			// something to do with shared memory access?
			//break;
		}
	}, "cpu_interface_w").umask16(0xff);

	map(0x0380, 0x0381).lw8(NAME([this](u8 data) { logerror("led_w 0x%02x\n", data); })).umask16(0xff00);
}

void rx2030_state::rx2030_map(address_map &map)
{
	map(0x02000000, 0x02000003).lrw8(
		NAME([this]() { return m_iop_interface; }),
		[this](u8 data)
		{
			switch (data)
			{
			case 0: LOG("cpu interrupt 0 cleared\n"); m_cpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE); break;
			case 1: LOG("cpu interrupt 1 cleared\n"); m_cpu->set_input_line(INPUT_LINE_IRQ1, CLEAR_LINE); break;
			case 2: LOG("cpu interrupt 2 cleared\n"); m_cpu->set_input_line(INPUT_LINE_IRQ2, CLEAR_LINE); break;
			case 3: LOG("cpu interrupt 4 cleared\n"); m_cpu->set_input_line(INPUT_LINE_IRQ4, CLEAR_LINE); break;
				break;

			case 4:
				if (VERBOSE & LOG_IOCB)
				{
					static char const *const iop_commands[] =
					{
						"IOP",     "UART0", "UART1", "NVRAM", "LED",   "CLOCK",  "TOD",   "SCSI0",
						"SCSI1",   "SCSI2", "SCSI3", "SCSI4", "SCSI5", "SCSI6",  "SCSI7", "FLOPPY0",
						"FLOPPY1", "LANCE", "PP",    "KYBD",  "MOUSE", "BUZZER", "UNK22", "UNK23"
					};
					static char const *const iop_lance[] =
					{
						"", "PROBE", "INIT", "STOP", "STRT", "RECV", "XMIT", "XMIT_DONE",
						"STAT", "INIT_DONE", "RESET",  "DBG_ON",  "DBG_OFF", "MISS"
					};

					for (int iocb = 0; iocb < 24; iocb++)
					{
						// check if command semaphore set
						if (m_ram->read(0x1000 + iocb * 16 + 10) || m_ram->read(0x1000 + iocb * 16 + 11))
						{
							u32 const iocb_cmdparam = m_ram->read(0x1000 + iocb * 16 + 0)
								| (m_ram->read(0x1000 + iocb * 16 + 1) << 8)
								| (m_ram->read(0x1000 + iocb * 16 + 2) << 16)
								| (m_ram->read(0x1000 + iocb * 16 + 3) << 24);

							u16 const iop_cmd = m_ram->read(0x1000 + iocb_cmdparam + 2) | (m_ram->read(0x1000 + iocb_cmdparam + 3) << 8);

							switch (iocb)
							{
							case 5: // clock
								LOGMASKED(LOG_IOCB, "iocb %s command 0x%04x (%s)\n",
									iop_commands[iocb], m_ram->read(0x1000 + iocb_cmdparam + 6) | (m_ram->read(0x1000 + iocb_cmdparam + 7) << 8),
									machine().describe_context());
								break;

							case 17: // lance
								LOGMASKED(LOG_IOCB, "iocb %s command %s (%s)\n",
									iop_commands[iocb], iop_lance[iocb_cmdparam],
									machine().describe_context());
								break;

							case 19: // keyboard
								LOGMASKED(LOG_IOCB, "iocb %s command 0x%04x data 0x%02x (%s)\n",
									iop_commands[iocb], iop_cmd,
									m_ram->read(0x1000 + iocb_cmdparam + 7),
									machine().describe_context());
								break;

							default:
								LOGMASKED(LOG_IOCB, "iocb %s command 0x%04x (%s)\n",
									iop_commands[iocb], iop_cmd,
									machine().describe_context());
								break;
							}
						}
					}
				}

				// interrupt the iop
				m_iop_interface &= ~IOP_IACK;
				m_iop_interface |= IOP_IRQ;
				m_iop->set_input_line(INPUT_LINE_IRQ2, ASSERT_LINE);
				break;

			case 6: LOG("led on\n"); break;
			case 7: LOG("led off\n"); break;

			default:
				LOG("iop interface command 0x%02x (%s)\n", data, machine().describe_context());
				break;
			}
		}, "iop_interface_w"
	).umask32(0xff);
}

void rx2030_state::rs2030_map(address_map &map)
{
	rx2030_map(map);

	// video hardware
	map(0x01000000, 0x011fffff).ram().share("vram");
	map(0x01ffff00, 0x01ffffff).m(m_ramdac, FUNC(bt458_device::map)).umask32(0xff);

	//map(0x01ff1000, 0x01ff1001).w() // graphics register?
	//map(0x01ff0080, 0x01ff0081).w() // graphics register?
}

u16 rx2030_state::lance_r(offs_t offset, u16 mem_mask)
{
	u16 const data =
		(m_ram->read(BYTE4_XOR_BE(offset + 1)) << 8) |
		m_ram->read(BYTE4_XOR_BE(offset + 0));

	return data;
}

void rx2030_state::lance_w(offs_t offset, u16 data, u16 mem_mask)
{
	if (ACCESSING_BITS_0_7)
		m_ram->write(BYTE4_XOR_BE(offset + 0), data);

	if (ACCESSING_BITS_8_15)
		m_ram->write(BYTE4_XOR_BE(offset + 1), data >> 8);
}

static void mips_scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
	device.option_add("cdrom", NSCSI_CDROM);
}

static void mips_rs232_devices(device_slot_interface &device)
{
	default_rs232_devices(device);

	device.option_add("mouse", MSYSTEMS_HLE_SERIAL_MOUSE);
}

void rx2030_state::rx2030(machine_config &config)
{
	R2000A(config, m_cpu, 33.333_MHz_XTAL / 2, 32768, 32768);
	m_cpu->set_fpu(mips1_device_base::MIPS_R2010A);
	m_cpu->in_brcond<0>().set([]() { return 1; }); // writeback complete

	V50(config, m_iop, 20_MHz_XTAL);
	m_iop->set_addrmap(AS_PROGRAM, &rx2030_state::iop_program_map);
	m_iop->set_addrmap(AS_IO, &rx2030_state::iop_io_map);
	m_iop->tout2_cb().set(m_buzzer, FUNC(speaker_sound_device::level_w));

	// general dma configuration
	m_iop->out_hreq_cb().set(m_iop, FUNC(v50_device::hack_w));
	m_iop->in_mem16r_cb().set(FUNC(rx2030_state::mmu_r));
	m_iop->out_mem16w_cb().set(FUNC(rx2030_state::mmu_w));

	// dma channel 1: scsi
	m_iop->in_io16r_cb<1>().set(m_scsi, FUNC(aic6250_device::dma16_r));
	m_iop->out_io16w_cb<1>().set(m_scsi, FUNC(aic6250_device::dma16_w));
	m_iop->out_dack_cb<1>().set(m_scsi, FUNC(aic6250_device::back_w));

	RAM(config, m_ram);
	m_ram->set_default_size("16M");
	m_ram->set_extra_options("4M,8M,12M");
	m_ram->set_default_value(0);

	// rtc and nvram
	MC146818(config, m_rtc, 32.768_kHz_XTAL);

	// parallel port
	Z8038(config, m_fio, 0);
	m_fio->out_int_cb<1>().set_inputline(m_iop, INPUT_LINE_IRQ4);

	// keyboard connector
	PC_KBDC(config, m_kbd, pc_at_keyboards, nullptr);
	m_kbd->out_clock_cb().set(m_kbdc, FUNC(at_keyboard_controller_device::kbd_clk_w));
	m_kbd->out_data_cb().set(m_kbdc, FUNC(at_keyboard_controller_device::kbd_data_w));

	// keyboard controller
	AT_KEYBOARD_CONTROLLER(config, m_kbdc, 12_MHz_XTAL);
	//m_kbdc->hot_res().set_inputline(m_maincpu, INPUT_LINE_RESET);
	m_kbdc->kbd_clk().set(m_kbd, FUNC(pc_kbdc_device::clock_write_from_mb));
	m_kbdc->kbd_data().set(m_kbd, FUNC(pc_kbdc_device::data_write_from_mb));
	m_kbdc->set_default_bios_tag("award15");

	SCC85C30(config, m_scc, 1.8432_MHz_XTAL);
	m_scc->configure_channels(m_scc->clock(), m_scc->clock(), m_scc->clock(), m_scc->clock());
	m_scc->out_int_callback().set_inputline(m_iop, INPUT_LINE_IRQ3);

	// scc channel A (tty0)
	RS232_PORT(config, m_tty[0], mips_rs232_devices, nullptr);
	m_tty[0]->cts_handler().set(m_scc, FUNC(z80scc_device::ctsa_w));
	m_tty[0]->dcd_handler().set(m_scc, FUNC(z80scc_device::dcda_w));
	m_tty[0]->rxd_handler().set(m_scc, FUNC(z80scc_device::rxa_w));
	m_scc->out_rtsa_callback().set(m_tty[0], FUNC(rs232_port_device::write_rts));
	m_scc->out_txda_callback().set(m_tty[0], FUNC(rs232_port_device::write_txd));

	// scc channel B (tty1)
	RS232_PORT(config, m_tty[1], mips_rs232_devices, nullptr);
	m_tty[1]->cts_handler().set(m_scc, FUNC(z80scc_device::ctsb_w));
	m_tty[1]->dcd_handler().set(m_scc, FUNC(z80scc_device::dcdb_w));
	m_tty[1]->rxd_handler().set(m_scc, FUNC(z80scc_device::rxb_w));
	m_scc->out_rtsb_callback().set(m_tty[1], FUNC(rs232_port_device::write_rts));
	m_scc->out_txdb_callback().set(m_tty[1], FUNC(rs232_port_device::write_txd));

	// floppy controller and drive
	WD37C65C(config, m_fdc, 16_MHz_XTAL);
	m_fdc->intrq_wr_callback().set_inputline(m_iop, INPUT_LINE_IRQ6);
	//m_fdc->drq_wr_callback().set();
	FLOPPY_CONNECTOR(config, "fdc:0", "35hd", FLOPPY_35_HD, true, floppy_image_device::default_pc_floppy_formats).enable_sound(false);

	// scsi bus and devices
	NSCSI_BUS(config, m_scsibus);
	NSCSI_CONNECTOR(config, "scsi:0", mips_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:1", mips_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", mips_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", mips_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", mips_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", mips_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", mips_scsi_devices, nullptr);

	// scsi host adapter (clock assumed)
	NSCSI_CONNECTOR(config, "scsi:7").option_set("aic6250", AIC6250).clock(10_MHz_XTAL).machine_config(
		[this](device_t *device)
		{
			aic6250_device &adapter = downcast<aic6250_device &>(*device);

			adapter.int_cb().set_inputline(m_iop, INPUT_LINE_IRQ7).invert();
			adapter.breq_cb().set(m_iop, FUNC(v50_device::dreq_w<1>));
		});

	// ethernet
	AM7990(config, m_net);
	m_net->intr_out().set_inputline(m_iop, INPUT_LINE_IRQ5).invert();
	m_net->dma_in().set(FUNC(rx2030_state::lance_r));
	m_net->dma_out().set(FUNC(rx2030_state::lance_w));

	// buzzer
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_buzzer);
	m_buzzer->add_route(ALL_OUTPUTS, "mono", 0.50);
}

void rx2030_state::rc2030(machine_config &config)
{
	rx2030(config);

	m_cpu->set_addrmap(AS_PROGRAM, &rx2030_state::rx2030_map);

	m_tty[1]->set_default_option("terminal");
}

void rx2030_state::rs2030(machine_config &config)
{
	rx2030(config);

	m_cpu->set_addrmap(AS_PROGRAM, &rx2030_state::rs2030_map);

	m_kbd->set_default_option(STR_KBD_MICROSOFT_NATURAL);
	m_tty[0]->set_default_option("mouse");

	// video hardware (1280x1024x8bpp @ 60Hz), 40 parts vram
	u32 const pixclock = 108'189'000;

	// timing from VESA 1280x1024 @ 60Hz
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(pixclock, 1688, 248, 1528, 1066, 38, 1062);
	m_screen->set_screen_update(FUNC(rx2030_state::screen_update));
	m_screen->screen_vblank().set_inputline(m_cpu, INPUT_LINE_IRQ5);

	BT458(config, m_ramdac, pixclock);
}

u32 rx2030_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect)
{
	/*
	 * The graphics board has 1280KiB of video ram fitted, which is organised
	 * such that each 1280 pixel line occupies 2048 bytes of the address space;
	 * the remaining 768 addresses are presumably not mapped to anything.
	 */
	u32 *pixel_pointer = m_vram;

	for (int y = screen.visible_area().min_y; y <= screen.visible_area().max_y; y++)
	{
		for (int x = screen.visible_area().min_x; x <= screen.visible_area().max_x; x += 4)
		{
			u32 const pixel_data = *pixel_pointer++;

			bitmap.pix(y, x + 0) = m_ramdac->pen_color((pixel_data >> 24) & 0xff);
			bitmap.pix(y, x + 1) = m_ramdac->pen_color((pixel_data >> 16) & 0xff);
			bitmap.pix(y, x + 2) = m_ramdac->pen_color((pixel_data >> 8) & 0xff);
			bitmap.pix(y, x + 3) = m_ramdac->pen_color((pixel_data >> 0) & 0xff);
		}

		// compensate by 2048 - 1280 pixels per line
		pixel_pointer += 0xc0;
	}

	return 0;
}

void rx3230_state::rx3230_map(address_map &map)
{
	map(0x00000000, 0x07ffffff).noprw(); // silence ram

	//map(0x10000000, 0x13ffffff); // restricted AT I/O space
	//map(0x14000000, 0x17ffffff); // restricted AT memory space

	map(0x16080004, 0x16080007).nopr(); // silence graphics register

	map(0x18000000, 0x1800003f).m(m_scsi, FUNC(ncr53c94_device::map)).umask32(0xff);
	map(0x19000000, 0x19000003).rw(m_kbdc, FUNC(at_keyboard_controller_device::data_r), FUNC(at_keyboard_controller_device::data_w)).umask32(0xff);
	map(0x19000004, 0x19000007).rw(m_kbdc, FUNC(at_keyboard_controller_device::status_r), FUNC(at_keyboard_controller_device::command_w)).umask32(0xff);
	map(0x19800000, 0x19800003).lr8(NAME([this]() { return m_int_reg; })).umask32(0xff);
	map(0x1a000000, 0x1a000007).rw(m_net, FUNC(am7990_device::regs_r), FUNC(am7990_device::regs_w)).umask32(0xffff);
	map(0x1b000000, 0x1b00001f).rw(m_scc, FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w)).umask32(0xff); // TODO: order?

	map(0x1c000000, 0x1c000fff).m(m_rambo, FUNC(mips_rambo_device::map));

	map(0x1d000000, 0x1d001fff).rw(m_rtc, FUNC(m48t02_device::read), FUNC(m48t02_device::write)).umask32(0xff);
	map(0x1e000000, 0x1e000007).m(m_fdc, FUNC(i82072_device::map)).umask32(0xff);
	//map(0x1e800000, 0x1e800003).umask32(0xff); // fdc tc

	map(0x1fc00000, 0x1fc3ffff).rom().region("rx3230", 0);
	map(0x1ff00000, 0x1ff3ffff).rom().region("rx3230", 0); // mirror
}

void rx3230_state::rs3230_map(address_map &map)
{
	rx3230_map(map);

	map(0x10000000, 0x12ffffff).lrw32(
		NAME([this](offs_t offset)
		{
			u32 const ram_offset = ((offset >> 13) * 0x500) + ((offset & 0x1ff) << 2);

			u32 const data =
				u32(m_vram->read(ram_offset | 0)) << 24 |
				u32(m_vram->read(ram_offset | 1)) << 16 |
				u32(m_vram->read(ram_offset | 2)) << 8 |
				u32(m_vram->read(ram_offset | 3)) << 0;

			return data;
		}),
		NAME([this](offs_t offset, u32 data)
		{
			u32 const ram_offset = ((offset >> 13) * 0x500) + ((offset & 0x1ff) << 2);

			m_vram->write(ram_offset | 0, data >> 24);
			m_vram->write(ram_offset | 1, data >> 16);
			m_vram->write(ram_offset | 2, data >> 8);
			m_vram->write(ram_offset | 3, data >> 0);
		}));

	map(0x14000000, 0x14000003).rw(m_ramdac, FUNC(bt459_device::address_lo_r), FUNC(bt459_device::address_lo_w)).umask32(0xff);
	map(0x14080000, 0x14080003).rw(m_ramdac, FUNC(bt459_device::address_hi_r), FUNC(bt459_device::address_hi_w)).umask32(0xff);
	map(0x14100000, 0x14100003).rw(m_ramdac, FUNC(bt459_device::register_r), FUNC(bt459_device::register_w)).umask32(0xff);
	map(0x14180000, 0x14180003).rw(m_ramdac, FUNC(bt459_device::palette_r), FUNC(bt459_device::palette_w)).umask32(0xff);

	map(0x16080004, 0x16080007).lr8(NAME([this] ()
	{
		u8 const data = (m_screen->vblank() ? GFX_V_BLANK : 0) | (m_screen->hblank() ? GFX_H_BLANK : 0);

		return data;
	})).umask32(0xff); // also write 0

	//map(0x16000004, 0x16000007).w(); // write 0x00000001
	//map(0x16100000, 0x16100003).w(); // write 0xffffffff
}

void rx3230_state::machine_start()
{
	save_item(NAME(m_int_reg));
	save_item(NAME(m_int0_state));
	save_item(NAME(m_int1_state));
}

void rx3230_state::machine_reset()
{
	m_int_reg = INT_CLR;
	m_int0_state = 1;
	m_int1_state = 1;
}

void rx3230_state::rx3230_init()
{
	// map the configured ram
	m_cpu->space(0).install_ram(0x00000000, m_ram->mask(), m_ram->pointer());

	/*
	 * HACK: the prom bootp code broadcasts to the network address (i.e. the
	 * host portion is "all zeroes"), instead of to the standard "all ones".
	 * This makes it very difficult to receive the bootp request in a host OS,
	 * so this patch changes the code to broadcast to the standard broadcast
	 * address instead.
	 *
	 * 0xbfc1f1b0: addu  r6,0,0
	 *             jal   $bfc0be10   # set host portion from r6
	 *
	 * This patch changes the first instruction to one which loads r6 with
	 * 0xffffffff, which is then or'd into the host part of the address, i.e.:
	 *
	 *             addiu r6,0,-$1
	 */
	m_rom[0x1f1b0 >> 2] = 0x2406ffff;
}

void rx3230_state::rx3230(machine_config &config)
{
	R3000A(config, m_cpu, 50_MHz_XTAL / 2, 32768, 32768);
	m_cpu->set_addrmap(AS_PROGRAM, &rx3230_state::rx3230_map);
	m_cpu->set_fpu(mips1_device_base::MIPS_R3010A);
	m_cpu->in_brcond<0>().set([]() { return 1; }); // writeback complete

	// 32 SIMM slots, 8-128MB memory, banks of 8 1MB or 4MB SIMMs
	RAM(config, m_ram);
	m_ram->set_default_size("32M");
	m_ram->set_extra_options("16M,64M,128M");
	m_ram->set_default_value(0);

	MIPS_RAMBO(config, m_rambo, 25_MHz_XTAL / 4);
	m_rambo->timer_out().set_inputline(m_cpu, INPUT_LINE_IRQ2);
	m_rambo->irq_out().set_inputline(m_cpu, INPUT_LINE_IRQ1);
	m_rambo->parity_out().set_inputline(m_cpu, INPUT_LINE_IRQ5);
	//m_rambo->buzzer_out().set(m_buzzer, FUNC(speaker_sound_device::level_w));
	m_rambo->set_ram(m_ram);
	m_rambo->dma_r<0>().set("scsi:7:ncr53c94", FUNC(ncr53c94_device::dma16_swap_r));
	m_rambo->dma_w<0>().set("scsi:7:ncr53c94", FUNC(ncr53c94_device::dma16_swap_w));

	// scsi bus and devices
	NSCSI_BUS(config, m_scsibus);
	NSCSI_CONNECTOR(config, "scsi:0", mips_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:1", mips_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", mips_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", mips_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", mips_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", mips_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", mips_scsi_devices, nullptr);

	// scsi host adapter
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr53c94", NCR53C94).clock(24_MHz_XTAL).machine_config(
		[this](device_t *device)
		{
			ncr53c94_device &adapter = downcast<ncr53c94_device &>(*device);

			adapter.set_busmd(ncr53c94_device::busmd_t::BUSMD_1);
			adapter.irq_handler_cb().set(*this, FUNC(rx3230_state::irq_w<INT_SCSI>)).invert();
			adapter.drq_handler_cb().set(m_rambo, FUNC(mips_rambo_device::drq_w<0>));
		});

	// ethernet
	AM7990(config, m_net);
	m_net->intr_out().set(FUNC(rx3230_state::irq_w<INT_NET>));
	m_net->dma_in().set(FUNC(rx3230_state::lance_r));
	m_net->dma_out().set(FUNC(rx3230_state::lance_w));

	SCC85C30(config, m_scc, 9.8304_MHz_XTAL); // TODO: clock working but unverified
	m_scc->out_int_callback().set(FUNC(rx3230_state::irq_w<INT_SCC>)).invert();

	// scc channel A (tty0)
	RS232_PORT(config, m_tty[0], default_rs232_devices, nullptr);
	m_tty[0]->cts_handler().set(m_scc, FUNC(z80scc_device::ctsa_w));
	m_tty[0]->dcd_handler().set(m_scc, FUNC(z80scc_device::dcda_w));
	m_tty[0]->rxd_handler().set(m_scc, FUNC(z80scc_device::rxa_w));
	m_scc->out_rtsa_callback().set(m_tty[0], FUNC(rs232_port_device::write_rts));
	m_scc->out_txda_callback().set(m_tty[0], FUNC(rs232_port_device::write_txd));

	// scc channel B (tty1)
	RS232_PORT(config, m_tty[1], default_rs232_devices, nullptr);
	m_tty[1]->cts_handler().set(m_scc, FUNC(z80scc_device::ctsb_w));
	m_tty[1]->dcd_handler().set(m_scc, FUNC(z80scc_device::dcdb_w));
	m_tty[1]->rxd_handler().set(m_scc, FUNC(z80scc_device::rxb_w));
	m_scc->out_rtsb_callback().set(m_tty[1], FUNC(rs232_port_device::write_rts));
	m_scc->out_txdb_callback().set(m_tty[1], FUNC(rs232_port_device::write_txd));

	M48T02(config, m_rtc);

	// floppy controller and drive
	I82072(config, m_fdc, 16_MHz_XTAL);
	m_fdc->intrq_wr_callback().set_inputline(m_cpu, INPUT_LINE_IRQ4);
	//m_fdc->drq_wr_callback().set();
	FLOPPY_CONNECTOR(config, "fdc:0", "35hd", FLOPPY_35_HD, true, floppy_image_device::default_pc_floppy_formats).enable_sound(false);

	// keyboard connector
	PC_KBDC(config, m_kbd, pc_at_keyboards, nullptr);
	m_kbd->out_clock_cb().set(m_kbdc, FUNC(at_keyboard_controller_device::kbd_clk_w));
	m_kbd->out_data_cb().set(m_kbdc, FUNC(at_keyboard_controller_device::kbd_data_w));

	// keyboard controller
	AT_KEYBOARD_CONTROLLER(config, m_kbdc, 12_MHz_XTAL); // TODO: confirm
	m_kbdc->kbd_clk().set(m_kbd, FUNC(pc_kbdc_device::clock_write_from_mb));
	m_kbdc->kbd_data().set(m_kbd, FUNC(pc_kbdc_device::data_write_from_mb));
	//m_kbdc->kbd_irq().set(FUNC(rx3230_state::irq_w<INT_KBD>));

	// buzzer
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_buzzer);
	m_buzzer->add_route(ALL_OUTPUTS, "mono", 0.50);

	// motherboard monochrome video (1152x900 @ 72Hz)
	u32 const pixclock = 74'649'600;

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(pixclock, 1152, 0, 1152, 900, 0, 900);
	m_screen->set_screen_update(m_rambo.finder_tag(), FUNC(mips_rambo_device::screen_update));

	// TODO: slot - motherboard can accept either the colour graphics board, or
	// a riser which presents an ISA 16-bit slot.
}

void rx3230_state::rc3230(machine_config &config)
{
	rx3230(config);

	m_cpu->set_addrmap(AS_PROGRAM, &rx3230_state::rx3230_map);

	m_kbd->set_default_option(STR_KBD_MICROSOFT_NATURAL);
	//m_tty[1]->set_default_option("terminal");
}

void rx3230_state::rs3230(machine_config &config)
{
	rx3230(config);

	m_kbd->set_default_option(STR_KBD_MICROSOFT_NATURAL);

	// FIXME: colour video board disabled for now
	if (false)
	{
		m_cpu->set_addrmap(AS_PROGRAM, &rx3230_state::rs3230_map);

		// video hardware (1280x1024x8bpp @ 60Hz), 16 parts vram
		u32 const pixclock = 108'180'000;

		// timing from VESA 1280x1024 @ 60Hz
		m_screen->set_raw(pixclock, 1688, 248, 1528, 1066, 38, 1062);
		m_screen->set_screen_update(FUNC(rx3230_state::screen_update));
		//m_screen->screen_vblank().set_inputline(m_cpu, INPUT_LINE_IRQ5);

		BT459(config, m_ramdac, pixclock);

		RAM(config, m_vram);
		m_vram->set_default_size("2M");
		m_vram->set_default_value(0);
	}
}

template <u8 Source> void rx3230_state::irq_w(int state)
{
	if (state)
		m_int_reg |= Source;
	else
		m_int_reg &= ~Source;

	switch (Source)
	{
	case INT_SLOT:
	case INT_KBD:
	case INT_SCC:
	case INT_NET:
		if (m_int0_state != state)
		{
			m_int0_state = state;
			m_cpu->set_input_line(INPUT_LINE_IRQ0, !state);
		}
		break;

	case INT_SCSI:
		if (m_int1_state != state)
		{
			m_int1_state = state;
			m_cpu->set_input_line(INPUT_LINE_IRQ1, !state);
		}
		break;
	}
}

u32 rx3230_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect)
{
	m_ramdac->screen_update(screen, bitmap, cliprect, m_vram->pointer());

	return 0;
}

u16 rx3230_state::lance_r(offs_t offset, u16 mem_mask)
{
	u16 const data =
		(m_ram->read(BYTE4_XOR_BE(offset + 0)) << 8) |
		m_ram->read(BYTE4_XOR_BE(offset + 1));

	return data;
}

void rx3230_state::lance_w(offs_t offset, u16 data, u16 mem_mask)
{
	if (ACCESSING_BITS_0_7)
		m_ram->write(BYTE4_XOR_BE(offset + 1), data);

	if (ACCESSING_BITS_8_15)
		m_ram->write(BYTE4_XOR_BE(offset + 0), data >> 8);
}

ROM_START(rx2030)
	ROM_REGION16_LE(0x40000, "rx2030", 0)
	ROM_SYSTEM_BIOS(0, "v4.32", "Rx2030 v4.32, Jan 1991")
	ROMX_LOAD("50-00121__005.u139", 0x00000, 0x10000, CRC(b2f42665) SHA1(81c83aa6b8865338fda5c03733ede91749997648), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("50-00120__005.u140", 0x00001, 0x10000, CRC(0ffa485e) SHA1(7cdfb81d1a547c5ccc88e1e0ef73d447cd03e9e2), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("50-00119__005.u141", 0x20001, 0x10000, CRC(68fb219d) SHA1(7161ad8e5e0207d8730e09753ca74bfec0e782f8), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("50-00118__005.u142", 0x20000, 0x10000, CRC(b59426d3) SHA1(3fc09b0368f731c2c07cf29b481f30c01e330929), ROM_BIOS(0) | ROM_SKIP(1))

	ROM_SYSTEM_BIOS(1, "v4.30", "Rx2030 v4.30, Jul 1989")
	ROMX_LOAD("50-00121__003.u139", 0x00000, 0x10000, CRC(ebc580ac) SHA1(63f9a1d344d53f32ee769f5137820faf64ffa291), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("50-00120__003.u140", 0x00001, 0x10000, CRC(e1991721) SHA1(028d33be271c95f198473b650f7800f9ca4a60b2), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("50-00119__003.u141", 0x20001, 0x10000, CRC(c8469906) SHA1(69bbf4b5c415b2e2156a4467bf9cb30e79f586ef), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("50-00118__003.u142", 0x20000, 0x10000, CRC(18cc001a) SHA1(198023e92e1e3ba2fc8637f5dd6f370e7e023fdd), ROM_BIOS(1) | ROM_SKIP(1))

	/*
	 * The following isn't a real dump, but a hand-made nvram image that allows
	 * entry to the boot monitor. Variables can be adjusted via the monitor,
	 * and are laid out as follows:
	 *
	 *   Offset  Length  Variable
	 *    0x0e      4    netaddr
	 *    0x12      1    lbaud
	 *    0x13      1    rbaud
	 *    0x14     20    bootfile
	 *    0x28      1    bootmode
	 *    0x29      1    console
	 *    0x2a      1    ponmask? or something similar
	 *    0x2b      3    unused?
	 *    0x2e      4    resetepc
	 *    0x32      4    resetra
	 *    0x36      1    keyswtch
	 *    0x37      1    flag
	 *    0x38      8    unused?
	 *
	 */
	ROM_REGION(0x40, "rtc", 0)
	ROM_LOAD("ds1287.bin", 0x00, 0x40, CRC(28369bf3) SHA1(64f24e1d8fb7103ab0bd3023c66490447bdcbf89))
ROM_END
#define rom_rc2030 rom_rx2030
#define rom_rs2030 rom_rx2030

ROM_START(rx3230)
	ROM_REGION32_BE(0x40000, "rx3230", 0)
	ROM_SYSTEM_BIOS(0, "v5.40", "Rx3230 v5.40, Jun 1990")
	ROMX_LOAD("50-314-003__3230_left.bin",  0x00002, 0x20000, CRC(77ce42c9) SHA1(b2d5e5a386ed0ff840646647ba90b3c36732a7fe), ROM_BIOS(0) | ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2))
	ROMX_LOAD("50-314-003__3230_right.bin", 0x00000, 0x20000, CRC(5bc1ce2f) SHA1(38661234bf40b76395393459de49e48619b2b454), ROM_BIOS(0) | ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2))

	ROM_SYSTEM_BIOS(1, "v5.42", "Rx3230 v5.42, Mar 1991")
	ROMX_LOAD("unknown.bin", 0x00002, 0x20000, NO_DUMP, ROM_BIOS(1) | ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2))
	ROMX_LOAD("unknown.bin", 0x00000, 0x20000, NO_DUMP, ROM_BIOS(1) | ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2))

	//ROM_REGION(0x800, "i8042", 0)
	//ROM_LOAD("unknown.bin", 0x000, 0x800, NO_DUMP)

	//ROM_REGION(0x800, "rtc", 0)
	//ROM_LOAD("m48t02.bin", 0x000, 0x800, NO_DUMP)
ROM_END
#define rom_rc3230 rom_rx3230
#define rom_rs3230 rom_rx3230

}

/*   YEAR   NAME       PARENT  COMPAT  MACHINE    INPUT  CLASS         INIT         COMPANY  FULLNAME       FLAGS */
COMP(1989,  rc2030,    0,      0,      rc2030,    0,     rx2030_state, rx2030_init, "MIPS",  "RC2030",      0)
COMP(1989,  rs2030,    0,      0,      rs2030,    0,     rx2030_state, rx2030_init, "MIPS",  "RS2030",      0)
COMP(1990,  rc3230,    0,      0,      rc3230,    0,     rx3230_state, rx3230_init, "MIPS",  "RC3230",      MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
COMP(1990,  rs3230,    0,      0,      rs3230,    0,     rx3230_state, rx3230_init, "MIPS",  "Magnum 3000", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



mirage.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
/*******************************************************************************

Excalibur Mirage (model 702E)

It's Excalibur's first chess computer, and also Ron Nelson's official return to
chess programming. The x/y motorized magnet is similar to the one used in
Fidelity Phantom (and Milton Bradley Grand·Master before that).

Before moving a piece, wait until the computer is done with its own move. After
capturing a piece, select the captured piece from the MAME sensorboard spawn
block and place it anywhere on a free spot at the designated box at the edge
of the chessboard.

Hardware notes:
- PCB label: EXCALIBUR ELECTRONICS, INC. 6/5/96, MIRAGE, 00-55052-000
- Hitachi H8/3256 MCU (only 32KB out of 48KB internal ROM used), either Mask ROM
  or PROM, 20MHz XTAL
- 2*L293DNE motor drivers, 2 DC motors (like a plotter), electromagnet under the
  chessboard for automatically moving the pieces
- LCD with 5 7segs and custom segments
- piezo, button sensors chessboard

There's also a version with a fake wood housing instead of black plastic, it's
most likely the same hardware.

TODO:
- like fphantom, sensorboard undo buffer fills up pretty quickly
- dump/add PROM version, maybe they improved the motor drift issue?
- it does a cold boot at every reset, so nvram won't work properly unless MAME
  adds some kind of auxillary autosave state feature at power-off

BTANB:
- Motors gradually drift, causing it to place/pick up pieces off-center. It
  recalibrates itself once in a while but it's not enough. MAME's sensorboard
  device can't deal with it, so there's a workaround (see realign_magnet_pos).
  Ron Nelson blamed it on the hardware engineer, but it's a software fault.

*******************************************************************************/

#include "emu.h"

#include "cpu/h8/h8325.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "excal_mirage.lh"


namespace {

class mirage_state : public driver_device
{
public:
	mirage_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_lcd_pwm(*this, "lcd_pwm"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_out_lcd(*this, "s%u.%u", 0U, 0U),
		m_piece_hand(*this, "cpu_hand"),
		m_out_motor(*this, "motor%u", 0U),
		m_out_pos(*this, "pos_%c", unsigned('x'))
	{ }

	void mirage(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(on_button);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<h83256_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_lcd_pwm;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<3> m_inputs;
	output_finder<2, 24> m_out_lcd;
	output_finder<> m_piece_hand;
	output_finder<5> m_out_motor;
	output_finder<2> m_out_pos;

	u16 m_inp_mux = 0;
	u32 m_lcd_segs = 0;
	u8 m_lcd_com = 0;

	u8 m_magnet = 0;
	u8 m_pieces_map[0x40][0x40] = { };

	u8 m_motor_control[2] = { };
	u32 m_motor_max[2] = { };
	u32 m_motor_pos[2] = { };
	s32 m_motor_drift[2] = { };
	u8 m_motor_quad[2] = { };

	attotime m_motor_period;
	attotime m_motor_remain[2];
	emu_timer *m_motor_timer[2];

	void init_board(u8 data);
	void clear_board(u8 data);
	void init_motors();

	void get_scaled_pos(double *x, double *y);
	void output_magnet_pos();
	void realign_magnet_pos();
	void update_piece(u8 magnet);

	TIMER_CALLBACK_MEMBER(motor_count);
	void motor_control(int m, u8 control);

	// I/O handlers
	void lcd_pwm_w(offs_t offset, u8 data);
	void update_lcd();
	template <int N> void lcd_segs_w(u8 data);

	u8 p4_r();
	u8 p5_r();
	void p5_w(u8 data);
	u8 p6_r();
	void p6_w(u8 data);
	u8 p7_r();
	void p7_w(offs_t offset, u8 data, u8 mem_mask);
};



/*******************************************************************************
    Initialization
*******************************************************************************/

void mirage_state::machine_start()
{
	init_motors();

	// resolve outputs
	m_out_lcd.resolve();
	m_piece_hand.resolve();
	m_out_motor.resolve();
	m_out_pos.resolve();

	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_lcd_segs));
	save_item(NAME(m_lcd_com));

	save_item(NAME(m_magnet));
	save_item(NAME(m_pieces_map));
	save_item(NAME(m_motor_control));
	save_item(NAME(m_motor_pos));
	save_item(NAME(m_motor_drift));
	save_item(NAME(m_motor_quad));
	save_item(NAME(m_motor_remain));
}

void mirage_state::machine_reset()
{
	memset(m_motor_drift, 0, sizeof(m_motor_drift));
	output_magnet_pos();
}

void mirage_state::init_board(u8 data)
{
	m_board->preset_chess(data);

	// reposition pieces if board will be rotated
	if (data & 2)
	{
		for (int y = 0; y < 8; y++)
			for (int x = 7; x >= 0; x--)
			{
				m_board->write_piece(x + 4, y, m_board->read_piece(x, y));
				m_board->write_piece(x, y, 0);
			}
	}
}

void mirage_state::clear_board(u8 data)
{
	memset(m_pieces_map, 0, sizeof(m_pieces_map));
	m_piece_hand = 0;
	m_board->clear_board(data);
}



/*******************************************************************************
    Motor Sim
*******************************************************************************/

void mirage_state::init_motors()
{
	m_motor_period = attotime::from_usec(800);

	for (int i = 0; i < 2; i++)
	{
		m_motor_timer[i] = timer_alloc(FUNC(mirage_state::motor_count), this);
		m_motor_remain[i] = m_motor_period / 2;
	}

	m_motor_max[1] = 1500 - 1;
	m_motor_pos[1] = 1480;

	m_motor_max[0] = 2020 - 1;
	m_motor_pos[0] = 24;
}

void mirage_state::get_scaled_pos(double *x, double *y)
{
	// 176 counts per square
	*x = std::clamp(double(int(m_motor_pos[0]) - 20) / (176.0 / 4.0) + 2.0, 0.0, 48.0);
	*y = std::clamp(double(int(m_motor_pos[1]) - 160) / (176.0 / 4.0) + 2.0, 0.0, 32.0);
}

void mirage_state::output_magnet_pos()
{
	double x, y;
	get_scaled_pos(&x, &y);

	// put active state on x bit 11
	const int active = m_magnet ? 0x800 : 0;
	m_out_pos[0] = int(x * 25.0 + 0.5) | active;
	m_out_pos[1] = int(y * 25.0 + 0.5);
}

void mirage_state::realign_magnet_pos()
{
	// compensate for gradual drift, see BTANB
	for (int i = 0; i < 2; )
	{
		double pos[2];
		get_scaled_pos(&pos[0], &pos[1]);

		const double limit = 1.0 / 11.0; // 4 counts
		int inc = 0;

		if ((round(pos[i]) - pos[i]) > limit && m_motor_pos[i] < m_motor_max[i] - 4)
			inc = 1;
		else if ((round(pos[i]) - pos[i]) < -limit && m_motor_pos[i] > 4)
			inc = -1;
		else
			i++;

		if (inc != 0)
		{
			m_motor_pos[i] += inc * 4;
			m_motor_drift[i] -= inc;

			logerror("motor %c drift error (%d total)\n", 'X' + i, m_motor_drift[i]);
		}
	}
}

void mirage_state::update_piece(u8 magnet)
{
	if (magnet == m_magnet)
		return;

	realign_magnet_pos();

	m_magnet = magnet;
	output_magnet_pos();

	double dx, dy;
	get_scaled_pos(&dx, &dy);

	int mx = dx + 0.5;
	int my = dy + 0.5;

	// convert motors position into board coordinates
	int x = mx / 4 - 2;
	int y = 7 - (my / 4);

	if (x < 0)
		x += 12;

	const bool valid_pos = (mx & 3) == 2 && (my & 3) == 2;

	// sensorboard handling is almost the same as fidelity/phantom.cpp
	if (magnet)
	{
		if (valid_pos)
		{
			// pick up piece, unless it was picked up by the user
			const int pos = (y << 4 & 0xf0) | (x & 0x0f);
			if (pos != m_board->get_handpos())
			{
				m_piece_hand = m_board->read_piece(x, y);

				if (m_piece_hand != 0)
				{
					m_board->write_piece(x, y, 0);
					m_board->refresh();
				}
			}
		}
		else
		{
			// pick up piece from internal pieces map
			m_piece_hand = m_pieces_map[my][mx];
			m_pieces_map[my][mx] = 0;
		}
	}
	else if (m_piece_hand != 0)
	{
		if (valid_pos)
		{
			// collision with piece on board (user interference)
			if (m_board->read_piece(x, y) != 0)
				popmessage("Collision at %c%d!", x + 'A', y + 1);
			else
			{
				m_board->write_piece(x, y, m_piece_hand);
				m_board->refresh();
			}
		}
		else
		{
			// collision with internal pieces map (shouldn't happen)
			if (m_pieces_map[my][mx] != 0)
				popmessage("Internal collision!");
			else
				m_pieces_map[my][mx] = m_piece_hand;
		}

		m_piece_hand = 0;
	}
}

TIMER_CALLBACK_MEMBER(mirage_state::motor_count)
{
	const int m = param ? 1 : 0;
	assert(m_motor_control[m] & 2);

	// 1 quarter rotation per period
	int inc = 0;
	if (m_motor_control[m] & 1)
	{
		if (m_motor_pos[m] < m_motor_max[m])
			inc = 1;
	}
	else if (m_motor_pos[m] > 0)
		inc = -1;

	m_motor_remain[m] = m_motor_period;

	if (inc == 0)
		return;

	m_motor_pos[m] += inc;
	m_motor_timer[m]->adjust(m_motor_period, m);

	output_magnet_pos();

	// update quadrature encoder
	static const u8 lut_quad[4] = { 0,1,3,2 };
	m_motor_quad[m] = lut_quad[m_motor_pos[m] & 3];

	m_maincpu->set_input_line(INPUT_LINE_IRQ1 + m, (m_motor_quad[m] & 1) ? ASSERT_LINE : CLEAR_LINE);
}

void mirage_state::motor_control(int m, u8 control)
{
	if (control == m_motor_control[m])
		return;

	// remember remaining time
	if (m_motor_control[m] & 2 && !m_motor_timer[m]->remaining().is_never())
	{
		m_motor_remain[m] = m_motor_timer[m]->remaining();
		m_motor_timer[m]->adjust(attotime::never);
	}

	// invert remaining time if direction flipped
	if ((m_motor_control[m] ^ control) & 1)
		m_motor_remain[m] = m_motor_period - m_motor_remain[m];

	m_motor_control[m] = control;

	// (re)start the timer
	if (control & 2)
		m_motor_timer[m]->adjust(m_motor_remain[m], m);
}



/*******************************************************************************
    I/O
*******************************************************************************/

// LCD

void mirage_state::lcd_pwm_w(offs_t offset, u8 data)
{
	m_out_lcd[offset & 0x3f][offset >> 6] = data;
}

void mirage_state::update_lcd()
{
	u32 lcd_segs = (m_lcd_segs & 0xfff000) | bitswap<12>(m_lcd_segs,7,6,5,4,3,2,1,0,8,9,10,11);

	for (int i = 0; i < 2; i++)
	{
		// LCD common is 0/1/Hi-Z
		const u32 data = BIT(m_lcd_com, i + 2) ? (BIT(m_lcd_com, i) ? ~lcd_segs : lcd_segs) : 0;
		m_lcd_pwm->write_row(i, data);
	}
}

template <int N>
void mirage_state::lcd_segs_w(u8 data)
{
	// P1x, P2x, P3x: LCD segments
	const u8 shift = 8 * N;
	m_lcd_segs = (m_lcd_segs & ~(0xff << shift)) | (data << shift);
	update_lcd();

	// P1x,P20-P23: input mux (chessboard)
	m_inp_mux = (m_inp_mux & 0x3000) | bitswap<12>(~m_lcd_segs,18,19,8,9,10,11,12,13,14,15,16,17);
}


// misc

INPUT_CHANGED_MEMBER(mirage_state::on_button)
{
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, newval ? ASSERT_LINE : CLEAR_LINE);
}

u8 mirage_state::p4_r()
{
	// P40-P47: multiplexed inputs
	u8 data = 0;

	// read buttons
	for (int i = 0; i < 2; i++)
		if (BIT(m_inp_mux, i + 12))
			data |= m_inputs[i]->read();

	// read chessboard
	for (int i = 0; i < 12; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_file(i);

	return bitswap<8>(~data,7,6,5,4,0,1,2,3);
}

u8 mirage_state::p5_r()
{
	// P55: motor Y quadrature B
	return ~(BIT(m_motor_quad[1], 1) << 5);
}

void mirage_state::p5_w(u8 data)
{
	// P52: speaker out
	m_dac->write(BIT(~data, 2));

	// P53: motor Y direction
	// P54: motor Y on
	motor_control(1, data >> 3 & 3);

	for (int i = 0; i < 2; i++)
		m_out_motor[i] = BIT(data, i + 3);
}

u8 mirage_state::p6_r()
{
	// P63: battery status
	// P64: on/off button (IRQ0)
	u8 data = m_inputs[2]->read() << 3 & 0x18;

	// P65: motor X quadrature A (IRQ1)
	// P66: motor Y quadrature A (IRQ2)
	data |= m_motor_quad[0] << 5 & 0x20;
	data |= m_motor_quad[1] << 6 & 0x40;

	return ~data;
}

void mirage_state::p6_w(u8 data)
{
	// P60,P61: input mux (buttons)
	m_inp_mux = (m_inp_mux & 0xfff) | (~data << 12 & 0x3000);
}

u8 mirage_state::p7_r()
{
	// P73: motor X quadrature B
	return ~(BIT(m_motor_quad[0], 1) << 3);
}

void mirage_state::p7_w(offs_t offset, u8 data, u8 mem_mask)
{
	// P70,P71: LCD common
	m_lcd_com = (mem_mask << 2 & 0xc) | (data & 3);
	update_lcd();

	// P74: motor X direction
	// P75: motor X on
	motor_control(0, data >> 4 & 3);

	// P76: electromagnet
	update_piece(BIT(data, 6));

	for (int i = 0; i < 3; i++)
		m_out_motor[i + 2] = BIT(data, i + 4);

	// P77: motor board power?
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( mirage )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Hint / Bishop")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Takeback / Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Move / Pawn")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Clear")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Level / Rook")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Verify / Queen")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Setup / King")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Option")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Replay")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Auto / Stop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Multi-Move / Right")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Black / White / Left")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Mode")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.2")
	PORT_CONFNAME( 0x01, 0x00, "Battery Status" )
	PORT_CONFSETTING(    0x01, "Low" )
	PORT_CONFSETTING(    0x00, DEF_STR( Normal ) )
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mirage_state::on_button), 0) PORT_NAME("On / Off")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void mirage_state::mirage(machine_config &config)
{
	// basic machine hardware
	H83256(config, m_maincpu, 20_MHz_XTAL);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(h83256_device::nvram_set_battery));
	m_maincpu->standby_cb().append([this](int state) { if (state) m_lcd_pwm->clear(); });
	m_maincpu->write_port1().set(FUNC(mirage_state::lcd_segs_w<1>));
	m_maincpu->write_port2().set(FUNC(mirage_state::lcd_segs_w<2>));
	m_maincpu->write_port3().set(FUNC(mirage_state::lcd_segs_w<0>));
	m_maincpu->read_port4().set(FUNC(mirage_state::p4_r));
	m_maincpu->read_port5().set(FUNC(mirage_state::p5_r));
	m_maincpu->write_port5().set(FUNC(mirage_state::p5_w));
	m_maincpu->read_port6().set(FUNC(mirage_state::p6_r));
	m_maincpu->write_port6().set(FUNC(mirage_state::p6_w));
	m_maincpu->read_port7().set(FUNC(mirage_state::p7_r));
	m_maincpu->write_port7().set(FUNC(mirage_state::p7_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->set_size(8+4, 8);
	m_board->clear_cb().set(FUNC(mirage_state::clear_board));
	m_board->init_cb().set(FUNC(mirage_state::init_board));
	m_board->set_delay(attotime::from_msec(150));
	//m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_lcd_pwm).set_size(2, 24);
	m_lcd_pwm->output_x().set(FUNC(mirage_state::lcd_pwm_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/5, 697/5);
	screen.set_visarea_full();

	config.set_default_layout(layout_excal_mirage);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( emirage )
	ROM_REGION( 0xc000, "maincpu", 0 )
	ROM_LOAD("1996_7012_excalibur_hd6433256a33p.ic1", 0x0000, 0xc000, CRC(41eed8ea) SHA1(8b5370814d2bfc2d5fcb4ee86c30d676517bcd3a) )

	ROM_REGION( 109652, "screen", 0 )
	ROM_LOAD("regency.svg", 0, 109652, CRC(6840c49e) SHA1(a9c91143c5bea5ab41fe323e719da4a46ab9d631) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1996, emirage, 0,      0,      mirage,  mirage, mirage_state, empty_init, "Excalibur Electronics", "Mirage (Excalibur)", MACHINE_SUPPORTS_SAVE | MACHINE_MECHANICAL | MACHINE_IMPERFECT_CONTROLS )



mits680b.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************

MITS Altair 680b

2009-12-03 Skeleton driver.
2011-06-08 Connected to a terminal

Monitor Commands:
J
L switch to terminal mode
M
N modify memory in a limited way
P this does a rti and causes a momentary crash. Weird.


ToDo:


****************************************************************************/

#include "emu.h"
#include "cpu/m6800/m6800.h"
#include "machine/6850acia.h"
#include "bus/rs232/rs232.h"
#include "machine/f4702.h"

namespace {

class mits680b_state : public driver_device
{
public:
	mits680b_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void mits680b(machine_config &config);

private:
	uint8_t status_check_r();

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};

uint8_t mits680b_state::status_check_r()
{
	return 0; // crashes at start if bit 7 high
}


void mits680b_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x03ff).ram(); // 1024 bytes RAM
	map(0xf000, 0xf001).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xf002, 0xf002).r(FUNC(mits680b_state::status_check_r));
	map(0xff00, 0xffff).rom().region("roms", 0);
}

/* Input ports */
static INPUT_PORTS_START( mits680b )
	PORT_START("BAUD")
	PORT_DIPNAME(0xf, 0x8, "Baud Rate") PORT_DIPLOCATION("0-3:1,2,3,4")
	PORT_DIPSETTING(0x2, "50")
	PORT_DIPSETTING(0x3, "75")
	PORT_DIPSETTING(0xf, "110")
	PORT_DIPSETTING(0x4, "134.5")
	PORT_DIPSETTING(0xe, "150")
	PORT_DIPSETTING(0x5, "200")
	PORT_DIPSETTING(0xd, "300")
	PORT_DIPSETTING(0x6, "600")
	PORT_DIPSETTING(0xb, "1200")
	PORT_DIPSETTING(0xa, "1800")
	PORT_DIPSETTING(0x7, "2400")
	PORT_DIPSETTING(0x9, "4800")
	PORT_DIPSETTING(0x8, "9600")
INPUT_PORTS_END


void mits680b_state::mits680b(machine_config &config)
{
	/* basic machine hardware */
	M6800(config, m_maincpu, 2_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &mits680b_state::mem_map);

	f4702_device &brg(F4702(config, "brg", 2.4576_MHz_XTAL));
	brg.s_callback().set_ioport("BAUD");
	brg.z_callback().set("acia", FUNC(acia6850_device::write_txc));
	brg.z_callback().append("acia", FUNC(acia6850_device::write_rxc));

	acia6850_device &acia(ACIA6850(config, "acia"));
	acia.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	acia.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("acia", FUNC(acia6850_device::write_rxd));
	rs232.cts_handler().set("acia", FUNC(acia6850_device::write_cts));
}

/* ROM definition */
ROM_START( mits680b )
	ROM_REGION( 0x100, "roms", ROMREGION_ERASEFF )
	ROM_LOAD( "mits680b.bin", 0x0000, 0x0100, CRC(397e717f) SHA1(257d3eb1343b8611dc05455aeed33615d581f29c))
ROM_END

} // Anonymous namespace

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY  FULLNAME       FLAGS
COMP( 1976, mits680b, 0,      0,      mits680b, mits680b, mits680b_state, empty_init, "MITS",  "Altair 680b", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



miuchiz.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:O. Galibert, Jonathan Gevaryahu
// thanks-to: ChrisMiuchiz
/*
    Driver for the Miuchiz handhelds
    CPU: ST2205U;
        XTAL: Y1 16MHz
        XTAL: Y2 32.768KHz
    LCDC: ST7626 (https://www.crystalfontz.com/controllers/Sitronix/ST7626/)
        the ST7626 is embedded into a epoxy part just below the screen glass with the flex cable attached to it
        it has internal 98x68x16bit ram
*/

/* Core includes */
#include "emu.h"
#include "cpu/m6502/st2205u.h"
#include "video/st7626.h"
#include "screen.h"

// defines and logging
#define VERBOSE (LOG_GENERAL)
//#define LOG_OUTPUT_FUNC printf
#include "logmacro.h"

namespace {

// class definition
class miuchiz_state : public driver_device
{
public:
	miuchiz_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "lcdc")
		, m_screen(*this, "screen")
	{ }

	void miuchiz(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<st2205u_device> m_maincpu;
	required_device<st7626_device> m_lcdc;
	required_device<screen_device> m_screen;
	[[maybe_unused]] u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};


// address maps
void miuchiz_state::mem_map(address_map &map)
{
	map(0x0000000, 0x0003fff).rom().region("otp", 0);
	map(0x0600000, 0x0600001).m(m_lcdc, FUNC(st7626_device::map8));
	map(0x0800000, 0x09fffff).rom().region("flash", 0);
}

// flash map?
//  map(0x01000000, 0x011fffff).rom().region("flash", 0);


// input ports
static INPUT_PORTS_START( miuchiz )
	PORT_START("INPUTS")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_NAME("Up")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_NAME("Down")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_NAME("Left")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_NAME("Right")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_START1 ) PORT_NAME("Power")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_START2 ) PORT_NAME("Menu")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("Upside-up")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON7 ) PORT_NAME("Upside-down")
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Screen-up-left")
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Screen-up-right")
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Screen-low-left")
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("Screen-low-right")
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Action")
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_BUTTON8 ) PORT_NAME("Mute/Pause")
	PORT_BIT( 0xc000, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

u32 miuchiz_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	// todo
	return 0;
}

void miuchiz_state::miuchiz(machine_config &config)
{
	ST2205U(config, m_maincpu, XTAL(16'000'000)/2); // Y1 is a hynix HY16.000 crystal, divider is unknown. Y2 is a 32.768KHz xtal for clock
	m_maincpu->set_addrmap(AS_DATA, &miuchiz_state::mem_map);

	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(0));
	m_screen->set_size(68, 98);
	m_screen->set_visarea(0, 68 - 1, 0, 98 - 1);
	m_screen->set_screen_update(m_lcdc, FUNC(st7626_device::screen_update));

	ST7626(config, m_lcdc);
}

// 'bootrom', in on-st2205u-chip mask or flash ROM, common for all versions
#define BIOS \
	ROM_REGION(0x4000, "otp", 0) \
	ROM_LOAD( "otp.dat", 0x000000, 0x004000, CRC(2ff7ec96) SHA1(633365fd19a3d0f2ce56cb499b2577a5fb53e466) )


ROM_START( miuchiz )
	BIOS
	ROM_REGION(0x200000, "flash", 0) // external SST39VF1681 flash chips
	ROM_FILL(0, 0x20000, 0xff)
ROM_END

#define ROM_LOAD_BIOS(bios,name,offset,length,hash) \
	ROMX_LOAD(name, offset, length, hash, ROM_BIOS(bios))


// Bratz series

ROM_START( mcb_cloe )
	BIOS

	ROM_REGION(0x200000, "flash", 0) // external SST39VF1681 flash chips
	ROM_DEFAULT_BIOS("v203")
	ROM_SYSTEM_BIOS( 0, "v102", "Version 1.02" )
	ROM_LOAD_BIOS( 0, "cloe_1.02.dat",      0x000000, 0x200000, CRC(569e14db) SHA1(5600c3a9cd945f53e7897bbad81b4dc0d7e9457d) )
	ROM_SYSTEM_BIOS( 1, "v108", "Version 1.08" )
	ROM_LOAD_BIOS( 1, "cloe_1.08.dat",      0x000000, 0x200000, CRC(6989e9d9) SHA1(70841a2aed9b567e049b93aab1e7c0b04db8c1d9) )
	ROM_SYSTEM_BIOS( 2, "v109", "Version 1.09.03" )
	ROM_LOAD_BIOS( 2, "cloe_1.09.03.dat",   0x000000, 0x200000, CRC(9f1c9414) SHA1(74c71b0e4cc8d18b0d6072558d593a3672299e95) )
	ROM_SYSTEM_BIOS( 3, "v203", "Version 2.03.01" )
	ROM_LOAD_BIOS( 3, "cloe_2.03.01.dat",   0x000000, 0x200000, CRC(c78640cf) SHA1(f58d0fb5c18e474a70ff5bbe529816fbb1a1d295) )
ROM_END

ROM_START( mcb_yasmin )
	BIOS

	ROM_REGION(0x200000, "flash", 0) // external SST39VF1681 flash chips
	ROM_DEFAULT_BIOS("v203")
	ROM_SYSTEM_BIOS( 0, "v102", "Version 1.02" )
	ROM_LOAD_BIOS( 0, "yasmin_1.02.dat",    0x000000, 0x200000, CRC(41749f71) SHA1(d388c977ccc87837b167395423196b480b569e0e) )
	ROM_SYSTEM_BIOS( 1, "v108", "Version 1.08" )
	ROM_LOAD_BIOS( 1, "yasmin_1.08.dat",    0x000000, 0x200000, CRC(56395608) SHA1(4ae9a7026faa7a01d49168336a86beeef56f8e5d) )
	ROM_SYSTEM_BIOS( 2, "v109", "Version 1.09.03" )
	ROM_LOAD_BIOS( 2, "yasmin_1.09.03.dat", 0x000000, 0x200000, CRC(8c4da665) SHA1(e6948324fd880bfb6b2fdaa282aa2e1841a7e6f1) )
	ROM_SYSTEM_BIOS( 3, "v200", "Version 2.00.04" )
	ROM_LOAD_BIOS( 3, "yasmin_2.00.04.dat", 0x000000, 0x200000, CRC(03e30985) SHA1(378bec055c6e37e1cfb24845acd83b7f829072d9) )
	ROM_SYSTEM_BIOS( 4, "v203", "Version 2.03.01" )
	ROM_LOAD_BIOS( 4, "yasmin_2.03.01.dat", 0x000000, 0x200000, CRC(cdc4d525) SHA1(e17f280b54647ec76deb9dab8e5fbf35f1a48266) )
ROM_END


// Monsterz series

ROM_START( mcm_creeper )
	BIOS

	ROM_REGION(0x200000, "flash", 0) // external SST39VF1681 flash chips
	ROM_DEFAULT_BIOS("v109")
	ROM_SYSTEM_BIOS( 0, "v104", "Version 1.04" )
	ROM_LOAD_BIOS( 0, "creeper_1.04.dat",   0x000000, 0x200000, CRC(86411ef2) SHA1(4060050e2133550b61e21632c4f6237a2ce0c35b) )
	ROM_SYSTEM_BIOS( 1, "v109", "Version 1.09.03" )
	ROM_LOAD_BIOS( 1, "creeper_1.09.03.dat",0x000000, 0x200000, CRC(80b0a752) SHA1(99022d3256ba000582b8a429c9105b9564227392) )
ROM_END


ROM_START( mcm_inferno )
	BIOS

	ROM_REGION(0x200000, "flash", 0) // external SST39VF1681 flash chips
	ROM_DEFAULT_BIOS("v200")
	ROM_SYSTEM_BIOS( 0, "v104", "Version 1.04" )
	ROM_LOAD_BIOS( 0, "inferno_1.04.dat",   0x000000, 0x200000, CRC(902b0443) SHA1(3cdaf2b2131132e20c5d63b079060553ff8304bb) )
	ROM_SYSTEM_BIOS( 1, "v109", "Version 1.09.03" )
	ROM_LOAD_BIOS( 1, "inferno_1.09.03.dat",0x000000, 0x200000, CRC(3c6c2bf8) SHA1(e049bc62902c755da1779c57fc7a6724f5210095) )
	ROM_SYSTEM_BIOS( 2, "v200", "Version 2.00.04" )
	ROM_LOAD_BIOS( 2, "inferno_2.00.04.dat",0x000000, 0x200000, CRC(f7f39596) SHA1(3d8d4310a76cff0420b3f885e4df4baa4ff9023f) )
ROM_END

ROM_START( mcm_roc )
	BIOS

	ROM_REGION(0x200000, "flash", 0) // external SST39VF1681 flash chips
	ROM_DEFAULT_BIOS("v200")
	ROM_SYSTEM_BIOS( 0, "v104", "Version 1.04" )
	ROM_LOAD_BIOS( 0, "roc_1.04.dat",       0x000000, 0x200000, CRC(640f0d3a) SHA1(e68a187c4fb9674591cb1661de90bd2fb0464530) )
	ROM_SYSTEM_BIOS( 1, "v108", "Version 1.08" )
	ROM_LOAD_BIOS( 1, "roc_1.08.dat",       0x000000, 0x200000, CRC(bace7bc5) SHA1(8abb22c3ee136af0a95e110f55572d06064f64e3) )
	ROM_SYSTEM_BIOS( 2, "v109", "Version 1.09.03" )
	ROM_LOAD_BIOS( 2, "roc_1.09.03.dat",    0x000000, 0x200000, CRC(1d3bbbeb) SHA1(c3b0658019948c128e9f827027c98f649a9e61f8) )
	ROM_SYSTEM_BIOS( 3, "v200", "Version 2.00.04" )
	ROM_LOAD_BIOS( 3, "roc_2.00.04.dat",    0x000000, 0x200000, CRC(4c92ce75) SHA1(e00f37402d15cb166afdfd4132a4d48556eb3a04) )
ROM_END


// Pawz series

ROM_START( mcp_dash )
	BIOS

	ROM_REGION(0x200000, "flash", 0) // external SST39VF1681 flash chips
	ROM_DEFAULT_BIOS("v109")
	ROM_SYSTEM_BIOS( 0, "v101", "Version 1.01" )
	ROM_LOAD_BIOS( 0, "dash_1.01.dat",      0x000000, 0x200000, CRC(483c9366) SHA1(43f752e73a21b1ce8eee0c37119e77d2915f8f94) )
	ROM_SYSTEM_BIOS( 1, "v102", "Version 1.02" )
	ROM_LOAD_BIOS( 1, "dash_1.02.dat",      0x000000, 0x200000, CRC(68d616a1) SHA1(6bd97f124a53b22143a015597aa3c7d545a53913) )
	ROM_SYSTEM_BIOS( 2, "v109", "Version 1.09.03" )
	ROM_LOAD_BIOS( 2, "dash_1.09.03.dat",   0x000000, 0x200000, CRC(e36047b3) SHA1(04bf0ce65305ca78d755a0a802d7af87882428ed) )
ROM_END

ROM_START( mcp_spike )
	BIOS

	ROM_REGION(0x200000, "flash", 0) // external SST39VF1681 flash chips
	ROM_DEFAULT_BIOS("v109")
	ROM_SYSTEM_BIOS( 0, "v102", "Version 1.02" )
	ROM_LOAD_BIOS( 0, "spike_1.02.dat",     0x000000, 0x200000, CRC(20c3e78e) SHA1(48c9ab622a32ea96abd6186bc4799fb021b9605b) )
	ROM_SYSTEM_BIOS( 1, "v109", "Version 1.09.03" )
	ROM_LOAD_BIOS( 1, "spike_1.09.03.dat",  0x000000, 0x200000, CRC(991e2f4d) SHA1(8f635fbd2a6a3a606cd7cbd19ac097974620fb21) )
ROM_END


} // anonymous namespace

//    YEAR  NAME         PARENT   COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY              FULLNAME                                  FLAGS
COMP( 2006, miuchiz,     0,       0,      miuchiz, miuchiz, miuchiz_state, empty_init, "MGA Entertainment", "Miuchiz Virtual Companions common BIOS", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_IS_BIOS_ROOT | MACHINE_SUPPORTS_SAVE )
COMP( 2006, mcb_cloe,    miuchiz, 0,      miuchiz, miuchiz, miuchiz_state, empty_init, "MGA Entertainment", "Miuchiz Bratz Cloe",                     MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )
COMP( 2006, mcb_yasmin,  miuchiz, 0,      miuchiz, miuchiz, miuchiz_state, empty_init, "MGA Entertainment", "Miuchiz Bratz Yasmin",                   MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )
COMP( 2006, mcm_creeper, miuchiz, 0,      miuchiz, miuchiz, miuchiz_state, empty_init, "MGA Entertainment", "Miuchiz Monsterz Creeper",               MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )
COMP( 2006, mcm_inferno, miuchiz, 0,      miuchiz, miuchiz, miuchiz_state, empty_init, "MGA Entertainment", "Miuchiz Monsterz Inferno",               MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )
COMP( 2006, mcm_roc,     miuchiz, 0,      miuchiz, miuchiz, miuchiz_state, empty_init, "MGA Entertainment", "Miuchiz Monsterz Roc",                   MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )
COMP( 2006, mcp_dash,    miuchiz, 0,      miuchiz, miuchiz, miuchiz_state, empty_init, "MGA Entertainment", "Miuchiz Pawz Dash",                      MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )
COMP( 2006, mcp_spike,   miuchiz, 0,      miuchiz, miuchiz, miuchiz_state, empty_init, "MGA Entertainment", "Miuchiz Pawz Spike",                     MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



mk14.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/*********************************************************************************************************************************

Science of Cambridge MK-14

2009-11-20 Skeleton driver.
2016-08-21 Working

Keys:

UP: MEM increments the currently displayed address, (and goes into data entry mode in V1 bios).
= : TERM changes to "data entry" mode. In this mode, entering hex digits will change the byte at the currently displayed address
- : ABORT changes to "address entry" mode. In this mode, entering hex digits will change the address.
X : GO runs the program from the currently displayed address. On exit, the instruction after the program is displayed

Pasting:
        0-F : as is
        MEM : ^
        TERM: =
        AB :  -
        GO :  X

Example program: ("organ" from p82 of the manual)
-F20=C4^0D^35^C4^00^31^C4^08^C8^F6^C5^01^E4^FF^98^08^8F^00^06^E4^07^07^90^EB^B8^E6^9C^EE^90^E5^-F20X
Pressing keys will produce different tones.

*********************************************************************************************************************************/

#include "emu.h"

#include "cpu/scmp/scmp.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "machine/ins8154.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "mk14.lh"
#include "mk14vdu.lh"

namespace {

class mk14_state : public driver_device
{
public:
	mk14_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cass(*this, "cassette")
		, m_dac(*this, "dac")
		, m_display(*this, "display")
		, m_io_keyboard(*this, "X%u", 0U)
	{ }

	void mk14(machine_config &config);

protected:
	uint8_t keyboard_r(offs_t offset);
	void display_w(offs_t offset, uint8_t data);
	void port_a_w(uint8_t data);
	void cass_w(int state);
	int cass_r();
	void mk14_map(address_map &map) ATTR_COLD;

	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);

	required_device<scmp_device> m_maincpu;
	required_device<cassette_image_device> m_cass;
	required_device<dac_bit_interface> m_dac;
	required_device<pwm_display_device> m_display;
	required_ioport_array<8> m_io_keyboard;
};

class mk14vdu_state : public mk14_state
{
public:
	mk14vdu_state(const machine_config &mconfig, device_type type, const char *tag)
		: mk14_state(mconfig, type, tag)
		, m_screen(*this, "screen")
		, m_palette(*this, "palette")
		, m_chargen(*this, "chargen")
		, m_cfg_ps(*this, "CFG_PS")
		, m_cfg_vdu(*this, "CFG_VDU")
	{ }

	void mk14vdu(machine_config &config);

private:
	void mk14vdu_map(address_map &map) ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void draw_page_character(int page, uint16_t addr, int invert, bitmap_rgb32 &bitmap);
	void draw_page_graphics(int page, uint16_t addr, int invert, bitmap_rgb32 &bitmap);

	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_region_ptr<uint8_t> m_chargen;
	required_ioport m_cfg_ps;
	required_ioport m_cfg_vdu;
};

/*
000-1FF  512 byte SCIOS ROM  Decoded by 0xxx
200-3FF  ROM Shadow / Expansion RAM
400-5FF  ROM Shadow / Expansion RAM
600-7FF  ROM Shadow / Expansion RAM
800-87F  I/O Ports  Decoded by 1xx0
880-8FF  128 bytes I/O chip RAM  Decoded by 1xx0
900-9FF  Keyboard & Display  Decoded by 1x01
A00-AFF  I/O Port & RAM Shadow
B00-BFF  256 bytes RAM (Extended) / VDU RAM  Decoded by 1011
C00-CFF  I/O Port & RAM Shadow
D00-DFF  Keyboard & Display Shadow
E00-EFF  I/O Port & RAM Shadow
F00-FFF  256 bytes RAM (Standard) / VDU RAM  Decoded by 1111
*/


uint8_t mk14_state::keyboard_r(offs_t offset)
{
	if (offset < 8)
		return m_io_keyboard[offset]->read();
	else
		return 0xff;
}

void mk14_state::display_w(offs_t offset, uint8_t data)
{
	if (offset < 8 )
		m_display->matrix(1 << offset, data);
}

void mk14_state::mk14_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x0fff);
	map(0x000, 0x1ff).mirror(0x600).rom(); // ROM
	map(0x800, 0x87f).mirror(0x600).rw("ic8", FUNC(ins8154_device::read_io), FUNC(ins8154_device::write_io)); // I/O
	map(0x880, 0x8ff).mirror(0x600).rw("ic8", FUNC(ins8154_device::read_ram), FUNC(ins8154_device::write_ram)); // 128 bytes I/O chip RAM
	map(0x900, 0x9ff).mirror(0x400).rw(FUNC(mk14_state::keyboard_r), FUNC(mk14_state::display_w));
	map(0xb00, 0xbff).ram(); // VDU RAM
	map(0xf00, 0xfff).ram(); // Standard RAM
}

void mk14vdu_state::mk14vdu_map(address_map &map)
{
	mk14_map(map);
	map(0x200, 0x7ff).ram(); // Expansion RAM
}

/* Input ports */
static INPUT_PORTS_START( mk14 )
	PORT_START("X0")
		PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A")     PORT_CODE(KEYCODE_A)      PORT_CHAR('A')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8")     PORT_CODE(KEYCODE_8)      PORT_CHAR('8')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0")     PORT_CODE(KEYCODE_0)      PORT_CHAR('0')
	PORT_START("X1")
		PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B")     PORT_CODE(KEYCODE_B)      PORT_CHAR('B')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9")     PORT_CODE(KEYCODE_9)      PORT_CHAR('9')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1")     PORT_CODE(KEYCODE_1)      PORT_CHAR('1')
	PORT_START("X2")
		PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C")     PORT_CODE(KEYCODE_C)      PORT_CHAR('C')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("GO")    PORT_CODE(KEYCODE_X)      PORT_CHAR('X')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2")     PORT_CODE(KEYCODE_2)      PORT_CHAR('2')
	PORT_START("X3")
		PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D")     PORT_CODE(KEYCODE_D)      PORT_CHAR('D')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MEM")   PORT_CODE(KEYCODE_UP)     PORT_CHAR('^')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3")     PORT_CODE(KEYCODE_3)      PORT_CHAR('3')
	PORT_START("X4")
		PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ABORT") PORT_CODE(KEYCODE_MINUS)  PORT_CHAR('-')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4")     PORT_CODE(KEYCODE_4)      PORT_CHAR('4')
	PORT_START("X5")
		PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5")     PORT_CODE(KEYCODE_5)      PORT_CHAR('5')
	PORT_START("X6")
		PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E")     PORT_CODE(KEYCODE_E)      PORT_CHAR('E')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6")     PORT_CODE(KEYCODE_6)      PORT_CHAR('6')
	PORT_START("X7")
		PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F")     PORT_CODE(KEYCODE_F)      PORT_CHAR('F')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TERM")  PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7")     PORT_CODE(KEYCODE_7)      PORT_CHAR('7')
INPUT_PORTS_END

static INPUT_PORTS_START( mk14vdu )
	PORT_INCLUDE(mk14)

	PORT_START("CFG_PS")
		PORT_CONFNAME(0x0f, 0x02, "Page Select")
		PORT_CONFSETTING(0x00, "000")
		PORT_CONFSETTING(0x01, "100")
		PORT_CONFSETTING(0x02, "200")
		PORT_CONFSETTING(0x03, "300")
		PORT_CONFSETTING(0x04, "400")
		PORT_CONFSETTING(0x05, "500")
		PORT_CONFSETTING(0x06, "600")
		PORT_CONFSETTING(0x07, "700")
		PORT_CONFSETTING(0x08, "800")
		PORT_CONFSETTING(0x09, "900")
		PORT_CONFSETTING(0x0a, "A00")
		PORT_CONFSETTING(0x0b, "B00")
		PORT_CONFSETTING(0x0c, "C00")
		PORT_CONFSETTING(0x0d, "D00")
		PORT_CONFSETTING(0x0e, "E00")
		PORT_CONFSETTING(0x0f, "F00")
		PORT_CONFNAME(0xf0, 0x10, "Top Page > Page Select")
		PORT_CONFSETTING(0x00, "None")
		PORT_CONFSETTING(0x10, "PS1")
		PORT_CONFSETTING(0x20, "PS2")
		PORT_CONFSETTING(0x40, "PS3")
		PORT_CONFSETTING(0x80, "PS4")
	PORT_START("CFG_VDU")
		PORT_CONFNAME(0x01, 0x01, "Reverse Pages")
		PORT_CONFSETTING(0x00, DEF_STR( Yes ))
		PORT_CONFSETTING(0x01, DEF_STR( No ))
		PORT_CONFNAME(0x22, 0x00, "Video Mode")
		PORT_CONFSETTING(0x00, "Character (Low)" )
		PORT_CONFSETTING(0x02, "Graphics (High)" )
		PORT_CONFSETTING(0x20, "Mixed (Top Page)" )
		PORT_CONFNAME(0x44, 0x04, "Invert Video")
		PORT_CONFSETTING(0x00, "Inverted (Low)" )
		PORT_CONFSETTING(0x04, "Normal (High)")
		PORT_CONFSETTING(0x40, "Mixed (Top Page)" )
INPUT_PORTS_END

void mk14_state::port_a_w(uint8_t data)
{
}

void mk14_state::cass_w(int state)
{
	m_cass->output(state ? -1.0 : +1.0);
	m_dac->write(state);
}

int mk14_state::cass_r()
{
	return (m_cass->input() > 0.03) ? 1 : 0;
}

//-------------------------------------------------
//  gfx_layout acorn_vdu80_charlayout
//-------------------------------------------------

static const gfx_layout mk14vdu_charlayout =
{
	5, 7,                   /* 5 x 7 characters */
	64,                     /* 64 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0 * 8,  1 * 8,  2 * 8,  3 * 8,  4 * 8,  5 * 8,  6 * 8 },
	8 * 8                   /* every char takes 8 bytes */
};

//-------------------------------------------------
//  GFXDECODE( gfx_mk14vdu )
//-------------------------------------------------

static GFXDECODE_START(gfx_mk14vdu)
	GFXDECODE_ENTRY("chargen", 0, mk14vdu_charlayout, 0, 1)
GFXDECODE_END

uint32_t mk14vdu_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	for (int page = 0; page < 2; page++)
	{
		// top page adjusted with reverse pages
		bool top_page = BIT(m_cfg_vdu->read(), 0) ? (page == 1) : (page == 0);

		// page select
		uint16_t addr = BIT(m_cfg_ps->read(), 0, 4) << 8;
		if (top_page)
			addr |= (BIT(m_cfg_ps->read(), 4, 4) << 8);

		// invert video
		int invert = BIT(m_cfg_vdu->read(), 6) ? !top_page : !BIT(m_cfg_vdu->read(), 2);

		// character/graphic mode
		if (BIT(m_cfg_vdu->read(), 5) ? top_page : BIT(m_cfg_vdu->read(), 1))
			draw_page_graphics(page, addr, invert, bitmap);
		else
			draw_page_character(page, addr, invert, bitmap);
	}
	return 0;
}

void mk14vdu_state::draw_page_character(int page, uint16_t addr, int invert, bitmap_rgb32 &bitmap)
{
	address_space &space = m_maincpu->space(AS_PROGRAM);

	const pen_t *pens = m_palette->pens();

	for (int y = 0; y < 128; y++)
	{
		uint16_t videoram_addr = addr + ((y >> 3) * 16);

		int x = 0;

		for (int sx = 0; sx < 16; sx++)
		{
			uint8_t videoram_data = space.read_byte(videoram_addr++);
			uint16_t const charrom_addr = ((videoram_data & 0x3f) << 3) | (y % 8);
			uint8_t charrom_data = m_chargen[charrom_addr];

			for (int bit = 0; bit < 8; bit++)
			{
				// invert single character (implemented in some replica boards)
				int color = BIT(charrom_data, 6) ^ invert ^ BIT(videoram_data, 7);
				bitmap.pix(y + (page * 128), x++) = pens[color];
				charrom_data <<= 1;
			}
		}
	}
}

void mk14vdu_state::draw_page_graphics(int page, uint16_t addr, int invert, bitmap_rgb32 &bitmap)
{
	address_space &space = m_maincpu->space(AS_PROGRAM);

	const pen_t *pens = m_palette->pens();

	for (int y = 0; y < 128; y++)
	{
		uint16_t videoram_addr = addr + ((y >> 2) * 8);

		int x = 0;

		for (int sx = 0; sx < 8; sx++)
		{
			uint8_t videoram_data = space.read_byte(videoram_addr++);

			for (int bit = 0; bit < 8; bit++)
			{
				int color = BIT(videoram_data, 7) ^ invert;
				bitmap.pix(y + (page * 128), x++) = pens[color];
				bitmap.pix(y + (page * 128), x++) = pens[color];
				videoram_data <<= 1;
			}
		}
	}
}

QUICKLOAD_LOAD_MEMBER(mk14_state::quickload_cb)
{
	if (image.software_entry() == nullptr)
		return std::make_pair(image_error::UNSUPPORTED, "Unsupported quickload format");

	uint16_t const size = image.length();
	int load_addr, exec_addr;
	sscanf(image.get_feature("load"), "%x", &load_addr);
	sscanf(image.get_feature("exec"), "%x", &exec_addr);

	for (uint16_t i = 0; i < size; i++)
	{
		uint8_t data;

		if (image.fread(&data, 1) != 1)
			return std::make_pair(image_error::UNSPECIFIED, std::string());
		m_maincpu->space(AS_PROGRAM).write_byte(load_addr + i, data);
	}

	m_maincpu->set_pc(exec_addr);

	return std::make_pair(std::error_condition(), std::string());
}

void mk14_state::mk14(machine_config &config)
{
	// IC1 1SP-8A/600 (8060) SC/MP Microprocessor
	INS8060(config, m_maincpu, 4.433619_MHz_XTAL / 2);
	m_maincpu->flag_out().set(FUNC(mk14_state::cass_w));
	m_maincpu->s_out().set_nop();
	m_maincpu->s_in().set(FUNC(mk14_state::cass_r));
	m_maincpu->sense_a().set_constant(0);
	m_maincpu->sense_b().set(FUNC(mk14_state::cass_r));
	m_maincpu->halt().set_nop();
	m_maincpu->set_addrmap(AS_PROGRAM, &mk14_state::mk14_map);

	config.set_default_layout(layout_mk14);
	PWM_DISPLAY(config, m_display).set_size(8, 8);
	m_display->set_segmask(0xff, 0xff);

	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.25);
	ZN425E(config, "dac8", 0).add_route(ALL_OUTPUTS, "speaker", 0.5); // Ferranti ZN425E

	ins8154_device &ic8(INS8154(config, "ic8"));
	ic8.out_a().set(FUNC(mk14_state::port_a_w));
	ic8.out_b().set("dac8", FUNC(dac_byte_interface::data_w));

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "speaker", 0.05);

	quickload_image_device &quickload(QUICKLOAD(config, "quickload", "bin"));
	quickload.set_load_callback(FUNC(mk14_state::quickload_cb));
	quickload.set_interface("mk14_quik");

	SOFTWARE_LIST(config, "quik_ls").set_original("mk14_quik");
}

void mk14vdu_state::mk14vdu(machine_config &config)
{
	mk14(config);

	m_maincpu->set_clock(4_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &mk14vdu_state::mk14vdu_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(4_MHz_XTAL, 256, 0, 128, 312, 0, 256);
	m_screen->set_screen_update(FUNC(mk14vdu_state::screen_update));
	config.set_default_layout(layout_mk14vdu);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_mk14vdu);
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	subdevice<software_list_device>("quik_ls")->set_filter("VDU");
}

/* ROM definition */
ROM_START( mk14 )
	ROM_REGION( 0x200, "maincpu", 0 )
	// IC2,3 74S571 512 x 4 bit ROM
	ROM_DEFAULT_BIOS("v2")
	ROM_SYSTEM_BIOS(0, "v2", "SCIOS V2")
	ROMX_LOAD( "scios_v2.bin", 0x0000, 0x0200, CRC(8b667daa) SHA1(802dc637ce5391a2a6627f76f919b12a869b56ef), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v1", "SCIOS V1")
	ROMX_LOAD( "scios_v1.bin", 0x0000, 0x0200, CRC(3d2477e7) SHA1(795829a2025e24d87a413e245d72a284f872e0db), ROM_BIOS(1))
ROM_END

ROM_START( mk14vdu )
	ROM_REGION( 0x200, "maincpu", 0 )
	// IC2,3 74S571 512 x 4 bit ROM
	ROM_DEFAULT_BIOS("v2")
	ROM_SYSTEM_BIOS(0, "v2", "SCIOS V2")
	ROMX_LOAD( "scios_v2.bin", 0x0000, 0x0200, CRC(8b667daa) SHA1(802dc637ce5391a2a6627f76f919b12a869b56ef), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v1", "SCIOS V1")
	ROMX_LOAD( "scios_v1.bin", 0x0000, 0x0200, CRC(3d2477e7) SHA1(795829a2025e24d87a413e245d72a284f872e0db), ROM_BIOS(1))

	ROM_REGION( 0x200, "chargen", 0 )
	ROM_LOAD( "dm8678cab.bin", 0x0000, 0x0200, CRC(8da502e7) SHA1(30d2dd9658823cdc2b2f6ef37f5a05d6f3e0db76))
ROM_END

} // Anonymous namespace

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT     CLASS          INIT        COMPANY                 FULLNAME      FLAGS
COMP( 1977, mk14,    0,      0,      mk14,    mk14,     mk14_state,    empty_init, "Science of Cambridge", "MK-14",      MACHINE_SUPPORTS_SAVE )
COMP( 1978, mk14vdu, mk14,   0,      mk14vdu, mk14vdu,  mk14vdu_state, empty_init, "Science of Cambridge", "MK-14 VDU",  MACHINE_SUPPORTS_SAVE )



mk1forth.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Mark 1 FORTH Computer (Copyright © Andrew Holme, 2003)

    This is a homebrew fig-FORTH interpreter using an original TTL CPU,
    operating at 1 microinstruction per microsecond. Neither the microcode
    nor the FORTH bytecode fills very much of the 2764 EPROMs specified.

    Notes on the I/O board design:
    * The second peripheral was supposed to be a 8255, but the port pins
      were never connected to anything and its socket was left empty.
    * RxC and TxC rates are generated by a HEF4060BP divider. The original
      plan was to operate the 82C51 in 1x mode at 38400, 19200 or 9600
      baud, but reliable 1x asynchronous reception could not be obtained.
      9600 baud is the maximum rate obtainable in 16x mode.
    * Before the ALU was designed, the RxRDY signal was buffered directly
      onto the data bus to be tested as a skip condition. Since this could
      not be guaranteed to be stable through an entire instruction cycle,
      an open-drain IRQ signal had to be added to the bus, with the
      requisite termination and synchronization provided on the ALU board.

***************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/mk1/mk1.h"
#include "machine/clock.h"
#include "machine/input_merger.h"
#include "machine/i8251.h"


namespace {

class mk1forth_state : public driver_device
{
public:
	mk1forth_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
	{
	}

	void mk1forth(machine_config &config);

private:
	void ucode_map(address_map &map) ATTR_COLD;
	void data_map(address_map &map) ATTR_COLD;
	void stack_map(address_map &map) ATTR_COLD;
};

void mk1forth_state::ucode_map(address_map &map)
{
	map(0x000, 0xfff).rom().region("ucode", 0);
}

void mk1forth_state::data_map(address_map &map)
{
	map(0x0000, 0x1fff).rom().region("forth", 0);
	map(0x2000, 0x7fff).ram();
	map(0xe000, 0xe001).mirror(0xffe).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	//map(0xf000, 0xf003).mirror(0xffc).rw("pio", FUNC(i8255_device::read), FUNC(i8255_device::write));
}

void mk1forth_state::stack_map(address_map &map)
{
	map(0x000, 0x3ff).ram();
}

void mk1forth_state::mk1forth(machine_config &config)
{
	mk1_cpu_device &maincpu(MK1_CPU(config, "maincpu", 2_MHz_XTAL / 2));
	maincpu.set_addrmap(AS_PROGRAM, &mk1forth_state::ucode_map);
	maincpu.set_addrmap(AS_DATA, &mk1forth_state::data_map);
	maincpu.set_addrmap(mk1_cpu_device::AS_STACK, &mk1forth_state::stack_map);

	INPUT_MERGER_ANY_HIGH(config, "uartirq").output_handler().set_inputline("maincpu", mk1_cpu_device::IRQ_LINE);

	i8251_device &uart(I8251(config, "uart", 2.4576_MHz_XTAL));
	uart.rxrdy_handler().set("uartirq", FUNC(input_merger_device::in_w<0>));
	uart.txrdy_handler().set("uartirq", FUNC(input_merger_device::in_w<1>));
	uart.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	uart.dtr_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	uart.dtr_handler().append("rs232", FUNC(rs232_port_device::write_dtr));
	uart.write_cts(0);

	clock_device &clock(CLOCK(config, "baudclock", 2.4576_MHz_XTAL / 16));
	clock.signal_handler().set("uart", FUNC(i8251_device::write_rxc));
	clock.signal_handler().append("uart", FUNC(i8251_device::write_txc));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("uart", FUNC(i8251_device::write_rxd));
	rs232.cts_handler().set("uart", FUNC(i8251_device::write_dsr));
}

ROM_START(mk1forth)
	ROM_REGION(0x2000, "ucode", 0)
	ROM_LOAD("rom.bin", 0x0000, 0x2000, CRC(d55d8a09) SHA1(8043a276826d40aa35b4ab8c94b99e3862fb62e7))

	ROM_REGION(0x2000, "forth", 0)
	ROM_LOAD("forth.bin", 0x0000, 0x2000, CRC(8b2863fa) SHA1(cc1d0662f10cf767f3f2581270ca2b4711121ffc))
ROM_END

} // anonymous namespace


COMP(2003, mk1forth, 0, 0, mk1forth, 0, mk1forth_state, empty_init, "Andrew Holme", "Mark 1 FORTH Computer", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE)



mk85.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

        Elektronika MK-85

        12/05/2009 Skeleton driver.

    http://www.taswegian.com/MOSCOW/mk-85.html

This is a Soviet computer-calculator, very similar in looks to the Sharp.
It has a LCD display.

Models:
    MK-85:  2K of RAM
    MK-85M: 6K of RAM
    MK-85C: Military cryptographic device. Typing text into it produces
            a string of numbers.

****************************************************************************/

#include "emu.h"
#include "cpu/t11/t11.h"
#include "emupal.h"
#include "screen.h"


namespace {

class mk85_state : public driver_device
{
public:
	mk85_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void mk85(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

	void mem_map(address_map &map) ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	required_device<k1801vm2_device> m_maincpu;
};


void mk85_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).rom().mirror(0x4000);
	map(0x8000, 0xffff).ram();
}

/* Input ports */
static INPUT_PORTS_START( mk85 )
INPUT_PORTS_END


void mk85_state::machine_reset()
{
}

void mk85_state::machine_start()
{
}

uint32_t mk85_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void mk85_state::mk85(machine_config &config)
{
	/* basic machine hardware */
	K1801VM2(config, m_maincpu, XTAL(4'000'000));
	m_maincpu->set_initial_mode(0);
	m_maincpu->set_addrmap(AS_PROGRAM, &mk85_state::mem_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	screen.set_screen_update(FUNC(mk85_state::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);
}

/* ROM definition */
ROM_START( mk85 )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "mk85.rom", 0x0000, 0x4000, CRC(398e4fd1) SHA1(5e2f877d0f451b46840f01190004552bad5248c8))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY        FULLNAME  FLAGS */
COMP( 1986, mk85, 0,      0,      mk85,    mk85,  mk85_state, empty_init, "Elektronika", "MK-85",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



mk90.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

Elektronika MK-90

2009-05-12 Skeleton driver.


    http://www.pisi.com.pl/piotr433/index.htm#mk90
    http://www.taswegian.com/MOSCOW/mk-90.html

This is a Soviet computer-calculator, very similar in looks to the Sharp.
It has a LCD display. It cost about 1500 roubles, which is the wages for 6
months for an average citizen.

Status: Starts in the weeds.

****************************************************************************/

#include "emu.h"
#include "cpu/t11/t11.h"
#include "emupal.h"
#include "screen.h"


namespace {

class mk90_state : public driver_device
{
public:
	mk90_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void mk90(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

	void mem_map(address_map &map) ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	required_device<k1801vm2_device> m_maincpu;
};


void mk90_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).ram(); // RAM
	map(0x4000, 0x7fff).rom(); // Extension ROM
	map(0x8000, 0xffff).rom(); // Main ROM
//  map(0xe800, 0xe801) LCD address
//  map(0xe802, 0xe803) LCD data
//  map(0xe810, 0xe810) serial bus controller data
//  map(0xe812, 0xe813) serial bus controller transfer rate
//  map(0xe814, 0xe814) serial bus controller control/status
//  map(0xe816, 0xe816) serial bus controller command
//  map(0xea00, 0xea7e) RTC
}

/* Input ports */
static INPUT_PORTS_START( mk90 )
INPUT_PORTS_END


void mk90_state::machine_reset()
{
}

void mk90_state::machine_start()
{
}

uint32_t mk90_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void mk90_state::mk90(machine_config &config)
{
	/* basic machine hardware */
	K1801VM2(config, m_maincpu, XTAL(4'000'000));
	m_maincpu->set_initial_mode(0x8000);
	m_maincpu->set_addrmap(AS_PROGRAM, &mk90_state::mem_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	screen.set_screen_update(FUNC(mk90_state::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);
}

/* ROM definition */
ROM_START( mk90 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "bas1", "Basic 1")
	ROMX_LOAD( "mk90ro10.bin",  0x8000, 0x8000, CRC(fac18038) SHA1(639f09a1be5f781f897603d0f799f7c6efd1b67f), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "bas2", "Basic 2")
	ROMX_LOAD( "mk90ro20.bin",  0x8000, 0x8000, CRC(d8b3a5f5) SHA1(8f7ab2d97c7466392b6354c0ea7017531c2133ae), ROM_BIOS(1))
	ROMX_LOAD( "mk90ro20t.bin", 0x4000, 0x4000, CRC(0f4b9434) SHA1(c74bbde6d201913c9e67ef8e2abe14b784187f8d), ROM_BIOS(1))  // Expansion ROM
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY        FULLNAME  FLAGS */
COMP( 1988, mk90, 0,      0,      mk90,    mk90,  mk90_state, empty_init, "Elektronika", "MK-90",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



mk98.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
// thanks-to:Sergei Frolov, iret
/***************************************************************************

    Elektronika MK-98 palmtop prototype

    80C86 clone CPU @ ??? MHz
    ASIC 1
        8250 UART
        8254 timer
        8259 PIC (always runs in x86 mode)
    ASIC 2
        memory controller?
    ASIC 3
        video controller?
    128KB of RAM
    128KB of ROM
        self-tests
        monitor
        serial transfer
        memo pad
        spreadsheet
    LCD, 240x128 pixels (40x16 chars in text mode), two shades of gray (?)

    2 slots for battery-backed SRAM carts (10KB known to exist, max size 64KB).
    Carts are also compatible with MK-90 and MK-92 palmtops.

    To do:
    - native keyboard
    - carts
***************************************************************************/


#include "emu.h"

#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "bus/rs232/rs232.h"
#include "cpu/i86/i86.h"
#include "machine/ins8250.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/ram.h"

#include "screen.h"
#include "emupal.h"
#include "softlist.h"
#include "speaker.h"


#define LOG_KEYBOARD  (1U << 1)
#define LOG_DEBUG     (1U << 2)

#define VERBOSE (LOG_GENERAL|LOG_DEBUG)
//#define LOG_OUTPUT_FUNC printf
#include "logmacro.h"

#define LOGKBD(...) LOGMASKED(LOG_KEYBOARD, __VA_ARGS__)
#define LOGDBG(...) LOGMASKED(LOG_DEBUG, __VA_ARGS__)


namespace {

class mk98_state : public driver_device
{
public:
	mk98_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_pic8259(*this, "pic8259")
		, m_screen(*this, "screen")
		, m_p_videoram(*this, "video")
	{ }

	void mk98(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<mk98pic_device> m_pic8259;
	required_device<screen_device> m_screen;

private:
	void mk98_palette(palette_device &palette) const;

	void keyboard_clock_w(int state);
	void keyboard_data_w(int state);
	uint8_t keyboard_r(offs_t offset);
	void keyboard_w(offs_t offset, uint8_t data);
	uint8_t serial_r(offs_t offset);
	void serial_w(offs_t offset, uint8_t data);
	uint8_t video_r(offs_t offset);
	void video_w(offs_t offset, uint8_t data);
	void video_address_w(uint8_t data);
	uint8_t video_register_r();
	void video_register_w(uint8_t data);

	void mk98_io(address_map &map) ATTR_COLD;
	void mk98_map(address_map &map) ATTR_COLD;

	required_shared_ptr<u8> m_p_videoram;

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	// crtc
	u8 m_crtc[64]{}, m_cursor_start_ras = 0;
	u16 m_disp_start_addr = 0, m_cursor_addr = 0;
	int m_register_address_latch = 0, m_font_upload = 0;
	bool m_graphics_mode = false;

	// from pt68k4.cpp
	bool m_kclk = false;
	uint8_t m_kdata = 0;
	uint8_t m_scancode = 0;
	uint8_t m_kbdflag = 0;
	int m_kbit = 0;
	u8 m_p_chargen[0x800] = { };
};


void mk98_state::mk98_palette(palette_device &palette) const
{
	palette.set_pen_color(0, 0xa0, 0xa8, 0xa0);
	palette.set_pen_color(1, 0x50, 0x58, 0x20);
	palette.set_pen_color(2, 0x03, 0x03, 0x01);
}

uint32_t mk98_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	if (m_graphics_mode)
	{
		for (int y = 0; y < 128; y++)
		{
			uint16_t *p = &bitmap.pix(y);
			int const offset = y * (240 / 8);

			for (int x = offset; x < offset + (240 / 8); x++)
			{
				uint16_t const gfx = m_p_videoram[x];

				for (int i = 7; i >= 0; i--)
				{
					*p++ = BIT(gfx, i);
				}
			}
		}
	}
	else
	{
		bool blink((m_screen->frame_number() % 10) > 4);

		// screen memory is normal MDA 80x25, but only a fixed 40x16 window is displayed
		for (int y = 0; y < 128; y++)
		{
			uint16_t *p = &bitmap.pix(y);
			int const offset = (y / 8) * 160;

			for (int x = offset; x < offset + 80; x += 2)
			{
				uint8_t const chr = m_p_videoram[x];
				uint8_t const attr = m_p_videoram[x + 1];

				uint16_t gfx = m_p_chargen[(chr)*8 + (y % 8) + 1];
				int fg = 1;

				if ((x >> 1) == m_cursor_addr && blink && (y % 8) >= m_cursor_start_ras)
				{
					gfx = 0xff;
				}

				switch (attr)
				{
				case 0x00:
					gfx = 0;
					break;

				case 0x01:
					if (y % 8 == 7) gfx = 0xff;
					break;

				case 0x0f:
					fg = 2;
					break;

				case 0x70:
					gfx ^= 0xff;
					break;

				case 0xf0:
					gfx ^= 0xff;
					fg = 2;
					break;
				}

				for (int i = 7; i >= 2; i--)
				{
					*p++ = BIT(gfx, i) ? fg : 0;
				}
			}
		}
	}

	return 0;
}


void mk98_state::machine_start()
{
	save_item(NAME(m_crtc));
	save_item(NAME(m_graphics_mode));

	//
	save_item(NAME(m_kclk));
	save_item(NAME(m_kdata));
	save_item(NAME(m_scancode));
	save_item(NAME(m_kbdflag));
	save_item(NAME(m_kbit));
}

void mk98_state::machine_reset()
{
	std::fill(std::begin(m_crtc), std::end(m_crtc), 0);
	m_graphics_mode = false;
	m_font_upload = 0;
	m_cursor_start_ras = 7;
	m_disp_start_addr = 0;
	m_cursor_addr = 0;

	m_kclk = true;
	m_kbit = 0;
	m_scancode = 0;
	m_kbdflag = 0;
}

uint8_t mk98_state::serial_r(offs_t offset)
{
	LOGDBG("aux: read  == %02X\n", m_crtc[0]);
	return m_crtc[0] | (m_crtc[0] & 1) << 7;
}

void mk98_state::serial_w(offs_t offset, uint8_t data)
{
	LOGDBG("aux: write <= %02X\n", data);
	m_crtc[0] = data & 0xe7;
}

/* keyboard HLE -- adapted from pt68k4.cpp */

uint8_t mk98_state::keyboard_r(offs_t offset)
{
	if (offset == 0)
	{
		LOGKBD("kbd: read  %02X == %02X\n", offset + 0x60, m_scancode);
		m_pic8259->ir1_w(CLEAR_LINE);
		return m_scancode;
	}
	else
		return 0;
}

void mk98_state::keyboard_w(offs_t offset, uint8_t data)
{
	LOGKBD("kbd: write %02X <= %02X\n", offset + 0x60, data);
	m_pic8259->ir1_w(CLEAR_LINE);
}

void mk98_state::keyboard_clock_w(int state)
{
	LOGKBD("kbd: KCLK: %d kbit: %d\n", state ? 1 : 0, m_kbit);

	if ((state == ASSERT_LINE) && (!m_kclk))
	{
		if (m_kbit >= 1 && m_kbit <= 8)
		{
			m_scancode >>= 1;
			m_scancode |= m_kdata;
		}

		// stop bit?
		if (m_kbit == 9)
		{
			m_scancode >>= 1;
			m_scancode |= m_kdata;
			// arrow keys
			switch (m_scancode)
			{
				case 0x48: m_scancode = 0x3c; break;
				case 0x50: m_scancode = 0x3e; break;
				case 0x4b: m_scancode = 0x3d; break;
				case 0x4d: m_scancode = 0x3f; break;
				case 0x53: m_scancode = 0x3b; break;
			}
			LOGKBD("kbd: scancode %02x\n", m_scancode);
			m_kbit = 0;
			m_pic8259->ir1_w(ASSERT_LINE);
		}
		else
		{
			m_kbit++;
		}
	}

	m_kclk = (state == ASSERT_LINE) ? true : false;
}

void mk98_state::keyboard_data_w(int state)
{
	LOGKBD("kbd: KDATA: %d\n", state ? 1 : 0);
	m_kdata = (state == ASSERT_LINE) ? 0x80 : 0x00;
}

/* video HLE */

uint8_t mk98_state::video_register_r()
{
	uint8_t ret = 0;

	switch (m_register_address_latch)
	{
	case 0x0e:
		ret = (m_cursor_addr >> 8) & 0xff;
		break;

	case 0x0f:
		ret = (m_cursor_addr >> 0) & 0xff;
		break;

	/* all other registers are write only and return 0 */
	default:
		break;
	}

	return ret;
}

void mk98_state::video_register_w(uint8_t data)
{
	switch (m_register_address_latch)
	{
	case 1:
		m_graphics_mode = BIT(data, 4);
		break;

	case 0x0a:
		m_cursor_start_ras = data & 0x7f;
		break;

	case 0x0c:
		m_disp_start_addr = ((data & 0x3f) << 8) | (m_disp_start_addr & 0x00ff);
		break;

	case 0x0d:
		m_disp_start_addr = ((data & 0xff) << 0) | (m_disp_start_addr & 0xff00);
		break;

	case 0x0e:
		m_cursor_addr = ((data & 0x3f) << 8) | (m_cursor_addr & 0x00ff);
		break;

	case 0x0f:
		m_cursor_addr = ((data & 0xff) << 0) | (m_cursor_addr & 0xff00);
		break;
	}
}

void mk98_state::video_address_w(uint8_t data)
{
	m_register_address_latch = data & 0x3f;
}

uint8_t mk98_state::video_r(offs_t offset)
{
	switch (offset)
	{
	case 0:
		return keyboard_r(0);

	case 5:
		return video_register_r();
	}

	return 0xff;
}

void mk98_state::video_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 2:
		m_font_upload++;
		if ((m_font_upload > 63) && (m_font_upload < 0x840))
			m_p_chargen[m_font_upload - 64] = data;
		break;

	case 4:
		video_address_w(data);
		break;

	case 5:
		video_register_w(data);
		break;
	}
}


void mk98_state::mk98_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x1ffff).ram();
	map(0xb8000, 0xbdfff).ram().share("video");
	map(0xc0000, 0xdffff).noprw(); // ???
	map(0xe0000, 0xfffff).rom().region("romdos", 0);
}

void mk98_state::mk98_io(address_map &map)
{
	map.unmap_value_low();
//  map(0x0000, 0x000f).unmaprw();
	map(0x0020, 0x002f).rw(m_pic8259, FUNC(mk98pic_device::read), FUNC(mk98pic_device::write));
	map(0x0040, 0x004f).rw("pit8254", FUNC(pit8254_device::read), FUNC(pit8254_device::write));
	map(0x0060, 0x0063).rw(FUNC(mk98_state::keyboard_r), FUNC(mk98_state::keyboard_w));
//  unidentified devices
//  map(0x00a0, 0x00a1).unmapw();
//  map(0x0110, 0x0111).unmapw();
//  map(0x0112, 0x0113).unmaprw();
//  map(0x0150, 0x0150).unmapw(); -- cart slot select
//  map(0x0170, 0x0170).unmapw();
	map(0x03d0, 0x03df).rw(FUNC(mk98_state::video_r), FUNC(mk98_state::video_w));
	map(0x03f8, 0x03fe).rw("uart0", FUNC(ins8250_device::ins8250_r), FUNC(ins8250_device::ins8250_w));
	map(0x03ff, 0x03ff).rw(FUNC(mk98_state::serial_r), FUNC(mk98_state::serial_w));
}


void mk98_state::mk98(machine_config &config)
{
	I8088(config, m_maincpu, XTAL(5'370'000)); // actually a 80C86 clone
	m_maincpu->set_addrmap(AS_PROGRAM, &mk98_state::mk98_map);
	m_maincpu->set_addrmap(AS_IO, &mk98_state::mk98_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259", FUNC(mk98pic_device::inta_cb));

	pit8254_device &pit8254(PIT8254(config, "pit8254", 0));
	pit8254.set_clk<0>(16000000/2/8); // FIXME unknown clock

	MK98PIC(config, m_pic8259, 0);
	m_pic8259->out_int_callback().set_inputline(m_maincpu, 0);

	pc_kbdc_device &pc_kbdc(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_KEYTRONIC_PC3270));
	pc_kbdc.out_clock_cb().set(FUNC(mk98_state::keyboard_clock_w));
	pc_kbdc.out_data_cb().set(FUNC(mk98_state::keyboard_data_w));

	ins8250_device &uart0(INS8250(config, "uart0", XTAL(1'843'200)));
	uart0.out_tx_callback().set("serport0", FUNC(rs232_port_device::write_txd));
	uart0.out_dtr_callback().set("serport0", FUNC(rs232_port_device::write_dtr));
	uart0.out_rts_callback().set("serport0", FUNC(rs232_port_device::write_rts));

	rs232_port_device &serport0(RS232_PORT(config, "serport0", default_rs232_devices, nullptr));
	serport0.rxd_handler().set(uart0, FUNC(ins8250_device::rx_w));
	serport0.dcd_handler().set(uart0, FUNC(ins8250_device::dcd_w));
	serport0.dsr_handler().set(uart0, FUNC(ins8250_device::dsr_w));
	serport0.ri_handler().set(uart0, FUNC(ins8250_device::ri_w));
	serport0.cts_handler().set(uart0, FUNC(ins8250_device::cts_w));

	SCREEN(config, m_screen, SCREEN_TYPE_LCD, rgb_t::white());
	m_screen->set_screen_update(FUNC(mk98_state::screen_update));
	m_screen->set_raw(XTAL(5'370'000) / 2, 300, 0, 240, 180, 0, 128);
	m_screen->set_palette("palette");

	PALETTE(config, "palette", FUNC(mk98_state::mk98_palette), 3);

	RAM(config, RAM_TAG).set_default_size("128K");
}


ROM_START( mk98 )
	ROM_REGION(0x20000, "romdos", 0)
	ROM_LOAD("e0000.bin", 0, 0x20000, CRC(85785bd5) SHA1(b10811715f44cf8e2b41baea7b62a35082e04048))
ROM_END

} // anonymous namespace


//    YEAR  NAME   PARENT   COMPAT  MACHINE  INPUT  CLASS        INIT         COMPANY         FULLNAME  FLAGS
COMP( 1998, mk98,  0,       0,      mk98,    0,     mk98_state,  empty_init,  "Elektronika",  "MK-98",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



mkit09.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Multitech Microkit09

2013-12-08 Mostly working driver.


ToDo:
    - Need software to test with

Pasting:
    0-F : as is
    (inc) : ^
    (dec) : V
    M (memory) : -
    G (Go) : X

Test Paste:
    -0000 00^11^22^33^44^55^66^77^88^99^--0000
    Now press up-arrow to confirm the data has been entered.



2015-10-02 Added alternate bios found on a forum. Memory map is different.
               Still to fix keyboard and display. No documentation exists.

2019-10-10 Adjusted mkit09a to display and accept input. However it appears
               to be some other 6809 trainer. Although it "works", the usage
               is largely unknown, as are some of the keys. Cassette status
               also unknown. There may be a device at E400-E407.
               When R (regs) is pressed, you can press 0(CC),1(A),2(B),3(DP),
               4,(IX),5(IY),6(U),7(PC),8(SP).
               Some patches were needed due to bugs or bad dump?

****************************************************************************/

#include "emu.h"
#include "cpu/m6809/m6809.h"
#include "imagedev/cassette.h"
#include "machine/6821pia.h"
#include "video/pwm.h"
#include "speaker.h"

#include "mkit09.lh"


namespace {

class mkit09_state : public driver_device
{
public:
	mkit09_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_pia(*this, "pia")
		, m_cass(*this, "cassette")
		, m_maincpu(*this, "maincpu")
		, m_display(*this, "display")
		, m_io_keyboard(*this, "X%u", 0U)
	{ }

	void mkit09(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(trigger_reset);
	DECLARE_INPUT_CHANGED_MEMBER(trigger_nmi);

protected:
	u8 pa_r();
	u8 pb_r();
	u8 m_digit = 0U;
	u8 m_seg = 0U;
	void machine_reset() override ATTR_COLD;
	void machine_start() override ATTR_COLD;
	required_device<pia6821_device> m_pia;
	required_device<cassette_image_device> m_cass;
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_ioport_array<4> m_io_keyboard;

private:
	void pa_w(u8 data);
	void pb_w(u8 data);
	void mkit09_mem(address_map &map) ATTR_COLD;
};

class mkit09a_state : public mkit09_state
{
public:
	using mkit09_state::mkit09_state;

	void mkit09a(machine_config &config);

private:
	void pa_w(u8 data);
	void pb_w(u8 data);
	void mkit09a_mem(address_map &map) ATTR_COLD;
};


void mkit09_state::mkit09_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).mirror(0x1800).ram();
	map(0xa004, 0xa007).mirror(0x1ff8).rw(m_pia, FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
	map(0xe000, 0xe7ff).mirror(0x1800).rom().region("roms", 0);
}

void mkit09a_state::mkit09a_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).ram();
	map(0xe600, 0xe603).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xee00, 0xefff).ram();
	map(0xf000, 0xffff).rom().region("roms", 0);
}

/* Input ports */
static INPUT_PORTS_START( mkit09 )
	PORT_START("X0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Inc") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Dec") PORT_CODE(KEYCODE_DOWN) PORT_CHAR('V')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("MEM") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BP") PORT_CODE(KEYCODE_Q)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("REG") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GO") PORT_CODE(KEYCODE_G) PORT_CHAR('X')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("cnt") PORT_CODE(KEYCODE_W)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ofs") PORT_CODE(KEYCODE_O)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("L") PORT_CODE(KEYCODE_L)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("P") PORT_CODE(KEYCODE_P)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("X") PORT_CODE(KEYCODE_X)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("SPECIAL")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RST") PORT_CODE(KEYCODE_ESC) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mkit09_state::trigger_reset), 0)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("NMI") PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mkit09_state::trigger_nmi), 0)
INPUT_PORTS_END

// ToDo: work out what the keys marked "??" do.
static INPUT_PORTS_START( mkit09a )
	PORT_START("X0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Inc") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Dec") PORT_CODE(KEYCODE_DOWN) PORT_CHAR('V')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("MEM") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("L") PORT_CODE(KEYCODE_L) // ??
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("P") PORT_CODE(KEYCODE_P) // ??
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("cnt") PORT_CODE(KEYCODE_W) // ??
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ofs") PORT_CODE(KEYCODE_O) // ?? (same as G?)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BP") PORT_CODE(KEYCODE_Q) // ?? (same as X?)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("X3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("REG") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GO") PORT_CODE(KEYCODE_G) PORT_CHAR('X')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("X") PORT_CODE(KEYCODE_X) // ??
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("SPECIAL")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RST") PORT_CODE(KEYCODE_ESC) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mkit09_state::trigger_reset), 0)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("NMI") PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mkit09_state::trigger_nmi), 0)
INPUT_PORTS_END

INPUT_CHANGED_MEMBER( mkit09_state::trigger_reset )
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? CLEAR_LINE : ASSERT_LINE);
}

INPUT_CHANGED_MEMBER( mkit09_state::trigger_nmi )
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, newval ? CLEAR_LINE : ASSERT_LINE);
}


void mkit09_state::machine_reset()
{
	m_digit = 0;
}

void mkit09_state::machine_start()
{
	save_item(NAME(m_digit));
	save_item(NAME(m_seg));
}
// read keyboard
u8 mkit09_state::pa_r()
{
	if (m_digit < 4)
		return m_io_keyboard[m_digit]->read();

	return 0xff;
}

// read cassette
u8 mkit09_state::pb_r()
{
	return m_digit | ((m_cass->input() > +0.03) ? 0x80 : 0);
}

// write display segments
void mkit09_state::pa_w(u8 data)
{
	m_seg = bitswap<8>(~data, 7, 0, 5, 6, 4, 2, 1, 3);

	if ((m_digit > 3) && (m_digit < 10))
		m_display->matrix(1 << m_digit, m_seg);
}

void mkit09a_state::pa_w(u8 data)
{
	m_seg = data;

	if ((m_digit > 3) && (m_digit < 10))
		m_display->matrix(1 << (13-m_digit), m_seg);
}

// write cassette, select keyboard row, select a digit
void mkit09_state::pb_w(u8 data)
{
	m_cass->output(BIT(data, 6) ? -1.0 : +1.0);
	m_digit = data & 15;

	if ((m_digit > 3) && (m_digit < 10))
		m_display->matrix(1 << m_digit, m_seg);
}

void mkit09a_state::pb_w(u8 data)
{
	m_cass->output(BIT(data, 6) ? -1.0 : +1.0);
	m_digit = data & 15;

	if ((m_digit > 3) && (m_digit < 10))
		m_display->matrix(1 << (13-m_digit), m_seg);
}


void mkit09_state::mkit09(machine_config &config)
{
	/* basic machine hardware */
	MC6809(config, m_maincpu, XTAL(4'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &mkit09_state::mkit09_mem);

	/* video hardware */
	config.set_default_layout(layout_mkit09);
	PWM_DISPLAY(config, m_display).set_size(10, 8);
	m_display->set_segmask(0x3f0, 0xff);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	/* Devices */
	PIA6821(config, m_pia);
	m_pia->readpa_handler().set(FUNC(mkit09_state::pa_r));
	m_pia->readpb_handler().set(FUNC(mkit09_state::pb_r));
	m_pia->writepa_handler().set(FUNC(mkit09_state::pa_w));
	m_pia->writepb_handler().set(FUNC(mkit09_state::pb_w));
	m_pia->irqa_handler().set_inputline("maincpu", M6809_IRQ_LINE);
	m_pia->irqb_handler().set_inputline("maincpu", M6809_IRQ_LINE);

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
}

void mkit09a_state::mkit09a(machine_config &config)
{
	/* basic machine hardware */
	MC6809(config, m_maincpu, XTAL(4'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &mkit09a_state::mkit09a_mem);

	/* video hardware */
	config.set_default_layout(layout_mkit09);
	PWM_DISPLAY(config, m_display).set_size(10, 8);
	m_display->set_segmask(0x3f0, 0xff);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	/* Devices */
	PIA6821(config, m_pia);
	m_pia->readpa_handler().set(FUNC(mkit09a_state::pa_r));
	m_pia->readpb_handler().set(FUNC(mkit09a_state::pb_r));
	m_pia->writepa_handler().set(FUNC(mkit09a_state::pa_w));
	m_pia->writepb_handler().set(FUNC(mkit09a_state::pb_w));
	m_pia->cb2_handler().set_nop(); // stop errorlog filling up - is it a keyclick?
	m_pia->irqa_handler().set_inputline("maincpu", M6809_IRQ_LINE);
	m_pia->irqb_handler().set_inputline("maincpu", M6809_IRQ_LINE);

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
}

/* ROM definition */
ROM_START( mkit09 )
	ROM_REGION( 0x800, "roms", 0 )
	ROM_LOAD( "micromon.bin", 0x0000, 0x0800, CRC(c993c7c2) SHA1(2f54a2b423b925798f669f8a6d2cadeb8a82e968) )
ROM_END

ROM_START( mkit09a )
	ROM_REGION( 0x1000, "roms", 0 )
	ROM_LOAD( "ukit09like.bin", 0x0000, 0x1000, CRC(2cdb6a84) SHA1(edfc1dfc954bdba80c3df64abf4d7553343c1fae) )
	ROM_FILL(0x1d8,1,0x03) // fix data display
	ROM_FILL(0x99b,1,0x06) // fix start address
	ROM_FILL(0x99c,1,0x63) // ... so that MEM starts at 0000 instead of F908.
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY      FULLNAME                    FLAGS
COMP( 1983, mkit09,  0,      0,      mkit09,  mkit09,  mkit09_state,  empty_init, "Multitech", "Microkit09",               MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1983, mkit09a, mkit09, 0,      mkit09a, mkit09a, mkit09a_state, empty_init, "Multitech", "Microkit09 (Alt version)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



ml20.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    Digitek Micrologic 20

    Access control device with dot-matrix display

  ML20/232
  ____________________________________________________
  |   ________  ________  ________  ______    __      |
  |   |74HC04N|M74HC139B1 |__DP1__| |_XT1_| : CN7     |
  |__ _______________    ____________      JP2        |
  ||C||IC4 M27C512  ||   |D70320L-8  |       (CN4)__  |
  ||N||_____________||   |NEC V25    |   ______   |C| |
  ||2|_______________    |           |   |BATT |  |N| |
  ||_||KM681000CLP-7L|   |           |   |_____|  |6| |
  |__ |______________|   |___________|   o <- LED     |
  ||C| ________          ________       .. <- JP1     |
  ||N||_M6242B_|         MAX693CPE              __    |
  ||8|                   ________               |C|__ |
  |         XT2          ULN2003A               |N|| ||
  | :<-JP4       ________               ____    |3||_|<- LM2575T
  |    ________  SN74HC05N             TLP504A  |_|   |
  |__  MAX232CPE                :<-JP8                |
  || |            MEISEI         ________             |
  || | ________  P12  P12        MAX232CPE            |
  ||_| 26LS32ACN                                   __ |
  |   :: <-JP5                                     |F||
  |  ____________   ________  ________             |U||
  |  |___CN10____|  |_CN11__| |_______|   ________ |S||
  |  |___________|  |_____  | |_CN9___|   |_CN1___| E |
  |___________________________________________________|

 XT1 = 16.000 MHz
 XT2 = S873
 DP1 = 8 dipswitches
 CN4 = Speaker
 CN9 = RS232
 CN8 = Power Out
 CN6 = Magnetic stripe reader
 CN2 = Display (dot-matrix, 2 lines x 16 characters, 5x7 each character)
 CN7 = Keypad

 Display = Hyundai HC16203-A (Hitachi HD44780A00 based).

   Status:
   - Needs currently unimplemented V25 features (serial etc.)

***************************************************************************/

#include "emu.h"
#include "cpu/nec/v25.h"
#include "machine/msm6242.h"
#include "video/hd44780.h"
#include "sound/spkrdev.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "ml20.lh"


namespace {

class ml20_state : public driver_device
{
public:
	ml20_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_lcdc(*this, "lcdc"),
		m_speaker(*this, "speaker"),
		m_keys(*this, "COL.%d", 0U),
		m_dsw(*this, "DSW")
		{}

	void ml20(machine_config &config);

	void lcd_palette(palette_device &palette) const;
	HD44780_PIXEL_UPDATE(lcd_pixel_update);

protected:
	void machine_start() override ATTR_COLD;

private:
	required_device<v25_device> m_maincpu;
	required_device<hd44780_device> m_lcdc;
	required_device<speaker_sound_device> m_speaker;
	required_ioport_array<4> m_keys;
	required_ioport m_dsw;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	uint8_t p0_r();
	uint8_t p1_r();
	uint8_t p2_r();
	void p0_w(uint8_t data);
	void p1_w(uint8_t data);
	void p2_w(uint8_t data);
	uint8_t pt_r();

	uint8_t m_p0;
	uint8_t m_p1;
	uint8_t m_p2;
};

void ml20_state::mem_map(address_map &map)
{
	map(0x00000, 0x1ffff).ram();
	map(0xf0000, 0xfffff).rom().region("bios", 0);
}

void ml20_state::io_map(address_map &map)
{
	map(0x00, 0x0f).rw("rtc", FUNC(msm6242_device::read), FUNC(msm6242_device::write));
	map(0x10, 0x10).rw(m_lcdc, FUNC(hd44780_device::data_r), FUNC(hd44780_device::data_w));
	map(0x14, 0x14).rw(m_lcdc, FUNC(hd44780_device::control_r), FUNC(hd44780_device::control_w));
}

static INPUT_PORTS_START( ml20 )
	PORT_START("COL.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_NAME("ENTER")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)     PORT_NAME("0")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)   PORT_NAME("CLEAR")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)     PORT_NAME("ler. MARC")
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("COL.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_NAME("9")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_NAME("8")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_NAME("7")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_NAME("MENSAJ.")
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("COL.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_NAME("6")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_NAME("5")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_NAME("4")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_NAME("SALDOS")
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("COL.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_NAME("3")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_NAME("2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_NAME("1")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_NAME("ANULAR")
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("DSW")
	PORT_DIPUNKNOWN(0x01, 0x01)
	PORT_DIPUNKNOWN(0x02, 0x02)
	PORT_DIPUNKNOWN(0x04, 0x04)
	PORT_DIPUNKNOWN(0x08, 0x08)
	PORT_DIPUNKNOWN(0x10, 0x10)
	PORT_DIPUNKNOWN(0x20, 0x20)
	PORT_DIPUNKNOWN(0x40, 0x40)
	PORT_DIPUNKNOWN(0x80, 0x80)
INPUT_PORTS_END

void ml20_state::lcd_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148)); // background
	palette.set_pen_color(1, rgb_t( 92,  83,  88)); // lcd pixel on
	palette.set_pen_color(2, rgb_t(131, 136, 139)); // lcd pixel off
}

HD44780_PIXEL_UPDATE( ml20_state::lcd_pixel_update )
{
	// char size is 5x7
	if (x > 4 || y > 6)
		return;

	if (line < 2 && pos < 16)
		bitmap.pix(1 + y + line*8 + line, 1 + pos*6 + x) = state ? 1 : 2;
}

// 7654----  unknown (set to input)
// ----3210  keypad column

uint8_t ml20_state::p0_r()
{
	return m_p0;
}

void ml20_state::p0_w(uint8_t data)
{
	if (0)
		logerror("p0_w: %d %d %d %d  %d %d %d %d\n", BIT(data, 7), BIT(data, 6), BIT(data, 5), BIT(data, 4), BIT(data, 3), BIT(data, 2), BIT(data, 1), BIT(data, 0));

	m_p0 = data;
}

// 765-----  unknown (set to output)
// ---43210  unknown (set to input)

uint8_t ml20_state::p1_r()
{
	return m_p1;
}

void ml20_state::p1_w(uint8_t data)
{
	if (0)
		logerror("p1_w: %d %d %d %d  %d %d %d %d\n", BIT(data, 7), BIT(data, 6), BIT(data, 5), BIT(data, 4), BIT(data, 3), BIT(data, 2), BIT(data, 1), BIT(data, 0));

	m_p1 = data;
}

// 7-------  some kind of heartbeat? led?
// -6------  unknown (set to output)
// --543---  unknown (set to input)
// -----2--  set when waiting for keypad or other data?
// ------1-  toggles continously
// -------0  unknown (set to output)

uint8_t ml20_state::p2_r()
{
	return m_p2;
}

void ml20_state::p2_w(uint8_t data)
{
	if (0)
		logerror("p2_w: %d %d %d %d  %d %d %d %d\n", BIT(data, 7), BIT(data, 6), BIT(data, 5), BIT(data, 4), BIT(data, 3), BIT(data, 2), BIT(data, 1), BIT(data, 0));

	m_p2 = data;
}

uint8_t ml20_state::pt_r()
{
	uint8_t data = 0xff;

	if (BIT(m_p0, 0) == 0) data &= m_keys[0]->read();
	if (BIT(m_p0, 1) == 0) data &= m_keys[1]->read();
	if (BIT(m_p0, 2) == 0) data &= m_keys[2]->read();
	if (BIT(m_p0, 3) == 0) data &= m_keys[3]->read();

	return data;
}

void ml20_state::machine_start()
{
	// register for save states
	save_item(NAME(m_p0));
	save_item(NAME(m_p1));
	save_item(NAME(m_p2));
}

void ml20_state::ml20(machine_config &config)
{
	V25(config, m_maincpu, 16_MHz_XTAL / 2); // unknown clock
	m_maincpu->set_addrmap(AS_PROGRAM, &ml20_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &ml20_state::io_map);
	m_maincpu->p0_in_cb().set(FUNC(ml20_state::p0_r));
	m_maincpu->p0_out_cb().set(FUNC(ml20_state::p0_w));
	m_maincpu->p1_in_cb().set(FUNC(ml20_state::p1_r));
	m_maincpu->p1_out_cb().set(FUNC(ml20_state::p1_w));
	m_maincpu->p2_in_cb().set(FUNC(ml20_state::p2_r));
	m_maincpu->p2_out_cb().set(FUNC(ml20_state::p2_w));
	m_maincpu->pt_in_cb().set(FUNC(ml20_state::pt_r));

	MSM6242(config, "rtc", 32.768_kHz_XTAL);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // not accurate
	screen.set_size(6*16+1, 18);
	screen.set_visarea_full();
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(ml20_state::lcd_palette), 3);

	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 16);
	m_lcdc->set_pixel_update_cb(FUNC(ml20_state::lcd_pixel_update));

	config.set_default_layout(layout_ml20);

	// sound
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

ROM_START( ml20 )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD("ml-20_v1.27.ic4", 0x0000, 0x10000, CRC(844d6d23) SHA1(08cd290bc342da328abc91b0699662c9ba335c0d) )
ROM_END

} // anonymous namespace


//    YEAR  NAME  PARENT  COMPAT  MACHINE INPUT  CLASS       INIT        COMPANY    FULLNAME         FLAGS
COMP( 1999, ml20, 0,      0,      ml20,   ml20,  ml20_state, empty_init, "Digitek", "Micrologic 20", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE)



mm1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Mephisto MM I, the first H+G slide-in chesscomputer module

The module was included with either the Modular or Exclusive chessboards.
Initially, the module itself didn't have a name. It was only later in retrospect,
after the release of Modul MM II that it became known as the MM I. The program is
actually more like a prequel of III-S Glasgow, same chess engine authors too.

Hardware notes:
- PCB label: HGS 10 121 01
- CDP1806 @ 8MHz, 6.5V (IRQ from internal timer)
- 32KB ROM (2*D27128, or HN613256P)
- 4KB RAM (2*HM6116LP-3)
- CDP1853CE, CD4011BE, 3*40373BP, 4556BE
- Mephisto modular display module
- Mephisto modular chessboard
- 18-button keypad, beeper

It supports the HG 170 opening book module.
LCD module is assumed to be same as MM II and others.

Mephisto Mirage is on similar hardware, but it's a single module (LCD is included
on the main PCB). Like MM I, the module by itself didn't have a name at first.
The boards that were included with the product were either Mephisto Mirage
(button sensors, no leds), or Mephisto Mobil (no sensors, no leds). The module
also works on the more expensive wooden chessboards like Modular, Exclusive or
Muenchen, as long as it supports the higher voltage.

TODO:
- mmirage unknown_w
- mm1 unknown expansion rom at $c000?
- add mm1 STP/ON buttons? (they're off/on, game continues when ON again)

*******************************************************************************/

#include "emu.h"

#include "mmboard.h"
#include "mmdisplay1.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/cosmac/cosmac.h"
#include "sound/dac.h"

#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "mephisto_mm1.lh"
#include "mephisto_mirage.lh"


namespace {

class mm1_state : public driver_device
{
public:
	mm1_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(mirage_switch_sensor_type);

	// machine configs
	void mirage(machine_config &config);
	void mm1(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD { m_reset = true; }

private:
	// devices/pointers
	required_device<cdp1806_device> m_maincpu;
	required_device<mephisto_board_device> m_board;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<2> m_inputs;

	bool m_reset = false;
	u8 m_kp_mux = 0;

	// address maps
	void mirage_map(address_map &map) ATTR_COLD;
	void mm1_map(address_map &map) ATTR_COLD;
	void mm1_io(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_display();
	int clear_r();
	void sound_w(u8 data);
	void unknown_w(u8 data);
	void keypad_w(u8 data);
	template<int N> int keypad_r();
};

void mm1_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_reset));
	save_item(NAME(m_kp_mux));
}



/*******************************************************************************
    I/O
*******************************************************************************/

int mm1_state::clear_r()
{
	// CLEAR low + WAIT high resets cpu
	int ret = (m_reset) ? 0 : 1;
	m_reset = false;
	return ret;
}

void mm1_state::sound_w(u8 data)
{
	// d0: speaker out
	m_dac->write(~data & 1);
}

void mm1_state::unknown_w(u8 data)
{
	// mmirage: unused serial device?
}

void mm1_state::keypad_w(u8 data)
{
	// d0-d7: keypad input mux
	m_kp_mux = ~data;
}

template<int N>
int mm1_state::keypad_r()
{
	// EF3,EF4: multiplexed inputs (keypad)
	return (m_inputs[N]->read() & m_kp_mux) ? 1 : 0;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void mm1_state::mirage_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().nopw();
	map(0xd000, 0xdfff).mirror(0x2000).ram();
}

void mm1_state::mm1_map(address_map &map)
{
	mirage_map(map);
	map(0x8000, 0xbfff).r("cartslot", FUNC(generic_slot_device::read_rom)); // opening library
	map(0xc000, 0xc000).nopr(); // looks for $c0, jumps to $c003 if true
}

void mm1_state::mm1_io(address_map &map)
{
	map(0x01, 0x01).w(FUNC(mm1_state::sound_w));
	map(0x02, 0x02).w(FUNC(mm1_state::keypad_w));
	map(0x03, 0x03).r(m_board, FUNC(mephisto_board_device::input_r));
	map(0x04, 0x04).w(m_board, FUNC(mephisto_board_device::mux_w));
	map(0x05, 0x05).w(m_board, FUNC(mephisto_board_device::led_w));
	map(0x06, 0x06).w("display", FUNC(mephisto_display1_device::data_w));
	map(0x07, 0x07).w(FUNC(mm1_state::unknown_w));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( mm1 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("E / 5 / Rook")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("INFO")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Right / White / 0")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("POS")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("H / 8")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("LEV")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("G / 7 / King")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("MEM")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Left / Black / 9")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_NAME("CL")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("C / 3 / Knight")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("ENT")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("D / 4 / Bishop")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("A / 1")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("F / 6 / Queen")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("B / 2 / Pawn")

	PORT_START("PIECE")
	PORT_CONFNAME( 0x01, 0x00, "Piece Notation" )
	PORT_CONFSETTING(    0x01, DEF_STR( English ) ) // KQRBNP
	PORT_CONFSETTING(    0x00, DEF_STR( German ) ) // KDTLSB
INPUT_PORTS_END

static INPUT_PORTS_START( mirage )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_NAME("CL")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("A / 1")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("ENT")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("B / 2 / Pawn")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("STA")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("C / 3 / Knight")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("LEV")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("D / 4 / Bishop")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("LIST")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("E / 5 / Rook")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("Black / 9")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("F / 6 / Queen")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("White / 0")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("G / 7 / King")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("REV")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("H / 8")

	PORT_START("FAKE") // module came with buttons sensorboard by default
	PORT_CONFNAME( 0x01, 0x00, "Board Sensors" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mm1_state::mirage_switch_sensor_type), 0)
	PORT_CONFSETTING(    0x00, "Buttons (Mirage)" )
	PORT_CONFSETTING(    0x01, "Magnets (Modular)" )
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(mm1_state::mirage_switch_sensor_type)
{
	m_board->get()->set_type(newval ? sensorboard_device::MAGNETS : sensorboard_device::BUTTONS);
}



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void mm1_state::mirage(machine_config &config)
{
	// basic machine hardware
	CDP1806(config, m_maincpu, 8_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mm1_state::mirage_map);
	m_maincpu->set_addrmap(AS_IO, &mm1_state::mm1_io);
	m_maincpu->clear_cb().set(FUNC(mm1_state::clear_r));
	m_maincpu->q_cb().set("display", FUNC(mephisto_display1_device::common_w));
	m_maincpu->ef3_cb().set(FUNC(mm1_state::keypad_r<0>));
	m_maincpu->ef4_cb().set(FUNC(mm1_state::keypad_r<1>));

	MEPHISTO_BUTTONS_BOARD(config, m_board); // see mirage_switch_sensor_type
	m_board->set_delay(attotime::from_msec(200));

	// video hardware
	MEPHISTO_DISPLAY_MODULE1(config, "display");
	config.set_default_layout(layout_mephisto_mirage);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void mm1_state::mm1(machine_config &config)
{
	mirage(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &mm1_state::mm1_map);
	m_maincpu->ef1_cb().set_ioport("PIECE"); // hardwired

	MEPHISTO_SENSORS_BOARD(config.replace(), m_board);
	m_board->set_delay(attotime::from_msec(200));

	config.set_default_layout(layout_mephisto_mm1);

	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "mephisto_cart");
	SOFTWARE_LIST(config, "cart_list").set_original("mephisto_mm1");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( mm1 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("mm1b.bin", 0x0000, 0x8000, CRC(90bf840e) SHA1(cdec6b02c1352b2a00d66964989a17c2b81ec79e) ) // HN613256P
ROM_END

ROM_START( mm1a )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("114", 0x0000, 0x4000, CRC(208b4c43) SHA1(48f891d614fa643f47d099f94aff15a44c2efc07) ) // D27128
	ROM_LOAD("214", 0x4000, 0x4000, CRC(93734e49) SHA1(9ad6c191074c4122300f059e2ef9cfeff7b81463) ) // "
ROM_END


ROM_START( mmirage )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("g79", 0x0000, 0x8000, CRC(8cbaff40) SHA1(693086ae179f1ada4ac403b3a6bc7ea718b4e71e) ) // HN613256P, 2nd half empty
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS      INIT        COMPANY, FULLNAME, FLAGS
SYST( 1983, mm1,     0,      0,      mm1,     mm1,    mm1_state, empty_init, "Hegener + Glaser", "Mephisto MM I (ver. B)", MACHINE_SUPPORTS_SAVE )
SYST( 1983, mm1a,    mm1,    0,      mm1,     mm1,    mm1_state, empty_init, "Hegener + Glaser", "Mephisto MM I (ver. A)", MACHINE_SUPPORTS_SAVE )

SYST( 1984, mmirage, 0,      0,      mirage,  mirage, mm1_state, empty_init, "Hegener + Glaser", "Mephisto Mirage", MACHINE_SUPPORTS_SAVE )



mm2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Dirk Verwiebe, Cowering, hap
/*******************************************************************************

Mephisto MM II series chesscomputers
2007 Dirk V.

TODO:
- rebel5 unknown read from 0x4002, looks like leftover bookrom check
- need to emulate TurboKit properly as a slot device (it's not as simple as a CPU
  overclock), TK20 EPROM is dumped for the common version (6502 Mephisto/Fidelity/
  Novag/etc.) and for the SciSys Maestro/Analyst version
- correct rom labels (applies to the filenames with .bin extension)

================================================================================

For rebel5 and newer, the chess engine is by Ed Schröder. Older chesscomputers in
this driver were authored by Ulf Rathsman.

The MM II program was also licensed to Daimler-Benz, who gave away several custom
chesscomputers as a parting gift to retiring executives. The hardware is same as MM II.
see(1): http://chesseval.com/ChessEvalJournal/DaimlerBenz.htm
see(2): http://chesseval.com/RareBoard/DaimlerBenzBoard.htm

MM III was never released officially. Rebell 5,0 is commonly known as MM III, but the
real one (updated MM II engine) didn't get further than a prototype.

MM II (Nona program) wasn't a commercial release. After Mondial came out, Frans Morsch
ported his Nona program to MM II hardware, using Ed Schröder's interface (hence the
similarity with Rebel). According to research, this version competed in the 1985 Dutch
Open Computer Chess Championship.

MM IV TurboKit 18MHz - (mm4tk)
This is a replacement ROM combining the TurboKit initial ROM with the original MM IV.
The TurboKit powers up to its tiny ROM, copies itself to RAM, banks in normal ROM,
copies that to faster SRAM, then patches the checksum and the LED blink delays.

There is an undumped MM V TurboKit, which will be the exact same except for location
of the patches. The mm5tk just needs the normal mm5 ROM swapped out for that one to
blinks the LEDs a little slower.

Correction: The real TK20 TurboKit does not patch the ROM, so mm4tk (and a possible
mm5 version of this) is more likely a SteveUK hack.

MM IV (Rebel program) was never sold in this format, the chess engine is from Mephisto
Polgar. Ed Schröder participated with it at the 1989 WMCCC in Portorose, on an MM IV
module combined with the TK20 TurboKit. For more information, see:
http://chesseval.com/ChessEvalJournal/PrototypeMMV.htm (mistakenly claims it's MM V)

MM VI (Saitek, 1994) is on different hardware, H8 CPU.

================================================================================

MM IV + MM V hardware notes

Overview:
- CPU: R65C02P3/R65C02P4 or G65SC02P-4
- Clock: 4.9152 MHz
- NMI CLK: 600 Hz
- IRQ Line is set to VSS
- 8 KByte SRAM Sony CXK5864-15L

1-CD74HC4060E: 14 Bit Counter
1-CD74HC166E
1-CD74HC251E
1-SN74HC138N TI
1-SN74HC139N TI
1-74HC14AP Toshiba
1-74HC02AP Toshiba
1-74HC00AP Toshiba
1-CD74HC259E

LCD module:
PCB label HGS 10 122 01
1-CD4011
4-CD4015

$0000-$1fff S-RAM
$2000 LCD 4 Byte Shift Register writeonly right to left
every 2nd char xor'd by $FF

2c00-2c07 Keyboard (8to1 Multiplexer) 74HCT251
2*8 Matrix
Adr. 0x3407
==0 !=0
2c00 CL E5
2c01 POS F6
2c02 MEMO G7
2c03 INFO A1
2c04 LEV H8
2c05 ENT B2
2c06 >0 C3
2c07 <9 D4

$3400-$3407 LED 1-6, Buzzer, Keyboard select

$2400 // Chess Board
$2800 // Chess Board
$3000 // Chess Board

$4000-$7FFF Opening Module HG550
$8000-$FFFF ROM

*******************************************************************************/

#include "emu.h"

#include "mmboard.h"
#include "mmdisplay1.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/m6502/r65c02.h"
#include "machine/74259.h"
#include "sound/dac.h"

#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "mephisto_bup.lh"
#include "mephisto_mm2.lh"
#include "mephisto_mm5.lh"


namespace {

class mm2_state : public driver_device
{
public:
	mm2_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_outlatch(*this, "outlatch"),
		m_display(*this, "display"),
		m_keys(*this, "KEY.%u", 0),
		m_reset(*this, "RESET")
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(reset_button);

	void rebel5(machine_config &config);
	void mm4(machine_config &config);
	void mm4rebel(machine_config &config);
	void mm4tk(machine_config &config);
	void mm5(machine_config &config);
	void mm2(machine_config &config);
	void mm2nona(machine_config &config);
	void bup(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD { m_maincpu->set_input_line(0, CLEAR_LINE); }

private:
	required_device<cpu_device> m_maincpu;
	required_device<hc259_device> m_outlatch;
	required_device<mephisto_display1_device> m_display;
	required_ioport_array<2> m_keys;
	required_ioport m_reset;

	void bup_mem(address_map &map) ATTR_COLD;
	void mm2_mem(address_map &map) ATTR_COLD;
	void mm4_mem(address_map &map) ATTR_COLD;
	void mm4rebel_mem(address_map &map) ATTR_COLD;
	void rebel5_mem(address_map &map) ATTR_COLD;

	void lcd_irqack_w(u8 data);
	u8 keys_r(offs_t offset);
};



/*******************************************************************************
    I/O
*******************************************************************************/

INPUT_CHANGED_MEMBER(mm2_state::reset_button)
{
	// RES buttons in serial tied to CPU RESET
	if (m_reset->read() == 3)
	{
		m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
		machine_reset();
	}
}

void mm2_state::lcd_irqack_w(u8 data)
{
	m_display->data_w(data);

	// accessing here also clears irq
	m_maincpu->set_input_line(0, CLEAR_LINE);
}

u8 mm2_state::keys_r(offs_t offset)
{
	// lcd common is shared with keypad select
	return ~(BIT(m_keys[m_outlatch->q7_r()]->read(), offset) << 7);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void mm2_state::bup_mem(address_map &map)
{
	map(0x0000, 0x0fff).ram();
	map(0x1000, 0x1007).w("outlatch", FUNC(hc259_device::write_d7)).nopr();
	map(0x1800, 0x1807).r(FUNC(mm2_state::keys_r));
	map(0x2000, 0x2000).r("board", FUNC(mephisto_board_device::input_r));
	map(0x2800, 0x2800).w(FUNC(mm2_state::lcd_irqack_w));
	map(0x3000, 0x3000).w("board", FUNC(mephisto_board_device::led_w));
	map(0x3800, 0x3800).w("board", FUNC(mephisto_board_device::mux_w));
	map(0x8000, 0xffff).rom();
}

void mm2_state::mm2_mem(address_map &map)
{
	bup_mem(map);
	map(0x4000, 0x7fff).r("cartslot", FUNC(generic_slot_device::read_rom)); // opening library
}

void mm2_state::rebel5_mem(address_map &map)
{
	map(0x0000, 0x1fff).ram();
	map(0x2000, 0x2007).w("outlatch", FUNC(hc259_device::write_d7)).nopr();
	map(0x3000, 0x3007).r(FUNC(mm2_state::keys_r));
	map(0x4000, 0x4000).r("board", FUNC(mephisto_board_device::input_r));
	map(0x5000, 0x5000).w(FUNC(mm2_state::lcd_irqack_w));
	map(0x6000, 0x6000).w("board", FUNC(mephisto_board_device::led_w));
	map(0x7000, 0x7000).w("board", FUNC(mephisto_board_device::mux_w));
	map(0x8000, 0xffff).rom();
}

void mm2_state::mm4rebel_mem(address_map &map)
{
	map(0x0000, 0x1fff).ram();
	map(0x2000, 0x2000).mirror(0x03ff).w(m_display, FUNC(mephisto_display1_device::data_w));
	map(0x2400, 0x2400).mirror(0x03ff).w("board", FUNC(mephisto_board_device::led_w)).nopr();
	map(0x2800, 0x2800).mirror(0x03ff).w("board", FUNC(mephisto_board_device::mux_w));
	map(0x2c00, 0x2c07).mirror(0x03f8).r(FUNC(mm2_state::keys_r));
	map(0x3000, 0x3000).mirror(0x03ff).r("board", FUNC(mephisto_board_device::input_r));
	map(0x3400, 0x3407).mirror(0x03f8).w("outlatch", FUNC(hc259_device::write_d7)).nopr();
	map(0x3800, 0x3800).mirror(0x03ff).nopw(); // N/C
	map(0x4000, 0xffff).rom();
}

void mm2_state::mm4_mem(address_map &map)
{
	mm4rebel_mem(map);
	map(0x4000, 0x7fff).r("cartslot", FUNC(generic_slot_device::read_rom));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( mm2 )
	PORT_START("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("CL") PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("POS") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("MEM") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("INFO") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("LEV") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("ENT") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Right / White / 0") PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Left / Black / 9") PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_LEFT)

	PORT_START("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("E / 5 / Queen") PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("F / 6 / King") PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("G / 7") PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("A / 1 / Pawn") PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("H / 8") PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("B / 2 / Knight") PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("C / 3 / Bishop") PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("D / 4 / Rook") PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("RES 1") PORT_CODE(KEYCODE_Z) PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mm2_state::reset_button), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("RES 2") PORT_CODE(KEYCODE_X) PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mm2_state::reset_button), 0)
INPUT_PORTS_END

static INPUT_PORTS_START( bup )
	PORT_INCLUDE( mm2 )

	PORT_MODIFY("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("1 / Pawn") PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("2 / Knight") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("3 / Bishop") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("4 / Rook") PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("5 / Queen") PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("6 / King") PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("White") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Black") PORT_CODE(KEYCODE_B)

	PORT_MODIFY("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("CL") PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("ENT") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("BEST") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("INFO") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("MON") PORT_CODE(KEYCODE_N)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("POS") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("LEV") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("MEM") PORT_CODE(KEYCODE_M)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void mm2_state::rebel5(machine_config &config)
{
	// basic machine hardware
	R65C02(config, m_maincpu, 9.8304_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &mm2_state::rebel5_mem);

	const attotime irq_period = attotime::from_hz(9.8304_MHz_XTAL / 2 / 0x2000); // 600Hz
	m_maincpu->set_periodic_int(FUNC(mm2_state::irq0_line_assert), irq_period);

	HC259(config, m_outlatch);
	m_outlatch->q_out_cb<0>().set_output("led100");
	m_outlatch->q_out_cb<1>().set_output("led101");
	m_outlatch->q_out_cb<2>().set_output("led102");
	m_outlatch->q_out_cb<3>().set_output("led103");
	m_outlatch->q_out_cb<4>().set_output("led104");
	m_outlatch->q_out_cb<5>().set_output("led105");
	m_outlatch->q_out_cb<6>().set("dac", FUNC(dac_1bit_device::write));
	m_outlatch->q_out_cb<7>().set(m_display, FUNC(mephisto_display1_device::common_w));

	MEPHISTO_SENSORS_BOARD(config, "board");
	MEPHISTO_DISPLAY_MODULE1(config, m_display);
	config.set_default_layout(layout_mephisto_mm2);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, "dac").add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void mm2_state::mm4rebel(machine_config &config)
{
	rebel5(config);

	// basic machine hardware
	m_maincpu->set_clock(4.9152_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mm2_state::mm4rebel_mem);

	const attotime nmi_period = attotime::from_hz(4.9152_MHz_XTAL / 0x2000); // 600Hz
	m_maincpu->set_periodic_int(FUNC(mm2_state::nmi_line_pulse), nmi_period);
}

void mm2_state::mm4(machine_config &config)
{
	mm4rebel(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &mm2_state::mm4_mem);

	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "mephisto_cart");
	SOFTWARE_LIST(config, "cart_list").set_original("mephisto_mm4");
}

void mm2_state::mm4tk(machine_config &config)
{
	mm4(config);
	m_maincpu->set_clock(36_MHz_XTAL / 2);
}

void mm2_state::mm5(machine_config &config)
{
	mm4(config);
	SOFTWARE_LIST(config.replace(), "cart_list").set_original("mephisto_mm5");

	config.set_default_layout(layout_mephisto_mm5);
}

void mm2_state::bup(machine_config &config)
{
	rebel5(config);

	// basic machine hardware
	m_maincpu->set_clock(7.3728_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &mm2_state::bup_mem);

	const attotime irq_period = attotime::from_hz(7.3728_MHz_XTAL / 2 / 0x2000); // 450Hz from 4020 Q13
	m_maincpu->set_periodic_int(FUNC(mm2_state::irq0_line_assert), irq_period);

	config.set_default_layout(layout_mephisto_bup);
}

void mm2_state::mm2(machine_config &config)
{
	bup(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &mm2_state::mm2_mem);

	config.set_default_layout(layout_mephisto_mm2);

	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "mephisto_cart");
	SOFTWARE_LIST(config, "cart_list").set_original("mephisto_mm2");
}

void mm2_state::mm2nona(machine_config &config)
{
	bup(config);

	config.set_default_layout(layout_mephisto_mm2);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( bup )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("bup_1.bin", 0x8000, 0x4000, CRC(e1e9625a) SHA1(8a757e28b7afca2a092f8ff419087e06b07b743e) )
	ROM_LOAD("bup_2.bin", 0xc000, 0x4000, CRC(6db30b80) SHA1(df4b379c4e916dff6b4110ec9c3591a9620c3424) )
ROM_END

ROM_START( bupa )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("bupa_1.bin", 0x8000, 0x4000, CRC(e1e9625a) SHA1(8a757e28b7afca2a092f8ff419087e06b07b743e) )
	ROM_LOAD("bupa_2.bin", 0xc000, 0x4000, CRC(708338ea) SHA1(d617c4aa2161865a22b4b0646ba793f8a1fda863) )
ROM_END


ROM_START( mm2 ) // 10 Sep 1986
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("400", 0x8000, 0x8000, CRC(e8c1f431) SHA1(c32dfa66eefbf3e539438d2fe6e6916f78a128be) ) // HN27C256G-20
	// 2-EPROM version also exists: CRC32 e9adcb8f & d40cbfc2
ROM_END

ROM_START( mm2a )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("300", 0x8000, 0x8000, CRC(60c777d4) SHA1(a77d678be60094073275558b4e8f0d34b43dd9ae) ) // D27C256D-20
	// 2-EPROM version also exists: CRC32 86a5a14f & a122f2c0
ROM_END

ROM_START( mm2b ) // 21 Apr 1986
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("200", 0x8000, 0x8000, CRC(9b69aaab) SHA1(98ee4879eef4d8b06553290f16ca661cf4181af8) )
	// 2-EPROM version also exists: CRC32 09cf6228 & 86d77724, ROM labels 8-b_21.4 & c-f_21.4
ROM_END

ROM_START( mm2c ) // serial 05780xx
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("hg86", 0x8000, 0x4000, CRC(e26d1f17) SHA1(3227bb1f8f22dd0d902a9a8be3c508b45c57d6cc) )
	ROM_LOAD("50",   0xc000, 0x4000, CRC(86d77724) SHA1(e46c59e87465a9a1784fbaf4743649b2c10006e0) )
ROM_END

ROM_START( mm2d ) // serial 05650xx
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("mm2d_1.bin", 0x8000, 0x4000, CRC(b91dab77) SHA1(67762304afe51fb8f1eb91259567b2451bf9bbfd) )
	ROM_LOAD("mm2d_2.bin", 0xc000, 0x4000, CRC(01143cc1) SHA1(f78474b410dbecb209aa23ef81e9f894e8b54942) )
ROM_END

ROM_START( mm2e ) // 13 Sep 1985, serial 05569xx
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("hg_8b_13.9", 0x8000, 0x4000, CRC(e2daac82) SHA1(c9fa59ca92362f8ee770733073bfa2ab8c7904ad) )
	ROM_LOAD("c-f_6.9",    0xc000, 0x4000, CRC(5e296939) SHA1(badd2a377259cf738cd076d8fb245c3dc284c24d) )
ROM_END


ROM_START( mm2nona )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("l7", 0x8000, 0x4000, CRC(9a45e1d4) SHA1(26e4c9cd1afe9aea0e8cfc25bdc9138bd99d5992) )
	ROM_LOAD("r7", 0xc000, 0x4000, CRC(2285af5e) SHA1(bea22e32b65eea5fa7617d9d1b3a824a7affe678) )
ROM_END


ROM_START( rebel5 ) // 8 Feb 1987?
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("rebel5.bin", 0x8000, 0x8000, CRC(17232752) SHA1(3cd6893c0071f3dc02785bf99f1950eed81eba39) )
ROM_END

ROM_START( rebel5a ) // 5 Dec 1986
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("rebell_5.12.86", 0x8000, 0x8000, CRC(8d02e1ef) SHA1(9972c75936613bd68cfd3fe62bd222e90e8b1083) )
ROM_END

ROM_START( rebel5b ) // 18 Aug 1986
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("reb_18.8.86", 0x8000, 0x8000, CRC(c8c95e81) SHA1(0fb83ade11d2a2a74c94d7bd6f71130ebbc77497) )
ROM_END


ROM_START( mm4 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("710", 0x8000, 0x8000, CRC(f68a4124) SHA1(d1d03a9aacc291d5cb720d2ee2a209eeba13a36c) )
ROM_END

ROM_START( mm4a )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("700", 0x8000, 0x8000, CRC(c97da840) SHA1(10bd2a391338ed2e417b35dcb6396ab4a4e360f0) )
ROM_END

ROM_START( mm4b )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("600", 0x8000, 0x8000, CRC(837d49b9) SHA1(9fb2dfaaeca2559ce582211137635c069180e95f) )
ROM_END

ROM_START( mm4tk ) // hack of 710
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("mm4tk.bin", 0x8000, 0x8000, CRC(51cb36a4) SHA1(9e184b4e85bb721e794b88d8657ae8d2ff5a24af) )
ROM_END


ROM_START( mm4rebel )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("rebel_w.m.89_24_8_89", 0x0000, 0x10000, CRC(db630dcd) SHA1(92bcdd5ed7a27ea8331b810e8e81b2cc29abfce1) ) // Atmel 27C512-15DC
ROM_END


ROM_START( mm5 ) // v5.1 (MEM->INFO to see version number)
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("mm5.bin", 0x8000, 0x8000, CRC(89c3d9d2) SHA1(77cd6f8eeb03c713249db140d2541e3264328048) )
ROM_END

ROM_START( mm5a ) // v5.0
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("mm5a.bin", 0x8000, 0x8000, CRC(fcfa7e6e) SHA1(afeac3a8c957ba58cefaa27b11df974f6f2066da) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT  CLASS      INIT        COMPANY, FULLNAME, FLAGS
SYST( 1985, bup,      0,      0,      bup,      bup,   mm2_state, empty_init, "Hegener + Glaser", u8"Mephisto Blitz- und Problemlösungs-Modul (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1985, bupa,     bup,    0,      bup,      bup,   mm2_state, empty_init, "Hegener + Glaser", u8"Mephisto Blitz- und Problemlösungs-Modul (set 2)", MACHINE_SUPPORTS_SAVE )

SYST( 1985, mm2,      0,      0,      mm2,      mm2,   mm2_state, empty_init, "Hegener + Glaser", "Mephisto MM II (set 1, v4.00)", MACHINE_SUPPORTS_SAVE )
SYST( 1985, mm2a,     mm2,    0,      mm2,      mm2,   mm2_state, empty_init, "Hegener + Glaser", "Mephisto MM II (set 2, v3.00)", MACHINE_SUPPORTS_SAVE )
SYST( 1985, mm2b,     mm2,    0,      mm2,      mm2,   mm2_state, empty_init, "Hegener + Glaser", "Mephisto MM II (set 3, v2.00)", MACHINE_SUPPORTS_SAVE )
SYST( 1985, mm2c,     mm2,    0,      mm2,      mm2,   mm2_state, empty_init, "Hegener + Glaser", "Mephisto MM II (set 4)", MACHINE_SUPPORTS_SAVE )
SYST( 1985, mm2d,     mm2,    0,      mm2,      mm2,   mm2_state, empty_init, "Hegener + Glaser", "Mephisto MM II (set 5)", MACHINE_SUPPORTS_SAVE )
SYST( 1985, mm2e,     mm2,    0,      mm2,      mm2,   mm2_state, empty_init, "Hegener + Glaser", "Mephisto MM II (set 6)", MACHINE_SUPPORTS_SAVE )

SYST( 1985, mm2nona,  0,      0,      mm2nona,  mm2,   mm2_state, empty_init, "Hegener + Glaser", "Mephisto MM II (Nona program, DOCCC 1985 Leiden TM)", MACHINE_SUPPORTS_SAVE )

SYST( 1986, rebel5,   0,      0,      rebel5,   mm2,   mm2_state, empty_init, "Hegener + Glaser", "Mephisto Rebell 5,0 (set 1)", MACHINE_SUPPORTS_SAVE ) // aka MM III
SYST( 1986, rebel5a,  rebel5, 0,      rebel5,   mm2,   mm2_state, empty_init, "Hegener + Glaser", "Mephisto Rebell 5,0 (set 2)", MACHINE_SUPPORTS_SAVE ) // "
SYST( 1986, rebel5b,  rebel5, 0,      rebel5,   mm2,   mm2_state, empty_init, "Hegener + Glaser", "Mephisto Rebell 5,0 (set 3)", MACHINE_SUPPORTS_SAVE ) // "

SYST( 1987, mm4,      0,      0,      mm4,      mm2,   mm2_state, empty_init, "Hegener + Glaser", "Mephisto MM IV (v7.10)", MACHINE_SUPPORTS_SAVE )
SYST( 1987, mm4a,     mm4,    0,      mm4,      mm2,   mm2_state, empty_init, "Hegener + Glaser", "Mephisto MM IV (v7.00)", MACHINE_SUPPORTS_SAVE )
SYST( 1987, mm4b,     mm4,    0,      mm4,      mm2,   mm2_state, empty_init, "Hegener + Glaser", "Mephisto MM IV (v6.00)", MACHINE_SUPPORTS_SAVE )
SYST( 1987, mm4tk,    mm4,    0,      mm4tk,    mm2,   mm2_state, empty_init, "hack",             "Mephisto MM IV (TurboKit)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_TIMING )

SYST( 1989, mm4rebel, 0,      0,      mm4rebel, mm2,   mm2_state, empty_init, "Hegener + Glaser", "Mephisto MM IV (Rebel program, WMCCC 1989 Portorose TM)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_TIMING )

SYST( 1990, mm5,      0,      0,      mm5,      mm2,   mm2_state, empty_init, "Hegener + Glaser", "Mephisto MM V (v5.1)", MACHINE_SUPPORTS_SAVE )
SYST( 1990, mm5a,     mm5,    0,      mm5,      mm2,   mm2_state, empty_init, "Hegener + Glaser", "Mephisto MM V (v5.0)", MACHINE_SUPPORTS_SAVE )



mmd1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************

MMD-1 driver by Miodrag Milanovic

2009-05-12 Initial version


 It appears that you enter an 3 digit octal number and then hit a function key.
 H - puts the number in the H register
 L - puts the number in the L register
 S - puts the number into memory pointed to by HL and then increments HL.
 G - Loads the program counter with the contents of HL

1) There is a 'working byte' which you can enter using the octal digits
(just press them in order), and which is displayed on the port 2 LEDs
when KEX is running.

2) 'R' is a hardware reset

3) 'H' and 'L' are used to load the address (high and low parts, and it
really is the HL register of the 8080). So to enter a particular address,
you type in the high half (in octal), press H. Then type in the low half
and press L. The address is displayed on the port 0 and port 1 LEDs when
KEX is running.

4) 'S' is 'Step' or 'Store'. It stores the working byte at the current
address (in HL), and then increments the address. It's used to enter
bytes into memory

5) 'G' is 'go'. It loads the 8080 PC with the address in HL, and thus
executes a program at that address.

OK, this is what I would try.

1) Press 'R' to reset the 8080 and start KEX running.

2) Type 004 H 000 L  to load the start address of your program. The bytes
should appear on the rightmost 8 LEDs as you enter them and should then
also appear on the left and middle sets of LEDs when you press H and L.

3) Enter the program

076 S 123 S 323 S 000 S 166S

As you type each byte it should appear on the rightmost LEDs. When you
press S, the address on the rest of the LEDs should increment by 1.

4) Re-enter the start address
004 H 000 L

5) Press G to run the program. The left most LEDs should change to
.*.*..** (. = off, * = on), I think. The keys will then do nothing (as
the CPU is halted) until you press R again to re-run KEX.


Cassette:
- The only info available is that an unknown UART is used on ports 12/13.
- Since MMD2 uses Kansas City format, I've used it here too. As there are
  no setup bytes, the UART is assumed to be the AY-3-1015 or equivalent.
  The result works perfectly.
- To save: 001H 025L G (start recording) press 0-7 to indicate number of
           blocks to save (0 = 8 blocks). The start address is always 1800.
           When it's finished, control will return.
- To load: 001H 000L (start playing), press G. When it's finished (cassette
           sound stops), press R. It always loads to 1800-up.

ToDo:
- tty uart ports 0x10/11, and rs232 interface
- Need software

****************************************************************************/

#include "emu.h"

#include "cpu/i8085/i8085.h"
#include "imagedev/cassette.h"
#include "machine/ay31015.h"
#include "machine/clock.h"
#include "machine/timer.h"

#include "speaker.h"

#include "mmd1.lh"


namespace {

class mmd1_state : public driver_device
{
public:
	mmd1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cass(*this, "cassette")
		, m_uart(*this, "uart")
		, m_lines(*this, "LINE%u", 1U)
		, m_digits(*this, "digit%u", 0U)
		, m_p(*this, "p%u_%u", 0U, 0U)
	{ }

	void mmd1(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(reset_button);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void round_leds_w(offs_t offset, u8 data);
	u8 keyboard_r();
	u8 port13_r();
	int si();
	void so(int state);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);
	void kansas_w(int state);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	u8 m_cass_data[4]{};
	bool m_cassinbit = 0, m_cassoutbit = 0, m_cassold = 0;
	u8 m_return_code = 0U;

	required_device<i8080_cpu_device> m_maincpu;
	required_device<cassette_image_device> m_cass;
	required_device<ay31015_device> m_uart;
	required_ioport_array<2> m_lines;
	output_finder<9> m_digits;
	output_finder<3, 8> m_p;
};


void mmd1_state::round_leds_w(offs_t offset, u8 data)
{
	for (u8 i = 0; i < 8; i++)
		m_p[offset][i] = BIT(data, i) ? 0 : 1;
}


// keyboard has a keydown and a keyup code. Keyup = last keydown + bit 7 set
u8 mmd1_state::keyboard_r()
{
	const u8 line1 = m_lines[0]->read();
	const u8 line2 = m_lines[1]->read();
	u8 data = 0xff;

	for (unsigned i = 0; i < 8; i++)
	{
		if (!BIT(line1, i))
			data = i;
	}

	for (unsigned i = 0; i < 8; i++)
	{
		if (!BIT(line2, i))
			data = i + 8;
	}

	if (data < 0x10)
	{
		m_return_code = data | 0x80;
		return data;
	}
	else
		return m_return_code;
}

u8 mmd1_state::port13_r()
{
	u8 data = 0xfa;
	data |= m_uart->dav_r() ? 1 : 0;
	data |= m_uart->tbmt_r() ? 4 : 0;
	return data;
}

int mmd1_state::si()
{
	return m_cassinbit;
}

void mmd1_state::so(int state)
{
	m_cassoutbit = state;
}

void mmd1_state::kansas_w(int state)
{
	if ((m_cass->get_state() & CASSETTE_MASK_UISTATE) == CASSETTE_RECORD)
	{
		// incoming @4800Hz
		u8 twobit = m_cass_data[3] & 15;

		if (state)
		{
			if (twobit == 0)
				m_cassold = m_cassoutbit;

			if (m_cassold)
				m_cass->output(BIT(m_cass_data[3], 0) ? -1.0 : +1.0); // 2400Hz
			else
				m_cass->output(BIT(m_cass_data[3], 1) ? -1.0 : +1.0); // 1200Hz

			m_cass_data[3]++;
		}
	}

	m_uart->write_tcp(state);
	m_uart->write_rcp(state);
}

TIMER_DEVICE_CALLBACK_MEMBER( mmd1_state::kansas_r )
{
	// no tape - set to idle
	m_cass_data[1]++;
	if (m_cass_data[1] > 48)
	{
		m_cass_data[1] = 48;
		m_cassinbit = 1;
	}

	if ((m_cass->get_state() & CASSETTE_MASK_UISTATE) != CASSETTE_PLAY)
		return;

	/* cassette - turn 1200/2400Hz to a bit */
	u8 cass_ws = (m_cass->input() > +0.04) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		m_cassinbit = (m_cass_data[1] < 12) ? 1 : 0;
		m_cass_data[1] = 0;
	}
}

void mmd1_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).rom(); // Main ROM
	map(0x0100, 0x01ff).rom(); // Expansion slot
	map(0x0200, 0x02ff).ram();
	map(0x0300, 0x03ff).ram();
	map(0x1800, 0x1fff).ram(); // Area that can be accessed by cassette
}

void mmd1_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x02).w(FUNC(mmd1_state::round_leds_w));
	map(0x00, 0x00).r(FUNC(mmd1_state::keyboard_r));
	//map(0x10, 0x11).rw  TTY UART
	map(0x12, 0x12).rw(m_uart, FUNC(ay51013_device::receive), FUNC(ay51013_device::transmit));
	map(0x13, 0x13).r(FUNC(mmd1_state::port13_r));
}


/* Input ports */
static INPUT_PORTS_START( mmd1 )
	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_LALT) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mmd1_state::reset_button), 0)
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(mmd1_state::reset_button)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}

void mmd1_state::machine_start()
{
	m_digits.resolve();
	m_p.resolve();
	save_item(NAME(m_cass_data));
	save_item(NAME(m_cassinbit));
	save_item(NAME(m_cassoutbit));
	save_item(NAME(m_cassold));
	save_item(NAME(m_return_code));
}

void mmd1_state::machine_reset()
{
	m_return_code = 0xff;
	// setup uart to 8N2
	m_uart->write_np(1);
	m_uart->write_tsb(1);
	m_uart->write_nb1(1);
	m_uart->write_nb2(1);
	m_uart->write_eps(1);
	m_uart->write_cs(1);
	m_uart->write_cs(0);
}

void mmd1_state::mmd1(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, 6750000 / 9);
	m_maincpu->set_addrmap(AS_PROGRAM, &mmd1_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &mmd1_state::io_map);

	/* video hardware */
	config.set_default_layout(layout_mmd1);

	AY51013(config, m_uart);
	m_uart->read_si_callback().set(FUNC(mmd1_state::si));
	m_uart->write_so_callback().set(FUNC(mmd1_state::so));
	m_uart->set_auto_rdav(true);

	clock_device &uart_clock(CLOCK(config, "uart_clock", 4800));
	uart_clock.signal_handler().set(FUNC(mmd1_state::kansas_w));
	TIMER(config, "kansas_r").configure_periodic(FUNC(mmd1_state::kansas_r), attotime::from_hz(40000));

	// cassette is connected to the uart
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	SPEAKER(config, "mono").front_center();
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
}

/* ROM definition */
ROM_START( mmd1 )
	ROM_REGION( 0x0200, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "kex.ic15",    0x0000, 0x0100, CRC(434f6923) SHA1(a2af60deda54c8d3f175b894b47ff554eb37e9cb))
	ROM_LOAD( "prom1.ic16",  0x0100, 0x0100, BAD_DUMP CRC(d23a6ac3) SHA1(469d981b635058dd23e843a3efc555316f87ece4) )     // Typed in by hand from the manual
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY                FULLNAME  FLAGS
COMP( 1976, mmd1,  0,      0,      mmd1,    mmd1,  mmd1_state, empty_init, "E&L Instruments Inc", "MMD-1 Mini-Micro Designer",  MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



mmd2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************

MMD-2 driver by Miodrag Milanovic

2009-05-12 Initial version
2011-01-12 MMD2 working [Robbbert]


http://www.cs.unc.edu/~yakowenk/classiccmp/mmd2/
Memory map:

    * 4K RAM addresses $0000..$0FFF
    * ROM addresses $D800..$E7FF
    * 256 bytes of RAM, ($FC00..$FCFF?)

DIP switches:

    * WE 0 - Write enable for addresses $0000..$03FF
    * WE 1 - Write enable for addresses $0400..$07FF
    * WE 2 - Write enable for addresses $0800..$0BFF
    * WE 3 - Write enable for addresses $0C00..$0FFF
    * SPARE - ???
    * HEX OCT - choose display and entry to be in Hexadecimal or Octal
    * PUP RESET - ???
    * EXEC USER - update binary LED's with data entry? Or not?
      (in either setting, outputs to ports 0,1,2 still show)

Operation:

    * Enter bytes on the keypad of hex digits
    * Set MSByte of address by entering it on the keypad & pressing "HIGH".
    * ... LSByte ... "LOW"
    * Change contents of memory at the selected address by entering the new value & pressing "STORE".
    * Look at adjacent memory locations with "NEXT" and "PREV".
    * Execute the program at the selected address by pressing "GO".

AUX functions:

    * BRL HI # - OFF disables BRL LO
    * BRL LO #
    * STEP #
    * SRC HI # - source for COPY/DUMP - OFF disables "DUMP" function
    * DES HI # - destination for COPY/DUMP
    * LEN HI # - length for COPY/DUMP
    * CLR TST ON - test if PROM is empty
    * POP PRM ON - program a PROM
    * DUP TST ON - test if PROM duplicated okay
    * PROM 2708/2716
    * MEM MAP RAM/ROM
    * BAUD 110/150/300/600/1200

The memory map can be rearranged by the system by using IN5, IN6, IN7.
A pair of undumped proms control what goes where on each map.
Each set of ROMs also has its own pair of PROMs.


I/O ports:
IN0: user expansion
IN1: 0-TTYIN, 1-CASSIN, 3-SW8(binary/ports), 4-SW7(reset/pup), 5-SW6(hex/oct), 6-(pup signal)
IN3: 8279 status
IN4: 8279 key
IN5: set MAP1
IN6: set MAP2
IN7: set MAP3
IN8: read eprom (in the eprom programmer)
OUT0: PORT0 LEDs
OUT1: PORT1 LEDs
OUT2: PORT2 LEDs
OUT3: 8279 control
OUT4: 8279 7-segment LED data
OUT5: TTYOUT, CASSOUT
OUT9: turn pup signal off
OUTA: programming pulse on/off (eprom programmer)

Dips:
SW1-4 hardware control of RAM being writable
SW5 not used
SW6 Control if the 7-segment displays show Octal (low) or Hex (high).
SW7 Reset only causes the cpu to perform a warm start. PUP does a cold start (Power-UP).
SW8 Control if the PORT displays echo the 7-segment displays (high), or just act as normal output ports (low).


ToDo
- Hook up WE0-3
- tty rs232 interface
- Add interrupt module (INTE LED is always on atm)
- Need software
- Probably lots of other things

****************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8279.h"
#include "imagedev/cassette.h"
#include "speaker.h"
#include "mmd2.lh"


namespace {

class mmd2_state : public driver_device
{
public:
	mmd2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cass(*this, "cassette")
		, m_banks(*this, "bank%u", 1U)
		, m_io_keyboard(*this, "X%u", 0U)
		, m_io_dsw(*this, "DSW")
		, m_digits(*this, "digit%u", 0U)
		, m_p(*this, "p%u_%u", 0U, 0U)
		, m_led_halt(*this, "led_halt")
		, m_led_hold(*this, "led_hold")
	{ }

	void mmd2(machine_config &config);

	void init_mmd2();

	DECLARE_INPUT_CHANGED_MEMBER(reset_button);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void round_leds_w(offs_t, u8);
	void port05_w(u8 data);
	u8 port01_r();
	u8 port13_r();
	u8 bank_r(address_space &space, offs_t offset);
	u8 keyboard_r();
	void scanlines_w(u8 data);
	void digit_w(u8 data);
	void status_callback(u8 data);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	void reset_banks();

	u8 m_digit = 0U;
	std::unique_ptr<u8[]> m_ram;
	required_device<i8080_cpu_device> m_maincpu;
	required_device<cassette_image_device> m_cass;
	required_memory_bank_array<8> m_banks;
	required_ioport_array<4> m_io_keyboard;
	required_ioport m_io_dsw;
	output_finder<9> m_digits;
	output_finder<3, 8> m_p;
	output_finder<> m_led_halt;
	output_finder<> m_led_hold;
};


void mmd2_state::round_leds_w(offs_t offset, u8 data)
{
	for (u8 i = 0; i < 8; i++)
		m_p[offset][i] = BIT(~data, i);
}

void mmd2_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x03ff).bankr("bank1").bankw("bank2");
	map(0x0400, 0x0fff).bankr("bank3").bankw("bank4");
	map(0xd800, 0xe3ff).bankr("bank5").bankw("bank6");
	map(0xe400, 0xe7ff).bankr("bank7").bankw("bank8");
	map(0xfc00, 0xfcff).ram();
}

void mmd2_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x02).w(FUNC(mmd2_state::round_leds_w));
	map(0x01, 0x01).r(FUNC(mmd2_state::port01_r));
	map(0x03, 0x03).rw("i8279", FUNC(i8279_device::status_r), FUNC(i8279_device::cmd_w));
	map(0x04, 0x04).rw("i8279", FUNC(i8279_device::data_r), FUNC(i8279_device::data_w));
	map(0x05, 0x07).r(FUNC(mmd2_state::bank_r));
	map(0x05, 0x05).w(FUNC(mmd2_state::port05_w));
	//map(0x09, 0x09).w  PUP signal
	//map(0x0a, 0x0a).w  Eprom programmer
}


/* Input ports */
static INPUT_PORTS_START( mmd2 )
	PORT_START("DSW")
	PORT_DIPNAME( 0x20, 0x00, "Sw6") PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(    0x20, "Hex")
	PORT_DIPSETTING(    0x00, "Octal")
	PORT_DIPNAME( 0x10, 0x10, "Sw7") PORT_DIPLOCATION("SW1:2")
	PORT_DIPSETTING(    0x10, "PUP")
	PORT_DIPSETTING(    0x00, "Reset")
	PORT_DIPNAME( 0x08, 0x08, "Sw8") PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(    0x08, "Exec")
	PORT_DIPSETTING(    0x00, "User")

	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CANCEL") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("AUX") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("REGS") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MEM") PORT_CODE(KEYCODE_MINUS)
	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("COPY") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PROM") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DUMP") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LOAD") PORT_CODE(KEYCODE_O)
	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("OPTION") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("GO") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LOW") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("HIGH") PORT_CODE(KEYCODE_H)
	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("STEP") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NEXT") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("STORE") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PREV") PORT_CODE(KEYCODE_DOWN)
	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RESET") PORT_CODE(KEYCODE_LALT) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mmd2_state::reset_button), 0)
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(mmd2_state::reset_button)
{
	if (newval)
		reset_banks();
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}

/*
Keyboard
0  1  2  3      PREV  STORE  NEXT  STEP
4  5  6  7      HIGH  LOW  GO  OPTION
8  9  A  B      LOAD  DUMP  PROM  COPY
C  D  E  F      MEM  REGS  AUX  CANCEL

*/

u8 mmd2_state::bank_r(address_space &space, offs_t offset)
{
	for (auto &bank : m_banks)
		bank->set_entry(offset);
	return space.unmap();
}

u8 mmd2_state::port01_r()
{
	// need to add ttyin bit 0
	u8 data = 0x84;
	data |= m_io_dsw->read();
	data |= (m_cass->input() < 0.02) ? 0 : 2;
	return data;
}

void mmd2_state::port05_w(u8 data)
{
	// need to add ttyout bit 0
	m_cass->output(BIT(data, 1) ? -1.0 : +1.0);
}

void mmd2_state::scanlines_w(u8 data)
{
	m_digit = data;
}

void mmd2_state::digit_w(u8 data)
{
	if (m_digit < 9)
		m_digits[m_digit] = data;
}

u8 mmd2_state::keyboard_r()
{
	u8 data = 0xff;

	if ((m_digit & 7) < 4)
		data = m_io_keyboard[m_digit & 7]->read();

	return data;
}

void mmd2_state::status_callback(u8 data)
{
	// operate the HALT LED
	m_led_halt = (~data & i8080_cpu_device::STATUS_HLTA) ? 1 : 0;
	// operate the HOLD LED - this should connect to the HLDA pin,
	// but it isn't emulated, using WO instead (whatever that does).
	m_led_hold = (data & i8080_cpu_device::STATUS_WO) ? 1 : 0;
}

void mmd2_state::machine_start()
{
	m_digits.resolve();
	m_p.resolve();
	m_led_halt.resolve();
	m_led_hold.resolve();

	save_pointer(NAME(m_ram), 0x1400);
	save_item(NAME(m_digit));
}

void mmd2_state::machine_reset()
{
	reset_banks();
}

void mmd2_state::reset_banks()
{
	for (auto &bank : m_banks)
		bank->set_entry(0);
}

void mmd2_state::init_mmd2()
{
	// We preset all banks here, so that bankswitching will incur no speed penalty.
	// ROM 0000/0400 indicate ROMs, RAM /0400/0C00 indicate RAM, 1000 is a dummy write area for ROM banks.
	u8 *const ROM = memregion("maincpu")->base();
	m_ram = make_unique_clear<u8[]>(0x1400);
	u8 *RAM = m_ram.get();
	m_banks[0]->configure_entry(0, &ROM[0x0000]);
	m_banks[0]->configure_entry(1,  RAM);
	m_banks[0]->configure_entry(2, &ROM[0x0c00]);
	m_banks[1]->configure_entry(0,  RAM+0x1000);
	m_banks[1]->configure_entry(1,  RAM);
	m_banks[1]->configure_entry(2,  RAM+0x1000);
	m_banks[2]->configure_entry(0, &ROM[0x0400]);
	m_banks[2]->configure_entry(1,  RAM+0x0400);
	m_banks[2]->configure_entry(2,  RAM+0x0400);
	m_banks[3]->configure_entry(0,  RAM+0x1000);
	m_banks[3]->configure_entry(1,  RAM+0x0400);
	m_banks[3]->configure_entry(2,  RAM+0x0400);
	m_banks[4]->configure_entry(0,  RAM);
	m_banks[4]->configure_entry(1, &ROM[0x0000]);
	m_banks[4]->configure_entry(2, &ROM[0x0000]);
	m_banks[5]->configure_entry(0,  RAM);
	m_banks[5]->configure_entry(1,  RAM+0x1000);
	m_banks[5]->configure_entry(2,  RAM+0x1000);
	m_banks[6]->configure_entry(0,  RAM+0x0c00);
	m_banks[6]->configure_entry(1, &ROM[0x0c00]);
	m_banks[6]->configure_entry(2,  RAM);
	m_banks[7]->configure_entry(0,  RAM+0x0c00);
	m_banks[7]->configure_entry(1,  RAM+0x1000);
	m_banks[7]->configure_entry(2,  RAM);
}

void mmd2_state::mmd2(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, 6750000 / 9);
	m_maincpu->set_addrmap(AS_PROGRAM, &mmd2_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &mmd2_state::io_map);
	m_maincpu->out_status_func().set(FUNC(mmd2_state::status_callback));
	m_maincpu->out_inte_func().set_output("led_inte");         // operate the INTE LED

	/* video hardware */
	config.set_default_layout(layout_mmd2);

	/* Devices */
	i8279_device &kbdc(I8279(config, "i8279", 400000));        // based on divider
	kbdc.out_sl_callback().set(FUNC(mmd2_state::scanlines_w)); // scan SL lines
	kbdc.out_disp_callback().set(FUNC(mmd2_state::digit_w));   // display A&B
	kbdc.in_rl_callback().set(FUNC(mmd2_state::keyboard_r));   // kbd RL lines
	kbdc.in_shift_callback().set_constant(1);                  // Shift key
	kbdc.in_ctrl_callback().set_constant(1);

	// Cassette
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	SPEAKER(config, "mono").front_center();
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
}

/* ROM definition */
ROM_START( mmd2 )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "mmd2330.bin", 0x0000, 0x0800, CRC(69a77199) SHA1(6c83093b2c32a558c969f4fe8474b234023cc348))
	ROM_LOAD( "mmd2340.bin", 0x0800, 0x0800, CRC(70681bd6) SHA1(c37e3cf34a75e8538471030bb49b8aed45d00ec3))
	ROM_LOAD( "mmd2350.bin", 0x1000, 0x0800, CRC(359f577c) SHA1(9405ca0c1977721e4540a4017907c06dab08d398))
	ROM_LOAD( "mmd2360.bin", 0x1800, 0x0800, CRC(967e69b8) SHA1(c21ec8bef955806a2c6e1b1c8e9068662fb88038))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY                FULLNAME  FLAGS
COMP( 1976, mmd2,  mmd1,   0,      mmd2,    mmd2,  mmd2_state, init_mmd2,  "E&L Instruments Inc", "MMD-2 Mini-Micro Designer",  MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



mod8.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

Microsystems International Limited MOD-8

M.I.L. was formed in 1968 from a joint venture between the Canadian
Government and Northern Telecom. It produced a variety of computer
chips, eproms, etc, plus parts for the telephone company. It folded
in 1975.
(Info from http://www.cse.yorku.ca/museum/v_tour/artifacts/artifacts.htm)


    2009-11-18 Skeleton driver.
    2009-12-02 Working driver [Miodrag Milanovic]
    2011-06-14 Modernised & above notes added.

Commands:
All commands consist of 3 uppercase letters. If further info is required
then a * prompt is printed on a new line, where you will enter the data.
All numbers are OCTAL (3/6 digits with leading zeros). Since a teletypewriter
is being used, there is no cursor. Do NOT press Enter except after these
commands, otherwise things get confusing.

LOC - set current location pointer (the CLP)
DLP - display CLP
DPS - dump symbolic
LDO - load octal
DPO - dump octal
LBF - load BNPF format
DBF - dump BNPF format
EDT - enter Edit Mode
XQT - initiate program execution
CPY - copy routine
TRN - translate routine
SBP - set breakpoint
CBP - clear breakpoint
PRG - program PROM

Pressing Ctrl-A will escape back to the monitor. You will see 8 dashes.

Commands in the Edit Mode:
When you enter the Edit Mode it displays the CLP followed by a slash.

nnn - enter a new value into this memory location and increment the CLP
` (tic) - decrement CLP
@ - same as XQT
R - return to monitor
*nnnnnn - change CLP to this value
space - display current contents of memory

While in 'space' mode, press a letter to increment CLP, or shift-delete
(underscore character) followed by a new byte for this location.

****************************************************************************/

#include "emu.h"

#include "teleprinter.h"

#include "cpu/i8008/i8008.h"

namespace {

class mod8_state : public driver_device
{
public:
	mod8_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_teleprinter(*this, "teleprinter")
		, m_maincpu(*this, "maincpu")
	{ }

	void mod8(machine_config &config);

private:
	void out_w(uint8_t data);
	void tty_w(uint8_t data);
	void kbd_put(u8 data);
	uint8_t tty_r();
	IRQ_CALLBACK_MEMBER(mod8_irq_callback);
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	uint16_t m_tty_data_out = 0U;
	uint8_t m_tty_data_in = 0U;
	int m_tty_cnt = 0;
	void machine_start() override ATTR_COLD;
	required_device<teleprinter_device> m_teleprinter;
	required_device<cpu_device> m_maincpu;
};

void mod8_state::out_w(uint8_t data)
{
	m_tty_data_out >>= 1;
	m_tty_data_out |= BIT(data, 0) ? 0x8000 : 0;
	m_tty_cnt++;

	if (m_tty_cnt == 10)
	{
		m_teleprinter->write(BIT(m_tty_data_out, 7, 7));
		m_tty_cnt = 0;
	}
}

void mod8_state::tty_w(uint8_t data)
{
	m_tty_data_out = 0;
	m_tty_cnt = 0;
}

uint8_t mod8_state::tty_r()
{
	uint8_t d = m_tty_data_in & 1;
	m_tty_data_in >>= 1;
	return d;
}

void mod8_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x000, 0x7ff).rom();
	map(0x800, 0xbff).ram();
}

void mod8_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x1f);
	map(0x00, 0x00).r(FUNC(mod8_state::tty_r));
	map(0x0a, 0x0a).w(FUNC(mod8_state::out_w));
	map(0x0b, 0x0b).w(FUNC(mod8_state::tty_w));
}

/* Input ports */
static INPUT_PORTS_START( mod8 )
INPUT_PORTS_END

IRQ_CALLBACK_MEMBER(mod8_state::mod8_irq_callback)
{
	return 0xC0; // LAA - NOP equivalent
}

void mod8_state::machine_start()
{
	save_item(NAME(m_tty_data_out));
	save_item(NAME(m_tty_data_in));
	save_item(NAME(m_tty_cnt));
}

void mod8_state::kbd_put(u8 data)
{
	m_tty_data_in = data ^ 0xff;
	m_maincpu->set_input_line(0, HOLD_LINE);
}

void mod8_state::mod8(machine_config &config)
{
	/* basic machine hardware */
	I8008(config, m_maincpu, 800000);
	m_maincpu->set_addrmap(AS_PROGRAM, &mod8_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &mod8_state::io_map);
	m_maincpu->set_irq_acknowledge_callback(FUNC(mod8_state::mod8_irq_callback));

	/* video hardware */
	TELEPRINTER(config, m_teleprinter, 0);
	m_teleprinter->set_keyboard_callback(FUNC(mod8_state::kbd_put));
}


/* ROM definition */
ROM_START( mod8 )
	ROM_REGION( 0x0800, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "mon8.001", 0x0000, 0x0100, CRC(b82ac6b8) SHA1(fbea5a6dd4c779ca1671d84089f857a3f548ffcb))
	ROM_LOAD( "mon8.002", 0x0100, 0x0100, CRC(8b82bc3c) SHA1(66222511527b27e56a5a1f9656d424d407eac7d3))
	ROM_LOAD( "mon8.003", 0x0200, 0x0100, CRC(679ae913) SHA1(22423efcb9051c9812fcbac9a27af70415d0dd81))
	ROM_LOAD( "mon8.004", 0x0300, 0x0100, CRC(2a4e580f) SHA1(8b0cb9660fde3cacd299faaa31724e4f3262d77f))
	ROM_LOAD( "mon8.005", 0x0400, 0x0100, CRC(e281bb1a) SHA1(cc7c2746e075512dbf5eed88ae3aea009558dbd0))
	ROM_LOAD( "mon8.006", 0x0500, 0x0100, CRC(b7e2f585) SHA1(5408adabc3df6e6ea8dcfb2327b2883b435ab85e))
	ROM_LOAD( "mon8.007", 0x0600, 0x0100, CRC(49a5c626) SHA1(66c1865db9151818d3b20ec3c68dd793cb98a221))
ROM_END

} // anonymous namespace

/* Driver */

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY                           FULLNAME  FLAGS
COMP( 1974, mod8, 0,      0,      mod8,    mod8,  mod8_state, empty_init, "Microsystems International Ltd", "MOD-8",  MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



modellot.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/**********************************************************************************

General Processor Modello T

2012-12-10 Skeleton driver.
2013-09-27 Added keyboard and cursor.

Made in Italy, a single board with numerous small daughter boards.
The 3 units (keyboard, disk drives, main unit) had wooden cabinets.
It had an inbuilt small green-screen CRT, like a Kaypro, and the RAM could
be 16, 32, or 48k. The FDC is a FD1791.

All the articles and doco (what there is of it) is all in Italian.

Doco found...

Port 77 out (cassette control):
- d0 = recording signal #1
- d1 = relay #1 (0 = open)
- d2 = recording signal #2
- d3 = relay #2 (0 = open)

Port 77 in:
- d0 = free
- d1 = playback signal
- d2 = signal from the anti-glare circuit
- d3 = same as d2

Optional ports:
- 3c to 3f (FDC)
- 5c to 5f (PRT)
- 6c to 6f (US2)
- 78 to 7b (US1)
It's not clear if these are meant to be 3881 PIOs connected to the devices, or for
the devices themselves. An example shows a i8251 used as the US1 device.

There's a rom missing EC00-EFFF, it is used if you try to save to tape.
All input must in UPPER case.

***********************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/keyboard.h"

#include "emupal.h"
#include "screen.h"


namespace {

class modellot_state : public driver_device
{
public:
	modellot_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_p_videoram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
	{ }

	void modellot(machine_config &config);

private:
	u8 port77_r();
	u8 portff_r();
	void kbd_put(u8 data);
	u32 screen_update_modellot(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	u8 m_term_data = 0U;
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;
	memory_passthrough_handler m_rom_shadow_tap;
	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_shared_ptr<u8> m_p_videoram;
	required_region_ptr<u8> m_p_chargen;
};

void modellot_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xbfff).ram().share("mainram"); // 48k ram
	map(0xc000, 0xc3ff).ram().share("videoram");
	map(0xe000, 0xffff).rom().region("maincpu", 0);
}

void modellot_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x77, 0x77).r(FUNC(modellot_state::port77_r));
	map(0xff, 0xff).r(FUNC(modellot_state::portff_r));
}


/* Input ports */
static INPUT_PORTS_START( modellot )
INPUT_PORTS_END

u8 modellot_state::port77_r()
{
	return 4;
}

u8 modellot_state::portff_r()
{
	u8 data = (m_term_data) ? (m_term_data ^ 0x7f) : 0xff;
	m_term_data = 0;
	return data;
}

void modellot_state::kbd_put(u8 data)
{
	m_term_data = data;
}

void modellot_state::machine_reset()
{
	m_term_data = 1;

	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x0000, 0x07ff, m_rom);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0xe000, 0xe7ff,
			"rom_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// reinstall RAM over the ROM shadow
					m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x07ff, m_ram);
				}
			},
			&m_rom_shadow_tap);
}

void modellot_state::machine_start()
{
	save_item(NAME(m_term_data));
}

const gfx_layout charlayout =
{
	8, 16,              /* 8x16 characters */
	128,                /* 128 characters */
	1,              /* 1 bits per pixel */
	{0},                /* no bitplanes; 1 bit per pixel */
	{0,1,2,3,4,5,6,7},
	{0, 8, 2 * 8, 3 * 8, 4 * 8, 5 * 8, 6 * 8, 7 * 8,
		0x400*8, 0x401*8, 0x402*8, 0x403*8, 0x404*8, 0x405*8, 0x406*8, 0x407*8},
	8*8             /* space between characters */
};

static GFXDECODE_START( gfx_modellot )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END


u32 modellot_state::screen_update_modellot(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 sy=0,ma=0;

	for (u8 y = 0; y < 16; y++)
	{
		for (u8 ra = 0; ra < 16; ra++)
		{
			u16 *p = &bitmap.pix(sy++);

			for (u16 x = 0; x < 64; x++)
			{
				u8 inv = 0;

				u8 chr = m_p_videoram[x+ma];

				if (BIT(chr, 7)) inv = 0xff;

				chr &= 0x7f; // cursor

				u8 gfx;
				if (ra < 8)
					gfx = m_p_chargen[(chr<<3) | ra ];
				else
					gfx = m_p_chargen[(chr<<3) | (ra-8) | 0x400];

				gfx ^= inv;

				/* Display a scanline of a character */
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma+=64;
	}
	return 0;
}

void modellot_state::modellot(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &modellot_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &modellot_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(64*8, 16*16);
	screen.set_visarea(0, 64*8-1, 0, 16*16-1);
	screen.set_screen_update(FUNC(modellot_state::screen_update_modellot));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_modellot);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* Devices */
	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(modellot_state::kbd_put));
}

/* ROM definition */
ROM_START( modellot )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )
	//ROM_LOAD( "fdc8119.u3", 0x0000, 0x0400, CRC(a8aee944) SHA1(f2cc598ed2e7a1a620e2f3f53c1a573965f6af26))
	ROM_LOAD( "dt49-48.u1", 0x0000, 0x0400, CRC(2441c438) SHA1(832994a4214a744b7e19e5f74000c95ae65e3759))
	ROM_LOAD( "ht20.u2",    0x0400, 0x0400, CRC(497c0495) SHA1(d03beebc4c31284729f6eac3bdf1fbf44adf7fff))

	ROM_REGION( 0x0800, "chargen", ROMREGION_INVERT )
	ROM_LOAD( "gcem1.u3", 0x0000, 0x0200, CRC(e7739268) SHA1(091ef69282abe657d5f38c70a572964f5200a1d5))
	ROM_CONTINUE(0x400, 0x200)
	ROM_LOAD( "gcem2.u4", 0x0200, 0x0200, CRC(6614330e) SHA1(880a541fb0ef6f37ac89439f9ea75a313c3e53d6))
	ROM_CONTINUE(0x600, 0x200)
ROM_END

} // anonymous namespace

/* Driver */
COMP( 1979, modellot, 0, 0, modellot, modellot, modellot_state, empty_init, "General Processor", "Modello T", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )




modena.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco, hap
// thanks-to:yoyo_chessboard
/*******************************************************************************

Mephisto Modena

The chess engine is by Frans Morsch, same one as CXG Sphinx Dominator 2.05.
Hold Pawn + Knight buttons at boot for test mode.

Hardware notes:
- PCB label: MODENA-A-2
- W65C02SP or RP65C02G @ 4.19MHz
- 8KB RAM (battery-backed), 32KB ROM
- 8*8 chessboard buttons, 16+6 leds, 7seg lcd, piezo

*******************************************************************************/

#include "emu.h"

#include "mmdisplay1.h"

#include "cpu/m6502/w65c02.h"
#include "machine/clock.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "mephisto_modena.lh"


namespace {

class modena_state : public driver_device
{
public:
	modena_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_led_pwm(*this, "led_pwm"),
		m_dac(*this, "dac"),
		m_keys(*this, "KEY")
	{ }

	void modena(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<mephisto_display1_device> m_display;
	required_device<pwm_display_device> m_led_pwm;
	required_device<dac_1bit_device> m_dac;
	required_ioport m_keys;

	u8 m_board_mux = 0;
	u8 m_io_ctrl = 0;

	void modena_mem(address_map &map) ATTR_COLD;

	u8 input_r();
	void io_w(u8 data);
	void led_w(u8 data);
	void update_display();
};

void modena_state::machine_start()
{
	save_item(NAME(m_board_mux));
	save_item(NAME(m_io_ctrl));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void modena_state::update_display()
{
	m_led_pwm->matrix(m_io_ctrl >> 1 & 7, m_board_mux);
}

u8 modena_state::input_r()
{
	u8 data = 0;

	// read buttons
	if (~m_io_ctrl & 1)
		data |= m_keys->read();

	// read chessboard sensors
	for (int i = 0; i < 8; i++)
		if (BIT(m_board_mux, i))
			data |= m_board->read_rank(i);

	return data;
}

void modena_state::led_w(u8 data)
{
	// d0-d7: chessboard mux, led data
	m_board_mux = ~data;
	update_display();
}

void modena_state::io_w(u8 data)
{
	// d0: button select
	// d1-d3: led select
	m_io_ctrl = data;
	update_display();

	// d4: lcd common
	m_display->common_w(BIT(data, 4));

	// d6: speaker out
	m_dac->write(BIT(data, 6));
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void modena_state::modena_mem(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("nvram");
	map(0x4000, 0x4000).w(m_display, FUNC(mephisto_display1_device::data_w));
	map(0x5000, 0x5000).w(FUNC(modena_state::led_w));
	map(0x6000, 0x6000).w(FUNC(modena_state::io_w));
	map(0x7000, 0x7000).r(FUNC(modena_state::input_r));
	map(0x7f00, 0x7fff).nopr(); // dummy read on 6502 absolute X page wrap
	map(0x8000, 0xffff).rom().region("maincpu", 0);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( modena )
	PORT_START("KEY")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Book / Pawn")       PORT_CODE(KEYCODE_B)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Info / Knight")     PORT_CODE(KEYCODE_I)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Memory / Bishop")   PORT_CODE(KEYCODE_M)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Position / Rook")   PORT_CODE(KEYCODE_P)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Level / Queen")     PORT_CODE(KEYCODE_L)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Function / King")   PORT_CODE(KEYCODE_F)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Enter / New Game")  PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CODE(KEYCODE_F1) // combine for NEW GAME
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Clear / New Game")  PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_F1) // "
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void modena_state::modena(machine_config &config)
{
	// basic machine hardware
	W65C02(config, m_maincpu, 4.194304_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &modena_state::modena_mem);

	clock_device &nmi_clock(CLOCK(config, "nmi_clock", 4.194304_MHz_XTAL / 0x2000)); // active for 975us
	nmi_clock.signal_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	MEPHISTO_DISPLAY_MODULE1(config, m_display); // internal
	PWM_DISPLAY(config, m_led_pwm).set_size(3, 8);
	config.set_default_layout(layout_mephisto_modena);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( modena )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD("modena_12aug1992_441d.u3", 0x0000, 0x8000, CRC(dd7b4920) SHA1(4606b9d1f8a30180aabedfc0ed3cca0c96618524) )
ROM_END

ROM_START( modenaa )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD("27c256_457f.u3", 0x0000, 0x8000, CRC(2889082c) SHA1(b63f0d856793b4f87471837e2219ce2a42fe18de) )
ROM_END

ROM_START( modenab )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD("modena_4929_270192.u3", 0x0000, 0x8000, CRC(99212677) SHA1(f0565e5441fb38df201176d01793c953886b0303) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1992, modena,   0,      0,      modena,  modena, modena_state, empty_init, "Hegener + Glaser", "Mephisto Modena (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1992, modenaa,  modena, 0,      modena,  modena, modena_state, empty_init, "Hegener + Glaser", "Mephisto Modena (set 2)", MACHINE_SUPPORTS_SAVE )
SYST( 1992, modenab,  modena, 0,      modena,  modena, modena_state, empty_init, "Hegener + Glaser", "Mephisto Modena (set 3)", MACHINE_SUPPORTS_SAVE )



modular.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Dirk Verwiebe, Cowering, Sandro Ronco, hap
/*******************************************************************************

Hegener + Glaser Mephisto chesscomputers with plugin modules
3rd generation (2nd gen is Glasgow/Amsterdam, 1st gen is MM series)

After Roma, H+G started naming the different versions 16 Bit/32 Bit instead of 68000/68020.
With Genius and the TM versions, they still applied "68030".

The London program (1994 competition) is not a dedicated module, but an EPROM upgrade
released by Richard Lang for Almeria, Lyon, Portorose and Vancouver modules, and also
available as upgrades for Berlin/Berlin Pro and Genius. The engine is the same as
Saitek's 1996 Mephisto London 68030 (limited release TM version).

Hardware notes:

Almeria 16 Bit, Lyon 16 Bit:
- MC68HC000FN12, 12.288MHz XTAL
- 128KB ROM (2*27C512)
- 512KB RAM (4*TC514256AP-10, or equivalent)

Portorose 32 Bit:
- MC68020RC12E, 12.288MHz XTAL
- 128KB ROM (TC5710000-20)
- 1MB RAM (8*TC514256AP-70)

Genius 68030:
- M68EC030RP40B, 33.3330MHz XTAL, 6.144MHz XTAL
- 256KB ROM (M27C2001)
- 512+256 KB RAM (TC518512PL-10, 8*TC55465P-20), note: chess engine resides in the
  256KB RAM, probably because the EPROM is too slow for this CPU at full speed.

Display Modul:
- HD44780, 2-line LCD display
- 8KB RAM (TC5565APL-15L), battery
- piezo speaker

For the dedicated tournament machines, see modular_tm.cpp

Undocumented buttons:
- holding ENTER and LEFT cursor on boot runs diagnostics
- holding CLEAR on boot will clear the battery backed RAM

TODO:
- verify IRQ level for 32bit modules, and how IRQ is acknowledged
- match I/S= diag speed test with real hardware (good test for proper waitstates?)
- gen32 waitstates emulation is preliminary (without it, sound pitch is way too high
  and lcd write speed too fast). Real gen32 sound is a bit lower pitched than MAME.

================================================================================

Bavaria piece recognition board:
 _______________________________________________
|                                               |
| 74HC21                      74HC74    74HC238 |
| 74HC4040   74HC574          74HC173   74HC374 |
| ROM                  XTAL   74HC368   74HC374 |
| 74HC4024   74HC32           74HC139   74HC374 |
|_______________________________________________|

XTAL = 7.37280MHz
ROM = TC57256AD-12, sine table (not used in MAME)

Only usable with Weltmeister modules, Portorose until London (aka this MAME driver).
It does not work on Portorose 1.01, even though it detects the Bavaria board.
See German patent DE4207534 for detailed description.

Each piece has a Tank circuit, and in each square of the board there is a coil.
By scanning all the squares at different frequencies, the resonance frequency
of every piece is obtained in order to identify it.

Coil resonance frequency:
wJ,  bJ,  wK,  bK,  wQ,  bQ,  wP,  bP,  wB,  bB,  wN,  bN,  wR,  bR  (J = Joker)
460, 421, 381, 346, 316, 289, 259, 238, 217, 203, 180, 167, 154, 138 kHz
14,  13,  12,  11,  10,  9,   8,   7,   6,   5,   4,   3,   2,   1   piece ID

In MAME, to switch between the magnets chessboard and the Bavaria board, there's
a configuration port. It's also doable by clicking on the sensorboard UI header
(where it says "SB.MAGNETS" or "SB.BAVARIA"). The board gets detected at boot,
so restart or reset(F3) afterwards.

Reminder: unsupported on Almeria and Portorose 1.01, this is not a bug.

*******************************************************************************/

#include "emu.h"

#include "mmboard.h"
#include "mmdisplay2.h"

#include "cpu/m68000/m68000.h"
#include "cpu/m68000/m68020.h"
#include "cpu/m68000/m68030.h"
#include "machine/nvram.h"
#include "machine/timer.h"

// internal artwork
#include "mephisto_alm16.lh"
#include "mephisto_alm32.lh"
#include "mephisto_gen32.lh"


namespace {

class mmodular_state : public driver_device
{
public:
	mmodular_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_rom(*this, "maincpu"),
		m_nvram(*this, "nvram", 0x2000, ENDIANNESS_BIG),
		m_board(*this, "board"),
		m_bav_busy(*this, "bav_busy"),
		m_fake(*this, "FAKE")
	{ }

	// machine configs
	void alm16(machine_config &config);
	void alm32(machine_config &config);
	void port16(machine_config &config);
	void port32(machine_config &config);
	void van16(machine_config &config);
	void van32(machine_config &config);
	void gen32(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(switch_sensor_type) { set_sbtype(newval); }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	optional_region_ptr<u32> m_rom;
	memory_share_creator<u8> m_nvram;
	required_device<mephisto_board_device> m_board;
	required_device<timer_device> m_bav_busy;
	optional_ioport m_fake;

	u8 m_bav_data = 0;

	// address maps
	void alm16_mem(address_map &map) ATTR_COLD;
	void alm32_mem(address_map &map) ATTR_COLD;
	void port16_mem(address_map &map) ATTR_COLD;
	void port32_mem(address_map &map) ATTR_COLD;
	void van16_mem(address_map &map) ATTR_COLD;
	void van32_mem(address_map &map) ATTR_COLD;
	void gen32_mem(address_map &map) ATTR_COLD;

	// I/O handlers
	u32 rom_r(offs_t offset);
	void bavaria_w(u8 data);
	u8 bavaria1_r();
	u8 bavaria2_r();

	u8 nvram_r(offs_t offset) { return m_nvram[offset]; }
	void nvram_w(offs_t offset, u8 data) { m_nvram[offset] = data; }

	u8 spawn_cb(offs_t offset);
	void set_sbtype(ioport_value newval);
};

void mmodular_state::machine_start()
{
	save_item(NAME(m_bav_data));
}

void mmodular_state::machine_reset()
{
	set_sbtype(m_fake.read_safe(0) & 1);
}



/*******************************************************************************
    I/O
*******************************************************************************/

u32 mmodular_state::rom_r(offs_t offset)
{
	// waitstates for gen32 (preliminary)
	if (!machine().side_effects_disabled())
		m_maincpu->adjust_icount(-20);

	return m_rom[offset];
}


// Bavaria board

void mmodular_state::set_sbtype(ioport_value newval)
{
	m_bav_data = 0;
	m_board->get()->set_type(newval ? sensorboard_device::INDUCTIVE : sensorboard_device::MAGNETS);

	if (machine().phase() == machine_phase::RUNNING)
	{
		m_board->get()->cancel_hand();
		m_board->get()->refresh();
	}
}

u8 mmodular_state::spawn_cb(offs_t offset)
{
	// ignore jokers
	return (!m_board->get()->is_inductive() && offset > 12) ? 0 : offset;
}

void mmodular_state::bavaria_w(u8 data)
{
	if (!m_board->get()->is_inductive())
		return;

	// d0-d5: select square
	// d6: no function?
	// d7: start search
	if (m_bav_data & ~data & 0x80)
		m_bav_busy->adjust(attotime::from_usec(3000));

	m_bav_data = data;
}

u8 mmodular_state::bavaria1_r()
{
	if (!m_board->get()->is_inductive())
		return 0;

	// d0-d3: piece id
	// other: unused?
	static const u8 piece_id[0x10] =
		{ 0, 8, 4, 6, 2, 10, 12, 7, 3, 5, 1, 9, 11, 14, 13, 0 };

	int x = m_bav_data >> 3 & 7;
	int y = m_bav_data & 7;

	return piece_id[m_board->get()->read_sensor(x, y) & 0xf];
}

u8 mmodular_state::bavaria2_r()
{
	if (!m_board->get()->is_inductive())
		return 0;

	// d7: busy signal
	// other: unused?
	return m_bav_busy->enabled() ? 0x80 : 0;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void mmodular_state::alm16_mem(address_map &map)
{
	map(0x000000, 0x01ffff).rom();
	map(0x400000, 0x47ffff).ram();
	map(0x800000, 0x803fff).rw(FUNC(mmodular_state::nvram_r), FUNC(mmodular_state::nvram_w)).umask16(0xff00);
	map(0xc00000, 0xc00000).r("board", FUNC(mephisto_board_device::input_r)); // 0xff on Bavaria
	map(0xc80000, 0xc80000).w("board", FUNC(mephisto_board_device::mux_w));
	map(0xd00000, 0xd00000).w("board", FUNC(mephisto_board_device::led_w));
	map(0xd00000, 0xd00001).nopr(); // clr.b
	map(0xf00000, 0xf00003).portr("KEY1");
	map(0xf00004, 0xf00007).portr("KEY2");
	map(0xf00008, 0xf0000b).portr("KEY3");
	map(0xd80000, 0xd80000).w("display", FUNC(mephisto_display2_device::latch_w));
	map(0xd80008, 0xd80008).w("display", FUNC(mephisto_display2_device::io_w));
}

void mmodular_state::port16_mem(address_map &map)
{
	alm16_mem(map);

	map(0xe80002, 0xe80002).r(FUNC(mmodular_state::bavaria1_r));
	map(0xe80004, 0xe80004).w(FUNC(mmodular_state::bavaria_w));
	map(0xe80006, 0xe80006).r(FUNC(mmodular_state::bavaria2_r));
}

void mmodular_state::van16_mem(address_map &map)
{
	port16_mem(map);

	map(0x020000, 0x03ffff).rom();
}


void mmodular_state::alm32_mem(address_map &map)
{
	map(0x00000000, 0x0001ffff).rom();
	map(0x40000000, 0x400fffff).ram();
	map(0x800000ec, 0x800000ef).portr("KEY1");
	map(0x800000f4, 0x800000f7).portr("KEY2");
	map(0x800000f8, 0x800000fb).portr("KEY3");
	map(0x800000fc, 0x800000fc).r("board", FUNC(mephisto_board_device::input_r));
	map(0x88000000, 0x88000007).w("board", FUNC(mephisto_board_device::mux_w)).umask32(0xff000000);
	map(0x90000000, 0x90000007).w("board", FUNC(mephisto_board_device::led_w)).umask32(0xff000000);
	map(0xa0000000, 0xa0000000).w("display", FUNC(mephisto_display2_device::latch_w));
	map(0xa0000010, 0xa0000010).w("display", FUNC(mephisto_display2_device::io_w));
	map(0xa8000000, 0xa8007fff).rw(FUNC(mmodular_state::nvram_r), FUNC(mmodular_state::nvram_w)).umask32(0xff000000);
}

void mmodular_state::port32_mem(address_map &map)
{
	alm32_mem(map);

	map(0x98000004, 0x98000004).r(FUNC(mmodular_state::bavaria1_r));
	map(0x98000008, 0x98000008).w(FUNC(mmodular_state::bavaria_w));
	map(0x9800000c, 0x9800000c).r(FUNC(mmodular_state::bavaria2_r));
}

void mmodular_state::van32_mem(address_map &map)
{
	port32_mem(map);

	map(0x00020000, 0x0003ffff).rom();
}


void mmodular_state::gen32_mem(address_map &map)
{
	map(0x00000000, 0x0003ffff).r(FUNC(mmodular_state::rom_r));
	map(0x40000000, 0x4007ffff).ram();
	map(0x80000000, 0x8003ffff).ram();
	map(0xc0000000, 0xc0000000).r("board", FUNC(mephisto_board_device::input_r));
	map(0xc8000000, 0xc8000003).nopw();
	map(0xc8000004, 0xc8000004).w("board", FUNC(mephisto_board_device::mux_w));
	map(0xd0000000, 0xd0000003).nopw();
	map(0xd0000004, 0xd0000004).w("board", FUNC(mephisto_board_device::led_w));
	map(0xd8000004, 0xd8000004).r(FUNC(mmodular_state::bavaria1_r));
	map(0xd8000008, 0xd8000008).w(FUNC(mmodular_state::bavaria_w));
	map(0xd800000c, 0xd800000c).r(FUNC(mmodular_state::bavaria2_r));
	map(0xe0000000, 0xe0000000).w("display", FUNC(mephisto_display2_device::latch_w));
	map(0xe0000010, 0xe0000010).w("display", FUNC(mephisto_display2_device::io_w));
	map(0xe8000000, 0xe8007fff).rw(FUNC(mmodular_state::nvram_r), FUNC(mmodular_state::nvram_w)).umask32(0xff000000);
	map(0xf0000004, 0xf0000007).portr("KEY1");
	map(0xf0000008, 0xf000000b).portr("KEY2");
	map(0xf0000010, 0xf0000013).portr("KEY3");
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( bavaria )
	PORT_START("FAKE")
	PORT_CONFNAME( 0x01, 0x00, "Board Sensors" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mmodular_state::switch_sensor_type), 0)
	PORT_CONFSETTING(    0x00, "Magnets (Exclusive)" ) // or Muenchen/Modular
	PORT_CONFSETTING(    0x01, "Induction (Bavaria)" )
INPUT_PORTS_END

static INPUT_PORTS_START( gen32 )
	PORT_INCLUDE( bavaria )

	PORT_START("KEY1")
	PORT_BIT(0x01000000, IP_ACTIVE_HIGH, IPT_KEYPAD)  PORT_NAME("ENT")    PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x02000000, IP_ACTIVE_HIGH, IPT_KEYPAD)  PORT_NAME("LEFT")   PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_4_PAD)

	PORT_START("KEY2")
	PORT_BIT(0x01000000, IP_ACTIVE_HIGH, IPT_KEYPAD)  PORT_NAME("UP")     PORT_CODE(KEYCODE_UP) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x02000000, IP_ACTIVE_HIGH, IPT_KEYPAD)  PORT_NAME("DOWN")   PORT_CODE(KEYCODE_DOWN) PORT_CODE(KEYCODE_2_PAD)

	PORT_START("KEY3")
	PORT_BIT(0x01000000, IP_ACTIVE_HIGH, IPT_KEYPAD)  PORT_NAME("CL")     PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x02000000, IP_ACTIVE_HIGH, IPT_KEYPAD)  PORT_NAME("RIGHT")  PORT_CODE(KEYCODE_RIGHT) PORT_CODE(KEYCODE_6_PAD)
INPUT_PORTS_END

static INPUT_PORTS_START( alm16 )
	PORT_START("KEY1")
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYPAD)      PORT_NAME("LEFT")   PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYPAD)      PORT_NAME("ENT")    PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CODE(KEYCODE_7_PAD)

	PORT_START("KEY2")
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYPAD)      PORT_NAME("RIGHT")  PORT_CODE(KEYCODE_RIGHT) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYPAD)      PORT_NAME("UP")     PORT_CODE(KEYCODE_UP) PORT_CODE(KEYCODE_8_PAD)

	PORT_START("KEY3")
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYPAD)      PORT_NAME("DOWN")   PORT_CODE(KEYCODE_DOWN) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYPAD)      PORT_NAME("CL")     PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_9_PAD)
INPUT_PORTS_END

static INPUT_PORTS_START( alm32 )
	PORT_START("KEY1")
	PORT_BIT(0x4000, IP_ACTIVE_LOW, IPT_KEYPAD)       PORT_NAME("RIGHT")  PORT_CODE(KEYCODE_RIGHT) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x8000, IP_ACTIVE_LOW, IPT_KEYPAD)       PORT_NAME("CL")     PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_9_PAD)

	PORT_START("KEY2")
	PORT_BIT(0x4000, IP_ACTIVE_LOW, IPT_KEYPAD)       PORT_NAME("DOWN")   PORT_CODE(KEYCODE_DOWN) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x8000, IP_ACTIVE_LOW, IPT_KEYPAD)       PORT_NAME("UP")     PORT_CODE(KEYCODE_UP) PORT_CODE(KEYCODE_8_PAD)

	PORT_START("KEY3")
	PORT_BIT(0x4000, IP_ACTIVE_LOW, IPT_KEYPAD)       PORT_NAME("LEFT")   PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x8000, IP_ACTIVE_LOW, IPT_KEYPAD)       PORT_NAME("ENT")    PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CODE(KEYCODE_7_PAD)
INPUT_PORTS_END

static INPUT_PORTS_START( port16 )
	PORT_INCLUDE( bavaria )
	PORT_INCLUDE( alm16 )
INPUT_PORTS_END

static INPUT_PORTS_START( port32 )
	PORT_INCLUDE( bavaria )
	PORT_INCLUDE( alm32 )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void mmodular_state::alm16(machine_config &config)
{
	// basic machine hardware
	M68000(config, m_maincpu, 12.288_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mmodular_state::alm16_mem);

	const attotime irq_period = attotime::from_hz(12.288_MHz_XTAL / 10 / 0x800); // 600Hz
	m_maincpu->set_periodic_int(FUNC(mmodular_state::irq3_line_hold), irq_period);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	MEPHISTO_SENSORS_BOARD(config, m_board);
	subdevice<sensorboard_device>("board:board")->set_spawnpoints(12+2); // +2 jokers
	subdevice<sensorboard_device>("board:board")->spawn_cb().set(FUNC(mmodular_state::spawn_cb));
	subdevice<sensorboard_device>("board:board")->set_nvram_enable(true);

	TIMER(config, "bav_busy").configure_generic(nullptr);

	// video hardware
	MEPHISTO_DISPLAY_MODULE2(config, "display");
	config.set_default_layout(layout_mephisto_alm16);
}

void mmodular_state::port16(machine_config &config)
{
	alm16(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &mmodular_state::port16_mem);
}

void mmodular_state::van16(machine_config &config)
{
	port16(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &mmodular_state::van16_mem);
}

void mmodular_state::alm32(machine_config &config)
{
	alm16(config);

	// basic machine hardware
	M68020(config.replace(), m_maincpu, 12.288_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mmodular_state::alm32_mem);

	const attotime irq_period = attotime::from_hz(12.288_MHz_XTAL / 0x4000); // 750Hz
	m_maincpu->set_periodic_int(FUNC(mmodular_state::irq3_line_hold), irq_period);

	config.set_default_layout(layout_mephisto_alm32);
}

void mmodular_state::port32(machine_config &config)
{
	alm32(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &mmodular_state::port32_mem);
}

void mmodular_state::van32(machine_config &config)
{
	port32(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &mmodular_state::van32_mem);
}

void mmodular_state::gen32(machine_config &config)
{
	van32(config);

	// basic machine hardware
	M68EC030(config.replace(), m_maincpu, 33.333_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mmodular_state::gen32_mem);

	const attotime irq_period = attotime::from_hz(6.144_MHz_XTAL / 0x4000); // through 4060, 375Hz
	m_maincpu->set_periodic_int(FUNC(mmodular_state::irq3_line_hold), irq_period);

	config.set_default_layout(layout_mephisto_gen32);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

// sinus table lookup ROM for Mephisto Bavaria chessboard (unused on MAME)
#define BAVARIA_BOARD_ROM() \
	ROM_REGION( 0x8000, "bavaria", 0 ) \
	ROM_LOAD("sinus_15_bavaria", 0x0000, 0x8000, CRC(84421306) SHA1(5aab13bf38d80a4233c11f6eb5657f2749c14547) )

ROM_START( alm32 ) // U012 D21A 2FCE
	ROM_REGION32_BE( 0x20000, "maincpu", 0 )
	ROM_LOAD("alm32.bin", 0x00000, 0x20000, CRC(38f4b305) SHA1(43459a057ff29248c74d656a036ac325202b9c15) )
ROM_END

ROM_START( alm16 ) // U013 65CE 2FCE
	ROM_REGION16_BE( 0x20000, "maincpu", 0 )
	ROM_LOAD16_BYTE("almeria_16bit_v0.13_even", 0x00000, 0x10000, CRC(ee5b6ec4) SHA1(30920c1b9e16ffae576da5afa0b56da59ada3dbb) ) // AT26C512
	ROM_LOAD16_BYTE("almeria_16bit_v0.13_odd",  0x00001, 0x10000, CRC(d0be4ee4) SHA1(d36c074802d2c9099cd44e75f9de3fc7d1fd9908) ) // "
ROM_END

ROM_START( alm16a ) // U012 737C 2FCE
	ROM_REGION16_BE( 0x20000, "maincpu", 0 )
	ROM_LOAD16_BYTE("almeria_16bit_v0.121_even", 0x00000, 0x10000, CRC(3ab8fd3b) SHA1(0147f2f7aa57a5afab656d05be77bda2d35deb92) ) // TMS27C512-2JL
	ROM_LOAD16_BYTE("almeria_16bit_v0.121_odd",  0x00001, 0x10000, CRC(436c1d85) SHA1(b141789c2be0a22bab58532d7fb8e57131811547) ) // "
ROM_END

ROM_START( port32 ) // V104 3F63 1CD7
	ROM_REGION32_BE( 0x20000, "maincpu", 0 )
	ROM_LOAD("portorose_32bit_v104", 0x00000, 0x20000, CRC(66a6c84c) SHA1(b71035f91a452901e1765e351fb36c3f18888e42) )

	BAVARIA_BOARD_ROM()
ROM_END

ROM_START( port32a ) // V103 C734 1CD7
	ROM_REGION32_BE( 0x20000, "maincpu", 0 )
	ROM_LOAD("portorose_32bit_v103", 0x00000, 0x20000, CRC(02c091b3) SHA1(f1d48e73b24093288dbb8a06617bb62420c07508) )

	BAVARIA_BOARD_ROM()
ROM_END

ROM_START( port32b ) // V101 7805 1CD7
	ROM_REGION32_BE( 0x20000, "maincpu", 0 )
	ROM_LOAD("portorose_32bit_v101", 0x00000, 0x20000, CRC(405bd668) SHA1(8c6eacff7f6784fa1d38344d594c7e52ac828a23) )

	BAVARIA_BOARD_ROM()
ROM_END

ROM_START( port16 ) // V101 630D 1CD7
	ROM_REGION16_BE( 0x20000, "maincpu", 0 )
	ROM_LOAD16_BYTE("port16ev.bin", 0x00000, 0x10000, CRC(68e4a37d) SHA1(33e7216db664174a8448e455bba97738a29c0f31) )
	ROM_LOAD16_BYTE("port16od.bin", 0x00001, 0x10000, CRC(cae77a05) SHA1(9a0ca8bb37325698f8d208f64a340690b9a933b5) )

	BAVARIA_BOARD_ROM()
ROM_END

ROM_START( lyon32 ) // V207 AE64 5805
	ROM_REGION32_BE( 0x20000, "maincpu", 0 )
	ROM_LOAD("lyon32.bin", 0x00000, 0x20000, CRC(5c128b06) SHA1(954c8f0d3fae29900cb1e9c14a41a9a07a8e185f) )

	BAVARIA_BOARD_ROM()
ROM_END

ROM_START( lyon16 ) // V209 DEE0 5805
	ROM_REGION16_BE( 0x20000, "maincpu", 0 )
	ROM_LOAD16_BYTE("lyon_16bit_even_v_209", 0x00000, 0x10000, CRC(a8b91d0d) SHA1(ba9d5ce827250a044c8f317dbc7cb536a6f521b5) )
	ROM_LOAD16_BYTE("lyon_16bit_odd_v_209",  0x00001, 0x10000, CRC(a335753f) SHA1(32604f00ab54da884425cb19fdc9c03f793ee521) )

	BAVARIA_BOARD_ROM()
ROM_END

ROM_START( lyon16a ) // V207 EC82 5805
	ROM_REGION16_BE( 0x20000, "maincpu", 0 )
	ROM_LOAD16_BYTE("lyon_16bit_even_v_207", 0x00000, 0x10000, CRC(497bd41a) SHA1(3ffefeeac694f49997c10d248ec6a7aa932898a4) )
	ROM_LOAD16_BYTE("lyon_16bit_odd_v_207",  0x00001, 0x10000, CRC(f9de3f54) SHA1(4060e29566d2f40122ccde3c1f84c94a9c1ed54f) )

	BAVARIA_BOARD_ROM()
ROM_END

ROM_START( van32 ) // V309 3FD3 18D3
	ROM_REGION32_BE( 0x40000, "maincpu", 0 )
	ROM_LOAD("vanc32.bin", 0x00000, 0x40000, CRC(f872beb5) SHA1(9919f207264f74e2b634b723b048ae9ca2cefbc7) )

	BAVARIA_BOARD_ROM()
ROM_END

ROM_START( van16 ) // V309 C8F3 18D3
	ROM_REGION16_BE( 0x40000, "maincpu", 0 )
	ROM_LOAD16_BYTE("vancouver_16_even_v_309", 0x00000, 0x20000, CRC(e87602d5) SHA1(90cb2767b4ae9e1b265951eb2569b9956b9f7f44) )
	ROM_LOAD16_BYTE("vancouver_16_odd_v_309",  0x00001, 0x20000, CRC(585f3bdd) SHA1(90bb94a12d3153a91e3760020e1ea2a9eaa7ec0a) )

	BAVARIA_BOARD_ROM()
ROM_END

ROM_START( gen32 ) // V401 D1BB 5A88
	ROM_REGION32_BE( 0x40000, "maincpu", 0 )
	ROM_LOAD("genius_68030_version_4.01", 0x00000, 0x40000, CRC(ea9938c0) SHA1(645cf0b5b831b48104ad6cec8d78c63dbb6a588c) )

	BAVARIA_BOARD_ROM()
ROM_END

ROM_START( gen32a ) // V400 3B95 5A88
	ROM_REGION32_BE( 0x40000, "maincpu", 0 )
	ROM_LOAD("gen32_4.bin", 0x00000, 0x40000, CRC(6cc4da88) SHA1(ea72acf9c67ed17c6ac8de56a165784aa629c4a1) )

	BAVARIA_BOARD_ROM()
ROM_END

ROM_START( gen32l ) // V500 EDC1 B0D1
	ROM_REGION32_BE( 0x40000, "maincpu", 0 )
	ROM_LOAD("gen32l.bin", 0x00000, 0x40000, CRC(853baa4e) SHA1(946951081d4e91e5bdd9e93d0769568a7fe79bad) )

	BAVARIA_BOARD_ROM()
ROM_END

ROM_START( lond32 ) // V500 DF8B B0D1
	ROM_REGION32_BE( 0x40000, "maincpu", 0 )
	ROM_LOAD("london_program_68020_module", 0x00000, 0x40000, CRC(3225b8da) SHA1(fd8f6f4e9c03b6cdc86d8405e856c26041bfad12) )

	BAVARIA_BOARD_ROM()
ROM_END

ROM_START( lond16 ) // V500 5ED1 B0D1
	ROM_REGION16_BE( 0x40000, "maincpu", 0 )
	ROM_LOAD16_BYTE("london_program_68000_module_even", 0x00000, 0x20000, CRC(68cfc2de) SHA1(93b551180f01f8ed6991c082795cd9ead922179a) )
	ROM_LOAD16_BYTE("london_program_68000_module_odd",  0x00001, 0x20000, CRC(2d75e2cf) SHA1(2ec9222c95f4be9667fb3b4be1b6f90fd4ad11c4) )

	BAVARIA_BOARD_ROM()
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE INPUT   CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1988, alm32,   0,      0,      alm32,  alm32,  mmodular_state, empty_init, "Hegener + Glaser", "Mephisto Almeria 32 Bit (v0.12?)", MACHINE_SUPPORTS_SAVE ) // 0.12 or 0.121?
SYST( 1988, alm16,   alm32,  0,      alm16,  alm16,  mmodular_state, empty_init, "Hegener + Glaser", "Mephisto Almeria 16 Bit (v0.13)", MACHINE_SUPPORTS_SAVE )
SYST( 1988, alm16a,  alm32,  0,      alm16,  alm16,  mmodular_state, empty_init, "Hegener + Glaser", "Mephisto Almeria 16 Bit (v0.121)", MACHINE_SUPPORTS_SAVE )

SYST( 1989, port32,  0,      0,      port32, port32, mmodular_state, empty_init, "Hegener + Glaser", "Mephisto Portorose 32 Bit (v1.04)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, port32a, port32, 0,      port32, port32, mmodular_state, empty_init, "Hegener + Glaser", "Mephisto Portorose 32 Bit (v1.03)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, port32b, port32, 0,      port32, port32, mmodular_state, empty_init, "Hegener + Glaser", "Mephisto Portorose 32 Bit (v1.01)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, port16,  port32, 0,      port16, port16, mmodular_state, empty_init, "Hegener + Glaser", "Mephisto Portorose 16 Bit (v1.01)", MACHINE_SUPPORTS_SAVE )

SYST( 1990, lyon32,  0,      0,      port32, port32, mmodular_state, empty_init, "Hegener + Glaser", "Mephisto Lyon 32 Bit (v2.07)", MACHINE_SUPPORTS_SAVE )
SYST( 1990, lyon16,  lyon32, 0,      port16, port16, mmodular_state, empty_init, "Hegener + Glaser", "Mephisto Lyon 16 Bit (v2.09)", MACHINE_SUPPORTS_SAVE )
SYST( 1990, lyon16a, lyon32, 0,      port16, port16, mmodular_state, empty_init, "Hegener + Glaser", "Mephisto Lyon 16 Bit (v2.07)", MACHINE_SUPPORTS_SAVE )

SYST( 1991, van32,   0,      0,      van32,  port32, mmodular_state, empty_init, "Hegener + Glaser", "Mephisto Vancouver 32 Bit", MACHINE_SUPPORTS_SAVE )
SYST( 1991, van16,   van32,  0,      van16,  port16, mmodular_state, empty_init, "Hegener + Glaser", "Mephisto Vancouver 16 Bit", MACHINE_SUPPORTS_SAVE )

SYST( 1993, gen32,   0,      0,      gen32,  gen32,  mmodular_state, empty_init, "Hegener + Glaser", "Mephisto Genius 68030 (v4.01)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_TIMING )
SYST( 1993, gen32a,  gen32,  0,      gen32,  gen32,  mmodular_state, empty_init, "Hegener + Glaser", "Mephisto Genius 68030 (v4.00)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_TIMING )
SYST( 1996, gen32l,  gen32,  0,      gen32,  gen32,  mmodular_state, empty_init, "Richard Lang", "Mephisto Genius 68030 (London upgrade)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_TIMING )

SYST( 1996, lond32,  0,      0,      van32,  port32, mmodular_state, empty_init, "Richard Lang", "Mephisto London 32 Bit", MACHINE_SUPPORTS_SAVE ) // for alm32/port32/lyon32/van32
SYST( 1996, lond16,  lond32, 0,      van16,  port16, mmodular_state, empty_init, "Richard Lang", "Mephisto London 16 Bit", MACHINE_SUPPORTS_SAVE ) // for alm16/port16/lyon16/van16



modular_tm.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
/*******************************************************************************

Mephisto Turniermaschinen (dedicated in-house chesscomputers used at tournaments),
and their limited-release home versions. These are mephisto_modular hardware
generation, see that driver for more information.

V versions were sold in limited quantities by Hobby Computer Centrale.
T versions were the ones used in actual tournaments, some of them sold to fans.

The Bavaria board does not work on these. Not only does it not have the connector
for it, but no software 'driver' either.

Mephisto TM Almeria is not on this hardware.

Mephisto TM Berlin is an unreleased dev version, an update to TM Vancouver, but
not used in any tournament. Internal string and version matches TM Vancouver,
but ROM has many differences.

BTANB:
- lyon32t8 still says "2048Kbyte" even though it uses 8MB RAM

================================================================================

Hardware notes:

V(Verkauf?) home version:
- XC68030RC33B @ 36MHz
- 256KB SRAM (8*TC55465P-25), 128KB or 256KB ROM
- 2MB DRAM (16*TC514256AP-70)
- 8KB battery-backed SRAM (TC5565PL-15)
- 8*8 LEDs, magnets chessboard

T(Turnier) tournament version: (differences)
- XC68030RC50B, CPU frequency tuned for tournament (see change_cpu_freq)
- 3 more 2MB DRAM rows

After boot, it copies ROM to RAM, probably to circumvent waitstates on slow ROM.

*******************************************************************************/

#include "emu.h"

#include "mmboard.h"
#include "mmdisplay2.h"

#include "cpu/m68000/m68030.h"
#include "machine/nvram.h"

// internal artwork
#include "mephisto_modular_tm.lh"


namespace {

class mmtm_state : public driver_device
{
public:
	mmtm_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_boot_view(*this, "boot_view"),
		m_nvram(*this, "nvram", 0x2000, ENDIANNESS_BIG)
	{ }

	// machine configs
	void mmtm_v(machine_config &config);
	void mmtm_t(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(change_cpu_freq);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	memory_view m_boot_view;
	memory_share_creator<u8> m_nvram;

	emu_timer *m_boot_timer;

	// address maps
	void mmtm_2m_map(address_map &map) ATTR_COLD;
	void mmtm_8m_map(address_map &map) ATTR_COLD;

	u8 nvram_r(offs_t offset) { return m_nvram[offset]; }
	void nvram_w(offs_t offset, u8 data) { m_nvram[offset] = data; }

	TIMER_CALLBACK_MEMBER(disable_bootrom) { m_boot_view.disable(); }
};



/*******************************************************************************
    Initialization
*******************************************************************************/

void mmtm_state::machine_start()
{
	m_boot_timer = timer_alloc(FUNC(mmtm_state::disable_bootrom), this);
}

void mmtm_state::machine_reset()
{
	// disable bootrom after reset
	m_boot_view.select(0);
	m_boot_timer->adjust(m_maincpu->cycles_to_attotime(50));
}

INPUT_CHANGED_MEMBER(mmtm_state::change_cpu_freq)
{
	// "Mephisto X" were usually overclocked at tournaments
	// rare versions sold to fans seen overclocked at 60MHz or 66MHz
	// default frequency of TM version is 50MHz (also matches beeper pitch with V version)
	static const XTAL xtal[3] = { 50_MHz_XTAL, 60_MHz_XTAL, 66_MHz_XTAL };
	m_maincpu->set_unscaled_clock(xtal[newval % 3]);

	// lcd busy flag timing problem when overclocked
	subdevice<hd44780_device>("display:hd44780")->set_clock_scale((newval == 0) ? 1.0 : 1.32);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void mmtm_state::mmtm_2m_map(address_map &map)
{
	map(0x00000000, 0x0003ffff).ram();
	map(0x00000000, 0x0003ffff).view(m_boot_view);
	m_boot_view[0](0x00000000, 0x0003ffff).rom().region("maincpu", 0);

	map(0x80000000, 0x801fffff).ram();
	map(0xf0000000, 0xf003ffff).rom().region("maincpu", 0);
	map(0xfc000000, 0xfc001fff).rw(FUNC(mmtm_state::nvram_r), FUNC(mmtm_state::nvram_w)).umask32(0xffffffff);
	map(0xfc020004, 0xfc020007).portr("KEY1");
	map(0xfc020008, 0xfc02000b).portr("KEY2");
	map(0xfc020010, 0xfc020013).portr("KEY3");
	map(0xfc040000, 0xfc040000).w("display", FUNC(mephisto_display2_device::latch_w));
	map(0xfc060000, 0xfc060000).w("display", FUNC(mephisto_display2_device::io_w));
	map(0xfc080000, 0xfc080000).w("board", FUNC(mephisto_board_device::mux_w));
	map(0xfc0a0000, 0xfc0a0000).w("board", FUNC(mephisto_board_device::led_w));
	map(0xfc0c0000, 0xfc0c0000).r("board", FUNC(mephisto_board_device::input_r));
}

void mmtm_state::mmtm_8m_map(address_map &map)
{
	mmtm_2m_map(map);
	map(0x80000000, 0x807fffff).ram();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( mmtm_v )
	PORT_START("KEY1")
	PORT_BIT(0x01000000, IP_ACTIVE_HIGH, IPT_KEYPAD)  PORT_NAME("LEFT")   PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x02000000, IP_ACTIVE_HIGH, IPT_KEYPAD)  PORT_NAME("ENT")    PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CODE(KEYCODE_7_PAD)

	PORT_START("KEY2")
	PORT_BIT(0x01000000, IP_ACTIVE_HIGH, IPT_KEYPAD)  PORT_NAME("RIGHT")  PORT_CODE(KEYCODE_RIGHT) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x02000000, IP_ACTIVE_HIGH, IPT_KEYPAD)  PORT_NAME("UP")     PORT_CODE(KEYCODE_UP) PORT_CODE(KEYCODE_8_PAD)

	PORT_START("KEY3")
	PORT_BIT(0x01000000, IP_ACTIVE_HIGH, IPT_KEYPAD)  PORT_NAME("DOWN")   PORT_CODE(KEYCODE_DOWN) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x02000000, IP_ACTIVE_HIGH, IPT_KEYPAD)  PORT_NAME("CL")     PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_9_PAD)
INPUT_PORTS_END

static INPUT_PORTS_START( mmtm_t )
	PORT_INCLUDE( mmtm_v )

	PORT_START("CPU")
	PORT_CONFNAME( 0x03, 0x00, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mmtm_state::change_cpu_freq), 0)
	PORT_CONFSETTING(    0x00, "50MHz" )
	PORT_CONFSETTING(    0x01, "60MHz" )
	PORT_CONFSETTING(    0x02, "66MHz" )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void mmtm_state::mmtm_v(machine_config &config)
{
	// basic machine hardware
	M68030(config, m_maincpu, 36_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mmtm_state::mmtm_2m_map);

	const attotime irq_period = attotime::from_hz(12.288_MHz_XTAL / 0x8000); // through 4060, 375Hz
	m_maincpu->set_periodic_int(FUNC(mmtm_state::irq3_line_hold), irq_period);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	MEPHISTO_SENSORS_BOARD(config, "board");
	subdevice<sensorboard_device>("board:board")->set_nvram_enable(true);

	MEPHISTO_DISPLAY_MODULE2(config, "display");
	config.set_default_layout(layout_mephisto_modular_tm);
}

void mmtm_state::mmtm_t(machine_config &config)
{
	mmtm_v(config);

	// basic machine hardware
	m_maincpu->set_clock(50_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mmtm_state::mmtm_8m_map);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( port32t ) // V101 FA1D 1CD7
	ROM_REGION32_BE( 0x40000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD("portorose_68030_tournament.bin", 0x00000, 0x20000, CRC(31f4c916) SHA1(d22572d224b7308d0d03617997a1ad90e63403c5) )
ROM_END

ROM_START( lyon32t ) // V207 6D28 5805
	ROM_REGION32_BE( 0x40000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD("lyon_68030_tournament.bin", 0x00000, 0x20000, CRC(f07856af) SHA1(a1d5191a4ab4518f2df22f10e6e1305bea8afb37) )
ROM_END

ROM_START( lyon32t8 ) // T207 3C9F 5805
	ROM_REGION32_BE( 0x40000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD("lyon_68030_tournament_8mb.bin", 0x00000, 0x20000, CRC(7b3e6db5) SHA1(28ce87c2d12d92e1a534aaa8dd5489fcf1b42964) )
ROM_END

ROM_START( van32t ) // V309 15C8 18D3
	ROM_REGION32_BE( 0x40000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD("vancouver_68030_tournament.bin", 0x00000, 0x40000, CRC(7b25c9ec) SHA1(bf955b9521f52341754814a7c0bf5941989c8be6) )
ROM_END

ROM_START( van32t8 ) // T309 E04C 18D3
	ROM_REGION32_BE( 0x40000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD("vancouver_68030_tournament_8mb.bin", 0x00000, 0x40000, CRC(d9f0190b) SHA1(7631d2499baad7b8075a1ca2c49f6544d7020c95) )
ROM_END

ROM_START( berl32t8p ) // T309 B138 C981
	ROM_REGION32_BE( 0x40000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD("berlin_68030_tournament_8mb.bin", 0x00000, 0x40000, CRC(4fa6d99d) SHA1(4cda1ce11136e5055f5307f3de3a29b1d98d4f00) )
ROM_END

ROM_START( lond32t ) // V500 BAC6 B0D1
	ROM_REGION32_BE( 0x40000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD("london_68030_tournament.bin", 0x00000, 0x40000, CRC(cc7a1a19) SHA1(860b84ac354280cec1cfcc36627e0a6e1d80c108) )
ROM_END

ROM_START( lond32t8 ) // T500 854A B0D1
	ROM_REGION32_BE( 0x40000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD("london_68030_tournament_8mb.bin", 0x00000, 0x40000, CRC(1ef51242) SHA1(7d4ffec7d80789aaf0a9e796baa8dac7ef2c2f1b) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT  COMPAT  MACHINE  INPUT   CLASS       INIT        COMPANY, FULLNAME, FLAGS
SYST( 1989, port32t,   port32, 0,      mmtm_v,  mmtm_v, mmtm_state, empty_init, "Hegener + Glaser", "Mephisto Portorose 68030", MACHINE_SUPPORTS_SAVE )

SYST( 1990, lyon32t,   lyon32, 0,      mmtm_v,  mmtm_v, mmtm_state, empty_init, "Hegener + Glaser", "Mephisto Lyon 68030", MACHINE_SUPPORTS_SAVE )
SYST( 1990, lyon32t8,  lyon32, 0,      mmtm_t,  mmtm_t, mmtm_state, empty_init, "Hegener + Glaser", "Mephisto TM Lyon", MACHINE_SUPPORTS_SAVE )

SYST( 1991, van32t,    van32,  0,      mmtm_v,  mmtm_v, mmtm_state, empty_init, "Hegener + Glaser", "Mephisto Vancouver 68030", MACHINE_SUPPORTS_SAVE )
SYST( 1991, van32t8,   van32,  0,      mmtm_t,  mmtm_t, mmtm_state, empty_init, "Hegener + Glaser", "Mephisto TM Vancouver", MACHINE_SUPPORTS_SAVE )
SYST( 1991, berl32t8p, van32,  0,      mmtm_t,  mmtm_t, mmtm_state, empty_init, "Hegener + Glaser", "Mephisto TM Berlin (prototype)", MACHINE_SUPPORTS_SAVE )

SYST( 1996, lond32t,   lond32, 0,      mmtm_v,  mmtm_v, mmtm_state, empty_init, "Saitek", "Mephisto London 68030", MACHINE_SUPPORTS_SAVE ) // after Saitek took over H+G
SYST( 1996, lond32t8,  lond32, 0,      mmtm_t,  mmtm_t, mmtm_state, empty_init, "Saitek", "Mephisto TM London", MACHINE_SUPPORTS_SAVE ) // "



molecular.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Angelo Salese
/***********************************************************************************

MOLECULAR Computer

Notes:

File Processor i/os
0x00 DMA
0x10-0x13 CTC
0x20-0x23 CTC (Serial A Clock, Serial B Clock, Parity Error IRQ, Bus IRQ)
0x40-0x43 SIO (Serial A Data, Serial A Control, Serial B Data, Serial B Control)
0x60-0x63 FDC (Control, Cylinder, Sector, Data)
0x70 PLOW Lower Parallel i/o
0x71 PHI Upper Parallel i/o
0x80-0x87 HDD (Status, Data, Result 0-5)

Application Processor i/os
0x00-0x03 PIO (Parallel A Data, Parallel A Control, Parallel B Data, Parallel B Control)
0x20 DMA
0x30-0x33 CTC (Serial A Clock, Serial B Clock, Parity Error IRQ, Bus IRQ)
0x60-0x63 SIO (Serial A Data, Serial A Control, Serial B Data, Serial B Control)

File Processor irqs:
0xf80c PIO Ch A
0xf80e PIO Ch B
0xf810-0xf81e SIO Vector N
0xf820-0xf826 CTC A, Channel N
0xf828-0xf82e CTC B, Channel N (do not use)
0xf830 DMA (do not use)

Application Processor irqs:
0xf80c PIO Ch A
0xf80e PIO Ch B
0xf810 DMA (do not use)
0xf820-0xf82e SIO Vector N

App Processor puts on sio port:
"??250 INITIALIZING..."

File Processor puts on sio port:
"??FP RESTART"

TODO:
- Info on HW ports is pretty scarse, other than a trim i/o documentation and schematics

***********************************************************************************/

#include "emu.h"
#include "cpu/i86/i86.h"
#include "cpu/z80/z80.h"
//#include "sound/ay8910.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

#define I86_CLOCK XTAL(24'000'000)
#define Z80_CLOCK XTAL(16'000'000)

class molecula_state : public driver_device
{
public:
	molecula_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_filecpu(*this, "filecpu")
	{ }

	void molecula(machine_config &config);

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

private:
	// devices
	required_device<cpu_device> m_filecpu;

	uint8_t *m_file_rom = nullptr;
	uint8_t *m_app_rom = nullptr;
	std::unique_ptr<uint8_t[]> m_file_ram;
	std::unique_ptr<uint8_t[]> m_app_ram;

	// screen updates
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	uint8_t file_r(offs_t offset);
	void file_w(offs_t offset, uint8_t data);

	uint8_t app_r(offs_t offset);
	void app_w(offs_t offset, uint8_t data);

	void file_output_w(offs_t offset, uint8_t data);
	void app_output_w(uint8_t data);

	uint8_t sio_r(offs_t offset);
	void sio_w(offs_t offset, uint8_t data);

	uint8_t app_ram_enable = 0;
	uint8_t file_ram_enable = 0;

	void molecula_palette(palette_device &palette) const;

	void molecula_app_io(address_map &map) ATTR_COLD;
	void molecula_app_map(address_map &map) ATTR_COLD;
	void molecula_file_io(address_map &map) ATTR_COLD;
	void molecula_file_map(address_map &map) ATTR_COLD;
};

void molecula_state::video_start()
{
}

uint32_t molecula_state::screen_update( screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect )
{
	return 0;
}

uint8_t molecula_state::file_r(offs_t offset)
{
	if(file_ram_enable)
		return m_file_ram[offset];

	return m_file_rom[offset & 0x7ff];
}


void molecula_state::file_w(offs_t offset, uint8_t data)
{
	m_file_ram[offset] = data;
}

uint8_t molecula_state::app_r(offs_t offset)
{
	if(app_ram_enable)
		return m_app_ram[offset];

	return m_app_rom[offset & 0x7ff];
}


void molecula_state::app_w(offs_t offset, uint8_t data)
{
	m_app_ram[offset] = data;
}

void molecula_state::file_output_w(offs_t offset, uint8_t data)
{
	if(offset == 0)
		file_ram_enable = (data & 0x80) >> 7;

	if(data & 0x7f || offset)
		printf("FILE output -> %02x %02x\n",data,offset);
}


void molecula_state::app_output_w(uint8_t data)
{
	app_ram_enable = (data & 0x80) >> 7;

	if(data & 0x7f)
		printf("APP 0x10 -> %02x\n",data);
}

uint8_t molecula_state::sio_r(offs_t offset)
{
	if(offset == 1)
		return 4;

	return 0;
}

void molecula_state::sio_w(offs_t offset, uint8_t data)
{
	if(offset == 0)
		printf("%c\n",data);
}

void molecula_state::molecula_file_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(molecula_state::file_r), FUNC(molecula_state::file_w));
}

void molecula_state::molecula_file_io(address_map &map)
{
	map.global_mask(0xff);
//  map(0x40, 0x43).rw(FUNC(molecula_state::sio_r), FUNC(molecula_state::sio_w));
	map(0x72, 0x73).w(FUNC(molecula_state::file_output_w)); // unknown
}

void molecula_state::molecula_app_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(molecula_state::app_r), FUNC(molecula_state::app_w));
}

void molecula_state::molecula_app_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x10, 0x10).w(FUNC(molecula_state::app_output_w));
	map(0x60, 0x63).rw(FUNC(molecula_state::sio_r), FUNC(molecula_state::sio_w));
}

static INPUT_PORTS_START( molecula )
	/* dummy active high structure */
	PORT_START("SYSA")
	PORT_DIPNAME( 0x01, 0x00, "SYSA" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )

	/* dummy active low structure */
	PORT_START("DSWA")
	PORT_DIPNAME( 0x01, 0x01, "DSWA" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

#if 0
static const gfx_layout charlayout =
{
	8,8,
	RGN_FRAC(1,1),
	1,
	{ RGN_FRAC(0,1) },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};
#endif

static GFXDECODE_START( gfx_molecula )
//  GFXDECODE_ENTRY( "gfx1", 0, charlayout,     0, 1 )
GFXDECODE_END


void molecula_state::machine_start()
{
	m_file_rom = memregion("fileipl")->base();
	m_app_rom = memregion("appipl")->base();

	m_file_ram = make_unique_clear<uint8_t[]>(0x10000);
	m_app_ram = make_unique_clear<uint8_t[]>(0x10000);
}

void molecula_state::machine_reset()
{
	app_ram_enable = 0;
	file_ram_enable = 0;
}


void molecula_state::molecula_palette(palette_device &palette) const
{
}

void molecula_state::molecula(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_filecpu, Z80_CLOCK/2);
	m_filecpu->set_addrmap(AS_PROGRAM, &molecula_state::molecula_file_map);
	m_filecpu->set_addrmap(AS_IO, &molecula_state::molecula_file_io);
	m_filecpu->set_disable();

	z80_device &appcpu(Z80(config, "appcpu", Z80_CLOCK/2));
	appcpu.set_addrmap(AS_PROGRAM, &molecula_state::molecula_app_map);
	appcpu.set_addrmap(AS_IO, &molecula_state::molecula_app_io);

//  i8086_device &sub(I8086(config, "sub", I86_CLOCK/2));
//  sub.set_addrmap(AS_PROGRAM, &molecula_state::molecula_map);
//  sub.set_addrmap(AS_IO, &molecula_state::molecula_io);
//  sub.set_disable();

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.set_screen_update(FUNC(molecula_state::screen_update));
	screen.set_size(32*8, 32*8);
	screen.set_visarea(0*8, 32*8-1, 0*8, 32*8-1);
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_molecula);

	PALETTE(config, "palette", FUNC(molecula_state::molecula_palette), 8);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
//  AY8910(config, "aysnd", MAIN_CLOCK/4).add_route(ALL_OUTPUTS, "mono", 0.30);
}


/***************************************************************************

  Game driver(s)

***************************************************************************/

ROM_START( molecula )
	ROM_REGION( 0x10000, "fileipl", ROMREGION_ERASE00 )
	ROM_LOAD( "sm32-111782-255-ff.bin", 0x000000, 0x000800, CRC(818f5f5a) SHA1(829b8fef99e6667d04d1944112823f3ea6a1a7e8) )

	ROM_REGION( 0x10000, "appipl", ROMREGION_ERASE00 )
	ROM_LOAD( "ap-111181-fa.bin", 0x000000, 0x000800, CRC(c289fc90) SHA1(2c0a17808971400e0289d65a11ef09e1f5eb90a0) )

	ROM_REGION( 0x1000, "x86ipl", ROMREGION_ERASE00 )
	ROM_LOAD16_BYTE( "ap86eve_2716.bin", 0x000000, 0x000800, CRC(d6484a11) SHA1(19cfea72afbc796e5b59c47095529d6d6350dd98) )
	ROM_LOAD16_BYTE( "ap86odd_2716.bin", 0x000001, 0x000800, CRC(a4403c13) SHA1(2aeb07ad1f3381cbd1ec901e1f095ae737ded538) )

	/* same as ap-111181-fa.bin, with a different & bad byte at [3]*/
	ROM_REGION( 0x10000, "user2", ROMREGION_ERASE00 )
	ROM_LOAD( "ap-111181-253-fd.bin", 0x000000, 0x000800, CRC(10f16080) SHA1(10f13a04c525cd4b67e2b6e1a3eed46c619a439c) )

	/* proms */
	ROM_REGION( 0x10000, "user3", ROMREGION_ERASE00 )
	ROM_LOAD( "ms01_82s131.bin", 0x000000, 0x000200, CRC(433b139c) SHA1(32c1e853faacf85b11506a6903789ef129dd6adc) )
	ROM_LOAD( "ms11_82s131.bin", 0x000200, 0x000200, CRC(cff86fed) SHA1(554ec15799afc6305c8cae0f9d55b32004479cdb) )

	ROM_REGION( 0x10000, "misc", ROMREGION_ERASE00 )
	ROM_LOAD( "mem_16r4.jed", 0x000000, 0x00caef, CRC(a8cfbdfe) SHA1(92be58ab3299e0d3ac37bd1e258054c881b9ad7f) )
	ROM_LOAD( "rx_16r8.jed",  0x000000, 0x00caef, CRC(76f11ea8) SHA1(3ec41e5af04a89659f1b30270ee00e1fdf302ad6) )
	ROM_LOAD( "tx_16r8.jed",  0x000000, 0x00caef, CRC(a91dcf0b) SHA1(16d261f064a4af29c3da58f3ae8a867bdc953ce6) )
	ROM_LOAD( "wait_16r4.jed", 0x000000, 0x00caef, CRC(3aacfeb4) SHA1(1af1a8046e5a8a0337c85b55adceaef6e45702b7) )
ROM_END

} // anonymous namespace


COMP( 1982, molecula, 0, 0, molecula, molecula, molecula_state, empty_init, "MOLECULAR", "MOLECULAR Computer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



mondial.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco, hap
/*******************************************************************************

Mephisto Mondial

The chess engine is Nona by Frans Morsch, the first time his program was included
in a chess computer.

Hardware notes:
- G65SC02-1 @ 2MHz
- 2KB RAM(TC5517AP), 16KB ROM
- expansion slot at underside
- 8*8 chessboard buttons, 24 leds, active buzzer

TODO:
- verify XTAL (or maybe RC or LC circuit), 2MHz is correct
- dump/add MM 1000 module

*******************************************************************************/

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/m6502/g65sc02.h"
#include "machine/sensorboard.h"
#include "sound/beep.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "mephisto_mondial.lh"


namespace {

class mondial_state : public driver_device
{
public:
	mondial_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_led_pwm(*this, "led_pwm"),
		m_beeper(*this, "beeper"),
		m_keys(*this, "KEY.%u", 0)
	{ }

	void mondial(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_led_pwm;
	required_device<beep_device> m_beeper;
	required_ioport_array<2> m_keys;

	u8 m_inp_mux = 0;

	void mondial_mem(address_map &map) ATTR_COLD;

	void control_w(u8 data);
	u8 irq_ack_r();
	u8 input_r(offs_t offset);
};

void mondial_state::machine_start()
{
	save_item(NAME(m_inp_mux));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void mondial_state::control_w(u8 data)
{
	// d0-d3: input mux, led select
	// d4-d6: led data
	m_inp_mux = data & 0xf;
	m_led_pwm->matrix(1 << m_inp_mux, ~data >> 4 & 7);

	// d7: enable beeper
	m_beeper->set_state(BIT(data, 7));
}

u8 mondial_state::irq_ack_r()
{
	if (!machine().side_effects_disabled())
		m_maincpu->set_input_line(0, CLEAR_LINE);

	return 0;
}

u8 mondial_state::input_r(offs_t offset)
{
	u8 data = 0;

	// read chessboard sensors
	if (m_inp_mux < 8)
		data = m_board->read_rank(m_inp_mux);

	// read keypad
	else
		data = m_keys[m_inp_mux & 1]->read();

	return ~(BIT(data, offset) << 7);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void mondial_state::mondial_mem(address_map &map)
{
	map(0x0000, 0x07ff).ram();
	map(0x1000, 0x1000).w(FUNC(mondial_state::control_w));
	map(0x1800, 0x1807).r(FUNC(mondial_state::input_r));
	map(0x2000, 0x2000).r(FUNC(mondial_state::irq_ack_r));
	map(0xc000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( mondial )
	PORT_START("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Pawn / 1")   PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Knight / 2") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Bishop / 3") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Rook / 4")   PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Queen / 5")  PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("King / 6")   PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Black / 7")  PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("White / 8")  PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)

	PORT_START("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("PLAY")       PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("POS")        PORT_CODE(KEYCODE_P)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("MEM")        PORT_CODE(KEYCODE_M)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("INFO")       PORT_CODE(KEYCODE_I)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("CL")         PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("LEV")        PORT_CODE(KEYCODE_L)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("ENT")        PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("RES")        PORT_CODE(KEYCODE_F1)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void mondial_state::mondial(machine_config &config)
{
	// basic machine hardware
	G65SC02(config, m_maincpu, 2'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &mondial_state::mondial_mem);

	const attotime irq_period = attotime::from_hz(2'000'000 / 0x1000);
	m_maincpu->set_periodic_int(FUNC(mondial_state::irq0_line_assert), irq_period);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(250));

	//GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "mondial_cart");
	//SOFTWARE_LIST(config, "cart_list").set_original("mephisto_mondial");

	// video hardware
	PWM_DISPLAY(config, m_led_pwm).set_size(8, 3);
	config.set_default_layout(layout_mephisto_mondial);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	BEEP(config, m_beeper, 2150); // approximation
	m_beeper->add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( mondial )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("cn61057n_hgs_10_470_00", 0xc000, 0x4000, CRC(5cde2e26) SHA1(337be35d5120ca12143ca17f8aa0642b313b3851) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1985, mondial,  0,      0,      mondial, mondial, mondial_state, empty_init, "Hegener + Glaser", "Mephisto Mondial", MACHINE_SUPPORTS_SAVE )



mondial2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco, hap
// thanks-to:yoyo_chessboard
/*******************************************************************************

Mephisto Mondial II

Hardware notes:
- G65SC02 @ 2MHz (unsure about rating)
- 2KB RAM, 32KB ROM
- expansion slot at underside (not used)
- 8*8 chessboard buttons, 24 leds, piezo

*******************************************************************************/

#include "emu.h"

#include "cpu/m6502/g65sc02.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "mephisto_mondial2.lh"


namespace {

class mondial2_state : public driver_device
{
public:
	mondial2_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_led_pwm(*this, "led_pwm"),
		m_dac(*this, "dac"),
		m_keys(*this, "KEY.%u", 0)
	{ }

	void mondial2(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_led_pwm;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<4> m_keys;

	u8 m_keypad_mux = 0;
	u8 m_board_mux = 0;
	u8 m_led_data = 0;

	void mondial2_mem(address_map &map) ATTR_COLD;

	void update_leds();
	void control_w(u8 data);
	void board_w(u8 data);
	u8 input_r(offs_t offset);
};

void mondial2_state::machine_start()
{
	save_item(NAME(m_keypad_mux));
	save_item(NAME(m_board_mux));
	save_item(NAME(m_led_data));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void mondial2_state::update_leds()
{
	m_led_pwm->matrix(m_board_mux, m_led_data);
}

void mondial2_state::control_w(u8 data)
{
	// d0-d3: keypad mux
	m_keypad_mux = ~data & 0xf;

	// d4-d7: led data
	m_led_data = data >> 4 & 7;
	update_leds();

	// d7: speaker out
	m_dac->write(BIT(data, 7));
}

void mondial2_state::board_w(u8 data)
{
	// d0-d7: chessboard mux, led select
	m_board_mux = ~data;
	update_leds();
}

u8 mondial2_state::input_r(offs_t offset)
{
	u8 data = 0;

	// read chessboard sensors
	for (int i = 0; i < 8; i++)
		if (BIT(m_board_mux, i))
			data |= BIT(m_board->read_rank(i), offset);

	// read keypad
	for (int i = 0; i < 4; i++)
		if (BIT(m_keypad_mux, i))
			data |= BIT(m_keys[i]->read(), offset & 3);

	return ~(data << 7);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void mondial2_state::mondial2_mem(address_map &map)
{
	map(0x0000, 0x07ff).ram();
	map(0x2000, 0x2000).w(FUNC(mondial2_state::control_w));
	map(0x2800, 0x2800).w(FUNC(mondial2_state::board_w));
	map(0x3000, 0x3007).r(FUNC(mondial2_state::input_r));
	map(0x8000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( mondial2 )
	PORT_START("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Pawn / 1")   PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Knight / 2") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Bishop / 3") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Rook / 4")   PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)

	PORT_START("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Queen / 5")  PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("King / 6")   PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Black / 7")  PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("White / 8")  PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)

	PORT_START("KEY.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("PLAY")       PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("POS")        PORT_CODE(KEYCODE_P)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("MEM")        PORT_CODE(KEYCODE_M)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("INFO")       PORT_CODE(KEYCODE_I)

	PORT_START("KEY.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("CL")         PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("LEV")        PORT_CODE(KEYCODE_L)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("ENT")        PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("RES")        PORT_CODE(KEYCODE_F1)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void mondial2_state::mondial2(machine_config &config)
{
	// basic machine hardware
	G65SC02(config, m_maincpu, 2_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mondial2_state::mondial2_mem);

	const attotime nmi_period = attotime::from_hz(2_MHz_XTAL / 0x1000);
	m_maincpu->set_periodic_int(FUNC(mondial2_state::nmi_line_pulse), nmi_period);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(250));

	// video hardware
	PWM_DISPLAY(config, m_led_pwm).set_size(8, 3);
	config.set_default_layout(layout_mephisto_mondial2);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( mondial2 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("mondial_ii_01.08.87", 0x8000, 0x8000, CRC(e5945ce6) SHA1(e75bbf9d54087271d9d46fb1de7634eb957f8db0) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1987, mondial2, 0,      0,      mondial2, mondial2, mondial2_state, empty_init, "Hegener + Glaser", "Mephisto Mondial II", MACHINE_SUPPORTS_SAVE )



mondial68k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco, hap
// thanks-to:Berger
/*******************************************************************************

Mephisto Mondial 68000XL

It was made for the US market. The chess engine is actually the one from
Mephisto Dallas.

Hardware notes:
- PCB label: HGS 10 115 01
- TS68000CP12 @ 12MHz
- 64KB ROM (2*D27C256)
- 16KB RAM (2*TC5565APL-15L)
- PCF2112T LCD driver

TODO:
- does it have waitstates, like glasgow/amsterdam?

*******************************************************************************/

#include "emu.h"

#include "cpu/m68000/m68000.h"
#include "machine/74259.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pcf2100.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "mephisto_mondial68k.lh"


namespace {

class mondial68k_state : public driver_device
{
public:
	mondial68k_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_lcd_latch(*this, "lcd_latch"),
		m_led_pwm(*this, "led_pwm"),
		m_lcd(*this, "lcd"),
		m_inputs(*this, "IN.%u", 0),
		m_digits(*this, "digit%u", 0U)
	{ }

	void mondial68k(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<hc259_device> m_lcd_latch;
	required_device<pwm_display_device> m_led_pwm;
	required_device<pcf2112_device> m_lcd;
	required_ioport_array<4> m_inputs;
	output_finder<4> m_digits;

	u8 m_input_mux = 0xff;
	u8 m_board_mux = 0xff;

	void mondial68k_mem(address_map &map) ATTR_COLD;

	void update_leds();
	void lcd_output_w(u32 data);
	void input_mux_w(u8 data);
	void board_mux_w(u8 data);
	u8 inputs_r();
};

void mondial68k_state::machine_start()
{
	m_digits.resolve();

	save_item(NAME(m_input_mux));
	save_item(NAME(m_board_mux));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void mondial68k_state::update_leds()
{
	m_led_pwm->matrix(m_input_mux >> 6, ~m_board_mux);
}

void mondial68k_state::lcd_output_w(u32 data)
{
	// output LCD digits (note: last digit DP segment is unused)
	for (int i = 0; i < 4; i++)
		m_digits[i] = bitswap<8>((data & 0x7fffffff) >> (8 * i), 7,4,5,0,1,2,3,6);
}

void mondial68k_state::board_mux_w(u8 data)
{
	// d0-d7: chessboard mux, led data
	m_board_mux = data;
	update_leds();
}

void mondial68k_state::input_mux_w(u8 data)
{
	// d0-d3: button mux
	// d6,d7: led select
	m_input_mux = data;
	update_leds();
}

u8 mondial68k_state::inputs_r()
{
	u8 data = 0;

	// read buttons
	for (int i = 0; i < 4; i++)
		if (!BIT(m_input_mux, i))
			data |= m_inputs[i]->read();

	// read chessboard sensors
	for (int i = 0; i < 8; i++)
		if (!BIT(m_board_mux, i))
			data |= m_board->read_rank(i);

	return data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void mondial68k_state::mondial68k_mem(address_map &map)
{
	map(0x000000, 0x00ffff).rom();
	map(0x800000, 0x800000).r(FUNC(mondial68k_state::inputs_r));
	map(0x820000, 0x82000f).nopr().w(m_lcd_latch, FUNC(hc259_device::write_d0)).umask16(0xff00);
	map(0x840000, 0x840000).w(FUNC(mondial68k_state::input_mux_w));
	map(0x860000, 0x860000).w(FUNC(mondial68k_state::board_mux_w));
	map(0xc00000, 0xc03fff).ram();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( mondial68k )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("A / 1") PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("E / 5 / Rook") PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Left / Black / 9") PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("LEV") PORT_CODE(KEYCODE_L)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("B / 2 / Pawn") PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("F / 6 / Queen") PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Right / White / 0") PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("MEM") PORT_CODE(KEYCODE_M)

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("C / 3 / Knight") PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("G / 7 / King") PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("INFO") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("CL") PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_F1) // combine ENT/CL for NEW GAME

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("D / 4 / Bishop") PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("H / 8") PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("POS") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("ENT") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CODE(KEYCODE_F1) // combine ENT/CL for NEW GAME
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void mondial68k_state::mondial68k(machine_config &config)
{
	// basic machine hardware
	M68000(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mondial68k_state::mondial68k_mem);

	const attotime irq_period = attotime::from_hz(32.768_kHz_XTAL / 256); // 128Hz
	m_maincpu->set_periodic_int(FUNC(mondial68k_state::irq6_line_hold), irq_period);

	HC259(config, m_lcd_latch);
	m_lcd_latch->q_out_cb<0>().set(m_lcd, FUNC(pcf2112_device::clb_w));
	m_lcd_latch->q_out_cb<1>().set(m_lcd, FUNC(pcf2112_device::data_w));
	m_lcd_latch->q_out_cb<2>().set(m_lcd, FUNC(pcf2112_device::dlen_w));
	m_lcd_latch->parallel_out_cb().set("dac", FUNC(dac_2bit_ones_complement_device::write)).rshift(6).mask(0x3);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(100));

	// video hardware
	PCF2112(config, m_lcd, 50); // frequency guessed
	m_lcd->write_segs().set(FUNC(mondial68k_state::lcd_output_w));

	PWM_DISPLAY(config, m_led_pwm).set_size(2, 8);
	config.set_default_layout(layout_mephisto_mondial68k);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_2BIT_ONES_COMPLEMENT(config, "dac").add_route(ALL_OUTPUTS, "speaker", 0.125);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( mondl68k )
	ROM_REGION16_BE( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("68000xl_u_06.11.87", 0x0000, 0x8000, CRC(aebe482a) SHA1(900c91ec836cd65e4cd38e50555976ab8064be41) )
	ROM_LOAD16_BYTE("68000xl_l_06.11.87", 0x0001, 0x8000, CRC(564e32c5) SHA1(8c9df46bc5ced114e72fb663f1055d775b8e2e0b) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT   COMPAT  MACHINE     INPUT       CLASS             INIT        COMPANY, FULLNAME, FLAGS
SYST( 1988, mondl68k, 0,       0,      mondial68k, mondial68k, mondial68k_state, empty_init, "Hegener + Glaser", "Mephisto Mondial 68000XL", MACHINE_SUPPORTS_SAVE )



monkey_king_3b.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Shah

/*

Monkey King SoCs (currently only 3B is supported)

Presumably-custom ARM-based system-on-chips by Digital Media Cartridge (DMC).
Intended to run NES and Genesis emulators, primarily for ATgames systems.

Sometimes abbreviated MK. It is a successor of the Titan SoC used in previous
emulation based ATgames systems.

Monkey King and Monkey 2: Presumed custom. Used in some ATgames/Blaze
Genesis systems and the Atari Flashback Portable.

Monkey King 3 and Monkey King 3B: Presumed custom. Used in the ATgames
BLAST system and the RS-70 648-in-1 "PS1 form factor" clone. Supports
HDMI output.

Monkey King 3.6: not a custom part but a rebranded RK3036, usually
running a cut-down Android based OS. Used in newer ATgames systems.

The typical configuration of the Monkey King SoCs (other than the
3.6) is with 8/16MB of SDRAM, NOR flash for the firmware and
built-in games, and a SD card for additional games.

The RS-70 is notable for having a debug UART on the USB port
(serial TX on D+, 115200). It prints the following messages on boot:

    EXEC: Executing 'boot' with 0 args (ZLib ON)...
    EXEC: Loading 'boot' at 0x18000000...
    EXEC: Loaded 372272 bytes of 2097152 available.

This is different from the serial output that this emulation model
currently produces. Perhaps one of the unimplemented IO is causing
it to go into some kind of debug mode. The log output produced by
this machine is:

    Modes:0x00000000
    PUT: Setting joystick to mode 0x0, timer to 250us

    ******************************************************
     MK FIRMWARE INFORMATION
     Mode:       0xB4
     Build Time: May  8 2019 14:09:21
     CPU Clock:  240MHz
     TFS Start:  0x8070000
     Video Buf:  0x6000000
     Stack Top:  0x3001EE8
     IWRAM Size: 32kB
     EVRAM Size: 16384kB
     Heap Size:  6144kB at 0x18200000
     Video Mode: 0
     Video Size: 1280x720x16bpp
    ******************************************************

There are other strings in the ROM that imply there may be more serial
debug possibilities.

TODO:
    implement everything
    add dumps of more Monkey King systems
*/

#include "emu.h"
#include "cpu/arm7/arm7.h"
#include "emupal.h"
#include "screen.h"


namespace {

class mk3b_soc_state : public driver_device
{
public:
	mk3b_soc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_iram0(*this, "iram0"),
		m_iram3(*this, "iram3"),
		m_sdram(*this, "sdram"),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen")
	{ }

	void mk3b_soc(machine_config &config);

	void init_rs70();

private:
	required_shared_ptr<uint32_t> m_iram0, m_iram3;
	required_shared_ptr<uint32_t> m_sdram;
	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;

	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;
	uint32_t io4_r(offs_t offset, uint32_t mem_mask = ~0);
	void io4_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);

	uint32_t io7_r(offs_t offset, uint32_t mem_mask = ~0);
	void io7_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);

	uint32_t io10_r(offs_t offset, uint32_t mem_mask = ~0);
	void io10_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);

	uint32_t screen_update_mk3b_soc(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void map(address_map &map) ATTR_COLD;
	std::string debug_buf;
};


void mk3b_soc_state::map(address_map &map)
{
	// 64MB external NOR flash
	map(0x08000000, 0x0BFFFFFF).rom().region("norflash", 0);
	// unknown amount and configuration of internal RAM
	map(0x00000000, 0x0000FFFF).ram().share("iram0");
	// This section of RAM seems to contain the stack
	map(0x03000000, 0x0300FFFF).ram().share("iram3");

	// 16MB of external SDRAM
	map(0x18000000, 0x18FFFFFF).ram().share("sdram");
	// IO is totally unknown for now
	map(0x04000000, 0x0400FFFF).rw(FUNC(mk3b_soc_state::io4_r), FUNC(mk3b_soc_state::io4_w));
	map(0x07000000, 0x0700FFFF).rw(FUNC(mk3b_soc_state::io7_r), FUNC(mk3b_soc_state::io7_w));
	map(0x10000000, 0x1000FFFF).rw(FUNC(mk3b_soc_state::io10_r), FUNC(mk3b_soc_state::io10_w));
}

static INPUT_PORTS_START( mk3b_soc )

INPUT_PORTS_END

void mk3b_soc_state::video_start()
{
}

void mk3b_soc_state::machine_reset()
{
	// In practice, this will probably be done by a small
	// internal boot ROM.
	m_iram0[0] = 0xe59f0000; // ldr r0, [pc]
	m_iram0[1] = 0xe12fff10; // bx, r0
	m_iram0[2] = 0x08000000; // target address
}

uint32_t mk3b_soc_state::screen_update_mk3b_soc(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void mk3b_soc_state::mk3b_soc(machine_config &config)
{
	// type unknown (should actually have VFP?)
	// debug output suggests 240MHz clock
	ARM920T(config, m_maincpu, 240000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &mk3b_soc_state::map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500) /* not accurate */);
	m_screen->set_size(1280, 720);
	m_screen->set_visarea(0, 1280-1, 0, 720-1);
	m_screen->set_screen_update(FUNC(mk3b_soc_state::screen_update_mk3b_soc));
}

uint32_t mk3b_soc_state::io4_r(offs_t offset, uint32_t mem_mask)
{
	switch (offset) {
		case 0x0001:
			return (m_screen->vblank() << 27) | m_screen->vblank(); // who knows? seems to need to toggle between 0 and 1
		case 0x021a:
			return m_screen->vblank() << 15; // same
		default:
			logerror("%s: IO 0x04 read 0x%04X\n", machine().describe_context(), offset);
			return 0x00;
	}
}

void mk3b_soc_state::io4_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	logerror("%s: IO 0x04 write 0x%04X 0x%08X & 0x%08X\n", machine().describe_context(), offset, data, mem_mask);
}


uint32_t mk3b_soc_state::io7_r(offs_t offset, uint32_t mem_mask)
{
	switch (offset) {
		case 0x21: // video size
			return (1280 << 16) | (720);
		default:
			logerror("%s: IO 0x07 read 0x%04X\n", machine().describe_context(), offset);
			return 0x00;
	}
}

void mk3b_soc_state::io7_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	logerror("%s: IO 0x07 write 0x%04X 0x%08X & 0x%08X\n", machine().describe_context(), offset, data, mem_mask);
}

uint32_t mk3b_soc_state::io10_r(offs_t offset, uint32_t mem_mask)
{
	switch (offset) {
		// Definitely not correct, but toggling somehow keeps things moving
		case 0x148:
		case 0x149:
			return m_screen->vblank() ? 0x00000000 : 0xFFFFFFFF;
		default:
			logerror("%s: IO 0x10 read 0x%04X\n", machine().describe_context(), offset);
			return 0x00;
	}
}

void mk3b_soc_state::io10_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	switch (offset) {
		case 0x148: { // debug UART
			char c =  data & 0xFF;
			logerror("%s: UART W: %c\n", machine().describe_context(), c);
			if (c == '\n') {
				logerror("%s: [DEBUG] %s\n", machine().describe_context(), debug_buf.c_str());
				debug_buf.clear();
			} else if (c != '\r') {
				debug_buf += c;
			}
			break;
		}
		default:
			logerror("%s: IO 0x10 write 0x%04X 0x%08X & 0x%08X\n", machine().describe_context(), offset, data, mem_mask);
	}
}

void mk3b_soc_state::init_rs70()
{
	// Uppermost address bit seems to be inverted
	uint8_t *ROM = memregion("norflash")->base();
	int size = memregion("norflash")->bytes();

	for (int i = 0; i < (size / 2); i++) {
		std::swap(ROM[i], ROM[i + (size / 2)]);
	}
	// FIXME: Work around missing FPU for now
	ROM[0x32f24] = 0x00;
	ROM[0x32f25] = 0x00;
	ROM[0x32f26] = 0x00;
	ROM[0x32f27] = 0x00;
}


ROM_START( afbp4 )
	ROM_REGION32_LE(0x04000000, "norflash", 0)
	ROM_LOAD("mx29lv640eb.bin", 0x000000, 0x00800000, CRC(477e8039) SHA1(ee0d351afece111816aca242ec4bc62d92e5eec2) )
ROM_END

ROM_START( rs70_648 )
	ROM_REGION32_LE(0x04000000, "norflash", 0)
	ROM_LOAD("s29gl512p.bin", 0x000000, 0x04000000, CRC(cb452bd7) SHA1(0b19a13a3d0b829725c10d64d7ff852ff5202ed0) )
ROM_END

} // anonymous namespace


CONS( 2019, afbp4,     0,        0, mk3b_soc, mk3b_soc, mk3b_soc_state, empty_init, "AtGames", "Atari Flashback Portable (version 4)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2019, rs70_648,  0,        0, mk3b_soc, mk3b_soc, mk3b_soc_state, init_rs70,  "CoolBoy", "RS-70 648-in-1",                       MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



monon_color.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/***************************************************************************
   Monon Color - Chinese handheld

   see
   https://gbatemp.net/threads/the-monon-color-a-new-video-game-console-from-china.467788/
   https://twitter.com/Splatoon2weird/status/1072182093206052864

   uses AX208 CPU (custom 8051 @ 96Mhz with single cycle instructions, extended '16 bit' opcodes + integrated video, jpeg decoder etc.)
   https://docplayer.net/52724058-Ax208-product-specification.html

   Emulation Notes:

   The Monon makes very little use of many of the AX208 features, for example

   - Encryption is only enabled once, for a throw-away transfer that is never
   checked.
   - JPEG RAM is simply used for extra RAM.
   - USB support not used
   - SD card support not used
   - Most IRQ levels not used
   - Onboard LCDC not used

   While the SFX used by each game are done through the AX208 DAC the music
   is not.

   Instead the music is supplied by a glob in each cartridge, which differs
   for each game.  This glob is assumed to be a self-contained MCU, responding
   to music requests.  Swapping the game ROM onto a different cartridge
   results in incorrect music, or silence, and in some cases hangs if you go
   into the volume adjustment menu.

   There is currently no way of reading the music data from this glob, nor
   has the exact die underneath it been identified.

   Games save data directly to the SPI ROM in the 0x50000-0x5ffff region

   Some games made use of either a card reader, or a badge reader on the
   cartridge, this is not yet emulated.  The card reader was based on
   barcode-like patterns, the badge reader looks to also be based on barcodes

   AXC51_TMR1PWML / AXC51_TMR1PWMH (Timer 1 Duty Registers) in the AX208
   core are responsible for screen brightness (can be adjusted in the pause
   menu)  There's no Timer 1 IRQ Handler in the code, but presumably the LCD
   is driven directly off the timer pins.

   Some games can be linked via the Infrared support provided by the AX208,
   this is not currently supported.

***************************************************************************/

#include "emu.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "bus/mononcol/slot.h"
#include "bus/mononcol/carts.h"

#include "cpu/axc51/axc51.h"
#include "cpu/m6502/m65ce02.h"
#include "sound/dac.h"

#define LOG_VDP (1U << 1)
#define LOG_MUSICMCUCOMMS (1U << 2)

//#define VERBOSE     (LOG_VDP)
#define VERBOSE     (0)

#include "logmacro.h"


namespace {

class monon_color_state : public driver_device
{
public:
	monon_color_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_cart(*this, "cartslot"),
		m_maincpu(*this, "maincpu"),
		m_musicmcu(*this, "musicmcu"),
		m_musicrom(*this, "musicmcu"),
		m_screen(*this, "screen"),
		m_palette(*this, "palette"),
		m_dac(*this, "dac"),
		m_debugin(*this, "DEBUG%u", 0U),
		m_controls(*this, "CONTROLS%u", 0U)
	{ }

	void monon_color(machine_config &config);
protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// screen updates
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

private:
	static constexpr unsigned VIDEO_WIDTH = 320;
	static constexpr unsigned VIDEO_HEIGHT = 240;

	required_device<mononcol_cartslot_device> m_cart;
	required_device<ax208_cpu_device> m_maincpu;
	required_device<m65ce02_device> m_musicmcu;
	required_region_ptr<uint8_t> m_musicrom;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<dac_8bit_r2r_twos_complement_device> m_dac;
	required_ioport_array<4> m_debugin;
	required_ioport_array<4> m_controls;

	rgb_t m_linebuf[VIDEO_HEIGHT];
	std::unique_ptr<rgb_t[]> m_vidbuffer;
	int16_t m_bufpos_y;
	uint32_t m_bufpos_x;
	uint8_t m_storeregs[0x20];
	uint8_t m_dacbyte;

	uint8_t m_out4data;
	uint8_t m_out3data;
	uint8_t m_out2data;
	uint8_t m_out1data;
	uint8_t m_out0data;

	uint8_t m_curpal[0x800 * 3];

	void music_mem(address_map &map) ATTR_COLD;
	uint8_t music_rts_r();

	uint8_t in0_r();
	uint8_t in1_r();
	uint8_t in2_r();
	uint8_t in3_r();
	uint8_t in4_r();
	void out0_w(uint8_t data);
	void out1_w(uint8_t data);
	void out2_w(uint8_t data);
	void out3_w(uint8_t data);
	void out4_w(uint8_t data);

	uint8_t read_current_inputs();

	uint8_t read_from_video_device();
	void write_to_video_device(uint8_t data);

	void dacout0_w(uint8_t data);
	void dacout1_w(uint8_t data);

	uint8_t spibuf_r()
	{
		return m_cart->read();
	}

	void spidir_w(int state)
	{
		m_cart->dir_w(state);
	}

	void spibuf_w(uint8_t data)
	{
		m_cart->write(data);
	}

	void get_music_command_bit(uint8_t bit);
	bool m_music_direction_iswrite;
	uint16_t m_music_latch;
	uint8_t m_music_bitpos;
	uint8_t m_music_response_bit;
	uint8_t m_music_bits_in_needed;

	void do_draw_inner(int pal_to_use, int start, int step, int pixmask, int amount);
	void do_draw(int amount, int pal_to_use);
	void do_palette(int amount, int pal_to_use);
};

void monon_color_state::machine_start()
{
	m_vidbuffer = std::make_unique<rgb_t[]>(VIDEO_WIDTH * VIDEO_HEIGHT);

	save_item(NAME(m_dacbyte));
	save_item(NAME(m_out4data));
	save_item(NAME(m_out3data));
	save_item(NAME(m_out2data));
	save_item(NAME(m_out1data));
	save_item(NAME(m_out0data));
	save_item(NAME(m_linebuf));
	save_pointer(NAME(m_vidbuffer), VIDEO_WIDTH * VIDEO_HEIGHT);
	save_item(NAME(m_bufpos_x));
	save_item(NAME(m_bufpos_y));
	save_item(NAME(m_curpal));
	save_item(NAME(m_storeregs));

	save_item(NAME(m_music_direction_iswrite));
	save_item(NAME(m_music_latch));
	save_item(NAME(m_music_bitpos));
	save_item(NAME(m_music_response_bit));
	save_item(NAME(m_music_bits_in_needed));
}


void monon_color_state::machine_reset()
{
	/*  block starting at e000 in flash is not code? (or encrypted?)
	    no code to map at 0x9000 in address space (possible BIOS?)
	    no code in flash ROM past the first 64kb(?) which is basically the same on all games, must be some kind of script interpreter? J2ME maybe?

	    there are 5 different 'versions' of the code in the dumped ROMs, where the code is the same the roms match up until 0x50000 after which the game specific data starts

	    by game number:

	    103alt                           (earliest? doesn't have bank9)
	    101,102,103,104,105              (1st revision)
	    106,107                          (2nd revision)
	    201                              (3rd revision)
	    202,203,204,205,301,302,303,304  (4th revision)
	*/

	m_dacbyte = 0;
	m_out2data = 0;

	m_out4data = 0;
	m_out3data = 0;
	m_out1data = 0;
	m_out0data = 0;
	m_bufpos_x = 0;
	m_bufpos_y = 239;

	std::fill(std::begin(m_linebuf), std::end(m_linebuf), 0);
	std::fill_n(m_vidbuffer.get(), VIDEO_WIDTH * VIDEO_HEIGHT, 0);
	std::fill(std::begin(m_storeregs), std::end(m_storeregs), 0);
	std::fill(std::begin(m_curpal), std::end(m_curpal), 0);

	m_music_direction_iswrite = true;
	m_music_latch = 0;
	m_music_bitpos = 0;
	m_music_response_bit = 0;
	m_music_bits_in_needed = 0;

	// don't need it running for now as the program is incomplete
	m_musicmcu->suspend(SUSPEND_REASON_DISABLE, 1);
}



uint32_t monon_color_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	rgb_t const *videoram = m_vidbuffer.get();

	for (int y = 0; y < VIDEO_HEIGHT; y++)
	{
		int count = y * VIDEO_WIDTH;
		for(int x = 0; x < VIDEO_WIDTH; x++)
		{
			rgb_t pixel = videoram[count++];
			bitmap.pix(y, x) = pixel;
		}
	}

	return 0;
}

static INPUT_PORTS_START( monon_color )
	PORT_START("DEBUG0") // Port 0
	PORT_DIPNAME( 0x01, 0x01, "PORT0" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN ) // input related (read bits here)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNKNOWN ) // input related (read bits here)
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("DEBUG1") // Port 1
	PORT_DIPNAME( 0x01, 0x01, "PORT1" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // input related (mux select bits here)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // input related (mux select bits here)
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("DEBUG2") // Port 2
	PORT_DIPNAME( 0x01, 0x01, "PORT2" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNKNOWN ) // input related (read bits here)
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("DEBUG3") // Port 4
	PORT_DIPNAME( 0x01, 0x01, "PORT4" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNKNOWN )  // input related (enables reading?)
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("CONTROLS0")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_BUTTON1 )

	PORT_START("CONTROLS1")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Right Trigger")

	PORT_START("CONTROLS2")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Left Trigger")

	PORT_START("CONTROLS3")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Menu")
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Pause")
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_UNUSED )

INPUT_PORTS_END

/*
    muxselect = p1 & 0x18;

    0x30 input bits in p0
    0x04 input bits in p2

    0x04 in port 4 combine inputs?
*/

uint8_t monon_color_state::read_current_inputs()
{
	uint8_t iomux = (m_out1data & 0x18) >> 3;

	if ((m_out4data & 0x04) == 0x00)
	{
		if (iomux == 0x00)
		{
			uint8_t in = (m_controls[0]->read() | m_controls[1]->read() | m_controls[2]->read() | m_controls[3]->read()) ^ 0x7;
			return in;
		}
		else if (iomux == 0x01)
		{
			return m_controls[0]->read() ^ 0x7;
		}
		else if (iomux == 0x02)
		{
			return m_controls[1]->read() ^ 0x7;
		}
		else if (iomux == 0x03)
		{
			return m_controls[2]->read() ^ 0x7;
		}
	}
	else
	{
		return m_controls[3]->read() ^ 0x7;
	}

	return 0x00;
}

uint8_t monon_color_state::in0_r()
{
	uint8_t in = read_current_inputs();
	uint8_t ret = m_debugin[0]->read() & 0xcf;
	if (in & 0x01) ret |= 0x10;
	if (in & 0x02) ret |= 0x20;

	return ret;
}

uint8_t monon_color_state::in1_r()
{
	uint8_t ret = m_debugin[1]->read() & 0xe7;
	ret |= m_out1data & 0x18;

	return ret;
}

uint8_t monon_color_state::in2_r()
{
	uint8_t in = read_current_inputs();
	uint8_t ret = m_out2data & 0x7b;
	ret |= m_music_response_bit << 7;
	if (in & 0x04) ret |= 0x04;

	return ret;
}

uint8_t monon_color_state::in3_r()
{
	return m_out3data;
}

uint8_t monon_color_state::in4_r()
{
	uint8_t ret = m_debugin[3]->read() & 0xfb;
	ret |= m_out4data & 0x04;
	return ret;
}

void monon_color_state::dacout0_w(uint8_t data)
{
	m_dacbyte ^= 1;

	if (m_dacbyte == 1)
		m_dac->write(data);
}

void monon_color_state::dacout1_w(uint8_t data)
{
	// mono sound, not used?
}



void monon_color_state::out0_w(uint8_t data)
{
	m_out0data = data & 0x1f;
}

void monon_color_state::out1_w(uint8_t data)
{
	// out1 goes e5, e5, e7, e7 when WRITING a byte to the video device
	//        or e3, e3, e7, e7 when READING a byte from the video device
	//
	// presumably this actually causes the data from P3 to be written to the
	// device, or data from the device to be latched in P3

	if ((m_out1data == 0xe5) && (data == 0xe7))
	{
		write_to_video_device(m_out3data);
	}
	else if ((m_out1data == 0xe3) && (data == 0xe7))
	{
		m_out3data = read_from_video_device();
	}

	m_out1data = data;

}

void monon_color_state::get_music_command_bit(uint8_t bit)
{
	bit &= 1;

	if (m_music_bitpos == 0)
	{
		// first bit isn't part of the data? but isn't 0/1 depending on if it's a read/write either?
		//LOGMASKED(LOG_MUSICMCUCOMMS, "%s: started read/write command\n", machine().describe_context());
		m_music_bitpos++;
	}
	else
	{
		if (m_music_direction_iswrite)
		{
			m_music_latch = m_music_latch << 1;
			m_music_latch |= bit;

			m_music_bitpos++;

			if (m_music_bitpos == 17)
			{
				//LOGMASKED(LOG_MUSICMCUCOMMS, "%s: sent sound word %04x to MCU\n", machine().describe_context(), m_music_latch);
				m_music_bitpos = 0;

				switch (m_music_latch & 0xf000)
				{

				case 0x0000:
					LOGMASKED(LOG_MUSICMCUCOMMS, "0-00x set volume to %04x\n", m_music_latch & 0x0fff);
					m_music_latch = 0;
					break;

				case 0x2000:
					LOGMASKED(LOG_MUSICMCUCOMMS, "2-00x play music %04x\n", m_music_latch & 0x0fff);
					m_music_latch = 0;
					break;

				case 0x3000:
					LOGMASKED(LOG_MUSICMCUCOMMS, "3-000 stop all %04x\n", m_music_latch & 0x0fff);
					m_music_latch = 0;
					break;

				case 0x4000:
					LOGMASKED(LOG_MUSICMCUCOMMS, "4-000 reset? %04x\n", m_music_latch & 0x0fff);
					m_music_latch = 0;
					break;

				case 0xa000:
					// Spams this command a variable amount of this between scenes

					// There is a protection check involving the axxx commands
					// The game expects to read a byte from the music MCU and
					// compares it with a byte in the SPI ROM from e000 - efff
					//
					// This data in the SPI ROM is 65ce02 code that maps at
					// 0x2000, so the check is presumably checking a random byte
					// from part of the internal music MCU ROM against the table
					// stored in ROM.  Sadly the internal program is larger than
					// the 0x1000 bytes of it that have been duplicated in the
					// SPI ROM for this check.
					//
					// If it doesn't get this, it will loop until by chance it gets the same
					// value, which results in random delays (eg. when moving the volume
					// slider from 0 to any other value)
					//
					// mechcycla (the game with the earliest firmware) will jump to an infinite
					// loop if this is wrong, this doesn't happen with mechcycl, which uses a
					// later firmware.
					//
					// unfortunately no way of getting the MCU to read out anytihng
					// other than the single 0x1000 range it checks using the axxx commands
					// has been found.

					LOGMASKED(LOG_MUSICMCUCOMMS, "a-xxx status return wanted %04x\n", m_music_latch & 0x0fff);
					m_music_direction_iswrite = false;
					m_music_bits_in_needed = 9;
					m_music_latch = m_musicrom[m_music_latch & 0xfff];
					break;

				default:
					LOGMASKED(LOG_MUSICMCUCOMMS, "x-xxx unknown MCU write %04x\n", m_music_latch);
					m_music_latch = 0;
					break;

				}
			}

		}
		else
		{
			m_music_response_bit = (m_music_latch & 0x80)>>7;
			m_music_latch <<= 1;

			m_music_bitpos++;

			if (m_music_bitpos == m_music_bits_in_needed)
			{
				//LOGMASKED(LOG_MUSICMCUCOMMS, "%s: finished clocking in MCU response to read?\n", machine().describe_context());
				m_music_bitpos = 0;
				m_music_direction_iswrite = true;
			}
		}
	}
}

void monon_color_state::out2_w(uint8_t data)
{
	// on the cartridge PCB the connections to the music MCU glob are marked P21, P25 and P27
	// This is where the signals go on the AX208 (Port 2 bit 1, Port 2 bit 5, Port 2 bit 7)

	// m-C- --Ms

	// m = from MCU serial data (in)
	// C = clock MCU serial data (out)
	// M = to MCU serial data (out)
	// s = SPI Chip Enable / Reset?

	if ((data & 0x01) != (m_out2data & 0x01))
	{
		if (data & 0x01)
		{
			// nothing?
		}
		else
		{
			m_cart->set_ready();
		}
	}

	if ((data & 0x20) != (m_out2data & 0x20))
	{
		if (data & 0x20)
		{
			// .. nothing
		}
		else
		{
			// sends m_out2data & 0x02 to external device / latches a read bit into m_out2data & 0x80
			//logerror("%s: send / receive music MCU, bit written %d\n", machine().describe_context(), (data & 0x02) >> 1);
			get_music_command_bit((data & 0x02) >> 1);
		}
	}

	m_out2data = data;
}

uint8_t monon_color_state::read_from_video_device()
{
	LOGMASKED(LOG_VDP,"%s: read_from_video_device (m_out0data is %02x)\n", machine().describe_context(), m_out0data);

	if (m_out0data == 0x0c)
		return 0xff ^ 0x02;

	if (m_out0data == 0x1b)
		return 0x00;

	if (m_out0data == 0x17)
		return 0x00;

	return 0x00;
}

void monon_color_state::do_draw_inner(int pal_to_use, int start, int step, int pixmask, int amount)
{
	uint8_t blend;

	if (m_storeregs[0x1c] == 0x0a) // see drgnbma main character, doesn't set this, expects solid
		blend = (m_storeregs[0x09] & 0x3f) << 2;
	else
		blend = 0;

	int yadjust = (m_storeregs[0x16] << 8) | m_storeregs[0x1a];
	yadjust >>= 7;
	yadjust -= 8;

	for (int i = 0; i < amount; i++)
	{
		spibuf_w(0x00); // clock
		uint8_t pix = spibuf_r();

		for (int i = start; i >= 0; i -= step)
		{
			int real_ypos = m_bufpos_y;
			real_ypos -= yadjust;
			if ((real_ypos >= 0) && (real_ypos < VIDEO_HEIGHT))
			{
				uint8_t pixx = (pix >> i) & pixmask;
				uint8_t newr = m_curpal[((pixx + pal_to_use) * 3) + 2];
				uint8_t newg = m_curpal[((pixx + pal_to_use) * 3) + 1];
				uint8_t newb = m_curpal[((pixx + pal_to_use) * 3) + 0];
				rgb_t rgb = rgb_t(newr, newg, newb);

				if (rgb != rgb_t(0xdc, 0x32, 0xdc)) // magic transparency colour?!
				{

					if (blend != 0x00)
					{
						rgb_t behind = m_linebuf[real_ypos];
						behind.scale8(0xff - blend);
						rgb.scale8(blend);

						rgb += behind;
						m_linebuf[real_ypos] = rgb;
					}
					else
					{
						m_linebuf[real_ypos] = rgb;

					}
				}
			}
			m_bufpos_y--;
		}
	}
}
void monon_color_state::do_draw(int amount, int pal_to_use)
{
	if (m_storeregs[0x12] == 0x13)  // 8bpp mode
	{
		do_draw_inner(pal_to_use, 0, 1, 0xff, amount);
	}
	else if (m_storeregs[0x12] == 0x12) // 4bpp mode
	{
		do_draw_inner(pal_to_use, 4, 4, 0xf, amount);
	}
	else if (m_storeregs[0x12] == 0x11) // 2bpp mode
	{
		do_draw_inner(pal_to_use, 6, 2, 0x3, amount);
	}
	else if (m_storeregs[0x12] == 0x10)  // 1bpp mode
	{
		do_draw_inner(pal_to_use, 7, 1, 1, amount);
	}

	m_bufpos_y = 239;
}

void monon_color_state::do_palette(int amount, int pal_to_use)
{
	for (int i = 0; i < amount; i++)
	{
		spibuf_w(0x00); // clock
		uint8_t romdat = spibuf_r();

		int address = i + pal_to_use * 3;
		m_curpal[address] = romdat;
		int entry = address / 3;

		m_palette->set_pen_color(entry, rgb_t(m_curpal[(entry * 3) + 2], m_curpal[(entry * 3) + 1], m_curpal[(entry * 3) + 0]));
	}
}



void monon_color_state::write_to_video_device(uint8_t data)
{
	static const char* const names[] =
	{
		"(Direct Data)", // Direct Data port, after setting 0x8 / 0x04 ? writes basic 'maximum intensity' R/G/B palettes here on startup
		"(Transfer Size MSB)",
		"(Allow/Disallow SPI Access?)",
		"(unknown 0x03)",
		"(Direct VRAM Address Select MSB?)",
		"(unknown 0x05)",
		"(unknown 0x06)",
		"(unknown 0x07)",
		"(Direct VRAM Address Select LSB?)",
		"(Blend level, when enabled)",
		"(unknown 0x0a)",
		"(unknown 0x0b)",
		"(Trigger Transfer/Mode)",
		"(unknown 0x0d)",
		"(Transfer Size LSB)",
		"(unknown 0x0f)",
		"(Configure Magic Trans Pen B?)", // on startup
		"(Palette Select)",
		"(BPP Select)",
		"(unknown 0x13)",
		"(Configure Magic Trans Pen R?)", // on startup
		"(unknown 0x15)",
		"(Y Adjust MSB)",
		"(used 0x17)", // some kind of command / data? (writes a 320x240 set of 00 here at startup, followed by a 0x1f 'clock' write every time?)
		"(Configure Magic Trans Pen G?)", // on startup
		"(unknown 0x19)",
		"(Y Adjust LSB)",
		"(Column Advance+More?)",
		"(Draw Mode, alpha enable, trans pen disable?)",
		"(unknown 0x1d)",
		"(Extra Palette Select?)", // written when there is text on the screen some kind of extra base select (LSB?) in 1bpp mode? seems to be for palettes directly uploaded at startup
		"(Clock for 0x17?)", // clock for 0x17? always writes after reading / writing 0x1f?
	};

	LOGMASKED(LOG_VDP, "%s: write_to_video_device (m_out0data is %02x) %s (data is %02x)\n", machine().describe_context(), m_out0data, names[m_out0data], data);

	if (m_out4data == 0x03)
	{
		if (m_out0data < 0x20)
		{
			if (m_out0data >= 0x10) // when out4data is 0x03 registers(?) used are also always >= 0x10?
				m_storeregs[m_out0data] = data;
		}

		//  popmessage("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x | %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", m_storeregs[0x00], m_storeregs[0x01], m_storeregs[0x02], m_storeregs[0x03], m_storeregs[0x04], m_storeregs[0x05], m_storeregs[0x06], m_storeregs[0x07], m_storeregs[0x08], m_storeregs[0x09], m_storeregs[0x0a], m_storeregs[0x0b], m_storeregs[0x0c], m_storeregs[0x0d], m_storeregs[0x0e], m_storeregs[0x0f],
		//      m_storeregs[0x10], m_storeregs[0x11], m_storeregs[0x12], m_storeregs[0x13], m_storeregs[0x14], m_storeregs[0x15], m_storeregs[0x16], m_storeregs[0x17], m_storeregs[0x18], m_storeregs[0x19], m_storeregs[0x1a], m_storeregs[0x1b], m_storeregs[0x1c], m_storeregs[0x1d], m_storeregs[0x1e], m_storeregs[0x1f]);

	}
	else if (m_out4data == 0x09)
	{
		if (m_out0data < 0x20)
		{
			if (m_out0data < 0x10) // when out4data is 0x09 registers(?) used are also always < 0x10?
			{
				m_storeregs[m_out0data] = data;
			}
		}
	}

	// it uploads some colours at startup (and sometimes changes values using this during the game)
	// it is unclear how the address of 0x8e relates to palette entries 0x400+
	// these seem to be used for the 1bpp text see purcfs name entry, lolfight stats
	if (m_out0data == 0x00)
	{
		if (m_storeregs[0x04] == 0x8e)
		{
			int addressx = (m_storeregs[0x08]++);
			int address = addressx + 0x400 * 3;
			m_curpal[address] = data;
			int entry = address / 3;
			m_palette->set_pen_color(entry, rgb_t(m_curpal[(entry * 3) + 2], m_curpal[(entry * 3) + 1], m_curpal[(entry * 3) + 0]));
		}
	}

	if (m_out0data == 0x1b)
	{
		if (data == 0x81)
		{
			LOGMASKED(LOG_VDP, "Finished Column %d\n", m_bufpos_x);
			m_bufpos_x++;

			if (m_bufpos_x == VIDEO_WIDTH)
			{
				LOGMASKED(LOG_VDP, "------------------------------------------------------------------------------------------------\n");
				m_bufpos_x = 0;
			}

			m_bufpos_y = 239;
		}
	}

	//  (m_storeregs[0x1c] == 0x11) backgrounds (maybe no trans pen?)
	//  (m_storeregs[0x1c] == 0x01) most elements
	//  (m_storeregs[0x1c] == 0x00) some elements in purceb and zombhunt
	//  (m_storeregs[0x1c] == 0x0a) blended elements

	if (m_out0data == 0x0c)
	{
		if ((m_storeregs[0x1c] == 0x00) || (m_storeregs[0x1c] == 0x0a) || (m_storeregs[0x1c] == 0x01) || (m_storeregs[0x1c] == 0x11))
		{
			uint16_t amount = m_storeregs[0x0e] | (m_storeregs[0x01] << 8);

			//if (amount != 0x00)
			{
				int pal_to_use = (m_storeregs[0x11] << 8) | m_storeregs[0x1e];

				pal_to_use >>= 5;
				pal_to_use &= 0x7ff;


				// d4/d6 are odd-even columns
				// d0/d2 are odd-even columns, after frame data? (writing them seems conditional on something else?)
				// da/de are palettes? (maybe bit 0x04 is just ignored here, these aren't column specific)

				if ((data == 0xde) || (data == 0xda))
				{
					if (m_storeregs[0x02] == 0x20)
						do_palette(amount, pal_to_use);
				}
				else if ((data == 0xd2) || (data == 0xd6))
				{
					if (m_storeregs[0x02] == 0x20)
						do_draw(amount, pal_to_use);

				}
				else if ((data == 0xd0) || (data == 0xd4))
				{
					// assume there's some kind of line column buffer, the exact swap trigger is unknown
					for (int i = 0; i < VIDEO_HEIGHT; i++)
						m_vidbuffer[(i * VIDEO_WIDTH) + m_bufpos_x] = m_linebuf[i];
				}
			}
		}
		else
		{
			fatalerror("m_storeregs[0x1c] set to unknown value %02x while drawing\n", m_storeregs[0x1c]);
		}
	}
}

void monon_color_state::out3_w(uint8_t data)
{
	m_out3data = data;
}

void monon_color_state::out4_w(uint8_t data)
{
	m_out4data = data;
}


uint8_t monon_color_state::music_rts_r()
{
	return 0x60;
}

void monon_color_state::music_mem(address_map &map)
{
	map(0x0000, 0x01ff).ram();

	map(0x2000, 0x2fff).rom().region("musicmcu", 0x0000);
	map(0x3000, 0x3fff).r(FUNC(monon_color_state::music_rts_r)); // likely ROM here, lots of JSR calls

	map(0xa000, 0xafff).r(FUNC(monon_color_state::music_rts_r)); // likely ROM here, lots of JSR calls

	map(0xfff0, 0xffff).rom().region("musicmcu", 0x1ff0);
}


void monon_color_state::monon_color(machine_config &config)
{
	/* basic machine hardware */
	AX208(config, m_maincpu, 96000000/2); // divider is not correct, need to check if this is configured to run at full speed
	m_maincpu->port_in_cb<0>().set(FUNC(monon_color_state::in0_r));
	m_maincpu->port_in_cb<1>().set(FUNC(monon_color_state::in1_r));
	m_maincpu->port_in_cb<2>().set(FUNC(monon_color_state::in2_r));
	m_maincpu->port_in_cb<3>().set(FUNC(monon_color_state::in3_r));
	m_maincpu->port_in_cb<4>().set(FUNC(monon_color_state::in4_r));
	m_maincpu->port_out_cb<0>().set(FUNC(monon_color_state::out0_w));
	m_maincpu->port_out_cb<1>().set(FUNC(monon_color_state::out1_w));
	m_maincpu->port_out_cb<2>().set(FUNC(monon_color_state::out2_w));
	m_maincpu->port_out_cb<3>().set(FUNC(monon_color_state::out3_w));
	m_maincpu->port_out_cb<4>().set(FUNC(monon_color_state::out4_w));
	m_maincpu->spi_in_cb().set(FUNC(monon_color_state::spibuf_r));
	m_maincpu->spi_out_cb().set(FUNC(monon_color_state::spibuf_w));
	m_maincpu->spi_out_dir_cb().set(FUNC(monon_color_state::spidir_w));
	m_maincpu->dac_out_cb<0>().set(FUNC(monon_color_state::dacout0_w));
	m_maincpu->dac_out_cb<1>().set(FUNC(monon_color_state::dacout1_w));

	M65CE02(config, m_musicmcu, 4000000);
	m_musicmcu->set_addrmap(AS_PROGRAM, &monon_color_state::music_mem);

	/* video hardware */

	// is this a 3:4 (true vertical) 240x320 screen rotated on its side?
	// the scan direction for drawing is highly unusual
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(120);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(0));
	m_screen->set_screen_update(FUNC(monon_color_state::screen_update));
	m_screen->set_size(VIDEO_WIDTH, VIDEO_HEIGHT);
	m_screen->set_visarea(0*8, VIDEO_WIDTH-1, 0*8, VIDEO_HEIGHT-1);

	PALETTE(config, m_palette).set_entries(0x800);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	DAC_8BIT_R2R_TWOS_COMPLEMENT(config, m_dac, 0).add_route(ALL_OUTPUTS, "mono", 0.500); // should this be in the AX208 device?

	mononcol_cartslot_device &cartslot(MONONCOL_CARTSLOT(config, "cartslot", mononcol_plain_slot));
	cartslot.set_must_be_loaded(true);

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("monon_color");
}

ROM_START( mononcol )
	// TODO: each cartridge has a music MCU inside of it.  This data appears to be 0x1000 bytes of the
	//       program that is checked as 'security' and can be found in the SPI ROMs.  A way to dump
	//       the full ROM is needed, at which point the game specific music MCU ROMs should be
	//       moved into the software list.  This just allows us to study what exists of this sound
	//       program until then.
	ROM_REGION( 0x2000, "musicmcu", ROMREGION_ERASEFF )
	ROM_LOAD( "music_mcu.bin", 0x0000, 0x1000, BAD_DUMP CRC(54224c67) SHA1(4a4e1d6995e6c8a36e3979bc22b8e9a1ea8f954f) )
	// fake boot vector, as we don't have one, but the code above expects to boot at 0x2000
	ROM_FILL( 0x1ffc, 0x1, 0x00 )
	ROM_FILL( 0x1ffd, 0x1, 0x20 )
ROM_END

} // anonymous namespace


CONS( 2014, mononcol,    0,          0,  monon_color,  monon_color,    monon_color_state, empty_init,    "M&D",   "Monon Color", MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )



montec.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco, hap
/*******************************************************************************

Mephisto Monte Carlo
Mephisto Monte Carlo IV
Mephisto Monte Carlo IV: Limited Edition

The chess engine is by Frans Morsch, but for the IV version it's by Ed Schroeder.
Limited Edition has a twice faster CPU.

Hardware notes:
- R65C02P4 @ 4MHz
- 8KB RAM(battery-backed), 32KB ROM
- expansion slot at underside (not used)
- 2*PCF2112, 2 7seg LCD screens
- 8 tri-color leds (like academy, always 6 red and 2 green?)
- magnetic chessboard with 64 leds, piezo

*******************************************************************************/

#include "emu.h"

#include "mmboard.h"

#include "cpu/m6502/r65c02.h"
#include "cpu/m6502/w65c02.h"
#include "machine/74259.h"
#include "machine/nvram.h"
#include "sound/dac.h"
#include "video/pcf2100.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "mephisto_montec.lh"


namespace {

class montec_state : public driver_device
{
public:
	montec_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_lcd_latch(*this, "lcd_latch"),
		m_led_pwm(*this, "led_pwm"),
		m_lcd(*this, "lcd%u", 0),
		m_keys(*this, "KEY.%u", 0),
		m_digits(*this, "digit%u", 0U)
	{ }

	void montec(machine_config &config);
	void montec4(machine_config &config);
	void montec4le(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<mephisto_board_device> m_board;
	required_device<hc259_device> m_lcd_latch;
	required_device<pwm_display_device> m_led_pwm;
	required_device_array<pcf2112_device, 2> m_lcd;
	required_ioport_array<2> m_keys;
	output_finder<8> m_digits;

	void montec_mem(address_map &map) ATTR_COLD;

	template<int N> void lcd_output_w(u32 data);
	void led_w(u8 data);
	void irq_ack_w(u8 data) { irq_ack_r(); }
	u8 irq_ack_r();
	u8 input_r();
};

void montec_state::machine_start()
{
	m_digits.resolve();
}



/*******************************************************************************
    I/O
*******************************************************************************/

template<int N>
void montec_state::lcd_output_w(u32 data)
{
	// lcd segment outputs
	for (int i = 0; i < 4; i++)
		m_digits[i + N*4] = bitswap<8>(data >> (8 * i), 7,4,5,0,1,2,3,6);
}

void montec_state::led_w(u8 data)
{
	// d0-d3: keypad led select
	// d4-d7: keypad led data
	m_led_pwm->matrix(data & 0xf, ~data >> 4 & 0xf);
}

u8 montec_state::irq_ack_r()
{
	if (!machine().side_effects_disabled())
		m_maincpu->set_input_line(0, CLEAR_LINE);

	return 0;
}

u8 montec_state::input_r()
{
	u8 data = 0;

	// 74259 q0/q1 selects keypad
	for (int i = 0; i < 2; i++)
		if (!BIT(m_lcd_latch->output_state(), i))
			data |= m_keys[i]->read();

	return ~m_board->input_r() | data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void montec_state::montec_mem(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("nvram");
	map(0x2000, 0x2000).rw(FUNC(montec_state::irq_ack_r), FUNC(montec_state::irq_ack_w));
	map(0x2400, 0x2400).r(FUNC(montec_state::input_r));
	map(0x2800, 0x2800).w(m_board, FUNC(mephisto_board_device::mux_w));
	map(0x2c00, 0x2c00).w(m_board, FUNC(mephisto_board_device::led_w));
	map(0x3000, 0x3007).w(m_lcd_latch, FUNC(hc259_device::write_d7)).nopr();
	map(0x3400, 0x3400).w(FUNC(montec_state::led_w));
	map(0x8000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( montec )
	PORT_START("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("BOOK / 9")   PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_B)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("POS / 0")    PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_P)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("MEM")        PORT_CODE(KEYCODE_M)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("INFO")       PORT_CODE(KEYCODE_I)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("CL")         PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("LEV")        PORT_CODE(KEYCODE_L)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("ENT")        PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("RES")        PORT_CODE(KEYCODE_F1)

	PORT_START("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Pawn / 1")   PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Knight / 2") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Bishop / 3") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Rook / 4")   PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Queen / 5")  PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("King / 6")   PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Black / 7")  PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("White / 8")  PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void montec_state::montec(machine_config &config)
{
	// basic machine hardware
	R65C02(config, m_maincpu, 8_MHz_XTAL / 2); // R65C02P4
	m_maincpu->set_addrmap(AS_PROGRAM, &montec_state::montec_mem);

	const attotime irq_period = attotime::from_hz(8_MHz_XTAL / 2 / 0x2000);
	m_maincpu->set_periodic_int(FUNC(montec_state::irq0_line_assert), irq_period);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	HC259(config, m_lcd_latch);
	m_lcd_latch->q_out_cb<2>().set("dac", FUNC(dac_1bit_device::write)).invert();
	m_lcd_latch->q_out_cb<4>().set(m_lcd[0], FUNC(pcf2112_device::data_w));
	m_lcd_latch->q_out_cb<4>().append(m_lcd[1], FUNC(pcf2112_device::data_w));
	m_lcd_latch->q_out_cb<5>().set(m_lcd[1], FUNC(pcf2112_device::dlen_w));
	m_lcd_latch->q_out_cb<6>().set(m_lcd[0], FUNC(pcf2112_device::clb_w));
	m_lcd_latch->q_out_cb<6>().append(m_lcd[1], FUNC(pcf2112_device::clb_w));
	m_lcd_latch->q_out_cb<7>().set(m_lcd[0], FUNC(pcf2112_device::dlen_w));

	MEPHISTO_SENSORS_BOARD(config, m_board); // internal
	m_board->set_delay(attotime::from_msec(300));

	// video hardware
	PCF2112(config, m_lcd[0], 50); // frequency guessed
	m_lcd[0]->write_segs().set(FUNC(montec_state::lcd_output_w<0>));
	PCF2112(config, m_lcd[1], 50); // "
	m_lcd[1]->write_segs().set(FUNC(montec_state::lcd_output_w<1>));

	PWM_DISPLAY(config, m_led_pwm).set_size(4, 4);
	config.set_default_layout(layout_mephisto_montec);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, "dac").add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void montec_state::montec4(machine_config &config)
{
	montec(config);
	m_board->set_delay(attotime::from_msec(150));
}

void montec_state::montec4le(machine_config &config)
{
	montec4(config);

	// basic machine hardware
	W65C02(config.replace(), m_maincpu, 8_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &montec_state::montec_mem);

	const attotime irq_period = attotime::from_hz(8_MHz_XTAL / 0x4000);
	m_maincpu->set_periodic_int(FUNC(montec_state::irq0_line_assert), irq_period);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( montec )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("mc3_12.11.87", 0x8000, 0x8000, CRC(8eb26043) SHA1(26454a37eea29283bbb2762a3a68e95e4be6aa1c) )
ROM_END

ROM_START( monteca )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("mc2_20.7.87", 0x8000, 0x8000, CRC(05524da9) SHA1(bee2ffe09a27095f733584e0fb1203b95c23e17e) )
ROM_END


ROM_START( montec4 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("mc4.bin", 0x8000, 0x8000, CRC(b231be57) SHA1(8bb39db6a3f476090574ef6c681a241a96cfbd5c) )
ROM_END

ROM_START( montec4le )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("mc4le.bin", 0x8000, 0x8000, CRC(c4887694) SHA1(7f482d2a40fcb3125266e7a5407da315b4f9b49c) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME        PARENT    COMPAT  MACHINE    INPUT      CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1987, montec,     0,        0,      montec,    montec,    montec_state, empty_init, "Hegener + Glaser", "Mephisto Monte Carlo (ver. MC3)", MACHINE_SUPPORTS_SAVE )
SYST( 1987, monteca,    montec,   0,      montec,    montec,    montec_state, empty_init, "Hegener + Glaser", "Mephisto Monte Carlo (ver. MC2)", MACHINE_SUPPORTS_SAVE )

SYST( 1989, montec4,    0,        0,      montec4,   montec,    montec_state, empty_init, "Hegener + Glaser", "Mephisto Monte Carlo IV", MACHINE_SUPPORTS_SAVE )
SYST( 1990, montec4le,  montec4,  0,      montec4le, montec,    montec_state, empty_init, "Hegener + Glaser", "Mephisto Monte Carlo IV: Limited Edition", MACHINE_SUPPORTS_SAVE )



monty.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Andrew Gardner, hap
/*******************************************************************************

Driver for Ritam Monty Plays Scrabble Brand Crossword Game - Portable Computer Console

Scrabble computer that allows you play a game of Scrabble by yourself (or you
can play with up to 3 players). Has a built-in 12,000 vocabulary, expandable
to 44,000 by way of 2 expansion modules each containing 16,000 more obscure words.
You can use the included 'score cards' (which look like little Scrabble boards),
or you can use a real Scrabble board and tiles to play.  Also note, Monty
apparently originally came with a little pen.

This game was later upgraded by Ritam to Master Monty which had 24,000 words
built-in (expandable to a total of 56,000 with the same 2 expansion modules).
Two variations on Master Monty have been seen: one looks exactly the same as the
Monty but the electronics on the inside have been upgraded.  The later version
is blue and says Master Monty at the top.  Both of these versions are hand-upgraded
by adding chips and wires to the inside of the game.

Hardware notes:
- Z80, 3.58MT ceramic resonator
- 2KB SRAM, 16KB ROM(32KB on mmonty)
- 2*16KB ROM sockets for vocabulary expansion
- 2*SED1503F, 40*32 LCD screen, beeper

TODO:
- put expansion roms in softwarelist? (note: slot #1 only accepts rom #1,
  slot #2 only accepts rom #2), they can be disabled in-game with option M

*******************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "sound/dac.h"
#include "video/sed1500.h"

#include "screen.h"
#include "speaker.h"

#include "monty.lh"


namespace {

class monty_state : public driver_device
{
public:
	monty_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcd(*this, "lcd%u", 0)
		, m_dac(*this, "dac")
		, m_inputs(*this, "IN.%u", 0)
	{ }

	void key_pressed(int state);

	void monty(machine_config &config);
	void mmonty(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;
	required_device_array<sed1503_device, 2> m_lcd;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<6> m_inputs;

	u64 m_lcd_data[32] = { };
	int m_lcd_cs = 0;
	int m_halt = 0;

	void monty_mem(address_map &map) ATTR_COLD;
	void mmonty_mem(address_map &map) ATTR_COLD;
	void monty_io(address_map &map) ATTR_COLD;

	template<int N> void lcd_output_w(offs_t offset, u64 data) { m_lcd_data[N << 4 | offset] = data; } // buffer for screen_update
	u32 screen_update(screen_device& screen, bitmap_rgb32& bitmap, const rectangle& cliprect);

	void control_w(offs_t offset, u8 data);
	void lcd_w(offs_t offset, u8 data) { m_lcd[m_lcd_cs]->write(offset, data); }
	u8 input_r(offs_t offset);
	void halt_changed(int state) { m_halt = state; }
};

void monty_state::machine_start()
{
	save_item(NAME(m_lcd_data));
	save_item(NAME(m_lcd_cs));
	save_item(NAME(m_halt));
}



/*******************************************************************************
    Video
*******************************************************************************/

u32 monty_state::screen_update(screen_device& screen, bitmap_rgb32& bitmap, const rectangle& cliprect)
{
	bitmap.fill(0xffffff);

	// letters with width 5 with space in between them
	for (int y = 0; y < 32; y++)
		for (int x = 0; x < 40; x++)
			bitmap.pix(y + 1, x + x/5 + 1) = BIT(m_lcd_data[y], x) ? 0 : 0xffffff;

	return 0;
}



/*******************************************************************************
    I/O
*******************************************************************************/

void monty_state::control_w(offs_t offset, u8 data)
{
	// a0: speaker out
	m_dac->write(BIT(offset, 0));

	// a1: lcd chip select
	m_lcd_cs = BIT(offset, 1);
}

u8 monty_state::input_r(offs_t offset)
{
	u8 data = 0;

	// a0-a5, d0-d4: multiplexed inputs
	for (int i = 0; i < 6; i++)
		if (BIT(offset, i))
			data |= m_inputs[i]->read();

	return ~data;
}

void monty_state::key_pressed(int state)
{
	if (state && m_halt)
		m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void monty_state::monty_mem(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	map(0x4000, 0x7fff).rom(); // slot 1
	map(0x8000, 0xbfff).rom(); // slot 2
	map(0xf800, 0xffff).ram();
}

void monty_state::mmonty_mem(address_map &map)
{
	map(0x0000, 0x77ff).rom();
	map(0x7800, 0x7fff).ram();
	map(0x8000, 0xbfff).rom(); // slot 2
	map(0xc000, 0xffff).rom(); // slot 1
}

void monty_state::monty_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0xff).r(FUNC(monty_state::input_r));
	map(0x00, 0x03).w(FUNC(monty_state::control_w));
	map(0x80, 0xff).w(FUNC(monty_state::lcd_w));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( monty )
	PORT_START("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Erase") PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Option") PORT_CODE(KEYCODE_SPACE) PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Blank") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))

	PORT_START("IN.1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))

	PORT_START("IN.2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))

	PORT_START("IN.3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CODE(KEYCODE_DOWN) PORT_CHAR('M') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))

	PORT_START("IN.4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR('I') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_LEFT) PORT_CHAR('G') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))

	PORT_START("IN.5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_UP) PORT_CHAR('C') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_WRITE_LINE_MEMBER(FUNC(monty_state::key_pressed))
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void monty_state::monty(machine_config &config)
{
	// Basic machine hardware
	Z80(config, m_maincpu, 3.58_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &monty_state::monty_mem);
	m_maincpu->set_addrmap(AS_IO, &monty_state::monty_io);
	m_maincpu->halt_cb().set(FUNC(monty_state::halt_changed));

	// Video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(0);
	screen.set_size(40+8+1, 32+1);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(monty_state::screen_update));

	SED1503(config, m_lcd[0], 32768).write_segs().set(FUNC(monty_state::lcd_output_w<0>));
	SED1503(config, m_lcd[1], 32768).write_segs().set(FUNC(monty_state::lcd_output_w<1>));
	config.set_default_layout(layout_monty);

	// Sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void monty_state::mmonty(machine_config &config)
{
	monty(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &monty_state::mmonty_mem);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( monty )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "monty_main.bin",    0x0000, 0x4000, CRC(720b4f55) SHA1(0106eb88d3fbbf25a745b9b6ee785ba13689d095) ) // 27128
	ROM_LOAD( "monty_module1.bin", 0x4000, 0x4000, CRC(2725d8c3) SHA1(8273b9779c0915f9c7c43ea4fb460f43ce036358) ) // 27128
	ROM_LOAD( "monty_module2.bin", 0x8000, 0x4000, CRC(db672e47) SHA1(bb14fe86df06cfa4b19625ba417d1a5bc8eae155) ) // 27128
ROM_END

ROM_START( mmonty )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "master_monty_main.bin", 0x0000, 0x8000, CRC(bb5ef4d4) SHA1(ba2c759e429f8740df419f9abb60832eddfba8ab) ) // 27C256
	ROM_LOAD( "monty_module2.bin",     0x8000, 0x4000, CRC(db672e47) SHA1(bb14fe86df06cfa4b19625ba417d1a5bc8eae155) ) // 27128
	ROM_LOAD( "monty_module1.bin",     0xc000, 0x4000, CRC(2725d8c3) SHA1(8273b9779c0915f9c7c43ea4fb460f43ce036358) ) // 27128
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME                FLAGS
SYST( 1983, monty,  0,      0,      monty,   monty, monty_state, empty_init, "Ritam", "Monty Plays Scrabble", MACHINE_SUPPORTS_SAVE )
SYST( 1987, mmonty, 0,      0,      mmonty,  monty, monty_state, empty_init, "Ritam", "Master Monty",         MACHINE_SUPPORTS_SAVE )



mpc3000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    mpc3000.cpp - Akai / Roger Linn MPC-3000 music workstation
    Skeleton by R. Belmont

    Hardware:
        CPU: NEC V53 (33 MHz?)
             8086-compatible CPU
             8237-compatible DMA controller
             8254-compatible timer
             8259-compatible IRQ controller
        Floppy: uPD72069
        SCSI: MB89352
        LCD: LC7981
        Quad-UART: TE7774
        Panel controller CPU: NEC uPD78C10AGQ @ 12 MHz
        Sound DSP: L7A1045-L6048
            DSP's wavedata bus is 16 bits wide and has 24 address bits

        DMA channel 0 is SCSI, 1 is floppy, 2 is IC31 (some sort of direct-audio stream?), and 3 is the L7A1045 DSP
        IRQ 3 is wire-OR of the 72069 FDC and 89352 SCSI
        IRQ 4 is wire-OR of all 4 TXRDYs on the TE7774
        IRQ 5 is wire-OR of RXRDY1, 2, and 3 on the TE7774
        IRQ 6 is the SMPTE sync in
        IRQ 7 is RXRDY4 on the TE7774

        TE7774 hookups: RXD1 is MIDI IN 1, RXD2 is MIDI IN 2, RXD3 and 4 are wire-ORed to the uPD7810's TX line.
                        TXD1-4 are MIDI OUTs 1, 2, 3, and 4.

    MPC2000XL &  Classic:
        CPU: NEC V53
        Floppy: uPD72068
        SCSI: MB89352
        LCD:
        Dual UART: MB89371A
        (V53's 8251 is used for panel comms here)
        Panel controller CPU: NEC uPD7810 @ 12 MHz
        Sound DSP: L6048

MPCs on other hardware:

    MPC4000:
        CPU, LCD, panel controller: SA1110 StrongARM @ 176 MHz
        SCSI: FAS236 (same as NCR 53CF96)
        IDE, UART, DMA controller: FPGA XC2S100-5TQ144C
        USB Host: SL811HST
        USB function controller: NET2890
        DSP, MIDI: MB87L185PFVS-G-BND (gate array?)

    MPC1000:
        CPU, LCD, UART, panel controller, DSP: SH-3 7712 (HD6417712)

    MPC500:
        CPU, LCD, UART, panel controller, DSP: SH-3 7727 (HD6417727) @ 100 MHz

    MPC2500:
        CPU, LCD, UART, panel controller, DSP: SH-3 7727 (HD6417727) @ 160 MHz

***************************************************************************/

#include "emu.h"
#include "cpu/nec/v5x.h"
#include "cpu/upd7810/upd7810.h"
#include "imagedev/floppy.h"
#include "sound/l7a1045_l6028_dsp_a.h"
#include "video/hd61830.h"
#include "bus/midi/midi.h"
#include "bus/nscsi/devices.h"
#include "speaker.h"
#include "screen.h"
#include "emupal.h"
#include "machine/74259.h"
#include "machine/i8255.h"
#include "machine/input_merger.h"
#include "machine/mb87030.h"
#include "machine/pit8253.h"
#include "machine/upd765.h"

class mpc3000_state : public driver_device
{
public:
	mpc3000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_subcpu(*this, "subcpu")
		, m_lcdc(*this, "lcdc")
		, m_dsp(*this, "dsp")
		, m_mdout(*this, "mdout")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:0")
	{ }

	void mpc3000(machine_config &config);

	void init_mpc3000();

private:
	required_device<v53a_device> m_maincpu;
	required_device<upd7810_device> m_subcpu;
	required_device<hd61830_device> m_lcdc;
	required_device<l7a1045_sound_device> m_dsp;
	required_device<midi_port_device> m_mdout;
	required_device<upd72069_device> m_fdc;
	required_device<floppy_connector> m_floppy;

	static void floppies(device_slot_interface &device);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void mpc3000_map(address_map &map) ATTR_COLD;
	void mpc3000_io_map(address_map &map) ATTR_COLD;
	void mpc3000_sub_map(address_map &map) ATTR_COLD;

	uint16_t dsp_0008_hack_r();
	void dsp_0008_hack_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t dma_memr_cb(offs_t offset);
	void dma_memw_cb(offs_t offset, uint16_t data);
	void mpc3000_palette(palette_device &palette) const;
};

void mpc3000_state::machine_start()
{
}

void mpc3000_state::machine_reset()
{
}

void mpc3000_state::mpc3000_map(address_map &map)
{
	map(0x000000, 0x07ffff).mirror(0x80000).rom().region("maincpu", 0);
	map(0x300000, 0x3fffff).ram();
	map(0x500000, 0x500fff).ram(); // actually 8-bit battery-backed RAM
}

void mpc3000_state::dsp_0008_hack_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	// this is related to the DSP's DMA capability.  The DSP
	// connects to the V53's DMA3 channel on both the MPCs and HNG64.
	m_maincpu->dreq_w<3>(data&0x1);
	m_dsp->l7a1045_sound_w(8/2,data,mem_mask);
}


uint16_t mpc3000_state::dsp_0008_hack_r()
{
	// read in irq5
	return 0;
}

void mpc3000_state::mpc3000_io_map(address_map &map)
{
	map(0x0000, 0x0000).w("loledlatch", FUNC(hc259_device::write_nibble_d3));
	map(0x0020, 0x0020).w("hiledlatch", FUNC(hc259_device::write_nibble_d3));
	map(0x0060, 0x0067).rw(m_dsp, FUNC(l7a1045_sound_device::l7a1045_sound_r), FUNC(l7a1045_sound_device::l7a1045_sound_w));
	map(0x0068, 0x0069).rw(FUNC(mpc3000_state::dsp_0008_hack_r), FUNC(mpc3000_state::dsp_0008_hack_w));
	map(0x0080, 0x0087).rw("dioexp", FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
	map(0x00a0, 0x00bf).m("scsi:7:spc", FUNC(mb89352_device::map)).umask16(0x00ff);
	//map(0x00c0, 0x00c7).rw("sio", FUNC(te7774_device::read0), FUNC(te7774_device::write0)).umask16(0x00ff);
	//map(0x00c8, 0x00cf).rw("sio", FUNC(te7774_device::read1), FUNC(te7774_device::write1)).umask16(0x00ff);
	//map(0x00d0, 0x00d7).rw("sio", FUNC(te7774_device::read2), FUNC(te7774_device::write2)).umask16(0x00ff);
	//map(0x00d8, 0x00df).rw("sio", FUNC(te7774_device::read3), FUNC(te7774_device::write3)).umask16(0x00ff);
	map(0x00e0, 0x00e0).rw(m_lcdc, FUNC(hd61830_device::data_r), FUNC(hd61830_device::data_w)).umask16(0x00ff);
	map(0x00e2, 0x00e2).rw(m_lcdc, FUNC(hd61830_device::status_r), FUNC(hd61830_device::control_w)).umask16(0x00ff);
	map(0x00e8, 0x00eb).m(m_fdc, FUNC(upd72069_device::map)).umask16(0x00ff);
	map(0x00f0, 0x00f7).rw("synctmr", FUNC(pit8254_device::read), FUNC(pit8254_device::write)).umask16(0x00ff);
	map(0x00f8, 0x00ff).rw("adcexp", FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
}

uint16_t mpc3000_state::dma_memr_cb(offs_t offset)
{
	return m_maincpu->space(AS_PROGRAM).read_word(offset << 1);
}

void mpc3000_state::dma_memw_cb(offs_t offset, uint16_t data)
{
	m_maincpu->space(AS_PROGRAM).write_word(offset << 1, data);
}

void mpc3000_state::mpc3000_sub_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("subcpu", 0);
}

void mpc3000_state::mpc3000_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}

void mpc3000_state::floppies(device_slot_interface &device)
{
	device.option_add("35hd", FLOPPY_35_HD);
}

void mpc3000_state::mpc3000(machine_config &config)
{
	V53A(config, m_maincpu, 32_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mpc3000_state::mpc3000_map);
	m_maincpu->set_addrmap(AS_IO, &mpc3000_state::mpc3000_io_map);
	m_maincpu->out_hreq_cb().set(m_maincpu, FUNC(v53a_device::hack_w));
	m_maincpu->in_mem16r_cb().set(FUNC(mpc3000_state::dma_memr_cb));
	m_maincpu->out_mem16w_cb().set(FUNC(mpc3000_state::dma_memw_cb));
	m_maincpu->out_eop_cb().set("tc", FUNC(input_merger_device::in_w<0>));
	m_maincpu->in_ior_cb<0>().set("scsi:7:spc", FUNC(mb89352_device::dma_r));
	m_maincpu->out_iow_cb<0>().set("scsi:7:spc", FUNC(mb89352_device::dma_w));
	m_maincpu->out_dack_cb<1>().set("tc", FUNC(input_merger_device::in_w<1>));
	m_maincpu->in_ior_cb<1>().set(m_fdc, FUNC(upd72069_device::dma_r));
	m_maincpu->out_iow_cb<1>().set(m_fdc, FUNC(upd72069_device::dma_w));
	m_maincpu->in_io16r_cb<3>().set(m_dsp, FUNC(l7a1045_sound_device::dma_r16_cb));
	m_maincpu->out_io16w_cb<3>().set(m_dsp, FUNC(l7a1045_sound_device::dma_w16_cb));
	m_maincpu->set_tclk(4'000'000); // FIXME: DAWCK generated by DSP (also tied to V53 DSR input)
	m_maincpu->tout_handler<0>().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_maincpu->tout_handler<1>().set_inputline(m_maincpu, INPUT_LINE_IRQ1);
	m_maincpu->tout_handler<2>().set_inputline(m_maincpu, INPUT_LINE_IRQ2);

	constexpr XTAL V53_CLKOUT = 32_MHz_XTAL / 2;
	constexpr XTAL V53_PCLKOUT = 32_MHz_XTAL / 4;

	// HC02 gates
	INPUT_MERGER_ANY_HIGH(config, "intp3").output_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ3);
	INPUT_MERGER_ALL_HIGH(config, "tc").output_handler().set(m_fdc, FUNC(upd72069_device::tc_line_w));

	hc259_device &loledlatch(HC259(config, "loledlatch"));
	loledlatch.q_out_cb<0>().set_output("led0").invert(); // Edit Loop
	loledlatch.q_out_cb<1>().set_output("led1").invert(); // Simul Seq
	loledlatch.q_out_cb<2>().set_output("led2").invert(); // Transpose
	loledlatch.q_out_cb<3>().set_output("led3").invert(); // Wait For
	loledlatch.q_out_cb<4>().set_output("led4").invert(); // Count In
	loledlatch.q_out_cb<5>().set_output("led5").invert(); // Auto Punch
	loledlatch.q_out_cb<6>().set_output("led6").invert(); // Rec
	loledlatch.q_out_cb<7>().set_output("led7").invert(); // Over Dub

	hc259_device &hiledlatch(HC259(config, "hiledlatch"));
	hiledlatch.q_out_cb<0>().set_output("led8").invert(); // Play
	hiledlatch.q_out_cb<1>().set_output("led9").invert(); // Bank A
	hiledlatch.q_out_cb<2>().set_output("led10").invert(); // Bank B
	hiledlatch.q_out_cb<3>().set_output("led11").invert(); // Bank C
	hiledlatch.q_out_cb<4>().set_output("led12").invert(); // Bank D
	hiledlatch.q_out_cb<5>().set_output("led13").invert(); // Full Level
	hiledlatch.q_out_cb<6>().set_output("led14").invert(); // 16 Levels
	hiledlatch.q_out_cb<7>().set_output("led15").invert(); // After

	UPD78C10(config, m_subcpu, 12_MHz_XTAL);
	m_subcpu->set_addrmap(AS_PROGRAM, &mpc3000_state::mpc3000_sub_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(80);
	screen.set_screen_update("lcdc", FUNC(hd61830_device::screen_update));
	screen.set_size(240, 64);   //6x20, 8x8
	screen.set_visarea(0, 240-1, 0, 64-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(mpc3000_state::mpc3000_palette), 2);

	UPD72069(config, m_fdc, V53_CLKOUT); // TODO: upd72069 supports motor control
	m_fdc->intrq_wr_callback().set("intp3", FUNC(input_merger_device::in_w<0>));
	m_fdc->drq_wr_callback().set(m_maincpu, FUNC(v53a_device::dreq_w<1>));

	FLOPPY_CONNECTOR(config, m_floppy, mpc3000_state::floppies, "35hd", floppy_image_device::default_mfm_floppy_formats);

	pit8254_device &pit(PIT8254(config, "synctmr", 0)); // MB89254
	pit.set_clk<0>(V53_PCLKOUT);
	pit.set_clk<1>(V53_PCLKOUT);
	pit.set_clk<2>(V53_PCLKOUT);

	I8255(config, "adcexp"); // MB89255B
	I8255(config, "dioexp"); // MB89255B

	HD61830(config, m_lcdc, 4.9152_MHz_XTAL / 2 / 2); // LC7981

	//TE7774(config, "sio", V53_PCLKOUT);

	auto &mdin(MIDI_PORT(config, "mdin"));
	midiin_slot(mdin);
	//mdin.rxd_handler().set(m_maincpu, FUNC());

	midiout_slot(MIDI_PORT(config, "mdout"));

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:1", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:7").option_set("spc", MB89352).machine_config(
		[this](device_t *device)
		{
			mb89352_device &spc = downcast<mb89352_device &>(*device);

			spc.set_clock(32_MHz_XTAL / 4); // PCLKOUT
			spc.out_irq_callback().set(":intp3", FUNC(input_merger_device::in_w<1>));
			spc.out_dreq_callback().set(m_maincpu, FUNC(v53a_device::dreq_w<0>));
		});

	SPEAKER(config, "speaker", 2).front();

	L7A1045(config, m_dsp, 33.8688_MHz_XTAL / 2); // TODO: verify clock
	m_dsp->add_route(0, "speaker", 1.0, 0);
	m_dsp->add_route(1, "speaker", 1.0, 1);
}

static INPUT_PORTS_START( mpc3000 )
INPUT_PORTS_END

ROM_START( mpc3000 )
	ROM_REGION(0x80000, "maincpu", 0)   // V53 code
	ROM_SYSTEM_BIOS(0, "default", "ver 3.12")
	ROMX_LOAD( "mpc312ls.bin", 0x000000, 0x040000, CRC(d4fb6439) SHA1(555d388ed25f8b85638c325e7d9012eaa271ffa0), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "mpc312ms.bin", 0x000001, 0x040000, CRC(80ee0ab9) SHA1(b8855118d59b8f73a3af5ff2e824cdaa0a9f564a), ROM_SKIP(1) | ROM_BIOS(0) )

	ROM_SYSTEM_BIOS(1, "vailixi", "ver 3.50")
	ROMX_LOAD( "3.50 vailixi lse.bin", 0x000000, 0x040000, CRC(9f3031b5) SHA1(c270a92f8ed273a1ede16388bb8f30c85ac1faab), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "3.50 vailixi mso.bin", 0x000001, 0x040000, CRC(e62ebb26) SHA1(f6d080481de40bea2c94bbc96b222c5f9b7afaf4), ROM_SKIP(1) | ROM_BIOS(1) )

	ROM_SYSTEM_BIOS(2, "v311", "ver 3.11")
	ROMX_LOAD( "mpc311ls.bin", 0x000000, 0x040000, CRC(5a272061) SHA1(87cc8aef3233d95ad1febbd77b42f720d718baa3), ROM_SKIP(1) | ROM_BIOS(2) )
	ROMX_LOAD( "mpc311ms.bin", 0x000001, 0x040000, CRC(a8e177de) SHA1(b63a5c27066c03f7de56659aff183f15d95277c5), ROM_SKIP(1) | ROM_BIOS(2) )

	ROM_SYSTEM_BIOS(3, "v310", "ver 3.10")
	ROMX_LOAD( "mpc3000__ls__v3.10.am27c020__id0197.ic13_ls.bin", 0x000000, 0x040000, CRC(cbd1b3a6) SHA1(5464a57137549d9d9c47f9aafc2b89f4c0af8b31), ROM_SKIP(1) | ROM_BIOS(3) )
	ROMX_LOAD( "mpc3000__ms__v3.10.am27c020__id0197.ic14_ms.bin", 0x000001, 0x040000, CRC(e2ba1904) SHA1(27a9f047c63964fac2b453f2317b77834490983d), ROM_SKIP(1) | ROM_BIOS(3) )

	ROM_REGION(0x8000, "subcpu", 0)    // uPD78C10 panel controller code
	ROM_LOAD( "mp3000__op_v1.0.am27c256__id0110.ic602.bin", 0x000000, 0x008000, CRC(b0b783d3) SHA1(a60016184fc07ba00dcc19ba4da60e78aceff63c) )

	ROM_REGION( 0x2000000, "dsp", ROMREGION_ERASE00 )   // sample RAM
ROM_END

void mpc3000_state::init_mpc3000()
{
}

CONS( 1994, mpc3000, 0, 0, mpc3000, mpc3000, mpc3000_state, init_mpc3000, "Akai / Roger Linn", "MPC-3000", MACHINE_NOT_WORKING )



mpc60.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for Akai MPC60 MIDI Production Center.

***************************************************************************/

#include "emu.h"
//#include "bus/midi/midi.h"
#include "cpu/i86/i186.h"
#include "cpu/upd7810/upd7810.h"
#include "machine/clock.h"
#include "machine/i8255.h"
#include "machine/nvram.h"
#include "machine/upd765.h"
#include "video/hd61830.h"
#include "emupal.h"
#include "screen.h"

namespace {

class mpc60_state : public driver_device
{
public:
	mpc60_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_panelcpu(*this, "panelcpu")
		, m_fdc(*this, "fdc")
	{
	}

	void mpc60(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	u8 nvram_r(offs_t offset);
	void nvram_w(offs_t offset, u8 data);
	void fdc_tc_w(u8 data);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void panel_map(address_map &map) ATTR_COLD;
	void lcd_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<upd7810_device> m_panelcpu;
	required_device<upd72065_device> m_fdc;

	std::unique_ptr<u8[]> m_nvram_data;
};

void mpc60_state::machine_start()
{
	m_nvram_data = make_unique_clear<u8[]>(0x800);
	subdevice<nvram_device>("nvram")->set_base(&m_nvram_data[0], 0x800);

	save_pointer(NAME(m_nvram_data), 0x800);
}

u8 mpc60_state::nvram_r(offs_t offset)
{
	return m_nvram_data[offset];
}

void mpc60_state::nvram_w(offs_t offset, u8 data)
{
	m_nvram_data[offset] = data;
}

void mpc60_state::fdc_tc_w(u8 data)
{
	m_fdc->tc_w(0);
	m_fdc->tc_w(1);
}

void mpc60_state::mem_map(address_map &map)
{
	map(0x00000, 0x7ffff).ram();
	map(0xbf000, 0xbffff).rw(FUNC(mpc60_state::nvram_r), FUNC(mpc60_state::nvram_w)).umask16(0x00ff);
	map(0xc0000, 0xfffff).rom().region("program", 0);
}

void mpc60_state::io_map(address_map &map)
{
	map(0x0080, 0x0083).m(m_fdc, FUNC(upd72065_device::map)).umask16(0x00ff);
	map(0x0090, 0x0090).w(FUNC(mpc60_state::fdc_tc_w));
	map(0x00a0, 0x00a0).rw(m_fdc, FUNC(upd72065_device::dma_r), FUNC(upd72065_device::dma_w));
	map(0x00b0, 0x00b0).r("lcdc", FUNC(hd61830_device::status_r));
	map(0x00b2, 0x00b2).r("lcdc", FUNC(hd61830_device::data_r));
	map(0x00b4, 0x00b4).w("lcdc", FUNC(hd61830_device::control_w));
	map(0x00b6, 0x00b6).w("lcdc", FUNC(hd61830_device::data_w));
	map(0x0200, 0x0207).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
}

void mpc60_state::panel_map(address_map &map)
{
	map(0x4000, 0x5fff).rom().region("panel", 0);
}

void mpc60_state::lcd_map(address_map &map)
{
	map.global_mask(0x07ff);
	map(0x0000, 0x07ff).ram();
}


static INPUT_PORTS_START(mpc60)
INPUT_PORTS_END

void mpc60_state::mpc60(machine_config &config)
{
	I80186(config, m_maincpu, 20_MHz_XTAL); // MBL80186-10
	m_maincpu->set_addrmap(AS_PROGRAM, &mpc60_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &mpc60_state::io_map);

	CLOCK(config, "tmrck", 20_MHz_XTAL / 10).signal_handler().set(m_maincpu, FUNC(i80186_cpu_device::tmrin1_w)); // CPUCK output divided by 74HC390

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // CXK5816PN-15L + CL2020 battery

	UPD78C11(config, m_panelcpu, 12_MHz_XTAL);
	m_panelcpu->set_addrmap(AS_PROGRAM, &mpc60_state::panel_map);

	//MB89371(config, "sio01", 20_MHz_XTAL / 4);
	//MB89371(config, "sio23", 20_MHz_XTAL / 4);

	I8255(config, "ppi"); // MB89255A-P-C

	UPD72065(config, m_fdc, 16_MHz_XTAL / 4); // μPD72066C (clocked by SED9420CAC)
	m_fdc->set_ready_line_connected(false); // RDY tied to VDD (TODO: drive ready signal connected to PPI's PB2 instead)
	m_fdc->set_select_lines_connected(false);
	m_fdc->intrq_wr_callback().set(m_maincpu, FUNC(i80186_cpu_device::int0_w));
	m_fdc->drq_wr_callback().set(m_maincpu, FUNC(i80186_cpu_device::drq1_w)); // FIXME: delayed and combined with DRQAD

	hd61830_device &lcdc(HD61830(config, "lcdc", 0)); // LC7981
	lcdc.set_addrmap(0, &mpc60_state::lcd_map);
	lcdc.set_screen("screen");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(80);
	screen.set_screen_update("lcdc", FUNC(hd61830_device::screen_update));
	screen.set_size(240, 64);
	screen.set_visarea(0, 240-1, 0, 64-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	//L4003(config, "voicelsi", 35.84_MHz_XTAL);
}

ROM_START(mpc60)
	ROM_REGION16_LE(0x40000, "program", 0)
	ROM_SYSTEM_BIOS(0, "v212", "v2.12") // V2.12 CPU ROMs (MBM27C512-20)
	ROMX_LOAD("mp6cpu2.ic2", 0x00000, 0x10000, CRC(e71b1acb) SHA1(b56ddfff1c546fc21341b1a614e18da9726312f4), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("mp6cpu3.ic3", 0x00001, 0x10000, CRC(f068838b) SHA1(42e815880d1c1a5b7d1c7933aad9c28410fc2627), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("mp6cpu1.ic4", 0x20000, 0x10000, CRC(1271bc73) SHA1(99fd6fa4c04e5bdf868e78072fec5b55c01350da), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("mpc6cpu4.ic5", 0x20001, 0x10000, CRC(d922a66d) SHA1(0f4bc0522b9826d617f4af72382d75853515d7f5), ROM_BIOS(0) | ROM_SKIP(1))
	ROM_SYSTEM_BIOS(1, "v112", "v1.12")
	ROMX_LOAD("mpc60_v1-12_2.ic2", 0x00000, 0x10000, CRC(ddf26146) SHA1(987547198dc3984ab3dfa7f133ba7dca702cc269), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("mpc60_v1-12_4.ic3", 0x00001, 0x10000, CRC(9725d193) SHA1(6efda3d6760b3951c5036108106d446f6e128c59), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("mpc60_v1-12_1.ic4", 0x20000, 0x10000, CRC(f202dbb1) SHA1(6fd82224a99b52b6c414b88d5c920abda32ffa32), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("mpc60_v1-12_3.ic5", 0x20001, 0x10000, CRC(ba5a1640) SHA1(1f9f49c49a3682b9a44d614ac411a7c043df399e), ROM_BIOS(1) | ROM_SKIP(1))

	ROM_REGION16_LE(0x10000, "waves", 0)
	ROM_LOAD16_BYTE("mpc60_voice_1_v1-0.ic17", 0x00000, 0x08000, CRC(b8fdfe3e) SHA1(c2f0e1d8813d4178d2f883a3f3e461e036b56229)) // lowest nibble is unused
	ROM_LOAD16_BYTE("mpc60_voice_2_v1-0.ic18", 0x00001, 0x08000, CRC(42f8e0a6) SHA1(a22dbefb9dafbb0c4095fd0bf4e63e67b5ec3b95))

	ROM_REGION(0x1000, "panelcpu", 0)
	ROM_LOAD("upd78c11g-044-36.ic1", 0x0000, 0x1000, NO_DUMP)
	ROM_FILL(0x0000, 1, 0x54) // dummy reset vector
	ROM_FILL(0x0001, 1, 0x00)
	ROM_FILL(0x0002, 1, 0x40)
	ROM_FILL(0x0018, 1, 0x54) // dummy interrupt vector
	ROM_FILL(0x0019, 1, 0x18)
	ROM_FILL(0x001a, 1, 0x40)
	ROM_FILL(0x0090, 1, 0xca) // dummy CALT vectors
	ROM_FILL(0x0091, 1, 0x41)
	ROM_FILL(0x0092, 1, 0xca)
	ROM_FILL(0x0093, 1, 0x41)
	ROM_FILL(0x0094, 1, 0xca)
	ROM_FILL(0x0095, 1, 0x41)
	ROM_FILL(0x0096, 1, 0xca)
	ROM_FILL(0x0097, 1, 0x41)
	ROM_FILL(0x0098, 1, 0xca)
	ROM_FILL(0x0099, 1, 0x41)
	ROM_FILL(0x009a, 1, 0xca)
	ROM_FILL(0x009b, 1, 0x41)

	ROM_REGION(0x2000, "panel", 0)
	ROM_LOAD("akai mpc60 panel eprom op v1-1 2764.ic2", 0x0000, 0x2000, CRC(f1332f47) SHA1(dd5e917d16941fce3db4bfe21d37f722d6262561))
ROM_END

} // anonymous namespace

SYST(1987, mpc60, 0, 0, mpc60, mpc60, mpc60_state, empty_init, "Akai Electric", "MPC60 MIDI Production Center", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



mpf1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol, Curt Coder
/************************************************\
* Multitech Micro Professor 1                    *
*                                                *
*     CPU: Z80 @ 1.79 MHz                        *
*     ROM: 4-kilobyte ROM monitor                *
*     RAM: 4 kilobytes                           *
*   Input: Hex keypad                            *
* Storage: Cassette tape                         *
*   Video: 6x 7-segment LED display              *
*   Sound: Speaker                               *
\************************************************/

/*

    Keys:
        0-9,A-F : hexadecimal numbers
        ADR : enter an address to work with. After the 4 digits are entered,
              the data at that address shows, and you can modify the data.
        +   : Enter the data into memory, and increment the address by 1.
        GO  : execute the program located at the current address.

    Pasting:
        0-F : as is
        +   : ^
        -   : V
        ADDR : -
        DATA : =
        GO : X
        PC : P

    Test Paste:
        -1800=11^22^33^44^55^66^77^88^99^-1800
        Now press up-arrow to confirm the data has been entered.

    TODO:
    - crt board
    - clickable artwork
    - computer can't keep up with paste
    - paste only set up for mpf1

*/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/i8255.h"
#include "machine/z80ctc.h"
#include "machine/z80daisy.h"
#include "machine/z80pio.h"
#include "sound/spkrdev.h"
#include "video/pwm.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "bus/mpf1/slot.h"
#include "imagedev/cassette.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "mpf1.lh"
#include "mpf1b.lh"
#include "mt80z.lh"


namespace {

class mpf1_state : public driver_device
{
public:
	mpf1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_speaker(*this, "speaker")
		, m_7seg_pwm(*this, "7seg_pwm")
		, m_cassette(*this, "cassette")
		, m_rom_region(*this, "maincpu")
		, m_rom_u7(*this, "rom_u7")
		, m_pc(*this, "PC%u", 0U)
		, m_special(*this, "SPECIAL")
		, m_leds(*this, "led%u", 0U)
	{ }

	void mpf1(machine_config &config);
	void mpf1b(machine_config &config);
	void mt80z(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER( trigger_special );

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;
	required_device<speaker_sound_device> m_speaker;
	required_device<pwm_display_device> m_7seg_pwm;
	required_device<cassette_image_device> m_cassette;
	required_region_ptr<uint8_t> m_rom_region;
	required_device<generic_slot_device> m_rom_u7;
	required_ioport_array<6> m_pc;
	required_ioport m_special;
	output_finder<2> m_leds;

	void mpf1_io_map(address_map &map) ATTR_COLD;
	void mpf1_map(address_map &map) ATTR_COLD;
	void mpf1_step(address_map &map) ATTR_COLD;

	uint8_t rom_r(offs_t offset);
	uint8_t step_r(offs_t offset);
	uint8_t ppi_pa_r();
	void ppi_pb_w(uint8_t data);
	void ppi_pc_w(uint8_t data);

	int m_break = 0;
	int m_m1 = 0;

	uint8_t m_select = 0;
};

/* Address Maps */

void mpf1_state::mpf1_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).rom();
	map(0x1800, 0x1fff).ram();
	map(0x2000, 0x2fff).r(FUNC(mpf1_state::rom_r));
}

void mpf1_state::mpf1_step(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(mpf1_state::step_r));
}

void mpf1_state::mpf1_io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x03).mirror(0x3c).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x40, 0x43).mirror(0x3c).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x80, 0x83).mirror(0x3c).rw("pio", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
}

uint8_t mpf1_state::rom_r(offs_t offset)
{
	if (m_rom_u7->exists())
		return m_rom_u7->read_rom(offset & 0x1fff);
	else
		return m_rom_region[offset + 0x2000];
}

/* Input Ports */

INPUT_CHANGED_MEMBER( mpf1_state::trigger_special )
{
	m_maincpu->set_input_line(param, newval ? CLEAR_LINE : ASSERT_LINE);
}

static INPUT_PORTS_START( mpf1 )
	PORT_START("PC0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("3 HL") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("7 HL'") PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("B I.IF") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F .PNC'") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("2 DE") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("6 DE'") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A SP") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("E SZ.H'") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("1 BC") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("5 BC'") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("9 IY") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D .PNC") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("STEP") PORT_CODE(KEYCODE_F1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("TAPE RD") PORT_CODE(KEYCODE_F5)
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("0 AF") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("4 AF'") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("8 IX") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C SZ.H") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GO") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("TAPE WR") PORT_CODE(KEYCODE_F6)
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CBR") PORT_CODE(KEYCODE_N)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PC") PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("REG") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ADDR") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DEL") PORT_CODE(KEYCODE_SLASH)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RELA") PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SBR") PORT_CODE(KEYCODE_H)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("-") PORT_CODE(KEYCODE_DOWN) PORT_CHAR('V')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DATA") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("+") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INS") PORT_CODE(KEYCODE_COLON)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("MOVE") PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("SPECIAL")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("USER KEY") PORT_CODE(KEYCODE_U)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("MONI") PORT_CODE(KEYCODE_M) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mpf1_state::trigger_special), INPUT_LINE_NMI)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INTR") PORT_CODE(KEYCODE_I) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mpf1_state::trigger_special), INPUT_LINE_IRQ0)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RESET") PORT_CODE(KEYCODE_F3) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mpf1_state::trigger_special), INPUT_LINE_RESET)
INPUT_PORTS_END

static INPUT_PORTS_START( mpf1b )
	PORT_START("PC0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("3 /") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("7 >") PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("B STOP") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F LET") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("2 *") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("6 <") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A CALL") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("E INPUT") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("1 -") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("5 =") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("9 P") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D PRINT") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CONT") PORT_CODE(KEYCODE_F1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LOAD") PORT_CODE(KEYCODE_F5)
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("0 +") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("4 * *") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("8 M") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C NEXT") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RUN") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SAVE") PORT_CODE(KEYCODE_F6)
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"IF/\u2227") PORT_CODE(KEYCODE_PGUP)   // U+2227 = ∧
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"TO/\u21e9") PORT_CODE(KEYCODE_T) PORT_CODE(KEYCODE_DOWN) // U+21E9 = ⇩
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"THEN/\u2228") PORT_CODE(KEYCODE_PGDN) // U+2228 = ∨
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GOTO") PORT_CODE(KEYCODE_G)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RET/~") PORT_CODE(KEYCODE_R)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GOSUB") PORT_CODE(KEYCODE_O)
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"FOR/\u21e7") PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_UP) // U+21E7 = ⇧
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LIST") PORT_CODE(KEYCODE_L)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("NEW") PORT_CODE(KEYCODE_N)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"CLR/\u21e8") PORT_CODE(KEYCODE_INSERT) PORT_CODE(KEYCODE_RIGHT) // U+21E8 = ⇨
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"DEL/\u21e6") PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_LEFT)     // U+21E6 = ⇦
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("SPECIAL")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("MONI") PORT_CODE(KEYCODE_M) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mpf1_state::trigger_special), INPUT_LINE_NMI)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INTR") PORT_CODE(KEYCODE_I) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mpf1_state::trigger_special), INPUT_LINE_IRQ0)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RESET") PORT_CODE(KEYCODE_F3) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mpf1_state::trigger_special), INPUT_LINE_RESET)
INPUT_PORTS_END

static INPUT_PORTS_START( mt80z )
	PORT_START("PC0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GO") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DUMP") PORT_CODE(KEYCODE_F6)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("COPY") PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("NEXT") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("STEP") PORT_CODE(KEYCODE_F1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LOAD") PORT_CODE(KEYCODE_F5)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DEL") PORT_CODE(KEYCODE_SLASH)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PREV") PORT_CODE(KEYCODE_DOWN) PORT_CHAR('V')
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PC") PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CLR BRK PT") PORT_CODE(KEYCODE_N)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INS") PORT_CODE(KEYCODE_COLON)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DATA") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("9 IY") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C SZ.H") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("REG") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SET BRK PT") PORT_CODE(KEYCODE_H)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RELA") PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ADDR") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D .PNC") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("8 IX") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("6 DE'") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("3 HL") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("7 HL'") PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F .PNC'") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("5 BC'") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("4 AF'") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("2 DE") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A SP") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("B I.IF") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("E SZ.H'") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("1 BC") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("0 AF") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("SPECIAL")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("USER KEY") PORT_CODE(KEYCODE_U)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BREAK") PORT_CODE(KEYCODE_M) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mpf1_state::trigger_special), INPUT_LINE_NMI)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INTR") PORT_CODE(KEYCODE_I) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mpf1_state::trigger_special), INPUT_LINE_IRQ0)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RESET") PORT_CODE(KEYCODE_F3) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mpf1_state::trigger_special), INPUT_LINE_RESET)
INPUT_PORTS_END


/* Intel 8255A Interface */

uint8_t mpf1_state::ppi_pa_r()
{
	uint8_t data = 0x7f;

	/* bit 0 to 5, keyboard rows 0 to 5 */
	for (int row = 0; row < 6; row++)
		if (!BIT(m_select, row))
			data &= m_pc[row]->read();

	/* bit 6, user key */
	data &= m_special->read() & 1 ? 0xff : 0xbf;

	/* bit 7, tape input */
	data |= (m_cassette->input() > 0) ? 0x80 : 0x00;

	return data;
}

void mpf1_state::ppi_pb_w(uint8_t data)
{
	/* swap bits around for the 7-segment emulation */
	m_7seg_pwm->write_mx(bitswap<8>(data, 6, 1, 2, 0, 7, 5, 4, 3));
}

void mpf1_state::ppi_pc_w(uint8_t data)
{
	/* bits 0-5, led select and keyboard latch */
	m_select = data & 0x3f;
	m_7seg_pwm->write_my(m_select);

	/* bit 6, monitor break control */
	m_break = BIT(data, 6);

	if (m_break)
	{
		m_m1 = 0;
		m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
	}

	/* bit 7, tape output, tone and led */
	m_leds[0] = !BIT(data, 7);
	m_speaker->level_w(BIT(data, 7));
	m_cassette->output(BIT(data, 7) ? 1.0 : -1.0);
}

uint8_t mpf1_state::step_r(offs_t offset)
{
	if (!m_break)
	{
		m_m1++;

		if (m_m1 == 5)
			m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
	}

	return m_maincpu->space(AS_PROGRAM).read_byte(offset);
}


/* Z80 Daisy Chain */

static const z80_daisy_config mpf1_daisy_chain[] =
{
	{ "ctc" },
	{ "pio" },
	{ nullptr }
};


/* Machine Initialization */

void mpf1_state::machine_start()
{
	m_leds.resolve();

	/* register for state saving */
	save_item(NAME(m_break));
	save_item(NAME(m_m1));
	save_item(NAME(m_select));
}

void mpf1_state::machine_reset()
{
	m_select = 0;
}


/* Machine Drivers */

void mpf1_state::mpf1(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 3.579545_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &mpf1_state::mpf1_map);
	m_maincpu->set_addrmap(AS_OPCODES, &mpf1_state::mpf1_step);
	m_maincpu->set_addrmap(AS_IO, &mpf1_state::mpf1_io_map);
	m_maincpu->halt_cb().set_output("led1");
	m_maincpu->set_daisy_config(mpf1_daisy_chain);

	/* devices */
	z80pio_device &pio(Z80PIO(config, "pio", 3.579545_MHz_XTAL / 2));
	pio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80ctc_device &ctc(Z80CTC(config, "ctc", 3.579545_MHz_XTAL / 2));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	i8255_device &ppi(I8255A(config, "ppi"));
	ppi.in_pa_callback().set(FUNC(mpf1_state::ppi_pa_r));
	ppi.out_pb_callback().set(FUNC(mpf1_state::ppi_pb_w));
	ppi.out_pc_callback().set(FUNC(mpf1_state::ppi_pc_w));

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);

	/* video hardware */
	PWM_DISPLAY(config, m_7seg_pwm).set_size(6, 8);
	m_7seg_pwm->set_segmask(0x3f, 0xff);

	config.set_default_layout(layout_mpf1);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	/* expansion */
	mpf1_exp_device &exp(MPF1_EXP(config, "exp", 3.579545_MHz_XTAL / 2, mpf1_exp_devices, nullptr));
	exp.set_program_space(m_maincpu, AS_PROGRAM);
	exp.set_io_space(m_maincpu, AS_IO);
	exp.int_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	exp.nmi_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);
	exp.wait_handler().set_inputline(m_maincpu, Z80_INPUT_LINE_WAIT);

	GENERIC_SOCKET(config, m_rom_u7, generic_linear_slot, "mpf1_rom", "bin,rom");

	SOFTWARE_LIST(config, "rom_ls").set_original("mpf1_rom").set_filter("IA");
}

void mpf1_state::mpf1b(machine_config &config)
{
	mpf1(config);

	config.set_default_layout(layout_mpf1b);

	subdevice<software_list_device>("rom_ls")->set_filter("IA,IB");
}

void mpf1_state::mt80z(machine_config &config)
{
	mpf1b(config);

	config.set_default_layout(layout_mt80z);

	// TODO: MT-80Z expansion board with switches (DIPs) and indicators (LEDs).
}


ROM_START( mpf1 )
	ROM_REGION(0x3000, "maincpu", ROMREGION_ERASEFF)
	ROM_LOAD("mpf1.u6", 0x0000, 0x0800, CRC(820030b1) SHA1(f618e2044b8f6e055e4d9a4c719b53bf16e22330))
ROM_END

ROM_START( mpf1b )
	ROM_REGION(0x3000, "maincpu", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "basic", "BASIC")
	ROMX_LOAD("mpf1b.u6",    0x0000, 0x1000, CRC(28b06dac) SHA1(99cfbab739d71a914c39302d384d77bddc4b705b), ROM_BIOS(0))
	ROMX_LOAD("unknown.u7",  0x2000, 0x1000, CRC(d276ed6b) SHA1(a45fb98961be5e5396988498c6ed589a35398dcf), ROM_BIOS(0)) // TODO: this was previously labelled as BASIC, it's not and is unknown.
	ROM_SYSTEM_BIOS(1, "olsl1", "OLS-L1")
	ROMX_LOAD("ols_l1.u6",   0x0000, 0x1000, CRC(b60249ce) SHA1(78e0e8874d1497fabfdd6378266d041175e3797f), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "olsl2", "OLS-L2")
	ROMX_LOAD("ols_l1-3.u6", 0x0000, 0x1000, CRC(2f038b4b) SHA1(58ba895d2683d26d48da36afdcee9f1dae2f7b7c), ROM_BIOS(2))
	ROMX_LOAD("ols_l2.u7",   0x2000, 0x1000, CRC(77e51dde) SHA1(45d2c4b520291d5a346a0c3567bcb4b4406675f9), ROM_BIOS(2))
ROM_END

ROM_START( cs113 )
	ROM_REGION(0x3000, "maincpu", ROMREGION_ERASEFF)
	ROM_LOAD("cs113.bin", 0x0000, 0x2000, CRC(d293a83b) SHA1(890e8b478e38fed6326b4151bf68c587f82f6a94))
ROM_END

ROM_START( mt80z )
	ROM_REGION(0x3000, "maincpu", ROMREGION_ERASEFF)
	ROM_LOAD("fox.u6", 0x0000, 0x0800, CRC(298ffc59) SHA1(26d144aa63581407dd2f14437646afd807d2bb34))
ROM_END

} // anonymous namespace


/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY            FULLNAME                     FLAGS */
COMP( 1981, mpf1,   0,      0,      mpf1,    mpf1,  mpf1_state, empty_init, "Multitech",       "Micro-Professor 1",         0 )
COMP( 1982, mpf1b,  mpf1,   0,      mpf1b,   mpf1b, mpf1_state, empty_init, "Multitech",       "Micro-Professor 1B",        0 )
COMP( 198?, cs113,  0,      0,      mpf1,    mpf1,  mpf1_state, empty_init, "Sciento b.v.",    "Robot Training Arm CS-113", MACHINE_NOT_WORKING )
COMP( 1982, mt80z,  mpf1,   0,      mt80z,   mt80z, mpf1_state, empty_init, "E&L Instruments", "MT-80Z",                    0 )



mpf1_88.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/***************************************************************************

    Multitech Micro-Professor I/88

    TODO:
    - IFM-I/88 interrupts, need documentation/schematic.

***************************************************************************/

#include "emu.h"
#include "cpu/i86/i86.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "sound/spkrdev.h"
#include "video/hd44780.h"
#include "bus/centronics/ctronics.h"
#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "bus/isa/isa.h"
#include "bus/isa/cga.h"
#include "bus/isa/com.h"
#include "bus/isa/mda.h"
//#include "bus/mpf1/slot.h"
#include "imagedev/cassette.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class mpf1_88_state : public driver_device
{
public:
	mpf1_88_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, "ram")
		, m_speaker(*this, "speaker")
		, m_cassette(*this, "cassette")
		, m_key(*this, "KC%u", 0U)
		, m_ctrl(*this, "CTRL")
		, m_centronics(*this, "centronics")
		, m_cent_data_out(*this, "cent_data_out")
		, m_leds(*this, "led%u", 0U)
		, m_centronics_busy(0)
	{ }

	void mpf1_88(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER( trigger_res );

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cassette;
	required_ioport_array<12> m_key;
	required_ioport m_ctrl;
	required_device<centronics_device> m_centronics;
	required_device<output_latch_device> m_cent_data_out;
	output_finder<2> m_leds;

	TIMER_DEVICE_CALLBACK_MEMBER(check_halt_callback);
	TIMER_DEVICE_CALLBACK_MEMBER(key_nmi);

	HD44780_PIXEL_UPDATE(lcd_pixel_update);

	void lcd_palette(palette_device &palette) const;

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	uint8_t ipd_port_r();
	void opd_port1_w(uint8_t data);
	void opd_port2_w(uint8_t data);

	uint16_t m_key_col = 0;

	int m_centronics_busy;
	bool m_nmi_enable = false;
};


void mpf1_88_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0xf4000, 0xf7fff).r("rom_2", FUNC(generic_slot_device::read_rom));
	map(0xf8000, 0xfbfff).r("rom_1", FUNC(generic_slot_device::read_rom));
	map(0xfc000, 0xfffff).rom().region("rom_0", 0);
}

void mpf1_88_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0160, 0x0160).w(FUNC(mpf1_88_state::opd_port2_w));
	map(0x0180, 0x0180).w(FUNC(mpf1_88_state::opd_port1_w));
	map(0x01a0, 0x01a1).w("lcdc", FUNC(hd44780_device::write));
	map(0x01a2, 0x01a3).r("lcdc", FUNC(hd44780_device::read));
	map(0x01c0, 0x01c0).r(FUNC(mpf1_88_state::ipd_port_r));
	map(0x01e0, 0x01e0).w(m_cent_data_out, FUNC(output_latch_device::write));
}


INPUT_CHANGED_MEMBER( mpf1_88_state::trigger_res )
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? CLEAR_LINE : ASSERT_LINE);
}


static INPUT_PORTS_START( mpf1_88 )
	PORT_START("KC0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CAPSLOCK)   PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')  PORT_CHAR('<')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)          PORT_CHAR('V')  PORT_CHAR('v')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)          PORT_CHAR('B')  PORT_CHAR('b')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KC1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2)         PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)          PORT_CHAR('K')  PORT_CHAR('k')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)          PORT_CHAR('T')  PORT_CHAR('t')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)          PORT_CHAR('U')  PORT_CHAR('u')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KC2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(8)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0)          PORT_CHAR('0')  PORT_CHAR(')')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)          PORT_CHAR('C')  PORT_CHAR('c')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)          PORT_CHAR('Q')  PORT_CHAR('q')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)          PORT_CHAR('Y')  PORT_CHAR('y')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KC3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)          PORT_CHAR('L')  PORT_CHAR('l')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)          PORT_CHAR('F')  PORT_CHAR('f')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8)          PORT_CHAR('8')  PORT_CHAR('*')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KC4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/')  PORT_CHAR('?')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')  PORT_CHAR('>')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)          PORT_CHAR('X')  PORT_CHAR('x')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)          PORT_CHAR('W')  PORT_CHAR('w')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)          PORT_CHAR('J')  PORT_CHAR('j')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KC5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[')  PORT_CHAR('{')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9)          PORT_CHAR('9')  PORT_CHAR('(')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)          PORT_CHAR('S')  PORT_CHAR('s')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)          PORT_CHAR('3')  PORT_CHAR('#')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7)          PORT_CHAR('7')  PORT_CHAR('&')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KC6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)          PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)          PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)          PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KC7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';')  PORT_CHAR(':')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)          PORT_CHAR('E')  PORT_CHAR('e')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2)          PORT_CHAR('2')  PORT_CHAR('@')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)          PORT_CHAR('N')  PORT_CHAR('n')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KC8")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')  PORT_CHAR('}')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)          PORT_CHAR('P')  PORT_CHAR('p')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)          PORT_CHAR('4')  PORT_CHAR('$')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)          PORT_CHAR('A')  PORT_CHAR('a')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)          PORT_CHAR('G')  PORT_CHAR('g')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KC9")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('`')  PORT_CHAR('~')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)          PORT_CHAR('M')  PORT_CHAR('m')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)          PORT_CHAR('R')  PORT_CHAR('r')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1)          PORT_CHAR('1')  PORT_CHAR('!')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)          PORT_CHAR('I')  PORT_CHAR('i')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KC10")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1)         PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)          PORT_CHAR('O')  PORT_CHAR('o')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5)          PORT_CHAR('5')  PORT_CHAR('%')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_END)        PORT_CHAR(UCHAR_MAMEKEY(END))  PORT_NAME("\xe2\x87\xa5 \xe2\x87\xa4")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6)          PORT_CHAR('6')  PORT_CHAR('^')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KC11")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LALT)       PORT_CHAR(UCHAR_MAMEKEY(LALT))
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("CTRL")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0xdf, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("RESET")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3) PORT_NAME("RESET") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mpf1_88_state::trigger_res), 0)
INPUT_PORTS_END


uint8_t mpf1_88_state::ipd_port_r()
{
	uint8_t data = 0x3f;

	// bit 0 to 4, keyboard array input
	for (int row = 0; row < 12; row++)
		if (!BIT(m_key_col, row))
			data &= m_key[row]->read();

	// bit 5, control key
	data &= m_ctrl->read();

	// bit 6, printer busy
	data |= m_centronics_busy ? 0x40 : 0;

	// bit 7, tape in
	data |= ((m_cassette)->input() > 0) ? 0x80 : 0;

	return data;
}

void mpf1_88_state::opd_port1_w(uint8_t data)
{
	// bit 0-3, keyboard array output
	m_key_col = (m_key_col & 0x00ff) | (data << 8);

	// bit 4, NMI enable */
	m_nmi_enable = BIT(data, 4);

	// bit 6, tape out (beep)
	m_leds[0] = !BIT(data, 6);
	m_speaker->level_w(BIT(data, 6));
	m_cassette->output(BIT(data, 6) ? 1.0 : -1.0);

	// bit 7, printer strobe
	m_centronics->write_strobe(BIT(data, 7));
}

void mpf1_88_state::opd_port2_w(uint8_t data)
{
	// bit 0-7, keyboard array output
	m_key_col = (m_key_col & 0xff00) | data;
}


HD44780_PIXEL_UPDATE(mpf1_88_state::lcd_pixel_update)
{
	if (line < 2 && pos < 20)
		bitmap.pix(6 + line * (8 + 1) + y, 6 + pos * 6 + x) = state ? 1 : 2;
}

void mpf1_88_state::lcd_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148)); // background
	palette.set_pen_color(1, rgb_t( 92,  83,  88)); // lcd pixel on
	palette.set_pen_color(2, rgb_t(131, 136, 139)); // lcd pixel off
}


TIMER_DEVICE_CALLBACK_MEMBER(mpf1_88_state::check_halt_callback)
{
	// hold-LED; the red one, is turned on when the processor is halted
	int led_halt = m_maincpu->state_int(I8086_HALT);
	m_leds[1] = led_halt;
}

TIMER_DEVICE_CALLBACK_MEMBER(mpf1_88_state::key_nmi)
{
	if (m_nmi_enable)
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}


void mpf1_88_state::machine_start()
{
	m_leds.resolve();

	m_maincpu->space(AS_PROGRAM).install_ram(0, m_ram->mask(), m_ram->pointer());

	// register for state saving
	save_item(NAME(m_nmi_enable));
}

void mpf1_88_state::machine_reset()
{
	m_nmi_enable = false;

	m_key_col = 0x00;
}


static void mpf1_88_isa8_cards(device_slot_interface &device)
{
	device.option_add("com", ISA8_COM);
	device.option_add("cga", ISA8_CGA);
	device.option_add("mda", ISA8_MDA);
}


void mpf1_88_state::mpf1_88(machine_config &config)
{
	I8088(config, m_maincpu, 14.318181_MHz_XTAL/3);
	m_maincpu->set_addrmap(AS_PROGRAM, &mpf1_88_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &mpf1_88_state::io_map);

	TIMER(config, "halt_timer").configure_periodic(FUNC(mpf1_88_state::check_halt_callback), attotime::from_hz(1));

	TIMER(config, "nmi_timer").configure_periodic(FUNC(mpf1_88_state::key_nmi), attotime::from_msec(15));

	auto &screen = SCREEN(config, "screen", SCREEN_TYPE_LCD);
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.set_size(132, 28);
	screen.set_visarea_full();
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(mpf1_88_state::lcd_palette), 3);

	hd44780_device &lcdc(HD44780(config, "lcdc", 270'000)); // TODO: clock not measured, datasheet typical clock used
	lcdc.set_lcd_size(2, 20);
	lcdc.set_pixel_update_cb(FUNC(mpf1_88_state::lcd_pixel_update));
	lcdc.set_function_set_at_any_time(true);

	RAM(config, m_ram).set_default_size("8K").set_extra_options("2K,4K,6K,16K,24K");

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set([this](int state) { m_centronics_busy = state; });
	output_latch_device &latch(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(latch);

	GENERIC_SOCKET(config, "rom_1", generic_linear_slot, "mpf1_rom", "bin,rom");
	GENERIC_SOCKET(config, "rom_2", generic_linear_slot, "mpf1_rom", "bin,rom");

	SOFTWARE_LIST(config, "rom_ls").set_original("mpf1_rom").set_filter("I88");

	// IFM-I/88 (Interface Module)
	//mpf1_exp_device &exp(MPF1_EXP(config, "exp", 3.579545_MHz_XTAL/2, mpf1_88_exp_devices, nullptr));
	//exp.set_program_space(m_maincpu, AS_PROGRAM);
	//exp.set_io_space(m_maincpu, AS_IO);
	isa8_device &isa8(ISA8(config, "isa", 3.579545_MHz_XTAL/2));
	isa8.set_memspace(m_maincpu, AS_PROGRAM);
	isa8.set_iospace(m_maincpu, AS_IO);
	ISA8_SLOT(config, "isa1", 0, "isa", mpf1_88_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa2", 0, "isa", mpf1_88_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa3", 0, "isa", mpf1_88_isa8_cards, nullptr, false);
}

} // anonymous namespace


ROM_START( mpf1_88 )
	ROM_REGION(0x4000, "rom_0", 0)
	ROM_LOAD("mon88_v1.11.u18", 0x0000, 0x4000, CRC(caa200f3) SHA1(9dfdd618e4cf94b3d3ed97a41d2fb0bf2842b4de))
ROM_END

//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY      FULLNAME                  FLAGS
COMP( 1985, mpf1_88,  0,      0,      mpf1_88, mpf1_88, mpf1_88_state, empty_init, "Multitech", "Micro-Professor I/88",   0 )



mpf1p.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/************************************************\
* Multitech Micro Professor 1 Plus               *
*                                                *
*     CPU: Z80 @ 1.79 MHz                        *
*     ROM: 8-kilobyte ROM monitor                *
*     RAM: 4 kilobytes                           *
*   Input: 49 key keyboard                       *
* Storage: Cassette tape                         *
*   Video: 20x 16-segment VFD                    *
*   Sound: Speaker                               *
\************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/i8255.h"
#include "sound/spkrdev.h"
#include "video/pwm.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "bus/mpf1/slot.h"
#include "imagedev/cassette.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "mpf1p.lh"


namespace {

class mpf1p_state : public driver_device
{
public:
	mpf1p_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_speaker(*this, "speaker")
		, m_vfd_pwm(*this, "vfd_pwm")
		, m_cassette(*this, "cassette")
		, m_key(*this, "PC%u", 1U)
		, m_special(*this, "SPECIAL")
		, m_leds(*this, "led%u", 0U)
	{ }

	void mpf1p(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER( trigger_res );

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;
	required_device<speaker_sound_device> m_speaker;
	required_device<pwm_display_device> m_vfd_pwm;
	required_device<cassette_image_device> m_cassette;
	required_ioport_array<20> m_key;
	required_ioport m_special;
	output_finder<2> m_leds;

	void mpf1_step(address_map &map) ATTR_COLD;
	void mpf1p_io_map(address_map &map) ATTR_COLD;
	void mpf1p_map(address_map &map) ATTR_COLD;

	uint8_t step_r(offs_t offset);

	uint8_t ppi1_pc_r();
	void ppi1_pa_w(uint8_t data);
	void ppi1_pb_w(uint8_t data);
	void ppi1_pc_w(uint8_t data);

	uint8_t ppi2_pc_r();
	void ppi2_pa_w(uint8_t data);
	void ppi2_pb_w(uint8_t data);
	void ppi2_pc_w(uint8_t data);

	int m_break = 0;
	int m_m1 = 0;

	uint32_t m_select = 0;
	uint16_t m_vfd_data = 0;

	void vfd_refresh();
};


void mpf1p_state::mpf1p_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom().region("rom_u2", 0);
	map(0x2000, 0x3fff).r("rom_u3", FUNC(generic_slot_device::read_rom));
	map(0xf000, 0xffff).ram();
}

void mpf1p_state::mpf1_step(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(mpf1p_state::step_r));
}

void mpf1p_state::mpf1p_io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x80, 0x83).mirror(0x0c).rw("ppi_1", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x90, 0x93).mirror(0x0c).rw("ppi_2", FUNC(i8255_device::read), FUNC(i8255_device::write));
}


INPUT_CHANGED_MEMBER( mpf1p_state::trigger_res )
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? CLEAR_LINE : ASSERT_LINE);
}

static INPUT_PORTS_START( mpf1p )
	PORT_START("PC1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1)     PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)     PORT_CHAR('A')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0xf8, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2)     PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)     PORT_CHAR('S')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LEFT)  PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0xf8, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)     PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)     PORT_CHAR('D')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0xf8, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)     PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)     PORT_CHAR('F')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DOWN)  PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0xf8, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5)     PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)     PORT_CHAR('G')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_UP)    PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0xf8, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6)     PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)     PORT_CHAR('H')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0xf8, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7)     PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)     PORT_CHAR('J')
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC8")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8)     PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)     PORT_CHAR('K') PORT_CHAR('^')
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC9")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9)     PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)     PORT_CHAR('L') PORT_CHAR('@')
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC10")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0)     PORT_CHAR('0') PORT_CHAR('*')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(':') PORT_CHAR(';')
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC11")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)     PORT_CHAR('Q')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)     PORT_CHAR('Z')
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC12")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)     PORT_CHAR('W')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)     PORT_CHAR('X')
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC13")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)     PORT_CHAR('E')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)     PORT_CHAR('C')
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC14")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)     PORT_CHAR('R')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)     PORT_CHAR('V')
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC15")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)     PORT_CHAR('T')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)     PORT_CHAR('B')
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC16")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)     PORT_CHAR('Y')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)     PORT_CHAR('N')
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC17")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)     PORT_CHAR('U')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)     PORT_CHAR('M')
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC18")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)     PORT_CHAR('I') PORT_CHAR('-')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC19")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)     PORT_CHAR('O') PORT_CHAR('=')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)  PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("PC20")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)     PORT_CHAR('P') PORT_CHAR('+')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('?') PORT_CHAR('/')
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("SPECIAL")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CONTROL") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT( 0xcf, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("RESET")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RESET") PORT_CODE(KEYCODE_F3) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mpf1p_state::trigger_res), 0)
INPUT_PORTS_END


void mpf1p_state::vfd_refresh()
{
	m_vfd_pwm->matrix(~m_select, ~m_vfd_data);
}


uint8_t mpf1p_state::ppi1_pc_r()
{
	uint8_t data = 0xff;

	// bit 4 and 5, shift and control keys
	data &= m_special->read();

	return data;
}

void mpf1p_state::ppi1_pa_w(uint8_t data)
{
	m_select = (m_select & 0xffffff00) | data;
	vfd_refresh();
}

void mpf1p_state::ppi1_pb_w(uint8_t data)
{
	m_select = (m_select & 0xffff00ff) | (data << 8);
	vfd_refresh();
}

void mpf1p_state::ppi1_pc_w(uint8_t data)
{
	m_select = (m_select & 0xff00ffff) | (data << 16);
	vfd_refresh();
}

uint8_t mpf1p_state::ppi2_pc_r()
{
	uint8_t data = 0xf7;

	// bit 0 to 2, keyboard rows 0 to 20
	for (int row = 0; row < 20; row++)
		if (!BIT(m_select, row))
			data &= m_key[row]->read();

	// bit 3, tape input
	data |= (m_cassette->input() > 0) ? 0x08 : 0x00;

	return data;
}

void mpf1p_state::ppi2_pa_w(uint8_t data)
{
	m_vfd_data = (m_vfd_data & 0xff00) | data;
	vfd_refresh();
}

void mpf1p_state::ppi2_pb_w(uint8_t data)
{
	// swap bits around for the 14-segment emulation
	data = bitswap<8>(data, 7, 6, 4, 2, 3, 5, 1, 0);

	m_vfd_data = (m_vfd_data & 0x00ff) | (data << 8);
	vfd_refresh();
}

void mpf1p_state::ppi2_pc_w(uint8_t data)
{
	// bit 4, monitor break control
	m_break = BIT(data, 4);

	if (m_break)
	{
		m_m1 = 0;
		m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
	}

	// bit 5, tape output, tone and led
	m_leds[0] = !BIT(data, 5);
	m_speaker->level_w(BIT(data, 5));
	m_cassette->output(BIT(data, 5) ? 1.0 : -1.0);
}


uint8_t mpf1p_state::step_r(offs_t offset)
{
	if (!m_break)
	{
		m_m1++;

		if (m_m1 == 5)
			m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
	}

	return m_maincpu->space(AS_PROGRAM).read_byte(offset);
}


void mpf1p_state::machine_start()
{
	m_leds.resolve();

	// register for state saving */
	save_item(NAME(m_break));
	save_item(NAME(m_m1));
	save_item(NAME(m_select));
}


void mpf1p_state::mpf1p(machine_config &config)
{
	Z80(config, m_maincpu, 3.579545_MHz_XTAL/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &mpf1p_state::mpf1p_map);
	m_maincpu->set_addrmap(AS_OPCODES, &mpf1p_state::mpf1_step);
	m_maincpu->set_addrmap(AS_IO, &mpf1p_state::mpf1p_io_map);
	m_maincpu->halt_cb().set_output("led1");

	config.set_default_layout(layout_mpf1p);

	i8255_device &ppi_1(I8255A(config, "ppi_1"));
	ppi_1.out_pa_callback().set(FUNC(mpf1p_state::ppi1_pa_w));
	ppi_1.out_pb_callback().set(FUNC(mpf1p_state::ppi1_pb_w));
	ppi_1.out_pc_callback().set(FUNC(mpf1p_state::ppi1_pc_w));
	ppi_1.in_pc_callback().set(FUNC(mpf1p_state::ppi1_pc_r));

	i8255_device &ppi_2(I8255A(config, "ppi_2"));
	ppi_2.out_pa_callback().set(FUNC(mpf1p_state::ppi2_pa_w));
	ppi_2.out_pb_callback().set(FUNC(mpf1p_state::ppi2_pb_w));
	ppi_2.out_pc_callback().set(FUNC(mpf1p_state::ppi2_pc_w));
	ppi_2.in_pc_callback().set(FUNC(mpf1p_state::ppi2_pc_r));

	PWM_DISPLAY(config, m_vfd_pwm).set_size(20, 16);
	m_vfd_pwm->set_segmask(0xfffff, 0xffff);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	mpf1_exp_device &exp(MPF1_EXP(config, "exp", 3.579545_MHz_XTAL/2, mpf1p_exp_devices, nullptr));
	exp.set_program_space(m_maincpu, AS_PROGRAM);
	exp.set_io_space(m_maincpu, AS_IO);
	exp.int_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	exp.nmi_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);
	exp.wait_handler().set_inputline(m_maincpu, Z80_INPUT_LINE_WAIT);

	GENERIC_SOCKET(config, "rom_u3", generic_linear_slot, "mpf1_rom", "bin,rom");

	SOFTWARE_LIST(config, "rom_ls").set_original("mpf1_rom").set_filter("IP");
}

} // anonymous namespace


ROM_START( mpf1p )
	ROM_REGION(0x2000, "rom_u2", 0)
	ROM_LOAD("mpf-1p_v1.1.u2", 0x0000, 0x2000, CRC(8bb241d3) SHA1(a05cf397452fe6a03e1ea9320985654f5910b20f))
ROM_END

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY      FULLNAME                  FLAGS
COMP( 1983, mpf1p,  0,      0,      mpf1p,   mpf1p, mpf1p_state, empty_init, "Multitech", "Micro-Professor 1 Plus", 0 )



mps1230.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    mps1230.c - Commodore MPS-1230 dot-matrix printer

    Skeleton made September, 2015 by R. Belmont

-------------------------------------

Commodore MPS-1230 Dot Matrix Printer
Commodore 1988/89
Hardware info by Guru

This is a 9-pin dot matrix printer manufactured by Commodore (possibly under license from Olivetti)
The printer is based on the Olivetti DM100 9-pin dot matrix printer with an added Commodore
serial port and other Commodore modifications. The service manual shows the PCB as 'DM10XC2',
however the actual PCB does not exactly match that shown in the service manual.
The firmware is modified by Commodore to support the CBM ASCII character and command set.

The printer comes standard with Centronics Parallel and Commodore Serial ports, sheet feed
and tractor feed and can be used with Commodore 64, 128/D, Amiga and IBM compatibles.

The ink ribbons are compatible with, and used in the following printers....
Commodore MPS 1230 printer
Olivetti DM 100, DM 90-1, DM 95, DM 99 & PR 98 printers
Birch BR-600 POS/cash register
Facit E 440 printer
Philips NMS 1016 & NMS 1432 printers
Senor Science K-700 & K-750 POS/cash register
Triumph Adler MPR 7120 printer


PCB Layout
----------

BA235
OLIVETTI OPE MADE IN HONGKONG
750746Y00-A-B9

    CENTRONICS         SERIAL
|-|------------|--------|---|----------|
|J899  J369   9306 7407 J02G      L387A|
|                                  J169|
|               D7810  6264 27512      |
|J708   MB624207                       |
|-----|                                |
      |      11.06MHz         J532     |
      |                                |
      |    555  L293  L6210 2803 2803  |
      |J404 74LS14  J477      2803 J821|
      |--------------------------------|
Notes: (all IC's shown)
      D7810    - NEC D7810HG ROM-less Microcontroller, clock input 11.06MHz  (64 pin 'spider' package)
      MB624207 - Fujitsu MB624207U MB62XXXX UHB Series 1.5um CMOS Gate Array marked 'OPE 200' (SDIP64)
      6264     - 8k x8 SRAM (DIP28)
      27512    - TMS27C512 64k x8 EPROM (DIP28)
                 S/W revisions dumped:
                                     'Release R - 1.1D 10/NOV/1988' with label 'PDL2'
                                     'Release R - 2.1E 09/AUG/1989' with label 'PEEK'
      L387A    - ST L387A 5V @ 0.5A Voltage Regulator
      2803     - ULN2803A 8-Channel Darlinton Driver (DIP18)
      L6210    - ST L6210R Dual Schottky Diode Bridge (DIP16)
      L293     - ST L293ER Quad Push-Pull Driver (DIP20)
      74LS14   - National DM74LS14N Hex Inverter with Schmitt Trigger (DIP14)
      555      - Texas Instruments NE555P Timer (DIP8)
      9306     - National NMC9306N 32bytes Serial EEPROM (DIP8)
      7407     - Texas Instruments ON7404N Hex Buffer with High Voltage Open-Collector Outputs (DIP14)
      J169     - 3-pin 9v power input connector (from built-in transformer)
      J821     - 6-pin connector for paper feed motor
      J532     - 12-pin connector for print head
      J477     - 2-pin print head transport motor connector
      J404     - 4-pin connector for paper-out photosensor
      J708     - 6-pin connector for transport motor encoder
      J899     - 15-pin connector for ribbon cable for console buttons and leds (feed, online etc)
      J369     - 36-pin centronics parallel connector
      J02G     - 6-pin Commodore serial bus connector

************************************************************************/

#include "emu.h"

#include "cpu/upd7810/upd7810.h"
#include "cpu/z80/z80.h"


namespace {

#define CPU_TAG "maincpu"

class mps1230_state : public driver_device
{
public:
	mps1230_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, CPU_TAG)
	{ }

	void mps1000(machine_config &config);
	void mps1230(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void mps1230_map(address_map &map) ATTR_COLD;
};

/***************************************************************************
    PARAMETERS
***************************************************************************/

/***************************************************************************
    START/RESET
***************************************************************************/

void mps1230_state::machine_start()
{
}

void mps1230_state::machine_reset()
{
}

/***************************************************************************
    ADDRESS MAP
***************************************************************************/

void mps1230_state::mps1230_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("maincpu", 0);
	map(0xc000, 0xdfff).ram(); // as per the service manual
}

/***************************************************************************
    INPUT PORTS
***************************************************************************/

static INPUT_PORTS_START( mps1230 )
INPUT_PORTS_END

void mps1230_state::mps1230(machine_config &config)
{
	UPD7810(config, m_maincpu, 11060000);
	m_maincpu->set_addrmap(AS_PROGRAM, &mps1230_state::mps1230_map);
}

void mps1230_state::mps1000(machine_config &config)
{
	Z80(config, m_maincpu, 4000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &mps1230_state::mps1230_map);
}

/***************************************************************************

  Game driver(s)

***************************************************************************/

ROM_START(mps1000)
	ROM_REGION(0x10000, "maincpu", 0)
	// ver 2.20, 06/DEC/1986 (But it could also perhaps mean 12/JUN/1986)
	// I can't tell the PCB reference because this was dumped from spare EPROMs from a drawer
	// The dump seems to be good because the data content of all of my 4 spare EPROMs matched perfectly.
	ROM_LOAD( "mps_1000_vers_2.20_12-06-86.rom", 0x000000, 0x02000, CRC(0a91ea8a) SHA1(ccb679f3d1f7f4eddb4e8899fe9e9a594dcfca5d) )
	// this is another Z80-based bios.
	ROM_LOAD( "hwh_28.06.88_ep12-2_s80r2.rom", 0x002000, 0x002000, CRC(1d17c586) SHA1(1afd1d1bbebe4d79c3cfcc30ac617b1a8ddb99e1) ) // to locate at 0000
ROM_END

ROM_START(mps1230)
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD( "pdl2.f03ee",   0x000000, 0x010000, CRC(f8a9f45c) SHA1(4e7bb0d382c55432665f5576b6a5cd3c4c33bd8e) ) // ver 1.1D, 10/NOV/1988
	ROM_LOAD( "peek.f03ee",   0x000000, 0x010000, CRC(b5215f25) SHA1(dcfdd16942652447c472301392d9b39514547af1) ) // ver 2.1E, 09/AUG/1989
ROM_END

} // anonymous namespace


/*    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY                        FULLNAME */
SYST( 1986, mps1000, 0,      0,      mps1000, mps1230, mps1230_state, empty_init, "Commodore Business Machines", "MPS-1000 Printer",     MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )
SYST( 1988, mps1230, 0,      0,      mps1230, mps1230, mps1230_state, empty_init, "Commodore Business Machines", "MPS-1230 NLQ Printer", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



mpz80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

    Morrow MPZ80 "Decision"

    Skeleton driver

*/

/*

    TODO:

    - trap logic
        - trap stop
        - trap aux
        - trap halt
        - trap int
        - trap void
        - in/out trap
        - read/write trap
        - exec trap
    - front panel LEDs
    - keyboard
    - I/O
        - Mult I/O (8259A PIC, 3x 8250 ACE, uPD1990C RTC, 4K ROM/RAM)
    - floppy
        - DJ/DMA controller (Z80, 1K RAM, 2/4K ROM, TTL floppy control logic) for 5.25" floppy drives
        - DJ2D/B controller for 8" floppy drives
    - hard disk
        - HDC/DMA controller (Seagate ST-506/Shugart SA1000)
        - HDCA controller (Shugart SA4000/Fujitsu M2301B/Winchester M2320B)
    - AM9512 FPU
    - models
        - Decision I Desk Top Model D1 (MPZ80, MM65KS, Wunderbus)
        - Decision I Desk Top Model D2 (MPZ80, MM65KS, Wunderbus, DJDMA, 2x DSDD 5.25")
        - Decision I Desk Top Model D2A (MPZ80, MM65KS, Wunderbus, DJDMA, 2x DSDD 5.25", HDCDMA, 5 or 16 MB hard disk?)
        - Decision I Desk Top Model D3A (MPZ80, MM65KS, Wunderbus, DJDMA, 2x DSDD 5.25", HDCDMA, 5 or 16 MB hard disk?)
        - Decision I Desk Top Model D3C (MPZ80, MM65KS, Wunderbus, DJDMA, 2x DSDD 5.25", HDCDMA, 5 or 16 MB hard disk?)

*/

#include "emu.h"
#include "mpz80.h"
#include "softlist_dev.h"

//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

enum
{
	NO_ACCESS = 0,
	READ_ONLY,
	EXECUTE_ONLY,
	FULL_ACCESS
};

#define TASK0   ((m_task & 0x0f) == 0)

#define R10 0x04


// mask register
#define MASK_STOP_ENBL      0x01
#define MASK_AUX_ENBL       0x02
#define MASK_TINT_ENBL      0x04
#define MASK_RUN_ENBL       0x08
#define MASK_HALT_ENBL      0x10
#define MASK_SINT_ENBL      0x20
#define MASK_IOENBL         0x40
#define MASK_ZIO_MODE       0x80



//**************************************************************************
//  INTERRUPTS
//**************************************************************************

//-------------------------------------------------
//  check_traps -
//-------------------------------------------------

inline void mpz80_state::check_traps()
{
	m_pretrap = !(m_trap_int & m_trap_halt & m_trap_stop & m_trap_aux);

	if (m_pretrap)
	{
		// latch trap condition
		m_status = m_trap_void;
		m_status |= m_trap_halt << 2;
		m_status |= m_trap_int << 3;
		m_status |= m_trap_stop << 4;
		m_status |= m_trap_aux << 5;

		// latch trap address
		m_pretrap_addr = ((m_addr >> 8) & 0xf0) | (m_pretrap_addr >> 4);

		// set M1 trap region start address
		m_trap_start = m_addr;
	}
}


//-------------------------------------------------
//  check_interrupt -
//-------------------------------------------------

inline void mpz80_state::check_interrupt()
{
	int tint_enbl = (m_mask & MASK_TINT_ENBL) ? 1 : 0;
	int sint_enbl = (m_mask & MASK_SINT_ENBL) ? 0 : 1;

	m_int_pend = !(m_nmi & m_pint);
	m_trap_int = !(m_int_pend & tint_enbl);

	int z80irq = CLEAR_LINE;
	int z80nmi = CLEAR_LINE;

	if (TASK0)
	{
		if (!m_pint && !sint_enbl) z80irq = ASSERT_LINE;
		if (!m_nmi && !sint_enbl) z80nmi = ASSERT_LINE;
	}
	else
	{
		if (!m_pint && !tint_enbl) z80irq = ASSERT_LINE;
		if (!m_nmi && !tint_enbl) z80nmi = ASSERT_LINE;
	}

	m_maincpu->set_input_line(INPUT_LINE_IRQ0, z80irq);
	m_maincpu->set_input_line(INPUT_LINE_NMI, z80nmi);

	check_traps();
}



//**************************************************************************
//  MEMORY MANAGEMENT
//**************************************************************************

//-------------------------------------------------
//  get_address -
//-------------------------------------------------

inline offs_t mpz80_state::get_address(offs_t offset)
{
	uint16_t map_addr = ((m_task & 0x0f) << 5) | ((offset & 0xf000) >> 11);
	uint8_t map = m_map_ram[map_addr];
	//uint8_t attr = m_map_ram[map_addr + 1];

	//logerror("task %02x map_addr %03x map %02x attr %02x address %06x\n", m_task, map_addr, map, attr, offset);

	// T7 T6 T5 T4 M7 M6 M5 M4 M3 M2 M1 M0 A11 A10 A9 A8 A7 A6 A5 A4 A3 A2 A1 A0
	return ((m_task & 0xf0) << 16) | (map << 12) | (offset & 0xfff);
}


//-------------------------------------------------
//  mmu_r -
//-------------------------------------------------

uint8_t mpz80_state::mmu_r(offs_t offset)
{
	if (m_trap && offset >= m_trap_start && offset <= m_trap_start + 0xf)
		return m_rom->base()[0x3f0 | (m_trap_reset << 10) | (offset - m_trap_start)];

	m_addr = get_address(offset);
	uint8_t data = 0;

	if (m_pretrap)
	{
		m_pretrap = 0;
		m_trap = 1;

		// latch trap address
		m_pretrap_addr = ((m_addr >> 8) & 0xf0) | (m_pretrap_addr >> 4);
		m_trap_addr = m_pretrap_addr;
	}

	if (TASK0 && (offset < 0x1000))
	{
		if (offset < 0x400)
		{
			data = m_ram->pointer()[offset & 0x3ff];
		}
		else if (offset == 0x400)
		{
			data = trap_addr_r();
		}
		else if (offset == 0x401)
		{
			data = keyboard_r();
		}
		else if (offset == 0x402)
		{
			data = switch_r();
		}
		else if (offset == 0x403)
		{
			data = status_r();
		}
		else if (offset >= 0x600 && offset < 0x800)
		{
			// TODO this might change the map RAM contents
		}
		else if (offset < 0xc00)
		{
			uint16_t rom_addr = (m_trap_reset << 10) | (offset & 0x3ff);
			data = m_rom->base()[rom_addr];
		}
		else
		{
			logerror("Unmapped LOCAL read at %06x\n", offset);
		}
	}
	else
	{
		data = m_s100->smemr_r(m_addr);
	}

	return data;
}


//-------------------------------------------------
//  mmu_w -
//-------------------------------------------------

void mpz80_state::mmu_w(offs_t offset, uint8_t data)
{
	m_addr = get_address(offset);

	if (TASK0 && (offset < 0x1000))
	{
		if (offset < 0x400)
		{
			m_ram->pointer()[offset & 0x3ff] = data;
		}
		else if (offset == 0x400)
		{
			disp_seg_w(data);
		}
		else if (offset == 0x401)
		{
			disp_col_w(data);
		}
		else if (offset == 0x402)
		{
			task_w(data);
		}
		else if (offset == 0x403)
		{
			mask_w(data);
		}
		else if (offset >= 0x600 && offset < 0x800)
		{
			m_map_ram[offset - 0x600] = data;
		}
		else
		{
			logerror("Unmapped LOCAL write at %06x\n", offset);
		}
	}
	else
	{
		m_s100->mwrt_w(m_addr, data);
	}
}


//-------------------------------------------------
//  get_io_address -
//-------------------------------------------------

inline offs_t mpz80_state::get_io_address(offs_t offset)
{
	if (m_mask & MASK_ZIO_MODE)
	{
		// echo port address onto upper address lines (8080 emulation)
		offset = ((offset << 8) & 0xff00) | (offset & 0xff);
	}

	return offset;
}


//-------------------------------------------------
//  mmu_io_r -
//-------------------------------------------------

uint8_t mpz80_state::mmu_io_r(offs_t offset)
{
	return m_s100->sinp_r(get_io_address(offset));
}


//-------------------------------------------------
//  mmu_io_w -
//-------------------------------------------------

void mpz80_state::mmu_io_w(offs_t offset, uint8_t data)
{
	m_s100->sout_w(get_io_address(offset), data);
}


//-------------------------------------------------
//  trap_addr_r - trap address register
//-------------------------------------------------

uint8_t mpz80_state::trap_addr_r()
{
	/*

	    bit     description

	    0       DADDR 12
	    1       DADDR 13
	    2       DADDR 14
	    3       DADDR 15
	    4       I-ADDR 12
	    5       I-ADDR 13
	    6       I-ADDR 14
	    7       I-ADDR 15

	*/

	return m_trap_addr;
}


//-------------------------------------------------
//  status_r - trap status register
//-------------------------------------------------

uint8_t mpz80_state::status_r()
{
	/*

	    bit     description

	    0       _TRAP VOID
	    1       _IORQ
	    2       _TRAP HALT
	    3       _TRAP INT
	    4       _TRAP STOP
	    5       _TRAP AUX
	    6       R10
	    7       _RD STB

	*/

	return m_status;
}


//-------------------------------------------------
//  task_w - task register
//-------------------------------------------------

void mpz80_state::task_w(uint8_t data)
{
	/*

	    bit     description

	    0       T0, A16
	    1       T1, A17
	    2       T2, A18
	    3       T3, A19
	    4       T4, S-100 A20
	    5       T5, S-100 A21
	    6       T6, S-100 A22
	    7       T7, S-100 A23

	*/

	m_task = data;

	m_trap_reset = 1;
	check_traps();
}


//-------------------------------------------------
//  mask_w - mask register
//-------------------------------------------------

void mpz80_state::mask_w(uint8_t data)
{
	/*

	    bit     description

	    0       _STOP ENBL
	    1       AUX ENBL
	    2       _TINT ENBL
	    3       RUN ENBL
	    4       _HALT ENBL
	    5       SINT ENBL
	    6       _IOENBL
	    7       _ZIO MODE

	*/

	m_mask = data;
}



//**************************************************************************
//  FRONT PANEL
//**************************************************************************

//-------------------------------------------------
//  keyboard_r - front panel keyboard
//-------------------------------------------------

uint8_t mpz80_state::keyboard_r()
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4
	    5
	    6
	    7

	*/

	return 0;
}


//-------------------------------------------------
//  switch_r - switch register
//-------------------------------------------------

uint8_t mpz80_state::switch_r()
{
	/*

	    bit     description

	    0       _TRAP RESET
	    1       INT PEND
	    2       16C S6
	    3       16C S5
	    4       16C S4
	    5       16C S3
	    6       16C S2
	    7       16C S1

	*/

	uint8_t data = 0;

	// trap reset
	data |= m_trap_reset;

	// interrupt pending
	data |= m_int_pend << 1;

	// boot address
	data |= m_16c->read() & 0xfc;

	return data;
}


//-------------------------------------------------
//  disp_seg_w - front panel segment
//-------------------------------------------------

void mpz80_state::disp_seg_w(uint8_t data)
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4
	    5
	    6
	    7

	*/
}


//-------------------------------------------------
//  disp_col_w - front panel column
//-------------------------------------------------

void mpz80_state::disp_col_w(uint8_t data)
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4
	    5
	    6
	    7

	*/
}



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( mpz80_mem )
//-------------------------------------------------

void mpz80_state::mpz80_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(mpz80_state::mmu_r), FUNC(mpz80_state::mmu_w));
/*
    Task 0 Segment 0 map:

    map(0x0000, 0x03ff).ram();
    map(0x0400, 0x0400).rw(FUNC(mpz80_state::trap_addr_r), FUNC(mpz80_state::disp_seg_w));
    map(0x0401, 0x0401).rw(FUNC(mpz80_state::keyboard_r), FUNC(mpz80_state::disp_col_w));
    map(0x0402, 0x0402).rw(FUNC(mpz80_state::switch_r), FUNC(mpz80_state::task_w));
    map(0x0403, 0x0403).rw(FUNC(mpz80_state::status_r), FUNC(mpz80_state::mask_w));
    map(0x0600, 0x07ff).ram().share("map_ram");
    map(0x0800, 0x0bff).rom().region(Z80_TAG, 0);
    map(0x0c00, 0x0c00).rw(AM9512_TAG, FUNC(am9512_device::read), FUNC(am9512_device::write));
*/
}

//-------------------------------------------------
//  ADDRESS_MAP( mpz80_io )
//-------------------------------------------------

void mpz80_state::mpz80_io(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(mpz80_state::mmu_io_r), FUNC(mpz80_state::mmu_io_w));
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( mpz80 )
//-------------------------------------------------

static INPUT_PORTS_START( mpz80 )
	PORT_START("16C")
	PORT_DIPNAME( 0xf8, 0xf8, "Power-On-Jump Address" ) PORT_DIPLOCATION("16C:1,2,3,4,5") PORT_CONDITION("12C", 0x02, EQUALS, 0x02)
	PORT_DIPSETTING(    0xf8, "F800H" )
	PORT_DIPSETTING(    0xf0, "F000H" )
	PORT_DIPSETTING(    0xe8, "E800H" )
	PORT_DIPSETTING(    0xe0, "E000H" )
	PORT_DIPSETTING(    0xd8, "D800H" )
	PORT_DIPSETTING(    0xd0, "D000H" )
	PORT_DIPSETTING(    0xc8, "C800H" )
	PORT_DIPSETTING(    0xc0, "C000H" )
	PORT_DIPSETTING(    0xb8, "B800H" )
	PORT_DIPSETTING(    0xb0, "B000H" )
	PORT_DIPSETTING(    0xa8, "A800H" )
	PORT_DIPSETTING(    0xa0, "A000H" )
	PORT_DIPSETTING(    0x98, "9800H" )
	PORT_DIPSETTING(    0x90, "9000H" )
	PORT_DIPSETTING(    0x88, "8800H" )
	PORT_DIPSETTING(    0x80, "8000H" )
	PORT_DIPSETTING(    0x78, "7800H" )
	PORT_DIPSETTING(    0x70, "7000H" )
	PORT_DIPSETTING(    0x68, "6800H" )
	PORT_DIPSETTING(    0x60, "6000H" )
	PORT_DIPSETTING(    0x58, "5800H" )
	PORT_DIPSETTING(    0x50, "5000H" )
	PORT_DIPSETTING(    0x48, "4800H" )
	PORT_DIPSETTING(    0x40, "4000H" )
	PORT_DIPSETTING(    0x38, "3800H" )
	PORT_DIPSETTING(    0x30, "3000H" )
	PORT_DIPSETTING(    0x28, "2800H" )
	PORT_DIPSETTING(    0x20, "2000H" )
	PORT_DIPSETTING(    0x18, "1800H" )
	PORT_DIPSETTING(    0x10, "Boot DJ/DMA" )
	PORT_DIPSETTING(    0x08, "Boot HD/DMA" )
	PORT_DIPSETTING(    0x00, "Boot HDCA" )
	PORT_DIPNAME( 0x70, 0x00, "Diagnostics" ) PORT_DIPLOCATION("16C:2,3,4") PORT_CONDITION("12C", 0x02, EQUALS, 0x00)
	PORT_DIPSETTING(    0x00, "Read Registers" )
	PORT_DIPSETTING(    0x10, "Write Registers" )
	PORT_DIPSETTING(    0x20, "Write Map RAMs" )
	PORT_DIPSETTING(    0x30, "Write R/W RAMs" )
	PORT_DIPSETTING(    0x40, "R/W FPP" )
	PORT_DIPSETTING(    0x50, "R/W S-100 Bus (High/Low)" )
	PORT_DIPSETTING(    0x60, "R/W S-100 Bus (Alternating)" )
	PORT_DIPSETTING(    0x70, "Read Switches" )
	PORT_DIPNAME( 0x04, 0x00, "Power Up" ) PORT_DIPLOCATION("16C:6")
	PORT_DIPSETTING(    0x04, "Boot Address" )
	PORT_DIPSETTING(    0x00, "Monitor" )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unused ) ) PORT_DIPLOCATION("16C:7")
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x01, 0x00, "S-100 MWRITE" ) PORT_DIPLOCATION("16C:8")
	PORT_DIPSETTING(    0x01, "Disabled" )
	PORT_DIPSETTING(    0x00, "Enabled" )

	PORT_START("12C")
	PORT_DIPNAME( 0x02, 0x02, "Operation Mode" )
	PORT_DIPSETTING(    0x02, "Monitor" )
	PORT_DIPSETTING(    0x00, "Diagnostic" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  S100_INTERFACE( s100_intf )
//-------------------------------------------------

void mpz80_state::s100_pint_w(int state)
{
	m_pint = (state == ASSERT_LINE) ? 0 : 1;

	check_interrupt();
}

void mpz80_state::s100_nmi_w(int state)
{
	if (state == ASSERT_LINE)
	{
		m_nmi = 0;
	}

	check_interrupt();
}

// slot devices
#include "bus/s100/dj2db.h"
#include "bus/s100/djdma.h"
#include "bus/s100/mm65k16s.h"
//#include "bus/s100/nsmdsa.h"
//#include "bus/s100/nsmdsad.h"
#include "bus/s100/wunderbus.h"

static void mpz80_s100_cards(device_slot_interface &device)
{
	device.option_add("mm65k16s", S100_MM65K16S);
	device.option_add("wunderbus", S100_WUNDERBUS);
	device.option_add("dj2db", S100_DJ2DB);
	device.option_add("djdma", S100_DJDMA);
//  device.option_add("multio", S100_MULTIO);
//  device.option_add("hdcdma", S100_HDCDMA);
//  device.option_add("hdca", S100_HDCA);
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( mpz80 )
//-------------------------------------------------

void mpz80_state::machine_start()
{
	m_task = 0;
}


void mpz80_state::machine_reset()
{
	m_trap_reset = 0;
	m_trap = 1;
	m_trap_start = 0;

	m_nmi = 1;

	check_interrupt();
}



//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

//-------------------------------------------------
//  machine_config( mpz80 )
//-------------------------------------------------

void mpz80_state::mpz80(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, XTAL(4'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &mpz80_state::mpz80_mem);
	m_maincpu->set_addrmap(AS_IO, &mpz80_state::mpz80_io);

	// S-100
	S100_BUS(config, m_s100, XTAL(4'000'000) / 2);
	m_s100->irq().set(FUNC(mpz80_state::s100_pint_w));
	m_s100->nmi().set(FUNC(mpz80_state::s100_nmi_w));
	//m_s100->rdy().set_inputline(m_maincpu, Z80_INPUT_LINE_WAIT);
	S100_SLOT(config, S100_TAG ":1", mpz80_s100_cards, "mm65k16s");
	S100_SLOT(config, S100_TAG ":2", mpz80_s100_cards, "wunderbus");
	S100_SLOT(config, S100_TAG ":3", mpz80_s100_cards, "dj2db");
	S100_SLOT(config, S100_TAG ":4", mpz80_s100_cards, nullptr);//"hdcdma")
	S100_SLOT(config, S100_TAG ":5", mpz80_s100_cards, nullptr);
	S100_SLOT(config, S100_TAG ":6", mpz80_s100_cards, nullptr);
	S100_SLOT(config, S100_TAG ":7", mpz80_s100_cards, nullptr);
	S100_SLOT(config, S100_TAG ":8", mpz80_s100_cards, nullptr);
	S100_SLOT(config, S100_TAG ":9", mpz80_s100_cards, nullptr);
	S100_SLOT(config, S100_TAG ":10", mpz80_s100_cards, nullptr);
	S100_SLOT(config, S100_TAG ":11", mpz80_s100_cards, nullptr);
	S100_SLOT(config, S100_TAG ":12", mpz80_s100_cards, nullptr);
	S100_SLOT(config, S100_TAG ":13", mpz80_s100_cards, nullptr);
	S100_SLOT(config, S100_TAG ":14", mpz80_s100_cards, nullptr);

	// internal ram
	RAM(config, RAM_TAG).set_default_size("65K"); // 65K???

	// software list
	SOFTWARE_LIST(config, "flop_list").set_original("mpz80");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( mpz80 )
//-------------------------------------------------

ROM_START( mpz80 )
	ROM_REGION( 0x1000, Z80_TAG, 0 )
	ROM_DEFAULT_BIOS("447")
	ROM_SYSTEM_BIOS( 0, "373", "3.73" )
	ROMX_LOAD( "mpz80 mon3.73 fb34.17c", 0x0000, 0x1000, CRC(0bbffaec) SHA1(005ba726fc071f06cb1c969d170960438a3fc1a8), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "375", "3.75" )
	ROMX_LOAD( "mpz80 mon3.75 0706.17c", 0x0000, 0x1000, CRC(1118a592) SHA1(d70f94c09602cd0bdc4fbaeb14989e8cc1540960), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "447", "4.47" )
	ROMX_LOAD( "mon 4.47 f4f6.17c", 0x0000, 0x1000, CRC(b99c5d7f) SHA1(11181432ee524c7e5a68ead0671fc945256f5d1b), ROM_BIOS(2) )

	ROM_REGION( 0x20, "s100rev2", 0 )
	ROM_LOAD( "z80-2 15a.15a", 0x00, 0x20, CRC(8a84249d) SHA1(dfbc49c5944f110f48419fd893fa84f4f0e113b8) ) // 82S123 or 6331

	ROM_REGION( 0xeb, "s100rev3", 0 )
	ROM_LOAD( "z80-15a-a.15a", 0x00, 0xeb, CRC(713243cd) SHA1(802b318cc9795d87f03622e878d9a4d5d7dea7d4) ) // 82S153

	ROM_REGION( 0x104, "mm1", 0 )
	ROM_LOAD( "z80-2 5c.5c", 0x000, 0x104, CRC(732be0cd) SHA1(545a37e5a871fcd1bb59b30056b837f03889b4d5) ) // PAL16R4
ROM_END


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY            FULLNAME    FLAGS
COMP( 1980, mpz80, 0,      0,      mpz80,   mpz80, mpz80_state, empty_init, "Morrow Designs",  "MPZ80",    MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



ms0515.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Sergey Svishchev
/***************************************************************************

    Elektronika MS 0515

    To do:
    - softlist
    . sound
    - overscan color
    - serial printer
    - ?? 512K memory expansion
    - ?? refresh rate change
    - ?? parallel printer
    - ?? cassette (only with Version A firmware)
    - ?? port 177770
    - ?? mc1702 (8086 co-processor)

    Docs:
    - http://www.tis.kz/docs/MC-0515/mc0515-ed.rar schematics etc.
    - http://www.tis.kz/docs/MC-0515/mc0515-to.rar user manual
    - http://www.tis.kz/docs/MC-0515/hc4-to.rar technical manual
    - http://www.tis.kz/docs/MC-0515/mc0515-po.rar diag manual
    - http://www.tis.kz/docs/MC-0515/mc0515-osa.rar OS manual

****************************************************************************/

#include "emu.h"

#include "ms7004.h"

#include "bus/rs232/rs232.h"
#include "cpu/t11/t11.h"
#include "imagedev/floppy.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/pit8253.h"
#include "machine/ram.h"
#include "machine/wd_fdc.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "formats/ms0515_dsk.h"

#include "ms0515.lh"


#define LOG_BANK    (1U << 1)
#define LOG_SYSREG  (1U << 2)

//#define VERBOSE (LOG_GENERAL | LOG_BANK | LOG_SYSREG)
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"

#define LOGBANK(format, ...)    LOGMASKED(LOG_BANK,   "%11.6f at %s: " format, machine().time().as_double(), machine().describe_context(), __VA_ARGS__)
#define LOGSYSREG(format, ...)  LOGMASKED(LOG_SYSREG, "%11.6f at %s: " format, machine().time().as_double(), machine().describe_context(), __VA_ARGS__)


namespace {

class ms0515_state : public driver_device
{
public:
	ms0515_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_fdc(*this, "vg93")
		, m_floppies(*this, "vg93:%u", 0U)
		, m_i8251line(*this, "i8251line")
		, m_rs232(*this, "rs232")
		, m_i8251kbd(*this, "i8251kbd")
		, m_ms7004(*this, "ms7004")
		, m_pit8253(*this, "pit8253")
		, m_speaker(*this, "speaker")
		, m_bank(*this, "bank%u", 0U)
		, m_led9(*this, "led9")
		, m_led16(*this, "led16")
		, m_led17(*this, "led17")
	{ }

	void ms0515(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void ms0515_palette(palette_device &palette) const;
	uint32_t screen_update_ms0515(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void screen_vblank(int state);

	void ms0515_bank_w(uint16_t data);

	uint16_t ms0515_halt_r();
	void ms0515_halt_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

	void ms0515_porta_w(uint8_t data);
	uint8_t ms0515_portb_r();
	void ms0515_portc_w(uint8_t data);

	void write_keyboard_clock(int state);
	void write_line_clock(int state);
	void pit8253_out2_changed(int state);

	static void floppy_formats(format_registration &fr);

	void irq2_w(int state);
	void irq5_w(int state);
	void irq8_w(int state);
	void irq9_w(int state);
	void irq11_w(int state);

	void ms0515_mem(address_map &map) ATTR_COLD;

	void irq_encoder(int irq, int state);

	required_device<t11_device> m_maincpu; // actual CPU is T11 clone, KR1807VM1
	required_device<ram_device> m_ram;
	required_device<kr1818vg93_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppies;
	required_device<i8251_device> m_i8251line;
	required_device<rs232_port_device> m_rs232;
	required_device<i8251_device> m_i8251kbd;
	required_device<ms7004_device> m_ms7004;
	required_device<pit8253_device> m_pit8253;
	required_device<speaker_sound_device> m_speaker;
	required_memory_bank_array<7> m_bank;
	output_finder<> m_led9;
	output_finder<> m_led16;
	output_finder<> m_led17;

	uint8_t *m_video_ram = nullptr;
	uint8_t m_sysrega = 0, m_sysregc = 0;
	uint16_t m_bankreg = 0, m_haltreg = 0;
	uint16_t m_irqs = 0;
	int m_blink = 0;
	floppy_image_device *m_floppy = nullptr;
};

void ms0515_state::ms0515_mem(address_map &map)
{
	map.unmap_value_high();
	map(0000000, 0017777).bankrw("bank0"); // RAM
	map(0020000, 0037777).bankrw("bank1"); // RAM
	map(0040000, 0057777).bankrw("bank2"); // RAM
	map(0060000, 0077777).bankrw("bank3"); // RAM
	map(0100000, 0117777).bankrw("bank4"); // RAM
	map(0120000, 0137777).bankrw("bank5"); // RAM
	map(0140000, 0157777).bankrw("bank6"); // RAM

	map(0160000, 0177377).rom().nopw();

	map(0177400, 0177437).w(FUNC(ms0515_state::ms0515_bank_w)); // Register for RAM expansion

	map(0177440, 0177440).r(m_i8251kbd, FUNC(i8251_device::data_r));
	map(0177442, 0177442).rw(m_i8251kbd, FUNC(i8251_device::status_r), FUNC(i8251_device::control_w));
	map(0177460, 0177460).w(m_i8251kbd, FUNC(i8251_device::data_w));
	map(0177462, 0177462).w(m_i8251kbd, FUNC(i8251_device::control_w));

	map(0177500, 0177507).rw(m_pit8253, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0177520, 0177527).w(m_pit8253, FUNC(pit8253_device::write)).umask16(0x00ff);

	map(0177540, 0177547).noprw();
//  map(0177540, 0177541)
//  map(0177542, 0177543)
//  map(0177544, 0177545)  // i8255 for MS-7007 Keyboard
//  map(0177546, 0177547)

	map(0177600, 0177607).rw("ppi8255_1", FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);

	map(0177640, 0177640).rw(m_fdc, FUNC(kr1818vg93_device::status_r), FUNC(kr1818vg93_device::cmd_w));
	map(0177642, 0177642).rw(m_fdc, FUNC(kr1818vg93_device::track_r), FUNC(kr1818vg93_device::track_w));
	map(0177644, 0177644).rw(m_fdc, FUNC(kr1818vg93_device::sector_r), FUNC(kr1818vg93_device::sector_w));
	map(0177646, 0177646).rw(m_fdc, FUNC(kr1818vg93_device::data_r), FUNC(kr1818vg93_device::data_w));

	map(0177700, 0177700).r(m_i8251line, FUNC(i8251_device::data_r));
	map(0177702, 0177702).rw(m_i8251line, FUNC(i8251_device::status_r), FUNC(i8251_device::control_w));
	map(0177720, 0177720).w(m_i8251line, FUNC(i8251_device::data_w));
	map(0177722, 0177722).w(m_i8251line, FUNC(i8251_device::control_w));

	map(0177770, 0177771).rw(FUNC(ms0515_state::ms0515_halt_r), FUNC(ms0515_state::ms0515_halt_w)); // read/write -- halt and system timer
}

/*
 * (page 15-16)
 *
 * 6-0  RAM banking
 * 7    VRAM access enable
 * 8    vblank IRQ line (1 -- assert)
 * 9    timer IRQ enable (1 -- enable)
 * 11-10 VRAM banking
 * 12   parallel port STROBE signal
 * 13   parallel port ... signal
 * 14-15 unused
 */
void ms0515_state::ms0515_bank_w(uint16_t data)
{
	uint8_t *ram = m_ram->pointer();

	LOGBANK("Bank <- %04x (vblank %d timer %d)\n", data, BIT(data, 8), BIT(data, 9));

	if (BIT(data ^ m_bankreg, 8)) irq2_w(BIT(data, 8) ? ASSERT_LINE : CLEAR_LINE);

	m_bankreg = data;

	m_bank[0]->set_base(ram + 0000000 + BIT(data, 0) * 0160000);
	m_bank[1]->set_base(ram + 0020000 + BIT(data, 1) * 0160000);
	m_bank[2]->set_base(ram + 0040000 + BIT(data, 2) * 0160000);
	m_bank[3]->set_base(ram + 0060000 + BIT(data, 3) * 0160000);
	m_bank[4]->set_base(ram + 0100000 + BIT(data, 4) * 0160000);
	m_bank[5]->set_base(ram + 0120000 + BIT(data, 5) * 0160000);
	m_bank[6]->set_base(ram + 0140000 + BIT(data, 6) * 0160000);

	if (BIT(data, 7))
	{
		switch ((data >> 10) & 3)
		{
		case 0: // 000000 - 037777
			m_bank[0]->set_base(ram + 0000000 + 0340000);
			m_bank[1]->set_base(ram + 0020000 + 0340000);
			break;
		case 1: // 040000 - 077777
			m_bank[2]->set_base(ram + 0000000 + 0340000);
			m_bank[3]->set_base(ram + 0020000 + 0340000);
			break;
		case 2:
		case 3: // 100000 - 137777
			m_bank[4]->set_base(ram + 0000000 + 0340000);
			m_bank[5]->set_base(ram + 0020000 + 0340000);
			break;
		}
	}
}

uint16_t ms0515_state::ms0515_halt_r()
{
	return m_haltreg;
}

void ms0515_state::ms0515_halt_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	COMBINE_DATA(&m_haltreg);
}

/*
 * b7 -- ROM bank
 * b6 -- cassette data out
 * b5 -- LED VD16
 * b4 -- LED VD9
 * b3 -- floppy side select (?? 1 -- top)
 * b2 -- floppy motor (0 -- on)
 * b1-0 -- floppy drive select
 *
 * DZ0 = drive 0 side 0 (bottom)
 * DZ1 = drive 1 side 0 (bottom)
 * DZ2 = drive 0 side 1 (top)
 * DZ3 = drive 1 side 1 (top)
 *
 * MZ1 = drive 1 side 0-1
 */
void ms0515_state::ms0515_porta_w(uint8_t data)
{
	LOGSYSREG("Sysreg A <- %02x\n", data);

	m_led16 = BIT(data, 5);
	m_led9 = BIT(data, 4);

	switch (data & 3)
	{
	case 0:
		m_floppy = m_floppies[0]->get_device();
		break;

	case 1:
		m_floppy = m_floppies[1]->get_device();
		break;

	default:
		m_floppy = nullptr;
		break;
	}

	if (m_floppy)
	{
		m_fdc->set_floppy(m_floppy);
		m_floppy->ss_w(!BIT(data, 3));
		m_floppy->mon_w(BIT(data, 2));
	}
	else
	{
		for (auto &floppy : m_floppies)
			if (floppy->get_device() != nullptr)
				floppy->get_device()->mon_w(1);
	}

	m_sysrega = data;
}

/*
 * b7 -- cassette data in
 * b6-5 -- reserved for IRPR-M (parallel) port
 * b4-3 -- DIP switches on video board, 00 -- 50 Hz, 01 -- 60 Hz, 1x -- 72 Hz
 * b2 -- floppy ready signal (0 -- ready)
 * b1 -- floppy drq (1 -- ready)
 * b0 -- floppy intrq (0 -- ready)
 */
uint8_t ms0515_state::ms0515_portb_r()
{
	uint8_t data;

	data = m_fdc->intrq_r();
	data |= m_fdc->drq_r() << 1;

	if (m_floppy)
	{
		data |= !m_floppy->ready_r() << 2;
	}

	LOGSYSREG("Sysreg B == %02x\n", data);

	return data;
}


/*
 * b7 -- sound out gate
 * b6 -- sound out route to speaker
 * b5 -- sound ??
 * b4 -- LED VD17
 * b3 -- video resolution, 0: 320x200, 1: 640x200
 * b2-0 -- overscan color
 */
void ms0515_state::ms0515_portc_w(uint8_t data)
{
	LOGSYSREG("Sysreg C <- %02x\n", data);

	m_pit8253->write_gate2(BIT(data, 7));
	m_led17 = BIT(data, 4);

	m_sysregc = data;
}

void ms0515_state::write_keyboard_clock(int state)
{
	m_i8251kbd->write_txc(state);
	m_i8251kbd->write_rxc(state);
}

void ms0515_state::write_line_clock(int state)
{
	m_i8251line->write_txc(state);
	m_i8251line->write_rxc(state);
}

void ms0515_state::pit8253_out2_changed(int state)
{
	m_speaker->level_w(state);
}

void ms0515_state::machine_start()
{
	m_led9.resolve();
	m_led16.resolve();
	m_led17.resolve();
}

void ms0515_state::machine_reset()
{
	uint8_t *ram = m_ram->pointer();
	ms0515_bank_w(0);

	m_video_ram = ram + 0000000 + 0340000;
	m_blink = 0;
	m_haltreg = 0;
	m_irqs = 0;
	m_floppy = nullptr;
}

/* Input ports */
static INPUT_PORTS_START( ms0515 )
	PORT_START("SA1")
	PORT_DIPNAME(0x03, 0x00, "Refresh rate") PORT_DIPLOCATION("E:3,4")
	PORT_DIPSETTING(0x00, "50 Hz")
	PORT_DIPSETTING(0x01, "60 Hz")
	PORT_DIPSETTING(0x02, "72 Hz")
INPUT_PORTS_END

void ms0515_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_MS0515_FORMAT);
}

static void ms0515_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

uint32_t ms0515_state::screen_update_ms0515(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	int addr = 0;

	if (BIT(m_sysregc, 3))
	{
		uint8_t fg = m_sysregc & 7;
		uint8_t bg = fg ^ 7;
		for (int y = 0; y < 200; y++)
		{
			int horpos = 0;
			for (int x = 0; x < 40; x++)
			{
				uint16_t code = (m_video_ram[addr++] << 8);
				code |= m_video_ram[addr++];
				for (int b = 0; b < 16; b++)
				{
					// In lower res mode we will just double pixels
					bitmap.pix(y, horpos++) = ((code >> (15 - b)) & 0x01) ? bg : fg;
				}
			}
		}
	}
	else
	{
		for (int y = 0; y < 200; y++)
		{
			int horpos = 0;
			for (int x = 0; x < 40; x++)
			{
				uint8_t code = m_video_ram[addr++];
				uint8_t attr = m_video_ram[addr++];
				uint8_t fg = (attr & 7) + BIT(attr, 6) * 8;
				uint8_t bg = ((attr >> 3) & 7) + BIT(attr, 6) * 8;
				if (BIT(attr, 7) && (m_blink == 20))
				{
					uint8_t tmp = fg;
					fg = bg;
					bg = tmp;
					m_blink = -1;
				}
				for (int b = 0; b < 8; b++)
				{
					// In lower res mode we will just double pixels
					bitmap.pix(y, horpos++) = ((code >> (7 - b)) & 0x01) ? fg : bg;
					bitmap.pix(y, horpos++) = ((code >> (7 - b)) & 0x01) ? fg : bg;
				}
			}
		}
	}
	m_blink++;
	return 0;
}

void ms0515_state::screen_vblank(int state)
{
//  irq2_w(state ? ASSERT_LINE : CLEAR_LINE);
	if (BIT(m_bankreg, 9))
		irq11_w(state ? ASSERT_LINE : CLEAR_LINE);
}

void ms0515_state::ms0515_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(0, 0, 0));
	palette.set_pen_color(1, rgb_t(0, 0, 127));
	palette.set_pen_color(2, rgb_t(127, 0, 0));
	palette.set_pen_color(3, rgb_t(127, 0, 127));
	palette.set_pen_color(4, rgb_t(0, 127, 0));
	palette.set_pen_color(5, rgb_t(0, 127, 127));
	palette.set_pen_color(6, rgb_t(127, 127, 0));
	palette.set_pen_color(7, rgb_t(127, 127, 127));

	palette.set_pen_color(8, rgb_t(127, 127, 127));
	palette.set_pen_color(9, rgb_t(127, 127, 255));
	palette.set_pen_color(10, rgb_t(255, 127, 127));
	palette.set_pen_color(11, rgb_t(255, 127, 255));
	palette.set_pen_color(12, rgb_t(127, 255, 127));
	palette.set_pen_color(13, rgb_t(127, 255, 255));
	palette.set_pen_color(14, rgb_t(255, 255, 127));
	palette.set_pen_color(15, rgb_t(255, 255, 255));
}

// from vt240.cpp
void ms0515_state::irq_encoder(int irq, int state)
{
	if (state == ASSERT_LINE)
		m_irqs |= (1 << irq);
	else
		m_irqs &= ~(1 << irq);

	int i;
	for (i = 15; i > 0; i--)
	{
		if (m_irqs & (1 << i)) break;
	}
	m_maincpu->set_input_line(t11_device::CP3_LINE, (i & 8) ? ASSERT_LINE : CLEAR_LINE);
	m_maincpu->set_input_line(t11_device::CP2_LINE, (i & 4) ? ASSERT_LINE : CLEAR_LINE);
	m_maincpu->set_input_line(t11_device::CP1_LINE, (i & 2) ? ASSERT_LINE : CLEAR_LINE);
	m_maincpu->set_input_line(t11_device::CP0_LINE, (i & 1) ? ASSERT_LINE : CLEAR_LINE);
}

/*
 * interrupts (p. 21-22)
 *
 * IRQ  CPx  Pri Vec Device
 * ---  ---  --- --- ------
 * 11   LHLL 6   100 timer
 * 9    LHHL 6   110 serial RX
 * 8    LHHH 6   114 serial TX
 * 5    HLHL 5   130 7004 keyboard
 * 3    HHLL 4   060 7007 keyboard
 * 2    HHLH 4   064 vblank
 */

void ms0515_state::irq2_w(int state)
{
	irq_encoder(2, state);
}

void ms0515_state::irq5_w(int state)
{
	irq_encoder(5, state);
}

void ms0515_state::irq8_w(int state)
{
	irq_encoder(8, state);
}

void ms0515_state::irq9_w(int state)
{
	irq_encoder(9, state);
}

void ms0515_state::irq11_w(int state)
{
	irq_encoder(11, state);
}

void ms0515_state::ms0515(machine_config &config)
{
	/* basic machine hardware */
	T11(config, m_maincpu, XTAL(15'000'000) / 2); // actual CPU is T11 clone, KR1807VM1
	m_maincpu->set_initial_mode(0xf2ff);
	m_maincpu->set_addrmap(AS_PROGRAM, &ms0515_state::ms0515_mem);

	/* video hardware -- 50 Hz refresh rate */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(XTAL(15'000'000), 958, 0, 640, 313, 0, 200);
	screen.set_screen_update(FUNC(ms0515_state::screen_update_ms0515));
	screen.screen_vblank().set(FUNC(ms0515_state::screen_vblank));
	screen.set_palette("palette");
	config.set_default_layout(layout_ms0515);

	PALETTE(config, "palette", FUNC(ms0515_state::ms0515_palette), 16);

	KR1818VG93(config, m_fdc, 1000000);
	FLOPPY_CONNECTOR(config, "vg93:0", ms0515_floppies, "525qd", ms0515_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "vg93:1", ms0515_floppies, "525qd", ms0515_state::floppy_formats).enable_sound(true);

	i8255_device &ppi(I8255(config, "ppi8255_1"));
	ppi.out_pa_callback().set(FUNC(ms0515_state::ms0515_porta_w));
	ppi.in_pb_callback().set(FUNC(ms0515_state::ms0515_portb_r));
	ppi.out_pc_callback().set(FUNC(ms0515_state::ms0515_portc_w));

	// serial connection to printer
	I8251(config, m_i8251line, 0);
	m_i8251line->txd_handler().set(m_rs232, FUNC(rs232_port_device::write_txd));
	m_i8251line->rxrdy_handler().set(FUNC(ms0515_state::irq9_w));
	m_i8251line->txrdy_handler().set(FUNC(ms0515_state::irq8_w));

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set(m_i8251line, FUNC(i8251_device::write_rxd));
	m_rs232->cts_handler().set(m_i8251line, FUNC(i8251_device::write_cts));
	m_rs232->dsr_handler().set(m_i8251line, FUNC(i8251_device::write_dsr));

	// serial connection to MS7004 keyboard
	I8251(config, m_i8251kbd, 0);
	m_i8251kbd->rxrdy_handler().set(FUNC(ms0515_state::irq5_w));
	m_i8251kbd->txd_handler().set("ms7004", FUNC(ms7004_device::write_rxd));

	MS7004(config, m_ms7004, 0);
	m_ms7004->tx_handler().set(m_i8251kbd, FUNC(i8251_device::write_rxd));
	m_ms7004->rts_handler().set(m_i8251kbd, FUNC(i8251_device::write_cts));

	clock_device &keyboard_clock(CLOCK(config, "keyboard_clock", 4800 * 16));
	keyboard_clock.signal_handler().set(FUNC(ms0515_state::write_keyboard_clock));

	PIT8253(config, m_pit8253, 0);
	m_pit8253->set_clk<0>(XTAL(2'000'000));
//  m_pit8253->out_handler<0>().set(FUNC(ms0515_state::write_keyboard_clock));
	m_pit8253->set_clk<1>(XTAL(2'000'000));
	m_pit8253->out_handler<0>().set(FUNC(ms0515_state::write_line_clock));
	m_pit8253->set_clk<2>(XTAL(2'000'000));
	m_pit8253->out_handler<2>().set(FUNC(ms0515_state::pit8253_out2_changed));

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.45);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("128K");
}

/* ROM definition */
ROM_START( ms0515 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS( "b" )
	ROM_SYSTEM_BIOS( 0, "a", "Version A" )
	ROMX_LOAD( "7004l.bin", 0xc000, 0x2000, CRC(b08b3b73) SHA1(c12fd4672598cdf499656dcbb4118d787769d589), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "7004h.bin", 0xc001, 0x2000, CRC(515dcf99) SHA1(edd34300fd642c89ce321321e1b12493cd16b7a5), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "b", "Version B" )
	ROMX_LOAD( "0515l.rf4", 0xc000, 0x2000, CRC(85b608a4) SHA1(5b1bb0586d8f7a8a21de69200b08e0b28a318999), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "0515h.rf4", 0xc001, 0x2000, CRC(e3ff6da9) SHA1(3febccf40abc2e3ca7db3f6f3884be117722dd8b), ROM_SKIP(1) | ROM_BIOS(1))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY        FULLNAME   FLAGS
COMP( 1990, ms0515, 0,      0,      ms0515,  ms0515, ms0515_state, empty_init, "Elektronika", "MS 0515", 0 )



ms6102.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************

    Elektronika MS6102.02 terminal

    https://goo.gl/photos/xJManS26QTxG1T7M7
        Schematics

    http://sfrolov.livejournal.com/110770.html
        Photos

    To do:
    - character attributes
    - improve keyboard response and add LED layout (MS7002)
    - verify CRTC clock

    Chips:
    - DD5 - KR580WM80A (8080 clone) - CPU
    - DD7 - KR580WT57 (8257 clone) - DMAC
    - DD9 - KR1601RR1 (1024x4 bit NVRAM)
    - DD21 - KR581WA1A (TR6402 clone) - UART
    - DD55, DD56 - KR580WG75 (8275 clone) - CRTC
    - DD59 - KR556RT5 - alternate chargen ROM
    - DD64 - K556RT4 - chargen layout table ROM
    - DD70 - K555RE4 - default chargen ROM
    - DD75 - KR580WI53 (8253 clone) - timer
    - DD76 - KR580WW51A (8251 clone) - UART
    - DD80 - K589IK14 (8214 clone) - interrupt control unit

****************************************************************************/

#include "emu.h"

#include "vt100_kbd.h"

#include "bus/rs232/rs232.h"
#include "cpu/i8085/i8085.h"
#include "machine/ay31015.h"
#include "machine/clock.h"
#include "machine/i8214.h"
#include "machine/i8251.h"
#include "machine/i8257.h"
#include "machine/kr1601rr1.h"
#include "machine/pit8253.h"
#include "machine/ripple_counter.h"
#include "video/i8275.h"

#include "emupal.h"
#include "screen.h"


#define VERBOSE (LOG_GENERAL)
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"


namespace {

class ms6102_state : public driver_device
{
public:
	ms6102_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_p_videoram(*this, "videoram")
		, m_maincpu(*this, "maincpu")
		, m_earom(*this, "earom")
		, m_pic(*this, "i8214")
		, m_dma8257(*this, "dma8257")
		, m_i8251(*this, "i8251")
		, m_rs232(*this, "rs232")
		, m_kbd_uart(*this, "589wa1")
		, m_keyboard(*this, "keyboard")
		, m_screen(*this, "screen")
		, m_palette(*this, "palette")
		, m_crtc1(*this, "i8275_1")
		, m_crtc2(*this, "i8275_2")
		, m_p_chargen(*this, "chargen")
		, m_p_charmap(*this, "charmap")
	{ }

	void ms6102(machine_config &config);
	void ms6102_io(address_map &map) ATTR_COLD;
	void ms6102_mem(address_map &map) ATTR_COLD;

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	template <unsigned N> void irq(int state) { m_pic->r_w(N, state ? 0 : 1); }

	I8275_DRAW_CHARACTER_MEMBER(display_pixels);

	void hrq_w(int state);
	void irq_w(int state);

	void pic_w(u8 data);
	IRQ_CALLBACK_MEMBER(ms6102_int_ack);

	u8 memory_read_byte(offs_t offset);
	void vdack_w(u8 data);

	u8 crtc_r(offs_t offset);
	void crtc_w(offs_t offset, u8 data);

	u8 misc_status_r();
	u16 m_dmaaddr = 0;

	void kbd_uart_clock_w(u8 data);

	required_shared_ptr<u8> m_p_videoram;
	required_device<i8080_cpu_device> m_maincpu;
	required_device<kr1601rr1_device> m_earom;
	required_device<i8214_device> m_pic;
	required_device<i8257_device> m_dma8257;
	required_device<i8251_device> m_i8251;
	required_device<rs232_port_device> m_rs232;
	required_device<ay31015_device> m_kbd_uart;
	required_device<vt100_keyboard_device> m_keyboard;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<i8275_device> m_crtc1;
	required_device<i8275_device> m_crtc2;
	required_region_ptr<u8> m_p_chargen;
	required_region_ptr<u8> m_p_charmap;
};

void ms6102_state::ms6102_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x2fff).rom();
	map(0x3800, 0x3bff).rw(m_earom, FUNC(kr1601rr1_device::read), FUNC(kr1601rr1_device::write));
	map(0xc000, 0xffff).ram().share("videoram");
}

void ms6102_state::ms6102_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x01).rw(m_i8251, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x10, 0x18).rw(m_dma8257, FUNC(i8257_device::read), FUNC(i8257_device::write));
	map(0x20, 0x23).rw("pit8253", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x30, 0x30).mirror(0x0f).rw("589wa1", FUNC(ay31015_device::receive), FUNC(ay31015_device::transmit));
	map(0x40, 0x41).rw(FUNC(ms6102_state::crtc_r), FUNC(ms6102_state::crtc_w));
	map(0x50, 0x5f).noprw(); // video disable?
	map(0x60, 0x6f).w(FUNC(ms6102_state::pic_w));
	map(0x70, 0x7f).r(FUNC(ms6102_state::misc_status_r));
}

static const gfx_layout ms6102_charlayout =
{
	8, 12,
	256,
	1,
	{ 0 },
	{ STEP8(1,1) },
	{ 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8 },
	16*8
};

static GFXDECODE_START(gfx_ms6102)
	GFXDECODE_ENTRY("chargen", 0x0000, ms6102_charlayout, 0, 1)
GFXDECODE_END


void ms6102_state::hrq_w(int state)
{
	/* FIXME: this should be connected to the HOLD line of 8080 */
	m_maincpu->set_input_line(INPUT_LINE_HALT, state);

	/* FIXME: this should be connected to the HLDA line of 8080 */
	m_dma8257->hlda_w(state);
}

void ms6102_state::irq_w(int state)
{
	m_maincpu->set_input_line(I8085_INTR_LINE, ASSERT_LINE);
}

u8 ms6102_state::memory_read_byte(offs_t offset)
{
	m_dmaaddr = offset;
	return m_maincpu->space(AS_PROGRAM).read_byte(offset);
}

I8275_DRAW_CHARACTER_MEMBER(ms6102_state::display_pixels)
{
	using namespace i8275_attributes;

	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u8 gfx = BIT(attrcode, LTEN) ? 0xff : 0;
	if (!BIT(attrcode, VSP) && !BIT(attrcode, LTEN))
		gfx = m_p_chargen[linecount | (charcode & 0x7f) << 4 | (BIT(attrcode, GPA0 + 8) ? 0x800 : 0)];

	if (BIT(attrcode, RVV))
		gfx ^= 0xff;

	bool hlgt = BIT(attrcode, HLGT);
	for(u8 i=0; i<8; i++)
		bitmap.pix(y, x + i) = palette[BIT(gfx, 7-i) ? (hlgt ? 2 : 1) : 0];
}

u8 ms6102_state::crtc_r(offs_t offset)
{
	m_crtc2->read(offset);
	return m_crtc1->read(offset); // cs is same for both crtcs so they should return the same thing
}

void ms6102_state::crtc_w(offs_t offset, u8 data)
{
	m_crtc1->write(offset, data);
	m_crtc2->write(offset, data);
}

u8 ms6102_state::misc_status_r()
{
	u8 status = 0;
	if (!m_kbd_uart->tbmt_r())
		status |= 1 << 6;
	return status;
}

void ms6102_state::kbd_uart_clock_w(u8 data)
{
	m_kbd_uart->write_tcp(BIT(data, 1));
	m_kbd_uart->write_rcp(BIT(data, 1));

	if (data == 0 || data == 3)
		m_keyboard->signal_line_w(m_kbd_uart->so_r());
	else
		m_keyboard->signal_line_w(BIT(data, 0));
}


void ms6102_state::pic_w(u8 data)
{
	m_pic->b_sgs_w(~data);
}

void ms6102_state::vdack_w(u8 data)
{
	if(m_dmaaddr & 1)
		m_crtc1->dack_w(data);
	m_crtc2->dack_w(data | 0x80);
}

IRQ_CALLBACK_MEMBER(ms6102_state::ms6102_int_ack)
{
	m_maincpu->set_input_line(I8085_INTR_LINE, CLEAR_LINE);
	return 0xc7 | ((m_pic->a_r() ^ 7) << 3);
}


void ms6102_state::machine_reset()
{
}

void ms6102_state::machine_start()
{
	m_kbd_uart->write_eps(1);
	m_kbd_uart->write_nb1(1);
	m_kbd_uart->write_nb2(1);
	m_kbd_uart->write_tsb(0);
	m_kbd_uart->write_np(1);
	m_kbd_uart->write_cs(1);
	m_kbd_uart->write_swe(0);

	m_i8251->write_cts(0);

	m_pic->etlg_w(1);

	// rearrange the chargen to be easier for us to access
	for (int i = 0; i < 0x40; i++)
	{
		const uint8_t *srcp = &m_p_chargen[0x1000 | bitswap<3>(m_p_charmap[i], 0, 1, 2) << 8 | (m_p_charmap[i] & 8) >> 1 | (i & 0x01) << 1];
		uint8_t *dstp = &m_p_chargen[(i & 0x38) << 6 | (i & 0x07) << 1];

		// copy a 2-line slice of a 32-character block
		for (int j = 0; j < 0x20; j++)
			std::copy_n(srcp + j * 8, 2, dstp + j * 16);
	}
}


void ms6102_state::ms6102(machine_config &config)
{
	I8080(config, m_maincpu, XTAL(18'432'000) / 9);
	m_maincpu->set_addrmap(AS_PROGRAM, &ms6102_state::ms6102_mem);
	m_maincpu->set_addrmap(AS_IO, &ms6102_state::ms6102_io);
	m_maincpu->out_inte_func().set("i8214", FUNC(i8214_device::inte_w));
	m_maincpu->set_irq_acknowledge_callback(FUNC(ms6102_state::ms6102_int_ack));

	I8214(config, m_pic, XTAL(18'432'000) / 9);
	m_pic->int_wr_callback().set(FUNC(ms6102_state::irq_w));

	KR1601RR1(config, m_earom, 0);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_screen_update("i8275_1", FUNC(i8275_device::screen_update));
	m_screen->set_raw(XTAL(16'400'000), 784, 0, 80*8, 375, 0, 25*12);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_ms6102);
	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	I8257(config, m_dma8257, XTAL(18'432'000) / 9);
	m_dma8257->out_hrq_cb().set(FUNC(ms6102_state::hrq_w));
	m_dma8257->in_memr_cb().set(FUNC(ms6102_state::memory_read_byte));
	m_dma8257->out_iow_cb<2>().set(FUNC(ms6102_state::vdack_w));

	I8275(config, m_crtc1, XTAL(16'400'000) / 8);
	m_crtc1->set_character_width(8);
	m_crtc1->set_display_callback(FUNC(ms6102_state::display_pixels));
	m_crtc1->drq_wr_callback().set("dma8257", FUNC(i8257_device::dreq2_w));
	m_crtc1->set_screen(m_screen);
	m_crtc1->set_next_crtc(m_crtc2);

	I8275(config, m_crtc2, XTAL(16'400'000) / 8);
	m_crtc2->set_character_width(8);
	m_crtc2->irq_wr_callback().set(FUNC(ms6102_state::irq<5>));
	m_crtc2->set_screen(m_screen);

	// keyboard
	AY31015(config, m_kbd_uart);
	m_kbd_uart->write_dav_callback().set(FUNC(ms6102_state::irq<1>));
	m_kbd_uart->set_auto_rdav(true);

	ripple_counter_device &ie5(RIPPLE_COUNTER(config, "ie5", XTAL(16'400'000) / 30));
	ie5.set_stages(2);
	ie5.count_out_cb().set(FUNC(ms6102_state::kbd_uart_clock_w));

	MS7002(config, m_keyboard, 0).signal_out_callback().set(m_kbd_uart, FUNC(ay31015_device::write_si));

	// serial connection to host
	I8251(config, m_i8251, 0);
	m_i8251->txd_handler().set(m_rs232, FUNC(rs232_port_device::write_txd));
	m_i8251->rxrdy_handler().set(FUNC(ms6102_state::irq<3>));

	RS232_PORT(config, m_rs232, default_rs232_devices, "null_modem");
	m_rs232->rxd_handler().set(m_i8251, FUNC(i8251_device::write_rxd));

	pit8253_device &pit8253(PIT8253(config, "pit8253", 0));
	pit8253.set_clk<0>(XTAL(16'400'000) / 9);
	pit8253.out_handler<0>().set(m_i8251, FUNC(i8251_device::write_txc));
	pit8253.set_clk<1>(XTAL(16'400'000) / 9);
	pit8253.out_handler<1>().set(m_i8251, FUNC(i8251_device::write_rxc));
}

ROM_START( ms6102 )
	ROM_REGION(0x3000, "maincpu", 0)
	ROM_LOAD("mc6102_02_ks573rf2_dd26",   0x0000, 0x0800, CRC(f96ba806) SHA1(60d155b781e97e86d31dc2194ad367030470eeb6))
	ROM_LOAD("mc6102_02_ks573rf2_dd30",   0x0800, 0x0800, CRC(1d69ba62) SHA1(bf7d19400fe606239ce8a057850cf4c63ff4cdb2))
	ROM_LOAD("mc6102_02_ks573rf2_0034",   0x1000, 0x0800, CRC(4bce121a) SHA1(e97c635c2fab70a71a31db3b53284209b5881f2c))
	ROM_LOAD("mc6102_02_ks573rf2_0037",   0x1800, 0x0800, CRC(1b22543f) SHA1(fc6cc54baf3abadca30dfaf39a50cae7fbf601b2))
	ROM_LOAD("mc6102_02_ks573rf2_0045",   0x2000, 0x0800, CRC(fd741cfe) SHA1(153abb57ca4833286811082ff50c7b36136274dc))
	ROM_LOAD("mc6102_02_ks573rf2_dd49",   0x2800, 0x0800, CRC(748f6cee) SHA1(a35e6495ea108824f2f1f9907f5e651174e9cf15))

	ROM_REGION(0x2000, "chargen", ROMREGION_ERASE00)
	ROM_LOAD("mc6102_02_k555re4_chargen", 0x1000, 0x0800, CRC(b0e3546b) SHA1(25aca264cc64f368ffcefdfd356120a314a44947))

	ROM_REGION(0x0100, "charmap", 0)
	ROM_LOAD("mc6102_02_k556rt4_d64",     0x0000, 0x0100, CRC(a59fdaa7) SHA1(0851a8b12e838e8f7e5ce840a0262facce303442))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS         INIT        COMPANY        FULLNAME      FLAGS */
COMP( 1984, ms6102, 0,      0,      ms6102,  0,     ms6102_state, empty_init, "Elektronika", "MS 6102.02", MACHINE_NOT_WORKING )



ms68k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Chris Hanson
/*
 * ms68k.cpp - Marion Systems MS68K
 *
 * Documentation:
 *   http://bitsavers.org/pdf/marionSystems/Marion_Systems_MS68K_Single_Board_Computer_Users_Manual.pdf
 *
 *  The Marion Systems MS68K was a single-board MC68000 system designed by Tom
 *  Oberheim in 1985 with two serial ports, a parallel port, WD1772 floppy,
 *  optional SCSI, and up to 512KB of RAM and ROM each onboard, plus an
 *  expansion connector providing full access to the 68000 bus. It was used in
 *  a number of university-level computer systems classes, and a variant of
 *  Peter Stark’s SK*DOS was also available.
 *
 *  The default ROM contains a port of Tiny BASIC 1.2 at 0xF04900, and a
 *  loader for it at 0xF04800. Thus while there is no direct command to start
 *  it, a simple GF04800 will start Tiny BASIC with it.
 *
 * Specifications:
 * - 8MHz MC68000 CPU
 * - 512KB maximum onboard RAM
 * - 128KB maximum onboard EPROM
 * - MC68681 DUART
 * - TTL-supported parallel port
 * - NCR53C80 SCSI controller
 * - Expansion bus
 *
 * TODO:
 * - Expansion bus
 *
 */

#include "emu.h"

#include "bus/centronics/ctronics.h"
#include "bus/nscsi/devices.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
#include "imagedev/floppy.h"
#include "machine/mc68681.h"
#include "machine/ncr5380.h"
#include "machine/wd_fdc.h"


namespace {

class ms68k_state : public driver_device
{
public:
	ms68k_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_bootvect(*this, "bootvect")
		, m_maincpu(*this, "maincpu")
		, m_duart(*this, "duart")
		, m_terminal(*this, "terminal")
		, m_modem(*this, "modem")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0)
		, m_floppy_selected_drive(0)
		, m_scsi(*this, "scsibus:7:ncr5380")
		, m_scsibus(*this, "scsibus")
		, m_printer_conn(*this, "prn")
		, m_printer_out(*this, "prn_out")
	{ }

	void ms68k(machine_config &config) ATTR_COLD;

	DECLARE_INPUT_CHANGED_MEMBER(nmi_button);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	memory_view m_bootvect;

	required_device<cpu_device> m_maincpu;

	required_device<mc68681_device> m_duart;
	required_device<rs232_port_device> m_terminal;
	required_device<rs232_port_device> m_modem;

	required_device<wd1772_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	uint8_t m_floppy_selected_drive;

	required_device<ncr5380_device> m_scsi;
	required_device<nscsi_bus_device> m_scsibus;

	required_device<centronics_device> m_printer_conn;
	required_device<output_latch_device> m_printer_out;

	void mem_map(address_map &map) ATTR_COLD;

	uint8_t duart_r(offs_t offset);
	void duart_w(offs_t offset, uint8_t data);

	// Floppy
	static void floppy_types(device_slot_interface &device) ATTR_COLD;
	static void floppy_formats(format_registration &fr) ATTR_COLD;
	void floppy_side_w(int state);
	void floppy_drive0_w(int state);
	void floppy_drive1_w(int state);
	void floppy_drive_select(int drive);

	// Printer
	uint8_t printer_r(offs_t offset);
	void printer_w(offs_t offset, uint8_t data);
};

/* Input ports */
static INPUT_PORTS_START( ms68k )
	PORT_START("ABORT")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("NMI button") PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ms68k_state::nmi_button), 0)
INPUT_PORTS_END


void ms68k_state::ms68k(machine_config &config)
{
	M68000(config, m_maincpu, 16_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &ms68k_state::mem_map);

	// Set up DUART.

	MC68681(config, m_duart, 16_MHz_XTAL / 2);
	// DUART Interrupt = 3
	m_duart->irq_cb().set_inputline(m_maincpu, M68K_IRQ_3);
	m_duart->a_tx_cb().set(m_terminal, FUNC(rs232_port_device::write_txd));
	// DUART OP0 = RTS A
	m_duart->outport_cb().append(m_terminal, FUNC(rs232_port_device::write_rts)).bit(0);
	m_duart->b_tx_cb().set(m_modem, FUNC(rs232_port_device::write_txd));
	// DUART OP1 = RTS B
	m_duart->outport_cb().append(m_modem, FUNC(rs232_port_device::write_rts)).bit(1);
	// DUART OP3 = Floppy Side 1 Select
	m_duart->outport_cb().append(FUNC(ms68k_state::floppy_side_w)).bit(3);
	// DUART OP4 = Floppy Drive Select 0
	m_duart->outport_cb().append(FUNC(ms68k_state::floppy_drive0_w)).bit(4);
	// DUART OP5 = Floppy Drive Select 1
	m_duart->outport_cb().append(FUNC(ms68k_state::floppy_drive1_w)).bit(5);
	// DUART OP6 = Floppy Double Density
	m_duart->outport_cb().append(m_fdc, FUNC(wd1772_device::dden_w)).bit(6);
	// DUART OP2 = Printer Initialize
	m_duart->outport_cb().append(m_printer_conn, FUNC(centronics_device::write_init)).bit(2);
	// DUART OP7 = Printer Data Strobe
	m_duart->outport_cb().append(m_printer_conn, FUNC(centronics_device::write_strobe)).bit(7);

	// Set up terminal RS-232.

	RS232_PORT(config, m_terminal, default_rs232_devices, "terminal");
	m_terminal->rxd_handler().set(m_duart, FUNC(mc68681_device::rx_a_w));
	// DUART IP0 = CTS
	m_terminal->cts_handler().set(m_duart, FUNC(mc68681_device::ip0_w));

	// Set up modem RS-232.

	RS232_PORT(config, m_modem, default_rs232_devices, nullptr);
	m_modem->rxd_handler().set(m_duart, FUNC(mc68681_device::rx_b_w));
	// DUART IP1 = CTS
	m_modem->cts_handler().set(m_duart, FUNC(mc68681_device::ip1_w));

	// Set up SCSI.

	NSCSI_BUS(config, m_scsibus);
	NSCSI_CONNECTOR(config, "scsibus:0", default_scsi_devices, "harddisk", false);
	NSCSI_CONNECTOR(config, "scsibus:1", default_scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsibus:2", default_scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsibus:3", default_scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsibus:4", default_scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsibus:5", default_scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsibus:6", default_scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsibus:7").option_set("ncr5380", NCR53C80)
		.machine_config([this](device_t *device) {
			// SCSI Interrupt = 5
			downcast<ncr53c80_device &>(*device).irq_handler().set_inputline(m_maincpu, M68K_IRQ_5);
		});

	// Set up FDC.

	WD1772(config, m_fdc, 16_MHz_XTAL / 2);
	// FDC Interrupt = 4
	m_fdc->intrq_wr_callback().set_inputline(m_maincpu, M68K_IRQ_3);
	FLOPPY_CONNECTOR(config, m_floppy[0], floppy_types, "525sd", floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[1], floppy_types, "525sd", floppy_formats);

	// Set up printer.

	CENTRONICS(config, m_printer_conn, centronics_devices, "printer");
	OUTPUT_LATCH(config, m_printer_out);
	m_printer_conn->set_output_latch(*m_printer_out);
	// Printer Interrupt = 2
	m_printer_conn->ack_handler().append_inputline(m_maincpu, M68K_IRQ_2);
	// DUART IP2 = Busy
	m_printer_conn->busy_handler().set(m_duart, FUNC(mc68681_device::ip2_w));
	// DUART IP3 = Paper Out
	m_printer_conn->perror_handler().set(m_duart, FUNC(mc68681_device::ip3_w));
	// DUART IP4 = Device Selected
	m_printer_conn->select_handler().set(m_duart, FUNC(mc68681_device::ip4_w));
	// DUART IP5 = Acknowledge
	m_printer_conn->ack_handler().set(m_duart, FUNC(mc68681_device::ip5_w));

	// Set up Expansion.
	// EXT Interrupt = 6
	// TODO: Implement expansion bus.
}

void ms68k_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x07ffff).ram().share("ram"); // 512KB RAM onboard maximum
	map(0x000008, 0x07ffff).ram();
	map(0xd00000, 0xd7ffff).rw(m_scsi, FUNC(ncr5380_device::read), FUNC(ncr5380_device::write)).umask16(0x00ff);
	map(0xd80000, 0xdfffff).rw(FUNC(ms68k_state::printer_r), FUNC(ms68k_state::printer_w)).umask16(0x00ff);
	map(0xe00000, 0xe7ffff).rw(m_fdc, FUNC(wd1772_device::read), FUNC(wd1772_device::write)).umask16(0x00ff);
	map(0xe80000, 0xefffff).r(m_duart, FUNC(mc68681_device::read)).w(FUNC(ms68k_state::duart_w)).umask16(0x00ff);
	map(0xf00000, 0xf1ffff).rom().region("roms", 0); // 128KB ROM onboard maximum

	// ROM is mapped to 0 on reset, remapped on first DUART write
	map(0x000000, 0x000007).view(m_bootvect);
	m_bootvect[0](0x000000, 0x000007).rom().region("roms", 0);
}

void ms68k_state::machine_start()
{
	save_item(NAME(m_floppy_selected_drive));
}

void ms68k_state::machine_reset()
{
	// Reset pointer to bootvector in ROM for bootvector view
	m_bootvect.select(0);
}

/* DUART */

void ms68k_state::duart_w(offs_t offset, uint8_t data)
{
	m_duart->write(offset, data);

	// The first write to the DUART also swaps ROM and RAM.
	if (m_bootvect.entry().has_value()) {
		m_bootvect.disable(); // stop mapping ROM until reset
	}
}

/* Floppy */

void ms68k_state::floppy_types(device_slot_interface &device)
{
	device.option_add("525sd", FLOPPY_525_SD);
	device.option_add("525dd", FLOPPY_525_DD);
	device.option_add("35dd", FLOPPY_35_DD);
	device.option_add("35hd", FLOPPY_35_HD);
}

void ms68k_state::floppy_formats(format_registration &fr)
{
	floppy_image_device::default_mfm_floppy_formats(fr);
}

void ms68k_state::floppy_side_w(int state)
{
	floppy_image_device *floppy = nullptr;
	floppy = m_floppy[m_floppy_selected_drive]->get_device();
	if (floppy) {
		floppy->ss_w(state);
	}
}

void ms68k_state::floppy_drive0_w(int state)
{
	if (state)
		floppy_drive_select(0);
}

void ms68k_state::floppy_drive1_w(int state)
{
	if (state)
		floppy_drive_select(1);
}

void ms68k_state::floppy_drive_select(int drive)
{
	m_floppy_selected_drive = drive;
	floppy_image_device *floppy = nullptr;
	floppy = m_floppy[m_floppy_selected_drive]->get_device();
	m_fdc->set_floppy(floppy);
}


/* Printer */
uint8_t ms68k_state::printer_r(offs_t offset)
{
	return 0;
}

void ms68k_state::printer_w(offs_t offset, uint8_t data)
{
	m_printer_out->write(data);
}


/* NMI button */
INPUT_CHANGED_MEMBER(ms68k_state::nmi_button)
{
	m_maincpu->set_input_line(M68K_IRQ_7, newval ? ASSERT_LINE : CLEAR_LINE);
}


/* ROM definition */
ROM_START( ms68k )
	ROM_REGION16_BE(0x20000, "roms", ROMREGION_ERASE00)
	ROM_DEFAULT_BIOS("msmon")

	ROM_SYSTEM_BIOS(0, "msmon", "MSMON")
	ROMX_LOAD("msmonu27.bin", 0x000000, 0x010000, CRC(b278ea2a) SHA1(383911c213448d41198f2b44494376c1dc26e897), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD("msmonu26.bin", 0x000001, 0x010000, CRC(7ff029cf) SHA1(4338ef716d455fd86e1c32c04e146d2be1603dff), ROM_SKIP(1) | ROM_BIOS(0) )
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME   PARENT COMPAT MACHINE INPUT  CLASS        INIT        COMPANY           FULLNAME FLAGS */
COMP( 1981, ms68k, 0,     0,     ms68k,  ms68k, ms68k_state, empty_init, "Marion Systems", "MS68K", MACHINE_NO_SOUND_HW )



ms9540.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Millennium Systems 9520 and 9540 Software Development systems.

2013-08-30 Skeleton driver

The only roms known to be dumped are the main roms of the 9540, and the disk
controller roms of the 9520.

There's no known documents for the 9540, so everything in here is a guess.

The 9520 is Z80-based, but is currently not emulated.
The 9540 is 68000-based.

Chips:
- Main board: MC68000L8, 2x SCN2661C, uPD8253C. Crystals: 5.0688, 16MHz
  ROMS: 0950-0131-01, 0950-0132-01, 0954-0133-01, 0954-0134-01,
        0954-0135-01, 0954-0135-02
- Disk board: FD1797A-02, AM9517A, 0954-0039-01
- Serial board: 6x SCN2661C, 2x AM9517A, 0954-0029-01. Crystal: 5.0688MHz

****************************************************************************/

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "machine/terminal.h"


namespace {

class ms9540_state : public driver_device
{
public:
	ms9540_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_ram(*this, "mainram")
		, m_maincpu(*this, "maincpu")
		, m_terminal(*this, "terminal")
	{ }

	void ms9540(machine_config &config);

private:
	uint8_t latch_1e001_r();
	void latch_1e001_w(u8 data);
	uint8_t latch_1f001_r();
	void latch_1f001_w(u8 data);
	void kbd_put(u8 data);
	void mem_map(address_map &map) ATTR_COLD;
	uint8_t m_term_data = 0U;
	uint8_t m_latch_1e001 = 0U;
	uint8_t m_latch_1f001 = 0U;
	void machine_reset() override ATTR_COLD;
	void machine_start() override ATTR_COLD;
	required_shared_ptr<uint16_t> m_ram;
	required_device<cpu_device> m_maincpu;
	required_device<generic_terminal_device> m_terminal;
};


uint8_t ms9540_state::latch_1e001_r()
{
	return m_latch_1e001;
}

void ms9540_state::latch_1e001_w(uint8_t data)
{
	logerror("%s: $1e001 <- #$%02x\n", machine().describe_context(), data);
	m_latch_1e001 = data;
}

uint8_t ms9540_state::latch_1f001_r()
{
	return m_latch_1f001;
}

void ms9540_state::latch_1f001_w(uint8_t data)
{
	logerror("%s: $1f001 <- #$%02x\n", machine().describe_context(), data);
	m_latch_1f001 = data;
}

void ms9540_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x00ffff).ram().share("mainram");
	map(0x010000, 0x013fff).rom().region("9540", 0);
	map(0x018000, 0x018fff).ram();
	map(0x01e001, 0x01e001).rw(FUNC(ms9540_state::latch_1e001_r), FUNC(ms9540_state::latch_1e001_w));
	map(0x01f001, 0x01f001).rw(FUNC(ms9540_state::latch_1f001_r), FUNC(ms9540_state::latch_1f001_w));
}


/* Input ports */
static INPUT_PORTS_START( ms9540 )
INPUT_PORTS_END


void ms9540_state::machine_reset()
{
	uint8_t* ROM = memregion("9540")->base();
	memcpy(m_ram, ROM, 8);
}

void ms9540_state::machine_start()
{
	save_item(NAME(m_term_data));
	save_item(NAME(m_latch_1e001));
	save_item(NAME(m_latch_1f001));
}

void ms9540_state::kbd_put(u8 data)
{
	m_term_data = data;
}

void ms9540_state::ms9540(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, 8000000); // unknown clock
	m_maincpu->set_addrmap(AS_PROGRAM, &ms9540_state::mem_map);

	/* video hardware */
	GENERIC_TERMINAL(config, m_terminal, 0);
	m_terminal->set_keyboard_callback(FUNC(ms9540_state::kbd_put));
}

/* ROM definition */
ROM_START( ms9540 )
	ROM_REGION16_BE(0x4000, "9540", 0)
	ROM_LOAD16_BYTE("0954-0135-01.20n", 0x00001, 0x2000, CRC(93ee9363) SHA1(73bc09e0379e06e0da96279cb5cc1581a0f0bf77) )
	ROM_LOAD16_BYTE("0954-0135-02.16n", 0x00000, 0x2000, CRC(a21077c5) SHA1(51dcbe543317d2042fb1acb1885461ba1790721e) )

	ROM_REGION(0x1800, "9520", 0)
	ROM_LOAD( "z80-hd.bin",   0x0000, 0x1000, CRC(b1c37286) SHA1(36b38fec9ef46e3e594423bbd1c64c5e9a4bc74d) )
	ROM_LOAD( "z80-flp.bin",  0x1000, 0x0800, CRC(f256b8c3) SHA1(780b444e999c3149eb0f137733703682d65746b4) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY               FULLNAME  FLAGS
COMP( 198?, ms9540, 0,      0,      ms9540,  ms9540, ms9540_state, empty_init, "Millennium Systems", "ms9540", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



msbc1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Curt Coder
/*

Omnibyte MSBC-1 Multibus Single Board Computer

PCB Layout
----------

REV B

|-----------------------------------------------------------|
|       CN1     CN2     CN3     CN4         SW1     CN5     |
|                                                           |
|           3.6864MHz   3.6864MHz   ROM1                    |
|                                   ROM0                    |
|       SIO         SIO     SW3 SW2                         |
|                               PIT                         |
|               12.5MHz                 41256 41256 41256   |
|                                       41256 41256 41256   |
|   8MHz                        CPU           41245 41256   |
|                                                           |
|                                               8419        |
|                           SW4                             |
|-|-------------CN6----------------|----|-------CN7-------|-|
  |--------------------------------|    |-----------------|

Notes:
    Relevant IC's shown.

    CPU     - Motorola MC68000R12
    SIO     - Mostek MK68564N-5A Serial Input/Output
    PIT     - Motorola MC68230L10 Parallel Interface/Timer
    8419    - National Semiconductor DP8419N DRAM Controller
    41256   - Fujitsu MB81256-12 256Kx1 RAM
    ROM0    - AMD AM27128ADC 16Kx8 ROM "47-2818-36G1"
    ROM1    - AMD AM27128ADC 16Kx8 ROM "47-2818-36G2"
    SW1     - push button
    SW2     - rotary hex DIP
    SW3     - rotary hex DIP
    SW4     - DIP8
    CN1     -
    CN2     -
    CN3     -
    CN4     -
    CN5     -
    CN6     -
    CN7     -

*/


#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "machine/68230pit.h"
#include "machine/z80sio.h"


namespace {

#define MC68000R12_TAG  "u50"
#define MK68564_0_TAG   "u14"
#define MK68564_1_TAG   "u15"
#define MC68230L10_TAG  "u16"

class msbc1_state : public driver_device
{
public:
	msbc1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, MC68000R12_TAG)
	{ }

	void msbc1(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
};

void msbc1_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x5fffff).ram();
	map(0xf80000, 0xf87fff).rom().region(MC68000R12_TAG, 0);
	map(0xfffa00, 0xfffa3f).rw("sio1", FUNC(mk68564_device::read), FUNC(mk68564_device::write)).umask16(0x00ff);
	map(0xfffc00, 0xfffc3f).rw("sio2", FUNC(mk68564_device::read), FUNC(mk68564_device::write)).umask16(0x00ff);
	map(0xfffe00, 0xfffe3f).rw("pit", FUNC(pit68230_device::read), FUNC(pit68230_device::write)).umask16(0x00ff);
}

/* Input ports */
static INPUT_PORTS_START( msbc1 )
INPUT_PORTS_END

void msbc1_state::machine_reset()
{
	void *ram = m_maincpu->space(AS_PROGRAM).get_write_ptr(0);
	uint8_t *rom = memregion(MC68000R12_TAG)->base();

	memcpy(ram, rom, 8);
}

void msbc1_state::msbc1(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, 12.5_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &msbc1_state::mem_map);

	PIT68230(config, "pit", 8_MHz_XTAL);

	MK68564(config, "sio1", 8_MHz_XTAL / 2).set_xtal(3.6864_MHz_XTAL);
	MK68564(config, "sio2", 8_MHz_XTAL / 2).set_xtal(3.6864_MHz_XTAL);
}

/* ROM definition */
ROM_START( msbc1 )
	ROM_REGION16_BE( 0x8000, MC68000R12_TAG, 0 )
	ROM_LOAD16_BYTE( "47-2818-36g1.u18", 0x0000, 0x4000, CRC(14f25d47) SHA1(964bc49c1dd9e9680c0d3d89ff3794c5044bea62) )
	ROM_LOAD16_BYTE( "47-2818-36g2.u19", 0x0001, 0x4000, CRC(4814b9e1) SHA1(d96cf75084c6588cb33513830c6beeeffc2de853) )

	ROM_REGION( 0x100, "plds", 0 )
	ROM_LOAD( "p21.1.u22", 0x000, 0x100, NO_DUMP )
	ROM_LOAD( "p22.0.u37", 0x000, 0x100, NO_DUMP )
	ROM_LOAD( "p23.0.u38", 0x000, 0x100, NO_DUMP )
	ROM_LOAD( "p24.0.u43", 0x000, 0x100, NO_DUMP )
	ROM_LOAD( "p25.0.u44", 0x000, 0x100, NO_DUMP )
	ROM_LOAD( "p26.0.u45", 0x000, 0x100, NO_DUMP )
	ROM_LOAD( "p27.0.u46", 0x000, 0x100, NO_DUMP )
	ROM_LOAD( "p28.1.u49", 0x000, 0x100, NO_DUMP )
	ROM_LOAD( "p29.0.u58", 0x000, 0x100, NO_DUMP )
	ROM_LOAD( "p30.0.u60", 0x000, 0x100, NO_DUMP )
	ROM_LOAD( "p31.0.u61", 0x000, 0x100, NO_DUMP )
	ROM_LOAD( "p32.0.u76", 0x000, 0x100, NO_DUMP )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY     FULLNAME  FLAGS
COMP( 1985, msbc1, 0,      0,      msbc1,   msbc1, msbc1_state, empty_init, "Omnibyte", "MSBC-1", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



msc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Fidelity Mini Sensory Chess Challenger (model MSC, 1981 version)

Two versions exist, both of them are model MSC. The 1981 version has a Z8 MCU,
the 1982 version has an I8049. They can also be distinguished from the button
panel design, the 2nd version has rectangular buttons. See sc6.cpp for the
2nd version.

Hardware notes:
- Zilog Z8 MCU (probably Z8601, custom label: SR0016 1001011A01 or SR0022
  1001011B01), 8MHz XTAL
- buzzer, 18 leds, 8*8 chessboard buttons, module slot

released modules, * denotes not dumped yet:
- CAC: Challenger Advanced Chess
- CBO: Challenger Book Openings
- *CGG: Challenger Greatest Games

As noted in the hash file: The modules have 2 programs in them, one for Z8
and one for MCS48. A12 is forced high or low to select the bank.

*******************************************************************************/

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/z8/z8.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "fidel_msc_v1.lh"


namespace {

class msc_state : public driver_device
{
public:
	msc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.0")
	{ }

	void msc(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<z8_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport m_inputs;

	u8 m_led_select = 0;
	u16 m_inp_mux = 0;

	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_display();
	void mux_w(u8 data);
	void control_w(u8 data);
	u8 input_r();
};

void msc_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_led_select));
	save_item(NAME(m_inp_mux));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void msc_state::update_display()
{
	m_display->matrix(m_led_select, m_inp_mux);
}

void msc_state::mux_w(u8 data)
{
	// P20-P27: input mux, led data
	m_inp_mux = (m_inp_mux & 0x100) | data;
	update_display();
}

void msc_state::control_w(u8 data)
{
	// P34: speaker out
	m_dac->write(BIT(~data, 4));

	// P35,P36: led select
	m_led_select = ~data >> 5 & 3;

	// P37: input mux highest bit, led data
	m_inp_mux = (m_inp_mux & 0xff) | (data << 1 & 0x100);
	update_display();
}

u8 msc_state::input_r()
{
	// P30-P33,P04-P07: multiplexed inputs
	u8 data = 0;

	// read chessboard sensors
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_file(i);

	// read button panel
	if (m_inp_mux & 0x100)
		data |= m_inputs->read();

	return bitswap<8>(~data,0,1,2,3,4,5,6,7);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void msc_state::main_map(address_map &map)
{
	map(0x0000, 0x0fff).mirror(0xf000).r("cartslot", FUNC(generic_slot_device::read_rom));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( msc )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("RV / Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("DM / Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Speaker / Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("LV / Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("PV / Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("PB / King")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CL")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_N) PORT_NAME("RE")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void msc_state::msc(machine_config &config)
{
	// basic machine hardware
	Z8601(config, m_maincpu, 8_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &msc_state::main_map);
	m_maincpu->p0_in_cb().set(FUNC(msc_state::input_r)).mask(0xf0);
	m_maincpu->p0_in_cb().append_constant(0x0f).mask(0x0f);
	m_maincpu->p2_out_cb().set(FUNC(msc_state::mux_w));
	m_maincpu->p3_in_cb().set(FUNC(msc_state::input_r)).mask(0x0f);
	m_maincpu->p3_in_cb().append_constant(0xf0).mask(0xf0);
	m_maincpu->p3_out_cb().set(FUNC(msc_state::control_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(2, 9);
	config.set_default_layout(layout_fidel_msc_v1);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);

	// cartridge
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "fidel_msc");
	SOFTWARE_LIST(config, "cart_list").set_original("fidel_msc");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( miniscco )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD("sr0016_1001011a01", 0x0000, 0x0800, CRC(c8cd9bf1) SHA1(4ba165555b8419b03b2ef355da0ed9675315e18b) ) // internal ROM
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT   COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY, FULLNAME, FLAGS
SYST( 1981, miniscco, miniscc, 0,      msc,     msc,   msc_state, empty_init, "Fidelity Electronics", "Mini Sensory Chess Challenger (Z8 version)", MACHINE_SUPPORTS_SAVE )



mstation.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

    CIDCO MailStation

    22/10/2011 Preliminary driver by Sandro Ronco

    Hardware:
        - Z80 CPU
        - 29f080 8 Mbit flash
        - 28SF040 4 Mbit flash (for user data)
        - 128kb RAM
        - 320x128 LCD
        - RCV336ACFW 33.6kbps modem

    TODO:
    - RCV336ACFW modem
    - Add similar models (Mivo 100/150/200/250/350)
    - New Mail led
    - NVRAM

    More info:
      http://www.fybertech.net/mailstation/info.php

****************************************************************************/


#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/bankdev.h"
#include "machine/intelfsh.h"
#include "machine/ram.h"
#include "machine/rp5c01.h"
#include "machine/timer.h"
#include "emupal.h"
#include "screen.h"


namespace {

class mstation_state : public driver_device
{
public:
	mstation_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_bankdev1(*this, "bank0")
		, m_bankdev2(*this, "bank1")
		, m_keyboard(*this, "LINE.%u", 0)
		, m_nvram(*this, "nvram")
	{
	}

	void mstation(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<address_map_bank_device> m_bankdev1;
	required_device<address_map_bank_device> m_bankdev2;
	required_ioport_array<10> m_keyboard;
	required_shared_ptr<uint8_t> m_nvram;

	uint8_t m_bank1[2];
	uint8_t m_bank2[2];
	std::unique_ptr<uint8_t[]> m_vram;
	uint8_t m_screen_column = 0;
	uint8_t m_port2 = 0;
	uint8_t m_irq = 0;
	uint16_t m_kb_matrix = 0;

	uint8_t modem_r();
	void modem_w(uint8_t data);

	void lcd_w(uint16_t offset, int column, uint8_t data);
	uint8_t lcd_r(uint16_t offset, int column);
	uint8_t lcd_right_r(offs_t offset);
	void lcd_right_w(offs_t offset, uint8_t data);
	uint8_t lcd_left_r(offs_t offset);
	void lcd_left_w(offs_t offset, uint8_t data);

	uint8_t bank1_r(offs_t offset);
	void bank1_w(offs_t offset, uint8_t data);
	uint8_t bank2_r(offs_t offset);
	void bank2_w(offs_t offset, uint8_t data);

	uint8_t battery_status_r();
	void port2_w(uint8_t data);
	uint8_t kb_r();
	void kb_w(uint8_t data);
	uint8_t irq_r();
	void irq_w(uint8_t data);
	void refresh_ints();

	void rtc_irq(int state);

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void mstation_palette(palette_device &palette) const;
	TIMER_DEVICE_CALLBACK_MEMBER(mstation_1hz_timer);
	TIMER_DEVICE_CALLBACK_MEMBER(mstation_kb_timer);
	void mstation_banked_map(address_map &map) ATTR_COLD;
	void mstation_io(address_map &map) ATTR_COLD;
	void mstation_mem(address_map &map) ATTR_COLD;
};


uint8_t mstation_state::modem_r()
{
	return 0xff;
}

void mstation_state::modem_w(uint8_t data)
{
}


//***************************************************************************
//  video hardware emulation
//***************************************************************************/

void mstation_state::lcd_w(uint16_t offset, int column, uint8_t data)
{
	if (m_port2 & 0x08)
		m_vram[(column * 240) + (offset % 240)] = data;
	else
		m_screen_column = data % 20;
}

uint8_t mstation_state::lcd_r(uint16_t offset, int column)
{
	if (m_port2 & 0x08)
		return m_vram[(column * 240) + (offset % 240)];
	else
		return m_screen_column % 20;
}

uint8_t mstation_state::lcd_left_r(offs_t offset) {  return lcd_r(offset, m_screen_column);  }
void mstation_state::lcd_left_w(offs_t offset, uint8_t data) {  lcd_w(offset, m_screen_column, data);   }
uint8_t mstation_state::lcd_right_r(offs_t offset) {  return lcd_r(offset, m_screen_column + 20); }
void mstation_state::lcd_right_w(offs_t offset, uint8_t data) {  lcd_w(offset, m_screen_column + 20, data);  }

uint32_t mstation_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (int x=0; x<40; x++)
		for (int y=0; y<128; y++)
		{
			uint8_t data = m_vram[56 + y + x * 240];

			for (int b=0; b<8; b++)
			{
				// columns are inverted (right to left)
				int col = ((x < 20) ? 19 : 59) - x;

				bitmap.pix(y, col*8 + b)= BIT(data, 0);
				data >>= 1;
			}
		}
	return 0;
}

//***************************************************************************
//  Bankswitch
//***************************************************************************

uint8_t mstation_state::bank1_r(offs_t offset)
{
	return m_bank1[offset];
}

uint8_t mstation_state::bank2_r(offs_t offset)
{
	return m_bank2[offset];
}

void mstation_state::bank1_w(offs_t offset, uint8_t data)
{
	m_bank1[offset] = data;

	m_bankdev1->set_bank(((m_bank1[1] & 0x07) << 8) | m_bank1[0]);
}

void mstation_state::bank2_w(offs_t offset, uint8_t data)
{
	m_bank2[offset] = data;

	m_bankdev2->set_bank(((m_bank2[1] & 0x07) << 8) | m_bank2[0]);
}



void mstation_state::port2_w(uint8_t data)
{
	m_port2 = data;

	m_kb_matrix = (m_kb_matrix & 0xff) | ((data & 0x03)<<8);
}

/*
    IRQ bits (port 3) ordered by priority:

    bit 7: power down request
    bit 5: real time clock
    bit 6: modem
    bit 4: 1 sec int
    bit 3: unknown
    bit 0: unknown
    bit 1: keyboard int
    bit 2: unknown
*/

void mstation_state::refresh_ints()
{
	if (m_irq != 0)
		m_maincpu->set_input_line(0, HOLD_LINE);
	else
		m_maincpu->set_input_line(0, CLEAR_LINE);
}

uint8_t mstation_state::irq_r()
{
	return m_irq;
}

void mstation_state::irq_w(uint8_t data)
{
	m_irq &= data;

	refresh_ints();
}

uint8_t mstation_state::battery_status_r()
{
	/*
	  bit 0-3 - unknown
	  bit 4-7 - battery status
	*/
	return 0xf0;
}

void mstation_state::kb_w(uint8_t data)
{
	m_kb_matrix = (m_kb_matrix & 0x300) | data;
}

uint8_t mstation_state::kb_r()
{
	uint8_t data = 0xff;

	for (int i=0; i<10; i++)
	{
		if (!(m_kb_matrix & (1<<i)))
			data &= m_keyboard[i]->read();
	}

	return data;
}


void mstation_state::mstation_banked_map(address_map &map)
{
	map(0x0000000, 0x00fffff).mirror(0x0300000).rw("flash0", FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0x0400000, 0x041ffff).mirror(0x03e0000).ram().share("nvram");
	map(0x0c00000, 0x0c7ffff).mirror(0x0380000).rw("flash1", FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0x0800000, 0x0803fff).mirror(0x03fc000).rw(FUNC(mstation_state::lcd_left_r), FUNC(mstation_state::lcd_left_w));
	map(0x1000000, 0x1003fff).mirror(0x03fc000).rw(FUNC(mstation_state::lcd_right_r), FUNC(mstation_state::lcd_right_w));
	map(0x1400000, 0x1403fff).mirror(0x03fc000).rw(FUNC(mstation_state::modem_r), FUNC(mstation_state::modem_w));
}

void mstation_state::mstation_mem(address_map &map)
{
	map(0x0000, 0x3fff).rw("flash0", FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0x4000, 0x7fff).rw(m_bankdev1, FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0x8000, 0xbfff).rw(m_bankdev2, FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0xc000, 0xffff).bankrw("sysram");    // system ram always first RAM bank
}

void mstation_state::mstation_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x01, 0x01).rw(FUNC(mstation_state::kb_r), FUNC(mstation_state::kb_w));
	map(0x02, 0x02).w(FUNC(mstation_state::port2_w));
	map(0x03, 0x03).rw(FUNC(mstation_state::irq_r), FUNC(mstation_state::irq_w));
	map(0x05, 0x06).rw(FUNC(mstation_state::bank1_r), FUNC(mstation_state::bank1_w));
	map(0x07, 0x08).rw(FUNC(mstation_state::bank2_r), FUNC(mstation_state::bank2_w));
	map(0x09, 0x09).r(FUNC(mstation_state::battery_status_r));
	map(0x10, 0x1f).rw("rtc", FUNC(rp5c01_device::read), FUNC(rp5c01_device::write));
	//map(0x2c, 0x2c) printer
}


/* Input ports */
static INPUT_PORTS_START( mstation )
	PORT_START( "LINE.0" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("Main Menu")  PORT_CODE( KEYCODE_HOME )   PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("Back")   PORT_CODE( KEYCODE_DEL )    PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("Print")  PORT_CODE( KEYCODE_F6 )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("F1")     PORT_CODE( KEYCODE_F1 )     PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("F2")     PORT_CODE( KEYCODE_F2 )     PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("F3")     PORT_CODE( KEYCODE_F3 )     PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("F4")     PORT_CODE( KEYCODE_F4 )     PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("F5")     PORT_CODE( KEYCODE_F5 )     PORT_CHAR(UCHAR_MAMEKEY(F5))

	PORT_START( "LINE.1" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_END )    PORT_CHAR('@')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("A-A Size")       PORT_CODE( KEYCODE_F7 )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("Check Spelling") PORT_CODE( KEYCODE_F8 )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("Get E-Mail")     PORT_CODE( KEYCODE_F9 )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("PG Up")      PORT_CODE( KEYCODE_PGUP )   PORT_CHAR(UCHAR_MAMEKEY(PGUP))

	PORT_START( "LINE.2" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_0_PAD )  PORT_CHAR(U'´')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_1 )      PORT_CHAR('1')      PORT_CHAR('!')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_2 )      PORT_CHAR('2')      PORT_CHAR('@')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_3 )      PORT_CHAR('3')      PORT_CHAR('#')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_4 )      PORT_CHAR('4')      PORT_CHAR('$')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_5 )      PORT_CHAR('5')      PORT_CHAR('%')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_6 )      PORT_CHAR('6')      PORT_CHAR('^')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_7 )      PORT_CHAR('7')      PORT_CHAR('&')

	PORT_START( "LINE.3" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_8 )      PORT_CHAR('8')      PORT_CHAR('*')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_9 )      PORT_CHAR('9')      PORT_CHAR('(')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_0 )      PORT_CHAR('0')      PORT_CHAR(')')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_MINUS )  PORT_CHAR('-')      PORT_CHAR('_')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_EQUALS ) PORT_CHAR('=')      PORT_CHAR('+')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("Backspace")      PORT_CODE( KEYCODE_BACKSPACE )      PORT_CHAR(8)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_BACKSLASH )  PORT_CHAR('\\')     PORT_CHAR('|')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("PG Down")        PORT_CODE( KEYCODE_PGDN )       PORT_CHAR(UCHAR_MAMEKEY(PGDN))

	PORT_START( "LINE.4" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("Tab")    PORT_CODE( KEYCODE_TAB )        PORT_CHAR(9)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_Q )      PORT_CHAR('q')      PORT_CHAR('Q')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_W )      PORT_CHAR('w')      PORT_CHAR('W')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_E )      PORT_CHAR('e')      PORT_CHAR('E')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_R )      PORT_CHAR('r')      PORT_CHAR('R')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_T )      PORT_CHAR('t')      PORT_CHAR('T')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_Y )      PORT_CHAR('y')      PORT_CHAR('Y')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_U )      PORT_CHAR('u')      PORT_CHAR('U')

	PORT_START( "LINE.5" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_I )      PORT_CHAR('i')      PORT_CHAR('I')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_O )      PORT_CHAR('o')      PORT_CHAR('O')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_P )      PORT_CHAR('p')      PORT_CHAR('P')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_OPENBRACE )  PORT_CHAR('[')      PORT_CHAR('{')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_CLOSEBRACE ) PORT_CHAR(']')      PORT_CHAR('}')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_COLON )      PORT_CHAR(';')      PORT_CHAR(':')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_QUOTE )      PORT_CHAR('\'')     PORT_CHAR('\"')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("Enter")      PORT_CODE( KEYCODE_ENTER )  PORT_CHAR(13)

	PORT_START( "LINE.6" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("CapsLock")       PORT_CODE( KEYCODE_CAPSLOCK )       PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_A )      PORT_CHAR('a')      PORT_CHAR('A')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_S )      PORT_CHAR('s')      PORT_CHAR('S')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_D )      PORT_CHAR('d')      PORT_CHAR('D')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_F )      PORT_CHAR('f')      PORT_CHAR('F')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_G )      PORT_CHAR('g')      PORT_CHAR('G')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_H )      PORT_CHAR('h')      PORT_CHAR('H')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_J )      PORT_CHAR('j')      PORT_CHAR('J')

	PORT_START( "LINE.7" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_K )      PORT_CHAR('K')      PORT_CHAR('K')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_L )      PORT_CHAR('l')      PORT_CHAR('L')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_COMMA )  PORT_CHAR(',')      PORT_CHAR('<')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_STOP )   PORT_CHAR('.')      PORT_CHAR('>')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_SLASH )  PORT_CHAR('/')      PORT_CHAR('?')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("Up")         PORT_CODE( KEYCODE_UP )     PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("Down")       PORT_CODE( KEYCODE_DOWN )   PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("Right")      PORT_CODE( KEYCODE_RIGHT )  PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

	PORT_START( "LINE.8" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("Left Shift")     PORT_CODE( KEYCODE_LSHIFT )         PORT_CHAR(UCHAR_MAMEKEY(LSHIFT))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_Z )      PORT_CHAR('z')      PORT_CHAR('Z')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_X )      PORT_CHAR('x')      PORT_CHAR('X')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_C )      PORT_CHAR('c')      PORT_CHAR('C')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_V )      PORT_CHAR('v')      PORT_CHAR('V')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_B )      PORT_CHAR('b')      PORT_CHAR('B')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_N )      PORT_CHAR('n')      PORT_CHAR('N')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_CODE( KEYCODE_M )      PORT_CHAR('m')      PORT_CHAR('M')

	PORT_START( "LINE.9" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("Function")       PORT_CODE( KEYCODE_LCONTROL )   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("Space")          PORT_CODE( KEYCODE_SPACE )      PORT_CHAR(' ')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("Right Shift")    PORT_CODE( KEYCODE_RSHIFT ) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )   PORT_NAME("Left")           PORT_CODE( KEYCODE_LEFT )   PORT_CHAR(UCHAR_MAMEKEY(LEFT))
INPUT_PORTS_END

void mstation_state::machine_start()
{
	// allocate the videoram
	m_vram = make_unique_clear<uint8_t[]>(9600);

	// map firsh RAM bank at 0xc000-0xffff
	membank("sysram")->set_base(m_nvram);
}


void mstation_state::machine_reset()
{
	m_bank1[0] =  m_bank1[1] = 0;
	m_bank2[0] =  m_bank2[1] = 0;

	// reset banks
	m_bankdev1->set_bank(0);
	m_bankdev2->set_bank(0);
}

void mstation_state::rtc_irq(int state)
{
	if (state)
		m_irq |= (1<<5);
	else
		m_irq &=~(1<<5);

	refresh_ints();
}

TIMER_DEVICE_CALLBACK_MEMBER(mstation_state::mstation_1hz_timer)
{
	m_irq |= (1<<4);

	refresh_ints();
}

TIMER_DEVICE_CALLBACK_MEMBER(mstation_state::mstation_kb_timer)
{
	m_irq |= (1<<1);

	refresh_ints();
}

void mstation_state::mstation_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}


void mstation_state::mstation(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'000'000));      //unknown clock
	m_maincpu->set_addrmap(AS_PROGRAM, &mstation_state::mstation_mem);
	m_maincpu->set_addrmap(AS_IO, &mstation_state::mstation_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(mstation_state::screen_update));
	screen.set_size(320, 128);
	screen.set_visarea(0, 320-1, 0, 128-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(mstation_state::mstation_palette), 2);

	AMD_29F080(config, "flash0");
	SST_28SF040(config, "flash1");

	// IRQ 4 is generated every second, used for auto power off
	TIMER(config, "1hz_timer").configure_periodic(FUNC(mstation_state::mstation_1hz_timer), attotime::from_hz(1));

	// IRQ 1 is used for scan the kb and for cursor blinking
	TIMER(config, "kb_timer").configure_periodic(FUNC(mstation_state::mstation_kb_timer), attotime::from_hz(50));

	rp5c01_device &rtc(RP5C01(config, "rtc", XTAL(32'768)));
	rtc.out_alarm_callback().set(FUNC(mstation_state::rtc_irq));

	ADDRESS_MAP_BANK(config, "bank0").set_map(&mstation_state::mstation_banked_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);
	ADDRESS_MAP_BANK(config, "bank1").set_map(&mstation_state::mstation_banked_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("128K");
}

/* ROM definition */
ROM_START( mstation )
	ROM_REGION( 0x100000, "flash0", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v303a", "v3.03a" )
	ROMX_LOAD("ms303a.bin", 0x000000, 0x100000, CRC(7a5cf752) SHA1(15629ccaecd8094dd883987bed94c16eee6de7c2), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v253", "v2.53" )
	ROMX_LOAD("ms253.bin",  0x000000, 0x0fc000, BAD_DUMP CRC(a27e7f8b) SHA1(ae5a0aa0f1e23f3b183c5c0bcf4d4c1ae54b1798), ROM_BIOS(1))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY  FULLNAME       FLAGS
COMP( 1999, mstation, 0,      0,      mstation, mstation, mstation_state, empty_init, "CIDCO", "MailStation", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



msx1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol

#include "emu.h"
#include "msx.h"
#include "msx_keyboard.h"
#include "bus/msx/slot/ax230.h"
#include "bus/msx/slot/disk.h"
#include "bus/msx/slot/msx_rs232.h"
#include "bus/msx/slot/ram.h"
#include "bus/msx/slot/ram_mm.h"
#include "bus/msx/slot/rom.h"

#include "msx_ar.lh"
#include "msx_en.lh"
#include "msx_jp.lh"
#include "msx_jp_1fdd.lh"
#include "msx_kr.lh"
#include "msx_nocode.lh"
#include "msx_nocode_1fdd.lh"
#include "msx_nocode_nocaps.lh"

using namespace msx_keyboard;


/***************************************************************************

  MSX1 machine drivers

Undumped and/or not emulated:
- Daewoo CPC-200
- Daewoo Zemmix CPC-50
- Daewoo Zemmix DTX-1493FW
- General PCT-50
- General PCT-55
- Goldstar FC-80
- Hitachi MB-H21
- Hitachi MB-H80 (unreleased)
- In Tensai DPC-200CD
- Jotan Holland Bingo
- Misawa-Van CX-5
- Mitsubishi ML-FX2
- Mitsubishi ML-TS1
- Network DPC-200
- Olympia DPC-200
- Panasonic FS-3900
- Philips NMS 800
- Philips VG-8020/29 - MSX1
- Philips VG-8020/40 - MSX1
- Phonola VG-8000 (Italian market, mostly likely same as Philips VG-8000)
- Phonola VG-8010 (Italian market, mostly likely same as Philips VG-8010)
- Phonola VG-8020 (Italian market, mostly likely same as Philips VG-8020)
- Pioneer PX-V7
- Radiola MK 180
- Sakhr AH-200
- Sakhr AX-100
- Sakhr AX-170F
- Sakhr AX-330
- Sakhr AX-660
- Sakhr AX-990
- Salora MSX (prototypes)
- Sanno PHC-SPC
- Sanno SPCmk-II
- Sanno SPCmk-III
- Sanyo MPC-1 / Wavy1
- Schneider MC 810
- Sincorp SBX (Argentina, homebrew)
- Sony HB-10B
- Sony HB-10D
- Sony HB-501F
- Sony HB-75AS
- Sony HB-75B
- Sony HB-701
- Spectravideo SVI-728 (Arabic)
- Spectravideo SVI-728 (Danish/Norwegian)
- Spectravideo SVI-728 (Swedish/Finnish)
- Talent DPS-201
- Toshiba HX-10AA
- Toshiba HX-10DPN
- Toshiba HX-10I
- Toshiba HX-10SF
- Toshiba HX-20AR
- Toshiba HX-22CH
- Toshiba HX-22GB
- Toshiba HX-30
- Tosbiba HX-31
- Toshiba HX-52
- Triton PC64
- Vestel FC-200
- Victor HC-30
- Victor HC-60
- Wandy DPC-200
- Yamaha CX5
- Yamaha CX5MA (Australia / New Zealand)
- Yamaha CX5MC (Canada)
- Yamaha CX5ME (UK)
- Yamaha CX5MF (France)
- Yamaha CX5MG (Germany)
- Yamaha CX5MS (Scandinavia)
- Yamaha YIS-603
- Yeno DPC-64
***************************************************************************/

namespace {

class msx1_state : public msx_state
{
public:
	msx1_state(const machine_config &mconfig, device_type type, const char *tag)
		: msx_state(mconfig, type, tag, 10.738635_MHz_XTAL, 3)
	{
	}

	void ax150(machine_config &config);
	void ax170(machine_config &config);
	void ax230(machine_config &config);
	void canonv8(machine_config &config);
	void canonv10(machine_config &config);
	void canonv20(machine_config &config);
	void canonv20e(machine_config &config);
	void canonv25(machine_config &config);
	void cf1200(machine_config &config);
	void cf2000(machine_config &config);
	void cf2700(machine_config &config);
	void cf2700g(machine_config &config);
	void cf2700uk(machine_config &config);
	void cf3000(machine_config &config);
	void cf3300(machine_config &config);
	void cpc50a(machine_config &config);
	void cpc50b(machine_config &config);
	void cpc51(machine_config &config);
	void cpc88(machine_config &config);
	void cx5f(machine_config &config);
	void cx5f1(machine_config &config);
	void cx5mu(machine_config &config);
	void dgnmsx(machine_config &config);
	void dpc100(machine_config &config);
	void dpc180(machine_config &config);
	void dpc200(machine_config &config);
	void dpc200e(machine_config &config);
	void expert10(machine_config &config);
	void expert11(machine_config &config);
	void expert13(machine_config &config);
	void expertdp(machine_config &config);
	void expertpl(machine_config &config);
	void fmx(machine_config &config);
	void fdpc200(machine_config &config);
	void fpc500(machine_config &config);
	void fs1300(machine_config &config);
	void fs4000(machine_config &config);
	void fs4000a(machine_config &config);
	void fspc800(machine_config &config);
	void gfc1080(machine_config &config);
	void gfc1080a(machine_config &config);
	void gsfc80u(machine_config &config);
	void gsfc200(machine_config &config);
	void hb10(machine_config &config);
	void hb10p(machine_config &config);
	void hb20p(machine_config &config);
	void hb55(machine_config &config);
	void hb55d(machine_config &config);
	void hb55p(machine_config &config);
	void hb75(machine_config &config);
	void hb75d(machine_config &config);
	void hb75p(machine_config &config);
	void hb101(machine_config &config);
	void hb101p(machine_config &config);
	void hb201(machine_config &config);
	void hb201p(machine_config &config);
	void hb501p(machine_config &config);
	void hb701fd(machine_config &config);
	void hb8000(machine_config &config);
	void hc5(machine_config &config);
	void hc6(machine_config &config);
	void hc7(machine_config &config);
	void hotbi13b(machine_config &config);
	void hotbi13p(machine_config &config);
	void hx10(machine_config &config);
	void hx10d(machine_config &config);
	void hx10dp(machine_config &config);
	void hx10e(machine_config &config);
	void hx10f(machine_config &config);
	void hx10s(machine_config &config);
	void hx10sa(machine_config &config);
	void hx20(machine_config &config);
	void hx20e(machine_config &config);
	void hx20i(machine_config &config);
	void hx21(machine_config &config);
	void hx21f(machine_config &config);
	void hx22(machine_config &config);
	void hx22i(machine_config &config);
	void hx32(machine_config &config);
	void hx51i(machine_config &config);
	void jvchc7gb(machine_config &config);
	void mbh1(machine_config &config);
	void mbh1e(machine_config &config);
	void mbh2(machine_config &config);
	void mbh25(machine_config &config);
	void mbh50(machine_config &config);
	void ml8000(machine_config &config);
	void mlf48(machine_config &config);
	void mlf80(machine_config &config);
	void mlf110(machine_config &config);
	void mlf120(machine_config &config);
	void mlfx1(machine_config &config);
	void mpc10(machine_config &config);
	void mpc64(machine_config &config);
	void mpc100(machine_config &config);
	void mpc200(machine_config &config);
	void mpc200sp(machine_config &config);
	void mx10(machine_config &config);
	void mx15(machine_config &config);
	void mx64(machine_config &config);
	void mx101(machine_config &config);
	void nms801(machine_config &config);
	void perfect1(machine_config &config);
	void phc2(machine_config &config);
	void phc28(machine_config &config);
	void phc28l(machine_config &config);
	void phc28s(machine_config &config);
	void piopx7(machine_config &config);
	void piopx7uk(machine_config &config);
	void piopxv60(machine_config &config);
	void pv7(machine_config &config);
	void pv16(machine_config &config);
	void spc800(machine_config &config);
	void svi728(machine_config &config);
	void sx100(machine_config &config);
	void tadpc200(machine_config &config);
	void vg8000(machine_config &config);
	void vg8010(machine_config &config);
	void vg8010f(machine_config &config);
	void vg802000(machine_config &config);
	void vg802020(machine_config &config);
	void vg8020f(machine_config &config);
	void yc64(machine_config &config);
	void yis303(machine_config &config);
	void yis503(machine_config &config);
	void yis503f(machine_config &config);
};

/* MSX - Al Fateh 100 - rebranded Sakhr / Al Alamiah AX-170, dump needed to verify */

/* MSX - Al Fateh 123 - rebranded Sakhr / Al Alamiah AX-230, dump needed to verify */

/* MSX - AVT DPC-200 - See Fenner DPC-200 (they are the same machine, same roms) */

/* MSX - AVT FC-200 - probably same as Goldstar FC-80, dump needed to verify */

/* MSX - Bawareth Perfect MSX1 (DPC-200CD) */

ROM_START(perfect1)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("perfect1bios.rom", 0x0000, 0x8000, CRC(a317e6b4) SHA1(e998f0c441f4f1800ef44e42cd1659150206cf79))

	ROM_REGION(0x8000, "firmware", 0)
	ROM_SYSTEM_BIOS(0, "v1990", "1990 Firmware")
	ROMX_LOAD("cpc-200bw_v1_0", 0x0000, 0x8000, CRC(d6373270) SHA1(29a9169b605b5881e4a15fcfd65209a4e8679285), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v1987", "1987 Firmware (v3.21)")
	ROMX_LOAD("perfect1arab.rom", 0x0000, 0x8000, CRC(6db04a4d) SHA1(01012a0e2738708861f66b6921b2e2108f2edb54), ROM_BIOS(1))
ROM_END

void msx1_state::perfect1(machine_config &config)
{
	// GSS Z8400A PS cpu
	// AY-3-8910
	// TMS9129
	// DW64MX1
	// FDC: None, 0 drives
	// 1 Cartridge slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 0, 1, 1, 2, "firmware");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 2, 0, 4); // 64KB RAM
	add_cartridge_slot<1>(config, 1);
	// expansion slot in slot #2

	// The bios code sets the bit for the language mode LED but there is no LED present on the keyboard.
	msx1(VDP_TMS9129, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Canon V-8 */

ROM_START(canonv8)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("v8bios.rom", 0x0000, 0x8000, CRC(e941b08e) SHA1(97f9a0b45ee4b34d87ca3f163df32e1f48b0f09c))
ROM_END

void msx1_state::canonv8(machine_config &config)
{
	// NEC D780C cpu
	// YM2149 in S3527
	// FDC: None, 0 drives
	// 1 Cartridge slots
	// S3527
	// No printer port

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 3, 1);

	m_hw_def.has_printer_port(false);
	msx1(VDP_TMS9118, SND_YM2149, config, layout_msx_jp);
}

/* MSX - Canon V-10 */

ROM_START(canonv10)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("v10bios.rom", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))
ROM_END

void msx1_state::canonv10(machine_config &config)
{
	// Zilog Z80A
	// YM2149
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 3, 1);   // 16KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);

	msx1(VDP_TMS9918A, SND_YM2149, config, layout_msx_jp);
}

/* MSX - Canon V-20 */

ROM_START(canonv20)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("v20bios.rom", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))
ROM_END

void msx1_state::canonv20(machine_config &config)
{
	// NEC D780C cpu
	// XTAL: 14.31818(Z80/PSG) + 10.6875(VDP)
	// YM2149
	// TMS9918ANL
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4);  // 64KB RAM

	msx1(VDP_TMS9918A, SND_YM2149, config, layout_msx_jp);
}

/* MSX - Canon V-20E */

ROM_START(canonv20e)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("3256m67-5a3_z-2_uk.u20", 0x0000, 0x8000, CRC(e9ccd789) SHA1(8963fc041975f31dc2ab1019cfdd4967999de53e))
ROM_END

void msx1_state::canonv20e(machine_config &config)
{
	// Zilog Z8400A PS Z80A cpu
	// XTAL: 14.31818(Z80/PSG) + 10.6875(VDP)
	// YM2149
	// TMS9929ANL
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4);  // 64KB RAM

	msx1(VDP_TMS9929A, SND_YM2149, config, layout_msx_nocode);
}

/* MSX - Canon V-20F */

ROM_START(canonv20f)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("v20fbios.rom", 0x0000, 0x8000, BAD_DUMP CRC(e0e894b7) SHA1(d99eebded5db5fce1e072d08e642c0909bc7efdd)) // need verification
ROM_END

/* MSX - Canon V-20G */

ROM_START(canonv20g)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("v20gbios.rom", 0x0000, 0x8000, BAD_DUMP CRC(d6e704ad) SHA1(d67be6d7d56d7229418f4e122f2ec27990db7d19)) // need verification
ROM_END

/* MSX - Canon V-20S */

ROM_START(canonv20s)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("v20sbios.rom", 0x0000, 0x8000, BAD_DUMP CRC(c72b186e) SHA1(9fb289ea5c11d497ee00703f64e82575d1c59923)) // need verification
ROM_END

/* MSX - Casio MX-10 */

ROM_START(mx10)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("3256d19-5k3_z-1.g2", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))
ROM_END

void msx1_state::mx10(machine_config &config)
{
	// Z80: uPD780C-1
	// AY-3-8910
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Expansion slot for KB-10 to add a printer port and 2 more cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 3, 1); // 16KB RAM
	add_cartridge_slot<1>(config, 1);

	m_hw_def.has_cassette(false).has_printer_port(false);
	msx1(VDP_TMS9118, SND_AY8910, config, layout_msx_nocode_nocaps);
}

/* MSX - Casio MX-15 */

ROM_START(mx15)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("mx15bios.rom", 0x0000, 0x8000, CRC(6481230f) SHA1(a7ed5fd940f4e3a33e676840c0a83ac7ee54d972))
ROM_END

void msx1_state::mx15(machine_config &config)
{
	// AY-3-8910
	// T6950
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Expansion slot for KB-15 to add a printer port and 2 more cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 3, 1); // 16KB RAM
	add_cartridge_slot<1>(config, 1);

	m_hw_def.has_printer_port(false);
	msx1(VDP_TMS9928A, SND_AY8910, config, layout_msx_nocode_nocaps);
}

/* MSX - Casio MX-101 */

ROM_START(mx101)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("3256d19-5k3_z-1", 0x0000, 0x8000, BAD_DUMP CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821)) // need verification
ROM_END

void msx1_state::mx101(machine_config &config)
{
	// Z80: uPD780C-1
	// AY-3-8910
	// FDC: None, 0 drives
	// 1 Cartridge slots
	// 1 Expansion slot for KB-10 to add a printer port and 2 more cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 3, 1); // 16KB RAM
	add_cartridge_slot<1>(config, 1);

	m_hw_def.has_cassette(false).has_printer_port(false);
	msx1(VDP_TMS9118, SND_AY8910, config, layout_msx_nocode_nocaps);
}

/* MSX - Casio PV-7 */

ROM_START(pv7)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("pv7bios.rom", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))
ROM_END

void msx1_state::pv7(machine_config &config)
{
	// AY8910?
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// Non-standard cassette port
	// No printer port
	// 1 Expansion slot for KB-7 to add a printer port, 2 more cartridge slots, and 8KB RAM
	// Z80: uPD780C-1

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 3, 1).force_start_address(0xe000);   // 8KB RAM
	add_cartridge_slot<1>(config, 1);

	m_hw_def.has_printer_port(false);
	msx1(VDP_TMS9118, SND_AY8910, config, layout_msx_nocode_nocaps);
}

/* MSX - Casio PV-16 */

ROM_START(pv16)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("3256d19-5k3_z-1", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))
ROM_END

void msx1_state::pv16(machine_config &config)
{
	// NEC UPD780C
	// AY-3-8910
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// No printer port
	// 1 Expansion slot for KB-7 to add a printer port, 2 more cartridge slots, and 8KB RAM

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 3, 1);   // 16KB RAM
	add_cartridge_slot<1>(config, 1);

	m_hw_def.has_printer_port(false);
	msx1(VDP_TMS9118, SND_AY8910, config, layout_msx_nocode_nocaps);
}

/* MSX - CE-TEC MPC-80, German version of Daewoo DPC-200, dump needed to verify */

/* MSX - Daewoo CPC-200 */

/* MSX - Daewoo CPC-88 */

ROM_START(cpc88)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("88bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(3ab0cd3b) SHA1(171b587bd5a947a13f3114120b6e7baca3b57d78)) // need verification

	ROM_REGION(0x4000, "hangul", 0)
	ROM_LOAD("88han.rom",  0x0000, 0x2000, BAD_DUMP CRC(938db440) SHA1(d41676fde0a3047792f93c4a41509b8749e55e74)) // need verification
	ROM_RELOAD(0x2000, 0x2000) // Are the contents really mirrored?
ROM_END

void msx1_state::cpc88(machine_config &config)
{
	// AY-3-8910A
	// TMS9928A ?
	// FDC: None, 0 drives
	// 0 Cartridge slots
	// Expansion slot allows addition of cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "hangul", 0, 2, 1, "hangul");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4);   // 64KB RAM

	m_hw_def.has_printer_port(false);
	msx1(VDP_TMS9928A, SND_AY8910, config, layout_msx_kr);
}

/* MSX - Daewoo DPC-100 */

ROM_START(dpc100)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("100bios.rom", 0x0000, 0x8000, CRC(3ab0cd3b) SHA1(171b587bd5a947a13f3114120b6e7baca3b57d78))

	ROM_REGION(0x4000, "hangul", 0)
	// should be 0x2000?
	ROM_LOAD("100han.rom",  0x0000, 0x4000, CRC(97478efb) SHA1(4421fa2504cbce18f7c84b5ea97f04e017007f07))
ROM_END

void msx1_state::dpc100(machine_config &config)
{
	// GSS Z8400A PS
	// AY-3-8910A
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Expansion slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "hangul", 0, 2, 1, "hangul");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 3, 1);   // 16KB RAM
	// expansion slot is in slot #3

	msx1(VDP_TMS9918A, SND_AY8910, config, layout_msx_kr);
}

/* MSX - Daewoo DPC-180 */

ROM_START(dpc180)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("180bios.rom", 0x0000, 0x8000, CRC(3ab0cd3b) SHA1(171b587bd5a947a13f3114120b6e7baca3b57d78))

	ROM_REGION(0x4000, "hangul", 0)
	// should be 0x2000?
	ROM_LOAD("180han.rom",  0x0000, 0x4000, CRC(97478efb) SHA1(4421fa2504cbce18f7c84b5ea97f04e017007f07))
ROM_END

void msx1_state::dpc180(machine_config &config)
{
	// AY-3-8910A
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Expansion slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "hangul", 0, 2, 1, "hangul");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4);   // 64KB RAM
	// Expansion slot is in slot #3

	msx1(VDP_TMS9918A, SND_AY8910, config, layout_msx_kr);
}

/* MSX - Daewoo DPC-200 */

ROM_START(dpc200)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("200bios.rom", 0x0000, 0x8000, CRC(3ab0cd3b) SHA1(171b587bd5a947a13f3114120b6e7baca3b57d78))

	ROM_REGION(0x4000, "hangul", 0)
	// should be 0x2000?
	ROM_LOAD("200han.rom",  0x0000, 0x4000, CRC(97478efb) SHA1(4421fa2504cbce18f7c84b5ea97f04e017007f07))
ROM_END

void msx1_state::dpc200(machine_config &config)
{
	// GSS Z8400A PS cpu
	// AY-3-8910A
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Expansion slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "hangul", 0, 2, 1, "hangul");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4);  // 64KB RAM
	// Expansion slot is in slot #3

	msx1(VDP_TMS9918A, SND_AY8910, config, layout_msx_kr);
}

/* MSX - Daewoo DPC-200E (France) */

ROM_START(dpc200e)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("dpc200ebios.rom", 0x0000, 0x8000, CRC(d2110d66) SHA1(d3af963e2529662eae63f04a2530454685a1989f))
ROM_END

void msx1_state::dpc200e(machine_config &config)
{
	// AY-3-8910
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Expansion slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 1, 0, 4);  // 64KB RAM
	add_cartridge_slot<1>(config, 2);
	// Expansion slot is in slot #3

	msx1(VDP_TMS9129, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Daewoo Zemmix CPC-50 */

/* MSX - Daewoo Zemmix CPC-50A */

ROM_START(cpc50a)
	ROM_REGION(0x8000, "mainrom", 0)
	// HM6264LP-15 / U0422880 (ic4)
	// GMCE? VER1.01 (ic5)
	ROM_LOAD("50abios.rom", 0x0000, 0x8000, BAD_DUMP CRC(c3a868ef) SHA1(a08a940aa87313509e00bc5ac7494d53d8e03492)) // need verification
ROM_END

void msx1_state::cpc50a(machine_config &config)
{
	// NEC uPD780C cpu
	// AY-3-8910A
	// DW64MX1
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// No keyboard
	// No cassette port
	// No printer port

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 3, 1).force_start_address(0xe000);  // 8KB RAM

	m_hw_def.has_cassette(false).has_printer_port(false);
	// It was released in Korea but there are no Korean specific things about the hardware
	msx1(VDP_TMS9118, SND_AY8910, config, layout_msx_nocode_nocaps);
}

/* MSX - Daewoo Zemmix CPC-50B */

ROM_START(cpc50b)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("50bbios.rom", 0x0000, 0x8000, BAD_DUMP CRC(c3a868ef) SHA1(a08a940aa87313509e00bc5ac7494d53d8e03492)) // need verification
ROM_END

void msx1_state::cpc50b(machine_config &config)
{
	// AY-3-8910A
	// DW64MX1
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// No keyboard
	// No cassette port
	// No printer port

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 3, 1);  // 16KB RAM

	m_hw_def.has_cassette(false).has_printer_port(false);
	// It was released in Korea but there are no Korean specific things about the hardware
	msx1(VDP_TMS9118, SND_AY8910, config, layout_msx_nocode_nocaps);
}

/* MSX - Daewoo Zemmix CPC-51 */

ROM_START(cpc51)
	ROM_REGION(0x8000, "mainrom", 0)
	// Sticker: CPC-51 V 1.01 (ic05)
	ROM_LOAD("cpc-51_v_1_01.ic05", 0x0000, 0x8000, CRC(c3a868ef) SHA1(a08a940aa87313509e00bc5ac7494d53d8e03492))
ROM_END

void msx1_state::cpc51(machine_config &config)
{
	// GSS Z8400A PS cpu
	// AY-3-8910A
	// FDC: None, 0 drives
	// DW64MX1
	// 1 Cartridge slot
	// No keyboard, just a keyboard connector
	// No cassette port
	// No printer port

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4);  // 64KB RAM

	m_hw_def.has_cassette(false).has_printer_port(false);
	// It was released in Korea but there are no Korean specific things about the hardware
	msx1(VDP_TMS9118, SND_AY8910, config, layout_msx_nocode_nocaps);
}

/* MSX - Daewoo Zemmix DTX-1493FW */

/* MSX - Dragon MSX-64 */

ROM_START(dgnmsx)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("uk1msx048.ic37", 0x0000, 0x4000, BAD_DUMP CRC(24c198be) SHA1(7f8c94cb8913db32a696dec80ffc78e46693f1b7)) // need verification
	ROM_LOAD("uk2msx058.ic6",  0x4000, 0x4000, BAD_DUMP CRC(e516e7e5) SHA1(05fedd4b9bfcf4949020c79d32c4c3f03a54fb62)) // need verification
ROM_END

void msx1_state::dgnmsx(machine_config &config)
{
	// Sharp LH0080A cpu
	// AY-3-8910
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4);  // 64KB RAM
	add_cartridge_slot<2>(config, 3);

	msx1(VDP_TMS9129, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Dynadata DPC-200 */
// GSS Z8400A PS
// AY-3-8910A
// 1 Cartridge slot
// 1 Expansion slot
// TMS9929A

/* MSX - Fenner DPC-200 */

ROM_START(fdpc200)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("dpc200bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(8205795e) SHA1(829c00c3114f25b3dae5157c0a238b52a3ac37db)) // need verification
ROM_END

void msx1_state::fdpc200(machine_config &config)
{
	// AY-3-8910
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Expansion slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 1, 0, 4);  // 64KB RAM
	add_cartridge_slot<1>(config, 2);
	// Expansion slot

	msx1(VDP_TMS9929A, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Fenner FPC-500 */

ROM_START(fpc500)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("fpc500bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(8205795e) SHA1(829c00c3114f25b3dae5157c0a238b52a3ac37db)) // need verification
ROM_END

void msx1_state::fpc500(machine_config &config)
{
	// AY-3-8910
	// T6950 vdp
	// T7775 msx engine
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4);  // 64KB RAM

	msx1(VDP_TMS9929A, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Fenner SPC-800 */

ROM_START(fspc800)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("spc800bios.u7", 0x0000, 0x8000, CRC(e9ccd789) SHA1(8963fc041975f31dc2ab1019cfdd4967999de53e))
ROM_END

void msx1_state::fspc800(machine_config &config)
{
	// GSS Z8400A
	// AY-3-8910A
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4);  // 64KB RAM

	msx1(VDP_TMS9129, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Fujitsu FM-X */

ROM_START(fmx)
	ROM_REGION(0x8000, "mainrom", 0)
	// mb62h010 ?
	ROM_LOAD("fmxbios.rom", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))
ROM_END

void msx1_state::fmx(machine_config &config)
{
	// 21.4772Mhz
	// AY-3-8910A
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 "Fujitsu expansion" slot for MB22450 (to connect FM-X to FM7) or MB22451 (printer port + 16KB ram)

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 3, 1); // 16KB RAM
	// Fujitsu expansion slot #1 in slot 1
	add_cartridge_slot<1>(config, 2);

	m_hw_def.has_printer_port(false);
	msx1(VDP_TMS9928A, SND_AY8910, config, layout_msx_jp);
}

/* MSX - General PCT-50 */

/* MSX - General PCT-55 */

/* MSX - Goldstar FC-80 */

/* MSX - Goldstar FC-80U */

ROM_START(gsfc80u)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("fc80ubios.rom", 0x0000, 0x8000, CRC(3ab0cd3b) SHA1(171b587bd5a947a13f3114120b6e7baca3b57d78))

	ROM_REGION(0x4000, "hangul", 0)
	ROM_LOAD("fc80uhan.rom",  0x0000, 0x2000, CRC(0cdb8501) SHA1(58dbe73ae80c2c409e766c3ace730ecd7bec89d0))
	ROM_RELOAD(0x2000, 0x2000)
ROM_END

void msx1_state::gsfc80u(machine_config &config)
{
	// AY-3-8910A
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Expansion slot
	// Hangul LED

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "hangul", 0, 2, 1, "hangul");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4);  // 64KB RAM
	// Expansion slot in slot #3

	msx1(VDP_TMS9918A, SND_AY8910, config, layout_msx_kr);
}

/* MSX - Goldstar FC-200 */

ROM_START(gsfc200)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("fc200bios.rom.u5a", 0x0000, 0x4000, CRC(61f473fb) SHA1(c425750bbb2ae1d278216b45029d303e37d8df2f))
	ROM_LOAD("fc200bios.rom.u5b", 0x4000, 0x4000, CRC(1a99b1a1) SHA1(e18f72271b64693a2a2bc226e1b9ebd0448e07c0))
ROM_END

void msx1_state::gsfc200(machine_config &config)
{
	// AY-3-8910A
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Expansion slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4);  // 64KB RAM
	// Expansion slot in slot #3

	msx1(VDP_TMS9129, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Goldstar GFC-1080 */

ROM_START(gfc1080)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("gfc1080bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(d9cdd4a6) SHA1(6b0be712b9c95c1e912252ab5703e1c0bc457d9e)) // need verification

	ROM_REGION(0x4000, "hangul", 0)
	ROM_LOAD("gfc1080han.rom", 0x0000, 0x4000, BAD_DUMP CRC(f209448c) SHA1(141b44212ba28e7d03e0b54126fedd9e0807dc42)) // need verification

	ROM_REGION(0x4000, "pasocalc", 0)
	ROM_LOAD("gfc1080pasocalc.rom", 0x0000, 0x4000, BAD_DUMP CRC(4014f7ea) SHA1(a5581fa3ce10f90f15ba3dc53d57b02d6e4af172)) // need verification
ROM_END

void msx1_state::gfc1080(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Expansion slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "hangul", 0, 2, 1, "hangul");
	add_internal_slot(config, MSX_SLOT_ROM, "pasocalc", 0, 3, 1, "pasocalc");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4); // 64KB RAM
	// Expansion slot in slot #3

	msx1(VDP_TMS9928A, SND_AY8910, config, layout_msx_kr);
}

/* MSX - Goldstar GFC-1080A */

ROM_START(gfc1080a)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("gfc1080abios.rom", 0x0000, 0x8000, BAD_DUMP CRC(3ab0cd3b) SHA1(171b587bd5a947a13f3114120b6e7baca3b57d78)) // need verification

	ROM_REGION(0x4000, "hangul", 0)
	ROM_LOAD("gfc1080ahan.rom", 0x0000, 0x2000, BAD_DUMP CRC(0cdb8501) SHA1(58dbe73ae80c2c409e766c3ace730ecd7bec89d0)) // need verification
	ROM_RELOAD(0x2000, 0x2000)
ROM_END

void msx1_state::gfc1080a(machine_config &config)
{
	// AY-3-8910A
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Expansion slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "hangul", 0, 2, 1, "hangul");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4); // 64KB RAM
	// Expansion slot in slot #3

	msx1(VDP_TMS9918A, SND_AY8910, config, layout_msx_kr);
}

/* MSX - Gradiente Expert 1.3 - source? */

ROM_START(expert13)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("expbios13.rom", 0x0000, 0x8000, BAD_DUMP CRC(5638bc38) SHA1(605f5af3f358c6811f54e0173bad908614a198c0)) // need verification
ROM_END

void msx1_state::expert13(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: None, 0 drives
	// 2 Cartridge slots?

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 2, 0, 4).set_total_size(0x10000);   // 64KB Mapper RAM
	add_cartridge_slot<2>(config, 3);

	msx1(VDP_TMS9928A, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Gradiente Expert DDPlus */
ROM_START(expertdp)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("eddpbios.rom", 0x0000, 0x8000, CRC(efb4b972) SHA1(d6720845928ee848cfa88a86accb067397685f02))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("eddpdisk.rom", 0x0000, 0x4000, CRC(549f1d90) SHA1(f1525de4e0b60a6687156c2a96f8a8b2044b6c56))
ROM_END

void msx1_state::expertdp(machine_config &config)
{
	// T7766A (AY-3-8910A compatible, integrated in T7937A)
	// FDC: mb8877a, 1 3.5" DSDD drive
	// non-standard cassette port
	// non-standard printer port
	// 2 Cartridge slots
	// T6950 (integrated in T7937A)
	// MSX Engine T7937A (also VDP)

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 0, 4);  // 64KB RAM
	add_internal_disk_mirrored(config, MSX_SLOT_DISK2_MB8877, "diskrom", 3, 3, 1, 2, "diskrom");

	msx1(VDP_TMS9928A, SND_AY8910, config, layout_msx_nocode_1fdd);
}

/* MSX - Gradiente Expert Plus */

ROM_START(expertpl)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("exppbios.rom", 0x0000, 0x8000, CRC(efb4b972) SHA1(d6720845928ee848cfa88a86accb067397685f02))

	ROM_REGION(0x4000, "demo", 0)
	ROM_LOAD("exppdemo.rom", 0x0000, 0x4000, CRC(a9bbef64) SHA1(d4cea8c815f3eeabe0c6a1c845f902ec4318bf6b))
ROM_END

void msx1_state::expertpl(machine_config &config)
{
	// T7766A (AY-3-8910 compatible in T7937A)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// MSX Engine T7937A (with T6950 VDP and T7766A psg)

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 0, 4);  // 64KB RAM
	add_internal_slot(config, MSX_SLOT_ROM, "demo", 3, 3, 2, 1, "demo");

	msx1(VDP_TMS9928A, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Gradiente Expert XP-800 (1.0) */

ROM_START(expert10)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("expbios.rom", 0x0000, 0x8000, CRC(07610d77) SHA1(ef3e010eb57e4476700a3bbff9d2119ab3acdf62))
ROM_END

void msx1_state::expert10(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: None, 0 drives
	// non-standard cassette port
	// non-standard printer port
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4);  // 64KB RAM
	// when no cartridge is inserted the expansion slot can be used in this slot
	add_cartridge_slot<2>(config, 3);

	msx1(VDP_TMS9128, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Gradiente Expert XP-800 (1.1) / GPC-1 */
ROM_START(expert11)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("expbios11.rom", 0x0000, 0x8000, CRC(efb4b972) SHA1(d6720845928ee848cfa88a86accb067397685f02))
ROM_END

void msx1_state::expert11(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4);  // 64KB RAM
	// when no cartridge is inserted the expansion slot can be used in this slot
	add_cartridge_slot<2>(config, 3);

	msx1(VDP_TMS9128, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Hitachi MB-H1 */

ROM_START(mbh1)
	ROM_REGION(0xc000, "mainrom", 0)
	ROM_LOAD("mbh1bios.rom", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))

	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("mbh1firm.rom", 0x0000, 0x2000, CRC(83f5662b) SHA1(3e005832138ffde8b1c36025754f81c2112b236d))
ROM_END

void msx1_state::mbh1(machine_config &config)
{
	// AY-3-8910
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// Speed controller (normal, slow 1, slow 2)
	// Firmware should be bypassed when a cartridge is inserted

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 2, 2); // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 3, 1, 1, "firmware");

	msx1(VDP_TMS9918A, SND_AY8910, config, layout_msx_jp);
}

/* MSX - Hitachi MB-H1E */

ROM_START(mbh1e)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("mbh1bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821)) // need verification
ROM_END

void msx1_state::mbh1e(machine_config &config)
{
	// AY-3-8910
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// Speed controller (normal, slow 1, slow 2)

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 3, 1); // 16KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);

	msx1(VDP_TMS9918A, SND_AY8910, config, layout_msx_jp);
}

/* MSX - Hitachi MB-H2 */

ROM_START(mbh2)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("mbh2bios.rom", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))

	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("mbh2firm.rom", 0x0000, 0x4000, CRC(4f03c947) SHA1(e2140fa2e8e59090ecccf55b62323ea9dcc66d0b))
ROM_END

void msx1_state::mbh2(machine_config &config)
{
	// AY-3-8910
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// Builtin cassette player
	// Speed controller (normal, slow 1, slow 2)

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 0, 2, 1, "firmware");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4); // 64KB RAM

	msx1(VDP_TMS9928A, SND_AY8910, config, layout_msx_jp);
}

/* MSX - Hitachi MB-H21 */

/* MSX - Hitachi MB-H25 */

ROM_START(mbh25)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("mbh25bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821)) // need verification
ROM_END

void msx1_state::mbh25(machine_config &config)
{
	// AY-3-8910
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// Speed controller (normal, slow 1, slow 2)

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 2, 2); // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);

	msx1(VDP_TMS9118, SND_AY8910, config, layout_msx_jp);
}

/* MSX - Hitachi MB-H50 */

ROM_START(mbh50)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("mbh50bios.rom", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))
ROM_END

void msx1_state::mbh50(machine_config &config)
{
	// YM2149 in S3527
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// T6950A
	// MSX-Engine S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 2, 0, 4); // 64KB RAM

	msx1(VDP_TMS9928A, SND_YM2149, config, layout_msx_jp);
}

/* MSX - Hitachi MB-H80 (unreleased) */

/* MSX - In Tensai DPC-200CD */

/* MSX - JVC HC-7E / HC-7GB (different power supplies) */

ROM_START(jvchc7gb)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hc7gbbios.rom", 0x0000, 0x8000, CRC(e9ccd789) SHA1(8963fc041975f31dc2ab1019cfdd4967999de53e))
ROM_END

void msx1_state::jvchc7gb(machine_config &config)
{
	// AY-3-8910A
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4);  // 64KB RAM
	add_cartridge_slot<2>(config, 3);

	msx1(VDP_TMS9929A, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Jotan Holland Bingo */

/* MSX - Misawa-Van CX-5 */

/* MSX - Mitsubishi ML-8000 */

ROM_START(ml8000)
	ROM_REGION(0x8000, "mainrom", 0)
	// Same contents as standard 32kb bios with sha1 302afb5d8be26c758309ca3df611ae69cced2821
	// split across 4 eeproms
	ROM_LOAD("1.ic56", 0x0000, 0x2000, BAD_DUMP CRC(782e39fd) SHA1(ad20865df0d33ee5379b69be984302fb85d74c5a)) // need verification
	ROM_LOAD("2.ic32", 0x2000, 0x2000, BAD_DUMP CRC(d859cf14) SHA1(b6894ed3cd5be5a73a93fd02d275d6c1237310e6)) // need verification
	ROM_LOAD("3.ic26", 0x4000, 0x2000, BAD_DUMP CRC(b3211adf) SHA1(fad7be46d1137f636c23dc01e498cc38cff486e3)) // need verification
	ROM_LOAD("4.ic22", 0x6000, 0x2000, BAD_DUMP CRC(24e09092) SHA1(d6fdff0fa86a248ce319888275d1c634480b58c4)) // need verification
ROM_END

void msx1_state::ml8000(machine_config &config)
{
	// NEC uPD780C
	// AY-3-8910
	// FDC: None, 0 drives
	// 1 Cartridge slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 2, 2); // 32KB RAM
	add_cartridge_slot<1>(config, 1);

	msx1(VDP_TMS9918A, SND_AY8910, config, layout_msx_jp);
}

/* MSX - Mitsubishi ML-F48 */

ROM_START(mlf48)
	ROM_REGION(0x8000, "mainrom", 0)
	// 4j3 hn613256p m82 ?
	ROM_LOAD("mlf48bios.ic2d", 0x0000, 0x8000, BAD_DUMP CRC(e9ccd789) SHA1(8963fc041975f31dc2ab1019cfdd4967999de53e)) // needs verification
ROM_END

void msx1_state::mlf48(machine_config &config)
{
	// NEC uPD780C
	// AY-3-8910A
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 1, 2, 2); // 32KB RAM
	add_cartridge_slot<1>(config, 2);
	add_cartridge_slot<2>(config, 3);

	msx1(VDP_TMS9929A, SND_AY8910, config, layout_msx_nocode); // videochip needs verification
}

/* MSX - Mitsubishi ML-F80 */

ROM_START(mlf80)
	ROM_REGION(0x8000, "mainrom", 0)
	// 4j1 hn613256p m82 ?
	ROM_LOAD("mlf80bios.ic2d", 0x0000, 0x8000, CRC(e9ccd789) SHA1(8963fc041975f31dc2ab1019cfdd4967999de53e))
ROM_END

void msx1_state::mlf80(machine_config &config)
{
	// 21.3750MHz
	// AY-3-8910A
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 1, 0, 4);  // 64KB RAM
	add_cartridge_slot<1>(config, 2);
	add_cartridge_slot<2>(config, 3);

	msx1(VDP_TMS9929A, SND_AY8910, config, layout_msx_nocode); // videochip needs verification
}

/* MSX - Mitsubishi ML-F110 */

ROM_START(mlf110)
	ROM_REGION(0x8000, "mainrom", 0)
	// 4j1 mn613256p d35
	ROM_LOAD("hn613256p.ic6d", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))
ROM_END

void msx1_state::mlf110(machine_config &config)
{
	// NEC uPD780C
	// AY-3-8910A
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 1, 3, 1); // 16KB RAM
	add_cartridge_slot<1>(config, 2);
	add_cartridge_slot<2>(config, 3);

	msx1(VDP_TMS9918A, SND_AY8910, config, layout_msx_jp);
}

/* MSX - Mitsubishi ML-F120 / ML-F120D */

ROM_START(mlf120)
	ROM_REGION(0x8000, "mainrom", 0)
	// 4k3 hn613256p d35 ?
	// 904p874h11 ?
	ROM_LOAD("hn613256p.ic6d", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))

	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("t704p890h11.ic8d", 0x0000, 0x4000, CRC(4b5f3173) SHA1(21a9f60cb6370d0617ce54c42bb7d8e40a4ab560))
ROM_END

void msx1_state::mlf120(machine_config &config)
{
	// NEC uPD780C
	// AY-3-8910A
	// FDC: None, 0 drives
	// 2 Cartridge slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 2, 2); // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 3, 1, 1, "firmware");

	msx1(VDP_TMS9918A, SND_AY8910, config, layout_msx_jp);
}

/* MSX - Mitsubishi ML-F120D (functionality wise same as ML-F120 but with RGB out instead of composite)
Different PCB from ML-F120:
21.4772Mhz
TMS9928A
AY-3-8910
3 ROMs? - labels unreadable, locations ic5?, ic7f, ic10f
 */

/* MSX - Mitsubishi ML-FX1 */

ROM_START(mlfx1)
	ROM_REGION(0x8000, "mainrom", 0)
	// 5c1 hn613256p t21 t704p874h21-u
	ROM_LOAD("mlfx1bios.ic6c", 0x0000, 0x8000, CRC(62867dce) SHA1(0cbe0df4af45e8f531e9c761403ac9e71808f20c))
ROM_END

void msx1_state::mlfx1(machine_config &config)
{
	// NEC uPD780C
	// YM2149 (in S3527 MSX Engine)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// MSX-Engine S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 2, 0, 4);  // 64KB RAM

	msx1(VDP_TMS9929A, SND_YM2149, config, layout_msx_nocode);
}

/* MSX - Mitsubishi ML-FX2 */

/* MSX - Mitsubishi ML-TS1 */

/* MSX - National CF-1200 */

ROM_START(cf1200)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("1200bios.rom", 0x0000, 0x8000, CRC(5ad03407) SHA1(c7a2c5baee6a9f0e1c6ee7d76944c0ab1886796c))
ROM_END

void msx1_state::cf1200(machine_config &config)
{
	// AY8910, needs verification
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 3, 1);   // 16KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);

	msx1(VDP_TMS9918A, SND_AY8910, config, layout_msx_jp); // soundchip needs verification
}

/* MSX - National CF-2000 */

ROM_START(cf2000)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("2000bios.rom", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))
ROM_END

void msx1_state::cf2000(machine_config &config)
{
	// AY-3-8910A, needs verification
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 3, 1);   // 16KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);

	msx1(VDP_TMS9928A, SND_AY8910, config, layout_msx_jp); // soundchip needs verification
}

/* MSX - National CF-2700 */

ROM_START(cf2700)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("2700bios.rom.ic32", 0x0000, 0x8000, CRC(5ad03407) SHA1(c7a2c5baee6a9f0e1c6ee7d76944c0ab1886796c))
ROM_END

void msx1_state::cf2700(machine_config &config)
{
	// NEC upD780C
	// AY-3-8910A
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 2, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);

	msx1(VDP_TMS9918A, SND_AY8910, config, layout_msx_jp);
}

/* MSX - National CF-3000 */

ROM_START(cf3000)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("3000bios.rom", 0x0000, 0x8000, CRC(5ad03407) SHA1(c7a2c5baee6a9f0e1c6ee7d76944c0ab1886796c))
ROM_END

void msx1_state::cf3000(machine_config &config)
{
	// AY-3-8910
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 1, 0, 4);  // 64KB RAM
	add_cartridge_slot<1>(config, 2);
	add_cartridge_slot<2>(config, 3);

	msx1(VDP_TMS9928A, SND_AY8910, config, layout_msx_jp);
}

/* MSX - National CF-3300 */

ROM_START(cf3300)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("3300bios.rom", 0x0000, 0x8000, CRC(5ad03407) SHA1(c7a2c5baee6a9f0e1c6ee7d76944c0ab1886796c))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("3300disk.rom", 0x0000, 0x4000, CRC(549f1d90) SHA1(f1525de4e0b60a6687156c2a96f8a8b2044b6c56))
ROM_END

void msx1_state::cf3300(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: mb8877a, 1 3.5" SSDD drive
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 0, 4);  // 64KB RAM
	add_internal_disk_mirrored(config, MSX_SLOT_DISK2_MB8877_SS, "diskrom", 3, 1, 1, 2, "diskrom");

	msx1(VDP_TMS9928A, SND_AY8910, config, layout_msx_jp_1fdd);
}

/* MSX - National FS-1300 */

ROM_START(fs1300)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("1300bios.rom", 0x0000, 0x8000, CRC(5ad03407) SHA1(c7a2c5baee6a9f0e1c6ee7d76944c0ab1886796c))
ROM_END

void msx1_state::fs1300(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4);  // 64KB RAM

	// Unclear from images whether caps and code leds are present; assuming they are present.
	msx1(VDP_TMS9918A, SND_AY8910, config, layout_msx_jp);
}

/* MSX - National FS-4000 */

ROM_START(fs4000)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("4000bios.rom",  0x0000, 0x8000, CRC(071135e0) SHA1(df48902f5f12af8867ae1a87f255145f0e5e0774))

	ROM_REGION(0x8000, "word", 0)
	ROM_LOAD("4000word.rom",  0x0000, 0x8000, CRC(950b6c87) SHA1(931d6318774bd495a32ec3dabf8d0edfc9913324))

	ROM_REGION(0x8000, "kdr", 0)
	ROM_LOAD("4000kdr.rom",  0x0000, 0x8000, CRC(ebaa5a1e) SHA1(77bd67d5d10d459d343e79eafcd8e17eb0f209dd))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("4000kfn.rom", 0, 0x20000, CRC(956dc96d) SHA1(9ed3ab6d893632b9246e91b412cd5db519e7586b))
ROM_END

void msx1_state::fs4000(machine_config &config)
{
	// YM2149 in S3527
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// MSX-Engine S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "word", 3, 0, 0, 2, "word");
	add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 1, 1, 2, "kdr");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 2, 0, 4);  // 64KB RAM

	msx1(VDP_TMS9128, SND_YM2149, config, layout_msx_jp);
}

/* MSX - National FS-4000 (Alt) */

ROM_START(fs4000a)
	ROM_REGION(0x8000 ,"mainrom", 0)
	ROM_LOAD("4000bios.rom",  0x0000, 0x8000, BAD_DUMP CRC(071135e0) SHA1(df48902f5f12af8867ae1a87f255145f0e5e0774)) // need verification

	ROM_REGION(0x8000, "word", 0)
	ROM_LOAD("4000wora.rom",  0x0000, 0x8000, BAD_DUMP CRC(52f4cdf7) SHA1(acbac3cb5b700254bed2cacc19fa54f1950f371d)) // need verification

	ROM_REGION(0x8000, "kdr", 0)
	ROM_LOAD("4000kdra.rom", 0x0000, 0x8000, BAD_DUMP CRC(b2db6bf5) SHA1(3a9a942ed888dd641cddf8deada1879c454df3c6)) // need verification

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("4000kfn.rom", 0, 0x20000, BAD_DUMP CRC(956dc96d) SHA1(9ed3ab6d893632b9246e91b412cd5db519e7586b)) // need verification
ROM_END

void msx1_state::fs4000a(machine_config &config)
{
	// YM2149 in S3527
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// MSX-Engine S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "word", 3, 0, 0, 2, "word");
	add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 1, 1, 2, "kdr");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 2, 0, 4);  // 64KB RAM

	msx1(VDP_TMS9128, SND_YM2149, config, layout_msx_jp);
}

/* MSX - Network DPC-200 */

/* MSX - Olympia DPC-200 */
// GSS Z8400A PS
// AY-3-8910A
// ic8 ROM 9256C-0047 R09256C-INT

/* MSX - Olympia PHC-2 */

ROM_START(phc2)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("r09256c-fra.ic8", 0x0000, 0x8000, CRC(d2110d66) SHA1(d3af963e2529662eae63f04a2530454685a1989f))
ROM_END

void msx1_state::phc2(machine_config &config)
{
	// AY-3-8910
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Expansion slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 1, 0, 4);  // 64KB RAM
	add_cartridge_slot<1>(config, 2);
	// Expansion slot in slot #3

	msx1(VDP_TMS9129, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Olympia PHC-28 */

ROM_START(phc28)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("phc28bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(eceb2802) SHA1(195950173701abeb460a1a070d83466f3f53b337)) // need verification
ROM_END

void msx1_state::phc28(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// 1 Expansion slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 3, 1);   // 16KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	// Expansion slot in slot #3

	msx1(VDP_TMS9929A, SND_AY8910, config, layout_msx_nocode); // soundchip and videochip need verification
}

/* MSX - Panasonic CF-2700 (Germany) */

ROM_START(cf2700g)
	ROM_REGION(0x8000, "mainrom", 0)
	// mn23257rfa
	ROM_LOAD("cf2700g.ic32", 0x0000, 0x8000, CRC(4aa194f4) SHA1(69bf27b610e11437dad1f7a1c37a63179a293d12))
ROM_END

void msx1_state::cf2700g(machine_config &config)
{
	// NEC uPD780C
	// AY-3-8910A
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 1, 0, 4);  // 64KB RAM
	add_cartridge_slot<1>(config, 2);
	add_cartridge_slot<2>(config, 3);

	msx1(VDP_TMS9129, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Panasonic CF-2700 (UK) */

ROM_START(cf2700uk)
	ROM_REGION(0x8000, "mainrom", 0)
	// mn23257rfa
	ROM_LOAD("cf2700uk.ic32", 0x0000, 0x8000, CRC(15e503de) SHA1(5e6b1306a30bbb46af61487d1a3cc1b0a69004c3))
ROM_END

void msx1_state::cf2700uk(machine_config &config)
{
	// NEC uPD780C
	// AY-3-8910A
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 1, 0, 4);  // 64KB RAM
	add_cartridge_slot<1>(config, 2);
	add_cartridge_slot<2>(config, 3);

	msx1(VDP_TMS9129, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Panasonic FS-3900 */

/* MSX - Philips NMS 800 */
// SGS Z8400AB1
// AY-3-8910A
// TMS9129
// U3 - ST27256-25CP
// 0 Cartridge slots
// No printer port

/* MSX - Philips NMS 801 */

ROM_START(nms801)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("801bios.rom", 0x0000, 0x8000, CRC(fa089461) SHA1(21329398c0f350e330b353f45f21aa7ba338fc8d))
ROM_END

void msx1_state::nms801(machine_config &config)
{
	// SGS Z8400AB1
	// AY-3-8910A
	// FDC: None, 0 drives
	// 0 Cartridge slots
	// No printer port

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4);  // 64KB RAM

	m_hw_def.has_printer_port(false);
	msx1(VDP_TMS9129, SND_AY8910, config, layout_msx_nocode_nocaps);
}

/* MSX - Philips VG-8000 */

ROM_START(vg8000)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("8000bios.rom", 0x0000, 0x8000, CRC(efd970b0) SHA1(42252cf87deeb58181a7bfec7c874190a1351779))
ROM_END

void msx1_state::vg8000(machine_config &config)
{
	// SGS Z8400AB1
	// AY-3-8910
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// No printer port

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 3, 1);   // 16KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);

	m_hw_def.has_printer_port(false);
	msx1(VDP_TMS9129, SND_AY8910, config, layout_msx_en);
}

/* MSX - Philips VG-8010 / VG-8010/00 */

ROM_START(vg8010)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("vg8000v1_0.663", 0x0000, 0x8000, CRC(efd970b0) SHA1(42252cf87deeb58181a7bfec7c874190a1351779))
ROM_END

void msx1_state::vg8010(machine_config &config)
{
	// SGS Z8400AB1
	// AY-3-8910
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// No printer port

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 2, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);

	m_hw_def.has_printer_port(false);
	msx1(VDP_TMS9129, SND_AY8910, config, layout_msx_en);
}

/* MSX - Philips VG-8010F / VG-8010/19 */

ROM_START(vg8010f)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("8010fbios.663", 0x0000, 0x8000, CRC(df57c9ca) SHA1(898630ad1497dc9a329580c682ee55c4bcb9c30c))
ROM_END

void msx1_state::vg8010f(machine_config &config)
{
	// SGS Z8400AB1
	// AY-3-8910
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// No printer port

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 2, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);

	m_hw_def.has_printer_port(false);
	msx1(VDP_TMS9129, SND_AY8910, config, layout_msx_en);
}

/* MSX - Philips VG-8020-00 */

ROM_START(vg802000)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("8020-00bios.rom", 0x0000, 0x8000, CRC(8205795e) SHA1(829c00c3114f25b3dae5157c0a238b52a3ac37db))
ROM_END

void msx1_state::vg802000(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4);  // 64KB RAM

	msx1(VDP_TMS9929A, SND_YM2149, config, layout_msx_nocode);
}

/* MSX - Philips VG-8020/19 / VG-8020F */

ROM_START(vg8020f)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("8020-19bios.u11", 0x0000, 0x8000, CRC(70ce2d45) SHA1(ae4a6632d4456ef44603e72f5acd5bbcd6c0d124))
ROM_END

void msx1_state::vg8020f(machine_config &config)
{
	// YM2149 (in S-3527 MSX Engine)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// S-3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4);   // 64KB RAM

	msx1(VDP_TMS9929A, SND_YM2149, config, layout_msx_nocode);
}

/* MSX - Philips VG-8020/20 */

ROM_START(vg802020)
	ROM_REGION(0x8000, "mainrom", 0)
	// m38256-k5 z-4 int rev1
	ROM_LOAD("8020-20bios.u11", 0x0000, 0x8000, CRC(a317e6b4) SHA1(e998f0c441f4f1800ef44e42cd1659150206cf79))
ROM_END

void msx1_state::vg802020(machine_config &config)
{
	// Zilog Z8400APS
	// YM2149 (in S-3527 MSX Engine)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// S-3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4);   // 64KB RAM

	msx1(VDP_TMS9129, SND_YM2149, config, layout_msx_nocode);
}

/* MSX - Phonola VG-8000 (Italian market, mostly likely same as Philips VG-8000) */

/* MSX - Phonola VG-8010 (Italian market, mostly likely same as Philips VG-8010) */

/* MSX - Phonola VG-8020 (Italian market, mostly likely same as Philips VG-8020) */

/* MSX - Pioneer PX-7

|---------------------------------------|
|  CN1     CN2                          |
|                                       |
|                                       |
|  IC33                                 |---------------------------------|
|                                                      CN3                |
|   IC32   IC34            IC38  IC40                                     |
|                                                               IC20      |
|   IC15   IC18  IC43      IC8   IC35   IC6     |----IC3---|              |
|                                               |----------|    IC21      |
|   IC16   IC19  |---IC13---|    IC7    IC10                              |
|                |----------|                   IC36  IC29      ---       |
|   IC17   IC14                                     X2          | |       |
|                |--IC12---|     |----IC1-----|       IC37      |I|       |
|   IC28   IC11  |---------|     |------------|   X1            |C|       |
|                                                               |2|       |
|  |----IC4----| |----IC5----|   IC39  IC9      IC42  IC44      | |       |
|  |-----------| |-----------|                                  ---       |
|                                                                         |
|       IC45   IC31    IC30      IC41                                     |
|                                                                         |
|  CN4 CN5  CN6  CN7                                  CN8                 |
|-------------------------------------------------------------------------|

Notes:
  X1 - 3.579MHz
  X2 - 500kHz
  IC1 - Sharp LH0080A Z80A-CPU-D
  IC2 - TMS91289NL
  IC3 - MB111S112  Z10 (500kHz)
  IC4  - M5L8255AP-5
  IC5  - YM2149F
  IC6,IC7,IC8,IC10,IC45 - SN74LS367AN
  IC9 - SN74LS245N
  IC11,IC34 - SN74LS139N
  IC12 - YM2301-23908 / 53 18 85 A (might indicate a version)
  IC13 - Pioneer PD5031 2364-213 514100 (M5L2764-213)
  IC14,IC17,IC30,IC31 - SN74LS157N
  IC15-IC19 - MB81416-12
  IC20,IC21 - TMS4416-I5NL
  IC28 - SN74LS153N
  IC29 - SN74LS02N
  IC32 - SN74LS374N
  IC33 - M5218P
  IC35 - SN74LS74AN
  IC36 - SN74LS30N
  IC37-IC39 - SN74LS04N
  IC40,IC41 - SN74LS05N
  IC42 - SN74LS08N
  IC43,IC44 - SN74LS32N
  CN1 - Printer
  CN2 - Cassette recorder
  CN3 - Expansion slot
  CN4 - Keyboard
  CN5 - Keyboard
  CN6 - Controller #1
  CN7 - Controller #2
  CN8 - Expansion slot

 */

// BIOS is for an international keyboard while the machine has a Japanese layout
ROM_START(piopx7)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("ym2301.ic12", 0x0000, 0x8000, BAD_DUMP CRC(e9ccd789) SHA1(8963fc041975f31dc2ab1019cfdd4967999de53e))

	ROM_REGION(0x4000, "pbasic", 0)
	ROM_LOAD("pd5031.ic13", 0x0000, 0x2000, CRC(a5d0c0b9) SHA1(665d805f96616e1037f1823050657b7849899283)) // v1.0
	ROM_FILL(0x2000, 0x2000, 0x6e)
ROM_END

void msx1_state::piopx7(machine_config &config)
{
	// TMS9928ANL VDP with sync/overlay interface
	// AY-3-8910 PSG
	// Pioneer System Remote (SR) system control interface
	// FDC: None, 0 drives
	// 2 Cartridge slots

	// Line-level stereo audio input can be mixed with sound output, balance controlled with slider on front panel
	// Front-panel switch allows audio input to be passed through bypassing the mixing circuit
	// Line input can be muted under software control, e.g. when loading data from Laserdisc
	// Right channel of line input is additionally routed via some signal processing to the cassette input for loading data from Laserdisc

	// PSG port B bits 0-5 can be used to drive controller pins 1-6, 1-7, 2-6, 2-7, 1-8 and 2-8 low if 0 is written

	// Slot #2 7FFE is the SR control register LCON
	// Bit 7 R = /ACK (significant with acknowledge 1->0 with respect to remote control signal transmission)
	// Bit 0 R = RMCLK (clock produced by dividing CLK1/CLK2 frequency by 128)
	// Bit 0 W = /REM (high output with bit serial data output generated in synchronisation with RMCLK)

	// Slot #2 7FFF is the video overlay control register VCON
	// Bit 7 R = /EXTV (low when external video input available; high when not available)
	// Bit 7 W = Mute (line input signal muting)
	// Bit 0 R = INTEXV (interrupt available when external video signal OFF, reset on read)
	// Bit 0 W = /OVERLAY (0 = superimpose, 1 = non-superimpose)

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 2, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_ROM, "pbasic", 2, 1, 1, "pbasic");
	add_cartridge_slot<2>(config, 3);

	msx1(VDP_TMS9928A, SND_AY8910, config, layout_msx_jp);
}

/* MSX - Pioneer PX-7UK */

ROM_START(piopx7uk)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("px7ukbios.rom",   0x0000, 0x8000, CRC(e9ccd789) SHA1(8963fc041975f31dc2ab1019cfdd4967999de53e))

	ROM_REGION(0x4000, "pbasic", 0)
	ROM_LOAD("px7ukpbasic.rom", 0x0000, 0x2000, CRC(91e0df72) SHA1(4f0102cdc27216fd9bcdb9663db728d2ccd8ca6d)) // v1,1
	ROM_FILL(0x2000, 0x2000, 0x6e)

// Is this a cartridge that came with the machine?
// ROM_REGION(0x8000, "videoart", 0)
// ROM_LOAD("videoart.rom", 0x0000, 0x8000, CRC(0ba148dc) SHA1(b7b4e4cd40a856bb071976e6cf0f5e546fc86a78))
ROM_END

void msx1_state::piopx7uk(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 2, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_ROM, "pbasic", 2, 1, 1, "pbasic");
	add_cartridge_slot<2>(config, 3);

	msx1(VDP_TMS9129, SND_YM2149, config, layout_msx_nocode);
}

/* MSX - Pioneer PX-V60 */

ROM_START(piopxv60)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("pxv60bios.rom",   0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))

	ROM_REGION(0x4000, "pbasic", 0)
	ROM_LOAD("pd5031.rom", 0x0000, 0x2000, CRC(91e0df72) SHA1(4f0102cdc27216fd9bcdb9663db728d2ccd8ca6d)) // v1.1
	ROM_FILL(0x2000, 0x2000, 0x6E)
ROM_END

void msx1_state::piopxv60(machine_config &config)
{
	// Sharp LH0080A
	// YM2149 in S3527
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 2, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_ROM, "pbasic", 2, 1, 1, "pbasic");
	add_cartridge_slot<2>(config, 3);

	msx1(VDP_TMS9128, SND_YM2149, config, layout_msx_jp);
}

/* MSX - Pioneer PX-V7 */

/* MSX - Radiola MK 180 */

/* MSX - Sakhr AH-200 */

/* MSX - Sakhr AX-100 */

/* MSX - Sakhr AX-150 */

ROM_START(ax150)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("ax150bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(bd95c436) SHA1(5e094fca95ab8e91873ee372a3f1239b9a48a48d)) // need verification

	ROM_REGION(0x8000, "arabic", 0)
	ROM_LOAD("ax150arab.rom", 0x0000, 0x8000, BAD_DUMP CRC(339cd1aa) SHA1(0287b2ec897b9196788cd9f10c99e1487d7adbbb)) // need verification
ROM_END

void msx1_state::ax150(machine_config &config)
{
	// YM2149 in S3527
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// YM2220 (compatible with TMS9918)
	// MSX-Engine S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram1", 0, 0, 2, 2); // 32KB RAM
	add_internal_slot(config, MSX_SLOT_ROM, "arabic", 0, 1, 1, 2, "arabic");
	add_internal_slot(config, MSX_SLOT_RAM, "ram2", 0, 2, 0, 2); // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);

	msx1(VDP_TMS9918, SND_YM2149, config, layout_msx_ar);
}

/* MSX - Sakhr AX-170 */

ROM_START (ax170)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("ax170bios.rom", 0x0000, 0x8000, CRC(bd95c436) SHA1(5e094fca95ab8e91873ee372a3f1239b9a48a48d))

	ROM_REGION(0x8000, "arabic", 0)
	ROM_LOAD("ax170arab.rom", 0x0000, 0x8000, CRC(339cd1aa) SHA1(0287b2ec897b9196788cd9f10c99e1487d7adbbb))
ROM_END

void msx1_state::ax170(machine_config &config)
{
	// AY-3-8910 in T7937
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// T6950 in T7937
	// T7937 (in ax170mk2)

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "arabic", 1, 1, 2, "arabic");
	add_cartridge_slot<1>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 0, 4); // 64KB RAM
	add_cartridge_slot<2>(config, 3, 3);

	msx1(VDP_TMS9929A, SND_AY8910, config, layout_msx_ar);
}

/* MSX - Sakhr AX-170F */

/* MSX - Sakhr AX-230 */

ROM_START (ax230)
	ROM_REGION(0x20000, "mainrom", 0)
	ROM_LOAD("qxxca0259.ic125", 0x0000, 0x20000, CRC(f1a3e650) SHA1(0340707c5de2310dcf5e569b7db4c6a6a5590cb7))

	ROM_REGION(0x100000, "games", 0)
	ROM_LOAD("qxxca0270.ic127", 0x00000, 0x100000, CRC(103c11c4) SHA1(620a209bdfdb65a22380031fce654bd1df61def2))
ROM_END

void msx1_state::ax230(machine_config &config)
{
	// AY-3-8910 in T7937
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// T6950 in T7937
	// T7937

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	// TODO: is and if so, how is the rest of ic125 accessed?
	add_internal_slot(config, MSX_SLOT_ROM, "arabic1", 1, 1, 1, "mainrom", 0xc000);
	add_internal_slot(config, MSX_SLOT_ROM, "arabic2", 1, 2, 1, "mainrom", 0x8000);
	add_cartridge_slot<1>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 0, 4); // 64KB RAM
	add_internal_slot(config, MSX_SLOT_AX230, "games", 3, 3, 1, 2, "games");

	msx1(VDP_TMS9918, SND_AY8910, config, layout_msx_ar);
}

/* MSX - Sakhr AX-330 */

/* MSX - Sakhr AX-660 */

/* MSX - Sakhr AX-990 */

/* MSX - Salora MSX (prototypes) */

/* MSX - Samsung SPC-800 */

ROM_START(spc800)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("spc800bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(3ab0cd3b) SHA1(171b587bd5a947a13f3114120b6e7baca3b57d78)) // need verification

	ROM_REGION(0x4000, "hangul", 0)
	ROM_LOAD("spc800han.rom",  0x0000, 0x4000, BAD_DUMP CRC(5ae2b013) SHA1(1e7616261a203580c1044205ad8766d104f1d874)) // need verification
ROM_END

void msx1_state::spc800(machine_config &config)
{
	// AY-3-8910
	// FDC: None, 0 drives
	// 1 Cartridge slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "hangul", 0, 2, 1, "hangul");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4);  // 64KB?? RAM
	// Expansion slot in slot #3

	msx1(VDP_TMS9118, SND_AY8910, config, layout_msx_kr);
}

/* MSX - Sanno PHC-SPC */

/* MSX - Sanno SPCmk-II */

/* MSX - Sanno SPCmk-III */

/* MSX - Sanyo MPC-1 / Wavy1 */

/* MSX - Sanyo MPC-10 / Wavy10 */

/* MSX - Sanyo MPC-64 */

ROM_START(mpc64)
	ROM_REGION(0x8000, "mainrom", 0)
	// hn613256p t22 5c1 ?
	ROM_LOAD("mpc64bios.ic111", 0x0000, 0x8000, BAD_DUMP CRC(d6e704ad) SHA1(d67be6d7d56d7229418f4e122f2ec27990db7d19)) // needs verification
ROM_END

void msx1_state::mpc64(machine_config &config)
{
	// AY8910
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4);  // 64KB RAM

	msx1(VDP_TMS9928A, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Sanyo MPC-100 */

ROM_START(mpc100)
	ROM_REGION(0x8000, "mainrom", 0)
	// hn613256p 4j1 ?
	ROM_LOAD("mpc100bios.ic122", 0x0000, 0x8000, CRC(e9ccd789) SHA1(8963fc041975f31dc2ab1019cfdd4967999de53e))
ROM_END

void msx1_state::mpc100(machine_config &config)
{
	// AY8910
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4);  // 64KB RAM

	msx1(VDP_TMS9929A, SND_AY8910, config, layout_msx_nocode_nocaps);
}

/* MSX - Sanyo MPC-200 */

ROM_START(mpc200)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("mpc200bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(e9ccd789) SHA1(8963fc041975f31dc2ab1019cfdd4967999de53e)) // need verification
ROM_END

void msx1_state::mpc200(machine_config &config)
{
	// AY8910
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// T6950
	// T7775 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "bios", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4); // 64KB RAM

	msx1(VDP_TMS9929A, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Sanyo MPC-200SP (same as Sanyo MPC-200 ?) */

ROM_START(mpc200sp)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("mpcsp200bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(bcd79900) SHA1(fc8c2b69351e60dc902add232032c2d69f00e41e)) // need verification
ROM_END

void msx1_state::mpc200sp(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: None, 0 drives
	// 2? Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4); // 64KB RAM

	msx1(VDP_TMS9929A, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Sanyo PHC-28L */

ROM_START(phc28l)
	ROM_REGION(0x8000, "mainrom", 0)
	// 5a1 hn613256p g42
	ROM_LOAD("28lbios.ic20", 0x0000, 0x8000, CRC(d2110d66) SHA1(d3af963e2529662eae63f04a2530454685a1989f))
ROM_END

void msx1_state::phc28l(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4);  // 64KB RAM

	msx1(VDP_TMS9929A, SND_YM2149, config, layout_msx_nocode);
}

/* MSX - Sanyo PHC-28S */

ROM_START(phc28s)
	ROM_REGION(0x8000, "mainrom", 0)
	// 4l1 hn613256p m74
	ROM_LOAD("28sbios.ic2", 0x0000, 0x8000, CRC(e5cf6b3c) SHA1(b1cce60ef61c058f5e42ef7ac635018d1a431168))
ROM_END

void msx1_state::phc28s(machine_config &config)
{
	// 10.738MHz and 3.574545MHz
	// AY8910
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 2, 2);   // 32KB RAM

	msx1(VDP_TMS9929A, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Sanyo Wavy MPC-10 */

ROM_START(mpc10)
	ROM_REGION(0x8000, "mainrom", 0)
	// Split across 2 roms? (image hard to read)
	// ic14? hn613256p d24 ?
	// ic15? hn613256p d25 ?
	ROM_LOAD("mpc10.rom", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))
ROM_END

void msx1_state::mpc10(machine_config &config)
{
	// AY8910
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Expansion slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	// Expansion slot in slot #2
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 2, 2);   // 32KB RAM

	msx1(VDP_TMS9918, SND_AY8910, config, layout_msx_jp);
}

/* MSX - Schneider MC 810 */

/* MSX - Sharp Epcom HB-8000 (HotBit) */

ROM_START(hb8000)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_SYSTEM_BIOS(0, "v12", "v1.2")
	ROMX_LOAD("hotbit12.ic28", 0x0000, 0x8000, CRC(f59a4a0c) SHA1(9425815446d468058705bae545ffa13646744a87), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v11", "v1.1")
	ROMX_LOAD("hotbit11.ic28", 0x0000, 0x8000, CRC(b6942694) SHA1(663f8c512d04d213fa616b0db5eefe3774012a4b), ROM_BIOS(1))
	// v1.0 missing
ROM_END

void msx1_state::hb8000(machine_config &config)
{
	// AY8910 and YM2149
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4);  // 64KB RAM

	msx1(VDP_TMS9128, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Sharp Epcom HB-8000 (HotBit 1.3b) */

ROM_START(hotbi13b)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hotbit13b.rom", 0x0000, 0x8000, BAD_DUMP CRC(7a19820e) SHA1(e0c2bfb078562d15acabc5831020a2370ea87052)) // need verification
ROM_END

void msx1_state::hotbi13b(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: None, 0 drives
	// 2 Cartridge slots?

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 4).set_total_size(0x10000);   // 64KB Mapper RAM

	msx1(VDP_TMS9928A, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Sharp Epcom HB-8000 (HotBit 1.3p) */

ROM_START(hotbi13p)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hotbit13p.rom", 0x0000, 0x8000, BAD_DUMP CRC(150e239c) SHA1(942f9507d206cd8156f15601fe8032fcf0e3875b)) // need verification
ROM_END

void msx1_state::hotbi13p(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: None, 0 drives
	// 2 Cartridge slots?

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 4).set_total_size(0x10000);   // 64KB Mapper RAM

	msx1(VDP_TMS9928A, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Sincorp SBX (Argentina, homebrew) */

/* MSX - Sony HB-10 */

ROM_START(hb10)
	ROM_REGION(0x8000, "mainrom", 0)
	// 5h3 hn613256p c78
	// 3l1 hn613256p c78 ?
	ROM_LOAD("hb10bios.ic12", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))
ROM_END

void msx1_state::hb10(machine_config &config)
{
	// YM2149 (in S-S3527 MSX-Engine)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// S3527 MSX-Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 3, 1);  // 16KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);

	msx1(VDP_TMS9118, SND_YM2149, config, layout_msx_jp);
}

/* MSX - Sony HB-10B */

/* MSX - Sony HB-10D */
// ic12 - tmm23256p

/* MSX - Sony HB-10P */

ROM_START(hb10p)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("10pbios.ic12", 0x0000, 0x8000, CRC(0f488dd8) SHA1(5e7c8eab238712d1e18b0219c0f4d4dae180420d))
ROM_END

void msx1_state::hb10p(machine_config &config)
{
	// XTAL: 3.579545 + 22.168(VDP)
	// YM2149 (in S3527 MSX-Engine)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// T6950

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	// A mirror of RAM appears in slot #0, page #3
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4);  // 64KB RAM

	msx1(VDP_TMS9929A, SND_YM2149, config, layout_msx_nocode);
}

/* MSX - Sony HB-20P */

ROM_START(hb20p)
	ROM_REGION(0x8000, "mainrom", 0)
	// lh2359z3
	ROM_LOAD("20pbios.ic12", 0x0000, 0x8000, CRC(15ddeb5c) SHA1(63050d2d21214a721cc55f152c22b7be8061ac33))
ROM_END

void msx1_state::hb20p(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// T6950A

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	// A mirror of RAM appears in slot #0, page #3
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4);  // 64KB RAM

	msx1(VDP_TMS9929A, SND_YM2149, config, layout_msx_nocode);
}

/* MSX - Sony HB-201 */

ROM_START(hb201)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("201bios.ic9", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))

	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("201note.ic8", 0x0000, 0x4000, CRC(74567244) SHA1(0f4f09f1a6ef7535b243afabfb44a3a0eb0498d9))
ROM_END

void msx1_state::hb201(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 0, 2, 1, "firmware");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4);  // 64KB RAM

	msx1(VDP_TMS9118, SND_YM2149, config, layout_msx_jp);
}

/* MSX - Sony HB-201P */

ROM_START(hb201p)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("201pbios.rom.ic9", 0x0000, 0x8000, CRC(0f488dd8) SHA1(5e7c8eab238712d1e18b0219c0f4d4dae180420d))

	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("201pnote.rom.ic8", 0x0000, 0x4000, CRC(1ff9b6ec) SHA1(e84d3ec7a595ee36b50e979683c84105c1871857))
ROM_END

void msx1_state::hb201p(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 0, 2, 1, "firmware");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4);  // 64KB RAM

	msx1(VDP_TMS9129, SND_YM2149, config, layout_msx_nocode);
}

/* MSX - Sony HB-501F */
// ic1 - tmm23256p
// YM2149
// TMS9129
// S3527

/* MSX - Sony HB-501P */

ROM_START(hb501p)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("501pbios.rom", 0x0000, 0x8000, CRC(0f488dd8) SHA1(5e7c8eab238712d1e18b0219c0f4d4dae180420d))
ROM_END

void msx1_state::hb501p(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// T6950
	// S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4);  // 64KB RAM

	msx1(VDP_TMS9929A, SND_YM2149, config, layout_msx_nocode);
}

/* MSX - Sony HB-55 (Version 1) */

ROM_START(hb55)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hb55bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821)) // need verification

	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("hb55note.rom", 0x0000, 0x2000, BAD_DUMP CRC(5743ab55) SHA1(b9179db93608c4da649532e704f072e0a3ea1b22)) // need verification
ROM_END

void msx1_state::hb55(machine_config &config)
{
	// AY8910
	// FDC: None, 0 drives
	// 1 Cartridge slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 0, 2, 1, "firmware");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 3, 1);   // 16KB RAM
	add_cartridge_slot<1>(config, 1);
	// Expansion slot in slot #3

	msx1(VDP_TMS9918A, SND_AY8910, config, layout_msx_jp);
}

/* MSX - Sony HB-55D, is this HB-55 2nd version? */

ROM_START(hb55d)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("55dbios.rom", 0x0000, 0x8000, BAD_DUMP CRC(7e2b32dd) SHA1(38a645febd0e0fe86d594f27c2d14be995acc730)) // need verification

	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("55dnote.rom", 0x0000, 0x4000, BAD_DUMP CRC(8aae0494) SHA1(97ce59892573cac3c440efff6d74c8a1c29a5ad3)) // need verification
ROM_END

void msx1_state::hb55d(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: None, 0 drives
	// 2 Cartridge slots?

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 0, 2, 1, "firmware");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 3, 1);   // 16KB RAM
	add_cartridge_slot<2>(config, 3);

	msx1(VDP_TMS9929A, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Sony HB-55P */

ROM_START(hb55p)
	ROM_REGION(0x8000, "mainrom", 0)
	// Are there machines with ic42 and ic43 populated like this?
	// Image on msx.org shows only ic42 and ic44 populated (for hb-55)
//  ROM_LOAD("55pbios.ic42", 0x0000, 0x4000, CRC(24c198be) SHA1(7f8c94cb8913db32a696dec80ffc78e46693f1b7))
//  ROM_LOAD("55pbios.ic43", 0x4000, 0x4000, CRC(e516e7e5) SHA1(05fedd4b9bfcf4949020c79d32c4c3f03a54fb62))
	ROM_LOAD("55pbios.ic42", 0x0000, 0x8000, CRC(e9ccd789) SHA1(8963fc041975f31dc2ab1019cfdd4967999de53e))

	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("55pnote.ic44", 0x0000, 0x4000, CRC(492b12f8) SHA1(b262aedc71b445303f84efe5e865cbb71fd7d952))
ROM_END

void msx1_state::hb55p(machine_config &config)
{
	// AY8910
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 0, 2, 1, "firmware");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 3, 1);   // 16KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 3);

	msx1(VDP_TMS9929A, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Sony HB-75 */

ROM_START(hb75)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("75bios.rom", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))

	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("75note.rom", 0x0000, 0x4000, CRC(2433dd0b) SHA1(5f26319aec3354a94e2a98e07b2c70046bc45417))
ROM_END

void msx1_state::hb75(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: None, 0 drives
	// 1 Cartridge slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 0, 2, 1, "firmware");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4);  // 64KB RAM
	// Expansion slot in slot #3

	msx1(VDP_TMS9918A, SND_AY8910, config, layout_msx_jp);
}

/* MSX - Sony HB-75AS */

/* MSX - Sony HB-75B */

/* MSX - Sony HB-75D */

ROM_START(hb75d)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("75dbios.rom", 0x0000, 0x8000, CRC(7e2b32dd) SHA1(38a645febd0e0fe86d594f27c2d14be995acc730))

	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("75dnote.rom", 0x0000, 0x4000, CRC(8aae0494) SHA1(97ce59892573cac3c440efff6d74c8a1c29a5ad3))
ROM_END

void msx1_state::hb75d(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 0, 2, 1, "firmware");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4);  // 64KB RAM
	add_cartridge_slot<2>(config, 3);

	msx1(VDP_TMS9929A, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Sony HB-75F */

/* MSX - Sony HB-75P */

ROM_START(hb75p)
	ROM_REGION(0x8000, "mainrom", 0)
	// Are there machines with ic42 and ic43 populated like this?
	// HB-75P internal image on msx.org only has 2 roms populated (ic42 and ic44)
//  ROM_LOAD("75pbios.ic42", 0x0000, 0x4000, CRC(24c198be) SHA1(7f8c94cb8913db32a696dec80ffc78e46693f1b7))
//  ROM_LOAD("75pbios.ic43", 0x4000, 0x4000, CRC(e516e7e5) SHA1(05fedd4b9bfcf4949020c79d32c4c3f03a54fb62))
	ROM_LOAD("75pbios.ic42", 0x0000, 0x8000, CRC(e9ccd789) SHA1(8963fc041975f31dc2ab1019cfdd4967999de53e))

	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("75pnote.ic44", 0x0000, 0x4000, CRC(492b12f8) SHA1(b262aedc71b445303f84efe5e865cbb71fd7d952))
ROM_END

void msx1_state::hb75p(machine_config &config)
{
	// AY8910
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 0, 2, 1, "firmware");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4);  // 64KB RAM
	add_cartridge_slot<2>(config, 3);

	msx1(VDP_TMS9929A, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Sony HB-101 */

ROM_START(hb101)
	ROM_REGION(0x8000, "mainrom", 0)
	// 4l1 hn613256p d78
	ROM_LOAD("101pbios.ic108", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))

	ROM_REGION(0x4000, "firmware", 0)
	// m38128a-91 4202
	ROM_LOAD("101pnote.ic111", 0x0000, 0x4000, CRC(f62e75f6) SHA1(64adb7fcf9b86f59d8658badb02f58e61bb15712))
ROM_END

void msx1_state::hb101(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 3, 1);   // 16KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 3, 1, 1, "firmware");

	msx1(VDP_TMS9118, SND_YM2149, config, layout_msx_jp);
}

/* MSX - Sony HB-101P */

ROM_START(hb101p)
	ROM_REGION(0x8000, "mainrom", 0)
	// m38256-7b 5411
	ROM_LOAD("101pbios.ic9", 0x0000, 0x8000, CRC(0f488dd8) SHA1(5e7c8eab238712d1e18b0219c0f4d4dae180420d))

	ROM_REGION(0x4000, "firmware", 0)
	// m38128a-f6 5501
	ROM_LOAD("101pnote.ic8", 0x0000, 0x4000, CRC(525017c2) SHA1(8ffc24677fd9d2606a79718764261cdf02434f0a))
ROM_END

void msx1_state::hb101p(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 2, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 3, 1, 1, "firmware");

	msx1(VDP_TMS9929A, SND_YM2149, config, layout_msx_nocode);
}

/* MSX - Sony HB-701 */

/* MSX - Sony HB-701FD */

ROM_START(hb701fd)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hb701fdbios.rom", 0x0000, 0x8000, BAD_DUMP CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	// FDC register contents at 3ff8-3fff
	ROM_LOAD("hb701fddisk.rom", 0x0000, 0x4000, BAD_DUMP CRC(71961d9d) SHA1(2144036d6573d666143e890e5413956bfe8f66c5)) // need verification
ROM_END

void msx1_state::hb701fd(machine_config &config)
{
	// YM2149
	// FDC: WD2793, 1 3.5" SSDD drive
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 0, 4);  // 64KB RAM
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_SS, "disk", 3, 1, 1, 2, "diskrom");

	msx1(VDP_TMS9928A, SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX - Spectravideo SVI-728 */

ROM_START(svi728)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("728bios.rom", 0x0000, 0x8000, CRC(1ce9246c) SHA1(ea6a82cf8c6e65eb30b98755c8577cde8d9186c0))
ROM_END

void msx1_state::svi728(machine_config &config)
{
	// AY8910
	// FDC: None, 0 drives
	// 1 Cartridge slots, 1 Expansion slot (eg for SVI-707)

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "mainmirror", 0, 2, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 1, 0, 4);  // 64KB RAM
	add_cartridge_slot<1>(config, 2);
	// Expansion slot (for eg SVI-707) in slot #3

	msx1(VDP_TMS9129, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Spectravideo SVI-728 (Arabic) */

/* MSX - Spectravideo SVI-728 (Danish/Norwegian) */

/* MSX - Spectravideo SVI-728 (Spanish) */

ROM_START(svi728es)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("728esbios.rom", 0x0000, 0x8000, CRC(76c5e381) SHA1(82415ee031721d1954bfa42e1c6dd79d71c692d6))
ROM_END

/* MSX - Spectravideo SVI-728 (Swedish/Finnish) */

/* MSX - Talent DPC-200 */

// Spanish keyboard
ROM_START(tadpc200)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("dpc200bios.rom", 0x0000, 0x8000, CRC(01ad4fab) SHA1(d5bf4814ea694481c8badbb8de8d56a08ee03cc0))
ROM_END

// International keyboard
ROM_START(tadpc200b)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("dpc200altbios.rom", 0x0000, 0x8000, BAD_DUMP CRC(8205795e) SHA1(829c00c3114f25b3dae5157c0a238b52a3ac37db)) // need verification
ROM_END

void msx1_state::tadpc200(machine_config &config)
{
	// AY8910
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// DW64MX1

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 1, 0, 4);  // 64KB RAM
	add_cartridge_slot<1>(config, 2);
	// Expansion slot in slot #3

	msx1(VDP_TMS9129, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Talent DPS-201 */

/* MSX - Toshiba HX-10 / HX-10P
Code on PCB: MSX TUK
        |---------------------------|-------------------|-------------|
        |   CN1  CN2  CN3  CN4               CN5                      |
        |                        |---------------------------|        |
        |                        |---------------------------|        |
        |                                    CN6                      |
        |                        IC40                                 |
        |                                                         CN7 |
        |                      IC38     IC32     IC33     IC37        |
        |                                                             |
        |                      Q2       IC31     IC34     IC35        |
        |    Q1                                                   CN8 |
        |                                                 IC39        |
        |   |--IC15------| |--IC2----|   |----IC1-----|               |
        |   |------------| |---------|   |------------|               |
        |                                                 IC30        |
        |                 IC3    IC4                              CN9 |
        |                               |-----IC15-------|            |
        |  IC17   IC18    IC7    IC8    |----------------|            |
        |                                                 IC27        |
        |  IC19   IC20    IC9    IC10   |----IC25------|              |
|----|  |                               |--------------|  IC26        |
| Q  |  |  IC21   IC22    IC11   IC12                                 |
|    |  |                                                             |
| S  |  |  IC23   IC24    IC13   IC14    IC29             IC28        |
|  L |  |                                                             |
|    |  |                                 CN11   CN10                 |
|----|  |-------------------------------------------------------------|

Notes:
  Mainboard components:
   IC1               - Sharp LH0080A Z80A-CPU-D
   IC2               - MB83256
   IC3,IC4,IC27,IC28 - Texas Instruments SN74LS157N
   IC7-IC14          - HM4864AP
   IC15              - Toshiba TCX-1007 (64pin custom chip)
   IC16              - 40pin chip covered with some kind of heatsink(?), probably TMS9929A
   IC17-IC24         - 4116-3
   IC25              - AY-3-8910A
   IC26              - SN74LS09N
   IC29              - HD74LS145P
   IC30-IC34         - M74LS367AP
   IC35              - MB74LS74A
   IC37              - HD74LS373P
   IC38              - Toshiba TC74HCU04P
   IC39              - HD74LS08P
   IC40              - TA75559P
   Q1                - 10687.5
   Q2                - 3579545
   CN1               - Cassette connector
   CN2               - RF connector
   CN3               - Audio connector
   CN4               - Video connector
   CN5               - Expansion connector
   CN6               - Cartridge connector
   CN7               - Printer connector
   CN8               - Joystick 2 connector
   CN9               - Joystick 1 connector
   CN10              - Keyboard connector 1
   CN11              - Keyboard connector 2

  Extra pcb (video related?) components::
   Q - 4.433619
   S - 74LS04
   L - LVA510
 */

ROM_START(hx10)
	ROM_REGION(0x8000, "mainrom", 0)
	// tmm23256p
	ROM_LOAD("hx10bios.ic2", 0x0000, 0x8000, CRC(5486b711) SHA1(4dad9de7c28b452351cc12910849b51bd9a37ab3))
ROM_END

void msx1_state::hx10(machine_config &config)
{
	// AY8910
	// FDC: None, 0 drives
	// 1 Cartridge slot, 1 Toshiba Expension slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4);  // 64KB RAM
	// Expansion slot in slot #3

	msx1(VDP_TMS9929A, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Toshiba HX-10AA */

/* MSX - Toshiba HX-10D */

ROM_START(hx10d)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hx10dbios.ic5", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))
ROM_END

void msx1_state::hx10d(machine_config &config)
{
	// AY8910
	// FDC: None, 0 drives
	// 1 Cartridge slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4);   // 64KB RAM
	// Expansion slot in slot #3

	m_hw_def.has_printer_port(false);
	msx1(VDP_TMS9918A, SND_AY8910, config, layout_msx_jp);
}

/* MSX - Toshiba HX-10DP */

ROM_START(hx10dp)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hx10dpbios.ic2", 0x0000, 0x8000, BAD_DUMP CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821)) // need verification
ROM_END

void msx1_state::hx10dp(machine_config &config)
{
	// AY8910?
	// FDC: None, 0 drives
	// 1 Cartridge slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4);   // 64KB RAM
	// Expansion slot in slot #3

	msx1(VDP_TMS9918A, SND_AY8910, config, layout_msx_jp);
}

/* MSX - Toshiba HX-10DPN */

/* MSX - Toshiba HX-10E */

ROM_START(hx10e)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hx10ebios.rom", 0x0000, 0x8000, BAD_DUMP CRC(5486b711) SHA1(4dad9de7c28b452351cc12910849b51bd9a37ab3)) // need verification
ROM_END

void msx1_state::hx10e(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: None, 0 drives
	// 1 Cartridge slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4);   // 64KB RAM
	// Expansion slot in slot #3

	msx1(VDP_TMS9929A, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Toshiba HX-10F */

ROM_START(hx10f)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hx10fbios.rom", 0x0000, 0x8000, BAD_DUMP CRC(e0e894b7) SHA1(d99eebded5db5fce1e072d08e642c0909bc7efdd)) // need verification
ROM_END

void msx1_state::hx10f(machine_config &config)
{
	// AY8910?
	// FDC: None, 0 drives
	// 1 Cartridge slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4);   // 64KB RAM
	// Expansion slot in slot #3

	msx1(VDP_TMS9929A, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Toshiba HX-10I */

/* MSX - Toshiba HX-10S */

// BIOS is for an international keyboard but the machine had a japanese keyboard layout so that cannot be correct
ROM_START(hx10s)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hx10sbios.rom", 0x0000, 0x8000, BAD_DUMP CRC(5486b711) SHA1(4dad9de7c28b452351cc12910849b51bd9a37ab3)) // need verification
ROM_END

void msx1_state::hx10s(machine_config &config)
{
	// AY8910?
	// FDC: None, 0 drives
	// 1 Cartridge slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 3, 1);   // 16KB RAM
	add_cartridge_slot<1>(config, 1);
	// Expansion slot in slot #3

	m_hw_def.has_printer_port(false);
	msx1(VDP_TMS9918A, SND_AY8910, config, layout_msx_jp);
}

/* MSX - Toshiba HX-10SA */

ROM_START(hx10sa)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hx10sabios.rom", 0x0000, 0x8000, BAD_DUMP CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821)) // need verification
ROM_END

void msx1_state::hx10sa(machine_config &config)
{
	// AY8910?
	// FDC: None, 0 drives
	// 1 Cartridge slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 3, 1);   // 16KB RAM
	add_cartridge_slot<1>(config, 1);
	// Expansion slot in slot #3

	m_hw_def.has_printer_port(false);
	msx1(VDP_TMS9918A, SND_AY8910, config, layout_msx_jp);
}

/* MSX - Toshiba HX-10SF */

/* MSX - Toshiba HX-20 */

// BIOS is for an internation keyboard but the machine had japanese keyboard layout
// Firmware is in English
ROM_START(hx20)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hx20bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(8205795e) SHA1(829c00c3114f25b3dae5157c0a238b52a3ac37db)) // need verification

	ROM_REGION(0x8000, "firmware", 0)
	ROM_LOAD("hx20word.rom", 0x0000, 0x8000, BAD_DUMP CRC(39b3e1c0) SHA1(9f7cfa932bd7dfd0d9ecaadc51655fb557c2e125)) // need verification
ROM_END

void msx1_state::hx20(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// HX-R701 RS-232 optional
	// T6950

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram1", 0, 2, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram2", 3, 0, 0, 2);   // 32KB RAM
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 3, 3, 1, 2, "firmware");

	msx1(VDP_TMS9129, SND_YM2149, config, layout_msx_jp);
}

/* MSX - Toshiba HX-20AR */
// TMS9128

/* MSX - Toahiba HX-20E */

ROM_START(hx20e)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("tmm23256p_2023.ic2", 0x0000, 0x8000, CRC(8205795e) SHA1(829c00c3114f25b3dae5157c0a238b52a3ac37db))

	ROM_REGION(0x8000, "firmware", 0)
	ROM_LOAD("tc53257p_2047.ic3", 0x0000, 0x8000, CRC(39b3e1c0) SHA1(9f7cfa932bd7dfd0d9ecaadc51655fb557c2e125))
ROM_END

void msx1_state::hx20e(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// T6950

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram1", 0, 2, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram2", 3, 0, 0, 2);   // 32KB RAM
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 3, 3, 1, 2, "firmware");

	msx1(VDP_TMS9129, SND_YM2149, config, layout_msx_nocode);
}

/* MSX - Toshiba HX-20I */

ROM_START(hx20i)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hx20ibios.rom", 0x0000, 0x8000, BAD_DUMP CRC(8205795e) SHA1(829c00c3114f25b3dae5157c0a238b52a3ac37db)) // need verification

	ROM_REGION(0x8000, "firmware", 0)
	ROM_LOAD("hx20iword.rom", 0x0000, 0x8000, BAD_DUMP CRC(39b3e1c0) SHA1(9f7cfa932bd7dfd0d9ecaadc51655fb557c2e125)) // need verification
ROM_END

void msx1_state::hx20i(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// T6950

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram1", 0, 2, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram2", 3, 0, 0, 2);   // 32KB RAM
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 3, 3, 1, 2, "firmware");

	msx1(VDP_TMS9129, SND_YM2149, config, layout_msx_nocode);
}

/* MSX - Toshiba HX-21 */

ROM_START(hx21)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("tmm23256p_2011.ic2", 0x0000, 0x8000, CRC(83ba6fde) SHA1(01600d06d83072d4e757b29728555efde2c79705))

	ROM_REGION(0x8000, "firmware", 0)
	ROM_LOAD("tmm23256p_2014.ic3", 0x0000, 0x8000, CRC(87508e78) SHA1(4e2ec9c0294a18a3ab463f182f9333d2af68cdca))
ROM_END

void msx1_state::hx21(machine_config &config)
{
	// AY8910
	// FDC: None, 0 drives
	// HX-R701 RS-232 optional
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram2", 3, 0, 0, 4);   // 64KB RAM
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 3, 3, 1, 2, "firmware");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware_mirror1", 3, 3, 0, 1, "firmware");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware_mirror2", 3, 3, 3, 1, "firmware", 0x4000);

	msx1(VDP_TMS9928A, SND_AY8910, config, layout_msx_jp);
}

/* MSX - Toshiba HX-21F */

ROM_START(hx21f)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hx21fbios.rom", 0x0000, 0x8000, BAD_DUMP CRC(8205795e) SHA1(829c00c3114f25b3dae5157c0a238b52a3ac37db)) // need verification

	ROM_REGION(0x8000, "firmware", 0)
	ROM_LOAD("hx21fword.rom", 0x0000, 0x8000, BAD_DUMP CRC(f9e29c66) SHA1(3289336b2c12161fd926a7e5ce865770ae7038af)) // need verification
ROM_END

void msx1_state::hx21f(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram1", 0, 2, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram2", 3, 0, 2, 2);   // 32KB RAM
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 3, 3, 1, 2, "firmware");

	msx1(VDP_TMS9929A, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Toshiba HX-22 */

ROM_START(hx22)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("tms23256p_2011.ic2", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))

	ROM_REGION(0x8000, "firmware", 0)
	ROM_LOAD("tms23256p_2014.ic3", 0x0000, 0x8000, CRC(87508e78) SHA1(4e2ec9c0294a18a3ab463f182f9333d2af68cdca))
ROM_END

void msx1_state::hx22(machine_config &config)
{
	// AY8910
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// RS232C builtin

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram2", 3, 0, 0, 4);   // 64KB RAM
	add_internal_slot_irq<3>(config, MSX_SLOT_RS232_TOSHIBA, "firmware", 3, 3, 1, 2, "firmware");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware_mirror1", 3, 3, 0, 1, "firmware");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware_mirror2", 3, 3, 3, 1, "firmware", 0x4000);

	msx1(VDP_TMS9928A, SND_AY8910, config, layout_msx_jp);
}

/* MSX - Toshiba HX-22CH */

/* MSX - Toshibe HX-22GB */

/* MSX - Toshiba HX-22I */

ROM_START(hx22i)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("tmm23256p_2023.ic2", 0x0000, 0x8000, CRC(8205795e) SHA1(829c00c3114f25b3dae5157c0a238b52a3ac37db))

	ROM_REGION(0x8000, "firmware", 0)
	ROM_LOAD("tmm23256p_2046.ic3", 0x0000, 0x8000, CRC(f9e29c66) SHA1(3289336b2c12161fd926a7e5ce865770ae7038af))
ROM_END

void msx1_state::hx22i(machine_config &config)
{
	// AY8910
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// RS232C builtin
	// Z80: LH0080A

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram1", 0, 2, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram2", 3, 0, 2, 2);   // 32KB RAM
	add_internal_slot_irq<3>(config, MSX_SLOT_RS232_TOSHIBA, "firmware", 3, 3, 1, 2, "firmware");

	msx1(VDP_TMS9929A, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Toshiba HX-30 */

/* MSX - Tosbiba HX-31 */

/* MSX - Toshiba HX-32 */

ROM_START(hx32)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hx32bios.ic3", 0x0000, 0x8000, CRC(83ba6fde) SHA1(01600d06d83072d4e757b29728555efde2c79705))

	ROM_REGION(0x8000, "firmware", 0)
	ROM_LOAD("hx32firm.ic2", 0x0000, 0x8000, CRC(efc3aca7) SHA1(ed589da7f359a4e139a23cd82d9a6a6fa3d70db0))

	// Same as HX-M200 Kanji cartridge
	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("hx32kfn.rom", 0x0000, 0x20000, CRC(d23d4d2d) SHA1(db03211b7db46899df41db2b1dfbec972109a967))
ROM_END

void msx1_state::hx32(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// HX-R701 RS-232 optional
	// T6950

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram2", 3, 0, 0, 4);   // 64KB RAM
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 3, 3, 1, 2, "firmware");

	msx1(VDP_TMS9928A, SND_YM2149, config, layout_msx_jp);
}

/* MSX - Toshiba HX-51I */

ROM_START(hx51i)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("tmm23256ad_2023.ic8", 0x0000, 0x8000, CRC(8205795e) SHA1(829c00c3114f25b3dae5157c0a238b52a3ac37db))
ROM_END

void msx1_state::hx51i(machine_config &config)
{
	// AY8910 in T7937
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// T6950 in T7937
	// T7937

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram1", 0, 2, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram2", 3, 0, 2);   // 32KB RAM

	msx1(VDP_TMS9918, SND_AY8910, config, layout_msx_nocode);
}

/* MSX - Toshiba HX-52 */

/* MSX - Triton PC64 */

/* MSX - Vestel FC-200 */

/* MSX - Victor HC-5 */

ROM_START(hc5)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hc5bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821)) // need verification
ROM_END

void msx1_state::hc5(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives,
	// 1 Cartridge slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 3, 1); // 16KB RAM
	add_cartridge_slot<1>(config, 1);
	// Expansion slot in slot #2
	// Module slot in slot #3

	msx1(VDP_TMS9928A, SND_YM2149, config, layout_msx_jp);
}

/* MSX - Victor HC-6 */

ROM_START(hc6)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hc6bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821)) // need verification
ROM_END

void msx1_state::hc6(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives,
	// 1 Cartridge slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 2, 2); // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	// Expansion slot in slot #2
	// Module slot in slot #3

	msx1(VDP_TMS9928A, SND_YM2149, config, layout_msx_jp);
}

/* MSX - Victor HC-7 */

ROM_START(hc7)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hc7bios.rom", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))

	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("hc7firm.rom", 0x0000, 0x4000, CRC(7d62951d) SHA1(e211534064ea6f436f2e7458ded35c398f17b761))
ROM_END

void msx1_state::hc7(machine_config &config)
{
	// AY8910
	// FDC: None, 0 drives,
	// 2 Cartridge slots?

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 0, 3, 1, "firmware");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4); // 64KB RAM
	add_cartridge_slot<2>(config, 3);

	msx1(VDP_TMS9928A, SND_AY8910, config, layout_msx_jp);
}

/* MSX - Victor HC-30 */

/* MSX - Victor HC-60 */

/* MSX - Wandy DPC-200 */

/* MSX - Yamaha CX5 */

/* MSX - Yamaha CX5F (with SFG01) */

ROM_START(cx5f1)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("cx5fbios.rom", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))
//  ROM_LOAD("cx5fbios.rom", 0x0000, 0x8000, CRC(dc662057) SHA1(36d77d357a5fd15af2ab266ee66e5091ba4770a3))
ROM_END

void msx1_state::cx5f1(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Yamaha expansion slot (50 pins)
	// 1 Yamaha module slot (60 pins)
	// S-5327 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 2, 2); // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	// Expansion bus in slot #2
	add_cartridge_slot<3>(config, MSX_SLOT_YAMAHA_EXPANSION, "module", 3, msx_yamaha_60pin, "sfg01");

	msx1(VDP_TMS9928A, SND_YM2149, config, layout_msx_jp);
}

/* MSX - Yamaha CX5F (with SFG05) */

ROM_START(cx5f)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("cx5fbios.rom", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))
//  ROM_LOAD("cx5fbios.rom", 0x0000, 0x8000, CRC(dc662057) SHA1(36d77d357a5fd15af2ab266ee66e5091ba4770a3))
ROM_END

void msx1_state::cx5f(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Yamaha expansion slot (50 pins)
	// 1 Yamaha module slot (60 pins)
	// S-5327 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 2, 2); // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	// Expansion bus in slot #2
	add_cartridge_slot<3>(config, MSX_SLOT_YAMAHA_EXPANSION, "module", 3, msx_yamaha_60pin, "sfg05");

	msx1(VDP_TMS9928A, SND_YM2149, config, layout_msx_jp);
}

/* MSX - Yamaha CX5MA (Australia / New Zealand */

/* MSX - Yamaha CX5MC (Canada) */

/* MSX - Yamaha CX5ME (UK) */

/* MSX - Yamaha CX5MF (France) */

/* MSX - Yamaha CX5MG (Germany) */

/* MSX - Yamaha CX5MS (Scandinavia) */

/* MSX - Yamaha CX5MU (USA) */

ROM_START(cx5mu)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("ym2301-23907.tmm23256p-2011", 0x0000, 0x8000, CRC(e2242b53) SHA1(706dd67036baeec7127e4ccd8c8db8f6ce7d0e4c))
ROM_END

void msx1_state::cx5mu(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Expansion slot (50 pins)
	// 1 Module slot (60 pins interface instead of regular 50 pin cartridge interface)

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 2, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	// Expansion slot in slot #2
	add_cartridge_slot<3>(config, MSX_SLOT_YAMAHA_EXPANSION, "module", 3, msx_yamaha_60pin, "sfg01");

	msx1(VDP_TMS9918A, SND_YM2149, config, layout_msx_nocode);
}

/* MSX - Yamaha SX-100 */

ROM_START(sx100)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("sx100bios.rom", 0x0000, 0x8000, CRC(3b08dc03) SHA1(4d0c37ad722366ac7aa3d64291c3db72884deb2d))
ROM_END

void msx1_state::sx100(machine_config &config)
{
	// YM2149 in S3527
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// YM2220, TMS9918 compatible
	// S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 2, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);

	msx1(VDP_TMS9918, SND_YM2149, config, layout_msx_jp);
}

/* MSX - Yamaha YIS-303 */

// BIOS is for an international keyboard while the machine has a Japanese layout
ROM_START(yis303)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("ym2301-23907.tmm23256p-2011", 0x0000, 0x8000, BAD_DUMP CRC(e2242b53) SHA1(706dd67036baeec7127e4ccd8c8db8f6ce7d0e4c))
ROM_END

void msx1_state::yis303(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Yamaha expansion slot (50 pin)
	// 1 Yamaha module slot (60 pin)

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 3, 1);   // 16KB RAM
	add_cartridge_slot<1>(config, 1);
	// Expansion slot in slot #2
	// mounting sfg01 works, sfg05 does not
	add_cartridge_slot<3>(config, MSX_SLOT_YAMAHA_EXPANSION, "module", 3, 0, msx_yamaha_60pin, nullptr);

	m_hw_def.has_printer_port(false);
	msx1(VDP_TMS9918A, SND_YM2149, config, layout_msx_jp);
}

/* MSX - Yamaha YIS-503 */

ROM_START(yis503)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("yis503bios.rom", 0x0000, 0x8000, CRC(ee229390) SHA1(302afb5d8be26c758309ca3df611ae69cced2821))
ROM_END

void msx1_state::yis503(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Yamaha expansion slot
	// 1 Yamaha module slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 2, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	// Expansion slot in slot #2
	add_cartridge_slot<3>(config, MSX_SLOT_YAMAHA_EXPANSION, "module", 3, msx_yamaha_60pin, nullptr);

	msx1(VDP_TMS9928A, SND_YM2149, config, layout_msx_jp);
}

/* MSX - Yamaha YIS-503F */

ROM_START(yis503f)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("yis503f.rom", 0x0000, 0x8000, CRC(e9ccd789) SHA1(8963fc041975f31dc2ab1019cfdd4967999de53e))
ROM_END

void msx1_state::yis503f(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Yamaha expansion slot (50 pin)
	// 1 Yamaha module slot (60 pin)

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 2, 2);  // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	// Expansion slot in slot #2
	add_cartridge_slot<3>(config, MSX_SLOT_YAMAHA_EXPANSION, "module", 3, msx_yamaha_60pin, nullptr);

	msx1(VDP_TMS9929A, SND_YM2149, config, layout_msx_nocode);
}

/* MSX - Yamaha YIS-603 */

/* MSX - Yashica YC-64 */

ROM_START(yc64)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("yc64bios.u20", 0x0000, 0x8000, CRC(e9ccd789) SHA1(8963fc041975f31dc2ab1019cfdd4967999de53e))
ROM_END

void msx1_state::yc64(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives
	// 1 Cartridge slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4);  // 64KB RAM

	msx1(VDP_TMS9929A, SND_YM2149, config, layout_msx_nocode_nocaps);
}

/* MSX - Yeno DPC-64 */

/* MSX - Yeno MX64 */

ROM_START(mx64)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("tmm23256p_7953.ic2d", 0x0000, 0x8000, BAD_DUMP CRC(e0e894b7) SHA1(d99eebded5db5fce1e072d08e642c0909bc7efdd)) // need verification
ROM_END

void msx1_state::mx64(machine_config &config)
{
	// AY8910
	// FDC: None, 0 drives
	// 2 Cartridge slots

	// slot layout needs verification
	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 2, 0, 4);  // 64KB RAM
	add_cartridge_slot<2>(config, 3);

	msx1(VDP_TMS9928A, SND_AY8910, config, layout_msx_nocode);
}

} // anonymous namespace


/*   YEAR  NAME        PARENT    COMPAT MACHINE     INPUT     CLASS      INIT        COMPANY       FULLNAME */
/* MSX1 */
COMP(1986, perfect1,   0,        0,     perfect1,   msx,      msx1_state, empty_init, "Bawareth", "Perfect MSX1 (MSX1, Middle East)", 0)
COMP(1985, canonv8,    0,        0,     canonv8,    msxjp,    msx1_state, empty_init, "Canon", "V-8 (MSX1, Japan)", 0)
COMP(1984, canonv10,   canonv20, 0,     canonv10,   msxjp,    msx1_state, empty_init, "Canon", "V-10 (MSX1, Japan)", 0)
COMP(1984, canonv20,   0,        0,     canonv20,   msxjp,    msx1_state, empty_init, "Canon", "V-20 (MSX1, Japan)", 0)
COMP(1985, canonv20e,  canonv20, 0,     canonv20e,  msxuk,    msx1_state, empty_init, "Canon", "V-20E (MSX1, UK)", 0)
COMP(1985, canonv20f,  canonv20, 0,     canonv20e,  msxfr,    msx1_state, empty_init, "Canon", "V-20F (MSX1, France)", 0)
COMP(198?, canonv20g,  canonv20, 0,     canonv20e,  msxde,    msx1_state, empty_init, "Canon", "V-20G (MSX1, Germany)", 0)
COMP(198?, canonv20s,  canonv20, 0,     canonv20e,  msxsp,    msx1_state, empty_init, "Canon", "V-20S (MSX1, Spain)", 0)
COMP(1985, mx10,       mx15,     0,     mx10,       msxjp,    msx1_state, empty_init, "Casio", "MX-10 (MSX1, Japan)", 0)
COMP(1987, mx101,      mx15,     0,     mx101,      msxjp,    msx1_state, empty_init, "Casio", "MX-101 (MSX1, Japan)", 0)
COMP(1986, mx15,       0,        0,     mx15,       msx,      msx1_state, empty_init, "Casio", "MX-15 (MSX1, International)", 0)
COMP(1984, pv7,        pv16,     0,     pv7,        msxjp,    msx1_state, empty_init, "Casio", "PV-7 (MSX1, Japan)", 0)
COMP(1985, pv16,       0,        0,     pv16,       msxjp,    msx1_state, empty_init, "Casio", "PV-16 (MSX1, Japan)", 0)
COMP(1984, cpc88,      0,        0,     cpc88,      msxkr,    msx1_state, empty_init, "Daewoo", "CPC-88 (MSX1, Korea)", 0)
COMP(1984, dpc100,     dpc200,   0,     dpc100,     msxkr,    msx1_state, empty_init, "Daewoo", "IQ-1000 DPC-100 (MSX1, Korea)", 0)
COMP(1984, dpc180,     dpc200,   0,     dpc180,     msxkr,    msx1_state, empty_init, "Daewoo", "IQ-1000 DPC-180 (MSX1, Korea)", 0)
COMP(1984, dpc200,     0,        0,     dpc200,     msxkr,    msx1_state, empty_init, "Daewoo", "IQ-1000 DPC-200 (MSX1, Korea)", 0)
COMP(1985, dpc200e,    0,        0,     dpc200e,    msxfr,    msx1_state, empty_init, "Daewoo", "DPC-200E (MSX1, French)", 0)
COMP(1986, cpc50a,     cpc51,    0,     cpc50a,     msxkr,    msx1_state, empty_init, "Daewoo", "Zemmix CPC-50A (MSX1, Korea)", 0)
COMP(1987, cpc50b,     cpc51,    0,     cpc50b,     msxkr,    msx1_state, empty_init, "Daewoo", "Zemmix CPC-50B (MSX1, Korea)", 0)
COMP(1988, cpc51,      0,        0,     cpc51,      msxkr,    msx1_state, empty_init, "Daewoo", "Zemmix CPC-51 (MSX1, Korea)", 0)
COMP(1985, dgnmsx,     0,        0,     dgnmsx,     msxuk,    msx1_state, empty_init, "Eurohard S.A.", "Dragon MSX-64 (MSX1, Spain)", 0)
COMP(1985, fdpc200,    0,        0,     fdpc200,    msx,      msx1_state, empty_init, "Fenner", "DPC-200 (MSX1, Italy)", 0)
COMP(1985, fpc500,     0,        0,     fpc500,     msx,      msx1_state, empty_init, "Fenner", "FPC-500 (MSX1, Italy)", 0)
COMP(1984, fspc800,    0,        0,     fspc800,    msxuk,    msx1_state, empty_init, "Fenner", "SPC-800 (MSX1, Italy)", 0)
COMP(1983, fmx,        0,        0,     fmx,        msxjp,    msx1_state, empty_init, "Fujitsu", "FM-X (MSX1, Japan)", 0)
COMP(1984, gsfc80u,    0,        0,     gsfc80u,    msxkr,    msx1_state, empty_init, "Goldstar", "FC-80U (MSX1, Korea)", 0)
COMP(1984, gsfc200,    0,        0,     gsfc200,    msx,      msx1_state, empty_init, "Goldstar", "FC-200 (MSX1, Europe)", 0)
COMP(1985, gfc1080,    0,        0,     gfc1080,    msxkr,    msx1_state, empty_init, "Goldstar", "GFC-1080 (MSX1, Korea)", 0)
COMP(1985, gfc1080a,   0,        0,     gfc1080a,   msxkr,    msx1_state, empty_init, "Goldstar", "GFC-1080A (MSX1, Korea)", 0)
COMP(1985, expert10,   expert13, 0,     expert10,   expert10, msx1_state, empty_init, "Gradiente", "Expert XP-800 (1.0) (MSX1, Brazil)", 0)
COMP(198?, expert11,   expert13, 0,     expert11,   expert11, msx1_state, empty_init, "Gradiente", "Expert XP-800 (1.1) / Expert GPC-1 (MSX1, Brazil)", 0)
COMP(198?, expert13,   0,        0,     expert13,   expert11, msx1_state, empty_init, "Gradiente", "Expert 1.3 (MSX1, Brazil)", 0)
COMP(1989, expertdp,   0,        0,     expertdp,   expert11, msx1_state, empty_init, "Gradiente", "Expert DDPlus (MSX1, Brazil)", 0)
COMP(1989, expertpl,   0,        0,     expertpl,   expert11, msx1_state, empty_init, "Gradiente", "Expert Plus (MSX1, Brazil)", 0)
COMP(1983, mbh1,       0,        0,     mbh1,       msxjp,    msx1_state, empty_init, "Hitachi", "MB-H1 (MSX1, Japan)", 0)
COMP(1984, mbh1e,      mbh1,     0,     mbh1e,      msxjp,    msx1_state, empty_init, "Hitachi", "MB-H1E (MSX1, Japan)", 0)
COMP(1984, mbh2,       0,        0,     mbh2,       msxjp,    msx1_state, empty_init, "Hitachi", "MB-H2 (MSX1, Japan)", 0)
COMP(1988, mbh25,      0,        0,     mbh25,      msxjp,    msx1_state, empty_init, "Hitachi", "MB-H25 (MSX1, Japan)", 0)
COMP(1986, mbh50,      0,        0,     mbh50,      msxjp,    msx1_state, empty_init, "Hitachi", "MB-H50 (MSX1, Japan)", 0)
COMP(1985, jvchc7gb,   0,        0,     jvchc7gb,   msxuk,    msx1_state, empty_init, "JVC", "HC-7E / HC-7GB (MSX1, Europe)", 0)
COMP(1983, ml8000,     0,        0,     ml8000,     msxjp,    msx1_state, empty_init, "Mitsubishi", "ML-8000 (MSX1, Japan)", 0)
COMP(1984, mlf48,      0,        0,     mlf48,      msxuk,    msx1_state, empty_init, "Mitsubishi", "ML-F48 (MSX1, UK)", 0)
COMP(1984, mlf80,      0,        0,     mlf80,      msxuk,    msx1_state, empty_init, "Mitsubishi", "ML-F80 (MSX1, UK)", 0)
COMP(1984, mlf110,     0,        0,     mlf110,     msxjp,    msx1_state, empty_init, "Mitsubishi", "ML-F110 (MSX1, Japan)", 0)
COMP(1984, mlf120,     0,        0,     mlf120,     msxjp,    msx1_state, empty_init, "Mitsubishi", "ML-F120 (MSX1, Japan)", 0)
COMP(1986, mlfx1,      0,        0,     mlfx1,      mlfx1,    msx1_state, empty_init, "Mitsubishi", "ML-FX1 (MSX1, Spain)", 0)
COMP(1985, cf1200,     0,        0,     cf1200,     msxjp,    msx1_state, empty_init, "National", "CF-1200 (MSX1, Japan)", 0)
COMP(1983, cf2000,     0,        0,     cf2000,     msxjp,    msx1_state, empty_init, "National", "CF-2000 (MSX1, Japan)", 0)
COMP(1984, cf2700,     0,        0,     cf2700,     msxjp,    msx1_state, empty_init, "National", "CF-2700 (MSX1, Japan)", 0)
COMP(1984, cf3000,     0,        0,     cf3000,     cf3000,   msx1_state, empty_init, "National", "CF-3000 (MSX1, Japan)", 0)
COMP(1985, cf3300,     0,        0,     cf3300,     cf3000,   msx1_state, empty_init, "National", "CF-3300 (MSX1, Japan)", 0)
COMP(1985, fs1300,     0,        0,     fs1300,     msxjp,    msx1_state, empty_init, "National", "FS-1300 (MSX1, Japan)", 0)
COMP(1985, fs4000,     0,        0,     fs4000,     fs4000,   msx1_state, empty_init, "National", "FS-4000 (MSX1, Japan)", 0)
COMP(1985, fs4000a,    fs4000,   0,     fs4000a,    fs4000,   msx1_state, empty_init, "National", "FS-4000 (alt) (MSX1, Japan)", 0)
COMP(1985, phc2,       0,        0,     phc2,       msxfr,    msx1_state, empty_init, "Olympia", "PHC-2 (MSX1, France)" , 0)
COMP(1984, phc28,      0,        0,     phc28,      msxfr,    msx1_state, empty_init, "Olympia", "PHC-28 (MSX1, France)", 0)
COMP(1985, cf2700g,    cf2700uk, 0,     cf2700g,    msxde,    msx1_state, empty_init, "Panasonic", "CF-2700 (MSX1, Germany)", 0)
COMP(1985, cf2700uk,   0,        0,     cf2700uk,   msxuk,    msx1_state, empty_init, "Panasonic", "CF-2700 (MSX1, UK)", 0)
COMP(1989, nms801,     0,        0,     nms801,     msx,      msx1_state, empty_init, "Philips", "NMS-801 (MSX1, Italy)", 0)
COMP(1984, vg8000,     vg8010,   0,     vg8000,     vg8010,   msx1_state, empty_init, "Philips", "VG-8000 (MSX1, Europe)", 0)
COMP(1985, vg8010,     0,        0,     vg8010,     vg8010,   msx1_state, empty_init, "Philips", "VG-8010 / VG-8010/00 (MSX1, Europe)", 0)
COMP(1985, vg8010f,    vg8010,   0,     vg8010f,    vg8010f,  msx1_state, empty_init, "Philips", "VG-8010F / VG-8010/19 (MSX1, French)" , 0)
COMP(1985, vg802000,   vg802020, 0,     vg802000,   msx,      msx1_state, empty_init, "Philips", "VG-8020/00 (MSX1, Europe)", 0)
COMP(1985, vg8020f,    vg802020, 0,     vg8020f,    msxfr,    msx1_state, empty_init, "Philips", "VG-8020/19 / VG-8020F (MSX1, French)", 0)
COMP(1985, vg802020,   0,        0,     vg802020,   msx,      msx1_state, empty_init, "Philips", "VG-8020/20 (MSX1, Europe)", 0)
COMP(1984, piopx7,     0,        0,     piopx7,     msxjp,    msx1_state, empty_init, "Pioneer", "PX-07 Palcom (MSX1, Japan)", 0)
COMP(1985, piopx7uk,   piopx7,   0,     piopx7uk,   msxuk,    msx1_state, empty_init, "Pioneer", "PX-07UK Palcom (MSX1, UK)", 0)
COMP(1986, piopxv60,   piopx7,   0,     piopxv60,   msxjp,    msx1_state, empty_init, "Pioneer", "PX-V60 (MSX1, Japan)", 0)
COMP(1986, ax150,      0,        0,     ax150,      msx,      msx1_state, empty_init, "Sakhr", "AX-150 (MSX1, Arabic)", 0)
COMP(1986, ax170,      0,        0,     ax170,      msx,      msx1_state, empty_init, "Sakhr", "AX-170 (MSX1, Arabic)", 0)
COMP(1986, ax230,      0,        0,     ax230,      msx,      msx1_state, empty_init, "Sakhr", "AX-230 (MSX1, Arabic)", MACHINE_IMPERFECT_GRAPHICS)
COMP(1984, spc800,     0,        0,     spc800,     msxkr,    msx1_state, empty_init, "Samsung", "SPC-800 (MSX1, Korea)", 0)
COMP(1983, mpc10,      0,        0,     mpc10,      msxjp,    msx1_state, empty_init, "Sanyo", "MPC-10 / Wavy10 (MSX1, Japan)", 0)
COMP(1985, mpc64,      0,        0,     mpc64,      msxde,    msx1_state, empty_init, "Sanyo", "MPC-64 (MSX1, Germany)", 0)
COMP(1985, mpc100,     0,        0,     mpc100,     msxuk,    msx1_state, empty_init, "Sanyo", "MPC-100 (MSX1, UK)", 0)
COMP(1985, mpc200,     0,        0,     mpc200,     msxuk,    msx1_state, empty_init, "Sanyo", "MPC-200 (MSX1, UK)", 0) // Is this the same as MPC-200SP, is it even real ?
COMP(1985, mpc200sp,   mpc200,   0,     mpc200sp,   msxsp,    msx1_state, empty_init, "Sanyo", "MPC-200SP (MSX1, Spain)", 0)
COMP(1985, phc28l,     0,        0,     phc28l,     msxfr,    msx1_state, empty_init, "Sanyo", "PHC-28L (MSX1, France)", 0)
COMP(1984, phc28s,     0,        0,     phc28s,     msxuk,    msx1_state, empty_init, "Sanyo", "PHC-28S (MSX1, France)", 0)
COMP(1985, hb8000,     0,        0,     hb8000,     hotbit,   msx1_state, empty_init, "Sharp / Epcom", "HB-8000 Hotbit (MSX1, Brazil)", 0)
COMP(198?, hotbi13b,   hotbi13p, 0,     hotbi13b,   hotbit,   msx1_state, empty_init, "Sharp / Epcom", "HB-8000 Hotbit 1.3b (MSX1, Brazil)", 0)
COMP(198?, hotbi13p,   0,        0,     hotbi13p,   hotbit,   msx1_state, empty_init, "Sharp / Epcom", "HB-8000 Hotbit 1.3p (MSX1, Brazil)", 0)
COMP(1985, hb10,       hb10p,    0,     hb10,       msxjp,    msx1_state, empty_init, "Sony", "HB-10 (MSX1, Japan)", 0)
COMP(1986, hb10p,      0,        0,     hb10p,      msxuk,    msx1_state, empty_init, "Sony", "HB-10P (MSX1, Netherlands)", 0)
COMP(1984, hb101,      hb101p,   0,     hb101,      msxjp,    msx1_state, empty_init, "Sony", "HB-101 (MSX1, Japan)", 0)
COMP(1984, hb101p,     0,        0,     hb101p,     msxuk,    msx1_state, empty_init, "Sony", "HB-101P (MSX1, Europe)", 0)
COMP(1986, hb20p,      0,        0,     hb20p,      msxsp,    msx1_state, empty_init, "Sony", "HB-20P (MSX1, Spain)", 0)
COMP(1985, hb201,      hb201p,   0,     hb201,      msxjp,    msx1_state, empty_init, "Sony", "HB-201 (MSX1, Japan)", 0)
COMP(1985, hb201p,     0,        0,     hb201p,     msxuk,    msx1_state, empty_init, "Sony", "HB-201P (MSX1, Europe)", 0)
COMP(1984, hb501p,     0,        0,     hb501p,     msxuk,    msx1_state, empty_init, "Sony", "HB-501P (MSX1, Europe)", 0)
COMP(1983, hb55,       hb55p,    0,     hb55,       msxjp,    msx1_state, empty_init, "Sony", "HB-55 (MSX1, Japan)", 0)
COMP(1984, hb55d,      hb55p,    0,     hb55d,      msxde,    msx1_state, empty_init, "Sony", "HB-55D (MSX1, Germany)", 0)
COMP(1984, hb55p,      0,        0,     hb55p,      msxuk,    msx1_state, empty_init, "Sony", "HB-55P (MSX1, Europe)", 0)
COMP(1984, hb701fd,    0,        0,     hb701fd,    msxjp,    msx1_state, empty_init, "Sony", "HB-701FD (MSX1, Japan)", 0)
COMP(1984, hb75,       hb75p,    0,     hb75,       msxjp,    msx1_state, empty_init, "Sony", "HB-75 (MSX1, Japan)", 0)
COMP(1984, hb75d,      hb75p,    0,     hb75d,      msxde,    msx1_state, empty_init, "Sony", "HB-75D (MSX1, Germany)", 0)
COMP(1984, hb75p,      0,        0,     hb75p,      msxuk,    msx1_state, empty_init, "Sony", "HB-75P (MSX1, Europe)", 0)
COMP(1984, svi728,     0,        0,     svi728,     svi728,   msx1_state, empty_init, "Spectravideo", "SVI-728 (MSX1, International)", 0)
COMP(1984, svi728es,   svi728,   0,     svi728,     svi728sp, msx1_state, empty_init, "Spectravideo", "SVI-728 (MSX1, Spanish)", 0)
COMP(1986, tadpc200,   dpc200,   0,     tadpc200,   msxsp,    msx1_state, empty_init, "Talent", "DPC-200 (MSX1, Argentina, Spanish keyboard)", 0)
COMP(1986, tadpc200b,  dpc200,   0,     tadpc200,   msx,      msx1_state, empty_init, "Talent", "DPC-200 (MSX1, Argentina, international keyboard)", 0)
COMP(1984, hx10,       0,        0,     hx10,       msx,      msx1_state, empty_init, "Toshiba", "HX-10AA (MSX1, Europe)", 0)
COMP(1983, hx10d,      hx10,     0,     hx10d,      msxjp,    msx1_state, empty_init, "Toshiba", "HX-10D (MSX1, Japan)", 0)
COMP(1984, hx10dp,     hx10,     0,     hx10dp,     msxjp,    msx1_state, empty_init, "Toshiba", "HX-10DP (MSX1, Japan)", 0)
COMP(1984, hx10e,      hx10,     0,     hx10e,      msx,      msx1_state, empty_init, "Toshiba", "HX-10E (MSX1, Spain)", 0)
COMP(1984, hx10f,      hx10,     0,     hx10f,      msx,      msx1_state, empty_init, "Toshiba", "HX-10F (MSX1, France)", 0)
COMP(1983, hx10s,      hx10,     0,     hx10s,      msxjp,    msx1_state, empty_init, "Toshiba", "HX-10S (MSX1, Japan)", 0)
COMP(1984, hx10sa,     hx10,     0,     hx10sa,     msxjp,    msx1_state, empty_init, "Toshiba", "HX-10SA (MSX1, Japan)", 0)
COMP(1984, hx20,       0,        0,     hx20,       msxjp,    msx1_state, empty_init, "Toshiba", "HX-20 (MSX1, Japan)", 0)
COMP(1985, hx20e,      hx20,     0,     hx20e,      msx,      msx1_state, empty_init, "Toshiba", "HX-20E (MSX1, Spain)", 0)
COMP(1985, hx20i,      hx20,     0,     hx20i,      msx,      msx1_state, empty_init, "Toshiba", "HX-20I (MSX1, Italy)", 0)
COMP(1984, hx21,       0,        0,     hx21,       msxjp,    msx1_state, empty_init, "Toshiba", "HX-21 (MSX1, Japan)", 0)
COMP(1985, hx21f,      hx21,     0,     hx21f,      msx,      msx1_state, empty_init, "Toshiba", "HX-21F (MSX1, France)", 0)
COMP(1984, hx22,       0,        0,     hx22,       msxjp,    msx1_state, empty_init, "Toshiba", "HX-22 (MSX1, Japan)", 0)
COMP(1985, hx22i,      hx22,     0,     hx22i,      msx,      msx1_state, empty_init, "Toshiba", "HX-22I (MSX1, Italy)", 0)
COMP(1985, hx32,       0,        0,     hx32,       msxjp,    msx1_state, empty_init, "Toshiba", "HX-32 (MSX1, Japan)", 0)
COMP(1985, hx51i,      0,        0,     hx51i,      msx,      msx1_state, empty_init, "Toshiba", "HX-51I (MSX1, Italy, Spain)", 0)
COMP(1983, hc5,        hc7,      0,     hc5,        msxjp,    msx1_state, empty_init, "Victor", "HC-5 (MSX1, Japan)", 0)
COMP(1984, hc6,        hc7,      0,     hc6,        msxjp,    msx1_state, empty_init, "Victor", "HC-6 (MSX1, Japan)", 0)
COMP(1985, hc7,        0,        0,     hc7,        msxjp,    msx1_state, empty_init, "Victor", "HC-7 (MSX1, Japan)", 0)
COMP(1984, cx5f1,      cx5f,     0,     cx5f1,      msxjp,    msx1_state, empty_init, "Yamaha", "CX5F w/SFG01 (MSX1, Japan)", 0)
COMP(1984, cx5f,       0,        0,     cx5f,       msxjp,    msx1_state, empty_init, "Yamaha", "CX5F w/SFG05 (MSX1, Japan)", 0)
COMP(1984, cx5mu,      0,        0,     cx5mu,      msx,      msx1_state, empty_init, "Yamaha", "CX5MU (MSX1, USA)", 0)
COMP(1985, sx100,      0,        0,     sx100,      msxjp,    msx1_state, empty_init, "Yamaha", "SX-100 (MSX1, Japan)", 0)
COMP(1984, yis303,     0,        0,     yis303,     msxjp,    msx1_state, empty_init, "Yamaha", "YIS303 (MSX1, Japan)", 0)
COMP(1984, yis503,     0,        0,     yis503,     msxjp,    msx1_state, empty_init, "Yamaha", "YIS503 (MSX1, Japan)", 0)
COMP(1984, yis503f,    yis503,   0,     yis503f,    msxuk,    msx1_state, empty_init, "Yamaha", "YIS503F (MSX1, French)", 0)
COMP(1984, yc64,       0,        0,     yc64,       msxuk,    msx1_state, empty_init, "Yashica", "YC-64 (MSX1, Europe)", 0)
COMP(1985, mx64,       0,        0,     mx64,       msxfr,    msx1_state, empty_init, "Yeno", "MX64 (MSX1, France)", 0)



msx1_bruc100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol

/*
** TODO:
** - Not all keypad keys hooked up yet
*/

#include "emu.h"
#include "msx.h"
#include "msx_keyboard.h"
#include "bus/msx/slot/bruc100.h"
#include "bus/msx/slot/ram.h"
#include "bus/msx/slot/ram_mm.h"

#include "msx_nocode.lh"

using namespace msx_keyboard;


namespace {

class bruc100_state : public msx_state
{
public:
	bruc100_state(const machine_config &mconfig, device_type type, const char *tag);

	void bruc100(machine_config &config);
	void bruc100a(machine_config &config);

private:
	required_device<msx_slot_bruc100_device> m_bruc100_firm;

	void io_map(address_map &map) ATTR_COLD;
	void port90_w(u8 data);
};


bruc100_state::bruc100_state(const machine_config &mconfig, device_type type, const char *tag)
	: msx_state(mconfig, type, tag, 10.738635_MHz_XTAL, 3)
	, m_bruc100_firm(*this, "firm")
{
}

void bruc100_state::io_map(address_map &map)
{
	msx1_io_map(map);
	map(0x90, 0x90).w(FUNC(bruc100_state::port90_w));
}

void bruc100_state::port90_w(u8 data)
{
	m_bruc100_firm->select_bank(BIT(data, 7));
	m_cent_ctrl_out->write(data);
}

/* MSX - Frael Bruc 100-1 */

ROM_START(bruc100)
	ROM_REGION(0x10000, "mainrom", 0)
	ROM_LOAD("v1_1_mcl", 0x0000, 0x8000, CRC(c7bc4298) SHA1(3abca440cba16ac5e162b602557d30169f77adab))
	ROM_LOAD("f_v1_0", 0x8000, 0x2000, CRC(707a62b6) SHA1(e4ffe02abbda17986cb161c332e9e54d24fd053c))
	ROM_RELOAD(0xa000, 0x2000)
ROM_END

void bruc100_state::bruc100(machine_config &config)
{
	// AY-3-8910
	// FDC: None, 0 drives
	// Non-standard cassette port
	// 0 Cartridge slots
	// 1 Expansion slot

	add_internal_slot(config, MSX_SLOT_BRUC100, "firm", 0, 0, 2, "mainrom");
	// Expansion slot in slot 1
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 4).set_total_size(0x10000);   // 64KB RAM

	msx1(VDP_TMS9129, SND_AY8910, config, layout_msx_nocode);
	m_maincpu->set_addrmap(AS_IO, &bruc100_state::io_map);
}

/* MSX - Frael Bruc 100-2 */

ROM_START(bruc100a)
	ROM_REGION(0x10000, "mainrom", 0)
	ROM_LOAD("bruc100-2bios.rom", 0x0000, 0x8000, CRC(24464a7b) SHA1(88611b54cdbb79aa5380570f3dfef8b3a1cc2057))
	// v1.3
	ROM_SYSTEM_BIOS(0, "v13", "v1.3 firmware")
	ROMX_LOAD("bruc100_2_firmware1.3.rom", 0x8000, 0x8000, CRC(286fd001) SHA1(85ab6946950d4e329d5703b5defcce46cd96a50e), ROM_BIOS(0))
	// v1.2
	ROM_SYSTEM_BIOS(1, "v12", "v1.2 firmware")
	ROMX_LOAD("c_v1_2.u8", 0x8000, 0x8000, CRC(de12f81d) SHA1(f92d3aaa314808b7a9a14c40871d24f0d558ea00), ROM_BIOS(1))
	// v1.1
	ROM_SYSTEM_BIOS(2, "v11", "v1.1 firmware")
	// Firmware 1.1 - 8kb bootlogo rom (mirrored to 2000-3fff)
	ROMX_LOAD("bruc100_2_firmware1.2.rom", 0x8000, 0x2000, CRC(54d60863) SHA1(b4c9a06054cda5fd31311a79cc06e6f018cf828f), ROM_BIOS(2))
	ROMX_LOAD("bruc100_2_firmware1.2.rom", 0xa000, 0x2000, CRC(54d60863) SHA1(b4c9a06054cda5fd31311a79cc06e6f018cf828f), ROM_BIOS(2))
ROM_END

void bruc100_state::bruc100a(machine_config &config)
{
	// AY-3-8910
	// FDC: None, 0 drives
	// Non-standard cassette port
	// 1 Cartridge slot
	// 1 Expansion slot

	add_internal_slot(config, MSX_SLOT_BRUC100, "firm", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 1, 0, 4);   // 64KB RAM
	add_cartridge_slot<1>(config, 2);
	// Expansion slot in slot 3

	msx1(VDP_TMS9129, SND_AY8910, config, layout_msx_nocode);
	m_maincpu->set_addrmap(AS_IO, &bruc100_state::io_map);
}

} // anonymous namespace

COMP(1987, bruc100,    0,        0,     bruc100,    bruc100,  bruc100_state, empty_init, "Frael", "Bruc 100-1 (MSX1, Italy)", 0)
COMP(1988, bruc100a,   bruc100,  0,     bruc100a,   bruc100,  bruc100_state, empty_init, "Frael", "Bruc 100-2 (MSX1, Italy)", 0)



msx1_v9938.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol

#include "emu.h"
#include "msx.h"
#include "msx_keyboard.h"
#include "bus/msx/slot/disk.h"
#include "bus/msx/slot/msx_rs232.h"
#include "bus/msx/slot/ram.h"
#include "bus/msx/slot/ram_mm.h"
#include "bus/msx/slot/rom.h"
#include "softlist_dev.h"

#include "msx_ar.lh"
#include "msx_ar_1fdd.lh"
#include "msx_en.lh"
#include "msx_ru.lh"
#include "msx_nocode.lh"
#include "msx_nocode_1fdd.lh"

using namespace msx_keyboard;

/***************************************************************************

  MSX1 with v9938 Game drivers

Undumped and/or not emulated:
- Sakhr AX-200 (Arabic/French)
- Spectravideo SVI-738 (German)
- Yamaha CX5MII-128A (Australia, New Zealand)
- Yamaha CX5MII-128 C (Canada)
- Yamaha CX5MII-128 E (UK)
- Yamaha CX5MII-128 F (France)
- Yamaha CX5MII-128 G (Germany)
- Yamaha CX5MII-128 P (Spain)
- Yamaha CX5MII-128 S (Scandinavia)
- Yamaha CX5MII-128 U (USA)
- Yamaha CX5MIIA (Australia, New Zealand)
- Yamaha CX5MIIC (Canada)
- Yamaha CX5MIIE (UK)
- Yamaha CX5MIIF (France)
- Yamaha CX5MIIG (Germany)
- Yamaha CX5MIIP (Spain)
- Yamaha CX5MIIS (Scandinavia)
- Yamaha CX5MIIU (USA)
***************************************************************************/

namespace {

class msx1_v9938_state : public msx_state
{
public:
	msx1_v9938_state(const machine_config &mconfig, device_type type, const char *tag);

	void ax200(machine_config &mconfig);
	void ax200m(machine_config &mconfig);
	void cx5m128(machine_config &config);
	void cx5miib(machine_config &config);
	void svi738(machine_config &config);
	void svi738ar(machine_config &config);
	void tadpc200a(machine_config &config);
	void y503iir(machine_config &config);
	void y503iir2(machine_config &config);
	void yis503ii(machine_config &config);

protected:
	void msx1_v9938(ay8910_type ay8910_type, machine_config &config, const internal_layout &layout);
	void msx1_v9938_pal(ay8910_type ay8910_type, machine_config &config, const internal_layout &layout);
	void svi738_base(machine_config &config, const internal_layout &layout);

	void io_map(address_map &map) ATTR_COLD;

	required_device<v9938_device> m_v9938;
};

msx1_v9938_state::msx1_v9938_state(const machine_config &mconfig, device_type type, const char *tag)
	: msx_state(mconfig, type, tag, 21.477272_MHz_XTAL, 6)
	, m_v9938(*this, "v9938")
{
}

void msx1_v9938_state::io_map(address_map &map)
{
	msx_base_io_map(map);
	map(0x98, 0x9b).rw(m_v9938, FUNC(v9938_device::read), FUNC(v9938_device::write));
}

void msx1_v9938_state::msx1_v9938(ay8910_type ay8910_type, machine_config &config, const internal_layout &layout)
{
	msx_base(ay8910_type, config, layout);

	m_maincpu->set_addrmap(AS_IO, &msx1_v9938_state::io_map);

	// video hardware
	V9938(config, m_v9938, 21.477272_MHz_XTAL);
	m_v9938->set_screen_ntsc(m_screen);
	m_v9938->set_vram_size(0x4000);
	m_v9938->int_cb().set(m_mainirq, FUNC(input_merger_device::in_w<0>));

	// Software lists
	msx1_add_softlists(config);
}

void msx1_v9938_state::msx1_v9938_pal(ay8910_type ay8910_type, machine_config &config, const internal_layout &layout)
{
	msx1_v9938(ay8910_type, config, layout);
	m_v9938->set_screen_pal(m_screen);
}

/* MSX - Sakhr AX-200 (Arabic/English) */

ROM_START (ax200)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("ax200bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(cae98b30) SHA1(079c018739c37485f3d64ef2145a0267fce6e20e)) // need verification

	ROM_REGION(0x8000, "arabic", 0)
	ROM_LOAD("ax200arab.rom", 0x0000, 0x8000, BAD_DUMP CRC(b041e610) SHA1(7574cc5655805ea316011a8123b064917f06f83c)) // need verification
ROM_END

void msx1_v9938_state::ax200(machine_config &config)
{
	// YM2149 in S3527
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// V9938
	// MSX Engine S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram1", 0, 0, 2, 2); // 32KB RAM
	add_internal_slot(config, MSX_SLOT_RAM, "ram2", 0, 2, 0, 2); // 32KB RAM
	add_internal_slot(config, MSX_SLOT_ROM, "arabic", 0, 3, 1, 2, "arabic");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_cartridge_slot<3>(config, MSX_SLOT_YAMAHA_EXPANSION, "module", 3, msx_yamaha_60pin, nullptr);

	msx1_v9938_pal(SND_YM2149, config, layout_msx_ar);
}

/* MSX - Sakhr AX-200 (Arabic/French) */

/* MSX - Sakhr AX-200M (Arabic/English) */

ROM_START (ax200m)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("ax200bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(cae98b30) SHA1(079c018739c37485f3d64ef2145a0267fce6e20e)) // need verification

	ROM_REGION(0x8000, "arabic", 0)
	ROM_LOAD("ax200arab.rom", 0x0000, 0x8000, BAD_DUMP CRC(b041e610) SHA1(7574cc5655805ea316011a8123b064917f06f83c)) // need verification
ROM_END

void msx1_v9938_state::ax200m(machine_config &config)
{
	// YM2149 in S3527
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// V9938
	// MSX Engine S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram1", 0, 0, 2, 2); // 32KB RAM
	add_internal_slot(config, MSX_SLOT_RAM, "ram2", 0, 2, 0, 2); // 32KB RAM
	add_internal_slot(config, MSX_SLOT_ROM, "arabic", 0, 3, 1, 2, "arabic");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	// Dumped unit had a SFG05 with version M5.00.011 rom
	add_cartridge_slot<3>(config, MSX_SLOT_YAMAHA_EXPANSION, "module", 3, msx_yamaha_60pin, "sfg05");

	msx1_v9938_pal(SND_YM2149, config, layout_msx_ar);
}

/* MSX - Spectravideo SVI-738 */

ROM_START(svi738)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("738bios.rom", 0x0000, 0x8000, CRC(ad007d62) SHA1(c53b3f2c00f31683914f7452f3f4d94ae2929c0d))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("738disk.rom", 0x0000, 0x4000, CRC(acd27a36) SHA1(99a40266bc296cef1d432cb0caa8df1a7e570be4))

	ROM_REGION(0x4000, "rs232rom", ROMREGION_ERASEFF)
	ROM_LOAD("738232c.rom", 0x0000, 0x2000, CRC(3353dcc6) SHA1(4e9384c9d137f0ab65ffc5a78f04cd8c9df6c8b7))
ROM_END

void msx1_v9938_state::svi738_base(machine_config &config, const internal_layout &layout)
{
	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 1, 0, 4);  // 64KB RAM
	add_cartridge_slot<1>(config, 2);
	add_internal_slot_irq<2>(config, MSX_SLOT_RS232_SVI738, "rs232", 3, 0, 1, 1, "rs232rom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK2_FD1793_SS, "disk", 3, 1, 1, 2, "diskrom").use_motor_for_led();

	msx1_v9938_pal(SND_AY8910, config, layout);
}

void msx1_v9938_state::svi738(machine_config &config)
{
	// AY8910
	// FDC: fd1793, 1 3.5" SSDD drive
	// 1 Cartridge slot
	// builtin 80 columns card (V9938)
	// RS-232C interface

	svi738_base(config, layout_msx_nocode_1fdd);
}

/* MSX - Spectravideo SVI-738 Arabic */

ROM_START(svi738ar)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("738arbios.rom", 0x0000, 0x8000, BAD_DUMP CRC(ad007d62) SHA1(c53b3f2c00f31683914f7452f3f4d94ae2929c0d)) // need verification

	ROM_REGION(0x8000, "arab", 0)
	ROM_LOAD("738arab.rom",  0x0000, 0x8000, BAD_DUMP CRC(339cd1aa) SHA1(0287b2ec897b9196788cd9f10c99e1487d7adbbb)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("738ardisk.rom", 0x0000, 0x4000, BAD_DUMP CRC(acd27a36) SHA1(99a40266bc296cef1d432cb0caa8df1a7e570be4)) // meed verification

	ROM_REGION(0x4000, "rs232rom", ROMREGION_ERASEFF)
	ROM_LOAD("738ar232c.rom", 0x0000, 0x2000, BAD_DUMP CRC(3353dcc6) SHA1(4e9384c9d137f0ab65ffc5a78f04cd8c9df6c8b7)) // need verification
ROM_END

void msx1_v9938_state::svi738ar(machine_config &config)
{
	svi738_base(config, layout_msx_ar_1fdd);
	add_internal_slot(config, MSX_SLOT_ROM, "arab", 3, 3, 1, 2, "arab");
}

/* MSX - Spectravideo SVI-738 Danish/Norwegian */

ROM_START(svi738dk)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("738dkbios.rom", 0x0000, 0x8000, BAD_DUMP CRC(88720320) SHA1(1bda5af20cb86565bdc1ebd1e59a691fed7f9256)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("738dkdisk.rom", 0x0000, 0x4000, BAD_DUMP CRC(fb884df4) SHA1(6d3a530ae822ec91f6444c681c9b08b9efadc7e7)) // need verification

	ROM_REGION(0x4000, "rs232rom", ROMREGION_ERASEFF)
	ROM_LOAD("738dk232c.rom", 0x0000, 0x2000, BAD_DUMP CRC(3353dcc6) SHA1(4e9384c9d137f0ab65ffc5a78f04cd8c9df6c8b7)) // need verification
ROM_END

/* MSX - Spectravideo SVI-738 German */

/* MSX - Spectravideo SVI-738 Polish */

ROM_START(svi738pl)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("738plbios.rom", 0x0000, 0x8000, BAD_DUMP CRC(431b8bf5) SHA1(c90077ed84133a947841e07856e71133ba779da6)) // IC51 on board, need verification

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("738disk.rom",   0x0000, 0x4000, BAD_DUMP CRC(acd27a36) SHA1(99a40266bc296cef1d432cb0caa8df1a7e570be4)) // need verification

	ROM_REGION(0x4000, "rs232rom", ROMREGION_ERASEFF)
	ROM_LOAD("738232c.rom",   0x0000, 0x2000, BAD_DUMP CRC(3353dcc6) SHA1(4e9384c9d137f0ab65ffc5a78f04cd8c9df6c8b7)) // need verification
ROM_END

/* MSX - Spectravideo SVI-738 Spanish */

ROM_START(svi738sp)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("738spbios.rom", 0x0000, 0x8000, BAD_DUMP CRC(f0c0cbb9) SHA1(5f04d5799ed72ea4993e7c4302a1dd55ac1ea8cd)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("738spdisk.rom", 0x0000, 0x4000, BAD_DUMP CRC(fb884df4) SHA1(6d3a530ae822ec91f6444c681c9b08b9efadc7e7)) // need verification

	ROM_REGION(0x4000, "rs232rom", ROMREGION_ERASEFF)
	ROM_LOAD("738sp232c.rom", 0x0000, 0x2000, BAD_DUMP CRC(3353dcc6) SHA1(4e9384c9d137f0ab65ffc5a78f04cd8c9df6c8b7)) // need verification
ROM_END

/* MSX - Spectravideo SVI-738 Swedish/Finnish */

ROM_START(svi738sw)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("738sebios.rom", 0x0000, 0x8000, CRC(c8ccdaa0) SHA1(87f4d0fa58cfe9cef818a3185df2735e6da6168c))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("738sedisk.rom", 0x0000, 0x4000, CRC(fb884df4) SHA1(6d3a530ae822ec91f6444c681c9b08b9efadc7e7))

	ROM_REGION(0x4000, "rs232rom", ROMREGION_ERASEFF)
	ROM_LOAD("738se232c.rom", 0x0000, 0x2000, CRC(3353dcc6) SHA1(4e9384c9d137f0ab65ffc5a78f04cd8c9df6c8b7))
ROM_END

/* MSX - Talent DPC-200A */

ROM_START(tadpc200a)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("dpc200abios.rom", 0x0000, 0x8000, BAD_DUMP CRC(8205795e) SHA1(829c00c3114f25b3dae5157c0a238b52a3ac37db)) // need verification
ROM_END

void msx1_v9938_state::tadpc200a(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// S1985

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 1, 0, 4);  // 64KB RAM
	add_cartridge_slot<1>(config, 2);
	// Expansion slot

	msx1_v9938_pal(SND_YM2149, config, layout_msx_nocode);
}

/* MSX - Yamaha CX5MII-128A (Australia, New Zealand) */

/* MSX - Yamaha CX5MII-128B (Italy) */

// Exact region unknown
ROM_START(cx5m128)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("cx5m128bios.rom", 0x0000, 0x8000, CRC(7973e080) SHA1(ea4a723cf098be7d7b40f23a7ab831cf5e2190d7))

	ROM_REGION(0x4000, "subrom", ROMREGION_ERASEFF)
	ROM_LOAD("cx5m128sub.rom",  0x0000, 0x2000, CRC(b17a776d) SHA1(c2340313bfda751181e8a5287d60f77bc6a2f3e6))
ROM_END

void msx1_v9938_state::cx5m128(machine_config &config)
{
	// YM2149 in S3527
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// 1 Mini cart slot (with YRM-502)
	// 1 Yamaha Module slot
	// S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 1, 1, "subrom");
	add_cartridge_slot<3>(config, MSX_SLOT_YAMAHA_MINICART, "minicart", 3, 1, msx_yamaha_minicart, nullptr);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000);   // 128KB Mapper RAM
	add_cartridge_slot<4>(config, MSX_SLOT_YAMAHA_EXPANSION, "module", 3, 3, msx_yamaha_60pin, "sfg05");

	msx1_v9938_pal(SND_YM2149, config, layout_msx_nocode);
	SOFTWARE_LIST(config, "minicart_list").set_original("msx_yamaha_minicart");
}

/* MSX - Yamaha CX5MII-128 C (Canada) */

/* MSX - Yamaha CX5MII-128 E (UK) */

/* MSX - Yamaha CX5MII-128 F (France) */

/* MSX - Yamaha CX5MII-128 G (Germany) */

/* MSX - Yamaha CX5MII-128 P (Spain) */

/* MSX - Yamaha CX5MII-128 S (Scandinavia) */

/* MSX - Yamaha CX5MII-128 U (USA) */

/* MSX - Yamaha CX5MIIA (Australia, New Zealand) */

/* MSX - Yamaha CX5MIIB (Italy) */

ROM_START(cx5miib)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("cx5mii_basic-bios1.rom", 0x0000, 0x8000, CRC(507b2caa) SHA1(0dde59e8d98fa524961cd37b0e100dbfb42cf576))

	ROM_REGION(0x4000, "subrom", 0)
	// overdump?
	ROM_LOAD("cx5mii_sub.rom",  0x0000, 0x4000, BAD_DUMP CRC(317f9bb5) SHA1(0ce800666c0d66bc2aa0b73a16f228289b9198be))
ROM_END

void msx1_v9938_state::cx5miib(machine_config &config)
{
	// YM2149 in S3527
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// 1 Mini cartridge slot (with YRM-502)
	// 1 Module slot
	// S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 1, 1, "subrom");
	add_cartridge_slot<3>(config, MSX_SLOT_YAMAHA_MINICART, "minicart", 3, 1, msx_yamaha_minicart, nullptr);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x10000);   // 64KB Mapper RAM
	add_cartridge_slot<4>(config, MSX_SLOT_YAMAHA_EXPANSION, "module", 3, 3, msx_yamaha_60pin, "sfg05");

	msx1_v9938_pal(SND_YM2149, config, layout_msx_nocode);
	SOFTWARE_LIST(config, "minicart_list").set_original("msx_yamaha_minicart");
}

/* MSX - Yamaha CX5MIIC (Canada) */

/* MSX - Yamaha CX5MIIE (UK) */

/* MSX - Yamaha CX5MIIF (France) */

/* MSX - Yamaha CX5MIIG (Germany) */

/* MSX - Yamaha CX5MIIP (Spain) */

/* MSX - Yamaha CX5MIIS (Scandinavia) */

/* MSX - Yamaha CX5MIIU (USA) */

/* MSX - Yamaha YIS-503II */

ROM_START(yis503ii)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("yis503iibios.rom", 0x0000, 0x8000, CRC(3b08dc03) SHA1(4d0c37ad722366ac7aa3d64291c3db72884deb2d))
ROM_END

void msx1_v9938_state::yis503ii(machine_config &config)
{
	// YM2149 in S3527
	// FDC: None, 0 drives
	// S3527
	// 2 Cartridge slots
	// 1 Yamaha module slot (60 pin)

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 2, 0, 4);  // 64KB RAM
	add_cartridge_slot<3>(config, MSX_SLOT_YAMAHA_EXPANSION, "module", 3, 3, msx_yamaha_60pin, nullptr);

	msx1_v9938(SND_YM2149, config, layout_msx_nocode);
}

/* MSX - Yamaha YIS503-IIR Russian */

ROM_START(y503iir)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("yis503iirbios.rom", 0x0000, 0x8000, CRC(e751d55c) SHA1(807a823d4cac527c9f3758ed412aa2584c7f6d37))
// This is in the module slot by default
// ROM_LOAD("yis503iirnet.rom",  0xc000, 0x2000, CRC(0731db3f) SHA1(264fbb2de69fdb03f87dc5413428f6aa19511a7f))
ROM_END

void msx1_v9938_state::y503iir(machine_config &config)
{
	// YM2149 (in S-3527 MSX Engine)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// 1 Mini cartridge slot
	// 1 Yamaha module slot
	// S-3527 MSX Engine
	// V9938 VDP

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_cartridge_slot<3>(config, MSX_SLOT_YAMAHA_MINICART, "minicart", 3, 1, msx_yamaha_minicart, nullptr);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 2, 0, 4);  // 64KB RAM
	// This should have a serial network interface by default
	add_cartridge_slot<4>(config, MSX_SLOT_YAMAHA_EXPANSION, "module", 3, 3, msx_yamaha_60pin, nullptr);

	msx1_v9938_pal(SND_YM2149, config, layout_msx_ru);
	SOFTWARE_LIST(config, "minicart_list").set_original("msx_yamaha_minicart");
}

/* MSX - Yamaha YIS503-IIR Estonian */

ROM_START(y503iir2)
	ROM_REGION(0x10000, "mainrom", 0)
	ROM_LOAD("yis503ii2bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(1548cee3) SHA1(42c7fff25b1bd90776ac0aea971241aedce8947d)) // need verification
// This is in the module slot by default
// ROM_LOAD("yis503iirnet.rom",  0xc000, 0x2000, CRC(0731db3f) SHA1(264fbb2de69fdb03f87dc5413428f6aa19511a7f))
ROM_END

void msx1_v9938_state::y503iir2(machine_config &config)
{
	// YM2149 (in S-3527 MSX Engine)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// 1 Mini cartridge slot
	// 1 Yamaha module slot
	// S-3527 MSX Engine
	// V9938 VDP

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_cartridge_slot<3>(config, MSX_SLOT_YAMAHA_MINICART, "minicart", 3, 1, msx_yamaha_minicart, nullptr);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 2, 0, 4);  // 64KB RAM
	// This should have a serial network interface by default
	add_cartridge_slot<4>(config, MSX_SLOT_YAMAHA_EXPANSION, "module", 3, 3, msx_yamaha_60pin, nullptr);

	msx1_v9938_pal(SND_YM2149, config, layout_msx_ru);
	SOFTWARE_LIST(config, "minicart_list").set_original("msx_yamaha_minicart");
}

} // anonymous namespace

COMP(1986, ax200,      0,        0,     ax200,      msx,      msx1_v9938_state, empty_init, "Sakhr", "AX-200 (MSX1, Arabic/English)", 0)
COMP(1986, ax200m,     ax200,    0,     ax200m,     msx,      msx1_v9938_state, empty_init, "Sakhr", "AX-200M (MSX1, Arabic/English)", 0)
COMP(1985, svi738,     0,        0,     svi738,     msx,      msx1_v9938_state, empty_init, "Spectravideo", "SVI-738 (MSX1, International)", 0)
COMP(1987, svi738ar,   svi738,   0,     svi738ar,   msx,      msx1_v9938_state, empty_init, "Spectravideo", "SVI-738 (MSX1, Arabic)", 0)
COMP(1985, svi738dk,   svi738,   0,     svi738,     svi738dk, msx1_v9938_state, empty_init, "Spectravideo", "SVI-738 (MSX1, Denmark, Norway)", 0)
COMP(1986, svi738pl,   svi738,   0,     svi738,     msx,      msx1_v9938_state, empty_init, "Spectravideo", "SVI-738 (MSX1, Poland)", 0)
COMP(1985, svi738sp,   svi738,   0,     svi738,     msxsp,    msx1_v9938_state, empty_init, "Spectravideo", "SVI-738 (MSX1, Spain)", 0)
COMP(1985, svi738sw,   svi738,   0,     svi738,     svi738sw, msx1_v9938_state, empty_init, "Spectravideo", "SVI-738 (MSX1, Finland, Sweden)", 0)
COMP(1988, tadpc200a,  dpc200,   0,     tadpc200a,  msx,      msx1_v9938_state, empty_init, "Talent", "DPC-200A (MSX1, Argentina)", 0) // Should have a Spanish keyboard layout?
COMP(1984, cx5m128,    0,        0,     cx5m128,    msx,      msx1_v9938_state, empty_init, "Yamaha", "CX5M-128 (MSX1)", 0)
COMP(1984, cx5miib,    cx5m128,  0,     cx5miib,    msx,      msx1_v9938_state, empty_init, "Yamaha", "CX5MIIB (MSX1, Italy)", 0)
COMP(1985, yis503ii,   yis503,   0,     yis503ii,   msxjp,    msx1_v9938_state, empty_init, "Yamaha", "YIS503II (MSX1, Japan)", 0)
COMP(1985, y503iir,    yis503,   0,     y503iir,    msxru,    msx1_v9938_state, empty_init, "Yamaha", "YIS503IIR (MSX1, USSR)", 0)
COMP(1986, y503iir2,   yis503,   0,     y503iir2,   y503iir2, msx1_v9938_state, empty_init, "Yamaha", "YIS503IIR (MSX1, Estonian)", 0)



msx2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol

#include "emu.h"
#include "msx.h"
#include "msx_kanji12.h"
#include "msx_keyboard.h"
#include "msx_matsushita.h"
#include "msx_s1985.h"
#include "msx_systemflags.h"
#include "bus/msx/slot/bunsetsu.h"
#include "bus/msx/slot/disk.h"
#include "bus/msx/slot/fs4600.h"
#include "bus/msx/slot/fsa1fm.h"
#include "bus/msx/slot/msx_rs232.h"
#include "bus/msx/slot/msx_write.h"
#include "bus/msx/slot/music.h"
#include "bus/msx/slot/panasonic08.h"
#include "bus/msx/slot/ram.h"
#include "bus/msx/slot/ram_mm.h"
#include "bus/msx/slot/rom.h"
#include "bus/msx/slot/sony08.h"
#include "softlist_dev.h"

#include "msx_ar.lh"
#include "msx_ar_1fdd.lh"
#include "msx_ar_2fdd.lh"
#include "msx_jp.lh"
#include "msx_jp_1fdd.lh"
#include "msx_jp_2fdd.lh"
#include "msx_kr.lh"
#include "msx_kr_1fdd.lh"
#include "msx_ru.lh"
#include "msx_ru_1fdd.lh"
#include "msx_ru_2fdd.lh"
#include "msx_nocode.lh"
#include "msx_nocode_1fdd.lh"
#include "msx_nocode_2fdd.lh"
#include "msx_nocode_nocaps.lh"

using namespace msx_keyboard;


/***************************************************************************

  MSX2 machine drivers

Undumped and/or not emulated:
- AVT CPC-300 (prototype)
- Bawareth Perfect MSX2
- Daisen Sangyo MX-2021
- Laser MSX2 (unreleased)
- ML-TS2
- Philips NMS 8245 Home Banking (Italy)
- Perfect Perfect2 - MSX2
- Phonola NMS 8245
- Phonola NMS 8280
- Phonola VG-8235
- Sanyo MPC-25F
- Sanyo MPC-25FK
- Sanyo PCT-100
- Sony HB-F750 (prototype)
- Sony HB-G900D
- Sony HB-G900F
- Sony HB-T600
- Sony HB-T7
- Talent DPC-300
- Victor HC-90(A)
- Victor HC-90(B)
- Victor HC-90(V)
- Victor HC-90(T)
- Wandy CPC-300
***************************************************************************/

namespace {

class msx2_state : public msx2_base_state
{
public:
	msx2_state(const machine_config &mconfig, device_type type, const char *tag)
		: msx2_base_state(mconfig, type, tag, 21.477272_MHz_XTAL, 6)
	{
	}

	void ax350(machine_config &config);
	void ax350ii(machine_config &config);
	void ax350iif(machine_config &config);
	void ax370(machine_config &config);
	void ax500(machine_config &config);
	void canonv25(machine_config &config);
	void canonv30(machine_config &config);
	void canonv30f(machine_config &config);
	void cpc300(machine_config &config);
	void cpc300e(machine_config &config);
	void cpc330k(machine_config &config);
	void cpc400(machine_config &config);
	void cpc400s(machine_config &config);
	void cpc61(machine_config &config);
	void cpg120(machine_config &config);
	void cx7128(machine_config &config);
	void cx7m128(machine_config &config);
	void expert20(machine_config &config);
	void fpc900(machine_config &config);
	void kmc5000(machine_config &config);
	void mbh70(machine_config &config);
	void mlg1(machine_config &config);
	void mlg3(machine_config &config);
	void mlg10(machine_config &config);
	void mlg30(machine_config &config);
	void mlg30_2(machine_config &config);
	void mpc2300(machine_config &config);
	void mpc2500f(machine_config &config);
	void mpc25fd(machine_config &config);
	void mpc25fs(machine_config &config);
	void mpc27(machine_config &config);
	void fs4500(machine_config &config);
	void fs4600f(machine_config &config);
	void fs4700f(machine_config &config);
	void fs5000f2(machine_config &config);
	void fs5500f1(machine_config &config);
	void fs5500f2(machine_config &config);
	void fsa1(machine_config &config);
	void fsa1a(machine_config &config);
	void fsa1f(machine_config &config);
	void fsa1fm(machine_config &config);
	void fsa1mk2(machine_config &config);
	void fstm1(machine_config &config);
	void hbf1(machine_config &config);
	void hbf1ii(machine_config &config);
	void hbf1xd(machine_config &config);
	void hbf5(machine_config &config);
	void hbf500(machine_config &config);
	void hbf500_2(machine_config &config);
	void hbf500f(machine_config &config);
	void hbf500p(machine_config &config);
	void hbf700d(machine_config &config);
	void hbf700f(machine_config &config);
	void hbf700p(machine_config &config);
	void hbf700s(machine_config &config);
	void hbf900(machine_config &config);
	void hbf900a(machine_config &config);
	void hbf9p(machine_config &config);
	void hbf9pr(machine_config &config);
	void hbf9s(machine_config &config);
	void hbg900ap(machine_config &config);
	void hbg900p(machine_config &config);
	void hotbit20(machine_config &config);
	void hx23(machine_config &config);
	void hx23f(machine_config &config);
	void hx33(machine_config &config);
	void hx34(machine_config &config);
	void mbh3(machine_config &config);
	void nms8220(machine_config &config);
	void nms8245(machine_config &config);
	void nms8245f(machine_config &config);
	void nms8250(machine_config &config);
	void nms8255(machine_config &config);
	void nms8255f(machine_config &config);
	void nms8260(machine_config &config);
	void nms8280(machine_config &config);
	void nms8280f(machine_config &config);
	void nms8280g(machine_config &config);
	void phc23(machine_config &config);
	void phc23jb(machine_config &config);
	void phc55fd2(machine_config &config);
	void phc77(machine_config &config);
	void tpc310(machine_config &config);
	void tpp311(machine_config &config);
	void tps312(machine_config &config);
	void ucv102(machine_config &config);
	void vg8230(machine_config &config);
	void vg8235(machine_config &config);
	void vg8235f(machine_config &config);
	void vg8240(machine_config &config);
	void victhc80(machine_config &config);
	void victhc90(machine_config &config);
	void victhc90a(machine_config &config);
	void victhc95(machine_config &config);
	void victhc95a(machine_config &config);
	void y503iiir(machine_config &config);
	void y503iiire(machine_config &config);
	void yis604(machine_config &config);
	void y805128(machine_config &config);
	void y805128r2(machine_config &config);
	void y805128r2e(machine_config &config);
	void y805256(machine_config &config);
};

/* MSX2 - AVT CPC-300 (prototype) */

/* MSX2 - Bawareth Perfect MSX2 */

/* MSX2 - Canon V-25 */

ROM_START(canonv25)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("v25bios.rom", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("v25ext.rom", 0x0000, 0x4000, CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33))
ROM_END

void msx2_state::canonv25(machine_config &config)
{
	// YM2149 (in S3527 MSX Engine)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// S3527
	// 64KB VRAM

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x10000); // 64KB Mapper RAM

	msx2(SND_YM2149, config, layout_msx_jp);
	msx2_64kb_vram(config);
}

/* MSX2 - Canon V-30F */

ROM_START(canonv30f)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("v30fbios.rom", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("v30fext.rom",  0x0000, 0x4000, CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("v30fdisk.rom", 0x0000, 0x4000, CRC(b499277c) SHA1(33117c47543a4eb00392cb9ff4e503004999a97a))
ROM_END

void msx2_state::canonv30f(machine_config &config)
{
	// YM2149 (in S3527 MSX Engine)
	// FDC: mb8877a, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// S3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK7_MB8877, "disk", 3, 1, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000); // 128KB Mapper RAM

	msx2(SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX2 - Daewoo CPC-300 */

ROM_START(cpc300)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("300bios.rom", 0x0000, 0x8000, CRC(53850907) SHA1(affa3c5cd8db79a1450ad8a7f405a425b251653d))

	ROM_REGION(0x8000, "subrom", 0)
	ROM_LOAD("300ext.rom", 0x0000, 0x8000, CRC(d64da39c) SHA1(fb51c505adfbc174df94289fa894ef969f5357bc))

	ROM_REGION(0x8000, "hangul", 0)
	ROM_LOAD("300han.rom", 0x0000, 0x8000, CRC(e78cd87f) SHA1(47a9d9a24e4fc6f9467c6e7d61a02d45f5a753ef))
ROM_END

void msx2_state::cpc300(machine_config &config)
{
	// YM2149 (in S-1985 MSX Engine)
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Expansion slot
	// S-1985 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "hangul", 0, 1, 1, 2, "hangul");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 0, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0x80);   // 128KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 3, 0, 2, "subrom");
	add_cartridge_slot<1>(config, 1);
	// Expansion slot in slot #3

	MSX_S1985(config, "s1985", 0);
	msx2(SND_YM2149, config, layout_msx_kr);
}

/* MSX2 - Daewoo CPC-300E */

ROM_START(cpc300e)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("300ebios.rom", 0x0000, 0x8000, CRC(53850907) SHA1(affa3c5cd8db79a1450ad8a7f405a425b251653d))

	ROM_REGION(0x8000, "subrom", 0)
	ROM_LOAD("300eext.rom",  0x0000, 0x8000, CRC(c52bddda) SHA1(09f7d788698a23aa7eec140237e907d4c37cbfe0))

	ROM_REGION(0x8000, "hangul", 0)
	ROM_LOAD("300ehan.rom", 0x0000, 0x8000, CRC(e78cd87f) SHA1(47a9d9a24e4fc6f9467c6e7d61a02d45f5a753ef))
ROM_END

void msx2_state::cpc300e(machine_config &config)
{
	// YM2149 in S1985
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Expansion slot
	// No clockchip
	// No joystick ports
	// S1985

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "hangul", 0, 1, 1, 2, "hangul");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 0, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0x80);   // 128KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 3, 0, 2, "subrom");
	add_cartridge_slot<1>(config, 1);
	// Expansion slot in slot #3

	MSX_S1985(config, "s1985", 0);
	msx2(SND_YM2149, config, layout_msx_kr);
}

/* MSX2 - Daewoo CPC-330K */

ROM_START(cpc330k)
	ROM_REGION(0x18000, "mainrom", 0)
	ROM_LOAD("330kbios.rom", 0x0000, 0x8000, BAD_DUMP CRC(53850907) SHA1(affa3c5cd8db79a1450ad8a7f405a425b251653d)) // need verification

	ROM_REGION(0x8000, "subrom", 0)
	ROM_LOAD("330kext.rom",  0x0000, 0x8000, BAD_DUMP CRC(5d685cca) SHA1(97afbadd8fe34ab658cce8222a27cdbe19bcef39)) // need verification

	ROM_REGION(0x8000, "hangul", 0)
	ROM_LOAD("330khan.rom", 0x0000, 0x4000, BAD_DUMP CRC(3d6dd335) SHA1(d2b058989a700ca772b9591f42c01ed0f45f74d6)) // need verification
ROM_END

void msx2_state::cpc330k(machine_config &config)
{
	// AY8910
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 1 Expansion slot
	// DW64MX1
	// Ergonomic keyboard, 2 builtin game controllers

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "hangul", 0, 1, 1, 2, "hangul");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 0, 2, 0, 4).set_total_size(0x20000);   // 128KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 3, 0, 2, "subrom");
	add_cartridge_slot<1>(config, 1);

	msx2(SND_AY8910, config, layout_msx_kr);
}

/* MSX2 - Daewoo CPC-400 */

ROM_START(cpc400)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("400bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(53850907) SHA1(affa3c5cd8db79a1450ad8a7f405a425b251653d)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("400disk.rom", 0x0000, 0x4000, BAD_DUMP CRC(5fa517df) SHA1(914f6ccb25d78621186001f2f5e2aaa2d628cd0c)) // need verification

	ROM_REGION(0x8000, "subrom", 0)
	ROM_LOAD("400ext.rom",  0x0000, 0x8000, BAD_DUMP CRC(2ba104a3) SHA1(b6d3649a6647fa9f6bd61efc317485a20901128f)) // need verification

	ROM_REGION(0x8000, "hangul", 0)
	ROM_LOAD("400han.rom", 0x0000, 0x8000, BAD_DUMP CRC(a8ead5e3) SHA1(87936f808423dddfd00629056d6807b4be1dc63e)) // need verification

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("400kfn.rom", 0, 0x20000, BAD_DUMP CRC(b663c605) SHA1(965f4982790f1817bcbabbb38c8777183b231a55)) // need verification
ROM_END

void msx2_state::cpc400(machine_config &config)
{
	// AY8910
	// FDC: mb8877a, 1 3.5" DS?DD drive
	// 1 Cartridge slot
	// 1 Expansion slot
	// The check for an external kanji ROM always fails; deliberately? Expects other type of kanji extension? Bad dump?

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "hangul", 0, 1, 1, 2, "hangul");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 0, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0x80);   // 128KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 3, 0, 2, "subrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_disk_mirrored(config, MSX_SLOT_DISK2_MB8877, "disk", 2, 1, 2, "diskrom");
	// Expansion slot in slot #3

	msx2(SND_AY8910, config, layout_msx_kr_1fdd);
}

/* MSX2 - Daewoo CPC-400S */

ROM_START(cpc400s)
	ROM_REGION(0x10000, "mainrom", 0)
	// bios, disk, and sub
	ROM_LOAD("400sbios.u38", 0x0000, 0x10000, CRC(dbb0f26d) SHA1(75f5f0a5a2e8f0935f33bb3cf07b83dd3e5f3347))

	ROM_REGION(0x10000, "hangul", 0)
	// hangul and kanji driver
	ROM_LOAD("400shan.u44", 0x0000, 0x10000, CRC(5fdb493c) SHA1(6b640c1d8cbeda6ca7d6facd16a206b62e059eee))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("400skfn.rom", 0, 0x20000, CRC(fa85368c) SHA1(30fff22e3e3d464993707488442721a5e56a9707))
ROM_END

void msx2_state::cpc400s(machine_config &config)
{
	// AY8910
	// FDC: mb8877a, 1 3.5" DS?DD drive
	// 1 Cartridge slot
	// DW64MX1
	// The check for an external kanji rom always fails; deliberately? expects other type of kanji extension? bad dump?

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "hangul1", 0, 1, 1, 1, "hangul", 0x4000);
	add_internal_slot(config, MSX_SLOT_ROM, "kfn", 0, 1, 2, 1, "hangul", 0xc000);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 0, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0x80);   // 128KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 3, 0, 1, "mainrom", 0xc000);
	add_internal_slot(config, MSX_SLOT_ROM, "hangul2", 0, 3, 1, 1, "hangul");
	add_cartridge_slot<1>(config, 1);
	add_internal_disk_mirrored(config, MSX_SLOT_DISK2_MB8877, "disk", 2, 1, 2, "mainrom", 0x8000);
	// Expansion slot in slot #3

	msx2(SND_AY8910, config, layout_msx_kr_1fdd);
}

/* MSX2 - Daewoo Zemmix CPC-61 */

ROM_START(cpc61)
	ROM_REGION(0x10000, "mainrom", 0)
	ROM_LOAD("cpc64122.rom", 0x0000, 0x10000, CRC(914dcad9) SHA1(dd3c39c8cfa06ec69f54c95c3b2291e3da7bd4f2))
ROM_END

void msx2_state::cpc61(machine_config &config)
{
	// AY8910
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// DW64MX1
	// No keyboard, but a keyboard connector
	// No printer port
	// No cassette port

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "hangul", 0, 1, 1, 1, "mainrom", 0xc000);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 0, 2, 0, 4).set_total_size(0x10000); // 64KB Mapper RAM?
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 3, 0, 1, "mainrom", 0x8000);
	add_cartridge_slot<1>(config, 1);

	m_hw_def.has_cassette(false).has_printer_port(false);
	msx2(SND_AY8910, config, layout_msx_nocode_nocaps);
}

/* MSX2 - Daewoo Zemmix CPG-120 Normal */

ROM_START(cpg120)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("cpg120bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(b80c8e45) SHA1(310a02a9746bc062834e0cf2fabf7f3e0f7e829e)) // need verification

	ROM_REGION(0x8000, "subrom", 0)
	ROM_LOAD("cpg120ext.rom", 0x0000, 0x8000, BAD_DUMP CRC(b3d740b4) SHA1(7121c3c5ee6e4931fceda14a06f4c0e3b8eda437)) // need verification

	ROM_REGION(0x4000, "music", 0)
	ROM_LOAD("cpg128music.rom", 0x0000, 0x4000, BAD_DUMP CRC(73491999) SHA1(b9ee4f30a36e283a2b1b9a28a70ab9b9831570c6)) // need verification
ROM_END

void msx2_state::cpg120(machine_config &config)
{
	// By pressing the turbo button the CPU can be switched between 3.579545 and 5.369317 MHz
	// YM2149 (in S1985)
	// FDC: None, 0 drives
	// 2 Cartridge slots?
	// S-1985 MSX Engine
	// V9958 VDP
	// FM built in
	// No keyboard, but a keyboard connector?
	// No clock chip?
	// No printer port

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 0, 2, 0, 4).set_total_size(0x20000); // 128KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "saubrom", 0, 3, 0, 2, "subrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_MUSIC, "mus", 2, 1, 1, "music").set_ym2413_tag(m_ym2413);
	add_cartridge_slot<2>(config, 3);

	MSX_S1985(config, "s1985", 0);

	msx_ym2413(config);
	m_hw_def.has_printer_port(false);
	msx2_v9958_base(SND_AY8910, config, layout_msx_nocode_nocaps);
	msx2_add_softlists(config);
}

/* MSX2 - Daisen Sangyo MX-2021 */

/* MSX2 - Fenner FPC-900 */

ROM_START(fpc900)
	ROM_REGION(0x10000, "mainrom", 0)
	ROM_LOAD("fpc900bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(6cdaf3a5) SHA1(6103b39f1e38d1aa2d84b1c3219c44f1abb5436e)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("fpc900ext.rom", 0x0000, 0x4000, BAD_DUMP CRC(66237ecf) SHA1(5c1f9c7fb655e43d38e5dd1fcc6b942b2ff68b02)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("fpc900disk.rom", 0x0000, 0x4000, BAD_DUMP CRC(ca3307d3) SHA1(c3efedda7ab947a06d9345f7b8261076fa7ceeef)) // need verification
ROM_END

void msx2_state::fpc900(machine_config &config)
{
	// YM2149 (in S-3527 MSX Engine)
	// FDC: WD2793?, 1 3.5" DSDD drive
	// 2? Cartridge slots
	// S-3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x40000); // 256KB? Mapper RAM
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793, "disk", 3, 3, 1, 2, "diskrom");

	msx2_pal(SND_YM2149, config, layout_msx_nocode_1fdd);
}

/* MSX2 - GR8Bit (should probably be a separate driver) */

/* MSX2 - Gradiente Expert 2.0 */

ROM_START(expert20)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("exp20bios.rom", 0x0000, 0x8000, CRC(6bacdce4) SHA1(9c43106dba3ae2829e9a11dffa9d000ed6d6454c))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("exp20ext.rom", 0x0000, 0x4000, CRC(08ced880) SHA1(4f2a7e0172f0214f025f23845f6e053d0ffd28e8))

	ROM_REGION(0x4000, "xbasic", 0)
	ROM_LOAD("xbasic2.rom", 0x0000, 0x4000, CRC(2825b1a0) SHA1(47370bec7ca1f0615a54eda548b07fbc0c7ef398))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("microsoldisk.rom", 0x0000, 0x4000, CRC(6704ef81) SHA1(a3028515ed829e900cc8deb403e17b09a38bf9b0))
ROM_END

void msx2_state::expert20(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: microsol, 1? 3.5"? DS?DD drive
	// 2 Cartridge slots?

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 1, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "xbasic", 1, 1, 1, 1, "xbasic");
	add_internal_disk(config, MSX_SLOT_DISK5_WD2793, "disk", 1, 3, 1, 1, "diskrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 2, 0, 0, 4).set_total_size(0x20000).set_unused_bits(0x80);   // 128KB Mapper RAM
	add_cartridge_slot<2>(config, 3);

	msx2_pal(SND_AY8910, config, layout_msx_nocode_1fdd);
}

/* MSX2 - Hitachi MB-H3 */

ROM_START(mbh3)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("mbh3bios.rom", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("mbh3sub.rom", 0x000, 0x4000, CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33))

	ROM_REGION(0x8000, "firmware", 0)
	ROM_LOAD("mbh3firm.rom", 0x0000, 0x8000, CRC(8c844173) SHA1(74ee82cc09ffcf78f6e9a3f0d993f8f80d81444c))
ROM_END

void msx2_state::mbh3(machine_config &config)
{
	// YM2149 in S3527
	// FDC: None, 0 drives
	// 64KB VRAM
	// Touchpad
	// 2 Cartrdige slots
	// S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0 ,1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 3, 0, 1, 2, "firmware");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000); // 128KB Mapper RAM

	msx2(SND_YM2149, config, layout_msx_jp);
	msx2_64kb_vram(config);
}

/* MSX2 - Hitachi MB-H70 */

ROM_START(mbh70)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("mbh70bios.rom" , 0x0000, 0x8000, BAD_DUMP CRC(a27c563d) SHA1(c1e46c00f1e38fc9e0ab487bf0513bd93ce61f3f)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("mbh70ext.rom", 0x0000, 0x4000, BAD_DUMP CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("mbh70disk.rom", 0x0000, 0x4000, BAD_DUMP CRC(05661a3f) SHA1(e695fc0c917577a3183901a08ca9e5f9c60b8317)) // need verification

	ROM_REGION(0x100000, "firmware", 0)
	ROM_LOAD("mbh70halnote.rom", 0x0000, 0x100000, BAD_DUMP CRC(40313fec) SHA1(1af617bfd11b10a71936c606174a80019762ea71)) // need verification

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("mbh70kfn.rom", 0x0000, 0x20000, BAD_DUMP CRC(d23d4d2d) SHA1(db03211b7db46899df41db2b1dfbec972109a967)) // need verification
ROM_END

void msx2_state::mbh70(machine_config &config)
{
	// YM2149 (in S-1985)
	// FDC: WD2793?, 2? 3.5" DSDD drives
	// S-1985 MSX Engine
	// 3 Cartridge slots?

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_SONY08, "firmware", 0, 3, 0, 4, "firmware");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_N_2_DRIVES, "disk", 3, 0, 1, 2, "diskrom").use_motor_for_led();
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000); // 128KB Mapper RAM

	MSX_S1985(config, "s1985", 0);

	msx2(SND_YM2149, config, layout_msx_jp_2fdd);
}

/* MSX2 - Kawai KMC-5000 */

ROM_START(kmc5000)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("kmc5000bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("kmc5000ext.rom", 0x0000, 0x4000, BAD_DUMP CRC(43e7a7fc) SHA1(0fbd45ef3dd7bb82d4c31f1947884f411f1ca344)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	// FDC register contents at 3ff8-3fff
	ROM_LOAD("kmc5000disk.rom", 0x0000, 0x4000, BAD_DUMP CRC(e25cacca) SHA1(607cfca605eaf82e3efa33459d6583efb7ecc13b)) // need verification

	ROM_REGION(0x8000, "kdr", 0)
	ROM_LOAD("kmc5000kdr.rom", 0x0000, 0x8000, BAD_DUMP CRC(2dbea5ec) SHA1(ea35cc2cad9cfdf56cae224d8ee41579de37f000)) // need verification

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("kmc5000kfn.rom", 0, 0x20000, BAD_DUMP CRC(c61ddc5d) SHA1(5e872d5853698731a0ed22fb72dbcdfd59cd19c3)) // need verification
ROM_END

void msx2_state::kmc5000(machine_config &config)
{
	// YM2149 (in S-1985)
	// FDC: TC8566AF?, 1? 3.5" DSDD drive
	// S-1985 MSX Engine
	// 2? Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x10000);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 1, 1, 2, "kdr");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK3_TC8566, "disk", 3, 2, 1, 2, "diskrom");

	MSX_S1985(config, "s1985", 0);

	msx2(SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX2 - Laser MSX2 (unreleased) */

/* MSX2 - Mitsubishi ML-G1 */

ROM_START(mlg1)
	// ic8f - hn27256g-25 (paint?)
	// ic10f - hn613128pn33-sp2 - t704p874h52 (sub?)
	// ic11f - hn613256pdc1 - t704p874h50 (bios?)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("mlg1bios.rom", 0x0000, 0x8000, CRC(0cc7f817) SHA1(e4fdf518a8b9c8ab4290c21b83be2c347965fc24))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("mlg1ext.rom", 0x0000, 0x4000, CRC(dc0951bd) SHA1(1e9a955943aeea9b1807ddf1250ba6436d8dd276))

	ROM_REGION(0x8000, "firmware", 0)
	ROM_LOAD("mlg1paint.rom", 0x0000, 0x8000, CRC(64df1750) SHA1(5cf0abca6dbcf940bc33c433ecb4e4ada02fbfe6))
ROM_END

void msx2_state::mlg1(machine_config &config)
{
	// YM2149 (in S3527)
	// FDC: None, 0 drives
	// S3527 MSX Engine
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x10000); // 64KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 3, 3, 0, 2, "firmware");

	msx2_pal(SND_YM2149, config, layout_msx_nocode);
}

/* MSX2 - Mitsubishi ML-G3 */

ROM_START(mlg3)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("mlg3bios.rom", 0x0000, 0x8000, CRC(0cc7f817) SHA1(e4fdf518a8b9c8ab4290c21b83be2c347965fc24))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("mlg3ext.rom", 0x0000, 0x4000, CRC(dc0951bd) SHA1(1e9a955943aeea9b1807ddf1250ba6436d8dd276))

	ROM_REGION(0x4000, "diskrom", 0)
	// FDC register contents at 3ff8-3fff
	ROM_LOAD("mlg3disk.rom", 0x0000, 0x4000, BAD_DUMP CRC(b94ebc7a) SHA1(30ba1144c872a0bb1c91768e75a2c28ab1f4e3c6))

	ROM_REGION(0x4000, "rs232", 0)
	ROM_LOAD("mlg3rs232c.rom", 0x0000, 0x2000, CRC(849b325e) SHA1(b1ac74c2550d553579c1176f5dfde814218ec311))
	ROM_RELOAD(0x2000, 0x2000)
ROM_END

void msx2_state::mlg3(machine_config &config)
{
	// YM2149 (in S3527)
	// FDC: wd2793?, 1 3.5" DSDD drive
	// S3527 MSX Engine
	// 4 Cartridge slots (3 internal, 1 taken by RS232 board)
	// RS232 with switch. What does the switch do?

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_N, "disk", 3, 0, 1, 2, "diskrom").use_motor_for_led();
	add_cartridge_slot<3>(config, 3, 1);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x10000); // 64KB Mapper RAM
	add_internal_slot_irq<4>(config, MSX_SLOT_RS232_MITSUBISHI, "rs232", 3, 3, 1, 1, "rs232");

	msx2_pal(SND_YM2149, config, layout_msx_nocode_1fdd);
}

/* MSX2 - Mitsubishi ML-G10 */

ROM_START(mlg10)
	// ic8f - 00000210 - hn27256g-25
	// ic10f - 5j1 - hn613128p - m50 - t704p874h42-g
	// ic11f - 5j1 - hn613256p - cn9 - t704p874h40-g
	ROM_REGION(0xc000, "mainrom", 0)
	ROM_LOAD("mlg10bios.rom", 0x0000, 0x8000, CRC(a27c563d) SHA1(c1e46c00f1e38fc9e0ab487bf0513bd93ce61f3f))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("mlg10ext.rom", 0x0000, 0x4000, CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33))

	ROM_REGION(0x8000, "firmware", 0)
	ROM_LOAD("g10firm.rom", 0x0000, 0x8000, CRC(dd4007f9) SHA1(789bb6cdb2d1ed0348f36336da11b149d74e4d9f))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("mlg10kfn.rom", 0, 0x20000, CRC(d23d4d2d) SHA1(db03211b7db46899df41db2b1dfbec972109a967))
ROM_END

void msx2_state::mlg10(machine_config &config)
{
	// YM2149 (in S3527)
	// FDC: None, 0 drives
	// S3527 MSX Engine
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x10000); // 64KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 3, 3, 0, 2, "firmware");

	msx2(SND_YM2149, config, layout_msx_jp);
}

/* MSX2 - Mitsubishi ML-G30 Model 1 */

ROM_START(mlg30)
	ROM_REGION(0x10000, "mainrom", 0)
	ROM_LOAD("g30bios.rom", 0x0000, 0x8000, CRC(a27c563d) SHA1(c1e46c00f1e38fc9e0ab487bf0513bd93ce61f3f))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("g30ext.rom", 0x0000, 0x4000, CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("g30disk.rom", 0x0000, 0x4000, CRC(a90be8d5) SHA1(f7c3ac138918a493eb91628ed88cf37999059579))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("g30kfn.rom", 0x0000, 0x20000, CRC(d23d4d2d) SHA1(db03211b7db46899df41db2b1dfbec972109a967))
ROM_END

void msx2_state::mlg30(machine_config &config)
{
	// YM2149 (in S3527)
	// FDC: wd2793/tc8566af?, 1 3.5" DSDD drives
	// 4 Cartridge slots
	// S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_N, "disk", 3, 0, 1, 2, "diskrom").use_motor_for_led();
	add_cartridge_slot<3>(config, 3, 1);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000);   // 128KB Mapper RAM
	add_cartridge_slot<4>(config, 3, 3);

	msx2(SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX2 - Mitsubishi ML-G30 Model 2 */

ROM_START(mlg30_2)
	ROM_REGION(0x10000, "mainrom", 0)
	ROM_LOAD("g30bios.rom", 0x0000, 0x8000, CRC(a27c563d) SHA1(c1e46c00f1e38fc9e0ab487bf0513bd93ce61f3f))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("g30ext.rom", 0x0000, 0x4000, CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("g30disk.rom", 0x0000, 0x4000, CRC(995e6bf6) SHA1(6069d63c68b03fa56de040fb5f52eeadbffe2a2c))

	ROM_REGION(0x4000, "rs232", 0)
	ROM_LOAD("g30rs232c.rom", 0x0000, 0x2000, CRC(15d6ba9e) SHA1(782e54cf88eb4a974631eaa707aad97d3eb1ea14))
	ROM_RELOAD(0x2000, 0x2000)

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("g30kfn.rom", 0x0000, 0x20000, CRC(d23d4d2d) SHA1(db03211b7db46899df41db2b1dfbec972109a967))
ROM_END

void msx2_state::mlg30_2(machine_config &config)
{
	// YM2149 (in S3527)
	// FDC: wd2793/tc8566af?, 2 3.5" DSDD drives
	// 4 Cartridge slots (1 taken by RS232 board)
	// S3527
	// RS232 with switch. What does the switch do?

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_N_2_DRIVES, "disk", 3, 0, 1, 2, "diskrom").use_motor_for_led();
	add_cartridge_slot<3>(config, 3, 1);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000);   // 128KB Mapper RAM
	add_internal_slot_irq<4>(config, MSX_SLOT_RS232_MITSUBISHI, "rs232", 3, 3, 1, 1, "rs232");

	msx2(SND_YM2149, config, layout_msx_jp_2fdd);
}

/* MSX2 - ML-TS100 (should be a separate driver) */

/* MSX2 - ML-TS100M2 (should be a separate driver) */

/* MSX2 - ML-TS2 */

/* MSX2 - NTT Captain Multi-Station (should be a separate driver due to the added V99C37-F) */

/* MSX2 - National FS-4500 */

ROM_START(fs4500)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("4500bios.rom", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("4500ext.rom", 0x0000, 0x4000, CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33))

	ROM_REGION(0x4000, "font", 0)
	ROM_LOAD("4500font.rom", 0x0000, 0x4000, CRC(4bd54f95) SHA1(3ce8e35790eb4689b21e14c7ecdd4b63943ee158))

	ROM_REGION(0x8000, "jukugo", 0)
	ROM_LOAD("4500buns.rom", 0x0000, 0x8000, CRC(c9398e11) SHA1(e89ea1e8e583392e2dd9debb8a4b6a162f58ba91))

	ROM_REGION(0x8000, "jusho", 0)
	ROM_LOAD("4500jush.rom", 0x0000, 0x8000, CRC(4debfd2d) SHA1(6442c1c5cece64c6dae90cc6ae3675f070d93e06))

	ROM_REGION(0xc000, "wordpro1", 0)
	ROM_LOAD("4500wor1.rom", 0x0000, 0xc000, CRC(0c8b5cfb) SHA1(3f047469b62d93904005a0ea29092e892724ce0b))

	ROM_REGION(0xc000, "wordpro2", 0)
	ROM_LOAD("4500wor2.rom", 0x0000, 0xc000, CRC(d9909451) SHA1(4c8ea05c09b40c41888fa18db065575a317fda16))

	ROM_REGION(0x4000, "kdr1", 0)
	ROM_LOAD("4500kdr1.rom", 0x0000, 0x4000, CRC(f8c7f0db) SHA1(df07e89fa0b1c7874f9cdf184c136f964fea4ff4))

	ROM_REGION(0x4000, "kdr2", 0)
	ROM_LOAD("4500kdr2.rom", 0x0000, 0x4000, CRC(69e87c31) SHA1(c63db26660da96af56f8a7d3ea18544b9ae5a37c))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("4500kfn.rom", 0, 0x20000, CRC(956dc96d) SHA1(9ed3ab6d893632b9246e91b412cd5db519e7586b))

	ROM_REGION(0x20000, "bunsetsu", 0)
	ROM_LOAD("4500budi.rom", 0, 0x20000, CRC(f94590f8) SHA1(1ebb06062428fcdc66808a03761818db2bba3c73))
ROM_END

void msx2_state::fs4500(machine_config &config)
{
	// YM2149 (in S3527 MSX Engine)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// S3527 MSX Engine
	// Matsushita switched device
	// 8KB SRAM (NEC D4364C-15L)
	// Builtin thermal printer
	// Switch to switch between JP50on and JIS keyboard layout
	// Switch to switch between internal and external printer
	// Switch to bypass the firmware

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "font", 0, 2, 0, 1, "font");
	add_internal_slot(config, MSX_SLOT_BUNSETSU, "jukugo", 0, 2, 1, 2, "jukugo").set_bunsetsu_region_tag("bunsetsu");
	add_internal_slot(config, MSX_SLOT_ROM, "jusho", 0, 3, 1, 2, "jusho");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "wordpro1", 3, 0, 0, 3, "wordpro1");
	add_internal_slot(config, MSX_SLOT_ROM, "kdr1", 3, 0, 3, 1, "kdr1");
	add_internal_slot(config, MSX_SLOT_ROM, "wordpro2", 3, 1, 0, 3, "wordpro2");
	add_internal_slot(config, MSX_SLOT_ROM, "kdr2", 3, 1, 3, 1, "kdr2");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 2, 0, 4);  // 64KB RAM

	MSX_MATSUSHITA(config, "matsushita", 0);
	msx2(SND_YM2149, config, layout_msx_jp);
}

/* MSX2 - National FS-4600F */

ROM_START(fs4600f)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("4600bios.rom", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("4600ext.rom", 0x0000, 0x4000, CRC(43e7a7fc) SHA1(0fbd45ef3dd7bb82d4c31f1947884f411f1ca344))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("4600disk.rom", 0x0000, 0x4000, CRC(ae4e65b7) SHA1(073feb8bb645d935e099afaf61e6f04f52adee42))

	ROM_REGION(0x4000, "font1", 0)
	ROM_LOAD("4600fon1.rom", 0x0000, 0x4000, CRC(7391389b) SHA1(31292b9ca9fe7d1d8833530f44c0a5671bfefe4e))

	ROM_REGION(0x4000, "font2", 0)
	ROM_LOAD("4600fon2.rom", 0x0000, 0x4000, CRC(c3a6b445) SHA1(02155fc25c9bd23e1654fe81c74486351e1ecc28))

	ROM_REGION(0x8000, "kdr", 0)
	ROM_LOAD("4600kdr.rom", 0x0000, 0x8000, CRC(b2db6bf5) SHA1(3a9a942ed888dd641cddf8deada1879c454df3c6))

	ROM_REGION(0x100000, "firmware", 0)
	ROM_LOAD("4600firm.rom", 0x0000, 0x100000, CRC(1df57472) SHA1(005794c10a4237de3907ba4a44d436078d3c06c2))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("4600kfn.rom", 0, 0x20000, CRC(c61ddc5d) SHA1(5e872d5853698731a0ed22fb72dbcdfd59cd19c3))

	/* Matsushita 12 dots Kanji ROM must be emulated */
	ROM_REGION(0x20000, "kanji12", 0)
	ROM_LOAD("4600kf12.rom", 0, 0x20000, CRC(340d1ef7) SHA1(a7a23dc01314e88381eee88b4878b39931ab4818))
ROM_END

void msx2_state::fs4600f(machine_config &config)
{
	// YM2149 (in S-1985 MSX Engine)
	// FDC: mb8877a, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// S-1985 MSX Engine
	// 8KB SRAM (NEC D4364C-15L)
	// Builtin thermal printer
	// Switch to switch between JP50on and JIS keyboard layout
	// Switch to switch between internal and external printer
	// Switch to bypass the firmware

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "font1", 0, 2, 0, 1, "font1");
	add_internal_slot(config, MSX_SLOT_ROM, "kdr", 0, 2, 1, 2, "kdr");
	add_internal_slot(config, MSX_SLOT_ROM, "font2", 0, 3, 0, 1, "font2");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_FS4600, "firmware", 3, 1, 0, 4, "firmware");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0x80);   // 128KB Mapper RAM
	add_internal_disk_mirrored(config, MSX_SLOT_DISK2_MB8877, "disk", 3, 3, 1, 2, "diskrom");

	msx_kanji12_device &kanji12(MSX_KANJI12(config, "kanji12", 0));
	kanji12.set_rom_start("kanji12");

	MSX_S1985(config, "s1985", 0);

	msx2(SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX2 - National FS-4700 */

ROM_START(fs4700f)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("4700bios.rom", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("4700ext.rom", 0x0000, 0x4000, CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33))

	ROM_REGION(0x4000, "diskrom", 0)
	// ic403 - disk basic - copyright mei - 1986 das4700j1
	ROM_LOAD("4700disk.rom",  0x0000, 0x4000, CRC(1e7d6512) SHA1(78cd7f847e77fd8cd51a647efb2725ba93f4c471))

	ROM_REGION(0x4000, "font", 0)
	ROM_LOAD("4700font.rom", 0x0000, 0x4000, CRC(4bd54f95) SHA1(3ce8e35790eb4689b21e14c7ecdd4b63943ee158))

	ROM_REGION(0x8000, "jukugo", 0)
	ROM_LOAD("4700buns.rom", 0x0000, 0x8000, CRC(c9398e11) SHA1(e89ea1e8e583392e2dd9debb8a4b6a162f58ba91))

	ROM_REGION(0x8000, "jusho", 0)
	ROM_LOAD("4700jush.rom", 0x0000, 0x8000, CRC(4debfd2d) SHA1(6442c1c5cece64c6dae90cc6ae3675f070d93e06))

	ROM_REGION(0xc000, "wordpro1", 0)
	ROM_LOAD("4700wor1.rom", 0x0000, 0xc000, CRC(5f39a727) SHA1(f5af1d2a8bcf247f78847e1a9d995e581df87e8e))

	ROM_REGION(0xc000, "wordpro2", 0)
	ROM_LOAD("4700wor2.rom", 0x0000, 0xc000, CRC(d9909451) SHA1(4c8ea05c09b40c41888fa18db065575a317fda16))

	ROM_REGION(0x4000, "kdr1", 0)
	ROM_LOAD("4700kdr1.rom", 0x0000, 0x4000, CRC(f8c7f0db) SHA1(df07e89fa0b1c7874f9cdf184c136f964fea4ff4))

	ROM_REGION(0x4000, "kdr2", 0)
	ROM_LOAD("4700kdr2.rom", 0x0000, 0x4000, CRC(69e87c31) SHA1(c63db26660da96af56f8a7d3ea18544b9ae5a37c))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("4700kfn.rom", 0, 0x20000, CRC(956dc96d) SHA1(9ed3ab6d893632b9246e91b412cd5db519e7586b))

	ROM_REGION(0x20000, "bunsetsu", 0)
	ROM_LOAD("4700budi.rom", 0, 0x20000, CRC(f94590f8) SHA1(1ebb06062428fcdc66808a03761818db2bba3c73))
ROM_END

void msx2_state::fs4700f(machine_config &config)
{
	// YM2149 (in S3527 MSX Engine)
	// FDC: mb8877a, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// S3527 MSX Engine
	// Matsushita switched device
	// 8KB SRAM (NEC D4364C-15L)
	// Builtin thermal printer
	// Switch to switch between JP50on and JIS keyboard layout
	// Switch to switch between internal and external printer
	// Switch to bypass the firmware

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "font", 0, 2, 0, 1, "font");
	add_internal_slot(config, MSX_SLOT_BUNSETSU, "buns", 0, 2, 1, 2, "jukugo").set_bunsetsu_region_tag("bunsetsu");
	add_internal_slot(config, MSX_SLOT_ROM, "jusho", 0, 3, 1, 2, "jusho");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "wordpro1", 3, 0, 0, 3, "wordpro1");
	add_internal_slot(config, MSX_SLOT_ROM, "kdr1", 3, 0, 3, 1, "kdr1");
	add_internal_slot(config, MSX_SLOT_ROM, "wordpro2", 3, 1, 0, 3, "wordpro2");
	add_internal_slot(config, MSX_SLOT_ROM, "kdr2", 3, 1, 3, 1, "kdr2");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 2, 0, 4);  // 64KB RAM
	add_internal_disk_mirrored(config, MSX_SLOT_DISK2_MB8877, "disk", 3, 3, 1, 2, "diskrom");

	MSX_S1985(config, "s1985", 0);

	MSX_MATSUSHITA(config, "matsushita", 0);

	msx2(SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX2 - National FS-5000F2 */

ROM_START(fs5000f2)
	ROM_REGION(0x18000, "mainrom", ROMREGION_ERASEFF)
	ROM_LOAD("5000bios.rom", 0x0000, 0x8000, CRC(a44ea707) SHA1(59967765d6e9328909dee4dac1cbe4cf9d47d315))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("5000ext.rom",  0x0000, 0x4000, CRC(43e7a7fc) SHA1(0fbd45ef3dd7bb82d4c31f1947884f411f1ca344))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("5000disk.rom", 0x0000, 0x4000, CRC(ae4e65b7) SHA1(073feb8bb645d935e099afaf61e6f04f52adee42))

	ROM_REGION(0x8000, "setup", 0)
	ROM_LOAD("5000rtc.rom", 0x0000, 0x8000, CRC(03351598) SHA1(98bbfa3ab07b7a5cad55d7ddf7cbd9440caa2a86))

	ROM_REGION(0x8000, "kdr", 0)
	ROM_LOAD("5000kdr.rom", 0x0000, 0x8000, CRC(b2db6bf5) SHA1(3a9a942ed888dd641cddf8deada1879c454df3c6))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("5000kfn.rom", 0, 0x20000, CRC(c61ddc5d) SHA1(5e872d5853698731a0ed22fb72dbcdfd59cd19c3))
ROM_END

void msx2_state::fs5000f2(machine_config &config)
{
	// YM2149 (in S-1985 MSX Engine)
	// FDC: wd2793, 2 3.5" DSDD drives
	// 3 Expansion slots
	// 2 Cartridge slots
	// S-1985 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "cn904", 0, 1, 0, 4, "mainrom", 0x8000); // expansion slot
	add_internal_slot(config, MSX_SLOT_ROM, "cn906", 0, 2, 0, 4, "mainrom", 0x8000); // expansion slot
	add_internal_slot(config, MSX_SLOT_ROM, "cn907", 0, 3, 0, 4, "mainrom", 0x8000); // expansion slot
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 0, 1, 2, "kdr");
	add_internal_slot(config, MSX_SLOT_ROM, "rtcrom", 3, 1, 1, 2, "setup");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0x80);   // 128KB Mapper RAM
	add_internal_disk_mirrored(config, MSX_SLOT_DISK2_WD2793_2_DRIVES, "disk", 3, 3, 1, 2, "diskrom");

	MSX_S1985(config, "s1985", 0);

	msx2(SND_YM2149, config, layout_msx_jp_2fdd);
}

/* MSX2 - National FS-5500F1 */

ROM_START(fs5500f1)
	// ic206
	// OS1 ROM
	// MATSUSHITA
	// 1985 DAS5500A1
	// DFQT9062ZA
	// MSL27256K
	//
	// ic208
	// HKN ROM
	// MASTUSHITA
	// 1985 DAS5500E1
	// DFQT9068ZA
	//
	// ic209
	// API ROM
	// MATSUSHITA
	// 1985 DAS500C1
	// DFQT9064ZA
	// MSL27256K
	//
	// ic210
	// OS2 ROM
	// MATSUSHITA
	// 1985 DAS5500B1
	// DFQT9063ZA
	// HN4827128G-25
	//
	// ic211
	// FDC ROM
	// MATSUSHITA
	// 1985 DAS5500F1
	// DFQT9067ZA
	// HN4827128G-25
	ROM_REGION(0x18000, "mainrom", ROMREGION_ERASEFF)
	ROM_LOAD("5500bios.rom", 0x0000, 0x8000, CRC(5bf38e13) SHA1(44e0dd215b2a9f0770dd76fb49187c05b083eed9))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("5500ext.rom", 0x0000, 0x4000, CRC(3c42c367) SHA1(4be8371f3b03e70ddaca495958345f3c4f8e2d36))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("5500disk.rom", 0x0000, 0x4000, CRC(1e7d6512) SHA1(78cd7f847e77fd8cd51a647efb2725ba93f4c471))

	ROM_REGION(0x8000, "impose", 0)
	ROM_LOAD("5500imp.rom", 0x0000, 0x8000, CRC(6173a88c) SHA1(b677a861b67e8763a11d5dcf52416b42493ade57))

	ROM_REGION(0x8000, "kdr", 0)
	ROM_LOAD("5500kdr.rom", 0x0000, 0x8000, CRC(b2db6bf5) SHA1(3a9a942ed888dd641cddf8deada1879c454df3c6))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("tc531000p_6611.ic212", 0, 0x20000, CRC(956dc96d) SHA1(9ed3ab6d893632b9246e91b412cd5db519e7586b))
ROM_END

void msx2_state::fs5500f1(machine_config &config)
{
	// YM2149 in (S3527 MSX Engine)
	// FDC: mb8877a, 1 3.5" DSDD drive
	// 3 Expansion slots
	// 2 Cartridge slots
	// S3527 MSX Engine
	// Matsushita switched device
	// Switch to switch between JP50on and JIS keyboard layout
	// Switch to bypass the firmware

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "cn904", 0, 1, 0, 4, "mainrom", 0x8000); // expansion slot
	add_internal_slot(config, MSX_SLOT_ROM, "cn906", 0, 2, 0, 4, "mainrom", 0x8000); // expansion slot
	add_internal_slot(config, MSX_SLOT_ROM, "cn908", 0, 3, 0, 4, "mainrom", 0x8000); // expansion slot
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 0, 1, 2, "kdr");
	add_internal_slot(config, MSX_SLOT_ROM, "impose", 3, 1, 1, 2, "impose");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 2, 0, 4);  // 64KB RAM
	add_internal_disk_mirrored(config, MSX_SLOT_DISK2_MB8877, "disk", 3, 3, 1, 2, "diskrom");

	MSX_MATSUSHITA(config, "matsushita", 0);

	msx2(SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX2 - National FS-5500F2 */

ROM_START(fs5500f2)
	ROM_REGION(0x18000, "mainrom", ROMREGION_ERASEFF)
	ROM_LOAD("5500bios.rom", 0x0000, 0x8000, CRC(5bf38e13) SHA1(44e0dd215b2a9f0770dd76fb49187c05b083eed9))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("5500ext.rom", 0x0000, 0x4000, CRC(3c42c367) SHA1(4be8371f3b03e70ddaca495958345f3c4f8e2d36))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("5500disk.rom", 0x0000, 0x4000, CRC(1e7d6512) SHA1(78cd7f847e77fd8cd51a647efb2725ba93f4c471))

	ROM_REGION(0x8000, "impose", 0)
	ROM_LOAD("5500imp.rom", 0x0000, 0x8000, CRC(6173a88c) SHA1(b677a861b67e8763a11d5dcf52416b42493ade57))

	ROM_REGION(0x8000, "kdr", 0)
	ROM_LOAD("5500kdr.rom", 0x0000, 0x8000, CRC(b2db6bf5) SHA1(3a9a942ed888dd641cddf8deada1879c454df3c6))

	ROM_REGION(0x20000, "kanji", 0)
	// ic212 - tc531000p
	ROM_LOAD("5500kfn.rom", 0, 0x20000, CRC(956dc96d) SHA1(9ed3ab6d893632b9246e91b412cd5db519e7586b))
ROM_END

void msx2_state::fs5500f2(machine_config &config)
{
	// YM2149 in (S3527 MSX Engine)
	// FDC: mb8877a, 2 3.5" DSDD drives
	// 3 Expansion slots
	// 2 Cartridge slots
	// S3527 MSX Engine
	// Matsushita switched device
	// Switch to switch between JP50on and JIS keyboard layout
	// Switch to bypass the firmware

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "cn904", 0, 1, 0, 4, "mainrom", 0x8000); // expansion slot
	add_internal_slot(config, MSX_SLOT_ROM, "cn906", 0, 2, 0, 4, "mainrom", 0x8000); // expansion slot
	add_internal_slot(config, MSX_SLOT_ROM, "cn908", 0, 3, 0, 4, "mainrom", 0x8000); // expansion slot
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 0, 1, 2, "kdr");
	add_internal_slot(config, MSX_SLOT_ROM, "impose", 3, 1, 1, 2, "impose");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 2, 0, 4);  // 64KB RAM
	add_internal_disk_mirrored(config, MSX_SLOT_DISK2_MB8877_2_DRIVES, "disk", 3, 3, 1, 2, "diskrom");

	MSX_MATSUSHITA(config, "matsushita", 0);

	msx2(SND_YM2149, config, layout_msx_jp_2fdd);
}

/* MSX2 - Panasonic FS-A1 */

ROM_START(fsa1)
	ROM_REGION(0x20000, "mainrom", 0)
	ROM_LOAD("fsa1.ic3", 0x00000, 0x20000, CRC(4d6dae42) SHA1(7bbe3f355d3129592268ae87f40ea7e3ced88f98))
ROM_END

void msx2_state::fsa1(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: None, 0 drives
	// S1985
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 0, 4);  // 64 KB RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 2, "mainrom", 0x8000);
	add_internal_slot(config, MSX_SLOT_ROM, "deskpac1", 3, 2, 1, 2, "mainrom", 0x10000);
	add_internal_slot(config, MSX_SLOT_ROM, "deskpac2", 3, 3, 1, 2, "mainrom", 0x18000);

	MSX_S1985(config, "s1985", 0);

	msx2(SND_YM2149, config, layout_msx_nocode_nocaps);
}

/* MSX2 - Panasonic FS-A1 (a) */

ROM_START(fsa1a)
	ROM_REGION(0x1c000, "maincpu", 0)
	ROM_LOAD("a1bios.rom",   0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))
	ROM_LOAD("a1ext.rom",    0x8000, 0x4000, CRC(43e7a7fc) SHA1(0fbd45ef3dd7bb82d4c31f1947884f411f1ca344))
	ROM_LOAD("a1desk1a.rom", 0xc000, 0x8000, CRC(25b5b170) SHA1(d9307bfdaab1312d25e38af7c0d3a7671a9f716b))
	ROM_LOAD("a1desk2.rom", 0x14000, 0x8000, CRC(7f6f4aa1) SHA1(7f5b76605e3d898cc4b5aacf1d7682b82fe84353))
ROM_END

void msx2_state::fsa1a(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: None, 0 drives
	// S1985
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "bios", 0, 0, 2, "maincpu");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 0, 4);  // 64KB RAM
	add_internal_slot(config, MSX_SLOT_ROM, "ext", 3, 1, 0, 1, "maincpu", 0x8000);
	add_internal_slot(config, MSX_SLOT_ROM, "desk1", 3, 2, 1, 2, "maincpu", 0xc000);
	add_internal_slot(config, MSX_SLOT_ROM, "desk2", 3, 3, 1, 2, "maincpu", 0x14000);

	MSX_S1985(config, "s1985", 0);

	msx2(SND_YM2149, config, layout_msx_nocode_nocaps);
}

/* MSX2 - Panasonic FS-A1F */

ROM_START(fsa1f)
	ROM_REGION(0x20000, "mainrom", 0)
	ROM_LOAD("da1024d0365r.ic18", 0x00000, 0x20000, CRC(64a53ec8) SHA1(9a62d7a5ccda974261f7c0600476d85e10deb99b))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("da531p6616_0.ic17", 0, 0x20000, CRC(c61ddc5d) SHA1(5e872d5853698731a0ed22fb72dbcdfd59cd19c3)) // da531p6616-0 - tc531000ap-6616
ROM_END

void msx2_state::fsa1f(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: tc8566af, 1 3.5" DSDD drive
	// S1985
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x10000).set_unused_bits(0x80);   // 64KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "mainrom", 0x8000);
	add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 1, 1, 2, "mainrom", 0x10000);
	add_internal_disk_mirrored(config, MSX_SLOT_DISK3_TC8566, "disk", 3, 2, 1, 2, "mainrom", 0xc000);
	add_internal_slot(config, MSX_SLOT_ROM, "cockpit", 3, 3, 1, 2, "mainrom", 0x18000);

	MSX_S1985(config, "s1985", 0);

	msx2(SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX2 - Panasonic FS-A1FM */

ROM_START(fsa1fm)
	// Everything should be in one rom??
	// da534p66220 - tc534000p-6620 - ic12 - 4mbit rom
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("a1fmbios.rom", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("a1fmext.rom", 0x0000, 0x4000, CRC(ad295b5d) SHA1(d552319a19814494e3016de4b8f010e8f7b97e02))

	ROM_REGION(0x4000, "diskrom", 0)
	// FDC register contents at 3ff8-3fff
	ROM_LOAD("a1fmdisk.rom", 0x0000, 0x4000, BAD_DUMP CRC(e25cacca) SHA1(607cfca605eaf82e3efa33459d6583efb7ecc13b))

	ROM_REGION(0x100000, "firmware", 0)
	ROM_LOAD("a1fmfirm.rom", 0x000000, 0x100000, CRC(8ce0ece7) SHA1(f89e3d8f3b6855c29d71d3149cc762e0f6918ad5))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("a1fmkfn.rom", 0, 0x20000, CRC(c61ddc5d) SHA1(5e872d5853698731a0ed22fb72dbcdfd59cd19c3))

	ROM_REGION(0x20000, "kanji12", 0)
	ROM_LOAD("a1fmkf12.rom", 0, 0x20000, CRC(340d1ef7) SHA1(a7a23dc01314e88381eee88b4878b39931ab4818))
ROM_END

void msx2_state::fsa1fm(machine_config &config)
{
	// AY8910 (in T9769)
	// FDC: tc8566af, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// T9769
	// 8KB SRAM
	// Integrated 1200baud modem

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x10000).set_unused_bits(0x80);   // 64KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_FSA1FM, "modem", 3, 1, 1, 1, "firmware");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK3_TC8566, "disk", 3, 2, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_FSA1FM2, "firmware", 3, 3, 0, 3, "firmware");

	msx_kanji12_device &kanji12(MSX_KANJI12(config, "kanji12", 0));
	kanji12.set_rom_start("kanji12");

	msx2(SND_AY8910, config, layout_msx_jp_1fdd);
}

/* MSX2 - Panasonic FS-A1MK2 */

ROM_START(fsa1mk2)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("a1mkbios.rom", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("a1mk2ext.rom", 0x0000, 0x4000, CRC(43e7a7fc) SHA1(0fbd45ef3dd7bb82d4c31f1947884f411f1ca344))

	ROM_REGION(0x8000, "deskpac1", 0)
	ROM_LOAD("a1mkcoc1.rom", 0x0000, 0x8000, CRC(0eda3f57) SHA1(2752cd89754c05abdf7c23cba132d38e3ef0f27d))

	ROM_REGION(0x4000, "deskpac2", 0)
	ROM_LOAD("a1mkcoc2.rom", 0x0000, 0x4000, CRC(756d7128) SHA1(e194d290ebfa4595ce0349ea2fc15442508485b0))

	ROM_REGION(0x8000, "deskpac3", 0)
	ROM_LOAD("a1mkcoc3.rom", 0x0000, 0x8000, CRC(c1945676) SHA1(a3f4e2e4934074925d775afe30ac72f150ede543))
ROM_END

void msx2_state::fsa1mk2(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// S1985
	// pause button

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x10000).set_unused_bits(0x80);   // 64 KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "deskpac1", 3, 1, 1, 2, "deskpac1");
	add_internal_slot(config, MSX_SLOT_ROM, "deskpac2", 3, 2, 1, 1, "deskpac2");
	add_internal_slot(config, MSX_SLOT_ROM, "deskpac3", 3, 3, 1, 2, "deskpac3");

	MSX_S1985(config, "s1985", 0);

	msx2(SND_YM2149, config, layout_msx_jp);
}

/* MSX2 - Philips HCS 280 */

/* MSX2 - Philips NMS 8220 - 2 possible sets (/00 /16) */

ROM_START(nms8220)
	ROM_REGION(0x20000, "mainrom", 0)
	ROM_LOAD("m531000-52_68503.u14", 0x0000, 0x20000, CRC(f506d7ab) SHA1(e761e7081c613ad4893a664334ce105841d0e80e))

	ROM_REGION(0x4000, "designer", 0)
	ROM_SYSTEM_BIOS(0, "v1308", "13-08-1986 Designer")
	ROMX_LOAD("8220pena.u13", 0x0000, 0x4000, CRC(17817b5a) SHA1(5df95d033ae70b107697b69470126ce1b7ae9eb5), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v1206", "12-06-1986 Designer")
	ROMX_LOAD("8220pen.u13",  0x0000, 0x4000, CRC(3d38c53e) SHA1(cb754aed85b3e97a7d3c5894310df7ca18f89f41), ROM_BIOS(1))
ROM_END

void msx2_state::nms8220(machine_config &config)
{
	// YM2149 (in S-3527 MSX Engine)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// S-3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom", 0x10000);
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "mainrom", 0x18000);
	// Memory mapper blocks mirrored every 8 blocks: 4x ram, 4x empty, 4x ram, 4x empty, etc
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x10000).set_unused_bits(0xf8);   // 64KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "designer", 3, 3, 1, 1, "designer");
	add_internal_slot(config, MSX_SLOT_ROM, "designer_mirror", 3, 3, 2, 1, "designer");

	msx2_pal(SND_YM2149, config, layout_msx_nocode);
}

/* MSX2 - Philips NMS 8245 - 2 possible sets (/00 /16) */
/* /00 - A16 = 0 */
/* /16 - A16 = 1 */
/* /19 - Azerty keyboard */

ROM_START(nms8245)
	ROM_REGION(0x10000, "mainrom", 0)
	ROM_SYSTEM_BIOS(0, "v1.08", "v1.08")
	ROMX_LOAD("v1_08.u7", 0x00000, 0x10000, CRC(69d5cbe6) SHA1(cc57c1dcd7249ea9f8e2547244592e7d97308ed0), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v1.06", "v1.06")
	ROMX_LOAD("v1_06.u7", 0x00000, 0x10000, CRC(be4ae17e) SHA1(b746192dc333eaf2a725a44777303808a3649d63), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v1.05", "v1.05")
	ROMX_LOAD("v1_05.u7", 0x00000, 0x10000, CRC(cef8895d) SHA1(816ceb21088b116ec34be0ff869d02222b525e58), ROM_BIOS(2))
ROM_END

void msx2_state::nms8245(machine_config &config)
{
	// XTAL: 21328.1 (different from default)
	// YM2149 (in S-3527 MSX Engine)
	// FDC: wd2793, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// S-3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "mainrom", 0x8000);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0xf8);   // 128KB Mapper RAM
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793, "disk", 3, 3, 1, 2, "mainrom", 0xc000);

	msx2_pal(SND_YM2149, config, layout_msx_nocode_1fdd);
}

/* MSX2 - Philips NMS 8245/19 */

ROM_START(nms8245f)
	ROM_REGION(0x20000, "maincpu", 0)
	ROM_LOAD("nms8245.u7", 0x0000, 0x20000, BAD_DUMP CRC(0c827d5f) SHA1(064e706cb1f12b99b329944ceeedc0efc3b2d9be))
ROM_END

void msx2_state::nms8245f(machine_config &config)
{
	// YM2149 (in S-3527 MSX Engine)
	// FDC: wd2793, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// S-3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "bios", 0, 0, 2, "maincpu", 0x10000);
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "ext", 3, 0, 0, 1, "maincpu", 0x18000);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0xf8);   // 128KB Mapper RAM
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793, "disk", 3, 3, 1, 2, "maincpu", 0x1c000);

	msx2_pal(SND_YM2149, config, layout_msx_nocode_1fdd);
}

/* MSX2 - Philips NMS 8245 Home Banking (Italy) */

/* MSX2 - Philips NMS 8250/00 */

ROM_START(nms8250)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("d23c256eac.ic119", 0x0000, 0x8000, CRC(6cdaf3a5) SHA1(6103b39f1e38d1aa2d84b1c3219c44f1abb5436e))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("d23128ec.ic118", 0x0000, 0x4000, CRC(66237ecf) SHA1(5c1f9c7fb655e43d38e5dd1fcc6b942b2ff68b02))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_SYSTEM_BIOS(0, "v1.08", "v1.08 diskrom")
	ROMX_LOAD("v1.08.ic117", 0x0000, 0x4000, CRC(61f6fcd3) SHA1(dab3e6f36843392665b71b04178aadd8762c6589), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "unknown", "Unknown version diskrom")
	ROMX_LOAD("jq00014.ic117", 0x0000, 0x4000, CRC(ca3307d3) SHA1(c3efedda7ab947a06d9345f7b8261076fa7ceeef), ROM_BIOS(1))
ROM_END

void msx2_state::nms8250(machine_config &config)
{
	// YM2149 (in S-3527 MSX Engine)
	// FDC: wd2793, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// S-3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	// ROM is mirrored in all 4 pages
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom1", 3, 0, 1, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom2", 3, 0, 2, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom3", 3, 0, 3, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0xf8);   // 128KB Mapper RAM
	// ROM is not mirrored but the FDC registers are in all pages
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793, "disk", 3, 3, 1, 2, "diskrom");

	msx2_pal(SND_YM2149, config, layout_msx_nocode_1fdd);
}

/* MSX2 - Philips NMS 8250/16 */

ROM_START(nms8250_16)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("bios.ic119", 0x0000, 0x8000, CRC(5e3caf18) SHA1(ee0d8ccfc247368078d27183c34b3a5c0f4ae0f1))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("sub.ic118", 0x0000, 0x4000, CRC(0a0aeb2f) SHA1(b83770cca8453a153d7e260070a3a3c059d64ed3))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("jq00014.ic117", 0x0000, 0x4000, CRC(ca3307d3) SHA1(c3efedda7ab947a06d9345f7b8261076fa7ceeef))
ROM_END

/* MSX2 - Philips NMS 8250/19 */

ROM_START(nms8250_19)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("nms8250fbios.ic119", 0x0000, 0x8000, CRC(bee21533) SHA1(d18694e9e7040b2851fe985cefb89766868a2fd3))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("nms8250fext.ic118",  0x0000, 0x4000, CRC(781ba055) SHA1(fd4bcc81a8160a1dea06036c5f79d200f948f4d6))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("nms8250fdisk.ic117", 0x0000, 0x4000, CRC(ca3307d3) SHA1(c3efedda7ab947a06d9345f7b8261076fa7ceeef))
ROM_END

/* MSX2 - Philips NMS 8255 */

ROM_START(nms8255)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("8255bios.rom.ic119", 0x0000, 0x8000, CRC(6cdaf3a5) SHA1(6103b39f1e38d1aa2d84b1c3219c44f1abb5436e))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("8255ext.rom.ic118",  0x0000, 0x4000, CRC(66237ecf) SHA1(5c1f9c7fb655e43d38e5dd1fcc6b942b2ff68b02))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("8255disk.rom.ic117", 0x0000, 0x4000, CRC(ca3307d3) SHA1(c3efedda7ab947a06d9345f7b8261076fa7ceeef))
ROM_END

void msx2_state::nms8255(machine_config &config)
{
	// YM2149 (in S-3527 MSX Engine)
	// FDC: wd2793, 2 3.5" DSDD drives
	// 2 Cartridge slots
	// S-3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	// ROM is mirrored in all 4 pages
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom1", 3, 0, 1, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom2", 3, 0, 2, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom3", 3, 0, 3, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0xf8);   // 128KB Mapper RAM
	// ROM is not mirrored but the FDC registers are in all pages
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_2_DRIVES, "disk", 3, 3, 1, 2, "diskrom");

	msx2_pal(SND_YM2149, config, layout_msx_nocode_2fdd);
}

/* MSX2 - Philips NMS 8255/19 */

ROM_START(nms8255f)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("nms8255fbios.rom", 0x0000, 0x8000, BAD_DUMP CRC(5cd35ced) SHA1(b034764e6a8978db60b1d652917f5e24a66a7925)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("nms8255fext.rom",  0x0000, 0x4000, BAD_DUMP CRC(781ba055) SHA1(fd4bcc81a8160a1dea06036c5f79d200f948f4d6)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("nms8255fdisk.rom", 0x0000, 0x4000, BAD_DUMP CRC(13b60725) SHA1(58ba1887e8fd21c912b6859cae6514bd874ffcca)) // need verification
ROM_END

void msx2_state::nms8255f(machine_config &config)
{
	// YM2149 (in S-3527 MSX Engine)
	// FDC: wd2793, 2 3.5" DSDD drives
	// 2 Cartridge slots
	// S-3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0xf8);   // 128KB Mapper RAM
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_2_DRIVES, "disk", 3, 3, 1, 2, "diskrom");

	msx2_pal(SND_YM2149, config, layout_msx_nocode_2fdd);
}

/* MSX2 - Philips NMS 8260 */
/* Prototype created by JVC for Philips. Based on an NMS-8250 with the floppy drive removed and replaced with a 20MB JVC harddisk */

ROM_START(nms8260)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("nms8260bios.rom", 0x0000, 0x8000, CRC(6cdaf3a5) SHA1(6103b39f1e38d1aa2d84b1c3219c44f1abb5436e))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("nms8260ext.rom",  0x0000, 0x4000, CRC(66237ecf) SHA1(5c1f9c7fb655e43d38e5dd1fcc6b942b2ff68b02))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("nms8260disk.rom", 0x0000, 0x4000, CRC(ca3307d3) SHA1(c3efedda7ab947a06d9345f7b8261076fa7ceeef))

	ROM_REGION(0x4000, "hddrom", 0)
	ROM_LOAD("nms8260hdd.rom", 0x0000, 0x4000, CRC(0051afc3) SHA1(77f9fe964f6d8cb8c4af3b5fe63ce6591d5288e6))
ROM_END

void msx2_state::nms8260(machine_config &config)
{
	// YM2149 (in S-3527 MSX Engine)
	// FDC: wd2793, 0 3.5" DSDD drives
	// 2 Cartridge slots
	// S-3527 MSX Engine
	// HDD

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_ROM, "hddrom", 2, 1, 1, "hddrom");
	// ROM is mirrored in all 4 pages
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom1", 3, 0, 1, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom2", 3, 0, 2, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom3", 3, 0, 3, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0xf8);   // 128KB Mapper RAM
	// There is actually only an FDC inside with a floppy controller to attach an external floppy drive
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_0, "disk", 3, 3, 1, 2, "diskrom");

	// Not confirmed as there are no pictures of the keyboard
	msx2_pal(SND_YM2149, config, layout_msx_nocode);
}

/* MSX2 - Philips NMS 8280 - 5 possible sets (/00 /02 /09 /16 /19) */

ROM_START(nms8280)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("8280bios.rom.ic119", 0x0000, 0x8000, BAD_DUMP CRC(6cdaf3a5) SHA1(6103b39f1e38d1aa2d84b1c3219c44f1abb5436e)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("8280ext.rom.ic118",  0x0000, 0x4000, BAD_DUMP CRC(66237ecf) SHA1(5c1f9c7fb655e43d38e5dd1fcc6b942b2ff68b02)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("8280disk.rom.ic117", 0x0000, 0x4000, BAD_DUMP CRC(ca3307d3) SHA1(c3efedda7ab947a06d9345f7b8261076fa7ceeef)) // need verification
ROM_END

void msx2_state::nms8280(machine_config &config)
{
	// YM2149 (in S3527)
	// FDC: wd2793, 2 3.5" DSDD drives
	// 2 Cartridge slots
	// S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0xf8);   // 128KB Mapper RAM
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_2_DRIVES, "disk", 3, 3, 1, 2, "diskrom");

	msx2_pal(SND_YM2149, config, layout_msx_nocode_2fdd);
}

/* MSX2 - Philips NMS 8280F */

ROM_START(nms8280f)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("8280fbios.rom", 0x0000, 0x8000, BAD_DUMP CRC(5cd35ced) SHA1(b034764e6a8978db60b1d652917f5e24a66a7925)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("8280fext.rom",  0x0000, 0x4000, BAD_DUMP CRC(781ba055) SHA1(fd4bcc81a8160a1dea06036c5f79d200f948f4d6)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("8280fdisk.rom", 0x0000, 0x4000, BAD_DUMP CRC(13b60725) SHA1(58ba1887e8fd21c912b6859cae6514bd874ffcca)) // need verification
ROM_END

void msx2_state::nms8280f(machine_config &config)
{
	// YM2149 (in S3527)
	// FDC: wd2793, 2 3.5" DSDD drives
	// 2 Cartridge slots
	// S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0xf8);   // 128KB Mapper RAM
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_2_DRIVES, "disk", 3, 3, 1, 2, "diskrom");

	msx2_pal(SND_YM2149, config, layout_msx_nocode_2fdd);
}

/* MSX2 - Philips NMS 8280G */

ROM_START(nms8280g)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("8280gbios.rom.ic119", 0x0000, 0x8000, BAD_DUMP CRC(8fa060e2) SHA1(b17d9bea0eb16a1aa2d0ccbd7c9488da9f57698e)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("8280gext.rom.ic118", 0x0000, 0x4000, BAD_DUMP CRC(41e36d03) SHA1(4ab7b2030d022f5486abaab22aaeaf8aa23e05f3)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	// FDC register contents at 3ff8-3fff
	ROM_LOAD("8280gdisk.rom.ic117", 0x0000, 0x4000, BAD_DUMP CRC(d0beebb8) SHA1(d1001f93c87ff7fb389e418e33bf7bc81bdbb65f)) // need verification
ROM_END

void msx2_state::nms8280g(machine_config &config)
{
	// YM2149 (in S3527)
	// FDC: wd2793, 2 3.5" DSDD drives
	// 2 Cartridge slots
	// S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0xf8);   // 128KB Mapper RAM
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_2_DRIVES, "disk", 3, 3, 1, 2, "diskrom");

	msx2_pal(SND_YM2149, config, layout_msx_nocode_2fdd);
}

/* MSX2 - Philips VG-8230 (u11 - exp, u12 - basic, u13 - disk */

ROM_START(vg8230)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("8230bios.rom.u12", 0x0000, 0x8000, CRC(b31c851d) SHA1(0de3c802057560560a03d7965fcc4cff69f8575c))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("8230ext.rom.u11",  0x0000, 0x4000, CRC(8f84f783) SHA1(3288894e1be6af705871499b23c85732dbc40993))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("8230disk.rom.u13", 0x0000, 0x4000, CRC(7639758a) SHA1(0f5798850d11b316a4254b222ca08cc4ad6d4da2))
ROM_END

void msx2_state::vg8230(machine_config &config)
{
	// YM2149 (in S-3527 MSX Engine)
	// FDC: wd2793, 1 3.5" SSDD drive
	// 2 Cartridge slots
	// S-3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 2, 0, 4);   // 64KB RAM
	add_internal_disk(config, MSX_SLOT_DISK1_WD2793, "disk", 3, 3, 1, 1, "diskrom");

	msx2_pal(SND_YM2149, config, layout_msx_nocode_1fdd);
}

/* MSX2 - Philips VG-8235 (/00 and /20) */
/* 9 versions:
 * /00 NL,BE QWERTY        2.0
 * /02 DE    QWERTZ        2.0
 * /16 ES    QWERTY with ñ 2.0
 * /19 FR,BE AZERTY        2.0
 * /20 NL,BE QWERTY        2.1
 * /22 DE    QWERTZ        2.1
 * /29 DE    QWERTZ        2.1
 * /36 ES    QWERTY with ñ 2.1
 * /39 FR,BE AZERTY        2.1
 */

ROM_START(vg8235)
	ROM_SYSTEM_BIOS(0, "r20", "VG8235/20")
	ROM_SYSTEM_BIOS(1, "r00", "VG8235/00")

	ROM_REGION(0x8000, "mainrom", 0)
	ROMX_LOAD("8235_20.u48", 0x0000, 0x8000, CRC(6cdaf3a5) SHA1(6103b39f1e38d1aa2d84b1c3219c44f1abb5436e), ROM_BIOS(0))
	ROMX_LOAD("8235_00.u48", 0x0000, 0x8000, CRC(f05ed518) SHA1(5e1a4bd6826b29302a1eb88c340477e7cbd0b50a), ROM_BIOS(1))

	ROM_REGION(0x4000, "subrom", 0)
	ROMX_LOAD("8235_20.u49", 0x0000, 0x4000, CRC(66237ecf) SHA1(5c1f9c7fb655e43d38e5dd1fcc6b942b2ff68b02), ROM_BIOS(0))
	ROMX_LOAD("8235_00.u49", 0x0000, 0x4000, CRC(474439d1) SHA1(c289dad246364e2dd716c457ca5eecf98e76c9ab), ROM_BIOS(1))

	ROM_REGION(0x4000, "diskrom", 0)
	// Different versions might exist and mixed between /00 and /20
	ROMX_LOAD("8235_20.u50", 0x0000, 0x4000, CRC(0efbea8a) SHA1(1ee2abc68a81ae7e39548985021b6532f31171b2), ROM_BIOS(0))
	ROMX_LOAD("8235_00.u50", 0x0000, 0x4000, CRC(f39342ce) SHA1(7ce255ab63ba79f81d8b83d66f1108062d0b61f1), ROM_BIOS(1))
ROM_END

void msx2_state::vg8235(machine_config &config)
{
	// YM2149 (in S-3527 MSX Engine)
	// FDC: wd2793, 1 3.5" SSDD drive
	// 2 Cartridge slots
	// S-3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0xf8);   // 128KB Mapper RAM
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793, "disk", 3, 3, 1, 2, "diskrom");

	msx2_pal(SND_YM2149, config, layout_msx_nocode_1fdd);
}

/* MSX2 - Philips VG-8235F (/19 and /39) */

ROM_START(vg8235f)
	ROM_SYSTEM_BIOS(0, "r39", "VG8235/39")
	ROM_SYSTEM_BIOS(1, "r19", "VG8235/19")

	ROM_REGION(0x8000, "mainrom", 0)
	ROMX_LOAD("8235_39.u48", 0x0000, 0x8000, CRC(5cd35ced) SHA1(b034764e6a8978db60b1d652917f5e24a66a7925), ROM_BIOS(0))
	ROMX_LOAD("8235_19.u48", 0x0000, 0x8000, BAD_DUMP CRC(c0577a50) SHA1(3926cdd91fa89657a811463e48cfbdb350676e51), ROM_BIOS(1)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROMX_LOAD("8235_39.u49", 0x0000, 0x4000, CRC(781ba055) SHA1(fd4bcc81a8160a1dea06036c5f79d200f948f4d6), ROM_BIOS(0))
	ROMX_LOAD("8235_19.u49", 0x0000, 0x4000, BAD_DUMP CRC(e235d5c8) SHA1(792e6b2814ab783d06c7576c1e3ccd6a9bbac34a), ROM_BIOS(1)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	ROMX_LOAD("8235_39.u50", 0x0000, 0x4000, CRC(768549a9) SHA1(959060445e92eb980ddd9df3ef9cfefcae2de1d0), ROM_BIOS(0))
	ROMX_LOAD("8235_19.u50", 0x0000, 0x4000, BAD_DUMP CRC(768549a9) SHA1(959060445e92eb980ddd9df3ef9cfefcae2de1d0), ROM_BIOS(1)) // need verification
ROM_END

void msx2_state::vg8235f(machine_config &config)
{
	// YM2149 (in S-3527 MSX Engine)
	// FDC: wd2793, 1 3.5" SSDD drive
	// 2 Cartridge slots
	// S-3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0xf8);   // 128KB Mapper RAM
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_SS, "disk", 3, 3, 1, 2, "diskrom");

	msx2_pal(SND_YM2149, config, layout_msx_nocode_1fdd);
}

/* MSX2 - Philips VG-8240 (unreleased) */

ROM_START(vg8240)
	ROM_REGION(0x10000, "mainrom", 0)
	ROM_LOAD("8240bios.rom", 0x0000, 0x8000, CRC(6cdaf3a5) SHA1(6103b39f1e38d1aa2d84b1c3219c44f1abb5436e))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("8240ext.rom",  0x0000, 0x4000, CRC(66237ecf) SHA1(5c1f9c7fb655e43d38e5dd1fcc6b942b2ff68b02))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("8240disk.rom", 0x0000, 0x4000, CRC(ca3307d3) SHA1(c3efedda7ab947a06d9345f7b8261076fa7ceeef))
ROM_END

void msx2_state::vg8240(machine_config &config)
{
	// YM2149 (in S3527)
	// FDC: wd2793, 1 3.5" DSDD drive
	// 2 Cartridge slots?
	// S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 2, 0, 4);   // 64KB RAM
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793, "disk", 3, 3, 1, 2, "diskrom");

	msx2_pal(SND_YM2149, config, layout_msx_nocode_1fdd);
}

/* MSX2 - Phonola NMS 8245 */

/* MSX2 - Phonola NMS 8280 */

/* MSX2 - Phonola VG-8235 */

/* MSX2 - Pioneer UC-V102 */

ROM_START(ucv102)
	ROM_REGION(0x8000, "mainrom", 0)
	// Machine is not supposed to display the MSX logo on startup
	ROM_LOAD("uc-v102bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(a27c563d) SHA1(c1e46c00f1e38fc9e0ab487bf0513bd93ce61f3f))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("uc-v102sub.rom", 0x0000, 0x4000, BAD_DUMP CRC(43e7a7fc) SHA1(0fbd45ef3dd7bb82d4c31f1947884f411f1ca344))

	ROM_REGION(0x4000, "rs232", ROMREGION_ERASEFF)
	ROM_LOAD("uc-v102rs232.rom", 0x0000, 0x2000, CRC(7c6790fc) SHA1(a4f19371fd09b73f2776cb637b0e9cbd8415f8eb))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("uc-v102disk.rom", 0x0000, 0x4000, CRC(a90be8d5) SHA1(f7c3ac138918a493eb91628ed88cf37999059579))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("kanjifont.rom", 0x0000, 0x20000, CRC(d23d4d2d) SHA1(db03211b7db46899df41db2b1dfbec972109a967))
ROM_END

void msx2_state::ucv102(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: wd1793, 1 3.5" DSDD drives (could be upgraded to 2)
	// 1 Cartridge slots
	// S1985
	// RS232

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 0, 2, 0, 4).set_total_size(0x10000);   // 64KB Mapper RAM
	add_cartridge_slot<1>(config, 1);
	add_internal_slot_irq<2>(config, MSX_SLOT_RS232, "rs232", 2, 1, 1, "rs232");
	// Expansion slot 1 connects to slots 2-1 and 3-1 (2x 50 pin)
	// Expansion slot 2 connects to slots 2-2 and 3-2 (2x 50 pin)
	// Expansion slot 3 connects to slots 2-3 and 3-3 (2x 50 pin)
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_FD1793, "disk", 3, 1, 2, "diskrom").use_motor_for_led(); // Mitsubishi MSW1793

	MSX_S1985(config, "s1985", 0);

	m_hw_def.has_cassette(false);
	msx2(SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX2 - Sakhr AX-350 */

ROM_START(ax350)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("ax350bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(ea306155) SHA1(35195ab67c289a0b470883464df66bc6ea5b00d3)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("ax350ext.rom", 0x0000, 0x4000, BAD_DUMP CRC(7c7540b7) SHA1(ebb76f9061e875365023523607db610f2eda1d26)) // need verification

	ROM_REGION(0x8000, "arabic", 0)
	ROM_LOAD("ax350arab.rom", 0x0000, 0x8000, BAD_DUMP CRC(c0d8fc85) SHA1(2c9600c6e0025fee10d249e97448ecaa37e38c42)) // need verification

	ROM_REGION(0x8000, "swp", 0)
	ROM_LOAD("ax350swp.rom", 0x0000, 0x8000, BAD_DUMP CRC(076f40fc) SHA1(4b4508131dca6d811694ae6379f41364c477de58)) // need verification

	ROM_REGION(0x10000, "painter", 0)
	ROM_LOAD("ax350paint.rom", 0x0000, 0x10000, BAD_DUMP CRC(18956e3a) SHA1(ace202e87337fbc54fea21e22c0b3af0abe6f4ae)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("ax350disk.rom", 0x0000, 0x4000, BAD_DUMP CRC(1e7d6512) SHA1(78cd7f847e77fd8cd51a647efb2725ba93f4c471)) // need verification
ROM_END

void msx2_state::ax350(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: wd2793/tc8566af?, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// S1985

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "arabic", 0, 1, 1, 2, "arabic");
	add_internal_slot(config, MSX_SLOT_ROM, "swp", 0, 2, 1, 2, "swp");
	add_internal_slot(config, MSX_SLOT_ROM, "painter", 0, 3, 0, 4, "painter");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_disk_mirrored(config, MSX_SLOT_DISK2_WD2793, "disk", 3, 1, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0xf8);   // 128KB Mapper RAM

	MSX_S1985(config, "s1985", 0);

	msx2_pal(SND_YM2149, config, layout_msx_ar_1fdd);
}

/* MSX2 - Sakhr AX-350 II */

ROM_START(ax350ii)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("ax350iibios.rom", 0x0000, 0x8000, CRC(ea306155) SHA1(35195ab67c289a0b470883464df66bc6ea5b00d3))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("ax350iiext.rom", 0x0000, 0x4000, CRC(7c7540b7) SHA1(ebb76f9061e875365023523607db610f2eda1d26))

	ROM_REGION(0x8000, "arabic", 0)
	ROM_LOAD("ax350iiarab.rom", 0x0000, 0x8000, CRC(e62f9bc7) SHA1(f8cd4c05083decfc098cff077e055a4ae1e91a73))

	ROM_REGION(0x8000, "swp", 0)
	ROM_LOAD("ax350iiword.rom", 0x0000, 0x8000, CRC(307ae37c) SHA1(3a74e73b94d066b0187feb743c5eceddf0c61c2b))

	ROM_REGION(0x10000, "painter", 0)
	ROM_LOAD("ax350iipaint.rom", 0x0000, 0x10000, CRC(18956e3a) SHA1(ace202e87337fbc54fea21e22c0b3af0abe6f4ae))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("ax350iidisk.rom", 0x0000, 0x4000, CRC(d07782a6) SHA1(358e69f427390041b5aa28018550a88f996bddb6))
ROM_END

void msx2_state::ax350ii(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: wd2793/tc8566af?, 1 3.5" DSDD drive (mb8877a in pcb picture)
	// 2 Cartridge slots
	// S1985

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "arabic", 0, 1, 1, 2, "arabic");
	add_internal_slot(config, MSX_SLOT_ROM, "swp", 0, 2, 1, 2, "swp");
	add_internal_slot(config, MSX_SLOT_ROM, "painter", 0, 3, 0, 4, "painter");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	// mirroring not confirmed
	add_internal_disk_mirrored(config, MSX_SLOT_DISK8_MB8877, "disk", 3, 1, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0xf8);   // 128KB Mapper RAM

	MSX_S1985(config, "s1985", 0);

	msx2_pal(SND_YM2149, config, layout_msx_ar_1fdd);
}

/* MSX2 - Sakhr AX-350 II F */

ROM_START(ax350iif)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("ax350iifbios.rom", 0x0000, 0x8000, CRC(5cd35ced) SHA1(b034764e6a8978db60b1d652917f5e24a66a7925))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("ax350iifext.rom", 0x0000, 0x4000, CRC(16afa2e9) SHA1(4cbceba8f37f08272b612b6fc212eeaf379da9c3))

	ROM_REGION(0x8000, "arabic", 0)
	ROM_LOAD("ax350iifarab.rom", 0x0000, 0x8000, CRC(a64c3192) SHA1(5077b9c86ce1dc0a22c71782dac7fb3ca2a467e0))

	ROM_REGION(0x8000, "swp", 0)
	ROM_LOAD("ax350iifword.rom", 0x0000, 0x8000, CRC(097fd8ca) SHA1(54ff13b58868018fcd43c916b8d7c7200ebdcabe))

	ROM_REGION(0x10000, "painter", 0)
	ROM_LOAD("ax350iifpaint.rom", 0x0000, 0x10000, CRC(18956e3a) SHA1(ace202e87337fbc54fea21e22c0b3af0abe6f4ae))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("ax350iifdisk.rom", 0x0000, 0x4000, CRC(5eb46cd5) SHA1(bd0ad648d728c691fcee08eaaaa95e15e29c0d0d))
ROM_END

void msx2_state::ax350iif(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: wd2793/tc8566af?, 1 3.5" DSDD drive (mb8877a in pcb picture)
	// 2 Cartridge slots
	// S1985

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "arabic", 0, 1, 1, 2, "arabic");
	add_internal_slot(config, MSX_SLOT_ROM, "swp", 0, 2, 1, 2, "swp");
	add_internal_slot(config, MSX_SLOT_ROM, "painter", 0, 3, 0, 4, "painter");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	// mirroring not confirmed
	add_internal_disk_mirrored(config, MSX_SLOT_DISK8_MB8877, "disk", 3, 1, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0xf8);   // 128KB Mapper RAM

	MSX_S1985(config, "s1985", 0);

	msx2_pal(SND_YM2149, config, layout_msx_ar_1fdd);
}

/* MSX2 - Sakhr AX-370 */

ROM_START(ax370)
	ROM_REGION(0x30000, "mainrom", 0)
	ROM_LOAD("ax370bios.rom", 0x0000, 0x8000, CRC(ea306155) SHA1(35195ab67c289a0b470883464df66bc6ea5b00d3))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("ax370ext.rom", 0x0000, 0x4000, CRC(3c011d12) SHA1(ee9c6a073766bef2220a57372f5c0dbfc6e55c8c))

	ROM_REGION(0x8000, "arabic", 0)
	ROM_LOAD("ax370arab.rom", 0x0000, 0x8000, CRC(66c2c71e) SHA1(0c08e799a7cf130ae2b9bc93f28bd4959cee6fdc))

	ROM_REGION(0x8000, "swp", 0)
	ROM_LOAD("ax370swp.rom", 0x0000, 0x8000, CRC(076f40fc) SHA1(4b4508131dca6d811694ae6379f41364c477de58))

	ROM_REGION(0x8000, "sakhr", 0)
	ROM_LOAD("ax370sakhr.rom", 0x0000, 0x8000, CRC(71a356ce) SHA1(8167117a003824220c36775682acbb36b3733c5e))

	ROM_REGION(0x10000, "painter", 0)
	ROM_LOAD("ax370paint.rom", 0x00000, 0x10000, CRC(0394cb41) SHA1(1c9a5867d39f6f02a0a4ef291904623e2521c2c5))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("ax370disk.rom", 0x0000, 0x4000, CRC(db7f1125) SHA1(9efa744be8355675e7bfdd3976bbbfaf85d62e1d))
ROM_END

void msx2_state::ax370(machine_config &config)
{
	// AY8910 (in T9769B)
	// FDC: tc8566af, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// V9958
	// T9769B

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "arabic", 0, 1, 1, 2, "arabic");
	add_internal_slot(config, MSX_SLOT_ROM, "swp", 0, 2, 1, 2, "swp");
	add_internal_slot(config, MSX_SLOT_ROM, "sakhr", 0, 3, 1, 2, "sakhr");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK3_TC8566, "disk", 3, 0, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_ROM, "painter", 3, 1, 0, 4, "painter");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 3, 0, 4).set_total_size(0x20000).set_unused_bits(0xf8);   // 128KB Mapper RAM

	msx2_v9958_base(SND_AY8910, config, layout_msx_ar_1fdd);
	m_v9958->set_screen_pal(m_screen);
	msx2_add_softlists(config);
}

/* MSX2 - Sakhr AX-500 */

ROM_START(ax500)
	ROM_REGION(0x30000, "mainrom", 0)
	ROM_LOAD("ax500bios.rom", 0x0000, 0x8000, CRC(0a6e2e13) SHA1(dd1b577ea3ea69de84a68d311261392881f9eac3))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("ax500ext.rom", 0x0000, 0x4000, CRC(c9186a21) SHA1(7f86af13e81259a0db8f70d8a7e026fb918ee652))

	ROM_REGION(0x8000, "arabic", 0)
	ROM_LOAD("ax500arab.rom", 0x0000, 0x8000, CRC(11830686) SHA1(92bac0b2995f54f0eebf167cd447361a6a4923eb))

	ROM_REGION(0x8000, "swp", 0)
	ROM_LOAD("ax500swp.rom", 0x0000, 0x8000, CRC(17ed0610) SHA1(8674d000a52ec01fd80c8cb7cbaa66d4c3ca5cf7))

	ROM_REGION(0xc000, "sakhr", 0)
	ROM_LOAD("ax500sakhr.rom", 0x0000, 0xc000, CRC(bee11490) SHA1(8e889999ecec302f05d3bd0a0f127b489fcf3739))

	ROM_REGION(0x10000, "painter", 0)
	ROM_LOAD("ax500paint.rom", 0x00000, 0x10000, CRC(c32ea7ec) SHA1(80872d997d18e1a633e70b9da35a0d28113073e5))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("ax500disk.rom", 0x0000, 0x4000, CRC(a7d7746e) SHA1(a953bef071b603d6280bdf7ab6249c2e6f1a4cd8))
ROM_END

void msx2_state::ax500(machine_config &config)
{
	// YM2149 (in S9185)
	// FDC: wd2793?, 2 3.5" DSDD drives
	// 2 Cartridge slots
	// S1985

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "arabic", 0, 1, 1, 2, "arabic");
	add_internal_slot(config, MSX_SLOT_ROM, "swp", 0, 2, 1, 2, "swp");
	add_internal_slot(config, MSX_SLOT_ROM, "sakhr", 0, 3, 1, 2, "sakhr");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK8_WD2793_2_DRIVES, "disk", 3, 0, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_ROM, "painter", 3, 1, 0, 4, "painter");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x40000).set_unused_bits(0xf0);   // 256KB Mapper RAM
	add_cartridge_slot<3>(config, MSX_SLOT_YAMAHA_EXPANSION, "module", 3, 3, msx_yamaha_60pin, nullptr);

	MSX_S1985(config, "s1985", 0);

	msx2_pal(SND_YM2149, config, layout_msx_ar_2fdd);
}

/* MSX2 - Sanyo MPC-2300 */

ROM_START(mpc2300)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("2300bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(e7d08e29) SHA1(0f851ee7a1cf79819f61cc89e9948ee72a413802)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("2300ext.rom", 0x0000, 0x4000, BAD_DUMP CRC(3d7dc718) SHA1(e1f834b28c3ee7c9f79fe6fbf2b23c8a0617892b)) // need verification
ROM_END

void msx2_state::mpc2300(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: None, 0 drives
	// 2 Cartridge slots?
	// S1985

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x20000);   // 128KB?? Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "subrom");

	MSX_S1985(config, "s1985", 0);

	msx2(SND_YM2149, config, layout_msx_ru);
}

/* MSX2 - Sanyo MPC-2500FD */

ROM_START(mpc2500f)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("mpc2500fdbios.rom", 0x0000, 0x8000, BAD_DUMP CRC(e7d08e29) SHA1(0f851ee7a1cf79819f61cc89e9948ee72a413802)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("mpc2500fdext.rom",  0x0000, 0x4000, BAD_DUMP CRC(3d7dc718) SHA1(e1f834b28c3ee7c9f79fe6fbf2b23c8a0617892b)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	// FDC register contents at 3ff8-3fff
	ROM_LOAD("mpc2500fddisk.rom", 0x0000, 0x4000, BAD_DUMP CRC(38454059) SHA1(58ac78bba29a06645ca8d6a94ef2ac68b743ad32)) // need verification
ROM_END

void msx2_state::mpc2500f(machine_config &config)
{
	// YM2149
	// FDC: wd2793?, 1? 3.5" DSDD drive?
	// 2 Cartridge slots?
	// S-3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 2, 0, 1, "subrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793, "disk", 3, 1, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x10000);   // 64KB?? Mapper RAM

	msx2(SND_YM2149, config, layout_msx_ru_1fdd);
}

/* MSX2 - Sanyo MPC-25F */

/* MSX2 - Sanyo MPC-25FD */

ROM_START(mpc25fd)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("25fdbios.rom", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("25fdext.rom",  0x0000, 0x4000, CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("25fddisk.rom", 0x0000, 0x4000, CRC(1a91f241) SHA1(bdbc75aacba4ea0f359694f304ae436914733460))
ROM_END

void msx2_state::mpc25fd(machine_config &config)
{
	// YM2149 (in S-3527 MSX Engine)
	// FDC: wd2793, 1 drive
	// 1 Cartridge slot (slot 1)
	// S-3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 2, 3, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom_mirror1", 2, 3, 1, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom_mirror2", 2, 3, 2, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom_mirror3", 2, 3, 3, 1, "subrom");
	// Mirrored in all 4 pages (only rom or also registers?)
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793, "disk", 3, 1, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 2, 0, 4);   // 64KB RAM

	msx2(SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX2 - Sanyo MPC-25FK */

/* MSX2 - Sanyo MPC-25FS */

ROM_START(mpc25fs)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("25fdbios.rom", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("25fdext.rom",  0x0000, 0x4000, CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("25fddisk.rom", 0x0000, 0x4000, CRC(0fa7b10e) SHA1(50f4098a77e7af7093e29cc8683d2b34b2d07b13))
ROM_END

void msx2_state::mpc25fs(machine_config &config)
{
	// YM2149 (in S-3527 MSX Engine)
	// FDC: wd2793, 1 drive
	// 1 Cartridge slot (slot 1)
	// S-3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 2, 3, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom_mirror1", 2, 3, 1, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom_mirror2", 2, 3, 2, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom_mirror3", 2, 3, 3, 1, "subrom");
	// Mirrored in all 4 pages (only rom or also registers?)
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_SS, "disk", 3, 1, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 2, 0, 4);   // 64KB RAM

	msx2(SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX2 - Sanyo Wavy MPC-27 */

ROM_START(mpc27)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("mpc27bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(ba81b3dd) SHA1(4ce41fcc1a603411ec4e99556409c442078f0ecf)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("mpc27ext.rom", 0x0000, 0x4000, BAD_DUMP CRC(90ca25b5) SHA1(fd9fa78bac25aa3c0792425b21d14e364cf7eea4)) // need verificaiton

	ROM_REGION(0x4000, "diskrom", 0)
	// FDC register contents at 3ff8-3fff
	ROM_LOAD("mpc27disk.rom", 0x0000, 0x4000, BAD_DUMP CRC(38454059) SHA1(58ac78bba29a06645ca8d6a94ef2ac68b743ad32)) // need verification

	ROM_REGION(0x4000, "lpen", 0)
	ROM_LOAD("mlp27.rom", 0x0000, 0x2000, BAD_DUMP CRC(8f9e6ba0) SHA1(c3a47480c9dd2235f40f9a53dab68e3c48adca01)) // need verification
	ROM_RELOAD(0x2000, 0x2000)
ROM_END

void msx2_state::mpc27(machine_config &config)
{
	// YM2149 (in S-3527 MSX Engine)
	// FDC: wd2793?, 1 drive
	// 2 Cartridge slots?
	// S-3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x20000);   // 128KB?? RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "subrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_SS, "disk", 3, 2, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_ROM, "lpen", 3, 3, 1, 1, "lpen");

	msx2(SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX2 - Sanyo PCT-100 */

/* MSX2 - Sanyo PHC-23 - "Wavy23" (and PHC-23J with different keyboard layout) */

ROM_START(phc23)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("23bios.rom", 0x0000, 0x8000, CRC(ba81b3dd) SHA1(4ce41fcc1a603411ec4e99556409c442078f0ecf))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("23ext.rom", 0x0000, 0x4000, CRC(90ca25b5) SHA1(fd9fa78bac25aa3c0792425b21d14e364cf7eea4))
ROM_END

void msx2_state::phc23(machine_config &config)
{
	// YM2149 (in S3527 MSX Engine)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// S3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 2, 0, 4);  // 64KB RAM

	msx2(SND_YM2149, config, layout_msx_jp);
}

/* MSX2 - Sanyo PHC-23J(B) / PHC-23J(GR) - "Wavy23" */

ROM_START(phc23jb)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("23bios.rom", 0x0000, 0x8000, CRC(ba81b3dd) SHA1(4ce41fcc1a603411ec4e99556409c442078f0ecf))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("23ext.rom", 0x0000, 0x4000, CRC(90ca25b5) SHA1(fd9fa78bac25aa3c0792425b21d14e364cf7eea4))
ROM_END

void msx2_state::phc23jb(machine_config &config)
{
	// YM2149 (in S-1985 MSX Engine)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// S-1985 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 2, 0, 4);  // 64KB RAM

	MSX_S1985(config, "s1985", 0);
	msx2(SND_YM2149, config, layout_msx_jp);
}

/* MSX2 - Sanyo Wavy PHC-55FD2 */

ROM_START(phc55fd2)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("phc55fd2bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(ba81b3dd) SHA1(4ce41fcc1a603411ec4e99556409c442078f0ecf)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("phc55fd2ext.rom", 0x0000, 0x4000, BAD_DUMP CRC(90ca25b5) SHA1(fd9fa78bac25aa3c0792425b21d14e364cf7eea4)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	// FDC register contents at 3ff8-3fff
	ROM_LOAD("phc55fd2disk.rom", 0x0000, 0x4000, BAD_DUMP CRC(38454059) SHA1(58ac78bba29a06645ca8d6a94ef2ac68b743ad32)) // need verification
ROM_END

void msx2_state::phc55fd2(machine_config &config)
{
	// YM2149 (in S-1985 MSX Engine)
	// FDC: wd2793?, 2 3.5" DSDD drives
	// 2 Cartridge slots
	// S-1985 MSX Engine
	// T9763

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x20000);   // 128KB RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "subrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_2_DRIVES, "disk", 3, 2, 1, 2, "diskrom");

	MSX_S1985(config, "s1985", 0);

	msx2(SND_YM2149, config, layout_msx_jp_2fdd);
}

/* MSX2 - Sanyo Wavy PHC-77 */

ROM_START(phc77)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("phc77bios.rom", 0x0000, 0x8000, CRC(ba81b3dd) SHA1(4ce41fcc1a603411ec4e99556409c442078f0ecf))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("phc77ext.rom", 0x0000, 0x4000, CRC(43e7a7fc) SHA1(0fbd45ef3dd7bb82d4c31f1947884f411f1ca344))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("phc77disk.rom", 0x0000, 0x4000, BAD_DUMP CRC(7c79759a) SHA1(a427b0c9a344c87b587568ecca7fee0abbe72189)) // Floppy registers visible in dump

	ROM_REGION(0x80000, "msxwrite", 0)
	ROM_LOAD("phc77msxwrite.rom", 0x00000, 0x80000, CRC(ef02e4f3) SHA1(4180544158a57c99162269e33e4f2c77c9fce84e))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("phc77kfn.rom", 0x0000, 0x20000, CRC(d23d4d2d) SHA1(db03211b7db46899df41db2b1dfbec972109a967))
ROM_END

void msx2_state::phc77(machine_config &config)
{
	// YM2149 (in S-1985 MSX Engine)
	// FDC: wd2793?, 1 drive; looks like mb8877a
	// 1 Cartridge slot
	// S-1985 MSX Engine
	// SRAM?
	// Builtin printer
	// Switch to turn off firmware

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_MSX_WRITE, "msxwrite", 2, 1, 2, "msxwrite");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_disk(config, MSX_SLOT_DISK9_WD2793_N, "disk", 3, 0, 1, 1, "diskrom");
	add_cartridge_slot<2>(config, 3, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 3, 0, 4);   // 64KB RAM

	MSX_S1985(config, "s1985", 0);

	msx2(SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX2 - Sharp Epcom HotBit 2.0 - is this an officially released machine? */

ROM_START(hotbit20)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hb2bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(0160e8c9) SHA1(d0cfc35f22b150a1cb10decae4841dfe63b78251)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("hb2ext.rom", 0x0000, 0x4000, BAD_DUMP CRC(08ced880) SHA1(4f2a7e0172f0214f025f23845f6e053d0ffd28e8)) // need verification

	ROM_REGION(0x4000, "xbasic", 0)
	ROM_LOAD("xbasic2.rom", 0x0000, 0x4000, BAD_DUMP CRC(2825b1a0) SHA1(47370bec7ca1f0615a54eda548b07fbc0c7ef398)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("microsoldisk.rom", 0x0000, 0x4000, BAD_DUMP CRC(6704ef81) SHA1(a3028515ed829e900cc8deb403e17b09a38bf9b0)) // need verification
ROM_END

void msx2_state::hotbit20(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: microsol, 1 or 2 drives?
	// 2 Cartridge slots?

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1, 0);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 1, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "xbasic", 1, 1, 1, 1, "xbasic");
	add_internal_disk(config, MSX_SLOT_DISK5_WD2793, "disk", 1, 3, 1, 1, "diskrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 2, 0, 4).set_total_size(0x20000).set_unused_bits(0x80);   // 128KB Mapper RAM
	add_cartridge_slot<2>(config, 3);

	msx2_pal(SND_AY8910, config, layout_msx_nocode_1fdd);
}

/* MSX2 - Sony HB-F1 */

ROM_START(hbf1)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("f1bios.rom", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("f1ext.rom", 0x0000, 0x4000, CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33))

	ROM_REGION(0x4000, "firmware1", 0)
	ROM_LOAD("f1note1.rom", 0x0000, 0x4000, CRC(84810ea8) SHA1(9db72bb78792595a12499c821048504dc96ef848))

	ROM_REGION(0x8000, "firmware2", 0)
	ROM_LOAD("f1note2.rom", 0x0000, 0x8000, CRC(e32e5ee0) SHA1(aa78fc9bcd2343f84cf790310a768ee47f90c841))

	ROM_REGION(0x8000, "firmware3", 0)
	ROM_LOAD("f1note3.rom", 0x0000, 0x8000, CRC(73eb9329) SHA1(58accf41a90693874b86ce98d8d43c27beb8b6dc))
ROM_END

void msx2_state::hbf1(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// S1985
	// pause button

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware1", 3, 0, 1, 1, "firmware1");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware2", 3, 1, 1, 2, "firmware2");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware3", 3, 2, 1, 2, "firmware3");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 3, 0, 4);  // 64KB RAM

	MSX_S1985(config, "s1985", 0);

	msx2(SND_YM2149, config, layout_msx_nocode_nocaps);
}

/* MSX2 - Sony HB-F1II */

ROM_START(hbf1ii)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("f12bios.rom", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("f12ext.rom", 0x0000, 0x4000, CRC(43e7a7fc) SHA1(0fbd45ef3dd7bb82d4c31f1947884f411f1ca344))

	ROM_REGION(0x4000, "firmware1", 0)
	ROM_LOAD("f12note1.rom", 0x0000, 0x4000, CRC(dcacf970) SHA1(30d914cda2180889a40a3328e0a0c1327f4eaa10))

	ROM_REGION(0x8000, "firmware2", 0)
	ROM_LOAD("f12note2.rom", 0x0000, 0x8000, CRC(b0241a61) SHA1(ed2fea5c2a3c2e58d4f69f9d636e08574486a2b1))

	ROM_REGION(0x8000, "firmware3", 0)
	ROM_LOAD("f12note3.rom", 0x0000, 0x8000, CRC(44a10e6a) SHA1(917d1c079e03c4a44de864f123d03c4e32c8daae))
ROM_END

void msx2_state::hbf1ii(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// S1985
	// rensha-turbo slider

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware1", 3, 0, 1, 1, "firmware1");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware2", 3, 1, 1, 2, "firmware2");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware3", 3, 2, 1, 2, "firmware3");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 3, 0, 4);   // 64KB RAM

	MSX_S1985(config, "s1985", 0);

	msx2(SND_YM2149, config, layout_msx_jp);
}

/* MSX2 - Sony HB-F1XD  / HB-F1XDmk2 */
/* HB-F1XDmk2 is a cost-reduced version of HB-F1XD but identical in emulation */

ROM_START(hbf1xd)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("f1xdbios.rom.ic27", 0x0000, 0x8000, CRC(ba81b3dd) SHA1(4ce41fcc1a603411ec4e99556409c442078f0ecf))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("f1xdext.rom.ic27", 0x0000, 0x4000, CRC(43e7a7fc) SHA1(0fbd45ef3dd7bb82d4c31f1947884f411f1ca344))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("f1xddisk.rom.ic27", 0x0000, 0x4000, CRC(54c73ad6) SHA1(12f2cc79b3d09723840bae774be48c0d721ec1c6))
ROM_END

void msx2_state::hbf1xd(machine_config &config)
{
	// YM2149 (in S-1895 MSX Engine)
	// FDC: wd2793, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// S-1985 MSX Engine
	// pause button
	// speed controller slider
	// rensha turbo slider

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_N, "disk", 3, 0, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 3, 0, 4);   // 64KB RAM

	MSX_S1985(config, "s1985", 0);

	msx2(SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX2 - Sony HB-F5 */

ROM_START(hbf5)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hbf5bios.ic25", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("hbf5ext.rom", 0x0000, 0x4000, CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33))

	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("hbf5note.rom", 0x0000, 0x4000, CRC(0cdc0777) SHA1(06ba91d6732ee8a2ecd5dcc38b0ce42403d86708))
ROM_END

void msx2_state::hbf5(machine_config &config)
{
	// YM2149
	// FDC: None, 0 drives
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 0, 1, 1, 1, "firmware");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 0, 2, 0, 4);   // 64KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);

	msx2_pal(SND_YM2149, config, layout_msx_jp);
}

/* MSX2 - Sony HB-F500 */

ROM_START(hbf500)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("f500bios.rom", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("f500ext.rom", 0x0000, 0x4000, CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("f500disk.rom", 0x0000, 0x4000, CRC(f7f5b0ea) SHA1(e93b8da1e8dddbb3742292b0e5e58731b90e9313))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("f500kfn.rom", 0, 0x20000, CRC(5a59926e) SHA1(6acaf2eeb57f65f7408235d5e07b7563229de799))
ROM_END

void msx2_state::hbf500(machine_config &config)
{
	// YM2149 (in S3527)
	// FDC: wd2793, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram1", 0, 0, 2, 2);   // 32KB RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "subrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793, "disk", 0, 1, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram2", 0, 2, 0, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);

	msx2(SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX2 - Sony HB-F500 2nd version (slot layout is different) */

ROM_START(hbf500_2)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("f500bios.rom", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("f500ext.rom", 0x0000, 0x4000, CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("f500disk.rom", 0x0000, 0x4000, CRC(f7f5b0ea) SHA1(e93b8da1e8dddbb3742292b0e5e58731b90e9313))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("f500kfn.rom", 0, 0x20000, CRC(5a59926e) SHA1(6acaf2eeb57f65f7408235d5e07b7563229de799))
ROM_END

void msx2_state::hbf500_2(machine_config &config)
{
	// YM2149 (in S3527)
	// FDC: wd2793, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "subrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793, "disk", 3, 1, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 2, 0, 4);   // 64KB RAM

	msx2(SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX2 - Sony HB-F500F */

ROM_START(hbf500f)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hbf500fbios.rom", 0x0000, 0x8000, BAD_DUMP CRC(440dae3c) SHA1(fedd9b682d056ddd1e9b3d281723e12f859b2e69)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("hbf500fext.rom", 0x0000, 0x4000, BAD_DUMP CRC(e235d5c8) SHA1(792e6b2814ab783d06c7576c1e3ccd6a9bbac34a)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	// FDC register contents at 3ff8-3fff
	ROM_LOAD("hbf500fdisk.rom", 0x0000, 0x4000, BAD_DUMP CRC(6e718f5c) SHA1(0e081572f84555dc13bdb0c7044a19d6c164d985)) // need verification
ROM_END

void msx2_state::hbf500f(machine_config &config)
{
	// YM2149 (in S3527)
	// FDC: wd2793, 1 3.5" DSDD drive
	// 3 Cartridge slots
	// S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram1", 0, 0, 2, 2);   // 32KB RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "subrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_N, "disk", 0, 1, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram2", 0, 2, 0, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_cartridge_slot<3>(config, 3);

	msx2_pal(SND_YM2149, config, layout_msx_nocode_1fdd);
}

/* MSX2 - Sony HB-F500P */

ROM_START(hbf500p)
	ROM_REGION(0x10000, "mainrom", 0)
	ROM_LOAD("500pbios.rom.ic41", 0x0000, 0x8000, CRC(b31c851d) SHA1(0de3c802057560560a03d7965fcc4cff69f8575c))
	// FDC register contents at 7ff8-7fff
	ROM_LOAD("500pext.ic47",      0x8000, 0x8000, BAD_DUMP CRC(cdd4824a) SHA1(505031f1e8396a6e0cb11c1540e6e7f6999d1191))
ROM_END

void msx2_state::hbf500p(machine_config &config)
{
	// YM2149 (in S3527)
	// FDC: wd2793, 1 3.5" DSDD drive
	// 3 Cartridge slots
	// S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram1", 0, 0, 2, 2);   // 32KB RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "mainrom", 0x8000);
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_N, "disk", 0, 1, 1, 2, "mainrom", 0xc000);
	add_internal_slot(config, MSX_SLOT_RAM, "ram2", 0, 2, 0, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_cartridge_slot<3>(config, 3);

	msx2_pal(SND_YM2149, config, layout_msx_nocode_1fdd);
}

/* MSX2 - Sony HB-F700D */

ROM_START(hbf700d)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("700dbios.rom.ic5", 0x0000, 0x8000, CRC(e975aa79) SHA1(cef16eb95502ba6ab2265fcafcedde470a101541))

	ROM_REGION(0x8000, "extrom", 0)
	// dumps as listed in openMSX and blueMSX
	//  ROM_LOAD("700dsub.ic6", 0x0000, 0x4000, CRC(8f84f783) SHA1(3288894e1be6af705871499b23c85732dbc40993))
	//  ROM_LOAD("700ddisk.ic6", 0x4000, 0x4000, CRC(54c73ad6) SHA1(12f2cc79b3d09723840bae774be48c0d721ec1c6))
	//
	// however according to the service manual these should be in the same rom chip
	// concatenation of 3288894e1be6af705871499b23c85732dbc40993 and 12f2cc79b3d09723840bae774be48c0d721ec1c6
	ROM_LOAD("700dext.ic6", 0x0000, 0x8000, BAD_DUMP CRC(2aba42dc) SHA1(9dee68aab6c921b0b20862a3f2f4e38ff8d155c0)) // to be verified with direct dump
ROM_END

void msx2_state::hbf700d(machine_config &config)
{
	// YM2149 (in S-1985 MSX Engine)
	// FDC: wd2793, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// S-1985 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "extrom", 3, 0, 0, 1, "extrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_N, "disk", 3, 0, 1, 2, "extrom", 0x4000);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 3, 0, 4).set_total_size(0x40000).set_unused_bits(0x80);   // 256KB Mapper RAM

	MSX_S1985(config, "s1985", 0);

	msx2_pal(SND_YM2149, config, layout_msx_nocode_1fdd);
}

/* MSX2 - Sony HB-F700F */

ROM_START(hbf700f)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("700fbios.ic5", 0x0000, 0x8000, CRC(440dae3c) SHA1(fedd9b682d056ddd1e9b3d281723e12f859b2e69))

	ROM_REGION(0x8000, "extrom", 0)
	// dumps as listed in openMSX and blueMSX
	//  ROM_LOAD("700fsub.ic6", 0x0000, 0x4000, CRC(e235d5c8) SHA1(792e6b2814ab783d06c7576c1e3ccd6a9bbac34a))
	//  ROM_LOAD("700fdisk.ic6", 0x4000, 0x4000, CRC(54c73ad6) SHA1(12f2cc79b3d09723840bae774be48c0d721ec1c6))
	//
	// however according to the service manual these should be in the same rom chip
	// concatenation of 792e6b2814ab783d06c7576c1e3ccd6a9bbac34a and 12f2cc79b3d09723840bae774be48c0d721ec1c6
	ROM_LOAD("700fext.ic6",  0x0000, 0x8000, BAD_DUMP CRC(463db23b) SHA1(2ab5be13b356692e75a5d76a23f8e4cfc094b3df)) // to be verified with direct dump
ROM_END

void msx2_state::hbf700f(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: wd2793, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// S1985

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "extrom", 3, 0, 0, 1, "extrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_N, "disk", 3, 0, 1, 2, "extrom", 0x4000);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 3, 0, 4).set_total_size(0x40000).set_unused_bits(0x80);   // 256KB Mapper RAM

	MSX_S1985(config, "s1985", 0);

	msx2_pal(SND_YM2149, config, layout_msx_nocode_1fdd);
}

/* MSX2 - Sony HB-F700P */

ROM_START(hbf700p)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("700pbios.rom.ic5", 0x0000, 0x8000, CRC(b31c851d) SHA1(0de3c802057560560a03d7965fcc4cff69f8575c))

	ROM_REGION(0x8000, "extrom", 0)
	// dumps as listed in openMSX / blueMSX
	// openMSX also lists 24624c5fa3a8069b1d865cdea8a029f15c1955ea for the subrom but the disk rom
	// part of that machine is 'certainly not original' so this may also not be original.
	//  ROM_LOAD("700psub.ic6", 0x0000, 0x4000, CRC(8f84f783) SHA1(3288894e1be6af705871499b23c85732dbc40993))
	//  ROM_LOAD("700pdisk.ic6", 0x4000, 0x4000, CRC(1d9cc7f6) SHA1(3376cf9dd2b1ac9b41bf6bf6598b33136e86f9d5))
	//
	// however according to the service manual these should be in the same rom chip
	// concatenation of 3288894e1be6af705871499b23c85732dbc40993 and 3376cf9dd2b1ac9b41bf6bf6598b33136e86f9d5
	ROM_LOAD("700pext.ic6", 0x0000, 0x8000, BAD_DUMP CRC(63e1bffc) SHA1(496698a60432490dc1306c8cc1d4a6ded275261a)) // to be verified with direct dump
ROM_END

void msx2_state::hbf700p(machine_config &config)
{
	// YM2149 (in S-1985 MSX Engine)
	// FDC: wd2793, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// S-1985 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "extrom", 3, 0, 0, 1, "extrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793, "disk", 3, 0, 1, 2, "extrom", 0x4000);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 3, 0, 4).set_total_size(0x40000).set_unused_bits(0x80);   // 256KB Mapper RAM

	MSX_S1985(config, "s1985", 0);

	msx2_pal(SND_YM2149, config, layout_msx_nocode_1fdd);
}

/* MSX2 - Sony HB-F700S */

ROM_START(hbf700s)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("700sbios.rom.ic5", 0x0000, 0x8000, CRC(c2b889a5) SHA1(4811956f878c3e03da46317f787cdc4bebc86f47))

	ROM_REGION(0x8000, "extrom", 0)
	// dumps as listed in openMSX / blueMSX
	//  ROM_LOAD("700ssub.ic6", 0x0000, 0x4000, CRC(dc0951bd) SHA1(1e9a955943aeea9b1807ddf1250ba6436d8dd276))
	//  ROM_LOAD("700sdisk.ic6", 0x4000, 0x4000, CRC(1d9cc7f6) SHA1(3376cf9dd2b1ac9b41bf6bf6598b33136e86f9d5))
	//
	// however according to the service manual these should be in the same rom chip
	// concatenation of 1e9a955943aeea9b1807ddf1250ba6436d8dd276 and 3376cf9dd2b1ac9b41bf6bf6598b33136e86f9d5
	ROM_LOAD("700sext.ic6", 0x0000, 0x8000, BAD_DUMP CRC(28d1badf) SHA1(ae3ed88a2d7034178e08f7bdf5409f462bf67fc9)) // to be verified with direct dump
ROM_END

void msx2_state::hbf700s(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: wd2793, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// S1985

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "extrom", 3, 0, 0, 1, "extrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793, "disk", 3, 0, 1, 2, "extrom", 0x4000);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 3, 0, 4).set_total_size(0x40000).set_unused_bits(0x80);   // 256KB Mapper RAM

	MSX_S1985(config, "s1985", 0);

	msx2_pal(SND_YM2149, config, layout_msx_nocode_1fdd);
}

/* MSX2 - Sony HB-F750 (prototype) */

/* MSX2 - Sony HB-F900 */

ROM_START(hbf900)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("f900bios.rom", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("f900ext.rom", 0x0000, 0x4000, CRC(43e7a7fc) SHA1(0fbd45ef3dd7bb82d4c31f1947884f411f1ca344))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("f900disk.rom", 0x0000, 0x4000, CRC(f83d0ea6) SHA1(fc760d1d7b16370abc7eea39955f230b95b37df6))

	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("f900util.rom", 0x0000, 0x4000, CRC(bc6c7c66) SHA1(558b7383544542cf7333700ff90c3efbf93ba2a3))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("f900kfn.rom", 0, 0x20000, CRC(5a59926e) SHA1(6acaf2eeb57f65f7408235d5e07b7563229de799))
ROM_END

void msx2_state::hbf900(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: wd2793, 2 3.5" DSDD drives
	// 2 Cartridge slots
	// S1985

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 1, 0, 4).set_total_size(0x40000).set_unused_bits(0x80);   // 256KB Mapper RAM
	add_internal_disk(config, MSX_SLOT_DISK1_WD2793_2_DRIVES, "disk", 3, 2, 1, 1, "diskrom");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 3, 3, 1, 1, "firmware");

	MSX_S1985(config, "s1985", 0);

	msx2(SND_YM2149, config, layout_msx_jp_2fdd);
}

/* MSX2 - Sony HB-F900 (a) */

ROM_START(hbf900a)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("f900bios.rom", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("f900ext.rom", 0x0000, 0x4000, CRC(43e7a7fc) SHA1(0fbd45ef3dd7bb82d4c31f1947884f411f1ca344))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("f900disa.rom", 0x0000, 0x4000, CRC(54c73ad6) SHA1(12f2cc79b3d09723840bae774be48c0d721ec1c6))

	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("f900util.rom", 0x0000, 0x4000, CRC(bc6c7c66) SHA1(558b7383544542cf7333700ff90c3efbf93ba2a3))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("f900kfn.rom", 0, 0x20000, CRC(5a59926e) SHA1(6acaf2eeb57f65f7408235d5e07b7563229de799))
ROM_END

void msx2_state::hbf900a(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: wd2793, 2 3.5" DSDD drives
	// 2 Cartridge slots
	// S1985

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 1, 0, 4).set_total_size(0x40000).set_unused_bits(0x80);   // 256KB Mapper RAM
	add_internal_disk(config, MSX_SLOT_DISK1_WD2793_N_2_DRIVES, "disk", 3, 2, 1, 1, "diskrom");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 3, 3, 1, 1, "firmware");

	MSX_S1985(config, "s1985", 0);

	msx2(SND_YM2149, config, layout_msx_jp_2fdd);
}

/* MSX2 - Sony HB-F9P */

ROM_START(hbf9p)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("f9pbios.rom.ic11", 0x0000, 0x8000, CRC(b31c851d) SHA1(0de3c802057560560a03d7965fcc4cff69f8575c))

	ROM_REGION(0x8000, "subfirm", 0)
	// dumps as listed in openMSX / blueMSX
	// ROM_LOAD("f9psub.rom", 0x0000, 0x4000, CRC(7c456c8b) SHA1(7b4a96402847decfc110ff9eda713bdcd218bd83))
	// ROM_LOAD("f9pfirm2.rom", 0x0000, 0x4000, CRC(dea2cb50) SHA1(8cc1f7ceeef745bb34e80253971e137213671486))
	// concatenation of 7b4a96402847decfc110ff9eda713bdcd218bd83 and 8cc1f7ceeef745bb34e80253971e137213671486
	ROM_LOAD("f9pfirm1.ic12", 0x0000, 0x8000, BAD_DUMP CRC(524f67aa) SHA1(41a186afced50ca6312cb5b6c4adb684faca6232))

	ROM_REGION(0x8000, "firmware", 0)
	// like in HB-F9S, the halves should be swapped?
	ROM_LOAD("f9pfirm2.rom.ic13", 0x0000, 0x8000, BAD_DUMP CRC(ea97069f) SHA1(2d1880d1f5a6944fcb1b198b997a3d90ecd1903d))
ROM_END

void msx2_state::hbf9p(machine_config &config)
{
	// YM2149 (in S-1985 MSX Engine)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// S-1985 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subfirm", 3, 0, 0, 2, "subfirm");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 3, 1, 1, 2, "firmware");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0x80);   // 128KB Mapper RAM

	MSX_S1985(config, "s1985", 0);
	msx2_pal(SND_YM2149, config, layout_msx_nocode);
}

/* MSX2 - Sony HB-F9P Russian */

ROM_START(hbf9pr)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("f9prbios.rom", 0x0000, 0x8000, CRC(f465311b) SHA1(7f440ec7295d889b097e1b66bf9bc5ce086f59aa))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("f9prext.rom", 0x0000, 0x4000, CRC(d701adac) SHA1(a6d7b1fd4ee896ca7513d02c033fc9a8aa065235))
ROM_END

void msx2_state::hbf9pr(machine_config &config)
{
	// YM2149 (in S-1985 MSX Engine)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// S-1985 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "ext", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "ext_mirror", 3, 0, 1, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000);   // 128KB Mapper RAM

	MSX_S1985(config, "s1985", 0);
	msx2_pal(SND_YM2149, config, layout_msx_ru);
}

/* MSX2 - Sony HB-F9S */

ROM_START(hbf9s)
	ROM_REGION(0x18000, "mainrom", 0)
	ROM_LOAD("f9sbios.ic11", 0x0000, 0x8000, CRC(c2b889a5) SHA1(4811956f878c3e03da46317f787cdc4bebc86f47))

	ROM_REGION(0x8000, "subfirm", 0)
	ROM_LOAD("f9sfirm1.ic12", 0x0000, 0x8000, CRC(cf39620b) SHA1(1166a93d7185ba024bdf2bfa9a30e1c447fb6db1))

	ROM_REGION(0x8000, "firmware", 0)
	ROM_LOAD("f9sfirm2.ic13", 0x0000, 0x8000, CRC(4a271395) SHA1(7efac54dd8f580f3b7809ab35db4ae58f0eb84d1))
ROM_END

void msx2_state::hbf9s(machine_config &config)
{
	// YM2149 (in S-1985 MSX Engine)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// S-1985 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subfirm", 3, 0, 0, 2, "subfirm");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware_lo", 3, 1, 1, 1, "firmware", 0x4000);
	add_internal_slot(config, MSX_SLOT_ROM, "firmware_hi", 3, 1, 2, 1, "firmware");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0x80);   // 128KB Mapper RAM

	MSX_S1985(config, "s1985", 0);
	msx2_pal(SND_YM2149, config, layout_msx_nocode);
}

/* MSX2 - Sony HB-G900AP */

/* IC109 - 32KB Basic ROM SLOT#00 0000-7FFF */
/* IC112 - 16KB Basic ROM SLOT#01 0000-3FFF */
/* IC117 - 16KB Disk ROM SLOT#01 4000-7FFF */
/* IC123 - 32KB ROM RS232C ROM SLOT#02 4000-7FFF / Video Utility ROM SLOT#03 4000-7FFF */

/* MSX2 - Sony HB-G900AP */
ROM_START(hbg900ap)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("g900bios.ic109",  0x0000, 0x8000, CRC(b31c851d) SHA1(0de3c802057560560a03d7965fcc4cff69f8575c))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("g900ext.ic112", 0x0000, 0x4000, CRC(8f84f783) SHA1(3288894e1be6af705871499b23c85732dbc40993))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("g900disk.ic117", 0x0000, 0x4000, CRC(54c73ad6) SHA1(12f2cc79b3d09723840bae774be48c0d721ec1c6))

	ROM_REGION(0x4000, "rs232", 0)
	// Contents very likely to be ok, but should be inside a single rom together with firmware
	ROM_LOAD("g900232c.rom", 0x0000, 0x2000, BAD_DUMP CRC(be88e5f7) SHA1(b2776159a7b92d74308b434a6b3e5feba161e2b7))

	ROM_REGION(0x4000, "firmware", 0)
	// Contents very likely to be ok, but should be inside a single rom together with rs232 code
	ROM_LOAD("g900util.rom", 0x0000, 0x4000, BAD_DUMP CRC(ecf6abcf) SHA1(6bb18cd2d69f124ad0c7c23a13eb0d2139037696))
ROM_END

void msx2_state::hbg900ap(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: wd2793, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// S1985
	// rs232 switch for terminal / modem operation
	// rs232 2kb ram

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "subrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_N, "disk", 0, 1, 1, 2, "diskrom");
	add_internal_slot_irq_mirrored<3>(config, MSX_SLOT_RS232_SONY, "rs232", 0, 2, 0, 4, "rs232");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 0, 3, 1, 1, "firmware");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	// slot #3 is expanded
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x80000).set_unused_bits(0x80);   // 512KB Mapper RAM

	msx2_pal(SND_YM2149, config, layout_msx_nocode_1fdd);
}

/* MSX2 - Sony HB-G900D */

/* MSX2 - Sony HB-G900F */

/* MSX2 - Sony HB-G900P - 3x 32KB ROMs */

ROM_START(hbg900p)
	ROM_REGION(0x18000, "mainrom", 0)
	ROM_LOAD("g900bios.rom", 0x0000, 0x8000, CRC(b31c851d) SHA1(0de3c802057560560a03d7965fcc4cff69f8575c))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("g900ext.rom", 0x0000, 0x4000, CRC(8f84f783) SHA1(3288894e1be6af705871499b23c85732dbc40993))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("g900disk.rom", 0x0000, 0x4000, CRC(54c73ad6) SHA1(12f2cc79b3d09723840bae774be48c0d721ec1c6))

	ROM_REGION(0x4000, "rs232", 0)
	ROM_LOAD("g900232c.rom", 0x0000, 0x2000, CRC(be88e5f7) SHA1(b2776159a7b92d74308b434a6b3e5feba161e2b7))

	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("g900util.rom", 0x0000, 0x4000, CRC(d0417c20) SHA1(8779b004e7605a3c419825f0373a5d8fa84e1d5b))
ROM_END

void msx2_state::hbg900p(machine_config &config)
{
	// AY8910
	// FDC: wd2793, 1 3.5" DSDD drive
	// 2 Cartridge slots

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "subrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_N, "diskrom", 0, 1, 1, 2, "diskrom");
	add_internal_slot_irq_mirrored<3>(config, MSX_SLOT_RS232_SONY, "rs232", 0, 2, 0, 4, "rs232");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 0, 3, 1, 1, "firmware");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram", 3, 0, 4);   // 64KB RAM

	msx2_pal(SND_AY8910, config, layout_msx_nocode_1fdd);
}

/* MSX2 - Sony HB-T600 */

/* MSX2 - Sony HB-T7 */

/* MSX2 - Talent DPC-300 */

/* MSX2 - Talent TPC-310 */

ROM_START(tpc310)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("tpc310bios.rom", 0x0000, 0x8000, CRC(8cd3e845) SHA1(7bba23669b7abfb6a142f9e1735b847d6e4e8267))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("tpc310ext.rom", 0x0000, 0x4000, CRC(094a9e7a) SHA1(39dfc46260f99b670916b1e55f67a5d4136e6e54))

	ROM_REGION(0x4000, "turbo", 0)
	ROM_LOAD("tpc310turbo.rom", 0x0000, 0x4000, CRC(0ea62a4d) SHA1(181bf58da7184e128cd419da3109b93344a543cf))

	ROM_REGION(0x8000, "firmware", 0)
	ROM_LOAD("tpc310acc.rom", 0x0000, 0x8000, CRC(4fb8fab3) SHA1(cdeb0ed8adecaaadb78d5a5364fd603238591685))
ROM_END

void msx2_state::tpc310(machine_config &config)
{
	// YM2149 (in S-1985 MSX Engine)
	// FDC: None, 0 drives
	// 1 Cartridge slot (slot 2)
	// S-1985 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 1, 0, 4).set_total_size(0x20000).set_unused_bits(0x80);   // 128KB Mapper RAM
	add_cartridge_slot<1>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "turbo", 3, 0, 1, 1, "turbo");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 3, 1, 1, 2, "firmware");
	// Expansion slot in slot #3-2

	MSX_S1985(config, "s1985", 0);

	msx2_pal(SND_YM2149, config, layout_msx_nocode);
}

/* MSX2 - Talent TPP-311 */

ROM_START(tpp311)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("311bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(8cd3e845) SHA1(7bba23669b7abfb6a142f9e1735b847d6e4e8267)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("311ext.rom", 0x0000, 0x4000, BAD_DUMP CRC(094a9e7a) SHA1(39dfc46260f99b670916b1e55f67a5d4136e6e54)) // need verification

	ROM_REGION(0x8000, "logo", 0)
	ROM_LOAD("311logo.rom", 0x0000, 0x8000, BAD_DUMP CRC(0e6ecb9f) SHA1(e45ddc5bf1a1e63756d11fb43fc50276ca35cab0)) // need verification
ROM_END

void msx2_state::tpp311(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: None, 0 drives
	// 0 Cartridge slots?
	// S1985
	// 64KB VRAM

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 1, 0, 4).set_total_size(0x10000);   // 64KB?? Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "logo", 2, 1, 2, "logo");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 1, "subrom");

	MSX_S1985(config, "s1985", 0);

	msx2_pal(SND_YM2149, config, layout_msx_nocode);
	msx2_64kb_vram(config);
}

/* MSX2 - Talent TPS-312 */

ROM_START(tps312)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("312bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(8cd3e845) SHA1(7bba23669b7abfb6a142f9e1735b847d6e4e8267)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("312ext.rom", 0x0000, 0x4000, BAD_DUMP CRC(094a9e7a) SHA1(39dfc46260f99b670916b1e55f67a5d4136e6e54)) // need verification

	ROM_REGION(0x8000, "plan", 0)
	ROM_LOAD("312plan.rom", 0x0000, 0x8000, BAD_DUMP CRC(b3a6aaf6) SHA1(6de80e863cdd7856ab7aac4c238224a5352bda3b)) // need verification

	ROM_REGION(0x4000, "write", 0)
	ROM_LOAD("312write.rom", 0x0000, 0x4000, BAD_DUMP CRC(63c6992f) SHA1(93682f5baba7697c40088e26f99ee065c78e83b8)) // need verification
ROM_END

void msx2_state::tps312(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: None, 0 drives
	// 1 Cartridge slot
	// 64KB VRAM
	// S1985

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 1, 0, 4).set_total_size(0x10000);   // 64KB Mapper RAM
	add_cartridge_slot<1>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "write", 3, 0, 1, 1, "write");
	add_internal_slot(config, MSX_SLOT_ROM, "plan", 3, 1, 0, 2, "plan");
	add_internal_slot(config, MSX_SLOT_ROM, "plan_mirror", 3, 1, 2, 2, "plan");
	// Expansion slot in slot #3-2

	MSX_S1985(config, "s1985", 0);

	msx2_pal(SND_YM2149, config, layout_msx_nocode);
	msx2_64kb_vram(config);
}

/* MSX2 - Toshiba FS-TM1 */

ROM_START(fstm1)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("fstm1bios.rom", 0x0000, 0x8000, CRC(d1e11d52) SHA1(7a69e9b9595f3b0060155f4b419c915d4d9d8ca1))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("fstm1ext.rom", 0x0000, 0x4000, CRC(4eebe9b1) SHA1(a4bdbdb20bf9fd3c492a890fbf541bf092eaa8e1))

	ROM_REGION(0x8000, "deskpac1", 0)
	ROM_LOAD("fstm1desk1.rom", 0x0000, 0x8000, CRC(8b802086) SHA1(30737040d90c136d34dd409fe579bc4cca11c469))

	ROM_REGION(0x8000, "deskpac2", 0)
	ROM_LOAD("fstm1desk2.rom", 0x0000, 0x8000, CRC(304820ea) SHA1(ff6e07d3976b0874164fae680ae028d598752049))
ROM_END

void msx2_state::fstm1(machine_config &config)
{
	// YM2149 (in S-1985)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// S-1985 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x10000); // 64KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "deskpac1", 3, 1, 1, 2, "deskpac1");
	add_internal_slot(config, MSX_SLOT_ROM, "deskpac2", 3, 3, 1, 2, "deskpac2");

	MSX_S1985(config, "s1985", 0);
	// Hard to see on pictures whether the machine has a CAPS led
	msx2_pal(SND_YM2149, config, layout_msx_nocode);
}

/* MSX2 - Toshiba HX-23 */

ROM_START(hx23)
	// roms from hx23f, assumed to be the same for hx23 but need verification
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hx23bios.ic2", 0x0000, 0x8000, BAD_DUMP CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd)) // need verification

	ROM_REGION(0x8000, "subjwp", 0)
	ROM_LOAD("hx23subjwp.ic52", 0x0000, 0x8000, BAD_DUMP CRC(478016bf) SHA1(6ecf73a1dd55b363c2e68cc6245ece979aec1fc5)) // need verification

	ROM_REGION(0x8000, "rs232jwp", 0)
	ROM_LOAD("hx23rs232jwp.ic3", 0x0000, 0x8000, BAD_DUMP CRC(60160d3b) SHA1(0958361ac9b19782cf7017b2e762b416e0203f37)) // need verification
ROM_END

void msx2_state::hx23(machine_config &config)
{
	// AY8910
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// 64KB VRAM
	// HX-R701 RS-232 optional
	// TCX-1012

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram1", 0, 2, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram2", 3, 0, 0, 2);   // 32KB RAM
	add_internal_slot(config, MSX_SLOT_ROM, "sub", 3, 1, 0, 1, "subjwp");
	add_internal_slot(config, MSX_SLOT_ROM, "jwp", 3, 1, 2, 1, "subjwp", 0x4000);
	add_internal_slot(config, MSX_SLOT_ROM, "rs232jwp", 3, 3, 1, 2, "rs232jwp");

	msx2(SND_AY8910, config, layout_msx_jp);
	msx2_64kb_vram(config);
}

/* MSX2 - Toshiba HX-23F */

ROM_START(hx23f)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hx23bios.ic2", 0x0000, 0x8000, BAD_DUMP CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd)) // need verification

	ROM_REGION(0x8000, "subjwp", 0)
	ROM_LOAD("hx23subjwp.ic52", 0x0000, 0x8000, BAD_DUMP CRC(478016bf) SHA1(6ecf73a1dd55b363c2e68cc6245ece979aec1fc5)) // need verification

	ROM_REGION(0x8000, "rs232jwp", 0)
	ROM_LOAD("hx23rs232jwp.ic3", 0x0000, 0x8000, BAD_DUMP CRC(60160d3b) SHA1(0958361ac9b19782cf7017b2e762b416e0203f37)) // need verification
ROM_END

void msx2_state::hx23f(machine_config &config)
{
	// AY8910
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// HX-R701 RS-232 optional
	// TCX-1012

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram1", 0, 2, 2);   // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM, "ram2", 3, 0, 0, 2);   // 32KB RAM
	add_internal_slot(config, MSX_SLOT_ROM, "sub", 3, 1, 0, 1, "subjwp");
	add_internal_slot(config, MSX_SLOT_ROM, "jwp", 3, 1, 2, 1, "subjwp", 0x4000);
	add_internal_slot(config, MSX_SLOT_ROM, "rs232jwp", 3, 3, 1, 2, "rs232jwp");

	msx2(SND_AY8910, config, layout_msx_jp);
}

/* MSX2 - Toshiba HX-33 */

ROM_START(hx33)
	ROM_REGION(0x20000, "mainrom", 0)
	ROM_LOAD("hx33bios.ic7", 0x0000, 0x20000, CRC(8dd5502b) SHA1(5e057526fe39d79e88e7ff1ce02ed669bd38929e))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("hx33kfn.rom", 0x0000, 0x20000, CRC(d23d4d2d) SHA1(db03211b7db46899df41db2b1dfbec972109a967))
ROM_END

void msx2_state::hx33(machine_config &config)
{
	// YM2149
	// FDC: None, 0, drives
	// 2 Cartridge slots
	// TCX-2001 + TCX-2002
	// HX-R702 RS-232 optional
	// 2KB SRAM
	// copy button

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x10000); // 64KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "mainrom", 0x8000);
	add_internal_slot(config, MSX_SLOT_RS232_TOSHIBA_HX3X, "firmware", 3, 3, 1, 2, "mainrom", 0xc000);

	msx2(SND_YM2149, config, layout_msx_jp);
	msx2_64kb_vram(config);
}

/* MSX@ - Toshiba HX-34 */

ROM_START(hx34)
	ROM_REGION(0x20000, "mainrom", 0)
	ROM_LOAD("hx33bios.ic7", 0x0000, 0x20000, CRC(8dd5502b) SHA1(5e057526fe39d79e88e7ff1ce02ed669bd38929e))

	ROM_REGION(0x4000, "diskrom", 0)
	// hx34disk.rom has contents of floppy registers at offset 3ff0-3ff7 and mirrored at 3ff8-3fff
	ROM_LOAD("hx34disk.rom", 0x0000, 0x4000, BAD_DUMP CRC(626b719d) SHA1(c88ef953b21370cbaef5e82575d093d6f9047ec6))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("hx34kfn.rom", 0x0000, 0x20000, CRC(d23d4d2d) SHA1(db03211b7db46899df41db2b1dfbec972109a967))
ROM_END

void msx2_state::hx34(machine_config &config)
{
	// YM2149
	// FDC: wd2793??, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// TCX-2001 + TCX-2002
	// HX-R703 RS232 optional
	// 2KB SRAM
	// copy button

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x10000); // 64KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "mainrom", 0x8000);
	add_internal_disk(config, MSX_SLOT_DISK6_WD2793_N, "disk", 3, 2, 1, 1, "diskrom");
	add_internal_slot(config, MSX_SLOT_RS232_TOSHIBA_HX3X, "firmware", 3, 3, 1, 2, "mainrom", 0xc000);

	msx2(SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX2 - Victor HC-80 */

ROM_START(victhc80)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hc80bios.rom",  0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("hc80ext.rom", 0x0000, 0x4000, CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33))

	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("hc80firm.rom", 0x0000, 0x4000, CRC(30e8c08d) SHA1(7f498db2f431b9c0b42dac1c7ca46a236b780228))
ROM_END

void msx2_state::victhc80(machine_config &config)
{
	// YM2149 (in S3527)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// S3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram1", 0, 0, 2, 2); // 32KB RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_RAM, "ram2", 0, 2, 0, 2); // 32KB RAM
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 3, 2, 1, "firmware");

	msx2(SND_YM2149, config, layout_msx_jp);
}

/* MSX2 - Victor HC-90 */

ROM_START(victhc90)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hc90bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("hc90ext.rom", 0x0000, 0x4000, BAD_DUMP CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	// FDC register contents at 3ff8-3fff
	ROM_LOAD("hc90disk.rom", 0x0000, 0x4000, BAD_DUMP CRC(11bca2ed) SHA1(a7a34671bddb48fa6c74182e2977f9129558ec32)) // need verification

	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("hc90firm.rom", 0x0000, 0x4000, BAD_DUMP CRC(53791d91) SHA1(caeffdd654394726c8c0824b21af7ff51c0b1031)) // need verification

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("hc90kfn.rom", 0x0000, 0x20000, BAD_DUMP CRC(d23d4d2d) SHA1(db03211b7db46899df41db2b1dfbec972109a967)) // need verification
ROM_END

void msx2_state::victhc90(machine_config &config)
{
	// YM2149
	// FDC: mb8877a?, 1 3.5" DSDD drive
	// RS232C builtin
	// 2nd CPU HD-64B180 @ 6.144 MHz
	// 1 Cartridge slot (slot 1 or 2?)

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "subrom");
	add_internal_slot_irq<2>(config, MSX_SLOT_RS232, "firmware", 0, 1, 1, 1, "firmware");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 0, 2, 0, 4).set_total_size(0x10000); // 64KB Mapper RAM
	add_cartridge_slot<1>(config, 1);
	add_internal_disk(config, MSX_SLOT_DISK10_MB8877, "disk", 3, 1, 1, "diskrom");

	msx2(SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX2 - Victor HC-90(A) */

ROM_START(victhc90a)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("msx2basic_tmm23256.ic023", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("msx2basicext_tmm23128p.ic034", 0x0000, 0x4000, CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33))

	ROM_REGION(0x8000, "rs232fdd", 0)
	ROM_LOAD("rs232c_fdd_jvc024c_27c256.ic052", 0x000, 0x8000, CRC(19cfc325) SHA1(c991440778d5dc9ba54cc0e0f8e032d2f451366f))
	// Patch to fake reads from the system control register
	ROM_FILL(0x3ffd, 1, 0x80)
	ROM_FILL(0x7ffd, 1, 0x80)

	ROM_REGION(0x8000, "turbo", 0)
	ROM_LOAD("turbo_jvc019e_27c256.ic040", 0x0000, 0x8000, CRC(7820ea1a) SHA1(ae81cc93e3992e253d42f48451adc4806074f494))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("hc90a_kanjifont.rom", 0x0000, 0x20000, CRC(d23d4d2d) SHA1(db03211b7db46899df41db2b1dfbec972109a967))
ROM_END

void msx2_state::victhc90a(machine_config &config)
{
	// YM2149
	// FDC: mb8877a?, 1 3.5" DSDD drive
	// RS232C builtin
	// 2nd CPU HD-64B180 @ 6.144 MHz
	// 1 Cartridge slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "subrom");
	add_internal_slot_irq<2>(config, MSX_SLOT_RS232, "rs232fdd", 0, 1, 1, 1, "rs232fdd");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 0, 2, 0, 4).set_total_size(0x40000); // 256KB Mapper RAM
	add_cartridge_slot<1>(config, 1);
	add_internal_disk(config, MSX_SLOT_DISK10_MB8877, "disk", 3, 1, 1, "rs232fdd", 0x4000);

	msx2(SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX2 - Victor HC-90(B) */

/* MSX2 - Victor HC-90(V) */

/* MSX2 - Victor HC-90(T) */

/* MSX2 - Victor HC-95 */

ROM_START(victhc95)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hc95bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("hc95ext.rom", 0x0000, 0x4000, BAD_DUMP CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	// FDC register contents at 3ff8-3fff
	ROM_LOAD("hc95disk.rom", 0x0000, 0x4000, BAD_DUMP CRC(11bca2ed) SHA1(a7a34671bddb48fa6c74182e2977f9129558ec32)) // need verification

	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("hc95firm.rom", 0x0000, 0x4000, BAD_DUMP CRC(53791d91) SHA1(caeffdd654394726c8c0824b21af7ff51c0b1031)) // need verification

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("hc95kfn.rom", 0x0000, 0x20000, BAD_DUMP CRC(d23d4d2d) SHA1(db03211b7db46899df41db2b1dfbec972109a967)) // need verification
ROM_END

void msx2_state::victhc95(machine_config &config)
{
	// YM2149
	// FDC: mb8877a, 2 3.5" DSDD drive
	// RS232C builtin
	// 2nd CPU HD-64B180 @ 6.144 MHz
	// 1 Cartridge slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 0, 1, 1, 1, "firmware");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 0, 2, 0, 4).set_total_size(0x10000); // 64KB Mapper RAM
	// 96 pin expansion bus in slot #0-3
	add_cartridge_slot<1>(config, 1);
	// 96 pin expansion bus in slot #2
	add_internal_disk(config, MSX_SLOT_DISK10_MB8877_2_DRIVES, "disk", 3, 1, 1, "diskrom");

	msx2(SND_YM2149, config, layout_msx_jp_2fdd);
}

/* MSX2 - Victor HC-95A */

ROM_START(victhc95a)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("hc95abios.rom", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("hc95aext.rom", 0x0000, 0x4000, CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33))

	ROM_REGION(0x4000, "diskrom", 0)
	// FDC register contents at 3ff8-3fff
	ROM_LOAD("hc95adisk.rom", 0x0000, 0x4000, BAD_DUMP CRC(11bca2ed) SHA1(a7a34671bddb48fa6c74182e2977f9129558ec32))

	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("hc95afirm.rom", 0x0000, 0x4000, CRC(53791d91) SHA1(caeffdd654394726c8c0824b21af7ff51c0b1031))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("hc95akfn.rom", 0x0000, 0x20000, CRC(d23d4d2d) SHA1(db03211b7db46899df41db2b1dfbec972109a967))
ROM_END

void msx2_state::victhc95a(machine_config &config)
{
	// YM2149
	// FDC: mb8877a, 2 3.5" DSDD drive
	// RS232C builtin
	// 2nd CPU HD-64B180 @ 6.144 MHz
	// 1 Cartridge slot

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 0, 1, 1, 1, "firmware");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 0, 2, 0, 4).set_total_size(0x10000); // 64KB Mapper RAM
	// 96 pin expansion bus in slot #0-3
	add_cartridge_slot<1>(config, 1);
	// 96 pin expansion bus in slot #2
	add_internal_disk(config, MSX_SLOT_DISK10_MB8877_2_DRIVES, "disk", 3, 1, 1, "diskrom");

	msx2(SND_YM2149, config, layout_msx_jp_2fdd);
}

/* MSX2 - Wandy CPC-300 */

/* MSX2 - Yamaha CX7/128 */

ROM_START(cx7128)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("cx7mbios.rom", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("cx7mext.rom", 0x0000, 0x4000, CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33))
ROM_END

void msx2_state::cx7128(machine_config &config)
{
	// YM2149 (in S3527)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_cartridge_slot<3>(config, MSX_SLOT_YAMAHA_MINICART, "minicart", 3, 1, msx_yamaha_minicart, nullptr);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0x80);   // 64KB Mapper RAM
	add_cartridge_slot<4>(config, MSX_SLOT_YAMAHA_EXPANSION, "module", 3, 3, msx_yamaha_60pin, nullptr);

	msx2(SND_YM2149, config, layout_msx_jp);
	SOFTWARE_LIST(config, "minicart_list").set_original("msx_yamaha_minicart");
}

/* MSX2 - Yamaha CX7M/128 */

ROM_START(cx7m128)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("cx7mbios.rom", 0x0000, 0x8000, BAD_DUMP CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd)) // needs verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("cx7mext.rom", 0x0000, 0x4000, BAD_DUMP CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33)) // needs verification
ROM_END

void msx2_state::cx7m128(machine_config &config)
{
	// YM2149 (in S3527)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// S3527

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_cartridge_slot<3>(config, MSX_SLOT_YAMAHA_MINICART, "minicart", 3, 1, msx_yamaha_minicart, nullptr);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000).set_unused_bits(0x80);   // 64KB Mapper RAM
	add_cartridge_slot<4>(config, MSX_SLOT_YAMAHA_EXPANSION, "module", 3, 3, msx_yamaha_60pin, "sfg05");

	msx2(SND_YM2149, config, layout_msx_jp);
	SOFTWARE_LIST(config, "minicart_list").set_original("msx_yamaha_minicart");
}

/* MSX2 - Yamaha YIS-503 III R (student) */

ROM_START(y503iiir)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("yis503iiirbios.rom", 0x0000, 0x8000, CRC(e7d08e29) SHA1(0f851ee7a1cf79819f61cc89e9948ee72a413802))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("yis503iiirext.rom", 0x0000, 0x4000, CRC(34d21778) SHA1(03bf6d2ac86f5c9ab618e155442787c700f99fed))

	ROM_REGION(0x4000, "cpm", 0)
	ROM_LOAD("yis503iiircpm.rom", 0x0000, 0x4000, CRC(417bf00e) SHA1(f4f7a54cdf5a9dd6c59f7cb219c2c5eb0a00fa8a))

	ROM_REGION(0x8000, "network", 0)
	// has 2 * 2KB RAM?
	ROM_LOAD("yis503iiirnet.rom", 0x0000, 0x8000, CRC(75331cac) SHA1(307a7be064442feb4ab2e1a2bc971b138c1a1169))
ROM_END

void msx2_state::y503iiir(machine_config &config)
{
	// YM2149 (in S-3527)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// Networking builtin
	// S-3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 0, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "cpm", 3, 0, 1, 1, "cpm");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000); // 128KB Mapper RAM
	// Yamaha expansion slot in slot #3-3
	add_internal_slot(config, MSX_SLOT_ROM, "network", 3, 3, 1, 2, "network");

	msx2_pal(SND_YM2149, config, layout_msx_ru);
}

/* MSX2 - Yamaha YIS-503 III R Estonian */

ROM_START(y503iiire)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("yis503iiirebios.rom", 0x0000, 0x8000, CRC(d0c20f54) SHA1(ebb7eb540a390509edfd36c84288ba85e63f2d1f))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("yis503iiireext.rom", 0x0000, 0x4000, CRC(34d21778) SHA1(03bf6d2ac86f5c9ab618e155442787c700f99fed))

	ROM_REGION(0x4000, "cpm", 0)
	ROM_LOAD("yis503iiirecpm.rom", 0x0000, 0x4000, CRC(417bf00e) SHA1(f4f7a54cdf5a9dd6c59f7cb219c2c5eb0a00fa8a))

	ROM_REGION(0x8000, "network", 0)
	ROM_LOAD("yis503iiirnet.rom", 0x0000, 0x8000, CRC(75331cac) SHA1(307a7be064442feb4ab2e1a2bc971b138c1a1169))
ROM_END

/* MSX2 - Yamaha YIS604/128 */

ROM_START(yis604)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("yis604bios.rom", 0x0000, 0x8000, CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("yis604ext.rom", 0x0000, 0x4000, CRC(4a48779c) SHA1(b8e30d604d319d511cbfbc61e5d8c38fbb9c5a33))
ROM_END

void msx2_state::yis604(machine_config &config)
{
	// YM2149 (in S-3527)
	// FDC: None, 0 drives
	// 1 Minicart slot (with Beginnner's Lesson)
	// 2 Cartridge slots
	// S-3527 MSX Engine

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "subrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_cartridge_slot<3>(config, MSX_SLOT_YAMAHA_MINICART, "minicart", 3, 1, msx_yamaha_minicart, nullptr);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000); // 128KB Mapper RAM
	add_cartridge_slot<4>(config, MSX_SLOT_YAMAHA_EXPANSION, "module", 3, 3, msx_yamaha_60pin, nullptr);

	msx2(SND_YM2149, config, layout_msx_jp);
	SOFTWARE_LIST(config, "minicart_list").set_original("msx_yamaha_minicart");
}

/* MSX2 - Yamaha YIS-805/128 */

ROM_START(y805128)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("yis805128bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("yis805128ext.rom", 0x0000, 0x4000, BAD_DUMP CRC(43e7a7fc) SHA1(0fbd45ef3dd7bb82d4c31f1947884f411f1ca344)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("yis805128disk.rom", 0x0000, 0x4000, BAD_DUMP CRC(ab94a273) SHA1(4b08a057e5863ade179dcf8bc9377e90940e6d61)) // need verification

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("yis805128kfn.rom", 0x0000, 0x20000, BAD_DUMP CRC(5a59926e) SHA1(6acaf2eeb57f65f7408235d5e07b7563229de799)) // need verification
ROM_END

void msx2_state::y805128(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: wd2793?, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// S1985 MSX Engine
	// 2KB SRAM
	// no mini cartridge slot?

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "subrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_disk_mirrored(config, MSX_SLOT_DISK11_WD2793, "disk", 3, 0, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000); // 128KB Mapper RAM
	// Default: SKW-05
	add_cartridge_slot<3>(config, MSX_SLOT_YAMAHA_EXPANSION, "module", 3, 3, msx_yamaha_60pin, nullptr);

	MSX_S1985(config, "s1985", 0);
	msx2(SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX2 - Yamaha YIS-805/256 */

ROM_START(y805256)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("yis805256bios.rom", 0x0000, 0x8000, BAD_DUMP CRC(9b3e7b97) SHA1(0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd)) // need verification

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("yis805256ext.rom", 0x0000, 0x4000, BAD_DUMP CRC(43e7a7fc) SHA1(0fbd45ef3dd7bb82d4c31f1947884f411f1ca344)) // need verification

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("yis805256disk.rom", 0x0000, 0x4000, BAD_DUMP CRC(ab94a273) SHA1(4b08a057e5863ade179dcf8bc9377e90940e6d61)) // need verification

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("yis805256kfn.rom", 0x0000, 0x20000, BAD_DUMP CRC(5a59926e) SHA1(6acaf2eeb57f65f7408235d5e07b7563229de799)) // need verification
ROM_END

void msx2_state::y805256(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: wd2793?, 2 3.5" DSDD drives
	// 2 Cartridge slots
	// S1985 MSX Engine
	// 2KB SRAM
	// RS232C
	// no mini cartridge slot?

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 0, 1, 0, 1, "subrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_2_DRIVES, "disk", 3, 0, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x40000); // 256KB Mapper RAM
	// Default: SKW-05
	add_cartridge_slot<3>(config, MSX_SLOT_YAMAHA_EXPANSION, "module", 3, 3, msx_yamaha_60pin, nullptr);

	MSX_S1985(config, "s1985", 0);
	msx2(SND_YM2149, config, layout_msx_jp_2fdd);
}

/* MSX2 - Yamaha YIS-805/128R2 (teacher) */

ROM_START(y805128r2)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("yis805128r2bios.rom", 0x0000, 0x8000, CRC(e7d08e29) SHA1(0f851ee7a1cf79819f61cc89e9948ee72a413802))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("yis805128r2ext.rom", 0x0000, 0x4000, CRC(34d21778) SHA1(03bf6d2ac86f5c9ab618e155442787c700f99fed))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("yis805128r2disk.rom", 0x0000, 0x4000, CRC(9eb7e24d) SHA1(3a481c7b7e4f0406a55952bc5b9f8cf9d699376c))

	ROM_REGION(0x8000, "network", 0)
	// has 2 * 2KB RAM ?
	ROM_LOAD("yis805128r2net.rom", 0x0000, 0x8000, CRC(0e345b43) SHA1(e8fd2bbc1bdab12c73a0fec178a190f9063547bb))

	ROM_REGION(0x10000, "firmware", 0)
	ROM_LOAD("yis805128r2paint.rom", 0x00000, 0x10000, CRC(1bda68a3) SHA1(7fd2a28c4fdaeb140f3c8c8fb90271b1472c97b9))
ROM_END

void msx2_state::y805128r2(machine_config &config)
{
	// YM2149 (in S1985)
	// FDC: wd2793, 2 3.5" DSDD drives
	// 2 Cartridge slots
	// S1985 MSX Engine
	// Networking built in

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 3, 0, 0, 4, "firmware");
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "subrom");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK8_WD2793_2_DRIVES, "disk", 3, 1, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 2, 0, 4).set_total_size(0x20000); // 128KB Mapper RAM
	// This is actually the module slot
	add_internal_slot(config, MSX_SLOT_ROM, "network", 3, 3, 0, 2, "network", 0x00000);

	MSX_S1985(config, "s1985", 0);
	msx2_pal(SND_YM2149, config, layout_msx_ru_2fdd);
}

/* MSX2 - Yamaha YIS-805/128R2 Estonian */

ROM_START(y805128r2e)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("yis805128r2ebios.rom", 0x0000, 0x8000, CRC(d0c20f54) SHA1(ebb7eb540a390509edfd36c84288ba85e63f2d1f))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("yis805128r2eext.rom", 0x0000, 0x4000, CRC(34d21778) SHA1(03bf6d2ac86f5c9ab618e155442787c700f99fed))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("yis805128r2edisk.rom", 0x0000, 0x4000, CRC(9eb7e24d) SHA1(3a481c7b7e4f0406a55952bc5b9f8cf9d699376c))

	ROM_REGION(0x8000, "network", 0)
	ROM_LOAD("yis805128r2enet.rom", 0x0000, 0x8000, CRC(0e345b43) SHA1(e8fd2bbc1bdab12c73a0fec178a190f9063547bb))

	ROM_REGION(0x10000, "firmware", 0)
	ROM_LOAD("yis805128r2epaint.rom", 0x00000, 0x10000, CRC(1bda68a3) SHA1(7fd2a28c4fdaeb140f3c8c8fb90271b1472c97b9))
ROM_END

} // anonymous namespace

/* MSX2 */
COMP(1985, canonv25,   0,        0,     canonv25,   msxjp,    msx2_state, empty_init, "Canon", "V-25 (MSX2, Japan)", 0)
COMP(1985, canonv30f,  0,        0,     canonv30f,  msx2,     msx2_state, empty_init, "Canon", "V-30F (MSX2, Japan)", 0)
COMP(1986, cpc300,     0,        0,     cpc300,     msx2kr,   msx2_state, empty_init, "Daewoo", "IQ-2000 CPC-300 (MSX2, Korea)", 0)
COMP(1987, cpc300e,    0,        0,     cpc300e,    msx2kr,   msx2_state, empty_init, "Daewoo", "IQ-2000 CPC-300E (MSX2, Korea)", 0)
COMP(1988, cpc330k,    0,        0,     cpc330k,    msx2kr,   msx2_state, empty_init, "Daewoo", "CPC-330K KOBO (MSX2, Korea)", 0)
COMP(1987, cpc400,     0,        0,     cpc400,     msx2kr,   msx2_state, empty_init, "Daewoo", "X-II CPC-400 (MSX2, Korea)", 0)
COMP(1988, cpc400s,    0,        0,     cpc400s,    msx2kr,   msx2_state, empty_init, "Daewoo", "X-II CPC-400S (MSX2, Korea)", 0)
COMP(1990, cpc61,      0,        0,     cpc61,      msxkr,    msx2_state, empty_init, "Daewoo", "Zemmix CPC-61 (MSX2, Korea)", 0)
COMP(1991, cpg120,     0,        0,     cpg120,     msx2kr,   msx2_state, empty_init, "Daewoo", "Zemmix CPG-120 Normal (MSX2, Korea)", 0)
COMP(1986, fpc900,     0,        0,     fpc900,     msx2,     msx2_state, empty_init, "Fenner", "FPC-900 (MSX2, Italy)", 0)
COMP(1986, expert20,   0,        0,     expert20,   msx2,     msx2_state, empty_init, "Gradiente", "Expert 2.0 (MSX2, Brazil)", 0)
COMP(1985, mbh3,       0,        0,     mbh3,       msx2jp,   msx2_state, empty_init, "Hitachi", "MB-H3 (MSX2, Japan)", 0)
COMP(1986, mbh70,      0,        0,     mbh70,      msx2jp,   msx2_state, empty_init, "Hitachi", "MB-H70 (MSX2, Japan)", MACHINE_NOT_WORKING) // How to enter/use the firmware?
COMP(1987, kmc5000,    0,        0,     kmc5000,    msx2jp,   msx2_state, empty_init, "Kawai", "KMC-5000 (MSX2, Japan)", 0)
COMP(1986, mlg1,       0,        0,     mlg1,       msx2sp,   msx2_state, empty_init, "Mitsubishi", "ML-G1 (MSX2, Spain)", 0)
COMP(1986, mlg3,       0,        0,     mlg3,       msx2sp,   msx2_state, empty_init, "Mitsubishi", "ML-G3 (MSX2, Spain)", 0)
COMP(1985, mlg10,      0,        0,     mlg10,      msx2jp,   msx2_state, empty_init, "Mitsubishi", "ML-G10 (MSX2, Japan)", 0)
COMP(1985, mlg30,      0,        0,     mlg30,      msx2jp,   msx2_state, empty_init, "Mitsubishi", "ML-G30 Model 1 (MSX2, Japan)", 0)
COMP(1985, mlg30_2,    0,        0,     mlg30_2,    msx2jp,   msx2_state, empty_init, "Mitsubishi", "ML-G30 Model 2 (MSX2, Japan)", 0)
COMP(1986, fs4500,     0,        0,     fs4500,     msx2jp,   msx2_state, empty_init, "National", "FS-4500 (MSX2, Japan)", 0)
COMP(1986, fs4600f,    0,        0,     fs4600f,    msx2jp,   msx2_state, empty_init, "National", "FS-4600F (MSX2, Japan)", 0)
COMP(1986, fs4700f,    0,        0,     fs4700f,    msx2jp,   msx2_state, empty_init, "National", "FS-4700F (MSX2, Japan)", 0)
COMP(1986, fs5000f2,   0,        0,     fs5000f2,   msx2jp,   msx2_state, empty_init, "National", "FS-5000F2 (MSX2, Japan)", 0)
COMP(1985, fs5500f1,   fs5500f2, 0,     fs5500f1,   msx2jp,   msx2_state, empty_init, "National", "FS-5500F1 (MSX2, Japan)", 0)
COMP(1985, fs5500f2,   0,        0,     fs5500f2,   msx2jp,   msx2_state, empty_init, "National", "FS-5500F2 (MSX2, Japan)", 0)
COMP(1986, fsa1,       fsa1a,    0,     fsa1,       msxjp,    msx2_state, empty_init, "Panasonic", "FS-A1 / 1st released version (MSX2, Japan)", 0)
COMP(1986, fsa1a,      0,        0,     fsa1a,      msxjp,    msx2_state, empty_init, "Panasonic", "FS-A1 / 2nd released version (MSX2, Japan)", 0)
COMP(1987, fsa1mk2,    0,        0,     fsa1mk2,    msx2jp,   msx2_state, empty_init, "Panasonic", "FS-A1MK2 (MSX2, Japan)", 0)
COMP(1987, fsa1f,      0,        0,     fsa1f,      msx2jp,   msx2_state, empty_init, "Panasonic", "FS-A1F (MSX2, Japan)", 0)
COMP(1988, fsa1fm,     0,        0,     fsa1fm,     msx2jp,   msx2_state, empty_init, "Panasonic", "FS-A1FM (MSX2, Japan)", MACHINE_NOT_WORKING) // Modem not emulated, firmware partially working
COMP(1987, nms8220,    0,        0,     nms8220,    msx,      msx2_state, empty_init, "Philips", "NMS 8220 (MSX2, Europe)", 0)
COMP(1987, nms8245,    0,        0,     nms8245,    msx,      msx2_state, empty_init, "Philips", "NMS 8245 (MSX2, Europe)", 0)
COMP(1987, nms8245f,   nms8245,  0,     nms8245f,   msxfr,    msx2_state, empty_init, "Philips", "NMS 8245F (MSX2, France)", 0)
COMP(1987, nms8250,    nms8255,  0,     nms8250,    msx2,     msx2_state, empty_init, "Philips", "NMS 8250 (MSX2, Europe)", 0)
COMP(1987, nms8250_16, nms8255,  0,     nms8250,    msx2sp,   msx2_state, empty_init, "Philips", "NMS 8250/16 (MSX2, Spain)", 0)
COMP(1987, nms8250_19, nms8255,  0,     nms8250,    msx2fr,   msx2_state, empty_init, "Philips", "NMS 8250/19 (MSX2, France)", 0)
COMP(1987, nms8255,    0,        0,     nms8255,    msx2,     msx2_state, empty_init, "Philips", "NMS 8255 (MSX2, Europe)", 0)
COMP(1987, nms8255f,   nms8255,  0,     nms8255f,   msx2fr,   msx2_state, empty_init, "Philips", "NMS 8255F (MSX2, France)", 0)
COMP(1987, nms8260,    0,        0,     nms8260,    msx2,     msx2_state, empty_init, "Philips", "NMS 8260 (MSX2, Prototype)", MACHINE_NOT_WORKING)
COMP(1987, nms8280,    0,        0,     nms8280,    msx2,     msx2_state, empty_init, "Philips", "NMS 8280 (MSX2, Europe)", 0)
COMP(1986, nms8280f,   nms8280,  0,     nms8280f,   msx2fr,   msx2_state, empty_init, "Philips", "NMS 8280F (MSX2, France)", 0)
COMP(1986, nms8280g,   nms8280,  0,     nms8280g,   msx2de,   msx2_state, empty_init, "Philips", "NMS 8280G (MSX2, Germany)", 0)
COMP(1986, vg8230,     0,        0,     vg8230,     msx,      msx2_state, empty_init, "Philips", "VG-8230 (MSX2, Netherlands)", 0)
COMP(1986, vg8235,     0,        0,     vg8235,     msx,      msx2_state, empty_init, "Philips", "VG-8235 (MSX2, Europe)", 0)
COMP(1986, vg8235f,    vg8235,   0,     vg8235f,    msxfr,    msx2_state, empty_init, "Philips", "VG-8235F (MSX2, France)", 0)
COMP(1986, vg8240,     0,        0,     vg8240,     msx,      msx2_state, empty_init, "Philips", "VG-8240 (MSX2, Prototype)", 0)
COMP(1987, ucv102,     0,        0,     ucv102,     msx2jp,   msx2_state, empty_init, "Pioneer", "UC-V102 (MSX2, Japan)", 0)
COMP(1987, ax350,      ax350ii,  0,     ax350,      msx,      msx2_state, empty_init, "Sakhr", "AX-350 (MSX2, Arabic)", 0)
COMP(1987, ax350ii,    0,        0,     ax350ii,    msx,      msx2_state, empty_init, "Sakhr", "AX-350 II (MSX2, Arabic)", 0)
COMP(1987, ax350iif,   ax350ii,  0,     ax350iif,   msxfr,    msx2_state, empty_init, "Sakhr", "AX-350 II F (MSX2, Arabic)", 0)
COMP(1988, ax370,      0,        0,     ax370,      msx2,     msx2_state, empty_init, "Sakhr", "AX-370 (MSX2, Arabic)", 0)
COMP(1987, ax500,      0,        0,     ax500,      msx2,     msx2_state, empty_init, "Sakhr", "AX-500 (MSX2, Arabic)", 0)
COMP(1987, mpc2300,    0,        0,     mpc2300,    msxru,    msx2_state, empty_init, "Sanyo", "MPC-2300 (MSX2, USSR)", 0)
COMP(1987, mpc2500f,   0,        0,     mpc2500f,   msx2ru,   msx2_state, empty_init, "Sanyo", "MPC-2500FD (MSX2, USSR)", 0)
COMP(1985, mpc25fd,    0,        0,     mpc25fd,    msx2jp,   msx2_state, empty_init, "Sanyo", "MPC-25FD (MSX2, Japan)", 0)
COMP(1985, mpc25fs,    0,        0,     mpc25fs,    msx2jp,   msx2_state, empty_init, "Sanyo", "MPC-25FS (MSX2, Japan)", 0)
COMP(1985, mpc27,      0,        0,     mpc27,      msx2jp,   msx2_state, empty_init, "Sanyo", "MPC-27 (MSX2, Japan)", MACHINE_NOT_WORKING) // Light pen not emulated
COMP(1986, phc23,      0,        0,     phc23,      msx2jp,   msx2_state, empty_init, "Sanyo", "PHC-23 / Wavy23 (MSX2, Japan)", 0)
//COMP(1987, phc23j,     0,        0,     phc23,      msx2jp,   msx2_state, empty_init, "Sanyo", "PHC-23J / Wavy23 (MSX2, Japan)", 0) // different keyboard layout
COMP(1987, phc23jb,    0,        0,     phc23jb,    msx2jp,   msx2_state, empty_init, "Sanyo", "PHC-23JB / Wavy23 (MSX2, Japan)", 0)
COMP(1988, phc55fd2,   0,        0,     phc55fd2,   msx2jp,   msx2_state, empty_init, "Sanyo", "PHC-55FD2 / Wavy55FD2 (MSX2, Japan)", 0)
COMP(1987, phc77,      0,        0,     phc77,      msx2jp,   msx2_state, empty_init, "Sanyo", "PHC-77 / Wavy77 (MSX2, Japan)", 0)
COMP(1986, hotbit20,   0,        0,     hotbit20,   msx2,     msx2_state, empty_init, "Sharp / Epcom", "HB-8000 Hotbit 2.0 (MSX2)", 0)
COMP(1986, hbf1,       hbf1xd,   0,     hbf1,       msxjp,    msx2_state, empty_init, "Sony", "HB-F1 (MSX2, Japan)", 0)
COMP(1987, hbf1ii,     hbf1xd,   0,     hbf1ii,     msxjp,    msx2_state, empty_init, "Sony", "HB-F1II (MSX2, Japan)", 0)
COMP(1987, hbf1xd,     0,        0,     hbf1xd,     msx2jp,   msx2_state, empty_init, "Sony", "HB-F1XD (MSX2, Japan)", 0)
COMP(1985, hbf5,       0,        0,     hbf5,       msx2jp,   msx2_state, empty_init, "Sony", "HB-F5 (MSX2, Japan)", 0)
COMP(1986, hbf9p,      0,        0,     hbf9p,      msx2uk,   msx2_state, empty_init, "Sony", "HB-F9P (MSX2, Europe)", 0)
COMP(19??, hbf9pr,     hbf9p,    0,     hbf9pr,     msx2ru,   msx2_state, empty_init, "Sony", "HB-F9P (MSX2, Russian, prototype)", 0)
COMP(1986, hbf9s,      hbf9p,    0,     hbf9s,      msx2sp,   msx2_state, empty_init, "Sony", "HB-F9S (MSX2, Spain)", 0)
COMP(1986, hbf500,     hbf500p,  0,     hbf500,     msx2jp,   msx2_state, empty_init, "Sony", "HB-F500 (MSX2, Japan)", 0)
COMP(1986, hbf500_2,   hbf500p,  0,     hbf500_2,   msx2jp,   msx2_state, empty_init, "Sony", "HB-F500 2nd version (MSX2, Japan)", 0)
COMP(1986, hbf500f,    hbf500p,  0,     hbf500f,    msx2fr,   msx2_state, empty_init, "Sony", "HB-F500F (MSX2, France)", 0)
COMP(1986, hbf500p,    0,        0,     hbf500p,    msx2,     msx2_state, empty_init, "Sony", "HB-F500P (MSX2, Europe)", 0)
COMP(1986, hbf700d,    hbf700p,  0,     hbf700d,    msx2de,   msx2_state, empty_init, "Sony", "HB-F700D (MSX2, Germany)", 0)
COMP(1986, hbf700f,    hbf700p,  0,     hbf700f,    msx2fr,   msx2_state, empty_init, "Sony", "HB-F700F (MSX2, France)", 0)
COMP(1986, hbf700p,    0,        0,     hbf700p,    msx2uk,   msx2_state, empty_init, "Sony", "HB-F700P (MSX2, Europe)", 0)
COMP(1986, hbf700s,    hbf700p,  0,     hbf700s,    msx2sp,   msx2_state, empty_init, "Sony", "HB-F700S (MSX2, Spain)", 0)
COMP(1986, hbf900,     hbf900a,  0,     hbf900,     msx2jp,   msx2_state, empty_init, "Sony", "HB-F900 (MSX2, Japan)", 0)
COMP(1986, hbf900a,    0,        0,     hbf900a,    msx2jp,   msx2_state, empty_init, "Sony", "HB-F900 (alt) (MSX2, Japan)", 0)
COMP(1987, hbg900ap,   hbg900p,  0,     hbg900ap,   msx2uk,   msx2_state, empty_init, "Sony", "HB-G900AP (MSX2, Europe)", MACHINE_NOT_WORKING) // rs232 not communicating
COMP(1986, hbg900p,    0,        0,     hbg900p,    msx2uk,   msx2_state, empty_init, "Sony", "HB-G900P (MSX2, Europe)", MACHINE_NOT_WORKING) // rs232 not communicating
COMP(1987, tpc310,     0,        0,     tpc310,     msxsp,    msx2_state, empty_init, "Talent", "TPC-310 (MSX2, Argentina)", 0)
COMP(1987, tpp311,     0,        0,     tpp311,     msxsp,    msx2_state, empty_init, "Talent", "TPP-311 (MSX2, Argentina)", 0)
COMP(1987, tps312,     0,        0,     tps312,     msxsp,    msx2_state, empty_init, "Talent", "TPS-312 (MSX2, Argentina)", 0)
COMP(1985, hx23,       hx23f,    0,     hx23,       msxjp,    msx2_state, empty_init, "Toshiba", "HX-23 (MSX2, Japan)", MACHINE_NOT_WORKING) // firmware goes into an infinite loop on the title screen
COMP(1985, hx23f,      0,        0,     hx23f,      msxjp,    msx2_state, empty_init, "Toshiba", "HX-23F (MSX2, Japan)", MACHINE_NOT_WORKING) // firmware goes into an infinite loop on the title screen
COMP(1985, hx33,       hx34,     0,     hx33,       msxjp,    msx2_state, empty_init, "Toshiba", "HX-33 w/HX-R702 (MSX2, Japan)", MACHINE_NOT_WORKING) // half the pixels are missing in the firmware?
COMP(1985, hx34,       0,        0,     hx34,       msx2jp,   msx2_state, empty_init, "Toshiba", "HX-34 w/HX-R703 (MSX2, Japan)", 0)
COMP(1986, fstm1,      0,        0,     fstm1,      msx,      msx2_state, empty_init, "Toshiba", "FS-TM1 (MSX2, Italy)", 0)
COMP(1986, victhc80,   0,        0,     victhc80,   msxjp,    msx2_state, empty_init, "Victor", "HC-80 (MSX2, Japan)", 0)
COMP(1986, victhc90,   victhc95, 0,     victhc90,   msx2jp,   msx2_state, empty_init, "Victor", "HC-90 (MSX2, Japan)", MACHINE_NOT_WORKING) // 2nd cpu/turbo not emulated, firmware won't start
COMP(1986, victhc90a,  victhc95, 0,     victhc90a,  msx2jp,   msx2_state, empty_init, "Victor", "HC-90A (MSX2, Japan)", MACHINE_NOT_WORKING) // 2nd cpu/turbo not emulated
COMP(1986, victhc95,   0,        0,     victhc95,   msx2jp,   msx2_state, empty_init, "Victor", "HC-95 (MSX2, Japan)", MACHINE_NOT_WORKING) // 2nd cpu/turbo not emulated, firmware won't start
COMP(1986, victhc95a,  victhc95, 0,     victhc95a,  msx2jp,   msx2_state, empty_init, "Victor", "HC-95A (MSX2, Japan)", MACHINE_NOT_WORKING) // 2nd cpu/turbo not emulated, firmware won't start
COMP(1985, cx7128,     cx7m128,  0,     cx7128,     msxjp,    msx2_state, empty_init, "Yamaha", "CX7/128 (MSX2, Japan)", 0)
COMP(1985, cx7m128,    0,        0,     cx7m128,    msxjp,    msx2_state, empty_init, "Yamaha", "CX7M/128 (MSX2, Japan)", 0)
COMP(1985, y503iiir,   0,        0,     y503iiir,   msxru,    msx2_state, empty_init, "Yamaha", "YIS-503 III R (MSX2, USSR)", MACHINE_NOT_WORKING) // network not implemented
COMP(198?, y503iiire,  y503iiir, 0,     y503iiir,   msx2,     msx2_state, empty_init, "Yamaha", "YIS-503 III R (MSX2, Estonian)", MACHINE_NOT_WORKING) // network not implemented
COMP(1985, yis604,     0,        0,     yis604,     msx2jp,   msx2_state, empty_init, "Yamaha", "YIS604/128 (MSX2, Japan)", 0)
COMP(1986, y805128,    y805256,  0,     y805128,    msx2jp,   msx2_state, empty_init, "Yamaha", "YIS805/128 (MSX2, Japan)", MACHINE_NOT_WORKING) // Floppy support broken
COMP(1986, y805128r2,  y805256,  0,     y805128r2,  msx2,     msx2_state, empty_init, "Yamaha", "YIS805/128R2 (MSX2, USSR)", MACHINE_NOT_WORKING) // Network not implemented
COMP(198?, y805128r2e, y805256,  0,     y805128r2,  y503iir2, msx2_state, empty_init, "Yamaha", "YIS805/128R2 (MSX2, Estonian)", MACHINE_NOT_WORKING) // Network not implemented
COMP(198?, y805256,    0,        0,     y805256,    msx2jp,   msx2_state, empty_init, "Yamaha", "YIS805/256 (MSX2, Japan)", MACHINE_NOT_WORKING) // Floppy support broken?



msx2p.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol

#include "emu.h"
#include "msx.h"
#include "msx_keyboard.h"
#include "msx_matsushita.h"
#include "msx_s1985.h"
#include "msx_systemflags.h"
#include "bus/msx/slot/disk.h"
#include "bus/msx/slot/music.h"
#include "bus/msx/slot/panasonic08.h"
#include "bus/msx/slot/ram_mm.h"
#include "bus/msx/slot/sony08.h"
#include "softlist_dev.h"

#include "msx_jp.lh"
#include "msx_jp_1fdd.lh"
#include "msx_jp_2fdd.lh"
#include "msx_nocode_1fdd.lh"

using namespace msx_keyboard;


/***************************************************************************

  MSX2+ machines

***************************************************************************/

namespace {

class msx2p_state : public msx2p_base_state
{
public:
	msx2p_state(const machine_config &mconfig, device_type type, const char *tag)
		: msx2p_base_state(mconfig, type, tag, 21.477272_MHz_XTAL, 6)
	{
	}

	// MSX2+ machines
	// Did the expert machines have the f4 boot flags i/o port? (not referenced from system bios)
	void expert3i(machine_config &config);
	void expert3t(machine_config &config);
	void expertac(machine_config &config);
	void expertdx(machine_config &config);
	void fsa1fx(machine_config &config);
	void fsa1wsx(machine_config &config);
	void fsa1wx(machine_config &config);
	void fsa1wxa(machine_config &config);
	void phc70fd(machine_config &config);
	void phc70fd2(machine_config &config);
	void phc35j(machine_config &config);
	void hbf1xdj(machine_config &config);
	void hbf1xv(machine_config &config);
};

/********************************  MSX 2+ **********************************/

/* MSX2+ - Ciel Expert 3 IDE */

ROM_START(expert3i)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("exp30bios.rom", 0x0000,  0x8000, CRC(a10bb1ce) SHA1(5029cf47031b22bd5d1f68ebfd3be6d6da56dfe9))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("exp30ext.rom", 0x0000, 0x4000, CRC(6bcf4100) SHA1(cc1744c6c513d6409a142b4fb42fbe70a95d9b7f))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("cieldisk.rom", 0x0000, 0x4000, CRC(bb550b09) SHA1(0274dd9b5096065a7f4ed019101124c9bd1d56b8))

	ROM_REGION(0x4000, "music", 0)
	ROM_LOAD("exp30mus.rom", 0x0000, 0x4000, CRC(9881b3fd) SHA1(befebc916bfdb5e8057040f0ae82b5517a7750db))

	ROM_REGION(0x10000, "ide", 0)
	ROM_LOAD("ide240a.rom", 0x00000, 0x10000, CRC(7adf857f) SHA1(8a919dbeed92db8c06a611279efaed8552810239))
ROM_END

void msx2p_state::expert3i(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: wd2793, 1 or 2? drives
	// 2 Cartridge slots?

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 1, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_MUSIC, "music", 1, 1, 1, 1, "music").set_ym2413_tag(m_ym2413);
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793, "disk", 1, 2, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_ROM, "ide", 1, 3, 0, 4, "ide");         /* IDE hardware needs to be emulated */
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 2, 0, 4).set_total_size(0x40000);       // 256KB?? Mapper RAM
	add_cartridge_slot<2>(config, 3);

	msx_ym2413(config);

	msx2plus(SND_AY8910, config, layout_msx_nocode_1fdd);
}

/* MSX2+ - Ciel Expert 3 Turbo
This one is a full motherboard by CIEL (not an upgrade kit), created to replace the motherboard of a Gradiente Expert (which means that only the case, the analog boards and the keyboard remains Gradiente). This new motherboard has the following built-in features:

1) MSX2+
2) Support either 3.57MHz or 7.14MHz natively, switched either by software (*1) or by a hardware-switch on the front panel. Turbo-led included.
3) Up to 4MB of Memory Mapper (1MB is the most common configuration)
4) MSX-Music
5) 4 expansion slots (two external on the front panel, two internal)
6) Stereo sound (YM2413 channels 0-6 on right, PSG+YM2413 channels 7-9 on left)
7) Support the V9938 instead of the V9958 by switching some jumpers
8) The main-ram can be placed on slot 2 or slot 3, using jumpers (slot 2 is the default)


*1: A routine hidden inside the BIOS frame-0 is used to switch the turbo.
 */

/* Uses a Z84C0010 - CMOS processor working at 7 MHz */
ROM_START(expert3t)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("exp30bios.rom", 0x0000, 0x8000, CRC(a10bb1ce) SHA1(5029cf47031b22bd5d1f68ebfd3be6d6da56dfe9))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("exp30ext.rom", 0x0000, 0x4000, CRC(6bcf4100) SHA1(cc1744c6c513d6409a142b4fb42fbe70a95d9b7f))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("cieldisk.rom", 0x0000, 0x4000, CRC(bb550b09) SHA1(0274dd9b5096065a7f4ed019101124c9bd1d56b8))

	ROM_REGION(0x4000, "music", 0)
	ROM_LOAD("exp30mus.rom", 0x0000, 0x4000, CRC(9881b3fd) SHA1(befebc916bfdb5e8057040f0ae82b5517a7750db))

	ROM_REGION(0x4000, "turbo", 0)
	ROM_LOAD("turbo.rom", 0x0000, 0x4000, CRC(ab528416) SHA1(d468604269ae7664ac739ea9f922a05e14ffa3d1))
ROM_END

void msx2p_state::expert3t(machine_config &config)
{
	// AY8910
	// FDC: wd2793?, 1 or 2? drives
	// 4 Cartridge/Expansion slots?
	// FM/YM2413 built-in

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1, 0);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 1, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_MUSIC, "music", 1, 1, 1, 1, "music").set_ym2413_tag(m_ym2413);
	add_internal_slot(config, MSX_SLOT_ROM, "turbo", 1, 2, 1, 1, "turbo");          /* Turbo hardware needs to be emulated */
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793, "disk", 1, 3, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 2, 0, 4).set_total_size(0x40000);       // 256KB Mapper RAM
	add_cartridge_slot<2>(config, 3);

	msx_ym2413(config);

	msx2plus(SND_AY8910, config, layout_msx_nocode_1fdd);
}

/* MSX2+ - Gradiente Expert AC88+ */

ROM_START(expertac)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("ac88bios.rom", 0x0000, 0x8000, CRC(9ce0da44) SHA1(1fc2306911ab6e1ebdf7cb8c3c34a7f116414e88))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("ac88ext.rom", 0x0000, 0x4000, CRC(c74c005c) SHA1(d5528825c7eea2cfeadd64db1dbdbe1344478fc6))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("panadisk.rom", 0x0000, 0x4000, CRC(17fa392b) SHA1(7ed7c55e0359737ac5e68d38cb6903f9e5d7c2b6))

	ROM_REGION(0x4000, "asm", 0)
	ROM_LOAD("ac88asm.rom", 0x0000, 0x4000, CRC(a8a955ae) SHA1(91e522473a8470511584df3ee5b325ea5e2b81ef))

	ROM_REGION(0x4000, "xbasic", 0)
	ROM_LOAD("xbasic2.rom", 0x0000, 0x4000, CRC(2825b1a0) SHA1(47370bec7ca1f0615a54eda548b07fbc0c7ef398))
ROM_END

void msx2p_state::expertac(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: wd2793?, 1 or 2? drives
	// 2 Cartridge slots?

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x10000);   // 64KB Mapper RAM??
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "asm", 3, 1, 1, 1, "asm");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793, "disk", 3, 2, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_ROM, "xbasic", 3, 3, 1, 1, "xbasic");

	msx2plus(SND_AY8910, config, layout_msx_nocode_1fdd);
}

/* MSX2+ - Gradiente Expert DDX+ */

ROM_START(expertdx)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("ddxbios.rom", 0x0000, 0x8000, CRC(e00af3dc) SHA1(5c463dd990582e677c8206f61035a7c54d8c67f0))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("ddxext.rom", 0x0000, 0x4000, CRC(b8ba44d3) SHA1(fe0254cbfc11405b79e7c86c7769bd6322b04995))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("panadisk.rom", 0x0000, 0x4000, CRC(17fa392b) SHA1(7ed7c55e0359737ac5e68d38cb6903f9e5d7c2b6))

	ROM_REGION(0x4000, "xbasic", 0)
	ROM_LOAD("xbasic2.rom", 0x0000, 0x4000, CRC(2825b1a0) SHA1(47370bec7ca1f0615a54eda548b07fbc0c7ef398))

	ROM_REGION(0x8000, "kanjirom", 0)
	ROM_LOAD("kanji.rom", 0x0000, 0x8000, CRC(b4fc574d) SHA1(dcc3a67732aa01c4f2ee8d1ad886444a4dbafe06))
ROM_END

void msx2p_state::expertdx(machine_config &config)
{
	// AY8910/YM2149?
	// FDC: tc8566af, 1 3.5" DSDD drive?
	// 2 Cartridge slots?

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1, 0);
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 1, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "xbasic", 1, 2, 1, 1, "xbasic");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK3_TC8566, "disk", 1, 3, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 2, 0, 4).set_total_size(0x10000);   // 64KB Mapper RAM??
	add_cartridge_slot<2>(config, 3);
	/* Kanji? */

	msx2plus(SND_AY8910, config, layout_msx_nocode_1fdd);
}

/* MSX2+ - Panasonic FS-A1FX */

ROM_START(fsa1fx)
	ROM_REGION(0x40000, "maincpu", 0)
	ROM_LOAD("a1fx.ic16", 0, 0x40000, CRC(c0b2d882) SHA1(623cbca109b6410df08ee7062150a6bda4b5d5d4))

	// Kanji rom contents are the first half of the single rom
//  ROM_REGION(0x20000, "kanji", 0)
	ROM_REGION(0x40000, "kanji", 0)
	ROM_LOAD("a1fx.ic16", 0, 0x40000, CRC(c0b2d882) SHA1(623cbca109b6410df08ee7062150a6bda4b5d5d4))
ROM_END

void msx2p_state::fsa1fx(machine_config &config)
{
	// AY8910 (in T9769)
	// FDC: tc8566af, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// T9769(B)
	// ren-sha turbo slider
	// pause button
	// firmware switch

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "maincpu", 0x30000);
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x10000).set_unused_bits(0x80);   // 64KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "maincpu", 0x38000);
	add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 1, 1, 2, "maincpu", 0x28000);
	add_internal_disk_mirrored(config, MSX_SLOT_DISK3_TC8566, "disk", 3, 2, 1, 2, "maincpu", 0x3c000);
	add_internal_slot(config, MSX_SLOT_ROM, "firmware", 3, 3, 1, 2, "maincpu", 0x20000);

	msx_matsushita_device &matsushita(MSX_MATSUSHITA(config, "matsushita", 0));
	matsushita.turbo_callback().set([this] (int state) {
		// 0 - 5.369317 MHz
		// 1 - 3.579545 MHz
		m_maincpu->set_unscaled_clock(m_main_xtal / (state ? 6 : 4));
	});

	MSX_SYSTEMFLAGS(config, "sysflags", m_maincpu, 0xff);
	set_cold_boot_flags(0xff);

	m_kanji_fsa1fx = true;
	msx2plus(SND_AY8910, config, layout_msx_jp_1fdd);
}

/* MSX2+ - Panasonic FS-A1WSX */

ROM_START(fsa1wsx)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("a1wsbios.rom", 0x0000, 0x8000, CRC(358ec547) SHA1(f4433752d3bf876bfefb363c749d4d2e08a218b6))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("a1wsext.rom", 0x0000, 0x4000, CRC(b8ba44d3) SHA1(fe0254cbfc11405b79e7c86c7769bd6322b04995))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("a1wsdisk.rom", 0x0000, 0x4000, CRC(17fa392b) SHA1(7ed7c55e0359737ac5e68d38cb6903f9e5d7c2b6))

	ROM_REGION(0x8000, "kdr", 0)
	ROM_LOAD("a1wskdr.rom", 0x0000, 0x8000, CRC(b4fc574d) SHA1(dcc3a67732aa01c4f2ee8d1ad886444a4dbafe06))

	ROM_REGION(0x4000, "msxmusic", 0)
	ROM_LOAD("a1wsmusp.rom", 0x0000, 0x4000, CRC(5c32eb29) SHA1(aad42ba4289b33d8eed225d42cea930b7fc5c228))

	ROM_REGION(0x200000, "firmware", 0)
	ROM_LOAD("a1wsfirm.rom", 0x000000, 0x200000, CRC(e363595d) SHA1(3330d9b6b76e3c4ccb7cf252496ed15d08b95d3f))

	ROM_REGION(0x40000, "kanji", 0)
	ROM_LOAD("a1wskfn.rom", 0, 0x40000, CRC(1f6406fb) SHA1(5aff2d9b6efc723bc395b0f96f0adfa83cc54a49))
ROM_END

void msx2p_state::fsa1wsx(machine_config &config)
{
	// AY8910 (in T9769)
	// FDC: tc8566af, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// FM built-in
	// T9769(C)
	// ren-sha turbo slider
	// firmware switch
	// pause button

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_MUSIC, "msxmusic", 0, 2, 1, 1, "msxmusic").set_ym2413_tag(m_ym2413);
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x10000).set_unused_bits(0x80);   // 64KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 1, 1, 2, "kdr");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK3_TC8566, "disk", 3, 2, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_PANASONIC08, "firmware", 3, 3, 0, 4, "firmware");

	msx_matsushita_device &matsushita(MSX_MATSUSHITA(config, "matsushita", 0));
	matsushita.turbo_callback().set([this] (int state) {
		// 0 - 5.369317 MHz
		// 1 - 3.579545 MHz
		m_maincpu->set_unscaled_clock(m_main_xtal / (state ? 6 : 4));
	});

	MSX_SYSTEMFLAGS(config, "sysflags", m_maincpu, 0xff);
	set_cold_boot_flags(0xff);

	msx_ym2413(config);

	msx2plus(SND_AY8910, config, layout_msx_jp_1fdd);
}

/* MSX2+ - Panasonic FS-A1WX */

ROM_START(fsa1wx)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("a1wxbios.rom", 0x0000, 0x8000, CRC(19771608) SHA1(e90f80a61d94c617850c415e12ad70ac41e66bb7))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("a1wxext.rom", 0x0000, 0x4000, CRC(b8ba44d3) SHA1(fe0254cbfc11405b79e7c86c7769bd6322b04995))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("a1wxdisk.rom", 0x0000, 0x4000, CRC(905daa1b) SHA1(bb59c849898d46a23fdbd0cc04ab35088e74a18d))

	ROM_REGION(0x8000, "kdr", 0)
	ROM_LOAD("a1wxkdr.rom", 0x0000, 0x8000, CRC(a068cba9) SHA1(1ef3956f7f918873fb9b031339bba45d1e5e5878))

	ROM_REGION(0x4000, "msxmusic", 0)
	ROM_LOAD("a1wxmusp.rom", 0x0000, 0x4000, CRC(456e494e) SHA1(6354ccc5c100b1c558c9395fa8c00784d2e9b0a3))

	ROM_REGION(0x200000, "firmware", 0)
	ROM_LOAD("a1wxfirm.rom", 0x000000, 0x200000, CRC(283f3250) SHA1(d37ab4bd2bfddd8c97476cbe7347ae581a6f2972))

	ROM_REGION(0x40000, "kanji", 0)
	ROM_LOAD("a1wxkfn.rom", 0, 0x40000, CRC(1f6406fb) SHA1(5aff2d9b6efc723bc395b0f96f0adfa83cc54a49))
ROM_END

void msx2p_state::fsa1wx(machine_config &config)
{
	// AY8910 (in T9769)
	// FDC: tc8566af, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// FM built-in
	// MSX Engine T9769A/B
	// ren-sha turbo slider
	// firmware switch
	// pause button

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_MUSIC, "msxmusic", 0, 2, 1, 1, "msxmusic").set_ym2413_tag(m_ym2413);
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x10000).set_unused_bits(0x80);   // 64KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 1, 1, 2, "kdr");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK3_TC8566, "disk", 3, 2, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_PANASONIC08, "firmware", 3, 3, 0, 4, "firmware");

	msx_matsushita_device &matsushita(MSX_MATSUSHITA(config, "matsushita", 0));
	matsushita.turbo_callback().set([this] (int state) {
		// 0 - 5.369317 MHz
		// 1 - 3.579545 MHz
		m_maincpu->set_unscaled_clock(m_main_xtal / (state ? 6 : 4));
	});

	MSX_SYSTEMFLAGS(config, "sysflags", m_maincpu, 0xff);
	set_cold_boot_flags(0xff);

	msx_ym2413(config);

	msx2plus(SND_AY8910, config, layout_msx_jp_1fdd);
}

/* MSX2+ - Panasonic FS-A1WX (a) */

ROM_START(fsa1wxa)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("a1wxbios.rom", 0x0000, 0x8000, CRC(19771608) SHA1(e90f80a61d94c617850c415e12ad70ac41e66bb7))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("a1wxext.rom", 0x0000, 0x4000, CRC(b8ba44d3) SHA1(fe0254cbfc11405b79e7c86c7769bd6322b04995))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("a1wxdisk.rom", 0x0000, 0x4000, CRC(2bda0184) SHA1(2a0d228afde36ac7c5d3c2aac9c7c664dd071a8c))

	ROM_REGION(0x8000, "kdr", 0)
	ROM_LOAD("a1wxkdr.rom", 0x0000, 0x8000, CRC(a068cba9) SHA1(1ef3956f7f918873fb9b031339bba45d1e5e5878))

	ROM_REGION(0x4000, "msxmusic", 0)
	ROM_LOAD("a1wxmusp.rom", 0x0000, 0x4000, CRC(456e494e) SHA1(6354ccc5c100b1c558c9395fa8c00784d2e9b0a3))

	ROM_REGION(0x200000, "firmware", 0)
	ROM_LOAD("a1wxfira.rom", 0x000000, 0x200000, CRC(58440a8e) SHA1(8e0d4a77e7d5736e8225c2df4701509363eb230f))

	ROM_REGION(0x40000, "kanji", 0)
	ROM_LOAD("a1wxkfn.rom", 0, 0x40000, CRC(1f6406fb) SHA1(5aff2d9b6efc723bc395b0f96f0adfa83cc54a49))
ROM_END

/* MSX2+ - Sanyo Wavy PHC-35J */

ROM_START(phc35j)
	ROM_REGION(0x8000, "mainrom", 0)
	ROM_LOAD("35jbios.rom", 0x0000, 0x8000, CRC(358ec547) SHA1(f4433752d3bf876bfefb363c749d4d2e08a218b6))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("35jext.rom", 0x0000, 0x4000, CRC(b8ba44d3) SHA1(fe0254cbfc11405b79e7c86c7769bd6322b04995))

	ROM_REGION(0x8000, "kdr", 0)
	ROM_LOAD("35jkdr.rom", 0x0000, 0x8000, CRC(b4fc574d) SHA1(dcc3a67732aa01c4f2ee8d1ad886444a4dbafe06))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("35jkfn.rom", 0, 0x20000, CRC(c9651b32) SHA1(84a645becec0a25d3ab7a909cde1b242699a8662))
ROM_END

void msx2p_state::phc35j(machine_config &config)
{
	// AY8910 (in T9769)
	// FDC: None, 0 drives
	// 2 Cartridge slots
	// T9769A
	// ren-sha turbo slider
	// pause button

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x10000).set_unused_bits(0x80);   // 64KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 1, 1, 2, "kdr");

	MSX_SYSTEMFLAGS(config, "sysflags", m_maincpu, 0xff);
	set_cold_boot_flags(0xff);
	msx2plus(SND_AY8910, config, layout_msx_jp);
}

/* MSX2+ - Sanyo Wavy PHC-70FD */

ROM_START(phc70fd)
	ROM_REGION(0x20000, "mainrom", 0)
	ROM_LOAD("phc-70fd.rom", 0x0000, 0x20000, CRC(d2307ddf) SHA1(b6f2ca2e8a18d6c7cd326cb8d1a1d7d747f23059))
//  ROM_LOAD("70fdbios.rom", 0x0000, 0x8000, CRC(19771608) SHA1(e90f80a61d94c617850c415e12ad70ac41e66bb7))
//  ROM_LOAD("70fdext.rom",  0x8000, 0x4000, CRC(b8ba44d3) SHA1(fe0254cbfc11405b79e7c86c7769bd6322b04995))
//  ROM_LOAD("70fddisk.rom", 0xc000, 0x4000, CRC(db7f1125) SHA1(9efa744be8355675e7bfdd3976bbbfaf85d62e1d))
//  ROM_LOAD("70fdkdr.rom", 0x10000, 0x8000, CRC(a068cba9) SHA1(1ef3956f7f918873fb9b031339bba45d1e5e5878))
//  ROM_LOAD("70fdmus.rom", 0x18000, 0x4000, CRC(5c32eb29) SHA1(aad42ba4289b33d8eed225d42cea930b7fc5c228))
//  ROM_LOAD("70fdbas.rom", 0x1c000, 0x4000, CRC(da7be246) SHA1(22b3191d865010264001b9d896186a9818478a6b))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("70fdkfn.rom", 0, 0x20000, CRC(c9651b32) SHA1(84a645becec0a25d3ab7a909cde1b242699a8662))
ROM_END

void msx2p_state::phc70fd(machine_config &config)
{
	// AY8910 (in T9769)
	// FDC: tc8566af, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// T9769
	// FM built-in
	// ren-sha turbo slider
	// pause button

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom", 0x10000);
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x10000).set_unused_bits(0x80);   // 64KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "mainrom", 0x18000);
	add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 1, 1, 2, "mainrom", 0x8000);
	add_internal_disk_mirrored(config, MSX_SLOT_DISK3_TC8566, "disk", 3, 2, 1, 2, "mainrom", 0x1c000);
	add_internal_slot(config, MSX_SLOT_MUSIC, "msxmusic", 3, 3, 1, 1, "mainrom", 0x00000).set_ym2413_tag(m_ym2413);
	add_internal_slot(config, MSX_SLOT_ROM, "basickun", 3, 3, 2, 1, "mainrom", 0x04000);

	MSX_SYSTEMFLAGS(config, "sysflags", m_maincpu, 0xff);
	set_cold_boot_flags(0xff);

	msx_ym2413(config);

	msx2plus(SND_AY8910, config, layout_msx_jp_1fdd);
}

/* MSX2+ - Sanyo Wavy PHC-70FD2 */

ROM_START(phc70fd2)
	ROM_REGION(0x20000, "mainrom", 0)
	ROM_LOAD("70f2bios.rom", 0x0000, 0x8000, CRC(19771608) SHA1(e90f80a61d94c617850c415e12ad70ac41e66bb7))

	ROM_REGION(0x4000, "subrom", 0)
	ROM_LOAD("70f2ext.rom", 0x0000, 0x4000, CRC(b8ba44d3) SHA1(fe0254cbfc11405b79e7c86c7769bd6322b04995))

	ROM_REGION(0x4000, "diskrom", 0)
	ROM_LOAD("70f2disk.rom", 0x0000, 0x4000, CRC(db7f1125) SHA1(9efa744be8355675e7bfdd3976bbbfaf85d62e1d))

	ROM_REGION(0x8000, "kdr", 0)
	ROM_LOAD("70f2kdr.rom", 0x0000, 0x8000, CRC(a068cba9) SHA1(1ef3956f7f918873fb9b031339bba45d1e5e5878))

	ROM_REGION(0x4000, "msxmusic", 0)
	ROM_LOAD("70f2mus.rom", 0x0000, 0x4000, CRC(5c32eb29) SHA1(aad42ba4289b33d8eed225d42cea930b7fc5c228))

	ROM_REGION(0x4000, "basickun", 0)
	ROM_LOAD("70f2bas.rom", 0x0000, 0x4000, CRC(da7be246) SHA1(22b3191d865010264001b9d896186a9818478a6b))

	ROM_REGION(0x40000, "kanji", 0)
	ROM_LOAD("70f2kfn.rom", 0, 0x40000, CRC(9a850db9) SHA1(bcdb4dae303dfe5234f372d70a5e0271d3202c36))
ROM_END

void msx2p_state::phc70fd2(machine_config &config)
{
	// AY8910
	// FDC: tc8566af, 2 3.5" DSDD drives
	// 2 Cartridge slots
	// FM built-in
	// T9769

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 2, "mainrom");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x10000).set_unused_bits(0x80);   // 64KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "subrom");
	add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 1, 1, 2, "kdr");
	add_internal_disk_mirrored(config, MSX_SLOT_DISK3_TC8566_2_DRIVES, "disk", 3, 2, 1, 2, "diskrom");
	add_internal_slot(config, MSX_SLOT_MUSIC, "msxmusic", 3, 3, 1, 1, "msxmusic").set_ym2413_tag(m_ym2413);
	add_internal_slot(config, MSX_SLOT_ROM, "basickun", 3, 3, 2, 1, "basickun");

	MSX_SYSTEMFLAGS(config, "sysflags", m_maincpu, 0xff);
	set_cold_boot_flags(0xff);

	msx_ym2413(config);

	msx2plus(SND_AY8910, config, layout_msx_jp_2fdd);
}

/* MSX2+ - Sony HB-F1XDJ */

ROM_START(hbf1xdj)
	ROM_REGION(0x20000, "mainrom", 0)
	ROM_LOAD("hb-f1xdj_main.rom", 0x0000, 0x20000, CRC(d89bab74) SHA1(f2a1d326d72d4c70ea214d7883838de8847a82b7))

	ROM_REGION(0x100000, "firmware", 0)
	ROM_LOAD("f1xjfirm.rom", 0x000000, 0x100000, CRC(77be583f) SHA1(ade0c5ba5574f8114d7079050317099b4519e88f))

	ROM_REGION(0x40000, "kanji", 0)
	ROM_LOAD("f1xjkfn.rom", 0, 0x40000, CRC(7016dfd0) SHA1(218d91eb6df2823c924d3774a9f455492a10aecb))
ROM_END

void msx2p_state::hbf1xdj(machine_config &config)
{
	// YM2149 (in S-1985 MSX Engine)
	// FDC: MB89311, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// FM built-in
	// S-1985 MSX Engine
	// speed controller
	// pause button
	// ren-sha turbo

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_SONY08, "firmware", 0, 3, 0, 4, "firmware");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x10000).set_unused_bits(0x80);   // 64KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "mainrom", 0x8000);
	add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 1, 1, 2, "mainrom", 0x10000);
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_N, "disk", 3, 2, 1, 2, "mainrom", 0xc000);
	add_internal_slot(config, MSX_SLOT_MUSIC, "msxmusic", 3, 3, 1, 1, "mainrom", 0x18000).set_ym2413_tag(m_ym2413);

	MSX_SYSTEMFLAGS(config, "sysflags", m_maincpu, 0x00);

	MSX_S1985(config, "s1985", 0);

	msx_ym2413(config);

	msx2plus(SND_YM2149, config, layout_msx_jp_1fdd);
}

/* MSX2+ - Sony HB-F1XV */

ROM_START(hbf1xv)
	ROM_REGION(0x20000, "mainrom", 0)
	ROM_LOAD("hb-f1xdj_main.rom", 0x0000, 0x20000, CRC(d89bab74) SHA1(f2a1d326d72d4c70ea214d7883838de8847a82b7))

	ROM_REGION(0x100000, "firmware", 0)
	ROM_LOAD("f1xvfirm.rom", 0x0, 0x100000, CRC(77be583f) SHA1(ade0c5ba5574f8114d7079050317099b4519e88f))

	ROM_REGION(0x40000, "kanji", 0)
	ROM_LOAD("f1xvkfn.rom", 0, 0x40000, CRC(7016dfd0) SHA1(218d91eb6df2823c924d3774a9f455492a10aecb))
ROM_END

void msx2p_state::hbf1xv(machine_config &config)
{
	// YM2149 (in S-1985 MSX Engine)
	// FDC: mb89311, 1 3.5" DSDD drives
	// 2 Cartridge slots
	// FM built-in
	// S-1985 MSX Engine
	// speed controller
	// pause button
	// ren-sha turbo

	add_internal_slot(config, MSX_SLOT_ROM, "mainrom", 0, 0, 0, 2, "mainrom");
	add_internal_slot(config, MSX_SLOT_SONY08, "firmware", 0, 3, 0, 4, "firmware");
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x10000).set_unused_bits(0x80);   // 64KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "subrom", 3, 1, 0, 1, "mainrom", 0x8000);
	add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 1, 1, 2, "mainrom", 0x10000);
	add_internal_disk_mirrored(config, MSX_SLOT_DISK1_WD2793_N, "disk", 3, 2, 1, 2, "mainrom", 0xc000);
	add_internal_slot(config, MSX_SLOT_MUSIC, "msxmusic", 3, 3, 1, 1, "mainrom", 0x18000).set_ym2413_tag(m_ym2413);

	MSX_SYSTEMFLAGS(config, "sysflags", m_maincpu, 0x00);

	MSX_S1985(config, "s1985", 0);

	msx_ym2413(config);

	msx2plus(SND_YM2149, config, layout_msx_jp_1fdd);
}

} // anonymous namespace

COMP(19??, expert3i,   0,        0,     expert3i,   msx2,     msx2p_state, empty_init, "Ciel", "Expert 3 IDE (MSX2+, Brazil)", MACHINE_NOT_WORKING) // Some hardware not emulated
COMP(1996, expert3t,   0,        0,     expert3t,   msx2,     msx2p_state, empty_init, "Ciel", "Expert 3 Turbo (MSX2+, Brazil)", MACHINE_NOT_WORKING) // Some hardware not emulated
COMP(19??, expertac,   0,        0,     expertac,   msx2,     msx2p_state, empty_init, "Gradiente", "Expert AC88+ (MSX2+, Brazil)", MACHINE_NOT_WORKING) // Some hardware not emulated
COMP(19??, expertdx,   0,        0,     expertdx,   msx2,     msx2p_state, empty_init, "Gradiente", "Expert DDX+ (MSX2+, Brazil)", MACHINE_NOT_WORKING) // Some hardware not emulated
COMP(1988, fsa1fx,     0,        0,     fsa1fx,     msx2jp,   msx2p_state, empty_init, "Panasonic", "FS-A1FX (MSX2+, Japan)", 0)
COMP(1989, fsa1wsx,    0,        0,     fsa1wsx,    msx2jp,   msx2p_state, empty_init, "Panasonic", "FS-A1WSX (MSX2+, Japan)", 0)
COMP(1988, fsa1wx,     fsa1wxa,  0,     fsa1wx,     msx2jp,   msx2p_state, empty_init, "Panasonic", "FS-A1WX / 1st released version (MSX2+, Japan)", 0)
COMP(1988, fsa1wxa,    0,        0,     fsa1wx,     msx2jp,   msx2p_state, empty_init, "Panasonic", "FS-A1WX / 2nd released version (MSX2+, Japan)", 0)
COMP(1988, phc70fd,    phc70fd2, 0,     phc70fd,    msx2jp,   msx2p_state, empty_init, "Sanyo", "PHC-70FD / Wavy70FD (MSX2+, Japan)", 0)
COMP(1989, phc70fd2,   0,        0,     phc70fd2,   msx2jp,   msx2p_state, empty_init, "Sanyo", "PHC-70FD2 / Wavy70FD2 (MSX2+, Japan)", 0)
COMP(1989, phc35j,     0,        0,     phc35j,     msx2jp,   msx2p_state, empty_init, "Sanyo", "PHC-35J / Wavy35 (MSX2+, Japan)", 0)
COMP(1988, hbf1xdj,    0,        0,     hbf1xdj,    msx2jp,   msx2p_state, empty_init, "Sony", "HB-F1XDJ (MSX2+, Japan)", 0)
COMP(1989, hbf1xv,     0,        0,     hbf1xv,     msx2jp,   msx2p_state, empty_init, "Sony", "HB-F1XV (MSX2+, Japan)", 0)



msxtr.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol

#include "emu.h"
#include "msx.h"
#include "msx_keyboard.h"
#include "msx_s1990.h"
#include "msx_systemflags.h"
#include "bus/midi/midi.h"
#include "bus/msx/slot/disk.h"
#include "bus/msx/slot/music.h"
#include "bus/msx/slot/panasonic08r.h"
#include "bus/msx/slot/ram_mm.h"
#include "bus/msx/slot/rom.h"
#include "machine/i8251.h"
#include "machine/pit8253.h"
#include "softlist_dev.h"

#include "msx_turbor.lh"

using namespace msx_keyboard;


/***************************************************************************

  MSX Turbo-R machine drivers

The R800 and Z80 see exactly the same memory layouts, only one of the two CPUs
is active at any one time. The S1990 controls which CPU is active and can inject
wait states depending on which cpu is active.

TODO:
- sfg extensions may not work due to irq callbacks being registered with the Z80 only.
- v9958 commands seem to execute too fast. Several software items hang because of this.
- verify midi interface operation on fsa1gt.
- Implement DAC/PCM related hardware/filter and hook it up to the S1990.
- Microphone input.

***************************************************************************/

namespace {

class msxtr_state : public msx2p_base_state
{
public:
	msxtr_state(const machine_config &mconfig, device_type type, const char *tag)
		: msx2p_base_state(mconfig, type, tag, 21.477272_MHz_XTAL, 6)
		, m_r800(*this, "r800")
		, m_s1990(*this, "s1990")
		, m_pause_switch(*this, "PAUSE")
		, m_firmware_switch(*this, "FIRMWARE")
		, m_pause_led(*this, "pause_led")
		, m_r800_led(*this, "r800_led")
		, m_pcmdac(*this, "pcmdac")
	{
	}

	void fsa1st(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void turbor(ay8910_type ay8910_type, machine_config &config, const internal_layout &layout);
	void turbor_add_softlists(machine_config &config);
	void s1990_mem_map(address_map &map) ATTR_COLD;
	void s1990_io_map(address_map &map) ATTR_COLD;
	void cpu_mem_map(address_map &map) ATTR_COLD;
	void cpu_io_map(address_map &map) ATTR_COLD;

	virtual void setup_slot_spaces(msx_internal_slot_interface &device) override;
	virtual address_space& get_io_space() override;

	void pause_led_w(int state);
	void r800_led_w(int state);
	void pcm_dac_w(u8 data);
	void pcm_sample_hold_w(int state);
	void pcm_select_w(int state);
	void pcm_filter_w(int state);
	void muting_w(int state);
	int pcm_comp_r();

	required_device<r800_device> m_r800;
	required_device<msx_s1990_device> m_s1990;
	required_ioport m_pause_switch;
	required_ioport m_firmware_switch;
	output_finder<> m_pause_led;
	output_finder<> m_r800_led;
	required_device<dac_8bit_r2r_device> m_pcmdac;
	u8 m_pcm_last_sample;
	u8 m_pcm_held_sample;
	u8 m_pcm_sample_hold;
};

class fsa1gt_state : public msxtr_state
{
public:
	fsa1gt_state(const machine_config &mconfig, device_type type, const char *tag)
		: msxtr_state(mconfig, type, tag)
		, m_i8251(*this, "i8251")
		, m_i8254(*this, "i8254")
		, m_midiin(*this, "midiin_port")
		, m_midiout(*this, "midiout_port")
		, m_dtr(false)
		, m_rts(false)
		, m_rxrdy(false)
		, m_timer2_ff(false)
	{
	}

	void fsa1gt(machine_config &config);

private:
	required_device<i8251_device> m_i8251;
	required_device<pit8254_device> m_i8254;
	required_device<midi_port_device> m_midiin;
	required_device<midi_port_device> m_midiout;
	bool m_dtr;
	bool m_rts;
	bool m_rxrdy;
	bool m_timer2_ff;

	void s1990_io_map(address_map &map) ATTR_COLD;
	void dtr_w(int state);
	void rts_w(int state);
	void rxrdy_w(int state);
	void timer0_w(int state);
	void timer2_w(int state);
	void update_midi_int_state();
	void clear_timer2_ff(u8 data);
};


address_space& msxtr_state::get_io_space()
{
	return m_s1990->space(AS_IO);
}

void msxtr_state::setup_slot_spaces(msx_internal_slot_interface &device)
{
	device.set_memory_space(m_s1990, AS_PROGRAM);
	device.set_io_space(m_s1990, AS_IO);
	// TODO: This is used for signalling irq vectors. But that should go to the currently active cpu not just the z80.
	device.set_maincpu(m_maincpu);
}

void msxtr_state::machine_start()
{
	msx2p_base_state::machine_start();
	m_pause_led.resolve();
	m_r800_led.resolve();

	save_item(NAME(m_pcm_last_sample));
	save_item(NAME(m_pcm_held_sample));
	save_item(NAME(m_pcm_sample_hold));
}

void msxtr_state::machine_reset()
{
	msx2p_base_state::machine_reset();
	m_pause_led = 0;
	m_r800_led = 0;
}

void msxtr_state::s1990_mem_map(address_map &map)
{
	memory_map(map);
}

void msxtr_state::s1990_io_map(address_map &map)
{
	msx2plus_io_map(map);
	map(0xa4, 0xa4).rw(m_s1990, FUNC(msx_s1990_device::pmcnt), FUNC(msx_s1990_device::pmdac));
	map(0xa5, 0xa5).rw(m_s1990, FUNC(msx_s1990_device::pmstat), FUNC(msx_s1990_device::pmcntl));
	map(0xa7, 0xa7).portr(m_pause_switch.finder_tag());
	map(0xa7, 0xa7).w(m_s1990, FUNC(msx_s1990_device::pause_w));
	map(0xe4, 0xe4).w(m_s1990, FUNC(msx_s1990_device::reg_index_write));
	map(0xe5, 0xe5).rw(m_s1990, FUNC(msx_s1990_device::regs_read), FUNC(msx_s1990_device::regs_write));
	map(0xe6, 0xe6).w(m_s1990, FUNC(msx_s1990_device::counter_write));
	map(0xe6, 0xe7).r(m_s1990, FUNC(msx_s1990_device::counter_read));
}

void msxtr_state::cpu_mem_map(address_map &map)
{
	map(0x0000, 0xffff).rw(m_s1990, FUNC(msx_s1990_device::mem_read), FUNC(msx_s1990_device::mem_write));
}

void msxtr_state::cpu_io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0xff).rw(m_s1990, FUNC(msx_s1990_device::io_read), FUNC(msx_s1990_device::io_write));
}

void msxtr_state::pause_led_w(int state)
{
	m_pause_led = state;
}

void msxtr_state::r800_led_w(int state)
{
	m_r800_led = state;
}

void msxtr_state::turbor(ay8910_type ay8910_type, machine_config &config, const internal_layout &layout)
{
	msx2plus_base(ay8910_type, config, layout);

	m_maincpu->set_addrmap(AS_PROGRAM, &msxtr_state::cpu_mem_map);
	m_maincpu->set_addrmap(AS_IO, &msxtr_state::cpu_io_map);

	R800(config, m_r800, 28.636363_MHz_XTAL);
	m_r800->set_addrmap(AS_PROGRAM, &msxtr_state::cpu_mem_map);
	m_r800->set_addrmap(AS_IO, &msxtr_state::cpu_io_map);

	MSX_S1990(config, m_s1990, 28.636363_MHz_XTAL / 4);
	m_s1990->set_addrmap(AS_PROGRAM, &msxtr_state::s1990_mem_map);
	m_s1990->set_addrmap(AS_IO, &msxtr_state::s1990_io_map);
	m_s1990->set_z80_tag(m_maincpu);
	m_s1990->set_r800_tag(m_r800);
	m_s1990->pause_led_callback().set(FUNC(msxtr_state::pause_led_w));
	m_s1990->r800_led_callback().set(FUNC(msxtr_state::r800_led_w));
	m_s1990->firmware_switch_callback().set_ioport(m_firmware_switch);
	m_s1990->dac_write_callback().set(FUNC(msxtr_state::pcm_dac_w));
	m_s1990->sample_hold_callback().set(FUNC(msxtr_state::pcm_sample_hold_w));
	m_s1990->select_callback().set(FUNC(msxtr_state::pcm_select_w));
	m_s1990->filter_callback().set(FUNC(msxtr_state::pcm_filter_w));
	m_s1990->muting_callback().set(FUNC(msxtr_state::muting_w));
	m_s1990->comp_callback().set(FUNC(msxtr_state::pcm_comp_r));

	// TODO: Do IRQ requests go to both cpus (only to be ignored by the inactive cpu) or only to the currently active cpu?
	m_mainirq->output_handler().append_inputline(m_r800, INPUT_LINE_IRQ0);

	DAC_8BIT_R2R(config, m_pcmdac).add_route(ALL_OUTPUTS, m_speaker, 1.0); // Unknown DAC type

	// Software lists
	turbor_add_softlists(config);
}

void msxtr_state::pcm_dac_w(u8 data)
{
	m_pcmdac->write(data);
	m_pcm_last_sample = data;
}

void msxtr_state::pcm_sample_hold_w(int state)
{
	if (!m_pcm_sample_hold && state)
		m_pcm_held_sample = m_pcm_last_sample;
	m_pcm_sample_hold = state;
}

void msxtr_state::pcm_select_w(int state)
{
	// TODO pcm select
}

void msxtr_state::pcm_filter_w(int state)
{
	// TODO enable pcm filter
}

int msxtr_state::pcm_comp_r()
{
	// TODO pcm output compare
	return 0;
}

void msxtr_state::muting_w(int state)
{
	// TODO mute all sound
}

void msxtr_state::turbor_add_softlists(machine_config &config)
{
	if (m_hw_def.has_cartslot())
	{
		SOFTWARE_LIST(config, "cart_list").set_original("msxr_cart");
		SOFTWARE_LIST(config, "msx2p_cart_list").set_compatible("msx2p_cart");
		SOFTWARE_LIST(config, "msx2_cart_list").set_compatible("msx2_cart");
		SOFTWARE_LIST(config, "msx1_cart_list").set_compatible("msx1_cart");
	}

	if (m_hw_def.has_fdc())
	{
		SOFTWARE_LIST(config, "flop_list").set_original("msxr_flop");
		SOFTWARE_LIST(config, "msx2p_flop_list").set_compatible("msx2p_flop");
		SOFTWARE_LIST(config, "msx2_flop_list").set_compatible("msx2_flop");
		SOFTWARE_LIST(config, "msx1_flop_list").set_compatible("msx1_flop");
	}
}

/* MSX Turbo-R - Panasonic FS-A1GT */

ROM_START(fsa1gt)
	ROM_REGION(0x400000, "firmware", 0)
	// This should be 2 1MB roms at locations IC20 and IC23.
	ROM_LOAD("a1gtfirm.rom", 0, 0x400000, CRC(feefeadc) SHA1(e779c338eb91a7dea3ff75f3fde76b8af22c4a3a) BAD_DUMP)

	ROM_REGION(0x40000, "kanji", 0)
	ROM_LOAD("a1gtkfn.rom", 0, 0x40000, CRC(1f6406fb) SHA1(5aff2d9b6efc723bc395b0f96f0adfa83cc54a49))
ROM_END

void fsa1gt_state::s1990_io_map(address_map &map)
{
	msxtr_state::s1990_io_map(map);
	map(0xe8, 0xe9).rw(m_i8251, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xea, 0xea).w(FUNC(fsa1gt_state::clear_timer2_ff));
	map(0xec, 0xef).rw(m_i8254, FUNC(pit8254_device::read), FUNC(pit8254_device::write));
}

void fsa1gt_state::fsa1gt(machine_config &config)
{
	// AY8910 (in T9769)
	// FDC: tc8566af, 1 3.5" DSDD drive
	// 2 Cartridge slots
	// T9769C + S1990
	// FM built-in
	// Microphone
	// MIDI-in
	// MIDI-out
	// firmware switch
	// pause button
	// ren-sha turbo slider

	add_internal_slot(config, MSX_SLOT_ROM, "bios", 0, 0, 0, 2, "firmware", 0x050000);
	add_internal_slot(config, MSX_SLOT_MUSIC, "msxmusic", 0, 2, 1, 1, "firmware", 0x07c000).set_ym2413_tag(m_ym2413);
	add_internal_slot(config, MSX_SLOT_ROM, "opt", 0, 3, 1, 1, "firmware", 0x048000);
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x80000);   // 512KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "ext", 3, 1, 0, 1, "firmware", 0x070000);
	add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 1, 1, 2, "firmware", 0x074000);
	add_internal_disk(config, MSX_SLOT_DISK4_TC8566, "dos", 3, 2, 1, 3, "firmware", 0x060000);
	add_internal_slot(config, MSX_SLOT_PANASONIC08R, "firmware", 3, 3, 0, 4, "firmware").set_sram_size(0x8000).set_mm_tag("ram_mm");

	MSX_SYSTEMFLAGS(config, "sysflags", m_maincpu, 0x00);

	msx_ym2413(config);

	m_hw_def.has_cassette(false);
	turbor(SND_AY8910, config, layout_msx_turbor);
	m_s1990->set_addrmap(AS_IO, &fsa1gt_state::s1990_io_map);

	I8251(config, m_i8251, 16_MHz_XTAL / 4); // Not sure about this
	m_i8251->txd_handler().set(m_midiout, FUNC(midi_port_device::write_txd));
	m_i8251->dtr_handler().set(*this, FUNC(fsa1gt_state::dtr_w));
	m_i8251->rts_handler().set(*this, FUNC(fsa1gt_state::rts_w));
	m_i8251->rxrdy_handler().set(*this, FUNC(fsa1gt_state::rxrdy_w));

	PIT8254(config, m_i8254);
	m_i8254->set_clk<0>(16_MHz_XTAL / 4);
	m_i8254->set_clk<2>(16_MHz_XTAL/ 4);
	m_i8254->out_handler<0>().set(*this, FUNC(fsa1gt_state::timer0_w));
	m_i8254->out_handler<2>().set(*this, FUNC(fsa1gt_state::timer2_w));

	MIDI_PORT(config, m_midiin, midiin_slot, "midiin").rxd_handler().set(m_i8251, FUNC(i8251_device::rx_w));
	MIDI_PORT(config, m_midiout, midiout_slot, "midiout");
}

void fsa1gt_state::rts_w(int state)
{
	m_rts = state;
	update_midi_int_state();
}

void fsa1gt_state::rxrdy_w(int state)
{
	m_rxrdy = state;
	update_midi_int_state();
}

void fsa1gt_state::dtr_w(int state)
{
	m_dtr = state;
	update_midi_int_state();
}

void fsa1gt_state::timer0_w(int state)
{
	m_i8251->tx_clock_w(state);
	m_i8251->rx_clock_w(state);
}

void fsa1gt_state::timer2_w(int state)
{
	m_timer2_ff = m_timer2_ff | state;
	m_i8254->write_clk1(state);
	update_midi_int_state();
}

void fsa1gt_state::clear_timer2_ff(u8 data)
{
	m_timer2_ff = false;
	update_midi_int_state();
}

void fsa1gt_state::update_midi_int_state()
{
	m_i8251->write_dsr(m_timer2_ff && m_dtr);
	m_mainirq->in_w<3>(!((m_timer2_ff && m_dtr) || (m_rts && m_rxrdy)));
}

/* MSX Turbo-R - Panasonic FS-A1ST */

ROM_START(fsa1st)
	ROM_REGION(0x400000, "firmware", 0)
	// This should be either 3 512KB roms or 1 1MB and 1 512KB rom (at IC12 and IC18?). Both variants are known to exist.
	ROM_LOAD("a1stfirm.rom", 0, 0x400000, CRC(139ac99c) SHA1(c212b11fda13f83dafed688c54d098e7e47ab225) BAD_DUMP)

	ROM_REGION(0x40000, "kanji", 0)
	ROM_LOAD("a1stkfn.rom", 0, 0x40000, CRC(1f6406fb) SHA1(5aff2d9b6efc723bc395b0f96f0adfa83cc54a49))
ROM_END

void msxtr_state::fsa1st(machine_config &config)
{
	// AY8910 (in T9769)
	// FDC: tc8566af, 1 3.5" DSDD drive
	// T9769C + S1990
	// 2 Cartridge slots
	// FM built-in
	// microphone
	// firmware switch
	// pause button
	// ren-sha turbo slider

	add_internal_slot(config, MSX_SLOT_ROM, "bios", 0, 0, 0, 2, "firmware", 0x050000);
	add_internal_slot(config, MSX_SLOT_MUSIC, "msxmusic", 0, 2, 1, 1, "firmware", 0x07c000).set_ym2413_tag(m_ym2413);
	add_internal_slot(config, MSX_SLOT_ROM, "opt", 0, 3, 1, 1, "firmware", 0x048000);
	add_cartridge_slot<1>(config, 1);
	add_cartridge_slot<2>(config, 2);
	add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x40000);   // 256KB Mapper RAM
	add_internal_slot(config, MSX_SLOT_ROM, "ext", 3, 1, 0, 1, "firmware", 0x070000);
	add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 1, 1, 2, "firmware", 0x074000);
	add_internal_disk(config, MSX_SLOT_DISK4_TC8566, "dos", 3, 2, 1, 3, "firmware", 0x060000);
	add_internal_slot(config, MSX_SLOT_PANASONIC08R, "firmware", 3, 3, 0, 4, "firmware").set_sram_size(0x4000).set_mm_tag("ram_mm");

	MSX_SYSTEMFLAGS(config, "sysflags", m_maincpu, 0x00);

	msx_ym2413(config);

	m_hw_def.has_cassette(false);
	turbor(SND_AY8910, config, layout_msx_turbor);
}

INPUT_PORTS_START(msxtr)
	PORT_INCLUDE(msx2jp)

	PORT_START("PAUSE")
	PORT_CONFNAME(0x01, 0x00, "Pause") PORT_CHANGED_MEMBER("s1990", FUNC(msx_s1990_device::pause_callback), 0)
	PORT_CONFSETTING(0x00, "off")
	PORT_CONFSETTING(0x01, "on")

	PORT_START("FIRMWARE")
	PORT_CONFNAME(0x01, 0x01, "Firmware")
	PORT_CONFSETTING(0x01, "enabled")
	PORT_CONFSETTING(0x00, "disabled")
INPUT_PORTS_END

} // anonymous namespace

/* MSX Turbo-R */
COMP(1991, fsa1gt, 0, 0, fsa1gt, msxtr, fsa1gt_state, empty_init, "Panasonic", "FS-A1GT (MSX Turbo-R, Japan)", MACHINE_NOT_WORKING)
COMP(1991, fsa1st, 0, 0, fsa1st, msxtr, msxtr_state,  empty_init, "Panasonic", "FS-A1ST (MSX Turbo-R, Japan)", MACHINE_NOT_WORKING)



mt420.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

Skeleton driver for Micro-Term Model 420 terminal.

************************************************************************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/eepromser.h"
#include "machine/input_merger.h"
#include "machine/mc68681.h"
#include "machine/scn_pci.h"
#include "video/scn2674.h"
#include "screen.h"

namespace {

class mt420_state : public driver_device
{
public:
	mt420_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mainnmi(*this, "mainnmi")
		, m_screen(*this, "screen")
		, m_chargen(*this, "chargen")
		, m_attrram(*this, "attrram")
		, m_cgram(*this, "cgram")
		, m_vram_view(*this, "vram")
		, m_avdc_intr(false)
	{
	}

	void mt420(machine_config &config);

private:
	SCN2674_DRAW_CHARACTER_MEMBER(draw_character);
	void avdc_intr_w(int state);

	void control_w(u8 data);
	u8 attr_r(offs_t offset);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void chars_map(address_map &map) ATTR_COLD;
	void attrs_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<input_merger_device> m_mainnmi;
	required_device<screen_device> m_screen;
	required_region_ptr<u8> m_chargen;
	required_shared_ptr<u8> m_attrram;
	required_shared_ptr<u8> m_cgram;
	memory_view m_vram_view;

	bool m_avdc_intr;
};

void mt420_state::avdc_intr_w(int state)
{
	m_avdc_intr = state;
}

SCN2674_DRAW_CHARACTER_MEMBER(mt420_state::draw_character)
{
	u16 dots = BIT(charcode, 7) ? m_cgram[u8(charcode & 0x7f) << 4 | linecount] : m_chargen[linecount << 7 | charcode];
	dots |= dots << 1;

	for (int i = 0; i < 12; i++)
	{
		bitmap.pix(y, x++) = BIT(dots, 8) ? rgb_t::white() : rgb_t::black();
		if (i < 8)
			dots <<= 1;
	}
}

void mt420_state::control_w(u8 data)
{
	m_vram_view.select(BIT(data, 1));
	m_mainnmi->in_w<1>(!BIT(data, 4));
}

u8 mt420_state::attr_r(offs_t offset)
{
	// TBD: Bit 7 is special, but what is its source?
	return (m_attrram[offset] & 0x7f) | (m_screen->vblank() || m_screen->hblank() || m_avdc_intr ? 0x80 : 0);
}

void mt420_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("maincpu", 0);
	map(0x8000, 0x87ff).ram().share("cgram");
	map(0x9000, 0x9000).w(FUNC(mt420_state::control_w));
	map(0xc000, 0xcfff).writeonly().share("attrram").r(FUNC(mt420_state::attr_r));
	map(0xe000, 0xefff).view(m_vram_view);
	m_vram_view[0](0xeff8, 0xefff).rw("avdc", FUNC(scn2674_device::read), FUNC(scn2674_device::write));
	m_vram_view[1](0xe000, 0xefff).ram().share("charram");
	map(0xf000, 0xf7ff).ram();
}

void mt420_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0xe0, 0xef).rw("duart", FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0xf0, 0xf3).rw("aci", FUNC(scn2641_device::read), FUNC(scn2641_device::write));
}

void mt420_state::chars_map(address_map &map)
{
	map(0x0000, 0x0fff).ram().share("charram");
	map(0x2000, 0x27ff).ram().share("cgram");
}

void mt420_state::attrs_map(address_map &map)
{
	map(0x0000, 0x0fff).ram().share("attrram");
}

static INPUT_PORTS_START(mt420)
INPUT_PORTS_END

void mt420_state::mt420(machine_config &config)
{
	Z80(config, m_maincpu, 4'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &mt420_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &mt420_state::io_map);

	INPUT_MERGER_ANY_HIGH(config, "mainint").output_handler().set_inputline(m_maincpu, 0);
	INPUT_MERGER_ALL_HIGH(config, m_mainnmi).output_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);

	scn2681_device &duart(SCN2681(config, "duart", 3.6864_MHz_XTAL)); // MC2681
	duart.irq_cb().set("mainint", FUNC(input_merger_device::in_w<0>));
	duart.outport_cb().set("eeprom", FUNC(eeprom_serial_93cxx_device::di_write)).bit(5);
	duart.outport_cb().append("eeprom", FUNC(eeprom_serial_93cxx_device::cs_write)).bit(4);
	duart.outport_cb().append("eeprom", FUNC(eeprom_serial_93cxx_device::clk_write)).bit(3);

	scn2641_device &aci(SCN2641(config, "aci", 3.6864_MHz_XTAL));
	aci.intr_handler().set("mainint", FUNC(input_merger_device::in_w<1>));

	EEPROM_93C46_16BIT(config, "eeprom").do_callback().set("duart", FUNC(scn2681_device::ip6_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(9.87768_MHz_XTAL * 2, 612 * 2, 0, 480 * 2, 269, 0, 250);
	//screen.set_raw(15.30072_MHz_XTAL * 2, 948 * 2, 0, 792 * 2, 269, 0, 250);
	screen.set_screen_update("avdc", FUNC(scn2674_device::screen_update));

	scn2674_device &avdc(SCN2674(config, "avdc", 9.87768_MHz_XTAL / 6));
	//avdc.set_clock(15.30072_MHz_XTAL / 6);
	avdc.set_character_width(12);
	avdc.intr_callback().set(m_mainnmi, FUNC(input_merger_device::in_w<0>));
	avdc.intr_callback().append(FUNC(mt420_state::avdc_intr_w));
	avdc.set_display_callback(FUNC(mt420_state::draw_character));
	avdc.set_addrmap(0, &mt420_state::chars_map);
	avdc.set_addrmap(1, &mt420_state::attrs_map);
	avdc.set_screen("screen");
}


/**************************************************************************************************************

Micro-Term Model 420.
Chips: Z80, MC2681P, SCN2674, 2x CDM6264E3, TMM2016BP-12, SCN2641, NMC9345N. Undumped PAL10L8NC at U18 and PROM (N82S129N) at U41.
Crystals: 3.6864, 15.30072 (hard to read), 9.87768

***************************************************************************************************************/

ROM_START(mt420)
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD( "1910_m.p._r1.9.u8",   0x0000, 0x8000, CRC(e79154e9) SHA1(7c3f22097b931986c921bf731de98a1d0536aec9) )

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "mt420cg_rev2.1.u44",  0x0000, 0x0fe0, CRC(7950e485) SHA1(1f03525958464bbe861d2e78f07cc5264e17c0e8) ) // incomplete?
ROM_END

} // anonymous namespace

COMP(1986, mt420, 0, 0, mt420, mt420, mt420_state, empty_init, "Micro-Term", "Micro-Term 420", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



mt440.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/****************************************************************************

    Micro-Term 440

    VT240 compatible serial terminal

    Hardware for the terminal PCB:
    - Z8400APS Z80A CPU
    - 27256 EPROM "1965 M.P. R4.0 27256 U6"
    - 2764 EPROM "1965 M.P. R4.0 2764 U7"
    - D4364C-20L (work RAM?)
    - 2732 EPROM "1965 M.P. R4.0 2732 U9"
    - 2x NMC 9345N EEPROM
    - Z8430AB1 Z80ACTC
    - 2x Z8470AB1 Z80ADART
    - SCN2674B AVDC
    - 4x TMM2016BP-10 (VRAM?)
    - 1x M58725P
    - 2732 EPROM "MT425 CG REV 2.1"
    - 2716 EPROM "MT425 ATTR U25 REV1.1"
    - 4 MHz XTAL, 9.87768 MHz XTAL, 15.30072 MHz XTAL

    Hardware for the graphics PCB:
    - D8088-2 CPU
    - IM6402-1IJL
    - 27256 EPROM "1955 8088 R4.0 27256 U 58"
    - CDM6264E3
    - 2732 EPROM "1959 BLNKG R1.0 2732 U56"
    - 2732 EPROM "1959 CNTRL R1.0 2732 U75"
    - 24 MHz XTAL

    External:
    - RCA connector "Video Out"
    - DB9 connector "Printer"
    - DB25 connector "I/O"
    - RJ? connector "Keyboard"

    TODO:
    - Everything

    Notes:
    - The graphics PCB appears to be serially connected to the terminal PCB

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"

#include "emupal.h"


namespace {


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class mt440_state : public driver_device
{
public:
	mt440_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu")
	{ }

	void mt440(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void mt440_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).rom();
	map(0x8000, 0x87ff).ram();
}

void mt440_state::io_map(address_map &map)
{
}


//**************************************************************************
//  INPUT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( mt440 )
INPUT_PORTS_END


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

static const gfx_layout char_layout =
{
	8, 10,
	128,
	1,
	{ 0 },
	{ STEP8(0, 1) },
	{ 0*0x80*8, 1*0x80*8, 2*0x80*8, 3*0x80*8, 4*0x80*8, 5*0x80*8, 6*0x80*8, 7*0x80*8, 8*0x80*8, 9*0x80*8 },
	8
};

static GFXDECODE_START(chars)
	GFXDECODE_ENTRY("cg", 0x000, char_layout, 0, 1)
	GFXDECODE_ENTRY("cg", 0x800, char_layout, 0, 1)
GFXDECODE_END


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void mt440_state::machine_start()
{
}

void mt440_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void mt440_state::mt440(machine_config &config)
{
	Z80(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mt440_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &mt440_state::io_map);

	PALETTE(config, "palette", palette_device::MONOCHROME_HIGHLIGHT);

	GFXDECODE(config, "gfxdecode", "palette", chars);
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( mt440 )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD("1965_mp_r40.u6", 0x0000, 0x8000, CRC(cecda080) SHA1(0b40709a5f729c7d39a216fccee4b1ac7a59ad60))
	ROM_LOAD("1965_mp_r40.u7", 0x8000, 0x2000, CRC(798cceef) SHA1(6de7b536d756666c058662e067bb718629bccb39))
	ROM_LOAD("1965_mp_r40.u9", 0xa000, 0x1000, CRC(e39d145c) SHA1(79c4a8dd62398b6f2f6e7e8004ec42b2511016de))

	ROM_REGION(0x1000, "cg", 0)
	// bad? gfxdecode shows missing pixels
	ROM_LOAD("mt425_cg_rev21.u37", 0x0000, 0x1000, CRC(3ba34cf4) SHA1(dddddf69d736f04a17aae2019f47257484e88377))

	ROM_REGION(0x800, "attr", 0)
	ROM_LOAD("mt425_attr_rev11.u25", 0x000, 0x800, CRC(128a461a) SHA1(716cf85680b7cec8732a934c6f9eebd68c47c8d6))

	ROM_REGION(0x8000, "gfxcpu", 0)
	ROM_LOAD("1955_8088_r40.u58", 0x0000, 0x8000, CRC(836445cf) SHA1(540fcb2daeb9aebf474c213025641d3adb480697))

	ROM_REGION(0x2000, "gfxextra", 0)
	ROM_LOAD("1959_blnkg_r10.u56", 0x0000, 0x1000, CRC(a8f04ba4) SHA1(c37cc33eb426201d53e1c3b4e237a1dfd94f0f40))
	ROM_LOAD("1959_cntrl_r10.u75", 0x1000, 0x1000, CRC(098c8a37) SHA1(4dd3612d576809acfae1e62996dbc6ae4ece1ada))

	ROM_REGION(0x20, "gfxpal", 0)
	ROM_LOAD("1959_13.u3", 0x00, 0x20, CRC(ebeb6d51) SHA1(faa06769938d6832baeb904775162f8436eeba59))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY       FULLNAME          FLAGS
COMP( 1986, mt440, 0,      0,      mt440,   mt440, mt440_state, empty_init, "Micro-Term", "Micro-Term 440", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



mt5510.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

Skeleton driver for Microterm Model 5510 terminal.

************************************************************************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/eepromser.h"
#include "machine/mc68681.h"
#include "screen.h"

namespace {

class mt5510_state : public driver_device
{
public:
	mt5510_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rombank(*this, "rombank")
	{
	}

	void mt5510(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void bank_w(u8 data);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_memory_bank m_rombank;
};

void mt5510_state::machine_start()
{
	m_rombank->configure_entries(0, 4, memregion("maincpu")->base() + 0x8000, 0x2000);
}

void mt5510_state::machine_reset()
{
	m_rombank->set_entry(0);
}

u32 mt5510_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void mt5510_state::bank_w(u8 data)
{
	m_rombank->set_entry(BIT(data, 2, 2));
}

void mt5510_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("maincpu", 0).nopw();
	map(0x8000, 0x9fff).bankr("rombank");
	map(0xa000, 0xbfff).ram();
	map(0xc000, 0xdfff).ram().share("charram");
	map(0xe000, 0xffff).ram().share("attrram");
}

void mt5510_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x60, 0x6f).rw("duart", FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0x71, 0x71).w(FUNC(mt5510_state::bank_w));
}

static INPUT_PORTS_START(mt5510)
INPUT_PORTS_END

void mt5510_state::mt5510(machine_config &config)
{
	Z80(config, m_maincpu, 6_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mt5510_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &mt5510_state::io_map);

	scn2681_device &duart(SCN2681(config, "duart", 3.6864_MHz_XTAL));
	duart.irq_cb().set_inputline(m_maincpu, 0);
	duart.outport_cb().set("eeprom1", FUNC(eeprom_serial_93cxx_device::di_write)).bit(6);
	duart.outport_cb().append("eeprom2", FUNC(eeprom_serial_93cxx_device::di_write)).bit(5);
	duart.outport_cb().append("eeprom1", FUNC(eeprom_serial_93cxx_device::cs_write)).bit(4);
	duart.outport_cb().append("eeprom2", FUNC(eeprom_serial_93cxx_device::cs_write)).bit(4);
	duart.outport_cb().append("eeprom1", FUNC(eeprom_serial_93cxx_device::clk_write)).bit(3);
	duart.outport_cb().append("eeprom2", FUNC(eeprom_serial_93cxx_device::clk_write)).bit(3);

	EEPROM_93C46_16BIT(config, "eeprom1").do_callback().set("duart", FUNC(scn2681_device::ip6_w));

	EEPROM_93C46_16BIT(config, "eeprom2").do_callback().set("duart", FUNC(scn2681_device::ip5_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(45.8304_MHz_XTAL / 2, 1120, 0, 960, 341, 0, 300); // wild guess at resolution
	screen.set_screen_update(FUNC(mt5510_state::screen_update));
}


/**************************************************************************************************************

Micro-Term 5510.
Chips: Z80, SCN2681, S8842C4/SCX6244UNT, 4x CXK5864BP-70L, 2x NMC9346N
Crystals: 6.000, 3.68640, 45.8304

***************************************************************************************************************/

ROM_START( mt5510 )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD( "2500_m.p._r1.9.u11", 0x00000, 0x10000, CRC(71f19a53) SHA1(91df26d46a93359cd033d7137f1676bcfa58223b) )
ROM_END

} // anonymous namespace

COMP(1988, mt5510, 0, 0, mt5510, mt5510, mt5510_state, empty_init, "Microterm", "Microterm 5510", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



mt735.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Cowering, Olivier Galibert
/*
  Mannesmann Tally MT735 portable thermal printer
  Also released by Siemens as HighPrint 730/735 Compact?

  Designed by Siemens Office Products Division

  Features:
    - 6ppm
    - 1MB of RAM (178KB used for downloaded soft fonts, 50KB input buffer)
    - 18 resident Portrait and Landscape fonts
    - print resolution of 300x300 dpi
    - four standard emulations (HP LaserJet Series II, HP DeskJet Plus, IBM Proprinter X24, Epson LQ850)
    - one Centronics parallel port
    - 5 buttons (Power, On-line, Print Quality, Form-feed, Copy)
    - 5 LEDs (Battery Check, Charger Active, Ribbon/Paper, Power, On-Line)

  2 dipswitches (strings from ROM)
   - = switched off
   o = switched on

  - - - - PC-8
  - - - o HP Roman-8
  - - o - PC-8 Den/Nor
  - - o o ISO 04 UK
  - o - - ISO 21 GER
  - o - o ISO 69 FRA
  - o o - ISO 15 ITA
  - o o o ISO 60 NOR
  o - - - ISO 11 SWN
  o - - o ISO 17 SPA
  o - o - ISO 06 ASCII US
  o - o o ISO 16 PORT
  o o - - ISO 14 JAP
  o o - o ISO 02 IRV / Denmark 1
  o o o - ECMA-94
  o o o o Legal / PC-8/DOWN LOAD
          - -     HP LaserJet Series II
          - o     HP DeskJet Plus
          o -     IBM Proprinter 4207 001
          o o     EPSON LQ850
              - - DIN A4
              - o US legal
              o - US letter
              o o undefined

  o CR=CR+LF
  - CR=CR
    o Select intern fixed ON
    - Select intern fixed OFF
      o Landscape
      - Portrait
        o no function
        - no function
          o AGM ON
          - AGM OFF
            o LF=LF+CR,FF=FF+CR
            - LF=LF,FF=FF
              o no function
              - no function
                o LF=LF+CR,FF=FF+CR
                - LF=LF,FF=FF+CR

*/


#include "emu.h"
#include "cpu/m68000/m68000.h"


namespace {

class mt735_state : public driver_device
{
public:
	mt735_state(const machine_config &mconfig, device_type type, const char *tag);

	void mt735(machine_config &config);

private:
	required_device<m68000_device> m_cpu;

	uint8_t p4_r();
	uint8_t p5_r();

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	void mt735_map(address_map &map) ATTR_COLD;
};

mt735_state::mt735_state(const machine_config &mconfig, device_type type, const char *tag) :
	driver_device(mconfig, type, tag),
	m_cpu(*this, "maincpu")
{
}

void mt735_state::machine_start()
{
}

void mt735_state::machine_reset()
{
}

uint8_t mt735_state::p4_r()
{
	logerror("p4_r (%06x)\n", m_cpu->pc());
	return 0xe0;
}

uint8_t mt735_state::p5_r()
{
	logerror("p5_r (%06x)\n", m_cpu->pc());
	return 0x00;
}

void mt735_state::mt735_map(address_map &map)
{
	map(0x000000, 0x03ffff).rom().region("maincpu", 0);
	map(0x278000, 0x287fff).ram();
	map(0x400000, 0x4fffff).ram();
	map(0xff8004, 0xff8004).r(FUNC(mt735_state::p4_r));
	map(0xff8005, 0xff8005).r(FUNC(mt735_state::p5_r));
}

static INPUT_PORTS_START( mt735 )
INPUT_PORTS_END

void mt735_state::mt735(machine_config &config)
{
	M68000(config, m_cpu, XTAL(48'000'000)/6);
	m_cpu->set_addrmap(AS_PROGRAM, &mt735_state::mt735_map);
}

ROM_START( mt735 )
	ROM_REGION( 0x40000, "maincpu", 0 )
	ROM_LOAD16_BYTE( "spg_m_e_ic103.bin", 0, 0x20000, CRC(1ab58bc9) SHA1(c10d50f38819c037d28435b77e09f2b6923e8369) )
	ROM_LOAD16_BYTE( "spg_m_o_ic102.bin", 1, 0x20000, CRC(84d8446b) SHA1(b1cedd8b09556eb8118f79b012aeec5b61e3ff32) )
ROM_END

} // anonymous namespace


COMP( 1990, mt735, 0, 0, mt735, mt735, mt735_state, empty_init, "Mannesmann Tally", "MT735", MACHINE_NOT_WORKING|MACHINE_NO_SOUND ) // Program ROM Datecode: 19901126, Internal Font ROM Datecode: 19900807



mtd1256.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/**********************************************************************************

    Skeleton driver for data acquisition station by Geonica S.A.

**********************************************************************************/

#include "emu.h"
//#include "bus/rs232/rs232.h"
#include "cpu/mc68hc11/mc68hc11.h"
#include "machine/bankdev.h"
//#include "machine/icl7109.h"
#include "machine/mm58274c.h"
#include "machine/mm74c922.h"
//#include "machine/nvram.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"


namespace {

class mtd1256_state : public driver_device
{
public:
	mtd1256_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_bank0(*this, "bank0")
		, m_encoder(*this, "encoder")
		, m_bankreg(0)
		, m_portd(0xff)
	{
	}

	void mtd1256(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	HD44780_PIXEL_UPDATE(pixel_update);

	u8 porta_r();
	void portd_w(u8 data);

	void bank0_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<mc68hc11_cpu_device> m_maincpu;
	required_device<address_map_bank_device> m_bank0;
	required_device<mm74c923_device> m_encoder;

	u8 m_bankreg;
	u8 m_portd;
};

void mtd1256_state::machine_start()
{
	save_item(NAME(m_bankreg));
	save_item(NAME(m_portd));
}

HD44780_PIXEL_UPDATE(mtd1256_state::pixel_update)
{
	// Is this the right layout?
	if (pos < 20)
		bitmap.pix(line * 16 + y, pos * 6 + x) = state;
	else if (pos < 40)
		bitmap.pix(line * 16 + 8 + y, (pos - 20) * 6 + x) = state;
}


u8 mtd1256_state::porta_r()
{
	// FIXME: bit 2 should be ICL7109 busy output
	return m_encoder->da_r() | (machine().rand() & 0x04);
}

void mtd1256_state::portd_w(u8 data)
{
	if (!BIT(m_portd, 4) && BIT(data, 4))
		m_bankreg = (m_bankreg << 1) | BIT(data, 5);

	m_bank0->set_bank(BIT(data, 5) ? m_bankreg : 0);

	m_portd = data;
}

void mtd1256_state::bank0_map(address_map &map)
{
	map(0x000000, 0x007fff).ram();
	map(0x040000, 0x047fff).ram();
	map(0x080000, 0x087fff).ram();
	map(0x0c0000, 0x0c000f).rw("rtc", FUNC(mm58274c_device::read), FUNC(mm58274c_device::write));
	map(0x0c0020, 0x0c0020).nopw();
	map(0x100000, 0x100000).r(m_encoder, FUNC(mm74c922_device::read));
	map(0x100020, 0x100021).rw("lcdc", FUNC(hd44780_device::read), FUNC(hd44780_device::write));
	map(0x180000, 0x187fff).ram();
	map(0x1c0000, 0x1dffff).ram();
}

void mtd1256_state::mem_map(address_map &map)
{
	map(0x0000, 0x1fff).m(m_bank0, FUNC(address_map_bank_device::amap8));
	map(0x2000, 0xffff).rom().region("program", 0x2000); // partly overlaid by internal spaces
}


static INPUT_PORTS_START(mtd1256)
INPUT_PORTS_END

void mtd1256_state::mtd1256(machine_config &config)
{
	MC68HC11A1(config, m_maincpu, 1.8432_MHz_XTAL); // yes, this appears to be the CPU XTAL
	m_maincpu->set_addrmap(AS_PROGRAM, &mtd1256_state::mem_map);
	m_maincpu->in_pa_callback().set(FUNC(mtd1256_state::porta_r));
	m_maincpu->out_pd_callback().set(FUNC(mtd1256_state::portd_w));

	ADDRESS_MAP_BANK(config, m_bank0);
	m_bank0->set_addrmap(0, &mtd1256_state::bank0_map);
	m_bank0->set_data_width(8);
	m_bank0->set_endianness(ENDIANNESS_BIG);
	m_bank0->set_addr_width(21);
	m_bank0->set_stride(0x2000);

	// TODO: NVRAM is 4x NEC µPD431000AGW-70L 128Kx8 SRAMs + 3V lithium battery

	MM58274C(config, "rtc", 32.768_kHz_XTAL); // TODO: 1 second interrupt output configured

	MM74C923(config, m_encoder, 0); // timing parameters unknown

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(20*6, 32);
	screen.set_visarea(0, 20*6-1, 0, 32-1);
	screen.set_palette("palette");

	hd44780_device &hd44780(HD44780(config, "lcdc", 270'000)); // TODO: clock not measured, datasheet typical clock used
	hd44780.set_lcd_size(2, 16);
	hd44780.set_pixel_update_cb(FUNC(mtd1256_state::pixel_update));

	PALETTE(config, "palette").set_entries(2);

	// TODO: add Maxim ICL7109CQH 12-bit A/D converter
	// TODO: add RS232 port (through ADM202JRW)
}


ROM_START(mtd1256)
	ROM_REGION(0x10000, "program", 0)
	ROM_LOAD("cieenres.b_26-11-92.u7", 0x00000, 0x10000, CRC(a507effd) SHA1(46b3399c0c26c6952a5582c79c14663515e3e180))
ROM_END

} // anonymous namespace


SYST(1992, mtd1256, 0, 0, mtd1256, mtd1256, mtd1256_state, empty_init, "Geonica", "Meteodata 1256", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



mtu130.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert

// Micro Technology Unlimited MTU-130

// Silently boots to floppy, so nothing will happen if you don't have
// a bootable disk in the drive.

// The backplane has 5 slots, one if which is used by the main cpu
// card, the Monomeg, and one by the FDC card.

// Unimplemented:
// - MTUTAPE, a kind of digital tape?
// - MTUNET, some proprietary network
// - Sound on user via cb2, it's weird

// Implemented extension boards:
// - DATAMOVER, a 68k-based board, used by BASIC 1.5 to accelerate floating point operations

// Unimplemented extension boards:
// - PROGRAMMOVER, a z80-based board
// - MultI-O, an i/o board

#include "emu.h"
#include "cpu/m6502/m6502.h"
#include "imagedev/floppy.h"
#include "imagedev/cartrom.h"
#include "machine/6522via.h"
#include "machine/input_merger.h"
#include "machine/mos6551.h"
#include "machine/upd765.h"
#include "bus/rs232/rs232.h"
#include "bus/mtu130/board.h"
#include "bus/mtu130/datamover.h"
#include "sound/dac.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


class mtu130_rom_device : public device_t, public device_rom_image_interface {
public:
	mtu130_rom_device(const machine_config &mconfig, char const *tag, device_t *owner, uint32_t clock);
	template <typename T> mtu130_rom_device(const machine_config &mconfig, char const *tag, device_t *owner, T &&romdata_tag, offs_t load_offset) :
		mtu130_rom_device(mconfig, tag, owner, 0)
	{
		m_load_offset = load_offset;
		m_romdata.set_tag(std::forward<T>(romdata_tag));
	}

	virtual bool is_reset_on_load() const noexcept override { return false; }
	virtual const char *file_extensions() const noexcept override { return "rom,bin"; }

protected:
	virtual void device_start() override ATTR_COLD;
	virtual std::pair<std::error_condition, std::string> call_load() override;
	virtual void call_unload() override;

private:
	required_shared_ptr<u8> m_romdata;
	offs_t m_load_offset;
};

class mtu130_state : public driver_device
{
public:
	mtu130_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_user_6522(*this, "user_6522"),
		m_sys1_6522(*this, "sys1_6522"),
		m_sys2_6522(*this, "sys2_6522"),
		m_acia(*this, "acia"),
		m_irq_merger(*this, "irq_merger"),
		m_rs232(*this, "rs232"),
		m_fdc(*this, "fdc"),
		m_floppy(*this, "fdc:%d", 0U),
		m_ext(*this, "ext%d", 0U),
		m_roms(*this, "rom%d", 0U),
		m_screen(*this, "screen"),
		m_palette(*this, "palette"),
		m_dac(*this, "dac"),
		m_speaker(*this, "mono"),
		m_io_view(*this, "io_view"),
		m_se_view(*this, "sw_view"),
		m_rom_view(*this, "rom_view"),
		m_rof_view(*this, "rof_view"),
		m_mainram(*this, "mainram"),
		m_fdcram(*this, "fdcram"),
		m_videoram(*this, "videoram"),
		m_romdata(*this, "romdata"),
		m_ipl(*this, "ipl"),
		m_sequencer(*this, "sequencer"),
		m_id(*this, "id"),
		m_keyboard(*this, "K%X", 0L),
		m_keyboard_meta(*this, "KM"),
		m_jumpers(*this, "jumpers"),
		m_lightpen_w(*this, "lightpen_w"),
		m_lightpen_x(*this, "lightpen_x"),
		m_lightpen_y(*this, "lightpen_y")
	{ }

	void mtu130(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(nmi_w);
	DECLARE_INPUT_CHANGED_MEMBER(reset_w);
	DECLARE_INPUT_CHANGED_MEMBER(break_w);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

private:
	class memory_interface : public m6502_device::memory_interface {
	public:
		u8 m_sadr, m_write_count, m_banks;

		memory_access<18, 0, 0, ENDIANNESS_LITTLE>::specific m_program;

		const u8 *m_sequencer;

		memory_interface(const u8 *sequencer, address_space &space);
		virtual ~memory_interface() = default;
		virtual uint8_t read(uint16_t adr) override;
		virtual uint8_t read_sync(uint16_t adr) override;
		virtual uint8_t read_arg(uint16_t adr) override;
		virtual void write(uint16_t adr, uint8_t val) override;

		std::pair<u32, u8> normal_step(u16 adr);
		u32 banked_address(u16 adr, u8 sadr) const;

		void set_banks(u8 pbank, u8 dbank) { m_banks = (dbank << 2) | pbank; }
	};

	required_device<m6502_device> m_maincpu;
	required_device<via6522_device> m_user_6522;
	required_device<via6522_device> m_sys1_6522;
	required_device<via6522_device> m_sys2_6522;
	required_device<mos6551_device> m_acia;
	required_device<input_merger_device> m_irq_merger;
	required_device<rs232_port_device> m_rs232;
	required_device<upd765a_device> m_fdc;
	required_device_array<floppy_connector, 4> m_floppy;
	required_device_array<mtu130_extension_device, 3> m_ext;
	required_device_array<mtu130_rom_device, 4> m_roms;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<dac_byte_interface> m_dac;
	required_device<speaker_device> m_speaker;

	memory_view m_io_view;
	memory_view m_se_view;
	memory_view m_rom_view;
	memory_view m_rof_view;
	required_shared_ptr<u8> m_mainram;
	required_shared_ptr<u8> m_fdcram;
	required_shared_ptr<u8> m_videoram;
	required_shared_ptr<u8> m_romdata;

	required_memory_region m_ipl;
	required_region_ptr<u8> m_sequencer;
	required_region_ptr<u8> m_id;
	required_ioport_array<16> m_keyboard;
	required_ioport m_keyboard_meta;
	required_ioport m_jumpers;
	required_ioport m_lightpen_w;
	required_ioport m_lightpen_x;
	required_ioport m_lightpen_y;

	memory_interface *m_maincpu_intf;

	emu_timer *m_timer_lightpen_hit;

	int m_lightpen_hit_x, m_lightpen_hit_y;

	uint16_t m_dma_adr;
	u8 m_keyboard_col;
	u8 m_dac_level;
	u8 m_id_adr;

	u8 m_lightpen_status, m_lightpen_low, m_lightpen_high;

	bool m_dma_direction;
	bool m_video_unblank;
	bool m_video_bw;
	bool m_fdc_irq_enabled;

	static void floppies(device_slot_interface &device);
	void map(address_map &map) ATTR_COLD;
	void extension_board(machine_config &config, int slot_id, const char *tag, const char *def);
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	u8 fdc_ctrl_r();
	void fdc_ctrl_w(u8 data);
	void fdc_irq_w(int state);
	void dma_adr_w(u8 data);
	void dma_drq_w(int state);

	TIMER_CALLBACK_MEMBER(lightpen_hit);
	void lightpen_trigger(int state);
	u8 lightpen_status_r();
	u8 lightpen_low_r();
	u8 lightpen_high_r();
	void lightpen_clear_w(u8);

	void tape_w(u8);

	void keyboard_col_clear_w(u8);
	void user_cb2_w(int line);
	void sys1_pb_w(u8 data);
	u8 sys1_pa_r();
	void sys1_ca2_w(int line);
	void sys2_pa_w(u8 data);

	void id_reset_w(u8);
	u8 id_r();

	void io_enable_w(u8);
	void io_disable_w(u8);
};

DEFINE_DEVICE_TYPE(MTU130_ROM, mtu130_rom_device, "mtu130_rom", "MTU130 rom slot")

	mtu130_rom_device::mtu130_rom_device(const machine_config &mconfig, char const *tag, device_t *owner, uint32_t clock) :
	device_t(mconfig, MTU130_ROM, tag, owner, clock),
	device_rom_image_interface(mconfig, *this),
	m_romdata(*this, finder_base::DUMMY_TAG),
	m_load_offset(0)
{
}

void mtu130_rom_device::device_start()
{
	if(!exists())
		memset(m_romdata + m_load_offset, 0xff, 4096);
}

std::pair<std::error_condition, std::string> mtu130_rom_device::call_load()
{
	u32 len = !loaded_through_softlist() ? length() : get_software_region_length("rom");
	if(!len || len > 4096 || (4096 % len))
		return std::make_pair(image_error::INVALIDLENGTH, std::string());

	if (!loaded_through_softlist())
		fread(m_romdata + m_load_offset, len);
	else
		memcpy(m_romdata + m_load_offset, get_software_region("rom"), len);

	if(len < 4096) {
		offs_t delta = len;
		while(delta < 4096) {
			memcpy(m_romdata + m_load_offset + delta, m_romdata + m_load_offset, len);
			delta += len;
		}
	}

	return std::make_pair(std::error_condition(), std::string());
}

void mtu130_rom_device::call_unload()
{
	memset(m_romdata + m_load_offset, 0xff, 4096);
}

mtu130_state::memory_interface::memory_interface(const u8 *sequencer, address_space &space) : m_sadr(0), m_write_count(0), m_banks(0), m_sequencer(sequencer)
{
	space.specific(m_program);
}

u32 mtu130_state::memory_interface::banked_address(u16 adr, u8 sadr) const
{
	u32 cur_bank_slot = m_sequencer[sadr] & 3;
	u32 cur_bank = (m_banks >> (cur_bank_slot << 1)) & 3;
	return adr | (cur_bank << 16);
}

u8 mtu130_state::memory_interface::read_sync(u16 adr)
{
	m_write_count = 0;

	u8 sadr = m_sadr & 0xf0; // Cycle counter to zero
	if(adr >= 0x200)
		sadr |= 0x08;

	u8 data = m_program.read_byte(banked_address(adr, sadr));

	sadr &= 0x8f;

	// All of that happens after the read
	if((data & 0x0f) != 0x0c && (data & 0x0f) != 0x0d && (data & 0x0f) != 0x0e && (data & 0x17) != 0x01)
		sadr |= 0x10;

	if((data & 0x0f) != 0x01)
		sadr |= 0x20;

	if(adr >= 0x200)
		sadr |= 0x40;

	// rti recognition
	if(data == 0x40)
		sadr &= ~0x80;

	m_sadr = sadr;

	return data;
}

std::pair<u32, u8> mtu130_state::memory_interface::normal_step(u16 adr)
{
	u8 sadr = m_sadr;
	if(adr >= 0x200)
		sadr |= 0x08;
	else
		sadr &= ~0x08;

	u32 badr = banked_address(adr, sadr);

	sadr = ((sadr + 1) & 7) | (sadr & 0xf8);
	return std::make_pair(badr, sadr);
}

u8 mtu130_state::memory_interface::read(u16 adr)
{
	auto [badr, sadr] = normal_step(adr);
	u8 data = m_program.read_byte(badr);
	m_write_count = 0;
	m_sadr = sadr;
	return data;
}

u8 mtu130_state::memory_interface::read_arg(u16 adr)
{
	return read(adr);
}

void mtu130_state::memory_interface::write(u16 adr, u8 val)
{
	auto [badr, sadr] = normal_step(adr);
	m_program.write_byte(badr, val);
	m_write_count ++;
	if(m_write_count == 3)
		sadr |= 0x80;
	m_sadr = sadr;
}

void mtu130_state::machine_start()
{
	auto intf = std::make_unique<memory_interface>(m_sequencer, m_maincpu->space(AS_PROGRAM));
	m_maincpu_intf = intf.get();

	save_item(NAME(intf->m_sadr));
	save_item(NAME(intf->m_write_count));

	m_maincpu->set_custom_memory_interface(std::move(intf));

	m_dma_adr = 0;
	m_dma_direction = false;
	m_keyboard_col = 0;
	m_dac_level = 0x80;
	m_id_adr = 0;
	m_video_unblank = true;
	m_video_bw = true;
	m_fdc_irq_enabled = false;
	m_lightpen_hit_x = 0;
	m_lightpen_hit_y = 0;
	m_lightpen_status = 0;
	m_lightpen_low = 0;
	m_lightpen_high = 0;

	save_item(NAME(m_dma_adr));
	save_item(NAME(m_dma_direction));
	save_item(NAME(m_keyboard_col));
	save_item(NAME(m_dac_level));
	save_item(NAME(m_id_adr));
	save_item(NAME(m_video_unblank));
	save_item(NAME(m_video_bw));
	save_item(NAME(m_fdc_irq_enabled));
	save_item(NAME(m_lightpen_hit_x));
	save_item(NAME(m_lightpen_hit_y));
	save_item(NAME(m_lightpen_status));
	save_item(NAME(m_lightpen_low));
	save_item(NAME(m_lightpen_high));

	m_timer_lightpen_hit = timer_alloc(FUNC(mtu130_state::lightpen_hit), this);

	m_fdc->set_rate(500000);
	m_io_view.select(1);
	m_rom_view.disable();
	m_rof_view.disable();

	for(auto e : m_ext)
		e->map_io(m_io_view[1]);
}

void mtu130_state::machine_reset()
{
	m_se_view.select(m_jumpers->read() & 0x01);
}

void mtu130_state::video_start()
{
	m_palette->set_pen_color(0,   0,   0,   0);
	m_palette->set_pen_color(1,  85,  85,  85);
	m_palette->set_pen_color(2, 170, 170, 170);
	m_palette->set_pen_color(3, 255, 255, 255);
}

uint32_t mtu130_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	if(m_video_unblank && m_video_bw) {
		int x0 = cliprect.left() - 120;
		int x1 = cliprect.right() - 120;
		for(int y = cliprect.top(); y <= cliprect.bottom(); y++) {
			const u8 *src = m_videoram + 60 * y + (x0 >> 3);
			u16 *dest = &bitmap.pix(y, cliprect.left());
			u8 v = 0;
			if(x0 & 7)
				v = *src++;
			for(int x = x0; x <= x1; x++) {
				if(!(x & 7))
					v = *src++;
				*dest++ = ((v >> (7 - (x & 7))) & 1) ? 3 : 0;
			}
		}

	} else if(m_video_unblank) {
		int x0 = cliprect.left() - 120;
		int x1 = cliprect.right() - 120;
		for(int y = cliprect.top(); y <= cliprect.bottom(); y++) {
			const u8 *src = m_videoram + 60 * y + (x0 >> 4);
			u16 *dest = &bitmap.pix(y, cliprect.left());
			u8 v = 0;
			if(x0 & 7)
				v = *src++;
			for(int x = x0; x <= x1; x++) {
				if(!(x & 7))
					v = *src++;
				*dest++ = (v >> (6 - 2*((x >> 1) & 3))) & 3;
			}
		}

	} else
		bitmap.fill(0, cliprect);

	return 0;
}

u8 mtu130_state::fdc_ctrl_r()
{
	return m_fdc->get_irq() ? 0x00 : 0x80;
}

void mtu130_state::fdc_ctrl_w(u8 data)
{
	m_dma_direction = data & 1;
	if(data & 2)
		m_rof_view.select(1);
	else
		m_rof_view.disable();
	m_fdc_irq_enabled = data & 4;
	m_irq_merger->in_w<0>(m_fdc_irq_enabled && m_fdc->get_irq());
}

void mtu130_state::fdc_irq_w(int state)
{
	m_irq_merger->in_w<0>(m_fdc_irq_enabled && state);
}

void mtu130_state::dma_adr_w(u8 data)
{
	m_dma_adr = data << 6;
}

void mtu130_state::dma_drq_w(int state)
{
	while(m_fdc->get_drq()) {
		if(m_dma_direction) {
		// Read from floppy
			u8 data = m_fdc->dma_r();
			m_fdcram[m_dma_adr & 0x3fff] = data;
			m_dma_adr ++;

		} else {
			// Write to floppy
			u8 data = m_fdcram[m_dma_adr & 0x3fff];
			m_fdc->dma_w(data);
			m_dma_adr ++;
		}
	}
}

void mtu130_state::id_reset_w(u8)
{
	m_id_adr = 0;
}

u8 mtu130_state::id_r()
{
	m_id_adr = (m_id_adr + 1) & 0xf;
	return m_id[m_id_adr];
}

void mtu130_state::user_cb2_w(int line)
{
	logerror("%s user cb2 %d\n", machine().time().to_string(), line);
}

void mtu130_state::sys1_pb_w(u8 data)
{
	m_maincpu_intf->set_banks((~data >> 2) & 3, (~data) & 3);

	// The jumper inverts the via-driven selection between external
	// and fdc rom, changing the reset default.
	if(data & 0x40)
		m_se_view.select(m_jumpers->read() & 0x01);
	else
		m_se_view.select((m_jumpers->read() & 0x01) ^ 1);

	if(data & 0x80)
		m_rom_view.disable();
	else
		m_rom_view.select(1);

	bool video_bw = data & 0x10;
	bool video_unblank = data & 0x20;

	if(video_bw != m_video_bw || video_unblank != m_video_unblank) {
		m_screen->update_now();

		m_video_bw = video_bw;
		m_video_unblank = video_unblank;
	}
}

u8 mtu130_state::sys1_pa_r()
{
	// A capacitor is used to pretend the MOD key is pressed at power-on time.
	return m_keyboard[m_keyboard_col & 0xf]->read() |
		((m_keyboard_meta->read() & 0x04) || (machine().time().as_double() < 0.1) ? 1 : 0);
}

void mtu130_state::sys2_pa_w(u8 data)
{
	m_dac_level = data;
	m_dac->write(data);
}

void mtu130_state::sys1_ca2_w(int state)
{
	if(state)
		m_keyboard_col ++;
}

void mtu130_state::io_enable_w(u8)
{
	m_io_view.select(1);
}

void mtu130_state::io_disable_w(u8)
{
	m_io_view.disable();
}

INPUT_CHANGED_MEMBER(mtu130_state::nmi_w)
{
	m_maincpu->set_input_line(m6502_device::NMI_LINE, newval ? ASSERT_LINE : CLEAR_LINE);
}

INPUT_CHANGED_MEMBER(mtu130_state::reset_w)
{
	if(newval == 0)
		reset();
}

INPUT_CHANGED_MEMBER(mtu130_state::break_w)
{
	m_sys1_6522->write_ca1(newval);
}

void mtu130_state::keyboard_col_clear_w(u8)
{
	m_keyboard_col = 0;
}

u8 mtu130_state::lightpen_status_r()
{
	return m_lightpen_status;
}

u8 mtu130_state::lightpen_low_r()
{
	return m_lightpen_low;
}

u8 mtu130_state::lightpen_high_r()
{
	return m_lightpen_high;
}

void mtu130_state::lightpen_clear_w(u8)
{
	m_lightpen_status &= ~8;
}

void mtu130_state::lightpen_trigger(int state)
{
	if(state && m_lightpen_w->read()) {
		m_lightpen_hit_x = m_lightpen_x->read();
		m_lightpen_hit_y = m_lightpen_y->read();
		m_timer_lightpen_hit->adjust(m_screen->time_until_pos(m_lightpen_hit_y, m_lightpen_hit_x + 120));
	}
}

TIMER_CALLBACK_MEMBER(mtu130_state::lightpen_hit)
{
	if(m_lightpen_status & 8)
		return;

	// aaaa aaaa .ppp ...s  (a = address, p = pixel, s = skew)
	// The os-provided coordinates decoding routine cannot generate x >= 474
	static const uint16_t x_to_address[480] = {
		0x0261, 0x0271, 0x0300, 0x0310, 0x0320, 0x0330, 0x0340, 0x0350, 0x0360, 0x0370,
		0x0301, 0x0311, 0x0421, 0x0431, 0x0440, 0x0450, 0x0460, 0x0470, 0x0400, 0x0410,
		0x0420, 0x0430, 0x0540, 0x0550, 0x0560, 0x0570, 0x0501, 0x0511, 0x0521, 0x0531,
		0x0541, 0x0551, 0x0660, 0x0670, 0x0700, 0x0710, 0x0720, 0x0730, 0x0740, 0x0750,
		0x0760, 0x0770, 0x0801, 0x0811, 0x0821, 0x0831, 0x0840, 0x0850, 0x0860, 0x0870,
		0x0800, 0x0810, 0x0820, 0x0830, 0x0940, 0x0950, 0x0960, 0x0970, 0x0901, 0x0911,
		0x0921, 0x0931, 0x0a41, 0x0a51, 0x0a61, 0x0a71, 0x0a00, 0x0a10, 0x0a20, 0x0a30,
		0x0a40, 0x0a50, 0x0a60, 0x0a70, 0x0c00, 0x0c10, 0x0c20, 0x0c30, 0x0c40, 0x0c50,
		0x0c61, 0x0c71, 0x0d00, 0x0d10, 0x0d20, 0x0d30, 0x0d40, 0x0d50, 0x0d60, 0x0d70,
		0x0d01, 0x0d11, 0x0e21, 0x0e31, 0x0e40, 0x0e50, 0x0e60, 0x0e70, 0x0e00, 0x0e10,
		0x0e20, 0x0e30, 0x0f40, 0x0f50, 0x0f60, 0x0f70, 0x0f01, 0x0f11, 0x0f21, 0x0f31,
		0x0f41, 0x0f51, 0x1060, 0x1070, 0x1100, 0x1110, 0x1120, 0x1130, 0x1140, 0x1150,
		0x1160, 0x1170, 0x1201, 0x1211, 0x1221, 0x1231, 0x1240, 0x1250, 0x1260, 0x1270,
		0x1200, 0x1210, 0x1220, 0x1230, 0x1340, 0x1350, 0x1360, 0x1370, 0x1301, 0x1311,
		0x1321, 0x1331, 0x1441, 0x1451, 0x1461, 0x1471, 0x1400, 0x1410, 0x1420, 0x1430,
		0x1440, 0x1450, 0x1460, 0x1470, 0x1600, 0x1610, 0x1620, 0x1630, 0x1640, 0x1650,
		0x1661, 0x1671, 0x1700, 0x1710, 0x1720, 0x1730, 0x1740, 0x1750, 0x1760, 0x1770,
		0x1701, 0x1711, 0x1821, 0x1831, 0x1840, 0x1850, 0x1860, 0x1870, 0x1800, 0x1810,
		0x1820, 0x1830, 0x1940, 0x1950, 0x1960, 0x1970, 0x1901, 0x1911, 0x1921, 0x1931,
		0x1941, 0x1951, 0x1a60, 0x1a70, 0x1b00, 0x1b10, 0x1b20, 0x1b30, 0x1b40, 0x1b50,
		0x1b60, 0x1b70, 0x1c01, 0x1c11, 0x1c21, 0x1c31, 0x1c40, 0x1c50, 0x1c60, 0x1c70,
		0x1c00, 0x1c10, 0x1c20, 0x1c30, 0x1d40, 0x1d50, 0x1d60, 0x1d70, 0x1d01, 0x1d11,
		0x1d21, 0x1d31, 0x1e41, 0x1e51, 0x1e61, 0x1e71, 0x1e00, 0x1e10, 0x1e20, 0x1e30,
		0x1e40, 0x1e50, 0x1e60, 0x1e70, 0x2000, 0x2010, 0x2020, 0x2030, 0x2040, 0x2050,
		0x2061, 0x2071, 0x2100, 0x2110, 0x2120, 0x2130, 0x2140, 0x2150, 0x2160, 0x2170,
		0x2101, 0x2111, 0x2221, 0x2231, 0x2240, 0x2250, 0x2260, 0x2270, 0x2200, 0x2210,
		0x2220, 0x2230, 0x2340, 0x2350, 0x2360, 0x2370, 0x2301, 0x2311, 0x2321, 0x2331,
		0x2341, 0x2351, 0x2460, 0x2470, 0x2500, 0x2510, 0x2520, 0x2530, 0x2540, 0x2550,
		0x2560, 0x2570, 0x2601, 0x2611, 0x2621, 0x2631, 0x2640, 0x2650, 0x2660, 0x2670,
		0x2600, 0x2610, 0x2620, 0x2630, 0x2740, 0x2750, 0x2760, 0x2770, 0x2701, 0x2711,
		0x2721, 0x2731, 0x2841, 0x2851, 0x2861, 0x2871, 0x2800, 0x2810, 0x2820, 0x2830,
		0x2840, 0x2850, 0x2860, 0x2870, 0x2a00, 0x2a10, 0x2a20, 0x2a30, 0x2a40, 0x2a50,
		0x2a61, 0x2a71, 0x2b00, 0x2b10, 0x2b20, 0x2b30, 0x2b40, 0x2b50, 0x2b60, 0x2b70,
		0x2b01, 0x2b11, 0x2c21, 0x2c31, 0x2c40, 0x2c50, 0x2c60, 0x2c70, 0x2c00, 0x2c10,
		0x2c20, 0x2c30, 0x2d40, 0x2d50, 0x2d60, 0x2d70, 0x2d01, 0x2d11, 0x2d21, 0x2d31,
		0x2d41, 0x2d51, 0x2e60, 0x2e70, 0x2f00, 0x2f10, 0x2f20, 0x2f30, 0x2f40, 0x2f50,
		0x2f60, 0x2f70, 0x3001, 0x3011, 0x3021, 0x3031, 0x3040, 0x3050, 0x3060, 0x3070,
		0x3000, 0x3010, 0x3020, 0x3030, 0x3140, 0x3150, 0x3160, 0x3170, 0x3101, 0x3111,
		0x3121, 0x3131, 0x3241, 0x3251, 0x3261, 0x3271, 0x3200, 0x3210, 0x3220, 0x3230,
		0x3240, 0x3250, 0x3260, 0x3270, 0x3400, 0x3410, 0x3420, 0x3430, 0x3440, 0x3450,
		0x3461, 0x3471, 0x3500, 0x3510, 0x3520, 0x3530, 0x3540, 0x3550, 0x3560, 0x3570,
		0x3501, 0x3511, 0x3621, 0x3631, 0x3640, 0x3650, 0x3660, 0x3670, 0x3600, 0x3610,
		0x3620, 0x3630, 0x3740, 0x3750, 0x3760, 0x3770, 0x3701, 0x3711, 0x3721, 0x3731,
		0x3741, 0x3751, 0x3860, 0x3870, 0x3900, 0x3910, 0x3920, 0x3930, 0x3940, 0x3950,
		0x3960, 0x3970, 0x3a01, 0x3a11, 0x3a21, 0x3a31, 0x3a40, 0x3a50, 0x3a60, 0x3a70,
		0x3a00, 0x3a10, 0x3a20, 0x3a30, 0x3b40, 0x3b50, 0x3b60, 0x3b70, 0x3b01, 0x3b11,
		0x3b21, 0x3b31, 0x3c41, 0x3c51, 0x3c61, 0x3c71, 0x3c00, 0x3c10, 0x3c20, 0x3c30,
		0x3c40, 0x3c50, 0x3c60, 0x3c70, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	};

	uint16_t base = x_to_address[m_lightpen_hit_x];
	if(!base)
		return;

	uint16_t pixel = (base >> 4) & 7;
	uint16_t skew = base & 1;
	uint16_t address = (base >> 8) + 60*m_lightpen_hit_y;

	m_lightpen_status = pixel | 8;
	m_lightpen_low = address & 0xff;
	m_lightpen_high = ((address >> 8) & 0x3f) | (skew ? 0x80 : 0x00);
}

void mtu130_state::tape_w(u8)
{
	logerror("tape_w\n");
}

void mtu130_state::map(address_map &map)
{
	map(0x00000, 0x0bfff).ram().share(m_mainram);                   // Main ram, in a single block
	map(0x08000, 0x0bfff).view(m_rom_view);                         // View to write-protect the top of the main ram
	m_rom_view[1](0x08000, 0x0bfff).nopw();

	map(0x0be00, 0x0bfff).view(m_io_view);                          // I/O dynamically overrides part of the main ram
	m_io_view[1](0x0be00, 0x0bfff).unmaprw();                       // Fully mask out the ram when active
	m_io_view[1](0x0bfc0, 0x0bfc0).rw(FUNC(mtu130_state::lightpen_status_r), FUNC(mtu130_state::lightpen_clear_w)).mirror(4);
	m_io_view[1](0x0bfc1, 0x0bfc1).rw(FUNC(mtu130_state::lightpen_low_r), FUNC(mtu130_state::keyboard_col_clear_w)).mirror(4);
	m_io_view[1](0x0bfc2, 0x0bfc2).rw(FUNC(mtu130_state::lightpen_high_r), FUNC(mtu130_state::tape_w)).mirror(4);
	m_io_view[1](0x0bfc3, 0x0bfc3).rw(FUNC(mtu130_state::id_r), FUNC(mtu130_state::id_reset_w)).mirror(4);
	m_io_view[1](0x0bfc8, 0x0bfcb).rw(m_acia, FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	m_io_view[1](0x0bfd0, 0x0bfdf).m(m_user_6522, FUNC(via6522_device::map));
	m_io_view[1](0x0bfe0, 0x0bfef).m(m_sys1_6522, FUNC(via6522_device::map));
	m_io_view[1](0x0bff0, 0x0bfff).m(m_sys2_6522, FUNC(via6522_device::map));

	map(0x0c000, 0x0ffff).view(m_se_view);                          // System (fdc)/External view
	m_se_view[0](0x0c000, 0x0ffff).ram().share(m_fdcram);           // FDC ram is a single 16K block, fully adressible by the dma
	m_se_view[0](0x0e000, 0x0ffff).view(m_rof_view);                // View to write-protect the top half of the fdc ram
	m_rof_view[1](0x0e000, 0x0ffff).nopw();

	m_se_view[0](0x0ff00, 0x0ffff).rom().region(m_ipl, 0).unmapw(); // Bootrom overrides the end of the ram
	m_se_view[0](0x0ffe8, 0x0ffef).unmaprw();                       // Hole in the prom access for floppy i/o
	m_se_view[0](0x0ffe8, 0x0ffe8).rw(FUNC(mtu130_state::fdc_ctrl_r), FUNC(mtu130_state::fdc_ctrl_w));
	m_se_view[0](0x0ffea, 0x0ffea).w(FUNC(mtu130_state::dma_adr_w));
	m_se_view[0](0x0ffee, 0x0ffef).m(m_fdc, FUNC(upd765a_device::map));

	m_se_view[1](0x0c000, 0x0ffff).rom().share(m_romdata);          // External rom view, contents set by the MTU130_ROM subdevices

	map(0x0fffe, 0x0fffe).w(FUNC(mtu130_state::io_enable_w));
	map(0x0ffff, 0x0ffff).w(FUNC(mtu130_state::io_disable_w));

	map(0x1c000, 0x1ffff).ram().share(m_videoram);                  // 16k of video ram

	map(0x20000, 0x3ffff).lrw8(NAME([this](offs_t offset) -> u8 { return m_ext[0]->read23(offset) & m_ext[1]->read23(offset) & m_ext[2]->read23(offset); }),
							   NAME([this](offs_t offset, u8 data) { m_ext[0]->write23(offset, data); m_ext[1]->write23(offset, data); m_ext[2]->write23(offset, data); }));
}

void mtu130_state::floppies(device_slot_interface &device)
{
	device.option_add("8ssdd", FLOPPY_8_SSDD);
	device.option_add("8dsdd", FLOPPY_8_DSDD);
}

void mtu130_state::extension_board(machine_config &config, int slot_id, const char *tag, const char *def)
{
	MTU130_EXTENSION(config, m_ext[slot_id]);
	m_ext[slot_id]->set_slot_id(slot_id);
	m_ext[slot_id]->set_irq_merger(m_irq_merger);
	if(def)
		m_ext[slot_id]->set_default_option(def);

	m_ext[slot_id]->option_add("datamover", MTU130_DATAMOVER0);
	m_ext[slot_id]->option_add("datamover_sec", MTU130_DATAMOVER1); // Datamover using secondary i/o addresses
}

void mtu130_state::mtu130(machine_config &config)
{
	config.set_perfect_quantum(m_maincpu); // Needs tight sync with the 68000 in the datamover

	M6502(config, m_maincpu, 10_MHz_XTAL/10);
	m_maincpu->set_address_width(18, true);
	m_maincpu->set_addrmap(AS_PROGRAM, &mtu130_state::map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	// HSync is between 40 and 79, VSync between 256 and 259, boundaries included
	m_screen->set_raw(10_MHz_XTAL, 620, 120, 600, 269, 0, 256);
	m_screen->set_screen_update(FUNC(mtu130_state::screen_update));
	m_screen->set_palette(m_palette);
	m_screen->screen_vblank().set(m_sys2_6522, FUNC(via6522_device::write_ca2));
	m_screen->screen_vblank().append(FUNC(mtu130_state::lightpen_trigger));

	PALETTE(config, m_palette).set_entries(4);

	MOS6522(config, m_user_6522, 10_MHz_XTAL/10);
	m_user_6522->irq_handler().set(m_irq_merger, FUNC(input_merger_device::in_w<1>));
	m_user_6522->cb2_handler().set(FUNC(mtu130_state::user_cb2_w));

	MOS6522(config, m_sys1_6522, 10_MHz_XTAL/10);
	m_sys1_6522->irq_handler().set(m_irq_merger, FUNC(input_merger_device::in_w<2>));
	m_sys1_6522->readpa_handler().set(FUNC(mtu130_state::sys1_pa_r));
	m_sys1_6522->writepb_handler().set(FUNC(mtu130_state::sys1_pb_w));
	m_sys1_6522->ca2_handler().set(FUNC(mtu130_state::sys1_ca2_w));

	MOS6522(config, m_sys2_6522, 10_MHz_XTAL/10);
	m_sys2_6522->irq_handler().set(m_irq_merger, FUNC(input_merger_device::in_w<3>));
	m_sys2_6522->writepa_handler().set(FUNC(mtu130_state::sys2_pa_w));

	MOS6551(config, m_acia, 3.68_MHz_XTAL/2);
	m_acia->irq_handler().set(m_irq_merger, FUNC(input_merger_device::in_w<4>));
	m_acia->txd_handler().set(m_rs232, FUNC(rs232_port_device::write_txd));

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set(m_acia, FUNC(mos6551_device::write_rxd));
	m_rs232->dcd_handler().set(m_acia, FUNC(mos6551_device::write_dcd));
	m_rs232->dsr_handler().set(m_acia, FUNC(mos6551_device::write_dsr));
	m_rs232->cts_handler().set(m_acia, FUNC(mos6551_device::write_cts));

	INPUT_MERGER_ANY_HIGH(config, m_irq_merger).output_handler().set_inputline(m_maincpu, m6502_device::IRQ_LINE);

	UPD765A(config, m_fdc, 10_MHz_XTAL/10*8, true, true); // *8 done through a PLL
	m_fdc->intrq_wr_callback().set(FUNC(mtu130_state::fdc_irq_w));
	m_fdc->drq_wr_callback().set(FUNC(mtu130_state::dma_drq_w));

	FLOPPY_CONNECTOR(config, m_floppy[0], mtu130_state::floppies, "8dsdd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[1], mtu130_state::floppies, "8dsdd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[2], mtu130_state::floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[3], mtu130_state::floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);

	DAC_8BIT_R2R(config, m_dac, 0).add_route(ALL_OUTPUTS, m_speaker, 1.0);
	SPEAKER(config, m_speaker).front_center();

	extension_board(config, 0, "ext0", "datamover");
	extension_board(config, 1, "ext1", nullptr);
	extension_board(config, 2, "ext2", nullptr);

	MTU130_ROM(config, m_roms[0], m_romdata, 0x3000);
	MTU130_ROM(config, m_roms[1], m_romdata, 0x2000);
	MTU130_ROM(config, m_roms[2], m_romdata, 0x1000);
	MTU130_ROM(config, m_roms[3], m_romdata, 0x0000);

	SOFTWARE_LIST(config, "flop_list").set_original("mtu130_flop");
}


static INPUT_PORTS_START(mtu130)
	PORT_START("jumpers")
	PORT_CONFNAME(0x01, 0x00, "Boot rom")
	PORT_CONFSETTING(0x00, "FDC rom")
	PORT_CONFSETTING(0x01, "ROM1 rom (aka rom F)")

	PORT_START("lightpen_w")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON1)
	PORT_BIT(0xfe, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("lightpen_x")
	PORT_BIT(0x1ff, 0x000, IPT_LIGHTGUN_X) PORT_MINMAX(0, 479) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(45) PORT_KEYDELTA(15)

	PORT_START("lightpen_y")
	PORT_BIT(0x0ff, 0x000, IPT_LIGHTGUN_Y) PORT_MINMAX(0, 255) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_SENSITIVITY(45) PORT_KEYDELTA(15)

	PORT_START("K0")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT)      PORT_CHAR(UCHAR_SHIFT_2) PORT_NAME("RIGHT SHIFT")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)      PORT_CHAR(UCHAR_SHIFT_1) PORT_NAME("LEFT SHIFT")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK)    PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_TOGGLE
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)    PORT_NAME("CTRL")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                PORT_NAME("REPEAT")

	PORT_START("K1")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD)   PORT_NAME("Keypad -")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)          PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)         PORT_CHAR(9)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)         PORT_CHAR(UCHAR_MAMEKEY(ESC))

	PORT_START("K2")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)       PORT_NAME("Keypad 6")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)          PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)       PORT_CHAR(' ')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)           PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)           PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)           PORT_CHAR('1') PORT_CHAR('!')

	PORT_START("K3")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD)    PORT_NAME("Keypad +")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)          PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)           PORT_CHAR('Z')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)           PORT_CHAR('S')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)           PORT_CHAR('W')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)           PORT_CHAR('2') PORT_CHAR('@')

	PORT_START("K4")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD)   PORT_NAME("Keypad /")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)          PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)           PORT_CHAR('X')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)           PORT_CHAR('D')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)           PORT_CHAR('E')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)           PORT_CHAR('3') PORT_CHAR('#')

	PORT_START("K5")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)       PORT_NAME("Keypad 5")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)          PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)           PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)           PORT_CHAR('F')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)           PORT_CHAR('R')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)           PORT_CHAR('4') PORT_CHAR('$')

	PORT_START("K6")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK)    PORT_NAME("Keypad *")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)          PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)           PORT_CHAR('V')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)           PORT_CHAR('G')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)           PORT_CHAR('T')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)           PORT_CHAR('5') PORT_CHAR('%')

	PORT_START("K7")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)       PORT_NAME("Keypad 9")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)          PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)           PORT_CHAR('B')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)           PORT_CHAR('H')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)           PORT_CHAR('Y')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)           PORT_CHAR('6') PORT_CHAR('^')

	PORT_START("K8")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                PORT_NAME("PF2")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)          PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)           PORT_CHAR('N')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)           PORT_CHAR('J')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)           PORT_CHAR('U')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)           PORT_CHAR('7') PORT_CHAR('&')

	PORT_START("K9")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)       PORT_NAME("Keypad 1")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)        PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)           PORT_CHAR('M')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)           PORT_CHAR('K')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)           PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)           PORT_CHAR('8') PORT_CHAR('*')

	PORT_START("KA")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)       PORT_NAME("Keypad 4")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)       PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)       PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)           PORT_CHAR('L')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)           PORT_CHAR('O')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)           PORT_CHAR('9') PORT_CHAR('(')

	PORT_START("KB")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)       PORT_NAME("Keypad 3")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD)   PORT_NAME("Keypad ENTER")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)        PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)       PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)           PORT_CHAR('P')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)           PORT_CHAR('0') PORT_CHAR(')')

	PORT_START("KC")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)       PORT_NAME("Keypad 7")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME)        PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)       PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)       PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)   PORT_CHAR('[') PORT_CHAR(']')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)       PORT_CHAR('-') PORT_CHAR('_')

	PORT_START("KD")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD)                                PORT_NAME("PF1")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)        PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)  PORT_CHAR('{') PORT_CHAR('}')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)   PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)      PORT_CHAR('=') PORT_CHAR('+')

	PORT_START("KE")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)       PORT_NAME("Keypad 8")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)          PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT)      PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)       PORT_NAME("RETURN")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                PORT_NAME("LINE FEED")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)       PORT_CHAR('`') PORT_CHAR('~')

	PORT_START("KF")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)       PORT_NAME("Keypad 2")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)       PORT_NAME("Keypad 0")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD)     PORT_NAME("Keypad .")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                PORT_NAME("RUBOUT")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)   PORT_CHAR(8)

	PORT_START("KM")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("INT")   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mtu130_state::nmi_w), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MOD")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RESET") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mtu130_state::reset_w), 0)
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BREAK") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mtu130_state::break_w), 0)
INPUT_PORTS_END

ROM_START(mtu130)
	ROM_REGION(0x100, "ipl", 0)
	ROM_LOAD("ipl_prom.u6", 0, 0x100, CRC(edb1525a) SHA1(a16cce3b096f0fea9ac5c6993ee4241e4af1efde))

	ROM_REGION(0x100, "sequencer", 0) // 4-bit prom
	ROM_LOAD("6301.u55", 0, 0x100, CRC(1541eb91) SHA1(78ab124865dc6ffd646abd2fcab5b881edd619c1))

	ROM_REGION(0x100, "id", 0) // 4-bit prom, only first 16 nibbles reachable, rest all f
	// address 1     unused f
	// address 2-6   vendor 00102
	// address 7-b   group  00000
	// address c-f,0 user   00175 (used by BASIC 1.0 and 1.5 for system-locking)

	ROM_LOAD("6301.u24", 0, 0x100, CRC(7ebc5451) SHA1(402bd7bf343d995bc9c857fe4f3a23e0a8e7bd1c))
ROM_END

COMP(1981, mtu130, 0, 0, mtu130, mtu130, mtu130_state, empty_init, "Micro Technology Unlimited", "MTU-130", MACHINE_SUPPORTS_SAVE|MACHINE_IMPERFECT_SOUND)




mtx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Lee Ward, Dirk Best, Curt Coder
/*************************************************************************

    Memotech MTX 500, MTX 512 and RS 128

**************************************************************************/

/*

    TODO:

    - FDX floppy
    - HDX hard disk
    - HRX high resolution graphics
    - "Silicon" disks
    - Multi Effect Video Wall

 */

#include "emu.h"
#include "mtx.h"

#include "bus/centronics/ctronics.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "video/tms9928a.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "utf8.h"


/***************************************************************************
    MEMORY MAPS
***************************************************************************/

/*-------------------------------------------------
    ADDRESS_MAP( mtx_mem )
-------------------------------------------------*/

void mtx_state::mtx_mem(address_map &map)
{
}

/*-------------------------------------------------
    ADDRESS_MAP( mtx_io )
-------------------------------------------------*/

void mtx_state::mtx_io(address_map &map)
{
	map.unmap_value_high(); // TODO: floating bus
	map.global_mask(0xff);
	map(0x00, 0x00).rw(FUNC(mtx_state::mtx_strobe_r), FUNC(mtx_state::mtx_bankswitch_w));
	map(0x01, 0x01).rw("tms9929a", FUNC(tms9929a_device::vram_read), FUNC(tms9929a_device::vram_write));
	map(0x02, 0x02).rw("tms9929a", FUNC(tms9929a_device::register_read), FUNC(tms9929a_device::register_write));
	map(0x03, 0x03).rw(FUNC(mtx_state::mtx_sound_strobe_r), FUNC(mtx_state::mtx_cst_w));
	map(0x04, 0x04).r(FUNC(mtx_state::mtx_prt_r)).w("cent_data_out", FUNC(output_latch_device::write));
	map(0x05, 0x05).rw(FUNC(mtx_state::mtx_key_lo_r), FUNC(mtx_state::mtx_sense_w));
	map(0x06, 0x06).rw(FUNC(mtx_state::mtx_key_hi_r), FUNC(mtx_state::mtx_sound_latch_w));
	//  map(0x07, 0x07) PIO
	map(0x08, 0x0b).rw(m_z80ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x1f, 0x1f).w(FUNC(mtx_state::mtx_cst_motor_w));
	map(0x30, 0x31).w(FUNC(mtx_state::hrx_address_w));
	map(0x32, 0x32).rw(FUNC(mtx_state::hrx_data_r), FUNC(mtx_state::hrx_data_w));
	map(0x33, 0x33).rw(FUNC(mtx_state::hrx_attr_r), FUNC(mtx_state::hrx_attr_w));
//  map(0x38, 0x38).w(MC6845_TAG, FUNC(mc6845_device::address_w));
//  map(0x39, 0x39).rw(MC6845_TAG, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
/*  map(0x40, 0x43).rw(FD1791_TAG, FUNC(fd1791_device::read), FUNC(fd1791_device::write));
    map(0x44, 0x44).rw(FUNC(mtx_state::fdx_status_r), FUNC(mtx_state::fdx_control_w));
    map(0x45, 0x45).w(FUNC(mtx_state::fdx_drv_sel_w));
    map(0x46, 0x46).w(FUNC(mtx_state::fdx_dma_lo_w));
    map(0x47, 0x47).w(FUNC(mtx_state::fdx_dma_hi_w);*/
}

/*-------------------------------------------------
    ADDRESS_MAP( rs128_io )
-------------------------------------------------*/

void mtx_state::rs128_io(address_map &map)
{
	mtx_io(map);
	map(0x0c, 0x0f).rw(m_z80dart, FUNC(z80dart_device::cd_ba_r), FUNC(z80dart_device::cd_ba_w));
}

/***************************************************************************
    INPUT PORTS
***************************************************************************/

/*-------------------------------------------------
    INPUT_PORTS( mtx512 )
-------------------------------------------------*/

static INPUT_PORTS_START( mtx512 )
	PORT_START("ROW0")
	PORT_BIT( 0x001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1)     PORT_CHAR('1')  PORT_CHAR('!')
	PORT_BIT( 0x002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)     PORT_CHAR('3')  PORT_CHAR('#')
	PORT_BIT( 0x004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5)     PORT_CHAR('5')  PORT_CHAR('%')
	PORT_BIT( 0x008, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7)     PORT_CHAR('7')  PORT_CHAR('\'')
	PORT_BIT( 0x010, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9)     PORT_CHAR('9')  PORT_CHAR(')')
	PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')  PORT_CHAR('=')
	PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 7 PAGE") PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(PGDN))   PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 9 BRK")  PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(CANCEL)) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT( 0x200, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1)    PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_START("ROW1")
	PORT_BIT( 0x001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ESC)    PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2)      PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)      PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x008, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6)      PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x010, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8)      PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0)      PORT_CHAR('0')
	PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 8 EOL") PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(END)) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x200, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F5)        PORT_CHAR(UCHAR_MAMEKEY(F5))

	PORT_START("ROW2")
	PORT_BIT( 0x001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)          PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)          PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x008, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x010, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 5 " UTF8_UP) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 4 TAB")      PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('\t')              PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT( 0x200, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2)    PORT_CHAR(UCHAR_MAMEKEY(F2))

	PORT_START("ROW3")
	PORT_BIT( 0x001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)         PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)         PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)         PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x008, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)         PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x010, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)         PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Line Feed")           PORT_CODE(KEYCODE_PRTSCR)
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 1 " UTF8_LEFT) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 6 DEL")        PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(DEL))  PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT( 0x200, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F6)    PORT_CHAR(UCHAR_MAMEKEY(F6))

	PORT_START("ROW4")
	PORT_BIT( 0x001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Alpha Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)          PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)          PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x008, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)          PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x010, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)          PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 3 " UTF8_RIGHT) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT( 0x200, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))

	PORT_START("ROW5")
	PORT_BIT( 0x001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)     PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)     PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)     PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x008, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)     PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x010, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)     PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 2 HOME") PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT( 0x200, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))

	PORT_START("ROW6")
	PORT_BIT( 0x001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LSHIFT)  PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)       PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)       PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x008, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)       PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x010, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA)   PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH)   PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RSHIFT)  PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad . " UTF8_DOWN) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT( 0x200, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))

	PORT_START("ROW7")
	PORT_BIT( 0x001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)      PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)      PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)      PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x008, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)      PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT( 0x010, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)   PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('_')
	PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 0 INS")   PORT_CODE(KEYCODE_0_PAD)     PORT_CHAR(UCHAR_MAMEKEY(INSERT)) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad ENT CLS") PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
	PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x200, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F4)    PORT_CHAR(UCHAR_MAMEKEY(F4))

	PORT_START("RESET")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mtx_state::trigger_reset), 0)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(RALT)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mtx_state::trigger_reset), 0)

	PORT_START("JOY0")
	PORT_BIT( 0x3ff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("JOY1")
	PORT_BIT( 0x3ff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("JOY2")
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x37f, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("JOY3")
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x37f, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("JOY4")
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x37f, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("JOY5")
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x37f, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("JOY6")
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x37f, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("JOY7")
	PORT_BIT( 0x001, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x002, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x004, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x008, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x2f0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("JOYSTICKS")
	PORT_CONFNAME(0x03, 0x02, "Joysticks")
	PORT_CONFSETTING(0x00, "None" )
	PORT_CONFSETTING(0x01, "Left" )
	PORT_CONFSETTING(0x02, "Right" )
	PORT_CONFSETTING(0x03, "Left + Right" )

	PORT_START("SWA")
	PORT_DIPNAME(0xc00, 0x000, "Country Code")
	PORT_DIPSETTING(0x000, "English" )
	PORT_DIPSETTING(0x400, "French" )
	PORT_DIPSETTING(0x800, "German" )
	PORT_DIPSETTING(0xc00, "Swedish" )

	PORT_START("keyboard_rom")
	PORT_CONFNAME(0x03, 0x00, "Keyboard ROM")
	PORT_CONFSETTING(0x00, "None")
	PORT_CONFSETTING(0x01, "Denmark")
	PORT_CONFSETTING(0x02, "Finland")
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(mtx_state::trigger_reset)
{
	if (m_reset->read() & 0x03)
	{
		m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
	}
	else
	{
		bankswitch(0);
		m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
	}
}

/***************************************************************************
    DEVICE CONFIGURATION
***************************************************************************/

/*-------------------------------------------------
    Z80CTC
-------------------------------------------------*/

TIMER_DEVICE_CALLBACK_MEMBER(mtx_state::ctc_tick)
{
	m_z80ctc->trg1(1);
	m_z80ctc->trg1(0);
	m_z80ctc->trg2(1);
	m_z80ctc->trg2(0);
}

void mtx_state::ctc_trg1_w(int state)
{
	if (m_z80dart)
	{
		m_z80dart->rxca_w(state);
		m_z80dart->txca_w(state);
	}
}

void mtx_state::ctc_trg2_w(int state)
{
	if (m_z80dart)
	{
		m_z80dart->rxtxcb_w(state);
	}
}

/*-------------------------------------------------
    z80_daisy_config mtx_daisy_chain
-------------------------------------------------*/

static const z80_daisy_config mtx_daisy_chain[] =
{
	{ "z80ctc" },
	{ nullptr }
};

/*-------------------------------------------------
    z80_daisy_config rs128_daisy_chain
-------------------------------------------------*/

static const z80_daisy_config rs128_daisy_chain[] =
{
	{ "z80ctc" },
	{ "z80dart" },
	{ nullptr }
};


TIMER_DEVICE_CALLBACK_MEMBER(mtx_state::cassette_tick)
{
	bool cass_ws = (m_cassette->input() > +0.04) ? 1 : 0;

	if (cass_ws != m_cassold)
	{
		m_cassold = cass_ws;
		m_z80ctc->trg3(1);
		m_z80ctc->trg3(0);   // this causes interrupt
	}
}

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

/*-------------------------------------------------
    machine_config( mtx512 )
-------------------------------------------------*/

void mtx_state::mtx512(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mtx_state::mtx_mem);
	m_maincpu->set_addrmap(AS_IO, &mtx_state::mtx_io);
	m_maincpu->set_daisy_config(mtx_daisy_chain);

	/* video hardware */
	tms9929a_device &vdp(TMS9929A(config, "tms9929a", 10.6875_MHz_XTAL));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	vdp.int_callback().set(m_z80ctc, FUNC(z80ctc_device::trg0)).invert();
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SN76489A(config, m_sn, 4_MHz_XTAL).add_route(ALL_OUTPUTS, "mono", 1.00);

	/* devices */
	Z80CTC(config, m_z80ctc, 4_MHz_XTAL);
	m_z80ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_z80ctc->zc_callback<1>().set(FUNC(mtx_state::ctc_trg1_w));
	m_z80ctc->zc_callback<2>().set(FUNC(mtx_state::ctc_trg2_w));

	TIMER(config, "z80ctc_timer").configure_periodic(FUNC(mtx_state::ctc_tick), attotime::from_hz(4_MHz_XTAL/13));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(mtx_state::write_centronics_busy));
	m_centronics->fault_handler().set(FUNC(mtx_state::write_centronics_fault));
	m_centronics->perror_handler().set(FUNC(mtx_state::write_centronics_perror));
	m_centronics->select_handler().set(FUNC(mtx_state::write_centronics_select));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	SNAPSHOT(config, "snapshot", "mtx", attotime::from_seconds(1)).set_load_callback(FUNC(mtx_state::snapshot_cb));
	QUICKLOAD(config, "quickload", "run", attotime::from_seconds(1)).set_load_callback(FUNC(mtx_state::quickload_cb));

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->set_interface("mtx_cass");

	TIMER(config, "cassette_timer").configure_periodic(FUNC(mtx_state::cassette_tick), attotime::from_hz(44100));

	/* internal ram */
	RAM(config, m_ram).set_default_size("64K").set_extra_options("96K,128K,192K,320K,448K,512K");

	/* rom extension board */
	GENERIC_SOCKET(config, m_extrom, generic_plain_slot, "mtx_rom", "bin,rom");
	m_extrom->set_device_load(FUNC(mtx_state::extrom_load));

	/* J10 external cartridge */
	MTX_EXP_SLOT(config, m_exp_ext, mtx_ext_expansion_devices, nullptr);
	m_exp_ext->set_program_space(m_maincpu, AS_PROGRAM);
	m_exp_ext->set_io_space(m_maincpu, AS_IO);
	m_exp_ext->int_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_exp_ext->nmi_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_exp_ext->busreq_handler().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);

	/* J0 internal expansion - rs232 board with disk drive bus */
	MTX_EXP_SLOT(config, m_exp_int, mtx_int_expansion_devices, nullptr);
	m_exp_int->set_program_space(m_maincpu, AS_PROGRAM);
	m_exp_int->set_io_space(m_maincpu, AS_IO);
	m_exp_int->int_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_exp_int->nmi_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_exp_int->busreq_handler().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);

	/* software lists */
	SOFTWARE_LIST(config, "cass_list").set_original("mtx_cass");
	SOFTWARE_LIST(config, "flop_list").set_original("mtx_flop");
	SOFTWARE_LIST(config, "cart_list").set_original("mtx_cart");
	SOFTWARE_LIST(config, "hdd_list").set_original("mtx_hdd");
	SOFTWARE_LIST(config, "rom_list").set_original("mtx_rom");
}

void mtx_state::mtx500(machine_config &config)
{
	mtx512(config);

	/* internal ram */
	m_ram->set_default_size("32K").set_extra_options("64K,96K,128K,160K,288K,416K");
}

/*-------------------------------------------------
    machine_config( rs128 )
-------------------------------------------------*/

void mtx_state::rs128(machine_config &config)
{
	mtx512(config);

	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_IO, &mtx_state::rs128_io);
	m_maincpu->set_daisy_config(rs128_daisy_chain);

	/* devices */
	Z80DART(config, m_z80dart, 4_MHz_XTAL);
	m_z80dart->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	/* internal ram */
	m_ram->set_default_size("128K").set_extra_options("192K,320K,448K,512K");
}

/***************************************************************************
    ROMS
***************************************************************************/

ROM_START( mtx512 )
	ROM_REGION( 0x02000, "user1", 0 )
	ROM_SYSTEM_BIOS( 0, "uk", "UK" )
	ROMX_LOAD( "mtx1a.9h",   0x0000, 0x2000, CRC(9ca858cc) SHA1(3804503a58f0bcdea96bb6488833782ebd03976d), ROM_BIOS(0) ) /* OS */
	ROM_SYSTEM_BIOS( 1, "de", "German" )
	ROMX_LOAD( "mtx2a.9h",   0x0000, 0x2000, CRC(1c7bbe98) SHA1(8c950051b830e5e0c41072d6bd893151acd3839d), ROM_BIOS(1) ) /* OS */

	ROM_REGION( 0x10000, "user2", ROMREGION_ERASEFF )
	ROMX_LOAD( "mtx1b.8h",  0x0000, 0x2000, CRC(87b4e59c) SHA1(c49782a82a7f068c1195cd967882ba9edd546eaf), ROM_BIOS(0) ) /* BASIC */
	ROMX_LOAD( "mtx1c.10h", 0x2000, 0x2000, CRC(9d7538c3) SHA1(d1882c4ea61a68b1715bd634ded5603e18a99c5f), ROM_BIOS(0) ) /* ASSEM */
	ROMX_LOAD( "mtx2b.8h",  0x0000, 0x2000, CRC(599d5b6b) SHA1(3ec1f7f476a21ca3206012ded22198c020b47f7d), ROM_BIOS(1) ) /* BASIC */
	ROMX_LOAD( "mtx1c.10h", 0x2000, 0x2000, CRC(9d7538c3) SHA1(d1882c4ea61a68b1715bd634ded5603e18a99c5f), ROM_BIOS(1) ) /* ASSEM */

	/* Keyboard PROMs (N82S181N) are piggy-backed on top of the OS ROM, wired to be selected as ROM 7 */
	/* Country character sets documented in the Operators Manual are:
	    U.S.A, France, Germany, England, Denmark, Sweden, Italy, Spain */
	ROM_REGION( 0x4000, "keyboard_rom", 0 )
	ROM_LOAD( "danish.rom",  0x0000, 0x2000, CRC(9c1b3fae) SHA1(82bc021660d88eebcf0c4d3856558ee9acc1c348) )
	ROM_LOAD( "finnish.rom", 0x2000, 0x2000, CRC(9b96cf72) SHA1(b46d1a733e0e635ccdaf4752cc370d793c3b5c55) )

	/* Device GAL16V8 converted from PAL14L4 JEDEC map */
	ROM_REGION( 0x117, "plds", 0 )
	ROM_LOAD( "memotech-mtx512.bin", 0x0000, 0x0117, CRC(31f88133) SHA1(5bef3ce764121b3510b538824b2768f082b422bb) )
ROM_END

#define rom_mtx500  rom_mtx512
#define rom_rs128   rom_mtx512

/***************************************************************************
    SYSTEM DRIVERS
***************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS      INIT        COMPANY         FULLNAME   FLAGS
COMP( 1983, mtx512, 0,      0,      mtx512,  mtx512, mtx_state, empty_init, "Memotech Ltd", "MTX 512", 0 )
COMP( 1983, mtx500, mtx512, 0,      mtx500,  mtx512, mtx_state, empty_init, "Memotech Ltd", "MTX 500", 0 )
COMP( 1984, rs128,  mtx512, 0,      rs128,   mtx512, mtx_state, empty_init, "Memotech Ltd", "RS 128",  0 )


/*
The following roms are available should they be considered useful:

ROM_START( mtx_roms )
    ROM_LOAD( "assem.rom",   0x0000, 0x2000, CRC(599d5b6b) SHA1(3ec1f7f476a21ca3206012ded22198c020b47f7d) )
    ROM_LOAD( "basic.rom",   0x0000, 0x4000, CRC(d1e9ff36) SHA1(e89ae3a627716e6cee7e35054be8a2472bdd49d4) )
    ROM_LOAD( "boot.rom",    0x0000, 0x2000, CRC(ed98d6dd) SHA1(4671ee49bb96262b0468f7122a49bf2588170903) )
    ROM_LOAD( "mtx3-an.rom", 0x0000, 0x2000, CRC(54c9eca2) SHA1(3e628beaa360e635264c8c2c3a5b8312951a220b) )
    ROM_LOAD( "nboot.rom",   0x0000, 0x2000, CRC(9caea81c) SHA1(93fca6e7ffbc7ae3283b8bda9f01c36b2bed1c54) )
ROM_END

BASIC.ROM   this contains the monitor ROM plus the BASIC ROM.
        It's good to view them as one, because the monitor
        ROM contains a good deal of BASIC code and the machine
        code just runs from the monitor ROM into the BASIC ROM.
        It's also handy if you want to disassemble it (which
        you don't need because it's already done).

MTX3-AN.ROM the monitor ROM, but slightly modified
        While detecting memory size, the startup code destroys
        RAM content. When you have a lot of RAM it is convenient
        to have a ramdisk for CP/M, but it is a nuisance if the
        ramdisk is trashed at each reset. The modification simply
        prevents RAM trashing.

ASSEM.ROM   assembler ROM

BOOT.ROM    FDX floppy boot ROM

NBOOT.ROM   replacement FDX boot ROM written by M. Kessler (supports
        booting from different disk formats and different drives)
*/



multi16.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese, Dirk Best
/***************************************************************************

    Mitsubishi MULTI-16 series

    MULTI-16 (1982, 1983 for S models)
    - 8088, 4 MHz
    - 8041 (MCU), 8253 (PIT), 8259A (PIC), MB8866 (FDC)
    - MP-1601 (128 KB RAM, 32 KB VRAM, monochrome, 1x 5.25")
    - MP-1602 (192 KB RAM, 32 KB VRAM, monochrome, 2x 5.25")
    - MP-1605 (256 KB RAM, 96 KB VRAM, 8 colors, 2x 5.25")
    - MP-1601S (8087, 128 KB RAM, 32 KB VRAM, monochrome, 1x 5.25")
    - MP-1602S (8087, 192 KB RAM, 32 KB VRAM, monochrome, 2x 5.25")
    - MP-1605S (8087, 256 KB RAM, 96 KB VRAM, 8 colors, 2x 5.25")
    - MP-1622 (8087, 192 KB RAM, 32 KB VRAM, monochrome, 2x 8")
    - MP-1625 (8087, 256 KB RAM, 96 KB VRAM, 8 colors, 2x 8")

    MULTI-16 II (1984)
    - 8086, 8 MHz
    - MP-1642 (256 KB RAM, 64 KB VRAM, monochrome, 2x 5.25")
    - MP-1645 (256 KB RAM, 192 KB VRAM, 8 colors, 2x 5.25")

    MULTI-16 IV (1986)
    - 80286, 8 MHz
    - MP-1652-A20 (512 KB RAM, 128 KB VRAM, monochrome, 2x 5.25")
    - MP-1652-A22 (512 KB RAM, 128 KB VRAM, monochrome, 2x 5.25", 20 MB HDD)
    - MP-1655-A20 (512 KB RAM, 384 KB VRAM, 8 colors, 2x 5.25")
    - MP-1655-A22 (512 KB RAM, 384 KB VRAM, 8 colors, 2x 5.25", 20 MB HDD)

    The IPL dump we currently have seems to be from the MP-1645.

****************************************************************************/

#include "emu.h"
#include "cpu/i86/i86.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/wd_fdc.h"
#include "video/mc6845.h"
#include "imagedev/floppy.h"
#include "emupal.h"
#include "screen.h"


namespace {

class multi16_state : public driver_device
{
public:
	multi16_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_pic(*this, "pic"),
		m_pit(*this, "pit"),
		m_crtc(*this, "crtc"),
		m_palette(*this, "palette"),
		m_fdc(*this, "fdc"),
		m_floppy(*this, "fdc:%u", 0U),
		m_vram(*this, "vram")
	{ }

	void multi16(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<pic8259_device> m_pic;
	required_device<pit8253_device> m_pit;
	required_device<hd6845s_device> m_crtc;
	required_device<palette_device> m_palette;
	required_device<mb8866_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_shared_ptr<uint16_t> m_vram;

	MC6845_UPDATE_ROW(crtc_update_row);

	void multi16_io(address_map &map) ATTR_COLD;
	void multi16_map(address_map &map) ATTR_COLD;
};

MC6845_UPDATE_ROW(multi16_state::crtc_update_row)
{
	uint8_t const *const vram = reinterpret_cast<uint8_t *>(m_vram.target());

	for (int i = 0; i < x_count; i++)
	{
		offs_t offs = ((ra & 1) << 14) + (ma << 3) + ((ra >> 1) * 80) + i;

		// vram is from c0000 to effff, the ipl uses the last half
		uint8_t data_r = vram[3 * 0x8000 + offs];
		uint8_t data_g = vram[4 * 0x8000 + offs];
		uint8_t data_b = vram[5 * 0x8000 + offs];

		// draw 8 pixels of the character
		for (int x = 0; x < 8; x++)
		{
			int color = 0;
			color |= BIT(data_r, 7 - x) << 0;
			color |= BIT(data_g, 7 - x) << 1;
			color |= BIT(data_b, 7 - x) << 2;

			bitmap.pix(y, x + i * 8) = m_palette->pens()[color];
		}
	}
}

void multi16_state::multi16_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x3ffff).ram();
	map(0x40000, 0x7ffff).noprw();
	map(0xc0000, 0xeffff).ram().share("vram");
	map(0xf0000, 0xf3fff).mirror(0xc000).rom().region("ipl", 0);
}

void multi16_state::multi16_io(address_map &map)
{
	map.unmap_value_high();
//  map(0x02, 0x03) // ?
//  map(0x06, 0x06) // ?
//  map(0x07, 0x07) // ?
//  map(0x08, 0x0b) // ?
	map(0x0c, 0x0f).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x40, 0x40).w(m_crtc, FUNC(hd6845s_device::address_w));
	map(0x41, 0x41).rw(m_crtc, FUNC(hd6845s_device::register_r), FUNC(hd6845s_device::register_w));
//  map(0x42, 0x44) // ?
//  map(0x48, 0x48) // ?
//  map(0x4e, 0x4e) // ?
//  map(0x82, 0x82) // ?
//  map(0x92, 0x92) // ?
}

/* Input ports */
static INPUT_PORTS_START( multi16 )
INPUT_PORTS_END

void multi16_state::machine_start()
{
}


void multi16_state::machine_reset()
{
}

static void multi16_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

void multi16_state::multi16(machine_config &config)
{
	I8086(config, m_maincpu, 8000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &multi16_state::multi16_map);
	m_maincpu->set_addrmap(AS_IO, &multi16_state::multi16_io);
	m_maincpu->set_irq_acknowledge_callback("pic", FUNC(pic8259_device::inta_cb));

	PIC8259(config, m_pic);
	m_pic->out_int_callback().set_inputline(m_maincpu, 0);

	PIT8253(config, m_pit);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(16000000, 848, 0, 640, 518, 0, 400); // unknown clock
	screen.set_screen_update("crtc", FUNC(hd6845s_device::screen_update));

	PALETTE(config, m_palette, palette_device::RGB_3BIT);

	HD6845S(config, m_crtc, 2000000); // unknown clock
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(multi16_state::crtc_update_row));

	// floppy
	MB8866(config, m_fdc, 2000000);

	FLOPPY_CONNECTOR(config, m_floppy[0], multi16_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[1], multi16_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);
}

/* ROM definition */

// MULTI16MODEL2-24
ROM_START( multi16 )
	ROM_REGION16_LE( 0x4000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD("ipl.rom", 0x0000, 0x4000, CRC(5beb5e94) SHA1(d3b9dc9a08995a0f26af9671893417e795370306))

	ROM_REGION(0x20000, "kanji", 0)
	ROM_LOAD("kanji.rom", 0x00000, 0x20000, NO_DUMP)
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY       FULLNAME    FLAGS
COMP( 1984, multi16, 0,      0,      multi16, multi16, multi16_state, empty_init, "Mitsubishi", "MULTI 16-II MP-1645", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



multi8.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
/***************************************************************************

    Multi 8 (c) 1983 Mitsubishi

    preliminary driver by Angelo Salese

    TODO:
    - dunno how to trigger the text color mode in BASIC, I just modify
      $f0b1 to 1 for now
    - bitmap B/W mode is untested
    - keyboard
    - check cassette with real software

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/pit8253.h"
#include "machine/i8255.h"
#include "machine/pic8259.h"
#include "machine/timer.h"
#include "machine/upd765.h"
#include "imagedev/cassette.h"
#include "sound/ay8910.h"
#include "sound/beep.h"
#include "sound/ymopn.h"
#include "video/mc6845.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class multi8_state : public driver_device
{
public:
	multi8_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_p_chargen(*this, "chargen")
		, m_ppi(*this, "ppi")
		, m_crtc(*this, "crtc")
		, m_uart(*this, "uart")
		, m_cass(*this, "cassette")
		, m_beeper(*this, "beeper")
		, m_palette(*this, "palette")
		, m_aysnd(*this, "aysnd")
	{ }

	void multi8(machine_config &config);

private:
	uint8_t key_input_r();
	uint8_t key_status_r();
	uint8_t vram_r(offs_t offset);
	void vram_w(offs_t offset, uint8_t data);
	uint8_t pal_r(offs_t offset);
	void pal_w(offs_t offset, uint8_t data);
	uint8_t kanji_r(offs_t offset);
	void kanji_w(offs_t offset, uint8_t data);
	uint8_t porta_r();
	void portb_w(uint8_t data);
	void portc_w(uint8_t data);
	void ym2203_porta_w(uint8_t data);
	uint8_t ay8912_0_r();
	uint8_t ay8912_1_r();
	TIMER_DEVICE_CALLBACK_MEMBER(keyboard_callback);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);
	void kansas_w(int state);
	MC6845_UPDATE_ROW(crtc_update_row);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	uint8_t *m_p_vram = nullptr;
	uint8_t *m_p_wram = nullptr;
	uint8_t *m_p_kanji = nullptr;
	uint8_t m_mcu_init = 0;
	uint8_t m_keyb_press = 0;
	uint8_t m_keyb_press_flag = 0;
	uint8_t m_shift_press_flag = 0;
	uint8_t m_display_reg = 0;
	uint8_t m_vram_bank = 0;
	uint8_t m_pen_clut[8]{};
	uint8_t m_bw_mode = 0;
	uint16_t m_knj_addr = 0;
	u8 m_cass_data[4]{};
	bool m_cassbit = 0, m_cassold = 0;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_p_chargen;
	required_device<i8255_device> m_ppi;
	required_device<mc6845_device> m_crtc;
	required_device<i8251_device> m_uart;
	required_device<cassette_image_device> m_cass;
	required_device<beep_device> m_beeper;
	required_device<palette_device> m_palette;
	required_device<ay8910_device> m_aysnd;
};

void multi8_state::video_start()
{
	m_keyb_press = m_keyb_press_flag = m_shift_press_flag = m_display_reg = 0;

	for (m_bw_mode = 0; m_bw_mode < 8; m_bw_mode++)
		m_pen_clut[m_bw_mode]=0;

	m_vram_bank = 8;
	m_bw_mode = 0;
}

MC6845_UPDATE_ROW( multi8_state::crtc_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u16 mem = y*80;
	u32 *p = &bitmap.pix(y);

	for(u16 x = 0; x < x_count; x++)
	{
		for(u8 i = 0; i < 8; i++)
		{
			u8 pen_b = BIT(m_p_vram[mem | 0x0000], 7-i);
			u8 pen_r = BIT(m_p_vram[mem | 0x4000], 7-i);
			u8 pen_g = BIT(m_p_vram[mem | 0x8000], 7-i);
			u8 color = (pen_b) | (pen_r << 1) | (pen_g << 2);

			if (m_bw_mode)
			{
				pen_b = BIT(m_display_reg, 0) ? pen_b : 0;
				pen_r = BIT(m_display_reg, 1) ? pen_r : 0;
				pen_g = BIT(m_display_reg, 2) ? pen_g : 0;

				color = (pen_b | pen_r | pen_g) ? 7 : 0;
			}

			*p++ = palette[color];
		}
		mem++;
	}

	const u8 x_width = BIT(m_display_reg, 6) ? 80 : 40;
	const u8 x_step = BIT(m_display_reg, 6) ? 1 : 2;
	mem = 0xc000 + ma;
	p = &bitmap.pix(y);

	for(u16 x = 0; x < x_width; x++)
	{
		const u8 chr = m_p_vram[mem];
		const u8 attr = m_p_vram[mem | 0x800];
		const u8 color = (BIT(m_display_reg, 7)) ? 7 : (attr & 0x07);

		u8 gfx = BIT(attr, 5);

		if (cursor_x >= 0)
			gfx ^= (x == (cursor_x / x_step));

		if (gfx)
			gfx = 0xff;

		if (ra < 8)
			gfx ^= m_p_chargen[(chr << 3) | ra];

		for(u8 i = 0; i < 8; i++)
		{
			u8 pen = BIT(gfx, 7-i) ? color : 0;

			if (x_step == 1)
			{
				if (pen)
					*p = palette[color];
				p++;
			}
			else
			{
				if (pen)
					*p = palette[color];
				p++;
				if (pen)
					*p = palette[color];
				p++;
			}
		}
		mem += x_step;
	}
}

uint8_t multi8_state::key_input_r()
{
	if (m_mcu_init == 0)
	{
		m_mcu_init++;
		return 3;
	}

	m_keyb_press_flag &= 0xfe;

	return m_keyb_press;
}

uint8_t multi8_state::key_status_r()
{
	if (m_mcu_init == 0)
		return 1;
	else
	if (m_mcu_init == 1)
	{
		m_mcu_init++;
		return 1;
	}
	else
	if (m_mcu_init == 2)
	{
		m_mcu_init++;
		return 0;
	}

	return m_keyb_press_flag | (m_shift_press_flag << 7);
}

uint8_t multi8_state::vram_r(offs_t offset)
{
	uint8_t res;

	if (!BIT(m_vram_bank, 4)) //select plain work ram
		return m_p_wram[offset];

	res = 0xff;

	if (!BIT(m_vram_bank, 0))
		res &= m_p_vram[offset | 0x0000];

	if (!BIT(m_vram_bank, 1))
		res &= m_p_vram[offset | 0x4000];

	if (!BIT(m_vram_bank, 2))
		res &= m_p_vram[offset | 0x8000];

	if (!BIT(m_vram_bank, 3))
		res &= m_p_vram[offset | 0xc000];

	return res;
}

void multi8_state::vram_w(offs_t offset, uint8_t data)
{
	if (!BIT(m_vram_bank, 4)) //select plain work ram
	{
		m_p_wram[offset] = data;
		return;
	}

	if (!BIT(m_vram_bank, 0))
		m_p_vram[offset | 0x0000] = data;

	if (!BIT(m_vram_bank, 1))
		m_p_vram[offset | 0x4000] = data;

	if (!BIT(m_vram_bank, 2))
		m_p_vram[offset | 0x8000] = data;

	if (!BIT(m_vram_bank, 3))
		m_p_vram[offset | 0xc000] = data;
}

uint8_t multi8_state::pal_r(offs_t offset)
{
	return m_pen_clut[offset];
}

void multi8_state::pal_w(offs_t offset, uint8_t data)
{
	m_pen_clut[offset] = data;

	uint8_t i;
	for(i=0;i<8;i++)
	{
		if (m_pen_clut[i])
		{
			m_bw_mode = 0;
			return;
		}
		m_bw_mode = 1;
	}
}

uint8_t multi8_state::ay8912_0_r(){ return m_aysnd->data_r(); }
uint8_t multi8_state::ay8912_1_r(){ return m_aysnd->data_r(); }

uint8_t multi8_state::kanji_r(offs_t offset)
{
	return m_p_kanji[(m_knj_addr << 1) | (offset & 1)];
}

void multi8_state::kanji_w(offs_t offset, uint8_t data)
{
	m_knj_addr = (offset == 0) ? (m_knj_addr & 0xff00) | (data & 0xff) : (m_knj_addr & 0x00ff) | (data << 8);
}

void multi8_state::kansas_w(int state)
{
	// incoming @19200Hz
	u8 twobit = m_cass_data[3] & 3;

	if (state)
	{
		if (twobit == 0)
			m_cassold = m_cassbit;

		if (m_cassold)
			m_cass->output(BIT(m_cass_data[3], 2) ? -1.0 : +1.0); // 2400Hz
		else
			m_cass->output(BIT(m_cass_data[3], 3) ? -1.0 : +1.0); // 1200Hz

		m_cass_data[3]++;
	}

	m_uart->write_txc(state);
	m_uart->write_rxc(state);
}

TIMER_DEVICE_CALLBACK_MEMBER( multi8_state::kansas_r )
{
	/* cassette - turn 1200/2400Hz to a bit */
	m_cass_data[1]++;
	uint8_t cass_ws = (m_cass->input() > +0.04) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		m_uart->write_rxd((m_cass_data[1] < 12) ? 1 : 0);
		m_cass_data[1] = 0;
	}
}


void multi8_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).rom();
	map(0x8000, 0xbfff).rw(FUNC(multi8_state::vram_r), FUNC(multi8_state::vram_w));
	map(0xc000, 0xffff).ram();
}

void multi8_state::io_map(address_map &map)
{
//  map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x00).r(FUNC(multi8_state::key_input_r)); //keyboard
	map(0x01, 0x01).r(FUNC(multi8_state::key_status_r)); //keyboard
	map(0x18, 0x19).w(m_aysnd, FUNC(ay8910_device::address_data_w));
	map(0x18, 0x18).r(FUNC(multi8_state::ay8912_0_r));
	map(0x1a, 0x1a).r(FUNC(multi8_state::ay8912_1_r));
	map(0x1c, 0x1c).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x1d, 0x1d).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x20, 0x21).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write)); //cmt
	map(0x24, 0x27).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write)); //pit
	map(0x28, 0x2b).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x2c, 0x2d).rw("pic", FUNC(pic8259_device::read), FUNC(pic8259_device::write)); //i8259
	map(0x30, 0x37).rw(FUNC(multi8_state::pal_r), FUNC(multi8_state::pal_w));
	map(0x40, 0x41).rw(FUNC(multi8_state::kanji_r), FUNC(multi8_state::kanji_w)); //kanji regs
//  map(0x70, 0x74) //upd765a fdc
//  map(0x78, 0x78) //memory banking
}

/* Input ports */
static INPUT_PORTS_START( multi8 )
	PORT_START("VBLANK")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("screen", FUNC(screen_device::vblank))

	PORT_START("key1") //0x00-0x1f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_UNUSED) //0x00 null
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_UNUSED) //0x01 soh
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_UNUSED) //0x02 stx
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_UNUSED) //0x03 etx
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_UNUSED) //0x04 etx
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_UNUSED) //0x05 eot
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_UNUSED) //0x06 enq
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_UNUSED) //0x07 ack
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_UNUSED) //0x0a
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_UNUSED) //0x0b lf
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x0c vt
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(27)
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x0e cr
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x0f so

	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x10 si
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x11 dle
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x12 dc1
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x13 dc2
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("HOME") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x15 dc4
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x17 syn
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x18 etb
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x1a em
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("ESC") PORT_CHAR(27)
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x1c ???
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x1d fs
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x1e gs
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x1f us

	PORT_START("key2") //0x20-0x3f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_UNUSED) //0x21 !
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_UNUSED) //0x22 "
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_UNUSED) //0x23 #
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_UNUSED) //0x24 $
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_UNUSED) //0x25 %
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_UNUSED) //0x26 &
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_UNUSED) //0x27 '
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_UNUSED) //0x28 (
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_UNUSED) //0x29 )
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_UNUSED) //0x2a *
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_UNUSED)
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x2c ,
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x2e .
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x2f /

	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':')
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';')
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("<") PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('<')
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x3d =
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x3e >
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x3f ?

	PORT_START("key3") //0x40-0x5f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@')
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[')
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_UNUSED)
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(']')
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^')
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("_")

	PORT_START("key_modifiers")
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("KANA") PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("CAPS") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("GRPH") PORT_CODE(KEYCODE_LALT)
INPUT_PORTS_END

TIMER_DEVICE_CALLBACK_MEMBER(multi8_state::keyboard_callback)
{
	static const char *const portnames[3] = { "key1","key2","key3" };
	int i,port_i,scancode;
	uint8_t keymod = ioport("key_modifiers")->read() & 0x1f;
	scancode = 0;

	m_shift_press_flag = ((keymod & 0x02) >> 1);

	for(port_i=0;port_i<3;port_i++)
	{
		for(i=0;i<32;i++)
		{
			if((ioport(portnames[port_i])->read()>>i) & 1)
			{
				//key_flag = 1;
				if(!m_shift_press_flag)  // shift not pressed
				{
					if(scancode >= 0x41 && scancode < 0x5b)
						scancode += 0x20;  // lowercase
				}
				else
				{
					if(scancode >= 0x31 && scancode < 0x3a)
						scancode -= 0x10;
					if(scancode == 0x30)
						scancode = 0x3d;

					if(scancode == 0x3b)
						scancode = 0x2c;

					if(scancode == 0x3a)
						scancode = 0x2e;
					if(scancode == 0x5b)
						scancode = 0x2b;
					if(scancode == 0x3c)
						scancode = 0x3e;
				}
				m_keyb_press = scancode;
				m_keyb_press_flag = 1;
				return;
			}
			scancode++;
		}
	}
}

static const gfx_layout multi8_charlayout =
{
	8,8,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP8(0,1) },
	{ STEP8(0,8) },
	8*8
};

static const gfx_layout multi8_kanjilayout =
{
	16,16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP16(0,1) },
	{ STEP16(0,16) },
	16*16
};

static GFXDECODE_START( gfx_multi8 )
	GFXDECODE_ENTRY( "chargen", 0x0000, multi8_charlayout, 0, 1 )
	GFXDECODE_ENTRY( "kanji",   0x0000, multi8_kanjilayout, 0, 1 )
GFXDECODE_END


uint8_t multi8_state::porta_r()
{
	int vsync = (ioport("VBLANK")->read() & 0x1) << 5;
	/*
	-x-- ---- kanji rom is present (0) yes
	--x- ---- vsync
	---- --x- fdc rom is present (0) yes
	*/

	return 0x9f | vsync | 0x00;
}


void multi8_state::portb_w(uint8_t data)
{
	/*
	    x--- ---- color mode
	    -x-- ---- screen width (80 / 40)
	    ---- x--- memory bank status
	    ---- -xxx page screen graphics in B/W mode
	*/

	m_display_reg = data;
}

void multi8_state::portc_w(uint8_t data)
{
//  printf("Port C w = %02x\n",data);
	m_vram_bank = data & 0x1f;

	if (data & 0x20 && data != 0xff)
		printf("Work RAM bank selected!\n");
//      fatalerror("Work RAM bank selected\n");
}


void multi8_state::ym2203_porta_w(uint8_t data)
{
	m_beeper->set_state(BIT(data, 3));
}

void multi8_state::machine_start()
{
	m_p_vram = memregion("vram")->base();
	m_p_wram = memregion("wram")->base();
	m_p_kanji = memregion("kanji")->base();
}

void multi8_state::machine_reset()
{
	m_beeper->set_state(0);
	m_mcu_init = 0;
	m_uart->write_cts(0);
}

void multi8_state::multi8(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &multi8_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &multi8_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 200);
	screen.set_visarea(0, 320-1, 0, 200-1);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	PALETTE(config, m_palette, palette_device::BRG_3BIT);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_multi8);

	/* Audio */
	SPEAKER(config, "mono").front_center();
	AY8912(config, m_aysnd, 1500000); //unknown clock / divider
	m_aysnd->port_a_write_callback().set(FUNC(multi8_state::ym2203_porta_w));
	m_aysnd->add_route(ALL_OUTPUTS, "mono", 0.50);
	BEEP(config, m_beeper, 1200).add_route(ALL_OUTPUTS,"mono",0.50); // guesswork

	/* devices */
	TIMER(config, "keyboard_timer").configure_periodic(FUNC(multi8_state::keyboard_callback), attotime::from_hz(240/32));

	MC6845(config, m_crtc, XTAL(3'579'545)/2);    /* unknown variant, unknown clock, hand tuned to get ~60 fps */
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(multi8_state::crtc_update_row));

	I8255(config, m_ppi);
	m_ppi->in_pa_callback().set(FUNC(multi8_state::porta_r));
	m_ppi->out_pb_callback().set(FUNC(multi8_state::portb_w));
	m_ppi->out_pc_callback().set(FUNC(multi8_state::portc_w));

	clock_device &uart_clock(CLOCK(config, "uart_clock", 19200));
	uart_clock.signal_handler().set(FUNC(multi8_state::kansas_w));
	TIMER(config, "kansas_r").configure_periodic(FUNC(multi8_state::kansas_r), attotime::from_hz(40000));

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	I8251(config, m_uart, 0); // for cassette
	m_uart->txd_handler().set([this] (bool state) { m_cassbit = state; });

	PIT8253(config, "pit", 0);
	PIC8259(config, "pic", 0);

	//UPD765A(config, "fdc", false, true);
	//FLOPPY_CONNECTOR(config, "fdc:0", multi8_floppies, "525hd", floppy_image_device::default_mfm_floppy_formats);
}

/* ROM definition */
ROM_START( multi8 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "basic.rom", 0x0000, 0x8000, CRC(2131a3a8) SHA1(0f5a565ecfbf79237badbf9011dcb374abe74a57))

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "font.rom",  0x0000, 0x0800, BAD_DUMP CRC(08f9ed0e) SHA1(57480510fb30af1372df5a44b23066ca61c6f0d9))

	ROM_REGION( 0x20000, "kanji", 0 )
	ROM_LOAD( "kanji.rom",  0x0000, 0x20000, BAD_DUMP CRC(c3cb3ff9) SHA1(7173cc5053a281d73cfecfacd31442e21ee7474a))

	ROM_REGION( 0x0100, "mcu", ROMREGION_ERASEFF )
	ROM_LOAD( "kbd.rom",  0x0000, 0x0100, NO_DUMP )

	ROM_REGION( 0x1000, "fdc_bios", 0 )
	ROM_LOAD( "disk.rom",  0x0000, 0x1000, NO_DUMP )

	ROM_REGION( 0x10000, "vram", ROMREGION_ERASE00 )

	ROM_REGION( 0x4000, "wram", ROMREGION_ERASEFF )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY       FULLNAME                FLAGS
COMP( 1983, multi8, 0,      0,      multi8,  multi8, multi8_state, empty_init, "Mitsubishi", "Multi 8 (Mitsubishi)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



mupid2.cpp
<---------------------------------------------------------------------->
// license: GPL-2.0+
// copyright-holders: Dirk Best
/***************************************************************************

    mupid/Infonova C2A2
    Grundig PTC-100

    - Z80
    - 128 + 8 KB RAM
    - Z80 SIO/0
    - 8035
    - M58990P-1 ADC

***************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/adc0808.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "machine/z80sio.h"
#include "emupal.h"


namespace {

//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class mupid2_state : public driver_device
{
public:
	mupid2_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_kbdcpu(*this, "kbdcpu"),
		m_ram(*this, "ram"),
		m_rombank(*this, "rombank"),
		m_rambank(*this, "rambank"),
		m_palette(*this, "palette"),
		m_nmi_enabled(false)
		{ }

	void c2a2(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	TIMER_DEVICE_CALLBACK_MEMBER(nmi);
	void port_a0_w(uint8_t data);
	void port_c0_w(uint8_t data);
	template<int C> void palette_w(uint8_t data);
	uint8_t kbd_bus_r();
	uint8_t kbd_p1_r();
	void kbd_p1_w(uint8_t data);
	void kbd_p2_w(uint8_t data);

	void palette_init(palette_device &palette);

	required_device<cpu_device> m_maincpu;
	required_device<i8035_device> m_kbdcpu;
	required_device<ram_device> m_ram;
	required_memory_bank m_rombank;
	required_memory_bank m_rambank;
	required_device<palette_device> m_palette;

	void maincpu_mem(address_map &map) ATTR_COLD;
	void maincpu_io(address_map &map) ATTR_COLD;
	void kbdcpu_mem(address_map &map) ATTR_COLD;

	std::unique_ptr<std::array<uint8_t, 3>[]> m_color_ram;
	bool m_nmi_enabled;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void mupid2_state::maincpu_mem(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	map(0x4000, 0x5fff).bankr(m_rombank);
	map(0x6000, 0x7fff).ram();
	map(0x8000, 0xffff).bankrw(m_rambank);
}

void mupid2_state::maincpu_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).rw("sio", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0xa0, 0xa0).w(FUNC(mupid2_state::port_a0_w));
	map(0xc0, 0xc0).w(FUNC(mupid2_state::port_c0_w));
	map(0xe0, 0xe0).w(FUNC(mupid2_state::palette_w<0>));
	map(0xe8, 0xe8).w(FUNC(mupid2_state::palette_w<1>));
	map(0xf0, 0xf0).w(FUNC(mupid2_state::palette_w<2>));
}

void mupid2_state::kbdcpu_mem(address_map &map)
{
	map(0x000, 0x3ff).mirror(0xc00).rom();
}


//**************************************************************************
//  INPUTS
//**************************************************************************

INPUT_PORTS_START( mupid2 )
INPUT_PORTS_END

uint8_t mupid2_state::kbd_bus_r()
{
//  logerror("kbd_bus_r\n");
	return 0xff;
}

uint8_t mupid2_state::kbd_p1_r()
{
//  logerror("kbd_p1_r\n");
	return 0xff;
}

void mupid2_state::kbd_p1_w(uint8_t data)
{
//  logerror("kbd_p1_w: %02x\n", data);
}

void mupid2_state::kbd_p2_w(uint8_t data)
{
//  logerror("kbd_p2_w: %02x\n", data);
}


//**************************************************************************
//  VIDEO
//**************************************************************************

void mupid2_state::palette_init(palette_device &palette)
{
	// first 16 colors are fixed (arbitrary, colors unknown)
	for (int i = 0; i < 16; i++)
	{
		int r = (i >> 0) & 1;
		int g = (i >> 1) & 1;
		int b = (i >> 2) & 3;
		palette.set_pen_color(i, rgb_t(pal1bit(r), pal1bit(g), pal2bit(b)));
	}
}

template<int C>
void mupid2_state::palette_w(uint8_t data)
{
	int i = data & 0x0f;
	m_color_ram[i][C] = data >> 4;
	m_palette->set_pen_color(16 + i, rgb_t(
		pal4bit(m_color_ram[i][0]),
		pal4bit(m_color_ram[i][1]),
		pal4bit(m_color_ram[i][2])
	));
}


//**************************************************************************
//  MACHINE
//**************************************************************************

void mupid2_state::machine_start()
{
	// additional rom space
	m_rombank->configure_entries(0, 4, memregion("maincpu")->base() + 0x4000, 0x2000);
	m_rombank->set_entry(0);

	// 128k memory
	m_rambank->configure_entries(0, 4, m_ram->pointer(), 0x8000);
	m_rambank->set_entry(0);

	// allocate space for colors
	m_color_ram = make_unique_clear<std::array<uint8_t, 3>[]>(16);

	// register for save states
	save_pointer(NAME(m_color_ram), 16);
	save_item(NAME(m_nmi_enabled));
}

TIMER_DEVICE_CALLBACK_MEMBER(mupid2_state::nmi)
{
	if (m_nmi_enabled)
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}

void mupid2_state::port_a0_w(uint8_t data)
{
	logerror("port_a0_w: %02x\n", data);

	// 7-------  unknown
	// -6------  unknown
	// --5-----  unknown
	// ---4----  unknown
	// ----3---  unknown
	// -----2--  unknown
	// ------1-  unknown
	// -------0  unknown
}

void mupid2_state::port_c0_w(uint8_t data)
{
	logerror("port_c0_w: %02x\n", data);

	// 7-------  nmi mask
	// -6------  unknown
	// --5-----  unknown
	// ---4----  unknown
	// ----32--  ram bank?
	// ------1-  unknown
	// -------0  unknown

	m_nmi_enabled = BIT(data, 7);
	m_rambank->set_entry((data >> 2) & 0x03);
}


//**************************************************************************
//  MACHINE DEFINITIONS
//**************************************************************************

void mupid2_state::c2a2(machine_config &config)
{
	Z80(config, m_maincpu, 4000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &mupid2_state::maincpu_mem);
	m_maincpu->set_addrmap(AS_IO, &mupid2_state::maincpu_io);

	RAM(config, m_ram).set_default_size("128K");

	// timing unknown
	TIMER(config, "nmi_timer").configure_periodic(FUNC(mupid2_state::nmi), attotime::from_hz(5000));

	Z80SIO(config, "sio", 4000000);

	I8035(config, m_kbdcpu, 4000000);
	m_kbdcpu->set_addrmap(AS_PROGRAM, &mupid2_state::kbdcpu_mem);
	m_kbdcpu->bus_in_cb().set(FUNC(mupid2_state::kbd_bus_r));
	m_kbdcpu->p1_in_cb().set(FUNC(mupid2_state::kbd_p1_r));
	m_kbdcpu->p1_out_cb().set(FUNC(mupid2_state::kbd_p1_w));
	m_kbdcpu->p2_out_cb().set(FUNC(mupid2_state::kbd_p2_w));

	M58990(config, "adc", 1000000);

	PALETTE(config, m_palette, FUNC(mupid2_state::palette_init), 32);
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( mupid2 )
	ROM_REGION(0xc000, "maincpu", 0)
	ROM_LOAD("0090.7012.40.02_27.09.85.u40", 0x0000, 0x4000, CRC(0b320f46) SHA1(064e1b1697b9b767f89be7c1e3d20e1157324791))
	ROM_LOAD("0090.7012.39.02_27.09.85.u39", 0x4000, 0x4000, CRC(b2fb634c) SHA1(70ced48d0a27a661ddd7fbc529a891bd0bcec926))
	ROM_RELOAD(0x8000, 0x4000)

	ROM_REGION(0x800, "kbdcpu", 0)
	ROM_LOAD("motronic_0090.6820.06.00", 0x000, 0x400, CRC(78b6d827) SHA1(200f2b33c518a889f8c6a5f4dd4443ac76884b21))
	ROM_CONTINUE(0x000, 0x400)
ROM_END

ROM_START( mupid2i )
	ROM_REGION(0xc000, "maincpu", 0)
	ROM_LOAD("kv_2.5.90_c2a2_ffd3.u40", 0x0000, 0x8000, CRC(a77ccb92) SHA1(9588e07ee0d4f06b346b7f5b58b8086a1b6ef140))
	ROM_LOAD("kh_2.5.90_c2a2_6860.u39", 0x8000, 0x4000, CRC(cdf64a6d) SHA1(a4a76ac761de016c7a196120a1c9fef6016c171c))

	ROM_REGION(0x800, "kbdcpu", 0)
	ROM_LOAD("motronic_0090.6820.06.00", 0x000, 0x400, CRC(78b6d827) SHA1(200f2b33c518a889f8c6a5f4dd4443ac76884b21))
	ROM_CONTINUE(0x000, 0x400)
ROM_END

ROM_START( ptc100 )
	ROM_REGION(0xc000, "maincpu", 0)
	ROM_LOAD("mup.u40", 0x0000, 0x4000, CRC(b4ac8ccb) SHA1(01bc818ec571d099176b6f69aaa736bb5410dd8e))
	ROM_LOAD("mup.u39", 0x4000, 0x4000, CRC(9812fefc) SHA1(bb4e69eba504dae6065094d9668be2b0478b0433))
	ROM_RELOAD(0x8000, 0x4000)

	ROM_REGION(0x800, "kbdcpu", 0)
	ROM_LOAD("kbc.bin", 0x000, 0x400, CRC(72502f02) SHA1(4adb5c55691e1c53a3364d97e64d194be4886b52))
	ROM_CONTINUE(0x000, 0x400)
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY     FULLNAME            FLAGS
COMP( 1985, mupid2,  0,      0,      c2a2,    mupid2, mupid2_state, empty_init, "mupid",    "Post-Mupid C2A2", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1990, mupid2i, mupid2, 0,      c2a2,    mupid2, mupid2_state, empty_init, "Infonova", "C2A2",            MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 198?, ptc100,  mupid2, 0,      c2a2,    mupid2, mupid2_state, empty_init, "Grundig",  "PTC-100",         MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



mvme162.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Joakim Larsson Edstrom
/***************************************************************************
 *
 *  Motorola MVME series of CPU boards: MVME-162
 *
 *  16/05/2016, rebased 2019
 *
 *
 *       ||
 * ||    ||  MVME-162
 * ||||--||_____________________________________________________________
 * ||||--||                                                             |
 * ||    ||                                                           _ |__
 *       C|                                                          | |   |
 *       ||                                                          | |   |
 *       C|                                                          | |   |
 *       ||                                                          | |   |
 *       C|                                                          | |   |
 *       ||                                                          | |   |
 *       C|                                                          | |VME|
 *       ||                                                          | |   |
 *       ||                                                          | |P1 |
 *       ||                                                          | |   |
 *       ||                                                          | |   |
 *       ||                                                          | |   |
 *       ||                                                          | |   |
 *       ||                                                          | |   |
 *       ||                                                          | |   |
 *       ||                                                          |_|   |
 *       ||                                                            |___|
 *       ||                                                            |
 *       ||                                                            |
 *       ||                                                            |
 *       ||                                                            |
 *       ||                                                            |
 *       ||                                                            |
 *       ||                                                            |
 *       ||                                                            |
 *       ||                                                            |___
 *       ||                                                           _|   |
 *       ||                                                          | |   |
 *       ||                                                          | |   |
 *       ||                                                          | |   |
 *       ||                                                          | |   |
 *       ||                                                          | |VME|
 *       ||                                                          | |   |
 *       ||                                                          | |P2 |
 *       ||                                                          | |   |
 *       ||                                                          | |   |
 *       ||                                                          | |   |
 *       ||                                                          | |   |
 *       ||                                                          | |   |
 *       ||                                                          | |   |
 *       ||                                                          | |   |
 *       ||                                                          | |   |
 *       ||                                                          |_|   |
 *       ||                                                            |___|
 * ||    ||                                                              +
 * ||||--||                                                              |
 * ||||--||--------------------------------------------------------------+
 * ||
 *
 * History of Motorola VME division (https://en.wikipedia.org/wiki/VMEbus)
 *---------------------------------
 * See mvme147.cpp
 *
 * Misc links about this board:
 * ----------------------------
 * http://bitsavers.informatik.uni-stuttgart.de/pdf/motorola/VME/MVME162/
 * http://www.m88k.com/mvme162.html
 *
 * Description(s)
 * -------------
 * MVME-162 has the following feature set
 *      - 25/33MHz MHzMC68040 or MC68LC040 Microprocessor
 *      - 1 or 4 MB of DRAM with parity protection on a mezzanine module, or 16 MB ECC DRAM on a mezzanine board
 *      - 128 KB of SRAM with battery backup, or 2 MB SRAM on a mezzanine board with battery backup
 *      - Four JEDEC standard 32-pin DIP PROM sockets
 *      - One Intel 28F008SA 1M x 8 Flash memory device with write protection.
 *      - Status LEDs for FAIL, RUN, SCON, and FUSES
 *      - 8K by 8 Non-Volatile RAM (NVRAM) and time of day (TOD) clock with battery backup
 *      - RESET and ABORT switches
 *      - Four 32-bit Tick Timers and Watchdog Timer (in the MCchip ASIC) for periodic interrupts
 *      - Two 32-bit Tick Timers and Watchdog Timer (in the VMEchip2 ASIC) for periodic interrupts
 *      - Eight software interrupts (for MVME162LX versions that have the VMEchip2)
 *      - Optional SCSI Bus interface with DMA
 *      - Four serial ports with EIA-232-D interface (serial port controllers are the Z85230s
 *      - Optional Ethernet transceiver interface with DMA Two IndustryPack interfaces
 * VMEbus interface
 *      - VMEbus system controller functions
 *      - VMEbus interface to local bus (A24/A32,
 *      - D8/D16/D32 (D8/D16/D32/D64 BLT) (BLT = Block Transfer)
 *      - Local bus to VMEbus interface (A16/A24/A32, D8/D16/D32)
 *      - VMEbus interrupter
 *      - VMEbus interrupt handler
 *      - Global CSR for interprocessor communications
 *      - DMA for fast local memory - VMEbus transfers (A16/A24/A32, D16/D32 (D16/D32/D64 BLT)
 *
 * Address Map
 * --------------------------------------------------------------------------
 *                          Decscription
 * --------------------------------------------------------------------------
 * 00000000-001FFFFF        Boot ROM until ROM0 bit is cleared
 * Programmable             DRAM on Parity Mezzanine D32 1-4MB
 * Programmable             DRAM on ECC Mezzanine D32 16MB
 * Programmable             On-board SRAM D32 128KB
 * Programmable             SRAM on Mezzanine D32 2MB
 * Programmable             VMEbus A32/A24 D32/D16
 * Programmable             IP_a Memory D32-D8 64KB-8MB
 * Programmable             IP_b Memory D32-D8 64KB-8MB
 * FF800000-FF9FFFFF        Flash/EPROM D32 2Mb
 * FFA00000-FFBFFFFF        EPROM/Flash D32 2Mb
 * FFC00000-FFDFFFFF        Not decoded
 * FFE00000-FFE1FFFF        On-board SRAM D32 128Kb
 * FFE80000-FFEFFFFF        Not decoded
 * ------------------------ Local I/O devices D8/D16/D32
 * FFF00000-FFF3FFFF        Reserved 256KB
 * FFF40000-FFF400FF        VMEchip2 (LCSR) D32 256B
 * FFF40100-FFF401FF        VMEchip2 (GCSR) D32-D8 256B
 * FFF40200-FFF40FFF        Reserved 3.5KB
 * FFF41000-FFF41FFF        Reserved 4KB
 * FFF42000-FFF41FFF        MCchip D32-D8 4KB
 * FFF43000-FFF430FF        MCECC #1 D8 256B
 * FFF43100-FFF431FF        MCECC #2 D8 256B
 * FFF43200-FFF43FFF        MCECC:s mirrored
 * FFF44000-FFF44FFF        Reserved
 * FFF45000-FFF45800        SCC #1 (Z85230) D8 2Kb
 * FFF45801-FFF45FFF        SCC #2 (Z85230) D8 2Kb
 * FFF46000-FFF46FFF        LAN (82596CA) D32 4Kb
 * FFF47000-FFF47FFF        SCSI (53C710) D32-D8 4Kb
 * FFF48000-FFF57FFF        Reserved
 * FFF58000-FFF587FF        IPIC IP_* D32-D16
 * FFF58800-FFF58FFF        Reserved
 * FFFBC000-FFFBC01F        IPIC Registers D32-D8
 * FFFBC800-FFFBFFFF        Reserved
 * FFFC0000-FFFC7FFF        MK48T08 (BBRAM, TOD Clock) D32-D8 32Kb
 * FFFC8000-FFFCBFFF        MK48T08 & Disable Flash writes D32-D8 16Kb
 * FFFC8000-FFFCBFFF        MK48T08 & Enable Flash writes D32-D8 16Kb
 * FFFD0000-FFFEFFFF        Reserved
 * FFFF0000-FFFFFFFF        VMEbux short I/O D16
 * --------------------------------------------------------------------------
 *
 *  TODO: (at a high level)
 *  - Add all SCC
 *  - Pass 162bug bootup tests
 *  - Add more divices as required by each board configuration (at least 30+ variants)
 *  - Write and add ASIC devices
 *  - Add local bus(es)
 *  - Add VME bus
 *  - Boot pSOS and VxWorks
 *  - Add variants of boards, preferably as runtime configurations
 *
 ****************************************************************************/

#include "emu.h"
#include "cpu/m68000/m68040.h"
#include "machine/z80scc.h"
#include "bus/rs232/rs232.h"
#include "machine/clock.h"
#include "machine/timekpr.h"

#define VERBOSE (0) // (LOG_GENERAL)
//#define LOG_OUTPUT_STREAM osd_printf_info
#include "logmacro.h"


//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

#ifdef _MSC_VER
#define FUNCNAME __func__
#else
#define FUNCNAME __PRETTY_FUNCTION__
#endif

/*Serial Communications Interface
The MVME162LX uses two Zilog Z85230 serial port controllers to implement the four serial communications
interfaces. Each interface supports CTS, DCD, RTS, and DTR control signals; as well as the TXD and RXD
transmit/receive data signals. Because the serial clocks are omitted in the MVME162LX implementation,
serial communications are strictly asynchronous. The MVME162LX hardware supports serial baud rates of
110b/s to 38.4Kb/s. The Z85230 supplies an interrupt vector during interrupt acknowledge cycles.
The vector is modified based upon the interrupt source within the Z85230. Interrupt request levels are
programmed via the MCchip. The Z85230s are interfaced as DTE (data terminal equipment) with EIA-232-D
signal levels. The four serial ports are routed to four RJ45 telephone connectors on the MVME162LX front panel.*/


namespace {

/* This gives prompt at the RS232 terminal device (9600) */
#define BAUDGEN_CLOCK 10_MHz_XTAL // Not verified nor seen on the PCB:s
#define SCC_CLOCK (BAUDGEN_CLOCK)

class mvme162_state : public driver_device
{
public:
mvme162_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device (mconfig, type, tag)
		, m_maincpu (*this, "maincpu")
		, m_sccterm(*this, "scc")
	{
	}

	void mvme162(machine_config &confg);

private:
	uint32_t bootvect_r(offs_t offset);
	void bootvect_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	virtual void machine_start () override;
	virtual void machine_reset () override;
	void mvme162_mem(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<scc85230_device> m_sccterm;
	//required_device<scc85230_device> m_sccterm2;

	// Pointer to System ROMs needed by bootvect_r and masking RAM buffer for post reset accesses
	uint32_t *m_sysrom = nullptr;
	uint32_t m_sysram[2]{};

	// PCC registers
	uint8_t m_genpurp_stat = 0;

	// VME chip registers
	uint8_t m_vc_cntl_conf = 0;
};

void mvme162_state::mvme162_mem(address_map &map)
{
	map.unmap_value_high();

	map(0x00000000, 0x00000007).ram().w(FUNC(mvme162_state::bootvect_w));       /* After first write we act as RAM */
	map(0x00000000, 0x00000007).rom().r(FUNC(mvme162_state::bootvect_r));       /* ROM mirror just during reset */

	map(0x00000008, 0x003fffff).ram(); /* 4 Mb RAM */

	map(0xff800000, 0xff9fffff).rom().region("roms", 0x800000); /* ROM/EEPROM bank 1 - 162bug/firmware */
	map(0xffa00000, 0xffbfffff).rom().region("roms", 0xa00000); /* ROM/EEPROM bank 2 - unpopulated/VxWorks/etc */

	map(0xffe00000, 0xffe1ffff).ram(); /* 128KB on board SRAM */

		/*  SGS-Thompson M48T18 RAM and clock chip, only 4088 bytes used,  and 8 bytes for the RTC, out of 8Kb though */
	map(0xfffc0000, 0xfffc7fff).rw("m48t18", FUNC(timekeeper_device::read), FUNC(timekeeper_device::write));

	map(0xfff45000, 0xfff457ff).rw(m_sccterm, FUNC(scc85230_device::ab_dc_r), FUNC(scc85230_device::ab_dc_w)).umask32(0x00ff00ff); /* Port 1&2 - Dual serial port Z80-SCC */
}

/* Input ports */
static INPUT_PORTS_START (mvme162)
INPUT_PORTS_END

/* Start it up */
void mvme162_state::machine_start ()
{
	LOG("--->%s\n", FUNCNAME);

	/* Setup pointer to bootvector in ROM for bootvector handler bootvect_r */
	m_sysrom = (uint32_t*)(memregion ("roms")->base () + 0x800000);
	m_genpurp_stat = 0x02; /* Indicate power up reset */
	m_vc_cntl_conf = 0x01; /* We are the system controller */
}

void mvme162_state::machine_reset ()
{
	LOG("--->%s\n", FUNCNAME);

	/* Reset pointer to bootvector in ROM for bootvector handler bootvect_r */
	if (m_sysrom == &m_sysram[0]) /* Condition needed because memory map is not setup first time */
		m_sysrom = (uint32_t*)(memregion ("roms")->base () + 0x800000);
	m_genpurp_stat &= 0xfe; /* Clear parity error bit - not used by MAME at this point so just for the record */
}

/*
  Boot vector handler. Devices mapped at $FFF80000-$FFF9FFFF also appear at $00000000-$001FFFFF when the ROM0 bit
  in the MCchip EPROM control register is high (ROM0=1). ROM0 is set to 1 after each reset. The ROM0 bit must be
  cleared before other resources (DRAM or SRAM) can be mapped in this range ($00000000 - $001FFFFF).
*/
uint32_t mvme162_state::bootvect_r(offs_t offset)
{
	return m_sysrom[offset];
}

void mvme162_state::bootvect_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	m_sysram[offset % sizeof(m_sysram)] &= ~mem_mask;
	m_sysram[offset % sizeof(m_sysram)] |= (data & mem_mask);
	m_sysrom = &m_sysram[0]; // redirect all upcoming accesses to masking RAM until reset.
}

/*
 * Machine configuration
 */
void mvme162_state::mvme162(machine_config &config)
{
	M68040(config, m_maincpu, 25_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mvme162_state::mvme162_mem);

	M48T02(config, "m48t18", 0); /* t08 differs only in accepted voltage levels compared to t18 */

	/* Terminal Port config */
	SCC85230(config, m_sccterm, SCC_CLOCK);
	m_sccterm->out_txda_callback().set("rs232trm", FUNC(rs232_port_device::write_txd));
	m_sccterm->out_dtra_callback().set("rs232trm", FUNC(rs232_port_device::write_dtr));
	m_sccterm->out_rtsa_callback().set("rs232trm", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232trm(RS232_PORT(config, "rs232trm", default_rs232_devices, "terminal"));
	rs232trm.rxd_handler().set(m_sccterm, FUNC(scc85230_device::rxa_w));
	rs232trm.cts_handler().set(m_sccterm, FUNC(scc85230_device::ctsa_w));
}

/* ROM definitions */
ROM_START (mvme162)
	ROM_REGION32_BE(0xf00000, "roms", 0)
	ROM_DEFAULT_BIOS("162bug-v4.0")

	ROM_SYSTEM_BIOS(0, "162bug-v2.3", "MVME162 162bug v2.3")
	ROMX_LOAD("162bug_2.3.bin", 0x800000, 0x80000, CRC (301f52a8) SHA1 (ffc77561dce26a70020452baef76f4eb9dc14543), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "162bug-v4.0", "MVME162 162bug v4.0")
	ROMX_LOAD("162bug_4.0.bin", 0x800000, 0x80000, CRC (56728e5b) SHA1 (0b8b6725c21d8a9048d24857d6acd2b68a7f3ba0), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "probe", "MVME162 pSOS pROBE+ boot loader")
	ROMX_LOAD("162probe+_3.1.0.bin", 0x800000, 0x80000, CRC (1d050793) SHA1 (d060fbbf548b2559c0d251fae5a2eb87b0132f0b), ROM_BIOS(2))

	ROM_SYSTEM_BIOS(3, "vxworks", "MVME162 WindRiver VxWorks boot loader")
	ROMX_LOAD("162bug_2.3.bin", 0x800000, 0x80000, CRC (301f52a8) SHA1 (ffc77561dce26a70020452baef76f4eb9dc14543), ROM_BIOS(3))
	ROMX_LOAD("vxworks_5.1.1_162lx-223.bin", 0xa00000, 0x20000, CRC (b40b39ac) SHA1 (fbc7f7e05ff276fe4570daeadcc5c08fc11f1a2b), ROM_BIOS(3))
ROM_END

} // anonymous namespace


/* Driver */
//    YEAR  NAME     PARENT  COMPAT   MACHINE  INPUT     CLASS          INIT        COMPANY       FULLNAME    FLAGS
COMP (1993, mvme162, 0,      0,       mvme162, mvme162,  mvme162_state, empty_init, "Motorola",   "MVME-162", MACHINE_NOT_WORKING)



mw4pole.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Waldorf MiniWorks 4-Pole analog filter module.

****************************************************************************/

#include "emu.h"
#include "cpu/mc68hc11/mc68hc11.h"

namespace {

class mw4pole_state : public driver_device
{
public:
	mw4pole_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void mw4pole(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<mc68hc11_cpu_device> m_maincpu;
};


void mw4pole_state::mem_map(address_map &map)
{
	map(0x8000, 0xffff).rom().region("eprom", 0);
}

static INPUT_PORTS_START(mw4pole)
INPUT_PORTS_END

void mw4pole_state::mw4pole(machine_config &config)
{
	MC68HC11E1(config, m_maincpu, 8_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mw4pole_state::mem_map);
}

ROM_START(mw4pole)
	ROM_REGION(0x8000, "eprom", 0)
	ROM_LOAD("4-pole v1.48 001eaf32.bin", 0x0000, 0x8000, CRC(51be6962) SHA1(20e793573a49002c854b012280aed13058ba0b63)) // TMS27C256
ROM_END

} // anonymous namespace

SYST(1995, mw4pole, 0, 0, mw4pole, mw4pole, mw4pole_state, empty_init, "Waldorf Electronics", "MiniWorks 4-Pole", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



mx2178.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************************************

Skeleton driver for Memorex 2178

Chips: Z80A, N8X305N, 2x B8452A, 4x D4016C-3, 2x HD468A50P, HD46505SP-1
Crystal: 18.8696MHz
There is a large piezo-beeper.

TODO:
- Connect up the beeper
- Unknown memory i/o C000, 4000
- Need schematic / tech manual
- Doesn't seem to be any dips, looks like all settings and modes are controlled by keystrokes.
- 8X305 is a 16-bit bipolar processor which appears to use four external PROMs (undumped). It
  would communicate with the Z80 via a common 8-bit I/O bus. No idea what it is used for here,
  but in another system it acts as the floppy disk controller.
- Debug trick: set pc=809 to see the test menu.
- It shows the status line but keystrokes are ignored. After 20 minutes of inactivity, the screen
  goes blank. Pressing a key will restore it.

***************************************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
//#include "cpu/8x300/8x300.h" // device = N8X300
#include "machine/6850acia.h"
#include "bus/rs232/rs232.h"
#include "machine/clock.h"
#include "video/mc6845.h"
#include "emupal.h"
#include "screen.h"

namespace {

class mx2178_state : public driver_device
{
public:
	mx2178_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_palette(*this, "palette")
		, m_p_videoram(*this, "videoram")
		, m_maincpu(*this, "maincpu")
		, m_p_chargen(*this, "chargen")
	{ }

	void mx2178(machine_config &config);

private:
	MC6845_UPDATE_ROW(crtc_update_row);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	virtual void machine_reset() override ATTR_COLD;
	required_device<palette_device> m_palette;
	required_shared_ptr<u8> m_p_videoram;
	required_device<z80_device> m_maincpu;
	required_region_ptr<u8> m_p_chargen;
};

void mx2178_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom().region("roms", 0);
	map(0x2000, 0x27ff).ram().share("videoram");
	map(0x6000, 0x6fff).ram();
	map(0xe000, 0xe7ff).ram();
}

void mx2178_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x00).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x01, 0x01).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x80, 0x81).rw("acia1", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xa0, 0xa1).rw("acia2", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
}


/* Input ports */
static INPUT_PORTS_START( mx2178 )
INPUT_PORTS_END

MC6845_UPDATE_ROW( mx2178_state::crtc_update_row )
{
	rgb_t const *const pens = m_palette->palette()->entry_list_raw();
	uint32_t *p = &bitmap.pix(y);

	for (uint16_t x = 0; x < x_count; x++)
	{
		uint16_t mem = (ma + x) & 0x7ff;
		uint8_t chr = m_p_videoram[mem];

		/* get pattern of pixels for that character scanline */
		uint8_t gfx = m_p_chargen[(chr<<4) | ra] ^ ((x == cursor_x) ? 0xff : 0);

		/* Display a scanline of a character (8 pixels) */
		*p++ = pens[BIT(gfx, 7)];
		*p++ = pens[BIT(gfx, 6)];
		*p++ = pens[BIT(gfx, 5)];
		*p++ = pens[BIT(gfx, 4)];
		*p++ = pens[BIT(gfx, 3)];
		*p++ = pens[BIT(gfx, 2)];
		*p++ = pens[BIT(gfx, 1)];
		*p++ = pens[BIT(gfx, 0)];
	}
}

/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 16,                  /* 8 x 16 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8 },
	8*16                    /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_mx2178 )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END

void mx2178_state::machine_reset()
{
}

void mx2178_state::mx2178(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(18'869'600) / 5); // guess
	m_maincpu->set_addrmap(AS_PROGRAM, &mx2178_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &mx2178_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // not correct
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	screen.set_size(32*8, 32*8);
	screen.set_visarea(0*8, 32*8-1, 2*8, 30*8-1);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_mx2178);
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	/* Devices */
	mc6845_device &crtc(MC6845(config, "crtc", XTAL(18'869'600) / 8)); // clk unknown
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(8);
	crtc.set_update_row_callback(FUNC(mx2178_state::crtc_update_row));
	crtc.out_vsync_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);

	clock_device &acia_clock(CLOCK(config, "acia_clock", XTAL(18'869'600) / 30));
	acia_clock.signal_handler().set("acia1", FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append("acia1", FUNC(acia6850_device::write_rxc));
	acia_clock.signal_handler().append("acia2", FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append("acia2", FUNC(acia6850_device::write_rxc));

	acia6850_device &acia1(ACIA6850(config, "acia1", 0));
	acia1.txd_handler().set("rs232a", FUNC(rs232_port_device::write_txd));
	acia1.rts_handler().set("rs232a", FUNC(rs232_port_device::write_rts));
	acia1.irq_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set("acia1", FUNC(acia6850_device::write_rxd));
	rs232a.cts_handler().set("acia1", FUNC(acia6850_device::write_cts));

	acia6850_device &acia2(ACIA6850(config, "acia2", 0));
	acia2.txd_handler().set("rs232b", FUNC(rs232_port_device::write_txd));
	acia2.rts_handler().set("rs232b", FUNC(rs232_port_device::write_rts));
	acia2.irq_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, "keyboard"));
	rs232b.rxd_handler().set("acia2", FUNC(acia6850_device::write_rxd));
	rs232b.cts_handler().set("acia2", FUNC(acia6850_device::write_cts));
}

/* ROM definition */
ROM_START( mx2178 )
	ROM_REGION(0x2000, "roms", 0) // MBM2764-25
	ROM_LOAD( "96274139.u9", 0x000000, 0x002000, CRC(eb471a27) SHA1(433abefd1a72653d0bf35bcaaeccf9943b96260b) )

	ROM_REGION(0x800, "proms", 0) // MB7122E - actual mapping not known
	ROMX_LOAD( "96270350.q2", 0x0000, 0x0400, NO_DUMP, ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO | ROM_SKIP(1) )
	ROMX_LOAD( "96270368.r2", 0x0000, 0x0400, NO_DUMP, ROM_NIBBLE | ROM_SHIFT_NIBBLE_HI | ROM_SKIP(1) )
	ROMX_LOAD( "96270376.s2", 0x0001, 0x0400, NO_DUMP, ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO | ROM_SKIP(1) )
	ROMX_LOAD( "96270384.t2", 0x0001, 0x0400, NO_DUMP, ROM_NIBBLE | ROM_SHIFT_NIBBLE_HI | ROM_SKIP(1) )

	ROM_REGION(0x1000, "chargen", 0) // D2732A-3
	ROM_LOAD( "96273883.c7", 0x000000, 0x001000, CRC(8311fadd) SHA1(573bbad23e893ad9374edc929642dc1cba3452d2) )
ROM_END

} // Anonymous namespace

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY    FULLNAME        FLAGS
COMP( 1984, mx2178, 0,      0,      mx2178,  mx2178, mx2178_state, empty_init, "Memorex", "Memorex 2178", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



mx3210.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/************************************************************************************************************

    Skeleton driver for Xyplex MAXserver 3210 Local Router

    PCB for model MX-3210-001:
      _________________________________________________________________________________________________________
     |                                                                    __________________   __________    __|__
     |                                                                    |  AM7990PC/80    |  |HALO TD01|  |     |
     |                                                                    |_________________|               |     |
     |                                                                                     __________       |     |
     |                                                                                     |AM7992BPC|      |_____|
     |                                                                    __________________       Xtal        |
     |                                                                    |  AM7990PC/80    |     20.000 MHz   |
     |                                                                    |_________________|            FUSE  |
     |                                                                    ___________          __________      |
     |                                                                    |_PAL_U66__|         |HALO TD01|   __|__
     |                                                                                                      |     |
     |                                                                                                      |     |
     |                                                                                     __________       |     |
     |                                                                    ___________      |AM7992BPC|      |_____|
     |                                                                    |_PAL_U67__|            Xtal         |
     |                                                            Floppy conn->:::::::::::::::   20.000 MHz    |
     |                                                     _________  Xtal                                     |
     |                                                  FDC37C65CLJ P  ?? ___________   ___________            |
     o<-LED                                                |        |     |_74ALS574_|  |74BCT543NT|           |
     |                                                     |        |                                          |
     o<-LED                                                |________|     ___________   ___________            |
     |                                    ____________                    |_74ALS574_|  |74BCT543NT|           |
     o<-LED                               |MS7201AL-80PC                                                       |
     |                                    |__________|      ___________   ___________   ___________            |
     o<-LED                                                 |_PAL_U40__|  |_74ALS574_|  |74BCT543NT|           |
     |                                                                                                         |
    [ ]<-SWITCH                                             ___________   ___________   ___________            |
     |                                                      |_GAL_U41__|  |_74ALS574_|  |74BCT543NT|           |
LED->o                                                                                                         |
     |      ___________   ___________   ___________   ___________                                              |
LED->o     |74HCT259N_|  |_PAL_U25__|  |_9412H_A__|  |74ALS138N_|                                              |
     |                                                                                                         |
LED->o      ___________   ___________   ___________   ___________    ___________                               |
     |     |_74AS289AN|  |_74AS289AN|  |_74AS804AN|  |74ALS245AN|   |MC74F153N_|                               |
LED->o                                                                                                         |
     |      ___________   _______________             ___________    ___________                               |
LED->o     |_74AS289AN|  |              |            SN74BCT543NT   |MC74F153N_|                               |
     |                   | MC68020RP20E |                                                                      |
     |                   |              |             ___________    ___________   ___________   ___________   |
     |      ___________  |              |            |74ALS245AN|   |MC74F153N_|  |_GAL_U83__|  |_74ALS848N|   |
     |     |74HCT259N_|  |              |                                                                      |
     |                   |______________| ___________  ___________   ___________                               |
LED->o                                   |74ALS138AN| |74HCT259N_|  |MC74F153N_|                               |
     |   ______________                                                                                        |
LED->o  | EPROM U13   |                   ___________  ___________   ___________                               |
     |  |_____________|                  |74ALS138AN| |74ALS573CN|  |MC74F153N_|                               |
LED->o                                                                                                         |
     |      ___________   ___________                  ___________   ___________                               |
LED->o     |74HCT259N_|  |_GAL_U28__|                 |74ALS139N_|  |_SN74F374N|                               |
     |                                                                                                         |
LED->o      ___________   ___________   ___________    ___________   ___________                               |
     |     |__DS1234__|  | ACTEL    |  | CA82C55A |   |_GAL_U49__|  |_PAL_U59__|                               |
     |                   |          |  |          |                                                            |
     |      ___________  |          |  |          |    ___________   ___________                               |
     |     |RTC_72421B|  |          |  |__________|   |_GAL_U50__|  |_GAL_U60__|                               |
     |                   |__________|                                                                          |
     |                                                 ___________   ___________   Xtal                        |
     |                                                |_GAL_U51__|  |_GAL_U61__|  40.000 MHz                   |
     |                                                                                                         |
     |    BATT       _______   Xtal   ________   ___________   ___   ___________    : :                        |
     |              |CA82C52|   ??   |CA82C54|  |_74HCT259N| 93C06N |_74F5074N_|    : :                        |
     |              |_______|        |_______|                                                                 |
     |____________________________________________________________________________                           __|_
                                                                                  |        ________          |   |
                                                                                  |       MC145406P          |___|
                                                                                  |____________________________|
*/

#include "emu.h"

#include "cpu/m68000/m68020.h"

#include "machine/am79c90.h"
#include "machine/upd765.h"

//#include "imagedev/floppy.h"

namespace {

class mx3210_state : public driver_device
{
public:
	mx3210_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_fdc(*this, "fdc")
		//, m_floppy(*this, "floppy")
	{
	}

	void mx3210(machine_config &config);

private:
	required_device<m68020_device> m_maincpu;
	required_device<upd765_family_device> m_fdc;
	//required_device<floppy_connector> m_floppy;
};

static INPUT_PORTS_START(mx3210)
INPUT_PORTS_END

void mx3210_state::mx3210(machine_config &config)
{
	M68020(config, m_maincpu, 40_MHz_XTAL/2);

	WD37C65C(config, m_fdc, 20_MHz_XTAL/2, 20_MHz_XTAL/2); // FDC37C65CLJ-P, unknown clock
	//FLOPPY_CONNECTOR(...)

	AM7990(config, "lance1", 0); // AMD AM7990PC/80
	AM7990(config, "lance2", 0); // AMD AM7990PC/80
}

ROM_START(mx3210)
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD("sc_4315-b.u13",     0x000000, 0x010000, CRC(9b934ca9) SHA1(152240f0d468783f26854bc491fcfad0ced9d9fd))

	ROM_REGION(0x168000, "floppy", 0)
	ROM_LOAD("mx3210_floppy.img", 0x000000, 0x168000, CRC(b215e9d1) SHA1(7b18cc81a7c6f6e44cfc804a7ba6c7fc0c9bbb92))

	ROM_REGION(0x200, "plds", 0)
	ROM_LOAD("atm_4238_b_palce20v8h-25pc-4.u59", 0x000, 0x157, CRC(ddc38476) SHA1(ab4f20c0f15521febb6f66a6272fff0ea63920c5))
	ROM_LOAD("atm_4242_a_palce16v8h-25pc-4.u40", 0x000, 0x117, CRC(c40e26a3) SHA1(c72d88f6218fc292a0680a8dd8782957a768f605))
	ROM_LOAD("m5v_4233_a_gal20v8a.u41",          0x000, 0x157, CRC(d3a0317e) SHA1(e0377fbaf2a802f235540eac8556edbd492e0e5c))
	ROM_LOAD("m5v_4240_b_gal20v8a.u83",          0x000, 0x157, CRC(adfbd1fb) SHA1(b7ad2cac2c205be751d6ffa98ea2de467df5936e))
	ROM_LOAD("m5v_4308_a_tibpal20l8-7.u25",      0x000, 0x144, CRC(217e7c63) SHA1(acdbc4c8bfba99d5170ccc1c7ee87aed35b5b0f8))
	ROM_LOAD("m5y_4236_a_gal16v8b.u50",          0x000, 0x117, CRC(f696922a) SHA1(528051a221d4c2502502e3f92c6d25089e99c8cb))
	ROM_LOAD("sc_4239-c_palce16v8h-15pc-4.u66",  0x000, 0x117, CRC(985c5117) SHA1(cd00458bf1d4c600e9e672ff955049ffc160975e))
	ROM_LOAD("sc_4239-c_palce16v8h-15pc-4.u67",  0x000, 0x117, CRC(985c5117) SHA1(cd00458bf1d4c600e9e672ff955049ffc160975e)) // Same as U66
	ROM_LOAD("sc_4241-a_gal16v8b.u51",           0x000, 0x117, CRC(1fb46b28) SHA1(5540631603960d50f5fb7593f1fd390d8415f8c2))
	ROM_LOAD("sc_4243-a_gal16v8b.u49",           0x000, 0x117, CRC(7e574a22) SHA1(b6d8ddaa490cafccffb8274c2d242725ce59d911))
	ROM_LOAD("sc_4276-a_palce16v8h-25pc-4.u61",  0x000, 0x117, CRC(3ac53a84) SHA1(6611a979803ad537e7c509b9b534b5d94af7e265))
	ROM_LOAD("sc_4318-a_gal16v8b.u28",           0x000, 0x117, CRC(0332f842) SHA1(299ecd64c4e702e5f0d3b0279353ea40995316dd))
	ROM_LOAD("sc_4321-a_gal20v8a.u60",           0x000, 0x157, CRC(2fb25010) SHA1(32d8a07728f2a7e82112279de845a65bc49dad78))
ROM_END

} // anonymous namespace

SYST(1994, mx3210, 0, 0, mx3210, mx3210, mx3210_state, empty_init, "Xyplex Inc.", "MAXserver MX-3210 Local Router", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



myaatari.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: David Haywood

// CPU/SoC is simply marked "3805"

#include "emu.h"

#include "cpu/arm7/arm7.h"

#include "screen.h"
#include "speaker.h"


namespace {

class myaatari_state : public driver_device
{
public:
	myaatari_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
	{ }

	void myaatari(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void arm_map(address_map &map) ATTR_COLD;
};

uint32_t myaatari_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void myaatari_state::machine_start()
{
}

void myaatari_state::machine_reset()
{
	m_maincpu->set_state_int(arm7_cpu_device::ARM7_R15, 0x08000000);
}

static INPUT_PORTS_START( myaatari )
INPUT_PORTS_END


void myaatari_state::arm_map(address_map &map)
{
	map(0x00000000, 0x00ffffff).ram();
	map(0x03000000, 0x03001fff).ram();
	map(0x08000000, 0x08ffffff).rom().region("maincpu", 0);
	map(0x18f00000, 0x18f3ffff).ram();
}

void myaatari_state::myaatari(machine_config &config)
{
	ARM9(config, m_maincpu, 72000000); // unknown ARM core
	m_maincpu->set_addrmap(AS_PROGRAM, &myaatari_state::arm_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320, 262);
	m_screen->set_visarea(0, 320-1, 0, 240-1);
	m_screen->set_screen_update(FUNC(myaatari_state::screen_update));

	SPEAKER(config, "speaker", 2).front();
}

ROM_START( myaatari )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "atariarcade_s29gl128p11tfi02_0001227e.bin", 0x000000, 0x1000000, CRC(c838563c) SHA1(7b3a76d29556f5c30679efcece50e31ae5a5d489) )
	ROM_IGNORE(0x100)
ROM_END

ROM_START( kuniotv )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "s29gl064n90tfi04.u2", 0x000000, 0x0800000, CRC(f26cd4a2) SHA1(92b7af5ecb8b58065cfa39cac77e32242383af78) )
	ROM_IGNORE(0x100)
ROM_END

} // anonymous namespace

CONS( 2021, myaatari,      0,              0,      myaatari, myaatari, myaatari_state, empty_init, "dreamGEAR", "My Arcade Atari (DGUNL-7013, Micro Player Pro)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )

// HDMI stick, runs the Famicom titles using an emulator
CONS( 2021, kuniotv,       0,              0,      myaatari, myaatari, myaatari_state, empty_init, "Lithon", "Kunio-kun TV (5-in-1)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



myb3k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese, Joakim Larsson Edstrom
/***************************************************************************

    Matsushita Mybrain 3000 / Panasonic JB-3000 / Ericsson Step/One

    preliminary driver by Angelo Salese

    Slotified and corrected by Joakim Larsson Edstrom based on
    Step/One service manuals: http://nivelleringslikaren.eu/stepone/

    TODO:
    - Fix proper cursor support
    - Fix Hi-Res 640x400 mode when MC6845 supports interlaced mode
    - Expansion Unit with 6 more ISA8 slots
    - Proper waitstate support when 8088 CPU core admits it and remove the workaround in machine_start

    PC INCOMPATIBILITIES:
    - COM card lives at io address 0x540
    - FDC card lives at io address 0x20
    - DMA channel 0 is not part of ISA8 but implemented on B8 (DREQ0/SRDY)
      and B19 (DACK0/MEMREF)
    - Keyboard is not interfaced through 8255
    - Non standard graphics board

    These and other incompatibilities required many PC softwares to be
    recompiled to work on this computer.

****************************************************************************/
#include "emu.h"
#include "cpu/i86/i86.h"
#include "machine/i8255.h"
#include "machine/myb3k_kbd.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/i8257.h"
#include "sound/spkrdev.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "machine/ram.h"
#include "machine/wd_fdc.h"
#include "bus/isa/isa.h"
#include "bus/isa/myb3k_com.h"
#include "bus/isa/myb3k_fdc.h"
#include "bus/centronics/ctronics.h"
#include "video/mc6845.h"
#include "screen.h"

#define LOG_PPI     (1U << 1)
#define LOG_PIT     (1U << 2)
#define LOG_PIC     (1U << 3)
#define LOG_CRT     (1U << 4)
#define LOG_DMA     (1U << 5)
#define LOG_KBD     (1U << 6)
#define LOG_VMOD    (1U << 7)
#define LOG_PIX     (1U << 8)
#define LOG_M2      (1U << 9)
#define LOG_M3      (1U << 10)
#define LOG_SCRL    (1U << 11)
#define LOG_CENT    (1U << 12)
#define LOG_RAM     (1U << 13)

//#define VERBOSE (LOG_VMOD|LOG_RAM)
//#define LOG_OUTPUT_STREAM std::cout

#include "logmacro.h"

#define LOGPPI(...)  LOGMASKED(LOG_PPI,  __VA_ARGS__)
#define LOGPIT(...)  LOGMASKED(LOG_PIT,  __VA_ARGS__)
#define LOGPIC(...)  LOGMASKED(LOG_PIC,  __VA_ARGS__)
#define LOGCRT(...)  LOGMASKED(LOG_CRT,  __VA_ARGS__)
#define LOGDMA(...)  LOGMASKED(LOG_DMA,  __VA_ARGS__)
#define LOGKBD(...)  LOGMASKED(LOG_KBD,  __VA_ARGS__)
#define LOGVMOD(...) LOGMASKED(LOG_VMOD, __VA_ARGS__)
#define LOGPIX(...)  LOGMASKED(LOG_PIX,  __VA_ARGS__)
#define LOGM2(...)   LOGMASKED(LOG_M2,   __VA_ARGS__)
#define LOGM3(...)   LOGMASKED(LOG_M3,   __VA_ARGS__)
#define LOGSCRL(...) LOGMASKED(LOG_SCRL, __VA_ARGS__)
#define LOGCENT(...) LOGMASKED(LOG_CENT, __VA_ARGS__)
#define LOGRAM(...)  LOGMASKED(LOG_RAM, __VA_ARGS__)


namespace {

class myb3k_state : public driver_device
{
public:
	myb3k_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_pic8259(*this, "pic")
		, m_pit8253(*this, "pit")
		, m_ppi8255(*this, "ppi")
		, m_dma8257(*this, "dma")
		, m_speaker(*this, "speaker")
		, m_kb(*this, "myb3k_keyboard")
		, m_crtc(*this, "crtc")
		, m_vram(*this, "vram")
		, m_isabus(*this, "isa")
		, m_centronics(*this, "centronics")
		, m_io_dsw1(*this, "DSW1")
		, m_io_monitor(*this, "MONITOR")
		, m_io_j4(*this, "J4")
		, m_io_j5(*this, "J5")
		, m_screen(*this, "screen")
	{ }

	void stepone(machine_config &config);
	void jb3000(machine_config &config);
	void myb3k(machine_config &config);

	/* Monitor */
	DECLARE_INPUT_CHANGED_MEMBER(monitor_changed);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void myb3k_io(address_map &map) ATTR_COLD;
	void myb3k_map(address_map &map) ATTR_COLD;

	/* Interrupt controller */
	void pic_int_w(int state);

	/* Parallel port */
	uint8_t ppi_portb_r();
	void ppi_portc_w(uint8_t data);

	/* DMA controller */
	void hrq_w(int state);
	void tc_w(int state);
	void dma_segment_w(uint8_t data);
	uint8_t dma_memory_read_byte(offs_t offset);
	void dma_memory_write_byte(offs_t offset, uint8_t data);
	uint8_t io_dack0_r()  { uint8_t tmp = m_isabus->dack_r(0); LOGDMA("io_dack0_r: %02x\n", tmp); return tmp; }
	uint8_t io_dack1_r()  { uint8_t tmp = m_isabus->dack_r(1); LOGDMA("io_dack1_r: %02x\n", tmp); return tmp; }
	uint8_t io_dack2_r()  { uint8_t tmp = m_isabus->dack_r(2); LOGDMA("io_dack2_r: %02x\n", tmp); return tmp; }
	uint8_t io_dack3_r()  { uint8_t tmp = m_isabus->dack_r(3); LOGDMA("io_dack3_r: %02x\n", tmp); return tmp; }
	void io_dack0_w(uint8_t data) { LOGDMA("io_dack0_w: %02x\n", data); m_isabus->dack_w(0, data); }
	void io_dack1_w(uint8_t data) { LOGDMA("io_dack1_w: %02x\n", data); m_isabus->dack_w(1, data); }
	void io_dack2_w(uint8_t data) { LOGDMA("io_dack2_w: %02x\n", data); m_isabus->dack_w(2, data); }
	void io_dack3_w(uint8_t data) { LOGDMA("io_dack3_w: %02x\n", data); m_isabus->dack_w(3, data); }
	void dack0_w(int state) { LOGDMA("dack0_w: %d\n", state); select_dma_channel(0, state); }
	void dack1_w(int state) { LOGDMA("dack1_w: %d\n", state); select_dma_channel(1, state); }
	void dack2_w(int state) { LOGDMA("dack2_w: %d\n", state); select_dma_channel(2, state); }
	void dack3_w(int state) { LOGDMA("dack3_w: %d\n", state); select_dma_channel(3, state); }

	/* Timer */
	void pit_out1_changed(int state);

	/* Video controller */
	MC6845_UPDATE_ROW(crtc_update_row);

	/* ISA+ Expansion bus */
	void isa_irq5_w(int state);
	void isa_irq7_w(int state);

	/* Centronics  */
	void centronics_ack_w(int state);
	void centronics_busy_w(int state);
	void centronics_perror_w(int state);
	void centronics_select_w(int state);

	/* Keyboard */
	uint8_t myb3k_kbd_r();
	void kbd_set_data_and_interrupt(u8 data);

	/* Video Controller */
	void myb3k_video_mode_w(uint8_t data);

	/* Status bits */
	uint8_t myb3k_io_status_r();

	/* Interrupt Controller */
	void pic_ir5_w(int source, int state);
	void pic_ir7_w(int source, int state);

	/* Jumper fields J4/J5 */
	enum
	{
		PPI_PC3  = 0x01,
		ISA_IRQ5 = 0x02,
		ISA_IRQ7 = 0x04,
		CENT_ACK = 0x08
	};

	/* Paralell port */
	enum
	{
		PC0_STROBE  = 0x01, // Printer interface
		PC1_SETPAGE = 0x02, // Graphics circuit
		PC2_DISPST  = 0x04, // Graphics circuit
		PC3_LPENB   = 0x08, // Lightpen enable
		PC4_CURSR   = 0x10, // Cursor Odd/Even
		PC5_BUZON   = 0x20, // Speaker On/Off
		PC6_CMTWRD  = 0x40,
		PC7_CMTEN   = 0x80  // Cassette or Speaker
	};

	/* DMA controller */
	void select_dma_channel(int channel, bool state);

	/* Timer */
	TIMER_CALLBACK_MEMBER(key_interrupt);

	/* Status bits */
	enum
	{
		IOSTAT_BUSY  = 0x01,
		IOSTAT_NONE  = 0x02,
		IOSTAT_PRPE  = 0x04,
		IOSTAT_FAULT = 0x08
	};

	required_device<cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<pic8259_device> m_pic8259;
	required_device<pit8253_device> m_pit8253;
	required_device<i8255_device> m_ppi8255;
	required_device<i8257_device> m_dma8257;
	required_device<speaker_sound_device>   m_speaker;
	required_device<myb3k_keyboard_device> m_kb;
	required_device<hd6845s_device> m_crtc;
	required_shared_ptr<uint8_t> m_vram;
	required_device<isa8_device> m_isabus;
	optional_device<centronics_device> m_centronics;

	required_ioport m_io_dsw1;
	required_ioport m_io_monitor;
	required_ioport m_io_j4;
	required_ioport m_io_j5;
	required_device<screen_device> m_screen;

	int m_dma_channel;
	bool m_cur_tc;
	uint8_t m_kbd_data; // Data inside the 74LS164 serial to parallel converter.
	uint8_t m_vmode;
	rgb_t (*m_pal)[8];
	rgb_t m_cpal[8];
	rgb_t m_mpal[8];
	uint8_t m_portc;
	uint8_t m_dma_page[4]; // a 74670, 4 x 4 bit storage latch
	int8_t m_io_status;
	emu_timer *m_key_timer = nullptr;
};

uint8_t myb3k_state::myb3k_io_status_r()
{
	LOGCENT("myb3k_io_status_r\n");
	return m_io_status & 0x0f;
}

uint8_t myb3k_state::myb3k_kbd_r()
{
	LOGKBD("myb3k_kbd_r: %02x\n", m_kbd_data);

	/* IN from port 0x04 enables a 74LS244 buffer that
	   presents to the CPU the parallel bits from the 74LS164
	   serial to parallel converter.*/
	m_pic8259->ir1_w(CLEAR_LINE);
	return m_kbd_data;
}

void myb3k_state::kbd_set_data_and_interrupt(u8 data)
{
	LOGKBD("kbd_set_data_and_interrupt: %02x\n", data);
	m_kbd_data = data;

	/* The INT7 line is pulled low when a clock is detected from the keyboard. */
	m_pic8259->ir1_w(CLEAR_LINE);

	/* When the clock stops, the INT7 line goes back high. This triggers the interrupt.
	   We simulate the time it takes to send the 8 bits over the serial line
	   here. It should be 0.8ms but is rounded off to 1ms. */
	m_key_timer->adjust(attotime::from_msec(1));
}

TIMER_CALLBACK_MEMBER(myb3k_state::key_interrupt)
{
	/* The serial transfer of 8 bits is complete. Now trigger INT7. */
	m_pic8259->ir1_w(ASSERT_LINE);
}

MC6845_UPDATE_ROW( myb3k_state::crtc_update_row )
{
	/* The 6845 is not programmed to get 80 character modes or 400 pixels width but this is managed by external circuitry that selects
	   the apropriate pixelclock based on the video mode latch at i/o address 0x04. This callback always get x_count set to 40  */
	uint8_t row_length = (m_vmode & 0x02) || (m_vmode == 0)  ? x_count * 2 : x_count;
	for (int x_pos = 0; x_pos < row_length; x_pos++)
	{
		uint16_t page = (m_portc & PC1_SETPAGE) ? 0x8000 : 0;

		if ((m_portc & PC2_DISPST) == 0)
		{
			for (int pxl = 0; pxl < 8; pxl++)
			{
				bitmap.pix(y, ( x_pos * 8) + pxl) = rgb_t::black();
			}
		}
		else
		{
			switch (m_vmode)
			{
			case 0:
				{
					uint32_t rowstart = (((x_pos + ma * 2) * 16 + ra) & 0x7fff) + page;
					uint32_t pdat  = ((m_vram[rowstart +  0]  & 0xff) << 16); // Green 8 bits
					pdat |= ((m_vram[rowstart +  8] & 0xf0) << 8);  // Red upper 4 bits
					pdat |= ((m_vram[rowstart +  8] & 0x0f) <<  4); // Blue upper 4 bits
					pdat |= ((m_vram[rowstart +  8] & 0xf0) <<  4); // Red lower 4 bits
					pdat |= ((m_vram[rowstart +  8] & 0x0f) <<  0); // Blue lower 4 bits
					for (int pxl = 0; pxl < 8; pxl++)
					{
						uint16_t pind = 0;
						/* Pixeldata for 8 pixels are stored in pdat as GGRRBB  */
						pind = (((pdat & (0x800000 >> pxl)) ? 0x04 : 0x00) |
							((pdat & (0x008000 >> pxl)) ? 0x02 : 0x00) |
							((pdat & (0x000080 >> pxl)) ? 0x01 : 0x00) );

						/* Check if we are in a cursor and create cursor if so */
						//pind ^= ((cursor_x != -1 && x_pos == cursor_x && ra == 7) ? 7 : 0);

						/* Create the grey scale */
						bitmap.pix(y, ( x_pos * 8) + pxl) = (*m_pal)[pind & 0x07];
					}
				}
				break;
			case 1:
				{
					uint32_t pdat;
					// Crude fix of 36CH mode
					if ((m_io_dsw1->read() & 0x0c) == 0 && ra > 7)
					{
						// text mode only, 36x25 char, 8 color, discarding raster rows with what appears to be garbish
						// needs to be implemented LLE to verify the function of that data, possibly related to Kanji support
						pdat = 0x00; // This gets wrong when background color is not black
					}
					else
					{   // 320x200, 40x25 char, 8 color
						uint32_t rowstart = (((x_pos + ma) * 32 + ra) & 0x7fff) + page;
						pdat  = ((m_vram[rowstart +  0]  & 0xff) << 16); // Green 8 bits
						pdat |= ((m_vram[rowstart +  8]  & 0xf0) << 8);  // Red upper 4 bits
						pdat |= ((m_vram[rowstart +  8]  & 0x0f) <<  4); // Blue upper 4 bits
						pdat |= ((m_vram[rowstart +  24] & 0xf0) <<  4); // Red lower 4 bits
						pdat |= ((m_vram[rowstart +  24] & 0x0f) <<  0); // Blue lower 4 bits
						if (pdat != 0)
						{
							LOGPIX(" - PDAT:%06x from offset %04x RA=%d\n", pdat, rowstart + 0, ra);
						}
					}

					for (int pxl = 0; pxl < 8; pxl++)
					{
						uint16_t pind = 0;
						/* Pixeldata for 8 pixels are stored in pdat as GGRRBB  */
						pind = (((pdat & (0x800000 >> pxl)) ? 0x04 : 0x00) |
							((pdat & (0x008000 >> pxl)) ? 0x02 : 0x00) |
							((pdat & (0x000080 >> pxl)) ? 0x01 : 0x00) );

						/* Check if we are in a cursor and create cursor if so */
						pind ^= ((cursor_x != -1 && x_pos == cursor_x && ra == 7) ? 7 : 0);

						/* Pick up the color */
						bitmap.pix(y, ( x_pos * 8) + pxl) = (*m_pal)[pind & 0x07];
					}
				}
				break;

			case 6: /* 640x400, 80 char, white on black -  fall through to case 2 if monochrome monitor
			       Mode 6 is an interlaced mode so should induce flicker on the color monitor but be
			       ok on the monochrome green monitor */
			case 2: /* 640x200, 80 char, white on black */
				{
					uint32_t rowstart = (((x_pos + ma * 2) * 16 + ra) & 0x7fff) + page;
					uint16_t pdat = m_vram[rowstart];
					//if (pdat16 != 0)
					LOGM2(" - PDAT:%06x from offset %04x RA=%d X:%d Y:%d DE:%d HBP:%d VBP:%d\n", pdat, (x_pos * 2 + ma * 2) * 16 + ra + page + 0, ra, x_pos, y, de, hbp, vbp);

					/* Check if we are in a cursor and create cursor if so */
					//pdat ^= ((cursor_x != -1 && x_pos/2 == cursor_x && ra == 7) ? 0xff : 0);

					for (int pxl = 0; pxl < 8; pxl++)
					{
						if ((pdat & (0x80 >> pxl)) != 0)
						{
							bitmap.pix(y, ( x_pos * 8) + pxl) = (*m_pal)[0x07];
						}
						else
						{
							bitmap.pix(y, ( x_pos * 8) + pxl) = rgb_t::black();
						}
					}
				}
				break;
			default:
				logerror("unimplemented video mode: %02x\n", m_vmode);
			}
		}
	}
}

/*
 * Setup of 6845 in different graphics modes from basica command line
 *  screen chars per row 36/40/80, rows 20/25, mode 0/1/2/3
 *
 *  chars 36  40  80  80  36  40  80  36  40
 *  rows  25  25  25  25  20  20  20  25  25
 *  mode   1   1   2   3   0   0   0   0   0 - as per basica "screen x,y,mode"
 *----------------------------------------------------------------------------
 *  R0    55  55  55  55  55  55  55  55  55 - Horizontal Total
 *  R1    40  40  40  40  40  40  40  40  40 - Horizontal Displayed
 *  R2    44  44  44  44  44  44  44  44  44 - Horizontal Sync Position
 *  R3   132 132 132  52 132 132 132  52  52 - Sync Width
 *  R4    31  31  31  26  24  24  24  26  26 - Vertical Total
 *  R5     0   0   0   7   9   9   9   7   7 - Vertical Total Adjust
 *  R6    25  25  25  25  20  20  20  25  25 - Vertical Displayed
 *  R7    27  27  27  25  22  22  22  25  25 - Vertical Sync Position
 *  R8     0   0   0   3   0   0   0   3   3 - Interlace & Skew
 *  R9     7   7   7  14   9   9   9  14  14 - Maximum Raster Address
 *  R10   96  96  96  96  96  96  96  96  96 - Cursor Start Address
 *  R11    7   7   7  15   9   9   9  15  15 - Cursor End Address
 *  R12    0   0   0   0   0   0   0   0   0 - Start Address (H)
 *  R13    0   0   0   0   0   0   0   0   0 - Start Address (L)
 * ---------------------------------------------------------------------------
 *  vmode  1   1   2   6   1   1   2   5   5 - as per 3 bits latch at I/O 0x04
 *  xres 320 320 640 640 320 320 640 320 320
 *  yres 200 200 200 400 200 200 200 400 400
 *  char  40  40  80  80  40  40  80  40  40
 */

void myb3k_state::myb3k_video_mode_w(uint8_t data)
{
	LOG("myb3k_video_mode_w: %02x\n", data);
	LOGVMOD("Video Mode %02x\n", data);

	/* ---- -x-- interlace mode */
	/* ---- --xx horizontal step count (number of offsets of vram RAM data to skip, 64 >> n) */

	m_vmode = data;
	switch (data & 7)
	{
	case 0: // Disambiguity between reality and the service manual. Reality is 640x200 in 8 color or tones!
			{
			LOGVMOD(" - 640x200 on 80x25  \n");
			rectangle rect(0, 640 - 1, 0, 200 - 1);
			m_screen->configure(640, 200, rect, HZ_TO_ATTOSECONDS(50));
			break;
		}

	case 1: /* 320x200 */
		{
			LOGVMOD(" - 320x200, 40 char, 8 color or 8 tones of green...\n");
			rectangle rect(0, 320 - 1, 0, 200 - 1);
			m_screen->configure(320, 200, rect, HZ_TO_ATTOSECONDS(50));
		}
		break;

	case 2: /* 640x200 - boots up in this mode */
		{
			LOGVMOD(" - 640x200, 80 char, white on black...\n");
			rectangle rect(0, 640 - 1, 0, 200 - 1);
			m_screen->configure(640, 200, rect, HZ_TO_ATTOSECONDS(50));
		}
		break;

	case 3: /* Fail  */
		LOGVMOD(" - bad mode...\n");
		break;

	case 4: /* Fail  */
		LOGVMOD(" - bad mode...\n");
		break;

	case 5: /* 320x400 */
		{
			LOGVMOD("320x400, 40 char, white on black\n");
			rectangle rect(0, 320 - 1, 0, 400 - 1);
			m_screen->configure(320, 400, rect, HZ_TO_ATTOSECONDS(50));
		}
		break;

	case 6: /* 640x400 */
		{
			LOGVMOD("640x400, 80 char, white on black\n");
			rectangle rect(0, 640 - 1, 0, 400 - 1);
			m_screen->configure(640, 400, rect, HZ_TO_ATTOSECONDS(50));
		}
		break;

	case 7: /* Fail  */
		LOGVMOD(" - bad mode...\n");
		break;
	default: logerror("Wrong Video Mode %d, contact the maintainer\n", data);
	}
}

/**********************************************************
 *
 * PPI8255 interface
 *
 *
 * PORT A (output)
 *
 *  Printer data lines
 *
 * PORT B (input)
 *
 *  SW1
 *
 * PORT C
 * 0 - PC0 - STROBE       - Printer handshake
 * 1 - PC1 - SET PAGE     - Video RAM page
 * 2 - PC2 - DISP ST      -
 * 3 - PC3 - LPSTB        - Light Pen
 * 4 - PC4 - CURS ODD/EVN -
 * 5 - PC5 - BUZON        - ON-OFF of speaker circuit
 * 6 - PC6 - CMTWD
 * 7 - PC7 - CMTEN        - Separation from cassette interface
 *
 * Mybrain 3000, JB-3000, Step/One SW1: (Service manual numbers switches 1-8)
 * 0   - Unused
 * 1   - Unused
 * 2+3 - Display Mode
 *       OFF OFF - 36 Char/line, 10 raster
 *       ON  OFF - 40 Char/line,  8 raster
 *       OFF ON  - 80 Char/line, 16 raster
 *       ON  ON  - 80 Char/line,  8 raster
 * 4   - Expansion Unit
 *       OFF     - None
 *       ON      - Attached
 * 5+6 - Boot drive
 *       OFF OFF - A:
 *       ON  OFF - B:
 *       OFF ON  - C:
 *       ON  ON  - D:
 * 7   - Boot drive type
 *       OFF     - 5.25 inch Flexible Disk Drive
 *       ON      - 8 inch Flexible Disk Unit
 *
 * Mybrain 3000, JB-3000, Step/One SW2:  (Service manual numbers switches 1-8)
 * 0   - Check Mode (BIOS version info on boot screen)
 *       OFF     - Yes
 *       ON      - No
 * 1-3 - Reserved
 * 4   - Always off (yes so service manual says)
 * 5   - Number of disk drives
 *       OFF     - 2 drives
 *       ON      - 1 drive
 * 6-7   Initial Setting of "basic RS232 Adapter", read by software
 *       OFF OFF - ?
 *       ON  OFF - ?
 *       OFF ON  - ?
 *       ON  ON  - ?
 **********************************************************/
void myb3k_state::myb3k_map(address_map &map)
{
	map.unmap_value_high();
	//map(0x00000,0x3ffff).ram(); // It's either 128Kb or 256Kb RAM installed by machine_start()
	map(0x40000, 0x7ffff).noprw();
	map(0x80000, 0xcffff).noprw(); // Expansion Unit connected through an ISA8 cable
	map(0xd0000, 0xeffff).ram().share("vram");  // Area 6, physical at 30000-3FFFF (128Kb RAM) or 10000-1FFFF (256KB RAM)
	map(0xf0000, 0xfffff).rom().region("ipl", 0); // Area 7, 8 x 8Kb
}

void myb3k_state::myb3k_io(address_map &map)
{
	map.unmap_value_low();
	/* Main Unit 0-0x7ff */
	// 0-3 8255A PPI parallel port
	map(0x00, 0x03).rw(m_ppi8255, FUNC(i8255_device::read), FUNC(i8255_device::write));

	// Discrete latches
	map(0x04, 0x04).r(FUNC(myb3k_state::myb3k_kbd_r));
	map(0x04, 0x04).w(FUNC(myb3k_state::myb3k_video_mode_w)); // b0=40CH, b1=80CH, b2=16 raster
	map(0x05, 0x05).r(FUNC(myb3k_state::myb3k_io_status_r)); // printer connector bits: b3=fault b2=paper out b1 or b0=busy
	map(0x05, 0x05).w(FUNC(myb3k_state::dma_segment_w)); // b0-b3=addr, b6=A b7=B
	map(0x06, 0x06).portr("DSW2");

	// 8-9 8259A interrupt controller
	map(0x08, 0x09).rw(m_pic8259, FUNC(pic8259_device::read), FUNC(pic8259_device::write));

	// c-f 8253 PIT timer
	map(0x0c, 0x0f).rw(m_pit8253, FUNC(pit8253_device::read), FUNC(pit8253_device::write));

	// 10-18 8257 DMA
	map(0x10, 0x18).rw(m_dma8257, FUNC(i8257_device::read), FUNC(i8257_device::write));

	// 1c-1d HD46505S CRTC
	map(0x1c, 0x1c).rw(m_crtc, FUNC(hd6845s_device::status_r), FUNC(hd6845s_device::address_w));
	map(0x1d, 0x1d).rw(m_crtc, FUNC(hd6845s_device::register_r), FUNC(hd6845s_device::register_w));

	/* Expansion Unit 0x800 - 0xfff */
}

/* Input ports - from Step/One service manual */
static INPUT_PORTS_START( myb3k )
	PORT_START("J4")
	PORT_CONFNAME( 0x03, 0x01, "IRQ5 source")
	PORT_CONFSETTING(    0x01, "ISA IRQ5")
	PORT_CONFSETTING(    0x02, "PPI PC3 (Light Pen)")

	PORT_START("J5")
	PORT_CONFNAME( 0x0c, 0x08, "IRQ7 source")
	PORT_CONFSETTING(    0x04, "ISA IRQ7")
	PORT_CONFSETTING(    0x08, "Centronics Ack")

	PORT_START("MONITOR")
	PORT_CONFNAME( 0x01, 0x00, "Monitor") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(myb3k_state::monitor_changed), 0)
	PORT_CONFSETTING(    0x00, "Color")
	PORT_CONFSETTING(    0x01, "Monochrome")

	PORT_START("DSW1")
	PORT_DIPUNUSED_DIPLOC(0x01, 0x01, "SW1:1")
	PORT_DIPUNUSED_DIPLOC(0x02, 0x02, "SW1:2")
	PORT_DIPNAME( 0x0c, 0x00, "Display Mode") PORT_DIPLOCATION("SW1:3,4")
	PORT_DIPSETTING(    0x0c, "80CH 8 raster" )
	PORT_DIPSETTING(    0x04, "80CH 16 raster" )
	PORT_DIPSETTING(    0x08, "40CH 8 raster" )
	PORT_DIPSETTING(    0x00, "36CH 10 raster" )
	PORT_DIPNAME( 0x10, 0x10, "Expansion Unit" ) PORT_DIPLOCATION("SW1:5")
	PORT_DIPSETTING(    0x00, "Attached" )
	PORT_DIPSETTING(    0x10, "None" )
	PORT_DIPNAME( 0x60, 0x60, "Flexible Disk Drive for boot" )  PORT_DIPLOCATION("SW1:6,7")
	PORT_DIPSETTING(    0x60, "Drive A" )
	PORT_DIPSETTING(    0x20, "Drive B" )
	PORT_DIPSETTING(    0x40, "Drive C" )
	PORT_DIPSETTING(    0x00, "Drive D" )
	PORT_DIPNAME( 0x80, 0x80, "Flexible Disk Drive type for boot" )  PORT_DIPLOCATION("SW1:8")
	PORT_DIPSETTING(    0x00, "8-inch Flexible Disk Unit" )     // 0x520-0x524 range
	PORT_DIPSETTING(    0x80, "5.25-inch Flexible Disk Drive" ) // 0x20-0x24 range

	PORT_START("DSW2")
	PORT_DIPNAME( 0x01, 0x00, "Check mode" )  PORT_DIPLOCATION("SW2:1") // ROM information
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPUNUSED_DIPLOC(0x02, 0x02, "SW2:2")
	PORT_DIPUNUSED_DIPLOC(0x04, 0x04, "SW2:3")
	PORT_DIPUNUSED_DIPLOC(0x08, 0x08, "SW2:4")
	PORT_DIPNAME( 0x10, 0x10, "Always off" ) PORT_DIPLOCATION("SW2:5") // Some factory magic maybe?
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, "Number of disk drive(s)" ) PORT_DIPLOCATION("SW2:6")
	PORT_DIPSETTING(    0x20, "2 drives" )
	PORT_DIPSETTING(    0x00, "1 drive" )
	PORT_DIPNAME( 0xc0, 0xc0, "Initial setting of basic RS-232C Adapter" ) PORT_DIPLOCATION("SW2:7,8")
	PORT_DIPSETTING(    0xc0, "00" )
	PORT_DIPSETTING(    0x80, "01" )
	PORT_DIPSETTING(    0x40, "10" )
	PORT_DIPSETTING(    0x00, "11" )
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(myb3k_state::monitor_changed)
{
	if ((m_io_monitor->read() & 1) == 1)
	{
		m_pal = &m_mpal;
	}
	else
	{
		m_pal = &m_cpal;
	}
}

void myb3k_state::machine_start()
{
	LOG("machine_start\n");

	/* Color palette for use with a RGB color CRT monitor such as the 12" Ericsson DU4720:
	   76 degrees deflection, WxHxD: 373x375x428mm, Weight 12.8 Kg, 215x134.4mm display area,
	   RGB signal with separate syncs */
	m_cpal[0] = rgb_t(  0,   0,   0); // black   0.29v
	m_cpal[1] = rgb_t(  0,   0, 255); // blue    0.52v
	m_cpal[2] = rgb_t(255,   0,   0); // red     0.58v
	m_cpal[3] = rgb_t(255,   0, 255); // magenta 0.63v
	m_cpal[4] = rgb_t(  0, 255,   0); // green   0.71v
	m_cpal[5] = rgb_t(  0, 255, 255); // cyan    0.80v
	m_cpal[6] = rgb_t(255, 255,   0); // yellow  0.90v
	m_cpal[7] = rgb_t(255, 255, 255); // white   1.04v

	/* Monochrome offset based on the voltage levels from the Service Manual, relation between colors anc voltage might
	   not be linear though so may need visual tweaking for a Green P39 phospor CRT such as the 12" Ericsson DU4721
	   90 degrees deflection, WxHxD: 373x370x351mm, Weight 9.3 Kg, 215x134.4mm display area, Composite video signal */
	m_mpal[0] = rgb_t((uint8_t)(( (0.29 - 0.29) / (1.04 - 0.29) ) * 74), (uint8_t)(( (0.29 - 0.29) / (1.04 - 0.29) ) * 255), 0); // 0.29v
	m_mpal[1] = rgb_t((uint8_t)(( (0.52 - 0.29) / (1.04 - 0.29) ) * 74), (uint8_t)(( (0.52 - 0.29) / (1.04 - 0.29) ) * 255), 0); // 0.52v
	m_mpal[2] = rgb_t((uint8_t)(( (0.58 - 0.29) / (1.04 - 0.29) ) * 74), (uint8_t)(( (0.58 - 0.29) / (1.04 - 0.29) ) * 255), 0); // 0.58v
	m_mpal[3] = rgb_t((uint8_t)(( (0.63 - 0.29) / (1.04 - 0.29) ) * 74), (uint8_t)(( (0.63 - 0.29) / (1.04 - 0.29) ) * 255), 0); // 0.63v
	m_mpal[4] = rgb_t((uint8_t)(( (0.71 - 0.29) / (1.04 - 0.29) ) * 74), (uint8_t)(( (0.71 - 0.29) / (1.04 - 0.29) ) * 255), 0); // 0.71v
	m_mpal[5] = rgb_t((uint8_t)(( (0.80 - 0.29) / (1.04 - 0.29) ) * 74), (uint8_t)(( (0.80 - 0.29) / (1.04 - 0.29) ) * 255), 0); // 0.80v
	m_mpal[6] = rgb_t((uint8_t)(( (0.90 - 0.29) / (1.04 - 0.29) ) * 74), (uint8_t)(( (0.90 - 0.29) / (1.04 - 0.29) ) * 255), 0); // 0.90v
	m_mpal[7] = rgb_t((uint8_t)(( (1.04 - 0.29) / (1.04 - 0.29) ) * 74), (uint8_t)(( (1.04 - 0.29) / (1.04 - 0.29) ) * 255), 0); // 1.04v

	/* Init a default palette */
	m_pal = &m_mpal; // In case screen starts rendering before machine_reset where we read the settings

	/* CPU can only access RAM 50% of the time and the CRTC the other 50%. This waitstate workaround gives
	   close enough performance of the DOS 1.25 "basica demo" compared to the real hardware */
	m_maincpu->set_clock_scale(0.5f);

	/* Setup ram - NOTE; video RAM is really a mirror of last 64KB of main memory and not the opposite as indicated below. */
	if (m_ram->size() <= (1024 * 128))
	{
		LOGRAM("128KB System\n");
		m_maincpu->space(AS_PROGRAM).install_ram(0, 0xffff, m_ram->pointer()); // Install first 64KB of main memory
		m_maincpu->space(AS_PROGRAM).install_ram(0x10000, 0x1ffff, m_vram);    // Install mirror of video RAM as the last 64Kb of main memory
	}
	else
	{
		LOGRAM("256KB System\n");
		m_maincpu->space(AS_PROGRAM).install_ram(0, 0x2ffff, m_ram->pointer()); // Install 192KB (128KB Storage Module + first 64KB of main memory)
		m_maincpu->space(AS_PROGRAM).install_ram(0x30000, 0x3ffff, m_vram);     // Install mirror of video RAM as the last 64KB of main memory
	}

	/* No key presses allowed yet */
	m_kbd_data = 0;

	save_item(NAME(m_dma_channel));
	save_item(NAME(m_cur_tc));
	save_item(NAME(m_kbd_data));
	save_item(NAME(m_vmode));
	save_item(NAME(m_portc));
	save_item(NAME(*m_pal));
	save_pointer(NAME(m_mpal), sizeof(m_mpal));
	save_pointer(NAME(m_cpal), sizeof(m_cpal));
	save_pointer(NAME(m_dma_page), sizeof(m_dma_page));
	save_item(NAME(m_io_status));

	m_key_timer = timer_alloc(FUNC(myb3k_state::key_interrupt), this);
}

void myb3k_state::machine_reset()
{
	LOG("machine_reset\n");
	m_cur_tc = false;
	m_dma_channel = -1;
	m_vmode = 0;
	m_portc = 0;
	memset(m_dma_page, 0, sizeof(m_dma_page));
	m_pal = (m_io_monitor->read() & 1) ? &m_mpal : &m_cpal;
}

void myb3k_state::select_dma_channel(int channel, bool state)
{
	LOGDMA("select_dma_channel: %d:%d\n", channel, state);
	if (!state)
	{
		m_dma_channel = channel;
		if (!m_cur_tc)
			m_isabus->eop_w(channel, ASSERT_LINE);

	}
	else if (m_dma_channel == channel)
	{
		m_dma_channel = -1;
		if(m_cur_tc)
			m_isabus->eop_w(channel, CLEAR_LINE);
	}
}

void myb3k_state::tc_w(int state)
{
	LOGDMA("tc_w: %d\n", state);
	if (m_dma_channel != -1 && (state == ASSERT_LINE) != m_cur_tc)
		m_isabus->eop_w(m_dma_channel, m_cur_tc ? ASSERT_LINE : CLEAR_LINE);
	m_cur_tc = state == ASSERT_LINE;
}

void myb3k_state::pic_int_w(int state)
{
	LOGPIC("pic_int_w: %d\n", state);
	m_maincpu->set_input_line(0, state);
}

/* pic_ir5_w - select interrupt source depending on jumper J4 setting, either ISA IRQ5 or PPI PC3 (Light Pen) */
void myb3k_state::pic_ir5_w(int source, int state)
{
	LOGPIC("pic_ir5_w: %d\n", state);
	if (!machine().paused() && (source & m_io_j4->read()))
		m_pic8259->ir5_w(state);
}

/* pic_ir7_w - select interrupt source depending on jumper J5 setting, either ISA IRQ7 or Centronics Ack */
void myb3k_state::pic_ir7_w(int source, int state)
{
	LOGPIC("pic_ir7_w: %d\n", state);
	if (!machine().paused() && (source & m_io_j5->read()))
		m_pic8259->ir7_w(state);
}

void myb3k_state::pit_out1_changed(int state)
{
	LOGPIT("pit_out1_changed: %d\n", state);
	m_speaker->level_w(state);
}

void myb3k_state::dma_segment_w(uint8_t data)
{
	LOGDMA("dma_segment_w: %02x\n", data);
	m_dma_page[(data >> 6) & 3] = data & 0x0f;
}

void myb3k_state::hrq_w(int state)
{
	LOGDMA("hrq_w: %d\n", state);

	// Should connect to hold input clocked by DMA clock but hold isn't emulated
	m_maincpu->set_input_line(INPUT_LINE_HALT, state);

	// Should be called from CPU clocked by the DMA clock but hlda output isn't emulated
	m_dma8257->hlda_w(state);
}

uint8_t myb3k_state::dma_memory_read_byte(offs_t offset)
{
	assert(m_dma_channel != -1);

	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	uint8_t tmp = prog_space.read_byte(offset | m_dma_page[m_dma_channel & 3] << 16);
	LOGDMA("dma_memory_read_byte: %x:%04x => %02x\n", m_dma_channel, offset, tmp);

	return tmp;
}

void myb3k_state::dma_memory_write_byte(offs_t offset, uint8_t data)
{
	assert(m_dma_channel != -1);

	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	LOGDMA("dma_memory_write_byte: %x:%04x <= %02x\n", m_dma_channel, offset, data);

	return prog_space.write_byte(offset |  m_dma_page[m_dma_channel & 3] << 16, data);
}

uint8_t myb3k_state::ppi_portb_r()
{
	LOGPPI("ppi_portb_r\n");

	return m_io_dsw1->read();
}

void myb3k_state::ppi_portc_w(uint8_t data)
{
	LOGPPI("ppi_portc_w: %02x\n", data);
	LOGPPI(" - STROBE : %d\n", (data & PC0_STROBE)  ? 1 : 0);
	LOGPPI(" - SETPAGE: %d\n", (data & PC1_SETPAGE) ? 1 : 0);
	LOGPPI(" - DISPST : %d\n", (data & PC2_DISPST)  ? 1 : 0);
	LOGPPI(" - LPENB  : %d\n", (data & PC3_LPENB)   ? 1 : 0);
	LOGPPI(" - CURSR  : %d\n", (data & PC4_CURSR)   ? 1 : 0);
	LOGPPI(" - BUZON  : %d\n", (data & PC5_BUZON)   ? 1 : 0);
	LOGPPI(" - CMTWRD : %d\n", (data & PC6_CMTWRD)  ? 1 : 0);
	LOGPPI(" - CMTEN  : %d\n", (data & PC7_CMTEN)   ? 1 : 0);
	LOGPPI(" => CMTEN: %d BUZON: %d\n", (data & PC7_CMTEN) ? 1 : 0, (data & PC5_BUZON) ? 1 : 0);

	/* Centronics strobe signal */
	LOGCENT("Centronics strobe %d\n", (data & PC0_STROBE) ? 1 : 0);
	m_centronics->write_strobe((data & PC0_STROBE) ? 1 : 0);

	/*
	 * The actual logic around enabling the buzzer is a bit more complicated involving the cassette interface
	 * According to the schematics gate1 is enabled if either
	 *  (CMTEN is inactive high and BUZON active high) OR
	 *  (CMTEN is active   low  and CMTRD is inactive high)
	 * and CMTRD is low). Problem is that the schematics fails to show where CMTRD comes from so only the first case is emulated
	 */
	m_pit8253->write_gate1(!(data & PC5_BUZON) && (data & PC7_CMTEN) ? 1 : 0);

	/* Update light pen interrupt */
	pic_ir5_w(PPI_PC3, (data & PC3_LPENB) ? 1 : 0);

	m_portc = data;

	return;
}

/* ISA IRQ5 handler */
void myb3k_state::isa_irq5_w(int state)
{
	LOGCENT("isa_irq5_w %d\n", state);
	pic_ir5_w(ISA_IRQ5, state);
}

/* ISA IRQ7 handler */
void myb3k_state::isa_irq7_w(int state)
{
	LOGCENT("isa_irq7_w %d\n", state);
	pic_ir7_w(ISA_IRQ7, state);
}

/* Centronics ACK handler */
void myb3k_state::centronics_ack_w(int state)
{
	LOGCENT("centronics_ack_w %d\n", state);
	pic_ir7_w(CENT_ACK, state);
}

/* Centronics BUSY handler
 * The busy line is enterring the schematics from the connector but is lost to its way to the status latch
 * but there is only two possibilities, either D0 or D1  */
void myb3k_state::centronics_busy_w(int state)
{
	LOGCENT("centronics_busy_w %d\n", state);
	if (state == ASSERT_LINE)
		m_io_status &= ~IOSTAT_BUSY;
	else
		m_io_status |= IOSTAT_BUSY;
}

/* Centronics PERROR handler */
void myb3k_state::centronics_perror_w(int state)
{
	LOGCENT("centronics_perror_w %d\n", state);
	if (state == ASSERT_LINE)
		m_io_status &= ~IOSTAT_FAULT;
	else
		m_io_status |= IOSTAT_FAULT;
}

/* Centronics SELECT handler - The centronics select signal is not used by this hardware */
void myb3k_state::centronics_select_w(int state)
{
	LOGCENT("centronics_select_w %d - not used by machine\n", state);
}

static void stepone_isa_cards(device_slot_interface &device)
{
	device.option_add("myb3k_com", ISA8_MYB3K_COM);
	device.option_add("myb3k_fdc4710", ISA8_MYB3K_FDC4710);
	device.option_add("myb3k_fdc4711", ISA8_MYB3K_FDC4711);
	device.option_add("myb3k_fdc4712", ISA8_MYB3K_FDC4712);
}

void myb3k_state::myb3k(machine_config &config)
{
	/* basic machine hardware */
	I8088(config, m_maincpu, XTAL(14'318'181) / 3); /* 14.3182 main crystal divided by three through a 8284A */
	m_maincpu->set_addrmap(AS_PROGRAM, &myb3k_state::myb3k_map);
	m_maincpu->set_addrmap(AS_IO, &myb3k_state::myb3k_io);
	m_maincpu->set_irq_acknowledge_callback("pic", FUNC(pic8259_device::inta_cb));

	/* RAM options */
	RAM(config, RAM_TAG).set_default_size("256K").set_extra_options("128K, 256K");

	/* Interrupt controller */
	PIC8259(config, m_pic8259);
	m_pic8259->out_int_callback().set(FUNC(myb3k_state::pic_int_w));

	/* Parallel port */
	I8255A(config, m_ppi8255);
	m_ppi8255->out_pa_callback().set("cent_data_out", FUNC(output_latch_device::write));
	m_ppi8255->in_pb_callback().set(FUNC(myb3k_state::ppi_portb_r));
	m_ppi8255->out_pc_callback().set(FUNC(myb3k_state::ppi_portc_w));

	/* DMA controller */
	I8257(config, m_dma8257, XTAL(14'318'181) / 6);
	m_dma8257->out_hrq_cb().set(FUNC(myb3k_state::hrq_w));
	m_dma8257->out_tc_cb().set(FUNC(myb3k_state::tc_w));
	m_dma8257->in_memr_cb().set(FUNC(myb3k_state::dma_memory_read_byte));
	m_dma8257->out_memw_cb().set(FUNC(myb3k_state::dma_memory_write_byte));
	m_dma8257->in_ior_cb<0>().set(FUNC(myb3k_state::io_dack0_r));
	m_dma8257->in_ior_cb<1>().set(FUNC(myb3k_state::io_dack1_r));
	m_dma8257->in_ior_cb<2>().set(FUNC(myb3k_state::io_dack2_r));
	m_dma8257->in_ior_cb<3>().set(FUNC(myb3k_state::io_dack3_r));
	m_dma8257->out_iow_cb<0>().set(FUNC(myb3k_state::io_dack0_w));
	m_dma8257->out_iow_cb<1>().set(FUNC(myb3k_state::io_dack1_w));
	m_dma8257->out_iow_cb<2>().set(FUNC(myb3k_state::io_dack2_w));
	m_dma8257->out_iow_cb<3>().set(FUNC(myb3k_state::io_dack3_w));
	m_dma8257->out_dack_cb<0>().set(FUNC(myb3k_state::dack0_w));
	m_dma8257->out_dack_cb<1>().set(FUNC(myb3k_state::dack1_w));
	m_dma8257->out_dack_cb<2>().set(FUNC(myb3k_state::dack2_w));
	m_dma8257->out_dack_cb<3>().set(FUNC(myb3k_state::dack3_w));

	/* Timer */
	PIT8253(config, m_pit8253);
	m_pit8253->set_clk<0>(XTAL(14'318'181) / 12.0); /* TIMINT straight into IRQ0 */
	m_pit8253->out_handler<0>().set(m_pic8259, FUNC(pic8259_device::ir0_w));
	m_pit8253->set_clk<1>(XTAL(14'318'181) / 12.0); /* speaker if port c bit 5 is low */
	m_pit8253->out_handler<1>().set(FUNC(myb3k_state::pit_out1_changed));
	// m_pit8253->set_clk<2>(XTAL(14'318'181) / 12.0); /* ANDed with port c bit 6 but marked as "not use"*/
	// m_pit8253->out_handler<2>().set(FUNC(myb3k_state::pit_out2_changed));

	/* Video controller */
	HD6845S(config, m_crtc, XTAL(14'318'181) / 16); /* Main crystal divided by 16 through a 74163 4 bit counter */
	m_crtc->set_screen(m_screen);
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(myb3k_state::crtc_update_row));

	/* ISA8+ Expansion bus */
	ISA8(config, m_isabus, 0);
	m_isabus->set_memspace("maincpu", AS_PROGRAM);
	m_isabus->set_iospace("maincpu", AS_IO);
	m_isabus->irq2_callback().set(m_pic8259, FUNC(pic8259_device::ir2_w));
	m_isabus->irq3_callback().set(m_pic8259, FUNC(pic8259_device::ir3_w));
	m_isabus->irq4_callback().set(m_pic8259, FUNC(pic8259_device::ir4_w));
	m_isabus->irq5_callback().set(FUNC(myb3k_state::isa_irq5_w)); // Jumper J4 selectable
	m_isabus->irq6_callback().set(m_pic8259, FUNC(pic8259_device::ir6_w));
	m_isabus->irq7_callback().set(FUNC(myb3k_state::isa_irq7_w)); // Jumper J5 selectable
	//m_isabus->drq0_callback().set("dma", FUNC(i8257_device::dreq0_w)); // Part of ISA16 but not ISA8 standard but implemented on ISA8 B8 (SRDY) on this motherboard
	m_isabus->drq1_callback().set("dma", FUNC(i8257_device::dreq1_w));
	m_isabus->drq2_callback().set("dma", FUNC(i8257_device::dreq2_w));
	m_isabus->drq3_callback().set("dma", FUNC(i8257_device::dreq3_w));

	ISA8_SLOT(config, "isa1", 0, m_isabus, stepone_isa_cards, "myb3k_fdc4711", false); // FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa2", 0, m_isabus, stepone_isa_cards, "myb3k_com", false);
	ISA8_SLOT(config, "isa3", 0, m_isabus, stepone_isa_cards, nullptr, false);

	/* Centronics */

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set(FUNC(myb3k_state::centronics_ack_w));
	m_centronics->busy_handler().set(FUNC(myb3k_state::centronics_busy_w));
	m_centronics->perror_handler().set(FUNC(myb3k_state::centronics_perror_w));
	m_centronics->select_handler().set(FUNC(myb3k_state::centronics_select_w));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	/* Sound */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);

	/* Keyboard */
	MYB3K_KEYBOARD(config, m_kb, 0);
	m_kb->set_keyboard_callback(FUNC(myb3k_state::kbd_set_data_and_interrupt));

	/* Monitor */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(XTAL(14'318'181) / 3, 600, 0, 600, 400, 0, 400);
	m_screen->set_screen_update("crtc", FUNC(hd6845s_device::screen_update));
}

void myb3k_state::jb3000(machine_config &config)
{
	myb3k(config);
	/* Keyboard */
	JB3000_KEYBOARD(config.replace(), m_kb, 0);
	m_kb->set_keyboard_callback(FUNC(myb3k_state::kbd_set_data_and_interrupt));

	/* software lists */
	SOFTWARE_LIST(config, "flop_list").set_original("jb3000_flop");
}

void myb3k_state::stepone(machine_config &config)
{
	myb3k(config);
	/* Keyboard */
	STEPONE_KEYBOARD(config.replace(), m_kb, 0);
	m_kb->set_keyboard_callback(FUNC(myb3k_state::kbd_set_data_and_interrupt));

	/* software lists */
	SOFTWARE_LIST(config, "stepone_flop_list").set_original("stepone_flop");
}

/* ROM definitions, ROM area is 8 x 8Kb and can be populated with 2732 mask ROMs or 2764s */
ROM_START( myb3k )
	ROM_REGION( 0x10000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "myb3kchrg-v2.07.bin", 0xc000, 0x2000, CRC(1e43e1e3) SHA1(956b580c9cbcaf2c5ff74e3ef80a5ac98c2df434))
	ROM_LOAD( "myb3kbios-v2.07.bin", 0xe000, 0x2000, CRC(c4c46cc5) SHA1(a3e186513fbe9ad0e369b481999393a3506db39e))
ROM_END

ROM_START( jb3000 )
	ROM_REGION( 0x10000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "jb3000chrg-v2.07.bin", 0xc000, 0x2000, CRC(efffe4cb) SHA1(1305d1fb0bc39b6464f4e2f000a584f9e67f784a))
	ROM_LOAD( "jb3000bios-v2.07.bin", 0xe000, 0x2000, CRC(c4c46cc5) SHA1(a3e186513fbe9ad0e369b481999393a3506db39e)) // Verified to be identical to the original myb3k BIOS
ROM_END

ROM_START( stepone )
	ROM_REGION( 0x10000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "steponechrg-v2.07.bin", 0xc000, 0x2000, CRC(8284a391) SHA1(7203c5e9d83be37c1c195946fbee5c53b1bce391))
	ROM_LOAD( "steponebios-v2.07.bin", 0xe000, 0x2000, CRC(322c1618) SHA1(a7a3cc2af7cc9556007d98014714ba656f6e79d1))
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY       FULLNAME        FLAGS
COMP( 1982, myb3k,   0,      0,      myb3k,   myb3k, myb3k_state, empty_init, "Matsushita", "MyBrain 3000", 0)
COMP( 1982, jb3000,  myb3k,  0,      jb3000,  myb3k, myb3k_state, empty_init, "Panasonic",  "JB-3000",      0)
COMP( 1984, stepone, myb3k,  0,      stepone, myb3k, myb3k_state, empty_init, "Ericsson",   "Step/One",     0)



mycom.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese, Robbbert
/******************************************************************************

    MYCOMZ-80A (c) 1981 Japan Electronics College

    preliminary driver by Angelo Salese
    additions by Robbbert

    THE KEYBOARD
    - Uses a 4MO1003A custom mcu.
    - Every combination is documented in the manual, including the unused ones.
    - All commands must be in uppercase - lowercase will produce errors.
    - There are 5 special keys which do crude editing functions. These are in
      the range 0x61 to 0x75. Kana characters occupy the range 0xa0 to 0xff.
      Graphics characters are found in 0x00 to 0x1f, and 0x80 to 0x9f.
    - Editing characters (hold down shift to get them):
        a - shiftlock (toggle). You can then enter any lowercase character.
        c - clear screen and home cursor
        d - insert
        f - vertical tab (cursor up) You can scroll backwards with this,
            and you can reuse old input lines.
        u - cursor right
    - There are switches on the right-hand side which are connected directly
      to one of the PIAs. The switches (not emulated):
        s2 - ?unknown
        s3 - ?unknown but must be high for the keyboard to function
        s4 - cassette motor on/off
        s5 - ?unknown
    - There is also switch s1 which it is not known what it connects to.
    - Please note: The Takeda 80-column monitor expects the enter key to be the
      line feed key. This is the numpad-enter in our emulation. Strangely, the
      standard monitor, and Basic, will also respond to this key.
    - The keyboard has a "English" key on the left, and a "Japan" key on the
      right. Pressing the appropriate key toggles the input language mode.
      Internally, this turns the Kana bit off/on. On our keyboard, the ALT key
      toggles between English and Kana.

    TODO/info:
    - FDC, little info, guessing (143kb, single sided, 525sd)
    - Cassette doesn't load
    - Printer
    - Keyboard lookup table for Kana and Shifted Kana
    - Keyboard autorepeat
    - Need software

    Basic:
    - To enter Basic, type BASIC. To quit, type EXIT.

    Cassette:
    - BIOS 0: you can SAVE and LOAD from the monitor, but not from Basic. (see ToDo)
    - BIOS 1: Doesn't seem to be supported.

    Sound:
    - BIOS 0: Sound is initialised with the volume turned off. In Basic, you
              can POKE 4382,144 to enable sound.
    - BIOS 1: Doesn't appear to support sound. The included Basic has a SOUND
              command (e.g SOUND 127,80), but no sound is heard.

*******************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "machine/i8255.h"
#include "machine/msm5832.h"
#include "machine/timer.h"
#include "machine/wd_fdc.h"
#include "sound/sn76496.h"
#include "video/mc6845.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class mycom_state : public driver_device
{
public:
	mycom_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_bank1(*this, "bank1")
		, m_bank2(*this, "bank2")
		, m_ppi0(*this, "ppi8255_0")
		, m_ppi1(*this, "ppi8255_1")
		, m_ppi2(*this, "ppi8255_2")
		, m_cass(*this, "cassette")
		, m_crtc(*this, "crtc")
		, m_fdc(*this, "fdc")
		, m_floppy0(*this, "fdc:0")
		, m_floppy1(*this, "fdc:1")
		, m_audio(*this, "sn1")
		, m_rtc(*this, "rtc")
		, m_palette(*this, "palette")
		, m_p_chargen(*this, "chargen")
		, m_keyboard(*this, "X%d", 0U)
	{ }

	void mycom(machine_config &config);

private:
	u8 vram_data_r();
	void vram_data_w(u8 data);
	void port00_w(u8 data);
	void port04_w(u8 data);
	void port06_w(u8 data);
	void port0a_w(u8 data);
	u8 port05_r();
	u8 port06_r();
	u8 port08_r();
	TIMER_DEVICE_CALLBACK_MEMBER(mycom_kbd);
	void mycom_rtc_w(u8 data);
	MC6845_UPDATE_ROW(crtc_update_row);

	void mycom_io(address_map &map) ATTR_COLD;
	void mycom_map(address_map &map) ATTR_COLD;

	std::unique_ptr<u8[]> m_vram;
	u8 m_port0a = 0U;
	u8 m_keyb_press = 0U;
	u8 m_sn_we = 0U;
	u16 m_i_videoram = 0U;
	bool m_keyb_press_flag = false;
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_memory_bank    m_bank1;
	required_memory_bank    m_bank2;
	required_device<i8255_device> m_ppi0;
	required_device<i8255_device> m_ppi1;
	required_device<i8255_device> m_ppi2;
	required_device<cassette_image_device> m_cass;
	required_device<mc6845_device> m_crtc;
	required_device<fd1771_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	required_device<floppy_connector> m_floppy1;
	required_device<sn76489_device> m_audio;
	required_device<msm5832_device> m_rtc;
	required_device<palette_device> m_palette;
	required_region_ptr<u8> m_p_chargen;
	required_ioport_array<9> m_keyboard;
};



MC6845_UPDATE_ROW( mycom_state::crtc_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u32 *p = &bitmap.pix(y);

	if (m_port0a & 0x40)
	{
		for (u16 x = 0; x < x_count; x++)                   // lores pixels
		{
			u8 dbit = (x == cursor_x) ? 0 : 1;
			u16 mem = (ma + x) & 0x7ff;
			u8 chr = m_vram[mem];
			u8 z = ra / 3;
			*p++ = palette[BIT( chr, z ) ? dbit: dbit^1];
			*p++ = palette[BIT( chr, z ) ? dbit: dbit^1];
			*p++ = palette[BIT( chr, z ) ? dbit: dbit^1];
			*p++ = palette[BIT( chr, z ) ? dbit: dbit^1];
			z += 4;
			*p++ = palette[BIT( chr, z ) ? dbit: dbit^1];
			*p++ = palette[BIT( chr, z ) ? dbit: dbit^1];
			*p++ = palette[BIT( chr, z ) ? dbit: dbit^1];
			*p++ = palette[BIT( chr, z ) ? dbit: dbit^1];
		}
	}
	else
	{
		for (u16 x = 0; x < x_count; x++)                   // text
		{
			u8 inv = (x == cursor_x) ? 0xff : 0;
			u16 mem = (ma + x) & 0x7ff;
			u8 gfx;
			if (ra > 7)
				gfx = inv;  // some blank spacing lines
			else
			{
				u8 chr = m_vram[mem];
				gfx = m_p_chargen[(chr<<3) | ra] ^ inv;
			}

			/* Display a scanline of a character */
			*p++ = palette[BIT(gfx, 7)];
			*p++ = palette[BIT(gfx, 6)];
			*p++ = palette[BIT(gfx, 5)];
			*p++ = palette[BIT(gfx, 4)];
			*p++ = palette[BIT(gfx, 3)];
			*p++ = palette[BIT(gfx, 2)];
			*p++ = palette[BIT(gfx, 1)];
			*p++ = palette[BIT(gfx, 0)];
		}
	}
}

void mycom_state::port00_w(u8 data)
{
	switch(data)
	{
		case 0x00: m_bank1->set_entry(1); break;   // rom
		case 0x01: m_bank1->set_entry(0); break;   // ram
		case 0x02: m_bank2->set_entry(1); break;   // rom
		case 0x03: m_bank2->set_entry(0); break;   // ram
	}
}

u8 mycom_state::vram_data_r()
{
	return m_vram[m_i_videoram];
}

void mycom_state::vram_data_w(u8 data)
{
	m_vram[m_i_videoram] = data;
}

void mycom_state::mycom_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).ram().share("mainram");
	map(0x0000, 0x0fff).bankr("bank1");
	map(0xc000, 0xffff).bankr("bank2");
}

void mycom_state::mycom_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x00).w(FUNC(mycom_state::port00_w));
	map(0x01, 0x01).rw(FUNC(mycom_state::vram_data_r), FUNC(mycom_state::vram_data_w));
	map(0x02, 0x02).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x03, 0x03).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x04, 0x07).rw(m_ppi0, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x08, 0x0b).rw(m_ppi1, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x0c, 0x0f).rw(m_ppi2, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x10, 0x13).rw(m_fdc, FUNC(fd1771_device::read), FUNC(fd1771_device::write));
}

/* Input ports */
static INPUT_PORTS_START( mycom )
	PORT_START("X0")
	PORT_BIT(0x001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("ESC") PORT_CHAR(27)
	PORT_BIT(0x100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)

	PORT_START("X1")
	PORT_BIT(0x002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1 !") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2 \"") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3 #") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("4 $") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("5 %") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("6 &") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("7 '") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("8 (") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("9 )") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')

	PORT_START("X2")
	PORT_BIT(0x002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q') PORT_CHAR(17)
	PORT_BIT(0x004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w') PORT_CHAR(23)
	PORT_BIT(0x008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e') PORT_CHAR(5)
	PORT_BIT(0x010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r') PORT_CHAR(18)
	PORT_BIT(0x020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t') PORT_CHAR(20)
	PORT_BIT(0x040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y') PORT_CHAR(25)
	PORT_BIT(0x080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u') PORT_CHAR(21)
	PORT_BIT(0x100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i') PORT_CHAR(9)
	PORT_BIT(0x200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o') PORT_CHAR(15)

	PORT_START("X3")
	PORT_BIT(0x002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a') PORT_CHAR(1)
	PORT_BIT(0x004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s') PORT_CHAR(19)
	PORT_BIT(0x008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d') PORT_CHAR(4)
	PORT_BIT(0x010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f') PORT_CHAR(6)
	PORT_BIT(0x020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g') PORT_CHAR(7)
	PORT_BIT(0x040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h') PORT_CHAR(8)
	PORT_BIT(0x080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j') PORT_CHAR(10)
	PORT_BIT(0x100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k') PORT_CHAR(11)
	PORT_BIT(0x200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l') PORT_CHAR(12)

	PORT_START("X4")
	PORT_BIT(0x001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT(0x002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z') PORT_CHAR(26)
	PORT_BIT(0x004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x') PORT_CHAR(24)
	PORT_BIT(0x008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c') PORT_CHAR(3)
	PORT_BIT(0x010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v') PORT_CHAR(22)
	PORT_BIT(0x020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b') PORT_CHAR(2)
	PORT_BIT(0x040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n') PORT_CHAR(14)
	PORT_BIT(0x080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m') PORT_CHAR(13)
	PORT_BIT(0x100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("< ,") PORT_CODE(KEYCODE_COMMA) PORT_CHAR('<') PORT_CHAR(',')
	PORT_BIT(0x200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("> .") PORT_CODE(KEYCODE_STOP) PORT_CHAR('>') PORT_CHAR('.')

	PORT_START("X5")
	PORT_BIT(0x020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("(u)") PORT_CHAR('u')
	PORT_BIT(0x200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("(c)") PORT_CHAR('c')

	PORT_START("X6")
	PORT_BIT(0x001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("- =") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("^ ~") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') PORT_CHAR('~') PORT_CHAR(30)
	PORT_BIT(0x008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\\ _") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('_') PORT_CHAR(28)
	PORT_BIT(0x020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("(d)") PORT_CHAR('d')
	PORT_BIT(0x200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("(f)") PORT_CHAR('f')

	PORT_START("X7")
	PORT_BIT(0x001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p') PORT_CHAR(16)
	PORT_BIT(0x002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("@ `") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@') PORT_CHAR('`') PORT_CHAR(0)
	PORT_BIT(0x004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{') PORT_CHAR(27)
	PORT_BIT(0x008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("LineFeed") PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(10)
	PORT_BIT(0x010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("(a)") PORT_CHAR('a')

	PORT_START("X8")
	PORT_BIT(0x001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("; +") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(": *") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}') PORT_CHAR(29)
	PORT_BIT(0x020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("XX")
	PORT_BIT(0x001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("KANA") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT) PORT_TOGGLE
INPUT_PORTS_END


/* F4 Character Displayer */
static const gfx_layout mycom_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_mycom )
	GFXDECODE_ENTRY( "chargen", 0x0000, mycom_charlayout, 0, 1 )
GFXDECODE_END

void mycom_state::port04_w(u8 data)
{
	m_i_videoram = (m_i_videoram & 0x700) | data;

	m_sn_we = data;
}

void mycom_state::port06_w(u8 data)
{
	m_i_videoram = (m_i_videoram & 0x0ff) | ((data & 0x007) << 8);
}

u8 mycom_state::port08_r()
{
	/*
	x--- ---- display flag
	---- --x- keyboard shift
	---- ---x keyboard strobe
	*/
	u8 data = 0;

	data = m_keyb_press_flag; //~m_keyb_press_flag;

	if ((m_cass)->input() > 0.03) // not working
		data+=4;

	return data;
}

u8 mycom_state::port06_r()
{
	/*
	x--- ---- keyboard s5
	-x-- ---- keyboard s4 (motor on/off)
	--x- ---- keyboard s3 (must be high)
	---x ---- keyboard s2
	*/
	return 0xff;
}

u8 mycom_state::port05_r()
{
	return m_keyb_press;
}

void mycom_state::port0a_w(u8 data)
{
	/*
	x--- ---- width 80/40 (0 = 80, 1 = 40)
	-x-- ---- video mode (0= tile, 1 = bitmap)
	--x- ---- PSG Chip Select bit
	---x ---- PSG Write Enable bit
	---- x--- cmt remote (defaults to on)
	---- -x-- cmt output
	---- --x- printer reset
	---- ---x printer strobe
	*/

	if ( (BIT(m_port0a, 3)) != (BIT(data, 3)) )
		m_cass->change_state(
		BIT(data,3) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);

	if (BIT(data, 3)) // motor on
		m_cass->output( BIT(data, 2) ? -1.0 : +1.0);

	if ( (BIT(data, 7)) != (BIT(m_port0a, 7)) )
		m_crtc->set_unscaled_clock(BIT(data, 7) ? 1008000 : 2016000);

	m_port0a = data;

	// if WE & CE are low, pass sound command to audio chip
	if ((data & 0x30)==0)
		m_audio->write(m_sn_we);
}

void mycom_state::mycom_rtc_w(u8 data)
{
	m_rtc->address_w(data & 0x0f);

	m_rtc->hold_w(BIT(data, 4));
	m_rtc->read_w(BIT(data, 5));
	m_rtc->write_w(BIT(data, 6));
	m_rtc->cs_w(BIT(data, 7));
}

static const u8 mycom_keyval[] = { 0,
0x1b,0x1b,0x7c,0x7c,0x18,0x18,0x0f,0x0f,0x09,0x09,0x1c,0x1c,0x30,0x00,0x50,0x70,0x3b,0x2b,
0x00,0x00,0x31,0x21,0x51,0x71,0x41,0x61,0x5a,0x7a,0x17,0x17,0x2d,0x3d,0x40,0x60,0x3a,0x2a,
0x0b,0x0b,0x32,0x22,0x57,0x77,0x53,0x73,0x58,0x78,0x03,0x03,0x5e,0x7e,0x5b,0x7b,0x5d,0x7d,
0x1f,0x1f,0x33,0x23,0x45,0x65,0x44,0x64,0x43,0x63,0x0c,0x0c,0x5c,0x7c,0x0a,0x0a,0x00,0x00,
0x01,0x01,0x34,0x24,0x52,0x72,0x46,0x66,0x56,0x76,0x04,0x04,0x7f,0x7f,0x08,0x08,0x0e,0x0e,
0x02,0x02,0x35,0x25,0x54,0x74,0x47,0x67,0x42,0x62,0x75,0x75,0x64,0x64,0x00,0x00,0x20,0x20,
0x1e,0x1e,0x36,0x26,0x59,0x79,0x48,0x68,0x4e,0x6e,0x37,0x37,0x34,0x34,0x31,0x31,0x30,0x30,
0x1d,0x1d,0x37,0x27,0x55,0x75,0x4a,0x6a,0x4d,0x6d,0x38,0x38,0x35,0x35,0x32,0x32,0x11,0x11,
0x0d,0x0d,0x38,0x28,0x49,0x69,0x4b,0x6b,0x2c,0x3c,0x39,0x39,0x36,0x36,0x33,0x33,0x12,0x12,
0x07,0x07,0x39,0x29,0x4f,0x6f,0x4c,0x6c,0x2e,0x3e,0x63,0x63,0x66,0x66,0x61,0x61,0x2f,0x3f };

TIMER_DEVICE_CALLBACK_MEMBER(mycom_state::mycom_kbd)
{
	u8 modifiers = ioport("XX")->read();
	u8 shift_pressed = (modifiers & 2) >> 1;
	m_keyb_press_flag = false;

	// Read keyboard
	for (u8 x = 0; x < 9; x++)
	{
		u16 pressed = m_keyboard[x]->read();
		if (pressed)
		{
			// get scankey value
			for (u8 y = 0; y < 10; y++)
			{
				if (BIT(pressed, y))
				{
					u8 scancode = ((x + y * 9) << 1) + shift_pressed + 1;
					m_keyb_press_flag = true;
					m_keyb_press = mycom_keyval[scancode];
				}
			}
		}
	}

	if (m_keyb_press_flag)
	{
		if (modifiers & 1) m_keyb_press &= 0xbf;
		if (modifiers & 4) m_keyb_press |= 0x80;
	}
}



static void mycom_floppies(device_slot_interface &device)
{
	device.option_add("525sd", FLOPPY_525_SD);
}

void mycom_state::machine_start()
{
	m_vram = make_unique_clear<u8[]>(0x0800);
	save_pointer(NAME(m_vram), 0x0800);
	m_bank1->configure_entry(0, m_ram);
	m_bank1->configure_entry(1, m_rom);
	m_bank2->configure_entry(0, m_ram+0xc000);
	m_bank2->configure_entry(1, m_rom);
	save_item(NAME(m_port0a));
	save_item(NAME(m_i_videoram));
	save_item(NAME(m_keyb_press));
	save_item(NAME(m_keyb_press_flag));
	save_item(NAME(m_sn_we));
}

void mycom_state::machine_reset()
{
	m_bank1->set_entry(1);
	m_bank2->set_entry(1);
	m_port0a = 0;
}

void mycom_state::mycom(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 10_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &mycom_state::mycom_map);
	m_maincpu->set_addrmap(AS_IO, &mycom_state::mycom_io);

	I8255(config, m_ppi0);
	m_ppi0->out_pa_callback().set(FUNC(mycom_state::port04_w));
	m_ppi0->in_pb_callback().set(FUNC(mycom_state::port05_r));
	m_ppi0->in_pc_callback().set(FUNC(mycom_state::port06_r));
	m_ppi0->out_pc_callback().set(FUNC(mycom_state::port06_w));

	I8255(config, m_ppi1);
	m_ppi1->in_pa_callback().set(FUNC(mycom_state::port08_r));
	m_ppi1->out_pc_callback().set(FUNC(mycom_state::port0a_w));

	I8255(config, m_ppi2);
	m_ppi2->in_pb_callback().set(m_rtc, FUNC(msm5832_device::data_r));
	m_ppi2->out_pb_callback().set(m_rtc, FUNC(msm5832_device::data_w));
	m_ppi2->out_pc_callback().set(FUNC(mycom_state::mycom_rtc_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	screen.set_size(640, 480);
	screen.set_visarea(0, 320-1, 0, 192-1);
	PALETTE(config, m_palette, palette_device::MONOCHROME);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_mycom);

	/* Manual states clock is 1.008mhz for 40 cols, and 2.016 mhz for 80 cols.
	The manual states the CRTC is a HD46505S (apparently same as HD6845S). The start registers need to be readable. */
	HD6845S(config, m_crtc, 1008000);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(mycom_state::crtc_update_row));

	SPEAKER(config, "mono").front_center();
	SN76489(config, m_audio, 10_MHz_XTAL / 4).add_route(ALL_OUTPUTS, "mono", 1.50);

	/* Devices */
	MSM5832(config, m_rtc, 32.768_kHz_XTAL);

	CASSETTE(config, m_cass);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	FD1771(config, m_fdc, 16_MHz_XTAL / 16);
	FLOPPY_CONNECTOR(config, "fdc:0", mycom_floppies, "525sd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", mycom_floppies, "525sd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	TIMER(config, "keyboard_timer").configure_periodic(FUNC(mycom_state::mycom_kbd), attotime::from_hz(20));
}

/* ROM definition */
ROM_START( mycom )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_DEFAULT_BIOS("mycom")
	ROM_SYSTEM_BIOS(0, "mycom", "40 column")
	ROMX_LOAD("bios0.rom", 0x0000, 0x3000, CRC(e6f50355) SHA1(5d3acea360c0a8ab547db03a43e1bae5125f9c2a), ROM_BIOS(0))
	ROMX_LOAD("basic0.rom",0x3000, 0x1000, CRC(3b077465) SHA1(777427182627f371542c5e0521ed3ca1466a90e1), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "takeda", "80 column")
	ROMX_LOAD("bios1.rom", 0x0000, 0x3000, CRC(c51d7fcb) SHA1(31d39db43b77cca4d49ff9814d531e056924e716), ROM_BIOS(1))
	ROMX_LOAD("basic1.rom",0x3000, 0x1000, CRC(30a573f1) SHA1(e3fe2e73644e831b52e2789dc7c181989cc30b82), ROM_BIOS(1))
	/* Takeda bios has no cursor. Use the next lines to turn on cursor, but you must comment out when done. */
	//ROM_FILL( 0x1eb6, 1, 0x47 )
	//ROM_FILL( 0x1eb7, 1, 0x07 )

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "font.rom", 0x0000, 0x0800, CRC(4039bb6f) SHA1(086ad303bf4bcf983fd6472577acbf744875fea8) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY                      FULLNAME      FLAGS
COMP( 1981, mycom, 0,      0,      mycom,   mycom, mycom_state, empty_init, "Japan Electronics College", "MYCOMZ-80A", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



myvision.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/***************************************************************************

    Nichibutsu My Vision
      driver by Wilbert Pol

        2013/12/01 Skeleton driver.
        2013/12/02 Working driver.

    Known issues:
    - The inputs sometimes feel a bit unresponsive. Was the real unit like
      that? Or is it just because we have incorrect clocks?

    TODO:
    - Review software list
    - Add clickable artwork
    - Verify sound chip model
    - Verify exact TMS9918 model
    - Verify clock crystal(s)
    - Verify size of vram

****************************************************************************/


#include "emu.h"

#include "cpu/z80/z80.h"
#include "sound/ay8910.h"
#include "video/tms9928a.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "softlist_dev.h"
#include "speaker.h"


namespace {

class myvision_state : public driver_device
{
public:
	myvision_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cart(*this, "cartslot")
		, m_io_row0(*this, "ROW0")
		, m_io_row1(*this, "ROW1")
		, m_io_row2(*this, "ROW2")
		, m_io_row3(*this, "ROW3")
	{ }

	void myvision(machine_config &config);

private:
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER( cart_load );
	uint8_t ay_port_a_r();
	uint8_t ay_port_b_r();
	void ay_port_a_w(uint8_t data);
	void ay_port_b_w(uint8_t data);

	void myvision_io(address_map &map) ATTR_COLD;
	void myvision_mem(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_device<generic_slot_device> m_cart;
	uint8_t m_column;
	required_ioport m_io_row0;
	required_ioport m_io_row1;
	required_ioport m_io_row2;
	required_ioport m_io_row3;
};


void myvision_state::myvision_mem(address_map &map)
{
	map.unmap_value_high();
	//map(0x0000, 0x5fff)      // mapped by the cartslot
	map(0xa000, 0xa7ff).ram();
	map(0xe000, 0xe000).rw("tms9918", FUNC(tms9918a_device::vram_read), FUNC(tms9918a_device::vram_write));
	map(0xe002, 0xe002).rw("tms9918", FUNC(tms9918a_device::register_read), FUNC(tms9918a_device::register_write));
}


void myvision_state::myvision_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x00).w("ay8910", FUNC(ay8910_device::address_w));
	map(0x01, 0x01).w("ay8910", FUNC(ay8910_device::data_w));
	map(0x02, 0x02).r("ay8910", FUNC(ay8910_device::data_r));
}


/* Input ports */
/*
  Keyboard layout is something like:
                       B
                  A          D    E
                       C
  1 2 3 4 5 6 7 8 9 10 11 12 13   14
 */
static INPUT_PORTS_START( myvision )
	PORT_START("ROW0")
	PORT_BIT(0x07, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_MAHJONG_M) PORT_NAME("13")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN) PORT_NAME("C/Down")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_MAHJONG_I) PORT_NAME("9")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_MAHJONG_E) PORT_NAME("5")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_MAHJONG_A) PORT_NAME("1")

	PORT_START("ROW1")
	PORT_BIT(0x07, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP) PORT_NAME("B/Up")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_MAHJONG_L) PORT_NAME("12")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_MAHJONG_H) PORT_NAME("8")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_MAHJONG_D) PORT_NAME("4")

	PORT_START("ROW2")
	PORT_BIT(0x07, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_MAHJONG_N) PORT_NAME("14/Start")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_NAME("D/Right")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_MAHJONG_J) PORT_NAME("10")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_MAHJONG_F) PORT_NAME("6")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_MAHJONG_B) PORT_NAME("2")

	PORT_START("ROW3")
	PORT_BIT(0x07, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT) PORT_NAME("A/Left")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("E")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_MAHJONG_K) PORT_NAME("11")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_MAHJONG_G) PORT_NAME("7")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_MAHJONG_C) PORT_NAME("3")

INPUT_PORTS_END


void myvision_state::machine_start()
{
	if (m_cart->exists())
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x0000, 0x5fff, read8sm_delegate(*m_cart, FUNC(generic_slot_device::read_rom)));

	save_item(NAME(m_column));
}


void myvision_state::machine_reset()
{
	m_column = 0xff;
}


DEVICE_IMAGE_LOAD_MEMBER( myvision_state::cart_load )
{
	uint32_t const size = m_cart->common_get_size("rom");

	if (size != 0x4000 && size != 0x6000)
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported cartridge size (must be 16K or 24K)");

	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}


uint8_t myvision_state::ay_port_a_r()
{
	uint8_t data = 0xff;

	if (!(m_column & 0x80))
		data &= m_io_row0->read();

	if (!(m_column & 0x40))
		data &= m_io_row1->read();

	if (!(m_column & 0x20))
		data &= m_io_row2->read();

	if (!(m_column & 0x10))
		data &= m_io_row3->read();

	return data;
}


uint8_t myvision_state::ay_port_b_r()
{
	return 0xff;
}


void myvision_state::ay_port_a_w(uint8_t data)
{
}


// Upper 4 bits select column
void myvision_state::ay_port_b_w(uint8_t data)
{
	m_column = data;
}

void myvision_state::myvision(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(10'738'635)/3);  /* Not verified */
	m_maincpu->set_addrmap(AS_PROGRAM, &myvision_state::myvision_mem);
	m_maincpu->set_addrmap(AS_IO, &myvision_state::myvision_io);

	/* video hardware */
	tms9918a_device &vdp(TMS9918A(config, "tms9918", XTAL(10'738'635)));  /* Exact model not verified */
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);  /* Not verified */
	vdp.int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	ay8910_device &ay8910(AY8910(config, "ay8910", XTAL(10'738'635)/3/2));  /* Exact model and clock not verified */
	ay8910.port_a_read_callback().set(FUNC(myvision_state::ay_port_a_r));
	ay8910.port_b_read_callback().set(FUNC(myvision_state::ay_port_b_r));
	ay8910.port_a_write_callback().set(FUNC(myvision_state::ay_port_a_w));
	ay8910.port_b_write_callback().set(FUNC(myvision_state::ay_port_b_w));
	ay8910.add_route(ALL_OUTPUTS, "mono", 0.50);

	/* cartridge */
	generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "myvision_cart"));
	cartslot.set_device_load(FUNC(myvision_state::cart_load));
	//cartslot.set_must_be_loaded(true);

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("myvision");
}

/* ROM definition */
ROM_START( myvision )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
ROM_END

} // anonymous namespace

/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     STATE           INIT        COMPANY       FULLN AME              FLAGS
CONS( 1983, myvision, 0,      0,      myvision, myvision, myvision_state, empty_init, "Nichibutsu", "My Vision (KH-1000)", 0 )



mz2000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
// thanks-to: Tomasz Slanina
/**************************************************************************************************

Sharp MZ-80B/MZ-2000/MZ-2200 B-series

TODO:
- Cassette loads aren't consistant, several SWs wants a slower clock.
  Original recording tapes looks very different compared to .mzt et al, may warrant a major
  normalization work;
- Emulate MZ-1U01 expansion unit bus slot (common with other MZ machines);
- add 80b compatibility support;
- MZ-1R12 option wants a specific IPL ROM revision, with "(/: S-RAM or ROM)" option listed.
  cfr. https://www.youtube.com/watch?v=MBeyqqqOFiY startup.

Notes:
- Memory controller Sharp LZ90D02, video controlller Sharp LZ90D01
- MZ-2000 has built-in monochrome monitor, while MZ-2200 is standalone. Color board for both is
  given by installing an MZ-1R01, former has jack connection for separate monitor
  cfr. https://www.youtube.com/watch?v=vgdUj-tUvCU
- MZ-80B is the odd one: its normally monochrome but has a specific (incompatible) color board
  named PIO-3039;

**************************************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "imagedev/snapquik.h"
#include "machine/i8255.h"
#include "machine/pit8253.h"
#include "machine/wd_fdc.h"
#include "machine/z80pio.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/2d_dsk.h"
#include "formats/mz_cas.h"


namespace {

class mz80b_state : public driver_device
{
public:
	mz80b_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_palette(*this, "palette")
		, m_region_chargen(*this, "chargen")
		, m_ipl_view(*this, "ipl_view")
		, m_cassette(*this, "cassette")
		, m_fdc(*this, "fdc")
		, m_floppy0(*this, "fdc:0")
		, m_floppy1(*this, "fdc:1")
		, m_floppy2(*this, "fdc:2")
		, m_floppy3(*this, "fdc:3")
		, m_floppy(nullptr)
		, m_pit(*this, "pit")
		, m_dac1bit(*this, "dac1bit")
		, m_io_keys(*this, {"KEY0", "KEY1", "KEY2", "KEY3", "KEY4", "KEY5", "KEY6", "KEY7", "KEY8", "KEY9", "KEYA", "KEYB", "KEYC", "KEYD", "UNUSED", "UNUSED"})
		, m_io_config(*this, "CONFIG")
	{ }

	void mz80b(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(boot_reset_cb);
	DECLARE_INPUT_CHANGED_MEMBER(ipl_reset_cb);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

	virtual uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	virtual void draw_graphics_layer(bitmap_ind16 &bitmap, const rectangle &cliprect);
	virtual void draw_text_layer(bitmap_ind16 &bitmap, const rectangle &cliprect);

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_memory_region m_region_chargen;
	memory_view m_ipl_view;
	std::unique_ptr<u8[]> m_work_ram;
	std::unique_ptr<u8[]> m_tvram;
	std::unique_ptr<u8[]> m_gvram;

	u8 m_width80;
	u8 m_tvram_attr;
	u8 m_gvram_mask;
	u8 m_back_color_mask;
	u8 m_video_reverse;
	u8 m_gvram_bank;
	u8 m_back_color;
	bool m_vgate;

	virtual void set_palette_bank();

	void mz80b_io(address_map &map) ATTR_COLD;

	void tvram_w(offs_t offset, u8 data);
	u8 tvram_r(offs_t offset);

	u8 m_vram_overlay_enable, m_vram_overlay_select;
	bool m_wait_state, m_hblank_state;

	bitmap_ind16 m_text_bitmap;
	bitmap_ind16 m_graphic_bitmap;

private:
	static void floppy_formats(format_registration &fr);

	required_device<cassette_image_device> m_cassette;
	required_device<mb8877_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	required_device<floppy_connector> m_floppy1;
	required_device<floppy_connector> m_floppy2;
	required_device<floppy_connector> m_floppy3;
	floppy_image_device *m_floppy;
	required_device<pit8253_device> m_pit;
	required_device<speaker_sound_device> m_dac1bit;
	required_ioport_array<16> m_io_keys;
	required_ioport m_io_config;

	u8 m_key_mux;

	u8 m_old_portc;
	u8 m_porta_latch;
	u8 m_tape_ctrl;

	u8 m_has_fdc;

	emu_timer *m_ipl_reset_timer = nullptr;
	emu_timer *m_hblank_timer = nullptr;

	void floppy_select_w(u8 data);
	void floppy_side_w(u8 data);
	void timer_w(u8 data);
	u8 fdc_r(offs_t offset);
	void fdc_w(offs_t offset, u8 data);
	u8 ppi_portb_r();
	void ppi_porta_w(u8 data);
	void ppi_portc_w(u8 data);
	void pio_porta_w(u8 data);
	u8 pio_portb_r();
	u8 pio_porta_r();

	TIMER_CALLBACK_MEMBER(ipl_timer_reset_cb);
	TIMER_CALLBACK_MEMBER(hblank_cb);

	DECLARE_SNAPSHOT_LOAD_MEMBER(snapshot_cb);

	// MZ-80B specifics
	void mz80b_map(address_map &map) ATTR_COLD;

	template <unsigned N> void mz80b_work_ram_w(offs_t offset, u8 data);
	template <unsigned N> u8 mz80b_work_ram_r(offs_t offset);
	void mz80b_vram_w(offs_t offset, u8 data);
	u8 mz80b_vram_r(offs_t offset);
	void mz80b_gvram_w(offs_t offset, u8 data);
	u8 mz80b_gvram_r(offs_t offset);
};

class mz2000_state : public mz80b_state
{
public:
	mz2000_state(const machine_config &mconfig, device_type type, const char *tag)
		: mz80b_state(mconfig, type, tag)
	{ }

	void mz2000(machine_config &config);

protected:
	virtual void draw_graphics_layer(bitmap_ind16 &bitmap, const rectangle &cliprect) override;
//  virtual void draw_text_layer(bitmap_ind16 &bitmap, const rectangle &cliprect) override;

	virtual uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) override;

private:
	void mz2000_io(address_map &map) ATTR_COLD;
	void mz2000_map(address_map &map) ATTR_COLD;

	template <unsigned N> void work_ram_w(offs_t offset, u8 data);
	template <unsigned N> u8 work_ram_r(offs_t offset);

	void gvram_w(offs_t offset, u8 data);
	u8 gvram_r(offs_t offset);

	void gvram_bank_w(u8 data);
	void back_color_w(u8 data);
	void tvram_attr_w(u8 data);
	void gvram_mask_w(u8 data);
};

class mz2200_state : public mz2000_state
{
public:
	mz2200_state(const machine_config &mconfig, device_type type, const char *tag)
		: mz2000_state(mconfig, type, tag)
	{ }

	void mz2200(machine_config &config);

protected:
	virtual void video_start() override ATTR_COLD;

	virtual void draw_graphics_layer(bitmap_ind16 &bitmap, const rectangle &cliprect) override;
	virtual void draw_text_layer(bitmap_ind16 &bitmap, const rectangle &cliprect) override;

	virtual void set_palette_bank() override { };
};


void mz80b_state::video_start()
{
	m_tvram = std::make_unique<u8[]>(0x1000);
	m_gvram = std::make_unique<u8[]>(0x10000);
	// back color register doesn't apply to monochrome monitor
	m_back_color = 0;
	m_back_color_mask = 0;

	save_pointer(NAME(m_tvram), 0x1000);
	save_pointer(NAME(m_gvram), 0x10000);
	save_item(NAME(m_width80));
	save_item(NAME(m_tvram_attr));
	save_item(NAME(m_gvram_mask));
	save_item(NAME(m_video_reverse));
	save_item(NAME(m_gvram_bank));
	save_item(NAME(m_back_color));
	save_item(NAME(m_vgate));

	m_screen->register_screen_bitmap(m_text_bitmap);
	m_screen->register_screen_bitmap(m_graphic_bitmap);

	m_video_reverse = 0;
	set_palette_bank();
}

void mz2200_state::video_start()
{
	mz2000_state::video_start();
	m_back_color_mask = 7;
}

/*
 * MZ-80B / MZ-2000 (monochrome)
 */

void mz80b_state::set_palette_bank()
{
	m_palette->set_pen_color(m_video_reverse ^ 0, rgb_t(0, 0, 0));
	m_palette->set_pen_color(m_video_reverse ^ 1, rgb_t(0, 255, 0));
	m_screen->update_partial(m_screen->vpos());
}

// MZ-80B is in 320 horizontal res compared to MZ-2000/MZ-2200 that is in pure 640.
void mz80b_state::draw_graphics_layer(bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	const u8 layer1_mask = BIT(m_gvram_mask, 0) * 0xff;
	const u8 layer2_mask = BIT(m_gvram_mask, 1) * 0xff;

	for (unsigned y = cliprect.min_y; y <= cliprect.max_y; y++)
	{
		const u16 gfx_offset = y * 40;
		auto *const dst = &bitmap.pix(y);

		for (unsigned x = cliprect.min_x; x <= cliprect.max_x; x += 16)
		{
			const u8 x_offset = x >> 4;
			const u8 gfx_1 = m_gvram[gfx_offset + x_offset + 0x0000] & layer1_mask;
			const u8 gfx_2 = m_gvram[gfx_offset + x_offset + 0x2000] & layer2_mask;

			for (unsigned xi = 0; xi < 8; xi ++)
			{
				const u8 pen = BIT(gfx_1, xi) || BIT(gfx_2, xi);

				dst[x + xi * 2 + 0] = m_palette->pen(pen);
				dst[x + xi * 2 + 1] = m_palette->pen(pen);
			}
		}
	}
}


void mz2000_state::draw_graphics_layer(bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (unsigned y = cliprect.min_y; y <= cliprect.max_y; y++)
	{
		const u16 gfx_offset = y * 80;
		auto *const dst = &bitmap.pix(y);

		for (unsigned x = cliprect.min_x; x <= cliprect.max_x; x += 8)
		{
			const u8 x_offset = x >> 3;
			const u8 gfx_b = m_gvram[gfx_offset + x_offset + 0x4000];
			const u8 gfx_r = m_gvram[gfx_offset + x_offset + 0x8000];
			const u8 gfx_g = m_gvram[gfx_offset + x_offset + 0xc000];

			for (unsigned xi = 0; xi < 8; xi ++)
			{
				const u8 pen = BIT(gfx_b, xi) || BIT(gfx_r, xi) || BIT(gfx_g, xi);

				dst[x + xi] = m_palette->pen(pen);
			}
		}
	}
}

void mz80b_state::draw_text_layer(bitmap_ind16 &bitmap, const rectangle &cliprect)
{
//  const u8 color = m_tvram_attr & 7;
	const u8 x_size = (m_width80 + 1) * 40;
	const u8 x_inc = m_width80 ? 8 : 16;
	const u8 x_shift = m_width80 ? 0 : 1;
	u8 *gfx_data = m_region_chargen->base();

	for (unsigned y = cliprect.min_y; y <= cliprect.max_y; y ++)
	{
		const u16 tile_offset = (y >> 3) * x_size;
		auto *const dst = &bitmap.pix(y);

		for (unsigned x = cliprect.min_x; x <= cliprect.max_x; x += x_inc)
		{
			const u8 x_offset = x >> (3 + x_shift);
			const u8 tile = m_tvram[tile_offset + x_offset];

			for (unsigned xi = 0; xi < x_inc; xi ++)
			{
				const u8 pen = BIT(gfx_data[tile * 8 + (y & 7)], (7 - (xi >> x_shift)));

				dst[x + xi] = m_palette->pen(pen);
			}
		}
	}
}


/*
 * MZ-2200 (color)
 */

void mz2200_state::draw_graphics_layer(bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (unsigned y = cliprect.min_y; y <= cliprect.max_y; y++)
	{
		const u16 gfx_offset = y * 80;
		auto *const dst = &bitmap.pix(y);

		for (unsigned x = cliprect.min_x; x <= cliprect.max_x; x += 8)
		{
			const u8 x_offset = x >> 3;
			const u8 gfx_b = m_gvram[gfx_offset + x_offset + 0x4000];
			const u8 gfx_r = m_gvram[gfx_offset + x_offset + 0x8000];
			const u8 gfx_g = m_gvram[gfx_offset + x_offset + 0xc000];

			for (unsigned xi = 0; xi < 8; xi ++)
			{
				const u8 pen = (BIT(gfx_b, xi) << 0) | (BIT(gfx_r, xi) << 1) | (BIT(gfx_g, xi) << 2);

				dst[x + xi] = m_palette->pen(pen & m_gvram_mask);
			}
		}
	}
}

void mz2200_state::draw_text_layer(bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	const u8 color = m_tvram_attr & 7;
	const u8 x_size = (m_width80 + 1) * 40;
	const u8 x_inc = m_width80 ? 8 : 16;
	const u8 x_shift = m_width80 ? 0 : 1;
	u8 *gfx_data = m_region_chargen->base();

	for (unsigned y = cliprect.min_y; y <= cliprect.max_y; y ++)
	{
		const u16 tile_offset = (y >> 3) * x_size;
		auto *const dst = &bitmap.pix(y);

		for (unsigned x = cliprect.min_x; x <= cliprect.max_x; x += x_inc)
		{
			const u8 x_offset = x >> (3 + x_shift);
			const u8 tile = m_tvram[tile_offset + x_offset];

			for (unsigned xi = 0; xi < x_inc; xi ++)
			{
				const u8 pen = (BIT(gfx_data[tile * 8 + (y & 7)], (7 - (xi >> x_shift)))) ? color : 0;

				dst[x + xi] = m_palette->pen(pen);
			}
		}
	}
}


uint32_t mz80b_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	bitmap.fill(m_back_color, cliprect);

	if (m_vgate)
	{
		return 0;
	}

	draw_text_layer(m_text_bitmap, cliprect);
	draw_graphics_layer(m_graphic_bitmap, cliprect);

	copybitmap_trans(bitmap, m_graphic_bitmap, 0, 0, 0, 0, cliprect, 0);
	copybitmap_trans(bitmap, m_text_bitmap, 0, 0, 0, 0, cliprect, 0);

	return 0;
}

uint32_t mz2000_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	// mz2000_cass:harvestc uses this extensively
	bitmap.fill(m_back_color, cliprect);

	if (m_vgate)
	{
		return 0;
	}

	draw_text_layer(m_text_bitmap, cliprect);
	draw_graphics_layer(m_graphic_bitmap, cliprect);

	// if bit 3 high then graphic layer has priority over text
	if (BIT(m_tvram_attr, 3))
	{
		copybitmap_trans(bitmap, m_text_bitmap, 0, 0, 0, 0, cliprect, 0);
		copybitmap_trans(bitmap, m_graphic_bitmap, 0, 0, 0, 0, cliprect, 0);
	}
	else
	{
		copybitmap_trans(bitmap, m_graphic_bitmap, 0, 0, 0, 0, cliprect, 0);
		copybitmap_trans(bitmap, m_text_bitmap, 0, 0, 0, 0, cliprect, 0);
	}

	return 0;
}


u8 mz80b_state::tvram_r(offs_t offset) { return m_tvram[offset]; }
void mz80b_state::tvram_w(offs_t offset, u8 data) { m_tvram[offset] = data; }

u8 mz2000_state::gvram_r(offs_t offset)
{
	if (!m_gvram_bank)
		return 0xff;
	return m_gvram[offset + m_gvram_bank * 0x4000];
}

void mz2000_state::gvram_w(offs_t offset, u8 data)
{
	if (!m_gvram_bank)
		return;
	m_gvram[offset + m_gvram_bank * 0x4000] = data;
}

void mz2000_state::gvram_bank_w(u8 data)
{
	m_gvram_bank = data & 3;
}

/*
 * MZ-2000 work RAM handling
 */

template <unsigned N> u8 mz2000_state::work_ram_r(offs_t offset)
{
	if (m_vram_overlay_enable)
	{
		const u8 page_mem = (offset | (N << 15)) >> 12;

		if (page_mem == 0xd && m_vram_overlay_select == 1)
		{
			if (!m_hblank_state && !machine().side_effects_disabled())
			{
				m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE);
				m_maincpu->retry_access();
				m_wait_state = true;
				return 0xff;
			}
			return tvram_r(offset & 0xfff);
		}
		else if (page_mem >= 0xc && m_vram_overlay_select == 0)
		{
			if (!m_hblank_state && !machine().side_effects_disabled())
			{
				m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE);
				m_maincpu->retry_access();
				m_wait_state = true;
				return 0xff;
			}

			return gvram_r(offset & 0x3fff);
		}
	}

	return m_work_ram[offset];
}

template <unsigned N> void mz2000_state::work_ram_w(offs_t offset, u8 data)
{
	if (m_vram_overlay_enable)
	{
		const u8 page_mem = (offset | (N << 15)) >> 12;

		if (page_mem == 0xd && m_vram_overlay_select == 1)
		{
			if (!m_hblank_state && !machine().side_effects_disabled())
			{
				m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE);
				m_maincpu->retry_access();
				m_wait_state = true;
				return;
			}

			tvram_w(offset & 0xfff, data);
			return;
		}
		else if (page_mem >= 0xc && m_vram_overlay_select == 0)
		{
			if (!m_hblank_state && !machine().side_effects_disabled())
			{
				m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE);
				m_maincpu->retry_access();
				m_wait_state = true;
				return;
			}

			gvram_w(offset & 0x3fff, data);
			return;
		}
	}

	m_work_ram[offset] = data;
}

/*
 * MZ-80B work RAM handling
 */

u8 mz80b_state::mz80b_gvram_r(offs_t offset)
{
	return m_gvram[(offset + m_gvram_bank * 0x2000)];
}

void mz80b_state::mz80b_gvram_w(offs_t offset, u8 data)
{
	m_gvram[(offset + m_gvram_bank * 0x2000)] = data;
}

template <unsigned N> u8 mz80b_state::mz80b_work_ram_r(offs_t offset)
{
	if (m_vram_overlay_enable)
	{
		const u8 page_mem = (offset | (N << 15)) >> 12;

		// TVRAM: 0xd*** or 0x5***
		// GVRAM: 0xe*** or 0x6***
		if (page_mem == (0xd ^ (m_vram_overlay_select << 3)))
		{
			if (!m_hblank_state && !machine().side_effects_disabled())
			{
				m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE);
				m_maincpu->retry_access();
				m_wait_state = true;
				return 0xff;
			}
			return tvram_r(offset & 0xfff);
		}
		else if (page_mem >= (0xe ^ (m_vram_overlay_select << 3)))
		{
			if (!m_hblank_state && !machine().side_effects_disabled())
			{
				m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE);
				m_maincpu->retry_access();
				m_wait_state = true;
				return 0xff;
			}

			return mz80b_gvram_r(offset & 0x1fff);
		}
	}

	return m_work_ram[offset];
}

template <unsigned N> void mz80b_state::mz80b_work_ram_w(offs_t offset, u8 data)
{
	if (m_vram_overlay_enable)
	{
		const u8 page_mem = (offset | (N << 15)) >> 12;

		if (page_mem == (0xd ^ (m_vram_overlay_select << 3)))
		{
			if (!m_hblank_state && !machine().side_effects_disabled())
			{
				m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE);
				m_maincpu->retry_access();
				m_wait_state = true;
				return;
			}

			tvram_w(offset & 0xfff, data);
			return;
		}
		else if (page_mem >= (0xe ^ (m_vram_overlay_select << 3)))
		{
			if (!m_hblank_state && !machine().side_effects_disabled())
			{
				m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE);
				m_maincpu->retry_access();
				m_wait_state = true;
				return;
			}

			mz80b_gvram_w(offset & 0x1fff, data);
			return;
		}
	}

	m_work_ram[offset] = data;
}

// 0x5000-0x7fff alias when IPL is enabled
u8 mz80b_state::mz80b_vram_r(offs_t offset)
{
	if (m_vram_overlay_enable && offset & 0x3000)
	{
		offset |= 0x4000;
		const u8 page_mem = (offset) >> 12;

		if (page_mem == (0xd ^ (m_vram_overlay_select << 3)))
		{
			if (!m_hblank_state && !machine().side_effects_disabled())
			{
				m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE);
				m_maincpu->retry_access();
				m_wait_state = true;
				return 0xff;
			}
			return tvram_r(offset & 0xfff);
		}
		else if (page_mem >= (0xe ^ (m_vram_overlay_select << 3)))
		{
			if (!m_hblank_state && !machine().side_effects_disabled())
			{
				m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE);
				m_maincpu->retry_access();
				m_wait_state = true;
				return 0xff;
			}

			return mz80b_gvram_r(offset & 0x1fff);
		}
	}

	return 0xff;
}

void mz80b_state::mz80b_vram_w(offs_t offset, u8 data)
{
	if (m_vram_overlay_enable && offset & 0x3000)
	{
		offset |= 0x4000;
		const u8 page_mem = (offset) >> 12;

		if (page_mem == (0xd ^ (m_vram_overlay_select << 3)))
		{
			if (!m_hblank_state && !machine().side_effects_disabled())
			{
				m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE);
				m_maincpu->retry_access();
				m_wait_state = true;
				return;
			}

			tvram_w(offset & 0xfff, data);
			return;
		}
		else if (page_mem >= (0xe ^ (m_vram_overlay_select << 3)))
		{
			if (!m_hblank_state && !machine().side_effects_disabled())
			{
				m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE);
				m_maincpu->retry_access();
				m_wait_state = true;
				return;
			}

			mz80b_gvram_w(offset & 0x1fff, data);
			return;
		}
	}
}

u8 mz80b_state::fdc_r(offs_t offset)
{
	if(m_has_fdc)
		return m_fdc->read(offset) ^ 0xff;

	return 0xff;
}

void mz80b_state::fdc_w(offs_t offset, u8 data)
{
	if(m_has_fdc)
		m_fdc->write(offset, data ^ 0xff);
}

void mz80b_state::floppy_select_w(u8 data)
{
	switch (data & 0x03)
	{
	case 0: m_floppy = m_floppy0->get_device(); break;
	case 1: m_floppy = m_floppy1->get_device(); break;
	case 2: m_floppy = m_floppy2->get_device(); break;
	case 3: m_floppy = m_floppy3->get_device(); break;
	}

	m_fdc->set_floppy(m_floppy);

	// TODO: bit 2 is connected to something too...

	if (m_floppy)
		m_floppy->mon_w(!BIT(data, 7));
}

void mz80b_state::floppy_side_w(u8 data)
{
	if (m_floppy)
		m_floppy->ss_w(BIT(data, 0));
}

void mz80b_state::timer_w(u8 data)
{
	m_pit->write_gate0(1);
	m_pit->write_gate1(1);
	m_pit->write_gate0(0);
	m_pit->write_gate1(0);
	m_pit->write_gate0(1);
	m_pit->write_gate1(1);
}

void mz2000_state::back_color_w(u8 data)
{
	m_back_color = data & m_back_color_mask;
	m_screen->update_partial(m_screen->vpos());
}

void mz2000_state::tvram_attr_w(u8 data)
{
	m_tvram_attr = data;
	m_screen->update_partial(m_screen->vpos());
}

void mz2000_state::gvram_mask_w(u8 data)
{
	m_gvram_mask = data;
	m_screen->update_partial(m_screen->vpos());
}

void mz80b_state::mz80b_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(mz80b_state::mz80b_work_ram_r<0>), FUNC(mz80b_state::mz80b_work_ram_w<0>));
	map(0x0000, 0xffff).view(m_ipl_view);
	m_ipl_view[0](0x0000, 0x07ff).rom().region("ipl", 0);
	m_ipl_view[0](0x0800, 0x3fff).unmaprw();
	m_ipl_view[0](0x4000, 0x7fff).rw(FUNC(mz80b_state::mz80b_vram_r), FUNC(mz80b_state::mz80b_vram_w));
	m_ipl_view[0](0x8000, 0xffff).rw(FUNC(mz80b_state::mz80b_work_ram_r<1>), FUNC(mz80b_state::mz80b_work_ram_w<1>));

	// theoretical, cfr. below
//  map(0x5000, 0x7fff).view(m_vram_lo_view);
//  m_vram_lo_view[0](0x5000, 0x5fff).ram().share("tvram");
//  m_vram_lo_view[0](0x6000, 0x7fff).rw(FUNC(mz80b_state::gvram_r), FUNC(mz80b_state::gvram_w));

//  map(0xd000, 0xffff).view(m_vram_hi_view);
//  m_vram_hi_view[0](0xd000, 0xdfff).ram().share("tvram");
//  m_vram_hi_view[0](0xe000, 0xffff).rw(FUNC(mz80b_state::gvram_r), FUNC(mz80b_state::gvram_w));
}

void mz2000_state::mz2000_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(mz2000_state::work_ram_r<0>), FUNC(mz2000_state::work_ram_w<0>));
	map(0x0000, 0xffff).view(m_ipl_view);
	m_ipl_view[0](0x0000, 0x07ff).rom().region("ipl", 0);
	m_ipl_view[0](0x0800, 0x7fff).unmaprw();
	m_ipl_view[0](0x8000, 0xffff).rw(FUNC(mz2000_state::work_ram_r<1>), FUNC(mz2000_state::work_ram_w<1>));

	// theoretical, cpm22 executes stuff from GVRAM at 0xfa00 during bootstrap,
	// core gets confused and execute from work RAM instead.
	// wpset 0,ffff,r,1,{printf "%04x",wpaddr;g} will make this approach to work ...
//  map(0xc000, 0xffff).view(m_vram_view);
//  m_vram_view[0](0xc000, 0xffff).rw(FUNC(mz2000_state::gvram_r), FUNC(mz2000_state::gvram_w));
//  m_vram_view[1](0xd000, 0xdfff).ram().share("tvram");
}

void mz80b_state::mz80b_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
//  map(0xb4, 0xb4) I/O Data PIO-3039 palette control
//  map(0xb8, 0xbb) MZ-1R13 Kanji ROM
//  map(0xd0, 0xd3) quick disk SIO
//  map(0xd4, 0xd7) MZ-1M01 PIO for 16-bit board
	map(0xd8, 0xdb).rw(FUNC(mz2000_state::fdc_r), FUNC(mz2000_state::fdc_w));
	map(0xdc, 0xdc).w(FUNC(mz2000_state::floppy_select_w));
	map(0xdd, 0xdd).w(FUNC(mz2000_state::floppy_side_w));
//  map(0xde, 0xde) floppy density register
	map(0xe0, 0xe3).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xe4, 0xe7).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xe8, 0xeb).rw("pio", FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0xf0, 0xf3).w(FUNC(mz2000_state::timer_w));
	map(0xf4, 0xf7).lw8(NAME([this] (u8 data) {
		m_gvram_bank = (data & 1);
		m_gvram_mask = (data & 6) >> 1;
		m_screen->update_partial(m_screen->vpos());
	}));
//  map(0xf4, 0xf7).w(FUNC(mz2000_state::vram_bank_w));
//  map(0xf8, 0xfa) MZ-1R12 SRAM
//  map(0xfe, 0xfe) printer (w) strobe/reset (r) status
//  map(0xff, 0xff) printer data
}

void mz2000_state::mz2000_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	mz80b_io(map);
	map(0xf4, 0xf4).w(FUNC(mz2000_state::back_color_w));
	map(0xf5, 0xf5).w(FUNC(mz2000_state::tvram_attr_w));
	map(0xf6, 0xf6).w(FUNC(mz2000_state::gvram_mask_w));
	map(0xf7, 0xf7).w(FUNC(mz2000_state::gvram_bank_w));
}

/*
 * x--- ---- break key
 * -x-- ---- read tape data
 * --x- ---- no tape signal
 * ---x ---- no tape write signal
 * ---- x--- end of tape reached
 * ---- ---x "blank" control
 */
u8 mz80b_state::ppi_portb_r()
{
	u8 res = m_io_keys[3]->read() & 0x80;

	if(m_cassette->get_image() != nullptr)
	{
		res |= (m_cassette->input() > 0.0038) ? 0x40 : 0x00;
		res |= ((m_cassette->get_state() & CASSETTE_MASK_UISTATE) == CASSETTE_PLAY) ? 0x10 : 0x30;
		res |= (m_cassette->get_position() >= m_cassette->get_length()) ? 0x08 : 0x00;
	}
	else
		res |= 0x30;

	res |= (m_screen->vblank()) ? 0x00 : 0x01;

	return res;
}

/*
 * All tape control are enabled thru a 0->1 transition
 * x--- ---- tape "APSS"
 * -x-- ---- tape "APLAY"
 * --x- ---- tape "AREW"
 * ---x ---- reverse video (monochrome only?)
 * ---- x--- tape stop
 * ---- -x-- tape play
 * ---- --x- tape ff
 * ---- ---x tape rewind
 */
void mz80b_state::ppi_porta_w(u8 data)
{
	if((m_tape_ctrl & 0x80) == 0 && data & 0x80)
	{
		//popmessage("Tape APSS control");
	}

	if((m_tape_ctrl & 0x40) == 0 && data & 0x40)
	{
		//popmessage("Tape APLAY control");
	}

	if((m_tape_ctrl & 0x20) == 0 && data & 0x20)
	{
		//popmessage("Tape AREW control");
	}

	if (BIT(m_tape_ctrl, 4) != BIT(data, 4))
	{
		m_video_reverse = !BIT(data, 4);
		set_palette_bank();
	}

	//if (BIT(data, 4))
	//  popmessage("Reverse video");

	if((m_tape_ctrl & 0x08) == 0 && data & 0x08) // stop
	{
		m_cassette->change_state(CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR);
		m_cassette->change_state(CASSETTE_STOPPED,CASSETTE_MASK_UISTATE);
	}

	if((m_tape_ctrl & 0x04) == 0 && data & 0x04) // play
	{
		m_cassette->change_state(CASSETTE_MOTOR_ENABLED,CASSETTE_MASK_MOTOR);
		m_cassette->change_state(CASSETTE_PLAY,CASSETTE_MASK_UISTATE);
	}

	if((m_tape_ctrl & 0x02) == 0 && data & 0x02)
	{
		//popmessage("Tape FF control");
	}

	if((m_tape_ctrl & 0x01) == 0 && data & 0x01)
	{
		//popmessage("Tape Rewind control");
	}

	m_tape_ctrl = data;
}

/*
 * x--- ---- tape data write
 * -x-- ---- tape rec
 * --x- ---- tape ?
 * ---x ---- tape eject
 * ---- x--- 1->0 transition = IPL start
 * ---- -x-- DAC1BIT state
 * ---- --x- 0->1 transition = Work RAM reset
 */
void mz80b_state::ppi_portc_w(u8 data)
{
	//logerror("C W %02x\n",data);

	if(BIT(m_old_portc, 3) != BIT(data, 3))
	{
		logerror("PIO PC: IPL reset %s\n", BIT(data, 3) ? "stopped" : "started");
		// TODO: timing
		m_ipl_reset_timer->adjust(!BIT(data, 3) ? attotime::from_hz(100) : attotime::never);
	}

	if(!BIT(m_old_portc, 1) && BIT(data, 1))
	{
		logerror("PIO PC: Work RAM reset\n");
		m_ipl_view.disable();
		m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
	}

	m_dac1bit->level_w(BIT(data, 2));

	m_vgate = !!(BIT(data, 0));

	m_old_portc = data;
}

void mz80b_state::pio_porta_w(u8 data)
{
	m_vram_overlay_enable = BIT(data, 7);
	m_vram_overlay_select = BIT(data, 6);
	//logerror("PIO PA vram %s %02x\n", m_vram_overlay_enable ? "select" : "disable", BIT(data, 6));
//  if (BIT(data, 7))
//  {
//      m_vram_view.select(BIT(data, 6));
//  }
//  else
//  {
//      m_vram_view.disable();
//  }

	m_width80 = ((data & 0x20) >> 5);
	m_key_mux = data & 0x1f;

	m_porta_latch = data;
}

u8 mz80b_state::pio_portb_r()
{
	if(((m_key_mux & 0x10) == 0x00) || ((m_key_mux & 0x0f) == 0x0f)) //status read
	{
		int res,i;

		res = 0xff;
		for(i = 0; i < 0xe; i++)
			res &= m_io_keys[i]->read();

		return res;
	}

	return m_io_keys[m_key_mux & 0xf]->read();
}

u8 mz80b_state::pio_porta_r()
{
	return m_porta_latch;
}

/*
   The \ key is actually directly to the left of the BREAK key; the CLR/HOME and INST/DEL keys sit
   between the BREAK key and the CR key, and the ] key lies directly to the left of CR. The somewhat
   fudged key bindings for this corner of the keyboard approximate those used for other JIS keyboards.
   (The Japanese MZ-80B/MZ-2000 keyboard layout is almost but not quite JIS.)

   For the natural keyboard, GRPH and RVS/KANA are mapped to the left and right ALT keys; this follows
   their positions on the MZ-2500 keyboard. The unshifted INST/DEL functions as a backspace key and
   has been mapped accordingly.
*/

INPUT_CHANGED_MEMBER(mz80b_state::boot_reset_cb)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? CLEAR_LINE : ASSERT_LINE);
}

INPUT_CHANGED_MEMBER(mz80b_state::ipl_reset_cb)
{
	machine().schedule_soft_reset();
}

TIMER_CALLBACK_MEMBER(mz80b_state::ipl_timer_reset_cb)
{
	logerror("IPL reset kicked in\n");
	machine().schedule_soft_reset();
}

static INPUT_PORTS_START( mz80be ) // European keyboard
	PORT_START("BACK_PANEL")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mz80b_state::boot_reset_cb), 0) PORT_NAME("Boot Reset")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mz80b_state::ipl_reset_cb), 0) PORT_NAME("IPL Reset")

	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_00_PAD) PORT_CHAR(UCHAR_MAMEKEY(00_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TAB") PORT_CODE(KEYCODE_LALT) PORT_CHAR('\t')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CR") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR('\r') // also "ENT" on keypad
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BREAK") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(UCHAR_MAMEKEY(PAUSE))

	PORT_START("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"/  \u2190  \u2192") PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/')                 // ← → (arrows)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"A  \u2514")         PORT_CODE(KEYCODE_A)          PORT_CHAR('a') PORT_CHAR('A')  // └ (box drawing)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"B  \u2663")         PORT_CODE(KEYCODE_B)          PORT_CHAR('b') PORT_CHAR('B')  // ♣ (card suit)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"C  \u2665")         PORT_CODE(KEYCODE_C)          PORT_CHAR('c') PORT_CHAR('C')  // ♥ (card suit)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"D  \u2502")         PORT_CODE(KEYCODE_D)          PORT_CHAR('d') PORT_CHAR('D')  // │ (box drawing)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"E  \u2524")         PORT_CODE(KEYCODE_E)          PORT_CHAR('e') PORT_CHAR('E')  // ┤ (box drawing)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"F  \u2500")         PORT_CODE(KEYCODE_F)          PORT_CHAR('f') PORT_CHAR('F')  // ─ (box drawing)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"G  \u253c")         PORT_CODE(KEYCODE_G)          PORT_CHAR('g') PORT_CHAR('G')  // ┼ (box drawing)

	PORT_START("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"H  \u2551")         PORT_CODE(KEYCODE_H)          PORT_CHAR('h') PORT_CHAR('H')  // ║  (box drawing)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"I  \u255e")         PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')  // ╞  (box drawing)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"J  \u2550")         PORT_CODE(KEYCODE_J)          PORT_CHAR('j') PORT_CHAR('J')  // ═  (box drawing)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"K  \u256c")         PORT_CODE(KEYCODE_K)          PORT_CHAR('k') PORT_CHAR('K')  // ╬  (box drawing)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"L  \u256b")         PORT_CODE(KEYCODE_L)          PORT_CHAR('l') PORT_CHAR('L')  // ╫  (box drawing)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"M  £")              PORT_CODE(KEYCODE_M)          PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"N  \u25cb")         PORT_CODE(KEYCODE_N)          PORT_CHAR('n') PORT_CHAR('N')  // ○
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"O  \u2568")         PORT_CODE(KEYCODE_O)          PORT_CHAR('o') PORT_CHAR('O')  // ╨  (box drawing)

	PORT_START("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"P  \u2565")         PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P')  // ╥ (box drawing)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Q  \u250c")         PORT_CODE(KEYCODE_Q)          PORT_CHAR('q') PORT_CHAR('Q')  // ┌ (box drawing)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"R  \u251c")         PORT_CODE(KEYCODE_R)          PORT_CHAR('r') PORT_CHAR('R')  // ├ (box drawing)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"S  \u2518")         PORT_CODE(KEYCODE_S)          PORT_CHAR('s') PORT_CHAR('S')  // ┘ (box drawing)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"T  \u2534")         PORT_CODE(KEYCODE_T)          PORT_CHAR('t') PORT_CHAR('T')  // ┴ (box drawing)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"U  \u2521")         PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U')  // ┡ (box drawing)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"V  \u2666")         PORT_CODE(KEYCODE_V)          PORT_CHAR('v') PORT_CHAR('V')  // ♦ (card suit)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"W  \u2510")         PORT_CODE(KEYCODE_W)          PORT_CHAR('w') PORT_CHAR('W')  // ┐ (box drawing)

	PORT_START("KEY7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"X  \u2660")         PORT_CODE(KEYCODE_X)          PORT_CHAR('x') PORT_CHAR('X')  // ♠ (card suit)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Y  \u252c")         PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')  // ┬ (box drawing)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Z  \u256a")         PORT_CODE(KEYCODE_Z)          PORT_CHAR('z') PORT_CHAR('Z')  // ╪ (box drawing)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                  PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                                  PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"?  \u2191  \u2193") PORT_CODE(KEYCODE_RCONTROL)   PORT_CHAR('?')                 // ↑ ↓ (arrows)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8".  >  π")           PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8",  <  ¥")           PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("KEY8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('_')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("KEY9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@') PORT_CHAR('`') // actually between P and ~
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEYA")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CLR  HOME") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("INST  DEL") PORT_CODE(KEYCODE_DEL) PORT_CHAR('\b') PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEYB")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("GRPH") PORT_CODE(KEYCODE_TAB) PORT_CHAR(UCHAR_MAMEKEY(LALT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SFT LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RVS") PORT_CODE(KEYCODE_TILDE) PORT_CHAR(UCHAR_MAMEKEY(RALT))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEYC")
	PORT_BIT(0xff,IP_ACTIVE_LOW,IPT_UNUSED)

	PORT_START("KEYD")
	PORT_BIT(0xff,IP_ACTIVE_LOW,IPT_UNUSED)

	PORT_START("UNUSED")
	PORT_BIT(0xff,IP_ACTIVE_LOW,IPT_UNUSED )

	PORT_START("CONFIG")
	PORT_CONFNAME( 0x02, 0x02, "Floppy Device" )
	PORT_CONFSETTING( 0x00, DEF_STR( No ) )
	PORT_CONFSETTING( 0x02, DEF_STR( Yes ) )
INPUT_PORTS_END

static INPUT_PORTS_START( mz80bj ) // Japanese keyboard (kana, no RVS)
	PORT_INCLUDE( mz80be )

	PORT_MODIFY("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"/  \u2190  \u30e1  \u2192") PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/')                 // ← メ → (me, arrows)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"A  \u30c1  \u2514")         PORT_CODE(KEYCODE_A)          PORT_CHAR('a') PORT_CHAR('A')  // チ └ (chi, box drawing)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"B  \u30b3  \u2663")         PORT_CODE(KEYCODE_B)          PORT_CHAR('b') PORT_CHAR('B')  // コ ♣ (ko, card suit)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"C  \u30bd  \u2665")         PORT_CODE(KEYCODE_C)          PORT_CHAR('c') PORT_CHAR('C')  // ソ ♥ (so, card suit)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"D  \u30b7  \u2502")         PORT_CODE(KEYCODE_D)          PORT_CHAR('d') PORT_CHAR('D')  // シ │ (shi, box drawing)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"E  \u30a4  \u2524")         PORT_CODE(KEYCODE_E)          PORT_CHAR('e') PORT_CHAR('E')  // イ ┤ (i, box drawing)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"F  \u30cf  \u2500")         PORT_CODE(KEYCODE_F)          PORT_CHAR('f') PORT_CHAR('F')  // ハ ─ (ha, box drawing)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"G  \u30ad  \u253c")         PORT_CODE(KEYCODE_G)          PORT_CHAR('g') PORT_CHAR('G')  // キ ┼ (ki, box drawing)

	PORT_MODIFY("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"H  \u30af  \u2551")         PORT_CODE(KEYCODE_H)          PORT_CHAR('h') PORT_CHAR('H')  // ク ║ (ku, box drawing)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"I  \u30cb  \u255e")         PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')  // ニ ╞ (ni, box drawing)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"J  \u30de  \u2550")         PORT_CODE(KEYCODE_J)          PORT_CHAR('j') PORT_CHAR('J')  // マ ═ (ma, box drawing)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"K  \u30ce  \u256c")         PORT_CODE(KEYCODE_K)          PORT_CHAR('k') PORT_CHAR('K')  // ノ ╬ (no, box drawing)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"L  \u30ea  \u256b")         PORT_CODE(KEYCODE_L)          PORT_CHAR('l') PORT_CHAR('L')  // リ ╫ (ri, box drawing)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"M  \u30e2  ¥")              PORT_CODE(KEYCODE_M)          PORT_CHAR('m') PORT_CHAR('M')  // モ (mo)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"N  \u30df  \u25cb")         PORT_CODE(KEYCODE_N)          PORT_CHAR('n') PORT_CHAR('N')  // ミ ○ (mi)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"O  \u30e9  \u2568")         PORT_CODE(KEYCODE_O)          PORT_CHAR('o') PORT_CHAR('O')  // ラ ╨ (ra, box drawing)

	PORT_MODIFY("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"P  \u30bb  \u2565")         PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P')  // セ ╥ (se, box drawing)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Q  \u30bf  \u250c")         PORT_CODE(KEYCODE_Q)          PORT_CHAR('q') PORT_CHAR('Q')  // タ ┌ (ta, box drawing)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"R  \u30b9  \u251c")         PORT_CODE(KEYCODE_R)          PORT_CHAR('r') PORT_CHAR('R')  // ス ├ (su, box drawing)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"S  \u30c8  \u2518")         PORT_CODE(KEYCODE_S)          PORT_CHAR('s') PORT_CHAR('S')  // ト ┘ (to, box drawing)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"T  \u30ab  \u2534")         PORT_CODE(KEYCODE_T)          PORT_CHAR('t') PORT_CHAR('T')  // カ ┴ (ka, box drawing)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"U  \u30ca  \u2561")         PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U')  // ナ ╡ (na, box drawing)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"V  \u30d2  \u2666")         PORT_CODE(KEYCODE_V)          PORT_CHAR('v') PORT_CHAR('V')  // ヒ ♦ (hi, card suit)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"W  \u30c6  \u2510")         PORT_CODE(KEYCODE_W)          PORT_CHAR('w') PORT_CHAR('W')  // テ ┐ (te, box drawing)

	PORT_MODIFY("KEY7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"X  \u30b5  \u2660")         PORT_CODE(KEYCODE_X)          PORT_CHAR('x') PORT_CHAR('X')  // サ ♠ (sa, card suit
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Y  \u30f3  \u252c")         PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')  // ン ┬ (n, box drawing)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Z  \u30c4  \u256a")         PORT_CODE(KEYCODE_Z)          PORT_CHAR('z') PORT_CHAR('Z')  // ツ ╪ (tsu, box drawing)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"^  ~  \u30d8")              PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('^') PORT_CHAR('~')  // ヘ (he)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\\  |  \u30f2")             PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('|')                 // ヲ (wo)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"?  \u2191  \u30ed  \u2193") PORT_CODE(KEYCODE_RCONTROL)   PORT_CHAR('?')                 // ↑ ロ ↓ (ro, arrows)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8".  >  \u30eb  \u3002")      PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR('>')  // ル 。 (ru, full stop)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8",  <  \u30cd  π")           PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR('<')  // ネ (ne)

	PORT_MODIFY("KEY8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"0  _  \u30ef")              PORT_CODE(KEYCODE_0)          PORT_CHAR('0') PORT_CHAR('_')  // ワ (wa)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"1  !  \u30cc")              PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')  // ヌ (nu)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"2  \"  \u30d5")             PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('"')  // フ (fu)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"3  #  \u30a2")              PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('#')  // ア (a)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"4  $  \u30a6")              PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$')  // ウ (u)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"5  %  \u30a8")              PORT_CODE(KEYCODE_5)          PORT_CHAR('5') PORT_CHAR('%')  // エ (e)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"6  &  \u30aa")              PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('&')  // オ (o)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"7  \'  \u30e4")             PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('\'') // ヤ (ya)

	PORT_MODIFY("KEY9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"8  (  \u30e6")              PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('(')  // ユ (yu
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"9  )  \u30e8")              PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')')  // ヨ (yo
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8":  *  \u30b1")              PORT_CODE(KEYCODE_COLON)      PORT_CHAR(':') PORT_CHAR('*')  // ケ (ke
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8";  +  \u30ec")              PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(';') PORT_CHAR('+')  // レ (re
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"-  =  \u30db")              PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-') PORT_CHAR('=')  // ホ (ho
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"@  `  \u309b")              PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('@') PORT_CHAR('`')  // ゛ (dakuten)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"[  {  \u309c  \u300c")      PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[') PORT_CHAR('{')  // ゜ 「 (handakuten, bracket)

	PORT_MODIFY("KEYA")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"]  }  \u30e0  \u300d")      PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR(']') PORT_CHAR('}')  // ム 」 (mu, bracket)

	PORT_MODIFY("KEYB")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u30ab\u30ca")              PORT_CODE(KEYCODE_TILDE)      PORT_CHAR(UCHAR_MAMEKEY(RALT)) // カナ (kana)
INPUT_PORTS_END

static const gfx_layout charlayout_8x8 =
{
	8, 8,
	256,
	1,
	{ 0 },
	{ STEP8(0, 1) },
	{ STEP8(0, 8) },
	8*8
};

//static const gfx_layout charlayout_8x16 =
//{
//  8, 16,
//  256,
//  1,
//  { 0 },
//  { STEP8(0,1) },
//  { STEP16(0,8) },
//  8*16
//};

static GFXDECODE_START( gfx_mz2000 )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout_8x8, 0, 1 )
//  GFXDECODE_ENTRY( "chargen", 0x0800, charlayout_8x16, 0, 1 )
GFXDECODE_END

void mz80b_state::machine_start()
{
	m_work_ram = make_unique_clear<u8[]>(0x10000);
	save_pointer(NAME(m_work_ram), 0x10000);
	save_item(NAME(m_vram_overlay_enable));
	save_item(NAME(m_vram_overlay_select));
	save_item(NAME(m_key_mux));
	save_item(NAME(m_old_portc));
	save_item(NAME(m_porta_latch));
	save_item(NAME(m_tape_ctrl));
	save_item(NAME(m_wait_state));
	save_item(NAME(m_hblank_state));

	m_vram_overlay_enable = 0;
	m_vram_overlay_select = 0;
	// m_vram_view.disable();

	m_ipl_reset_timer = timer_alloc(FUNC(mz2000_state::ipl_timer_reset_cb), this);
	m_hblank_timer = timer_alloc(FUNC(mz2000_state::hblank_cb), this);
}

void mz80b_state::machine_reset()
{
	m_ipl_view.select(0);

	m_ipl_reset_timer->adjust(attotime::never);
	m_dac1bit->level_w(0);
	m_hblank_timer->adjust(m_screen->time_until_pos(0, 0), true);

	m_has_fdc = (m_io_config->read() & 2) >> 1;
}


void mz80b_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_2D_FORMAT);
}

// TODO: "MZ-2000/2200 5/3/3.5'2-D System generator" printed with cpm22 SYSGEN.COM, investigate
static void mz2000_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
	device.option_add("3ssdd", FLOPPY_3_SSDD);
	device.option_add("35dd", FLOPPY_35_DD);
}

SNAPSHOT_LOAD_MEMBER(mz80b_state::snapshot_cb)
{
	if (image.length() > 0x10000)
		return std::make_pair(image_error::INVALIDLENGTH, std::string());

	std::vector<u8> snapshot(image.length());
	image.fread(&snapshot[0], image.length());

	std::copy(std::begin(snapshot), std::end(snapshot), m_work_ram.get());

	m_ipl_view.disable();
	m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);

	m_cassette->change_state(CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
	m_cassette->change_state(CASSETTE_STOPPED, CASSETTE_MASK_UISTATE);

	return std::make_pair(std::error_condition(), std::string());
}

TIMER_CALLBACK_MEMBER(mz80b_state::hblank_cb)
{
	m_hblank_state = param;

	if (m_wait_state)
	{
		m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, CLEAR_LINE);
		m_wait_state = false;
	}

	const int next_x = param ? 0 : 640;
	const int next_y = (m_screen->vpos() + param) % m_screen->height();

	//printf("%d %d = %d\n", m_screen->hpos(), m_screen->vpos(), param);

	m_hblank_timer->adjust(m_screen->time_until_pos(next_y, next_x), !param);
}

// TODO: verify all clocks
void mz80b_state::mz80b(machine_config &config)
{
	constexpr XTAL MASTER_CLOCK = XTAL(24'000'000) / 6;

	Z80(config, m_maincpu, MASTER_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &mz80b_state::mz80b_map);
	m_maincpu->set_addrmap(AS_IO, &mz80b_state::mz80b_io);
//  m_maincpu->set_addrmap(AS_OPCODES, &mz2000_state::mz2000_opcodes);

	i8255_device &ppi(I8255(config, "ppi"));
	ppi.out_pa_callback().set(FUNC(mz80b_state::ppi_porta_w));
	ppi.in_pb_callback().set(FUNC(mz80b_state::ppi_portb_r));
	ppi.out_pc_callback().set(FUNC(mz80b_state::ppi_portc_w));

	z80pio_device &pio(Z80PIO(config, "pio", MASTER_CLOCK));
	pio.in_pa_callback().set(FUNC(mz80b_state::pio_porta_r));
	pio.out_pa_callback().set(FUNC(mz80b_state::pio_porta_w));
	pio.in_pb_callback().set(FUNC(mz80b_state::pio_portb_r));

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(MASTER_CLOCK / 128); // 31'250
	// 1 sec, needed by mz2000_flop:gfxedit/mz2000_cass:vosque2k
	m_pit->out_handler<0>().set(m_pit, FUNC(pit8253_device::write_clk1));
	// 12h AM/PM clock
	m_pit->out_handler<1>().set(m_pit, FUNC(pit8253_device::write_clk2));

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_dac1bit).add_route(ALL_OUTPUTS, "mono", 0.15);

	// TODO: MB8866 for MZ-80B
	MB8877(config, m_fdc, 1_MHz_XTAL);

	FLOPPY_CONNECTOR(config, "fdc:0", mz2000_floppies, "525dd", mz2000_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", mz2000_floppies, "525dd", mz2000_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:2", mz2000_floppies, nullptr, mz2000_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:3", mz2000_floppies, nullptr, mz2000_state::floppy_formats).enable_sound(true);


	CASSETTE(config, m_cassette);
	m_cassette->set_formats(mz700_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("mz_cass");

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	// TODO: unverified, 60 Hz/15.75 kHz according to MZ-80B service manual
	m_screen->set_raw(XTAL(14'318'181), 910, 0, 640, 262, 0, 200);
	m_screen->set_screen_update(FUNC(mz80b_state::screen_update));
	m_screen->set_palette(m_palette);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_mz2000);
	PALETTE(config, m_palette).set_entries(2);

	// TODO: placeholder for actual MZ-1E18 / MZ-1R12 options.
	// mz800 actually reads $f8-$fa from IPL
	snapshot_image_device &snapshot(SNAPSHOT(config, "snapshot", "bin,dat", attotime::from_seconds(1)));
	snapshot.set_load_callback(FUNC(mz80b_state::snapshot_cb));

	SOFTWARE_LIST(config, "flop_list").set_original("mz80b_flop");
	SOFTWARE_LIST(config, "cass_list").set_original("mz80b_cass");
}


void mz2000_state::mz2000(machine_config &config)
{
	mz80b(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &mz2000_state::mz2000_map);
	m_maincpu->set_addrmap(AS_IO, &mz2000_state::mz2000_io);

	m_screen->set_screen_update(FUNC(mz2000_state::screen_update));

	SOFTWARE_LIST(config.replace(), "flop_list").set_original("mz2000_flop");
	SOFTWARE_LIST(config.replace(), "cass_list").set_original("mz2000_cass").set_filter("MONO");
	SOFTWARE_LIST(config, "flop_generic_list").set_compatible("generic_flop_525").set_filter("mz2000");
}

void mz2200_state::mz2200(machine_config &config)
{
	mz2000(config);

	PALETTE(config.replace(), m_palette, palette_device::BRG_3BIT);

	SOFTWARE_LIST(config.replace(), "cass_list").set_original("mz2000_cass").set_filter("COLOR");
	SOFTWARE_LIST(config.replace(), "flop_generic_list").set_compatible("generic_flop_525").set_filter("mz2200");
}



ROM_START( mz80b )
	ROM_REGION( 0x800, "ipl", 0 )
	ROM_LOAD( "ipl.rom",  0x0000, 0x0800, CRC(80beeec0) SHA1(d2b8167cc77ad023a807198993cb5e7a94c9e19e) )

	ROM_REGION( 0x800, "chargen", 0 )
	ROM_LOAD( "mzfont.rom", 0x0000, 0x0800, CRC(0631efc3) SHA1(99b206af5c9845995733d877e9e93e9681b982a8) )
ROM_END

ROM_START( mz2000 )
	ROM_REGION( 0x800, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "mz20ipl.bin",0x0000, 0x0800, CRC(d7ccf37f) SHA1(692814ffc2cf50fa8bf9e30c96ebe4a9ee536a86))

	ROM_REGION( 0x800, "chargen", 0 )
//  ROM_LOAD( "mzfont.rom", 0x0000, 0x0800, BAD_DUMP CRC(0631efc3) SHA1(99b206af5c9845995733d877e9e93e9681b982a8) ) //original has JP characters
	/* these are hand-crafted roms, converted from bmps floating around the net */
	ROM_LOAD( "font.bin",    0x0000, 0x0800, BAD_DUMP CRC(6ae6ce8e) SHA1(6adcdab9e4647429dd8deb73146264746b5eccda) )
//  ROM_LOAD( "font400.bin", 0x0800, 0x1000, BAD_DUMP CRC(56c5d2bc) SHA1(fea655ff5eedacf8978fa3c185485db44376e24d) )
ROM_END

ROM_START( mz2200 )
	ROM_REGION( 0x800, "ipl", 0 )
	ROM_LOAD( "mz2200ipl.bin", 0x0000, 0x0800, CRC(476801e8) SHA1(6b1f0620945c5492475ea1694bd09a3fcf88549d) )

	ROM_REGION( 0x800, "chargen", 0 )
//  ROM_LOAD( "mzfont.rom", 0x0000, 0x0800, BAD_DUMP CRC(0631efc3) SHA1(99b206af5c9845995733d877e9e93e9681b982a8) ) //original has JP characters
	/* these are hand-crafted roms, converted from bmps floating around the net */
	ROM_LOAD( "font.bin",    0x0000, 0x0800, BAD_DUMP CRC(6ae6ce8e) SHA1(6adcdab9e4647429dd8deb73146264746b5eccda) )
//  ROM_LOAD( "font400.bin", 0x0800, 0x1000, BAD_DUMP CRC(56c5d2bc) SHA1(fea655ff5eedacf8978fa3c185485db44376e24d) )
ROM_END

} // anonymous namespace


COMP( 1981, mz80b,  0,      0,      mz80b,   mz80be, mz80b_state,  empty_init, "Sharp", "MZ-80B",  MACHINE_NOT_WORKING )
COMP( 1982, mz2000, 0,      0,      mz2000,  mz80bj, mz2000_state, empty_init, "Sharp", "MZ-2000", MACHINE_NOT_WORKING )
COMP( 1983, mz2200, mz2000, 0,      mz2200,  mz80bj, mz2200_state, empty_init, "Sharp", "MZ-2200", MACHINE_NOT_WORKING ) // Released in July 17 1983



mz2500.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
/**************************************************************************************************

Sharp MZ-2500 (c) 1985 Sharp Corporation

Computer box contains 2 floppy drives on the right, and a front-loading cassette player on the left.
Keyboard is separate, and the key layout is very similar to the fm7.

TODO:
- Kanji text is cutted in half when font_size is 1 / interlace is disabled, different ROM used?
  (check Back to the Future);
- Add remaining missing peripherals, SIO, HDD and w1300a network;
- reverse / blanking tvram attributes;
- Implement backward compatibility with MZ-2000/MZ-80B;
- Implement expansion box unit;
- Remaining SIO ports (DA-25 and DE-9);
- mouse: missing select line from OPN Port A;
- dustbx**: cannot detect mouse reliably from bootstrap, works in main menu regardless;

**************************************************************************************************/

#include "emu.h"
#include "mz2500.h"

#include "softlist_dev.h"


/* machine stuff */

#define WRAM_RESET 0
#define IPL_RESET 1

static constexpr u8 bank_reset_val[2][8] =
{
	{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 },
	{ 0x34, 0x35, 0x36, 0x37, 0x04, 0x05, 0x06, 0x07 }
};

/* video stuff */

void mz2500_state::video_start()
{
	std::fill(std::begin(m_cg_reg), std::end(m_cg_reg), 0);

}

/*
[0] xxxx xxxx tile
[1] ---- -xxx color offset
[1] ---- x--- PCG combine mode
[1] --xx ---- PCG select
[1] xx-- ---- reverse / blink attributes
[2] --xx xxxx tile bank (for kanji ROMs)
[2] xx-- ---- kanji select
*/

/* helper function, to draw stuff without getting crazy with height / width conditions :) */
void mz2500_state::draw_pixel(bitmap_ind16 &bitmap,int x,int y,uint16_t  pen,u8 width,u8 height)
{
	if(width && height)
	{
		bitmap.pix(y*2+0, x*2+0) = m_palette->pen(pen);
		bitmap.pix(y*2+0, x*2+1) = m_palette->pen(pen);
		bitmap.pix(y*2+1, x*2+0) = m_palette->pen(pen);
		bitmap.pix(y*2+1, x*2+1) = m_palette->pen(pen);
	}
	else if(width)
	{
		bitmap.pix(y, x*2+0) = m_palette->pen(pen);
		bitmap.pix(y, x*2+1) = m_palette->pen(pen);
	}
	else if(height)
	{
		bitmap.pix(y*2+0, x) = m_palette->pen(pen);
		bitmap.pix(y*2+1, x) = m_palette->pen(pen);
	}
	else
		bitmap.pix(y, x) = m_palette->pen(pen);
}

void mz2500_state::draw_80x25(bitmap_ind16 &bitmap,const rectangle &cliprect,uint16_t map_addr)
{
	u8 *vram = m_tvram;
	int x,y,count,xi,yi;
	u8 *gfx_data;
	u8 y_step;
	u8 s_y;
	u8 y_height;

	count = (map_addr & 0x7ff);

	y_step = (m_text_font_reg) ? 1 : 2;
	y_height = (m_text_reg[0] & 0x10) ? 10 : 8;
	s_y = m_text_reg[9] & 0xf;

	for (y=0;y<26*y_step;y+=y_step)
	{
		for (x=0;x<80;x++)
		{
			int tile = vram[count+0x0000] & 0xff;
			int attr = vram[count+0x0800];
			int tile_bank = vram[count+0x1000] & 0x3f;
			int gfx_sel = (attr & 0x38) | (vram[count+0x1000] & 0xc0);
			int color = attr & 7;
			int inv_col = (attr & 0x40) >> 6;

			if(gfx_sel & 8) // Xevious, PCG 8 colors have priority above kanji roms
				gfx_data = m_pcg_ram.get();
			else if(gfx_sel == 0x80)
			{
				gfx_data = m_kanji_rom;
				tile|= tile_bank << 8;
				if(y_step == 2)
					tile &= 0x3ffe;
			}
			else if(gfx_sel == 0xc0)
			{
				gfx_data = m_kanji_rom;
				tile|= (tile_bank << 8);
				if(y_step == 2)
					tile &= 0x3ffe;
				tile|=0x4000;
			}
			else
			{
				gfx_data = m_pcg_ram.get();
			}

			for(yi=0;yi<8*y_step;yi++)
			{
				for(xi=0;xi<8;xi++)
				{
					u8 pen_bit[3],pen;
					int res_x,res_y;

					res_x = x*8+xi;
					res_y = y*y_height+yi-s_y;

					/* check TV window boundaries */
					if(res_x < m_tv_hs || res_x >= m_tv_he || res_y < m_tv_vs || res_y >= m_tv_ve)
						continue;

					if(gfx_sel & 0x8)
					{
						pen_bit[0] = ((gfx_data[tile*8+yi+0x1800]>>(7-xi)) & 1) ? (4+8) : 0; //G
						pen_bit[1] = ((gfx_data[tile*8+yi+0x1000]>>(7-xi)) & 1) ? (2+8) : 0; //R
						pen_bit[2] = ((gfx_data[tile*8+yi+0x0800]>>(7-xi)) & 1) ? (1+8) : 0; //B

						pen = (pen_bit[0]|pen_bit[1]|pen_bit[2]);

						//if(inv_col) { pen ^= 7; } breaks Mappy
					}
					else
						pen = (((gfx_data[tile*8+yi+((gfx_sel & 0x30)<<7)]>>(7-xi)) & 1) ^ inv_col) ? (color+8) : 0;

					if(pen)
					{
						if((res_y) >= 0 && (res_y) < 200*y_step)
							draw_pixel(bitmap,res_x,res_y,pen+(m_pal_select ? 0x00 : 0x10),0,0);
					}
				}
			}

			count++;
			count&=0x7ff;
		}
	}
}

void mz2500_state::draw_40x25(bitmap_ind16 &bitmap,const rectangle &cliprect,int plane,uint16_t map_addr)
{
	u8 *vram = m_tvram;
	int x,y,count,xi,yi;
	u8 *gfx_data;
	u8 y_step;
	u8 s_y;
	u8 y_height;

	count = (((plane * 0x400) + map_addr) & 0x7ff);

	y_step = (m_text_font_reg) ? 1 : 2;
	y_height = (m_text_reg[0] & 0x10) ? 10 : 8;
	s_y = m_text_reg[9] & 0xf;

	for (y=0;y<26*y_step;y+=y_step)
	{
		for (x=0;x<40;x++)
		{
			int tile = vram[count+0x0000] & 0xff;
			int attr = vram[count+0x0800];
			int tile_bank = vram[count+0x1000] & 0x3f;
			int gfx_sel = (attr & 0x38) | (vram[count+0x1000] & 0xc0);
			//int gfx_num;
			int color = attr & 7;
			int inv_col = (attr & 0x40) >> 6;

			if(gfx_sel & 8) // Xevious, PCG 8 colors have priority above kanji roms
				gfx_data = m_pcg_ram.get();
			else if(gfx_sel == 0x80)
			{
				gfx_data = m_kanji_rom;
				tile|= tile_bank << 8;
				if(y_step == 2)
					tile &= 0x3ffe;
			}
			else if(gfx_sel == 0xc0)
			{
				gfx_data = m_kanji_rom;
				tile|= (tile_bank << 8);
				if(y_step == 2)
					tile &= 0x3ffe;
				tile|=0x4000;
			}
			else
			{
				gfx_data = m_pcg_ram.get();
			}

			for(yi=0;yi<8*y_step;yi++)
			{
				for(xi=0;xi<8;xi++)
				{
					u8 pen_bit[3],pen;
					int res_x,res_y;

					res_x = x*8+xi;
					res_y = y*y_height+yi-s_y;

					/* check TV window boundaries */
					if(res_x < m_tv_hs || res_x >= m_tv_he || res_y < m_tv_vs || res_y >= m_tv_ve)
						continue;

					if(gfx_sel & 0x8)
					{
						pen_bit[0] = ((gfx_data[tile*8+yi+0x1800]>>(7-xi)) & 1) ? (4+8) : 0; //G
						pen_bit[1] = ((gfx_data[tile*8+yi+0x1000]>>(7-xi)) & 1) ? (2+8) : 0; //R
						pen_bit[2] = ((gfx_data[tile*8+yi+0x0800]>>(7-xi)) & 1) ? (1+8) : 0; //B

						pen = (pen_bit[0]|pen_bit[1]|pen_bit[2]);

						//if(inv_col) { pen ^= 7; } breaks Mappy
					}
					else
						pen = (((gfx_data[tile*8+yi+((gfx_sel & 0x30)<<7)]>>(7-xi)) & 1) ^ inv_col) ? (color+8) : 0;

					if(pen)
					{
						if((res_y) >= 0 && (res_y) < 200*y_step)
							draw_pixel(bitmap,res_x,res_y,pen+(m_pal_select ? 0x00 : 0x10),m_scr_x_size == 640,0);
					}
				}
			}

			count++;
			count&=0x7ff;
		}
	}
}

void mz2500_state::draw_cg4_screen(bitmap_ind16 &bitmap,const rectangle &cliprect,int pri)
{
	uint32_t count;
	u8 *vram = m_cgram;
	u8 pen,pen_bit[2];
	int x,y,xi,pen_i;
	int res_x,res_y;

	count = 0x0000;

	for(y=0;y<400;y++)
	{
		for(x=0;x<640;x+=8)
		{
			for(xi=0;xi<8;xi++)
			{
				res_x = x+xi;
				res_y = y;

				/* check window boundaries */
				//if(res_x < m_cg_hs || res_x >= m_cg_he || res_y < m_cg_vs || res_y >= m_cg_ve)
				//  continue;

				/* TODO: very preliminary, just Yukar K2 uses this so far*/
				pen_bit[0] = (vram[count + 0x00000]>>(xi)) & 1 ? 7 : 0; // B
				pen_bit[1] = (vram[count + 0x0c000]>>(xi)) & 1 ? 7 : 0; // R

				pen = 0;
				for(pen_i=0;pen_i<2;pen_i++)
					pen |= pen_bit[pen_i];

				{
					//if(pri == ((m_clut256[pen] & 0x100) >> 8))
					draw_pixel(bitmap,res_x,res_y,pen,0,0);
				}
			}
			count++;
		}
	}
}

void mz2500_state::draw_cg16_screen(bitmap_ind16 &bitmap,const rectangle &cliprect,int plane,int x_size,int pri)
{
	uint32_t count;
	u8 *vram = m_cgram; //TODO
	u8 pen,pen_bit[4];
	int x,y,xi,pen_i;
	uint32_t wa_reg;
	u8 s_x;
	u8 base_mask;
	int res_x,res_y;
	u8 cg_interlace;
	u8 pen_mask;

	base_mask = (x_size == 640) ? 0x3f : 0x1f;

	count = (m_cg_reg[0x10]) | ((m_cg_reg[0x11] & base_mask) << 8);
	wa_reg = (m_cg_reg[0x12]) | ((m_cg_reg[0x13] & base_mask) << 8);
	/* TODO: layer 2 scrolling */
	s_x = (m_cg_reg[0x0f] & 0xf);
	cg_interlace = m_text_font_reg ? 1 : 2;
	pen_mask = (m_cg_reg[0x18] >> ((plane & 1) * 4)) & 0x0f;

//  popmessage("%d %d %d %d",m_cg_hs,m_cg_he,m_cg_vs,m_cg_ve);

	for(y=0;y<200;y++)
	{
		for(x=0;x<x_size;x+=8)
		{
			for(xi=0;xi<8;xi++)
			{
				res_x = x+xi+s_x;
				res_y = y;

				/* check window boundaries */
				if(res_x < m_cg_hs || res_x >= m_cg_he || res_y < m_cg_vs || res_y >= m_cg_ve)
					continue;

				pen_bit[0] = (vram[count+0x0000+((plane & 1) * 0x2000)+(((plane & 2)>>1) * 0x10000)]>>(xi)) & 1 ? (pen_mask & 0x01) : 0; //B
				pen_bit[1] = (vram[count+0x4000+((plane & 1) * 0x2000)+(((plane & 2)>>1) * 0x10000)]>>(xi)) & 1 ? (pen_mask & 0x02) : 0; //R
				pen_bit[2] = (vram[count+0x8000+((plane & 1) * 0x2000)+(((plane & 2)>>1) * 0x10000)]>>(xi)) & 1 ? (pen_mask & 0x04) : 0; //G
				pen_bit[3] = (vram[count+0xc000+((plane & 1) * 0x2000)+(((plane & 2)>>1) * 0x10000)]>>(xi)) & 1 ? (pen_mask & 0x08) : 0; //I

				pen = 0;
				for(pen_i=0;pen_i<4;pen_i++)
					pen |= pen_bit[pen_i];

				if(pri == ((m_clut16[pen] & 0x10) >> 4) && m_clut16[pen] != 0x00 && pen_mask) //correct?
					draw_pixel(bitmap,res_x,res_y,(m_clut16[pen] & 0x0f)+0x10,(x_size == 320 && m_scr_x_size == 640),cg_interlace == 2);
			}
			count++;
			count&=((base_mask<<8) | 0xff);
			if(count > wa_reg)
				count = 0;
		}
	}
}

void mz2500_state::draw_cg256_screen(bitmap_ind16 &bitmap,const rectangle &cliprect,int plane,int pri)
{
	uint32_t count;
	u8 *vram = m_cgram;
	u8 pen,pen_bit[8];
	int x,y,xi,pen_i;
	uint32_t wa_reg;
	u8 s_x;
	u8 base_mask;
	int res_x,res_y;
	u8 cg_interlace;

	base_mask = 0x3f; //no x_size == 640

	count = (m_cg_reg[0x10]) | ((m_cg_reg[0x11] & base_mask) << 8);
	wa_reg = (m_cg_reg[0x12]) | ((m_cg_reg[0x13] & base_mask) << 8);
	/* TODO: layer 2 scrolling */
	s_x = (m_cg_reg[0x0f] & 0xf);
	cg_interlace = m_text_font_reg ? 1 : 2;

	for(y=0;y<200;y++)
	{
		for(x=0;x<320;x+=8)
		{
			for(xi=0;xi<8;xi++)
			{
				res_x = x+xi+s_x;
				res_y = y;

				/* check window boundaries */
				if(res_x < m_cg_hs || res_x >= m_cg_he || res_y < m_cg_vs || res_y >= m_cg_ve)
					continue;

				pen_bit[0] = (vram[count + 0x0000 + (((plane & 2)>>1) * 0x10000) + 0x2000]>>(xi)) & 1 ? (m_cg_reg[0x18] & 0x10) : 0; // B1
				pen_bit[1] = (vram[count + 0x0000 + (((plane & 2)>>1) * 0x10000) + 0x0000]>>(xi)) & 1 ? (m_cg_reg[0x18] & 0x01) : 0; // B0
				pen_bit[2] = (vram[count + 0x0000 + (((plane & 2)>>1) * 0x10000) + 0x6000]>>(xi)) & 1 ? (m_cg_reg[0x18] & 0x20) : 0; // R1
				pen_bit[3] = (vram[count + 0x0000 + (((plane & 2)>>1) * 0x10000) + 0x4000]>>(xi)) & 1 ? (m_cg_reg[0x18] & 0x02) : 0; // R0
				pen_bit[4] = (vram[count + 0x0000 + (((plane & 2)>>1) * 0x10000) + 0xa000]>>(xi)) & 1 ? (m_cg_reg[0x18] & 0x40) : 0; // G1
				pen_bit[5] = (vram[count + 0x0000 + (((plane & 2)>>1) * 0x10000) + 0x8000]>>(xi)) & 1 ? (m_cg_reg[0x18] & 0x04) : 0; // G0
				pen_bit[6] = (vram[count + 0x0000 + (((plane & 2)>>1) * 0x10000) + 0xe000]>>(xi)) & 1 ? (m_cg_reg[0x18] & 0x80) : 0; // I1
				pen_bit[7] = (vram[count + 0x0000 + (((plane & 2)>>1) * 0x10000) + 0xc000]>>(xi)) & 1 ? (m_cg_reg[0x18] & 0x08) : 0; // I0

				pen = 0;
				for(pen_i=0;pen_i<8;pen_i++)
					pen |= pen_bit[pen_i];

				if(pri == ((m_clut256[pen] & 0x100) >> 8))
					draw_pixel(bitmap,res_x,res_y,(m_clut256[pen] & 0xff)+0x100,m_scr_x_size == 640,cg_interlace == 2);
			}
			count++;
			count&=((base_mask<<8) | 0xff);
			if(count > wa_reg)
				count = 0;
		}
	}
}

void mz2500_state::draw_tv_screen(bitmap_ind16 &bitmap,const rectangle &cliprect)
{
	uint16_t base_addr;

	base_addr = m_text_reg[1] | ((m_text_reg[2] & 0x7) << 8);

//  popmessage("%02x",m_clut16[0]);
//  popmessage("%d %d %d %d",m_tv_hs,(m_tv_he),m_tv_vs,(m_tv_ve));

	if(m_text_col_size)
		draw_80x25(bitmap,cliprect,base_addr);
	else
	{
		int tv_mode;

		tv_mode = m_text_reg[0] >> 2;

		switch(tv_mode & 3)
		{
			case 0: //mixed 6bpp mode, TODO
				draw_40x25(bitmap,cliprect,0,base_addr);
				break;
			case 1:
				draw_40x25(bitmap,cliprect,0,base_addr);
				break;
			case 2:
				draw_40x25(bitmap,cliprect,1,base_addr);
				break;
			case 3:
				draw_40x25(bitmap,cliprect,1,base_addr);
				draw_40x25(bitmap,cliprect,0,base_addr);
				break;
			//default: popmessage("%02x %02x %02x",tv_mode & 3,m_text_reg[1],m_text_reg[2]); break;
		}
	}
}

void mz2500_state::draw_cg_screen(bitmap_ind16 &bitmap,const rectangle &cliprect,int pri)
{
	//popmessage("%02x %02x",m_cg_reg[0x0e],m_cg_reg[0x18]);

	switch(m_cg_reg[0x0e])
	{
		case 0x00:
			break;
		case 0x03:
			draw_cg4_screen(bitmap,cliprect,0);
			break;
		case 0x14:
			draw_cg16_screen(bitmap,cliprect,0,320,pri);
			draw_cg16_screen(bitmap,cliprect,1,320,pri);
			break;
		case 0x15:
			draw_cg16_screen(bitmap,cliprect,1,320,pri);
			draw_cg16_screen(bitmap,cliprect,0,320,pri);
			break;
		case 0x17:
			draw_cg16_screen(bitmap,cliprect,0,640,pri);
			break;
		case 0x1d:
			draw_cg256_screen(bitmap,cliprect,0,pri);
			break;
		case 0x97:
			draw_cg16_screen(bitmap,cliprect,2,640,pri);
			break;
		default:
			popmessage("Unsupported CG mode %02x",m_cg_reg[0x0e]);
			break;
	}
}

uint32_t mz2500_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	bitmap.fill(m_palette->pen(0), cliprect); //TODO: correct?

	if(m_screen_enable)
		return 0;

	draw_cg_screen(bitmap,cliprect,0);
	draw_tv_screen(bitmap,cliprect);
	draw_cg_screen(bitmap,cliprect,1);
	//  popmessage("%02x (%02x %02x) (%02x %02x) (%02x %02x) (%02x %02x)",m_cg_reg[0x0f],m_cg_reg[0x10],m_cg_reg[0x11],m_cg_reg[0x12],m_cg_reg[0x13],m_cg_reg[0x14],m_cg_reg[0x15],m_cg_reg[0x16],m_cg_reg[0x17]);
	//  popmessage("%02x",m_text_reg[0x0f]);


	return 0;
}

void mz2500_state::crtc_reconfigure_screen()
{
	rectangle visarea;

	if((m_cg_reg[0x0e] & 0x1f) == 0x17 || (m_cg_reg[0x0e] & 0x1f) == 0x03 || m_text_col_size)
		m_scr_x_size = 640;
	else
		m_scr_x_size = 320;

	if((m_cg_reg[0x0e] & 0x1f) == 0x03)
		m_scr_y_size = 400;
	else
		m_scr_y_size = 200 * ((m_text_font_reg) ? 1 : 2);

	visarea.set(0, m_scr_x_size - 1, 0, m_scr_y_size - 1);

	//popmessage("%d %d %d %d %02x",vs,ve,hs,he,m_cg_reg[0x0e]);

	m_screen->configure(720, 480, visarea, m_screen->frame_period().attoseconds());

	/* calculate CG window parameters here */
	m_cg_vs = m_cg_reg[0x08] | (BIT(m_cg_reg[0x09], 0)<<8);
	m_cg_ve = m_cg_reg[0x0a] | (BIT(m_cg_reg[0x0b], 0)<<8);
	m_cg_hs = (m_cg_reg[0x0c] & 0x7f)*8;
	m_cg_he = (m_cg_reg[0x0d] & 0x7f)*8;

	if(m_scr_x_size == 320)
	{
		m_cg_hs /= 2;
		m_cg_he /= 2;
	}

	/* calculate TV window parameters here */
	{
		int x_offs,y_offs;

		m_monitor_type = ((m_text_reg[0x0f] & 0x08) >> 3);

		switch((m_monitor_type|m_text_col_size<<1) & 3)
		{
			default:
			case 0: x_offs = 64; break;
			case 1: x_offs = 80; break;
			case 2: x_offs = 72; break;
			case 3: x_offs = 88; break;
		}
		//logerror("%d %d %d\n",x_offs,(m_text_reg[7] & 0x7f) * 8,(m_text_reg[8] & 0x7f)* 8);

		y_offs = (m_monitor_type) ? 76 : 34;

		m_tv_hs = ((m_text_reg[7] & 0x7f)*8) - x_offs;
		m_tv_he = ((m_text_reg[8] & 0x7f)*8) - x_offs;
		m_tv_vs = (m_text_reg[3]*2) - y_offs;
		m_tv_ve = (m_text_reg[5]*2) - y_offs;

		if(m_scr_x_size == 320)
		{
			m_tv_hs /= 2;
			m_tv_he /= 2;
		}

		if(m_scr_y_size == 200)
		{
			m_tv_vs /= 2;
			m_tv_ve /= 2;
		}
	}
}

u8 mz2500_state::cg_latch_compare()
{
	u8 compare_val = m_cg_reg[0x07] & 0xf;
	u8 pix_val;
	u8 res;
	uint16_t i;
	res = 0;

	for(i=1;i<0x100;i<<=1)
	{
		pix_val = ((m_cg_latch[0] & i) ? 1 : 0) | ((m_cg_latch[1] & i) ? 2 : 0) | ((m_cg_latch[2] & i) ? 4 : 0) | ((m_cg_latch[3] & i) ? 8 : 0);
		if(pix_val == compare_val)
			res|=i;
	}

	return res;
}

u8 mz2500_state::bank_addr_r()
{
	return m_bank_addr;
}

void mz2500_state::bank_addr_w(u8 data)
{
//  logerror("%02x\n",data);
	m_bank_addr = data & 7;
}

u8 mz2500_state::bank_data_r()
{
	u8 res;

	res = m_bank_val[m_bank_addr];

	m_bank_addr++;
	m_bank_addr&=7;

	return res;
}

void mz2500_state::bank_data_w(u8 data)
{
	m_bank_val[m_bank_addr] = data & 0x3f;
	m_rambank[m_bank_addr]->set_bank(m_bank_val[m_bank_addr]);

//  if((data*2) >= 0x70)
//  logerror("%s %02x\n",bank_name[m_bank_addr],m_bank_val[m_bank_addr]*2);

//  membank(bank_name[m_bank_addr])->set_base(&m_main_ram[m_bank_val[m_bank_addr]*0x2000]);

	m_bank_addr++;
	m_bank_addr&=7;
}

void mz2500_state::bank_mode_w(u8 data)
{
	// TODO: controls memory banking for MZ-2000/MZ-80B compatibility
}

void mz2500_state::kanji_bank_w(u8 data)
{
	m_kanji_bank = data;
}

void mz2500_state::dictionary_bank_w(u8 data)
{
	m_dic_bank = data;
}

/* 0xf4 - 0xf7 all returns vblank / hblank states */
u8 mz2500_state::crtc_hvblank_r()
{
	u8 vblank_bit, hblank_bit;

	vblank_bit = m_screen->vblank() ? 0 : 1;
	hblank_bit = m_screen->hblank() ? 0 : 2;

	return vblank_bit | hblank_bit;
}

/*
TVRAM / CRTC registers

[0x00] ---x ---- line height (0) 16 (1) 20
[0x00] ---- xx-- 40 column mode (0) 64 colors (1) screen 1 (2) screen 2 (3) screen 1 + screen 2
[0x00] ---- --x- (related to the transparent pen)
[0x01] xxxx xxxx TV map offset low address value
[0x02] ---- -xxx TV map offset high address value
[0x03] xxxx xxxx CRTC vertical start register

[0x05] xxxx xxxx CRTC vertical end register

[0x07] -xxx xxxx CRTC horizontal start register
[0x08] -xxx xxxx CRTC horizontal end register
[0x09] ---- xxxx vertical scrolling shift position
[0x0a] --GG RRBB 256 color mode
[0x0b] -r-- ---- Back plane red gradient
[0x0b] ---- -b-- Back plane blue gradient
[0x0b] ---- ---i Back plane i gradient
[0x0c] ---- ---g Back plane green gradient

[0x0f] ---- x--- sets monitor type interlace / progressive
*/

u8 mz2500_state::pal_256_param(int index, int param)
{
	u8 val = 0;

	switch(param & 3)
	{
		case 0: val = index & 0x80 ? 1 : 0; break;
		case 1: val = index & 0x08 ? 1 : 0; break;
		case 2: val = 1; break;
		case 3: val = 0; break;
	}

	return val;
}

void mz2500_state::tv_crtc_w(offs_t offset, u8 data)
{
	switch(offset)
	{
		case 0: m_text_reg_index = data; break;
		case 1:
			m_text_reg[m_text_reg_index] = data;

			#if 0
			//logerror("[%02x] <- %02x\n",m_text_reg_index,data);
			popmessage("(%02x %02x) (%02x %02x %02x %02x) (%02x %02x %02x) (%02x %02x %02x %02x)"
			,m_text_reg[0] & ~0x1e,m_text_reg[3]
			,m_text_reg[4],m_text_reg[5],m_text_reg[6],m_text_reg[7]
			,m_text_reg[8],m_text_reg[10],m_text_reg[11]
			,m_text_reg[12],m_text_reg[13],m_text_reg[14],m_text_reg[15]);

			#endif
			//popmessage("%d %02x %d %02x %d %d",m_text_reg[3],m_text_reg[4],m_text_reg[5],m_text_reg[6],m_text_reg[7]*8,m_text_reg[8]*8);

			crtc_reconfigure_screen();

			if(m_text_reg_index == 0x0a) // set 256 color palette
			{
				int i,r,g,b;
				u8 b_param,r_param,g_param;

				b_param = (data & 0x03) >> 0;
				r_param = (data & 0x0c) >> 2;
				g_param = (data & 0x30) >> 4;

				for(i = 0;i < 0x100;i++)
				{
					int bit0,bit1,bit2;

					bit0 = pal_256_param(i,b_param) ? 1 : 0;
					bit1 = i & 0x01 ? 2 : 0;
					bit2 = i & 0x10 ? 4 : 0;
					b = bit0|bit1|bit2;
					bit0 = pal_256_param(i,r_param) ? 1 : 0;
					bit1 = i & 0x02 ? 2 : 0;
					bit2 = i & 0x20 ? 4 : 0;
					r = bit0|bit1|bit2;
					bit0 = pal_256_param(i,g_param) ? 1 : 0;
					bit1 = i & 0x04 ? 2 : 0;
					bit2 = i & 0x40 ? 4 : 0;
					g = bit0|bit1|bit2;

					m_palette->set_pen_color(i+0x100,pal3bit(r),pal3bit(g),pal3bit(b));
				}
			}
			if(m_text_reg_index >= 0x80 && m_text_reg_index <= 0x8f) //Bitmap 16 clut registers
			{
				/*
				---x ---- priority
				---- xxxx clut number
				*/
				m_clut16[m_text_reg_index & 0xf] = data & 0x1f;
				//logerror("%02x -> [%02x]\n",m_text_reg[m_text_reg_index],m_text_reg_index);

				{
					int i;

					for(i=0;i<0x10;i++)
					{
						m_clut256[(m_text_reg_index & 0xf) | (i << 4)] = (((data & 0x1f) << 4) | i);
					}
				}
			}
			break;
		case 2: /* CG Mask reg (priority mixer) */
			m_cg_mask = data;
			break;
		case 3:
			/* Font size reg */
			m_text_font_reg = data & 1;
			crtc_reconfigure_screen();
			break;
	}
}

void mz2500_state::irq_sel_w(u8 data)
{
	m_irq_sel = data;
	//logerror("%02x\n",m_irq_sel);
	// activeness is trusted, see Tower of Druaga
	m_irq_mask[0] = (data & 0x08); //CRTC
	m_irq_mask[1] = (data & 0x04); //i8253
	m_irq_mask[2] = (data & 0x02); //printer
	m_irq_mask[3] = (data & 0x01); //RP5c15
}

void mz2500_state::irq_data_w(u8 data)
{
	if(m_irq_sel & 0x80)
		m_irq_vector[0] = data; //CRTC
	if(m_irq_sel & 0x40)
		m_irq_vector[1] = data; //i8253
	if(m_irq_sel & 0x20)
		m_irq_vector[2] = data; //printer
	if(m_irq_sel & 0x10)
		m_irq_vector[3] = data; //RP5c15

//  popmessage("%02x %02x %02x %02x",m_irq_vector[0],m_irq_vector[1],m_irq_vector[2],m_irq_vector[3]);
}

void mz2500_state::floppy_select_w(u8 data)
{
	m_selected_floppy = m_floppy[(data & 0x03) ^ m_fdc_reverse]->get_device();
	m_fdc->set_floppy(m_selected_floppy);

	if (m_selected_floppy)
		m_selected_floppy->mon_w(!BIT(data, 7));
}

void mz2500_state::floppy_side_w(u8 data)
{
	if (m_selected_floppy)
		m_selected_floppy->ss_w(BIT(data, 0));
}

void mz2500_state::floppy_dden_w(u8 data)
{
	m_fdc->dden_w(BIT(data, 0));
}

u8 mz2500_state::rmw_r(offs_t offset)
{
	// TODO: correct?
	if(m_cg_reg[0x0e] == 0x3)
		return 0xff;

	int plane;
	m_cg_latch[0] = m_cgram[offset+0x0000]; //B
	m_cg_latch[1] = m_cgram[offset+0x4000]; //R
	m_cg_latch[2] = m_cgram[offset+0x8000]; //G
	m_cg_latch[3] = m_cgram[offset+0xc000]; //I
	plane = m_cg_reg[0x07] & 3;

	if(m_cg_reg[0x07] & 0x10)
		return cg_latch_compare();

	return m_cg_latch[plane];
}

void mz2500_state::rmw_w(offs_t offset, u8 data)
{
	// TODO: correct?
	if(m_cg_reg[0x0e] == 0x3)
		return;

	if((m_cg_reg[0x05] & 0xc0) == 0x00) //replace
	{
		if(m_cg_reg[5] & 1) //B
		{
			m_cgram[offset+0x0000] &= ~m_cg_reg[6];
			m_cgram[offset+0x0000] |= (m_cg_reg[4] & 1) ? (data & m_cg_reg[0] & m_cg_reg[6]) : 0;
		}
		if(m_cg_reg[5] & 2) //R
		{
			m_cgram[offset+0x4000] &= ~m_cg_reg[6];
			m_cgram[offset+0x4000] |= (m_cg_reg[4] & 2) ? (data & m_cg_reg[1] & m_cg_reg[6]) : 0;
		}
		if(m_cg_reg[5] & 4) //G
		{
			m_cgram[offset+0x8000] &= ~m_cg_reg[6];
			m_cgram[offset+0x8000] |= (m_cg_reg[4] & 4) ? (data & m_cg_reg[2] & m_cg_reg[6]) : 0;
		}
		if(m_cg_reg[5] & 8) //I
		{
			m_cgram[offset+0xc000] &= ~m_cg_reg[6];
			m_cgram[offset+0xc000] |= (m_cg_reg[4] & 8) ? (data & m_cg_reg[3] & m_cg_reg[6]) : 0;
		}
	}
	else if((m_cg_reg[0x05] & 0xc0) == 0x40) //pset
	{
		if(m_cg_reg[5] & 1) //B
		{
			m_cgram[offset+0x0000] &= ~data;
			m_cgram[offset+0x0000] |= (m_cg_reg[4] & 1) ? (data & m_cg_reg[0]) : 0;
		}
		if(m_cg_reg[5] & 2) //R
		{
			m_cgram[offset+0x4000] &= ~data;
			m_cgram[offset+0x4000] |= (m_cg_reg[4] & 2) ? (data & m_cg_reg[1]) : 0;
		}
		if(m_cg_reg[5] & 4) //G
		{
			m_cgram[offset+0x8000] &= ~data;
			m_cgram[offset+0x8000] |= (m_cg_reg[4] & 4) ? (data & m_cg_reg[2]) : 0;
		}
		if(m_cg_reg[5] & 8) //I
		{
			m_cgram[offset+0xc000] &= ~data;
			m_cgram[offset+0xc000] |= (m_cg_reg[4] & 8) ? (data & m_cg_reg[3]) : 0;
		}
	}
}

u8 mz2500_state::kanji_pcg_r(offs_t offset)
{
	// kanji ROM
	if(m_kanji_bank & 0x80)
		return m_kanji_rom[(offset & 0x7ff)+((m_kanji_bank & 0x7f)*0x800)];

	// PCG RAM
	return m_pcg_ram[offset];
}

void mz2500_state::kanji_pcg_w(offs_t offset, u8 data)
{
	// PCG RAM
	if((m_kanji_bank & 0x80) == 0)
	{
		m_pcg_ram[offset] = data;
		if((offset & 0x1800) == 0x0000)
			m_gfxdecode->gfx(3)->mark_dirty((offset) >> 3);
		else
			m_gfxdecode->gfx(4)->mark_dirty((offset & 0x7ff) >> 3);
	}
	// kanji ROM is read only
}

u8 mz2500_state::dict_rom_r(offs_t offset)
{
	return m_dic_rom[(offset & 0x1fff) + ((m_dic_bank & 0x1f)*0x2000)];
}

/* sets 16 color entries out of 4096 possible combinations */
void mz2500_state::palette4096_io_w(offs_t offset, u8 data)
{
	u8 pal_index;
	u8 pal_entry;

	pal_index = (offset >> 8) & 0xff;
	pal_entry = (pal_index & 0x1e) >> 1;

	if(pal_index & 1)
		m_pal[pal_entry].g = (data & 0x0f);
	else
	{
		m_pal[pal_entry].r = (data & 0xf0) >> 4;
		m_pal[pal_entry].b = data & 0x0f;
	}

	m_palette->set_pen_color(pal_entry+0x10, pal4bit(m_pal[pal_entry].r), pal4bit(m_pal[pal_entry].g), pal4bit(m_pal[pal_entry].b));
}

u8 mz2500_state::bplane_latch_r()
{
	if(m_cg_reg[7] & 0x10)
		return cg_latch_compare();
	else
		return m_cg_latch[0];
}


u8 mz2500_state::rplane_latch_r()
{
	if(m_cg_reg[0x07] & 0x10)
	{
		u8 vblank_bit;

		vblank_bit = m_screen->vblank() ? 0 : 0x80 | m_cg_clear_flag;

		return vblank_bit;
	}
	else
		return m_cg_latch[1];
}

u8 mz2500_state::gplane_latch_r()
{
	return m_cg_latch[2];
}

u8 mz2500_state::iplane_latch_r()
{
	return m_cg_latch[3];
}

/*
"GDE" CRTC registers

0x00: CG B - Replace / Pset register data mask
0x01: CG R - Replace / Pset register data mask
0x02: CG G - Replace / Pset register data mask
0x03: CG I - Replace / Pset register data mask
0x04: CG Replace / Pset active pixel
0x05: CG Replace / Pset mode register
0x06: CG Replace data register
0x07: compare CG buffer register
0x08: CG window vertical start lo reg
0x09: CG window vertical start hi reg (upper 1 bit)
0x0a: CG window vertical end lo reg
0x0b: CG window vertical end hi reg (upper 1 bit)
0x0c: CG window horizontal start reg (7 bits, val x 8)
0x0d: CG window horizontal end reg (7 bits, val x 8)
0x0e: CG mode
0x0f: vertical scroll shift (---- xxxx)
0x10: CG map base lo reg
0x11: CG map base hi reg
0x12: CG layer 0 end address lo reg
0x13: CG layer 0 end address hi reg
0x14: CG layer 1 start address lo reg
0x15: CG layer 1 start address hi reg
0x16: CG layer 1 y pixel start lo reg
0x17: CG layer 1 y pixel start hi reg
0x18: CG color masking
*/

void mz2500_state::cg_addr_w(u8 data)
{
	m_cg_reg_index = data;
}

void mz2500_state::cg_data_w(u8 data)
{
	m_cg_reg[m_cg_reg_index & 0x1f] = data;

	if((m_cg_reg_index & 0x1f) == 0x08) //accessing VS LO reg clears VS HI reg
		m_cg_reg[0x09] = 0;

	if((m_cg_reg_index & 0x1f) == 0x0a) //accessing VE LO reg clears VE HI reg
		m_cg_reg[0x0b] = 0;

	if((m_cg_reg_index & 0x1f) == 0x05 && (m_cg_reg[0x05] & 0xc0) == 0x80) //clear bitmap buffer
	{
		uint32_t i;
		u8 *vram = m_cgram;
		uint32_t layer_bank;

		layer_bank = (m_cg_reg[0x0e] & 0x80) ? 0x10000 : 0x00000;

		/* TODO: this isn't yet 100% accurate */
		if(m_cg_reg[0x05] & 1)
		{
			for(i=0;i<0x4000;i++)
				vram[i+0x0000+layer_bank] = 0x00; //clear B
		}
		if(m_cg_reg[0x05] & 2)
		{
			for(i=0;i<0x4000;i++)
				vram[i+0x4000+layer_bank] = 0x00; //clear R
		}
		if(m_cg_reg[0x05] & 4)
		{
			for(i=0;i<0x4000;i++)
				vram[i+0x8000+layer_bank] = 0x00; //clear G
		}
		if(m_cg_reg[0x05] & 8)
		{
			for(i=0;i<0x4000;i++)
				vram[i+0xc000+layer_bank] = 0x00; //clear I
		}
		m_cg_clear_flag = 1;
	}

	{
		crtc_reconfigure_screen();
	}

	if(m_cg_reg_index & 0x80) //enable auto-inc
		m_cg_reg_index = (m_cg_reg_index & 0xfc) | ((m_cg_reg_index + 1) & 0x03);
}

void mz2500_state::timer_w(u8 data)
{
	m_pit->write_gate0(1);
	m_pit->write_gate1(1);
	m_pit->write_gate0(0);
	m_pit->write_gate1(0);
	m_pit->write_gate0(1);
	m_pit->write_gate1(1);
}


u8 mz2500_state::joystick_r()
{
	u8 res,dir_en,in_r;

	res = 0xff;
	in_r = ~m_joy[BIT(m_joy_mode, 6)]->read();

	if(m_joy_mode & 0x40)
	{
		if(!(m_joy_mode & 0x04)) res &= ~0x20;
		if(!(m_joy_mode & 0x08)) res &= ~0x10;
		dir_en = (m_joy_mode & 0x20) ? 0 : 1;
	}
	else
	{
		if(!(m_joy_mode & 0x01)) res &= ~0x20;
		if(!(m_joy_mode & 0x02)) res &= ~0x10;
		dir_en = (m_joy_mode & 0x10) ? 0 : 1;
	}

	if(dir_en)
		res &= (~((in_r) & 0x0f));

	res &= (~((in_r) & 0x30));

	return res;
}

void mz2500_state::joystick_w(u8 data)
{
	m_joy_mode = data;
	m_joy[0]->pin_6_w(BIT(data, 0));
	m_joy[0]->pin_7_w(BIT(data, 1));
	m_joy[1]->pin_6_w(BIT(data, 2));
	m_joy[1]->pin_7_w(BIT(data, 3));
	m_joy[0]->pin_8_w(BIT(data, 4));
	m_joy[1]->pin_8_w(BIT(data, 5));
}


u8 mz2500_state::kanji_r(offs_t offset)
{
	logerror("Read from kanji 2 ROM\n");

	return m_kanji2_rom[(m_kanji_index << 1) | (offset & 1)];
}

void mz2500_state::kanji_w(offs_t offset, u8 data)
{
	(offset & 1) ? (m_kanji_index = (data << 8) | (m_kanji_index & 0xff)) : (m_kanji_index = (data & 0xff) | (m_kanji_index & 0xff00));
}

u8 mz2500_state::rp5c15_8_r(offs_t offset)
{
	u8 rtc_index = (offset >> 8) & 0xff;

	return m_rtc->read(rtc_index);
}

void mz2500_state::rp5c15_8_w(offs_t offset, u8 data)
{
	u8 rtc_index = (offset >> 8) & 0xff;

	m_rtc->write(rtc_index, data);
}

template <unsigned N> u8 mz2500_state::sio_access_r(address_space &space, offs_t offset)
{
	if (N != m_sio_access_bit)
		return space.unmap();
	return m_sio->ba_cd_r(offset);
}

template <unsigned N> void mz2500_state::sio_access_w(address_space &space, offs_t offset, u8 data)
{
	if (N == m_sio_access_bit)
		m_sio->ba_cd_w(offset, data);
}

/*
 * x--- ---- Select SIO port (0) $a0-$a3 (1) $b0-$b3
 * --xx x--- Baud rate for Ch. A
 * ---- -xxx Baud rate for Ch. B
 * ---- -000 307'000 Hz / 19200 bps
 * ...
 * ---- -111 2400 Hz / 150 bps
 */
void mz2500_state::sio_setup_w(u8 data)
{
	m_sio_access_bit = !!(BIT(data, 7));

	attotime serial_clock_a = attotime::from_hz((4'000'000 / 13) / (1 << ((data >> 3) & 7)));
	attotime serial_clock_b = attotime::from_hz((4'000'000 / 13) / (1 << ((data >> 0) & 7)));

	m_sio_timer[0]->adjust(serial_clock_a, 0, serial_clock_a);
	m_sio_timer[1]->adjust(serial_clock_b, 0, serial_clock_b);
}

void mz2500_state::z80_map(address_map &map)
{
	map(0x0000, 0x1fff).m(m_rambank[0], FUNC(address_map_bank_device::amap8));
	map(0x2000, 0x3fff).m(m_rambank[1], FUNC(address_map_bank_device::amap8));
	map(0x4000, 0x5fff).m(m_rambank[2], FUNC(address_map_bank_device::amap8));
	map(0x6000, 0x7fff).m(m_rambank[3], FUNC(address_map_bank_device::amap8));
	map(0x8000, 0x9fff).m(m_rambank[4], FUNC(address_map_bank_device::amap8));
	map(0xa000, 0xbfff).m(m_rambank[5], FUNC(address_map_bank_device::amap8));
	map(0xc000, 0xdfff).m(m_rambank[6], FUNC(address_map_bank_device::amap8));
	map(0xe000, 0xffff).m(m_rambank[7], FUNC(address_map_bank_device::amap8));
}

void mz2500_state::bank_window_map(address_map &map)
{
	// 0x00-0x1f
	map(0x00000, 0x3ffff).ram().share("wram");
	// 0x20-0x2f
	map(0x40000, 0x5ffff).ram().share("cgram");
	// 0x30-0x33
	map(0x60000, 0x67fff).rw(FUNC(mz2500_state::rmw_r),FUNC(mz2500_state::rmw_w));
	// 0x34-0x37
	map(0x68000, 0x6ffff).rom().region("ipl", 0);
	// 0x38
	map(0x70000, 0x71fff).ram().share("tvram");
	// 0x39
	map(0x72000, 0x73fff).rw(FUNC(mz2500_state::kanji_pcg_r),FUNC(mz2500_state::kanji_pcg_w));
	// 0x3a
	map(0x74000, 0x75fff).r(FUNC(mz2500_state::dict_rom_r));
	// 0x3b
//  map(0x76000, 0x77fff).noprw();
	// 0x3c-0x3f
	map(0x78000, 0x7ffff).rom().region("phone", 0);
}

void mz2500_state::z80_io(address_map &map)
{
	map.unmap_value_high();
//  map(0x60, 0x63).mirror(0xff00).w(FUNC(mz2500_state::w3100a_w));
//  map(0x63, 0x63).mirror(0xff00).r(FUNC(mz2500_state::w3100a_r));
//  map(0x98, 0x99) MZ-1E35 ADPCM
	map(0xa0, 0xa3).mirror(0xff00).rw(FUNC(mz2500_state::sio_access_r<0>), FUNC(mz2500_state::sio_access_w<0>));
	map(0xb0, 0xb3).mirror(0xff00).rw(FUNC(mz2500_state::sio_access_r<1>), FUNC(mz2500_state::sio_access_w<1>));
//  map(0xa4, 0xa5) MZ-1E30 SASI
//  map(0xa8, 0xa9) ^
//  map(0xac, 0xac) MZ-1R37 EMM
//  map(0xad, 0xad) ^
	map(0xae, 0xae).select(0xff00).w(FUNC(mz2500_state::palette4096_io_w));
//  map(0xb0, 0xb3).rw(FUNC(mz2500_state::sio_r), FUNC(mz2500_state::sio_w));
	map(0xb4, 0xb4).mirror(0xff00).rw(FUNC(mz2500_state::bank_addr_r), FUNC(mz2500_state::bank_addr_w));
	map(0xb5, 0xb5).mirror(0xff00).rw(FUNC(mz2500_state::bank_data_r), FUNC(mz2500_state::bank_data_w));
	map(0xb7, 0xb7).mirror(0xff00).w(FUNC(mz2500_state::bank_mode_w));
	map(0xb8, 0xb9).mirror(0xff00).rw(FUNC(mz2500_state::kanji_r), FUNC(mz2500_state::kanji_w));
	map(0xbc, 0xbc).mirror(0xff00).r(FUNC(mz2500_state::bplane_latch_r)).w(FUNC(mz2500_state::cg_addr_w));
	map(0xbd, 0xbd).mirror(0xff00).r(FUNC(mz2500_state::rplane_latch_r)).w(FUNC(mz2500_state::cg_data_w));
	map(0xbe, 0xbe).mirror(0xff00).r(FUNC(mz2500_state::gplane_latch_r));
	map(0xbf, 0xbf).mirror(0xff00).r(FUNC(mz2500_state::iplane_latch_r));
	map(0xc6, 0xc6).mirror(0xff00).w(FUNC(mz2500_state::irq_sel_w));
	map(0xc7, 0xc7).mirror(0xff00).w(FUNC(mz2500_state::irq_data_w));
	map(0xc8, 0xc9).mirror(0xff00).rw("ym", FUNC(ym2203_device::read), FUNC(ym2203_device::write));
//  map(0xca, 0xca).mirror(0xff00).rw(FUNC(mz2500_state::voice_r), FUNC(mz2500_state::voice_w));
	// MZ-1E26
	map(0xca, 0xca).mirror(0xff00).lr8(NAME([] () { return 0x30; }));
	map(0xcc, 0xcc).select(0xff00).rw(FUNC(mz2500_state::rp5c15_8_r), FUNC(mz2500_state::rp5c15_8_w));
	map(0xcd, 0xcd).mirror(0xff00).w(FUNC(mz2500_state::sio_setup_w));
	map(0xce, 0xce).mirror(0xff00).w(FUNC(mz2500_state::dictionary_bank_w));
	map(0xcf, 0xcf).mirror(0xff00).w(FUNC(mz2500_state::kanji_bank_w));
	map(0xd8, 0xdb).mirror(0xff00).rw(m_fdc, FUNC(mb8876_device::read), FUNC(mb8876_device::write));
	map(0xdc, 0xdc).mirror(0xff00).w(FUNC(mz2500_state::floppy_select_w));
	map(0xdd, 0xdd).mirror(0xff00).w(FUNC(mz2500_state::floppy_side_w));
	map(0xde, 0xde).mirror(0xff00).w(FUNC(mz2500_state::floppy_dden_w));
	map(0xe0, 0xe3).mirror(0xff00).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xe4, 0xe7).mirror(0xff00).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xe8, 0xeb).mirror(0xff00).rw("pio", FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0xef, 0xef).mirror(0xff00).rw(FUNC(mz2500_state::joystick_r), FUNC(mz2500_state::joystick_w));
	map(0xf0, 0xf3).mirror(0xff00).w(FUNC(mz2500_state::timer_w));
	map(0xf4, 0xf7).mirror(0xff00).r(FUNC(mz2500_state::crtc_hvblank_r)).w(FUNC(mz2500_state::tv_crtc_w));
//  map(0xf8, 0xf9).?(0xff00).rw(FUNC(mz2500_state::extrom_r), FUNC(mz2500_state::extrom_w));
//  map(0xfe, 0xff).mirror(0xff00) printer
}


INPUT_CHANGED_MEMBER(mz2500_state::boot_reset_cb)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? CLEAR_LINE : ASSERT_LINE);
}

INPUT_CHANGED_MEMBER(mz2500_state::ipl_reset_cb)
{
	machine().schedule_soft_reset();
}

TIMER_CALLBACK_MEMBER(mz2500_state::ipl_timer_reset_cb)
{
	logerror("IPL reset kicked in\n");
	machine().schedule_soft_reset();
}

static INPUT_PORTS_START( mz2500 )
	// section 8 of schematics
	// CN1 switches 8-9
	PORT_START("FRONT_PANEL")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mz2500_state::boot_reset_cb), 0) PORT_NAME("Boot Reset")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mz2500_state::ipl_reset_cb), 0) PORT_NAME("IPL Reset")

	// TODO: generalize keyboard in device (mostly same as other MZ machines)
	PORT_START("KEY0")
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F6") PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F7") PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F8") PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))

	PORT_START("KEY1")
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F9") PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F10") PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("8 (PAD)") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("9 (PAD)") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME(", (PAD)") PORT_CODE(KEYCODE_COMMA_PAD)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME(". (PAD)") PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("+ (PAD)") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("- (PAD)") PORT_CODE(KEYCODE_MINUS_PAD)

	PORT_START("KEY2")
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("0 (PAD)") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("1 (PAD)") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("2 (PAD)") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("3 (PAD)") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("4 (PAD)") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("5 (PAD)") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("6 (PAD)") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("7 (PAD)") PORT_CODE(KEYCODE_7_PAD)

	PORT_START("KEY3")
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("TAB") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("BREAK") PORT_CHAR(3) //PORT_CODE(KEYCODE_ESC)

	PORT_START("KEY4")
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')

	PORT_START("KEY5")
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("KEY6")
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')

	PORT_START("KEY7")
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^')
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("\xEF\xBF\xA5") PORT_CHAR(165) PORT_CHAR('|') //Yen symbol
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("_") PORT_CHAR('_')
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("KEY8")
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("KEY9")
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')  PORT_CHAR('=')
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_UNUSED)

	PORT_START("KEYA")
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("COPY")
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("CLR")
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("INST")
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("BACKSPACE") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_TILDE) PORT_CHAR(27)
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("* (PAD)") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("/ (PAD)") PORT_CODE(KEYCODE_SLASH_PAD)

	PORT_START("KEYB")
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("GRPH")
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("SLOCK")
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("KANA")
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0xe0,IP_ACTIVE_LOW,IPT_UNUSED)

	PORT_START("KEYC")
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("KJ1")
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("KJ2")
	PORT_BIT(0xfc,IP_ACTIVE_LOW,IPT_UNUSED)

	PORT_START("KEYD")
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("LOGO KEY")
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("HELP")
	PORT_BIT(0xfc,IP_ACTIVE_LOW,IPT_UNUSED)

	PORT_START("UNUSED")
	PORT_BIT(0xff,IP_ACTIVE_LOW,IPT_UNUSED )

	/* this enables HD-loader */
	PORT_START("DSW1")
	PORT_DIPNAME( 0x01, 0x01, "DSW1" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x30, 0x30, "IPLPRO" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x30, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, "Monitor Interlace" ) //not all games support this
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )
INPUT_PORTS_END

void mz2500_state::reset_banks(u8 type)
{
	int i;

	for(i=0;i<8;i++)
	{
		m_bank_val[i] = bank_reset_val[type][i];
		m_rambank[i]->set_bank(m_bank_val[i]);
	}
}

static const gfx_layout pcg_layout_1bpp =
{
	8, 8,
	0x100,
	1,
	{ 0 },
	{ STEP8(0, 1) },
	{ STEP8(0, 8) },
	8 * 8
};

static const gfx_layout pcg_layout_3bpp =
{
	8, 8,
	0x100,
	3,
	{ 0x1800*8, 0x1000*8, 0x800*8 },
	{ STEP8(0, 1) },
	{ STEP8(0, 8) },
	8 * 8
};

void mz2500_state::machine_start()
{
	m_pcg_ram = make_unique_clear<u8[]>(0x2000);
	m_ipl_rom = memregion("ipl")->base();
	m_kanji_rom = memregion("kanji")->base();
	m_kanji2_rom = memregion("kanji2")->base();
	m_dic_rom = memregion("dictionary")->base();
	m_phone_rom = memregion("phone")->base();

	save_pointer(NAME(m_pcg_ram), 0x2000);

	m_gfxdecode->set_gfx(3, std::make_unique<gfx_element>(m_palette, pcg_layout_1bpp, m_pcg_ram.get(), 0, 0x10, 0));
	m_gfxdecode->set_gfx(4, std::make_unique<gfx_element>(m_palette, pcg_layout_3bpp, m_pcg_ram.get(), 0, 4, 0));

	std::fill(std::begin(m_cgram), std::end(m_cgram), 0x00);
	std::fill(std::begin(m_irq_pending), std::end(m_irq_pending), 0);
	std::fill(std::begin(m_irq_mask), std::end(m_irq_mask), 0);

	m_text_col_size = 0;
	m_text_font_reg = 0;

	m_kanji_bank = 0;

	m_cg_clear_flag = 0;

	m_ipl_reset_timer = timer_alloc(FUNC(mz2500_state::ipl_timer_reset_cb), this);

	m_sio_timer[0] = timer_alloc(FUNC(mz2500_state::sio_clock_cb<0>), this);
	m_sio_timer[1] = timer_alloc(FUNC(mz2500_state::sio_clock_cb<1>), this);
}

void mz2500_state::machine_reset()
{
	m_bank_addr = 0;
	reset_banks(IPL_RESET);

	m_ipl_reset_timer->adjust(attotime::never);

	m_sio_access_bit = false;
	m_dac1bit->level_w(0);

//  m_monitor_type = ioport("DSW1")->read() & 0x40 ? 1 : 0;

	joystick_w(0x3f); // LS273 reset
}

static const gfx_layout kanji_cg_layout =
{
	8, 8,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP8(0, 1) },
	{ STEP8(0, 8) },
	8 * 8
};

/* gfx1 is mostly 16x16, but there are some 8x8 characters */
static const gfx_layout kanji_8_layout =
{
	8, 8,
	1920,
	1,
	{ 0 },
	{ STEP8(0, 1) },
	{ STEP8(0, 8) },
	8 * 8
};

static const gfx_layout kanji_16_layout =
{
	16, 16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP8(0, 1), STEP8(128, 1) },
	{ STEP16(0, 8) },
	16 * 16
};

/* these are just for viewer sake, actually they aren't used in drawing routines */
static GFXDECODE_START( gfx_mz2500 )
	GFXDECODE_ENTRY("kanji", 0, kanji_cg_layout, 0, 256)
	GFXDECODE_ENTRY("kanji", 0x4400, kanji_8_layout, 0, 256)
	GFXDECODE_ENTRY("kanji", 0, kanji_16_layout, 0, 256)
//  GFXDECODE_ENTRY("pcg", 0, pcg_layout_1bpp, 0, 0x10)
//  GFXDECODE_ENTRY("pcg", 0, pcg_layout_3bpp, 0, 4)
GFXDECODE_END

INTERRUPT_GEN_MEMBER(mz2500_state::vblank_cb)
{
	if(m_irq_mask[0])
	{
		m_irq_pending[0] = 1;
		m_maincpu->set_input_line(0, ASSERT_LINE);
	}
	m_cg_clear_flag = 0;
}

IRQ_CALLBACK_MEMBER(mz2500_state::irq_ack_cb)
{
	int i;
	for(i=0;i<4;i++)
	{
		if(m_irq_mask[i] && m_irq_pending[i])
		{
			m_irq_pending[i] = 0;
			m_maincpu->set_input_line(0, CLEAR_LINE);
			return m_irq_vector[i];
		}
	}
	return 0;
}

u8 mz2500_state::ppi_porta_r()
{
	logerror("PPI PORTA R\n");

	return 0xff;
}

u8 mz2500_state::ppi_portb_r()
{
	u8 vblank_bit;

	vblank_bit = m_screen->vblank() ? 0 : 1; //Guess: NOBO wants this bit to be high/low

	return 0xfe | vblank_bit;
}

u8 mz2500_state::ppi_portc_r()
{
	logerror("PPI PORTC R\n");

	return 0xff;
}

void mz2500_state::ppi_porta_w(u8 data)
{
	logerror("PPI PORTA W %02x\n",data);
}

void mz2500_state::ppi_portb_w(u8 data)
{
	logerror("PPI PORTB W %02x\n",data);
}

void mz2500_state::ppi_portc_w(u8 data)
{
	/*
	---- x--- 0->1 transition = IPL reset
	---- -x-- beeper state
	---- --x- 0->1 transition = Work RAM reset
	---- ---x screen mask
	*/

	if(BIT(m_old_portc, 3) != BIT(data, 3))
	{
		logerror("PIO PC: IPL reset %s\n", BIT(data, 3) ? "stopped" : "started");
		// TODO: timing
		m_ipl_reset_timer->adjust(!BIT(data, 3) ? attotime::from_hz(100) : attotime::never);
	}

	if(!BIT(m_old_portc, 1) && BIT(data, 1))
	{
		logerror("PIO PC: Work RAM reset\n");
		reset_banks(WRAM_RESET);
		m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
	}

	m_old_portc = data;

	m_dac1bit->level_w(BIT(data, 2));

	m_screen_enable = data & 1;

	if(data & ~0x0f)
		logerror("PPI PORTC W %02x\n",data & ~0x0f);
}

void mz2500_state::pio_porta_w(u8 data)
{
//  logerror("%02x\n",data);

	if(m_prev_col_val != ((data & 0x20) >> 5))
	{
		m_text_col_size = ((data & 0x20) >> 5);
		m_prev_col_val = m_text_col_size;
		crtc_reconfigure_screen();
	}
	m_key_mux = data & 0x1f;
}


u8 mz2500_state::pio_porta_r()
{
	static const char *const keynames[] = { "KEY0", "KEY1", "KEY2", "KEY3",
											"KEY4", "KEY5", "KEY6", "KEY7",
											"KEY8", "KEY9", "KEYA", "KEYB",
											"KEYC", "KEYD", "UNUSED", "UNUSED" };

	if(((m_key_mux & 0x10) == 0x00) || ((m_key_mux & 0x0f) == 0x0f)) //status read
	{
		int res,i;

		res = 0xff;
		for(i=0;i<0xe;i++)
			res &= ioport(keynames[i])->read();

		m_pio_latchb = res;

		return res;
	}

	m_pio_latchb = ioport(keynames[m_key_mux & 0xf])->read();

	return ioport(keynames[m_key_mux & 0xf])->read();
}

u8 mz2500_state::opn_porta_r()
{
	return m_ym_porta;
}

void mz2500_state::opn_porta_w(u8 data)
{
	/*
	---- x--- mouse select
	---- -x-- palette bit (16/4096 colors)
	---- --x- floppy reverse bit (controls wd17xx bits in command registers)
	*/

	m_fdc_reverse = data & 2;
	m_pal_select = (data & 4) ? 1 : 0;

	m_ym_porta = data;
}

void mz2500_state::palette_init(palette_device &palette) const
{
	for (int i = 0; i < 0x200; i++)
		palette.set_pen_color(i,pal1bit(0),pal1bit(0),pal1bit(0));

	// set up 8 colors (PCG)
	for (int i = 0; i < 8; i++)
		palette.set_pen_color(i+8,pal1bit((i & 2)>>1),pal1bit((i & 4)>>2),pal1bit((i & 1)>>0));

	// set up 16 colors (PCG / CG)

	// set up 256 colors (CG)
	for (int i = 0; i < 0x100; i++)
	{
		int bit0, bit1, bit2;

		bit0 = pal_256_param(i,0) ? 1 : 0;
		bit1 = i & 0x01 ? 2 : 0;
		bit2 = i & 0x10 ? 4 : 0;
		int const b = bit0|bit1|bit2;
		bit0 = pal_256_param(i,0) ? 1 : 0;
		bit1 = i & 0x02 ? 2 : 0;
		bit2 = i & 0x20 ? 4 : 0;
		int const r = bit0|bit1|bit2;
		bit0 = pal_256_param(i,0) ? 1 : 0;
		bit1 = i & 0x04 ? 2 : 0;
		bit2 = i & 0x40 ? 4 : 0;
		int const g = bit0|bit1|bit2;

		palette.set_pen_color(i + 0x100, pal3bit(r), pal3bit(g), pal3bit(b));
	}
}

/* PIT8253 Interface */

void mz2500_state::pit8253_clk0_irq(int state)
{
	if(m_irq_mask[1] && state & 1)
	{
		m_irq_pending[1] = 1;
		m_maincpu->set_input_line(0, ASSERT_LINE);
	}
}

void mz2500_state::rtc_alarm_irq(int state)
{
	// TODO: doesn't work yet
//  if(m_irq_mask[3] && state & 1)
//      m_maincpu->set_input_line_and_vector(0, HOLD_LINE,drvm_irq_vector[3]); // Z80
}


static void mz2500_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
	device.option_add("3ssdd", FLOPPY_3_SSDD);
	device.option_add("35dd", FLOPPY_35_DD);
}

static void mouse_devices(device_slot_interface &device)
{
	device.option_add("x68k", X68K_MOUSE);
}

template <unsigned N> TIMER_CALLBACK_MEMBER(mz2500_state::sio_clock_cb)
{
	if (N)
	{
		m_sio->rxtxcb_w(0);
		m_sio->rxtxcb_w(1);
	}
	else
	{
		m_sio->rxca_w(0);
		m_sio->rxca_w(1);
		m_sio->txca_w(0);
		m_sio->txca_w(1);
	}
}


static const z80_daisy_config daisy_chain[] =
{
//  { "pio" },
	{ "sio" },
	{ nullptr }
};

void mz2500_state::mz2500(machine_config &config)
{
	Z80(config, m_maincpu, 24_MHz_XTAL / 4);
	m_maincpu->set_daisy_config(daisy_chain);
	m_maincpu->set_addrmap(AS_PROGRAM, &mz2500_state::z80_map);
	m_maincpu->set_addrmap(AS_IO, &mz2500_state::z80_io);
	m_maincpu->set_vblank_int("screen", FUNC(mz2500_state::vblank_cb));
	m_maincpu->set_irq_acknowledge_callback(FUNC(mz2500_state::irq_ack_cb));

	for (int bank = 0; bank < 8; bank++)
	{
		ADDRESS_MAP_BANK(config, m_rambank[bank]).set_map(&mz2500_state::bank_window_map).set_options(ENDIANNESS_LITTLE, 8, 16+3, 0x2000);
	}

	i8255_device &ppi(I8255(config, "ppi"));
	ppi.in_pa_callback().set(FUNC(mz2500_state::ppi_porta_r));
	ppi.out_pa_callback().set(FUNC(mz2500_state::ppi_porta_w));
	ppi.in_pb_callback().set(FUNC(mz2500_state::ppi_portb_r));
	ppi.out_pb_callback().set(FUNC(mz2500_state::ppi_portb_w));
	ppi.in_pc_callback().set(FUNC(mz2500_state::ppi_portc_r));
	ppi.out_pc_callback().set(FUNC(mz2500_state::ppi_portc_w));

	z80pio_device& pio(Z80PIO(config, "pio", 24_MHz_XTAL / 4));
	pio.in_pa_callback().set(FUNC(mz2500_state::pio_porta_r));
	pio.out_pa_callback().set(FUNC(mz2500_state::pio_porta_w));
	pio.in_pb_callback().set(FUNC(mz2500_state::pio_porta_r));

	Z80SIO(config, m_sio, 24_MHz_XTAL / 4);

	RP5C15(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->alarm().set(FUNC(mz2500_state::rtc_alarm_irq));

	PIT8253(config, m_pit);
	m_pit->set_clk<0>(24_MHz_XTAL / 768);
	m_pit->out_handler<0>().set(FUNC(mz2500_state::pit8253_clk0_irq));
	m_pit->out_handler<0>().append(m_pit, FUNC(pit8253_device::write_clk1));
	m_pit->out_handler<1>().set(m_pit, FUNC(pit8253_device::write_clk2)); //CH2, used by Super MZ demo / The Black Onyx and a few others

	MB8876(config, m_fdc, 8_MHz_XTAL / 8); // clocked by MB4107 VFO

	MSX_GENERAL_PURPOSE_PORT(config, m_joy[0], msx_general_purpose_port_devices, "joystick");
	MSX_GENERAL_PURPOSE_PORT(config, m_joy[1], msx_general_purpose_port_devices, "joystick");

	rs232_port_device &mouse(RS232_PORT(config, "mouse_port", mouse_devices, "x68k"));
	mouse.rxd_handler().set(m_sio, FUNC(z80sio_device::rxb_w));
	m_sio->out_dtrb_callback().set(mouse, FUNC(rs232_port_device::write_rts));

	FLOPPY_CONNECTOR(config, "fdc:0", mz2500_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", mz2500_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:2", mz2500_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:3", mz2500_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(42.954545_MHz_XTAL / 2, 640+108, 0, 640, 480, 0, 200); //unknown clock / divider
	m_screen->set_screen_update(FUNC(mz2500_state::screen_update));
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(mz2500_state::palette_init), 0x200);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_mz2500);

	SPEAKER(config, "mono").front_center();

	ym2203_device &ym(YM2203(config, "ym", 24_MHz_XTAL / 12));
	ym.port_a_read_callback().set(FUNC(mz2500_state::opn_porta_r));
	ym.port_b_read_callback().set_ioport("DSW1");
	ym.port_a_write_callback().set(FUNC(mz2500_state::opn_porta_w));
	ym.add_route(0, "mono", 0.25);
	ym.add_route(1, "mono", 0.25);
	ym.add_route(2, "mono", 0.50);
	ym.add_route(3, "mono", 0.50);

	SPEAKER_SOUND(config, m_dac1bit).add_route(ALL_OUTPUTS, "mono", 0.50);

	// MZ-1U09, built-in 2 slots
	for (unsigned i = 0; i < 2; i++)
	{
		MZ80_EXP_SLOT(config, m_exp[i], mz2500_exp_devices, nullptr);
		m_exp[i]->set_iospace(m_maincpu, AS_IO);
	}

	SOFTWARE_LIST(config, "flop_list").set_original("mz2500_flop");
	SOFTWARE_LIST(config, "flop_list2").set_compatible("mz2000_flop");
}



ROM_START( mz2500 )
	ROM_REGION( 0x08000, "ipl", 0 )
	ROM_LOAD( "ipl.rom", 0x00000, 0x8000, CRC(7a659f20) SHA1(ccb3cfdf461feea9db8d8d3a8815f7e345d274f7) )

	// hand made?
	ROM_REGION( 0x1000, "cgrom", 0 )
	ROM_LOAD( "cg.rom", 0x0000, 0x0800, CRC(a082326f) SHA1(dfa1a797b2159838d078650801c7291fa746ad81) )

	ROM_REGION( 0x40000, "kanji", 0 )
	ROM_LOAD( "kanji.rom", 0x0000, 0x40000, CRC(dd426767) SHA1(cc8fae0cd1736bc11c110e1c84d3f620c5e35b80) )

	ROM_REGION( 0x20000, "kanji2", 0 )
	ROM_LOAD( "kanji2.rom", 0x0000, 0x20000, CRC(eaaf20c9) SHA1(771c4d559b5241390215edee798f3bce169d418c) )

	ROM_REGION( 0x40000, "dictionary", 0 )
	ROM_LOAD( "dict.rom", 0x00000, 0x40000, CRC(aa957c2b) SHA1(19a5ba85055f048a84ed4e8d471aaff70fcf0374) )

	ROM_REGION( 0x8000, "phone", ROMREGION_ERASEFF )
	ROM_LOAD( "phone.rom", 0x00000, 0x4000, CRC(8e49e4dc) SHA1(2589f0c95028037a41ca32a8fd799c5f085dab51) )
ROM_END

ROM_START( mz2520 )
	ROM_REGION( 0x08000, "ipl", 0 )
	ROM_LOAD( "ipl2520.rom", 0x00000, 0x8000, CRC(0a126eb2) SHA1(faf71236b3ad82d30184adea951d43d10ced663d) )

	// hand made?
	ROM_REGION( 0x1000, "cgrom", 0 )
	ROM_LOAD( "cg.rom", 0x0000, 0x0800, CRC(a082326f) SHA1(dfa1a797b2159838d078650801c7291fa746ad81) )

	ROM_REGION( 0x40000, "kanji", 0 )
	ROM_LOAD( "kanji.rom", 0x0000, 0x40000, CRC(dd426767) SHA1(cc8fae0cd1736bc11c110e1c84d3f620c5e35b80) )

	ROM_REGION( 0x20000, "kanji2", 0 )
	ROM_LOAD( "kanji2.rom", 0x0000, 0x20000, CRC(eaaf20c9) SHA1(771c4d559b5241390215edee798f3bce169d418c) )

	ROM_REGION( 0x40000, "dictionary", 0 )
	ROM_LOAD( "dict.rom", 0x00000, 0x40000, CRC(aa957c2b) SHA1(19a5ba85055f048a84ed4e8d471aaff70fcf0374) )

	ROM_REGION( 0x8000, "phone", ROMREGION_ERASEFF )
	ROM_LOAD( "phone.rom", 0x00000, 0x4000, CRC(8e49e4dc) SHA1(2589f0c95028037a41ca32a8fd799c5f085dab51) )
ROM_END



COMP( 1985, mz2500, 0,      0, mz2500, mz2500, mz2500_state, empty_init, "Sharp", "MZ-2500", MACHINE_IMPERFECT_GRAPHICS )
COMP( 1985, mz2520, mz2500, 0, mz2500, mz2500, mz2500_state, empty_init, "Sharp", "MZ-2520", MACHINE_IMPERFECT_GRAPHICS ) // stripped down version of the regular MZ-2500, no MZ-2000/MZ-80B compatability and no cassette interface



mz3500.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
/***************************************************************************

MZ-3500 (c) 198? Sharp

References:
- https://sharpmz.org/mz-3500/mztdata.htm
- http://retropc.net/ohishi/museum/mz3500.htm

TODO:
- BUSREQ / BUSACK signals.
- master/slave comms aren't perfect (especially noticeable if you change the video DIP)
- Interrupt system;
- Neither available floppy disks boots (missing boot sector);
- Sound;
- Printer;
- Light pen;
- SIO & RS-232C;
- Backward compatibility with PC-3100 and PC-3200;
- Verify relationship with MZ-5500 and MZ-6500;

Notes:
Sub-CPU test meanings:
* RA (tests RAM, first is work RAM, other two are shared RAM banks)
* VR (tests VRAM)
* CRT interface test:
  - 40x20
  - 80x25
  - monochrome attribute test
  - 80x25 color test (text B-R-G-W, border: black, blue, red, green, black)
  - 40x20 color test (text B-R-G-W, border: black, blue, red, green, black)
* Speaker test
* PR (Printer interface test)
* LP (Light pen test)
* RS (RS-232C interface test)

***************************************************************************/


#include "emu.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/upd765.h"
#include "machine/i8255.h"
#include "machine/pit8253.h"
#include "sound/beep.h"
#include "video/upd7220.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

#define MAIN_CLOCK XTAL(8'000'000)

class mz3500_state : public driver_device
{
public:
	mz3500_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_master(*this, "master")
		, m_slave(*this, "slave")
		, m_hgdc1(*this, "upd7220_chr")
		, m_hgdc2(*this, "upd7220_gfx")
		, m_fdc(*this, "fdc")
		, m_video_ram(*this, "video_ram")
		, m_beeper(*this, "beeper")
		, m_palette(*this, "palette")
		, m_pit(*this, "pit")
		, m_system_dsw(*this, "SYSTEM_DSW")
		, m_fd_dsw(*this, "FD_DSW")
		, m_floppy_connector(*this, "fdc:%u", 0U)
	{ }

	void mz3500(machine_config &config);

private:
	// devices
	required_device<z80_device> m_master;
	required_device<z80_device> m_slave;
	required_device<upd7220_device> m_hgdc1;
	required_device<upd7220_device> m_hgdc2;
	required_device<upd765a_device> m_fdc;
	required_shared_ptr<u16> m_video_ram;
	required_device<beep_device> m_beeper;
	required_device<palette_device> m_palette;
	required_device<pit8253_device> m_pit;

	u8 *m_ipl_rom;
	u8 *m_basic_rom;
	std::unique_ptr<u8[]> m_work_ram;
	std::unique_ptr<u8[]> m_shared_ram;
	u8 *m_char_rom;

	u8 m_ma, m_mo, m_ms, m_me2, m_me1;
	u8 m_crtc[0x10];

	u8 m_srdy;
	int m_sack;
	bool m_old_sres;

	u8 m_fdd_sel;

	u8 master_mem_r(offs_t offset);
	void master_mem_w(offs_t offset, u8 data);
	u8 ipl_r(offs_t offset);
	u8 basic_r(offs_t offset);
	u8 work_ram_r(offs_t offset);
	void work_ram_w(offs_t offset, u8 data);
	u8 shared_ram_r(offs_t offset);
	void shared_ram_w(offs_t offset, u8 data);
	u8 io_r(offs_t offset);
	void io_w(offs_t offset, u8 data);
	void crtc_w(offs_t offset, u8 data);
	u8 fdc_r();
	void fdc_w(u8 data);
	u8 fdc_dma_r();
	void pa_w(u8 data);
	void pb_w(u8 data);
	void pc_w(u8 data);

	// screen updates
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	UPD7220_DISPLAY_PIXELS_MEMBER( hgdc_display_pixels );
	UPD7220_DRAW_TEXT_LINE_MEMBER( hgdc_draw_text );

	void master_io(address_map &map) ATTR_COLD;
	void master_map(address_map &map) ATTR_COLD;
	void slave_io(address_map &map) ATTR_COLD;
	void slave_map(address_map &map) ATTR_COLD;
	void upd7220_1_map(address_map &map) ATTR_COLD;
	void upd7220_2_map(address_map &map) ATTR_COLD;

	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual void video_start() override ATTR_COLD;

private:
	required_ioport m_system_dsw;
	required_ioport m_fd_dsw;
	required_device_array<floppy_connector, 4> m_floppy_connector;
};

void mz3500_state::video_start()
{
}

/*
CRTC regs
[0]
---- -x-- "Choice of whether attribute or cursor be put on the frame that displayed on CRT2" (???)
---- --x- CRT2 output
---- ---x CRT1 output
[1]
---- -GRB CRT1 color output
[2]
---- -GRB CRT2 color output
[3]
---- -GRB background color output
[4]
---- --x- border color mode in effect
---- ---x color mode
[5]
---- --x- width setting (0: 40 chars, 1: 80 chars)
---- ---x data size for graphics RAM (0: 8 bits, 1: 16 bits)
[6]
---- -x-- "Connection of graphic GDC"
---- --x- "Connection of the 96K bytes VRAM"
---- ---x "Connection of a 400 raster CRT"
[7]
---- ---x 0: 25 lines, 1: 20 lines display
[d]
(mirror of [5]?)
*/

UPD7220_DISPLAY_PIXELS_MEMBER( mz3500_state::hgdc_display_pixels )
{
	// ...
}

UPD7220_DRAW_TEXT_LINE_MEMBER( mz3500_state::hgdc_draw_text )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();

//  popmessage("%02x",m_crtc[6]);

	u8 color_mode = m_crtc[4] & 1;
	u8 width80 = (m_crtc[5] & 2) >> 1;
	u8 hires = (m_crtc[6] & 1);
	u8 char_size = (hires) ? 16 : 8;

	for( int x = 0; x < pitch; x++ )
	{
		int tile = (m_video_ram[(((addr+x)*2) & 0x1fff) >> 1] & 0xff);
		int attr = ((m_video_ram[(((addr+x)*2+1) & 0x3ffff) >> 1] >> 8) & 0x0f);

		//if(hires)
		//  tile <<= 1;

		for( int yi = 0; yi < lr; yi++)
		{
			u8 tile_data = m_char_rom[((tile*16+yi) & 0xfff) | (hires*0x1000)];

			for( int xi = 0; xi < 8; xi++)
			{
				int pen;

				if(yi >= char_size)
					pen = -1;
				else
				{
					if(color_mode)
						pen = (tile_data >> (7-xi)) & 1 ? (attr ^ 7) : -1;
					else
					{
						/* TODO: "highlight"  */
						//if(attr & 4)
						//  tile_data ^= 0xff;

						if(attr & 1) // VL
							tile_data |= 8;

						/* TODO: correct position of HL */
						if(attr & 2 && yi == char_size/2) // HL
							tile_data |= 0xff;

						/* TODO: blink (bit 3) */

						pen = (tile_data >> (7-xi)) & 1 ? 7 : -1;
					}
				}

				int res_x = x * 8 + xi;
				int res_y = y + yi;

				if(pen != -1)
				{
					if(!width80)
					{
						bitmap.pix(res_y, res_x*2+0) = palette[pen];
						bitmap.pix(res_y, res_x*2+1) = palette[pen];
					}
					else
						bitmap.pix(res_y, res_x) = palette[pen];
				}
			}
		}
	}

}

uint32_t mz3500_state::screen_update( screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect )
{
	bitmap.fill(m_palette->pen((m_crtc[4] & 2) ? m_crtc[3] & 7 : 0), cliprect);

	/* graphics */
	m_hgdc2->screen_update(screen, bitmap, cliprect);
	m_hgdc1->screen_update(screen, bitmap, cliprect);
	return 0;
}

u8 mz3500_state::ipl_r(offs_t offset)
{
	return m_ipl_rom[offset];
}

u8 mz3500_state::basic_r(offs_t offset)
{
	return m_basic_rom[offset];
}

u8 mz3500_state::work_ram_r(offs_t offset)
{
	return m_work_ram[offset];
}

void mz3500_state::work_ram_w(offs_t offset, u8 data)
{
	m_work_ram[offset] = data;
}


u8 mz3500_state::master_mem_r(offs_t offset)
{
	if(m_ms == 0)
	{
		if((offset & 0xe000) == 0x0000) { return ipl_r((offset & 0xfff) | 0x1000); }
		if((offset & 0xe000) == 0x2000) { return basic_r((offset & 0x1fff) | 0x2000); }
		if((offset & 0xc000) == 0x4000) { return work_ram_r((offset & 0x3fff) | 0x4000); }
		if((offset & 0xc000) == 0x8000) { return work_ram_r((offset & 0x3fff) | 0x8000); }
		if((offset & 0xc000) == 0xc000)
		{
			if(m_ma == 0x0) { return work_ram_r((offset & 0x3fff) | 0xc000); }
			if(m_ma == 0x1) { return work_ram_r((offset & 0x3fff) | 0x0000); }
			if(m_ma == 0xf) { return shared_ram_r((offset & 0x7ff)); }
		}

		logerror("Read with unmapped memory bank offset %04x MS %02x MA %02x\n",offset,m_ms,m_ma);
	}
	else if(m_ms == 1)
	{
		return ((offset & 0xf800) == 0xf800) ? shared_ram_r((offset & 0x7ff)) : work_ram_r(offset);
	}
	else if(m_ms == 2) // ROM based BASIC
	{
		if((offset & 0xe000) == 0x0000) { return basic_r(offset & 0x1fff); }
		if((offset & 0xe000) == 0x2000)
		{
			switch(m_mo)
			{
				case 0x0: return basic_r((offset & 0x1fff) | 0x2000);
				case 0x1: return basic_r((offset & 0x1fff) | 0x4000);
				case 0x2: return basic_r((offset & 0x1fff) | 0x6000);
			}
		}
		if((offset & 0xc000) == 0x4000) { return work_ram_r((offset & 0x3fff) | 0x4000); }
		if((offset & 0xc000) == 0x8000) { return work_ram_r((offset & 0x3fff) | 0x8000); }
		if((offset & 0xc000) == 0xc000)
		{
			switch(m_ma)
			{
				case 0x0: return work_ram_r((offset & 0x3fff) | 0x0c000);
				case 0x1: return work_ram_r((offset & 0x3fff) | 0x00000);
				case 0x2: return work_ram_r((offset & 0x3fff) | 0x10000);
				case 0x3: return work_ram_r((offset & 0x3fff) | 0x14000);
				case 0x4: return work_ram_r((offset & 0x3fff) | 0x18000);
				case 0x5: return work_ram_r((offset & 0x3fff) | 0x1c000);
				case 0x6: return work_ram_r((offset & 0x3fff) | 0x20000);
				case 0x7: return work_ram_r((offset & 0x3fff) | 0x24000);
				case 0x8: return work_ram_r((offset & 0x3fff) | 0x28000);
				case 0x9: return work_ram_r((offset & 0x3fff) | 0x2c000);
				case 0xa: return work_ram_r((offset & 0x3fff) | 0x30000);
				case 0xb: return work_ram_r((offset & 0x3fff) | 0x34000);
				case 0xc: return work_ram_r((offset & 0x3fff) | 0x38000);
				case 0xd: return work_ram_r((offset & 0x3fff) | 0x3c000);
				case 0xf: return shared_ram_r((offset & 0x7ff));
			}
		}

		logerror("Read with unmapped memory bank offset %04x MS %02x MA %02x MO %02x\n",offset,m_ms,m_ma,m_mo);
	}
	else if (m_ms == 3) // RAM based BASIC
	{
		if((offset & 0xe000) == 0x0000) { return work_ram_r(offset & 0x1fff); }
		if((offset & 0xe000) == 0x2000)
		{
			switch(m_mo)
			{
				case 0x0: return work_ram_r((offset & 0x1fff) | 0x2000);
				case 0x1: return work_ram_r((offset & 0x1fff) | 0xc000);
				case 0x2: return work_ram_r((offset & 0x1fff) | 0xe000);
			}

			logerror("Read with unmapped memory bank offset %04x MS %02x MO %02x\n",offset,m_ms,m_mo);
		}
		if((offset & 0xc000) == 0x4000) { return work_ram_r((offset & 0x3fff) | 0x4000); }
		if((offset & 0xc000) == 0x8000) { return work_ram_r((offset & 0x3fff) | 0x8000); }
		if((offset & 0xc000) == 0xc000)
		{
			switch(m_ma)
			{
				case 0x0: return work_ram_r((offset & 0x3fff) | 0x10000);
				case 0x1: return work_ram_r((offset & 0x3fff) | 0x14000);
				case 0x2: return work_ram_r((offset & 0x3fff) | 0x18000);
				case 0x3: return work_ram_r((offset & 0x3fff) | 0x1c000);
				case 0x4: return work_ram_r((offset & 0x3fff) | 0x20000);
				case 0x5: return work_ram_r((offset & 0x3fff) | 0x24000);
				case 0x6: return work_ram_r((offset & 0x3fff) | 0x28000);
				case 0x7: return work_ram_r((offset & 0x3fff) | 0x2c000);
				case 0x8: return work_ram_r((offset & 0x3fff) | 0x30000);
				case 0x9: return work_ram_r((offset & 0x3fff) | 0x34000);
				case 0xa: return work_ram_r((offset & 0x3fff) | 0x38000);
				case 0xb: return work_ram_r((offset & 0x3fff) | 0x3c000);
				case 0xf: return shared_ram_r((offset & 0x7ff));
			}
		}
	}


	return 0xff; // shouldn't happen
}

void mz3500_state::master_mem_w(offs_t offset, u8 data)
{
	if(m_ms == 0) // Initialize State
	{
		if((offset & 0xc000) == 0x4000) { work_ram_w((offset & 0x3fff) | 0x4000,data); return; }
		if((offset & 0xc000) == 0x8000) { work_ram_w((offset & 0x3fff) | 0x8000,data); return; }
		if((offset & 0xc000) == 0xc000)
		{
			if(m_ma == 0x0) { work_ram_w((offset & 0x3fff) | 0xc000,data); return; }
			if(m_ma == 0x1) { work_ram_w((offset & 0x3fff) | 0x0000,data); return; }
			if(m_ma == 0xf) { shared_ram_w((offset & 0x7ff),data); return; }
		}

		logerror("Write with unmapped memory bank offset %04x data %02x MS %02x MA %02x\n",offset,data,m_ms,m_ma);
	}
	else if(m_ms == 1) // System Loading & CP/M
	{
		if((offset & 0xf800) == 0xf800)
			shared_ram_w((offset & 0x7ff),data);
		else
			work_ram_w(offset,data);

		return;
	}
	else if(m_ms == 2) // ROM based BASIC
	{
		if((offset & 0xc000) == 0x4000) { work_ram_w((offset & 0x3fff) | 0x4000,data); return; }
		if((offset & 0xc000) == 0x8000) { work_ram_w((offset & 0x3fff) | 0x8000,data); return; }
		if((offset & 0xc000) == 0xc000)
		{
			switch(m_ma)
			{
				case 0x0: work_ram_w((offset & 0x3fff) | 0x0c000,data); return;
				case 0x1: work_ram_w((offset & 0x3fff) | 0x00000,data); return;
				case 0x2: work_ram_w((offset & 0x3fff) | 0x10000,data); return;
				case 0x3: work_ram_w((offset & 0x3fff) | 0x14000,data); return;
				case 0x4: work_ram_w((offset & 0x3fff) | 0x18000,data); return;
				case 0x5: work_ram_w((offset & 0x3fff) | 0x1c000,data); return;
				case 0x6: work_ram_w((offset & 0x3fff) | 0x20000,data); return;
				case 0x7: work_ram_w((offset & 0x3fff) | 0x24000,data); return;
				case 0x8: work_ram_w((offset & 0x3fff) | 0x28000,data); return;
				case 0x9: work_ram_w((offset & 0x3fff) | 0x2c000,data); return;
				case 0xa: work_ram_w((offset & 0x3fff) | 0x30000,data); return;
				case 0xb: work_ram_w((offset & 0x3fff) | 0x34000,data); return;
				case 0xc: work_ram_w((offset & 0x3fff) | 0x38000,data); return;
				case 0xd: work_ram_w((offset & 0x3fff) | 0x3c000,data); return;
				case 0xf: shared_ram_w((offset & 0x7ff),data); return;
			}
		}

		logerror("Write with unmapped memory bank offset %04x data %02x MS %02x MA %02x\n",offset,data,m_ms,m_ma);
	}
	else if (m_ms == 3) // RAM based BASIC
	{
		if((offset & 0xe000) == 0x0000) { work_ram_w(offset & 0x1fff,data); return; }
		if((offset & 0xe000) == 0x2000)
		{
			switch(m_mo)
			{
				case 0x0: work_ram_w((offset & 0x1fff) | 0x2000,data); return;
				case 0x1: work_ram_w((offset & 0x1fff) | 0xc000,data); return;
				case 0x2: work_ram_w((offset & 0x1fff) | 0xe000,data); return;
			}

			logerror("Write with unmapped memory bank offset %04x MS %02x MO %02x\n",offset,m_ms,m_mo);
		}
		if((offset & 0xc000) == 0x4000) { work_ram_w((offset & 0x3fff) | 0x4000,data); return; }
		if((offset & 0xc000) == 0x8000) { work_ram_w((offset & 0x3fff) | 0x8000,data); return; }
		if((offset & 0xc000) == 0xc000)
		{
			switch(m_ma)
			{
				case 0x0: work_ram_w((offset & 0x3fff) | 0x10000,data); return;
				case 0x1: work_ram_w((offset & 0x3fff) | 0x14000,data); return;
				case 0x2: work_ram_w((offset & 0x3fff) | 0x18000,data); return;
				case 0x3: work_ram_w((offset & 0x3fff) | 0x1c000,data); return;
				case 0x4: work_ram_w((offset & 0x3fff) | 0x20000,data); return;
				case 0x5: work_ram_w((offset & 0x3fff) | 0x24000,data); return;
				case 0x6: work_ram_w((offset & 0x3fff) | 0x28000,data); return;
				case 0x7: work_ram_w((offset & 0x3fff) | 0x2c000,data); return;
				case 0x8: work_ram_w((offset & 0x3fff) | 0x30000,data); return;
				case 0x9: work_ram_w((offset & 0x3fff) | 0x34000,data); return;
				case 0xa: work_ram_w((offset & 0x3fff) | 0x38000,data); return;
				case 0xb: work_ram_w((offset & 0x3fff) | 0x3c000,data); return;
				case 0xf: shared_ram_w((offset & 0x7ff),data); return;
			}
		}
	}
}

u8 mz3500_state::shared_ram_r(offs_t offset)
{
	return m_shared_ram[offset];
}

void mz3500_state::shared_ram_w(offs_t offset, u8 data)
{
	m_shared_ram[offset] = data;
}

/*
 * [2]
 * ---x xxx- system assign switch
 * ---- ---x "SEC" FD assign
 * [3]
 * xxx- ---- FD assign
 * ---x ---- slave CPU Ready signal
 * ---- x--- slave CPU ack signal
 * ---- -xxx interrupt status
 */
u8 mz3500_state::io_r(offs_t offset)
{
	switch(offset)
	{
		case 2:
			return ((m_system_dsw->read() & 0x0f) << 1) | ((m_fd_dsw->read() & 0x8) >> 3);
		case 3:
			return ((m_fd_dsw->read() & 0x7) << 5) | (m_srdy << 4) | (!m_sack << 3);
	}

	return 0;
}

/*
 * [0]
 * ---- --x- SRQ bus request from master to slave
 * ---- ---x E1
 * [1]
 * x--- ---- slave reset signal
 * ---- --xx memory system define
 * [2]
 * xxxx ---- ma bank (memory 0xc000-0xffff)
 * ---- -xxx mo bank (memory 0x2000-0x3fff)
 * [3]
 * x--- ---- me2 bank (memory 0x8000-0xbfff)
 * -x-- ---- me1 bank (memory 0x4000-0x7fff)
 */
void mz3500_state::io_w(offs_t offset, u8 data)
{
	switch(offset)
	{
		case 0:
			m_slave->set_input_line(Z80_INPUT_LINE_BUSRQ, data & 2 ? ASSERT_LINE : CLEAR_LINE);
			break;
		case 1:
			if (!BIT(m_old_sres, 7) && BIT(data, 7))
			{
				// TODO: will never reach the "no system media" with this enabled
				//m_slave->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
			}
			m_ms = data & 3;
			m_old_sres = data;
			break;
		case 2:
			m_ma = (data & 0xf0) >> 4;
			m_mo = (data & 0x07);
			break;
		case 3:
			m_me2 = (data & 0x80) >> 7;
			m_me1 = (data & 0x40) >> 6;
			break;
	}
}

void mz3500_state::crtc_w(offs_t offset, u8 data)
{
	if(offset & 8)
	{
		if(offset == 0xd)
			m_crtc[offset & 7] = data;
		else
			logerror("CRTC register access %02x\n",offset); // probably just a mirror, but who knows ...
	}
	else
		m_crtc[offset] = data;
}

/*
 * ---- -x-- Motor
 * ---- --x- Index
 * ---- ---x DRQ
 */
u8 mz3500_state::fdc_r()
{
	floppy_image_device *floppy = m_floppy_connector[m_fdd_sel]->get_device();

	return 0xf8 | (floppy->mon_r() << 2) | (floppy->idx_r() << 1) | (m_fdc->get_drq() & 1);
}

/*
 * x--- ---- FDC int enable
 * -x-- ---- FDD select signal
 * --x- ---- FDC TC
 * ---x ---- motor on signal
 * ---- xxxx Select FDD 0-3 (bit-wise)
 */
void mz3500_state::fdc_w(u8 data)
{
	if(data & 0x40)
	{
		for(int i = 0; i < 4; i++)
		{
			if(data & 1 << i)
			{
				m_fdd_sel = i;
				break;
			}
		}
	}

	m_floppy_connector[m_fdd_sel]->get_device()->mon_w(data & 0x10 ? CLEAR_LINE : ASSERT_LINE);
	m_fdc->tc_w(BIT(data, 5));
}

u8 mz3500_state::fdc_dma_r()
{
	return m_fdc->dma_r();
}

void mz3500_state::master_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(mz3500_state::master_mem_r), FUNC(mz3500_state::master_mem_w));
}

void mz3500_state::master_io(address_map &map)
{
	map.global_mask(0xff);
//  map.unmap_value_high();
//  map(0xe4, 0xe7) SFD upd765
//  map(0xe8, 0xeb) SFD I/O port and DMAC chip select
	map(0xec, 0xef).lrw8(
		NAME([this] (offs_t offset) {
			if (!machine().side_effects_disabled())
				m_master->set_input_line(0, CLEAR_LINE);
			return 0xff;
		}),
		NAME([this] (offs_t offset, u8 data) {
			(void)data;
			m_master->set_input_line(0, CLEAR_LINE);
		})
	);
	map(0xf4, 0xf5).m(m_fdc, FUNC(upd765a_device::map)); // MFD upd765
//  map(0xf8, 0xfb) MFD I/O port
	map(0xf8, 0xf8).rw(FUNC(mz3500_state::fdc_r), FUNC(mz3500_state::fdc_w));
	map(0xf9, 0xf9).r(FUNC(mz3500_state::fdc_dma_r));
	map(0xfc, 0xff).rw(FUNC(mz3500_state::io_r), FUNC(mz3500_state::io_w)); // memory mapper
}

void mz3500_state::slave_map(address_map &map)
{
	map(0x0000, 0x1fff).rom().region("ipl", 0);
	map(0x2000, 0x27ff).rw(FUNC(mz3500_state::shared_ram_r), FUNC(mz3500_state::shared_ram_w));
	map(0x4000, 0x5fff).ram();
}

void mz3500_state::slave_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x0f).lrw8(
		NAME([this] (offs_t offset) {
			if (!machine().side_effects_disabled())
				m_master->set_input_line(0, ASSERT_LINE);
			return 0xff;
		}),
		NAME([this] (offs_t offset, u8 data) {
			(void)data;
			m_master->set_input_line(0, ASSERT_LINE);
		})
	);
//  map(0x10, 0x1f) i8251
	map(0x20, 0x23).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x30, 0x33).rw("i8255", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x40, 0x40).portr("DSW");
	map(0x50, 0x5f).ram().w(FUNC(mz3500_state::crtc_w));
	map(0x60, 0x61).rw(m_hgdc2, FUNC(upd7220_device::read), FUNC(upd7220_device::write));
	map(0x70, 0x71).rw(m_hgdc1, FUNC(upd7220_device::read), FUNC(upd7220_device::write));
}

void mz3500_state::pa_w(u8 data)
{
	// printer data
}

void mz3500_state::pb_w(u8 data)
{
	/*
	x--- ---- CG select (ROM and/or upd7220 clock?)
	-x-- ---- SRDY signal (to master)
	--xx xxxx upd1990 RTC (CLK, Din, C2, C1, C0, STRB)

	*/
	//logerror("%02x PB\n",data);

	m_srdy = (data & 0x40) >> 6;
}

void mz3500_state::pc_w(u8 data)
{
	/*
	x--- ---- printer OBF output
	-x-- ---- printer ACK input
	--x- ---- printer STROBE
	---x ---- buzzer
	---- -xxx Keyboard (ACKC, STC, DC)
	*/
	//logerror("%02x PC\n",data);

	m_beeper->set_state(data & 0x10);

}

static INPUT_PORTS_START( mz3500 )
	PORT_START("DSW")
	PORT_DIPNAME( 0x01, 0x00, "DSW" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, "Sub-CPU Test" ) /* hack */
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("FD_DSW")
	PORT_DIPNAME( 0x01, 0x01, "FD_DSW" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("SYSTEM_DSW")
	PORT_DIPNAME( 0x01, 0x01, "SYSTEM_DSW" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, "CRT Select" )
	PORT_DIPSETTING(    0x04, "Normal Display (MZ1D01, MZ1D06)" )
	PORT_DIPSETTING(    0x00, "Hi-Res Display (MZ1D02, MZ1D03)" )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

static const gfx_layout charlayout_8x8 =
{
	8,8,
	0x100,
	1,
	{ RGN_FRAC(0,1) },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*16
};


static const gfx_layout charlayout_8x16 =
{
	8,16,
	0x100,
	1,
	{ RGN_FRAC(0,1) },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ STEP16(0,8) },
	8*16
};

static GFXDECODE_START( gfx_mz3500 )
	GFXDECODE_ENTRY( "gfx1", 0x0000, charlayout_8x8,     0, 1 )
	GFXDECODE_ENTRY( "gfx1", 0x0008, charlayout_8x8,     0, 1 )
	GFXDECODE_ENTRY( "gfx1", 0x1000, charlayout_8x16,     0, 1 )
GFXDECODE_END

void mz3500_state::machine_start()
{
	m_ipl_rom = memregion("ipl")->base();
	m_basic_rom = memregion("basic")->base();
	m_char_rom = memregion("gfx1")->base();
	m_work_ram = make_unique_clear<u8[]>(0x40000);
	m_shared_ram = make_unique_clear<u8[]>(0x800);
}

void mz3500_state::machine_reset()
{
	/* init memory bank states */
	m_ms = 0;
	m_ma = 0;
	m_mo = 0;
	m_me1 = 0;
	m_me2 = 0;
	//m_slave->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
	m_srdy = 0;
	m_sack = 1;
	m_old_sres = 0;

	m_fdd_sel = 0;
	for(auto & elem : m_floppy_connector)
	{
		elem->get_device()->mon_w(ASSERT_LINE);
		elem->get_device()->set_rpm(300);
	}

	m_fdc->set_rate(250000);

	m_beeper->set_state(0);
}


void mz3500_state::upd7220_1_map(address_map &map)
{
	map.global_mask(0x1fff);
	map(0x00000, 0x00fff).ram().share("video_ram");
}

void mz3500_state::upd7220_2_map(address_map &map)
{
	map(0x00000, 0x3ffff).ram(); // .share("video_ram_2");
}

static void mz3500_floppies(device_slot_interface &device)
{
	// MZ-3530/MZ-3531
	device.option_add("525ssdd", FLOPPY_525_SSDD);
	// MZ-3540/MZ-3541, requires dip option
	device.option_add("525dd", FLOPPY_525_DD);
}

// TODO: clocks
void mz3500_state::mz3500(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_master, MAIN_CLOCK/2);
	m_master->set_addrmap(AS_PROGRAM, &mz3500_state::master_map);
	m_master->set_addrmap(AS_IO, &mz3500_state::master_io);

	Z80(config, m_slave, MAIN_CLOCK/2);
	m_slave->set_addrmap(AS_PROGRAM, &mz3500_state::slave_map);
	m_slave->set_addrmap(AS_IO, &mz3500_state::slave_io);
	m_slave->busack_cb().set([this] (int state) { m_sack = state; });

	config.set_perfect_quantum(m_master);

	PIT8253(config, m_pit);
	m_pit->set_clk<0>(2'450'760);
	m_pit->set_clk<1>(2'450'760);
	// TODO: ch. 1 for DAC1BIT
	m_pit->set_clk<2>(2'450'760);
	m_pit->out_handler<2>().set([this] (int state) { m_pit->write_gate1(state); });

	i8255_device &ppi(I8255A(config, "i8255"));
	ppi.out_pa_callback().set(FUNC(mz3500_state::pa_w));
	ppi.out_pb_callback().set(FUNC(mz3500_state::pb_w));
	ppi.out_pc_callback().set(FUNC(mz3500_state::pc_w));

	UPD765A(config, m_fdc, 8'000'000, true, true);
	m_fdc->intrq_wr_callback().set_inputline(m_master, INPUT_LINE_IRQ0);
	FLOPPY_CONNECTOR(config, "fdc:0", mz3500_floppies, "525ssdd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", mz3500_floppies, "525ssdd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:2", mz3500_floppies, "525ssdd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:3", mz3500_floppies, "525ssdd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	UPD7220(config, m_hgdc1, MAIN_CLOCK/5);
	m_hgdc1->set_addrmap(0, &mz3500_state::upd7220_1_map);
	m_hgdc1->set_draw_text(FUNC(mz3500_state::hgdc_draw_text));
	m_hgdc1->vsync_wr_callback().set(m_hgdc2, FUNC(upd7220_device::ext_sync_w));

	UPD7220(config, m_hgdc2, MAIN_CLOCK/5);
	m_hgdc2->set_addrmap(0, &mz3500_state::upd7220_2_map);
	m_hgdc2->set_display_pixels(FUNC(mz3500_state::hgdc_display_pixels));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.set_screen_update(FUNC(mz3500_state::screen_update));
	screen.set_size(32*8, 32*8);
	screen.set_visarea(0*8, 32*8-1, 0*8, 32*8-1);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_mz3500);

	PALETTE(config, m_palette, palette_device::BRG_3BIT);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	BEEP(config, m_beeper, 2400).add_route(ALL_OUTPUTS, "mono", 0.15);
}


/***************************************************************************

  Game driver(s)

***************************************************************************/

ROM_START( mz3500 )
	ROM_REGION( 0x2000, "ipl", ROMREGION_ERASE00 )
	ROM_LOAD( "mz-3500_ipl-rom_2-0a_m5l2764k.bin", 0x000000, 0x002000, CRC(119708b9) SHA1(de81979608ba6ab76f09088a92bfd1a5bc42530e) )

	ROM_REGION( 0x8000, "basic", ROMREGION_ERASE00 )
	ROM_LOAD( "basic.rom", 0x00000, 0x8000, NO_DUMP )

	ROM_REGION( 0x2000, "gfx1", ROMREGION_ERASE00 )
	ROM_LOAD( "mz-3500_cg-rom_2-b_m5l2764k.bin", 0x000000, 0x002000, CRC(29f2f80a) SHA1(64b307cd9de5a3327e3ec9f3d0d6b3485706f436) )
ROM_END

} // anonymous namespace


COMP( 1982, mz3500, 0, 0, mz3500, mz3500, mz3500_state, empty_init, "Sharp", "MZ-3500", MACHINE_NO_SOUND | MACHINE_NOT_WORKING ) // released in November '82



mz5500.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/**************************************************************************************************

Sharp MZ-6500

TODO:
- keyboard (from MCU thru PIO, serial i/f)
- "ERROR 06" during selfcheck (r/w from <reserved> "I/O slot" $f5 port);
- mz5500: "ERROR 12" when testing floppies;
- mz6550: hangs for missing second CTC;
- mz6550: hookup EMM to bus slot (tested in selfcheck, "ERROR 05");

**************************************************************************************************/

#include "emu.h"
#include "cpu/i86/i86.h"
#include "cpu/i86/i286.h"
#include "imagedev/floppy.h"
#include "machine/am9517a.h"
#include "machine/bankdev.h"
#include "machine/i8255.h"
#include "machine/pic8259.h"
#include "machine/rp5c01.h"
#include "machine/upd765.h"
#include "machine/z80ctc.h"
#include "sound/ay8910.h"
#include "video/upd7220.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

namespace {

class mz5500_state : public driver_device
{
public:
	mz5500_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pic(*this, "pic_%u", 1U)
		, m_dmac(*this, "dmac")
		, m_ctc(*this, "ctc")
		, m_rtc(*this, "rtc")
		, m_hgdc(*this, "upd7220")
		, m_vram(*this, "videoram")
		, m_palette(*this, "palette")
		, m_user_bank(*this, "user_bank")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0U)
		, m_psg(*this, "psg")
	{ }

	void mz5500(machine_config &config);
	DECLARE_INPUT_CHANGED_MEMBER(nmi_reset_cb);

protected:
	required_device<cpu_device> m_maincpu;
	required_device_array<pic8259_device, 2>  m_pic;
	required_device<am9517a_device> m_dmac;
	required_device<z80ctc_device> m_ctc;
	required_device<rp5c01_device> m_rtc;
	required_device<upd7220_device> m_hgdc;
	required_shared_ptr<u16> m_vram;
	required_device<palette_device> m_palette;
	required_device<address_map_bank_device> m_user_bank;
	required_device<upd765a_device> m_fdc;
	optional_device_array<floppy_connector, 4> m_floppy;
	floppy_image_device *m_current_floppy;
	required_device<ay8912_device> m_psg;

	void io_map(address_map &map) ATTR_COLD;
	virtual void mem_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;
private:
	u8 vram_r(offs_t offset);
	void vram_w(offs_t offset, u8 data);
	void user_map(address_map &map);

	UPD7220_DISPLAY_PIXELS_MEMBER( hgdc_display_pixels );
	void upd7220_map(address_map &map) ATTR_COLD;

	void psg_porta_w(u8 data);

	u8 dma_read_byte(offs_t offset);
	void dma_write_byte(offs_t offset, u8 data);
	void set_dma_channel(int channel, int state);

	u8 m_dma_offset[4];
	u8 m_dma_channel;
};

class mz6500_state : public mz5500_state
{
public:
	mz6500_state(const machine_config &mconfig, device_type type, const char *tag)
		: mz5500_state(mconfig, type, tag)
	{ }

	void mz6500(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
};


class mz6550_state : public mz6500_state
{
public:
	mz6550_state(const machine_config &mconfig, device_type type, const char *tag)
		: mz6500_state(mconfig, type, tag)
	{ }

	void mz6550(machine_config &config);

private:
	virtual void mem_map(address_map &map) override ATTR_COLD;
};

UPD7220_DISPLAY_PIXELS_MEMBER( mz5500_state::hgdc_display_pixels )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	int const gfx[3] = { m_vram[(address + 0x00000)], m_vram[(address + 0x8000)], m_vram[(address + 0x10000)] };

	for(u8 i=0; i<16; i++)
	{
		u8 pen = (BIT(gfx[0], i)) | (BIT(gfx[1], i) << 1) | (BIT(gfx[2], i) << 2);

		bitmap.pix(y, x + i) = palette[pen];
	}
}


u8 mz5500_state::vram_r(offs_t offset)
{
	return m_vram[offset >> 1] >> ((offset & 1) ? 8 : 0);
}

void mz5500_state::vram_w(offs_t offset, u8 data)
{
	int mask = (offset & 1) ? 8 : 0;
	offset >>= 1;
	m_vram[offset] &= 0xff00 >> mask;
	m_vram[offset] |= data << mask;
}

void mz5500_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x9ffff).ram();
	map(0xa0000, 0xbffff).m(m_user_bank, FUNC(address_map_bank_device::amap8));
	map(0xc0000, 0xeffff).rw(FUNC(mz5500_state::vram_r), FUNC(mz5500_state::vram_w));
	map(0xfc000, 0xfffff).rom().region("ipl", 0);
}

void mz5500_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x000f).rw(m_dmac, FUNC(am9517a_device::read), FUNC(am9517a_device::write));
	map(0x0010, 0x0013).mirror(0xc).rw("pio", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x0020, 0x0021).mirror(0xe).m(m_fdc, FUNC(upd765a_device::map));
	map(0x0030, 0x0033).mirror(0xc).rw(m_pic[0], FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x0040, 0x0043).mirror(0xc).rw(m_pic[1], FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x0050, 0x005f).lw8(NAME([this] (u8 data) {
		// TODO: no way to select different segments?
		for (int ch = 0; ch < 4; ch ++)
			m_dma_offset[ch] = data >> 4;
	}));
	map(0x0060, 0x0061).portr("SYSA");
//  map(0x0070, 0x0070) system port C
//  map(0x00cd, 0x00cd) MZ-1R32
	map(0x0100, 0x0103).mirror(0xc).rw(m_hgdc, FUNC(upd7220_device::read), FUNC(upd7220_device::write)).umask16(0x00ff);
//  map(0x0110, 0x011f) video address / data registers (priority)
//  map(0x0120, 0x012f) video registers
//  map(0x0130, 0x013f) video register
//  map(0x0140, 0x015f) palette pens
//  map(0x0200, 0x020f) z80sio
	// TODO: second ctc at 0x214-0x217, just one for MZ-5500
	map(0x0210, 0x0213).mirror(0x8).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x0220, 0x022f).rw(m_rtc, FUNC(rp5c01_device::read), FUNC(rp5c01_device::write));
	map(0x0230, 0x0231).mirror(0xe).rw(m_psg, FUNC(ay8912_device::data_r), FUNC(ay8912_device::data_address_w));
//  map(0x0240, 0x0240) z80ctc vector ack
//  map(0x0250, 0x0250) z80sio vector ack
//  map(0x0270, 0x0270) system port B
}

void mz5500_state::user_map(address_map &map)
{
	map.unmap_value_high();
//  map(0x60000, 0x7ffff) MZ-1R32 EMM
	map(0x80000, 0x9ffff).rom().region("dictionary", 0x20000);
	map(0xa0000, 0xbffff).rom().region("dictionary", 0x00000);
	map(0xc0000, 0xdffff).rom().region("kanji", 0x20000);
	map(0xe0000, 0xfffff).rom().region("kanji", 0x00000);
}

void mz6550_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	mz6500_state::mem_map(map);
	map(0x0f8000, 0x0fffff).rom().region("ipl", 0);
	map(0xff8000, 0xffffff).rom().region("ipl", 0);
}

INPUT_CHANGED_MEMBER(mz5500_state::nmi_reset_cb)
{
	if (newval)
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}


// TODO: mz5500 is slightly different
// (SW3-4-5 was originally a drive selection option)
static INPUT_PORTS_START( mz6500 )
	PORT_START("SYSA")
	PORT_DIPNAME( 0x01, 0x01, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	// /RSTSW, front panel
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mz5500_state::nmi_reset_cb), 0) PORT_NAME("Reset Switch")
	PORT_DIPNAME( 0x04, 0x04, "Display resolution" ) PORT_DIPLOCATION("SW:1")
	PORT_DIPSETTING(    0x04, "High resolution (400)" )
	PORT_DIPSETTING(    0x00, "Medium resolution (200)" )
	PORT_DIPNAME( 0x08, 0x08, "Selfcheck mode" ) PORT_DIPLOCATION("SW:2")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	// TODO: "fixed to ON" for MZ-5600, but definitely used in MZ-6500
	// MZ-5500 uses these SW in following way:
	// 3 4 5
	// 0 0 0 Standard MFD drive (54B) DT/DD, 1F02,07,09
	// 1 0 0 MZ-80BF
	// * * * <reserved>
	PORT_DIPNAME( 0x10, 0x10, "Floppy density" ) PORT_DIPLOCATION("SW:3")
	PORT_DIPSETTING(    0x10, "2HD" )
	PORT_DIPSETTING(    0x00, "2D/2DD" )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) ) PORT_DIPLOCATION("SW:4") // MZ-5600 "fixed to OFF"
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, "CPU clock" ) PORT_DIPLOCATION("SW:5")
	PORT_DIPSETTING(    0x40, "8 MHz" )
	PORT_DIPSETTING(    0x00, "5 MHz" )
	PORT_DIPNAME( 0x80, 0x00, "Use 8087 numerical coprocessor" ) PORT_DIPLOCATION("SW:6")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_BIT( 0xff00, IP_ACTIVE_LOW, IPT_UNUSED )

	// SW7 and SW8 at $270, "user-defined dips"
INPUT_PORTS_END


void mz5500_state::machine_start()
{
	m_fdc->set_rate(250000);
	m_current_floppy = nullptr;
}

void mz6500_state::machine_start()
{
	mz5500_state::machine_start();
	m_fdc->set_rate(500000);
}


void mz5500_state::machine_reset()
{
}


static void mz6500_floppies(device_slot_interface &device)
{
	device.option_add("525hd", FLOPPY_525_HD);
	device.option_add("525dd", FLOPPY_525_DD);
	device.option_add("525ssdd", FLOPPY_525_SSDD);
}

void mz5500_state::upd7220_map(address_map &map)
{
	map(0x00000, 0x3ffff).ram().share("videoram");
}

uint8_t mz5500_state::dma_read_byte(offs_t offset)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	offs_t addr = (m_dma_offset[m_dma_channel] << 16) | offset;
	return program.read_byte(addr);
}


void mz5500_state::dma_write_byte(offs_t offset, uint8_t data)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	offs_t addr = (m_dma_offset[m_dma_channel] << 16) | offset;
//  printf("%05x %d -> %02x\n", addr, m_dma_channel, data);
	program.write_byte(addr, data);
}

void mz5500_state::set_dma_channel(int channel, int state)
{
	if (!state) m_dma_channel = channel;
}


void mz5500_state::psg_porta_w(u8 data)
{
	// SL0-SL3
	// TODO: error 13 without this for drives B (first time) and C (second time onward)
	// However both msdos211 and cpm86 will be unhappy with this.
	//if ((data & 0xf) == 0)
	//{
	//  m_current_floppy = nullptr;
	//  m_fdc->set_floppy(nullptr);
	//}
	//else
	{
		for (int sl_i = 0; sl_i < 4; sl_i ++)
		{
			// Assume multiselect not doable
			if (BIT(data, sl_i))
			{
				m_current_floppy = m_floppy[sl_i]->get_device();
				m_fdc->set_floppy(m_current_floppy);

				if (m_current_floppy != nullptr)
				{
					// /MOTOR ON
					m_current_floppy->mon_w(BIT(data, 4));
				}
				break;
			}
		}
	}

	// TODO: MA0-MA1 also sends ir7 traps
	// m_pic[0]->ir7_w(BIT(!data, 5));
	// m_pic[1]->ir7_w(BIT(!data, 6));
	m_user_bank->set_bank((data & 0xe0) >> 5);
}


void mz5500_state::mz5500(machine_config &config)
{
	// MZ-5500: 5 MHz
	// MZ-5600 onward: 8 or 5 MHz modes, user settable
	// TODO: clocks for peripherals
	I8086(config, m_maincpu, 8000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &mz5500_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &mz5500_state::io_map);
	m_maincpu->set_irq_acknowledge_callback(m_pic[0], FUNC(pic8259_device::inta_cb));

	ADDRESS_MAP_BANK(config, m_user_bank).set_map(&mz5500_state::user_map).set_options(ENDIANNESS_LITTLE, 8, 20, 0x20000);

	PIC8259(config, m_pic[0], 0);
	m_pic[0]->out_int_callback().set_inputline(m_maincpu, 0);
	m_pic[0]->in_sp_callback().set_constant(1);
	m_pic[0]->read_slave_ack_callback().set(m_pic[1], FUNC(pic8259_device::acknowledge));

	PIC8259(config, m_pic[1], 0);
	m_pic[1]->out_int_callback().set(m_pic[0], FUNC(pic8259_device::ir6_w));
	m_pic[1]->in_sp_callback().set_constant(0);

	AM9517A(config, m_dmac, 5000000);
	m_dmac->dreq_active_low();
	m_dmac->out_hreq_callback().set([this] (int state) {
		m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);
		m_dmac->hack_w(state);
	});
	m_dmac->out_eop_callback().set([this] (int state) { m_fdc->tc_w(state);});
	m_dmac->in_memr_callback().set(FUNC(mz5500_state::dma_read_byte));
	m_dmac->out_memw_callback().set(FUNC(mz5500_state::dma_write_byte));
	// ch. 0: HDD
	m_dmac->in_ior_callback<1>().set(m_fdc, FUNC(upd765a_device::dma_r));
	m_dmac->out_iow_callback<1>().set(m_fdc, FUNC(upd765a_device::dma_w));
	// ch. 2: memory refresh (from CTC ZC0)
	// ch. 3: user selectable
	m_dmac->out_dack_callback<0>().set([this] (int state) { set_dma_channel(0, state); });
	m_dmac->out_dack_callback<1>().set([this] (int state) { set_dma_channel(1, state); });
	m_dmac->out_dack_callback<2>().set([this] (int state) { set_dma_channel(2, state); });
	m_dmac->out_dack_callback<3>().set([this] (int state) { set_dma_channel(3, state); });

	i8255_device &pio(I8255A(config, "pio"));
//  pio.out_pa_callback() Data output to centronics (negated)
	/*
	 * x--- ---- FDC motor on status input
	 * -x-- ---- SIO /CI
	 * --x- ---- SIO /CD
	 * ---x ---- Keyboard SRK (output request)
	 * ---- x--- Keyboard DK (data bit)
	 * ---- -x-- Centronics PDTR (select signal)
	 * ---- --x- Centronics /PE
	 * ---- ---x Centronics /BUSY
	 */
	pio.in_pb_callback().set([this] () {
		u8 res = 0;

		if (m_current_floppy != nullptr)
			res |= m_current_floppy->mon_r() << 7;
		return res;
	});
//  pio.in_pc_callback().set([this] () {
//      return 0;
//  });
	/*
	 * -x-- ---- Centronics /ACK input
	 * --x- ---- Centronics /STROBE output
	 * ---- x--- Trap IRQ for Centronics /ACK
	 * ---- -x-- BSC external clock /EXCLK EN
	 * ---- --x- Keyboard STC (strobe) output
	 * ---- ---x Keyboard DC (data bit) output
	 */
	pio.out_pc_callback().set([this] (u8 data) {
		logerror("PIO PORTC: %02x\n", data);
	});

	Z80CTC(config, m_ctc, 5000000 / 2);
	m_ctc->intr_callback().set(m_pic[0], FUNC(pic8259_device::ir5_w));

	RP5C01(config, m_rtc, XTAL(32'768));
	m_rtc->out_alarm_callback().set(m_pic[1], FUNC(pic8259_device::ir0_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("upd7220", FUNC(upd7220_device::screen_update));
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	PALETTE(config, "palette").set_entries(8);

	// TODO: should reach ~56 Hz
	UPD7220(config, m_hgdc, 5000000 / 2);
	m_hgdc->set_addrmap(0, &mz5500_state::upd7220_map);
	m_hgdc->set_display_pixels(FUNC(mz5500_state::hgdc_display_pixels));
	m_hgdc->vsync_wr_callback().set(m_pic[0], FUNC(pic8259_device::ir0_w));

	// wants ready for detecting if disk is in.
	UPD765A(config, m_fdc, 5000000, true, true);
	m_fdc->intrq_wr_callback().set(m_pic[1], FUNC(pic8259_device::ir1_w));
	m_fdc->drq_wr_callback().set(m_dmac, FUNC(am9517a_device::dreq1_w)).invert();
	FLOPPY_CONNECTOR(config, "fdc:0", mz6500_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", mz6500_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:2", mz6500_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:3", mz6500_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	SOFTWARE_LIST(config, "flop_list").set_original("mz5500_flop");

	SPEAKER(config, "mono").front_center();

	// TODO: clock, discrete mixing
	AY8912(config, m_psg, 4000000);
	m_psg->port_a_write_callback().set(FUNC(mz5500_state::psg_porta_w));
	m_psg->add_route(ALL_OUTPUTS, "mono", 0.25);
}

void mz6500_state::mz6500(machine_config &config)
{
	mz5500_state::mz5500(config);
	m_maincpu->set_clock(8000000);

	FLOPPY_CONNECTOR(config.replace(), "fdc:0", mz6500_floppies, "525hd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config.replace(), "fdc:1", mz6500_floppies, "525hd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
}

void mz6550_state::mz6550(machine_config &config)
{
	mz6500_state::mz6500(config);

	// TODO: still 8 MHz?
	I80286(config.replace(), m_maincpu, 8000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &mz6550_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &mz6550_state::io_map);
	m_maincpu->set_irq_acknowledge_callback(m_pic[0], FUNC(pic8259_device::inta_cb));
}


// TODO: actual romlabels for all
ROM_START( mz5500 )
	ROM_REGION16_LE( 0x4000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "ipl.rom", 0x0000, 0x4000, CRC(e5d47f08) SHA1(40f8463b140c18ebd596618907e78cd9f909e7f4))

	ROM_REGION( 0x40000, "dictionary", ROMREGION_ERASEFF )
	ROM_LOAD( "dict.rom", 0x00000, 0x40000, CRC(422d8996) SHA1(6854f5f2b8a1975847b8ad85a2e038bc52f96d98))

	ROM_REGION( 0x40000, "kanji", ROMREGION_ERASEFF )
	ROM_LOAD( "kanji.rom", 0x00000, 0x40000, CRC(b618e25d) SHA1(1da93337fecde6c0f8a5bd68f3f0b3222a38d63e))
ROM_END

ROM_START( mz6500 )
	ROM_REGION16_LE( 0x4000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "ipl.rom", 0x0000, 0x4000, CRC(6c978ac4) SHA1(7872d7e6d9cda2ed9f47ed4833a5caa4dfe0e55c))

	ROM_REGION( 0x40000, "dictionary", ROMREGION_ERASEFF )
	ROM_LOAD( "dict.rom", 0x00000, 0x40000, CRC(2df3cfd3) SHA1(d420ede09658c2626b0bb650a063d88b1783e554))

	ROM_REGION( 0x40000, "kanji", ROMREGION_ERASEFF )
	ROM_LOAD( "kanji.rom", 0x00000, 0x40000, CRC(b618e25d) SHA1(1da93337fecde6c0f8a5bd68f3f0b3222a38d63e))
ROM_END

ROM_START( mz6550 )
	ROM_REGION16_LE( 0x8000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "ipl.rom", 0x0000, 0x8000, CRC(7a751f21) SHA1(4f89eb1400c72540c68fddd8ffc12d1161006fc9))

	ROM_REGION( 0x40000, "dictionary", ROMREGION_ERASEFF )
	ROM_LOAD( "dict.rom", 0x00000, 0x40000,  CRC(2df3cfd3) SHA1(d420ede09658c2626b0bb650a063d88b1783e554))

	ROM_REGION( 0x40000, "kanji", ROMREGION_ERASEFF )
	ROM_LOAD( "kanji.rom", 0x00000, 0x40000, CRC(b618e25d) SHA1(1da93337fecde6c0f8a5bd68f3f0b3222a38d63e))
ROM_END


} // Anonymous namespace



COMP( 1984, mz5500, 0,      0, mz5500, mz6500, mz5500_state, empty_init, "Sharp", "MZ-5500", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
// Released as MZ-5600 in U.K.
COMP( 1984, mz6500, mz5500, 0, mz6500, mz6500, mz6500_state, empty_init, "Sharp", "MZ-6500", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1985, mz6550, mz5500, 0, mz6550, mz6500, mz6550_state, empty_init, "Sharp", "MZ-6550", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )



mz700.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Juergen Buchmueller, Dirk Best
/******************************************************************************
 *  Sharp MZ700
 *
 *  Reference: https://original.sharpmz.org/
 *
 *  MZ700 memory map
 *
 *  0000-0FFF   1Z-013A ROM or RAM
 *  1000-CFFF   RAM
 *  D000-D7FF   videoram or RAM
 *  D800-DFFF   colorram or RAM
 *  E000-FFFF   memory mapped IO or RAM
 *
 *      xxx0    PPI8255 port A (output)
 *              bit 7   556RST (reset NE556)
 *              bit 6-4 unused
 *              bit 3-0 keyboard row demux (LS145)
 *
 *      xxx1    PPI8255 port B (input)
 *              bit 7-0 keyboard matrix code
 *
 *      xxx2    PPI8255 port C (input/output)
 *              bit 7 R -VBLANK input
 *              bit 6 R 556OUT (1.5Hz)
 *              bit 5 R RDATA from cassette
 *              bit 4 R MOTOR from cassette
 *              bit 3 W M-ON control
 *              bit 2 W INTMASK 1=enable 0=disabel clock interrupt
 *              bit 1 W WDATA to cassette
 *              bit 0 W unused
 *
 *      xxx3    PPI8255 control
 *
 *      xxx4    PIT8253 timer 0 (clock input 1,108800 MHz)
 *      xxx5    PIT8253 timer 1 (clock input 15,611 kHz)
 *      xxx6    PIT8253 timer 2 (clock input OUT1 1Hz (default))
 *      xxx7    PIT8253 control/status
 *
 *      xxx8    bit 7 R -HBLANK
 *              bit 6 R unused
 *              bit 5 R unused
 *              bit 4 R joystick JB2
 *              bit 3 R joystick JB1
 *              bit 2 R joystick JA2
 *              bit 1 R joystick JA1
 *              bit 0 R NE556 OUT (32Hz IC BJ)
 *                    W gate0 of PIT8253 (sound enable)
 *
 *  MZ800 memory map
 *
 *  0000-0FFF   ROM or RAM
 *  1000-1FFF   PCG ROM or RAM
 *  2000-7FFF   RAM
 *  8000-9FFF   videoram or RAM
 *  A000-BFFF   videoram or RAM
 *  C000-CFFF   PCG RAM or RAM
 *  D000-DFFF   videoram or RAM
 *  E000-FFFF   memory mapped IO or RAM
 *
 *  ToDo:
    - slows down while making sound
    - MZ800:
      - Port CF not done.
      - Dips not connected.
      - MZ800-mode display not working /Hi-res not coded.
      - The CRTC is a very complex custom device, mostly unemulated.
    - MZ1500:
      - Various ports not done.
      - Floppy disk and quick disk not done.
      - F4 display is blank.
      - Need manuals.

  Note: MZ800 hardware starts in memory map (mode A), but switches to MZ700
  compatibility mode (mode B) as soon as it starts up. We start in Mode B
  because it helps MZ1500 get started and it doesn't break anything.

*
 *****************************************************************************/

#include "emu.h"
#include "mz700.h"

#include "bus/mz80/mz80_exp.h"
#include "cpu/z80/z80.h"
#include "sound/sn76496.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "formats/mz_cas.h"


/***************************************************************************
    TIMER DEVICE CALLBACKS
***************************************************************************/

TIMER_DEVICE_CALLBACK_MEMBER(mz_state::ne556_cursor_callback)
{
	m_cursor_bit ^= 1;
}

TIMER_DEVICE_CALLBACK_MEMBER(mz_state::ne556_other_callback)
{
	m_other_timer ^= 1;
}


/***************************************************************************
    ADDRESS MAPS
***************************************************************************/

void mz_state::mz700_mem(address_map &map)
{
	map(0x0000, 0x0fff).bankr("bankr0").bankw("bankw0");
	map(0x1000, 0xcfff).ram();
	map(0xd000, 0xdfff).bankrw("bankd");
	map(0xe000, 0xffff).m(m_banke, FUNC(address_map_bank_device::amap8));
}

void mz_state::mz700_banke(address_map &map)
{
	// bank 0: ram (mz700_bank1)
	map(0x0000, 0x1fff).ram();
	// bank 1: devices (mz700_bank3)
	map(0x2000, 0x2003).mirror(0x1ff0).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x2004, 0x2007).mirror(0x1ff0).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x2008, 0x200b).mirror(0x1ff0).rw(FUNC(mz_state::mz700_e008_r), FUNC(mz_state::mz700_e008_w));
	map(0x200c, 0x200f).mirror(0x1ff0).noprw();
	// bank 2: switched out (mz700_bank5)
	map(0x4000, 0x5fff).noprw();
}

void mz_state::mz700_io(address_map &map)
{
	map.global_mask(0xff);
	map(0xe0, 0xe0).w(FUNC(mz_state::mz700_bank_0_w));
	map(0xe1, 0xe1).w(FUNC(mz_state::mz700_bank_1_w));
	map(0xe2, 0xe2).w(FUNC(mz_state::mz700_bank_2_w));
	map(0xe3, 0xe3).w(FUNC(mz_state::mz700_bank_3_w));
	map(0xe4, 0xe4).w(FUNC(mz_state::mz700_bank_4_w));
	map(0xe5, 0xe5).w(FUNC(mz_state::mz700_bank_5_w));
	map(0xe6, 0xe6).w(FUNC(mz_state::mz700_bank_6_w));
}

void mz800_state::mz800_mem(address_map &map)
{
	map(0x0000, 0x0fff).bankr("bankr0").bankw("bankw0");
	map(0x1000, 0x1fff).bankrw("bank1");
	map(0x2000, 0x7fff).ram();
	map(0x8000, 0xbfff).bankrw("banka");
	map(0xc000, 0xcfff).bankrw("bankc");
	map(0xd000, 0xdfff).bankrw("bankd");
	map(0xe000, 0xffff).m(m_bankf, FUNC(address_map_bank_device::amap8));
}

void mz800_state::mz800_bankf(address_map &map)
{
	// bank 0: ram (mz700_bank1)
	map(0x0000, 0x1fff).ram();
	// bank 1: devices (mz700_bank3)
	map(0x2000, 0x2003).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x2004, 0x2007).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x2008, 0x200b).rw(FUNC(mz800_state::mz700_e008_r), FUNC(mz800_state::mz700_e008_w));
	map(0x200c, 0x200f).noprw();
	map(0x2010, 0x3fff).rom().region("monitor", 0x2010);
	// bank 2: switched out (mz700_bank5)
	map(0x4000, 0x5fff).noprw();
}

void mz800_state::mz800_io(address_map &map)
{
	// Expansion I/O options may use 16-bit addressing
	map(0xcc, 0xcc).mirror(0xff00).w(FUNC(mz800_state::mz800_write_format_w));
	map(0xcd, 0xcd).mirror(0xff00).w(FUNC(mz800_state::mz800_read_format_w));
	map(0xce, 0xce).mirror(0xff00).rw(FUNC(mz800_state::mz800_crtc_r), FUNC(mz800_state::mz800_display_mode_w));
	map(0xcf, 0xcf).mirror(0xff00).w(FUNC(mz800_state::mz800_scroll_border_w));
	map(0xd0, 0xd3).mirror(0xff00).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xd4, 0xd7).mirror(0xff00).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xe0, 0xe0).mirror(0xff00).rw(FUNC(mz800_state::mz800_bank_0_r), FUNC(mz800_state::mz800_bank_0_w));
	map(0xe1, 0xe1).mirror(0xff00).rw(FUNC(mz800_state::mz800_bank_1_r), FUNC(mz800_state::mz700_bank_1_w));
	map(0xe2, 0xe2).mirror(0xff00).w(FUNC(mz800_state::mz700_bank_2_w));
	map(0xe3, 0xe3).mirror(0xff00).w(FUNC(mz800_state::mz700_bank_3_w));
	map(0xe4, 0xe4).mirror(0xff00).w(FUNC(mz800_state::mz700_bank_4_w));
	map(0xe5, 0xe5).mirror(0xff00).w(FUNC(mz800_state::mz700_bank_5_w));
	map(0xe6, 0xe6).mirror(0xff00).w(FUNC(mz800_state::mz700_bank_6_w));
	map(0xf0, 0xf0).mirror(0xff00).r(m_joy[0], FUNC(msx_general_purpose_port_device::read)).w(FUNC(mz800_state::mz800_palette_w));
	map(0xf1, 0xf1).mirror(0xff00).r(m_joy[1], FUNC(msx_general_purpose_port_device::read));
	map(0xf2, 0xf2).mirror(0xff00).w("sn76489n", FUNC(sn76489a_device::write));
	map(0xfc, 0xff).mirror(0xff00).rw("z80pio", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
}

/***************************************************************************
    INPUT PORTS
***************************************************************************/

/* 2008-05 FP:
Notice that there is no Backspace key, only a 'Del' one.

Small note about natural keyboard support: currently,
- "Alpha" is mapped to 'F6'
- "Graph" is mapped to 'F7'
- "Break" is mapped to 'F8'                      */

static INPUT_PORTS_START( mz700 )
	PORT_START("ROW0")
	PORT_BIT(0x01, 0x01, IPT_KEYBOARD) PORT_NAME("CR") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x02, 0x02, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)         PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x04, 0x04, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)         PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x08, 0x08, IPT_UNUSED )
	PORT_BIT(0x10, 0x10, IPT_KEYBOARD) PORT_NAME("Alpha") PORT_CODE(KEYCODE_TAB) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x20, 0x20, IPT_KEYBOARD) PORT_NAME("\xE2\x86\x93  \xC2\xA3") PORT_CODE(KEYCODE_TILDE) PORT_CHAR(U'£') // this one would be 2nd row, 3rd key after 'P'
	PORT_BIT(0x40, 0x40, IPT_KEYBOARD) PORT_NAME("Graph") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x80, 0x80, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP)          PORT_CHAR('_') // this one would be 2nd row, 4th key after 'P'

	PORT_START("ROW1")
	PORT_BIT(0x07, 0x07, IPT_UNUSED )
	PORT_BIT(0x08, 0x08, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)     PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x10, 0x10, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)    PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x20, 0x20, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)     PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(0x40, 0x40, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)             PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x80, 0x80, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)             PORT_CHAR('Y') PORT_CHAR('y')

	PORT_START("ROW2")
	PORT_BIT(0x01, 0x01, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)             PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x02, 0x02, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)             PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x04, 0x04, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)             PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x08, 0x08, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)             PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x10, 0x10, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)             PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x20, 0x20, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)             PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x40, 0x40, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)             PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x80, 0x80, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)             PORT_CHAR('Q') PORT_CHAR('q')

	PORT_START("ROW3")
	PORT_BIT(0x01, 0x01, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)             PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x02, 0x02, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)             PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT(0x04, 0x04, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)             PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x08, 0x08, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)             PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x10, 0x10, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)             PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x20, 0x20, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)             PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x40, 0x40, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)             PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x80, 0x80, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)             PORT_CHAR('I') PORT_CHAR('i')

	PORT_START("ROW4")
	PORT_BIT(0x01, 0x01, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)             PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x02, 0x02, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)             PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT(0x04, 0x04, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)             PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x08, 0x08, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)             PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x10, 0x10, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)             PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x20, 0x20, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)             PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x40, 0x40, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)             PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x80, 0x80, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)             PORT_CHAR('A') PORT_CHAR('a')

	PORT_START("ROW5")
	PORT_BIT(0x01, 0x01, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)             PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, 0x02, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)             PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x04, 0x04, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)             PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x08, 0x08, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)             PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x10, 0x10, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)             PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, 0x20, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)             PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x40, 0x40, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)             PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x80, 0x80, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)             PORT_CHAR('1') PORT_CHAR('!')

	PORT_START("ROW6")
	PORT_BIT(0x01, 0x01, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)          PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x02, 0x02, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)         PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x04, 0x04, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)             PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x08, 0x08, IPT_KEYBOARD) PORT_NAME("0  Pi")               PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x10, 0x10, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)         PORT_CHAR(' ')
	PORT_BIT(0x20, 0x20, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)         PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x40, 0x40, IPT_KEYBOARD) PORT_NAME("\xE2\x86\x91  ~")     PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT(0x80, 0x80, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN)          PORT_CHAR('\\') PORT_CHAR('|')  // this one would be 1st row, 3rd key after '0'

	PORT_START("ROW7")
	PORT_BIT(0x01, 0x01, IPT_KEYBOARD) PORT_NAME("/  \xE2\x86\x90") PORT_CODE(KEYCODE_SLASH)    PORT_CHAR('/')
	PORT_BIT(0x02, 0x02, IPT_KEYBOARD) PORT_NAME("?  \xE2\x86\x92") PORT_CODE(KEYCODE_END)      PORT_CHAR('?')  // this one would be 4th row, 4th key after 'M'
	PORT_BIT(0x04, 0x04, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)                                  PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x08, 0x08, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)                                 PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x10, 0x10, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)                                  PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x20, 0x20, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)                                    PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x40, 0x40, IPT_KEYBOARD) PORT_NAME("DEL  HOME") PORT_CODE(KEYCODE_DEL)            PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x80, 0x80, IPT_KEYBOARD) PORT_NAME("INST  CLR") PORT_CODE(KEYCODE_INSERT)         PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START("ROW8")
	PORT_BIT(0x01, 0x01, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x3e, 0x3e, IPT_UNUSED)
	PORT_BIT(0x40, 0x40, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x80, 0x80, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_HOME)               PORT_CHAR(UCHAR_MAMEKEY(F8))    // this one would be at Backspace position

	PORT_START("ROW9")
	PORT_BIT(0x07, 0x07, IPT_UNUSED)
	PORT_BIT(0x08, 0x08, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)            PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x10, 0x10, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)            PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x20, 0x20, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)            PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x40, 0x40, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)            PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x80, 0x80, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)            PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_START("JOY")
	PORT_BIT(0x01, 0x00, IPT_UNUSED)
	PORT_BIT(0x02, 0x00, IPT_JOYSTICK_UP)       PORT_8WAY
	PORT_BIT(0x04, 0x00, IPT_JOYSTICK_DOWN)     PORT_8WAY
	PORT_BIT(0x08, 0x00, IPT_JOYSTICK_LEFT)     PORT_8WAY
	PORT_BIT(0x10, 0x00, IPT_JOYSTICK_RIGHT)    PORT_8WAY
INPUT_PORTS_END

static INPUT_PORTS_START( mz800 )
	PORT_INCLUDE(mz700)

	PORT_MODIFY("JOY")
	PORT_BIT(0x1f, 0x00, IPT_UNUSED)

	PORT_START("system_settings")
	PORT_DIPNAME(0x01, 0x00, "Mode selection")
	PORT_DIPLOCATION("SW:4")
	PORT_DIPSETTING(0x01, "MZ-700")
	PORT_DIPSETTING(0x00, "MZ-800")
	PORT_DIPNAME(0x06, 0x06, "Printer selection")
	PORT_DIPLOCATION("SW:3,2")
	PORT_DIPSETTING(0x06, "MZ printer")
	PORT_DIPSETTING(0x00, "Centronics printer")
	PORT_DIPNAME(0x08, 0x08, "Cassette polarity")
	PORT_DIPLOCATION("SW:1")
	PORT_DIPSETTING(0x08, DEF_STR(Unknown))
	PORT_DIPSETTING(0x00, DEF_STR(Unknown))
INPUT_PORTS_END


/***************************************************************************
    GFX LAYOUT
***************************************************************************/

static const gfx_layout mz700_layout =
{
	8, 8,       /* 8 x 8 graphics */
	512,        /* 512 codes */
	1,      /* 1 bit per pixel */
	{ 0 },      /* no bitplanes */
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8 * 8       /* code takes 8 times 8 bits */
};

static GFXDECODE_START( gfx_mz700 )
	GFXDECODE_ENTRY("cgrom", 0, mz700_layout, 0, 4)
GFXDECODE_END

static GFXDECODE_START( gfx_mz800 )
	GFXDECODE_ENTRY("monitor", 0x1000, mz700_layout, 0, 4)    // for mz800 viewer only
GFXDECODE_END


/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

void mz_state::mz700(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(17'734'470)/5);
	m_maincpu->set_addrmap(AS_PROGRAM, &mz_state::mz700_mem);
	m_maincpu->set_addrmap(AS_IO, &mz_state::mz700_io);

	ADDRESS_MAP_BANK(config, "banke").set_map(&mz_state::mz700_banke).set_options(ENDIANNESS_LITTLE, 8, 16, 0x2000);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(XTAL(17'734'470)/2, 568, 0, 40*8, 312, 0, 25*8);
	m_screen->set_screen_update(FUNC(mz_state::screen_update_mz700));
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette, palette_device::RGB_3BIT);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_mz700);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	/* ne556 timers */
	TIMER(config, "cursor").configure_periodic(FUNC(mz_state::ne556_cursor_callback), attotime::from_hz(1.5));
	TIMER(config, "other").configure_periodic(FUNC(mz_state::ne556_other_callback), attotime::from_hz(34.5));

	/* devices */
	PIT8253(config, m_pit);
	m_pit->set_clk<0>(XTAL(17'734'470)/20);
	m_pit->out_handler<0>().set(FUNC(mz_state::pit_out0_changed));
	m_pit->set_clk<1>(XTAL(17'734'470)/2/568);
	m_pit->out_handler<1>().set(m_pit, FUNC(pit8253_device::write_clk2));
	m_pit->out_handler<2>().set(FUNC(mz_state::pit_irq_2));

	I8255(config, m_ppi);
	m_ppi->out_pa_callback().set(FUNC(mz_state::pio_port_a_w));
	m_ppi->in_pb_callback().set(FUNC(mz_state::pio_port_b_r));
	m_ppi->in_pc_callback().set(FUNC(mz_state::pio_port_c_r));
	m_ppi->out_pc_callback().set(FUNC(mz_state::pio_port_c_w));

	TTL74145(config, m_ls145);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(mz700_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("mz_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("mz700_cass");

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("64K");
}


void mz800_state::mz800(machine_config &config)
{
	mz700(config);
	config.device_remove("banke");

	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &mz800_state::mz800_mem);
	m_maincpu->set_addrmap(AS_IO, &mz800_state::mz800_io);

	ADDRESS_MAP_BANK(config, "bankf").set_map(&mz800_state::mz800_bankf).set_options(ENDIANNESS_LITTLE, 8, 16, 0x2000);

	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_mz800);

	m_screen->set_screen_update(FUNC(mz800_state::screen_update_mz800));

	SN76489A(config, "sn76489n", XTAL(17'734'470)/5).add_route(ALL_OUTPUTS, "mono", 1.0);

	config.device_remove("cass_list");
	SOFTWARE_LIST(config, "cass_list").set_original("mz800_cass");

	/* devices */
	m_pit->set_clk<0>(XTAL(17'734'470)/16);

	m_ppi->out_pa_callback().set(FUNC(mz800_state::pio_port_a_w));

	z80pio_device& pio(Z80PIO(config, "z80pio", XTAL(17'734'470)/5));
	pio.out_int_callback().set_inputline(m_maincpu, 0);
	pio.in_pa_callback().set(FUNC(mz800_state::mz800_z80pio_port_a_r));
	pio.out_pa_callback().set(FUNC(mz800_state::mz800_z80pio_port_a_w));
	pio.out_pb_callback().set("cent_data_out", FUNC(output_latch_device::write));

	MSX_GENERAL_PURPOSE_PORT(config, m_joy[0], msx_general_purpose_port_devices, "joystick");
	MSX_GENERAL_PURPOSE_PORT(config, m_joy[1], msx_general_purpose_port_devices, "joystick");

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set("z80pio", FUNC(z80pio_device::strobe_b)).invert();

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	MZ80_EXP_SLOT(config, "exp1", mz800_exp_devices, nullptr).set_iospace(m_maincpu, AS_IO);
	MZ80_EXP_SLOT(config, "exp2", mz800_exp_devices, nullptr).set_iospace(m_maincpu, AS_IO);
}


/***************************************************************************
    ROM DEFINITIONS
***************************************************************************/

ROM_START( mz700 )
	ROM_REGION( 0x1000, "monitor", 0 )
	ROM_LOAD( "1z-013a.rom", 0x0000, 0x1000, CRC(4c6c6b7b) SHA1(ef8f7399e86c1dc638a5cb83efdb73369c2b5735) )

	ROM_REGION( 0x1000, "cgrom", 0 )
	ROM_LOAD( "mz700fon.int", 0x0000, 0x1000, CRC(42b9e8fb) SHA1(5128ad179a702f8e0bd9910a58bad8fbe4c20167) )
ROM_END

ROM_START( mz700j )
	ROM_REGION( 0x1000, "monitor", 0 )
	ROM_LOAD( "1z-009b.rom", 0x0000, 0x1000, CRC(ab1fbe6f) SHA1(7b10d7965c541393e33a265bcf71a00314d2db7a))

	ROM_REGION( 0x1000, "cgrom", 0 )
	//ROM_LOAD( "mz700fon.jp", 0x0000, 0x1000, CRC(697ec121) SHA1(5eb1d42d273b1fd2cab120486279ab8ff6c85dc7))
	ROM_LOAD( "mz700fon.jpn", 0x0000, 0x1000, CRC(425eedf5) SHA1(bd2cc750f2d2f63e50a59786668509e81a276e32) )
ROM_END

ROM_START( mz800 )
	ROM_REGION( 0x4000, "monitor", 0 )
	ROM_SYSTEM_BIOS( 0, "9z504m", "Monitor 9Z-504M" )
	ROMX_LOAD( "mz800.rom", 0x0000, 0x4000, CRC(600d17e1) SHA1(950ce4b51429916f8036e41ba6130fac149b36e4), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "willy", "Willy's MZ-800 Monitor (English)" )
	ROMX_LOAD( "800willy_en.rom", 0x0000, 0x4000, CRC(f98b4bea) SHA1(84a0316a3a52e6ad5c9d0f1c7365fca06f069566), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "willyg", "Willy's MZ-800 Monitor (German)" )
	ROMX_LOAD( "800willy_ge.rom", 0x0000, 0x4000, CRC(2471034f) SHA1(68d988c816b644b4e52d2474a9159311d47c3790), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "willyj", "Willy's MZ-800 Monitor (Japanese)" )
	ROMX_LOAD( "800willy_jap.rom", 0x0000, 0x4000, CRC(92bbf0a3) SHA1(02647816cc33ba2162f84997974f7fe3bab1323a), ROM_BIOS(3) )
ROM_END

ROM_START( mz1500 )
	ROM_REGION( 0x4000, "monitor", 0 )
	ROM_LOAD( "9z-502m.rom",  0x0000, 0x1000, CRC(643db428) SHA1(c2ad8af2ef00db32afde54d5741b07de5d4da16a))
	ROM_CONTINUE(0x2800, 0x1800)

	ROM_REGION( 0x1000, "cgrom", 0 )
	//ROM_LOAD( "mz700fon.jp", 0x0000, 0x1000, CRC(697ec121) SHA1(5eb1d42d273b1fd2cab120486279ab8ff6c85dc7))
	ROM_LOAD( "mz700fon.jpn", 0x0000, 0x1000, CRC(425eedf5) SHA1(bd2cc750f2d2f63e50a59786668509e81a276e32) )
ROM_END

/***************************************************************************
    GAME DRIVERS
***************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME          FLAGS
COMP( 1982, mz700,  0,      0,      mz700,   mz700, mz_state,    init_mz700, "Sharp", "MZ-700",         0 )
COMP( 1982, mz700j, mz700,  0,      mz700,   mz700, mz_state,    init_mz700, "Sharp", "MZ-700 (Japan)", 0 )
COMP( 1984, mz800,  0,      0,      mz800,   mz800, mz800_state, init_mz800, "Sharp", "MZ-800",         MACHINE_NOT_WORKING )
COMP( 1984, mz1500, 0,      0,      mz800,   mz800, mz800_state, init_mz800, "Sharp", "MZ-1500",        MACHINE_NOT_WORKING )    // Japanese version of the MZ-800



mz80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

        MZ80 driver by Miodrag Milanovic

        22/11/2008 Preliminary driver.

MZ80K Monitor:
LOAD - load a cassette

MZ80A Monitor Commands:
B - turn key beep on/off
F - boot from Floppy (press enter at the question)
J - jump (goto)
L - load a cassette

MZ80A ToDo:
- System writes CF to D800-DFFF
- System uses E200-E2FF (contents are read then discarded)
- System uses E800
- Disk uses ports D8-DC
- Keyboard issues listed below

MZ80B
- Makes extensive use of io ports
- D000-FFFF is banked ram

****************************************************************************/

#include "emu.h"
#include "mz80.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "formats/mz_cas.h"


/* Note about natural keyboard support:
    - Keys G1 -> G25 are not mapped in natural mode, switch to emulated mode to use them
 */
static INPUT_PORTS_START( mz80k )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('+')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G4") PORT_CODE(KEYCODE_4_PAD)

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(0x03C0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G5") PORT_CODE(KEYCODE_5_PAD)

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('<')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E') // shifted= left arrow symbol
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR(']')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('@')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR(':')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('*')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G9") PORT_CODE(KEYCODE_9_PAD)

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('>')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('[')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('\\')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('?')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P') // shifted=up arrow symbol
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G10") PORT_CODE(KEYCODE_0_PAD)

	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(0xA3)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G12") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G14") PORT_CODE(KEYCODE_F4)

	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G11") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G13") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G15") PORT_CODE(KEYCODE_F5)

	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') // shifted=right arrow symbol
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SML CAP") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G17") PORT_CODE(KEYCODE_F7)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G19") PORT_CODE(KEYCODE_F9)

	PORT_START("LINE7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X') // shifted=down arrow symbol
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G16") PORT_CODE(KEYCODE_F6)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G18") PORT_CODE(KEYCODE_F8)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G20") PORT_CODE(KEYCODE_F10)

	PORT_START("LINE8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del  Ins") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right  Left") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CR") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R Shift") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G22") PORT_CODE(KEYCODE_PGUP)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G24") PORT_CODE(KEYCODE_PGDN)

	PORT_START("LINE9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Clr Home") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down  Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G21") PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G23") PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G25") PORT_CODE(KEYCODE_TILDE)
INPUT_PORTS_END

// PORT_NAMEs need to be verified
static INPUT_PORTS_START( mz80a )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Grph") PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Unused?")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Unused?")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Unused?")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Unused?")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Unused?")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl  Break") PORT_CODE(KEYCODE_ESC)

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Unused?")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del  Ins") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')

	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(' ')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')

	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('_')

	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') // shifted=Left Arrow symbol
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') PORT_CHAR('~')

	PORT_START("LINE7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_END) PORT_CHAR('?')  // shifted=Up Arrow symbol
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Unused?")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down  Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right  Left") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("HOME  CLR") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))

	PORT_START("LINE8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_00_PAD) PORT_CHAR(UCHAR_MAMEKEY(00_PAD))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))

	PORT_START("LINE9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Unused?")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))
INPUT_PORTS_END

void mz80_state::mz80k_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).rom();
	map(0x1000, 0xcfff).ram().share("p_ram"); // 48 KB of RAM
	map(0xd000, 0xd7ff).ram().share("videoram"); // Video RAM
	map(0xe000, 0xe003).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write)); /* PPIA 8255 */
	map(0xe004, 0xe007).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));  /* PIT 8253  */
	map(0xe008, 0xe00b).rw(FUNC(mz80_state::mz80k_strobe_r), FUNC(mz80_state::mz80k_strobe_w));
	map(0xf000, 0xf3ff).rom();
}

void mz80_state::mz80k_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
}

static GFXDECODE_START( gfx_mz80k )
	GFXDECODE_ENTRY( "chargen", 0x0000, mz80k_charlayout, 0, 1 )
GFXDECODE_END

static GFXDECODE_START( gfx_mz80kj )
	GFXDECODE_ENTRY( "chargen", 0x0000, mz80kj_charlayout, 0, 1 )
GFXDECODE_END

TIMER_DEVICE_CALLBACK_MEMBER(mz80_state::ne555_tempo_callback)
{
	m_mz80k_tempo_strobe ^= 1;
}

void mz80_state::mz80k(machine_config &config)
{
	/* basic machine hardware */

	/* main CPU */
	Z80(config, m_maincpu, XTAL(8'000'000) / 4);        /* 2 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &mz80_state::mz80k_mem);
	m_maincpu->set_addrmap(AS_IO, &mz80_state::mz80k_io);


	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_size(320, 200);
	screen.set_visarea(0, 319, 0, 199);
	screen.set_screen_update(FUNC(mz80_state::screen_update_mz80k));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_mz80k);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* Audio */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	/* Devices */
	I8255(config, m_ppi);
	m_ppi->out_pa_callback().set(FUNC(mz80_state::mz80k_8255_porta_w));
	m_ppi->in_pb_callback().set(FUNC(mz80_state::mz80k_8255_portb_r));
	m_ppi->in_pc_callback().set(FUNC(mz80_state::mz80k_8255_portc_r));
	m_ppi->out_pc_callback().set(FUNC(mz80_state::mz80k_8255_portc_w));

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(XTAL(8'000'000)/4);
	m_pit->out_handler<0>().set(FUNC(mz80_state::pit_out0_changed));
	m_pit->set_clk<1>(XTAL(8'000'000)/256);
	m_pit->out_handler<1>().set(m_pit, FUNC(pit8253_device::write_clk2));
	m_pit->set_clk<2>(0);
	m_pit->out_handler<2>().set(FUNC(mz80_state::pit_out2_changed));

	TIMER(config, "tempo").configure_periodic(FUNC(mz80_state::ne555_tempo_callback), attotime::from_hz(34));
	CASSETTE(config, m_cassette);
	m_cassette->set_formats(mz700_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
}

void mz80_state::mz80kj(machine_config &config)
{
	mz80k(config);

	subdevice<screen_device>("screen")->set_screen_update(FUNC(mz80_state::screen_update_mz80kj));
	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_mz80kj);
}

void mz80_state::mz80a(machine_config &config)
{
	mz80k(config);

	subdevice<screen_device>("screen")->set_screen_update(FUNC(mz80_state::screen_update_mz80a));
}


ROM_START( mz80k )
	ROM_REGION( 0x10000, "maincpu", 0)
	ROM_SYSTEM_BIOS( 0, "sp1002", "sp1002" )
	ROMX_LOAD( "sp1002.rom",    0x0000, 0x1000, CRC(2223e677) SHA1(518ffbe2333582ab36e6d76d1e03879a246ffa1c), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "tc", "tc" )
	ROMX_LOAD( "80ktc.rom",     0x0000, 0x1000, CRC(19ed6546) SHA1(2bbeff916c2fa8991e718070ca4195beb45e0848), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v44", "v44" )
	ROMX_LOAD( "80kv44.rom",    0x0000, 0x1000, CRC(d66af028) SHA1(718904c011dfcfcfabbb7dbdaaa35b9f3ac41baf), ROM_BIOS(2) )
	ROM_LOAD( "mz80kfdif.rom",  0xf000, 0x0400, CRC(d36505e0) SHA1(1f60027e8739313962a37edbf98172df7062df49) )

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "80kcg.rom",      0x0000, 0x0800, CRC(9b2fb88b) SHA1(6d4d120bd0e3a8c781c581e3c379a39db610a4d2) )
	// This is listed as "fixed" English
	ROM_LOAD( "80kcgf.rom",     0x0800, 0x0800, CRC(be952852) SHA1(e8273de9cce88b10630187841f81aa9be22520b7) )
ROM_END

ROM_START( mz80kj )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "sp1002.rom",    0x0000, 0x1000, CRC(2223e677) SHA1(518ffbe2333582ab36e6d76d1e03879a246ffa1c) )
	// TC monitor not possible to be used on japanese version since chargen doesn't have upcase/lowercase, but japanese letters
	ROM_LOAD( "mz80kfdif.rom", 0xf000, 0x0400, CRC(d36505e0) SHA1(1f60027e8739313962a37edbf98172df7062df49) )

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "mz80k.jpn",     0x0000, 0x0800, CRC(bffe3312) SHA1(7058e565f338f5ec2bba85c19c9671e5c4fe258e) )
	// This has a different Japanese set
	ROM_LOAD( "80kcgj.rom",    0x0800, 0x0800, CRC(7767f11e) SHA1(8dc88d088b2e7d3198bf856bd44d364fb6e5cfa4) )
ROM_END

ROM_START( mz80a )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "sa1510.rom",  0x0000, 0x1000, CRC(ae63ab39) SHA1(3c2180c52fc470131ba668a6c16e77656adeccf8) )
	ROM_LOAD( "mz80afi.rom", 0xf000, 0x0800, CRC(e3b3ddfb) SHA1(ec94cc3d236716f53e747f0c8ac9186d72bf9c10) )

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "mz80acg.rom", 0x0000, 0x0800, CRC(a87c2e2b) SHA1(e8aefbdb48a63e5f96692af868c353ca7e1bfcd2) )
ROM_END

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY  FULLNAME             FLAGS
COMP( 1979, mz80kj, 0,      0,      mz80kj,  mz80k, mz80_state, init_mz80k, "Sharp", "MZ-80K (Japanese)", 0 )
COMP( 1979, mz80k,  mz80kj, 0,      mz80k,   mz80k, mz80_state, init_mz80k, "Sharp", "MZ-80K",            0 )
COMP( 1982, mz80a,  0,      0,      mz80a,   mz80a, mz80_state, init_mz80k, "Sharp", "MZ-80A",            0 )



n64.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/*
    Nintendo 64

    Driver by Ville Linde
    Reformatted to share hardware by R. Belmont
    Additional work by Ryan Holtz
    Porting from Mupen64 by Ryan Holtz
*/

#include "emu.h"
#include "n64.h"

#include "emupal.h"
#include "softlist.h"
#include "speaker.h"

class n64_console_state : public n64_state
{
public:
	n64_console_state(const machine_config &mconfig, device_type type, const char *tag)
		: n64_state(mconfig, type, tag)
		{ }

	void n64(machine_config &config);
	void n64dd(machine_config &config);

private:
	uint32_t dd_null_r();
	void n64_map(address_map &map) ATTR_COLD;
	void n64dd_map(address_map &map) ATTR_COLD;

	DECLARE_MACHINE_START(n64dd);
	INTERRUPT_GEN_MEMBER(n64_reset_poll);
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
	void mempak_format(uint8_t* pak);
	std::error_condition disk_load(device_image_interface &image);
	void disk_unload(device_image_interface &image);
	void rsp_imem_map(address_map &map) ATTR_COLD;
	void rsp_dmem_map(address_map &map) ATTR_COLD;
};

uint32_t n64_console_state::dd_null_r()
{
	return 0xffffffff;
}

void n64_console_state::n64_map(address_map &map)
{
	map(0x00000000, 0x007fffff).ram().share("rdram");                   // RDRAM
	map(0x03f00000, 0x03f00027).rw("rcp", FUNC(n64_periphs::rdram_reg_r), FUNC(n64_periphs::rdram_reg_w));
	map(0x04000000, 0x04000fff).ram().share("rsp_dmem");                    // RSP DMEM
	map(0x04001000, 0x04001fff).ram().share("rsp_imem");                    // RSP IMEM
	map(0x04040000, 0x040fffff).rw("rcp", FUNC(n64_periphs::sp_reg_r), FUNC(n64_periphs::sp_reg_w));  // RSP
	map(0x04100000, 0x041fffff).rw("rcp", FUNC(n64_periphs::dp_reg_r), FUNC(n64_periphs::dp_reg_w));  // RDP
	map(0x04300000, 0x043fffff).rw("rcp", FUNC(n64_periphs::mi_reg_r), FUNC(n64_periphs::mi_reg_w));    // MIPS Interface
	map(0x04400000, 0x044fffff).rw("rcp", FUNC(n64_periphs::vi_reg_r), FUNC(n64_periphs::vi_reg_w));    // Video Interface
	map(0x04500000, 0x045fffff).rw("rcp", FUNC(n64_periphs::ai_reg_r), FUNC(n64_periphs::ai_reg_w));    // Audio Interface
	map(0x04600000, 0x046fffff).rw("rcp", FUNC(n64_periphs::pi_reg_r), FUNC(n64_periphs::pi_reg_w));    // Peripheral Interface
	map(0x04700000, 0x047fffff).rw("rcp", FUNC(n64_periphs::ri_reg_r), FUNC(n64_periphs::ri_reg_w));    // RDRAM Interface
	map(0x04800000, 0x048fffff).rw("rcp", FUNC(n64_periphs::si_reg_r), FUNC(n64_periphs::si_reg_w));    // Serial Interface
	map(0x05000508, 0x0500050b).r(FUNC(n64_console_state::dd_null_r));
	map(0x08000000, 0x0801ffff).ram().share("sram");                                        // Cartridge SRAM
	map(0x10000000, 0x13ffffff).rom().region("user2", 0);                                   // Cartridge
	map(0x1fc00000, 0x1fc007bf).rom().region("user1", 0);                                   // PIF ROM
	map(0x1fc007c0, 0x1fc007ff).rw("rcp", FUNC(n64_periphs::pif_ram_r), FUNC(n64_periphs::pif_ram_w));
}

void n64_console_state::n64dd_map(address_map &map)
{
	n64_console_state::n64_map(map);
	// TODO: expansion bus
	map(0x05000000, 0x05ffffff).rw("rcp", FUNC(n64_periphs::dd_reg_r), FUNC(n64_periphs::dd_reg_w)); // 64DD Interface
	map(0x06000000, 0x063fffff).rom().region("ddipl", 0);                                   // 64DD IPL ROM
}

void n64_console_state::rsp_imem_map(address_map &map)
{
	map(0x00000000, 0x00000fff).ram().share("rsp_imem");
}

void n64_console_state::rsp_dmem_map(address_map &map)
{
	map(0x00000000, 0x00000fff).ram().share("rsp_dmem");
}

static INPUT_PORTS_START( n64 )
	PORT_START("input")
	PORT_CONFNAME(0x0003, 0x0001, "Controller Port 0 Device")
	PORT_CONFSETTING(0x00, "None")
	PORT_CONFSETTING(0x01, "Joypad")
	PORT_CONFSETTING(0x02, "Mouse")
	PORT_CONFNAME(0x000C, 0x0000, "Controller Port 1 Device")
	PORT_CONFSETTING(0x00, "None")
	PORT_CONFSETTING(0x04, "Joypad")
	PORT_CONFSETTING(0x08, "Mouse")
	PORT_CONFNAME(0x0030, 0x0000, "Controller Port 2 Device")
	PORT_CONFSETTING(0x00, "None")
	PORT_CONFSETTING(0x10, "Joypad")
	PORT_CONFSETTING(0x20, "Mouse")
	PORT_CONFNAME(0x00C0, 0x0000, "Controller Port 3 Device")
	PORT_CONFSETTING(0x00, "None")
	PORT_CONFSETTING(0x40, "Joypad")
	PORT_CONFSETTING(0x80, "Mouse")

	PORT_CONFNAME(0x0100, 0x0000, "Disk Drive")
	PORT_CONFSETTING(0x0000, "Retail")
	PORT_CONFSETTING(0x0100, "Development")

	PORT_CONFNAME(0xC000, 0x8000, "EEPROM Size")
	PORT_CONFSETTING(0x0000, "None")
	PORT_CONFSETTING(0x8000, "4KB")
	PORT_CONFSETTING(0xC000, "16KB")

	//Player 1
	PORT_START("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_BUTTON2 )        PORT_PLAYER(1) PORT_NAME("P1 Button A / Left Click")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(1) PORT_NAME("P1 Button B / Right Click")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_BUTTON3 )        PORT_PLAYER(1) PORT_NAME("P1 Button Z")
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_START )          PORT_PLAYER(1) PORT_NAME("P1 Start")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1) PORT_NAME("P1 Joypad \xE2\x86\x91") /* Up */
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1) PORT_NAME("P1 Joypad \xE2\x86\x93") /* Down */
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1) PORT_NAME("P1 Joypad \xE2\x86\x90") /* Left */
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("P1 Joypad \xE2\x86\x92") /* Right */
	PORT_BIT( 0x00c0, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON4 )        PORT_PLAYER(1) PORT_NAME("P1 Button L")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON5 )        PORT_PLAYER(1) PORT_NAME("P1 Button R")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_BUTTON6 )        PORT_PLAYER(1) PORT_NAME("P1 Button C \xE2\x86\x91") /* Up */
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_BUTTON7 )        PORT_PLAYER(1) PORT_NAME("P1 Button C \xE2\x86\x93") /* Down */
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON8 )        PORT_PLAYER(1) PORT_NAME("P1 Button C \xE2\x86\x90") /* Left */
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_BUTTON9 )        PORT_PLAYER(1) PORT_NAME("P1 Button C \xE2\x86\x92") /* Right */

	PORT_START("P1_ANALOG_X")
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_X ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(30) PORT_KEYDELTA(30) PORT_PLAYER(1)

	PORT_START("P1_ANALOG_Y")
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_Y ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(30) PORT_KEYDELTA(30) PORT_PLAYER(1) PORT_REVERSE

	PORT_START("P1_MOUSE_X")
	PORT_BIT( 0xffff, 0x0000, IPT_MOUSE_X ) PORT_MINMAX(0x0000,0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1)

	PORT_START("P1_MOUSE_Y")
	PORT_BIT( 0xffff, 0x0000, IPT_MOUSE_Y ) PORT_MINMAX(0x0000,0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1) PORT_REVERSE

	//Player 2
	PORT_START("P2")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_BUTTON2 )        PORT_PLAYER(2) PORT_NAME("P2 Button A / Left Click")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(2) PORT_NAME("P2 Button B / Right Click")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_BUTTON3 )        PORT_PLAYER(2) PORT_NAME("P2 Button Z")
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_START )          PORT_PLAYER(2) PORT_NAME("P2 Start")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(2) PORT_NAME("P2 Joypad \xE2\x86\x91") /* Up */
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(2) PORT_NAME("P2 Joypad \xE2\x86\x93") /* Down */
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(2) PORT_NAME("P2 Joypad \xE2\x86\x90") /* Left */
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_NAME("P2 Joypad \xE2\x86\x92") /* Right */
	PORT_BIT( 0x00c0, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON4 )        PORT_PLAYER(2) PORT_NAME("P2 Button L")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON5 )        PORT_PLAYER(2) PORT_NAME("P2 Button R")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_BUTTON6 )        PORT_PLAYER(2) PORT_NAME("P2 Button C \xE2\x86\x91") /* Up */
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_BUTTON7 )        PORT_PLAYER(2) PORT_NAME("P2 Button C \xE2\x86\x93") /* Down */
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON8 )        PORT_PLAYER(2) PORT_NAME("P2 Button C \xE2\x86\x90") /* Left */
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_BUTTON9 )        PORT_PLAYER(2) PORT_NAME("P2 Button C \xE2\x86\x92") /* Right */

	PORT_START("P2_ANALOG_X")
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_X ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(30) PORT_KEYDELTA(30) PORT_PLAYER(2)

	PORT_START("P2_ANALOG_Y")
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_Y ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(30) PORT_KEYDELTA(30) PORT_PLAYER(2) PORT_REVERSE

	PORT_START("P2_MOUSE_X")
	PORT_BIT( 0xffff, 0x0000, IPT_MOUSE_X ) PORT_MINMAX(0x0000,0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(2)

	PORT_START("P2_MOUSE_Y")
	PORT_BIT( 0xffff, 0x0000, IPT_MOUSE_Y ) PORT_MINMAX(0x0000,0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(2) PORT_REVERSE

	//Player 3
	PORT_START("P3")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_BUTTON2 )        PORT_PLAYER(3) PORT_NAME("P3 Button A / Left Click")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(3) PORT_NAME("P3 Button B / Right Click")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_BUTTON3 )        PORT_PLAYER(3) PORT_NAME("P3 Button Z")
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_START )          PORT_PLAYER(3) PORT_NAME("P3 Start")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(3) PORT_NAME("P3 Joypad \xE2\x86\x91") /* Up */
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(3) PORT_NAME("P3 Joypad \xE2\x86\x93") /* Down */
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(3) PORT_NAME("P3 Joypad \xE2\x86\x90") /* Left */
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(3) PORT_NAME("P3 Joypad \xE2\x86\x92") /* Right */
	PORT_BIT( 0x00c0, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON4 )        PORT_PLAYER(3) PORT_NAME("P3 Button L")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON5 )        PORT_PLAYER(3) PORT_NAME("P3 Button R")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_BUTTON6 )        PORT_PLAYER(3) PORT_NAME("P3 Button C \xE2\x86\x91") /* Up */
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_BUTTON7 )        PORT_PLAYER(3) PORT_NAME("P3 Button C \xE2\x86\x93") /* Down */
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON8 )        PORT_PLAYER(3) PORT_NAME("P3 Button C \xE2\x86\x90") /* Left */
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_BUTTON9 )        PORT_PLAYER(3) PORT_NAME("P3 Button C \xE2\x86\x92") /* Right */

	PORT_START("P3_ANALOG_X")
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_X ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(30) PORT_KEYDELTA(30) PORT_PLAYER(3)

	PORT_START("P3_ANALOG_Y")
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_Y ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(30) PORT_KEYDELTA(30) PORT_PLAYER(3) PORT_REVERSE

	PORT_START("P3_MOUSE_X")
	PORT_BIT( 0xffff, 0x0000, IPT_MOUSE_X ) PORT_MINMAX(0x0000,0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(3)

	PORT_START("P3_MOUSE_Y")
	PORT_BIT( 0xffff, 0x0000, IPT_MOUSE_Y ) PORT_MINMAX(0x0000,0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(3) PORT_REVERSE

	//Player 4
	PORT_START("P4")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_BUTTON2 )        PORT_PLAYER(4) PORT_NAME("P4 Button A / Left Click")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(4) PORT_NAME("P4 Button B / Right Click")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_BUTTON3 )        PORT_PLAYER(4) PORT_NAME("P4 Button Z")
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_START )          PORT_PLAYER(4) PORT_NAME("P4 Start")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(4) PORT_NAME("P4 Joypad \xE2\x86\x91") /* Up */
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(4) PORT_NAME("P4 Joypad \xE2\x86\x93") /* Down */
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(4) PORT_NAME("P4 Joypad \xE2\x86\x90") /* Left */
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(4) PORT_NAME("P4 Joypad \xE2\x86\x92") /* Right */
	PORT_BIT( 0x00c0, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON4 )        PORT_PLAYER(4) PORT_NAME("P4 Button L")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON5 )        PORT_PLAYER(4) PORT_NAME("P4 Button R")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_BUTTON6 )        PORT_PLAYER(4) PORT_NAME("P4 Button C \xE2\x86\x91") /* Up */
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_BUTTON7 )        PORT_PLAYER(4) PORT_NAME("P4 Button C \xE2\x86\x93") /* Down */
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON8 )        PORT_PLAYER(4) PORT_NAME("P4 Button C \xE2\x86\x90") /* Left */
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_BUTTON9 )        PORT_PLAYER(4) PORT_NAME("P4 Button C \xE2\x86\x92") /* Right */

	PORT_START("P4_ANALOG_X")
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_X ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(30) PORT_KEYDELTA(30) PORT_PLAYER(4)

	PORT_START("P4_ANALOG_Y")
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_Y ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(30) PORT_KEYDELTA(30) PORT_PLAYER(4) PORT_REVERSE

	PORT_START("P4_MOUSE_X")
	PORT_BIT( 0xffff, 0x0000, IPT_MOUSE_X ) PORT_MINMAX(0x0000,0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(4)

	PORT_START("P4_MOUSE_Y")
	PORT_BIT( 0xffff, 0x0000, IPT_MOUSE_Y ) PORT_MINMAX(0x0000,0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(4) PORT_REVERSE

	PORT_START("RESET")
	PORT_BIT( 0xfffe, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_OTHER )        PORT_NAME("Warm Reset") PORT_CODE(KEYCODE_3)

INPUT_PORTS_END

void n64_console_state::mempak_format(uint8_t* pak)
{
	unsigned char pak_header[] =
	{
		0x81,0x01,0x02,0x03, 0x04,0x05,0x06,0x07, 0x08,0x09,0x0a,0x0b, 0x0c,0x0d,0x0e,0x0f,
		0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17, 0x18,0x19,0x1a,0x1b, 0x1c,0x1d,0x1e,0x1f,
		0xff,0xff,0xff,0xff, 0x05,0x1a,0x5f,0x13, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
		0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x01,0xff, 0x66,0x25,0x99,0xcd,
		0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
		0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
		0xff,0xff,0xff,0xff, 0x05,0x1a,0x5f,0x13, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
		0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x01,0xff, 0x66,0x25,0x99,0xcd,
		0xff,0xff,0xff,0xff, 0x05,0x1a,0x5f,0x13, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
		0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x01,0xff, 0x66,0x25,0x99,0xcd,
		0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
		0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
		0xff,0xff,0xff,0xff, 0x05,0x1a,0x5f,0x13, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
		0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x01,0xff, 0x66,0x25,0x99,0xcd,
		0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
		0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00
	};

	unsigned char pak_inode_table[] =
	{
		0x01,0x71,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x03, 0x00,0x03,0x00,0x03,
		0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03,
		0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03,
		0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03,
		0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03,
		0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03,
		0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03,
		0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03,
		0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03,
		0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03,
		0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03,
		0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03,
		0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03,
		0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03,
		0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03,
		0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03
	};

	memset(pak, 0, 0x8000);
	memcpy(pak, pak_header, 256);
	memcpy(pak+256, pak_inode_table, 256); // Main
	memcpy(pak+512, pak_inode_table, 256); // Mirror
}

DEVICE_IMAGE_LOAD_MEMBER(n64_console_state::cart_load)
{
	int i, length;
	uint8_t *cart = memregion("user2")->base();

	if (!image.loaded_through_softlist())
	{
		length = image.fread(cart, 0x4000000);
	}
	else
	{
		length = image.get_software_region_length("rom");
		memcpy(cart, image.get_software_region("rom"), length);
	}
	m_rcp_periphs->cart_length = length;

	if (cart[0] == 0x37 && cart[1] == 0x80)
	{
		for (i = 0; i < length; i += 4)
		{
			uint8_t b1 = cart[i + 0];
			uint8_t b2 = cart[i + 1];
			uint8_t b3 = cart[i + 2];
			uint8_t b4 = cart[i + 3];
			cart[i + 0] = b3;
			cart[i + 1] = b4;
			cart[i + 2] = b1;
			cart[i + 3] = b2;
		}
	}
	else
	{
		for (i = 0; i < length; i += 4)
		{
			uint8_t b1 = cart[i + 0];
			uint8_t b2 = cart[i + 1];
			uint8_t b3 = cart[i + 2];
			uint8_t b4 = cart[i + 3];
			cart[i + 0] = b4;
			cart[i + 1] = b3;
			cart[i + 2] = b2;
			cart[i + 3] = b1;
		}
	}

	m_rcp_periphs->m_nvram_image = &image.device();

	logerror("cart length = %d\n", length);

	device_image_interface *battery_image = dynamic_cast<device_image_interface *>(m_rcp_periphs->m_nvram_image);
	if (battery_image)
	{
		//printf("Loading\n");
		uint8_t data[0x30800];
		battery_image->battery_load(data, 0x30800, 0x00);
		if (m_sram != nullptr)
		{
			memcpy(m_sram, data, 0x20000);
		}
		memcpy(m_rcp_periphs->m_save_data.eeprom, data + 0x20000, 0x800);
		memcpy(m_rcp_periphs->m_save_data.mempak[0], data + 0x20800, 0x8000);
		memcpy(m_rcp_periphs->m_save_data.mempak[1], data + 0x28800, 0x8000);
	}

	if (m_rcp_periphs->m_save_data.mempak[0][0] == 0) // Init if new
	{
		memset(m_rcp_periphs->m_save_data.eeprom, 0, 0x800);
		mempak_format(m_rcp_periphs->m_save_data.mempak[0]);
		mempak_format(m_rcp_periphs->m_save_data.mempak[1]);
	}

	return std::make_pair(std::error_condition(), std::string());
}

MACHINE_START_MEMBER(n64_console_state,n64dd)
{
	machine_start();
	m_rcp_periphs->dd_present = true;
}

std::error_condition n64_console_state::disk_load(device_image_interface &image)
{
	image.fseek(0, SEEK_SET);
	image.fread(memregion("disk")->base(), image.length());
	m_rcp_periphs->disk_present = true;
	return std::error_condition();
}

void n64_console_state::disk_unload(device_image_interface &image)
{
	m_rcp_periphs->disk_present = false;
}

INTERRUPT_GEN_MEMBER(n64_console_state::n64_reset_poll)
{
	m_rcp_periphs->poll_reset_button((ioport("RESET")->read() & 1) ? true : false);
}

void n64_console_state::n64(machine_config &config)
{
	/* basic machine hardware */
	VR4300BE(config, m_vr4300, 93750000);
	m_vr4300->set_force_no_drc(false);
	//m_vr4300->set_icache_size(16384);
	//m_vr4300->set_dcache_size(8192);
	//m_vr4300->set_system_clock(62500000);
	m_vr4300->set_addrmap(AS_PROGRAM, &n64_console_state::n64_map);
	m_vr4300->set_vblank_int("screen", FUNC(n64_console_state::n64_reset_poll));

	RSP(config, m_rsp, 62500000);
	m_rsp->set_force_no_drc(false);
	m_rsp->dp_reg_r().set(m_rcp_periphs, FUNC(n64_periphs::dp_reg_r));
	m_rsp->dp_reg_w().set(m_rcp_periphs, FUNC(n64_periphs::dp_reg_w));
	m_rsp->sp_reg_r().set(m_rcp_periphs, FUNC(n64_periphs::sp_reg_r));
	m_rsp->sp_reg_w().set(m_rcp_periphs, FUNC(n64_periphs::sp_reg_w));
	m_rsp->status_set().set(m_rcp_periphs, FUNC(n64_periphs::sp_set_status));
	m_rsp->set_addrmap(AS_PROGRAM, &n64_console_state::rsp_imem_map);
	m_rsp->set_addrmap(AS_DATA, &n64_console_state::rsp_dmem_map);

	config.set_maximum_quantum(attotime::from_hz(500000));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	/* Video DACRATE is for quarter pixels, so the horizontal is also given in quarter pixels.  However, the horizontal and vertical timing and sizing is adjustable by register and will be reset when the registers are written. */
	// TODO: with 480 vertical will generate invalid vblanks
	// cfr. amenairc -drc
	m_screen->set_raw(DACRATE_NTSC*2,3093,0,3093,525,0,525);
	//m_screen->set_raw(DACRATE_NTSC*2,3093,0,3093,525,0,480);
	m_screen->set_screen_update(FUNC(n64_state::screen_update));
	m_screen->screen_vblank().set(FUNC(n64_state::screen_vblank));

	PALETTE(config, "palette").set_entries(0x1000);

	SPEAKER(config, "speaker", 2).front();

	DMADAC(config, "dac2").add_route(ALL_OUTPUTS, "speaker", 1.0, 0);
	DMADAC(config, "dac1").add_route(ALL_OUTPUTS, "speaker", 1.0, 1);

	N64PERIPH(config, m_rcp_periphs, 0);

	/* cartridge */
	generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "n64_cart", "v64,z64,rom,n64,bin"));
	cartslot.set_must_be_loaded(true);
	cartslot.set_device_load(FUNC(n64_console_state::cart_load));

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("n64");
}

// TODO: different xtal for PAL

void n64_console_state::n64dd(machine_config &config)
{
	n64(config);
	m_vr4300->set_addrmap(AS_PROGRAM, &n64_console_state::n64dd_map);

	MCFG_MACHINE_START_OVERRIDE(n64_console_state, n64dd)

	generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config.replace(), "cartslot", generic_plain_slot, "n64_cart"));
	cartslot.set_extensions("v64,z64,rom,n64,bin");
	cartslot.set_device_load(FUNC(n64_console_state::cart_load));

	harddisk_image_device &hdd(HARDDISK(config, "n64disk"));
	hdd.set_device_load(FUNC(n64_console_state::disk_load));
	hdd.set_device_unload(FUNC(n64_console_state::disk_unload));
	hdd.set_interface("n64dd_disk");

	SOFTWARE_LIST(config, "dd_list").set_original("n64dd");
}

ROM_START( n64 )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )      /* dummy region for R4300 */

	ROM_REGION32_BE( 0x800, "user1", 0 )
	ROM_LOAD( "pifntsc.bin", 0x0000, 0x0800, CRC(5ec82be9) SHA1(9174eadc0f0ea2654c95fd941406ab46b9dc9bdd) )

	ROM_REGION32_BE( 0x4000000, "user2", ROMREGION_ERASEFF)

	ROM_REGION16_BE( 0x80, "normpoint", 0 )
	ROM_LOAD( "normpnt.rom", 0x00, 0x80, CRC(e7f2a005) SHA1(c27b4a364a24daeee6e99fd286753fd6216362b4) )

	ROM_REGION16_BE( 0x80, "normslope", 0 )
	ROM_LOAD( "normslp.rom", 0x00, 0x80, CRC(4f2ae525) SHA1(eab43f8cc52c8551d9cff6fced18ef80eaba6f05) )
ROM_END

ROM_START( n64pal )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )      /* dummy region for R4300 */

	ROM_REGION32_BE( 0x800, "user1", 0 )
	ROM_LOAD( "pifpal.bin", 0x0000, 0x0800, CRC(2ae77e68) SHA1(46cae59d31f9298b93f3380879454fcef54ee6cc) )

	ROM_REGION32_BE( 0x4000000, "user2", ROMREGION_ERASEFF)

	ROM_REGION16_BE( 0x80, "normpoint", 0 )
	ROM_LOAD( "normpnt.rom", 0x00, 0x80, CRC(e7f2a005) SHA1(c27b4a364a24daeee6e99fd286753fd6216362b4) )

	ROM_REGION16_BE( 0x80, "normslope", 0 )
	ROM_LOAD( "normslp.rom", 0x00, 0x80, CRC(4f2ae525) SHA1(eab43f8cc52c8551d9cff6fced18ef80eaba6f05) )
ROM_END


ROM_START( n64dd )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )      /* dummy region for R4300 */

	ROM_REGION32_BE( 0x800, "user1", 0 )
	ROM_LOAD( "pifdata.bin", 0x0000, 0x0800, CRC(5ec82be9) SHA1(9174eadc0f0ea2654c95fd941406ab46b9dc9bdd) )

	ROM_REGION32_BE( 0x4000000, "user2", ROMREGION_ERASEFF)

	ROM_REGION32_BE( 0x400000, "ddipl", ROMREGION_ERASEFF)
	ROM_LOAD( "64ddipl.bin", 0x000000, 0x400000, CRC(7f933ce2) SHA1(bf861922dcb78c316360e3e742f4f70ff63c9bc3) )

	ROM_REGION32_BE( 0x4400000, "disk", ROMREGION_ERASEFF)

	ROM_REGION16_BE( 0x80, "normpoint", 0 )
	ROM_LOAD( "normpnt.rom", 0x00, 0x80, CRC(e7f2a005) SHA1(c27b4a364a24daeee6e99fd286753fd6216362b4) )

	ROM_REGION16_BE( 0x80, "normslope", 0 )
	ROM_LOAD( "normslp.rom", 0x00, 0x80, CRC(4f2ae525) SHA1(eab43f8cc52c8551d9cff6fced18ef80eaba6f05) )
ROM_END

CONS(1996, n64,    0,   0, n64,   n64, n64_console_state, empty_init, "Nintendo", "Nintendo 64 (NTSC)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
CONS(1997, n64pal, n64, 0, n64,   n64, n64_console_state, empty_init, "Nintendo", "Nintendo 64 (PAL)",  MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
CONS(1999, n64dd,  n64, 0, n64dd, n64, n64_console_state, empty_init, "Nintendo", "Nintendo 64DD",      MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )



n64_gateway.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Foxhack, Angelo Salese
/*
    Nintendo Gateway 64

    LodgeNet was a pay-per-view system that allowed television, movie and video
    game rentals on hotels starting in 1993. They entered an agreement with
    Nintendo to allow the rental of their games on airplanes and hotels for a
    moderate fee.

    The LodgeNet 64 system was exclusively used in hotels, and consisted of a
    set-top box with a customized N64 system inside. You could choose one of
    several games via a TV-driven menu, which would then be downloaded to your
    room's system, and you would be charged a fee for one hour of playtime.

    The games were different from their retail counterparts; Games had to have
    their multiplayer modes, accessories and game save support removed, plus have
    additional support for the system's "halt" functions, so they would be allowed
    on the system. The game controllers were custom, with additional buttons for
    use with their rental system and a reset button, and did not allow the use of
    Rumble Paks or Memory Paks. While the hardware allowed temporary save files,
    these would be deleted from the system with a special utility before a new game
    was loaded. These files were stored in encrypted format in the LodgeNet
    servers, and were decrypted after being downloaded to the system. An extra file
    with additional info about the game, its encryption code, and EEPROM settings
    was also present.

    While the development info released mentions a requirement for PAL versions of
    the games, none are currently known to exist.

    38 Nintendo 64 titles were made available on the service, and 31 are currently
    dumped. The following games have not been found:

    - Extreme-G
    - Forsaken 64
    - Iggy's Reckin' Balls
    - Milo's Astro Lanes
    - Namco Museum 64
    - San Francisco Rush: Extreme Racing
    - Turok 2: Seeds of Evil

    These games will function, but the actual LodgeNet system is not emulated,
    so the extra controller buttons and other system functions are not
    implemented.

    The existing roms are decrypted, and should be considered bad dumps.

    TODO:
    - Set-top box part;
    - Extra joypad buttons;
    - Doesn't really have cartslot but rather uploads games thru a DOS/V Windows 95/98 program
      that works with encrypted files.
      \- Notice that the available dump is "patched to work on emulators",
         it normally expect a specific ISA/PCI SCSI board plus a setup/install CD-ROM;
    - Host HALT override
      \- maps thru the "cartslot" space, adds extra state signals to the joypad
         (up + down reads halts, left + right unhalts)

*/

#include "emu.h"
#include "n64.h"

#include "emupal.h"
#include "screen.h"
#include "softlist.h"
#include "speaker.h"

class n64_gateway_state : public n64_state
{
public:
	n64_gateway_state(const machine_config& mconfig, device_type type, const char* tag) :
		n64_state(mconfig, type, tag)
	{ }

	void n64_lodgenet_map(address_map &map) ATTR_COLD;
	void n64_lodgenet(machine_config &config);

private:
	void n64_map(address_map &map) ATTR_COLD;
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	void rsp_imem_map(address_map &map) ATTR_COLD;
	void rsp_dmem_map(address_map &map) ATTR_COLD;
};

void n64_gateway_state::n64_lodgenet_map(address_map &map)
{
	map(0x00000000, 0x007fffff).ram().share("rdram");                   // RDRAM
	map(0x03f00000, 0x03f00027).rw("rcp", FUNC(n64_periphs::rdram_reg_r), FUNC(n64_periphs::rdram_reg_w));
	map(0x04000000, 0x04000fff).ram().share("rsp_dmem");                    // RSP DMEM
	map(0x04001000, 0x04001fff).ram().share("rsp_imem");                    // RSP IMEM
	map(0x04040000, 0x040fffff).rw("rcp", FUNC(n64_periphs::sp_reg_r), FUNC(n64_periphs::sp_reg_w));  // RSP
	map(0x04100000, 0x041fffff).rw("rcp", FUNC(n64_periphs::dp_reg_r), FUNC(n64_periphs::dp_reg_w));  // RDP
	map(0x04300000, 0x043fffff).rw("rcp", FUNC(n64_periphs::mi_reg_r), FUNC(n64_periphs::mi_reg_w));    // MIPS Interface
	map(0x04400000, 0x044fffff).rw("rcp", FUNC(n64_periphs::vi_reg_r), FUNC(n64_periphs::vi_reg_w));    // Video Interface
	map(0x04500000, 0x045fffff).rw("rcp", FUNC(n64_periphs::ai_reg_r), FUNC(n64_periphs::ai_reg_w));    // Audio Interface
	map(0x04600000, 0x046fffff).rw("rcp", FUNC(n64_periphs::pi_reg_r), FUNC(n64_periphs::pi_reg_w));    // Peripheral Interface
	map(0x04700000, 0x047fffff).rw("rcp", FUNC(n64_periphs::ri_reg_r), FUNC(n64_periphs::ri_reg_w));    // RDRAM Interface
	map(0x04800000, 0x048fffff).rw("rcp", FUNC(n64_periphs::si_reg_r), FUNC(n64_periphs::si_reg_w));    // Serial Interface
	map(0x05000508, 0x0500050b).lr32(NAME([] () { return 0xffffffff; }));
	map(0x08000000, 0x0801ffff).ram().share("sram");                                        // Cartridge SRAM
	map(0x10000000, 0x13ffffff).rom().region("user2", 0);                                   // Cartridge
	map(0x1fc00000, 0x1fc007bf).rom().region("user1", 0);                                   // PIF ROM
	map(0x1fc007c0, 0x1fc007ff).rw("rcp", FUNC(n64_periphs::pif_ram_r), FUNC(n64_periphs::pif_ram_w));

	// map(0x0ff70000, 0x0ff70003) Host HALT control
}

void n64_gateway_state::rsp_imem_map(address_map &map)
{
	map(0x00000000, 0x00000fff).ram().share("rsp_imem");
}

void n64_gateway_state::rsp_dmem_map(address_map &map)
{
	map(0x00000000, 0x00000fff).ram().share("rsp_dmem");
}



static INPUT_PORTS_START( n64_lodgenet )
	PORT_START("input")
	PORT_CONFNAME(0x0003, 0x0001, "Controller Port 0 Device")
	PORT_CONFSETTING(0x00, "None")
	PORT_CONFSETTING(0x01, "Joypad")
//  PORT_CONFSETTING(0x02, "Mouse")
	PORT_BIT( 0xfc, IP_ACTIVE_HIGH, IPT_UNUSED )
//  PORT_CONFNAME(0x000C, 0x0000, "Controller Port 1 Device")
//  PORT_CONFSETTING(0x00, "None")
//  PORT_CONFSETTING(0x04, "Joypad")
//  PORT_CONFSETTING(0x08, "Mouse")
//  PORT_CONFNAME(0x0030, 0x0000, "Controller Port 2 Device")
//  PORT_CONFSETTING(0x00, "None")
//  PORT_CONFSETTING(0x10, "Joypad")
//  PORT_CONFSETTING(0x20, "Mouse")
//  PORT_CONFNAME(0x00C0, 0x0000, "Controller Port 3 Device")
//  PORT_CONFSETTING(0x00, "None")
//  PORT_CONFSETTING(0x40, "Joypad")
//  PORT_CONFSETTING(0x80, "Mouse")

	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )

//  PORT_CONFNAME(0x0100, 0x0000, "Disk Drive")
//  PORT_CONFSETTING(0x0000, "Retail")
//  PORT_CONFSETTING(0x0100, "Development")
//

	PORT_CONFNAME(0xC000, 0x8000, "EEPROM Size")
	PORT_CONFSETTING(0x0000, "None")
	PORT_CONFSETTING(0x8000, "4KB")
	PORT_CONFSETTING(0xC000, "16KB")

	//Player 1
	// TODO: extra buttons
	PORT_START("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_BUTTON2 )        PORT_PLAYER(1) PORT_NAME("P1 Button A / Left Click")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(1) PORT_NAME("P1 Button B / Right Click")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_BUTTON3 )        PORT_PLAYER(1) PORT_NAME("P1 Button Z")
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_START )          PORT_PLAYER(1) PORT_NAME("P1 Start")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1) PORT_NAME("P1 Joypad \xE2\x86\x91") /* Up */
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1) PORT_NAME("P1 Joypad \xE2\x86\x93") /* Down */
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1) PORT_NAME("P1 Joypad \xE2\x86\x90") /* Left */
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("P1 Joypad \xE2\x86\x92") /* Right */
	PORT_BIT( 0x00c0, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON4 )        PORT_PLAYER(1) PORT_NAME("P1 Button L")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON5 )        PORT_PLAYER(1) PORT_NAME("P1 Button R")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_BUTTON6 )        PORT_PLAYER(1) PORT_NAME("P1 Button C \xE2\x86\x91") /* Up */
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_BUTTON7 )        PORT_PLAYER(1) PORT_NAME("P1 Button C \xE2\x86\x93") /* Down */
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON8 )        PORT_PLAYER(1) PORT_NAME("P1 Button C \xE2\x86\x90") /* Left */
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_BUTTON9 )        PORT_PLAYER(1) PORT_NAME("P1 Button C \xE2\x86\x92") /* Right */

	PORT_START("P1_ANALOG_X")
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_X ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(30) PORT_KEYDELTA(30) PORT_PLAYER(1)

	PORT_START("P1_ANALOG_Y")
	PORT_BIT( 0xff, 0x80, IPT_AD_STICK_Y ) PORT_MINMAX(0x00,0xff) PORT_SENSITIVITY(30) PORT_KEYDELTA(30) PORT_PLAYER(1) PORT_REVERSE

	PORT_START("P1_MOUSE_X")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P1_MOUSE_Y")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	//Player 2
	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2_ANALOG_X")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2_ANALOG_Y")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2_MOUSE_X")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2_MOUSE_Y")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	//Player 3
	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3_ANALOG_X")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3_ANALOG_Y")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3_MOUSE_X")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3_MOUSE_Y")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	//Player 4
	PORT_START("P4")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P4_ANALOG_X")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P4_ANALOG_Y")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P4_MOUSE_X")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P4_MOUSE_Y")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("RESET")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )
	// no reset button available
//  PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_OTHER )        PORT_NAME("Warm Reset") PORT_CODE(KEYCODE_3)

INPUT_PORTS_END

// TODO: c&p from n64.cpp, shouldn't have this at all
DEVICE_IMAGE_LOAD_MEMBER(n64_gateway_state::cart_load)
{
	int i, length;
	uint8_t *cart = memregion("user2")->base();

	if (!image.loaded_through_softlist())
	{
		length = image.fread(cart, 0x4000000);
	}
	else
	{
		length = image.get_software_region_length("rom");
		memcpy(cart, image.get_software_region("rom"), length);
	}
	m_rcp_periphs->cart_length = length;

	if (cart[0] == 0x37 && cart[1] == 0x80)
	{
		for (i = 0; i < length; i += 4)
		{
			uint8_t b1 = cart[i + 0];
			uint8_t b2 = cart[i + 1];
			uint8_t b3 = cart[i + 2];
			uint8_t b4 = cart[i + 3];
			cart[i + 0] = b3;
			cart[i + 1] = b4;
			cart[i + 2] = b1;
			cart[i + 3] = b2;
		}
	}
	else
	{
		for (i = 0; i < length; i += 4)
		{
			uint8_t b1 = cart[i + 0];
			uint8_t b2 = cart[i + 1];
			uint8_t b3 = cart[i + 2];
			uint8_t b4 = cart[i + 3];
			cart[i + 0] = b4;
			cart[i + 1] = b3;
			cart[i + 2] = b2;
			cart[i + 3] = b1;
		}
	}

	m_rcp_periphs->m_nvram_image = &image.device();

	logerror("cart length = %d\n", length);

	device_image_interface *battery_image = dynamic_cast<device_image_interface *>(m_rcp_periphs->m_nvram_image);
	if (battery_image)
	{
		//printf("Loading\n");
		uint8_t data[0x30800];
		battery_image->battery_load(data, 0x30800, 0x00);
		if (m_sram != nullptr)
		{
			memcpy(m_sram, data, 0x20000);
		}
		memcpy(m_rcp_periphs->m_save_data.eeprom, data + 0x20000, 0x800);
		//memcpy(m_rcp_periphs->m_save_data.mempak[0], data + 0x20800, 0x8000);
		//memcpy(m_rcp_periphs->m_save_data.mempak[1], data + 0x28800, 0x8000);
	}

	//if (m_rcp_periphs->m_save_data.mempak[0][0] == 0) // Init if new
	//{
		//memset(m_rcp_periphs->m_save_data.eeprom, 0, 0x800);
		//mempak_format(m_rcp_periphs->m_save_data.mempak[0]);
		//mempak_format(m_rcp_periphs->m_save_data.mempak[1]);
	//}

	return std::make_pair(std::error_condition(), std::string());
}

void n64_gateway_state::n64_lodgenet(machine_config &config)
{
	VR4300BE(config, m_vr4300, 93750000);
	m_vr4300->set_force_no_drc(false);
	//m_vr4300->set_icache_size(16384);
	//m_vr4300->set_dcache_size(8192);
	//m_vr4300->set_system_clock(62500000);
	m_vr4300->set_addrmap(AS_PROGRAM, &n64_gateway_state::n64_lodgenet_map);
//  m_vr4300->set_vblank_int("screen", FUNC(n64_console_state::n64_reset_poll));

	RSP(config, m_rsp, 62500000);
	m_rsp->set_force_no_drc(false);
	m_rsp->dp_reg_r().set(m_rcp_periphs, FUNC(n64_periphs::dp_reg_r));
	m_rsp->dp_reg_w().set(m_rcp_periphs, FUNC(n64_periphs::dp_reg_w));
	m_rsp->sp_reg_r().set(m_rcp_periphs, FUNC(n64_periphs::sp_reg_r));
	m_rsp->sp_reg_w().set(m_rcp_periphs, FUNC(n64_periphs::sp_reg_w));
	m_rsp->status_set().set(m_rcp_periphs, FUNC(n64_periphs::sp_set_status));
	m_rsp->set_addrmap(AS_PROGRAM, &n64_gateway_state::rsp_imem_map);
	m_rsp->set_addrmap(AS_DATA, &n64_gateway_state::rsp_dmem_map);

	config.set_maximum_quantum(attotime::from_hz(500000));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(DACRATE_NTSC*2,3093,0,3093,525,0,525);
	m_screen->set_screen_update(FUNC(n64_state::screen_update));
	m_screen->screen_vblank().set(FUNC(n64_state::screen_vblank));

	PALETTE(config, "palette").set_entries(0x1000);

	SPEAKER(config, "speaker", 2).front();

	DMADAC(config, "dac2").add_route(ALL_OUTPUTS, "speaker", 1.0, 0);
	DMADAC(config, "dac1").add_route(ALL_OUTPUTS, "speaker", 1.0, 1);

	N64PERIPH(config, m_rcp_periphs, 0);

	/* cartridge */
	generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "n64_cart", "v64,z64,rom,n64,bin"));
	cartslot.set_must_be_loaded(true);
	cartslot.set_device_load(FUNC(n64_gateway_state::cart_load));


	SOFTWARE_LIST(config, "cart_list").set_original("n64_lodgenet");
}

ROM_START( n64_lodgenet )
	ROM_REGION( 0x20000, "gateway64", ROMREGION_ERASEFF )
	// unknown CPU/controller, likely multiple ROMs for set-top box & game data receiver.
	ROM_LOAD( "gateway64.bios", 0, 0x20000, NO_DUMP )

	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )      /* dummy region for R4300 */

	ROM_REGION32_BE( 0x800, "user1", 0 )
	ROM_LOAD( "pifdata.bin", 0x0000, 0x0800, CRC(5ec82be9) SHA1(9174eadc0f0ea2654c95fd941406ab46b9dc9bdd) )

	ROM_REGION32_BE( 0x4000000, "user2", ROMREGION_ERASEFF)

	ROM_REGION16_BE( 0x80, "normpoint", 0 )
	ROM_LOAD( "normpnt.rom", 0x00, 0x80, CRC(e7f2a005) SHA1(c27b4a364a24daeee6e99fd286753fd6216362b4) )

	ROM_REGION16_BE( 0x80, "normslope", 0 )
	ROM_LOAD( "normslp.rom", 0x00, 0x80, CRC(4f2ae525) SHA1(eab43f8cc52c8551d9cff6fced18ef80eaba6f05) )
ROM_END

CONS(1997?, n64_lodgenet,   n64,   0, n64_lodgenet,   n64_lodgenet, n64_gateway_state, empty_init, "Nintendo / LodgeNet", "Nintendo Gateway 64",   MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )



nabupc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Brian Johnson
/************************************************************************
 *   NABU PC
 *
 *   https://nabu.ca/
 *
 *
 ***********************************************************************/

#include "emu.h"

#include "nabupc_kbd.h"

#include "bus/centronics/ctronics.h"
#include "bus/nabupc/adapter.h"
#include "bus/nabupc/option.h"
#include "bus/rs232/null_modem.h"
#include "bus/rs232/pty.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/ay31015.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/ram.h"
#include "sound/ay8910.h"
#include "video/tms9928a.h"
#include "speaker.h"

#include "nabupc.lh"

//**************************************************************************
//  UTILITY FUNCTIONS
//**************************************************************************

// Bit Manipulation
namespace {
	template<typename T> constexpr T BIT_SHIFT(unsigned n, unsigned v)
	{
		return (T)v << n;
	}

	template<typename T> void BIT_SET(T& w , unsigned n, unsigned v)
	{
		T mask = ~BIT_SHIFT<T>(n, 1);
		w = (w & mask) | BIT_SHIFT<T>(n, v);
	}

// Priority Encoder
/** @brief F9318 input lines */
enum f9318_in {
	PRIO_IN_EI = (1<<8),
	PRIO_IN_I7 = (1<<7),
	PRIO_IN_I6 = (1<<6),
	PRIO_IN_I5 = (1<<5),
	PRIO_IN_I4 = (1<<4),
	PRIO_IN_I3 = (1<<3),
	PRIO_IN_I2 = (1<<2),
	PRIO_IN_I1 = (1<<1),
	PRIO_IN_I0 = (1<<0),
	/* masks */
	PRIO_I7 = PRIO_IN_I7,
	PRIO_I6_I7 = (PRIO_IN_I6 | PRIO_IN_I7),
	PRIO_I5_I7 = (PRIO_IN_I5 | PRIO_I6_I7),
	PRIO_I4_I7 = (PRIO_IN_I4 | PRIO_I5_I7),
	PRIO_I3_I7 = (PRIO_IN_I3 | PRIO_I4_I7),
	PRIO_I2_I7 = (PRIO_IN_I2 | PRIO_I3_I7),
	PRIO_I1_I7 = (PRIO_IN_I1 | PRIO_I2_I7),
	PRIO_I0_I7 = (PRIO_IN_I0 | PRIO_I1_I7),
};

/** @brief F9318 output lines */
enum f9318_out {
	PRIO_OUT_Q0 = (1<<0),
	PRIO_OUT_Q1 = (1<<1),
	PRIO_OUT_Q2 = (1<<2),
	PRIO_OUT_EO = (1<<3),
	PRIO_OUT_GS = (1<<4),
	/* masks */
	PRIO_OUT_QZ = (PRIO_OUT_Q0 | PRIO_OUT_Q1 | PRIO_OUT_Q2)
};

/**
 * @brief F9318 priority encoder 8 to 3-bit
 *
 * Emulation of the F9318 chip (pin compatible with 74348).
 *
 * <PRE>
 *            F9318
 *         +---+-+---+
 *         |   +-+   |         +---------------------------------+----------------+
 *    I4' -|1      16|-  Vcc   |              input              |     output     |
 *         |         |         +---------------------------------+----------------+
 *    I5' -|2      15|-  EO'   |      EI I0 I1 I2 I3 I4 I5 I6 I7 | GS Q0 Q1 Q2 EO |
 *         |         |         +---------------------------------+----------------+
 *    I6' -|3      14|-  GS'   | (a)  H  x  x  x  x  x  x  x  x  | H  H  H  H  H  |
 *         |         |         | (b)  L  H  H  H  H  H  H  H  H  | H  H  H  H  L  |
 *    I7' -|4      13|-  I3'   +---------------------------------+----------------+
 *         |         |         | (c)  L  x  x  x  x  x  x  x  L  | L  L  L  L  H  |
 *    EI' -|5      12|-  I2'   | (d)  L  x  x  x  x  x  x  L  H  | L  H  L  L  H  |
 *         |         |         | (e)  L  x  x  x  x  x  L  H  H  | L  L  H  L  H  |
 *    Q2' -|6      11|-  I1'   | (f)  L  x  x  x  x  L  H  H  H  | L  H  H  L  H  |
 *         |         |         | (g)  L  x  x  x  L  H  H  H  H  | L  L  L  H  H  |
 *    Q1' -|7      10|-  I0'   | (h)  L  x  x  L  H  H  H  H  H  | L  H  L  H  H  |
 *         |         |         | (i)  L  x  L  H  H  H  H  H  H  | L  L  H  H  H  |
 *   GND  -|8       9|-  Q0'   | (j)  L  L  H  H  H  H  H  H  H  | L  H  H  H  H  |
 *         |         |         +---------------------------------+----------------+
 *         +---------+
 * </PRE>
 */
inline uint8_t f9318(uint16_t in)
{
	uint8_t out;

	if (in & PRIO_IN_EI) {
		out = PRIO_OUT_EO | PRIO_OUT_GS | PRIO_OUT_QZ;
		return out;
	}

	if (0 == (in & PRIO_I7)) {
		out = PRIO_OUT_EO;
		return out;
	}

	if (PRIO_I7 == (in & PRIO_I6_I7)) {
		out = PRIO_OUT_EO | PRIO_OUT_Q0;
		return out;
	}

	if (PRIO_I6_I7 == (in & PRIO_I5_I7)) {
		out = PRIO_OUT_EO | PRIO_OUT_Q1;
		return out;
	}

	if (PRIO_I5_I7 == (in & PRIO_I4_I7)) {
		out = PRIO_OUT_EO | PRIO_OUT_Q0 | PRIO_OUT_Q1;
		return out;
	}

	if (PRIO_I4_I7 == (in & PRIO_I3_I7)) {
		out = PRIO_OUT_EO | PRIO_OUT_Q2;
		return out;
	}

	if (PRIO_I3_I7 == (in & PRIO_I2_I7)) {
		out = PRIO_OUT_EO | PRIO_OUT_Q0 | PRIO_OUT_Q2;
		return out;
	}

	if (PRIO_I2_I7 == (in & PRIO_I1_I7)) {
		out = PRIO_OUT_EO | PRIO_OUT_Q1 | PRIO_OUT_Q2;
		return out;
	}

	if (PRIO_I1_I7 == (in & PRIO_I0_I7)) {
		out = PRIO_OUT_EO | PRIO_OUT_Q0 | PRIO_OUT_Q1 | PRIO_OUT_Q2;
		return out;
	}

	out = PRIO_OUT_QZ | PRIO_OUT_GS;
	return out;
}

}

//**************************************************************************
//  DRIVER CLASS
//**************************************************************************

class nabupc_state : public driver_device
{
public:
	nabupc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_tms9928a(*this, "tms9928a")
		, m_screen(*this, "screen")
		, m_ay8910(*this, "ay8910")
		, m_speaker(*this, "speaker")
		, m_kbduart(*this, "kbduart")
		, m_hccauart(*this, "hccauart")
		, m_rom_view(*this, "rom")
		, m_centronics(*this, "centronics")
		, m_bus(*this, "bus")
		, m_rom_base(*this, "bios")
		, m_bios_sel(*this, "CONFIG")
		, m_leds(*this, "led%u", 0U)
		, m_irq_in_prio(PRIO_I0_I7)
		, m_int_lines(0)
		, m_porta(0)
		, m_portb(0)
		, m_control(0)
	{
	}

	void nabupc(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void memory_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	uint8_t read_mem(offs_t offset);

	uint8_t psg_portb_r();
	void psg_porta_w(uint8_t data);
	void control_w(uint8_t data);
	void centronics_busy_handler(uint8_t state);
	void update_irq();

	void hcca_fe_w(int state);
	void hcca_oe_w(int state);

	template <unsigned N> void int_w(int state);

	IRQ_CALLBACK_MEMBER(int_ack_cb);

	required_device<z80_device> m_maincpu;
	required_device<tms9928a_device> m_tms9928a;
	required_device<screen_device> m_screen;
	required_device<ay8910_device> m_ay8910;
	required_device<speaker_device> m_speaker;
	required_device<i8251_device> m_kbduart;
	required_device<ay31015_device> m_hccauart;
	memory_view m_rom_view;
	required_device<centronics_device> m_centronics;
	required_device<bus::nabupc::option_bus_device> m_bus;
	required_region_ptr<uint8_t> m_rom_base;
	required_ioport m_bios_sel;

	output_finder<4> m_leds;

	uint16_t m_irq_in_prio;
	uint8_t m_int_lines;
	uint8_t m_porta;
	uint8_t m_portb;
	uint8_t m_control;
	uint16_t m_bios_size;
};

//**************************************************************************
//  INPUT PORTS
//**************************************************************************

// CONFIG Input Port
static INPUT_PORTS_START( nabupc )
	PORT_START("CONFIG")
	PORT_CONFNAME( 0x03, 0x02, "BIOS Size" )
	PORT_CONFSETTING( 0x02, "4k BIOS" )
	PORT_CONFSETTING( 0x01, "8k BIOS" )
INPUT_PORTS_END


//**************************************************************************
//  Peripheral Devices
//**************************************************************************

// HCCA Devices
static void hcca_devices(device_slot_interface &device)
{
	device.option_add("pty",           PSEUDO_TERMINAL);
	device.option_add("null_modem",    NULL_MODEM);
	device.option_add("hcca_adapter",  NABUPC_NETWORK_ADAPTER);
}

//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

void nabupc_state::nabupc(machine_config &config)
{
	// Front Panel LEDS
	config.set_default_layout(layout_nabupc);

	// CPU
	Z80(config, m_maincpu, 10.738635_MHz_XTAL / 3);         // 3.579545 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &nabupc_state::memory_map);
	m_maincpu->set_addrmap(AS_IO, &nabupc_state::io_map);
	m_maincpu->set_irq_acknowledge_callback(FUNC(nabupc_state::int_ack_cb));

	// Video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);

	TMS9918A(config, m_tms9928a, 10.738635_MHz_XTAL);
	m_tms9928a->set_screen(m_screen);
	m_tms9928a->set_vram_size(0x4000);
	m_tms9928a->int_callback().set(*this, FUNC(nabupc_state::int_w<4>));

	// Sound hardware
	SPEAKER(config, m_speaker).front_center();

	AY8910(config, m_ay8910, 10.738635_MHz_XTAL / 6);
	m_ay8910->set_flags(AY8910_SINGLE_OUTPUT);
	m_ay8910->port_b_read_callback().set(FUNC(nabupc_state::psg_portb_r));
	m_ay8910->port_a_write_callback().set(FUNC(nabupc_state::psg_porta_w));
	m_ay8910->add_route(ALL_OUTPUTS, m_speaker, 0.3);

	// Keyboard
	I8251(config, m_kbduart, 10.738635_MHz_XTAL / 6);
	m_kbduart->rxrdy_handler().set(*this, FUNC(nabupc_state::int_w<5>));

	nabupc_keyboard_device &kbd(NABUPC_KEYBOARD(config, "kbd"));
	kbd.rxd_cb().set(m_kbduart, FUNC(i8251_device::write_rxd));

	// HCCA
	AY31015(config, m_hccauart);
	m_hccauart->set_auto_rdav(true);
	m_hccauart->write_dav_callback().set(FUNC(nabupc_state::int_w<7>));
	m_hccauart->write_tbmt_callback().set(FUNC(nabupc_state::int_w<6>));
	m_hccauart->write_fe_callback().set(FUNC(nabupc_state::hcca_fe_w));
	m_hccauart->write_or_callback().set(FUNC(nabupc_state::hcca_oe_w));
	m_hccauart->write_so_callback().set("hcca", FUNC(rs232_port_device::write_txd));

	rs232_port_device &hcca(RS232_PORT(config, "hcca", hcca_devices, "pty"));
	hcca.rxd_handler().set(m_hccauart, FUNC(ay31015_device::write_si));

	// Printer
	output_latch_device &prndata(OUTPUT_LATCH(config, "prndata"));
	CENTRONICS(config, m_centronics, centronics_devices, nullptr);
	m_centronics->set_output_latch(prndata);
	m_centronics->busy_handler().set(FUNC(nabupc_state::centronics_busy_handler));

	// Clocks
	clock_device &sclk(CLOCK(config, "sclk", 10.738635_MHz_XTAL / 96)); // 111.9 kHz Clock
	sclk.signal_handler().set(m_kbduart, FUNC(i8251_device::write_rxc));

	clock_device &pclk(CLOCK(config, "pclk", 10.738635_MHz_XTAL / 6));  // 1.79 Mhz Clock
	pclk.signal_handler().set(m_hccauart, FUNC(ay31015_device::write_rcp));
	pclk.signal_handler().append(m_hccauart, FUNC(ay31015_device::write_tcp));

	// Bus
	NABUPC_OPTION_BUS(config, m_bus, 10.738635_MHz_XTAL / 3);
	m_bus->out_int_callback<0>().set(FUNC(nabupc_state::int_w<3>)).invert();
	m_bus->out_int_callback<1>().set(FUNC(nabupc_state::int_w<2>)).invert();
	m_bus->out_int_callback<2>().set(FUNC(nabupc_state::int_w<1>)).invert();
	m_bus->out_int_callback<3>().set(FUNC(nabupc_state::int_w<0>)).invert();
	NABUPC_OPTION_BUS_SLOT(config, "option1", m_bus, 0, bus::nabupc::option_bus_devices, nullptr);
	NABUPC_OPTION_BUS_SLOT(config, "option2", m_bus, 1, bus::nabupc::option_bus_devices, nullptr);
	NABUPC_OPTION_BUS_SLOT(config, "option3", m_bus, 2, bus::nabupc::option_bus_devices, nullptr);
	NABUPC_OPTION_BUS_SLOT(config, "option4", m_bus, 3, bus::nabupc::option_bus_devices, nullptr);
}


//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

// Machine Start
void nabupc_state::machine_start()
{
	m_leds.resolve();

	m_hccauart->write_np(1);
	m_hccauart->write_nb2(1);
	m_hccauart->write_nb1(1);
	m_hccauart->write_eps(0);
	m_hccauart->write_tsb(0);
	m_hccauart->write_cs(1);
	m_hccauart->write_swe(0);

	save_item(NAME(m_irq_in_prio));
	save_item(NAME(m_int_lines));
	save_item(NAME(m_porta));
	save_item(NAME(m_portb));
	save_item(NAME(m_control));
	save_item(NAME(m_bios_size));

}

// Machine Reset
void nabupc_state::machine_reset()
{
	m_irq_in_prio = PRIO_I0_I7;
	m_int_lines = 0;
	m_porta = 0;
	m_portb &= 0x70;
	m_control = 0;
	m_bios_size = m_bios_sel->read() == 1 ? 0x2000 : 0x1000;
	m_leds[0] = 1; // Power LED

	m_rom_view[0].install_rom(0x0000, m_bios_size - 1, m_rom_base);
	m_rom_view.select(0);
}


//**************************************************************************
//  Control/Status Ports
//**************************************************************************

// Control Register
void nabupc_state::control_w(uint8_t data)
{
	m_control = data;
	m_centronics->write_strobe(BIT(m_control, 2));
	for (int i = 3 ; i < 6 ; ++i) {
		m_leds[6 - i] = BIT(m_control, i);
	}
	if ((m_control & 1) == 0) {
		m_rom_view.select(0);
	} else {
		m_rom_view.disable();
	}
}

// Status Port
uint8_t nabupc_state::psg_portb_r()
{
	return m_portb;
}

// Interrupt Enable Port
void nabupc_state::psg_porta_w(uint8_t data)
{
	if (data != m_porta) {
		m_porta = data;
		update_irq();
	}
}

// Printer Busy State
void nabupc_state::centronics_busy_handler(uint8_t state)
{
	BIT_SET(m_portb, 4, state);
}

// HCCA Framing Error
void nabupc_state::hcca_fe_w(int state)
{
	BIT_SET(m_portb, 5, state);
}

// HCCA Overrun Error
void nabupc_state::hcca_oe_w(int state)
{
	BIT_SET(m_portb, 6, state);
}


//**************************************************************************
//  Interrupt Handling
//**************************************************************************

template <unsigned N>
void nabupc_state::int_w(int state)
{
	BIT_SET(m_int_lines, N, state);
	update_irq();
}

// IRQ Acknowledge
IRQ_CALLBACK_MEMBER(nabupc_state::int_ack_cb)
{
	uint32_t vector = m_portb & 0xe;
	return vector;
}

// Update CPU IRQ line
void nabupc_state::update_irq()
{
	uint8_t interrupts = ~(m_porta & m_int_lines);
	m_irq_in_prio = (m_irq_in_prio & 0xff00) | interrupts;

	m_portb &= 0xf0;
	uint8_t out = f9318(m_irq_in_prio);
	m_portb |= bitswap(out, 2, 1, 0, 3);
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, !(BIT(out, 4)));
}


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

// RAM/ROM
void nabupc_state::memory_map(address_map &map)
{
	map(0x0000, 0xffff).ram();
	map(0x0000, 0x2000).view(m_rom_view);
}

// IO Memory
void nabupc_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x00).w(FUNC(nabupc_state::control_w));
	map(0x40, 0x40).r(m_ay8910, FUNC(ay8910_device::data_r));
	map(0x40, 0x41).w(m_ay8910, FUNC(ay8910_device::data_address_w));
	map(0x80, 0x80).rw(m_hccauart, FUNC(ay31015_device::receive), FUNC(ay31015_device::transmit));
	map(0x90, 0x91).rw(m_kbduart, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xa0, 0xa1).rw(m_tms9928a, FUNC(tms9928a_device::read), FUNC(tms9928a_device::write));
	map(0xb0, 0xb0).w("prndata", FUNC(output_latch_device::write));
	map(0xc0, 0xcf).rw(m_bus, FUNC(bus::nabupc::option_bus_device::read<0>), FUNC(bus::nabupc::option_bus_device::write<0>));
	map(0xd0, 0xdf).rw(m_bus, FUNC(bus::nabupc::option_bus_device::read<1>), FUNC(bus::nabupc::option_bus_device::write<1>));
	map(0xe0, 0xef).rw(m_bus, FUNC(bus::nabupc::option_bus_device::read<2>), FUNC(bus::nabupc::option_bus_device::write<2>));
	map(0xf0, 0xff).rw(m_bus, FUNC(bus::nabupc::option_bus_device::read<3>), FUNC(bus::nabupc::option_bus_device::write<3>));
}

//**************************************************************************
//  BIOS ROMS
//**************************************************************************

ROM_START( nabupc )
	ROM_REGION( 0x2000, "bios", 0 )
	ROM_DEFAULT_BIOS("reva")
	ROM_SYSTEM_BIOS( 0, "reva", "4k BIOS (Rev A)" )
	ROMX_LOAD( "nabupc-u53-90020060-reva-2732.bin", 0x0000, 0x1000, CRC(8110bde0) SHA1(57e5f34645df06d7cb6c202a6d35a442776af2cb), ROM_BIOS(0) )
	ROM_RELOAD(0x1000, 0x1000)
	ROM_SYSTEM_BIOS( 1, "ver14", "4k BIOS - Floppy support (Ver 14)" )
	ROMX_LOAD( "nabupc-u53-ver14-2732.bin", 0x0000, 0x1000, CRC(ca5e1ae9) SHA1(d713abd5d387a63a287d4ea51196ba5de42db052), ROM_BIOS(1) )
	ROM_RELOAD(0x1000, 0x1000)
	ROM_SYSTEM_BIOS( 2, "ver17", "4k BIOS - Floppy support (Ver 17)" )
	ROMX_LOAD( "nabupc-u53-ver17-2732.bin", 0x0000, 0x1000, CRC(24d4f1fa) SHA1(1b9533c709604a21aa5fcc32071d0b0630e89c20), ROM_BIOS(2) )
	ROM_RELOAD(0x1000, 0x1000)
	ROM_SYSTEM_BIOS( 3, "revb", "8k BIOS - Floppy support (Rev B)" )
	ROMX_LOAD( "nabupc-u53-90020060-revb-2764.bin", 0x0000, 0x2000, CRC(3088f21b) SHA1(bf2f1eb5d9f5a8e9d022ce0056f2a5a8526b830e), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "ver29", "8k BIOS - Floppy support (Ver 29)" )
	ROMX_LOAD( "nabupc-u53-90037150-ver29-2764.bin", 0x0000, 0x2000, CRC(3c484e3d) SHA1(dd10ad6e0a59c54561335272d3c808b0543ba0ef), ROM_BIOS(4) )
ROM_END


/***************************************************************************
    SYSTEM DRIVERS
***************************************************************************/

/*   YEAR  NAME      PARENT    COMPAT MACHINE   INPUT        CLASS         INIT        COMPANY FULLNAME   */
COMP(1982, nabupc,   0,        0,     nabupc,   nabupc,      nabupc_state, empty_init, "NABU", "NABU PC", 0)




nakajies.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol,Sandro Ronco
/******************************************************************************

  Driver for the ES-2xx series electronic typewriters made by Nakajima.

Nakajima was the OEM manufacturer for a series of typewriters which were
sold by different brands around the world. The PCB layouts for these
machines are the same. The models differed in the amount of (static) RAM:
128KB or 256KB; and in the system rom (mainly only different language
support).


Model   |  SRAM | Language | Branded model
--------+-------+----------+----------------------
ES-210N | 128KB | German   | Walther ES-210
ES-220  | 128KB | English  | NTS DreamWriter T100
ES-220  | 256KB | English  | NTS DreamWriter T400
ES-210N | 128KB | Spanish  | Dator 3000
ES-210N | 128KB | English  | NTS DreamWriter 325
ES-250  | xxxKB | English  | NTS DreamWriter T200


The LCD is driven by 6x Sanyo LC7940 and 1x Sanyo LC7942.


The keyboard matrix:

   |         |       |        |        |    |    |    |         |
   |         |       |        |        |    |    |    |         |
-- LSHIFT --   ----- LEFT --- ENTER --   --   --   -- RSHIFT --   ---
   |         |       |        |        |    |    |    |         |
-- 3 ------- Q ----- W ------ E ------ S -- D --   -- 2 -------   ---
   |         |       |        |        |    |    |    |         |
-- 4 ------- Z ----- X ------ A ------ R -- F --   --   -------   ---
   |         |       |        |        |    |    |    |         |
--   ------- B ----- V ------ T ------ G -- C -- Y --   -------   ---
   |         |       |        |        |    |    |    |         |
-- CTRL ---- 1 ----- TAB ----   ------   --   --   -- CAPS ----   ---
   |         |       |        |        |    |    |    |         |
-- ALT ----- CAN --- SPACE --   ------ 5 --   --   -- ` -------   ---
   |         |       |        |        |    |    |    |         |
--   ------- INS --- RIGHT -- \ ------ H -- N -- / -- DOWN ---- 6 ---
   |         |       |        |        |    |    |    |         |
--   ------- ORGN -- UP ----- WP ----- M -- K -- U -- 7 ------- = ---
   |         |       |        |        |    |    |    |         |
--   ------- ] ----- [ ------ ' ------ J -- , -- I -- - ------- 8 ---
   |         |       |        |        |    |    |    |         |
--   ------- BACK -- P ------ ; ------ O -- . -- L -- 9 ------- 0 ---
   |         |       |        |        |    |    |    |         |
   |         |       |        |        |    |    |    |         |



NTS information from http://web.archive.org/web/19980205154137/nts.dreamwriter.com/dreamwt4.html:

File Management & Memory:

- Uniquely name up to 128 files
- Recall, rename or delete files
- Copy files to and from PCMCIA Memory card
- PCMCIA Memory expansion cards available for 60 or 250 pages of text
- Working memory allows up to 20 pages of text (50KB) to be displayed
- Storage memory allows up to 80 pages of text (128KB) in total
- DreamLink software exports and imports documents in RTF retaining all
  formatting to Macintosh or Windows PC to all commonly used Word Processing programs
- Transfer cable provided compatible to both Macintosh and Windows PC's.
- T400 is field upgradeable to IR with the optional Infrared module.

Hardware:

- LCD Screen displays 8 lines by 80 characters raised and tilted 30 degrees
- Contrast Dial and feet adjust to user preference
- Parallel and Serial ports (IR Upgrade Optional) for connectivity to printers, Macintosh and Windows PC's
- PCMCIA Slot
- Full size 64 key keyboard with color coded keys and quick reference menu bar
- NiCad rechargeable batteries for up to 8 hours of continuous use prior to recharging
- AC adapter for recharging batteries is lightweight and compact design
- NEC V20HL 9.83 MHz processor for fast response time
- Durable solid state construction weighing 2.2 lbs including battery pack
- Dimensions approximately 11" wide by 8" long by 1" deep
- FCC and CSA approved


I/O Map:

0000 - unknown
       0x00 written during boot sequence

0010 - unknown
       0x17 written during boot sequence

0011 - unknown
       0x1e written during boot sequence

0012 - unknown
       0x1f written during boot sequence

0013 - unknown
       0x1e written during boot sequence

0014 - unknown
       0x1d written during boot sequence

0015 - unknown
       0x1c written during boot sequence

0012-0015 - banking?
T200:
Time: 15 - 02; call ADxxx
Database: 15 - 02; call B3xxx
Spreadsheet: 15 - 02; call B9xxx

T450:
Regular boot: 12 - 1f; 13 - 1e; 14 - 1d; 15 - 1c
Typing game: 15 - 02; call B100:0;
Edit Text: 12 - 07; 13 - 06; 14 - 05; 15 - 04; call 4000:0000
c000:0000 - ffff:ffff = not banked?

T400, wales210:
Regular boot: 12 - 1f; 13 - 1e; 14 - 1d; 15 - 1c
Next step during boot: 12 - 17; 13 - 3; 14 - 2; jump to 3000:0000
writing 17 to port 12 maps rom offset 30000 to 3000:0000?

1f, 1e, 1d, 1c is banked RAM ??

banking possibility:
0010-0017 - control banking:
0010 - 00000 - 1ffff
0011 - 20000 - 3ffff
0012 - 40000 - 5ffff
0013 - 60000 - 7ffff
0014 - 80000 - 9ffff
0015 - a0000 - bffff
0016 - c0000 - dffff
0017 - e0000 - fffff

values 00-0f select a rom bank
      00 - selects last 20000h region of rom
      01 - 20000h region before last
      02 - etc

values 10-1f select a ram bank

on reset 0017 is set to 0, pointing to last 20000h bytes of ROM containing the boot setup code


0016 - unknown
       0x01 written during boot sequence

0017 - unknown
       0x00 written during boot sequence

0020 - unknown
       0x00 written during boot sequence

0030 - unknown
       Looking at code at C0769 bit 5 this seems to be used
       as some kind of clock for data that was written to
       I/O port 0040h.

0040 - unknown
       0xff written during boot sequence

Sounds related?
On boot: 50 = 98, 51 = 06, 52 = 7f
         52 = ff
         50 - 26, 51 = 01, 52 = 7f
         52 = ff
         (mem check)
         50 = 98, 51 = 06, 52 = 7f
         52 = ff
         50 = 74, 51 = 04, 52 = 7f
         52 = ff
         50 = 98, 51 = 06, 52 = 7f
         52 = ff
         (menu)
         (type 1 - medium frequency sound )
         50 = 5d, 51 = 01, 52 = 7f
         52 = ff
         52 = ff
         52 = ff
         50 = 00, 51 = 01, 52 = 7f
         52 = ff
         (type 2 - simple lower sound)
         50 = ba, 51 = 02, 52 = 7f
         52 = ff
         (type 3 - highest frequency sound)
         50 = 26, 51 = 01, 52 = 7f
         52 = ff
         50 = 06, 51 = 01, 52 = 7f
         52 = ff
         50 = e9, 51 = 00, 52 = 7f
         52 = ff
         50 = dc, 51 = 00, 52 = 7f
         52 = ff
         50 = c4, 51 = 00, 52 = 7f
         52 = ff
0050 - counter low?
0051 - counter high?
0052 - counter enable/disable?

0060 - Irq enable/disable (?)
       0xff written at start of boot sequence
       0x7e written just before enabling interrupts

0061 - unknown
       0xFE written in irq 0xFB handler

0070 - unknown 0x01 is written when going to terminal mode (enable rs232 receive?)

0090 - Interrupt source clear
       b7 - 1 = clear interrupt source for irq vector 0xf8
       b6 - 1 = clear interrupt source for irq vector 0xf9
       b5 - 1 = clear interrupt source for irq vector 0xfa
       b4 - 1 = clear interrupt source for irq vector 0xfb
       b3 - 1 = clear interrupt source for irq vector 0xfc
       b2 - 1 = clear interrupt source for irq vector 0xfd
       b1 - 1 = clear interrupt source for irq vector 0xfe
       b0 - 1 = clear interrupt source for irq vector 0xff

00A0 - unknown
       Read during initial boot sequence, expects to have bit 3 set at least once during the boot sequence

00D0 - 00DC - Keyboard??

00DD - unknown
       0xf8 written during boot sequence

00DE - unknown
       0xf0 written during boot sequence


IRQ 0xF8:
The handler clears the irq active bit and copies a word from 6D79 to 6D85 and
from 6D7B to 6D87 then enters an endless loop (with interrupts disabled).
Perhaps power on/off related??


IRQ 0xF9: (T400: C049A)
Purpose unknown. IRQ handler clear bit 0 of 6DA9.


IRQ 0xFA: (T400: C04AE)
Purpose unknown. Expects 6D4F to be set up properly. Enables irq 0xFD. Reads
from input port 0xB0 and resets and sets bit 0 of output port 0x61.


IRQ 0xFB: (T400: C04D1)
Purpose unknown. Reads from input port 0xB0, sets bit 7 of 6D28 when
non-zero data was read.


IRQ 0xFC: (T400: C0550)
Purpose unknown. Reads from input port 0xC1 and 0xC0 and possibly outputs
something to output port 0xC1 depending on data read from 0xC1.


IRQ 0xFD: (T400: C0724)
Purpose unknown. Clears bit 3 of 70A5.


IRQ 0xFE: (T400: C0738)
Purpose unknown. Expects 6D4F to be set up properly. Enables irq 0xf9 or outputs
a byte to ports 0x40 and 0x30. No endless loop.


IRQ 0xFF:
This does some simple processing and then enters an endless loop (with interrupts
disabled). Perhaps power on/off related??

TODO:
- On boot, systems do not display version and copyright messages.
- Add PCMCIA slot

******************************************************************************/

#include "emu.h"

#include "cpu/nec/nec.h"
#include "machine/rp5c01.h"
#include "machine/timer.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

#define LOG     0
#define X301    19660000


class nakajies_state : public driver_device
{
public:
	nakajies_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "v20hl")
		, m_rtc(*this, "rtc")
		, m_view0(*this, "view0")
		, m_view1(*this, "view1")
		, m_view2(*this, "view2")
		, m_view3(*this, "view3")
		, m_view4(*this, "view4")
		, m_view5(*this, "view5")
		, m_view6(*this, "view6")
		, m_view7(*this, "view7")
		, m_port_row(*this, "ROW%u", 0U)
		, m_port_debug(*this, "debug")
		, m_rombank(*this, "rombank%u", 0U)
		, m_rambank(*this, "rambank%u", 0U)
		, m_rom_region(*this, "bios")
	{
	}

	void nakajies210(machine_config &config);
	void nakajies220(machine_config &config);
	void nakajies220_256(machine_config &config);
	void nakajies250(machine_config &config);
	void dator3k(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(trigger_irq);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void nakajies_update_irqs();
	u8 irq_clear_r();
	void irq_clear_w(u8 data);
	u8 irq_enable_r();
	void irq_enable_w(u8 data);
	u8 unk_a0_r();
	void lcd_memory_start_w(u8 data);
	u8 keyboard_r();
	void banking_w(offs_t offset, u8 data);

	void nakajies_palette(palette_device &palette) const;
	TIMER_DEVICE_CALLBACK_MEMBER(kb_timer);
	void nakajies_io_map(address_map &map) ATTR_COLD;
	void nakajies_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<rp5c01_device> m_rtc;
	memory_view m_view0;
	memory_view m_view1;
	memory_view m_view2;
	memory_view m_view3;
	memory_view m_view4;
	memory_view m_view5;
	memory_view m_view6;
	memory_view m_view7;
	required_ioport_array<10> m_port_row;
	required_ioport m_port_debug;
	memory_bank_array_creator<8> m_rombank;
	memory_bank_array_creator<8> m_rambank;
	required_memory_region m_rom_region;

	u8 m_irq_enabled = 0;
	u8 m_irq_active = 0;
	u8 m_lcd_memory_start = 0;
	u8 m_matrix = 0;
	std::unique_ptr<u8[]> m_ram_base;
	u32 m_ram_size = 0;
};


void nakajies_state::nakajies_map(address_map &map)
{
	map(0x00000, 0x1ffff).view(m_view0);
	m_view0[0](0x00000, 0x1ffff).bankr(m_rombank[0]);
	m_view0[1](0x00000, 0x1ffff).bankrw(m_rambank[0]);
	map(0x20000, 0x3ffff).view(m_view1);
	m_view1[0](0x20000, 0x3ffff).bankr(m_rombank[1]);
	m_view1[1](0x20000, 0x3ffff).bankrw(m_rambank[1]);
	map(0x40000, 0x5ffff).view(m_view2);
	m_view2[0](0x40000, 0x5ffff).bankr(m_rombank[2]);
	m_view2[1](0x40000, 0x5ffff).bankrw(m_rambank[2]);
	map(0x60000, 0x7ffff).view(m_view3);
	m_view3[0](0x60000, 0x7ffff).bankr(m_rombank[3]);
	m_view3[1](0x60000, 0x7ffff).bankrw(m_rambank[3]);
	map(0x80000, 0x9ffff).view(m_view4);
	m_view4[0](0x80000, 0x9ffff).bankr(m_rombank[4]);
	m_view4[1](0x80000, 0x9ffff).bankrw(m_rambank[4]);
	map(0xa0000, 0xbffff).view(m_view5);
	m_view5[0](0xa0000, 0xbffff).bankr(m_rombank[5]);
	m_view5[1](0xa0000, 0xbffff).bankrw(m_rambank[5]);
	map(0xc0000, 0xdffff).view(m_view6);
	m_view6[0](0xc0000, 0xdffff).bankr(m_rombank[6]);
	m_view6[1](0xc0000, 0xdffff).bankrw(m_rambank[6]);
	map(0xe0000, 0xfffff).view(m_view7);
	m_view7[0](0xe0000, 0xfffff).bankr(m_rombank[7]);
	m_view7[1](0xe0000, 0xfffff).bankrw(m_rambank[7]);
}


void nakajies_state::nakajies_update_irqs()
{
	// Hack: IRQ mask is temporarily disabled because it doesn't allow the IRQ vectors 0xFA
	// and 0xFB that are used for scanning the kb, this need further investigation.
	uint8_t irq = m_irq_active; // & m_irq_enabled;
	uint8_t vector = 0xff;

	if (LOG)
		logerror("nakajies_update_irqs: irq_enabled = %02x, irq_active = %02x\n", m_irq_enabled, m_irq_active );

	// Assuming irq 0xFF has the highest priority and 0xF8 the lowest
	while (vector >= 0xf8 && !(irq & 0x01))
	{
		irq >>= 1;
		vector -= 1;
	}

	if (vector >= 0xf8)
	{
		m_maincpu->set_input_line_and_vector(0, ASSERT_LINE, vector); // V20
	}
	else
	{
		m_maincpu->set_input_line(0, CLEAR_LINE);
	}
}


u8 nakajies_state::irq_clear_r()
{
	return 0x00;
}


void nakajies_state::irq_clear_w(u8 data)
{
	m_irq_active &= ~data;
	nakajies_update_irqs();
}


u8 nakajies_state::irq_enable_r()
{
	return m_irq_enabled;
}


void nakajies_state::irq_enable_w(u8 data)
{
	m_irq_enabled = data;
	nakajies_update_irqs();
}


/*
  I/O Port a0:
  bit 7-4 - unknown
  bit 3   - battery low (when set)
  bit 2-0 - unknown
*/
u8 nakajies_state::unk_a0_r()
{
	return 0xf7;
}

void nakajies_state::lcd_memory_start_w(u8 data)
{
	m_lcd_memory_start = data;
}


void nakajies_state::banking_w(offs_t offset, u8 data)
{
	m_rombank[offset]->set_entry((data & 0x0f) ^ 0xf);
	m_rambank[offset]->set_entry((data & 0x0f) ^ 0xf);

	switch (offset)
	{
	case 0: m_view0.select(BIT(data, 4)); break;
	case 1: m_view1.select(BIT(data, 4)); break;
	case 2: m_view2.select(BIT(data, 4)); break;
	case 3: m_view3.select(BIT(data, 4)); break;
	case 4: m_view4.select(BIT(data, 4)); break;
	case 5: m_view5.select(BIT(data, 4)); break;
	case 6: m_view6.select(BIT(data, 4)); break;
	case 7: m_view7.select(BIT(data, 4)); break;
	}
}


u8 nakajies_state::keyboard_r()
{
	return (m_matrix > 0x00) ? m_port_row[m_matrix - 1]->read() : 0;
}


void nakajies_state::nakajies_io_map(address_map &map)
{
	map(0x0000, 0x0000).w(FUNC(nakajies_state::lcd_memory_start_w));
	map(0x0010, 0x0017).w(FUNC(nakajies_state::banking_w));
	map(0x0060, 0x0060).rw(FUNC(nakajies_state::irq_enable_r), FUNC(nakajies_state::irq_enable_w));
	map(0x0090, 0x0090).rw(FUNC(nakajies_state::irq_clear_r), FUNC(nakajies_state::irq_clear_w));
	map(0x00a0, 0x00a0).r(FUNC(nakajies_state::unk_a0_r));
	map(0x00b0, 0x00b0).r(FUNC(nakajies_state::keyboard_r));
	map(0x00d0, 0x00df).rw(m_rtc, FUNC(rp5c01_device::read), FUNC(rp5c01_device::write));
}


INPUT_CHANGED_MEMBER(nakajies_state::trigger_irq)
{
	m_irq_active |= m_port_debug->read();
	nakajies_update_irqs();
}


static INPUT_PORTS_START(nakajies)
	PORT_START("debug")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_NAME("irq 0xff") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(nakajies_state::trigger_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_NAME("irq 0xfe") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(nakajies_state::trigger_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_NAME("irq 0xfd") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(nakajies_state::trigger_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_NAME("irq 0xfc") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(nakajies_state::trigger_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_NAME("irq 0xfb") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(nakajies_state::trigger_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_NAME("irq 0xfa") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(nakajies_state::trigger_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_NAME("irq 0xf9") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(nakajies_state::trigger_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_NAME("irq 0xf8") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(nakajies_state::trigger_irq), 0)

	PORT_START("ROW0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Shift")  PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LEFT")        PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENTER")       PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("ROW1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ALT")         PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("`")           PORT_CODE(KEYCODE_BACKSLASH2)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CAN")         PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SPACE")       PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5")           PORT_CODE(KEYCODE_5)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("ROW2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CONTROL")     PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CAPSLOCK")    PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1")           PORT_CODE(KEYCODE_1)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TAB")         PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("ROW3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3")           PORT_CODE(KEYCODE_3)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2")           PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q")           PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W")           PORT_CODE(KEYCODE_W)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E")           PORT_CODE(KEYCODE_E)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S")           PORT_CODE(KEYCODE_S)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D")           PORT_CODE(KEYCODE_D)

	PORT_START("ROW4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4")           PORT_CODE(KEYCODE_4)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z")           PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X")           PORT_CODE(KEYCODE_X)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A")           PORT_CODE(KEYCODE_A)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R")           PORT_CODE(KEYCODE_R)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F")           PORT_CODE(KEYCODE_F)

	PORT_START("ROW5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B")           PORT_CODE(KEYCODE_B)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V")           PORT_CODE(KEYCODE_V)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T")           PORT_CODE(KEYCODE_T)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y")           PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G")           PORT_CODE(KEYCODE_G)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C")           PORT_CODE(KEYCODE_C)

	PORT_START("ROW6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6")           PORT_CODE(KEYCODE_6)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DOWN")        PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("INSERT")      PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RIGHT")       PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("\\")          PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/")           PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H")           PORT_CODE(KEYCODE_H)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N")           PORT_CODE(KEYCODE_N)

	PORT_START("ROW7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("=")           PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7")           PORT_CODE(KEYCODE_7)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ORGN")        PORT_CODE(KEYCODE_PGUP)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UP")          PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("WP")          PORT_CODE(KEYCODE_PGDN)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U")           PORT_CODE(KEYCODE_U)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M")           PORT_CODE(KEYCODE_M)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K")           PORT_CODE(KEYCODE_K)

	PORT_START("ROW8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8")           PORT_CODE(KEYCODE_8)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("-")           PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("]")           PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("[")           PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("\'")          PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I")           PORT_CODE(KEYCODE_I)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J")           PORT_CODE(KEYCODE_J)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(",")           PORT_CODE(KEYCODE_COMMA)

	PORT_START("ROW9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0")           PORT_CODE(KEYCODE_0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9")           PORT_CODE(KEYCODE_9)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BACK")        PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P")           PORT_CODE(KEYCODE_P)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(";")           PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L")           PORT_CODE(KEYCODE_L)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O")           PORT_CODE(KEYCODE_O)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(".")           PORT_CODE(KEYCODE_STOP)
INPUT_PORTS_END


void nakajies_state::machine_start()
{
	u32 rom_size = m_rom_region->bytes();

	m_ram_base = make_unique_clear<uint8_t[]>(m_ram_size);

	for (int i = 0; i < 8; i++)
	{
		// TODO: rom banks outside max bank size; assuming the banks are simply mirrored
		for (int j = 0; j < 16; j += rom_size / 0x20000)
			m_rombank[i]->configure_entries(j, rom_size / 0x20000, m_rom_region->base(), 0x20000);
		// TODO: ram banks outside max bank size; assuming the banks are simply mirrored
		// how would that work for a system with 160KB RAM?
		for (int j = 0; j < 16; j += m_ram_size / 0x20000)
			m_rambank[i]->configure_entries(j, m_ram_size / 0x20000, &m_ram_base[0], 0x20000);
	}

	save_item(NAME(m_irq_enabled));
	save_item(NAME(m_irq_active));
	save_item(NAME(m_lcd_memory_start));
	save_item(NAME(m_matrix));
}


void nakajies_state::machine_reset()
{
	m_irq_enabled = 0;
	m_irq_active = 0;
	m_lcd_memory_start = 0;
	m_matrix = 0;

	m_view0.select(0);
	m_view1.select(0);
	m_view2.select(0);
	m_view3.select(0);
	m_view4.select(0);
	m_view5.select(0);
	m_view6.select(0);
	m_view7.select(0);
	for (int i = 0; i < 8; i++)
	{
		m_rombank[i]->set_entry(0x0f);
		m_rambank[i]->set_entry(0x0f);
	}
}

u32 nakajies_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint8_t* lcd_memory_start = &m_ram_base[m_lcd_memory_start << 9];
	int height = screen.height();

	for (int y = 0; y < height; y++)
		for (int x = 0; x < 60; x++)
		{
			u8 data = lcd_memory_start[y*64 + x];

			for (int px = 0; px < 8; px++)
			{
				bitmap.pix(y, (x * 8) + px) = BIT(data, 7);
				data <<= 1;
			}
		}

	return 0;
}


TIMER_DEVICE_CALLBACK_MEMBER(nakajies_state::kb_timer)
{
	if (m_matrix > 0x09)
	{
		// reset the keyboard scan
		m_matrix = 0;
		m_irq_active |= 0x20;
	}
	else
	{
		// next row
		m_matrix++;
		m_irq_active |= 0x10;
	}

	nakajies_update_irqs();
}


void nakajies_state::nakajies_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}


/* F4 Character Displayer */
static const gfx_layout nakajies_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	2331,                   /* 2331 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START(gfx_wales210)
	GFXDECODE_ENTRY("bios", 0x55043, nakajies_charlayout, 0, 1)
GFXDECODE_END

static GFXDECODE_START(gfx_dator3k)
	GFXDECODE_ENTRY("bios", 0x54fb1, nakajies_charlayout, 0, 1)
GFXDECODE_END

static GFXDECODE_START(gfx_drwrt200)
	GFXDECODE_ENTRY("bios", 0xdbbeb, nakajies_charlayout, 0, 1)
GFXDECODE_END

static GFXDECODE_START(gfx_drwrt400)
	GFXDECODE_ENTRY("bios", 0x580b6, nakajies_charlayout, 0, 1)
GFXDECODE_END

void nakajies_state::nakajies210(machine_config &config)
{
	V20(config, m_maincpu, X301 / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &nakajies_state::nakajies_map);
	m_maincpu->set_addrmap(AS_IO, &nakajies_state::nakajies_io_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);  // Wild guess
	screen.set_screen_update(FUNC(nakajies_state::screen_update));
	screen.set_size(80 * 6, 8 * 8);
	screen.set_visarea(0, 6 * 80 - 1, 0, 8 * 8 - 1);
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_wales210);
	PALETTE(config, "palette", FUNC(nakajies_state::nakajies_palette), 2);

	/* sound */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 1.00);

	/* rtc */
	RP5C01(config, m_rtc, XTAL(32'768));

	TIMER(config, "kb_timer").configure_periodic(FUNC(nakajies_state::kb_timer), attotime::from_hz(250));

	m_ram_size = 128 * 1024;
}

void nakajies_state::dator3k(machine_config &config)
{
	nakajies210(config);
	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_dator3k);
}

void nakajies_state::nakajies220(machine_config &config)
{
	nakajies210(config);
	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_drwrt400);
}

void nakajies_state::nakajies220_256(machine_config &config)
{
	nakajies220(config);
	m_ram_size = 256 * 1024;
}

void nakajies_state::nakajies250(machine_config &config)
{
	nakajies210(config);
	subdevice<screen_device>("screen")->set_size(80 * 6, 16 * 8);
	subdevice<screen_device>("screen")->set_visarea(0, 6 * 80 - 1, 0, 16 * 8 - 1);
	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_drwrt200);
	m_ram_size = 256 * 1024;
}


ROM_START(wales210)
	ROM_REGION(0x80000, "bios", 0)

	ROM_SYSTEM_BIOS(0, "wales210", "Walther ES-210")
	ROMX_LOAD("wales210.ic303", 0x00000, 0x80000, CRC(a8e8d991) SHA1(9a133b37b2fbf689ae1c7ab5c7f4e97cd33fd596), ROM_BIOS(0))        /* 27c4001 */

	ROM_SYSTEM_BIOS(1, "drwrtr325_102", "NTS DreamWriter 325 (v1.02)")
	ROMX_LOAD("dr3_1_02uk.ic303", 0x00000, 0x80000, CRC(027db9fe) SHA1(eb52a30510f2e2924c6dae9bc4348cd3572f4997), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "drwrtr325_103", "NTS DreamWriter 325 (v1.03)")
	ROMX_LOAD("dr3_1_03.ic303", 0x00000, 0x80000, CRC(21fd074e) SHA1(52cd07527f3ba96b0ce37578996af502db144e80), ROM_BIOS(2)) // label actually DR3 (1.03)

	ROM_SYSTEM_BIOS(3, "drwrtr325_20", "NTS DreamWriter 325 (v2.0)")
	ROMX_LOAD("nts_325_basic.ic303", 0x00000, 0x80000, CRC(feb40854) SHA1(8838dcbcd7f6be282bb8ed1d7f97187b45f00a58), ROM_BIOS(3))
ROM_END


ROM_START(dator3k)
	ROM_REGION(0x80000, "bios", 0)
	ROM_LOAD("dator3000.ic303", 0x00000, 0x80000, CRC(b67fffeb) SHA1(e48270d15bef9453bcb6ba8aa949fd2ab3feceed))
ROM_END


ROM_START(drwrt100)
	ROM_REGION(0x80000, "bios", 0)
	ROM_LOAD("t100_2.3.ic303", 0x00000, 0x80000, CRC(8a16f12f) SHA1(0a907186db3d1756566d767ee847a7ecf694e74b))      /* Checksum 01F5 on label */
ROM_END


ROM_START(drwrt200)
	ROM_REGION(0x100000, "bios", 0)
	ROM_LOAD("drwrt200.bin", 0x000000, 0x100000, CRC(3c39483c) SHA1(48293e6bdb7e7322d76da7174b716243c0ab7c2c))
ROM_END


ROM_START(drwrt400)
	ROM_REGION(0x100000, "bios", 0)

	ROM_SYSTEM_BIOS(0, "v3_1", "v3.1")
	ROMX_LOAD("t4_ir_3.1_e588.ic303", 0x00000, 0x100000, CRC(1724ceb2) SHA1(101effc8cfa2f084c09b25c4074f684aeb97d044), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "v2_1", "v2.1")
	ROMX_LOAD("t4_ir_2.1.ic303", 0x80000, 0x80000, CRC(f0f45fd2) SHA1(3b4d5722b3e32e202551a1be8ae36f34ad705ddd), ROM_BIOS(1))
ROM_END


ROM_START(drwrt450)
	ROM_REGION(0x100000, "bios", 0)
	ROM_LOAD("t4_ir_35ba308.ic303", 0x00000, 0x100000, CRC(3b5a580d) SHA1(72df34ece1e6d70adf953025d1c458e22ce819e1))
ROM_END


ROM_START(es210_es)
	ROM_REGION(0x80000, "bios", 0)
	ROM_LOAD("nakajima_es.ic303", 0x00000, 0x80000, CRC(214d73ce) SHA1(ce9967c5b2d122ebebe9401278d8ea374e8fb289))
ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT    COMPAT  MACHINE      INPUT     CLASS           INIT        COMPANY     FULLNAME            FLAGS
COMP( 199?, wales210, 0,        0,      nakajies210, nakajies, nakajies_state, empty_init, "Walther",  "ES-210",           MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // German, 128KB RAM
COMP( 199?, dator3k,  wales210, 0,      dator3k,     nakajies, nakajies_state, empty_init, "Dator",    "Dator 3000",       MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // Spanish, 128KB RAM
COMP( 199?, es210_es, wales210, 0,      nakajies210, nakajies, nakajies_state, empty_init, "Nakajima", "ES-210 (Spain)",   MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // Spanish, 128KB RAM
COMP( 199?, drwrt100, wales210, 0,      nakajies220, nakajies, nakajies_state, empty_init, "NTS",      "DreamWriter T100", MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // English, 128KB RAM
COMP( 1996, drwrt400, wales210, 0,      nakajies220_256, nakajies, nakajies_state, empty_init, "NTS",      "DreamWriter T400", MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // English, 256KB RAM, also found a machine with 160KB RAM
COMP( 199?, drwrt450, wales210, 0,      nakajies220, nakajies, nakajies_state, empty_init, "NTS",      "DreamWriter 450",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // English, 128KB RAM
COMP( 199?, drwrt200, wales210, 0,      nakajies250, nakajies, nakajies_state, empty_init, "NTS",      "DreamWriter T200", MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // English, 256KB? RAM



nanos.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

Nanos

2009-05-12 Skeleton driver.

Status:
- Waiting for a floppy disk. Need software to proceed.

****************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/z80daisy.h"
#include "machine/timer.h"
#include "machine/upd765.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "machine/z80pio.h"

#include "formats/nanos_dsk.h"

#include "emupal.h"
#include "screen.h"


namespace {

class nanos_state : public driver_device
{
public:
	nanos_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_bank1(*this, "bank1")
		, m_pio(*this, "pio")
		, m_pio0(*this, "pio0")
		, m_pio1(*this, "pio1")
		, m_sio0(*this, "sio0")
		, m_sio1(*this, "sio1")
		, m_ctc0(*this, "ctc0")
		, m_ctc1(*this, "ctc1")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:0")
		, m_key_t(*this, "keyboard_timer")
		, m_p_chargen(*this, "chargen")
		, m_vram(*this, "videoram")
		, m_io_keyboard(*this, "LINE%u", 0U)
		, m_io_linec(*this, "LINEC")
	{ }

	void nanos(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	void tc_w(uint8_t data);
	void ctc_z0_w(int state);
	void ctc_z1_w(int state);
	void ctc_z2_w(int state);
	TIMER_DEVICE_CALLBACK_MEMBER(keyboard_callback);
	void z80daisy_interrupt(int state);
	uint8_t port_a_r();
	uint8_t port_b_r();
	void port_b_w(uint8_t data);
	static void floppy_formats(format_registration &fr);
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	uint8_t m_key_command;
	uint8_t m_last_code;
	uint8_t m_key_pressed;
	uint8_t row_number(uint8_t code);

	required_device<z80_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_memory_bank    m_bank1;
	required_device<z80pio_device> m_pio;
	required_device<z80pio_device> m_pio0;
	required_device<z80pio_device> m_pio1;
	required_device<z80sio_device> m_sio0;
	required_device<z80sio_device> m_sio1;
	required_device<z80ctc_device> m_ctc0;
	required_device<z80ctc_device> m_ctc1;
	required_device<upd765a_device> m_fdc;
	required_device<floppy_connector> m_floppy;
	required_device<timer_device> m_key_t;
	required_region_ptr<u8> m_p_chargen;
	required_shared_ptr<u8> m_vram;
	required_ioport_array<7> m_io_keyboard;
	required_ioport m_io_linec;
};



void nanos_state::mem_map(address_map &map)
{
	map(0x0000, 0xf7ff).ram().share("mainram");
	map(0x0000, 0x0fff).bankr(m_bank1);
	map(0xf800, 0xffff).ram().share("videoram");
}

void nanos_state::tc_w(uint8_t data)
{
	m_fdc->tc_w(BIT(data,1));
}


/* Z80-CTC Interface */

void nanos_state::ctc_z0_w(int state)
{
}

void nanos_state::ctc_z1_w(int state)
{
}

void nanos_state::ctc_z2_w(int state)
{
}

/* Z80-SIO Interface */

void nanos_state::z80daisy_interrupt(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, state);
}

/* Z80 Daisy Chain */

static const z80_daisy_config daisy_chain[] =
{
	{ "pio" },
	{ "pio0" },
	{ "pio1" },
	{ "sio0" },
	{ "ctc0" },
	{ "sio1" },
	{ "ctc1" },
	{ nullptr }
};

void nanos_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	/* CPU card */
	map(0x00, 0x03).rw(m_pio, FUNC(z80pio_device::read), FUNC(z80pio_device::write));

	/* I/O card */
	map(0x80, 0x83).rw(m_pio0, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x84, 0x87).rw(m_sio0, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x88, 0x8B).rw(m_pio1, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x8C, 0x8F).rw(m_ctc0, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));

	/* FDC card */
	map(0x92, 0x92).w(FUNC(nanos_state::tc_w));
	map(0x94, 0x95).m(m_fdc, FUNC(upd765a_device::map));
	/* V24+IFSS card */
	map(0xA0, 0xA3).rw(m_sio0, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0xA4, 0xA7).rw(m_ctc1, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));

	/* 256-k RAM card I  -  64k OS-Memory + 192k-RAM-Floppy */
	//map(0xC0, 0xC7)

	/* 256-k RAM card II -  64k OS-Memory + 192k-RAM-Floppy */
	//map(0xC8, 0xCF)
}

/* Input ports */
static INPUT_PORTS_START( nanos )
	PORT_START("LINEC")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH)

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_END)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)

	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)

	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("]")  PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("~")  PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DEL")PORT_CODE(KEYCODE_BACKSPACE)

	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LF") PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER)
INPUT_PORTS_END


uint32_t nanos_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
//  static uint8_t framecnt=0;
	uint16_t sy=0,ma=0;

//  framecnt++;

	for (uint8_t y = 0; y < 25; y++)
	{
		for (uint8_t ra = 0; ra < 10; ra++)
		{
			uint16_t *p = &bitmap.pix(sy++);

			for (uint16_t x = ma; x < ma + 80; x++)
			{
				uint8_t gfx;
				if (ra < 8)
				{
					uint8_t const chr = m_vram[x];

					/* get pattern of pixels for that character scanline */
					gfx = m_p_chargen[(chr<<3) | ra ];
				}
				else
					gfx = 0;

				/* Display a scanline of a character (8 pixels) */
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma+=80;
	}
	return 0;
}

uint8_t nanos_state::port_a_r()
{
	if (m_key_command==0)
		return m_key_pressed;
	else
	{
		uint8_t retVal = m_last_code;
		m_last_code = 0;
		return retVal;
	}
}

uint8_t nanos_state::port_b_r()
{
	return 0xff;
}


void nanos_state::port_b_w(uint8_t data)
{
	m_key_command = BIT(data,1);

	m_bank1->set_entry(BIT(data,7));
}

uint8_t nanos_state::row_number(uint8_t code)
{
	if (BIT(code, 0)) return 0;
	if (BIT(code, 1)) return 1;
	if (BIT(code, 2)) return 2;
	if (BIT(code, 3)) return 3;
	if (BIT(code, 4)) return 4;
	if (BIT(code, 5)) return 5;
	if (BIT(code, 6)) return 6;
	if (BIT(code, 7)) return 7;
	return 0;
}

// TODO: clean this up when the machine starts to work.
TIMER_DEVICE_CALLBACK_MEMBER(nanos_state::keyboard_callback)
{
	uint8_t key_code = 0;
	bool shift = BIT(m_io_linec->read(), 1);
	bool ctrl =  BIT(m_io_linec->read(), 0);
	m_key_pressed = 0xff;
	for(int i = 0; i < 7; i++)
	{
		uint8_t code = m_io_keyboard[i]->read();
		if (code != 0)
		{
			if (i==0 && !shift)
				key_code = 0x30 + row_number(code) + 8*i; // for numbers and some signs

			if (i==0 && shift)
				key_code = 0x20 + row_number(code) + 8*i; // for shifted numbers

			if (i==1 && !shift)
			{
				if (row_number(code) < 4)
					key_code = 0x30 + row_number(code) + 8*i; // for numbers and some signs
				else
					key_code = 0x20 + row_number(code) + 8*i; // for numbers and some signs
			}

			if (i==1 && shift)
			{
				if (row_number(code) < 4)
					key_code = 0x20 + row_number(code) + 8*i; // for numbers and some signs
				else
					key_code = 0x30 + row_number(code) + 8*i; // for numbers and some signs
			}

			if (i>=2 && i<=4 && shift && !ctrl)
				key_code = 0x60 + row_number(code) + (i-2)*8; // for small letters

			if (i>=2 && i<=4 && !shift && !ctrl)
				key_code = 0x40 + row_number(code) + (i-2)*8; // for big letters

			if (i>=2 && i<=4 && ctrl)
				key_code = 0x00 + row_number(code) + (i-2)*8; // for CTRL + letters

			if (i==5 && shift && !ctrl)
			{
				if (row_number(code)<7)
					key_code = 0x60 + row_number(code) + (i-2)*8; // for small letters
				else
					key_code = 0x40 + row_number(code) + (i-2)*8; // for signs it is switched
			}

			if (i==5 && !shift && !ctrl)
			{
				if (row_number(code)<7)
					key_code = 0x40 + row_number(code) + (i-2)*8; // for small letters
				else
					key_code = 0x60 + row_number(code) + (i-2)*8; // for signs it is switched
			}

			if (i==5 && !shift && ctrl)
				key_code = 0x00 + row_number(code) + (i-2)*8; // for letters + ctrl

			if (i==6)
			{
				switch(row_number(code))
				{
					case 0: key_code = 0x11; break;
					case 1: key_code = 0x12; break;
					case 2: key_code = 0x13; break;
					case 3: key_code = 0x14; break;
					case 4: key_code = 0x20; break; // Space
					case 5: key_code = 0x0D; break; // Enter
					case 6: key_code = 0x09; break; // TAB
					case 7: key_code = 0x0A; break; // LF
				}
			}
			m_last_code = key_code;
		}
	}

	if (key_code==0)
		m_key_pressed = 0xf7;
}

void nanos_state::machine_start()
{
	save_item(NAME(m_key_command));
	save_item(NAME(m_last_code));
	save_item(NAME(m_key_pressed));

	m_bank1->configure_entry(0, m_ram);
	m_bank1->configure_entry(1, m_rom);
}

void nanos_state::machine_reset()
{
	m_bank1->set_entry(1);
	m_floppy->get_device()->mon_w(0);
	m_key_pressed = 0xff;
}

void nanos_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_NANOS_FORMAT);
}

static void nanos_floppies(device_slot_interface &device)
{
	device.option_add("525hd", FLOPPY_525_HD);
}

/* F4 Character Displayer */
static const gfx_layout nanos_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_nanos )
	GFXDECODE_ENTRY( "chargen", 0x0000, nanos_charlayout, 0, 1 )
GFXDECODE_END

void nanos_state::nanos(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &nanos_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &nanos_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(nanos_state::screen_update));
	screen.set_size(80*8, 25*10);
	screen.set_visarea(0,80*8-1,0,25*10-1);
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_nanos);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* devices */
	Z80CTC(config, m_ctc0, XTAL(4'000'000));
	m_ctc0->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc0->zc_callback<0>().set(FUNC(nanos_state::ctc_z0_w));
	m_ctc0->zc_callback<1>().set(FUNC(nanos_state::ctc_z1_w));
	m_ctc0->zc_callback<2>().set(FUNC(nanos_state::ctc_z2_w));

	Z80CTC(config, m_ctc1, XTAL(4'000'000));
	m_ctc1->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc1->zc_callback<0>().set(FUNC(nanos_state::ctc_z0_w));
	m_ctc1->zc_callback<1>().set(FUNC(nanos_state::ctc_z1_w));
	m_ctc1->zc_callback<2>().set(FUNC(nanos_state::ctc_z2_w));

	Z80PIO(config, m_pio0, XTAL(4'000'000));
	m_pio0->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80PIO(config, m_pio1, XTAL(4'000'000));
	m_pio1->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80SIO(config, m_sio0, XTAL(4'000'000));
	m_sio0->out_int_callback().set(FUNC(nanos_state::z80daisy_interrupt));

	Z80SIO(config, m_sio1, XTAL(4'000'000));
	m_sio1->out_int_callback().set(FUNC(nanos_state::z80daisy_interrupt));

	Z80PIO(config, m_pio, XTAL(4'000'000));
	m_pio->in_pa_callback().set(FUNC(nanos_state::port_a_r));
	m_pio->in_pb_callback().set(FUNC(nanos_state::port_b_r));
	m_pio->out_pb_callback().set(FUNC(nanos_state::port_b_w));

	/* UPD765 */
	UPD765A(config, m_fdc, 8'000'000, false, true);
	FLOPPY_CONNECTOR(config, m_floppy, nanos_floppies, "525hd", nanos_state::floppy_formats);

	TIMER(config, "keyboard_timer").configure_periodic(FUNC(nanos_state::keyboard_callback), attotime::from_hz(240));
}

/* ROM definition */
ROM_START( nanos )
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "k7634_1.rom", 0x0000, 0x0800, CRC(8e34e6ac) SHA1(fd342f6effe991823c2a310737fbfcba213c4fe3))
	ROM_LOAD( "k7634_2.rom", 0x0800, 0x0180, CRC(4e01b02b) SHA1(8a279da886555c7470a1afcbb3a99693ea13c237))

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "zg_nanos.rom", 0x0000, 0x0800, CRC(5682d3f9) SHA1(5b738972c815757821c050ee38b002654f8da163))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY                                                FULLNAME  FLAGS */
COMP( 1985, nanos, 0,      0,      nanos,   nanos, nanos_state, empty_init, "Ingenieurhochschule fur Seefahrt Warnemunde/Wustrow", "NANOS",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



nascom1.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Dirk Best,Paul Danials
/******************************************************************************************************

Nascom 1/2/3

Single board computer


To Do:
- TTY
- Nascom2 has two dipswitch banks, and a memory control header
- Nascom3 (Gemini), nothing usable found

Cassette (nascom1):
  It outputs a string of pulses at 1953.125Hz to indicate a 1, and blank tape for 0. This means that
  no tape will present a 0 to the UART instead of the expected 1 (idle). Part of the cassette
  schematic is missing, so a few liberties have been taken. The result is you can save a file and
  load it back. Haven't found wav files on the net to test with.

Cassette (nascom2):
  Standard Kansas City format, switchable (with a real switch) between 300 and 1200 baud.

*****************************************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "machine/ay31015.h"
#include "machine/clock.h"
#include "machine/ram.h"
#include "machine/z80pio.h"

#include "machine/timer.h"
#include "speaker.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "bus/nasbus/nasbus.h"

#include "emupal.h"
#include "softlist_dev.h"
#include "screen.h"

#include <tuple>


namespace {

//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class nascom_state : public driver_device
{
public:
	nascom_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_hd6402(*this, "hd6402")
		, m_cass(*this, "cassette")
		, m_screen(*this, "screen")
		, m_gfxdecode(*this, "gfxdecode")
		, m_palette(*this, "palette")
		, m_videoram(*this, "videoram")
		, m_gfx1_region(*this, "gfx1")
		, m_keyboard(*this, "KEY.%u", 0)
	{ }

	void nascom(machine_config &config);

	void init_nascom();

protected:
	required_device<cpu_device> m_maincpu;
	required_device<ram_device> m_ram;

	virtual void machine_reset() override ATTR_COLD;

	uint8_t nascom1_port_00_r();
	void nascom1_port_00_w(uint8_t data);
	uint8_t nascom1_port_01_r();
	void nascom1_port_01_w(uint8_t data);
	uint8_t nascom1_port_02_r();
	int hd6402_si();
	void hd6402_so(int state);

	void screen_update(bitmap_ind16 &bitmap, const rectangle &cliprect, int char_height);

	required_device<ay31015_device> m_hd6402;
	required_device<cassette_image_device> m_cass;
	required_device<screen_device> m_screen;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<palette_device> m_palette;
	required_shared_ptr<uint8_t> m_videoram;
	required_memory_region m_gfx1_region;
	required_ioport_array<8> m_keyboard;

	uint8_t m_kb_select;
	uint8_t m_kb_control;
	bool m_cassinbit, m_cassoutbit, m_cassold;
	u8 m_port00;

	template<int Dest> DECLARE_SNAPSHOT_LOAD_MEMBER( snapshot_cb );
};

class nascom1_state : public nascom_state
{
public:
	nascom1_state(const machine_config &mconfig, device_type type, const char *tag)
		: nascom_state(mconfig, type, tag)
	{ }

	void nascom1(machine_config &config);
	uint32_t screen_update_nascom(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

private:
	void nascom1_io(address_map &map) ATTR_COLD;
	void nascom1_mem(address_map &map) ATTR_COLD;
	TIMER_DEVICE_CALLBACK_MEMBER(nascom1_kansas_r);
	void nascom1_kansas_w(int state);
	u16 m_cass_cnt[2];
};

class nascom2_state : public nascom_state
{
public:
	nascom2_state(const machine_config &mconfig, device_type type, const char *tag)
		: nascom_state(mconfig, type, tag)
		, m_clock(*this, "uart_clock")
		, m_nasbus(*this, "nasbus")
		, m_socket1(*this, "socket1")
		, m_socket2(*this, "socket2")
		, m_lsw1(*this, "lsw1")
	{ }

	void nascom2(machine_config &config);
	void nascom2c(machine_config &config);

	void init_nascom2c();

	DECLARE_INPUT_CHANGED_MEMBER(cass_speed);

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	TIMER_DEVICE_CALLBACK_MEMBER(nascom2_kansas_r);
	void nascom2_kansas_w(int state);
	void ram_disable_w(int state);
	void ram_disable_cpm_w(int state);
	uint32_t screen_update_nascom(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	std::pair<std::error_condition, std::string> load_cart(device_image_interface &image, generic_slot_device *slot, int slot_id);
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(socket1_load) { return load_cart(image, m_socket1, 1); }
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(socket2_load) { return load_cart(image, m_socket2, 2); }

	void nascom2_io(address_map &map) ATTR_COLD;
	void nascom2_mem(address_map &map) ATTR_COLD;
	void nascom2c_mem(address_map &map) ATTR_COLD;

	required_device<clock_device> m_clock;
	required_device<nasbus_device> m_nasbus;
	required_device<generic_slot_device> m_socket1;
	required_device<generic_slot_device> m_socket2;
	required_ioport m_lsw1;
	u8 m_cass_data[4];
	bool m_cass_speed;
};


//**************************************************************************
//  KEYBOARD
//**************************************************************************

uint8_t nascom_state::nascom1_port_00_r()
{
	return m_keyboard[m_kb_select]->read() | ~0x7f;
}

void nascom_state::nascom1_port_00_w(uint8_t data)
{
	u8 bits = data ^ m_port00;

	if (BIT(bits, 4))
		m_cass->change_state(BIT(data, 4) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);

	// d0 falling edge: increment keyboard matrix column select counter
	if (m_kb_control & ~data & 1)
		m_kb_select = (m_kb_select + 1) & 7;

	// d1 falling edge: reset it
	if (m_kb_control & ~data & 2)
		m_kb_select = 0;

	m_kb_control = data & 3;
}


//**************************************************************************
//  CASSETTE
//**************************************************************************

uint8_t nascom_state::nascom1_port_01_r()
{
	return m_hd6402->receive();
}

void nascom_state::nascom1_port_01_w(uint8_t data)
{
	m_hd6402->transmit(data);
}

uint8_t nascom_state::nascom1_port_02_r()
{
	uint8_t data = 0x31; // bits 0,4,5 not used

	m_hd6402->write_swe(0);
	data |= m_hd6402->or_r(  ) ? 0x02 : 0;
	data |= m_hd6402->pe_r(  ) ? 0x04 : 0;
	data |= m_hd6402->fe_r(  ) ? 0x08 : 0;
	data |= m_hd6402->tbmt_r() ? 0x40 : 0;
	data |= m_hd6402->dav_r( ) ? 0x80 : 0;
	m_hd6402->write_swe(1);

	return data;
}

int nascom_state::hd6402_si()
{
	return m_cassinbit;
}

void nascom_state::hd6402_so(int state)
{
	m_cassoutbit = state;
}

void nascom1_state::nascom1_kansas_w(int state)
{
	// incoming 3906.25Hz
	if (state)
	{
		if (m_cassoutbit)
			m_cass->output(BIT(m_cass_cnt[0], 0) ? -1.0 : +1.0); // 1953.125Hz
		else
			m_cass->output(1.0);

		m_cass_cnt[0]++;
	}
	m_hd6402->write_tcp(state);
}

TIMER_DEVICE_CALLBACK_MEMBER( nascom1_state::nascom1_kansas_r )
{
	// cassette - pulses = 1; no pulses = 0
	m_cass_cnt[1]++;
	bool cass_ws = (m_cass->input() > +0.04) ? 1 : 0;

	if (cass_ws != m_cassold)
	{
		m_cassold = cass_ws;
		m_cassinbit = 1;
		m_cass_cnt[1] = 0;
	}
	else
	if (m_cass_cnt[1] > 10)
	{
		m_cass_cnt[1] = 10;
		m_cassinbit = !cass_ws;
	}
}

void nascom2_state::nascom2_kansas_w(int state)
{
	// incoming @19230Hz
	u8 twobit = m_cass_data[3] & 3;

	if (state)
	{
		if (twobit == 0)
			m_cassold = m_cassoutbit;

		if (m_cassold)
			m_cass->output(BIT(m_cass_data[3], 2) ? -1.0 : +1.0); // 2400Hz
		else
			m_cass->output(BIT(m_cass_data[3], 3) ? -1.0 : +1.0); // 1200Hz

		m_cass_data[3]++;
	}

	if (m_cass_speed || !twobit)
	{
		m_hd6402->write_tcp(state);
		m_hd6402->write_rcp(state);
	}
}

TIMER_DEVICE_CALLBACK_MEMBER( nascom2_state::nascom2_kansas_r )
{
	/* cassette - turn 1200/2400Hz to a bit */
	m_cass_data[1]++;
	uint8_t cass_ws = (m_cass->input() > +0.04) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		m_cassinbit = (m_cass_data[1] < 12) ? 1 : 0;
		m_cass_data[1] = 0;
	}
}


//**************************************************************************
//  SNAPSHOTS
//**************************************************************************

template<int Dest>
SNAPSHOT_LOAD_MEMBER(nascom_state::snapshot_cb)
{
	util::random_read &file = image.image_core_file();
	std::error_condition err;

	while (true)
	{
		size_t actual;

		uint8_t line[29];
		std::tie(err, actual) = read(file, &line, sizeof(line));
		if (err || (sizeof(line) != actual))
			break;

		unsigned int addr, b[8];
		if (sscanf((char *)line, "%4x %x %x %x %x %x %x %x %x",
			&addr, &b[0], &b[1], &b[2], &b[3], &b[4], &b[5], &b[6], &b[7]) == 9)
		{
			for (int i = 0; i < 8; i++)
			{
				switch (Dest)
				{
				case 0: // snapshot
					m_maincpu->space(AS_PROGRAM).write_byte(addr++, b[i]);
					break;
				case 1: // character rom
					m_gfx1_region->base()[addr++] = b[i];
					break;
				}
			}
		}
		else
		{
			return std::make_pair(image_error::INVALIDIMAGE, "Unsupported file format");
		}
		int dummy = 0x00;
		do
		{
			std::tie(err, actual) = read(file, &dummy, 1);
			if (err || (actual != 1))
				return std::make_pair(err, std::string());
		}
		while (dummy != 0x0a && dummy != 0x1f);
	}

	return std::make_pair(err, std::string());
}


//**************************************************************************
//  SOCKETS
//**************************************************************************

std::pair<std::error_condition, std::string> nascom2_state::load_cart(
		device_image_interface &image,
		generic_slot_device *slot,
		int slot_id)
{
	if (!image.loaded_through_softlist())
	{
		// loading directly from file
		if (slot->length() > 0x1000)
			return std::make_pair(image_error::INVALIDLENGTH, "Unsupported image file size (must be no more than 4K)");

		slot->rom_alloc(slot->length(), GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);

		auto const [err, actual] = read(slot->image_core_file(), slot->get_rom_base(), slot->length());
		if (err || actual != slot->length())
			return std::make_pair(err ? err : std::errc::io_error, std::string());

		// we just assume that socket1 should be loaded to 0xc000 and socket2 to 0xd000
		switch (slot_id)
		{
		case 1:
			m_maincpu->space(AS_PROGRAM).install_rom(0xc000, 0xc000 + slot->length() - 1, slot->get_rom_base());
			break;
		case 2:
			m_maincpu->space(AS_PROGRAM).install_rom(0xd000, 0xd000 + slot->length() - 1, slot->get_rom_base());
			break;
		}
	}
	else
	{
		// loading from software list. this supports multiple regions to load to
		uint8_t *const region_b000 = image.get_software_region("b000");
		uint8_t *const region_c000 = image.get_software_region("c000");
		uint8_t *const region_d000 = image.get_software_region("d000");

		if (region_b000 != nullptr)
		{
			uint32_t size = image.get_software_region_length("b000");
			m_maincpu->space(AS_PROGRAM).install_rom(0xb000, 0xb000 + size - 1, region_b000);
		}

		if (region_c000 != nullptr)
		{
			uint32_t size = image.get_software_region_length("c000");
			m_maincpu->space(AS_PROGRAM).install_rom(0xc000, 0xc000 + size - 1, region_c000);
		}

		if (region_d000 != nullptr)
		{
			uint32_t size = image.get_software_region_length("d000");
			m_maincpu->space(AS_PROGRAM).install_rom(0xd000, 0xd000 + size - 1, region_d000);
		}
	}

	return std::make_pair(std::error_condition(), std::string());
}


//**************************************************************************
//  DRIVER INIT
//**************************************************************************

void nascom_state::machine_reset()
{
	m_kb_select = 0;
	m_kb_control = 0;
	m_port00 = 0;

	// Set up hd6402 pins
	m_hd6402->write_swe(1);

	m_hd6402->write_cs(0);
	m_hd6402->write_np(1);
	m_hd6402->write_nb1(1);
	m_hd6402->write_nb2(1);
	m_hd6402->write_eps(1);
	m_hd6402->write_tsb(1);
	m_hd6402->write_cs(1);
}

void nascom2_state::machine_reset()
{
	// nascom2: restore speed at machine start
	m_cass_speed = ioport("DSW0")->read();

	// base machine reset
	nascom_state::machine_reset();

	// restart address (on the real system, a12 to a15 are forced to 1 for one memory cycle)
	m_maincpu->set_state_int(Z80_PC, m_lsw1->read() << 12);
}

void nascom_state::init_nascom()
{
	// install extra memory
	if (m_ram->size() > 0)
	{
		m_maincpu->space(AS_PROGRAM).install_ram(0x1000, 0x1000 + m_ram->size() - 1, m_ram->pointer());
	}
}


// since we don't know for which regions we should disable ram, we just let other devices
// overwrite the region they need, and re-install our ram when they are disabled
void nascom2_state::ram_disable_w(int state)
{
	if (state)
	{
		// enable ram again
		m_maincpu->space(AS_PROGRAM).install_ram(0x1000, 0x1000 + m_ram->size() - 1, m_ram->pointer());
	}
}

void nascom2_state::init_nascom2c()
{
	// install memory
	m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x0000 + m_ram->size() - 1, m_ram->pointer());
}

void nascom2_state::ram_disable_cpm_w(int state)
{
	if (state)
	{
		// enable ram again
		m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x0000 + m_ram->size() - 1, m_ram->pointer());
	}
}


//**************************************************************************
//  VIDEO
//**************************************************************************

static const gfx_layout nascom1_charlayout =
{
	8, 16,
	128,
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8,
		8*8, 9*8,10*8,11*8,12*8,13*8,14*8,15*8 },
	8 * 16
};

static GFXDECODE_START( gfx_nascom1 )
	GFXDECODE_ENTRY("gfx1", 0x0000, nascom1_charlayout, 0, 1)
GFXDECODE_END

uint32_t nascom1_state::screen_update_nascom(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	screen_update(bitmap, cliprect, 16);
	return 0;
}

static const gfx_layout nascom2_charlayout =
{
	8, 14,
	256,
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8,  3*8,  4*8,  5*8,  6*8,
		7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8 },
	8 * 16
};

static GFXDECODE_START( gfx_nascom2 )
	GFXDECODE_ENTRY("gfx1", 0x0000, nascom2_charlayout, 0, 1)
GFXDECODE_END

uint32_t nascom2_state::screen_update_nascom(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	screen_update(bitmap, cliprect, 14);
	return 0;
}

void nascom_state::screen_update(bitmap_ind16 &bitmap, const rectangle &cliprect, int char_height)
{
	for (int sx = 0; sx < 48; sx++)
	{
		m_gfxdecode->gfx(0)->opaque(bitmap,cliprect, m_videoram[0x03ca + sx], 1, 0, 0, sx * 8, 0);
	}

	for (int sy = 0; sy < 15; sy++)
	{
		for (int sx = 0; sx < 48; sx++)
		{
			m_gfxdecode->gfx(0)->opaque(bitmap,cliprect, m_videoram[0x000a + (sy * 64) + sx], 1, 0, 0, sx * 8, (sy + 1) * char_height);
		}
	}
}


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void nascom1_state::nascom1_mem(address_map &map)
{
	map(0x0000, 0x07ff).rom(); // MONITOR
	map(0x0800, 0x0bff).ram().share("videoram");
	map(0x0c00, 0x0fff).ram(); // WRAM
}

void nascom1_state::nascom1_io(address_map &map)
{
	map.global_mask(0x0f);
	map(0x00, 0x00).rw(FUNC(nascom1_state::nascom1_port_00_r), FUNC(nascom1_state::nascom1_port_00_w));
	map(0x01, 0x01).rw(FUNC(nascom1_state::nascom1_port_01_r), FUNC(nascom1_state::nascom1_port_01_w));
	map(0x02, 0x02).r(FUNC(nascom1_state::nascom1_port_02_r));
	map(0x04, 0x07).rw("z80pio", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
}

void nascom2_state::nascom2_mem(address_map &map)
{
	map(0x0000, 0x07ff).rom(); // MONITOR
	map(0x0800, 0x0bff).ram().share("videoram");
	map(0x0c00, 0x0fff).ram(); // WRAM
	map(0xe000, 0xffff).rom().region("basic", 0);
}

void nascom2_state::nascom2_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x00).rw(FUNC(nascom2_state::nascom1_port_00_r), FUNC(nascom2_state::nascom1_port_00_w));
	map(0x01, 0x01).rw(FUNC(nascom2_state::nascom1_port_01_r), FUNC(nascom2_state::nascom1_port_01_w));
	map(0x02, 0x02).r(FUNC(nascom2_state::nascom1_port_02_r));
	map(0x04, 0x07).rw("z80pio", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
}

void nascom2_state::nascom2c_mem(address_map &map)
{
	map(0xf000, 0xf7ff).rom().region("maincpu", 0);
	map(0xf800, 0xfbff).ram().share("videoram");
	map(0xfc00, 0xffff).ram(); // WRAM
}


//**************************************************************************
//  INPUT PORTS
//**************************************************************************

static INPUT_PORTS_START( nascom1 )
	PORT_START("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Backspace ClearScreen") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(8)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("New Line")              PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR(0xff) PORT_CHAR('@')  // have to press shift to get @
	PORT_BIT(0x48, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)  PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)  PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)  PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)  PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)  PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)  PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))

	PORT_START("KEY.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)    PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)    PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)    PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)    PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)    PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)    PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))

	PORT_START("KEY.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)    PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)    PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)    PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)    PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)    PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)    PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))

	PORT_START("KEY.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)     PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)     PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)     PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)     PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)     PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

	PORT_START("KEY.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)  PORT_CHAR('.') PORT_CHAR('>') // > nascom2 only
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)     PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)     PORT_CHAR('3') PORT_CHAR(0xA3)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)     PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)     PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)     PORT_CHAR('0') PORT_CHAR('^')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)     PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)     PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)     PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)     PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)     PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)     PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)     PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)     PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( nascom2 )
	PORT_INCLUDE(nascom1)
	PORT_MODIFY("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Back CS")       PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(8)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter  Escape") PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)  PORT_CHAR(27)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)  PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_MODIFY("KEY.6")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                            PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('[') PORT_CHAR('\\')

	PORT_MODIFY("KEY.7")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                            PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(']') PORT_CHAR('_')

	PORT_START("DSW0")
	PORT_DIPNAME(0x01, 0x00, "Cassette Baud Rate") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(nascom2_state::cass_speed), 0)
	PORT_DIPSETTING(0x00, "300")
	PORT_DIPSETTING(0x01, "1200")

	// link switch on board
	PORT_START("lsw1")
	PORT_DIPNAME(0x0f, 0x00, "Restart address")
	PORT_DIPLOCATION("LSW1:1,2,3,4")
	PORT_DIPSETTING(0x00, "0000H")
	PORT_DIPSETTING(0x01, "1000H")
	PORT_DIPSETTING(0x02, "2000H")
	PORT_DIPSETTING(0x03, "3000H")
	PORT_DIPSETTING(0x04, "4000H")
	PORT_DIPSETTING(0x05, "5000H")
	PORT_DIPSETTING(0x06, "6000H")
	PORT_DIPSETTING(0x07, "7000H")
	PORT_DIPSETTING(0x08, "8000H")
	PORT_DIPSETTING(0x09, "9000H")
	PORT_DIPSETTING(0x0a, "A000H")
	PORT_DIPSETTING(0x0b, "B000H")
	PORT_DIPSETTING(0x0c, "C000H")
	PORT_DIPSETTING(0x0d, "D000H")
	PORT_DIPSETTING(0x0e, "E000H")
	PORT_DIPSETTING(0x0f, "F000H")
INPUT_PORTS_END

static INPUT_PORTS_START( nascom2c )
	PORT_INCLUDE(nascom2)

	PORT_MODIFY("lsw1")
	PORT_DIPNAME(0x0f, 0x0f, "Restart address")
	PORT_DIPLOCATION("LSW1:1,2,3,4")
	PORT_DIPSETTING(0x00, "0000H")
	PORT_DIPSETTING(0x01, "1000H")
	PORT_DIPSETTING(0x02, "2000H")
	PORT_DIPSETTING(0x03, "3000H")
	PORT_DIPSETTING(0x04, "4000H")
	PORT_DIPSETTING(0x05, "5000H")
	PORT_DIPSETTING(0x06, "6000H")
	PORT_DIPSETTING(0x07, "7000H")
	PORT_DIPSETTING(0x08, "8000H")
	PORT_DIPSETTING(0x09, "9000H")
	PORT_DIPSETTING(0x0a, "A000H")
	PORT_DIPSETTING(0x0b, "B000H")
	PORT_DIPSETTING(0x0c, "C000H")
	PORT_DIPSETTING(0x0d, "D000H")
	PORT_DIPSETTING(0x0e, "E000H")
	PORT_DIPSETTING(0x0f, "F000H")
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(nascom2_state::cass_speed)
{
	m_cass_speed = newval ? 1 : 0;
}


//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

void nascom_state::nascom(machine_config &config)
{
	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_size(48 * 8, 16 * 16);
	m_screen->set_visarea(0, 48 * 8 - 1, 0, 16 * 16 - 1);
	m_screen->set_screen_update(FUNC(nascom1_state::screen_update_nascom));
	m_screen->set_palette(m_palette);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_nascom1);
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	// pio
	Z80PIO(config, "z80pio", 16_MHz_XTAL / 8);

	// internal extra ram
	RAM(config, m_ram).set_default_size("48K").set_extra_options("8K,16K,32K");

	// uart
	AY31015(config, m_hd6402);
	m_hd6402->read_si_callback().set(FUNC(nascom_state::hd6402_si));
	m_hd6402->write_so_callback().set(FUNC(nascom_state::hd6402_so));
	m_hd6402->set_auto_rdav(true);

	// cassette is connected to the uart
	CASSETTE(config, m_cass);
	m_cass->set_interface("nascom_cass");
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	SPEAKER(config, "mono").front_center();
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	// devices
	snapshot_image_device &snapshot(SNAPSHOT(config, "snapshot", "nas", attotime::from_msec(500)));
	snapshot.set_load_callback(FUNC(nascom_state::snapshot_cb<0>));
	snapshot.set_interface("nascom_snap");
	snapshot_image_device &snapchar(SNAPSHOT(config, "snapchar", "chr", attotime::from_msec(500)));
	snapchar.set_load_callback(FUNC(nascom_state::snapshot_cb<1>));
	snapchar.set_interface("nascom_char");
}

void nascom1_state::nascom1(machine_config &config)
{
	nascom(config);

	Z80(config, m_maincpu, 16_MHz_XTAL / 8);
	m_maincpu->set_addrmap(AS_PROGRAM, &nascom1_state::nascom1_mem);
	m_maincpu->set_addrmap(AS_IO, &nascom1_state::nascom1_io);

	// software
	SOFTWARE_LIST(config, "snap_list").set_original("nascom_snap").set_filter("NASCOM1");

	clock_device &uart_clock(CLOCK(config, "uart_clock", (16_MHz_XTAL / 16) / 256));
	uart_clock.signal_handler().set(FUNC(nascom1_state::nascom1_kansas_w));
	uart_clock.signal_handler().append(m_hd6402, FUNC(ay31015_device::write_rcp));
	TIMER(config, "kansas_r").configure_periodic(FUNC(nascom1_state::nascom1_kansas_r), attotime::from_hz(40000));
}

void nascom2_state::nascom2(machine_config &config)
{
	nascom(config);

	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &nascom2_state::nascom2_mem);
	m_maincpu->set_addrmap(AS_IO, &nascom2_state::nascom2_io);

	// video hardware
	m_screen->set_size(48 * 8, 16 * 14);
	m_screen->set_visarea(0, 48 * 8 - 1, 0, 16 * 14 - 1);
	m_screen->set_screen_update(FUNC(nascom2_state::screen_update_nascom));

	m_gfxdecode->set_info(gfx_nascom2);

	// generic sockets for ram/rom (todo: support ram here)
	GENERIC_SOCKET(config, m_socket1, generic_plain_slot, "nascom_socket", "bin,rom");
	m_socket1->set_device_load(FUNC(nascom2_state::socket1_load));
	GENERIC_SOCKET(config, m_socket2, generic_plain_slot, "nascom_socket", "bin,rom");
	m_socket2->set_device_load(FUNC(nascom2_state::socket2_load));

	// nasbus expansion bus
	NASBUS(config, m_nasbus);
	m_nasbus->ram_disable().set(FUNC(nascom2_state::ram_disable_w));
	m_nasbus->set_program_space(m_maincpu, AS_PROGRAM);
	m_nasbus->set_io_space(m_maincpu, AS_IO);
	NASBUS_SLOT(config, "nasbus1", m_nasbus, nasbus_slot_cards, nullptr);
	NASBUS_SLOT(config, "nasbus2", m_nasbus, nasbus_slot_cards, nullptr);
	NASBUS_SLOT(config, "nasbus3", m_nasbus, nasbus_slot_cards, nullptr);
	NASBUS_SLOT(config, "nasbus4", m_nasbus, nasbus_slot_cards, nullptr);

	// software
	SOFTWARE_LIST(config, "snap_list").set_original("nascom_snap").set_filter("NASCOM2");
	SOFTWARE_LIST(config, "socket_list").set_original("nascom_socket");
	SOFTWARE_LIST(config, "floppy_list").set_original("nascom_flop");

	CLOCK(config, m_clock, (16_MHz_XTAL / 32) / 26);
	m_clock->signal_handler().set(FUNC(nascom2_state::nascom2_kansas_w));
	TIMER(config, "kansas_r").configure_periodic(FUNC(nascom2_state::nascom2_kansas_r), attotime::from_hz(40000));
}

void nascom2_state::nascom2c(machine_config &config)
{
	nascom2(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &nascom2_state::nascom2c_mem);

	m_ram->set_default_size("60K");

	m_nasbus->ram_disable().set(FUNC(nascom2_state::ram_disable_cpm_w));
	subdevice<nasbus_slot_device>("nasbus1")->set_default_option("floppy");
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( nascom1 )
	ROM_REGION(0x0800, "maincpu", 0)
	ROM_DEFAULT_BIOS("t4")
	ROM_SYSTEM_BIOS(0, "t1", "NasBug T1")
	ROMX_LOAD("nasbugt1.ic38", 0x0000, 0x0400, CRC(8ea07054) SHA1(3f9a8632826003d6ea59d2418674d0fb09b83a4c), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "t2", "NasBug T2")
	ROMX_LOAD("nasbugt2.ic38", 0x0000, 0x0400, CRC(e371b58a) SHA1(485b20a560b587cf9bb4208ba203b12b3841689b), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "t4", "NasBug T4")
	ROMX_LOAD("nasbugt4.rom", 0x0000, 0x0800, CRC(f391df68) SHA1(00218652927afc6360c57e77d6a4fd32d4e34566), ROM_BIOS(2)) // should really be split in halves for ic38 and ic39
	ROM_SYSTEM_BIOS(3, "bbug", "B-Bug") // by Viewfax 1978
	ROMX_LOAD("bbug.rom",     0x0000, 0x0800, CRC(1b1a340d) SHA1(99ce4d771871b3d797cc465d6245c40e41acef3e), ROM_BIOS(3))

	ROM_REGION(0x0800, "gfx1", 0)
	ROM_LOAD("nascom1.ic16",   0x0000, 0x0800, CRC(33e92a04) SHA1(be6e1cc80e7f95a032759f7df19a43c27ff93a52)) // MCM6576P
ROM_END

ROM_START( nascom2 )
	ROM_REGION(0x0800, "maincpu", 0)
	ROM_DEFAULT_BIOS("ns3")
	ROM_SYSTEM_BIOS(0, "ns1", "Nas-Sys 1")
	ROMX_LOAD("nassys1.ic34", 0x0000, 0x0800, CRC(b6300716) SHA1(29da7d462ba3f569f70ed3ecd93b981f81c7adfa), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "ns3", "Nas-Sys 3")
	ROMX_LOAD("nassys3.ic34", 0x0000, 0x0800, CRC(6804e675) SHA1(d55dccec2d1da992a39c38b0b6d24e3809073513), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "ns3a", "Nas-Sys 3 (AVC)")
	ROMX_LOAD("nassys3a.ic34", 0x0000, 0x0800, CRC(39d24a05) SHA1(7bfb574c1f8ce0f460a53b9a6c11c711aabccbb8), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "ns3n", "Nas-Sys 3 (NET)")
	ROMX_LOAD("nassys3n.ic34", 0x0000, 0x0800, CRC(87ef62bb) SHA1(dab81511925be36044b3e8b0ba26a0c717fe83ae), ROM_BIOS(3))

	ROM_REGION(0x2000, "basic", 0)
	ROM_LOAD("basic.ic43", 0x0000, 0x2000, CRC(5cb5197b) SHA1(c41669c2b6d6dea808741a2738426d97bccc9b07))

	ROM_REGION(0x1000, "gfx1", 0)
	ROM_LOAD("nascom1.ic66", 0x0000, 0x0800, CRC(33e92a04) SHA1(be6e1cc80e7f95a032759f7df19a43c27ff93a52))
	ROM_LOAD("nasgra.ic54",  0x0800, 0x0800, CRC(2bc09d32) SHA1(d384297e9b02cbcb283c020da51b3032ff62b1ae))
ROM_END

ROM_START( nascom2c )
	ROM_REGION(0x0800, "maincpu", 0)
	ROM_DEFAULT_BIOS("cpm32")
	ROM_SYSTEM_BIOS(0, "cpm21", "CP/M boot v2.1")
	ROMX_LOAD("cpmbt21.ic34", 0x0000, 0x0800, CRC(44b67ffc) SHA1(60c8335f24798f8de7ad48a4cd03e56a60d87b63), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "cpm32", "CP/M boot v3.2")
	ROMX_LOAD("cpmbt32.ic34", 0x0000, 0x0800, CRC(724f03ba) SHA1(d0958c231e5b121b6c4c97d03c76c207acf90f5a), ROM_BIOS(1))

	ROM_REGION(0x1000, "gfx1", 0)
	ROM_LOAD("nascom1.ic66", 0x0000, 0x0800, CRC(33e92a04) SHA1(be6e1cc80e7f95a032759f7df19a43c27ff93a52))
	ROM_LOAD("nasgra.ic54",  0x0800, 0x0800, CRC(2bc09d32) SHA1(d384297e9b02cbcb283c020da51b3032ff62b1ae))
ROM_END

} // Anonymous namespace


//**************************************************************************
//  GAME DRIVERS
//**************************************************************************

//    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT     CLASS          INIT           COMPANY                  FULLNAME           FLAGS
COMP( 1978, nascom1,  0,       0,      nascom1,  nascom1,  nascom1_state, init_nascom,   "Nascom Microcomputers", "Nascom 1",        MACHINE_NO_SOUND_HW )
COMP( 1979, nascom2,  0,       0,      nascom2,  nascom2,  nascom2_state, init_nascom,   "Nascom Microcomputers", "Nascom 2",        MACHINE_NO_SOUND_HW )
COMP( 1980, nascom2c, nascom2, 0,      nascom2c, nascom2c, nascom2_state, init_nascom2c, "Nascom Microcomputers", "Nascom 2 (CP/M)", MACHINE_NO_SOUND_HW )



nc.cpp
<---------------------------------------------------------------------->
// license: GPL-2.0+
// copyright-holders: Wilbert Pol, Kevin Thacker, Dirk Best
// thanks-to: Cliff Lawson, Russell Marks, Tim Surtel
/***************************************************************************

    Amstrad NC100/NC150/N200 portable computer

    Hardware:
    - Z80 CPU, 4.606000 MHz
    - RAM: 64k (NC100) or 128k (NC150/200)
    - ROM: 256k (NC100) or 512k (NC150/200)
    - Custom ASIC integrating many components
    - LCD screen with 480x64 (NC100/150) or 480x128 (NC200) pixels
    - 2 channel sound (programmable frequency beeps)
    - I8251 compatible UART
    - RTC: TC8521 (NC100/150) or MC146818 (NC200)
    - PCMCIA slot (supports SRAM cards up to 1 MB)
    - 3.5" DD floppy drive, PC compatible FAT format (NC200)
    - 64 key keyboard
    - RS232 and Centronics port
    - Lithium and alkaline batteries

    TODO:
    - Investigate why the FDC IRQ needs to be delayed
    - The dw225 rom image is identical to the nc100 v1.06 rom with the
      last half filled with 0x00. Correct?
    - Computer can be turned off, but not on anymore
    - IRQ 6 is not connected
    - Disk change hardware (disk change is not detected)
    - Verify card status port
    - Artwork

    Notes:
    - Keymap: 'Function' is mapped to 'Left Alt', 'Symbol' is mapped to
      'Right Alt', 'Menu' is mapped to 'Right Control'
    - The system will complain and reset the clock if you don't turn it off
      properly using the 'on/off' key
    - NC100 self test: Turn off, hold Function+Symbol, reset
    - NC200 self test: Turn off, hold Function+Control+Symbol, reset

***************************************************************************/

#include "emu.h"

#include "bus/centronics/ctronics.h"
#include "bus/rs232/rs232.h"
#include "bus/pccard/sram.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/mc146818.h"
#include "machine/nvram.h"
#include "machine/ram.h"
#include "machine/rp5c01.h"
#include "machine/timer.h"
#include "machine/upd765.h"
#include "sound/beep.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#define LOG_DEBUG   (1U << 1)
#define LOG_IRQ     (1U << 2)

#define VERBOSE (LOG_GENERAL | LOG_DEBUG)
#include "logmacro.h"


namespace {


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class nc_state : public driver_device
{
public:
	nc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ram(*this, RAM_TAG),
		m_rombank(*this, "rombank%u", 0U),
		m_rambank(*this, "rambank%u", 0U),
		m_screen(*this, "screen"),
		m_beeper1(*this, "beep.1"),
		m_beeper2(*this, "beep.2"),
		m_centronics(*this, "centronics"),
		m_uart(*this, "uart"),
		m_uart_clock(*this, "uart_clock"),
		m_nvram(*this, "nvram"),
		m_pcmcia(*this, "pcmcia"),
		m_mem_view0(*this, "block0"),
		m_mem_view1(*this, "block1"),
		m_mem_view2(*this, "block2"),
		m_mem_view3(*this, "block3"),
		m_keyboard(*this, "line%d", 0U),
		m_battery(*this, "battery"),
		m_pcmcia_card_detect(1),
		m_pcmcia_write_protect(1),
		m_pcmcia_battery_voltage_1(1),
		m_pcmcia_battery_voltage_2(1)
	{
	}

	int pcmcia_card_detect_r() { return m_pcmcia_card_detect; }
	int pcmcia_write_protect_r() { return m_pcmcia_write_protect; }
	int pcmcia_battery_voltage_r() { return m_pcmcia_battery_voltage_1 && m_pcmcia_battery_voltage_2; }

	void nc_base(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void update_interrupts();

	uint8_t memory_management_r(offs_t offset);
	void memory_management_w(offs_t offset, uint8_t data);
	virtual void uart_control_w(uint8_t data);
	void nc_sound_w(offs_t offset, uint8_t data);
	virtual void poweroff_control_w(uint8_t data);
	uint8_t irq_status_r();

	void centronics_busy_w(int state);

	template<int N> uint8_t pcmcia_r(offs_t offset);
	template<int N> void pcmcia_w(offs_t offset, uint8_t data);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_memory_bank_array<4> m_rombank;
	required_memory_bank_array<4> m_rambank;
	required_device<screen_device> m_screen;
	required_device<beep_device> m_beeper1;
	required_device<beep_device> m_beeper2;
	required_device<centronics_device> m_centronics;
	required_device<i8251_device> m_uart;
	required_device<clock_device> m_uart_clock;
	required_device<nvram_device> m_nvram;
	required_device<pccard_slot_device> m_pcmcia;
	memory_view m_mem_view0;
	memory_view m_mem_view1;
	memory_view m_mem_view2;
	memory_view m_mem_view3;
	required_ioport_array<10> m_keyboard;
	required_ioport m_battery;

	uint8_t m_rom_banks;
	uint8_t m_ram_banks;

	uint16_t m_display_memory_start = 0;
	uint8_t m_mmc[4];
	uint8_t m_uart_control = 0;

	int m_irq_mask = 0;
	int m_irq_status = 0;

	int m_uart_rxrdy;
	int m_uart_txrdy;
	int m_centronics_busy = 0;

	int m_pcmcia_card_detect;
	int m_pcmcia_write_protect;
	int m_pcmcia_battery_voltage_1;
	int m_pcmcia_battery_voltage_2;

	rgb_t m_fg_color;
	rgb_t m_bg_color;

private:
	TIMER_DEVICE_CALLBACK_MEMBER(keyscan_timer);
	void nc_sound_update(int channel);

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void pcmcia_card_detect_w(int state) { m_pcmcia_card_detect = state; }
	void pcmcia_write_protect_w(int state) { m_pcmcia_write_protect = state; }
	void pcmcia_battery_voltage_1_w(int state) { m_pcmcia_battery_voltage_1 = state; }
	void pcmcia_battery_voltage_2_w(int state) { m_pcmcia_battery_voltage_2 = state; }

	int m_sound_channel_periods[2]{};
};

class nc100_state : public nc_state
{
public:
	nc100_state(const machine_config &mconfig, device_type type, const char *tag) :
		nc_state(mconfig, type, tag),
		m_centronics_ack(0)
	{
	}

	DECLARE_INPUT_CHANGED_MEMBER( power_button );

	int centronics_ack_r() { return m_centronics_ack; }
	int centronics_busy_r() { return m_centronics_busy; }

	void nc100(machine_config &config);
	void nc150(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void io_map(address_map &map) ATTR_COLD;

	void display_memory_start_w(uint8_t data);
	void card_wait_control_w(uint8_t data);
	void irq_mask_w(uint8_t data);
	void irq_status_w(uint8_t data);
	uint8_t keyboard_r(offs_t offset);

	void uart_txrdy_w(int state);
	void uart_rxrdy_w(int state);
	void centronics_ack_w(int state);

	int m_centronics_ack;
};

class nc200_state : public nc_state
{
public:
	nc200_state(const machine_config &mconfig, device_type type, const char *tag) :
		nc_state(mconfig, type, tag),
		m_rtc(*this, "rtc"),
		m_fdc(*this, "fdc"),
		m_floppy(*this, "fdc:0")
	{
	}

	DECLARE_INPUT_CHANGED_MEMBER( power_button );

	void nc200(machine_config &config);

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

private:
	required_device<mc146818_device> m_rtc;
	required_device<upd765a_device> m_fdc;
	required_device<floppy_connector> m_floppy;

	void io_map(address_map &map) ATTR_COLD;

	void display_memory_start_w(uint8_t data);
	void card_wait_control_w(uint8_t data);
	virtual void uart_control_w(uint8_t data) override;
	uint8_t centronics_busy_r();
	void irq_mask_w(uint8_t data);
	virtual void poweroff_control_w(uint8_t data) override;
	void irq_status_w(uint8_t data);
	uint8_t keyboard_r(offs_t offset);

	void fdc_int_w(int state);
	void uart_rxrdy_w(int state);
	void centronics_ack_w(int state);

	emu_timer *m_fdc_irq_timer;
	TIMER_CALLBACK_MEMBER(fdc_irq);
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void nc_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).view(m_mem_view0);
	m_mem_view0[0](0x0000, 0x3fff).bankr(m_rombank[0]);
	m_mem_view0[1](0x0000, 0x3fff).bankrw(m_rambank[0]);
	m_mem_view0[2](0x0000, 0x3fff).rw(FUNC(nc_state::pcmcia_r<0>), FUNC(nc_state::pcmcia_w<0>));
	m_mem_view0[3](0x0000, 0x3fff).bankr(m_rombank[0]);
	map(0x4000, 0x7fff).view(m_mem_view1);
	m_mem_view1[0](0x4000, 0x7fff).bankr(m_rombank[1]);
	m_mem_view1[1](0x4000, 0x7fff).bankrw(m_rambank[1]);
	m_mem_view1[2](0x4000, 0x7fff).rw(FUNC(nc_state::pcmcia_r<1>), FUNC(nc_state::pcmcia_w<1>));
	m_mem_view1[3](0x4000, 0x7fff).bankr(m_rombank[1]);
	map(0x8000, 0xbfff).view(m_mem_view2);
	m_mem_view2[0](0x8000, 0xbfff).bankr(m_rombank[2]);
	m_mem_view2[1](0x8000, 0xbfff).bankrw(m_rambank[2]);
	m_mem_view2[2](0x8000, 0xbfff).rw(FUNC(nc_state::pcmcia_r<2>), FUNC(nc_state::pcmcia_w<2>));
	m_mem_view2[3](0x8000, 0xbfff).bankr(m_rombank[2]);
	map(0xc000, 0xffff).view(m_mem_view3);
	m_mem_view3[0](0xc000, 0xffff).bankr(m_rombank[3]);
	m_mem_view3[1](0xc000, 0xffff).bankrw(m_rambank[3]);
	m_mem_view3[2](0xc000, 0xffff).rw(FUNC(nc_state::pcmcia_r<3>), FUNC(nc_state::pcmcia_w<3>));
	m_mem_view3[3](0xc000, 0xffff).bankr(m_rombank[3]);
}

void nc100_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x0f).w(FUNC(nc100_state::display_memory_start_w));
	map(0x10, 0x13).rw(FUNC(nc100_state::memory_management_r), FUNC(nc100_state::memory_management_w));
	map(0x20, 0x20).w(FUNC(nc100_state::card_wait_control_w));
	map(0x30, 0x30).w(FUNC(nc100_state::uart_control_w));
	map(0x40, 0x40).w("cent_data_out", FUNC(output_latch_device::write));
	map(0x50, 0x53).w(FUNC(nc100_state::nc_sound_w));
	map(0x60, 0x60).w(FUNC(nc100_state::irq_mask_w));
	map(0x70, 0x70).w(FUNC(nc100_state::poweroff_control_w));
	map(0x90, 0x90).rw(FUNC(nc100_state::irq_status_r), FUNC(nc100_state::irq_status_w));
	map(0xa0, 0xa0).portr("battery");
	map(0xb0, 0xb9).r(FUNC(nc100_state::keyboard_r));
	map(0xc0, 0xc1).rw(m_uart, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xd0, 0xdf).rw("rtc", FUNC(tc8521_device::read), FUNC(tc8521_device::write));
}

void nc200_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x0f).w(FUNC(nc200_state::display_memory_start_w));
	map(0x10, 0x13).rw(FUNC(nc200_state::memory_management_r), FUNC(nc200_state::memory_management_w));
	map(0x20, 0x20).w(FUNC(nc200_state::card_wait_control_w));
	map(0x30, 0x30).w(FUNC(nc200_state::uart_control_w));
	map(0x40, 0x40).w("cent_data_out", FUNC(output_latch_device::write));
	map(0x50, 0x53).w(FUNC(nc200_state::nc_sound_w));
	map(0x60, 0x60).w(FUNC(nc200_state::irq_mask_w));
	map(0x70, 0x70).w(FUNC(nc200_state::poweroff_control_w));
	map(0x80, 0x80).r(FUNC(nc200_state::centronics_busy_r));
	map(0x90, 0x90).rw(FUNC(nc200_state::irq_status_r), FUNC(nc200_state::irq_status_w));
	map(0xa0, 0xa0).portr("battery");
	map(0xb0, 0xb9).r(FUNC(nc200_state::keyboard_r));
	map(0xc0, 0xc1).rw(m_uart, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xd0, 0xd0).w(m_rtc, FUNC(mc146818_device::address_w));
	map(0xd1, 0xd1).rw(m_rtc, FUNC(mc146818_device::data_r), FUNC(mc146818_device::data_w));
	map(0xe0, 0xe1).m(m_fdc, FUNC(upd765a_device::map));
}


//**************************************************************************
//  INPUT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( nc100 )
	PORT_START("line0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)         PORT_NAME("Left Shift")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_MAMEKEY(RSHIFT)) PORT_NAME("Right Shift")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)   PORT_CHAR(UCHAR_MAMEKEY(LEFT))   PORT_NAME(u8"\u2190 Word (Red)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)  PORT_CHAR(13)                    PORT_NAME(u8"\u21b2")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("line1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT)     PORT_CHAR(UCHAR_MAMEKEY(LALT))     PORT_NAME("Function (Yellow)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_NAME("Control")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)      PORT_CHAR(UCHAR_MAMEKEY(ESC))      PORT_NAME("Stop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)    PORT_CHAR(' ')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)        PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("line2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RALT)     PORT_CHAR(UCHAR_SHIFT_2)           PORT_NAME("Symbol")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)        PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)      PORT_CHAR('\t')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("line3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR(U'£')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')

	PORT_START("line4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')

	PORT_START("line5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') PORT_NAME("y  Y  M+ (Calc)")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')

	PORT_START("line6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)         PORT_CHAR('6') PORT_CHAR('^')   PORT_NAME("6  ^  MRC (Calc)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)      PORT_CHAR(UCHAR_MAMEKEY(DOWN))  PORT_NAME(u8"\u2193 Diary (Blue)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)       PORT_CHAR(UCHAR_MAMEKEY(DEL))   PORT_NAME(u8"Del\u2192")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)     PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_NAME(u8"\u2192 Calc (Green)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('#') PORT_CHAR('~')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)     PORT_CHAR('/') PORT_CHAR('?')   PORT_NAME("/  ?  + (Calc)")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)         PORT_CHAR('h') PORT_CHAR('H')   PORT_NAME("h  H  M- (Calc)")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)         PORT_CHAR('n') PORT_CHAR('N')   PORT_NAME("n  N  CE/C (Calc)")

	PORT_START("line7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)      PORT_CHAR('=')  PORT_CHAR('+')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)           PORT_CHAR('7')  PORT_CHAR('&')      PORT_NAME("7  &  7 (Calc)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2)  PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)          PORT_CHAR(UCHAR_MAMEKEY(UP))        PORT_NAME(u8"\u2191 (White)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RCONTROL)    PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))  PORT_NAME("Menu  Secret")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)           PORT_CHAR('u')  PORT_CHAR('U')      PORT_NAME("u  U  4 (Calc)")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)           PORT_CHAR('m')  PORT_CHAR('M')      PORT_NAME("m  M  0 (Calc)")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)           PORT_CHAR('k')  PORT_CHAR('K')      PORT_NAME("k  K  2 (Calc)")

	PORT_START("line8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)           PORT_CHAR('8')  PORT_CHAR('*')  PORT_NAME("8  *  8 (Calc)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)       PORT_CHAR('-')  PORT_CHAR('_')  PORT_NAME(u8"-  _  ± (Calc)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)  PORT_CHAR(']')  PORT_CHAR('}')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)   PORT_CHAR('[')  PORT_CHAR('{')  PORT_NAME("[  {  % (Calc)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)       PORT_CHAR('\'') PORT_CHAR('@')  PORT_NAME(u8"'  @  \u221a (Calc)")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)           PORT_CHAR('i')  PORT_CHAR('I')  PORT_NAME("i  I  5 (Calc)")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)           PORT_CHAR('j')  PORT_CHAR('J')  PORT_NAME("j  J  1 (Calc)")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)       PORT_CHAR(',')  PORT_CHAR('<')  PORT_NAME(",  <  = (Calc)")

	PORT_START("line9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)         PORT_CHAR('0') PORT_CHAR(')') PORT_NAME(u8"0  )  ÷ (Calc)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)         PORT_CHAR('9') PORT_CHAR('(') PORT_NAME("9  (  9 (Calc)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)                  PORT_NAME("Del\u2190")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)         PORT_CHAR('p') PORT_CHAR('P') PORT_NAME(u8"p  P  × (Calc)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)     PORT_CHAR(';') PORT_CHAR(':') PORT_NAME(";  :  - (Calc)")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)         PORT_CHAR('l') PORT_CHAR('L') PORT_NAME("l  L  3 (Calc)")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)         PORT_CHAR('o') PORT_CHAR('O') PORT_NAME("o  O  6 (Calc)")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)      PORT_CHAR('.') PORT_CHAR('>') PORT_NAME(".  >  . (Calc)")

	PORT_START("power")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Power On/Off") PORT_CODE(KEYCODE_END) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(nc100_state::power_button), 0)

	PORT_START("battery")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(nc100_state::centronics_ack_r))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(nc100_state::centronics_busy_r))
	PORT_CONFNAME(0x04, 0x00, "Lithium Battery")
	PORT_CONFSETTING(   0x00, "Good")
	PORT_CONFSETTING(   0x04, "Bad")
	PORT_CONFNAME(0x08, 0x00, "Main Battery")
	PORT_CONFSETTING(   0x00, "Good")
	PORT_CONFSETTING(   0x08, "Bad")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(nc100_state::pcmcia_battery_voltage_r))
	PORT_BIT(0x20, 0x00, IPT_UNKNOWN) // input voltage?
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(nc100_state::pcmcia_write_protect_r))
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(nc100_state::pcmcia_card_detect_r))
INPUT_PORTS_END

static INPUT_PORTS_START( nc100de )
	PORT_INCLUDE(nc100)

	PORT_MODIFY("line0")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_NAME(u8"\u2190 Text (Red)")

	PORT_MODIFY("line1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT)     PORT_CHAR(UCHAR_MAMEKEY(LALT))     PORT_NAME("Funktion (Yellow)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_NAME("Befehl")

	PORT_MODIFY("line3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR(U'§') PORT_CHAR(U'\u207f')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')  PORT_CHAR(U'²')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')  PORT_CHAR('@')

	PORT_MODIFY("line4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') PORT_CHAR(U'£')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('y') PORT_CHAR('Y')

	PORT_MODIFY("line5")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('z') PORT_CHAR('Z') PORT_NAME("z  Z  M+ (Calc)")

	PORT_MODIFY("line6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)         PORT_CHAR('6') PORT_CHAR('&') PORT_CHAR('^') PORT_NAME("6  &  ^  MRC (Calc)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)      PORT_CHAR(UCHAR_MAMEKEY(DOWN))               PORT_NAME(u8"\u2193 Kal/Uhr (Blue)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)       PORT_CHAR(UCHAR_MAMEKEY(DEL))                PORT_NAME(u8"Lösch\u2192")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)     PORT_CHAR(UCHAR_MAMEKEY(RIGHT))              PORT_NAME(u8"\u2192 Rechner (Green)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('#') PORT_CHAR('\'')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)     PORT_CHAR('-') PORT_CHAR('_')                PORT_NAME("-  _  + (Calc)")

	PORT_MODIFY("line7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR(U'´') PORT_CHAR('`')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7')  PORT_CHAR('/') PORT_CHAR('{')  PORT_NAME("7  /  {  7 (Calc)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('<')  PORT_CHAR('>') PORT_CHAR('|')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))             PORT_NAME(u8"Menü  Geheim")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)          PORT_CHAR('m')  PORT_CHAR('M') PORT_CHAR(U'μ') PORT_NAME(u8"m  M  μ  0 (Calc)")

	PORT_MODIFY("line8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8')  PORT_CHAR('(')  PORT_CHAR('[')  PORT_NAME("8  (  [  8 (Calc)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR(U'ß') PORT_CHAR('?')  PORT_CHAR('\\') PORT_NAME(u8"ß  ?  \\  ± (Calc)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('+')  PORT_CHAR('*')  PORT_CHAR('~')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR(U'ü') PORT_CHAR(U'Ü')                 PORT_NAME(u8"ü  Ü  % (Calc)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(U'ä') PORT_CHAR(U'Ä')                 PORT_NAME(u8"ä  Ä  \u221a (Calc)")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')  PORT_CHAR(';')                  PORT_NAME(",  ;  = (Calc)")

	PORT_MODIFY("line9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)         PORT_CHAR('0')  PORT_CHAR('=')  PORT_CHAR('}') PORT_NAME("0  =  }  ÷ (Calc)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)         PORT_CHAR('9')  PORT_CHAR(')')  PORT_CHAR(']') PORT_NAME("9  )  ]  9 (Calc)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)                                   PORT_NAME(u8"Lösch\u2190")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)     PORT_CHAR(U'ö') PORT_CHAR(U'Ö')                PORT_NAME(u8"ö  Ö  - (Calc)")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)      PORT_CHAR('.')  PORT_CHAR(':')                 PORT_NAME(".  :  . (Calc)")
INPUT_PORTS_END

static INPUT_PORTS_START( nc100dk )
	PORT_INCLUDE(nc100)

	PORT_MODIFY("line0")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_NAME(u8"\u2190 Ord (Red)")

	PORT_MODIFY("line1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT)     PORT_CHAR(UCHAR_MAMEKEY(LALT))     PORT_NAME("Funktion (Yellow)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_NAME("Kontrol")

	PORT_MODIFY("line2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_NAME("Versaler")

	PORT_MODIFY("line3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#') PORT_CHAR(U'£')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"') PORT_CHAR('@')

	PORT_MODIFY("line6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)         PORT_CHAR('6')  PORT_CHAR('&')  PORT_NAME("6  &  MRC (Calc)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)      PORT_CHAR(UCHAR_MAMEKEY(DOWN))  PORT_NAME(u8"\u2193 Kalend. (Blue)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)       PORT_CHAR(UCHAR_MAMEKEY(DEL))   PORT_NAME(u8"Slet\u2192")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)     PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_NAME(u8"\u2192 Regn. (Green)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\'') PORT_CHAR('*')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)     PORT_CHAR('-')  PORT_CHAR('_')  PORT_NAME("-  _  + (Calc)")

	PORT_MODIFY("line7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR(U'´') PORT_CHAR('`') PORT_CHAR('|')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7')  PORT_CHAR('/') PORT_CHAR('{')  PORT_NAME("7  /  {  7 (Calc)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('<')  PORT_CHAR('>') PORT_CHAR('\\')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))             PORT_NAME(u8"Menu  Skjult")

	PORT_MODIFY("line8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8')  PORT_CHAR('(')  PORT_CHAR('[') PORT_NAME("8  (  [  8 (Calc)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('+')  PORT_CHAR('?')                 PORT_NAME(u8"+  ?  ± (Calc)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(U'¨') PORT_CHAR('^')  PORT_CHAR('~')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR(U'å') PORT_CHAR(U'Å')                PORT_NAME(u8"å  Å  % (Calc)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(U'ø') PORT_CHAR(U'Ø')                PORT_NAME(u8"ø  Ø  \u221a (Calc)")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')  PORT_CHAR(';')                 PORT_NAME(",  ;  = (Calc)")

	PORT_MODIFY("line9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)         PORT_CHAR('0')  PORT_CHAR('=')  PORT_CHAR('}') PORT_NAME("0  =  }  ÷ (Calc)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)         PORT_CHAR('9')  PORT_CHAR(')')  PORT_CHAR(']') PORT_NAME("9  )  ]  9 (Calc)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)                                   PORT_NAME(u8"Slet\u2190")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)     PORT_CHAR(U'æ') PORT_CHAR(U'Æ')                PORT_NAME(u8"æ  Æ  - (Calc)")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)      PORT_CHAR('.')  PORT_CHAR(':')                 PORT_NAME(".  :  . (Calc)")
INPUT_PORTS_END

static INPUT_PORTS_START( nc100sv )
	PORT_INCLUDE(nc100)

	PORT_MODIFY("line0")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_NAME(u8"\u2190 Text (Red)") // label on picture unclear

	PORT_MODIFY("line1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT)     PORT_CHAR(UCHAR_MAMEKEY(LALT))     PORT_NAME("Funktion (Yellow)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_NAME("Kontroll")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)      PORT_CHAR(UCHAR_MAMEKEY(ESC))      PORT_NAME("Stopp")

	PORT_MODIFY("line2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_NAME(u8"Versallås")

	PORT_MODIFY("line3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#') PORT_CHAR(U'£')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"') PORT_CHAR('@')

	PORT_MODIFY("line6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)         PORT_CHAR('6')  PORT_CHAR('&')  PORT_NAME("6  &  MRC (Calc)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)      PORT_CHAR(UCHAR_MAMEKEY(DOWN))  PORT_NAME(u8"\u2193 Kalend. (Blue)") // label on picture unclear
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)       PORT_CHAR(UCHAR_MAMEKEY(DEL))   PORT_NAME(u8"Rensa\u2192")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)     PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_NAME(u8"\u2192 Calc (Green)") // label on picture unclear
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\'') PORT_CHAR('*')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)     PORT_CHAR('-')  PORT_CHAR('_')  PORT_NAME("-  _  + (Calc)")

	PORT_MODIFY("line7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR(U'´') PORT_CHAR('`')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7')  PORT_CHAR('/') PORT_CHAR('{') PORT_NAME("7  /  {  7 (Calc)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('<')  PORT_CHAR('>') PORT_CHAR('|')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))            PORT_NAME(u8"Menu  Hemlig") // label on picture unclear

	PORT_MODIFY("line8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8')  PORT_CHAR('(')  PORT_CHAR('[') PORT_NAME("8  (  [  8 (Calc)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('+')  PORT_CHAR('?')                 PORT_NAME(u8"+  ?  ± (Calc)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(U'¨') PORT_CHAR('^')  PORT_CHAR('~')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR(U'å') PORT_CHAR(U'Å')                PORT_NAME(u8"å  Å  % (Calc)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(U'ä') PORT_CHAR(U'Ä')                PORT_NAME(u8"ä  Ä  \u221a (Calc)")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')  PORT_CHAR(';')                 PORT_NAME(",  ;  = (Calc)")

	PORT_MODIFY("line9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)         PORT_CHAR('0')  PORT_CHAR('=') PORT_CHAR('}') PORT_NAME("0  =  }  ÷ (Calc)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)         PORT_CHAR('9')  PORT_CHAR(')') PORT_CHAR(']') PORT_NAME("9  )  ]  9 (Calc)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)                                  PORT_NAME(u8"Rensa\u2190")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)     PORT_CHAR(U'ö') PORT_CHAR(U'Ö')               PORT_NAME(u8"ö  Ö  - (Calc)")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)      PORT_CHAR('.')  PORT_CHAR(':')                PORT_NAME(".  :  . (Calc)")
INPUT_PORTS_END

static INPUT_PORTS_START( nc150fr )
	PORT_INCLUDE(nc100)

	PORT_MODIFY("line0")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_NAME(u8"\u2190 Texte (Red)")

	PORT_MODIFY("line1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT))               PORT_NAME("Fonction (Yellow)")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)    PORT_CHAR('(') PORT_CHAR('5') PORT_CHAR('[')

	PORT_MODIFY("line2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_NAME("Maj/Min")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)        PORT_CHAR('&') PORT_CHAR('1')

	PORT_MODIFY("line3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('"')  PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR(U'é') PORT_CHAR('2') PORT_CHAR('~')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('a')  PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('z')  PORT_CHAR('Z')

	PORT_MODIFY("line4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR(U'´') PORT_CHAR('4') PORT_CHAR('{')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('w')  PORT_CHAR('W')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('q')  PORT_CHAR('Q')

	PORT_MODIFY("line6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)         PORT_CHAR('-') PORT_CHAR('6')  PORT_CHAR('|') PORT_NAME("-  6  |  MRC (Calc)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)      PORT_CHAR(UCHAR_MAMEKEY(DOWN))                PORT_NAME(u8"\u2193 Agenda (Blue)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)       PORT_CHAR(UCHAR_MAMEKEY(DEL))                 PORT_NAME(u8"Eff\u2192")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('*') PORT_CHAR(U'μ')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)     PORT_CHAR('!') PORT_CHAR(U'§')                PORT_NAME(u8"!  §  + (Calc)")

	PORT_MODIFY("line7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('=')  PORT_CHAR('+') PORT_CHAR('}')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR(U'è') PORT_CHAR('7') PORT_CHAR('`') PORT_NAME(u8"è  7  `  7 (Calc)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('<')  PORT_CHAR('>')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))                  PORT_NAME(u8"\u2191 Table (White)")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)          PORT_CHAR(',')  PORT_CHAR('?')                PORT_NAME(",  ?  0 (Calc)")

	PORT_MODIFY("line8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('_')  PORT_CHAR('8')  PORT_CHAR('\\') PORT_NAME("_  8  \\  8 (Calc)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR(')')  PORT_CHAR(U'°') PORT_CHAR(']')  PORT_NAME(u8")  °  ]  ± (Calc)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('$')  PORT_CHAR(U'£')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('^')  PORT_CHAR(U'¨')                 PORT_NAME(u8"^  ¨  % (Calc)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(U'ù') PORT_CHAR('%')                  PORT_NAME(u8"ù  %  \u221a (Calc)")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(':')  PORT_CHAR('/')                  PORT_NAME(":  /  = (Calc)")

	PORT_MODIFY("line9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)         PORT_CHAR(U'à') PORT_CHAR('0') PORT_CHAR('@') PORT_NAME(u8"à  0  @  ÷ (Calc)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)         PORT_CHAR(U'ç') PORT_CHAR('9') PORT_CHAR('^') PORT_NAME(u8"ç  9  ^  9 (Calc)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)                                  PORT_NAME(u8"Eff\u2190")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)     PORT_CHAR('m')  PORT_CHAR('M')                PORT_NAME(u8"m  M  - (Calc)")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)      PORT_CHAR(';')  PORT_CHAR('.')                PORT_NAME(";  .  . (Calc)")
INPUT_PORTS_END

static INPUT_PORTS_START( nc150it )
	PORT_INCLUDE(nc100)

	PORT_MODIFY("line0")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_NAME(u8"\u2190 Testi (Red)")

	PORT_MODIFY("line1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT)     PORT_CHAR(UCHAR_MAMEKEY(LALT))     PORT_NAME("Funzione (Yellow)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_NAME("CTRL")

	PORT_MODIFY("line2")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_SHIFT_2) PORT_NAME("Simboli")

	PORT_MODIFY("line6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)         PORT_CHAR('6')  PORT_CHAR('&')  PORT_NAME("6  &  MRC (Calc)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)      PORT_CHAR(UCHAR_MAMEKEY(DOWN))  PORT_NAME(u8"\u2193 Agenda (Blue)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)       PORT_CHAR(UCHAR_MAMEKEY(DEL))   PORT_NAME(u8"Canc\u2192")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(U'ù') PORT_CHAR(U'§')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)     PORT_CHAR('-')  PORT_CHAR('_')  PORT_NAME("-  _  + (Calc)")

	PORT_MODIFY("line7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR(U'ì') PORT_CHAR('^')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7')  PORT_CHAR('/') PORT_NAME("7  /  7 (Calc)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('<')  PORT_CHAR('>')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))   PORT_NAME(u8"\u2191 Tabella. El. (White)")

	PORT_MODIFY("line8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8')  PORT_CHAR('(')                 PORT_NAME("8  (  8 (Calc)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('\'') PORT_CHAR('?')                 PORT_NAME(u8"'  ?  ± (Calc)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('+')  PORT_CHAR('*')  PORT_CHAR(']')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR(U'è') PORT_CHAR(U'é') PORT_CHAR('[') PORT_NAME("è  é  [  % (Calc)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(U'à') PORT_CHAR(U'°') PORT_CHAR('#') PORT_NAME(u8"à  °  #  \u221a (Calc)")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')  PORT_CHAR(';')                 PORT_NAME(",  ;  = (Calc)")

	PORT_MODIFY("line9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)         PORT_CHAR('0')  PORT_CHAR('=')  PORT_NAME(u8"0  =  ÷ (Calc)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)         PORT_CHAR('9')  PORT_CHAR(')')  PORT_NAME("9  )  9 (Calc)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)                    PORT_NAME("Canc\u2190")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)     PORT_CHAR(U'ò') PORT_CHAR(U'ç') PORT_NAME(u8"ò  ç  - (Calc)")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)      PORT_CHAR('.')  PORT_CHAR(':')  PORT_NAME(".  :  . (Calc)")
INPUT_PORTS_END

static INPUT_PORTS_START( nc200 )
	PORT_INCLUDE(nc100)

	PORT_MODIFY("line0")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')

	PORT_MODIFY("line1")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(') PORT_NAME("9  (  9 (Calc)")

	PORT_MODIFY("line2")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^') PORT_NAME("6  ^  MRC (Calc)")

	PORT_MODIFY("line4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*') PORT_NAME("8  *  8 (Calc)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&') PORT_NAME("7  &  7 (Calc)")

	PORT_MODIFY("line6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_MODIFY("line7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')

	PORT_MODIFY("line8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_MODIFY("line9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')') PORT_NAME(u8"0  )  ÷ (Calc)")

	PORT_MODIFY("power")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Power On/Off") PORT_CODE(KEYCODE_END) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(nc200_state::power_button), 0)

	PORT_MODIFY("battery")
	PORT_CONFNAME(0x01, 0x00, "Battery for Floppy Drive")
	PORT_CONFSETTING(   0x00, "Good")
	PORT_CONFSETTING(   0x01, "Bad")
	PORT_BIT(0x02, 0x00, IPT_UNKNOWN)
	PORT_CONFNAME(0x04, 0x00, "Main Battery")
	PORT_CONFSETTING(   0x00, "Good")
	PORT_CONFSETTING(   0x04, "Bad")
	PORT_BIT(0x08, 0x00, IPT_UNKNOWN)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(nc200_state::pcmcia_battery_voltage_r))
	PORT_CONFNAME(0x20, 0x00, "Lithium Battery")
	PORT_CONFSETTING(   0x00, "Good")
	PORT_CONFSETTING(   0x20, "Bad")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(nc200_state::pcmcia_write_protect_r))
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(nc200_state::pcmcia_card_detect_r))
INPUT_PORTS_END


//**************************************************************************
//  INPUT HANDLING
//**************************************************************************

INPUT_CHANGED_MEMBER( nc100_state::power_button )
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, newval ? ASSERT_LINE : CLEAR_LINE);
}

INPUT_CHANGED_MEMBER( nc200_state::power_button )
{
	m_irq_status |= 0x10;
	update_interrupts();
}

uint8_t nc100_state::keyboard_r(offs_t offset)
{
	if (offset == 9)
	{
		m_irq_status &= ~0x08;
		update_interrupts();
	}

	return m_keyboard[offset]->read();
}

uint8_t nc200_state::keyboard_r(offs_t offset)
{
	return m_keyboard[offset]->read();
}

TIMER_DEVICE_CALLBACK_MEMBER( nc_state::keyscan_timer )
{
	m_irq_status |= 0x08;
	update_interrupts();
}


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

void nc100_state::display_memory_start_w(uint8_t data)
{
	// 7654----  a15 to a12 of display memory start address
	// ----3210  not used

	LOG("display_memory_start_w: %04x\n", data);

	m_display_memory_start = BIT(data, 4, 4) << 12;
}

void nc200_state::display_memory_start_w(uint8_t data)
{
	// 765-----  a15 to a13 of display memory start address
	// ---43210  not used

	LOG("display_memory_start_w: %04x\n", data);

	m_display_memory_start = BIT(data, 5, 3) << 13;
}

uint32_t nc_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	for (int y = 0; y <= cliprect.max_y; y++)
	{
		// 64 bytes/line
		uint8_t const *line = reinterpret_cast<uint8_t const *>(m_ram->pointer()) + m_display_memory_start + (y << 6);

		for (int x = 0; x <= cliprect.max_x; x+= 8)
		{
			for (int i = 0; i < 8; i++)
				bitmap.pix(y, x + i) = BIT(*line, 7 - i) ? m_fg_color : m_bg_color;

			line++;
		}
	}

	return 0;
}


//**************************************************************************
//  AUDIO EMULATION
//**************************************************************************

void nc_state::nc_sound_update(int channel)
{
	channel &= 1;
	beep_device *beeper_device = channel ? m_beeper2 : m_beeper1;

	int period = m_sound_channel_periods[channel];

	/* if top bit is 0, sound is on */
	int on = ((period & (1<<15))==0);

	/* calculate frequency from period */
	int frequency = (int)(1000000.0f/((float)((period & 0x07fff)<<1) * 1.6276f));

	/* set state */
	beeper_device->set_state(on);
	/* set frequency */
	beeper_device->set_clock(frequency);
}

void nc_state::nc_sound_w(offs_t offset, uint8_t data)
{
	LOG("sound w: %04x %02x\n", offset, data);

	switch (offset)
	{
		case 0x0:
		{
			/* update period value */
			m_sound_channel_periods[0]  =
				(m_sound_channel_periods[0] & 0x0ff00) | (data & 0x0ff);

			nc_sound_update(0);
		}
		break;

		case 0x01:
		{
			m_sound_channel_periods[0] =
				(m_sound_channel_periods[0] & 0x0ff) | ((data & 0x0ff)<<8);

			nc_sound_update(0);
		}
		break;

		case 0x02:
		{
			/* update period value */
			m_sound_channel_periods[1]  =
				(m_sound_channel_periods[1] & 0x0ff00) | (data & 0x0ff);

			nc_sound_update(1);
		}
		break;

		case 0x03:
		{
			m_sound_channel_periods[1] =
				(m_sound_channel_periods[1] & 0x0ff) | ((data & 0x0ff)<<8);

			nc_sound_update(1);
		}
		break;

		default:
			break;
	}
}


//**************************************************************************
//  PCMCIA
//**************************************************************************

template<int N>
uint8_t nc_state::pcmcia_r(offs_t offset)
{
	if (BIT(m_uart_control, 7) == 1)
		return m_pcmcia->read_memory_byte(((m_mmc[N] & 0x3f) << 14) | offset);
	else
		return m_pcmcia->read_reg_byte(((m_mmc[N] & 0x3f) << 14) | offset);
}

template<int N>
void nc_state::pcmcia_w(offs_t offset, uint8_t data)
{
	if (BIT(m_uart_control, 7) == 1)
		m_pcmcia->write_memory_byte(((m_mmc[N] & 0x3f) << 14) | offset, data);
	else
		m_pcmcia->write_reg_byte(((m_mmc[N] & 0x3f) << 14) | offset, data);
}

void nc100_state::card_wait_control_w(uint8_t data)
{
	// 7-------  enable memory wait states for card
	// -6543210  not used

	LOG("card_wait_control_w: %02x\n", data);
}

void nc200_state::card_wait_control_w(uint8_t data)
{
	// 7-------  enable memory wait states for card
	// -6543---  not used
	// -----2--  floppy motor
	// ------1-  unknown (floppy related?)
	// -------0  fdc terminal count

	LOG("card_wait_control_w: %02x\n", data);

	floppy_image_device *floppy = m_floppy->get_device();

	if (floppy)
		floppy->mon_w(BIT(data, 2));

	m_fdc->tc_w(BIT(data, 0));
}


//**************************************************************************
//  CENTRONICS
//**************************************************************************

void nc_state::centronics_busy_w(int state)
{
	m_centronics_busy = state;
}

uint8_t nc200_state::centronics_busy_r()
{
	// 7654321-  not used
	// -------0  centronics busy

	return m_centronics_busy;
}

void nc100_state::centronics_ack_w(int state)
{
	LOGMASKED(LOG_IRQ, "centronics_ack_w: %02x\n", state);

	m_centronics_ack = state;

	if (state)
		m_irq_status |= 0x04;

	update_interrupts();
}

void nc200_state::centronics_ack_w(int state)
{
	LOGMASKED(LOG_IRQ, "centronics_ack_w: %02x\n", state);

	if (state)
		m_irq_status |= 0x01;

	update_interrupts();
}


//**************************************************************************
//  UART
//**************************************************************************

void nc_state::uart_control_w(uint8_t data)
{
	// 7-------  card register space
	// -6------  centronics strobe
	// --5-----  not used
	// ---4----  line driver on/off
	// ----3---  uart clock/reset
	// -----210  baud rate: 150, 300, 600, 1200, 2400, 4800, 9600, 19200

	LOG("uart_control_w: %02x\n", data);

	m_centronics->write_strobe(BIT(data, 6));

	if (BIT(m_uart_control, 3) == 1 && BIT(data, 3) == 0)
		m_uart->reset();

	m_uart_clock->set_clock_scale(1 << (data & 0x07));

	m_uart_control = data;
}

void nc200_state::uart_control_w(uint8_t data)
{
	// 76------  see above
	// --5-----  floppy related?
	// ---43210  see above

	LOG("uart_control_w: %02x\n", data);

	nc_state::uart_control_w(data);
}

void nc100_state::uart_txrdy_w(int state)
{
	LOGMASKED(LOG_IRQ, "uart_txrdy_w: %02x\n", state);

	if (m_uart_txrdy == 0 && state == 1)
		m_irq_status |= 0x02;

	update_interrupts();

	m_uart_txrdy = state;
}

void nc100_state::uart_rxrdy_w(int state)
{
	LOGMASKED(LOG_IRQ, "uart_rxrdy_w: %02x\n", state);

	if (m_uart_rxrdy == 0 && state == 1)
		m_irq_status |= 0x01;

	update_interrupts();

	m_uart_rxrdy = state;
}

void nc200_state::uart_rxrdy_w(int state)
{
	LOGMASKED(LOG_IRQ, "uart_rxrdy_w: %02x\n", state);

	if (m_uart_rxrdy == 0 && state == 1)
		m_irq_status |= 0x04;

	update_interrupts();

	m_uart_rxrdy = state;
}


//**************************************************************************
//  FLOPPY
//**************************************************************************

void nc200_state::fdc_int_w(int state)
{
	LOGMASKED(LOG_IRQ, "fdc_int_w: %02x\n", state);

	if (state)
		m_fdc_irq_timer->adjust(attotime::from_usec(100), 0);
}

TIMER_CALLBACK_MEMBER( nc200_state::fdc_irq )
{
	LOGMASKED(LOG_IRQ, "fdc_irq assert\n");

	m_irq_status |= 0x20;
	update_interrupts();
}


//**************************************************************************
//  INTERRUPTS
//**************************************************************************

void nc_state::update_interrupts()
{
	LOGMASKED(LOG_IRQ, "update_interrupts: %02x & %02x\n", m_irq_status, m_irq_mask);

	if (m_irq_status & m_irq_mask)
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
	else
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
}

uint8_t nc_state::irq_status_r()
{
	return m_irq_status ^ 0xff;
}

void nc100_state::irq_status_w(uint8_t data)
{
	// 7654----  not used
	// ----3---  key scan interrupt
	// -----2--  ack from centronics
	// ------1-  tx ready from uart
	// -------0  rx ready from uart

	LOGMASKED(LOG_IRQ, "irq_status_w: %02x\n", data);

	m_irq_status &= data;
}

void nc200_state::irq_status_w(uint8_t data)
{
	// 7-------  not used
	// -6------  ?
	// --5-----  fdc interrupt
	// ---4----  power off interrupt
	// ----3---  key scan interrupt
	// -----2--  rx ready from uart
	// ------1-  not used
	// -------0  ack from centronics

	LOGMASKED(LOG_IRQ, "irq_status_w: %02x\n", data);

	m_irq_status &= data;
	update_interrupts();
}

void nc100_state::irq_mask_w(uint8_t data)
{
	// see irq_status_w for bit assignment

	LOGMASKED(LOG_IRQ, "irq_mask_w: %02x\n", data);

	m_irq_mask = data;
	update_interrupts();
}

void nc200_state::irq_mask_w(uint8_t data)
{
	// see irq_status_w for bit assignment

	LOGMASKED(LOG_IRQ, "irq_mask_w: %02x\n", data);

	m_irq_mask = data;
	update_interrupts();
}


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void nc_state::poweroff_control_w(uint8_t data)
{
	// 7654321-  not used
	// -------0  power off

	LOG("poweroff_control_w: %02x\n", data);
}

void nc200_state::poweroff_control_w(uint8_t data)
{
	// 76543---  not used
	// -----2--  backlight
	// ------1-  floppy?
	// -------0  power off

	LOG("poweroff_control_w: %02x\n", data);

	nc_state::poweroff_control_w(data);

	m_fg_color = BIT(data, 2) ? rgb_t(0x2b, 0x42, 0x66) : rgb_t(0x0c, 0x54, 0x9f);
	m_bg_color = BIT(data, 2) ? rgb_t(0xae, 0xa0, 0x66) : rgb_t(0xbe, 0xb7, 0x94);
}

uint8_t nc_state::memory_management_r(offs_t offset)
{
	return m_mmc[offset];
}

void nc_state::memory_management_w(offs_t offset, uint8_t data)
{
	// 76------  memory mode select
	// --543210  address lines 19 to 14

	m_mmc[offset] = data;

	memory_view *const mem_view[4] = { &m_mem_view0, &m_mem_view1, &m_mem_view2, &m_mem_view3 };
	mem_view[offset]->select(BIT(m_mmc[offset], 6, 2));
	m_rombank[offset]->set_entry(m_mmc[offset] & 0x3f & (m_rom_banks - 1));
	m_rambank[offset]->set_entry(m_mmc[offset] & 0x3f & (m_ram_banks - 1));
}

void nc_state::machine_start()
{
	m_rom_banks = (memregion("maincpu")->bytes() / 0x4000);
	m_ram_banks = (m_ram->size() / 0x4000);

	for (int i = 0; i < 4; i++)
	{
		m_rombank[i]->configure_entries(0, m_rom_banks, memregion("maincpu")->base(), 0x4000);
		m_rambank[i]->configure_entries(0, m_ram_banks, m_ram->pointer(), 0x4000);
	}

	m_nvram->set_base(m_ram->pointer(), m_ram->size());

	// setup lcd colors
	m_fg_color = rgb_t(0x38, 0x40, 0x63);
	m_bg_color = rgb_t(0xad, 0xa0, 0x71);

	// register for save states
	save_item(NAME(m_display_memory_start));
	save_item(NAME(m_mmc));
	save_item(NAME(m_uart_control));
	save_item(NAME(m_irq_mask));
	save_item(NAME(m_irq_status));
	save_item(NAME(m_uart_rxrdy));
	save_item(NAME(m_uart_txrdy));
	save_item(NAME(m_centronics_busy));
	save_item(NAME(m_pcmcia_card_detect));
	save_item(NAME(m_pcmcia_write_protect));
	save_item(NAME(m_pcmcia_battery_voltage_1));
	save_item(NAME(m_pcmcia_battery_voltage_2));
}

void nc100_state::machine_start()
{
	nc_state::machine_start();

	// register for save states
	save_item(NAME(m_centronics_ack));
}

void nc200_state::machine_start()
{
	nc_state::machine_start();

	// allocate a timer to delay the fdc interrupt
	m_fdc_irq_timer = timer_alloc(FUNC(nc200_state::fdc_irq), this);
}

void nc_state::machine_reset()
{
	// display memory start is set to 0 on reset
	m_display_memory_start = 0;

	// memory management control is set to 0 on reset
	memory_management_w(0, 0x00);
	memory_management_w(1, 0x00);
	memory_management_w(2, 0x00);
	memory_management_w(3, 0x00);

	// all interrupts masked and none active
	m_irq_mask = 0;
	m_irq_status = 0;
	update_interrupts();

	// set to 0x01 on reset
	poweroff_control_w(0x01);

	// set to 0xff on reset
	uart_control_w(0xff);

	/* setup reset state */
	m_sound_channel_periods[0] = (m_sound_channel_periods[1] = 0x0ffff);
}

void nc200_state::machine_reset()
{
	nc_state::machine_reset();

	m_fdc_irq_timer->adjust(attotime::never);
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

static void pcmcia_devices(device_slot_interface &device)
{
	device.option_add("melcard_1m", PCCARD_SRAM_MITSUBISHI_1M);
	device.option_add("sram_1m", PCCARD_SRAM_CENTENNIAL_1M);
}

void nc_state::nc_base(machine_config &config)
{
	Z80(config, m_maincpu, /*6000000*/ 4606000);        /* Russell Marks says this is more accurate */
	m_maincpu->set_addrmap(AS_PROGRAM, &nc_state::mem_map);
	config.set_maximum_quantum(attotime::from_hz(60));

	// ram
	RAM(config, m_ram).set_default_size("64K");
	NVRAM(config, "nvram", nvram_device::DEFAULT_NONE);

	TIMER(config, "keyscan_timer").configure_periodic(FUNC(nc_state::keyscan_timer), attotime::from_msec(10));

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // not accurate
	m_screen->set_screen_update(FUNC(nc_state::screen_update));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper1, 0).add_route(ALL_OUTPUTS, "mono", 0.50);
	BEEP(config, m_beeper2, 0).add_route(ALL_OUTPUTS, "mono", 0.50);

	// centronics
	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(nc_state::centronics_busy_w));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	// uart
	I8251(config, m_uart, 0);
	m_uart->txd_handler().set("serial", FUNC(rs232_port_device::write_txd));
	m_uart->rts_handler().set("serial", FUNC(rs232_port_device::write_rts));
	m_uart->dtr_handler().set("serial", FUNC(rs232_port_device::write_dtr));

	clock_device &uart_clock(CLOCK(config, "uart_clock", 150 * 16));
	uart_clock.signal_handler().set(m_uart, FUNC(i8251_device::write_rxc));
	uart_clock.signal_handler().append(m_uart, FUNC(i8251_device::write_txc));

	rs232_port_device &rs232(RS232_PORT(config, "serial", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_uart, FUNC(i8251_device::write_rxd));
	rs232.cts_handler().set(m_uart, FUNC(i8251_device::write_cts));
	rs232.dsr_handler().set(m_uart, FUNC(i8251_device::write_dsr));

	PCCARD_SLOT(config, m_pcmcia, pcmcia_devices, nullptr);
	m_pcmcia->cd1().set(FUNC(nc_state::pcmcia_card_detect_w));
	m_pcmcia->wp().set(FUNC(nc_state::pcmcia_write_protect_w));
	m_pcmcia->bvd1().set(FUNC(nc_state::pcmcia_battery_voltage_1_w));
	m_pcmcia->bvd2().set(FUNC(nc_state::pcmcia_battery_voltage_2_w));
}

void nc100_state::nc100(machine_config &config)
{
	nc_base(config);

	m_maincpu->set_addrmap(AS_IO, &nc100_state::io_map);

	// video hardware
	m_screen->set_size(480, 64);
	m_screen->set_visarea_full();

	// centronics
	m_centronics->ack_handler().set(FUNC(nc100_state::centronics_ack_w));

	// uart
	m_uart->rxrdy_handler().set(FUNC(nc100_state::uart_rxrdy_w));
	m_uart->txrdy_handler().set(FUNC(nc100_state::uart_txrdy_w));

	// rtc
	TC8521(config, "rtc", XTAL(32'768));
}

void nc100_state::nc150(machine_config &config)
{
	nc100(config);

	m_ram->set_default_size("128K");
}

void nc200_state::nc200(machine_config &config)
{
	nc_base(config);

	m_maincpu->set_addrmap(AS_IO, &nc200_state::io_map);

	// ram
	m_ram->set_default_size("128K");

	// video hardware
	m_screen->set_size(480, 128);
	m_screen->set_visarea_full();

	// centronics
	m_centronics->ack_handler().set(FUNC(nc200_state::centronics_ack_w));

	// uart
	m_uart->rxrdy_handler().set(FUNC(nc200_state::uart_rxrdy_w));

	// floppy
	UPD765A(config, m_fdc, 4'000'000, true, true);
	m_fdc->intrq_wr_callback().set(FUNC(nc200_state::fdc_int_w));

	FLOPPY_CONNECTOR(config, "fdc:0", "fdd", FLOPPY_35_DD, true, floppy_image_device::default_pc_floppy_formats);

	// rtc
	MC146818(config, m_rtc, 4.194304_MHz_XTAL);
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( nc100 )
	ROM_REGION(0x40000, "maincpu", 0)
	ROM_DEFAULT_BIOS("106")
	ROM_SYSTEM_BIOS(0, "100", "ROM v1.00")
	ROMX_LOAD("nc100.rom",  0x00000, 0x40000, CRC(a699eca3) SHA1(ce217d5a298b959ccc3d7bc5c93b1dba043f1339), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "106", "ROM v1.06")
	ROMX_LOAD("nc100a.rom", 0x00000, 0x40000, CRC(849884f9) SHA1(ff030dd334ca867d620ee4a94b142ef0d93b69b6), ROM_BIOS(1))
ROM_END

ROM_START( nc100de )
	ROM_REGION(0x40000, "maincpu", 0)
	ROM_LOAD("nc100_de_a1.rom", 0x00000, 0x40000, CRC(bd9ce223) SHA1(2efb26911832bf1456d76d2508e24c0733dc216d))
ROM_END

ROM_START( nc100dk )
	ROM_REGION(0x40000, "maincpu", 0)
	ROM_LOAD("nc100_dk_a1.rom", 0x00000, 0x40000, CRC(ebb54923) SHA1(30321011384c5e10204b9a837430c36fc63580d2))
ROM_END

ROM_START( nc100sv )
	ROM_REGION(0x40000, "maincpu", 0)
	ROM_LOAD("nc100_sv_a1.rom", 0x00000, 0x40000, CRC(86e24cca) SHA1(1ca716c46400e2acfc4665c8e12acc3762f1e401))
ROM_END

ROM_START( dw225 )
	ROM_REGION(0x80000, "maincpu", 0)
	// identical to nc100a.rom with the last half filled with 0x00
	ROM_LOAD("dr,1.06.ic303", 0x00000, 0x80000, CRC(fcf2f7bd) SHA1(a69951618b24e97154cb4284d215cbf4aa9fb34f))
ROM_END

ROM_START( nc150fr )
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_LOAD("nc150_fr_b2.rom", 0x00000, 0x80000, CRC(be442d14) SHA1(f141d409dc72dc1e6662c21a147231c4df3be6b8))
ROM_END

ROM_START( nc150it )
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_LOAD("nc150_it_b1.rom", 0x00000, 0x80000, CRC(1b2fe2fd) SHA1(67eb6bce0b0d4668401d9c8f5a900dc6bd135c21))
ROM_END

ROM_START( nc200 )
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_LOAD("nc200.rom", 0x00000, 0x80000, CRC(bb8180e7) SHA1(fb5c93b0a3e199202c6a12548d2617f7a09bae47))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS        INIT        COMPANY                 FULLNAME           FLAGS
COMP( 1992, nc100,   0,      0,      nc100,   nc100,   nc100_state, empty_init, "Amstrad plc",          "NC100",           MACHINE_SUPPORTS_SAVE )
COMP( 1992, nc100de, nc100,  0,      nc100,   nc100de, nc100_state, empty_init, "Amstrad plc",          "NC100 (Germany)", MACHINE_SUPPORTS_SAVE )
COMP( 1992, nc100dk, nc100,  0,      nc100,   nc100dk, nc100_state, empty_init, "Amstrad plc",          "NC100 (Denmark)", MACHINE_SUPPORTS_SAVE )
COMP( 1992, nc100sv, nc100,  0,      nc100,   nc100sv, nc100_state, empty_init, "Amstrad plc",          "NC100 (Sweden)",  MACHINE_SUPPORTS_SAVE )
COMP( 1992, dw225,   nc100,  0,      nc100,   nc100,   nc100_state, empty_init, "NTS Computer Systems", "DreamWriter 225", MACHINE_SUPPORTS_SAVE )
COMP( 1992, nc150fr, nc100,  0,      nc150,   nc150fr, nc100_state, empty_init, "Amstrad plc",          "NC150 (France)",  MACHINE_SUPPORTS_SAVE )
COMP( 1992, nc150it, nc100,  0,      nc150,   nc150it, nc100_state, empty_init, "Amstrad plc",          "NC150 (Italy)",   MACHINE_SUPPORTS_SAVE )
COMP( 1993, nc200,   0,      0,      nc200,   nc200,   nc200_state, empty_init, "Amstrad plc",          "NC200",           MACHINE_SUPPORTS_SAVE )



ncd68k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/****************************************************************************

    drivers/ncd68k.cpp
    NCD 16" monochrome X terminal
    NCD 17" color X terminal
    NCD 19" monochrome X terminal

    Hardware:
        - MC68020 CPU, optional FPU, no MMU
        - 2681 DUART (Logitech serial mouse)
        - AMD LANCE Ethernet controller
        - Bt478 RAMDAC
        - MC6805 keyboard and NVRAM handler

        68020 IRQs: 1 = software-triggered IRQ, 2 = keyboard, 3 = LANCE, 4 = DUART, 5 = vblank
        68000     : 1 = DUART, 2 = keyboard, 3 = LANCE, 7 = vblank

        6805 port assignments:
        A0 - IN  - keyboard data in
        A1 - OUT - keyboard data out
        A2 - OUT - keyboard clock out
        A3 - OUT - chip select on the 93C46 EEPROM
        A4 - OUT - when clear port B reads as the mailslot from the 68020
        A5 - IN  - set if the 68020 has nothing new for us
        A6 - OUT - rising edge latches port B to the 68020 mailslot and raises the IRQ
        A7 - IN  - set if the 68020 hasn't yet read our last transmission

        B0 - OUT - SK line on 93C46 EEPROM
        B1 - OUT - Data In line on 93C46 EEPROM
        B2 - IN  - Data Out line on 93C46 EEPROM
        B3-B7 unused except '020 mailslot interface

        C0-C3 = speaker (4-bit DAC?)  The 6805 timer is used to control this.

        IRQ in = keyboard clock in

****************************************************************************/

/*
 * WIP status
 *   - ncd16   boots from prom, crc error booting from network
 *   - ncd17c  nvram timeout or checksum failure
 *   - ncd19   loads server from network, then hangs
 *
 * DUART IP2 is set when the 68k mailbox is full (inverse of mcu A5), probably
 * both of these are tied to a latch with a flip-flop
 *
 * TODO
 *   - tidy latches
 *   - fix keyboard
 *   - sound
 */
#include "emu.h"

#include "bert_m.h"

// processors
#include "cpu/m68000/m68000.h"
#include "cpu/m68000/m68020.h"
#include "cpu/m6805/m68705.h"

// devices
#include "machine/ram.h"
#include "machine/mc68681.h"
#include "machine/am79c90.h"
#include "machine/eepromser.h"

// busses and connectors
#include "bus/pc_kbd/pc_kbdc.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/rs232/rs232.h"
#include "bus/rs232/hlemouse.h"

// video and audio
#include "screen.h"
#include "video/bt47x.h"

#define LOG_MCU     (1U << 1)

//#define VERBOSE (LOG_GENERAL|LOG_MCU)
#include "logmacro.h"


namespace {

class ncd68k_state : public driver_device
{
public:
	ncd68k_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mcu(*this, "mcu")
		, m_ram(*this, "ram")
		, m_vram(*this, "vram")
		, m_duart(*this, "duart")
		, m_lance(*this, "lance")
		, m_kbd_con(*this, "kbd")
		, m_serial(*this, "serial%u", 0U)
		, m_eeprom(*this, "eeprom")
		, m_screen(*this, "screen")
	{
	}

protected:
	virtual void machine_reset() override ATTR_COLD;
	void common(machine_config &config);

	u8 mcu_r();
	void mcu_w(u8 data);

	u8 mcu_porta_r();
	u8 mcu_portb_r();
	void mcu_porta_w(u8 data);
	void mcu_portb_w(u8 data);
	void mcu_portc_w(u8 data);

	required_device<m68000_base_device> m_maincpu;
	required_device<m6805_hmos_device> m_mcu;
	required_device<ram_device> m_ram;
	required_device<ram_device> m_vram;
	required_device<scn2681_device> m_duart;
	required_device<am7990_device> m_lance;
	required_device<pc_kbdc_device> m_kbd_con;
	required_device_array<rs232_port_device, 2> m_serial;
	required_device<eeprom_serial_93c46_16bit_device> m_eeprom;
	required_device<screen_device> m_screen;

//private:
	u8 m_portc = 0, m_to_68k = 0, m_from_68k = 0;
	u8 m_porta_in = 0, m_porta_out = 0;
	u8 m_portb_out = 0;
};

class ncd16_state : public ncd68k_state
{
public:
	ncd16_state(machine_config const &mconfig, device_type type, char const *tag)
		: ncd68k_state(mconfig, type, tag)
		, m_bert(*this, "bert")
	{
	}

	void configure(machine_config &config);
	void initialise();

private:
	void map(address_map &map) ATTR_COLD;

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect);

	u16 lance_dma_r(offs_t offset);
	void lance_dma_w(offs_t offset, u16 data, u16 mem_mask = ~0);

	required_device<bert_device> m_bert;
};

class ncd17c_state : public ncd68k_state
{
public:
	ncd17c_state(machine_config const &mconfig, device_type type, char const *tag)
		: ncd68k_state(mconfig, type, tag)
		, m_ramdac(*this, "ramdac")
	{
	}

	void configure(machine_config &config);
	void initialise();

private:
	void map(address_map &map) ATTR_COLD;

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect);

	u8 mcu_status_r();
	void irq_w(u8 data);

	u16 lance_dma_r(offs_t offset);
	void lance_dma_w(offs_t offset, u16 data, u16 mem_mask = ~0);

	required_device<bt478_device> m_ramdac;
};

class ncd19_state : public ncd68k_state
{
public:
	ncd19_state(machine_config const &mconfig, device_type type, char const *tag)
		: ncd68k_state(mconfig, type, tag)
	{
	}

	void configure(machine_config &config);
	void initialise();

private:
	void map(address_map &map) ATTR_COLD;

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect);

	u16 lance_dma_r(offs_t offset);
	void lance_dma_w(offs_t offset, u16 data, u16 mem_mask = ~0);
};

void ncd68k_state::machine_reset()
{
	m_porta_in = m_porta_out = m_portb_out = m_portc = 0;
	m_to_68k = m_from_68k = 0;

	m_porta_in |= 0x20;
}

u32 ncd16_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect)
{
	for (unsigned y = 0; y < 1024; y++)
	{
		u32 *scanline = &bitmap.pix(y);
		for (unsigned x = 0; x < 1024 / 8; x++)
		{
			u8 const pixels = m_vram->read(BYTE_XOR_BE(y * (1024 / 8) + x));

			for (unsigned b = 0; b < 8; b++)
				*scanline++ = BIT(pixels, 7 - b) ? rgb_t::white() : rgb_t::black();
		}
	}

	return 0;
}

u32 ncd17c_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect)
{
	for (unsigned y = 0; y < 768; y++)
	{
		u32 *scanline = &bitmap.pix(y);
		for (unsigned x = 0; x < 1024; x++)
		{
			u8 const pixels = m_vram->read((y * 1024) + BYTE4_XOR_BE(x));
			*scanline++ = m_ramdac->palette_lookup(pixels);
		}
	}

	return 0;
}

u32 ncd19_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect)
{
	for (unsigned y = 0; y < 1024; y++)
	{
		u32 *scanline = &bitmap.pix(y);
		for (unsigned x = 0; x < 1280/8; x++)
		{
			u8 const pixels = m_vram->read((y * (2048/8)) + BYTE4_XOR_BE(x));

			for (unsigned b = 0; b < 8; b++)
				*scanline++ = BIT(pixels, 7 - b) ? rgb_t::white() : rgb_t::black();
		}
	}

	return 0;
}

void ncd16_state::map(address_map &map)
{
	map(0x000000, 0x0bffff).rom().region("maincpu", 0);
	map(0x0c0000, 0x0c0001).rw(FUNC(ncd16_state::mcu_r), FUNC(ncd16_state::mcu_w)).umask16(0x00ff);
	map(0x0e0000, 0x0e003f).rw(m_duart, FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask16(0xff00);
	map(0x100000, 0x100003).rw(m_lance, FUNC(am79c90_device::regs_r), FUNC(am79c90_device::regs_w));
	map(0x800000, 0xffffff).m(m_bert, FUNC(bert_device::map));
}

void ncd17c_state::map(address_map &map)
{
	map(0x00000000, 0x000bffff).rom().region("maincpu", 0);
	map(0x001c0000, 0x001c0003).rw(FUNC(ncd17c_state::mcu_r), FUNC(ncd17c_state::mcu_w)).umask32(0xff000000);
	map(0x001c8000, 0x001c803f).rw(m_duart, FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask32(0xff000000);
	map(0x001d0000, 0x001d0007).m(m_ramdac, FUNC(bt478_device::map));
	map(0x001d8000, 0x001d8003).rw(FUNC(ncd17c_state::mcu_status_r), FUNC(ncd17c_state::irq_w)).umask32(0xff000000);
	//map(0x001e000c, 0x001e000f).r().umask32(0xff000000); // unknown
	map(0x00200000, 0x00200003).rw(m_lance, FUNC(am79c90_device::regs_r), FUNC(am79c90_device::regs_w));
}

void ncd19_state::map(address_map &map)
{
	map(0x00000000, 0x0000ffff).rom().region("maincpu", 0);
	map(0x001c0000, 0x001c0003).rw(FUNC(ncd19_state::mcu_r), FUNC(ncd19_state::mcu_w)).umask32(0xff000000);
	map(0x001e0000, 0x001e003f).rw(m_duart, FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask32(0xff000000);
	map(0x00200000, 0x00200003).rw(m_lance, FUNC(am79c90_device::regs_r), FUNC(am79c90_device::regs_w));
}

u8 ncd68k_state::mcu_porta_r()
{
	return m_porta_in;
}

u8 ncd68k_state::mcu_portb_r()
{
	// read from the mailbox or from the port B latch
	if (!BIT(m_porta_out, 4))
	{
		// FIXME: ncd19 requires this, but ncd17c fails with an nvram timeout
		m_porta_in |= 0x20;
		m_duart->ip2_w(0);

		return m_from_68k;
	}
	else
		return m_eeprom->do_read() ? 0x04 : 0x00;
}

void ncd68k_state::mcu_porta_w(u8 data)
{
	if ((data & 0x40) && !(m_porta_out & 0x40))
	{
		m_to_68k = m_portb_out;
		m_porta_in |= 0x80;

		m_maincpu->set_input_line(M68K_IRQ_2, ASSERT_LINE);
	}

	m_kbd_con->data_write_from_mb(BIT(data, 1));
	m_kbd_con->clock_write_from_mb(BIT(data, 2));
	m_eeprom->cs_write(BIT(data, 3));

	m_porta_out = data;
}

void ncd68k_state::mcu_portb_w(u8 data)
{
	// check if eeprom chip select asserted
	if (BIT(m_porta_out, 3))
	{
		m_eeprom->clk_write(BIT(data, 0));
		m_eeprom->di_write(BIT(data, 1));
	}

	// TODO: what is bit 7 used as an output for?

	m_portb_out = data;
}

void ncd68k_state::mcu_portc_w(u8 data)
{
	m_portc = data;
}

u8 ncd68k_state::mcu_r()
{
	LOGMASKED(LOG_MCU, "mcu_r 0x%02x\n", m_to_68k);

	m_porta_in &= ~0x80;
	m_maincpu->set_input_line(M68K_IRQ_2, CLEAR_LINE);

	return m_to_68k;
}

void ncd68k_state::mcu_w(u8 data)
{
	LOGMASKED(LOG_MCU, "mcu_w 0x%02x (%s)\n", data, machine().describe_context());

	m_from_68k = data;

	m_porta_in &= ~0x20;
	m_duart->ip2_w(1);
}

u8 ncd17c_state::mcu_status_r()
{
	u8 rv = 0;
	if (!(m_porta_in & 0x20))
	{
		rv |= 0x01;
	}
	if (m_porta_in & 0x80)
	{
		rv |= 0x02;
	}

	LOGMASKED(LOG_MCU, "mcu_status_r 0x%02x\n", rv);

	return rv;
}

void ncd17c_state::irq_w(u8 data)
{
	LOGMASKED(LOG_MCU, "irq_w %d (%s)\n", data, machine().describe_context());

	m_maincpu->set_input_line(M68K_IRQ_1, BIT(data, 7));
}

u16 ncd16_state::lance_dma_r(offs_t offset)
{
	if (offset < 0x380000)
		fatalerror("lance_dma_r DMA target %08x not handled", offset);

	offset -= 0x380000;

	u16 const data =
		(u16(m_ram->read(BYTE_XOR_BE(offset + 0))) << 8) | m_ram->read(BYTE_XOR_BE(offset + 1));

	return data;
}

void ncd16_state::lance_dma_w(offs_t offset, u16 data, u16 mem_mask)
{
	if (offset < 0x380000)
		fatalerror("lance_dma_w DMA target %08x not handled", offset);

	offset -= 0x380000;

	if (ACCESSING_BITS_8_15)
		m_ram->write(BYTE_XOR_BE(offset + 0), u8(data >> 8));
	if (ACCESSING_BITS_0_7)
		m_ram->write(BYTE_XOR_BE(offset + 1), u8(data >> 0));
}

u16 ncd17c_state::lance_dma_r(offs_t offset)
{
	u16 const data =
		(u16(m_ram->read(BYTE4_XOR_BE(offset + 0))) << 8) | m_ram->read(BYTE4_XOR_BE(offset + 1));

	return data;
}

void ncd17c_state::lance_dma_w(offs_t offset, u16 data, u16 mem_mask)
{
	if (ACCESSING_BITS_8_15)
		m_ram->write(BYTE4_XOR_BE(offset + 0), u8(data >> 8));
	if (ACCESSING_BITS_0_7)
		m_ram->write(BYTE4_XOR_BE(offset + 1), u8(data >> 0));
}

u16 ncd19_state::lance_dma_r(offs_t offset)
{
	if (offset < 0x800000)
		fatalerror("lance_dma_r DMA target %08x not handled!", offset);

	u16 const data =
		(u16(m_ram->read(BYTE4_XOR_BE(offset + 0))) << 8) | m_ram->read(BYTE4_XOR_BE(offset + 1));

	return data;
}

void ncd19_state::lance_dma_w(offs_t offset, u16 data, u16 mem_mask)
{
	if (offset < 0x800000)
		fatalerror("lance_dma_w DMA target %08x not handled!", offset);

	if (ACCESSING_BITS_8_15)
		m_ram->write(BYTE4_XOR_BE(offset + 0), u8(data >> 8));
	if (ACCESSING_BITS_0_7)
		m_ram->write(BYTE4_XOR_BE(offset + 1), u8(data >> 0));
}

void ncd16_state::initialise()
{
	// map the configured ram and vram
	m_maincpu->space(0).install_ram(0x380000, 0x380000 + m_ram->mask(), m_ram->pointer());
	m_maincpu->space(0).install_ram(0x200000, 0x200000 + m_vram->mask(), m_vram->pointer());
}

void ncd17c_state::initialise()
{
	// map the configured ram and vram
	m_maincpu->space(0).install_ram(0x01000000, 0x01000000 + m_ram->mask(), m_ram->pointer());
	m_maincpu->space(0).install_ram(0x03000000, 0x03000000 + m_vram->mask(), m_vram->pointer());
}

void ncd19_state::initialise()
{
	// map the configured ram and vram
	m_maincpu->space(0).install_ram(0x00800000, 0x00800000 + m_ram->mask(), m_ram->pointer());
	m_maincpu->space(0).install_ram(0x00400000, 0x00400000 + m_vram->mask(), m_vram->pointer());
}

void ncd16_state::configure(machine_config &config)
{
	// basic machine hardware
	M68000(config, m_maincpu, 12.5_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &ncd16_state::map);

	// on-board 512K plus one or two pairs of 256K or 1M SIMMs
	RAM(config, m_ram).set_default_size("4608K");
	m_ram->set_extra_options("512K,1024K,1536K,2560K,3072K");

	RAM(config, m_vram).set_default_size("128K");
	m_vram->set_default_value(0);

	// duart
	SCN2681(config, m_duart, 3.6864_MHz_XTAL);
	m_duart->irq_cb().set_inputline(m_maincpu, M68K_IRQ_4);

	// keyboard controller
	M68705P3(config, m_mcu, 3'750'000);

	// ethernet
	AM7990(config, m_lance);
	m_lance->intr_out().set_inputline(m_maincpu, M68K_IRQ_3).invert();
	m_lance->dma_in().set(FUNC(ncd16_state::lance_dma_r));
	m_lance->dma_out().set(FUNC(ncd16_state::lance_dma_w));

	// 124.652 MHz dot clock generated by DP8530; 82.88 kHz horizontal, 70 Hz vertical
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(124'652'000, 1504, 0, 1024, 1184, 0, 1024);
	m_screen->set_screen_update(FUNC(ncd16_state::screen_update));
	m_screen->screen_vblank().set_inputline(m_maincpu, M68K_IRQ_5, HOLD_LINE);

	BERT(config, m_bert, 0).set_memory(m_maincpu, AS_PROGRAM);

	common(config);

	m_duart->outport_cb().set(
			[this] (u8 data)
			{
				m_serial[0]->write_rts(BIT(data, 0));
				m_serial[1]->write_rts(BIT(data, 1));
				m_serial[0]->write_dtr(BIT(data, 2));
				m_serial[1]->write_dtr(BIT(data, 3));
				m_bert->set_qlc_mode(BIT(data, 5));

				// TODO: bit 4 - usually set
				// TODO: bit 6 - usually set
				// TODO: bit 7 - set/cleared continuously
			});
}

void ncd17c_state::configure(machine_config &config)
{
	// basic machine hardware
	M68020(config, m_maincpu, 20000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &ncd17c_state::map);

	RAM(config, m_ram).set_default_size("32M");
	// TODO: m_ram->set_extra_options("");

	RAM(config, m_vram).set_default_size("1M");

	// duart
	SCN2681(config, m_duart, 77.4144_MHz_XTAL / 21);
	m_duart->irq_cb().set_inputline(m_maincpu, M68K_IRQ_4);

	// keyboard controller
	M6805P2(config, m_mcu, 3'750'000);

	// ethernet
	AM7990(config, m_lance);
	m_lance->intr_out().set_inputline(m_maincpu, M68K_IRQ_3).invert();
	m_lance->dma_in().set(FUNC(ncd17c_state::lance_dma_r));
	m_lance->dma_out().set(FUNC(ncd17c_state::lance_dma_w));

	// 56.260 kHz horizontal, 70.06 Hz vertical
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(77.4144_MHz_XTAL, 1376, 0, 1024, 803, 0, 768);
	m_screen->set_screen_update(FUNC(ncd17c_state::screen_update));
	m_screen->screen_vblank().set_inputline(m_maincpu, M68K_IRQ_5, HOLD_LINE);

	BT478(config, m_ramdac, 77.4144_MHz_XTAL);

	common(config);
}

void ncd19_state::configure(machine_config &config)
{
	// basic machine hardware
	M68020(config, m_maincpu, 15_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &ncd19_state::map);

	RAM(config, m_ram).set_default_size("8M");
	m_ram->set_extra_options("4M");

	RAM(config, m_vram).set_default_size("256K");

	// duart
	SCN2681(config, m_duart, 3.6864_MHz_XTAL);
	m_duart->irq_cb().set_inputline(m_maincpu, M68K_IRQ_4);

	// keyboard controller
	M6805P2(config, m_mcu, 15_MHz_XTAL / 4);

	// ethernet
	AM7990(config, m_lance);
	m_lance->intr_out().set_inputline(m_maincpu, M68K_IRQ_3).invert();
	m_lance->dma_in().set(FUNC(ncd19_state::lance_dma_r));
	m_lance->dma_out().set(FUNC(ncd19_state::lance_dma_w));

	// 128 MHz dot clock generated by DP8530; 74.074 kHz horizontal, 70.1459 Hz vertical
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(16_MHz_XTAL * 8, 1728, 0, 1280, 1056, 0, 1024);
	m_screen->set_screen_update(FUNC(ncd19_state::screen_update));
	m_screen->screen_vblank().set_inputline(m_maincpu, M68K_IRQ_5, HOLD_LINE);

	common(config);
}

void ncd68k_state::common(machine_config &config)
{
	// HACK: this makes the ncd16 and ncd19 keyboard work
	config.set_perfect_quantum(m_mcu);

	// mcu ports
	m_mcu->porta_w().set(FUNC(ncd68k_state::mcu_porta_w));
	m_mcu->portb_w().set(FUNC(ncd68k_state::mcu_portb_w));
	m_mcu->portc_w().set(FUNC(ncd68k_state::mcu_portc_w));

	m_mcu->porta_r().set(FUNC(ncd68k_state::mcu_porta_r));
	m_mcu->portb_r().set(FUNC(ncd68k_state::mcu_portb_r));

	// keyboard connector
	PC_KBDC(config, m_kbd_con, pc_at_keyboards, STR_KBD_MICROSOFT_NATURAL);
	m_kbd_con->out_clock_cb().set_inputline(m_mcu, M6805_IRQ_LINE).invert();
	m_kbd_con->out_data_cb().set(
			[this] (int state)
			{
				if (state)
					m_porta_in |= 0x01;
				else
					m_porta_in &= ~0x01;
			});

	// mouse and auxiliary ports
	RS232_PORT(config, m_serial[0],
			[] (device_slot_interface &device)
			{
				default_rs232_devices(device);
				device.option_add("mouse", LOGITECH_HLE_SERIAL_MOUSE);
			},
			"mouse");
	RS232_PORT(config, m_serial[1], default_rs232_devices, nullptr);

	// duart outputs
	m_duart->a_tx_cb().set(m_serial[0], FUNC(rs232_port_device::write_txd));
	m_duart->b_tx_cb().set(m_serial[1], FUNC(rs232_port_device::write_txd));
	m_duart->outport_cb().set(
			[this] (u8 data)
			{
				m_serial[0]->write_rts(BIT(data, 0));
				m_serial[1]->write_rts(BIT(data, 1));
				m_serial[0]->write_dtr(BIT(data, 2));
				m_serial[1]->write_dtr(BIT(data, 3));

				// TODO: bit 4 - usually set
				// TODO: bit 5 - usually clear
				// TODO: bit 6 - usually set
				// TODO: bit 7 - set/cleared continuously
			});

	// duart inputs
	// FIXME: rts/dsr external loopback test fails - dsr might not be correct?
	m_serial[0]->rxd_handler().set(m_duart, FUNC(scn2681_device::rx_a_w));
	m_serial[0]->cts_handler().set(m_duart, FUNC(scn2681_device::ip0_w));
	m_serial[0]->dsr_handler().set(m_duart, FUNC(scn2681_device::ip3_w));
	m_serial[0]->dcd_handler().set(m_duart, FUNC(scn2681_device::ip4_w));

	m_serial[1]->rxd_handler().set(m_duart, FUNC(scn2681_device::rx_b_w));
	m_serial[1]->cts_handler().set(m_duart, FUNC(scn2681_device::ip1_w));
	m_serial[1]->dsr_handler().set(m_duart, FUNC(scn2681_device::ip5_w));
	m_serial[1]->dcd_handler().set(m_duart, FUNC(scn2681_device::ip6_w));

	// eeprom
	EEPROM_93C46_16BIT(config, m_eeprom);
}

static INPUT_PORTS_START(ncd68k)
INPUT_PORTS_END

ROM_START(ncd16)
	ROM_REGION16_BE(0xc0000, "maincpu", 0)
	ROM_DEFAULT_BIOS("v23l")

	ROM_SYSTEM_BIOS(0, "v210",  "V2.1.0") // Server 2.1.0 03/05/90  Boot Prom V2.1.0
	ROMX_LOAD("v210_b0e.u3",  0x000000, 0x020000, CRC(0e8cec37) SHA1(6561de99d44710e5fd6d969e7e2191456e258df4), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("v210_b0o.u14", 0x000001, 0x020000, CRC(8bdf2843) SHA1(0d907006e9643fb00f36cf14ac24bf11118b62c1), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("v210_b1e.u2",  0x040000, 0x020000, CRC(72c78a46) SHA1(1fd0bc4b7d2dc9d354c6181daf1a7d209172728b), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("v210_b1o.u13", 0x040001, 0x020000, CRC(595fdbf6) SHA1(5c6427db3e47809a1ed20cf5c91b0f60ae3f8803), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("v210_b2e.u1",  0x080000, 0x020000, CRC(6bf7594a) SHA1(4c0a6664cde0617c65074d898ba0bcbfe79e1cf8), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("v210_b2o.u12", 0x080001, 0x020000, CRC(8232774b) SHA1(6ea26e964fcea1c0386f8a02d84a0e9bbdded78f), ROM_SKIP(1) | ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "v23l",  "V2.3.L") // NCD16 Xremote server 2.3.L beta 02/11/91  Boot Monitor V2.2.2
	ROMX_LOAD("ncd_16_xr__v23l_b0e.u3",  0x000000, 0x020000, CRC(8996f939) SHA1(8d6c5649fa927ee16b8632c2f2949997014346d0), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("ncd_16_xr__v23l_b0o.u14", 0x000001, 0x020000, CRC(3143e82c) SHA1(37b917304705a450b3272945d4cff851afb97dbe), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("ncd_16_xr__v23l_b1e.u2",  0x040000, 0x020000, CRC(82712624) SHA1(7e2ff025bf4e9638bdd58b99dc4dabd86c0d21ee), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("ncd_16_xr__v23l_b1o.u13", 0x040001, 0x020000, CRC(432aaa02) SHA1(960291a9b692449e4e787b88528c83a37b3562f2), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("ncd_16_xr__v23l_b2e.u1",  0x080000, 0x020000, CRC(290116df) SHA1(1042764eb5f308046c0f019f31df457d360c9db1), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("ncd_16_xr__v23l_b2o.u12", 0x080001, 0x020000, CRC(2b364281) SHA1(63c2b3093eee6d40f405ead911843ebeb5193b7a), ROM_SKIP(1) | ROM_BIOS(1))

	ROM_REGION16_LE(0x80, "eeprom", 0)
	ROM_LOAD("93c46_0000a76f002b.u17", 0x00, 0x80, CRC(656c8444) SHA1(162a71e2d91a104f4251beb17a4a53f24d1e5e03))

	ROM_REGION(0x800, "mcu", 0)
	ROM_LOAD("4903001_v2.00_key_scan.u10", 0x000, 0x800, CRC(acec6140) SHA1(003a27cb22652d37b3be05d4ad4e924fc6c5c8de))
ROM_END

ROM_START(ncd17c)
	ROM_REGION32_BE(0xc0000, "maincpu", 0)
	ROM_DEFAULT_BIOS("221")
	ROM_SYSTEM_BIOS(0, "210", "Boot Prom v2.1.0")
	ROMX_LOAD("ncd17c_v2.1.0_b0e.u3",  0x000000, 0x008000, CRC(00d11b19) SHA1(1039b8a44c045c51ef70e3494c59be0e9f0a1922), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("ncd17c_v2.1.0_b0o.u14", 0x000001, 0x008000, CRC(1e0a8644) SHA1(cf9661ced32df1285682566041ffaaf794ca005f), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "221", "Boot Prom v2.2.1")
	ROMX_LOAD("ncd17c_v2.2.1_b0e.u3",  0x000000, 0x020000, CRC(c47648af) SHA1(563e17ea8f5c9d418fd81f1e797a226937c0e187), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("ncd17c_v2.2.1_b0o.u14", 0x000001, 0x020000, CRC(b6a8c3ca) SHA1(f02e33d88861ebcb402fb554719c1cb072a5fd14), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("ncd17c_v2.2.1_b1e.u2",  0x040000, 0x020000, CRC(25500987) SHA1(cebdf07c69f1c783a67b92d6efdfdd7067dc910f), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("ncd17c_v2.2.1_b1o.u13", 0x040001, 0x020000, CRC(a5d5ab8a) SHA1(51ddb7020abd5f83224bff48eab254375e9d27f9), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("ncd17c_v2.2.1_b2e.u1",  0x080000, 0x020000, CRC(390dac65) SHA1(3f9c886433dff87847135b8f3d8e8ead75d3abf3), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("ncd17c_v2.2.1_b2o.u12", 0x080001, 0x020000, CRC(2e5ebfaa) SHA1(d222c6cc743046a1c1dec1829c24fa918a54849d), ROM_SKIP(1) | ROM_BIOS(1))

	ROM_REGION16_LE(0x80, "eeprom", 0)
	ROM_LOAD("93c46_0000a7103f0f.u17", 0x00, 0x80, CRC(f7515683) SHA1(3c5796d4fc1e18db8d12b5a4758f083f9dac3f36))

	ROM_REGION(0x800, "mcu", 0)
	ROM_LOAD("ncd4200005.u18", 0x000, 0x800, CRC(075c3746) SHA1(6954cfab5141138df975f1b15d2c8e08d4d203c1))
ROM_END

ROM_START(ncd19)
	ROM_REGION32_BE(0x10000, "maincpu", 0)
	ROM_LOAD16_BYTE("ncd19_v2.1.1_e.u3",  0x000000, 0x008000, CRC(28786528) SHA1(8f4ad6a593c55cce0477169132ecf38577086f4e))
	ROM_LOAD16_BYTE("ncd19_v2.1.1_o.u14", 0x000001, 0x008000, CRC(aeefbcf1) SHA1(0c28426d0ae7c18de02daee7d340c17dc461e7f4))

	ROM_REGION16_LE(0x80, "eeprom", 0)
	ROM_LOAD("93c46_0000a71028bd.u17", 0x00, 0x80, CRC(c2cd34fe) SHA1(77bbbc46c717b15c4a075511e50dfb26cca74029))

	ROM_REGION(0x800, "mcu", 0)
	ROM_LOAD("ncd4200005.u18", 0x000, 0x800, CRC(075c3746) SHA1(6954cfab5141138df975f1b15d2c8e08d4d203c1))
ROM_END

} // anonymous namespace


//   YEAR  NAME    PARENT  COMPAT  MACHINE    INPUT   CLASS         INIT        COMPANY                      FULLNAME   FLAGS
COMP(1989, ncd16,  0,      0,      configure, ncd68k, ncd16_state,  initialise, "Network Computing Devices", "NCD 16",  MACHINE_NOT_WORKING|MACHINE_NO_SOUND)
COMP(1990, ncd17c, 0,      0,      configure, ncd68k, ncd17c_state, initialise, "Network Computing Devices", "NCD 17C", MACHINE_NOT_WORKING|MACHINE_NO_SOUND)
COMP(1990, ncd19,  0,      0,      configure, ncd68k, ncd19_state,  initialise, "Network Computing Devices", "NCD 19",  MACHINE_NOT_WORKING|MACHINE_NO_SOUND)



ncd88k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for MC88100-based NCD X terminals.

****************************************************************************/
/*
 * WIP notes
 *
 * There are two basic machines, NCD88k and Modular Color X (MCX). They share
 * the same firmware and have very similar hardware, however the system memory
 * maps differ. Marketing material for the MCX systems describes 3D and audio
 * hardware as enhancements over the prior NCD88k, and the latter system board
 * includes an additional custom ASIC. Various models of terminal differ only
 * by the type of monitor supplied.
 *
 * 88k "base" (19c, 19g, 17cr, 17g, 19cp)
 *  - no on-board memory
 *  - J6 and J7 are code memory SIMM sockets (up to 64M)
 *  - J8, J9 and J10 are data memory SIMM sockets (up to 96M)
 *  - 19c shipped with 2M code memory (1991)
 *  - 17g 1280x1024 gray-scale (1992)
 *  - 17cr 1280x1024 color (1992)
 *
 * MCX
 *  - 2MB code and 4MB data memory on motherboard
 *  - J10 SIMM slot for code, J11/J12 for data
 *  - Code memory can be added as 256Kx32, 512Kx32, 1Mx32, 2Mx32, 4Mx32,
 *    or 8Mx32 SIMMs. Data memory can be added as 1Mx32, 2Mx32, 4Mx32,
 *    or 8Mx32 SIMMs.
 *  - 256Kx32 and 512Kx32 SIMMs are only supported as code memory
 *  - ports: ethernet (TP & AUI), monitor, aux, mouse, keyboard, audio in, audio out
 *  - 50kHz 16-bit sound
 *
 * Model    Type, Colors/Grayscales           Res.      Mem.  CPU/GCC     List US$
 * -------------------------------------------------------------------------------
 * MCX-L   base only, VGA color, 100dpi       1152x900  6.0   88100-20       2,295
 * MCX14   14" color, 103dpi/70Hz     640x480,1024x768  6.0   88100-20       3,295
 * MCX15   15" color, 100dpi/70Hz     640x480,1152x900  6.0   88100-20       3,495
 * MCX17   17" color, 87dpi/75Hz     1024x768,1152x900  6.0   88100-20       4,295
 * MCX-L19 19" color, 84dpi/72Hz              1152x900  6.0   88100-20       4,695
 *
 * Sources:
 *  - https://web-docs.gsi.de/~kraemer/COLLECTION/ftp.ncd.com/pub/ncd/Archive/NCD-Articles/NCD_X_Terminals/Memory_specs/NCD_88k_family_memory_specs
 *  - http://books.google.com/books?id=ujsEAAAAMBAJ&lpg=PA24&pg=PA24#v=onepage&q&f=false
 *
 * TODO:
 *  - ram control/sizing
 *  - keyboard and mouse
 *  - interrupts and mask
 */

#include "emu.h"
#include "cpu/m88000/m88000.h"
#include "machine/mc68681.h"
#include "machine/am79c90.h"
#include "machine/eepromser.h"
#include "machine/pckeybrd.h"

#include "bus/rs232/rs232.h"

#include "video/bt45x.h"
#include "video/bt47x.h"
#include "screen.h"

#define VERBOSE (0)
#include "logmacro.h"

namespace {

class ncd88k_state : public driver_device
{
public:
	ncd88k_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_ramdac(*this, "ramdac")
		, m_vram(*this, "vram")
	{
	}

	void ncd19c(machine_config &config);

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void code_map(address_map &map) ATTR_COLD;
	void data_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<bt458_device> m_ramdac;

	required_shared_ptr<u32> m_vram;
};

u32 ncd88k_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	u32 *pixel_pointer = m_vram;

	for (int y = screen.visible_area().min_y; y <= screen.visible_area().max_y; y++)
	{
		for (int x = screen.visible_area().min_x; x <= screen.visible_area().max_x; x += 4)
		{
			u32 const pixel_data = *pixel_pointer++;

			bitmap.pix(y, x + 0) = m_ramdac->pen_color(u8(pixel_data >> 24));
			bitmap.pix(y, x + 1) = m_ramdac->pen_color(u8(pixel_data >> 16));
			bitmap.pix(y, x + 2) = m_ramdac->pen_color(u8(pixel_data >> 8));
			bitmap.pix(y, x + 3) = m_ramdac->pen_color(u8(pixel_data >> 0));
		}

		// compensate by 2048 - 1280 pixels per line
		pixel_pointer += 0xc0;
	}

	return 0;
}

void ncd88k_state::code_map(address_map &map)
{
	map(0x00000000, 0x0001cfff).rom().region("prom", 0);
	map(0x04000000, 0x07ffffff).ram().share("cram");
}

void ncd88k_state::data_map(address_map &map)
{
	map(0x00000000, 0x0001cfff).rom().region("prom", 0);
	map(0x01000000, 0x0100003f).rw("duart", FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask32(0xff000000);
	map(0x01400000, 0x0140001f).m(m_ramdac, FUNC(bt458_device::map)).umask32(0xff000000);

	map(0x04000000, 0x07ffffff).ram().share("cram");
	map(0x08000000, 0x0d03ffff).ram().share("dram");

	map(0x0e000000, 0x0e1fffff).ram().share("vram");
}

static INPUT_PORTS_START(ncd19c)
INPUT_PORTS_END

void ncd88k_state::ncd19c(machine_config &config)
{
	MC88100(config, m_maincpu, 15'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &ncd88k_state::code_map);
	m_maincpu->set_addrmap(AS_DATA, &ncd88k_state::data_map);

	SCN2681(config, "duart", 3'686'400);

	BT458(config, m_ramdac, 0);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(125'000'000, 1680, 0, 1280, 1063, 0, 1024); // 74.4 kHz horizontal, 70 Hz vertical
	m_screen->set_screen_update(FUNC(ncd88k_state::screen_update));
}

ROM_START(ncd19c)
	ROM_REGION32_BE(0x20000, "prom", ROMREGION_ERASE00)
	// These dumps have very strange lengths. The actual ROMs should be standard EEPROM types.
	ROM_LOAD16_BYTE("ncd19c-e.rom", 0x0000, 0xb000, CRC(01e31b42) SHA1(28da6e4465415d00a739742ded7937a144129aad) BAD_DUMP)
	ROM_LOAD16_BYTE("ncd19c-o.rom", 0x0001, 0xb000, CRC(dfd9be7c) SHA1(2e99a325b039f8c3bb89833cd1940e6737b64d79) BAD_DUMP)
ROM_END

class ncdmcx_state : public driver_device
{
public:
	ncdmcx_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_eeprom(*this, "eeprom")
		, m_lance(*this, "lance")
		, m_duart(*this, "duart")
		, m_ramdac(*this, "ramdac")
		, m_screen(*this, "screen")
		, m_kbd(*this, "kbd")
		, m_dram(*this, "dram")
		, m_vram(*this, "vram")
	{
	}

	void ncdmcx(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect);

	void code_map(address_map &map) ATTR_COLD;
	void data_map(address_map &map) ATTR_COLD;

	template <unsigned N> void irq_w(int state);

	required_device<cpu_device> m_cpu;
	required_device<eeprom_serial_93cxx_device> m_eeprom;
	required_device<am7990_device> m_lance;
	required_device<scn2681_device> m_duart;
	required_device<bt477_device> m_ramdac;
	required_device<screen_device> m_screen;

	required_device<at_keyboard_device> m_kbd;

	required_shared_ptr<u32> m_dram;
	required_shared_ptr<u32> m_vram;

	u8 m_int;
	u8 m_msk;
};

void ncdmcx_state::machine_reset()
{
	m_int = 0;
	m_msk = 0;

	m_duart->ip2_w(0);
}

u32 ncdmcx_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect)
{
	u32 *pixel_pointer = m_vram;

	for (int y = screen.visible_area().min_y; y <= screen.visible_area().max_y; y++)
	{
		for (int x = screen.visible_area().min_x; x <= screen.visible_area().max_x; x += 4)
		{
			u32 const pixel_data = *pixel_pointer++;

			bitmap.pix(y, x + 0) = m_ramdac->pen_color(u8(pixel_data >> 24));
			bitmap.pix(y, x + 1) = m_ramdac->pen_color(u8(pixel_data >> 16));
			bitmap.pix(y, x + 2) = m_ramdac->pen_color(u8(pixel_data >> 8));
			bitmap.pix(y, x + 3) = m_ramdac->pen_color(u8(pixel_data >> 0));
		}

		// compensate by 2048 - 1280 pixels per line
		pixel_pointer += 0xc0;
	}

	return 0;
}

void ncdmcx_state::ncdmcx(machine_config &config)
{
	MC88100(config, m_cpu, 80_MHz_XTAL / 4);
	m_cpu->set_addrmap(AS_PROGRAM, &ncdmcx_state::code_map);
	m_cpu->set_addrmap(AS_DATA, &ncdmcx_state::data_map);

	EEPROM_93C66_16BIT(config, m_eeprom); // CAT35C104P
	m_eeprom->default_value(0);
	m_eeprom->do_callback().set(m_duart, FUNC(scn2681_device::ip2_w));

	// ICD2061ASC-1

	AM7990(config, m_lance, 20_MHz_XTAL / 2); // 4100004
	m_lance->intr_out().set(FUNC(ncdmcx_state::irq_w<2>)).invert();
	m_lance->dma_in().set([this](offs_t offset) { return util::big_endian_cast<const u16>(m_dram.target())[offset >> 1]; });
	m_lance->dma_out().set([this](offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&util::big_endian_cast<u16>(m_dram.target())[offset >> 1]); });

	SCN2681(config, m_duart, 3'686'400);
	m_duart->irq_cb().set(FUNC(ncdmcx_state::irq_w<3>));
	m_duart->outport_cb().set(
		[this](u8 data)
		{
			m_eeprom->cs_write(BIT(data, 5));
			m_eeprom->di_write(BIT(data, 4));
			m_eeprom->clk_write(BIT(data, 6));
		});

	BT477(config, m_ramdac, 125'000'000); // ATT20C497-11

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(125'000'000, 1680, 0, 1280, 1063, 0, 1024); // 74.4 kHz horizontal, 70 Hz vertical
	m_screen->set_screen_update(FUNC(ncdmcx_state::screen_update));
	m_screen->screen_vblank().set(FUNC(ncdmcx_state::irq_w<4>));

	AT_KEYB(config, m_kbd);
	m_kbd->set_type(at_keyboard_device::KEYBOARD_TYPE::AT, 3);
	m_kbd->keypress().set(FUNC(ncdmcx_state::irq_w<1>));
}

void ncdmcx_state::code_map(address_map &map)
{
	map(0x00000000, 0x0003ffff).rom().region("prom", 0);

	map(0x04000000, 0x07ffffff).ram().share("cram"); // maximum 64M
}

void ncdmcx_state::data_map(address_map &map)
{
	map(0x00000000, 0x0003ffff).rom().region("prom", 0);

	map(0x00c00000, 0x00c00003).rw(m_lance, FUNC(am7990_device::regs_r), FUNC(am7990_device::regs_w)).mirror(0x000fff04);
	map(0x01000000, 0x0100003f).rw(m_duart, FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask32(0xff000000);
	map(0x01400000, 0x0140001f).m(m_ramdac, FUNC(bt477_device::map)).umask32(0xff000000);

	// 014c0000.b - reset?

	// bit 0 -> icd2061a clock
	// bit 1 -> icd2016a data
	map(0x01580000, 0x01580003).nopw();

	map(0x01d80000, 0x01d80000).lrw8(
		[this]()
		{
			return m_int;
		}, "int_r",
		[this](u8 data)
		{
			LOG("msk_w 0x%02x (%s)\n", data, machine().describe_context());

			m_msk = data;
			m_cpu->set_input_line(INPUT_LINE_IRQ0, bool(m_int & m_msk));
		}, "msk_w");

	// 0x64 -> ack or clear irq 4?
	map(0x01d80001, 0x01d80001).lrw8(
		[]()
		{
			return 0;
		}, "int?_r",
		[this](u8 data)
		{
			LOG("ack_w 0x%02x int 0x%02x (%s)\n", data, m_int, machine().describe_context());

			// 0x20?
			// 0x65

			// 0x60 - clr irq 0? (soft interrupt)
			// 0x61 - set irq 0? (soft interrupt)
			// 0x63 - clr irq 6?
			// 0x64 - ack irq 4?
			// 0x65 - 0x0400582c?
			switch (data)
			{
			case 0x60: m_int &= ~0x01; break;
			case 0x61: m_int |= 0x41; break;
			//case 0x62: m_int &= ~0x40; break;
			case 0x63: m_int &= ~0x40; break;
			case 0x64: m_int &= ~0x10; break;
			}

			m_cpu->set_input_line(INPUT_LINE_IRQ0, bool(m_int & m_msk));
		}, "ack_w");

	map(0x01d80003, 0x01d80003).lr8(
		[this]()
		{
			irq_w<1>(0);

			// TODO: this might be keyboard rx?
			return m_kbd->read();
		}, "kbd_r");

	map(0x02000000, 0x02ffffff).lw8(
		[this](offs_t offset, u8 data)
		{
			LOG("dram_ctrl 0x%02x\n", offset >> 16);
		}, "dram_ctrl_w");
	map(0x03000000, 0x03ffffff).lw8(
		[this](offs_t offset, u8 data)
		{
			LOG("cram_ctrl 0x%02x\n", offset >> 16);
		}, "cram_ctrl_w");

	map(0x04000000, 0x07ffffff).ram().share("cram"); // maximum 64M
	map(0x08000000, 0x0dffffff).ram().share("dram"); // maximum 96M

	map(0x0e000000, 0x0e3fffff).ram().share("vram");
}

template <unsigned N> void ncdmcx_state::irq_w(int state)
{
	if (state)
		m_int |= 1U << N;
	else
		m_int &= ~(1U << N);

	m_cpu->set_input_line(INPUT_LINE_IRQ0, bool(m_int & m_msk));
}

ROM_START(ncdmcx)
	ROM_REGION32_BE(0x40000, "prom", 0)
	ROM_SYSTEM_BIOS(0, "v2.7.3", "v2.7.3")
	ROMX_LOAD("ncd88k_mcx_bm__v2.7.3_b0e.u3",  0x0000, 0x20000, CRC(70305680) SHA1(b10b250fe319e823cff28ba7b449b0a40755f5a2), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("ncd88k_mcx_bm__v2.7.3_b0o.u14", 0x0001, 0x20000, CRC(fc066464) SHA1(fa894de56b77bd4bc619040a2cf3a0d260914727), ROM_BIOS(0) | ROM_SKIP(1))

	ROM_SYSTEM_BIOS(1, "v2.6.0", "v2.6.0")
	ROMX_LOAD("ncd88k_mcx_bm__v2.6.0_b0e.u3",  0x0000, 0x20000, CRC(99644196) SHA1(d5091fd4f096000de4970ae778112ff3c01ac340), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("ncd88k_mcx_bm__v2.6.0_b0o.u14", 0x0001, 0x20000, CRC(db2ed336) SHA1(8be4e08bf097d2b85be84da62b2a24c6e55661d9), ROM_BIOS(1) | ROM_SKIP(1))
ROM_END

} // anonymous namespace

COMP(1991, ncd19c, 0, 0, ncd19c, ncd19c, ncd88k_state, empty_init, "Network Computing Devices", "19c", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP(1993, ncdmcx, 0, 0, ncdmcx, 0,      ncdmcx_state, empty_init, "Network Computing Devices", "MCX", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



ncdmips.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/****************************************************************************

    drivers/ncdmips.cpp
    NCD MIPS-based color X terminals

    Hardware:
        - R4600 CPU
        - 2681 DUART (Logitech serial mouse)
        - AM79C950 Ethernet
        - AT&T ATT21C505 "PrecisionDAC" for audio out
        - PS/2 keyboard port

****************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/mips/mips3.h"
#include "machine/mc68681.h"
#include "screen.h"


namespace {

class ncd_mips_state : public driver_device
{
public:
	ncd_mips_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen"),
		m_mainram(*this, "mainram"),
		m_duart(*this, "duart")
	{
	}

	void hmxpro(machine_config &config);
	void hmxpro_map(address_map &map) ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void duart_irq_handler(int state);
	INTERRUPT_GEN_MEMBER(vblank);

private:
	virtual void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	optional_shared_ptr<uint32_t> m_mainram;
	required_device<scn2681_device> m_duart;

	u32 unk_r();
	void tty_w(u32 data);

//  u32 m_palette[256];
//  u8 m_r, m_g, m_b, m_entry, m_stage;
};


void ncd_mips_state::machine_reset()
{
//  m_entry = 0;
//  m_stage = 0;
//  m_r = m_g = m_b = 0;
}

INTERRUPT_GEN_MEMBER(ncd_mips_state::vblank)
{
}


uint32_t ncd_mips_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

u32 ncd_mips_state::unk_r()
{
	return 0xffffffff;
}

void ncd_mips_state::tty_w(u32 data)
{
	printf("%c", (data>>16) & 0x7f);
}

void ncd_mips_state::hmxpro_map(address_map &map)
{
	map(0x00000000, 0x003fffff).ram();  // VRAM
	map(0x10000000, 0x103fffff).ram();
	map(0x18000028, 0x1800002b).r(FUNC(ncd_mips_state::unk_r));
	map(0x18000058, 0x1800005b).w(FUNC(ncd_mips_state::tty_w));
	map(0x19000010, 0x19000013).r(FUNC(ncd_mips_state::unk_r));
	map(0x1b000000, 0x1b00007f).rw(m_duart, FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask32(0xff000000);

	map(0x1fc00000, 0x1fc3ffff).rom().region("maincpu", 0);
	map(0x20000000, 0x207fffff).ram();
}

void ncd_mips_state::duart_irq_handler(int state)
{
	//m_maincpu->set_input_line(M68K_IRQ_6, state);
}

void ncd_mips_state::hmxpro(machine_config &config)
{
	/* basic machine hardware */
	R4600BE(config, m_maincpu, 50000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &ncd_mips_state::hmxpro_map);
	m_maincpu->set_periodic_int(FUNC(ncd_mips_state::vblank), attotime::from_hz(70.06));

	SCN2681(config, m_duart, 3.6864_MHz_XTAL);
	m_duart->irq_cb().set(FUNC(ncd_mips_state::duart_irq_handler));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(77.4144_MHz_XTAL, 1376, 0, 1024, 803, 0, 768);
	m_screen->set_screen_update(FUNC(ncd_mips_state::screen_update));
}

static INPUT_PORTS_START( hmxpro )
INPUT_PORTS_END

/***************************************************************************

  ROM definition(s)

***************************************************************************/

ROM_START( hmxpro )
	ROM_REGION32_BE(0x40000, "maincpu", 0)
	ROM_LOAD16_BYTE( "ncdhmx_bm_v2.7.2_b0e.bin", 0x000000, 0x020000, CRC(66072e5c) SHA1(a12dbd3befda55f755e684ba6e5c3b067f2ded93) )
	ROM_LOAD16_BYTE( "ncdhmx_bm_v2.7.2_b0o.bin", 0x000001, 0x020000, CRC(7f7af795) SHA1(5b31bda8cb42dfb52869d29637fe415e43aa53f4) )
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT               COMPANY                 FULLNAME           FLAGS
COMP( 1994, hmxpro, 0,      0,        hmxpro, hmxpro, ncd_mips_state,  empty_init,   "Network Computing Devices", "NCD HMX PRO", MACHINE_NOT_WORKING|MACHINE_NO_SOUND )



ncdppc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/****************************************************************************

    drivers/ncdppc.cpp
    NCD PowerPC-based color X terminals

    Hardware (Explora Pro):
        - PPC 403GA CPU
        - Custom PCI bridge
        - AM79C965 (? photos are not conclusive) "PCNet" PCI Ethernet
        - S3 86C868 Vision868 PCI graphics
        - National Semiconductor PC87303 PCI "SuperI/O"
        - PS/2 keyboard port

****************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/powerpc/ppc.h"
#include "screen.h"


namespace {

class ncd_ppc_state : public driver_device
{
public:
	ncd_ppc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen"),
		m_mainram(*this, "mainram")
	{
	}

	void explorapro(machine_config &config);
	void explorapro_map(address_map &map) ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

private:
	virtual void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	optional_shared_ptr<uint32_t> m_mainram;

	uint32_t unk_r();
	void tty_w(uint32_t data);
};


void ncd_ppc_state::machine_reset()
{
}

uint32_t ncd_ppc_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

uint32_t ncd_ppc_state::unk_r()
{
	return 0xffffffff;
}

void ncd_ppc_state::tty_w(uint32_t data)
{
	printf("%c", (data>>24) & 0x7f);
}

void ncd_ppc_state::explorapro_map(address_map &map)
{
	map(0x01000000, 0x3fffffff).ram();

	map(0x740002f8, 0x740002fb).w(FUNC(ncd_ppc_state::tty_w));
	map(0x740002fc, 0x740002ff).r(FUNC(ncd_ppc_state::unk_r));
	map(0x7ffc0000, 0x7fffffff).rom().region("maincpu", 0);
}

void ncd_ppc_state::explorapro(machine_config &config)
{
	/* basic machine hardware */
	PPC403GA(config, m_maincpu, 50000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &ncd_ppc_state::explorapro_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(77.4144_MHz_XTAL, 1376, 0, 1024, 803, 0, 768);
	m_screen->set_screen_update(FUNC(ncd_ppc_state::screen_update));
}

static INPUT_PORTS_START( explorapro )
INPUT_PORTS_END

/***************************************************************************

  ROM definition(s)

***************************************************************************/

ROM_START( explorapro )
	ROM_REGION(0x40000, "maincpu", 0)
	ROM_LOAD( "explora__v2.7.6_bm.u37", 0x000000, 0x040000, CRC(038fb1dc) SHA1(036836359f59e70d5bebc50d69083bbe020ddf98) )
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT  COMPAT  MACHINE       INPUT    CLASS          INIT               COMPANY                 FULLNAME           FLAGS
COMP( 1995, explorapro, 0,      0,   explorapro, explorapro, ncd_ppc_state,  empty_init,   "Network Computing Devices", "NCD Explora Pro XQ", MACHINE_NOT_WORKING|MACHINE_NO_SOUND )



nds.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, R. Belmont
/***************************************************************************

  nds.cpp

  Preliminary driver for first-generation Nintendo DS.

  Tech info: http://problemkaputt.de/gbatek.htm

  Notes:
    Timers and DMAs 0-3 are ARM9's, 4-7 are ARM7's.
    Interrupt registers [0] is ARM9, [1] is ARM7.

***************************************************************************/

#include "emu.h"
#include "nds.h"

#include <cstdarg>

#define VERBOSE_LEVEL   (0)

// Measured value from GBATEK.  Actual crystal unknown.
#define MASTER_CLOCK (33513982)

#define INT_VBL                 0x00000001
#define INT_HBL                 0x00000002
#define INT_VCNT                0x00000004
#define INT_TM0_OVERFLOW        0x00000008
#define INT_TM1_OVERFLOW        0x00000010
#define INT_TM2_OVERFLOW        0x00000020
#define INT_TM3_OVERFLOW        0x00000040
#define INT_SIO                 0x00000080  // also RCNT/RTC (arm7 only)
#define INT_DMA0                0x00000100
#define INT_DMA1                0x00000200
#define INT_DMA2                0x00000400
#define INT_DMA3                0x00000800
#define INT_KEYPAD              0x00001000
#define INT_GAMEPAK             0x00002000  // GBA slot IRQ line (never used?)
#define INT_NA1                 0x00004000  // unused
#define INT_NA2                 0x00008000  // unused
#define INT_IPCSYNC             0x00010000
#define INT_IPCSENDEMPTY        0x00020000
#define INT_IPCRECVNOTEMPTY     0x00040000
#define INT_CARDXFERCOMPLETE    0x00080000
#define INT_CARDIREQ            0x00100000
#define INT_GEOCMDFIFO          0x00200000  // arm9 only
#define INT_SCREENUNFOLD        0x00400000  // arm7 only
#define INT_SPIBUS              0x00800000  // arm7 only
#define INT_WIFI                0x01000000  // arm7 only - also DSP on DSi
#define INT_CAMERA              0x02000000  // DSi only
#define INT_NA3                 0x04000000
#define INT_NA4                 0x08000000
#define INT_NEWDMA0             0x10000000  // DSi only
#define INT_NEWDMA1             0x20000000  // DSi only
#define INT_NEWDMA2             0x40000000  // DSi only
#define INT_NEWDMA3             0x80000000  // DSi only

static const uint32_t timer_clks[4] = { MASTER_CLOCK, MASTER_CLOCK / 64, MASTER_CLOCK / 256, MASTER_CLOCK / 1024 };

static inline void ATTR_PRINTF(3,4) verboselog(device_t &device, int n_level, const char *s_fmt, ...)
{
	if( VERBOSE_LEVEL >= n_level )
	{
		va_list v;
		char buf[ 32768 ];
		va_start( v, s_fmt );
		vsprintf( buf, s_fmt, v );
		va_end( v );
		device.logerror( "%08x: %s", device.machine().describe_context(), buf );
	}
}

uint32_t nds_state::arm7_io_r(offs_t offset, uint32_t mem_mask)
{
	uint8_t temp1, temp2;
	switch(offset)
	{
		case TIMER_OFFSET:
		case TIMER_OFFSET+1:
		case TIMER_OFFSET+2:
		case TIMER_OFFSET+3:
			{
				uint32_t elapsed;
				double time, ticks;
				int timer = (offset - TIMER_OFFSET) + 4;

				printf("Read timer reg %x (PC=%x)\n", timer, m_arm7->pc());

				// update times for
				if (m_timer_regs[timer] & 0x800000)
				{
					if (m_timer_regs[timer] & 0x00040000)
					{
						elapsed = m_timer_regs[timer] & 0xffff;
					}
					else
					{
						time = 0.1; //m_tmr_timer[timer]->elapsed().as_double();

						ticks = (double)(0x10000 - (m_timer_regs[timer] & 0xffff));

	//                  printf("time %f ticks %f 1/hz %f\n", time, ticks, 1.0 / m_timer_hz[timer]);

						time *= ticks;
						time /= (1.0 / m_timer_hz[timer]);

						elapsed = (uint32_t)time;
					}

//                  printf("elapsed = %x\n", elapsed);
				}
				else
				{
//                  printf("Reading inactive timer!\n");
					elapsed = 0;
				}

				return (m_timer_regs[timer] & 0xffff0000) | (elapsed & 0xffff);
			}
			break;

		case IME_OFFSET:
			return m_ime[1];

		case IE_OFFSET:
			return m_ie[1];

		case IF_OFFSET:
			return m_if[1];

		case IPCSYNC_OFFSET:
			return m_arm7_ipcsync;

		case AUX_SPI_CNT_OFFSET:
			printf("arm7: read AUX_SPI_CNT mask %08x\n", mem_mask);
			return 0;
			break;

		case GAMECARD_BUS_CTRL_OFFSET:
			//printf("arm7: read GAMECARD_BUS_CTRL (%08x) mask %08x\n", m_gamecard_ctrl, mem_mask);
			return m_gamecard_ctrl;
			break;

		case GAMECARD_DATA_OFFSET:
			printf("arm7: read to GAMECARD_DATA mask %08x\n", mem_mask);
			return 0xffffffff;
			break;

		case GAMECARD_DATA_2_OFFSET:
			printf("arm7: read to GAMECARD_DATA2 mask %08x\n", mem_mask);
			return 0xffffffff;
			break;

		case GAMECARD_DATA_IN_OFFSET:
			//printf("arm7: read to GAMECARD_DATA_IN mask %08x (len = %x)\n", mem_mask, m_cartdata_len);
			if (m_cartdata_len >= 4)
			{
				m_cartdata_len -= 4;
			}
			else
			{
				m_cartdata_len = 0;
			}

			if (m_cartdata_len == 0)
			{
				printf("NDS: xfer over\n");
				m_gamecard_ctrl &= ~GAMECARD_DATA_READY;
				m_gamecard_ctrl &= ~GAMECARD_BLOCK_BUSY;
			}
			return 0xffffffff;
			break;

		case SPI_CTRL_OFFSET:
			//printf("arm7: read SPI_CTRL mask %08x\n", mem_mask);
			return 0;
			break;

		case POSTFLG_OFFSET:
			/* Bit   Use
			*  0     0=Booting, 1=Booted (set by BIOS/firmware)
			*/
			return m_arm7_postflg;

		case WRAMSTAT_OFFSET:
			temp1 = (((m_vramcntc & 3) == 2) && (m_vramcntc & 0x80)) ? 1 : 0;
			temp2 = (((m_vramcntd & 3) == 2) && (m_vramcntd & 0x80)) ? 2 : 0;
			return (m_wramcnt << 8) | temp1 | temp2;

		default:
			verboselog(*this, 0, "[ARM7] [IO] Unknown read: %08x (%08x)\n", offset*4, mem_mask);
			break;
	}

	return 0;
}

void nds_state::arm7_io_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	switch(offset)
	{
		case TIMER_OFFSET:
		case TIMER_OFFSET+1:
		case TIMER_OFFSET+2:
		case TIMER_OFFSET+3:
			{
				double rate, clocksel;
				uint32_t old_timer_regs;

				int timer = (offset - TIMER_OFFSET)+4;

				old_timer_regs = m_timer_regs[timer];

				m_timer_regs[timer] = (m_timer_regs[timer] & ~(mem_mask & 0xFFFF0000)) | (data & (mem_mask & 0xFFFF0000));

				printf("%08x to timer %d (mask %08x PC %x)\n", data, timer, ~mem_mask, m_arm7->pc());

				if (ACCESSING_BITS_0_15)
				{
					m_timer_reload[timer] = ((m_timer_reload[timer] & ~mem_mask) | (data & mem_mask)) & 0x0000FFFF;
					m_timer_recalc[timer] = 1;
				}

				// enabling this timer?
				if ((ACCESSING_BITS_16_31) && (data & 0x800000))
				{
					double final;

					if ((old_timer_regs & 0x00800000) == 0) // start bit 0 -> 1
					{
						m_timer_regs[timer] = (m_timer_regs[timer] & 0xFFFF0000) | (m_timer_reload[timer] & 0x0000FFFF);
					}

					rate = 0x10000 - (m_timer_regs[timer] & 0xffff);

					clocksel = timer_clks[(m_timer_regs[timer] >> 16) & 3];

					final = clocksel / rate;

					m_timer_hz[timer] = final;

					m_timer_recalc[timer] = 0;

					printf("Enabling timer %d @ %f Hz regs %08x\n", timer, final, m_timer_regs[timer]);

					// enable the timer
					if( !(data & 0x40000) ) // if we're not in Count-Up mode
					{
						attotime time = attotime::from_hz(final);
						m_tmr_timer[timer]->adjust(time, timer, time);
					}
				}
			}
			break;

		case IME_OFFSET:
			printf("ARM7: %08x to IME\n", data);
			COMBINE_DATA(&m_ime[1]);
			break;

		case IE_OFFSET:
			printf("ARM7: %08x to IE\n", data);
			COMBINE_DATA(&m_ie[1]);
			break;

		case IF_OFFSET:
			COMBINE_DATA(&m_if[1]);
			break;

		case IPCSYNC_OFFSET:
			//printf("ARM7: %x to IPCSYNC\n", data);
			m_arm9_ipcsync &= ~0xf;
			m_arm9_ipcsync |= ((data >> 8) & 0xf);
			m_arm7_ipcsync &= 0xf;
			m_arm7_ipcsync |= (data & ~0xf);
			break;

		case AUX_SPI_CNT_OFFSET:
			//printf("arm7: %08x to AUX_SPI_CNT mask %08x\n", data, mem_mask);
			m_spicnt &= 0x0080;
			m_spicnt |= (data & 0xe043);

			break;

		case GAMECARD_BUS_CTRL_OFFSET:
			//printf("arm7: %08x to GAMECARD_BUS_CTRL mask %08x\n", data, mem_mask);
			m_gamecard_ctrl &= GAMECARD_DATA_READY;
			m_gamecard_ctrl |= (data & ~GAMECARD_DATA_READY);

			if (!(m_spicnt & (1<<15)))
			{
				return;
			}

			if (!(m_gamecard_ctrl & GAMECARD_BLOCK_BUSY))
			{
				return;
			}

			m_cartdata_len = (m_gamecard_ctrl >> 24) & 7;
			if (m_cartdata_len == 7)
			{
				m_cartdata_len = 4;
			}
			else if (m_cartdata_len != 0)
			{
				m_cartdata_len = 256 << m_cartdata_len;
			}
			printf("nds: cartdata for transfer = %x\n", m_cartdata_len);

			if (m_cartdata_len > 0)
			{
				m_gamecard_ctrl |= GAMECARD_DATA_READY;
			}
			else
			{
				printf("NDS: xfer over\n");
				m_gamecard_ctrl &= ~GAMECARD_DATA_READY;
				m_gamecard_ctrl &= ~GAMECARD_BLOCK_BUSY;
			}
			break;

		case GAMECARD_DATA_OFFSET:
			//printf("arm7: %08x to GAMECARD_DATA mask %08x\n", data, mem_mask);
			break;

		case GAMECARD_DATA_2_OFFSET:
			//printf("arm7: %08x to GAMECARD_DATA2 mask %08x\n", data, mem_mask);
			break;

		case SPI_CTRL_OFFSET:
			//printf("arm7: %08x to SPI_CTRL mask %08x\n", data, mem_mask);
			break;

		case POSTFLG_OFFSET:
			/* Bit   Use
			*  0     0=Booting, 1=Booted (set by BIOS/firmware)
			*/
			if (!(m_arm7_postflg & POSTFLG_PBF_MASK) && m_arm7->pc() < 0x4000)
			{
				m_arm7_postflg &= ~POSTFLG_PBF_MASK;
				m_arm7_postflg |= data & POSTFLG_PBF_MASK;
			}

			if (ACCESSING_BITS_8_15)
			{
				if ((data>>8) & 0x80)
				{
					printf("arm7: HALT\n"); // halts the arm7 until an interrupt occurs
					m_arm7->suspend(SUSPEND_REASON_HALT, 1);
					m_arm7halted = true;
				}
			}
			break;
		default:
			verboselog(*this, 0, "[ARM7] [IO] Unknown write: %08x = %08x (%08x)\n", offset*4, data, mem_mask);
			break;
	}
}

uint32_t nds_state::arm9_io_r(offs_t offset, uint32_t mem_mask)
{
	switch(offset)
	{
		case TIMER_OFFSET:
		case TIMER_OFFSET+1:
		case TIMER_OFFSET+2:
		case TIMER_OFFSET+3:
			{
				uint32_t elapsed;
				double time, ticks;
				int timer = (offset - TIMER_OFFSET);

				//printf("Read timer reg %x (PC=%x)\n", timer, m_arm9->pc());

				// update times for
				if (m_timer_regs[timer] & 0x800000)
				{
					if (m_timer_regs[timer] & 0x00040000)
					{
						elapsed = m_timer_regs[timer] & 0xffff;
					}
					else
					{
						time = 0.1; //m_tmr_timer[timer]->elapsed().as_double();

						ticks = (double)(0x10000 - (m_timer_regs[timer] & 0xffff));

	//                  printf("time %f ticks %f 1/hz %f\n", time, ticks, 1.0 / m_timer_hz[timer]);

						time *= ticks;
						time /= (1.0 / m_timer_hz[timer]);

						elapsed = (uint32_t)time;
					}

//                  printf("elapsed = %x\n", elapsed);
				}
				else
				{
//                  printf("Reading inactive timer!\n");
					elapsed = 0;
				}

				return (m_timer_regs[timer] & 0xffff0000) | (elapsed & 0xffff);
			}
			break;

		case IME_OFFSET:
			return m_ime[0];

		case IE_OFFSET:
			return m_ie[0];

		case IF_OFFSET:
			return m_if[0];

		case IPCSYNC_OFFSET:
			return m_arm9_ipcsync;

		case POSTFLG_OFFSET:
			/* Bit   Use
			*  0     0=Booting, 1=Booted (set by BIOS/firmware)
			*  1     RAM
			*/
			return m_arm9_postflg;
		default:
			verboselog(*this, 0, "[ARM9] [IO] Unknown read: %08x (%08x)\n", offset*4, mem_mask);
			break;
	}

	return 0;
}

void nds_state::arm9_io_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	switch(offset)
	{
		case TIMER_OFFSET:
		case TIMER_OFFSET+1:
		case TIMER_OFFSET+2:
		case TIMER_OFFSET+3:
			{
				double rate, clocksel;
				uint32_t old_timer_regs;

				int timer = (offset - TIMER_OFFSET)+4;

				old_timer_regs = m_timer_regs[timer];

				m_timer_regs[timer] = (m_timer_regs[timer] & ~(mem_mask & 0xFFFF0000)) | (data & (mem_mask & 0xFFFF0000));

				printf("%x to timer %d (mask %x PC %x)\n", data, timer, ~mem_mask, m_arm9->pc());

				if (ACCESSING_BITS_0_15)
				{
					m_timer_reload[timer] = ((m_timer_reload[timer] & ~mem_mask) | (data & mem_mask)) & 0x0000FFFF;
					m_timer_recalc[timer] = 1;
				}

				// enabling this timer?
				if ((ACCESSING_BITS_16_31) && (data & 0x800000))
				{
					double final;

					if ((old_timer_regs & 0x00800000) == 0) // start bit 0 -> 1
					{
						m_timer_regs[timer] = (m_timer_regs[timer] & 0xFFFF0000) | (m_timer_reload[timer] & 0x0000FFFF);
					}

					rate = 0x10000 - (m_timer_regs[timer] & 0xffff);

					clocksel = timer_clks[(m_timer_regs[timer] >> 16) & 3];

					final = clocksel / rate;

					m_timer_hz[timer] = final;

					m_timer_recalc[timer] = 0;

					printf("Enabling timer %d @ %f Hz\n", timer, final);

					// enable the timer
					if( !(data & 0x40000) ) // if we're not in Count-Up mode
					{
						attotime time = attotime::from_hz(final);
						m_tmr_timer[timer]->adjust(time, timer, time);
					}
				}
			}
			break;

		case IME_OFFSET:
			printf("ARM9: %08x to IME\n", data);
			COMBINE_DATA(&m_ime[0]);
			break;

		case IE_OFFSET:
			printf("ARM9: %08x to IE\n", data);
			COMBINE_DATA(&m_ie[0]);
			break;

		case IF_OFFSET:
			COMBINE_DATA(&m_if[0]);
			break;

		case IPCSYNC_OFFSET:
			printf("ARM9: %x to IPCSYNC\n", data);
			m_arm7_ipcsync &= ~0xf;
			m_arm7_ipcsync |= ((data >> 8) & 0xf);
			m_arm9_ipcsync &= 0xf;
			m_arm9_ipcsync |= (data & ~0xf);
			break;

		case VRAMCNT_A_OFFSET:
			if (ACCESSING_BITS_0_7) // VRAMCNT_A
			{
				m_vramcnta = data & 0xff;
			}
			if (ACCESSING_BITS_8_15) // VRAMCNT_B
			{
				m_vramcntb = (data >> 8) & 0xff;
			}
			if (ACCESSING_BITS_16_23) // VRAMCNT_C
			{
				m_vramcntc = (data >> 16) & 0xff;
			}
			if (ACCESSING_BITS_24_31) // VRAMCNT_D
			{
				m_vramcntd = (data >> 24) & 0xff;
			}
			break;

		case WRAMCNT_OFFSET:
			if (ACCESSING_BITS_0_7) // VRAMCNT_E
			{
				m_vramcnte = data & 0xff;
			}
			if (ACCESSING_BITS_8_15) // VRAMCNT_F
			{
				m_vramcntf = (data >> 8) & 0xff;
			}
			if (ACCESSING_BITS_16_23) // VRAMCNT_G
			{
				m_vramcntg = (data >> 16) & 0xff;
			}
			if (ACCESSING_BITS_24_31) // WRAMCNT
			{
				m_wramcnt = (data>>24) & 0x3;
				m_arm7wrambnk->set_bank(m_wramcnt);
				m_arm9wrambnk->set_bank(m_wramcnt);
			}
			break;

		case VRAMCNT_H_OFFSET:
			if (ACCESSING_BITS_0_7) // VRAMCNT_H
			{
				m_vramcnth = data & 0xff;
			}
			if (ACCESSING_BITS_8_15) // VRAMCNT_I
			{
				m_vramcnti = (data >> 8) & 0xff;
			}
			break;

		case POSTFLG_OFFSET:
			/* Bit   Use
			*  0     0=Booting, 1=Booted (set by BIOS/firmware)
			*  1     RAM
			*/
			if (!(m_arm9_postflg & POSTFLG_PBF_MASK))
			{
				m_arm9_postflg &= ~POSTFLG_PBF_MASK;
				m_arm9_postflg |= data & POSTFLG_PBF_MASK;
			}
			m_arm9_postflg &= ~POSTFLG_RAM_MASK;
			m_arm9_postflg |= data & POSTFLG_RAM_MASK;
			break;
		default:
			verboselog(*this, 0, "[ARM7] [IO] Unknown write: %08x = %08x (%08x)\n", offset*4, data, mem_mask);
			break;
	}
}

void nds_state::nds_arm7_map(address_map &map)
{
	map(0x00000000, 0x00003fff).rom().region("arm7", 0);
	map(0x02000000, 0x023fffff).ram().mirror(0x00400000).share("mainram");
	map(0x03000000, 0x03007fff).mirror(0x007f8000).m(m_arm7wrambnk, FUNC(address_map_bank_device::amap32));
	map(0x03800000, 0x0380ffff).ram().mirror(0x007f0000).share("arm7ram");
	map(0x04000000, 0x0410ffff).rw(FUNC(nds_state::arm7_io_r), FUNC(nds_state::arm7_io_w));
}

void nds_state::nds_arm9_map(address_map &map)
{
	map(0x02000000, 0x023fffff).ram().mirror(0x00400000).share("mainram");
	map(0x03000000, 0x03007fff).mirror(0x00ff8000).m("nds9wram", FUNC(address_map_bank_device::amap32));
	map(0x04000000, 0x0410ffff).rw(FUNC(nds_state::arm9_io_r), FUNC(nds_state::arm9_io_w));
	map(0xffff0000, 0xffff0fff).rom().mirror(0x1000).region("arm9", 0);
}

// ARM7 views of WRAM
void nds_state::nds7_wram_map(address_map &map)
{
	map(0x00000, 0x07fff).rw(FUNC(nds_state::wram_arm7mirror_r), FUNC(nds_state::wram_arm7mirror_w));
	map(0x08000, 0x0bfff).rw(FUNC(nds_state::wram_first_half_r), FUNC(nds_state::wram_first_half_w));
	map(0x0c000, 0x0ffff).rw(FUNC(nds_state::wram_first_half_r), FUNC(nds_state::wram_first_half_w));
	map(0x10000, 0x13fff).rw(FUNC(nds_state::wram_second_half_r), FUNC(nds_state::wram_second_half_w));
	map(0x14000, 0x17fff).rw(FUNC(nds_state::wram_second_half_r), FUNC(nds_state::wram_second_half_w));
	map(0x18000, 0x1ffff).rw(FUNC(nds_state::wram_first_half_r), FUNC(nds_state::wram_first_half_w));
}

// ARM9 views of WRAM
void nds_state::nds9_wram_map(address_map &map)
{
	map(0x00000, 0x07fff).rw(FUNC(nds_state::wram_first_half_r), FUNC(nds_state::wram_first_half_w));
	map(0x08000, 0x0bfff).rw(FUNC(nds_state::wram_second_half_r), FUNC(nds_state::wram_second_half_w));
	map(0x0c000, 0x0ffff).rw(FUNC(nds_state::wram_second_half_r), FUNC(nds_state::wram_second_half_w));
	map(0x10000, 0x13fff).rw(FUNC(nds_state::wram_first_half_r), FUNC(nds_state::wram_first_half_w));
	map(0x14000, 0x17fff).rw(FUNC(nds_state::wram_first_half_r), FUNC(nds_state::wram_first_half_w));
	map(0x18000, 0x1ffff).noprw().nopw();       // probably actually open bus?  GBATEK describes as "random"
}

uint32_t nds_state::wram_first_half_r(offs_t offset) { return m_WRAM[offset]; }
uint32_t nds_state::wram_second_half_r(offs_t offset) { return m_WRAM[offset+0x4000]; }
void nds_state::wram_first_half_w(offs_t offset, uint32_t data, uint32_t mem_mask) { COMBINE_DATA(&m_WRAM[offset]); }
void nds_state::wram_second_half_w(offs_t offset, uint32_t data, uint32_t mem_mask) { COMBINE_DATA(&m_WRAM[offset+0x4000]); }
uint32_t nds_state::wram_arm7mirror_r(offs_t offset) { return m_arm7ram[offset]; }
void nds_state::wram_arm7mirror_w(offs_t offset, uint32_t data, uint32_t mem_mask) { COMBINE_DATA(&m_arm7ram[offset]); }

static INPUT_PORTS_START( nds )
INPUT_PORTS_END

void nds_state::machine_reset()
{
	m_arm7_postflg = 0;
	m_arm9_postflg = 0;
	m_wramcnt = 0;
	m_arm7wrambnk->set_bank(0);
	m_arm9wrambnk->set_bank(0);
	m_arm7halted = false;
}

void nds_state::machine_start()
{
	int i;

	for (i = 0; i < 8; i++)
	{
		m_dma_timer[i] = timer_alloc(FUNC(nds_state::dma_complete), this);
		m_dma_timer[i]->adjust(attotime::never, i);

		m_tmr_timer[i] = timer_alloc(FUNC(nds_state::timer_expire), this);
		m_tmr_timer[i]->adjust(attotime::never, i);
	}

	m_irq_timer = timer_alloc(FUNC(nds_state::handle_irq), this);
	m_irq_timer->adjust(attotime::never);
}

TIMER_CALLBACK_MEMBER(nds_state::dma_complete)
{
	#if 0
	static const uint32_t ch_int[8] = { INT_DMA0, INT_DMA1, INT_DMA2, INT_DMA3, INT_DMA0, INT_DMA1, INT_DMA2, INT_DMA3 };

	uintptr_t ch = param;

//  printf("dma complete: ch %d\n", ch);

	m_dma_timer[ch]->adjust(attotime::never);

	int ctrl = DMACNT_H(ch);

	// IRQ
	if (ctrl & 0x4000)
	{
		request_irq(ch_int[ch]);
	}

	// if we're supposed to repeat, don't clear "active" and then the next vbl/hbl will retrigger us
	// always clear active for immediate DMAs though
	if (!((ctrl>>9) & 1) || ((ctrl & 0x3000) == 0))
	{
		DMACNT_H_RESET(ch, 0x8000); // clear "active" bit
	}
	else
	{
		// if repeat, reload the count
		if ((ctrl>>9) & 1)
		{
			m_dma_cnt[ch] = DMACNT_L(ch);

			// if increment & reload mode, reload the destination
			if (((ctrl>>5)&3) == 3)
			{
				m_dma_dst[ch] = DMADAD(ch);
			}
		}
	}
	#endif
}

void nds_state::dma_exec(int ch)
{
	#if 0
	address_space &space;
	uint32_t src = m_dma_src[ch];
	uint32_t dst = m_dma_dst[ch];
	uint16_t ctrl = DMACNT_H(ch);
	int srcadd = (ctrl >> 7) & 3;
	int dstadd = (ctrl >> 5) & 3;

	if (ch > 4)
	{
		space = m_arm7->space(AS_PROGRAM);
	}
	else
	{
		space = m_arm9->space(AS_PROGRAM);
	}

	int cnt = m_dma_cnt[ch];
	if (cnt == 0)
	{
		if (ch == 3)
			cnt = 0x10000;
		else
			cnt = 0x4000;
	}

//  if (dst >= 0x6000000 && dst <= 0x6017fff)
//  printf("DMA exec: ch %d from %08x to %08x, mode %04x, count %04x (%s)\n", (int)ch, src, dst, ctrl, cnt, ((ctrl>>10) & 1) ? "32" : "16");

	for (int i = 0; i < cnt; i++)
	{
		if ((ctrl>>10) & 1)
		{
			src &= 0xfffffffc;
			dst &= 0xfffffffc;

			// 32-bit
			space.write_dword(dst, space.read_dword(src));
			switch (dstadd)
			{
				case 0: // increment
					dst += 4;
					break;
				case 1: // decrement
					dst -= 4;
					break;
				case 2: // don't move
					break;
				case 3: // increment and reload
					dst += 4;
					break;
			}
			switch (srcadd)
			{
				case 0: // increment
					src += 4;
					break;
				case 1: // decrement
					src -= 4;
					break;
				case 2: // don't move
					break;
				case 3: // not used ("Metal Max 2 Kai" expects no increment/decrement)
					break;
			}
		}
		else
		{
			src &= 0xfffffffe;
			dst &= 0xfffffffe;

			// 16-bit
			space.write_word(dst, space.read_word(src));
			switch (dstadd)
			{
				case 0: // increment
					dst += 2;
					break;
				case 1: // decrement
					dst -= 2;
					break;
				case 2: // don't move
					break;
				case 3: // increment and reload
					dst += 2;
					break;
			}
			switch (srcadd)
			{
				case 0: // increment
					src += 2;
					break;
				case 1: // decrement
					src -= 2;
					break;
				case 2: // don't move
					break;
				case 3: // not used (see note in 32-bit version above)
					break;
			}
		}
	}

	m_dma_src[ch] = src;
	m_dma_dst[ch] = dst;
#endif
//  printf("settng DMA timer %d for %d cycs (tmr %x)\n", ch, cnt, (uint32_t)m_dma_timer[ch]);
//  m_dma_timer[ch]->adjust(ATTOTIME_IN_CYCLES(0, cnt), ch);
	dma_complete(ch);
}

TIMER_CALLBACK_MEMBER(nds_state::handle_irq)
{
	request_irq(0, m_if[0]);
	request_irq(1, m_if[1]);

	m_irq_timer->adjust(attotime::never);
}

void nds_state::request_irq(int cpu, uint32_t int_type)
{
	// set flag for later recovery
	m_if[cpu] |= int_type;

	printf("request IRQ %08x on CPU %d\n", int_type, cpu);

	// is this specific interrupt enabled?
	int_type &= m_ie[cpu];
	if (int_type != 0)
	{
		// master enable?
		if (m_ime[cpu] & 1)
		{
			if (cpu == 0)
			{
				m_arm9->set_input_line(arm7_cpu_device::ARM7_IRQ_LINE, ASSERT_LINE);
				m_arm9->set_input_line(arm7_cpu_device::ARM7_IRQ_LINE, CLEAR_LINE);
			}
			else
			{
				if (m_arm7halted)
				{
					printf("ARM7 unhalting\n");
					m_arm7->resume(SUSPEND_REASON_HALT);
					m_arm7halted = false;
				}

				m_arm7->set_input_line(arm7_cpu_device::ARM7_IRQ_LINE, ASSERT_LINE);
				m_arm7->set_input_line(arm7_cpu_device::ARM7_IRQ_LINE, CLEAR_LINE);
			}
		}
	}
}

TIMER_CALLBACK_MEMBER(nds_state::timer_expire)
{
	static const uint32_t tmr_ints[8] = { INT_TM0_OVERFLOW, INT_TM1_OVERFLOW, INT_TM2_OVERFLOW, INT_TM3_OVERFLOW };
	uintptr_t tmr = (uintptr_t) param;
	int cpu = (tmr > 4) ? 1 : 0;

	verboselog(*this, 1, "Timer %d expired\n", (int)tmr);

	// "The reload value is copied into the counter only upon following two situations: Automatically upon timer overflows,"
	// "or when the timer start bit becomes changed from 0 to 1."
	if (m_timer_recalc[tmr] != 0)
	{
		double rate, clocksel, final;
		attotime time;
		m_timer_recalc[tmr] = 0;
		m_timer_regs[tmr] = (m_timer_regs[tmr] & 0xFFFF0000) | (m_timer_reload[tmr] & 0x0000FFFF);
		rate = 0x10000 - (m_timer_regs[tmr] & 0xffff);
		clocksel = timer_clks[(m_timer_regs[tmr] >> 16) & 3];
		final = clocksel / rate;
		m_timer_hz[tmr] = final;
		time = attotime::from_hz(final);
		m_tmr_timer[tmr]->adjust(time, tmr, time);
	}

	// Handle count-up timing
	switch (tmr)
	{
	case 0:
		if (m_timer_regs[1] & 0x40000)
		{
			m_timer_regs[1] = (( ( m_timer_regs[1] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[1] & 0xffff0000);
			if( ( m_timer_regs[1] & 0x0000ffff ) == 0 )
			{
				m_timer_regs[1] |= m_timer_reload[1];
				if( ( m_timer_regs[1] & 0x400000 ) && ( m_ime[cpu] != 0 ) )
				{
					request_irq(cpu, tmr_ints[1]);
				}
				if( ( m_timer_regs[2] & 0x40000 ) )
				{
					m_timer_regs[2] = (( ( m_timer_regs[2] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[2] & 0xffff0000);
					if( ( m_timer_regs[2] & 0x0000ffff ) == 0 )
					{
						m_timer_regs[2] |= m_timer_reload[2];
						if( ( m_timer_regs[2] & 0x400000 ) && ( m_ime[cpu] != 0 ) )
						{
							request_irq(cpu, tmr_ints[2]);
						}
						if( ( m_timer_regs[3] & 0x40000 ) )
						{
							m_timer_regs[3] = (( ( m_timer_regs[3] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[3] & 0xffff0000);
							if( ( m_timer_regs[3] & 0x0000ffff ) == 0 )
							{
								m_timer_regs[3] |= m_timer_reload[3];
								if( ( m_timer_regs[3] & 0x400000 ) && ( m_ime[cpu] != 0 ) )
								{
									request_irq(cpu, tmr_ints[3]);
								}
							}
						}
					}
				}
			}
		}
		break;
	case 1:
		if (m_timer_regs[2] & 0x40000)
		{
			m_timer_regs[2] = (( ( m_timer_regs[2] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[2] & 0xffff0000);
			if( ( m_timer_regs[2] & 0x0000ffff ) == 0 )
			{
				m_timer_regs[2] |= m_timer_reload[2];
				if( ( m_timer_regs[2] & 0x400000 ) && ( m_ime[cpu] != 0 ) )
				{
					request_irq(cpu, tmr_ints[2]);
				}
				if( ( m_timer_regs[3] & 0x40000 ) )
				{
					m_timer_regs[3] = (( ( m_timer_regs[3] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[3] & 0xffff0000);
					if( ( m_timer_regs[3] & 0x0000ffff ) == 0 )
					{
						m_timer_regs[3] |= m_timer_reload[3];
						if( ( m_timer_regs[3] & 0x400000 ) && ( m_ime[cpu] != 0 ) )
						{
							request_irq(cpu, tmr_ints[3]);
						}
					}
				}
			}
		}
		break;
	case 2:
		if (m_timer_regs[3] & 0x40000)
		{
			m_timer_regs[3] = (( ( m_timer_regs[3] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[3] & 0xffff0000);
			if( ( m_timer_regs[3] & 0x0000ffff ) == 0 )
			{
				m_timer_regs[3] |= m_timer_reload[3];
				if( ( m_timer_regs[3] & 0x400000 ) && ( m_ime[cpu] != 0 ) )
				{
					request_irq(cpu, tmr_ints[3]);
				}
			}
		}
		break;
	}

	// are we supposed to IRQ?
	if ((m_timer_regs[tmr] & 0x400000) && (m_ime[cpu] != 0))
	{
		request_irq(cpu, tmr_ints[tmr & 3]);
	}
}

void nds_state::nds(machine_config &config)
{
	ARM7(config, m_arm7, MASTER_CLOCK);
	m_arm7->set_addrmap(AS_PROGRAM, &nds_state::nds_arm7_map);

	ARM946ES(config, m_arm9, MASTER_CLOCK*2);
	m_arm9->set_high_vectors();
	m_arm9->set_addrmap(AS_PROGRAM, &nds_state::nds_arm9_map);

	// WRAM
	ADDRESS_MAP_BANK(config, "nds7wram").set_map(&nds_state::nds7_wram_map).set_options(ENDIANNESS_LITTLE, 32, 32, 0x8000);
	ADDRESS_MAP_BANK(config, "nds9wram").set_map(&nds_state::nds9_wram_map).set_options(ENDIANNESS_LITTLE, 32, 32, 0x8000);
}

// Help identifying the region and revisions of the main set would be greatly appreciated!
ROM_START( nds )
	ROM_REGION( 0x1000, "arm9", 0 )
	ROM_LOAD( "biosnds9.rom", 0x0000, 0x1000, CRC(2ab23573) SHA1(bfaac75f101c135e32e2aaf541de6b1be4c8c62d) )

	ROM_REGION( 0x4000, "arm7", 0 )
	ROM_LOAD( "biosnds7.rom", 0x0000, 0x4000, CRC(1280f0d5) SHA1(24f67bdea115a2c847c8813a262502ee1607b7df) )

	ROM_REGION32_LE( 0x40000, "firmware", 0 )
	ROM_SYSTEM_BIOS( 0, "nds", "Nintendo DS" )
	ROMX_LOAD( "firmware.bin", 0x0000, 0x40000, CRC(945f9dc9) SHA1(cfe072921ee3fb93f688743f8beef89043c3e9ad), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "40820d", "Nintendo DS ver.40820D prototype" ) // from X4 prototype unit
	ROMX_LOAD( "fw0802d6.bin", 0x0000, 0x40000, CRC(18e137df) SHA1(d51be561a6538941f8f43d6db9cdb964a383080a), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "040615", "Nintendo DS ver.040615 prototype" )
	ROMX_LOAD( "fw64b19d.bin", 0x0000, 0x40000, CRC(93487f12) SHA1(3af896a05736cc0385d0f858b431ff719164caf6), ROM_BIOS(2) )
ROM_END

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY     FULLNAME  FLAGS
CONS( 2004, nds,  0,      0,      nds,     nds,   nds_state, empty_init, "Nintendo", "DS",     MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



neogeo.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Bryan McPhail,Ernesto Corvi,Andrew Prime,Zsolt Vasvari
// thanks-to:Fuzz
/***************************************************************************

    Neo-Geo hardware

    Credits:
        * This driver was made possible by the research done by
          Charles MacDonald.  For a detailed description of the Neo-Geo
          hardware, please visit his page at:
          http://cgfm2.emuviews.com/txt/mvstech.txt
        * Presented to you by the Shin Emu Keikaku team.
        * The following people have all spent probably far
          too much time on this:
          AVDB
          Bryan McPhail
          Fuzz
          Ernesto Corvi
          Andrew Prime
          Zsolt Vasvari


    Known driver issues/to-do's:
    ============================

        * Fatal Fury 3 crashes during the ending - this doesn't occur if
          the language is set to Japanese, maybe the English endings
          are incomplete / buggy?
        * Graphical Glitches caused by incorrect timing?
          - Some raster effects are imperfect (off by a couple of lines)
        * 68000 waitstates on ROM region access, determined by jumpers on cart
        * AES Input clock is incorrect (24.167829MHz for NTSC systems, PAL is same?)
        * PAL region AES behavior is not verified


    Confirmed non-bugs:

        * Bad zooming in the Kof2003 bootlegs - this is what happens
          if you try and use the normal bios with a PCB set, it
          looks like the bootleggers didn't care.
        * Glitches at the edges of the screen - the real hardware
          can display 320x224 but most of the games seem designed
          to work with a width of 304, some less.
        * Distorted jumping sound in Nightmare in the Dark
        * Ninja Combat sometimes glitches


*****************************************************************************

    The Neo-Geo Multi Video System (MVS), is an arcade system board, being
    the first product in the Neo-Geo family, designed by Alpha Denshi (ADK)
    and released in 1990 by SNK.  It was known to the coin-op industry, and
    offered arcade operators the ability to put up to 6 different arcade
    titles into a single cabinet, a key economic consideration for operators
    with limited floorspace (games for the Neo-Geo are cartridge based and are
    easily exchangeable).  It comes in many different cabinets but basically
    consists of an add on board that can be linked to a standard Jamma system.
    The system was discontinued in 2004.
    Source (modified): http://en.wikipedia.org/wiki/Neo_Geo


    MVS motherboards were produced in 1 / 2 / 4 and 6 Slot versions.

    Known motherboards:
    ===================
    +---------+------+-------+------------+-----------------------+--------------------------------------------------------------------------+
    | Model   | Year | Slots | Generation | Video chipset         | Notes                                                                    |
    +---------+------+-------+------------+-----------------------+--------------------------------------------------------------------------+
    | MV-1    | 1990 | 1     | 1          | PRO-B0/PRO-C0/LSPC-A0 | Original full-featured 1-slot board                                      |
    | MV-1F   |      | 1     | 2          | NEO-B1/LSPC2-A2       | 2nd-generation 1-slot board, no memory card headers                      |
    | MV-1FZ  |      | 1     | 2          | NEO-B1/LSPC2-A2       | Cost-reduced MV-1F without LED displays/mahjong inputs/stereo output     |
    | MV-1FZS |      | 1     | 2          | NEO-B1/LSPC2-A2       | Spanish MV-1FZ                                                           |
    | MV-1A   | 1995 | 1     | 3          | NEO-MGA/NEO-GRC       | 3rd-generation 1-slot board, removes coin lockouts                       |
    | MV-1ACH | 1995 | 1     | 3          | NEO-MGA/NEO-GRC       | Chinese MV-1A                                                            |
    | MV-1AX  |      | 1     | 3          | NEO-MGA/NEO-GRC       | MV-1A with soldered BIOS                                                 |
    | MV-1B   |      | 1     | 4          | NEO-GRC2              | 4th-generation 1-slot board, soldered BIOS, no SM1 ROM, 8-pin SIT header |
    | MV-1C   | 1999 | 1     | 5          | NEO-GRZ               | Final iteration, vertical cartridge slot                                 |
    +---------+------+-------+------------+-----------------------+--------------------------------------------------------------------------+
    | MV-2B   |      | 2     | 1          | PRO-B0/PRO-C0/LSPC-A0 | Original full-featured 2-slot board                                      |
    | MV-2F   |      | 2     | 2          | NEO-B1/LSPC2-A2       | 2nd-generation 2-slot board, onboard PCMCIA slot and 3.5mm jacks         |
    | MV-2FS  |      | 2     | 2          | NEO-B1/LSPC2-A2       | Spanish MV-2F                                                            |
    +---------+------+-------+------------+-----------------------+--------------------------------------------------------------------------+
    | MV-4    | 1990 | 4     | 1          | PRO-B0/PRO-C0/LSPC-A0 | Original 4-slot board                                                    |
    | MV-4F   |      | 4     | 2          | NEO-B1/LSPC2-A2       | 2nd-generation 4-slot board                                              |
    | MV-4FS  |      | 4     | 2          | NEO-B1/LSPC2-A2       | Spanish MV-4                                                             |
    +---------+------+-------+------------+-----------------------+--------------------------------------------------------------------------+
    | MV-6    | 1990 | 6     | 1          | PRO-B0/PRO-C0/LSPC-A0 | 2-board stack - mainboard and slot board                                 |
    +---------+------+-------+------------+-----------------------+--------------------------------------------------------------------------+

    Other mainboards:

    1 Slot:
    NEO-MVH MV1-1
     . NEO-MVH MV1A CHX ??
    NEO-MVH MV1B (1996.1.19) (miniaturised, 8-pin header for irrmaze/kizuna4p)
     . NEO-MVH MV1B CHX (1996.1.19) ??
    NEO-MVH MV1B1 (1998.6.17)
    NEO-MVH MV1C (1999.4.30)
    NEO-MVH MV1FT (cost-reduced MV1T without memory card headers)
    NEO-MVH MV1T

    2 Slot:
    NEO-MVH MV2
    NEO-MVH MV2F-01

    4 Slot:
    NEO-MVH MV4-25 (US MV4?)
    NEO-MVH MV4FT
    NEO-MVH MV4FT2

    6 Slot:
    NEO-MVH MV6F


    Neo-Geo Motherboard (info - courtesy of Guru):

          NEO-MVH MV1
          |---------------------------------------------------------------------|
          |       4558                                                          |
          |                                          HC04  HC32                 |
          |                      SP-S2.SP1  NEO-E0   000-L0.L0   LS244  AS04    |
          |             YM2610                                                  |
          | 4558                                                                |
          |       4558                        5814  HC259   SFIX.SFIX           |
          |                                                             NEO-I0  |
          | HA13001 YM3016                    5814                              |
          --|                                                                   |
            |     4558                                                          |
          --|                                                 SM1.SM1   LS32    |
          |                                                                     |
          |                           LSPC-A0         PRO-C0            LS244   |
          |                                                                     |
          |J              68000                                                 |
          |A                                                                    |
          |M                                                                    |
          |M                                                      NEO-ZMC2      |
          |A                                                                    |
          |   LS273  NEO-G0                          58256  58256     Z80A      |
          |                           58256  58256   58256  58256     6116      |
          |   LS273 5864                                                        |
          --| LS05  5864  PRO-B0                                                |
            |                                                                   |
          --|             LS06   HC32           D4990A    NEO-F0   24.000MHz    |
          |                      DSW1    BATT3.6V 32.768kHz       NEO-D0        |
          |                                           2003  2003                |
          |---------------------------------------------------------------------|


    Mainboard features
    ==================
    +-------+----+-----+-----+-----+--------+--------+-------+-----+-------+-------+------+-----+---+--------+-------+------+
    |       |Vid |Slots|Edge |Coins|Counters|Lockouts|Mahjong|8-pin|Memcard|Mono/St|Phones|7-seg|EL |  BIOS  |Data In|Orient|
    +-------+----+-----+-----+-----+--------+--------+-------+-----+-------+-------+------+-----+---+--------+-------+------+
    |MV-1   | B0 |  1  |JAMMA|  2  |   2    |   2    |   2   | no  |header |switch |header| yes |no | socket |  yes  |horiz |
    |MV-1F  | B1 |  1  |JAMMA|  2  |   2    |   2    |   2   | no  | none  |switch |header| yes |no | socket |  no   |horiz |
    |MV-1FZ | B1 |  1  |JAMMA|  2  |   2    |   2    |   0   | no  | none  | mono  | none | no  |no | socket |  yes  |horiz |
    |MV-1A  |MGA |  1  |JAMMA|  2  |   2    |   0    |   0   | no  | none  | mono  | none | no  |no | socket |  no   |horiz |
    |MV-1AX |MGA |  1  |JAMMA|  2  |   2    |   0    |   0   | no  | none  | mono  | none | no  |no |soldered|  no   |horiz |
    |MV-1B  |GRC2|  1  |JAMMA|  2  |   2    |   0    |   0   | yes | none  | mono  | none | no  |no |soldered|  no   |horiz |
    |MV-1C  |GRZ |  1  |JAMMA|  2  |   2    |   0    |   0   | yes | none  | mono  | none | no  |no |soldered|  no   | vert |
    +-------+----+-----+-----+-----+--------+--------+-------+-----+-------+-------+------+-----+---+--------+-------+------+
    |MV-2B  | B0 |  2  | MVS |  4  |   2    |   2    |   2   | no  |header | both  |header| yes |yes| socket |  no   | vert |
    |MV-2F  | B1 |  2  | MVS |  4  |   2    |   2    |   2   | no  |onboard| both  |jacks | yes |yes| socket |  no   | vert |
    +-------+----+-----+-----+-----+--------+--------+-------+-----+-------+-------+------+-----+---+--------+-------+------+
    |MV-4-25| B0 |  4  | MVS |  4  |   2    |   2    |   2   | no  |header | both  |header| yes |yes| socket |  no   | vert |
    |MV-4F  | B1 |  4  | MVS |  4  |   2    |   2    |   2   | no  |header | both  |header| yes |yes| socket |  no   | vert |
    +-------+----+-----+-----+-----+--------+--------+-------+-----+-------+-------+------+-----+---+--------+-------+------+
    |MV-6   | B0 |  6  | MVS |  4  |   2    |   2    |   2   | no  |header | both  |header| yes |yes| socket |  no   | vert |
    +-------+----+-----+-----+-----+--------+--------+-------+-----+-------+-------+------+-----+---+--------+-------+------+

    * Function of the data input switch is unknown
    * MVS connector has high-level single-ended stereo outputs and low-level mono output
    * JAMMA connector has high-level balanced mono output
    * Single-slot boards with stereo capability have a 4-pin stereo speaker header
    * Boards with stereo support have headphone connectors or headers for them
    * Newer single-slot boards have an 8-pin header for the trackball or 4-player interface (SIT)

*****************************************************************************

    Neo-Geo game PCB infos:
    =======================

    The Neo-Geo games for AES (home) and MVS (arcade) systems are cartridge based.

    Each cartridge consists of two PCBs: CHA and PROG.
    .CHA PCB contains gfx data ('C' - rom), text layer data ('S' - rom) and sound driver ('M' - rom).
    .PROG PCB contains sample data ('V' - rom) and program code ('P' - rom).

    On most PCBs various custom/protection chips can also be found:
    (Custom chip detail information (modified) from: http://wiki.neogeodev.org)

    CHA:
    . NEO-273  (C and S-ROM address latch)
    . NEO-CMC 90G06CF7042 (NEO-273 logic / NEO-ZMC logic / C-ROM decryption / C and S-ROM multiplexer / S-ROM bankswitching)
    . NEO-CMC 90G06CF7050 (NEO-273 logic / NEO-ZMC logic / C-ROM decryption / M-ROM decryption / C and S-ROM multiplexer / S-ROM bankswitching)
    . NEO-ZMC  (Z80 memory controller)
    . NEO-ZMC2 (Z80 memory controller / Tile serializer)
    . PRO-CT0  (C-ROM serializer and multiplexer?; used on early AES-CHA boards)
    . SNK-9201 (C-ROM serializer and multiplexer?; used on early AES-CHA boards)

    PROG:
    . 0103 (QFP144) (Only found on Metal Slug X NEO-MVS PROGEOP board; function unknown)
    . ALTERA   (EPM7128SQC100-15) (P-ROM protection chip used for KOF98 NEO-MVS PROGSF1 board and Metal Slug X NEO-MVS PROGEOP board)
    . NEO-COMA (Microcontroller; used for MULTI PLAY MODE, boards and sets see below)
    . NEO-PCM2 (SNK 1999) (PCM functionality / V-ROM decryption / P-ROM decoding and bankswitching)
    . NEO-PCM2 (PLAYMORE 2002) (PCM functionality / V-ROM decryption / P-ROM decoding and bankswitching)
    . NEO-PVC  (P-ROM decryption and bankswitching) / RAM
    . NEO-SMA  (P-ROM decryption and bankswitching / RNG / Storage of 256kb game data)
    . PCM      (ADPCM bus latches / V-ROM multiplexer)
    . PRO-CT0  (On PROG board used for P-ROM protection -> Fatal Fury 2)
    . SNK-9201 (On PROG board used for P-ROM protection -> Fatal Fury 2)



    Known PCBs:
    ============

    MVS CHA:
    -- SNK --
    . NEO-MVS CHA-32
    . NEO-MVS CHA-8M
    . NEO-MVS CHA42G
    . NEO-MVS CHA42G-1
    . NEO-MVS CHA 42G-2
    . NEO-MVS CHA 42G-3
    . NEO-MVS CHA42G-3B
    . NEO-MVS CHA256
    . NEO-MVS CHA256B
    . NEO-MVS CHA512Y
    . NEO-MVS CHAFIO (1999.6.14) - used with NEO-CMC 90G06C7042 or NEO-CMC 90G06C7050
    . MVS CHAFIO REV1.0 (KOF-2001)
    . NEO-MVS CHAFIO (SNK 2002) - MADE IN KOREA
    -- SNKPLAYMORE --
    . NEO-MVS CHAFIO (2003.7.24) - used only with NEO-CMC 90G06C7050

    -- SNK development boards --
    . NEO-MVS CHAMC2

    MVS PROG:
    -- SNK --
    . NEO-MVS PROG-NAM
    . NEO-MVS PROG-HERO
    . NEO-MVS PROG-EP
    . NEO-MVS PROG-8MB
    . NEO-MVS PROGEP8M
    . NEO-MVS PROG8M42
    . NEO-MVS PROG16
    . NEO-MVS PROG42G
    . NEO-MVS PROG42G-COM
    . NEO-MVS PROG42G-1
    . NEO-MVS PROG-G2
    . NEO-MVS PROG 4096
    . NEO-MVS PROG 4096 B
    . NEO-MVS PROGGSC
    . NEO-MVS PROGSM
    . NEO-MVS PROGSS3
    . NEO-MVS PROGTOP
    . NEO-MVS PROGSF1 (1998.6.17)
    . NEO-MVS PROGSF1E (1998.6.18)
    . NEO-MVS PROGEOP (1999.2.2)
    . NEO-MVS PROGLBA (1999.4.12) - LBA-SUB (2000.2.24)
    . NEO-MVS PROGBK1 (1994)
    . NEO-MVS PROGBK1 (2001)
    . NEO-MVS PROGBK2 (2000.3.21) - used with NEO-PCM2 (1999 SNK) or NEO-PCM2 (2002 PLAYMORE)
    . MVS PROGBK2 REV1.0 (KOF-2001)
    . NEO-MVS PROGBK2 (SNK 2002) - MADE IN KOREA
    -- SNKPLAYMORE --
    . NEO-MVS PROGBK2R (2003.8.26) - NEO-HYCS (2003.9.29)
    . NEO-MVS PROGBK3R (2003.9.2) - NEO-HYCS (2003.9.29)
    . NEO-MVS PROGBK3S (2003.10.1)
    . NEO-MVS PROGBK2S (2003.10.18)

    -- SNK development boards --
    . NEO-MVS PROGMC2


    AES CHA:
    -- SNK --
    . NEO-AEG CHA-32
    . NEO-AEG CHA-8M
    . NEO-AEG CHA42G
    . NEO-AEG CHA42G-1
    . NEO-AEG CHA42G-2B
    . NEO-AEG CHA42G-3
    . NEO-AEG CHA42G-4
    . NEO-AEG CHA256
    . NEO-AEG CHA256 B
    . NEO-AEG CHA256[B]
    . NEO-AEG CHA256BY
    . NEO-AEG CHA256RY
    . NEO-AEG CHA512Y
    . NEO-AEG CHAFIO (1999.8.10) - used with NEO-CMC 90G06C7042 or NEO-CMC 90G06C7050
    -- SNKPLAYMORE --
    . NEO-AEG CHAFIO (2003.7.24) - used only with NEO-CMC 90G06C7050

    AES PROG:
    -- SNK --
    . NEO-AEG PROG-NAM
    . NEO-AEG PROG-HERO
    . NEO-AEG PROG-4A
    . NEO-AEG PROG-4B
    . NEO-AEG PROG 8M42
    . NEO-AEG PROG B
    . NEO-AEG PROG16
    . NEO-AEG PROG42G
    . NEO-AEG PROG42G-COM
    . NEO-AEG PROG42G-1
    . NEO-AEG PROG-G2
    . NEO-AEG PROG4096 B
    . NEO-AEG PROGGS
    . NEO-AEG PROGTOP2
    . NEO-AEG PROGTOP2Y
    . NEO-AEG PROGEOP (1999.4.2)
    . NEO-AEG PROGLBA (1999.7.6)
    . NEO-AEG PROGRK
    . NEO-AEG PROGRKB
    . NEO-AEG PROGBK1Y
    . NEO-AEG PROGBK1F
    -- PLAYMORE --
    . NEO-AEG PROGBK2 (2002.4.1) - used with NEO-PCM2 (1999 SNK) or NEO-PCM2 (2002 PLAYMORE)
    -- SNKPLAYMORE --
    . NEO-AEG PROGBK3R (2003.8.29) - NEO-HYCS (2003.9.29)
    . NEO-AEG PROGBK3S (2003.10.6)
    . NEO-AEG PROGBK2S (2003.10.16)



    Cartridge colours:
    ==================

    MVS cartridges were produced in different colours.

    Known cartridge colours:
    . Black
    . Blue
    . Green
    . Grey
    . Red
    . Transparent
    . Transparent Blue
    . Transparent Green
    . White
    . Yellow

    The above listed only covers SNK / PLAYMORE / SNKPLAYMORE PCBs. There also exists a
    wide range of 'bootleg' PCBs.


    Unofficial PCBs from NG:DEV.TEAM:

    MVS CHA:
    GIGA CHAR Board 1.0 Rev. A
    GIGA CHAR Board 1.5 Rev. 0
    GIGA CHAR Board 1.5 Rev. C

    MVS PROG:
    GIGA PROG Board 1.0 Rev. B
    GIGA PROG Board 1.5 Rev. A
    GIGA PROG Board 1.5 Rev. C


    Unofficial PCBs from NEOBITZ:

    MVS CHA:
    CHARBITZ1 2013.12.01

    MVS PROG:
    PROGBITZ1 2013.12.01


    Neo-Geo game PCB infos by Johnboy



    MVS cart pinout:
    ================

    Kindly submitted by Apollo69 (apollo69@columbus.rr.com)
    =================================================================
                CTRG1                            CTRG2
    =================================================================
         GND = 01A | 01B = GND            GND = 01A | 01B = GND
         GND = 02A | 02B = GND            GND = 02A | 02B = GND
          P0 = 03A | 03B = P1             GND = 03A | 03B = GND
          P2 = 04A | 04B = P3             GND = 04A | 04B = GND
          P4 = 05A | 05B = P5              D0 = 05A | 05B = A1
          P6 = 06A | 06B = P7              D1 = 06A | 06B = A2
          P8 = 07A | 07B = P9              D2 = 07A | 07B = A3
         P10 = 08A | 08B = P11             D3 = 08A | 08B = A4
         P12 = 09A | 09B = P13             D4 = 09A | 09B = A5
         P14 = 10A | 10B = P15             D5 = 10A | 10B = A6
         P16 = 11A | 11B = P17             D6 = 11A | 11B = A7
         P18 = 12A | 12B = P19             D7 = 12A | 12B = A8
         P20 = 13A | 13B = P21             D8 = 13A | 13B = A9
         P22 = 14A | 14B = P23             D9 = 14A | 14B = A10
       PCK1B = 15A | 15B = 24M            D10 = 15A | 15B = A11
       PCK2B = 16A | 16B = 12M            D11 = 16A | 16B = A12
         2H1 = 17A | 17B = 8M             D12 = 17A | 17B = A13
         CA4 = 18A | 18B = RESET          D13 = 18A | 18B = A14
         CR0 = 19A | 19B = CR1            D14 = 19A | 19B = A15
         CR2 = 20A | 20B = CR3            D15 = 20A | 20B = A16
         CR4 = 21A | 21B = CR5            R/W = 21A | 21B = A17
         CR6 = 22A | 22B = CR7             AS = 22A | 22B = A18
         CR8 = 23A | 23B = CR9         ROMOEU = 23A | 23B = A19
        CR10 = 24A | 24B = CR11        ROMOEL = 24A | 24B = 68KCLKB
        CR12 = 25A | 25B = CR13       PORTOEU = 25A | 25B = ROMWAIT
        CR14 = 26A | 26B = CR15       PORTOEL = 26A | 26B = PWAIT0
        CR16 = 27A | 27B = CR17       PORTWEU = 27A | 27B = PWAIT1
        CR18 = 28A | 28B = CR19       PORTWEL = 28A | 28B = PDTACT
         VCC = 29A | 29B = VCC            VCC = 29A | 29B = VCC
         VCC = 30A | 30B = VCC            VCC = 30A | 30B = VCC
         VCC = 31A | 31B = VCC            VCC = 31A | 31B = VCC
         VCC = 32A | 32B = VCC            VCC = 32A | 32B = VCC
        CR20 = 33A | 33B = CR21      PORTADRS = 33A | 33B = ROMOE
        CR22 = 34A | 34B = CR23            NC = 34A | 34B = 4MB
        CR24 = 35A | 35B = CR25            NC = 35A | 35B = RESET
        CR26 = 36A | 36B = CR27            NC = 36A | 36B = NC
        CR28 = 37A | 37B = CR29            NC = 37A | 37B = NC
        CR30 = 38A | 38B = CR31            NC = 38A | 38B = NC
          NC = 39A | 39B = FIX00           NC = 39A | 39B = NC
          NC = 40A | 40B = FIX01           NC = 40A | 40B = NC
          NC = 41A | 41B = FIX02           NC = 41A | 41B = SDPAD0
      SLOTCS = 42A | 42B = FIX03       SLOTCS = 42A | 42B = SDPAD1
        SDA0 = 43A | 43B = FIX04        SDPA8 = 43A | 43B = SDPAD2
        SDA1 = 44A | 44B = FIX05        SDPA9 = 44A | 44B = SDPAD3
        SDA2 = 45A | 45B = FIX06       SDPA10 = 45A | 45B = SDPAD4
        SDA3 = 46A | 46B = FIX07       SDPA11 = 46A | 46B = SDPAD5
        SDA4 = 47A | 47B = SDRD0       SDPMPX = 47A | 47B = SDPAD6
        SDA5 = 48A | 48B = SDRD1        SDPOE = 48A | 48B = SDPAD7
        SDA6 = 49A | 49B = SDROM        SDRA8 = 49A | 49B = SDRAD0
        SDA7 = 50A | 50B = SDMRD        SDRA9 = 50A | 50B = SDRAD1
        SDA8 = 51A | 51B = SDD0        SDRA20 = 51A | 51B = SDRAD2
        SDA9 = 52A | 52B = SDD1        SDRA21 = 52A | 52B = SDRAD3
       SDA10 = 53A | 53B = SDD2        SDRA22 = 53A | 53B = SDRAD4
       SDA11 = 54A | 54B = SDD3        SDRA23 = 54A | 54B = SDRAD5
       SDA12 = 55A | 55B = SDD4        SDRMPX = 55A | 55B = SDRAD6
       SDA13 = 56A | 56B = SDD5         SDROE = 56A | 56B = SDRAD7
       SDA14 = 57A | 57B = SDD6           GND = 57A | 57B = GND
       SDA15 = 58A | 58B = SDD7           GND = 58A | 58B = GND
         GND = 59A | 59B = GND            GND = 59A | 59B = GND
         GND = 60A | 60B = GND            GND = 60A | 60B = GND

    CTRG1 (CHA)  = Contains gfx data ('C' - rom), text layer data ('S' - rom) and sound driver ('M' - rom)
    CTRG2 (PROG) = Contains sample data ('V' - rom) and program code ('P' - rom)

    NOTE: On CTRG2-B, The "A" lines start at "A1". If you trace this on an
    actual cart, you will see that this is actually "A0" (A0 - A18).

    These are from a very hard to read copy of the schematics, so
    I hope that I got the pin names correct.

    Apollo69 10/19/99



    Edge connector pinout:
    ======================

    Kindly submitted by Apollo69 (apollo69@columbus.rr.com)
    =================================================================
                  MVS                            JAMMA
    =================================================================
           GND = A |  1 = GND              GND = A |  1 = GND
           GND = B |  2 = GND              GND = B |  2 = GND
           +5V = C |  3 = +5V              +5V = C |  3 = +5V
           +5V = D |  4 = +5V              +5V = D |  4 = +5V
           N/C = E |  5 = N/C                    E |  5
          +12V = F |  6 = +12V            +12V = F |  6 = +12V
           key = H |  7 = key              key = H |  7 = key
     counter 2 = J |  8 = counter 1  counter 2 = J |  8 = counter 1
     lockout 2 = K |  9 = lockout 1  lockout 2 = K |  9 = lockout 1
     L speaker = L | 10 = R speaker   speaker- = L | 10 = speaker+
          test = M | 11 = audio+     audio GND = M | 11
         green = N | 12 = red            green = N | 12 = red
          sync = P | 13 = blue            sync = P | 13 = blue
       service = R | 14 = video GND    service = R | 14 = video GND
     coin 4 2P = S | 15 = coin 3 1P              S | 15 = test
     coin 2 2P = T | 16 = coin 1 1P  coin 2 2P = T | 16 = coin 1 1P
      2P start = U | 17 = 1P start    2P start = U | 17 = 1P start
         2P up = V | 18 = 1P up          2P up = V | 18 = 1P up
       2P down = W | 19 = 1P down      2P down = W | 19 = 1P down
       2P left = X | 20 = 1P left      2P left = X | 20 = 1P left
      2P right = Y | 21 = 1P right    2P right = Y | 21 = 1P right
          2P A = Z | 22 = 1P A            2P A = Z | 22 = 1P A
          2P B = a | 23 = 1P B            2P B = a | 23 = 1P B
          2P C = b | 24 = 1P C            2P C = b | 24 = 1P C
          2P D = c | 25 = 1P D            2P D = c | 25 = 1P D
      sel down = d | 26 = sel up                 d | 26 = data input
           GND = e | 27 = GND              GND = e | 27 = GND
           GND = f | 28 = GND              GND = f | 28 = GND

    Later JAMMA systems drop coin lockouts and audio ground.
    Some JAMMA systems omit data input switch.



*****************************************************************************

    Watchdog:
    =========

    The watchdog timer will reset the system after ~0.13 seconds.
    By cgfm's research, exactly 3,244,030 cycles (based on 24MHz clock).

    Newer games force a reset using the following code (this from kof99):
        009CDA  203C 0003 0D40             MOVE.L   #0x30D40,D0
        009CE0  5380                       SUBQ.L   #1,D0
        009CE2  64FC                       BCC.S    *-0x2 [0x9CE0]
    Note however that there is a valid code path after this loop.

    The watchdog is used as a form of protection on a number of games,
    previously this was implemented as a specific hack which locked a single
    address of SRAM.

    What actually happens is if the game doesn't find valid data in the
    backup ram it will initialize it, then sit in a loop.  The watchdog
    should then reset the system while it is in this loop.  If the watchdog
    fails to reset the system the code will continue and set a value in
    backup ram to indicate that the protection check has failed.

****************************************************************************

    Mahjong Panel notes (2009-03 FP):
    =================================

    * In Service Mode menu with mahjong panel active, controls are as
      follows:

        A = select / up (for options)
        B = down (for options)
        C = go to previous menu
        E = up (for menu entries)
        F = down (for menu entries)
        G = left (for options)
        H = right (for options)

    * These only work with Japanese BIOS, but I think it's not a bug: I
      doubt other BIOS were programmed to be compatible with mahjong panels

****************************************************************************

    AES driver (home version of MVS)
    Current emulation status:
    - Cartridges run.
    - Riding Hero runs in slow-mo due to the unemulated comm link MCU in the cartridge.
      In MAME if dip SW6 is set to ON to enable link play, it runs the same way!
      On AES there are no dipswitches, and so it always tries to talk to the MCU.

****************************************************************************/

#include "emu.h"
#include "neogeo.h"

#include "machine/nvram.h"
#include "machine/watchdog.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "irrmaze.lh"
#include "neogeo.lh"

#define LOG_VIDEO_SYSTEM         (1U << 1)

#define VERBOSE (0)
#include "logmacro.h"


class mvs_state : public ngarcade_base_state
{
public:
	mvs_state(const machine_config &mconfig, device_type type, const char *tag)
		: ngarcade_base_state(mconfig, type, tag)
	{
	}

	// mainboard configurations
	void cartslot_config(machine_config &config, unsigned count);
	void cartslot_fixed(machine_config &config, char const *dflt);
	void mv1fz(machine_config &config);

	// fixed software configurations
	void kizuna4p(machine_config &config);
	void irrmaze(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

	virtual void device_post_load() override;
	virtual void output_strobe(uint8_t bits, uint8_t data) { }
	virtual void set_outputs() { }

	virtual void io_control_w(offs_t offset, uint8_t data) override;

private:
	uint8_t m_output_data = 0;
	uint8_t m_output_latch = 0;
};


class mvs_led_state : public mvs_state
{
public:
	mvs_led_state(const machine_config &mconfig, device_type type, const char *tag)
		: mvs_state(mconfig, type, tag)
		, m_digits(*this, "digit%u", 1U)
	{
	}

	// mainboard configurations
	void mv1(machine_config &config);
	void mv1f(machine_config &config);

	// fixed software configurations
	void neobase(machine_config &config);
	void fatfur2(machine_config &config);
	void kof97oro(machine_config &config);
	void kog(machine_config &config);
	void kof98(machine_config &config);
	void mslugx(machine_config &config);
	void kof99(machine_config &config);
	void kof99k(machine_config &config);
	void garou(machine_config &config);
	void garouh(machine_config &config);
	void garoubl(machine_config &config);
	void mslug3(machine_config &config);
	void mslug3a(machine_config &config);
	void mslug3h(machine_config &config);
	void mslug3b6(machine_config &config);
	void kof2000(machine_config &config);
	void kof2000n(machine_config &config);
	void zupapa(machine_config &config);
	void sengoku3(machine_config &config);
	void kof2001(machine_config &config);
	void cthd2k3(machine_config &config);
	void ct2k3sp(machine_config &config);
	void ct2k3sa(machine_config &config);
	void kof2002(machine_config &config);
	void kof2002b(machine_config &config);
	void kf2k2pls(machine_config &config);
	void kf2k2mp(machine_config &config);
	void kf2k2mp2(machine_config &config);
	void kof10th(machine_config &config);
	void kf10thep(machine_config &config);
	void kf2k5uni(machine_config &config);
	void kof2k4se(machine_config &config);
	void mslug5(machine_config &config);
	void ms5plus(machine_config &config);
	void mslug5b(machine_config &config);
	void svc(machine_config &config);
	void svcboot(machine_config &config);
	void svcplus(machine_config &config);
	void svcplusa(machine_config &config);
	void svcsplus(machine_config &config);
	void samsho5(machine_config &config);
	void samsho5b(machine_config &config);
	void kof2003(machine_config &config);
	void kof2003h(machine_config &config);
	void kf2k3bl(machine_config &config);
	void kf2k3pl(machine_config &config);
	void kf2k3upl(machine_config &config);
	void samsh5sp(machine_config &config);
	void neogeo_mj(machine_config &config);
	void preisle2(machine_config &config);
	void nitd(machine_config &config);
	void s1945p(machine_config &config);
	void lans2004(machine_config &config);
	void pnyaa(machine_config &config);
	void popbounc(machine_config &config);
	void ganryu(machine_config &config);
	void bangbead(machine_config &config);
	void mslug4(machine_config &config);
	void ms4plus(machine_config &config);
	void rotd(machine_config &config);
	void matrim(machine_config &config);
	void matrimbl(machine_config &config);
	void jockeygp(machine_config &config);
	void vliner(machine_config &config);
	void sbp(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

	virtual void output_strobe(uint8_t bits, uint8_t data) override;
	virtual void set_outputs() override;

	void mv1_fixed(machine_config &config);

private:
	output_finder<4> m_digits;

	uint8_t m_led1_value = 0;
	uint8_t m_led2_value = 0;
};


class mvs_led_el_state : public mvs_led_state
{
public:
	mvs_led_el_state(const machine_config &mconfig, device_type type, const char *tag)
		: mvs_led_state(mconfig, type, tag)
		, m_lamps(*this, "lamp%u", 1U)
	{
	}

	// mainboard configurations
	void mv2f(machine_config &config);
	void mv4f(machine_config &config);
	void mv6f(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

	virtual void output_strobe(uint8_t bits, uint8_t data) override;
	virtual void set_outputs() override;

private:
	output_finder<6> m_lamps;

	uint8_t m_el_value = 0;
};


class aes_state : public aes_base_state
{
public:
	aes_state(const machine_config &mconfig, device_type type, const char *tag)
		: aes_base_state(mconfig, type, tag)
	{
	}

	void aes_ntsc(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

	virtual void device_post_load() override;

	void aes_main_map(address_map &map) ATTR_COLD;
};



/*************************************
 *
 *  Main CPU interrupt generation
 *
 *************************************/

// The display counter is automatically reloaded with the load register contents on scanline 224,
// 1146 mclks from the rising edge of /HSYNC.
#define NEOGEO_VBLANK_RELOAD_HTIM (attotime::from_ticks(1146, NEOGEO_MASTER_CLOCK))

static constexpr unsigned IRQ2CTRL_ENABLE          = 4;
static constexpr unsigned IRQ2CTRL_LOAD_RELATIVE   = 5;
static constexpr unsigned IRQ2CTRL_AUTOLOAD_VBLANK = 6;
static constexpr unsigned IRQ2CTRL_AUTOLOAD_REPEAT = 7;


void neogeo_base_state::adjust_display_position_interrupt_timer()
{
	attotime period = attotime::from_ticks((uint64_t)m_display_counter + 1, NEOGEO_PIXEL_CLOCK);
	LOGMASKED(LOG_VIDEO_SYSTEM, "adjust_display_position_interrupt_timer  current y: %02x  current x: %02x   target y: %x  target x: %x\n", m_screen->vpos(), m_screen->hpos(), (m_display_counter + 1) / NEOGEO_HTOTAL, (m_display_counter + 1) % NEOGEO_HTOTAL);

	m_display_position_interrupt_timer->adjust(period);
}


void neogeo_base_state::set_display_position_interrupt_control(uint16_t data)
{
	m_display_position_interrupt_control = data;
}


void neogeo_base_state::set_display_counter_msb(uint16_t data)
{
	m_display_counter = (m_display_counter & 0x0000ffff) | ((uint32_t)data << 16);

	LOGMASKED(LOG_VIDEO_SYSTEM, "PC %06x: set_display_counter %08x\n", m_maincpu->pc(), m_display_counter);
}


void neogeo_base_state::set_display_counter_lsb(uint16_t data)
{
	m_display_counter = (m_display_counter & 0xffff0000) | data;

	LOGMASKED(LOG_VIDEO_SYSTEM, "PC %06x: set_display_counter %08x\n", m_maincpu->pc(), m_display_counter);

	if (BIT(m_display_position_interrupt_control, IRQ2CTRL_LOAD_RELATIVE))
	{
		LOGMASKED(LOG_VIDEO_SYSTEM, "AUTOLOAD_RELATIVE ");
		adjust_display_position_interrupt_timer();
	}
}


void neogeo_base_state::update_interrupts()
{
	m_maincpu->set_input_line(3, m_irq3_pending ? ASSERT_LINE : CLEAR_LINE);
	m_maincpu->set_input_line(m_raster_level, m_display_position_interrupt_pending ? ASSERT_LINE : CLEAR_LINE);
	m_maincpu->set_input_line(m_vblank_level, m_vblank_interrupt_pending ? ASSERT_LINE : CLEAR_LINE);
}


void neogeo_base_state::acknowledge_interrupt(uint16_t data)
{
	if (BIT(data, 0))
		m_irq3_pending = 0;
	if (BIT(data, 1))
		m_display_position_interrupt_pending = 0;
	if (BIT(data, 2))
		m_vblank_interrupt_pending = 0;

	update_interrupts();
}


TIMER_CALLBACK_MEMBER(neogeo_base_state::display_position_interrupt_callback)
{
	LOGMASKED(LOG_VIDEO_SYSTEM, "--- Scanline @ %d,%d\n", m_screen->vpos(), m_screen->hpos());

	if (BIT(m_display_position_interrupt_control, IRQ2CTRL_ENABLE))
	{
		LOGMASKED(LOG_VIDEO_SYSTEM, "*** Scanline interrupt (IRQ2) ***  y: %02x  x: %02x\n", m_screen->vpos(), m_screen->hpos());
		m_display_position_interrupt_pending = 1;

		update_interrupts();
	}

	if (BIT(m_display_position_interrupt_control, IRQ2CTRL_AUTOLOAD_REPEAT))
	{
		LOGMASKED(LOG_VIDEO_SYSTEM, "AUTOLOAD_REPEAT ");
		adjust_display_position_interrupt_timer();
	}
}


TIMER_CALLBACK_MEMBER(neogeo_base_state::display_position_vblank_callback)
{
	if (BIT(m_display_position_interrupt_control, IRQ2CTRL_AUTOLOAD_VBLANK))
	{
		LOGMASKED(LOG_VIDEO_SYSTEM, "AUTOLOAD_VBLANK ");
		adjust_display_position_interrupt_timer();
	}

	/* set timer for next screen */
	m_display_position_vblank_timer->adjust(m_screen->time_until_pos(NEOGEO_VBSTART) + NEOGEO_VBLANK_RELOAD_HTIM);
}


TIMER_CALLBACK_MEMBER(neogeo_base_state::vblank_interrupt_callback)
{
	LOGMASKED(LOG_VIDEO_SYSTEM, "+++ VBLANK @ %d,%d\n", m_screen->vpos(), m_screen->hpos());

	m_vblank_interrupt_pending = 1;
	update_interrupts();

	/* set timer for next screen */
	m_vblank_interrupt_timer->adjust(m_screen->time_until_pos(NEOGEO_VBSTART) + NEOGEO_VBLANK_IRQ_HTIM);
}


void neogeo_base_state::create_interrupt_timers()
{
	m_display_position_interrupt_timer = timer_alloc(FUNC(neogeo_base_state::display_position_interrupt_callback), this);
	m_display_position_vblank_timer = timer_alloc(FUNC(neogeo_base_state::display_position_vblank_callback), this);
	m_vblank_interrupt_timer = timer_alloc(FUNC(neogeo_base_state::vblank_interrupt_callback), this);
}


void neogeo_base_state::start_interrupt_timers()
{
	m_vblank_interrupt_timer->adjust(m_screen->time_until_pos(NEOGEO_VBSTART) + NEOGEO_VBLANK_IRQ_HTIM);
	m_display_position_vblank_timer->adjust(m_screen->time_until_pos(NEOGEO_VBSTART) + NEOGEO_VBLANK_RELOAD_HTIM);
}



/*************************************
 *
 *  Audio CPU interrupt generation
 *
 *************************************/

void neogeo_base_state::audio_cpu_enable_nmi_w(offs_t offset, uint8_t data)
{
	// out ($08) enables the nmi, out ($18) disables it
	m_audionmi->in_w<1>(BIT(~offset, 4));
}



/*************************************
 *
 *  Input ports / Controllers
 *
 *************************************/

uint16_t ngarcade_base_state::in0_edge_r()
{
	return (m_edge->in0_r() << 8) | m_dsw->read();
}

uint16_t ngarcade_base_state::in0_edge_joy_r()
{
	return ((m_edge->in0_r() & m_ctrl1->read_ctrl()) << 8) | m_dsw->read();
}

uint16_t ngarcade_base_state::in1_edge_r()
{
	return (m_edge->in1_r() << 8) | 0xff;
}

uint16_t ngarcade_base_state::in1_edge_joy_r()
{
	return ((m_edge->in1_r() & m_ctrl2->read_ctrl()) << 8) | 0xff;
}

ioport_value ngarcade_base_state::startsel_edge_joy_r()
{
	uint32_t ret = m_edge->read_start_sel() | ~0x05;
	if (m_ctrl1)
		ret &= (m_ctrl1->read_start_sel() << 0) | ~0x03;
	if (m_ctrl2)
		ret &= (m_ctrl2->read_start_sel() << 2) | ~0x0c;
	return ret;
}

void neogeo_base_state::io_control_w(offs_t offset, uint8_t data)
{
	switch (offset & 0x38) // TODO: the mask is supposedly less restrictive on AES?
	{
	case 0x00:
		if (m_ctrl1) m_ctrl1->write_ctrlsel(data & 0x07);
		if (m_ctrl2) m_ctrl2->write_ctrlsel((data >> 3) & 0x07);
		if (m_edge) m_edge->write_ctrlsel(data & 0x3f); // FIXME: only MV-1B and MV-1C have this output
		break;

	case 0x08:
		m_card_bank = data & 0x07;
		break;

	default:
		logerror("%s: Unmapped I/O control write.  Offset: %02x  Data: %02x\n", machine().describe_context(), offset, data);
	}
}

void ngarcade_base_state::io_control_w(offs_t offset, uint8_t data)
{
	switch (offset & 0x78)
	{
	case 0x00:
	case 0x08:
		neogeo_base_state::io_control_w(offset, data);
		break;

	case 0x28:
		m_upd4990a->data_in_w(BIT(data, 0));
		m_upd4990a->clk_w(BIT(data, 1));
		m_upd4990a->stb_w(BIT(data, 2));
		break;

	case 0x30:
	case 0x70:
		if (BIT(offset, 1))
			machine().bookkeeping().coin_lockout_w(BIT(offset, 0), BIT(offset, 6));
		else
			machine().bookkeeping().coin_counter_w(BIT(offset, 0), BIT(offset, 6));
		break;

	default:
		logerror("%s: Unmapped I/O control write.  Offset: %02x  Data: %02x\n", machine().describe_context(), offset, data);
	}
}

void mvs_state::io_control_w(offs_t offset, uint8_t data)
{
	switch (offset & 0x78)
	{
	case 0x10:
		if (m_slots[data])
			set_slot_idx(data);
		break;

	case 0x18:
		// strobe on falling edge
		output_strobe(m_output_latch & ~data, m_output_data);
		m_output_latch = data;
		set_outputs();
		break;

	case 0x20:
		m_output_data = data;
		break;

	default:
		ngarcade_base_state::io_control_w(offset, data);
	}
}


void neogeo_base_state::audio_command_w(uint8_t data)
{
	// glitches in s1945p without the perfect_quantum here
	m_soundlatch->write(data);
	machine().scheduler().perfect_quantum(attotime::from_usec(50));
}


/*************************************
 *
 *  Unmapped memory access
 *
 *************************************/

uint16_t neogeo_base_state::unmapped_r(address_space &space)
{
	uint16_t ret;

	/* unmapped memory returns the last word on the data bus, which is almost always the opcode
	   of the next instruction due to prefetch */

	/* prevent recursion */
	if (m_recurse)
		ret = 0xffff;
	else
	{
		m_recurse = true;
		ret = space.read_word(m_maincpu->pc());
		m_recurse = false;
	}
	return ret;
}



/*************************************
 *
 *  NVRAM (Save RAM)
 *
 *************************************/

void ngarcade_base_state::set_save_ram_unlock(int state)
{
	m_save_ram_unlocked = state;
}


void ngarcade_base_state::save_ram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (m_save_ram_unlocked)
		COMBINE_DATA(&m_save_ram[offset]);
}



/*************************************
 *
 *  Memory card
 *
 *************************************/

ioport_value neogeo_base_state::get_memcard_status()
{
	// D0 and D1 are memcard 1 and 2 presence indicators, D2 indicates memcard
	// write protect status (we are always write enabled)
	return (!m_memcard || !m_memcard->present()) ? 0x07 : 0x00;
}


uint16_t neogeo_base_state::memcard_r(offs_t offset, uint16_t mem_mask)
{
	if (!machine().side_effects_disabled())
		m_maincpu->eat_cycles(2); // insert waitstate

	// memory card enabled by /UDS
	if (ACCESSING_BITS_8_15 && m_memcard->present())
		return m_memcard->read((offs_t(m_card_bank) << 21) | offset);
	else
		return 0xffff;
}


void neogeo_base_state::memcard_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	m_maincpu->eat_cycles(2); // insert waitstate

	// memory card enabled by /UDS
	if (ACCESSING_BITS_8_15 && m_memcard->present())
		m_memcard->write((offs_t(m_card_bank) << 21) | offset, data);
}


/*************************************
 *
 *  Inter-CPU communications
 *
 *************************************/

ioport_value neogeo_base_state::get_audio_result()
{
	return m_soundlatch2->read();
}


/*************************************
 *
 *  Audio CPU banking
 *
 *************************************/

uint8_t neogeo_base_state::audio_cpu_bank_select_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		m_bank_audio_cart[offset & 3]->set_entry(offset >> 8);

	return 0;
}


/*************************************
 *
 *  System control register
 *
 *************************************/

void neogeo_base_state::set_use_cart_vectors(int state)
{
	m_use_cart_vectors = state;
}


void neogeo_base_state::set_use_cart_audio(int state)
{
	m_use_cart_audio = state;
	m_sprgen->set_fixed_layer_source(state);
	m_bank_audio_main->set_entry(m_use_cart_audio);
}


void neogeo_base_state::write_banksel(uint16_t data)
{
	uint32_t const len = (!m_slots[m_curr_slot] || m_slots[m_curr_slot]->get_rom_size() == 0) ? m_region_maincpu->bytes() : m_slots[m_curr_slot]->get_rom_size();

	if ((len <= 0x100000) && (data & 0x07))
		logerror("PC %06x: warning: bankswitch to %02x but no banks available\n", m_maincpu->pc(), data);
	else
	{
		int bank = data & 0x07;

		if ((bank + 1) * 0x100000 >= len)
		{
			logerror("PC %06x: warning: bankswitch to empty bank %02x\n", m_maincpu->pc(), data);
			bank = 0;
		}
		uint8_t *ROM = (!m_slots[m_curr_slot] || m_slots[m_curr_slot]->get_rom_size() == 0) ? m_region_maincpu->base() : (uint8_t *)m_slots[m_curr_slot]->get_rom_base();
		m_bank_base = (bank + 1) * 0x100000;
		m_bank_cartridge->set_base(ROM + m_bank_base);
	}
}


/*************************************
 *
 *  LEDs
 *
 *************************************/

void mvs_led_state::set_outputs()
{
	static constexpr uint8_t led_map[0x10] = { 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x58,0x4c,0x62,0x69,0x78,0x00 };

	// LED1
	m_digits[0] = led_map[m_led1_value >> 4];
	m_digits[1] = led_map[m_led1_value & 0x0f];

	// LED2
	m_digits[2] = led_map[m_led2_value >> 4];
	m_digits[3] = led_map[m_led2_value & 0x0f];

	mvs_state::set_outputs();
}

void mvs_led_el_state::set_outputs()
{
	// EL
	for (unsigned i = 0; 6U > i; ++i)
		m_lamps[i] = m_el_value == i;

	mvs_led_state::set_outputs();
}


void mvs_led_state::output_strobe(uint8_t bits, uint8_t data)
{
	if (BIT(bits, 4))
		m_led1_value = ~data;

	if (BIT(bits, 5))
		m_led2_value = ~data;

	mvs_state::output_strobe(bits, data);
}

void mvs_led_el_state::output_strobe(uint8_t bits, uint8_t data)
{
	if (BIT(bits, 3))
		m_el_value = ~data & 0x07;

	mvs_led_state::output_strobe(bits, data);
}


/*************************************
 *
 *  Bankswitch passthrus for cart-specific
 *  handling (temporary)
 *  These are installed in set_slot_idx
 *
 *************************************/

// FIXME: These are a temporary workaround for slot-driven bankswitch with protected carts.
// A cleaner implementation is in progress.

void neogeo_base_state::write_bankprot(uint16_t data)
{
	m_bank_base = m_slots[m_curr_slot]->get_bank_base(data);
	m_bank_cartridge->set_base((uint8_t *)m_slots[m_curr_slot]->get_rom_base() + m_bank_base);
}

void neogeo_base_state::write_bankprot_pvc(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	// write to cart ram
	m_slots[m_curr_slot]->protection_w(offset, data, mem_mask);

	// actual bankswitch
	if (offset >= 0xff8)
	{
		m_bank_base = m_slots[m_curr_slot]->get_bank_base(data);
		m_bank_cartridge->set_base((uint8_t *)m_slots[m_curr_slot]->get_rom_base() + m_bank_base);
	}
}

void neogeo_base_state::write_bankprot_kf2k3bl(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	// write to cart ram
	m_slots[m_curr_slot]->protection_w(offset, data, mem_mask);

	// actual bankswitch
	if (offset == 0x1ff0/2 || offset == 0x1ff2/2)
	{
		m_bank_base = m_slots[m_curr_slot]->get_bank_base(data);
		m_bank_cartridge->set_base((uint8_t *)m_slots[m_curr_slot]->get_rom_base() + m_bank_base);
	}
}

void neogeo_base_state::write_bankprot_ms5p(offs_t offset, uint16_t data)
{
	logerror("ms5plus bankswitch - offset: %06x PC %06x: set banking %04x\n", offset, m_maincpu->pc(), data);

	if ((offset == 0) && (data == 0xa0))
	{
		m_bank_base = 0xa0;
		m_bank_cartridge->set_base((uint8_t *)m_slots[m_curr_slot]->get_rom_base() + m_bank_base);
	}
	else if (offset == 2)
	{
		m_bank_base = m_slots[m_curr_slot]->get_bank_base(data);
		m_bank_cartridge->set_base((uint8_t *)m_slots[m_curr_slot]->get_rom_base() + m_bank_base);
	}
}

void neogeo_base_state::write_bankprot_kof10th(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	m_slots[m_curr_slot]->protection_w(offset, data, mem_mask);

	if (offset == 0xffff0/2)
	{
		// Standard bankswitch
		m_bank_base = m_slots[m_curr_slot]->get_bank_base(data);
		m_bank_cartridge->set_base((uint8_t *)m_slots[m_curr_slot]->get_rom_base() + m_bank_base);
	}
}

uint16_t neogeo_base_state::read_lorom_kof10th(offs_t offset)
{
	uint16_t* rom = (m_slots[m_curr_slot] && m_slots[m_curr_slot]->get_rom_size() > 0) ? m_slots[m_curr_slot]->get_rom_base() : (uint16_t*)m_region_maincpu->base();
	if (offset + 0x80/2 >= 0x10000/2)
		offset += m_slots[m_curr_slot]->get_special_bank();
	return rom[offset + 0x80/2];
}


/*************************************
 *
 *  Machine initialization
 *
 *************************************/

void neogeo_base_state::init_cpu()
{
	uint8_t *ROM = (!m_slots[m_curr_slot] || m_slots[m_curr_slot]->get_rom_size() == 0) ? m_region_maincpu->base() : (uint8_t *)m_slots[m_curr_slot]->get_rom_base();
	uint32_t const len = (!m_slots[m_curr_slot] || m_slots[m_curr_slot]->get_rom_size() == 0) ? m_region_maincpu->bytes() : m_slots[m_curr_slot]->get_rom_size();

	if (len > 0x100000)
		m_bank_base = 0x100000;
	else
		m_bank_base = 0;

	m_bank_cartridge->set_base(ROM + m_bank_base);
}

void neogeo_base_state::init_audio()
{
	uint8_t *ROM = (!m_slots[m_curr_slot] || m_slots[m_curr_slot]->get_audio_size() == 0) ? m_region_audiocpu->base() : m_slots[m_curr_slot]->get_audio_base();
	uint32_t const len = (!m_slots[m_curr_slot] || m_slots[m_curr_slot]->get_audio_size() == 0) ? m_region_audiocpu->bytes() : m_slots[m_curr_slot]->get_audio_size();

	/* audio bios/cartridge selection */
	m_bank_audio_main->configure_entry(0, (m_region_audiobios != nullptr) ? m_region_audiobios->base() : ROM); /* on hardware with no SM1 ROM, the cart ROM is always enabled */
	m_bank_audio_main->configure_entry(1, ROM);
	m_bank_audio_main->set_entry(1);

	/* audio banking */
	m_bank_audio_cart[0] = membank("audio_f000");
	m_bank_audio_cart[1] = membank("audio_e000");
	m_bank_audio_cart[2] = membank("audio_c000");
	m_bank_audio_cart[3] = membank("audio_8000");

	uint32_t const address_mask = (len - 0x10000 - 1) & 0x3ffff;
	for (int region = 0; region < 4; region++)
	{
		for (int bank = 0xff; bank >= 0; bank--)
		{
			uint32_t const bank_address = 0x10000 + ((bank << (11 + region)) & address_mask);
			m_bank_audio_cart[region]->configure_entry(bank, &ROM[bank_address]);
		}
	}

	// set initial audio banks - THIS IS A HACK
	// Z80 banking is handled by the NEO-ZMC chip in the cartridge
	// (in later cartridges, by multifunction banking/protection chips that implement the same bank scheme)
	// On the real chip, initial banks are all 0.
	// However, early cartridges with less than 64KB of Z80 code and data don't have ROM banking at all.
	// These initial bank settings are required so non-banked games will work until we identify them
	// and use a different Z80 address map for them.
	m_bank_audio_cart[0]->set_entry(0x1e);
	m_bank_audio_cart[1]->set_entry(0x0e);
	m_bank_audio_cart[2]->set_entry(0x06);
	m_bank_audio_cart[3]->set_entry(0x02);
}

void neogeo_base_state::init_ym()
{
	// Resetting a sound device causes the core to update() it and generate samples if it's not up to date.
	m_ym->reset();

	address_space &adpcm_a_space = m_ym->space(0);
	adpcm_a_space.unmap_readwrite(0x000000, 0xffffff);

	if (m_slots[m_curr_slot] && m_slots[m_curr_slot]->get_ym_size())
		adpcm_a_space.install_rom(0, m_slots[m_curr_slot]->get_ym_size() - 1, m_slots[m_curr_slot]->get_ym_base());

	address_space &adpcm_b_space = m_ym->space(1);
	adpcm_b_space.unmap_readwrite(0x000000, 0xffffff);

	if (m_slots[m_curr_slot] && m_slots[m_curr_slot]->get_ymdelta_size())
		adpcm_b_space.install_rom(0, m_slots[m_curr_slot]->get_ymdelta_size() - 1, m_slots[m_curr_slot]->get_ymdelta_base());
	else if (m_slots[m_curr_slot] && m_slots[m_curr_slot]->get_ym_size())
		adpcm_b_space.install_rom(0, m_slots[m_curr_slot]->get_ym_size() - 1, m_slots[m_curr_slot]->get_ym_base());
}

void neogeo_base_state::init_sprites()
{
	if (m_slots[m_curr_slot] && m_slots[m_curr_slot]->get_sprites_size() > 0)
	{
		m_sprgen->set_sprite_region(m_slots[m_curr_slot]->get_sprites_base(), m_slots[m_curr_slot]->get_sprites_size());
		m_sprgen->set_fixed_regions(m_slots[m_curr_slot]->get_fixed_base(), m_slots[m_curr_slot]->get_fixed_size(), m_region_fixedbios);
		if (!m_slots[m_curr_slot]->user_loadable())
			m_sprgen->optimize_sprite_data();
		else
			m_sprgen->set_optimized_sprite_data(m_slots[m_curr_slot]->get_sprites_opt_base(), m_slots[m_curr_slot]->get_sprites_opt_size() - 1);
		m_sprgen->set_fixed_layer_bank_type(m_slots[m_curr_slot]->get_fixed_bank_type());
	}
	else
	{
		m_sprgen->set_sprite_region(m_region_sprites->base(), m_region_sprites->bytes());
		m_sprgen->set_fixed_regions(m_region_fixed->base(), m_region_fixed->bytes(), m_region_fixedbios);
	}
}


void neogeo_base_state::set_slot_idx(int slot)
{
	if (slot != m_curr_slot)
	{
		address_space &space = m_maincpu->space(AS_PROGRAM);
		m_curr_slot = slot;
		m_bank_base = 0;

		if (!m_slots[m_curr_slot]->user_loadable())
			m_slots[m_curr_slot]->late_decrypt_all();

		// unmap any handler that previous carts could have installed
		space.unmap_readwrite(0x000080, 0x0fffff);
		space.unmap_readwrite(0x200000, 0x2fffff);
		if (!m_slots[m_curr_slot] || m_slots[m_curr_slot]->get_rom_size() == 0)
			space.install_rom(0x000080, 0x0fffff, (uint16_t *)m_region_maincpu->base() + 0x80/2);
		else
			space.install_rom(0x000080, 0x0fffff, (uint16_t *)m_slots[m_curr_slot]->get_rom_base() + 0x80/2);


		space.install_read_bank(0x200000, 0x2fffff, m_bank_cartridge);
		space.install_write_handler(0x2ffff0, 0x2fffff, write16smo_delegate(*this, FUNC(neogeo_base_state::write_banksel)));

		init_cpu();

		init_audio();
		m_audiocpu->reset(); // svc have no sound if in higher slots without this?

		init_ym();

		init_sprites();

		if (!m_slots[m_curr_slot]->user_loadable())
			m_slots[m_curr_slot]->set_cart_type(m_slots[m_curr_slot]->default_option());

		int const type = m_slots[m_curr_slot]->get_type();
		switch (type)
		{
		case NEOGEO_FATFURY2:
			space.install_read_handler(0x200000, 0x2fffff, read16m_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::protection_r)));
			space.install_write_handler(0x200000, 0x2fffff, write16s_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::protection_w)));
			break;
		case NEOGEO_KOF98:
			space.install_read_handler(0x00100, 0x00103, read16m_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::protection_r)));
			space.install_write_handler(0x20aaaa, 0x20aaab, write16s_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::protection_w)));
			break;
		case NEOGEO_MSLUGX:
			space.install_read_handler(0x2fffe0, 0x2fffef, read16m_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::protection_r)));
			space.install_write_handler(0x2fffe0, 0x2fffef, write16s_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::protection_w)));
			break;
		case NEOGEO_KOF99:
			// addon_r here gives SMA random number
			space.install_write_handler(0x2ffff0, 0x2ffff1, write16smo_delegate(*this, FUNC(neogeo_base_state::write_bankprot)));
			space.install_read_handler(0x2fe446, 0x2fe447, read16m_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::protection_r)));
			space.install_read_handler(0x2ffff8, 0x2ffff9, read16sm_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::addon_r)));
			space.install_read_handler(0x2ffffa, 0x2ffffb, read16sm_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::addon_r)));
			break;
		case NEOGEO_GAROU:
			// addon_r here gives SMA random number
			space.install_write_handler(0x2fffc0, 0x2fffc1, write16smo_delegate(*this, FUNC(neogeo_base_state::write_bankprot)));
			space.install_read_handler(0x2fe446, 0x2fe447, read16m_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::protection_r)));
			space.install_read_handler(0x2fffcc, 0x2fffcd, read16sm_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::addon_r)));
			space.install_read_handler(0x2ffff0, 0x2ffff1, read16sm_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::addon_r)));
			break;
		case NEOGEO_GAROUH:
			// addon_r here gives SMA random number
			space.install_write_handler(0x2fffc0, 0x2fffc1, write16smo_delegate(*this, FUNC(neogeo_base_state::write_bankprot)));
			space.install_read_handler(0x2fe446, 0x2fe447, read16m_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::protection_r)));
			space.install_read_handler(0x2fffcc, 0x2fffcd, read16sm_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::addon_r)));
			space.install_read_handler(0x2ffff0, 0x2ffff1, read16sm_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::addon_r)));
			break;
		case NEOGEO_MSLUG3:
		case NEOGEO_MSLUG3A:
			space.install_write_handler(0x2fffe4, 0x2fffe5, write16smo_delegate(*this, FUNC(neogeo_base_state::write_bankprot)));
			space.install_read_handler(0x2fe446, 0x2fe447, read16m_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::protection_r)));
			//space.install_read_handler(0x2ffff8, 0x2ffff9, read16sm_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::addon_r)));
			//space.install_read_handler(0x2ffffa, 0x2ffffb, read16sm_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::addon_r)));
			break;
		case NEOGEO_KOF2K:
			// addon_r here gives SMA random number
			space.install_write_handler(0x2fffec, 0x2fffed, write16smo_delegate(*this, FUNC(neogeo_base_state::write_bankprot)));
			space.install_read_handler(0x2fe446, 0x2fe447, read16m_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::protection_r)));
			space.install_read_handler(0x2fffd8, 0x2fffd9, read16sm_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::addon_r)));
			space.install_read_handler(0x2fffda, 0x2fffdb, read16sm_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::addon_r)));
			break;
		case NEOGEO_MSLUG5:
		case NEOGEO_SVC:
		case NEOGEO_KOF2K3:
		case NEOGEO_KOF2K3H:
		case NEOGEO_SVCBOOT:
		case NEOGEO_SVCSPLUS:
			space.install_read_handler(0x2fe000, 0x2fffff, read16m_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::protection_r)));
			space.install_write_handler(0x2fe000, 0x2fffff, write16s_delegate(*this, FUNC(neogeo_base_state::write_bankprot_pvc)));
			break;
		case NEOGEO_CTHD2K3:
		case NEOGEO_CT2K3SP:
			space.install_write_handler(0x2ffff0, 0x2ffff1, write16smo_delegate(*this, FUNC(neogeo_base_state::write_bankprot)));
			break;
		case NEOGEO_MSLUG5P:
			space.install_read_handler(0x2ffff0, 0x2fffff, read16m_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::protection_r)));
			space.install_write_handler(0x2ffff0, 0x2fffff, write16sm_delegate(*this, FUNC(neogeo_base_state::write_bankprot_ms5p)));
			break;
		case NEOGEO_KOG:
			space.install_read_handler(0x0ffffe, 0x0fffff, read16m_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::protection_r)));
			break;
		case NEOGEO_KOF2K3B:
		case NEOGEO_KOF2K3UP:
			// addon_r here gives m_overlay member from bootleg protection (possibly hack?)
			space.install_read_handler(0x2fe000, 0x2fffff, read16m_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::protection_r)));
			space.install_write_handler(0x2fe000, 0x2fffff, write16s_delegate(*this, FUNC(neogeo_base_state::write_bankprot_kf2k3bl)));
			space.install_read_handler(0x58196, 0x58197, read16sm_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::addon_r)));
			break;
		case NEOGEO_KOF2K3P:
			space.install_read_handler(0x2fe000, 0x2fffff, read16m_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::protection_r)));
			space.install_write_handler(0x2fe000, 0x2fffff, write16s_delegate(*this, FUNC(neogeo_base_state::write_bankprot_kf2k3bl)));
			break;
		case NEOGEO_SBP:
			// there seems to be a protection device living around here..
			// if you nibble swap the data in the rom the game will boot
			// there are also writes to 0x1080..
			//
			// other stuff going on as well tho, the main overlay is still missing, and p1 inputs don't work
			space.install_read_handler(0x00200, 0x001fff, read16m_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::protection_r)));
			space.install_write_handler(0x00200, 0x001fff, write16s_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::protection_w)));
			break;
		case NEOGEO_KOF10TH:
			// addon_r here reads from ram2 bank
			space.install_read_handler(0x000080, 0x0dffff, read16sm_delegate(*this, FUNC(neogeo_base_state::read_lorom_kof10th)));
			space.install_read_handler(0x0e0000, 0x0fffff, read16sm_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::addon_r)));
			space.install_read_handler(0x2fe000, 0x2fffff, read16m_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::protection_r)));
			// REVIEW ME: we might possibly need to split this, by adding further write handlers
			space.install_write_handler(0x200000, 0x2fffff, write16s_delegate(*this, FUNC(neogeo_base_state::write_bankprot_kof10th)));
			break;
		case NEOGEO_JOCKEYGP:
			space.install_read_handler(0x200000, 0x201fff, read16sm_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::ram_r)));
			space.install_write_handler(0x200000, 0x201fff, write16s_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::ram_w)));
			break;
		case NEOGEO_VLINER:
			space.install_read_handler(0x200000, 0x201fff, read16sm_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::ram_r)));
			space.install_write_handler(0x200000, 0x201fff, write16s_delegate(*m_slots[m_curr_slot], FUNC(neogeo_cart_slot_device::ram_w)));
			// custom input handling... install it here for the moment.
			space.install_read_port(0x300000, 0x300001, 0x01ff7e, "DSW");
			space.install_read_port(0x280000, 0x280001, "IN5");
			space.install_read_port(0x2c0000, 0x2c0001, "IN6");
			break;
		}
	}
}


/*************************************
 *
 *  Machine start
 *
 *************************************/

void neogeo_base_state::machine_start()
{
	m_use_cart_vectors = 0;

	create_interrupt_timers();

	/* irq levels for MVS / AES */
	m_vblank_level = 1;
	m_raster_level = 2;

	/* start with an IRQ3 - but NOT on a reset */
	m_irq3_pending = 1;

	/* register state save */
	save_item(NAME(m_display_position_interrupt_control));
	save_item(NAME(m_display_counter));
	save_item(NAME(m_vblank_interrupt_pending));
	save_item(NAME(m_display_position_interrupt_pending));
	save_item(NAME(m_irq3_pending));
	save_item(NAME(m_curr_slot));
	save_item(NAME(m_bank_base));
	save_item(NAME(m_use_cart_vectors));
	save_item(NAME(m_use_cart_audio));
	save_item(NAME(m_card_bank));
}

void ngarcade_base_state::machine_start()
{
	neogeo_base_state::machine_start();

	address_space &main_program_space(m_maincpu->space(AS_PROGRAM));

	if (m_ctrl1)
		main_program_space.install_read_handler(0x300000, 0x300001, 0, 0x01ff7e, 0, read16smo_delegate(*this, FUNC(ngarcade_base_state::in0_edge_joy_r)));
	else if (m_edge)
		main_program_space.install_read_handler(0x300000, 0x300001, 0, 0x01ff7e, 0, read16smo_delegate(*this, FUNC(ngarcade_base_state::in0_edge_r)));

	if (m_ctrl2)
		main_program_space.install_read_handler(0x340000, 0x340001, 0, 0x01fffe, 0, read16smo_delegate(*this, FUNC(ngarcade_base_state::in1_edge_joy_r)));
	else if (m_edge)
		main_program_space.install_read_handler(0x340000, 0x340001, 0, 0x01fffe, 0, read16smo_delegate(*this, FUNC(ngarcade_base_state::in1_edge_r)));

	if (m_memcard)
	{
		main_program_space.unmap_readwrite(0x800000, 0xbfffff);
		main_program_space.install_read_handler(0x800000, 0xbfffff, read16s_delegate(*this, FUNC(ngarcade_base_state::memcard_r)));
		main_program_space.install_write_handler(0x800000, 0xbfffff, write16s_delegate(*this, FUNC(ngarcade_base_state::memcard_w)));
	}

	// enable rtc and serial mode
	m_upd4990a->cs_w(1);
	m_upd4990a->oe_w(1);
	m_upd4990a->c0_w(1);
	m_upd4990a->c1_w(1);
	m_upd4990a->c2_w(1);

	save_item(NAME(m_save_ram_unlocked));
}

void mvs_state::machine_start()
{
	ngarcade_base_state::machine_start();

	m_sprgen->set_fixed_layer_bank_type(neosprite_base_device::FIX_BANKTYPE_STD);

	m_curr_slot = -1;
	set_slot_idx(0);

	save_item(NAME(m_output_data));
	save_item(NAME(m_output_latch));
}

void mvs_led_state::machine_start()
{
	mvs_state::machine_start();

	m_digits.resolve();

	save_item(NAME(m_led1_value));
	save_item(NAME(m_led2_value));
}

void mvs_led_el_state::machine_start()
{
	mvs_led_state::machine_start();

	m_lamps.resolve();

	save_item(NAME(m_el_value));
}


void neogeo_base_state::device_post_load()
{
	m_bank_audio_main->set_entry(m_use_cart_audio);
	set_pens();
}

void mvs_state::device_post_load()
{
	ngarcade_base_state::device_post_load();

	set_outputs();
	if (m_slots[m_curr_slot] && m_slots[m_curr_slot]->get_rom_size() > 0)
		m_bank_cartridge->set_base((uint8_t *)m_slots[m_curr_slot]->get_rom_base() + m_bank_base);
}


/*************************************
 *
 *  Machine reset
 *
 *************************************/


void neogeo_base_state::machine_reset()
{
	// disable audiocpu NMI
	m_audionmi->in_w<1>(0);
	m_soundlatch->acknowledge_w();

	start_interrupt_timers();

	// trigger the IRQ3 that was set by MACHINE_START
	update_interrupts();

	m_recurse = false;
}

void ngarcade_base_state::machine_reset()
{
	neogeo_base_state::machine_reset();

	machine().bookkeeping().coin_lockout_w(0, 1);
	machine().bookkeeping().coin_lockout_w(1, 1);
}


/*************************************
 *
 *  Main CPU memory handlers
 *
 *************************************/


uint16_t neogeo_base_state::banked_vectors_r(offs_t offset)
{
	if (!m_use_cart_vectors)
	{
		uint16_t* bios = (uint16_t*)m_region_mainbios->base();
		return bios[offset];
	}
	else
	{
		uint16_t* rom = (m_slots[m_curr_slot] && m_slots[m_curr_slot]->get_rom_size() > 0) ? m_slots[m_curr_slot]->get_rom_base() : m_region_maincpu ? (uint16_t*)m_region_maincpu->base() : m_share_maincpu;
		return rom[offset];
	}
}

/*************************************
 *
 *  Main CPU memory handlers
 *
 *************************************/

void neogeo_base_state::base_main_map(address_map &map)
{
	map(0x320000, 0x320000).mirror(0x01fffe).w(FUNC(neogeo_base_state::audio_command_w));
	map(0x360000, 0x37ffff).r(FUNC(neogeo_base_state::unmapped_r));
	map(0x380000, 0x3800ff).mirror(0x01ff00).w(FUNC(neogeo_base_state::io_control_w)).umask16(0x00ff);
	map(0x3a0000, 0x3a001f).mirror(0x01ffe0).r(FUNC(neogeo_base_state::unmapped_r));
	map(0x3a0000, 0x3a001f).mirror(0x01ffe0).w(m_systemlatch, FUNC(hc259_device::write_a3)).umask16(0x00ff); // BITW1 (system control registers)
	map(0x3c0000, 0x3c0007).mirror(0x01fff8).r(FUNC(neogeo_base_state::video_register_r));
	map(0x3c0000, 0x3c000f).mirror(0x01fff0).w(FUNC(neogeo_base_state::video_register_w));
	map(0x3e0000, 0x3fffff).r(FUNC(neogeo_base_state::unmapped_r));
	map(0x400000, 0x401fff).mirror(0x3fe000).rw(FUNC(neogeo_base_state::paletteram_r), FUNC(neogeo_base_state::paletteram_w));
}

void ngarcade_base_state::neogeo_main_map(address_map &map)
{
	base_main_map(map);

	map(0x000000, 0x00007f).r(FUNC(ngarcade_base_state::banked_vectors_r));
	map(0x100000, 0x10ffff).mirror(0x0f0000).ram();
	// some games have protection devices in the 0x200000 region, it appears to map to cart space, not surprising, the ROM is read here too
	map(0x300001, 0x300001).mirror(0x01fffe).w("watchdog", FUNC(watchdog_timer_device::reset_w));
	map(0x300080, 0x300081).mirror(0x01ff7e).portr("TEST");
	map(0x320000, 0x320001).mirror(0x01fffe).portr("AUDIO_COIN");
	map(0x380000, 0x380001).mirror(0x01fffe).portr("SYSTEM");
	map(0x800000, 0x800fff).r(FUNC(ngarcade_base_state::unmapped_r)); // memory card mapped here if present
	map(0xc00000, 0xc1ffff).mirror(0x0e0000).rom().region("mainbios", 0);
	map(0xd00000, 0xd0ffff).mirror(0x0f0000).ram().w(FUNC(ngarcade_base_state::save_ram_w)).share("saveram");
	map(0xe00000, 0xffffff).r(FUNC(ngarcade_base_state::unmapped_r));
}



uint16_t aes_base_state::aes_in2_r()
{
	uint32_t ret = m_io_in2->read() & 0xf0ff;
	ret |= ((m_ctrl1->read_start_sel() & 0x03) << 8) | ((m_ctrl2->read_start_sel() & 0x03) << 10);
	return ret;
}

void aes_base_state::aes_base_main_map(address_map &map)
{
	base_main_map(map);

	map(0x300000, 0x300000).mirror(0x01fffe).r(m_ctrl1, FUNC(neogeo_control_port_device::read_ctrl));
	map(0x320000, 0x320001).mirror(0x01fffe).portr("AUDIO");
	map(0x340000, 0x340000).mirror(0x01fffe).r(m_ctrl2, FUNC(neogeo_control_port_device::read_ctrl));
	map(0x380000, 0x380001).mirror(0x01fffe).r(FUNC(aes_base_state::aes_in2_r));
}

void aes_state::aes_main_map(address_map &map)
{
	aes_base_main_map(map);

	map(0x000000, 0x00007f).r(FUNC(aes_state::banked_vectors_r));
	map(0x100000, 0x10ffff).mirror(0x0f0000).ram();
	// some games have protection devices in the 0x200000 region, it appears to map to cart space, not surprising, the ROM is read here too
	map(0x800000, 0xbfffff).rw(FUNC(aes_state::memcard_r), FUNC(aes_state::memcard_w));
	map(0xc00000, 0xc1ffff).mirror(0x0e0000).rom().region("mainbios", 0);
	map(0xd00000, 0xffffff).r(FUNC(aes_state::unmapped_r));
}


/*************************************
 *
 *  Audio CPU memory handlers
 *
 *************************************/

void neogeo_base_state::audio_map(address_map &map)
{
	map(0x0000, 0x7fff).bankr(m_bank_audio_main);
	map(0x8000, 0xbfff).bankr("audio_8000");
	map(0xc000, 0xdfff).bankr("audio_c000");
	map(0xe000, 0xefff).bankr("audio_e000");
	map(0xf000, 0xf7ff).bankr("audio_f000");
	map(0xf800, 0xffff).ram();
}



/*************************************
 *
 *  Audio CPU port handlers
 *
 *************************************/

void neogeo_base_state::audio_io_map(address_map &map)
{
	map(0x00, 0x00).mirror(0xff00).rw(m_soundlatch, FUNC(generic_latch_8_device::read), FUNC(generic_latch_8_device::clear_w));
	map(0x04, 0x07).mirror(0xff00).rw(m_ym, FUNC(ym2610_device::read), FUNC(ym2610_device::write));
	map(0x08, 0x08).mirror(0xff00).select(0x0010).w(FUNC(neogeo_base_state::audio_cpu_enable_nmi_w));
	map(0x08, 0x0b).mirror(0x00f0).select(0xff00).r(FUNC(neogeo_base_state::audio_cpu_bank_select_r));
	map(0x0c, 0x0c).mirror(0xff00).w(m_soundlatch2, FUNC(generic_latch_8_device::write));
}



/*************************************
 *
 *  Standard Neo-Geo DIPs and
 *  input port definition
 *
 *************************************/

INPUT_PORTS_START( neogeo )
	PORT_START("DSW")
	PORT_DIPNAME( 0x01, 0x01, "Setting Mode" ) PORT_DIPLOCATION("SW:1")
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Cabinet ) ) PORT_DIPLOCATION("SW:2")
	PORT_DIPSETTING(    0x02, DEF_STR( Normal ) )
	PORT_DIPSETTING(    0x00, "VS Mode" )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Controller ) ) PORT_DIPLOCATION("SW:3")
	PORT_DIPSETTING(    0x04, DEF_STR( Joystick ) )
	PORT_DIPSETTING(    0x00, "Mahjong Panel" )
	PORT_DIPNAME( 0x18, 0x18, "COMM Setting (Cabinet No.)" ) PORT_DIPLOCATION("SW:4,5")
	PORT_DIPSETTING(    0x18, "1" )
	PORT_DIPSETTING(    0x10, "2" )
	PORT_DIPSETTING(    0x08, "3" )
	PORT_DIPSETTING(    0x00, "4" )
	PORT_DIPNAME( 0x20, 0x20, "COMM Setting (Link Enable)" ) PORT_DIPLOCATION("SW:6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Free_Play ) ) PORT_DIPLOCATION("SW:7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, "Freeze" ) PORT_DIPLOCATION("SW:8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("SYSTEM")
	PORT_BIT( 0x00ff, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0f00, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(ngarcade_base_state::startsel_edge_joy_r))
	PORT_BIT( 0x7000, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(neogeo_base_state::get_memcard_status))
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_CUSTOM ) // Hardware type (AES=0, MVS=1). Some games check this and show a piracy warning screen if the hardware and BIOS don't match

	PORT_START("AUDIO_COIN")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_COIN1 ) // coin 1 (combined) or P1 coin 1 (separate) for BIOS that supports it
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_COIN2 ) // coin 2 (combined) or P2 coin 1 (separate) for BIOS that supports it
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_SERVICE1 )
	PORT_BIT( 0x0018, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_CUSTOM ) // sense: 4-slot
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("upd4990a", FUNC(upd1990a_device::tp_r))
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("upd4990a", FUNC(upd1990a_device::data_out_r))
	PORT_BIT( 0xff00, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(neogeo_base_state::get_audio_result))

	PORT_START("TEST")
	PORT_BIT( 0x003f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_CUSTOM ) // sense: JAMMA
	PORT_SERVICE_NO_TOGGLE( 0x0080, IP_ACTIVE_LOW )
	PORT_BIT( 0xff00, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( neogeo_mvs )
	PORT_INCLUDE( neogeo )

	PORT_MODIFY("SYSTEM")
	PORT_BIT( 0x0500, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(ngarcade_base_state::startsel_edge_joy_r))
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Next Game") PORT_CODE(KEYCODE_3)
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Previous Game") PORT_CODE(KEYCODE_4)

	PORT_MODIFY("AUDIO_COIN")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_COIN3 ) // P1 coin 2 (separate) for BIOS that supports it
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_COIN4 ) // P2 coin 2 (separate) for BIOS that supports it

	PORT_MODIFY("TEST")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_CUSTOM ) // sense: MVS
INPUT_PORTS_END

static INPUT_PORTS_START( neogeo_mvs6 )
	PORT_INCLUDE( neogeo_mvs )

	PORT_MODIFY("AUDIO_COIN")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_CUSTOM ) // sense: 6-slot
INPUT_PORTS_END



INPUT_PORTS_START( aes )
	PORT_START("IN2")
	PORT_BIT( 0x0fff, IP_ACTIVE_LOW, IPT_UNUSED )
	// Start & Select are read from controller slot device
	PORT_BIT( 0x7000, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(neogeo_base_state::get_memcard_status))
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_CUSTOM ) /* Hardware type (AES=0, MVS=1) Some games check this and show a piracy warning screen if the hardware and BIOS don't match */

	PORT_START("AUDIO")
	PORT_BIT( 0x0007, IP_ACTIVE_HIGH, IPT_UNUSED )  /* AES has no coin slots, it's a console */
	PORT_BIT( 0x0018, IP_ACTIVE_HIGH, IPT_UNKNOWN ) /* what is this? Universe BIOS uses these bits to detect MVS or AES hardware */
	PORT_BIT( 0x00e0, IP_ACTIVE_HIGH, IPT_UNUSED )  /* AES has no upd4990a */
	PORT_BIT( 0xff00, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(neogeo_base_state::get_audio_result))

	PORT_START("JP") // JP1 and JP2 are jumpers or solderpads depending on AES board revision, intended for use on the Development BIOS
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Short JP1 (Debug Monitor)") PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(aes_base_state::aes_jp1), 0)
//  PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) // what is JP2 for? somehow related to system reset, disable watchdog?
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(aes_base_state::aes_jp1)
{
	// Shorting JP1 causes a 68000 /BERR (Bus Error). On Dev BIOS, this pops up the debug monitor.
	if (newval)
		m_maincpu->trigger_bus_error();
}




/*************************************
 *
 *  Machine driver
 *
 *************************************/

void neogeo_base_state::neogeo_base(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, NEOGEO_MAIN_CPU_CLOCK);

	Z80(config, m_audiocpu, NEOGEO_AUDIO_CPU_CLOCK);
	m_audiocpu->set_addrmap(AS_PROGRAM, &neogeo_base_state::audio_map);
	m_audiocpu->set_addrmap(AS_IO, &neogeo_base_state::audio_io_map);

	HC259(config, m_systemlatch);
	m_systemlatch->q_out_cb<0>().set(FUNC(neogeo_base_state::set_screen_shadow));
	m_systemlatch->q_out_cb<1>().set(FUNC(neogeo_base_state::set_use_cart_vectors));
	m_systemlatch->q_out_cb<2>().set_nop(); // memory card 1: write enable/disable
	m_systemlatch->q_out_cb<3>().set_nop(); // memory card 2: write disable/enable
	m_systemlatch->q_out_cb<4>().set_nop(); // memory card: register select enable/set to normal
	m_systemlatch->q_out_cb<7>().set(FUNC(neogeo_base_state::set_palette_bank));

	/* video hardware */
	config.set_default_layout(layout_neogeo);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(NEOGEO_PIXEL_CLOCK, NEOGEO_HTOTAL, NEOGEO_HBEND, NEOGEO_HBSTART, NEOGEO_VTOTAL, NEOGEO_VBEND, NEOGEO_VBSTART);
	m_screen->set_screen_update(FUNC(neogeo_base_state::screen_update));

	/* 4096 colors * two banks * normal and shadow */
	PALETTE(config, m_palette, palette_device::BLACK, 4096*2*2);

	NEOGEO_SPRITE_OPTIMZIED(config, m_sprgen, 0).set_screen(m_screen);

	/* audio hardware */
	INPUT_MERGER_ALL_HIGH(config, m_audionmi);
	m_audionmi->output_handler().set_inputline(m_audiocpu, INPUT_LINE_NMI);

	GENERIC_LATCH_8(config, m_soundlatch);
	m_soundlatch->set_separate_acknowledge(false);
	m_soundlatch->data_pending_callback().set(m_audionmi, FUNC(input_merger_device::in_w<0>));

	GENERIC_LATCH_8(config, m_soundlatch2);

	YM2610(config, m_ym, NEOGEO_YM2610_CLOCK);
	m_ym->irq_handler().set_inputline(m_audiocpu, 0);
}


void neogeo_base_state::neogeo_stereo(machine_config &config)
{
	SPEAKER(config, "speaker", 2).front();

	m_ym->add_route(0, "speaker", 0.84, 0);
	m_ym->add_route(0, "speaker", 0.84, 1);
	m_ym->add_route(1, "speaker", 0.98, 0);
	m_ym->add_route(2, "speaker", 0.98, 1);
}


void neogeo_base_state::neogeo_memcard(machine_config &config)
{
	NG_MEMCARD(config, m_memcard, 0);

	m_systemlatch->q_out_cb<2>().set(m_memcard, FUNC(ng_memcard_device::lock1_w));
	m_systemlatch->q_out_cb<3>().set(m_memcard, FUNC(ng_memcard_device::unlock2_w));
	m_systemlatch->q_out_cb<4>().set(m_memcard, FUNC(ng_memcard_device::regsel_w));
}


void ngarcade_base_state::neogeo_arcade(machine_config &config)
{
	neogeo_base(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &ngarcade_base_state::neogeo_main_map);

	m_systemlatch->q_out_cb<5>().set(FUNC(ngarcade_base_state::set_use_cart_audio));
	m_systemlatch->q_out_cb<6>().set(FUNC(ngarcade_base_state::set_save_ram_unlock));

	WATCHDOG_TIMER(config, "watchdog").set_time(attotime::from_ticks(3244030, NEOGEO_MASTER_CLOCK));

	UPD4990A(config, m_upd4990a);

	NVRAM(config, "saveram", nvram_device::DEFAULT_ALL_0);
}


void ngarcade_base_state::neogeo_mono(machine_config &config)
{
	SPEAKER(config, "speaker").front_center();

	m_ym->add_route(0, "speaker", 0.84);
	m_ym->add_route(1, "speaker", 0.49);
	m_ym->add_route(2, "speaker", 0.49);
}

// configurable slot
void mvs_state::cartslot_config(machine_config &config, unsigned count)
{
	for (unsigned i = 0; i < count; i++)
		NEOGEO_CART_SLOT(config, m_slots[i], neogeo_cart, nullptr);
}

void mvs_led_state::mv1(machine_config &config)
{
	neogeo_arcade(config);
	neogeo_stereo(config);
	neogeo_memcard(config);

	NEOGEO_CTRL_EDGE_CONNECTOR(config, m_edge, neogeo_arc_edge, "joy", false);

	NEOGEO_CONTROL_PORT(config, m_ctrl1, neogeo_arc_pin15, nullptr, false);
	NEOGEO_CONTROL_PORT(config, m_ctrl2, neogeo_arc_pin15, nullptr, false);

	cartslot_config(config, 1);

	SOFTWARE_LIST(config, "cart_list").set_original("neogeo");
}

void mvs_led_state::mv1f(machine_config &config)
{
	neogeo_arcade(config);
	neogeo_stereo(config);

	NEOGEO_CTRL_EDGE_CONNECTOR(config, m_edge, neogeo_arc_edge, "joy", false);

	NEOGEO_CONTROL_PORT(config, m_ctrl1, neogeo_arc_pin15, nullptr, false);
	NEOGEO_CONTROL_PORT(config, m_ctrl2, neogeo_arc_pin15, nullptr, false);

	cartslot_config(config, 1);

	SOFTWARE_LIST(config, "cart_list").set_original("neogeo");
}

void mvs_state::mv1fz(machine_config &config)
{
	neogeo_arcade(config);
	neogeo_mono(config);

	NEOGEO_CTRL_EDGE_CONNECTOR(config, m_edge, neogeo_arc_edge, "joy", false);

	cartslot_config(config, 1);

	SOFTWARE_LIST(config, "cart_list").set_original("neogeo");
}

void mvs_led_el_state::mv2f(machine_config &config)
{
	neogeo_arcade(config);
	neogeo_stereo(config);
	neogeo_memcard(config);

	NEOGEO_CTRL_EDGE_CONNECTOR(config, m_edge, neogeo_arc_edge, "joy", false);

	NEOGEO_CONTROL_PORT(config, m_ctrl1, neogeo_arc_pin15, nullptr, false);
	NEOGEO_CONTROL_PORT(config, m_ctrl2, neogeo_arc_pin15, nullptr, false);

	cartslot_config(config, 2);

	SOFTWARE_LIST(config, "cart_list").set_original("neogeo");
}

void mvs_led_el_state::mv4f(machine_config &config)
{
	neogeo_arcade(config);
	neogeo_stereo(config);
	neogeo_memcard(config);

	NEOGEO_CTRL_EDGE_CONNECTOR(config, m_edge, neogeo_arc_edge, "joy", false);

	NEOGEO_CONTROL_PORT(config, m_ctrl1, neogeo_arc_pin15, nullptr, false);
	NEOGEO_CONTROL_PORT(config, m_ctrl2, neogeo_arc_pin15, nullptr, false);

	cartslot_config(config, 4);

	SOFTWARE_LIST(config, "cart_list").set_original("neogeo");
}

void mvs_led_el_state::mv6f(machine_config &config)
{
	neogeo_arcade(config);
	neogeo_stereo(config);
	neogeo_memcard(config);

	NEOGEO_CTRL_EDGE_CONNECTOR(config, m_edge, neogeo_arc_edge, "joy", false);

	NEOGEO_CONTROL_PORT(config, m_ctrl1, neogeo_arc_pin15, nullptr, false);
	NEOGEO_CONTROL_PORT(config, m_ctrl2, neogeo_arc_pin15, nullptr, false);

	cartslot_config(config, 6);

	SOFTWARE_LIST(config, "cart_list").set_original("neogeo");
}

void mvs_led_state::mv1_fixed(machine_config &config)
{
	neogeo_arcade(config);
	neogeo_stereo(config);
	neogeo_memcard(config);

	NEOGEO_CTRL_EDGE_CONNECTOR(config, m_edge, neogeo_arc_edge, "joy", true);

	NEOGEO_CONTROL_PORT(config, m_ctrl1, neogeo_arc_pin15, nullptr, true);
	NEOGEO_CONTROL_PORT(config, m_ctrl2, neogeo_arc_pin15, nullptr, true);
}



void aes_base_state::machine_start()
{
	neogeo_base_state::machine_start();

	m_sprgen->set_fixed_layer_bank_type(neosprite_base_device::FIX_BANKTYPE_STD);
}

void aes_state::machine_start()
{
	aes_base_state::machine_start();

	m_curr_slot = -1;
	set_slot_idx(0);

	// AES has no SFIX ROM and always uses the cartridge's
	m_sprgen->set_fixed_layer_source(1);
}

void aes_state::device_post_load()
{
	aes_base_state::device_post_load();

	if (m_slots[m_curr_slot] && m_slots[m_curr_slot]->get_rom_size() > 0)
		m_bank_cartridge->set_base((uint8_t *)m_slots[m_curr_slot]->get_rom_base() + m_bank_base);
}

// NTSC region
void aes_state::aes_ntsc(machine_config &config)
{
	neogeo_base(config);
	neogeo_stereo(config);
	neogeo_memcard(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &aes_state::aes_main_map);

	NEOGEO_CART_SLOT(config, m_slots[0], neogeo_cart, nullptr);

	NEOGEO_CONTROL_PORT(config, m_ctrl1, neogeo_controls, "joy", false);
	NEOGEO_CONTROL_PORT(config, m_ctrl2, neogeo_controls, "joy", false);

	SOFTWARE_LIST(config, "cart_list").set_original("neogeo").set_filter("AES");
}



/*************************************
 *
 *  Neo-Geo BIOS
 *
 *************************************

    These are the known BIOS Roms, Set options.bios to the one you want.

    The Universe BIOS roms are supported because they're now used on enough PCBs
    to be considered 'in active arcade use' rather than just homebrew hacks.
    Some may be missing, there have been multiple CRCs reported for the same
    revision in some cases (the Universe BIOS has an option for entering / displaying
    a serial number; these should be noted as such if they're added).
    Universe BIOS prior to version 1.3 was incompatible with AES.

    The 'japan-hotel' BIOS is a dump of an MVS which could be found in some japanese
    hotels. it is a custom MVS mobo which uses MVS carts but it hasn't jamma
    connector and it's similar to a console with a coin mechanism, so it's a sort
    of little coin op console installed in hotels.

    The sp-45.sp1 BIOS is the latest 'ASIA' revision. Japan-j3.bin is the latest 'JAPAN'
    revision. Both of them are also used in the sp-4x.sp1 BIOS of the Jamma PCB boards.

    The current Neo-Geo MVS system set (SFIX/SM1/000-LO) used is from a NEO-MVH MV1FS board.
    Other boards (MV1xx / MV2x / MV4x /MV6x) other system sets?

    Zoom ROM (LO)    128K   TC531000CP      1x 128Kx8   Zoom look-up table ROM
    Fix ROM (SFIX)   128K   27C1000         1x 128Kx8   Text layer graphics ROM
    Sound ROM (SM1)  128K   27C1000/23C1000 1x 128Kx8   Z80 program ROM

*/

#define ROM_LOAD16_WORD_SWAP_BIOS(bios,name,offset,length,hash) \
		ROMX_LOAD(name, offset, length, hash, ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(bios))

#define NEOGEO_UNIBIOS_2_2_AND_NEWER(x) \
	ROM_SYSTEM_BIOS( x+ 0, "unibios40", "Universe BIOS (Hack, Ver. 4.0)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( x+ 0, "uni-bios_4_0.rom",  0x00000, 0x020000, CRC(a7aab458) SHA1(938a0bda7d9a357240718c2cec319878d36b8f72) ) /* Universe BIOS v4.0 (hack) */ \
	ROM_SYSTEM_BIOS( x+ 1, "unibios33", "Universe BIOS (Hack, Ver. 3.3)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( x+ 1, "uni-bios_3_3.rom",  0x00000, 0x020000, CRC(24858466) SHA1(0ad92efb0c2338426635e0159d1f60b4473d0785) ) /* Universe BIOS v3.3 (hack) */ \
	ROM_SYSTEM_BIOS( x+ 2, "unibios32", "Universe BIOS (Hack, Ver. 3.2)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( x+ 2, "uni-bios_3_2.rom",  0x00000, 0x020000, CRC(a4e8b9b3) SHA1(c92f18c3f1edda543d264ecd0ea915240e7c8258) ) /* Universe BIOS v3.2 (hack) */ \
	ROM_SYSTEM_BIOS( x+ 3, "unibios31", "Universe BIOS (Hack, Ver. 3.1)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( x+ 3, "uni-bios_3_1.rom",  0x00000, 0x020000, CRC(0c58093f) SHA1(29329a3448c2505e1ff45ffa75e61e9693165153) ) /* Universe BIOS v3.1 (hack) */ \
	ROM_SYSTEM_BIOS( x+ 4, "unibios30", "Universe BIOS (Hack, Ver. 3.0)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( x+ 4, "uni-bios_3_0.rom",  0x00000, 0x020000, CRC(a97c89a9) SHA1(97a5eff3b119062f10e31ad6f04fe4b90d366e7f) ) /* Universe BIOS v3.0 (hack) */ \
	ROM_SYSTEM_BIOS( x+ 5, "unibios23", "Universe BIOS (Hack, Ver. 2.3)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( x+ 5, "uni-bios_2_3.rom",  0x00000, 0x020000, CRC(27664eb5) SHA1(5b02900a3ccf3df168bdcfc98458136fd2b92ac0) ) /* Universe BIOS v2.3 (hack) */ \
	ROM_SYSTEM_BIOS( x+ 6, "unibios23o", "Universe BIOS (Hack, Ver. 2.3, older?)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( x+ 6, "uni-bios_2_3o.rom", 0x00000, 0x020000, CRC(601720ae) SHA1(1b8a72c720cdb5ee3f1d735bbcf447b09204b8d9) ) /* Universe BIOS v2.3 (hack) alt version, withdrawn? */ \
	ROM_SYSTEM_BIOS( x+ 7, "unibios22", "Universe BIOS (Hack, Ver. 2.2)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( x+ 7, "uni-bios_2_2.rom",  0x00000, 0x020000, CRC(2d50996a) SHA1(5241a4fb0c63b1a23fd1da8efa9c9a9bd3b4279c) ) /* Universe BIOS v2.2 (hack) */
#define NEOGEO_UNIBIOS_1_3_TO_2_1(x) \
	ROM_SYSTEM_BIOS( x+ 8, "unibios21", "Universe BIOS (Hack, Ver. 2.1)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( x+ 8, "uni-bios_2_1.rom",  0x00000, 0x020000, CRC(8dabf76b) SHA1(c23732c4491d966cf0373c65c83c7a4e88f0082c) ) /* Universe BIOS v2.1 (hack) */ \
	ROM_SYSTEM_BIOS( x+ 9, "unibios20", "Universe BIOS (Hack, Ver. 2.0)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( x+ 9, "uni-bios_2_0.rom",  0x00000, 0x020000, CRC(0c12c2ad) SHA1(37bcd4d30f3892078b46841d895a6eff16dc921e) ) /* Universe BIOS v2.0 (hack) */ \
	ROM_SYSTEM_BIOS( x+10, "unibios13", "Universe BIOS (Hack, Ver. 1.3)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( x+10, "uni-bios_1_3.rom",  0x00000, 0x020000, CRC(b24b44a0) SHA1(eca8851d30557b97c309a0d9f4a9d20e5b14af4e) ) /* Universe BIOS v1.3 (hack) */
#define NEOGEO_UNIBIOS_1_2_AND_OLDER(x) \
	ROM_SYSTEM_BIOS( x+11, "unibios12", "Universe BIOS (Hack, Ver. 1.2)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( x+11, "uni-bios_1_2.rom",  0x00000, 0x020000, CRC(4fa698e9) SHA1(682e13ec1c42beaa2d04473967840c88fd52c75a) ) /* Universe BIOS v1.2 (hack) */ \
	ROM_SYSTEM_BIOS( x+12, "unibios12o", "Universe BIOS (Hack, Ver. 1.2, older)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( x+12, "uni-bios_1_2o.rom", 0x00000, 0x020000, CRC(e19d3ce9) SHA1(af88ef837f44a3af2d7144bb46a37c8512b67770) ) /* Universe BIOS v1.2 (hack) alt version */ \
	ROM_SYSTEM_BIOS( x+13, "unibios11", "Universe BIOS (Hack, Ver. 1.1)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( x+13, "uni-bios_1_1.rom",  0x00000, 0x020000, CRC(5dda0d84) SHA1(4153d533c02926a2577e49c32657214781ff29b7) ) /* Universe BIOS v1.1 (hack) */ \
	ROM_SYSTEM_BIOS( x+14, "unibios10", "Universe BIOS (Hack, Ver. 1.0)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( x+14, "uni-bios_1_0.rom",  0x00000, 0x020000, CRC(0ce453a0) SHA1(3b4c0cd26c176fc6b26c3a2f95143dd478f6abf9) ) /* Universe BIOS v1.0 (hack) */

/* The number shown in the top right corner (only displayed on the colour test in early versions) should be connected to the revision, the actual numbering / naming here is a mess, possibly due to upgrades where stickers weren't replaced
   The colour of the outside of the test grid appears to be connected to the region / cabinet type (most regions have a single colour, but for the US there are multiple colours, which seem to indicate defaults / intended cabinet type)

   the Cyan US sets will default to
   Game Select: Free
   Game Start Compulsion: Without
   while all others default to
   Game Select: Only When Credited
   Game Start Compulsion: 30 seconds
   They also allow you to set the continue price, rather than the Coin 2 rate (Coin 2 rate doesn't show up, even if you set Dipswitch to 'VS mode')

   The Yellow BIOS ROM does not show the 'Winners Don't Use Drugs' logo for several earlier games (eg. Metal Slug, Neo Bomberman) but does still show other US specific screens (Parental Advisory)
   Later games seem to be unaffected by this and show all screens regardless
*/

#define NEOGEO_BIOS \
	ROM_REGION16_BE( 0x80000, "mainbios", 0 ) \
	ROM_SYSTEM_BIOS( 0, "euro", "Europe MVS (Ver. 2)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( 0, "sp-s2.sp1",         0x00000, 0x020000, CRC(9036d879) SHA1(4f5ed7105b7128794654ce82b51723e16e389543) ) /* 5 Dark Blue - Europe, 1 Slot, has also been found on 2 Slot and 4 Slot (the old hacks were designed for this one) */ \
	ROM_SYSTEM_BIOS( 1, "euro-s1", "Europe MVS (Ver. 1)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( 1, "sp-s.sp1",          0x00000, 0x020000, CRC(c7f2fa45) SHA1(09576ff20b4d6b365e78e6a5698ea450262697cd) ) /* 3 Dark Blue - Europe, 4 Slot */ \
	ROM_SYSTEM_BIOS( 2, "asia-mv1c", "Asia NEO-MVH MV1C" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( 2, "sp-45.sp1",         0x00000, 0x080000, CRC(03cc9f6a) SHA1(cdf1f49e3ff2bac528c21ed28449cf35b7957dc1) ) /* 6 Dark Blue - Latest Asia BIOS (MV1C - mask ROM) */ \
	ROM_SYSTEM_BIOS( 3, "asia-mv1b", "Asia MV1B" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( 3, "sp-s3.sp1",         0x00000, 0x020000, CRC(91b64be3) SHA1(720a3e20d26818632aedf2c2fd16c54f213543e1) ) /* 6 Dark Blue - Asia (MV1B) */ \
	\
	ROM_SYSTEM_BIOS( 4, "us", "US MVS (Ver. 2?)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( 4, "sp-u2.sp1",         0x00000, 0x020000, CRC(e72943de) SHA1(5c6bba07d2ec8ac95776aa3511109f5e1e2e92eb) ) /* 5 Cyan - US, 2 Slot */ \
	ROM_SYSTEM_BIOS( 5, "us-e", "US MVS (Ver. 1)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( 5, "sp-e.sp1",          0x00000, 0x020000, CRC(2723a5b5) SHA1(5dbff7531cf04886cde3ef022fb5ca687573dcb8) ) /* 5 Yellow - US, 6 Slot (V5?) */ \
	ROM_SYSTEM_BIOS( 6, "us-v2", "US MVS (4 slot, Ver 2)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( 6, "sp1-u2",            0x00000, 0x020000, CRC(62f021f4) SHA1(62d372269e1b3161c64ae21123655a0a22ffd1bb) ) /* 3 Cyan - US, 4 slot - also seen with "v2" label*/ \
	ROM_SYSTEM_BIOS( 7, "us-u4", "US MVS (U4)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( 7, "sp1-u4.bin",        0x00000, 0x020000, CRC(1179a30f) SHA1(866817f47aa84d903d0b819d61f6ef356893d16a) ) /* 3 Green - 4 Slot (MV-4F) */ \
	ROM_SYSTEM_BIOS( 8, "us-u3", "US MVS (U3)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( 8, "sp1-u3.bin",        0x00000, 0x020000, CRC(2025b7a2) SHA1(73d774746196f377111cd7aa051cc8bb5dd948b3) ) /* 2 Green - 6 Slot */ \
	\
	ROM_SYSTEM_BIOS( 9, "japan", "Japan MVS (Ver. 3)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( 9, "vs-bios.rom",       0x00000, 0x020000, CRC(f0e8f27d) SHA1(ecf01eda815909f1facec62abf3594eaa8d11075) ) /* 6 Red - Japan, Ver 6 VS BIOS */ \
	ROM_SYSTEM_BIOS( 10, "japan-s2", "Japan MVS (Ver. 2)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( 10, "sp-j2.sp1",        0x00000, 0x020000, CRC(acede59c) SHA1(b6f97acd282fd7e94d9426078a90f059b5e9dd91) ) /* 5 Red - Japan, Older */ \
	ROM_SYSTEM_BIOS( 11, "japan-s1", "Japan MVS (Ver. 1)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( 11, "sp1.jipan.1024",   0x00000, 0x020000, CRC(9fb0abe4) SHA1(18a987ce2229df79a8cf6a84f968f0e42ce4e59d) ) /* 3 Red - Japan, Older */ \
	ROM_SYSTEM_BIOS( 12, "japan-mv1b", "Japan MV1B" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( 12, "japan-j3.bin",     0x00000, 0x020000, CRC(dff6d41f) SHA1(e92910e20092577a4523a6b39d578a71d4de7085) ) /* 6 Red - Latest Japan BIOS (MV1B) */ \
	ROM_SYSTEM_BIOS( 13, "japan-j3a", "Japan MVS (J3, alt)" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( 13, "sp1-j3.bin",       0x00000, 0x020000, CRC(fbc6d469) SHA1(46b2b409b5b68869e367b40c846373623edb632a) ) /* 2 Red - 6 Slot */ \
	ROM_SYSTEM_BIOS( 14, "japan-mv1c", "Japan NEO-MVH MV1C" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( 14, "sp-j3.sp1",        0x00000, 0x080000, CRC(486cb450) SHA1(52c21ea817928904b80745a8c8d15cbad61e1dc1) ) /* 6 Red - Latest Japan BIOS (MV1C - mask ROM) */ \
	\
	ROM_SYSTEM_BIOS( 15, "japan-hotel", "Custom Japanese Hotel" ) \
	ROM_LOAD16_WORD_SWAP_BIOS( 15, "sp-1v1_3db8c.bin", 0x00000, 0x020000, CRC(162f0ebe) SHA1(fe1c6dd3dfcf97d960065b1bb46c1e11cb7bf271) ) /* 6 Red - 'rare MVS found in japanese hotels' shows v1.3 in test mode */ \
	\
	NEOGEO_UNIBIOS_2_2_AND_NEWER(16) \
	NEOGEO_UNIBIOS_1_3_TO_2_1(16) \
	NEOGEO_UNIBIOS_1_2_AND_OLDER(16)


#define NEO_BIOS_AUDIO_64K(name, hash) \
	NEOGEO_BIOS \
	ROM_REGION( 0x20000, "audiobios", 0 ) \
	ROM_LOAD( "sm1.sm1", 0x00000, 0x20000, CRC(94416d67) SHA1(42f9d7ddd6c0931fd64226a60dc73602b2819dcf) ) \
	ROM_REGION( 0x20000, "cslot1:audiocpu", 0 ) \
	ROM_LOAD( name, 0x00000, 0x10000, hash ) \
	ROM_RELOAD(     0x10000, 0x10000 )

#define NEO_BIOS_AUDIO_128K(name, hash) \
	NEOGEO_BIOS \
	ROM_REGION( 0x20000, "audiobios", 0 ) \
	ROM_LOAD( "sm1.sm1", 0x00000, 0x20000, CRC(94416d67) SHA1(42f9d7ddd6c0931fd64226a60dc73602b2819dcf) ) \
	ROM_REGION( 0x30000, "cslot1:audiocpu", 0 ) \
	ROM_LOAD( name, 0x00000, 0x20000, hash ) \
	ROM_RELOAD(     0x10000, 0x20000 )

#define NEO_BIOS_AUDIO_256K(name, hash) \
	NEOGEO_BIOS \
	ROM_REGION( 0x20000, "audiobios", 0 ) \
	ROM_LOAD( "sm1.sm1", 0x00000, 0x20000, CRC(94416d67) SHA1(42f9d7ddd6c0931fd64226a60dc73602b2819dcf) ) \
	ROM_REGION( 0x50000, "cslot1:audiocpu", 0 ) \
	ROM_LOAD( name, 0x00000, 0x40000, hash ) \
	ROM_RELOAD(     0x10000, 0x40000 )

#define NEO_BIOS_AUDIO_512K(name, hash) \
	NEOGEO_BIOS \
	ROM_REGION( 0x20000, "audiobios", 0 ) \
	ROM_LOAD( "sm1.sm1", 0x00000, 0x20000, CRC(94416d67) SHA1(42f9d7ddd6c0931fd64226a60dc73602b2819dcf) ) \
	ROM_REGION( 0x90000, "cslot1:audiocpu", 0 ) \
	ROM_LOAD( name, 0x00000, 0x80000, hash ) \
	ROM_RELOAD(     0x10000, 0x80000 )

#define NEO_BIOS_AUDIO_ENCRYPTED_128K(name, hash) \
	NEOGEO_BIOS \
	ROM_REGION( 0x20000, "audiobios", 0 ) \
	ROM_LOAD( "sm1.sm1", 0x00000, 0x20000, CRC(94416d67) SHA1(42f9d7ddd6c0931fd64226a60dc73602b2819dcf) ) \
	ROM_REGION( 0x90000, "cslot1:audiocpu", ROMREGION_ERASEFF ) \
	ROM_REGION( 0x80000, "cslot1:audiocrypt", 0 ) \
	ROM_LOAD( name, 0x00000, 0x20000, hash )

#define NEO_BIOS_AUDIO_ENCRYPTED_256K(name, hash) \
	NEOGEO_BIOS \
	ROM_REGION( 0x20000, "audiobios", 0 ) \
	ROM_LOAD( "sm1.sm1", 0x00000, 0x20000, CRC(94416d67) SHA1(42f9d7ddd6c0931fd64226a60dc73602b2819dcf) ) \
	ROM_REGION( 0x90000, "cslot1:audiocpu", ROMREGION_ERASEFF ) \
	ROM_REGION( 0x80000, "cslot1:audiocrypt", 0 ) \
	ROM_LOAD( name, 0x00000, 0x40000, hash )

#define NEO_BIOS_AUDIO_ENCRYPTED_512K(name, hash) \
	NEOGEO_BIOS \
	ROM_REGION( 0x20000, "audiobios", 0 ) \
	ROM_LOAD( "sm1.sm1", 0x00000, 0x20000, CRC(94416d67) SHA1(42f9d7ddd6c0931fd64226a60dc73602b2819dcf) ) \
	ROM_REGION( 0x90000, "cslot1:audiocpu", ROMREGION_ERASEFF ) \
	ROM_REGION( 0x80000, "cslot1:audiocrypt", 0 ) \
	ROM_LOAD( name,      0x00000, 0x80000, hash )



#define NEO_SFIX_64K(name, hash) \
	ROM_REGION( 0x20000, "cslot1:fixed", 0 ) \
	ROM_LOAD( name, 0x000000, 0x10000, hash ) \
	ROM_REGION( 0x20000, "fixedbios", 0 ) \
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) ) \
	ROM_Y_ZOOM

#define NEO_SFIX_128K(name, hash) \
	ROM_REGION( 0x20000, "cslot1:fixed", 0 ) \
	ROM_LOAD( name, 0x000000, 0x20000, hash ) \
	ROM_REGION( 0x20000, "fixedbios", 0 ) \
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) ) \
	ROM_Y_ZOOM

#define ROM_Y_ZOOM \
	ROM_REGION( 0x20000, "spritegen:zoomy", 0 ) \
	ROM_LOAD( "000-lo.lo", 0x00000, 0x20000, CRC(5a86cff2) SHA1(5992277debadeb64d1c1c64b0a92d9293eaf7e4a) )



// dummy entry for the dummy bios driver
ROM_START( neogeo )
	NEOGEO_BIOS

	ROM_REGION( 0x100000, "maincpu", ROMREGION_ERASEFF )

	ROM_REGION( 0x20000, "audiobios", 0 )
	ROM_LOAD( "sm1.sm1", 0x00000, 0x20000, CRC(94416d67) SHA1(42f9d7ddd6c0931fd64226a60dc73602b2819dcf) )

	ROM_REGION( 0x50000, "audiocpu", 0 )
	ROM_LOAD( "sm1.sm1", 0x00000, 0x20000, CRC(94416d67) SHA1(42f9d7ddd6c0931fd64226a60dc73602b2819dcf) )

	ROM_Y_ZOOM

	ROM_REGION( 0x20000, "fixed", ROMREGION_ERASEFF )

	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	ROM_REGION( 0x100000, "sprites", ROMREGION_ERASEFF )
ROM_END

#define rom_ng_mv4f rom_neogeo
#define rom_ng_mv2f rom_neogeo
#define rom_ng_mv1 rom_neogeo
#define rom_ng_mv1f rom_neogeo
#define rom_ng_mv1fz rom_neogeo

ROM_START( aes )
	ROM_REGION16_BE( 0x20000, "mainbios", 0 )
	ROM_SYSTEM_BIOS( 0, "asia", "Asia AES" )
	ROM_LOAD16_WORD_SWAP_BIOS( 0, "neo-epo.bin",  0x00000, 0x020000, CRC(d27a71f1) SHA1(1b3b22092f30c4d1b2c15f04d1670eb1e9fbea07) ) // AES Console (Asia?) BIOS
	ROM_SYSTEM_BIOS( 1, "japan", "Japan AES" )
	ROM_LOAD16_WORD_SWAP_BIOS( 1, "neo-po.bin",   0x00000, 0x020000, CRC(16d0c132) SHA1(4e4a440cae46f3889d20234aebd7f8d5f522e22c) ) // AES Console (Japan) BIOS
	ROM_SYSTEM_BIOS( 2, "devel", "Development System ROM" )
	ROM_LOAD16_WORD_SWAP_BIOS( 2, "neodebug.rom", 0x00000, 0x020000, CRC(698ebb7d) SHA1(081c49aa8cc7dad5939833dc1b18338321ea0a07) ) // Official debug (development) ROM, for home-use base board
	NEOGEO_UNIBIOS_2_2_AND_NEWER(3)
	NEOGEO_UNIBIOS_1_3_TO_2_1(3)

	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASEFF )

	ROM_REGION( 0x90000, "audiocpu", ROMREGION_ERASEFF )

	ROM_REGION( 0x20000, "spritegen:zoomy", 0 )
	ROM_LOAD( "000-lo.lo", 0x00000, 0x20000, CRC(5a86cff2) SHA1(5992277debadeb64d1c1c64b0a92d9293eaf7e4a) )

	ROM_REGION( 0x20000, "fixed", ROMREGION_ERASEFF )

	ROM_REGION( 0x900000, "sprites", ROMREGION_ERASEFF )
ROM_END



//    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT        CLASS             INIT
CONS( 1990, neogeo,   0,       0,      mv6f,     neogeo_mvs6, mvs_led_el_state, empty_init, "SNK", "Neo-Geo MV-6F",      MACHINE_IS_BIOS_ROOT | MACHINE_SUPPORTS_SAVE )
CONS( 1990, ng_mv4f,  neogeo,  0,      mv4f,     neogeo_mvs,  mvs_led_el_state, empty_init, "SNK", "Neo-Geo MV-4F",      MACHINE_SUPPORTS_SAVE )
CONS( 1990, ng_mv2f,  neogeo,  0,      mv2f,     neogeo_mvs,  mvs_led_el_state, empty_init, "SNK", "Neo-Geo MV-2F",      MACHINE_SUPPORTS_SAVE )
CONS( 1990, ng_mv1,   neogeo,  0,      mv1,      neogeo,      mvs_led_state,    empty_init, "SNK", "Neo-Geo MV-1",       MACHINE_SUPPORTS_SAVE )
CONS( 1990, ng_mv1f,  ng_mv1,  0,      mv1f,     neogeo,      mvs_led_state,    empty_init, "SNK", "Neo-Geo MV-1F",      MACHINE_SUPPORTS_SAVE )
CONS( 1990, ng_mv1fz, ng_mv1,  0,      mv1fz,    neogeo,      mvs_state,        empty_init, "SNK", "Neo-Geo MV-1FZ",     MACHINE_SUPPORTS_SAVE )
CONS( 1990, aes,      0,       0,      aes_ntsc, aes,         aes_state,        empty_init, "SNK", "Neo-Geo AES (NTSC)", MACHINE_SUPPORTS_SAVE )




// non-configurable slot (to be used for non-softlist sets, until we introduce some 'template' concept)
// a single cart in slot 1, with pre-defined cart type
void mvs_state::cartslot_fixed(machine_config &config, char const *dflt)
{
	NEOGEO_CART_SLOT(config, m_slots[0], neogeo_cart, dflt).set_user_loadable(false);
}

// machine config for one-game fixed config, loaded without using softlists

void mvs_led_state::neobase(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "rom");
}

// used by fatfury2 & ssideki
void mvs_led_state::fatfur2(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "rom_fatfur2");
}

void mvs_state::kizuna4p(machine_config &config)
{
	neogeo_arcade(config);
	neogeo_mono(config);

	NEOGEO_CTRL_EDGE_CONNECTOR(config, m_edge, neogeo_arc_edge_fixed, "kiz4p", true);

	cartslot_fixed(config, "rom");
}

void mvs_led_state::kof97oro(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_kof97oro");
}

void mvs_led_state::kog(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_kog");
}

void mvs_state::irrmaze(machine_config &config)
{
	neogeo_arcade(config);
	neogeo_mono(config);

	NEOGEO_CTRL_EDGE_CONNECTOR(config, m_edge, neogeo_arc_edge_irrmaze, "irrmaze", false);

	config.set_default_layout(layout_irrmaze);

	cartslot_fixed(config, "rom");
}

void mvs_led_state::kof98(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "rom_kof98");
}

void mvs_led_state::mslugx(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "rom_mslugx");
}

void mvs_led_state::kof99(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "sma_kof99");
}

void mvs_led_state::kof99k(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "cmc42_kof99k");
}

void mvs_led_state::garou(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "sma_garou");
}

void mvs_led_state::garouh(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "sma_garouh");
}

void mvs_led_state::garoubl(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_garoubl");
}

void mvs_led_state::mslug3(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "sma_mslug3");
}

void mvs_led_state::mslug3a(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "sma_mslug3a");
}

void mvs_led_state::mslug3h(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "cmc42_mslug3h");
}

void mvs_led_state::mslug3b6(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_mslug3b6");
}

void mvs_led_state::kof2000(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "sma_kof2k");
}

void mvs_led_state::kof2000n(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "cmc50_kof2000n");
}

void mvs_led_state::zupapa(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "cmc42_zupapa");
}

void mvs_led_state::sengoku3(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "cmc42_sengoku3");
}

void mvs_led_state::kof2001(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "cmc50_kof2001");
}

void mvs_led_state::cthd2k3(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_cthd2k3");
}

void mvs_led_state::ct2k3sp(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_ct2k3sp");
}

void mvs_led_state::ct2k3sa(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_ct2k3sa");
}

void mvs_led_state::kof2002(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "k2k2_kof2k2");
}

void mvs_led_state::kof2002b(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_kf2k2b");
}

void mvs_led_state::kf2k2pls(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "k2k2_kf2k2p");
}

void mvs_led_state::kf2k2mp(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_kf2k2mp");
}

void mvs_led_state::kf2k2mp2(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_kf2k2mp2");
}

void mvs_led_state::kof10th(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_kf10th");
}

void mvs_led_state::kf10thep(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_kf10thep");
}

void mvs_led_state::kf2k5uni(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_kf2k5uni");
}

void mvs_led_state::kof2k4se(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_kf2k4se");
}

void mvs_led_state::mslug5(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "pvc_mslug5");
}

void mvs_led_state::ms5plus(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_ms5plus");
}

void mvs_led_state::mslug5b(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_mslug5b");
}

void mvs_led_state::svc(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "pvc_svc");
}

void mvs_led_state::svcboot(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_svcboot");
}

void mvs_led_state::svcplus(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_svcplus");
}

void mvs_led_state::svcplusa(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_svcplusa");
}

void mvs_led_state::svcsplus(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_svcsplus");
}

void mvs_led_state::samsho5(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "k2k2_samsh5");
}

void mvs_led_state::samsho5b(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_samsho5b");
}

void mvs_led_state::kof2003(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "pvc_kf2k3");
}

void mvs_led_state::kof2003h(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "pvc_kf2k3h");
}

void mvs_led_state::kf2k3bl(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_kf2k3bl");
}

void mvs_led_state::kf2k3pl(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_kf2k3pl");
}

void mvs_led_state::kf2k3upl(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_kf2k3upl");
}

void mvs_led_state::samsh5sp(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "k2k2_sams5s");
}

void mvs_led_state::neogeo_mj(machine_config &config)
{
	mv1_fixed(config);
	set_default_bios_tag("japan");

	// no joystick panel
	m_edge->set_default_option(nullptr);
	m_edge->set_fixed(false);

	// P1 mahjong controller
	m_ctrl1->set_default_option("mahjong");
	m_ctrl1->set_fixed(false);

	cartslot_fixed(config, "rom");
}

void mvs_led_state::preisle2(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "cmc42_preisle2");
}

void mvs_led_state::nitd(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "cmc42_nitd");
}

void mvs_led_state::s1945p(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "cmc42_s1945p");
}

void mvs_led_state::lans2004(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_lans2004");
}

void mvs_led_state::pnyaa(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "pcm2_pnyaa");
}

void mvs_led_state::popbounc(machine_config &config)
{
	neogeo_arcade(config);
	neogeo_mono(config);

	NEOGEO_CTRL_EDGE_CONNECTOR(config, m_edge, neogeo_arc_edge_fixed, "dial", true);

	cartslot_fixed(config, "rom");
}

void mvs_led_state::ganryu(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "cmc42_ganryu");
}

void mvs_led_state::bangbead(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "cmc42_bangbead");
}

void mvs_led_state::mslug4(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "pcm2_mslug4");
}

void mvs_led_state::ms4plus(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "pcm2_ms4p");
}

void mvs_led_state::rotd(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "pcm2_rotd");
}

void mvs_led_state::matrim(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "k2k2_matrim");
}

void mvs_led_state::matrimbl(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_matrimbl");
}

void mvs_led_state::jockeygp(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "cmc50_jockeygp");
}

void mvs_led_state::vliner(machine_config &config)
{
	mv1_fixed(config);

	// input handlers are installed at DRIVER_INIT...
	config.device_remove("edge");
	config.device_remove("ctrl1");
	config.device_remove("ctrl2");

	cartslot_fixed(config, "rom_vliner");
}

void mvs_led_state::sbp(machine_config &config)
{
	mv1_fixed(config);
	cartslot_fixed(config, "boot_sbp");
}



/*************************************
 *
 *  Official sets
 *
 *************************************

    About supported sets:
    =====================

    MVS carts (arcade) were released before the AES carts (home)
    The actual codepath taken depends entirely on the BIOS rom, not the roms in the cartridge, which (with
    a few exceptions) support both codepaths.

    The initial AES releases are therefore later revisions of the game, often with bug fixes over the
    initial MVS releases. It isn't uncommon for later production runs and bootlegs to use these newer sets,
    so all of them are supported in MAME.

    Likewise, because the MVS carts were released first (and were produced in higher numbers and generally
    have a lower cost) it's not uncommon for AES units to operate with converted MVS carts, so, with the
    exception of the sets that specifically lock out the AES mode* these sets are all equally suitable
    for MESS.
    * nitd, kof2001 (initial release has no AES code), and a number of the hacked bootlegs.

    The 'MVS ONLY RELEASE' tagged sets were not officially released for the AES (home) system.
    Information about this can be found at 'The NeoGeo Master List' (unofficial) - http://www.neo-geo.com
    and the official NeoGeo museum - http://neogeomuseum.snkplaymore.co.jp/english/index.php
    Several unofficial 'conversions' of these sets can be found across the internet.
    For completeness sake: Some of these have sets have been released for the CD system.


    M1 (sound driver) rom information:
    ==================================
    . Many 'M1' roms contain mirrored data (64k mirrored or 128k mirrored).
    . Found on several early sets (ID 0001 ~ 0045) and on the last sets (ID 0267 ~ 0272).
    . This caused some confusion and incorrect rom sizes.
    . Minimum 'M1' size is 1mbit, maximum size 4mbit.
    . The remaining 64k 'M1' are marked BAD_DUMP.


    S1 (text layer) rom information:
    ================================
    . All 'S1' roms found on prom are 1mbit.
    . The remaining 64k 'S1' are marked BAD_DUMP.


    MULTI PLAY MODE:
    ================
    The NeoGeo has three games which support MULTI PLAY MODE (Riding Hero / League Bowling / Thrash Rally).
    This allows you to 'link' 4 games (MVS) / 2 games (AES) using in game 'Multi-Play' option. To establish
    a link between the carts you have to connect the carts to each other by a communicator cable. The communicator
    cable is a regular headphone cable with stereo pin jack. It has been reported that you can also 'link' MVS <-> AES.

    All three games use a special PROG board for MULTI PLAY MODE support:
    . Riding Hero    (AES - NEO-AEG PROG-HERO   / MVS NEO-MVS PROG-HERO)
    . League Bowling (AES - NEO-AEG PROG-HERO   / MVS NEO-MVS PROG-HERO)
    . Thrash Rally   (AES - NEO-AEG PROG42G-COM / NEO-MVS PROG42G-COM)

    A HD6301V1P MCU on the above boards is used for establishing the 'link'. The MCU has a 4kb internal ROM which
    is not dumped.
    To use the MULTI PLAY MODE on your MVS you have to set the following hardware dips:
    HARD DIP SETTING  4   5   6
    CABINET 1:        OFF OFF ON
    CABINET 2:        OFF ON  ON
    CABINET 3:        ON  OFF ON
    CABINET 4:        ON  ON  ON


    SPHERO SYMPHONY:
    ================
    Several early games have a 'feature' called "sphero symphony". None of the games featuring "sphero symphony"
    uses special hardware. It is something sound based, but what exactly it is (specially arranged samples,
    FM synthesis etc.) is unknown. The AES and MVS releases share the same sound data and driver.

    The AES game-inserts and manuals have an eye-shaped logo with the following text (not to be found on MVS sets):
    sphero
    symphony
    STEREOPHONIC SOUND

    Experience this "LIVE" 3 dimensional sound coming from all around you.

    Games featuring "sphero symphony":
    ID-0006 - Riding Hero
    ID-0007 - Alpha Mission II / ASO II - Last Guardian
    ID-0009 - Ninja Combat
    ID-0010 - Cyber-Lip
    ID-0011 - The Super Spy
    ID-0014 - Mutation Nation
    ID-0017 - Sengoku / Sengoku Denshou
    ID-0018 - Burning Fight
    ID-0020 - Ghost Pilots
    ID-0024 - Last Resort
    ID-0031 - Soccer Brawl
    ID-0033 - Fatal Fury - King of Fighters / Garou Densetsu - shukumei no tatakai
    ID-0034 - Football Frenzy
    ID-0037 - Crossed Swords
    ID-0038 - Thrash Rally
    ID-0039 - King of the Monsters 2 - The Next Thing
    ID-0041 - Baseball Stars 2
    ID-0044 - Art of Fighting / Ryuuko no Ken
    ID-0047 - Fatal Fury 2 / Garou Densetsu 2 - arata-naru tatakai
    ID-0049 - Andro Dunos

*/


// Game specific input definitions

static INPUT_PORTS_START( neogeo_mj )
	PORT_INCLUDE( neogeo )

	PORT_MODIFY("DSW")
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Controller ) ) PORT_DIPLOCATION("SW:3")
	PORT_DIPSETTING(    0x04, DEF_STR( Joystick ) )
	PORT_DIPSETTING(    0x00, "Mahjong Panel" )
INPUT_PORTS_END

static INPUT_PORTS_START( kizuna4p )
	PORT_INCLUDE( neogeo )

	PORT_MODIFY("DSW")
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Players ) ) PORT_DIPLOCATION("SW:2")
	PORT_DIPSETTING(    0x02, "2" )
	PORT_DIPSETTING(    0x00, "4" )
INPUT_PORTS_END

static INPUT_PORTS_START( vliner )
	PORT_INCLUDE( neogeo )

	PORT_MODIFY("DSW")
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("View Payout Table/Big")
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Bet/Small")
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Stop/Double Up")
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Start/Collect")

	PORT_MODIFY("SYSTEM")
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_UNKNOWN ) /* this bit is used.. */
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_UNKNOWN ) /* this bit is used.. */

	PORT_MODIFY("AUDIO_COIN")
	PORT_BIT( 0x003f, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN5")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_COIN1 )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_COIN2 )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Operator Menu") PORT_CODE(KEYCODE_F1)
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("Clear Credit")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("Hopper Out")
	PORT_BIT( 0xff00, IP_ACTIVE_LOW, IPT_UNKNOWN )

	/* not sure what any of these bits are */
	PORT_START("IN6")
	PORT_BIT( 0x0003, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0xffc0, IP_ACTIVE_HIGH, IPT_UNKNOWN )
INPUT_PORTS_END


static INPUT_PORTS_START( jockeygp )
	PORT_INCLUDE( neogeo )

	PORT_MODIFY("SYSTEM")
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_UNKNOWN ) /* game freezes with this bit enabled */
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_UNKNOWN ) /* game freezes with this bit enabled */
INPUT_PORTS_END


/****************************************
 ID-0001
 . NGM-001
 NEO-MVS PROG-NAM / NEO-MVS CHA-32
 . NGH-001
 NEO-AEG PROG-NAM / NEO-AEG CHA-32
****************************************/

ROM_START( nam1975 ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "001-p1.p1", 0x000000, 0x080000, CRC(cc9fc951) SHA1(92f4e6ddeeb825077d92dbb70b50afea985f15c0) ) /* MB834200 */

	NEO_SFIX_128K( "001-s1.s1", CRC(7988ba51) SHA1(bc2f661f381b06b34ac2fa215dd5689d3bf84832) ) /* MB831000 */

	NEO_BIOS_AUDIO_256K( "001-m1.m1", CRC(ba874463) SHA1(a83514f4b20301f84a98699900e2593f1c1b8846) ) /* MB832000 */

	ROM_REGION( 0x080000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "001-v11.v11", 0x000000, 0x080000, CRC(a7c3d5e5) SHA1(e3efc86940f91c53b7724c4566cfc21ea1a7a465) ) /* MB834200 */
	// AES has different label, data is the same: 001-v11.v1

	ROM_REGION( 0x180000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_LOAD( "001-v21.v21", 0x000000, 0x080000, CRC(55e670b3) SHA1(a047049646a90b6db2d1882264df9256aa5a85e5) ) /* MB834200 */
	ROM_LOAD( "001-v22.v22", 0x080000, 0x080000, CRC(ab0d8368) SHA1(404114db9f3295929080b87a5d0106b40da6223a) ) /* MB834000 */
	ROM_LOAD( "001-v23.v23", 0x100000, 0x080000, CRC(df468e28) SHA1(4e5d4a709a4737a87bba4083aeb788f657862f1a) ) /* MB834000 */

	ROM_REGION( 0x300000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "001-c1.c1", 0x000000, 0x80000, CRC(32ea98e1) SHA1(c2fb3fb7dd14523a4b4b7fbdb81f44cb4cc48239) ) /* Plane 0,1 */ /* MB834200 */
	ROM_LOAD16_BYTE( "001-c2.c2", 0x000001, 0x80000, CRC(cbc4064c) SHA1(224c970fd060d841fd430c946ef609bb57b6d78c) ) /* Plane 2,3 */ /* MB834200 */
	ROM_LOAD16_BYTE( "001-c3.c3", 0x100000, 0x80000, CRC(0151054c) SHA1(f24fb501a7845f64833f4e5a461bcf9dc3262557) ) /* Plane 0,1 */ /* MB834200 */
	ROM_LOAD16_BYTE( "001-c4.c4", 0x100001, 0x80000, CRC(0a32570d) SHA1(f108446ec7844fde25f7a4ab454f76d384bf5e52) ) /* Plane 2,3 */ /* MB834200 */
	ROM_LOAD16_BYTE( "001-c5.c5", 0x200000, 0x80000, CRC(90b74cc2) SHA1(89898da36db259180e5261ed45eafc99ca13e504) ) /* Plane 0,1 */ /* MB834200 */
	ROM_LOAD16_BYTE( "001-c6.c6", 0x200001, 0x80000, CRC(e62bed58) SHA1(d05b2903b212a51ee131e52c761b714cb787683e) ) /* Plane 2,3 */ /* MB834200 */
ROM_END

/****************************************
 ID-0002
 . NGM-002
 NEO-MVS PROG-EP / NEO-MVS CHA-32
 . NGH-002
 NEO-AEG PROG-4A / NEO-AEG CHA-32
****************************************/

ROM_START( bstars ) /* MVS VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "002-pg.p1", 0x000000, 0x080000, CRC(c100b5f5) SHA1(4cea9f29ad67288c3eccfa4cf961ee9782e49165) ) /* MB834200 */
	/* also found set with P1 on maskrom with chip label 002-P1 */

	NEO_SFIX_128K( "002-s1.s1", CRC(1a7fd0c6) SHA1(3fc701b7afddab369ddf9dedfbc5e1aaf80b8af3) ) /* MB831000 */

	NEO_BIOS_AUDIO_256K( "002-m1.m1", CRC(4ecaa4ee) SHA1(50abfb8eed6cb4887393089f9ccc76f306ef69b5) ) /* MB832000 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "002-v11.v11", 0x000000, 0x080000, CRC(b7b925bd) SHA1(644c92fa90e74998e73714f74b1e0680ee372a07) ) /* MB834000 */
	ROM_LOAD( "002-v12.v12", 0x080000, 0x080000, CRC(329f26fc) SHA1(2c8009edc88c6b26f7be5beb2b8d260aac394ee1) ) /* MB834000 */
	ROM_LOAD( "002-v13.v13", 0x100000, 0x080000, CRC(0c39f3c8) SHA1(db8f8670639601215707d918d4fb93221460446a) ) /* MB834000 */
	ROM_LOAD( "002-v14.v14", 0x180000, 0x080000, CRC(c7e11c38) SHA1(5abf2a7877e0162c758a4dcf09f183930fa7ef24) ) /* MB834000 */

	ROM_REGION( 0x080000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_LOAD( "002-v21.v21", 0x000000, 0x080000, CRC(04a733d1) SHA1(84159368c0f6de2c3b8121227201cd3422455cf6) ) /* MB834000 */
	/* also found set with different chip labels on V11 and V21 locations; chip labels are : 002-V1 and 002-V2 */

	ROM_REGION( 0x300000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "002-c1.c1", 0x000000, 0x080000, CRC(aaff2a45) SHA1(c91ee72d1d74514df8ec44fca703409d92158ae3) ) /* Plane 0,1 */ /* MB834200 */
	ROM_LOAD16_BYTE( "002-c2.c2", 0x000001, 0x080000, CRC(3ba0f7e4) SHA1(f023b134b9c7994f477867307d2732026033501d) ) /* Plane 2,3 */ /* MB834200 */
	ROM_LOAD16_BYTE( "002-c3.c3", 0x100000, 0x080000, CRC(96f0fdfa) SHA1(9f779a1ae46aeda54d69382b074392ade687f62f) ) /* Plane 0,1 */ /* MB834200 */
	ROM_LOAD16_BYTE( "002-c4.c4", 0x100001, 0x080000, CRC(5fd87f2f) SHA1(a5dd6f26f9485f216c2428ae1792c182beb10dbc) ) /* Plane 2,3 */ /* MB834200 */
	ROM_LOAD16_BYTE( "002-c5.c5", 0x200000, 0x080000, CRC(807ed83b) SHA1(3268e7d4602c3f55f1e0da2c80653d5ae461ef67) ) /* Plane 0,1 */ /* MB834200 */
	ROM_LOAD16_BYTE( "002-c6.c6", 0x200001, 0x080000, CRC(5a3cad41) SHA1(c620d18f4ff32ed5489c941dfc641030a54f1c14) ) /* Plane 2,3 */ /* MB834200 */
ROM_END

ROM_START( bstarsh ) /* AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "002-p1.p1", 0x000000, 0x080000, CRC(3bc7790e) SHA1(50b2fffb1278151bb4849fbe1f8cb23916019815) ) /* MB834200 */

	NEO_SFIX_128K( "002-s1.s1", CRC(1a7fd0c6) SHA1(3fc701b7afddab369ddf9dedfbc5e1aaf80b8af3) ) /* MB831000 */

	NEO_BIOS_AUDIO_256K( "002-m1.m1", CRC(4ecaa4ee) SHA1(50abfb8eed6cb4887393089f9ccc76f306ef69b5) ) /* MB832000 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "002-v11.v11", 0x000000, 0x080000, CRC(b7b925bd) SHA1(644c92fa90e74998e73714f74b1e0680ee372a07) ) /* MB834000 */
	ROM_LOAD( "002-v12.v12", 0x080000, 0x080000, CRC(329f26fc) SHA1(2c8009edc88c6b26f7be5beb2b8d260aac394ee1) ) /* MB834000 */
	ROM_LOAD( "002-v13.v13", 0x100000, 0x080000, CRC(0c39f3c8) SHA1(db8f8670639601215707d918d4fb93221460446a) ) /* MB834000 */
	ROM_LOAD( "002-v14.v14", 0x180000, 0x080000, CRC(c7e11c38) SHA1(5abf2a7877e0162c758a4dcf09f183930fa7ef24) ) /* MB834000 */

	ROM_REGION( 0x080000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_LOAD( "002-v21.v21", 0x000000, 0x080000, CRC(04a733d1) SHA1(84159368c0f6de2c3b8121227201cd3422455cf6) ) /* MB834000 */

	ROM_REGION( 0x300000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "002-c1.c1", 0x000000, 0x080000, CRC(aaff2a45) SHA1(c91ee72d1d74514df8ec44fca703409d92158ae3) ) /* Plane 0,1 */ /* MB834200 */
	ROM_LOAD16_BYTE( "002-c2.c2", 0x000001, 0x080000, CRC(3ba0f7e4) SHA1(f023b134b9c7994f477867307d2732026033501d) ) /* Plane 2,3 */ /* MB834200 */
	ROM_LOAD16_BYTE( "002-c3.c3", 0x100000, 0x080000, CRC(96f0fdfa) SHA1(9f779a1ae46aeda54d69382b074392ade687f62f) ) /* Plane 0,1 */ /* MB834200 */
	ROM_LOAD16_BYTE( "002-c4.c4", 0x100001, 0x080000, CRC(5fd87f2f) SHA1(a5dd6f26f9485f216c2428ae1792c182beb10dbc) ) /* Plane 2,3 */ /* MB834200 */
	ROM_LOAD16_BYTE( "002-c5.c5", 0x200000, 0x080000, CRC(807ed83b) SHA1(3268e7d4602c3f55f1e0da2c80653d5ae461ef67) ) /* Plane 0,1 */ /* MB834200 */
	ROM_LOAD16_BYTE( "002-c6.c6", 0x200001, 0x080000, CRC(5a3cad41) SHA1(c620d18f4ff32ed5489c941dfc641030a54f1c14) ) /* Plane 2,3 */ /* MB834200 */
ROM_END

/****************************************
 ID-0003
 . NGM-003
 NEO-MVS PROG-EP / NEO-MVS CHA-32
 . NGH-003
 NEO-AEG PROG-4B / NEO-AEG CHA-32
****************************************/

ROM_START( tpgolf ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "003-p1.p1", 0x000000, 0x080000, CRC(f75549ba) SHA1(3f7bdf5e2964e921fe1dd87c51a79a1a501fc73f) ) /* TC534200 */
	ROM_LOAD16_WORD_SWAP( "003-p2.p2", 0x080000, 0x080000, CRC(b7809a8f) SHA1(1604c889592c9610668bff296de48a0d6906156d) ) /* TC534200 */

	NEO_SFIX_128K( "003-s1.s1", CRC(7b3eb9b1) SHA1(39cd8bad9f8bfdeb8ac681b5b79ae5aa81c8dd5f) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "003-m1.m1", CRC(4cc545e6) SHA1(8e014b8cab3e5b3995756a4ea52ce49c36866377) ) /* TC531001 */

	ROM_REGION( 0x080000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "003-v11.v11", 0x000000, 0x080000, CRC(ff97f1cb) SHA1(defa249d46ae220f7bfa70746f5202bbbcc3e5fe) ) /* TC534000 */
	// AES has different label, data is the same (also found on MVS): 003-v1.v11

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_LOAD( "003-v21.v21", 0x000000, 0x080000, CRC(d34960c6) SHA1(36d5877d5e42aab943f4d693159f4f3ad8b0addc) ) /* TC534000 */
	// AES has different label, data is the same (also found on MVS): 003-v2.v21
	ROM_LOAD( "003-v22.v22", 0x080000, 0x080000, CRC(9a5f58d4) SHA1(2b580595e1820430a36f06fd3e0e0b8f7d686889) ) /* TC534000 */
	ROM_LOAD( "003-v23.v23", 0x100000, 0x080000, CRC(30f53e54) SHA1(22461f88a56d272b78dbc23204c0c6816200532b) ) /* TC534000 */
	ROM_LOAD( "003-v24.v24", 0x180000, 0x080000, CRC(5ba0f501) SHA1(ca02937a611a2c50c9e4b54f8fd4eaea09259894) ) /* TC534000 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "003-c1.c1", 0x000000, 0x80000, CRC(0315fbaf) SHA1(583c9253219c1026d81ee5e0cf5568683adc2633) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "003-c2.c2", 0x000001, 0x80000, CRC(b4c15d59) SHA1(b0d8ec967f9b8e5216301c10b2d36912abce6515) ) /* Plane 2,3 */ /* TC534200 */
	ROM_LOAD16_BYTE( "003-c3.c3", 0x100000, 0x80000, CRC(8ce3e8da) SHA1(bc6c49b27d498f75a0d1a8c4d0cca75e140b9efc) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "003-c4.c4", 0x100001, 0x80000, CRC(29725969) SHA1(f1407da84919c2b3fe0e8f1fca65934b147c86c7) ) /* Plane 2,3 */ /* TC534200 */
	ROM_LOAD16_BYTE( "003-c5.c5", 0x200000, 0x80000, CRC(9a7146da) SHA1(2fc83d13e3e9565919aab01bf2a1b028f433b547) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "003-c6.c6", 0x200001, 0x80000, CRC(1e63411a) SHA1(ee397e2f679042e87b37d95837af62bb95a72af9) ) /* Plane 2,3 */ /* TC534200 */
	ROM_LOAD16_BYTE( "003-c7.c7", 0x300000, 0x80000, CRC(2886710c) SHA1(1533dd935f0a8f92a0a3c47d1d2bc6d035454244) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "003-c8.c8", 0x300001, 0x80000, CRC(422af22d) SHA1(f67c844c34545de6ea187f5bfdf440dec8518532) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

/****************************************
 ID-0004
 . NGM-004
 NEO-MVS PROG-EP / NEO-MVS CHA-32
 . NGH-004
 NEO-AEG PROG B  / NEO-AEG CHA-32
****************************************/

ROM_START( mahretsu ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "004-p1.p1", 0x000000, 0x080000, CRC(fc6f53db) SHA1(64a62ca4c8fb68954e06121399c9402278bd0467) ) /* TC534200 */

	NEO_SFIX_128K( "004-s1.s1", CRC(2bd05a06) SHA1(876deadd4645373d82a503154eeddf18f440d743) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "004-m1.m1", CRC(c71fbb3b) SHA1(59c58665b53da61352359d191a0569de5dd1f4b3) ) /* TC531001 */

	ROM_REGION( 0x100000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "004-v11.v11", 0x000000, 0x080000, CRC(b2fb2153) SHA1(36e0cc8927b11105de40188af46f6cf532794c10) ) /* CXK384001 */
	ROM_LOAD( "004-v12.v12", 0x080000, 0x080000, CRC(8503317b) SHA1(ab22f1aba1e977ab234a4f1d73dc6ed789dbeb85) ) /* CXK384001 */

	ROM_REGION( 0x180000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_LOAD( "004-v21.v21", 0x000000, 0x080000, CRC(4999fb27) SHA1(2d4926a220ea21bdd5e816bb16f985fff089500a) ) /* CXK384001 */
	ROM_LOAD( "004-v22.v22", 0x080000, 0x080000, CRC(776fa2a2) SHA1(e7d5a362ab7806b7b009700a435c815a20e8ec68) ) /* CXK384001 */
	ROM_LOAD( "004-v23.v23", 0x100000, 0x080000, CRC(b3e7eeea) SHA1(4d1e97f380702a3a06e7f954b4caddd9c4119d8f) ) /* CXK384001 */

	ROM_REGION( 0x200000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "004-c1.c1", 0x000000, 0x80000, CRC(f1ae16bc) SHA1(df68feed4dcba1e1566032b01ebb7b478a1792bf) ) /* Plane 0,1 */ /* MB834200 */
	ROM_LOAD16_BYTE( "004-c2.c2", 0x000001, 0x80000, CRC(bdc13520) SHA1(2bc4c996d019a4c539f6c3188ef18089e54b7efa) ) /* Plane 2,3 */ /* MB834200 */
	ROM_LOAD16_BYTE( "004-c3.c3", 0x100000, 0x80000, CRC(9c571a37) SHA1(21388aeb92bb8e15a55a063701ca9df79e292127) ) /* Plane 0,1 */ /* MB834200 */
	ROM_LOAD16_BYTE( "004-c4.c4", 0x100001, 0x80000, CRC(7e81cb29) SHA1(5036f04df30cf6903bd1a8cc06ff6f015c24a74b) ) /* Plane 2,3 */ /* MB834200 */
ROM_END

/****************************************
 ID-0005
 . NGM-005
 NEO-MVS PROG-EP / NEO-MVS CHA-32
 . NGH-005
 NEO-AEG PROG-4B / NEO-AEG CHA-32
****************************************/

ROM_START( maglord ) /* MVS VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "005-pg1.p1", 0x000000, 0x080000, CRC(bd0a492d) SHA1(d043d3710cf2b0d2b3798008e65e4c7c3ead1af3) ) /* MB834200 */

	NEO_SFIX_128K( "005-s1.s1", CRC(1c5369a2) SHA1(db0dba0a7dced6c9ca929c5abda491b05d84199c) ) /* MB831000 */

	NEO_BIOS_AUDIO_256K( "005-m1.m1", CRC(26259f0f) SHA1(4f3e500093d61585048767dbd9fa09b3911a05d6) ) /* MB832000 */

	ROM_REGION( 0x080000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "005-v11.v11", 0x000000, 0x080000, CRC(cc0455fd) SHA1(a8ff4270e7705e263d25ff0b301f503bccea7e59) ) /* MB834000 */

	ROM_REGION( 0x100000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_LOAD( "005-v21.v21", 0x000000, 0x080000, CRC(f94ab5b7) SHA1(2c16985102e3585e08622d8c54ac5c60425b9ff8) ) /* MB834000 */
	ROM_LOAD( "005-v22.v22", 0x080000, 0x080000, CRC(232cfd04) SHA1(61b66a9decbbd1f500a8c186615e7fd077c6861e) ) /* MB834000 */

	ROM_REGION( 0x300000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "005-c1.c1", 0x000000, 0x80000, CRC(806aee34) SHA1(3c32a0edbbddb694495b510c13979c44b83de8bc) ) /* Plane 0,1 */ /* MB834200 */
	ROM_LOAD16_BYTE( "005-c2.c2", 0x000001, 0x80000, CRC(34aa9a86) SHA1(cec97e1ff7f91158040c629ba75742db82c4ae5e) ) /* Plane 2,3 */ /* MB834200 */
	ROM_LOAD16_BYTE( "005-c3.c3", 0x100000, 0x80000, CRC(c4c2b926) SHA1(478bfafca21f5a1338808251a06ab405e6a9e65f) ) /* Plane 0,1 */ /* MB834200 */
	ROM_LOAD16_BYTE( "005-c4.c4", 0x100001, 0x80000, CRC(9c46dcf4) SHA1(4c05f3dc25777a87578ce09a6cefb3a4cebf3266) ) /* Plane 2,3 */ /* MB834200 */
	ROM_LOAD16_BYTE( "005-c5.c5", 0x200000, 0x80000, CRC(69086dec) SHA1(7fa47f4a765948813ebf366168275dcc3c42e951) ) /* Plane 0,1 */ /* MB834200 */
	ROM_LOAD16_BYTE( "005-c6.c6", 0x200001, 0x80000, CRC(ab7ac142) SHA1(e6ad2843947d35d8e913d2666f87946c1ba7944f) ) /* Plane 2,3 */ /* MB834200 */
ROM_END

ROM_START( maglordh ) /* AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "005-p1.p1", 0x000000, 0x080000, CRC(599043c5) SHA1(43f234b0f89b72b4c6050c40d9daa5c4e96b94ce) ) /* MB834200 */

	NEO_SFIX_128K( "005-s1.s1", CRC(1c5369a2) SHA1(db0dba0a7dced6c9ca929c5abda491b05d84199c) ) /* MB831000 */

	NEO_BIOS_AUDIO_256K( "005-m1.m1", CRC(26259f0f) SHA1(4f3e500093d61585048767dbd9fa09b3911a05d6) ) /* MB832000 */

	ROM_REGION( 0x080000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "005-v11.v11", 0x000000, 0x080000, CRC(cc0455fd) SHA1(a8ff4270e7705e263d25ff0b301f503bccea7e59) ) /* MB834000 */

	ROM_REGION( 0x100000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_LOAD( "005-v21.v21", 0x000000, 0x080000, CRC(f94ab5b7) SHA1(2c16985102e3585e08622d8c54ac5c60425b9ff8) ) /* MB834000 */
	ROM_LOAD( "005-v22.v22", 0x080000, 0x080000, CRC(232cfd04) SHA1(61b66a9decbbd1f500a8c186615e7fd077c6861e) ) /* MB834000 */

	ROM_REGION( 0x300000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "005-c1.c1", 0x000000, 0x80000, CRC(806aee34) SHA1(3c32a0edbbddb694495b510c13979c44b83de8bc) ) /* Plane 0,1 */ /* MB834200 */
	ROM_LOAD16_BYTE( "005-c2.c2", 0x000001, 0x80000, CRC(34aa9a86) SHA1(cec97e1ff7f91158040c629ba75742db82c4ae5e) ) /* Plane 2,3 */ /* MB834200 */
	ROM_LOAD16_BYTE( "005-c3.c3", 0x100000, 0x80000, CRC(c4c2b926) SHA1(478bfafca21f5a1338808251a06ab405e6a9e65f) ) /* Plane 0,1 */ /* MB834200 */
	ROM_LOAD16_BYTE( "005-c4.c4", 0x100001, 0x80000, CRC(9c46dcf4) SHA1(4c05f3dc25777a87578ce09a6cefb3a4cebf3266) ) /* Plane 2,3 */ /* MB834200 */
	ROM_LOAD16_BYTE( "005-c5.c5", 0x200000, 0x80000, CRC(69086dec) SHA1(7fa47f4a765948813ebf366168275dcc3c42e951) ) /* Plane 0,1 */ /* MB834200 */
	ROM_LOAD16_BYTE( "005-c6.c6", 0x200001, 0x80000, CRC(ab7ac142) SHA1(e6ad2843947d35d8e913d2666f87946c1ba7944f) ) /* Plane 2,3 */ /* MB834200 */
ROM_END

/****************************************
 ID-0006
 . NGM-006
 NEO-MVS PROG-HERO / NEO-MVS CHA-32
 . NGH-006
 NEO-AEG PROG-HERO / NEO-AEG CHA-32
****************************************/

ROM_START( ridhero ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "006-p1.p1", 0x000000, 0x080000, CRC(d4aaf597) SHA1(34d35b71adb5bd06f4f1b50ffd9c58ab9c440a84) ) /* MB834200 */

	// dumped from a prototype with external ROM, not 100% confirmed as being the same on a final, or other games (lbowling, trally)
	ROM_REGION( 0x2000, "mcu", 0 )    /* Hitachi HD6301V1 MCU */
	ROM_LOAD( "hd6301v1p_k78.com", 0x0000, 0x2000, CRC(e5cd6306) SHA1(f6bbb8ae562804d67e137290c765c3589fa334c0) )

	NEO_SFIX_128K( "006-s1.s1", CRC(eb5189f0) SHA1(0239c342ea62e73140a2306052f226226461a478) ) /* TC531000 */

	NEO_BIOS_AUDIO_256K( "006-m1.m1", CRC(92e7b4fe) SHA1(d240056cd632f92bdfaa5e0492c09aa9bd7b0471) ) /* MB832000 */

	ROM_REGION( 0x100000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "006-v11.v11", 0x000000, 0x080000, CRC(cdf74a42) SHA1(a17106cc3f9e5c5d52b4def861c0545a98151da2) ) /* MB834000 */
	ROM_LOAD( "006-v12.v12", 0x080000, 0x080000, CRC(e2fd2371) SHA1(cc95297bee7ffbdcb24ac4daeb5307cb39a52067) ) /* MB834000 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_LOAD( "006-v21.v21", 0x000000, 0x080000, CRC(94092bce) SHA1(1a2906271fe6bc396898a756153629a5862930eb) ) /* MB834000 */
	ROM_LOAD( "006-v22.v22", 0x080000, 0x080000, CRC(4e2cd7c3) SHA1(72fb215a4f208a22a764e801186d1643d3d840ca) ) /* MB834000 */
	ROM_LOAD( "006-v23.v23", 0x100000, 0x080000, CRC(069c71ed) SHA1(f450e9f60cd6ef846dbc77993159ec6157fb64e7) ) /* MB834000 */
	ROM_LOAD( "006-v24.v24", 0x180000, 0x080000, CRC(89fbb825) SHA1(656a97c6a8832dab3a5e1577d9cd257b561cc356) ) /* MB834000 */

	ROM_REGION( 0x200000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "006-c1.c1", 0x000000, 0x080000, CRC(4a5c7f78) SHA1(f8f1e6b7841c74368210d52a84307bb28f722a2d) ) /* Plane 0,1 */ /* CXK384000 */
	ROM_LOAD16_BYTE( "006-c2.c2", 0x000001, 0x080000, CRC(e0b70ece) SHA1(e2b750e43cdddcea29d1c9c943a3628117a16a1b) ) /* Plane 2,3 */ /* CXK384000 */
	ROM_LOAD16_BYTE( "006-c3.c3", 0x100000, 0x080000, CRC(8acff765) SHA1(11fe89b9d112d0658c9ddf40d928584de6ea9202) ) /* Plane 0,1 */ /* CXK384000 */
	ROM_LOAD16_BYTE( "006-c4.c4", 0x100001, 0x080000, CRC(205e3208) SHA1(aa2acf2c6f48ffffdcc0c94ddc031acc9e4a2e68) ) /* Plane 2,3 */ /* CXK384000 */
ROM_END

ROM_START( ridheroh )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "006-pg1.p1", 0x000000, 0x080000, BAD_DUMP CRC(52445646) SHA1(647bb31f2f68453c1366cb6e2e867e37d1df7a54) )
	/* Chip label p1h does not exist, renamed temporarily to pg1, marked BAD_DUMP. This needs to be verified. */

	// dumped from a prototype with external ROM, not 100% confirmed as being the same on a final, or other games (lbowling, trally)
	ROM_REGION( 0x2000, "mcu", 0 )    /* Hitachi HD6301V1 MCU */
	ROM_LOAD( "hd6301v1p_k78.com", 0x0000, 0x2000, CRC(e5cd6306) SHA1(f6bbb8ae562804d67e137290c765c3589fa334c0) )

	NEO_SFIX_128K( "006-s1.s1", CRC(eb5189f0) SHA1(0239c342ea62e73140a2306052f226226461a478) ) /* TC531000 */

	NEO_BIOS_AUDIO_256K( "006-m1.m1", CRC(92e7b4fe) SHA1(d240056cd632f92bdfaa5e0492c09aa9bd7b0471) ) /* MB832000 */

	ROM_REGION( 0x100000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "006-v11.v11", 0x000000, 0x080000, CRC(cdf74a42) SHA1(a17106cc3f9e5c5d52b4def861c0545a98151da2) ) /* MB834000 */
	ROM_LOAD( "006-v12.v12", 0x080000, 0x080000, CRC(e2fd2371) SHA1(cc95297bee7ffbdcb24ac4daeb5307cb39a52067) ) /* MB834000 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_LOAD( "006-v21.v21", 0x000000, 0x080000, CRC(94092bce) SHA1(1a2906271fe6bc396898a756153629a5862930eb) ) /* MB834000 */
	ROM_LOAD( "006-v22.v22", 0x080000, 0x080000, CRC(4e2cd7c3) SHA1(72fb215a4f208a22a764e801186d1643d3d840ca) ) /* MB834000 */
	ROM_LOAD( "006-v23.v23", 0x100000, 0x080000, CRC(069c71ed) SHA1(f450e9f60cd6ef846dbc77993159ec6157fb64e7) ) /* MB834000 */
	ROM_LOAD( "006-v24.v24", 0x180000, 0x080000, CRC(89fbb825) SHA1(656a97c6a8832dab3a5e1577d9cd257b561cc356) ) /* MB834000 */

	ROM_REGION( 0x200000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "006-c1.c1", 0x000000, 0x080000, CRC(4a5c7f78) SHA1(f8f1e6b7841c74368210d52a84307bb28f722a2d) ) /* Plane 0,1 */ /* CXK384000 */
	ROM_LOAD16_BYTE( "006-c2.c2", 0x000001, 0x080000, CRC(e0b70ece) SHA1(e2b750e43cdddcea29d1c9c943a3628117a16a1b) ) /* Plane 2,3 */ /* CXK384000 */
	ROM_LOAD16_BYTE( "006-c3.c3", 0x100000, 0x080000, CRC(8acff765) SHA1(11fe89b9d112d0658c9ddf40d928584de6ea9202) ) /* Plane 0,1 */ /* CXK384000 */
	ROM_LOAD16_BYTE( "006-c4.c4", 0x100001, 0x080000, CRC(205e3208) SHA1(aa2acf2c6f48ffffdcc0c94ddc031acc9e4a2e68) ) /* Plane 2,3 */ /* CXK384000 */
ROM_END

/****************************************
 ID-0007
 . NGM-007
 NEO-MVS PROG42G / NEO-MVS CHA42G
 . NGH-007
 NEO-AEG PROG42G / NEO-AEG CHA42G
 NEO-AEG PROG42G / NEO-AEG CHA-8M
 . prototype
 NEO-AEG PROG-EP / NEO-AEG CHA-EP
****************************************/

ROM_START( alpham2 ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "007-p1.p1", 0x000000, 0x080000, CRC(5b266f47) SHA1(8afbf995989f47ad93fea1f31a884afc7228b53a) ) /* TC534200 */
	ROM_LOAD16_WORD_SWAP( "007-p2.p2", 0x080000, 0x020000, CRC(eb9c1044) SHA1(65d3416dcd96663bc4e7cefe90ecb7c1eafb2dda) ) /* TC531024 */

	NEO_SFIX_128K( "007-s1.s1", CRC(85ec9acf) SHA1(39a11974438ad36a2cc84307151b31474c3c5518) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "007-m1.m1", CRC(28dfe2cd) SHA1(1a1a99fb917c6c8db591e3be695ce03f843ee1df) ) /* TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "007-v1.v1", 0x000000, 0x100000, CRC(cd5db931) SHA1(b59f9f2df29f49470312a6cd20f5669b6aaf51ff) ) /* TC538200 */
	ROM_LOAD( "007-v2.v2", 0x100000, 0x100000, CRC(63e9b574) SHA1(1ade4cd0b15c84dd4a0fb7f7abf0885eef3a3f71) ) /* TC538200 */

	ROM_REGION( 0x300000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "007-c1.c1", 0x000000, 0x100000, CRC(8fba8ff3) SHA1(1a682292e99eb91b0edb9771c44bc5e762867e98) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "007-c2.c2", 0x000001, 0x100000, CRC(4dad2945) SHA1(ac85a146276537fed124bda892bb93ff549f1d93) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "007-c3.c3", 0x200000, 0x080000, CRC(68c2994e) SHA1(4f8dfc6e5188942e03b853a2c9f0ea6138dec791) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "007-c4.c4", 0x200001, 0x080000, CRC(7d588349) SHA1(a5ed789d7bbc25be5c5b2d99883b64d379c103a2) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

ROM_START( alpham2p ) /* early prototype - all roms were hand labeled with CRCs, dumps verified against them */ /* AES VERSION*/
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_BYTE( "007_p1_faac.p1", 0x000001, 0x080000, CRC(c763e52a) SHA1(b24acbac255c5ee1a2e92e77cdde6620a24081cf) )
	ROM_LOAD16_BYTE( "007_p2_1813.p2", 0x000000, 0x080000, CRC(7a0b435c) SHA1(40e6f42a92001d9f4e51898dd7489da143b6b74b) )

	NEO_SFIX_128K( "007_s1_36f1.s1", CRC(efc9ae2e) SHA1(a594826b0082fe5a13191673e8d9aa42517230f5) )

	NEO_BIOS_AUDIO_128K( "007_m1_9384.m1", CRC(5976b464) SHA1(ec824567ecc3579f6d86c9d9385710cbaeef16a3) )

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "007_v11_1bb4.v11", 0x000000, 0x080000, CRC(18eaa9e1) SHA1(85c94d8660f8a32e4ca8e015f0bd704208482d68) )
	ROM_LOAD( "007_v12_c8e8.v12", 0x080000, 0x080000, CRC(2487d495) SHA1(49af3c4dc6a38c5158d3641fd8f9a40041b42aa6) )
	ROM_LOAD( "007_v13_09fa.v13", 0x100000, 0x080000, CRC(25e60f25) SHA1(d06b0df872372de38fcf90187195070ac5f8c651) )
	ROM_LOAD( "007_v21_28c2.v21", 0x180000, 0x080000, CRC(ac44b75a) SHA1(7399a05cd4e2c7ecde4a7323d3e189255afe5fc2) )

	ROM_REGION( 0x080000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_COPY( "cslot1:ymsnd:adpcma", 0x180000, 0x00000, 0x80000 )

	ROM_REGION( 0x400000, "cslot1:sprites", 0 ) // note, ROM_LOAD32_BYTE
	ROM_LOAD32_BYTE( "007_c1_210e.c11", 0x000000, 0x80000, CRC(24841639) SHA1(fcc2a349121dad86ffefc44b9f0e8ba616ce0d30) ) /* Plane 0 */
	ROM_LOAD32_BYTE( "007_c2_29ae.c12", 0x000002, 0x80000, CRC(912763ab) SHA1(cedf26d7d85ad140399ee62813c71f35e65498d6) ) /* Plane 2 */
	ROM_LOAD32_BYTE( "007_c3_c873.c13", 0x000001, 0x80000, CRC(0743bde2) SHA1(0d13ad6333909ad3cf10f9ac360f9abf191318de) ) /* Plane 1 */
	ROM_LOAD32_BYTE( "007_c4_2462.c14", 0x000003, 0x80000, CRC(61240212) SHA1(dee36f6604adaeb96e0d761a7256241c066b1cd2) ) /* Plane 3 */
	ROM_LOAD32_BYTE( "007_c5_5f16.c15", 0x200000, 0x80000, CRC(cf9f4c53) SHA1(f979c85f83d9f76e554c2617f85f6d4efca6799c) ) /* Plane 0 */
	ROM_LOAD32_BYTE( "007_c6_8abc.c16", 0x200002, 0x80000, CRC(3d903b19) SHA1(001a8c762336b855fe1df69fe2e605d30a3f00a1) ) /* Plane 2 */
	ROM_LOAD32_BYTE( "007_c7_c983.c17", 0x200001, 0x80000, CRC(e41e3875) SHA1(730aceb8a66cb33d0194b096568f053ad7dc000a) ) /* Plane 1 */
	ROM_LOAD32_BYTE( "007_c8_b599.c18", 0x200003, 0x80000, CRC(4483e2cf) SHA1(47c3364f5c36ae9dc3a49fe37ca60bcee0e73314) ) /* Plane 3 */
ROM_END

/****************************************
 ID-0008
 Sun Shine (prototype) 1990 SNK / Alpha
****************************************/

/****************************************
 ID-0009
 . NGM-009
 NEO-MVS PROG-EP / NEO-MVS CHA-32
 . NGH-009
 NEO-AEG PROG-4A / NEO-AEG CHA-32
****************************************/

ROM_START( ncombat ) /* MVS VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "009-p1.p1", 0x000000, 0x080000, CRC(b45fcfbf) SHA1(3872147dda2d1ba905d35f4571065d87b1958b4a) ) /* TC534200 */
	/* also found set with P1 on TC534200 with chip label 009-PG1 */

	NEO_SFIX_128K( "009-s1.s1", CRC(d49afee8) SHA1(77615f12edf08ae8f1353f7a056a8f3a50d3ebdc) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "009-m1.m1", CRC(b5819863) SHA1(6f2309d51531052dbf7d712993c9e35649db0d84) ) /* TC531001 */

	ROM_REGION( 0x180000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "009-v11.v11", 0x000000, 0x080000, CRC(cf32a59c) SHA1(af5b7fcd8a4aff1307c0a1d937e5f0460c32de79) ) /* TC534000 */
	ROM_LOAD( "009-v12.v12", 0x080000, 0x080000, CRC(7b3588b7) SHA1(a4e6d9d4113ff4ce48b371f65e9187d551821d3b) ) /* TC534000 */
	ROM_LOAD( "009-v13.v13", 0x100000, 0x080000, CRC(505a01b5) SHA1(9426a4f5b31e16f74e72e61951c189a878f211c5) ) /* TC534000 */

	ROM_REGION( 0x080000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_LOAD( "009-v21.v21", 0x000000, 0x080000, CRC(365f9011) SHA1(aebd292214ab280b05ee9e759b7e9a681a099c4a) ) /* TC534000 */

	ROM_REGION( 0x300000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "009-c1.c1", 0x000000, 0x80000, CRC(33cc838e) SHA1(c445c891c0ba4190aa0b472786150620e76df5b4) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "009-c2.c2", 0x000001, 0x80000, CRC(26877feb) SHA1(8f48097fb8e4757f50b6d86219122fbf4b6f87ef) ) /* Plane 2,3 */ /* TC534200 */
	ROM_LOAD16_BYTE( "009-c3.c3", 0x100000, 0x80000, CRC(3b60a05d) SHA1(0a165a17af4834876fcd634599cd2208adc9248f) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "009-c4.c4", 0x100001, 0x80000, CRC(39c2d039) SHA1(8ca6c3f977c43c7abba2a00a0e70f02e2a49f5f2) ) /* Plane 2,3 */ /* TC534200 */
	ROM_LOAD16_BYTE( "009-c5.c5", 0x200000, 0x80000, CRC(67a4344e) SHA1(b325f152c7b2388fc92c5826e1dc99094b9ea749) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "009-c6.c6", 0x200001, 0x80000, CRC(2eca8b19) SHA1(16764ef10e404325ba0a1a2ad3a4c0af287be21f) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

ROM_START( ncombath ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "009-pg1.p1", 0x000000, 0x080000, CRC(8e9f0add) SHA1(d0b908a86a58f2537eea73a431038f1cd74a5a2f) ) /* TC534200 */

	NEO_SFIX_128K( "009-s1.s1", CRC(d49afee8) SHA1(77615f12edf08ae8f1353f7a056a8f3a50d3ebdc) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "009-m1.m1", CRC(b5819863) SHA1(6f2309d51531052dbf7d712993c9e35649db0d84) ) /* TC531001 */

	ROM_REGION( 0x180000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "009-v11.v11", 0x000000, 0x080000, CRC(cf32a59c) SHA1(af5b7fcd8a4aff1307c0a1d937e5f0460c32de79) ) /* TC534000 */
	ROM_LOAD( "009-v12.v12", 0x080000, 0x080000, CRC(7b3588b7) SHA1(a4e6d9d4113ff4ce48b371f65e9187d551821d3b) ) /* TC534000 */
	ROM_LOAD( "009-v13.v13", 0x100000, 0x080000, CRC(505a01b5) SHA1(9426a4f5b31e16f74e72e61951c189a878f211c5) ) /* TC534000 */

	ROM_REGION( 0x080000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_LOAD( "009-v21.v21", 0x000000, 0x080000, CRC(365f9011) SHA1(aebd292214ab280b05ee9e759b7e9a681a099c4a) ) /* TC534000 */

	ROM_REGION( 0x300000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "009-c1.c1", 0x000000, 0x80000, CRC(33cc838e) SHA1(c445c891c0ba4190aa0b472786150620e76df5b4) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "009-c2.c2", 0x000001, 0x80000, CRC(26877feb) SHA1(8f48097fb8e4757f50b6d86219122fbf4b6f87ef) ) /* Plane 2,3 */ /* TC534200 */
	ROM_LOAD16_BYTE( "009-c3.c3", 0x100000, 0x80000, CRC(3b60a05d) SHA1(0a165a17af4834876fcd634599cd2208adc9248f) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "009-c4.c4", 0x100001, 0x80000, CRC(39c2d039) SHA1(8ca6c3f977c43c7abba2a00a0e70f02e2a49f5f2) ) /* Plane 2,3 */ /* TC534200 */
	ROM_LOAD16_BYTE( "009-c5.c5", 0x200000, 0x80000, CRC(67a4344e) SHA1(b325f152c7b2388fc92c5826e1dc99094b9ea749) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "009-c6.c6", 0x200001, 0x80000, CRC(2eca8b19) SHA1(16764ef10e404325ba0a1a2ad3a4c0af287be21f) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

/****************************************
 ID-0010
 . NGM-010
 NEO-MVS PROG-EP / NEO-MVS CHA-32
 . NGH-010
 NEO-AEG PROG-B / NEO-AEG CHA-32
****************************************/

ROM_START( cyberlip )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "010-p1.p1", 0x000000, 0x080000, CRC(69a6b42d) SHA1(6e7cb089de83f1d22cc4a87db5b1a94bf76fb1e8) ) /* TC534200 */

	NEO_SFIX_128K( "010-s1.s1", CRC(79a35264) SHA1(c2819a82adbe1f5e489496e0e03477863a5b7665) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "010-m1.m1", CRC(8be3a078) SHA1(054ec6a061fcc88df1ecbb0a01611a31f37a7709) ) /* TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "010-v11.v11", 0x000000, 0x080000, CRC(90224d22) SHA1(5443ee6f90d80d43194cb4b4f0e08851a59e7784) ) /* TC534000 */
	ROM_LOAD( "010-v12.v12", 0x080000, 0x080000, CRC(a0cf1834) SHA1(8df57a7941bdae7e446a6056039adb012cdde246) ) /* TC534000 */
	ROM_LOAD( "010-v13.v13", 0x100000, 0x080000, CRC(ae38bc84) SHA1(c0937b4f89b8b26c8a0e747b234f44ad6a3bf2ba) ) /* TC534000 */
	ROM_LOAD( "010-v14.v14", 0x180000, 0x080000, CRC(70899bd2) SHA1(8cf01144f0bcf59f09777175ae6b71846b09f3a1) ) /* TC534000 */

	ROM_REGION( 0x080000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_LOAD( "010-v21.v21", 0x000000, 0x080000, CRC(586f4cb2) SHA1(588460031d84c308e3353ecf714db9986425c21c) ) /* TC534000 */

	ROM_REGION( 0x300000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "010-c1.c1", 0x000000, 0x80000, CRC(8bba5113) SHA1(70f0926409ab265da4b8632500d1d32d63cf77cf) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "010-c2.c2", 0x000001, 0x80000, CRC(cbf66432) SHA1(cc529640c475d08330e116ea9c5e5a28b7cd13db) ) /* Plane 2,3 */ /* TC534200 */
	ROM_LOAD16_BYTE( "010-c3.c3", 0x100000, 0x80000, CRC(e4f86efc) SHA1(fa60863d8a7ed4f21d30f91eb1936d0b8329db7a) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "010-c4.c4", 0x100001, 0x80000, CRC(f7be4674) SHA1(b4ad0432d4bb6d5a98e27015910343c964b73ed4) ) /* Plane 2,3 */ /* TC534200 */
	ROM_LOAD16_BYTE( "010-c5.c5", 0x200000, 0x80000, CRC(e8076da0) SHA1(3ec5cc19809dea688041a42b32c13d257576f3da) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "010-c6.c6", 0x200001, 0x80000, CRC(c495c567) SHA1(2f58475fbb5f1adafce027d396fb05dd71e8fb55) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

/****************************************
 ID-0011
 . NGM-011
 NEO-MVS PROG-8MB / NEO-MVS CHA-8M
 . NGH-011
 NEO-AEG PROG-8MB / NEO-AEG CHA-8M
****************************************/

ROM_START( superspy ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "011-p1.p1",  0x000000, 0x080000, CRC(c7f944b5) SHA1(da7560e09187c68f1d9f7656218497b4464c56c9) ) /* MB834200 */
	ROM_LOAD16_WORD_SWAP( "sp2.p2",     0x080000, 0x020000, CRC(811a4faf) SHA1(8169dfaf79f52d80ecec402ce1b1ab9cafb7ebdd) ) /* TC531024 */

	NEO_SFIX_128K( "011-s1.s1", CRC(ec5fdb96) SHA1(8003028025ac7bf531e568add6ba66c02d0b7e84) ) /* MB831000 */

	NEO_BIOS_AUDIO_256K( "011-m1.m1", CRC(ca661f1b) SHA1(4e3cb57db716ec48487c1b070c3a55a5faf40856) ) /* MB832000 */

	ROM_REGION( 0x180000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "011-v11.v11", 0x000000, 0x100000, CRC(5c674d5c) SHA1(d7b9beddeb247b584cea9ca6c43ec6869809b673) ) /* MB838000 */
	ROM_LOAD( "011-v12.v12", 0x100000, 0x080000, CRC(9f513d5a) SHA1(37b04962f0b8e2a74abd35c407337a6151dc4e95) ) /* MB834000 */

	ROM_REGION( 0x080000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_LOAD( "011-v21.v21", 0x000000, 0x080000, CRC(426cd040) SHA1(b2b45189837c8287223c2b8bd4df9525b72a3f16) ) /* MB834000 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "011-c1.c1", 0x000000, 0x100000, CRC(cae7be57) SHA1(43b35b349594535689c358d9f324adda55e5281a) ) /* Plane 0,1 */ /* MB838200 */
	ROM_LOAD16_BYTE( "011-c2.c2", 0x000001, 0x100000, CRC(9e29d986) SHA1(b417763bad1acf76116cd56f4203c2d2677e22e5) ) /* Plane 2,3 */ /* MB838200 */
	ROM_LOAD16_BYTE( "011-c3.c3", 0x200000, 0x100000, CRC(14832ff2) SHA1(1179792d773d97d5e45e7d8f009051d362d72e24) ) /* Plane 0,1 */ /* MB838200 */
	ROM_LOAD16_BYTE( "011-c4.c4", 0x200001, 0x100000, CRC(b7f63162) SHA1(077a81b2bb0a8f17c9df6945078608f74432877a) ) /* Plane 2,3 */ /* MB838200 */
ROM_END

/****************************************
 ID-0012
 unknown
****************************************/

/****************************************
 ID-0013
 unknown
****************************************/

/****************************************
 ID-0014
 . NGM-014
 NEO-MVS PROG42G-1 / NEO-MVS CHA42G-1
 . NGH-014
 NEO-AEG PROG42G-1 / NEO-AEG CHA42G-1
****************************************/

ROM_START( mutnat ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "014-p1.p1", 0x000000, 0x080000, CRC(6f1699c8) SHA1(87206f67a619dede7959230f9ff3701b8b78957a) ) /* CXK384500 */

	NEO_SFIX_128K( "014-s1.s1", CRC(99419733) SHA1(b2524af8704941acc72282aa1d62fd4c93e3e822) ) /* CXK381000 */

	NEO_BIOS_AUDIO_128K( "014-m1.m1", CRC(b6683092) SHA1(623ec7ec2915fb077bf65b4a16c815e071c25259) ) /* CXK381003A */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "014-v1.v1", 0x000000, 0x100000, CRC(25419296) SHA1(c9fc04987c4e0875d276e1a0fb671740b6f548ad) ) /* CXK388000 */
	ROM_LOAD( "014-v2.v2", 0x100000, 0x100000, CRC(0de53d5e) SHA1(467f6040da3dfb1974785e95e14c3f608a93720a) ) /* CXK388000 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "014-c1.c1", 0x000000, 0x100000, CRC(5e4381bf) SHA1(d429a5e09dafd2fb99495658b3652eecbf58f91b) ) /* Plane 0,1 */ /* CXK388000 */
	ROM_LOAD16_BYTE( "014-c2.c2", 0x000001, 0x100000, CRC(69ba4e18) SHA1(b3369190c47771a790c7adffa958ff55d90e758b) ) /* Plane 2,3 */ /* CXK388000 */
	ROM_LOAD16_BYTE( "014-c3.c3", 0x200000, 0x100000, CRC(890327d5) SHA1(47f97bf120a8480758e1f3bb8982be4c5325c036) ) /* Plane 0,1 */ /* CXK388000 */
	ROM_LOAD16_BYTE( "014-c4.c4", 0x200001, 0x100000, CRC(e4002651) SHA1(17e53a5f4708866a120415bf24f3b89621ad0bcc) ) /* Plane 2,3 */ /* CXK388000 */
ROM_END

/****************************************
 ID-0015
 unknown
****************************************/

/****************************************
 ID-0016
 . NGM-016
 NEO-MVS PROG42G   / NEO-MVS CHA42G
 NEO-MVS PROG42G-1 / NEO-MVS CHA42G-1
 . NGH-016
 NEO-AEG PROG42G   / NEO-AEG CHA42G
 NEO-AEG PROG42G-1 / NEO-AEG CHA42G-1
****************************************/

ROM_START( kotm ) /* MVS VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "016-p1.p1", 0x000000, 0x080000, CRC(1b818731) SHA1(b98b1b33c0301fd79aac908f6b635dd00d1cb08d) ) /* TC534200 */
	ROM_LOAD16_WORD_SWAP( "016-p2.p2", 0x080000, 0x020000, CRC(12afdc2b) SHA1(3a95f5910cbb9f17e63ddece995c6e120fa2f622) ) /* TC531024 */

	NEO_SFIX_128K( "016-s1.s1", CRC(1a2eeeb3) SHA1(8d2b96d395020197bc59294b6b0c8d62b1d8d4dd) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "016-m1.m1", CRC(9da9ca10) SHA1(88b915827d529f39c365d3e41197d5461e07a085) ) /* TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "016-v1.v1", 0x000000, 0x100000, CRC(86c0a502) SHA1(7fe2db0c64aefdd14d6c36f7fcd6442591e9a014) ) /* TC538200 */
	ROM_LOAD( "016-v2.v2", 0x100000, 0x100000, CRC(5bc23ec5) SHA1(f4ff5d20587469daa026d5c812739335ce53cfdf) ) /* TC538200 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "016-c1.c1", 0x000000, 0x100000, CRC(71471c25) SHA1(bc8e3fee56b33ef2bac5b4b852339d2fbcd09b7c) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "016-c2.c2", 0x000001, 0x100000, CRC(320db048) SHA1(d6b43834de6f5442e23ca8fb26b3a36e96790d8d) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "016-c3.c3", 0x200000, 0x100000, CRC(98de7995) SHA1(e33edf4d36c82196d2b474e37be180a05976f558) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "016-c4.c4", 0x200001, 0x100000, CRC(070506e2) SHA1(3a2ec365e1d87a9c5ce1ee9bea88402a8eef4ed7) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

ROM_START( kotmh ) /* AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "016-hp1.p1",0x000000, 0x080000, CRC(b774621e) SHA1(7684b2e07163aec68cd083ef1d8900f855f6cb42) ) /* TC534200 */
	ROM_LOAD16_WORD_SWAP( "016-p2.p2", 0x080000, 0x020000, CRC(12afdc2b) SHA1(3a95f5910cbb9f17e63ddece995c6e120fa2f622) ) /* TC531024 */
	/* also found sets with P1 on TC534200 and P2 on TC531024 with chip labels 016-P1 and 016-P2 */

	NEO_SFIX_128K( "016-s1.s1", CRC(1a2eeeb3) SHA1(8d2b96d395020197bc59294b6b0c8d62b1d8d4dd) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "016-m1.m1", CRC(9da9ca10) SHA1(88b915827d529f39c365d3e41197d5461e07a085) ) /* TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "016-v1.v1", 0x000000, 0x100000, CRC(86c0a502) SHA1(7fe2db0c64aefdd14d6c36f7fcd6442591e9a014) ) /* TC538200 */
	ROM_LOAD( "016-v2.v2", 0x100000, 0x100000, CRC(5bc23ec5) SHA1(f4ff5d20587469daa026d5c812739335ce53cfdf) ) /* TC538200 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "016-c1.c1", 0x000000, 0x100000, CRC(71471c25) SHA1(bc8e3fee56b33ef2bac5b4b852339d2fbcd09b7c) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "016-c2.c2", 0x000001, 0x100000, CRC(320db048) SHA1(d6b43834de6f5442e23ca8fb26b3a36e96790d8d) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "016-c3.c3", 0x200000, 0x100000, CRC(98de7995) SHA1(e33edf4d36c82196d2b474e37be180a05976f558) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "016-c4.c4", 0x200001, 0x100000, CRC(070506e2) SHA1(3a2ec365e1d87a9c5ce1ee9bea88402a8eef4ed7) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

/****************************************
 ID-0017
 . NGM-017
 NEO-MVS PROG42G / NEO-MVS CHA42G
 . NGH-017
 NEO-AEG PROG42G  / NEO-AEG CHA42G
 NEO-AEG PRO42G-1 / NEO-AEG CHA42G-1
****************************************/

ROM_START( sengoku ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "017-p1.p1", 0x000000, 0x080000, CRC(f8a63983) SHA1(7a10ecb2f0fd8315641374c065d2602107b09e72) ) /* TC534200 */
	ROM_LOAD16_WORD_SWAP( "017-p2.p2", 0x080000, 0x020000, CRC(3024bbb3) SHA1(88892e1292dd60f35a76f9a22e623d4f0f9693cc) ) /* TC531024 */

	NEO_SFIX_128K( "017-s1.s1", CRC(b246204d) SHA1(73dce64c61fb5bb7e836a8e60f081bb77d80d281) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "017-m1.m1", CRC(9b4f34c6) SHA1(7f3a51f47fcbaa598f5c76bc66e2c53c8dfd852d) ) /* TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "017-v1.v1", 0x000000, 0x100000, CRC(23663295) SHA1(9374a5d9f3de8e6a97c11f07d8b4485ac9d55edb) ) /* TC538200 */
	ROM_LOAD( "017-v2.v2", 0x100000, 0x100000, CRC(f61e6765) SHA1(1c9b287996947319eb3d288c3d82932cf01039db) ) /* TC538200 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "017-c1.c1", 0x000000, 0x100000, CRC(b4eb82a1) SHA1(79879e2ea78c07d04c88dc9a1ad59604b7a078be) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "017-c2.c2", 0x000001, 0x100000, CRC(d55c550d) SHA1(6110f693aa23710939c04153cf5af26493e4a03f) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "017-c3.c3", 0x200000, 0x100000, CRC(ed51ef65) SHA1(e8a8d86e24454948e51a75c883bc6e4091cbf820) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "017-c4.c4", 0x200001, 0x100000, CRC(f4f3c9cb) SHA1(8faafa89dbd0345218f71f891419d2e4e7578200) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

ROM_START( sengokuh ) /* AES VERSION (US) */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "017-hp1.p1", 0x000000, 0x080000, CRC(33eccae0) SHA1(000ccf9a9c73df75eeba3f2c367c3a1a9e0a3a6b) ) /* TC534200 */
	ROM_LOAD16_WORD_SWAP( "017-p2.p2",  0x080000, 0x020000, CRC(3024bbb3) SHA1(88892e1292dd60f35a76f9a22e623d4f0f9693cc) ) /* TC531024 */

	NEO_SFIX_128K( "017-s1.s1", CRC(b246204d) SHA1(73dce64c61fb5bb7e836a8e60f081bb77d80d281) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "017-m1.m1", CRC(9b4f34c6) SHA1(7f3a51f47fcbaa598f5c76bc66e2c53c8dfd852d) ) /* TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "017-v1.v1", 0x000000, 0x100000, CRC(23663295) SHA1(9374a5d9f3de8e6a97c11f07d8b4485ac9d55edb) ) /* TC538200 */
	ROM_LOAD( "017-v2.v2", 0x100000, 0x100000, CRC(f61e6765) SHA1(1c9b287996947319eb3d288c3d82932cf01039db) ) /* TC538200 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "017-c1.c1", 0x000000, 0x100000, CRC(b4eb82a1) SHA1(79879e2ea78c07d04c88dc9a1ad59604b7a078be) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "017-c2.c2", 0x000001, 0x100000, CRC(d55c550d) SHA1(6110f693aa23710939c04153cf5af26493e4a03f) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "017-c3.c3", 0x200000, 0x100000, CRC(ed51ef65) SHA1(e8a8d86e24454948e51a75c883bc6e4091cbf820) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "017-c4.c4", 0x200001, 0x100000, CRC(f4f3c9cb) SHA1(8faafa89dbd0345218f71f891419d2e4e7578200) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

/****************************************
 ID-0018
 . NGM-018
 NEO-MVS PROG42G-1 / NEO-MVS CHA42G-1
 also found on (seen several times; CHA and PROG board are 'manually patched up' with wires and resistors)
 NEO-MVS PROG42G / NEO-MVS CHA42G
 . NGH-018
 NEO-AEG PROG42G / NEO-AEG CHA42G
 NEO-AEG PROG42G-1 / NEO-AEG CHA42-G1
 . prototype
 NEO-AEG PROG-EP / NEO-AEG CHA-EP
****************************************/

ROM_START( burningf ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "018-p1.p1", 0x000000, 0x080000, CRC(4092c8db) SHA1(df194a4ad2c35e0e18bc053ff9284183444a4666) ) /* HN62434 */

	NEO_SFIX_128K( "018-s1.s1", CRC(6799ea0d) SHA1(ec75ef9dfdcb0b123574fc6d81ebaaadfba32fb5) ) /* HN62321 */

	NEO_BIOS_AUDIO_128K( "018-m1.m1", CRC(0c939ee2) SHA1(57d580d3279e66b9fe66bbcc68529d3384a926ff) ) /* HN62321A */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "018-v1.v1", 0x000000, 0x100000, CRC(508c9ffc) SHA1(cd3a97a233a4585f8507116aba85884623cccdc4) ) /* HN62408 */
	ROM_LOAD( "018-v2.v2", 0x100000, 0x100000, CRC(854ef277) SHA1(4b3083b9c80620064cb44e812a787a700e32a6f3) ) /* HN62408 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "018-c1.c1", 0x000000, 0x100000, CRC(25a25e9b) SHA1(3cf02d0662e190678d0530d7b7d3f425209adf83) ) /* Plane 0,1 */ /* HN62408 */
	ROM_LOAD16_BYTE( "018-c2.c2", 0x000001, 0x100000, CRC(d4378876) SHA1(45659aa1755d96b992c977042186e47fff68bba9) ) /* Plane 2,3 */ /* HN62408 */
	ROM_LOAD16_BYTE( "018-c3.c3", 0x200000, 0x100000, CRC(862b60da) SHA1(e2303eb1609f1050f0b4f46693a15e37deb176fb) ) /* Plane 0,1 */ /* HN62408 */
	ROM_LOAD16_BYTE( "018-c4.c4", 0x200001, 0x100000, CRC(e2e0aff7) SHA1(1c691c092a6e2787de4f433b0eb9252bfdaa7e16) ) /* Plane 2,3 */ /* HN62408 */
ROM_END

ROM_START( burningfh ) /* AES VERSION (US) */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "018-hp1.p1", 0x000000, 0x080000, CRC(ddffcbf4) SHA1(c646c4bbdb4e9b32df76c90f582ccd69fcc9f8e7) ) /* HN62434 */

	NEO_SFIX_128K( "018-s1.s1", CRC(6799ea0d) SHA1(ec75ef9dfdcb0b123574fc6d81ebaaadfba32fb5) ) /* HN62321 */

	NEO_BIOS_AUDIO_128K( "018-m1.m1", CRC(0c939ee2) SHA1(57d580d3279e66b9fe66bbcc68529d3384a926ff) ) /* HN62321A */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "018-v1.v1", 0x000000, 0x100000, CRC(508c9ffc) SHA1(cd3a97a233a4585f8507116aba85884623cccdc4) ) /* HN62408 */
	ROM_LOAD( "018-v2.v2", 0x100000, 0x100000, CRC(854ef277) SHA1(4b3083b9c80620064cb44e812a787a700e32a6f3) ) /* HN62408 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "018-c1.c1", 0x000000, 0x100000, CRC(25a25e9b) SHA1(3cf02d0662e190678d0530d7b7d3f425209adf83) ) /* Plane 0,1 */ /* HN62408 */
	ROM_LOAD16_BYTE( "018-c2.c2", 0x000001, 0x100000, CRC(d4378876) SHA1(45659aa1755d96b992c977042186e47fff68bba9) ) /* Plane 2,3 */ /* HN62408 */
	ROM_LOAD16_BYTE( "018-c3.c3", 0x200000, 0x100000, CRC(862b60da) SHA1(e2303eb1609f1050f0b4f46693a15e37deb176fb) ) /* Plane 0,1 */ /* HN62408 */
	ROM_LOAD16_BYTE( "018-c4.c4", 0x200001, 0x100000, CRC(e2e0aff7) SHA1(1c691c092a6e2787de4f433b0eb9252bfdaa7e16) ) /* Plane 2,3 */ /* HN62408 */
ROM_END

ROM_START( burningfpa ) /* later prototype - Sx, Vx and Cx data all matches final game, but with different rom arranagement, Px & Mx unique */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_BYTE( "018_p1_1f28.podd", 0x000001, 0x080000, CRC(f7d15752) SHA1(334b7e5c8e9099d39424e72db66846d0b9e534de) )
	ROM_LOAD16_BYTE( "018_p2_3217.peven", 0x000000, 0x080000, CRC(ffae22fb) SHA1(82f2f81fe96b3c6953985964e12e93e79410ab9e) )

	NEO_SFIX_128K( "018_s1_4491.s1", CRC(6799ea0d) SHA1(ec75ef9dfdcb0b123574fc6d81ebaaadfba32fb5) )

	NEO_BIOS_AUDIO_128K( "018_m1_d13e.m1", CRC(2b0c0415) SHA1(a5a0d5e3ea2ef77b6f5cd14f2907ab2f1aed76b7) )

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "018_v11_6c00.v11", 0x000000, 0x080000, CRC(b55b9670) SHA1(b0ff38b2dca1361ad155e90be3674dbea1c0a53c) )
	ROM_LOAD( "018_v12_8146.v12", 0x080000, 0x080000, CRC(a0bcf260) SHA1(5d1f2613cf4a292557f37e6f44a90b20d11ed8cd) )
	ROM_LOAD( "018_v13_b813.v13", 0x100000, 0x080000, CRC(270f4707) SHA1(29e763acc9fd4b94d8b5d0ad917e20d8a66bbe49) )
	ROM_LOAD( "018_v24_22ee.v24", 0x180000, 0x080000, CRC(924e3d69) SHA1(927091f9a098f00eb303f5567f4ee926bcb170e3) )

	ROM_REGION( 0x400000, "cslot1:sprites", 0 ) // note, ROM_LOAD32_BYTE
	ROM_LOAD32_BYTE( "018_c1_2505.c1", 0x000000, 0x80000, CRC(3a441c6a) SHA1(0347d47a689af67426be42b4c99da101ee01ce11) ) /* Plane 0 */
	ROM_LOAD32_BYTE( "018_c2_3f55.c2", 0x000002, 0x80000, CRC(bb72404b) SHA1(7620fccc50ecacf732bb1382abef42212d369f8c) ) /* Plane 2 */
	ROM_LOAD32_BYTE( "018_c3_a20d.c3", 0x000001, 0x80000, CRC(87bffd2f) SHA1(0f7c835029a3f561861a1a07987dad23a32a899c) ) /* Plane 1 */
	ROM_LOAD32_BYTE( "018_c4_c6e1.c4", 0x000003, 0x80000, CRC(33803163) SHA1(888ba63618a2ca541883a612f0c75ffa96f1c528) ) /* Plane 3 */
	ROM_LOAD32_BYTE( "018_c5_2cd1.c5", 0x200000, 0x80000, CRC(74391952) SHA1(0f7451ff74901e7250214a7604a37add56ad7e1f) ) /* Plane 0 */
	ROM_LOAD32_BYTE( "018_c6_8135.c6", 0x200002, 0x80000, CRC(95e220e6) SHA1(cef4f7c697ab6d20e1b5c85df773d0f1a79e3008) ) /* Plane 2 */
	ROM_LOAD32_BYTE( "018_c7_d009.c7", 0x200001, 0x80000, CRC(41326c0d) SHA1(64bbecb560d2795ae5292776604ea318a9ae53a6) ) /* Plane 1 */
	ROM_LOAD32_BYTE( "018_c8_dc63.c8", 0x200003, 0x80000, CRC(ed3b1f04) SHA1(0cb0d30e294e7ff398d553d0f08befb53ac65269) ) /* Plane 3 */
ROM_END

ROM_START( burningfpb ) /* later prototype, but Sx, Cx, Vx etc. not yet final, Eproms are labeled Ver07 from Feb 1991 */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_BYTE( "p1.bin", 0x000001, 0x080000, CRC(f05ce157) SHA1(87c515ba009ef952942434f48d9a3849d354b6e3) )
	ROM_LOAD16_BYTE( "p2.bin", 0x000000, 0x080000, CRC(768ddc90) SHA1(a125f7bcadf4d8630ae3d08911fe061d15252c76) )

	NEO_SFIX_128K( "s1.bin", CRC(3effc183) SHA1(706bf3649bd08798be031263b9edadb7ecfa122a) )

	NEO_BIOS_AUDIO_128K( "m1.bin", CRC(3031af09) SHA1(72d3776f8c818f5db984e7a1b2fa7cb4a9c8d1ee) )

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "v11.bin", 0x000000, 0x080000, CRC(20b40b70) SHA1(21e0e069650ad4a90567509f2932bb74f3f4205a) )
	ROM_LOAD( "v12.bin", 0x080000, 0x080000, CRC(a0bcf260) SHA1(5d1f2613cf4a292557f37e6f44a90b20d11ed8cd) ) // matches final
	ROM_LOAD( "v13.bin", 0x100000, 0x080000, CRC(a4e0e58a) SHA1(33e9b60e3aaca8e3f625a4238ec768ea04cd58d0) )
	ROM_LOAD( "v24-bad.bin", 0x180000, 0x080000, BAD_DUMP CRC(4389e65a) SHA1(2f18ac9ba390bb09abacefc9ccfa3bd6a45d515b) ) // chip was physically damaged
	ROM_LOAD( "018_v24_22ee.v24", 0x180000, 0x080000, CRC(924e3d69) SHA1(927091f9a098f00eb303f5567f4ee926bcb170e3) ) // load over it with the final data, could be different however

	ROM_REGION( 0x400000, "cslot1:sprites", 0 ) // note, ROM_LOAD32_BYTE
	ROM_LOAD32_BYTE( "c1.bin", 0x000000, 0x80000, CRC(8fd6a9bb) SHA1(8f840385ba8e8a7f3d6260f48f1ecb27ccf05c5c) ) /* Plane 0 */
	ROM_LOAD32_BYTE( "c2.bin", 0x000002, 0x80000, CRC(7c3464b8) SHA1(3f3544a7a70827b81ce6978627b7f21edf0072c7) ) /* Plane 2 */
	ROM_LOAD32_BYTE( "c3.bin", 0x000001, 0x80000, CRC(0ac8f1be) SHA1(66d22a7a58fe86ed747bb27e480254826e1d921b) ) /* Plane 1 */
	ROM_LOAD32_BYTE( "c4.bin", 0x000003, 0x80000, CRC(3af6b235) SHA1(e8903a8ab92e6df29f80b9fe6c5926038e2b2896) ) /* Plane 3 */
	ROM_LOAD32_BYTE( "c5.bin", 0x200000, 0x80000, CRC(707205ab) SHA1(fe6a1122958084a04ffa9892004d3f3513f43fa2) ) /* Plane 0 */
	ROM_LOAD32_BYTE( "c6.bin", 0x200002, 0x80000, CRC(10d0d2cf) SHA1(f1df3b327ab9af71ed6d03a3b788334928c4dbae) ) /* Plane 2 */
	ROM_LOAD32_BYTE( "c7.bin", 0x200001, 0x80000, CRC(def06900) SHA1(4106203f0a14e6c723cacf77ba75ce3aea563a34) ) /* Plane 1 */
	ROM_LOAD32_BYTE( "c8.bin", 0x200003, 0x80000, CRC(6fdc1691) SHA1(3842735456550734b703a0243c38dd83d4722b0c) ) /* Plane 3 */
ROM_END

ROM_START( burningfp ) /* early prototype - all roms were hand labeled with CRCs, dumps verified against them */ /* AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_BYTE( "018_p1_9397.p1", 0x000001, 0x080000, CRC(5b4032e7) SHA1(55df91dad6f484d3d49c28ab5972700bf71a8662) )
	ROM_LOAD16_BYTE( "018_p2_e335.p2", 0x000000, 0x080000, CRC(78762f68) SHA1(12170fc6efe75cb5d32624033d3d341032c97548) )

	NEO_SFIX_128K( "018_s1_9899.s1", CRC(f3d130e8) SHA1(2fdeb93f4bb2a60d391cac2822be41661b1e1795) )

	NEO_BIOS_AUDIO_128K( "018_m1_4586.m1", CRC(470dd5d4) SHA1(4291811b4aefe45261a1ae3631b6999fcd74fb3f) )

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "018_v11_56ac.v11", 0x000000, 0x080000, CRC(dc07ea3c) SHA1(c3e71aaec44ead7ddc581565d16b90030e6db5fd) )
	ROM_LOAD( "018_v12_db95.v12", 0x080000, 0x080000, CRC(f1ae637c) SHA1(02a4c7d4a544350a314ab7b26d8c9d3baa8f5778) )
	ROM_LOAD( "018_v21_98dd.v21", 0x100000, 0x080000, CRC(9f3b4eda) SHA1(7f516923d04daa483b4b99c9babba66505931a34) )

	ROM_REGION( 0x080000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_COPY( "cslot1:ymsnd:adpcma", 0x100000, 0x00000, 0x80000 )

	ROM_REGION( 0x400000, "cslot1:sprites", 0 ) // note, ROM_LOAD32_BYTE
	ROM_LOAD32_BYTE( "018_c1_ba06.c11", 0x000000, 0x80000, CRC(6569018b) SHA1(25040e0a9c2b72900100a22a2a41de5f6c339d8a) ) /* Plane 0 */
	ROM_LOAD32_BYTE( "018_c2_d534.c12", 0x000002, 0x80000, CRC(6949b501) SHA1(d8ee48837faff6cc849046ee8757b2b94d440303) ) /* Plane 2 */
	ROM_LOAD32_BYTE( "018_c3_9d70.c13", 0x000001, 0x80000, CRC(410f653b) SHA1(ce94667721baa7b2c318fc268e3bb9209671c9f5) ) /* Plane 1 */
	ROM_LOAD32_BYTE( "018_c4_bf9d.c14", 0x000003, 0x80000, CRC(d43bf2a5) SHA1(c27985d8973611d02570f469a0d8cb4f5b63b614) ) /* Plane 3 */
	ROM_LOAD32_BYTE( "018_c5_ac9d.c15", 0x200000, 0x80000, CRC(837d09d3) SHA1(d3b06931fca6123604549599544b04529ef34c53) ) /* Plane 0 */
	ROM_LOAD32_BYTE( "018_c6_d2a5.c16", 0x200002, 0x80000, CRC(5fee51e7) SHA1(835c632fa12a1d5b4104cd80b8f686ac80b314a1) ) /* Plane 2 */
	ROM_LOAD32_BYTE( "018_c7_d568.c17", 0x200001, 0x80000, CRC(0f3f0823) SHA1(ec1d681c1795de43d20f30f85956e2473ec39c95) ) /* Plane 1 */
	ROM_LOAD32_BYTE( "018_c8_d256.c18", 0x200003, 0x80000, CRC(67cc9e34) SHA1(dc72a464c1456a4d2f7b992b416a984fb7885e99) ) /* Plane 3 */
ROM_END

/****************************************
 ID-0019
 . NGM-019
 NEO-MVS PROG-HERO / NEO-MVS CHA-32
 . NGH-019
 NEO-AEG PROG-HERO / NEO-AEG CHA-32
****************************************/

ROM_START( lbowling ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "019-p1.p1", 0x000000, 0x080000, CRC(a2de8445) SHA1(893d7ae72b4644123469de143fa35fac1cbcd61e) ) /* TC534200 */

	ROM_REGION( 0x1000, "mcu", 0 )    /* Hitachi HD6301V1 MCU */
	ROM_LOAD( "hd6301v1p_k78.com", 0x0000, 0x1000, NO_DUMP )

	NEO_SFIX_128K( "019-s1.s1", CRC(5fcdc0ed) SHA1(86415077e7adc3ba6153eeb4fb0c62cf36e903fa) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "019-m1.m1", CRC(d568c17d) SHA1(a2e318ed6ad1809c79f3f0853d75e0dd1a2f275c) ) /* TC531001 */

	ROM_REGION( 0x100000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "019-v11.v11", 0x000000, 0x080000, CRC(0fb74872) SHA1(38c555926c77576d63472bc075210c42e9ce13a3) ) /* TC534000 */
	ROM_LOAD( "019-v12.v12", 0x080000, 0x080000, CRC(029faa57) SHA1(7bbaa87e38929ab1e32df5f6a2ec0fd5001e7cdb) ) /* TC534000 */

	ROM_REGION( 0x080000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_LOAD( "019-v21.v21", 0x000000, 0x080000, CRC(2efd5ada) SHA1(8ba70f5f665d566824333075227d9bce1253b8d8) ) /* TC534000 */

	ROM_REGION( 0x100000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "019-c1.c1", 0x000000, 0x080000, CRC(4ccdef18) SHA1(5011e30ec235d0b0a5a513a11d4275777e61acdb) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "019-c2.c2", 0x000001, 0x080000, CRC(d4dd0802) SHA1(82069752028c118d42384a95befde45844f0f247) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

/****************************************
 ID-0020
 . NGM-020
 NEO-MVS PROG8M42 / NEO-MVS CHA-8M
 . NGH-020
 NEO-AEG PROG8M42 / NEO-AEG CHA-8M
****************************************/

ROM_START( gpilots ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "020-p1.p1", 0x000000, 0x080000, CRC(e6f2fe64) SHA1(50ab82517e077727d97668a4df2b9b96d2e78ab6) ) /* TC534200 */
	ROM_LOAD16_WORD_SWAP( "020-p2.p2", 0x080000, 0x020000, CRC(edcb22ac) SHA1(505d2db38ae999b7d436e8f2ff56b81796d62b54) ) /* TC531024 */

	NEO_SFIX_128K( "020-s1.s1", CRC(a6d83d53) SHA1(9a8c092f89521cc0b27a385aa72e29cbaca926c5) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "020-m1.m1", CRC(48409377) SHA1(0e212d2c76856a90b2c2fdff675239525972ac43) ) /* TC531001 */

	ROM_REGION( 0x180000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "020-v11.v11", 0x000000, 0x100000, CRC(1b526c8b) SHA1(2801868d2badcf8aaf5d490e010e4049d81d7bc1) ) /* TC538200 */
	ROM_LOAD( "020-v12.v12", 0x100000, 0x080000, CRC(4a9e6f03) SHA1(d3ac11f333b03d8a318921bdaefb14598e289a14) ) /* TC534200 */

	ROM_REGION( 0x080000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_LOAD( "020-v21.v21", 0x000000, 0x080000, CRC(7abf113d) SHA1(5b2a0e70f2eaf4638b44702dacd4cb17838fb1d5) ) /* TC534200 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "020-c1.c1", 0x000000, 0x100000, CRC(bd6fe78e) SHA1(50b704862cd79d64fa488e621b079f6e413c33bc) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "020-c2.c2", 0x000001, 0x100000, CRC(5f4a925c) SHA1(71c5ef8141234daaa7025427a6c65e79766973a5) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "020-c3.c3", 0x200000, 0x100000, CRC(d1e42fd0) SHA1(f0d476aebbdc2ce008f5f0783be86d295b24aa44) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "020-c4.c4", 0x200001, 0x100000, CRC(edde439b) SHA1(79be7b10ecdab54c2f77062b8f5fda0e299fa982) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

ROM_START( gpilotsh ) /* AES VERSION (US) */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "020-hp1.p1", 0x000000, 0x080000, CRC(7cdb01ce) SHA1(32cae2ddf5e26fb7e8a09132e600220db82df3b8) ) /* TC534200 */
	ROM_LOAD16_WORD_SWAP( "020-p2.p2",  0x080000, 0x020000, CRC(edcb22ac) SHA1(505d2db38ae999b7d436e8f2ff56b81796d62b54) ) /* TC531024 */

	NEO_SFIX_128K( "020-s1.s1", CRC(a6d83d53) SHA1(9a8c092f89521cc0b27a385aa72e29cbaca926c5) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "020-m1.m1", CRC(48409377) SHA1(0e212d2c76856a90b2c2fdff675239525972ac43) ) /* TC531001 */

	ROM_REGION( 0x180000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "020-v11.v11", 0x000000, 0x100000, CRC(1b526c8b) SHA1(2801868d2badcf8aaf5d490e010e4049d81d7bc1) ) /* TC538200 */
	ROM_LOAD( "020-v12.v12", 0x100000, 0x080000, CRC(4a9e6f03) SHA1(d3ac11f333b03d8a318921bdaefb14598e289a14) ) /* TC534200 */

	ROM_REGION( 0x080000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_LOAD( "020-v21.v21", 0x000000, 0x080000, CRC(7abf113d) SHA1(5b2a0e70f2eaf4638b44702dacd4cb17838fb1d5) ) /* TC534200 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "020-c1.c1", 0x000000, 0x100000, CRC(bd6fe78e) SHA1(50b704862cd79d64fa488e621b079f6e413c33bc) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "020-c2.c2", 0x000001, 0x100000, CRC(5f4a925c) SHA1(71c5ef8141234daaa7025427a6c65e79766973a5) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "020-c3.c3", 0x200000, 0x100000, CRC(d1e42fd0) SHA1(f0d476aebbdc2ce008f5f0783be86d295b24aa44) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "020-c4.c4", 0x200001, 0x100000, CRC(edde439b) SHA1(79be7b10ecdab54c2f77062b8f5fda0e299fa982) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

ROM_START( gpilotsp ) /* prototype - different revisions of the 68k and z80 programs, gfx + samples different ROM arrangement but same data, rest identical */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_BYTE( "p1", 0x000001, 0x080000, CRC(c42125b4) SHA1(b5879d06152bba0c0211c44fff4837b54405e7ee) )
	ROM_LOAD16_BYTE( "p2", 0x000000, 0x080000, CRC(5a4db1f3) SHA1(535b6abd266bba174d20bfff17b2668c6a36d1e6) )

	NEO_SFIX_128K( "s1", CRC(a6d83d53) SHA1(9a8c092f89521cc0b27a385aa72e29cbaca926c5) )

	NEO_BIOS_AUDIO_128K( "m1", CRC(32a108cf) SHA1(4d38a283376725e1c1aca0d5aeedd4862146ba1f) )

	ROM_REGION( 0x180000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "v11", 0x000000, 0x080000, CRC(8cc44140) SHA1(0a88d8962e057bf0cabd4819d4ba67e093c49b82) )
	ROM_LOAD( "v12", 0x080000, 0x080000, CRC(415c61cd) SHA1(638e5fd9b02b4553df42f00188a7ac52a5a7bf84) )
	ROM_LOAD( "v13", 0x100000, 0x080000, CRC(4a9e6f03) SHA1(d3ac11f333b03d8a318921bdaefb14598e289a14) )

	ROM_REGION( 0x080000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_LOAD( "v21", 0x000000, 0x080000, CRC(7abf113d) SHA1(5b2a0e70f2eaf4638b44702dacd4cb17838fb1d5) )

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD32_BYTE( "c11", 0x000000, 0x80000, CRC(7a5158b3) SHA1(01dd241534c16e81ce1113a53495606d8f907e1e) ) /* Plane 0 */
	ROM_LOAD32_BYTE( "c12", 0x000002, 0x80000, CRC(41e67a5a) SHA1(21df717cea93c0c4c5773e98ffefcdc2a06b83c4) ) /* Plane 2 */
	ROM_LOAD32_BYTE( "c13", 0x000001, 0x80000, CRC(5f80c0d0) SHA1(48a5c081de88811b170da794634f1a82e42bd472) ) /* Plane 1 */
	ROM_LOAD32_BYTE( "c14", 0x000003, 0x80000, CRC(61227b93) SHA1(563283401cf648edd7cc881969a1107d78f31b75) ) /* Plane 3 */
	ROM_LOAD32_BYTE( "c15", 0x200000, 0x80000, CRC(96de9bb5) SHA1(c3d42b1dd27cc153310a6fc14a02380b6b673574) ) /* Plane 0 */
	ROM_LOAD32_BYTE( "c16", 0x200002, 0x80000, CRC(2b904c85) SHA1(9e162449af1f37cc22900df09e37226e74d62d35) ) /* Plane 2 */
	ROM_LOAD32_BYTE( "c17", 0x200001, 0x80000, CRC(c0d7517d) SHA1(db60e23d1b3323ad757c1fda009f2c08abd5630b) ) /* Plane 1 */
	ROM_LOAD32_BYTE( "c18", 0x200003, 0x80000, CRC(75569e35) SHA1(fac74ec97c6612bffdfd1239c115d6247eb7fabc) ) /* Plane 3 */
ROM_END

/****************************************
 ID-0021
 . NGM-021
 NEO-MVS PROG-EP / NEO-MVS CHA-32
 . NGH-021
 NEO-AEG PROG B  / NEO-AEG CHA-32
****************************************/

ROM_START( joyjoy ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "021-p1.p1", 0x000000, 0x080000, CRC(39c3478f) SHA1(06ebe54c9c4e14c5c31e770013d58b7162359ecc) ) /* MB834200 */

	NEO_SFIX_128K( "021-s1.s1", CRC(6956d778) SHA1(e3757776d60dc07d8e07c9ca61b223b14732f860) ) /* MB831000 */

	NEO_BIOS_AUDIO_256K( "021-m1.m1", CRC(5a4be5e8) SHA1(552f025ce0d51c25f42e1a81cf0d08376ca5475d) ) /* MB832000 */

	ROM_REGION( 0x080000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "021-v11.v11", 0x000000, 0x080000, CRC(66c1e5c4) SHA1(7e85420021d4c39c36ed75a1cec567c5610ffce0) ) /* MB834000 */

	ROM_REGION( 0x080000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_LOAD( "021-v21.v21", 0x000000, 0x080000, CRC(8ed20a86) SHA1(d15cba5eac19ea56fdd4877541f1bb3eb755ebba) ) /* MB834000 */

	ROM_REGION( 0x100000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "021-c1.c1", 0x000000, 0x080000, CRC(509250ec) SHA1(d6ddb16d8222088f153a85a905bcb99541a5f2cf) ) /* Plane 0,1 */ /* MB834200 */
	ROM_LOAD16_BYTE( "021-c2.c2", 0x000001, 0x080000, CRC(09ed5258) SHA1(6bf50cd10236e29146b49e714a0e0ebcfe30a682) ) /* Plane 2,3 */ /* MB834200 */
ROM_END

/****************************************
 ID-0022
 . ALM-001
 NEO-MVS PROG8M42  / NEO-MVS CHA-8M
 . ALH-001
 NEO-AEG PROG 8M42 / NEO-AEG CHA-8M
****************************************/

ROM_START( bjourney ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "022-p1.p1", 0x000000, 0x100000, CRC(6a2f6d4a) SHA1(b8ca548e56f1c7abcdce415ba7329e0cf698ee13) ) /* TC538200 */

	NEO_SFIX_128K( "022-s1.s1", CRC(843c3624) SHA1(dbdf86c193b7c1d795f8c21f2c103c1d3e18abbe) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "022-m1.m1", CRC(8e1d4ab6) SHA1(deabc11ab81e7e68a3e041c03a127ae28d0d7264) ) /* TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "022-v11.v11", 0x000000, 0x100000, CRC(2cb4ad91) SHA1(169ec7303c4275155a66a88cc08270c24132bb36) ) /* TC538200 */
	ROM_LOAD( "022-v22.v22", 0x100000, 0x100000, CRC(65a54d13) SHA1(a591fbcedca8f679dacbebcd554e3aa3fd163e92) ) /* TC538200 */

	ROM_REGION( 0x300000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "022-c1.c1", 0x000000, 0x100000, CRC(4d47a48c) SHA1(6e282285be72583d828e7765b1c1695ecdc44777) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "022-c2.c2", 0x000001, 0x100000, CRC(e8c1491a) SHA1(c468d2556b3de095aaa05edd1bc16d71303e9478) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "022-c3.c3", 0x200000, 0x080000, CRC(66e69753) SHA1(974b823fc62236fbc23e727f25b61a805a707a9e) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "022-c4.c4", 0x200001, 0x080000, CRC(71bfd48a) SHA1(47288be69e6992d09ebef108b4de9ffab6293dc8) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

ROM_START( bjourneyh ) /* AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "022-hp1.p1", 0x000000, 0x100000, CRC(62cbe7b2) SHA1(f9a8fd98702c623ae793804ba50d09751e3fee4c) ) /* TC538200 */
	/* also found sets with P1 on TC5358200; chip label is 022-P1 */

	NEO_SFIX_128K( "022-s1.s1", CRC(843c3624) SHA1(dbdf86c193b7c1d795f8c21f2c103c1d3e18abbe) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "022-m1.m1", CRC(8e1d4ab6) SHA1(deabc11ab81e7e68a3e041c03a127ae28d0d7264) ) /* TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "022-v11.v11", 0x000000, 0x100000, CRC(2cb4ad91) SHA1(169ec7303c4275155a66a88cc08270c24132bb36) ) /* TC538200 */
	ROM_LOAD( "022-v22.v22", 0x100000, 0x100000, CRC(65a54d13) SHA1(a591fbcedca8f679dacbebcd554e3aa3fd163e92) ) /* TC538200 */

	ROM_REGION( 0x300000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "022-c1.c1", 0x000000, 0x100000, CRC(4d47a48c) SHA1(6e282285be72583d828e7765b1c1695ecdc44777) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "022-c2.c2", 0x000001, 0x100000, CRC(e8c1491a) SHA1(c468d2556b3de095aaa05edd1bc16d71303e9478) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "022-c3.c3", 0x200000, 0x080000, CRC(66e69753) SHA1(974b823fc62236fbc23e727f25b61a805a707a9e) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "022-c4.c4", 0x200001, 0x080000, CRC(71bfd48a) SHA1(47288be69e6992d09ebef108b4de9ffab6293dc8) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

/****************************************
 ID-0023
 . NGM-023
 NEO-MVS PROG42G / NEO-MVS CHA42G
 NEO-MVS PROGTOP / NEO-MVS CHA-256
 Boards used for the Korean release
 . NGH-023
 NEO-AEG PROG42G-1 / NEO-AEG CHA42G-1
****************************************/

ROM_START( quizdais ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "023-p1.p1", 0x000000, 0x100000, CRC(c488fda3) SHA1(4cdf2f1837fffd720efef42f81f933bdf2ef1402) ) /* TC538200 */

	NEO_SFIX_128K( "023-s1.s1", CRC(ac31818a) SHA1(93c8d67a93606a2e02f12ca4cab849dc3f3de286) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "023-m1.m1", CRC(2a2105e0) SHA1(26fc13556fda2dbeb7b5b035abd994e302dc7662) ) /* TC531001 */

	ROM_REGION( 0x100000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "023-v1.v1", 0x000000, 0x100000, CRC(a53e5bd3) SHA1(cf115c6478ce155d889e6a5acb962339e08e024b) ) /* TC538200 */

	ROM_REGION( 0x200000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "023-c1.c1", 0x000000, 0x100000, CRC(2999535a) SHA1(0deabf771039987b559edc2444eea741bd7ba861) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "023-c2.c2", 0x000001, 0x100000, CRC(876a99e6) SHA1(8d1dcfc0927d7523f8be8203573192406ec654b4) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

ROM_START( quizdaisk ) /* KOREAN VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "123-p1.p1", 0x000000, 0x100000, CRC(a6f35eae) SHA1(edd3fd5ba8eae2231e2b0a6605fa00e5c6de094a) )

	NEO_SFIX_128K( "123-s1.s1", CRC(53de938a) SHA1(5024fee3b245f8a069d7ecfa6f033b70ed1a5fce) )

	NEO_BIOS_AUDIO_128K( "123-m1.m1", CRC(d67f53f9) SHA1(73a1bd175ae29dd957a907a046884f8715bd0a34) )

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "123-v1.v1", 0x000000, 0x200000, CRC(986f4af9) SHA1(9e15d2142ec5e5d076582dc1cecfd724b0924f54) )

	ROM_REGION( 0x200000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "123-c1.c1", 0x000000, 0x100000, CRC(e579a606) SHA1(b9430ec157902f0707e5d52e69bd5d93792e7118) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "123-c2.c2", 0x000001, 0x100000, CRC(e182c837) SHA1(a8f7648bf21ebd3efe3a49606b53220815a60d0f) ) /* Plane 2,3 */
ROM_END

/****************************************
 ID-0024
 . NGM-024
 NEO-MVS PROG42G-1 / NEO-MVS CHA42G-1
 . NGH-024
 NEO-AEG PROG42G-1 / NEO-AEG CHA42G-1
 . prototype
 NEO-MVS PROG-EP / NEO-MVS CHA-EPG
****************************************/

ROM_START( lresort )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "024-p1.p1", 0x000000, 0x080000, CRC(89c4ab97) SHA1(3a1817c427185ea1b44fe52f009c00b0a9007c85) ) /* TC534200 */

	NEO_SFIX_128K( "024-s1.s1", CRC(5cef5cc6) SHA1(9ec305007bdb356e9f8f279beae5e2bcb3f2cf7b) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "024-m1.m1", CRC(cec19742) SHA1(ab6c6ba7737e68d2420a0617719c6d4c89039c45) ) /* TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "024-v1.v1", 0x000000, 0x100000, CRC(efdfa063) SHA1(e4609ecbcc1c820758f229da5145f51285b50555) ) /* TC538200 */
	ROM_LOAD( "024-v2.v2", 0x100000, 0x100000, CRC(3c7997c0) SHA1(8cb7e8e69892b19d318978370dbc510d51b06a69) ) /* TC538200 */

	ROM_REGION( 0x300000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "024-c1.c1", 0x000000, 0x100000, CRC(3617c2dc) SHA1(8de2643a618272f8aa1c705363edb007f4a5f5b7) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "024-c2.c2", 0x000001, 0x100000, CRC(3f0a7fd8) SHA1(d0c9c7a9dde9ce175fb243d33ec11fa719d0158c) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "024-c3.c3", 0x200000, 0x080000, CRC(e9f745f8) SHA1(bbe6141da28b0db7bf5cf321d69b7e613e2414d7) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "024-c4.c4", 0x200001, 0x080000, CRC(7382fefb) SHA1(e916dec5bb5462eb9ae9711f08c7388937abb980) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

ROM_START( lresortp ) /* prototype - all roms were hand labeled with CRCs */ /* MVS VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_BYTE( "024_p1_5937.podd",  0x000001, 0x080000, CRC(8e6008ee) SHA1(6779663118782207156dc9fa9e24e81e30c6391c) )
	ROM_LOAD16_BYTE( "024_p2_8d37.peven", 0x000000, 0x080000, CRC(6d9ee90f) SHA1(ecd42182988092417bc96db8301ea408e47735f2) )

	NEO_SFIX_128K( "024_s1_22fe.s1", CRC(5cef5cc6) SHA1(9ec305007bdb356e9f8f279beae5e2bcb3f2cf7b) )

	NEO_BIOS_AUDIO_128K( "024_m1_fc7a.m1", CRC(22122875) SHA1(540c21559163381467679f836cb068adaf526659) )

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "024_v11_b085.v11", 0x000000, 0x080000, CRC(0722da38) SHA1(66a9b463d5277908b3a01c03de82b3de9118f2cb) ) // 024_v11_b085.bin            024-v1.v1    [1/2]      IDENTICAL
	ROM_LOAD( "024_v12_d3b7.v12", 0x080000, 0x080000, CRC(670ce3ec) SHA1(9004aa85d4a9b0ecf9cf9357b073ed55a98fdb02) ) // 024_v12_d3b7.bin            024-v1.v1    [2/2]      IDENTICAL
	ROM_LOAD( "024_v13_a31e.v13", 0x100000, 0x080000, CRC(2e39462b) SHA1(b0a9b1a3377bf0369f3020192505c46ca52927d6) ) // 024_v13_a31e.bin            024-v2.v2    [1/2]      IDENTICAL
	ROM_LOAD( "024_v24_2f0f.v24", 0x180000, 0x080000, CRC(7944754f) SHA1(d42a46c5127c6c62041ebffb0007af8a24abd360) ) // 024_v24_2f0f.bin            024-v2.v2    [2/2]      IDENTICAL

	ROM_REGION( 0x400000, "cslot1:sprites", 0 ) // note, ROM_LOAD32_BYTE
	ROM_LOAD32_BYTE( "024_c1_b764.c1", 0x000000, 0x80000, CRC(677749ec) SHA1(6f94675e037956a380652ab1056e6f1dec605bec) ) /* Plane 0 */
	ROM_LOAD32_BYTE( "024_c2_1951.c2", 0x000002, 0x80000, CRC(104d7b59) SHA1(404e8776ee8df4ca282eb7b747759af8628ddca1) ) /* Plane 2 */
	ROM_LOAD32_BYTE( "024_c3_0f63.c3", 0x000001, 0x80000, CRC(b0965a74) SHA1(e8026dd4f722ccab9c913261d09ab8843ef56a0e) ) /* Plane 1 */
	ROM_LOAD32_BYTE( "024_c4_c5b5.c4", 0x000003, 0x80000, CRC(dcfc3860) SHA1(8443b455ae8de656adab57f3b7e68919f22d3b9d) ) /* Plane 3 */
	ROM_LOAD32_BYTE( "024_c5_dd03.c5", 0x200000, 0x80000, CRC(50322397) SHA1(3308fbe48ad165b9894a52fc3a8d9898bbbc0c0e) ) /* Plane 0 */
	ROM_LOAD32_BYTE( "024_c6_c81a.c6", 0x200002, 0x80000, CRC(c3c93894) SHA1(f23b9d7e2b54d44c96370dde282bdf45cebd9cba) ) /* Plane 2 */
	ROM_LOAD32_BYTE( "024_c7_5edc.c7", 0x200001, 0x80000, CRC(21faf72b) SHA1(00bf257b06180ae6ede57744cea6257b3488d9f0) ) /* Plane 1 */
	ROM_LOAD32_BYTE( "024_c8_0850.c8", 0x200003, 0x80000, CRC(fb57217b) SHA1(607a98da754e8b1dd94a6432b21a36cc38e06a0f) ) /* Plane 3 */
ROM_END


/****************************************
 ID-0025
 . NGM-025
 NEO-MVS PROG42G-1 / NEO-MVS CHA42G-1
 . NGH-025
 NEO-AEG PROG42G / NEO-AEG CHA42G
****************************************/

ROM_START( eightman ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "025-p1.p1", 0x000000, 0x080000, CRC(43344cb0) SHA1(29dfd699f35b0a74e20fedd6c9174c289f0ef6e0) ) /* TC574200 */

	NEO_SFIX_128K( "025-s1.s1", CRC(a402202b) SHA1(75c44e1af459af155f5b892fd18706268dd5e602) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "025-m1.m1", CRC(9927034c) SHA1(205665361c5b2ab4f01ec480dd3c9b69db858d09) ) /* TC541000 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "025-v1.v1", 0x000000, 0x100000, CRC(4558558a) SHA1(a4b277703ed67225c652be0d618daeca65a27b88) ) /* TC538200 */
	ROM_LOAD( "025-v2.v2", 0x100000, 0x100000, CRC(c5e052e9) SHA1(fa1119c90ce4c706a6aa0c17d7bc06aa3068d9b2) ) /* TC538200 */

	ROM_REGION( 0x300000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "025-c1.c1", 0x000000, 0x100000, CRC(555e16a4) SHA1(1c96f3d2fd0991680fbf627a6cdd26ad2cd60319) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "025-c2.c2", 0x000001, 0x100000, CRC(e1ee51c3) SHA1(da8d074bb4e923ed7b8a154fd31b42f2d65b8e96) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "025-c3.c3", 0x200000, 0x080000, CRC(0923d5b0) SHA1(ab72ba1e3ebf56dd356f9ad181f986b1360a1089) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "025-c4.c4", 0x200001, 0x080000, CRC(e3eca67b) SHA1(88154cbc1a261c2f425430119ebc08a30adc9675) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

/****************************************
 ID-0026
 Fun Fun Brothers (prototype) 1991 Alpha
****************************************/

/****************************************
 ID-0027
 . MOM-001
 NEO-MVS PROG-8MB / NEO-MVS CHA-8M
 . MOH-001
 NEO-AEG PROG-8MB / NEO-AEG CHA-8M
****************************************/

ROM_START( minasan ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "027-p1.p1", 0x000000, 0x080000, CRC(c8381327) SHA1(c8f8be0ba276c6d12ef13d05af3cf83a2b924894) ) /* HN62422PC */

	NEO_SFIX_128K( "027-s1.s1", CRC(e5824baa) SHA1(8230ff7fe3cabeacecc762d90a084e893db84906) ) /* HN62321BP */

	NEO_BIOS_AUDIO_128K( "027-m1.m1", CRC(add5a226) SHA1(99995bef2584abbba16777bac52f55523f7aa97d) ) /* HN62321AP */

	ROM_DEFAULT_BIOS( "japan" ) /* so the mahjong panel will work in the service menu */

	ROM_REGION( 0x100000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "027-v11.v11", 0x000000, 0x100000, CRC(59ad4459) SHA1(bbb8ba8a8e337dd2946eefda4757e80d0547d54a) ) /* HN62308BPC */

	ROM_REGION( 0x100000, "cslot1:ymsnd:adpcmb", 0 )
	ROM_LOAD( "027-v21.v21", 0x000000, 0x100000, CRC(df5b4eeb) SHA1(134f3bcc3bb82e2a5711496af1019f343f9c0f7e) ) /* HN62308BPC */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "027-c1.c1", 0x000000, 0x100000, CRC(d0086f94) SHA1(7d6579530ccb5188f653be69b1df17e47e40e7a6) ) /* Plane 0,1 */ /* HN62408PD */
	ROM_LOAD16_BYTE( "027-c2.c2", 0x000001, 0x100000, CRC(da61f5a6) SHA1(82c5b4e5c5c5e30a3fd1c2e11c6157f39d033c42) ) /* Plane 2,3 */ /* HN62408PD */
	ROM_LOAD16_BYTE( "027-c3.c3", 0x200000, 0x100000, CRC(08df1228) SHA1(288b7ad328c2249f28d17df4dad3584995dca7bf) ) /* Plane 0,1 */ /* HN62408PD */
	ROM_LOAD16_BYTE( "027-c4.c4", 0x200001, 0x100000, CRC(54e87696) SHA1(90816dc86be3983dc57f56ededf7738475c0c61e) ) /* Plane 2,3 */ /* HN62408PD */
ROM_END

/****************************************
 ID-0028
 Dunk Star (prototype) 1991 Sammy
****************************************/

/****************************************
 ID-0029
 . ??M-029
 NEO-MVS PROG42G-1 / NEO-MVS CHA42G-1
 . ??H-029
 NEO-AEG PROG42G / NEO-AEG CHA42G
****************************************/

ROM_START( legendos )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "029-p1.p1", 0x000000, 0x080000, CRC(9d563f19) SHA1(9bff7bf9fdcf81a0a6c4ce3e196097d4f05e67b6) ) /* TC534200 */

	NEO_SFIX_128K( "029-s1.s1", CRC(bcd502f0) SHA1(a3400f52c037aa6a42e59e602cc24fa45fcbc951) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "029-m1.m1", CRC(6f2843f0) SHA1(975fb1598b87a2798fff05e951fca2e2e0329e79) ) /* TC531001 */

	ROM_REGION( 0x100000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "029-v1.v1", 0x000000, 0x100000, CRC(85065452) SHA1(7154b7c59b16c32753ac6b5790fb50b51ce30a20) ) /* TC538200 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "029-c1.c1", 0x000000, 0x100000, CRC(2f5ab875) SHA1(3e060973bba41a6c22ff7054104bdc5eee1fa13a) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "029-c2.c2", 0x000001, 0x100000, CRC(318b2711) SHA1(7014110cee98280317e1189f306ca40652b61f6f) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "029-c3.c3", 0x200000, 0x100000, CRC(6bc52cb2) SHA1(14323a4664b7dcbcde82e594168e535d7a921e44) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "029-c4.c4", 0x200001, 0x100000, CRC(37ef298c) SHA1(7a0c4c896dc3e730e06dcadbf00cf354f08a4466) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

/****************************************
 ID-0030
 . NGM-030
 NEO-MVS PROG42G-1 / NEO-MVS CHA42G-1
 . NGH-030
 NEO-AEG PROG42G-1 / NEO-AEG CHA42G-1
 NEO-AEG PROG42G   / NEO-AEG CHA42G
****************************************/

ROM_START( 2020bb )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "030-p1.p1", 0x000000, 0x080000, CRC(d396c9cb) SHA1(47ba421d14d05b965a8d44e7475b227a208e5a07) ) /* TC534200 */
	/* also found AES set with p1 label 030-P1 on TC534200 on NEO-AEG PROG42G and m1 label 030-M1 on TC531001 on NEO-AEG CHA42G; other chip labels are the same */

	NEO_SFIX_128K( "030-s1.s1", CRC(7015b8fc) SHA1(8c09bc3e6c62e0f7c9557c1e10c901be325bae7f) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "030-m1.m1", CRC(4cf466ec) SHA1(6a003b53c7a4af9d7529e2c10f27ffc4e58dcda5) ) /* TC54H1000 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "030-v1.v1", 0x000000, 0x100000, CRC(d4ca364e) SHA1(b0573744b0ea2ef1e2167a225f0d254883f5af04) ) /* TC538200 */
	ROM_LOAD( "030-v2.v2", 0x100000, 0x100000, CRC(54994455) SHA1(76eb62b86e8ed51a77f44313d5cc8091b3f58d57) ) /* TC538200 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "030-c1.c1", 0x000000, 0x100000, CRC(4f5e19bd) SHA1(ef7975c4b33a7aea4a25a385f604799f054d3200) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "030-c2.c2", 0x000001, 0x100000, CRC(d6314bf0) SHA1(0920cc580d7997fcb0170dd619af2f305d635577) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "030-c3.c3", 0x200000, 0x100000, CRC(47fddfee) SHA1(297c505a63448c999a2510c27bf4549102134db8) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "030-c4.c4", 0x200001, 0x100000, CRC(780d1c4e) SHA1(2e2cf9de828e3b48642dd2203637103438c62142) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

ROM_START( 2020bba ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "030-epr.p1", 0x000000, 0x080000, CRC(c59be3dd) SHA1(4fbd462c1c18e85a252c58b04b54fd3b82b46cb0) ) /* TC574200 */
	/* P1 on eprom, correct chip label unknown */

	NEO_SFIX_128K( "030-s1.s1", CRC(7015b8fc) SHA1(8c09bc3e6c62e0f7c9557c1e10c901be325bae7f) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "030-m1.m1", CRC(4cf466ec) SHA1(6a003b53c7a4af9d7529e2c10f27ffc4e58dcda5) ) /* TC54H1000 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "030-v1.v1", 0x000000, 0x100000, CRC(d4ca364e) SHA1(b0573744b0ea2ef1e2167a225f0d254883f5af04) ) /* TC538200 */
	ROM_LOAD( "030-v2.v2", 0x100000, 0x100000, CRC(54994455) SHA1(76eb62b86e8ed51a77f44313d5cc8091b3f58d57) ) /* TC538200 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "030-c1.c1", 0x000000, 0x100000, CRC(4f5e19bd) SHA1(ef7975c4b33a7aea4a25a385f604799f054d3200) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "030-c2.c2", 0x000001, 0x100000, CRC(d6314bf0) SHA1(0920cc580d7997fcb0170dd619af2f305d635577) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "030-c3.c3", 0x200000, 0x100000, CRC(47fddfee) SHA1(297c505a63448c999a2510c27bf4549102134db8) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "030-c4.c4", 0x200001, 0x100000, CRC(780d1c4e) SHA1(2e2cf9de828e3b48642dd2203637103438c62142) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

ROM_START( 2020bbh )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "030-pg1.p1", 0x000000, 0x080000, BAD_DUMP CRC(12d048d7) SHA1(ee0d03a565b11ca3bee2d24f62ff46a85ef18d90) )
	/* Chip label p1h does not exist, renamed temporarily to pg1, marked BAD_DUMP. This needs to be verified. */

	NEO_SFIX_128K( "030-s1.s1", CRC(7015b8fc) SHA1(8c09bc3e6c62e0f7c9557c1e10c901be325bae7f) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "030-m1.m1", CRC(4cf466ec) SHA1(6a003b53c7a4af9d7529e2c10f27ffc4e58dcda5) ) /* TC54H1000 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "030-v1.v1", 0x000000, 0x100000, CRC(d4ca364e) SHA1(b0573744b0ea2ef1e2167a225f0d254883f5af04) ) /* TC538200 */
	ROM_LOAD( "030-v2.v2", 0x100000, 0x100000, CRC(54994455) SHA1(76eb62b86e8ed51a77f44313d5cc8091b3f58d57) ) /* TC538200 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "030-c1.c1", 0x000000, 0x100000, CRC(4f5e19bd) SHA1(ef7975c4b33a7aea4a25a385f604799f054d3200) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "030-c2.c2", 0x000001, 0x100000, CRC(d6314bf0) SHA1(0920cc580d7997fcb0170dd619af2f305d635577) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "030-c3.c3", 0x200000, 0x100000, CRC(47fddfee) SHA1(297c505a63448c999a2510c27bf4549102134db8) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "030-c4.c4", 0x200001, 0x100000, CRC(780d1c4e) SHA1(2e2cf9de828e3b48642dd2203637103438c62142) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

/****************************************
 ID-0031
 . NGM-031
 NEO-MVS PROG42G-1 / NEO-MVS CHA42G-1
 . NGH-031
 NEO-AEG PROG42G-1 / NEO-AEG CHA42G-1
****************************************/

ROM_START( socbrawl ) /* MVS VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "031-pg1.p1", 0x000000, 0x080000, CRC(17f034a7) SHA1(2e66c7bd93a08efe63c4894494db50bbf58f60e4) ) /* TC534200 */
	/* also found set with P1 on TC534200 with chip label 031-P1 */

	NEO_SFIX_128K( "031-s1.s1", CRC(4c117174) SHA1(26e52c4f628338a9aa1c159517cdf873f738fb98) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "031-m1.m1", CRC(cb37427c) SHA1(99efe9600ebeda48331f396e3203c7588bdb7d24) ) /* TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "031-v1.v1", 0x000000, 0x100000, CRC(cc78497e) SHA1(895bd647150fae9b2259ef043ed681f4c4de66ea) ) /* TC538200 */
	ROM_LOAD( "031-v2.v2", 0x100000, 0x100000, CRC(dda043c6) SHA1(08165a59700ab6b1e523079dd2a3549e520cc594) ) /* TC538200 */

	ROM_REGION( 0x300000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "031-c1.c1", 0x000000, 0x100000, CRC(bd0a4eb8) SHA1(b67988cb3e550d083e81c9bd436da55b242785ed) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "031-c2.c2", 0x000001, 0x100000, CRC(efde5382) SHA1(e42789c8d87ee3d4549d0a903e990c03338cbbd8) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "031-c3.c3", 0x200000, 0x080000, CRC(580f7f33) SHA1(f4f95a7c8de00e1366a723fc4cd0e8c1905af636) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "031-c4.c4", 0x200001, 0x080000, CRC(ed297de8) SHA1(616f8fa4c86231f3e79faf9f69f8bb909cbc35f0) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

ROM_START( socbrawlh ) /* AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "031-p1.p1", 0x000000, 0x080000, CRC(a2801c24) SHA1(627d76ff0740ca29586f37b268f47fb469822529) ) /* TC534200 */

	NEO_SFIX_128K( "031-s1.s1", CRC(4c117174) SHA1(26e52c4f628338a9aa1c159517cdf873f738fb98) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "031-m1.m1", CRC(cb37427c) SHA1(99efe9600ebeda48331f396e3203c7588bdb7d24) ) /* TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "031-v1.v1", 0x000000, 0x100000, CRC(cc78497e) SHA1(895bd647150fae9b2259ef043ed681f4c4de66ea) ) /* TC538200 */
	ROM_LOAD( "031-v2.v2", 0x100000, 0x100000, CRC(dda043c6) SHA1(08165a59700ab6b1e523079dd2a3549e520cc594) ) /* TC538200 */

	ROM_REGION( 0x300000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "031-c1.c1", 0x000000, 0x100000, CRC(bd0a4eb8) SHA1(b67988cb3e550d083e81c9bd436da55b242785ed) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "031-c2.c2", 0x000001, 0x100000, CRC(efde5382) SHA1(e42789c8d87ee3d4549d0a903e990c03338cbbd8) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "031-c3.c3", 0x200000, 0x080000, CRC(580f7f33) SHA1(f4f95a7c8de00e1366a723fc4cd0e8c1905af636) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "031-c4.c4", 0x200001, 0x080000, CRC(ed297de8) SHA1(616f8fa4c86231f3e79faf9f69f8bb909cbc35f0) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

/****************************************
 ID-0032
 . NGM-032
 NEO-MVS PROG42G-1 / NEO-MVS CHA42G-1
 NEO-MVS PROG42G-COM / NEO-MVS CHA42G-1
 . NGH-032
 NEO-AEG PROG42G-1 / NEO-AEG CHA42G-1
****************************************/

ROM_START( roboarmy )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "032-p1.p1", 0x000000, 0x080000, CRC(cd11cbd4) SHA1(23163e3da2f07e830a7f4a02aea1cb01a54ccbf3) ) /* TC534200 */
	/* also found sets with P1 on eprom; correct chip label unknown */

	NEO_SFIX_128K( "032-s1.s1", CRC(ac0daa1b) SHA1(93bae4697dc403fce19422752a514326ccf66a91) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "032-m1.m1", CRC(35ec952d) SHA1(8aed30e26d7e2c70dbce5de752df416091066f7b) ) /* TC541000 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "032-v1.v1", 0x000000, 0x100000, CRC(63791533) SHA1(4479e9308cdc906b9e03b985303f4ebedd00512f) ) /* TC538200 */
	ROM_LOAD( "032-v2.v2", 0x100000, 0x100000, CRC(eb95de70) SHA1(b34885201116d2b3bbdee15ec7b5961cf5c069e1) ) /* TC538200 */

	ROM_REGION( 0x300000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "032-c1.c1", 0x000000, 0x100000, CRC(97984c6c) SHA1(deea59c0892f05dc7db98cb57b3eb83688dc57f0) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "032-c2.c2", 0x000001, 0x100000, CRC(65773122) SHA1(2c0162a8e971e5e57933e4ae16040bf824ffdefe) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "032-c3.c3", 0x200000, 0x080000, CRC(40adfccd) SHA1(b11f866dd70ba0ed9123424508355cb948b19bdc) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "032-c4.c4", 0x200001, 0x080000, CRC(462571de) SHA1(5c3d610d492f91564423873b3b434dcda700373f) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

ROM_START( roboarmya ) /* MVS AND AES VERSION*/
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "032-epr.p1", 0x000000, 0x080000, CRC(27c773cb) SHA1(597ca73f142b1129cc7780540bb9cfacd47bc6ce) ) /* D27C4000 */
	/* Found on legitimate Robo Army MVS and AES Carts. Debug code button which check control a sound test has been patched out because in a multislot situation that code could have been enabled
	that would stop roboarmy from working, sending it into an infinite loop ; correct chip label unknown */

	NEO_SFIX_128K( "032-s1.s1", CRC(ac0daa1b) SHA1(93bae4697dc403fce19422752a514326ccf66a91) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "032-m1.m1", CRC(35ec952d) SHA1(8aed30e26d7e2c70dbce5de752df416091066f7b) ) /* TC541000 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "032-v1.v1", 0x000000, 0x100000, CRC(63791533) SHA1(4479e9308cdc906b9e03b985303f4ebedd00512f) ) /* TC538200 */
	ROM_LOAD( "032-v2.v2", 0x100000, 0x100000, CRC(eb95de70) SHA1(b34885201116d2b3bbdee15ec7b5961cf5c069e1) ) /* TC538200 */

	ROM_REGION( 0x300000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "032-c1.c1", 0x000000, 0x100000, CRC(97984c6c) SHA1(deea59c0892f05dc7db98cb57b3eb83688dc57f0) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "032-c2.c2", 0x000001, 0x100000, CRC(65773122) SHA1(2c0162a8e971e5e57933e4ae16040bf824ffdefe) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "032-c3.c3", 0x200000, 0x080000, CRC(40adfccd) SHA1(b11f866dd70ba0ed9123424508355cb948b19bdc) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "032-c4.c4", 0x200001, 0x080000, CRC(462571de) SHA1(5c3d610d492f91564423873b3b434dcda700373f) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

/****************************************
 ID-0033
 . NGM-033
 NEO-MVS PROG42G-1 / NEO-MVS CHA42G-1
 . NGH-033
 NEO-AEG PROG42G-1 / NEO-AEG CHA42G-1
****************************************/

ROM_START( fatfury1 ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "033-p1.p1", 0x000000, 0x080000, CRC(47ebdc2f) SHA1(d46786502920fb510f1999db00c5e09fb641c0bd) ) /* TC534200 */
	ROM_LOAD16_WORD_SWAP( "033-p2.p2", 0x080000, 0x020000, CRC(c473af1c) SHA1(4919eeca20abe807493872ca7c79a5d1f496fe68) ) /* TC531024 */
	/* also found MVS sets with P1 and P2 on eprom; correct chip label unknown */

	NEO_SFIX_128K( "033-s1.s1", CRC(3c3bdf8c) SHA1(2f3e5feed6c27850b2a0f6fae0b97041690e944c) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "033-m1.m1", CRC(5be10ffd) SHA1(90a5e6cbbc58a7883cd2a3a597180d631a466882) ) /* TC531001 */
	/* also found MVS sets with M1 on eprom */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "033-v1.v1", 0x000000, 0x100000, CRC(212fd20d) SHA1(120c040db8c01a6f140eea03725448bfa9ca98c2) ) /* TC538200 */
	ROM_LOAD( "033-v2.v2", 0x100000, 0x100000, CRC(fa2ae47f) SHA1(80d0ba4cd30aab59b6f0db8fa341387bd7388afc) ) /* TC538200 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "033-c1.c1", 0x000000, 0x100000, CRC(74317e54) SHA1(67b9c2814a12603b959612456f59de55f9bf6f57) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "033-c2.c2", 0x000001, 0x100000, CRC(5bb952f3) SHA1(ea964bbcc0408b6ae07cbb5043d003281b1aca15) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "033-c3.c3", 0x200000, 0x100000, CRC(9b714a7c) SHA1(b62bdcede3207d062a89e0a4a9adf706101bb681) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "033-c4.c4", 0x200001, 0x100000, CRC(9397476a) SHA1(a12dbb74020aeb6ebf24ec2abbfba5129cabcb7d) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

/****************************************
 ID-0034
 . NGM-034
 NEO-MVS PROG42G-1 / NEO-MVS CHA42G-1
 . NGH-034
 NEO-AEG PROG42G-1 / NEO-AEG CHA42G-1
****************************************/

ROM_START( fbfrenzy ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "034-p1.p1", 0x000000, 0x080000, CRC(cdef6b19) SHA1(97482db0dffc6d625fb41fa38449c0a74d741a72) ) /* TC534200 */

	NEO_SFIX_128K( "034-s1.s1", CRC(8472ed44) SHA1(42e1a9671dddd090d2a634cff986f6c73ba08b70) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "034-m1.m1", CRC(f41b16b8) SHA1(f3e1cfc4cd2c5baece176f169906aa796367d303) ) /* TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "034-v1.v1", 0x000000, 0x100000, CRC(50c9d0dd) SHA1(2b3f2875b00e5f307d274128bd73c1521a7d901b) ) /* TC538200 */
	ROM_LOAD( "034-v2.v2", 0x100000, 0x100000, CRC(5aa15686) SHA1(efe47954827a98d539ba719347c5f8aa60e6338b) ) /* TC538200 */

	ROM_REGION( 0x300000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "034-c1.c1", 0x000000, 0x100000, CRC(91c56e78) SHA1(2944d49ebfc71239d345209ca7f25993c2cc5a77) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "034-c2.c2", 0x000001, 0x100000, CRC(9743ea2f) SHA1(cf4fccdf10d521d555e92bc24123142393c2b3bb) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "034-c3.c3", 0x200000, 0x080000, CRC(e5aa65f5) SHA1(714356a2cee976ec0f515b1034ce971018e5c02e) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "034-c4.c4", 0x200001, 0x080000, CRC(0eb138cc) SHA1(21d31e1f136c674caa6dd44073281cd07b72ea9b) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

/****************************************
 ID-0035
 Mystic Wand (prototype) 1991 Alpha
****************************************/

/****************************************
 ID-0036
 . MOM-002
 NEO-MVS PROG42G-1 / NEO-MVS CHA42G-1
 NEO-MVS PROG42G-COM / NEO-MVS CHA42G-1
 . MOH-002
 NEO-AEG PROG42G / NEO-AEG CHA42G
****************************************/

ROM_START( bakatono ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "036-p1.p1", 0x000000, 0x080000, CRC(1c66b6fa) SHA1(6c50cc452971c46c763ae0b2def95792671a1798) ) /* CXK384500 */

	NEO_SFIX_128K( "036-s1.s1", CRC(f3ef4485) SHA1(c30bfceed7e669e4c97b0b3ec2e9f4271e5b6662) ) /* CXK381000 */

	NEO_BIOS_AUDIO_128K( "036-m1.m1", CRC(f1385b96) SHA1(e7e3d1484188a115e262511116aaf466b8b1f428) ) /* CXK381003 */

	ROM_DEFAULT_BIOS( "japan" ) /* so the mahjong panel will work in the service menu */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "036-v1.v1", 0x000000, 0x100000, CRC(1c335dce) SHA1(493c273fa71bf81861a20af4c4eaae159e169f39) ) /* CXK388000 */
	ROM_LOAD( "036-v2.v2", 0x100000, 0x100000, CRC(bbf79342) SHA1(45a4f40e415cdf35c3073851506648c8f7d53958) ) /* CXK388000 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "036-c1.c1", 0x000000, 0x100000, CRC(fe7f1010) SHA1(5b6f5053821f4da8dc3768371e2cd51bb29da963) ) /* Plane 0,1 */ /* CXK388000 */
	ROM_LOAD16_BYTE( "036-c2.c2", 0x000001, 0x100000, CRC(bbf003f5) SHA1(054b2a3327e038836eece652055bb84c115cf8ed) ) /* Plane 2,3 */ /* CXK388000 */
	ROM_LOAD16_BYTE( "036-c3.c3", 0x200000, 0x100000, CRC(9ac0708e) SHA1(8decfe06d73a3dd3c3cf280719978fcf6d559d29) ) /* Plane 0,1 */ /* CXK388000 */
	ROM_LOAD16_BYTE( "036-c4.c4", 0x200001, 0x100000, CRC(f2577d22) SHA1(a37db8055ca4680e244c556dc6df8bdba16c2083) ) /* Plane 2,3 */ /* CXK388000 */
ROM_END

/****************************************
 ID-0037
 . ALM-002
 NEO-MVS PROG42G-1 / NEO-MVS CHA42G-1
 . ALH-002
 NEO-AEG PROG42G / NEO-AEG CHA42G
****************************************/

ROM_START( crsword ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "037-p1.p1", 0x000000, 0x080000, CRC(e7f2553c) SHA1(8469ecb900477feed05ae3311fe9515019bbec2a) ) /* TC534200 */

	NEO_SFIX_128K( "037-s1.s1", CRC(74651f27) SHA1(bff7ff2429d2be82c1647abac2ee45b339b3b310) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "037-m1.m1", CRC(9504b2c6) SHA1(9ce8e681b9df6eacd0d23a36bad836bd5074233d) ) /* TC531001 */

	ROM_REGION( 0x100000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "037-v1.v1", 0x000000, 0x100000, CRC(61fedf65) SHA1(98f31d1e23bf7c1f7844e67f14707a704134042e) ) /* TC538200 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "037-c1.c1", 0x000000, 0x100000, CRC(09df6892) SHA1(df2579dcf9c9dc88d461212cb74de106be2983c1) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "037-c2.c2", 0x000001, 0x100000, CRC(ac122a78) SHA1(7bfa4d29b7d7d9443f64d81caeafa74fe05c606e) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "037-c3.c3", 0x200000, 0x100000, CRC(9d7ed1ca) SHA1(2bbd25dc3a3f825d0af79a418f06a23a1bf03cc0) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "037-c4.c4", 0x200001, 0x100000, CRC(4a24395d) SHA1(943f911f40985db901eaef4c28dfcda299fca73e) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

/****************************************
 ID-0038
 . ALM-003
 NEO-MVS PROG42G-COM / NEO-MVS CHA42G-1
 . ALH-003
 NEO-AEG PROG42G-COM / NEO-AEG CHA42G-1
****************************************/

ROM_START( trally ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "038-p1.p1", 0x000000, 0x080000, CRC(1e52a576) SHA1(a1cb56354c3378e955b0cd482c3c41ae15add952) ) /* TC534200 */
	ROM_LOAD16_WORD_SWAP( "038-p2.p2", 0x080000, 0x080000, CRC(a5193e2f) SHA1(96803480439e90da23cdca70d59ff519ee85beeb) ) /* TC534200 */

	ROM_REGION( 0x1000, "mcu", 0 )    /* Hitachi HD6301V1 MCU */
	ROM_LOAD( "hd6301v1p_m58_neo-coma", 0x0000, 0x1000, NO_DUMP )

	NEO_SFIX_128K( "038-s1.s1", CRC(fff62ae3) SHA1(6510a762ea41557a8938cbfc0557cd5921306061) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "038-m1.m1", CRC(0908707e) SHA1(df7489ea6abf84d7f137ba7a8f52a4fd1b088fd7) ) /* TC531001 */

	ROM_REGION( 0x180000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "038-v1.v1", 0x000000, 0x100000, CRC(5ccd9fd5) SHA1(c3c8c758a320c39e4ceb0b6d9f188ed6d122eec4) ) /* TC538200 */
	ROM_LOAD( "038-v2.v2", 0x100000, 0x080000, CRC(ddd8d1e6) SHA1(65c819fa2392f264f5a1a0a4967c96775732500b) ) /* TC534200 */

	ROM_REGION( 0x300000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "038-c1.c1", 0x000000, 0x100000, CRC(c58323d4) SHA1(a6bd277471a4b612d165f8b804f3cb662f499b70) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "038-c2.c2", 0x000001, 0x100000, CRC(bba9c29e) SHA1(b70bbfdfa8c4f9ea76406530e86b16e42498d284) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "038-c3.c3", 0x200000, 0x080000, CRC(3bb7b9d6) SHA1(bc1eae6181ad5abf79736afc8db4ca34113d43f8) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "038-c4.c4", 0x200001, 0x080000, CRC(a4513ecf) SHA1(934aa103c226eac55157b44d7b4dfa35515322c3) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

/****************************************
 ID-0039
 . NGM-039
 NEO-MVS PROG16 / NEO-MVS CHA42G-1
 . NGH-039
 NEO-AEG PROG16 / NEO-AEG CHA42G-1
****************************************/

ROM_START( kotm2 ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "039-p1.p1", 0x000000, 0x080000, CRC(b372d54c) SHA1(b70fc6f72e16a66b6e144cc01370548e3398b8b8) ) /* TC534200 */
	ROM_LOAD16_WORD_SWAP( "039-p2.p2", 0x080000, 0x080000, CRC(28661afe) SHA1(6c85ff6ab334b1ca744f726f42dac211537e7315) ) /* TC534200 */

	NEO_SFIX_128K( "039-s1.s1", CRC(63ee053a) SHA1(7d4b92bd022708975b1470e8f24d1f5a712e1b94) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "039-m1.m1", CRC(0c5b2ad5) SHA1(15eb5ea10fecdbdbcfd06225ae6d88bb239592e7) ) /* TC531001 */

	ROM_REGION( 0x300000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "039-v2.v2", 0x000000, 0x200000, CRC(86d34b25) SHA1(89bdb614b0c63d678962da52e2f596750d20828c) ) /* TC5316200 */
	ROM_LOAD( "039-v4.v4", 0x200000, 0x100000, CRC(8fa62a0b) SHA1(58ac2fdd73c542eb8178cfc4adfa0e5940183283) ) /* TC538200 */

	ROM_REGION( 0x600000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "039-c1.c1", 0x000000, 0x100000, CRC(6d1c4aa9) SHA1(4fbc9d7cb37522ec298eefbe38c75a2d050fbb4a) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_CONTINUE( 0x400000, 0x100000 )
	ROM_LOAD16_BYTE( "039-c2.c2", 0x000001, 0x100000, CRC(f7b75337) SHA1(4d85f85948c3e6ed38b0b0ccda79de3ce026e2d9) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_CONTINUE( 0x400001, 0x100000 )
	ROM_LOAD16_BYTE( "039-c3.c3", 0x200000, 0x080000, CRC(bfc4f0b2) SHA1(f4abe2b52882b966412f3b503b8f2c8f49b57968) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "039-c4.c4", 0x200001, 0x080000, CRC(81c9c250) SHA1(e3a34ff69081a8681b5ca895915892dcdccfa7aa) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

ROM_START( kotm2a ) /* MVS VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "039_p1.p1", 0x000000, 0x080000, CRC(8d186638) SHA1(c3874bbb1bfcb220173afb2e0a2123ffaeb7bd2b) ) /* TC534200 */
	ROM_LOAD16_WORD_SWAP( "039-p2.p2", 0x080000, 0x080000, CRC(28661afe) SHA1(6c85ff6ab334b1ca744f726f42dac211537e7315) ) /* TC534200 */

	NEO_SFIX_128K( "039-s1.s1", CRC(63ee053a) SHA1(7d4b92bd022708975b1470e8f24d1f5a712e1b94) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "039-m1.m1", CRC(0c5b2ad5) SHA1(15eb5ea10fecdbdbcfd06225ae6d88bb239592e7) ) /* TC531001 */

	ROM_REGION( 0x300000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "039-v2.v2", 0x000000, 0x200000, CRC(86d34b25) SHA1(89bdb614b0c63d678962da52e2f596750d20828c) ) /* TC5316200 */
	ROM_LOAD( "039-v4.v4", 0x200000, 0x100000, CRC(8fa62a0b) SHA1(58ac2fdd73c542eb8178cfc4adfa0e5940183283) ) /* TC538200 */

	ROM_REGION( 0x600000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "039-c1.c1", 0x000000, 0x100000, CRC(6d1c4aa9) SHA1(4fbc9d7cb37522ec298eefbe38c75a2d050fbb4a) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_CONTINUE( 0x400000, 0x100000 )
	ROM_LOAD16_BYTE( "039-c2.c2", 0x000001, 0x100000, CRC(f7b75337) SHA1(4d85f85948c3e6ed38b0b0ccda79de3ce026e2d9) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_CONTINUE( 0x400001, 0x100000 )
	ROM_LOAD16_BYTE( "039-c3.c3", 0x200000, 0x080000, CRC(bfc4f0b2) SHA1(f4abe2b52882b966412f3b503b8f2c8f49b57968) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "039-c4.c4", 0x200001, 0x080000, CRC(81c9c250) SHA1(e3a34ff69081a8681b5ca895915892dcdccfa7aa) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

ROM_START( kotm2p ) /* fairly late prototype release, only the code differs from the main set */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_BYTE( "proto_039-p1.p1", 0x000001, 0x080000, CRC(3c1d17e7) SHA1(aeaff465fafa46ded903ed4e8cb8cd05de8dc096) )
	ROM_LOAD16_BYTE( "proto_039-p2.p2", 0x000000, 0x080000, CRC(bc9691f0) SHA1(3854659b952d4f8c2edd5d59858a61ce6d518604) )

	NEO_SFIX_128K( "039-s1.s1", CRC(63ee053a) SHA1(7d4b92bd022708975b1470e8f24d1f5a712e1b94) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "039-m1.m1", CRC(0c5b2ad5) SHA1(15eb5ea10fecdbdbcfd06225ae6d88bb239592e7) ) /* TC531001 */

	// same data as main set, but prototype board layout
	ROM_REGION( 0x300000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "proto_039-v1.v1", 0x000000, 0x080000, CRC(dd3566f1) SHA1(f7c2a3747aaab2bc57cdfa33d8bb9fab057b5214) )
	ROM_LOAD( "proto_039-v2.v2", 0x080000, 0x080000, CRC(57f60274) SHA1(7e17740aa05cf7ad4f9084e147600a8eb82c7284) )
	ROM_LOAD( "proto_039-v3.v3", 0x100000, 0x080000, CRC(0f008a07) SHA1(ed243a0449232bbea409308c3fec7e057fcd8501) )
	ROM_LOAD( "proto_039-v4.v4", 0x180000, 0x080000, CRC(1943d0fe) SHA1(47fb716c76ea6b5fe64204ff6d72b7feea10bda9) )
	ROM_LOAD( "proto_039-v5.v5", 0x200000, 0x080000, CRC(13be045b) SHA1(0e3713ae6b164ebae434c0f18c466365b26b9a77) )
	ROM_LOAD( "proto_039-v6.v6", 0x280000, 0x080000, CRC(d1dd3fd6) SHA1(052b92168a76cf3a97c8cacebcc3ebab228726df) )

	// same data as main set, but prototype board layout
	ROM_REGION( 0x600000, "cslot1:sprites", 0 ) // note, ROM_LOAD32_BYTE
	ROM_LOAD32_BYTE( "proto_039-c1.c1", 0x000000, 0x100000, CRC(7192a787) SHA1(7bef6ce79c618103485480aee3c6f856968eb51f) ) /* Plane 0 */
	ROM_LOAD32_BYTE( "proto_039-c2.c2", 0x000002, 0x100000, CRC(7157eca1) SHA1(65f36c6a3834775b04076d2c38a6047bffe9a8cf) ) /* Plane 2 */
	ROM_LOAD32_BYTE( "proto_039-c3.c3", 0x000001, 0x100000, CRC(11d75727) SHA1(5a4c7b5ca3f1195e7853b45c5e71c13fe74d16e9) ) /* Plane 1 */
	ROM_LOAD32_BYTE( "proto_039-c4.c4", 0x000003, 0x100000, CRC(7ad48b28) SHA1(27e65d948f08c231107cb1a810e2b06731091fc3) ) /* Plane 3 */
	ROM_LOAD32_BYTE( "proto_039-c5.c5", 0x400000, 0x080000, CRC(5bdaf9ca) SHA1(60620d42ac6cd0e5da019fede2814a2f4171ff3f) ) /* Plane 0 */
	ROM_LOAD32_BYTE( "proto_039-c6.c6", 0x400002, 0x080000, CRC(21d4be8c) SHA1(f1b19d37d52d21584f304b7d37d5c096b58219d6) ) /* Plane 2 */
	ROM_LOAD32_BYTE( "proto_039-c7.c7", 0x400001, 0x080000, CRC(da55fd00) SHA1(52804f955597591fdd1d7478dc340b36d3c08c4a) ) /* Plane 1 */
	ROM_LOAD32_BYTE( "proto_039-c8.c8", 0x400003, 0x080000, CRC(592e9267) SHA1(0d27de59970ccbcaa1d47909ea3d741ffb0d9e07) ) /* Plane 3 */
ROM_END

/****************************************
 ID-0040
 . NGM-040
 NEO-MVS PROG 4096 / NEO-MVS CHA 42G-2
 . NGH-040
 NEO-AEG PROG16 / NEO-AEG CHA42G-1
****************************************/

ROM_START( sengoku2 )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "040-p1.p1", 0x000000, 0x100000, CRC(6dde02c2) SHA1(e432e63feb88c71629ec96aa84650dcfe356a551) ) /* TC538200 */

	NEO_SFIX_128K( "040-s1.s1", CRC(cd9802a3) SHA1(f685d4638f4f68e7e3f101c0c39128454536721b) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "040-m1.m1", CRC(d4de4bca) SHA1(ecf604d06f01d40b04e285facef66a6ae2d35661) ) /* TC531001 */

	ROM_REGION( 0x300000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "040-v1.v1", 0x000000, 0x200000, CRC(71cb4b5d) SHA1(56d9aca1d476c19c7d0f707176a8fed53e0189b7) ) /* TC5316200 */
	// AES has different label, data is the same: 040-v1.v2
	ROM_LOAD( "040-v2.v2", 0x200000, 0x100000, CRC(c5cece01) SHA1(923a3377dac1919e8c3d9ab316902250caa4785f) ) /* TC538200 */
	// AES has different label, data is the same: 040-v2.v4

	ROM_REGION( 0x600000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "040-c1.c1", 0x000000, 0x100000, CRC(faa8ea99) SHA1(714575e57ea1990612f960ec42b38d2e157ad400) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_CONTINUE( 0x400000, 0x100000 )
	ROM_LOAD16_BYTE( "040-c2.c2", 0x000001, 0x100000, CRC(87d0ec65) SHA1(23645e0cf859fb4cec3745b3846ca0ef64c689fb) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_CONTINUE( 0x400001, 0x100000 )
	ROM_LOAD16_BYTE( "040-c3.c3", 0x200000, 0x080000, CRC(24b5ba80) SHA1(29d58a6b56bd24ee2046a8d45e023b4d7ab7685b) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "040-c4.c4", 0x200001, 0x080000, CRC(1c9e9930) SHA1(d017474873750a7602b7708c663d29b25ef7bb63) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

/****************************************
 ID-0041
 . NGM-041
 NEO-MVS PROG42G-1 / NEO-MVS CHA42G-1
 . NGH-041
 NEO-AEG PROG42G-1 / NEO-AEG CHA42G-1
****************************************/

ROM_START( bstars2 )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "041-p1.p1", 0x000000, 0x080000, CRC(523567fd) SHA1(f1e81eb4678f586b214ea102cde6effea1b0f768) ) /* TC534200 */

	NEO_SFIX_128K( "041-s1.s1", CRC(015c5c94) SHA1(f1c60cd3dc54986b39f630ef3bf48f68c68695dc) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "041-m1.m1", CRC(15c177a6) SHA1(3f4abed635102f9bc8b44809750828c82e79b44f) ) /* TC531001 */

	ROM_REGION( 0x280000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "041-v1.v1", 0x000000, 0x100000, CRC(cb1da093) SHA1(4f4d1d5fefa9dda372083c045bf0d268a57ce8f1) ) /* TC538200 */
	ROM_LOAD( "041-v2.v2", 0x100000, 0x100000, CRC(1c954a9d) SHA1(159bc6efdd531615461f6e16f83f6d4c4e67c237) ) /* TC538200 */
	ROM_LOAD( "041-v3.v3", 0x200000, 0x080000, CRC(afaa0180) SHA1(c4a047e21f093830498a163598ed7bd48a8cf9d1) ) /* TC534200 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "041-c1.c1", 0x000000, 0x100000, CRC(b39a12e1) SHA1(bafe383bd7c5a6aac4cb92dabbc56e3672fe174d) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "041-c2.c2", 0x000001, 0x100000, CRC(766cfc2f) SHA1(79e1063925d54a57df943019a88bea56c9152df3) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "041-c3.c3", 0x200000, 0x100000, CRC(fb31339d) SHA1(f4e821299680970b2e979acc4a170029b968c807) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "041-c4.c4", 0x200001, 0x100000, CRC(70457a0c) SHA1(a1e307f11ddab85d2e9c09d0428fac2e6da774b1) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

/****************************************
 ID-0042
 . NGM-042
 NEO-MVS PROG42G-1 / NEO-MVS CHA42G-1
 . NGH-042
 NEO-AEG PROG42G-1 / NEO-AEG CHA42G-1
****************************************/

ROM_START( quizdai2 ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "042-p1.p1", 0x000000, 0x100000, CRC(ed719dcf) SHA1(12baf2601e86c0e4358517b9fa1c55f2f5835f1d) ) /* TC538200 */

	NEO_SFIX_128K( "042-s1.s1", CRC(164fd6e6) SHA1(dad35bedc33d502a5ae745a45a972af8d901b160) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "042-m1.m1", CRC(bb19995d) SHA1(ed458fad5a23c6bd0d099927d98c31e1e6562d1b) ) /* TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "042-v1.v1", 0x000000, 0x100000, CRC(af7f8247) SHA1(99a47014017c20e4e22010c60612b6b7f6efc9e5) ) /* TC538200 */
	ROM_LOAD( "042-v2.v2", 0x100000, 0x100000, CRC(c6474b59) SHA1(a6c5054032b698116247b2f09a8b94a1b588c4f1) ) /* TC538200 */

	ROM_REGION( 0x300000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "042-c1.c1", 0x000000, 0x100000, CRC(cb5809a1) SHA1(b53d06685246dd51b82b5c1d54d639d10e2ec26d) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "042-c2.c2", 0x000001, 0x100000, CRC(1436dfeb) SHA1(27d136fb1be793bd345a741f5e55a977275fff86) ) /* Plane 2,3 */ /* TC538200 */
	ROM_LOAD16_BYTE( "042-c3.c3", 0x200000, 0x080000, CRC(bcd4a518) SHA1(f355298fe0f2cf50ddcc0d613db56a5c04d7230f) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "042-c4.c4", 0x200001, 0x080000, CRC(d602219b) SHA1(34cf0f16db1e224396464ac838f4cd2e6d1c640e) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

/****************************************
 ID-0043
 . NGM-043
 NEO-MVS PROG 4096 / NEO-MVS CHA 42G-2
 NEO-MVS PROG-G2 / NEO-MVS CHA 42G-2
 . NGH-043
 NEO-AEG PROG16 / NEO-AEG CHA42G-1
****************************************/

ROM_START( 3countb ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "043-p1.p1", 0x000000, 0x100000, CRC(ffbdd928) SHA1(05b24655ca32723661adc5509b450824deb0c176) ) /* TC538200 */
	/* The original p1 is 8mbit; also found sets with p1 / p2 4mbit on eprom. */

	NEO_SFIX_128K( "043-s1.s1", CRC(c362d484) SHA1(a3c029292572842feabe9aa8c3372628fb63978d) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "043-m1.m1", CRC(7eab59cb) SHA1(5ae4107457e091f73960bfba39b589ae36d51ca3) ) /* TC531001 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "043-v1.v1", 0x000000, 0x200000, CRC(63688ce8) SHA1(5c6ac29a0cc0655a87cfe3ada8706838b86b86e4) ) /* TC5316200 */
	// AES has different label: 043-v1.v2; same data
	ROM_LOAD( "043-v2.v2", 0x200000, 0x200000, CRC(c69a827b) SHA1(f5197ea87bb6573fa6aef3a1713c3679c58c1e74) ) /* TC5316200 */
	// AES has different label: 040-v2.v4; same data

	ROM_REGION( 0x0800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "043-c1.c1", 0x000000, 0x100000, CRC(bad2d67f) SHA1(04928e50ca75b7fbc52b64e816ec5701901f5893) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_CONTINUE( 0x400000, 0x100000 )
	ROM_LOAD16_BYTE( "043-c2.c2", 0x000001, 0x100000, CRC(a7fbda95) SHA1(9da3c5faf22592a7eaf8df9fa6454f48c2a927ae) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_CONTINUE( 0x400001, 0x100000 )
	ROM_LOAD16_BYTE( "043-c3.c3", 0x200000, 0x100000, CRC(f00be011) SHA1(2721cdba37a511a966a2a53b9bd6240f181d920c) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_CONTINUE( 0x600000, 0x100000 )
	ROM_LOAD16_BYTE( "043-c4.c4", 0x200001, 0x100000, CRC(1887e5c0) SHA1(9b915359add7c10c78d8b281b4084eceea8f0499) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_CONTINUE( 0x600001, 0x100000 )
ROM_END

/****************************************
 ID-0044
 . NGM-044
 NEO-MVS PROG16 / NEO-MVS CHA42G-1
 NEO-MVS PROG4096 / NEO-MVS CHA42G-1
 . NGH-044
 NEO-AEG PROG16 / NEO-AEG CHA42G-1
****************************************/

ROM_START( aof ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "044-p1.p1", 0x000000, 0x080000, CRC(ca9f7a6d) SHA1(4d28ef86696f7e832510a66d3e8eb6c93b5b91a1) ) /* TC534200 */
	/* also found sets with EP1 or P1 on eprom. */

	NEO_SFIX_128K( "044-s1.s1", CRC(89903f39) SHA1(a04a0c244a5d5c7a595fcf649107969635a6a8b6) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "044-m1.m1", CRC(0987e4bb) SHA1(8fae4b7fac09d46d4727928e609ed9d3711dbded) ) /* TC531001 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "044-v2.v2", 0x000000, 0x200000, CRC(3ec632ea) SHA1(e3f413f580b57f70d2dae16dbdacb797884d3fce) ) /* TC5316200 */
	ROM_LOAD( "044-v4.v4", 0x200000, 0x200000, CRC(4b0f8e23) SHA1(105da0cc5ba19869c7147fba8b177500758c232b) ) /* TC5316200 */
	/* Also found MVS set with different label: 044-v2.v1 and 044-v4.v2 */

	ROM_REGION( 0x800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "044-c1.c1", 0x000000, 0x100000, CRC(ddab98a7) SHA1(f20eb81ec431268798c142c482146c1545af1c24) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_CONTINUE( 0x400000, 0x100000 )
	ROM_LOAD16_BYTE( "044-c2.c2", 0x000001, 0x100000, CRC(d8ccd575) SHA1(f697263fe92164e274bf34c55327b3d4a158b332) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_CONTINUE( 0x400001, 0x100000 )
	ROM_LOAD16_BYTE( "044-c3.c3", 0x200000, 0x100000, CRC(403e898a) SHA1(dd5888f8b24a33b2c1f483316fe80c17849ccfc4) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_CONTINUE( 0x600000, 0x100000 )
	ROM_LOAD16_BYTE( "044-c4.c4", 0x200001, 0x100000, CRC(6235fbaa) SHA1(9090e337d7beed25ba81ae0708d0aeb57e6cf405) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_CONTINUE( 0x600001, 0x100000 )
ROM_END

/****************************************
 ID-0045
 . NGM-045
 NEO-MVS PROGGSC / NEO-MVS CHA 42G-3
 . NGH-045
 NEO-AEG PROGGS / NEO-AEG CHA42G-4
****************************************/

ROM_START( samsho ) /* MVS VERSION */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "045-p1.p1",   0x000000, 0x100000, CRC(dfe51bf0) SHA1(2243af3770a516ae698b69bcd9daf53632d9128d) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "045-pg2.sp2", 0x100000, 0x100000, CRC(46745b94) SHA1(d9e959fd1f88c9402915c1d0dcdb4a9e3d49cdcb) ) /* TC538200 */
	/* also found set with EP1 / EP2 on eprom and P2 on maskrom; same rom data as samshoh is used. */

	NEO_SFIX_128K( "045-s1.s1", CRC(9142a4d3) SHA1(54088e99fcfd75fd0f94852890a56350066a05a3) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "045-m1.m1", CRC(95170640) SHA1(125c502db0693e8d11cef619b090081c14a9a300) ) /* TC531001 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "045-v1.v1", 0x000000, 0x200000, CRC(37f78a9b) SHA1(6279b497d12fa90b49ab5ac3aae20fb302ec8b81) ) /* TC5316200 */
	ROM_LOAD( "045-v2.v2", 0x200000, 0x200000, CRC(568b20cf) SHA1(61af858685472a1fad608e230cccc2b108509ddb) ) /* TC5316200 */

	ROM_REGION( 0xa00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "045-c1.c1",  0x000000, 0x200000, CRC(2e5873a4) SHA1(65c74c1e2d34390666bbb630df7d1f4c9570c3db) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "045-c2.c2",  0x000001, 0x200000, CRC(04febb10) SHA1(16a8cbf0fd9468e81bf9eab6dbe7a8e3623a843e) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "045-c3.c3",  0x400000, 0x200000, CRC(f3dabd1e) SHA1(c80e52df42be9f8b2e89b467b11ab140a480cee8) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "045-c4.c4",  0x400001, 0x200000, CRC(935c62f0) SHA1(0053d40085fac14096b683f4341f65e543b71dc1) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "045-c51.c5", 0x800000, 0x100000, CRC(81932894) SHA1(550f15dc5892c4602422c51869f0d59f70f01e9e) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "045-c61.c6", 0x800001, 0x100000, CRC(be30612e) SHA1(5e8b785f917c176d6796eba0caed37b13ddb3e63) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

ROM_START( samshoh ) /* AES VERSION */
	ROM_REGION( 0x180000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "045-p1.p1",  0x000000, 0x100000, CRC(dfe51bf0) SHA1(2243af3770a516ae698b69bcd9daf53632d9128d) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "045-p2.sp2", 0x100000, 0x080000, CRC(38ee9ba9) SHA1(48190699a6be83cb6257365ae81f93fdd23abe09) ) /* TC534200 */

	NEO_SFIX_128K( "045-s1.s1", CRC(9142a4d3) SHA1(54088e99fcfd75fd0f94852890a56350066a05a3) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "045-m1.m1", CRC(95170640) SHA1(125c502db0693e8d11cef619b090081c14a9a300) ) /* TC531001 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "045-v1.v1", 0x000000, 0x200000, CRC(37f78a9b) SHA1(6279b497d12fa90b49ab5ac3aae20fb302ec8b81) ) /* TC5316200 */
	ROM_LOAD( "045-v2.v2", 0x200000, 0x200000, CRC(568b20cf) SHA1(61af858685472a1fad608e230cccc2b108509ddb) ) /* TC5316200 */

	ROM_REGION( 0x900000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "045-c1.c1", 0x000000, 0x200000, CRC(2e5873a4) SHA1(65c74c1e2d34390666bbb630df7d1f4c9570c3db) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "045-c2.c2", 0x000001, 0x200000, CRC(04febb10) SHA1(16a8cbf0fd9468e81bf9eab6dbe7a8e3623a843e) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "045-c3.c3", 0x400000, 0x200000, CRC(f3dabd1e) SHA1(c80e52df42be9f8b2e89b467b11ab140a480cee8) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "045-c4.c4", 0x400001, 0x200000, CRC(935c62f0) SHA1(0053d40085fac14096b683f4341f65e543b71dc1) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "045-c5.c5", 0x800000, 0x080000, CRC(a2bb8284) SHA1(aa118e3b8c062daa219b36758b9a3814c08c69dc) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "045-c6.c6", 0x800001, 0x080000, CRC(4fa71252) SHA1(afe374a9d1f2d955a59efe7b6196b89e021b164c) ) /* Plane 2,3 */ /* TC534200 */
	/* also found AES set with C6 on TC538200; label is 045-C61.C6; same rom data as samsho */
ROM_END

/****************************************
 ID-0046
 . NGM-046
 NEO-MVS PROGTOP / NEO-MVS CHA256B
 . NGH-046
 NEO-AEG PROGTOP / NEO-AEG CHA256[B]
****************************************/

ROM_START( tophuntr ) /* MVS VERSION */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "046-p1.p1",  0x000000, 0x100000, CRC(69fa9e29) SHA1(9a40a16163193bb506a32bd34f6323b25ec69622) ) /* mask rom TC538200 */
	ROM_LOAD16_WORD_SWAP( "046-p2.sp2", 0x100000, 0x100000, CRC(f182cb3e) SHA1(6b4e0af5d4e623f0682f37ff5c69e5b705e20028) ) /* mask rom TC538200 */

	NEO_SFIX_128K( "046-s1.s1", CRC(14b01d7b) SHA1(618ce75c25d6cc86a3b46bd64a0aa34ab82f75ae) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "046-m1.m1", CRC(3f84bb9f) SHA1(07446040871d11da3c2217ee9d1faf8c3cae7420) ) /* mask rom TC531001 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "046-v1.v1", 0x000000, 0x100000, CRC(c1f9c2db) SHA1(bed95a76afefa46503a12e0f0a9787c4c967ac50) ) /* mask rom TC538200 */
	ROM_LOAD( "046-v2.v2", 0x100000, 0x100000, CRC(56254a64) SHA1(1cf049cb4c414419859d2c8ee714317a35a85251) ) /* mask rom TC538200 */
	ROM_LOAD( "046-v3.v3", 0x200000, 0x100000, CRC(58113fb1) SHA1(40972982a63c7adecef840f9882f4165da723ab6) ) /* mask rom TC538200 */
	ROM_LOAD( "046-v4.v4", 0x300000, 0x100000, CRC(4f54c187) SHA1(63a76949301b83bdd44aa1a4462f642ab9ca3c0b) ) /* mask rom TC538200 */

	ROM_REGION( 0x800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "046-c1.c1", 0x000000, 0x100000, CRC(fa720a4a) SHA1(364913b9fa40d46e4e39ae3cdae914cfd0de137d) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "046-c2.c2", 0x000001, 0x100000, CRC(c900c205) SHA1(50274e79aa26f334eb806288688b30720bade883) ) /* Plane 2,3 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "046-c3.c3", 0x200000, 0x100000, CRC(880e3c25) SHA1(b6974af0c833b766866919b6f15b6f8cef82530d) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "046-c4.c4", 0x200001, 0x100000, CRC(7a2248aa) SHA1(8af0b26025a54e3b91604dd24a3c1c518fbd8536) ) /* Plane 2,3 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "046-c5.c5", 0x400000, 0x100000, CRC(4b735e45) SHA1(2f8b46388c4696aee6a97e1e21cdadf6b142b01a) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "046-c6.c6", 0x400001, 0x100000, CRC(273171df) SHA1(9c35832221e016c12ef1ed71da167f565daaf86c) ) /* Plane 2,3 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "046-c7.c7", 0x600000, 0x100000, CRC(12829c4c) SHA1(ac5f3d848d7116fc35c97f53a72c85e049dd3a2f) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "046-c8.c8", 0x600001, 0x100000, CRC(c944e03d) SHA1(be23999b8ce09ee15ba500ce4d5e2a82a4f58d9b) ) /* Plane 2,3 */ /* mask rom TC538200 */
ROM_END

ROM_START( tophuntrh ) /* AES VERSION */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "046-pg1.p1",0x000000, 0x100000, CRC(771e39bc) SHA1(c0e05fd1ca81926438bb75e2fa6894e40ab6521e) ) /* mask rom TC538200 */
	ROM_LOAD16_WORD_SWAP( "046-p2.sp2",0x100000, 0x100000, CRC(f182cb3e) SHA1(6b4e0af5d4e623f0682f37ff5c69e5b705e20028) ) /* mask rom TC538200 */

	NEO_SFIX_128K( "046-sg1.s1", CRC(92e9ac8c) SHA1(cab5c77c091e8d12d9c3a2cc8d741b74e4386efb) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "046-m1.m1", CRC(3f84bb9f) SHA1(07446040871d11da3c2217ee9d1faf8c3cae7420) ) /* mask rom TC531001 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "046-v1.v1", 0x000000, 0x100000, CRC(c1f9c2db) SHA1(bed95a76afefa46503a12e0f0a9787c4c967ac50) ) /* mask rom TC538200 */
	ROM_LOAD( "046-v2.v2", 0x100000, 0x100000, CRC(56254a64) SHA1(1cf049cb4c414419859d2c8ee714317a35a85251) ) /* mask rom TC538200 */
	ROM_LOAD( "046-v3.v3", 0x200000, 0x100000, CRC(58113fb1) SHA1(40972982a63c7adecef840f9882f4165da723ab6) ) /* mask rom TC538200 */
	ROM_LOAD( "046-v4.v4", 0x300000, 0x100000, CRC(4f54c187) SHA1(63a76949301b83bdd44aa1a4462f642ab9ca3c0b) ) /* mask rom TC538200 */

	ROM_REGION( 0x800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "046-c1.c1", 0x000000, 0x100000, CRC(fa720a4a) SHA1(364913b9fa40d46e4e39ae3cdae914cfd0de137d) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "046-c2.c2", 0x000001, 0x100000, CRC(c900c205) SHA1(50274e79aa26f334eb806288688b30720bade883) ) /* Plane 2,3 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "046-c3.c3", 0x200000, 0x100000, CRC(880e3c25) SHA1(b6974af0c833b766866919b6f15b6f8cef82530d) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "046-c4.c4", 0x200001, 0x100000, CRC(7a2248aa) SHA1(8af0b26025a54e3b91604dd24a3c1c518fbd8536) ) /* Plane 2,3 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "046-c5.c5", 0x400000, 0x100000, CRC(4b735e45) SHA1(2f8b46388c4696aee6a97e1e21cdadf6b142b01a) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "046-c6.c6", 0x400001, 0x100000, CRC(273171df) SHA1(9c35832221e016c12ef1ed71da167f565daaf86c) ) /* Plane 2,3 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "046-c7.c7", 0x600000, 0x100000, CRC(12829c4c) SHA1(ac5f3d848d7116fc35c97f53a72c85e049dd3a2f) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "046-c8.c8", 0x600001, 0x100000, CRC(c944e03d) SHA1(be23999b8ce09ee15ba500ce4d5e2a82a4f58d9b) ) /* Plane 2,3 */ /* mask rom TC538200 */
ROM_END

/****************************************
 ID-0047
 . NGM-047
 NEO-MVS PROG-G2 (SNK-9201) / NEO-MVS CHA42G-1
 NEO-MVS PROG-G2 (SNK-9201) / NEO-MVS CHA42G-2
 . NGH-047
 NEO-AEG PROG-G2 (PRO-CT0) / NEO-AEG CHA42G-2B
 NEO-AEG PROG-G2 (PRO-CT0) / NEO-AEG CHA42G-2
****************************************/

ROM_START( fatfury2 ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "047-p1.p1", 0x000000, 0x100000, CRC(ecfdbb69) SHA1(59e2f137c6eaf043df4ddae865a9159a10265c60) ) /* TC538200 */
	/* The original P1 is 8mbit; also found sets with EP1 / EP2 4mbit on eprom. */

	NEO_SFIX_128K( "047-s1.s1", CRC(d7dbbf39) SHA1(29253e596f475ebd41a6e3bb53952e3a0ccd2eed) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "047-m1.m1", CRC(820b0ba7) SHA1(5708248d89446e49184eaadb52f7c61b2b6c13c5) ) /* TC531001 */
	/* also found sets with M1 on eprom */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "047-v1.v1", 0x000000, 0x200000, CRC(d9d00784) SHA1(f6a91eada8c23aa4518c4b82eeebca69f79d845c) ) /* TC5316200 */
	ROM_LOAD( "047-v2.v2", 0x200000, 0x200000, CRC(2c9a4b33) SHA1(d4a1c0951c02c8919b3ec32ed96933634ff9e54c) ) /* TC5316200 */

	ROM_REGION( 0x800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "047-c1.c1", 0x000000, 0x100000, CRC(f72a939e) SHA1(67fc398ec28061adca0d3be82bbe7297015800da) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_CONTINUE( 0x400000, 0x100000 )
	ROM_LOAD16_BYTE( "047-c2.c2", 0x000001, 0x100000, CRC(05119a0d) SHA1(c2f100b73eb04f65b6ba6089d49aceb51b470ec6) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_CONTINUE( 0x400001, 0x100000 )
	ROM_LOAD16_BYTE( "047-c3.c3", 0x200000, 0x100000, CRC(01e00738) SHA1(79654f24d777dd5eb68bafc3b8cb9db71d5822e2) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_CONTINUE( 0x600000, 0x100000 )
	ROM_LOAD16_BYTE( "047-c4.c4", 0x200001, 0x100000, CRC(9fe27432) SHA1(89d22d77ba8bc6d1f6c974195c34ad61b9010de7) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_CONTINUE( 0x600001, 0x100000 )
ROM_END

/****************************************
 ID-0048
 . ??M-048
 NEO-MVS PROGGSC / NEO-MVS CHA256
****************************************/

ROM_START( janshin ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "048-p1.p1", 0x000000, 0x100000, CRC(fa818cbb) SHA1(afee2c897b766c84f13891fb52c574fb18df0951) ) /* mask rom TC538200 */

	NEO_SFIX_128K( "048-s1.s1", CRC(8285b25a) SHA1(d983640cda3e346e38469b4d3ec8048b116a7bb7) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "048-m1.m1", CRC(310467c7) SHA1(c529961195c9bdf5d1ce70a38ad129002d1f3b5f) ) /* mask rom TC531001 */

	ROM_DEFAULT_BIOS( "japan" ) /* so the mahjong panel will work in the service menu */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "048-v1.v1", 0x000000, 0x200000, CRC(f1947d2b) SHA1(955ff91ab24eb2a7ec51ff46c9f9f2ec060456b2) ) /* mask rom TC5316200 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "048-c1.c1", 0x000000, 0x200000, CRC(3fa890e9) SHA1(e73d2802bacfbc2b2b16fbbedddde17488e4bbde) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "048-c2.c2", 0x000001, 0x200000, CRC(59c48ad8) SHA1(2630817e735a6d197377558f4324c1442803fe15) ) /* Plane 2,3 */ /* mask rom TC5316200 */
ROM_END

/****************************************
 ID-0049
 . NGM-049
 NEO-MVS PROG16 / NEO-MVS CHA42G-1
 . NGH-049
 NEO-AEG PROG16 / NEO-AEG CHA42G-1
****************************************/

ROM_START( androdun ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "049-p1.p1", 0x000000, 0x080000, CRC(3b857da2) SHA1(4dd86c739944696c16c3cdd85935d6dfa9fdc276) ) /* CXK384500 */
	ROM_LOAD16_WORD_SWAP( "049-p2.p2", 0x080000, 0x080000, CRC(2f062209) SHA1(991cf3e3677929b2cc0b2787b0c7b6ad3700f618) ) /* CXK384500 */

	NEO_SFIX_128K( "049-s1.s1", CRC(6349de5d) SHA1(bcc44b9576d7bedd9a39294530bb66f707690c72) ) /* CXK381000 */

	NEO_BIOS_AUDIO_128K( "049-m1.m1", CRC(edd2acf4) SHA1(c4ee6ba834d54b9fc5a854dbc41a05877e090371) ) /* CXK381003 */

	ROM_REGION( 0x100000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "049-v1.v1", 0x000000, 0x100000, CRC(ce43cb89) SHA1(47f82e077abb6efc6b1b0490412ae147d5d2acef) ) /* CXK388000 */

	ROM_REGION( 0x200000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "049-c1.c1", 0x000000, 0x100000, CRC(7ace6db3) SHA1(c41cc9de8c0788dcc49ca494fd3bb3124062d9dd) ) /* Plane 0,1 */ /* CXK388000 */
	ROM_LOAD16_BYTE( "049-c2.c2", 0x000001, 0x100000, CRC(b17024f7) SHA1(fcf7efae48fcdccaf5255c145de414fb246128f0) ) /* Plane 2,3 */ /* CXK388000 */
ROM_END

/****************************************
 ID-0050
 . ALM-004
 NEO-MVS PROG42G-1 / NEO-MVS CHA42G-1
 . ALH-004
 NEO-AEG PROG42G-1 / NEO-AEG CHA42G-1
****************************************/

ROM_START( ncommand )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "050-p1.p1", 0x000000, 0x100000, CRC(4e097c40) SHA1(43311a7ca14a14dcd4a99d8576a12e897b078643) ) /* CXK388000 */

	NEO_SFIX_128K( "050-s1.s1", CRC(db8f9c8e) SHA1(11cb82cf3c4d0fc2da5df0c26410a64808093610) ) /* CXK381000 */

	NEO_BIOS_AUDIO_128K( "050-m1.m1", CRC(6fcf07d3) SHA1(e9ecff4bfec1f5964bf06645f75d80d611b6231c) ) /* CXK381003 */

	ROM_REGION( 0x180000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "050-v1.v1", 0x000000, 0x100000, CRC(23c3ab42) SHA1(b6c59bb180f1aa34c95f3ec923f3aafb689d57b0) ) /* CXK388000 */
	ROM_LOAD( "050-v2.v2", 0x100000, 0x080000, CRC(80b8a984) SHA1(950cf0e78ceffa4037663f1086fbbc88588f49f2) ) /* CXK384000 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "050-c1.c1", 0x000000, 0x100000, CRC(87421a0a) SHA1(1d8faaf03778f7c5b062554d7333bbd3f0ca12ad) ) /* Plane 0,1 */ /* CXK388000 */
	ROM_LOAD16_BYTE( "050-c2.c2", 0x000001, 0x100000, CRC(c4cf5548) SHA1(ef9eca5aeff9dda2209a050c2af00ed8979ae2bc) ) /* Plane 2,3 */ /* CXK388000 */
	ROM_LOAD16_BYTE( "050-c3.c3", 0x200000, 0x100000, CRC(03422c1e) SHA1(920e5015aebe2ffc5ce43a52365c7f0a705f3b9e) ) /* Plane 0,1 */ /* CXK388000 */
	ROM_LOAD16_BYTE( "050-c4.c4", 0x200001, 0x100000, CRC(0845eadb) SHA1(3c71a02bf0e07a5381846bb6d75bbe7dd546adea) ) /* Plane 2,3 */ /* CXK388000 */
ROM_END

/****************************************
 ID-0051
 . AIM-051
 NEO-MVS PROG-G2 / NEO-MVS CHA42G-1
 NEO-MVS PROG 4096 / NEO-MVS CHA 42G-2
 . AIH-051
 NEO-AEG PROG16 / NEO-AEG CHA42G-1
****************************************/

ROM_START( viewpoin )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "051-p1.p1", 0x000000, 0x100000, CRC(17aa899d) SHA1(674cd8ace7acdf4f407de741e3d0071bcb49c902) ) /* CXK388002 */

	NEO_SFIX_128K( "051-s1.s1", CRC(9fea5758) SHA1(5c6f01da89f2639cf741ee7c39e27023b8083052) ) /* CXK381000 */

	NEO_BIOS_AUDIO_128K( "051-m1.m1", CRC(8e69f29a) SHA1(7a25f4997996434ea1b7d0d1ca9e7aaf966cbd03) ) /* CXK381003 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	/* What board was originally used (labels 051-V2 and 051-V4)? MVS is twice confirmed on NEO-MVS PROG-G2 */
	ROM_LOAD( "051-v2.v1", 0x000000, 0x200000, CRC(019978b6) SHA1(7896a551115fc6ed38b5944e0c8dcb2b2c1c077d) ) /* CXK381600 */
	ROM_LOAD( "051-v4.v2", 0x200000, 0x200000, CRC(5758f38c) SHA1(da10f4b7d22d9139bbf068bd940be82168a74ca1) ) /* CXK381600 */

	ROM_REGION( 0x600000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "051-c1.c1", 0x000000, 0x100000, CRC(d624c132) SHA1(49c7e9f020cba45d7083b45252bcc03397f8c286) ) /* Plane 0,1 */ /* CXK381600 */
	ROM_CONTINUE( 0x400000, 0x100000 )
	ROM_LOAD16_BYTE( "051-c2.c2", 0x000001, 0x100000, CRC(40d69f1e) SHA1(ec4a13582772594957f927622d50f54b0dfcd8d8) ) /* Plane 2,3 */ /* CXK381600 */
	ROM_CONTINUE( 0x400001, 0x100000 )
ROM_END

ROM_START( viewpoinp ) // crashes during stage 4 on real hardware (but stage 5 can be loaded with memcard)
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_BYTE( "p1", 0x000001, 0x080000, CRC(0adb5eee) SHA1(1766652601cfb69b227aa0a9cecb7039b45c19b5) )
	ROM_LOAD16_BYTE( "p2", 0x000000, 0x080000, CRC(31129194) SHA1(b088a0d400808a75a95895083845defdc01ed531) )

	NEO_SFIX_128K( "s1", CRC(9fea5758) SHA1(5c6f01da89f2639cf741ee7c39e27023b8083052) )

	NEO_BIOS_AUDIO_128K( "m1", CRC(8e69f29a) SHA1(7a25f4997996434ea1b7d0d1ca9e7aaf966cbd03) )

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "v1", 0x000000, 0x100000, CRC(7e9af736) SHA1(0a47080d48bf220f7388fa1338924b6b94149c33) )
	ROM_LOAD( "v2", 0x100000, 0x100000, CRC(503a9c51) SHA1(2ecfe6c0cf348a01121399176bb84bf6a6ff2a45) )
	ROM_LOAD( "v3", 0x200000, 0x100000, CRC(dfa8d079) SHA1(d33f94bd132aa7d5b953cc1be8c2bb22f63bd13c) )
	ROM_LOAD( "v4", 0x300000, 0x100000, CRC(612b9292) SHA1(edf407150997a83c2d003be972e1f9dc781b07d1) )

	ROM_REGION( 0x400000, "cslot1:sprites", 0 ) // the actual GFX are the same, but there's no gap between 0x200000-0x3fffff on the prototype, and the data pointers in the program ROM differ because of this
	ROM_LOAD32_BYTE( "c1", 0x000000, 0x080000, CRC(97a3183b) SHA1(2531681c28f39adc9de2dc360970c38b71fcf0d7) )
	ROM_LOAD32_BYTE( "c2", 0x000002, 0x080000, CRC(f637667a) SHA1(477d44f7d8505eecf97010343fd186681bbe3318) )
	ROM_LOAD32_BYTE( "c3", 0x000001, 0x080000, CRC(b017d2d1) SHA1(160bd4f3f4c09a244a9cfdd076f37299ec0ae4d5) )
	ROM_LOAD32_BYTE( "c4", 0x000003, 0x080000, CRC(10f1624a) SHA1(762fd106886c7f13cb29c97c3a3af9fbd6b30405) )
	ROM_LOAD32_BYTE( "c5", 0x200000, 0x080000, CRC(714a8bc4) SHA1(5f69a7e6acaf6916d76f3de6465a5b548d90af68) )
	ROM_LOAD32_BYTE( "c6", 0x200002, 0x080000, CRC(5bc32495) SHA1(d9e2635de3fe60d4adcc11d4acde2d613808c0b8) )
	ROM_LOAD32_BYTE( "c7", 0x200001, 0x080000, CRC(28a0a127) SHA1(488fa8acd474cd357e3d18043341bc7b5a1a654e) )
	ROM_LOAD32_BYTE( "c8", 0x200003, 0x080000, CRC(439c9036) SHA1(7a5b965ceab9343ece3b6d02cc6dbfc83ec67f04) )
ROM_END

/****************************************
 ID-0052
 . NGM-052
 NEO-MVS PROG-G2 (SNK-9201) / NEO-MVS CHA 42G-2
 . NGH-052
 NEO-AEG PROG-G2 (SNK-9201) / NEO-AEG CHA42G-2
****************************************/

ROM_START( ssideki )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "052-p1.p1", 0x000000, 0x080000, CRC(9cd97256) SHA1(1c780b711137fd79cc81b01941e84f3d59e0071f) ) /* TC534200 */

	NEO_SFIX_128K( "052-s1.s1", CRC(97689804) SHA1(fa8dab3b3353d7115a0368f3fc749950c0186fbc) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "052-m1.m1", CRC(49f17d2d) SHA1(70971fcf71ae3a6b2e26e7ade8063941fb178ae5) ) /* TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "052-v1.v1", 0x000000, 0x200000, CRC(22c097a5) SHA1(328c4e6db0a026f54a633cff1443a3f964a8daea) ) /* TC5316200 */

	ROM_REGION( 0x600000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "052-c1.c1", 0x000000, 0x100000, CRC(53e1c002) SHA1(2125b1be379ea7933893ffb1cd65d6c4bf8b03bd) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_CONTINUE( 0x400000, 0x100000 )
	ROM_LOAD16_BYTE( "052-c2.c2", 0x000001, 0x100000, CRC(776a2d1f) SHA1(bca0bac87443e9e78c623d284f6cc96cc9c9098f) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_CONTINUE( 0x400001, 0x100000 )
ROM_END

/****************************************
 ID-0053
 . ALM-005
 NEO-MVS PROG16 / NEO-MVS CHA42G-1
 . ALH-005
 NEO-AEG PROG16 / NEO-AEG CHA42G-1
 NEO-AEG PROG-G2 (SNK-9201) / NEO-AEG CHA42G-2C
****************************************/

ROM_START( wh1 ) /* MVS VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "053-epr.p1", 0x000000, 0x080000, CRC(d42e1e9a) SHA1(9d1911fe4bf6202466ec45557dd008cbf01ca9c6) ) /* D27C4000 */
	ROM_LOAD16_WORD_SWAP( "053-epr.p2", 0x080000, 0x080000, CRC(0e33e8a3) SHA1(4b7086edb504f3c30529d51ba8f453d48eba5164) ) /* D27C4000 */
	/* P's on eprom, correct chip label unknown */

	NEO_SFIX_128K( "053-s1.s1", CRC(8c2c2d6b) SHA1(87fa79611c6f8886dcc8766814829c669c65b40f) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "053-m1.m1", CRC(1bd9d04b) SHA1(65cd7b002123ed1a3111e3d942608d0082799ff3) ) /* TC54H1000 */

	ROM_REGION( 0x300000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "053-v2.v2", 0x000000, 0x200000, CRC(a68df485) SHA1(007fa715423fba72c899cd3db3f4bec13281cf7a) ) /* TC5316200 */
	ROM_LOAD( "053-v4.v4", 0x200000, 0x100000, CRC(7bea8f66) SHA1(428e8721bd87f7faa756adb1e12672219be46c1d) ) /* TC538200 */

	ROM_REGION( 0x600000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "053-c1.c1", 0x000000, 0x100000, CRC(85eb5bce) SHA1(3d03d29296ca6e6b5106aac4aaeec9d4b4ed1313) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_CONTINUE( 0x400000, 0x100000 )
	ROM_LOAD16_BYTE( "053-c2.c2", 0x000001, 0x100000, CRC(ec93b048) SHA1(d4159210df94e259f874a4671d271ec27be13451) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_CONTINUE( 0x400001, 0x100000 )
	ROM_LOAD16_BYTE( "053-c3.c3", 0x200000, 0x100000, CRC(0dd64965) SHA1(e97b3b8a461da5e8861b3dfdacb25e007ced37a1) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "053-c4.c4", 0x200001, 0x100000, CRC(9270d954) SHA1(a2ef909868f6b06cdcc22a63ddf6c96be12b999c) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

ROM_START( wh1h ) /* AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "053-p1.p1", 0x000000, 0x080000, CRC(95b574cb) SHA1(b7b7af6a04c3d902e7f8852897741ecaf0b1062c) ) /* TC534200 */
	ROM_LOAD16_WORD_SWAP( "053-p2.p2", 0x080000, 0x080000, CRC(f198ed45) SHA1(24ccc091e97f63796562bb5b30df51f39bd504ef) ) /* TC534200 */

	NEO_SFIX_128K( "053-s1.s1", CRC(8c2c2d6b) SHA1(87fa79611c6f8886dcc8766814829c669c65b40f) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "053-m1.m1", CRC(1bd9d04b) SHA1(65cd7b002123ed1a3111e3d942608d0082799ff3) ) /* TC531001 */

	ROM_REGION( 0x300000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "053-v2.v2", 0x000000, 0x200000, CRC(a68df485) SHA1(007fa715423fba72c899cd3db3f4bec13281cf7a) ) /* TC5316200 */
	ROM_LOAD( "053-v4.v4", 0x200000, 0x100000, CRC(7bea8f66) SHA1(428e8721bd87f7faa756adb1e12672219be46c1d) ) /* TC538200 */

	ROM_REGION( 0x600000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "053-c1.c1", 0x000000, 0x100000, CRC(85eb5bce) SHA1(3d03d29296ca6e6b5106aac4aaeec9d4b4ed1313) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_CONTINUE( 0x400000, 0x100000 )
	ROM_LOAD16_BYTE( "053-c2.c2", 0x000001, 0x100000, CRC(ec93b048) SHA1(d4159210df94e259f874a4671d271ec27be13451) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_CONTINUE( 0x400001, 0x100000 )
	ROM_LOAD16_BYTE( "053-c3.c3", 0x200000, 0x100000, CRC(0dd64965) SHA1(e97b3b8a461da5e8861b3dfdacb25e007ced37a1) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "053-c4.c4", 0x200001, 0x100000, CRC(9270d954) SHA1(a2ef909868f6b06cdcc22a63ddf6c96be12b999c) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

ROM_START( wh1ha )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "053-p1_a.p1", 0x000000, 0x080000, CRC(ed29fde2) SHA1(52b8ca5b804f786f95e1dfb348d8c7b82f1d4ddf) ) /* D27C4000 */
	ROM_LOAD16_WORD_SWAP( "053-p2_a.p2", 0x080000, 0x080000, CRC(98f2b158) SHA1(a64e1425970eb53cc910891db39973dee3d54ccc) ) /* D27C4000 */
	/* Correct chip labels for p1 and p2 unknown */

	NEO_SFIX_128K( "053-s1.s1", CRC(8c2c2d6b) SHA1(87fa79611c6f8886dcc8766814829c669c65b40f) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "053-m1.m1", CRC(1bd9d04b) SHA1(65cd7b002123ed1a3111e3d942608d0082799ff3) ) /* TC531001 */

	ROM_REGION( 0x300000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "053-v2.v2", 0x000000, 0x200000, CRC(a68df485) SHA1(007fa715423fba72c899cd3db3f4bec13281cf7a) ) /* TC5316200 */
	ROM_LOAD( "053-v4.v4", 0x200000, 0x100000, CRC(7bea8f66) SHA1(428e8721bd87f7faa756adb1e12672219be46c1d) ) /* TC538200 */

	ROM_REGION( 0x600000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "053-c1.c1", 0x000000, 0x100000, CRC(85eb5bce) SHA1(3d03d29296ca6e6b5106aac4aaeec9d4b4ed1313) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_CONTINUE( 0x400000, 0x100000 )
	ROM_LOAD16_BYTE( "053-c2.c2", 0x000001, 0x100000, CRC(ec93b048) SHA1(d4159210df94e259f874a4671d271ec27be13451) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_CONTINUE( 0x400001, 0x100000 )
	ROM_LOAD16_BYTE( "053-c3.c3", 0x200000, 0x100000, CRC(0dd64965) SHA1(e97b3b8a461da5e8861b3dfdacb25e007ced37a1) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "053-c4.c4", 0x200001, 0x100000, CRC(9270d954) SHA1(a2ef909868f6b06cdcc22a63ddf6c96be12b999c) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

/****************************************
 ID-0054
 Crossed Swords 2 (CD only? not confirmed, MVS might exist)
****************************************/

/****************************************
 ID-0055
 . NGM-055
 NEO-MVS PROGTOP / NEO-MVS CHA256
 NEO-MVS PROGTOP / NEO-MVS CHA256B
 . NGH-055
 NEO-AEG PROGRK / NEO-AEG CHA256
****************************************/

ROM_START( kof94 ) /* MVS AND AES VERSION */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "055-p1.p1", 0x100000, 0x100000, CRC(f10a2042) SHA1(d08a3f3c28be4b1793de7d362456281329fe1828) ) /* mask rom TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "055-s1.s1", CRC(825976c1) SHA1(cb6a70bdd95d449d25196ca269b621c362db6743) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "055-m1.m1", CRC(f6e77cf5) SHA1(292a3e3a4918ffe72bd1c41acb927b91844e035e) ) /* mask rom TC531001 */

	ROM_REGION( 0x600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "055-v1.v1", 0x000000, 0x200000, CRC(8889596d) SHA1(c9ce713b720511438dbd3fe3bcc7c246f475c6a2) ) /* mask rom TC5316200 */
	ROM_LOAD( "055-v2.v2", 0x200000, 0x200000, CRC(25022b27) SHA1(2b040a831c3c92ac6e4719de38009a0d55b64f6b) ) /* mask rom TC5316200 */
	ROM_LOAD( "055-v3.v3", 0x400000, 0x200000, CRC(83cf32c0) SHA1(34a31a37eb10945b5169e96321bcea06eec33a00) ) /* mask rom TC5316200 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "055-c1.c1", 0x000000, 0x200000, CRC(b96ef460) SHA1(e52f5303c17b50ce165c008be2837336369c110b) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "055-c2.c2", 0x000001, 0x200000, CRC(15e096a7) SHA1(237c2a3d059de00bfca66e0016ed325d7a32bfec) ) /* Plane 2,3 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "055-c3.c3", 0x400000, 0x200000, CRC(54f66254) SHA1(c594384bcd8b03beb8c595591505fecc44b185ac) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "055-c4.c4", 0x400001, 0x200000, CRC(0b01765f) SHA1(ec1fdcc944611408367bf5023d4ebe7edd9dfa88) ) /* Plane 2,3 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "055-c5.c5", 0x800000, 0x200000, CRC(ee759363) SHA1(8a5621c1b1f8267b9b9b6a14ab4944de542e1945) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "055-c6.c6", 0x800001, 0x200000, CRC(498da52c) SHA1(1e6e6202ee053a5261db889177ce3a087e078bda) ) /* Plane 2,3 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "055-c7.c7", 0xc00000, 0x200000, CRC(62f66888) SHA1(ac91a0eab0753bee175ad40213a4ae5d38ed5b87) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "055-c8.c8", 0xc00001, 0x200000, CRC(fe0a235d) SHA1(a45c66836e4e3c77dfef9d4c6cc422cb59169149) ) /* Plane 2,3 */ /* mask rom TC5316200 */
ROM_END

/****************************************
 ID-0056
 . NGM-056
 NEO-MVS PROGGSC / NEO-MVS CHA256
 . NGH-056
 NEO-AEG PROGRKB / NEO-AEG CHA256[B]
****************************************/

ROM_START( aof2 ) /* MVS VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "056-p1.p1", 0x000000, 0x100000, CRC(a3b1d021) SHA1(ee42f3ca4516226b0088d0303ed28e3ecdabcd71) ) /* TC538200 */
	/* also found set with EP1 / EP2 on eprom; correct chip label unknown */

	NEO_SFIX_128K( "056-s1.s1", CRC(8b02638e) SHA1(aa4d28804ca602da776948b5f223ea89e427906b) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "056-m1.m1", CRC(f27e9d52) SHA1(dddae733d87ce7c88ad2580a8f64cb6ff9572e67) ) /* TC531001 */

	ROM_REGION( 0x500000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "056-v1.v1", 0x000000, 0x200000, CRC(4628fde0) SHA1(ece2a50f5270d844d58401b1447d1d856d78ea45) ) /* TC5316200 */
	ROM_LOAD( "056-v2.v2", 0x200000, 0x200000, CRC(b710e2f2) SHA1(df4da585203eea7554d3ce718eb107e9cb6a0254) ) /* TC5316200 */
	ROM_LOAD( "056-v3.v3", 0x400000, 0x100000, CRC(d168c301) SHA1(969273d1d11943e81560959359a2c4e69522af0e) ) /* TC538200 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	/* Different layout with 4xC (32mbit) also exists; chip labels are 056-C13, 056-C24, 056-C57 and 056-C68 */
	ROM_LOAD16_BYTE( "056-c1.c1", 0x000000, 0x200000, CRC(17b9cbd2) SHA1(1eee81e02763d384bd1c10a6012473ca931e4093) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "056-c2.c2", 0x000001, 0x200000, CRC(5fd76b67) SHA1(11925a41a53b53c6df4a5ebd28f98300950f743b) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "056-c3.c3", 0x400000, 0x200000, CRC(d2c88768) SHA1(22e2d84aa0c095944190e249ce87ef50d3f7b8ce) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "056-c4.c4", 0x400001, 0x200000, CRC(db39b883) SHA1(59de86c513dc4e230ae25d9e3b7e84621b657b54) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "056-c5.c5", 0x800000, 0x200000, CRC(c3074137) SHA1(9a75e3d63cb98d54f900dcfb3a03e21f3148d32f) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "056-c6.c6", 0x800001, 0x200000, CRC(31de68d3) SHA1(13ba7046cdd6863125f8284e60f102d4720af5a4) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "056-c7.c7", 0xc00000, 0x200000, CRC(3f36df57) SHA1(79ee97e9ae811a51141b535633f90e1491209d54) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "056-c8.c8", 0xc00001, 0x200000, CRC(e546d7a8) SHA1(74a2fca994a5a93a5784a46c0f68193122456a09) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

ROM_START( aof2a ) /* AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	/* Also found AES sets with P1 and SP2 on maskrom; chip label are 056-P1 and 056-SP2G */
	ROM_LOAD16_WORD_SWAP( "056-p1.p1",  0x000000, 0x100000, CRC(a3b1d021) SHA1(ee42f3ca4516226b0088d0303ed28e3ecdabcd71) ) /* TC538200 */
	/* the rom below acts as a patch to the program rom in the cart, replacing the first 512kb */
	ROM_LOAD16_WORD_SWAP( "056-epr.ep1", 0x000000, 0x80000, CRC(75d6301c) SHA1(e72d15fba55f96be7b4fa29e705a7b78f56edf7d) ) /* M27C4002 */
	/* P is on eprom, correct chip label unknown */

	NEO_SFIX_128K( "056-s1.s1", CRC(8b02638e) SHA1(aa4d28804ca602da776948b5f223ea89e427906b) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "056-m1.m1", CRC(f27e9d52) SHA1(dddae733d87ce7c88ad2580a8f64cb6ff9572e67) ) /* TC531001 */

	ROM_REGION( 0x500000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "056-v1.v1", 0x000000, 0x200000, CRC(4628fde0) SHA1(ece2a50f5270d844d58401b1447d1d856d78ea45) ) /* TC5316200 */
	ROM_LOAD( "056-v2.v2", 0x200000, 0x200000, CRC(b710e2f2) SHA1(df4da585203eea7554d3ce718eb107e9cb6a0254) ) /* TC5316200 */
	ROM_LOAD( "056-v3.v3", 0x400000, 0x100000, CRC(d168c301) SHA1(969273d1d11943e81560959359a2c4e69522af0e) ) /* TC538200 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	/* Different layout with 4xC (32mbit) also exists; chip labels are 056-C13, 056-C24, 056-C57 and 056-C68 */
	ROM_LOAD16_BYTE( "056-c1.c1", 0x000000, 0x200000, CRC(17b9cbd2) SHA1(1eee81e02763d384bd1c10a6012473ca931e4093) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "056-c2.c2", 0x000001, 0x200000, CRC(5fd76b67) SHA1(11925a41a53b53c6df4a5ebd28f98300950f743b) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "056-c3.c3", 0x400000, 0x200000, CRC(d2c88768) SHA1(22e2d84aa0c095944190e249ce87ef50d3f7b8ce) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "056-c4.c4", 0x400001, 0x200000, CRC(db39b883) SHA1(59de86c513dc4e230ae25d9e3b7e84621b657b54) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "056-c5.c5", 0x800000, 0x200000, CRC(c3074137) SHA1(9a75e3d63cb98d54f900dcfb3a03e21f3148d32f) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "056-c6.c6", 0x800001, 0x200000, CRC(31de68d3) SHA1(13ba7046cdd6863125f8284e60f102d4720af5a4) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "056-c7.c7", 0xc00000, 0x200000, CRC(3f36df57) SHA1(79ee97e9ae811a51141b535633f90e1491209d54) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "056-c8.c8", 0xc00001, 0x200000, CRC(e546d7a8) SHA1(74a2fca994a5a93a5784a46c0f68193122456a09) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

/****************************************
 ID-0057
 . ALM-006
 NEO-MVS PROG 4096 B / NEO-MVS CHA 42G-3
 . ALH-006
 NEO-AEG PROG4096 B / NEO-AEG CHA42G-3
****************************************/

ROM_START( wh2 ) /* MVS AND AES VERSION */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "057-p1.p1", 0x100000, 0x100000, CRC(65a891d9) SHA1(ff8d5ccb0dd22c523902bb3db3c645583a335056) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "057-s1.s1", CRC(fcaeb3a4) SHA1(1f3f85e38b8552333261c04ae5af0d6e3b310622) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "057-m1.m1", CRC(8fa3bc77) SHA1(982f92978671e4ee66630948e6bb7565b37b5dc0) ) /* TC531001 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "057-v1.v1", 0x000000, 0x200000, CRC(8877e301) SHA1(1bab988d74ea8fd12db201c257ec844622cf5f4e) ) /* TC5316200 */
	ROM_LOAD( "057-v2.v2", 0x200000, 0x200000, CRC(c1317ff4) SHA1(4c28b2b5998abaeaa5143f2f3a9ba52c6041f4f3) ) /* TC5316200 */

	ROM_REGION( 0xc00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "057-c1.c1", 0x000000, 0x200000, CRC(21c6bb91) SHA1(a2c17d0c91dd59528d8fa7fe110af8b20b25ff99) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "057-c2.c2", 0x000001, 0x200000, CRC(a3999925) SHA1(0ee861a77850d378d03c1bf00b9692abd860c759) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "057-c3.c3", 0x400000, 0x200000, CRC(b725a219) SHA1(4857687d156a9150a69b97d2729245a51c144a0c) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "057-c4.c4", 0x400001, 0x200000, CRC(8d96425e) SHA1(0f79c868a6a33ad25e38d842f30ec4440d809033) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "057-c5.c5", 0x800000, 0x200000, CRC(b20354af) SHA1(da7609fd467f2f4d71d92970f438a04d11ab1cc1) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "057-c6.c6", 0x800001, 0x200000, CRC(b13d1de3) SHA1(7d749c23a33d90fe50279e884540d71cf1aaaa6b) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

ROM_START( wh2h ) /* AES VERSION */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "057-pg1.p1", 0x100000, 0x100000, CRC(cde9aff5) SHA1(003f79cbff1383b59588ccee4b372027e506235c) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "057-s1.s1", CRC(fcaeb3a4) SHA1(1f3f85e38b8552333261c04ae5af0d6e3b310622) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "057-m1.m1", CRC(8fa3bc77) SHA1(982f92978671e4ee66630948e6bb7565b37b5dc0) ) /* TC531001 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "057-v1.v1", 0x000000, 0x200000, CRC(8877e301) SHA1(1bab988d74ea8fd12db201c257ec844622cf5f4e) ) /* TC5316200 */
	ROM_LOAD( "057-v2.v2", 0x200000, 0x200000, CRC(c1317ff4) SHA1(4c28b2b5998abaeaa5143f2f3a9ba52c6041f4f3) ) /* TC5316200 */

	ROM_REGION( 0xc00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "057-c1.c1", 0x000000, 0x200000, CRC(21c6bb91) SHA1(a2c17d0c91dd59528d8fa7fe110af8b20b25ff99) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "057-c2.c2", 0x000001, 0x200000, CRC(a3999925) SHA1(0ee861a77850d378d03c1bf00b9692abd860c759) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "057-c3.c3", 0x400000, 0x200000, CRC(b725a219) SHA1(4857687d156a9150a69b97d2729245a51c144a0c) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "057-c4.c4", 0x400001, 0x200000, CRC(8d96425e) SHA1(0f79c868a6a33ad25e38d842f30ec4440d809033) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "057-c5.c5", 0x800000, 0x200000, CRC(b20354af) SHA1(da7609fd467f2f4d71d92970f438a04d11ab1cc1) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "057-c6.c6", 0x800001, 0x200000, CRC(b13d1de3) SHA1(7d749c23a33d90fe50279e884540d71cf1aaaa6b) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

/****************************************
 ID-0058
 . NGM-058
 NEO-MVS PROGGSC / NEO-MVS CHA42G-3B
 NEO-MVS PROGGSC / NEO-MVS CHA 42G-3
 . NGH-058
 NEO-AEG PROGGS / NEO-AEG CHA42G-4
****************************************/

ROM_START( fatfursp ) /* MVS AND AES VERSION */
	ROM_REGION( 0x180000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "058-p1.p1",  0x000000, 0x100000, CRC(2f585ba2) SHA1(429b4bf43fb9b1082c15d645ca328f9d175b976b) ) /* mask rom TC538200 */
	ROM_LOAD16_WORD_SWAP( "058-p2.sp2", 0x100000, 0x080000, CRC(d7c71a6b) SHA1(b3428063031a2e5857da40a5d2ffa87fb550c1bb) ) /* mask rom TC534200 */
	/* also found MVS set with EP1 / EP2 / SP2 on eprom; correct chip label unknown */

	NEO_SFIX_128K( "058-s1.s1", CRC(2df03197) SHA1(24083cfc97e720ac9e131c9fe37df57e27c49294) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "058-m1.m1", CRC(ccc5186e) SHA1(cf9091c523c182aebfb928c91640b2d72fd70123) ) /* mask rom TC531001 */

	ROM_REGION( 0x500000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "058-v1.v1", 0x000000, 0x200000, CRC(55d7ce84) SHA1(05ac6a395d9bf9166925acca176a8d6129f533c8) ) /* mask rom TC5316200 */
	ROM_LOAD( "058-v2.v2", 0x200000, 0x200000, CRC(ee080b10) SHA1(29814fc21bbe30d37745c8918fab00c83a309be4) ) /* mask rom TC5316200 */
	ROM_LOAD( "058-v3.v3", 0x400000, 0x100000, CRC(f9eb3d4a) SHA1(d1747f9460b965f6daf4f881ed4ecd04c5253434) ) /* mask rom TC538200 */

	ROM_REGION( 0xc00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "058-c1.c1", 0x000000, 0x200000, CRC(044ab13c) SHA1(569d283638a132bc163faac2a9055497017ee0d2) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "058-c2.c2", 0x000001, 0x200000, CRC(11e6bf96) SHA1(c093a4f93f13e07b276e28b30c2a14dda9135d8f) ) /* Plane 2,3 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "058-c3.c3", 0x400000, 0x200000, CRC(6f7938d5) SHA1(be057b0a3faeb76d5fff161d3e6fea8a26e11d2c) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "058-c4.c4", 0x400001, 0x200000, CRC(4ad066ff) SHA1(4e304646d954d5f7bbabc5d068e85de31d38830f) ) /* Plane 2,3 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "058-c5.c5", 0x800000, 0x200000, CRC(49c5e0bf) SHA1(f3784178f90751990ea47a082a6aa869ee3566c9) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "058-c6.c6", 0x800001, 0x200000, CRC(8ff1f43d) SHA1(6180ceb5412a3e2e34e9513a3283b9f63087f747) ) /* Plane 2,3 */ /* mask rom TC5316200 */
ROM_END

ROM_START( fatfurspa ) /* MVS AND AES VERSION */
	ROM_REGION( 0x180000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "058-p1.p1",  0x000000, 0x100000, CRC(2f585ba2) SHA1(429b4bf43fb9b1082c15d645ca328f9d175b976b) ) /* mask rom TC538200 */
	ROM_LOAD16_WORD_SWAP( "058-p2.sp2", 0x100000, 0x080000, CRC(d7c71a6b) SHA1(b3428063031a2e5857da40a5d2ffa87fb550c1bb) ) /* mask rom TC534200 */
	/* the rom below acts as a patch to the program rom in the cart, replacing the first 512kb */
	ROM_LOAD16_WORD_SWAP( "058-epr.ep1", 0x000000, 0x080000, CRC(9f0c1e1a) SHA1(02861b0f230541becccc3df6a2c85dbe8733e7ce) ) /* M27C4002 */
	/* P is on eprom, correct chip label unknown */

	NEO_SFIX_128K( "058-s1.s1", CRC(2df03197) SHA1(24083cfc97e720ac9e131c9fe37df57e27c49294) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "058-m1.m1", CRC(ccc5186e) SHA1(cf9091c523c182aebfb928c91640b2d72fd70123) ) /* mask rom TC531001 */

	ROM_REGION( 0x500000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "058-v1.v1", 0x000000, 0x200000, CRC(55d7ce84) SHA1(05ac6a395d9bf9166925acca176a8d6129f533c8) ) /* mask rom TC5316200 */
	ROM_LOAD( "058-v2.v2", 0x200000, 0x200000, CRC(ee080b10) SHA1(29814fc21bbe30d37745c8918fab00c83a309be4) ) /* mask rom TC5316200 */
	ROM_LOAD( "058-v3.v3", 0x400000, 0x100000, CRC(f9eb3d4a) SHA1(d1747f9460b965f6daf4f881ed4ecd04c5253434) ) /* mask rom TC538200 */

	ROM_REGION( 0xc00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "058-c1.c1", 0x000000, 0x200000, CRC(044ab13c) SHA1(569d283638a132bc163faac2a9055497017ee0d2) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "058-c2.c2", 0x000001, 0x200000, CRC(11e6bf96) SHA1(c093a4f93f13e07b276e28b30c2a14dda9135d8f) ) /* Plane 2,3 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "058-c3.c3", 0x400000, 0x200000, CRC(6f7938d5) SHA1(be057b0a3faeb76d5fff161d3e6fea8a26e11d2c) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "058-c4.c4", 0x400001, 0x200000, CRC(4ad066ff) SHA1(4e304646d954d5f7bbabc5d068e85de31d38830f) ) /* Plane 2,3 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "058-c5.c5", 0x800000, 0x200000, CRC(49c5e0bf) SHA1(f3784178f90751990ea47a082a6aa869ee3566c9) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "058-c6.c6", 0x800001, 0x200000, CRC(8ff1f43d) SHA1(6180ceb5412a3e2e34e9513a3283b9f63087f747) ) /* Plane 2,3 */ /* mask rom TC5316200 */
ROM_END

/****************************************
 ID-0059
 . NGM-059
 NEO-MVS PROGTOP / NEO-MVS CHA256
 NEO-MVS PROG 4096 B / NEO-MVS CHA 42G-3
 . NGH-059
****************************************/

ROM_START( savagere )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "059-p1.p1", 0x100000, 0x100000, CRC(01d4e9c0) SHA1(3179d2be59bf2de6918d506117cff50acf7e09f3) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "059-s1.s1", CRC(e08978ca) SHA1(55152cb9bd0403ae8656b93a6b1522dba5db6d1a) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "059-m1.m1", CRC(29992eba) SHA1(187be624abe8670503edb235ff21ae8fdc3866e0) ) /* TC531001 */

	ROM_REGION( 0x600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "059-v1.v1", 0x000000, 0x200000, CRC(530c50fd) SHA1(29401cee7f7d2c199c7cb58092e86b28205e81ad) ) /* TC5316200 */
	ROM_LOAD( "059-v2.v2", 0x200000, 0x200000, CRC(eb6f1cdb) SHA1(7a311388315ea543babf872f62219fdc4d39d013) ) /* TC5316200 */
	ROM_LOAD( "059-v3.v3", 0x400000, 0x200000, CRC(7038c2f9) SHA1(c1d6f86b24feba03fe009b58199d2eeabe572f4e) ) /* TC5316200 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "059-c1.c1", 0x000000, 0x200000, CRC(763ba611) SHA1(d3262e0332c894ee149c5963f882cc5e5562ee57) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "059-c2.c2", 0x000001, 0x200000, CRC(e05e8ca6) SHA1(986a9b16ff92bc101ab567d2d01348e093abea9a) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "059-c3.c3", 0x400000, 0x200000, CRC(3e4eba4b) SHA1(770adec719e63a30ebe9522cc7576caaca44f3b2) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "059-c4.c4", 0x400001, 0x200000, CRC(3c2a3808) SHA1(698adcec0715c9e78b6286be38debf0ce28fd644) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "059-c5.c5", 0x800000, 0x200000, CRC(59013f9e) SHA1(5bf48fcc450da72a8c4685f6e3887e67eae49988) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "059-c6.c6", 0x800001, 0x200000, CRC(1c8d5def) SHA1(475d89a5c4922a9f6bd756d23c2624d57b6e9d62) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "059-c7.c7", 0xc00000, 0x200000, CRC(c88f7035) SHA1(c29a428b741f4fe7b71a3bc23c87925b6bc1ca8f) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "059-c8.c8", 0xc00001, 0x200000, CRC(484ce3ba) SHA1(4f21ed20ce6e2b67e2b079404599310c94f591ff) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

/****************************************
 ID-0060
 . ??M-060
 NEO-MVS PROGGSC / NEO-MVS CHA256B
****************************************/

ROM_START( fightfev ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "060-p1.p1", 0x0000000, 0x100000, CRC(2a104b50) SHA1(3eb663d3df7074e1cdf4c0e450a35c9cf55d8979) ) /* VIC940800 */
	/* also found set with EP1 / EP2 on eprom with sticker; stickers have Viccom logo with '1' for EP1 and '2' for EP2 in the center */

	NEO_SFIX_128K( "060-s1.s1", CRC(d62a72e9) SHA1(a23e4c4fd4ec11a7467ce41227c418b4dd1ef649) ) /* VIC930100 */

	NEO_BIOS_AUDIO_128K( "060-m1.m1", CRC(0b7c4e65) SHA1(999a1e784de18db3f1332b30bc425836ea6970be) ) /* VIC930100 */

	ROM_REGION( 0x300000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "060-v1.v1", 0x000000, 0x200000, CRC(f417c215) SHA1(0f53b8dd056f43b5d880628e8b74c2b27881ffac) ) /* VIC931600 */
	ROM_LOAD( "060-v2.v2", 0x200000, 0x100000, CRC(efcff7cf) SHA1(e8372303724284a750b706dc6bf7641e4c52bb95) ) /* VIC930800 */

	ROM_REGION( 0x0800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "060-c1.c1", 0x0000000, 0x200000, CRC(8908fff9) SHA1(f8c16ab0248b60f3a62e0d4d65c456e2f8e4da49) ) /* Plane 0,1 */ /* VIC931600 */
	ROM_LOAD16_BYTE( "060-c2.c2", 0x0000001, 0x200000, CRC(c6649492) SHA1(5d39b077387ed6897ac075ede4a2aa94bb64545e) ) /* Plane 2,3 */ /* VIC931600 */
	ROM_LOAD16_BYTE( "060-c3.c3", 0x0400000, 0x200000, CRC(0956b437) SHA1(c70be8b5cebf321afe4c3f5e9a12413c3077694a) ) /* Plane 0,1 */ /* VIC931600 */
	ROM_LOAD16_BYTE( "060-c4.c4", 0x0400001, 0x200000, CRC(026f3b62) SHA1(d608483b70d60e7aa0e41f25a8b3fed508129eb7) ) /* Plane 2,3 */ /* VIC931600 */
ROM_END

ROM_START( fightfeva ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "060-p1.p1", 0x0000000, 0x100000, CRC(2a104b50) SHA1(3eb663d3df7074e1cdf4c0e450a35c9cf55d8979) ) /* VIC940800 */
	/* the rom below acts as a patch to the program rom in the cart, replacing the first 512kb */
	ROM_LOAD16_WORD_SWAP( "1.sp2", 0x000000, 0x080000, CRC(3032041b) SHA1(4b8ed2e6f74579ea35a53e06ccac42d6905b0f51) )
	/* P is on eprom, has a Viccom logo at the top of the label with a circled '1' in the center */

	NEO_SFIX_128K( "060-s1.s1", CRC(d62a72e9) SHA1(a23e4c4fd4ec11a7467ce41227c418b4dd1ef649) ) /* VIC930100 */

	NEO_BIOS_AUDIO_128K( "060-m1.m1", CRC(0b7c4e65) SHA1(999a1e784de18db3f1332b30bc425836ea6970be) ) /* VIC930100 */

	ROM_REGION( 0x300000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "060-v1.v1", 0x000000, 0x200000, CRC(f417c215) SHA1(0f53b8dd056f43b5d880628e8b74c2b27881ffac) ) /* VIC931600 */
	ROM_LOAD( "060-v2.v2", 0x200000, 0x100000, CRC(efcff7cf) SHA1(e8372303724284a750b706dc6bf7641e4c52bb95) ) /* VIC930800 */

	ROM_REGION( 0x0800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "060-c1.c1", 0x0000000, 0x200000, CRC(8908fff9) SHA1(f8c16ab0248b60f3a62e0d4d65c456e2f8e4da49) ) /* Plane 0,1 */ /* VIC931600 */
	ROM_LOAD16_BYTE( "060-c2.c2", 0x0000001, 0x200000, CRC(c6649492) SHA1(5d39b077387ed6897ac075ede4a2aa94bb64545e) ) /* Plane 2,3 */ /* VIC931600 */
	ROM_LOAD16_BYTE( "060-c3.c3", 0x0400000, 0x200000, CRC(0956b437) SHA1(c70be8b5cebf321afe4c3f5e9a12413c3077694a) ) /* Plane 0,1 */ /* VIC931600 */
	ROM_LOAD16_BYTE( "060-c4.c4", 0x0400001, 0x200000, CRC(026f3b62) SHA1(d608483b70d60e7aa0e41f25a8b3fed508129eb7) ) /* Plane 2,3 */ /* VIC931600 */
ROM_END

/****************************************
 ID-0061
 . NGM-061
 NEO-MVS PROGGSC / NEO-MVS CHA256
 NEO-MVS PROGGSC / NEO-MVS CHA256B
 NEO-MVS PROGTOP / NEO-MVS CHA256
 NEO-MVS PROG 4096 B / NEO-MVS CHA256
 . NGH-061
 NEO-AEG PROGRKB / NEO-AEG CHA256[B]
****************************************/

ROM_START( ssideki2 ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "061-p1.p1", 0x000000, 0x100000, CRC(5969e0dc) SHA1(78abea880c125ec5a85bef6404478512a34b5513) ) /* mask rom TC538200 */
	/* also found MVS sets with EP1 / EP2 on eprom; correct chip label unknown. */

	NEO_SFIX_128K( "061-s1.s1", CRC(226d1b68) SHA1(de010f6fda3ddadb181fe37daa6105f22e78b970) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "061-m1.m1", CRC(156f6951) SHA1(49686f615f109a02b4f23931f1c84fee13872ffd) ) /* mask rom TC531001 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "061-v1.v1", 0x000000, 0x200000, CRC(f081c8d3) SHA1(fc9da0ddc1ddd1f9ae1443a726815c25e9dc38ae) ) /* mask rom TC5316200 */
	ROM_LOAD( "061-v2.v2", 0x200000, 0x200000, CRC(7cd63302) SHA1(c39984c0ae0a8e76f1fc036344bbb83635c18937) ) /* mask rom TC5316200 */

	ROM_REGION( 0x800000, "cslot1:sprites", 0 )
	/* Different layout with 8xC (8 mbit) also exists; naming sheme 061-Cx */
	ROM_LOAD16_BYTE( "061-c1-16.c1", 0x000000, 0x200000, CRC(a626474f) SHA1(d695f0dcb9480088b3a7c1488bd541b4c159528a) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "061-c2-16.c2", 0x000001, 0x200000, CRC(c3be42ae) SHA1(7fa65538bd0a0a162e4d3e9f49913da59d915e02) ) /* Plane 2,3 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "061-c3-16.c3", 0x400000, 0x200000, CRC(2a7b98b9) SHA1(75e1019dca8a8583afcc53651ac856cba3a96315) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "061-c4-16.c4", 0x400001, 0x200000, CRC(c0be9a1f) SHA1(228f41eaefdf3e147761f8ef849e3b5f321877d4) ) /* Plane 2,3 */ /* mask rom TC5316200 */
ROM_END

/****************************************
 ID-0062
 . DEM-001
 NEO-MVS PROGGSC / NEO-MVS CHA256
 . DEH-001
****************************************/

ROM_START( spinmast )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "062-p1.p1",  0x000000, 0x100000, CRC(37aba1aa) SHA1(1a2ab9593371cc2f665121d554eec3f6bb4d09ff) ) /* mask rom TC538200 */
	ROM_LOAD16_WORD_SWAP( "062-p2.sp2", 0x100000, 0x100000, CRC(f025ab77) SHA1(4944be04648296d0b93cfe4c5ca7b9cede072cff) ) /* mask rom TC538200 */

	NEO_SFIX_128K( "062-s1.s1", CRC(289e2bbe) SHA1(f52c7f2bffc89df3130b3cabd200408509a28cdc) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "062-m1.m1", CRC(76108b2f) SHA1(08c89a8b746dbb10ff885b41cde344173c2e3699) ) /* mask rom TC531001 */

	ROM_REGION( 0x100000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "062-v1.v1", 0x000000, 0x100000, CRC(cc281aef) SHA1(68be154b3e25f837afb4a477600dbe0ee69bec44) ) /* mask rom TC538200 */

	ROM_REGION( 0x800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "062-c1.c1", 0x000000, 0x100000, CRC(a9375aa2) SHA1(69218d8f1361e9ea709da11e3f15fe46b1db7181) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "062-c2.c2", 0x000001, 0x100000, CRC(0e73b758) SHA1(a247f736fbca0b609818dca4844ebb8442753bc1) ) /* Plane 2,3 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "062-c3.c3", 0x200000, 0x100000, CRC(df51e465) SHA1(171953c7a870f3ab96e0f875117ee7343931fd38) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "062-c4.c4", 0x200001, 0x100000, CRC(38517e90) SHA1(f7c64b94ac20f5146f9bb48b53cb2b30fe5b8f8c) ) /* Plane 2,3 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "062-c5.c5", 0x400000, 0x100000, CRC(7babd692) SHA1(0d4cd5006baa8d951cd2b6194ace566fa2845b8a) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "062-c6.c6", 0x400001, 0x100000, CRC(cde5ade5) SHA1(5899ef5dfcdbb8cf8c6aba748dbb52f3c5fed5fe) ) /* Plane 2,3 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "062-c7.c7", 0x600000, 0x100000, CRC(bb2fd7c0) SHA1(cce11c4cf39ac60143235ff89261806df339dae5) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "062-c8.c8", 0x600001, 0x100000, CRC(8d7be933) SHA1(e7097cfa26a959f90721e2e8368ceb47ea9db661) ) /* Plane 2,3 */ /* mask rom TC538200 */
ROM_END

/****************************************
 ID-0063
 . NGM-063
 NEO-MVS PROGTOP / NEO-MVS CHA256
 . NGH-063
 NEO-AEG PROGTOP2 / NEO-AEG CHA256 B
****************************************/

ROM_START( samsho2 ) /* MVS AND AES VERSION */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "063-p1.p1", 0x100000, 0x100000, CRC(22368892) SHA1(0997f8284aa0f57a333be8a0fdea777d0d01afd6) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )
	/* also found MVS sets with EP1 / EP2 on M27C4002 eprom and P1 / SP2 on D27C4000 maskrom; correct chip label unknown */

	NEO_SFIX_128K( "063-s1.s1", CRC(64a5cd66) SHA1(12cdfb27bf9ccd5a8df6ddd4628ef7cf2c6d4964) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "063-m1.m1", CRC(56675098) SHA1(90429fc40d056d480d0e2bbefbc691d9fa260fc4) ) /* TC531001 */

	ROM_REGION( 0x700000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "063-v1.v1", 0x000000, 0x200000, CRC(37703f91) SHA1(a373ebef4c33ba1d8340e826981a58769aada238) ) /* TC5316200 */
	ROM_LOAD( "063-v2.v2", 0x200000, 0x200000, CRC(0142bde8) SHA1(0be6c53acac44802bf70b6925452f70289a139d9) ) /* TC5316200 */
	ROM_LOAD( "063-v3.v3", 0x400000, 0x200000, CRC(d07fa5ca) SHA1(1da7f081f8b8fc86a91feacf900f573218d82676) ) /* TC5316200 */
	ROM_LOAD( "063-v4.v4", 0x600000, 0x100000, CRC(24aab4bb) SHA1(10ee4c5b3579865b93dcc1e4079963276aa700a6) ) /* TC538200 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "063-c1.c1", 0x000000, 0x200000, CRC(86cd307c) SHA1(0d04336f7c436d74638d8c1cd8651faf436a6bec) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c2.c2", 0x000001, 0x200000, CRC(cdfcc4ca) SHA1(179dc81432424d68cefedd20cc1c4b2a95deb891) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c3.c3", 0x400000, 0x200000, CRC(7a63ccc7) SHA1(49d97c543bc2860d493a353ab0d059088c6fbd21) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c4.c4", 0x400001, 0x200000, CRC(751025ce) SHA1(e1bbaa7cd67fd04e4aab7f7ea77f63ae1cbc90d0) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c5.c5", 0x800000, 0x200000, CRC(20d3a475) SHA1(28da44a136bd14c73c62c147c3f6e6bcfa1066de) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c6.c6", 0x800001, 0x200000, CRC(ae4c0a88) SHA1(cc8a7d11daa3821f83a6fd0942534706f939e576) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c7.c7", 0xc00000, 0x200000, CRC(2df3cbcf) SHA1(e54f9022359963711451c2025825b862d36c6975) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c8.c8", 0xc00001, 0x200000, CRC(1ffc6dfa) SHA1(acea18aca76c072e0bac2a364fc96d49cfc86e77) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

ROM_START( samsho2k ) /* KOREAN VERSION */
	// This has corrupt text if used with the Japan bios due to the replacement of the s1 rom to contain the new logo
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "063-p1-kan.p1", 0x100000, 0x100000, CRC(147cc6d7) SHA1(8e22305f41a0688786ff55437c25948e6c8fda58) )
	ROM_CONTINUE( 0x000000, 0x100000 )
	// the roms below apply as patch over the main program (I haven't checked what they change, the game boots as the Korean version even with just the above program)
	ROM_LOAD16_WORD_SWAP( "063-ep1-kan.ep1", 0x000000, 0x080000, CRC(fa32e2d8) SHA1(94f56759ec04ab3a1e557bc2dc51b92176b3c147) )
	ROM_LOAD16_WORD_SWAP( "063-ep2-kan.ep2", 0x080000, 0x080000, CRC(70b1a4d9) SHA1(387737e87a68d0ea4fd13693f1f30d3227a17c82) ) // this is exactly the same data anyway!

	NEO_SFIX_128K( "063-s1-kan.s1", CRC(ff08f80b) SHA1(240c6a1c52edebb49cc99ea08484c6a2d61ebf84) )

	NEO_BIOS_AUDIO_128K( "063-m1.m1", CRC(56675098) SHA1(90429fc40d056d480d0e2bbefbc691d9fa260fc4) ) /* TC531001 */

	ROM_REGION( 0x700000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "063-v1.v1", 0x000000, 0x200000, CRC(37703f91) SHA1(a373ebef4c33ba1d8340e826981a58769aada238) ) /* TC5316200 */
	ROM_LOAD( "063-v2.v2", 0x200000, 0x200000, CRC(0142bde8) SHA1(0be6c53acac44802bf70b6925452f70289a139d9) ) /* TC5316200 */
	ROM_LOAD( "063-v3.v3", 0x400000, 0x200000, CRC(d07fa5ca) SHA1(1da7f081f8b8fc86a91feacf900f573218d82676) ) /* TC5316200 */
	ROM_LOAD( "063-v4.v4", 0x600000, 0x100000, CRC(24aab4bb) SHA1(10ee4c5b3579865b93dcc1e4079963276aa700a6) ) /* TC538200 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "063-c1.c1", 0x000000, 0x200000, CRC(86cd307c) SHA1(0d04336f7c436d74638d8c1cd8651faf436a6bec) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c2.c2", 0x000001, 0x200000, CRC(cdfcc4ca) SHA1(179dc81432424d68cefedd20cc1c4b2a95deb891) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c3.c3", 0x400000, 0x200000, CRC(7a63ccc7) SHA1(49d97c543bc2860d493a353ab0d059088c6fbd21) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c4.c4", 0x400001, 0x200000, CRC(751025ce) SHA1(e1bbaa7cd67fd04e4aab7f7ea77f63ae1cbc90d0) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c5.c5", 0x800000, 0x200000, CRC(20d3a475) SHA1(28da44a136bd14c73c62c147c3f6e6bcfa1066de) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c6.c6", 0x800001, 0x200000, CRC(ae4c0a88) SHA1(cc8a7d11daa3821f83a6fd0942534706f939e576) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c7.c7", 0xc00000, 0x200000, CRC(2df3cbcf) SHA1(e54f9022359963711451c2025825b862d36c6975) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c8.c8", 0xc00001, 0x200000, CRC(1ffc6dfa) SHA1(acea18aca76c072e0bac2a364fc96d49cfc86e77) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

ROM_START( samsho2ka ) /* KOREAN VERSION */
	// This has corrupt text if used with the Japan bios due to the replacement of the s1 rom to contain the new logo
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "063-p1-kan.p1", 0x100000, 0x100000, CRC(147cc6d7) SHA1(8e22305f41a0688786ff55437c25948e6c8fda58) )
	ROM_CONTINUE( 0x000000, 0x100000 )
	// Basically samsho2k without loading ep1 and ep2 over p1-kan ; The game has been confirmed to exist in this state

	NEO_SFIX_128K( "063-s1-kan.s1", CRC(ff08f80b) SHA1(240c6a1c52edebb49cc99ea08484c6a2d61ebf84) )

	NEO_BIOS_AUDIO_128K( "063-m1.m1", CRC(56675098) SHA1(90429fc40d056d480d0e2bbefbc691d9fa260fc4) ) /* TC531001 */

	ROM_REGION( 0x700000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "063-v1.v1", 0x000000, 0x200000, CRC(37703f91) SHA1(a373ebef4c33ba1d8340e826981a58769aada238) ) /* TC5316200 */
	ROM_LOAD( "063-v2.v2", 0x200000, 0x200000, CRC(0142bde8) SHA1(0be6c53acac44802bf70b6925452f70289a139d9) ) /* TC5316200 */
	ROM_LOAD( "063-v3.v3", 0x400000, 0x200000, CRC(d07fa5ca) SHA1(1da7f081f8b8fc86a91feacf900f573218d82676) ) /* TC5316200 */
	ROM_LOAD( "063-v4.v4", 0x600000, 0x100000, CRC(24aab4bb) SHA1(10ee4c5b3579865b93dcc1e4079963276aa700a6) ) /* TC538200 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "063-c1.c1", 0x000000, 0x200000, CRC(86cd307c) SHA1(0d04336f7c436d74638d8c1cd8651faf436a6bec) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c2.c2", 0x000001, 0x200000, CRC(cdfcc4ca) SHA1(179dc81432424d68cefedd20cc1c4b2a95deb891) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c3.c3", 0x400000, 0x200000, CRC(7a63ccc7) SHA1(49d97c543bc2860d493a353ab0d059088c6fbd21) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c4.c4", 0x400001, 0x200000, CRC(751025ce) SHA1(e1bbaa7cd67fd04e4aab7f7ea77f63ae1cbc90d0) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c5.c5", 0x800000, 0x200000, CRC(20d3a475) SHA1(28da44a136bd14c73c62c147c3f6e6bcfa1066de) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c6.c6", 0x800001, 0x200000, CRC(ae4c0a88) SHA1(cc8a7d11daa3821f83a6fd0942534706f939e576) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c7.c7", 0xc00000, 0x200000, CRC(2df3cbcf) SHA1(e54f9022359963711451c2025825b862d36c6975) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "063-c8.c8", 0xc00001, 0x200000, CRC(1ffc6dfa) SHA1(acea18aca76c072e0bac2a364fc96d49cfc86e77) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

/****************************************
 ID-0064
 . ADM-007
 NEO-MVS PROGGSC / NEO-MVS CHA256
 . ADH-007
 NEO-AEG PROGRK / NEO-AEG CHA256
 NEO-AEG PROGRKB / NEO-AEG CHA256[B]
****************************************/

ROM_START( wh2j ) /* MVS AND AES VERSION */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "064-p1.p1", 0x100000, 0x100000, CRC(385a2e86) SHA1(cfde4a1aeae038a3d6ca9946065624f097682d3d) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "064-s1.s1", CRC(2a03998a) SHA1(5e33f469982f12d4622a06d323a345f192bf88e6) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "064-m1.m1", CRC(d2eec9d3) SHA1(09478787045f1448d19d064dd3d540d1741fd619) ) /* TC531001 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "064-v1.v1", 0x000000, 0x200000, CRC(aa277109) SHA1(35c22b15bb0a4d0ab118cb22a2d450d03995a17c) ) /* TC5316200 */
	ROM_LOAD( "064-v2.v2", 0x200000, 0x200000, CRC(b6527edd) SHA1(2bcf5bfa6e117cf4a3728a5e5f5771313c93f22a) ) /* TC5316200 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	/* Different layout with 4xC (32 mbit) also exists; chip labels are 064-C13, 064-C24, 064-C57 and 064-c68. */
	ROM_LOAD16_BYTE( "064-c1.c1", 0x000000, 0x200000, CRC(2ec87cea) SHA1(e713ec7839a7665edee6ee3f82a6e530b3b4bd7c) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "064-c2.c2", 0x000001, 0x200000, CRC(526b81ab) SHA1(b5f0a2f04489539ed6b9d0810b12787356c64b23) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "064-c3.c3", 0x400000, 0x200000, CRC(436d1b31) SHA1(059776d77b91377ed0bcfc278802d659c917fc0f) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "064-c4.c4", 0x400001, 0x200000, CRC(f9c8dd26) SHA1(25a9eea1d49b21b4a988beb32c25bf2f7796f227) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "064-c5.c5", 0x800000, 0x200000, CRC(8e34a9f4) SHA1(67b839b426ef3fad0a85d951fdd44c0a45c55226) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "064-c6.c6", 0x800001, 0x200000, CRC(a43e4766) SHA1(54f282f2b1ff2934cca7acbb4386a2b99a29df3a) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "064-c7.c7", 0xc00000, 0x200000, CRC(59d97215) SHA1(85a960dc7f364df13ee0c2f99a4c53aefb081486) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "064-c8.c8", 0xc00001, 0x200000, CRC(fc092367) SHA1(69ff4ae909dd857de3ca8645d63f8b4bde117448) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

/****************************************
 ID-0065
 . DEM-002
 NEO-MVS PROG42G-1 / NEO-MVS CHA42G-1
 . DEH-002
 NEO-AEG PROG42G-1 / NEO-AEG CHA42G-1
****************************************/

ROM_START( wjammers )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "065-p1.p1", 0x000000, 0x100000, CRC(6692c140) SHA1(5da574e906974fac92bb2f49bdeea257c014a897) ) /* mask rom TC538200 */

	NEO_SFIX_128K( "065-s1.s1", CRC(074b5723) SHA1(86d3b3bb5414f43e4d3b7a2af0db23cc71ce8412) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "065-m1.m1", CRC(52c23cfc) SHA1(809a7e072ad9acbffc25e9bd27cdb97638d09d07) ) /* mask rom TC531001 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "065-v1.v1", 0x000000, 0x100000, CRC(ce8b3698) SHA1(7d75e2a5cf8c90be422f8b425447e81597fe145a) ) /* mask rom TC538200 */
	ROM_LOAD( "065-v2.v2", 0x100000, 0x100000, CRC(659f9b96) SHA1(62f40365212153bc3b92a1187fa44f6cdc7f7b83) ) /* mask rom TC538200 */
	ROM_LOAD( "065-v3.v3", 0x200000, 0x100000, CRC(39f73061) SHA1(ec57cd58e7f8569cff925d11e2320d588ce4fe49) ) /* mask rom TC538200 */
	ROM_LOAD( "065-v4.v4", 0x300000, 0x100000, CRC(5dee7963) SHA1(f8e6de73d65dd80b29c711f00835a574a770cb4e) ) /* mask rom TC538200 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "065-c1.c1", 0x000000, 0x100000, CRC(c7650204) SHA1(42918d700d59864f8ab15caf968a062a563c9b09) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "065-c2.c2", 0x000001, 0x100000, CRC(d9f3e71d) SHA1(fad1f64061eac1bf85bf6d75d2eae974a8c94069) ) /* Plane 2,3 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "065-c3.c3", 0x200000, 0x100000, CRC(40986386) SHA1(65795a50197049681265946713d416c9cdb68f08) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "065-c4.c4", 0x200001, 0x100000, CRC(715e15ff) SHA1(ac8b8b01f5c7384b883afbe0cf977430378e3fef) ) /* Plane 2,3 */ /* mask rom TC538200 */
ROM_END

/****************************************
 ID-0066
 . DEM-003
 NEO-MVS PROGGSC / NEO-MVS CHA256
 . DEH-003
****************************************/

ROM_START( karnovr )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "066-p1.p1", 0x000000, 0x100000, CRC(8c86fd22) SHA1(8cf97c6fb9c5717167ccc54bf5856248ccaf32c6) ) /* TC538200 */

	NEO_SFIX_128K( "066-s1.s1", CRC(bae5d5e5) SHA1(aa69d9b235b781ec51f72a528fada9cb12e72cbc) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "066-m1.m1", CRC(030beae4) SHA1(ceb6ee6c09514504efacdbca7b280901e4c97084) ) /* TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "066-v1.v1", 0x000000, 0x200000, CRC(0b7ea37a) SHA1(34e7d4f6db053674a7e8c8b2e3e398777d5b02e6) ) /* TC5316200 */

	ROM_REGION( 0xc00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "066-c1.c1", 0x000000, 0x200000, CRC(09dfe061) SHA1(ca4c0f0ce80967b4be2f18b72435c468bbfbac4c) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "066-c2.c2", 0x000001, 0x200000, CRC(e0f6682a) SHA1(addb4fbc30da2b8ffc86819d92a874eb232f67dd) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "066-c3.c3", 0x400000, 0x200000, CRC(a673b4f7) SHA1(d138f5b38fd65c61549ce36f5c4983f7c8a3e7f6) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "066-c4.c4", 0x400001, 0x200000, CRC(cb3dc5f4) SHA1(865d9ccfc3df517c341d6aac16120f6b6aa759fe) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "066-c5.c5", 0x800000, 0x200000, CRC(9a28785d) SHA1(19723e1f7ff429e8a038d89488b279f830dfaf6e) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "066-c6.c6", 0x800001, 0x200000, CRC(c15c01ed) SHA1(7cf5583e6610bcdc3b332896cefc71df84fb3f19) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

/****************************************
 ID-0067
 . ??M-067
 NEO-MVS PROG16 / NEO-MVS CHA256B
 NEO-MVS PROG16 / NEO-MVS CHA256
****************************************/

ROM_START( gururin ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "067-p1.p1", 0x000000, 0x80000, CRC(4cea8a49) SHA1(cea4a35db8de898e30eb40dd339b3cbe77ac0856) ) /* UM8303B */

	NEO_SFIX_128K( "067-s1.s1", CRC(b119e1eb) SHA1(f63a68a71aea220d3d4475847652e2a1f68b2b6f) ) /* UMK300 */

	NEO_BIOS_AUDIO_128K( "067-m1.m1", CRC(9e3c6328) SHA1(17e8479c258f28a01d2283be9e692ff7685898cc) ) /* UML359 */

	ROM_REGION( 0x80000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "067-v1.v1", 0x000000, 0x80000, CRC(cf23afd0) SHA1(10f87014ee10613f92b04f482f449721a6379db7) ) /* UM8302 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "067-c1.c1", 0x000000, 0x200000, CRC(35866126) SHA1(e4b24670ccc7901af5f66b11b15fae4e67f843ab) ) /* Plane 0,1 */ /* UMT301B */
	ROM_LOAD16_BYTE( "067-c2.c2", 0x000001, 0x200000, CRC(9db64084) SHA1(68a43c12f63f5e98d68ad0902a6551c5d30f8543) ) /* Plane 2,3 */ /* UMT302B */
ROM_END

/****************************************
 ID-0068
 . NGM-068
 NEO-MVS PROGTOP / NEO-MVS CHA256
****************************************/

ROM_START( pspikes2 ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "068-pg1.p1", 0x000000, 0x100000, CRC(105a408f) SHA1(2ee51defa1c24c66c63a6498ee542ac26de3cfbb) ) /* mask rom TC538200 */

	NEO_SFIX_128K( "068-sg1.s1", CRC(18082299) SHA1(efe93fabe6a76a5dc8cf12f255e571480afb40a0) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "068-mg1.m1", CRC(b1c7911e) SHA1(27b298e7d50981331e17aa642e2e363ffac4333a) ) /* mask rom TC531001 */

	ROM_REGION( 0x300000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "068-v1.v1", 0x000000, 0x100000, CRC(2ced86df) SHA1(d6b73d1f31efbd74fb745200d4dade5f80b71541) ) /* mask rom TC538200 */
	ROM_LOAD( "068-v2.v2", 0x100000, 0x100000, CRC(970851ab) SHA1(6c9b04e9cc6b92133f1154e5bdd9d38d8ef050a7) ) /* mask rom TC538200 */
	ROM_LOAD( "068-v3.v3", 0x200000, 0x100000, CRC(81ff05aa) SHA1(d74302f38c59055bfc83b39dff798a585314fecd) ) /* mask rom TC538200 */

	ROM_REGION( 0x600000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "068-c1.c1", 0x000000, 0x100000, CRC(7f250f76) SHA1(5109a41adcb7859e24dc43d88842d4cc18cd3305) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "068-c2.c2", 0x000001, 0x100000, CRC(20912873) SHA1(2df8766b531e47ffc30457e41c63b83557b4f468) ) /* Plane 2,3 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "068-c3.c3", 0x200000, 0x100000, CRC(4b641ba1) SHA1(7a9c42a30163eda455f7bde2302402b1a5de7178) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "068-c4.c4", 0x200001, 0x100000, CRC(35072596) SHA1(4150a21041f06514c97592bd8af686504b06e187) ) /* Plane 2,3 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "068-c5.c5", 0x400000, 0x100000, CRC(151dd624) SHA1(f2690a3fe9c64f70f283df785a5217d5b92a289f) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "068-c6.c6", 0x400001, 0x100000, CRC(a6722604) SHA1(b40c57fb4be93ac0b918829f88393ced3d4f8bde) ) /* Plane 2,3 */ /* mask rom TC538200 */
ROM_END

/****************************************
 ID-0069
 . NGM-069
 NEO-MVS PROGBK1 / NEO-MVS CHA256
. NGH-069
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( fatfury3 ) /* MVS AND AES VERSION */
	ROM_REGION( 0x300000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "069-p1.p1",   0x000000, 0x100000, CRC(a8bcfbbc) SHA1(519c4861151797e5f4d4f33432b83dfabed8e7c4) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "069-sp2.sp2", 0x100000, 0x200000, CRC(dbe963ed) SHA1(8ece7f663cfe8e563576a397e41161d392cee67e) ) /* TC5316200 */

	NEO_SFIX_128K( "069-s1.s1", CRC(0b33a800) SHA1(b7d2cc97da4f30ddebc7b801f5e1d17d2306b2db) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "069-m1.m1", CRC(fce72926) SHA1(a40c74f793900b8542f0b8383ce4bf46fca112d4) ) /* TC531001 */

	ROM_REGION( 0xa00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "069-v1.v1", 0x000000, 0x400000, CRC(2bdbd4db) SHA1(5f4fecf69c2329d699cbd45829c19303b1e2a80e) ) /* TC5332204 */
	ROM_LOAD( "069-v2.v2", 0x400000, 0x400000, CRC(a698a487) SHA1(11b8bc53bc26a51f4a408e900e3769958625c4ed) ) /* TC5332204 */
	ROM_LOAD( "069-v3.v3", 0x800000, 0x200000, CRC(581c5304) SHA1(e9550ec547b4f605afed996b22d711f49b48fa92) ) /* TC5316200 */

	ROM_REGION( 0x1400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "069-c1.c1", 0x0000000, 0x400000, CRC(e302f93c) SHA1(d8610b14900b2b8fe691b67ca9b1abb335dbff74) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "069-c2.c2", 0x0000001, 0x400000, CRC(1053a455) SHA1(69501bfac68739e63d798045b812badd251d57b8) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "069-c3.c3", 0x0800000, 0x400000, CRC(1c0fde2f) SHA1(cf6c2ef56c03a861de3b0b6dc0d7c9204d947f9d) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "069-c4.c4", 0x0800001, 0x400000, CRC(a25fc3d0) SHA1(83cb349e2f1032652060b233e741fb893be5af16) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "069-c5.c5", 0x1000000, 0x200000, CRC(b3ec6fa6) SHA1(7e4c8ee9dd8d9a25ff183d9d8b05f38769348bc7) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "069-c6.c6", 0x1000001, 0x200000, CRC(69210441) SHA1(6d496c549dba65caabeaffe5b762e86f9d648a26) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

/****************************************
 ID-0070
 . ??M-070
 NEO-MVS PROGBK1 / NEO-MVS CHAFIO (1999.6.14) (NEO-CMC 7042)
****************************************/

ROM_START( zupapa ) /* Original Version - Encrypted GFX */ /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "070-p1.p1", 0x000000, 0x100000, CRC(5a96203e) SHA1(49cddec9ca6cc51e5ecf8a34e447a23e1f8a15a1) ) /* mask rom TC538200 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do _not_ have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_128K( "070-epr.m1", CRC(5a3b3191) SHA1(fa9a9930e18c64e598841fb344c4471d3d2c1964) ) /* M27C1001 */
	/* M1 on eprom, correct chip label unknown */

	ROM_REGION( 0x0200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "070-v1.v1", 0x000000, 0x200000, CRC(d3a7e1ff) SHA1(4a4a227e10f4af58168f6c26011ea1d414253f92) ) /* mask rom TC5316200 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "070-c1.c1", 0x0000000, 0x800000, CRC(f8ad02d8) SHA1(9be54532332a8e963ec35ff1e518947bb11ebade) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "070-c2.c2", 0x0000001, 0x800000, CRC(70156dde) SHA1(06286bf043d50199b47df9a76ca91f39cb28cb90) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

/****************************************
 ID-0071
 Bang Bang Busters (prototype) 1994 Visco
 Prepared for release in 2000/2001, but apparently Zupapa was favored
 The 2000 version was released in 2010 for MVS and AES (Releaser claims that it is officially licensed by VISCO)

 Reported UNIVERSE BIOS CRC32:
  ROM     EC861CAF
  BANK 0  NOT USED
  BANK 1  NOT USED
  BANK 2  NOT USED
  BANK 3  NOT USED
 ****************************************/
ROM_START( b2b )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "071.p1", 0x000000, 0x080000, CRC(7687197d) SHA1(4bb9cb7819807f7a7e1f85f1c4faac4a2f8761e8) )

	NEO_SFIX_128K( "071.s1", CRC(44e5f154) SHA1(b3f80051789e60e5d8c5df0408f1aba51616e92d) )

	NEO_BIOS_AUDIO_128K( "071.m1", CRC(6da739ad) SHA1(cbf5f55c54b4ee00943e2a411eeee4e465ce9c34) )

	ROM_REGION( 0x100000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "071.v1", 0x000000, 0x100000, CRC(50feffb0) SHA1(00127dae0130889995bfa7560bc4b0662f74fba5) )

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "071.c1", 0x000000, 0x200000, CRC(23d84a7a) SHA1(9034658ad40e2c45558abc3db312aa2764102fc4) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "071.c2", 0x000001, 0x200000, CRC(ce7b6248) SHA1(ad1cd5adae5c151e183ff88b68afe10f7009f48e) ) /* Plane 2,3 */
ROM_END


/****************************************
 ID-0072
 Last Odyssey Pinball Fantasia (prototype) 1995 Monolith
 A video of this was on youtube in 2010/2011.
 ****************************************/

/****************************************
 ID-0073
 . ??M-073
 NEO-MVS PROGTOP / NEO-MVS CHA256
****************************************/

ROM_START( panicbom ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "073-p1.p1", 0x000000, 0x080000, CRC(adc356ad) SHA1(801e0a54b65d7a3500e6cef2d6bba40c6356dc1f) ) /* mask rom TC534200 */

	NEO_SFIX_128K( "073-s1.s1", CRC(b876de7e) SHA1(910347d7657470da914fb0a6b0ea02891e13c081) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "073-m1.m1", CRC(3cdf5d88) SHA1(6d8365a946fbd0b7c7b896536322638d80f6a764) ) /* mask rom TC531001 */

	ROM_REGION( 0x300000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "073-v1.v1", 0x000000, 0x200000, CRC(7fc86d2f) SHA1(aa4234d22157060e0ba97a09c4e85c5276b74099) ) /* mask rom TC5316200 */
	ROM_LOAD( "073-v2.v2", 0x200000, 0x100000, CRC(082adfc7) SHA1(19c168e9a6cadcbed79033c320bcf3a45f846daf) ) /* mask rom TC538200 */

	ROM_REGION( 0x200000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "073-c1.c1", 0x000000, 0x100000, CRC(8582e1b5) SHA1(e17d8f57b8ebee14b8e705374b34abe928937258) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "073-c2.c2", 0x000001, 0x100000, CRC(e15a093b) SHA1(548a418c81af79cd7ab6ad165b8d6daee30abb49) ) /* Plane 2,3 */ /* mask rom TC538200 */
ROM_END

/****************************************
 ID-0074
 . ADM-008
 NEO-MVS PROGTOP / NEO-MVS CHA256B
 . ADH-008
 NEO-AEG PROGRK / NEO-AEG CHA256
****************************************/

ROM_START( aodk ) /* MVS AND AES VERSION */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "074-p1.p1", 0x100000, 0x100000, CRC(62369553) SHA1(ca4d561ee08d16fe6804249d1ba49188eb3bd606) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "074-s1.s1", CRC(96148d2b) SHA1(47725a8059346ebe5639bbdbf62a2ac8028756a9) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "074-m1.m1", CRC(5a52a9d1) SHA1(ef913a9a55d29d5dd3beab1ce6039d64ce9b1a5b) ) /* TC531001 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "074-v1.v1", 0x000000, 0x200000, CRC(7675b8fa) SHA1(29f4facf89d551237b31bf779693cbbbc94e1ede) ) /* TC5316200 */
	ROM_LOAD( "074-v2.v2", 0x200000, 0x200000, CRC(a9da86e9) SHA1(ff65af61e42b79a75060a352b24077d1fa28c83f) ) /* TC5316200 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "074-c1.c1", 0x000000, 0x200000, CRC(a0b39344) SHA1(adfff7b8836347abf030611563e6068a91164d0a) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "074-c2.c2", 0x000001, 0x200000, CRC(203f6074) SHA1(737f2d707d504df1da1ca5c5cf61cf489a33eb56) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "074-c3.c3", 0x400000, 0x200000, CRC(7fff4d41) SHA1(bebd18a75adeb34c3bbd49cfc8fd3d8c2bf9e475) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "074-c4.c4", 0x400001, 0x200000, CRC(48db3e0a) SHA1(a88505e001e01bb45fb26beda5af24943d02552a) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "074-c5.c5", 0x800000, 0x200000, CRC(c74c5e51) SHA1(0399c53e2a3d721901dddc073fda6ec22e02dfd4) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "074-c6.c6", 0x800001, 0x200000, CRC(73e8e7e0) SHA1(dd6580227743e6a3db4950456ebe870008e022b2) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "074-c7.c7", 0xc00000, 0x200000, CRC(ac7daa01) SHA1(78407a464f67d949933ce2ccaa23fbed80dff1ea) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "074-c8.c8", 0xc00001, 0x200000, CRC(14e7ad71) SHA1(d4583fbce361fd1a11ac6c1a27b0b669e8a5c718) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

/****************************************
 ID-0075
 . NGM-075
 NEO-MVS PROGGSC / NEO-MVS CHA256
 . NGH-075
 NEO-AEG PROGRK / NEO-AEG CHA256
****************************************/

ROM_START( sonicwi2 )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "075-p1.p1", 0x100000, 0x100000, CRC(92871738) SHA1(fed040a7c1ff9e495109813a702d09fb1d2ecf3a) ) /* mask rom TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "075-s1.s1", CRC(c9eec367) SHA1(574e1afe7e0d54610c145131106e59ba2894eeb7) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "075-m1.m1", CRC(bb828df1) SHA1(eab8e2868173bdaac7c7ed97305a9aa1033fd303) ) /* mask rom TC531001 */

	ROM_REGION( 0x300000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "075-v1.v1", 0x000000, 0x200000, CRC(7577e949) SHA1(3ba9f11094dd0cf519f33a16016cfae0d2c6629c) ) /* mask rom TC5316200 */
	ROM_LOAD( "075-v2.v2", 0x200000, 0x100000, CRC(021760cd) SHA1(8a24e38f1d4982c4dcd82718995571ac94cbb390) ) /* mask rom TC538200 */

	ROM_REGION( 0x800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "075-c1.c1", 0x000000, 0x200000, CRC(3278e73e) SHA1(d9e6c8a3a5213690a1b8747d27806d8ac5aac405) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "075-c2.c2", 0x000001, 0x200000, CRC(fe6355d6) SHA1(ca72fff7a908b6d9325761079ff2a0e28f34cf89) ) /* Plane 2,3 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "075-c3.c3", 0x400000, 0x200000, CRC(c1b438f1) SHA1(b3751c5b426bca0fcc3a58bdb86712c22ef908ab) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "075-c4.c4", 0x400001, 0x200000, CRC(1f777206) SHA1(e29c5ae65ebdcc1167a894306d2446ce909639da) ) /* Plane 2,3 */ /* mask rom TC5316200 */
ROM_END

/****************************************
 ID-0076
 . ??M-076
 NEO-MVS PROGGSC / NEO-MVS CHA256
****************************************/

ROM_START( zedblade ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "076-p1.p1", 0x000000, 0x080000, CRC(d7c1effd) SHA1(485c2308a40baecd122be9ab4996044622bdcc7e) ) /* mask rom TC538200 */

	NEO_SFIX_128K( "076-s1.s1", CRC(f4c25dd5) SHA1(8ec9026219f393930634f9170edbaaee479f875e) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "076-m1.m1", CRC(7b5f3d0a) SHA1(4a301781a57ff236f49492b576ff4858b0ffbdf8) ) /* mask rom TC531001 */

	ROM_REGION( 0x500000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "076-v1.v1", 0x000000, 0x200000, CRC(1a21d90c) SHA1(4793ab06421228ad122e359653ed0f1339b90c7a) ) /* mask rom TC5316200 */
	ROM_LOAD( "076-v2.v2", 0x200000, 0x200000, CRC(b61686c3) SHA1(5a3405e833ce36abb7421190438b5cccc8537919) ) /* mask rom TC5316200 */
	ROM_LOAD( "076-v3.v3", 0x400000, 0x100000, CRC(b90658fa) SHA1(b9a4b34565ce3688495c47e35c9b888ef686ae9f) ) /* mask rom TC538200 */

	ROM_REGION( 0x800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "076-c1.c1", 0x000000, 0x200000, CRC(4d9cb038) SHA1(c0b52b32e1fa719b99ae242d61d5dbea1437331c) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "076-c2.c2", 0x000001, 0x200000, CRC(09233884) SHA1(1895cd0d126a022bce1cc4c7a569032d89f35e3f) ) /* Plane 2,3 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "076-c3.c3", 0x400000, 0x200000, CRC(d06431e3) SHA1(643bd1ad74af272795b02143ba80a76e375036ab) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "076-c4.c4", 0x400001, 0x200000, CRC(4b1c089b) SHA1(cd63961d88c5be84673cce83c683a86b222a064d) ) /* Plane 2,3 */ /* mask rom TC5316200 */
ROM_END

/****************************************
 ID-0077
 The Warlocks of the Fates / Shinryu Senki (prototype) 1995 SNK/Astec21
 a video of intro and full gameplay was on youtube in 2014.
****************************************/

/****************************************
 ID-0078
 . NGM-078
 NEO-MVS PROGTOP / NEO-MVS CHA256
 . NGH-078
****************************************/

ROM_START( galaxyfg )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "078-p1.p1", 0x100000, 0x100000, CRC(45906309) SHA1(cdcd96a564acf42e959193e139e149b29c103e25) ) /* mask rom TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "078-s1.s1", CRC(72f8923e) SHA1(da908bffc2b5d8baa2002dbb5bfb3aa17d2472b7) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "078-m1.m1", CRC(8e9e3b10) SHA1(7c44d1dbd4f8d337c99e90361d1dab837df85e31) ) /* mask rom TC531001 */

	ROM_REGION( 0x500000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "078-v1.v1", 0x000000, 0x200000, CRC(e3b735ac) SHA1(e16dfac09aef8115a20bae0bef8c86d4e7d0dc4a) ) /* mask rom TC5316200 */
	ROM_LOAD( "078-v2.v2", 0x200000, 0x200000, CRC(6a8e78c2) SHA1(f60b1f8a3a945f279a582745e82f37278ce5d83b) ) /* mask rom TC5316200 */
	ROM_LOAD( "078-v3.v3", 0x400000, 0x100000, CRC(70bca656) SHA1(218b7079c90898e7faa382b386e77f81f415e7ac) ) /* mask rom TC538200 */

	ROM_REGION( 0xe00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "078-c1.c1", 0x000000, 0x200000, CRC(c890c7c0) SHA1(b96c18a41c34070a4f24ca77cb7516fae8b0fd0c) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "078-c2.c2", 0x000001, 0x200000, CRC(b6d25419) SHA1(e089df9c9a9645f706e501108d634f4d222622a2) ) /* Plane 2,3 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "078-c3.c3", 0x400000, 0x200000, CRC(9d87e761) SHA1(ea1b6d7c9d5ef3a9b48968bde5a52d5699d591cc) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "078-c4.c4", 0x400001, 0x200000, CRC(765d7cb8) SHA1(7b9c86714d688602064d928c9d2b49d70bb7541e) ) /* Plane 2,3 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "078-c5.c5", 0x800000, 0x200000, CRC(e6b77e6a) SHA1(db3b8fc62a6f21c6653621c0665450d5d9a9913d) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "078-c6.c6", 0x800001, 0x200000, CRC(d779a181) SHA1(2761026abd9698a7b56114b76631563abd41fd12) ) /* Plane 2,3 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "078-c7.c7", 0xc00000, 0x100000, CRC(4f27d580) SHA1(c0f12496b45b2fe6e94aa8ac52b0157063127e0a) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "078-c8.c8", 0xc00001, 0x100000, CRC(0a7cc0d8) SHA1(68aaee6341c87e56ce11acc1c4ec8047839fe70d) ) /* Plane 2,3 */ /* mask rom TC538200 */
ROM_END

/****************************************
 ID-0079
 . DEM-004
 NEO-MVS PROGGSC / NEO-MVS CHA256
 . DEH-004
 NEO-AEG PROGRK  / NEO-AEG CHA256
****************************************/

ROM_START( strhoop ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "079-p1.p1", 0x000000, 0x100000, CRC(5e78328e) SHA1(7a00b096ed6dd77afc3008c5a4c83686e475f323) ) /* TC538200 */

	NEO_SFIX_128K( "079-s1.s1", CRC(3ac06665) SHA1(ba9ab51eb95c3568304377ef6d7b5f32e8fbcde1) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "079-m1.m1", CRC(bee3455a) SHA1(fd5345d9847982085a9b364fff542580889bf02f) ) /* TC531001 */

	ROM_REGION( 0x300000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "079-v1.v1", 0x000000, 0x200000, CRC(718a2400) SHA1(cefc5d0b302bd4a87ab1fa244ade4482c23c6806) ) /* TC5316200 */
	ROM_LOAD( "079-v2.v2", 0x200000, 0x100000, CRC(720774eb) SHA1(e4926f01322d0a15e700fb150b368152f2091146) ) /* TC538200 */
	/* AES 079-v2 is only 4 mbit (TC534200), data is the same */

	ROM_REGION( 0x800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "079-c1.c1", 0x000000, 0x200000, CRC(0581c72a) SHA1(453f7a8474195a1120da5fa24337d79674563d9e) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "079-c2.c2", 0x000001, 0x200000, CRC(5b9b8fb6) SHA1(362aa0de0d2cf9aa03758363ffb1e15e046a3930) ) /* Plane 2,3 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "079-c3.c3", 0x400000, 0x200000, CRC(cd65bb62) SHA1(6f47d77d61d4289bcee82df7c4efa5346a6e4c80) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "079-c4.c4", 0x400001, 0x200000, CRC(a4c90213) SHA1(1b9f7b5f31acd6df2bdab81b849f32c13aa1b884) ) /* Plane 2,3 */ /* mask rom TC5316200 */
ROM_END

/****************************************
 ID-0080
 . SAM-080
 NEO-MVS PROGGSC / NEO-MVS CHA256
 NEO-MVS PROGTOP / NEO-MVS CHA256
 Boards used for the Korean release
 . SAH-080
 NEO-AEG PROGTOP2 / NEO-AEG CHA256 B
****************************************/

ROM_START( quizkof ) /* MVS AND AES VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "080-p1.p1", 0x000000, 0x100000, CRC(4440315e) SHA1(f4adba8e341d64a1f6280dfd98ebf6918c00608d) ) /* mask rom TC538200 */

	NEO_SFIX_128K( "080-s1.s1", CRC(d7b86102) SHA1(09e1ca6451f3035ce476e3b045541646f860aad5) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "080-m1.m1", CRC(f5f44172) SHA1(eaaba1781622901b91bce9257be4e05f84df053b) ) /* mask rom TC531001 */

	ROM_REGION( 0x600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "080-v1.v1", 0x000000, 0x200000, CRC(0be18f60) SHA1(05c8b7d9f5a8583015f31902ad16d9c621f47d4e) ) /* mask rom TC5316200 */
	ROM_LOAD( "080-v2.v2", 0x200000, 0x200000, CRC(4abde3ff) SHA1(0188bfcafa9a1aac302705736a2bcb26b9d684c2) ) /* mask rom TC5316200 */
	ROM_LOAD( "080-v3.v3", 0x400000, 0x200000, CRC(f02844e2) SHA1(8c65ebe146f4ddb6c904f8125cb32767f74c24d5) ) /* mask rom TC5316200 */

	ROM_REGION( 0x800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "080-c1.c1", 0x000000, 0x200000, CRC(ea1d764a) SHA1(78cc1735624c37f90607baa92e110a3c5cc54c6f) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "080-c2.c2", 0x000001, 0x200000, CRC(d331d4a4) SHA1(94228d13fb1e30973eb54058e697f17456ee16ea) ) /* Plane 2,3 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "080-c3.c3", 0x400000, 0x200000, CRC(b4851bfe) SHA1(b8286c601de5755c1681ea46e177fc89006fc066) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "080-c4.c4", 0x400001, 0x200000, CRC(ca6f5460) SHA1(ed36e244c9335f4c0a97c57b7b7f1b849dd3a90d) ) /* Plane 2,3 */ /* mask rom TC5316200 */
ROM_END

ROM_START( quizkofk ) /* KOREAN VERSION */
	/* Made by Viccom Corp.; proms have manufacturer stamp VICxxxxxx-xxx, chip labels same as quizkof; Cart ID 0080 */
	/* Due to parent set naming limitations, roms have been named vic-xxx */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "vic-080-p1.p1", 0x000000, 0x100000, CRC(2589488e) SHA1(609f3095c1cf8b11335b56f23c5d955eebd66dd2) )

	NEO_SFIX_128K( "vic-080-s1.s1", CRC(af72c30f) SHA1(f6a2c583f38295b7da2cbcf4b2c7ed3d3e01db4f) )

	NEO_BIOS_AUDIO_128K( "vic-080-m1.m1", CRC(4f157e9a) SHA1(8397bfdd5738914670ada7cd8c611c20ed1f74da) )

	ROM_REGION( 0x600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "080-v1.v1", 0x000000, 0x200000, CRC(0be18f60) SHA1(05c8b7d9f5a8583015f31902ad16d9c621f47d4e) ) /* mask rom TC5316200 */
	ROM_LOAD( "vic-080-v2.v2", 0x200000, 0x200000, CRC(719fee5b) SHA1(c94f8ca066c9693cd7c9fd311db1ad9b2665fc69) )
	ROM_LOAD( "vic-080-v3.v3", 0x400000, 0x200000, CRC(64b7efde) SHA1(11727f9a3c4da17fa7b00559c7081b66e7211c49) )

	ROM_REGION( 0x800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "vic-080-c1.c1", 0x000000, 0x200000, CRC(94d90170) SHA1(4ab63dadc6ee0d32b8784c327681376f5fef0df9) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "vic-080-c2.c2", 0x000001, 0x200000, CRC(297f25a1) SHA1(0dd845726c640d70804b5fd5854921771e8dbf19) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "vic-080-c3.c3", 0x400000, 0x200000, CRC(cf484c4f) SHA1(f588908a693dbbb8362ffbfe5035dd5f867d9697) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "vic-080-c4.c4", 0x400001, 0x200000, CRC(36e5d997) SHA1(99955ff947e2e586e60c1146c978c70705787917) ) /* Plane 2,3 */
ROM_END

/****************************************
 ID-0081
 . NGM-081
 NEO-MVS PROGTOP / NEO-MVS CHA42G-3B
 NEO-MVS PROGTOP / NEO-MVS CHA256
 NEO-MVS PROG 4096 B / NEO-MVS CHA 42G-3
 NEO-MVS PROGBK1 / NEO-MVS CHA256B
 . NGH-081
 NEO-AEG PROGTOP2 / NEO-AEG CHA256 B
****************************************/

ROM_START( ssideki3 )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "081-p1.p1", 0x100000, 0x100000, CRC(6bc27a3d) SHA1(94692abe7343f9204a557acae4ab74d0af511ca3) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "081-s1.s1", CRC(7626da34) SHA1(30bad65633d0035fd578323c22cbddb8c9d549a6) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "081-m1.m1", CRC(82fcd863) SHA1(b219a5685450f9c24cc195f1c914bc3b292d72c0) ) /* TC531001 */

	ROM_REGION( 0x600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "081-v1.v1", 0x000000, 0x200000, CRC(201fa1e1) SHA1(9c27cc1b1d075223ed4a90dd02571d09a2f0d076) ) /* TC5316200 */
	ROM_LOAD( "081-v2.v2", 0x200000, 0x200000, CRC(acf29d96) SHA1(5426985c33aea2efc8ff774b59d34d8b03bd9a85) ) /* TC5316200 */
	ROM_LOAD( "081-v3.v3", 0x400000, 0x200000, CRC(e524e415) SHA1(8733e1b63471381b16c2b7c64b909745d99c8925) ) /* TC5316200 */

	ROM_REGION( 0xc00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "081-c1.c1", 0x000000, 0x200000, CRC(1fb68ebe) SHA1(abd9dbe7b7cbe0b6cd1d87e53c6bdc6edeccf83c) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "081-c2.c2", 0x000001, 0x200000, CRC(b28d928f) SHA1(9f05148e3e1e94339752658c066f47f133db8fbf) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "081-c3.c3", 0x400000, 0x200000, CRC(3b2572e8) SHA1(41aba1554bf59d4e5d5814249eaa0d531449e1de) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "081-c4.c4", 0x400001, 0x200000, CRC(47d26a7c) SHA1(591ef24a3d381163c5da80fa64e6883b8ea9abfb) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "081-c5.c5", 0x800000, 0x200000, CRC(17d42f0d) SHA1(7de7765bf43d390c50b2f59c2288502a7121d086) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "081-c6.c6", 0x800001, 0x200000, CRC(6b53fb75) SHA1(fadf7a12661d83ae35d9258aa4947969d51c08b8) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

/****************************************
 ID-0082
 . NGM-082
 NEO-MVS PROGTOP / NEO-MVS CHA256
 NEO-MVS PROGTOP / NEO-MVS CHA 42G-3
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 NEO-MVS PROGTOP / NEO-MVS CHA256B
 NEO-MVS PROG 4096 B / NEO-MVS CHA 42G-3
 NEO-MVS PROGGSC / NEO-MVS CHA 42G-3B
 . NGH-082
 NEO-AEG PROGTOP2 / NEO-AEG CHA256 B
****************************************/

ROM_START( doubledr )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "082-p1.p1", 0x100000, 0x100000, CRC(34ab832a) SHA1(fbb1bd195f5653f7b9c89648649f838eaf83cbe4) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "082-s1.s1", CRC(bef995c5) SHA1(9c89adbdaa5c1f827632c701688563dac2e482a4) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "082-m1.m1", CRC(10b144de) SHA1(cf1ed0a447da68240c62bcfd76b1569803f6bf76) ) /* TC531001 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "082-v1.v1", 0x000000, 0x200000, CRC(cc1128e4) SHA1(bfcfff24bc7fbde0b02b1bc0dffebd5270a0eb04) ) /* TC5316200 */
	ROM_LOAD( "082-v2.v2", 0x200000, 0x200000, CRC(c3ff5554) SHA1(c685887ad64998e5572607a916b023f8b9efac49) ) /* TC5316200 */

	ROM_REGION( 0xe00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "082-c1.c1", 0x000000, 0x200000, CRC(b478c725) SHA1(3a777c5906220f246a6dc06cb084e6ad650d67bb) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "082-c2.c2", 0x000001, 0x200000, CRC(2857da32) SHA1(9f13245965d23db86d46d7e73dfb6cc63e6f25a1) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "082-c3.c3", 0x400000, 0x200000, CRC(8b0d378e) SHA1(3a347215e414b738164f1fe4144102f07d4ffb80) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "082-c4.c4", 0x400001, 0x200000, CRC(c7d2f596) SHA1(e2d09d4d1b1fef9c0c53ecf3629e974b75e559f5) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "082-c5.c5", 0x800000, 0x200000, CRC(ec87bff6) SHA1(3fa86da93881158c2c23443855922a7b32e55135) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "082-c6.c6", 0x800001, 0x200000, CRC(844a8a11) SHA1(b2acbd4cacce66fb32c052b2fba9984904679bda) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "082-c7.c7", 0xc00000, 0x100000, CRC(727c4d02) SHA1(8204c7f037d46e0c58f269f9c7a535bc2589f526) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "082-c8.c8", 0xc00001, 0x100000, CRC(69a5fa37) SHA1(020e70e0e8b3c5d00a40fe97e418115a3187e50a) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

/****************************************
 ID-0083
 . NGM-083
 NEO-MVS PROGTOP / NEO-MVS CHA256
****************************************/

ROM_START( pbobblen ) /* MVS ONLY RELEASE */
	/* This set uses CHA and PROG board from Power Spikes II. Six Power Spikes II prom's are replaced with
	Puzzle Bobble prom's. Confirmed on several original carts. Do other layouts also exist? */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "d96-07.ep1", 0x000000, 0x080000, CRC(6102ca14) SHA1(328429d11de5b327a0654ae0548da4d0025a2ae6) ) /* 27C240 */

	NEO_SFIX_128K( "d96-04.s1", CRC(9caae538) SHA1(cf2d90a7c1a42107c0bb8b9a61397634286dbe0a) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "d96-06.m1", CRC(f424368a) SHA1(5e5bbcaeb82bed2ee17df08f005ca20ad1030723) ) /* M27C1001 */
	/* M1 on eprom with sticker; label is D96-06 */

	ROM_REGION( 0x380000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "068-v1.v1", 0x000000, 0x100000, CRC(2ced86df) SHA1(d6b73d1f31efbd74fb745200d4dade5f80b71541) ) /* unused */ /* mask rom TC538200 */
	ROM_LOAD( "068-v2.v2", 0x100000, 0x100000, CRC(970851ab) SHA1(6c9b04e9cc6b92133f1154e5bdd9d38d8ef050a7) ) /* unused */ /* mask rom TC538200 */
	ROM_LOAD( "d96-01.v3", 0x200000, 0x100000, CRC(0840cbc4) SHA1(1adbd7aef44fa80832f63dfb8efdf69fd7256a57) ) /* mask rom TC538200 */
	ROM_LOAD( "d96-05.v4", 0x300000, 0x080000, CRC(0a548948) SHA1(e1e4afd17811cb60401c14fbcf0465035165f4fb) ) /* mask rom TC534200 */

	ROM_REGION( 0x500000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "068-c1.c1", 0x000000, 0x100000, CRC(7f250f76) SHA1(5109a41adcb7859e24dc43d88842d4cc18cd3305) ) /* unused */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "068-c2.c2", 0x000001, 0x100000, CRC(20912873) SHA1(2df8766b531e47ffc30457e41c63b83557b4f468) ) /* unused */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "068-c3.c3", 0x200000, 0x100000, CRC(4b641ba1) SHA1(7a9c42a30163eda455f7bde2302402b1a5de7178) ) /* unused */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "068-c4.c4", 0x200001, 0x100000, CRC(35072596) SHA1(4150a21041f06514c97592bd8af686504b06e187) ) /* unused */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "d96-02.c5", 0x400000, 0x080000, CRC(e89ad494) SHA1(69c9ea415773af94ac44c48af05d55ada222b138) ) /* Plane 0,1 */ /* mask rom TC534200 */
	ROM_LOAD16_BYTE( "d96-03.c6", 0x400001, 0x080000, CRC(4b42d7eb) SHA1(042ae50a528cea21cf07771d3915c57aa16fd5af) ) /* Plane 2,3 */ /* mask rom TC534200 */
ROM_END

/****************************************
 ID-0084
 . NGM-084
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 NEO-MVS PROGSM / NEO-MVS CHA256
 . NGH-084
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( kof95 ) /* MVS VERSION */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "084-p1.p1",0x100000, 0x100000, CRC(2cba2716) SHA1(f6c2d0537c9c3e0938065c65b1797c47198fcff8) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "084-s1.s1", CRC(de716f8a) SHA1(f7386454a943ed5caf625f67ee1d0197b1c6fa13) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "084-m1.m1", CRC(6f2d7429) SHA1(6f8462e4f07af82a5ca3197895d5dcbb67bdaa61) ) /* TC531001 */

	ROM_REGION( 0x900000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "084-v1.v1", 0x000000, 0x400000, CRC(84861b56) SHA1(1b6c91ddaed01f45eb9b7e49d9c2b9b479d50da6) ) /* TC5332201 */
	ROM_LOAD( "084-v2.v2", 0x400000, 0x200000, CRC(b38a2803) SHA1(dbc2c8606ca09ed7ff20906b022da3cf053b2f09) ) /* TC5316200 */
	/* 600000-7fffff empty */
	ROM_LOAD( "084-v3.v3", 0x800000, 0x100000, CRC(d683a338) SHA1(eb9866b4b286edc09963cb96c43ce0a8fb09adbb) ) /* TC538200 */

	ROM_REGION( 0x1a00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "084-c1.c1", 0x0000000, 0x400000, CRC(fe087e32) SHA1(e8e89faa616027e4fb9b8a865c1a67f409c93bdf) ) /* Plane 0,1 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "084-c2.c2", 0x0000001, 0x400000, CRC(07864e09) SHA1(0817fcfd75d0735fd8ff27561eaec371e4ff5829) ) /* Plane 2,3 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "084-c3.c3", 0x0800000, 0x400000, CRC(a4e65d1b) SHA1(740a405b40b3a4b324697d2652cae29ffe0ac0bd) ) /* Plane 0,1 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "084-c4.c4", 0x0800001, 0x400000, CRC(c1ace468) SHA1(74ea2a3cfd7b744f0988a05baaff10016ca8f625) ) /* Plane 2,3 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "084-c5.c5", 0x1000000, 0x200000, CRC(8a2c1edc) SHA1(67866651bc0ce27122285a66b0aab108acf3d065) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "084-c6.c6", 0x1000001, 0x200000, CRC(f593ac35) SHA1(302c92c63f092a8d49429c3331e5e5678f0ea48d) ) /* Plane 2,3 */ /* TC5316200 */
	/* 1400000-17fffff empty */
	ROM_LOAD16_BYTE( "084-c7.c7", 0x1800000, 0x100000, CRC(9904025f) SHA1(eec770746a0ad073f7d353ab16a2cc3a5278d307) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "084-c8.c8", 0x1800001, 0x100000, CRC(78eb0f9b) SHA1(2925ea21ed2ce167f08a25589e94f28643379034) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

ROM_START( kof95a ) /* MVS VERSION */
	/* This set uses NEO-MVS PROGSM board; same rom data as in kof95h is used */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "084-epr.ep1", 0x000000, 0x080000, CRC(577ca1bb) SHA1(0d9d8b6db8a5a4ea47fd6602bc77df68b74b1691) ) /* M27C4002 */
	ROM_LOAD16_WORD_SWAP( "084-epr.ep2", 0x080000, 0x080000, CRC(30802a5d) SHA1(04109e7c4f8d171fcebbe1198f85a271b008f8f1) ) /* M27C4002 */
	ROM_LOAD16_WORD_SWAP( "084-epr.ep3", 0x100000, 0x080000, CRC(21ae248a) SHA1(87318a1bc667f31a9824beefee94617b4724dc2d) ) /* M27C4002 */
	ROM_LOAD16_WORD_SWAP( "084-epr.ep4", 0x180000, 0x080000, CRC(19d3fbee) SHA1(39225ec8a7ed5d2f5e83f5d575b9fa38800b0704) ) /* M27C4002 */
	/* P's on eprom, correct chip label unknown */

	NEO_SFIX_128K( "084-s1.s1", CRC(de716f8a) SHA1(f7386454a943ed5caf625f67ee1d0197b1c6fa13) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "084-m1.m1", CRC(6f2d7429) SHA1(6f8462e4f07af82a5ca3197895d5dcbb67bdaa61) ) /* TC531001 */

	ROM_REGION( 0x900000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "084-v1.v1", 0x000000, 0x400000, CRC(84861b56) SHA1(1b6c91ddaed01f45eb9b7e49d9c2b9b479d50da6) ) /* TC5332201 */
	ROM_LOAD( "084-v2.v2", 0x400000, 0x200000, CRC(b38a2803) SHA1(dbc2c8606ca09ed7ff20906b022da3cf053b2f09) ) /* TC5316200 */
	/* 600000-7fffff empty */
	ROM_LOAD( "084-v3.v3", 0x800000, 0x100000, CRC(d683a338) SHA1(eb9866b4b286edc09963cb96c43ce0a8fb09adbb) ) /* TC538200 */

	ROM_REGION( 0x1a00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "084-c1.c1", 0x0000000, 0x400000, CRC(fe087e32) SHA1(e8e89faa616027e4fb9b8a865c1a67f409c93bdf) ) /* Plane 0,1 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "084-c2.c2", 0x0000001, 0x400000, CRC(07864e09) SHA1(0817fcfd75d0735fd8ff27561eaec371e4ff5829) ) /* Plane 2,3 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "084-c3.c3", 0x0800000, 0x400000, CRC(a4e65d1b) SHA1(740a405b40b3a4b324697d2652cae29ffe0ac0bd) ) /* Plane 0,1 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "084-c4.c4", 0x0800001, 0x400000, CRC(c1ace468) SHA1(74ea2a3cfd7b744f0988a05baaff10016ca8f625) ) /* Plane 2,3 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "084-c5.c5", 0x1000000, 0x200000, CRC(8a2c1edc) SHA1(67866651bc0ce27122285a66b0aab108acf3d065) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "084-c6.c6", 0x1000001, 0x200000, CRC(f593ac35) SHA1(302c92c63f092a8d49429c3331e5e5678f0ea48d) ) /* Plane 2,3 */ /* TC5316200 */
	/* 1400000-17fffff empty */
	ROM_LOAD16_BYTE( "084-c7.c7", 0x1800000, 0x100000, CRC(9904025f) SHA1(eec770746a0ad073f7d353ab16a2cc3a5278d307) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "084-c8.c8", 0x1800001, 0x100000, CRC(78eb0f9b) SHA1(2925ea21ed2ce167f08a25589e94f28643379034) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

ROM_START( kof95h ) /* MVS AND AES VERSION */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "084-pg1.p1", 0x100000, 0x100000, CRC(5e54cf95) SHA1(41abe2042fdbb1526e92a0789976a9b1ac5e60f0) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "084-s1.s1", CRC(de716f8a) SHA1(f7386454a943ed5caf625f67ee1d0197b1c6fa13) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "084-m1.m1", CRC(6f2d7429) SHA1(6f8462e4f07af82a5ca3197895d5dcbb67bdaa61) ) /* TC531001 */

	ROM_REGION( 0x900000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "084-v1.v1", 0x000000, 0x400000, CRC(84861b56) SHA1(1b6c91ddaed01f45eb9b7e49d9c2b9b479d50da6) ) /* TC5332201 */
	ROM_LOAD( "084-v2.v2", 0x400000, 0x200000, CRC(b38a2803) SHA1(dbc2c8606ca09ed7ff20906b022da3cf053b2f09) ) /* TC5316200 */
	/* 600000-7fffff empty */
	ROM_LOAD( "084-v3.v3", 0x800000, 0x100000, CRC(d683a338) SHA1(eb9866b4b286edc09963cb96c43ce0a8fb09adbb) ) /* TC538200 */

	ROM_REGION( 0x1a00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "084-c1.c1", 0x0000000, 0x400000, CRC(fe087e32) SHA1(e8e89faa616027e4fb9b8a865c1a67f409c93bdf) ) /* Plane 0,1 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "084-c2.c2", 0x0000001, 0x400000, CRC(07864e09) SHA1(0817fcfd75d0735fd8ff27561eaec371e4ff5829) ) /* Plane 2,3 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "084-c3.c3", 0x0800000, 0x400000, CRC(a4e65d1b) SHA1(740a405b40b3a4b324697d2652cae29ffe0ac0bd) ) /* Plane 0,1 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "084-c4.c4", 0x0800001, 0x400000, CRC(c1ace468) SHA1(74ea2a3cfd7b744f0988a05baaff10016ca8f625) ) /* Plane 2,3 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "084-c5.c5", 0x1000000, 0x200000, CRC(8a2c1edc) SHA1(67866651bc0ce27122285a66b0aab108acf3d065) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "084-c6.c6", 0x1000001, 0x200000, CRC(f593ac35) SHA1(302c92c63f092a8d49429c3331e5e5678f0ea48d) ) /* Plane 2,3 */ /* TC5316200 */
	/* 1400000-17fffff empty */
	ROM_LOAD16_BYTE( "084-c7.c7", 0x1800000, 0x100000, CRC(9904025f) SHA1(eec770746a0ad073f7d353ab16a2cc3a5278d307) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "084-c8.c8", 0x1800001, 0x100000, CRC(78eb0f9b) SHA1(2925ea21ed2ce167f08a25589e94f28643379034) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

/****************************************
 ID-0085
 Shinsetsu Samurai Spirits Bushidoretsuden / Samurai Shodown RPG (CD only)
****************************************/

/****************************************
 ID-0086
 . ??M-086
 NEO-MVS PROGBK1 / NEO-MVS CHA256B
****************************************/

ROM_START( twsoc96 ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "086-p1.p1", 0x000000, 0x100000, CRC(03e20ab6) SHA1(3a0a5a54649178ce7a6158980cb4445084b40fb5) ) /* mask rom TC538200 */

	NEO_SFIX_128K( "086-s1.s1", CRC(6f5e2b3a) SHA1(273341489f6625d35a4a920042a60e2b86373847) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "086-m1.m1", CRC(cb82bc5d) SHA1(8e3ecabec25d89adb6e0eed0ef5f94d34a4d5fc0) ) /* mask rom TC531001 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "086-v1.v1", 0x000000, 0x200000, CRC(97bf1986) SHA1(b80d3a37e18d0a52f1e0092dc300989c9647efd1) ) /* mask rom TC5316200 */
	ROM_LOAD( "086-v2.v2", 0x200000, 0x200000, CRC(b7eb05df) SHA1(ff2b55c7021c248cfdcfc9cd3658f2896bcbca38) ) /* mask rom TC5316200 */

	ROM_REGION( 0xa00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "086-c1.c1", 0x000000, 0x400000, CRC(2611bc2a) SHA1(bb5a96acd4a90fcb41c49cc8e9f760c4a06d6b84) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "086-c2.c2", 0x000001, 0x400000, CRC(6b0d6827) SHA1(3cb2bbab381a26ec69f97c3d6116ce47254286b4) ) /* Plane 2,3 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "086-c3.c3", 0x800000, 0x100000, CRC(750ddc0c) SHA1(9304a83d81afd544d88be0cd3ee47ae401d2da0e) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "086-c4.c4", 0x800001, 0x100000, CRC(7a6e7d82) SHA1(b1bb82cec3d68367d5e01e63c44c11b67e577411) ) /* Plane 2,3 */ /* mask rom TC538200 */
ROM_END

/****************************************
 ID-0087
 . NGM-087
 NEO-MVS PROGSS3 / NEO-MVS CHA256
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 . NGH-087
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( samsho3 ) /* MVS VERSION */
	ROM_REGION( 0x300000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "087-epr.ep1", 0x000000, 0x080000, CRC(23e09bb8) SHA1(79da99fa50a639fce9d1266699b5e53c9ac55642) ) /* M27C4002 */
	ROM_LOAD16_WORD_SWAP( "087-epr.ep2", 0x080000, 0x080000, CRC(256f5302) SHA1(e2d21b413a6059194a994b7902b2a7df98a15151) ) /* M27C4002 */
	ROM_LOAD16_WORD_SWAP( "087-epr.ep3", 0x100000, 0x080000, CRC(bf2db5dd) SHA1(b4fa1dc1eccc9eb1ce74f0a06992ef89b1cbc732) ) /* M27C4002 */
	ROM_LOAD16_WORD_SWAP( "087-epr.ep4", 0x180000, 0x080000, CRC(53e60c58) SHA1(f975e81cab6322d3260348402721c673023259fa) ) /* M27C4002 */
	/* P's on eprom, correct chip label unknown */
	ROM_LOAD16_WORD_SWAP( "087-p5.p5",  0x200000, 0x100000, CRC(e86ca4af) SHA1(5246acbab77ac2f232b88b8522187764ff0872f0) ) /* TC538200 */
	/* also found sets with P1 / SP2 on maskrom on PROG board NEO-MVS PROGBK1; chip label are 087-PG1 and 087-P2 */

	NEO_SFIX_128K( "087-s1.s1", CRC(74ec7d9f) SHA1(d79c479838a7ca51735a44f91f1968ec5b3c6b91) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "087-m1.m1", CRC(8e6440eb) SHA1(e3f72150af4e326543b29df71cda27d73ec087c1) ) /* T531001 */

	ROM_REGION( 0x600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "087-v1.v1", 0x000000, 0x400000, CRC(84bdd9a0) SHA1(adceceb00569eca13fcc2e0f0d9f0d9b06a06851) ) /* TC5332201 */
	ROM_LOAD( "087-v2.v2", 0x400000, 0x200000, CRC(ac0f261a) SHA1(5411bdff24cba7fdbc3397d45a70fb468d7a44b3) ) /* TC5316200 */

	ROM_REGION( 0x1a00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "087-c1.c1", 0x0000000, 0x400000, CRC(07a233bc) SHA1(654cb56cfd6eeebe6745c0b8b730317fb8ccd3d9) ) /* Plane 0,1 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "087-c2.c2", 0x0000001, 0x400000, CRC(7a413592) SHA1(b8c7a2d0d7a8b14d6cab94d7a5f347e73c6ab7a4) ) /* Plane 2,3 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "087-c3.c3", 0x0800000, 0x400000, CRC(8b793796) SHA1(053acc129ea56691607a5d255845703e61fd3ada) ) /* Plane 0,1 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "087-c4.c4", 0x0800001, 0x400000, CRC(728fbf11) SHA1(daa319d455f759bfc08a37b43218bdb48dc1c9e5) ) /* Plane 2,3 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "087-c5.c5", 0x1000000, 0x400000, CRC(172ab180) SHA1(a6122f683bdb78d0079e1e360c1b96ba28def7b7) ) /* Plane 0,1 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "087-c6.c6", 0x1000001, 0x400000, CRC(002ff8f3) SHA1(3a378708697d727796c4f702dd5bbf1c9eb4daec) ) /* Plane 2,3 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "087-c7.c7", 0x1800000, 0x100000, CRC(ae450e3d) SHA1(ec482632cc347ec3f9e68df0ebcaa16ebe41b9f9) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "087-c8.c8", 0x1800001, 0x100000, CRC(a9e82717) SHA1(e39ee15d5140dbe7f06eea945cce9984a5e8b06a) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

ROM_START( samsho3h ) /* AES VERSION */
	ROM_REGION( 0x300000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "087-pg1.p1", 0x000000, 0x100000, CRC(282a336e) SHA1(e062f1939d36a45f185b5dbd726cdd833dc7c28c) ) /* mask rom TC538200 */
	ROM_LOAD16_WORD_SWAP( "087-p2.sp2", 0x100000, 0x200000, CRC(9bbe27e0) SHA1(b18117102159903c8e8f4e4226e1cc91a400e816) ) /* mask rom TC5316200 */

	NEO_SFIX_128K( "087-s1.s1", CRC(74ec7d9f) SHA1(d79c479838a7ca51735a44f91f1968ec5b3c6b91) ) /* T531000 */

	NEO_BIOS_AUDIO_128K( "087-m1.m1", CRC(8e6440eb) SHA1(e3f72150af4e326543b29df71cda27d73ec087c1) ) /* T531001 */

	ROM_REGION( 0x600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "087-v1.v1", 0x000000, 0x400000, CRC(84bdd9a0) SHA1(adceceb00569eca13fcc2e0f0d9f0d9b06a06851) ) /* TC5332201 */
	ROM_LOAD( "087-v2.v2", 0x400000, 0x200000, CRC(ac0f261a) SHA1(5411bdff24cba7fdbc3397d45a70fb468d7a44b3) ) /* TC5316200 */

	ROM_REGION( 0x1a00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "087-c1.c1", 0x0000000, 0x400000, CRC(07a233bc) SHA1(654cb56cfd6eeebe6745c0b8b730317fb8ccd3d9) ) /* Plane 0,1 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "087-c2.c2", 0x0000001, 0x400000, CRC(7a413592) SHA1(b8c7a2d0d7a8b14d6cab94d7a5f347e73c6ab7a4) ) /* Plane 2,3 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "087-c3.c3", 0x0800000, 0x400000, CRC(8b793796) SHA1(053acc129ea56691607a5d255845703e61fd3ada) ) /* Plane 0,1 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "087-c4.c4", 0x0800001, 0x400000, CRC(728fbf11) SHA1(daa319d455f759bfc08a37b43218bdb48dc1c9e5) ) /* Plane 2,3 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "087-c5.c5", 0x1000000, 0x400000, CRC(172ab180) SHA1(a6122f683bdb78d0079e1e360c1b96ba28def7b7) ) /* Plane 0,1 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "087-c6.c6", 0x1000001, 0x400000, CRC(002ff8f3) SHA1(3a378708697d727796c4f702dd5bbf1c9eb4daec) ) /* Plane 2,3 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "087-c7.c7", 0x1800000, 0x100000, CRC(ae450e3d) SHA1(ec482632cc347ec3f9e68df0ebcaa16ebe41b9f9) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "087-c8.c8", 0x1800001, 0x100000, CRC(a9e82717) SHA1(e39ee15d5140dbe7f06eea945cce9984a5e8b06a) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

ROM_START( fswords ) /* KOREAN VERSION */
	ROM_REGION( 0x300000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "187-p1k.p1", 0x000000, 0x100000, CRC(c8e7c075) SHA1(7b74f2917114460d79d8f46ee24829a4c08cbf2a) )
	ROM_LOAD16_WORD_SWAP( "087-p2.sp2", 0x100000, 0x200000, CRC(9bbe27e0) SHA1(b18117102159903c8e8f4e4226e1cc91a400e816) ) /* mask rom TC5316200 */

	NEO_SFIX_128K( "087-s1.s1", CRC(74ec7d9f) SHA1(d79c479838a7ca51735a44f91f1968ec5b3c6b91) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "087-m1.m1", CRC(8e6440eb) SHA1(e3f72150af4e326543b29df71cda27d73ec087c1) ) /* TC531001 */

	ROM_REGION( 0x600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "087-v1.v1", 0x000000, 0x400000, CRC(84bdd9a0) SHA1(adceceb00569eca13fcc2e0f0d9f0d9b06a06851) ) /* TC5332201 */
	ROM_LOAD( "087-v2.v2", 0x400000, 0x200000, CRC(ac0f261a) SHA1(5411bdff24cba7fdbc3397d45a70fb468d7a44b3) ) /* TC5316200 */

	ROM_REGION( 0x1a00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "087-c1.c1", 0x0000000, 0x400000, CRC(07a233bc) SHA1(654cb56cfd6eeebe6745c0b8b730317fb8ccd3d9) ) /* Plane 0,1 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "087-c2.c2", 0x0000001, 0x400000, CRC(7a413592) SHA1(b8c7a2d0d7a8b14d6cab94d7a5f347e73c6ab7a4) ) /* Plane 2,3 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "087-c3.c3", 0x0800000, 0x400000, CRC(8b793796) SHA1(053acc129ea56691607a5d255845703e61fd3ada) ) /* Plane 0,1 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "087-c4.c4", 0x0800001, 0x400000, CRC(728fbf11) SHA1(daa319d455f759bfc08a37b43218bdb48dc1c9e5) ) /* Plane 2,3 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "087-c5.c5", 0x1000000, 0x400000, CRC(172ab180) SHA1(a6122f683bdb78d0079e1e360c1b96ba28def7b7) ) /* Plane 0,1 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "087-c6.c6", 0x1000001, 0x400000, CRC(002ff8f3) SHA1(3a378708697d727796c4f702dd5bbf1c9eb4daec) ) /* Plane 2,3 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "087-c7.c7", 0x1800000, 0x100000, CRC(ae450e3d) SHA1(ec482632cc347ec3f9e68df0ebcaa16ebe41b9f9) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "087-c8.c8", 0x1800001, 0x100000, CRC(a9e82717) SHA1(e39ee15d5140dbe7f06eea945cce9984a5e8b06a) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

/****************************************
 ID-0088
 . NGM-088
 NEO-MVS PROGBK1 / NEO-MVS CHA42-3B
 . NGH-088
 NEO-AEG PROGRKB / NEO-AEG CHA256[B]
****************************************/

ROM_START( stakwin )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "088-p1.p1", 0x100000, 0x100000, CRC(bd5814f6) SHA1(95179a4dee61ae88bb5d9fd74af0c56c8c29f5ea) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000)

	NEO_SFIX_128K( "088-s1.s1", CRC(073cb208) SHA1(c5b4697d767575884dd49ae416c1fe4a4a92d3f6) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "088-m1.m1", CRC(2fe1f499) SHA1(5b747eeef65be04423d2db05e086df9132758a47) ) /* TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "088-v1.v1", 0x000000, 0x200000, CRC(b7785023) SHA1(d11df1e623434669cd3f97f0feda747b24dac05d) ) /* TC5316200 */

	ROM_REGION( 0x800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "088-c1.c1", 0x000000, 0x200000, CRC(6e733421) SHA1(b67c5d2654a62cc4e44bd54d28e62c7da5eea424) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "088-c2.c2", 0x000001, 0x200000, CRC(4d865347) SHA1(ad448cf96f3dce44c83412ed6878c495eb4a8a1e) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "088-c3.c3", 0x400000, 0x200000, CRC(8fa5a9eb) SHA1(7bee19d8a2bccedd8e2cf0c0e9138902b9dafc23) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "088-c4.c4", 0x400001, 0x200000, CRC(4604f0dc) SHA1(ddf5dbb5e07313998a8f695ad19354ea54585dd6) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

/*

large development boards, has SRAMs + battery for some of the program & both MVS and AES connectors on different edges

X3002-C3 (Z80 program + C ROMs + S1 ROM)
X3002-P2B (68k program + V ROMs)

8x HM628128 SRAM marked P1-P8 near the 68k program

likely impossible to emulate as the content of the SRAM (most of the program / data) is lost due to dead battery

*/

ROM_START( stakwindev )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_BYTE( "sp1.sp1", 0x000001, 0x080000, CRC(96b58fa4) SHA1(23c8c2a4d7a16838fb54306fb193b355edc75060) )
	ROM_LOAD16_BYTE( "sp2.sp2", 0x000000, 0x080000, CRC(4e7202ee) SHA1(ab10dda5594ad8a21af219c6907a9841de7384c3) )
	// SP2/SP3 unpopulated, P1-P8 are RAM

	NEO_SFIX_128K( "s1.s1", CRC(2193d089) SHA1(1b6f57d2f4265f210194488356fc3653dfa35932) )

	NEO_BIOS_AUDIO_512K( "m1.m1", CRC(daedbcd7) SHA1(d6d0e67ff7865ff5a0262aba381468e198d50536) )

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "v1.v1", 0x000000, 0x80000, CRC(ac590b85) SHA1(47ff2ba9648b7145c29b534cd1f6839abb43bee2) )
	ROM_LOAD( "v2.v2", 0x080000, 0x80000, CRC(23ad8a91) SHA1(da620c182fe9fc36421da73f2937e627d9a641bf) )
	ROM_LOAD( "v3.v3", 0x100000, 0x80000, CRC(e2d216b6) SHA1(28b70e57a352ef6a08a5564f7756113a66e7dbb7) )
	// V4 - V16 unpopulated

	ROM_REGION( 0x800000, "cslot1:sprites", 0 ) // note, ROM_LOAD32_BYTE
	ROM_LOAD32_BYTE( "c1.c1",   0x000000, 0x80000, CRC(fb2f29d3) SHA1(1f4cf27d7da1262bae28a8dd88c56d8a9324e982) ) /* Plane 0 */
	ROM_LOAD32_BYTE( "c2.c2",   0x000002, 0x80000, CRC(6108e6db) SHA1(f4cdb5df29698f1ba362a227b82d43c3baaa8948) ) /* Plane 2 */
	ROM_LOAD32_BYTE( "c3.c3",   0x000001, 0x80000, CRC(702a21b1) SHA1(79006062b516bda35bb77c864ba9ea26135ee8ed) ) /* Plane 1 */
	ROM_LOAD32_BYTE( "c4.c4",   0x000003, 0x80000, CRC(3223b05f) SHA1(1595f8029f71c3acd949ddd5539d62424c0b697e) ) /* Plane 3 */
	ROM_LOAD32_BYTE( "c5.c5",   0x200000, 0x80000, CRC(36b93e77) SHA1(19431b1ad44b9418f26b94c15572d11caec0994e) ) /* Plane 0 */
	ROM_LOAD32_BYTE( "c6.c6",   0x200002, 0x80000, CRC(a7a104c8) SHA1(421a8ef11e299b7379a8198c871c8f11ad97deb0) ) /* Plane 2 */
	ROM_LOAD32_BYTE( "c7.c7",   0x200001, 0x80000, CRC(f0d8b2ca) SHA1(2565a44d3ee9375af595d84638c7a76f631f8f9b) ) /* Plane 1 */
	ROM_LOAD32_BYTE( "c8.c8",   0x200003, 0x80000, CRC(3bdd06eb) SHA1(dce84eb477229dfa8233cde8b946b64db1efae1d) ) /* Plane 3 */
	ROM_LOAD32_BYTE( "c9.c9",   0x400000, 0x80000, CRC(25621e44) SHA1(3e380c1f52233dd1346c0363d920d1f96491cc45) ) /* Plane 0 */
	ROM_LOAD32_BYTE( "c10.c10", 0x400002, 0x80000, CRC(17b2115e) SHA1(8c5d1b12ac7e739a7ac1f2189a2f3ca959d06377) ) /* Plane 2 */
	ROM_LOAD32_BYTE( "c11.c11", 0x400001, 0x80000, CRC(7d3cd161) SHA1(5ac30c35e815dd2b0235394c1768e419395a1b5a) ) /* Plane 1 */
	ROM_LOAD32_BYTE( "c12.c12", 0x400003, 0x80000, CRC(c6970ed6) SHA1(e963d0a5fb718e0eec2115d63ab34a0de0523792) ) /* Plane 3 */
	// C13 - C32 unpopulated
ROM_END


/****************************************
 ID-0089
 . NGM-089
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 NEO-MVS PROGBK1 / NEO-MVS CHA256B
 . NGH-089
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( pulstar )
	ROM_REGION( 0x300000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "089-p1.p1",  0x000000, 0x100000, CRC(5e5847a2) SHA1(b864d0ec4184b785569ddbf67c2115b5ab86ee3e) ) /* mask rom TC538200 */
	ROM_LOAD16_WORD_SWAP( "089-p2.sp2", 0x100000, 0x200000, CRC(028b774c) SHA1(fc5da2821a5072f2b78245fc59b6e3eeef116d16) ) /* mask rom TC5316200 */
	/* also found MVS sets with EP1 / EP2 on M27C4002 eprom with sticker and P1 / SP2 on TC578200D eprom with sticker; chip label are 089-P1, 089-SP2, EP1 and EP2 */

	NEO_SFIX_128K( "089-s1.s1", CRC(c79fc2c8) SHA1(914c224fb3c461a68d7425cae724cf22bd5f985d) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "089-m1.m1", CRC(ff3df7c7) SHA1(59d2ef64f734f6026073b365300221909057a512) ) /* mask rom TC531001 */

	ROM_REGION( 0x800000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "089-v1.v1", 0x000000, 0x400000, CRC(6f726ecb) SHA1(e8e2a46af690ce6c7ee64a58ab5010d22df9548c) ) /* mask rom TC5332204 */
	ROM_LOAD( "089-v2.v2", 0x400000, 0x400000, CRC(9d2db551) SHA1(83f7e5db7fb1502ceadcd334df90b11b1bba78e5) ) /* mask rom TC5332204 */

	ROM_REGION( 0x1c00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "089-c1.c1", 0x0000000, 0x400000, CRC(f4e97332) SHA1(54693827a99836e7d61c45d495dd78bf3fcf1544) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "089-c2.c2", 0x0000001, 0x400000, CRC(836d14da) SHA1(99cc4f9b764503eff7849ff2977d90bb47c5564a) ) /* Plane 2,3 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "089-c3.c3", 0x0800000, 0x400000, CRC(913611c4) SHA1(9664eb1fe1e6f8c3ddeeff872d38ea920ed38a82) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "089-c4.c4", 0x0800001, 0x400000, CRC(44cef0e3) SHA1(34f6f348ba86a2a06cb9c43a16b97cf6ee6158ac) ) /* Plane 2,3 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "089-c5.c5", 0x1000000, 0x400000, CRC(89baa1d7) SHA1(976c745c44967de61e2a23227835be580b1d283a) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "089-c6.c6", 0x1000001, 0x400000, CRC(b2594d56) SHA1(685c0bf8ff76c76e41c2ceaebb96349634cfdb2e) ) /* Plane 2,3 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "089-c7.c7", 0x1800000, 0x200000, CRC(6a5618ca) SHA1(9a1d5f998b0dfabacf9dad45c94bef2bb43e5e0c) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "089-c8.c8", 0x1800001, 0x200000, CRC(a223572d) SHA1(2791b1212f57937b2b2a95bc9e420c06d0c37669) ) /* Plane 2,3 */ /* mask rom TC5316200 */
ROM_END

/****************************************
 ID-0090
 . ADM-009
 NEO-MVS PROGTOP / NEO-MVS CHA256
 NEO-MVS PROGGSC / NEO-MVS CHA256
 NEO-MVS PROGGSC / NEO-MVS CHA256B
 NEO-MVS PROGBK1 / NEO-MVS CHA256B
 . ADH-009
****************************************/

ROM_START( whp )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "090-p1.p1", 0x100000, 0x100000, CRC(afaa4702) SHA1(83d122fddf17d4774353abf4a0655f3939f7b752) ) /* mask rom TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "090-s1.s1", CRC(174a880f) SHA1(c35d315d728d119a6e9aa42e0593937c90897449) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "090-m1.m1", CRC(28065668) SHA1(0c60d4afa1dccad0135e733104f056be73b54e4e) ) /* mask rom TC531001 */

	ROM_REGION( 0x600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "090-v1.v1", 0x000000, 0x200000, CRC(30cf2709) SHA1(d1845033f16de2470afd3858ee0efb45176d9ed7) ) /* mask rom TC5316200 */
	ROM_LOAD( "064-v2.v2", 0x200000, 0x200000, CRC(b6527edd) SHA1(2bcf5bfa6e117cf4a3728a5e5f5771313c93f22a) ) /* mask rom TC5316200 */
	ROM_LOAD( "090-v3.v3", 0x400000, 0x200000, CRC(1908a7ce) SHA1(78f31bcfea33eb94752bbf5226c481baec1af5ac) ) /* mask rom TC5316200 */

	ROM_REGION( 0x1c00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "090-c1.c1", 0x0000000, 0x400000, CRC(cd30ed9b) SHA1(839c20f7ff31251acc875ae402b5d267e55510c7) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "090-c2.c2", 0x0000001, 0x400000, CRC(10eed5ee) SHA1(12131b1c8c017ea77a98c044b392a5db6aad0143) ) /* Plane 2,3 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "064-c3.c3", 0x0800000, 0x200000, CRC(436d1b31) SHA1(059776d77b91377ed0bcfc278802d659c917fc0f) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "064-c4.c4", 0x0800001, 0x200000, CRC(f9c8dd26) SHA1(25a9eea1d49b21b4a988beb32c25bf2f7796f227) ) /* Plane 2,3 */ /* mask rom TC5316200 */
	/* 0c00000-0ffffff empty */
	ROM_LOAD16_BYTE( "064-c5.c5", 0x1000000, 0x200000, CRC(8e34a9f4) SHA1(67b839b426ef3fad0a85d951fdd44c0a45c55226) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "064-c6.c6", 0x1000001, 0x200000, CRC(a43e4766) SHA1(54f282f2b1ff2934cca7acbb4386a2b99a29df3a) ) /* Plane 2,3 */ /* mask rom TC5316200 */
	/* 1400000-17fffff empty */
	ROM_LOAD16_BYTE( "064-c7.c7", 0x1800000, 0x200000, CRC(59d97215) SHA1(85a960dc7f364df13ee0c2f99a4c53aefb081486) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "064-c8.c8", 0x1800001, 0x200000, CRC(fc092367) SHA1(69ff4ae909dd857de3ca8645d63f8b4bde117448) ) /* Plane 2,3 */ /* mask rom TC5316200 */
ROM_END

/****************************************
 ID-0091
 ADK World / ADK Special 1995 ADK (CD only)
****************************************/

/****************************************
 ID-0092
 . NGM-092
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 NEO-MVS PROGTOP / NEO-MVS CHA256
 . NGH-092
****************************************/

ROM_START( kabukikl )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "092-p1.p1", 0x100000, 0x100000, CRC(28ec9b77) SHA1(7cdc789a99f8127f437d68cbc41278c926be9efd) ) /* mask rom TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "092-s1.s1", CRC(a3d68ee2) SHA1(386f6110a16967a72fbf788f9d968fddcdcd2889) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "092-m1.m1", CRC(91957ef6) SHA1(7b6907532a0e02ceb643623cbd689cf228776ed1) ) /* mask rom TC531001 */

	ROM_REGION( 0x700000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "092-v1.v1", 0x000000, 0x200000, CRC(69e90596) SHA1(1a2007d7784b3ce90d115980c3353862f1664d45) ) /* mask rom TC5316200 */
	ROM_LOAD( "092-v2.v2", 0x200000, 0x200000, CRC(7abdb75d) SHA1(0bff764889fe02f37877514c7fc450250839f632) ) /* mask rom TC5316200 */
	ROM_LOAD( "092-v3.v3", 0x400000, 0x200000, CRC(eccc98d3) SHA1(b0dfbdb1ea045cb961323ac6906ab342256c3dc7) ) /* mask rom TC5316200 */
	ROM_LOAD( "092-v4.v4", 0x600000, 0x100000, CRC(a7c9c949) SHA1(574bc55b45e81ce357b14f5992426115de25cd35) ) /* mask rom TC538200 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "092-c1.c1", 0x000000, 0x400000, CRC(2a9fab01) SHA1(aa9f037df33ae0575b328734c76c0918ae1917e9) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "092-c2.c2", 0x000001, 0x400000, CRC(6d2bac02) SHA1(dfe96b62883333872be432e8af1ae617c9e62698) ) /* Plane 2,3 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "092-c3.c3", 0x800000, 0x400000, CRC(5da735d6) SHA1(f1c05a73794ece15576a0a30c81f4a44faac475a) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "092-c4.c4", 0x800001, 0x400000, CRC(de07f997) SHA1(c27a4d4bef868eed38dc152ff37d4135b16cc991) ) /* Plane 2,3 */ /* mask rom TC5332205 */
ROM_END

/****************************************
 ID-0093
 . ??M-093
 NEO-MVS PROGBK1 / NEO-MVS CHA256
****************************************/

ROM_START( neobombe ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "093-p1.p1", 0x000000, 0x100000, CRC(a1a71d0d) SHA1(059284c84f61a825923d86d2f29c91baa2c439cd) ) /* TC538200 */

	NEO_SFIX_128K( "093-s1.s1", CRC(4b3fa119) SHA1(41cb0909bfb017eb6f2c530cb92a423319ed7ab1) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "093-m1.m1", CRC(e81e780b) SHA1(c56c53984e0f92e180e850c60a75f550ee84917c) ) /* TC531001 */

	ROM_REGION( 0x600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "093-v1.v1", 0x000000, 0x400000, CRC(02abd4b0) SHA1(6bf33ebc9b01cd4a029f6a555694a9835e30ca1b) ) /* TC5332204 */
	ROM_LOAD( "093-v2.v2", 0x400000, 0x200000, CRC(a92b8b3d) SHA1(b672c97b85d2f52eba3cb26025008ebc7a18312a) ) /* TC5316200 */

	ROM_REGION( 0x900000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "093-c1.c1", 0x000000, 0x400000, CRC(d1f328f8) SHA1(ddf71280c2ce85225f15fe9e973f330609281878) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "093-c2.c2", 0x000001, 0x400000, CRC(82c49540) SHA1(5f37c1bc0d63c98a13967b44da3d2c85e6dbbe50) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "093-c3.c3", 0x800000, 0x080000, CRC(e37578c5) SHA1(20024caa0f09ee887a6418dd02d02a0df93786fd) ) /* Plane 0,1 */ /* TC534200 */
	ROM_LOAD16_BYTE( "093-c4.c4", 0x800001, 0x080000, CRC(59826783) SHA1(0110a2b6186cca95f75225d4d0269d61c2ad25b1) ) /* Plane 2,3 */ /* TC534200 */
ROM_END

/****************************************
 ID-0094
 . NGM-094
 NEO-MVS PROGBK1 / NEO-MVS CHA42G-3B
 . NGH-094
 NEO-AEG PROGTOP2Y / NEO-AEG CHA256BY
****************************************/

ROM_START( gowcaizr )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "094-p1.p1", 0x100000, 0x100000, CRC(33019545) SHA1(213db6c0b7d24b74b809854f9c606dbea1d9ba00) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "094-s1.s1", CRC(2f8748a2) SHA1(5cc723c4284120473d63d8b0c1a3b3be74bdc324) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "094-m1.m1", CRC(78c851cb) SHA1(a9923c002e4e2171a564af45cff0958c5d57b275) ) /* TC531001 */

	ROM_REGION( 0x500000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "094-v1.v1", 0x000000, 0x200000, CRC(6c31223c) SHA1(ede3a2806d7d872a0f737626a23ecce200b534e6) ) /* TC5316200 */
	ROM_LOAD( "094-v2.v2", 0x200000, 0x200000, CRC(8edb776c) SHA1(a9eac5e24f83ccdcf303d63261747b1bad876a24) ) /* TC5316200 */
	ROM_LOAD( "094-v3.v3", 0x400000, 0x100000, CRC(c63b9285) SHA1(6bbbacfe899e204e74657d6c3f3d05ce75e432f1) ) /* TC538200 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "094-c1.c1", 0x000000, 0x200000, CRC(042f6af5) SHA1(1c50df6a1a53ffb3079ea0a19c746f5c9536a3ed) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "094-c2.c2", 0x000001, 0x200000, CRC(0fbcd046) SHA1(9a6dc920a877f27424477c3478907b23afbaa5ea) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "094-c3.c3", 0x400000, 0x200000, CRC(58bfbaa1) SHA1(4c6f9cf138c5e6dfe89a45e2a690a986c75f5bfc) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "094-c4.c4", 0x400001, 0x200000, CRC(9451ee73) SHA1(7befee4a886b1d7493c06cefb7abf4ec01c14a8b) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "094-c5.c5", 0x800000, 0x200000, CRC(ff9cf48c) SHA1(5f46fb5d0812275b0006919d8540f22be7c16492) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "094-c6.c6", 0x800001, 0x200000, CRC(31bbd918) SHA1(7ff8c5e3f17d40e7a8a189ad8f8026de55368810) ) /* Plane 2,3 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "094-c7.c7", 0xc00000, 0x200000, CRC(2091ec04) SHA1(a81d4bdbef1ac6ea49845dc30e31bf9745694100) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "094-c8.c8", 0xc00001, 0x200000, CRC(d80dd241) SHA1(1356a64e4d4e271f62cd0d83f79ee9c906440810) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

/****************************************
 dev board, same ID as gowcaizr
****************************************/

ROM_START( dragonsh )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_BYTE( "ep2.bin", 0x000000, 0x080000, CRC(f25c71ad) SHA1(803fb6cd6a7ada59678ad901ff9788b1e54ddd0c) )
	ROM_LOAD16_BYTE( "ep1.bin", 0x000001, 0x080000, CRC(f353448c) SHA1(f0f966ca15d503e01b40e901765ff0888463b65d) )

	NEO_SFIX_128K( "s1.s1", BAD_DUMP CRC(706477a7) SHA1(8cbee7f6832e7edd2dc792ca330420a6a984b879) ) // was a dead AXS512PC 512KB sram card, this data is handcrafted to make the set usable (hence BAD_DUMP)

	NEOGEO_BIOS
	ROM_REGION( 0x20000, "audiobios", 0 )
	ROM_LOAD( "sm1.sm1", 0x00000, 0x20000, CRC(94416d67) SHA1(42f9d7ddd6c0931fd64226a60dc73602b2819dcf) )
	ROM_REGION( 0x30000, "cslot1:audiocpu", ROMREGION_ERASEFF )
	ROM_FILL(0x38,1,0x18)
	ROM_FILL(0x39,1,0xfe) // stop error.log filling up
	// not present

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", ROMREGION_ERASE00 )
	ROM_LOAD( "sram.v1", 0x000000, 0x200000, NO_DUMP ) // was a dead AXS2000PC 2MB sram card, battery dead, data lost.

	ROM_REGION( 0x2000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "no3.bin", 0x000000, 0x1000000, CRC(81821826) SHA1(b7c1a53e32633383675206a16c68f6f2ff984865) )
	ROM_LOAD16_BYTE( "no4.bin", 0x000001, 0x1000000, CRC(3601d568) SHA1(800323e52f5d33b402f84d31850b42c688082d67) )
ROM_END

/****************************************
 ID-0095
 . NGM-095
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 . NGH-095
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( rbff1 ) /* MVS AND AES VERSION */
	ROM_REGION( 0x300000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "095-p1.p1",  0x000000, 0x100000, CRC(63b4d8ae) SHA1(03aa9f6bab6aee685d1b57a52823797704eea845) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "095-p2.sp2", 0x100000, 0x200000, CRC(cc15826e) SHA1(44d6ac6c0ca697a6f367dcfd809b1e1771cb0635) ) /* TC5316200 */

	NEO_SFIX_128K( "095-s1.s1", CRC(b6bf5e08) SHA1(b527355c35ea097f3448676f2ffa65b8e56ae30c) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "095-m1.m1", CRC(653492a7) SHA1(39e511fb9ed5d2135dc8428a31d0baafb2ab36e0) ) /* TC531001 */

	ROM_REGION( 0xc00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "069-v1.v1", 0x000000, 0x400000, CRC(2bdbd4db) SHA1(5f4fecf69c2329d699cbd45829c19303b1e2a80e) ) /* TC5332204 */
	ROM_LOAD( "069-v2.v2", 0x400000, 0x400000, CRC(a698a487) SHA1(11b8bc53bc26a51f4a408e900e3769958625c4ed) ) /* TC5332204 */
	ROM_LOAD( "095-v3.v3", 0x800000, 0x400000, CRC(189d1c6c) SHA1(f0b8cd1ee40ea3feeb2800f0723b451ec8240203) ) /* TC5332201 */

	ROM_REGION( 0x1c00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "069-c1.c1", 0x0000000, 0x400000, CRC(e302f93c) SHA1(d8610b14900b2b8fe691b67ca9b1abb335dbff74) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "069-c2.c2", 0x0000001, 0x400000, CRC(1053a455) SHA1(69501bfac68739e63d798045b812badd251d57b8) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "069-c3.c3", 0x0800000, 0x400000, CRC(1c0fde2f) SHA1(cf6c2ef56c03a861de3b0b6dc0d7c9204d947f9d) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "069-c4.c4", 0x0800001, 0x400000, CRC(a25fc3d0) SHA1(83cb349e2f1032652060b233e741fb893be5af16) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "095-c5.c5", 0x1000000, 0x400000, CRC(8b9b65df) SHA1(e2a7e20855501f240bcd22f5cc92fcb4a9806abe) ) /* Plane 0,1 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "095-c6.c6", 0x1000001, 0x400000, CRC(3e164718) SHA1(53217f938c8964c1ca68a6fd5249c4169a5ac8e6) ) /* Plane 2,3 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "095-c7.c7", 0x1800000, 0x200000, CRC(ca605e12) SHA1(5150b835247fd705bc1dece97d423d9c20a51416) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "095-c8.c8", 0x1800001, 0x200000, CRC(4e6beb6c) SHA1(c0ac7cfc832ace6ad52c58f5da3a8101baead749) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

ROM_START( rbff1a ) /* MVS VERSION */
	/* This is a bug fixed revision applied over the original cart. The original P1 and P2 stayed in the cart and this */
	/* 512k ROM was added to replace the first 512k of P1. */
	ROM_REGION( 0x300000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "095-p1.p1",  0x000000, 0x100000, CRC(63b4d8ae) SHA1(03aa9f6bab6aee685d1b57a52823797704eea845) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "095-p2.sp2", 0x100000, 0x200000, CRC(cc15826e) SHA1(44d6ac6c0ca697a6f367dcfd809b1e1771cb0635) ) /* TC5316200 */
	/* the rom below acts as a patch to the program rom in the cart, replacing the first 512kb */
	ROM_LOAD16_WORD_SWAP( "095-epr.ep1", 0x000000, 0x080000, CRC(be0060a3) SHA1(fa741d34898ad5004a23e280139d1446f1a082c7) ) /* M27C4002 */
	/* P is on eprom, correct chip label unknown */

	NEO_SFIX_128K( "095-s1.s1", CRC(b6bf5e08) SHA1(b527355c35ea097f3448676f2ffa65b8e56ae30c) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "095-m1.m1", CRC(653492a7) SHA1(39e511fb9ed5d2135dc8428a31d0baafb2ab36e0) ) /* TC531001 */

	ROM_REGION( 0xc00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "069-v1.v1", 0x000000, 0x400000, CRC(2bdbd4db) SHA1(5f4fecf69c2329d699cbd45829c19303b1e2a80e) ) /* TC5332204 */
	ROM_LOAD( "069-v2.v2", 0x400000, 0x400000, CRC(a698a487) SHA1(11b8bc53bc26a51f4a408e900e3769958625c4ed) ) /* TC5332204 */
	ROM_LOAD( "095-v3.v3", 0x800000, 0x400000, CRC(189d1c6c) SHA1(f0b8cd1ee40ea3feeb2800f0723b451ec8240203) ) /* TC5332201 */

	ROM_REGION( 0x1c00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "069-c1.c1", 0x0000000, 0x400000, CRC(e302f93c) SHA1(d8610b14900b2b8fe691b67ca9b1abb335dbff74) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "069-c2.c2", 0x0000001, 0x400000, CRC(1053a455) SHA1(69501bfac68739e63d798045b812badd251d57b8) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "069-c3.c3", 0x0800000, 0x400000, CRC(1c0fde2f) SHA1(cf6c2ef56c03a861de3b0b6dc0d7c9204d947f9d) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "069-c4.c4", 0x0800001, 0x400000, CRC(a25fc3d0) SHA1(83cb349e2f1032652060b233e741fb893be5af16) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "095-c5.c5", 0x1000000, 0x400000, CRC(8b9b65df) SHA1(e2a7e20855501f240bcd22f5cc92fcb4a9806abe) ) /* Plane 0,1 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "095-c6.c6", 0x1000001, 0x400000, CRC(3e164718) SHA1(53217f938c8964c1ca68a6fd5249c4169a5ac8e6) ) /* Plane 2,3 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "095-c7.c7", 0x1800000, 0x200000, CRC(ca605e12) SHA1(5150b835247fd705bc1dece97d423d9c20a51416) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "095-c8.c8", 0x1800001, 0x200000, CRC(4e6beb6c) SHA1(c0ac7cfc832ace6ad52c58f5da3a8101baead749) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

ROM_START( rbff1ka ) /* KOREAN VERSION */
	/* This is a bug fixed revision applied over the original cart. The original P1 and P2 stayed in the cart and this */
	/* 512k ROM was added to replace the first 512k of P1. */
	ROM_REGION( 0x300000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "095-p1k.p1", 0x000000, 0x100000, CRC(f705364b) SHA1(2668e15663a2c7ec7396f40e4f5f454505968ce8) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "095-p2.sp2", 0x100000, 0x200000, CRC(cc15826e) SHA1(44d6ac6c0ca697a6f367dcfd809b1e1771cb0635) ) /* TC5316200 */
	/* the rom below acts as a patch to the program rom in the cart, replacing the first 512kb */
	ROM_LOAD16_WORD_SWAP( "095-eprk.ep1", 0x000000, 0x080000, CRC(422093ed) SHA1(1b4cceec9b622d35f1bfdf01e314771b98483348) ) /* M27C4002 */
	/* no label on eprom, correct chip label unknown */

	NEO_SFIX_128K( "095-s1.s1", CRC(b6bf5e08) SHA1(b527355c35ea097f3448676f2ffa65b8e56ae30c) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "095-m1.m1", CRC(653492a7) SHA1(39e511fb9ed5d2135dc8428a31d0baafb2ab36e0) ) /* TC531001 */

	ROM_REGION( 0xc00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "069-v1.v1", 0x000000, 0x400000, CRC(2bdbd4db) SHA1(5f4fecf69c2329d699cbd45829c19303b1e2a80e) ) /* TC5332204 */
	ROM_LOAD( "069-v2.v2", 0x400000, 0x400000, CRC(a698a487) SHA1(11b8bc53bc26a51f4a408e900e3769958625c4ed) ) /* TC5332204 */
	ROM_LOAD( "095-v3.v3", 0x800000, 0x400000, CRC(189d1c6c) SHA1(f0b8cd1ee40ea3feeb2800f0723b451ec8240203) ) /* TC5332201 */

	ROM_REGION( 0x1c00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "069-c1.c1", 0x0000000, 0x400000, CRC(e302f93c) SHA1(d8610b14900b2b8fe691b67ca9b1abb335dbff74) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "069-c2.c2", 0x0000001, 0x400000, CRC(1053a455) SHA1(69501bfac68739e63d798045b812badd251d57b8) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "069-c3.c3", 0x0800000, 0x400000, CRC(1c0fde2f) SHA1(cf6c2ef56c03a861de3b0b6dc0d7c9204d947f9d) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "069-c4.c4", 0x0800001, 0x400000, CRC(a25fc3d0) SHA1(83cb349e2f1032652060b233e741fb893be5af16) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "095-c5.c5", 0x1000000, 0x400000, CRC(8b9b65df) SHA1(e2a7e20855501f240bcd22f5cc92fcb4a9806abe) ) /* Plane 0,1 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "095-c6.c6", 0x1000001, 0x400000, CRC(3e164718) SHA1(53217f938c8964c1ca68a6fd5249c4169a5ac8e6) ) /* Plane 2,3 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "095-c7.c7", 0x1800000, 0x200000, CRC(ca605e12) SHA1(5150b835247fd705bc1dece97d423d9c20a51416) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "095-c8.c8", 0x1800001, 0x200000, CRC(4e6beb6c) SHA1(c0ac7cfc832ace6ad52c58f5da3a8101baead749) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

ROM_START( rbff1k ) /* KOREAN VERSION */
	ROM_REGION( 0x300000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "095-p1k.p1", 0x000000, 0x100000, CRC(f705364b) SHA1(2668e15663a2c7ec7396f40e4f5f454505968ce8) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "095-p2.sp2", 0x100000, 0x200000, CRC(cc15826e) SHA1(44d6ac6c0ca697a6f367dcfd809b1e1771cb0635) ) /* TC5316200 */

	NEO_SFIX_128K( "095-s1.s1", CRC(b6bf5e08) SHA1(b527355c35ea097f3448676f2ffa65b8e56ae30c) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "095-m1.m1", CRC(653492a7) SHA1(39e511fb9ed5d2135dc8428a31d0baafb2ab36e0) ) /* TC531001 */

	ROM_REGION( 0xc00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "069-v1.v1", 0x000000, 0x400000, CRC(2bdbd4db) SHA1(5f4fecf69c2329d699cbd45829c19303b1e2a80e) ) /* TC5332204 */
	ROM_LOAD( "069-v2.v2", 0x400000, 0x400000, CRC(a698a487) SHA1(11b8bc53bc26a51f4a408e900e3769958625c4ed) ) /* TC5332204 */
	ROM_LOAD( "095-v3.v3", 0x800000, 0x400000, CRC(189d1c6c) SHA1(f0b8cd1ee40ea3feeb2800f0723b451ec8240203) ) /* TC5332201 */

	ROM_REGION( 0x1c00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "069-c1.c1", 0x0000000, 0x400000, CRC(e302f93c) SHA1(d8610b14900b2b8fe691b67ca9b1abb335dbff74) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "069-c2.c2", 0x0000001, 0x400000, CRC(1053a455) SHA1(69501bfac68739e63d798045b812badd251d57b8) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "069-c3.c3", 0x0800000, 0x400000, CRC(1c0fde2f) SHA1(cf6c2ef56c03a861de3b0b6dc0d7c9204d947f9d) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "069-c4.c4", 0x0800001, 0x400000, CRC(a25fc3d0) SHA1(83cb349e2f1032652060b233e741fb893be5af16) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "095-c5.c5", 0x1000000, 0x400000, CRC(8b9b65df) SHA1(e2a7e20855501f240bcd22f5cc92fcb4a9806abe) ) /* Plane 0,1 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "095-c6.c6", 0x1000001, 0x400000, CRC(3e164718) SHA1(53217f938c8964c1ca68a6fd5249c4169a5ac8e6) ) /* Plane 2,3 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "095-c7.c7", 0x1800000, 0x200000, CRC(ca605e12) SHA1(5150b835247fd705bc1dece97d423d9c20a51416) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "095-c8.c8", 0x1800001, 0x200000, CRC(4e6beb6c) SHA1(c0ac7cfc832ace6ad52c58f5da3a8101baead749) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

/****************************************
 ID-0096
 . NGM-096
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 . NGH-096
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( aof3 )
	ROM_REGION( 0x300000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "096-p1.p1",  0x000000, 0x100000, CRC(9edb420d) SHA1(150d80707325ece351c72c21c6186cfb5996adba) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "096-p2.sp2", 0x100000, 0x200000, CRC(4d5a2602) SHA1(4c26d6135d2877d9c38169662033e9d0cc24d943) ) /* TC5316200 */

	NEO_SFIX_128K( "096-s1.s1", CRC(cc7fd344) SHA1(2c6846cf8ea61fb192ba181dbccb63594d572c0e) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "096-m1.m1", CRC(cb07b659) SHA1(940b379957c2987d7ab0443cb80c3ff58f6ba559) ) /* TC531001 */

	ROM_REGION( 0x600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "096-v1.v1", 0x000000, 0x200000, CRC(e2c32074) SHA1(69426e7e63fc31a73d1cd056cc9ae6a2c4499407) ) /* TC5316200 */
	ROM_LOAD( "096-v2.v2", 0x200000, 0x200000, CRC(a290eee7) SHA1(e66a98cd9740188bf999992b417f8feef941cede) ) /* TC5316200 */
	ROM_LOAD( "096-v3.v3", 0x400000, 0x200000, CRC(199d12ea) SHA1(a883bf34e685487705a8dafdd0b8db15eb360e80) ) /* TC5316200 */

	ROM_REGION( 0x1c00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "096-c1.c1", 0x0000000, 0x400000, CRC(f17b8d89) SHA1(7180df23f7c7a964b0835fda76970b12f0aa9ea8) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "096-c2.c2", 0x0000001, 0x400000, CRC(3840c508) SHA1(55adc7cd26fec3e4dbd779df6701bc6eaba41b84) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "096-c3.c3", 0x0800000, 0x400000, CRC(55f9ee1e) SHA1(fbe1b7891beae66c5fcbc7e36168dc1b460ede91) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "096-c4.c4", 0x0800001, 0x400000, CRC(585b7e47) SHA1(d50ea91397fc53d86470ff5b493a44d57c010306) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "096-c5.c5", 0x1000000, 0x400000, CRC(c75a753c) SHA1(fc977f8710816a369a5d0d49ee84059380e93fb7) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "096-c6.c6", 0x1000001, 0x400000, CRC(9a9d2f7a) SHA1(a89a713bfcd93974c9acb21ce699d365b08e7e39) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "096-c7.c7", 0x1800000, 0x200000, CRC(51bd8ab2) SHA1(c8def9c64de64571492b5b7e14b794e3c18f1393) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "096-c8.c8", 0x1800001, 0x200000, CRC(9a34f99c) SHA1(fca72d95ec42790a7f1e771a1e25dbc5bec5fc19) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

ROM_START( aof3k ) /* KOREAN VERSION */
	ROM_REGION( 0x300000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "196-p1k.p1", 0x000000, 0x100000, CRC(a0780789) SHA1(83657922a9a3502653ef8cda45b15d9f935aa96a) )
	ROM_LOAD16_WORD_SWAP( "096-p2.sp2", 0x100000, 0x200000, CRC(4d5a2602) SHA1(4c26d6135d2877d9c38169662033e9d0cc24d943) ) /* TC5316200 */

	NEO_SFIX_128K( "096-s1.s1", CRC(cc7fd344) SHA1(2c6846cf8ea61fb192ba181dbccb63594d572c0e) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "096-m1.m1", CRC(cb07b659) SHA1(940b379957c2987d7ab0443cb80c3ff58f6ba559) ) /* TC531001 */

	ROM_REGION( 0x600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "096-v1.v1", 0x000000, 0x200000, CRC(e2c32074) SHA1(69426e7e63fc31a73d1cd056cc9ae6a2c4499407) ) /* TC5316200 */
	ROM_LOAD( "096-v2.v2", 0x200000, 0x200000, CRC(a290eee7) SHA1(e66a98cd9740188bf999992b417f8feef941cede) ) /* TC5316200 */
	ROM_LOAD( "096-v3.v3", 0x400000, 0x200000, CRC(199d12ea) SHA1(a883bf34e685487705a8dafdd0b8db15eb360e80) ) /* TC5316200 */

	ROM_REGION( 0x1c00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "096-c1.c1", 0x0000000, 0x400000, CRC(f17b8d89) SHA1(7180df23f7c7a964b0835fda76970b12f0aa9ea8) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "096-c2.c2", 0x0000001, 0x400000, CRC(3840c508) SHA1(55adc7cd26fec3e4dbd779df6701bc6eaba41b84) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "096-c3.c3", 0x0800000, 0x400000, CRC(55f9ee1e) SHA1(fbe1b7891beae66c5fcbc7e36168dc1b460ede91) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "096-c4.c4", 0x0800001, 0x400000, CRC(585b7e47) SHA1(d50ea91397fc53d86470ff5b493a44d57c010306) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "096-c5.c5", 0x1000000, 0x400000, CRC(c75a753c) SHA1(fc977f8710816a369a5d0d49ee84059380e93fb7) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "096-c6.c6", 0x1000001, 0x400000, CRC(9a9d2f7a) SHA1(a89a713bfcd93974c9acb21ce699d365b08e7e39) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "096-c7.c7", 0x1800000, 0x200000, CRC(51bd8ab2) SHA1(c8def9c64de64571492b5b7e14b794e3c18f1393) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "096-c8.c8", 0x1800001, 0x200000, CRC(9a34f99c) SHA1(fca72d95ec42790a7f1e771a1e25dbc5bec5fc19) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

/****************************************
 ID-0097
 . NGM-097
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 . NGH-097
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( sonicwi3 )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "097-p1.p1", 0x100000, 0x100000, CRC(0547121d) SHA1(e0bb6c614f572b74ba9a9f0d3d5b69fbc91ebc52) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "097-s1.s1", CRC(8dd66743) SHA1(39214bb25a1d5b44a8524010be05bf5a0211981f) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "097-m1.m1", CRC(b20e4291) SHA1(0e891ab53f9fded510295dfc7818bc59b4a9dd97) ) /* TC531001 */

	ROM_REGION( 0x600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "097-v1.v1", 0x000000, 0x400000, CRC(6f885152) SHA1(8175804d5c1420c5d37b733d4a8fa2aa81e59f1b) ) /* TC5332201 */
	ROM_LOAD( "097-v2.v2", 0x400000, 0x200000, CRC(3359e868) SHA1(b7efd9f1a6dab33271fe8356bcc863aeae1d3ed8) ) /* TC5316200 */

	ROM_REGION( 0xc00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "097-c1.c1", 0x000000, 0x400000, CRC(33d0d589) SHA1(fe4aa95555e478ceb2d28fd27d83ee06cd09520c) ) /* Plane 0,1 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "097-c2.c2", 0x000001, 0x400000, CRC(186f8b43) SHA1(f5cced93e21dc841b00ebeaa30786cb0e047bd9a) ) /* Plane 2,3 */ /* TC5332202 */
	ROM_LOAD16_BYTE( "097-c3.c3", 0x800000, 0x200000, CRC(c339fff5) SHA1(58dfd1e30dc0ad3f816a5dbd1cc7e7ccbb792c53) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "097-c4.c4", 0x800001, 0x200000, CRC(84a40c6e) SHA1(061a13fba5fed883e5ee9566cedc208df2511bcf) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

/****************************************
 ID-0098
 Idol Mahjong - final romance 2 (CD only? not confirmed, MVS might exist)
****************************************/


/****************************************
 ID-0099
 Neo Pool Masters
****************************************/


/* ID range from 100 - 199 is used for Korean (some) and Neo Print carts */

/*
    The following ID's are used by Korean releases:

    ID-0122 - Pae Wang Jeon Seol / Legend of a Warrior (Korean censored Samurai Shodown IV)
    ID-0123 - Quiz Salibtamjeong - The Last Count Down (Korean localized Quiz Daisousa Sen)
    ID-0124 - Real Bout Fatal Fury Special / Real Bout Garou Densetsu Special (Korean release)
    ID-0134 - The Last Soldier (Korean release of The Last Blade)
    ID-0140 - Real Bout Fatal Fury 2 - The Newcomers (Korean release)
    ID-0152 - The King of Fighters '99 - Millennium Battle (Korean release)
    ID-0163 - Saulabi Spirits / Jin Saulabi Tu Hon (Korean release of Samurai Shodown II)
    ID-0187 - Fighters Swords (Korean release of Samurai Shodown III)
    ID-0196 - Art of Fighting 3 - The Path of the Warrior (Korean release)
*/


/****************************************
 ID-0200
 . NGM-200
 NEO-MVS PROGBK1 / NEO-MVS CHA256B
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 . NGH-200
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( turfmast )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "200-p1.p1", 0x100000, 0x100000, CRC(28c83048) SHA1(e7ef87e1de21d2bb17ef17bb08657e92363f0e9a) ) /* mask rom TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000)

	NEO_SFIX_128K( "200-s1.s1", CRC(9a5402b2) SHA1(ae1a0b5450869d61b2bb23671c744d3dda8769c4) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "200-m1.m1", CRC(9994ac00) SHA1(7bded797f3b80fd00bcbe451ac0abe6646b19a14) ) /* mask rom TC531001 */

	ROM_REGION( 0x800000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "200-v1.v1", 0x000000, 0x200000, CRC(00fd48d2) SHA1(ddfee09328632e598fd51537b3ae8593219b2111) ) /* mask rom TC5316200 */
	ROM_LOAD( "200-v2.v2", 0x200000, 0x200000, CRC(082acb31) SHA1(2f1c053040e9d50a6d45fd7bea1b96742bae694f) ) /* mask rom TC5316200 */
	ROM_LOAD( "200-v3.v3", 0x400000, 0x200000, CRC(7abca053) SHA1(e229bc0ea82a371d6ee8fd9fe442b0fd141d0a71) ) /* mask rom TC5316200 */
	ROM_LOAD( "200-v4.v4", 0x600000, 0x200000, CRC(6c7b4902) SHA1(d55e0f542d928a9a851133ff26763c8236cbbd4d) ) /* mask rom TC5316200 */

	ROM_REGION( 0x800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "200-c1.c1", 0x000000, 0x400000, CRC(8e7bf41a) SHA1(148eb747f2f4d8e921eb0411c88a636022ceab80) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "200-c2.c2", 0x000001, 0x400000, CRC(5a65a8ce) SHA1(d6c7afe035411f3eacdf6868d36f91572dd593e0) ) /* Plane 2,3 */ /* mask rom TC5332205 */
ROM_END

/****************************************
 ID-0201
 . NGM-201
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 NEO-MVS PROGBK1 / NEO-MVS CHA256B
 . NGH-201
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( mslug )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "201-p1.p1", 0x100000, 0x100000, CRC(08d8daa5) SHA1(b888993dbb7e9f0a28a01d7d2e1da00ef9cf6f38) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "201-s1.s1", CRC(2f55958d) SHA1(550b53628daec9f1e1e11a398854092d90f9505a) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "201-m1.m1", CRC(c28b3253) SHA1(fd75bd15aed30266a8b3775f276f997af57d1c06) ) /* TC531001 */

	ROM_REGION( 0x800000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "201-v1.v1", 0x000000, 0x400000, CRC(23d22ed1) SHA1(cd076928468ad6bcc5f19f88cb843ecb5e660681) ) /* TC5332204 */
	ROM_LOAD( "201-v2.v2", 0x400000, 0x400000, CRC(472cf9db) SHA1(5f79ea9286d22ed208128f9c31ca75552ce08b57) ) /* TC5332204 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "201-c1.c1", 0x000000, 0x400000, CRC(72813676) SHA1(7b045d1a48980cb1a140699011cb1a3d4acdc4d1) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "201-c2.c2", 0x000001, 0x400000, CRC(96f62574) SHA1(cb7254b885989223bba597b8ff0972dfa5957816) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "201-c3.c3", 0x800000, 0x400000, CRC(5121456a) SHA1(0a7a27d603d1bb2520b5570ebf5b34a106e255a6) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "201-c4.c4", 0x800001, 0x400000, CRC(f4ad59a3) SHA1(4e94fda8ee63abf0f92afe08060a488546e5c280) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

/****************************************
  Nazca development cartridge, purpose unknown

  MVS-TEMP (Program ROMs, also a HD64180SCP10, no S1 ROM)
  TENP0 SND (tiny board with only M1 ROM)
****************************************/

ROM_START( mvstemp )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_BYTE( "mvs-ph", 0x00000, 0x10000, CRC(c461c209) SHA1(baeb27b0c4cc05d0a692325687208d47933ae841))
	ROM_LOAD16_BYTE( "mvs-pl", 0x00001, 0x10000, CRC(f82c3595) SHA1(5af628377d445e1494e695bbb45795d65f3a42e7) )

	ROM_REGION( 0x20000, "cslot1:fixed", ROMREGION_ERASE00 )
	// No S1 data (no position for it)

	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) ) // BIOS
	ROM_Y_ZOOM

	NEO_BIOS_AUDIO_64K( "s-800h", CRC(307fffb2) SHA1(0a7a21c2f1a1f245de59d588f20b5ffea522a949) )

	ROM_REGION( 0x200000, "cslot1:hd64180", 0 ) // HD64180SCP10 program, not hooked up
	ROM_LOAD( "mvs64p", 0x000000, 0x10000, CRC(42a1d73a) SHA1(cb82a73f087ed6ca936dbe36a0a85451e63c9c8c) )

	ROM_REGION( 0x117, "cslot1:pld", 0 )
	ROM_LOAD( "ic6.bin",  0x000, 0x117, CRC(7e3da063) SHA1(2778932f8d4c48101c728e2f1906e54f8d73ff90) )
	ROM_LOAD( "ic12.bin", 0x000, 0x117, CRC(b4f3c5ae) SHA1(61a1cda077c6998f0427922b1d229ef05e65e6ee) )

	ROM_REGION( 0x800000, "cslot1:ymsnd:adpcma", ROMREGION_ERASE00 )
	// No V data (no positions for it)

	ROM_REGION( 0x1000000, "cslot1:sprites", ROMREGION_ERASE00 )
	// No C data (no positions for it)
ROM_END

/****************************************
 ID-0202
 . ??M-202
 NEO-MVS PROG 4096 / NEO-MVS CHA 42G-2
****************************************/

ROM_START( puzzledp ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "202-p1.p1", 0x000000, 0x080000, CRC(2b61415b) SHA1(0e3e4faf2fd6e63407425e1ac788003e75aeeb4f) ) /* TC534200 */
	/* also found set with p1 on eprom with sticker; label is 202-P1 */

	NEO_SFIX_128K( "202-s1.s1", CRC(cd19264f) SHA1(531be2305cd56d332fb7a53ab924214ade34a9e8) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "202-m1.m1", CRC(9c0291ea) SHA1(3fa67c62acba79be6b3a98cc1601e45569fa11ae) ) /* TC531001 */

	ROM_REGION( 0x080000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "202-v1.v1", 0x000000, 0x080000, CRC(debeb8fb) SHA1(49a3d3578c087f1a0050168571ef8d1b08c5dc05) ) /* TC534200 */

	ROM_REGION( 0x200000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "202-c1.c1", 0x000000, 0x100000, CRC(cc0095ef) SHA1(3d86f455e6db10a2449b775dc386f1826ba3b62e) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "202-c2.c2", 0x000001, 0x100000, CRC(42371307) SHA1(df794f989e2883634bf7ffeea48d6bc3854529af) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

/****************************************
 ID-0203
 . ADM-010
 NEO-MVS PROGTOP / NEO-MVS CHA42G-3B
 . ADH-010
****************************************/

ROM_START( moshougi )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "203-p1.p1", 0x000000, 0x100000, CRC(7ba70e2d) SHA1(945f472cc3e7706f613c52df18de35c986d166e7) ) /* TC538200 */

	NEO_SFIX_128K( "203-s1.s1", CRC(bfdc8309) SHA1(781337eab932a130b396a6c1080611d6f9c24c6e) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "203-m1.m1", CRC(a602c2c2) SHA1(19fd5d0379244c528b58343f6cbf78b4766fb23d) ) /* TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "203-v1.v1", 0x000000, 0x200000, CRC(baa2b9a5) SHA1(914782b6c81d9a76ce02251575592b0648434ba3) ) /* TC5316200 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "203-c1.c1", 0x000000, 0x200000, CRC(bba9e8c0) SHA1(db89b7275a59ae6104a8308025c7e142a67b947b) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "203-c2.c2", 0x000001, 0x200000, CRC(2574be03) SHA1(198cfd697c623022919ae4118928a7fe30cd6c46) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

/****************************************
 ID-0204
 QP (prototype) 1997 Success
****************************************/

/****************************************
 ID-0205
 Neo-Geo CD Special (CD only)
****************************************/

/****************************************
 ID-0206
 . ??M-206
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 . ??H-206
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( marukodq )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "206-p1.p1", 0x000000, 0x100000, CRC(c33ed21e) SHA1(bffff0d17e587e67672227e60c0ebd3f3a7193e6) ) /* mask rom TC538200 */

	NEO_SFIX_128K( "206-s1.s1", CRC(f0b68780) SHA1(3f60950b14d121a5af3e6a8155ae9832ddc6ec46) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "206-m1.m1", CRC(0e22902e) SHA1(fb8466c342d4abd8bb4cad01c6ceab03f96cdad8) ) /* mask rom TC531001 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "206-v1.v1", 0x000000, 0x200000, CRC(5385eca8) SHA1(1ca171ce74a5885ae8841d0924de21dc0af2214e) ) /* mask rom TC5316200 */
	ROM_LOAD( "206-v2.v2", 0x200000, 0x200000, CRC(f8c55404) SHA1(cecc41e9e08a7ff05b6f62e713fc86a816bf55a2) ) /* mask rom TC5316200 */

	ROM_REGION( 0xa00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "206-c1.c1", 0x000000, 0x400000, CRC(846e4e8e) SHA1(ba9b96340aca7fadaff0e6d484391ddb5c5e7bd4) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "206-c2.c2", 0x000001, 0x400000, CRC(1cba876d) SHA1(3254ceb5a2f76c172930d9889d5d81e093e87628) ) /* Plane 2,3 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "206-c3.c3", 0x800000, 0x100000, CRC(79aa2b48) SHA1(31f94217cd35f48845c74a55256314c16fd26ed7) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "206-c4.c4", 0x800001, 0x100000, CRC(55e1314d) SHA1(fffbc9eb9000ff5b1063af1817de7ea4a267fedd) ) /* Plane 2,3 */ /* mask rom TC538200 */
ROM_END

/****************************************
 ID-0207
 . ??M-207
 NEO-MVS PROGBK1 / NEO-MVS CHA42G-3B
 NEO-MVS PROG 4096 / NEO-MVS CHA42G-3B
****************************************/

ROM_START( neomrdo ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "207-p1.p1", 0x000000, 0x100000, CRC(334ea51e) SHA1(0a642f8565ec6e9587ed767bcf177f4677547162) ) /* TC538200 */

	NEO_SFIX_128K( "207-s1.s1", CRC(6aebafce) SHA1(5db03715fbed62f2ff3cef7f93606f30261c0362) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "207-m1.m1", CRC(b5b74a95) SHA1(7b01f3b87c247cc7472591f8cdcf0ae8065e31c6) ) /* TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "207-v1.v1", 0x000000, 0x200000, CRC(4143c052) SHA1(561b19bc8811b80f2f42ffc0b5df27132696470a) ) /* TC5316200 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "207-c1.c1", 0x000000, 0x200000, CRC(c7541b9d) SHA1(25ca1a2b14cc2648d8dbe432cbd1396017af822c) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "207-c2.c2", 0x000001, 0x200000, CRC(f57166d2) SHA1(bf3aa47d17156485c2177fb63cba093f050abb98) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

/****************************************
 ID-0208
 . ??M-208
 NEO-MVS PROGBK1 / NEO-MVS CHA256B
****************************************/

ROM_START( sdodgeb ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "208-p1.p1", 0x100000, 0x100000, CRC(127f3d32) SHA1(18e77b79b1197a89371533ef9b1e4d682c44d875) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "208-s1.s1", CRC(64abd6b3) SHA1(0315d724e4d83a44ce84c531ff9b8c398363c039) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "208-m1.m1", CRC(0a5f3325) SHA1(04e0236df478a5452654c823dcb42fea65b6a718) ) /* TC531001 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "208-v1.v1", 0x000000, 0x400000, CRC(e7899a24) SHA1(3e75b449898fee73fbacf58d70e3a460b9e0c573) ) /* TC5332204 */

	ROM_REGION( 0x0c00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "208-c1.c1", 0x0000000, 0x400000, CRC(93d8619b) SHA1(6588cb67e38722d5843fb29943d92e3905101aff) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "208-c2.c2", 0x0000001, 0x400000, CRC(1c737bb6) SHA1(8e341989981a713e61dfed8bde9a6459583ef46d) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "208-c3.c3", 0x0800000, 0x200000, CRC(14cb1703) SHA1(a46acec03c1b2351fe36810628f02b7c848d13db) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "208-c4.c4", 0x0800001, 0x200000, CRC(c7165f19) SHA1(221f03de893dca0e5305fa17aa94f96c67713818) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

/****************************************
 ID-0209
 . ??M-209
 NEO-MVS PROGBK1 / NEO-MVS CHA256
****************************************/

ROM_START( goalx3 ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "209-p1.p1", 0x100000, 0x100000, CRC(2a019a79) SHA1(422a639e74284fef2e53e1b49cf8803b0a7e80c6) ) /* mask rom TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "209-s1.s1", CRC(c0eaad86) SHA1(99412093c9707d51817893971e73fb8469cdc9d0) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "209-m1.m1", CRC(cd758325) SHA1(b51eac634fc646c07210dff993018ad9ebabd3f9) ) /* mask rom TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "209-v1.v1", 0x000000, 0x200000, CRC(ef214212) SHA1(3e05ccaa2d06decb18b379b96f900c0e6b39ce70) ) /* mask rom TC5316200 */

	ROM_REGION( 0xa00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "209-c1.c1", 0x000000, 0x400000, CRC(b49d980e) SHA1(722d10074f16fa7f14c71270f43fdab427b85e2b) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "209-c2.c2", 0x000001, 0x400000, CRC(5649b015) SHA1(9c9674f3841e6becd3b8e63bae9b9df45ac9f11e) ) /* Plane 2,3 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "209-c3.c3", 0x800000, 0x100000, CRC(5f91bace) SHA1(3864be27dce6d8f8828d3bf09bfc8116116a2b56) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "209-c4.c4", 0x800001, 0x100000, CRC(1e9f76f2) SHA1(b57fdc226bfe328b8848127fb4292295f1287bf6) ) /* Plane 2,3 */ /* mask rom TC538200 */
ROM_END

/****************************************
 ID-0210
 Karate Ninja Sho (prototype) 1995 Yumekobo
****************************************/

/****************************************
 ID-0211
 Oshidashi Zintrick (CD only? not confirmed, MVS might exist) 1996 SNK/ADK
****************************************/

/****************************************
 ID-0212
 . ADM-011
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 . ADH-011
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( overtop )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "212-p1.p1", 0x100000, 0x100000, CRC(16c063a9) SHA1(5432869f830eed816ee5ed71c7fd39f749d15619) ) /* mask rom TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "212-s1.s1", CRC(481d3ddc) SHA1(7b0df3fc5b19f282abfd0eb5a4c6ed836a536ece) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "212-m1.m1", CRC(fcab6191) SHA1(488b8310b0957f0012fe50f73641b606f6ac4a57) ) /* mask rom TC531001 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "212-v1.v1", 0x000000, 0x400000, CRC(013d4ef9) SHA1(438a697c44525bdf78b54432c4f7217ab5667047) ) /* mask rom TC5332204 */

	ROM_REGION( 0x1400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "212-c1.c1", 0x0000000, 0x400000, CRC(50f43087) SHA1(e5a8c914ef8e77c7a29bffdeb18f1877b5c2fc7d) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "212-c2.c2", 0x0000001, 0x400000, CRC(a5b39807) SHA1(e98e82cf99576cb48cc5e8dc655b7e9a428c2843) ) /* Plane 2,3 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "212-c3.c3", 0x0800000, 0x400000, CRC(9252ea02) SHA1(269066e0f893d3e8e7c308528026a486c2b023a2) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "212-c4.c4", 0x0800001, 0x400000, CRC(5f41a699) SHA1(abbb162658e06a37db8475b659ece7e1270ebb49) ) /* Plane 2,3 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "212-c5.c5", 0x1000000, 0x200000, CRC(fc858bef) SHA1(0031def13e7cf4a465a1eca7aa0d13d1b21427e2) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "212-c6.c6", 0x1000001, 0x200000, CRC(0589c15e) SHA1(b1167caf7cb61f3e05a5d342290bfe00e02e9d38) ) /* Plane 2,3 */ /* mask rom TC5316200 */
ROM_END

/****************************************
 ID-0213
 . ??M-213
 NEO-MVS PROGBK1 / NEO-MVS CHA256B
****************************************/

ROM_START( neodrift ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "213-p1.p1", 0x100000, 0x100000, CRC(e397d798) SHA1(10f459111db4bab7aaa63ca47e83304a84300812) ) /* mask rom TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000)

	NEO_SFIX_128K( "213-s1.s1", CRC(b76b61bc) SHA1(5fdb407d16ab9e33c4f26ee09ff70891ae1d2bd0) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "213-m1.m1", CRC(200045f1) SHA1(7a6cd1c8d4447ea260d7ff4520c676b8d685f2e4) ) /* mask rom TC531001*/

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "213-v1.v1", 0x000000, 0x200000, CRC(a421c076) SHA1(129f05c1a28a6493442f47a79c2d3577a1a43ef5) ) /* mask rom TC5316200 */
	ROM_LOAD( "213-v2.v2", 0x200000, 0x200000, CRC(233c7dd9) SHA1(be7f980aa83831b6605aaaf4ec904180bb96c935) ) /* mask rom TC5316200 */

	ROM_REGION( 0x800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "213-c1.c1", 0x000000, 0x400000, CRC(3edc8bd3) SHA1(71dcba9afd3b08ebfa13294644dcb365c2740780) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "213-c2.c2", 0x000001, 0x400000, CRC(46ae5f16) SHA1(a01310632734e776e889af6a531063cb1661c33a) ) /* Plane 2,3 */ /* mask rom TC5332205 */
ROM_END

/****************************************
 ID-0214
 . NGM-214
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 NEO-MVS PROGSS3 / NEO-MVS CHA256
 . NGH-214
 NEO-AEG PROGBK1Y / NEO-GEO AEG CHA256RY
****************************************/

ROM_START( kof96 ) /* MVS VERSION */
	ROM_REGION( 0x300000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "214-p1.p1",  0x000000, 0x100000, CRC(52755d74) SHA1(4232d627f1d2e6ea9fc8cf01571d77d4d5b8a1bb) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "214-p2.sp2", 0x100000, 0x200000, CRC(002ccb73) SHA1(3ae8df682c75027ca82db25491021eeba00a267e) ) /* TC5316200 */

	NEO_SFIX_128K( "214-s1.s1", CRC(1254cbdb) SHA1(fce5cf42588298711a3633e9c9c1d4dcb723ac76) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "214-m1.m1", CRC(dabc427c) SHA1(b76722ed142ee7addceb4757424870dbd003e8b3) ) /* TC531001 */

	ROM_REGION( 0xa00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "214-v1.v1", 0x000000, 0x400000, CRC(63f7b045) SHA1(1353715f1a8476dca6f8031d9e7a401eacab8159) ) /* TC5332204 */
	ROM_LOAD( "214-v2.v2", 0x400000, 0x400000, CRC(25929059) SHA1(6a721c4cb8f8dc772774023877d4a9f50d5a9e31) ) /* TC5332204 */
	ROM_LOAD( "214-v3.v3", 0x800000, 0x200000, CRC(92a2257d) SHA1(5064aec78fa0d104e5dd5869b95382aa170214ee) ) /* TC5316200 */

	ROM_REGION( 0x2000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "214-c1.c1", 0x0000000, 0x400000, CRC(7ecf4aa2) SHA1(f773c4c1f05d58dd37e7bb2ac1d1e0ec43998a71) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c2.c2", 0x0000001, 0x400000, CRC(05b54f37) SHA1(cc31653fe4cb05201fba234e080cb9c7a7592b1b) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c3.c3", 0x0800000, 0x400000, CRC(64989a65) SHA1(e6f3749d43be0afa9dad7b085cb782ba694252ca) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c4.c4", 0x0800001, 0x400000, CRC(afbea515) SHA1(ae875052728de33174827705646bd14cf3937b5c) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c5.c5", 0x1000000, 0x400000, CRC(2a3bbd26) SHA1(7c1a7e50a10a1b082e0d0d515c34135ee9f995ac) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c6.c6", 0x1000001, 0x400000, CRC(44d30dc7) SHA1(c8ae001e37224b55d9e4a4d99f6578b4f6eb055f) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c7.c7", 0x1800000, 0x400000, CRC(3687331b) SHA1(2be95caab76d7af51674f93884330ba73a6053e4) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c8.c8", 0x1800001, 0x400000, CRC(fa1461ad) SHA1(6c71a7f08e4044214223a6bf80984582ab5e0328) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

ROM_START( kof96a ) /* MVS VERSION */
	ROM_REGION( 0x300000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "blue.ep1",   0x000000, 0x080000, CRC(a6101486) SHA1(51357c216cc8e52fc4ecf5b2db4814e87d016132) ) // 27C4096DC, no label
	ROM_LOAD16_WORD_SWAP( "red.ep2",    0x080000, 0x080000, CRC(6abc7ae5) SHA1(1bcbed38ef1934a0701d1c95b30ffe63316f92a8) ) // "
	ROM_LOAD16_WORD_SWAP( "orange.ep3", 0x100000, 0x080000, CRC(a588dff4) SHA1(32d30bdf9b7bc9201b3930f5a0f126c79af97f3d) ) // "
	ROM_LOAD16_WORD_SWAP( "yellow.ep4", 0x180000, 0x080000, CRC(6d6f17eb) SHA1(51f61c9a4215584493cf1dc37c4e307e1216626c) ) // "
	ROM_LOAD16_WORD_SWAP( "214-p5.p5",  0x200000, 0x100000, CRC(9f3c6bc9) SHA1(90370a2ad42421a533182569e63ab81822e37878) ) // TC5316200

	NEO_SFIX_128K( "214-s1.s1", CRC(1254cbdb) SHA1(fce5cf42588298711a3633e9c9c1d4dcb723ac76) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "214-m1.m1", CRC(dabc427c) SHA1(b76722ed142ee7addceb4757424870dbd003e8b3) ) /* TC531001 */

	ROM_REGION( 0xa00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "214-v1.v1", 0x000000, 0x400000, CRC(63f7b045) SHA1(1353715f1a8476dca6f8031d9e7a401eacab8159) ) /* TC5332204 */
	ROM_LOAD( "214-v2.v2", 0x400000, 0x400000, CRC(25929059) SHA1(6a721c4cb8f8dc772774023877d4a9f50d5a9e31) ) /* TC5332204 */
	ROM_LOAD( "214-v3.v3", 0x800000, 0x200000, CRC(92a2257d) SHA1(5064aec78fa0d104e5dd5869b95382aa170214ee) ) /* TC5316200 */

	ROM_REGION( 0x2000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "214-c1.c1", 0x0000000, 0x400000, CRC(7ecf4aa2) SHA1(f773c4c1f05d58dd37e7bb2ac1d1e0ec43998a71) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c2.c2", 0x0000001, 0x400000, CRC(05b54f37) SHA1(cc31653fe4cb05201fba234e080cb9c7a7592b1b) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c3.c3", 0x0800000, 0x400000, CRC(64989a65) SHA1(e6f3749d43be0afa9dad7b085cb782ba694252ca) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c4.c4", 0x0800001, 0x400000, CRC(afbea515) SHA1(ae875052728de33174827705646bd14cf3937b5c) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c5.c5", 0x1000000, 0x400000, CRC(2a3bbd26) SHA1(7c1a7e50a10a1b082e0d0d515c34135ee9f995ac) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c6.c6", 0x1000001, 0x400000, CRC(44d30dc7) SHA1(c8ae001e37224b55d9e4a4d99f6578b4f6eb055f) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c7.c7", 0x1800000, 0x400000, CRC(3687331b) SHA1(2be95caab76d7af51674f93884330ba73a6053e4) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c8.c8", 0x1800001, 0x400000, CRC(fa1461ad) SHA1(6c71a7f08e4044214223a6bf80984582ab5e0328) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

ROM_START( kof96h ) /* AES VERSION */
	ROM_REGION( 0x300000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "214-pg1.p1", 0x000000, 0x100000, CRC(bd3757c9) SHA1(35392a044117e46c088ff0fdd07d69a3faa4f96e) ) /* mask rom TC538200 */
	ROM_LOAD16_WORD_SWAP( "214-p2.sp2", 0x100000, 0x200000, CRC(002ccb73) SHA1(3ae8df682c75027ca82db25491021eeba00a267e) ) /* TC5316200 */

	NEO_SFIX_128K( "214-s1.s1", CRC(1254cbdb) SHA1(fce5cf42588298711a3633e9c9c1d4dcb723ac76) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "214-m1.m1", CRC(dabc427c) SHA1(b76722ed142ee7addceb4757424870dbd003e8b3) ) /* TC531001 */

	ROM_REGION( 0xa00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "214-v1.v1", 0x000000, 0x400000, CRC(63f7b045) SHA1(1353715f1a8476dca6f8031d9e7a401eacab8159) ) /* TC5332204 */
	ROM_LOAD( "214-v2.v2", 0x400000, 0x400000, CRC(25929059) SHA1(6a721c4cb8f8dc772774023877d4a9f50d5a9e31) ) /* TC5332204 */
	ROM_LOAD( "214-v3.v3", 0x800000, 0x200000, CRC(92a2257d) SHA1(5064aec78fa0d104e5dd5869b95382aa170214ee) ) /* TC5316200 */

	ROM_REGION( 0x2000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "214-c1.c1", 0x0000000, 0x400000, CRC(7ecf4aa2) SHA1(f773c4c1f05d58dd37e7bb2ac1d1e0ec43998a71) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c2.c2", 0x0000001, 0x400000, CRC(05b54f37) SHA1(cc31653fe4cb05201fba234e080cb9c7a7592b1b) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c3.c3", 0x0800000, 0x400000, CRC(64989a65) SHA1(e6f3749d43be0afa9dad7b085cb782ba694252ca) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c4.c4", 0x0800001, 0x400000, CRC(afbea515) SHA1(ae875052728de33174827705646bd14cf3937b5c) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c5.c5", 0x1000000, 0x400000, CRC(2a3bbd26) SHA1(7c1a7e50a10a1b082e0d0d515c34135ee9f995ac) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c6.c6", 0x1000001, 0x400000, CRC(44d30dc7) SHA1(c8ae001e37224b55d9e4a4d99f6578b4f6eb055f) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c7.c7", 0x1800000, 0x400000, CRC(3687331b) SHA1(2be95caab76d7af51674f93884330ba73a6053e4) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "214-c8.c8", 0x1800001, 0x400000, CRC(fa1461ad) SHA1(6c71a7f08e4044214223a6bf80984582ab5e0328) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

/****************************************
 ID-0215
 . NGM-215
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 . NGH-215
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( ssideki4 )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "215-p1.p1", 0x100000, 0x100000, CRC(519b4ba3) SHA1(5aa59514b23aa663f2c4014ee94a31e9f59151de) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "215-s1.s1", CRC(f0fe5c36) SHA1(b7badd6d2ac3788ce5cace1fcf5cdad14734e4e6) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "215-m1.m1", CRC(a932081d) SHA1(376a45e19edb780ac8798c41ae2260c8a8a4bba8) ) /* TC531001 */

	ROM_REGION( 0x600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "215-v1.v1", 0x000000, 0x400000, CRC(877d1409) SHA1(77c58ebffe677ea6369c964ec7975b11df512fa1) ) /* TC5332204 */
	ROM_LOAD( "215-v2.v2", 0x400000, 0x200000, CRC(1bfa218b) SHA1(344836a578bde3c0ab59b58c8734f868e7403c26) ) /* TC5316200 */

	ROM_REGION( 0x1400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "215-c1.c1", 0x0000000, 0x400000, CRC(8ff444f5) SHA1(e2dc52d09512cb378df96ddf45435f9bcbbe9947) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "215-c2.c2", 0x0000001, 0x400000, CRC(5b155037) SHA1(68900c0fdcd35c9f38e0effdf27e1dbd3c53daf8) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "215-c3.c3", 0x0800000, 0x400000, CRC(456a073a) SHA1(3488013f371012eab4e788e1525c81260e0b7080) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "215-c4.c4", 0x0800001, 0x400000, CRC(43c182e1) SHA1(343f034c65ca498b437e22e06a866a5daf3b9602) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "215-c5.c5", 0x1000000, 0x200000, CRC(0c6f97ec) SHA1(b8d297f0ba2b04404eb0f7c6673ecc206fadae0c) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "215-c6.c6", 0x1000001, 0x200000, CRC(329c5e1b) SHA1(015c36b8d3efab9b4647f110ecb5c118a9c80f43) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

/****************************************
 ID-0216
 . ??M-216
 NEO-MVS PROGTOP / NEO-MVS CHA256
 . ??H-216
 NEO-AEG PROGTOP2Y / NEO-AEG CHA256BY
 NEO-AEG PROGTOP2Y / NEO-AEG CHA256[B]
****************************************/

ROM_START( kizuna )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "216-p1.p1", 0x100000, 0x100000, CRC(75d2b3de) SHA1(ee778656c26828935ee2a2bfd0ce5a22aa681c10) ) /* mask rom TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "216-s1.s1", CRC(efdc72d7) SHA1(be37cbf1852e2e4c907cc799b754b538544b6703) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "216-m1.m1", CRC(1b096820) SHA1(72852e78c620038f8dafde5e54e02e418c31be9c) ) /* mask rom TC531001 */

	ROM_REGION( 0x800000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "059-v1.v1", 0x000000, 0x200000, CRC(530c50fd) SHA1(29401cee7f7d2c199c7cb58092e86b28205e81ad) ) /* TC5316200 */
	ROM_LOAD( "216-v2.v2", 0x200000, 0x200000, CRC(03667a8d) SHA1(3b0475e553a49f8788f32b0c84f82645cc6b4273) ) /* mask rom TC5316200 */
	ROM_LOAD( "059-v3.v3", 0x400000, 0x200000, CRC(7038c2f9) SHA1(c1d6f86b24feba03fe009b58199d2eeabe572f4e) ) /* TC5316200 */
	ROM_LOAD( "216-v4.v4", 0x600000, 0x200000, CRC(31b99bd6) SHA1(5871751f8e9e6b98337472c22b5e1c7ede0a9311) ) /* mask rom TC5316200 */

	ROM_REGION( 0x1c00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "059-c1.c1", 0x0000000, 0x200000, CRC(763ba611) SHA1(d3262e0332c894ee149c5963f882cc5e5562ee57) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "059-c2.c2", 0x0000001, 0x200000, CRC(e05e8ca6) SHA1(986a9b16ff92bc101ab567d2d01348e093abea9a) ) /* Plane 2,3 */ /* TC5316200 */
	/* 400000-7fffff empty */
	ROM_LOAD16_BYTE( "216-c3.c3", 0x0800000, 0x400000, CRC(665c9f16) SHA1(7ec781a49a462f395b450460b29493f55134eac2) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "216-c4.c4", 0x0800001, 0x400000, CRC(7f5d03db) SHA1(365ed266c121f4df0bb76898955a8ae0e668a216) ) /* Plane 2,3 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "059-c5.c5", 0x1000000, 0x200000, CRC(59013f9e) SHA1(5bf48fcc450da72a8c4685f6e3887e67eae49988) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "059-c6.c6", 0x1000001, 0x200000, CRC(1c8d5def) SHA1(475d89a5c4922a9f6bd756d23c2624d57b6e9d62) ) /* Plane 2,3 */ /* TC5316200 */
	/* 1400000-17fffff empty */
	ROM_LOAD16_BYTE( "059-c7.c7", 0x1800000, 0x200000, CRC(c88f7035) SHA1(c29a428b741f4fe7b71a3bc23c87925b6bc1ca8f) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "059-c8.c8", 0x1800001, 0x200000, CRC(484ce3ba) SHA1(4f21ed20ce6e2b67e2b079404599310c94f591ff) ) /* Plane 2,3 */ /* TC538200 */
ROM_END


ROM_START( kizuna4p ) /* same cartridge as kizuna - 4-player mode is enabled by FTC1B JAMMA splitter board that plugs into MV-1B/MV-1C */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "216-p1.p1", 0x100000, 0x100000, CRC(75d2b3de) SHA1(ee778656c26828935ee2a2bfd0ce5a22aa681c10) ) /* mask rom TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "216-s1.s1", CRC(efdc72d7) SHA1(be37cbf1852e2e4c907cc799b754b538544b6703) ) /* mask rom TC531000 */

	ROM_REGION16_BE( 0x80000, "mainbios", 0 )
	/* these two BIOSes are the only ones we have that are compatible with the 4-player extension board */
	ROM_SYSTEM_BIOS( 0, "asia", "NEO-MVH MV1C" )
	ROM_LOAD16_WORD_SWAP_BIOS( 0, "sp-45.sp1",0x00000, 0x080000, CRC(03cc9f6a) SHA1(cdf1f49e3ff2bac528c21ed28449cf35b7957dc1) )
	ROM_SYSTEM_BIOS( 1, "japan", "Japan MVS (J3)" )
	ROM_LOAD16_WORD_SWAP_BIOS( 1, "japan-j3.bin",0x00000, 0x020000, CRC(dff6d41f) SHA1(e92910e20092577a4523a6b39d578a71d4de7085) )

	ROM_REGION( 0x30000, "cslot1:audiocpu", 0 )
	ROM_LOAD( "216-m1.m1", 0x00000, 0x20000, CRC(1b096820) SHA1(72852e78c620038f8dafde5e54e02e418c31be9c) ) /* mask rom TC531001 */
	ROM_RELOAD( 0x10000, 0x20000 )

	ROM_REGION( 0x800000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "059-v1.v1", 0x000000, 0x200000, CRC(530c50fd) SHA1(29401cee7f7d2c199c7cb58092e86b28205e81ad) ) /* TC5316200 */
	ROM_LOAD( "216-v2.v2", 0x200000, 0x200000, CRC(03667a8d) SHA1(3b0475e553a49f8788f32b0c84f82645cc6b4273) ) /* mask rom TC5316200 */
	ROM_LOAD( "059-v3.v3", 0x400000, 0x200000, CRC(7038c2f9) SHA1(c1d6f86b24feba03fe009b58199d2eeabe572f4e) ) /* TC5316200 */
	ROM_LOAD( "216-v4.v4", 0x600000, 0x200000, CRC(31b99bd6) SHA1(5871751f8e9e6b98337472c22b5e1c7ede0a9311) ) /* mask rom TC5316200 */

	ROM_REGION( 0x1c00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "059-c1.c1", 0x0000000, 0x200000, CRC(763ba611) SHA1(d3262e0332c894ee149c5963f882cc5e5562ee57) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "059-c2.c2", 0x0000001, 0x200000, CRC(e05e8ca6) SHA1(986a9b16ff92bc101ab567d2d01348e093abea9a) ) /* Plane 2,3 */ /* TC5316200 */
	/* 400000-7fffff empty */
	ROM_LOAD16_BYTE( "216-c3.c3", 0x0800000, 0x400000, CRC(665c9f16) SHA1(7ec781a49a462f395b450460b29493f55134eac2) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "216-c4.c4", 0x0800001, 0x400000, CRC(7f5d03db) SHA1(365ed266c121f4df0bb76898955a8ae0e668a216) ) /* Plane 2,3 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "059-c5.c5", 0x1000000, 0x200000, CRC(59013f9e) SHA1(5bf48fcc450da72a8c4685f6e3887e67eae49988) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "059-c6.c6", 0x1000001, 0x200000, CRC(1c8d5def) SHA1(475d89a5c4922a9f6bd756d23c2624d57b6e9d62) ) /* Plane 2,3 */ /* TC5316200 */
	/* 1400000-17fffff empty */
	ROM_LOAD16_BYTE( "059-c7.c7", 0x1800000, 0x200000, CRC(c88f7035) SHA1(c29a428b741f4fe7b71a3bc23c87925b6bc1ca8f) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "059-c8.c8", 0x1800001, 0x200000, CRC(484ce3ba) SHA1(4f21ed20ce6e2b67e2b079404599310c94f591ff) ) /* Plane 2,3 */ /* TC538200 */
ROM_END


/****************************************
 ID-0217
 . ADM-012
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 . ADH-012
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( ninjamas )
	ROM_REGION( 0x300000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "217-p1.p1",  0x000000, 0x100000, CRC(3e97ed69) SHA1(336bcae375a5109945d11356503bf0d9f4a9a50a) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "217-p2.sp2", 0x100000, 0x200000, CRC(191fca88) SHA1(e318e5931704779bbe461719a5eeeba89bd83a5d) ) /* TC5316200 */

	NEO_SFIX_128K( "217-s1.s1", CRC(8ff782f0) SHA1(90099c154357042ba658d4ef6abe4d9335bb7172) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "217-m1.m1", CRC(d00fb2af) SHA1(6bcaa52e1641cc24288e1f22f4dc98e8d8921b90) ) /* TC531001 */

	ROM_REGION( 0x600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "217-v1.v1", 0x000000, 0x400000, CRC(1c34e013) SHA1(5368e413d2188c4fd063b6bb7d5f498ff83ea812) ) /* TC5332204 */
	ROM_LOAD( "217-v2.v2", 0x400000, 0x200000, CRC(22f1c681) SHA1(09da03b2e63d180e55173ff25e8735c4162f027b) ) /* TC5316200 */

	ROM_REGION( 0x2000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "217-c1.c1", 0x0000000, 0x400000, CRC(5fe97bc4) SHA1(d76c955d83baa2b9fd24222a9b2852947b7b92f0) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "217-c2.c2", 0x0000001, 0x400000, CRC(886e0d66) SHA1(d407e1525e4ebe996e14f6e5c0396a10f736a50d) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "217-c3.c3", 0x0800000, 0x400000, CRC(59e8525f) SHA1(19f602c71545d6c021dc72e112d3a8b8efe7a9b7) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "217-c4.c4", 0x0800001, 0x400000, CRC(8521add2) SHA1(0d1a6f2979302c4c282e31ff334d2d887aec74f7) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "217-c5.c5", 0x1000000, 0x400000, CRC(fb1896e5) SHA1(777a8caa9ebdbddf89e3d5ab650c94a55228ce54) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "217-c6.c6", 0x1000001, 0x400000, CRC(1c98c54b) SHA1(cb1cad161d9b9f2f5a7cf8ae4d6d35b51acf90f5) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "217-c7.c7", 0x1800000, 0x400000, CRC(8b0ede2e) SHA1(ea632ac98291ddac95441b7fe2349974b2da8a42) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "217-c8.c8", 0x1800001, 0x400000, CRC(a085bb61) SHA1(6a3e9e6ba96072b8849b407f2b24103dc0852259) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

/****************************************
 ID-0218
 . NGM-218
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 . NGH-218
****************************************/

ROM_START( ragnagrd )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "218-p1.p1", 0x100000, 0x100000, CRC(ca372303) SHA1(67991e4fef9b36bc7d909810eebb857ac2f906f1) ) /* mask rom TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "218-s1.s1", CRC(7d402f9a) SHA1(59ec29d03e62e7a8bef689a124a9164f43b2ace1) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "218-m1.m1", CRC(17028bcf) SHA1(7a4e8f33ce9b41beac2152b8f6003f247699e2e1) ) /* mask rom TC531001 */

	ROM_REGION( 0x800000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "218-v1.v1", 0x000000, 0x400000, CRC(61eee7f4) SHA1(5b11b1a0b1b74dfbc2998cbda9f8f7a5e9059957) ) /* mask rom TC5332204 */
	ROM_LOAD( "218-v2.v2", 0x400000, 0x400000, CRC(6104e20b) SHA1(18e8aae3e51e141977d523a10e737ff68fe81910) ) /* mask rom TC5332204 */

	ROM_REGION( 0x2000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "218-c1.c1", 0x0000000, 0x400000, CRC(c31500a4) SHA1(cc82100038988872721028044ed2e9764bcc2fb0) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "218-c2.c2", 0x0000001, 0x400000, CRC(98aba1f9) SHA1(121276c569967e501d8e1b83747f1bdebff612ea) ) /* Plane 2,3 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "218-c3.c3", 0x0800000, 0x400000, CRC(833c163a) SHA1(b7e5356bbd9efab67fedb5bc671ba8bbd661fe0f) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "218-c4.c4", 0x0800001, 0x400000, CRC(c1a30f69) SHA1(f87ddda4695abcd14f5c2d4b7d41f72ad5b064cc) ) /* Plane 2,3 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "218-c5.c5", 0x1000000, 0x400000, CRC(6b6de0ff) SHA1(1abb24cb407258235f4a572cf101d0774823040b) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "218-c6.c6", 0x1000001, 0x400000, CRC(94beefcf) SHA1(d2ff0bac325c9c823dba68bd4f281b3b9f8f68e7) ) /* Plane 2,3 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "218-c7.c7", 0x1800000, 0x400000, CRC(de6f9b28) SHA1(455adb6bb986af8a00d7f32b7f4f3715fc3007f6) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "218-c8.c8", 0x1800001, 0x400000, CRC(d9b311f6) SHA1(ba61a7ab3f08bb7348ad6cd01e5d29ca5ee75074) ) /* Plane 2,3 */ /* mask rom TC5332205 */
ROM_END

/****************************************
 ID-0219
 . NGM-219
 NEO-MVS PROGBK1 / NEO-MVS CHA256
****************************************/

ROM_START( pgoal ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "219-p1.p1", 0x100000, 0x100000, CRC(6af0e574) SHA1(c3f0fed0d942e48c99c80b1713f271c033ce0f4f) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "219-s1.s1", CRC(002f3c88) SHA1(a8a5bbc5397c8ae9858e38997ebdc713b7b4f50a) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "219-m1.m1", CRC(958efdc8) SHA1(aacc6056b1ff48cde8f241a11a27473cfb4b4aa3) ) /* TC531001 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "219-v1.v1", 0x000000, 0x400000, CRC(d0ae33d9) SHA1(cb21a91184d9d84ff25ca86c00dcadfc210272a8) ) /* TC5332204 */

	ROM_REGION( 0xc00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "219-c1.c1", 0x0000000, 0x400000, CRC(67fec4dc) SHA1(b99767972a2a4fce2b704df8d08e6b092665a696) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "219-c2.c2", 0x0000001, 0x400000, CRC(86ed01f2) SHA1(9d7d1493946e8fbbd572503d2362b0156c023b76) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "219-c3.c3", 0x0800000, 0x200000, CRC(5fdad0a5) SHA1(56f6d2a7224aa4e82a1858079f918e85cadbd6c2) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "219-c4.c4", 0x0800001, 0x200000, CRC(f57b4a1c) SHA1(875ca69afbc5304ec23f4bc9186abe92f477f6c8) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

/****************************************
 ID-0220
 Choutetsu Brikin'ger - Iron clad (prototype) 1996 Saurus
****************************************/

ROM_START( ironclad ) /* Prototype - crcs should match the ones of the unreleased dump. */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "proto_220-p1.p1", 0x100000, 0x100000, CRC(62a942c6) SHA1(12aaa7d9bd84328d1bf4610e056b5c57d0252537) )
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "proto_220-s1.s1", CRC(372fe217) SHA1(493433e682f519bf647e1481c8bdd3a980830ffb) )

	NEO_BIOS_AUDIO_128K( "proto_220-m1.m1", CRC(3a08bb63) SHA1(d8fbbf42a006ccafc3cd99808d28c82dbaac4590) )

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "proto_220-v1.v1", 0x000000, 0x400000, CRC(8f30a215) SHA1(0ee866a468c4c3608d55df2b5cb9243c8016d77c) )

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "proto_220-c1.c1", 0x000000, 0x400000, CRC(9aa2b7dc) SHA1(6b3dff292c86f949890b1f8201bc5278f38c2668) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "proto_220-c2.c2", 0x000001, 0x400000, CRC(8a2ad708) SHA1(9568ac4cc0552e7fd3e50d3cd8d9f0f4fe7df1d4) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "proto_220-c3.c3", 0x800000, 0x400000, CRC(d67fb15a) SHA1(842971aeaf3c92e70f7c653bbf29058bc60f5b71) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "proto_220-c4.c4", 0x800001, 0x400000, CRC(e73ea38b) SHA1(27138d588e61e86c292f12d16e36c3681075c607) ) /* Plane 2,3 */
ROM_END

ROM_START( ironclado ) /* Prototype - bootleg/hack based on later release. */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "proto_220-p1o.p1", 0x100000, 0x100000, CRC(ce37e3a0) SHA1(488f95fa15f56eea6666dda13d96ec29dba18e19) )
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "proto_220-s1.s1", CRC(372fe217) SHA1(493433e682f519bf647e1481c8bdd3a980830ffb) )

	NEO_BIOS_AUDIO_128K( "proto_220-m1.m1", CRC(3a08bb63) SHA1(d8fbbf42a006ccafc3cd99808d28c82dbaac4590) )

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "proto_220-v1.v1", 0x000000, 0x400000, CRC(8f30a215) SHA1(0ee866a468c4c3608d55df2b5cb9243c8016d77c) )

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "proto_220-c1.c1", 0x000000, 0x400000, CRC(9aa2b7dc) SHA1(6b3dff292c86f949890b1f8201bc5278f38c2668) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "proto_220-c2.c2", 0x000001, 0x400000, CRC(8a2ad708) SHA1(9568ac4cc0552e7fd3e50d3cd8d9f0f4fe7df1d4) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "proto_220-c3.c3", 0x800000, 0x400000, CRC(d67fb15a) SHA1(842971aeaf3c92e70f7c653bbf29058bc60f5b71) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "proto_220-c4.c4", 0x800001, 0x400000, CRC(e73ea38b) SHA1(27138d588e61e86c292f12d16e36c3681075c607) ) /* Plane 2,3 */
ROM_END

/****************************************
 ID-0221
 . NGM-221
 NEO-MVS PROGBK1 / NEO-MVS CHA256B
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 . NGH-221
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( magdrop2 )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "221-p1.p1", 0x000000, 0x80000, CRC(7be82353) SHA1(08ab39f52b893591c13a7d7aa26b20ce86e9ddf5) ) /* mask rom TC534200 */

	NEO_SFIX_128K( "221-s1.s1", CRC(2a4063a3) SHA1(0e09a7d88d85b1a2100888f4211960ea56ef978b) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "221-m1.m1", CRC(bddae628) SHA1(02c77e6aaaed43e39778bf83a3184e7c21db63d4) ) /* TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "221-v1.v1", 0x000000, 0x200000, CRC(7e5e53e4) SHA1(72b063b2d4acaaf72a20d14ad5bfc90cb64d3fed) ) /* mask rom TC5316200 */

	ROM_REGION( 0x800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "221-c1.c1", 0x000000, 0x400000, CRC(1f862a14) SHA1(1253e8b65d863d552d00dbdbfc5c168f5fc7edd1) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "221-c2.c2", 0x000001, 0x400000, CRC(14b90536) SHA1(e0d41f6b84d8261729f154b44ddd95c9b9c0714a) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

/****************************************
 ID-0222
 . NGM-222
 NEO-MVS PROGBK1 / NEO-MVS CHA256B
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 . NGH-222
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( samsho4 ) /* MVS AND AES VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "222-p1.p1",  0x000000, 0x100000, CRC(1a5cb56d) SHA1(9a0a5a1c7c5d428829f22d3d17f7033d43a51b5b) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "222-p2.sp2", 0x100000, 0x400000, CRC(b023cd8b) SHA1(35b4cec9858225f90acdfa16ed8a3017d0d08327) ) /* TC5332205 */

	NEO_SFIX_128K( "222-s1.s1", CRC(8d3d3bf9) SHA1(9975ed9b458bdd14e23451d2534153f68a5e4e6c) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "222-m1.m1", CRC(7615bc1b) SHA1(b936f7b341f6fe0921b4c41049734684583e3596) ) /* TC531001 */

	ROM_REGION( 0xa00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "222-v1.v1", 0x000000, 0x400000, CRC(7d6ba95f) SHA1(03cb4e0d770e0b332b07b64cacef624460b84c78) ) /* TC5332204 */
	ROM_LOAD( "222-v2.v2", 0x400000, 0x400000, CRC(6c33bb5d) SHA1(fd5d4e08a962dd0d22c52c91bad5ec7f23cfb901) ) /* TC5332204 */
	ROM_LOAD( "222-v3.v3", 0x800000, 0x200000, CRC(831ea8c0) SHA1(f2987b7d09bdc4311e972ce8a9ab7ca9802db4db) ) /* TC5316200 */

	ROM_REGION( 0x2000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "222-c1.c1", 0x0000000, 0x400000, CRC(68f2ed95) SHA1(c0a02df012cd25bcfe341770ea861a80294148cb) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "222-c2.c2", 0x0000001, 0x400000, CRC(a6e9aff0) SHA1(15addca49951ed53fa3c000c8d7cd327d012a620) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "222-c3.c3", 0x0800000, 0x400000, CRC(c91b40f4) SHA1(dcda45e0336204e3e024de08edfd0a3217bc1fdd) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "222-c4.c4", 0x0800001, 0x400000, CRC(359510a4) SHA1(b6642677ebdff7788263266402080272b8a66b15) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "222-c5.c5", 0x1000000, 0x400000, CRC(9cfbb22d) SHA1(789c32f917d0c6e38601cd390a7bf9d803131a4a) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "222-c6.c6", 0x1000001, 0x400000, CRC(685efc32) SHA1(db21ba1c7e3631ce0f1cb6f503ae7e0e043ff71b) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "222-c7.c7", 0x1800000, 0x400000, CRC(d0f86f0d) SHA1(32502d71c2ab1469c492b6b382bf2bb3f85981d9) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "222-c8.c8", 0x1800001, 0x400000, CRC(adfc50e3) SHA1(7d7ee874355b5aa75ad9c9a5c9c3df98d098d85e) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

ROM_START( samsho4k ) /* KOREAN VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "222-p1k.p1", 0x000000, 0x100000, CRC(06e0a25d) SHA1(81e6727e0acb67ae41383518c0fc07f28d232979) )
	ROM_LOAD16_WORD_SWAP( "222-p2.sp2", 0x100000, 0x400000, CRC(b023cd8b) SHA1(35b4cec9858225f90acdfa16ed8a3017d0d08327) ) /* TC5332205 */

	NEO_SFIX_128K( "222-s1k.s1", CRC(d313687d) SHA1(9ba468a9d582ef76c863f57ad9a0f811f3f08bd9) )

	NEO_BIOS_AUDIO_128K( "222-m1.m1", CRC(7615bc1b) SHA1(b936f7b341f6fe0921b4c41049734684583e3596) ) /* TC531001 */

	ROM_REGION( 0xa00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "222-v1.v1", 0x000000, 0x400000, CRC(7d6ba95f) SHA1(03cb4e0d770e0b332b07b64cacef624460b84c78) ) /* TC5332204 */
	ROM_LOAD( "222-v2.v2", 0x400000, 0x400000, CRC(6c33bb5d) SHA1(fd5d4e08a962dd0d22c52c91bad5ec7f23cfb901) ) /* TC5332204 */
	ROM_LOAD( "222-v3.v3", 0x800000, 0x200000, CRC(831ea8c0) SHA1(f2987b7d09bdc4311e972ce8a9ab7ca9802db4db) ) /* TC5316200 */

	ROM_REGION( 0x2000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "222-c1.c1", 0x0000000, 0x400000, CRC(68f2ed95) SHA1(c0a02df012cd25bcfe341770ea861a80294148cb) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "222-c2.c2", 0x0000001, 0x400000, CRC(a6e9aff0) SHA1(15addca49951ed53fa3c000c8d7cd327d012a620) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "222-c3.c3", 0x0800000, 0x400000, CRC(c91b40f4) SHA1(dcda45e0336204e3e024de08edfd0a3217bc1fdd) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "222-c4.c4", 0x0800001, 0x400000, CRC(359510a4) SHA1(b6642677ebdff7788263266402080272b8a66b15) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "222-c5.c5", 0x1000000, 0x400000, CRC(9cfbb22d) SHA1(789c32f917d0c6e38601cd390a7bf9d803131a4a) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "222-c6.c6", 0x1000001, 0x400000, CRC(685efc32) SHA1(db21ba1c7e3631ce0f1cb6f503ae7e0e043ff71b) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "222-c7.c7", 0x1800000, 0x400000, CRC(d0f86f0d) SHA1(32502d71c2ab1469c492b6b382bf2bb3f85981d9) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "222-c8.c8", 0x1800001, 0x400000, CRC(adfc50e3) SHA1(7d7ee874355b5aa75ad9c9a5c9c3df98d098d85e) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

/****************************************
 ID-0223
 . NGM-223
 NEO-MVS PROGBK1 / NEO-MVS CHA256B
 . NGH-223
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( rbffspec )
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "223-p1.p1",  0x000000, 0x100000, CRC(f84a2d1d) SHA1(fc19225d9dbdb6bd0808023ee32c7829f6ffdef6) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "223-p2.sp2", 0x100000, 0x400000, CRC(addd8f08) SHA1(abaf5b86c8ec915c07ef2d83fce9ad03acaa4817) ) /* TC5332205 */

	NEO_SFIX_128K( "223-s1.s1", CRC(7ecd6e8c) SHA1(465455afc4d83cbb118142be4671b2539ffafd79) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "223-m1.m1", CRC(3fee46bf) SHA1(e750f85233953853618fcdff980a4721af1710a3) ) /* TC531001 */

	ROM_REGION( 0xc00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "223-v1.v1", 0x000000, 0x400000, CRC(76673869) SHA1(78a26afa29f73de552ffabdbf3fc4bf26be8ae9e) ) /* TC5332204 */
	ROM_LOAD( "223-v2.v2", 0x400000, 0x400000, CRC(7a275acd) SHA1(8afe87ce822614262b72a90b371fc79155ac0d0c) ) /* TC5332204 */
	ROM_LOAD( "223-v3.v3", 0x800000, 0x400000, CRC(5a797fd2) SHA1(94958e334f86d4d71059af8138f255b8d97a3b01) ) /* TC5332204 */

	ROM_REGION( 0x2000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "223-c1.c1", 0x0000000, 0x400000, CRC(ebab05e2) SHA1(0d60a8b631e3a3dcfbfdd7779dee081c9548ec39) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "223-c2.c2", 0x0000001, 0x400000, CRC(641868c3) SHA1(aa1aeb661842276b3326bfa4f1456f75bfecd52e) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "223-c3.c3", 0x0800000, 0x400000, CRC(ca00191f) SHA1(96977febfcc513e1848d7029ff169cdf51104038) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "223-c4.c4", 0x0800001, 0x400000, CRC(1f23d860) SHA1(e18df52f898a51074e07a0b8c6e75873e7cde35e) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "223-c5.c5", 0x1000000, 0x400000, CRC(321e362c) SHA1(39bd189334278f266124c97c6f70995f6f171cea) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "223-c6.c6", 0x1000001, 0x400000, CRC(d8fcef90) SHA1(bbccacb27f1e587bc144fe7ce68bd7b327ceaaee) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "223-c7.c7", 0x1800000, 0x400000, CRC(bc80dd2d) SHA1(086f372015eede88c6c578595fe915e28a589d2f) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "223-c8.c8", 0x1800001, 0x400000, CRC(5ad62102) SHA1(e28cc9840caed2a1a8bd65a03bef05231071040c) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

ROM_START( rbffspeck ) /* KOREAN VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "223-p1k.p1", 0x000000, 0x100000, CRC(b78c8391) SHA1(936b356ac135331b10a37bc10882ec2e4f6b400b) )
	/* Chip label is correct. They used Cart ID 0124 as 0123 was already used by quizdaisk */
	ROM_LOAD16_WORD_SWAP( "223-p2.sp2", 0x100000, 0x400000, CRC(addd8f08) SHA1(abaf5b86c8ec915c07ef2d83fce9ad03acaa4817) ) /* TC5332205 */

	NEO_SFIX_128K( "223-s1.s1", CRC(7ecd6e8c) SHA1(465455afc4d83cbb118142be4671b2539ffafd79) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "223-m1.m1", CRC(3fee46bf) SHA1(e750f85233953853618fcdff980a4721af1710a3) ) /* TC531001 */

	ROM_REGION( 0xc00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "223-v1.v1", 0x000000, 0x400000, CRC(76673869) SHA1(78a26afa29f73de552ffabdbf3fc4bf26be8ae9e) ) /* TC5332204 */
	ROM_LOAD( "223-v2.v2", 0x400000, 0x400000, CRC(7a275acd) SHA1(8afe87ce822614262b72a90b371fc79155ac0d0c) ) /* TC5332204 */
	ROM_LOAD( "223-v3.v3", 0x800000, 0x400000, CRC(5a797fd2) SHA1(94958e334f86d4d71059af8138f255b8d97a3b01) ) /* TC5332204 */

	ROM_REGION( 0x2000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "223-c1.c1", 0x0000000, 0x400000, CRC(ebab05e2) SHA1(0d60a8b631e3a3dcfbfdd7779dee081c9548ec39) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "223-c2.c2", 0x0000001, 0x400000, CRC(641868c3) SHA1(aa1aeb661842276b3326bfa4f1456f75bfecd52e) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "223-c3.c3", 0x0800000, 0x400000, CRC(ca00191f) SHA1(96977febfcc513e1848d7029ff169cdf51104038) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "223-c4.c4", 0x0800001, 0x400000, CRC(1f23d860) SHA1(e18df52f898a51074e07a0b8c6e75873e7cde35e) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "223-c5.c5", 0x1000000, 0x400000, CRC(321e362c) SHA1(39bd189334278f266124c97c6f70995f6f171cea) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "223-c6.c6", 0x1000001, 0x400000, CRC(d8fcef90) SHA1(bbccacb27f1e587bc144fe7ce68bd7b327ceaaee) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "223-c7.c7", 0x1800000, 0x400000, CRC(bc80dd2d) SHA1(086f372015eede88c6c578595fe915e28a589d2f) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "223-c8.c8", 0x1800001, 0x400000, CRC(5ad62102) SHA1(e28cc9840caed2a1a8bd65a03bef05231071040c) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

/****************************************
 ID-0224
 . ADM-013
 NEO-MVS PROGBK1 / NEO-MVS CHA256B
 . ADH-013
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( twinspri )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "224-p1.p1", 0x100000, 0x100000, CRC(7697e445) SHA1(5b55ca120f77a931d40719b14e0bfc8cac1d628c) ) /* mask rom TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "224-s1.s1", CRC(eeed5758) SHA1(24e48f396716e145b692468762cf595fb7267873) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "224-m1.m1", CRC(364d6f96) SHA1(779b95a6476089b71f48c8368d9043ee1dba9032) ) /* mask rom TC531001 */

	ROM_REGION( 0x600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "224-v1.v1", 0x000000, 0x400000, CRC(ff57f088) SHA1(1641989b8aac899dbd68aa2332bcdf9b90b33564) ) /* mask rom TC5332204 */
	ROM_LOAD( "224-v2.v2", 0x400000, 0x200000, CRC(7ad26599) SHA1(822030037b7664795bf3d64e1452d0aecc22497e) ) /* mask rom TC5316200 */

	ROM_REGION( 0xa00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "224-c1.c1", 0x000000, 0x400000, CRC(f7da64ab) SHA1(587a10ed9235c9046a3523fe80feba07764fac9b) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "224-c2.c2", 0x000001, 0x400000, CRC(4c09bbfb) SHA1(e781aafba3bdefb7ed152826f4c3eb441735331c) ) /* Plane 2,3 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "224-c3.c3", 0x800000, 0x100000, CRC(c59e4129) SHA1(93f02d1b4fbb152a9d336494fbff0d7642921de5) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "224-c4.c4", 0x800001, 0x100000, CRC(b5532e53) SHA1(7d896c25ba97f6e5d43c13d4df4ba72964a976ed) ) /* Plane 2,3 */ /* mask rom TC538200 */
ROM_END

/****************************************
 ID-0225
 . SUM-225
 NEO-MVS PROGBK1 / NEO-MVS CHA256B
 . SUH-225
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( wakuwak7 )
	ROM_REGION( 0x300000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "225-p1.p1",  0x000000, 0x100000, CRC(b14da766) SHA1(bdffd72ff705fc6b085a4026217bac1c4bc93163) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "225-p2.sp2", 0x100000, 0x200000, CRC(fe190665) SHA1(739d9a8fc2da34381654d9e291141eacc210ae5c) ) /* TC5316200 */
	/* also found Korean MVS set with ep1 /ep2 on eprom and sp2 on TC5316200 on NEO-MVS PROGBK1; correct chip label for eproms unknown */

	NEO_SFIX_128K( "225-s1.s1", CRC(71c4b4b5) SHA1(9410f13807f01082dc86f2d84051be4bed8e9f7c) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "225-m1.m1", CRC(0634bba6) SHA1(153aaf016440500df7a4454f3f2f2911219cb7d8) ) /* TC531001 */

	ROM_REGION( 0x800000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "225-v1.v1", 0x000000, 0x400000, CRC(6195c6b4) SHA1(66c06b5904aedb256e3997bbec60f8ab50c6ff0c) ) /* TC5332204 */
	ROM_LOAD( "225-v2.v2", 0x400000, 0x400000, CRC(6159c5fe) SHA1(9015e93416497f1ef877c717afed40f7ecfa42e4) ) /* TC5332204 */

	ROM_REGION( 0x1800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "225-c1.c1", 0x0000000, 0x400000, CRC(ee4fea54) SHA1(314b513a52b2cc88cbf2409d1934c357269a8bb2) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "225-c2.c2", 0x0000001, 0x400000, CRC(0c549e2d) SHA1(d8c4626231c92e43d9bf183202553ee2b5c532e6) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "225-c3.c3", 0x0800000, 0x400000, CRC(af0897c0) SHA1(2b8ec19b9dd0bd1f1171fb01b915e9d25ec8c421) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "225-c4.c4", 0x0800001, 0x400000, CRC(4c66527a) SHA1(6c8c9342fad70b456e282b0d52e7ad890e4673d3) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "225-c5.c5", 0x1000000, 0x400000, CRC(8ecea2b5) SHA1(cad51e6e76d8258a78becb6f4096dd061f537494) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "225-c6.c6", 0x1000001, 0x400000, CRC(0eb11a6d) SHA1(c6d4f978ff3ca190a3060ac52bd7347189194f76) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

/****************************************
 ID-0226
 Pair Pair Wars (prototype) 1996 Sunsoft?
****************************************/

/****************************************
 ID-0227
 . NGM-227
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 . NGH-227
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( stakwin2 )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "227-p1.p1", 0x100000, 0x100000, CRC(daf101d2) SHA1(96b90f884bae2969ebd8c04aba509928464e2433) ) /* mask rom TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "227-s1.s1", CRC(2a8c4462) SHA1(9155fbb5fee6d46a68d17ea780a7a92565f9aa47) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "227-m1.m1", CRC(c8e5e0f9) SHA1(09bb05ae6f09b59b9e4871fae1fc7c3bafd07394) ) /* mask rom TC531001 */

	ROM_REGION( 0x800000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "227-v1.v1", 0x000000, 0x400000, CRC(b8f24181) SHA1(0a3af88d20ff65b82c58325d32c20b99fc07f7f3) ) /* mask rom TC5332204 */
	ROM_LOAD( "227-v2.v2", 0x400000, 0x400000, CRC(ee39e260) SHA1(4ed6802564ce262ebe92c7276424056b70998758) ) /* mask rom TC5332204 */

	ROM_REGION( 0xc00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "227-c1.c1", 0x0000000, 0x400000, CRC(7d6c2af4) SHA1(e54f0ab15c95d7a6f965b5d8ab28b5445100650b) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "227-c2.c2", 0x0000001, 0x400000, CRC(7e402d39) SHA1(9d3a44f98ddd0b606c8b3efa0c6b9d5a46c0bfeb) ) /* Plane 2,3 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "227-c3.c3", 0x0800000, 0x200000, CRC(93dfd660) SHA1(5b473c556ef919cd7a872351dbb20a636aae32b6) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "227-c4.c4", 0x0800001, 0x200000, CRC(7efea43a) SHA1(3f2b1718fe7be06b6d75ec34badc2de2a3554d3e) ) /* Plane 2,3 */ /* mask rom TC5316200 */
ROM_END

/****************************************
 ID-0228
 Ghostlop (prototype) 1996 Data East Corp.

 Ghostlop (location test) 2001 Data East Corp.
 NEO-MVS PROGGSC / NEO-MVS CHA256
****************************************/

/* The location test version uses socketed EPROMs on the PROG board, flash chips on adapter boards for the C ROMs and EPROMs for M1 and S1 on the CHA.
It has a piece of cardboard between the 2 boards to avoid shorts because the socketed flash adapter boards are a bit too high and could get shorted with traces on the PROG board. */

ROM_START( ghostlop ) /* Prototype */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "proto_228-p1.p1", 0x000000, 0x100000, CRC(6033172e) SHA1(f57fb706aa8dd9e5f9e992a5d35c1799578b59f8) )

	NEO_SFIX_128K( "proto_228-s1.s1", CRC(83c24e81) SHA1(585ef209d8bfc23bdccc1f37d8b764eeedfedc1c) )

	NEO_BIOS_AUDIO_128K( "proto_228-m1.m1", CRC(fd833b33) SHA1(ab6c218c42cba821654cbdae154efecb69f844f6) )

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "proto_228-v1.v1", 0x000000, 0x200000, CRC(c603fce6) SHA1(5a866471d35895b2ae13cbd5d1cb41bf2e72e1b8) )

	ROM_REGION( 0x800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "proto_228-c1.c1", 0x000000, 0x400000, CRC(bfc99efe) SHA1(5cd2545310142080b8286e787cf5b859f627b3db) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "proto_228-c2.c2", 0x000001, 0x400000, CRC(69788082) SHA1(c3ecb42ddcbd9e16d0018a0c3adb56a911d813ca) ) /* Plane 2,3 */
ROM_END

/****************************************
 ID-0229
 King of Fighters '96 CD Collection (CD only)
****************************************/


/* With ID-0230 the product ID code changed from xxM-xxx / xxH-xxx to xxM-xxx0 / xxH-xxx0 */


/****************************************
 ID-0230
 . NGM-2300
 NEO-MVS PROGBK1 / NEO-MVS CHA256B
 . NGH-2300
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( breakers )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "230-p1.p1", 0x100000, 0x100000, CRC(ed24a6e6) SHA1(3fb77ae696d92d2f9a5d589e08b708545c7cda0a) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "230-s1.s1", CRC(076fb64c) SHA1(c166038128d7004f69932141f83b320a35c2b4ca) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "230-m1.m1", CRC(3951a1c1) SHA1(1e6442a7ea82ada9503d71045dd93e12bd05254f) ) /* TC531001 */

	ROM_REGION( 0x800000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "230-v1.v1", 0x000000, 0x400000, CRC(7f9ed279) SHA1(acd0558960ec29bfc3e3ee99d00e503bebff8513) ) /* TC5332204 */
	ROM_LOAD( "230-v2.v2", 0x400000, 0x400000, CRC(1d43e420) SHA1(26d09b8b18b4b802dbda4d6f06626c24d0b7c512) ) /* TC5332204 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "230-c1.c1", 0x000000, 0x400000, CRC(68d4ae76) SHA1(2e820067f6963669f104bebf19e865fe4127b4dd) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "230-c2.c2", 0x000001, 0x400000, CRC(fdee05cd) SHA1(efc4ffd790953ac7c25d5f045c64a9b49d24b096) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "230-c3.c3", 0x800000, 0x400000, CRC(645077f3) SHA1(0ae74f3b4b3b88f128c6d8c0f35ffa53f5d67ef2) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "230-c4.c4", 0x800001, 0x400000, CRC(63aeb74c) SHA1(9ff6930c0c3d79b46b86356e8565ce4fcd69ac38) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

/****************************************
 ID-0231
 . ??M-2310
 NEO-MVS PROGBK1 / NEO-MVS CHA42G-3B
****************************************/

ROM_START( miexchng ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "231-p1.p1", 0x000000, 0x80000, CRC(61be1810) SHA1(1ab0e11352ca05329c6e3f5657b60e4a227fcbfb) ) /* mask rom TC534200 */

	NEO_SFIX_128K( "231-s1.s1", CRC(fe0c0c53) SHA1(54d56d4463db193e504658f4f6f4997a62ae3d95) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "231-m1.m1", CRC(de41301b) SHA1(59ce3836ac8f064d56a446c9374f05bcb40fcfd8) ) /* mask rom TC531001 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "231-v1.v1", 0x000000, 0x400000, CRC(113fb898) SHA1(9168ba90c4aa969f69eb11ba3f4d76592d81e05a) ) /* mask rom TC5332204 */

	ROM_REGION( 0x600000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "231-c1.c1", 0x000000, 0x200000, CRC(6c403ba3) SHA1(3830446fbd07d5a6564f9ac68a4bec5ff5b7d5c9) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "231-c2.c2", 0x000001, 0x200000, CRC(554bcd9b) SHA1(e658161618bd41a66f1040be409efdea28020cf6) ) /* Plane 2,3 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "231-c3.c3", 0x400000, 0x100000, CRC(4f6f7a63) SHA1(10935dbc1f52b95979a097b13a114cff18d4d446) ) /* Plane 0,1 */ /* mask rom TC538200 */
	ROM_LOAD16_BYTE( "231-c4.c4", 0x400001, 0x100000, CRC(2e35e71b) SHA1(6f248191c2c60ca1b1b4f2ebf08756e036682144) ) /* Plane 2,3 */ /* mask rom TC538200 */
ROM_END

/****************************************
 ID-0232
 . NGM-2320
 NEO-MVS PROGBK1 / NEO-MVS CHA512Y
 . NGH-2320
 NEO-AEG PROGBK1Y / NEO-AEG CHA512Y
****************************************/

ROM_START( kof97 ) /* MVS VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "232-p1.p1",  0x000000, 0x100000, CRC(7db81ad9) SHA1(8bc42be872fd497eb198ca13bf004852b88eb1dc) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "232-p2.sp2", 0x100000, 0x400000, CRC(158b23f6) SHA1(9744620a70513490aaf9c5eda33e5ec31222be19) ) /* TC5332205 */
	/* also found sets with P1 on TC538200 and SP2 on TC5332205 with chip labels 232-PG1 and 232-P2 */

	NEO_SFIX_128K( "232-s1.s1", CRC(8514ecf5) SHA1(18d8e7feb51ea88816f1c786932a53655b0de6a0) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "232-m1.m1", CRC(45348747) SHA1(ed77cbae2b208d1177a9f5f6e8cd57070e90b65b) ) /* TC531001 */

	ROM_REGION( 0xc00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "232-v1.v1", 0x000000, 0x400000, CRC(22a2b5b5) SHA1(ebdbc977332e6d93e266755000b43857e0082965) ) /* TC5332204 */
	ROM_LOAD( "232-v2.v2", 0x400000, 0x400000, CRC(2304e744) SHA1(98d283e2bcc9291a53f52afd35ef76dfb0828432) ) /* TC5332204 */
	ROM_LOAD( "232-v3.v3", 0x800000, 0x400000, CRC(759eb954) SHA1(54e77c4e9e6b89458e59824e478ddc33a9c72655) ) /* TC5332204 */

	ROM_REGION( 0x2800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "232-c1.c1", 0x0000000, 0x800000, CRC(5f8bf0a1) SHA1(e8b63bbc814de171fd18c5864a7fc639970c1ecf) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "232-c2.c2", 0x0000001, 0x800000, CRC(e4d45c81) SHA1(fdb2b9326362e27b1c7a5beb977e0bc537488186) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "232-c3.c3", 0x1000000, 0x800000, CRC(581d6618) SHA1(14d3124a08ded59f86932c6b28e1a4e48c564ccd) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "232-c4.c4", 0x1000001, 0x800000, CRC(49bb1e68) SHA1(f769c1bd1b019521111ff3f0d22c63cb1f2640ef) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "232-c5.c5", 0x2000000, 0x400000, CRC(34fc4e51) SHA1(b39c65f27873f71a6f5a5d1d04e5435f874472ee) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "232-c6.c6", 0x2000001, 0x400000, CRC(4ff4d47b) SHA1(4d5689ede24a5fe4330bd85d4d3f4eb2795308bb) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

ROM_START( kof97h ) /* AES VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "232-pg1.p1", 0x000000, 0x100000, CRC(5c2400b7) SHA1(49e23f80c012c62146a1bb8f254a7597823de430) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "232-p2.sp2", 0x100000, 0x400000, CRC(158b23f6) SHA1(9744620a70513490aaf9c5eda33e5ec31222be19) ) /* TC5332205 */

	NEO_SFIX_128K( "232-s1.s1", CRC(8514ecf5) SHA1(18d8e7feb51ea88816f1c786932a53655b0de6a0) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "232-m1.m1", CRC(45348747) SHA1(ed77cbae2b208d1177a9f5f6e8cd57070e90b65b) ) /* TC531001 */

	ROM_REGION( 0xc00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "232-v1.v1", 0x000000, 0x400000, CRC(22a2b5b5) SHA1(ebdbc977332e6d93e266755000b43857e0082965) ) /* TC5332204 */
	ROM_LOAD( "232-v2.v2", 0x400000, 0x400000, CRC(2304e744) SHA1(98d283e2bcc9291a53f52afd35ef76dfb0828432) ) /* TC5332204 */
	ROM_LOAD( "232-v3.v3", 0x800000, 0x400000, CRC(759eb954) SHA1(54e77c4e9e6b89458e59824e478ddc33a9c72655) ) /* TC5332204 */

	ROM_REGION( 0x2800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "232-c1.c1", 0x0000000, 0x800000, CRC(5f8bf0a1) SHA1(e8b63bbc814de171fd18c5864a7fc639970c1ecf) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "232-c2.c2", 0x0000001, 0x800000, CRC(e4d45c81) SHA1(fdb2b9326362e27b1c7a5beb977e0bc537488186) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "232-c3.c3", 0x1000000, 0x800000, CRC(581d6618) SHA1(14d3124a08ded59f86932c6b28e1a4e48c564ccd) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "232-c4.c4", 0x1000001, 0x800000, CRC(49bb1e68) SHA1(f769c1bd1b019521111ff3f0d22c63cb1f2640ef) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "232-c5.c5", 0x2000000, 0x400000, CRC(34fc4e51) SHA1(b39c65f27873f71a6f5a5d1d04e5435f874472ee) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "232-c6.c6", 0x2000001, 0x400000, CRC(4ff4d47b) SHA1(4d5689ede24a5fe4330bd85d4d3f4eb2795308bb) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

ROM_START( kof97k ) /* KOREAN VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "232-p1.p1",  0x000000, 0x100000, CRC(7db81ad9) SHA1(8bc42be872fd497eb198ca13bf004852b88eb1dc) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "232-p2.sp2", 0x100000, 0x400000, CRC(158b23f6) SHA1(9744620a70513490aaf9c5eda33e5ec31222be19) ) /* TC5332205 */

	NEO_SFIX_128K( "232-s1.s1", CRC(8514ecf5) SHA1(18d8e7feb51ea88816f1c786932a53655b0de6a0) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "232-m1k.m1", CRC(bbea9070) SHA1(c26c2e29fe90966dd574838be63f0037ea799aca) )

	ROM_REGION( 0xc00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "232-v1.v1", 0x000000, 0x400000, CRC(22a2b5b5) SHA1(ebdbc977332e6d93e266755000b43857e0082965) ) /* TC5332204 */
	ROM_LOAD( "232-v2.v2", 0x400000, 0x400000, CRC(2304e744) SHA1(98d283e2bcc9291a53f52afd35ef76dfb0828432) ) /* TC5332204 */
	ROM_LOAD( "232-v3.v3", 0x800000, 0x400000, CRC(759eb954) SHA1(54e77c4e9e6b89458e59824e478ddc33a9c72655) ) /* TC5332204 */

	ROM_REGION( 0x2800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "232-c1.c1", 0x0000000, 0x800000, CRC(5f8bf0a1) SHA1(e8b63bbc814de171fd18c5864a7fc639970c1ecf) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "232-c2.c2", 0x0000001, 0x800000, CRC(e4d45c81) SHA1(fdb2b9326362e27b1c7a5beb977e0bc537488186) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "232-c3.c3", 0x1000000, 0x800000, CRC(581d6618) SHA1(14d3124a08ded59f86932c6b28e1a4e48c564ccd) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "232-c4.c4", 0x1000001, 0x800000, CRC(49bb1e68) SHA1(f769c1bd1b019521111ff3f0d22c63cb1f2640ef) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "232-c5.c5", 0x2000000, 0x400000, CRC(34fc4e51) SHA1(b39c65f27873f71a6f5a5d1d04e5435f874472ee) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "232-c6.c6", 0x2000001, 0x400000, CRC(4ff4d47b) SHA1(4d5689ede24a5fe4330bd85d4d3f4eb2795308bb) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

/****************************************
 ID-0233
 . NGM-2330
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 NEO-MVS PROGBK1 / NEO-MVS CHA256B
 . NGH-2330
 NEO-AEG PROGBK1Y / NEO-AEG CHA256RY
****************************************/

ROM_START( magdrop3 )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "233-p1.p1", 0x000000, 0x100000, CRC(931e17fa) SHA1(4a95c4b79d0878485ce272e9f4c4f647bec0e070) ) /* TC538200 */

	NEO_SFIX_128K( "233-s1.s1", CRC(7399e68a) SHA1(b535ee56a0f0995f04674e676f6aa636ffad26aa) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "233-m1.m1", CRC(5beaf34e) SHA1(2905d26945932cddc2dd3a1dc5abba8aa3baee14) ) /* TC531001 */

	ROM_REGION( 0x480000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "233-v1.v1", 0x000000, 0x400000, CRC(58839298) SHA1(18cae7bba997c52780761cbf119c4e4b34397a61) ) /* TC5332204 */
	ROM_LOAD( "233-v2.v2", 0x400000, 0x080000, CRC(d5e30df4) SHA1(bbbc0ff5b975471bd682f85976ac4a93f6d44f2e) ) /* TC534200 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "233-c1.c1", 0x000000, 0x400000, CRC(65e3f4c4) SHA1(a6deb75d802225327f8f1c2733a7f2b47e722e59) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "233-c2.c2", 0x000001, 0x400000, CRC(35dea6c9) SHA1(ea133bf947f950236f49d0ae0d1a9af3bc1a9a50) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "233-c3.c3", 0x800000, 0x400000, CRC(0ba2c502) SHA1(8e0f1e553aef04758aaaa14d5115f0ecace4391e) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "233-c4.c4", 0x800001, 0x400000, CRC(70dbbd6d) SHA1(32dd6a04c6329e89f4878e7a56f0d172a6388eea) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

/****************************************
 ID-0234
 . NGM-2340
 NEO-MVS PROGBK1 / NEO-MVS CHA512Y
 . NGH-2340
 NEO-AEG PROGBK1Y / NEO-AEG CHA512Y
****************************************/

ROM_START( lastblad ) /* MVS VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "234-p1.p1",  0x000000, 0x100000, CRC(e123a5a3) SHA1(a3ddabc00feeb54272b145246612ad4632b0e413) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "234-p2.sp2", 0x100000, 0x400000, CRC(0fdc289e) SHA1(1ff31c0b0f4f9ddbedaf4bcf927faaae81892ec7) ) /* TC5332205 */
	/* also found sets with P1 / SP2 / EP1 / EP2 / M1 on eprom with sticker */
	/* chip labels for eproms are 0234-P1, 0234-SP2, 0234-EP1, 0234-EP2 and 0234-M1; same data  */

	NEO_SFIX_128K( "234-s1.s1", CRC(95561412) SHA1(995de272f572fd08d909d3d0af4251b9957b3640) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "234-m1.m1", CRC(087628ea) SHA1(48dcf739bb16699af4ab8ed632b7dcb25e470e06) ) /* TC531001 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "234-v1.v1", 0x000000, 0x400000, CRC(ed66b76f) SHA1(8a05ff06d9b6f01c6c16b3026282eaabb0e25b44) ) /* TC5332204 */
	ROM_LOAD( "234-v2.v2", 0x400000, 0x400000, CRC(a0e7f6e2) SHA1(753ff74fa9294f695aae511ae01ead119b114a57) ) /* TC5332204 */
	ROM_LOAD( "234-v3.v3", 0x800000, 0x400000, CRC(a506e1e2) SHA1(b3e04ba1a5cb50b77c6fbe9fe353b9b64b6f3f74) ) /* TC5332204 */
	ROM_LOAD( "234-v4.v4", 0xc00000, 0x400000, CRC(0e34157f) SHA1(20a1f4833e5e29ba0073c1712d7a17ab7a2a035c) ) /* TC5332204 */

	ROM_REGION( 0x2800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "234-c1.c1", 0x0000000, 0x800000, CRC(9f7e2bd3) SHA1(2828aca0c0f5802110f10453c1cf640f69736554) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "234-c2.c2", 0x0000001, 0x800000, CRC(80623d3c) SHA1(ad460615115ec8fb25206f012da59ecfc8059b64) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "234-c3.c3", 0x1000000, 0x800000, CRC(91ab1a30) SHA1(e3cf9133784bef2c8f1bfe45f277ccf82cc6f6a1) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "234-c4.c4", 0x1000001, 0x800000, CRC(3d60b037) SHA1(78a50233bcd19e92c7b6f7ee1a53417d9db21f6a) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "234-c5.c5", 0x2000000, 0x400000, CRC(1ba80cee) SHA1(0c59057183b5279b747e73213b4cd3c6d7ad9eb1) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "234-c6.c6", 0x2000001, 0x400000, CRC(beafd091) SHA1(55df9cc128eb0f00856de3996c946e3efe8f09a5) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

ROM_START( lastbladh ) /* AES VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "234-pg1.p1", 0x000000, 0x100000, CRC(cd01c06d) SHA1(d66142571afe07c6191b52f319f1bc8bc8541c14) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "234-p2.sp2", 0x100000, 0x400000, CRC(0fdc289e) SHA1(1ff31c0b0f4f9ddbedaf4bcf927faaae81892ec7) ) /* TC5332205 */

	NEO_SFIX_128K( "234-s1.s1", CRC(95561412) SHA1(995de272f572fd08d909d3d0af4251b9957b3640) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "234-m1.m1", CRC(087628ea) SHA1(48dcf739bb16699af4ab8ed632b7dcb25e470e06) ) /* TC531001 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "234-v1.v1", 0x000000, 0x400000, CRC(ed66b76f) SHA1(8a05ff06d9b6f01c6c16b3026282eaabb0e25b44) ) /* TC5332204 */
	ROM_LOAD( "234-v2.v2", 0x400000, 0x400000, CRC(a0e7f6e2) SHA1(753ff74fa9294f695aae511ae01ead119b114a57) ) /* TC5332204 */
	ROM_LOAD( "234-v3.v3", 0x800000, 0x400000, CRC(a506e1e2) SHA1(b3e04ba1a5cb50b77c6fbe9fe353b9b64b6f3f74) ) /* TC5332204 */
	ROM_LOAD( "234-v4.v4", 0xc00000, 0x400000, CRC(0e34157f) SHA1(20a1f4833e5e29ba0073c1712d7a17ab7a2a035c) ) /* TC5332204 */

	ROM_REGION( 0x2800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "234-c1.c1", 0x0000000, 0x800000, CRC(9f7e2bd3) SHA1(2828aca0c0f5802110f10453c1cf640f69736554) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "234-c2.c2", 0x0000001, 0x800000, CRC(80623d3c) SHA1(ad460615115ec8fb25206f012da59ecfc8059b64) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "234-c3.c3", 0x1000000, 0x800000, CRC(91ab1a30) SHA1(e3cf9133784bef2c8f1bfe45f277ccf82cc6f6a1) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "234-c4.c4", 0x1000001, 0x800000, CRC(3d60b037) SHA1(78a50233bcd19e92c7b6f7ee1a53417d9db21f6a) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "234-c5.c5", 0x2000000, 0x400000, CRC(1ba80cee) SHA1(0c59057183b5279b747e73213b4cd3c6d7ad9eb1) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "234-c6.c6", 0x2000001, 0x400000, CRC(beafd091) SHA1(55df9cc128eb0f00856de3996c946e3efe8f09a5) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

ROM_START( lastsold ) /* KOREAN VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "134-p1k.p1", 0x000000, 0x100000, CRC(906f3065) SHA1(25167665f1b8e82e13f7fcf4d0e3c54a925c2a58) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "234-p2.sp2", 0x100000, 0x400000, CRC(0fdc289e) SHA1(1ff31c0b0f4f9ddbedaf4bcf927faaae81892ec7) ) /* TC5332205 */

	NEO_SFIX_128K( "234-s1.s1", CRC(95561412) SHA1(995de272f572fd08d909d3d0af4251b9957b3640) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "234-m1.m1", CRC(087628ea) SHA1(48dcf739bb16699af4ab8ed632b7dcb25e470e06) ) /* TC531001 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "234-v1.v1", 0x000000, 0x400000, CRC(ed66b76f) SHA1(8a05ff06d9b6f01c6c16b3026282eaabb0e25b44) ) /* TC5332204 */
	ROM_LOAD( "234-v2.v2", 0x400000, 0x400000, CRC(a0e7f6e2) SHA1(753ff74fa9294f695aae511ae01ead119b114a57) ) /* TC5332204 */
	ROM_LOAD( "234-v3.v3", 0x800000, 0x400000, CRC(a506e1e2) SHA1(b3e04ba1a5cb50b77c6fbe9fe353b9b64b6f3f74) ) /* TC5332204 */
	ROM_LOAD( "234-v4.v4", 0xc00000, 0x400000, CRC(0e34157f) SHA1(20a1f4833e5e29ba0073c1712d7a17ab7a2a035c) ) /* TC5332204 */

	ROM_REGION( 0x2800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "234-c1.c1", 0x0000000, 0x800000, CRC(9f7e2bd3) SHA1(2828aca0c0f5802110f10453c1cf640f69736554) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "234-c2.c2", 0x0000001, 0x800000, CRC(80623d3c) SHA1(ad460615115ec8fb25206f012da59ecfc8059b64) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "234-c3.c3", 0x1000000, 0x800000, CRC(91ab1a30) SHA1(e3cf9133784bef2c8f1bfe45f277ccf82cc6f6a1) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "234-c4.c4", 0x1000001, 0x800000, CRC(3d60b037) SHA1(78a50233bcd19e92c7b6f7ee1a53417d9db21f6a) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "234-c5.c5", 0x2000000, 0x400000, CRC(1ba80cee) SHA1(0c59057183b5279b747e73213b4cd3c6d7ad9eb1) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "234-c6.c6", 0x2000001, 0x400000, CRC(beafd091) SHA1(55df9cc128eb0f00856de3996c946e3efe8f09a5) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

/****************************************
 ID-0235
 . ??M-2350
 NEO-MVS PROG 4096 / NEO-MVS CHA 42G-2
****************************************/

ROM_START( puzzldpr ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "235-p1.p1", 0x000000, 0x080000, CRC(afed5de2) SHA1(a5d82c6dbe687505e8c8d7339908da45cd379a0b) ) /* TC534200 */

	NEO_SFIX_128K( "235-s1.s1", CRC(3b13a22f) SHA1(4506fc340d9658a50fa415676564f10bbfba2703) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "202-m1.m1", CRC(9c0291ea) SHA1(3fa67c62acba79be6b3a98cc1601e45569fa11ae) ) /* TC531001 */

	ROM_REGION( 0x080000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "202-v1.v1", 0x000000, 0x080000, CRC(debeb8fb) SHA1(49a3d3578c087f1a0050168571ef8d1b08c5dc05) ) /* TC534200 */

	ROM_REGION( 0x200000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "202-c1.c1", 0x000000, 0x100000, CRC(cc0095ef) SHA1(3d86f455e6db10a2449b775dc386f1826ba3b62e) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "202-c2.c2", 0x000001, 0x100000, CRC(42371307) SHA1(df794f989e2883634bf7ffeea48d6bc3854529af) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

/****************************************
 ID-0236
 . ??M-2360
 NEO-MVS PROGBK1 / NEO-MVS CHA512Y
****************************************/

ROM_START( irrmaze ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "236-p1.p1", 0x100000, 0x100000, CRC(4c2ff660) SHA1(4a0cbd09044648ff9ec67723729f16d422c34bda) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "236-s1.s1", CRC(5d1ca640) SHA1(40a9668a1742a44597a07ce72273d17119815637) ) /* TC531000 */

	ROM_REGION16_BE( 0x20000, "mainbios", 0 )
	// special BIOS with trackball support, we only have one Irritating Maze bios and that's Asian
	ROM_SYSTEM_BIOS( 0, "asia-sp1", "Asia MV1B 263" )
	ROM_LOAD16_WORD_SWAP_BIOS( 0, "236-bios.sp1", 0x00000, 0x020000, CRC(853e6b96) SHA1(de369cb4a7df147b55168fa7aaf0b98c753b735e) )
	ROM_SYSTEM_BIOS( 1, "japan", "Japan (hack?)" ) // from a 'refurbished' Japanese cabinet, had label of the arcade distributor rather than original sticker however, and looks like a hack of above Asia ROM
	ROM_LOAD16_WORD_SWAP_BIOS( 1, "236-bios_japan_hack.sp1", 0x00000, 0x020000, CRC(02bf4426) SHA1(f4aa64bfe0b93e5df07b4fe2e0f638d91c7f2e71) )
	// Universe BIOS 2.2 and later allow joystick play as a cheat
	NEOGEO_UNIBIOS_2_2_AND_NEWER(2)

	ROM_REGION( 0x30000, "cslot1:audiocpu", 0 )
	ROM_LOAD( "236-m1.m1", 0x00000, 0x20000, CRC(880a1abd) SHA1(905afa157aba700e798243b842792e50729b19a0) ) /* TC531001 */
	ROM_RELOAD( 0x10000, 0x20000 )

	ROM_REGION( 0x300000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "236-v1.v1", 0x000000, 0x200000, CRC(5f89c3b4) SHA1(dc8fd561cf8dfdd41696dcf14ea8d2d0ac4eec4b) ) /* TC5316200 */
	ROM_LOAD( "236-v2.v2", 0x200000, 0x100000, CRC(72e3add7) SHA1(dc49f05274693255416d2e41ecc088027b798eb9) ) /* TC538200 */

	ROM_REGION( 0x0800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "236-c1.c1", 0x000000, 0x400000, CRC(c1d47902) SHA1(727001c34f979226fc8f581113ce2aaac4fc0d42) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "236-c2.c2", 0x000001, 0x400000, CRC(e15f972e) SHA1(6a329559c57a67be73a6733513b59e9e6c8d61cc) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

/****************************************
 ID-0237
 . ??M-2370
 NEO-MVS PROG 4096 / NEO-MVS CHA42G-3B
****************************************/

ROM_START( popbounc ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "237-p1.p1", 0x000000, 0x100000, CRC(be96e44f) SHA1(43679da8664fbb491103a1108040ddf94d59fc2b) ) /* TC538200 */

	NEO_SFIX_128K( "237-s1.s1", CRC(b61cf595) SHA1(b14f8b78af7c634d41cf34d36b11b116e61f7342) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "237-m1.m1", CRC(d4c946dd) SHA1(6ca09040b5db8d89511d627954c783154d58ab01) ) /* TC531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "237-v1.v1", 0x000000, 0x200000, CRC(edcb1beb) SHA1(62f086b9968b366b59276ee4ae3c32c4d76fc6ce) ) /* TC5316200 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "237-c1.c1", 0x000000, 0x200000, CRC(eda42d66) SHA1(2735538fcb9dc0c16e043a8728c8b642650189f4) ) /* Plane 0,1 */ /* TC5316200 */
	ROM_LOAD16_BYTE( "237-c2.c2", 0x000001, 0x200000, CRC(5e633c65) SHA1(9a82107caf027317c173c1c1ef676f0fdeea79b2) ) /* Plane 2,3 */ /* TC5316200 */
ROM_END

/****************************************
 ID-0238
 . ??M-2380
 NEO-MVS PROGBK1 / NEO-MVS CHA256
****************************************/

// newer, sports lives setting in service mode plus other minor differences
ROM_START( shocktro ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "238-pg1.p1", 0x000000, 0x100000, CRC(efedf8dc) SHA1(f638df9bf7aa7d514ee2bccfc7f2adbf39ca83fc) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "238-p2.sp2", 0x100000, 0x400000, CRC(5b4a09c5) SHA1(de04036cba2da4bb2da73d902d1822b82b4f67a9) ) /* TC5332205 */

	NEO_SFIX_128K( "238-s1.s1", CRC(1f95cedb) SHA1(adfa74868147fd260481e4c387d254d3b6de83f4) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "238-m1.m1", CRC(075b9518) SHA1(ac21b88a860b9572bf24432b4cadcc96d108055d) ) /* TC531001 */

	ROM_REGION( 0x600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "238-v1.v1", 0x000000, 0x400000, CRC(260c0bef) SHA1(9c4f80ce4bb205afed11bb8b8926d20748eb5512) ) /* TC5332204 */
	ROM_LOAD( "238-v2.v2", 0x400000, 0x200000, CRC(4ad7d59e) SHA1(bfdf2684f7f38af4e75ad0068ff9463dc2601598) ) /* TC5316200 */

	ROM_REGION( 0x2000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "238-c1.c1", 0x0000000, 0x400000, CRC(90c6a181) SHA1(a381bc8449718814ff12b3a4f7fc4d1bb7ea1631) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "238-c2.c2", 0x0000001, 0x400000, CRC(888720f0) SHA1(cd4d65df8d3ef0dbcca2b7f3f803f45c457f5beb) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "238-c3.c3", 0x0800000, 0x400000, CRC(2c393aa3) SHA1(1cd7cebe5861a2d65f1d6615dd7752162e573a02) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "238-c4.c4", 0x0800001, 0x400000, CRC(b9e909eb) SHA1(33cc9b2d13e4ed2ab6040ff582a53dc9bca402e0) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "238-c5.c5", 0x1000000, 0x400000, CRC(c22c68eb) SHA1(a4b04118b1b1909d3b76be8d9ee5d97db6120600) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "238-c6.c6", 0x1000001, 0x400000, CRC(119323cd) SHA1(05a9d4b1fb4cc963b25452ff6f81e296e0c0b2a1) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "238-c7.c7", 0x1800000, 0x400000, CRC(a72ce7ed) SHA1(05b151554bd7af09ccf554a17bc3c75a0512faaf) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "238-c8.c8", 0x1800001, 0x400000, CRC(1c7c2efb) SHA1(b055ee43cbdaf9a3cb19e4e1f9dd2c40bde69d70) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

ROM_START( shocktroa ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "238-p1.p1",  0x000000, 0x100000, CRC(5677456f) SHA1(f76169fa5c90871d65e2a16fd1bb036c90533ac8) )
	ROM_LOAD16_WORD_SWAP( "238-p2.sp2", 0x100000, 0x400000, CRC(5b4a09c5) SHA1(de04036cba2da4bb2da73d902d1822b82b4f67a9) ) /* TC5332205 */

	NEO_SFIX_128K( "238-s1.s1", CRC(1f95cedb) SHA1(adfa74868147fd260481e4c387d254d3b6de83f4) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "238-m1.m1", CRC(075b9518) SHA1(ac21b88a860b9572bf24432b4cadcc96d108055d) ) /* TC531001 */

	ROM_REGION( 0x600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "238-v1.v1", 0x000000, 0x400000, CRC(260c0bef) SHA1(9c4f80ce4bb205afed11bb8b8926d20748eb5512) ) /* TC5332204 */
	ROM_LOAD( "238-v2.v2", 0x400000, 0x200000, CRC(4ad7d59e) SHA1(bfdf2684f7f38af4e75ad0068ff9463dc2601598) ) /* TC5316200 */

	ROM_REGION( 0x2000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "238-c1.c1", 0x0000000, 0x400000, CRC(90c6a181) SHA1(a381bc8449718814ff12b3a4f7fc4d1bb7ea1631) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "238-c2.c2", 0x0000001, 0x400000, CRC(888720f0) SHA1(cd4d65df8d3ef0dbcca2b7f3f803f45c457f5beb) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "238-c3.c3", 0x0800000, 0x400000, CRC(2c393aa3) SHA1(1cd7cebe5861a2d65f1d6615dd7752162e573a02) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "238-c4.c4", 0x0800001, 0x400000, CRC(b9e909eb) SHA1(33cc9b2d13e4ed2ab6040ff582a53dc9bca402e0) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "238-c5.c5", 0x1000000, 0x400000, CRC(c22c68eb) SHA1(a4b04118b1b1909d3b76be8d9ee5d97db6120600) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "238-c6.c6", 0x1000001, 0x400000, CRC(119323cd) SHA1(05a9d4b1fb4cc963b25452ff6f81e296e0c0b2a1) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "238-c7.c7", 0x1800000, 0x400000, CRC(a72ce7ed) SHA1(05b151554bd7af09ccf554a17bc3c75a0512faaf) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "238-c8.c8", 0x1800001, 0x400000, CRC(1c7c2efb) SHA1(b055ee43cbdaf9a3cb19e4e1f9dd2c40bde69d70) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

/****************************************
 ID-0239
 . NGM-2390
 NEO-MVS PROGBK1 / NEO-MVS CHA512Y
 . NGH-2390
 NEO-AEG PROGBK1Y / NEO-AEG CHA512Y
****************************************/

ROM_START( blazstar )
	ROM_REGION( 0x300000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "239-p1.p1",  0x000000, 0x100000, CRC(183682f8) SHA1(dcee1c2cf4a991ca1f9f2b40c4a738f21682807b) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "239-p2.sp2", 0x100000, 0x200000, CRC(9a9f4154) SHA1(f8805453d0995c8fa16cd9accfb7a990071ca630) ) /* TC5316200 */

	NEO_SFIX_128K( "239-s1.s1", CRC(d56cb498) SHA1(420ce56431dc7f3f7de84fcbc8c0a17b5eab205e) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "239-m1.m1", CRC(d31a3aea) SHA1(e23abfeb23052f0358edcf2c83401025fe632511) ) /* TC531001 */

	ROM_REGION( 0x800000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "239-v1.v1", 0x000000, 0x400000, CRC(1b8d5bf7) SHA1(67fc1f7e36e92a89cd1d415eb31a2892f57b0d04) ) /* TC5332204 */
	ROM_LOAD( "239-v2.v2", 0x400000, 0x400000, CRC(74cf0a70) SHA1(b00451a2a30de2517ae3eca35eb1fe985b950eb8) ) /* TC5332204 */

	ROM_REGION( 0x2000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "239-c1.c1", 0x0000000, 0x400000, CRC(84f6d584) SHA1(ff36db8504611b0d8d942d1e24823ff71e4aeb37) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "239-c2.c2", 0x0000001, 0x400000, CRC(05a0cb22) SHA1(4abe03e7f3a86f277131d413a3151c7b9c3646c8) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "239-c3.c3", 0x0800000, 0x400000, CRC(5fb69c9e) SHA1(77b96518d8ad8ad120537e0f8ba65d69d1c33566) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "239-c4.c4", 0x0800001, 0x400000, CRC(0be028c4) SHA1(d3f8b37786ca7838c3525895a7f2b49afc8530d4) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "239-c5.c5", 0x1000000, 0x400000, CRC(74bae5f8) SHA1(812c9a31f0721c2971a316b084ce69337dbe3747) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "239-c6.c6", 0x1000001, 0x400000, CRC(4e0700d2) SHA1(cd059fb713c403208923c17e1e8ef02fcfd2fe8d) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "239-c7.c7", 0x1800000, 0x400000, CRC(010ff4fd) SHA1(2571d406442f007a7458d8ccb0939a9201c9c9bf) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "239-c8.c8", 0x1800001, 0x400000, CRC(db60460e) SHA1(a5cb27c0983c8b400d96fd0828ef0639a66d4dba) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

/****************************************
 ID-0240
 . NGM-2400
 NEO-MVS PROGBK1 / NEO-MVS CHA512Y
 . NGH-2400
 NEO-AEG PROGBK1Y / NEO-AEG CHA512Y
****************************************/

ROM_START( rbff2 ) /* MVS VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "240-p1.p1",  0x000000, 0x100000, CRC(80e41205) SHA1(8f83eb8ff54be4ec40f8a0dd2cbe56c54908d00a) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "240-p2.sp2", 0x100000, 0x400000, CRC(960aa88d) SHA1(3d9e785891871af90313f178dca2724633406674) ) /* TC5332205 */

	NEO_SFIX_128K( "240-s1.s1", CRC(da3b40de) SHA1(e6bf74e057ac6fe1f249a7547f13ba7fbc694561) ) /* TC531000 */

	NEO_BIOS_AUDIO_256K( "240-m1.m1", CRC(ed482791) SHA1(1f54a45967cb7842c33aa24be322c9f33ff75ac3) ) /* TC532000 */

	ROM_REGION( 0x0e00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "240-v1.v1", 0x000000, 0x400000, CRC(f796265a) SHA1(736dff37eb91fc856b4d189249fb0de9b6c0813a) ) /* TC5332204 */
	ROM_LOAD( "240-v2.v2", 0x400000, 0x400000, CRC(2cb3f3bb) SHA1(697e677890892f4b028c9a27c66809ca0a8a9b18) ) /* TC5332204 */
	ROM_LOAD( "240-v3.v3", 0x800000, 0x400000, CRC(8fe1367a) SHA1(093d7a4ac2b54ad7ffb2dc316fe29415f7a99535) ) /* TC5332204 */
	ROM_LOAD( "240-v4.v4", 0xc00000, 0x200000, CRC(996704d8) SHA1(0bf7a1d0660199dedf3c25be757eeab75cc6147e) ) /* TC5316200 */

	ROM_REGION( 0x3000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "240-c1.c1", 0x0000000, 0x800000, CRC(effac504) SHA1(e36a96e7369b02c7e839b5abf3c6799453ba1927) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "240-c2.c2", 0x0000001, 0x800000, CRC(ed182d44) SHA1(a9fc0a3a786bf067c129ec7220df65953dff804f) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "240-c3.c3", 0x1000000, 0x800000, CRC(22e0330a) SHA1(0fe7f6a8aeba7f17dbb278e85003969ff10d3cd2) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "240-c4.c4", 0x1000001, 0x800000, CRC(c19a07eb) SHA1(139eac8b51cadf328dd42d8109f4e2463f57230c) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "240-c5.c5", 0x2000000, 0x800000, CRC(244dff5a) SHA1(156548156d3ceaa808d0053d0749af2526a3943e) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "240-c6.c6", 0x2000001, 0x800000, CRC(4609e507) SHA1(bb17f50a377dddb77c1eeda5944a7bcbf0cca5f7) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

ROM_START( rbff2h ) /* AES VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "240-pg1.p1", 0x000000, 0x100000, CRC(b6969780) SHA1(e3373d18e0f0724d69efb8024a27cca121f1b5b2) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "240-p2.sp2", 0x100000, 0x400000, CRC(960aa88d) SHA1(3d9e785891871af90313f178dca2724633406674) ) /* TC5332205 */

	NEO_SFIX_128K( "240-s1.s1", CRC(da3b40de) SHA1(e6bf74e057ac6fe1f249a7547f13ba7fbc694561) ) /* TC531000 */

	NEO_BIOS_AUDIO_256K( "240-m1.m1", CRC(ed482791) SHA1(1f54a45967cb7842c33aa24be322c9f33ff75ac3) ) /* TC532000 */

	ROM_REGION( 0x0e00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "240-v1.v1", 0x000000, 0x400000, CRC(f796265a) SHA1(736dff37eb91fc856b4d189249fb0de9b6c0813a) ) /* TC5332204 */
	ROM_LOAD( "240-v2.v2", 0x400000, 0x400000, CRC(2cb3f3bb) SHA1(697e677890892f4b028c9a27c66809ca0a8a9b18) ) /* TC5332204 */
	ROM_LOAD( "240-v3.v3", 0x800000, 0x400000, CRC(8fe1367a) SHA1(093d7a4ac2b54ad7ffb2dc316fe29415f7a99535) ) /* TC5332204 */
	ROM_LOAD( "240-v4.v4", 0xc00000, 0x200000, CRC(996704d8) SHA1(0bf7a1d0660199dedf3c25be757eeab75cc6147e) ) /* TC5316200 */

	ROM_REGION( 0x3000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "240-c1.c1", 0x0000000, 0x800000, CRC(effac504) SHA1(e36a96e7369b02c7e839b5abf3c6799453ba1927) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "240-c2.c2", 0x0000001, 0x800000, CRC(ed182d44) SHA1(a9fc0a3a786bf067c129ec7220df65953dff804f) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "240-c3.c3", 0x1000000, 0x800000, CRC(22e0330a) SHA1(0fe7f6a8aeba7f17dbb278e85003969ff10d3cd2) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "240-c4.c4", 0x1000001, 0x800000, CRC(c19a07eb) SHA1(139eac8b51cadf328dd42d8109f4e2463f57230c) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "240-c5.c5", 0x2000000, 0x800000, CRC(244dff5a) SHA1(156548156d3ceaa808d0053d0749af2526a3943e) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "240-c6.c6", 0x2000001, 0x800000, CRC(4609e507) SHA1(bb17f50a377dddb77c1eeda5944a7bcbf0cca5f7) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

ROM_START( rbff2k ) /* KOREAN VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "140-p1k.p1", 0x000000, 0x100000, CRC(965edee1) SHA1(7f4b947b19ccfee32fc73e4fd89645eb313b5c77) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "240-p2.sp2", 0x100000, 0x400000, CRC(960aa88d) SHA1(3d9e785891871af90313f178dca2724633406674) ) /* TC5332205 */

	NEO_SFIX_128K( "240-s1.s1", CRC(da3b40de) SHA1(e6bf74e057ac6fe1f249a7547f13ba7fbc694561) ) /* TC531000 */

	NEO_BIOS_AUDIO_256K( "240-m1.m1", CRC(ed482791) SHA1(1f54a45967cb7842c33aa24be322c9f33ff75ac3) ) /* TC532000 */

	ROM_REGION( 0x0e00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "240-v1.v1", 0x000000, 0x400000, CRC(f796265a) SHA1(736dff37eb91fc856b4d189249fb0de9b6c0813a) ) /* TC5332204 */
	ROM_LOAD( "240-v2.v2", 0x400000, 0x400000, CRC(2cb3f3bb) SHA1(697e677890892f4b028c9a27c66809ca0a8a9b18) ) /* TC5332204 */
	ROM_LOAD( "240-v3.v3", 0x800000, 0x400000, CRC(8fe1367a) SHA1(093d7a4ac2b54ad7ffb2dc316fe29415f7a99535) ) /* TC5332204 */
	ROM_LOAD( "240-v4.v4", 0xc00000, 0x200000, CRC(996704d8) SHA1(0bf7a1d0660199dedf3c25be757eeab75cc6147e) ) /* TC5316200 */

	ROM_REGION( 0x3000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "240-c1.c1", 0x0000000, 0x800000, CRC(effac504) SHA1(e36a96e7369b02c7e839b5abf3c6799453ba1927) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "240-c2.c2", 0x0000001, 0x800000, CRC(ed182d44) SHA1(a9fc0a3a786bf067c129ec7220df65953dff804f) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "240-c3.c3", 0x1000000, 0x800000, CRC(22e0330a) SHA1(0fe7f6a8aeba7f17dbb278e85003969ff10d3cd2) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "240-c4.c4", 0x1000001, 0x800000, CRC(c19a07eb) SHA1(139eac8b51cadf328dd42d8109f4e2463f57230c) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "240-c5.c5", 0x2000000, 0x800000, CRC(244dff5a) SHA1(156548156d3ceaa808d0053d0749af2526a3943e) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "240-c6.c6", 0x2000001, 0x800000, CRC(4609e507) SHA1(bb17f50a377dddb77c1eeda5944a7bcbf0cca5f7) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

/****************************************
 ID-0241
 . NGM-2410
 NEO-MVS PROGBK1 / NEO-MVS CHA512Y
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 . NGH-2410
 NEO-AEG PROGBK1Y / NEO-AEG CHA512Y
****************************************/

ROM_START( mslug2 ) /* MVS AND AES VERSION */
	ROM_REGION( 0x300000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "241-p1.p1",  0x000000, 0x100000, CRC(2a53c5da) SHA1(5a6aba482cac588a6c2c51179c95b487c6e11899) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "241-p2.sp2", 0x100000, 0x200000, CRC(38883f44) SHA1(fcf34b8c6e37774741542393b963635412484a27) ) /* TC5316200 */

	NEO_SFIX_128K( "241-s1.s1", CRC(f3d32f0f) SHA1(2dc38b7dfd3ff14f64d5c0733c510b6bb8c692d0) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "241-m1.m1", CRC(94520ebd) SHA1(f8a1551cebcb91e416f30f50581feed7f72899e9) ) /* TC531001 */

	ROM_REGION( 0x800000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "241-v1.v1", 0x000000, 0x400000, CRC(99ec20e8) SHA1(80597707f1fe115eed1941bb0701fc00790ad504) ) /* TC5332204 */
	ROM_LOAD( "241-v2.v2", 0x400000, 0x400000, CRC(ecb16799) SHA1(b4b4ddc680836ed55942c66d7dfe756314e02211) ) /* TC5332204 */

	ROM_REGION( 0x2000000, "cslot1:sprites", 0 )
	/* Different layout with 8xC (16 mbit) also exists; naming sheme 241-Cx */
	ROM_LOAD16_BYTE( "241-c1.c1", 0x0000000, 0x800000, CRC(394b5e0d) SHA1(4549926f5054ee6aa7689cf920be0327e3908a50) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "241-c2.c2", 0x0000001, 0x800000, CRC(e5806221) SHA1(1e5475cfab129c77acc610f09369ca42ba5aafa5) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "241-c3.c3", 0x1000000, 0x800000, CRC(9f6bfa6f) SHA1(a4319b48004e723f81a980887678e3e296049a53) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "241-c4.c4", 0x1000001, 0x800000, CRC(7d3e306f) SHA1(1499316fb381775218d897b81a6a0c3465d1a37c) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END


/****************************************
 ID-0941 - community produced hack
****************************************/
ROM_START( mslug2t )
	ROM_REGION( 0x300000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "941-p1.p1",  0x000000, 0x100000, CRC(df5d6fbc) SHA1(b9cc3e29afc12dc98daac9afb4f94e2cdd8b455c) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "241-p2.sp2", 0x100000, 0x200000, CRC(38883f44) SHA1(fcf34b8c6e37774741542393b963635412484a27) ) /* TC5316200 */

	NEO_SFIX_128K( "241-s1.s1", CRC(f3d32f0f) SHA1(2dc38b7dfd3ff14f64d5c0733c510b6bb8c692d0) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "241-m1.m1", CRC(94520ebd) SHA1(f8a1551cebcb91e416f30f50581feed7f72899e9) ) /* TC531001 */

	ROM_REGION( 0x800000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "241-v1.v1", 0x000000, 0x400000, CRC(99ec20e8) SHA1(80597707f1fe115eed1941bb0701fc00790ad504) ) /* TC5332204 */
	ROM_LOAD( "241-v2.v2", 0x400000, 0x400000, CRC(ecb16799) SHA1(b4b4ddc680836ed55942c66d7dfe756314e02211) ) /* TC5332204 */

	ROM_REGION( 0x2000000, "cslot1:sprites", 0 )
	/* Different layout with 8xC (16 mbit) also exists; naming sheme 241-Cx */
	ROM_LOAD16_BYTE( "241-c1.c1", 0x0000000, 0x800000, CRC(394b5e0d) SHA1(4549926f5054ee6aa7689cf920be0327e3908a50) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "241-c2.c2", 0x0000001, 0x800000, CRC(e5806221) SHA1(1e5475cfab129c77acc610f09369ca42ba5aafa5) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "241-c3.c3", 0x1000000, 0x800000, CRC(9f6bfa6f) SHA1(a4319b48004e723f81a980887678e3e296049a53) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "241-c4.c4", 0x1000001, 0x800000, CRC(7d3e306f) SHA1(1499316fb381775218d897b81a6a0c3465d1a37c) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END


/****************************************
 ID-0242
 . NGM-2420
 NEO-MVS PROGSF1 (1998.6.17) (protected board) / NEO-MVS CHA512Y
 NEO-MVS PROGSF1E (1998.6.18) (protected board) / NEO-MVS CHA512Y
 NEO-MVS PROGBK1 / NEO-MVS CHA512Y
 . NGH-2420
 NEO-AEG PROGBK1Y / NEO-AEG CHA512Y
****************************************/

ROM_START( kof98 ) /* encrypted code + protection */ /* MVS VERSION */
	/* This set uses NEO-MVS PROGSF1 board */
	ROM_REGION( 0x600000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "242-p1.p1",  0x000000, 0x200000, CRC(8893df89) SHA1(0452828785110601c65f667209fc2d2926cd3751) ) /* mask rom 16mbit */
	ROM_LOAD16_WORD_SWAP( "242-p2.sp2", 0x200000, 0x400000, CRC(980aba4c) SHA1(5e735929ec6c3ca5b2efae3c7de47bcbb8ade2c5) ) /* TC5332205 */

	NEO_SFIX_128K( "242-s1.s1", CRC(7f7b4805) SHA1(80ee6e5d0ece9c34ebca54b043a7cb33f9ff6b92) ) /* TC531000 */

	NEO_BIOS_AUDIO_256K( "242-m1.m1", CRC(4ef7016b) SHA1(4182235e963bd70d398a79abeb54ab4d62887c48) ) /* TC532000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "242-v1.v1", 0x000000, 0x400000, CRC(b9ea8051) SHA1(49606f64eb249263b3341b4f50cc1763c390b2af) ) /* TC5332204 */
	ROM_LOAD( "242-v2.v2", 0x400000, 0x400000, CRC(cc11106e) SHA1(d3108bc05c9bf041d4236b2fa0c66b013aa8db1b) ) /* TC5332204 */
	ROM_LOAD( "242-v3.v3", 0x800000, 0x400000, CRC(044ea4e1) SHA1(062a2f2e52098d73bc31c9ad66f5db8080395ce8) ) /* TC5332204 */
	ROM_LOAD( "242-v4.v4", 0xc00000, 0x400000, CRC(7985ea30) SHA1(54ed5f0324de6164ea81943ebccb3e8d298368ec) ) /* TC5332204 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "242-c1.c1", 0x0000000, 0x800000, CRC(e564ecd6) SHA1(78f22787a204f26bae9b2b1c945ddbc27143352f) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c2.c2", 0x0000001, 0x800000, CRC(bd959b60) SHA1(2c97c59e77c9a3fe7d664e741d37944f3d56c10b) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c3.c3", 0x1000000, 0x800000, CRC(22127b4f) SHA1(bd0d00f889d9da7c6ac48f287d9ed8c605ae22cf) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c4.c4", 0x1000001, 0x800000, CRC(0b4fa044) SHA1(fa13c3764fae6b035a626601bc43629f1ebaaffd) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c5.c5", 0x2000000, 0x800000, CRC(9d10bed3) SHA1(4d44addc7c808649bfb03ec45fb9529da413adff) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c6.c6", 0x2000001, 0x800000, CRC(da07b6a2) SHA1(9c3f0da7cde1ffa8feca89efc88f07096e502acf) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c7.c7", 0x3000000, 0x800000, CRC(f6d7a38a) SHA1(dd295d974dd4a7e5cb26a3ef3febcd03f28d522b) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c8.c8", 0x3000001, 0x800000, CRC(c823e045) SHA1(886fbf64bcb58bc4eabb1fc9262f6ac9901a0f28) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

ROM_START( kof98a ) /* encrypted code + protection */ /* MVS VERSION */
	/* This set uses NEO-MVS PROGSF1E board; same rom data as in kof98 is used */
	ROM_REGION( 0x600000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "242-p1.p1",   0x000000, 0x200000, CRC(8893df89) SHA1(0452828785110601c65f667209fc2d2926cd3751) ) /* mask rom 16mbit */
	ROM_LOAD16_WORD_SWAP( "242-ep1.ep1", 0x200000, 0x200000, CRC(3f74a000) SHA1(e887e0ac232683bd28703e08c4055fd0ea36402c) ) /* M27C160 */
	ROM_LOAD16_WORD_SWAP( "242-ep2.ep2", 0x400000, 0x200000, CRC(6e474841) SHA1(0ce401277f9c53435ea00b930efe361c8d25a7d9) ) /* M27C160 */

	NEO_SFIX_128K( "242-s1.s1", CRC(7f7b4805) SHA1(80ee6e5d0ece9c34ebca54b043a7cb33f9ff6b92) ) /* TC531000 */

	NEO_BIOS_AUDIO_256K( "242-m1.m1", CRC(4ef7016b) SHA1(4182235e963bd70d398a79abeb54ab4d62887c48) ) /* TC532000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "242-v1.v1", 0x000000, 0x400000, CRC(b9ea8051) SHA1(49606f64eb249263b3341b4f50cc1763c390b2af) ) /* TC5332204 */
	ROM_LOAD( "242-v2.v2", 0x400000, 0x400000, CRC(cc11106e) SHA1(d3108bc05c9bf041d4236b2fa0c66b013aa8db1b) ) /* TC5332204 */
	ROM_LOAD( "242-v3.v3", 0x800000, 0x400000, CRC(044ea4e1) SHA1(062a2f2e52098d73bc31c9ad66f5db8080395ce8) ) /* TC5332204 */
	ROM_LOAD( "242-v4.v4", 0xc00000, 0x400000, CRC(7985ea30) SHA1(54ed5f0324de6164ea81943ebccb3e8d298368ec) ) /* TC5332204 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "242-c1.c1", 0x0000000, 0x800000, CRC(e564ecd6) SHA1(78f22787a204f26bae9b2b1c945ddbc27143352f) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c2.c2", 0x0000001, 0x800000, CRC(bd959b60) SHA1(2c97c59e77c9a3fe7d664e741d37944f3d56c10b) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c3.c3", 0x1000000, 0x800000, CRC(22127b4f) SHA1(bd0d00f889d9da7c6ac48f287d9ed8c605ae22cf) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c4.c4", 0x1000001, 0x800000, CRC(0b4fa044) SHA1(fa13c3764fae6b035a626601bc43629f1ebaaffd) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c5.c5", 0x2000000, 0x800000, CRC(9d10bed3) SHA1(4d44addc7c808649bfb03ec45fb9529da413adff) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c6.c6", 0x2000001, 0x800000, CRC(da07b6a2) SHA1(9c3f0da7cde1ffa8feca89efc88f07096e502acf) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c7.c7", 0x3000000, 0x800000, CRC(f6d7a38a) SHA1(dd295d974dd4a7e5cb26a3ef3febcd03f28d522b) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c8.c8", 0x3000001, 0x800000, CRC(c823e045) SHA1(886fbf64bcb58bc4eabb1fc9262f6ac9901a0f28) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

ROM_START( kof98k ) /* encrypted code + protection, only z80 rom is different to kof98 */ /* KOREAN VERSION */
	ROM_REGION( 0x600000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "242-p1.p1",  0x000000, 0x200000, CRC(8893df89) SHA1(0452828785110601c65f667209fc2d2926cd3751) ) /* mask rom 16mbit */
	ROM_LOAD16_WORD_SWAP( "242-p2.sp2", 0x200000, 0x400000, CRC(980aba4c) SHA1(5e735929ec6c3ca5b2efae3c7de47bcbb8ade2c5) ) /* TC5332205 */

	NEO_SFIX_128K( "242-s1.s1", CRC(7f7b4805) SHA1(80ee6e5d0ece9c34ebca54b043a7cb33f9ff6b92) ) /* TC531000 */

	/* Correct chip label */
	NEO_BIOS_AUDIO_256K( "242-m1k.m1", CRC(ce12da0c) SHA1(e7c01dae2852d543d1a58d55735239f6a5aa05a5) ) /* mask rom TC532000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "242-v1.v1", 0x000000, 0x400000, CRC(b9ea8051) SHA1(49606f64eb249263b3341b4f50cc1763c390b2af) ) /* TC5332204 */
	ROM_LOAD( "242-v2.v2", 0x400000, 0x400000, CRC(cc11106e) SHA1(d3108bc05c9bf041d4236b2fa0c66b013aa8db1b) ) /* TC5332204 */
	ROM_LOAD( "242-v3.v3", 0x800000, 0x400000, CRC(044ea4e1) SHA1(062a2f2e52098d73bc31c9ad66f5db8080395ce8) ) /* TC5332204 */
	ROM_LOAD( "242-v4.v4", 0xc00000, 0x400000, CRC(7985ea30) SHA1(54ed5f0324de6164ea81943ebccb3e8d298368ec) ) /* TC5332204 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "242-c1.c1", 0x0000000, 0x800000, CRC(e564ecd6) SHA1(78f22787a204f26bae9b2b1c945ddbc27143352f) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c2.c2", 0x0000001, 0x800000, CRC(bd959b60) SHA1(2c97c59e77c9a3fe7d664e741d37944f3d56c10b) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c3.c3", 0x1000000, 0x800000, CRC(22127b4f) SHA1(bd0d00f889d9da7c6ac48f287d9ed8c605ae22cf) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c4.c4", 0x1000001, 0x800000, CRC(0b4fa044) SHA1(fa13c3764fae6b035a626601bc43629f1ebaaffd) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c5.c5", 0x2000000, 0x800000, CRC(9d10bed3) SHA1(4d44addc7c808649bfb03ec45fb9529da413adff) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c6.c6", 0x2000001, 0x800000, CRC(da07b6a2) SHA1(9c3f0da7cde1ffa8feca89efc88f07096e502acf) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c7.c7", 0x3000000, 0x800000, CRC(f6d7a38a) SHA1(dd295d974dd4a7e5cb26a3ef3febcd03f28d522b) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c8.c8", 0x3000001, 0x800000, CRC(c823e045) SHA1(886fbf64bcb58bc4eabb1fc9262f6ac9901a0f28) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

ROM_START( kof98ka ) /* encrypted code + protection, only z80 rom is different to kof98 */ /* KOREAN VERSION */
	ROM_REGION( 0x600000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "242-p1.p1",  0x000000, 0x200000, CRC(8893df89) SHA1(0452828785110601c65f667209fc2d2926cd3751) ) /* mask rom 16mbit */
	ROM_LOAD16_WORD_SWAP( "242-p2.sp2", 0x200000, 0x400000, CRC(980aba4c) SHA1(5e735929ec6c3ca5b2efae3c7de47bcbb8ade2c5) ) /* TC5332205 */

	NEO_SFIX_128K( "242-s1.s1", CRC(7f7b4805) SHA1(80ee6e5d0ece9c34ebca54b043a7cb33f9ff6b92) ) /* TC531000 */

	/* Correct chip label */
	NEO_BIOS_AUDIO_256K( "242-mg1k.m1", CRC(ce9fb07c) SHA1(631d995f1291dd803fb069f3b25e7b9ed30d8649) ) /* 27C2000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "242-v1.v1", 0x000000, 0x400000, CRC(b9ea8051) SHA1(49606f64eb249263b3341b4f50cc1763c390b2af) ) /* TC5332204 */
	ROM_LOAD( "242-v2.v2", 0x400000, 0x400000, CRC(cc11106e) SHA1(d3108bc05c9bf041d4236b2fa0c66b013aa8db1b) ) /* TC5332204 */
	ROM_LOAD( "242-v3.v3", 0x800000, 0x400000, CRC(044ea4e1) SHA1(062a2f2e52098d73bc31c9ad66f5db8080395ce8) ) /* TC5332204 */
	ROM_LOAD( "242-v4.v4", 0xc00000, 0x400000, CRC(7985ea30) SHA1(54ed5f0324de6164ea81943ebccb3e8d298368ec) ) /* TC5332204 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "242-c1.c1", 0x0000000, 0x800000, CRC(e564ecd6) SHA1(78f22787a204f26bae9b2b1c945ddbc27143352f) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c2.c2", 0x0000001, 0x800000, CRC(bd959b60) SHA1(2c97c59e77c9a3fe7d664e741d37944f3d56c10b) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c3.c3", 0x1000000, 0x800000, CRC(22127b4f) SHA1(bd0d00f889d9da7c6ac48f287d9ed8c605ae22cf) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c4.c4", 0x1000001, 0x800000, CRC(0b4fa044) SHA1(fa13c3764fae6b035a626601bc43629f1ebaaffd) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c5.c5", 0x2000000, 0x800000, CRC(9d10bed3) SHA1(4d44addc7c808649bfb03ec45fb9529da413adff) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c6.c6", 0x2000001, 0x800000, CRC(da07b6a2) SHA1(9c3f0da7cde1ffa8feca89efc88f07096e502acf) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c7.c7", 0x3000000, 0x800000, CRC(f6d7a38a) SHA1(dd295d974dd4a7e5cb26a3ef3febcd03f28d522b) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c8.c8", 0x3000001, 0x800000, CRC(c823e045) SHA1(886fbf64bcb58bc4eabb1fc9262f6ac9901a0f28) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

ROM_START( kof98h ) /* MVS AND AES VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "242-pn1.p1", 0x000000, 0x100000, CRC(61ac868a) SHA1(26577264aa72d6af272952a876fcd3775f53e3fa) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "242-p2.sp2", 0x100000, 0x400000, CRC(980aba4c) SHA1(5e735929ec6c3ca5b2efae3c7de47bcbb8ade2c5) ) /* TC5332205 */

	NEO_SFIX_128K( "242-s1.s1", CRC(7f7b4805) SHA1(80ee6e5d0ece9c34ebca54b043a7cb33f9ff6b92) ) /* TC531000 */

	NEO_BIOS_AUDIO_256K( "242-mg1.m1", CRC(4e7a6b1b) SHA1(b54d08f88713ed0271aa06f9f7c9c572ef555b1a) ) /* TC532000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "242-v1.v1", 0x000000, 0x400000, CRC(b9ea8051) SHA1(49606f64eb249263b3341b4f50cc1763c390b2af) ) /* TC5332204 */
	ROM_LOAD( "242-v2.v2", 0x400000, 0x400000, CRC(cc11106e) SHA1(d3108bc05c9bf041d4236b2fa0c66b013aa8db1b) ) /* TC5332204 */
	ROM_LOAD( "242-v3.v3", 0x800000, 0x400000, CRC(044ea4e1) SHA1(062a2f2e52098d73bc31c9ad66f5db8080395ce8) ) /* TC5332204 */
	ROM_LOAD( "242-v4.v4", 0xc00000, 0x400000, CRC(7985ea30) SHA1(54ed5f0324de6164ea81943ebccb3e8d298368ec) ) /* TC5332204 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "242-c1.c1", 0x0000000, 0x800000, CRC(e564ecd6) SHA1(78f22787a204f26bae9b2b1c945ddbc27143352f) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c2.c2", 0x0000001, 0x800000, CRC(bd959b60) SHA1(2c97c59e77c9a3fe7d664e741d37944f3d56c10b) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c3.c3", 0x1000000, 0x800000, CRC(22127b4f) SHA1(bd0d00f889d9da7c6ac48f287d9ed8c605ae22cf) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c4.c4", 0x1000001, 0x800000, CRC(0b4fa044) SHA1(fa13c3764fae6b035a626601bc43629f1ebaaffd) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c5.c5", 0x2000000, 0x800000, CRC(9d10bed3) SHA1(4d44addc7c808649bfb03ec45fb9529da413adff) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c6.c6", 0x2000001, 0x800000, CRC(da07b6a2) SHA1(9c3f0da7cde1ffa8feca89efc88f07096e502acf) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c7.c7", 0x3000000, 0x800000, CRC(f6d7a38a) SHA1(dd295d974dd4a7e5cb26a3ef3febcd03f28d522b) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "242-c8.c8", 0x3000001, 0x800000, CRC(c823e045) SHA1(886fbf64bcb58bc4eabb1fc9262f6ac9901a0f28) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

/****************************************
 ID-0243
 . NGM-2430
 NEO-MVS PROGBK1 / NEO-MVS CHA512Y
 . NGH-2430
 NEO-AEG PROGBK1Y / NEO-AEG CHA512Y
****************************************/

ROM_START( lastbld2 ) /* MVS AND AES VERSION */ /* later revision */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "243-pg1.p1",  0x000000, 0x100000, CRC(af1e6554) SHA1(bd8526f60c2472937728a5d933fbd19d899f2cba) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "243-pg2.sp2", 0x100000, 0x400000, CRC(add4a30b) SHA1(7db62564db49fe0218cbb35b119d62582a24d658) ) /* TC5332205 */

	NEO_SFIX_128K( "243-s1.s1", CRC(c9cd2298) SHA1(a9a18b5347f9dbe29a2ccb63fd4c8fd19537bf8b) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "243-m1.m1", CRC(acf12d10) SHA1(6e6b98cc1fa44f24a5168877559b0055e6957b60) ) /* TC531001 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "243-v1.v1", 0x000000, 0x400000, CRC(f7ee6fbb) SHA1(55137bcabeeb590e40a9b8a7c07dd106e4d12a90) ) /* TC5332204 */
	ROM_LOAD( "243-v2.v2", 0x400000, 0x400000, CRC(aa9e4df6) SHA1(a0b91f63e2552a8ad9e0d1af00e2c38288637161) ) /* TC5332204 */
	ROM_LOAD( "243-v3.v3", 0x800000, 0x400000, CRC(4ac750b2) SHA1(585a154acc67bd84ea5b944686b78ed082b768d9) ) /* TC5332204 */
	ROM_LOAD( "243-v4.v4", 0xc00000, 0x400000, CRC(f5c64ba6) SHA1(2eac455def8c27090862cc042f65a3a8aad88283) ) /* TC5332204 */

	ROM_REGION( 0x3000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "243-c1.c1", 0x0000000, 0x800000, CRC(5839444d) SHA1(0616921c4cce20422563578bd0e806d359508599) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "243-c2.c2", 0x0000001, 0x800000, CRC(dd087428) SHA1(ca27fdb60425664956a18c021ea465f452fb1527) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "243-c3.c3", 0x1000000, 0x800000, CRC(6054cbe0) SHA1(ec2f65e9c930250ee25fd064ee5ae76a7a9c61d9) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "243-c4.c4", 0x1000001, 0x800000, CRC(8bd2a9d2) SHA1(0935df65cd2b0891a708bcc0f1c188148058d4b5) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "243-c5.c5", 0x2000000, 0x800000, CRC(6a503dcf) SHA1(23241b16d7e20f923d41186b29487ab922c7f530) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "243-c6.c6", 0x2000001, 0x800000, CRC(ec9c36d0) SHA1(e145e9e359000dda6e1dfe95a996bc6d29cfca21) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

/****************************************
 ID-0244
 . ??M-2440
 NEO-MVS PROGBK1 / NEO-MVS CHA512Y
 . ??H-2440
****************************************/

ROM_START( neocup98 )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "244-p1.p1", 0x100000, 0x100000, CRC(f8fdb7a5) SHA1(f34ee5d1c24e70427d05ef488f46906dbd9f9950) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "244-s1.s1", CRC(9bddb697) SHA1(2f479bcd5a433201168792a578de3057252d649f) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "244-m1.m1", CRC(a701b276) SHA1(055550ebc650835bcf8ea4457b2c91bd73e21281) ) /* TC531001 */

	ROM_REGION( 0x600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "244-v1.v1", 0x000000, 0x400000, CRC(79def46d) SHA1(63414235de2e177654508f1c840040424f8993e6) ) /* TC5332204 */
	ROM_LOAD( "244-v2.v2", 0x400000, 0x200000, CRC(b231902f) SHA1(9209772e947a2c7ac31b49dd613bf2eab0cb3358) ) /* TC5316200 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "244-c1.c1", 0x000000, 0x800000, CRC(c7a62b23) SHA1(4534ecc9ade69c543188c66229dcad89dbc48668) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "244-c2.c2", 0x000001, 0x800000, CRC(33aa0f35) SHA1(3443c7765c6aa177003d42bbfcac9f31d1e12575) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

/****************************************
 ID-0245
 . ??M-2450
 NEO-MVS PROGBK1 / NEO-MVS CHA512Y
****************************************/

ROM_START( breakrev ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "245-p1.p1", 0x100000, 0x100000, CRC(c828876d) SHA1(1dcba850e5cf8219d0945612cfded6d20ca8682a) ) /* mask rom TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "245-s1.s1", CRC(e7660a5d) SHA1(1cd54964ba60b245ea57d9daf0e27b572b815d21) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "245-m1.m1", CRC(00f31c66) SHA1(8488598415c9b74bce00e05b31d96e3d1625c20d) ) /* mask rom TC531001 */

	ROM_REGION( 0x800000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "245-v1.v1", 0x000000, 0x400000, CRC(e255446c) SHA1(b3933340d49d4ba581f3bf1af7ad69d786205790) ) /* mask rom TC5332204 */
	ROM_LOAD( "245-v2.v2", 0x400000, 0x400000, CRC(9068198a) SHA1(71819b0475a5e173a2f9a6e4ff19a94655141c3c) ) /* mask rom TC5332204 */

	ROM_REGION( 0x1800000, "cslot1:sprites", 0 )
	/* The chip labels and sizes are correct */
	ROM_LOAD16_BYTE( "245-c1.c1", 0x0000000, 0x400000, CRC(68d4ae76) SHA1(2e820067f6963669f104bebf19e865fe4127b4dd) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "245-c2.c2", 0x0000001, 0x400000, CRC(fdee05cd) SHA1(efc4ffd790953ac7c25d5f045c64a9b49d24b096) ) /* Plane 2,3 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "245-c3.c3", 0x0800000, 0x400000, CRC(645077f3) SHA1(0ae74f3b4b3b88f128c6d8c0f35ffa53f5d67ef2) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "245-c4.c4", 0x0800001, 0x400000, CRC(63aeb74c) SHA1(9ff6930c0c3d79b46b86356e8565ce4fcd69ac38) ) /* Plane 2,3 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "245-c5.c5", 0x1000000, 0x400000, CRC(b5f40e7f) SHA1(b332bac64dbb9a9dd66c5315f47ea08634d36f45) ) /* Plane 0,1 */ /* mask rom TC5332205 */
	ROM_LOAD16_BYTE( "245-c6.c6", 0x1000001, 0x400000, CRC(d0337328) SHA1(dff86b75dc283bd4512557a5c64f16e6be6c16e4) ) /* Plane 2,3 */ /* mask rom TC5332205 */
ROM_END

/****************************************
 ID-0246
 . NGM-2460
 NEO-MVS PROGBK1 / NEO-MVS CHA512Y
 . NGH-2460
****************************************/

ROM_START( shocktr2 )
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "246-p1.p1",  0x000000, 0x100000, CRC(6d4b7781) SHA1(3c9d53d5da9842bfd45037c919064dda3fb2e089) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "246-p2.sp2", 0x100000, 0x400000, CRC(72ea04c3) SHA1(4fb1d22c30f5f3db4637dd92a4d2705c88de399d) ) /* TC5332205 */

	NEO_SFIX_128K( "246-s1.s1", CRC(2a360637) SHA1(431b43da5377dd189e51bd93d88d8a24d1b5090a) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "246-m1.m1", CRC(d0604ad1) SHA1(fae3cd52a177eadd5f5775ace957cc0f8301e65d) ) /* TC531001 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "246-v1.v1", 0x000000, 0x400000, CRC(16986fc6) SHA1(cff3103dadf2f4390460456a5bd3fb5f28e21f6a) ) /* TC5332204 */
	ROM_LOAD( "246-v2.v2", 0x400000, 0x400000, CRC(ada41e83) SHA1(78e37ffaaa5679c8775a3a71f6df7a0d15082bdc) ) /* TC5332204 */
	ROM_LOAD( "246-v3.v3", 0x800000, 0x200000, CRC(a05ba5db) SHA1(09d739cad323d918f4196f91b654627fcafd8f4d) ) /* TC5316200 */

	ROM_REGION( 0x3000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "246-c1.c1", 0x0000000, 0x800000, CRC(47ac9ec5) SHA1(2d9eea11ba87baa23b18a1a3f607dc137846e807) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "246-c2.c2", 0x0000001, 0x800000, CRC(7bcab64f) SHA1(08d0edddd14b53d606e9a7a46aa4fb4e7398e0d0) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "246-c3.c3", 0x1000000, 0x800000, CRC(db2f73e8) SHA1(8d0c3473a8b2a4e28fed1b74beb2e025b7e61867) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "246-c4.c4", 0x1000001, 0x800000, CRC(5503854e) SHA1(a0f2e7c609cbb2aa43493a39d7dcaeca3d511d26) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "246-c5.c5", 0x2000000, 0x800000, CRC(055b3701) SHA1(97f5e92538d1f2e437dcb3f80e56e1230287e8d1) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "246-c6.c6", 0x2000001, 0x800000, CRC(7e2caae1) SHA1(d9de14e3e323664a8c5b7f1df1ba9ec7dd0e6a46) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

/****************************************
 ID-0247
 . ??M-2470
 NEO-MVS PROGBK1 / NEO-MVS CHA256
****************************************/

ROM_START( flipshot ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "247-p1.p1", 0x000000, 0x100000, CRC(95779094) SHA1(a985e033bc6f137fa65855d3eed245d66d5b244a) ) /* mask rom TC538200 */

	NEO_SFIX_128K( "247-s1.s1", CRC(6300185c) SHA1(cb2f1de085fde214f96a962b1c2fa285eb387d44) ) /* mask rom TC531000 */

	NEO_BIOS_AUDIO_128K( "247-m1.m1", CRC(a9fe0144) SHA1(4cc076ecce9216a373f3dcd7ba28a03d6050e522) ) /* mask rom TC 531001 */

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "247-v1.v1", 0x000000, 0x200000, CRC(42ec743d) SHA1(f45b5167ebcbd59300f4e5b05448cd421654102a) ) /* mask rom TC5316200 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "247-c1.c1", 0x000000, 0x200000, CRC(c9eedcb2) SHA1(7627f2810322c146511525eb70b573a6a5ede926) ) /* Plane 0,1 */ /* mask rom TC5316200 */
	ROM_LOAD16_BYTE( "247-c2.c2", 0x000001, 0x200000, CRC(7d6d6e87) SHA1(6475b58b9f91c20d1f465f3e892de0c68e12a92b) ) /* Plane 2,3 */ /* mask rom TC5316200 */
ROM_END

/****************************************
 ID-0248
 . ??M-2480
 NEO-MVS PROGBK1 / NEO-MVS CHA256
****************************************/

ROM_START( pbobbl2n ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "248-p1.p1", 0x000000, 0x100000, CRC(9d6c0754) SHA1(95c70c2d51fc4de01e768e03cc800a850aaad5dc) ) /* TC538200 */

	NEO_SFIX_128K( "248-s1.s1", CRC(0a3fee41) SHA1(0ab2120e462086be942efcf6ffb37f58ea966ca3) ) /* TC531000DP */

	NEO_BIOS_AUDIO_128K( "248-m1.m1", CRC(883097a9) SHA1(677bf9684c0c7977a9a3f0c1288e430040a53b49) ) /* TC531001 */

	ROM_REGION( 0x800000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "248-v1.v1", 0x000000, 0x400000, CRC(57fde1fa) SHA1(af39bc141fc35b78dcacfd42b3abb29d7e5c2c89) ) /* TC5332204 */
	ROM_LOAD( "248-v2.v2", 0x400000, 0x400000, CRC(4b966ef3) SHA1(083c0e9fd7b8e506087648cdd8ec4206103984cd) ) /* TC5332204 */

	ROM_REGION( 0xa00000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "248-c1.c1", 0x000000, 0x400000, CRC(d9115327) SHA1(a49aa836a902326cfe785428e1699fefcf8566d4) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "248-c2.c2", 0x000001, 0x400000, CRC(77f9fdac) SHA1(4642d71d32b6a05dc8bfa0f95c936a77c7cef05e) ) /* Plane 2,3 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "248-c3.c3", 0x800000, 0x100000, CRC(8890bf7c) SHA1(a52f6bafd60e72003bfe38c80c1dde24b4983b2a) ) /* Plane 0,1 */ /* TC538200 */
	ROM_LOAD16_BYTE( "248-c4.c4", 0x800001, 0x100000, CRC(8efead3f) SHA1(f577d2f7c6f850b3d100c36947ad15e33dfa0bed) ) /* Plane 2,3 */ /* TC538200 */
ROM_END

/****************************************
 ID-0249
 . ??M-2490
 NEO-MVS PROGBK1 / NEO-MVS CHA256
 NEO-MVS PROGBK1 / NEO-MVS CHA512Y
****************************************/

ROM_START( ctomaday ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "249-p1.p1", 0x100000, 0x100000, CRC(c9386118) SHA1(5554662c7bc8605889cac4a67fee05bbb4eb786f) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "249-s1.s1", CRC(dc9eb372) SHA1(b8aa142243ba303799554479bfc88eb49260f3b1) ) /* TC531000DP */

	NEO_BIOS_AUDIO_128K( "249-m1.m1", CRC(80328a47) SHA1(34b6b1a81eab1cf38834b2eea55454ce1b6100e2) ) /* TC531001 */

	ROM_REGION( 0x500000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "249-v1.v1", 0x000000, 0x400000, CRC(de7c8f27) SHA1(3681a68a702ab5da8f509b8301d6cada75959332) ) /* TC5332204 */
	ROM_LOAD( "249-v2.v2", 0x400000, 0x100000, CRC(c8e40119) SHA1(738f525c381ed68c0b8a89318a3e4d0089473c45) ) /* TC538200 */

	ROM_REGION( 0x800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "249-c1.c1", 0x000000, 0x400000, CRC(041fb8ee) SHA1(dacc84d713d76818d89a26358374afaa22fa82a2) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "249-c2.c2", 0x000001, 0x400000, CRC(74f3cdf4) SHA1(55ddabaf77f4d575f4deb24fe63e4bdc2c6f31e1) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

/****************************************
 ID-0250
 . NGM-2500
 NEO-MVS PROGEOP (1999.2.2) / NEO-MVS CHA512Y
 . NGH-2500
 NEO-AEG PROGEOP (1999.4.2) / NEO-AEG CHA512Y
****************************************/

ROM_START( mslugx ) /* MVS AND AES VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "250-p1.p1",  0x000000, 0x100000, CRC(81f1f60b) SHA1(4c19f2e9824e606178ac1c9d4b0516fbaa625035) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "250-p2.ep1", 0x100000, 0x400000, CRC(1fda2e12) SHA1(18aaa7a3ba8da99f78c430e9be69ccde04bc04d9) ) /* TC5332205 */

	NEO_SFIX_128K( "250-s1.s1", CRC(fb6f441d) SHA1(2cc392ecde5d5afb28ddbaa1030552b48571dcfb) ) /* TC531000 */

	NEO_BIOS_AUDIO_128K( "250-m1.m1", CRC(fd42a842) SHA1(55769bad4860f64ef53a333e0da9e073db483d6a) ) /* TC531001 */

	ROM_REGION( 0xa00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "250-v1.v1", 0x000000, 0x400000, CRC(c79ede73) SHA1(ebfcc67204ff9677cf7972fd5b6b7faabf07280c) ) /* TC5332204 */
	ROM_LOAD( "250-v2.v2", 0x400000, 0x400000, CRC(ea9aabe1) SHA1(526c42ca9a388f7435569400e2f132e2724c71ff) ) /* TC5332204 */
	ROM_LOAD( "250-v3.v3", 0x800000, 0x200000, CRC(2ca65102) SHA1(45979d1edb1fc774a415d9386f98d7cb252a2043) ) /* TC5316200 */

	ROM_REGION( 0x3000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "250-c1.c1", 0x0000000, 0x800000, CRC(09a52c6f) SHA1(c3e8a8ccdac0f8bddc4c3413277626532405fae2) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "250-c2.c2", 0x0000001, 0x800000, CRC(31679821) SHA1(554f600a3aa09c16c13c625299b087a79d0d15c5) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "250-c3.c3", 0x1000000, 0x800000, CRC(fd602019) SHA1(c56646c62387bc1439d46610258c755beb8d7dd8) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "250-c4.c4", 0x1000001, 0x800000, CRC(31354513) SHA1(31be8ea2498001f68ce4b06b8b90acbf2dcab6af) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "250-c5.c5", 0x2000000, 0x800000, CRC(a4b56124) SHA1(d41069856df990a1a99d39fb263c8303389d5475) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "250-c6.c6", 0x2000001, 0x800000, CRC(83e3e69d) SHA1(39be66287696829d243fb71b3fb8b7dc2bc3298f) ) /* Plane 0,1 */ /* TC5364205 */
ROM_END

/****************************************
 ID-0251
 . NGM-2510
 SMA protected version found on:
 NEO-MVS PROGLBA (NEO-SMA) (1999.4.12) / NEO-MVS CHAFIO (1999.6.14) (NEO-CMC 7042)
 Non SMA protected version found on:
 NEO-MVS PROGBK1 / NEO-MVS CHAFIO (1999.6.14) (NEO-CMC 7042)
 . NGH-2510
 NEO-AEG PROGLBA (NEO-SMA) (1999.7.6) / NEO-AEG CHAFIO (1999.8.10) (NEO-CMC 7042)
****************************************/

ROM_START( kof99 ) /* Original Version - Encrypted Code & GFX */ /* MVS VERSION */
	ROM_REGION( 0x900000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "ka.neo-sma", 0x0c0000, 0x040000, CRC(7766d09e) SHA1(4e0a49d1ad669a62676cb30f527c6590cde80194) ) /* stored in the custom chip */
	ROM_LOAD16_WORD_SWAP( "251-p1.p1",  0x100000, 0x400000, CRC(006e4532) SHA1(47791ab4044ad55988b1d3412d95b65b91a163c8) ) /* mask rom TC5332205 */
	ROM_LOAD16_WORD_SWAP( "251-p2.p2",  0x500000, 0x400000, CRC(90175f15) SHA1(aa9e75810438a8b45808a8bf32cb04d91b5c0b3a) ) /* mask rom TC5332205 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_128K( "251-m1.m1", CRC(5e74539c) SHA1(6f49a9343cbd026b2c6720ff3fa2e5b1f85e80da) ) /* TC531001 */

	ROM_REGION( 0x0e00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "251-v1.v1", 0x000000, 0x400000, CRC(ef2eecc8) SHA1(8ed13b9db92dba3124bc5ba66e3e275885ece24a) ) /* TC5332204 */
	ROM_LOAD( "251-v2.v2", 0x400000, 0x400000, CRC(73e211ca) SHA1(0e60fa64cab6255d9721e2b4bc22e3de64c874c5) ) /* TC5332204 */
	ROM_LOAD( "251-v3.v3", 0x800000, 0x400000, CRC(821901da) SHA1(c6d4975bfaa19a62ed59126cadf2578c0a5c257f) ) /* TC5332204 */
	ROM_LOAD( "251-v4.v4", 0xc00000, 0x200000, CRC(b49e6178) SHA1(dde6f76e958841e8c99b693e13ced9aa9ef316dc) ) /* TC5316200 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "251-c1.c1", 0x0000000, 0x800000, CRC(0f9e93fe) SHA1(c7d77f0833c6f526f632e4f2dce59e302f6b9a15) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c2.c2", 0x0000001, 0x800000, CRC(e71e2ea3) SHA1(39c7a326fddbcca3b29c68cdc96aad4d62295c0f) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c3.c3", 0x1000000, 0x800000, CRC(238755d2) SHA1(01125b5c7a28e350f091280b041954fd1ac7c98f) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c4.c4", 0x1000001, 0x800000, CRC(438c8b22) SHA1(ffbc643f5b27dd00f2f95d4ef4c5f29ee814722b) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c5.c5", 0x2000000, 0x800000, CRC(0b0abd0a) SHA1(d5ad324fe523bdc6f09209d236cc4932524a48f1) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c6.c6", 0x2000001, 0x800000, CRC(65bbf281) SHA1(79ae174667a23dabcfe865b6cd6133c86098452e) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c7.c7", 0x3000000, 0x800000, CRC(ff65f62e) SHA1(7cd335fede05b56e15db90ce407c1183a78da4e9) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c8.c8", 0x3000001, 0x800000, CRC(8d921c68) SHA1(42acf1d27d52a8e3b6262eb7df50693c0b135565) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

ROM_START( kof99h ) /* Original Version - Encrypted Code & GFX */ /* AES VERSION */
	ROM_REGION( 0x900000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "kc.neo-sma",  0x0c0000, 0x040000, CRC(6c9d0647) SHA1(2a0ce62ca6c18007e8fbe1b60475c7874ab79389) ) /* stored in the custom chip */
	ROM_LOAD16_WORD_SWAP( "251-p1.p1",   0x100000, 0x400000, CRC(006e4532) SHA1(47791ab4044ad55988b1d3412d95b65b91a163c8) ) /* mask rom TC5332205 */
	ROM_LOAD16_WORD_SWAP( "251-pg2.p2",  0x500000, 0x400000, CRC(d9057f51) SHA1(8d365b4dd40351495df99d6c765df1434b0b0548) ) /* mask rom TC5332205 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_128K( "251-m1.m1", CRC(5e74539c) SHA1(6f49a9343cbd026b2c6720ff3fa2e5b1f85e80da) ) /* TC531001DP */

	ROM_REGION( 0x0e00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "251-v1.v1", 0x000000, 0x400000, CRC(ef2eecc8) SHA1(8ed13b9db92dba3124bc5ba66e3e275885ece24a) ) /* TC5332204 */
	ROM_LOAD( "251-v2.v2", 0x400000, 0x400000, CRC(73e211ca) SHA1(0e60fa64cab6255d9721e2b4bc22e3de64c874c5) ) /* TC5332204 */
	ROM_LOAD( "251-v3.v3", 0x800000, 0x400000, CRC(821901da) SHA1(c6d4975bfaa19a62ed59126cadf2578c0a5c257f) ) /* TC5332204 */
	ROM_LOAD( "251-v4.v4", 0xc00000, 0x200000, CRC(b49e6178) SHA1(dde6f76e958841e8c99b693e13ced9aa9ef316dc) ) /* TC5316200 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "251-c1.c1", 0x0000000, 0x800000, CRC(0f9e93fe) SHA1(c7d77f0833c6f526f632e4f2dce59e302f6b9a15) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c2.c2", 0x0000001, 0x800000, CRC(e71e2ea3) SHA1(39c7a326fddbcca3b29c68cdc96aad4d62295c0f) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c3.c3", 0x1000000, 0x800000, CRC(238755d2) SHA1(01125b5c7a28e350f091280b041954fd1ac7c98f) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c4.c4", 0x1000001, 0x800000, CRC(438c8b22) SHA1(ffbc643f5b27dd00f2f95d4ef4c5f29ee814722b) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c5.c5", 0x2000000, 0x800000, CRC(0b0abd0a) SHA1(d5ad324fe523bdc6f09209d236cc4932524a48f1) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c6.c6", 0x2000001, 0x800000, CRC(65bbf281) SHA1(79ae174667a23dabcfe865b6cd6133c86098452e) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c7.c7", 0x3000000, 0x800000, CRC(ff65f62e) SHA1(7cd335fede05b56e15db90ce407c1183a78da4e9) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c8.c8", 0x3000001, 0x800000, CRC(8d921c68) SHA1(42acf1d27d52a8e3b6262eb7df50693c0b135565) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

ROM_START( kof99e ) /* Original Version - Encrypted Code & GFX */
	ROM_REGION( 0x900000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "ka.neo-sma", 0x0c0000, 0x040000, CRC(7766d09e) SHA1(4e0a49d1ad669a62676cb30f527c6590cde80194) )  /* stored in the custom chip */
	/* Is the SMA for this set correct? A set with this layout and a SMA.KB is known */
	ROM_LOAD16_WORD_SWAP( "251-ep1.p1", 0x100000, 0x200000, CRC(1e8d692d) SHA1(eea1aa8c0a17f089ac14831889c36535e559072c) ) /* M27C160 */
	ROM_LOAD16_WORD_SWAP( "251-ep2.p2", 0x300000, 0x200000, CRC(d6206e5a) SHA1(0e1100d03c40c6d5cfa899d009e319ae73fce6b8) ) /* M27C160 */
	ROM_LOAD16_WORD_SWAP( "251-ep3.p3", 0x500000, 0x200000, CRC(d58c3ef8) SHA1(f927d90d55b49944f448d6286e0cb913cc70ade1) ) /* M27C160 */
	ROM_LOAD16_WORD_SWAP( "251-ep4.p4", 0x700000, 0x200000, CRC(52de02ae) SHA1(f16924ff8eef92da7716236a6a055e22e090a02b) ) /* M27C160 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_128K( "251-m1.m1", CRC(5e74539c) SHA1(6f49a9343cbd026b2c6720ff3fa2e5b1f85e80da) ) /* TC531001 */

	ROM_REGION( 0x0e00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "251-v1.v1", 0x000000, 0x400000, CRC(ef2eecc8) SHA1(8ed13b9db92dba3124bc5ba66e3e275885ece24a) ) /* TC5332204 */
	ROM_LOAD( "251-v2.v2", 0x400000, 0x400000, CRC(73e211ca) SHA1(0e60fa64cab6255d9721e2b4bc22e3de64c874c5) ) /* TC5332204 */
	ROM_LOAD( "251-v3.v3", 0x800000, 0x400000, CRC(821901da) SHA1(c6d4975bfaa19a62ed59126cadf2578c0a5c257f) ) /* TC5332204 */
	ROM_LOAD( "251-v4.v4", 0xc00000, 0x200000, CRC(b49e6178) SHA1(dde6f76e958841e8c99b693e13ced9aa9ef316dc) ) /* TC5316200 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "251-c1.c1", 0x0000000, 0x800000, CRC(0f9e93fe) SHA1(c7d77f0833c6f526f632e4f2dce59e302f6b9a15) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c2.c2", 0x0000001, 0x800000, CRC(e71e2ea3) SHA1(39c7a326fddbcca3b29c68cdc96aad4d62295c0f) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c3.c3", 0x1000000, 0x800000, CRC(238755d2) SHA1(01125b5c7a28e350f091280b041954fd1ac7c98f) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c4.c4", 0x1000001, 0x800000, CRC(438c8b22) SHA1(ffbc643f5b27dd00f2f95d4ef4c5f29ee814722b) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c5.c5", 0x2000000, 0x800000, CRC(0b0abd0a) SHA1(d5ad324fe523bdc6f09209d236cc4932524a48f1) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c6.c6", 0x2000001, 0x800000, CRC(65bbf281) SHA1(79ae174667a23dabcfe865b6cd6133c86098452e) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c7.c7", 0x3000000, 0x800000, CRC(ff65f62e) SHA1(7cd335fede05b56e15db90ce407c1183a78da4e9) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c8.c8", 0x3000001, 0x800000, CRC(8d921c68) SHA1(42acf1d27d52a8e3b6262eb7df50693c0b135565) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

ROM_START( kof99k ) /* Original Version - Encrypted Code & GFX */ /* KOREAN VERSION */
	ROM_REGION( 0x900000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "kb.neo-sma", 0x0c0000, 0x040000, CRC(9fccc688) SHA1(1a24d0fe7cabe7b346f538e099f1357bbf77bc89) ) /* stored in the custom chip */
	ROM_LOAD16_WORD_SWAP( "151-pg1k.p1",  0x100000, 0x400000, CRC(ef259292) SHA1(e02f2d0c71eac74424507e4845991fbc12821045) ) /* mask rom TC5332205 */
	ROM_LOAD16_WORD_SWAP( "151-pg2k.p2",  0x500000, 0x400000, CRC(f3898ec2) SHA1(c7fd230fbb97a9b171aeae8ff1a5cd39ba7fa9eb) ) /* mask rom TC5332205 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_128K( "251-m1.m1", CRC(5e74539c) SHA1(6f49a9343cbd026b2c6720ff3fa2e5b1f85e80da) ) /* TC531001 */

	ROM_REGION( 0x0e00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "251-v1.v1", 0x000000, 0x400000, CRC(ef2eecc8) SHA1(8ed13b9db92dba3124bc5ba66e3e275885ece24a) ) /* TC5332204 */
	ROM_LOAD( "251-v2.v2", 0x400000, 0x400000, CRC(73e211ca) SHA1(0e60fa64cab6255d9721e2b4bc22e3de64c874c5) ) /* TC5332204 */
	ROM_LOAD( "251-v3.v3", 0x800000, 0x400000, CRC(821901da) SHA1(c6d4975bfaa19a62ed59126cadf2578c0a5c257f) ) /* TC5332204 */
	ROM_LOAD( "251-v4.v4", 0xc00000, 0x200000, CRC(b49e6178) SHA1(dde6f76e958841e8c99b693e13ced9aa9ef316dc) ) /* TC5316200 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "251-c1.c1", 0x0000000, 0x800000, CRC(0f9e93fe) SHA1(c7d77f0833c6f526f632e4f2dce59e302f6b9a15) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c2.c2", 0x0000001, 0x800000, CRC(e71e2ea3) SHA1(39c7a326fddbcca3b29c68cdc96aad4d62295c0f) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c3.c3", 0x1000000, 0x800000, CRC(238755d2) SHA1(01125b5c7a28e350f091280b041954fd1ac7c98f) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c4.c4", 0x1000001, 0x800000, CRC(438c8b22) SHA1(ffbc643f5b27dd00f2f95d4ef4c5f29ee814722b) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c5.c5", 0x2000000, 0x800000, CRC(0b0abd0a) SHA1(d5ad324fe523bdc6f09209d236cc4932524a48f1) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c6.c6", 0x2000001, 0x800000, CRC(65bbf281) SHA1(79ae174667a23dabcfe865b6cd6133c86098452e) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c7.c7", 0x3000000, 0x800000, CRC(ff65f62e) SHA1(7cd335fede05b56e15db90ce407c1183a78da4e9) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c8.c8", 0x3000001, 0x800000, CRC(8d921c68) SHA1(42acf1d27d52a8e3b6262eb7df50693c0b135565) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

ROM_START( kof99ka ) /* Original Version - Encrypted GFX */ /* KOREAN VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "152-p1.p1",  0x000000, 0x100000, CRC(f2c7ddfa) SHA1(d592eecc53d442c55c2f26a6a721fdf2924d2a5b) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "152-p2.sp2", 0x100000, 0x400000, CRC(274ef47a) SHA1(98654b68cc85c19d4a90b46f3110f551fa2e5357) ) /* TC5332205 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_128K( "251-m1.m1", CRC(5e74539c) SHA1(6f49a9343cbd026b2c6720ff3fa2e5b1f85e80da) ) /* TC531001DP */

	ROM_REGION( 0x0e00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "251-v1.v1", 0x000000, 0x400000, CRC(ef2eecc8) SHA1(8ed13b9db92dba3124bc5ba66e3e275885ece24a) ) /* TC5332204 */
	ROM_LOAD( "251-v2.v2", 0x400000, 0x400000, CRC(73e211ca) SHA1(0e60fa64cab6255d9721e2b4bc22e3de64c874c5) ) /* TC5332204 */
	ROM_LOAD( "251-v3.v3", 0x800000, 0x400000, CRC(821901da) SHA1(c6d4975bfaa19a62ed59126cadf2578c0a5c257f) ) /* TC5332204 */
	ROM_LOAD( "251-v4.v4", 0xc00000, 0x200000, CRC(b49e6178) SHA1(dde6f76e958841e8c99b693e13ced9aa9ef316dc) ) /* TC5316200 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "251-c1.c1", 0x0000000, 0x800000, CRC(0f9e93fe) SHA1(c7d77f0833c6f526f632e4f2dce59e302f6b9a15) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c2.c2", 0x0000001, 0x800000, CRC(e71e2ea3) SHA1(39c7a326fddbcca3b29c68cdc96aad4d62295c0f) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c3.c3", 0x1000000, 0x800000, CRC(238755d2) SHA1(01125b5c7a28e350f091280b041954fd1ac7c98f) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c4.c4", 0x1000001, 0x800000, CRC(438c8b22) SHA1(ffbc643f5b27dd00f2f95d4ef4c5f29ee814722b) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c5.c5", 0x2000000, 0x800000, CRC(0b0abd0a) SHA1(d5ad324fe523bdc6f09209d236cc4932524a48f1) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c6.c6", 0x2000001, 0x800000, CRC(65bbf281) SHA1(79ae174667a23dabcfe865b6cd6133c86098452e) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c7.c7", 0x3000000, 0x800000, CRC(ff65f62e) SHA1(7cd335fede05b56e15db90ce407c1183a78da4e9) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "251-c8.c8", 0x3000001, 0x800000, CRC(8d921c68) SHA1(42acf1d27d52a8e3b6262eb7df50693c0b135565) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

ROM_START( kof99p ) /* Prototype Version - Possibly Hacked */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "proto_251-p1.p1", 0x000000, 0x100000, CRC(f37929c4) SHA1(226e7e3d629568399b88275e5bcd4e5b3839be95) )
	ROM_LOAD16_WORD_SWAP( "proto_251-p2.p2", 0x100000, 0x400000, CRC(739742ad) SHA1(31acaf05a9bf186305888d3db7e4e8a83f7bb0a4) )

	/* This is the S1 from the prototype, the final is different */
	NEO_SFIX_128K( "proto_251-s1.s1", CRC(fb1498ed) SHA1(d40060b31b6f217a4abdf3b336439fcd7bd7aaef) )

	/* Did the Prototype really use the same sound program / voice roms, sound isn't great .. */
	NEO_BIOS_AUDIO_128K( "251-m1.m1", CRC(5e74539c) SHA1(6f49a9343cbd026b2c6720ff3fa2e5b1f85e80da) ) /* TC531001 */

	ROM_REGION( 0x0e00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "251-v1.v1", 0x000000, 0x400000, CRC(ef2eecc8) SHA1(8ed13b9db92dba3124bc5ba66e3e275885ece24a) ) /* TC5332204 */
	ROM_LOAD( "251-v2.v2", 0x400000, 0x400000, CRC(73e211ca) SHA1(0e60fa64cab6255d9721e2b4bc22e3de64c874c5) ) /* TC5332204 */
	ROM_LOAD( "251-v3.v3", 0x800000, 0x400000, CRC(821901da) SHA1(c6d4975bfaa19a62ed59126cadf2578c0a5c257f) ) /* TC5332204 */
	ROM_LOAD( "251-v4.v4", 0xc00000, 0x200000, CRC(b49e6178) SHA1(dde6f76e958841e8c99b693e13ced9aa9ef316dc) ) /* TC5316200 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* these are probably decrypted versions of the roms found in the final */
	ROM_LOAD16_BYTE( "proto_251-c1.c1", 0x0000000, 0x800000, CRC(e5d8ffa4) SHA1(65f15f9f02424a7a9dd35916166594f283e8d424) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "proto_251-c2.c2", 0x0000001, 0x800000, CRC(d822778f) SHA1(b590055e9bf1549bd6e1ecdabd65702202615712) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "proto_251-c3.c3", 0x1000000, 0x800000, CRC(f20959e8) SHA1(38293043fa77ac51c5e3191118874c58f1ae4d30) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "proto_251-c4.c4", 0x1000001, 0x800000, CRC(54ffbe9f) SHA1(8e62442923551f07a552621951b1accab2830e3b) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "proto_251-c5.c5", 0x2000000, 0x800000, CRC(d87a3bbc) SHA1(430f6812088712e0eb5714dcc664d8bba75e921a) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "proto_251-c6.c6", 0x2000001, 0x800000, CRC(4d40a691) SHA1(2b580d0678a5e6033ef16130671e860364d35e56) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "proto_251-c7.c7", 0x3000000, 0x800000, CRC(a4479a58) SHA1(d50e6cc9ccfe1ddbc6d90d46b8ca2cb0304edd8c) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "proto_251-c8.c8", 0x3000001, 0x800000, CRC(ead513ce) SHA1(e9b07a0b01fdeb3004755a479df059c81b4d0ed6) ) /* Plane 2,3 */
ROM_END

/****************************************
 ID-0252
 . ??M-2520
 NEO-MVS PROGBK1 / NEO-MVS CHAFIO (1999.6.14) (NEO-CMC 7042)
****************************************/

ROM_START( ganryu ) /* Original Version - Encrypted GFX */ /* MVS ONLY RELEASE */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "252-p1.p1", 0x100000, 0x100000, CRC(4b8ac4fb) SHA1(93d90271bff281862b03beba3809cf95a47a1e44) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_128K( "252-m1.m1", CRC(30cc4099) SHA1(46453b7aac41855a92724a785372f8daf931d8d4) ) /* TC531001DP */

	ROM_REGION( 0x0400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "252-v1.v1", 0x000000, 0x400000, CRC(e5946733) SHA1(d5904a50465af03d6ff33399a98f3259721ca0b2) ) /* TC5332204 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "252-c1.c1", 0x0000000, 0x800000, CRC(50ee7882) SHA1(ace0f95407c246d0456341cf2ad8a7668b81df8a) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "252-c2.c2", 0x0000001, 0x800000, CRC(62585474) SHA1(b35461598087aa82886af0030c61b26cc064af5f) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

/****************************************
 ID-0253
 . NGM-2530
 NEO-MVS PROGLBA (1999.4.12) (NEO-SMA)(LBA-SUB) / NEO-MVS CHAFIO (1999.6.14) (NEO-CMC 7042)
 . NGH-2530
 NEO-AEG PROGLBA (1999.7.6) (NEO-SMA) / NEO-AEG CHAFIO (1999.8.10) (NEO-CMC 7042)
****************************************/

ROM_START( garou ) /* Original Version - Encrypted GFX */ /* MVS VERSION - later revision */
	ROM_REGION( 0x900000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "kf.neo-sma", 0x0c0000, 0x040000, CRC(98bc93dc) SHA1(01fe3d18b50f770e131e8d8eeff4c630ba8c9551) )  /* stored in the custom chip */
	ROM_LOAD16_WORD_SWAP( "253-ep1.p1", 0x100000, 0x200000, CRC(ea3171a4) SHA1(bbda40f652baa0dc5fc6a006c001a1bdb0df43f6) ) /* M27C160 */
	ROM_LOAD16_WORD_SWAP( "253-ep2.p2", 0x300000, 0x200000, CRC(382f704b) SHA1(0ace9c84a8b8a0524fd9a503e7d872de1bf1bd52) ) /* M27C160 */
	ROM_LOAD16_WORD_SWAP( "253-ep3.p3", 0x500000, 0x200000, CRC(e395bfdd) SHA1(6b50f5ac15bf66b7e4e9bff57594fd3d7530c831) ) /* M27C160 */
	ROM_LOAD16_WORD_SWAP( "253-ep4.p4", 0x700000, 0x200000, CRC(da92c08e) SHA1(5556f983ebcebc33160e90a6a6cf589d54c8cedc) ) /* M27C160 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x80000, "cslot1:fixed", 0 )   /* larger char set */
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_256K( "253-m1.m1", CRC(36a806be) SHA1(90fb44dc0c3fb57946a0f35716056abb84a0f191) ) /* TC532000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "253-v1.v1", 0x000000, 0x400000, CRC(263e388c) SHA1(11f05feee170370c4bfc5053af79246a6e3de5dc) ) /* TC5332204 */
	ROM_LOAD( "253-v2.v2", 0x400000, 0x400000, CRC(2c6bc7be) SHA1(c9c61054ce1a47bf1bf77a31117726b499df24a4) ) /* TC5332204 */
	ROM_LOAD( "253-v3.v3", 0x800000, 0x400000, CRC(0425b27d) SHA1(986863c98fc3445487242dcf2ea75b075e7f33ee) ) /* TC5332204 */
	ROM_LOAD( "253-v4.v4", 0xc00000, 0x400000, CRC(a54be8a9) SHA1(d7123e79b43e8adfaa5ecadbfcbeb6be890ec311) ) /* TC5332204 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "253-c1.c1", 0x0000000, 0x800000, CRC(0603e046) SHA1(5ef4557ce90ba65d36129de97be1fdc049c4a3d0) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c2.c2", 0x0000001, 0x800000, CRC(0917d2a4) SHA1(d4ed3a13ae22f880fb399671c1752f1a0283f316) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c3.c3", 0x1000000, 0x800000, CRC(6737c92d) SHA1(678f0c9cc1267bd131546981b9989bfb7289d8ba) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c4.c4", 0x1000001, 0x800000, CRC(5ba92ec6) SHA1(aae36b050a3a0321026a96eba06dd184c0e2acca) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c5.c5", 0x2000000, 0x800000, CRC(3eab5557) SHA1(47c433015aa81a0b0a1d3ee51382c4948b80c023) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c6.c6", 0x2000001, 0x800000, CRC(308d098b) SHA1(b052f1fa9fbc69606004c250e2505360eaa24949) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c7.c7", 0x3000000, 0x800000, CRC(c0e995ae) SHA1(8675ca787d28246174c313167f82557f021366fc) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c8.c8", 0x3000001, 0x800000, CRC(21a11303) SHA1(fd61221ad257c185ef5c1f9694bd6b840b591af3) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

ROM_START( garouh ) /* Original Version - Encrypted GFX */ /* MVS AND AES VERSION - earlier revision */
	ROM_REGION( 0x900000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "ke.neo-sma", 0x0c0000, 0x040000, CRC(96c72233) SHA1(29e19effd40fdf7e5144332396857f4ad0eff13e) )  /* stored in the custom chip */
	ROM_LOAD16_WORD_SWAP( "253-p1.p1",  0x100000, 0x400000, CRC(18ae5d7e) SHA1(bdb58ec9137d8653979b47132f2d10e1cc6aaa24) ) /* mask rom TC5332205 */
	ROM_LOAD16_WORD_SWAP( "253-p2.p2",  0x500000, 0x400000, CRC(afffa779) SHA1(ac017986f02277fbcd656b8c02492a3f4216a90e) ) /* mask rom TC5332205 */
	/* also found AES JP set with EP1 / EP2 / EP3 and EP4 on eproms and KE NEO-SMA with a white marking; chip labels for eproms are 253-EP1, 253-EP2, 253-EP3 and 253-EP4 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x80000, "cslot1:fixed", 0 )   /* larger char set */
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_256K( "253-m1.m1", CRC(36a806be) SHA1(90fb44dc0c3fb57946a0f35716056abb84a0f191) ) /* TC532000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "253-v1.v1", 0x000000, 0x400000, CRC(263e388c) SHA1(11f05feee170370c4bfc5053af79246a6e3de5dc) ) /* TC5332204 */
	ROM_LOAD( "253-v2.v2", 0x400000, 0x400000, CRC(2c6bc7be) SHA1(c9c61054ce1a47bf1bf77a31117726b499df24a4) ) /* TC5332204 */
	ROM_LOAD( "253-v3.v3", 0x800000, 0x400000, CRC(0425b27d) SHA1(986863c98fc3445487242dcf2ea75b075e7f33ee) ) /* TC5332204 */
	ROM_LOAD( "253-v4.v4", 0xc00000, 0x400000, CRC(a54be8a9) SHA1(d7123e79b43e8adfaa5ecadbfcbeb6be890ec311) ) /* TC5332204 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "253-c1.c1", 0x0000000, 0x800000, CRC(0603e046) SHA1(5ef4557ce90ba65d36129de97be1fdc049c4a3d0) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c2.c2", 0x0000001, 0x800000, CRC(0917d2a4) SHA1(d4ed3a13ae22f880fb399671c1752f1a0283f316) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c3.c3", 0x1000000, 0x800000, CRC(6737c92d) SHA1(678f0c9cc1267bd131546981b9989bfb7289d8ba) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c4.c4", 0x1000001, 0x800000, CRC(5ba92ec6) SHA1(aae36b050a3a0321026a96eba06dd184c0e2acca) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c5.c5", 0x2000000, 0x800000, CRC(3eab5557) SHA1(47c433015aa81a0b0a1d3ee51382c4948b80c023) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c6.c6", 0x2000001, 0x800000, CRC(308d098b) SHA1(b052f1fa9fbc69606004c250e2505360eaa24949) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c7.c7", 0x3000000, 0x800000, CRC(c0e995ae) SHA1(8675ca787d28246174c313167f82557f021366fc) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c8.c8", 0x3000001, 0x800000, CRC(21a11303) SHA1(fd61221ad257c185ef5c1f9694bd6b840b591af3) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

ROM_START( garouha ) /* Original Version - Encrypted GFX */ /* AES VERSION */
	ROM_REGION( 0x900000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "neo-sma", 0x0c0000, 0x040000, CRC(9aa450e6) SHA1(ba9c6fe10ef97255b004fcbf05065826d219a968) )  /* stored in the custom chip */
	/* standard program roms are identical to 'garou' set, only content of custom chip above differs */
	ROM_LOAD16_WORD_SWAP( "253-ep1.p1", 0x100000, 0x200000, CRC(ea3171a4) SHA1(bbda40f652baa0dc5fc6a006c001a1bdb0df43f6) ) /* M27C160 */
	ROM_LOAD16_WORD_SWAP( "253-ep2.p2", 0x300000, 0x200000, CRC(382f704b) SHA1(0ace9c84a8b8a0524fd9a503e7d872de1bf1bd52) ) /* M27C160 */
	ROM_LOAD16_WORD_SWAP( "253-ep3.p3", 0x500000, 0x200000, CRC(e395bfdd) SHA1(6b50f5ac15bf66b7e4e9bff57594fd3d7530c831) ) /* M27C160 */
	ROM_LOAD16_WORD_SWAP( "253-ep4.p4", 0x700000, 0x200000, CRC(da92c08e) SHA1(5556f983ebcebc33160e90a6a6cf589d54c8cedc) ) /* M27C160 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x80000, "cslot1:fixed", 0 )   /* larger char set */
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_256K( "253-m1.m1", CRC(36a806be) SHA1(90fb44dc0c3fb57946a0f35716056abb84a0f191) ) /* TC532000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "253-v1.v1", 0x000000, 0x400000, CRC(263e388c) SHA1(11f05feee170370c4bfc5053af79246a6e3de5dc) ) /* TC5332204 */
	ROM_LOAD( "253-v2.v2", 0x400000, 0x400000, CRC(2c6bc7be) SHA1(c9c61054ce1a47bf1bf77a31117726b499df24a4) ) /* TC5332204 */
	ROM_LOAD( "253-v3.v3", 0x800000, 0x400000, CRC(0425b27d) SHA1(986863c98fc3445487242dcf2ea75b075e7f33ee) ) /* TC5332204 */
	ROM_LOAD( "253-v4.v4", 0xc00000, 0x400000, CRC(a54be8a9) SHA1(d7123e79b43e8adfaa5ecadbfcbeb6be890ec311) ) /* TC5332204 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "253-c1.c1", 0x0000000, 0x800000, CRC(0603e046) SHA1(5ef4557ce90ba65d36129de97be1fdc049c4a3d0) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c2.c2", 0x0000001, 0x800000, CRC(0917d2a4) SHA1(d4ed3a13ae22f880fb399671c1752f1a0283f316) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c3.c3", 0x1000000, 0x800000, CRC(6737c92d) SHA1(678f0c9cc1267bd131546981b9989bfb7289d8ba) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c4.c4", 0x1000001, 0x800000, CRC(5ba92ec6) SHA1(aae36b050a3a0321026a96eba06dd184c0e2acca) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c5.c5", 0x2000000, 0x800000, CRC(3eab5557) SHA1(47c433015aa81a0b0a1d3ee51382c4948b80c023) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c6.c6", 0x2000001, 0x800000, CRC(308d098b) SHA1(b052f1fa9fbc69606004c250e2505360eaa24949) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c7.c7", 0x3000000, 0x800000, CRC(c0e995ae) SHA1(8675ca787d28246174c313167f82557f021366fc) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "253-c8.c8", 0x3000001, 0x800000, CRC(21a11303) SHA1(fd61221ad257c185ef5c1f9694bd6b840b591af3) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

ROM_START( garoup ) /* Prototype Version, seems genuine */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "proto_253-p1.p1", 0x000000, 0x100000, CRC(c72f0c16) SHA1(1ff6bb651682f93bef9ff02622c3cf63fe594986) )
	ROM_LOAD16_WORD_SWAP( "proto_253-p2.p2", 0x100000, 0x400000, CRC(bf8de565) SHA1(0e24574168cd38138bed0aa4dca49849f6901ca2) )

	NEO_SFIX_128K( "proto_253-s1.s1", CRC(779989de) SHA1(8bd550857b60f8a907f6d39a4225ceffdd330307) )

	NEO_BIOS_AUDIO_256K( "proto_253-m1.m1", CRC(bbe464f7) SHA1(f5f8f3e48f5d453f45107085d6f4023bcd24c053) )

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "proto_253-v1.v1", 0x000000, 0x400000, CRC(274f3923) SHA1(4c7a8ad1cd0e3afc1f78de3c2929120ed434f104) )
	ROM_LOAD( "proto_253-v2.v2", 0x400000, 0x400000, CRC(8f86dabe) SHA1(b3d2d9f5c1d97a6e7aee2c674fb6627f41bbb240) )
	ROM_LOAD( "proto_253-v3.v3", 0x800000, 0x400000, CRC(05fd06cd) SHA1(6cd699719614bb87547632ea3d61d92d81fdf563) )
	ROM_LOAD( "proto_253-v4.v4", 0xc00000, 0x400000, CRC(14984063) SHA1(170d5638327ec0eb3590b80dc11590897367250c) )

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "proto_253-c1.c1", 0x0000000, 0x800000, CRC(5bb5d137) SHA1(d648febd8e6a0bdd9bdbb6ce1f1f8b08567ec05a) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "proto_253-c2.c2", 0x0000001, 0x800000, CRC(5c8d2960) SHA1(f7503502be0332adf408ee0ea5ee5161c8939fd8) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "proto_253-c3.c3", 0x1000000, 0x800000, CRC(234d16fc) SHA1(7b9221f7ecc438150c8a10be72390329854ed21b) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "proto_253-c4.c4", 0x1000001, 0x800000, CRC(b9b5b993) SHA1(6059793eaf6e58c172235fe64aa9d25a40c38ed6) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "proto_253-c5.c5", 0x2000000, 0x800000, CRC(722615d2) SHA1(798832c535869f0e247c3db0d8253779b103e213) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "proto_253-c6.c6", 0x2000001, 0x800000, CRC(0a6fab38) SHA1(eaee6f2f18af91f7959d84d4b991b3fc182d07c4) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "proto_253-c7.c7", 0x3000000, 0x800000, CRC(d68e806f) SHA1(92bfd9839115bd590972ae4ecc45ad35dce22387) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "proto_253-c8.c8", 0x3000001, 0x800000, CRC(f778fe99) SHA1(c963f6ba90a36d02991728b44ffcf174ca18268a) ) /* Plane 2,3 */
ROM_END

/****************************************
 ID-0254
 . ??M-2540
 NEO-MVS PROGBK1 / NEO-MVS CHAFIO (1999.6.14) (NEO-CMC 7042)
****************************************/

ROM_START( s1945p ) /* Original Version, Encrypted GFX Roms */ /* MVS ONLY RELEASE */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "254-p1.p1",  0x000000, 0x100000, CRC(ff8efcff) SHA1(dcaeaca573385c172ecc43ee6bee355359091893) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "254-p2.sp2", 0x100000, 0x400000, CRC(efdfd4dd) SHA1(254f3e1b546eed788f7ae919be9d1bf9702148ce) ) /* TC5332205 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_128K( "254-m1.m1", CRC(994b4487) SHA1(a4e645a3ababa48a8325980ff022e04a8b51b017) ) /* TC531001DP */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "254-v1.v1", 0x000000, 0x400000, CRC(844f58fb) SHA1(e59544457be9f21481eac8b5a39b9cbb502f252d) ) /* TC5332204 */
	ROM_LOAD( "254-v2.v2", 0x400000, 0x400000, CRC(d9a248f0) SHA1(dd3e0974b753e6f94d0943a002de45668a1b072b) ) /* TC5332204 */
	ROM_LOAD( "254-v3.v3", 0x800000, 0x400000, CRC(0b0d2d33) SHA1(f8e76af42a997f36a40f66b39de00f68afe6a89c) ) /* TC5332204 */
	ROM_LOAD( "254-v4.v4", 0xc00000, 0x400000, CRC(6d13dc91) SHA1(8433513c0b5aea61939068a25ab90efbe3e44116) ) /* TC5332204 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "254-c1.c1", 0x0000000, 0x800000, CRC(ae6fc8ef) SHA1(544ccdaee8a4a45cdce9483e30852811d2d5f3cc) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "254-c2.c2", 0x0000001, 0x800000, CRC(436fa176) SHA1(d70141a91a360a1b1070753086f976608fec38af) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "254-c3.c3", 0x1000000, 0x800000, CRC(e53ff2dc) SHA1(31f6aaffe28146d574aa72f14f90a9d968f36bc6) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "254-c4.c4", 0x1000001, 0x800000, CRC(818672f0) SHA1(460c6738d0ee5ae440a23fc1434fab53bbb242b5) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "254-c5.c5", 0x2000000, 0x800000, CRC(4580eacd) SHA1(feb96eb5e80c9125ddd7836e0939212cd3011c34) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "254-c6.c6", 0x2000001, 0x800000, CRC(e34970fc) SHA1(6e43e15e27bc914357f977116ab1e2d98711bb21) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "254-c7.c7", 0x3000000, 0x800000, CRC(f2323239) SHA1(5b3e8dd77474203be010ec7363858d806344a320) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "254-c8.c8", 0x3000001, 0x800000, CRC(66848c7d) SHA1(24d4ed627940a4cf8129761c1da15556e52e199c) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

/****************************************
 ID-0255
 . ??M-2550
 NEO-MVS PROGBK1 / NEO-MVS CHAFIO (1999.6.14) (NEO-CMC 7042)
****************************************/

ROM_START( preisle2 ) /* Original Version, Encrypted GFX */ /* MVS ONLY RELEASE */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "255-p1.p1",  0x000000, 0x100000, CRC(dfa3c0f3) SHA1(793c6a46f3a794536dc0327a3f3fad20e25ab661) ) /* TC538200 */
	ROM_LOAD16_WORD_SWAP( "255-p2.sp2", 0x100000, 0x400000, CRC(42050b80) SHA1(0981a8295d43b264c2b95e5d7568bdda4e64c976) ) /* TC5332205 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_128K( "255-m1.m1", CRC(8efd4014) SHA1(5b73809b6e4e49264d281ef3e5004ac8a9de296d) ) /* TC531001DP */

	ROM_REGION( 0x0600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "255-v1.v1", 0x000000, 0x400000, CRC(5a14543d) SHA1(7146ac748f846c7e2d5b0bdcf953892e39b648fe) ) /* TC5332204 */
	ROM_LOAD( "255-v2.v2", 0x400000, 0x200000, CRC(6610d91a) SHA1(b2c6786920dc1712e88c3cc26d2c6c3ac2615bf4) ) /* TC5316200 */

	ROM_REGION( 0x3000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "255-c1.c1", 0x0000000, 0x800000, CRC(ea06000b) SHA1(1539b12e461fa48301190eb8171bbffff9d984b7) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "255-c2.c2", 0x0000001, 0x800000, CRC(04e67d79) SHA1(aadb6ee750da2c14c6eededa2218db95e051a32c) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "255-c3.c3", 0x1000000, 0x800000, CRC(60e31e08) SHA1(bd5b81ad9d04cdc4e0df31ac40eca305f98277eb) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "255-c4.c4", 0x1000001, 0x800000, CRC(40371d69) SHA1(90011ccc5672ff1b90737cf50c963e71b6217ce3) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "255-c5.c5", 0x2000000, 0x800000, CRC(0b2e6adf) SHA1(15c7d9aa8b1ad9a071e6fd0ef0de8a057c23b02e) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "255-c6.c6", 0x2000001, 0x800000, CRC(b001bdd3) SHA1(394ba8004644844ee97a120cfda48aeac685af8a) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

/****************************************
 ID-0256
 . NGM-2560
 SMA protected version found on:
 NEO-MVS PROGLBA (1999.4.12) (NEO-SMA)(LBA-SUB) / NEO-MVS CHAFIO (1999.6.14) (NEO-CMC 7042)
 Non SMA protected version found on:
 NEO-MVS PROGBK1 / NEO-MVS CHAFIO (1999.6.14) (NEO-CMC 7042)
 . NGH-2560
 SMA protected version found on:
 NEO-AEG PROGLBA (1999.7.6) (NEO-SMA)(LBA-SUB) / NEO-AEG CHAFIO (1999.8.10) (NEO-CMC 7042)
 Non SMA protected version found on:
 NEO-AEG PROGBK1F / NEO-AEG CHAFIO (1999.8.10) (NEO-CMC 7042)
****************************************/

ROM_START( mslug3 ) /* Original Version - Encrypted Code & GFX */ /* revision 2000.4.1 */ /* MVS VERSION */
	ROM_REGION( 0x900000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "green.neo-sma",    0x0c0000, 0x040000, CRC(9cd55736) SHA1(d6efb2b313127c2911d47d9324626b3f1e7c6ccb) )  /* stored in the custom SMA chip, the SMA has a green colour marking */
	ROM_LOAD16_WORD_SWAP( "256-pg1.p1", 0x100000, 0x400000, CRC(b07edfd5) SHA1(dcbd9e500bfae98d754e55cdbbbbf9401013f8ee) ) /* TC5332202 */
	ROM_LOAD16_WORD_SWAP( "256-pg2.p2", 0x500000, 0x400000, CRC(6097c26b) SHA1(248ec29d21216f29dc6f5f3f0e1ad1601b3501b6) ) /* TC5332202 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x80000, "cslot1:fixed", 0 ) /* larger char set */
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_512K( "256-m1.m1", CRC(eaeec116) SHA1(54419dbb21edc8c4b37eaac2e7ad9496d2de037a) ) /* mask rom TC534000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "256-v1.v1", 0x000000, 0x400000, CRC(f2690241) SHA1(fd56babc1934d10e0d27c32f032f9edda7ca8ce9) ) /* TC5332204 */
	ROM_LOAD( "256-v2.v2", 0x400000, 0x400000, CRC(7e2a10bd) SHA1(0d587fb9f64cba0315ce2d8a03e2b8fe34936dff) ) /* TC5332204 */
	ROM_LOAD( "256-v3.v3", 0x800000, 0x400000, CRC(0eaec17c) SHA1(c3ed613cc6993edd6fc0d62a90bcd85de8e21915) ) /* TC5332204 */
	ROM_LOAD( "256-v4.v4", 0xc00000, 0x400000, CRC(9b4b22d4) SHA1(9764fbf8453e52f80aa97a46fb9cf5937ef15a31) ) /* TC5332204 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "256-c1.c1", 0x0000000, 0x800000, CRC(5a79c34e) SHA1(b8aa51fa50935cae62ab3d125b723ab888691e60) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c2.c2", 0x0000001, 0x800000, CRC(944c362c) SHA1(3843ab300f956280475469caee70135658f67089) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c3.c3", 0x1000000, 0x800000, CRC(6e69d36f) SHA1(94e8cf42e999114b4bd8b30e0aa2f365578c4c9a) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c4.c4", 0x1000001, 0x800000, CRC(b755b4eb) SHA1(804700a0966a48f130c434ede3f970792ea74fa5) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c5.c5", 0x2000000, 0x800000, CRC(7aacab47) SHA1(312c1c9846175fe1a3cad51d5ae230cf674fc93d) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c6.c6", 0x2000001, 0x800000, CRC(c698fd5d) SHA1(16818883b06849ba2f8d61bdd5e21aaf99bd8408) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c7.c7", 0x3000000, 0x800000, CRC(cfceddd2) SHA1(7def666adf8bd1703f40c61f182fc040b6362dc9) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c8.c8", 0x3000001, 0x800000, CRC(4d9be34c) SHA1(a737bdfa2b815aea7067e7af2636e83a9409c414) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

/*
    The Program roms for the set below are actually 4 ROMs contained on a recycled NeoGeo Pocket card and marked

    SNK48249Q
    JAPAN9948HAK
    T8V12A
    (two of these)

    SNK48327V
    JAPAN9948HAK
    T8V12A
    (two of these)

    KOF2000 and Garou boards have been found with the same setup (data verified to match existing sets)
    The roms were dumped via the NGPC cartridge edge connector, so the exact way they should be split into 4 is unknown.
    As a result the ROM has been split to match the usual configuration as it likely exists like this too anyway.
*/

ROM_START( mslug3a ) /* Original Version - Encrypted Code & GFX */ /* MVS VERSION */
	ROM_REGION( 0x900000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "white.neo-sma",    0x0c0000, 0x040000, CRC(c60d29b2) SHA1(1647260ccbda833b35005608ef1fdc82fba02f04) ) /* stored in the custom SMA chip, the SMA has a white colour marking */
	ROM_LOAD16_WORD_SWAP( "256.p1", 0x100000, 0x400000, CRC(a1177628) SHA1(4c4c379d9fc3a83265b7f32fbfce9d16b7d0f0fd) )
	ROM_LOAD16_WORD_SWAP( "256.p2", 0x500000, 0x400000, CRC(9b659826) SHA1(d6bd03cf61879217922c18db4d3bd77095c0fe19) )

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x80000, "cslot1:fixed", 0 ) /* larger char set */
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_512K( "256-m1.m1", CRC(eaeec116) SHA1(54419dbb21edc8c4b37eaac2e7ad9496d2de037a) ) /* mask rom TC534000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "256-v1.v1", 0x000000, 0x400000, CRC(f2690241) SHA1(fd56babc1934d10e0d27c32f032f9edda7ca8ce9) ) /* TC5332204 */
	ROM_LOAD( "256-v2.v2", 0x400000, 0x400000, CRC(7e2a10bd) SHA1(0d587fb9f64cba0315ce2d8a03e2b8fe34936dff) ) /* TC5332204 */
	ROM_LOAD( "256-v3.v3", 0x800000, 0x400000, CRC(0eaec17c) SHA1(c3ed613cc6993edd6fc0d62a90bcd85de8e21915) ) /* TC5332204 */
	ROM_LOAD( "256-v4.v4", 0xc00000, 0x400000, CRC(9b4b22d4) SHA1(9764fbf8453e52f80aa97a46fb9cf5937ef15a31) ) /* TC5332204 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "256-c1.c1", 0x0000000, 0x800000, CRC(5a79c34e) SHA1(b8aa51fa50935cae62ab3d125b723ab888691e60) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c2.c2", 0x0000001, 0x800000, CRC(944c362c) SHA1(3843ab300f956280475469caee70135658f67089) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c3.c3", 0x1000000, 0x800000, CRC(6e69d36f) SHA1(94e8cf42e999114b4bd8b30e0aa2f365578c4c9a) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c4.c4", 0x1000001, 0x800000, CRC(b755b4eb) SHA1(804700a0966a48f130c434ede3f970792ea74fa5) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c5.c5", 0x2000000, 0x800000, CRC(7aacab47) SHA1(312c1c9846175fe1a3cad51d5ae230cf674fc93d) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c6.c6", 0x2000001, 0x800000, CRC(c698fd5d) SHA1(16818883b06849ba2f8d61bdd5e21aaf99bd8408) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c7.c7", 0x3000000, 0x800000, CRC(cfceddd2) SHA1(7def666adf8bd1703f40c61f182fc040b6362dc9) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c8.c8", 0x3000001, 0x800000, CRC(4d9be34c) SHA1(a737bdfa2b815aea7067e7af2636e83a9409c414) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END


ROM_START( mslug3h ) /* Original Version - Encrypted GFX */ /* revision 2000.3.17 */ /* AES VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "256-ph1.p1",  0x000000, 0x100000, CRC(9c42ca85) SHA1(7a8f77a89867b889295ae9b9dfd4ba28f02d234d) )
	ROM_LOAD16_WORD_SWAP( "256-ph2.sp2", 0x100000, 0x400000, CRC(1f3d8ce8) SHA1(08b05a8abfb86ec09a5e758d6273acf1489961f9) )
	/* also found AES sets with p1 / p2 on maskrom on NEO-AEG PROGLBA (NEO-SMA); chip labels are 256-PG1 and 256-PG2 */
	/* The SMA on those sets has a pink or green color marking */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x80000, "cslot1:fixed", 0 ) /* larger char set */
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_512K( "256-m1.m1", CRC(eaeec116) SHA1(54419dbb21edc8c4b37eaac2e7ad9496d2de037a) ) /* mask rom TC534000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "256-v1.v1", 0x000000, 0x400000, CRC(f2690241) SHA1(fd56babc1934d10e0d27c32f032f9edda7ca8ce9) ) /* TC5332204 */
	ROM_LOAD( "256-v2.v2", 0x400000, 0x400000, CRC(7e2a10bd) SHA1(0d587fb9f64cba0315ce2d8a03e2b8fe34936dff) ) /* TC5332204 */
	ROM_LOAD( "256-v3.v3", 0x800000, 0x400000, CRC(0eaec17c) SHA1(c3ed613cc6993edd6fc0d62a90bcd85de8e21915) ) /* TC5332204 */
	ROM_LOAD( "256-v4.v4", 0xc00000, 0x400000, CRC(9b4b22d4) SHA1(9764fbf8453e52f80aa97a46fb9cf5937ef15a31) ) /* TC5332204 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "256-c1.c1", 0x0000000, 0x800000, CRC(5a79c34e) SHA1(b8aa51fa50935cae62ab3d125b723ab888691e60) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c2.c2", 0x0000001, 0x800000, CRC(944c362c) SHA1(3843ab300f956280475469caee70135658f67089) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c3.c3", 0x1000000, 0x800000, CRC(6e69d36f) SHA1(94e8cf42e999114b4bd8b30e0aa2f365578c4c9a) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c4.c4", 0x1000001, 0x800000, CRC(b755b4eb) SHA1(804700a0966a48f130c434ede3f970792ea74fa5) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c5.c5", 0x2000000, 0x800000, CRC(7aacab47) SHA1(312c1c9846175fe1a3cad51d5ae230cf674fc93d) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c6.c6", 0x2000001, 0x800000, CRC(c698fd5d) SHA1(16818883b06849ba2f8d61bdd5e21aaf99bd8408) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c7.c7", 0x3000000, 0x800000, CRC(cfceddd2) SHA1(7def666adf8bd1703f40c61f182fc040b6362dc9) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c8.c8", 0x3000001, 0x800000, CRC(4d9be34c) SHA1(a737bdfa2b815aea7067e7af2636e83a9409c414) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

/****************************************
 ID-0257
 . NGM-2570
 SMA protected version found on:
 NEO-MVS PROGLBA (1999.4.12) (NEO-SMA)(LBA-SUB) / NEO-MVS CHAFIO (1999.6.14) (NEO-CMC 7050)
 Non SMA protected version found on:
 NEO-MVS PROGBK1 / NEO-MVS CHAFIO (1999.6.14) (NEO-CMC 7050)
 . NGH-2570
 NEO-AEG PROGLBA (1999.7.6) (NEO-SMA) / NEO-AEG CHAFIO (1999.8.10) (NEO-CMC 7050)
****************************************/

ROM_START( kof2000 ) /* Original Version, Encrypted Code + Sound + GFX Roms */ /* MVS AND AES VERSION */
	ROM_REGION( 0x900000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "neo-sma",   0x0c0000, 0x040000, CRC(71c6e6bb) SHA1(1bd29ded4c6b29780db8e8b772c452189699ca89) ) /* stored in the custom chip */
	ROM_LOAD16_WORD_SWAP( "257-p1.p1", 0x100000, 0x400000, CRC(60947b4c) SHA1(5faa0a7ac7734d6c8e276589bd12dd574264647d) ) /* mask rom TC5332202 */
	ROM_LOAD16_WORD_SWAP( "257-p2.p2", 0x500000, 0x400000, CRC(1b7ec415) SHA1(f19fa44e9ee5b5a6eb4a051349d6bc4acc3bbbdb) ) /* mask rom TC5332202 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x80000, "cslot1:fixed", 0 )   /* larger char set */
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_256K( "257-m1.m1", CRC(4b749113) SHA1(2af2361146edd0ce3966614d90165a5c1afb8de4) ) /* mask rom TC532000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "257-v1.v1", 0x000000, 0x400000, CRC(17cde847) SHA1(4bcc0205b70dc6d9216b29025450c9c5b08cb65d) ) /* TC5332204 */
	ROM_LOAD( "257-v2.v2", 0x400000, 0x400000, CRC(1afb20ff) SHA1(57dfd2de058139345ff2b744a225790baaecd5a2) ) /* TC5332204 */
	ROM_LOAD( "257-v3.v3", 0x800000, 0x400000, CRC(4605036a) SHA1(51b228a0600d38a6ec37aec4822879ec3b0ee106) ) /* TC5332204 */
	ROM_LOAD( "257-v4.v4", 0xc00000, 0x400000, CRC(764bbd6b) SHA1(df23c09ca6cf7d0ae5e11ff16e30c159725106b3) ) /* TC5332204 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "257-c1.c1", 0x0000000, 0x800000, CRC(cef1cdfa) SHA1(6135080f3a6b4712b76cc217edcc58e72b55c2b9) ) /* Plane 0,1 */  /* TC5364205 */
	ROM_LOAD16_BYTE( "257-c2.c2", 0x0000001, 0x800000, CRC(f7bf0003) SHA1(9f7b19a2100cf7d12867e742f440dd5277b4f895) ) /* Plane 2,3 */  /* TC5364205 */
	ROM_LOAD16_BYTE( "257-c3.c3", 0x1000000, 0x800000, CRC(101e6560) SHA1(8073ae1139e215d1167f8d32c14079a46ce3ee1c) ) /* Plane 0,1 */  /* TC5364205 */
	ROM_LOAD16_BYTE( "257-c4.c4", 0x1000001, 0x800000, CRC(bd2fc1b1) SHA1(da0006761923ad49b404a08d7a151193ee307a69) ) /* Plane 2,3 */  /* TC5364205 */
	ROM_LOAD16_BYTE( "257-c5.c5", 0x2000000, 0x800000, CRC(89775412) SHA1(b221b30224bc4239f1b3c2d2fd1cd4fa84e3523c) ) /* Plane 0,1 */  /* TC5364205 */
	ROM_LOAD16_BYTE( "257-c6.c6", 0x2000001, 0x800000, CRC(fa7200d5) SHA1(6f2b0d38af34e280d56a58955400e5c679906871) ) /* Plane 2,3 */  /* TC5364205 */
	ROM_LOAD16_BYTE( "257-c7.c7", 0x3000000, 0x800000, CRC(7da11fe4) SHA1(065336cf166807acb6c8569d59d3bf37a19b0a42) ) /* Plane 0,1 */  /* TC5364205 */
	ROM_LOAD16_BYTE( "257-c8.c8", 0x3000001, 0x800000, CRC(b1afa60b) SHA1(b916184f5cfe4121752270f4f65abf35d8eb0519) ) /* Plane 2,3 */  /* TC5364205 */
ROM_END

ROM_START( kof2000n ) /* Original Version, Encrypted Sound + GFX Roms */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "257-pg1.p1",  0x000000, 0x100000, CRC(5f809dbe) SHA1(2bc233dcff5622de86d01e3b74b840c7caf12982) ) /* mask rom TC538200 */
	ROM_LOAD16_WORD_SWAP( "257-pg2.sp2", 0x100000, 0x400000, CRC(693c2c5e) SHA1(dc9121b7369ef46596343cac055a00aec81704d4) ) /* mask rom TC5332205 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x80000, "cslot1:fixed", 0 )   /* larger char set */
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_256K( "257-m1.m1", CRC(4b749113) SHA1(2af2361146edd0ce3966614d90165a5c1afb8de4) ) /* mask rom TC532000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "257-v1.v1", 0x000000, 0x400000, CRC(17cde847) SHA1(4bcc0205b70dc6d9216b29025450c9c5b08cb65d) ) /* TC5332204 */
	ROM_LOAD( "257-v2.v2", 0x400000, 0x400000, CRC(1afb20ff) SHA1(57dfd2de058139345ff2b744a225790baaecd5a2) ) /* TC5332204 */
	ROM_LOAD( "257-v3.v3", 0x800000, 0x400000, CRC(4605036a) SHA1(51b228a0600d38a6ec37aec4822879ec3b0ee106) ) /* TC5332204 */
	ROM_LOAD( "257-v4.v4", 0xc00000, 0x400000, CRC(764bbd6b) SHA1(df23c09ca6cf7d0ae5e11ff16e30c159725106b3) ) /* TC5332204 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "257-c1.c1", 0x0000000, 0x800000, CRC(cef1cdfa) SHA1(6135080f3a6b4712b76cc217edcc58e72b55c2b9) ) /* Plane 0,1 */  /* TC5364205 */
	ROM_LOAD16_BYTE( "257-c2.c2", 0x0000001, 0x800000, CRC(f7bf0003) SHA1(9f7b19a2100cf7d12867e742f440dd5277b4f895) ) /* Plane 2,3 */  /* TC5364205 */
	ROM_LOAD16_BYTE( "257-c3.c3", 0x1000000, 0x800000, CRC(101e6560) SHA1(8073ae1139e215d1167f8d32c14079a46ce3ee1c) ) /* Plane 0,1 */  /* TC5364205 */
	ROM_LOAD16_BYTE( "257-c4.c4", 0x1000001, 0x800000, CRC(bd2fc1b1) SHA1(da0006761923ad49b404a08d7a151193ee307a69) ) /* Plane 2,3 */  /* TC5364205 */
	ROM_LOAD16_BYTE( "257-c5.c5", 0x2000000, 0x800000, CRC(89775412) SHA1(b221b30224bc4239f1b3c2d2fd1cd4fa84e3523c) ) /* Plane 0,1 */  /* TC5364205 */
	ROM_LOAD16_BYTE( "257-c6.c6", 0x2000001, 0x800000, CRC(fa7200d5) SHA1(6f2b0d38af34e280d56a58955400e5c679906871) ) /* Plane 2,3 */  /* TC5364205 */
	ROM_LOAD16_BYTE( "257-c7.c7", 0x3000000, 0x800000, CRC(7da11fe4) SHA1(065336cf166807acb6c8569d59d3bf37a19b0a42) ) /* Plane 0,1 */  /* TC5364205 */
	ROM_LOAD16_BYTE( "257-c8.c8", 0x3000001, 0x800000, CRC(b1afa60b) SHA1(b916184f5cfe4121752270f4f65abf35d8eb0519) ) /* Plane 2,3 */  /* TC5364205 */
ROM_END

/****************************************
 ID-0258
 SNK vs. Capcom?
****************************************/

/****************************************
 ID-0259
 . ??M-2590
 NEO-MVS PROGBK1 / NEO-MVS CHAFIO (1999.6.14) (NEO-CMC 7042)
****************************************/

ROM_START( bangbead ) /* Original Version - Encrypted GFX */ /* MVS ONLY RELEASE */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "259-p1.p1", 0x100000, 0x100000, CRC(88a37f8b) SHA1(566db84850fad5e8fe822e8bba910a33e083b550) ) /* TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_128K( "259-m1.m1", CRC(85668ee9) SHA1(7d3f51710cf90c097cd3faaeeef10ceb85cbb3e8) ) /* TC531001DP */

	ROM_REGION( 0x500000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "259-v1.v1", 0x000000, 0x400000, CRC(088eb8ab) SHA1(608306e35501dd7d382d9f96b28e7550aa896a03) ) /* TC533204 */
	ROM_LOAD( "259-v2.v2", 0x400000, 0x100000, CRC(97528fe9) SHA1(8f5eddbb3a9a225492479d1a44801f3916c8e791) ) /* TC538200 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "259-c1.c1", 0x0000000, 0x800000, CRC(1f537f74) SHA1(b8ef691e92191c20a5ed4f20a75cca3c7383bca6) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "259-c2.c2", 0x0000001, 0x800000, CRC(0efd98ff) SHA1(d350315d3c7f26d638458e5ccf2126069a4c7a5b) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

/****************************************
 ID-0260
 . ??M-2600
 NEO-MVS PROGBK1 / NEO-MVS CHAFIO (1999.6.14) (NEO-CMC 7042)
****************************************/

ROM_START( nitd ) /* Original Version - Encrypted GFX */ /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "260-p1.p1", 0x000000, 0x080000, CRC(61361082) SHA1(441f3f41c1aa752c0e0a9a0b1d92711d9e636b85) ) /* TC534200 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_512K( "260-m1.m1", CRC(6407c5e5) SHA1(d273e154cc905b63205a17a1a6d419cac3485a92) ) /* TC534000 */

	ROM_REGION( 0x0400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "260-v1.v1", 0x000000, 0x400000, CRC(24b0480c) SHA1(d769e621be52a5cd2e2568891b5f95a48268e1e0) ) /* TC5332204 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "260-c1.c1", 0x0000000, 0x800000, CRC(147b0c7f) SHA1(a647c3a2f6d146ff47521c1d39f58830601f5781) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "260-c2.c2", 0x0000001, 0x800000, CRC(d2b04b0d) SHA1(ce4322e6cfacb627fe997efe81018861e21d3c27) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

/****************************************
 ID-0261
 . NGM-2610
 NEO-MVS PROGBK1 / NEO-MVS CHAFIO (1999.6.14) (NEO-CMC 7042)
 . NGH-2610
 NEO-AEG PROGBK1F / NEO-AEG CHAFIO (1999.8.10) (NEO-CMC 7042)
****************************************/

ROM_START( sengoku3 ) /* Original Version - Encrypted GFX */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "261-ph1.p1", 0x100000, 0x100000, CRC(e0d4bc0a) SHA1(8df366097f224771ca6d1aa5c1691cd46776cd12) ) /* mask rom TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_512K( "261-m1.m1", CRC(7d501c39) SHA1(8e6bcc428f5ac7532d9c9be7e07ad0821461a080) ) /* mask rom TC534000 */

	ROM_REGION( 0x0e00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "261-v1.v1", 0x000000, 0x400000, CRC(64c30081) SHA1(f9ebd20cf59b72e864b7274c1bdb6d99ecaf4595) ) /* mask rom TC5332204 */
	ROM_LOAD( "261-v2.v2", 0x400000, 0x400000, CRC(392a9c47) SHA1(7ab90a54089236ca6c3ef1af8e566a8025d38159) ) /* mask rom TC5332204 */
	ROM_LOAD( "261-v3.v3", 0x800000, 0x400000, CRC(c1a7ebe3) SHA1(1d7bb481451f5ee0457e954bb5210300182c3c9c) ) /* mask rom TC5332204 */
	ROM_LOAD( "261-v4.v4", 0xc00000, 0x200000, CRC(9000d085) SHA1(11157b355ab4eb6627e9f322ed875332d3d77349) ) /* mask rom TC5316200 */

	ROM_REGION( 0x2000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "261-c1.c1", 0x0000000, 0x800000, CRC(ded84d9c) SHA1(d960523b813d4fae06d716298d4e431a5c77a0c5) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "261-c2.c2", 0x0000001, 0x800000, CRC(b8eb4348) SHA1(619d24312549932959481fa58f43f11c048e1ca5) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "261-c3.c3", 0x1000000, 0x800000, CRC(84e2034a) SHA1(38ec4ae4b86933a25c9a03799b8cade4b1346401) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "261-c4.c4", 0x1000001, 0x800000, CRC(0b45ae53) SHA1(a19fb21408ab633aee8bbf38bf43b5e26766b355) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

// only 2 bytes differ from above set, but both have been verified on multiple carts for each label.
ROM_START( sengoku3a ) /* Original Version - Encrypted GFX */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "261-pg1.p1", 0x100000, 0x100000, CRC(5b557201) SHA1(d01421d1dc80fe7d2a46b9f79c0f344b3c81c1e7) ) /* mask rom TC5316200 */
	ROM_CONTINUE( 0x000000, 0x100000 )

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_512K( "261-m1.m1", CRC(7d501c39) SHA1(8e6bcc428f5ac7532d9c9be7e07ad0821461a080) ) /* mask rom TC534000 */

	ROM_REGION( 0x0e00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "261-v1.v1", 0x000000, 0x400000, CRC(64c30081) SHA1(f9ebd20cf59b72e864b7274c1bdb6d99ecaf4595) ) /* mask rom TC5332204 */
	ROM_LOAD( "261-v2.v2", 0x400000, 0x400000, CRC(392a9c47) SHA1(7ab90a54089236ca6c3ef1af8e566a8025d38159) ) /* mask rom TC5332204 */
	ROM_LOAD( "261-v3.v3", 0x800000, 0x400000, CRC(c1a7ebe3) SHA1(1d7bb481451f5ee0457e954bb5210300182c3c9c) ) /* mask rom TC5332204 */
	ROM_LOAD( "261-v4.v4", 0xc00000, 0x200000, CRC(9000d085) SHA1(11157b355ab4eb6627e9f322ed875332d3d77349) ) /* mask rom TC5316200 */

	ROM_REGION( 0x2000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "261-c1.c1", 0x0000000, 0x800000, CRC(ded84d9c) SHA1(d960523b813d4fae06d716298d4e431a5c77a0c5) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "261-c2.c2", 0x0000001, 0x800000, CRC(b8eb4348) SHA1(619d24312549932959481fa58f43f11c048e1ca5) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "261-c3.c3", 0x1000000, 0x800000, CRC(84e2034a) SHA1(38ec4ae4b86933a25c9a03799b8cade4b1346401) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "261-c4.c4", 0x1000001, 0x800000, CRC(0b45ae53) SHA1(a19fb21408ab633aee8bbf38bf43b5e26766b355) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

/****************************************
 ID-0262
 . NGM-262?
 MVS PROGBK2 REV1.0 (NEO-PCM2 SNK)/ MVS CHAFIO REV1.0 (NEO-CMC 7050)
 . NGH-2621
 NEO-AEG PROGBK1F / NEO-AEG CHAFIO (1999.8.10) (NEO-CMC 7050)
  - Distribution by BrezzaSoft
****************************************/

ROM_START( kof2001 ) /* MVS VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "262-p1-08-e0.p1",  0x000000, 0x100000, CRC(9381750d) SHA1(dcfecd69e563ff52fe07d23c5372d0f748b07819) ) /* mask rom TC538200 */
	ROM_LOAD16_WORD_SWAP( "262-p2-08-e0.sp2", 0x100000, 0x400000, CRC(8e0d8329) SHA1(10dcc1baf0aaf1fc84c4d856bca6bcff85aed2bc) ) /* mask rom TC5332205 */
	/* The first/early production run sets have proms with above labels. Some later? sets found have eproms instead of proms */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_256K( "265-262-m1.m1", CRC(a7f8119f) SHA1(71805b39b8b09c32425cf39f9de59b2f755976c2) ) /* mask rom TC532000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "262-v1-08-e0.v1", 0x000000, 0x400000, CRC(83d49ecf) SHA1(2f2c116e45397652e77fcf5d951fa5f71b639572) ) /* mask rom TC5332204 */
	ROM_LOAD( "262-v2-08-e0.v2", 0x400000, 0x400000, CRC(003f1843) SHA1(bdd58837ad542548bd4053c262f558af88e3b989) ) /* mask rom TC5332204 */
	ROM_LOAD( "262-v3-08-e0.v3", 0x800000, 0x400000, CRC(2ae38dbe) SHA1(4e82b7dd3b899d61907620517a5a27bdaba0725d) ) /* mask rom TC5332204 */
	ROM_LOAD( "262-v4-08-e0.v4", 0xc00000, 0x400000, CRC(26ec4dd9) SHA1(8bd68d95a2d913be41a51f51e48dbe3bff5924fb) ) /* mask rom TC5332204 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "262-c1-08-e0.c1", 0x0000000, 0x800000, CRC(99cc785a) SHA1(374f0674871d0196fa274aa6c5956d7b3848d5da) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "262-c2-08-e0.c2", 0x0000001, 0x800000, CRC(50368cbf) SHA1(5d9e206e98e0b0c7735b72ea46b45058fdec2352) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "262-c3-08-e0.c3", 0x1000000, 0x800000, CRC(fb14ff87) SHA1(445a8db2fc69eff54a252700f2d3a89244c58e75) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "262-c4-08-e0.c4", 0x1000001, 0x800000, CRC(4397faf8) SHA1(6752b394f6647502a649a3e62bd3442f936b733e) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "262-c5-08-e0.c5", 0x2000000, 0x800000, CRC(91f24be4) SHA1(88190c41f7d4a0f4b1982149fc9acfc640af498d) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "262-c6-08-e0.c6", 0x2000001, 0x800000, CRC(a31e4403) SHA1(5cd1a14703aa58810e2377dfb7353c61e9dc9c1f) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "262-c7-08-e0.c7", 0x3000000, 0x800000, CRC(54d9d1ec) SHA1(80c3a8ec39130dd5d3da561f287709da6b8abcf4) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "262-c8-08-e0.c8", 0x3000001, 0x800000, CRC(59289a6b) SHA1(ddfce7c85b2a144975db5bb14b4b51aaf881880e) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

ROM_START( kof2001h ) /* AES VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "262-pg1.p1",  0x000000, 0x100000, CRC(2af7e741) SHA1(e41282d73ed6d521da056f1a16573bb61bfa3826) ) /* mask rom TC538200 */
	ROM_LOAD16_WORD_SWAP( "262-pg2.sp2", 0x100000, 0x400000, CRC(91eea062) SHA1(82bae42bbeedb9f3aa0c7c0b0a7a69be499cf98f) ) /* mask rom TC5332205 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_256K( "265-262-m1.m1", CRC(a7f8119f) SHA1(71805b39b8b09c32425cf39f9de59b2f755976c2) ) /* mask rom TC532000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "262-v1-08-e0.v1", 0x000000, 0x400000, CRC(83d49ecf) SHA1(2f2c116e45397652e77fcf5d951fa5f71b639572) ) /* mask rom TC5332204 */
	ROM_LOAD( "262-v2-08-e0.v2", 0x400000, 0x400000, CRC(003f1843) SHA1(bdd58837ad542548bd4053c262f558af88e3b989) ) /* mask rom TC5332204 */
	ROM_LOAD( "262-v3-08-e0.v3", 0x800000, 0x400000, CRC(2ae38dbe) SHA1(4e82b7dd3b899d61907620517a5a27bdaba0725d) ) /* mask rom TC5332204 */
	ROM_LOAD( "262-v4-08-e0.v4", 0xc00000, 0x400000, CRC(26ec4dd9) SHA1(8bd68d95a2d913be41a51f51e48dbe3bff5924fb) ) /* mask rom TC5332204 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "262-c1-08-e0.c1", 0x0000000, 0x800000, CRC(99cc785a) SHA1(374f0674871d0196fa274aa6c5956d7b3848d5da) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "262-c2-08-e0.c2", 0x0000001, 0x800000, CRC(50368cbf) SHA1(5d9e206e98e0b0c7735b72ea46b45058fdec2352) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "262-c3-08-e0.c3", 0x1000000, 0x800000, CRC(fb14ff87) SHA1(445a8db2fc69eff54a252700f2d3a89244c58e75) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "262-c4-08-e0.c4", 0x1000001, 0x800000, CRC(4397faf8) SHA1(6752b394f6647502a649a3e62bd3442f936b733e) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "262-c5-08-e0.c5", 0x2000000, 0x800000, CRC(91f24be4) SHA1(88190c41f7d4a0f4b1982149fc9acfc640af498d) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "262-c6-08-e0.c6", 0x2000001, 0x800000, CRC(a31e4403) SHA1(5cd1a14703aa58810e2377dfb7353c61e9dc9c1f) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "262-c7-08-e0.c7", 0x3000000, 0x800000, CRC(54d9d1ec) SHA1(80c3a8ec39130dd5d3da561f287709da6b8abcf4) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "262-c8-08-e0.c8", 0x3000001, 0x800000, CRC(59289a6b) SHA1(ddfce7c85b2a144975db5bb14b4b51aaf881880e) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

/****************************************
 ID-0263
 . NGM-2630
 NEO-MVS PROGBK2 (NEO-PCM2 SNK)/ NEO-MVS CHAFIO (NEO-CMC 7050)
 . NGH-2630
 NEO-AEG PROGBK2 (2002.4.1) (NEO-PCM2 SNK) / NEO-AEG CHAFIO (1999.8.10) (NEO-CMC 7050)
****************************************/

ROM_START( mslug4 ) /* Original Version - Encrypted GFX */ /* MVS VERSION */
	/* There also exist carts where p1 label is pg1; the PG1 revision has a Japanese cart label, SN 02Jxxxxx
	The P1 revision has a US/EUR cart label, SN 02Txxxxx ; Rom data on both is identical.
	These carts were manufactured by Mega Enterprise, not SNK. */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "263-p1.p1",  0x000000, 0x100000, CRC(27e4def3) SHA1(a08785e8145981bb6b5332a3b2df7eb321253cca) ) /* mask rom TC538200 */
	ROM_LOAD16_WORD_SWAP( "263-p2.sp2", 0x100000, 0x400000, CRC(fdb7aed8) SHA1(dbeaec38f44e58ffedba99e70fa1439c2bf0dfa3) ) /* mask rom TC5332205 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x80000, "cslot1:fixed", 0 )   /* larger char set */
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_128K( "263-m1.m1", CRC(46ac8228) SHA1(5aeea221050c98e4bb0f16489ce772bf1c80f787) ) /* mask rom TC531001 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "263-v1.v1", 0x000000, 0x800000, CRC(01e9b9cd) SHA1(0b045c2999449f7dab5ae8a42e957d5b6650431e) ) /* mask rom TC5364205 */
	ROM_LOAD( "263-v2.v2", 0x800000, 0x800000, CRC(4ab2bf81) SHA1(77ccfa48f7e3daddef5fe5229a0093eb2f803742) ) /* mask rom TC5364205 */

	ROM_REGION( 0x3000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "263-c1.c1", 0x0000000, 0x800000, CRC(84865f8a) SHA1(34467ada896eb7c7ca58658bf2a932936d8b632c) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "263-c2.c2", 0x0000001, 0x800000, CRC(81df97f2) SHA1(2b74493b8ec8fd49216a627aeb3db493f76124e3) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "263-c3.c3", 0x1000000, 0x800000, CRC(1a343323) SHA1(bbbb5232bba538c277ce2ee02e2956ca2243b787) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "263-c4.c4", 0x1000001, 0x800000, CRC(942cfb44) SHA1(d9b46c71726383c4581fb042e63897e5a3c92d1b) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "263-c5.c5", 0x2000000, 0x800000, CRC(a748854f) SHA1(2611bbedf9b5d8e82c6b2c99b88f842c46434d41) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "263-c6.c6", 0x2000001, 0x800000, CRC(5c8ba116) SHA1(6034db09c8706d4ddbcefc053efbc47a0953eb92) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

ROM_START( mslug4h ) /* Original Version - Encrypted GFX */ /* AES VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "263-ph1.p1",  0x000000, 0x100000, CRC(c67f5c8d) SHA1(12af74964843f103520d9f0825069ea2f67eeb2f) ) /* mask rom TC538200 */
	ROM_LOAD16_WORD_SWAP( "263-ph2.sp2", 0x100000, 0x400000, CRC(bc3ec89e) SHA1(2cb0626bc4fa57e1d25f208e04532b570d87b3fb) ) /* mask rom TC5332205 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x80000, "cslot1:fixed", 0 )   /* larger char set */
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_128K( "263-m1.m1", CRC(46ac8228) SHA1(5aeea221050c98e4bb0f16489ce772bf1c80f787) ) /* mask rom TC531001 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "263-v1.v1", 0x000000, 0x800000, CRC(01e9b9cd) SHA1(0b045c2999449f7dab5ae8a42e957d5b6650431e) ) /* mask rom TC5364205 */
	ROM_LOAD( "263-v2.v2", 0x800000, 0x800000, CRC(4ab2bf81) SHA1(77ccfa48f7e3daddef5fe5229a0093eb2f803742) ) /* mask rom TC5364205 */

	ROM_REGION( 0x3000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "263-c1.c1", 0x0000000, 0x800000, CRC(84865f8a) SHA1(34467ada896eb7c7ca58658bf2a932936d8b632c) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "263-c2.c2", 0x0000001, 0x800000, CRC(81df97f2) SHA1(2b74493b8ec8fd49216a627aeb3db493f76124e3) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "263-c3.c3", 0x1000000, 0x800000, CRC(1a343323) SHA1(bbbb5232bba538c277ce2ee02e2956ca2243b787) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "263-c4.c4", 0x1000001, 0x800000, CRC(942cfb44) SHA1(d9b46c71726383c4581fb042e63897e5a3c92d1b) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "263-c5.c5", 0x2000000, 0x800000, CRC(a748854f) SHA1(2611bbedf9b5d8e82c6b2c99b88f842c46434d41) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "263-c6.c6", 0x2000001, 0x800000, CRC(5c8ba116) SHA1(6034db09c8706d4ddbcefc053efbc47a0953eb92) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

/****************************************
 ID-0264
 . NGM-264?
 NEO-MVS PROGBK2 (2000.3.21) (NEO-PCM2 SNK) / NEO-MVS CHAFIO (1999.6.14) (NEO-CMC 7050)
 . NGH-2641
 NEO-AEG PROGBK2 (2002.4.1) (NEO-PCM2 PLAYMORE) / NEO-AEG CHAFIO (1999.8.10) (NEO-CMC 7050)
  - Distribution by BrezzaSoft
****************************************/

ROM_START( rotd ) /* Encrypted Set */ /* MVS VERSION */
	ROM_REGION( 0x800000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "264-p1.p1", 0x000000, 0x800000, CRC(b8cc969d) SHA1(4f2205b4bdd32dd1522106ef4df10ac0eb1b852d) ) /* mask rom TC5364205 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_128K( "264-m1.m1", CRC(4dbd7b43) SHA1(6b63756b0d2d30bbf13fbd219833c81fd060ef96) ) /* mask rom 27c010 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "264-v1.v1", 0x000000, 0x800000, CRC(fa005812) SHA1(73723126dab5a640ac11955ed6da1bf7a91394f5) ) /* mask rom TC5364205 */
	ROM_LOAD( "264-v2.v2", 0x800000, 0x800000, CRC(c3dc8bf0) SHA1(a105e37262d9500a30fb8a5dac05aa4fab2562a3) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "264-c1.c1", 0x0000000, 0x800000, CRC(4f148fee) SHA1(0821463765fad8fbd0dfbbabb7807337d0333719) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "264-c2.c2", 0x0000001, 0x800000, CRC(7cf5ff72) SHA1(ccb2f94bce943576d224cb326806942426d25584) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "264-c3.c3", 0x1000000, 0x800000, CRC(64d84c98) SHA1(8faf153f465ce6fb7770b27a7ce63caf11dd4086) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "264-c4.c4", 0x1000001, 0x800000, CRC(2f394a95) SHA1(82347e8f2b48b0522d7d91fd3f372d5768934ab2) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "264-c5.c5", 0x2000000, 0x800000, CRC(6b99b978) SHA1(8fd0a60029b41668f9e1e3056edd3c90f62efa83) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "264-c6.c6", 0x2000001, 0x800000, CRC(847d5c7d) SHA1(a2ce03f6302edf81f2645de9ec61df1a281ddd78) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "264-c7.c7", 0x3000000, 0x800000, CRC(231d681e) SHA1(87836e64dc816f8bf1c834641535ea96baacc024) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "264-c8.c8", 0x3000001, 0x800000, CRC(c5edb5c4) SHA1(253378c8739daa5da4edb15eff7050820b2b3755) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END


ROM_START( rotdh ) /* Encrypted Set */ /* AES VERSION */
	ROM_REGION( 0x300000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "264-pk1.p1",  0x000000, 0x100000, CRC(ff2fa719) SHA1(03009e3693648ac0d892390c3bba2ceac6c9564b) )
	ROM_LOAD16_WORD_SWAP( "264-pk2.sp2", 0x100000, 0x200000, CRC(0df2e112) SHA1(c521783483117859a2b250190be77f6d49412ae8) )

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_128K( "264-m1.m1", CRC(4dbd7b43) SHA1(6b63756b0d2d30bbf13fbd219833c81fd060ef96) ) /* mask rom 27c010 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "264-v1.v1", 0x000000, 0x800000, CRC(fa005812) SHA1(73723126dab5a640ac11955ed6da1bf7a91394f5) ) /* mask rom TC5364205 */
	ROM_LOAD( "264-v2.v2", 0x800000, 0x800000, CRC(c3dc8bf0) SHA1(a105e37262d9500a30fb8a5dac05aa4fab2562a3) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "264-c1.c1", 0x0000000, 0x800000, CRC(4f148fee) SHA1(0821463765fad8fbd0dfbbabb7807337d0333719) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "264-c2.c2", 0x0000001, 0x800000, CRC(7cf5ff72) SHA1(ccb2f94bce943576d224cb326806942426d25584) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "264-c3.c3", 0x1000000, 0x800000, CRC(64d84c98) SHA1(8faf153f465ce6fb7770b27a7ce63caf11dd4086) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "264-c4.c4", 0x1000001, 0x800000, CRC(2f394a95) SHA1(82347e8f2b48b0522d7d91fd3f372d5768934ab2) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "264-c5.c5", 0x2000000, 0x800000, CRC(6b99b978) SHA1(8fd0a60029b41668f9e1e3056edd3c90f62efa83) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "264-c6.c6", 0x2000001, 0x800000, CRC(847d5c7d) SHA1(a2ce03f6302edf81f2645de9ec61df1a281ddd78) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "264-c7.c7", 0x3000000, 0x800000, CRC(231d681e) SHA1(87836e64dc816f8bf1c834641535ea96baacc024) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "264-c8.c8", 0x3000001, 0x800000, CRC(c5edb5c4) SHA1(253378c8739daa5da4edb15eff7050820b2b3755) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

/****************************************
 ID-0265
 . NGM-2650
 NEO-MVS PROGBK2 (2000.3.21) (NEO-PCM2 SNK) / NEO-MVS CHAFIO (1999.6.14) (NEO-CMC 7050)
 . NGH-2650
 NEO-AEG PROGBK2 (2002.4.1) (NEO-PCM2 PLAYMORE) / NEO-AEG CHAFIO (1999.8.10) (NEO-CMC 7050)
****************************************/

ROM_START( kof2002 ) /* Encrypted Set */ /* MVS AND AES VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "265-p1.p1",  0x000000, 0x100000, CRC(9ede7323) SHA1(ad9d45498777fda9fa58e75781f48e09aee705a6) ) /* mask rom TC538200 */
	ROM_LOAD16_WORD_SWAP( "265-p2.sp2", 0x100000, 0x400000, CRC(327266b8) SHA1(98f445cc0a94f8744d74bca71cb420277622b034) ) /* mask rom TC5332205 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_128K( "265-m1.m1", CRC(85aaa632) SHA1(744fba4ca3bc3a5873838af886efb97a8a316104) ) /* mask rom TC531001 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "265-v1.v1", 0x000000, 0x800000, CRC(15e8f3f5) SHA1(7c9e6426b9fa6db0158baa17a6485ffce057d889) ) /* mask rom TC5364205 */
	ROM_LOAD( "265-v2.v2", 0x800000, 0x800000, CRC(da41d6f9) SHA1(a43021f1e58947dcbe3c8ca5283b20b649f0409d) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "265-c1.c1", 0x0000000, 0x800000, CRC(2b65a656) SHA1(9c46d8cf5b1ef322db442ac6a9b9406ab49206c5) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c2.c2", 0x0000001, 0x800000, CRC(adf18983) SHA1(150cd4a5e51e9df88688469d2ea7675c2cf3658a) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c3.c3", 0x1000000, 0x800000, CRC(875e9fd7) SHA1(28f52d56192d48bbc5dc3c97abf456bd34a58cbd) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c4.c4", 0x1000001, 0x800000, CRC(2da13947) SHA1(f8d79ec2c236aa3d3648a4f715676899602122c1) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c5.c5", 0x2000000, 0x800000, CRC(61bd165d) SHA1(b3424db84bc683d858fb635bc42728f9cdd89caf) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c6.c6", 0x2000001, 0x800000, CRC(03fdd1eb) SHA1(6155c7e802062f4eafa27e414c4e73ee59b868bf) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c7.c7", 0x3000000, 0x800000, CRC(1a2749d8) SHA1(af7d9ec1d576209826fa568f676bbff92f6d6ddd) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c8.c8", 0x3000001, 0x800000, CRC(ab0bb549) SHA1(d23afb60b7f831f7d4a98ad3c4a00ee19877a1ce) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

/****************************************
 ID-0266
 . NGM-2660
 NEO-MVS PROGBK2 (2000.3.21) (NEO-PCM2 SNK) / NEO-MVS CHAFIO (1999.6.14) (NEO-CMC 7050)
 . NGH-2660
 NEO-AEG PROGBK2 (2002.4.1) (NEO-PCM2 PLAYMORE) / NEO-AEG CHAFIO (1999.8.10) (NEO-CMC 7050)
****************************************/

ROM_START( matrim ) /* Encrypted Set */ /* MVS AND AES VERSION */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "266-p1.p1",  0x000000, 0x100000, CRC(5d4c2dc7) SHA1(8d723b0d28ec344eef26009b361a2b97d300dd51) ) /* mask rom TC538200 */
	ROM_LOAD16_WORD_SWAP( "266-p2.sp2", 0x100000, 0x400000, CRC(a14b1906) SHA1(1daa14d73512f760ef569b06f9facb279437d1db) ) /* mask rom TC5332205 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x80000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x80000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_128K( "266-m1.m1", CRC(456c3e6c) SHA1(5a07d0186198a18d2dda1331093cf29b0b9b2984) ) /* mask rom TC531001 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "266-v1.v1", 0x000000, 0x800000, CRC(a4f83690) SHA1(200d683d8c30ebc6d0ed331aa5bbba39b4e07160) ) /* mask rom TC5364205 */
	ROM_LOAD( "266-v2.v2", 0x800000, 0x800000, CRC(d0f69eda) SHA1(9d7e98976ad433ed8a35d7afffa38130444ba7db) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "266-c1.c1", 0x0000000, 0x800000, CRC(505f4e30) SHA1(f22b6f76fc0cad963555dc89d072967c8dc8b79a) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "266-c2.c2", 0x0000001, 0x800000, CRC(3cb57482) SHA1(dab15bc24391f9a5173de76af48b612fb9636ccf) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "266-c3.c3", 0x1000000, 0x800000, CRC(f1cc6ad0) SHA1(66c1cccc0332ffd2d3064f06330c41f95ca09ced) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "266-c4.c4", 0x1000001, 0x800000, CRC(45b806b7) SHA1(c2bb866fded53d62fad0fc88d89d5e7d4cb1894f) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "266-c5.c5", 0x2000000, 0x800000, CRC(9a15dd6b) SHA1(194a6973a7a9e3847efe1bdbaeaeb16e74aff2dd) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "266-c6.c6", 0x2000001, 0x800000, CRC(281cb939) SHA1(bdb7766cfde581ccfaee2be7fe48445f360a2301) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "266-c7.c7", 0x3000000, 0x800000, CRC(4b71f780) SHA1(d5611a6f6b730db58613b48f2b0174661ccfb7bb) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "266-c8.c8", 0x3000001, 0x800000, CRC(29873d33) SHA1(dc77f129ed49b8d40d0d4241feef3f6c2f19a987) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

/****************************************
 ID-0267
 . ??M-2670
 NEO-MVS PROGBK2 (2000.3.21) (NEO-PCM2 SNK) / NEO-MVS CHAFIO (1999.6.14) (NEO-CMC 7050)
****************************************/

ROM_START( pnyaa ) /* Encrypted Set */ /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "pn202.p1", 0x000000, 0x100000, CRC(bf34e71c) SHA1(cfa7a2c7c41601a758414faf34e59583d7537363) )
	/* P1 on eprom with sticker; chip label is P N 2. 02 */
	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_512K( "m1.m1", CRC(c7853ccd) SHA1(1b7a4c5093cf0fe3861ce44fd1d3b30c71ad0abe) ) /* mask rom TC534000 */
	/* M1 on eprom with sticker; chip label is M1 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "267-v1.v1", 0x000000, 0x400000, CRC(e2e8e917) SHA1(7f412d55aebff3d38a225a88c632916295ab0584) ) /* mask rom TC5332204 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "267-c1.c1", 0x0000000, 0x800000, CRC(5eebee65) SHA1(7eb3eefdeb24e19831d0f51d4ea07a0292c25ab6) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "267-c2.c2", 0x0000001, 0x800000, CRC(2b67187b) SHA1(149c3efd3c444fd0d35a97fa2268102bf76be3ed) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

ROM_START( pnyaaa ) /* Encrypted Set */ /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "267-p1.p1", 0x000000, 0x100000, CRC(112fe2c0) SHA1(01420e051f0bdbd4f68ce306a3738161b96f8ba8) ) /* mask rom TC538200 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_512K( "267-m1.m1", CRC(c7853ccd) SHA1(1b7a4c5093cf0fe3861ce44fd1d3b30c71ad0abe) ) /* mask rom TC534000 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "267-v1.v1", 0x000000, 0x400000, CRC(e2e8e917) SHA1(7f412d55aebff3d38a225a88c632916295ab0584) ) /* mask rom TC5332204 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "267-c1.c1", 0x0000000, 0x800000, CRC(5eebee65) SHA1(7eb3eefdeb24e19831d0f51d4ea07a0292c25ab6) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "267-c2.c2", 0x0000001, 0x800000, CRC(2b67187b) SHA1(149c3efd3c444fd0d35a97fa2268102bf76be3ed) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

/****************************************
 ID-0268
 . NGM-2680
 NEO-MVS PROGBK3S (2003.10.1) (NEO-PCM2 PLAYMORE) (NEO-PVC) / NEO-MVS CHAFIO (2003.7.24) (NEO-CMC 7050)
 . NGH-2680
 NEO-AEG PROGBK3S (2003.10.6) (NEO-PCM2 PLAYMORE) (NEO-PVC) / NEO-AEG CHAFIO (2003.7.24) (NEO-CMC 7050)
****************************************/

ROM_START( mslug5 ) /* Encrypted Set */ /* MVS VERSION */
	ROM_REGION( 0x800000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD32_WORD_SWAP( "268-p1cr.p1", 0x000000, 0x400000, CRC(d0466792) SHA1(880819933d997fab398f91061e9dbccb959ae8a1) ) /* mask rom TC5332205 */
	ROM_LOAD32_WORD_SWAP( "268-p2cr.p2", 0x000002, 0x400000, CRC(fbf6b61e) SHA1(9ec743d5988b5e3183f37f8edf45c72a8c0c893e) ) /* mask rom TC5332205 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_512K( "268-m1.m1", CRC(4a5a6e0e) SHA1(df0f660f2465e1db7be5adfcaf5e88ad61a74a42) ) /* mask rom TC534000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "268-v1c.v1", 0x000000, 0x800000, CRC(ae31d60c) SHA1(c42285cf4e52fea74247860813e826df5aa7600a) ) /* mask rom TC5364205 */
	ROM_LOAD( "268-v2c.v2", 0x800000, 0x800000, CRC(c40613ed) SHA1(af889570304e2867d7dfea1e94e388c06249fb67) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "268-c1c.c1", 0x0000000, 0x800000, CRC(ab7c389a) SHA1(025a188de589500bf7637fa8e7a37ab24bf4312e) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c2c.c2", 0x0000001, 0x800000, CRC(3560881b) SHA1(493d218c92290b4770024d6ee2917c4022753b07) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c3c.c3", 0x1000000, 0x800000, CRC(3af955ea) SHA1(cf36b6ae9b0d12744b17cb7a928399214de894be) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c4c.c4", 0x1000001, 0x800000, CRC(c329c373) SHA1(5073d4079958a0ef5426885af2c9e3178f37d5e0) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c5c.c5", 0x2000000, 0x800000, CRC(959c8177) SHA1(889bda7c65d71172e7d89194d1269561888fe789) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c6c.c6", 0x2000001, 0x800000, CRC(010a831b) SHA1(aec140661e3ae35d264df416478ba15188544d91) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c7c.c7", 0x3000000, 0x800000, CRC(6d72a969) SHA1(968dd9a4d1209b770b9b85ea6532fa24d262a262) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c8c.c8", 0x3000001, 0x800000, CRC(551d720e) SHA1(ebf69e334fcaba0fda6fd432fd0970283a365d12) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

ROM_START( mslug5h ) /* Encrypted Set */ /* AES release of the game but is also found in later MVS carts */
	ROM_REGION( 0x800000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD32_WORD_SWAP( "268-p1c.p1", 0x000000, 0x400000, CRC(3636690a) SHA1(e0da714b4bdc6efffe1250ded02ebddb3ab6d7b3) )
	ROM_LOAD32_WORD_SWAP( "268-p2c.p2", 0x000002, 0x400000, CRC(8dfc47a2) SHA1(27d618cfbd0107a4d2a836797e967b39d2eb4851) )
	/* also found AES set with P1 / P2 on maskrom and m1 on M27C4001 eprom with sticker; chip labels are 268-P1CR2, 268-P2CR2 and 268-M1 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_512K( "268-m1.m1", CRC(4a5a6e0e) SHA1(df0f660f2465e1db7be5adfcaf5e88ad61a74a42) ) /* mask rom TC534000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "268-v1c.v1", 0x000000, 0x800000, CRC(ae31d60c) SHA1(c42285cf4e52fea74247860813e826df5aa7600a) ) /* mask rom TC5364205 */
	ROM_LOAD( "268-v2c.v2", 0x800000, 0x800000, CRC(c40613ed) SHA1(af889570304e2867d7dfea1e94e388c06249fb67) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "268-c1c.c1", 0x0000000, 0x800000, CRC(ab7c389a) SHA1(025a188de589500bf7637fa8e7a37ab24bf4312e) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c2c.c2", 0x0000001, 0x800000, CRC(3560881b) SHA1(493d218c92290b4770024d6ee2917c4022753b07) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c3c.c3", 0x1000000, 0x800000, CRC(3af955ea) SHA1(cf36b6ae9b0d12744b17cb7a928399214de894be) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c4c.c4", 0x1000001, 0x800000, CRC(c329c373) SHA1(5073d4079958a0ef5426885af2c9e3178f37d5e0) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c5c.c5", 0x2000000, 0x800000, CRC(959c8177) SHA1(889bda7c65d71172e7d89194d1269561888fe789) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c6c.c6", 0x2000001, 0x800000, CRC(010a831b) SHA1(aec140661e3ae35d264df416478ba15188544d91) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c7c.c7", 0x3000000, 0x800000, CRC(6d72a969) SHA1(968dd9a4d1209b770b9b85ea6532fa24d262a262) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c8c.c8", 0x3000001, 0x800000, CRC(551d720e) SHA1(ebf69e334fcaba0fda6fd432fd0970283a365d12) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

/****************************************
 ID-0269
 . NGM-2690
 NEO-MVS PROGBK3R (2003.9.2) (NEO-PCM2 SNKPLAYMORE) (NEO-PVC) / NEO-MVS CHAFIO (2003.7.24) (NEO-CMC 7050)
 . NGH-2690
 NEO-AEG PROGBK3R (2003.8.29) (NEO-PCM2 SNKPLAYMORE) (NEO-PVC) / NEO-AEG CHAFIO (2003.7.24) (NEO-CMC 7050)
****************************************/

ROM_START( svc ) /* Encrypted Set */ /* MVS AND AES VERSION */
	ROM_REGION( 0x800000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD32_WORD_SWAP( "269-p1.p1", 0x000000, 0x400000, CRC(38e2005e) SHA1(1b902905916a30969282f1399a756e32ff069097) ) /* mask rom TC5332205 */
	ROM_LOAD32_WORD_SWAP( "269-p2.p2", 0x000002, 0x400000, CRC(6d13797c) SHA1(3cb71a95cea6b006b44cac0f547df88aec0007b7) ) /* mask rom TC5332205 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x80000, "cslot1:fixed", 0 ) /* larger char set */
	ROM_FILL( 0x000000, 0x80000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_512K( "269-m1.m1", CRC(f6819d00) SHA1(d3bbe09df502464f104e53501708ac6e2c1832c6) ) /* mask rom TC534000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "269-v1.v1", 0x000000, 0x800000, CRC(c659b34c) SHA1(1931e8111ef43946f68699f8707334c96f753a1e) ) /* mask rom TC5364205 */
	ROM_LOAD( "269-v2.v2", 0x800000, 0x800000, CRC(dd903835) SHA1(e58d38950a7a8697bb22a1cc7a371ae6664ae8f9) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "269-c1r.c1", 0x0000000, 0x800000, CRC(887b4068) SHA1(227cdcf7a10a415f1e3afe7ae97acc9afc2cc8e1) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "269-c2r.c2", 0x0000001, 0x800000, CRC(4e8903e4) SHA1(31daaa4fd6c23e8f0a8428931c513d97d2eee1bd) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "269-c3r.c3", 0x1000000, 0x800000, CRC(7d9c55b0) SHA1(1f94a948b3e3c31b3ff05518ef525031a3cb2c62) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "269-c4r.c4", 0x1000001, 0x800000, CRC(8acb5bb6) SHA1(2c27d6e309646d7b84da85f78c06e4aaa74e844b) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "269-c5r.c5", 0x2000000, 0x800000, CRC(097a4157) SHA1(54d839f55d27f68c704a94ea3c63c644ffc22ca4) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "269-c6r.c6", 0x2000001, 0x800000, CRC(e19df344) SHA1(20448add53ab25dd3a8f0b681131ad3b9c68acc9) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "269-c7r.c7", 0x3000000, 0x800000, CRC(d8f0340b) SHA1(43114af7557361a8903bb8cf8553f602946a9220) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "269-c8r.c8", 0x3000001, 0x800000, CRC(2570b71b) SHA1(99266e1c2ffcf324793fb5c55325fbc7e6265ac0) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

/****************************************
 ID-0270
 . NGM-2700
 NEO-MVS PROGBK2R (2003.8.26) (NEO-PCM2 SNKPLAYMORE) / NEO-MVS CHAFIO (2003.7.24) (NEO-CMC 7050)
 . NGH-2700
 NEO-AEG PROGBK2S (2003.10.16) (NEO-PCM2 SNKPLAYMORE) / NEO-AEG CHAFIO (2003.7.24) (NEO-CMC 7050)
****************************************/

ROM_START( samsho5 ) /* Encrypted Set */ /* MVS VERSION, Build Date: Tue Aug 26 22:27:30 2003 */
	ROM_REGION( 0x800000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "270-p1.p1",  0x000000, 0x400000, CRC(4a2a09e6) SHA1(2644de02cdab8ccc605488a7c76b8c9cd1d5bcb9) ) /* mask rom TC5332205 */
	ROM_LOAD16_WORD_SWAP( "270-p2.sp2", 0x400000, 0x400000, CRC(e0c74c85) SHA1(df24a4ee76438e40c2f04a714175a7f85cacdfe0) ) /* mask rom TC5332205 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_512K( "270-m1.m1", CRC(49c9901a) SHA1(2623e9765a0eba58fee2de72851e9dc502344a3d) ) /* mask rom 27c040 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "270-v1.v1", 0x000000, 0x800000, CRC(62e434eb) SHA1(1985f5e88f8e866f9683b6cea901aa28c04b80bf) ) /* mask rom TC5364205 */
	ROM_LOAD( "270-v2.v2", 0x800000, 0x800000, CRC(180f3c9a) SHA1(6d7dc2605ead6e78704efa127e7e0dfe621e2c54) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "270-c1.c1", 0x0000000, 0x800000, CRC(14ffffac) SHA1(2ccebfdd0c7907679ae95bf6eca85b8d322441e2) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c2.c2", 0x0000001, 0x800000, CRC(401f7299) SHA1(94e48cdf1682b1250f53c59f3f71d995e928d17b) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c3.c3", 0x1000000, 0x800000, CRC(838f0260) SHA1(d5c8d3c6e7221d04e0b20882a847752e5ba95635) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c4.c4", 0x1000001, 0x800000, CRC(041560a5) SHA1(d165e533699f15b1e079c82f97db3542b3a7dd66) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c5.c5", 0x2000000, 0x800000, CRC(bd30b52d) SHA1(9f8282e684415b4045218cf764ef7d75a70e3240) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c6.c6", 0x2000001, 0x800000, CRC(86a69c70) SHA1(526732cdb408cf680af9da39057bce6a4dfb5e13) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c7.c7", 0x3000000, 0x800000, CRC(d28fbc3c) SHA1(a82a6ba6760fad14d9309f9147cb7d80bd6f70fc) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c8.c8", 0x3000001, 0x800000, CRC(02c530a6) SHA1(7a3fafa6075506c6ef78cc4ec2cb72118ec83cb9) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

/* handwritten labels, but appears to be a legitimate alt revision based on the build date at 0xA6C00
   p1.bin                  270-p1c.p1              90.607002%
   p2.bin                  270-p2c.sp2             99.999763% (all bytes that differ do so by only bits 0x20 or 0x02)
*/
ROM_START( samsho5a ) /* Encrypted Set, Alternate Set */ /* MVS VERSION, Build Date: Mon Oct 20 10:11:27 2003 */
	ROM_REGION( 0x800000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "p1.bin", 0x000000, 0x400000, CRC(7795fffe) SHA1(2e74a4dbed553a01f1cb2f3db896375de5f1d212) ) /* EPROM */
	ROM_LOAD16_WORD_SWAP( "p2.bin", 0x400000, 0x400000, CRC(2b844fe9) SHA1(899fe6457db4a724bdd9c7e4a912eab50a5221d3) ) /* EPROM */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_512K( "270-m1.m1", CRC(49c9901a) SHA1(2623e9765a0eba58fee2de72851e9dc502344a3d) ) /* mask rom 27c040 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "270-v1.v1", 0x000000, 0x800000, CRC(62e434eb) SHA1(1985f5e88f8e866f9683b6cea901aa28c04b80bf) ) /* mask rom TC5364205 */
	ROM_LOAD( "270-v2.v2", 0x800000, 0x800000, CRC(180f3c9a) SHA1(6d7dc2605ead6e78704efa127e7e0dfe621e2c54) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "270-c1.c1", 0x0000000, 0x800000, CRC(14ffffac) SHA1(2ccebfdd0c7907679ae95bf6eca85b8d322441e2) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c2.c2", 0x0000001, 0x800000, CRC(401f7299) SHA1(94e48cdf1682b1250f53c59f3f71d995e928d17b) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c3.c3", 0x1000000, 0x800000, CRC(838f0260) SHA1(d5c8d3c6e7221d04e0b20882a847752e5ba95635) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c4.c4", 0x1000001, 0x800000, CRC(041560a5) SHA1(d165e533699f15b1e079c82f97db3542b3a7dd66) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c5.c5", 0x2000000, 0x800000, CRC(bd30b52d) SHA1(9f8282e684415b4045218cf764ef7d75a70e3240) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c6.c6", 0x2000001, 0x800000, CRC(86a69c70) SHA1(526732cdb408cf680af9da39057bce6a4dfb5e13) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c7.c7", 0x3000000, 0x800000, CRC(d28fbc3c) SHA1(a82a6ba6760fad14d9309f9147cb7d80bd6f70fc) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c8.c8", 0x3000001, 0x800000, CRC(02c530a6) SHA1(7a3fafa6075506c6ef78cc4ec2cb72118ec83cb9) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

ROM_START( samsho5h ) /* Encrypted Set, Alternate Set */ /* AES VERSION, Build Date: Tue Oct 28 02:24:45 2003 */
	ROM_REGION( 0x800000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "270-p1c.p1",  0x000000, 0x400000, CRC(bf956089) SHA1(c538289069bf338b9fa7ecc5c9143763dbb776a8) ) /* mask rom TC5332205 */
	ROM_LOAD16_WORD_SWAP( "270-p2c.sp2", 0x400000, 0x400000, CRC(943a6b1d) SHA1(12bd02fc197456da6ee86f066086094cef0f4bf9) ) /* mask rom TC5332205 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_512K( "270-m1.m1", CRC(49c9901a) SHA1(2623e9765a0eba58fee2de72851e9dc502344a3d) ) /* mask rom 27c040 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "270-v1.v1", 0x000000, 0x800000, CRC(62e434eb) SHA1(1985f5e88f8e866f9683b6cea901aa28c04b80bf) ) /* mask rom TC5364205 */
	ROM_LOAD( "270-v2.v2", 0x800000, 0x800000, CRC(180f3c9a) SHA1(6d7dc2605ead6e78704efa127e7e0dfe621e2c54) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "270-c1.c1", 0x0000000, 0x800000, CRC(14ffffac) SHA1(2ccebfdd0c7907679ae95bf6eca85b8d322441e2) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c2.c2", 0x0000001, 0x800000, CRC(401f7299) SHA1(94e48cdf1682b1250f53c59f3f71d995e928d17b) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c3.c3", 0x1000000, 0x800000, CRC(838f0260) SHA1(d5c8d3c6e7221d04e0b20882a847752e5ba95635) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c4.c4", 0x1000001, 0x800000, CRC(041560a5) SHA1(d165e533699f15b1e079c82f97db3542b3a7dd66) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c5.c5", 0x2000000, 0x800000, CRC(bd30b52d) SHA1(9f8282e684415b4045218cf764ef7d75a70e3240) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c6.c6", 0x2000001, 0x800000, CRC(86a69c70) SHA1(526732cdb408cf680af9da39057bce6a4dfb5e13) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c7.c7", 0x3000000, 0x800000, CRC(d28fbc3c) SHA1(a82a6ba6760fad14d9309f9147cb7d80bd6f70fc) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "270-c8.c8", 0x3000001, 0x800000, CRC(02c530a6) SHA1(7a3fafa6075506c6ef78cc4ec2cb72118ec83cb9) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

/****************************************
 ID-0271
 . NGM-2710
 NEO-MVS PROGBK3S (2003.10.1) (NEO-PCM2 SNKPLAYMORE) (NEO-PVC) / NEO-MVS CHAFIO (2003.7.24) (NEO-CMC 7050)
 . NGH-2710
 NEO-AEG PROGBK3S (2003.10.6) (NEO-PCM2 SNKPLAYMORE) (NEO-PVC) / NEO-AEG CHAFIO (2003.7.24) (NEO-CMC 7050)
****************************************/

ROM_START( kof2003 ) /* Encrypted Code + Sound + GFX Roms */ /* MVS VERSION */
	ROM_REGION( 0x900000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD32_WORD_SWAP( "271-p1c.p1", 0x000000, 0x400000, CRC(530ecc14) SHA1(812cf7e9902af3f5e9e330b7c05c2171b139ad2b) ) /* mask rom TC5332205 */
	ROM_LOAD32_WORD_SWAP( "271-p2c.p2", 0x000002, 0x400000, CRC(fd568da9) SHA1(46364906a1e81dc251117e91a1a7b43af1373ada) ) /* mask rom TC5332205 */
	ROM_LOAD16_WORD_SWAP( "271-p3c.p3", 0x800000, 0x100000, CRC(aec5b4a9) SHA1(74087f785590eda5898ce146029818f86ced42b6) ) /* mask rom TC538200 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x80000, "cslot1:fixed", 0 ) /* larger char set */
	ROM_FILL( 0x000000, 0x80000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_512K( "271-m1c.m1", CRC(f5515629) SHA1(7516bf1b0207a3c8d41dc30c478f8d8b1f71304b) ) /* mask rom TC534000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "271-v1c.v1", 0x000000, 0x800000, CRC(ffa3f8c7) SHA1(7cf4a933973ca23b7f87c81151d8659e6ec4bd20) ) /* mask rom TC5364205 */
	ROM_LOAD( "271-v2c.v2", 0x800000, 0x800000, CRC(5382c7d1) SHA1(1bf999705eda80ba1e7b0d6bdd010d9bfb18bd76) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "271-c1c.c1", 0x0000000, 0x800000, CRC(b1dc25d0) SHA1(50adc3c60d5b4b3abd10a49db2267306c6dbd772) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c2c.c2", 0x0000001, 0x800000, CRC(d5362437) SHA1(66db36522dc09106388c707252df9fe1c88b4856) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c3c.c3", 0x1000000, 0x800000, CRC(0a1fbeab) SHA1(9fe30d36ba98d00fda010832ff2f27783dd577c1) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c4c.c4", 0x1000001, 0x800000, CRC(87b19a0c) SHA1(b72a8e7d9124ce859b5149bb4381ba481c161ea5) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c5c.c5", 0x2000000, 0x800000, CRC(704ea371) SHA1(e75b80422f0d72eac826f8ffadf79efeccaab124) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c6c.c6", 0x2000001, 0x800000, CRC(20a1164c) SHA1(c9843b37612a16fc95f6851793b1cfb5d49d811d) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c7c.c7", 0x3000000, 0x800000, CRC(189aba7f) SHA1(7152195a57ad36b28290810fe87ed8c206262ba9) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c8c.c8", 0x3000001, 0x800000, CRC(20ec4fdc) SHA1(deb5f7ec5a090e419b9d1a6a74877bee081198e2) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

ROM_START( kof2003h ) /* Encrypted Code + Sound + GFX Roms */ /* AES VERSION */
	/* All chip labels for this set are correct */
	ROM_REGION( 0x900000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD32_WORD_SWAP( "271-p1k.p1", 0x000000, 0x400000, CRC(d0d0ae3e) SHA1(538d054ac50c91694fbbfefcce548b063713e14e) ) /* mask rom TC5332205 */
	ROM_LOAD32_WORD_SWAP( "271-p2k.p2", 0x000002, 0x400000, CRC(fb3f79d9) SHA1(f253d10e732d6e23ae82d74ac9269d21f69ddb4d) ) /* mask rom TC5332205 */
	ROM_LOAD16_WORD_SWAP( "271-p3k.p3", 0x800000, 0x100000, CRC(232702ad) SHA1(6045046027dac1cbd4cbd14b5c1ece522bc6197f) ) /* mask rom TC538200 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x80000, "cslot1:fixed", 0 ) /* larger char set */
	ROM_FILL( 0x000000, 0x80000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_512K( "271-m1k.m1", CRC(48d9affe) SHA1(68f01560b91bbada39001ce01bdeeed5c9bb29f2) ) /* mask rom TC534000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "271-v1c.v1", 0x000000, 0x800000, CRC(ffa3f8c7) SHA1(7cf4a933973ca23b7f87c81151d8659e6ec4bd20) ) /* mask rom TC5364205 */
	ROM_LOAD( "271-v2c.v2", 0x800000, 0x800000, CRC(5382c7d1) SHA1(1bf999705eda80ba1e7b0d6bdd010d9bfb18bd76) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "271-c1k.c1", 0x0000000, 0x800000, CRC(efb9dd24) SHA1(1c6fe10fdbfc3306c3b7321c731f28ffdbfb15b8) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c2k.c2", 0x0000001, 0x800000, CRC(3fb90447) SHA1(04d196de7c54c77bc75eba56d3060d46efc2d406) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c3k.c3", 0x1000000, 0x800000, CRC(27950f28) SHA1(924f4de61c86b9efde6f1104b986886f1117055d) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c4k.c4", 0x1000001, 0x800000, CRC(735177f8) SHA1(c95da1bc256995a7f44c9cc3312879ab6cbc15d6) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c5k.c5", 0x2000000, 0x800000, CRC(a51b7c0f) SHA1(53dcf692b35b8d32abe5962ac799b8d641f04710) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c6k.c6", 0x2000001, 0x800000, CRC(d5cae4e0) SHA1(248cd9eaac7a04d6b5d80c7534de90b057d566d7) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c7k.c7", 0x3000000, 0x800000, CRC(e65ae2d0) SHA1(39744e10697d7ac539ecfcfa597e75597f321955) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c8k.c8", 0x3000001, 0x800000, CRC(312f528c) SHA1(b4ad75f54f730ada6cb00112b74022250f055725) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

/****************************************
 ID-0272
 . NGM-2720
 NEO-MVS PROGBK2S (2003.10.18) (NEO-PCM2 SNKPLAYMORE) / NEO-MVS CHAFIO (2003.7.24) (NEO-CMC 7050)
 . NGH-2720
 NEO-AEG PROGBK2S (2003.10.16) (NEO-PCM2 SNKPLAYMORE) / NEO-MVS CHAFIO (2003.7.24) (NEO-CMC 7050)
****************************************/

ROM_START( samsh5sp ) /* Encrypted Set */ /* MVS VERSION */
	/* Uncensored */
	ROM_REGION( 0x800000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "272-p1.p1",  0x000000, 0x400000, CRC(fb7a6bba) SHA1(f68c527208d8a55ca44b0caaa8ab66b3a0ffdfe5) )
	ROM_LOAD16_WORD_SWAP( "272-p2.sp2", 0x400000, 0x400000, CRC(63492ea6) SHA1(6ba946acb62c63ed61a42fe72b7fff3828883bcc) )

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_512K( "272-m1.m1", CRC(adeebf40) SHA1(8cbd63dda3fff4de38060405bf70cd9308c9e66e) )

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "272-v1.v1", 0x000000, 0x800000, CRC(76a94127) SHA1(c3affd7ff1eb02345cfb755962ec173a8ec34acd) )
	ROM_LOAD( "272-v2.v2", 0x800000, 0x800000, CRC(4ba507f1) SHA1(728d139da3fe8a391fd8be4d24bb7fdd4bf9548a) )

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "272-c1.c1", 0x0000000, 0x800000, CRC(4f97661a) SHA1(87f1721bae5ef16bc23c06b05e64686c396413df) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "272-c2.c2", 0x0000001, 0x800000, CRC(a3afda4f) SHA1(86b475fce0bc0aa04d34e31324e8c7c7c847df19) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "272-c3.c3", 0x1000000, 0x800000, CRC(8c3c7502) SHA1(6639020a8860d2400308e110d7277cbaf6eccc2a) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "272-c4.c4", 0x1000001, 0x800000, CRC(32d5e2e2) SHA1(2b5612017152afd7433aaf99951a084ef5ad6bf0) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "272-c5.c5", 0x2000000, 0x800000, CRC(6ce085bc) SHA1(0432b04a2265c649bba1bbd934dfb425c5d80fb1) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "272-c6.c6", 0x2000001, 0x800000, CRC(05c8dc8e) SHA1(da45c222893f25495a66bdb302f9b0b1de3c8ae0) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "272-c7.c7", 0x3000000, 0x800000, CRC(1417b742) SHA1(dfe35eb4bcd022d2f2dc544ccbbb77078f08c0aa) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "272-c8.c8", 0x3000001, 0x800000, CRC(d49773cd) SHA1(cd8cf3b762d381c1f8f12919579c84a7ef7efb3f) ) /* Plane 2,3 */
ROM_END


/*  Some info about the 2nd AES release of Samurai Shodown 5 Special (samsh5sph):

    The fixed carts have a small round neogeo sticker applied to the front side of the cart (top right near cart sticker).
    SNK Playmore had authorized a recall of all Samurai Shodown V Special (Samurai Spirits 0 Special) home cartridges. This recall involved bug fixes
    and the addition of fatalities. (The fatalities were originally removed at the last minute due to the Nagasaki incident, a murder caused by a child killing her classmate by knife.)
    Bug fixes: Improvements on Voice, Back Ground Music, and Practice mode.
    Fatalities: SNK PLAYMORE modified the game program by including the removed "Zetumei Ougi" in a modified version.
    This new version does not show the complete fatalities, they are instead replaced by what SNK PLAYMORE refers to as "lessened fatalities".
*/

ROM_START( samsh5sph ) /* Encrypted Set */ /* AES VERSION, 2nd bugfix release */
	/* Less censored */
	ROM_REGION( 0x800000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "272-p1ca.p1",  0x000000, 0x400000, CRC(c30a08dd) SHA1(66864954017c841d7ca8490112c3aa7a71a4da70) )
	ROM_LOAD16_WORD_SWAP( "272-p2ca.sp2", 0x400000, 0x400000, CRC(bd64a518) SHA1(aa259a168930f106377d680db444535411b3bce0) )
	/* Correct chip labels unknown. Also found AES set on NEO-AEG PROGBK2S with P1 / P2 on eprom with sticker; chip label is 272-P1 and 272-P2 */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_512K( "272-m1.m1", CRC(adeebf40) SHA1(8cbd63dda3fff4de38060405bf70cd9308c9e66e) )

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "272-v1.v1", 0x000000, 0x800000, CRC(76a94127) SHA1(c3affd7ff1eb02345cfb755962ec173a8ec34acd) )
	ROM_LOAD( "272-v2.v2", 0x800000, 0x800000, CRC(4ba507f1) SHA1(728d139da3fe8a391fd8be4d24bb7fdd4bf9548a) )

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "272-c1.c1", 0x0000000, 0x800000, CRC(4f97661a) SHA1(87f1721bae5ef16bc23c06b05e64686c396413df) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "272-c2.c2", 0x0000001, 0x800000, CRC(a3afda4f) SHA1(86b475fce0bc0aa04d34e31324e8c7c7c847df19) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "272-c3.c3", 0x1000000, 0x800000, CRC(8c3c7502) SHA1(6639020a8860d2400308e110d7277cbaf6eccc2a) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "272-c4.c4", 0x1000001, 0x800000, CRC(32d5e2e2) SHA1(2b5612017152afd7433aaf99951a084ef5ad6bf0) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "272-c5.c5", 0x2000000, 0x800000, CRC(6ce085bc) SHA1(0432b04a2265c649bba1bbd934dfb425c5d80fb1) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "272-c6.c6", 0x2000001, 0x800000, CRC(05c8dc8e) SHA1(da45c222893f25495a66bdb302f9b0b1de3c8ae0) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "272-c7.c7", 0x3000000, 0x800000, CRC(1417b742) SHA1(dfe35eb4bcd022d2f2dc544ccbbb77078f08c0aa) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "272-c8.c8", 0x3000001, 0x800000, CRC(d49773cd) SHA1(cd8cf3b762d381c1f8f12919579c84a7ef7efb3f) ) /* Plane 2,3 */
ROM_END

ROM_START( samsh5spho ) /* Encrypted Set */ /* AES VERSION, 1st release */
	/* Censored */
	ROM_REGION( 0x800000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "272-p1c.p1",  0x000000, 0x400000, CRC(9291794d) SHA1(66588ff9b00ffad6508b03423548984e28a3209d) )
	ROM_LOAD16_WORD_SWAP( "272-p2c.sp2", 0x400000, 0x400000, CRC(fa1a7dd8) SHA1(62443dad76d6c1e18f515d7d4ef8e1295a4b7f1d) )

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_512K( "272-m1.m1", CRC(adeebf40) SHA1(8cbd63dda3fff4de38060405bf70cd9308c9e66e) )

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "272-v1.v1", 0x000000, 0x800000, CRC(76a94127) SHA1(c3affd7ff1eb02345cfb755962ec173a8ec34acd) )
	ROM_LOAD( "272-v2.v2", 0x800000, 0x800000, CRC(4ba507f1) SHA1(728d139da3fe8a391fd8be4d24bb7fdd4bf9548a) )

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "272-c1.c1", 0x0000000, 0x800000, CRC(4f97661a) SHA1(87f1721bae5ef16bc23c06b05e64686c396413df) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "272-c2.c2", 0x0000001, 0x800000, CRC(a3afda4f) SHA1(86b475fce0bc0aa04d34e31324e8c7c7c847df19) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "272-c3.c3", 0x1000000, 0x800000, CRC(8c3c7502) SHA1(6639020a8860d2400308e110d7277cbaf6eccc2a) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "272-c4.c4", 0x1000001, 0x800000, CRC(32d5e2e2) SHA1(2b5612017152afd7433aaf99951a084ef5ad6bf0) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "272-c5.c5", 0x2000000, 0x800000, CRC(6ce085bc) SHA1(0432b04a2265c649bba1bbd934dfb425c5d80fb1) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "272-c6.c6", 0x2000001, 0x800000, CRC(05c8dc8e) SHA1(da45c222893f25495a66bdb302f9b0b1de3c8ae0) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "272-c7.c7", 0x3000000, 0x800000, CRC(1417b742) SHA1(dfe35eb4bcd022d2f2dc544ccbbb77078f08c0aa) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "272-c8.c8", 0x3000001, 0x800000, CRC(d49773cd) SHA1(cd8cf3b762d381c1f8f12919579c84a7ef7efb3f) ) /* Plane 2,3 */
ROM_END


/*************************************
 *
 *  BrezzaSoft games, licensed?
 *
 *************************************/

/****************************************
 B-J-02
 . ???-????
 MVS PROGV (2000.11.17) / NEO-MVS CHAFIO (1999.6.14) (NEO-CMC 7050)
****************************************/

ROM_START( jockeygp ) /* MVS ONLY RELEASE */
	/* Officially licensed? Cart has a holographic 'SNK' sticker applied */
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "008-epr.p1", 0x000000, 0x100000, CRC(2fb7f388) SHA1(e3c9b03944b4c10cf5081caaf9c8be1f08c06493) ) /* M27C160 */
	/* P on eprom, correct chip label unknown */
	ROM_FILL( 0x100000, 0x100000, 0xff )

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_512K( "008-mg1.m1", CRC(d163c690) SHA1(1dfd04d20c5985037f07cd01000d0b04f3a8f4f4) ) /* M27C4001 */

	ROM_REGION( 0x0200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "008-v1.v1", 0x000000, 0x200000, CRC(443eadba) SHA1(3def3c22f0e276bc4c2fc7ff70ce473c08b0d2df) ) /* mask rom TC5316200 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "008-c1.c1", 0x0000000, 0x800000, CRC(a9acbf18) SHA1(d55122c70cbe78c2679598dc07863e1d1d1a31df) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "008-c2.c2", 0x0000001, 0x800000, CRC(6289eef9) SHA1(a2ede77bb2468a2e1486d74745a22a5451026039) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

ROM_START( jockeygpa ) /* MVS ONLY RELEASE */
	/* Officially licensed? Cart has a holographic 'SNK' sticker applied */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "008-epr_a.p1", 0x000000, 0x100000, CRC(b8f35532) SHA1(b46c96677f1bfe324b678112e9c614a20c550d51) ) /* M27C800 */
	/* P on eprom, correct chip label unknown */

	ROM_Y_ZOOM

	/* The Encrypted Boards do not have an s1 rom, data for it comes from the Cx ROMs */
	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x20000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_512K( "008-mg1.m1", CRC(d163c690) SHA1(1dfd04d20c5985037f07cd01000d0b04f3a8f4f4) ) /* M27C4001 */

	ROM_REGION( 0x0200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "008-v1.v1", 0x000000, 0x200000, CRC(443eadba) SHA1(3def3c22f0e276bc4c2fc7ff70ce473c08b0d2df) ) /* mask rom TC5316200 */

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "008-c1.c1", 0x0000000, 0x800000, CRC(a9acbf18) SHA1(d55122c70cbe78c2679598dc07863e1d1d1a31df) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "008-c2.c2", 0x0000001, 0x800000, CRC(6289eef9) SHA1(a2ede77bb2468a2e1486d74745a22a5451026039) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

/****************************************
 B-V-01
 . ???-????
 MVS PROGV (2000.11.17) / MVS CHAV (2000.10.26)

 Officially licensed? Cart has a holographic 'SNK' sticker applied

****************************************/

ROM_START( vliner ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "epr_7a.p1", 0x000000, 0x080000, CRC(052f93ed) SHA1(3a5330073d21fd068d44956680cfae7faa4f3951) ) /* AM27C400 */

	NEO_SFIX_128K( "s-1.s1", CRC(972d8c31) SHA1(41f09ef28a3791668ea304c74b8b06c117a50e9a) )

	NEO_BIOS_AUDIO_64K( "m-1.m1", CRC(9b92b7d1) SHA1(2c9b777feb9a8e43fa1bd942aba5afe3b5427d94) )

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", ROMREGION_ERASE00 )

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "c-1.c1", 0x000000, 0x80000, CRC(5118f7c0) SHA1(b6fb6e9cbb660580d98e00780ebf248c0995145a) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "c-2.c2", 0x000001, 0x80000, CRC(efe9b33e) SHA1(910c651aadce9bf59e51c338ceef62287756d2e8) ) /* Plane 2,3 */
ROM_END

ROM_START( vliner7e ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "epr_7e.p1", 0x000000, 0x080000, CRC(49a94db5) SHA1(5e3066ebe3afde9e59444b8c6e092a3713a173c0) ) /* AM27C400 */

	NEO_SFIX_128K( "s-1.s1", CRC(972d8c31) SHA1(41f09ef28a3791668ea304c74b8b06c117a50e9a) )

	NEO_BIOS_AUDIO_64K( "m-1.m1", CRC(9b92b7d1) SHA1(2c9b777feb9a8e43fa1bd942aba5afe3b5427d94) )

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", ROMREGION_ERASE00 )

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "c-1.c1", 0x000000, 0x80000, CRC(5118f7c0) SHA1(b6fb6e9cbb660580d98e00780ebf248c0995145a) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "c-2.c2", 0x000001, 0x80000, CRC(efe9b33e) SHA1(910c651aadce9bf59e51c338ceef62287756d2e8) ) /* Plane 2,3 */
ROM_END

ROM_START( vliner6e ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "epr_6e.p1", 0x000000, 0x080000, CRC(72a2c043) SHA1(b34bcc10ff33e4465126a6865fe8bf6b6a3d6cee) ) /* AM27C400 */

	NEO_SFIX_128K( "s-1.s1", CRC(972d8c31) SHA1(41f09ef28a3791668ea304c74b8b06c117a50e9a) )

	NEO_BIOS_AUDIO_64K( "m-1.m1", CRC(9b92b7d1) SHA1(2c9b777feb9a8e43fa1bd942aba5afe3b5427d94) )

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", ROMREGION_ERASE00 )

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "c-1.c1", 0x000000, 0x80000, CRC(5118f7c0) SHA1(b6fb6e9cbb660580d98e00780ebf248c0995145a) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "c-2.c2", 0x000001, 0x80000, CRC(efe9b33e) SHA1(910c651aadce9bf59e51c338ceef62287756d2e8) ) /* Plane 2,3 */
ROM_END

ROM_START( vliner54 ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "epr_54.p1", 0x000000, 0x080000, CRC(172efc18) SHA1(8ca739f8780a9e6fa19ac2c3e931d75871603f58) )

	NEO_SFIX_128K( "s-1.s1", CRC(972d8c31) SHA1(41f09ef28a3791668ea304c74b8b06c117a50e9a) )

	NEO_BIOS_AUDIO_64K( "m-1.m1", CRC(9b92b7d1) SHA1(2c9b777feb9a8e43fa1bd942aba5afe3b5427d94) )

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", ROMREGION_ERASE00 )

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "c-1.c1", 0x000000, 0x80000, CRC(5118f7c0) SHA1(b6fb6e9cbb660580d98e00780ebf248c0995145a) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "c-2.c2", 0x000001, 0x80000, CRC(efe9b33e) SHA1(910c651aadce9bf59e51c338ceef62287756d2e8) ) /* Plane 2,3 */
ROM_END

ROM_START( vliner53 ) /* MVS ONLY RELEASE */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "epr_53.p1", 0x000000, 0x080000, CRC(e263dce8) SHA1(c95e5b77c99828ee1b849d000a69fdd6bde502f8) )

	NEO_SFIX_128K( "s-1.s1", CRC(972d8c31) SHA1(41f09ef28a3791668ea304c74b8b06c117a50e9a) )

	NEO_BIOS_AUDIO_64K( "m-1.m1", CRC(9b92b7d1) SHA1(2c9b777feb9a8e43fa1bd942aba5afe3b5427d94) )

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", ROMREGION_ERASE00 )

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "c-1.c1", 0x000000, 0x80000, CRC(5118f7c0) SHA1(b6fb6e9cbb660580d98e00780ebf248c0995145a) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "c-2.c2", 0x000001, 0x80000, CRC(efe9b33e) SHA1(910c651aadce9bf59e51c338ceef62287756d2e8) ) /* Plane 2,3 */
ROM_END

/*************************************
 *
 *  Vektorlogic games, unlicensed
 *
 *************************************/

/****************************************
 NSBP V1.0U
 PROGRAM CART REVISION 2.0 COPYRIGHT VEKTORLOGIC (C) 2004 / GRAPHICS CART REVISION 1.2 COPYRIGHT VEKTORLOGIC (C) 2004
****************************************/

// this doesn't boot, protection like kof98?
// you can force it to boot with a simple debugger trick, but then it resets when starting a game
ROM_START( sbp ) /* Unlicensed, no official game ID # */ /* MVS ONLY VERSION */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "001-003-02a.u2", 0x000000, 0x080000, CRC(d054d264) SHA1(d1b4bc626d000e0679def0545940fa75035921ab) ) /* HN27C4096HG */

	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_LOAD( "001-003-02b.u2", 0x000000, 0x20000, CRC(2fd04b2a) SHA1(1acb446704ab56d0a33df7c48855aa8d00fd5a3c) ) /* M27C4001 */
	ROM_IGNORE(0x20000)
	ROM_IGNORE(0x20000)
	ROM_IGNORE(0x20000)

	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )
	ROM_Y_ZOOM

	NEO_BIOS_AUDIO_512K( "001-003-01b.u1", CRC(7b1f86f7) SHA1(15b6af7f9fbd0f1f6a1ecd912200ca8d0af2da2a) ) /* M27C4001 */

	ROM_REGION( 0x800000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "001-003-12a.u12", 0x000000, 0x400000, CRC(c96723b9) SHA1(52eec88550781d45f84efbf9b905d7e7912e96fa) ) /* M27C322 */
	ROM_LOAD( "001-003-13a.u13", 0x400000, 0x400000, CRC(08c339a5) SHA1(badc9510ae243ef2a7877977eb36efa81b1489fe) ) /* M27C322 */

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "001-003-03b.u3", 0x000000, 0x200000, CRC(44791317) SHA1(9e773eb9aae5ee767213bd17348ff8a312e9cb16) ) /* Plane 0,1 */ /* M27C160 */
	ROM_LOAD16_BYTE( "001-003-04b.u4", 0x000001, 0x200000, CRC(a3a1c0df) SHA1(3b1e5be673f7cbb04199a805b0e0de93dad8cb8c) ) /* Plane 2,3 */ /* M27C160 */
ROM_END



/*************************************
 *
 *  Bootleg sets
 *
 *************************************/

/*
    About supported sets:

    For many bootleg sets, only P's (program rom), M1 (sound driver) and S1 (text layer) roms were dumped.
    For these sets it is assumed that the original V's (sound data) and C's (gfx data) are used.
    This requires verification.

*/


/* Zintrick bootleg */

/* This Zintrick set appears to be a bootleg made from the CD version, not a genuine
   prototype the code is based on that of the NeoCD version with some minor patches,
   for example the ADK SAMPLE TEST text that appears on the screen is actually a hacked
   PROG LOAD ERROR message. The set is supported in order to distinguish the hacks from
   a real prototype should one turn up. */

ROM_START( zintrckb )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "zin-p1.bin", 0x000000, 0x100000, CRC(06c8fca7) SHA1(b7bf38965c3d0db4d7a9684d14cac94a45b4a45b))

	NEO_SFIX_128K( "zin-s1.bin", CRC(a7ab0e81) SHA1(f0649819b96cea79b05411e0b15c8edc677d79ba) )

	NEO_BIOS_AUDIO_128K( "zin-m1.bin", CRC(fd9627ca) SHA1(b640c1f1ff466f734bb1cb5d7b589cb7e8a55346) )

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "zin-v1.bin", 0x000000, 0x200000, CRC(c09f74f1) SHA1(d0b56a780a6eba85ff092240b1f1cc6718f17c21) )

	ROM_REGION( 0x400000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "zin-c1.bin", 0x000000, 0x200000, CRC(76aee189) SHA1(ad6929804c5b9a59aa609e6baebc6aa37e858a47) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "zin-c2.bin", 0x000001, 0x200000, CRC(844ed4b3) SHA1(fb7cd057bdc6cbe8b78097dd124118bae7402256) ) /* Plane 2,3 */
ROM_END

/* Idol Mahjong Final Romance 2 */

/* This set has been added from HBMAME for completeness, being analogous to the Zintrick one. */

ROM_START( froman2b )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "098.p1", 0x000000, 0x80000, CRC(09675541) SHA1(6afb89d43e67f93e40f3877cbedfec9566e3ff0f))

	NEO_SFIX_128K( "098.s1", CRC(0e6a7c73) SHA1(31b1194524dcc80ec4d63bac088b6fb4909f496c) )

	NEO_BIOS_AUDIO_128K( "098.m1", CRC(da4878cf) SHA1(ce13d18a4c5d01974df8542c67c4df00dbc6e7c1) )

	ROM_REGION( 0x100000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "098.v1", 0x000000, 0x100000, CRC(6f8ccddc) SHA1(696df2d0f416c2374b0eb7c858486054688c5bca) )

	ROM_REGION( 0x800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "098.c1", 0x0000000, 0x400000, CRC(29148bf7) SHA1(75097fbe8877720afbcbe4dbe30bc600466d759f) )
	ROM_LOAD16_BYTE( "098.c2", 0x0000001, 0x400000, CRC(226b1263) SHA1(dee6a4a0a727c1d8a6d298cb38ed1b9901992d5b) )
ROM_END


ROM_START( crswd2bl )
	ROM_REGION( 0x200000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "054-p1.p1", 0x100000, 0x100000, CRC(64836147) SHA1(083cb1626885893e736fc9998036c952cd4d503b) )
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "054-s1.s1", CRC(22e02ddd) SHA1(ebd834affc763cc5854abf1c6c42f43f3f3755fd) )

	NEO_BIOS_AUDIO_128K( "054-m1.m1", CRC(63e28343) SHA1(f46dbc2f1d6033b11047cca31a9a7d715dc69cb2) )

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "054-v1.v1", 0x000000, 0x200000, CRC(22d4b93b) SHA1(0515f2ee5d9a8ce424c80721e06f746ac6a543a8) )

	ROM_REGION( 0x800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "054-c1.c1", 0x000000, 0x400000, CRC(8221b712) SHA1(7e68871f1bfc402ef27c8fa088c680cbd133f71a) )
	ROM_LOAD16_BYTE( "054-c2.c2", 0x000001, 0x400000, CRC(d6c6183d) SHA1(cc546ff063fae2c01c109fabcd5b2d29ec3299db) )
ROM_END


/* The King of Fighters '97 bootlegs */

ROM_START( kof97pls )
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "kf97-p1p.bin", 0x000000, 0x100000, CRC(c01fda46) SHA1(bc6402f5082efc80a8936364c657165f19b49415) )
	ROM_LOAD16_WORD_SWAP( "kf97-p2p.bin", 0x100000, 0x400000, CRC(5502b020) SHA1(37c48198d8b3798910a44075782cd1a20b687b4a) )

	NEO_SFIX_128K( "kf97-s1p.bin", CRC(73254270) SHA1(8d06305f9d8890da1327356272b88bdd0dc089f5) )

	NEO_BIOS_AUDIO_128K( "232-m1.m1", CRC(45348747) SHA1(ed77cbae2b208d1177a9f5f6e8cd57070e90b65b) ) /* TC531001 */

	ROM_REGION( 0xc00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "232-v1.v1", 0x000000, 0x400000, CRC(22a2b5b5) SHA1(ebdbc977332e6d93e266755000b43857e0082965) ) /* TC5332204 */
	ROM_LOAD( "232-v2.v2", 0x400000, 0x400000, CRC(2304e744) SHA1(98d283e2bcc9291a53f52afd35ef76dfb0828432) ) /* TC5332204 */
	ROM_LOAD( "232-v3.v3", 0x800000, 0x400000, CRC(759eb954) SHA1(54e77c4e9e6b89458e59824e478ddc33a9c72655) ) /* TC5332204 */

	ROM_REGION( 0x2800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "232-c1.c1", 0x0000000, 0x800000, CRC(5f8bf0a1) SHA1(e8b63bbc814de171fd18c5864a7fc639970c1ecf) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "232-c2.c2", 0x0000001, 0x800000, CRC(e4d45c81) SHA1(fdb2b9326362e27b1c7a5beb977e0bc537488186) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "232-c3.c3", 0x1000000, 0x800000, CRC(581d6618) SHA1(14d3124a08ded59f86932c6b28e1a4e48c564ccd) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "232-c4.c4", 0x1000001, 0x800000, CRC(49bb1e68) SHA1(f769c1bd1b019521111ff3f0d22c63cb1f2640ef) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "232-c5.c5", 0x2000000, 0x400000, CRC(34fc4e51) SHA1(b39c65f27873f71a6f5a5d1d04e5435f874472ee) ) /* Plane 0,1 */ /* TC5332205 */
	ROM_LOAD16_BYTE( "232-c6.c6", 0x2000001, 0x400000, CRC(4ff4d47b) SHA1(4d5689ede24a5fe4330bd85d4d3f4eb2795308bb) ) /* Plane 2,3 */ /* TC5332205 */
ROM_END

ROM_START( kof97oro )
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "orochi-p1.bin",  0x0000000, 0x100000, CRC(6dcb2946) SHA1(3ccb3fdf3d32a75c7fcfefff5db1f3c75054731f) )
	ROM_LOAD16_WORD_SWAP( "orochi-p21.bin", 0x0200000, 0x100000, CRC(6e1c4d8c) SHA1(f514638a599a8a582c5f4df72f6a957bab776b7e) )
	ROM_CONTINUE( 0x100000, 0x100000 )
	ROM_LOAD16_WORD_SWAP( "orochi-p29.bin", 0x0400000, 0x100000, CRC(4c7c0221) SHA1(fdd05927743cb12210b74768155bb3f59bff01b5) )
	ROM_CONTINUE( 0x300000, 0x100000 )

	NEO_SFIX_128K( "orochi-s1.bin", CRC(4ee2149a) SHA1(180a1a90021031eac1a643b769d9cdeda56518f5) )

	NEO_BIOS_AUDIO_128K( "orochi-m1.bin", CRC(45348747) SHA1(ed77cbae2b208d1177a9f5f6e8cd57070e90b65b) )

	ROM_REGION( 0xc00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "orochi-v1.bin", 0x000000, 0x0400000, CRC(22a2b5b5) SHA1(ebdbc977332e6d93e266755000b43857e0082965) )
	ROM_LOAD( "orochi-v2.bin", 0x400000, 0x0400000, CRC(2304e744) SHA1(98d283e2bcc9291a53f52afd35ef76dfb0828432) )
	ROM_LOAD( "orochi-v3.bin", 0x800000, 0x0400000, CRC(759eb954) SHA1(54e77c4e9e6b89458e59824e478ddc33a9c72655) )

	ROM_REGION( 0x2800000, "cslot1:sprites", 0 )
	// The C1 and C2 here are reconstructed but very likely to be correct.
	ROM_LOAD16_BYTE( "orochi-c1.bin",  0x0000000, 0x1000000, BAD_DUMP CRC(f13e841c) SHA1(e24b3fb5f7e1c1f4752cad382c264f5f93e737a0) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "orochi-c2.bin",  0x0000001, 0x1000000, BAD_DUMP CRC(2db1f6d3) SHA1(13d957c04bd69f0db140e4633c39db4a9e44eab8) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "orochi-c51.bin", 0x2000000, 0x0200000, CRC(a90340cb) SHA1(97eaa89f0e860e2c591ca3a995fd910d8116347d) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "orochi-c61.bin", 0x2000001, 0x0200000, CRC(188e351a) SHA1(ab724250bc07ace0873fc825b798ace934260988) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "orochi-c52.bin", 0x2400000, 0x0200000, CRC(d4eec50a) SHA1(0930cce5346fbbd5c1524f9148d0577cbe634420) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "orochi-c62.bin", 0x2400001, 0x0200000, CRC(031b1ad5) SHA1(d47b3452953b553348be0a55473b863ce2872f6e) ) /* Plane 2,3 */
ROM_END

ROM_START( kog )
	ROM_REGION( 0x600000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "5232-p1.bin", 0x000000, 0x200000, CRC(d2413ec6) SHA1(c0bf409d1e714cba5fdc6f79e4c2aec805316634) )
	ROM_LOAD16_WORD_SWAP( "232-p2.sp2",  0x200000, 0x400000, CRC(158b23f6) SHA1(9744620a70513490aaf9c5eda33e5ec31222be19) ) /* TC5332205 */

	NEO_SFIX_128K( "5232-s1.bin", CRC(0bef69da) SHA1(80918586e694dce35c4dba796eb18abf6a070ebb) )

	NEO_BIOS_AUDIO_128K( "232-m1.m1", CRC(45348747) SHA1(ed77cbae2b208d1177a9f5f6e8cd57070e90b65b) ) /* TC531001 */

	ROM_REGION( 0xc00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "232-v1.v1", 0x000000, 0x400000, CRC(22a2b5b5) SHA1(ebdbc977332e6d93e266755000b43857e0082965) ) /* TC5332204 */
	ROM_LOAD( "232-v2.v2", 0x400000, 0x400000, CRC(2304e744) SHA1(98d283e2bcc9291a53f52afd35ef76dfb0828432) ) /* TC5332204 */
	ROM_LOAD( "232-v3.v3", 0x800000, 0x400000, CRC(759eb954) SHA1(54e77c4e9e6b89458e59824e478ddc33a9c72655) ) /* TC5332204 */

	ROM_REGION( 0x2800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "5232-c1a.bin", 0x0000000, 0x800000, CRC(4eab9b0a) SHA1(a6f6b755215a3f41474e0a76b5463303a522c2d3) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "5232-c2a.bin", 0x0000001, 0x800000, CRC(697f8fd0) SHA1(5784464c2357ccef8e6e79b6298843fc3d13b39c) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "5232-c1b.bin", 0x1000000, 0x800000, CRC(1143fdf3) SHA1(9dc5fe9a3b7599380db62095880e2d6f237a41bd) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "5232-c2b.bin", 0x1000001, 0x800000, CRC(ea82cf8f) SHA1(3d9ab64b69cecd6b3950839ac2c6d151ad66dcf8) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "5232-c3.bin",  0x2000000, 0x400000, CRC(abd1be07) SHA1(857eb68bbee4538770bbfa77aaa540d61ab0abcd) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "5232-c4.bin",  0x2000001, 0x400000, CRC(d2bd967b) SHA1(c494e0a98e127d37ca360a28accc167fa50fb626) ) /* Plane 2,3 */
ROM_END

/* Shock Troopers - 2nd Squad bootleg */

ROM_START( lans2004 )
	ROM_REGION( 0x600000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "lnsq-p1.bin",  0x000000, 0x200000, CRC(b40a879a) SHA1(379f3d44b0bae430b32157fd5f4018d77b243c58) )
	ROM_LOAD16_WORD_SWAP( "lnsq-p21.bin", 0x200000, 0x200000, CRC(ecdb2d42) SHA1(0d930cd369dfbcab7778b144355e5f70874aa324) )
	ROM_LOAD16_WORD_SWAP( "lnsq-p22.bin", 0x400000, 0x200000, CRC(fac5e2e7) SHA1(5cce7226c137da80c969df00e1cda41ef9c5082c) )

	NEO_SFIX_128K( "lnsq-s1.bin", CRC(39e82897) SHA1(24a8c94dd7e70ecde8f90ea17f75b6b5d065704f) )

	NEO_BIOS_AUDIO_128K( "246-m1.bin", CRC(d0604ad1) SHA1(fae3cd52a177eadd5f5775ace957cc0f8301e65d) )

	ROM_REGION( 0xA00000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "lnsq-v1.bin", 0x000000, 0x400000, CRC(4408ebc3) SHA1(e3f4d8a7e243a8cf48e97d91bbfec7829c0d9404) )
	ROM_LOAD( "lnsq-v2.bin", 0x400000, 0x400000, CRC(3d953975) SHA1(6a4ab02ab3d4416a65343cf16815007cb273f19b) )
	ROM_LOAD( "lnsq-v3.bin", 0x800000, 0x200000, CRC(437d1d8e) SHA1(95e015c21707b53ed7223eaa19f6cdcfb4d94f0c) )

	ROM_REGION( 0x3000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "lnsq-c1.bin", 0x0000000, 0x800000, CRC(b83de59f) SHA1(8cc060f9a57ab7d4238543b0bce5f5cd1d271d4f) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "lnsq-c2.bin", 0x0000001, 0x800000, CRC(e08969fd) SHA1(c192639d023cdad64a8f53dbcda02aa8cfb4168e) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "lnsq-c3.bin", 0x1000000, 0x800000, CRC(013f2cda) SHA1(6261111ce69dc23fbf97241131e5a6a49355d18c) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "lnsq-c4.bin", 0x1000001, 0x800000, CRC(d8c3a758) SHA1(d19ca3be06f9fb0cb1933b1eb3da318524c3145d) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "lnsq-c5.bin", 0x2000000, 0x800000, CRC(75500b82) SHA1(06d2afe94ea3eb3c4e523f593b8e709dd7c284a3) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "lnsq-c6.bin", 0x2000001, 0x800000, CRC(670ac13b) SHA1(f448a144caae51b69ea19e1f43940db135d1164a) ) /* Plane 2,3 */
ROM_END

/* Garou - Mark of the Wolves bootleg */

ROM_START( garoubl ) /* bootleg of garoup */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "garou-p1.bin", 0x000000, 0x100000, CRC(fd446d59) SHA1(e37484673a935b2af76c84cd26977c751c0f8cff) )
	ROM_LOAD16_WORD_SWAP( "garou-p2.bin", 0x100000, 0x400000, CRC(3fb10a84) SHA1(4e4a4f4cd7f0ad2520c938c64c8910e6f8805eaf) )

	NEO_SFIX_128K( "garou-s1.bin", CRC(df720e33) SHA1(58d05002d4851682bd626241fa7b70f78f6f3bc8) )

	NEO_BIOS_AUDIO_512K( "garou-m1.bin", CRC(7c51d002) SHA1(01ffba6cbc8da07804f7b21d8c71c39d64a1a4e2) )

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "garou-v1.bin", 0x000000, 0x400000, CRC(98d736e4) SHA1(3edda9a1d45b0e38a85156d24fc8ff2f0011239b) )
	ROM_LOAD( "garou-v2.bin", 0x400000, 0x400000, CRC(eb43c03f) SHA1(83c9c168b154e60a64f1033004b2d33e218bbb8b) )
	ROM_LOAD( "garou-v3.bin", 0x800000, 0x400000, CRC(10a0f814) SHA1(e86def80d6fb2a38ebc9f3338d22f28c15ce85da) )
	ROM_LOAD( "garou-v4.bin", 0xc00000, 0x400000, CRC(8918fdd3) SHA1(60ea2104a0f993341124728d8fde0e8e937c55ef) )

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "garou-c1.bin", 0x0000000, 0x1000000, CRC(e2ac83fa) SHA1(186f88a85d80efbb0371bd42cca152b6b59817fb) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "garou-c2.bin", 0x0000001, 0x1000000, CRC(7c344b24) SHA1(f8af62a917e0ce2bf8ae4f17736fdd84d55d0788) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "garou-c3.bin", 0x2000000, 0x1000000, CRC(d3aec5a6) SHA1(c1a584909a8a1519f676aa49351742b87c18276d) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "garou-c4.bin", 0x2000001, 0x1000000, CRC(e02a242d) SHA1(8a73826d14880303a7bea2a903e842c733178aca) ) /* Plane 2,3 */
ROM_END

/* Metal Slug 3 bootleg */

ROM_START( mslug3b6 ) /* This "Metal Slug 6" is a hack/bootleg of Metal Slug 3, the real Metal Slug 6 is on Atomiswave Hardware */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "299-p1.bin", 0x000000, 0x200000, CRC(5f2fe228) SHA1(747775a2dfc0da87ad2ddd4f57ce5b2522f23fa5) )
	ROM_LOAD16_WORD_SWAP( "299-p2.bin", 0x100000, 0x400000, CRC(193fa835) SHA1(fb1f26db7998b0bb6b1c8b92500c1596ec5dfc71) )

	NEO_SFIX_128K( "299-s1.bin", CRC(6f8b9635) SHA1(86b0c8c0ccac913c6192ed6a96c35d4e1a5e8061) )

	NEO_BIOS_AUDIO_512K( "256-m1.m1", CRC(eaeec116) SHA1(54419dbb21edc8c4b37eaac2e7ad9496d2de037a) ) /* mask rom TC534000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "256-v1.v1", 0x000000, 0x400000, CRC(f2690241) SHA1(fd56babc1934d10e0d27c32f032f9edda7ca8ce9) ) /* TC5332204 */
	ROM_LOAD( "256-v2.v2", 0x400000, 0x400000, CRC(7e2a10bd) SHA1(0d587fb9f64cba0315ce2d8a03e2b8fe34936dff) ) /* TC5332204 */
	ROM_LOAD( "256-v3.v3", 0x800000, 0x400000, CRC(0eaec17c) SHA1(c3ed613cc6993edd6fc0d62a90bcd85de8e21915) ) /* TC5332204 */
	ROM_LOAD( "256-v4.v4", 0xc00000, 0x400000, CRC(9b4b22d4) SHA1(9764fbf8453e52f80aa97a46fb9cf5937ef15a31) ) /* TC5332204 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "256-c1.c1", 0x0000000, 0x800000, CRC(5a79c34e) SHA1(b8aa51fa50935cae62ab3d125b723ab888691e60) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c2.c2", 0x0000001, 0x800000, CRC(944c362c) SHA1(3843ab300f956280475469caee70135658f67089) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c3.c3", 0x1000000, 0x800000, CRC(6e69d36f) SHA1(94e8cf42e999114b4bd8b30e0aa2f365578c4c9a) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c4.c4", 0x1000001, 0x800000, CRC(b755b4eb) SHA1(804700a0966a48f130c434ede3f970792ea74fa5) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c5.c5", 0x2000000, 0x800000, CRC(7aacab47) SHA1(312c1c9846175fe1a3cad51d5ae230cf674fc93d) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c6.c6", 0x2000001, 0x800000, CRC(c698fd5d) SHA1(16818883b06849ba2f8d61bdd5e21aaf99bd8408) ) /* Plane 2,3 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c7.c7", 0x3000000, 0x800000, CRC(cfceddd2) SHA1(7def666adf8bd1703f40c61f182fc040b6362dc9) ) /* Plane 0,1 */ /* TC5364205 */
	ROM_LOAD16_BYTE( "256-c8.c8", 0x3000001, 0x800000, CRC(4d9be34c) SHA1(a737bdfa2b815aea7067e7af2636e83a9409c414) ) /* Plane 2,3 */ /* TC5364205 */
ROM_END

/* Nightmare in the Dark bootleg */

ROM_START( nitdbl )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "nitd-p1.bin", 0x000000, 0x080000, CRC(1a05bd1b) SHA1(7bbddef842d50b0778711063af695b168a76ff61) )

	NEO_SFIX_128K( "nitd-s1.bin", CRC(dd3bf47c) SHA1(881271caee6508b8be51bf1b59c8f1e58e08e551) )

	/* Bootleg m1 is 128k, data is identical */
	NEO_BIOS_AUDIO_512K( "260-m1.m1", CRC(6407c5e5) SHA1(d273e154cc905b63205a17a1a6d419cac3485a92) ) /* TC534000 */

	ROM_REGION( 0x400000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "nitd-v1.bin", 0x000000, 0x200000, CRC(79008868) SHA1(90bd6aaefd37341297ab1f4ae7246e52facd87d0) )
	ROM_LOAD( "nitd-v2.bin", 0x200000, 0x200000, CRC(728558f9) SHA1(309aa7c933c199b2e540a601b363e7af8744fe00) )

	ROM_REGION( 0x800000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "nitd-c1.bin", 0x000000, 0x200000, CRC(b4353190) SHA1(90d5352e243a05f5c2be4fa7475667bb56e78016) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "nitd-c2.bin", 0x000001, 0x200000, CRC(6e27511f) SHA1(1fc5cf7786ad0f0bc7b1623acabe605ad04af3c1) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "nitd-c3.bin", 0x400000, 0x200000, CRC(472cf075) SHA1(7cdd25019e37a3d127e68a4179c051881df19afa) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "nitd-c4.bin", 0x400001, 0x200000, CRC(4c3926e6) SHA1(7fc54a9886dbef911f7b226e3cd20081c535e989) ) /* Plane 2,3 */
ROM_END

/* The King of Fighters 2001 bootlegs */

ROM_START( cthd2003 ) /* Protected hack/bootleg of kof2001 Phenixsoft */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "5003-p1.bin", 0x000000, 0x100000, CRC(bb7602c1) SHA1(abf329a40f34c88f7325b255e3bc090db1edaca4) )
	ROM_LOAD16_WORD_SWAP( "5003-p2.bin", 0x100000, 0x400000, CRC(adc1c22b) SHA1(271e0629989257a0d21d280c05df53df259414b1) )

	NEO_SFIX_128K( "5003-s1.bin", CRC(5ba29aab) SHA1(e7ea67268a10243693bff722e6fd2276ca540acf) )

	NEO_BIOS_AUDIO_128K( "5003-m1.bin", CRC(1a8c274b) SHA1(5f6f9c533f4a296a18c741ce59a69cf6f5c836b9) )

	/* sound roms are identical to kof2001 */
	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "262-v1-08-e0.v1", 0x000000, 0x400000, CRC(83d49ecf) SHA1(2f2c116e45397652e77fcf5d951fa5f71b639572) ) /* mask rom TC5332204 */
	ROM_LOAD( "262-v2-08-e0.v2", 0x400000, 0x400000, CRC(003f1843) SHA1(bdd58837ad542548bd4053c262f558af88e3b989) ) /* mask rom TC5332204 */
	ROM_LOAD( "262-v3-08-e0.v3", 0x800000, 0x400000, CRC(2ae38dbe) SHA1(4e82b7dd3b899d61907620517a5a27bdaba0725d) ) /* mask rom TC5332204 */
	ROM_LOAD( "262-v4-08-e0.v4", 0xc00000, 0x400000, CRC(26ec4dd9) SHA1(8bd68d95a2d913be41a51f51e48dbe3bff5924fb) ) /* mask rom TC5332204 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "5003-c1.bin", 0x0000000, 0x800000, CRC(68f54b67) SHA1(e2869709b11ea2846799fe431211c83e928e103e) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "5003-c2.bin", 0x0000001, 0x800000, CRC(2f8849d5) SHA1(7ef74981aa056f5acab4ddabffd3e98b4cb970be) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "5003-c3.bin", 0x1000000, 0x800000, CRC(ac4aff71) SHA1(c983f642e68deaa40fee3e208f2dd55f3bacbdc1) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "5003-c4.bin", 0x1000001, 0x800000, CRC(afef5d66) SHA1(39fe785563fbea54bba88de60dcc62e2458bd74a) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "5003-c5.bin", 0x2000000, 0x800000, CRC(c7c1ae50) SHA1(f54f5be7513a5ce2f01ab107a2b26f6a9ee1f2a9) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "5003-c6.bin", 0x2000001, 0x800000, CRC(613197f9) SHA1(6d1fefa1be81b79e251e55a1352544c0298e4674) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "5003-c7.bin", 0x3000000, 0x800000, CRC(64ddfe0f) SHA1(361f3f4618009bf6419961266eb9ab5002bef53c) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "5003-c8.bin", 0x3000001, 0x800000, CRC(917a1439) SHA1(6f28d1d7c6edee1283f25e632c69204dbebe40af) ) /* Plane 2,3 */
ROM_END

ROM_START( ct2k3sp ) /* Protected hack/bootleg of kof2001 Phenixsoft */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "5003-p1sp.bin", 0x000000, 0x100000, CRC(ab5c4de0) SHA1(ca9a6bfd4c32d791ecabb34ccbf2cbf0e84f97d5) )
	ROM_LOAD16_WORD_SWAP( "5003-p2.bin",   0x100000, 0x400000, CRC(adc1c22b) SHA1(271e0629989257a0d21d280c05df53df259414b1) )

	ROM_Y_ZOOM

	ROM_REGION( 0x40000, "cslot1:fixed", 0 )
	ROM_LOAD( "5003-s1sp.bin", 0x00000, 0x40000, CRC(6c355ab4) SHA1(71ac2bcd3dbda8402baecc56dabc2297b148a900) )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_128K( "5003-m1.bin", CRC(1a8c274b) SHA1(5f6f9c533f4a296a18c741ce59a69cf6f5c836b9) )

	/* sound roms are identical to kof2001 */
	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "262-v1-08-e0.v1", 0x000000, 0x400000, CRC(83d49ecf) SHA1(2f2c116e45397652e77fcf5d951fa5f71b639572) ) /* mask rom TC5332204 */
	ROM_LOAD( "262-v2-08-e0.v2", 0x400000, 0x400000, CRC(003f1843) SHA1(bdd58837ad542548bd4053c262f558af88e3b989) ) /* mask rom TC5332204 */
	ROM_LOAD( "262-v3-08-e0.v3", 0x800000, 0x400000, CRC(2ae38dbe) SHA1(4e82b7dd3b899d61907620517a5a27bdaba0725d) ) /* mask rom TC5332204 */
	ROM_LOAD( "262-v4-08-e0.v4", 0xc00000, 0x400000, CRC(26ec4dd9) SHA1(8bd68d95a2d913be41a51f51e48dbe3bff5924fb) ) /* mask rom TC5332204 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "5003-c1.bin", 0x0000000, 0x800000, CRC(68f54b67) SHA1(e2869709b11ea2846799fe431211c83e928e103e) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "5003-c2.bin", 0x0000001, 0x800000, CRC(2f8849d5) SHA1(7ef74981aa056f5acab4ddabffd3e98b4cb970be) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "5003-c3.bin", 0x1000000, 0x800000, CRC(ac4aff71) SHA1(c983f642e68deaa40fee3e208f2dd55f3bacbdc1) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "5003-c4.bin", 0x1000001, 0x800000, CRC(afef5d66) SHA1(39fe785563fbea54bba88de60dcc62e2458bd74a) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "5003-c5.bin", 0x2000000, 0x800000, CRC(c7c1ae50) SHA1(f54f5be7513a5ce2f01ab107a2b26f6a9ee1f2a9) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "5003-c6.bin", 0x2000001, 0x800000, CRC(613197f9) SHA1(6d1fefa1be81b79e251e55a1352544c0298e4674) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "5003-c7.bin", 0x3000000, 0x800000, CRC(64ddfe0f) SHA1(361f3f4618009bf6419961266eb9ab5002bef53c) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "5003-c8.bin", 0x3000001, 0x800000, CRC(917a1439) SHA1(6f28d1d7c6edee1283f25e632c69204dbebe40af) ) /* Plane 2,3 */
ROM_END

ROM_START( ct2k3sa ) /* Protected hack/bootleg of kof2001 Phenixsoft, alternate version */
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "5003-p1sa.bin", 0x000000, 0x100000, CRC(013a509d) SHA1(c61c9b777e6e062b5f4ad87cdb78e9ca05e9bfb9) )
	ROM_LOAD16_WORD_SWAP( "5003-p2.bin",   0x100000, 0x400000, CRC(adc1c22b) SHA1(271e0629989257a0d21d280c05df53df259414b1) )

	ROM_Y_ZOOM

	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_LOAD( "5003-s1sa.bin", 0x00000, 0x20000, CRC(4e1f7eae) SHA1(3302ad290804272447ccd2e8edd3ce968f043db1) )
	/* S1 needs redump, correct? */
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_128K( "5003-m1.bin", CRC(1a8c274b) SHA1(5f6f9c533f4a296a18c741ce59a69cf6f5c836b9) )

	/* Original set has 2x64 mbit sound roms */
	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "262-v1-08-e0.v1", 0x000000, 0x400000, CRC(83d49ecf) SHA1(2f2c116e45397652e77fcf5d951fa5f71b639572) ) /* mask rom TC5332204 */
	ROM_LOAD( "262-v2-08-e0.v2", 0x400000, 0x400000, CRC(003f1843) SHA1(bdd58837ad542548bd4053c262f558af88e3b989) ) /* mask rom TC5332204 */
	ROM_LOAD( "262-v3-08-e0.v3", 0x800000, 0x400000, CRC(2ae38dbe) SHA1(4e82b7dd3b899d61907620517a5a27bdaba0725d) ) /* mask rom TC5332204 */
	ROM_LOAD( "262-v4-08-e0.v4", 0xc00000, 0x400000, CRC(26ec4dd9) SHA1(8bd68d95a2d913be41a51f51e48dbe3bff5924fb) ) /* mask rom TC5332204 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "5003-c1.bin", 0x0000000, 0x800000, CRC(68f54b67) SHA1(e2869709b11ea2846799fe431211c83e928e103e) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "5003-c2.bin", 0x0000001, 0x800000, CRC(2f8849d5) SHA1(7ef74981aa056f5acab4ddabffd3e98b4cb970be) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "5003-c3.bin", 0x1000000, 0x800000, CRC(ac4aff71) SHA1(c983f642e68deaa40fee3e208f2dd55f3bacbdc1) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "5003-c4.bin", 0x1000001, 0x800000, CRC(afef5d66) SHA1(39fe785563fbea54bba88de60dcc62e2458bd74a) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "5003-c5.bin", 0x2000000, 0x800000, CRC(c7c1ae50) SHA1(f54f5be7513a5ce2f01ab107a2b26f6a9ee1f2a9) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "5003-c6.bin", 0x2000001, 0x800000, CRC(613197f9) SHA1(6d1fefa1be81b79e251e55a1352544c0298e4674) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "5003-c7.bin", 0x3000000, 0x800000, CRC(64ddfe0f) SHA1(361f3f4618009bf6419961266eb9ab5002bef53c) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "5003-c8.bin", 0x3000001, 0x800000, CRC(917a1439) SHA1(6f28d1d7c6edee1283f25e632c69204dbebe40af) ) /* Plane 2,3 */
ROM_END

/* Metal Slug 4 bootleg */

ROM_START( ms4plus )
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "ms4-p1p.bin", 0x000000, 0x100000, CRC(806a6e04) SHA1(df503772d607271ea51285154c9fd68e18b143ce) )
	ROM_LOAD16_WORD_SWAP( "263-p2.sp2",  0x100000, 0x400000, CRC(fdb7aed8) SHA1(dbeaec38f44e58ffedba99e70fa1439c2bf0dfa3) ) /* mask rom TC5332205 */

	NEO_SFIX_128K( "ms4-s1p.bin", CRC(07ff87ce) SHA1(96ddb439de2a26bf9869015d7fb19129d40f3fd9) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_128K( "263-m1.m1", CRC(46ac8228) SHA1(5aeea221050c98e4bb0f16489ce772bf1c80f787) ) /* mask rom TC531001 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "263-v1.v1", 0x000000, 0x800000, CRC(01e9b9cd) SHA1(0b045c2999449f7dab5ae8a42e957d5b6650431e) ) /* mask rom TC5364205 */
	ROM_LOAD( "263-v2.v2", 0x800000, 0x800000, CRC(4ab2bf81) SHA1(77ccfa48f7e3daddef5fe5229a0093eb2f803742) ) /* mask rom TC5364205 */

	ROM_REGION( 0x3000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "263-c1.c1", 0x0000000, 0x800000, CRC(84865f8a) SHA1(34467ada896eb7c7ca58658bf2a932936d8b632c) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "263-c2.c2", 0x0000001, 0x800000, CRC(81df97f2) SHA1(2b74493b8ec8fd49216a627aeb3db493f76124e3) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "263-c3.c3", 0x1000000, 0x800000, CRC(1a343323) SHA1(bbbb5232bba538c277ce2ee02e2956ca2243b787) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "263-c4.c4", 0x1000001, 0x800000, CRC(942cfb44) SHA1(d9b46c71726383c4581fb042e63897e5a3c92d1b) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "263-c5.c5", 0x2000000, 0x800000, CRC(a748854f) SHA1(2611bbedf9b5d8e82c6b2c99b88f842c46434d41) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "263-c6.c6", 0x2000001, 0x800000, CRC(5c8ba116) SHA1(6034db09c8706d4ddbcefc053efbc47a0953eb92) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

/* The King of Fighters 2002 bootlegs */

ROM_START( kof2002b )
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "265-p1.p1",  0x000000, 0x100000, CRC(9ede7323) SHA1(ad9d45498777fda9fa58e75781f48e09aee705a6) ) /* mask rom TC538200 */
	ROM_LOAD16_WORD_SWAP( "2k2-p2.bin", 0x100000, 0x400000, CRC(6dbee4df) SHA1(9a9646c81b233b44213c624b898c19f83e9a07f8) )

	NEO_SFIX_128K( "2k2-s1.bin", CRC(2255f5bf) SHA1(8a82b3e9717df30b580b9d0bac0b403f8102a002) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_128K( "265-m1.m1", CRC(85aaa632) SHA1(744fba4ca3bc3a5873838af886efb97a8a316104) ) /* mask rom TC531001 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "265-v1.v1", 0x000000, 0x800000, CRC(15e8f3f5) SHA1(7c9e6426b9fa6db0158baa17a6485ffce057d889) ) /* mask rom TC5364205 */
	ROM_LOAD( "265-v2.v2", 0x800000, 0x800000, CRC(da41d6f9) SHA1(a43021f1e58947dcbe3c8ca5283b20b649f0409d) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "2k2-c1.bin", 0x0000000, 0x800000, CRC(f25d3d66) SHA1(eb1da3e171c126d91e851ce141840709a2f62f8a) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "2k2-c2.bin", 0x0000001, 0x800000, CRC(e3e66f1d) SHA1(af93e9e134816353d6187a53959c6e418b83ad8d) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "2k2-c3.bin", 0x1000000, 0x800000, CRC(8732fa30) SHA1(81c482b375c04bcfbbc69e3e2a2e9ab567c9bb78) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "2k2-c4.bin", 0x1000001, 0x800000, CRC(0989fd40) SHA1(355d6b2c528319e41ce89952c5cf5bcc47cd6de0) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "2k2-c5.bin", 0x2000000, 0x800000, CRC(60635cd2) SHA1(0cf2c54e003edfcdbed64e0570e6b800e7ed3c1b) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "2k2-c6.bin", 0x2000001, 0x800000, CRC(bd736824) SHA1(d897fc8248ace145fef57d8aa393eaebc4a1ccc4) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "2k2-c7.bin", 0x3000000, 0x800000, CRC(2da8d8cf) SHA1(ab8aa88b8e1baba88e5fc01d0f3cb55503b6c81a) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "2k2-c8.bin", 0x3000001, 0x800000, CRC(2048404a) SHA1(d6d0f049ffc196334825328e0472b04e04bf6695) ) /* Plane 2,3 */
ROM_END

ROM_START( kf2k2pls )
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "2k2-p1p.bin", 0x000000, 0x100000, CRC(3ab03781) SHA1(86946c19f1c4d9ab5cde86688d698bf63118a39d) )
	ROM_LOAD16_WORD_SWAP( "265-p2.sp2",  0x100000, 0x400000, CRC(327266b8) SHA1(98f445cc0a94f8744d74bca71cb420277622b034) ) /* mask rom TC5332205 */

	NEO_SFIX_128K( "2k2-s1p.bin", CRC(595e0006) SHA1(ff086bdaa6f40e9ad963e1100a27f44618d684ed) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_128K( "265-m1.m1", CRC(85aaa632) SHA1(744fba4ca3bc3a5873838af886efb97a8a316104) ) /* mask rom TC531001 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "265-v1.v1", 0x000000, 0x800000, CRC(15e8f3f5) SHA1(7c9e6426b9fa6db0158baa17a6485ffce057d889) ) /* mask rom TC5364205 */
	ROM_LOAD( "265-v2.v2", 0x800000, 0x800000, CRC(da41d6f9) SHA1(a43021f1e58947dcbe3c8ca5283b20b649f0409d) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "265-c1.c1", 0x0000000, 0x800000, CRC(2b65a656) SHA1(9c46d8cf5b1ef322db442ac6a9b9406ab49206c5) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c2.c2", 0x0000001, 0x800000, CRC(adf18983) SHA1(150cd4a5e51e9df88688469d2ea7675c2cf3658a) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c3.c3", 0x1000000, 0x800000, CRC(875e9fd7) SHA1(28f52d56192d48bbc5dc3c97abf456bd34a58cbd) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c4.c4", 0x1000001, 0x800000, CRC(2da13947) SHA1(f8d79ec2c236aa3d3648a4f715676899602122c1) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c5.c5", 0x2000000, 0x800000, CRC(61bd165d) SHA1(b3424db84bc683d858fb635bc42728f9cdd89caf) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c6.c6", 0x2000001, 0x800000, CRC(03fdd1eb) SHA1(6155c7e802062f4eafa27e414c4e73ee59b868bf) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c7.c7", 0x3000000, 0x800000, CRC(1a2749d8) SHA1(af7d9ec1d576209826fa568f676bbff92f6d6ddd) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c8.c8", 0x3000001, 0x800000, CRC(ab0bb549) SHA1(d23afb60b7f831f7d4a98ad3c4a00ee19877a1ce) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

ROM_START( kf2k2pla )
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "2k2-p1pa.bin", 0x000000, 0x100000, CRC(6a3a02f3) SHA1(c9973b64e9a87fa38dde233ee3e9a73ba085b013) )
	ROM_LOAD16_WORD_SWAP( "265-p2.sp2",   0x100000, 0x400000, CRC(327266b8) SHA1(98f445cc0a94f8744d74bca71cb420277622b034) ) /* mask rom TC5332205 */

	NEO_SFIX_128K( "2k2-s1pa.bin", CRC(1a3ed064) SHA1(9749bb55c750e6b65d651998c2649c5fb68db68e))

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_128K( "265-m1.m1", CRC(85aaa632) SHA1(744fba4ca3bc3a5873838af886efb97a8a316104) ) /* mask rom TC531001 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "265-v1.v1", 0x000000, 0x800000, CRC(15e8f3f5) SHA1(7c9e6426b9fa6db0158baa17a6485ffce057d889) ) /* mask rom TC5364205 */
	ROM_LOAD( "265-v2.v2", 0x800000, 0x800000, CRC(da41d6f9) SHA1(a43021f1e58947dcbe3c8ca5283b20b649f0409d) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "265-c1.c1", 0x0000000, 0x800000, CRC(2b65a656) SHA1(9c46d8cf5b1ef322db442ac6a9b9406ab49206c5) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c2.c2", 0x0000001, 0x800000, CRC(adf18983) SHA1(150cd4a5e51e9df88688469d2ea7675c2cf3658a) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c3.c3", 0x1000000, 0x800000, CRC(875e9fd7) SHA1(28f52d56192d48bbc5dc3c97abf456bd34a58cbd) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c4.c4", 0x1000001, 0x800000, CRC(2da13947) SHA1(f8d79ec2c236aa3d3648a4f715676899602122c1) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c5.c5", 0x2000000, 0x800000, CRC(61bd165d) SHA1(b3424db84bc683d858fb635bc42728f9cdd89caf) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c6.c6", 0x2000001, 0x800000, CRC(03fdd1eb) SHA1(6155c7e802062f4eafa27e414c4e73ee59b868bf) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c7.c7", 0x3000000, 0x800000, CRC(1a2749d8) SHA1(af7d9ec1d576209826fa568f676bbff92f6d6ddd) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c8.c8", 0x3000001, 0x800000, CRC(ab0bb549) SHA1(d23afb60b7f831f7d4a98ad3c4a00ee19877a1ce) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

ROM_START( kf2k2mp )
	ROM_REGION( 0x800000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "kf02m-p1.bin", 0x000000, 0x400000, CRC(ff7c6ec0) SHA1(704c14d671dcb4cfed44d9f978a289cb7dd9d065) )
	ROM_LOAD16_WORD_SWAP( "kf02m-p2.bin", 0x400000, 0x400000, CRC(91584716) SHA1(90da863037cf775957fa154cd42536e221df5740) )

	NEO_SFIX_128K( "kf02m-s1.bin", CRC(348d6f2c) SHA1(586da8a936ebbb71af324339a4b60ec91dfa0990) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_128K( "265-m1.m1", CRC(85aaa632) SHA1(744fba4ca3bc3a5873838af886efb97a8a316104) ) /* mask rom TC531001 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "265-v1.v1", 0x000000, 0x800000, CRC(15e8f3f5) SHA1(7c9e6426b9fa6db0158baa17a6485ffce057d889) ) /* mask rom TC5364205 */
	ROM_LOAD( "265-v2.v2", 0x800000, 0x800000, CRC(da41d6f9) SHA1(a43021f1e58947dcbe3c8ca5283b20b649f0409d) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "265-c1.c1", 0x0000000, 0x800000, CRC(2b65a656) SHA1(9c46d8cf5b1ef322db442ac6a9b9406ab49206c5) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c2.c2", 0x0000001, 0x800000, CRC(adf18983) SHA1(150cd4a5e51e9df88688469d2ea7675c2cf3658a) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c3.c3", 0x1000000, 0x800000, CRC(875e9fd7) SHA1(28f52d56192d48bbc5dc3c97abf456bd34a58cbd) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c4.c4", 0x1000001, 0x800000, CRC(2da13947) SHA1(f8d79ec2c236aa3d3648a4f715676899602122c1) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c5.c5", 0x2000000, 0x800000, CRC(61bd165d) SHA1(b3424db84bc683d858fb635bc42728f9cdd89caf) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c6.c6", 0x2000001, 0x800000, CRC(03fdd1eb) SHA1(6155c7e802062f4eafa27e414c4e73ee59b868bf) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c7.c7", 0x3000000, 0x800000, CRC(1a2749d8) SHA1(af7d9ec1d576209826fa568f676bbff92f6d6ddd) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c8.c8", 0x3000001, 0x800000, CRC(ab0bb549) SHA1(d23afb60b7f831f7d4a98ad3c4a00ee19877a1ce) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

ROM_START( kf2k2mp2 )
	ROM_REGION( 0x600000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "k2k2m2p1.bin", 0x000000, 0x200000, CRC(1016806c) SHA1(a583b45e9c0d6f67b95c52e44444aabe88f68d97) )
	ROM_LOAD16_WORD_SWAP( "k2k2m2p2.bin", 0x200000, 0x400000, CRC(432fdf53) SHA1(d7e542cd84d948162c60768e40ee4ed33d8e7913) )

	NEO_SFIX_128K( "k2k2m2s1.bin", CRC(446e74c5) SHA1(efc2afb26578bad9eb21659c70eb0f827d6d1ef6) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_128K( "265-m1.m1", CRC(85aaa632) SHA1(744fba4ca3bc3a5873838af886efb97a8a316104) ) /* mask rom TC531001 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "265-v1.v1", 0x000000, 0x800000, CRC(15e8f3f5) SHA1(7c9e6426b9fa6db0158baa17a6485ffce057d889) ) /* mask rom TC5364205 */
	ROM_LOAD( "265-v2.v2", 0x800000, 0x800000, CRC(da41d6f9) SHA1(a43021f1e58947dcbe3c8ca5283b20b649f0409d) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "265-c1.c1", 0x0000000, 0x800000, CRC(2b65a656) SHA1(9c46d8cf5b1ef322db442ac6a9b9406ab49206c5) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c2.c2", 0x0000001, 0x800000, CRC(adf18983) SHA1(150cd4a5e51e9df88688469d2ea7675c2cf3658a) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c3.c3", 0x1000000, 0x800000, CRC(875e9fd7) SHA1(28f52d56192d48bbc5dc3c97abf456bd34a58cbd) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c4.c4", 0x1000001, 0x800000, CRC(2da13947) SHA1(f8d79ec2c236aa3d3648a4f715676899602122c1) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c5.c5", 0x2000000, 0x800000, CRC(61bd165d) SHA1(b3424db84bc683d858fb635bc42728f9cdd89caf) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c6.c6", 0x2000001, 0x800000, CRC(03fdd1eb) SHA1(6155c7e802062f4eafa27e414c4e73ee59b868bf) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c7.c7", 0x3000000, 0x800000, CRC(1a2749d8) SHA1(af7d9ec1d576209826fa568f676bbff92f6d6ddd) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "265-c8.c8", 0x3000001, 0x800000, CRC(ab0bb549) SHA1(d23afb60b7f831f7d4a98ad3c4a00ee19877a1ce) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

ROM_START( kof10th )
	ROM_REGION( 0x900000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT ) // Modified
	ROM_LOAD16_WORD_SWAP( "kf10-p1.bin", 0x000000, 0x800000, CRC(b1fd0c43) SHA1(5f842a8a27be2d957fd4140d6431ae47154997bb) )

	ROM_Y_ZOOM

	ROM_REGION( 0x40000, "cslot1:fixed", 0 ) // modified
	ROM_FILL( 0x000000, 0x40000, 0x000000 ) // modified
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_128K( "kf10-m1.bin", CRC(f6fab859) SHA1(0184aa1394b9f9946d610278b53b846020dd88dc) )

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "kf10-v1.bin", 0x000000, 0x800000, CRC(0fc9a58d) SHA1(9d79ef00e2c2abd9f29af5521c2fbe5798bf336f) )
	ROM_LOAD( "kf10-v2.bin", 0x800000, 0x800000, CRC(b8c475a4) SHA1(10caf9c69927a223445d2c4b147864c02ce520a8) )

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "kf10-c1a.bin", 0x0000000, 0x400000, CRC(3bbc0364) SHA1(e8aa7ff82f151ce1db56f259377b64cceef85af0) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c2a.bin", 0x0000001, 0x400000, CRC(91230075) SHA1(d9098e05a7ba6008661147b6bf8bc2f494b8b72b) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "kf10-c1b.bin", 0x0800000, 0x400000, CRC(b5abfc28) SHA1(eabf60992bb3485c95330065294071ec155bfe7c) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c2b.bin", 0x0800001, 0x400000, CRC(6cc4c6e1) SHA1(be824a944e745ee18efdc45c81fd496a4d624b9c) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "kf10-c3a.bin", 0x1000000, 0x400000, CRC(5b3d4a16) SHA1(93ac1cd7739100f8c32732644f81f2a19837b131) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c4a.bin", 0x1000001, 0x400000, CRC(c6f3419b) SHA1(340c17a73aeb7bf8a6209f8459e6f00000075b50) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "kf10-c3b.bin", 0x1800000, 0x400000, CRC(9d2bba19) SHA1(5ebbd0af3f83a60e33c8ccb743e3d5f5a96f1273) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c4b.bin", 0x1800001, 0x400000, CRC(5a4050cb) SHA1(8fd2291f349efa1ed5cd37ad4e273b60fe831a77) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "kf10-c5a.bin", 0x2000000, 0x400000, CRC(a289d1e1) SHA1(50c7d7ebde6e118a01036cc3e40827fcd9f0d3fd) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c6a.bin", 0x2000001, 0x400000, CRC(e6494b5d) SHA1(18e064b9867ae0b0794065f8dbefd486620419db) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "kf10-c5b.bin", 0x2800000, 0x400000, CRC(404fff02) SHA1(56d1b32c87ea4885e49264e8b21846e465a20e1f) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c6b.bin", 0x2800001, 0x400000, CRC(f2ccfc9e) SHA1(69db7fac7023785ab94ea711a72dbc2826cfe1a3) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "kf10-c7a.bin", 0x3000000, 0x400000, CRC(be79c5a8) SHA1(ded3c5eb3571647f50533eb682c2675372ace3fb) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c8a.bin", 0x3000001, 0x400000, CRC(a5952ca4) SHA1(76dbb3cb45ce5a4beffa1ed29491204fc6617e42) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "kf10-c7b.bin", 0x3800000, 0x400000, CRC(3fdb3542) SHA1(7d2050752a2064cd6729f483a0da93808e2c6033) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c8b.bin", 0x3800001, 0x400000, CRC(661b7a52) SHA1(0ae2ad2389134892f156337332b77adade3ddad1) ) /* Plane 2,3 */
ROM_END

ROM_START( kf10thep ) /* this is a hack of kof2002 much like the various korean hacks / bootlegs of games */
	ROM_REGION( 0x800000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "5008-p1.bin", 0x000000, 0x200000, CRC(bf5469ba) SHA1(f05236d8fffab5836c0d27becdeeb80def32ee49) )
	ROM_LOAD16_WORD_SWAP( "5008-p2.bin", 0x200000, 0x400000, CRC(a649ec38) SHA1(5c63ed5e5c848940f587c966da4908d04cf1293c) )
	ROM_LOAD16_WORD_SWAP( "5008-p3.bin", 0x600000, 0x200000, CRC(e629e13c) SHA1(6ebe080ce01c51064cb2f4d89315ba98a45ae727) )

	NEO_SFIX_128K( "5008-s1.bin", CRC(92410064) SHA1(1fb800b46341858207d3b6961a760289fbec7faa) )

	NEO_BIOS_AUDIO_128K( "5008-m1.bin", CRC(5a47d9ad) SHA1(0197737934653acc6c97221660d789e9914f3578) )
	//NEO_BIOS_AUDIO_128K( "5004-m1.bin", CRC(f6fab859) SHA1(0184aa1394b9f9946d610278b53b846020dd88dc) )

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "kf10-v1.bin", 0x000000, 0x800000, CRC(0fc9a58d) SHA1(9d79ef00e2c2abd9f29af5521c2fbe5798bf336f) )
	ROM_LOAD( "kf10-v2.bin", 0x800000, 0x800000, CRC(b8c475a4) SHA1(10caf9c69927a223445d2c4b147864c02ce520a8) )

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "kf10-c1a.bin", 0x0000000, 0x400000, CRC(3bbc0364) SHA1(e8aa7ff82f151ce1db56f259377b64cceef85af0) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c2a.bin", 0x0000001, 0x400000, CRC(91230075) SHA1(d9098e05a7ba6008661147b6bf8bc2f494b8b72b) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "kf10-c1b.bin", 0x0800000, 0x400000, CRC(b5abfc28) SHA1(eabf60992bb3485c95330065294071ec155bfe7c) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c2b.bin", 0x0800001, 0x400000, CRC(6cc4c6e1) SHA1(be824a944e745ee18efdc45c81fd496a4d624b9c) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "kf10-c3a.bin", 0x1000000, 0x400000, CRC(5b3d4a16) SHA1(93ac1cd7739100f8c32732644f81f2a19837b131) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c4a.bin", 0x1000001, 0x400000, CRC(c6f3419b) SHA1(340c17a73aeb7bf8a6209f8459e6f00000075b50) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "kf10-c3b.bin", 0x1800000, 0x400000, CRC(9d2bba19) SHA1(5ebbd0af3f83a60e33c8ccb743e3d5f5a96f1273) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c4b.bin", 0x1800001, 0x400000, CRC(5a4050cb) SHA1(8fd2291f349efa1ed5cd37ad4e273b60fe831a77) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "kf10-c5a.bin", 0x2000000, 0x400000, CRC(a289d1e1) SHA1(50c7d7ebde6e118a01036cc3e40827fcd9f0d3fd) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c6a.bin", 0x2000001, 0x400000, CRC(e6494b5d) SHA1(18e064b9867ae0b0794065f8dbefd486620419db) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "kf10-c5b.bin", 0x2800000, 0x400000, CRC(404fff02) SHA1(56d1b32c87ea4885e49264e8b21846e465a20e1f) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c6b.bin", 0x2800001, 0x400000, CRC(f2ccfc9e) SHA1(69db7fac7023785ab94ea711a72dbc2826cfe1a3) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "kf10-c7a.bin", 0x3000000, 0x400000, CRC(be79c5a8) SHA1(ded3c5eb3571647f50533eb682c2675372ace3fb) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c8a.bin", 0x3000001, 0x400000, CRC(a5952ca4) SHA1(76dbb3cb45ce5a4beffa1ed29491204fc6617e42) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "5008-c7b.bin", 0x3800000, 0x400000, CRC(33604ef0) SHA1(57deec23c81d5d673ce5992cef1f2567f1a2148e) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "5008-c8b.bin", 0x3800001, 0x400000, CRC(51f6a8f8) SHA1(9ef1cdbdd125a2b430346c22b59f36902312905f) ) /* Plane 2,3 */
ROM_END

ROM_START( kf2k5uni )
	ROM_REGION( 0x800000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "5006-p2a.bin", 0x000000, 0x400000, CRC(ced883a2) SHA1(f93db4d74ce0a73a3e9631966fee37be22470c89) )
	ROM_LOAD16_WORD_SWAP( "5006-p1.bin",  0x400000, 0x400000, CRC(72c39c46) SHA1(4ba0657de20319c0bc30c7c3bba7d7331d0ce9a7) )

	NEO_SFIX_128K( "5006-s1.bin", CRC(91f8c544) SHA1(9d16cafb9ca4bc54f31f7fd82b1be06ec8b11c79) )

	NEO_BIOS_AUDIO_128K( "5006-m1.bin", CRC(9050bfe7) SHA1(765bf3d954f775231b7ef2504bb844cd0b29e3f7) )

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "kf10-v1.bin", 0x000000, 0x800000, CRC(0fc9a58d) SHA1(9d79ef00e2c2abd9f29af5521c2fbe5798bf336f) )
	ROM_LOAD( "kf10-v2.bin", 0x800000, 0x800000, CRC(b8c475a4) SHA1(10caf9c69927a223445d2c4b147864c02ce520a8) )

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "kf10-c1a.bin", 0x0000000, 0x400000, CRC(3bbc0364) SHA1(e8aa7ff82f151ce1db56f259377b64cceef85af0) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c2a.bin", 0x0000001, 0x400000, CRC(91230075) SHA1(d9098e05a7ba6008661147b6bf8bc2f494b8b72b) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "kf10-c1b.bin", 0x0800000, 0x400000, CRC(b5abfc28) SHA1(eabf60992bb3485c95330065294071ec155bfe7c) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c2b.bin", 0x0800001, 0x400000, CRC(6cc4c6e1) SHA1(be824a944e745ee18efdc45c81fd496a4d624b9c) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "kf10-c3a.bin", 0x1000000, 0x400000, CRC(5b3d4a16) SHA1(93ac1cd7739100f8c32732644f81f2a19837b131) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c4a.bin", 0x1000001, 0x400000, CRC(c6f3419b) SHA1(340c17a73aeb7bf8a6209f8459e6f00000075b50) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "kf10-c3b.bin", 0x1800000, 0x400000, CRC(9d2bba19) SHA1(5ebbd0af3f83a60e33c8ccb743e3d5f5a96f1273) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c4b.bin", 0x1800001, 0x400000, CRC(5a4050cb) SHA1(8fd2291f349efa1ed5cd37ad4e273b60fe831a77) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "kf10-c5a.bin", 0x2000000, 0x400000, CRC(a289d1e1) SHA1(50c7d7ebde6e118a01036cc3e40827fcd9f0d3fd) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c6a.bin", 0x2000001, 0x400000, CRC(e6494b5d) SHA1(18e064b9867ae0b0794065f8dbefd486620419db) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "kf10-c5b.bin", 0x2800000, 0x400000, CRC(404fff02) SHA1(56d1b32c87ea4885e49264e8b21846e465a20e1f) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c6b.bin", 0x2800001, 0x400000, CRC(f2ccfc9e) SHA1(69db7fac7023785ab94ea711a72dbc2826cfe1a3) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "kf10-c7a.bin", 0x3000000, 0x400000, CRC(be79c5a8) SHA1(ded3c5eb3571647f50533eb682c2675372ace3fb) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c8a.bin", 0x3000001, 0x400000, CRC(a5952ca4) SHA1(76dbb3cb45ce5a4beffa1ed29491204fc6617e42) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "kf10-c7b.bin", 0x3800000, 0x400000, CRC(3fdb3542) SHA1(7d2050752a2064cd6729f483a0da93808e2c6033) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "kf10-c8b.bin", 0x3800001, 0x400000, CRC(661b7a52) SHA1(0ae2ad2389134892f156337332b77adade3ddad1) ) /* Plane 2,3 */
ROM_END

ROM_START( kof2k4se )
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "k2k4s-p2.bin", 0x000000, 0x080000, CRC(21a84084) SHA1(973e8a0bffa0e1f055803f663f81a8e03701802d) )
	ROM_LOAD16_WORD_SWAP( "k2k4s-p3.bin", 0x080000, 0x080000, CRC(febb484e) SHA1(4b1838795b84f22d578ad043641df0a7bf7d9774) )
	ROM_LOAD16_WORD_SWAP( "k2k4s-p1.bin", 0x100000, 0x400000, CRC(e6c50566) SHA1(cc6a3489a3bfeb4dcc65b6ddae0030f7e66fbabe) )

	NEO_SFIX_128K( "k2k4s-s1.bin", CRC(a3c9b2d8) SHA1(1472d2cbd7bb73e84824ecf773924007e6117e77) )

	NEO_BIOS_AUDIO_128K( "k2k4s-m1.bin", CRC(5a47d9ad) SHA1(0197737934653acc6c97221660d789e9914f3578) )

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "k2k4s-v2.bin", 0x000000, 0x800000, CRC(e4ddfb3f) SHA1(eb8220ab01c16cf9244b7f3f9912bec0db561b85) )
	ROM_LOAD( "k2k4s-v1.bin", 0x800000, 0x800000, CRC(b887d287) SHA1(f593a5722df6f6fac023d189a739a117e976bb2f) )

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "k2k4s-c4.bin", 0x0000000, 0x800000, CRC(7a050288) SHA1(55a20c5b01e11a859f096af3f8e09986025d288f) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "k2k4s-c8.bin", 0x0000001, 0x800000, CRC(e924afcf) SHA1(651e974f7339d2cdcfa58c5398013197a0525b77) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "k2k4s-c3.bin", 0x1000000, 0x800000, CRC(959fad0b) SHA1(63ab83ddc5f688dc8165a7ff8d262df3fcd942a2) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "k2k4s-c7.bin", 0x1000001, 0x800000, CRC(efe6a468) SHA1(2a414285e48aa948b5b0d4a9333bab083b5fb853) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "k2k4s-c2.bin", 0x2000000, 0x800000, CRC(74bba7c6) SHA1(e01adc7a4633bc0951b9b4f09abc07d728e9a2d9) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "k2k4s-c6.bin", 0x2000001, 0x800000, CRC(e20d2216) SHA1(5d28eea7b581e780b78f391a8179f1678ee0d9a5) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "k2k4s-c1.bin", 0x3000000, 0x800000, CRC(fa705b2b) SHA1(f314c66876589601806352484dd8e45bc41be692) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "k2k4s-c5.bin", 0x3000001, 0x800000, CRC(2c912ff9) SHA1(b624a625ea3e221808b7ea43fb0b1a51d8c1853e) ) /* Plane 2,3 */
ROM_END

/* Matrimelee bootleg */

ROM_START( matrimbl )
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "266-p1.p1",  0x000000, 0x100000, CRC(5d4c2dc7) SHA1(8d723b0d28ec344eef26009b361a2b97d300dd51) ) /* mask rom TC538200 */
	ROM_LOAD16_WORD_SWAP( "266-p2.sp2", 0x100000, 0x400000, CRC(a14b1906) SHA1(1daa14d73512f760ef569b06f9facb279437d1db) ) /* mask rom TC5332205 */

	ROM_Y_ZOOM

	ROM_REGION( 0x80000, "cslot1:fixed", 0 )
	ROM_FILL( 0x000000, 0x80000, 0x000000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEO_BIOS_AUDIO_128K( "mart-m1.bin", CRC(3ea96ab1) SHA1(e5053c4312f658faed2a34e38325a22ef792d384) )

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "mart-v1.bin", 0x000000, 0x400000, CRC(352b0a07) SHA1(19f7cc12f3f6d0fda9c7449816c4c32367447897) )
	ROM_LOAD16_WORD_SWAP( "mart-v2.bin", 0x400000, 0x400000, CRC(1e9bd59e) SHA1(0f754e780d0ebb815a92a45ad55f85f6d0181b70) )
	ROM_LOAD( "mart-v3.bin", 0x800000, 0x400000, CRC(e8362fcc) SHA1(42d558fd80cabe22a1c09a1fa75741afbcf46b7c) )
	ROM_LOAD16_WORD_SWAP( "mart-v4.bin", 0xc00000, 0x400000, CRC(c8c79b19) SHA1(9c7a5e694d68f37a27209e1400b60b6241a04cc7) )

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "mart-c1.bin", 0x0000000, 0x800000, CRC(a5595656) SHA1(d86281607f22e4f2001047eaeeda99cd673c508c) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "mart-c2.bin", 0x0000001, 0x800000, CRC(c5f7c300) SHA1(9ff5ffb750bd2e925667d84389192f92183e8677) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "mart-c3.bin", 0x1000000, 0x800000, CRC(574efd7d) SHA1(6cac303db705fe2800701ee51de9e9fca04e6e66) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "mart-c4.bin", 0x1000001, 0x800000, CRC(109d54d9) SHA1(22cb748b3b14317b90d9d9951297ada2bfc3a3f1) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "mart-c5.bin", 0x2000000, 0x800000, CRC(15c9e882) SHA1(1c9f1ccaed4fdd9d8f5cc9b6fcaca3c4e328e59e) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "mart-c6.bin", 0x2000001, 0x800000, CRC(77497b97) SHA1(c6481bea5a36f8210971fdcb4bfbe7ed93c769de) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "mart-c7.bin", 0x3000000, 0x800000, CRC(ab481bb6) SHA1(6b2d97c5505eeb28e300b075f37f0d69ef44463a) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "mart-c8.bin", 0x3000001, 0x800000, CRC(906cf267) SHA1(b0f2cf8887794d715f208751ddd1ed26b2c3ffdf) ) /* Plane 2,3 */
ROM_END

/* Metal Slug 5 bootlegs */

ROM_START( ms5plus )
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "ms5-p1p.bin", 0x000000, 0x100000, CRC(106b276f) SHA1(0e840df95f3813145e5043573483c7610d2d3e68) )
	ROM_LOAD16_WORD_SWAP( "ms5-p2p.bin", 0x100000, 0x200000, CRC(d6a458e8) SHA1(c0a8bdae06d62859fb6734766ccc190eb2a809a4) )
	ROM_LOAD16_WORD_SWAP( "ms5-p3p.bin", 0x300000, 0x200000, CRC(439ec031) SHA1(f0ad8f9be7d26bc504593c1321bd23c286a221f0) )

	ROM_Y_ZOOM

	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_LOAD("ms5-s1p.bin", 0x000000, 0x20000, CRC(21e04432) SHA1(10057a2aa487087f7143d1d69fdad978a6bef0f7) )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	/* Encrypted */
	NEO_BIOS_AUDIO_ENCRYPTED_512K( "268-m1.m1", CRC(4a5a6e0e) SHA1(df0f660f2465e1db7be5adfcaf5e88ad61a74a42) ) /* mask rom TC534000 */

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "268-v1c.v1", 0x000000, 0x800000, CRC(ae31d60c) SHA1(c42285cf4e52fea74247860813e826df5aa7600a) ) /* mask rom TC5364205 */
	ROM_LOAD( "268-v2c.v2", 0x800000, 0x800000, CRC(c40613ed) SHA1(af889570304e2867d7dfea1e94e388c06249fb67) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "268-c1c.c1", 0x0000000, 0x800000, CRC(ab7c389a) SHA1(025a188de589500bf7637fa8e7a37ab24bf4312e) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c2c.c2", 0x0000001, 0x800000, CRC(3560881b) SHA1(493d218c92290b4770024d6ee2917c4022753b07) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c3c.c3", 0x1000000, 0x800000, CRC(3af955ea) SHA1(cf36b6ae9b0d12744b17cb7a928399214de894be) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c4c.c4", 0x1000001, 0x800000, CRC(c329c373) SHA1(5073d4079958a0ef5426885af2c9e3178f37d5e0) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c5c.c5", 0x2000000, 0x800000, CRC(959c8177) SHA1(889bda7c65d71172e7d89194d1269561888fe789) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c6c.c6", 0x2000001, 0x800000, CRC(010a831b) SHA1(aec140661e3ae35d264df416478ba15188544d91) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c7c.c7", 0x3000000, 0x800000, CRC(6d72a969) SHA1(968dd9a4d1209b770b9b85ea6532fa24d262a262) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "268-c8c.c8", 0x3000001, 0x800000, CRC(551d720e) SHA1(ebf69e334fcaba0fda6fd432fd0970283a365d12) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

/* clear cart, orange pcbs
   prog board: no v encryption, uses a plcc epm7096lc84-15 for pcm, 16-bit v roms decoded by 2x 74hc245
   cha board: no c/m encryption, uses a soic palce16v8 for zmc, 5x 74hc273a for neo-273, 6x so44 m59pw064 64Mbit + 2x dip lh28f160 16MBit flash roms for gfx
   all roms are erasable flash chips, mixture of 5v and 3.3v
   produced sometime after early 2004 (going by youngest ic date code) */
ROM_START( mslug5b )
	ROM_REGION( 0x500000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "ms5b-p1.bin", 0x000000, 0x100000, CRC(1376f43c) SHA1(7ca4a8b11c7effda2603d04e793cf664e7aa39bf) ) /* MX29F1615PC-10     16Mbit  2nd half empty */
	ROM_LOAD16_WORD_SWAP( "ms5b-p2.bin", 0x100000, 0x400000, CRC(4becfba0) SHA1(fd3708f6c8fa26133b29b4b033148dff54dc1e7d) ) /* LH28F320BJD-TTL80  32Mbit  3.3v */

	/* Scrambled */
	NEO_SFIX_128K( "ms5b-s1.bin", CRC(3a427c9f) SHA1(6c6050640adb7148d42d35e3017cc171e53ae957) ) /* W29C011A-15  1Mbit */

	/* Not encrypted */
	NEO_BIOS_AUDIO_128K( "ms5b-m1.bin", CRC(bf1601bc) SHA1(5e285c98c65acefd77e893247482af0d09f3e1e4) ) /* W29EE011-15  1Mbit */

	/* Not encrypted, odd bytes scrambled */
	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "ms5b-v1.bin", 0x000000, 0x400000, CRC(e3f9fd75) SHA1(8772d0936c45623763b92c55816d0e56dd8d2ef2) ) /* LH28F320BJD-TTL80  32Mbit  3.3v  2 bytes diff vs decrypted */
	ROM_LOAD( "ms5b-v2.bin", 0x400000, 0x400000, CRC(a53618f6) SHA1(002e37f3d45aa6153593c7939902e1a022de14c7) ) /* LH28F320BJD-TTL80  32Mbit  3.3v */
	ROM_LOAD( "ms5b-v3.bin", 0x800000, 0x400000, CRC(14f000ee) SHA1(b30df60964cc8480b78a4bc2d323cad59e44a0ae) ) /* LH28F320BJD-TTL80  32Mbit  3.3v */
	ROM_LOAD( "ms5b-v4.bin", 0xc00000, 0x400000, CRC(0ccee813) SHA1(4bc034f7f37da956b4116a2dea8a856b96e43c18) ) /* LH28F320BJD-TTL80  32Mbit  3.3v */

	/* Not encrypted, a18/a19 lines swapped */
	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "ms5b-c1.bin", 0x0000000, 0x800000, CRC(4b0e5998) SHA1(458486d579db118ec4ba4f9fce9d62fedfef949b) ) /* M59PW064  64Mbit  3.3v */
	ROM_LOAD16_BYTE( "ms5b-c2.bin", 0x0000001, 0x800000, CRC(022fc30b) SHA1(7178900acbb377c3de95338c8fae56e308327cab) ) /* M59PW064  64Mbit  3.3v */
	ROM_LOAD16_BYTE( "ms5b-c3.bin", 0x1000000, 0x800000, CRC(ead86d28) SHA1(e1db4f839972748f49dddfe3bd4b0cf2e0ddf074) ) /* M59PW064  64Mbit  3.3v */
	ROM_LOAD16_BYTE( "ms5b-c4.bin", 0x1000001, 0x800000, CRC(0be6be35) SHA1(34e20e55423cefd2b98c15061f86198b64727173) ) /* M59PW064  64Mbit  3.3v */
	ROM_LOAD16_BYTE( "ms5b-c5.bin", 0x2000000, 0x200000, CRC(2a23e569) SHA1(576370a24a8ef5ca0f8e7afa4ccdb0cb3ad9bdaa) ) /* LH28F160BJD-TTL80  16Mbit  3.3v */
	ROM_LOAD16_BYTE( "ms5b-c6.bin", 0x2000001, 0x200000, CRC(6eb6bc9e) SHA1(4e54d904b0ce34cca429b3c86ab8bf972c66336e) ) /* LH28F160BJD-TTL80  16Mbit  3.3v */
	ROM_LOAD16_BYTE( "ms5b-c7.bin", 0x3000000, 0x800000, CRC(57f4e53f) SHA1(813d98175288045c0750d45afe03c74973d70cee) ) /* M59PW064  64Mbit  3.3v */
	ROM_LOAD16_BYTE( "ms5b-c8.bin", 0x3000001, 0x800000, CRC(9d59ff7c) SHA1(ff90dc79598de0880df17624c76df81c92f267ce) ) /* M59PW064  64Mbit  3.3v */
ROM_END

/* Puzzle Bobble / Bust-A-Move (Neo-Geo) bootleg */

ROM_START( pbobblenb )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "u7", 0x000000, 0x080000, CRC(ac1e9ef3) SHA1(a2b125ee70869667431ab125bc29e768500802ad) )

	NEO_SFIX_128K( "us1", CRC(9caae538) SHA1 (cf2d90a7c1a42107c0bb8b9a61397634286dbe0a) )

	NEO_BIOS_AUDIO_128K( "um1", CRC(f424368a) SHA1 (5e5bbcaeb82bed2ee17df08f005ca20ad1030723) )

	ROM_REGION( 0x380000, "cslot1:ymsnd:adpcma", 0 )
	/* 0x000000-0x1fffff empty */
	ROM_LOAD( "u8", 0x200000, 0x100000, CRC(0840cbc4) SHA1 (1adbd7aef44fa80832f63dfb8efdf69fd7256a57) )
	ROM_LOAD( "u9", 0x300000, 0x080000, CRC(0a548948) SHA1 (e1e4afd17811cb60401c14fbcf0465035165f4fb) )

	ROM_REGION( 0x100000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "uc5", 0x000000, 0x80000, CRC(e89ad494) SHA1 (69c9ea415773af94ac44c48af05d55ada222b138) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "uc6", 0x000001, 0x80000, CRC(4b42d7eb) SHA1 (042ae50a528cea21cf07771d3915c57aa16fd5af) ) /* Plane 2,3 */
ROM_END

/* SNK vs. CAPCOM SVC CHAOS bootlegs */

ROM_START( svcboot )
	ROM_REGION( 0x800000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "svc-p1.bin", 0x000000, 0x800000, CRC(0348f162) SHA1(c313351d68effd92aeb80ed320e4f8c26a3bb53e) )

	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_LOAD( "svc-s1.bin", 0x10000, 0x10000, CRC(70b44df1) SHA1(52ae3f264d7b33e94e770e6b2d0cf35a64e7dda4) )
	ROM_CONTINUE( 0x00000, 0x10000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEOGEO_BIOS

	ROM_REGION( 0x20000, "audiobios", 0 )
	ROM_LOAD( "sm1.sm1", 0x00000, 0x20000, CRC(94416d67) SHA1(42f9d7ddd6c0931fd64226a60dc73602b2819dcf) )

	ROM_REGION( 0x50000, "cslot1:audiocpu", 0 )
	ROM_LOAD( "svc-m1.bin", 0x20000, 0x10000, CRC(804328c3) SHA1(f931636c563b0789d4812033a77b47bf663db43f) )
	ROM_CONTINUE( 0x00000, 0x10000 )
	ROM_COPY( "cslot1:audiocpu", 0x000000, 0x10000, 0x10000 )

	ROM_Y_ZOOM

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD16_WORD_SWAP( "svc-v2.bin", 0x000000, 0x400000, CRC(b5097287) SHA1(3ba3a9b5624879616382ed40337a3d9c50a0f314) )
	ROM_LOAD16_WORD_SWAP( "svc-v1.bin", 0x400000, 0x400000, CRC(bd3a391f) SHA1(972bf09b75e99a683ee965bec93b0da8f15d72d9) )
	ROM_LOAD16_WORD_SWAP( "svc-v4.bin", 0x800000, 0x400000, CRC(33fc0b37) SHA1(d61017d829f44c7df8795ba10c55c727d9972662) )
	ROM_LOAD16_WORD_SWAP( "svc-v3.bin", 0xc00000, 0x400000, CRC(aa9849a0) SHA1(9539b3356a070a066a89f27c287f316e7367ce2a) )

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "svc-c1.bin", 0x0000000, 0x800000, CRC(a7826b89) SHA1(3bbe348ce54b80b56ef032ea532a18ef3cafeb11) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "svc-c2.bin", 0x0000001, 0x800000, CRC(ed3c2089) SHA1(b5d17692f15f5a678c273589fab2e3918711135e) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "svc-c3.bin", 0x1000000, 0x800000, CRC(71ed8063) SHA1(ea1df9e2e382a8560a06d447421844cc588f43dd) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "svc-c4.bin", 0x1000001, 0x800000, CRC(250bde2d) SHA1(8c72dcfceef6d022ab4b73ab37cf3ac0c3940c17) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "svc-c5.bin", 0x2000000, 0x800000, CRC(9817c082) SHA1(1bea9c7220c2b1524896c86841d6d8fd55f5d366) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "svc-c6.bin", 0x2000001, 0x800000, CRC(2bc0307f) SHA1(8090fa82c46eb503832359093c8cc3cee3141c90) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "svc-c7.bin", 0x3000000, 0x800000, CRC(4358d7b9) SHA1(9270b58c2abc072a046bedda72f1395df26d0714) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "svc-c8.bin", 0x3000001, 0x800000, CRC(366deee5) SHA1(d477ad7a5987fd6c7ef2c1680fbb7c884654590e) ) /* Plane 2,3 */
ROM_END

ROM_START( svcplus )
	ROM_REGION( 0x600000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "svc-p1p.bin", 0x000000, 0x200000, CRC(a194d842) SHA1(72b7bfa34a97632b1aa003488e074d766a6c2f08) )
	ROM_LOAD16_WORD_SWAP( "svc-p2p.bin", 0x200000, 0x200000, CRC(50c0e2b7) SHA1(97b396415ab0e692e43ddf371091e5a456712f0a) )
	ROM_LOAD16_WORD_SWAP( "svc-p3p.bin", 0x400000, 0x200000, CRC(58cdc293) SHA1(3c4f2418ec513bcc13ed33a727de11dfb98f7525) )

	NEO_SFIX_128K( "svc-s1p.bin", CRC(73344711) SHA1(04d84c4fe241b9135cd210f8ed8c725f595d11d2) )

	NEOGEO_BIOS

	ROM_REGION( 0x20000, "audiobios", 0 )
	ROM_LOAD( "sm1.sm1", 0x00000, 0x20000, CRC(94416d67) SHA1(42f9d7ddd6c0931fd64226a60dc73602b2819dcf) )

	ROM_REGION( 0x50000, "cslot1:audiocpu", 0 )
	ROM_LOAD( "svc-m1.bin", 0x20000, 0x10000, CRC(804328c3) SHA1(f931636c563b0789d4812033a77b47bf663db43f) )
	ROM_CONTINUE( 0x00000, 0x10000 )
	ROM_COPY( "cslot1:audiocpu", 0x000000, 0x10000, 0x10000 )

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD16_WORD_SWAP( "svc-v2.bin", 0x000000, 0x400000, CRC(b5097287) SHA1(3ba3a9b5624879616382ed40337a3d9c50a0f314) )
	ROM_LOAD16_WORD_SWAP( "svc-v1.bin", 0x400000, 0x400000, CRC(bd3a391f) SHA1(972bf09b75e99a683ee965bec93b0da8f15d72d9) )
	ROM_LOAD16_WORD_SWAP( "svc-v4.bin", 0x800000, 0x400000, CRC(33fc0b37) SHA1(d61017d829f44c7df8795ba10c55c727d9972662) )
	ROM_LOAD16_WORD_SWAP( "svc-v3.bin", 0xc00000, 0x400000, CRC(aa9849a0) SHA1(9539b3356a070a066a89f27c287f316e7367ce2a) )

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "svc-c1.bin", 0x0000000, 0x800000, CRC(a7826b89) SHA1(3bbe348ce54b80b56ef032ea532a18ef3cafeb11) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "svc-c2.bin", 0x0000001, 0x800000, CRC(ed3c2089) SHA1(b5d17692f15f5a678c273589fab2e3918711135e) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "svc-c3.bin", 0x1000000, 0x800000, CRC(71ed8063) SHA1(ea1df9e2e382a8560a06d447421844cc588f43dd) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "svc-c4.bin", 0x1000001, 0x800000, CRC(250bde2d) SHA1(8c72dcfceef6d022ab4b73ab37cf3ac0c3940c17) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "svc-c5.bin", 0x2000000, 0x800000, CRC(9817c082) SHA1(1bea9c7220c2b1524896c86841d6d8fd55f5d366) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "svc-c6.bin", 0x2000001, 0x800000, CRC(2bc0307f) SHA1(8090fa82c46eb503832359093c8cc3cee3141c90) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "svc-c7.bin", 0x3000000, 0x800000, CRC(4358d7b9) SHA1(9270b58c2abc072a046bedda72f1395df26d0714) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "svc-c8.bin", 0x3000001, 0x800000, CRC(366deee5) SHA1(d477ad7a5987fd6c7ef2c1680fbb7c884654590e) ) /* Plane 2,3 */
ROM_END

ROM_START( svcplusa )
	ROM_REGION( 0x600000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "svc-p1pl.bin", 0x000000, 0x200000, CRC(16b44144) SHA1(5eab530274b1b6f480a39a86c199da524cddfccc) )
	ROM_LOAD16_WORD_SWAP( "svc-p2pl.bin", 0x200000, 0x400000, CRC(7231ace2) SHA1(d2f13ddd5d3ee29b4b9824e8663f7ee0241f30cf) )

	ROM_REGION( 0x20000, "cslot1:fixed", 0 )
	ROM_LOAD( "svc-s1pl.bin", 0x10000, 0x10000, CRC(ca3c735e) SHA1(aebd15253c90432a2e0a4c40f37110c1e2176ee4) )
	ROM_CONTINUE( 0x00000, 0x10000 )
	ROM_REGION( 0x20000, "fixedbios", 0 )
	ROM_LOAD( "sfix.sfix", 0x000000, 0x20000, CRC(c2ea0cfd) SHA1(fd4a618cdcdbf849374f0a50dd8efe9dbab706c3) )

	NEOGEO_BIOS

	ROM_REGION( 0x20000, "audiobios", 0 )
	ROM_LOAD( "sm1.sm1", 0x00000, 0x20000, CRC(94416d67) SHA1(42f9d7ddd6c0931fd64226a60dc73602b2819dcf) )

	ROM_REGION( 0x50000, "cslot1:audiocpu", 0 )
	ROM_LOAD( "svc-m1.bin", 0x20000, 0x10000, CRC(804328c3) SHA1(f931636c563b0789d4812033a77b47bf663db43f) )
	ROM_CONTINUE( 0x00000, 0x10000 )
	ROM_COPY( "cslot1:audiocpu", 0x000000, 0x10000, 0x10000 )

	ROM_Y_ZOOM

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD16_WORD_SWAP( "svc-v2.bin", 0x000000, 0x400000, CRC(b5097287) SHA1(3ba3a9b5624879616382ed40337a3d9c50a0f314) )
	ROM_LOAD16_WORD_SWAP( "svc-v1.bin", 0x400000, 0x400000, CRC(bd3a391f) SHA1(972bf09b75e99a683ee965bec93b0da8f15d72d9) )
	ROM_LOAD16_WORD_SWAP( "svc-v4.bin", 0x800000, 0x400000, CRC(33fc0b37) SHA1(d61017d829f44c7df8795ba10c55c727d9972662) )
	ROM_LOAD16_WORD_SWAP( "svc-v3.bin", 0xc00000, 0x400000, CRC(aa9849a0) SHA1(9539b3356a070a066a89f27c287f316e7367ce2a) )

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "svc-c1.bin", 0x0000000, 0x800000, CRC(a7826b89) SHA1(3bbe348ce54b80b56ef032ea532a18ef3cafeb11) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "svc-c2.bin", 0x0000001, 0x800000, CRC(ed3c2089) SHA1(b5d17692f15f5a678c273589fab2e3918711135e) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "svc-c3.bin", 0x1000000, 0x800000, CRC(71ed8063) SHA1(ea1df9e2e382a8560a06d447421844cc588f43dd) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "svc-c4.bin", 0x1000001, 0x800000, CRC(250bde2d) SHA1(8c72dcfceef6d022ab4b73ab37cf3ac0c3940c17) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "svc-c5.bin", 0x2000000, 0x800000, CRC(9817c082) SHA1(1bea9c7220c2b1524896c86841d6d8fd55f5d366) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "svc-c6.bin", 0x2000001, 0x800000, CRC(2bc0307f) SHA1(8090fa82c46eb503832359093c8cc3cee3141c90) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "svc-c7.bin", 0x3000000, 0x800000, CRC(4358d7b9) SHA1(9270b58c2abc072a046bedda72f1395df26d0714) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "svc-c8.bin", 0x3000001, 0x800000, CRC(366deee5) SHA1(d477ad7a5987fd6c7ef2c1680fbb7c884654590e) ) /* Plane 2,3 */
ROM_END

ROM_START( svcsplus )
	ROM_REGION( 0x800000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "svc-p1sp.bin", 0x000000, 0x400000, CRC(2601902f) SHA1(202348a13c6480f7de37a3ee983823838822fc98) )
	ROM_LOAD16_WORD_SWAP( "svc-p2sp.bin", 0x400000, 0x400000, CRC(0ca13305) SHA1(ac8fbca71b754acbcdd11802161a62ae1cf32d88) )

	NEO_SFIX_128K( "svc-s1sp.bin", CRC(233d6439) SHA1(369024c7a2405c3144c14ac016c07c3dc0f44187) )

	NEOGEO_BIOS

	ROM_REGION( 0x20000, "audiobios", 0 )
	ROM_LOAD( "sm1.sm1", 0x00000, 0x20000, CRC(94416d67) SHA1(42f9d7ddd6c0931fd64226a60dc73602b2819dcf) )

	ROM_REGION( 0x50000, "cslot1:audiocpu", 0 )
	ROM_LOAD( "svc-m1.bin", 0x20000, 0x10000, CRC(804328c3) SHA1(f931636c563b0789d4812033a77b47bf663db43f) )
	ROM_CONTINUE( 0x00000, 0x10000 )
	ROM_COPY( "cslot1:audiocpu", 0x000000, 0x10000, 0x10000 )

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD16_WORD_SWAP( "svc-v2.bin", 0x000000, 0x400000, CRC(b5097287) SHA1(3ba3a9b5624879616382ed40337a3d9c50a0f314) )
	ROM_LOAD16_WORD_SWAP( "svc-v1.bin", 0x400000, 0x400000, CRC(bd3a391f) SHA1(972bf09b75e99a683ee965bec93b0da8f15d72d9) )
	ROM_LOAD16_WORD_SWAP( "svc-v4.bin", 0x800000, 0x400000, CRC(33fc0b37) SHA1(d61017d829f44c7df8795ba10c55c727d9972662) )
	ROM_LOAD16_WORD_SWAP( "svc-v3.bin", 0xc00000, 0x400000, CRC(aa9849a0) SHA1(9539b3356a070a066a89f27c287f316e7367ce2a) )

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "svc-c1.bin", 0x0000000, 0x800000, CRC(a7826b89) SHA1(3bbe348ce54b80b56ef032ea532a18ef3cafeb11) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "svc-c2.bin", 0x0000001, 0x800000, CRC(ed3c2089) SHA1(b5d17692f15f5a678c273589fab2e3918711135e) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "svc-c3.bin", 0x1000000, 0x800000, CRC(71ed8063) SHA1(ea1df9e2e382a8560a06d447421844cc588f43dd) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "svc-c4.bin", 0x1000001, 0x800000, CRC(250bde2d) SHA1(8c72dcfceef6d022ab4b73ab37cf3ac0c3940c17) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "svc-c5.bin", 0x2000000, 0x800000, CRC(9817c082) SHA1(1bea9c7220c2b1524896c86841d6d8fd55f5d366) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "svc-c6.bin", 0x2000001, 0x800000, CRC(2bc0307f) SHA1(8090fa82c46eb503832359093c8cc3cee3141c90) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "svc-c7.bin", 0x3000000, 0x800000, CRC(4358d7b9) SHA1(9270b58c2abc072a046bedda72f1395df26d0714) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "svc-c8.bin", 0x3000001, 0x800000, CRC(366deee5) SHA1(d477ad7a5987fd6c7ef2c1680fbb7c884654590e) ) /* Plane 2,3 */
ROM_END

/* Samurai Shodown 5 bootleg */

ROM_START( samsho5b )
	ROM_REGION( 0x800000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "ssv-p2.bin", 0x000000, 0x400000, CRC(5023067f) SHA1(b1d682fa7d158f19664356a919da6572e8cfeee0) )
	ROM_LOAD16_WORD_SWAP( "ssv-p1.bin", 0x400000, 0x400000, CRC(b6cbe386) SHA1(99c2407361116c2b2c5fe72df53e05c5f99163c1) )

	NEO_SFIX_128K( "ssv-s1.bin", CRC(70f667d0) SHA1(6d7ce62bb77eb215cc22d6c3c677accfd740aa83) )

	NEO_BIOS_AUDIO_128K( "ssv-m1.bin", CRC(18114fb1) SHA1(016dc2f328340f3637a9bff373a20973df29f6b8) )

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "ssv-v1.bin", 0x000000, 0x400000, CRC(a3609761) SHA1(6dce1dbfd228c739b3716ae1cf08fd7f925d8650) )
	ROM_LOAD( "ssv-v2.bin", 0x400000, 0x400000, CRC(cbd6ebd5) SHA1(00211be3fa32035b0947ac65920ea8acae7bfae2) )
	ROM_LOAD( "ssv-v3.bin", 0x800000, 0x400000, CRC(6f1c2703) SHA1(8015df3d788cb7926ebbcda64a96964fe102ba27) )
	ROM_LOAD( "ssv-v4.bin", 0xc00000, 0x400000, CRC(5020c055) SHA1(bd1e68d1b0a47b0e2b365159e210048f8b22823a) )

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "ssv-c1.bin", 0x0000000, 0x1000000, CRC(9c564a01) SHA1(99dc8900fd8f56ae04fff72b34ddcaa8abe4c1be) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "ssv-c2.bin", 0x0000001, 0x1000000, CRC(4b73b8da) SHA1(a8b626de74cf57bbd8c222e8e24c953c9e8680f4) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "ssv-c3.bin", 0x2000000, 0x1000000, CRC(029f9bb5) SHA1(6296c879aa0bbd22383ceeeac0326805cbc8b4ec) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "ssv-c4.bin", 0x2000001, 0x1000000, CRC(75722430) SHA1(30594c30a167e75463670249df7744755e39e75b) ) /* Plane 2,3 */
ROM_END

/* The King of Fighters 2003 bootlegs */

ROM_START( kf2k3bl )
	ROM_REGION( 0x800000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "2k3-p1.bin", 0x100000, 0x400000, CRC(92ed6ee3) SHA1(5e7e21eb40dfcc453ba73808760d5ddedd49c58a) )
	ROM_LOAD16_WORD_SWAP( "2k3-p2.bin", 0x500000, 0x200000, CRC(5d3d8bb3) SHA1(7f2341f14ca12ff5721eb038b3496228a1f34b60) )
	ROM_CONTINUE( 0x000000, 0x100000 )
	ROM_CONTINUE( 0x000000, 0x100000 )

	NEO_SFIX_128K( "2k3-s1.bin", CRC(482c48a5) SHA1(27e2f5295a9a838e112be28dafc111893a388a16) )

	NEO_BIOS_AUDIO_128K( "2k3-m1.bin", CRC(3a4969ff) SHA1(2fc107a023a82053a8df63025829bcf12cee9610) )

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "271-v1c.v1", 0x000000, 0x800000, CRC(ffa3f8c7) SHA1(7cf4a933973ca23b7f87c81151d8659e6ec4bd20) ) /* mask rom TC5364205 */
	ROM_LOAD( "271-v2c.v2", 0x800000, 0x800000, CRC(5382c7d1) SHA1(1bf999705eda80ba1e7b0d6bdd010d9bfb18bd76) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "271-c1c.c1", 0x0000000, 0x800000, CRC(b1dc25d0) SHA1(50adc3c60d5b4b3abd10a49db2267306c6dbd772) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c2c.c2", 0x0000001, 0x800000, CRC(d5362437) SHA1(66db36522dc09106388c707252df9fe1c88b4856) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c3c.c3", 0x1000000, 0x800000, CRC(0a1fbeab) SHA1(9fe30d36ba98d00fda010832ff2f27783dd577c1) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c4c.c4", 0x1000001, 0x800000, CRC(87b19a0c) SHA1(b72a8e7d9124ce859b5149bb4381ba481c161ea5) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c5c.c5", 0x2000000, 0x800000, CRC(704ea371) SHA1(e75b80422f0d72eac826f8ffadf79efeccaab124) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c6c.c6", 0x2000001, 0x800000, CRC(20a1164c) SHA1(c9843b37612a16fc95f6851793b1cfb5d49d811d) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c7c.c7", 0x3000000, 0x800000, CRC(189aba7f) SHA1(7152195a57ad36b28290810fe87ed8c206262ba9) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c8c.c8", 0x3000001, 0x800000, CRC(20ec4fdc) SHA1(deb5f7ec5a090e419b9d1a6a74877bee081198e2) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

ROM_START( kf2k3bla )
	ROM_REGION( 0x700000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "2k3-p1bl.bin", 0x000000, 0x100000, CRC(4ea414dd) SHA1(c242c9709c20a8cde3ad562adbe640a5dd5abcf1) )
	ROM_LOAD16_WORD_SWAP( "2k3-p3bl.bin", 0x100000, 0x400000, CRC(370acbff) SHA1(e72544de1c5e2e4f7478fc003caba9e33a306c19) )
	ROM_LOAD16_WORD_SWAP( "2k3-p2bl.bin", 0x500000, 0x200000, CRC(9c04fc52) SHA1(f41b53c79e4209373ec68276fa5941c91424bb15) )

	NEO_SFIX_128K( "2k3-s1.bin", CRC(482c48a5) SHA1(27e2f5295a9a838e112be28dafc111893a388a16) )

	NEO_BIOS_AUDIO_128K( "2k3-m1.bin", CRC(3a4969ff) SHA1(2fc107a023a82053a8df63025829bcf12cee9610) )

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "271-v1c.v1", 0x000000, 0x800000, CRC(ffa3f8c7) SHA1(7cf4a933973ca23b7f87c81151d8659e6ec4bd20) ) /* mask rom TC5364205 */
	ROM_LOAD( "271-v2c.v2", 0x800000, 0x800000, CRC(5382c7d1) SHA1(1bf999705eda80ba1e7b0d6bdd010d9bfb18bd76) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "271-c1c.c1", 0x0000000, 0x800000, CRC(b1dc25d0) SHA1(50adc3c60d5b4b3abd10a49db2267306c6dbd772) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c2c.c2", 0x0000001, 0x800000, CRC(d5362437) SHA1(66db36522dc09106388c707252df9fe1c88b4856) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c3c.c3", 0x1000000, 0x800000, CRC(0a1fbeab) SHA1(9fe30d36ba98d00fda010832ff2f27783dd577c1) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c4c.c4", 0x1000001, 0x800000, CRC(87b19a0c) SHA1(b72a8e7d9124ce859b5149bb4381ba481c161ea5) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c5c.c5", 0x2000000, 0x800000, CRC(704ea371) SHA1(e75b80422f0d72eac826f8ffadf79efeccaab124) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c6c.c6", 0x2000001, 0x800000, CRC(20a1164c) SHA1(c9843b37612a16fc95f6851793b1cfb5d49d811d) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c7c.c7", 0x3000000, 0x800000, CRC(189aba7f) SHA1(7152195a57ad36b28290810fe87ed8c206262ba9) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c8c.c8", 0x3000001, 0x800000, CRC(20ec4fdc) SHA1(deb5f7ec5a090e419b9d1a6a74877bee081198e2) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

ROM_START( kf2k3pl )
	ROM_REGION( 0x700000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "2k3-p1pl.bin", 0x000000, 0x100000, CRC(07b84112) SHA1(0b085a928a39ff9c0745a58bfa4ce6106b5f474a) )
	ROM_LOAD16_WORD_SWAP( "2k3-p3bl.bin", 0x100000, 0x400000, CRC(370acbff) SHA1(e72544de1c5e2e4f7478fc003caba9e33a306c19) )
	ROM_LOAD16_WORD_SWAP( "2k3-p2bl.bin", 0x500000, 0x200000, CRC(9c04fc52) SHA1(f41b53c79e4209373ec68276fa5941c91424bb15) )

	NEO_SFIX_128K( "2k3-s1pl.bin", CRC(ad548a36) SHA1(7483dbe2d74a1bd1b4dc501e99e48a683416d08e) )

	NEO_BIOS_AUDIO_128K( "2k3-m1.bin", CRC(3a4969ff) SHA1(2fc107a023a82053a8df63025829bcf12cee9610) )

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "271-v1c.v1", 0x000000, 0x800000, CRC(ffa3f8c7) SHA1(7cf4a933973ca23b7f87c81151d8659e6ec4bd20) ) /* mask rom TC5364205 */
	ROM_LOAD( "271-v2c.v2", 0x800000, 0x800000, CRC(5382c7d1) SHA1(1bf999705eda80ba1e7b0d6bdd010d9bfb18bd76) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "271-c1c.c1", 0x0000000, 0x800000, CRC(b1dc25d0) SHA1(50adc3c60d5b4b3abd10a49db2267306c6dbd772) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c2c.c2", 0x0000001, 0x800000, CRC(d5362437) SHA1(66db36522dc09106388c707252df9fe1c88b4856) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c3c.c3", 0x1000000, 0x800000, CRC(0a1fbeab) SHA1(9fe30d36ba98d00fda010832ff2f27783dd577c1) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c4c.c4", 0x1000001, 0x800000, CRC(87b19a0c) SHA1(b72a8e7d9124ce859b5149bb4381ba481c161ea5) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c5c.c5", 0x2000000, 0x800000, CRC(704ea371) SHA1(e75b80422f0d72eac826f8ffadf79efeccaab124) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c6c.c6", 0x2000001, 0x800000, CRC(20a1164c) SHA1(c9843b37612a16fc95f6851793b1cfb5d49d811d) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c7c.c7", 0x3000000, 0x800000, CRC(189aba7f) SHA1(7152195a57ad36b28290810fe87ed8c206262ba9) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c8c.c8", 0x3000001, 0x800000, CRC(20ec4fdc) SHA1(deb5f7ec5a090e419b9d1a6a74877bee081198e2) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END

ROM_START( kf2k3upl )
	ROM_REGION( 0x800000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "2k3-p1up.bin", 0x000000, 0x800000, CRC(87294c01) SHA1(21420415a6b2ba1b43ecc1934270dc085d6bd7d9) )

	NEO_SFIX_128K( "2k3-s1up.bin", CRC(e5708c0c) SHA1(5649446d3b0b1bd138b5a8b40b96a6d0f892f4d8) )

	NEO_BIOS_AUDIO_128K( "2k3-m1.bin", CRC(3a4969ff) SHA1(2fc107a023a82053a8df63025829bcf12cee9610) )

	ROM_REGION( 0x1000000, "cslot1:ymsnd:adpcma", 0 )
	/* Encrypted */
	ROM_LOAD( "271-v1c.v1", 0x000000, 0x800000, CRC(ffa3f8c7) SHA1(7cf4a933973ca23b7f87c81151d8659e6ec4bd20) ) /* mask rom TC5364205 */
	ROM_LOAD( "271-v2c.v2", 0x800000, 0x800000, CRC(5382c7d1) SHA1(1bf999705eda80ba1e7b0d6bdd010d9bfb18bd76) ) /* mask rom TC5364205 */

	ROM_REGION( 0x4000000, "cslot1:sprites", 0 )
	/* Encrypted */
	ROM_LOAD16_BYTE( "271-c1c.c1", 0x0000000, 0x800000, CRC(b1dc25d0) SHA1(50adc3c60d5b4b3abd10a49db2267306c6dbd772) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c2c.c2", 0x0000001, 0x800000, CRC(d5362437) SHA1(66db36522dc09106388c707252df9fe1c88b4856) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c3c.c3", 0x1000000, 0x800000, CRC(0a1fbeab) SHA1(9fe30d36ba98d00fda010832ff2f27783dd577c1) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c4c.c4", 0x1000001, 0x800000, CRC(87b19a0c) SHA1(b72a8e7d9124ce859b5149bb4381ba481c161ea5) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c5c.c5", 0x2000000, 0x800000, CRC(704ea371) SHA1(e75b80422f0d72eac826f8ffadf79efeccaab124) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c6c.c6", 0x2000001, 0x800000, CRC(20a1164c) SHA1(c9843b37612a16fc95f6851793b1cfb5d49d811d) ) /* Plane 2,3 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c7c.c7", 0x3000000, 0x800000, CRC(189aba7f) SHA1(7152195a57ad36b28290810fe87ed8c206262ba9) ) /* Plane 0,1 */ /* mask rom TC5364205 */
	ROM_LOAD16_BYTE( "271-c8c.c8", 0x3000001, 0x800000, CRC(20ec4fdc) SHA1(deb5f7ec5a090e419b9d1a6a74877bee081198e2) ) /* Plane 2,3 */ /* mask rom TC5364205 */
ROM_END


	/* Unlicensed Prototypes */

ROM_START( diggerma ) /* Unlicensed Prototype, no official game ID # */
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "dig-p1.bin", 0x000000, 0x080000, CRC(eda433d7) SHA1(abb14c66777ab0fe4ac76a402e253a49df7178d8) )

	NEO_SFIX_128K( "dig-s1.bin", CRC(9b3168f0) SHA1(9be8c625686a1482f7399e5a856cfe2fef25ec52) )

	NEO_BIOS_AUDIO_128K( "dig-m1.bin", CRC(e777a234) SHA1(9f3974ac07859337bc0203f903c40ae3f60dc1fb) )

	ROM_REGION( 0x200000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "dig-v1.bin", 0x000000, 0x080000, CRC(ee15bda4) SHA1(fe2206728e6efd02d6302869a98b196eb19a17df) )

	ROM_REGION( 0x100000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "dig-c1.bin", 0x000000, 0x080000, CRC(3db0a4ed) SHA1(6214faa883d97ea05809b6af7e0c85a236a18a28) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "dig-c2.bin", 0x000001, 0x080000, CRC(3e632161) SHA1(83711c4286fb1d9f3f91414ac6e5fed36618033e) ) /* Plane 2,3 */
ROM_END

// MVS cart
ROM_START( lasthope )
	ROM_REGION( 0x100000, "cslot1:maincpu", ROMREGION_BE|ROMREGION_16BIT )
	ROM_LOAD16_WORD_SWAP( "ngdt-300-p1.bin", 0x000000, 0x100000, CRC(3776a88f) SHA1(ea8b669da06d7c6b5ff7fa97a195f56a9253a7a1) )

	NEO_SFIX_64K( "ngdt-300-s1.bin", CRC(0c0ff9e6) SHA1(c87d1ea8731ac1e63ab960b8182dd1043bcc10bb) )

	NEO_BIOS_AUDIO_128K( "ngdt-300-m1.bin", CRC(113c870f) SHA1(854425eb4be0d7fa088a6c3bf6078fdd011707f5) )

	ROM_REGION( 0x600000, "cslot1:ymsnd:adpcma", 0 )
	ROM_LOAD( "ngdt-300-v1.bin", 0x000000, 0x200000, CRC(b765bafe) SHA1(b2048c44089bf250c8dcfabb27c7981e9ee5002a) )
	ROM_LOAD( "ngdt-300-v2.bin", 0x200000, 0x200000, CRC(9fd0d559) SHA1(09e70d5e1c6e172a33f48feb3e442515c34a8f3d) )
	ROM_LOAD( "ngdt-300-v3.bin", 0x400000, 0x200000, CRC(6d5107e2) SHA1(4ba74836e3d0421a28af47d3d8341ac16af1d7d7) )

	ROM_REGION( 0x1000000, "cslot1:sprites", 0 )
	ROM_LOAD16_BYTE( "ngdt-300-c1.bin", 0x000000, 0x400000, CRC(53ef41b5) SHA1(a8f1fe546403b609e12f0df211c05d7ac479d98d) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "ngdt-300-c2.bin", 0x000001, 0x400000, CRC(f9b15ab3) SHA1(d8ff2f43686bfc8c2f7ead3ef445e51c15dfbf16) ) /* Plane 2,3 */
	ROM_LOAD16_BYTE( "ngdt-300-c3.bin", 0x800000, 0x400000, CRC(50cc21cf) SHA1(0350aaef480c5fa12e68e540a4c974dbf5870add) ) /* Plane 0,1 */
	ROM_LOAD16_BYTE( "ngdt-300-c4.bin", 0x800001, 0x400000, CRC(8486ad9e) SHA1(19a2a73c825687e0cb9fd62bde00db91b5409529)) /* Plane 2,3 */
ROM_END


/*************************************
 *
 *  Title catalog
 *  (source: http://neogeomuseum.snkplaymore.co.jp/english/catalogue/index.php)
 *
 *************************************

    In 2010, SNK Playmore, the successor of SNK, released a title catalogue which lists the released
    games (MVS/AES/CD) including their release dates in Japan. It is not 100% complete.
    The included title catalogue is the english one.

    Game Title                                                  Genre           Publisher       Date Released (in Japan)
    =================================================================================================================================
    NAM-1975                                                    3D Action       SNK             MVS Cartridge:1990/04/26
                                                                                                NEOGEO ROM-cart:1991/07/01
                                                                                                NEOGEO CD:1994/09/09
    MAHJONG KYORETSUDEN                                         Mahjong         SNK             MVS Cartridge:1990/04/26
                                                                                                NEOGEO ROM-cart:1991/07/01
                                                                                                NEOGEO CD:1994/09/09
    MAGICIAN LORD                                               Action          ADK             MVS Cartridge:1990/04/26
                                                                                                NEOGEO ROM-cart:1991/07/01
                                                                                                NEOGEO CD:1994/10/31
    BASEBALL STARS PROFESSIONAL                                 Sports          SNK             MVS Cartridge:1990/04/26
                                                                                                NEOGEO ROM-cart:1991/07/01
                                                                                                NEOGEO CD:1995/04/21
    TOP PLAYER'S GOLF                                           Sports          SNK             MVS Cartridge:1990/05/23
                                                                                                NEOGEO ROM-cart:1991/07/01
                                                                                                NEOGEO CD:1994/09/09
    NINJA COMBAT                                                Action          ADK             MVS Cartridge:1990/07/24
                                                                                                NEOGEO ROM-cart:1991/07/01
                                                                                                NEOGEO CD:1994/10/31
    RIDING HERO                                                 3D Racing       SNK             MVS Cartridge:1990/07/24
                                                                                                NEOGEO ROM-cart:1991/07/01
                                                                                                NEOGEO CD:1995/05/26
    THE SUPER SPY                                               3D Action       SNK             MVS Cartridge:1990/10/08
                                                                                                NEOGEO ROM-cart:1991/07/01
                                                                                                NEOGEO CD:1994/09/09
    CYBER-LIP                                                   Action          SNK             MVS Cartridge:1990/11/07
                                                                                                NEOGEO ROM-cart:1991/07/01
                                                                                                NEOGEO CD:1995/04/21
    PUZZLED                                                     Puzzle          SNK             MVS Cartridge:1990/11/20
                                                                                                NEOGEO ROM-cart:1991/07/01
                                                                                                NEOGEO CD:1994/09/09
    LEAGUE BOWLING                                              Sports          SNK             MVS Cartridge:1990/12/10
                                                                                                NEOGEO ROM-cart:1991/07/01
                                                                                                NEOGEO CD:1994/09/09
    GHOST PILOTS                                                Shooter         SNK             MVS Cartridge:1991/01/25
                                                                                                NEOGEO ROM-cart:1991/07/01
                                                                                                NEOGEO CD:1995/03/17
    SENGOKU                                                     Action          SNK             MVS Cartridge:1991/02/12
                                                                                                NEOGEO ROM-cart:1991/07/01
                                                                                                NEOGEO CD:1995/03/17
    KING OF THE MONSTERS                                        Fighting        SNK             MVS Cartridge:1991/02/25
                                                                                                NEOGEO ROM-cart:1991/07/01
    BLUE'S JOURNEY                                              Action          ADK             MVS Cartridge:1991/03/14
                                                                                                NEOGEO ROM-cart:1991/07/01
                                                                                                NEOGEO CD:1994/10/31
    ALPHA MISSION II                                            Shooter         SNK             MVS Cartridge:1991/03/25
                                                                                                NEOGEO ROM-cart:1991/07/01
                                                                                                NEOGEO CD:1994/09/09
    BURNING FIGHT                                               Action          SNK             MVS Cartridge:1991/05/20
                                                                                                NEOGEO ROM-cart:1991/08/09
                                                                                                NEOGEO CD:1994/09/09
    MINNASAN NO OKAGESAMA DESU                                  Table           Monolith        MVS Cartridge:1991/07/25
                                                                                                NEOGEO ROM-cart:1991/07/21
    CROSSED SWORDS                                              Action          ADK             MVS Cartridge:1991/07/25
                                                                                                NEOGEO ROM-cart:1991/10/01
                                                                                                NEOGEO CD:1994/10/31
    LEGEND OF SUCCESS JOE                                       Action          Wave            MVS Cartridge:1991/07
                                                                                                NEOGEO ROM-cart:1991/08/30
    QUIZ DAISUSA SEN: THE LAST COUNT DOWN                       Quiz            SNK             MVS Cartridge:1991/07
                                                                                                NEOGEO ROM-cart:1991/08/30
    SUPER BASEBALL 2020                                         Sports          SNK             MVS Cartridge:1991/09/20
                                                                                                NEOGEO ROM-cart:1991/10/25
                                                                                                NEOGEO CD:1995/02/25
    ROBO ARMY                                                   Action          SNK             MVS Cartridge:1991/10/30
                                                                                                NEOGEO ROM-cart:1991/12/20
                                                                                                NEOGEO CD:1995/04/21
    THRASH RALLY                                                Racing          ADK             MVS Cartridge:1991/11/08
                                                                                                NEOGEO ROM-cart:1991/12/20
                                                                                                NEOGEO CD:1994/10/31
    EIGHT MAN                                                   Action          SNK             NEOGEO ROM-cart:1991/11/20
    FATAL FURY                                                  Fighting        SNK             MVS Cartridge:1991/11/25
                                                                                                NEOGEO ROM-cart:1991/12/20
                                                                                                NEOGEO CD:1994/09/09
    BAKATONO-SAMA MAHJONG MAN'YUKI                              Mahjong         Monolith        MVS Cartridge:1991/11
                                                                                                NEOGEO ROM-cart:1991/12/13
    THRASH RALLY                                                Racing          ADK             NEOGEO ROM-cart:1991/12/20
    FOOTBALL FRENZY                                             Sports          SNK             MVS Cartridge:1992/01/31
                                                                                                NEOGEO ROM-cart:1992/02/21
                                                                                                NEOGEO CD:1994/09/09
    SOCCER BRAWL                                                Sports          SNK             MVS Cartridge:1992/02/14
                                                                                                NEOGEO ROM-cart:1992/03/13
                                                                                                NEOGEO CD:1995/03/31
    MUTATION NATION                                             Action          SNK             MVS Cartridge:1992/03/16
                                                                                                NEOGEO ROM-cart:1992/04/17
                                                                                                NEOGEO CD:1995/02/25
    LAST RESORT                                                 Shooter         SNK             MVS Cartridge:1992/03/23
                                                                                                NEOGEO ROM-cart:1992/04/24
                                                                                                NEOGEO CD:1994/09/09
    QUIZ MEITANTEI NEO & GEO: QUIZ DAISOUSASEN PART 2           Quiz            SNK             MVS Cartridge:1992/03
                                                                                                NEOGEO ROM-cart:1991/04/24
    BASEBALL STARS 2                                            Sports          SNK             MVS Cartridge:1992/04/15
                                                                                                NEOGEO ROM-cart:1992/04/28
                                                                                                NEOGEO CD:1994/09/09
    NINJA COMMANDO                                              Shooter         ADK             MVS Cartridge:1992/04/30
                                                                                                NEOGEO ROM-cart:1992/05/29
                                                                                                NEOGEO CD:1994/10/31
    KING OF THE MONSTERS 2                                      Fighting        SNK             MVS Cartridge:1992/05/25
                                                                                                NEOGEO ROM-cart:1992/06/19
                                                                                                NEOGEO CD:1994/09/09
    ANDRO DUNOS                                                 Shooter         Visco           MVS Cartridge:1992/06/15
                                                                                                NEOGEO ROM-cart:1992/07/17
    WORLD HEROES                                                Fighting        ADK             MVS Cartridge:1992/07/28
                                                                                                NEOGEO ROM-cart:1992/09/11
                                                                                                NEOGEO CD:1995/03/17
    ART OF FIGHTING                                             Fighting        SNK             MVS Cartridge:1992/09/24
                                                                                                NEOGEO ROM-cart:1992/12/11
                                                                                                NEOGEO CD:1994/09/09
    VIEWPOINT                                                   Shooter         Sammy           MVS Cartridge:1992/11/20
                                                                                                NEOGEO ROM-cart:1992/12/11
                                                                                                NEOGEO CD:1995/02/25
    FATAL FURY 2                                                Fighting        SNK             MVS Cartridge:1992/12/10
                                                                                                NEOGEO ROM-cart:1993/03/05
                                                                                                NEOGEO CD:1994/09/09
    SUPER SIDEKICKS                                             Sports          SNK             MVS Cartridge:1992/12/14
                                                                                                NEOGEO ROM-cart:1993/02/19
                                                                                                NEOGEO CD:1995/03/31
    SENGOKU 2                                                   Action          SNK             MVS Cartridge:1993/02/18
                                                                                                NEOGEO ROM-cart:1993/04/09
                                                                                                NEOGEO CD:1995/03/17
    3 COUNT BOUT                                                Fighting        SNK             MVS Cartridge:1993/03/25
                                                                                                NEOGEO ROM-cart:1993/04/23
                                                                                                NEOGEO CD:1995/04/21
    WORLD HEROES 2                                              Fighting        ADK             MVS Cartridge:1993/04/26
                                                                                                NEOGEO ROM-cart:1993/06/04
                                                                                                NEOGEO CD:1995/04/14
    SAMURAI SHODOWN                                             Fighting        SNK             MVS Cartridge:1993/07/07
                                                                                                NEOGEO ROM-cart:1993/08/11
                                                                                                NEOGEO CD:1994/09/09
    FATAL FURY SPECIAL                                          Fighting        SNK             MVS Cartridge:1993/09/16
                                                                                                NEOGEO ROM-cart:1993/12/22
                                                                                                NEOGEO CD:1994/09/09
    SPINMASTER                                                  Sideview Action Data East       MVS Cartridge:1993/12/16
                                                                                                NEOGEO ROM-cart:1994/02/18
    ART OF FIGHTING 2                                           Fighting        SNK             MVS Cartridge:1994/02/03
                                                                                                NEOGEO ROM-cart:1994/03/11
                                                                                                NEOGEO CD:1994/09/09
    WINDJAMMERS                                                 Sports          Data East       MVS Cartridge:1994/02/17
                                                                                                NEOGEO ROM-cart:1994/04/08
                                                                                                NEOGEO CD:1995/01/20
    KARNOV'S REVENGE                                            Fighting        Data East       MVS Cartridge:1994/03/17
                                                                                                NEOGEO ROM-cart:1994/04/28
                                                                                                NEOGEO CD:1994/12/22
    SUPER SIDEKICKS 2                                           Sports          SNK             MVS Cartridge:1994/04/19
                                                                                                NEOGEO ROM-cart:1994/05/27
                                                                                                NEOGEO CD:1994/09/09
    WORLD HEROES 2 JET                                          Fighting        ADK             MVS Cartridge:1994/04/26
                                                                                                NEOGEO ROM-cart:1994/06/10
                                                                                                NEOGEO CD:1994/11/11
    TOP HUNTER                                                  Action          SNK             MVS Cartridge:1994/05/18
                                                                                                NEOGEO ROM-cart:1994/06/24
                                                                                                NEOGEO CD:1994/09/29
    GURURIN                                                     Puzzle          Face            MVS Cartridge:1994/05/25
    FIGHT FEVER                                                 Fighting        VICCOM          MVS Cartridge:1994/06/28
    JANSHIN DENSETSU: QUEST OF JONGMASTER                       Mahjong         Aicom           MVS Cartridge:1994/06/29
                                                                                                NEOGEO CD:1995/03/31
    AERO FIGHTERS 2                                             Topview Shooter Video System    MVS Cartridge:1994/07/18
                                                                                                NEOGEO ROM-cart:1994/08/26
                                                                                                NEOGEO CD:1994/09/29
    AGGRESSORS OF DARK KOMBAT                                   Fighting        ADK             MVS Cartridge:1994/07/26
                                                                                                NEOGEO ROM-cart:1994/08/26
                                                                                                NEOGEO CD:1995/01/13
    THE KING OF FIGHTERS '94                                    Fighting        SNK             MVS Cartridge:1994/08/25
                                                                                                NEOGEO ROM-cart:1994/10/01
                                                                                                NEOGEO CD:1994/11/02
    ZED BLADE                                                   Shooter         NMK             MVS Cartridge:1994/09/13
    POWER SPIKES II                                             Sports          Video System    MVS Cartridge:1994/10/19
                                                                                                NEOGEO CD:1995/03/18
    SAMURAI SHODOWN II                                          Fighting        SNK             MVS Cartridge:1994/10/28
                                                                                                NEOGEO ROM-cart:1994/12/02
                                                                                                NEOGEO CD:1994/12/15
    STREET HOOP                                                 Sports          Data East       MVS Cartridge:1994/12/08
                                                                                                NEOGEO ROM-cart:1994/12/09
                                                                                                NEOGEO CD:1995/01/20
    PUZZLE BOBBLE                                               Puzzle          TAITO           MVS Cartridge:1994/12/21
                                                                                                NEOGEO CD:1995/05/02
    SUPER VOLLEY '94                                            Sports          TAITO           MVS Cartridge:1994
    BOMBERMAN: PANIC BOMBER                                     Puzzle          Eighting        MVS Cartridge:1995/01/18
    GALAXY FIGHT: UNIVERSAL WARRIORS                            Fighting        Sunsoft         MVS Cartridge:1995/01/24
                                                                                                NEOGEO ROM-cart:1995/02/25
                                                                                                NEOGEO CD:1995/04/21
    QUIZ KING OF FIGHTERS                                       Quiz            Saurus          MVS Cartridge:1995/02/01
                                                                                                NEOGEO ROM-cart:1995/03/10
                                                                                                NEOGEO CD:1995/04/07
    DOUBLE DRAGON                                               Fighting        Technos         MVS Cartridge:1995/03/03
                                                                                                NEOGEO ROM-cart:1995/03/31
                                                                                                NEOGEO CD:1995/06/02
    SUPER SIDEKICKS 3                                           Sports          SNK             MVS Cartridge:1995/03/07
                                                                                                NEOGEO ROM-cart:1995/04/07
                                                                                                NEOGEO CD:1995/06/23
    FATAL FURY 3                                                Fighting        SNK             MVS Cartridge:1995/03/27
                                                                                                NEOGEO ROM-cart:1995/04/21
                                                                                                NEOGEO CD:1995/04/28
    SAVAGE REIGN                                                Fighting        SNK             MVS Cartridge:1995/04/25
                                                                                                NEOGEO ROM-cart:1995/03/10
                                                                                                NEOGEO CD:1995/06/16
    CROSSED SWORDS II                                           Action          ADK             NEOGEO CD:1995/05/02
    WORLD HEROES PERFECT                                        Fighting        ADK             MVS Cartridge:1995/05/25
                                                                                                NEOGEO ROM-cart:1995/06/30
                                                                                                NEOGEO CD:1995/07/21
    FAR EAST OF EDEN: KABUKI KLASH                              Fighting        Hudson Soft     MVS Cartridge:1995/06/20
                                                                                                NEOGEO ROM-cart:1995/07/28
                                                                                                NEOGEO CD:1995/11/24
    THE KING OF FIGHTERS '95                                    Fighting        SNK             MVS Cartridge:1995/07/25
                                                                                                NEOGEO ROM-cart:1995/09/01
                                                                                                NEOGEO CD:1995/09/29
    IDOL MAHJONG FINAL ROMANCE 2                                Mahjong         Video System    NEOGEO CD:1995/08/25
    PULSTAR                                                     Sidevi. Shooter Aicom           MVS Cartridge:1995/08/28
                                                                                                NEOGEO ROM-cart:1995/09/29
                                                                                                NEOGEO CD:1995/10/27
    VOLTAGE FIGHTER GOWCAIZER                                   Fighting        Technos         MVS Cartridge:1995/09/18
                                                                                                NEOGEO ROM-cart:1995/10/20
                                                                                                NEOGEO CD:1995/11/24
    STAKES WINNER                                               Action          Saurus          MVS Cartridge:1995/09/27
                                                                                                NEOGEO ROM-cart:1995/10/27
                                                                                                NEOGEO CD:1996/03/22
    SHOGI NO TATSUJIN - MASTER OF SHOUGI                        Japanese chess  ADK             MVS Cartridge:1995/09/28
                                                                                                NEOGEO ROM-cart:1995/10/13
                                                                                                NEOGEO CD:1995/10/20
    AERO FIGHTERS 3                                             Topview Action  Video System    MVS Cartridge:1995/10/12
                                                                                                NEOGEO ROM-cart:1995/11/17
                                                                                                NEOGEO CD:1995/12/08
    ADK WORLD                                                   Variety         ADK             NEOGEO CD:1995/11/10
    SAMURAI SHODOWN III                                         Fighting        SNK             MVS Cartridge:1995/11/15
                                                                                                NEOGEO ROM-cart:1995/12/01
                                                                                                NEOGEO CD:1995/12/29
    CHIBI MARUKO-CHAN DELUXE QUIZ                               Variety         Takara          MVS Cartridge:1995/11/27
                                                                                                NEOGEO ROM-cart:1996/01/26
    PUZZLE DE PON!                                              Puzzle          Visco           MVS Cartridge:1995/11/28
    REAL BOUT FATAL FURY                                        Fighting        SNK             MVS Cartridge:1995/12/21
                                                                                                NEOGEO ROM-cart:1996/01/26
                                                                                                NEOGEO CD:1996/02/23
    NEO-GEO CD SPECIAL                                          Variety         SNK             NEOGEO CD:1995/12/22
    NEO TURF MASTERS                                            Sports          Nazca           MVS Cartridge:1996/01/29
                                                                                                NEOGEO ROM-cart:1996/03/01
                                                                                                NEOGEO CD:1996/05/03
    ART OF FIGHTING 3                                           Fighting        SNK             MVS Cartridge:1996/03/12
                                                                                                NEOGEO ROM-cart:1996/04/26
                                                                                                NEOGEO CD:1996/06/14
    MAGICAL DROP II                                             Puzzle          Data East       MVS Cartridge:1996/03/21
                                                                                                NEOGEO ROM-cart:1996/04/19
                                                                                                NEOGEO CD:1996/05/24
    OSHIDASHI JIN TRICK                                         Puzzle          ADK             NEOGEO CD:1996/03/22
    NEO DRIFT OUT                                               Racing          Visco           MVS Cartridge:1996/03/28
                                                                                                NEOGEO CD:1996/07/26
    METAL SLUG                                                  Action          Nazca           MVS Cartridge:1996/04/19
                                                                                                NEOGEO ROM-cart:1996/05/24
                                                                                                NEOGEO CD:1996/07/05
    OVER TOP                                                    Racing          ADK             MVS Cartridge:1996/04/26
                                                                                                NEOGEO ROM-cart:1996/06/07
                                                                                                NEOGEO CD:1996/07/26
    NINJA MASTER'S                                              Fighting        ADK             MVS Cartridge:1996/05/27
                                                                                                NEOGEO ROM-cart:1996/06/28
                                                                                                NEOGEO CD:1996/09/27
    RAGNAGARD                                                   Fighting        Saurus          MVS Cartridge:1996/06/13
                                                                                                NEOGEO ROM-cart:1996/07/26
                                                                                                NEOGEO CD:1996/08/23
    FUTSAL                                                      Sports          Saurus          NEOGEO CD:1996/07/19
    THE KING OF FIGHTERS '96                                    Fighting        SNK             MVS Cartridge:1996/07/30
                                                                                                NEOGEO ROM-cart:1996/09/27
                                                                                                NEOGEO CD:1996/10/25
    KIZUNA ENCOUNTER SUPER TAG BATTLE                           Fighting        SNK             MVS Cartridge:1996/09/20
                                                                                                NEOGEO ROM-cart:1996/11/08
    CHOUTETSU BURIKINGA                                         Shooter         Saurus          NEOGEO CD:1996/09/20
    STAKES WINNER 2                                             Real Jockey Act Saurus          MVS Cartridge:1996/09/24
                                                                                                NEOGEO ROM-cart:1996/12/13
    THE ULTIMATE 11                                             Sports          SNK             MVS Cartridge:1996/10/16
                                                                                                NEOGEO ROM-cart:1996/12/20
    SAMURAI SHODOWN IV                                          Fighting        SNK             MVS Cartridge:1996/10/25
                                                                                                NEOGEO ROM-cart:1996/11/29
                                                                                                NEOGEO CD:1996/12/27
    WAKU WAKU 7                                                 Fighting        Sunsoft         MVS Cartridge:1996/11/21
                                                                                                NEOGEO ROM-cart:1996/12/27
    TWINKLE STAR SPRITES                                        Shooter         ADK             MVS Cartridge:1996/11/25
                                                                                                NEOGEO ROM-cart:1997/01/31
                                                                                                NEOGEO CD:1997/02/21
    BREAKERS                                                    Fighting        Visco           MVS Cartridge:1996/12/17
                                                                                                NEOGEO ROM-cart:1997/03/21
                                                                                                NEOGEO CD:1997/04/25
    MONEY IDOL EXCHANGER                                        Puzzle          Face            MVS Cartridge:1997/01/15
    Real Bout FATAL FURY SPECIAL                                Fighting        SNK             MVS Cartridge:1997/01/28
                                                                                                NEOGEO ROM-cart:1997/02/28
                                                                                                NEOGEO CD:1997/03/03
    THE KING OF FIGHTERS '96 NEOGEO COLLECTION                  Variety         SNK             NEOGEO CD:1997/02/14
    MAGICAL DROP III                                            Puzzle          Data East       MVS Cartridge:1997/02/25
                                                                                                NEOGEO ROM-cart:1997/04/25
    NEO BOMBERMAN                                               Action          Hudson Soft     MVS Cartridge:1997/05/01
    NEO MR.DO!                                                  Action          Visco           MVS Cartridge:1997/06/26
    SHINSETSU SAMURAI SHODOWN BUSHIDO RETSUDEN                  Role-playing    SNK             NEOGEO CD:1997/06/27
    THE KING OF FIGHTERS '97                                    Fighting        SNK             MVS Cartridge:1997/07/28
                                                                                                NEOGEO ROM-cart:1997/09/25
                                                                                                NEOGEO CD:1997/10/30
    UCCHAN NANCHAN NO HONO NO CHALLENGER ULTRA DENRYU IRAIRABOU Action          Saurus          MVS Cartridge:1997/08/25
    SHOCK TROOPERS                                              Shooter         Saurus          MVS Cartridge:1997/11/11
    THE LAST BLADE                                              Fighting        SNK             MVS Cartridge:1997/12/05
                                                                                                NEOGEO ROM-cart:1998/01/29
                                                                                                NEOGEO CD:1998/03/26
    BLAZING STAR                                                Shooter         Yumekobo        MVS Cartridge:1998/01/19
                                                                                                NEOGEO ROM-cart:1998/02/26
    METAL SLUG 2                                                Action          SNK             MVS Cartridge:1998/02/23
                                                                                                NEOGEO ROM-cart:1998/04/02
                                                                                                NEOGEO CD:1998/06/25
    REAL BOUT FATAL FURY 2                                      Fighting        SNK             MVS Cartridge:1998/03/20
                                                                                                NEOGEO ROM-cart:1998/04/29
                                                                                                NEOGEO CD:1998/07/23
    NEOGEO CUP '98                                              Sports          SNK             MVS Cartridge:1998/05/28
                                                                                                NEOGEO ROM-cart:1998/07/30
    BREAKERS REVENGE                                            Fighting        Visco           MVS Cartridge:1998/07/03
                                                                                                NEOGEO ROM-cart:
    THE KING OF FIGHTERS '98                                    Fighting        SNK             MVS Cartridge:1998/07/23
                                                                                                NEOGEO ROM-cart:1998/09/23
                                                                                                NEOGEO CD:1998/12/23
    SHOCK TROOPERS 2nd Squad                                    Action Shooter  Saurus          MVS Cartridge:1998/11/06
                                                                                                NEOGEO ROM-cart:1999/06/24
    THE LAST BLADE 2                                            Fighting        SNK             MVS Cartridge:1998/11/25
                                                                                                NEOGEO ROM-cart:1999/01/28
                                                                                                NEOGEO CD:1999/02/27
    FLIP SHOT                                                   Action          Visco           MVS Cartridge:1998/12/08
    METAL SLUG X                                                Action          SNK             MVS Cartridge:1999/03/19
                                                                                                NEOGEO ROM-cart:1999/05/27
    CAPTAIN TOMADAY                                             Shooter         Visco           MVS Cartridge:1999/05/27
    THE KING OF FIGHTERS '99                                    Fighting        SNK             MVS Cartridge:1999/07/22
                                                                                                NEOGEO ROM-cart:1999/09/23
                                                                                                NEOGEO CD:1999/12/02
    PREHISTORIC ISLE 2                                          Shooter         Yumekobo        MVS Cartridge:1999/09/27
    GAROU: MARK OF THE WOLVES                                   Fighting        SNK             MVS Cartridge:1999/11/26
                                                                                                NEOGEO ROM-cart:2000/02/25
    STRIKERS 1945 PLUS                                          Shooter         Psikyo          MVS Cartridge:1999/12/24
    METAL SLUG 3                                                Action Shooter  SNK             MVS Cartridge:2000/03/23
                                                                                                NEOGEO ROM-cart:2000/06/01
    THE KING OF FIGHTERS 2000                                   Fighting        SNK             MVS Cartridge:2000/07/26
                                                                                                NEOGEO ROM-cart:2000/12/21
    NIGHTMARE IN THE DARK                                       Horror Action   Gavaking        MVS Cartridge:2001
    ZUPAPA!                                                     Comical Action  Video System    MVS Cartridge:2001
    SENGOKU 3                                                   Action          SNK PLAYMORE    MVS Cartridge:2001/07/18
                                                                                                NEOGEO ROM-cart:2001/10/25
    THE KING OF FIGHTERS 2001                                   Fighting        SNK PLAYMORE    MVS Cartridge:2001/11/15
                                                                                                NEOGEO ROM-cart:2002/03/14
    METAL SLUG 4                                                Action Shooter  SNK PLAYMORE    MVS Cartridge:2002/03/27
                                                                                                NEOGEO ROM-cart:2002/06/13
    RAGE OF THE DRAGONS                                         Fighting        Evoga           MVS Cartridge:2002/06/06
                                                                                                NEOGEO ROM-cart:2002/09/26
    THE KING OF FIGHTERS 2002                                   Fighting        SNK PLAYMORE    MVS Cartridge:2002/10/10
                                                                                                NEOGEO ROM-cart:2002/12/19
    POWER INSTINCT MATRIMELEE                                   Fighting        ATLUS/NOISE FA. MVS Cartridge:2003/03/20
                                                                                                NEOGEO ROM-cart:2003/05/29
    SNK VS. CAPCOM: SVC CHAOS                                   Fighting        SNK PLAYMORE    MV-0:2003/07/24
                                                                                                NEOGEO ROM-cart:2003/11/13
    SAMURAI SHODOWN V                                           Fighting        SNK P/Yuki Ent  MVS Cartridge:2003/10/10
                                                                                                NEOGEO ROM-cart:2003/12/11
    METAL SLUG 5                                                Action Shooter  SNK PLAYMORE    MV-0:2003/11/14
                                                                                                NEOGEO ROM-cart:2004/02/19
    THE KING OF FIGHTERS 2003                                   Fighting        SNK PLAYMORE    MV-0:2003/12/12
                                                                                                NEOGEO ROM-cart:2004/03/18
    POCHI & NYAA                                                Puzzle          Aiky            MVS Cartridge:2003/12/24
    SAMURAI SHODOWN V SPECIAL                                   Fighting        SNK P/Yuki Ent  MVS Cartridge:2004/04/22
                                                                                                NEOGEO ROM-cart:2004/07/15
****************************************************************************/

//    YEAR  NAME        PARENT    MACHINE    INPUT      STATE          INIT        MONITOR
// SNK
GAME( 1990, nam1975,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "NAM-1975 (NGM-001 ~ NGH-001)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, bstars,     neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Baseball Stars Professional (NGM-002)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, bstarsh,    bstars,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Baseball Stars Professional (NGH-002)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, tpgolf,     neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Top Player's Golf (NGM-003 ~ NGH-003)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, mahretsu,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Mahjong Kyo Retsuden (NGM-004 ~ NGH-004)", MACHINE_SUPPORTS_SAVE ) // does not support mahjong panel in MVS mode <- it actually works fine???
GAME( 1990, ridhero,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Riding Hero (NGM-006 ~ NGH-006)", MACHINE_NODEVICE_LAN | MACHINE_SUPPORTS_SAVE )
GAME( 1990, ridheroh,   ridhero,  neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Riding Hero (set 2)", MACHINE_NODEVICE_LAN | MACHINE_SUPPORTS_SAVE )
GAME( 1991, alpham2,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Alpha Mission II / ASO II - Last Guardian (NGM-007 ~ NGH-007)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, alpham2p,   alpham2,  neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Alpha Mission II / ASO II - Last Guardian (prototype)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, cyberlip,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Cyber-Lip (NGM-010)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, superspy,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The Super Spy (NGM-011 ~ NGH-011)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, mutnat,     neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Mutation Nation (NGM-014 ~ NGH-014)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, kotm,       neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "King of the Monsters (set 1)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, kotmh,      kotm,     neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "King of the Monsters (set 2)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, sengoku,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Sengoku / Sengoku Denshou (NGM-017 ~ NGH-017)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, sengokuh,   sengoku,  neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Sengoku / Sengoku Denshou (NGH-017, US)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, burningf,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Burning Fight (NGM-018 ~ NGH-018)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, burningfh,  burningf, neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Burning Fight (NGH-018, US)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, burningfpa, burningf, neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Burning Fight (prototype, near final, ver 23.3, 910326)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, burningfpb, burningf, neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Burning Fight (prototype, newer, V07)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, burningfp,  burningf, neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Burning Fight (prototype, older)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, lbowling,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "League Bowling (NGM-019 ~ NGH-019)", MACHINE_NODEVICE_LAN | MACHINE_SUPPORTS_SAVE )
GAME( 1991, gpilots,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Ghost Pilots (NGM-020 ~ NGH-020)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, gpilotsh,   gpilots,  neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Ghost Pilots (NGH-020, US)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, gpilotsp,   gpilots,  neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Ghost Pilots (prototype)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, joyjoy,     neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Puzzled / Joy Joy Kid (NGM-021 ~ NGH-021)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, quizdais,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Quiz Daisousa Sen - The Last Count Down (NGM-023 ~ NGH-023)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, quizdaisk,  quizdais, neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK / Viccom", "Quiz Salibtamjeong - The Last Count Down (Korean localized Quiz Daisousa Sen)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, lresort,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Last Resort", MACHINE_SUPPORTS_SAVE )
GAME( 1992, lresortp,   lresort,  neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Last Resort (prototype)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, eightman,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK / Pallas", "Eight Man (NGM-025 ~ NGH-025)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, legendos,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK / Wave", "Legend of Success Joe / Ashita no Joe Densetsu", MACHINE_SUPPORTS_SAVE )
GAME( 1991, 2020bb,     neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK / Pallas", "2020 Super Baseball (set 1)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, 2020bba,    2020bb,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK / Pallas", "2020 Super Baseball (set 2)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, 2020bbh,    2020bb,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK / Pallas", "2020 Super Baseball (set 3)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, socbrawl,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Soccer Brawl (NGM-031)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, socbrawlh,  socbrawl, neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Soccer Brawl (NGH-031)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, fatfury1,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Fatal Fury - King of Fighters / Garou Densetsu - Shukumei no Tatakai (NGM-033 ~ NGH-033)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, roboarmy,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Robo Army", MACHINE_SUPPORTS_SAVE )
GAME( 1991, roboarmya,  roboarmy, neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Robo Army (NGM-032 ~ NGH-032)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, fbfrenzy,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Football Frenzy (NGM-034 ~ NGH-034)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, kotm2,      neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "King of the Monsters 2 - The Next Thing (NGM-039 ~ NGH-039)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, kotm2a,     kotm2,    neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "King of the Monsters 2 - The Next Thing (older)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, kotm2p,     kotm2,    neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "King of the Monsters 2 - The Next Thing (prototype)", MACHINE_SUPPORTS_SAVE )
GAME( 1993, sengoku2,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Sengoku 2 / Sengoku Denshou 2", MACHINE_SUPPORTS_SAVE )
GAME( 1992, bstars2,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Baseball Stars 2", MACHINE_SUPPORTS_SAVE )
GAME( 1992, quizdai2,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Quiz Meitantei Neo & Geo - Quiz Daisousa Sen part 2 (NGM-042 ~ NGH-042)", MACHINE_SUPPORTS_SAVE )
GAME( 1993, 3countb,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "3 Count Bout / Fire Suplex (NGM-043 ~ NGH-043)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, aof,        neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Art of Fighting / Ryuuko no Ken (NGM-044 ~ NGH-044)", MACHINE_SUPPORTS_SAVE )
GAME( 1993, samsho,     neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Samurai Shodown / Samurai Spirits (NGM-045)", MACHINE_SUPPORTS_SAVE )
GAME( 1993, samshoh,    samsho,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Samurai Shodown / Samurai Spirits (NGH-045)", MACHINE_SUPPORTS_SAVE )
GAME( 1994, tophuntr,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Top Hunter - Roddy & Cathy (NGM-046)", MACHINE_SUPPORTS_SAVE )
GAME( 1994, tophuntrh,  tophuntr, neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Top Hunter - Roddy & Cathy (NGH-046)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, fatfury2,   neogeo,   fatfur2,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Fatal Fury 2 / Garou Densetsu 2 - Arata-naru Tatakai (NGM-047 ~ NGH-047)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, ssideki,    neogeo,   fatfur2,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Super Sidekicks / Tokuten Ou", MACHINE_SUPPORTS_SAVE )
GAME( 1994, kof94,      neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '94 (NGM-055 ~ NGH-055)", MACHINE_SUPPORTS_SAVE )
GAME( 1994, aof2,       neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Art of Fighting 2 / Ryuuko no Ken 2 (NGM-056)", MACHINE_SUPPORTS_SAVE )
GAME( 1994, aof2a,      aof2,     neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Art of Fighting 2 / Ryuuko no Ken 2 (NGH-056)", MACHINE_SUPPORTS_SAVE )
GAME( 1993, fatfursp,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Fatal Fury Special / Garou Densetsu Special (NGM-058 ~ NGH-058, set 1)", MACHINE_SUPPORTS_SAVE )
GAME( 1993, fatfurspa,  fatfursp, neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Fatal Fury Special / Garou Densetsu Special (NGM-058 ~ NGH-058, set 2)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, savagere,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Savage Reign / Fu'un Mokushiroku - Kakutou Sousei", MACHINE_SUPPORTS_SAVE )
GAME( 1994, ssideki2,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Super Sidekicks 2 - The World Championship / Tokuten Ou 2 - Real Fight Football (NGM-061 ~ NGH-061)", MACHINE_SUPPORTS_SAVE )
GAME( 1994, samsho2,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Samurai Shodown II / Shin Samurai Spirits - Haohmaru Jigokuhen (NGM-063 ~ NGH-063)", MACHINE_SUPPORTS_SAVE )
GAME( 1994, samsho2k,   samsho2,  neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Saulabi Spirits / Jin Saulabi Tu Hon (Korean release of Samurai Shodown II, set 1)", MACHINE_SUPPORTS_SAVE )
GAME( 1994, samsho2ka,  samsho2,  neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Saulabi Spirits / Jin Saulabi Tu Hon (Korean release of Samurai Shodown II, set 2)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, fatfury3,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Fatal Fury 3 - Road to the Final Victory / Garou Densetsu 3 - Haruka-naru Tatakai (NGM-069 ~ NGH-069)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, ssideki3,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Super Sidekicks 3 - The Next Glory / Tokuten Ou 3 - Eikou e no Chousen", MACHINE_SUPPORTS_SAVE )
GAME( 1995, kof95,      neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '95 (NGM-084)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, kof95a,     kof95,    neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '95 (NGM-084, alt board)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, kof95h,     kof95,    neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '95 (NGH-084)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, samsho3,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Samurai Shodown III / Samurai Spirits - Zankurou Musouken (NGM-087)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, samsho3h,   samsho3,  neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Samurai Shodown III / Samurai Spirits - Zankurou Musouken (NGH-087)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, fswords,    samsho3,  neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Fighters Swords (Korean release of Samurai Shodown III)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, rbff1,      neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Real Bout Fatal Fury / Real Bout Garou Densetsu (NGM-095 ~ NGH-095)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, rbff1a,     rbff1,    neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Real Bout Fatal Fury / Real Bout Garou Densetsu (bug fix revision)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, rbff1k,     rbff1,    neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Real Bout Fatal Fury / Real Bout Garou Densetsu (Korean release)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, rbff1ka,    rbff1,    neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Real Bout Fatal Fury / Real Bout Garou Densetsu (Korean release, bug fix revision)", MACHINE_SUPPORTS_SAVE )
GAME( 1996, aof3,       neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Art of Fighting 3 - The Path of the Warrior / Art of Fighting - Ryuuko no Ken Gaiden", MACHINE_SUPPORTS_SAVE )
GAME( 1996, aof3k,      aof3,     neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Art of Fighting 3 - The Path of the Warrior (Korean release)", MACHINE_SUPPORTS_SAVE ) // no Japanese title / mode
GAME( 1996, kof96,      neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '96 (NGM-214)", MACHINE_SUPPORTS_SAVE )
GAME( 1996, kof96a,     kof96,    neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '96 (bug fix revision)", MACHINE_SUPPORTS_SAVE )
GAME( 1996, kof96h,     kof96,    neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '96 (NGH-214)", MACHINE_SUPPORTS_SAVE )
GAME( 1996, ssideki4,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The Ultimate 11 - The SNK Football Championship / Tokuten Ou - Honoo no Libero", MACHINE_SUPPORTS_SAVE )
GAME( 1996, kizuna,     neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Kizuna Encounter - Super Tag Battle / Fu'un Super Tag Battle", MACHINE_SUPPORTS_SAVE )
GAME( 1996, kizuna4p,   kizuna,   kizuna4p,  kizuna4p,  mvs_state,     empty_init, ROT0, "SNK", "Kizuna Encounter - Super Tag Battle 4 Way Battle Version / Fu'un Super Tag Battle Special Version", MACHINE_SUPPORTS_SAVE )
GAME( 1996, samsho4,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Samurai Shodown IV - Amakusa's Revenge / Samurai Spirits - Amakusa Kourin (NGM-222 ~ NGH-222)", MACHINE_SUPPORTS_SAVE )
GAME( 1996, samsho4k,   samsho4,  neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Paewang Jeonseol / Legend of a Warrior (Korean censored Samurai Shodown IV)", MACHINE_SUPPORTS_SAVE )
GAME( 1996, rbffspec,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Real Bout Fatal Fury Special / Real Bout Garou Densetsu Special", MACHINE_SUPPORTS_SAVE )
GAME( 1996, rbffspeck,  rbffspec, neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Real Bout Fatal Fury Special / Real Bout Garou Densetsu Special (Korean release)", MACHINE_SUPPORTS_SAVE )
GAME( 1997, kof97,      neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '97 (NGM-2320)", MACHINE_SUPPORTS_SAVE )
GAME( 1997, kof97h,     kof97,    neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '97 (NGH-2320)", MACHINE_SUPPORTS_SAVE )
GAME( 1997, kof97k,     kof97,    neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '97 (Korean release)", MACHINE_SUPPORTS_SAVE )
GAME( 1997, kof97pls,   kof97,    neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "The King of Fighters '97 Plus (bootleg)", MACHINE_SUPPORTS_SAVE )
GAME( 1997, kof97oro,   kof97,    kof97oro,  neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "The King of Fighters '97 Chongchu Jianghu Plus 2003 (bootleg)", MACHINE_SUPPORTS_SAVE )
GAME( 1997, kog,        kof97,    kog,       neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "King of Gladiator (bootleg of The King of Fighters '97)", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) // protected bootleg
GAME( 1997, lastblad,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The Last Blade / Bakumatsu Roman - Gekka no Kenshi (NGM-2340)", MACHINE_SUPPORTS_SAVE )
GAME( 1997, lastbladh,  lastblad, neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The Last Blade / Bakumatsu Roman - Gekka no Kenshi (NGH-2340)", MACHINE_SUPPORTS_SAVE )
GAME( 1997, lastsold,   lastblad, neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The Last Soldier (Korean release of The Last Blade)", MACHINE_SUPPORTS_SAVE )
GAME( 1997, irrmaze,    neogeo,   irrmaze,   neogeo,    mvs_state,     empty_init, ROT0, "SNK / Saurus", "The Irritating Maze / Ultra Denryu Iraira Bou", MACHINE_SUPPORTS_SAVE )
GAME( 1998, rbff2,      neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Real Bout Fatal Fury 2 - The Newcomers / Real Bout Garou Densetsu 2 - The Newcomers (NGM-2400)", MACHINE_SUPPORTS_SAVE )
GAME( 1998, rbff2h,     rbff2,    neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Real Bout Fatal Fury 2 - The Newcomers / Real Bout Garou Densetsu 2 - The Newcomers (NGH-2400)", MACHINE_SUPPORTS_SAVE )
GAME( 1998, rbff2k,     rbff2,    neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Real Bout Fatal Fury 2 - The Newcomers (Korean release)", MACHINE_SUPPORTS_SAVE ) // no Japanese title / mode
GAME( 1998, mslug2,     neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Metal Slug 2 - Super Vehicle-001/II (NGM-2410 ~ NGH-2410)", MACHINE_SUPPORTS_SAVE )
GAME( 2015, mslug2t,    mslug2,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "hack (trap15)", "Metal Slug 2 Turbo (NGM-9410) (hack)", MACHINE_SUPPORTS_SAVE )
GAME( 1998, kof98,      neogeo,   kof98,     neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '98 - The Slugfest / King of Fighters '98 - Dream Match Never Ends (NGM-2420)", MACHINE_SUPPORTS_SAVE )
GAME( 1998, kof98a,     kof98,    kof98,     neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '98 - The Slugfest / King of Fighters '98 - Dream Match Never Ends (NGM-2420, alt board)", MACHINE_SUPPORTS_SAVE )
GAME( 1998, kof98k,     kof98,    kof98,     neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '98 - The Slugfest / King of Fighters '98 - Dream Match Never Ends (Korean board, set 1)", MACHINE_SUPPORTS_SAVE )
GAME( 1998, kof98ka,    kof98,    kof98,     neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '98 - The Slugfest / King of Fighters '98 - Dream Match Never Ends (Korean board, set 2)", MACHINE_SUPPORTS_SAVE )
GAME( 1998, kof98h,     kof98,    neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '98 - The Slugfest / King of Fighters '98 - Dream Match Never Ends (NGH-2420)", MACHINE_SUPPORTS_SAVE )
GAME( 1998, lastbld2,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The Last Blade 2 / Bakumatsu Roman - Dai Ni Maku Gekka no Kenshi (NGM-2430 ~ NGH-2430)", MACHINE_SUPPORTS_SAVE )
GAME( 1998, neocup98,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Neo-Geo Cup '98 - The Road to the Victory", MACHINE_SUPPORTS_SAVE )
GAME( 1999, mslugx,     neogeo,   mslugx,    neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Metal Slug X - Super Vehicle-001 (NGM-2500 ~ NGH-2500)", MACHINE_SUPPORTS_SAVE )
GAME( 1999, kof99,      neogeo,   kof99,     neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '99 - Millennium Battle (NGM-2510)", MACHINE_SUPPORTS_SAVE ) /* Encrypted Code & GFX */
GAME( 1999, kof99h,     kof99,    kof99,     neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '99 - Millennium Battle (NGH-2510)", MACHINE_SUPPORTS_SAVE ) /* Encrypted Code & GFX, crashes going into attract demo */
GAME( 1999, kof99e,     kof99,    kof99,     neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '99 - Millennium Battle (earlier)", MACHINE_SUPPORTS_SAVE ) /* Encrypted Code & GFX */
GAME( 1999, kof99k,     kof99,    kof99,     neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '99 - Millennium Battle (Korean release)", MACHINE_SUPPORTS_SAVE ) /* Encrypted Code & GFX */
GAME( 1999, kof99ka,    kof99,    kof99k,    neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '99 - Millennium Battle (Korean release, non-encrypted program)", MACHINE_SUPPORTS_SAVE )   /* Encrypted GFX */
GAME( 1999, kof99p,     kof99,    neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters '99 - Millennium Battle (prototype)", MACHINE_SUPPORTS_SAVE )
GAME( 1999, garou,      neogeo,   garou,     neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Garou - Mark of the Wolves (NGM-2530)", MACHINE_SUPPORTS_SAVE ) /* Encrypted Code & GFX */
GAME( 1999, garouh,     garou,    garouh,    neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Garou - Mark of the Wolves (NGM-2530 ~ NGH-2530)", MACHINE_SUPPORTS_SAVE ) /* Encrypted Code & GFX */
GAME( 1999, garouha,    garou,    garou,     neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Garou - Mark of the Wolves (NGH-2530)", MACHINE_SUPPORTS_SAVE ) /* Encrypted Code & GFX */
GAME( 1999, garoup,     garou,    neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Garou - Mark of the Wolves (prototype)", MACHINE_SUPPORTS_SAVE )
GAME( 1999, garoubl,    garou,    garoubl,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "Garou - Mark of the Wolves (bootleg)", MACHINE_SUPPORTS_SAVE ) /* Bootleg of garoup */
GAME( 2000, mslug3,     neogeo,   mslug3,    neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Metal Slug 3 (NGM-2560)", MACHINE_SUPPORTS_SAVE ) /* Encrypted Code & GFX */
GAME( 2000, mslug3a,    mslug3,   mslug3a,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Metal Slug 3 (NGM-2560, earlier)", MACHINE_SUPPORTS_SAVE ) /* Encrypted Code & GFX - revision Mar/17/2000 1:36 (from SMA rom) */
GAME( 2000, mslug3h,    mslug3,   mslug3h,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Metal Slug 3 (NGH-2560)", MACHINE_SUPPORTS_SAVE ) /* Encrypted GFX */
GAME( 2000, mslug3b6,   mslug3,   mslug3b6,  neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "Metal Slug 6 (bootleg of Metal Slug 3)", MACHINE_SUPPORTS_SAVE ) /* real Metal Slug 6 is an Atomiswave HW game, see naomi.cpp ;-) */
GAME( 2000, kof2000,    neogeo,   kof2000,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters 2000 (NGM-2570 ~ NGH-2570)", MACHINE_SUPPORTS_SAVE ) /* Encrypted Code & GFX */
GAME( 2000, kof2000n,   kof2000,  kof2000n,  neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "The King of Fighters 2000 (not encrypted)", MACHINE_SUPPORTS_SAVE ) /* Encrypted GFX */
GAME( 2001, zupapa,     neogeo,   zupapa,    neogeo,    mvs_led_state, empty_init, ROT0, "SNK", "Zupapa!", MACHINE_SUPPORTS_SAVE ) /* Encrypted GFX */
GAME( 2001, sengoku3,   neogeo,   sengoku3,  neogeo,    mvs_led_state, empty_init, ROT0, "Noise Factory / SNK", "Sengoku 3 / Sengoku Densho 2001 (set 1)", MACHINE_SUPPORTS_SAVE ) /* Encrypted GFX */
GAME( 2001, sengoku3a,  sengoku3, sengoku3,  neogeo,    mvs_led_state, empty_init, ROT0, "Noise Factory / SNK", "Sengoku 3 / Sengoku Densho 2001 (set 2)", MACHINE_SUPPORTS_SAVE ) /* Encrypted GFX */
GAME( 2001, kof2001,    neogeo,   kof2001,   neogeo,    mvs_led_state, empty_init, ROT0, "Eolith / SNK", "The King of Fighters 2001 (NGM-262?)", MACHINE_SUPPORTS_SAVE ) /* Encrypted GFX */
GAME( 2001, kof2001h,   kof2001,  kof2001,   neogeo,    mvs_led_state, empty_init, ROT0, "Eolith / SNK", "The King of Fighters 2001 (NGH-2621)", MACHINE_SUPPORTS_SAVE ) /* Encrypted GFX */
GAME( 2003, cthd2003,   kof2001,  cthd2k3,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg (Phenixsoft)", "Crouching Tiger Hidden Dragon 2003 (hack of The King of Fighters 2001)", MACHINE_SUPPORTS_SAVE ) /* Protected Hack / Bootleg of kof2001 */
GAME( 2003, ct2k3sp,    kof2001,  ct2k3sp,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg (Phenixsoft)", "Crouching Tiger Hidden Dragon 2003 Super Plus (hack of The King of Fighters 2001)", MACHINE_SUPPORTS_SAVE ) /* Protected Hack / Bootleg of kof2001 */
GAME( 2003, ct2k3sa,    kof2001,  ct2k3sa,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg (Phenixsoft)", "Crouching Tiger Hidden Dragon 2003 Super Plus (hack of The King of Fighters 2001, alternate)", MACHINE_SUPPORTS_SAVE ) /* Hack / Bootleg of kof2001 */
GAME( 2002, kof2002,    neogeo,   kof2002,   neogeo,    mvs_led_state, empty_init, ROT0, "Eolith / Playmore", "The King of Fighters 2002 (NGM-2650 ~ NGH-2650)", MACHINE_SUPPORTS_SAVE ) /* Encrypted GFX */
GAME( 2002, kof2002b,   kof2002,  kof2002b,  neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "The King of Fighters 2002 (bootleg)", MACHINE_SUPPORTS_SAVE )
GAME( 2002, kf2k2pls,   kof2002,  kf2k2pls,  neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "The King of Fighters 2002 Plus (bootleg set 1)", MACHINE_SUPPORTS_SAVE ) /* Encrypted GFX */
GAME( 2002, kf2k2pla,   kof2002,  kf2k2pls,  neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "The King of Fighters 2002 Plus (bootleg set 2)", MACHINE_SUPPORTS_SAVE ) /* Encrypted GFX */
GAME( 2002, kf2k2mp,    kof2002,  kf2k2mp,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "The King of Fighters 2002 Magic Plus (bootleg)", MACHINE_SUPPORTS_SAVE ) /* Encrypted GFX */
GAME( 2002, kf2k2mp2,   kof2002,  kf2k2mp2,  neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "The King of Fighters 2002 Magic Plus II (bootleg)", MACHINE_SUPPORTS_SAVE ) /* Encrypted GFX */
GAME( 2002, kof10th,    kof2002,  kof10th,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "The King of Fighters 10th Anniversary (bootleg of The King of Fighters 2002)", MACHINE_SUPPORTS_SAVE ) // fake SNK copyright
GAME( 2005, kf10thep,   kof2002,  kf10thep,  neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "The King of Fighters 10th Anniversary Extra Plus (bootleg of The King of Fighters 2002)", MACHINE_SUPPORTS_SAVE ) // fake SNK copyright
GAME( 2004, kf2k5uni,   kof2002,  kf2k5uni,  neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "The King of Fighters 10th Anniversary 2005 Unique (bootleg of The King of Fighters 2002)", MACHINE_SUPPORTS_SAVE ) // fake SNK copyright
GAME( 2004, kof2k4se,   kof2002,  kof2k4se,  neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "The King of Fighters Special Edition 2004 (bootleg of The King of Fighters 2002)", MACHINE_SUPPORTS_SAVE ) /* Hack / Bootleg of kof2002 */
GAME( 2003, mslug5,     neogeo,   mslug5,    neogeo,    mvs_led_state, empty_init, ROT0, "SNK Playmore", "Metal Slug 5 (NGM-2680)", MACHINE_SUPPORTS_SAVE )
GAME( 2003, mslug5h,    mslug5,   mslug5,    neogeo,    mvs_led_state, empty_init, ROT0, "SNK Playmore", "Metal Slug 5 (NGH-2680)", MACHINE_SUPPORTS_SAVE ) /* Also found in later MVS carts */
GAME( 2003, ms5plus,    mslug5,   ms5plus,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "Metal Slug 5 Plus (bootleg)", MACHINE_SUPPORTS_SAVE )
GAME( 2003, mslug5b,    mslug5,   mslug5b,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "Metal Slug 5 (bootleg)", MACHINE_SUPPORTS_SAVE )
GAME( 2003, svc,        neogeo,   svc,       neogeo,    mvs_led_state, empty_init, ROT0, "Playmore / Capcom", "SNK vs. Capcom - SVC Chaos (NGM-2690 ~ NGH-2690)", MACHINE_SUPPORTS_SAVE )
GAME( 2003, svcboot,    svc,      svcboot,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "SNK vs. Capcom - SVC Chaos (bootleg)", MACHINE_SUPPORTS_SAVE )
GAME( 2003, svcplus,    svc,      svcplus,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "SNK vs. Capcom - SVC Chaos Plus (bootleg set 1)", MACHINE_SUPPORTS_SAVE )
GAME( 2003, svcplusa,   svc,      svcplusa,  neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "SNK vs. Capcom - SVC Chaos Plus (bootleg set 2)", MACHINE_SUPPORTS_SAVE )
GAME( 2003, svcsplus,   svc,      svcsplus,  neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "SNK vs. Capcom - SVC Chaos Super Plus (bootleg)", MACHINE_SUPPORTS_SAVE )
GAME( 2003, samsho5,    neogeo,   samsho5,   neogeo,    mvs_led_state, empty_init, ROT0, "Yuki Enterprise / SNK Playmore", "Samurai Shodown V / Samurai Spirits Zero (NGM-2700, set 1)", MACHINE_SUPPORTS_SAVE )
GAME( 2003, samsho5a,   samsho5,  samsho5,   neogeo,    mvs_led_state, empty_init, ROT0, "Yuki Enterprise / SNK Playmore", "Samurai Shodown V / Samurai Spirits Zero (NGM-2700, set 2)", MACHINE_SUPPORTS_SAVE )
GAME( 2003, samsho5h,   samsho5,  samsho5,   neogeo,    mvs_led_state, empty_init, ROT0, "Yuki Enterprise / SNK Playmore", "Samurai Shodown V / Samurai Spirits Zero (NGH-2700)", MACHINE_SUPPORTS_SAVE )
GAME( 2003, samsho5b,   samsho5,  samsho5b,  neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "Samurai Shodown V / Samurai Spirits Zero (bootleg)", MACHINE_SUPPORTS_SAVE ) // different program scrambling
GAME( 2003, kof2003,    neogeo,   kof2003,   neogeo,    mvs_led_state, empty_init, ROT0, "SNK Playmore", "The King of Fighters 2003 (NGM-2710, Export)", MACHINE_SUPPORTS_SAVE ) // it's display "Parental Advisory Warning" screen at US/Japan bios, Japanese letters aren't display
GAME( 2003, kof2003h,   kof2003,  kof2003h,  neogeo,    mvs_led_state, empty_init, ROT0, "SNK Playmore", "The King of Fighters 2003 (NGH-2710)", MACHINE_SUPPORTS_SAVE )
GAME( 2003, kf2k3bl,    kof2003,  kf2k3bl,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "The King of Fighters 2003 (bootleg set 1)", MACHINE_SUPPORTS_SAVE ) // zooming is wrong because its a bootleg of the pcb version on a cart (unless it was a bootleg pcb with the new bios?)
GAME( 2003, kf2k3bla,   kof2003,  kf2k3pl,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "The King of Fighters 2003 (bootleg set 2)", MACHINE_SUPPORTS_SAVE ) // zooming is wrong because its a bootleg of the pcb version on a cart
GAME( 2003, kf2k3pl,    kof2003,  kf2k3pl,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "The King of Fighters 2004 Plus / Hero (bootleg of The King of Fighters 2003)", MACHINE_SUPPORTS_SAVE ) // zooming is wrong because its a bootleg of the pcb version on a cart
GAME( 2003, kf2k3upl,   kof2003,  kf2k3upl,  neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "The King of Fighters 2004 Ultra Plus (bootleg of The King of Fighters 2003)", MACHINE_SUPPORTS_SAVE ) // zooming is wrong because its a bootleg of the pcb version on a cart
GAME( 2004, samsh5sp,   neogeo,   samsh5sp,  neogeo,    mvs_led_state, empty_init, ROT0, "Yuki Enterprise / SNK Playmore", "Samurai Shodown V Special / Samurai Spirits Zero Special (NGM-2720)", MACHINE_SUPPORTS_SAVE )
GAME( 2004, samsh5sph,  samsh5sp, samsh5sp,  neogeo,    mvs_led_state, empty_init, ROT0, "Yuki Enterprise / SNK Playmore", "Samurai Shodown V Special / Samurai Spirits Zero Special (NGH-2720, 2nd release, less censored)", MACHINE_SUPPORTS_SAVE )
GAME( 2004, samsh5spho, samsh5sp, samsh5sp,  neogeo,    mvs_led_state, empty_init, ROT0, "Yuki Enterprise / SNK Playmore", "Samurai Shodown V Special / Samurai Spirits Zero Special (NGH-2720, 1st release, censored)", MACHINE_SUPPORTS_SAVE )

// Alpha Denshi Co. / ADK (changed name in 1993)
GAME( 1990, maglord,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Alpha Denshi Co.", "Magician Lord (NGM-005)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, maglordh,   maglord,  neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Alpha Denshi Co.", "Magician Lord (NGH-005)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, ncombat,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Alpha Denshi Co.", "Ninja Combat (NGM-009)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, ncombath,   ncombat,  neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Alpha Denshi Co.", "Ninja Combat (NGH-009)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, bjourney,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Alpha Denshi Co.", "Blue's Journey / Raguy (ALM-001 ~ ALH-001)", MACHINE_SUPPORTS_SAVE )
GAME( 1990, bjourneyh,  bjourney, neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Alpha Denshi Co.", "Blue's Journey / Raguy (ALH-001)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, crsword,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Alpha Denshi Co.", "Crossed Swords (ALM-002 ~ ALH-002)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, trally,     neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Alpha Denshi Co.", "Thrash Rally (ALM-003 ~ ALH-003)", MACHINE_NODEVICE_LAN | MACHINE_SUPPORTS_SAVE )
GAME( 1992, ncommand,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Alpha Denshi Co.", "Ninja Commando", MACHINE_SUPPORTS_SAVE )
GAME( 1992, wh1,        neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Alpha Denshi Co.", "World Heroes (ALM-005)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, wh1h,       wh1,      neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Alpha Denshi Co.", "World Heroes (ALH-005)", MACHINE_SUPPORTS_SAVE )
GAME( 1992, wh1ha,      wh1,      neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Alpha Denshi Co.", "World Heroes (set 3)", MACHINE_SUPPORTS_SAVE )
GAME( 1993, wh2,        neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "ADK",              "World Heroes 2 (ALM-006 ~ ALH-006)", MACHINE_SUPPORTS_SAVE )
GAME( 1993, wh2h,       wh2,      neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "ADK",              "World Heroes 2 (ALH-006)", MACHINE_SUPPORTS_SAVE )
GAME( 1994, wh2j,       neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "ADK / SNK",        "World Heroes 2 Jet (ADM-007 ~ ADH-007)", MACHINE_SUPPORTS_SAVE )
GAME( 1994, aodk,       neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "ADK / SNK",        "Aggressors of Dark Kombat / Tsuukai GANGAN Koushinkyoku (ADM-008 ~ ADH-008)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, whp,        neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "ADK / SNK",        "World Heroes Perfect", MACHINE_SUPPORTS_SAVE )
GAME( 1995, moshougi,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "ADK / SNK",        "Shougi no Tatsujin - Master of Shougi", MACHINE_SUPPORTS_SAVE )
GAME( 1996, overtop,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "ADK",              "Over Top", MACHINE_SUPPORTS_SAVE )
GAME( 1996, ninjamas,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "ADK / SNK",        "Ninja Master's - Haoh-ninpo-cho", MACHINE_SUPPORTS_SAVE )
GAME( 1996, twinspri,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "ADK / SNK",        "Twinkle Star Sprites", MACHINE_SUPPORTS_SAVE )
GAME( 1996, zintrckb,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg",          "Zintrick / Oshidashi Zentrix (bootleg of CD version)", MACHINE_SUPPORTS_SAVE )
GAME( 1996, crswd2bl,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg (Razoola)","Crossed Swords 2 (bootleg of CD version)", MACHINE_SUPPORTS_SAVE )


// Aicom (was a part of Sammy) / Yumekobo (changed name in 1996)
GAME( 1992, viewpoin,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Sammy / Aicom", "Viewpoint", MACHINE_SUPPORTS_SAVE )
GAME( 1992, viewpoinp,  viewpoin, neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Sammy / Aicom", "Viewpoint (prototype)", MACHINE_SUPPORTS_SAVE )
GAME( 1994, janshin,    neogeo,   neogeo_mj, neogeo_mj, mvs_led_state, empty_init, ROT0, "Aicom", "Janshin Densetsu - Quest of Jongmaster", MACHINE_SUPPORTS_SAVE )
GAME( 1995, pulstar,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Aicom", "Pulstar", MACHINE_SUPPORTS_SAVE )
GAME( 1998, blazstar,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Yumekobo", "Blazing Star", MACHINE_SUPPORTS_SAVE )
GAME( 1999, preisle2,   neogeo,   preisle2,  neogeo,    mvs_led_state, empty_init, ROT0, "Yumekobo / Saurus", "Prehistoric Isle 2", MACHINE_SUPPORTS_SAVE ) /* Encrypted GFX */

// Data East Corporation
GAME( 1993, spinmast,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Data East Corporation", "Spin Master / Miracle Adventure", MACHINE_SUPPORTS_SAVE )
GAME( 1994, wjammers,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Data East Corporation", "Windjammers / Flying Power Disc", MACHINE_SUPPORTS_SAVE )
GAME( 1994, karnovr,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Data East Corporation", "Karnov's Revenge / Fighter's History Dynamite", MACHINE_SUPPORTS_SAVE )
GAME( 1994, strhoop,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Data East Corporation", "Street Hoop / Street Slam / Dunk Dream (DEM-004 ~ DEH-004)", MACHINE_SUPPORTS_SAVE )
GAME( 1996, ghostlop,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Data East Corporation", "Ghostlop (prototype)", MACHINE_SUPPORTS_SAVE )
GAME( 1996, magdrop2,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Data East Corporation", "Magical Drop II", MACHINE_SUPPORTS_SAVE )
GAME( 1997, magdrop3,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Data East Corporation", "Magical Drop III", MACHINE_SUPPORTS_SAVE )

// Eleven
GAME( 2000, nitd,       neogeo,   nitd,      neogeo,    mvs_led_state, empty_init, ROT0, "Eleven / Gavaking", "Nightmare in the Dark", MACHINE_SUPPORTS_SAVE ) /* Encrypted GFX */
GAME( 2001, nitdbl,     nitd,     neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "Nightmare in the Dark (bootleg)", MACHINE_SUPPORTS_SAVE )

// Face
GAME( 1994, gururin,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Face", "Gururin", MACHINE_SUPPORTS_SAVE )
GAME( 1997, miexchng,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Face", "Money Puzzle Exchanger / Money Idol Exchanger", MACHINE_SUPPORTS_SAVE )
GAME( 1997, dragonsh,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Face", "Dragon's Heaven (development board)", MACHINE_IS_INCOMPLETE | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE ) // same ID code as Voltage Fighter Gowkaizer, developed by ex-Technos staff

// Hudson Soft
GAME( 1994, panicbom,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Eighting / Hudson", "Panic Bomber", MACHINE_SUPPORTS_SAVE )
GAME( 1995, kabukikl,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Hudson", "Far East of Eden - Kabuki Klash / Tengai Makyou - Shin Den", MACHINE_SUPPORTS_SAVE )
GAME( 1997, neobombe,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Hudson", "Neo Bomberman", MACHINE_SUPPORTS_SAVE )

// Monolith Corp.
GAME( 1990, minasan,    neogeo,   neogeo_mj, neogeo_mj, mvs_led_state, empty_init, ROT0, "Monolith Corp.", "Minasan no Okagesamadesu! Dai Sugoroku Taikai (MOM-001 ~ MOH-001)", MACHINE_SUPPORTS_SAVE )
GAME( 1991, bakatono,   neogeo,   neogeo_mj, neogeo_mj, mvs_led_state, empty_init, ROT0, "Monolith Corp.", "Bakatonosama Mahjong Manyuuki (MOM-002 ~ MOH-002)", MACHINE_SUPPORTS_SAVE )

// Nazca (later acquired by SNK)
GAME( 1996, turfmast,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Nazca", "Neo Turf Masters / Big Tournament Golf", MACHINE_SUPPORTS_SAVE )
GAME( 1996, mslug,      neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Nazca", "Metal Slug - Super Vehicle-001", MACHINE_SUPPORTS_SAVE )
GAME( 1995?,mvstemp,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Nazca", "MVS-TEMP 'SubSystem Ver1.4' (Nazca development board)", MACHINE_NOT_WORKING )

// NMK
GAME( 1994, zedblade,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "NMK", "Zed Blade / Operation Ragnarok", MACHINE_SUPPORTS_SAVE )

// Psikyo
GAME( 1999, s1945p,     neogeo,   s1945p,    neogeo,    mvs_led_state, empty_init, ROT0, "Psikyo", "Strikers 1945 Plus", MACHINE_SUPPORTS_SAVE ) /* Encrypted GFX */

// Saurus
GAME( 1995, quizkof,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Saurus (SNK license)", "Quiz King of Fighters (SAM-080 ~ SAH-080)", MACHINE_SUPPORTS_SAVE )
GAME( 1996, quizkofk,   quizkof,  neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Saurus / Viccom (SNK license)", "Quiz King of Fighters (Korea)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, stakwin,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Saurus", "Stakes Winner / Stakes Winner - GI Kinzen Seiha e no Michi", MACHINE_SUPPORTS_SAVE )
GAME( 1995, stakwindev, stakwin,  neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Saurus", "Stakes Winner / Stakes Winner - GI Kinzen Seiha e no Michi (early development board)", MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING | MACHINE_IS_INCOMPLETE )
GAME( 1996, ragnagrd,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Saurus", "Ragnagard / Shin-Oh-Ken", MACHINE_SUPPORTS_SAVE )
GAME( 1996, pgoal,      neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Saurus", "Pleasure Goal / Futsal - 5 on 5 Mini Soccer (NGM-219)", MACHINE_SUPPORTS_SAVE )
GAME( 1996, ironclad,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Saurus", "Choutetsu Brikin'ger / Iron Clad (prototype)", MACHINE_SUPPORTS_SAVE )
GAME( 1996, ironclado,  ironclad, neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "Choutetsu Brikin'ger / Iron Clad (prototype, bootleg)", MACHINE_SUPPORTS_SAVE )
GAME( 1996, stakwin2,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Saurus", "Stakes Winner 2", MACHINE_SUPPORTS_SAVE )
GAME( 1997, shocktro,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Saurus", "Shock Troopers (set 1)", MACHINE_SUPPORTS_SAVE )
GAME( 1997, shocktroa,  shocktro, neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Saurus", "Shock Troopers (set 2)", MACHINE_SUPPORTS_SAVE )
GAME( 1998, shocktr2,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Saurus", "Shock Troopers - 2nd Squad", MACHINE_SUPPORTS_SAVE )
GAME( 1998, lans2004,   shocktr2, lans2004,  neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "Lansquenet 2004 (bootleg of Shock Troopers - 2nd Squad)", MACHINE_SUPPORTS_SAVE )

// Sunsoft
GAME( 1995, galaxyfg,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Sunsoft", "Galaxy Fight - Universal Warriors", MACHINE_SUPPORTS_SAVE )
GAME( 1996, wakuwak7,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Sunsoft", "Waku Waku 7", MACHINE_SUPPORTS_SAVE )

// Taito
GAME( 1994, pbobblen,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Taito", "Puzzle Bobble / Bust-A-Move (Neo-Geo, NGM-083)", MACHINE_SUPPORTS_SAVE )
GAME( 1994, pbobblenb,  pbobblen, neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "Puzzle Bobble / Bust-A-Move (Neo-Geo, bootleg)", MACHINE_SUPPORTS_SAVE )
GAME( 1999, pbobbl2n,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Taito (SNK license)", "Puzzle Bobble 2 / Bust-A-Move Again (Neo-Geo)", MACHINE_SUPPORTS_SAVE ) // ported to Neo-Geo by SNK, original version is on Taito F3
GAME( 2003, pnyaa,      neogeo,   pnyaa,     neogeo,    mvs_led_state, empty_init, ROT0, "Aiky / Taito", "Pochi and Nyaa (Ver 2.02)", MACHINE_SUPPORTS_SAVE ) // this version does not show the web address on the title screen
GAME( 2003, pnyaaa,     pnyaa,    pnyaa,     neogeo,    mvs_led_state, empty_init, ROT0, "Aiky / Taito", "Pochi and Nyaa (Ver 2.00)", MACHINE_SUPPORTS_SAVE )

// Takara
GAME( 1995, marukodq,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Takara", "Chibi Maruko-chan: Maruko Deluxe Quiz", MACHINE_SUPPORTS_SAVE )

// Technos Japan
GAME( 1995, doubledr,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Technos Japan", "Double Dragon (Neo-Geo)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, gowcaizr,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Technos Japan", "Voltage Fighter - Gowcaizer / Choujin Gakuen Gowcaizer", MACHINE_SUPPORTS_SAVE )
GAME( 1996, sdodgeb,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Technos Japan", "Super Dodge Ball / Kunio no Nekketsu Toukyuu Densetsu", MACHINE_SUPPORTS_SAVE )

// Tecmo
GAME( 1996, twsoc96,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Tecmo", "Tecmo World Soccer '96", MACHINE_SUPPORTS_SAVE )

// Viccom
GAME( 1994, fightfev,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Viccom", "Fight Fever / Wang Jung Wang (set 1)", MACHINE_SUPPORTS_SAVE )
GAME( 1994, fightfeva,  fightfev, neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Viccom", "Fight Fever / Wang Jung Wang (set 2)", MACHINE_SUPPORTS_SAVE )

// Video System Co.
GAME( 1994, pspikes2,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Video System Co.", "Power Spikes II (NGM-068)", MACHINE_SUPPORTS_SAVE )
GAME( 1994, sonicwi2,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Video System Co.", "Aero Fighters 2 / Sonic Wings 2", MACHINE_SUPPORTS_SAVE )
GAME( 1995, sonicwi3,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Video System Co.", "Aero Fighters 3 / Sonic Wings 3", MACHINE_SUPPORTS_SAVE )
GAME( 1997, popbounc,   neogeo,   popbounc,  neogeo,    mvs_led_state, empty_init, ROT0, "Video System Co.", "Pop 'n Bounce / Gapporin", MACHINE_SUPPORTS_SAVE )
GAME( 1995, froman2b,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "Idol Mahjong Final Romance 2 (Neo-Geo, bootleg of CD version)", MACHINE_SUPPORTS_SAVE )

// Visco
GAME( 1992, androdun,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Visco", "Andro Dunos (NGM-049 ~ NGH-049)", MACHINE_SUPPORTS_SAVE )
GAME( 1995, puzzledp,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Visco", "Puzzle De Pon!", MACHINE_SUPPORTS_SAVE ) // game concept licensed from Taito
GAME( 1996, neomrdo,    neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Visco", "Neo Mr. Do!", MACHINE_SUPPORTS_SAVE ) // game concept licensed from Universal
GAME( 1995, goalx3,     neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Visco", "Goal! Goal! Goal!", MACHINE_SUPPORTS_SAVE )
GAME( 1996, neodrift,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Visco", "Neo Drift Out - New Technology", MACHINE_SUPPORTS_SAVE )
GAME( 1996, breakers,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Visco", "Breakers", MACHINE_SUPPORTS_SAVE )
GAME( 1997, puzzldpr,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Visco", "Puzzle De Pon! R!", MACHINE_SUPPORTS_SAVE ) // game concept licensed from Taito
GAME( 1998, breakrev,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Visco", "Breakers Revenge", MACHINE_SUPPORTS_SAVE )
GAME( 1998, flipshot,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Visco", "Flip Shot", MACHINE_SUPPORTS_SAVE )
GAME( 1999, ctomaday,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Visco", "Captain Tomaday", MACHINE_SUPPORTS_SAVE )
GAME( 1999, ganryu,     neogeo,   ganryu,    neogeo,    mvs_led_state, empty_init, ROT0, "Visco", "Ganryu / Musashi Ganryuki", MACHINE_SUPPORTS_SAVE ) /* Encrypted GFX */
GAME( 2000, bangbead,   neogeo,   bangbead,  neogeo,    mvs_led_state, empty_init, ROT0, "Visco", "Bang Bead", MACHINE_SUPPORTS_SAVE )
GAME( 2000, b2b,        neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Visco", "Bang Bang Busters (2010 NCI release)", MACHINE_SUPPORTS_SAVE )

// Mega Enterprise
GAME( 2002, mslug4,     neogeo,   mslug4,    neogeo,    mvs_led_state, empty_init, ROT0, "Mega / Noise Factory / Playmore", "Metal Slug 4 (NGM-2630)", MACHINE_SUPPORTS_SAVE )
GAME( 2002, mslug4h,    mslug4,   mslug4,    neogeo,    mvs_led_state, empty_init, ROT0, "Mega / Noise Factory / Playmore", "Metal Slug 4 (NGH-2630)", MACHINE_SUPPORTS_SAVE )
GAME( 2002, ms4plus,    mslug4,   ms4plus,   neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "Metal Slug 4 Plus (bootleg)", MACHINE_SUPPORTS_SAVE )

// Evoga
GAME( 2002, rotd,       neogeo,   rotd,      neogeo,    mvs_led_state, empty_init, ROT0, "Evoga / Playmore", "Rage of the Dragons (NGM-2640?)", MACHINE_SUPPORTS_SAVE )
GAME( 2002, rotdh,      rotd,     rotd,      neogeo,    mvs_led_state, empty_init, ROT0, "Evoga / Playmore", "Rage of the Dragons (NGH-2640?)", MACHINE_SUPPORTS_SAVE )

// Atlus
GAME( 2003, matrim,     neogeo,   matrim,    neogeo,    mvs_led_state, empty_init, ROT0, "Noise Factory / Atlus", "Matrimelee / Shin Gouketsuji Ichizoku Toukon (NGM-2660 ~ NGH-2660)", MACHINE_SUPPORTS_SAVE )
GAME( 2003, matrimbl,   matrim,   matrimbl,  neogeo,    mvs_led_state, empty_init, ROT0, "bootleg", "Matrimelee / Shin Gouketsuji Ichizoku Toukon (bootleg)", MACHINE_SUPPORTS_SAVE )

/***** Unlicensed commercial releases *****/

// BrezzaSoft
GAME( 2001, jockeygp,   neogeo,   jockeygp,  jockeygp,  mvs_led_state, empty_init, ROT0, "Sun Amusement / BrezzaSoft", "Jockey Grand Prix (set 1)", MACHINE_SUPPORTS_SAVE )
GAME( 2001, jockeygpa,  jockeygp, jockeygp,  jockeygp,  mvs_led_state, empty_init, ROT0, "Sun Amusement / BrezzaSoft", "Jockey Grand Prix (set 2)", MACHINE_SUPPORTS_SAVE )

GAME( 2001, vliner,     neogeo,   vliner,    vliner,    mvs_led_state, empty_init, ROT0, "Dyna / BrezzaSoft", "V-Liner (v0.7a)", MACHINE_SUPPORTS_SAVE )
GAME( 2001, vliner7e,   vliner,   vliner,    vliner,    mvs_led_state, empty_init, ROT0, "Dyna / BrezzaSoft", "V-Liner (v0.7e)", MACHINE_SUPPORTS_SAVE )
GAME( 2001, vliner6e,   vliner,   vliner,    vliner,    mvs_led_state, empty_init, ROT0, "Dyna / BrezzaSoft", "V-Liner (v0.6e)", MACHINE_SUPPORTS_SAVE )
GAME( 2001, vliner54,   vliner,   vliner,    vliner,    mvs_led_state, empty_init, ROT0, "Dyna / BrezzaSoft", "V-Liner (v0.54)", MACHINE_SUPPORTS_SAVE )
GAME( 2001, vliner53,   vliner,   vliner,    vliner,    mvs_led_state, empty_init, ROT0, "Dyna / BrezzaSoft", "V-Liner (v0.53)", MACHINE_SUPPORTS_SAVE )

// Kyle Hodgetts
GAME( 2000, diggerma,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "Kyle Hodgetts", "Digger Man (prototype)", MACHINE_SUPPORTS_SAVE )

// Vektorlogic
GAME( 2004, sbp,        neogeo,   sbp,       neogeo,    mvs_led_state, empty_init, ROT0, "Vektorlogic", "Super Bubble Pop", MACHINE_SUPPORTS_SAVE | MACHINE_UNEMULATED_PROTECTION )

// NG:DEV.TEAM
GAME( 2005, lasthope,   neogeo,   neobase,   neogeo,    mvs_led_state, empty_init, ROT0, "NG:DEV.TEAM", "Last Hope (bootleg AES to MVS conversion, no coin support)", MACHINE_SUPPORTS_SAVE ) // wasn't actually released on MVS but bootleg carts have been sold, this doesn't accept coins, runs like a console game
// Last Hope Pink Bullets (c)2008 - MVS/AES
// Fast Striker (c)2010 - MVS/AES
// Fast Striker 1.5 (c)2010 - MVS/AES
// GunLord (c)2012 - MVS/AES
// Neo XYX (c)2013 - MVS/AES
// Razion (c)2014 - MVS/AES
// Kraut Buster (c)2016 - MVS/AES

// N.C.I - LE CORTEX
// Treasure of the Caribbean (c)2011 - AES only, see hash/neogeo.xml for entry (no credits system if run on an MVS, Freeplay)

// NEOBITZ
// Knight's Chance (c)2014 - MVS/AES



neogeocd.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Bryan McPhail,Ernesto Corvi,Andrew Prime,Zsolt Vasvari
// thanks-to:Fuzz
/***************************************************************************

    Neo-Geo CD hardware

    Thanks to:
        * Jan Klaassen (of the former FBA team) for much of the CDC / CDD code and system details.
        * Mirko Buffoni for a commented disassembly of the NeoCD bios rom.

    Current status:
        - NeoCDZ runs, the original NeoCD does not
           - Might think the tray is open? (check)
        - Some unknown / unhandled CD commands, code is still a bit messy
           - CDDA continues to play during loading, should stop it
        - Games using Raster Effects are broken without a kludge
           - CPU gets overloaded with IRQs from the timer callback...
        - Double Dragon doesn't load, it erases the IRQ table
           - might need better handling of the Vector Table Mapping, or better interrupts (see point above)
        - Softlist are based on an old Tosec set and should be updated to the TruRip set once we can convert CCD
          without throwing away gap data etc.

****************************************************************************/

#include "emu.h"
#include "neogeo.h"

#include "megacdcd.h"

#include "imagedev/cdromimg.h"
#include "machine/74259.h"
#include "machine/nvram.h"

#include "softlist.h"


namespace {

// was it actually released in eu / asia?
//static constexpr unsigned NEOCD_REGION_ASIA = 3; // IronClad runs with a darkened screen (MVS has the same issue)
//static constexpr unsigned NEOCD_REGION_EUROPE = 2; // ^
static constexpr unsigned NEOCD_REGION_US = 1;
static constexpr unsigned NEOCD_REGION_JAPAN = 0;


class ngcd_state : public aes_base_state
{
public:
	ngcd_state(const machine_config &mconfig, device_type type, const char *tag)
		: aes_base_state(mconfig, type, tag)
		, m_tempcdc(*this, "tempcdc")
		, m_z80_ram(*this, "z80_ram")
		, m_adpcm_ram(*this, "adpcm_ram")
	{
		m_dma_address1 = 0;
		m_dma_address2 = 0;
		m_dma_value1   = 0;
		m_dma_value2   = 0;
		m_dma_count    = 0;
		m_dma_mode = 0;
		m_irq_ack = ~0;
		m_irq_vector_ack = 0;
		m_irq_vector = 0;
		m_has_sprite_bus = true;
		m_has_text_bus = true;
		m_has_ymrom_bus = true;
		m_has_z80_bus = true;
	}

	void neocd_ntsc(machine_config &config) ATTR_COLD;

	void init_neocdz() ATTR_COLD;
	void init_neocdzj() ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	optional_device<lc89510_temp_device> m_tempcdc;
	required_shared_ptr<uint8_t> m_z80_ram;
	required_shared_ptr<uint8_t> m_adpcm_ram;

	std::unique_ptr<uint8_t[]> m_meminternal_data;
	std::unique_ptr<uint8_t[]> m_sprite_ram;
	std::unique_ptr<uint8_t[]> m_fix_ram;

	// neoCD
	uint8_t m_system_region = 0;
	int32_t m_active_transfer_area = 0;
	int32_t m_sprite_transfer_bank = 0;
	int32_t m_adpcm_transfer_bank = 0;
	int32_t m_dma_address1;
	int32_t m_dma_address2;
	int32_t m_dma_value1;
	int32_t m_dma_value2;
	int32_t m_dma_count;
	int32_t m_dma_mode;
	int32_t m_irq_ack;
	int32_t m_irq_vector_ack;
	int32_t m_irq_vector;

	bool m_has_sprite_bus;
	bool m_has_text_bus;
	bool m_has_ymrom_bus;
	bool m_has_z80_bus;

	uint8_t m_transfer_write_enable = 0;

	bool prohibit_cdc_irq = false; // hack?

	int32_t seek_idle(int32_t cycles);

	void do_dma(address_space& curr_space);
	void set_dma_regs(offs_t offset, uint16_t data);

	uint16_t memcard_r(offs_t offset);
	void memcard_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t control_r(offs_t offset);
	void control_w(address_space &space, offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint8_t transfer_r(offs_t offset);
	void transfer_w(offs_t offset, uint8_t data);

	DECLARE_INPUT_CHANGED_MEMBER(aes_jp1);

	int get_irq_vector_ack() { return m_irq_vector_ack; }
	void set_irq_vector_ack(int val) { m_irq_vector_ack = val; }
	int get_irq_vector() { return m_irq_vector; }
	void irq_update(uint8_t data);

	// from the CDC
	void interrupt_callback_type1();
	void interrupt_callback_type2();
	void interrupt_callback_type3();

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	uint8_t cdc_irq_ack();

	void neocd_audio_io_map(address_map &map) ATTR_COLD;
	void neocd_audio_map(address_map &map) ATTR_COLD;
	void neocd_main_map(address_map &map) ATTR_COLD;
	void neocd_vector_map(address_map &map) ATTR_COLD;
	void neocd_ym_map(address_map &map) ATTR_COLD;
};



/*************************************
 *
 *  Memory card
 *
 *************************************/


/* The NeoCD has an 8kB internal memory card, instead of memcard slots like the MVS and AES */
uint16_t ngcd_state::memcard_r(offs_t offset)
{
	return m_meminternal_data[offset] | 0xff00;
}


void ngcd_state::memcard_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (ACCESSING_BITS_0_7)
	{
		m_meminternal_data[offset] = data;
	}
}


/*************************************
 *
 *  System control register
 *
 *************************************/

uint16_t ngcd_state::control_r(offs_t offset)
{
	uint32_t const seek_address = 0xff0000 + (offset * 2);

	switch (seek_address & 0xffff)
	{
		case 0x0016:
			return m_tempcdc->nff0016_r();

		// LC8951 registers
		case 0x0100:
			return m_tempcdc->segacd_cdc_mode_address_r();
		case 0x0102:
			return m_tempcdc->CDC_Reg_r();

		// CD mechanism communication
		case 0x0160:
			return m_tempcdc->neocd_cdd_rx_r();

		case 0x011c: // region
			return ~((0x10 | (m_system_region & 3)) << 8);
	}


//  bprintf(PRINT_NORMAL, _T("  - NGCD port 0x%06X read (word, PC: 0x%06X)\n"), seek_address, SekGetPC(-1));

	return ~0;
}


void ngcd_state::control_w(address_space &space, offs_t offset, uint16_t data, uint16_t mem_mask)
{
	uint32_t const seek_address = 0xff0000 + (offset * 2);

//  bprintf(PRINT_NORMAL, _T("  - NGCD port 0x%06X -> 0x%04X (PC: 0x%06X)\n"), seek_address, data, SekGetPC(-1));
	uint8_t const byte_value = data & 0xff;

	switch (seek_address & 0xfffe)
	{
		case 0x0002:
			m_tempcdc->nff0002_set(data);
			break;

		case 0x000e:
			irq_update(data); // irqack
			break;

		case 0x0016:
			m_tempcdc->nff0016_set(byte_value);
			break;

			// DMA controller
		case 0x0060:
			if (BIT(byte_value, 6))
			{
				do_dma(space);
			}
			break;

		case 0x0064:
		case 0x0066:
		case 0x0068:
		case 0x006a:
		case 0x006c:
		case 0x006e:
		case 0x0070:
		case 0x0072:
		case 0x007e:
			set_dma_regs(seek_address & 0xfffe, data);
			break;

		// upload DMA controller program

		case 0x0080:
		case 0x0082:
		case 0x0084:
		case 0x0086:
		case 0x0088:
		case 0x008a:
		case 0x008c:
		case 0x008e:
//          bprintf(PRINT_NORMAL, _T("  - DMA controller program[%02i] -> 0x%04X (PC: 0x%06X)\n"), seek_address & 0x0F, data, SekGetPC(-1));
			break;

		// LC8951 registers
		case 0x0100:
			m_tempcdc->segacd_cdc_mode_address_w(0, byte_value, 0xffff);
			break;
		case 0x0102:
			m_tempcdc->CDC_Reg_w(byte_value);
			break;

		case 0x0104:
//          bprintf(PRINT_NORMAL, _T("  - NGCD 0xE00000 area -> 0x%02X (PC: 0x%06X)\n"), byte_value, SekGetPC(-1));
			if (ACCESSING_BITS_0_7)
			{
				m_active_transfer_area = byte_value;
			}
			break;

		case 0x0120:
//          bprintf(PRINT_NORMAL, _T("  - NGCD OBJ BUSREQ -> 1 (PC: 0x%06X)\n"), SekGetPC(-1));
			m_has_sprite_bus = false;
			break;
		case 0x0122:
//          bprintf(PRINT_NORMAL, _T("  - NGCD PCM BUSREQ -> 1 (PC: 0x%06X) %x\n"), SekGetPC(-1), byte_value);
			m_has_ymrom_bus = false;
			break;
		case 0x0126:
//          bprintf(PRINT_NORMAL, _T("  - NGCD Z80 BUSREQ -> 1 (PC: 0x%06X)\n"), SekGetPC(-1));
			m_has_z80_bus = false;
			machine().scheduler().synchronize();
			m_audiocpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
			break;
		case 0x0128:
//          bprintf(PRINT_NORMAL, _T("  - NGCD FIX BUSREQ -> 1 (PC: 0x%06X)\n"), SekGetPC(-1));
			m_has_text_bus = false;
			break;
		case 0x0140:
//          bprintf(PRINT_NORMAL, _T("  - NGCD OBJ BUSREQ -> 0 (PC: 0x%06X)\n"), SekGetPC(-1));
			m_has_sprite_bus = true;
			m_sprgen->optimize_sprite_data();
			break;
		case 0x0142:
//          bprintf(PRINT_NORMAL, _T("  - NGCD PCM BUSREQ -> 0 (PC: 0x%06X)\n"), SekGetPC(-1));
			m_has_ymrom_bus = true;
			break;
		case 0x0146:
//          bprintf(PRINT_NORMAL, _T("  - NGCD Z80 BUSREQ -> 0 (PC: 0x%06X)\n"), SekGetPC(-1));
			m_has_z80_bus = true;
			machine().scheduler().synchronize();
			m_audiocpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
			break;
		case 0x0148:
//          bprintf(PRINT_NORMAL, _T("  - NGCD FIX BUSREQ -> 0 (PC: 0x%06X)\n"), SekGetPC(-1));
			m_has_text_bus = true;
			break;

		// CD mechanism communication
		case 0x0162:
			m_tempcdc->neocd_cdd_tx_w(byte_value);
			break;
		case 0x0164:
			m_tempcdc->NeoCDCommsControl(byte_value & 1, byte_value & 2);
			break;

		case 0x016c:
//          bprintf(PRINT_ERROR, _T("  - NGCD port 0x%06X -> 0x%02X (PC: 0x%06X)\n"), seek_address, byte_value, SekGetPC(-1));
			//MapVectorTable(!(byte_value == 0xFF));
			if (ACCESSING_BITS_0_7)
			{
				// even like this doubledr ends up mapping vectors in, then erasing them causing the loading to crash??
				// is there some way to enable write protection on the RAM vector area or is it some IRQ masking issue?
				// the games still write to the normal address for this too?
				// writes 00 / 01 / ff
				printf("MapVectorTable? %04x %04x\n",data,mem_mask);

				//m_bank_vectors->set_entry(data == 0 ? 0 : 1);
				m_use_cart_vectors = (data == 0 ? 0 : 1);
			}

//extern int32_t bRunPause;
//bRunPause = 1;
			break;

		case 0x016e:
//          bprintf(PRINT_IMPORTANT, _T("  - NGCD 0xE00000 area write access %s (0x%02X, PC: 0x%06X)\n"), byte_value ? _T("enabled") : _T("disabled"), byte_value, SekGetPC(-1));

			m_transfer_write_enable = byte_value;
			break;

		case 0x0180:
		{
			// 1 during CD access, 0 otherwise, written frequently
			//printf("reset cdc %04x %04x\n",data, mem_mask);

			if (ACCESSING_BITS_0_7)
			{
				if (data == 0x00)
				{
				// not a good idea, causes hangs
				//  m_tempcdc->NeoCDCommsReset();

					// I doubt this is correct either, but we need something to stop
					// the interrupts during gameplay and I'm not sure what...
					prohibit_cdc_irq = true;
				}
				else
				{
					prohibit_cdc_irq = false;
				}
			}
			break;
		}
		case 0x0182:
		{
		//  printf("blah %02x\n", byte_value);
			if (byte_value == 0x00)
			{
				m_ym->reset();
				m_audiocpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
			}
			else m_audiocpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);

			break;
		}
		case 0x01a0:
			m_sprite_transfer_bank = (byte_value & 3) << 20;
			break;
		case 0x01a2:
			m_adpcm_transfer_bank  = (byte_value & 1) << 19;
			break;


		default:
		{
//          bprintf(PRINT_NORMAL, _T("  - NGCD port 0x%06X -> 0x%04X (PC: 0x%06X)\n"), seek_address, data, SekGetPC(-1));
		}
	}

}




/*
 *  Handling NeoCD banked RAM
 *  When the Z80 space is banked in to 0xe00000, only the low byte of each word is used
 */


uint8_t ngcd_state::transfer_r(offs_t offset)
{
	uint32_t const seek_address = (0xe00000 + offset) ^ 1;

	switch (m_active_transfer_area)
	{
		case 0: // Sprites
		{
			uint32_t const address = (m_sprite_transfer_bank + (seek_address & 0x0fffff));

			// address is swizzled a bit due to out sprite decoding
			return m_sprite_ram[address ^ ((BIT(address, 0) == BIT(address, 1)) ? 0 : 3)];
		}
		case 1:                         // ADPCM
			return m_adpcm_ram[m_adpcm_transfer_bank + ((seek_address & 0x0fffff) >> 1)];
		case 4:                         // Z80
			if ((seek_address & 0xfffff) >= 0x20000) return ~0;
			return m_z80_ram[(seek_address & 0x1ffff) >> 1];
		case 5:                         // Text
			return m_fix_ram[(seek_address & 0x3ffff) >> 1];
	}

	return ~0;

}

void ngcd_state::transfer_w(offs_t offset, uint8_t data)
{
	uint32_t const seek_address = (0xe00000 + offset) ^ 1;

	if (!m_transfer_write_enable)
	{
//      return;
	}

	switch (m_active_transfer_area)
	{
		case 0:                         // Sprites
		{
			uint32_t const address = (m_sprite_transfer_bank + (seek_address & 0x0fffff));

			// address is swizzled a bit due to out sprite decoding
			m_sprite_ram[address ^ ((BIT(address, 0) == BIT(address, 1)) ? 0 : 3)] = data;
			break;
		}
		case 1:                         // ADPCM
			m_adpcm_ram[m_adpcm_transfer_bank + ((seek_address & 0x0fffff) >> 1)] = data;
			break;
		case 4:                         // Z80

			// kof98 and lresort attempt to write here when the system still has the z80 bank
			// it seems they attempt to write regular samples (not even deltat) maybe there is
			// some kind of fall-through behavior, or it shouldn't be allowed to select a
			// transfer area without the bus? - this should really be checked on hw
			if (m_has_z80_bus)
			{
				m_adpcm_ram[m_adpcm_transfer_bank + ((seek_address & 0x0fffff) >> 1)] = data;
			}
			else
			{
		//  printf("seek_address %08x %02x\n", seek_address, data);
				if ((seek_address & 0xfffff) >= 0x20000) break;
				m_z80_ram[(seek_address & 0x1ffff) >> 1] = data;
			}
			break;
		case 5:                         // Text
			m_fix_ram[(seek_address & 0x3ffff) >> 1] = data;
			break;
	}
}



void ngcd_state::set_dma_regs(offs_t offset, uint16_t data)
{
	switch (offset)
	{
		case 0x0064:
			m_dma_address1 &= 0x0000ffff;
			m_dma_address1 |= data << 16;
			break;
		case 0x0066:
			m_dma_address1 &= 0xffff0000;
			m_dma_address1 |= data;
			break;
		case 0x0068:
			m_dma_address2 &= 0x0000ffff;
			m_dma_address2 |= data << 16;
			break;
		case 0x006a:
			m_dma_address2 &= 0xffff0000;
			m_dma_address2 |= data;
			break;
		case 0x006c:
			m_dma_value1 = data;
			break;
		case 0x006e:
			m_dma_value2 = data;
			break;
		case 0x0070:
			m_dma_count &= 0x0000ffff;
			m_dma_count |= data << 16;
			break;
		case 0x0072:
			m_dma_count &= 0xffff0000;
			m_dma_count |= data;
			break;

		case 0x007e:
			m_dma_mode = data;
//          bprintf(PRINT_NORMAL, _T("  - DMA controller 0x%2X -> 0x%04X (PC: 0x%06X)\n"), seek_address & 0xFF, data, SekGetPC(-1));
			break;
	}
}



int32_t ngcd_state::seek_idle(int32_t cycles)
{
	return cycles;
}



/*
 *  CD-ROM / DMA control
 *
 *  DMA

    FF0061  Write 0x40 means start DMA transfer
    FF0064  Source address (in copy mode), Target address (in fill mode)
    FF0068  Target address (in copy mode)
    FF006C  Fill word
    FF0070  Words count
    FF007E  \
    ......   | DMA programming words?   NeoGeoCD uses Sanyo Puppet LC8359 chip to
    FF008E  /                           interface with CD, and do DMA transfers

    Memory access control

    FF011C  DIP SWITCH (Region code)
    FF0105  Area Selector (5 = FIX, 0 = SPR, 4 = Z80, 1 = PCM)
    FF01A1  Sprite bank selector
    FF01A3  PCM bank selector
    FF0120  Prepare sprite area for transfer
    FF0122  Prepare PCM area for transfer
    FF0126  Prepare Z80 area for transfer
    FF0128  Prepare Fix area for transfer
    FF0140  Terminate work on Spr Area  (Sprites must be decoded here)
    FF0142  Terminate work on Pcm Area
    FF0146  Terminate work on Z80 Area  (Z80 needs to be reset)
    FF0148  Terminate work on Fix Area

    CD-ROM:
    0xff0102 == 0xF0 start cd transfer
    int m=bcd(fast_r8(0x10f6c8));
    int s=bcd(fast_r8(0x10f6c9));
    int f=bcd(fast_r8(0x10f6ca));
    int seccount=fast_r16(0x10f688);

    inisec=((m*60)+s)*75+f;
    inisec-=150;
    dstaddr=0x111204; // this must come from somewhere

    the value @ 0x10f688 is decremented each time a sector is read until it's 0.

 *
 */


void ngcd_state::do_dma(address_space& curr_space)
{
	// The LC8953 chip has a programmable DMA controller, which is not properly emulated.
	// Since the software only uses it in a limited way, we can apply a simple heuristic
	// to determine the requested operation.

	// Additionally, we don't know how many cycles DMA operations take.
	// Here, only bus access is used to get a rough approximation --
	// each read/write takes a single cycle, setup and everything else is ignored.

//  bprintf(PRINT_IMPORTANT, _T("  - DMA controller transfer started (PC: 0x%06X)\n"), SekGetPC(-1));

	switch (m_dma_mode)
	{
		case 0xcffd:
		{
//          bprintf(PRINT_NORMAL, _T("    adr : 0x%08X - 0x%08X <- address, skip odd bytes\n"), m_dma_address1, m_dma_address1 + m_dma_count * 8);

			//  - DMA controller 0x7E -> 0xCFFD (PC: 0xC07CE2)
			//  - DMA controller program[00] -> 0xFCF5 (PC: 0xC07CE8)
			//  - DMA controller program[02] -> 0xE8DA (PC: 0xC07CEE)
			//  - DMA controller program[04] -> 0x92DA (PC: 0xC07CF4)
			//  - DMA controller program[06] -> 0x92DB (PC: 0xC07CFA)
			//  - DMA controller program[08] -> 0x96DB (PC: 0xC07D00)
			//  - DMA controller program[10] -> 0x96F6 (PC: 0xC07D06)
			//  - DMA controller program[12] -> 0x2E02 (PC: 0xC07D0C)
			//  - DMA controller program[14] -> 0xFDFF (PC: 0xC07D12)

			seek_idle(m_dma_count * 4);

			while (m_dma_count--)
			{
				curr_space.write_word(m_dma_address1 + 0, m_dma_address1 >> 24);
				curr_space.write_word(m_dma_address1 + 2, m_dma_address1 >> 16);
				curr_space.write_word(m_dma_address1 + 4, m_dma_address1 >>  8);
				curr_space.write_word(m_dma_address1 + 6, m_dma_address1 >>  0);
				m_dma_address1 += 8;
			}

			break;
		}

		case 0xe2dd:
		{
//          bprintf(PRINT_NORMAL, _T("    copy: 0x%08X - 0x%08X <- 0x%08X - 0x%08X, skip odd bytes\n"), m_dma_address2, m_dma_address2 + m_dma_count * 2, m_dma_address1, m_dma_address1 + m_dma_count * 4);

			//  - DMA controller 0x7E -> 0xE2DD (PC: 0xC0A190)
			//  - DMA controller program[00] -> 0xFCF5 (PC: 0xC0A192)
			//  - DMA controller program[02] -> 0x82BE (PC: 0xC0A194)
			//  - DMA controller program[04] -> 0x93DA (PC: 0xC0A196)
			//  - DMA controller program[06] -> 0xBE93 (PC: 0xC0A198)
			//  - DMA controller program[08] -> 0xDABE (PC: 0xC0A19A)
			//  - DMA controller program[10] -> 0xF62D (PC: 0xC0A19C)
			//  - DMA controller program[12] -> 0x02FD (PC: 0xC0A19E)
			//  - DMA controller program[14] -> 0xFFFF (PC: 0xC0A1A0)

			seek_idle(m_dma_count * 1);

			while (m_dma_count--)
			{
				curr_space.write_word(m_dma_address2 + 0, curr_space.read_byte(m_dma_address1 + 0));
				curr_space.write_word(m_dma_address2 + 2, curr_space.read_byte(m_dma_address1 + 1));
				m_dma_address1 += 2;
				m_dma_address2 += 4;
			}

			break;
		}

		case 0xfc2d:
		{
//          bprintf(PRINT_NORMAL, _T("    copy: 0x%08X - 0x%08X <- LC8951 external buffer, skip odd bytes\n"), m_dma_address1, m_dma_address1 + m_dma_count * 4);

			//  - DMA controller 0x7E -> 0xFC2D (PC: 0xC0A190)
			//  - DMA controller program[00] -> 0xFCF5 (PC: 0xC0A192)
			//  - DMA controller program[02] -> 0x8492 (PC: 0xC0A194)
			//  - DMA controller program[04] -> 0xDA92 (PC: 0xC0A196)
			//  - DMA controller program[06] -> 0xDAF6 (PC: 0xC0A198)
			//  - DMA controller program[08] -> 0x2A02 (PC: 0xC0A19A)
			//  - DMA controller program[10] -> 0xFDFF (PC: 0xC0A19C)
			//  - DMA controller program[12] -> 0x48E7 (PC: 0xC0A19E)
			//  - DMA controller program[14] -> 0xFFFE (PC: 0xC0A1A0)

			char* data = m_tempcdc->LC8915InitTransfer(m_dma_count);
			if (data == nullptr)
			{
				break;
			}

			seek_idle(m_dma_count * 4);

			while (m_dma_count--)
			{
				curr_space.write_byte(m_dma_address1 + 0, data[0]);
				curr_space.write_byte(m_dma_address1 + 2, data[1]);
				m_dma_address1 += 4;
				data += 2;
			}

			m_tempcdc->LC8915EndTransfer();

			break;
		}

		case 0xfe3d:

			//  - DMA controller 0x7E -> 0xFE3D (PC: 0xC0A190)
			//  - DMA controller program[00] -> 0xFCF5 (PC: 0xC0A192)
			//  - DMA controller program[02] -> 0x82BF (PC: 0xC0A194)
			//  - DMA controller program[04] -> 0x93BF (PC: 0xC0A196)
			//  - DMA controller program[06] -> 0xF629 (PC: 0xC0A198)
			//  - DMA controller program[08] -> 0x02FD (PC: 0xC0A19A)
			//  - DMA controller program[10] -> 0xFFFF (PC: 0xC0A19C)
			//  - DMA controller program[12] -> 0xF17D (PC: 0xC0A19E)
			//  - DMA controller program[14] -> 0xFCF5 (PC: 0xC0A1A0)

		case 0xfe6d:
		{
//          bprintf(PRINT_NORMAL, _T("    copy: 0x%08X - 0x%08X <- 0x%08X - 0x%08X\n"), m_dma_address2, m_dma_address2 + m_dma_count * 2, m_dma_address1, m_dma_address1 + m_dma_count * 2);

			//  - DMA controller 0x7E -> 0xFE6D (PC: 0xC0FD7A)
			//  - DMA controller program[00] -> 0xFCF5 (PC: 0xC0FD7C)
			//  - DMA controller program[02] -> 0x82BF (PC: 0xC0FD7E)
			//  - DMA controller program[04] -> 0xF693 (PC: 0xC0FD80)
			//  - DMA controller program[06] -> 0xBF29 (PC: 0xC0FD82)
			//  - DMA controller program[08] -> 0x02FD (PC: 0xC0FD84)
			//  - DMA controller program[10] -> 0xFFFF (PC: 0xC0FD86)
			//  - DMA controller program[12] -> 0xC515 (PC: 0xC0FD88)
			//  - DMA controller program[14] -> 0xFCF5 (PC: 0xC0FD8A)

			seek_idle(m_dma_count * 1);

			while (m_dma_count--)
			{
				curr_space.write_word(m_dma_address2, curr_space.read_word(m_dma_address1));
				m_dma_address1 += 2;
				m_dma_address2 += 2;
			}

			if (m_dma_address2 == 0x0800)
			{
			// MapVectorTable(false);
			//  bprintf(PRINT_ERROR, _T("    RAM vectors mapped (PC = 0x%08X\n"), SekGetPC(0));
			//  extern int32_t bRunPause;
			//  bRunPause = 1;
			}
			break;
		}

		case 0xfef5:
		{
//          bprintf(PRINT_NORMAL, _T("    adr : 0x%08X - 0x%08X <- address\n"), m_dma_address1, m_dma_address1 + m_dma_count * 4);

			//  - DMA controller 0x7E -> 0xFEF5 (PC: 0xC07CE2)
			//  - DMA controller program[00] -> 0xFCF5 (PC: 0xC07CE8)
			//  - DMA controller program[02] -> 0x92E8 (PC: 0xC07CEE)
			//  - DMA controller program[04] -> 0xBE96 (PC: 0xC07CF4)
			//  - DMA controller program[06] -> 0xF629 (PC: 0xC07CFA)
			//  - DMA controller program[08] -> 0x02FD (PC: 0xC07D00)
			//  - DMA controller program[10] -> 0xFFFF (PC: 0xC07D06)
			//  - DMA controller program[12] -> 0xFC3D (PC: 0xC07D0C)
			//  - DMA controller program[14] -> 0xFCF5 (PC: 0xC07D12)

			seek_idle(m_dma_count * 2);

			while (m_dma_count--)
			{
				curr_space.write_word(m_dma_address1 + 0, m_dma_address1 >> 16);
				curr_space.write_word(m_dma_address1 + 2, m_dma_address1 >>  0);
				m_dma_address1 += 4;
			}

			break;
		}

		case 0xffc5:
		{
//          bprintf(PRINT_NORMAL, _T("    copy: 0x%08X - 0x%08X <- LC8951 external buffer\n"), m_dma_address1, m_dma_address1 + m_dma_count * 2);

			//  - DMA controller 0x7E -> 0xFFC5 (PC: 0xC0A190)
			//  - DMA controller program[00] -> 0xFCF5 (PC: 0xC0A192)
			//  - DMA controller program[02] -> 0xA6F6 (PC: 0xC0A194)
			//  - DMA controller program[04] -> 0x2602 (PC: 0xC0A196)
			//  - DMA controller program[06] -> 0xFDFF (PC: 0xC0A198)
			//  - DMA controller program[08] -> 0xFC2D (PC: 0xC0A19A)
			//  - DMA controller program[10] -> 0xFCF5 (PC: 0xC0A19C)
			//  - DMA controller program[12] -> 0x8492 (PC: 0xC0A19E)
			//  - DMA controller program[14] -> 0xDA92 (PC: 0xC0A1A0)

			char* data = m_tempcdc->LC8915InitTransfer(m_dma_count);
			if (data == nullptr)
			{
				break;
			}

			seek_idle(m_dma_count * 4);

			while (m_dma_count--)
			{
				curr_space.write_byte(m_dma_address1 + 0, data[0]);
				curr_space.write_byte(m_dma_address1 + 1, data[1]);
				m_dma_address1 += 2;
				data += 2;
			}

			m_tempcdc->LC8915EndTransfer();

			break;
		}

		case 0xffcd:

			//  - DMA controller 0x7E -> 0xFFCD (PC: 0xC0A190)
			//  - DMA controller program[00] -> 0xFCF5 (PC: 0xC0A192)
			//  - DMA controller program[02] -> 0x92F6 (PC: 0xC0A194)
			//  - DMA controller program[04] -> 0x2602 (PC: 0xC0A196)
			//  - DMA controller program[06] -> 0xFDFF (PC: 0xC0A198)
			//  - DMA controller program[08] -> 0x7006 (PC: 0xC0A19A)
			//  - DMA controller program[10] -> 0x6100 (PC: 0xC0A19C)
			//  - DMA controller program[12] -> 0x2412 (PC: 0xC0A19E)
			//  - DMA controller program[14] -> 0x13FC (PC: 0xC0A1A0)

		case 0xffdd:
		{
//          bprintf(PRINT_NORMAL, _T("    Fill: 0x%08X - 0x%08X <- 0x%04X\n"), m_dma_address1, m_dma_address1 + m_dma_count * 2, m_dma_value1);

			//  - DMA controller 0x7E -> 0xFFDD (PC: 0xC07CE2)
			//  - DMA controller program[00] -> 0xFCF5 (PC: 0xC07CE8)
			//  - DMA controller program[02] -> 0x92F6 (PC: 0xC07CEE)
			//  - DMA controller program[04] -> 0x2602 (PC: 0xC07CF4)
			//  - DMA controller program[06] -> 0xFDFF (PC: 0xC07CFA)
			//  - DMA controller program[08] -> 0xFFFF (PC: 0xC07D00)
			//  - DMA controller program[10] -> 0xFCF5 (PC: 0xC07D06)
			//  - DMA controller program[12] -> 0x8AF0 (PC: 0xC07D0C)
			//  - DMA controller program[14] -> 0x1609 (PC: 0xC07D12)

			seek_idle(m_dma_count * 1);

			while (m_dma_count--)
			{
				curr_space.write_word(m_dma_address1, m_dma_value1);
				m_dma_address1 += 2;
			}

			break;
		}
		default:
		{
			//bprintf(PRINT_ERROR, _T("    Unknown transfer type 0x%04X (PC: 0x%06X)\n"), m_dma_mode, SekGetPC(-1));
			//bprintf(PRINT_NORMAL, _T("    ??? : 0x%08X  0x%08X 0x%04X 0x%04X 0x%08X\n"), m_dma_address1, m_dma_address2, m_dma_value1, m_dma_value2, m_dma_count);

			//extern int32_t bRunPause;
			//bRunPause = 1;

		}
	}
}


/*************************************
 *
 *  Machine initialization
 *
 *************************************/

void ngcd_state::machine_start()
{
	aes_base_state::machine_start();

	// set curr_slot to 0, so to allow checking m_slots[m_curr_slot] != nullptr
	m_curr_slot = 0;

	// initialize sprite to point to memory regions
	m_sprite_ram = make_unique_clear<uint8_t[]>(0x400000);
	m_fix_ram = make_unique_clear<uint8_t[]>(0x20000);
	save_pointer(NAME(m_sprite_ram), 0x400000);
	save_pointer(NAME(m_fix_ram), 0x20000);

	m_sprgen->set_sprite_region(m_sprite_ram.get(), 0x400000);
	m_sprgen->set_fixed_regions(m_fix_ram.get(), 0x20000, nullptr);
	m_sprgen->set_fixed_layer_source(1);

	m_vblank_level = 1;
	m_raster_level = 3;

	// initialize the memcard data structure
	// NeoCD doesn't have memcard slots, rather, it has a larger internal memory which works the same
	m_meminternal_data = make_unique_clear<uint8_t[]>(0x2000);
	subdevice<nvram_device>("saveram")->set_base(m_meminternal_data.get(), 0x2000);
	save_pointer(NAME(m_meminternal_data), 0x2000);

	m_tempcdc->reset_cd();

	save_item(NAME(m_active_transfer_area));
	save_item(NAME(m_sprite_transfer_bank));
	save_item(NAME(m_adpcm_transfer_bank));
	save_item(NAME(m_dma_address1));
	save_item(NAME(m_dma_address2));
	save_item(NAME(m_dma_value1));
	save_item(NAME(m_dma_value2));
	save_item(NAME(m_dma_count));
	save_item(NAME(m_dma_mode));
	save_item(NAME(m_irq_ack));
	save_item(NAME(m_irq_vector_ack));
	save_item(NAME(m_irq_vector));

	save_item(NAME(m_has_sprite_bus));
	save_item(NAME(m_has_text_bus));
	save_item(NAME(m_has_ymrom_bus));
	save_item(NAME(m_has_z80_bus));

	save_item(NAME(m_transfer_write_enable));

	save_item(NAME(prohibit_cdc_irq));
}


/*************************************
 *
 *  Machine reset
 *
 *************************************/

void ngcd_state::machine_reset()
{
	aes_base_state::machine_reset();

	m_tempcdc->NeoCDCommsReset();

	m_audiocpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
	m_audiocpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);

	m_transfer_write_enable = 0;
}


/*************************************
 *
 *  Main CPU memory handlers
 *
 *************************************/

void ngcd_state::neocd_main_map(address_map &map)
{
	aes_base_main_map(map);

	map(0x000000, 0x1fffff).ram().share("maincpu");
	map(0x000000, 0x00007f).r(FUNC(ngcd_state::banked_vectors_r)); // writes will fall through to area above

	map(0x800000, 0x803fff).rw(FUNC(ngcd_state::memcard_r), FUNC(ngcd_state::memcard_w));
	map(0xc00000, 0xc7ffff).mirror(0x080000).rom().region("mainbios", 0);
	map(0xd00000, 0xdfffff).r(FUNC(ngcd_state::unmapped_r));
	map(0xe00000, 0xefffff).rw(FUNC(ngcd_state::transfer_r), FUNC(ngcd_state::transfer_w));
	map(0xf00000, 0xfeffff).r(FUNC(ngcd_state::unmapped_r));
	map(0xff0000, 0xff01ff).rw(FUNC(ngcd_state::control_r), FUNC(ngcd_state::control_w)); // CDROM / DMA
	map(0xff0200, 0xffffff).r(FUNC(ngcd_state::unmapped_r));
}

void ngcd_state::neocd_vector_map(address_map &map)
{
	map(0xfffff0, 0xffffff).m(m_maincpu, FUNC(m68000_base_device::autovectors_map));

	map(0xfffff3, 0xfffff3).lr8(NAME([]() { return m68000_base_device::autovector(2); }));
	map(0xfffff5, 0xfffff5).r(FUNC(ngcd_state::cdc_irq_ack));
	map(0xfffff7, 0xfffff7).lr8(NAME([]() { return m68000_base_device::autovector(1); }));
}


/*************************************
 *
 *  Audio CPU port handlers
 *
 *************************************/


void ngcd_state::neocd_audio_map(address_map &map)
{
	map(0x0000, 0xffff).ram().share(m_z80_ram);
}


void ngcd_state::neocd_audio_io_map(address_map &map)
{
	map(0x00, 0x00).mirror(0xff00).rw(m_soundlatch, FUNC(generic_latch_8_device::read), FUNC(generic_latch_8_device::clear_w));
	map(0x04, 0x07).mirror(0xff00).rw(m_ym, FUNC(ym2610_device::read), FUNC(ym2610_device::write));
	map(0x08, 0x08).mirror(0xff00).select(0x0010).w(FUNC(ngcd_state::audio_cpu_enable_nmi_w));
	// banking reads are actually NOP on NeoCD? but some games still access them
//  map(0x08, 0x0b).mirror(0x00f0).select(0xff00).r(FUNC(ngcd_state::audio_cpu_bank_select_r));
	map(0x0c, 0x0c).mirror(0xff00).w(m_soundlatch2, FUNC(generic_latch_8_device::write));

	// ??
	map(0x80, 0x80).mirror(0xff00).nopw();
	map(0xc0, 0xc0).mirror(0xff00).nopw();
	map(0xc1, 0xc1).mirror(0xff00).nopw();
}

void ngcd_state::neocd_ym_map(address_map &map)
{
	map(0x000000, 0x0fffff).ram().share(m_adpcm_ram);
}


/*************************************
 *
 *  Input port definitions
 *
 *************************************/

static INPUT_PORTS_START( neocd )
	PORT_INCLUDE( aes )

	PORT_MODIFY("IN2")
	PORT_BIT( 0x7000, IP_ACTIVE_HIGH, IPT_UNUSED ) // the NeoCD memcard is internal
INPUT_PORTS_END



/*************************************
 *
 *  Machine driver
 *
 *************************************/


/* NeoCD uses custom vectors on IRQ4 to handle various events from the CDC */

uint8_t ngcd_state::cdc_irq_ack()
{
	if (get_irq_vector_ack())
	{
		if (!machine().side_effects_disabled())
			set_irq_vector_ack(0);
		return get_irq_vector();
	}

	return (0x60+4*4)/4;
}

void ngcd_state::interrupt_callback_type1()
{
	m_irq_ack &= ~0x20;
	irq_update(0);
}

void ngcd_state::interrupt_callback_type2()
{
	m_irq_ack &= ~0x10;
	irq_update(0);
}

void ngcd_state::interrupt_callback_type3()
{
	m_irq_ack &= ~0x08;
	irq_update(0);
}


void ngcd_state::irq_update(uint8_t data)
{
	// do we also need to check the regular interrupts like FBA?

	m_irq_ack |= (data & 0x38);

	if (!prohibit_cdc_irq)
	{
		if ((m_irq_ack & 0x08) == 0)
		{
			m_irq_vector = 0x17;
			m_irq_vector_ack = 1;
			m_maincpu->set_input_line(2, HOLD_LINE);
			return;
		}
		if ((m_irq_ack & 0x10) == 0)
		{
			m_irq_vector = 0x16;
			m_irq_vector_ack = 1;
			m_maincpu->set_input_line(2, HOLD_LINE);
			return;
		}
		if ((m_irq_ack & 0x20) == 0)
		{
			m_irq_vector = 0x15;
			m_irq_vector_ack = 1;
			m_maincpu->set_input_line(2, HOLD_LINE);
			return;
		}
	}
}


uint32_t ngcd_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	// fill with background color first
	bitmap.fill(*m_bg_pen, cliprect);

	if (m_has_sprite_bus) m_sprgen->draw_sprites(bitmap, cliprect.min_y);

	if (m_has_text_bus) m_sprgen->draw_fixed_layer(bitmap, cliprect.min_y);

	return 0;
}

// NTSC region
void ngcd_state::neocd_ntsc(machine_config &config)
{
	neogeo_base(config);
	neogeo_stereo(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &ngcd_state::neocd_main_map);
	m_maincpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &ngcd_state::neocd_vector_map);

	m_audiocpu->set_addrmap(AS_PROGRAM, &ngcd_state::neocd_audio_map);
	m_audiocpu->set_addrmap(AS_IO, &ngcd_state::neocd_audio_io_map);

	// what IS going on with "neocdz doubledr" and why do games write here if it's hooked up to nothing?
	subdevice<hc259_device>("systemlatch")->q_out_cb<1>().set([this](int state) { logerror("%s NeoCD: write %d to regular vector change address?\n", machine().describe_context(), state); });

	m_screen->set_screen_update(FUNC(ngcd_state::screen_update));

	// temporary until things are cleaned up
	LC89510_TEMP(config, m_tempcdc, 0); // cd controller
	m_tempcdc->set_cdrom_tag("cdrom");
	m_tempcdc->set_is_neoCD(true);
	m_tempcdc->set_type1_interrupt_callback(FUNC(ngcd_state::interrupt_callback_type1));
	m_tempcdc->set_type2_interrupt_callback(FUNC(ngcd_state::interrupt_callback_type2));
	m_tempcdc->set_type3_interrupt_callback(FUNC(ngcd_state::interrupt_callback_type3));

	NVRAM(config, "saveram", nvram_device::DEFAULT_ALL_0);

	NEOGEO_CONTROL_PORT(config, m_ctrl1, neogeo_controls, "joy", false);
	NEOGEO_CONTROL_PORT(config, m_ctrl2, neogeo_controls, "joy", false);

	CDROM(config, "cdrom").set_interface("cdrom");
	SOFTWARE_LIST(config, "cd_list").set_original("neocd");

	m_ym->set_addrmap(0, &ngcd_state::neocd_ym_map);
}



/*************************************
 *
 *  Driver initialization
 *
 *************************************/

ROM_START( neocd )
	ROM_REGION16_BE( 0x80000, "mainbios", 0 )
	ROM_SYSTEM_BIOS( 0, "top",   "Top loading Neo-Geo CD" )
	ROMX_LOAD("top-sp1.bin",    0x00000, 0x80000, CRC(c36a47c0) SHA1(235f4d1d74364415910f73c10ae5482d90b4274f), ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "topproto",   "Top loading Neo-Geo CD (prototype, ver 0.02)" )
	ROMX_LOAD("prototype_neocd_ver_0.02.bin", 0x00000, 0x80000, CRC(0f348e10) SHA1(58f966724778d56c9c099c452e33e2264b5e1c0e), ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(1)) // tc574200d
	ROM_SYSTEM_BIOS( 2, "front",   "Front loading Neo-Geo CD" )
	ROMX_LOAD("front-sp1.bin",    0x00000, 0x80000, CRC(cac62307) SHA1(53bc1f283cdf00fa2efbb79f2e36d4c8038d743a), ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "unibios32", "Universe BIOS (Hack, Ver. 3.2)" )
	ROMX_LOAD("uni-bioscd32.rom",    0x00000, 0x80000, CRC(0ffb3127) SHA1(5158b728e62b391fb69493743dcf7abbc62abc82), ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(3))
	ROM_SYSTEM_BIOS( 4, "unibios33", "Universe BIOS (Hack, Ver. 3.3)" )
	ROMX_LOAD("uni-bioscd33.rom",    0x00000, 0x80000, CRC(ff3abc59) SHA1(5142f205912869b673a71480c5828b1eaed782a8), ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(4))

	ROM_REGION( 0x20000, "spritegen:zoomy", 0 )
	ROM_LOAD( "000-lo.lo", 0x00000, 0x20000, CRC(5a86cff2) SHA1(5992277debadeb64d1c1c64b0a92d9293eaf7e4a) )
ROM_END

ROM_START( neocdz )
	ROM_REGION16_BE( 0x80000, "mainbios", 0 )
	ROM_SYSTEM_BIOS( 0, "official",   "Official BIOS" )
	ROMX_LOAD("neocd.bin",    0x00000, 0x80000, CRC(df9de490) SHA1(7bb26d1e5d1e930515219cb18bcde5b7b23e2eda), ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "unibios32", "Universe BIOS (Hack, Ver. 3.2)" )
	ROMX_LOAD("uni-bioscd32.rom",    0x00000, 0x80000, CRC(0ffb3127) SHA1(5158b728e62b391fb69493743dcf7abbc62abc82), ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "unibios33", "Universe BIOS (Hack, Ver. 3.3)" )
	ROMX_LOAD("uni-bioscd33.rom",    0x00000, 0x80000, CRC(ff3abc59) SHA1(5142f205912869b673a71480c5828b1eaed782a8), ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(2))

	ROM_REGION( 0x20000, "spritegen:zoomy", 0 )
	ROM_LOAD( "000-lo.lo", 0x00000, 0x20000, CRC(5a86cff2) SHA1(5992277debadeb64d1c1c64b0a92d9293eaf7e4a) )
ROM_END

#define rom_neocdzj    rom_neocdz

void ngcd_state::init_neocdz()
{
	m_system_region = NEOCD_REGION_US;
}

void ngcd_state::init_neocdzj()
{
	m_system_region = NEOCD_REGION_JAPAN;
}

} // anonymous namespace


//    YEAR  NAME     PARENT  COMPAT  MACHINE     INPUT   CLASS        INIT          COMPANY FULLNAME               FLAGS */
CONS( 1996, neocdz,  0,      0,      neocd_ntsc, neocd,  ngcd_state,  init_neocdz,  "SNK",  "Neo-Geo CDZ (US)",    0 ) // the CDZ is the newer model
CONS( 1996, neocdzj, neocdz, 0,      neocd_ntsc, neocd,  ngcd_state,  init_neocdzj, "SNK",  "Neo-Geo CDZ (Japan)", 0 )

// NTSC region?
CONS( 1994, neocd,   neocdz, 0,      neocd_ntsc, neocd,  ngcd_state,  empty_init,   "SNK",  "Neo-Geo CD (NTSC?)",  MACHINE_NOT_WORKING ) // older  model, ignores disc protections?



nes.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Brad Oliver,Fabio Priuli
/***************************************************************************

  nes.cpp

  Driver file to handle emulation of the Nintendo Entertainment System (Famicom).

  MESS driver by Brad Oliver (bradman@pobox.com), NES sound code by Matt Conte.
  Based in part on the old xNes code, by Nicolas Hamel, Chuck Mason, Brad Oliver,
  Richard Bannister and Jeff Mitchell.

***************************************************************************/

#include "emu.h"

#include "bus/nes/disksys.h"
#include "bus/nes/nes_slot.h"
#include "bus/nes/nes_carts.h"
#include "bus/nes_ctrl/ctrl.h"
#include "cpu/m6502/rp2a03.h"
#include "video/ppu2c0x.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

namespace {

/***************************************************************************
    TYPE DEFINITIONS
***************************************************************************/

class nes_base_state : public driver_device
{
public:
	nes_base_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ctrl1(*this, "ctrl1"),
		m_ctrl2(*this, "ctrl2")
	{ }

	required_device<cpu_device> m_maincpu;
	optional_device<nes_control_port_device> m_ctrl1;
	optional_device<nes_control_port_device> m_ctrl2;

	uint8_t nes_in0_r();
	uint8_t nes_in1_r();
	void nes_in0_w(uint8_t data);
};

class nes_state : public nes_base_state
{
public:
	nes_state(const machine_config &mconfig, device_type type, const char *tag) :
		nes_base_state(mconfig, type, tag),
		m_mainram(*this, "mainram"),
		m_ppu(*this, "ppu"),
		m_screen(*this, "screen"),
		m_exp(*this, "exp"),
		m_special(*this, "special"),
		m_cartslot(*this, "nes_slot"),
		m_disk(*this, "disk"),
		m_prg_bank(*this, "prg%u", 0U)
	{ }

	uint8_t fc_in0_r();
	uint8_t fc_in1_r();
	void fc_in0_w(uint8_t data);
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;
	uint32_t screen_update_nes(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void screen_vblank_nes(int state);

	void init_famicom();

	// these are needed until we modernize the FDS controller
	DECLARE_MACHINE_START(fds);
	DECLARE_MACHINE_START(famitwin);
	DECLARE_MACHINE_RESET(fds);
	DECLARE_MACHINE_RESET(famitwin);
	DECLARE_MACHINE_RESET(famitvc1);
	void setup_disk(nes_disksys_device *slot);

	void suborkbd(machine_config &config);
	void famipalc(machine_config &config);
	void famicom(machine_config &config);
	void famicomo(machine_config &config);
	void famitvc1(machine_config &config);
	void famitwin(machine_config &config);
	void fctitler(machine_config &config);
	void nespal(machine_config &config);
	void nespalc(machine_config &config);
	void nes(machine_config &config);
	void fds(machine_config &config);
	void nes_map(address_map &map) ATTR_COLD;

private:
	// video-related
	int m_last_frame_flip = 0;

	// misc
	ioport_port       *m_io_disksel = nullptr;

	std::unique_ptr<uint8_t[]>    m_ciram; // PPU nametable RAM - external to PPU!

	required_shared_ptr<uint8_t> m_mainram;

	required_device<ppu2c0x_device> m_ppu;
	required_device<screen_device> m_screen;
	optional_device<nes_control_port_device> m_exp;
	optional_device<nes_control_port_device> m_special;
	optional_device<nes_cart_slot_device> m_cartslot;
	optional_device<nes_disksys_device> m_disk;
	memory_bank_array_creator<4> m_prg_bank;
};

/***************************************************************************
    FUNCTIONS
***************************************************************************/

//-------------------------------------------------
//  machine_reset
//-------------------------------------------------

void nes_state::machine_reset()
{
	// Reset the mapper variables. Will also mark the char-gen ram as dirty
	if (m_cartslot)
		m_cartslot->pcb_reset();

	m_maincpu->reset();
}

//-------------------------------------------------
//  machine_start
//-------------------------------------------------

void nes_state::machine_start()
{
	address_space &space = m_maincpu->space(AS_PROGRAM);

	// Fill main RAM with an arbitrary pattern (alternating 0x00/0xff) for software that depends on its contents at boot up (tsk tsk!)
	// The fill value is a compromise since certain games malfunction with zero-filled memory, others with one-filled memory
	// Examples: Minna no Taabou won't boot with all 0x00, Sachen's Dancing Block won't boot with all 0xff, Terminator 2 skips its copyright screen with all 0x00
	for (int i = 0; i < 0x800; i += 2)
	{
		m_mainram[i] = 0x00;
		m_mainram[i + 1] = 0xff;
	}

	// CIRAM (Character Internal RAM)
	// NES has 2KB of internal RAM which can be used to fill the 4x1KB banks of PPU RAM at $2000-$2fff
	// Line A10 is exposed to the carts, so that games can change CIRAM mapping in PPU (we emulate this with the set_nt_mirroring
	// function). CIRAM can also be disabled by the game (if e.g. VROM or cart RAM has to be used in PPU...
	m_ciram = std::make_unique<uint8_t[]>(0x800);
	// other pointers got set in the loading routine, because they 'belong' to the cart itself

	m_io_disksel = ioport("FLIPDISK");

	if (m_cartslot && m_cartslot->m_cart)
	{
		// Set up memory handlers
		space.install_read_handler(0x4100, 0x5fff, read8sm_delegate(*m_cartslot, FUNC(nes_cart_slot_device::read_l)));
		space.install_write_handler(0x4100, 0x5fff, write8sm_delegate(*m_cartslot, FUNC(nes_cart_slot_device::write_l)));
		space.install_read_handler(0x6000, 0x7fff, read8sm_delegate(*m_cartslot, FUNC(nes_cart_slot_device::read_m)));
		space.install_write_handler(0x6000, 0x7fff, write8sm_delegate(*m_cartslot, FUNC(nes_cart_slot_device::write_m)));
		for(int i = 0; i < 4; i++)
			space.install_read_bank(0x8000 + 0x2000*i, 0x9fff + 0x2000*i, m_prg_bank[i]);
		space.install_write_handler(0x8000, 0xffff, write8sm_delegate(*m_cartslot, FUNC(nes_cart_slot_device::write_h)));

		m_ppu->space(AS_PROGRAM).install_readwrite_handler(0, 0x1fff, read8sm_delegate(*m_cartslot->m_cart, FUNC(device_nes_cart_interface::chr_r)), write8sm_delegate(*m_cartslot->m_cart, FUNC(device_nes_cart_interface::chr_w)));
		m_ppu->space(AS_PROGRAM).install_readwrite_handler(0x2000, 0x3eff, read8sm_delegate(*m_cartslot->m_cart, FUNC(device_nes_cart_interface::nt_r)), write8sm_delegate(*m_cartslot->m_cart, FUNC(device_nes_cart_interface::nt_w)));
		m_ppu->set_scanline_callback(*m_cartslot->m_cart, FUNC(device_nes_cart_interface::scanline_irq));
		m_ppu->set_hblank_callback(*m_cartslot->m_cart, FUNC(device_nes_cart_interface::hblank_irq));
		m_ppu->set_latch(*m_cartslot->m_cart, FUNC(device_nes_cart_interface::ppu_latch));

		// install additional handlers (read_h, read_ex, write_ex)
		static const int r_h_pcbs[] =
		{
			AVE_MAXI15,
			BANDAI_DATACH,
			BANDAI_KARAOKE,
			BATMAP_SRRX,
			BMC_70IN1,
			BMC_800IN1,
			BMC_8157,
			BMC_970630C,
			BMC_DS927,
			BMC_KC885,
			BMC_TELETUBBIES,
			BMC_VT5201,
			BTL_PALTHENA,
			CAMERICA_ALADDIN,
			GG_NROM,
			KAISER_KS7010,
			KAISER_KS7022,
			KAISER_KS7030,
			KAISER_KS7031,
			KAISER_KS7037,
			KAISER_KS7057,
			SACHEN_3013,
			SACHEN_3014,
			STD_DISKSYS,
			STD_EXROM,
			STD_NROM368,
			SUNSOFT_DCS,
			UNL_2708,
			UNL_2A03PURITANS,
			UNL_43272,
			UNL_EH8813A,
			UNL_LH10,
			UNL_LH32,
			UNL_RT01
		};

		static const int w_ex_pcbs[] =
		{
			BMC_N32_4IN1,
			BTL_SMB2JB,
			BTL_YUNG08,
			UNL_AC08,
			UNL_SMB2J
		};

		static const int rw_ex_pcbs[] =
		{
			BTL_09034A,
			KAISER_KS7017,
			STD_DISKSYS,
			UNL_603_5052
		};

		int pcb_id = m_cartslot->get_pcb_id();

		if (std::find(std::begin(r_h_pcbs), std::end(r_h_pcbs), pcb_id) != std::end(r_h_pcbs))
		{
			logerror("read_h installed!\n");
			space.install_read_handler(0x8000, 0xffff, read8sm_delegate(*m_cartslot, FUNC(nes_cart_slot_device::read_h)));
		}

		if (std::find(std::begin(w_ex_pcbs), std::end(w_ex_pcbs), pcb_id) != std::end(w_ex_pcbs))
		{
			logerror("write_ex installed!\n");
			space.install_write_handler(0x4020, 0x40ff, write8sm_delegate(*m_cartslot, FUNC(nes_cart_slot_device::write_ex)));
		}

		if (std::find(std::begin(rw_ex_pcbs), std::end(rw_ex_pcbs), pcb_id) != std::end(rw_ex_pcbs))
		{
			logerror("read_ex & write_ex installed!\n");
			space.install_read_handler(0x4020, 0x40ff, read8sm_delegate(*m_cartslot, FUNC(nes_cart_slot_device::read_ex)));
			space.install_write_handler(0x4020, 0x40ff, write8sm_delegate(*m_cartslot, FUNC(nes_cart_slot_device::write_ex)));
		}

		m_cartslot->pcb_start(m_ciram.get());
		m_cartslot->m_cart->pcb_reg_postload(machine());
	}

	// register saves
	save_item(NAME(m_last_frame_flip));
	save_pointer(NAME(m_ciram), 0x800);
}


//-------------------------------------------------
//  INPUTS
//-------------------------------------------------

uint8_t nes_base_state::nes_in0_r()
{
	uint8_t ret = 0x40;
	ret |= m_ctrl1->read_bit0();
	ret |= m_ctrl1->read_bit34();
	return ret;
}

uint8_t nes_base_state::nes_in1_r()
{
	uint8_t ret = 0x40;
	ret |= m_ctrl2->read_bit0();
	ret |= m_ctrl2->read_bit34();
	return ret;
}

void nes_base_state::nes_in0_w(uint8_t data)
{
	m_ctrl1->write(data);
	m_ctrl2->write(data);
}


uint8_t nes_state::fc_in0_r()
{
	uint8_t ret = 0x40;
	// bit 0 from controller port
	ret |= m_ctrl1->read_bit0();

	// bit 2 from P2 controller microphone
	ret |= m_ctrl2->read_bit2();

	// and bit 1 comes from expansion port
	ret |= m_exp->read_exp(0);
	return ret;
}

uint8_t nes_state::fc_in1_r()
{
	uint8_t ret = 0x40;
	// bit 0 from controller port
	ret |= m_ctrl2->read_bit0();

	// bits 1-4 from expansion port (in theory bit 0 also can be read on AV Famicom when controller is unplugged)
	ret |= m_exp->read_exp(1);
	return ret;
}

void nes_state::fc_in0_w(uint8_t data)
{
	m_ctrl1->write(data);
	m_ctrl2->write(data);
	m_exp->write(data);
}


void nes_state::init_famicom()
{
	// setup alt input handlers for additional FC input devices
	address_space &space = m_maincpu->space(AS_PROGRAM);
	space.install_read_handler(0x4016, 0x4016, read8smo_delegate(*this, FUNC(nes_state::fc_in0_r)));
	space.install_write_handler(0x4016, 0x4016, write8smo_delegate(*this, FUNC(nes_state::fc_in0_w)));
	space.install_read_handler(0x4017, 0x4017, read8smo_delegate(*this, FUNC(nes_state::fc_in1_r)));
}


void nes_state::video_start()
{
	m_last_frame_flip =  0;
}


/***************************************************************************

  Display refresh

***************************************************************************/

uint32_t nes_state::screen_update_nes(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	// render the ppu
	m_ppu->render(bitmap, 0, 0, 0, 0, cliprect);
	return 0;
}

void nes_state::screen_vblank_nes(int state)
{
	// on rising edge
	if (!state)
	{
		// if this is a disk system game, check for the flip-disk key
		if ((m_cartslot && m_cartslot->exists() && (m_cartslot->get_pcb_id() == STD_DISKSYS))   // first scenario = disksys in m_cartslot (= famicom)
				|| m_disk)  // second scenario = disk via fixed internal disk option (fds & famitwin)
		{
			if (m_io_disksel)
			{
				// latch this input so it doesn't go at warp speed
				if ((m_io_disksel->read() & 0x01) && (!m_last_frame_flip))
				{
					if (m_disk)
						m_disk->disk_flip_side();
					else
						m_cartslot->disk_flip_side();
					m_last_frame_flip = 1;
				}

				if (!(m_io_disksel->read() & 0x01))
					m_last_frame_flip = 0;
			}
		}
	}
}


void nes_state::nes_map(address_map &map)
{
	map(0x0000, 0x07ff).ram().mirror(0x1800).share("mainram");                              // RAM
	map(0x2000, 0x3fff).rw(m_ppu, FUNC(ppu2c0x_device::read), FUNC(ppu2c0x_device::write)); // PPU registers
	map(0x4014, 0x4014).w(m_ppu, FUNC(ppu2c0x_device::spriteram_dma));                            // stupid address space hole
	map(0x4016, 0x4016).rw(FUNC(nes_state::nes_in0_r), FUNC(nes_state::nes_in0_w));         // IN0 - input port 1
	map(0x4017, 0x4017).r(FUNC(nes_state::nes_in1_r));                                      // IN1 - input port 2
	// 0x4100-0x5fff -> LOW HANDLER defined on a pcb base
	// 0x6000-0x7fff -> MID HANDLER defined on a pcb base
	// 0x8000-0xffff -> HIGH HANDLER defined on a pcb base
}

static INPUT_PORTS_START( nes )
	// input devices go through slot options
INPUT_PORTS_END

static INPUT_PORTS_START( famicom )
	// input devices go through slot options
	PORT_START("FLIPDISK") // fake key
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Change Disk Side") PORT_CODE(KEYCODE_SPACE)
INPUT_PORTS_END


void nes_state::nes(machine_config &config)
{
	// basic machine hardware
	rp2a03_device &maincpu(RP2A03G(config, m_maincpu, NTSC_APU_CLOCK));
	maincpu.set_addrmap(AS_PROGRAM, &nes_state::nes_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60.0988);
	// This isn't used so much to calulate the vblank duration (the PPU code tracks that manually) but to determine
	// the number of cycles in each scanline for the PPU scanline timer. Since the PPU has 20 vblank scanlines + 2
	// non-rendering scanlines, we compensate. This ends up being 2500 cycles for the non-rendering portion, 2273
	// cycles for the actual vblank period.
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC((113.66/(NTSC_APU_CLOCK.dvalue()/1000000)) *
							 (ppu2c0x_device::VBLANK_LAST_SCANLINE_NTSC-ppu2c0x_device::VBLANK_FIRST_SCANLINE+1+2)));
	m_screen->set_size(32*8, 262);
	m_screen->set_visarea(0*8, 32*8-1, 0*8, 30*8-1);
	m_screen->set_screen_update(FUNC(nes_state::screen_update_nes));
	m_screen->screen_vblank().set(FUNC(nes_state::screen_vblank_nes));

	PPU_2C02(config, m_ppu);
	m_ppu->set_cpu_tag(m_maincpu);
	m_ppu->int_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	maincpu.add_route(ALL_OUTPUTS, "mono", 0.90);

	NES_CONTROL_PORT(config, m_ctrl1, nes_control_port1_devices, "joypad").set_screen_tag(m_screen);
	NES_CONTROL_PORT(config, m_ctrl2, nes_control_port2_devices, "joypad").set_screen_tag(m_screen);
	NES_CONTROL_PORT(config, m_special, nes_control_special_devices, nullptr).set_screen_tag(m_screen);

	NES_CART_SLOT(config, m_cartslot, NTSC_APU_CLOCK, nes_cart, nullptr).set_must_be_loaded(true);
	SOFTWARE_LIST(config, "cart_list").set_original("nes");
	SOFTWARE_LIST(config, "ade_list").set_original("nes_ade");         // Camerica/Codemasters Aladdin Deck Enhancer mini-carts
	SOFTWARE_LIST(config, "ntb_list").set_original("nes_ntbrom");      // Sunsoft Nantettate! Baseball mini-carts
	SOFTWARE_LIST(config, "kstudio_list").set_original("nes_kstudio"); // Bandai Karaoke Studio expansion carts
	SOFTWARE_LIST(config, "datach_list").set_original("nes_datach");   // Bandai Datach Joint ROM System mini-carts
	SOFTWARE_LIST(config, "famibox_list").set_compatible("famibox");   // FamicomBox/FamicomStation carts
}

void nes_state::nespal(machine_config &config)
{
	nes(config);

	// basic machine hardware
	m_maincpu->set_clock(PAL_APU_CLOCK);

	PPU_2C07(config.replace(), m_ppu);
	m_ppu->set_cpu_tag(m_maincpu);
	m_ppu->int_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);

	m_cartslot->set_clock(PAL_APU_CLOCK);

	// video hardware
	m_screen->set_refresh_hz(50.0070);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC((106.53/(PAL_APU_CLOCK.dvalue()/1000000)) *
							 (ppu2c0x_device::VBLANK_LAST_SCANLINE_PAL-ppu2c0x_device::VBLANK_FIRST_SCANLINE+1+2)));
	m_screen->set_size(32*8, 312);
	m_screen->set_visarea(0*8, 32*8-1, 0*8, 30*8-1);
}

void nes_state::famicom(machine_config &config)
{
	nes(config);

	NES_CONTROL_PORT(config.replace(), m_ctrl1, fc_control_port1_devices, "joypad").set_screen_tag(m_screen);
	NES_CONTROL_PORT(config.replace(), m_ctrl2, fc_control_port2_devices, "joypad").set_screen_tag(m_screen);
	NES_CONTROL_PORT(config, m_exp, fc_expansion_devices, nullptr).set_screen_tag(m_screen);

	SOFTWARE_LIST(config, "flop_list").set_original("famicom_flop");
	SOFTWARE_LIST(config, "cass_list").set_original("famicom_cass");
}

void nes_state::famicomo(machine_config &config)
{
	famicom(config);

	// basic machine hardware
	rp2a03_device &maincpu(RP2A03(config.replace(), m_maincpu, NTSC_APU_CLOCK));
	maincpu.set_addrmap(AS_PROGRAM, &nes_state::nes_map);

	// sound hardware
	maincpu.add_route(ALL_OUTPUTS, "mono", 0.90);
}

void nes_state::nespalc(machine_config &config)
{
	nespal(config);

	m_maincpu->set_clock(PALC_APU_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &nes_state::nes_map);

	// UMC 6538 and friends -- extends time for rendering dummy scanlines
	PPU_PALC(config.replace(), m_ppu);
	m_ppu->set_cpu_tag(m_maincpu);
	m_ppu->int_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);

	m_cartslot->set_clock(PALC_APU_CLOCK);

	// video hardware
	m_screen->set_refresh_hz(50.0070);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC((113.66/(PALC_APU_CLOCK.dvalue()/1000000)) *
							 (ppu2c0x_device::VBLANK_LAST_SCANLINE_PAL-ppu2c0x_device::VBLANK_FIRST_SCANLINE_PALC+1+2)));
}

void nes_state::famipalc(machine_config &config)
{
	nespalc(config);

	NES_CONTROL_PORT(config.replace(), m_ctrl1, fc_control_port1_devices, "joypad").set_screen_tag(m_screen);
	NES_CONTROL_PORT(config.replace(), m_ctrl2, fc_control_port2_devices, "joypad").set_screen_tag(m_screen);
	NES_CONTROL_PORT(config, m_exp, fc_expansion_devices, nullptr).set_screen_tag(m_screen);

	SOFTWARE_LIST(config, "cass_list").set_original("famicom_cass");
}

void nes_state::suborkbd(machine_config &config)
{
	famipalc(config);

	// TODO: emulate the parallel port bus!
	m_exp->set_default_option("subor_keyboard");
	m_exp->set_fixed(true);
}

void nes_state::setup_disk(nes_disksys_device *slot)
{
	if (slot)
	{
		address_space &space = m_maincpu->space(AS_PROGRAM);

		// Set up memory handlers
		space.install_read_handler(0x4020, 0x40ff, read8sm_delegate(*slot, FUNC(nes_disksys_device::read_ex)));
		space.install_write_handler(0x4020, 0x40ff, write8sm_delegate(*slot, FUNC(nes_disksys_device::write_ex)));
		space.install_read_handler(0x4100, 0x5fff, read8sm_delegate(*slot, FUNC(device_nes_cart_interface::read_l)));
		space.install_write_handler(0x4100, 0x5fff, write8sm_delegate(*slot, FUNC(device_nes_cart_interface::write_l)));
		space.install_read_handler(0x6000, 0x7fff, read8sm_delegate(*slot, FUNC(nes_disksys_device::read_m)));
		space.install_write_handler(0x6000, 0x7fff, write8sm_delegate(*slot, FUNC(nes_disksys_device::write_m)));
		space.install_read_handler(0x8000, 0xffff, read8sm_delegate(*slot, FUNC(nes_disksys_device::read_h)));
		space.install_write_handler(0x8000, 0xffff, write8sm_delegate(*slot, FUNC(nes_disksys_device::write_h)));

		slot->vram_alloc(0x2000);
		slot->prgram_alloc(0x8000);

		slot->pcb_start(machine(), m_ciram.get(), false);
		m_ppu->space(AS_PROGRAM).install_readwrite_handler(0, 0x1fff, read8sm_delegate(*slot, FUNC(device_nes_cart_interface::chr_r)), write8sm_delegate(*slot, FUNC(device_nes_cart_interface::chr_w)));
		m_ppu->space(AS_PROGRAM).install_readwrite_handler(0x2000, 0x3eff, read8sm_delegate(*slot, FUNC(device_nes_cart_interface::nt_r)), write8sm_delegate(*slot, FUNC(device_nes_cart_interface::nt_w)));
		m_ppu->set_scanline_callback(*slot, FUNC(device_nes_cart_interface::scanline_irq));
		m_ppu->set_hblank_callback(*slot, FUNC(nes_disksys_device::hblank_irq));
		m_ppu->set_latch(*slot, FUNC(device_nes_cart_interface::ppu_latch));
	}
}

MACHINE_START_MEMBER( nes_state, fds )
{
	m_ciram = std::make_unique<uint8_t[]>(0x800);
	m_io_disksel = ioport("FLIPDISK");
	setup_disk(m_disk);

	// register saves
	save_item(NAME(m_last_frame_flip));
	save_pointer(NAME(m_ciram), 0x800);
}

MACHINE_RESET_MEMBER( nes_state, fds )
{
	// Reset the mapper variables
	m_disk->pcb_reset();

	// the rest is the same as for nes/famicom/dendy
	m_maincpu->reset();
}

void nes_state::fds(machine_config &config)
{
	famicom(config);

	MCFG_MACHINE_START_OVERRIDE(nes_state, fds)
	MCFG_MACHINE_RESET_OVERRIDE(nes_state, fds)

	config.device_remove("nes_slot");
	NES_DISKSYS(config, "disk", NTSC_APU_CLOCK);

	config.device_remove("cart_list");
	config.device_remove("cass_list");
	config.device_remove("ade_list");
	config.device_remove("ntb_list");
	config.device_remove("kstudio_list");
	config.device_remove("datach_list");
	config.device_remove("famibox_list");
}

MACHINE_RESET_MEMBER( nes_state, famitvc1 )
{
	// TODO: supposedly the C1 used the cartridge connector audio pins to detect
	// the presence of a cart and picks the builtin ROM accordingly. If so, the C1
	// should not support Famicom expansion audio chips.

	// Reset the mapper variables. Will also mark the char-gen ram as dirty
	if (m_cartslot->exists())
	{
		m_cartslot->pcb_reset();
	}
	else
	{
		m_maincpu->space(AS_PROGRAM).install_rom(0x8000, 0x9fff, 0x6000, memregion("canvas_prg")->base());
		m_ppu->space(AS_PROGRAM).install_rom(0x0000, 0x1fff, memregion("canvas_chr")->base());
	}

	m_maincpu->reset();
}

void nes_state::famitvc1(machine_config &config)
{
	famicomo(config); // has an RP2A03 like the original Famicom

	MCFG_MACHINE_RESET_OVERRIDE( nes_state, famitvc1 )

	PPU_2C03B(config.replace(), m_ppu);
	m_ppu->set_cpu_tag(m_maincpu);
	m_ppu->int_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);

	m_cartslot->set_must_be_loaded(false);
}

MACHINE_START_MEMBER( nes_state, famitwin )
{
	// start the base nes stuff
	machine_start();

	// if there is no cart inserted, setup the disk expansion instead
	if (!m_cartslot->exists())
	{
		setup_disk(m_disk);

		// replace the famicom disk ROM with the twin famicom one (until we modernize the floppy drive)
		m_maincpu->space(AS_PROGRAM).install_rom(0xe000, 0xffff, memregion("maincpu")->base() + 0xe000);
	}
}

MACHINE_RESET_MEMBER( nes_state, famitwin )
{
	// Reset the mapper variables. Will also mark the char-gen ram as dirty
	m_cartslot->pcb_reset();
	// if there is no cart inserted, initialize the disk expansion instead
	if (!m_cartslot->exists())
		m_disk->pcb_reset();

	// the rest is the same as for nes/famicom/dendy
	m_maincpu->reset();
}

void nes_state::famitwin(machine_config &config)
{
	famicom(config);

	MCFG_MACHINE_START_OVERRIDE( nes_state, famitwin )
	MCFG_MACHINE_RESET_OVERRIDE( nes_state, famitwin )

	m_cartslot->set_must_be_loaded(false);

	NES_DISKSYS(config, "disk", NTSC_APU_CLOCK);
}

void nes_state::fctitler(machine_config &config)
{
	famicom(config);

	// PPU is really RC2C05-99, but it can't be like the other 2C05s since they swap PPUCTRL and PPUMASK registers
	PPU_2C03B(config.replace(), m_ppu);
	m_ppu->set_cpu_tag(m_maincpu);
	m_ppu->int_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
}


ROM_START( nes )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )  // Main RAM
ROM_END

ROM_START( nespal )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )  // Main RAM
ROM_END

ROM_START( famicom )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )  // Main RAM
ROM_END

ROM_START( famicomo )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )  // Main RAM
ROM_END

ROM_START( famitvc1 )
	ROM_REGION( 0x2000, "canvas_prg", 0 )
	ROM_LOAD( "ix0402ce.ic109", 0x0000, 0x2000, CRC(96456b13) SHA1(a4dcb3c4f2be5077f0d197e870a26414287ce189) ) // dump needs verified

	ROM_REGION( 0x2000, "canvas_chr", 0 )
	ROM_LOAD( "ix0403ce.ic110", 0x0000, 0x2000, CRC(9cba4524) SHA1(2bd833f8049bf7a14ce337c3cde35f2140242a18) ) // dump needs verified

	ROM_REGION( 0xc0, "ppu:palette", 0 )
	ROM_LOAD( "rp2c0x.pal", 0x00, 0xc0, CRC(48de65dc) SHA1(d10acafc8da9ff479c270ec01180cca61efe62f5) )
ROM_END

#define rom_fds rom_famicom

ROM_START( famitwin )
	ROM_REGION( 0x10000, "maincpu", 0 )  // Main RAM
	ROM_LOAD( "rp2c33a-02.bin", 0xe000, 0x2000, CRC(4df24a6c) SHA1(e4e41472c454f928e53eb10e0509bf7d1146ecc1) ) // "Famicom" logo instead of Nintendo logo
ROM_END

ROM_START( fctitler )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )  // Main RAM

	// builtin has its own MMC1B1, 8K PRGRAM (battery backed), and 8K CHRRAM
	// TODO: add switch that selects which boots: builtin vs cart
	ROM_REGION( 0x80000, "builtin_prg", 0 )
	ROM_LOAD( "x1252ce.prg", 0x00000, 0x80000, CRC(696712f9) SHA1(1b9475f569ea9943122676ce65165dc82d11ef38) )

	ROM_REGION( 0xc0, "ppu:palette", 0 )
	ROM_LOAD( "rp2c0x.pal", 0x00, 0xc0, CRC(48de65dc) SHA1(d10acafc8da9ff479c270ec01180cca61efe62f5) )
ROM_END

// see http://www.disgruntleddesigner.com/chrisc/drpcjr/index.html
// and http://www.disgruntleddesigner.com/chrisc/drpcjr/DrPCJrMemMap.txt
ROM_START( drpcjr )
	ROM_REGION( 0x18000, "maincpu", 0 )  // Main RAM + program banks
	// 4 banks to be mapped in 0xe000-0xffff (or 8 banks to be mapped in 0xe000-0xefff & 0xf000-0xffff).
	// Banks selected by writing at 0x4180
	ROM_LOAD("drpcjr_bios.bin", 0x10000, 0x8000, CRC(c8fbef89) SHA1(2cb0a817b31400cdf27817d09bae7e69f41b062b) ) // bios vers. 1.0a
	// Not sure if we should support this: hacked version 1.5a by Chris Covell with bugfixes and GameGenie support
//  ROM_LOAD("drpcjr_v1_5_gg.bin", 0x10000, 0x8000, CRC(98f2033b) SHA1(93c114da787a19279d1a46667c2f69b49e25d4f1) )
ROM_END

ROM_START( iq501 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )  // Main RAM
ROM_END

ROM_START( iq502 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )  // Main RAM
ROM_END

ROM_START( dendy )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )  // Main RAM
ROM_END

ROM_START( dendy2 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )  // Main RAM
ROM_END

ROM_START( gchinatv )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )  // Main RAM
ROM_END

ROM_START( sb486 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )  // Main RAM
ROM_END

} // anonymous namespace

/***************************************************************************

  Game driver(s)

***************************************************************************/

//    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT    CLASS      INIT          COMPANY          FULLNAME

// Nintendo Entertainment System hardware
CONS( 1985, nes,      0,       0,      nes,      nes,     nes_state, empty_init,   "Nintendo",      "Nintendo Entertainment System / Famicom (NTSC)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
CONS( 1987, nespal,   nes,     0,      nespal,   nes,     nes_state, empty_init,   "Nintendo",      "Nintendo Entertainment System (PAL)",            MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )

// Famicom hardware
CONS( 1983, famicom,  0,       nes,    famicom,  famicom, nes_state, init_famicom, "Nintendo",      "Famicom",                         MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
CONS( 1983, famicomo, famicom, 0,      famicomo, famicom, nes_state, init_famicom, "Nintendo",      "Famicom (earlier, with RP2A03)",  MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
CONS( 1983, famitvc1, famicom, 0,      famitvc1, famicom, nes_state, init_famicom, "Sharp",         "My Computer Terebi C1",           MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
CONS( 1986, fds,      famicom, 0,      fds,      famicom, nes_state, init_famicom, "Nintendo",      "Famicom (w/ Disk System add-on)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
CONS( 1986, famitwin, famicom, 0,      famitwin, famicom, nes_state, init_famicom, "Sharp",         "Twin Famicom",                    MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
CONS( 1989, fctitler, famicom, 0,      fctitler, famicom, nes_state, init_famicom, "Sharp",         "Famicom Titler",                  MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )

// Clone hardware
// Many knockoffs using derivatives of the UMC board design, later incorporated into single CMOS chips, were manufactured before and past the end of the Famicom's timeline.

// !! PAL clones documented here !!
// Famicom-based
CONS( 1992, iq501,    0,       nes,    famipalc, nes,     nes_state, init_famicom, "Micro Genius",  "IQ-501",               MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
CONS( 1992, iq502,    0,       nes,    famipalc, nes,     nes_state, init_famicom, "Micro Genius",  "IQ-502",               MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
CONS( 1992, dendy,    iq501,   0,      famipalc, nes,     nes_state, init_famicom, "Steepler",      "Dendy Classic 1",      MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
CONS( 1992, dendy2,   iq502,   0,      famipalc, nes,     nes_state, init_famicom, "Steepler",      "Dendy Classic 2",      MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
CONS( 198?, gchinatv, 0,       nes,    famipalc, nes,     nes_state, init_famicom, "Golden China",  "Golden China TV Game", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )

// Subor/Xiao Ba Wang hardware and derivatives
// These clones implement a keyboard and a parallel port for printing from a word processor. Later models have mice, PS/2 ports, serial ports and a floppy drive.
CONS( 1993, sb486,    0,       nes,    suborkbd, nes,     nes_state, init_famicom, "Subor",         "SB-486", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )

// !! NTSC clones documented here !!
// Famicom-based
// Bung hardware
// Mice, keyboard, etc, including a floppy drive that allows you to run games with a selection of 4 internal "mappers" available on the system.
CONS( 1996, drpcjr,   0,       nes,    famicom,  famicom, nes_state, init_famicom, "Bung",          "Doctor PC Jr", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



nes_clone.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

/*
    NES clones that don't fit anywhere else / Plug & Play (non-VT)
*/

#include "emu.h"
#include "cpu/m6502/rp2a03.h"
#include "video/ppu2c0x.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"
#include "machine/bankdev.h"


namespace {

class nes_clone_state : public driver_device
{
public:
	nes_clone_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen"),
		m_io0(*this, "IO0"),
		m_io1(*this, "IO1"),
		m_ppu(*this, "ppu")
	{ }

	void nes_clone(machine_config &config) ATTR_COLD;
	void nes_clone_pal(machine_config &config) ATTR_COLD;

	void init_nes_clone() ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

	virtual uint8_t in0_r();
	virtual uint8_t in1_r();
	virtual void in0_w(uint8_t data);

	void nes_clone_basemap(address_map &map) ATTR_COLD;

	uint8_t* m_mainrom;
	int m_mainromsize;

	required_device<rp2a03_device> m_maincpu;
	required_device<screen_device> m_screen;
	optional_ioport m_io0;
	optional_ioport m_io1;
	uint8_t m_latch0;
	uint8_t m_latch1;
	uint8_t m_previous_port0;

	required_device<ppu2c0x_device> m_ppu;

private:

	void nes_clone_map(address_map &map) ATTR_COLD;
};


class nes_clone_dancexpt_state : public nes_clone_state
{
public:
	nes_clone_dancexpt_state(const machine_config &mconfig, device_type type, const char *tag) :
		nes_clone_state(mconfig, type, tag),
		m_nametables(*this, "nametable%u", 0),
		m_prgrom(*this, "prgrom"),
		m_gfxrom(*this, "gfxrom"),
		m_mainrom(*this, "maincpu")
	{ }
	void nes_clone_dancexpt(machine_config &config) ATTR_COLD;

private:
	void nes_clone_dancexpt_map(address_map &map) ATTR_COLD;
	memory_bank_array_creator<4> m_nametables;
	required_memory_bank m_prgrom;
	memory_bank_creator m_gfxrom;
	required_region_ptr<uint8_t> m_mainrom;

	std::unique_ptr<u8[]> m_nt_ram;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void mapper_5000_w(offs_t offset, uint8_t data);
	void mapper_5100_w(offs_t offset, uint8_t data);
	void mapper_5200_w(offs_t offset, uint8_t data);

	virtual uint8_t in0_r() override;
	virtual uint8_t in1_r() override;
	virtual void in0_w(uint8_t data) override;

	void update_video_bank();

	uint8_t m_5000_val;
	uint8_t m_5100_val;
	uint8_t m_5200_val;
};


class nes_clone_dnce2000_state : public nes_clone_state
{
public:
	nes_clone_dnce2000_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_clone_state(mconfig, type, tag)
	{ }
	void nes_clone_dnce2000(machine_config& config) ATTR_COLD;

private:
	void nes_clone_dnce2000_map(address_map &map) ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	uint8_t rom_r(offs_t offset);
	void bank_w(uint8_t data);
	uint8_t m_rombase = 0;
};

class nes_clone_sudoku_state : public nes_clone_state
{
public:
	nes_clone_sudoku_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_clone_state(mconfig, type, tag)
	{ }

	void init_sudoku() ATTR_COLD;

	void nes_clone_sudoku(machine_config& config) ATTR_COLD;

private:
	void nes_clone_sudoku_map(address_map &map) ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	uint8_t rom_r(offs_t offset);
	void bank_w(uint8_t data);
	uint8_t m_rombase = 0;
};

class nes_clone_vtvsocr_state : public nes_clone_state
{
public:
	nes_clone_vtvsocr_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_clone_state(mconfig, type, tag)
	{ }

	void nes_clone_vtvsocr(machine_config& config);

private:
	void nes_clone_vtvsocr_map(address_map &map) ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	uint8_t rom_r(offs_t offset);
	void bank_w(offs_t offset, uint8_t data);
	uint8_t m_bankregs[4];
};



class nes_clone_afbm7800_state : public nes_clone_state
{
public:
	nes_clone_afbm7800_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_clone_state(mconfig, type, tag),
		m_prgbank(*this, "prgbank%u", 0),
		m_cbank(*this, "cbank%u", 0),
		m_nametables(*this, "nametable%u", 0),
		m_charbank(*this, "charbank"),
		m_rom_solderpad_bank(*this, "rom_sldpad_bank")
	{ }
	void nes_clone_afbm7800(machine_config& config) ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

protected:
	// configured at startup
	uint16_t m_maxchrbank = 0;

	void nes_clone_afbm7800_map(address_map &map) ATTR_COLD;

	void mapper_6000_w(uint8_t data);
	void mapper_6001_w(uint8_t data);
	void mapper_6002_w(uint8_t data);
	void mapper_6003_w(uint8_t data);

	void mapper_8000_w(uint8_t data);
	void mapper_8001_w(uint8_t data);
	void mapper_a000_w(uint8_t data);
	void mapper_a001_w(uint8_t data);
	void mapper_c000_w(uint8_t data);
	void mapper_c001_w(uint8_t data);
	void mapper_e000_w(uint8_t data);
	void mapper_e001_w(uint8_t data);

	uint8_t solderpad_r(offs_t offset);

	uint8_t vram_r(offs_t offset);
	virtual void vram_w(offs_t offset, uint8_t data);

	uint8_t m_banksel = 0;
	uint8_t m_bankregs[8];
	uint8_t m_extraregs[4];

	void common_start();
	void handle_stdchr_banks(uint16_t* selected_chrbanks);
	virtual void handle_mmc3chr_banks(uint16_t* selected_chrbanks);
	void update_prg_banks();
	void update_banks();
	void mmc3_scanline_cb(int scanline, bool vblank, bool blanked);

	int16_t m_mmc3_scanline_counter = 0;
	uint8_t m_mmc3_scanline_latch = 0;
	uint8_t m_mmc3_irq_enable = 0;

	std::vector<u8> m_vram;

	uint8_t m_ntmirror = 0;
	uint8_t m_ramprot = 0;

	void update_nt_mirroring();
	std::vector<u8> m_nt_ram;

	void vram_map(address_map &map) ATTR_COLD;
	void ntram_map(address_map &map) ATTR_COLD;
	void romarea_map(address_map &map) ATTR_COLD;

	required_memory_bank_array<4> m_prgbank;
	required_memory_bank_array<6> m_cbank;
	memory_bank_array_creator<4> m_nametables;

	required_device<address_map_bank_device> m_charbank;
	required_device<address_map_bank_device> m_rom_solderpad_bank;
};

class nes_clone_taikee_new_state : public nes_clone_afbm7800_state
{
public:
	nes_clone_taikee_new_state(const machine_config &mconfig, device_type type, const char *tag) :
		nes_clone_afbm7800_state(mconfig, type, tag)
	{ }

	void init_vtvppong() ATTR_COLD;

protected:
	virtual void handle_mmc3chr_banks(uint16_t* selected_chrbanks) override;

	virtual void vram_w(offs_t offset, uint8_t data) override;

private:
	virtual void machine_start() override ATTR_COLD;
};

// Standard NES style inputs (not using bus device as there are no real NES controller ports etc. these are all-in-one units and can be custom
uint8_t nes_clone_state::in0_r()
{
	//logerror("%s: in0_r\n", machine().describe_context());
	uint8_t ret = 0x40;
	ret |= m_latch0 & 1;
	m_latch0 >>= 1;
	return ret;
}

uint8_t nes_clone_state::in1_r()
{
	//logerror("%s: in1_r\n", machine().describe_context());
	uint8_t ret = 0x40;
	ret |= m_latch1 & 1;
	m_latch1 >>= 1;
	return ret;
}

void nes_clone_state::in0_w(uint8_t data)
{
	//logerror("%s: in0_w %02x\n", machine().describe_context(), data);
	if ((data & 0x01) != (m_previous_port0 & 0x01))
	{
		if (data & 0x01)
		{
			m_latch0 = m_io0->read();
			m_latch1 = m_io1->read();
		}
	}

	m_previous_port0 = data;
}


void nes_clone_state::nes_clone_basemap(address_map& map)
{
	map(0x0000, 0x07ff).ram();
	map(0x2000, 0x3fff).rw(m_ppu, FUNC(ppu2c0x_device::read), FUNC(ppu2c0x_device::write));

	map(0x4014, 0x4014).w(m_ppu, FUNC(ppu2c0x_device::spriteram_dma));

	map(0x4016, 0x4016).rw(FUNC(nes_clone_state::in0_r), FUNC(nes_clone_state::in0_w));
	map(0x4017, 0x4017).r(FUNC(nes_clone_state::in1_r));
}

void nes_clone_state::nes_clone_map(address_map& map)
{
	nes_clone_basemap(map);
	map(0x8000, 0xffff).rom();
}

static INPUT_PORTS_START( nes_clone )
	PORT_START("IO0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_8WAY

	PORT_START("IO1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_8WAY
INPUT_PORTS_END


static INPUT_PORTS_START( papsudok )
	PORT_START("IO0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("Auto Play")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("Exit / Menu")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("Start / Enter") // this is actually 2 buttons that map to the same input

	PORT_START("IO1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_PLAYER(1) PORT_NAME("1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_PLAYER(1) PORT_NAME("2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_PLAYER(1) PORT_NAME("6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON8 ) PORT_PLAYER(1) PORT_NAME("3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON9 ) PORT_PLAYER(1) PORT_NAME("9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON10 ) PORT_PLAYER(1) PORT_NAME("5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON11 ) PORT_PLAYER(1) PORT_NAME("8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON12 ) PORT_PLAYER(1) PORT_NAME("4") PORT_CODE(KEYCODE_4_PAD)
INPUT_PORTS_END


static INPUT_PORTS_START( dnce2000 )
	PORT_START("IO0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("Down-Left")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("Down-Right")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1) PORT_NAME("Up-Left / Select")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1) PORT_NAME("Up-Right / Start")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_16WAY // NOT A JOYSTICK!!
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_16WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_16WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_16WAY

	PORT_START("IO1")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END



static INPUT_PORTS_START( dancexpt )
	PORT_START("IO1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_16WAY // NOT A JOYSTICK!!
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_16WAY
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(1)
INPUT_PORTS_END


static INPUT_PORTS_START( digezlg )
	PORT_START("IO0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1)

	PORT_START("IO1")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

void nes_clone_state::video_start()
{
}

void nes_clone_state::machine_reset()
{
	m_latch0 = 0;
	m_latch1 = 0;
	m_previous_port0 = 0;
}


void nes_clone_state::machine_start()
{
	m_mainrom = memregion("maincpu")->base();
	m_mainromsize = memregion("maincpu")->bytes();

	save_item(NAME(m_latch0));
	save_item(NAME(m_latch1));
}


void nes_clone_state::nes_clone(machine_config &config)
{
	/* basic machine hardware */
	RP2A03G(config, m_maincpu, NTSC_APU_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &nes_clone_state::nes_clone_map);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60.0988);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC((113.66/(NTSC_APU_CLOCK.dvalue()/1000000)) * (ppu2c0x_device::VBLANK_LAST_SCANLINE_NTSC-ppu2c0x_device::VBLANK_FIRST_SCANLINE+1+2)));
	m_screen->set_size(32*8, 262);
	m_screen->set_visarea(0*8, 32*8-1, 0*8, 30*8-1);
	m_screen->set_screen_update(m_ppu, FUNC(ppu2c0x_device::screen_update));

	PPU_2C02(config, m_ppu);
	m_ppu->set_cpu_tag("maincpu");
	m_ppu->int_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	m_maincpu->add_route(ALL_OUTPUTS, "mono", 0.50);
}

void nes_clone_state::nes_clone_pal(machine_config &config)
{
	/* basic machine hardware */
	RP2A03G(config, m_maincpu, PALC_APU_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &nes_clone_state::nes_clone_map);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50.0070);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC((113.66 / (PALC_APU_CLOCK.dvalue() / 1000000)) * (ppu2c0x_device::VBLANK_LAST_SCANLINE_PAL - ppu2c0x_device::VBLANK_FIRST_SCANLINE_PALC + 1 + 2)));
	m_screen->set_size(32 * 8, 312);
	m_screen->set_visarea(0 * 8, 32 * 8 - 1, 0 * 8, 30 * 8 - 1);
	m_screen->set_screen_update(m_ppu, FUNC(ppu2c0x_device::screen_update));

	PPU_PALC(config, m_ppu);
	m_ppu->set_cpu_tag("maincpu");
	m_ppu->int_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	m_maincpu->add_route(ALL_OUTPUTS, "mono", 0.50);
}

/**************************************************
 Dancing Expert specifics
**************************************************/

void nes_clone_dancexpt_state::machine_start()
{
	nes_clone_state::machine_start();

	m_nt_ram = std::make_unique<u8[]>(0x800);

	m_prgrom->configure_entries(0, 0x08, &m_mainrom[0], 0x4000);
	m_gfxrom->configure_entries(0, 0x20, memregion("gfx1")->base(), 0x2000);

	m_ppu->space(AS_PROGRAM).install_read_bank(0x0000, 0x1fff, m_gfxrom);

	for (int i = 0; i < 4; i++)
		m_nametables[i]->configure_entries(0, 2, &m_nt_ram[0], 0x400);

	save_pointer(NAME(m_nt_ram), 0x800);

	m_nametables[0]->set_entry(0);
	m_nametables[1]->set_entry(1);
	m_nametables[2]->set_entry(0);
	m_nametables[3]->set_entry(1);

	m_ppu->space(AS_PROGRAM).install_readwrite_bank(0x2000,0x23ff,m_nametables[0]);
	m_ppu->space(AS_PROGRAM).install_readwrite_bank(0x2400,0x27ff,m_nametables[1]);
	m_ppu->space(AS_PROGRAM).install_readwrite_bank(0x2800,0x2bff,m_nametables[2]);
	m_ppu->space(AS_PROGRAM).install_readwrite_bank(0x2c00,0x2fff,m_nametables[3]);
}


uint8_t nes_clone_dancexpt_state::in0_r()
{
	// polled frequently during game, music data? from some other device?
	return 0xfe; // bit 0x01 exits the song?
}

uint8_t nes_clone_dancexpt_state::in1_r()
{
	// read directly, not through shifter here
	return m_io1->read();
}

void nes_clone_dancexpt_state::machine_reset()
{
	nes_clone_state::machine_reset();
	m_5000_val = 0x6;
}

void nes_clone_dancexpt_state::update_video_bank()
{
	int bank = (m_previous_port0 & 0x7) * 4;
	bank += m_5100_val & 0x3;

	m_gfxrom->set_entry(bank);
}

void nes_clone_dancexpt_state::mapper_5000_w(offs_t offset, uint8_t data)
{
	// xxxx -bbb  b = CPU bank at 8000-bfff  x = unknown (sometimes 2, sometimes d)

	if (data & ~0x27)
		logerror("%s: mapper_5000_w %02x\n", machine().describe_context(), data);

	m_5000_val = data;

	m_prgrom->set_entry(m_5000_val & 0x7);
}

void nes_clone_dancexpt_state::mapper_5100_w(offs_t offset, uint8_t data)
{
	// ---- --cc   c = lower character ROM bank

	if (data & 0xfc)
		logerror("%s: mapper_5100_w %02x\n", machine().describe_context(), data);

	m_5100_val = data;
	update_video_bank();
}

void nes_clone_dancexpt_state::in0_w(uint8_t data)
{
	// ---- -CCC   C = upper character ROM bank

	// instead of input related writes, the video banking is here?!
	m_previous_port0 = data;
	update_video_bank();
}

void nes_clone_dancexpt_state::mapper_5200_w(offs_t offset, uint8_t data)
{
	// ---- ----  unknown

	logerror("%s: mapper_5200_w %02x\n", machine().describe_context(), data);
	m_5200_val = data;
	//update_video_bank();
}

void nes_clone_dancexpt_state::nes_clone_dancexpt(machine_config &config)
{
	nes_clone(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &nes_clone_dancexpt_state::nes_clone_dancexpt_map);
}

void nes_clone_dancexpt_state::nes_clone_dancexpt_map(address_map &map)
{
	nes_clone_basemap(map);

	map(0x5000, 0x5000).w(FUNC(nes_clone_dancexpt_state::mapper_5000_w));
	map(0x5100, 0x5100).w(FUNC(nes_clone_dancexpt_state::mapper_5100_w));
	map(0x5200, 0x5200).w(FUNC(nes_clone_dancexpt_state::mapper_5200_w));

	map(0x8000, 0xbfff).bankr("prgrom");
	map(0xc000, 0xffff).rom().region("maincpu", 0x1c000);
}


/**************************************************
 Dance 2000 Specifics
**************************************************/

void nes_clone_dnce2000_state::nes_clone_dnce2000(machine_config& config)
{
	nes_clone_pal(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &nes_clone_dnce2000_state::nes_clone_dnce2000_map);
}

void nes_clone_dnce2000_state::nes_clone_dnce2000_map(address_map& map)
{
	nes_clone_basemap(map);
	map(0x8000, 0xffff).rw(FUNC(nes_clone_dnce2000_state::rom_r), FUNC(nes_clone_dnce2000_state::bank_w));
}

void nes_clone_dnce2000_state::machine_reset()
{
	nes_clone_state::machine_reset();
	m_rombase = 0;
}

void nes_clone_dnce2000_state::machine_start()
{
	nes_clone_state::machine_start();
	save_item(NAME(m_rombase));
}

uint8_t nes_clone_dnce2000_state::rom_r(offs_t offset)
{
	return m_mainrom[(offset + (m_rombase * 0x8000)) & (m_mainromsize - 1)];
}

void nes_clone_dnce2000_state::bank_w(uint8_t data)
{
	m_rombase = data;
}

/**************************************************
 Atari Flashback Specifics
**************************************************/

void nes_clone_afbm7800_state::nes_clone_afbm7800(machine_config& config)
{
	nes_clone_pal(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &nes_clone_afbm7800_state::nes_clone_afbm7800_map);

	ADDRESS_MAP_BANK(config, "charbank").set_map(&nes_clone_afbm7800_state::vram_map).set_options(ENDIANNESS_NATIVE, 8, 14, 0x2000);
	ADDRESS_MAP_BANK(config, "ntbank").set_map(&nes_clone_afbm7800_state::ntram_map).set_options(ENDIANNESS_NATIVE, 8, 12, 0x1000);
	ADDRESS_MAP_BANK(config, "rom_sldpad_bank").set_map(&nes_clone_afbm7800_state::romarea_map).set_options(ENDIANNESS_NATIVE, 8, 16, 0x8000);

}

void nes_clone_afbm7800_state::update_prg_banks()
{
	uint8_t innerbankmask;
	uint8_t outerbank;

	uint8_t selected_banks[4] = { 0x00, 0x00, 0x00, 0x00 };

	if (m_extraregs[1] & 1) // ROM or solder pad select
	{
		m_rom_solderpad_bank->set_bank(1);
	}
	else
	{
		m_rom_solderpad_bank->set_bank(0);
	}

	if (m_extraregs[0] & 0x40)
	{
		innerbankmask = 0x0f;
		outerbank = (m_extraregs[0] & 0x07) << 4;
	}
	else
	{
		innerbankmask = 0x1f;
		outerbank = (m_extraregs[0] & 0x06) << 4;
	}

	if ((m_extraregs[3] & 0x03) == 0x00) // MMC3 style mode
	{
		selected_banks[1] = outerbank | (m_bankregs[7] & innerbankmask);
		selected_banks[3] = outerbank | (0x1f & innerbankmask);

		if (m_banksel & 0x40)
		{
			selected_banks[0] = outerbank | (0x1e & innerbankmask);
			selected_banks[2] = outerbank | (m_bankregs[6] & innerbankmask);

		}
		else
		{
			selected_banks[0] = outerbank | (m_bankregs[6] & innerbankmask);
			selected_banks[2] = outerbank | (0x1e & innerbankmask);
		}
	}
	else if ((m_extraregs[3] & 0x03) == 0x03)
	{
		int basebank = (m_bankregs[6] & innerbankmask);

		selected_banks[0] = outerbank | (basebank + 0);
		selected_banks[1] = outerbank | (basebank + 1);
		selected_banks[2] = outerbank | (basebank + 2);
		selected_banks[3] = outerbank | (basebank + 3);
	}
	else // 01 and 02 cases
	{
		int basebank = (m_bankregs[6] & innerbankmask);

		selected_banks[0] = outerbank | (basebank + 0);
		selected_banks[1] = outerbank | (basebank + 1);
		selected_banks[2] = outerbank | (basebank + 0);
		selected_banks[3] = outerbank | (basebank + 1);
	}

	//logerror("%s: PRG bank select: 0=$%X 1=$%X 2=$%X 3=$%X\n", machine().describe_context(), selected_banks[0] * 0x2000, selected_banks[1] * 0x2000, selected_banks[2] * 0x2000, selected_banks[3] * 0x2000);
	m_prgbank[0]->set_entry(selected_banks[0]);
	m_prgbank[1]->set_entry(selected_banks[1]);
	m_prgbank[2]->set_entry(selected_banks[2]);
	m_prgbank[3]->set_entry(selected_banks[3]);
}

void nes_clone_afbm7800_state::handle_stdchr_banks(uint16_t* selected_chrbanks)
{
	int outerchrbank = m_extraregs[2] & 0xf;
	m_charbank->set_bank(0);

	// should also have m_extraregs[0] & 0x38) applied?

	selected_chrbanks[0] = (outerchrbank << 3) | 0x0;
	selected_chrbanks[1] = (outerchrbank << 3) | 0x2;
	selected_chrbanks[2] = (outerchrbank << 3) | 0x4;
	selected_chrbanks[3] = (outerchrbank << 3) | 0x5;
	selected_chrbanks[4] = (outerchrbank << 3) | 0x6;
	selected_chrbanks[5] = (outerchrbank << 3) | 0x7;
}

void nes_clone_afbm7800_state::handle_mmc3chr_banks(uint16_t* selected_chrbanks)
{
	int bankmask;
	int outerchrbank;

	/* not correct? desert falcon
	if (m_extraregs[0] & 0x80)
	{
	    bankmask = 0x0f;
	    outerchrbank = (m_extraregs[0] & 0x38) << 1;
	}
	else
	*/
	{
		bankmask = 0x1f;
		outerchrbank = (m_extraregs[0] & 0x30) << 1;
	}

	if (m_banksel & 0x80)
		m_charbank->set_bank(1);
	else
		m_charbank->set_bank(0);

	selected_chrbanks[0] = (outerchrbank | (m_bankregs[0] & bankmask));
	selected_chrbanks[1] = (outerchrbank | (m_bankregs[1] & bankmask));
	selected_chrbanks[2] = (outerchrbank | (m_bankregs[2] & bankmask));
	selected_chrbanks[3] = (outerchrbank | (m_bankregs[3] & bankmask));
	selected_chrbanks[4] = (outerchrbank | (m_bankregs[4] & bankmask));
	selected_chrbanks[5] = (outerchrbank | (m_bankregs[5] & bankmask));
}

void nes_clone_afbm7800_state::update_banks()
{
	update_prg_banks();
	// chrbank stuff

	uint16_t selected_chrbanks[6] = { 0x00, 0x02, 0x04, 0x05, 0x06, 0x07 };

	if (m_extraregs[3] & 0x10)
	{
		handle_stdchr_banks(selected_chrbanks);
	}
	else // MMC3 mode
	{
		handle_mmc3chr_banks(selected_chrbanks);
	}

	// have to mask with m_maxchrbank because otherwise banks are set at 0x40 (would lower banks atually be chrrom?)
	m_cbank[0]->set_entry((selected_chrbanks[0] & m_maxchrbank)>>1);
	m_cbank[1]->set_entry((selected_chrbanks[1] & m_maxchrbank)>>1);
	m_cbank[2]->set_entry((selected_chrbanks[2] & m_maxchrbank));
	m_cbank[3]->set_entry((selected_chrbanks[3] & m_maxchrbank));
	m_cbank[4]->set_entry((selected_chrbanks[4] & m_maxchrbank));
	m_cbank[5]->set_entry((selected_chrbanks[5] & m_maxchrbank));
}

void nes_clone_taikee_new_state::handle_mmc3chr_banks(uint16_t* selected_chrbanks)
{
	int bankmask;
	int outerchrbank;

	bankmask = 0x3f;
	outerchrbank = 0x00;

	// is this more complex here, or is the CHR ROM just incorrectly loaded?

	// 1010 0000
	if (m_extraregs[0] == 0xa0)
		outerchrbank = 0x00;
	// 1110 0000
	else if (m_extraregs[0] == 0xe0)
		outerchrbank = 0x20; // with 3f mask for space car?
	// 1110 1000
	else if (m_extraregs[0] == 0xe8)
		outerchrbank = 0x80; // 1f mask, but no sprites?? (hot racing)
	// 1111 0010
	else if (m_extraregs[0] == 0xf2)
		outerchrbank = 0x100; // (winter race)
	// 1111 1011
	else if (m_extraregs[0] == 0xfb)
		outerchrbank = 0x180; // with 1f mask (power boat)

	if (m_banksel & 0x80)
		m_charbank->set_bank(1);
	else
		m_charbank->set_bank(0);

	selected_chrbanks[0] = (outerchrbank | (m_bankregs[0] & bankmask));
	selected_chrbanks[1] = (outerchrbank | (m_bankregs[1] & bankmask));
	selected_chrbanks[2] = (outerchrbank | (m_bankregs[2] & bankmask));
	selected_chrbanks[3] = (outerchrbank | (m_bankregs[3] & bankmask));
	selected_chrbanks[4] = (outerchrbank | (m_bankregs[4] & bankmask));
	selected_chrbanks[5] = (outerchrbank | (m_bankregs[5] & bankmask));
}

void nes_clone_afbm7800_state::update_nt_mirroring()
{
	if (m_ntmirror & 1)
	{
		m_nametables[0]->set_entry(0);
		m_nametables[1]->set_entry(0);
		m_nametables[2]->set_entry(1);
		m_nametables[3]->set_entry(1);
	}
	else
	{
		m_nametables[0]->set_entry(0);
		m_nametables[1]->set_entry(1);
		m_nametables[2]->set_entry(0);
		m_nametables[3]->set_entry(1);
	}
}

void nes_clone_afbm7800_state::mapper_8000_w(uint8_t data)
{
	m_banksel = data;
	update_banks();
}

void nes_clone_afbm7800_state::mapper_8001_w(uint8_t data)
{
	m_bankregs[m_banksel & 0x7] = data;
	update_banks();
}

void nes_clone_afbm7800_state::mapper_a000_w(uint8_t data)
{
	// nametable mirroring
	m_ntmirror = data;
	update_nt_mirroring();
}

void nes_clone_afbm7800_state::mapper_a001_w(uint8_t data)
{
	// ram protect
	m_ramprot = data;
}

void nes_clone_afbm7800_state::mapper_c000_w(uint8_t data)
{
	// irq latch
	m_mmc3_scanline_counter = data ^ 0xff;
}

void nes_clone_afbm7800_state::mapper_c001_w(uint8_t data)
{
	// irq reload
	m_mmc3_scanline_latch = data;
}

void nes_clone_afbm7800_state::mapper_e000_w(uint8_t data)
{
	// irq disable
	m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE);
	m_mmc3_irq_enable = 0;
}

void nes_clone_afbm7800_state::mapper_e001_w(uint8_t data)
{
	// irq enable
	m_mmc3_irq_enable = 1;
}



void nes_clone_afbm7800_state::mmc3_scanline_cb(int scanline, bool vblank, bool blanked)
{
	if (m_mmc3_irq_enable)
	{
		if (!vblank && !blanked)
		{
			if (--m_mmc3_scanline_counter == -1)
			{
				m_mmc3_scanline_counter = m_mmc3_scanline_latch;
				m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE);
			}
		}
	}
}



void nes_clone_afbm7800_state::mapper_6000_w(uint8_t data)
{
	if (m_ramprot & 0x80)
	{
		if (!(m_extraregs[3] & 0x80))
		{
			m_extraregs[0] = data;
			update_banks();
		}
	}
}

void nes_clone_afbm7800_state::mapper_6001_w(uint8_t data)
{
	if (m_ramprot & 0x80)
	{
		m_extraregs[1] = data;
		update_banks();
	}
}

void nes_clone_afbm7800_state::mapper_6002_w(uint8_t data)
{
	if (m_ramprot & 0x80)
	{
		m_extraregs[2] = data;
		update_banks();
	}
}

void nes_clone_afbm7800_state::mapper_6003_w(uint8_t data)
{
	if (m_ramprot & 0x80)
	{
		if (!(m_extraregs[3] & 0x80))
		{
			m_extraregs[3] = data;
			update_banks();
		}
	}
}

void nes_clone_afbm7800_state::nes_clone_afbm7800_map(address_map &map)
{
	nes_clone_basemap(map);

	map(0x6000, 0x6000).mirror(0x1ffc).w(FUNC(nes_clone_afbm7800_state::mapper_6000_w));
	map(0x6001, 0x6001).mirror(0x1ffc).w(FUNC(nes_clone_afbm7800_state::mapper_6001_w));
	map(0x6002, 0x6002).mirror(0x1ffc).w(FUNC(nes_clone_afbm7800_state::mapper_6002_w));
	map(0x6003, 0x6003).mirror(0x1ffc).w(FUNC(nes_clone_afbm7800_state::mapper_6003_w));

	map(0x8000, 0xffff).m(m_rom_solderpad_bank, FUNC(address_map_bank_device::amap8));

	map(0x8000, 0x8000).mirror(0x1ffe).w(FUNC(nes_clone_afbm7800_state::mapper_8000_w));
	map(0x8001, 0x8001).mirror(0x1ffe).w(FUNC(nes_clone_afbm7800_state::mapper_8001_w));
	map(0xa000, 0xa000).mirror(0x1ffe).w(FUNC(nes_clone_afbm7800_state::mapper_a000_w));
	map(0xa001, 0xa001).mirror(0x1ffe).w(FUNC(nes_clone_afbm7800_state::mapper_a001_w));
	map(0xc000, 0xc000).mirror(0x1ffe).w(FUNC(nes_clone_afbm7800_state::mapper_c000_w));
	map(0xc001, 0xc001).mirror(0x1ffe).w(FUNC(nes_clone_afbm7800_state::mapper_c001_w));
	map(0xe000, 0xe000).mirror(0x1ffe).w(FUNC(nes_clone_afbm7800_state::mapper_e000_w));
	map(0xe001, 0xe001).mirror(0x1ffe).w(FUNC(nes_clone_afbm7800_state::mapper_e001_w));
}

uint8_t nes_clone_afbm7800_state::solderpad_r(offs_t offset)
{
	logerror("%s: solderpad read\n", machine().describe_context());
	return 0x00;
}

void nes_clone_afbm7800_state::romarea_map(address_map &map)
{
	// when (m_extraregs[1] & 1) == 1
	map(0x0000, 0x1fff).bankr("prgbank0");
	map(0x2000, 0x3fff).bankr("prgbank1");
	map(0x4000, 0x5fff).bankr("prgbank2");
	map(0x6000, 0x7fff).bankr("prgbank3");

	// when (m_extraregs[1] & 1) == 1

	map(0x8000, 0xffff).r(FUNC(nes_clone_afbm7800_state::solderpad_r));
}

void nes_clone_afbm7800_state::vram_map(address_map &map)
{
	// in MMC3 mode when (m_banksel & 0x80) == 0x00 or when not in MMC3 mode
	map(0x0000, 0x07ff).bankrw("cbank0");
	map(0x0800, 0x0fff).bankrw("cbank1");
	map(0x1000, 0x13ff).bankrw("cbank2");
	map(0x1400, 0x17ff).bankrw("cbank3");
	map(0x1800, 0x1bff).bankrw("cbank4");
	map(0x1c00, 0x1fff).bankrw("cbank5");

	// in MMC3 mode when (m_banksel & 0x80) == 0x80
	map(0x2000, 0x23ff).bankrw("cbank2");
	map(0x2400, 0x27ff).bankrw("cbank3");
	map(0x2800, 0x2bff).bankrw("cbank4");
	map(0x2c00, 0x2fff).bankrw("cbank5");
	map(0x3000, 0x37ff).bankrw("cbank0");
	map(0x3800, 0x3fff).bankrw("cbank1");
}

void nes_clone_afbm7800_state::ntram_map(address_map& map)
{
	map(0x0000, 0x03ff).bankrw("nametable0");
	map(0x0400, 0x07ff).bankrw("nametable1");
	map(0x0800, 0x0bff).bankrw("nametable2");
	map(0x0c00, 0x0fff).bankrw("nametable3");
}

void nes_clone_afbm7800_state::machine_reset()
{
	nes_clone_state::machine_reset();
	m_banksel = 0;

	m_bankregs[0] = 0x00;
	m_bankregs[1] = 0x00;
	m_bankregs[2] = 0x00;
	m_bankregs[3] = 0x00;
	m_bankregs[4] = 0x00;
	m_bankregs[5] = 0x00;
	m_bankregs[6] = 0x00;
	m_bankregs[7] = 0x00;

	m_extraregs[0] = 0x00;
	m_extraregs[1] = 0x00;
	m_extraregs[2] = 0x00;
	m_extraregs[3] = 0x00;

	m_ntmirror = 0;
	m_ramprot = 0;

	update_banks();
	update_nt_mirroring();

	m_mmc3_scanline_counter = 0;
	m_mmc3_scanline_latch = 0;
	m_mmc3_irq_enable = 0;
}

uint8_t nes_clone_afbm7800_state::vram_r(offs_t offset)
{
	return m_charbank->read8(offset);
}

void nes_clone_afbm7800_state::vram_w(offs_t offset, uint8_t data)
{
	m_charbank->write8(offset, data);
}

void nes_clone_afbm7800_state::common_start()
{
	uint8_t* ROM = memregion("maincpu")->base();

	for (int i = 0; i < 4; i++)
		m_prgbank[i]->configure_entries(0, memregion("maincpu")->bytes() / 0x2000, &ROM[0x00000], 0x02000);

	m_nt_ram.resize(0x800);

	for (int i = 0; i < 4; i++)
		m_nametables[i]->configure_entries(0, 0x800 / 0x400, &m_nt_ram[0], 0x400);

	m_ppu->space(AS_PROGRAM).install_readwrite_handler(0x0000, 0x1fff, read8sm_delegate(*this, FUNC(nes_clone_afbm7800_state::vram_r)), write8sm_delegate(*this, FUNC(nes_clone_afbm7800_state::vram_w)));
	m_ppu->set_scanline_callback(*this, FUNC(nes_clone_afbm7800_state::mmc3_scanline_cb));

	m_ppu->space(AS_PROGRAM).install_readwrite_bank(0x2000,0x23ff,m_nametables[0]);
	m_ppu->space(AS_PROGRAM).install_readwrite_bank(0x2400,0x27ff,m_nametables[1]);
	m_ppu->space(AS_PROGRAM).install_readwrite_bank(0x2800,0x2bff,m_nametables[2]);
	m_ppu->space(AS_PROGRAM).install_readwrite_bank(0x2c00,0x2fff,m_nametables[3]);

	save_item(NAME(m_nt_ram));

	save_item(NAME(m_mmc3_scanline_counter));
	save_item(NAME(m_mmc3_scanline_latch));
	save_item(NAME(m_mmc3_irq_enable));

	save_item(NAME(m_ntmirror));
	save_item(NAME(m_ramprot));

	save_item(NAME(m_banksel));
	save_item(NAME(m_bankregs));
	save_item(NAME(m_extraregs));
}

void nes_clone_afbm7800_state::machine_start()
{
	nes_clone_state::machine_start();
	common_start();

	m_vram.resize(0x8000);

	m_maxchrbank = (0x8000/0x400)-1;

	for (int i = 0; i < 2; i++)
		m_cbank[i]->configure_entries(0, 0x8000 / 0x800, &m_vram[0], 0x800);

	for (int i = 2; i < 6; i++)
		m_cbank[i]->configure_entries(0, 0x8000 / 0x400, &m_vram[0], 0x400);

	save_item(NAME(m_vram));
}

void nes_clone_taikee_new_state::vram_w(offs_t offset, uint8_t data)
{
	// hot racing attempts to write this, but it's all ROM(!)
	//m_charbank->write8(offset, data);
}

void nes_clone_taikee_new_state::machine_start()
{
	nes_clone_state::machine_start();

	common_start();

	m_maxchrbank = (0x80000/0x400)-1;

	for (int i = 0; i < 2; i++)
		m_cbank[i]->configure_entries(0, 0x80000 / 0x800, memregion("gfx1")->base(), 0x800);

	for (int i = 2; i < 6; i++)
		m_cbank[i]->configure_entries(0, 0x80000 / 0x400, memregion("gfx1")->base(), 0x400);
}

/**************************************************
 Sudoku Specifics
**************************************************/

void nes_clone_sudoku_state::machine_reset()
{
	nes_clone_state::machine_reset();
	m_rombase = 0;
}

void nes_clone_sudoku_state::machine_start()
{
	nes_clone_state::machine_start();
	save_item(NAME(m_rombase));
}

void nes_clone_sudoku_state::nes_clone_sudoku(machine_config& config)
{
	nes_clone(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &nes_clone_sudoku_state::nes_clone_sudoku_map);
}

void nes_clone_sudoku_state::nes_clone_sudoku_map(address_map& map)
{
	nes_clone_basemap(map);
	map(0x8000, 0xffff).rw(FUNC(nes_clone_sudoku_state::rom_r), FUNC(nes_clone_sudoku_state::bank_w));
}

uint8_t nes_clone_sudoku_state::rom_r(offs_t offset)
{
	return m_mainrom[(offset + (m_rombase * 0x8000)) & (m_mainromsize - 1)];
}

void nes_clone_sudoku_state::bank_w(uint8_t data)
{
	logerror("%04x: bank_w %02x\n", machine().describe_context(), data);
	m_rombase = data;
}

void nes_clone_sudoku_state::init_sudoku()
{
	u8 *src = memregion("maincpu")->base();
	int len = memregion("maincpu")->bytes();

	std::vector<u8> buffer(len);
	{
		for (int i = 0; i < len; i += 8)
		{
			buffer[i+0] = src[i+5];
			buffer[i+1] = src[i+4];
			buffer[i+2] = src[i+7];
			buffer[i+3] = src[i+6];
			buffer[i+4] = src[i+1];
			buffer[i+5] = src[i+0];
			buffer[i+6] = src[i+3];
			buffer[i+7] = src[i+2];
		}

		std::copy(buffer.begin(), buffer.end(), &src[0]);
	}
}

/**************************************************
 Soccer Specifics
 INES Mapper 178 / NES 2.0 Mapper 551 clone or similar?
**************************************************/

void nes_clone_vtvsocr_state::machine_reset()
{
	nes_clone_state::machine_reset();
	m_bankregs[0] = 0;
	m_bankregs[1] = 0;
	m_bankregs[2] = 0;
	m_bankregs[3] = 0;
}

void nes_clone_vtvsocr_state::machine_start()
{
	nes_clone_state::machine_start();
	save_item(NAME(m_bankregs));
}

void nes_clone_vtvsocr_state::nes_clone_vtvsocr(machine_config& config)
{
	nes_clone(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &nes_clone_vtvsocr_state::nes_clone_vtvsocr_map);
}

void nes_clone_vtvsocr_state::nes_clone_vtvsocr_map(address_map& map)
{
	nes_clone_basemap(map);
	map(0x4800, 0x4803).w(FUNC(nes_clone_vtvsocr_state::bank_w));
	map(0x8000, 0xffff).r(FUNC(nes_clone_vtvsocr_state::rom_r));
}

uint8_t nes_clone_vtvsocr_state::rom_r(offs_t offset)
{
	int bank = m_bankregs[1] >> 1;
	return m_mainrom[(offset + (bank * 0x8000)) & (m_mainromsize - 1)];
}

void nes_clone_vtvsocr_state::bank_w(offs_t offset, uint8_t data)
{
	logerror("%04x: bank_w %02x %02x\n", machine().describe_context(), offset, data);
	m_bankregs[offset] = data;
}

/**************************************************
 Ping Pong Specifics
**************************************************/

void nes_clone_taikee_new_state::init_vtvppong()
{
	{
		u8 *src = memregion("maincpu")->base();
		int len = memregion("maincpu")->bytes();
		std::vector<u8> buffer(len);
		for (int i = 0; i < len; i++)
		{
			int newaddr = bitswap<18>(i, 17, 16, 15, 13, 14, 10,
				8, 4, 12, 11,
				9, 7, 6, 5,
				3, 2, 1, 0);

			buffer[i] = src[newaddr];
		}
		std::copy(buffer.begin(), buffer.end(), &src[0]);
	}

	{
		u8 *src = memregion("gfx1")->base();
		int len = memregion("gfx1")->bytes();
		std::vector<u8> buffer(len);
		for (int i = 0; i < len; i++)
		{
			// TODO: this doesn't look entirely correct
			int newaddr = bitswap<17>(i, 16, 12, 14, 13, 15,
				4, 10, 8, 11,
				6, 7, 5, 9,
				3, 2, 1, 0);

			buffer[i] = src[newaddr];
		}
		std::copy(buffer.begin(), buffer.end(), &src[0]);
	}
}


ROM_START( papsudok )
	ROM_REGION( 0x80000, "maincpu", 0 )
	ROM_LOAD( "sudoku2.bin", 0x00000, 0x80000, CRC(d1ffcc1e) SHA1(2010e60933a08d0b9271ef37f338758aacba6d2d) )
ROM_END

ROM_START( nytsudo )
	ROM_REGION( 0x80000, "maincpu", 0 )
	ROM_LOAD( "nytsudoku.bin", 0x00000, 0x80000, CRC(9aab1977) SHA1(6948f34d67204287418cdf79f1ca0fa0c26670e3) )
ROM_END


ROM_START( pjoypj001 )
	ROM_REGION( 0x100000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "powerjoy_pj001_lh28f008sc_89a6.bin", 0x00000, 0x100000, CRC(e655e0aa) SHA1(c96d3422e26451c366fee2151fedccb95014cbc7) )

	ROM_REGION( 0x80000, "gfx1", ROMREGION_ERASE00 )
	ROM_LOAD( "powerjoy_pj001_te28f400ceb_00894471.bin", 0x00000, 0x80000, CRC(edca9b66) SHA1(f2f6d9043f524748282065b2fa0ca323ddd7d008) )
ROM_END

ROM_START( afbm7800 )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "atariflashbackmini7800.bin", 0x00000, 0x100000, CRC(da4d9483) SHA1(c04465ff5bd5ca7abf088fe771b8e71c157afb89) )
ROM_END

ROM_START( dnce2000 ) // use Mapper 241 if you want to run this in a NES emulator
	ROM_REGION( 0x40000, "maincpu", 0 )
	ROM_LOAD( "dance.bin", 0x00000, 0x40000, CRC(0982bb50) SHA1(bd608159d7e624ea345f2a188de51cb1aa116421) )
ROM_END

ROM_START( croaky )
	ROM_REGION( 0x80000, "maincpu", 0 )
	ROM_LOAD( "croakykaraoke_16in1.bin", 0x00000, 0x80000, CRC(5f939fd6) SHA1(9dd56b1ee5f27a7d9b42c2638c6a06fac5554c9b) )
ROM_END

ROM_START( vtvppong )
	ROM_REGION( 0x40000, "maincpu", ROMREGION_ERASE00 ) // address lines are swapped
	ROM_LOAD( "vtvpongcpu.bin", 0x00000, 0x40000, CRC(52df95fa) SHA1(3015bcc90eee862b3568f122b402c9defa566aab) )

	ROM_REGION( 0x20000, "gfx1", ROMREGION_ERASE00 ) // address lines are swapped
	ROM_LOAD( "vtvpongppu.bin", 0x00000, 0x20000, CRC(474dfc0c) SHA1(4d0afab111e40172ae0b31e94f1b74b73a18385f) )
ROM_END

ROM_START( vtvsocr )
	ROM_REGION( 0x40000, "maincpu", 0 )
	// 8-bit ROM, but byteswapped for encryption?
	ROM_LOAD16_WORD_SWAP( "virtualtvsoccer.bin", 0x00000, 0x40000, CRC(2cfe42aa) SHA1(c2cafdbd5cc6491c94efd3f1be4b70c9de737b46) )
ROM_END

ROM_START( dancexpt )
	ROM_REGION( 0x20000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "dancingexpert27c010.bin", 0x0000, 0x20000, CRC(658ba2ea) SHA1(c08f6a2735b3c7383cba3a5fa3af905d5af6926f) )

	ROM_REGION( 0x40000, "gfx1", ROMREGION_ERASE00 )
	ROM_LOAD( "dancingexpert27c020.bin", 0x00000, 0x40000, CRC(b653c40c) SHA1(9b74e56768ea5b5309df9761fb442b9be0be46e9) )
ROM_END

ROM_START( digezlg )
	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "15-in-1_digitalezlg.bin", 0x00000, 0x80000, CRC(11944bf2) SHA1(7c3744926cd1be9d7d81d29333b1839be201fefc) )
ROM_END

ROM_START( racechl8 )
	ROM_REGION( 0x100000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "pro 400 0517.u4", 0x00000, 0x100000, CRC(13f24783) SHA1(a3b8ceb8954495a7471dfe4d6cdaa15c9945fcc5) )

	ROM_REGION( 0x80000, "gfx1", ROMREGION_ERASE00 )
	ROM_LOAD( "ppu 401 0517.u5", 0x00000, 0x80000, CRC(51c6d44b) SHA1(6a48ea1185cf0d2d0bcf9a1b2a8cc881e318d978) )
ROM_END

void nes_clone_state::init_nes_clone()
{
}

} // anonymous namespace


// aka 'Atari Flashback' or 'Atari Flashback 1'
// "Flashback Mini 7800 uses normal NES-style hardware, together with a mapper chipset similar to the Waixing kk33xx cartridges (NES 2.0 Mapper 534)"
CONS( 2004, afbm7800,  0,  0,  nes_clone_afbm7800,    nes_clone, nes_clone_afbm7800_state, init_nes_clone, "Atari / Nice Code", "Atari Flashback Mini 7800", 0 )

CONS( 200?, dnce2000, 0, 0, nes_clone_dnce2000, dnce2000, nes_clone_dnce2000_state, init_nes_clone, "Shenzhen Soyin Electric Appliance Ind. Co., Ltd.", "Dance 2000 / Hot 2000 (Jin Bao TV Dancing Carpet, SY-2000-04)", 0 )

CONS( 2002, croaky,   0, 0, nes_clone_dnce2000, nes_clone, nes_clone_dnce2000_state, init_nes_clone, "<unknown>", "Croaky Karaoke 16-in-1", MACHINE_NOT_WORKING ) // no inputs

// Black pad marked 'SUDOKU' with tails on the S and U characters looping over the logo.  Box says "Plug and Play Sudoku"
// Has 2 sets of 4 buttons in circular 'direction pad' layouts (on the left for directions, on the right for functions) and 9 red numbered buttons with red power LED on left of them, and reset button on right
// Alt. version was released with 'New York Times' titlescreen
CONS( 200?, papsudok,     0,  0,  nes_clone_sudoku, papsudok, nes_clone_sudoku_state, init_sudoku, "Nice Code", "Plug and Play Sudoku Game (NES based)", 0 ) // plays, but unclear how 'save' feature is meant to work, is it meant to save after shutdown or not? no obvious writes

CONS( 200?, nytsudo,      0,  0,  nes_clone_sudoku, papsudok, nes_clone_sudoku_state, init_sudoku, "Excalibur Electronics / Nice Code", "The New York Times Sudoku", 0 ) // based on the above

CONS( 200?, vtvppong, 0,  0, nes_clone_afbm7800, nes_clone, nes_clone_taikee_new_state, init_vtvppong, "<unknown>", "Virtual TV Ping Pong", MACHINE_NOT_WORKING )

CONS( 200?, pjoypj001, 0, 0, nes_clone_afbm7800, nes_clone, nes_clone_taikee_new_state, init_nes_clone, "Trump Grand", "PowerJoy (PJ001, NES based plug & play)", MACHINE_NOT_WORKING )

//

/*
    Dancing Export by Daidaixing (aka TimeTop)

    (notes from Sean Riddle regarding missing sound)
    There are two globs on the main PCB, the bigger one next to a label that says NT6561.
    Also two 32-pin COBs, one marked 27C020 and the other 27C010 (both dumped)

    Finally, a daughterboard with 1 glob.
    The daughterboard has 10 traces going to it; power, ground, and 7 from the smaller glob.
    The 10th trace looks like the audio output.

    I assume the daughterboard has a microcontroller for sound
    I'm not sure what the smaller glob is, but it looks like it sends commands to the daughterboard.
*/
CONS( 200?, dancexpt, 0, 0, nes_clone_dancexpt, dancexpt, nes_clone_dancexpt_state, init_nes_clone, "Daidaixing", "Dancing Expert", MACHINE_NOT_WORKING )

CONS( 200?, vtvsocr,     0,  0,  nes_clone_vtvsocr, nes_clone, nes_clone_vtvsocr_state, init_nes_clone, "<unknown>", "Virtual TV Soccer", MACHINE_NOT_WORKING )


// in early 2000s LG TVs
CONS( 200?, digezlg, 0, 0, nes_clone_dnce2000, digezlg, nes_clone_dnce2000_state, init_nes_clone, "LG", "Digital ez LG", MACHINE_NOT_WORKING )

// 2005-04-03 date on PCB
CONS( 2005, racechl8, 0, 0, nes_clone_afbm7800, nes_clone, nes_clone_taikee_new_state, init_nes_clone, "Play Vision / Taikee", "Racing Challenge - 8 Games In 1", 0 )



nes_m8.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:kmg

/***************************************************************************

  Nintendo NES M8 Game Selectable Working Product Display

  This was the first store display unit put out by Nintendo of America.

  Internally it contains a near-stock NES main PCB and a NES M8 board.
  The NES M8 board connects to the NES via the standard cartridge slot
  (by way of a 72-pin cart adapter as the M8 board's edge connector is
  60-pin Famicom-style), and multiplexes 16 pairs of PRG and CHR EPROMs.
  The near-stock NES is missing its CIC chip, and an additional wire
  runs from the M8 board to the host reset line of the CIC (that is the
  M8 board can directly reset the 2A03).

  The M8 board is also connected to the NES M8 LED board. This board
  houses the Game Select switch and 16 LEDs, one of which lights up near
  the title of the currently active game. The M8 board also has another
  connector that goes to a Play Time Limit nob on the back of the unit.

  Three standard NES controller connectors are hidden away in the case,
  and things are rewired so that 2 and 3 share the same lines. The M8
  unit was shipped with two joypads on 1 and 2, and a zapper on 3.

  TODO:
   - Determine how timer and game select button actually operate. Does the
     timer cycle the games like the game select button? Is the timer reset
     after game select button is pressed? What happens if the timer nob is
     adjusted while the machine is running? Timer chip is an MC1455PI.

***************************************************************************/

#include "emu.h"

#include "bus/nes_ctrl/ctrl.h"
#include "cpu/m6502/rp2a03.h"
#include "video/ppu2c0x.h"

#include "screen.h"
#include "speaker.h"


namespace {

class m8_state : public driver_device
{
public:
	m8_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ppu(*this, "ppu")
		, m_screen(*this, "screen")
		, m_ctrl(*this, "ctrl%u", 1U)
		, m_dsw(*this, "DSW%u", 0U)
		, m_s1(*this, "S1")
		, m_nt_page(*this, "nt_page%u", 0U)
		, m_prg(*this, "prg%u", 0U)
		, m_chr(*this, "chr%u", 0U)
		, m_prg_bank(*this, "prg_bank")
		, m_chr_bank(*this, "chr_bank")
	{
	}

	void nes_m8(machine_config &config);
	DECLARE_INPUT_CHANGED_MEMBER(m8_dsw_changed);
	DECLARE_INPUT_CHANGED_MEMBER(m8_game_select);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<rp2a03_device> m_maincpu;
	required_device<ppu2c0x_device> m_ppu;
	required_device<screen_device> m_screen;
	optional_device_array<nes_control_port_device, 3> m_ctrl;

	required_ioport_array<4> m_dsw;
	required_ioport m_s1;

	required_memory_bank_array<4> m_nt_page;
	std::unique_ptr<u8 []> m_nt_ram;

	required_memory_region_array<16> m_prg;
	required_memory_region_array<16> m_chr;
	required_memory_bank m_prg_bank;
	required_memory_bank m_chr_bank;

	u8 m_curr_game = 0;
	u16 m_time_limit = 0;
	emu_timer* m_play_timer = nullptr;

	TIMER_CALLBACK_MEMBER(m8_play_timer_cb);
	u8 m8_in0_r();
	u8 m8_in1_r();
	void m8_in0_w(u8 data);
	void m8_set_mirroring();
	void m8_romswitch();
	void m8_reset();
	void m8_map(address_map &map) ATTR_COLD;
	void m8_ppu_map(address_map &map) ATTR_COLD;
};


INPUT_CHANGED_MEMBER(m8_state::m8_dsw_changed)
{
	m8_set_mirroring();
}

INPUT_CHANGED_MEMBER(m8_state::m8_game_select)
{
	if (newval)
		m8_reset();
}

u8 m8_state::m8_in0_r()
{
	u8 ret = 0x40;

	ret |= m_ctrl[0]->read_bit0();
	ret |= m_ctrl[0]->read_bit34();

	return ret;
}

u8 m8_state::m8_in1_r()
{
	u8 ret = 0x40;

	// both 2nd and 3rd controllers are tied to $4017
	ret |= m_ctrl[1]->read_bit0();
	ret |= m_ctrl[1]->read_bit34();
	ret |= m_ctrl[2]->read_bit0();
	ret |= m_ctrl[2]->read_bit34();

	return ret;
}

void m8_state::m8_in0_w(u8 data)
{
	for (auto &ctrl : m_ctrl)
		ctrl->write(data);
}


/**************************************************************************/

void m8_state::m8_set_mirroring()
{
	// set nametable mirroring directly from DSW
	int dip = m_curr_game / 4;
	int sw = m_curr_game & 3;
	int bit = BIT(m_dsw[dip]->read(), sw);
	for (int i = 0; i < 4; i++)
		m_nt_page[i]->set_entry(BIT(i, bit));
}

void m8_state::m8_romswitch()
{
	m_prg_bank->set_entry(m_curr_game);
	m_chr_bank->set_entry(m_curr_game);
	m8_set_mirroring();
}

void m8_state::m8_reset()
{
	m_play_timer->adjust(attotime::from_seconds(m_time_limit));
	m_curr_game = (m_curr_game + 1) & 0x0f;
	m8_romswitch();
	m_maincpu->reset();
}

TIMER_CALLBACK_MEMBER(m8_state::m8_play_timer_cb)
{
	m8_reset();
}

/***************************************************************************

   Memory map

***************************************************************************/

void m8_state::m8_map(address_map &map)
{
	map(0x0000, 0x07ff).mirror(0x1800).ram();
	map(0x2000, 0x3fff).rw(m_ppu, FUNC(ppu2c0x_device::read), FUNC(ppu2c0x_device::write));
	map(0x4014, 0x4014).w(m_ppu, FUNC(ppu2c0x_device::spriteram_dma));
	map(0x4016, 0x4016).rw(FUNC(m8_state::m8_in0_r), FUNC(m8_state::m8_in0_w)); // IN0 - input port 1
	map(0x4017, 0x4017).r(FUNC(m8_state::m8_in1_r));     // IN1 - input port 2 / PSG second control register

	map(0x8000, 0xffff).bankr(m_prg_bank);
}

void m8_state::m8_ppu_map(address_map &map)
{
	map(0x0000, 0x1fff).bankr(m_chr_bank);
	map(0x2000, 0x23ff).mirror(0x1000).bankrw(m_nt_page[0]);
	map(0x2400, 0x27ff).mirror(0x1000).bankrw(m_nt_page[1]);
	map(0x2800, 0x2bff).mirror(0x1000).bankrw(m_nt_page[2]);
	map(0x2c00, 0x2fff).mirror(0x1000).bankrw(m_nt_page[3]);
	map(0x3f00, 0x3fff).rw(m_ppu, FUNC(ppu2c0x_device::palette_read), FUNC(ppu2c0x_device::palette_write));
}



static INPUT_PORTS_START( m8_base )
	PORT_START("P3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Game Select") PORT_CODE( KEYCODE_0 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_game_select), 0)

	PORT_START("S1")
	PORT_CONFNAME( 0x0f, 0x08, "Play Time Limit" )
	PORT_CONFSETTING(    0x01, "20 sec." )
	PORT_CONFSETTING(    0x02, "3 min." )
	PORT_CONFSETTING(    0x04, "6 min." )
	PORT_CONFSETTING(    0x08, "25 min." )
INPUT_PORTS_END

static INPUT_PORTS_START( nes_m8 )
	PORT_INCLUDE( m8_base )

	PORT_START("DSW0")
	PORT_DIPNAME( 0x01, 0x00, "Splash Screen Mirroring" )        PORT_DIPLOCATION("DS1:1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x01, "V" )
	PORT_DIPNAME( 0x02, 0x00, "Duck Hunt Mirroring" )            PORT_DIPLOCATION("DS1:2") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x02, "V" )
	PORT_DIPNAME( 0x04, 0x00, "Hogan's Alley Mirroring" )        PORT_DIPLOCATION("DS1:3") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x04, "V" )
	PORT_DIPNAME( 0x08, 0x00, "Wild Gunman Mirroring" )          PORT_DIPLOCATION("DS1:4") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x08, "V" )

	PORT_START("DSW1")
	PORT_DIPNAME( 0x01, 0x01, "Balloon Fight Mirroring" )        PORT_DIPLOCATION("DS2:1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x01, "V" )
	PORT_DIPNAME( 0x02, 0x02, "Ice Climber Mirroring" )          PORT_DIPLOCATION("DS2:2") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x02, "V" )
	PORT_DIPNAME( 0x04, 0x00, "Kung Fu Mirroring" )              PORT_DIPLOCATION("DS2:3") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x04, "V" )
	PORT_DIPNAME( 0x08, 0x08, "Pinball Mirroring" )              PORT_DIPLOCATION("DS2:4") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x08, "V" )

	PORT_START("DSW2")
	PORT_DIPNAME( 0x01, 0x00, "Super Mario Bros. Mirroring" )    PORT_DIPLOCATION("DS3:1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x01, "V" )
	PORT_DIPNAME( 0x02, 0x02, "Baseball Mirroring" )             PORT_DIPLOCATION("DS3:2") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x02, "V" )
	PORT_DIPNAME( 0x04, 0x04, "Golf Mirroring" )                 PORT_DIPLOCATION("DS3:3") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x04, "V" )
	PORT_DIPNAME( 0x08, 0x08, "Tennis Mirroring" )               PORT_DIPLOCATION("DS3:4") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x08, "V" )

	PORT_START("DSW3")
	PORT_DIPNAME( 0x01, 0x00, "Excitebike Mirroring" )           PORT_DIPLOCATION("DS4:1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x01, "V" )
	PORT_DIPNAME( 0x02, 0x00, "Mach Rider Mirroring" )           PORT_DIPLOCATION("DS4:2") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x02, "V" )
	PORT_DIPNAME( 0x04, 0x04, "Wrecking Crew Mirroring" )        PORT_DIPLOCATION("DS4:3") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x04, "V" )
	PORT_DIPNAME( 0x08, 0x08, "Popeye Mirroring" )               PORT_DIPLOCATION("DS4:4") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x08, "V" )
INPUT_PORTS_END

static INPUT_PORTS_START( nes_m8a )
	PORT_INCLUDE( m8_base )

	PORT_START("DSW0")
	PORT_DIPNAME( 0x01, 0x00, "Tennis Mirroring" )               PORT_DIPLOCATION("DS1:1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x01, "V" )
	PORT_DIPNAME( 0x02, 0x02, "Baseball Mirroring" )             PORT_DIPLOCATION("DS1:2") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x02, "V" )
	PORT_DIPNAME( 0x04, 0x00, "Soccer Mirroring" )               PORT_DIPLOCATION("DS1:3") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x04, "V" )
	PORT_DIPNAME( 0x08, 0x08, "Golf Mirroring" )                 PORT_DIPLOCATION("DS1:4") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x08, "V" )

	PORT_START("DSW1")
	PORT_DIPNAME( 0x01, 0x00, "Donkey Kong Jr. Math Mirroring" ) PORT_DIPLOCATION("DS2:1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x01, "V" )
	PORT_DIPNAME( 0x02, 0x00, "Wild Gunman Mirroring" )          PORT_DIPLOCATION("DS2:2") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x02, "V" )
	PORT_DIPNAME( 0x04, 0x00, "Hogan's Alley Mirroring" )        PORT_DIPLOCATION("DS2:3") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x04, "V" )
	PORT_DIPNAME( 0x08, 0x00, "Duck Hunt Mirroring" )            PORT_DIPLOCATION("DS2:4") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x08, "V" )

	PORT_START("DSW2")
	PORT_DIPNAME( 0x01, 0x01, "Wrecking Crew Mirroring" )        PORT_DIPLOCATION("DS3:1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x01, "V" )
	PORT_DIPNAME( 0x02, 0x00, "Excitebike Mirroring" )           PORT_DIPLOCATION("DS3:2") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x02, "V" )
	PORT_DIPNAME( 0x04, 0x00, "Kung Fu Mirroring" )              PORT_DIPLOCATION("DS3:3") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x04, "V" )
	PORT_DIPNAME( 0x08, 0x00, "Super Mario Bros. Mirroring" )    PORT_DIPLOCATION("DS3:4") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x08, "V" )

	PORT_START("DSW3")
	PORT_DIPNAME( 0x01, 0x01, "Ice Climber Mirroring" )          PORT_DIPLOCATION("DS4:1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x01, "V" )
	PORT_DIPNAME( 0x02, 0x02, "Pinball Mirroring" )              PORT_DIPLOCATION("DS4:2") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x02, "V" )
	PORT_DIPNAME( 0x04, 0x04, "Clu Clu Land Mirroring" )         PORT_DIPLOCATION("DS4:3") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x04, "V" )
	PORT_DIPNAME( 0x08, 0x00, "Splash Screen Mirroring" )        PORT_DIPLOCATION("DS1:1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x08, "V" )
INPUT_PORTS_END

static INPUT_PORTS_START( nes_m8b )
	PORT_INCLUDE( m8_base )

	PORT_START("DSW0")
	PORT_DIPNAME( 0x01, 0x00, "Tennis Mirroring" )               PORT_DIPLOCATION("DS1:1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x01, "V" )
	PORT_DIPNAME( 0x02, 0x02, "Baseball Mirroring" )             PORT_DIPLOCATION("DS1:2") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x02, "V" )
	PORT_DIPNAME( 0x04, 0x00, "Soccer Mirroring" )               PORT_DIPLOCATION("DS1:3") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x04, "V" )
	PORT_DIPNAME( 0x08, 0x08, "10-Yard Fight Mirroring" )        PORT_DIPLOCATION("DS1:4") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x08, "V" )

	PORT_START("DSW1")
	PORT_DIPNAME( 0x01, 0x01, "Golf Mirroring" )                 PORT_DIPLOCATION("DS2:1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x01, "V" )
	PORT_DIPNAME( 0x02, 0x00, "Wild Gunman Mirroring" )          PORT_DIPLOCATION("DS2:2") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x02, "V" )
	PORT_DIPNAME( 0x04, 0x00, "Hogan's Alley Mirroring" )        PORT_DIPLOCATION("DS2:3") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x04, "V" )
	PORT_DIPNAME( 0x08, 0x00, "Duck Hunt Mirroring" )            PORT_DIPLOCATION("DS2:4") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x08, "V" )

	PORT_START("DSW2")
	PORT_DIPNAME( 0x01, 0x01, "Wrecking Crew Mirroring" )        PORT_DIPLOCATION("DS3:1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x01, "V" )
	PORT_DIPNAME( 0x02, 0x00, "Excitebike Mirroring" )           PORT_DIPLOCATION("DS3:2") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x02, "V" )
	PORT_DIPNAME( 0x04, 0x00, "Kung Fu Mirroring" )              PORT_DIPLOCATION("DS3:3") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x04, "V" )
	PORT_DIPNAME( 0x08, 0x00, "Super Mario Bros. Mirroring" )    PORT_DIPLOCATION("DS3:4") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x08, "V" )

	PORT_START("DSW3")
	PORT_DIPNAME( 0x01, 0x01, "Ice Climber Mirroring" )          PORT_DIPLOCATION("DS4:1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x01, "V" )
	PORT_DIPNAME( 0x02, 0x02, "Pinball Mirroring" )              PORT_DIPLOCATION("DS4:2") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x02, "V" )
	PORT_DIPNAME( 0x04, 0x04, "Clu Clu Land Mirroring" )         PORT_DIPLOCATION("DS4:3") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x04, "V" )
	PORT_DIPNAME( 0x08, 0x08, "Stack Up Mirroring" )             PORT_DIPLOCATION("DS4:4") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m8_state::m8_dsw_changed), 0)
	PORT_DIPSETTING(    0x00, "H" )
	PORT_DIPSETTING(    0x08, "V" )
INPUT_PORTS_END



void m8_state::machine_start()
{
	m_nt_ram = std::make_unique<u8[]>(0x800);

	for (auto &page : m_nt_page)
		page->configure_entries(0, 2, m_nt_ram.get(), 0x400);

	for (int i = 0; i < 16; i++)
	{
		m_prg_bank->configure_entry(i, m_prg[i]->base());
		m_chr_bank->configure_entry(i, m_chr[i]->base());
	}

	m_play_timer = timer_alloc(FUNC(m8_state::m8_play_timer_cb), this);

	save_item(NAME(m_curr_game));
	save_item(NAME(m_time_limit));
	save_pointer(NAME(m_nt_ram), 0x800);
}

void m8_state::machine_reset()
{
	switch (m_s1->read())
	{
		case 1: m_time_limit =      20; break;
		case 2: m_time_limit =  3 * 60; break;
		case 4: m_time_limit =  6 * 60; break;
		case 8: m_time_limit = 25 * 60; break;
	}

	m_curr_game = 0xf;
	m8_reset();
}


void m8_state::nes_m8(machine_config &config)
{
	// basic machine hardware
	RP2A03G(config, m_maincpu, NTSC_APU_CLOCK); // actual model is RP2A03E
	m_maincpu->set_addrmap(AS_PROGRAM, &m8_state::m8_map);

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(RP2A03_NTSC_XTAL / 4, 341, 0, VISIBLE_SCREEN_WIDTH, ppu2c0x_device::NTSC_SCANLINES_PER_FRAME, 0, VISIBLE_SCREEN_HEIGHT);
	m_screen->set_screen_update(m_ppu, FUNC(ppu2c0x_device::screen_update));

	PPU_2C02(config, m_ppu);
	m_ppu->set_addrmap(0, &m8_state::m8_ppu_map);
	m_ppu->set_cpu_tag(m_maincpu);
	m_ppu->int_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	m_maincpu->add_route(ALL_OUTPUTS, "mono", 0.50);

	NES_CONTROL_PORT(config, m_ctrl[0], famibox_control_port12_devices, "joypad").set_screen_tag(m_screen);
	NES_CONTROL_PORT(config, m_ctrl[1], famibox_control_port12_devices, "joypad").set_screen_tag(m_screen);
	NES_CONTROL_PORT(config, m_ctrl[2], famibox_control_port12_devices, "zapper").set_screen_tag(m_screen);
}



ROM_START( nesm8 )
	// M8 splash screen and instructions
	ROM_REGION( 0x8000, "prg0", 0 )
	ROM_LOAD( "m8-prg-2 h.u1", 0x0000, 0x4000, CRC(15a0d398) SHA1(3601ee85d568271a1ebaae058191b092a8dcbcdc) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr0", 0 )
	ROM_LOAD( "m8-chr h.u29",  0x0000, 0x2000, CRC(7a7ad128) SHA1(a22be640cf04a6fdd9ec3b291b33d10ecbee0ef8) )

	// Duck Hunt
	ROM_REGION( 0x8000, "prg1", 0 )
	ROM_LOAD( "dh-prg h.u2",   0x0000, 0x4000, CRC(90ca616d) SHA1(b742576317cd6a04caac25252d5593844c9a0bb6) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr1", 0 )
	ROM_LOAD( "dh-chr h.u30",  0x0000, 0x2000, CRC(4e049e03) SHA1(ffad32a3bab2fb3826bc554b1b9838e837513576) )

	// Hogan's Alley
	ROM_REGION( 0x8000, "prg2", 0 )
	ROM_LOAD( "ha-prg h.u3",   0x0000, 0x4000, CRC(8963ae6e) SHA1(bca489ed0fb58e1e99f36c427bc0d7d805b6c61a) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr2", 0 )
	ROM_LOAD( "ha-chr h.u31",  0x0000, 0x2000, CRC(5df42fc4) SHA1(4fcf23151d9f11c1ef1b1007dd8058f5d5fe9ab8) )

	// Wild Gunman
	ROM_REGION( 0x8000, "prg3", 0 )
	ROM_LOAD( "wg-prg h.u4",   0x0000, 0x4000, CRC(389960db) SHA1(6b38f2c86ef27f653a2bdb9c682ac0bc981c7db6) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr3", 0 )
	ROM_LOAD( "wg-chr h.u32",  0x0000, 0x2000, CRC(a5e04856) SHA1(9194d89a34f687742216889cbb3e717a9ae81c92) )

	// Balloon Fight
	ROM_REGION( 0x8000, "prg4", 0 )
	ROM_LOAD( "bf-prg v.u5",   0x0000, 0x4000, CRC(bd2e9025) SHA1(6742fa2ea498e5b73dc2024ad3888a64e6b894e4) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr4", 0 )
	ROM_LOAD( "bf-chr v.u33",  0x0000, 0x2000, CRC(c642a1df) SHA1(e73cd3d4c0bad8e6f7a1aa6a580f3817a83756a9) )

	// Ice Climber
	ROM_REGION( 0x8000, "prg5", 0 )
	ROM_LOAD( "ic-prg v.u6",   0x0000, 0x4000, CRC(d548307f) SHA1(e323ebaa2c0be5b9a31a21743ddfe7f3d1580672) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr5", 0 )
	ROM_LOAD( "ic-chr v.u34",  0x0000, 0x2000, CRC(0d58a8b1) SHA1(7ae5f457bb5996a5373c9cf46db3d296e27ec56a) )

	// Kung Fu
	ROM_REGION( 0x8000, "prg6", 0 )
	ROM_LOAD( "sx-prg h.u7",   0x0000, 0x8000, CRC(0516375e) SHA1(55dc3550c6133f8624eb6cf3d2f145e4313c2ff6) )

	ROM_REGION( 0x2000, "chr6", 0 )
	ROM_LOAD( "sx-chr h.u35",  0x0000, 0x2000, CRC(430b49a4) SHA1(7e618dbff521c3d5ee0f3d8bb01d2e770395a6bc) )

	// Pinball
	ROM_REGION( 0x8000, "prg7", 0 )
	ROM_LOAD( "pn-prg v.u8",   0x0000, 0x4000, CRC(91d33e3c) SHA1(607c5954e5c577ac78db3234987f8fe62f86f068) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr7", 0 )
	ROM_LOAD( "pn-chr v.u36",  0x0000, 0x2000, CRC(f2a53b3d) SHA1(0d2a521fd984c76cbf9b3cbd47e68c6ce4c6eeae) )

	// Super Mario Bros.
	ROM_REGION( 0x8000, "prg8", 0 )
	ROM_LOAD( "sm-prg h.u15",  0x0000, 0x8000, CRC(5cf548d3) SHA1(fefa1097449a3a11ebf8c6199e905996c5dc8fbd) )

	ROM_REGION( 0x2000, "chr8", 0 )
	ROM_LOAD( "sm-chr h.u40",  0x0000, 0x2000, CRC(867b51ad) SHA1(394badaf0b0bdd0ea279a1bca89a9d9ddc00b1b5) )

	// Baseball
	ROM_REGION( 0x8000, "prg9", 0 )
	ROM_LOAD( "ba-prg v.u16",  0x0000, 0x4000, CRC(39d1fa03) SHA1(28d84cfefa81bbfd3d26e0f70f1b9f53383e54ad) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr9", 0 )
	ROM_LOAD( "ba-chr v.u41",  0x0000, 0x2000, CRC(cde71b82) SHA1(296ccef8a1fd9209f414ce0c788ab0dc95058242) )

	// Golf
	ROM_REGION( 0x8000, "prg10", 0 )
	ROM_LOAD( "gf-prg v.u17",  0x0000, 0x4000, CRC(f9622bfa) SHA1(b4e341a91f614bb19c67cc0205b2443591567aea) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr10", 0 )
	ROM_LOAD( "gf-chr v.u42",  0x0000, 0x2000, CRC(ff6fc790) SHA1(40177839b61f375f2ad03b203328683264845b5b) )

	// Tennis
	ROM_REGION( 0x8000, "prg11", 0 )
	ROM_LOAD( "te-prg h.u18",  0x0000, 0x4000, CRC(8b2e3e81) SHA1(e54274c0b0d651458c5459d41872b1f99904d0fb) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr11", 0 )
	ROM_LOAD( "te-chr h.u43",  0x0000, 0x2000, CRC(3a34c45b) SHA1(2cc26a01c38ead50503dccb3ee929ba7a2b6772c) )

	// Excitebike
	ROM_REGION( 0x8000, "prg12", 0 )
	ROM_LOAD( "eb-prg h.u19",  0x0000, 0x4000, CRC(3a94fa0b) SHA1(6239e91ccefdc017d233cbae388c6568a17ed04b) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr12", 0 )
	ROM_LOAD( "eb-chr h.u44",  0x0000, 0x2000, CRC(e5f72401) SHA1(a8bf028e1a62677e48e88cf421bb2a8051eb800c) )

	// Mach Rider
	ROM_REGION( 0x8000, "prg13", 0 )
	ROM_LOAD( "mr-prg h.u20",  0x0000, 0x8000, CRC(af2bbcbc) SHA1(79b0886c35137b1f31f86e935574c1816a823851) )

	ROM_REGION( 0x2000, "chr13", 0 )
	ROM_LOAD( "mr-chr h.u45",  0x0000, 0x2000, CRC(33a2b41a) SHA1(671f37bce742e63250296e62c143f8a82f860b04) )

	// Wrecking Crew
	ROM_REGION( 0x8000, "prg14", 0 )
	ROM_LOAD( "wr-prg v.u21",  0x0000, 0x8000, CRC(4328b273) SHA1(764d68f05f4a6e43fb26d7e654e237d2b0258fe4) )

	ROM_REGION( 0x2000, "chr14", 0 )
	ROM_LOAD( "wr-chr v.u46",  0x0000, 0x2000, CRC(23f0b9fd) SHA1(c7f2d4f5f555490847654b8458687f94fba3bd12) )

	// Popeye
	ROM_REGION( 0x8000, "prg15", 0 )
	ROM_LOAD( "pp-prg v.u22",  0x0000, 0x4000, CRC(0fa63a45) SHA1(50c594a6d8dcbeee2d83bca8c54c42cf57093aba) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr15", 0 )
	ROM_LOAD( "pp-chr v.u47",  0x0000, 0x2000, CRC(a5fd8d98) SHA1(09d229404babb6c89b417ac541bab80fb06d2ba9) )
ROM_END

// ROMs, labels, and locations all need confirmed
ROM_START( nesm8a )
	// Tennis
	ROM_REGION( 0x8000, "prg0", 0 )
	ROM_LOAD( "te-prg h.u1",   0x0000, 0x4000, CRC(8b2e3e81) SHA1(e54274c0b0d651458c5459d41872b1f99904d0fb) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr0", 0 )
	ROM_LOAD( "te-chr h.u29",  0x0000, 0x2000, CRC(3a34c45b) SHA1(2cc26a01c38ead50503dccb3ee929ba7a2b6772c) )

	// Baseball
	ROM_REGION( 0x8000, "prg1", 0 )
	ROM_LOAD( "ba-prg v.u2",   0x0000, 0x4000, CRC(39d1fa03) SHA1(28d84cfefa81bbfd3d26e0f70f1b9f53383e54ad) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr1", 0 )
	ROM_LOAD( "ba-chr v.u30",  0x0000, 0x2000, CRC(cde71b82) SHA1(296ccef8a1fd9209f414ce0c788ab0dc95058242) )

	// Soccer
	ROM_REGION( 0x8000, "prg2", 0 )
	ROM_LOAD( "sc-prg h.u3",   0x0000, 0x8000, CRC(32e37dcb) SHA1(9d881111634c88218a58e4404848f595824f1c0c) )

	ROM_REGION( 0x2000, "chr2", 0 )
	ROM_LOAD( "sc-chr h.u31",  0x0000, 0x2000, CRC(307b19ab) SHA1(b35ef4c2cf071db77cec1b4529b43a20cfcce172) )

	// Golf
	ROM_REGION( 0x8000, "prg3", 0 )
	ROM_LOAD( "gf-prg v.u4",   0x0000, 0x4000, CRC(f9622bfa) SHA1(b4e341a91f614bb19c67cc0205b2443591567aea) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr3", 0 )
	ROM_LOAD( "gf-chr v.u32",  0x0000, 0x2000, CRC(ff6fc790) SHA1(40177839b61f375f2ad03b203328683264845b5b) )

	// Donkey Kong Jr. Math
	ROM_REGION( 0x8000, "prg4", 0 )
	ROM_LOAD( "ca-prg h.u5",   0x0000, 0x4000, CRC(66f11648) SHA1(4e243d05d60ee578a720f6fadd6c3ebde305605b) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr4", 0 )
	ROM_LOAD( "ca-chr h.u33",  0x0000, 0x2000, CRC(73329878) SHA1(4c0c87f38af851bd605ccd980fb87ef49dfe98c8) )

	// Wild Gunman
	ROM_REGION( 0x8000, "prg5", 0 )
	ROM_LOAD( "wg-prg h.u6",   0x0000, 0x4000, CRC(389960db) SHA1(6b38f2c86ef27f653a2bdb9c682ac0bc981c7db6) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr5", 0 )
	ROM_LOAD( "wg-chr h.u34",  0x0000, 0x2000, CRC(a5e04856) SHA1(9194d89a34f687742216889cbb3e717a9ae81c92) )

	// Hogan's Alley
	ROM_REGION( 0x8000, "prg6", 0 )
	ROM_LOAD( "ha-prg h.u7",   0x0000, 0x4000, CRC(8963ae6e) SHA1(bca489ed0fb58e1e99f36c427bc0d7d805b6c61a) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr6", 0 )
	ROM_LOAD( "ha-chr h.u35",  0x0000, 0x2000, CRC(5df42fc4) SHA1(4fcf23151d9f11c1ef1b1007dd8058f5d5fe9ab8) )

	// Duck Hunt
	ROM_REGION( 0x8000, "prg7", 0 )
	ROM_LOAD( "dh-prg h.u8",   0x0000, 0x4000, CRC(90ca616d) SHA1(b742576317cd6a04caac25252d5593844c9a0bb6) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr7", 0 )
	ROM_LOAD( "dh-chr h.u36",  0x0000, 0x2000, CRC(4e049e03) SHA1(ffad32a3bab2fb3826bc554b1b9838e837513576) )

	// Wrecking Crew
	ROM_REGION( 0x8000, "prg8", 0 )
	ROM_LOAD( "wr-prg v.u15",  0x0000, 0x8000, CRC(4328b273) SHA1(764d68f05f4a6e43fb26d7e654e237d2b0258fe4) )

	ROM_REGION( 0x2000, "chr8", 0 )
	ROM_LOAD( "wr-chr v.u40",  0x0000, 0x2000, CRC(23f0b9fd) SHA1(c7f2d4f5f555490847654b8458687f94fba3bd12) )

	// Excitebike
	ROM_REGION( 0x8000, "prg9", 0 )
	ROM_LOAD( "eb-prg h.u16",  0x0000, 0x4000, CRC(3a94fa0b) SHA1(6239e91ccefdc017d233cbae388c6568a17ed04b) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr9", 0 )
	ROM_LOAD( "eb-chr h.u41",  0x0000, 0x2000, CRC(e5f72401) SHA1(a8bf028e1a62677e48e88cf421bb2a8051eb800c) )

	// Kung Fu
	ROM_REGION( 0x8000, "prg10", 0 )
	ROM_LOAD( "sx-prg h.u17",  0x0000, 0x8000, CRC(0516375e) SHA1(55dc3550c6133f8624eb6cf3d2f145e4313c2ff6) )

	ROM_REGION( 0x2000, "chr10", 0 )
	ROM_LOAD( "sx-chr h.u42",  0x0000, 0x2000, CRC(430b49a4) SHA1(7e618dbff521c3d5ee0f3d8bb01d2e770395a6bc) )

	// Super Mario Bros.
	ROM_REGION( 0x8000, "prg11", 0 )
	ROM_LOAD( "sm-prg h.u18",  0x0000, 0x8000, CRC(5cf548d3) SHA1(fefa1097449a3a11ebf8c6199e905996c5dc8fbd) )

	ROM_REGION( 0x2000, "chr11", 0 )
	ROM_LOAD( "sm-chr h.u43",  0x0000, 0x2000, CRC(867b51ad) SHA1(394badaf0b0bdd0ea279a1bca89a9d9ddc00b1b5) )

	// Ice Climber
	ROM_REGION( 0x8000, "prg12", 0 )
	ROM_LOAD( "ic-prg v.u19",  0x0000, 0x4000, CRC(d548307f) SHA1(e323ebaa2c0be5b9a31a21743ddfe7f3d1580672) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr12", 0 )
	ROM_LOAD( "ic-chr v.u44",  0x0000, 0x2000, CRC(0d58a8b1) SHA1(7ae5f457bb5996a5373c9cf46db3d296e27ec56a) )

	// Pinball
	ROM_REGION( 0x8000, "prg13", 0 )
	ROM_LOAD( "pn-prg v.u20",  0x0000, 0x4000, CRC(91d33e3c) SHA1(607c5954e5c577ac78db3234987f8fe62f86f068) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr13", 0 )
	ROM_LOAD( "pn-chr v.u45",  0x0000, 0x2000, CRC(f2a53b3d) SHA1(0d2a521fd984c76cbf9b3cbd47e68c6ce4c6eeae) )

	// Clu Clu Land
	ROM_REGION( 0x8000, "prg14", 0 )
	ROM_LOAD( "cl-prg v.u21",  0x0000, 0x4000, CRC(23a60a62) SHA1(36003de2a208bf132f1ecbf7af7efd9095ef85c8) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr14", 0 )
	ROM_LOAD( "cl-chr v.u46",  0x0000, 0x2000, CRC(a63b8b98) SHA1(8ddf97dcc8e28f2218c4a4c8b3d9d527fa0525fb) )

	// M8 splash screen and instructions
	ROM_REGION( 0x8000, "prg15", 0 )
	ROM_LOAD( "m8-prg h.u22",  0x0000, 0x4000, CRC(15a0d398) SHA1(3601ee85d568271a1ebaae058191b092a8dcbcdc) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr15", 0 )
	ROM_LOAD( "m8-chr h.u47",  0x0000, 0x2000, CRC(7a7ad128) SHA1(a22be640cf04a6fdd9ec3b291b33d10ecbee0ef8) )
ROM_END

// ROMs, labels, and locations all need confirmed
ROM_START( nesm8b )
	// Tennis
	ROM_REGION( 0x8000, "prg0", 0 )
	ROM_LOAD( "te-prg h.u1",   0x0000, 0x4000, CRC(8b2e3e81) SHA1(e54274c0b0d651458c5459d41872b1f99904d0fb) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr0", 0 )
	ROM_LOAD( "te-chr h.u29",  0x0000, 0x2000, CRC(3a34c45b) SHA1(2cc26a01c38ead50503dccb3ee929ba7a2b6772c) )

	// Baseball
	ROM_REGION( 0x8000, "prg1", 0 )
	ROM_LOAD( "ba-prg v.u2",   0x0000, 0x4000, CRC(39d1fa03) SHA1(28d84cfefa81bbfd3d26e0f70f1b9f53383e54ad) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr1", 0 )
	ROM_LOAD( "ba-chr v.u30",  0x0000, 0x2000, CRC(cde71b82) SHA1(296ccef8a1fd9209f414ce0c788ab0dc95058242) )

	// Soccer
	ROM_REGION( 0x8000, "prg2", 0 )
	ROM_LOAD( "sc-prg h.u3",   0x0000, 0x8000, CRC(32e37dcb) SHA1(9d881111634c88218a58e4404848f595824f1c0c) )

	ROM_REGION( 0x2000, "chr2", 0 )
	ROM_LOAD( "sc-chr h.u31",  0x0000, 0x2000, CRC(307b19ab) SHA1(b35ef4c2cf071db77cec1b4529b43a20cfcce172) )

	// 10-Yard Fight
	ROM_REGION( 0x8000, "prg3", 0 )
	ROM_LOAD( "ty-prg v.u4",   0x0000, 0x8000, CRC(df58fc5a) SHA1(7cc69b39ece2168574599d45ab8452edd4f5a3a1) )

	ROM_REGION( 0x2000, "chr3", 0 )
	ROM_LOAD( "ty-chr v.u32",  0x0000, 0x2000, CRC(2b8336ee) SHA1(6b40a3b7ad6214abf9c35bcbf89d9c1da06f47a0) )

	// Golf
	ROM_REGION( 0x8000, "prg4", 0 )
	ROM_LOAD( "gf-prg v.u5",   0x0000, 0x4000, CRC(f9622bfa) SHA1(b4e341a91f614bb19c67cc0205b2443591567aea) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr4", 0 )
	ROM_LOAD( "gf-chr v.u33",  0x0000, 0x2000, CRC(ff6fc790) SHA1(40177839b61f375f2ad03b203328683264845b5b) )

	// Wild Gunman
	ROM_REGION( 0x8000, "prg5", 0 )
	ROM_LOAD( "wg-prg h.u6",   0x0000, 0x4000, CRC(389960db) SHA1(6b38f2c86ef27f653a2bdb9c682ac0bc981c7db6) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr5", 0 )
	ROM_LOAD( "wg-chr h.u34",  0x0000, 0x2000, CRC(a5e04856) SHA1(9194d89a34f687742216889cbb3e717a9ae81c92) )

	// Hogan's Alley
	ROM_REGION( 0x8000, "prg6", 0 )
	ROM_LOAD( "ha-prg h.u7",   0x0000, 0x4000, CRC(8963ae6e) SHA1(bca489ed0fb58e1e99f36c427bc0d7d805b6c61a) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr6", 0 )
	ROM_LOAD( "ha-chr h.u35",  0x0000, 0x2000, CRC(5df42fc4) SHA1(4fcf23151d9f11c1ef1b1007dd8058f5d5fe9ab8) )

	// Duck Hunt
	ROM_REGION( 0x8000, "prg7", 0 )
	ROM_LOAD( "dh-prg h.u8",   0x0000, 0x4000, CRC(90ca616d) SHA1(b742576317cd6a04caac25252d5593844c9a0bb6) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr7", 0 )
	ROM_LOAD( "dh-chr h.u36",  0x0000, 0x2000, CRC(4e049e03) SHA1(ffad32a3bab2fb3826bc554b1b9838e837513576) )

	// Wrecking Crew
	ROM_REGION( 0x8000, "prg8", 0 )
	ROM_LOAD( "wr-prg v.u15",  0x0000, 0x8000, CRC(4328b273) SHA1(764d68f05f4a6e43fb26d7e654e237d2b0258fe4) )

	ROM_REGION( 0x2000, "chr8", 0 )
	ROM_LOAD( "wr-chr v.u40",  0x0000, 0x2000, CRC(23f0b9fd) SHA1(c7f2d4f5f555490847654b8458687f94fba3bd12) )

	// Excitebike
	ROM_REGION( 0x8000, "prg9", 0 )
	ROM_LOAD( "eb-prg h.u16",  0x0000, 0x4000, CRC(3a94fa0b) SHA1(6239e91ccefdc017d233cbae388c6568a17ed04b) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr9", 0 )
	ROM_LOAD( "eb-chr h.u41",  0x0000, 0x2000, CRC(e5f72401) SHA1(a8bf028e1a62677e48e88cf421bb2a8051eb800c) )

	// Kung Fu
	ROM_REGION( 0x8000, "prg10", 0 )
	ROM_LOAD( "sx-prg h.u17",  0x0000, 0x8000, CRC(0516375e) SHA1(55dc3550c6133f8624eb6cf3d2f145e4313c2ff6) )

	ROM_REGION( 0x2000, "chr10", 0 )
	ROM_LOAD( "sx-chr h.u42",  0x0000, 0x2000, CRC(430b49a4) SHA1(7e618dbff521c3d5ee0f3d8bb01d2e770395a6bc) )

	// Super Mario Bros.
	ROM_REGION( 0x8000, "prg11", 0 )
	ROM_LOAD( "sm-prg h.u18",  0x0000, 0x8000, CRC(5cf548d3) SHA1(fefa1097449a3a11ebf8c6199e905996c5dc8fbd) )

	ROM_REGION( 0x2000, "chr11", 0 )
	ROM_LOAD( "sm-chr h.u43",  0x0000, 0x2000, CRC(867b51ad) SHA1(394badaf0b0bdd0ea279a1bca89a9d9ddc00b1b5) )

	// Ice Climber
	ROM_REGION( 0x8000, "prg12", 0 )
	ROM_LOAD( "ic-prg v.u19",  0x0000, 0x4000, CRC(d548307f) SHA1(e323ebaa2c0be5b9a31a21743ddfe7f3d1580672) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr12", 0 )
	ROM_LOAD( "ic-chr v.u44",  0x0000, 0x2000, CRC(0d58a8b1) SHA1(7ae5f457bb5996a5373c9cf46db3d296e27ec56a) )

	// Pinball
	ROM_REGION( 0x8000, "prg13", 0 )
	ROM_LOAD( "pn-prg v.u20",  0x0000, 0x4000, CRC(91d33e3c) SHA1(607c5954e5c577ac78db3234987f8fe62f86f068) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr13", 0 )
	ROM_LOAD( "pn-chr v.u45",  0x0000, 0x2000, CRC(f2a53b3d) SHA1(0d2a521fd984c76cbf9b3cbd47e68c6ce4c6eeae) )

	// Clu Clu Land
	ROM_REGION( 0x8000, "prg14", 0 )
	ROM_LOAD( "cl-prg v.u21",  0x0000, 0x4000, CRC(23a60a62) SHA1(36003de2a208bf132f1ecbf7af7efd9095ef85c8) )
	ROM_RELOAD(                0x4000, 0x4000 )

	ROM_REGION( 0x2000, "chr14", 0 )
	ROM_LOAD( "cl-chr v.u46",  0x0000, 0x2000, CRC(a63b8b98) SHA1(8ddf97dcc8e28f2218c4a4c8b3d9d527fa0525fb) )

	// Stack Up
	ROM_REGION( 0x8000, "prg15", 0 )
	ROM_LOAD( "bl-prg v.u22",  0x0000, 0x8000, CRC(4ee735c1) SHA1(36b6b30f451c3b4a48464bee9a144c873ae04cc7) )

	ROM_REGION( 0x2000, "chr15", 0 )
	ROM_LOAD( "bl-chr v.u47",  0x0000, 0x2000, CRC(41f4b527) SHA1(49f8dabda7e8585e6961049c46ed913518cd959e) )
ROM_END

} // anonymous namespace


CONS( 1986, nesm8,  0,     0,  nes_m8, nes_m8,  m8_state, empty_init,  "Nintendo", "M8 Game Selectable Working Product Display (US, set 1)",     MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
CONS( 1986, nesm8a, nesm8, 0,  nes_m8, nes_m8a, m8_state, empty_init,  "Nintendo", "M8 Game Selectable Working Product Display (US, set 2)",     MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
CONS( 1986, nesm8b, nesm8, 0,  nes_m8, nes_m8b, m8_state, empty_init,  "Nintendo", "M8 Game Selectable Working Product Display (US, set 3)",     MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )



nes_m82.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:kmg

/***************************************************************************

  Nintendo NES M82 Game Selectable Working Product Display

  Emulation is incomplete. It loads the builtin splash screen ROM but lacks
  12 cartridge slots, timer, game select button, etc. Listen to the nice
  little tune in the meantime. It's not the same as M8 and FamicomBox.

  TODO:
   - Carts
   - Timer
   - Game Select
   - PAL version (same internal ROM?)

***************************************************************************/

#include "emu.h"

#include "bus/nes_ctrl/ctrl.h"
#include "cpu/m6502/rp2a03.h"
#include "video/ppu2c0x.h"

#include "screen.h"
#include "speaker.h"


namespace {

class m82_state : public driver_device
{
public:
	m82_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ppu(*this, "ppu")
		, m_screen(*this, "screen")
		, m_ctrl(*this, "ctrl%u", 1U)
		, m_cn12(*this, "CN12")
		, m_nt_page(*this, "nt_page%u", 0U)
	{
	}

	void nes_m82(machine_config &config);
	DECLARE_INPUT_CHANGED_MEMBER(m82_game_select);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<rp2a03_device> m_maincpu;
	required_device<ppu2c0x_device> m_ppu;
	required_device<screen_device> m_screen;
	optional_device_array<nes_control_port_device, 5> m_ctrl;

	required_ioport m_cn12;

	required_memory_bank_array<4> m_nt_page;
	std::unique_ptr<u8 []> m_nt_ram;

	u8 m_curr_slot = 0;
	u16 m_time_limit = 0;
	emu_timer* m_play_timer = nullptr;

	TIMER_CALLBACK_MEMBER(m82_play_timer_cb);
	u8 m82_in0_r();
	u8 m82_in1_r();
	void m82_in0_w(u8 data);
	void m82_map(address_map &map) ATTR_COLD;
	void m82_ppu_map(address_map &map) ATTR_COLD;
};


INPUT_CHANGED_MEMBER(m82_state::m82_game_select)
{
	if (newval)
	{
	}
}

// TODO: Connectors 1/2 are P1 and 3/4/5 are P2? Space between them on board implies this.
u8 m82_state::m82_in0_r()
{
	u8 ret = 0x40;

	ret |= m_ctrl[0]->read_bit0();
	ret |= m_ctrl[0]->read_bit34();
	ret |= m_ctrl[1]->read_bit0();
	ret |= m_ctrl[1]->read_bit34();

	return ret;
}

u8 m82_state::m82_in1_r()
{
	u8 ret = 0x40;

	ret |= m_ctrl[2]->read_bit0();
	ret |= m_ctrl[2]->read_bit34();
	ret |= m_ctrl[3]->read_bit0();
	ret |= m_ctrl[3]->read_bit34();
	ret |= m_ctrl[4]->read_bit0();
	ret |= m_ctrl[4]->read_bit34();

	return ret;
}

void m82_state::m82_in0_w(u8 data)
{
	for (auto &ctrl : m_ctrl)
		ctrl->write(data);
}


/**************************************************************************/

TIMER_CALLBACK_MEMBER(m82_state::m82_play_timer_cb)
{
}

/***************************************************************************

   Memory map

***************************************************************************/

void m82_state::m82_map(address_map &map)
{
	map(0x0000, 0x07ff).mirror(0x1800).ram();
	map(0x2000, 0x3fff).rw(m_ppu, FUNC(ppu2c0x_device::read), FUNC(ppu2c0x_device::write));
	map(0x4014, 0x4014).w(m_ppu, FUNC(ppu2c0x_device::spriteram_dma));
	map(0x4016, 0x4016).rw(FUNC(m82_state::m82_in0_r), FUNC(m82_state::m82_in0_w)); // IN0 - input port 1
	map(0x4017, 0x4017).r(FUNC(m82_state::m82_in1_r));     // IN1 - input port 2 / PSG second control register

	map(0x8000, 0xffff).rom(); // TODO: cart PRG muxed here
}

void m82_state::m82_ppu_map(address_map &map)
{
	map(0x0000, 0x1fff).rom(); // TODO: cart CHR muxed here
	map(0x2000, 0x23ff).mirror(0x1000).bankrw(m_nt_page[0]);
	map(0x2400, 0x27ff).mirror(0x1000).bankrw(m_nt_page[1]);
	map(0x2800, 0x2bff).mirror(0x1000).bankrw(m_nt_page[2]);
	map(0x2c00, 0x2fff).mirror(0x1000).bankrw(m_nt_page[3]);
	map(0x3f00, 0x3fff).rw(m_ppu, FUNC(ppu2c0x_device::palette_read), FUNC(ppu2c0x_device::palette_write));
}



static INPUT_PORTS_START( nes_m82 )
	PORT_START("CN13")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Game Select") PORT_CODE( KEYCODE_0 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(m82_state::m82_game_select), 0)

	PORT_START("CN12")
	PORT_CONFNAME( 0x0f, 0x08, "Play Time Limit" )
	PORT_CONFSETTING(    0x01, "30 sec." )
	PORT_CONFSETTING(    0x02, "3 min." )
	PORT_CONFSETTING(    0x04, "6 min." )
	PORT_CONFSETTING(    0x08, "128 min." )
INPUT_PORTS_END



void m82_state::machine_start()
{
	m_nt_ram = std::make_unique<u8[]>(0x800);

	for (auto &page : m_nt_page)
		page->configure_entries(0, 2, m_nt_ram.get(), 0x400);

	// vertical mirroring. TODO: replace this with proper code
	m_nt_page[1]->set_entry(1);
	m_nt_page[3]->set_entry(1);

	m_play_timer = timer_alloc(FUNC(m82_state::m82_play_timer_cb), this);
}

void m82_state::machine_reset()
{
	switch (m_cn12->read())
	{
		case 1: m_time_limit =       30; break;
		case 2: m_time_limit =   3 * 60; break;
		case 4: m_time_limit =   6 * 60; break;
		case 8: m_time_limit = 128 * 60; break;
	}

	m_curr_slot = 0;
}


void m82_state::nes_m82(machine_config &config)
{
	// basic machine hardware
	RP2A03G(config, m_maincpu, NTSC_APU_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &m82_state::m82_map);

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(RP2A03_NTSC_XTAL / 4, 341, 0, VISIBLE_SCREEN_WIDTH, ppu2c0x_device::NTSC_SCANLINES_PER_FRAME, 0, VISIBLE_SCREEN_HEIGHT);
	m_screen->set_screen_update(m_ppu, FUNC(ppu2c0x_device::screen_update));

	PPU_2C02(config, m_ppu);
	m_ppu->set_addrmap(0, &m82_state::m82_ppu_map);
	m_ppu->set_cpu_tag(m_maincpu);
	m_ppu->int_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	m_maincpu->add_route(ALL_OUTPUTS, "mono", 0.50);

	NES_CONTROL_PORT(config, m_ctrl[0], famibox_control_port12_devices, "joypad").set_screen_tag(m_screen);
	NES_CONTROL_PORT(config, m_ctrl[1], famibox_control_port12_devices, nullptr).set_screen_tag(m_screen);
	NES_CONTROL_PORT(config, m_ctrl[2], famibox_control_port12_devices, "joypad").set_screen_tag(m_screen);
	NES_CONTROL_PORT(config, m_ctrl[3], famibox_control_port12_devices, nullptr).set_screen_tag(m_screen);
	NES_CONTROL_PORT(config, m_ctrl[4], famibox_control_port12_devices, "zapper").set_screen_tag(m_screen);
}



ROM_START( m82 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "m82-prg v.1.0.ic31", 0xc000, 0x4000, CRC(7d56840a) SHA1(cbd2d14fa073273ba58367758f40d67fd8a9106d) )

	ROM_REGION( 0x2000, "ppu", 0 )
	ROM_LOAD( "m82-chr v.1.0.ic26", 0x0000, 0x2000, CRC(8e19c2b1) SHA1(1d9a34a8eadf1a6c4806d0da96f1ab690389cbd7) )
ROM_END

} // anonymous namespace


CONS( 1986, m82,  0,   0,   nes_m82, nes_m82, m82_state, empty_init, "Nintendo", "M82 Game Selectable Working Product Display",     MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING )



nes_sh6578.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

/*
    SH6578 NES clone hardware
    enhanced NES, different to VT / OneBus systems

    "UMC 1997.2 A35551S" on CPU die (maxx6in1)

    video rendering is changed significantly compared to NES so not using NES PPU device
    has 256x256 pixel pages, attributes are stored next to tile numbers (not in their own table after them) etc.

*/

#include "emu.h"
#include "video/ppu2c0x_sh6578.h"
#include "cpu/m6502/m6502.h"
#include "sound/nes_apu.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"
#include "machine/bankdev.h"
#include "machine/nvram.h"
#include "machine/timer.h"

#define LOG_DMA       (1U << 1)
#define LOG_PPU       (1U << 2)

//#define VERBOSE             (LOG_PPU)
#define VERBOSE             (0)

#include "logmacro.h"


namespace {

class nes_sh6578_state : public driver_device
{
public:
	nes_sh6578_state(const machine_config& mconfig, device_type type, const char* tag) :
		driver_device(mconfig, type, tag),
		m_bank(*this, "cartbank"),
		m_maincpu(*this, "maincpu"),
		m_ppu(*this, "ppu"),
		m_fullrom(*this, "fullrom"),
		m_screen(*this, "screen"),
		m_apu(*this, "nesapu"),
		m_timer(*this, "timer"),
		m_in(*this, "IN%u", 0U),
		m_ext(*this, "EXT")
	{ }

	void nes_sh6578(machine_config& config);
	void nes_sh6578_pal(machine_config& config);

	void init_nes_sh6578();

	int unknown_random_r()
	{
		if (!machine().side_effects_disabled())
			return machine().rand();
		else
			return 0;
	}

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

	virtual void io_w(uint8_t data);
	virtual uint8_t extio_r();
	virtual void extio_w(uint8_t data);
	bool m_isbanked;
	required_memory_bank m_bank;

private:
	required_device<cpu_device> m_maincpu;
	required_device<ppu_sh6578_device> m_ppu;
	required_device<address_map_bank_device> m_fullrom;
	required_device<screen_device> m_screen;
	required_device<nesapu_device> m_apu;
	required_device<timer_device> m_timer;

	uint8_t bankswitch_r(offs_t offset);
	void bankswitch_w(offs_t offset, uint8_t data);

	uint8_t dma_r(offs_t offset);
	void dma_w(offs_t offset, uint8_t data);

	uint8_t bank_r(int bank, uint16_t offset);
	void bank_w(int bank, uint16_t offset, uint8_t data);

	uint8_t bank0_r(offs_t offset) { return bank_r(0, offset); }
	void bank0_w(offs_t offset, uint8_t data) { bank_w(0, offset, data); }
	uint8_t bank1_r(offs_t offset) { return bank_r(1, offset); }
	void bank1_w(offs_t offset, uint8_t data) { bank_w(1, offset, data); }
	uint8_t bank2_r(offs_t offset) { return bank_r(2, offset); }
	void bank2_w(offs_t offset, uint8_t data) { bank_w(2, offset, data); }
	uint8_t bank3_r(offs_t offset) { return bank_r(3, offset); }
	void bank3_w(offs_t offset, uint8_t data) { bank_w(3, offset, data); }
	uint8_t bank4_r(offs_t offset) { return bank_r(4, offset); }
	void bank4_w(offs_t offset, uint8_t data) { bank_w(4, offset, data); }
	uint8_t bank5_r(offs_t offset) { return bank_r(5, offset); }
	void bank5_w(offs_t offset, uint8_t data) { bank_w(5, offset, data); }
	uint8_t bank6_r(offs_t offset) { return bank_r(6, offset); }
	void bank6_w(offs_t offset, uint8_t data) { bank_w(6, offset, data); }
	uint8_t bank7_r(offs_t offset) { return bank_r(7, offset); }
	void bank7_w(offs_t offset, uint8_t data) { bank_w(7, offset, data); }

	void timing_setting_control_w(uint8_t data);
	void initial_startup_w(uint8_t data);
	uint8_t irq_status_r();
	void irq_mask_w(uint8_t data);
	void timer_config_w(uint8_t data);
	void timer_value_w(uint8_t data);

	uint8_t io0_r();
	uint8_t io1_r();

	uint8_t apu_read_mem(offs_t offset);

	void apu_irq(int state);

	int m_initial_startup_state;

	uint8_t m_bankswitch[8];

	uint8_t m_dma_control;
	uint8_t m_dma_bank;
	uint8_t m_dma_source[2];
	uint8_t m_dma_dest[2];
	uint8_t m_dma_length[2];

	uint8_t m_irqmask;

	void do_dma();

	void rom_map(address_map &map) ATTR_COLD;
	void nes_sh6578_map(address_map &map) ATTR_COLD;
	void ppu_map(address_map &map);

	//uint16_t get_tileaddress(uint8_t x, uint8_t y, bool ishigh);

	uint32_t screen_update(screen_device& screen, bitmap_rgb32& bitmap, const rectangle& cliprect);

	TIMER_DEVICE_CALLBACK_MEMBER(timer_expired);
	int m_timerval;

	// this might be game specific
	uint8_t m_previo;
	uint8_t m_iolatch[2];
	required_ioport_array<2> m_in;
	required_ioport m_ext;
};

class nes_sh6578_abl_wikid_state : public nes_sh6578_state
{
public:
	nes_sh6578_abl_wikid_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_sh6578_state(mconfig, type, tag)
	{ }

protected:
	virtual void io_w(uint8_t data) override;
};

class nes_sh6578_max10in1_state : public nes_sh6578_state
{
public:
	nes_sh6578_max10in1_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_sh6578_state(mconfig, type, tag)
	{ }

protected:
	virtual void extio_w(uint8_t data) override;
	virtual void machine_reset() override ATTR_COLD;
};

uint8_t nes_sh6578_state::bank_r(int bank, uint16_t offset)
{
	uint32_t address;
	address = offset & 0x00fff;                   // 0x00fff part of address
	address |= (m_bankswitch[bank] & 0xff) << 12; // 0xff000 part of address
	return m_fullrom->read8(address);
}

void nes_sh6578_state::bank_w(int bank, uint16_t offset, uint8_t data)
{
	uint32_t address;
	address = offset & 0x00fff;                   // 0x00fff part of address
	address |= (m_bankswitch[bank] & 0xff) << 12; // 0xff000 part of address
	m_fullrom->write8(address, data);
}

uint8_t nes_sh6578_state::bankswitch_r(offs_t offset)
{
	return m_bankswitch[offset];
}

void nes_sh6578_state::bankswitch_w(offs_t offset, uint8_t data)
{
	m_bankswitch[offset] = data;
}

uint8_t nes_sh6578_state::dma_r(offs_t offset)
{
	switch (offset)
	{
	case 0x00: LOGMASKED(LOG_DMA, "%s: nes_sh6578_state::dma_r offset %01x : DMA Control : %02x\n", machine().describe_context(), offset, m_dma_control); return m_dma_control & 0x7f;
	case 0x01: LOGMASKED(LOG_DMA, "%s: nes_sh6578_state::dma_r offset %01x : DMA Bank Select\n", machine().describe_context(), offset); return m_dma_bank;
	case 0x02: LOGMASKED(LOG_DMA, "%s: nes_sh6578_state::dma_r offset %01x : DMA Source Address Low\n", machine().describe_context(), offset); return m_dma_source[0];
	case 0x03: LOGMASKED(LOG_DMA, "%s: nes_sh6578_state::dma_r offset %01x : DMA Source Address High\n", machine().describe_context(), offset); return m_dma_source[1];
	case 0x04: LOGMASKED(LOG_DMA, "%s: nes_sh6578_state::dma_r offset %01x : DMA Destination Address Low\n", machine().describe_context(), offset); return m_dma_dest[0];
	case 0x05: LOGMASKED(LOG_DMA, "%s: nes_sh6578_state::dma_r offset %01x : DMA Destination Address High\n", machine().describe_context(), offset); return m_dma_dest[1];
	case 0x06: LOGMASKED(LOG_DMA, "%s: nes_sh6578_state::dma_r offset %01x : DMA Length Low\n", machine().describe_context(), offset); return m_dma_length[0];
	case 0x07: LOGMASKED(LOG_DMA, "%s: nes_sh6578_state::dma_r offset %01x : DMA Length High\n", machine().describe_context(), offset); return m_dma_length[1];
	}
	return 0x00;
}

void nes_sh6578_state::do_dma()
{

	if (m_dma_control & 0x80)
	{
		uint16_t dma_source = m_dma_source[0] | (m_dma_source[1] << 8);
		uint16_t dma_dest = m_dma_dest[0] | (m_dma_dest[1] << 8);
		uint16_t dma_length = m_dma_length[0] | (m_dma_length[1] << 8);

		LOGMASKED(LOG_DMA, "Doing DMA :%02x bank:%02x: source:%04x dest:%04x length:%04x\n", m_dma_control, m_dma_bank, dma_source, dma_dest, dma_length);

		uint16_t realsourceaddress = dma_source;
		uint16_t realdestaddress = dma_dest;

		for (int i = 0; i <= dma_length; i++)
		{
			uint8_t readdat = 0x00;
			if (realsourceaddress & 0x8000)
			{
				// reading from ROM?
				uint32_t trueaddress = (realsourceaddress & 0x7fff) | ((m_dma_bank & 0x1f) * 0x8000);

				readdat = m_fullrom->read8(trueaddress);
			}
			else
			{
				//logerror("reading from system area %04x\n", realsourceaddress);
				uint32_t trueaddress = (realsourceaddress & 0x7fff);
				readdat = m_maincpu->space(AS_PROGRAM).read_byte(trueaddress);
			}

			if (m_dma_control & 0x20)
			{
				//logerror("writing to WORK RAM %04x %02x\n", realdestaddress, readdat);
				m_maincpu->space(AS_PROGRAM).write_byte(realdestaddress, readdat);
			}
			else
			{
				m_ppu->space(AS_PROGRAM).write_byte(realdestaddress, readdat);
			}

			realsourceaddress++;
			realdestaddress++;
		}
	}

	// but games seem to be making quite a few DMA writes with lengths that seem too large? buggy code?
	//m_dma_length[0] = 0;
	//m_dma_length[1] = 0;
}

void nes_sh6578_state::dma_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 0x0:
		LOGMASKED(LOG_DMA, "%s: nes_sh6578_state::dma_w offset %01x : DMA Control : %02x\n", machine().describe_context(), offset, data);
		m_dma_control = data;
		do_dma();
		break;

	case 0x1:
		LOGMASKED(LOG_DMA, "%s: nes_sh6578_state::dma_w offset %01x : DMA Bank Select : %02x\n", machine().describe_context(), offset, data);
		m_dma_bank = data;
		break;

	case 0x2:
		LOGMASKED(LOG_DMA, "%s: nes_sh6578_state::dma_w offset %01x : DMA Source Address Low : %02x\n", machine().describe_context(), offset, data);
		m_dma_source[0] = data;
		break;

	case 0x3:
		LOGMASKED(LOG_DMA, "%s: nes_sh6578_state::dma_w offset %01x : DMA Source Address High : %02x\n", machine().describe_context(), offset, data);
		m_dma_source[1] = data;
		break;

	case 0x4:
		LOGMASKED(LOG_DMA, "%s: nes_sh6578_state::dma_w offset %01x : DMA Destination Address Low : %02x\n", machine().describe_context(), offset, data);
		m_dma_dest[0] = data;
		break;

	case 0x5:
		LOGMASKED(LOG_DMA, "%s: nes_sh6578_state::dma_w offset %01x : DMA Destination Address High : %02x\n", machine().describe_context(), offset, data);
		m_dma_dest[1] = data;
		break;

	case 0x6:
		LOGMASKED(LOG_DMA, "%s: nes_sh6578_state::dma_w offset %01x : DMA Length Low : %02x\n", machine().describe_context(), offset, data);
		m_dma_length[0] = data;
		break;

	case 0x7:
		LOGMASKED(LOG_DMA, "%s: nes_sh6578_state::dma_w offset %01x : DMA Length High : %02x\n", machine().describe_context(), offset, data);
		m_dma_length[1] = data;
		break;
	}
}


void nes_sh6578_state::initial_startup_w(uint8_t data)
{
	// there is also a timeframe in which this must happen
	// if the writes are not correct the system does not operate
	if (m_initial_startup_state == 0)
	{
		if (data == 0x65)
		{
			logerror("initial_startup_w VALID first write (0x65)\n");
			m_initial_startup_state = 1;
		}
		else
		{
			logerror("initial_startup_w invalid first write (not 0x65)\n");
			m_initial_startup_state = -1;
		}
	}
	else if (m_initial_startup_state == 1)
	{
		if (data == 0x76)
		{
			logerror("initial_startup_w VALID second write (0x76)\n");
			m_initial_startup_state = 2;
		}
		else
		{
			logerror("initial_startup_w invalid second write (not 0x76)\n");
			m_initial_startup_state = -1;
		}
	}
	else if (m_initial_startup_state == 2)
	{
		logerror("initial_startup_w invalid write (already passed) (%02x)\n", data);
	}
	else if (m_initial_startup_state == -1)
	{
		logerror("initial_startup_w invalid write (already failed) (%02x)\n", data);
	}
}

uint8_t nes_sh6578_state::irq_status_r()
{
	logerror("%s: nes_sh6578_state::irq_status_r\n", machine().describe_context());
	return machine().rand();
}

void nes_sh6578_state::irq_mask_w(uint8_t data)
{
	m_irqmask = data;

	if (m_irqmask & 0x80)
		m_maincpu->set_input_line(0, CLEAR_LINE);
}

void nes_sh6578_state::timer_config_w(uint8_t data)
{
	logerror("%s: nes_sh6578_state::timer_config_w : %02x (at pos y: %d x: %d )\n", machine().describe_context(), data, m_screen->vpos(), m_screen->hpos() );

	if ((data & 0x80) && (data & 0x20))
	{
		m_timer->adjust(m_screen->scan_period() * m_timerval);
	}
	else
	{
		m_timer->adjust(attotime::never);
	}
}

void nes_sh6578_state::timer_value_w(uint8_t data)
{
	logerror("%s: nes_sh6578_state::timer_value_w : %02x\n", machine().describe_context(), data);
	m_timerval = data;
}


void nes_sh6578_state::timing_setting_control_w(uint8_t data)
{
	logerror("%s: nes_sh6578_state::timing_setting_control_w : %02x\n", machine().describe_context(), data);
}


uint8_t nes_sh6578_state::io0_r()
{
	uint8_t ret = m_iolatch[0] & 0x01;
	m_iolatch[0] >>= 1;
	return ret;
}

uint8_t nes_sh6578_state::io1_r()
{
	uint8_t ret = m_iolatch[1] & 0x01;
	m_iolatch[1] >>= 1;
	return ret;
}

void nes_sh6578_state::io_w(uint8_t data)
{
	if ((data != 0x00) && (data != 0x01) && (data != 0x02) && (data != 0x03))
		logerror("%s: io_w : unexpected value : %02x\n", machine().describe_context(), data);

	if ((m_previo & 0x01) != (data & 0x01))
	{
		// latch on rising or falling?
		if (!(data & 0x01))
		{
			m_iolatch[0] = m_in[0]->read();
			m_iolatch[1] = m_in[1]->read();
		}
	}

	m_previo = data;
}

void nes_sh6578_abl_wikid_state::io_w(uint8_t data)
{
	nes_sh6578_state::io_w(data);

	if (m_isbanked)
	{
		m_bank->set_entry((data>>1)&1);
	}
}

uint8_t nes_sh6578_state::extio_r()
{
	logerror("%s: extio_r\n", machine().describe_context());
	return m_ext->read();
}

void nes_sh6578_state::extio_w(uint8_t data)
{
	logerror("%s: extio_w : %02x\n", machine().describe_context(), data);
}

void nes_sh6578_max10in1_state::extio_w(uint8_t data)
{
	logerror("%s: extio_w : %02x (max10in1)\n", machine().describe_context(), data);

	m_bank->set_entry((data & 0x80) >> 7);
}





void nes_sh6578_state::apu_irq(int state)
{
	// unimplemented
}

uint8_t nes_sh6578_state::apu_read_mem(offs_t offset)
{
	return m_maincpu->space(AS_PROGRAM).read_byte(offset);
}



void nes_sh6578_state::nes_sh6578_map(address_map& map)
{
	map(0x0000, 0x1fff).ram();
	map(0x2000, 0x2007).rw(m_ppu, FUNC(ppu2c0x_device::read), FUNC(ppu2c0x_device::write));
	map(0x2008, 0x2008).rw(m_ppu, FUNC(ppu_sh6578_device::read_extended), FUNC(ppu_sh6578_device::write_extended));

	map(0x2040, 0x207f).rw(m_ppu, FUNC(ppu_sh6578_device::palette_read), FUNC(ppu_sh6578_device::palette_write));

	map(0x4000, 0x4017).w(m_apu, FUNC(nesapu_device::write));
	map(0x4014, 0x4014).w(m_ppu, FUNC(ppu_sh6578_device::spriteram_dma));
	map(0x4015, 0x4015).r(m_apu, FUNC(nesapu_device::status_r));
	map(0x4016, 0x4016).rw(FUNC(nes_sh6578_state::io0_r), FUNC(nes_sh6578_state::io_w));
	map(0x4017, 0x4017).r(FUNC(nes_sh6578_state::io1_r));

	map(0x4020, 0x4020).w(FUNC(nes_sh6578_state::timing_setting_control_w));
	//4021 write keyboard output port
	//4022 read/write keyboard data control
	//4023 read/write joystick,mouse control
	//4024 read - mouse port / write - mouse baud
	//4025 write - Printer Port
	map(0x4026, 0x4026).rw(FUNC(nes_sh6578_state::extio_r), FUNC(nes_sh6578_state::extio_w));
	map(0x4027, 0x4027).ram(); //4027 read/write - DAC data register

	map(0x4031, 0x4031).w(FUNC(nes_sh6578_state::initial_startup_w));
	map(0x4032, 0x4032).w(FUNC(nes_sh6578_state::irq_mask_w));
	map(0x4033, 0x4033).r(FUNC(nes_sh6578_state::irq_status_r));
	map(0x4034, 0x4034).w(FUNC(nes_sh6578_state::timer_config_w));
	map(0x4035, 0x4035).w(FUNC(nes_sh6578_state::timer_value_w));

	map(0x4040, 0x4047).rw(FUNC(nes_sh6578_state::bankswitch_r), FUNC(nes_sh6578_state::bankswitch_w));

	map(0x4048, 0x404f).rw(FUNC(nes_sh6578_state::dma_r), FUNC(nes_sh6578_state::dma_w));

	map(0x5000, 0x57ff).ram();

	map(0x5800, 0x7fff).ram(); // cpatrolm seems to expect RAM here too?

	map(0x8000, 0x8fff).rw(FUNC(nes_sh6578_state::bank0_r), FUNC(nes_sh6578_state::bank0_w));
	map(0x9000, 0x9fff).rw(FUNC(nes_sh6578_state::bank1_r), FUNC(nes_sh6578_state::bank1_w));
	map(0xa000, 0xafff).rw(FUNC(nes_sh6578_state::bank2_r), FUNC(nes_sh6578_state::bank2_w));
	map(0xb000, 0xbfff).rw(FUNC(nes_sh6578_state::bank3_r), FUNC(nes_sh6578_state::bank3_w));
	map(0xc000, 0xcfff).rw(FUNC(nes_sh6578_state::bank4_r), FUNC(nes_sh6578_state::bank4_w));
	map(0xd000, 0xdfff).rw(FUNC(nes_sh6578_state::bank5_r), FUNC(nes_sh6578_state::bank5_w));
	map(0xe000, 0xefff).rw(FUNC(nes_sh6578_state::bank6_r), FUNC(nes_sh6578_state::bank6_w));
	map(0xf000, 0xffff).rw(FUNC(nes_sh6578_state::bank7_r), FUNC(nes_sh6578_state::bank7_w));
}

static INPUT_PORTS_START(nes_sh6578)
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_8WAY

	PORT_START("IN1")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("EXT")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START(bancook)
	PORT_START("IN0")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN1")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("EXT")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(nes_sh6578_state::unknown_random_r))
INPUT_PORTS_END

static INPUT_PORTS_START(soulbird)
	PORT_START("IN0")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN1")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("EXT")
	PORT_BIT( 0x1f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON3 )
INPUT_PORTS_END


void nes_sh6578_state::video_start()
{
}

void nes_sh6578_state::machine_reset()
{
	for (int i = 0; i < 8; i++)
		m_bankswitch[i] = i;

	m_initial_startup_state = 0;
	m_bank->set_entry(0);

	m_irqmask = 0xff;
	m_timerval = 0x00;
}

void nes_sh6578_max10in1_state::machine_reset()
{
	nes_sh6578_state::machine_reset();
	m_bank->set_entry(1);
}

void nes_sh6578_state::machine_start()
{
	m_bank->configure_entry(0, memregion("maincpu")->base() + 0x0000000);
	m_bank->set_entry(0);

	if (memregion("maincpu")->bytes() == 0x200000)
	{
		m_isbanked = true;
		m_bank->configure_entry(1, memregion("maincpu")->base() + 0x100000);
	}
	else
	{
		m_isbanked = false;
	}
}

// SH6578 can address 20-bit address space (1MB of ROM)
void nes_sh6578_state::rom_map(address_map& map)
{
	map(0x00000, 0xfffff).bankr("cartbank");
}

TIMER_DEVICE_CALLBACK_MEMBER(nes_sh6578_state::timer_expired)
{
	if (!(m_irqmask & 0x80))
	{
		//printf("timer expired on line %d\n", m_screen->vpos());
		m_maincpu->set_input_line(0, ASSERT_LINE);
	}

	m_timer->adjust(attotime::never);
}


// from rp2a03.h verify that it actually uses these
#define RP2A03_NTSC_XTAL           XTAL(21'477'272)
#define RP2A03_PAL_XTAL            XTAL(26'601'712)
#define NTSC_APU_CLOCK      (RP2A03_NTSC_XTAL/12) /* 1.7897726666... MHz */
#define PAL_APU_CLOCK       (RP2A03_PAL_XTAL/16) /* 1.662607 MHz */
#define PALC_APU_CLOCK      (RP2A03_PAL_XTAL/15) /* 1.77344746666... MHz */

uint32_t nes_sh6578_state::screen_update(screen_device& screen, bitmap_rgb32& bitmap, const rectangle& cliprect)
{
	return m_ppu->screen_update(screen, bitmap, cliprect);
}

void nes_sh6578_state::ppu_map(address_map &map)
{
	map(0x8000, 0xffff).ram().share("nvram"); // machine specific? soulbird seems to expect fc00-fc7f to save at least
}


void nes_sh6578_state::nes_sh6578(machine_config& config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, NTSC_APU_CLOCK); // regular M6502 core, not RP2A03?
	m_maincpu->set_addrmap(AS_PROGRAM, &nes_sh6578_state::nes_sh6578_map);

	ADDRESS_MAP_BANK(config, m_fullrom).set_map(&nes_sh6578_state::rom_map).set_options(ENDIANNESS_NATIVE, 8, 20, 0x100000);

	PPU_SH6578(config, m_ppu, RP2A03_NTSC_XTAL);
	m_ppu->set_cpu_tag(m_maincpu);
	m_ppu->int_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_ppu->set_addrmap(AS_PROGRAM, &nes_sh6578_state::ppu_map);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60.0988);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC((113.66/(NTSC_APU_CLOCK.dvalue()/1000000)) *
							 (ppu2c0x_device::VBLANK_LAST_SCANLINE_NTSC-ppu2c0x_device::VBLANK_FIRST_SCANLINE+1+2)));
	m_screen->set_size(32*8, 262);
	m_screen->set_visarea(0*8, 32*8-1, 0*8, 30*8-1);
	m_screen->set_screen_update(FUNC(nes_sh6578_state::screen_update));

	TIMER(config, m_timer).configure_generic(FUNC(nes_sh6578_state::timer_expired));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	// have to add the APU separately due to using M6502
	NES_APU(config, m_apu, NTSC_APU_CLOCK);
	m_apu->irq().set(FUNC(nes_sh6578_state::apu_irq));
	m_apu->mem_read().set(FUNC(nes_sh6578_state::apu_read_mem));
	m_apu->add_route(ALL_OUTPUTS, "mono", 0.50);
}

void nes_sh6578_state::nes_sh6578_pal(machine_config& config)
{
	nes_sh6578(config);

	m_maincpu->set_clock(PALC_APU_CLOCK);
	m_apu->set_clock(PALC_APU_CLOCK);

	PPU_SH6578PAL(config.replace(), m_ppu, RP2A03_PAL_XTAL);
	m_ppu->set_cpu_tag(m_maincpu);
	m_ppu->int_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_ppu->set_addrmap(AS_PROGRAM, &nes_sh6578_state::ppu_map);

	m_screen->set_refresh_hz(50.0070);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC((113.66 / (PALC_APU_CLOCK.dvalue() / 1000000)) *
		(ppu2c0x_device::VBLANK_LAST_SCANLINE_PAL - ppu2c0x_device::VBLANK_FIRST_SCANLINE_PALC + 1 + 2)));
	m_screen->set_size(32 * 8, 312);
	m_screen->set_visarea(0 * 8, 32 * 8 - 1, 0 * 8, 30 * 8 - 1);

}

void nes_sh6578_state::init_nes_sh6578()
{
}



ROM_START( bandgpad )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "gamepad.bin", 0x00000, 0x100000, CRC(e2fbb532) SHA1(e9170a7739a8355acbf263fe2b1d291951dc07f0) )
ROM_END

ROM_START( bandggcn )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "gogoconniechan.bin", 0x00000, 0x100000, CRC(715d66ae) SHA1(9326c227bad86eea85194a90f746c60dc032a323) )
ROM_END

ROM_START( bancook )
	ROM_REGION( 0x100000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "mx27c8000.ic2", 0x00000, 0x100000, CRC(865bef26) SHA1(82820eac162a2b4b4b5da894df4bfc5521d4f89b) )
ROM_END

ROM_START( soulbird )
	ROM_REGION( 0x100000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "soulbird.ic1", 0x00000, 0x100000, CRC(307c7f95) SHA1(488ed8de5c22122581e89219889fe4f7e5d5bc96) )
ROM_END

ROM_START( ts_handy11 )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "tvplaypowercontroller.bin", 0x00000, 0x100000, CRC(9c7fe9ff) SHA1(c872e91ca835b66c9dd3b380e8374b51f12bcae0) ) // 29LV008B
ROM_END

ROM_START( cpatrolm )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "citypatrolman.bin", 0x00000, 0x100000, CRC(4b139c67) SHA1(a5b03f472a94ee879f58bbff201b671fbf4f1ea1) )
ROM_END

ROM_START( bb6578 )
	ROM_REGION( 0x80000, "maincpu", 0 )
	ROM_LOAD( "tv game baseball.bin", 0x00000, 0x80000, CRC(dec862b2) SHA1(fb3d97ccde17ab6ead8eafb4e0aafb72fbb6674c) )
ROM_END

ROM_START( 6578cjz1 )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "tv game1.bin", 0x00000, 0x100000, CRC(161c4119) SHA1(b2ce91b070bcaba73c8a23e7067fe8ba41151a40) )
ROM_END

ROM_START( 6578cjz2 )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "tv game2.bin", 0x00000, 0x100000, CRC(cda1395c) SHA1(b0fdf1d3ebd9b7138ec907a0acdf0ea2d275c990) )
ROM_END

ROM_START( ablwikid )
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_LOAD( "mx29f1610atc.u2", 0x00000, 0x200000, CRC(f16abf79) SHA1(aeccbb40d7fdd451ba8e5cca20464da2cf116461) )
ROM_END

ROM_START( maxx5in1 )
	ROM_REGION( 0x100000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "vsmaxxcasino5_e28f008sa_89a2.bin", 0x00000, 0x100000, CRC(e3d8f24f) SHA1(121411e72d53eabe6be927d1db2f871d59a9e08e) )
ROM_END

ROM_START( maxx6in1 )
	ROM_REGION( 0x100000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "maxx6in1.bin", 0x00000, 0x100000, CRC(8e582298) SHA1(89892b9095dbd5101cdf2477a66abd2cb11ad8c8) )
ROM_END

ROM_START( max10in1 )
	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "csmaxxcasino10.bin", 0x000000, 0x200000, CRC(2a05e9af) SHA1(fcf591c22ce8773f72e9d0fa0bae545f6a82a063) )
ROM_END

ROM_START( vsmaxx15 )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "vsmaxx15n1_e28f008sa_89a2.bin", 0x00000, 0x100000, CRC(713955ce) SHA1(f5ff02055fd4574cedc2f056d19388207a7244c0) )
ROM_END

ROM_START( vsmaxx25 )
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_LOAD( "vsmaxx25_am29lv160dt_000122c4.bin", 0x00000, 0x200000, CRC(0efd1625) SHA1(34e83f748af3eee475c5b2b24ff03c00c1b5b8ed) )
ROM_END

ROM_START( dgun806 )
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_LOAD( "dgpnpdgu806as29lv160be_000422c4.bin", 0x00000, 0x200000, CRC(576d6caf) SHA1(fdfa4712e6ed66d2af41ccfbfbf870cd01f7b0f7) )
ROM_END

ROM_START( dancmix3 )
	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "e28f008sa.u5", 0x00000, 0x100000, CRC(faf6480c) SHA1(68bf79910e091443aecc7bf256cd5378a04c550e) )
ROM_END

ROM_START( mousekid )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "mousekid.ic2", 0x00000, 0x100000, CRC(465d5b5a) SHA1(a27f01ccc7b741b51ea4a9e4455dc4cee4420a89) )
ROM_END

} // anonymous namespace


CONS( 200?, maxx5in1,  0, 0,  nes_sh6578, nes_sh6578, nes_sh6578_state,  init_nes_sh6578, "Senario / JungleTac", "Vs Maxx 5-in-1 Casino / Senario Card & Casino Games", 0 ) // advertised on box as 'With Solitaire" (was there an even older version without it?)

CONS( 200?, maxx6in1,  0, 0,  nes_sh6578, nes_sh6578, nes_sh6578_state,  init_nes_sh6578, "Senario / JungleTac", "Vs Maxx 6-in-1 Casino / Senario Card & Casino Games", 0 ) // advertised on box as "With Texas Hold 'Em" (which is the added game since the 5-in-1)

CONS( 200?, max10in1,  0, 0,  nes_sh6578, nes_sh6578, nes_sh6578_max10in1_state,  init_nes_sh6578, "Senario / JungleTac", "Vs Maxx 10-in-1 Casino / Senario Card & Casino Games", 0 )

// titles below have various issues (DMA, split interrupt timing etc.)

// aka Wik!d Joystick, sometimes also called Air Blaster like the real Air Blaster game.  Wik!d seems a rebranding (a 'gift' company) so did ABL reuse the Air Blaster name here instead?
CONS( 200?, ablwikid,    0,  0,  nes_sh6578_pal, nes_sh6578, nes_sh6578_abl_wikid_state, init_nes_sh6578, "Advance Bright Ltd. / JungleTac", "Wikid Joystick 14-in-1", MACHINE_NOT_WORKING )

CONS( 2001?, ts_handy11,  0,  0,  nes_sh6578,     nes_sh6578, nes_sh6578_state, init_nes_sh6578, "Techno Source / JungleTac", "Handy Boy 11-in-1 (TV Play Power)", MACHINE_NOT_WORKING ) // possibly newer than 2001

// from a blue coloured unit, a yellow one exists, is it the same?
CONS( 2004?, vsmaxx15,    0,  0,  nes_sh6578, nes_sh6578, nes_sh6578_state, init_nes_sh6578, "Senario / JungleTac", "Vs Maxx 15-in-1", MACHINE_NOT_WORKING )

// This is from the blue coloured unit with the 1p/2p slider (does it do anything / get read anywhere?)
// A version of the 25-in-1 on VT hardware also exists, with the downgraded version of Big Racing & removed copyrights etc. (probably the purple tinted version without the 1p/2p slider)
CONS( 2004?, vsmaxx25,    0,  0,  nes_sh6578, nes_sh6578, nes_sh6578_max10in1_state, init_nes_sh6578, "Senario / JungleTac", "Vs Maxx 25-in-1 (SH6578 hardware)", MACHINE_NOT_WORKING )

// DGUN-806 on sticker on battery compartment. DreamGear had some other products with the same pad type but different DGUN numbers on the packaging, is the ROM the same?
CONS( 2004?, dgun806,    0,  0,  nes_sh6578, nes_sh6578, nes_sh6578_max10in1_state, init_nes_sh6578, "dreamGEAR", "Plug 'N' Play 25-in-1 (DGUN-806)", MACHINE_NOT_WORKING )



// titles below need inputs mapping to go further

CONS( 1997, bandgpad,    0,  0,  nes_sh6578,     nes_sh6578, nes_sh6578_state, init_nes_sh6578, "Bandai", "Multi Game Player Gamepad", MACHINE_NOT_WORKING )

CONS( 1997, bandggcn,    0,  0,  nes_sh6578,     nes_sh6578, nes_sh6578_state, init_nes_sh6578, "Bandai", "Go! Go! Connie-chan! Asobou Mouse", MACHINE_NOT_WORKING )

// uses a mouse and buttons (no keyboard)
// テレビであそぼう! ミッキー&ミニー マウスキッズ
CONS( 1997, mousekid,    0,  0,  nes_sh6578,     bancook, nes_sh6578_state, init_nes_sh6578, "Tomy", "TV de Asobou! Mickey & Minnie Mouse Kids (Japan)", MACHINE_NOT_WORKING )

// おジャ魔女どれみのTVでマジカルクッキング
CONS( 2001, bancook,     0,  0,  nes_sh6578,     bancook,    nes_sh6578_state, init_nes_sh6578, "Bandai", "Ojamajo Doremi no TV de Magical Cooking (Japan)", MACHINE_NOT_WORKING )

// there's no SEEPROM, it uses a CR2032 to keep some RAM data alive
// 百獣戦隊ガオレンジャー ソウルバード アニマル救出大作戦 (aka DX Soul Bird)
CONS( 2001, soulbird,     0,  0,  nes_sh6578,     soulbird,    nes_sh6578_state, init_nes_sh6578, "Bandai", "Hyakujuu Sentai Gaoranger Soul Bird: Animal Kyuushutsu Daisakusen (Japan)", 0 )

CONS( 200?, cpatrolm,    0,  0,  nes_sh6578_pal, nes_sh6578, nes_sh6578_state, init_nes_sh6578, "TimeTop", "City Patrolman", MACHINE_NOT_WORKING )

CONS( 200?, bb6578,      0,  0,  nes_sh6578,     nes_sh6578, nes_sh6578_state, init_nes_sh6578, "DaiDaiXing Electronics", "TV Games Baseball (SH6578 hardware)", MACHINE_NOT_WORKING )

// these don't boot much further than the timetop logo and a splash screen
// 超级知识大富翁 (Chāojí Zhīshì Dà Fùwēng)
CONS( 200?, 6578cjz1,     0,         0,  nes_sh6578,     bancook, nes_sh6578_state, init_nes_sh6578, "TimeTop", "Chaoji Zhishi Da Fuweng 1", MACHINE_NOT_WORKING )
CONS( 200?, 6578cjz2,     0,         0,  nes_sh6578,     bancook, nes_sh6578_state, init_nes_sh6578, "TimeTop", "Chaoji Zhishi Da Fuweng 2", MACHINE_NOT_WORKING )

// Super Moto 3 https://youtu.be/DR5Y_r6C_qk - has JungleTac copyrights intact, and appears to have the SH6578 versions of the games

// Handy Max 15-in-1 https://youtu.be/jQTUHj1cP-k - has JungleTac copyrights intact, and appears to have the SH6578 versions of the games

// has sampled audio, not supported (make sure it isn't being done by an external device)
CONS( 201?, dancmix3, 0, 0, nes_sh6578_pal, nes_sh6578, nes_sh6578_state, init_nes_sh6578, "Venom", "TV Dance Mat 4 Games in 1 (Mix Party 3 / Dance Mix 3)", MACHINE_NOT_WORKING )



nes_vt02_vt03.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/***************************************************************************

  nes_vt.cpp

  VT02/VT03 systems go in here

  - 2KB RAM
  - VT03 adds alt palette style (not available on VT02?)

  NON-bugs (same happens on real hardware)

  Pause screen has corrupt GFX on enhanced version of Octopus

***************************************************************************/

#include "emu.h"
#include "nes_vt_soc.h"


namespace {

class nes_vt_base_state : public driver_device
{
public:
	nes_vt_base_state(const machine_config& mconfig, device_type type, const char* tag) :
		driver_device(mconfig, type, tag),
		m_io0(*this, "IO0"),
		m_io1(*this, "IO1"),
		m_exin(*this, "EXTRAIN%u", 0U),
		m_prgrom(*this, "mainrom")
	{ }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual uint8_t in0_r();
	virtual uint8_t in1_r();
	virtual void in0_w(uint8_t data);

	void nes_vt_map(address_map &map) ATTR_COLD;

	optional_ioport m_io0;
	optional_ioport m_io1;

	uint8_t m_latch0;
	uint8_t m_latch1;
	uint8_t m_previous_port0;

	optional_ioport_array<4> m_exin;

	required_region_ptr<uint8_t> m_prgrom;

	uint8_t vt_rom_r(offs_t offset);
	[[maybe_unused]] void vtspace_w(offs_t offset, uint8_t data);

	void configure_soc(nes_vt02_vt03_soc_device* soc);

	[[maybe_unused]] uint8_t upper_412c_r();
	[[maybe_unused]] uint8_t upper_412d_r();
	[[maybe_unused]] void upper_412c_w(uint8_t data);

private:
	/* Extra IO */
	template <uint8_t NUM> uint8_t extrain_r();
};

class nes_vt_state : public nes_vt_base_state
{
public:
	nes_vt_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt_base_state(mconfig, type, tag),
		m_soc(*this, "soc")
	{ }

	void nes_vt_pal_1mb(machine_config& config) ATTR_COLD;
	void nes_vt_pal_2mb(machine_config& config) ATTR_COLD;
	void nes_vt_pal_4mb(machine_config& config) ATTR_COLD;
	void nes_vt_pal_8mb(machine_config& config) ATTR_COLD;
	void nes_vt_pal_16mb(machine_config& config) ATTR_COLD;

	void nes_vt_512kb(machine_config& config) ATTR_COLD;
	void nes_vt_1mb(machine_config& config) ATTR_COLD;
	void nes_vt_2mb(machine_config& config) ATTR_COLD;
	void nes_vt_4mb(machine_config& config) ATTR_COLD;
	void nes_vt_8mb(machine_config& config) ATTR_COLD;
	void nes_vt_16mb(machine_config& config) ATTR_COLD;
	[[maybe_unused]] void nes_vt_32mb(machine_config& config) ATTR_COLD;

	void nes_vt_1mb_majkon(machine_config& config) ATTR_COLD;

	[[maybe_unused]] void vt_external_space_map_32mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_16mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_8mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_4mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_2mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_1mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_512kbyte(address_map &map) ATTR_COLD;

	void vt_external_space_map_1mbyte_majkon(address_map &map) ATTR_COLD;

	void init_protpp() ATTR_COLD;
	void init_gamezn2() ATTR_COLD;

protected:
	required_device<nes_vt02_vt03_soc_device> m_soc;
};


class nes_vt_swap_op_d5_d6_state : public nes_vt_state
{
public:
	nes_vt_swap_op_d5_d6_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt_state(mconfig, type, tag)
	{ }

	void nes_vt_vh2009(machine_config& config) ATTR_COLD;
	void nes_vt_vh2009_pal(machine_config& config) ATTR_COLD;
	void nes_vt_vh2009_1mb(machine_config& config) ATTR_COLD;
	void nes_vt_vh2009_2mb(machine_config& config) ATTR_COLD;
	void nes_vt_vh2009_pal_2mb(machine_config& config) ATTR_COLD;
	void nes_vt_vh2009_4mb(machine_config& config) ATTR_COLD;
	void nes_vt_vh2009_8mb(machine_config& config) ATTR_COLD;

	void nes_vt_senwld_512kb(machine_config& config) ATTR_COLD;

protected:
	void vt_external_space_map_senwld_512kbyte(address_map &map) ATTR_COLD;
};

class nes_vt_pjoy_state : public nes_vt_state
{
public:
	nes_vt_pjoy_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt_state(mconfig, type, tag)
	{ }

	void nes_vt_pjoy_4mb(machine_config& config) ATTR_COLD;
};

class nes_vt_waixing_state : public nes_vt_state
{
public:
	nes_vt_waixing_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt_state(mconfig, type, tag)
	{ }

	void nes_vt_waixing_512kb(machine_config& config) ATTR_COLD;
	void nes_vt_waixing_512kb_rasterhack(machine_config& config) ATTR_COLD;
	void nes_vt_waixing_2mb(machine_config& config) ATTR_COLD;

	void init_hs36in1() ATTR_COLD;
};

class nes_vt_waixing_alt_state : public nes_vt_waixing_state
{
public:
	nes_vt_waixing_alt_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt_waixing_state(mconfig, type, tag)
	{ }

	void nes_vt_waixing_alt_4mb(machine_config& config) ATTR_COLD;
	void nes_vt_waixing_alt_pal_8mb(machine_config& config) ATTR_COLD;
};

class nes_vt_waixing_alt_sporzpp_state : public nes_vt_waixing_alt_state
{
public:
	nes_vt_waixing_alt_sporzpp_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt_waixing_alt_state(mconfig, type, tag)
	{ }

	void nes_vt_waixing_alt_4mb_sporzpp(machine_config& config) ATTR_COLD;
	void nes_vt_pal_4mb_sporzbxa(machine_config& config) ATTR_COLD;

private:
	uint8_t in1_r() override
	{
		uint8_t i = machine().rand() & 0x18;
		uint8_t ret = m_io1->read() & ~0x18;
		return i | ret;
	}
};

class nes_vt_wldsoctv_state : public nes_vt_state
{
public:
	nes_vt_wldsoctv_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt_state(mconfig, type, tag)
	{ }

private:
	uint8_t in1_r() override
	{
		uint8_t i = machine().rand() & 0x18;
		uint8_t ret = m_io1->read() & ~0x18;
		return i | ret;
	}
};

class nes_vt_timetp36_state : public nes_vt_state
{
public:
	nes_vt_timetp36_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt_state(mconfig, type, tag)
	{ }
};

class nes_vt_hum_state : public nes_vt_state
{
public:
	nes_vt_hum_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt_state(mconfig, type, tag)
	{ }

	void nes_vt_hummer_2mb(machine_config& config) ATTR_COLD;
	void nes_vt_hummer_4mb(machine_config& config) ATTR_COLD;
};

class nes_vt_sp69_state : public nes_vt_state
{
public:
	nes_vt_sp69_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt_state(mconfig, type, tag)
	{ }

	void nes_vt_4mb_sp69(machine_config& config) ATTR_COLD;
};

class nes_vt_ablping_state : public nes_vt_state
{
public:
	nes_vt_ablping_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt_state(mconfig, type, tag)
	{ }

	void nes_vt_2mb_ablping(machine_config& config) ATTR_COLD;
	void nes_vt_2mb_vfootbal(machine_config& config) ATTR_COLD;

private:
	uint8_t ablping_extraio_r();
	void ablping_extraio_w(uint8_t data);
};


class nes_vt_ablpinb_state : public nes_vt_state
{
public:
	nes_vt_ablpinb_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt_state(mconfig, type, tag),
		m_ablpinb_in0_val(0),
		m_plunger(*this, "PLUNGER")
	{ }

	void nes_vt_waixing_alt_4mb_sporzpp(machine_config& config) ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	virtual uint8_t in0_r() override;
	virtual uint8_t in1_r() override;
	virtual void in0_w(uint8_t data) override;

	uint8_t m_ablpinb_in0_val;

	int m_plunger_off;
	int m_plunger_state_count;

	required_ioport m_plunger;
};


class nes_vt_tvmjfc_state : public nes_vt_state
{
public:
	nes_vt_tvmjfc_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt_state(mconfig, type, tag),
		m_mj_panel_index(0),
		m_mj_panel(*this, "MJ%u", 0U)
	{ }

	void nes_vt_tvmjfc(machine_config& config) ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;

	virtual uint8_t in1_r() override;
	virtual void in0_w(uint8_t data) override;

private:
	uint8_t m_mj_panel_index;
	required_ioport_array<10> m_mj_panel;
};


uint8_t nes_vt_base_state::vt_rom_r(offs_t offset)
{
	return m_prgrom[offset];
}

void nes_vt_base_state::vtspace_w(offs_t offset, uint8_t data)
{
	logerror("%s: vtspace_w %08x : %02x", machine().describe_context(), offset, data);
}

// VTxx can address 25-bit address space (32MB of ROM) so use maps with mirroring in depending on ROM size
void nes_vt_state::vt_external_space_map_32mbyte(address_map &map)
{
	map(0x0000000, 0x1ffffff).r(FUNC(nes_vt_state::vt_rom_r));
}

void nes_vt_state::vt_external_space_map_16mbyte(address_map &map)
{
	map(0x0000000, 0x0ffffff).mirror(0x1000000).r(FUNC(nes_vt_state::vt_rom_r));
}

void nes_vt_state::vt_external_space_map_8mbyte(address_map &map)
{
	map(0x0000000, 0x07fffff).mirror(0x1800000).r(FUNC(nes_vt_state::vt_rom_r));
}

void nes_vt_state::vt_external_space_map_4mbyte(address_map &map)
{
	map(0x0000000, 0x03fffff).mirror(0x1c00000).r(FUNC(nes_vt_state::vt_rom_r));
}

void nes_vt_state::vt_external_space_map_2mbyte(address_map &map)
{
	map(0x0000000, 0x01fffff).mirror(0x1e00000).r(FUNC(nes_vt_state::vt_rom_r));
}

void nes_vt_state::vt_external_space_map_1mbyte(address_map &map)
{
	map(0x0000000, 0x00fffff).mirror(0x1f00000).r(FUNC(nes_vt_state::vt_rom_r));
}

void nes_vt_state::vt_external_space_map_512kbyte(address_map &map)
{
	map(0x0000000, 0x007ffff).mirror(0x1f80000).r(FUNC(nes_vt_state::vt_rom_r));
}

// Win Lose Draw has RAM as well as ROM
void nes_vt_swap_op_d5_d6_state::vt_external_space_map_senwld_512kbyte(address_map &map)
{
	map(0x0000000, 0x007ffff).r(FUNC(nes_vt_swap_op_d5_d6_state::vt_rom_r));
	map(0x0100000, 0x010ffff).ram();
	map(0x0180000, 0x01fffff).r(FUNC(nes_vt_swap_op_d5_d6_state::vt_rom_r));
}

void nes_vt_state::vt_external_space_map_1mbyte_majkon(address_map &map)
{
	map(0x0000000, 0x00fffff).mirror(0x1f00000).r(FUNC(nes_vt_state::vt_rom_r));
	map(0x1400000, 0x1401fff).ram(); // rush'n attack writes to chr space, after setting the program and character outer bank to a mirror, is the correct way to handle it?
}

template <uint8_t NUM> uint8_t nes_vt_base_state::extrain_r()
{
	if (m_exin[NUM])
		return m_exin[NUM]->read();
	else
	{
		logerror("%s: extrain_r (port %d) (not hooked up)\n", NUM, machine().describe_context());
	}
	return 0x00;
}


/* Standard I/O handlers (NES Controller clone) */

uint8_t nes_vt_base_state::in0_r()
{
	//logerror("%s: in0_r\n", machine().describe_context());
	uint8_t ret = 0x40;
	ret |= m_latch0 & 1;
	m_latch0 >>= 1;
	return ret;
}

uint8_t nes_vt_base_state::in1_r()
{
	//logerror("%s: in1_r\n", machine().describe_context());
	uint8_t ret = 0x40;
	ret |= m_latch1 & 1;
	m_latch1 >>= 1;
	return ret;
}

void nes_vt_base_state::in0_w(uint8_t data)
{
	//logerror("%s: in0_w %02x\n", machine().describe_context(), data);

	// need to check this or some games (eg cybar120 Aero Engine) won't have working inputs as they repeatedly write a pattern of 02 / 00 here between fetches which resets the latch
	if ((data & 0x01) != (m_previous_port0 & 0x01))
	{
		if (data & 0x01)
		{
			m_latch0 = m_io0->read();
			m_latch1 = m_io1->read();
		}
	}

	m_previous_port0 = data;
}


// ablping polls this (also writes here) what is it? 4-bit DAC? PCM? (inputs only start responding once it finishes writing data on startup but takes longer than a sample should)
// (this is the extended IO port on VT)
uint8_t nes_vt_ablping_state::ablping_extraio_r()
{
	// needs to change at least
	return machine().rand()&0xf;
};

void nes_vt_ablping_state::ablping_extraio_w(uint8_t data)
{
	popmessage("ablping_extraio_w %02x", data);
};


void nes_vt_base_state::machine_start()
{
	m_latch0 = 0;
	m_latch1 = 0;
	m_previous_port0 = 0;

	save_item(NAME(m_latch0));
	save_item(NAME(m_latch1));
	save_item(NAME(m_previous_port0));
}

void nes_vt_base_state::machine_reset()
{

}


void nes_vt_ablpinb_state::machine_start()
{
	nes_vt_base_state::machine_start();

	save_item(NAME(m_plunger_off));
	save_item(NAME(m_plunger_state_count));
	save_item(NAME(m_ablpinb_in0_val));

}

void nes_vt_ablpinb_state::machine_reset()
{
	nes_vt_base_state::machine_reset();

	m_plunger_off = 0;
	m_plunger_state_count = 0;
	m_ablpinb_in0_val = 0;
}


uint8_t nes_vt_ablpinb_state::in0_r()
{
	if (m_plunger_off)
	{
		m_plunger_state_count++;

		if (m_plunger_state_count == 5) // make sure it's high for enough reads to keep the code flowing
		{
			m_plunger_off = 0;
			m_plunger_state_count = 0;
		}
	}
	else
	{
		m_plunger_state_count++;

		if ((m_plunger_state_count >= m_plunger->read()) || (m_plunger_state_count >= 0x80)) // if it stays low for too many frames the gfx corrupt,
		{
			m_plunger_off = 1;
			m_plunger_state_count = 0;
		}
	}

	uint8_t ret = m_io0->read() & ~0x01;

	return m_plunger_off | ret;
}


uint8_t nes_vt_ablpinb_state::in1_r()
{
	uint8_t i = machine().rand() & 0x10;

	// maybe this transition takes some time in reality?
	i |= (m_ablpinb_in0_val & 0x04) ? 0x00 : 0x08;

	uint8_t ret = m_io1->read() & ~0x18;

	return i | ret;
}

void nes_vt_ablpinb_state::in0_w(uint8_t data)
{
	// write 0x04 to 0x4016 sets bit 0x08 in 0x4017
	// write 0x00 to 0x4016 clears bit 0x08 in 0x4017
	// could be related to vibration motor?

	m_ablpinb_in0_val = data;
	logerror("in0_w %02x\n", data);
}


void nes_vt_tvmjfc_state::machine_start()
{
	nes_vt_base_state::machine_start();

	save_item(NAME(m_mj_panel_index));
}


uint8_t nes_vt_tvmjfc_state::in1_r()
{
	uint8_t panel_data = m_mj_panel_index >= 10 ? 0xff : m_mj_panel[m_mj_panel_index]->read();
	if (m_previous_port0 & 0x02)
	{
		// "B" side of input matrix: 4B -> bit 4; 2B -> bit 3; 3B -> bit 2; 1B -> bit 1
		return (panel_data & 0xf0) >> 3;
	}
	else
	{
		// "A" side of input matrix: 4A -> bit 4; 2A -> bit 3; 3A -> bit 2; 1A -> bit 1
		return (panel_data & 0x0f) << 1;
	}
}

void nes_vt_tvmjfc_state::in0_w(uint8_t data)
{
	if (data & 0x01)
		m_mj_panel_index = 0;
	else if (!(data & 0x02) && (m_previous_port0 & 0x02))
		m_mj_panel_index++;

	m_previous_port0 = data;
}


void nes_vt_base_state::configure_soc(nes_vt02_vt03_soc_device* soc)
{
	soc->set_addrmap(AS_PROGRAM, &nes_vt_state::vt_external_space_map_32mbyte);
	soc->read_0_callback().set(FUNC(nes_vt_base_state::in0_r));
	soc->read_1_callback().set(FUNC(nes_vt_base_state::in1_r));
	soc->write_0_callback().set(FUNC(nes_vt_base_state::in0_w));

	soc->extra_read_0_callback().set(FUNC(nes_vt_base_state::extrain_r<0>));
	soc->extra_read_1_callback().set(FUNC(nes_vt_base_state::extrain_r<1>));
	soc->extra_read_2_callback().set(FUNC(nes_vt_base_state::extrain_r<2>));
	soc->extra_read_3_callback().set(FUNC(nes_vt_base_state::extrain_r<3>));
}

void nes_vt_state::nes_vt_512kb(machine_config& config)
{
	NES_VT02_VT03_SOC(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_state::vt_external_space_map_512kbyte);
}

void nes_vt_state::nes_vt_1mb(machine_config& config)
{
	NES_VT02_VT03_SOC(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_state::vt_external_space_map_1mbyte);
}

void nes_vt_state::nes_vt_2mb(machine_config& config)
{
	NES_VT02_VT03_SOC(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_state::vt_external_space_map_2mbyte);
}



void nes_vt_state::nes_vt_4mb(machine_config& config)
{
	NES_VT02_VT03_SOC(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_state::vt_external_space_map_4mbyte);
}

void nes_vt_state::nes_vt_8mb(machine_config& config)
{
	NES_VT02_VT03_SOC(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_state::vt_external_space_map_8mbyte);
}

void nes_vt_state::nes_vt_16mb(machine_config& config)
{
	NES_VT02_VT03_SOC(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_state::vt_external_space_map_16mbyte);
}

void nes_vt_state::nes_vt_32mb(machine_config& config)
{
	NES_VT02_VT03_SOC(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_state::vt_external_space_map_32mbyte);
}

void nes_vt_state::nes_vt_pal_1mb(machine_config& config)
{
	NES_VT02_VT03_SOC_PAL(config, m_soc, PAL_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_state::vt_external_space_map_1mbyte);
}

void nes_vt_state::nes_vt_pal_2mb(machine_config& config)
{
	NES_VT02_VT03_SOC_PAL(config, m_soc, PAL_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_state::vt_external_space_map_2mbyte);
}

void nes_vt_state::nes_vt_pal_4mb(machine_config& config)
{
	NES_VT02_VT03_SOC_PAL(config, m_soc, PAL_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_state::vt_external_space_map_4mbyte);
}

void nes_vt_state::nes_vt_pal_8mb(machine_config& config)
{
	NES_VT02_VT03_SOC_PAL(config, m_soc, PAL_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_state::vt_external_space_map_8mbyte);
}

void nes_vt_state::nes_vt_pal_16mb(machine_config& config)
{
	NES_VT02_VT03_SOC_PAL(config, m_soc, PAL_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_state::vt_external_space_map_16mbyte);
}


void nes_vt_waixing_state::nes_vt_waixing_512kb(machine_config &config)
{
	NES_VT02_VT03_SOC_WAIXING(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_waixing_state::vt_external_space_map_512kbyte);
}

void nes_vt_waixing_state::nes_vt_waixing_512kb_rasterhack(machine_config &config)
{
	nes_vt_waixing_512kb(config);
	m_soc->force_raster_timing_hack();
}


void nes_vt_waixing_state::nes_vt_waixing_2mb(machine_config &config)
{
	NES_VT02_VT03_SOC_WAIXING(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_waixing_state::vt_external_space_map_2mbyte);
}

void nes_vt_waixing_alt_state::nes_vt_waixing_alt_4mb(machine_config &config)
{
	NES_VT02_VT03_SOC_WAIXING(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_waixing_state::vt_external_space_map_4mbyte);
	m_soc->set_8000_scramble(0x3, 0x2, 0x1, 0x0, 0x5, 0x4);
	//m_soc->set_8006_scramble(0x7, 0x8); // this is the default config in the SoC device
}

void nes_vt_waixing_alt_state::nes_vt_waixing_alt_pal_8mb(machine_config &config)
{
	NES_VT02_VT03_SOC_WAIXING_PAL(config, m_soc, PAL_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_waixing_alt_state::vt_external_space_map_8mbyte);
	m_soc->set_8000_scramble(0x3, 0x2, 0x1, 0x0, 0x5, 0x4);
	//m_soc->set_8006_scramble(0x7, 0x8); // this is the default config in the SoC device;
}

void nes_vt_waixing_alt_sporzpp_state::nes_vt_waixing_alt_4mb_sporzpp(machine_config& config)
{
	NES_VT02_VT03_SOC_WAIXING(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);

	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_ablping_state::vt_external_space_map_4mbyte);
	m_soc->set_8000_scramble(0x3, 0x2, 0x1, 0x0, 0x5, 0x4);
	//m_soc->set_8006_scramble(0x7, 0x8); // this is the default config in the SoC device
}

void nes_vt_waixing_alt_sporzpp_state::nes_vt_pal_4mb_sporzbxa(machine_config& config)
{
	NES_VT02_VT03_SOC_PAL(config, m_soc, PAL_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_state::vt_external_space_map_4mbyte);
}

void nes_vt_hum_state::nes_vt_hummer_2mb(machine_config& config)
{
	NES_VT02_VT03_SOC_HUMMER(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_sp69_state::vt_external_space_map_2mbyte);
	//m_soc->set_8000_scramble(0x4, 0x5, 0x0, 0x1, 0x2, 0x3); // this is the default config in the SoC device
	//m_soc->set_8006_scramble(0x7, 0x8); // this is the default config in the SoC device
}

void nes_vt_hum_state::nes_vt_hummer_4mb(machine_config& config)
{
	nes_vt_hummer_2mb(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_hum_state::vt_external_space_map_4mbyte);
}

void nes_vt_pjoy_state::nes_vt_pjoy_4mb(machine_config &config)
{
	NES_VT02_VT03_SOC(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_sp69_state::vt_external_space_map_4mbyte);
	//m_soc->set_8000_scramble(0x4, 0x5, 0x0, 0x1, 0x2, 0x3); // this is the default config in the SoC device
	m_soc->set_8006_scramble(0x8, 0x7);
	m_soc->set_410x_scramble(0x8, 0x7);
}


void nes_vt_sp69_state::nes_vt_4mb_sp69(machine_config& config)
{
	NES_VT02_VT03_SOC_SPORTS(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_sp69_state::vt_external_space_map_4mbyte);
	//m_soc->set_8000_scramble(0x4, 0x5, 0x0, 0x1, 0x2, 0x3); // this is the default config in the SoC device
	//m_soc->set_8006_scramble(0x7, 0x8);  // this is the default config in the SoC device
}

void nes_vt_ablping_state::nes_vt_2mb_ablping(machine_config &config)
{
	NES_VT02_VT03_SOC_SPORTS_PAL(config, m_soc, PAL_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_ablping_state::vt_external_space_map_2mbyte);
	//m_soc->set_8000_scramble(0x4, 0x5, 0x0, 0x1, 0x2, 0x3); // this is the default config in the SoC device
	//m_soc->set_8006_scramble(0x7, 0x8); // this is the default config in the SoC device

	m_soc->extra_read_2_callback().set(FUNC(nes_vt_ablping_state::ablping_extraio_r));
	m_soc->extra_read_3_callback().set(FUNC(nes_vt_ablping_state::ablping_extraio_r));
	m_soc->extra_write_2_callback().set(FUNC(nes_vt_ablping_state::ablping_extraio_w));
	m_soc->extra_write_3_callback().set(FUNC(nes_vt_ablping_state::ablping_extraio_w));
}

void nes_vt_ablping_state::nes_vt_2mb_vfootbal(machine_config &config)
{
	NES_VT02_VT03_SOC_PAL(config, m_soc, PAL_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_ablping_state::vt_external_space_map_2mbyte);

	m_soc->extra_read_2_callback().set(FUNC(nes_vt_ablping_state::ablping_extraio_r));
	m_soc->extra_read_3_callback().set(FUNC(nes_vt_ablping_state::ablping_extraio_r));
	m_soc->extra_write_2_callback().set(FUNC(nes_vt_ablping_state::ablping_extraio_w));
	m_soc->extra_write_3_callback().set(FUNC(nes_vt_ablping_state::ablping_extraio_w));
}

uint8_t nes_vt_base_state::upper_412c_r()
{
	logerror("%s: upper_412c_r\n", machine().describe_context());
	return 0x00;
}

uint8_t nes_vt_base_state::upper_412d_r()
{
	logerror("%s: upper_412d_r\n", machine().describe_context());
	return 0x00;
}

void nes_vt_base_state::upper_412c_w(uint8_t data)
{
	logerror("%s: upper_412c_w %02x\n", machine().describe_context(), data);
}


void nes_vt_state::nes_vt_1mb_majkon(machine_config& config)
{
	NES_VT02_VT03_SOC(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_state::vt_external_space_map_1mbyte_majkon);
	m_soc->force_raster_timing_hack();
}


static INPUT_PORTS_START( nes_vt )
	PORT_START("IO0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_8WAY

	PORT_START("IO1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_8WAY
INPUT_PORTS_END

static INPUT_PORTS_START( lxnoddy )
	PORT_START("IO0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1) // not used?
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) // not used?
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) // not used?
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_8WAY // steer left?
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_8WAY // steer right?

	PORT_START("IO1")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( nes_vt_ddr )
	PORT_START("IO0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_NAME("Up Arrow") PORT_16WAY // NOT A JOYSTICK!!
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_NAME("Down Arrow") PORT_16WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_NAME("Left Arrow") PORT_16WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("Right Arrow") PORT_16WAY

	PORT_START("IO1")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


static INPUT_PORTS_START( dbdancem )
	PORT_START("IO0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_NAME("P1 Up Arrow") PORT_16WAY // NOT A JOYSTICK!!
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_NAME("P1 Down Arrow") PORT_16WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_NAME("P1 Left Arrow") PORT_16WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("P1 Right Arrow") PORT_16WAY

	PORT_START("IO1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2) PORT_NAME("P2 Up Arrow") PORT_16WAY // NOT A JOYSTICK!!
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_NAME("P2 Down Arrow") PORT_16WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_NAME("P2 Left Arrow") PORT_16WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_NAME("P2 Right Arrow") PORT_16WAY
INPUT_PORTS_END


void nes_vt_swap_op_d5_d6_state::nes_vt_vh2009(machine_config &config)
{
	NES_VT02_VT03_SOC_SCRAMBLE(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
}

void nes_vt_swap_op_d5_d6_state::nes_vt_vh2009_pal(machine_config &config)
{
	NES_VT02_VT03_SOC_SCRAMBLE_PAL(config, m_soc, PAL_APU_CLOCK);
	configure_soc(m_soc);
}

void nes_vt_swap_op_d5_d6_state::nes_vt_vh2009_1mb(machine_config& config)
{
	nes_vt_vh2009(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_swap_op_d5_d6_state::vt_external_space_map_1mbyte);
}

void nes_vt_swap_op_d5_d6_state::nes_vt_vh2009_2mb(machine_config& config)
{
	nes_vt_vh2009(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_swap_op_d5_d6_state::vt_external_space_map_2mbyte);
}

void nes_vt_swap_op_d5_d6_state::nes_vt_vh2009_pal_2mb(machine_config& config)
{
	nes_vt_vh2009_pal(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_swap_op_d5_d6_state::vt_external_space_map_2mbyte);
}

void nes_vt_swap_op_d5_d6_state::nes_vt_vh2009_4mb(machine_config& config)
{
	nes_vt_vh2009(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_swap_op_d5_d6_state::vt_external_space_map_4mbyte);
}

void nes_vt_swap_op_d5_d6_state::nes_vt_vh2009_8mb(machine_config& config)
{
	nes_vt_vh2009(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_swap_op_d5_d6_state::vt_external_space_map_8mbyte);
}


void nes_vt_swap_op_d5_d6_state::nes_vt_senwld_512kb(machine_config &config)
{
	nes_vt_vh2009(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt_swap_op_d5_d6_state::vt_external_space_map_senwld_512kbyte);
}




static INPUT_PORTS_START( ablpinb )
	PORT_START("IO0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // (analog plunger) has to toggle or code gets stuck in interrupt and dies due to nested interrupts
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Select" )
	PORT_BIT( 0xfc, IP_ACTIVE_HIGH, IPT_UNUSED ) // not stored

	PORT_START("IO1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START1 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Left Flipper" )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Right Flipper" )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // has to toggle or code gets stuck on startup (maybe should cycle automatically when different inputs are available?)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // has to toggle once on the ABL logo or gets stuck in loop, checked in multiple places tho
	PORT_BIT( 0xe0, IP_ACTIVE_HIGH, IPT_UNUSED ) // not stored

	PORT_START("PLUNGER")
	PORT_BIT(0x00ff, 0x0000, IPT_PEDAL ) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_MINMAX(0x00,0xbf) PORT_NAME("Plunger")  PORT_CENTERDELTA(255)

	PORT_START("EXTRAIN3")
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("NUDGE" )
INPUT_PORTS_END


static INPUT_PORTS_START( sporzpp )
	PORT_START("IO0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_8WAY

	PORT_START("IO1")
	PORT_DIPNAME( 0x0001, 0x0000, "P2:0001" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0001, "0001" )
	PORT_DIPNAME( 0x0002, 0x0000, "P2:0002" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0002, "0002" )
	PORT_DIPNAME( 0x0004, 0x0000, "P2:0004" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0004, "0004" )
	PORT_DIPNAME( 0x0008, 0x0000, "P2:0008" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0008, "0008" )
	PORT_DIPNAME( 0x0010, 0x0000, "P2:0010" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0010, "0010" )
	PORT_DIPNAME( 0x0020, 0x0000, "P2:0020" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0020, "0020" )
	PORT_DIPNAME( 0x0040, 0x0000, "P2:0040" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0040, "0040" )
	PORT_DIPNAME( 0x0080, 0x0000, "P2:0080" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0080, "0080" )
INPUT_PORTS_END


// the test mode shows 2 gamepads, however this is not the control scheme the game uses
// there is a reset button too but it doesn't seem to be a software switch
static INPUT_PORTS_START( majgnc )
	PORT_INCLUDE(nes_vt)

	PORT_MODIFY("IO0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_8WAY

	PORT_MODIFY("IO1")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("EXTRAIN0")
	PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("EXTRAIN1")
	PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("EXTRAIN2")
	PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("EXTRAIN3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("3")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("5 / BET")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("4")
INPUT_PORTS_END

static INPUT_PORTS_START( timetp36 )
	PORT_INCLUDE(nes_vt)

	PORT_MODIFY("IO0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("B")

	PORT_MODIFY("IO1") // no 2nd player
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	// where does the 'Y' button map? no games use it?
	PORT_START("EXTRAIN0") // see code at 8084, stored at 0x66
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("X") // used in the NAM-1975 rip-off 'Army Strike'
	PORT_DIPNAME( 0x02, 0x02, "Unknown Bit 0" )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_DIPNAME( 0x04, 0x04, "Unknown Bit 1" ) // see code at 808D, stored at 0x68 (never read?)
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Unknown Bit 2" )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("EXTRAIN1") // code at 809A reads this in, stored at 0x156
	PORT_DIPNAME( 0x01, 0x01, "Unknown Bit 3" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x06, 0x04, DEF_STR( Difficulty ) ) // 3 possible slider positions
	PORT_DIPSETTING(    0x06, DEF_STR( Easy ) ) // 3 minutes timer in Bombs Away
	PORT_DIPSETTING(    0x04, DEF_STR( Normal ) ) // 2 minute 30
	PORT_DIPSETTING(    0x02, DEF_STR( Hard ) ) // 2 minute
	PORT_DIPSETTING(    0x00, "Hard (duplicate)" )
	PORT_DIPNAME( 0x08, 0x08, "Unknown Bit 4" ) //  ... code at 8064 instead seems to be reading 8 bits with a shifter? stored at 0x67 (investigate)
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("EXTRAIN2")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("EXTRAIN3")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


// Mahjong controller: ES-MJB08 LB090728
// Input matrix mostly matches silkscreen labels on solder side. Each button feed into "KP6401" 32-pin rectangular ASIC (or MCU?) glob @ U2.
// The program reencodes key presses to emulate the Famicom mahjong controller.
static INPUT_PORTS_START( tvmjfc )
	PORT_START("IO0")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IO1")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("MJ0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_MAHJONG_B ) // [3A/D1]
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_MAHJONG_A ) // [2A/D1]
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_MAHJONG_KAN ) // "GANG" [4A/D1]
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_MAHJONG_RON ) // "CHI" [3B/D1]
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("MJ1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) // pon
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_MAHJONG_REACH ) // "JIAO" [3A/D2; remapped to 1A/D5]
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_MAHJONG_N ) PORT_NAME("%p Mahjong N/Start") // "N/決定" [2A/D2]
	PORT_BIT( 0xf8, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("MJ2")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("MJ3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_MAHJONG_D ) // [3A/D4]
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_MAHJONG_G ) // [2A/D4]
	PORT_BIT( 0x18, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_MAHJONG_J ) // [3B/D4]
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("MJ4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) // reach
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED ) // performs "select" function when pressed together with I, but this seems like a glitch
	PORT_BIT( 0xf8, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("MJ5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_MAHJONG_L ) // [1A/D6]
	PORT_BIT( 0x1e, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_MAHJONG_PON ) // "PENG" [3B/D6; remapped to 1A/D2]
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("MJ6")
	PORT_BIT( 0x03, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_MAHJONG_F ) // [2A/D7]
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_MAHJONG_H ) // [4A/D7]
	PORT_BIT( 0x70, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_MAHJONG_E ) // [4B/D7]

	PORT_START("MJ7")
	PORT_BIT( 0x1f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_MAHJONG_K ) // [3B/D8]
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("MJ8")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_SELECT )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_MAHJONG_C ) // [3A/D9]
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_MAHJONG_I ) // [2A/D9]
	PORT_BIT( 0x18, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_MAHJONG_M ) // [3B/D9]
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_MAHJONG_CHI ) // [4B/D9]

	PORT_START("MJ9")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED ) // read but discarded
INPUT_PORTS_END



ROM_START( vdogdeme )
	ROM_REGION( 0x100000, "mainrom", 0 )
	ROM_LOAD( "vdog.bin", 0x00000, 0x100000, CRC(29dae36d) SHA1(e7192c5b16f3e658b0802e5c50fab244e974d9c2) )
ROM_END

ROM_START( vdogdemo )
	ROM_REGION( 0x80000, "mainrom", 0 )
	ROM_LOAD( "rom.bin", 0x00000, 0x80000, CRC(054af705) SHA1(e730aeaa94b9cc28aa8b512a5bf411ec45226831) )
ROM_END


ROM_START( pinkjelly )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "seesaw.bin", 0x00000, 0x200000, CRC(67b5a079) SHA1(36ebfd64809af072b73acfa3a426b57017851bf4) )
ROM_END

ROM_START( vtpinball )
	ROM_REGION( 0x80000, "mainrom", 0 )
	ROM_LOAD( "rom.bin", 0x00000, 0x80000, CRC(62e52c23) SHA1(b83b82c928b9fe82abfaa915196322153787c8ce) )
ROM_END

ROM_START( ablpinb )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "abl_pinball.bin", 0x00000, 0x200000, CRC(b2ce20fb) SHA1(f2af7f26fcdce6f26db5c71727ab380240f44f74) )
ROM_END

ROM_START( vtsndtest )
	ROM_REGION( 0x80000, "mainrom", 0 )
	ROM_LOAD( "rom.bin", 0x00000, 0x80000, CRC(ddc2bc9c) SHA1(fb9209c62d1496ba7fe379e8a078cabd48cccd9b) )
ROM_END

ROM_START( vtboxing )
	ROM_REGION( 0x80000, "mainrom", 0 )
	ROM_LOAD( "rom.bin", 0x00000, 0x80000, CRC(c115b1af) SHA1(82106e1c11c3279c5d8731c112f713fa3f290125) )
ROM_END

ROM_START( sudopptv )
	ROM_REGION( 0x80000, "mainrom", ROMREGION_ERASEFF )
	ROM_LOAD( "sudokupnptvgame_29lv400tc_000422b9.bin", 0x00000, 0x80000, CRC(722cc36d) SHA1(1f6d1f57478cf175a36722b39c52eded4b669f81) )
ROM_END

ROM_START( mc_dgear )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "dreamgear 75-in-1.prg", 0x00000, 0x400000, CRC(9aabcb8f) SHA1(aa9446b7777fa64503871225fcaf2a17aafd9af1) )
ROM_END

ROM_START( sudo6in1 )
	ROM_REGION( 0x100000, "mainrom", 0 )
	ROM_LOAD( "6n1sudoku.bin", 0x00000, 0x100000, CRC(31089cd4) SHA1(dbfe41d327278dbfa46c7ad7ef327c20648562c1) )
ROM_END

ROM_START( sen101 )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "101n1.bin", 0x00000, 0x400000, CRC(b03e1824) SHA1(c9ac4e16220414c1aa679133191140ced9986e9c) )
ROM_END

ROM_START( hs36red )
	ROM_REGION( 0x200000, "mainrom", 0 ) // address and data lines swapped
	ROM_LOAD( "mx29lv160cb.u3", 0x00000, 0x200000, CRC(318a81bb) SHA1(8b207e6a5fca53cbf383b79ff570fcdb89639fa3) )
ROM_END

ROM_START( hs36blk )
	ROM_REGION( 0x200000, "mainrom", 0 ) // address and data lines swapped
	ROM_LOAD( "mx29lv160cbtc.u3", 0x00000, 0x200000, CRC(b5cf91a0) SHA1(399f015fb0580c90928e7f3d73810cc4b6cc70d9) )
ROM_END

ROM_START( mc_dg101 )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "dreamgear 101-in-1.prg", 0x00000, 0x400000, CRC(6a7cd8f4) SHA1(9a5ceb8e5e38eb93699dbb14c2c36f3a501d9c45) )
ROM_END

ROM_START( mc_aa2 )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "100 in 1 arcade action ii.prg", 0x00000, 0x400000, CRC(33923995) SHA1(a206e8c0ee6e86adb800cf66697defabcbd01902) )
ROM_END

ROM_START( mc_105te )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "2011 super hik 105-in-1 turbo edition.prg", 0x00000, 0x800000, CRC(c0f85771) SHA1(8c4182b1de3be10dd895089823cc67a9d12589ef) )
ROM_END

ROM_START( mc_sp69 )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "sports game 69-in-1.prg", 0x00000, 0x400000, CRC(1242da7f) SHA1(bb8f99b1f4a4783b3f7e54d74f1f2a6a628da154) )
ROM_END

ROM_START( vsmaxx17 )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "vsmaxx17.bin", 0x00000, 0x200000, CRC(f3fccbb9) SHA1(8b70b10d28f03e72f6b35199001955033a65fd5d) )  // M6MG3D641RB
ROM_END

ROM_START( vsmax25v )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "vsmaxx25n1_2.bin", 0x00000, 0x400000, CRC(e17e076d) SHA1(0f4e3b6b33ab75dcc12dc02d3347ddb53275c777) )
ROM_END

ROM_START( dgun851 )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "dgun851.bin", 0x00000, 0x400000, CRC(9d51c9fc) SHA1(6f49ea3343eb6e90938aabc9660783f1fc7f6084) )
ROM_END

ROM_START( dgun853 )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "dgpnp50n1dgun853.bin", 0x00000, 0x800000, CRC(118f7286) SHA1(0f4ad7141e887bddba1ab37e75de08e9d56ad841) )
ROM_END

ROM_START( vsmaxx77 )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "vsmaxx77.bin", 0x00000, 0x800000, CRC(03f1f4b5) SHA1(13f7ecea3765cffcd3065de713abdabd24946b99) )
ROM_END

ROM_START( vsmaxxvd )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "vsmaxxvideo.bin", 0x00000, 0x800000, CRC(af365a77) SHA1(8119fcef3e1a2ade93d36740d5df451919f0e541) )
ROM_END

ROM_START( polmega )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "megamax.bin", 0x00000, 0x400000, CRC(ef3aade3) SHA1(0c130080ace000cbe43e70a805d4301e05840294) )
ROM_END

ROM_START( silv35 )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "silverlit35.bin", 0x00000, 0x400000, CRC(7540e350) SHA1(a0cb456136560fa4d8a365dd44d815ec0e9fc2e7) )
ROM_END

ROM_START( sporzpp )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "gamesporzduetpingpong.bin", 0x00000, 0x400000, CRC(96af199b) SHA1(c14ff15683545c1edf03376cebcee7ac408da733) )
ROM_END

ROM_START( sporzbx )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "sporzboxing.bin", 0x00000, 0x400000, CRC(8b832c0b) SHA1(8193955a81e894a01308a80d5153f2ecfe134da6) )
ROM_END

ROM_START( sporzbxa )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "wlboxing.bin", 0x00000, 0x400000, CRC(5df7beb9) SHA1(dadcec310e4a7b3ca061c6fe6be319cda2445b24) )
ROM_END

ROM_START( sporztn )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "wirelesstennis.bin", 0x00000, 0x400000, CRC(e60f5ee1) SHA1(838ba7f4e9dcd0101eaaef5be883206d8856f45c) )
ROM_END

ROM_START( wldsoctv )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "worldsoccer.bin", 0x00000, 0x400000, CRC(8c0b184b) SHA1(fe1e7e83b9a2ae50dca1e7ea3bf7d691b8407511) )
ROM_END

ROM_START( solargm )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "solargames.bin", 0x00000, 0x800000, CRC(b49f0985) SHA1(68231614b333911c25168c533f1ae9bc79c36c38) )
ROM_END

ROM_START( pjoyn50 )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "power joy navigator 50-in-1.prg", 0x00000, 0x400000, CRC(d1bbadd4) SHA1(2186c71bcedf6c2eedf58233faa26fca9586aa40) )
ROM_END

ROM_START( pjoys30 )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "power joy supermax 30-in-1.prg", 0x00000, 0x400000, CRC(947ac898) SHA1(08bb99a8ad39c56780bc66f4e0a9830fba7372dc) )
ROM_END

ROM_START( pjoys60 )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "power joy supermax 60-in-1.prg", 0x00000, 0x400000, CRC(1ab45228) SHA1(d148924afc39fc588235331a1a30df6e0d8e1e18) )
ROM_END

ROM_START( joysti30 )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "joystick30.bin", 0x00000, 0x400000, CRC(b3f089af) SHA1(478d53d38eeffdbc4a1271d0e060aeb29e919502) )
ROM_END

ROM_START( lxnoddy )
	ROM_REGION( 0x200000, "mainrom", 0 )
	// PCB had a ROM twice this capacity, but lower half didn't give consistent reads, and upper address line was tied to VCC, making lower half inaccessible anyway
	// They likely just used a known failed flash ROM
	ROM_LOAD( "noddy.bin", 0x00000, 0x200000, CRC(a955307f) SHA1(26bee6403bfe3c93831b02c090383593218f60a9) )
ROM_END

// CoolBoy AEF-390 8bit Console, B8VPCBVer03 20130703 0401E2015897A
ROM_START( mc_8x6cb )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "888888-in-1,coolboy aef-390 8bit console, b8vpcbver03 20130703 0401e2015897a.prg", 0x00000, 0x400000, CRC(ca4bd948) SHA1(cfd6c0b03bb432de43d070100031b223c9ee7496) )
ROM_END

ROM_START( fccomp88 )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "m29dw323db.u3", 0x00000, 0x400000, CRC(77664c7e) SHA1(2499cbabf74951ea99e71546e54d37b8b18bb1f3) )
ROM_END

ROM_START( mc_sam60 )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "29lv160b.bin", 0x00000, 0x200000, CRC(7dac8efe) SHA1(ffb27ebb4299d5b9a4b976c418fcc7695200060c) )
ROM_END

ROM_START( ddrdismx )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "disney-ddr.bin", 0x00000, 0x200000, CRC(17fb3abb) SHA1(4d0eda4069ff46173468e579cdf9cc92b350146a) ) // 29LV160 Flash
ROM_END

ROM_START( dbdancem )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "dancemania_29lv160bt_00c222a7.bin", 0x00000, 0x200000, CRC(7250a837) SHA1(7205936215df84e3642c34a8b5991e8125da1785) )
ROM_END

ROM_START( megapad )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "megapad.bin", 0x00000, 0x200000, CRC(1eb603a8) SHA1(3de6f0620a0db0558daa7fd7ccf08d9d5607a6af) )
ROM_END

ROM_START( timetp36 )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "36in1.bin", 0x00000, 0x400000, CRC(e2fb8a6c) SHA1(163d257dd0e6dc19c8fab19cc363ea8be659c40a) )
ROM_END

ROM_START( timetp7 )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "gm802m.bin", 0x00000, 0x200000, CRC(2ab17abf) SHA1(8e7818043f8e670a35f8dbaebe318b872d95f3ca) )
ROM_END

ROM_START( ddrstraw )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "straws-ddr.bin", 0x00000, 0x200000, CRC(ce94e53a) SHA1(10c6970205a4df28086029c0a348225f57bf0cc5) ) // 26LV160 Flash
ROM_END

ROM_START( majkon )
	ROM_REGION( 0x100000, "mainrom", ROMREGION_ERASEFF )
	ROM_LOAD( "konamicollectorsseries.bin", 0x00000, 0x100000, CRC(47505e51) SHA1(3bfb05d7cfa2bb4c115335f0383fa4aa59db0b28) )
ROM_END

ROM_START( majgnc )
	ROM_REGION( 0x100000, "mainrom", ROMREGION_ERASEFF )
	ROM_LOAD( "majescogoldennuggetcasino_st29w800at_002000d7.bin", 0x00000, 0x100000, CRC(1a156a9d) SHA1(08be4079dd68c9cf05bb92e11a3da4f092d7cfea) )
ROM_END

ROM_START( vt25in1 )
	ROM_REGION( 0x100000, "mainrom", ROMREGION_ERASEFF )
	ROM_LOAD( "25in1.bin", 0x00000, 0x100000, CRC(1038b5ec) SHA1(e7d1ccafe0edcfa44c11412d2aa771f4ba96b5b8) )
ROM_END

ROM_START( zudugo )
	ROM_REGION( 0x400000, "mainrom", ROMREGION_ERASEFF )
	ROM_LOAD( "zudugo.bin", 0x00000, 0x400000, CRC(0fa9d9ad) SHA1(7533eaf51785d8fcced900ea0498281b0cf49dbf) )
ROM_END

ROM_START( ablping )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "abl_pingpong.bin", 0x00000, 0x200000, CRC(b31de1fb) SHA1(94e8afb2315ba1fa0892191c8e1832391e401c70) )
ROM_END

ROM_START( cbrs8 )
	ROM_REGION( 0x1000000, "mainrom", 0 )
	ROM_LOAD( "rs-8.bin", 0x00000, 0x1000000, BAD_DUMP CRC(10b2bed0) SHA1(0453a1e6769818ccf25dcf22b2c6198a5688a1d4) ) // BADADDRxxxxxxxxxxxxxxxxxxxxxxx-
ROM_END

ROM_START( mc_tv200 )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "s29gl064n90.bin", 0x00000, 0x800000, CRC(ae1905d2) SHA1(11582055713ba937c1ad32c4ada8683eebc1c83c) )
ROM_END

ROM_START( senwld )
	ROM_REGION( 0x80000, "mainrom", 0 )
	ROM_LOAD( "winlosedraw.bin", 0x00000, 0x80000, CRC(55910bf8) SHA1(c3a7594979d2167be13bf5235c454a22e1f4bb44))
ROM_END

ROM_START( ablmini )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "ablmini.bin", 0x00000, 0x800000, CRC(e65a2c3a) SHA1(9b4811e5b50b67d74b9602471767b8bcd24dd59b) )
ROM_END

ROM_START( protpp )
	ROM_REGION( 0x100000, "mainrom", 0 )
	ROM_LOAD( "vpingpong_s29al008d70tfi02_0001225b.bin", 0x00000, 0x100000, CRC(8cf46272) SHA1(298a6341d26712ec1f282e7514e995a7af5ac012) )
ROM_END

ROM_START( vfootbal )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "vfootball.u3", 0x00000, 0x200000, CRC(3b586f64) SHA1(92ee41ccfad32f7629bd43503cfb15e9624283ce) )
ROM_END

ROM_START( zdog )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "zdog.bin", 0x00000, 0x400000, CRC(5ed3485b) SHA1(5ab0e9370d4ed1535205deb0456878c4e400dd81) )
ROM_END

ROM_START( dgun2500 )
	ROM_REGION( 0x1000000, "mainrom", 0 )
	ROM_LOAD( "dgun2500.bin", 0x00000, 0x1000000, CRC(a2f963f3) SHA1(e29ed20ccdcf25b5640a607b3d2c9e6a4944e172) ) // 1ST AND 2ND HALF IDENTICAL
	ROM_IGNORE(0x1000000)
ROM_END

ROM_START( ppgc200g )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "m29dw641.u2", 0x00000, 0x800000, CRC(b16dc677) SHA1(c1984fde4caf9345d41d127db946d1c21ec43ae0) )
ROM_END

ROM_START( dgun2869 )
	ROM_REGION( 0x1000000, "mainrom", 0 )
	ROM_LOAD( "myarcaderetromicro_s29gl128p11tfiv1_0001227e.bin", 0x00000, 0x1000000, CRC(5e7fded2) SHA1(cf55ae7a128e3254a22933150caf94e269303ffb) ) // 29GL128
	ROM_IGNORE(0x100)
ROM_END

ROM_START( dgun2959 )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "dgun2959.bin", 0x00000, 0x1000000, CRC(6e9b2f45) SHA1(abac2c1783e99b02f9c44f714d5184aea86661ae) )
ROM_END

ROM_START( 88in1joy )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "88in1joystick.bin", 0x00000, 0x400000, CRC(86b8d819) SHA1(6da387b2e6ce02a3ec203e2af8a961959ba1cf62) )
ROM_END

ROM_START( gamezn2 )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD16_WORD_SWAP( "gamezone2.bin", 0x00000, 0x400000, CRC(f7b2d609) SHA1(7d2d8f6e822c4e6b97e9accaa524b7910c6b97bf) ) // byteswapped as protection?
ROM_END

ROM_START( tvmjfc )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "s29al016m90tfir2_tsop48.bin", 0x00000, 0x200000, CRC(28ef6219) SHA1(7ac2592f2a88532f537629660074ebae08efab82) )
ROM_END


void nes_vt_state::init_protpp()
{
	// this gets the tiles correct
	u8 *src = memregion("mainrom")->base();
	int len = memregion("mainrom")->bytes();

	std::vector<u8> buffer(len);
	{
		for (int i = 0; i < len; i++)
		{
			buffer[i] = bitswap<8>(src[i],3,1,2,0,7,5,6,4);
		}

		std::copy(buffer.begin(), buffer.end(), &src[0]);
	}
}

void nes_vt_state::init_gamezn2()
{
	u8 *src = memregion("mainrom")->base();
	int len = memregion("mainrom")->bytes();

	std::vector<u8> buffer(len);
	{
		for (int i = 0; i < len; i++)
		{
			buffer[i] = bitswap<8>(src[i],7,6,5,4,3,2,0,1); // bottom 2 bits are swapped?
		}

		std::copy(buffer.begin(), buffer.end(), &src[0]);
	}
}

void nes_vt_waixing_state::init_hs36in1()
{
	u8 *src = memregion("mainrom")->base();
	int len = memregion("mainrom")->bytes();

	std::vector<u8> buffer(len);
	{
		for (int i = 0; i < len; i++)
		{
			// TODO: check if uppermost address lines shouldn't be swapped
			// A16-A7 swap should be OK for hs36red, but is it also good for hs36blk?
			int newaddr = bitswap<21>(i, 20, 19, 18, 17, 16,
				15, 7, 9, 10,
				11, 12, 8, 13,
				14, 6, 5, 4,
				3, 2, 1, 0);

			buffer[i] = bitswap<8>(src[newaddr], 7, 6, 4, 5, 3, 1, 2, 0);
		}
		std::copy(buffer.begin(), buffer.end(), &src[0]);
	}
}

} // anonymous namespace


// earlier version of vdogdemo
CONS( 200?, vdogdeme,  0,  0,  nes_vt_1mb,    nes_vt, nes_vt_state, empty_init, "VRT", "V-Dog (prototype, earlier)", MACHINE_NOT_WORKING )

// this is glitchy even in other emulators, might just be entirely unfinished, it selects banks but they don't contain the required gfx?
CONS( 200?, vdogdemo,  0,  0,  nes_vt_512kb,    nes_vt, nes_vt_state, empty_init, "VRT", "V-Dog (prototype)", MACHINE_NOT_WORKING )

// Bundled as "VT03 Demo" on the V.R. Technology VT SDK
CONS( 200?, pinkjelly, 0,  0,  nes_vt_2mb,    nes_vt, nes_vt_state, empty_init, "VRT / Simmer Technology Co., Ltd.", "VRT VT SDK 'Pink Jelly' (VT03 Demo)", MACHINE_IMPERFECT_GRAPHICS )

// Bundled as "C-Compiler Demo Program 2" on the V.R. Technology VT SDK
CONS( 200?, vtpinball, 0,  0,  nes_vt_512kb,    nes_vt, nes_vt_state, empty_init, "VRT / OJ-Jungle", "VRT VT SDK 'Pinball' (C-Compiler Demo Program 2)", MACHINE_NOT_WORKING )

// Bundled as "Sound Generator FMDemo" on the V.R. Technology VT SDK
CONS( 200?, vtsndtest, 0,  0,  nes_vt_512kb,    nes_vt, nes_vt_state, empty_init, "VRT", "VRT VT SDK 'VT03 Sound Test' (Sound Generator FMDemo)", MACHINE_IMPERFECT_CONTROLS )

// Bundled as "Demo for VT03 Pic32" on the V.R. Technology VT SDK
CONS( 200?, vtboxing,     0,  0,  nes_vt_512kb, nes_vt, nes_vt_state, empty_init, "VRT", "VRT VT SDK 'Boxing' (Demo for VT03 Pic32)", MACHINE_NOT_WORKING )


// Menu system clearly started off as 'vtpinball'  Many elements seem similar to Family Pinball for the Famicom.
// 050329 (29th March 2005) date on PCB
CONS( 2005, ablpinb, 0,  0,  nes_vt_pal_2mb,    ablpinb, nes_vt_ablpinb_state, empty_init, "Advance Bright Ltd", "Pinball (P8002, ABL TV Game)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// need to map 2 player controls for Ping Pong, 'Eat-Bean' (the PacMan hack) gets stuck during intermission? (same happens on hardware?)
CONS( 2004, sporzpp,   0,  0,  nes_vt_waixing_alt_4mb_sporzpp,        sporzpp, nes_vt_waixing_alt_sporzpp_state, empty_init, "Macro Winners", "Game Sporz Wireless Duet Play Ping-Pong", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
// has some longer than expected delays when sounds should play on the Boxing part, but NES hacks are all functional
CONS( 2004, sporzbx,   0,        0,  nes_vt_waixing_alt_4mb_sporzpp,        sporzpp, nes_vt_waixing_alt_sporzpp_state, empty_init, "Macro Winners",                       "Game Sporz Wireless Boxing", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 2004, sporzbxa,  sporzbx,  0,  nes_vt_pal_4mb_sporzbxa,               sporzpp, nes_vt_waixing_alt_sporzpp_state, empty_init, "Macro Winners (Play Vision license)", "Wireless Boxing (PAL, Play Vision)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
// has some longer than expected delays when sounds should play on the Tennis part, but NES hacks are all functional, US version is sold in DreamGear branded box.
CONS( 2004, sporztn,   0,  0,  nes_vt_pal_4mb,        sporzpp, nes_vt_wldsoctv_state, empty_init, "Macro Winners (Play Vision license)", "Wireless Tennis (PAL, Play Vision)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
// missing PCM audio, but regular APU SFX work
CONS( 200?, wldsoctv,  0,  0,  nes_vt_pal_4mb,        nes_vt,  nes_vt_wldsoctv_state, empty_init, "Taikee", "World Soccer TV Game 10-in-1 (PAL)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// should be VT03 based
// for testing 'Shark', 'Octopus', 'Harbor', and 'Earth Fighter' use the extended colour modes, other games just seem to use standard NES modes
CONS( 200?, mc_dgear,  0,  0,  nes_vt_4mb,    nes_vt, nes_vt_state, empty_init, "dreamGEAR", "dreamGEAR 75-in-1", MACHINE_IMPERFECT_GRAPHICS )

CONS( 200?, sudo6in1,  0,  0,  nes_vt_pal_1mb,    nes_vt, nes_vt_state, empty_init, "Nice Code", "6-in-1 Sudoku Plug & Play", MACHINE_IMPERFECT_GRAPHICS ) // no manufacturer info on packaging, games seem to be from Nice Code, although this isn't certain

// small black unit, dpad on left, 4 buttons (A,B,X,Y) on right, Start/Reset/Select in middle, unit text "Sudoku Plug & Play TV Game"
CONS( 200?, sudopptv,  0, 0,  nes_vt_waixing_512kb_rasterhack,        nes_vt, nes_vt_waixing_state, empty_init, "Smart Planet", "Sudoku Plug & Play TV Game '6 Intelligent Games'", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

CONS( 200?, megapad,   0, 0,  nes_vt_waixing_2mb,        nes_vt, nes_vt_waixing_state, empty_init, "Waixing", "Megapad 31-in-1", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // Happy Biqi has broken sprites just like standalone cart (nes:happybiqa)

// 060303 date code on PCB
CONS( 2006, ablmini,   0, 0,  nes_vt_waixing_alt_pal_8mb, nes_vt, nes_vt_waixing_alt_state, empty_init, "Advance Bright Ltd", "Double Players Mini Joystick 80-in-1 (MJ8500, ABL TV Game)", MACHINE_IMPERFECT_GRAPHICS )

CONS( 200?, solargm,   0,  0, nes_vt_waixing_alt_pal_8mb, nes_vt, nes_vt_waixing_alt_state, empty_init, "<unknown>", "Solar Games 80-in-1 (PAL)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // Solar Games logo is also found in the SunPlus based Millennium Arcade units

// silver 'N64' type controller design
CONS( 200?, zudugo,    0, 0,  nes_vt_waixing_alt_4mb,     nes_vt, nes_vt_waixing_alt_state, empty_init, "Macro Winners / Waixing", "Zudu-go / 2udu-go", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // the styling on the box looks like a '2' in places, a 'Z' in others.

 // needs PCM samples, Y button is not mapped (not used by any of the games? some sources indicate it's just a hardware autofire button)
CONS( 200?, timetp36,  0, 0,  nes_vt_pal_4mb, timetp36, nes_vt_timetp36_state,        empty_init, "TimeTop", "Super Game 36-in-1 (TimeTop SuperGame) (PAL)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

CONS( 200?, timetp7,   0, 0,  nes_vt_pal_2mb, timetp36, nes_vt_timetp36_state,        empty_init, "TimeTop", "Super Game 7-in-1 (TimeTop SuperGame) (PAL)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

CONS( 2004, majkon,    0, 0,  nes_vt_1mb_majkon, nes_vt, nes_vt_state, empty_init, "Majesco (licensed from Konami) / JungleTac", "Konami Collector's Series Arcade Advanced", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // raster timing for Frogger needs a hack, Rush'n Attack also has raster timing issues on the status bar split

CONS( 200?, majgnc,    0, 0,  nes_vt_1mb, majgnc, nes_vt_state,  empty_init, "Majesco / JungleTac", "Golden Nugget Casino", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

CONS( 2004, vt25in1,   0, 0,  nes_vt_1mb, nes_vt, nes_vt_state,  empty_init, "<unknown>", "unknown VT02 based 25-in-1 handheld", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// CPU die is marked 'VH2009' There's also a 62256 RAM chip on the PCB, some scrambled opcodes
CONS( 2004, vsmaxx17,  0,  0,  nes_vt_vh2009_2mb,        nes_vt, nes_vt_swap_op_d5_d6_state, empty_init, "Senario / JungleTac",   "Vs Maxx 17-in-1", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // from a Green unit, '17 Classic & Racing Game'
CONS( 200?, vsmax25v,  0,  0,  nes_vt_vh2009_4mb,        nes_vt, nes_vt_swap_op_d5_d6_state, empty_init, "Senario / JungleTac",   "Vs Maxx 25-in-1 (VT03 hardware)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 2004, polmega,   0,  0,  nes_vt_vh2009_4mb,        nes_vt, nes_vt_swap_op_d5_d6_state, empty_init, "Polaroid / JungleTac",  "TV MegaMax active power game system 30-in-1 (MegaMax GPD001SDG)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 200?, dgun851,   0,  0,  nes_vt_vh2009_4mb,        nes_vt, nes_vt_swap_op_d5_d6_state, empty_init, "dreamGEAR / JungleTac", "Plug 'N' Play 30-in-1 (DGUN-851)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 200?, dgun853,   0,  0,  nes_vt_vh2009_8mb,        nes_vt, nes_vt_swap_op_d5_d6_state, empty_init, "dreamGEAR / JungleTac", "Plug 'N' Play 50-in-1 (DGUN-853)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 200?, silv35,    0,  0,  nes_vt_vh2009_4mb,        nes_vt, nes_vt_swap_op_d5_d6_state, empty_init, "SilverLit / JungleTac", "35 in 1 Super Twins", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 2004, vsmaxxvd,  0,  0,  nes_vt_vh2009_8mb,        nes_vt, nes_vt_swap_op_d5_d6_state, empty_init, "Senario / JungleTac",   "Vs Maxx Video Extreme 50-in-1 (with Speed Racer and Snood)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 200?, vsmaxx77,  0,  0,  nes_vt_vh2009_8mb,        nes_vt, nes_vt_swap_op_d5_d6_state, empty_init, "Senario / JungleTac",   "Vs Maxx Wireless 77-in-1", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 200?, joysti30,  0,  0,  nes_vt_vh2009_4mb,        nes_vt, nes_vt_swap_op_d5_d6_state, empty_init, "WinFun / JungleTac",    "Joystick 30", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // doesn't show WinFun onscreen, but packaging does

// has no audio, is there extra hardware, or is it just using unemulated VT features?
CONS( 2005, lxnoddy,   0,  0,  nes_vt_vh2009_pal_2mb,        lxnoddy, nes_vt_swap_op_d5_d6_state, empty_init, "Lexibook",   "Noddy's TV Console", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND )

// mostly bootleg NES games, but also has Frogger, Scramble and Asteroids in it
CONS( 200?, gamezn2,   0,        0,  nes_vt_4mb,    nes_vt, nes_vt_state, init_gamezn2, "<unknown>", "Game Zone II 128-in-1", MACHINE_IMPERFECT_GRAPHICS ) // was this PAL? (lots of raster splits are broken at the moment either way)

CONS( 2005, senwld,   0,          0,  nes_vt_senwld_512kb,    nes_vt, nes_vt_swap_op_d5_d6_state, empty_init, "Senario", "Win, Lose or Draw (Senario)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS ) // needs RAM in banked space, Alpha display emulating, Touchpad emulating etc.

 // seems to use PCM for all sound, some garbage at bottom of screen, needs correct inputs (seems to respond to start, and any direction input for 'hit' - check if they're power related)
CONS( 200?, protpp,   0,  0,  nes_vt_vh2009_1mb,      nes_vt, nes_vt_swap_op_d5_d6_state,        init_protpp, "Protocol", "Virtual Ping Pong (Protocol)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )

// this has 'Shark' and 'Octopus' etc. like mc_dgear but uses scrambled bank registers
// This was also often found in cart form with SunPlus / Famiclone hybrid systems to boost the game count, eg. the WiWi (ROM verified to match)
CONS( 200?, mc_sp69,   0,  0,  nes_vt_4mb_sp69,    nes_vt, nes_vt_sp69_state, empty_init, "<unknown>", "Sports Game 69 in 1", MACHINE_IMPERFECT_GRAPHICS  | MACHINE_IMPERFECT_SOUND)

// this game was also sold by dreamGEAR and several others companies, each time with a different name and different case, although the dumped version was from ABL, and it hasn't been confirmed that the ROMs are identical for the other units
// Super Ping Pong appears on the title screen, but not the box / product art which simply has "Ping Pong Plug & Play TV Game" on front/back/bottom/manual, and "Table Tennis Plug & Play TV Game" on left/right sides.  Product code is PP1100
// PCB has PP1100-MB 061110 on it, possible date YYMMDD code? (pinball is 050329, guitar fever is 070516, air blaster 050423, kickboxing 061011 etc.)
CONS( 2006, ablping,   0,        0,  nes_vt_2mb_ablping, nes_vt, nes_vt_ablping_state, empty_init, "Advance Bright Ltd", "Ping Pong / Table Tennis / Super Ping Pong (PP1100, ABL TV Game)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

CONS( 200?, vfootbal,  0,        0,  nes_vt_2mb_vfootbal,nes_vt, nes_vt_ablping_state, empty_init, "<unknown>", "Virtual Football (with 3 bonus games)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// Hummer systems, scrambled bank register
CONS( 200?, mc_sam60,  0,  0,  nes_vt_hummer_2mb,    nes_vt, nes_vt_hum_state, empty_init, "Hummer Technology Co., Ltd.", "Samuri (60 in 1)", MACHINE_IMPERFECT_GRAPHICS  | MACHINE_IMPERFECT_SOUND )
CONS( 200?, zdog,      0,  0,  nes_vt_hummer_4mb,    nes_vt, nes_vt_hum_state, empty_init, "Hummer Technology Co., Ltd.", "ZDog (44 in 1)", MACHINE_IMPERFECT_GRAPHICS  | MACHINE_IMPERFECT_SOUND )

// very plain menus
CONS( 200?, pjoyn50,    0,        0,  nes_vt_4mb,    nes_vt, nes_vt_state, empty_init, "<unknown>", "PowerJoy Navigator 50 in 1", MACHINE_IMPERFECT_GRAPHICS )
CONS( 200?, pjoys30,    0,        0,  nes_vt_pjoy_4mb,    nes_vt, nes_vt_pjoy_state, empty_init, "<unknown>", "PowerJoy Supermax 30 in 1", MACHINE_IMPERFECT_GRAPHICS )
CONS( 200?, pjoys60,    0,        0,  nes_vt_pjoy_4mb,    nes_vt, nes_vt_pjoy_state, empty_init, "<unknown>", "PowerJoy Supermax 60 in 1", MACHINE_IMPERFECT_GRAPHICS )

// doesn't boot, bad dump
CONS( 201?, cbrs8,      0,        0,  nes_vt_16mb,    nes_vt, nes_vt_state, empty_init, "CoolBoy", "CoolBoy RS-8 168 in 1", MACHINE_NOT_WORKING )

// Notes about the DDR games:
// * Missing PCM sounds (unsupported in NES VT APU code right now)
// * Console has stereo output (dual RCA connectors).
CONS( 2006, ddrdismx,   0,        0,  nes_vt_2mb, nes_vt_ddr, nes_vt_state, empty_init, "Majesco (licensed from Konami, Disney)", "Dance Dance Revolution Disney Mix",           MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // shows (c)2001 Disney onscreen, but that's recycled art from the Playstation release, actual release was 2006
CONS( 2006, ddrstraw,   0,        0,  nes_vt_2mb, nes_vt_ddr, nes_vt_state, empty_init, "Majesco (licensed from Konami)",         "Dance Dance Revolution Strawberry Shortcake", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// there is also a 'Spectra Light Edition' which could be a different ROM as the title screen on this one does show the unit type.
CONS( 2006, dbdancem,   0,        0,  nes_vt_2mb, dbdancem, nes_vt_state, empty_init, "Senario", "Double Dance Mania - Techno Light Edition",           MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// Unlike other Senario products this one is mostly just NES games, it also appears to be one of the final Senario products.
// Some games aren't what they claim to be, Warpman is Soccer for example.
// Senario had another 101 unit with different games (not JungleTac or NES bootlegs) it might be the same as the dreamGEAR 101 below
CONS( 2009, sen101,   0,        0,  nes_vt_4mb,    nes_vt, nes_vt_state, empty_init, "Senario", "101 Games in 1 (Senario, NES/Famicom bootlegs)", MACHINE_IMPERFECT_GRAPHICS )

// possibly designed by wellminds (M350 etc.)
CONS( 2010, hs36red,  0,        0,  nes_vt_waixing_2mb, nes_vt, nes_vt_waixing_state, init_hs36in1, "HengSheng", "HengSheng 36-in-1 (Red pad)", MACHINE_NOT_WORKING )
CONS( 2010, hs36blk,  0,        0,  nes_vt_waixing_2mb, nes_vt, nes_vt_waixing_state, init_hs36in1, "HengSheng", "HengSheng 36-in-1 (Black pad)", MACHINE_NOT_WORKING )

// unsorted, these were all in nes.xml listed as ONE BUS systems
CONS( 200?, mc_dg101,   0,        0,  nes_vt_4mb,    nes_vt, nes_vt_state, empty_init, "dreamGEAR", "dreamGEAR 101 in 1", MACHINE_IMPERFECT_GRAPHICS ) // dreamGear, but no enhanced games?
CONS( 200?, mc_aa2,     0,        0,  nes_vt_4mb,    nes_vt, nes_vt_state, empty_init, "<unknown>", "100 in 1 Arcade Action II (AT-103)", MACHINE_IMPERFECT_GRAPHICS )
CONS( 200?, mc_105te,   0,        0,  nes_vt_8mb,    nes_vt, nes_vt_state, empty_init, "<unknown>", "2011 Super HiK 105 in 1 Turbo Edition", MACHINE_NOT_WORKING )
CONS( 200?, mc_8x6cb,   0,        0,  nes_vt_4mb,    nes_vt, nes_vt_state, empty_init, "CoolBoy",   "888888 in 1 (Coolboy AEF-390)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 201?, fccomp88,   0,        0,  nes_vt_4mb,    nes_vt, nes_vt_state, empty_init, "Columbus Circle", "FC Compact 88-in-1 (CC-SFFC-WT) (Japan)", MACHINE_IMPERFECT_GRAPHICS )

// Works fine, uses ony VT02 features
CONS( 201?, mc_tv200,   0,        0,  nes_vt_8mb,    nes_vt, nes_vt_state, empty_init, "Thumbs Up", "200 in 1 Retro TV Game", MACHINE_IMPERFECT_GRAPHICS )

// TODO: add cart port and hook up nes_vt_cart.xml
CONS( 201?, 88in1joy,   0,        0,  nes_vt_4mb,    nes_vt, nes_vt_state, empty_init, "Play Vision", "Joystick88", MACHINE_IMPERFECT_GRAPHICS )


// Runs fine, non-sport 121 in 1 games perfect, but minor graphical issues in
// sport games, also no sound in menu or sport games due to missing PCM
// emulation
CONS( 200?, dgun2500,  0,  0,  nes_vt_16mb, nes_vt, nes_vt_state, empty_init, "dreamGEAR", "dreamGEAR Wireless Motion Control with 130 games (DGUN-2500)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND)

// available in a number of colours, with various brands, but likely all the same.
// This was a red coloured pad, contains various unlicensed bootleg reskinned NES game eg Blob Buster is a hack of Dig Dug 2 and there are also hacks of Xevious, Donkey Kong Jr, Donkey Kong 3 and many others.
// Also available in handheld form where Supreme 200 is also shown on the main menu background (see vt369 driver)
// unclear if this is VT03 or VT09, the boot logo needs either VT09 or PAL mode for the DMA to be correct, dump is from a PAL unit
CONS( 201?, ppgc200g,   0,         0,  nes_vt_pal_8mb, nes_vt, nes_vt_state, empty_init, "Fizz Creations", "Plug & Play Game Controller with 200 Games (Supreme 200)", MACHINE_IMPERFECT_GRAPHICS )

// unknown tech level, it's most likely a vt09 or vt369 but isn't using any of the extended features
CONS( 201?, dgun2869,  0,         0,  nes_vt_16mb,     nes_vt, nes_vt_state, empty_init, "dreamGEAR", "My Arcade Retro Micro Controller - 220 Built-In Video Games (DGUN-2869)",  MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 201?, dgun2959,  0,         0,  nes_vt_pal_16mb, nes_vt, nes_vt_state, empty_init, "dreamGEAR", "My Arcade Plug And Play 220 Game Retro Controller (DGUN-2959)",  MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// This is a hack of Ide Yosuke Meijin no Jissen Mahjong for the Famicom (unless it was licensed by the original developer)
CONS( 200?, tvmjfc,    0,        0,  nes_vt_2mb,    tvmjfc, nes_vt_tvmjfc_state, empty_init, "bootleg?", "TV Mahjong Game (VTxx hardware)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )



nes_vt09.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/***************************************************************************

  nes_vt09.cpp

  VT09 and higher go in here

  - 4KB RAM
  - Optional alt 4bpp tile mode
  - DMA acts the same in both NTSC and PAL modes

  NON-bugs (same happens on real hardware)

  msisinv: Taito screen has bad palette, it's encoded as VT03 but hardware is VT09
  msimm2: Corruption on Metal Man boss

***************************************************************************/

#include "emu.h"
#include "nes_vt09_soc.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "softlist_dev.h"


namespace {

class nes_vt09_common_base_state : public driver_device
{
public:
	nes_vt09_common_base_state(const machine_config& mconfig, device_type type, const char* tag) :
		driver_device(mconfig, type, tag),
		m_io0(*this, "IO0"),
		m_io1(*this, "IO1"),
		m_exin(*this, "EXTRAIN%u", 0U),
		m_prgrom(*this, "mainrom")
	{ }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual uint8_t in0_r();
	virtual uint8_t in1_r();
	virtual void in0_w(uint8_t data);

	void nes_vt09_map(address_map &map) ATTR_COLD;

	optional_ioport m_io0;
	optional_ioport m_io1;

	uint8_t m_latch0;
	uint8_t m_latch1;
	uint8_t m_previous_port0;

	optional_ioport_array<4> m_exin;

	required_region_ptr<uint8_t> m_prgrom;

	uint8_t vt_rom_r(offs_t offset);
	[[maybe_unused]] void vtspace_w(offs_t offset, uint8_t data);

	void configure_soc(nes_vt02_vt03_soc_device* soc);

	uint8_t upper_412c_r();
	uint8_t upper_412d_r();
	void upper_412c_w(uint8_t data);

private:
	/* Extra IO */
	template <uint8_t NUM> uint8_t extrain_r();
};

class nes_vt09_common_state : public nes_vt09_common_base_state
{
public:
	nes_vt09_common_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt09_common_base_state(mconfig, type, tag),
		m_soc(*this, "soc")
	{ }

	void vt_external_space_map_32mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_16mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_8mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_4mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_2mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_1mbyte(address_map &map) ATTR_COLD;
	[[maybe_unused]] void vt_external_space_map_512kbyte(address_map &map) ATTR_COLD;


protected:
	required_device<nes_vt02_vt03_soc_device> m_soc;
};

class nes_vt09_state : public nes_vt09_common_state
{
public:
	nes_vt09_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt09_common_state(mconfig, type, tag)
	{ }

	void nes_vt09(machine_config& config);
	void nes_vt09_1mb(machine_config& config);
	void nes_vt09_2mb(machine_config& config);
	void nes_vt09_4mb(machine_config& config);
	void nes_vt09_4mb_rasterhack(machine_config& config);
	void nes_vt09_8mb(machine_config& config);
	void nes_vt09_16mb(machine_config& config);

private:
};

class nes_vt09_cart_state : public nes_vt09_state
{
public:
	nes_vt09_cart_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt09_state(mconfig, type, tag),
		m_bank(*this, "cartbank"),
		m_cart(*this, "cartslot"),
		m_cart_region(nullptr)
	{ }

	void nes_vt09_cart(machine_config& config);

protected:
	void machine_start() override ATTR_COLD;

private:
	void vt_external_space_map_cart(address_map &map) ATTR_COLD;

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	required_memory_bank m_bank;
	required_device<generic_slot_device> m_cart;
	memory_region *m_cart_region;
};

void nes_vt09_cart_state::machine_start()
{
	nes_vt09_state::machine_start();

	m_bank->configure_entries(0, 1, memregion("mainrom")->base(), 0x200000);
	m_bank->set_entry(0);

	// if there's a cart, override the standard banking
	if (m_cart && m_cart->exists())
	{
		m_cart_region = memregion(std::string(m_cart->tag()) + GENERIC_ROM_REGION_TAG);
		m_bank->configure_entries(0, 1, m_cart_region->base(), 0x200000);
		m_bank->set_entry(0);
	}
}

DEVICE_IMAGE_LOAD_MEMBER(nes_vt09_cart_state::cart_load)
{
	uint32_t size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}


uint8_t nes_vt09_common_base_state::vt_rom_r(offs_t offset)
{
	return m_prgrom[offset];
}

void nes_vt09_common_base_state::vtspace_w(offs_t offset, uint8_t data)
{
	logerror("%s: vtspace_w %08x : %02x", machine().describe_context(), offset, data);
}

// VTxx can address 25-bit address space (32MB of ROM) so use maps with mirroring in depending on ROM size
void nes_vt09_common_state::vt_external_space_map_32mbyte(address_map &map)
{
	map(0x0000000, 0x1ffffff).r(FUNC(nes_vt09_common_state::vt_rom_r));
}

void nes_vt09_common_state::vt_external_space_map_16mbyte(address_map &map)
{
	map(0x0000000, 0x0ffffff).mirror(0x1000000).r(FUNC(nes_vt09_common_state::vt_rom_r));
}

void nes_vt09_common_state::vt_external_space_map_8mbyte(address_map &map)
{
	map(0x0000000, 0x07fffff).mirror(0x1800000).r(FUNC(nes_vt09_common_state::vt_rom_r));
}

void nes_vt09_common_state::vt_external_space_map_4mbyte(address_map &map)
{
	map(0x0000000, 0x03fffff).mirror(0x1c00000).r(FUNC(nes_vt09_common_state::vt_rom_r));
}

void nes_vt09_common_state::vt_external_space_map_2mbyte(address_map &map)
{
	map(0x0000000, 0x01fffff).mirror(0x1e00000).r(FUNC(nes_vt09_common_state::vt_rom_r));
}

void nes_vt09_common_state::vt_external_space_map_1mbyte(address_map &map)
{
	map(0x0000000, 0x00fffff).mirror(0x1f00000).r(FUNC(nes_vt09_common_state::vt_rom_r));
}

void nes_vt09_common_state::vt_external_space_map_512kbyte(address_map &map)
{
	map(0x0000000, 0x007ffff).mirror(0x1f80000).r(FUNC(nes_vt09_common_state::vt_rom_r));
}

void nes_vt09_cart_state::vt_external_space_map_cart(address_map &map)
{
	map(0x0000000, 0x01fffff).mirror(0x1e00000).bankr("cartbank");
}


template <uint8_t NUM> uint8_t nes_vt09_common_base_state::extrain_r()
{
	if (m_exin[NUM])
		return m_exin[NUM]->read();
	else
	{
		logerror("%s: extrain_r (port %d) (not hooked up)\n", NUM, machine().describe_context());
	}
	return 0x00;
}


/* Standard I/O handlers (NES Controller clone) */

uint8_t nes_vt09_common_base_state::in0_r()
{
	//logerror("%s: in0_r\n", machine().describe_context());
	uint8_t ret = 0x40;
	ret |= m_latch0 & 1;
	m_latch0 >>= 1;
	return ret;
}

uint8_t nes_vt09_common_base_state::in1_r()
{
	//logerror("%s: in1_r\n", machine().describe_context());
	uint8_t ret = 0x40;
	ret |= m_latch1 & 1;
	m_latch1 >>= 1;
	return ret;
}

void nes_vt09_common_base_state::in0_w(uint8_t data)
{
	//logerror("%s: in0_w %02x\n", machine().describe_context(), data);
	if ((data & 0x01) != (m_previous_port0 & 0x01))
	{
		if (data & 0x01)
		{
			m_latch0 = m_io0->read();
			m_latch1 = m_io1->read();
		}
	}

	m_previous_port0 = data;
}


void nes_vt09_common_base_state::machine_start()
{
	m_latch0 = 0;
	m_latch1 = 0;
	m_previous_port0 = 0;

	save_item(NAME(m_latch0));
	save_item(NAME(m_latch1));
	save_item(NAME(m_previous_port0));
}

void nes_vt09_common_base_state::machine_reset()
{
}

void nes_vt09_common_base_state::configure_soc(nes_vt02_vt03_soc_device* soc)
{
	soc->set_addrmap(AS_PROGRAM, &nes_vt09_common_state::vt_external_space_map_32mbyte);
	soc->read_0_callback().set(FUNC(nes_vt09_common_base_state::in0_r));
	soc->read_1_callback().set(FUNC(nes_vt09_common_base_state::in1_r));
	soc->write_0_callback().set(FUNC(nes_vt09_common_base_state::in0_w));

	soc->extra_read_0_callback().set(FUNC(nes_vt09_common_base_state::extrain_r<0>));
	soc->extra_read_1_callback().set(FUNC(nes_vt09_common_base_state::extrain_r<1>));
	soc->extra_read_2_callback().set(FUNC(nes_vt09_common_base_state::extrain_r<2>));
	soc->extra_read_3_callback().set(FUNC(nes_vt09_common_base_state::extrain_r<3>));
}


uint8_t nes_vt09_common_base_state::upper_412c_r()
{
	logerror("%s: upper_412c_r\n", machine().describe_context());
	return 0x00;
}

uint8_t nes_vt09_common_base_state::upper_412d_r()
{
	logerror("%s: upper_412d_r\n", machine().describe_context());
	return 0x00;
}

void nes_vt09_common_base_state::upper_412c_w(uint8_t data)
{
	logerror("%s: upper_412c_w %02x\n", machine().describe_context(), data);
}



void nes_vt09_state::nes_vt09(machine_config &config)
{
	/* basic machine hardware */
	NES_VT09_SOC(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);

	dynamic_cast<nes_vt09_soc_device&>(*m_soc).upper_read_412c_callback().set(FUNC(nes_vt09_state::upper_412c_r));
	dynamic_cast<nes_vt09_soc_device&>(*m_soc).upper_read_412d_callback().set(FUNC(nes_vt09_state::upper_412d_r));
	dynamic_cast<nes_vt09_soc_device&>(*m_soc).upper_write_412c_callback().set(FUNC(nes_vt09_state::upper_412c_w));

	m_soc->force_bad_dma();
}

void nes_vt09_state::nes_vt09_16mb(machine_config& config)
{
	nes_vt09(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt09_state::vt_external_space_map_16mbyte);
}

void nes_vt09_state::nes_vt09_8mb(machine_config& config)
{
	nes_vt09(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt09_state::vt_external_space_map_8mbyte);
}

void nes_vt09_state::nes_vt09_1mb(machine_config& config)
{
	nes_vt09(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt09_state::vt_external_space_map_1mbyte);
}

void nes_vt09_state::nes_vt09_2mb(machine_config& config)
{
	nes_vt09(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt09_state::vt_external_space_map_2mbyte);
}

void nes_vt09_state::nes_vt09_4mb(machine_config& config)
{
	nes_vt09(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt09_state::vt_external_space_map_4mbyte);
}

void nes_vt09_state::nes_vt09_4mb_rasterhack(machine_config& config)
{
	nes_vt09_4mb(config);
	m_soc->force_raster_timing_hack();
}

void nes_vt09_cart_state::nes_vt09_cart(machine_config& config)
{
	nes_vt09(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt09_cart_state::vt_external_space_map_cart);

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "nes_vt_cart");
	m_cart->set_width(GENERIC_ROM8_WIDTH);
	m_cart->set_device_load(FUNC(nes_vt09_cart_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("nes_vt_cart");
}

static INPUT_PORTS_START( nes_vt09 )
	PORT_START("IO0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_8WAY

	PORT_START("IO1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_8WAY
INPUT_PORTS_END

static INPUT_PORTS_START( nes_vt09_msi )
	PORT_START("IO0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("B")
	//PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1) // doesn't exist?
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_8WAY

	PORT_START("IO1")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( nes_vt09_msi_mm2 )
	PORT_START("IO0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("B")
	//PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1) // doesn't exist?
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_8WAY

	PORT_START("IO1")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


ROM_START( msiwwe )
	ROM_REGION( 0x200000, "mainrom", 0 ) // the first half of this ROM is unused code from the Ms. Pac-Man game!
	ROM_LOAD( "wrestlemania_es29lv160fb_004a2249.bin", 0x00000, 0x200000, CRC(f524382d) SHA1(0c8d1c29c76e3e3c58018354f1eca9445c9ab945) )
ROM_END

ROM_START( msiwwea )
	ROM_REGION( 0x100000, "mainrom", 0 )
	ROM_LOAD( "wrestlemania_en29lv800bb_007f225b.bin", 0x00000, 0x100000, CRC(52102de3) SHA1(f858ad18e05d3de24dfab4c98798efb4d30e2987) )
ROM_END

ROM_START( msidd )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "doubledragon_m29w160eb_00202249.bin", 0x00000, 0x200000, CRC(44df5bb6) SHA1(a984aa1644d2d313d4263afdfed1cd64009f1137) )
ROM_END

ROM_START( msimm2 )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "megaman2_s99jl032hbt1_001227e_readas_s29jl032h.bin", 0x00000, 0x400000, CRC(f537a053) SHA1(bd9353df34c0c0ee7d0e5e9808fc36f1a5eecc22) )
ROM_END

ROM_START( msinamco )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "msinamco3in1.bin", 0x00000, 0x200000, CRC(c69ad54a) SHA1(f12b9274d827e8a8a8f1bf2646fa426d9f8e6ece) )
ROM_END

ROM_START( msimpac )
	ROM_REGION( 0x100000, "mainrom", 0 )
	ROM_LOAD( "mspacman_29dl800ba_000422cb.bin", 0x00000, 0x100000, CRC(c66300e3) SHA1(3fc0bdfbf449d884151f1b581e848243cd2df3a5) )
ROM_END

ROM_START( msisinv )
	ROM_REGION( 0x100000, "mainrom", 0 )
	ROM_LOAD( "spaceinvaders_en29lv800bb_007f225b.bin", 0x00000, 0x100000, CRC(e444d129) SHA1(33742bc3a6250337cc42b73812e797023818282a) )
ROM_END

ROM_START( msifrog )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "frogger_39vf3201_00bf235b.bin", 0x00000, 0x400000, CRC(c46c29c0) SHA1(b8f26445c2086b97db8ee98bf36dff9d63ca414b) )
ROM_END

ROM_START( cybar120 )
	ROM_REGION( 0x1000000, "mainrom", 0 )
	ROM_LOAD( "m2500p-vt09-epson,20091222ver05,_30r-sx1067-01_pcb,_12r0cob128m_12001-3d05_fw.bin", 0x00000, 0x1000000, CRC(f7138980) SHA1(de31264ee3a5a5c77a86733b2e2d6845fee91ea5) )
ROM_END

ROM_START( vsmaxtx2 )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "tx2.bin", 0x00000, 0x400000, CRC(eddf0ca8) SHA1(b87c5c3e945d1efdcb953325425d4ddb0fded00a) )
ROM_END

ROM_START( senario25 )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "senario25.bin", 0x00000, 0x200000, CRC(270c4517) SHA1(c099096d1c86f55f2b0826484cd3d3f68c90c794) )
ROM_END

ROM_START( dturbogt )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "dgturbogt.bin", 0x00000, 0x800000, CRC(9532fb78) SHA1(cd188672f9b8e9c12069612ad0d0b70d3dd6c1b1) )
ROM_END

ROM_START( rcapnp )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "rcapnp_mx29lv160ab_00c22249.bin", 0x00000, 0x200000, CRC(8cc30a47) SHA1(815bfc26360b01ed3fa077016222939d2184408c) )
ROM_END

ROM_START( ventur25 )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "25games_m5m29gt320vp_001c0020.bin", 0x00000, 0x400000, CRC(3f78a45a) SHA1(3e97333c13e09c580e66518dd2e1e031371d399c) )
ROM_END

ROM_START( vgtablet )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "vgtablet.bin", 0x00000, 0x400000, CRC(99ef3978) SHA1(0074445708d66a04ab02b4993069ce1ae0514c2f) )
ROM_END

ROM_START( vgpocket )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "vgpocket.bin", 0x00000, 0x400000, CRC(843634c6) SHA1(c59dab0e43d364f59eb3a138abb585bc54e5d674) )
	// there was a dump of a 'secure' area with this, but it was just the top 0x10000 bytes of the existing rom.
ROM_END

ROM_START( vgpmini )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "vgpmini.bin", 0x00000, 0x400000, CRC(a1121843) SHA1(c96013ae6cf2f8173e65a167d45685cb61536d36) )
	// there was a dump of a 'secure' area with this, but it was just the bottom 0x10000 bytes of the existing rom.
ROM_END

ROM_START( joypad65 )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "joypad65.bin", 0x00000, 0x800000, CRC(b7f81c5f) SHA1(8579d9bc866415e0049979b7c3427d8dd0a60813) )
ROM_END

ROM_START( timetp25 )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "s29al016d70tfi02.u2", 0x00000, 0x200000, CRC(6109816a) SHA1(e48699d48b72219d80b8d27b1337e8d09793f4da) )
	ROM_FILL(0x1fce36, 0x01, 0x04 | 0x40) // the code doesn't set the 'alt 4bpp' mode bit, but needs it? why? it isn't hardcoded as the system takes cartridges which don't want it
ROM_END

ROM_START( wfmotor )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "motorcycle.bin", 0x00000, 0x400000, CRC(978f12f0) SHA1(a0230cfe4398d3971d487ff5d4b7107341799424) )
ROM_END

ROM_START( gujtv108 )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "29lv320.u1", 0x00000, 0x400000, CRC(56df0a09) SHA1(03aa6ad71ab283c99608a6dfa55c96148841bd10) )
ROM_END

ROM_START( mc_dcat8 )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "100-in-1, d-cat8 8bit console, v5.01.11-frd, bl 20041217.prg", 0x00000, 0x800000, CRC(97d20611) SHA1(d49796e66d7b1dff0ee2781cb0e48b777969d83f) )
ROM_END

ROM_START( mc_dcat8a )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "s29gl064.u6", 0x00000, 0x800000, CRC(e28b1ef8) SHA1(4a6f107d2189cbe1bb0b86b3738d0af58e24e0f7) )
ROM_END

ROM_START( pumpactv )
	ROM_REGION( 0x100000, "mainrom", 0 )
	ROM_LOAD( "pumpactive.bin", 0x00000, 0x100000, CRC(e3c07561) SHA1(2bfff426d72d481ba0647e9110f942d142a4625f) )
ROM_END

ROM_START( techni4 )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "technigame.bin", 0x00000, 0x200000, CRC(3c96b1b1) SHA1(1acc81b26e740327bd6d9faa5a96ab027a48dd77) )
ROM_END

} // anonymous namespace


// MSI Entertainment games (MSI previously operated as Majesco Entertainment)

// There are meant to be multiple revisions of this software, some with theme tunes for the new wrestlers, some without. This one appears to lack them.
// 2 box variations exist, one with Randy Savage in purple attire and another with green, this was dumped from a unit with purple on the box.
CONS( 2017, msiwwe,     0,      0,  nes_vt09_2mb, nes_vt09_msi, nes_vt09_state, empty_init, "MSI", "WWE Wrestlemania Steel Cage Challenge (Plug & Play) (set 1)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
// this one was dumped from the version with Randy Savage in green, the box was much larger than the other one.  This one also has new theme music for the adjusted roster.
CONS( 2017, msiwwea,    msiwwe, 0,  nes_vt09_1mb, nes_vt09_msi, nes_vt09_state, empty_init, "MSI", "WWE Wrestlemania Steel Cage Challenge (Plug & Play) (set 2)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

CONS( 2017, msidd,      0,  0,  nes_vt09_2mb, nes_vt09_msi, nes_vt09_state, empty_init, "MSI / Arc System Works", "Double Dragon - 30 Years Anniversary (Plug & Play)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

CONS( 2016, msimpac,    0,  0,  nes_vt09_1mb, nes_vt09_msi, nes_vt09_state, empty_init, "MSI / Bandai Namco", "Ms. Pac-Man (MSI Plug & Play)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

CONS( 2017, msimm2,     0,  0,  nes_vt09_4mb, nes_vt09_msi_mm2, nes_vt09_state, empty_init, "MSI / Capcom", "Mega Man 2 (MSI Plug & Play)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // various issues (glitched Metal Man stage boss, missing 'ready' text) happen on real unit

CONS( 2016, msisinv,    0,  0,  nes_vt09_1mb, nes_vt09_msi, nes_vt09_state, empty_init, "MSI / Taito", "Space Invaders (MSI Plug & Play)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// This is from the version with the same case type as the above MSI units.
// MSI also issued a version in the original Majesco shell but with the updated case logos and boot logos in the software, the software on that revision might match this one.
CONS( 2016, msifrog,    0,  0,  nes_vt09_4mb_rasterhack, nes_vt09_msi, nes_vt09_state, empty_init, "MSI / Konami", "Frogger (MSI Plug & Play, white joystick)",  MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) //  raster timing for need a hack

CONS( 2018, msinamco,   0,  0,  nes_vt09_1mb, nes_vt09_msi, nes_vt09_state, empty_init, "MSI / Bandai Namco", "Namco Classics Vol.1 (3-in-1) (MSI Plug & Play)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// MSI Midway (Joust+Gauntlet 2 + Defender 2) has 2x Globs, rather than Glob + Flash ROM

// this is VT09 based
CONS( 2009, cybar120,  0,  0,  nes_vt09_16mb,nes_vt09, nes_vt09_state, empty_init, "Defender / JungleTac",                      "Defender M2500P 120-in-1", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 200?, vsmaxtx2,  0,  0,  nes_vt09_4mb, nes_vt09, nes_vt09_state, empty_init, "Senario / JungleTac",                       "Vs Maxx TX-2 50-in-1", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 200?, senario25, 0,  0,  nes_vt09_2mb, nes_vt09, nes_vt09_state, empty_init, "Senario / JungleTac",                       "25 Video Games - All in 1 Video System (Senario)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // no Vs Maxx branding, newer style packaging
CONS( 200?, rcapnp,    0,  0,  nes_vt09_2mb, nes_vt09, nes_vt09_state, empty_init, "RCA / JungleTac",                           "RCA NS-500 30-in-1", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 200?, dturbogt,  0,  0,  nes_vt09_8mb, nes_vt09, nes_vt09_state, empty_init, "dreamGEAR / JungleTac",                     "Turbo GT 50-in-1", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 200?, ventur25,  0,  0,  nes_vt09_4mb, nes_vt09, nes_vt09_state, empty_init, "<unknown> / JungleTac",                     "Venturer '25 Games' 25-in-1", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 200?, joypad65,  0,  0,  nes_vt09_8mb, nes_vt09, nes_vt09_state, empty_init, "WinFun / JungleTac",                        "Joypad 65", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 200?, wfmotor,   0,  0,  nes_vt09_4mb, nes_vt09, nes_vt09_state, empty_init, "WinFun / JungleTac",                        "Motorcycle 30-in-1", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 2005, vgpocket,  0,  0,  nes_vt09_4mb, nes_vt09, nes_vt09_state, empty_init, "Performance Designed Products / JungleTac", "VG Pocket (VG-2000)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 200?, vgpmini,   0,  0,  nes_vt09_4mb, nes_vt09, nes_vt09_state, empty_init, "Performance Designed Products / JungleTac", "VG Pocket Mini (VG-1500)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
// VG Pocket Max (VG-2500) (blue case, 75 games)
// VG Pocket Max (VG-3000) (white case, 75 games) (does the game selection differ, or only the case?)
CONS( 2006, vgtablet,  0, 0,  nes_vt09_4mb_rasterhack,  nes_vt09, nes_vt09_state, empty_init, "Performance Designed Products (licensed by Konami) / JungleTac", "VG Pocket Tablet (VG-4000)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // raster timing for Frogger needs a hack
// VG Pocket Caplet is SunPlus hardware instead, see spg2xx_lexibook.cpp

CONS( 200?, timetp25,   0,  0,  nes_vt09_cart, nes_vt09, nes_vt09_cart_state, empty_init, "Timetop", "Super Game 25-in-1 (GM-228)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

CONS( 200?, gujtv108,   0,  0,  nes_vt09_4mb, nes_vt09, nes_vt09_state, empty_init, "YSN", "GameU Joint TV Bank 108-in-1 (model AH9069)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

CONS( 2004, mc_dcat8,   0,        0,  nes_vt09_8mb, nes_vt09, nes_vt09_state, empty_init, "<unknown>", "100 in 1 (D-CAT8 8bit Console, set 1) (v5.01.11-frd, BL 20041217)", MACHINE_IMPERFECT_GRAPHICS )
CONS( 2004, mc_dcat8a,  mc_dcat8, 0,  nes_vt09_8mb, nes_vt09, nes_vt09_state, empty_init, "<unknown>", "100 in 1 (D-CAT8 8bit Console, set 2)", MACHINE_IMPERFECT_GRAPHICS )

// probably VT09 or similar, 'pump' control is mapped on extra IO address that I don't think is present on 02/03
CONS( 200?, pumpactv,  0,  0,  nes_vt09_1mb, nes_vt09, nes_vt09_state, empty_init, "Giggle", "TV Pump Active", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// die is marked as VH2009, like various sets in nes_vt02_vt03.cpp, but no scrambled opcodes here and also some VT09 register usage
CONS( 201?, techni4,   0,  0,  nes_vt09_2mb, nes_vt09, nes_vt09_state, empty_init, "Technigame", "Technigame Super 4-in-1 Sports (PAL)", MACHINE_IMPERFECT_GRAPHICS )



nes_vt32.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/***************************************************************************

  nes_vt32.cpp

  VT32

   - new 444 palette mode
   - backwards compatibility with mappers other than MMC3

 ***************************************************************************/

#include "emu.h"
#include "nes_vt32_soc.h"

namespace {

class nes_vt32_base_state : public driver_device
{
public:
	nes_vt32_base_state(const machine_config& mconfig, device_type type, const char* tag) :
		driver_device(mconfig, type, tag),
		m_io0(*this, "IO0"),
		m_io1(*this, "IO1"),
		m_cartsel(*this, "CARTSEL"),
		m_exin(*this, "EXTRAIN%u", 0U),
		m_prgrom(*this, "mainrom")
	{ }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual uint8_t in0_r();
	virtual uint8_t in1_r();
	virtual void in0_w(uint8_t data);

	optional_ioport m_io0;
	optional_ioport m_io1;

	uint8_t m_latch0;
	uint8_t m_latch1;
	uint8_t m_previous_port0;

	optional_ioport m_cartsel;
	optional_ioport_array<4> m_exin;

	/* Misc */
	uint32_t m_ahigh; // external banking bits

	required_region_ptr<uint8_t> m_prgrom;

	uint8_t vt_rom_r(offs_t offset);
	[[maybe_unused]] void vtspace_w(offs_t offset, uint8_t data);

	void configure_soc(nes_vt02_vt03_soc_device* soc);

	uint8_t upper_412c_r();
	uint8_t upper_412d_r();
	void upper_412c_w(uint8_t data);

private:
	/* Extra IO */
	template <uint8_t NUM> uint8_t extrain_r();
};

class nes_vt32_state : public nes_vt32_base_state
{
public:
	nes_vt32_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt32_base_state(mconfig, type, tag),
		m_soc(*this, "soc")
	{ }

	[[maybe_unused]] void vt_external_space_map_1mbyte(address_map &map) ATTR_COLD;
	[[maybe_unused]] void vt_external_space_map_2mbyte(address_map &map) ATTR_COLD;
	[[maybe_unused]] void vt_external_space_map_4mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_8mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_16mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_32mbyte(address_map &map) ATTR_COLD;

protected:
	required_device<nes_vt02_vt03_soc_device> m_soc;
};

class nes_vt32_unk_state : public nes_vt32_state
{
public:
	nes_vt32_unk_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt32_state(mconfig, type, tag)
	{ }

	void nes_vt32_fp(machine_config& config);
	void nes_vt32_8mb(machine_config& config);
	void nes_vt32_16mb(machine_config& config);
	void nes_vt32_32mb(machine_config& config);
	void nes_vt32_4x16mb(machine_config& config);

	void nes_vt32_pal_32mb(machine_config& config);

	void init_rfcp168();
	void init_g9_666();
	void init_hhgc319();

protected:
	uint8_t vt_rom_banked_r(offs_t offset);

private:
	void vt_external_space_map_fp_2x32mbyte(address_map &map) ATTR_COLD;

	uint8_t fcpocket_412d_r();
	void fcpocket_412c_w(uint8_t data);
};

uint8_t nes_vt32_base_state::vt_rom_r(offs_t offset)
{
	return m_prgrom[offset];
}

void nes_vt32_base_state::vtspace_w(offs_t offset, uint8_t data)
{
	logerror("%s: vtspace_w %08x : %02x", machine().describe_context(), offset, data);
}

// VTxx can address 25-bit address space (32MB of ROM) so use maps with mirroring in depending on ROM size
void nes_vt32_state::vt_external_space_map_1mbyte(address_map &map)
{
	map(0x0000000, 0x00fffff).mirror(0x1f00000).r(FUNC(nes_vt32_state::vt_rom_r));
}

void nes_vt32_state::vt_external_space_map_2mbyte(address_map &map)
{
	map(0x0000000, 0x01fffff).mirror(0x1e00000).r(FUNC(nes_vt32_state::vt_rom_r));
}

void nes_vt32_state::vt_external_space_map_4mbyte(address_map &map)
{
	map(0x0000000, 0x03fffff).mirror(0x1c00000).r(FUNC(nes_vt32_state::vt_rom_r));
}

void nes_vt32_state::vt_external_space_map_8mbyte(address_map &map)
{
	map(0x0000000, 0x07fffff).mirror(0x1800000).r(FUNC(nes_vt32_state::vt_rom_r));
}

void nes_vt32_state::vt_external_space_map_16mbyte(address_map &map)
{
	map(0x0000000, 0x0ffffff).mirror(0x1000000).r(FUNC(nes_vt32_state::vt_rom_r));
}

void nes_vt32_state::vt_external_space_map_32mbyte(address_map &map)
{
	map(0x0000000, 0x1ffffff).r(FUNC(nes_vt32_state::vt_rom_r));
}


uint8_t nes_vt32_unk_state::vt_rom_banked_r(offs_t offset)
{
	return m_prgrom[m_ahigh | offset];
}

void nes_vt32_unk_state::vt_external_space_map_fp_2x32mbyte(address_map &map)
{
	map(0x0000000, 0x1ffffff).r(FUNC(nes_vt32_unk_state::vt_rom_banked_r));
}


template <uint8_t NUM> uint8_t nes_vt32_base_state::extrain_r()
{
	if (m_exin[NUM])
		return m_exin[NUM]->read();
	else
	{
		logerror("%s: extrain_r (port %d) (not hooked up)\n", NUM, machine().describe_context());
	}
	return 0x00;
}


/* Standard I/O handlers (NES Controller clone) */

uint8_t nes_vt32_base_state::in0_r()
{
	//logerror("%s: in0_r\n", machine().describe_context());
	uint8_t ret = 0x40;
	ret |= m_latch0 & 1;
	m_latch0 >>= 1;
	return ret;
}

uint8_t nes_vt32_base_state::in1_r()
{
	//logerror("%s: in1_r\n", machine().describe_context());
	uint8_t ret = 0x40;
	ret |= m_latch1 & 1;
	m_latch1 >>= 1;
	return ret;
}

void nes_vt32_base_state::in0_w(uint8_t data)
{
	//logerror("%s: in0_w %02x\n", machine().describe_context(), data);
	if ((data & 0x01) != (m_previous_port0 & 0x01))
	{
		if (data & 0x01)
		{
			m_latch0 = m_io0->read();
			m_latch1 = m_io1->read();
		}
	}

	m_previous_port0 = data;
}


void nes_vt32_base_state::machine_start()
{
	m_latch0 = 0;
	m_latch1 = 0;
	m_previous_port0 = 0;

	m_ahigh = 0;

	save_item(NAME(m_latch0));
	save_item(NAME(m_latch1));
	save_item(NAME(m_previous_port0));

	save_item(NAME(m_ahigh));
}

void nes_vt32_base_state::machine_reset()
{

}

void nes_vt32_base_state::configure_soc(nes_vt02_vt03_soc_device* soc)
{
	soc->set_addrmap(AS_PROGRAM, &nes_vt32_state::vt_external_space_map_32mbyte);
	soc->read_0_callback().set(FUNC(nes_vt32_base_state::in0_r));
	soc->read_1_callback().set(FUNC(nes_vt32_base_state::in1_r));
	soc->write_0_callback().set(FUNC(nes_vt32_base_state::in0_w));

	soc->extra_read_0_callback().set(FUNC(nes_vt32_base_state::extrain_r<0>));
	soc->extra_read_1_callback().set(FUNC(nes_vt32_base_state::extrain_r<1>));
	soc->extra_read_2_callback().set(FUNC(nes_vt32_base_state::extrain_r<2>));
	soc->extra_read_3_callback().set(FUNC(nes_vt32_base_state::extrain_r<3>));
}

// TODO: these should be in the SoC devices - upper_412d_r gets read, compared against, and another register written based on the result (maybe detecting SoC type?)
uint8_t nes_vt32_base_state::upper_412c_r() { logerror("%s: nes_vt32_base_state:upper_412c_r\n", machine().describe_context()); return 0x00; }
uint8_t nes_vt32_base_state::upper_412d_r() { logerror("%s: nes_vt32_base_state:upper_412d_r\n", machine().describe_context()); return 0x00; }
void nes_vt32_base_state::upper_412c_w(uint8_t data) { logerror("%s: nes_vt32_base_state:upper_412c_w %02x\n", machine().describe_context(), data); }


static INPUT_PORTS_START( nes_vt32 )
	PORT_START("IO0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_8WAY

	PORT_START("IO1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_8WAY
INPUT_PORTS_END

uint8_t nes_vt32_unk_state::fcpocket_412d_r()
{
	if (m_cartsel)
		return m_cartsel->read();
	else
		return 0;
}

void nes_vt32_unk_state::fcpocket_412c_w(uint8_t data)
{
	// fcpocket
	logerror("%s: vtfp_412c_extbank_w %02x\n", machine().describe_context(), data);
	m_ahigh = (data & 0x01) ? (1 << 25) : 0x0;
}

void nes_vt32_unk_state::nes_vt32_fp(machine_config &config)
{
	/* basic machine hardware */
	NES_VT32_SOC(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);

	dynamic_cast<nes_vt09_soc_device&>(*m_soc).upper_read_412c_callback().set(FUNC(nes_vt32_unk_state::upper_412c_r));
	dynamic_cast<nes_vt09_soc_device&>(*m_soc).upper_read_412d_callback().set(FUNC(nes_vt32_unk_state::upper_412d_r));
	dynamic_cast<nes_vt09_soc_device&>(*m_soc).upper_write_412c_callback().set(FUNC(nes_vt32_unk_state::upper_412c_w));

	m_soc->set_default_palette_mode(PAL_MODE_NEW_RGB12);
	m_soc->force_bad_dma();
}

void nes_vt32_unk_state::nes_vt32_pal_32mb(machine_config& config)
{
	/* basic machine hardware */
	NES_VT32_SOC_PAL(config, m_soc, NTSC_APU_CLOCK); // TODO, proper clocks etc. for PAL
	configure_soc(m_soc);

	dynamic_cast<nes_vt09_soc_device&>(*m_soc).upper_read_412c_callback().set(FUNC(nes_vt32_unk_state::upper_412c_r));
	dynamic_cast<nes_vt09_soc_device&>(*m_soc).upper_read_412d_callback().set(FUNC(nes_vt32_unk_state::upper_412d_r));
	dynamic_cast<nes_vt09_soc_device&>(*m_soc).upper_write_412c_callback().set(FUNC(nes_vt32_unk_state::upper_412c_w));

	m_soc->set_default_palette_mode(PAL_MODE_NEW_RGB12);
	m_soc->force_bad_dma();

	m_soc->set_addrmap(AS_PROGRAM, &nes_vt32_unk_state::vt_external_space_map_32mbyte);
}

void nes_vt32_unk_state::nes_vt32_4x16mb(machine_config& config)
{
	nes_vt32_fp(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt32_unk_state::vt_external_space_map_fp_2x32mbyte);

	dynamic_cast<nes_vt09_soc_device&>(*m_soc).upper_write_412c_callback().set(FUNC(nes_vt32_unk_state::fcpocket_412c_w));
	dynamic_cast<nes_vt09_soc_device&>(*m_soc).upper_read_412d_callback().set(FUNC(nes_vt32_unk_state::fcpocket_412d_r));
}

void nes_vt32_unk_state::nes_vt32_8mb(machine_config& config)
{
	nes_vt32_fp(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt32_unk_state::vt_external_space_map_8mbyte);
}

void nes_vt32_unk_state::nes_vt32_16mb(machine_config& config)
{
	nes_vt32_fp(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt32_unk_state::vt_external_space_map_16mbyte);
}

void nes_vt32_unk_state::nes_vt32_32mb(machine_config& config)
{
	nes_vt32_fp(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt32_unk_state::vt_external_space_map_32mbyte);
}


static INPUT_PORTS_START( nes_vt32_fp )
	PORT_INCLUDE(nes_vt32)

	PORT_START("CARTSEL")
	PORT_DIPNAME( 0x06, 0x00, "Cartridge Select" ) PORT_CODE(KEYCODE_3) PORT_TOGGLE
	PORT_DIPSETTING(    0x00, "472-in-1" )
	PORT_DIPSETTING(    0x06, "128-in-1" )
INPUT_PORTS_END



ROM_START( dgun2573 ) // this one lacked a DreamGear logo but was otherwise physically identical, is it a clone product or did DreamGear drop the logo in favor of just using the 'My Arcade' brand?
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "myarcadegamerportable_s29gl256p10tfi01_0001227e.bin", 0x00000, 0x2000000, CRC(8f8c8da7) SHA1(76a18458922e39abe1982f05f184babb5e65acf2) )
ROM_END

ROM_START( dgun2573a )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "myarcadegamerportabledreamgear_s29gl256p10tfi01_0001227e.bin", 0x00000, 0x2000000, CRC(928c41ad) SHA1(c0119a13a47a5b784d0b834d1451973ff0b4a84f) )
ROM_END

ROM_START( rminitv )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "29gl256.bin", 0x00000, 0x2000000, CRC(cb4048d4) SHA1(9877ce5716d13f8498abfc1cbfaefa9426205d3e) )
ROM_END

ROM_START( dgunl3201 )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "s29gl256.u2", 0x00000, 0x2000000,  CRC(8174088a) SHA1(4854d83a5657f3043b9568b1356e54c7f8282491) )
ROM_END

ROM_START( dgunl3202 )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "dg308n1_s29gl256p90tfcr1_0001227e.bin", 0x00000, 0x2000000, CRC(489c806f) SHA1(979b2c00eec459646de5a658863aff0eaacc2402) )
	ROM_IGNORE(0x100)
ROM_END

ROM_START( myaass )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "s29gl256.u2", 0x00000, 0x2000000, CRC(71a3298d) SHA1(5a2441ae5a8bf3e5efe9f22843ad2b8ef2df0f40) )
ROM_END

ROM_START( myaasa )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "mx29gl256el.u2", 0x00000, 0x2000000, CRC(1882264c) SHA1(e594b5cea634fadc4aac217b6d651be72a3024c0) )
ROM_END

ROM_START( mymman )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "megaman_s29gl064n90tfi04_0001227e.bin", 0x00000, 0x800000, CRC(1954cc95) SHA1(be20d42d32d625ec7b3c5db983850763c0ceff73) )
	ROM_IGNORE(0x100)
ROM_END

ROM_START( fcpocket )
	ROM_REGION( 0x8000000, "mainrom", 0 )
	ROM_LOAD( "s29gl01gp.bin", 0x00000, 0x8000000, CRC(8703b18a) SHA1(07943443294e80ca93f83181c8bdbf950b87c52f) ) // 2nd half = 0x00 (so 64MByte of content)
ROM_END

ROM_START( matet300 )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "tetris.bin", 0x00000, 0x2000000, CRC(73cbd40a) SHA1(5996c97cebd6cec42a0ba1fba9517adf1af00098) )
ROM_END

ROM_START( matet220 )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "gamervtetris_s29gl128n10tfi01_0001227e.bin", 0x00000, 0x1000000, CRC(ac244e56) SHA1(89897f5f65f55a46bf0d6b5ca534ca31c79a0658) )
	ROM_IGNORE(0x100)
ROM_END

ROM_START( matet100 )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "picotetris_s29gl064n90tfi04_0001227e.bin", 0x00000, 0x800000, CRC(7d9296f2) SHA1(0db5883028d14783d0abff1f7672e59534b0e513) )
	ROM_IGNORE(0x100)
ROM_END

ROM_START( lxpcsp )
	ROM_REGION( 0x4000000, "mainrom", 0 )
	ROM_LOAD( "s29gl512n11tfi02.u2", 0x00000, 0x4000000, CRC(113e22f2) SHA1(c57184131db3f3c82d09d7757f0977223698f62c) )
ROM_END

ROM_START( lxpcli )
	ROM_REGION( 0x4000000, "mainrom", 0 )
	ROM_LOAD( "s29gl512n11tfi02.u2", 0x00000, 0x4000000, CRC(9df963c6) SHA1(e5cc7b48c31b761bb74b3e5e1563a16a0cefa272) )
ROM_END

ROM_START( lxpcpp )
	ROM_REGION( 0x4000000, "mainrom", 0 )
	ROM_LOAD( "s29gl512n11tfi02.u2", 0x00000, 0x4000000, CRC(75728b87) SHA1(4b6d8cfd19ea5160f5486a4bd72c8cc708716d2c) )
ROM_END

ROM_START( typo240 )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "240nes.u2", 0x00000, 0x1000000, CRC(d709f66c) SHA1(73ca34ce07a1a8782226bd74b1ae43fc6d7126e1) ) // s29gl128p90tfcr1
ROM_END

ROM_START( retror30 )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "s29gl032n90tfi03.u2", 0x00000, 0x400000, CRC(dfb89ef7) SHA1(401539b73521e018e3af70b8019e6b59ba67fcad) )
ROM_END

} // anonymous namespace


CONS( 2015, dgun2573,  0,         0,  nes_vt32_32mb,     nes_vt32, nes_vt32_unk_state, empty_init, "dreamGEAR", "My Arcade Gamer V Portable Gaming System (DGUN-2573) (set 1, newer)",  MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 2015, dgun2573a, dgun2573,  0,  nes_vt32_32mb,     nes_vt32, nes_vt32_unk_state, empty_init, "dreamGEAR", "My Arcade Gamer V Portable Gaming System (DGUN-2573) (set 2, older)",  MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // some menu graphics haven't been updated to reflect 'Panda' theme to the sports games

CONS( 2015, rminitv,   0,  0,  nes_vt32_pal_32mb, nes_vt32, nes_vt32_unk_state, empty_init, "Orb Gaming", "Retro 'Mini TV' Console 300-in-1", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // single 32Mbyte bank!

// This was available in at least 3 other form factors, some of those have been shown to use different menu backgrounds
// Gamestation Wireless : https://youtu.be/rlX-LGO-ewM Fish background
// Pixel Classic (DGUNL-3201) : https://youtu.be/XOUtT_wRXa4 Plane background, note, different revision, the copyright text on some games (eg. Heavy Barrel) hasn't been updated as it has on the dgunl3201 set here
// However, sometimes the different models use the same background as this one (confirmed on one Pixel Classic at least), so there doesn't appear to be a clear way of knowing without powering them on
CONS( 201?, dgunl3201, 0,  0,  nes_vt32_32mb, nes_vt32, nes_vt32_unk_state, empty_init, "dreamGEAR", "My Arcade Data East Classics - Pixel Classic (308-in-1) (DGUNL-3201)", MACHINE_NOT_WORKING ) // from a UK unit, single 32Mbyte bank!
CONS( 201?, dgunl3202, 0,  0,  nes_vt32_32mb, nes_vt32, nes_vt32_unk_state, empty_init, "dreamGEAR", "My Arcade Data East Classics - Pixel Player (308-in-1) (DGUNL-3202)", MACHINE_NOT_WORKING ) // from a US unit single 32Mbyte bank!
// There was also a 34-in-1 version of the Data East Classics in a mini-cabinet, NOT running on VT hardware, but using proper arcade ROMs, that one is reportedly running an old MAME build on an ARM SoC (although some sources say FBA)

// many of the games don't work or have scrambled graphics, it writes 0xc0 to vtfp_411e_encryption_state_w in such cases
CONS( 201?, myaass,    0,  0,  nes_vt32_32mb, nes_vt32, nes_vt32_unk_state, empty_init, "dreamGEAR", "My Arcade All Star Stadium - Pocket Player (307-in-1)", MACHINE_NOT_WORKING )
CONS( 201?, myaasa,    0,  0,  nes_vt32_32mb, nes_vt32, nes_vt32_unk_state, empty_init, "dreamGEAR", "My Arcade All Star Arena - Pocket Player (307-in-1)", MACHINE_NOT_WORKING )

CONS( 201?, mymman,    0,  0,  nes_vt32_8mb, nes_vt32, nes_vt32_unk_state, empty_init, "dreamGEAR", "My Arcade Mega Man (DGUNL-7011, Pico Player)", MACHINE_NOT_WORKING )

// most games work, a few minor graphical issues common to the same games in other units
CONS( 202?, typo240,   0,  0,  nes_vt32_16mb, nes_vt32, nes_vt32_unk_state, empty_init, "Typo", "Vintage Gamer 240-in-1", MACHINE_IMPERFECT_GRAPHICS )

// speed challenge doesn't work
CONS( 2021, retror30,  0,  0,  nes_vt32_32mb,      nes_vt32, nes_vt32_unk_state, empty_init, "Orb Gaming", "Retro Racer (30-in-1)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )

// Some games (eg F22) are scrambled like in myaass
// These use a 16x16x8bpp packed tile mode for the main menu which seems more like a VT3xx feature, but VT3xx extended video regs not written?
// also access 3e00 (not 3f00) for palette on said screens?
CONS( 2021, matet220,  0,         0,  nes_vt32_32mb,     nes_vt32, nes_vt32_unk_state, empty_init, "dreamGEAR", "My Arcade Tetris (DGUNL-7030, Gamer V, with 220 bonus games)", MACHINE_NOT_WORKING )
CONS( 2021, matet300,  0,         0,  nes_vt32_32mb,     nes_vt32, nes_vt32_unk_state, empty_init, "dreamGEAR", "My Arcade Tetris (DGUNL-7029, Go Gamer, with 300 bonus games)", MACHINE_NOT_WORKING )

// unknown tech level, uses vt32 style opcode scramble and palette, lots of unmapped accesses though
CONS( 2021, matet100,  0,        0,  nes_vt32_32mb,      nes_vt32, nes_vt32_unk_state, empty_init, "dreamGEAR", "My Arcade Tetris (DGUNL-7027, Pico Player, with 100+ bonus games)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS ) // box says 100+ bonus games

// Uses DIPs switch to select console or cartridge, as cartridge is fake and just toggles a GPIO
CONS( 2016, fcpocket,  0,  0,  nes_vt32_4x16mb,   nes_vt32_fp, nes_vt32_unk_state, empty_init, "<unknown>",   "FC Pocket 600 in 1", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )  // has external banking (2x 32mbyte banks)

// aside from the boot screens these have no theming and all contain a barely disguised bootleg version of Nintendo's Pinball in the Games section
CONS( 2020, lxpcsp,    0,  0,  nes_vt32_32mb, nes_vt32, nes_vt32_unk_state, empty_init,    "Lexibook", "Power Console - Marvel Spider-Man", MACHINE_NOT_WORKING )
CONS( 2020, lxpcli,    0,  0,  nes_vt32_32mb, nes_vt32, nes_vt32_unk_state, empty_init,    "Lexibook", "Power Console - Lilo & Stitch", MACHINE_NOT_WORKING )
CONS( 2020, lxpcpp,    0,  0,  nes_vt32_32mb, nes_vt32, nes_vt32_unk_state, empty_init,    "Lexibook", "Power Console - Paw Patrol", MACHINE_NOT_WORKING )
// Power Console - Gabby's Dollhouse
// Power Console - Disney Princess
// Power Console - Frozen
// Power Console - Generic EN/FR model
// Power Console - Generic EN/ES model
// Power Console - Generic EN/DE model



nes_vt369_vtunknown.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/***************************************************************************

  nes_vt369_vtunknown.cpp

  VT369 and unknown/higher

   - new screen modes
   - new registers for controlling banking
   - can run from SPI ROM
   - additional audio cpu (like VT1682) and multiplier on VT369 models

   (not all features are used all games, but anything that has an SPI ROM
    must at least be this tech level)

  TODO:
  this still needs significant cleanups before work is started on individual
  systems

  ***************************************************************************/

#include "emu.h"
#include "nes_vt369_vtunknown_soc.h"

#include "multibyte.h"

namespace {

class vt369_base_state : public driver_device
{
public:
	vt369_base_state(const machine_config& mconfig, device_type type, const char* tag) :
		driver_device(mconfig, type, tag),
		m_io0(*this, "IO0"),
		m_io1(*this, "IO1"),
		m_cartsel(*this, "CARTSEL"),
		m_exin(*this, "EXTRAIN%u", 0U),
		m_prgrom(*this, "mainrom")
	{ }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual u8 in0_r();
	virtual u8 in1_r();
	virtual void in0_w(u8 data);

	void vt369_map(address_map &map) ATTR_COLD;

	optional_ioport m_io0;
	optional_ioport m_io1;

	u8 m_latch0;
	u8 m_latch1;
	u8 m_previous_port0;

	optional_ioport m_cartsel;
	optional_ioport_array<4> m_exin;

	/* Misc */
	u32 m_ahigh; // external banking bits
	u8 m_4242;
	u8 m_411c;
	u8 m_411d;

	required_region_ptr<u8> m_prgrom;

	u8 vt_rom_r(offs_t offset);
	void configure_soc(nes_vt02_vt03_soc_device* soc);

	void extbank_w(u8 data);
	void extbank_red5mam_w(u8 data);

private:
	/* Extra IO */
	template <u8 NUM> u8 extrain_r();
};

class vt369_state : public vt369_base_state
{
public:
	vt369_state(const machine_config& mconfig, device_type type, const char* tag) :
		vt369_base_state(mconfig, type, tag),
		m_soc(*this, "soc")
	{ }

	void vt_external_space_map_32mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_32mbyte_bank(address_map &map) ATTR_COLD;
	void vt_external_space_map_16mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_8mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_4mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_2mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_1mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_512kbyte(address_map &map) ATTR_COLD;

	void init_lxcmcypp();
	void init_dgun2572();
	void init_s10fake();

protected:
	u8 vt_rom_banked_r(offs_t offset);

	required_device<nes_vt02_vt03_soc_device> m_soc;
};


class vt36x_state : public vt369_state
{
public:
	vt36x_state(const machine_config& mconfig, device_type type, const char* tag) :
		vt369_state(mconfig, type, tag)
	{ }

	void vt36x(machine_config& config);
	void vt36x_1mb(machine_config& config);
	void vt36x_4mb(machine_config& config);
	void vt36x_8mb(machine_config& config);
	void vt36x_16mb(machine_config& config);
	void vt36x_16mb_sdcard(machine_config& config);
	void vt36x_32mb(machine_config& config);
	void vt36x_32mb_2banks_lexi(machine_config& config);
	void vt36x_32mb_2banks_lexi300(machine_config& config);

	void vt36x_swap(machine_config& config);
	void vt36x_swap_2mb(machine_config& config);
	void vt36x_swap_4mb(machine_config& config);
	void vt36x_swap_8mb(machine_config& config);
	void vt36x_swap_16mb(machine_config& config);
	void vt36x_swap_512kb(machine_config& config);

	void vt36x_altswap(machine_config& config);
	void vt36x_altswap_2mb(machine_config& config);
	void vt36x_altswap_4mb(machine_config& config);
	void vt36x_altswap_16mb(machine_config& config);
	void vt36x_altswap_32mb_4banks_red5mam(machine_config& config);

	void vt36x_vibesswap_16mb(machine_config& config);
	void vt36x_gbox2020_16mb(machine_config& config);
	void vt36x_s10swap_8mb(machine_config& config);

	void vt369_unk(machine_config& config);
	void vt369_unk_1mb(machine_config& config);
	void vt369_unk_16mb(machine_config& config);
	void vt369_unk_32mb(machine_config& config);
};

class vt36x_tetrtin_state : public vt36x_state
{
public:
	vt36x_tetrtin_state(const machine_config& mconfig, device_type type, const char* tag) :
		vt36x_state(mconfig, type, tag)
	{ }

protected:
	virtual void machine_reset() override;

};

void vt36x_tetrtin_state::machine_reset()
{
	vt36x_state::machine_reset();
	// the game appears to require code/data from an additional device (not just the standard internal ROM)
	// there's an 8-pin chip on the PCB which is likely responsible

	// simulate what that code might be doing
	// copy VT369 internal ROM 0x0000 to 0x4ff4 in CPU space (copying boot vectors for sound CPU, as other games do in code)
	int src = 0;
	u8 *introm = memregion("soc:internal")->base();
	for (int i = 0x4ff4; i < 0x5000; i++)
	{
		m_soc->write_byte_to_cpu(i, introm[src++]);
	}
	u8* gamerom = memregion("mainrom")->base();

	int patchaddress;


	// tetrtin - jump over a whole lot of code, this is crude, there might be other code still in the startup we could be executing
	patchaddress = 0x7f675;
	if ((gamerom[patchaddress] == 0x20) && (gamerom[patchaddress+1] == 0xcb) && (gamerom[patchaddress+2] == 0xf5))
	{
		gamerom[patchaddress] = 0x4c;
		gamerom[patchaddress+1] = 0xcb;
		gamerom[patchaddress+2] = 0xf6;
	}
	// same for pactin
	patchaddress = 0x7f5a3;
	if ((gamerom[patchaddress] == 0x20) && (gamerom[patchaddress+1] == 0x04) && (gamerom[patchaddress+2] == 0xf5))
	{
		gamerom[patchaddress] = 0x4c;
		gamerom[patchaddress+1] = 0xf9;
		gamerom[patchaddress+2] = 0xf5;
	}
	// lxcap (will show menu, but accesses device again afterwards)
	patchaddress = 0x7ecd4;
	if ((gamerom[patchaddress] == 0x20) && (gamerom[patchaddress+1] == 0x96) && (gamerom[patchaddress+2] == 0xeb))
	{
		gamerom[patchaddress] = 0x4c;
		gamerom[patchaddress+1] = 0x2a;
		gamerom[patchaddress+2] = 0xed;
	}
}

u8 vt369_base_state::vt_rom_r(offs_t offset)
{
	return m_prgrom[offset];
}

u8 vt369_state::vt_rom_banked_r(offs_t offset)
{
	return m_prgrom[m_ahigh | offset];
}

// VTxx can address 25-bit address space (32MB of ROM) so use maps with mirroring in depending on ROM size
void vt369_state::vt_external_space_map_32mbyte(address_map &map)
{
	map(0x0000000, 0x1ffffff).r(FUNC(vt369_state::vt_rom_r));
}

void vt369_state::vt_external_space_map_32mbyte_bank(address_map &map)
{
	map(0x0000000, 0x1ffffff).r(FUNC(vt369_state::vt_rom_banked_r));
}

void vt369_state::vt_external_space_map_16mbyte(address_map &map)
{
	map(0x0000000, 0x0ffffff).mirror(0x1000000).r(FUNC(vt369_state::vt_rom_r));
}

void vt369_state::vt_external_space_map_8mbyte(address_map &map)
{
	map(0x0000000, 0x07fffff).mirror(0x1800000).r(FUNC(vt369_state::vt_rom_r));
}

void vt369_state::vt_external_space_map_4mbyte(address_map &map)
{
	map(0x0000000, 0x03fffff).mirror(0x1c00000).r(FUNC(vt369_state::vt_rom_r));
}

void vt369_state::vt_external_space_map_2mbyte(address_map &map)
{
	map(0x0000000, 0x01fffff).mirror(0x1e00000).r(FUNC(vt369_state::vt_rom_r));
}

void vt369_state::vt_external_space_map_1mbyte(address_map &map)
{
	map(0x0000000, 0x00fffff).mirror(0x1f00000).r(FUNC(vt369_state::vt_rom_r));
}

void vt369_state::vt_external_space_map_512kbyte(address_map &map)
{
	map(0x0000000, 0x007ffff).mirror(0x1f80000).r(FUNC(vt369_state::vt_rom_r));
}

template <u8 NUM> u8 vt369_base_state::extrain_r()
{
	if (m_exin[NUM])
		return m_exin[NUM]->read();
	else
	{
		logerror("%s: extrain_r (port %d) (not hooked up)\n", NUM, machine().describe_context());
	}
	return 0x00;
}


/* Standard I/O handlers (NES Controller clone) */

u8 vt369_base_state::in0_r()
{
	//logerror("%s: in0_r\n", machine().describe_context());
	u8 ret = 0x40;
	ret |= m_latch0 & 1;
	m_latch0 >>= 1;
	return ret;
}

u8 vt369_base_state::in1_r()
{
	//logerror("%s: in1_r\n", machine().describe_context());
	u8 ret = 0x40;
	ret |= m_latch1 & 1;
	m_latch1 >>= 1;
	return ret;
}

void vt369_base_state::in0_w(u8 data)
{
	//logerror("%s: in0_w %02x\n", machine().describe_context(), data);
	if ((data & 0x01) != (m_previous_port0 & 0x01))
	{
		if (data & 0x01)
		{
			m_latch0 = m_io0->read();
			m_latch1 = m_io1->read();
		}
	}

	m_previous_port0 = data;
}


void vt369_base_state::machine_start()
{
	m_latch0 = 0;
	m_latch1 = 0;
	m_previous_port0 = 0;

	m_4242 = 0;
	m_411c = 0;
	m_411d = 0;

	save_item(NAME(m_latch0));
	save_item(NAME(m_latch1));
	save_item(NAME(m_previous_port0));

	save_item(NAME(m_ahigh));
	save_item(NAME(m_4242));
	save_item(NAME(m_411c));
	save_item(NAME(m_411d));
}

void vt369_base_state::machine_reset()
{
	m_ahigh = 0;
}

void vt369_base_state::configure_soc(nes_vt02_vt03_soc_device* soc)
{
	soc->set_addrmap(AS_PROGRAM, &vt369_state::vt_external_space_map_32mbyte);
	soc->read_0_callback().set(FUNC(vt369_base_state::in0_r));
	soc->read_1_callback().set(FUNC(vt369_base_state::in1_r));
	soc->write_0_callback().set(FUNC(vt369_base_state::in0_w));

	soc->extra_read_0_callback().set(FUNC(vt369_base_state::extrain_r<0>));
	soc->extra_read_1_callback().set(FUNC(vt369_base_state::extrain_r<1>));
	soc->extra_read_2_callback().set(FUNC(vt369_base_state::extrain_r<2>));
	soc->extra_read_3_callback().set(FUNC(vt369_base_state::extrain_r<3>));
}




void vt36x_state::vt369_unk(machine_config &config)
{
	VT3XX_SOC_UNK_DG(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->force_bad_dma();
}

void vt36x_state::vt369_unk_16mb(machine_config& config)
{
	vt369_unk(config);
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_16mbyte);
}

void vt36x_state::vt369_unk_1mb(machine_config& config)
{
	vt369_unk(config);
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_1mbyte);
}

void vt36x_state::vt369_unk_32mb(machine_config& config)
{
	vt369_unk(config);
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_32mbyte);
}


// New mystery handheld architecture, VTxx derived
void vt36x_state::vt36x(machine_config &config)
{
	VT369_SOC_INTROM_NOSWAP(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);

	m_soc->set_default_palette_mode(PAL_MODE_NEW_RGB);
	m_soc->force_bad_dma();
}

void vt36x_state::vt36x_swap(machine_config &config)
{
	VT369_SOC_INTROM_SWAP(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_default_palette_mode(PAL_MODE_NEW_RGB);
	m_soc->force_bad_dma();
}

void vt36x_state::vt36x_swap_512kb(machine_config &config)
{
	vt36x_swap(config);
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_512kbyte);
}

void vt36x_state::vt36x_swap_2mb(machine_config &config)
{
	vt36x_swap(config);
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_2mbyte);
}

void vt36x_state::vt36x_swap_4mb(machine_config &config)
{
	vt36x_swap(config);
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_4mbyte);
}

void vt36x_state::vt36x_swap_8mb(machine_config &config)
{
	vt36x_swap(config);
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_8mbyte);
}

void vt36x_state::vt36x_swap_16mb(machine_config &config)
{
	vt36x_swap(config);
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_16mbyte);
}

void vt36x_state::vt36x_altswap(machine_config &config)
{
	VT369_SOC_INTROM_ALTSWAP(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_default_palette_mode(PAL_MODE_NEW_RGB);
	m_soc->force_bad_dma();
}

void vt36x_state::vt36x_altswap_2mb(machine_config &config)
{
	vt36x_altswap(config);
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_2mbyte);
}

void vt36x_state::vt36x_altswap_4mb(machine_config &config)
{
	vt36x_altswap(config);
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_4mbyte);
}

void vt36x_state::vt36x_altswap_16mb(machine_config& config)
{
	vt36x_altswap(config);
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_16mbyte);
}

void vt369_base_state::extbank_red5mam_w(u8 data)
{
//  printf("extbank_red5mam_w %02x\n", data);
	m_ahigh = ((data & 0x03) << 25);
}

void vt36x_state::vt36x_altswap_32mb_4banks_red5mam(machine_config& config)
{
	vt36x_altswap(config);
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_32mbyte_bank);
	m_soc->set_41e6_write_cb().set(FUNC(vt36x_state::extbank_red5mam_w));
}

void vt36x_state::vt36x_vibesswap_16mb(machine_config &config)
{
	vt36x_swap_16mb(config);

	VT369_SOC_INTROM_VIBESSWAP(config.replace(), m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
	//m_soc->set_default_palette_mode(PAL_MODE_NEW_RGB);
	m_soc->force_bad_dma();
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_16mbyte);
}

void vt36x_state::vt36x_gbox2020_16mb(machine_config &config)
{
	vt36x_swap_16mb(config);

	VT369_SOC_INTROM_GBOX2020(config.replace(), m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_default_palette_mode(PAL_MODE_NEW_RGB);
	m_soc->force_bad_dma();
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_16mbyte);
}

void vt36x_state::vt36x_s10swap_8mb(machine_config &config)
{
	vt36x_swap_8mb(config);

	VT369_SOC_INTROM_S10SWAP(config.replace(), m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);
	m_soc->set_default_palette_mode(PAL_MODE_NEW_RGB);
	m_soc->force_bad_dma();
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_8mbyte);
}


void vt36x_state::vt36x_1mb(machine_config& config)
{
	vt36x(config);
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_1mbyte);
}

void vt36x_state::vt36x_4mb(machine_config& config)
{
	vt36x(config);
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_4mbyte);
}

void vt36x_state::vt36x_8mb(machine_config& config)
{
	vt36x(config);
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_8mbyte);
}

void vt36x_state::vt36x_16mb(machine_config& config)
{
	vt36x(config);
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_16mbyte);
}

void vt36x_state::vt36x_16mb_sdcard(machine_config& config)
{
	vt36x(config);
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_16mbyte);
}

void vt36x_state::vt36x_32mb(machine_config& config)
{
	vt36x(config);
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_32mbyte);
}

void vt369_base_state::extbank_w(u8 data)
{
	m_ahigh = (data & 0x01) ? (1 << 25) : 0x0;
}

void vt36x_state::vt36x_32mb_2banks_lexi(machine_config& config)
{
	vt36x_32mb(config);
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_32mbyte_bank);
	m_soc->set_4150_write_cb().set(FUNC(vt36x_state::extbank_w));
}

void vt36x_state::vt36x_32mb_2banks_lexi300(machine_config& config)
{
	vt36x_32mb(config);
	m_soc->set_addrmap(AS_PROGRAM, &vt36x_state::vt_external_space_map_32mbyte_bank);
	m_soc->set_411e_write_cb().set(FUNC(vt36x_state::extbank_w)); // could be on 411d
}

static INPUT_PORTS_START( vt369 )
	PORT_START("IO0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_8WAY

	PORT_START("IO1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_8WAY
INPUT_PORTS_END

static INPUT_PORTS_START( vt369_rot )
	PORT_START("IO0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_8WAY

	PORT_START("IO1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2) PORT_8WAY
INPUT_PORTS_END

// internal ROMs - these seem to be generic, but that isn't yet verified, if they are move them to device
//
// maps at 0x1000-0x1fff on main CPU, and can boot using vectors in 1ffx area
// can also be mapped at 0x4000-0x4fff on the sound CPU, typically when this is
// done the main CPU fetch the vectors from 0x4000 and writes them to the RAM
// shared with the sound CPU vector area before enabling the sound CPU

#define VT3XX_INTERNAL_NO_SWAP \
	ROM_REGION( 0x1000, "soc:internal", 0 ) \
	ROM_LOAD( "internal.bin", 0x0000, 0x1000, CRC(da5850f0) SHA1(39d674d965818922aad5993e9499170d3ebc43bf) )

#define VT3XX_INTERNAL_OPCODE_SWAP \
	ROM_REGION( 0x1000, "soc:internal", 0 ) \
	ROM_LOAD( "internal.bin", 0x0000, 0x1000, CRC(57c9cea9) SHA1(4f338e5ef87a66601014ad726cfefefbc20dc4be) )

// below use Flash ROMs

ROM_START( dgun2593 )
	ROM_REGION( 0x8000000, "mainrom", 0 )
	ROM_LOAD( "dreamgear300.bin", 0x00000, 0x8000000, CRC(4fe0ed02) SHA1(a55590557bacca65ed9a17c5bcf0a4e5cb223126) )
ROM_END

ROM_START( 240in1ar )
	ROM_REGION( 0x8000000, "mainrom", 0 )
	ROM_LOAD( "mw-106-2g.u3", 0x00000, 0x8000000, CRC(c46d2ca9) SHA1(0fff7d3461ff620c5b5e43f54f9e7badd089b951) )
ROM_END


ROM_START( rtvgc300 )
	ROM_REGION( 0x8000000, "mainrom", 0 )
	// some of the higher address lines might be swapped
	ROM_LOAD( "lexibook300.bin", 0x00000, 0x0800000, CRC(015c4067) SHA1(a12986c4a366a23c4c7ca7b3d33e421a8dfdffc0) )
	ROM_CONTINUE(0x1000000, 0x0800000)
	ROM_CONTINUE(0x0800000, 0x0800000)
	ROM_CONTINUE(0x1800000, 0x0800000)
	ROM_CONTINUE(0x2000000, 0x0800000)
	ROM_CONTINUE(0x3000000, 0x0800000)
	ROM_CONTINUE(0x2800000, 0x0800000)
	ROM_CONTINUE(0x3800000, 0x0800000)

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( rtvgc300fz )
	ROM_REGION( 0x8000000, "mainrom", 0 )
	// some of the higher address lines might be swapped
	ROM_LOAD( "jg7800fz.bin", 0x00000, 0x4000000, CRC(c9d319d2) SHA1(9d0d1435b802f63ce11b94ce54d11f4065b324cc) )

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END


ROM_START( dgun2561 ) // all games selectable
	ROM_REGION( 0x4000000, "mainrom", 0 )
	ROM_LOAD( "dgun2561.bin", 0x00000, 0x4000000, CRC(a6e627b4) SHA1(2667d2feb02de349387f9dcfa5418e7ed3afeef6) )

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( lxccatv ) // all games selectable
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "120n1.bin", 0x00000, 0x2000000, CRC(6b9cf537) SHA1(44276c3ef928c76a3ecf404d2e531cd3ce5561af) )

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( lxcmcy ) // all games selectable
	ROM_REGION( 0x4000000, "mainrom", 0 )
	ROM_LOAD( "lxcmcy.bin", 0x00000, 0x4000000, CRC(3f3af72c) SHA1(76127054291568fcce1431d21af71f775cfb05a6) )

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( lxcmcysw ) // all games selectable
	ROM_REGION( 0x4000000, "mainrom", 0 )
	ROM_LOAD( "jl2365swr-1.u2", 0x0000000, 0x0800000, CRC(60ece391) SHA1(655de6b36ba596d873de2839522b948ccf45e006) )
	ROM_CONTINUE(0x1000000, 0x0800000)
	ROM_CONTINUE(0x0800000, 0x0800000)
	ROM_CONTINUE(0x1800000, 0x0800000)
	ROM_CONTINUE(0x2000000, 0x0800000)
	ROM_CONTINUE(0x3000000, 0x0800000)
	ROM_CONTINUE(0x2800000, 0x0800000)
	ROM_CONTINUE(0x3800000, 0x0800000)

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( lxcmcyfz ) // all games selectable
	ROM_REGION( 0x4000000, "mainrom", 0 )
	// sub-board was marked for 2GB capacity (A0-A26 address lines), but only address lines A0-A24 are connected to the chip
	ROM_LOAD( "jl2365_frozen.u1", 0x00000, 0x0800000, CRC(64d4c708) SHA1(1bc2d161326ce3039ab9ba46ad62695060cfb2e1) )
	ROM_CONTINUE(0x1000000, 0x0800000)
	ROM_CONTINUE(0x0800000, 0x0800000)
	ROM_CONTINUE(0x1800000, 0x0800000)
	ROM_CONTINUE(0x2000000, 0x0800000)
	ROM_CONTINUE(0x3000000, 0x0800000)
	ROM_CONTINUE(0x2800000, 0x0800000)
	ROM_CONTINUE(0x3800000, 0x0800000)

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( lxcmcydp ) // all games selectable
	ROM_REGION( 0x4000000, "mainrom", 0 )
	// sub-board was marked for 2GB capacity (A0-A26 address lines), but only address lines A0-A24 are connected to the chip
	ROM_LOAD( "cyberarcade-disneyprincess.bin", 0x00000, 0x4000000, CRC(05946f81) SHA1(33eea2b70f5427e7613c836b8a08148731fac231) )

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( lxcmcysp ) // all games selectable
	ROM_REGION( 0x4000000, "mainrom", 0 )
	// sub-board was marked for 2GB capacity (A0-A26 address lines), but only address lines A0-A24 are connected to the chip
	ROM_LOAD( "lexibookspiderman.bin", 0x00000, 0x4000000, CRC(ef6e8847) SHA1(0012df193c52fd48595d85886fd431619c5d5e3e) )

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( lxcmcycr ) // all games selectable
	ROM_REGION( 0x4000000, "mainrom", 0 )
	ROM_LOAD( "lexibook cars.bin", 0x00000, 0x4000000, CRC(198fe11b) SHA1(5e35caa3fc319ec69812c187a3ec89f01749f749) )

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( lxcmcypj ) // all games selectable
	ROM_REGION( 0x4000000, "mainrom", 0 )
	// sub-board was marked for 1GB capacity (A0-A25 address lines), but only address lines A0-A24 are connected to the chip
	ROM_LOAD( "cob66-1g-new02.u4", 0x00000, 0x0800000, CRC(78149671) SHA1(00dab8c0919e909e910525c18142e6a195b364f8) )
	ROM_CONTINUE(0x1000000, 0x0800000)
	ROM_CONTINUE(0x0800000, 0x0800000)
	ROM_CONTINUE(0x1800000, 0x0800000)
	ROM_CONTINUE(0x2000000, 0x0800000)
	ROM_CONTINUE(0x3000000, 0x0800000)
	ROM_CONTINUE(0x2800000, 0x0800000)
	ROM_CONTINUE(0x3800000, 0x0800000)

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( lxcmcyba ) // all games selectable
	ROM_REGION( 0x4000000, "mainrom", 0 )
	ROM_LOAD( "barbie.bin", 0x00000, 0x4000000, CRC(e38af9d0) SHA1(a978a4da61f007c152c70233e9628dbebb427743) )

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( lxcmcybt ) // all games selectable
	ROM_REGION( 0x4000000, "mainrom", 0 )
	ROM_LOAD( "batman.bin", 0x00000, 0x4000000, CRC(9f8f15ce) SHA1(396122ce68008e9c8f35b98f5246e8dc7725df17) )

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( lxcmcydpn ) // all games selectable
	ROM_REGION( 0x4000000, "mainrom", 0 )
	ROM_LOAD( "dp150.bin", 0x00000, 0x4000000, CRC(dce19f81) SHA1(e74190d5eea4c31ec0cdcc374b988db2dc1d37c6) )

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( lxcmcyspn ) // all games selectable
	ROM_REGION( 0x4000000, "mainrom", 0 )
	ROM_LOAD( "lexi_sp_cca_150.u2", 0x00000, 0x4000000, CRC(f57ee9cf) SHA1(4c9a322439f0c255845167e5a2e3762e56665c4e) )

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( lxcmcypp ) // all games selectable
	ROM_REGION( 0x4000000, "mainrom", 0 )
	// marked 512mbit, possible A22 / A23 are swapped as they were marked on the board in a different way.
	ROM_LOAD( "pawpatrol_compact.bin", 0x00000, 0x4000000, CRC(bf536762) SHA1(80dde8426a636bae33a82d779e564fa743eb3776) )

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( lxcypkdp ) // all games selectable
	ROM_REGION( 0x4000000, "mainrom", 0 )
	ROM_LOAD( "ddp.u2", 0x00000, 0x4000000, CRC(ac5ce022) SHA1(450d11886385aeadc81e62090acd1d8ef8fedcd8) )

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( lxcypksp ) // all games selectable
	ROM_REGION( 0x4000000, "mainrom", 0 )
	ROM_LOAD( "pocketspiderman.u2", 0x00000, 0x4000000, CRC(3e1af689) SHA1(e2ca78c35cd6d827928cf284ea3dcf8b397d347c) )

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( lxcmc250 ) // all games selectable
	ROM_REGION( 0x4000000, "mainrom", 0 )
	// sub-board was marked for 2GB capacity (A0-A26 address lines), but only address lines A0-A24 are connected to the chip
	ROM_LOAD( "cca250in1.u1", 0x00000, 0x0800000, CRC(6ccd6ad6) SHA1(fafed339097c3d1538faa306021a8373c1b799b3) )
	ROM_CONTINUE(0x1000000, 0x0800000)
	ROM_CONTINUE(0x0800000, 0x0800000)
	ROM_CONTINUE(0x1800000, 0x0800000)
	ROM_CONTINUE(0x2000000, 0x0800000)
	ROM_CONTINUE(0x3000000, 0x0800000)
	ROM_CONTINUE(0x2800000, 0x0800000)
	ROM_CONTINUE(0x3800000, 0x0800000)

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( lxccminn ) // all games selectable
	ROM_REGION( 0x4000000, "mainrom", 0 ) // sub-board was hardwired to only be able to address the lower 64MByte, was rewired to also dump upper half when dumping, upper half contains only garbage, hence ROM_IGNORE
	ROM_LOAD( "minnie_lexibook.bin", 0x00000, 0x4000000, CRC(3f8e5a69) SHA1(c9f11f3e5f9b73832a191f4d1620a85c1b70f79e) )
	ROM_IGNORE(0x4000000)

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( lxccplan ) // all games selectable
	ROM_REGION( 0x4000000, "mainrom", 0 ) // sub-board was hardwired to only be able to address the lower 64MByte, was rewired to also dump upper half when dumping, upper half contains only garbage, hence ROM_IGNORE
	ROM_LOAD( "planes_lexibook.bin", 0x00000, 0x4000000, CRC(76e1a962) SHA1(83b801c0e0e941ceb1c93e565e833b07c09412c3))
	ROM_IGNORE(0x4000000)

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END


ROM_START( red5mam )
	ROM_REGION( 0x8000000, "mainrom", 0 )
	ROM_LOAD( "mam.u3", 0x00000, 0x8000000, CRC(0c0a0ecd) SHA1(2dfd8437de17fc9975698f1933dd81fbac78466d) )
ROM_END

ROM_START( nubsupmf )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "w25q32fv.bin", 0x00000, 0x400000,  CRC(5ca234b2) SHA1(3eba3e690f68116fd3e5e914f8bd16b1dc2c0bc4) )
ROM_END

ROM_START( 36pcase )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "25q16.ic3", 0x00000, 0x200000, CRC(a8edb73e) SHA1(1028656530e411607ffa3b63788b42e41bf971d7) )
ROM_END


ROM_START( dvnimbus )
	ROM_REGION( 0x1000000, "mainrom", 0 )
	ROM_LOAD( "2012-7-4-v1.bin", 0x00000, 0x1000000, CRC(a91d7aa6) SHA1(9421b70b281bb630752bc352c3715258044c0bbe) )
ROM_END

ROM_START( zonefusn ) // all games selectable
	ROM_REGION( 0x1000000, "mainrom", 0 )
	ROM_LOAD( "fusion.bin", 0x00000, 0x1000000, CRC(240bf970) SHA1(1b82d95a252c08e52fb8da6320276574a30b60db) )

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( sealvt ) // all games selectable
	ROM_REGION( 0x1000000, "mainrom", 0 )
	ROM_LOAD( "l157-44 v02.u1", 0x00000, 0x1000000, CRC(0fabced0) SHA1(3f8cd85b12b125b01c831c9f2f2937e29c1b6205) )

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( gcs2mgp ) // all games selectable
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "gcs2_v4.u3", 0x00000, 0x1000000, CRC(3b5be765) SHA1(c54f1a732d638b0ee582ca822715c9d3a3af5ef3) )
ROM_END

// VT369 using SPI ROMs

ROM_START( lpgm240 )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "w25q64jv.u1", 0x00000, 0x800000, CRC(b973e65b) SHA1(36ff137068ea56b4679c2db386ac0067de0a9eaf) )

	VT3XX_INTERNAL_OPCODE_SWAP
ROM_END

ROM_START( tup240 )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "mini_arcade240.bin", 0x00000, 0x800000, CRC(d4b4bf6c) SHA1(9cf4557e27bc8659079c62abdd22a311e1843047) )

	VT3XX_INTERNAL_OPCODE_SWAP
ROM_END

ROM_START( sy889 )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "sy889_w25q64.bin", 0x00000, 0x800000, CRC(fcdaa6fc) SHA1(0493747facf2172b8af22010851668bb18cbb3e4) )
ROM_END

ROM_START( sy888b )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "sy888b_f25q32.bin", 0x00000, 0x400000, CRC(a8298c33) SHA1(7112dd13d5fb5f9f9d496816758defd22773ec6e) )
ROM_END

ROM_START( mc_cb280 )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "w25q32.u5", 0x00000, 0x400000, CRC(c9541bdf) SHA1(f0ce46f18658ca5dbed881e5a80460e59820bbd0) )
ROM_END

ROM_START( unkra200 ) // "Winbond 25Q64FVSIG 1324" SPI ROM
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "retro_machine_rom", 0x00000, 0x800000, CRC(0e824aa7) SHA1(957e98868559ecc22b3fa42c76692417b76bf132) )
ROM_END

ROM_START( dgun2577 )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "blackarcade_dump_dreambook-my_arcade.bin", 0x00000, 0x800000, CRC(9b95b912) SHA1(573c938a0f1acca8f3b75900fd0185bfe28d4fa5) )
ROM_END

ROM_START( lxcyber )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "lexibook_dump_correct.bin", 0x00000, 0x800000, CRC(74b71846) SHA1(e7dcfa7c53cc7d30678763c6e60f7a3250768849) )
ROM_END

ROM_START( gtct885 )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "ct-885 g25q64c.bin", 0x00000, 0x800000, CRC(a5b2b568) SHA1(79de79364fa731e421627ec68e3bfa9d311aa7fc) )

	ROM_REGION( 0x100, "extra", 0 ) // data from additional 8-pin chip for protection
	ROM_LOAD( "mystery chip.bin", 0x00000, 0x100, CRC(8173c1c2) SHA1(7521a4676166a81a79209638491026b2d8e32895) )
ROM_END

ROM_START( rd5_240 )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "red5.bin", 0x00000, 0x800000, CRC(0e564e73) SHA1(c29a927c830ab3876e9b63e2d41bef962c05518f) )

	ROM_REGION( 0x100, "extra", 0 ) // data from additional 8-pin chip for protection
	ROM_LOAD( "mystery chip.bin", 0x00000, 0x100, NO_DUMP )
ROM_END

ROM_START( myarccn )
	ROM_REGION( 0x100000, "mainrom", 0 )
	ROM_LOAD( "my_arcade_caveman_ninja.bin", 0x00000, 0x100000, CRC(dcc5590c) SHA1(a734cb9c81e58346ff5fa934347d7cb24a32cb39) )

	VT3XX_INTERNAL_NO_SWAP // verified for this set
ROM_END

ROM_START( hkb502 )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "red console.bin", 0x00000, 0x400000, CRC(e4766383) SHA1(64b0c20592f38928b3a639fa42b468ff09664808) )

	VT3XX_INTERNAL_NO_SWAP // verified for this set
ROM_END

ROM_START( hkb502a )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "hkb-502.bin", 0x00000, 0x400000, CRC(970f54d2) SHA1(b45df00d85a2e29fe9418563927584a048db94b3) )

	VT3XX_INTERNAL_NO_SWAP // verified for this set
ROM_END

ROM_START( lxcap )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "lexibook_cyber_arcade_pocket.bin", 0x00000, 0x800000, CRC(245d0cd3) SHA1(d91cca2d0f99a6ca202fa9ba6d03587ea8af0cd9) )

	VT3XX_INTERNAL_NO_SWAP // verified for this set

	ROM_REGION( 0x100, "extra", 0 ) // data from additional 8-pin chip for protection
	ROM_LOAD( "mystery chip.bin", 0x00000, 0x100, CRC(491d206b) SHA1(a5411a7afe3b4df93b1b22e5533f5010bd3aaa93) )
ROM_END

ROM_START( denv150 )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "denver150in1.bin", 0x00000, 0x800000, CRC(6b3819d7) SHA1(b0039945ce44a52ea224ab736d5f3c6980409b5d) )
	ROM_IGNORE(0x800000) // 2nd half is unused
ROM_END

ROM_START( egame150 )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "rom.bin", 0x00000, 0x800000, CRC(a19644ea) SHA1(01c004d126edf792f71c1e9ed98b3c96d9278a69) )
ROM_END

ROM_START( mog_m320 )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "w25q64fv.bin", 0x00000, 0x800000, CRC(3c5e1b36) SHA1(4bcbf35ebf2b1714ccde5de758a89a6a39528f89) )
ROM_END

ROM_START( otrail )
	ROM_REGION( 0x100000, "mainrom", 0 )
	ROM_LOAD( "g25q80cw.bin", 0x00000, 0x100000, CRC(b20a03ba) SHA1(c4ca8e590b07baaebed747537bc8f92e44bdd219) ) // dumped as QD25Q80C

	ROM_REGION( 0x200, "seeprom", 0 )
	ROM_LOAD( "t24c04a.bin", 0x000, 0x200, CRC(ce1fad6f) SHA1(82878996765739edba42042b6336460d5c8f8096) )
ROM_END

ROM_START( pactin )
	ROM_REGION( 0x100000, "mainrom", 0 )
	ROM_LOAD( "25q80a.u3", 0x00000, 0x100000, CRC(92935759) SHA1(2333e7dcab51fa34c8d875374371854121fff27a) )

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( tetrtin )
	ROM_REGION( 0x100000, "mainrom", 0 )
	ROM_LOAD( "25q80.u3", 0x00000, 0x100000, CRC(017a99b9) SHA1(e7f891762bbc3b80ae0f177654d8d066b7524bcd) )

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END


// GC31-369-20210702-V2 on PCB
ROM_START( unk128vt )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "w25q32.bin", 0x00000, 0x400000, CRC(35ccadf6) SHA1(80b25e374a097d1b9380b7e64013d7ac0d5aa2ca) )
ROM_END

ROM_START( 168pcase )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "25q32.u7", 0x000000, 0x400000, CRC(98e8e97a) SHA1(fd516ef2819a597130f5f7ace9a7838cb99ab08a) )
ROM_END

ROM_START( gbox2020 )
	ROM_REGION( 0x1000000, "mainrom", 0 )
	ROM_LOAD( "fgb2020.bin", 0x00000, 0x1000000, CRC(a685d943) SHA1(9b272daccd8fe244c910f031466a4fedd83d5236) ) // flash ROM
ROM_END

ROM_START( vibes240 )
	ROM_REGION( 0x1000000, "mainrom", 0 )
	// wouldn't read consistently
	ROM_LOAD( "s29gl128p11tfi01.bin", 0x000000, 0x1000000, BAD_DUMP CRC(7244d6e9) SHA1(951052f6cd8c873f85f79be9d64498a43e92fd10) )
	ROM_IGNORE(0x100)
ROM_END

ROM_START( lexi30 )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "lexi30.u3", 0x00000, 0x800000, CRC(0d4307ea) SHA1(0d7cf492f796b0bb871deebaca38a3ff3b2ed1e6) )

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( matet10 )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "tetriskeychain_p25q16sh_856015.bin", 0x00000, 0x200000, CRC(7a7251ea) SHA1(7ace8482a54f6b06982a90328779c21266d864fa) )
	ROM_IGNORE(0x300)
ROM_END

ROM_START( matetsl )
	ROM_REGION( 0x80000, "mainrom", 0 )
	ROM_LOAD( "slurpeetetris_p25q40sh_856013.bin", 0x00000, 0x80000, CRC(d3b68de8) SHA1(97bcdfcd31bc536b626f9a369afe18de60a399da) )
	ROM_IGNORE(0x300)
ROM_END

ROM_START( nesvt270 )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "w25q128jvs.u3", 0x00000, 0x1000300, CRC(fe189a90) SHA1(7f07ae89ae7ff49f139e936b08c9ef2a3467ea92) )
ROM_END

ROM_START( rbbrite )
	ROM_REGION( 0x100000, "mainrom", 0 )
	ROM_LOAD( "coleco_rainbowbrite_29dl800ba_000422cb.bin", 0x00000, 0x100000, CRC(d2ad0d7d) SHA1(4423a5aa2eda20b3621ab46e951ac08dc2d24789) )

	VT3XX_INTERNAL_NO_SWAP // not verified for this set, used for testing
ROM_END

ROM_START( goretrop )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "goretroportable.bin", 0x00000, 0x2000000, CRC(e7279dd3) SHA1(5f096ce22e46f112c2cc6588cb1c527f4f0430b5) )
ROM_END

ROM_START( s10fake )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "s29gl064a90tfir4.bin", 0x00000, 0x800000, CRC(8ba78851) SHA1(d482fc56efbbdf6ff890b775144fd49ecaa1b539) )
ROM_END

ROM_START( mc_89in1 )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "89in1.bin", 0x00000, 0x400000, CRC(b97f8ce5) SHA1(1a8e67f2b58a671ceec2b0ed18ec5954a71ae63a) )
ROM_END

ROM_START( mc_110cb )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "29w320dt.bin", 0x00000, 0x400000, CRC(a4bed7eb) SHA1(f1aa89916264ba781d3f1390a2336ef42129b607) )
ROM_END

ROM_START( mc_138cb )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "138-in-1 coolbaby, coolboy rs-5, pcb060-10009011v1.3.bin", 0x00000, 0x400000, CRC(6b5b1a1a) SHA1(2df0cd717bd0de0b0c973ac356426ddbb0d736fa) )
ROM_END

ROM_START( jl2050 )
	ROM_REGION( 0x1000000, "mainrom", 0 )
	ROM_LOAD( "jl2050.u5", 0x00000, 0x1000000, CRC(f96c5c02) SHA1(c7d0b57c2622b5213d3c7e6532495d9da74d4b01) )
ROM_END

ROM_START( supr200 )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "w25q64jv.u1", 0x00000, 0x800000, CRC(89b6f026) SHA1(4ba21d3d803984a9a6daf24f91cb429f566a2f4c) )
ROM_END

ROM_START( tiger108 )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "p25d32sh.u3", 0x00000, 0x400000, CRC(9d5112a3) SHA1(96a66b5b2c4e1fdc00b54951f867f7cc2a1a2a4a) )
	ROM_IGNORE(0x300)
ROM_END

ROM_START( gon100 )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "p25d32sh.bin", 0x00000, 0x400000, CRC(cd8a07c3) SHA1(4f5afc711eb214fbaad95b71087b9f50cf31345f) )
	ROM_IGNORE(0x300)
ROM_END

ROM_START( d12power )
	ROM_REGION( 0x1000000, "mainrom", 0 )
	ROM_LOAD( "25q128.u2", 0x00000, 0x1000000, CRC(02650ad4) SHA1(ca346409e11732d97b892c356fc5da61dc16ab01) )
ROM_END


ROM_START( dgun2572 )
	ROM_REGION( 0x2000000, "mainrom", 0 ) // extra pins on subboard not marked
	ROM_LOAD( "dreamgearwgun.bin", 0x00000, 0x2000000, CRC(92b55c75) SHA1(c7b2319e304a4bf480b5dcd4f24af2e6ba834d0d) )
ROM_END


ROM_START( gb50_150 )
	ROM_REGION( 0x1000000, "mainrom", 0 )
	ROM_LOAD( "w25q128jvsiq.bin", 0x00000, 0x1000000, CRC(3cc43fcb) SHA1(6c5e09fadb14e99e6db8c316026d124326a90557) )
ROM_END


void vt369_state::init_lxcmcypp()
{
	int size = memregion("mainrom")->bytes()/2;
	u16* ROM = (u16*)memregion("mainrom")->base();

	for (int i = 0; i < size; i++)
	{
		ROM[i] = bitswap<16>(ROM[i], 4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11);
	}
}

void vt369_state::init_dgun2572()
{
	u8 *rom = memregion("mainrom")->base();
	for (offs_t base = 0; base < 0x2000000; base += 0x200)
	{
		std::vector<u8> orig(&rom[base], &rom[base + 0x200]);
		for (offs_t offset = 0; offset < 0x200; offset += 2)
		{
			u16 data = get_u16le(&orig[bitswap<8>(offset, 6, 1, 8, 3, 4, 5, 2, 7) << 1]);
			put_u16le(&rom[base + offset], bitswap<16>(data, 15, 14, 13, 12, 11, 10, 1, 8, 7, 6, 5, 4, 0, 2, 9, 3));
		}
	}
}

void vt369_state::init_s10fake()
{
	uint8_t *romdata = memregion("mainrom")->base();
	for (offs_t i = 0; i < 0x800000; i += 2)
	{
		uint16_t w = get_u16le(&romdata[i]);
		put_u16le(&romdata[i], (w & 0xf9f9) | (w & 0x0600) >> 8 | (w & 0x0006) << 8);
	}
}

} // anonymous namespace


// Might not be VT369
// First half of games don't work, probably bad dump
CONS( 201?, dvnimbus,   0,        0,  vt369_unk_16mb, vt369, vt36x_state, empty_init, "<unknown>", "DVTech Nimbus 176 in 1", MACHINE_NOT_WORKING )


/****************************************************************************************************************

    Things below seem on heavily enhanced hardware of unknown VT type

    It's possible some of these are the same as some of the ones above (sy889, rminitv, dgun2573 etc.) but with
    more features used.

    In some cases these might be almost entirely different, and it is likely a number don't belong in this
    driver at all.

****************************************************************************************************************/

CONS( 2012, lexi30,  0,0,  vt36x_8mb, vt369_rot, vt36x_state, empty_init, "Lexibook", "Arcade Center (JL1800_01)", MACHINE_NOT_WORKING | ROT270 )


CONS( 2012, lxccatv,   0,  0,  vt36x_32mb, vt369, vt36x_state, empty_init,    "Lexibook", "Compact Cyber Arcade TV - 120 in 1 (JL2370)", MACHINE_NOT_WORKING ) // 32MByte ROM, 2011 on case, 2012 on PCB

// All Lexibook units below have 64Mbyte ROMs, must be externally banked, or different addressing scheme
CONS( 2012, lxcmcysp,  0,  0,  vt36x_32mb_2banks_lexi, vt369_rot, vt36x_state, empty_init,    "Lexibook", "Compact Cyber Arcade - Spider-Man (120-in-1)", MACHINE_NOT_WORKING | ROT270) // renders vertically, but screen stretches it to horizontal
CONS( 200?, lxcmc250,  0,  0,  vt36x_32mb_2banks_lexi, vt369, vt36x_state, empty_init,    "Lexibook", "Compact Cyber Arcade - 250-in-1 (JL2375)", MACHINE_NOT_WORKING )
CONS( 2012, lxcmcydp,  0,  0,  vt36x_32mb_2banks_lexi, vt369, vt36x_state, empty_init,    "Lexibook", "Compact Cyber Arcade - Disney Princess (120-in-1)", MACHINE_NOT_WORKING )
// JL2365 models (150-in-1 versions)
CONS( 200?, lxcmcysw,  0,  0,  vt36x_32mb_2banks_lexi, vt369, vt36x_state, empty_init,    "Lexibook", "Compact Cyber Arcade - Star Wars Rebels (JL2365SW)", MACHINE_NOT_WORKING )
CONS( 200?, lxcmcyfz,  0,  0,  vt36x_32mb_2banks_lexi, vt369, vt36x_state, empty_init,    "Lexibook", "Compact Cyber Arcade - Frozen (JL2365FZ)", MACHINE_NOT_WORKING )
CONS( 2018, lxcmcypj,  0,  0,  vt36x_32mb_2banks_lexi, vt369, vt36x_state, empty_init,    "Lexibook", "Compact Cyber Arcade - PJ Masks (JL2365PJM)", MACHINE_NOT_WORKING )
CONS( 2014, lxcmcyba,  0,  0,  vt36x_32mb_2banks_lexi, vt369, vt36x_state, empty_init,    "Lexibook", "Compact Cyber Arcade - Barbie (JL2365BB)", MACHINE_NOT_WORKING )
CONS( 2014, lxcmcycr,  0,  0,  vt36x_32mb_2banks_lexi, vt369, vt36x_state, empty_init,    "Lexibook", "Compact Cyber Arcade - Cars (JL2365DC)", MACHINE_NOT_WORKING )
// JL2367 models (150-in-1 versions, newer case style) - the data order is swapped for these (is this common to the JL2367 shell types?)
CONS( 2018, lxcmcypp,  0,  0,  vt36x_32mb_2banks_lexi, vt369, vt36x_state, init_lxcmcypp, "Lexibook", "Compact Cyber Arcade - Paw Patrol (JL2367PA)", MACHINE_NOT_WORKING )
CONS( 2020, lxcmcybt,  0,  0,  vt36x_32mb_2banks_lexi, vt369, vt36x_state, init_lxcmcypp, "Lexibook", "Compact Cyber Arcade - Batman (JL2367BAT)", MACHINE_NOT_WORKING )
CONS( 2014, lxcmcydpn, 0,  0,  vt36x_32mb_2banks_lexi, vt369, vt36x_state, init_lxcmcypp, "Lexibook", "Compact Cyber Arcade - Disney Princess (JL2367DP, 150-in-1)", MACHINE_NOT_WORKING )
CONS( 2014, lxcmcyspn, 0,  0,  vt36x_32mb_2banks_lexi, vt369, vt36x_state, init_lxcmcypp, "Lexibook", "Compact Cyber Arcade - Spider-Man (JL2367SP, 150-in-1)", MACHINE_NOT_WORKING )

// JL1895 models, Cyber Arcade Pocket.  This make strange use of the LCDC, the menus are vertical (so must be copied to the LCD rotated) but the games are horizontal as usual
CONS( 201?, lxcypkdp,  0,  0,  vt36x_32mb_2banks_lexi, vt369, vt36x_state, empty_init,    "Lexibook", "Cyber Arcade Pocket - Disney Princess (JL1895DP)", MACHINE_NOT_WORKING )
CONS( 201?, lxcypksp,  0,  0,  vt36x_32mb_2banks_lexi, vt369, vt36x_state, empty_init,    "Lexibook", "Cyber Arcade Pocket - Spider-Man (JL1895SP-2)", MACHINE_NOT_WORKING )

CONS( 200?, lxccminn,  0,  0,  vt36x_32mb_2banks_lexi, vt369, vt36x_state, empty_init,    "Lexibook", "Console Colour - Minnie Mouse (JL2800MN)", MACHINE_NOT_WORKING )
CONS( 200?, lxccplan,  0,  0,  vt36x_32mb_2banks_lexi, vt369, vt36x_state, empty_init,    "Lexibook", "Console Colour - Disney's Planes (JL2800PL)", MACHINE_NOT_WORKING )
// similar menus to the lxccminn/lxccplan sets
CONS( 2013, lxcmcy,    0,  0,  vt36x_32mb_2banks_lexi, vt369, vt36x_state, empty_init,    "Lexibook", "Compact Cyber Arcade - 200 in 1 (JL2355)", MACHINE_NOT_WORKING )
CONS( 2012, dgun2561,  0,  0,  vt36x_32mb_2banks_lexi, vt369, vt36x_state, empty_init, "dreamGEAR", "My Arcade Portable Gaming System with 140 Games (DGUN-2561)", MACHINE_NOT_WORKING ) // 64Mbyte ROM, must be externally banked, or different addressing scheme

// GB-NO13-Main-VT389-2 on PCBs - uses higher resolution mode (twice usual h-res?)
CONS( 2016, rtvgc300,  0,  0,  vt36x_32mb_2banks_lexi300, vt369, vt36x_state, empty_init,    "Lexibook", "Retro TV Game Console - 300 Games", MACHINE_NOT_WORKING )
CONS( 2017, rtvgc300fz,0,  0,  vt36x_32mb_2banks_lexi300, vt369, vt36x_state, empty_init,    "Lexibook", "Retro TV Game Console - Frozen - 300 Games", MACHINE_NOT_WORKING )


/* The following are also confirmed to be NES/VT derived units, most having a standard set of games with a handful of lazy graphic mods thrown in to fit the unit theme

    (handheld units, use standard AAA batteries)
    Lexibook Compact Cyber Arcade - Finding Dory

    (handheld units, use standard AAA batteries, smaller display)
    Lexibook Compact Cyber Arcade Pocket - Paw Patrol
    Lexibook Compact Cyber Arcade Pocket - Frozen

    (Handheld units, but different form factor to Compact Cyber Arcade, charged via USB, different menus)
    Lexibook Console Colour - Barbie

    (units for use with TV)
    Lexibook Retro TV Game Console (300 Games) - Cars
    Lexibook Retro TV Game Console (300 Games) - PJ Masks

    (more?)

    There are also updated 'Compact Cyber Arcade' branded units with a large + D-pad and internal battery / USB charger for
    Spiderman
    Frozen
    (generic)
    it isn't verified if these use the same ROMs as the original Compact Cyber Arcade releases, or if the software has been updated

*/

// uncertain, NOT SPI ROM
CONS( 200?, zonefusn,  0,         0,  vt36x_16mb,     vt369, vt36x_state, empty_init, "Ultimate Products / Jungle's Soft", "Zone Fusion",  MACHINE_NOT_WORKING )
// same as above but without Jungle's Soft boot logo? model number taken from cover of manual
CONS( 200?, sealvt,    zonefusn,  0,  vt36x_16mb,     vt369, vt36x_state, empty_init, "Lexibook / Sit Up Limited / Jungle's Soft", "Seal 30-in-1 (VT based, Model FN098134)",  MACHINE_NOT_WORKING )

// possibly VT269; contains high-resolution versions of classic NES games
// sub-CPU hangs on unemulated $2117 register and later uses vtsetdbk to switch opcode encryption
CONS( 201?, dgun2572, 0,  0,  vt36x_32mb, vt369, vt36x_state, init_dgun2572, "dreamGEAR", "My Arcade Wireless Video Game Station 200-in-1 (DGUN-2572)", MACHINE_NOT_WORKING )

// NOT SPI roms, altswap sets code starts with '6a'

CONS( 201?, red5mam,  0,  0,  vt36x_altswap_32mb_4banks_red5mam, vt369, vt36x_state, empty_init, "Red5", "Mini Arcade Machine (Red5, 'Xtra Game')", MACHINE_NOT_WORKING ) // 128Mbyte ROM, must be externally banked or different addressing scheme

CONS( 2016, dgun2593,  0,  0,  vt36x_altswap_32mb_4banks_red5mam, vt369, vt36x_state, empty_init, "dreamGEAR", "My Arcade Retro Arcade Machine - 300 Handheld Video Games (DGUN-2593)", MACHINE_NOT_WORKING ) // 128Mbyte ROM, must be externally banked or different addressing scheme

CONS( 200?, gcs2mgp,   0,  0,  vt36x_altswap_16mb, vt369_rot, vt36x_state, empty_init, "Jungle's Soft", "Mini Game Player 48-in-1",  MACHINE_NOT_WORKING | ROT270 )

// Not the same as the other 240-in-1 machine from Thumbs Up below (tup240) This one makes greater use of newer VT features with most games having sampled music, not APU sound.
// Several of the games contained in here are buggy / broken on real hardware (see https://www.youtube.com/watch?v=-mgGNaDQ1HE )
CONS( 201?, 240in1ar,  0,  0,  vt36x_altswap_32mb_4banks_red5mam, vt369, vt36x_state, empty_init, "Thumbs Up", "Mini Arcade Machine (Thumbs Up, 240IN1ARC)", MACHINE_NOT_WORKING ) // 128Mbyte ROM, must be externally banked or different addressing scheme
// portable fan + famiclone combo handheld, very similar to 240in1ar
CONS( 2020, nubsupmf,   0,      0,  vt36x_altswap_4mb, vt369, vt36x_state, empty_init, "<unknown>", "NubSup Mini Game Fan", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )

// protected?
CONS( 202?, 36pcase,    0,      0,  vt36x_altswap_2mb, vt369, vt36x_state, empty_init, "<unknown>", "36-in-1 Classic Games phone case", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )


/*****************************************************************************
* below are VT369? games that use flash ROM
*****************************************************************************/

// different SoC (and language select music) from 2019 version, opcodes are scrambled
CONS( 2020, gbox2020, gbox2019, 0, vt36x_gbox2020_16mb, vt369, vt36x_state, empty_init, "Sup", "Game Box 400 in 1 (2020 PCB)", MACHINE_NOT_WORKING )

// unknown tech, probably from 2021, probably VT369, ROM wouldn't read consistently
// boots with bad colors
CONS( 202?, vibes240, 0,        0,  vt36x_vibesswap_16mb, vt369, vt36x_state, empty_init, "<unknown>", "Vibes Retro Pocket Gamer 240-in-1", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )

// has extra protection?
CONS( 2018, rbbrite,    0,        0,  vt369_unk_1mb, vt369, vt36x_state, empty_init, "Coleco", "Rainbow Brite (mini-arcade)", MACHINE_NOT_WORKING )

// there's also a 250+ version of the unit below at least; protection(?) is similar to rbbrite
CONS( 2018, goretrop,  0,  0,  vt369_unk_32mb, vt369, vt36x_state, empty_init,    "Retro-Bit", "Go Retro Portable 260+ Games", MACHINE_NOT_WORKING )

// all games after the first 180 listed on the menu are duplicates. BTANB: games 501-520 are mislabeled duplicates: e.g., "511. Exerion" actually loads Pac-Man.
// unused routines suggest this was originally developed for nes_vt42xx.cpp hardware (cf. g9_666, g5_500 with the same bitswap)
// there are other S10 units available
CONS( 202?, s10fake,   0,  0,  vt36x_s10swap_8mb, vt369, vt36x_state, init_s10fake, "<unknown>", "S10 Handheld Game Console (520-in-1, fake entries)", MACHINE_NOT_WORKING )

/*****************************************************************************
* below are VT369 games that use SQI / SPI ROM
*****************************************************************************/

// Runs well, minor GFX issues in intro
CONS( 2017, sy889,      0,        0,  vt36x_8mb, vt369, vt36x_state, empty_init, "SY Corp",   "SY-889 300 in 1 Handheld", MACHINE_IMPERFECT_GRAPHICS )
CONS( 2016, sy888b,     0,        0,  vt36x_4mb, vt369, vt36x_state, empty_init, "SY Corp",   "SY-888B 288 in 1 Handheld", MACHINE_IMPERFECT_GRAPHICS )

// Same hardware as SY-889
CONS( 201?, mc_cb280,   0,        0,  vt36x_swap_4mb, vt369, vt36x_state, empty_init, "CoolBoy",   "Coolboy RS-18 (280 in 1)", MACHINE_IMPERFECT_GRAPHICS )

// Plays intro music but then crashes. same hardware as SY-88x but uses more features
CONS( 2016, mog_m320,   0,        0,  vt36x_8mb, vt369, vt36x_state, empty_init, "MOGIS",    "MOGIS M320 246 in 1 Handheld", MACHINE_NOT_WORKING )

// VT369, but doesn't use most features
CONS( 200?, lpgm240,    0,        0,  vt36x_swap_8mb,        vt369, vt36x_state, empty_init, "<unknown>", "Let's Play! Game Machine 240 in 1", MACHINE_NOT_WORKING ) // mini 'retro-arcade' style cabinet
CONS( 200?, tup240,     lpgm240,  0,  vt36x_swap_8mb,        vt369, vt36x_state, empty_init, "Thumbs Up", "Thumbs Up 240-in-1 Mini Arcade Machine", MACHINE_NOT_WORKING )

// VT369, but doesn't use most features
CONS( 201?, unkra200,   mc_tv200, 0,  vt36x_8mb, vt369, vt36x_state, empty_init, "<unknown>",    "200 in 1 Retro Arcade", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
CONS( 201?, dgun2577,   mc_tv200, 0,  vt36x_8mb, vt369, vt36x_state, empty_init, "dreamGEAR",    "My Arcade Retro Machine 200-in-1 (DGUN-2577)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
CONS( 201?, lxcyber,    mc_tv200, 0,  vt36x_8mb, vt369, vt36x_state, empty_init, "Lexibook",     "Cyber Arcade 200-in-1", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
 // menu is protected with code from extra ROM
CONS( 201?, gtct885,    mc_tv200, 0,  vt36x_8mb, vt369, vt36x_state, empty_init, "Gaming Tech",  "Gaming Tech CT-885", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
 // similar to above, but with 40 extra games, menu is protected with code from extra ROM (although RTS opcodes seem to work)
CONS( 201?, rd5_240,    0,        0,  vt36x_8mb, vt369, vt36x_state, empty_init, "Red5",         "Mini Arcade Machine 240-in-1 (Red5)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )

CONS( 201?, hkb502,   0,      0,  vt36x_4mb, vt369, vt36x_state, empty_init, "<unknown>", "HKB-502 268-in-1 (set 1)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
CONS( 201?, hkb502a,  hkb502, 0,  vt36x_4mb, vt369, vt36x_state, empty_init, "<unknown>", "HKB-502 268-in-1 (set 2)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
// similar to above, fewer games in menu
CONS( 2021, unk128vt, 0,      0,  vt36x_4mb, vt369, vt36x_state, empty_init, "<unknown>", "unknown VT369 based 128-in-1 (GC31-369-20210702-V2)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )

// case was for an iPhone 11, but seems to be available for many different phones
CONS( 202?, 168pcase, 0,      0,  vt36x_4mb, vt369, vt36x_state, empty_init, "<unknown>", "Diier-D-10 168-in-1 phone case", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )

// uses a LCD with resolution of 160x128 (image scaled to fit for some games, others run natively at 160x128)
// contains a protection chip, command 80 XX returns a byte
CONS( 201?, lxcap,    0,      0,  vt36x_8mb, vt369, vt36x_tetrtin_state, empty_init, "Lexibook", "Cyber Arcade Pocket (JL1895)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )

// 2022 date on 'BL-867 PCB03' PCB, has extra protection?
CONS( 2022, nesvt270,    0,  0,  vt36x_16mb, vt369, vt36x_state, empty_init, "<unknown>", "unknown VT3xx based 270-in-1 (BL-867 PCB03)", MACHINE_NOT_WORKING )

// VT369, but doesn't use most features
CONS( 201?, myarccn,   0, 0,  vt36x_1mb, vt369, vt36x_state, empty_init, "dreamGEAR", "My Arcade Caveman Ninja", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )

// confirmed VT369, uses more features (including sound CPU)
CONS( 201?, denv150,   0,        0,  vt36x_8mb, vt369, vt36x_state, empty_init, "Denver", "Denver Game Console GMP-240C 150-in-1", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
CONS( 201?, egame150,  denv150,  0,  vt36x_swap_8mb, vt369, vt36x_state, empty_init, "<unknown>", "E-Game! 150-in-1", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )

// uncertain, uses SPI ROM so probably VT369, has extra protection? (but RAM test goes up to 0x2000, over the internal ROM area?)
CONS( 2017, otrail,     0,        0,  vt36x_1mb, vt369, vt36x_state, empty_init, "Basic Fun", "The Oregon Trail", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )

// seems to be running the NES version of Pac-Man with some extra splash screens, has extra protection
CONS( 2021, pactin,     0,        0,  vt36x_1mb, vt369, vt36x_tetrtin_state, empty_init, "Fizz Creations", "Pac-Man Arcade in a Tin", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
// has extra protection
CONS( 2021, tetrtin,    0,        0,  vt36x_1mb, vt369, vt36x_tetrtin_state, empty_init, "Fizz Creations", "Tetris Arcade in a Tin", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )

// uses a low res display (so vt3xx?)
CONS( 2021, matet10,   0,        0,  vt36x_swap_2mb, vt369, vt36x_state, empty_init, "dreamGEAR", "My Arcade Tetris (DGUNL-7083, Pixel Pocket, with 10 bonus games)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2021, matetsl,   0,        0,  vt36x_swap_512kb, vt369, vt36x_state, empty_init, "dreamGEAR", "My Arcade Tetris (Slurpee)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS ) // no bonus games on this model

// Runs well, all games seem to work
CONS( 201?, mc_89in1,  0,        0,  vt36x_4mb, vt369, vt36x_state, empty_init, "<unknown>", "89 in 1 Mini Game Console (060-92023011V1.0)", MACHINE_IMPERFECT_GRAPHICS )

// both offer chinese or english menus
CONS( 200?, mc_110cb,  0,        0,  vt36x_4mb, vt369, vt36x_state, empty_init, "CoolBoy", "110 in 1 CoolBaby (CoolBoy RS-1S)", MACHINE_IMPERFECT_GRAPHICS )
CONS( 200?, mc_138cb,  0,        0,  vt36x_4mb, vt369, vt36x_state, empty_init, "CoolBoy", "138 in 1 CoolBaby (CoolBoy RS-5, PCB060-10009011V1.3)", MACHINE_IMPERFECT_GRAPHICS )

CONS( 200?, jl2050,    0,        0,  vt36x_16mb, vt369, vt36x_state, empty_init, "LexiBook / JungleTac / NiceCode",  "Cyber Console Center 200-in-1 (JL2050)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// the menus are very different to the plug-in TV version found in ppgc200g
CONS( 201?, supr200,    0,        0,  vt36x_swap_8mb, vt369, vt36x_state, empty_init, "Fizz Creations",  "Supreme 200 (handheld)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

CONS( 201?, tiger108,  0,        0,  vt36x_4mb, vt369, vt36x_state, empty_init, "Zebra AS / Tiger Retail", "Spillekonsol Game console - 108-in-1", MACHINE_IMPERFECT_GRAPHICS )

CONS( 201?, gon100,    0,        0,  vt36x_4mb, vt369, vt36x_state, empty_init, "<unknown>", "Game On 100-in-1", MACHINE_IMPERFECT_GRAPHICS )

CONS( 201?, d12power,  0,        0,  vt36x_16mb, vt369, vt36x_state, empty_init, "SZDiiER", "Power - Charging and playing games (D12) (416-in-1)", MACHINE_IMPERFECT_GRAPHICS )

// GB-50 console supports loading games from SD card (not emulated), main ROM is QSPI flash
// Games loaded from SD card are loaded into the QSPI flash at 0x800000 - dump is from a clean factory console
// PCB is marked "389" so possibly VT389 but VT369 string in a debug message in firmware
CONS( 2019, gb50_150,  0,        0,  vt36x_16mb_sdcard, vt369, vt36x_state, empty_init, "<unknown>",  "GB-50 Retro Station Pocket System", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )



nes_vt42xx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/***************************************************************************

  nes_vt42xx.cpp

  unknown VT-based SOC

 ***************************************************************************/

#include "emu.h"
#include "nes_vt42xx_soc.h"

#include "multibyte.h"

#include <algorithm>

namespace {

class nes_vt42xx_base_state : public driver_device
{
public:
	nes_vt42xx_base_state(const machine_config& mconfig, device_type type, const char* tag) :
		driver_device(mconfig, type, tag),
		m_io0(*this, "IO0"),
		m_io1(*this, "IO1"),
		m_cartsel(*this, "CARTSEL"),
		m_exin(*this, "EXTRAIN%u", 0U),
		m_prgrom(*this, "mainrom")
	{ }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual uint8_t in0_r();
	virtual uint8_t in1_r();
	virtual void in0_w(uint8_t data);

	optional_ioport m_io0;
	optional_ioport m_io1;

	uint8_t m_latch0;
	uint8_t m_latch1;
	uint8_t m_previous_port0;

	optional_ioport m_cartsel;
	optional_ioport_array<4> m_exin;

	/* Misc */
	uint32_t m_ahigh; // external banking bits

	required_region_ptr<uint8_t> m_prgrom;

	uint8_t vt_rom_r(offs_t offset);
	[[maybe_unused]] void vtspace_w(offs_t offset, uint8_t data);

	void configure_soc(nes_vt02_vt03_soc_device* soc);

	uint8_t upper_412c_r();
	uint8_t upper_412d_r();
	void upper_412c_w(uint8_t data);

private:
	/* Extra IO */
	template <uint8_t NUM> uint8_t extrain_r();
};

class nes_vt42xx_state : public nes_vt42xx_base_state
{
public:
	nes_vt42xx_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt42xx_base_state(mconfig, type, tag),
		m_soc(*this, "soc")
	{ }

	void vt_external_space_map_1mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_2mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_4mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_8mbyte(address_map &map) ATTR_COLD;
	void vt_external_space_map_16mbyte(address_map &map) ATTR_COLD;

	void nes_vt42xx(machine_config& config);
	void nes_vt42xx_1mb(machine_config& config);
	void nes_vt42xx_2mb(machine_config& config);
	void nes_vt42xx_4mb(machine_config& config);
	void nes_vt42xx_8mb(machine_config& config);
	void nes_vt42xx_16mb(machine_config& config);

	void init_rfcp168();
	void init_g9_666();
	void init_hhgc319();
	void init_bl339();

protected:
	uint8_t vt_rom_banked_r(offs_t offset);

	required_device<nes_vt02_vt03_soc_device> m_soc;
};

class nes_vt42xx_bitboy_state : public nes_vt42xx_state
{
public:
	nes_vt42xx_bitboy_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt42xx_state(mconfig, type, tag)
	{ }

	void nes_vt42xx_bitboy_2x16mb(machine_config& config);
	void nes_vt42xx_gprnrs16_2x16mb(machine_config& config);

	void vt_external_space_map_bitboy_2x16mbyte(address_map &map) ATTR_COLD;

private:
	void bittboy_412c_w(u8 data);
	void gprnrs16_412c_w(u8 data);
};

class nes_vt42xx_fapocket_state : public nes_vt42xx_state
{
public:
	nes_vt42xx_fapocket_state(const machine_config& mconfig, device_type type, const char* tag) :
		nes_vt42xx_state(mconfig, type, tag)
	{ }

	void nes_vt42xx_fa(machine_config& config);

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	void vt_external_space_map_fa_4x16mbyte(address_map &map) ATTR_COLD;

	u8 fapocket_412c_r();
	void fapocket_412c_w(u8 data);
};

uint8_t nes_vt42xx_base_state::vt_rom_r(offs_t offset)
{
	return m_prgrom[offset];
}

void nes_vt42xx_base_state::vtspace_w(offs_t offset, uint8_t data)
{
	logerror("%s: vtspace_w %08x : %02x", machine().describe_context(), offset, data);
}

// use maps with mirroring in depending on ROM size (this SoC can only access 16MB without banking?)
void nes_vt42xx_state::vt_external_space_map_1mbyte(address_map &map)
{
	map(0x0000000, 0x00fffff).mirror(0x1f00000).r(FUNC(nes_vt42xx_state::vt_rom_r));
}

void nes_vt42xx_state::vt_external_space_map_2mbyte(address_map &map)
{
	map(0x0000000, 0x01fffff).mirror(0x1e00000).r(FUNC(nes_vt42xx_state::vt_rom_r));
}

void nes_vt42xx_state::vt_external_space_map_4mbyte(address_map &map)
{
	map(0x0000000, 0x03fffff).mirror(0x1c00000).r(FUNC(nes_vt42xx_state::vt_rom_r));
}

void nes_vt42xx_state::vt_external_space_map_8mbyte(address_map &map)
{
	map(0x0000000, 0x07fffff).mirror(0x1800000).r(FUNC(nes_vt42xx_state::vt_rom_r));
}

void nes_vt42xx_state::vt_external_space_map_16mbyte(address_map &map)
{
	map(0x0000000, 0x0ffffff).mirror(0x1000000).r(FUNC(nes_vt42xx_state::vt_rom_r));
}


uint8_t nes_vt42xx_state::vt_rom_banked_r(offs_t offset)
{
	return m_prgrom[m_ahigh | offset];
}

void nes_vt42xx_bitboy_state::vt_external_space_map_bitboy_2x16mbyte(address_map &map)
{
	map(0x0000000, 0x0ffffff).mirror(0x1000000).r(FUNC(nes_vt42xx_bitboy_state::vt_rom_banked_r));
}

void nes_vt42xx_fapocket_state::vt_external_space_map_fa_4x16mbyte(address_map &map)
{
	map(0x0000000, 0x0ffffff).mirror(0x1000000).r(FUNC(nes_vt42xx_fapocket_state::vt_rom_banked_r));
}


template <uint8_t NUM> uint8_t nes_vt42xx_base_state::extrain_r()
{
	if (m_exin[NUM])
		return m_exin[NUM]->read();
	else
	{
		logerror("%s: extrain_r (port %d) (not hooked up)\n", NUM, machine().describe_context());
	}
	return 0x00;
}


/* Standard I/O handlers (NES Controller clone) */

uint8_t nes_vt42xx_base_state::in0_r()
{
	//logerror("%s: in0_r\n", machine().describe_context());
	uint8_t ret = 0x40;
	ret |= m_latch0 & 1;
	m_latch0 >>= 1;
	return ret;
}

uint8_t nes_vt42xx_base_state::in1_r()
{
	//logerror("%s: in1_r\n", machine().describe_context());
	uint8_t ret = 0x40;
	ret |= m_latch1 & 1;
	m_latch1 >>= 1;
	return ret;
}

void nes_vt42xx_base_state::in0_w(uint8_t data)
{
	//logerror("%s: in0_w %02x\n", machine().describe_context(), data);
	if ((data & 0x01) != (m_previous_port0 & 0x01))
	{
		if (data & 0x01)
		{
			m_latch0 = m_io0->read();
			m_latch1 = m_io1->read();
		}
	}

	m_previous_port0 = data;
}


void nes_vt42xx_base_state::machine_start()
{
	m_latch0 = 0;
	m_latch1 = 0;
	m_previous_port0 = 0;

	m_ahigh = 0;

	save_item(NAME(m_latch0));
	save_item(NAME(m_latch1));
	save_item(NAME(m_previous_port0));

	save_item(NAME(m_ahigh));
}

void nes_vt42xx_base_state::machine_reset()
{

}

void nes_vt42xx_fapocket_state::machine_reset()
{
	nes_vt42xx_state::machine_reset();

	m_ahigh = 0;
}

void nes_vt42xx_base_state::configure_soc(nes_vt02_vt03_soc_device* soc)
{
	soc->set_addrmap(AS_PROGRAM, &nes_vt42xx_state::vt_external_space_map_16mbyte);
	soc->read_0_callback().set(FUNC(nes_vt42xx_base_state::in0_r));
	soc->read_1_callback().set(FUNC(nes_vt42xx_base_state::in1_r));
	soc->write_0_callback().set(FUNC(nes_vt42xx_base_state::in0_w));

	soc->extra_read_0_callback().set(FUNC(nes_vt42xx_base_state::extrain_r<0>));
	soc->extra_read_1_callback().set(FUNC(nes_vt42xx_base_state::extrain_r<1>));
	soc->extra_read_2_callback().set(FUNC(nes_vt42xx_base_state::extrain_r<2>));
	soc->extra_read_3_callback().set(FUNC(nes_vt42xx_base_state::extrain_r<3>));
}

// TODO: these should be in the SoC devices - upper_412d_r gets read, compared against, and another register written based on the result (maybe detecting SoC type?)
uint8_t nes_vt42xx_base_state::upper_412c_r() { logerror("%s: nes_vt42xx_base_state:upper_412c_r\n", machine().describe_context()); return 0x00; }
uint8_t nes_vt42xx_base_state::upper_412d_r() { logerror("%s: nes_vt42xx_base_state:upper_412d_r\n", machine().describe_context()); return 0x00; }
void nes_vt42xx_base_state::upper_412c_w(uint8_t data) { logerror("%s: nes_vt42xx_base_state:upper_412c_w %02x\n", machine().describe_context(), data); }


static INPUT_PORTS_START( nes_vt42xx )
	PORT_START("IO0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_8WAY

	PORT_START("IO1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_8WAY
INPUT_PORTS_END

void nes_vt42xx_state::nes_vt42xx(machine_config &config)
{
	/* basic machine hardware */
	NES_VT42XX_SOC(config, m_soc, NTSC_APU_CLOCK);
	configure_soc(m_soc);

	dynamic_cast<nes_vt09_soc_device&>(*m_soc).upper_read_412c_callback().set(FUNC(nes_vt42xx_state::upper_412c_r));
	dynamic_cast<nes_vt09_soc_device&>(*m_soc).upper_read_412d_callback().set(FUNC(nes_vt42xx_state::upper_412d_r));
	dynamic_cast<nes_vt09_soc_device&>(*m_soc).upper_write_412c_callback().set(FUNC(nes_vt42xx_state::upper_412c_w));

	m_soc->set_default_palette_mode(PAL_MODE_NEW_RGB12);
	m_soc->force_bad_dma();
}

void nes_vt42xx_state::nes_vt42xx_1mb(machine_config& config)
{
	nes_vt42xx(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt42xx_state::vt_external_space_map_1mbyte);
}

void nes_vt42xx_state::nes_vt42xx_2mb(machine_config& config)
{
	nes_vt42xx(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt42xx_state::vt_external_space_map_2mbyte);
}

void nes_vt42xx_state::nes_vt42xx_4mb(machine_config& config)
{
	nes_vt42xx(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt42xx_state::vt_external_space_map_4mbyte);
}

void nes_vt42xx_state::nes_vt42xx_8mb(machine_config& config)
{
	nes_vt42xx(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt42xx_state::vt_external_space_map_8mbyte);
}

void nes_vt42xx_state::nes_vt42xx_16mb(machine_config& config)
{
	nes_vt42xx(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt42xx_state::vt_external_space_map_16mbyte);
}


void nes_vt42xx_bitboy_state::bittboy_412c_w(u8 data)
{
	// bittboy (ok)
	logerror("%s: vt03_412c_extbank_w %02x\n", machine().describe_context(),  data);
	m_ahigh = (data & 0x04) ? (1 << 24) : 0x0;
}

void nes_vt42xx_bitboy_state::gprnrs16_412c_w(u8 data)
{
	logerror("%s: vt03_412c_extbank_w %02x\n", machine().describe_context(),  data);
	m_ahigh = (data & 0x02) ? (1 << 24) : 0x0;
}

void nes_vt42xx_bitboy_state::nes_vt42xx_bitboy_2x16mb(machine_config& config)
{
	nes_vt42xx(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt42xx_bitboy_state::vt_external_space_map_bitboy_2x16mbyte);

	dynamic_cast<nes_vt09_soc_device&>(*m_soc).upper_write_412c_callback().set(FUNC(nes_vt42xx_bitboy_state::bittboy_412c_w));
}

void nes_vt42xx_bitboy_state::nes_vt42xx_gprnrs16_2x16mb(machine_config& config)
{
	nes_vt42xx(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt42xx_bitboy_state::vt_external_space_map_bitboy_2x16mbyte);

	dynamic_cast<nes_vt09_soc_device&>(*m_soc).upper_write_412c_callback().set(FUNC(nes_vt42xx_bitboy_state::gprnrs16_412c_w));
}


u8 nes_vt42xx_fapocket_state::fapocket_412c_r()
{
	if (m_cartsel)
		return m_cartsel->read();
	else
		return 0;
}

void nes_vt42xx_fapocket_state::fapocket_412c_w(u8 data)
{
	// fapocket (ok?) (also uses bank from config switch for fake cartridge slot)
	logerror("%s: vtfa_412c_extbank_w %02x\n", machine().describe_context(), data);
	m_ahigh = 0;
	m_ahigh |= (data & 0x01) ? (1 << 25) : 0x0;
	m_ahigh |= (data & 0x02) ? (1 << 24) : 0x0;
}

void nes_vt42xx_fapocket_state::nes_vt42xx_fa(machine_config& config)
{
	nes_vt42xx(config);
	m_soc->set_addrmap(AS_PROGRAM, &nes_vt42xx_fapocket_state::vt_external_space_map_fa_4x16mbyte);

	dynamic_cast<nes_vt09_soc_device&>(*m_soc).upper_read_412c_callback().set(FUNC(nes_vt42xx_fapocket_state::fapocket_412c_r));
	dynamic_cast<nes_vt09_soc_device&>(*m_soc).upper_write_412c_callback().set(FUNC(nes_vt42xx_fapocket_state::fapocket_412c_w));
}


static INPUT_PORTS_START( nes_vt42xx_fa )
	PORT_INCLUDE(nes_vt42xx)

	PORT_START("CARTSEL")
	PORT_DIPNAME( 0x08, 0x00, "Cartridge Select" ) PORT_CODE(KEYCODE_3) PORT_TOGGLE
	PORT_DIPSETTING(    0x00, "508-in-1" )
	PORT_DIPSETTING(    0x08, "130-in-1" )
INPUT_PORTS_END


ROM_START( rfcp168 )
	ROM_REGION( 0x1000000, "mainrom", 0 )
	ROM_LOAD( "winbond_w29gl128c.bin", 0x00000, 0x1000000, CRC(d11caf71) SHA1(64b269cee30a51549a2d0491bbeed07751771559) ) // ROM verified on 2 units
ROM_END

ROM_START( g9_666 )
	ROM_REGION( 0x1000000, "mainrom", 0 )
	ROM_LOAD( "666in1.u1", 0x00000, 0x1000000, CRC(e3a98465) SHA1(dfec3e74e36aef9bfa57ec530c37642015569dc5) )
ROM_END

ROM_START( g5_500 )
	ROM_REGION( 0x1000000, "mainrom", 0 )
	ROM_LOAD( "s29gl128.u1", 0x00000, 0x1000000, CRC(de779dd7) SHA1(ac6d3fa6f18ceb795532ba9e85edffc040d74347) )
ROM_END

ROM_START( hhgc319 )
	ROM_REGION( 0x1000000, "mainrom", 0 )
	ROM_LOAD( "s29gl128n10tfi01.u3", 0x000000, 0x1000000, CRC(4b51125f) SHA1(bab3981ae1652cf6620c7c6769a6729a1e4d588f) )
ROM_END

ROM_START( bl339 )
	ROM_REGION( 0x1000000, "mainrom", 0 )
	ROM_LOAD( "s29gl128n10tfi01.u3", 0x000000, 0x1000000, CRC(291cbd8c) SHA1(0b40b15dd8ba895e7fcaa806c9b6fd7309b37ff5) )
ROM_END


ROM_START( bittboy )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "bittboy_flash_read_s29gl256n-tf-v2.bin", 0x00000, 0x2000000, CRC(24c802d7) SHA1(c1300ff799b93b9b53060b94d3985db4389c5d3a) )
ROM_END

ROM_START( mc_hh210 )
	ROM_REGION( 0x1000000, "mainrom", 0 )
	ROM_LOAD( "msp55lv128t.u4", 0x00000, 0x1000000, CRC(9ba520d4) SHA1(627f811b24314197e289a2ade668ff4115421bed) )
ROM_END

ROM_START( retro400 )
	ROM_REGION( 0x1000000, "mainrom", 0 )
	ROM_LOAD( "retro fc 400-in-1.bin", 0x00000, 0x1000000, CRC(4bf9991b) SHA1(ce9cac61cfc950d832d47afc76eb6c1488eeb2ca) ) // BGA on Subboard
ROM_END

ROM_START( gbox2019 )
	ROM_REGION( 0x1000000, "mainrom", 0 )
	ROM_LOAD( "fgb2019.bin", 0x00000, 0x1000000, CRC(7ef130d5) SHA1(00f45974494707fdac78153b13d8cfb503716ad0) ) // flash ROM
ROM_END

ROM_START( gprnrs1 )
	ROM_REGION( 0x800000, "mainrom", 0 )
	ROM_LOAD( "gprnrs1.bin", 0x00000, 0x800000, CRC(c3ffcec8) SHA1(313a790fb51d0b155257f9de84726ed67da43a8f) )
ROM_END

ROM_START( gprnrs16 )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "gprnrs16.bin", 0x00000, 0x2000000, CRC(bdffa40a) SHA1(3d01907211f18e8415171dfc6c1d23cf5952e7bb) )
ROM_END

ROM_START( mc_7x6ss )
	ROM_REGION( 0x100000, "mainrom", 0 )
	ROM_LOAD( "777777-in-1, 8 bit slim station, newpxp-dvt22-a pcb.bin", 0x00000, 0x100000, CRC(7790c21a) SHA1(f320f3dd18b88ae5f65bb51f58d4cb869997bab3) )
ROM_END

ROM_START( mc_8x6ss )
	ROM_REGION( 0x100000, "mainrom", 0 ) // odd size rom, does it need stripping?
	ROM_LOAD( "888888-in-1, 8 bit slim station, newpxp-dvt22-a pcb.bin", 0x00000, 0x100000, CRC(47149d0b) SHA1(5a8733886b550e3235dd90fb415b5a602e967f91) )
	ROM_IGNORE(0xce1)
ROM_END

// PXP2 8Bit Slim Station
ROM_START( mc_9x6ss )
	ROM_REGION( 0x400000, "mainrom", 0 )
	ROM_LOAD( "s29gl032.u3", 0x00000, 0x400000, CRC(9f4194e8) SHA1(bd2a356aea56188ea78169095cbbe603d00e0063) )
ROM_END

// same machine as above? is one of these bad?
ROM_START( mc_9x6sa )
	ROM_REGION( 0x200000, "mainrom", 0 )
	ROM_LOAD( "999999-in-1, 8 bit slim station, newpxp-dvt22-a pcb.bin", 0x00000, 0x200000, CRC(6a47c6a0) SHA1(b4dd376167a57dbee3dea70eb16f1a38e16bcdaa) )
ROM_END

ROM_START( fapocket )
	ROM_REGION( 0x4000000, "mainrom", 0 )
	ROM_LOAD( "s29gl512n.bin", 0x00000, 0x4000000, CRC(37d0fb06) SHA1(0146a2fae32e23b65d4032c508f0d12cedd399c3) )
ROM_END

void nes_vt42xx_state::init_rfcp168()
{
	uint8_t *romdata = memregion("mainrom")->base();
	for (offs_t i = 0; i < 0x1000000; i += 0x10000)
	{
		// Swap A12 with A13 and A14 with A15
		std::swap_ranges(&romdata[i + 0x1000], &romdata[i + 0x2000], &romdata[i + 0x2000]);
		std::swap_ranges(&romdata[i + 0x4000], &romdata[i + 0x5000], &romdata[i + 0x8000]);
		std::swap_ranges(&romdata[i + 0x5000], &romdata[i + 0x6000], &romdata[i + 0xa000]);
		std::swap_ranges(&romdata[i + 0x6000], &romdata[i + 0x7000], &romdata[i + 0x9000]);
		std::swap_ranges(&romdata[i + 0x7000], &romdata[i + 0x8000], &romdata[i + 0xb000]);
		std::swap_ranges(&romdata[i + 0xd000], &romdata[i + 0xe000], &romdata[i + 0xe000]);
	}
}

void nes_vt42xx_state::init_g9_666()
{
	uint8_t *romdata = memregion("mainrom")->base();
	for (offs_t i = 0; i < 0x1000000; i += 2)
	{
		uint16_t w = get_u16le(&romdata[i]);
		put_u16le(&romdata[i], (w & 0xf9f9) | (w & 0x0600) >> 8 | (w & 0x0006) << 8);
	}
}

void nes_vt42xx_state::init_hhgc319()
{
	init_rfcp168();

	// Even more pairs of address and data lines to swap here...
	uint8_t *romdata = memregion("mainrom")->base();
	for (offs_t i = 0; i < 0x1000000; i += 0x800000)
	{
		std::swap_ranges(&romdata[i + 0x080000], &romdata[i + 0x100000], &romdata[i + 0x400000]);
		std::swap_ranges(&romdata[i + 0x180000], &romdata[i + 0x200000], &romdata[i + 0x500000]);
		std::swap_ranges(&romdata[i + 0x280000], &romdata[i + 0x300000], &romdata[i + 0x600000]);
		std::swap_ranges(&romdata[i + 0x380000], &romdata[i + 0x400000], &romdata[i + 0x700000]);
	}
	for (offs_t i = 0; i < 0x1000000; i += 0x800)
		std::swap_ranges(&romdata[i + 0x200], &romdata[i + 0x400], &romdata[i + 0x400]);
	for (offs_t i = 0; i < 0x1000000; i += 0x20)
		std::swap_ranges(&romdata[i + 0x08], &romdata[i + 0x10], &romdata[i + 0x10]);
	for (offs_t i = 0; i < 0x1000000; i += 2)
		put_u16le(&romdata[i], bitswap<16>(get_u16le(&romdata[i]), 15, 14, 6, 5, 3, 2, 9, 8, 7, 13, 12, 4, 11, 10, 1, 0));
}

void nes_vt42xx_state::init_bl339()
{
	init_rfcp168();

	// Even more pairs of address and data lines to swap here (but not the same lines as hhgc319)...
	uint8_t *romdata = memregion("mainrom")->base();
	for (offs_t i = 0; i < 0x1000000; i += 0x1000)
		std::swap_ranges(&romdata[i + 0x400], &romdata[i + 0x800], &romdata[i + 0x800]);
	for (offs_t i = 0; i < 0x1000000; i += 0x10)
		std::swap_ranges(&romdata[i + 0x04], &romdata[i + 0x08], &romdata[i + 0x08]);
	for (offs_t i = 0; i < 0x1000000; i += 2)
		put_u16le(&romdata[i], bitswap<16>(get_u16le(&romdata[i]), 15, 7, 13, 4, 3, 10, 2, 1, 14, 6, 5, 12, 11, 9, 8, 0));
}

} // anonymous namespace


CONS( 201?, rfcp168,  0,  0,  nes_vt42xx_16mb, nes_vt42xx, nes_vt42xx_state, init_rfcp168, "<unknown>", "Retro FC Plus 168 in 1 Handheld", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS ) // "RETRO_FC_V3.5"

// many duplicates, real game count to be confirmed, graphical issues in some games
CONS( 202?, g9_666,   0,  0,  nes_vt42xx_16mb, nes_vt42xx, nes_vt42xx_state, init_g9_666, "<unknown>", "G9 Game Box 666 Games", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )

// same bitswap as above
CONS( 201?, g5_500,   0,  0,  nes_vt42xx_16mb, nes_vt42xx, nes_vt42xx_state, init_g9_666, "<unknown>", "G5 500 in 1 Handheld", MACHINE_NOT_WORKING )

// highly scrambled
CONS( 201?, hhgc319,  0,  0,  nes_vt42xx_16mb, nes_vt42xx, nes_vt42xx_state, init_hhgc319, "<unknown>", "Handheld Game Console 319-in-1", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )

// very similar menus etc. to hhgc319, but claims 20 more games, different scramble
CONS( 201?, bl339,    0,  0,  nes_vt42xx_16mb, nes_vt42xx, nes_vt42xx_state, init_bl339, "BaoBaoLong", "Handheld Game Console 339-in-1", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )

// Runs well, only issues in SMB3 which crashes
CONS( 2017, bittboy,  0,  0,  nes_vt42xx_bitboy_2x16mb, nes_vt42xx, nes_vt42xx_bitboy_state, empty_init, "BittBoy",   "BittBoy Mini FC 300 in 1", MACHINE_IMPERFECT_GRAPHICS ) // has external banking (2x 16mbyte banks)

// No title screen, but press start and menu and games run fine. Makes odd
// memory accesses which probably explain broken title screen
CONS( 201?, mc_hh210, 0,  0,  nes_vt42xx_16mb, nes_vt42xx, nes_vt42xx_state, empty_init, "<unknown>", "Handheld 210 in 1", MACHINE_NOT_WORKING )

CONS( 201?, retro400, 0,  0,  nes_vt42xx_16mb, nes_vt42xx, nes_vt42xx_state, empty_init, "<unknown>", "Retro FC 400-in-1", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2019, gbox2019, 0,  0,  nes_vt42xx_16mb, nes_vt42xx, nes_vt42xx_state, empty_init, "Sup", "Game Box 400 in 1 (2019 PCB)", MACHINE_NOT_WORKING )

CONS( 200?, gprnrs1,  0,  0,  nes_vt42xx_8mb,  nes_vt42xx, nes_vt42xx_state, empty_init, "<unknown>", "Game Prince RS-1", MACHINE_IMPERFECT_GRAPHICS )
CONS( 200?, gprnrs16, 0,  0,  nes_vt42xx_gprnrs16_2x16mb, nes_vt42xx, nes_vt42xx_bitboy_state, empty_init, "<unknown>", "Game Prince RS-16", MACHINE_IMPERFECT_GRAPHICS )

CONS( 200?, mc_9x6ss, 0,        0, nes_vt42xx_4mb, nes_vt42xx, nes_vt42xx_state, empty_init, "<unknown>", "999999 in 1 (PXP2 Slim Station)", MACHINE_IMPERFECT_GRAPHICS )
CONS( 200?, mc_9x6sa, mc_9x6ss, 0, nes_vt42xx_2mb, nes_vt42xx, nes_vt42xx_state, empty_init, "<unknown>", "999999 in 1 (8 bit Slim Station, NEWPXP-DVT22-A PCB)", MACHINE_IMPERFECT_GRAPHICS )
CONS( 200?, mc_7x6ss, 0,        0, nes_vt42xx_1mb, nes_vt42xx, nes_vt42xx_state, empty_init, "<unknown>", "777777 in 1 (8 bit Slim Station, NEWPXP-DVT22-A PCB)", MACHINE_IMPERFECT_GRAPHICS )
CONS( 200?, mc_8x6ss, 0,        0, nes_vt42xx_1mb, nes_vt42xx, nes_vt42xx_state, empty_init, "<unknown>", "888888 in 1 (8 bit Slim Station, NEWPXP-DVT22-A PCB)", MACHINE_IMPERFECT_GRAPHICS )

// Uses DIP switch to select console or cartridge, as cartridge is fake and just toggles a GPIO
CONS( 2017, fapocket,  0,  0,  nes_vt42xx_fa,     nes_vt42xx_fa, nes_vt42xx_fapocket_state, empty_init, "<unknown>",   "Family Pocket 638 in 1", MACHINE_IMPERFECT_GRAPHICS ) // has external banking (4x 16mbyte banks)



newbrain.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder

/*

    NewBrain
    Grundy Business Systems Ltd.

    32K RAM
    28K ROM

    Z80 @ 2MHz
    COP420 @ 2MHz

    Z80 @ 4MHz (416): INT/NMI=+5V, WAIT=EXTBUSRQ|BUSAKD, RESET=_FDC RESET,
    NEC 765AC @ 4 MHz (418)
    MC6850 ACIA (459)
    Z80CTC (458)
    ADC0809 (427)
    DAC0808 (461)

    Models according to the Software Technical Manual:

    Model M: 'Page Register', Expansion Port onto Z80 bus, Video, ACIA/CTC, User Port
    Model A: Expansion Port, Video, no User Port but has software driver serial port - s/w Printer, s/w V24
    Model V: ACIA/CTC, User Port

*/

/*

    TODO:

    - COPINT @ 84Hz instead of 50Hz? (with clock 3240506 is 50Hz)
    - CLKINT arrives too late and COP reads the VFD data before CPU writes it
    - VFD does not receive data from main CPU
    - bitmapped video
    - accurate video timing
    - cassette motor control seems to have a COP cpu problem
    - EIM
    - floppy
    - CP/M 2.2 ROMs
    - localized ROM sets
    - Micropage ROM/RAM card
    - Z80 PIO board
    - peripheral (PI) box
    - sound card

*/

#include "emu.h"
#include "newbrain.h"
#include "speaker.h"
#include "screen.h"

#include "utf8.h"

#include "newbrain.lh"
#include "newbraina.lh"



//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

#define LOG_COP (1U << 1)
#define LOG_VFD (1U << 2)
#define VERBOSE 0
#include "logmacro.h"



//**************************************************************************
//  IMPLEMENTATION
//**************************************************************************

//-------------------------------------------------
//  check_interrupt -
//-------------------------------------------------

void newbrain_state::check_interrupt()
{
	int level = ((!m_clk && !m_clkint) || !m_copint) ? ASSERT_LINE : CLEAR_LINE;

	m_maincpu->set_input_line(INPUT_LINE_IRQ0, level);
}


//-------------------------------------------------
//  mreq_r - memory request read
//-------------------------------------------------

uint8_t newbrain_state::mreq_r(offs_t offset)
{
	bool romov = 1, raminh = 0;
	int exrm = 0;
	uint8_t data = m_exp->mreq_r(offset, 0xff, romov, exrm, raminh);

	int rom0 = 1, rom1 = 1, rom2 = 1;
	int a15_14_13 = romov ? (offset >> 13) : exrm;
	if (!m_pwrup) a15_14_13 = 7;
	int a15g = BIT(a15_14_13, 2);

	switch (a15_14_13)
	{
	case 5: rom0 = 0; break;
	case 6: rom1 = 0; break;
	case 7: rom2 = 0; break;
	}

	if (!a15g && !raminh)
	{
		data = m_ram->pointer()[offset];
	}

	if (!rom0)
	{
		data = m_rom->base()[0x0000 + (offset & 0x1fff)];
	}

	if (!rom1)
	{
		data = m_rom->base()[0x2000 + (offset & 0x1fff)];
	}

	if (!rom2)
	{
		data = m_rom->base()[0x4000 + (offset & 0x1fff)];
	}

	return data;
}


//-------------------------------------------------
//  mreq_w - memory request write
//-------------------------------------------------

void newbrain_state::mreq_w(offs_t offset, uint8_t data)
{
	bool romov = 1, raminh = 0;
	int exrm = 0;
	m_exp->mreq_w(offset, data, romov, exrm, raminh);

	int a15_14_13 = romov ? (offset >> 13) : exrm;
	if (!m_pwrup) a15_14_13 = 7;
	int a15g = BIT(a15_14_13, 2);

	if (!a15g && !raminh)
	{
		m_ram->pointer()[offset] = data;
	}
}


//-------------------------------------------------
//  iorq_r - I/O request read
//-------------------------------------------------

uint8_t newbrain_state::iorq_r(offs_t offset)
{
	bool prtov = 0;
	uint8_t data = m_exp->iorq_r(offset, 0xff, prtov);

	if (!prtov)
	{
		switch ((offset >> 2) & 0x07)
		{
		case 1: // EXP1
			switch (offset & 0x03)
			{
			case 0: // CLCLK
				clclk();
				break;

			case 2: // COP
				data = m_cop->microbus_r();
				break;
			}
			break;

		case 5: // UST
			if (BIT(offset, 1))
			{
				data = ust_b_r();
			}
			else
			{
				data = ust_a_r();
			}
			break;
		}
	}

	return data;
}


//-------------------------------------------------
//  iorq_w - I/O request write
//-------------------------------------------------

void newbrain_state::iorq_w(offs_t offset, uint8_t data)
{
	bool prtov = 0;
	m_exp->iorq_w(offset, 0xff, prtov);

	if (!prtov)
	{
		switch ((offset >> 2) & 0x07)
		{
		case 1: // EXP1
			switch (offset & 0x03)
			{
			case 0: // CLCLK
				clclk();
				break;

			case 2: // COP
				m_cop->microbus_w(data);
				break;

			case 3: // ENRG1
				enrg_w(data);
				break;
			}
			break;

		case 2: // TVL
			tvl(data, BIT(offset, 6));
			break;

		case 3: // TVTL
			tvtl_w(data);
			break;
		}
	}
}


//-------------------------------------------------
//  clclk - clear clock interrupt
//-------------------------------------------------

void newbrain_state::clclk()
{
	LOG("%s %s CLCLK\n", machine().time().as_string(), machine().describe_context());

	m_clkint = 1;
	check_interrupt();
}


//-------------------------------------------------
//  enrg_w -
//-------------------------------------------------

void newbrain_state::enrg_w(uint8_t data)
{
	/*

	    bit     signal

	    0       _CLK
	    1
	    2       TVP
	    3
	    4       _RTSD
	    5       DO
	    6
	    7       PO

	*/

	LOG("%s %s ENRG %02x\n", machine().time().as_string(), machine().describe_context(), data);

	// clock enable
	int clk = BIT(data, 0);

	if (m_clk != clk) {
		m_clk = clk;
		check_interrupt();
	}

	// TV enable
	m_tvp = BIT(data, 2);

	// V24
	m_rs232_v24->write_rts(BIT(data, 4));
	m_rs232_v24->write_txd(BIT(data, 5));

	// printer
	m_rs232_prn->write_txd(BIT(data, 7));
}


//-------------------------------------------------
//  ust_r -
//-------------------------------------------------

uint8_t newbrain_state::ust_a_r()
{
	/*

	    bit     signal

	    0       +5V
	    1       PWRUP
	    2
	    3
	    4
	    5       _CLKINT
	    6
	    7       _COPINT

	*/

	uint8_t data = 0x5d;

	// powered up
	data |= m_pwrup << 1;

	// interrupts
	data |= m_clkint << 5;
	data |= m_copint << 7;

	return data;
}


//-------------------------------------------------
//  user_r -
//-------------------------------------------------

uint8_t newbrain_state::ust_b_r()
{
	/*

	    bit     signal

	    0       RDDK
	    1       _CTSD
	    2
	    3
	    4
	    5       TPIN
	    6
	    7       _CTSP

	*/

	uint8_t data = 0x5c;

	// V24
	data |= m_rs232_v24->rxd_r();
	data |= m_rs232_v24->cts_r() << 1;

	// tape
	data |= tpin() << 5;

	// printer
	data |= m_rs232_prn->cts_r() << 7;

	return data;
}


//-------------------------------------------------
//  cop_in_r -
//-------------------------------------------------

uint8_t newbrain_state::cop_in_r()
{
	/*

	    bit     description

	    IN0     K8 (CD4076 Q2)
	    IN1     _RD
	    IN2     _COP
	    IN3     _WR

	*/

	uint8_t data = 0xe;

	// keyboard
	data |= BIT(m_403_q, 2);

	LOGMASKED(LOG_COP, "%s %s IN %01x\n", machine().time().as_string(), machine().describe_context(), data);

	return data;
}


//-------------------------------------------------
//  cop_g_r -
//-------------------------------------------------

uint8_t newbrain_state::cop_g_r()
{
	/*

	    bit     description

	    G0
	    G1      K9 (CD4076 Q1)
	    G2      K7 (CD4076 Q0)
	    G3      K3 (CD4076 Q3)

	*/

	uint8_t data = 0;

	// keyboard
	data |= BIT(m_403_q, 1) << 1;
	data |= BIT(m_403_q, 0) << 2;
	data |= BIT(m_403_q, 3) << 3;

	LOGMASKED(LOG_COP, "%s %s G %01x\n", machine().time().as_string(), machine().describe_context(), data);

	return data;
}


//-------------------------------------------------
//  cop_g_w -
//-------------------------------------------------

// m_cop_g1 and m_cop_g3, when activated, have 20 zeros and a 1 in a continuous sequence.
// m_cop_k6 randomly alternates between 0 and 1, spending more time at 1.
// The outcome is the cassette is unreadable.
// Therefore the motors are left permanently on until the above issues can be fixed.
void newbrain_state::tm()
{
//  cassette_state tm1 = (!m_cop_g1 && !m_cop_k6) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED;
//  cassette_state tm2 = (!m_cop_g3 && !m_cop_k6) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED;

//  m_cassette1->change_state(tm1, CASSETTE_MASK_MOTOR);
//  m_cassette2->change_state(tm2, CASSETTE_MASK_MOTOR);
}

void newbrain_state::cop_g_w(uint8_t data)
{
	/*

	    bit     description

	    G0      _COPINT
	    G1      _TM1
	    G2
	    G3      _TM2

	*/

	int copint = !BIT(data, 0);

	LOGMASKED(LOG_COP, "%s %s COPINT %u\n", machine().time().as_string(), machine().describe_context(), copint);

	if (m_copint != copint)
	{
		m_copint = copint;
		check_interrupt();
	}

	m_cop_g1 = BIT(data, 1);
	m_cop_g3 = BIT(data, 3);
	tm();
}


//-------------------------------------------------
//  cop_d_w -
//-------------------------------------------------

void newbrain_state::cop_d_w(uint8_t data)
{
	/*
	    bit     description

	    D0      inverted to K4 -> CD4024 pin 2 (reset)
	    D1      TDO
	    D2      inverted to K6 -> CD4024 pin 1 (clock), CD4076 pin 7 (clock), inverted to DS8881 pin 3 (enable)
	    D3      not connected

	*/

	int k4 = !BIT(data, 0);
	int k6 = !BIT(data, 2);

	LOGMASKED(LOG_COP, "%s %s K4 %u K6 %u\n", machine().time().as_string(), machine().describe_context(), k4, k6);

	m_cop_tdo = BIT(data, 1);
	m_cassette1->output(m_cop_tdo ? -1.0 : +1.0);
	m_cassette2->output(m_cop_tdo ? -1.0 : +1.0);

	if (k4) {
		m_405_q = 0;

		LOGMASKED(LOG_COP, "%s %s keylatch reset\n", machine().time().as_string(), machine().describe_context());
	} else if (m_cop_k6 && !k6) {
		m_405_q++;
		m_405_q &= 0x7f;

		LOGMASKED(LOG_COP, "%s %s keylatch %u\n", machine().time().as_string(), machine().describe_context(), m_405_q);
	}

	if (!m_cop_k6 && k6) {
		m_403_d = m_y[m_405_q & 0x0f]->read() & 0x0f;

		LOGMASKED(LOG_COP, "%s %s keydata %01x\n", machine().time().as_string(), machine().describe_context(), m_403_d);
	}

	if (k6) {
		m_403_q = m_403_d;
	} else {
		m_403_q = 0xf;

		LOGMASKED(LOG_COP, "%s %s keydata disabled\n", machine().time().as_string(), machine().describe_context());

		// COP to VFD serial format, bits 15..0
		// A B J I x H G2 C x F G1 E K L M D
		uint16_t value = bitswap<16>(m_402_q, 11, 7, 1, 13, 10, 3, 2, 12, 9, 5, 6, 4, 0, 8, 14, 15) & 0x3fff;
		m_digits[m_405_q & 0x0f] = value;

		LOGMASKED(LOG_VFD, "%s %s vfd segment %u 402.Q %04x data %04x\n", machine().time().as_string(), machine().describe_context(), m_405_q & 0x0f, m_402_q, value);
	}

	m_cop_k6 = k6;
	tm();
}


//-------------------------------------------------
//  k1_w -
//-------------------------------------------------

void newbrain_state::k1_w(int state)
{
	LOGMASKED(LOG_VFD, "%s %s SO %u\n", machine().time().as_string(), machine().describe_context(), state);

	m_cop_so = state;
}


//-------------------------------------------------
//  k2_w -
//-------------------------------------------------

void newbrain_state::k2_w(int state)
{
	LOGMASKED(LOG_VFD, "%s %s SK %u\n", machine().time().as_string(), machine().describe_context(), state);

	if (state)
	{
		m_402_q <<= 1;
		m_402_q = (m_402_q & 0xfffe) | m_cop_so;
	}
}


//-------------------------------------------------
//  tdi_r -
//-------------------------------------------------

int newbrain_state::tpin()
{
	return (m_cassette1->input() > +0.04) || (m_cassette2->input() > +0.04);
}

int newbrain_state::tdi_r()
{
	return tpin() ^ m_cop_tdo;
}



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( newbrain_mreq )
//-------------------------------------------------

void newbrain_state::newbrain_mreq(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(newbrain_state::mreq_r), FUNC(newbrain_state::mreq_w));
}


//-------------------------------------------------
//  ADDRESS_MAP( newbrain_iorq )
//-------------------------------------------------

void newbrain_state::newbrain_iorq(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(newbrain_state::iorq_r), FUNC(newbrain_state::iorq_w));
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( newbrain )
//-------------------------------------------------

static INPUT_PORTS_START( newbrain )
	PORT_START("Y0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("STOP") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("Y1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))

	PORT_START("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')

	PORT_START("Y3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')

	PORT_START("Y4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')

	PORT_START("Y5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('(') PORT_CHAR('[')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("Y6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(')') PORT_CHAR(']')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')

	PORT_START("Y7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('*') PORT_CHAR(U'£')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')

	PORT_START("Y8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("VIDEO TEXT") PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(RALT)) // Vd
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')

	PORT_START("Y9")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')

	PORT_START("Y10")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('@')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')

	PORT_START("Y11")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('-') PORT_CHAR('\\')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')

	PORT_START("Y12")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('+') PORT_CHAR('^')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("INSERT") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START("Y13")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("NEW LINE") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) // NL
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')

	PORT_START("Y14")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("HOME") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME)) // CH

	PORT_START("Y15")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1) // SH
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("GRAPHICS") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT)) // GR
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("REPEAT") // RPT
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CONTROL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) // GL
INPUT_PORTS_END



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

int newbrain_state::get_reset_t()
{
	return RES_K(220) * CAP_U(10) * 1000; // t = R128 * C125 = 2.2s
}

int newbrain_state::get_pwrup_t()
{
	return RES_K(560) * CAP_U(10) * 1000; // t = R129 * C127 = 5.6s
}


//-------------------------------------------------
//  machine_start -
//-------------------------------------------------

void newbrain_state::machine_start()
{
	m_digits.resolve();

	// initialize timers
	m_reset_timer = timer_alloc(FUNC(newbrain_state::clear_reset), this);
	m_power_timer = timer_alloc(FUNC(newbrain_state::power_on), this);

	m_reset_timer->adjust(attotime::never);
	m_power_timer->adjust(attotime::from_usec(get_pwrup_t()));

	// state saving
	save_item(NAME(m_clk));
	save_item(NAME(m_tvp));
	save_item(NAME(m_pwrup));
	save_item(NAME(m_clkint));
	save_item(NAME(m_copint));
	save_item(NAME(m_cop_so));
	save_item(NAME(m_cop_tdo));
	save_item(NAME(m_cop_g1));
	save_item(NAME(m_cop_g3));
	save_item(NAME(m_cop_k6));
	save_item(NAME(m_405_q));
	save_item(NAME(m_403_q));
	save_item(NAME(m_402_q));

	// patch COP ROM to read VFD data
	//memregion(COP420_TAG)->base()[0x20a] = 0xc6;
}


//-------------------------------------------------
//  machine_reset -
//-------------------------------------------------

void newbrain_state::machine_reset()
{
	m_maincpu->reset();
	m_cop->reset();

	m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
	m_cop->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);

	enrg_w(0);

	m_reset_timer->adjust(attotime::from_usec(get_reset_t()));
}


//-------------------------------------------------
//  timer events
//-------------------------------------------------

TIMER_CALLBACK_MEMBER(newbrain_state::clear_reset)
{
	LOG("%s %s RESET 1\n", machine().time().as_string(), machine().describe_context());
	m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
	m_cop->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
}

TIMER_CALLBACK_MEMBER(newbrain_state::power_on)
{
	LOG("%s %s PWRUP 1\n", machine().time().as_string(), machine().describe_context());
	m_pwrup = 1;
}

TIMER_CALLBACK_MEMBER(newbrain_state::clear_clkint)
{
	LOG("%s CLKINT\n", machine().time().as_string());
	m_clkint = 0;
	check_interrupt();
}



//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

//-------------------------------------------------
//  machine_config( newbrain )
//-------------------------------------------------

void newbrain_state::newbrain(machine_config &config)
{
	// basic system hardware
	Z80(config, m_maincpu, XTAL(16'000'000)/4);
	m_maincpu->set_addrmap(AS_PROGRAM, &newbrain_state::newbrain_mreq);
	m_maincpu->set_addrmap(AS_IO, &newbrain_state::newbrain_iorq);

	COP420(config, m_cop, XTAL(16'000'000)/4);
	m_cop->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, true);
	m_cop->read_g().set(FUNC(newbrain_state::cop_g_r));
	m_cop->write_g().set(FUNC(newbrain_state::cop_g_w));
	m_cop->write_d().set(FUNC(newbrain_state::cop_d_w));
	m_cop->read_in().set(FUNC(newbrain_state::cop_in_r));
	m_cop->write_so().set(FUNC(newbrain_state::k1_w));
	m_cop->write_sk().set(FUNC(newbrain_state::k2_w));
	m_cop->read_si().set(FUNC(newbrain_state::tdi_r));

	// video hardware
	newbrain_video(config);

	// devices
	NEWBRAIN_EXPANSION_SLOT(config, m_exp, XTAL(16'000'000)/4, newbrain_expansion_cards, "eim");

	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cassette1);
	m_cassette1->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette1->add_route(ALL_OUTPUTS, "mono", 0.05);

	CASSETTE(config, m_cassette2);
	m_cassette2->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette2->add_route(ALL_OUTPUTS, "mono", 0.05);

	RS232_PORT(config, RS232_V24_TAG, default_rs232_devices, nullptr);
	RS232_PORT(config, RS232_PRN_TAG, default_rs232_devices, nullptr);

	// internal ram
	RAM(config, RAM_TAG).set_default_size("32K");
}


//-------------------------------------------------
//  machine_config( newbrain_ad )
//-------------------------------------------------

void newbrain_state::newbrain_ad(machine_config &config)
{
	newbrain(config);
	config.set_default_layout(layout_newbrain);
}


//-------------------------------------------------
//  machine_config( newbrain_a )
//-------------------------------------------------

void newbrain_state::newbrain_a(machine_config &config)
{
	newbrain(config);
	config.set_default_layout(layout_newbraina);
}


//-------------------------------------------------
//  machine_config( newbrain_md )
//-------------------------------------------------

void newbrain_state::newbrain_md(machine_config &config)
{
	newbrain(config);
	config.set_default_layout(layout_newbrain);
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( newbrain )
//-------------------------------------------------

ROM_START( newbrain )
	ROM_REGION( 0x6000, Z80_TAG, 0 )
	ROM_DEFAULT_BIOS( "rom20" )

	ROM_SYSTEM_BIOS( 0, "issue1", "Issue 1 (v?)" )
	ROMX_LOAD( "aben.ic6",     0x0000, 0x2000, CRC(308f1f72) SHA1(a6fd9945a3dca47636887da2125fde3f9b1d4e25), ROM_BIOS(0) )
	ROMX_LOAD( "cd iss 1.ic7", 0x2000, 0x2000, CRC(6b4d9429) SHA1(ef688be4e75aced61f487c928258c8932a0ae00a), ROM_BIOS(0) )
	ROMX_LOAD( "ef iss 1.ic8", 0x4000, 0x2000, CRC(20dd0b49) SHA1(74b517ca223cefb588e9f49e72ff2d4f1627efc6), ROM_BIOS(0) )

	ROM_SYSTEM_BIOS( 1, "issue2", "Issue 2 (v1.9)" )
	ROMX_LOAD( "aben19.ic6",   0x0000, 0x2000, CRC(d0283eb1) SHA1(351d248e69a77fa552c2584049006911fb381ff0), ROM_BIOS(1) )
	ROMX_LOAD( "cdi2.ic7",     0x2000, 0x2000, CRC(6b4d9429) SHA1(ef688be4e75aced61f487c928258c8932a0ae00a), ROM_BIOS(1) )
	ROMX_LOAD( "ef iss 1.ic8", 0x4000, 0x2000, CRC(20dd0b49) SHA1(74b517ca223cefb588e9f49e72ff2d4f1627efc6), ROM_BIOS(1) )

	ROM_SYSTEM_BIOS( 2, "issue3", "Issue 3 (v1.91)" )
	ROMX_LOAD( "aben191.ic6",  0x0000, 0x2000, CRC(b7be8d89) SHA1(cce8d0ae7aa40245907ea38b7956c62d039d45b7), ROM_BIOS(2) )
	ROMX_LOAD( "cdi3.ic7",     0x2000, 0x2000, CRC(6b4d9429) SHA1(ef688be4e75aced61f487c928258c8932a0ae00a), ROM_BIOS(2) )
	ROMX_LOAD( "ef iss 1.ic8", 0x4000, 0x2000, CRC(20dd0b49) SHA1(74b517ca223cefb588e9f49e72ff2d4f1627efc6), ROM_BIOS(2) )

	ROM_SYSTEM_BIOS( 3, "series2", "Series 2 (v?)" )
	ROMX_LOAD( "abs2.ic6",     0x0000, 0x2000, CRC(9a042acb) SHA1(80d83a2ea3089504aa68b6cf978d80d296cd9bda), ROM_BIOS(3) )
	ROMX_LOAD( "cds2.ic7",     0x2000, 0x2000, CRC(6b4d9429) SHA1(ef688be4e75aced61f487c928258c8932a0ae00a), ROM_BIOS(3) )
	ROMX_LOAD( "efs2.ic8",     0x4000, 0x2000, CRC(b222d798) SHA1(c0c816b4d4135b762f2c5f1b24209d0096f22e56), ROM_BIOS(3) )

	ROM_SYSTEM_BIOS( 4, "rom20", "? (v2.0)" )
	ROMX_LOAD( "aben20.rom",   0x0000, 0x2000, CRC(3d76d0c8) SHA1(753b4530a518ad832e4b81c4e5430355ba3f62e0), ROM_BIOS(4) )
	ROMX_LOAD( "cd20tci.rom",  0x2000, 0x4000, CRC(f65b2350) SHA1(1ada7fbf207809537ec1ffb69808524300622ada), ROM_BIOS(4) )

	ROM_REGION( 0x400, COP420_TAG, 0 )
	ROM_LOAD( "cop420-guw.ic419", 0x000, 0x400, CRC(a1388ee7) SHA1(5822e16aa794545600bf7a9dbee2ef467ca2a3e0) ) // COP420-GUW/N

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "char eprom iss 1.ic453", 0x0000, 0x1000, CRC(6a38b7a2) SHA1(29f3e672fc41792ac2f2b405e571d79235193561) ) // 8248R7
ROM_END


//-------------------------------------------------
//  ROM( newbraina )
//-------------------------------------------------

#define rom_newbraina rom_newbrain


//-------------------------------------------------
//  ROM( newbrainmd )
//-------------------------------------------------

ROM_START( newbrainmd )
	ROM_REGION( 0x10000, Z80_TAG, 0 )
	ROM_LOAD( "cdmd.rom", 0x2000, 0x2000, CRC(6b4d9429) SHA1(ef688be4e75aced61f487c928258c8932a0ae00a) )
	ROM_LOAD( "efmd.rom", 0x4000, 0x2000, CRC(20dd0b49) SHA1(74b517ca223cefb588e9f49e72ff2d4f1627efc6) )

	ROM_REGION( 0x400, COP420_TAG, 0 )
	ROM_LOAD( "cop420-guw.ic419", 0x000, 0x400, CRC(a1388ee7) SHA1(5822e16aa794545600bf7a9dbee2ef467ca2a3e0) ) // COP420-GUW/N

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "char eprom iss 1.ic453", 0x0000, 0x1000, CRC(6a38b7a2) SHA1(29f3e672fc41792ac2f2b405e571d79235193561) ) // 8248R7
ROM_END



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME        PARENT    COMPAT  MACHINE      INPUT     CLASS           INIT        COMPANY                        FULLNAME       FLAGS
COMP( 1981, newbrain,   0,        0,      newbrain_ad, newbrain, newbrain_state, empty_init, "Grundy Business Systems Ltd", "NewBrain AD", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND )
COMP( 1981, newbraina,  newbrain, 0,      newbrain_a,  newbrain, newbrain_state, empty_init, "Grundy Business Systems Ltd", "NewBrain A",  MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND )
COMP( 1981, newbrainmd, newbrain, 0,      newbrain_md, newbrain, newbrain_state, empty_init, "Grundy Business Systems Ltd", "NewBrain MD", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND )



news_38xx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay,Brice Onken

/*
 * Sony NEWS NWS-38xx workstations
 *
 * The NWS-3800 series was the last model of NEWS workstation to use the original NWS-8xx/9xx architecture of
 * having a CPU running NEWS-OS (handling the processing of user code) and a CPU running a separate OS (iopboot/rtx/mrx)
 * to handle system I/O. Unlike the 8xx/9xx and 18xx/19xx, the NWS-3800 is a mixed architecture system. It uses a MIPS
 * R3000 CPU for NEWS-OS and user programs, and a 68030 running rtx (NEWS-OS 3) or mrx (NEWS-OS 4) as the IOP.
 *
 * The motherboard designator for the 3800 series is MPU-10.
 *
 * Supported:
 *  - NWS-3860
 *
 * Known NWS-3800 configurations
 *  - NWS-3840: Apr 1990, 20MHz CPU/IOP, 16MB RAM (expandable to 80MB), 286MB HDD, without tape drive
 *  - NWS-3860: Dec 1989, 20MHz CPU/IOP, 16MB RAM (expandable to 80MB), 640MB HDD, with tape drive
 *  - NWS-3865: Aug 1991, 25MHz CPU/IOP, 16MB RAM (expandable to 80MB), 640MB HDD (Type E) or 1.25GB HDD (Type G), with tape drive
 *  - NWS-3870: Jan 1991, 25MHz CPU/IOP, 64MB RAM (expandable to 128MB), 1.25GB HDD, with tape drive
 *  - NWS-3880: Feb 1992, 25MHz CPU/IOP, 64MB RAM (expandable to ?), 2.0GB HDD, tape drive unknown
 *
 * Sources:
 *  - NWS-3840/3860 Service Guide
 *  - http://bitsavers.org/pdf/sony/news/Sony_NEWS_Technical_Manual_3ed_199103.pdf
 *  - https://katsu.watanabe.name/doc/sonynews/model.html
 *
 * TODO:
 *   - slots (I/O and UBUS expansion slots)
 *   - graphics
 *   - sound
 *   - AST
 *   - Something in the format/install flow is iffy: you have to manually write the disklabel before the installer works
 *     I do not have the NEWS-OS 4.1R install manual, so I don't know if I am doing something wrong or if the emulation
 *     is missing something. The format program does seem to spin for a bit on reading the defect information after
 *     running FORMAT UNIT.
 */

#include "emu.h"

#include "cpu/mips/mips1.h"
#include "cpu/m68000/m68030.h"

// memory
#include "machine/ram.h"

// various hardware
#include "machine/msm6242.h"
#include "machine/z80scc.h"
#include "machine/am79c90.h"
#include "machine/upd765.h"
#include "dmac_0266.h"
#include "news_hid.h"
#include "machine/ncr5380.h"

// video
#include "screen.h"

// busses and connectors
#include "machine/nscsi_bus.h"
#include "bus/centronics/ctronics.h"
#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"
#include "bus/nscsi/tape.h"
#include "bus/rs232/rs232.h"

#include "imagedev/floppy.h"

#define LOG_INTERRUPT (1U << 1)
#define LOG_TIMER (1U << 2)
#define LOG_TAS (1U << 3)
#define LOG_IOP (1U << 4)
#define LOG_CPU (1U << 5)
#define LOG_PARALLEL (1U << 6)

#include "logmacro.h"


namespace {

using namespace std::literals::string_view_literals;

// Logging constants
constexpr auto ENABLED = "enabled";
constexpr auto DISABLED = "disabled";

class news_38xx_state : public driver_device
{
public:
	static constexpr feature_type unemulated_features() { return feature::GRAPHICS; }

	news_38xx_state(machine_config const &mconfig, device_type type, char const *tag) :
		driver_device(mconfig, type, tag),
		m_cpu(*this, "cpu"),
		m_iop(*this, "iop"),
		m_ram(*this, "ram"),
		m_dma(*this, "dma%u", 0U),
		m_rtc(*this, "rtc"),
		m_scc(*this, "scc"),
		m_net(*this, "net"),
		m_fdc(*this, "fdc"),
		m_hid(*this, "hid"),
		m_scsi(*this, "scsi%u:7:cxd1180", 0U),
		m_serial(*this, "serial%u", 0U),
		m_scsibus(*this, "scsi%u", 0U),
		m_parallel(*this, "parallel"),
		m_parallel_data(*this, "parallel_data"),
		m_eprom(*this, "eprom"),
		m_led(*this, "led%u", 0U)
	{
	}

	void nws3860(machine_config &config) ATTR_COLD;

	void init_common() ATTR_COLD;

protected:
	// Not all IRQs feed into the status register directly
	// Because we have to handle IRQ data being stored everywhere and/or apply filtering to the status register,
	// apply the same strategy used in news_68k_iop, which also had to deal with this problem.
	enum class iop_irq : unsigned
	{
		AST,
		SOFTINTR,
		FDCIRQ,
		PARALLEL,
		SLOT,
		LANCE,
		SCSI0,
		SCSI1,
		CPU,
		UBUS,
		SCC,
		KEYBOARD,
		MOUSE,
		TIMER,
		FDCDRQ,
		PERR
	};

	static constexpr std::array IOP_IRQ_NAMES = {
		"AST"sv, "SOFTINTR"sv, "FDCIRQ"sv, "PARALLEL"sv, "SLOT"sv, "LANCE"sv, "SCSI0"sv, "SCSI1"sv, "CPU"sv, "UBUS"sv,
		"SCC"sv, "KEYBOARD"sv, "MOUSE"sv, "TIMER"sv, "FDCDRQ"sv, "PERR"sv
	};
	static constexpr u32 IOP_NMI_MASK = 1 << u32(iop_irq::FDCDRQ) |
										1 << u32(iop_irq::SCC) |
										1 << u32(iop_irq::SCSI0) |
										1 << u32(iop_irq::SCSI1) |
										1 << u32(iop_irq::LANCE) |
										1 << u32(iop_irq::FDCIRQ) |
										1 << u32(iop_irq::SLOT) |
										1 << u32(iop_irq::SOFTINTR) |
										1 << u32(iop_irq::AST);
	static constexpr std::array IOP_LINE_MASK = {
		std::make_pair(INPUT_LINE_IRQ1, 1 << u32(iop_irq::AST)),
		std::make_pair(INPUT_LINE_IRQ2, 1 << u32(iop_irq::SOFTINTR)),
		std::make_pair(INPUT_LINE_IRQ3, 1 << u32(iop_irq::FDCIRQ) | 1 << u32(iop_irq::PARALLEL) | 1 << u32(iop_irq::SLOT)),
		std::make_pair(INPUT_LINE_IRQ4, 1 << u32(iop_irq::LANCE) | 1 << u32(iop_irq::SCSI0) | 1 << u32(iop_irq::SCSI1) | 1 << u32(iop_irq::CPU) | 1 << u32(iop_irq::UBUS)),
		std::make_pair(INPUT_LINE_IRQ5, 1 << u32(iop_irq::SCC) | 1 << u32(iop_irq::KEYBOARD) | 1 << u32(iop_irq::MOUSE)),
		std::make_pair(INPUT_LINE_IRQ6, 1 << u32(iop_irq::TIMER)),
		std::make_pair(INPUT_LINE_IRQ7, 1 << u32(iop_irq::FDCDRQ) | 1 << u32(iop_irq::PERR))};

	enum class cpu_irq : unsigned
	{
		UBUS = 0, // Interprocessor communication interrupt from UPU
		IOP = 1, // Interprocessor communication interrupt from IOP
		TIMER = 2, // 100Hz timer
		FPA [[maybe_unused]] = 3, // FPU interrupt (handled by set_fpu)
		WRBERR = 4, // async bus write error
		PERR = 5 // Main memory parity error
	};

	static constexpr std::array CPU_IRQ_NAMES = {"UBUS"sv, "IOP"sv, "TIMER"sv, "FPA"sv, "WRBERR"sv, "PERR"sv};
	static constexpr u32 CPU_NMI_MASK = 1 << u32(cpu_irq::WRBERR) | 1 << u32(cpu_irq::IOP);
	static constexpr std::array CPU_IRQ_LINES = {
		std::make_pair(INPUT_LINE_IRQ0, cpu_irq::UBUS),
		std::make_pair(INPUT_LINE_IRQ1, cpu_irq::IOP),
		std::make_pair(INPUT_LINE_IRQ2, cpu_irq::TIMER),
		std::make_pair(INPUT_LINE_IRQ4, cpu_irq::WRBERR),
		std::make_pair(INPUT_LINE_IRQ5, cpu_irq::PERR)};

	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// address maps
	void cpu_map(address_map &map) ATTR_COLD;
	void iop_map(address_map &map) ATTR_COLD;
	void iop_vector_map(address_map &map) ATTR_COLD;

	// machine config
	void common(machine_config &config) ATTR_COLD;

	template<iop_irq Number>
	void irq_w(u8 state);
	template<iop_irq Number>
	void inten_w(u8 state);
	template<iop_irq Number>
	bool is_irq_set();
	void int_check_iop();

	template<cpu_irq Number>
	void irq_w(u8 state);
	template<cpu_irq Number>
	void inten_w(u8 state);
	template<cpu_irq Number>
	bool is_irq_set();
	void int_check_cpu();

	// Hardware-enforced test-and-set access to RAM
	u32 ram_tas_r(offs_t offset, u32 mem_mask);
	void ram_tas_w(offs_t offset, u32 data, u32 mem_mask);

	// IOP platform hardware
	u8 iop_intst_r();
	u8 iop_ipc_intst_r();
	u8 park_status_r();
	u32 iop_bus_error_r();
	void poweron_w(u8 data);
	void romdis_w(u8 data);
	void iop_parity_check_enable_w(u8 data);
	void iop_timer_w(u8 data);
	void ast_w(u8 data);
	void iopled_w(offs_t offset, u8 data);
	void xpustart_w(offs_t offset, u8 data);
	void index_divide_w(offs_t offset, u8 data);

	// CPU platform hardware
	u32 cpu_status_r();
	u32 cpu_bus_error_address_r();
	u8 boot_vector_r(offs_t offset);
	void cpu_parity_check_enable_w(u32 data);
	void cpu_timer_w(u32 data);
	void cpu_boot_vector_map_w(u32 data);
	void cpuled_w(offs_t offset, u32 data);
	void reset_cpu_registers();

	TIMER_CALLBACK_MEMBER(timer);

	// devices
	required_device<r3000a_device> m_cpu;
	required_device<m68030_device> m_iop;
	required_device<ram_device> m_ram;
	required_device_array<dmac_0266_device, 2> m_dma;
	required_device<rtc62421_device> m_rtc;
	required_device<z80scc_device> m_scc;
	required_device<am7990_device> m_net;
	required_device<n82077aa_device> m_fdc;

	required_device<news_hid_hle_device> m_hid;
	required_device_array<cxd1180_device, 2> m_scsi;

	required_device_array<rs232_port_device, 2> m_serial;
	required_device_array<nscsi_bus_device, 2> m_scsibus;
	required_device<centronics_device> m_parallel;
	optional_device<output_latch_device> m_parallel_data;

	required_region_ptr<u32> m_eprom;
	output_finder<4> m_led;
	std::unique_ptr<u16[]> m_net_ram;

	// For RAM test-and-set implementation
	memory_access<32, 2, 0, ENDIANNESS_BIG>::specific m_memory_access;

	emu_timer *m_timer = nullptr;

	// IOP hardware
	bool m_parallel_busy = false;
	bool m_parallel_fault = false;

	// CPU hardware
	bool m_mapvec = false;
	u32 m_wrbeadr = 0;

	// IOP IRQ state
	bool m_scc_irq_state = false;
	u32 m_iop_intst = 0;
	u32 m_iop_inten = 0;

	// CPU IRQ state
	u32 m_cpu_intst = 0;
	u32 m_cpu_inten = 0;
};

void news_38xx_state::machine_start()
{
	m_led.resolve();
	m_cpu->space(AS_PROGRAM).specific(m_memory_access);
	m_timer = timer_alloc(FUNC(news_38xx_state::timer), this);
	m_net_ram = std::make_unique<u16[]>(8192);

	save_pointer(NAME(m_net_ram), 8192);
	save_item(NAME(m_parallel_busy));
	save_item(NAME(m_parallel_fault));
	save_item(NAME(m_mapvec));
	save_item(NAME(m_wrbeadr));
	save_item(NAME(m_scc_irq_state));
	save_item(NAME(m_iop_intst));
	save_item(NAME(m_iop_inten));
	save_item(NAME(m_cpu_intst));
	save_item(NAME(m_cpu_inten));
}

void news_38xx_state::machine_reset()
{
	// For the IOP, EPROM is mapped at 0 after reset
	m_iop->space(0).install_rom(0x00000000, 0x0000ffff, m_eprom);

	// External timer is 100Hz
	m_timer->adjust(attotime::from_hz(100), 0, attotime::from_hz(100));

	// CPU does not run until the IOP tells it to
	m_cpu->set_input_line(INPUT_LINE_HALT, 1);

	// Clear platform hardware state
	m_scc_irq_state = false;
	m_iop_intst = 0;
	m_iop_inten = 0;
	reset_cpu_registers();
}

void news_38xx_state::init_common()
{
	// HACK: hardwire the rate
	m_fdc->set_rate(500'000);

	// RAM is always mapped for the CPU, since the MIPS boot vector is well above the max RAM value
	m_cpu->space(0).install_ram(0x00000000, m_ram->mask(), m_ram->pointer());
}

void news_38xx_state::cpu_map(address_map &map)
{
	map.global_mask(0x1fffffff); // A28-A0 are connected

	map(0x08000000, 0x0fffffff).rw(FUNC(news_38xx_state::ram_tas_r), FUNC(news_38xx_state::ram_tas_w));

	// 0x10000000 - 0x17ffffff UBUS

	// All registers below this line are 32 bit registers, LSB 1 = Set, 0 = Reset
	map(0x18000000, 0x18000003).r(FUNC(news_38xx_state::cpu_status_r));
	map(0x18000000, 0x18000003).w(FUNC(news_38xx_state::cpu_parity_check_enable_w)).mirror(0xffff00);
	map(0x18000004, 0x18000007).w(FUNC(news_38xx_state::cpu_timer_w)).mirror(0xffff00);
	map(0x18000020, 0x18000023).lw32(NAME([this] (u32) { irq_w<iop_irq::CPU>(1); })).mirror(0xffff00);
	map(0x18000024, 0x18000027).lw32(
		NAME([] (u32) { fatalerror("CPU tried to interrupt UBUS without UPU installed!"); })).mirror(0xffff00);
	map(0x18000040, 0x18000043).w(FUNC(news_38xx_state::cpu_boot_vector_map_w)).mirror(0xffff00);
	map(0x18000044, 0x18000047).w(FUNC(news_38xx_state::inten_w<cpu_irq::UBUS>)).mirror(0xffff00);
	map(0x18000060, 0x18000063).lw32(NAME([this] (u32) { irq_w<cpu_irq::IOP>(0); })).mirror(0xffff00);
	map(0x18000064, 0x18000067).lw32(NAME([this] (u32) { irq_w<cpu_irq::UBUS>(0); })).mirror(0xffff00);
	map(0x18000080, 0x18000087).w(FUNC(news_38xx_state::cpuled_w)).mirror(0xffff00);
	map(0x1c000000, 0x1c000003).r(FUNC(news_38xx_state::cpu_bus_error_address_r));
	map(0x1f000000, 0x1fffffff).r(FUNC(news_38xx_state::boot_vector_r)); // MAPVEC forces A28~24 to 0
}

void news_38xx_state::iop_map(address_map &map)
{
	map.global_mask(0x3fffffff); // A29-A0 are connected

	map(0x08000000, 0x0fffffff).rw(FUNC(news_38xx_state::ram_tas_r), FUNC(news_38xx_state::ram_tas_w));

	map(0x18000000, 0x1803ffff).ram(); // IOP ram
	map(0x20000000, 0x2000ffff).rom().region("eprom", 0).mirror(0x1fff0000);

	// Platform control hardware
	map(0x22000000, 0x22000000).w(FUNC(news_38xx_state::poweron_w));
	map(0x22000001, 0x22000001).w(FUNC(news_38xx_state::romdis_w));
	map(0x22000002, 0x22000002).w(FUNC(news_38xx_state::iop_parity_check_enable_w));
	map(0x22000003, 0x22000003).w(FUNC(news_38xx_state::iop_timer_w));
	map(0x22000004, 0x22000004).w(FUNC(news_38xx_state::irq_w<iop_irq::SOFTINTR>));
	map(0x22000005, 0x22000005).w(FUNC(news_38xx_state::ast_w));
	map(0x22000006, 0x22000007).w(FUNC(news_38xx_state::iopled_w));

	map(0x22800000, 0x22800001).w(FUNC(news_38xx_state::xpustart_w));
	map(0x22800008, 0x22800008).lw8(NAME([this] (u8) { irq_w<cpu_irq::IOP>(1); }));
	map(0x22800009, 0x22800009).
			lw8(NAME([] (u8) { fatalerror("IOP tried to interrupt UBUS without UPU installed!"); }));
	map(0x22800010, 0x22800010).w(FUNC(news_38xx_state::inten_w<iop_irq::CPU>));
	map(0x22800011, 0x22800011).w(FUNC(news_38xx_state::inten_w<iop_irq::UBUS>));
	map(0x22800018, 0x22800018).lw8(NAME([this] (u8) { irq_w<iop_irq::CPU>(0); }));
	map(0x22800019, 0x22800019).lw8(NAME([this] (u8) { irq_w<iop_irq::UBUS>(0); }));

	map(0x23000000, 0x23000000).r(FUNC(news_38xx_state::iop_intst_r));
	map(0x23800000, 0x23800000).r(FUNC(news_38xx_state::iop_ipc_intst_r));

	map(0x24000000, 0x24000007).m(m_fdc, FUNC(n82077aa_device::map));
	map(0x24000105, 0x24000105).rw(m_fdc, FUNC(n82077aa_device::dma_r), FUNC(n82077aa_device::dma_w));

	map(0x26040000, 0x26040000).lw8(NAME([this] (u8 data) {
		LOGMASKED(LOG_PARALLEL, "Parallel data w 0x%x\n", data);
		m_parallel_data->write(data);
		}));
	map(0x26040001, 0x26040001).lw8(NAME([this] (u8 data) {
		LOGMASKED(LOG_PARALLEL, "Parallel strobe w 0x%x\n", data);
		m_parallel->write_strobe(!data);
		}));
	map(0x26040002, 0x26040002).lw8(NAME([this] (u8) { irq_w<iop_irq::PARALLEL>(0);}));
	map(0x26040003, 0x26040003).w(FUNC(news_38xx_state::inten_w<iop_irq::PARALLEL>));

	map(0x26080000, 0x26080007).m(m_scsi[0], FUNC(cxd1180_device::map));
	map(0x260c0000, 0x260c0007).m(m_scsi[1], FUNC(cxd1180_device::map));

	map(0x26100000, 0x26100007).m(m_hid, FUNC(news_hid_hle_device::map_68k));
	map(0x26140000, 0x26140003).rw(m_scc, FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w));
	map(0x26180000, 0x2618000f).rw(m_rtc, FUNC(rtc62421_device::read), FUNC(rtc62421_device::write));

	// 0x261c0000 // kb beep frequency

	map(0x26200000, 0x26203fff).lrw16(
		[this](offs_t offset) { return m_net_ram[offset]; }, "net_ram_r",
		[this](offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_net_ram[offset]); }, "net_ram_w");

	// 0x262c0000 // audio

	map(0x26300000, 0x26300003).rw(m_net, FUNC(am7990_device::regs_r), FUNC(am7990_device::regs_w));

	map(0x26340000, 0x26340000).r(FUNC(news_38xx_state::park_status_r));
	map(0x26380000, 0x26380000).w(FUNC(news_38xx_state::index_divide_w));

	map(0x28000000, 0x28000017).m(m_dma[0], FUNC(dmac_0266_device::map));
	map(0x2a000000, 0x2a000017).m(m_dma[1], FUNC(dmac_0266_device::map));

	map(0x2c000000, 0x2c0000ff).rom().region("idrom", 0);
	map(0x2c000100, 0x2c000103).portr("SW1");

	map(0x2e000000, 0x2effffff).r(FUNC(news_38xx_state::iop_bus_error_r)).mirror(0x10000000); // Expansion I/O
}

void news_38xx_state::iop_vector_map(address_map &map)
{
	map(0xfffffff3, 0xfffffff3).lr8(NAME([] { return m68000_base_device::autovector(1); }));
	map(0xfffffff5, 0xfffffff5).lr8(NAME([] { return m68000_base_device::autovector(2); }));
	map(0xfffffff7, 0xfffffff7).lr8(NAME([] { return m68000_base_device::autovector(3); }));
	map(0xfffffff9, 0xfffffff9).lr8(NAME([] { return m68000_base_device::autovector(4); }));
	map(0xfffffffb, 0xfffffffb).lr8(
		NAME([this] { return m_scc_irq_state ? m_scc->m1_r() : m68000_base_device::autovector(5); }));
	map(0xfffffffd, 0xfffffffd).lr8(NAME([] { return m68000_base_device::autovector(6); }));
	map(0xffffffff, 0xffffffff).lr8(NAME([] { return m68000_base_device::autovector(7); }));
}

u32 news_38xx_state::ram_tas_r(offs_t offset, u32 mem_mask)
{
	if (mem_mask != 0xffffffff)
	{
		fatalerror("%s tried to acquire lock with unaligned memory access!", machine().describe_context());
	}

	constexpr u32 LOCK_VALUE = 0x80000000;
	const u32 current_value = m_memory_access.read_dword(offset << 2);
	if (machine().side_effects_disabled())
	{
		return current_value;
	}

	if (current_value < LOCK_VALUE)
	{
		LOGMASKED(LOG_TAS, "%s acquired lock at offset 0x%x\n", machine().describe_context(), offset << 2);
	}

	// TODO: what is the actual value written by the hardware?
	//       Both vmunix and mrx appear to use bltz (r3k)/bmi (030) instructions for lock spin
	m_memory_access.write_dword(offset << 2, LOCK_VALUE);
	return current_value;
}

void news_38xx_state::ram_tas_w(offs_t offset, u32 data, u32 mem_mask)
{
	if (data && !machine().side_effects_disabled())
	{
		fatalerror("lock released by %s with non-zero data!", machine().describe_context());
	}

	if (mem_mask != 0xffffffff)
	{
		fatalerror("%s tried to release lock with unaligned memory access!", machine().describe_context());
	}

	offset <<= 2;
	if (m_memory_access.read_dword(offset) != 0x80000000)
	{
		fatalerror("%s tried to release unheld lock!");
	}

	LOGMASKED(LOG_TAS, "%s releasing lock at offset 0x%x\n", machine().describe_context(), offset);
	m_memory_access.write_dword(offset, data);
}

u8 news_38xx_state::iop_intst_r()
{
	const u8 iop_status = is_irq_set<iop_irq::PERR>() << 7 |
						  is_irq_set<iop_irq::SCSI0>() << 6 |
						  is_irq_set<iop_irq::SCSI1>() << 5 |
						  (is_irq_set<iop_irq::CPU>() || is_irq_set<iop_irq::UBUS>()) << 4 |
						  is_irq_set<iop_irq::LANCE>() << 3 |
						  is_irq_set<iop_irq::FDCIRQ>() << 2 |
						  is_irq_set<iop_irq::PARALLEL>() << 1 |
						  is_irq_set<iop_irq::SLOT>();
	LOGMASKED(LOG_INTERRUPT, "%s iop_intst_r: 0x%x\n", machine().describe_context(), iop_status);
	return iop_status;
}

u8 news_38xx_state::iop_ipc_intst_r()
{
	const u8 iop_status = is_irq_set<iop_irq::UBUS>() << 1 | is_irq_set<iop_irq::CPU>();
	LOGMASKED(LOG_INTERRUPT, "%s iop_ipc_intst_r: 0x%x\n", machine().describe_context(), iop_status);
	return iop_status;
}

u8 news_38xx_state::park_status_r()
{
	const u8 park_status = (m_parallel_fault ? 0x40 : 0x0) |
						   (m_serial[0]->dsr_r() ? 0x20 : 0x0) |
						   (m_parallel_busy ? 0x10 : 0x0) |
						   (!is_irq_set<iop_irq::PARALLEL>() ? 0x8 : 0x0) |
						   (m_serial[0]->ri_r() ? 0x4 : 0x0) |
						   (m_serial[1]->dsr_r() ? 0x2 : 0x0) |
						   (m_serial[1]->ri_r() ? 0x1 : 0x0);
	LOGMASKED(LOG_INTERRUPT, "%s park_status_r: 0x%x\n", machine().describe_context(), park_status);
	return park_status;
}

template<news_38xx_state::iop_irq Number>
void news_38xx_state::irq_w(const u8 state)
{
	if (Number != iop_irq::TIMER)
	{
		LOGMASKED(LOG_INTERRUPT, "(%s) IOP IRQ %s %s\n", machine().describe_context(),
				  IOP_IRQ_NAMES[(unsigned)Number], state ? "set" : "cleared");
	}
	else
	{
		LOGMASKED(LOG_TIMER, "(%s) IOP IRQ %s %s\n", machine().describe_context(),
				  IOP_IRQ_NAMES[(unsigned)Number], state ? "set" : "cleared");
	}

	if (state)
	{
		m_iop_intst |= 1U << u32(Number);
	}
	else
	{
		m_iop_intst &= ~(1U << u32(Number));
	}

	int_check_iop();
}

template<news_38xx_state::iop_irq Number>
void news_38xx_state::inten_w(const u8 state)
{
	if (Number != iop_irq::TIMER)
	{
		LOGMASKED(LOG_INTERRUPT, "(%s) IOP IRQ %s %s\n", machine().describe_context(),
				  IOP_IRQ_NAMES[(unsigned)Number], state ? ENABLED : DISABLED);
	}
	else
	{
		LOGMASKED(LOG_TIMER, "(%s) IOP IRQ %s %s\n", machine().describe_context(),
				  IOP_IRQ_NAMES[(unsigned)Number], state ? ENABLED : DISABLED);
	}

	if (state)
	{
		m_iop_inten |= 1 << u32(Number);
	}
	else
	{
		m_iop_inten &= ~(1 << u32(Number));
	}

	int_check_iop();
}

template<news_38xx_state::iop_irq Number>
bool news_38xx_state::is_irq_set()
{
	return BIT(m_iop_intst, u32(Number));
}

void news_38xx_state::int_check_iop()
{
	const u32 active_irq = m_iop_intst & (m_iop_inten | IOP_NMI_MASK);
	for (const auto &[input_line, line_mask]: IOP_LINE_MASK)
	{
		// Calculate state of input pin (logical OR of all attached inputs)
		const bool state = active_irq & line_mask;

		// Update input pin status if it has changed
		if (m_iop->input_line_state(input_line) != state)
		{
			if (input_line != INPUT_LINE_IRQ6)
			{
				LOGMASKED(LOG_INTERRUPT, "Setting IOP input line %d to %d\n", input_line, state ? 1 : 0);
			}

			m_iop->set_input_line(input_line, state ? 1 : 0);
		}
	}
}

template<news_38xx_state::cpu_irq Number>
void news_38xx_state::irq_w(const u8 state)
{
	if (Number != cpu_irq::TIMER)
	{
		LOGMASKED(LOG_INTERRUPT, "(%s) CPU IRQ %s %s\n", machine().describe_context(),
				  CPU_IRQ_NAMES[(unsigned)Number], state ? "set" : "cleared");
	}
	else
	{
		LOGMASKED(LOG_TIMER, "(%s) CPU IRQ %s %s\n", machine().describe_context(),
				  CPU_IRQ_NAMES[(unsigned)Number], state ? "set" : "cleared");
	}

	if (state)
	{
		m_cpu_intst |= 1U << u32(Number);
	}
	else
	{
		m_cpu_intst &= ~(1U << u32(Number));
	}

	int_check_cpu();
}

template<news_38xx_state::cpu_irq Number>
void news_38xx_state::inten_w(const u8 state)
{
	if (Number != cpu_irq::TIMER)
	{
		LOGMASKED(LOG_INTERRUPT, "(%s) CPU IRQ %s %s\n", machine().describe_context(),
				  CPU_IRQ_NAMES[(unsigned)Number], state ? ENABLED : DISABLED);
	}
	else
	{
		LOGMASKED(LOG_TIMER, "(%s) CPU IRQ %s %s\n", machine().describe_context(),
				  CPU_IRQ_NAMES[(unsigned)Number], state ? ENABLED : DISABLED);
	}

	if (state)
	{
		m_cpu_inten |= 1 << u32(Number);
	}
	else
	{
		m_cpu_inten &= ~(1 << u32(Number));
	}

	int_check_cpu();
}

template<news_38xx_state::cpu_irq Number>
bool news_38xx_state::is_irq_set()
{
	return BIT(m_cpu_intst, u32(Number));
}

void news_38xx_state::int_check_cpu()
{
	const u32 active_irq = m_cpu_intst & (m_cpu_inten | CPU_NMI_MASK);
	for (const auto &[input_line, irq_input] : CPU_IRQ_LINES)
	{
		// Update input pin status if it has changed
		const bool state = BIT(active_irq, u32(irq_input));
		if (m_cpu->input_line_state(input_line) != state)
		{
			if (input_line != INPUT_LINE_IRQ2)
			{
				LOGMASKED(LOG_INTERRUPT, "Setting CPU input line %d to %d\n", input_line, state ? 1 : 0);
			}

			m_cpu->set_input_line(input_line, state ? 1 : 0);
		}
	}
}

u32 news_38xx_state::iop_bus_error_r()
{
	if (!machine().side_effects_disabled())
		m_iop->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);

	return 0;
}

TIMER_CALLBACK_MEMBER(news_38xx_state::timer)
{
	// 100Hz clock from PARK is used to generate both IOP and CPU interrupts
	if (BIT(m_iop_inten, u32(iop_irq::TIMER)))
	{
		irq_w<iop_irq::TIMER>(1);
	}

	if (BIT(m_cpu_inten, u32(cpu_irq::TIMER)))
	{
		irq_w<cpu_irq::TIMER>(1);
	}
}

void news_38xx_state::poweron_w(u8 data)
{
	LOGMASKED(LOG_IOP, "(%s) Write POWERON = 0x%x\n", machine().describe_context(), data);

	if (!machine().side_effects_disabled() && !data)
	{
		machine().schedule_exit();
	}
}

void news_38xx_state::romdis_w(u8 data)
{
	LOGMASKED(LOG_IOP, "(%s) IOP ROMDIS = 0x%x\n", machine().describe_context(), data);

	if (data)
	{
		m_iop->space(0).install_ram(0x00000000, m_ram->mask(), m_ram->pointer());
	}
	else
	{
		m_iop->space(0).install_rom(0x00000000, 0x0000ffff, m_eprom);
	}
}

void news_38xx_state::iop_parity_check_enable_w(u8 data)
{
	LOGMASKED(LOG_IOP, "(%s) IOP parity checking %s\n", machine().describe_context(), data ? ENABLED : DISABLED);

	inten_w<iop_irq::PERR>(data > 0);
	if (!data)
	{
		irq_w<iop_irq::PERR>(0);
	}
}

void news_38xx_state::iop_timer_w(u8 data)
{
	LOGMASKED(LOG_TIMER, "(%s) IOP timer %s\n", machine().describe_context(), data ? ENABLED : DISABLED);

	inten_w<iop_irq::TIMER>(data > 0);
	if (!data)
	{
		irq_w<iop_irq::TIMER>(0);
	}
}

void news_38xx_state::ast_w(u8 data)
{
	LOGMASKED(LOG_IOP, "(%s) AST interrupt %s\n", machine().describe_context(), data ? ENABLED : DISABLED);
	// TODO: how to trigger AST IRQ when IOP enters user mode? No easy injection point like my experiments with NWS-831.
	//       mrx doesn't seem to use it, but that could be due to other issues.
}

void news_38xx_state::iopled_w(offs_t offset, u8 data)
{
	constexpr int IOP_LED_BASE = 2;
	m_led[IOP_LED_BASE + offset % 2] = data > 0;
}

void news_38xx_state::xpustart_w(offs_t offset, u8 data)
{
	LOGMASKED(LOG_IOP, "(%s) %s %cPU\n", machine().describe_context(), data ? "Starting" : "Stopping",
			  !offset ? 'C' : 'U');

	if (!offset)
	{
		m_cpu->set_input_line(INPUT_LINE_HALT, data ? 0 : 1);
		if (!data)
		{
			reset_cpu_registers();
		}
	}
	else
	{
		if (data) fatalerror("Tried to start UPU without UPU installed!");
	}
}

void news_38xx_state::index_divide_w(offs_t offset, u8 data)
{
	LOGMASKED(LOG_IOP, "(%s) Set divide index to %s\n", machine().describe_context(), data ? "divide" : "passthrough");
	// TODO: what to do with this?
	//       I think it divides INDX pulse by half (2x INDX pulses from the drive to make 1 go to the FDC controller)
}

u32 news_38xx_state::cpu_status_r()
{
	const u8 cpu_status = is_irq_set<cpu_irq::UBUS>() << 1 | is_irq_set<cpu_irq::IOP>();
	LOGMASKED(LOG_INTERRUPT, "%s read CPU status 0x%x\n", machine().describe_context(), cpu_status);
	return cpu_status;
}

u32 news_38xx_state::cpu_bus_error_address_r()
{
	LOGMASKED(LOG_CPU, "(%s) Read last bus error address = 0x%x\n", machine().describe_context(), m_wrbeadr);
	// This will need to be properly implemented if anything is added that can cause a CPU bus error
	return m_wrbeadr;
}

u8 news_38xx_state::boot_vector_r(offs_t offset)
{
	if (!m_mapvec)
	{
		return m_memory_access.read_byte(offset);
	}

	// In reality, this probably just causes a bus error, returns 0x0/0xff, or something like that
	fatalerror("CPU tried to read from boot vector space without boot vector mapping!");
}

void news_38xx_state::cpu_parity_check_enable_w(u32 data)
{
	LOGMASKED(LOG_CPU, "(%s) Write CPU parity check enable = 0x%x\n", machine().describe_context(), data);

	inten_w<cpu_irq::PERR>(data > 0);
	if (!data)
	{
		irq_w<cpu_irq::PERR>(0);
	}
}

void news_38xx_state::cpu_timer_w(u32 data)
{
	LOGMASKED(LOG_TIMER, "(%s) CPU timer %s\n", machine().describe_context(), data ? ENABLED : DISABLED);

	inten_w<cpu_irq::TIMER>(data > 0);
	if (!data)
	{
		irq_w<cpu_irq::TIMER>(0);
	}
}

void news_38xx_state::cpu_boot_vector_map_w(u32 data)
{
	LOGMASKED(LOG_CPU, "(%s) CPU boot vector mapping %s\n", machine().describe_context(), data ? DISABLED : ENABLED);
	m_mapvec = data > 0;
}

void news_38xx_state::cpuled_w(offs_t offset, u32 data)
{
	m_led[offset % 2] = data > 0;
}

void news_38xx_state::reset_cpu_registers()
{
	m_mapvec = false;
	m_wrbeadr = 0;
	m_cpu_intst = 0;
	m_cpu_inten = 0;
}

void news_scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
	device.option_add("cdrom", NSCSI_CDROM);
	device.option_add("tape", NSCSI_TAPE);
}

void news_38xx_state::common(machine_config &config)
{
	// 40MHz goes into the R3000 pkg, but is divided internally to 20MHz
	R3000A(config, m_cpu, 40_MHz_XTAL / 2, 65536, 65536);
	m_cpu->set_addrmap(AS_PROGRAM, &news_38xx_state::cpu_map);
	m_cpu->set_fpu(mips1_device_base::MIPS_R3010Av4, INPUT_LINE_IRQ3);

	M68030(config, m_iop, 40_MHz_XTAL / 2);
	m_iop->set_addrmap(AS_PROGRAM, &news_38xx_state::iop_map);
	m_iop->set_addrmap(m68000_base_device::AS_CPU_SPACE, &news_38xx_state::iop_vector_map);

	// For NWS3840/3860: 16M onboard
	// With NWB-109 expansion kit: 16M onboard + 16M (1Mbit DRAM chips) expansion = 32M total
	// With NWB-112 expansion kit: 16M onboard + 64M (4Mbit DRAM chips) expansion = 80M total
	RAM(config, m_ram);
	m_ram->set_default_size("16M");
	m_ram->set_extra_options("32M,80M");

	RTC62421(config, m_rtc, 32.768_kHz_XTAL);

	DMAC_0266(config, m_dma[0], 0);
	m_dma[0]->set_bus(m_iop, 0);

	DMAC_0266(config, m_dma[1], 0);
	m_dma[1]->set_bus(m_iop, 0);

	SCC85C30(config, m_scc, 4.9152_MHz_XTAL);
	m_scc->out_int_callback().set(
		[this](const int state) {
			m_scc_irq_state = (bool)state;
			irq_w<iop_irq::SCC>(state != 0);
		});

	// scc channel A
	RS232_PORT(config, m_serial[0], default_rs232_devices, "terminal");
	m_serial[0]->cts_handler().set(m_scc, FUNC(z80scc_device::ctsa_w));
	m_serial[0]->dcd_handler().set(m_scc, FUNC(z80scc_device::dcda_w));
	m_serial[0]->rxd_handler().set(m_scc, FUNC(z80scc_device::rxa_w));
	m_scc->out_rtsa_callback().set(m_serial[0], FUNC(rs232_port_device::write_rts));
	m_scc->out_txda_callback().set(m_serial[0], FUNC(rs232_port_device::write_txd));

	// scc channel B
	RS232_PORT(config, m_serial[1], default_rs232_devices, nullptr);
	m_serial[1]->cts_handler().set(m_scc, FUNC(z80scc_device::ctsb_w));
	m_serial[1]->dcd_handler().set(m_scc, FUNC(z80scc_device::dcdb_w));
	m_serial[1]->rxd_handler().set(m_scc, FUNC(z80scc_device::rxb_w));
	m_scc->out_rtsb_callback().set(m_serial[1], FUNC(rs232_port_device::write_rts));
	m_scc->out_txdb_callback().set(m_serial[1], FUNC(rs232_port_device::write_txd));

	AM7990(config, m_net, 20_MHz_XTAL / 2);
	m_net->intr_out().set(FUNC(news_38xx_state::irq_w<iop_irq::LANCE>)).invert();
	m_net->dma_in().set([this](const offs_t offset) { return m_net_ram[offset >> 1]; });
	m_net->dma_out().set([this](const offs_t offset, const u16 data, const u16 mem_mask) {
		COMBINE_DATA(&m_net_ram[offset >> 1]);
	});

	N82077AA(config, m_fdc, 24_MHz_XTAL, n82077aa_device::mode_t::PS2);
	m_fdc->intrq_wr_callback().set(FUNC(news_38xx_state::irq_w<iop_irq::FDCIRQ>));
	m_fdc->drq_wr_callback().set(FUNC(news_38xx_state::irq_w<iop_irq::FDCDRQ>));
	FLOPPY_CONNECTOR(config, "fdc:0", "35hd", FLOPPY_35_HD, true, floppy_image_device::default_pc_floppy_formats).
			enable_sound(false);

	// scsi bus 0 and devices
	NSCSI_BUS(config, m_scsibus[0]);
	NSCSI_CONNECTOR(config, "scsi0:0", news_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi0:1", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:2", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:3", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:4", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:5", news_scsi_devices, "tape");
	NSCSI_CONNECTOR(config, "scsi0:6", news_scsi_devices, nullptr);

	// scsi bus 0 host adapter
	NSCSI_CONNECTOR(config, "scsi0:7").option_set("cxd1180", CXD1180).machine_config(
		[this](device_t *device) {
			auto &adapter = downcast<cxd1180_device &>(*device);
			adapter.set_clock(20_MHz_XTAL / 2);

			adapter.irq_handler().set(*this, FUNC(news_38xx_state::irq_w<iop_irq::SCSI0>));
			adapter.irq_handler().append(m_dma[0], FUNC(dmac_0266_device::eop_w));
			adapter.drq_handler().set(m_dma[0], FUNC(dmac_0266_device::req_w));

			subdevice<dmac_0266_device>(":dma0")->dma_r_cb().set(adapter, FUNC(cxd1180_device::dma_r));
			subdevice<dmac_0266_device>(":dma0")->dma_w_cb().set(adapter, FUNC(cxd1180_device::dma_w));
		});

	// scsi bus 1 and devices
	NSCSI_BUS(config, m_scsibus[1]);
	NSCSI_CONNECTOR(config, "scsi1:0", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:1", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:2", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:3", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:4", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:5", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:6", news_scsi_devices, nullptr);

	// scsi bus 1 host adapter
	NSCSI_CONNECTOR(config, "scsi1:7").option_set("cxd1180", CXD1180).machine_config(
		[this](device_t *device) {
			auto &adapter = downcast<cxd1180_device &>(*device);
			adapter.set_clock(20_MHz_XTAL / 2);

			adapter.irq_handler().set(*this, FUNC(news_38xx_state::irq_w<iop_irq::SCSI1>));
			adapter.irq_handler().append(m_dma[1], FUNC(dmac_0266_device::eop_w));
			adapter.drq_handler().set(m_dma[1], FUNC(dmac_0266_device::req_w));

			subdevice<dmac_0266_device>(":dma1")->dma_r_cb().set(adapter, FUNC(cxd1180_device::dma_r));
			subdevice<dmac_0266_device>(":dma1")->dma_w_cb().set(adapter, FUNC(cxd1180_device::dma_w));
		});

	CENTRONICS(config, m_parallel, centronics_devices, nullptr);
	// Note: printing works, but I'm not sure how accurate triggering the IRQ on each edge is.
	// mrx properly checks the PARK status before taking action, but this might not be what real hw does
	m_parallel->busy_handler().set([this](const int status) {
		const bool new_status = status;
		if (m_parallel_busy != new_status)
		{
			LOGMASKED(LOG_PARALLEL, "Parallel busy changed to %s\n", new_status ? "H" : "L");
			m_parallel_busy = new_status;
			irq_w<iop_irq::PARALLEL>(1);
		}
	});
	m_parallel->fault_handler().set([this](const int status) {
		const bool new_status = status;
		if (m_parallel_fault != new_status)
		{
			LOGMASKED(LOG_PARALLEL, "Parallel fault changed to %s\n", new_status ? "H" : "L");
			m_parallel_fault = !new_status;
			irq_w<iop_irq::PARALLEL>(1);
		}
	});

	OUTPUT_LATCH(config, m_parallel_data);
	m_parallel->set_output_latch(*m_parallel_data);

	NEWS_HID_HLE(config, m_hid);
	m_hid->irq_out<news_hid_hle_device::KEYBOARD>().set(*this, FUNC(news_38xx_state::irq_w<iop_irq::KEYBOARD>));
	m_hid->irq_out<news_hid_hle_device::MOUSE>().set(*this, FUNC(news_38xx_state::irq_w<iop_irq::MOUSE>));

	SOFTWARE_LIST(config, "software_list").set_original("sony_news").set_filter("RISC,NWS3000");
}

void news_38xx_state::nws3860(machine_config &config)
{
	common(config);
}

ROM_START(nws3860)
	ROM_REGION32_BE(0x10000, "eprom", 0)
	ROM_SYSTEM_BIOS(0, "nws3860", "SONY NET WORK STATION MC68030 Monitor Release 1.6")
	ROMX_LOAD("mpu_10__ver.1.6__1990_sony.ic159", 0x00000, 0x10000, CRC(542e21b8) SHA1(631dca1f3446761973073f5c32c1a0aeba538c2c), ROM_BIOS(0))

	ROM_REGION32_BE(0x100, "idrom", 0)
	ROM_LOAD("idrom.bin", 0x000, 0x100, CRC(a2b9e968) SHA1(09c7d253c7ed0b368c49f4b60bfe5ca76acd7cc3) BAD_DUMP)
ROM_END

INPUT_PORTS_START(nws3860)
	PORT_START("SW1")
	PORT_DIPNAME(0x07000000, 0x07000000, "Display") PORT_DIPLOCATION("SW1:1,2,3")
	PORT_DIPSETTING(0x07000000, "Console")
	PORT_DIPSETTING(0x06000000, "NWB-512")
	PORT_DIPSETTING(0x03000000, "NWB-225A")
	PORT_DIPSETTING(0x00000000, "Autoselect")

	PORT_DIPNAME(0x08000000, 0x08000000, "Boot Device") PORT_DIPLOCATION("SW1:4")
	PORT_DIPSETTING(0x08000000, "SCSI")
	PORT_DIPSETTING(0x00000000, "Floppy")

	PORT_DIPNAME(0x10000000, 0x10000000, "Automatic Boot") PORT_DIPLOCATION("SW1:5")
	PORT_DIPSETTING(0x10000000, DEF_STR(Off))
	PORT_DIPSETTING(0x00000000, DEF_STR(On))

	PORT_DIPNAME(0x20000000, 0x20000000, "Diagnostic Mode") PORT_DIPLOCATION("SW1:6")
	PORT_DIPSETTING(0x20000000, DEF_STR(Off))
	PORT_DIPSETTING(0x00000000, DEF_STR(On))

	PORT_DIPNAME(0xc0000000, 0xc0000000, "Unused") PORT_DIPLOCATION("SW1:7,8")
	PORT_DIPSETTING(0xc0000000, DEF_STR(Off))
INPUT_PORTS_END

} // anonymous namespace


/*   YEAR  NAME     P  C  MACHINE  INPUT    CLASS            INIT         COMPANY  FULLNAME   FLAGS */
COMP(1989, nws3860, 0, 0, nws3860, nws3860, news_38xx_state, init_common, "Sony", "NWS-3860", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



news_68k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
   Sony NEWS M68K systems.

   Sources:
     - http://wiki.netbsd.org/ports/news68k/

   TODO:
     - mouse/keyboard
     - graphics/slots

   The Sony NEWS Portable Workstation NWS-1250 is a "laptop" (weight of more than 8 Kg) with a black-and-white LCD (1120×780),
   a keyboard, a 3.5″ floppy disk drive, a (SCSI) harddisk, and interfaces for mouse, audio (phones, line in, mic in), SCSI,
   Ethernet (AUI), serial (DB9), and parallel (proprietary).

   This is its main PCB layout:
   _____________________________________________________________________________________________________________________________________________
  |               __________   _____________________   _______  _______               ____________________________    _____                    |
  |              |MB834200A|  | EPROM AM27C1024    |  |HC257_| |HC257_|              |||||||||||||||||||||||||||||   |·····|                   |
  |              |         |  |                    |   _______  _______            __________       _________       _________                  |
  |              |_________|  |____________________|  |HC257_| |HC257_|           |         | Xtal | Sony   | Xtal | Sony   |  ___             |
  |                            _______                                            |HD64646FS|  19  |WSC-AIF2|  741 |CDX1123 | |  |<-74HC244A   |__
  |               __________  |74HC32A  __________   _______                      |_________|      |        |      |        | |__|              __|_
  |              |MB834200A|   _______ |Intel    |  ACT11004                     ___ ___           |________|      |________|                  |____|
__|              |         |  |ALS05A| |N82077   |   __             6 x 74F00J->|  | |  |   _________________________________________          __ |
|   ________     |_________|   _______ |         |  |-|                         |__| |__|   |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|         | | |
|  |74ACT139                  |74LS14| |_________|  |-| __________  __________   ___ ___    _________________________________________ DIPSx8->| | |
|   ________                                        |-||HM62256LFP |HM62256LFP  |  | |  |   |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|         |_| |
|  74ACT11244               ____________   ___      |-||_________| |_________|  |__| |__|   _________________________________________        ___  |
|   ________               | Sony      |  |  |      |-|                          ___ ___    |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|       T7705 |
|  74ACT11244              |WSC-MEMPAK |  |  |      |-|    ____________         |  | |  |   _________________________________________         __  |
|   ________               |9030EK712  |  |__|      |_|   | Sony      |         |__| |__|   |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_| Switch->__| |
|  74ACT11244       Xtal   |           | SG51KH           |WSC-LCMC   |               __    _________________________________________         __  |
|                 4915.2   |___________| 50 MHz           |9025EK442  |              |..|   |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_| Switch->__| |
|   ________                    ________                  |           |              |..|   _________________________________________         __  |
|  |74ACT139                   |ACT11244   ________       |___________|              |..|   |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_| Switch->__| |
|   ________                              |ACT11245                    :|            |..|   _________________________________________        ___  |
|  |ACT1124             ________________   ________                 :| :|            |..|   |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|      74AS00 |
|   ________           | MK48T02B-25   |  |ACT11245                 :| :|            |..|   _________________________________________             |
|  |AM27S21PC                                                                        |__|   |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|             |
|  _____________________________________       ________  ________  ________            ________   ________   ________   ________   ______________ |
|_|                                    |____  |ACT11353 |ACT11353 |ACT11020           |AC11004|  |AC11004|  |AC11004|  |AC11004|  |o o o o o o o ||
  |____________________________________|    |  ________  ________                ___    ___    ___    ___    ___    _________     ________        |
                                            | |ACT11353 |ACT11353               |  |   |  |   |  |   |  |   |  |   |Sony    |    |AC11004|        |
                                            |  ________  ________  ________    BCT245 BCT245 BCT245 BCT245 BCT245  |WSC-LANCE   __________        |
                                            | |ACT11353 |ACT11353 |ACT11027     |__|   |__|   |__|   |__|   |__|   |________| HM6264ALFP-12T      |
                                            |                                                                                   __________    ___ |
                                            | _____________       _____________      _____________                            HM6264ALFP-12T |__<-SG51KH 32 MHz
                                            || Motorola   |      | Motorola   |     | Sony       |                  __________________________    |
                                            ||MC68882FN25A|      |XC68030FE25B|     |L7A0266     |                 | AMD                     |    |
                                            ||            |      |            |     |WSC-ICKDMAC |                 | AM7990PC                |    |
                                            ||            |      |            |     |9019        |                 |_________________________|    |
                                            ||____________|      |____________|     |____________|          ___________    ___________            |
                                            |  ___________   ________   ________                           |Sony      |   |AM7992BDC_|            |
                                            | | Zilog    |  |74HC374|  74HCT244A       ________   ________ |CXD1185Q  |    _________              |
                                            | |Z85C3008VSC                ________    |74ACT139  |ACT11002 |__________|   SG51K 20 MHz            |
                                            |                            |MC1489A|                                                                |
                                            |             ________        _____________________________________                                   |
                                            |            |MC145406       |::::::::::::::::::::::::::::::::::::|                                   |
                                            |  __________   __________   _______________________                 __________________               |
                                            |_|         |__|         |__|                      |________________|                 |_______________|
                                              |_________|  |_________|  |______________________|                |_________________|

 On the other side of the PCB there are a few components too:
  - 3 x HM62256LFP-12T
  - 1 x DS1000S-50

 NWS-1250 came bundled with a Sony mouse based on a Fujitsu MB88201H MCU (undumped mask ROM 512 x 8 bits).

 */

#include "emu.h"

#include "cpu/m68000/m68030.h"

// memory
#include "machine/ram.h"

// various hardware
#include "machine/timekpr.h"
#include "machine/z80scc.h"
#include "machine/am79c90.h"
#include "machine/upd765.h"
#include "dmac_0266.h"
#include "news_hid.h"
#include "machine/ncr5380.h"

// video
#include "screen.h"
#include "video/bt45x.h"

// busses and connectors
#include "machine/nscsi_bus.h"
#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"
#include "bus/rs232/rs232.h"

#include "machine/input_merger.h"
#include "imagedev/floppy.h"

#define VERBOSE 0
#include "logmacro.h"

#define GRAPHICS 0


namespace {

class news_68k_state : public driver_device
{
public:
	news_68k_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_ram(*this, "ram")
		, m_dma(*this, "dma")
		, m_rtc(*this, "rtc")
		, m_scc(*this, "scc")
		, m_net(*this, "net")
		, m_fdc(*this, "fdc")
		, m_scsi(*this, "scsi:7:cxd1180")
		, m_hid(*this, "hid")
		, m_serial(*this, "serial%u", 0U)
		, m_irq5(*this, "irq5")
		, m_irq7(*this, "irq7")
		, m_sw1(*this, "SW1")
		, m_eprom(*this, "eprom")
		, m_led(*this, "led%u", 0U)
#if GRAPHICS
		, m_screen(*this, "screen")
		, m_ramdac(*this, "ramdac")
		, m_vram(*this, "vram")
#endif
	{
	}

	void nws1580(machine_config &config);
	void init_common();

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// address maps
	void cpu_map(address_map &map) ATTR_COLD;
	void cpu_autovector_map(address_map &map) ATTR_COLD;

	// machine config
	void common(machine_config &config);

	enum irq_number : unsigned
	{
		IPIRQ1  = 0,
		IPIRQ3  = 1,
		LANCE   = 2,
		VME4    = 3,
		VME2    = 4,
		FDC     = 5,
		PRINTER = 6,
		SCSI    = 7,
	};
	template <irq_number Number> void irq_w(int state);
	void int_check();

	void timer(s32 param);
	void timer_w(u8 data);

	u32 bus_error_r();

#if GRAPHICS
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect) { return 0; }
#endif

private:
	// devices
	required_device<m68030_device> m_cpu;
	required_device<ram_device> m_ram;
	required_device<dmac_0266_device> m_dma;
	required_device<m48t02_device> m_rtc;
	required_device<z80scc_device> m_scc;
	required_device<am7990_device> m_net;
	required_device<upd72067_device> m_fdc;
	required_device<cxd1180_device> m_scsi;
	required_device<news_hid_hle_device> m_hid;

	required_device_array<rs232_port_device, 2> m_serial;

	required_device<input_merger_device> m_irq5;
	required_device<input_merger_device> m_irq7;
	required_ioport m_sw1;

	required_region_ptr<u32> m_eprom;
	std::unique_ptr<u16[]> m_net_ram;
	output_finder<2> m_led;

#if GRAPHICS
	required_device<screen_device> m_screen;
	required_device<bt458_device> m_ramdac;
	required_device<ram_device> m_vram;
#endif

	emu_timer *m_timer;

	u8 m_intst;
	u8 m_parity_vector;
	bool m_int_state[2];
	bool m_scc_irq_state;
	bool m_parity_irq_state;
};

void news_68k_state::machine_start()
{
	m_led.resolve();

	m_net_ram = std::make_unique<u16[]>(8192);

	m_timer = timer_alloc(FUNC(news_68k_state::timer), this);

	m_intst = 0;
	for (bool &int_state : m_int_state)
		int_state = false;

	m_scc_irq_state = false;
	m_parity_irq_state = false;
}

void news_68k_state::machine_reset()
{
	// eprom is mapped at 0 after reset
	m_cpu->space(0).install_rom(0x00000000, 0x0000ffff, m_eprom);
}

void news_68k_state::init_common()
{
	// HACK: hardwire the rate
	m_fdc->set_rate(500000);
}

void news_68k_state::cpu_map(address_map &map)
{
	map(0xe0000000, 0xe000ffff).rom().region("eprom", 0);

	// 0xe0c40000 // centronics
	map(0xe0c80000, 0xe0c80003).m(m_fdc, FUNC(upd72067_device::map));
	map(0xe0c80100, 0xe0c80100).rw(m_fdc, FUNC(upd72067_device::dma_r), FUNC(upd72067_device::dma_w));
	map(0xe0cc0000, 0xe0cc0007).m(m_scsi, FUNC(ncr5380_device::map));

	map(0xe0d00000, 0xe0d00007).m(m_hid, FUNC(news_hid_hle_device::map_68k));
	map(0xe0d40000, 0xe0d40003).rw(m_scc, FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w));
	map(0xe0d80000, 0xe0d807ff).rw(m_rtc, FUNC(m48t02_device::read), FUNC(m48t02_device::write));
	map(0xe0dc0000, 0xe0dc0000).lw8([this](u8 data) { m_led[0] = BIT(data, 0); m_led[1] = BIT(data, 1); }, "led_w");

	map(0xe0e00000, 0xe0e03fff).lrw16(
		[this](offs_t offset) { return m_net_ram[offset]; }, "net_ram_r",
		[this](offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_net_ram[offset]); }, "net_ram_w");
	map(0xe0e80000, 0xe0e80017).m(m_dma, FUNC(dmac_0266_device::map));
	// e0ec0000 // sound board
	map(0xe0f00000, 0xe0f00003).rw(m_net, FUNC(am7990_device::regs_r), FUNC(am7990_device::regs_w));
	// e0f40000
	//map(0xe0f40000, 0xe0f40000).lr8([]() { return 0xfb; }, "scc_ridsr_r");

	map(0xe1000000, 0xe1000000).w(FUNC(news_68k_state::timer_w));
	map(0xe1080000, 0xe1080000).lw8([this](u8 data) { LOG("parity check enable 0x%02x\n", data); }, "parity_check_enable_w");
	map(0xe1180000, 0xe1180000).lw8([this](u8 data) { m_cpu->set_input_line(INPUT_LINE_IRQ2, bool(data)); }, "irq2_w");
	map(0xe1200000, 0xe1200000).lw8([this](u8 data) { m_cpu->space(0).install_ram(0, m_ram->mask(), 0xc0000000, m_ram->pointer()); }, "ram_enable");
	map(0xe1280000, 0xe1280000).lw8([this](u8 data) { m_cpu->set_input_line(INPUT_LINE_IRQ1, bool(data)); }, "ast_w");
	map(0xe1300000, 0xe1300000).lw8([this](u8 data) { LOG("cache enable 0x%02x (%s)\n", data, machine().describe_context()); }, "cache_enable_w");
	// 0xe1380000 // power on/off
	map(0xe1900000, 0xe1900000).lw8([this](u8 data) { LOG("cache clear 0x%02x\n", data); }, "cache_clear_w");
	map(0xe1a00000, 0xe1a00000).lw8([this](u8 data) { LOG("parity interrupt clear 0x%02x\n", data); }, "parity_interrupt_clear_w");
	// 0xe1b00000 // fdc vfo external/internal
	map(0xe1c00000, 0xe1c000ff).rom().region("idrom", 0);
	map(0xe1c00100, 0xe1c00103).lr8([this]() { return u8(m_sw1->read()); }, "sw1_r");
	// HACK: disable fdc irq for NetBSD
	map(0xe1c00200, 0xe1c00200).lrw8([this]() { return m_intst; }, "intst_r", [this](u8 data) { irq_w<FDC>(0); m_parity_vector = data; }, "parity_vector_w");

	// external I/O
	map(0xf0000000, 0xffffffff).r(FUNC(news_68k_state::bus_error_r));
#if GRAPHICS
	// POPC
	//map(0xf0fc0000, 0xf0fc0003).unmaprw();
	// f0fc0000 & 0x40 == 0x00 -> popm
	// f0fc0000 & 0xc0 == 0xc0 -> popc
	map(0xf0fc0000, 0xf0fc0001).lr16([]() {return 0x00c0; }, "popc_probe"); // lower 2 bits give busy state
	map(0xf0fc4000, 0xf0fc4007).m(m_ramdac, FUNC(bt458_device::map)).umask32(0x00ff00ff);

	//map(0xf0fc0000, 0xf10bffff).rom().region("krom", 0);
#endif

	// 0xf0c30000 expansion lance #1
	// 0xf0c20000   lance #1 memory
	// 0xf0c38000   lance #1 etherid
	// 0xf0c70000 expansion lance #2
	// 0xf0c60000   lance #2 memory
	// 0xf0c78000   lance #2 etherid

	// 0xf0d04000 isdn?

	// 0xf0f00000 nwb512_base
	// 0xf0fc0000 nwb512krom_base
	// 0xf0700000 nwb225_base
	// 0xf0600000 nwb225krom_base
}

void news_68k_state::cpu_autovector_map(address_map &map)
{
	map(0xfffffff3, 0xfffffff3).lr8(NAME([]() { return m68000_base_device::autovector(1); }));
	map(0xfffffff5, 0xfffffff5).lr8(NAME([]() { return m68000_base_device::autovector(2); }));
	map(0xfffffff7, 0xfffffff7).lr8(NAME([]() { return m68000_base_device::autovector(3); }));
	map(0xfffffff9, 0xfffffff9).lr8(NAME([]() { return m68000_base_device::autovector(4); }));
	map(0xfffffffb, 0xfffffffb).lr8(NAME([this]() { return m_scc_irq_state ? m_scc->m1_r() : m68000_base_device::autovector(5); }));
	map(0xfffffffd, 0xfffffffd).lr8(NAME([]() { return m68000_base_device::autovector(6); }));
	map(0xffffffff, 0xffffffff).lr8(NAME([this]() { return m_parity_irq_state ? m_parity_vector : m68000_base_device::autovector(7); }));
}

template <news_68k_state::irq_number Number> void news_68k_state::irq_w(int state)
{
	LOG("irq number %d state %d\n", Number, state);

	if (state)
		m_intst |= 1U << Number;
	else
		m_intst &= ~(1U << Number);

	int_check();
}

void news_68k_state::int_check()
{
	// TODO: assume 43334443, masking?
	static int const int_line[] = { INPUT_LINE_IRQ3, INPUT_LINE_IRQ4 };
	static u8 const int_mask[] = { 0x71, 0x8e };

	for (unsigned i = 0; i < std::size(m_int_state); i++)
	{
		bool const int_state = m_intst & int_mask[i];

		if (m_int_state[i] != int_state)
		{
			m_int_state[i] = int_state;
			m_cpu->set_input_line(int_line[i], int_state);
		}
	}
}

void news_68k_state::timer_w(u8 data)
{
	LOG("timer_w 0x%02x\n", data);

	if (data)
		m_timer->adjust(attotime::from_hz(100));
	else
		m_cpu->set_input_line(INPUT_LINE_IRQ6, CLEAR_LINE);
}

void news_68k_state::timer(s32 param)
{
	m_cpu->set_input_line(INPUT_LINE_IRQ6, ASSERT_LINE);
}

u32 news_68k_state::bus_error_r()
{
	if (!machine().side_effects_disabled())
		m_cpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);

	return 0;
}

static void news_scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
	device.option_add("cdrom", NSCSI_CDROM);
}

void news_68k_state::common(machine_config &config)
{
	M68030(config, m_cpu, 50_MHz_XTAL / 2);
	m_cpu->set_addrmap(AS_PROGRAM, &news_68k_state::cpu_map);
	m_cpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &news_68k_state::cpu_autovector_map);

	// 16 SIMM slots for RAM arranged as two groups of 8 slots, with each bank
	// corresponding to a pair of slots in each group; first bank soldered in
	RAM(config, m_ram);
	m_ram->set_default_size("8M");
	// TODO: assume only 1M modules are supported
	m_ram->set_extra_options("4M,12M,16M");
	m_ram->set_default_value(0);

	M48T02(config, m_rtc);

	DMAC_0266(config, m_dma, 0);
	m_dma->set_bus(m_cpu, 0);

	INPUT_MERGER_ANY_HIGH(config, m_irq5);
	m_irq5->output_handler().set_inputline(m_cpu, INPUT_LINE_IRQ5);

	SCC85C30(config, m_scc, 3993600);
	m_scc->out_int_callback().set(
		[this](int state)
		{
			m_scc_irq_state = bool(state);
			m_irq5->in_w<2>(state);
		});

	// scc channel A
	RS232_PORT(config, m_serial[0], default_rs232_devices, GRAPHICS ? nullptr : "terminal");
	m_serial[0]->cts_handler().set(m_scc, FUNC(z80scc_device::ctsa_w));
	m_serial[0]->dcd_handler().set(m_scc, FUNC(z80scc_device::dcda_w));
	m_serial[0]->rxd_handler().set(m_scc, FUNC(z80scc_device::rxa_w));
	m_scc->out_rtsa_callback().set(m_serial[0], FUNC(rs232_port_device::write_rts));
	m_scc->out_txda_callback().set(m_serial[0], FUNC(rs232_port_device::write_txd));

	// scc channel B
	RS232_PORT(config, m_serial[1], default_rs232_devices, nullptr);
	m_serial[1]->cts_handler().set(m_scc, FUNC(z80scc_device::ctsb_w));
	m_serial[1]->dcd_handler().set(m_scc, FUNC(z80scc_device::dcdb_w));
	m_serial[1]->rxd_handler().set(m_scc, FUNC(z80scc_device::rxb_w));
	m_scc->out_rtsb_callback().set(m_serial[1], FUNC(rs232_port_device::write_rts));
	m_scc->out_txdb_callback().set(m_serial[1], FUNC(rs232_port_device::write_txd));

	AM7990(config, m_net);
	m_net->intr_out().set(FUNC(news_68k_state::irq_w<LANCE>)).invert();
	m_net->dma_in().set([this](offs_t offset) { return m_net_ram[(offset >> 1) & 0x1fff]; });
	m_net->dma_out().set([this](offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_net_ram[(offset >> 1) & 0x1fff]); });

	INPUT_MERGER_ANY_HIGH(config, m_irq7);
	m_irq7->output_handler().set_inputline(m_cpu, INPUT_LINE_IRQ7);

	UPD72067(config, m_fdc, 16_MHz_XTAL);
	m_fdc->intrq_wr_callback().set(FUNC(news_68k_state::irq_w<FDC>));
	m_fdc->drq_wr_callback().set(m_irq7, FUNC(input_merger_device::in_w<0>));
	FLOPPY_CONNECTOR(config, "fdc:0", "35hd", FLOPPY_35_HD, true, floppy_image_device::default_pc_floppy_formats).enable_sound(false);

	// scsi bus and devices
	NSCSI_BUS(config, "scsi");

	/*
	 * CDC WREN V HH 94221-5 (5.25" half-height SCSI-1 single-ended)
	 * 1544 cylinders, 5 heads, 52 sectors/cylinder, ~170MiB formatted
	 *
	 * Vendor   Product          Rev. Vendor-specific
	 * CDC      94221-5          5457 00018715
	 */
	NSCSI_CONNECTOR(config, "scsi:0", news_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:1", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", news_scsi_devices, nullptr);

	// scsi host adapter
	NSCSI_CONNECTOR(config, "scsi:7").option_set("cxd1180", CXD1180).machine_config(
		[this](device_t *device)
		{
			cxd1180_device &adapter = downcast<cxd1180_device &>(*device);

			adapter.irq_handler().set(*this, FUNC(news_68k_state::irq_w<SCSI>));
			adapter.irq_handler().append(m_dma, FUNC(dmac_0266_device::eop_w));
			adapter.drq_handler().set(m_dma, FUNC(dmac_0266_device::req_w));

			subdevice<dmac_0266_device>(":dma")->dma_r_cb().set(adapter, FUNC(cxd1180_device::dma_r));
			subdevice<dmac_0266_device>(":dma")->dma_w_cb().set(adapter, FUNC(cxd1180_device::dma_w));
		});

	NEWS_HID_HLE(config, m_hid);

#if GRAPHICS
	m_hid->irq_out<news_hid_hle_device::KEYBOARD>().set(m_irq5, FUNC(input_merger_device::in_w<0>));
	m_hid->irq_out<news_hid_hle_device::MOUSE>().set(m_irq5, FUNC(input_merger_device::in_w<1>));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(64.0_MHz_XTAL, 1024, 0, 1024, 768, 0, 768);
	m_screen->set_screen_update(FUNC(news_68k_state::screen_update));

	// AM81C458-80JC
	BT458(config, m_ramdac, 64.0_MHz_XTAL);

	// 32 x MB81461-12 (256Kbit ZIP VRAM)
	RAM(config, m_vram);
	m_vram->set_default_size("1MiB");
	m_vram->set_default_value(0);
#endif

	SOFTWARE_LIST(config, "software_list").set_original("sony_news").set_filter("CISC");
}

void news_68k_state::nws1580(machine_config &config)
{
	common(config);
}

static INPUT_PORTS_START(nws15x0)
	PORT_START("SW1")
	PORT_DIPNAME(0x07, 0x07, "Display") PORT_DIPLOCATION("SW1:1,2,3")
	PORT_DIPSETTING(0x07, "Console")
	PORT_DIPSETTING(0x06, "NWB-512")
	PORT_DIPSETTING(0x03, "NWB-225A")
	PORT_DIPSETTING(0x00, "Autoselect")

	PORT_DIPNAME(0x08, 0x08, "Boot Device") PORT_DIPLOCATION("SW1:4")
	PORT_DIPSETTING(0x08, "SCSI")
	PORT_DIPSETTING(0x00, "Floppy")

	PORT_DIPNAME(0x10, 0x10, "Automatic Boot") PORT_DIPLOCATION("SW1:5")
	PORT_DIPSETTING(0x10, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))

	PORT_DIPNAME(0x20, 0x20, "Diagnostic Mode") PORT_DIPLOCATION("SW1:6")
	PORT_DIPSETTING(0x20, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))

	PORT_DIPUNUSED_DIPLOC(0xc0, 0xc0, "SW1:7,8")
INPUT_PORTS_END

ROM_START(nws1250)
	ROM_REGION32_BE(0x20000, "eprom", 0)
	ROM_SYSTEM_BIOS(0, "nws1580", "NWS-1250 v2.0a")
	ROMX_LOAD("nws-1200_ver_2.0a_9010.ic2", 0x00000, 0x20000, CRC(87eca9d2) SHA1(235585a55bc2b3206cfec532852526a638eccad2), ROM_BIOS(0))

	// AM27S21PC PROM
	ROM_REGION32_BE(0x100, "idrom", 0)
	ROM_LOAD("n1250_50292_am27s21pc.ic36", 0x000, 0x100, NO_DUMP)

	// 2 x MB834200A (mask ROM)
	ROM_REGION32_BE(0x100000, "krom", ROMREGION_ERASEFF)
	ROM_LOAD64_BYTE("mb834200a-20_051_aa_9020_g07.ic1",  0x00000, 0x20000, NO_DUMP)
	ROM_LOAD64_BYTE("mb834200a-20_052_aa_9002_g02.ic13", 0x00001, 0x20000, NO_DUMP)
ROM_END

ROM_START(nws1580)
	ROM_REGION32_BE(0x10000, "eprom", 0)
	ROM_SYSTEM_BIOS(0, "nws1580", "NWS-1580 v1.3")
	ROMX_LOAD("pws-1500__ver_1.3__8906.bin", 0x00000, 0x10000, CRC(76395ad9) SHA1(c2ae00218c23cef6519a4d7c74ac2c552790dfd4), ROM_BIOS(0))

	// MB7114 256x4 TTL PROM
	ROM_REGION32_BE(0x100, "idrom", 0)
	ROM_LOAD("n1580_50093.ic63", 0x000, 0x100, CRC(a7f293d6) SHA1(21deffed69e07af515ffc5511bdbf73a2a4c14fb))

	// 2 x HN62321BP (128K x 8-bit mask ROM)
	ROM_REGION32_BE(0x100000, "krom", ROMREGION_ERASEFF)
	ROM_LOAD64_BYTE("aa1.ic14", 0x00000, 0x20000, CRC(db274954) SHA1(4bc9b8a862ce9bdbf43c70f84921253876e21e58))
	ROM_LOAD64_BYTE("aa2.ic15", 0x00001, 0x20000, CRC(0d7686c7) SHA1(b0be18166b4690518e6a11ea194cc1c7a1ea6347))
ROM_END

} // anonymous namespace


//   YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS           INIT         COMPANY  FULLNAME    FLAGS
COMP(1988, nws1580, 0,      0,      nws1580, nws15x0, news_68k_state, init_common, "Sony",  "NWS-1580", MACHINE_NOT_WORKING)
COMP(1990, nws1250, 0,      0,      nws1580, nws15x0, news_68k_state, init_common, "Sony",  "NWS-1250", MACHINE_NOT_WORKING)



news_68k_iop.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Brice Onken

/*
 * Sony NEWS first generation dual-processor 68k systems
 *
 * The NWS-800 series was the first model of NEWS workstation. The design was also turned into the NWS-900 series for
 * server use. The later NWS-1800 and NWS-3800 models use the same system design concept as the 800 series (dual-CPU).
 * The NWS-1800/1900 series is especially similar to the NWS-800/900 series in terms of overall design, even though some peripheral
 * chips are different and the custom MMU is not needed because the 1800/1900 design uses the '030.
 *
 * The NWS-800 series uses one '020 as the I/O Processor (IOP). This processor is responsible for booting the system
 * and also runs a small sub-OS (called iopboot) with processes for handling DMA and interrupts for data-intensive peripherals
 * like SCSI, Ethernet, etc. The Main Processor (often referred to, confusingly, as the CPU) runs NEWS-OS (BSD derivative),
 * and uses a block of main memory allocated for CPU<->IOP interprocessor communication to trigger I/O.
 * The CPU and IOP interrupt each other whenever they need to communicate, after populating main memory with whatever data is needed.
 * The CPU delegates almost all I/O to the IOP (as the DMA controller), only handling I/O for some VME bus peripherals.
 * The CPU bus (on the NWS-800, this is routed through the MMU) is often called the Hyperbus. The NWS-1800/1900/3800 series and the single-CPU
 * 68k and R3000 models explictly call this the Hyperbus, but it is unclear if the NWS-800's CPU bus was only referred to that retroactively or not.
 * There are a couple of mentions of the Hyperbus in NEWS-OS 4 related to even the NWS-800, but the NWS-1xxx/3xxx series were already released by the time NEWS-OS 4 was.
 * So, for clarity due to the number of busses on this system, the Hyperbus term is used throughout this code, but it may or may not be a 100% accurate description.
 * The Hyperbus is directly connected to main memory, the system ROM, and the VME bus interface. Everything else is connected to the I/O
 * bus. The IOP, CPU/MMU, and VME bus can all access the Hyperbus through a buffer. As far as I can tell, nothing on the Hyperbus
 * can talk to the I/O bus, only the IOP is able to do that.
 *
 *  Supported:
 *   - NWS-831
 *
 *  Not supported yet:
 *   - All other NWS-8xx workstations
 *   - NWS-9xx servers (2x '020)
 *
 * Known NWS-800 Series Base Configurations
 * - NWS-811: Jun 1987, 4MB RAM, no cache, diskless
 * - NWS-820: Jan 1987, 4MB RAM, ?? cache, 86MB HDD
 * - NWS-821: Aug 1987, 4MB RAM, no cache, 156MB HDD
 * - NWS-830: Jan 1987, 8MB RAM (expandable?), ?? cache, 156MB HDD
 * - NWS-831: Sep 1987, 8MB RAM (up to 16MB), 8KB cache, 156MB HDD
 * - NWS-841: Sep 1987, 8MB RAM (up to 16MB), 8KB cache, 256MB HDD
 * - NWS-891: Apr 1988, 4MB RAM, no cache, 86MB HDD, with CD-ROM drive
 *
 * Known NWS-900 Series Base Configurations:
 * - NWS-911: Feb 1988, 2x 286MB HDD
 * - NWS-921: Feb 1988, 4x 286MB HDD
 *
 * References:
 *  - NWS-1960 Service Guide
 *  - http://bitsavers.org/pdf/sony/news/Sony_NEWS_Technical_Manual_3ed_199103.pdf
 *  - https://katsu.watanabe.name/doc/sonynews/model.html
 *
 * TODO:
 *   - MMU emulation improvements (are all the right status bits set? any missing features? etc)
 *   - Debug general OS issues - random segfaults when running `ps`, system sometimes fails to shutdown when running `shutdown -x now` after using the networking stack, etc.
 *   - AST (Asynchronous System Trap) emulation
 *   - System cache emulation
 *   - Expansion slots (I/O Bus and VMEBus)
 *   - Networking is very flaky, especially on NEWS-OS 4.
 *   - Graphics, kbms, and parallel port emulation
 *   - Hyperbus handshake for IOP and CPU accesses. The bus has arbitration circuitry to prevent bus contention when both the CPU and IOP are trying to access the hyperbus (RAM and VME)
 */

#include "emu.h"

#include "news_020_mmu.h"
#include "news_hid.h"
#include "news_iop_scsi.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"
#include "bus/nscsi/tape.h"
#include "bus/rs232/rs232.h"

#include "cpu/m68000/m68020.h"

#include "machine/am79c90.h"
#include "machine/bankdev.h"
#include "machine/msm58321.h"
#include "machine/ncr5380.h"
#include "machine/nscsi_bus.h"
#include "machine/pit8253.h"
#include "machine/ram.h"
#include "machine/timekpr.h"
#include "machine/upd765.h"
#include "machine/z80scc.h"

#include "imagedev/floppy.h"

#include "formats/pc_dsk.h"

#define LOG_INTERRUPT (1U << 1)
#define LOG_ALL_INTERRUPT (1U << 2)
#define LOG_LED (1U << 3)
#define LOG_MEMORY (1U << 4)
#define LOG_PANEL (1U << 5)
#define LOG_MEMORY_ERROR (1U << 6)
#define LOG_TIMER (1U << 7)
#define LOG_SCSI (1U << 8)
#define LOG_AST (1U << 9)
#define LOG_RTC (1U << 10)

#define VERBOSE (LOG_GENERAL)

#include "logmacro.h"

namespace
{
	using namespace std::literals::string_view_literals;

	class news_iop_state : public driver_device
	{
	public:
		static constexpr feature_type unemulated_features() { return feature::GRAPHICS; }

		news_iop_state(machine_config const &mconfig, device_type type, char const *tag) :
			driver_device(mconfig, type, tag),
			m_iop(*this, "iop"),
			m_cpu(*this, "cpu"),
			m_mmu(*this, "mmu"),
			m_ram(*this, "ram"),
			m_eprom(*this, "eprom"),
			m_idrom(*this, "idrom"),
			m_rtc(*this, "rtc"),
			m_interval_timer(*this, "interval_timer"),
			m_scc_external(*this, "scc_external"),
			m_scc_peripheral(*this, "scc_peripheral"),
			m_net(*this, "net"),
			m_fdc(*this, "fdc"),
			m_scsi(*this, "scsi:7:am5380"),
			m_scsi_dma(*this, "scsi_dma"),
			m_dip_switch(*this, "FRONT_PANEL"),
			m_serial(*this, "serial%u", 0U),
			iop_irq_line_map({
				{INPUT_LINE_IRQ1, {CPIRQ_3_1}},
				{INPUT_LINE_IRQ2, {SCSI_IRQ, SCSI_DRQ}},
				{INPUT_LINE_IRQ3, {LANCE}},
				{INPUT_LINE_IRQ4, {CPU}},
				{INPUT_LINE_IRQ5, {SCC, SCC_PERIPHERAL}},
				{INPUT_LINE_IRQ6, {TIMEOUT, FDCIRQ}},
				{INPUT_LINE_IRQ7, {FDCDRQ}} }),
			cpu_irq_line_map({
				// AST is excluded from this check
				{INPUT_LINE_IRQ2, CPIRQ1},
				{INPUT_LINE_IRQ3, IOPIRQ3},
				{INPUT_LINE_IRQ4, CPIRQ3},
				{INPUT_LINE_IRQ5, IOPIRQ5},
				{INPUT_LINE_IRQ6, TIMER},
				{INPUT_LINE_IRQ7, PERR} })
		{
		}

		void nws831(machine_config &config) ATTR_COLD;
		void init_common() ATTR_COLD;

	protected:
		// driver_device overrides
		virtual void machine_start() override ATTR_COLD;
		virtual void machine_reset() override ATTR_COLD;

		// address maps
		void iop_map(address_map &map) ATTR_COLD;
		void iop_autovector_map(address_map &map) ATTR_COLD;
		void mmu_map(address_map &map) ATTR_COLD;
		void cpu_map(address_map &map) ATTR_COLD;

		// machine config
		void common(machine_config &config) ATTR_COLD;

		// IOP IRQ setup
		enum iop_irq_number : unsigned
		{
			CPIRQ_3_1,      // Expansion I/O bus and VME bus interrupts
			SCSI_IRQ,       // SCSI IRQ (unmaskable)
			SCSI_DRQ,       // SCSI DRQ (maskable)
			LANCE,          // Ethernet controller interrupts
			CPU,            // Interprocessor communication interrupt (from CPU)
			SCC,            // Serial communication interrupts
			SCC_PERIPHERAL, // IRQ from kb/ms SCC
			TIMEOUT,        // IOP timeout and IRQ from FDC
			FDCIRQ,         // IRQ from FDC (uses same IRQ input as TIMEOUT)
			FDCDRQ          // DRQ from FDC
		};
		static constexpr std::array iop_irq_names = {"CPIRQ"sv, "SCSI_IRQ"sv, "SCSI_DRQ"sv, "LANCE"sv, "CPU"sv, "SCC"sv, "SCC_PERIPHERAL"sv, "TIMEOUT"sv, "FDCIRQ"sv, "FDCDRQ"sv};
		static constexpr uint32_t iop_nmi_mask = (1 << SCSI_IRQ) | (1 << SCC) | (1 << LANCE) | (1 << FDCIRQ) | (1 << FDCDRQ);

		template <iop_irq_number Number>
		void iop_irq_w(int state);
		template <iop_irq_number Number>
		void iop_inten_w(uint8_t state);
		template <iop_irq_number Number>
		bool is_iop_irq_set();
		void int_check_iop();

		// CPU IRQ setup
		enum cpu_irq_number : unsigned
		{
			AST,     // Asynchronous System Trap interrupt
			CPIRQ1,  // VME bus interrupt
			IOPIRQ3, // Low-priority interprocessor communication interrupts (from IOP)
			CPIRQ3,  // VME bus interrupt
			IOPIRQ5, // High-priority interprocessor communication interrupts (from IOP)
			TIMER,   // 100Hz timer interrupt
			PERR     // Parity error interrupt
		};
		static constexpr std::array cpu_irq_names = {"AST"sv, "CPIRQ1"sv, "IOPIRQ3"sv, "CPIRQ3"sv, "IOPIRQ5"sv, "TIMER"sv, "PERR"sv};

		template <cpu_irq_number Number>
		void cpu_irq_w(int state);
		template <cpu_irq_number Number>
		void cpu_inten_w(uint8_t state);
		void int_check_cpu();

		// 68k bus error handlers
		void cpu_bus_error(offs_t offset, uint32_t mem_mask, bool write, uint8_t status);
		void scsi_bus_error(uint8_t data);
		uint32_t extio_bus_error_r(offs_t offset, uint32_t mem_mask);
		void extio_bus_error_w(offs_t offset, uint32_t data, uint32_t mem_mask);
		uint32_t vme_bus_error_r(offs_t offset, uint32_t mem_mask);
		void vme_bus_error_w(offs_t offset, uint32_t data, uint32_t mem_mask);

		// Platform hardware used by the IOP
		uint8_t iop_status_r();
		void iop_romdis_w(uint8_t data);
		void min_w(uint8_t data);
		void motoron_w(uint8_t data);
		void powoff_w(uint8_t data);
		void cpureset_w(uint8_t data);
		uint8_t rtcreg_r();
		void rtcreg_w(uint8_t data);
		void rtcsel_w(uint8_t data);
		void set_rtc_data_bit(uint8_t bit_number, int data);
		uint32_t scsi_dma_r(offs_t offset, uint32_t mem_mask);
		void scsi_dma_w(offs_t offset, uint32_t data, uint32_t mem_mask);
		void scsi_drq_handler(int status);

		// Front panel signal handlers
		void handle_rts(int data);
		void handle_dtr(int data);
		void update_dcd();

		// Platform hardware used by the main processor
		void cpu_romdis_w(uint8_t data);
		void mmuen_w(uint8_t data);
		uint8_t berr_status_r();
		void astreset_w(uint8_t data);
		void astset_w(uint8_t data);

		// CPU timer handlers
		void interval_timer_tick(uint8_t data);
		TIMER_CALLBACK_MEMBER(bus_error_off_cpu);

	private:
		// CPUs (2x 68020, but only the main processor has an FPU)
		required_device<m68020_device> m_iop;    // I/O Processor (BSP, brings up system)
		required_device<m68020fpu_device> m_cpu; // Main Processor

		// Memory devices and main CPU's MMU
		required_device<news_020_mmu_device> m_mmu;
		required_device<ram_device> m_ram;
		required_region_ptr<u32> m_eprom;
		required_region_ptr<u32> m_idrom;

		// Platform hardware
		required_device<msm58321_device> m_rtc;
		required_device<pit8253_device> m_interval_timer;
		required_device<scc8530_device> m_scc_external;
		required_device<scc8530_device> m_scc_peripheral;
		required_device<am7990_device> m_net;
		required_device<upd765a_device> m_fdc;
		required_device<ncr5380_device> m_scsi;
		required_device<news_iop_scsi_helper_device> m_scsi_dma;
		required_ioport m_dip_switch;
		required_device_array<rs232_port_device, 2> m_serial;
		std::unique_ptr<u16[]> m_net_ram;

		// IOP IRQ state
		const std::map<int, std::vector<iop_irq_number>> iop_irq_line_map;
		uint32_t m_iop_intst = 0;
		uint32_t m_iop_inten = 0;

		// CPU IRQ state
		const std::map<int, cpu_irq_number> cpu_irq_line_map;
		uint32_t m_cpu_intst = 0;
		uint32_t m_cpu_inten = 0;

		// Bus error details
		bool m_cpu_bus_error = false;
		offs_t m_last_berr_pc_cpu = 0;
		uint8_t m_cpu_bus_error_status = 0;
		emu_timer *m_cpu_bus_error_timer;

		// Front panel
		bool m_panel_clear = false;
		bool m_panel_shift = false;
		int m_sw1_first_read = 0;
		int m_panel_shift_count = 0;
		uint8_t m_rtc_data = 0;

		// Other constants
		static constexpr int NET_RAM_SIZE = 8192; // 16K RAM, in 16-bit words
	};

	void news_iop_state::machine_start()
	{
		m_cpu_bus_error_timer = timer_alloc(FUNC(news_iop_state::bus_error_off_cpu), this);

		m_net_ram = std::make_unique<u16[]>(NET_RAM_SIZE);
		save_pointer(NAME(m_net_ram), NET_RAM_SIZE);

		m_mmu->space(0).install_ram(0x0, m_ram->mask(), m_ram->pointer());

		// Save state support
		save_item(NAME(m_iop_intst));
		save_item(NAME(m_cpu_intst));
		save_item(NAME(m_iop_inten));
		save_item(NAME(m_cpu_inten));
		save_item(NAME(m_cpu_bus_error));
		save_item(NAME(m_last_berr_pc_cpu));
		save_item(NAME(m_cpu_bus_error_status));
		save_item(NAME(m_panel_clear));
		save_item(NAME(m_panel_shift));
		save_item(NAME(m_sw1_first_read));
		save_item(NAME(m_panel_shift_count));
		save_item(NAME(m_rtc_data));
	}

	void news_iop_state::interval_timer_tick(uint8_t data)
	{
		// TODO: Confirm that channel 0 drives both CPU and IOP
		// On the NWS-1960, the 8253 uses channel 0 for the 100Hz CPU timer, channel 1 for main memory refresh, and channel 2 for the IOP timeout
		// On the 831, using the same assignment breaks things because channel 2 is too fast for the IOP to operate correctly because it gets interrupted far too frequently.
		// The 1850 (same design as 1960) loads channels 0 and 2 with count 0x4e20, whereas the 831 loads channel 0 with 0x4e20 and channel 2 with 0x2.
		// Therefore, because the 831 seems to behave with 0x4e20 driving the IOP as well as the CPU (100Hz), they both hook off of channel 0 for now. However, one or both could be
		// generated elsewhere (or some other trickery is afoot) on the real system, so this needs more investigation in the future.
		// Math: 0x4e20 cycles * 500ns = 10ms period, 1 / 10ms = 100Hz

		iop_irq_w<TIMEOUT>(data > 0);
		cpu_irq_w<TIMER>(data > 0);
	}

	TIMER_CALLBACK_MEMBER(news_iop_state::bus_error_off_cpu)
	{
		m_cpu_bus_error = false;
	}

	uint8_t news_iop_state::iop_status_r()
	{
		// IOP status bits defined as below for the NWS-18xx/19xx series
		// 7: FDC IRQ
		// 6: ~CPIRQ3
		// 5: Main Memory Parity Error Flag
		// 4: ~CPIRQ1 (On the NWS-800, this seems to be SCSI interrupt status?)
		// 3: DSR CHB
		// 2: RI CHB
		// 1: DSR CHA
		// 0: RI CHA

		const uint8_t status = (is_iop_irq_set<SCSI_IRQ>() ? 0x10 : 0) | (is_iop_irq_set<FDCIRQ>() ? 0x80 : 0);
		LOGMASKED(LOG_ALL_INTERRUPT, "Read IOPSTATUS = 0x%x\n", status);
		return status;
	}

	void news_iop_state::iop_romdis_w(uint8_t data)
	{
		LOG("IOP ROMDIS 0x%x\n", data);
		if (data > 0)
		{
			m_iop->space(0).install_ram(0, m_ram->mask(), 0x0, m_ram->pointer());
		}
		else
		{
			m_iop->space(0).install_rom(0x00000000, 0x0000ffff, m_eprom);
		}
		m_panel_shift_count = 0; // hack, clear state from init
	}

	void news_iop_state::min_w(uint8_t data)
	{
		constexpr int HD_RATE = 500000;
		constexpr int DD_RATE = 250000; // TODO: Test DD rate when image is available
		const int rate = (data > 0) ? DD_RATE : HD_RATE; // 0 = HD, 1 = DD

		LOG("Write MIN = 0x%x, set rate to %s (%s)\n", data, rate == HD_RATE ? "HD" : "DD", machine().describe_context());
		m_fdc->set_rate(rate);
	}

	void news_iop_state::motoron_w(uint8_t data)
	{
		LOG("Write MOTORON = 0x%x (%s)\n", data, machine().describe_context());
		m_fdc->subdevice<floppy_connector>("0")->get_device()->mon_w(data & 8 ? 1 : 0);
	}

	void news_iop_state::powoff_w(uint8_t data)
	{
		LOG("Write POWOFF = 0x%x (%s)\n", data, machine().describe_context());

		if (!data && !machine().side_effects_disabled())
		{
			machine().schedule_exit();
		}
	}

	void news_iop_state::cpureset_w(uint8_t data)
	{
		LOG("Write CPURESET = 0x%x\n", data);
		m_cpu->set_input_line(INPUT_LINE_HALT, data ? 0 : 1);
	}

	uint8_t news_iop_state::rtcreg_r()
	{
		m_rtc->cs1_w(1);
		m_rtc->cs2_w(1);

		m_rtc->read_w(1);
		uint8_t result = m_rtc_data;
		m_rtc->read_w(0);
		m_rtc->cs2_w(0);

		LOGMASKED(LOG_RTC, "rtc r 0x%x\n", result);
		return result;
	}

	void news_iop_state::rtcreg_w(uint8_t data)
	{
		LOGMASKED(LOG_RTC, "rtc w 0x%x\n", data);
		m_rtc->cs1_w(1);
		m_rtc->cs2_w(1);

		m_rtc->d0_w((data & 0x1) > 0);
		m_rtc->d1_w((data & 0x2) > 0);
		m_rtc->d2_w((data & 0x4) > 0);
		m_rtc->d3_w((data & 0x8) > 0);

		m_rtc->write_w(1);
		m_rtc->write_w(0);
		m_rtc->cs2_w(0);
	}

	void news_iop_state::rtcsel_w(uint8_t data)
	{
		LOGMASKED(LOG_RTC, "rtc sel w 0x%x\n", data);
		m_rtc->cs1_w(1);
		m_rtc->cs2_w(1);

		m_rtc->d0_w(BIT(data, 0));
		m_rtc->d1_w(BIT(data, 1));
		m_rtc->d2_w(BIT(data, 2));
		m_rtc->d3_w(BIT(data, 3));

		m_rtc->address_write_w(1);
		m_rtc->address_write_w(0);
		m_rtc->cs2_w(0);
	}

	void news_iop_state::set_rtc_data_bit(uint8_t bit_number, int data)
	{
		const uint8_t bit = 1 << bit_number;
		m_rtc_data = (m_rtc_data & ~bit) | (data ? bit : 0x0);
	}

	uint32_t news_iop_state::scsi_dma_r(offs_t offset, uint32_t mem_mask)
	{
		uint32_t result = 0;
		switch (mem_mask)
		{
		case 0xff000000:
			result = m_scsi_dma->read_wrapper(true, 6) << 24;
			break;

		case 0xffff0000:
			result = (m_scsi_dma->read_wrapper(true, 6) << 24) |
					 (m_scsi_dma->read_wrapper(true, 6) << 16);
			break;

		case 0xffffffff:
			result = (m_scsi_dma->read_wrapper(true, 6) << 24) |
					 (m_scsi_dma->read_wrapper(true, 6) << 16) |
					 (m_scsi_dma->read_wrapper(true, 6) << 8) |
					 m_scsi_dma->read_wrapper(true, 6);
			break;

		default:
			fatalerror("scsi_dma_r: unknown mem_mask %08x\n", mem_mask);
		}
		return result;
	}

	void news_iop_state::scsi_dma_w(offs_t offset, uint32_t data, uint32_t mem_mask)
	{
	   switch (mem_mask)
		{
		case 0xff000000:
			m_scsi_dma->write_wrapper(true, 0, data >> 24);
			break;

		case 0xffff0000:
			m_scsi_dma->write_wrapper(true, 0, data >> 24);
			m_scsi_dma->write_wrapper(true, 0, data >> 16);
			break;

		case 0xffffffff:
			m_scsi_dma->write_wrapper(true, 0, data >> 24);
			m_scsi_dma->write_wrapper(true, 0, data >> 16);
			m_scsi_dma->write_wrapper(true, 0, data >> 8);
			m_scsi_dma->write_wrapper(true, 0, data & 0xff);
			break;

		default:
			fatalerror("scsi_dma_w: unknown mem_mask %08x\n", mem_mask);
			break;
		}
	}

	void news_iop_state::scsi_drq_handler(int status)
	{
		LOGMASKED(LOG_SCSI, "SCSI DRQ changed to 0x%x\n", status);
		m_scsi_dma->drq_w(status);
		iop_irq_w<SCSI_DRQ>(status);
	}

	void news_iop_state::machine_reset()
	{
		// Reset memory configuration
		m_iop->space(0).install_rom(0x00000000, 0x0000ffff, m_eprom);

		// CPU doesn't run until the IOP tells it to
		m_cpu->set_input_line(INPUT_LINE_HALT, 1);
	}

	void news_iop_state::init_common()
	{
	}

	void news_iop_state::iop_map(address_map &map)
	{
		map.unmap_value_low();
		// Silence unmapped read/write warnings during memory probe - whatever RAM is present will be installed over top of this region
		map(0x00000000, 0x00ffffff).noprw();

		map(0x03000000, 0x0300ffff).rom().region("eprom", 0).mirror(0x007f0000);

		// IOP bus expansion I/O
		map(0x20000000, 0x20ffffff).rw(FUNC(news_iop_state::extio_bus_error_r),FUNC(news_iop_state::extio_bus_error_w)).mirror(0x1f000000);

		// Expansion slot SCC (bus errors here kill iopboot, so the probe process may not use bus errors, at least not with the same setup as extio)
		// TODO: Does the fact that NEWS-OS 4 poke at these addresses mean that something is not configured properly elsewhere?
		map(0x4c000100, 0x4c0001ff).noprw();

		map(0x60000000, 0x60000000).r(FUNC(news_iop_state::iop_status_r));
		map(0x40000000, 0x40000000).w(FUNC(news_iop_state::iop_inten_w<TIMEOUT>));
		map(0x40000001, 0x40000001).w(FUNC(news_iop_state::min_w));
		map(0x40000002, 0x40000002).w(FUNC(news_iop_state::motoron_w));
		map(0x40000003, 0x40000003).w(FUNC(news_iop_state::iop_inten_w<SCSI_DRQ>));
		map(0x40000004, 0x40000004).w(FUNC(news_iop_state::iop_romdis_w));
		map(0x40000005, 0x40000005).w(FUNC(news_iop_state::powoff_w));
		map(0x40000006, 0x40000006).w(FUNC(news_iop_state::iop_inten_w<CPU>));
		map(0x40000007, 0x40000007).w(FUNC(news_iop_state::cpureset_w));

		map(0x42000000, 0x42000003).rw(m_interval_timer, FUNC(pit8253_device::read), FUNC(pit8253_device::write));

		map(0x44000000, 0x44000003).m(m_fdc, FUNC(upd765a_device::map));
		map(0x44000007, 0x44000007).rw(m_fdc, FUNC(upd765a_device::dma_r), FUNC(upd765a_device::dma_w));

		map(0x46000001, 0x46000001).rw(FUNC(news_iop_state::rtcreg_r), FUNC(news_iop_state::rtcreg_w));
		map(0x46000002, 0x46000002).w(FUNC(news_iop_state::rtcsel_w));

		map(0x4a000000, 0x4a000003).rw(m_scc_peripheral, FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w)); // kbms
		map(0x4c000000, 0x4c000003).rw(m_scc_external, FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w));   // rs232

		map(0x4e000000, 0x4e000007).r(m_scsi, FUNC(ncr5380_device::read));
		map(0x4e000000, 0x4e000007).lrw8(NAME([this] (offs_t offset) {
			return m_scsi_dma->read_wrapper(false, offset);
		}), NAME([this] (offs_t offset, uint8_t data) {
			m_scsi_dma->write_wrapper(false, offset, data);
		}));

		map(0x6a000001, 0x6a000001).lw8(NAME([this] (uint8_t data) { cpu_irq_w<IOPIRQ5>(data > 0); }));
		map(0x6a000002, 0x6a000002).lw8(NAME([this] (uint8_t data) { cpu_irq_w<IOPIRQ3>(data > 0); }));

		map(0x64000000, 0x64000003).rw(FUNC(news_iop_state::scsi_dma_r), FUNC(news_iop_state::scsi_dma_w));

		map(0x66000000, 0x66003fff).lrw16([this] (offs_t offset) { return m_net_ram[offset]; }, "net_ram_r",
										  [this] (offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_net_ram[offset]); }, "net_ram_w");

		map(0x68000000, 0x68000003).rw(m_net, FUNC(am7990_device::regs_r), FUNC(am7990_device::regs_w));

		map(0x80000000, 0x8001ffff).ram(); // IOP work RAM
	}

	void news_iop_state::cpu_romdis_w(uint8_t data)
	{
		LOG("(%s) CPU ROMDIS 0x%x\n", machine().describe_context(), data);
		m_mmu->set_rom_enable(data == 0);
	}

	void news_iop_state::mmuen_w(uint8_t data)
	{
		LOGMASKED(LOG_MEMORY, "(%s) CPU MMUEN 0x%x\n", machine().describe_context(), data);
		const bool mmu_enable = data > 0;
		m_cpu->set_emmu_enable(mmu_enable);
		m_mmu->set_mmu_enable(mmu_enable);
	}

	uint8_t news_iop_state::berr_status_r()
	{
		LOGMASKED(LOG_MEMORY, "(%s) BERR_STATUS read = 0x%x\n", machine().describe_context(), m_cpu_bus_error_status);
		uint8_t status = m_cpu_bus_error_status;
		m_cpu_bus_error_status = 0;
		m_last_berr_pc_cpu = 0;
		return status;
	}

	// TODO: implement AST (Asynchronous System Trap)
	void news_iop_state::astreset_w(uint8_t data)
	{
		LOGMASKED(LOG_AST, "(%s) AST_RESET 0x%x\n", machine().describe_context(), data);
	}

	void news_iop_state::astset_w(uint8_t data)
	{
		LOGMASKED(LOG_AST, "(%s) AST_SET 0x%x\n", machine().describe_context(), data);
	}

	void news_iop_state::mmu_map(address_map &map)
	{
		map(0x03000000, 0x0300ffff).rom().region("eprom", 0).mirror(0x007f0000);

		// VME bus
		map(0x03900000, 0x039fffff).rw(FUNC(news_iop_state::vme_bus_error_r), FUNC(news_iop_state::vme_bus_error_w)); // TODO: full region start/end

		// Various platform control registers (MMU and otherwise)
		map(0x04400000, 0x04400000).w(FUNC(news_iop_state::cpu_romdis_w));
		map(0x04400001, 0x04400001).w(FUNC(news_iop_state::mmuen_w));
		map(0x04400002, 0x04400002).w(FUNC(news_iop_state::cpu_inten_w<IOPIRQ3>));
		map(0x04400003, 0x04400003).w(FUNC(news_iop_state::cpu_inten_w<IOPIRQ5>));
		map(0x04400004, 0x04400004).w(FUNC(news_iop_state::cpu_inten_w<TIMER>));
		map(0x04400005, 0x04400005).lw8([this] (uint8_t data)
										{
											LOGMASKED(LOG_MEMORY, "(%s) Write CACHEEN = 0x%x\n", machine().describe_context(), data);
										}, "CACHEEN");
		map(0x04400006, 0x04400006).w(FUNC(news_iop_state::cpu_inten_w<PERR>));
		map(0x04800000, 0x04800000).lw8([this] (uint8_t data) { iop_irq_w<CPU>(1); }, "INT_IOP");
		map(0x04c00000, 0x04c00000).select(0x10000000).w(m_mmu, FUNC(news_020_mmu_device::clear_entries));

		map(0x05000000, 0x05000000).select(0x400000).lw8([this] (offs_t offset, uint8_t data)
														 {
															 LOGMASKED(LOG_MEMORY, "%s cache clear = 0x%x (%s)\n", (offset & 0x400000) ? "system" : "user", data, machine().describe_context());
														 }, "CACHE_CLR");
		map(0x05800000, 0x05800000).w(FUNC(news_iop_state::astreset_w));
		map(0x05800001, 0x05800001).w(FUNC(news_iop_state::astset_w));
		map(0x05c00000, 0x05c00000).r(FUNC(news_iop_state::berr_status_r));
		map(0x06000000, 0x061fffff).rw(m_mmu, FUNC(news_020_mmu_device::mmu_entry_r), FUNC(news_020_mmu_device::mmu_entry_w)).select(0x10000000);
	}

	void news_iop_state::cpu_map(address_map &map)
	{
		map(0x0, 0xffffffff).lrw32(
				[this] (offs_t offset, uint32_t mem_mask) {
					return m_mmu->hyperbus_r(offset, mem_mask, m_cpu->supervisor_mode());
				}, "hyperbus_r",
				[this] (offs_t offset, uint32_t data, uint32_t mem_mask) {
					m_mmu->hyperbus_w(offset, data, mem_mask, m_cpu->supervisor_mode());
				}, "hyperbus_w");
	}

	template <news_iop_state::iop_irq_number Number>
	bool news_iop_state::is_iop_irq_set()
	{
		return BIT(m_iop_intst, Number);
	}

	template <news_iop_state::iop_irq_number Number>
	void news_iop_state::iop_irq_w(int state)
	{
		if (Number != TIMEOUT)
		{
			LOGMASKED(LOG_INTERRUPT, "(%s) IOP IRQ %s %s\n", machine().describe_context(), iop_irq_names[Number], state ? "set" : "cleared");
		}

		if (state)
		{
			m_iop_intst |= 1U << Number;
		}
		else
		{
			m_iop_intst &= ~(1U << Number);
		}

		int_check_iop();
	}

	template <news_iop_state::iop_irq_number Number>
	void news_iop_state::iop_inten_w(uint8_t state)
	{
		if (Number != TIMEOUT)
		{
			LOGMASKED(LOG_INTERRUPT, "(%s) IOP IRQ %s %s\n", machine().describe_context(), iop_irq_names[Number], state ? "enabled" : "disabled");
		}

		if (state)
		{
			m_iop_inten |= (1 << Number);
		}
		else
		{
			m_iop_inten &= ~(1 << Number);
			m_iop_intst &= ~(1 << Number); // TODO: does this actually clear in all cases?
		}

		int_check_iop();
	}

	void news_iop_state::int_check_iop()
	{
	   const int active_irq = m_iop_intst & (m_iop_inten | iop_nmi_mask);
	   for (auto irq : iop_irq_line_map)
	   {
			// Calculate state of input pin (logical OR of all attached inputs)
			bool state = false;
			for (auto irq_input : irq.second)
			{
				state |= active_irq & (1 << irq_input);
			}

			// Update input pin status if it has changed
			if (m_iop->input_line_state(irq.first) != state) {
				if (irq.first != INPUT_LINE_IRQ6)
				{
					LOGMASKED(LOG_INTERRUPT, "Setting IOP input line %d to %d\n", irq.first, state ? 1 : 0);
				}

				m_iop->set_input_line(irq.first, state ? 1 : 0);
			}
	   }
	}

	template <news_iop_state::cpu_irq_number Number>
	void news_iop_state::cpu_irq_w(int state)
	{
		if (Number != TIMER)
		{
			LOGMASKED(LOG_INTERRUPT, "(%s) CPU IRQ %s %s\n", machine().describe_context(), cpu_irq_names[Number], state ? "set" : "cleared");
		}

		if (state)
		{
			m_cpu_intst |= 1U << Number;
		}
		else
		{
			m_cpu_intst &= ~(1U << Number);
		}

		int_check_cpu();
	}

	template <news_iop_state::cpu_irq_number Number>
	void news_iop_state::cpu_inten_w(uint8_t state)
	{
		if (Number != TIMER)
		{
			LOGMASKED(LOG_INTERRUPT, "(%s) CPU IRQ %s %s\n", machine().describe_context(), cpu_irq_names[Number], state ? "enabled" : "disabled");
		}

		if (state)
		{
			m_cpu_inten |= (1 << Number);
		}
		else
		{
			m_cpu_inten &= ~(1 << Number);
			m_cpu_intst &= ~(1 << Number); // TODO: does this actually clear in all cases?
		}

		int_check_cpu();
	}

	void news_iop_state::int_check_cpu()
	{
		const int active_irq = m_cpu_intst & m_cpu_inten;
		for (auto irq : cpu_irq_line_map)
		{
			// Update input pin status if it has changed
			const bool state = BIT(active_irq, irq.second);
			if (m_cpu->input_line_state(irq.first) != state)
			{
				if (irq.first != INPUT_LINE_IRQ6)
				{
					LOGMASKED(LOG_INTERRUPT, "Setting CPU input line %d to %d\n", irq.first, state ? 1 : 0);
				}

				m_cpu->set_input_line(irq.first, state ? 1 : 0);
			}
		}
	}

	static void news_scsi_devices(device_slot_interface &device)
	{
		device.option_add("harddisk", NSCSI_HARDDISK);
		device.option_add("cdrom", NSCSI_CDROM);
		device.option_add("tape", NSCSI_TAPE);
	}

	void news_iop_state::handle_rts(int data)
	{
		if (m_panel_shift != !data)
		{
			m_panel_shift = !data;

			if (!m_panel_clear && data) // falling edge?
			{
				// Count not forced to 0 by the clear pin
				++m_panel_shift_count;
			}
		}
		LOGMASKED(LOG_PANEL, "panel I/O rts = %d new count = 0x%x (%s)\n", !data, m_panel_shift_count, machine().describe_context());
		update_dcd();
	}

	void news_iop_state::handle_dtr(int data)
	{
		if (m_panel_clear != data)
		{
			LOGMASKED(LOG_PANEL, "panel I/O dtr = %d (%s)\n", data, machine().describe_context());
			m_panel_clear = data;
			if (data)
			{
				m_panel_shift_count = 0;
			}
		}

		update_dcd();
	}

	void news_iop_state::update_dcd()
	{
		// the shift pin and the counter drive a multiplexer (the shift pin also drives the counter)
		uint8_t multiplexer_value = (m_panel_shift_count & 0x3) << 1;
		multiplexer_value |= (m_panel_shift ? 0x1 : 0x0);

		bool dcd_state = false;
		if (multiplexer_value < 6) // DIPSW
		{
			// The reset condition (somehow) seems to start the multiplexer count at 5. This is a workaround to achieve the same results.
			// This seems to be correct because it causes diag mode to start when SW6 is set, and also allows the NWB-512 init code to start
			// when SW1 is set. When framebuffer emulation is added, this can be confirmed (maybe also with an oscilloscope readout?)
			// The 2 is there because the reset condition of the emulated pins results in an extra reset.
			if ((m_sw1_first_read < 2) && multiplexer_value == 0)
			{
				m_sw1_first_read++;
				dcd_state = BIT(m_dip_switch->read(), 5);
			}
			else
			{
				dcd_state = BIT(m_dip_switch->read(), multiplexer_value);
			}
		}
		else // IDROM
		{
			// Step 1: Get IDROM word address and byte offset
			const uint8_t idrom_idx = m_panel_shift_count >> 4;
			const uint8_t offset = (m_panel_shift_count >> 2) % 4;

			// Step 2: Get target data byte
			const uint8_t idrom_data = m_idrom[idrom_idx] >> ((3 - offset) * 8);

			// Step 3: Apply bit mask to get one of two data bits from the byte-encoded nibble and set DCD
			dcd_state = idrom_data & (multiplexer_value == 6 ? 0x1 : 0x2);
			LOGMASKED(LOG_PANEL, "idrom d0 [0x%02x] -> 0x%02x\n", idrom_idx, dcd_state);
		}
		LOGMASKED(LOG_PANEL, "mplex 0x%04x cnt 0x%04x set dcd = %d\n", multiplexer_value, m_panel_shift_count, dcd_state);
		m_scc_peripheral->dcdb_w(dcd_state);
	}

	void news_iop_state::cpu_bus_error(offs_t offset, uint32_t mem_mask, bool write, uint8_t status)
	{
		if (machine().side_effects_disabled() || (m_cpu_bus_error && m_cpu->pc() == m_last_berr_pc_cpu))
		{
			LOGMASKED(LOG_MEMORY, "Suppressing cpu bus error at offset 0x%x mask 0x%x r/w %s status 0x%x pc 0x%x", offset, mem_mask, write ? "w" : "r", status, m_cpu->pc());
			return;
		}

		LOGMASKED(LOG_MEMORY, "Applying cpu bus error at offset 0x%x mask 0x%x r/w %s status 0x%x pc 0x%x\n", offset, mem_mask, write ? "w" : "r", status, m_cpu->pc());

		m_last_berr_pc_cpu = m_cpu->pc();
		m_cpu_bus_error = true;
		m_cpu_bus_error_status = status;
		m_cpu->set_buserror_details(offset, !write, m_cpu->get_fc(), true);
		m_cpu_bus_error_timer->adjust(m_cpu->cycles_to_attotime(16)); // TODO: 16 is what hcpu30 uses for a similar implementation - is that OK for NEWS?
	}

	void news_iop_state::scsi_bus_error(uint8_t data)
	{
		if (!machine().side_effects_disabled())
		{
			m_iop->set_buserror_details(0x64000000, data == news_iop_scsi_helper_device::READ_ERROR, m_iop->get_fc());
			m_iop->pulse_input_line(M68K_LINE_BUSERROR, attotime::zero);
		}
	}

	uint32_t news_iop_state::extio_bus_error_r(offs_t offset, uint32_t mem_mask)
	{
		if (!machine().side_effects_disabled())
		{
			LOG("extio_bus_error_r(0x%x, 0x%x) -> 0x%x = 0x%x (%s)\n", offset, mem_mask, 0x20000000 + offset, 0xff, machine().describe_context());
			m_iop->set_buserror_details(0x20000000 + offset, 1, m_iop->get_fc());
			m_iop->pulse_input_line(M68K_LINE_BUSERROR, attotime::zero);
		}
		return 0xff;
	}

	void news_iop_state::extio_bus_error_w(offs_t offset, uint32_t data, uint32_t mem_mask)
	{
		if (!machine().side_effects_disabled())
		{
			LOG("extio_bus_error_w(0x%x, 0x%x, 0x%x) -> 0x%x = (%s)\n", offset, data, mem_mask, 0x20000000 + offset, machine().describe_context());
			m_iop->set_buserror_details(0x20000000 + offset, 0, m_iop->get_fc());
			m_iop->pulse_input_line(M68K_LINE_BUSERROR, attotime::zero);
		}
	}

	uint32_t news_iop_state::vme_bus_error_r(offs_t offset, uint32_t mem_mask)
	{
		if (!machine().side_effects_disabled())
		{
			// TODO: this might be more accurate with the full cpu bus error func and setting the status to HB_BERR
			LOG("vme_r(0x%x, 0x%x) -> 0x%x = 0x%x (%s)\n", offset, mem_mask, 0x03900000 + offset, 0xff, machine().describe_context());
			m_cpu->set_buserror_details(0x03900000 + offset, 1, m_cpu->get_fc());
			m_cpu->pulse_input_line(M68K_LINE_BUSERROR, attotime::zero);
		}
		return 0xff;
	}

	void news_iop_state::vme_bus_error_w(offs_t offset, uint32_t data, uint32_t mem_mask)
	{
		if (!machine().side_effects_disabled())
		{
			LOG("vme_w(0x%x, 0x%x, 0x%x) -> 0x%x = (%s)\n", offset, data, mem_mask, 0x03900000 + offset, machine().describe_context());
			m_cpu->set_buserror_details(0x03900000 + offset, 0, m_cpu->get_fc());
			m_cpu->pulse_input_line(M68K_LINE_BUSERROR, attotime::zero);
		}
	}

	void news_iop_state::common(machine_config &config)
	{
		M68020(config, m_iop, 16.67_MHz_XTAL); // TODO: this might come from a 33.3333MHz crystal divided by two
		m_iop->set_addrmap(AS_PROGRAM, &news_iop_state::iop_map);
		m_iop->set_addrmap(m68000_base_device::AS_CPU_SPACE, &news_iop_state::iop_autovector_map);

		M68020FPU(config, m_cpu, 16.67_MHz_XTAL);
		m_cpu->set_addrmap(AS_PROGRAM, &news_iop_state::cpu_map);

		NEWS_020_MMU(config, m_mmu, 0);
		m_mmu->set_addrmap(AS_PROGRAM, &news_iop_state::mmu_map);
		m_mmu->set_bus_error_callback(FUNC(news_iop_state::cpu_bus_error));

		RAM(config, m_ram);
		m_ram->set_default_size("8M");
		m_ram->set_extra_options("16M");
		m_ram->set_default_value(0);

		// NEC uPD8253 programmable interval timer
		PIT8253(config, m_interval_timer, 0);
		constexpr XTAL PIT_INPUT_FREQUENCY = XTAL(2'000'000); // Assume same as 1960 for now
		m_interval_timer->set_clk<0>(PIT_INPUT_FREQUENCY);
		m_interval_timer->set_clk<1>(PIT_INPUT_FREQUENCY);
		m_interval_timer->set_clk<2>(PIT_INPUT_FREQUENCY);
		m_interval_timer->out_handler<0>().set(FUNC(news_iop_state::interval_timer_tick));

		// 2x Sharp LH8530A Z8530A-SCC
		SCC8530(config, m_scc_external, (16_MHz_XTAL / 4));
		SCC8530(config, m_scc_peripheral, (16_MHz_XTAL / 4));
		m_scc_external->out_int_callback().set(FUNC(news_iop_state::iop_irq_w<SCC>));
		m_scc_peripheral->out_int_callback().set(FUNC(news_iop_state::iop_irq_w<SCC_PERIPHERAL>));

		// The monitor ROM repurposes the status pins as a serial interface to read the front panel switches and IDROM data
		m_scc_peripheral->out_dtrb_callback().set(FUNC(news_iop_state::handle_dtr));
		m_scc_peripheral->out_rtsb_callback().set(FUNC(news_iop_state::handle_rts));

		// scc channel A
		RS232_PORT(config, m_serial[0], default_rs232_devices, "terminal");
		m_serial[0]->cts_handler().set(m_scc_external, FUNC(z80scc_device::ctsa_w));
		m_serial[0]->dcd_handler().set(m_scc_external, FUNC(z80scc_device::dcda_w));
		m_serial[0]->rxd_handler().set(m_scc_external, FUNC(z80scc_device::rxa_w));
		m_scc_external->out_rtsa_callback().set(m_serial[0], FUNC(rs232_port_device::write_rts));
		m_scc_external->out_txda_callback().set(m_serial[0], FUNC(rs232_port_device::write_txd));

		// scc channel B
		RS232_PORT(config, m_serial[1], default_rs232_devices, nullptr);
		m_serial[1]->cts_handler().set(m_scc_external, FUNC(z80scc_device::ctsb_w));
		m_serial[1]->dcd_handler().set(m_scc_external, FUNC(z80scc_device::dcdb_w));
		m_serial[1]->rxd_handler().set(m_scc_external, FUNC(z80scc_device::rxb_w));
		m_scc_external->out_rtsb_callback().set(m_serial[1], FUNC(rs232_port_device::write_rts));
		m_scc_external->out_txdb_callback().set(m_serial[1], FUNC(rs232_port_device::write_txd));

		// LANCE ethernet controller
		AM7990(config, m_net, 20_MHz_XTAL);
		m_net->intr_out().set(FUNC(news_iop_state::iop_irq_w<LANCE>)).invert();
		m_net->dma_in().set([this] (offs_t offset)
							{ return m_net_ram[(offset >> 1) & 0x1fff]; });
		m_net->dma_out().set([this] (offs_t offset, u16 data, u16 mem_mask)
							 { COMBINE_DATA(&m_net_ram[(offset >> 1) & 0x1fff]); });

		// uPD7265 FDC (Compatible with 765A except it should use Sony/ECMA format by default?)
		UPD765A(config, m_fdc, 4'000'000); // TODO: confirm clock rate
		m_fdc->intrq_wr_callback().set(FUNC(news_iop_state::iop_irq_w<FDCIRQ>));
		m_fdc->drq_wr_callback().set(FUNC(news_iop_state::iop_irq_w<FDCDRQ>));
		FLOPPY_CONNECTOR(config, "fdc:0", "35hd", FLOPPY_35_HD, true, floppy_image_device::default_pc_floppy_formats).enable_sound(false);

		// SCSI bus and devices
		// Most NEWS workstations generally follow this convention (when each given item is present):
		// ID 0-2: Internal hard drive(s)
		// ID 3: Free
		// ID 4: Internal MO drive
		// ID 5: Internal tape drive
		// ID 6: Internal CD-ROM drive
		// ID 7: NEWS (the workstation itself, SCSI initiator)
		// Most Sony tools can be configured to use any ID for anything, but the defaults will generally follow the above
		// So, even for expansion MO, tape drives, etc. it is usually easiest to use the above IDs for assignment
		// Early (pre-OS-3) bootloaders are extremely picky about IDNT data and reported capacity; ensure you are using IDNT data and the matching size reported
		// from an actual CDC drive if dealing with early versions.
		// Note: Only the NWS-891 came with a CD-ROM as a default option, others required an external CD-ROM drive
		NSCSI_BUS(config, "scsi");
		NSCSI_CONNECTOR(config, "scsi:0", news_scsi_devices, "harddisk");
		NSCSI_CONNECTOR(config, "scsi:1", news_scsi_devices, nullptr);
		NSCSI_CONNECTOR(config, "scsi:2", news_scsi_devices, nullptr);
		NSCSI_CONNECTOR(config, "scsi:3", news_scsi_devices, nullptr);
		NSCSI_CONNECTOR(config, "scsi:4", news_scsi_devices, nullptr);
		NSCSI_CONNECTOR(config, "scsi:5", news_scsi_devices, nullptr);
		NSCSI_CONNECTOR(config, "scsi:6", news_scsi_devices, nullptr);

		// AMD Am5380PC SCSI interface
		NSCSI_CONNECTOR(config, "scsi:7").option_set("am5380", NCR5380).machine_config([this] (device_t *device)
		{
			ncr5380_device &adapter = downcast<ncr5380_device &>(*device);
			adapter.irq_handler().set([this] (int state){ m_scsi_dma->irq_w(state); });
			adapter.drq_handler().set(*this, FUNC(news_iop_state::scsi_drq_handler));
		});

		// Virtual device for handling SCSI DMA
		// (workaround for 68k bus access limitiations; normally iopboot's BERR handler would deal with SCSI DMA stuff)
		NEWS_IOP_SCSI_HELPER(config, m_scsi_dma);
		m_scsi_dma->scsi_read_callback().set(m_scsi, FUNC(ncr53c80_device::read));
		m_scsi_dma->scsi_write_callback().set(m_scsi, FUNC(ncr53c80_device::write));
		m_scsi_dma->scsi_dma_read_callback().set(m_scsi, FUNC(ncr53c80_device::dma_r));
		m_scsi_dma->scsi_dma_write_callback().set(m_scsi, FUNC(ncr53c80_device::dma_w));
		m_scsi_dma->iop_halt_callback().set_inputline(m_iop, INPUT_LINE_HALT);
		m_scsi_dma->bus_error_callback().set(FUNC(news_iop_state::scsi_bus_error));
		m_scsi_dma->irq_out_callback().set(FUNC(news_iop_state::iop_irq_w<SCSI_IRQ>));

		// Epson RTC-58321B
		MSM58321(config, m_rtc, 32.768_kHz_XTAL);
		m_rtc->d0_handler().set([this] (int data) { set_rtc_data_bit(0, data); });
		m_rtc->d1_handler().set([this] (int data) { set_rtc_data_bit(1, data); });
		m_rtc->d2_handler().set([this] (int data) { set_rtc_data_bit(2, data); });
		m_rtc->d3_handler().set([this] (int data) { set_rtc_data_bit(3, data); });
	}

	void news_iop_state::iop_autovector_map(address_map &map)
	{
		map(0xfffffff3, 0xfffffff3).lr8(NAME([] ()
											 { return m68000_base_device::autovector(1); }));
		map(0xfffffff5, 0xfffffff5).lr8(NAME([] ()
											 { return m68000_base_device::autovector(2); }));
		map(0xfffffff7, 0xfffffff7).lr8(NAME([] ()
											 { return m68000_base_device::autovector(3); }));
		map(0xfffffff9, 0xfffffff9).lr8(NAME([] ()
											 { return m68000_base_device::autovector(4); }));
		map(0xfffffffb, 0xfffffffb).lr8(NAME([this] ()
											 {
												uint8_t vector = m68000_base_device::autovector(5);
												// TODO: serial port vs peripheral SCC vector priority?
												if (is_iop_irq_set<SCC>())
												{
													vector = m_scc_external->m1_r();
												}
												else if (is_iop_irq_set<SCC_PERIPHERAL>())
												{
													vector = m_scc_peripheral->m1_r();
												}
												return vector;
											 }));
		map(0xfffffffd, 0xfffffffd).lr8(NAME([] ()
											 { return m68000_base_device::autovector(6); }));
		map(0xffffffff, 0xffffffff).lr8(NAME([] ()
											 { return m68000_base_device::autovector(7); }));
	}

	void news_iop_state::nws831(machine_config &config)
	{
		common(config);
	}

	static INPUT_PORTS_START(nws8xx)
		PORT_START("FRONT_PANEL")
			PORT_DIPNAME(0x07, 0x07, "Display")
				PORT_DIPLOCATION("FRONT_PANEL:1,2,3")
				PORT_DIPSETTING(0x07, "Console")
				PORT_DIPSETTING(0x06, "NWB-512")
				PORT_DIPSETTING(0x03, "NWB-225A")
				PORT_DIPSETTING(0x00, "Autoselect")
			PORT_DIPNAME(0x08, 0x08, "Boot Device")
				PORT_DIPLOCATION("FRONT_PANEL:4")
				PORT_DIPSETTING(0x08, "SCSI")
				PORT_DIPSETTING(0x00, "Floppy")
			PORT_DIPNAME(0x10, 0x10, "Automatic Boot")
				PORT_DIPLOCATION("FRONT_PANEL:5")
				PORT_DIPSETTING(0x10, DEF_STR(Off))
				PORT_DIPSETTING(0x00, DEF_STR(On))
			PORT_DIPNAME(0x20, 0x20, "Diagnostic Mode")
				PORT_DIPLOCATION("FRONT_PANEL:6")
				PORT_DIPSETTING(0x20, DEF_STR(Off))
				PORT_DIPSETTING(0x00, DEF_STR(On))
			PORT_DIPUNUSED_DIPLOC(0xc0, 0xc0, "FRONT_PANEL:7,8")
	INPUT_PORTS_END

	ROM_START(nws831)
		// 2x Fujitsu MBM27C256-25 EPROMs
		ROM_REGION32_BE(0x10000, "eprom", 0)
		ROM_SYSTEM_BIOS(0, "nws831", "SONY NET WORK STATION monitor Release 2.1")
		ROM_LOAD16_BYTE("nws831-mbm27c256-ver2p1-h.bin", 0x0000, 0x8000, CRC(11e23140) SHA1(8c87a6198f918a69f611ff7be6497552ead67b31))
		ROM_LOAD16_BYTE("nws831-mbm27c256-ver2p1-l.bin", 0x0001, 0x8000, CRC(1ea72294) SHA1(e7edfad08e46b7dbcca5279457901ba83fbf481f))

		// Fujitsu MB7114L 256 x 4 bit (1K) TTL PROM
		// There is no golden dump of this PROM because it is holds per-system values
		// The front panel addresses the idrom and only uses the bottom two bits of each nibble.
		// The upper two bits per nibble are programmed to ones on my NWS-831
		ROM_REGION32_BE(0x100, "idrom", 0)
		ROM_LOAD("nws831-idrom.bin", 0x0, 0x100, CRC(167de13d) SHA1(3e46392671324e92f1e34ddd8ac3c6d94144b3d2) BAD_DUMP)
	ROM_END

} // anonymous namespace

// Machine definitions
//   YEAR  NAME    P  C  MACHINE INPUT   CLASS           INIT         COMPANY FULLNAME   FLAGS
COMP(1987, nws831, 0, 0, nws831, nws8xx, news_iop_state, init_common, "Sony", "NWS-831", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



news_r3k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * Sony NEWS R3000 single-processor systems.
 *
 * Sources:
 *   - https://github.com/robohack/ucb-csrg-bsd/blob/master/sys/news3400/
 *   - https://www.mmcc.it/resources/docs/NWS-3410_3460_ServiceManual_MMCC.PDF
 *   - https://www.mmcc.it/resources/docs/Sony_NEWS_NWS-3260_ROM_Monitor_User_Guide_r2.pdf
 *   - http://bitsavers.org/pdf/sony/news/Sony_NEWS_Technical_Manual_3ed_199103.pdf
 *
 * TODO:
 *   - LCD controller
 *   - screen timing parameters
 *   - floppy density/eject
 *   - Centronics port
 *   - sound
 *   - other models, including slots/cards
 */

#include "emu.h"

#include "dmac_0448.h"
#include "news_hid.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"
#include "bus/rs232/rs232.h"
#include "cpu/mips/mips1.h"
#include "imagedev/floppy.h"
#include "machine/am79c90.h"
#include "machine/cxd1185.h"
#include "machine/nscsi_bus.h"
#include "machine/ram.h"
#include "machine/timekpr.h"
#include "machine/upd765.h"
#include "machine/z80scc.h"
#include "sound/spkrdev.h"

#include "screen.h"
#include "speaker.h"

#define VERBOSE 0
#include "logmacro.h"


namespace {

class news_r3k_base_state : public driver_device
{
public:
	news_r3k_base_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_ram(*this, "ram")
		, m_dma(*this, "dma")
		, m_rtc(*this, "rtc")
		, m_scc(*this, "scc")
		, m_net(*this, "net")
		, m_fdc(*this, "fdc")
		, m_hid(*this, "hid")
		, m_scsi(*this, "scsi:7:cxd1185")
		, m_serial(*this, "serial%u", 0U)
		, m_scsibus(*this, "scsi")
		, m_led(*this, "led%u", 0U)
	{
	}

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// address maps
	void cpu_map(address_map &map) ATTR_COLD;

	// machine config
	void common(machine_config &config);

public:
	void init_common();

protected:
	void inten_w(offs_t offset, u16 data, u16 mem_mask);
	u16 inten_r() { return m_inten; }
	u16 intst_r() { return m_intst; }
	void intclr_w(offs_t offset, u16 data, u16 mem_mask);

	enum irq_number : unsigned
	{
		EXT3  = 0,
		EXT1  = 1,
		SLOT3 = 2,
		SLOT1 = 3,
		DMA   = 4,
		LANCE = 5,
		SCC   = 6,
		BEEP  = 7,
		CBSY  = 8,
		CFLT  = 9,
		MOUSE = 10,
		KBD   = 11,
		TIMER = 12,
		BERR  = 13,
		ABORT = 14,
		PERR  = 15,
	};
	template <irq_number Number> void irq_w(int state);
	void int_check();

	u32 bus_error();
	void itimer_w(u8 data);
	void itimer(s32 param);
	u8 debug_r() { return m_debug; }
	void debug_w(u8 data);

	// devices
	required_device<r3000a_device> m_cpu;
	required_device<ram_device> m_ram;
	required_device<dmac_0448_device> m_dma;
	required_device<m48t02_device> m_rtc;
	required_device<z80scc_device> m_scc;
	required_device<am7990_device> m_net;
	required_device<upd72067_device> m_fdc;

	required_device<news_hid_hle_device> m_hid;
	required_device<cxd1185_device> m_scsi;

	required_device_array<rs232_port_device, 2> m_serial;
	required_device<nscsi_bus_device> m_scsibus;

	output_finder<4> m_led;

	std::unique_ptr<u16[]> m_net_ram;

	emu_timer *m_itimer = nullptr;

	u16 m_inten = 0;
	u16 m_intst = 0;
	u8 m_debug = 0;

	bool m_int_state[4]{};
};

class nws3260_state : public news_r3k_base_state
{
public:
	nws3260_state(machine_config const &mconfig, device_type type, char const *tag)
		: news_r3k_base_state(mconfig, type, tag)
		, m_lcd(*this, "lcd")
		, m_vram(*this, "vram")
	{
	}

	void nws3260(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

	void nws3260_map(address_map &map) ATTR_COLD;
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect);

	required_device<screen_device> m_lcd;
	required_shared_ptr<u32> m_vram;

	bool m_lcd_enable = false;
	bool m_lcd_dim = false;
};

class news_r3k_desktop_state : public news_r3k_base_state
{
public:
	static constexpr feature_type unemulated_features() { return feature::GRAPHICS; }

	news_r3k_desktop_state(machine_config const &mconfig, device_type type, char const *tag)
		: news_r3k_base_state(mconfig, type, tag)
	{
	}

	void nws3410(machine_config &config);
	void nws3720(machine_config &config);

protected:
	void desktop_cpu_map(address_map &map) ATTR_COLD;
};

void nws3260_state::machine_start()
{
	news_r3k_base_state::machine_start();

	save_item(NAME(m_lcd_enable));
	save_item(NAME(m_lcd_dim));
	m_lcd_enable = false;
	m_lcd_dim = false;
}

void news_r3k_base_state::machine_start()
{
	m_led.resolve();

	m_net_ram = std::make_unique<u16[]>(8192);
	save_pointer(NAME(m_net_ram), 8192);

	save_item(NAME(m_inten));
	save_item(NAME(m_intst));
	save_item(NAME(m_debug));
	save_item(NAME(m_int_state));

	m_itimer = timer_alloc(FUNC(news_r3k_base_state::itimer), this);

	for (bool &int_state : m_int_state)
		int_state = false;

	m_inten = 0;
	m_intst = 0;
}

void news_r3k_base_state::machine_reset()
{
}

void news_r3k_base_state::init_common()
{
	// map the configured ram
	m_cpu->space(0).install_ram(0x00000000, m_ram->mask(), m_ram->pointer());

	// HACK: hardwire the rate until fdc is better understood
	m_fdc->set_rate(500000);

	// HACK: signal floppy density?
	m_scsi->port_w(0x02);
}

void nws3260_state::nws3260_map(address_map &map)
{
	cpu_map(map);
	map(0x10000000, 0x101fffff).rom().region("krom", 0);
	map(0x10000000, 0x10000003).lw32([this] (u32 data) { m_lcd_enable = bool(data); }, "lcd_enable_w");
	map(0x10100000, 0x10100003).lw32([this] (u32 data) { m_lcd_dim = BIT(data, 0); }, "lcd_dim_w");
	map(0x10200000, 0x1021ffff).ram().share("vram").mirror(0xa0000000);
	map(0x1ff60000, 0x1ff6001b).lw8([this] (offs_t offset, u8 data) { LOG("crtc offset %x 0x%02x\n", offset, data); }, "lfbm_crtc_w"); // TODO: HD64646FS
}

void news_r3k_desktop_state::desktop_cpu_map(address_map &map)
{
	cpu_map(map);

	// LCD framebuffer memory regions - without bus errors, the framebuffer probe logic in NEWS-OS will think there is an LCD attached
	// While this doesn't break anything, it does cause the device to be exposed when it isn't present.
	map(0x10000000, 0x1021ffff).r(FUNC(news_r3k_desktop_state::bus_error));
	map(0x1ff50000, 0x1ff6001b).r(FUNC(news_r3k_desktop_state::bus_error));
}

void news_r3k_base_state::cpu_map(address_map &map)
{
	map.unmap_value_high();

	map(0x18000000, 0x18ffffff).r(FUNC(news_r3k_base_state::bus_error));

	map(0x1fc00000, 0x1fc1ffff).rom().region("eprom", 0);
	//map(0x1fc40004, 0x1fc40007).w().umask32(0xff); ??
	// 1fc40007 // power/reboot/PARK?
	map(0x1fc80000, 0x1fc80001).rw(FUNC(news_r3k_base_state::inten_r), FUNC(news_r3k_base_state::inten_w));
	map(0x1fc80002, 0x1fc80003).r(FUNC(news_r3k_base_state::intst_r));
	map(0x1fc80004, 0x1fc80005).w(FUNC(news_r3k_base_state::intclr_w));
	map(0x1fc80006, 0x1fc80006).w(FUNC(news_r3k_base_state::itimer_w));
	// 1fcc0000 // cstrobe?
	// 1fcc0002 // sccstatus0?
	map(0x1fcc0003, 0x1fcc0003).rw(FUNC(news_r3k_base_state::debug_r), FUNC(news_r3k_base_state::debug_w));
	map(0x1fcc0007, 0x1fcc0007).lr8([this] () { return m_scc->m1_r(); }, "sccvect_r");

	map(0x1fd00000, 0x1fd00007).m(m_hid, FUNC(news_hid_hle_device::map));
	map(0x1fd40000, 0x1fd40003).noprw(); // FIXME: ignore buzzer for now

	map(0x1fe00000, 0x1fe0000f).m(m_dma, FUNC(dmac_0448_device::map));
	map(0x1fe00100, 0x1fe0010f).m(m_scsi, FUNC(cxd1185_device::map));
	map(0x1fe00200, 0x1fe00203).m(m_fdc, FUNC(upd72067_device::map));
	map(0x1fe00300, 0x1fe00300).lr8([] () { return 0xff; }, "sound_r"); // HACK: disable sound
	//map(0x1fe00300, 0x1fe00307); // sound
	map(0x1fe40000, 0x1fe40003).portr("SW2");
	//map(0x1fe70000, 0x1fe9ffff).ram(); // ??
	map(0x1fe80000, 0x1fe800ff).rom().region("idrom", 0).mirror(0x0003ff00);
	map(0x1fec0000, 0x1fec0003).rw(m_scc, FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w));

	map(0x1ff40000, 0x1ff407ff).rw(m_rtc, FUNC(m48t02_device::read), FUNC(m48t02_device::write));

	map(0x1ff80000, 0x1ff80003).rw(m_net, FUNC(am7990_device::regs_r), FUNC(am7990_device::regs_w));
	map(0x1ffc0000, 0x1ffc3fff).lrw16(
			[this] (offs_t offset) { return m_net_ram[offset]; }, "net_ram_r",
			[this] (offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_net_ram[offset]); }, "net_ram_w");
}

static INPUT_PORTS_START(nws3260)
	PORT_START("SW2")
	// TODO: other combinations of switches 1-3 may be valid
	PORT_DIPNAME(0x07000000, 0x02000000, "Display") PORT_DIPLOCATION("SW2:1,2,3")
	PORT_DIPSETTING(0x00000000, "Console")
	PORT_DIPSETTING(0x02000000, "LCD")
	PORT_DIPNAME(0x08000000, 0x00000000, "Boot Device") PORT_DIPLOCATION("SW2:4")
	PORT_DIPSETTING(0x00000000, "Disk")
	PORT_DIPSETTING(0x08000000, "Network")
	PORT_DIPNAME(0x10000000, 0x00000000, "Automatic Boot") PORT_DIPLOCATION("SW2:5")
	PORT_DIPSETTING(0x00000000, DEF_STR(Off))
	PORT_DIPSETTING(0x10000000, DEF_STR(On))
	PORT_DIPNAME(0x20000000, 0x00000000, "Diagnostic Mode") PORT_DIPLOCATION("SW2:6")
	PORT_DIPSETTING(0x00000000, DEF_STR(Off))
	PORT_DIPSETTING(0x20000000, DEF_STR(On))
	// TODO: not completely clear what this switch does
	PORT_DIPNAME(0x40000000, 0x00000000, "RAM") PORT_DIPLOCATION("SW2:7")
	PORT_DIPSETTING(0x00000000, "Enabled")
	PORT_DIPSETTING(0x40000000, "Disabled")
	PORT_DIPNAME(0x80000000, 0x00000000, "Console Baud") PORT_DIPLOCATION("SW2:8")
	PORT_DIPSETTING(0x00000000, "9600")
	PORT_DIPSETTING(0x80000000, "1200")

	PORT_START("SW3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNKNOWN)
INPUT_PORTS_END

static INPUT_PORTS_START(nws_r3k_desktop)
	PORT_START("SW2")
	PORT_DIPNAME(0x07000000, 0x02000000, "Console") PORT_DIPLOCATION("SW2:1,2,3")
	PORT_DIPSETTING(0x00000000, "Serial")
	PORT_DIPSETTING(0x02000000, "NWB-252/NWB-253") // NWB-252 (color) or NWB-253 (monochrome) NWS-3400 series bitmap board
	PORT_DIPSETTING(0x07000000, "NWB-514/NWB-251") // NWB-514 (monochrome) or NWB-251 (color) expansion framebuffer
	PORT_DIPNAME(0x08000000, 0x00000000, "Boot Device") PORT_DIPLOCATION("SW2:4")
	PORT_DIPSETTING(0x00000000, "Disk")
	PORT_DIPSETTING(0x08000000, "Network")
	PORT_DIPNAME(0x10000000, 0x00000000, "Automatic Boot") PORT_DIPLOCATION("SW2:5")
	PORT_DIPSETTING(0x00000000, DEF_STR(Off))
	PORT_DIPSETTING(0x10000000, DEF_STR(On))
	PORT_DIPNAME(0x20000000, 0x00000000, "Diagnostic Mode") PORT_DIPLOCATION("SW2:6")
	PORT_DIPSETTING(0x00000000, DEF_STR(Off))
	PORT_DIPSETTING(0x20000000, DEF_STR(On))
	PORT_DIPNAME(0x40000000, 0x00000000, "RAM") PORT_DIPLOCATION("SW2:7") // Controls "No Memory Mode" for testing, forces monitor to use RTC RAM instead
	PORT_DIPSETTING(0x00000000, "Enabled")
	PORT_DIPSETTING(0x40000000, "Disabled")
	PORT_DIPNAME(0x80000000, 0x00000000, "Console Baud") PORT_DIPLOCATION("SW2:8")
	PORT_DIPSETTING(0x00000000, "9600")
	PORT_DIPSETTING(0x80000000, "1200")
INPUT_PORTS_END

u32 nws3260_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect)
{
	if (!m_lcd_enable)
		return 0;

	rgb_t const black = rgb_t::black();
	rgb_t const white = m_lcd_dim ? rgb_t(191, 191, 191) : rgb_t::white();

	u32 const *pixel_pointer = m_vram;

	for (int y = screen.visible_area().min_y; y <= screen.visible_area().max_y; y++)
	{
		for (int x = screen.visible_area().min_x; x <= screen.visible_area().max_x; x += 32)
		{
			u32 const pixel_data = *pixel_pointer++;

			for (unsigned i = 0; i < 32; i++)
				bitmap.pix(y, x + i) = BIT(pixel_data, 31 - i) ? black : white;
		}
	}

	return 0;
}

void news_r3k_base_state::inten_w(offs_t offset, u16 data, u16 mem_mask)
{
	COMBINE_DATA(&m_inten);

	int_check();
}

template <news_r3k_base_state::irq_number Number> void news_r3k_base_state::irq_w(int state)
{
	LOG("irq number %d state %d\n",  Number, state);

	if (state)
		m_intst |= 1U << Number;
	else
		m_intst &= ~(1U << Number);

	int_check();
}

void news_r3k_base_state::intclr_w(offs_t offset, u16 data, u16 mem_mask)
{
	m_intst &= ~(data & mem_mask);

	int_check();
}

void news_r3k_base_state::int_check()
{
	// TODO: assume 44422222 11100000
	static int const int_line[] = { INPUT_LINE_IRQ0, INPUT_LINE_IRQ1, INPUT_LINE_IRQ2, INPUT_LINE_IRQ4 };
	static u16 const int_mask[] = { 0x001f, 0x00e0, 0x1f00, 0xe000 };

	for (unsigned i = 0; i < std::size(m_int_state); i++)
	{
		bool const int_state = m_intst & m_inten & int_mask[i];

		if (m_int_state[i] != int_state)
		{
			m_int_state[i] = int_state;
			m_cpu->set_input_line(int_line[i], int_state);
		}
	}
}

u32 news_r3k_base_state::bus_error()
{
	if (!machine().side_effects_disabled())
		irq_w<BERR>(ASSERT_LINE);

	return 0;
}

void news_r3k_base_state::itimer_w(u8 data)
{
	LOG("itimer_w 0x%02x (%s)\n", data, machine().describe_context());

	// TODO: assume 0xff stops the timer
	u8 const ticks = data + 1;

	m_itimer->adjust(attotime::from_ticks(ticks, 800), 0, attotime::from_ticks(ticks, 800));
}

void news_r3k_base_state::itimer(s32 param)
{
	irq_w<TIMER>(ASSERT_LINE);
}

void news_r3k_base_state::debug_w(u8 data)
{
	/*
	 * The low four bits of this register control the diagnostic LEDs labelled 1-4
	 * with bit 0 correspondig to LED #1, and a 0 value enabling the LED. A non-
	 * exhaustive list of diagnostic codes produced by the PROM follows:
	 *
	 *  4321  Stage
	 *  ...x  EPROM checksum
	 *  ..x.  NVRAM test (byte)
	 *  ..xx  NVRAM test (word)
	 *  .x..  NVRAM test (dword)
	 *  .x.x  read dip-switch SW2
	 *  .xx.  write test 0x1fe70000-1fe9ffff?
	 *  .xxx  address decode
	 *  x...  NVRAM test (dword)
	 *  x..x  RAM sizing
	 *  x.x.  inventory/boot
	 *
	 */
	LOG("debug_w 0x%02x (%s)\n", data, machine().describe_context());

	for (unsigned i = 0; i < 4; i++)
		if (BIT(data, i + 4))
			m_led[i] = BIT(data, i);

	m_debug = data;
}

static void news_scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
	device.option_add("cdrom", NSCSI_CDROM);
}

void news_r3k_base_state::common(machine_config &config)
{
	DMAC_0448(config, m_dma, 0);
	m_dma->set_bus(m_cpu, 0);
	m_dma->out_int_cb().set(FUNC(news_r3k_base_state::irq_w<DMA>));
	m_dma->dma_r_cb<1>().set(m_fdc, FUNC(upd72067_device::dma_r));
	m_dma->dma_w_cb<1>().set(m_fdc, FUNC(upd72067_device::dma_w));
	// TODO: channel 2 audio
	// TODO: channel 3 video

	M48T02(config, m_rtc);

	SCC85C30(config, m_scc, 4.9152_MHz_XTAL);
	m_scc->out_int_callback().set(FUNC(news_r3k_base_state::irq_w<SCC>));

	// scc channel A
	RS232_PORT(config, m_serial[0], default_rs232_devices, nullptr);
	m_serial[0]->cts_handler().set(m_scc, FUNC(z80scc_device::ctsa_w));
	m_serial[0]->dcd_handler().set(m_scc, FUNC(z80scc_device::dcda_w));
	m_serial[0]->rxd_handler().set(m_scc, FUNC(z80scc_device::rxa_w));
	m_scc->out_rtsa_callback().set(m_serial[0], FUNC(rs232_port_device::write_rts));
	m_scc->out_txda_callback().set(m_serial[0], FUNC(rs232_port_device::write_txd));

	// scc channel B
	RS232_PORT(config, m_serial[1], default_rs232_devices, nullptr);
	m_serial[1]->cts_handler().set(m_scc, FUNC(z80scc_device::ctsb_w));
	m_serial[1]->dcd_handler().set(m_scc, FUNC(z80scc_device::dcdb_w));
	m_serial[1]->rxd_handler().set(m_scc, FUNC(z80scc_device::rxb_w));
	m_scc->out_rtsb_callback().set(m_serial[1], FUNC(rs232_port_device::write_rts));
	m_scc->out_txdb_callback().set(m_serial[1], FUNC(rs232_port_device::write_txd));

	AM7990(config, m_net);
	m_net->intr_out().set(FUNC(news_r3k_base_state::irq_w<LANCE>)).invert();
	m_net->dma_in().set([this](offs_t offset) { return m_net_ram[offset >> 1]; });
	m_net->dma_out().set([this](offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_net_ram[offset >> 1]); });

	UPD72067(config, m_fdc, 16_MHz_XTAL);
	m_fdc->intrq_wr_callback().set(m_dma, FUNC(dmac_0448_device::irq<1>));
	m_fdc->drq_wr_callback().set(m_dma, FUNC(dmac_0448_device::drq<1>));
	FLOPPY_CONNECTOR(config, "fdc:0", "35hd", FLOPPY_35_HD, true, floppy_image_device::default_pc_floppy_formats).enable_sound(false);

	// scsi bus and devices
	NSCSI_BUS(config, m_scsibus);
	// inquiry content for hard disk is "HITACHI DK312C          CS01"
	// HD CHDs will be treated as MO disks using the inquiry content "SONY    SMO-C501        1.00"
	// The CHS for converting a raw MO dump for 282MByte per side disks is 18678,1,31 per NEWS-OS 4's disktab file
	NSCSI_CONNECTOR(config, "scsi:0", news_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:1", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", news_scsi_devices, nullptr);

	// scsi host adapter
	NSCSI_CONNECTOR(config, "scsi:7").option_set("cxd1185", CXD1185).clock(16_MHz_XTAL).machine_config(
		[this] (device_t *device)
		{
			cxd1185_device &adapter = downcast<cxd1185_device &>(*device);

			adapter.irq_out_cb().set(m_dma, FUNC(dmac_0448_device::irq<0>));
			adapter.drq_out_cb().set(m_dma, FUNC(dmac_0448_device::drq<0>));
			adapter.port_out_cb().set(
				[this] (u8 data)
				{
					LOG("floppy %s\n", BIT(data, 0) ? "mount" : "eject");
				});

			subdevice<dmac_0448_device>(":dma")->dma_r_cb<0>().set(adapter, FUNC(cxd1185_device::dma_r));
			subdevice<dmac_0448_device>(":dma")->dma_w_cb<0>().set(adapter, FUNC(cxd1185_device::dma_w));
		});

	NEWS_HID_HLE(config, m_hid);
	m_hid->irq_out<news_hid_hle_device::KEYBOARD>().set(FUNC(news_r3k_base_state::irq_w<KBD>));
	m_hid->irq_out<news_hid_hle_device::MOUSE>().set(FUNC(news_r3k_base_state::irq_w<MOUSE>));

	SOFTWARE_LIST(config, "software_list").set_original("sony_news").set_filter("RISC,NWS3000");
}

void nws3260_state::nws3260(machine_config &config)
{
	R3000A(config, m_cpu, 20_MHz_XTAL, 32768, 32768);
	m_cpu->set_fpu(mips1_device_base::MIPS_R3010Av4);
	m_cpu->set_addrmap(AS_PROGRAM, &nws3260_state::nws3260_map);

	// 3 banks of 4x30-pin SIMMs with parity, first bank is soldered
	RAM(config, m_ram);
	m_ram->set_default_size("16M");
	// TODO: confirm each bank supports 4x1M or 4x4M
	m_ram->set_extra_options("4M,8M,12M,20M,24M,32M,36M,48M");
	common(config);

	// Integrated LCD panel
	SCREEN(config, m_lcd, SCREEN_TYPE_LCD);
	m_lcd->set_raw(52416000, 1120, 0, 1120, 780, 0, 780);
	m_lcd->set_screen_update(FUNC(nws3260_state::screen_update));
}

void news_r3k_desktop_state::nws3410(machine_config &config)
{
	R3000A(config, m_cpu, 20_MHz_XTAL, 65536, 65536);
	m_cpu->set_fpu(mips1_device_base::MIPS_R3010Av4);
	m_cpu->set_addrmap(AS_PROGRAM, &news_r3k_desktop_state::desktop_cpu_map);

	// Per the service manual, one or more NWA-029 4MB expansion kits can be used to increase from the base 8M up to 16M
	RAM(config, m_ram);
	m_ram->set_default_size("8M");
	m_ram->set_extra_options("12M,16M");
	common(config);

	m_serial[0]->set_default_option("terminal"); // No framebuffer emulation yet
}

void news_r3k_desktop_state::nws3720(machine_config &config)
{
	R3000A(config, m_cpu, 20_MHz_XTAL, 65536, 65536);
	m_cpu->set_fpu(mips1_device_base::MIPS_R3010Av4);
	m_cpu->set_addrmap(AS_PROGRAM, &news_r3k_desktop_state::desktop_cpu_map);

	// 16MB expandable to 128MB (unknown increments)
	RAM(config, m_ram);
	m_ram->set_default_size("16M");
	m_ram->set_extra_options("128MB");
	common(config);

	m_serial[0]->set_default_option("terminal"); // No framebuffer emulation yet
}

ROM_START(nws3260)
	ROM_REGION32_BE(0x20000, "eprom", 0)
	ROM_SYSTEM_BIOS(0, "nws3260", "SONY NET WORK STATION R3000 Monitor Release 2.0A")
	ROMX_LOAD("mpu-16__ver.2.0a__1990_sony.ic64", 0x00000, 0x20000, CRC(61222991) SHA1(076fab0ad0682cd7dacc7094e42efe8558cbaaa1), ROM_BIOS(0))

	// 2 x MB834200A-20 (4Mb mask ROM)
	ROM_REGION32_BE(0x200000, "krom", ROMREGION_ERASEFF)
	ROM_LOAD64_WORD("051_aa.ic109", 0x00000, 0x80000, CRC(1411cbcb) SHA1(793394cd3919034f85bfb015d6d3c504f83b6626))
	ROM_LOAD64_WORD("052_aa.ic110", 0x00004, 0x80000, CRC(df0f39da) SHA1(076881da022a3fe6731de0ead217285293c25dc7))

	/*
	 * This is probably a 4-bit device: only the low 4 bits in each location
	 * are used, and are merged together into bytes when copied into RAM, with
	 * the most-significant bits at the lower address. The sum of resulting
	 * big-endian 32-bit words must be zero.
	 *
	 * offset  purpose
	 *  0x00   magic number (0x0f 0x0f)
	 *  0x10   ethernet mac address (low 4 bits of 12 bytes)
	 *  0x28   machine identification (low 4 bits of 8 bytes)
	 *  0x60   model number (null-terminated string)
	 */
	ROM_REGION32_BE(0x100, "idrom", 0)
	ROM_LOAD("idrom.bin", 0x000, 0x100, CRC(17a3d9c6) SHA1(d300e6908210f540951211802c38ad7f8037aa15) BAD_DUMP)
ROM_END

ROM_START(nws3410)
	ROM_REGION32_BE(0x20000, "eprom", 0)
	ROM_SYSTEM_BIOS(0, "nws3410", "SONY NET WORK STATION R3000 Monitor Release 2.0")
	ROMX_LOAD("sony_nws-3410_mpu-12_v2_rom.bin", 0x00000, 0x20000, CRC(48a726c4) SHA1(5c6e9e6bccaaa3d63bc136355a436c17c49c9876), ROM_BIOS(0))

	ROM_REGION32_BE(0x100, "idrom", 0)
	ROM_LOAD("idrom.bin", 0x000, 0x100, CRC(661e2516) SHA1(f0dca34174747321dad6f48c466e1c549b797d2e) BAD_DUMP)
ROM_END

ROM_START(nws3720)
	ROM_REGION32_BE(0x20000, "eprom", 0)
	ROM_SYSTEM_BIOS(0, "nws3720", "SONY NET WORK STATION R3000 Monitor Release 2.0A")
	ROMX_LOAD("sony_nws-3720.bin", 0x00000, 0x20000, CRC(61222991) SHA1(076fab0ad0682cd7dacc7094e42efe8558cbaaa1), ROM_BIOS(0))

	ROM_REGION32_BE(0x100, "idrom", 0)
	ROM_LOAD("idrom.bin", 0x000, 0x100, CRC(6ec5860e) SHA1(612d2c2f149b34551b5fd9392dd6f9b1612417b5) BAD_DUMP)
ROM_END

} // anonymous namespace


/*   YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT            CLASS                   INIT         COMPANY  FULLNAME    FLAGS */
COMP(1991, nws3260, 0,       0,      nws3260, nws3260,         nws3260_state,          init_common, "Sony",  "NWS-3260", MACHINE_NO_SOUND)
COMP(1991, nws3410, 0,       0,      nws3410, nws_r3k_desktop, news_r3k_desktop_state, init_common, "Sony",  "NWS-3410", MACHINE_NO_SOUND)
COMP(1991, nws3720, 0,       0,      nws3720, nws_r3k_desktop, news_r3k_desktop_state, init_common, "Sony",  "NWS-3720", MACHINE_NO_SOUND)



news_r4k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Brice Onken
// thanks-to:Patrick Mackinlay,Olivier Galibert,Tsubai Masanari

/*
 * Sony NEWS R4000/4400 APbus-based workstations
 *
 * Sources and more information:
 *   - http://ozuma.o.oo7.jp/nws5000x.htm
 *   - https://katsu.watanabe.name/doc/sonynews/
 *   - https://web.archive.org/web/20170202100940/www3.videa.or.jp/NEWS/
 *   - https://github.com/NetBSD/src/tree/trunk/sys/arch/newsmips
 *   - https://web.archive.org/web/19970713173157/http%3A%2F%2Fwww1.sony.co.jp%2FOCMP%2F
 *   - https://github.com/briceonk/news-os
 *
 *  CPU configuration:
 *  - CPU card has a 75MHz crystal, multiplier (if any) TBD
 *  - PRId = 0x450
 *  - config register = 0x1081E4BF
 *      [31]    CM = 0 (MC mode off)
 *      [30:28] EC = 001 (clock frequency divided by 3)
 *      [27:24] EP = 00000 (doubleword every cycle)
 *      [23:22] SB = 10 (16 word scache line size)
 *      [21]    SS = 0 (unified scache)
 *      [20]    SW = 0 (128-bit data path to scache)
 *      [19:18] EW = 0 (64-bit system port width)
 *      [17]    SC = 0 (scache present)
 *      [16]    SM = 1 (Dirty Shared state disabled)
 *      [15]    BE = 1 (Big endian)
 *      [14]    EM = 1 (Parity mode)
 *      [13]    EB = 1 (Sub-block ordering)
 *      [12]    Reserved (0)
 *      [11:9]  IC = 2 (16 KByte icache)
 *      [8:6]   DC = 2 (16 KByte dcache)
 *      Per page 90 of the R4400 user guide, the following bits ([5:0]) are mutable by software at runtime.
 *      [5]     IB = 1 (32 byte icache line)
 *      [4]     DB = 1 (32 byte icache line)
 *      [3]     CU = 1 (SC uses cacheable coherent update on write)
 *      [2:0]   K0 = 7 (cache coherency algo, 7 is reserved)
 *  - Known R4400 config differences between this driver and the physical platform:
 *      - emulated R4400 sets revision to 40 instead of 50. The user manual warns against using the revision field of PRId
 *        in software, so hopefully that won't cause any deltas in behavior before that can be configured.
 *      - emulated SM (Dirty Shared state) is on by default - however, is SM actually being emulated?
 *      - emulated CU and K0 are all 0 instead of all 1 like on the physical platform. Unlike SM, software can set these.
 *      - In general, the secondary cache isn't emulated, which might influence bits 3:0 of the config register.
 *
 *  General Emulation Status (major chips only, there are additional smaller chips including CPLDs on the boards)
 *  CPU card:
 *   - MIPS R4400: emulated, with the caveats above
 *   - 10x Motorola MCM67A618FN12 SRAMs (secondary cache): partially emulated
 *     - Tag manipulation is implemented. This seems to be enough for NEWS-OS 4 to work.
 *  Motherboard:
 *   - Sony CXD8490G, CXD8491G, CXD8492G, CXD8489G (unknown ASICs): not emulated
 *   - Main memory: partially emulated (memory controller is unknown and barely emulated - memory configurations other than 64MB don't work at the moment)
 *  I/O board:
 *   - Sony CXD8409Q Parallel Interface: not emulated
 *   - National Semi PC8477B Floppy Controller: emulated (-A differences unemulated but seem to be unneeded)
 *   - Zilog Z8523010VSC ESCC serial interface: emulated (see following)
 *   - Sony CXD8421Q WSC-ESCC1 serial APbus interface controller: skeleton (ESCC connections, probably DMA, APbus interface, etc. handled by this chip)
 *   - 2x Sony CXD8442Q WSC-FIFO APbus FIFO/interface chips: partially emulated (handles APbus connections and DMA for sound, floppy, etc.)
 *   - National Semi DP83932B-VF SONIC Ethernet controller: emulated
 *   - Sony CXD8452AQ WSC-SONIC3 SONIC Ethernet APbus interface controller: mostly(?) emulated
 *   - Sony CXD8418Q WSC-PARK3: not fully emulated, but some of the general platform functions may come from this chip (most likely a gate array based on what the PARK2 was in older gen NEWS systems)
 *   - Sony CXD8403Q DMAC3Q DMA controller: Partially emulated
 *   - 2x HP 1TV3-0302 SPIFI3 SCSI controllers: Partially emulated, only works with the monitor ROM and NEWS-OS 4 right now.
 *   - ST Micro M58T02-150PC1 Timekeeper RAM: emulated
 *  DSC-39 XB Framebuffer/video card:
 *   - Sony CXD8486Q XB: not emulated (most likely APbus interface)
 *   - 16x NEC D482235G5 Dual Port Graphics Buffers: not emulated
 *   - Brooktree Bt468KG220 RAMDAC: not emulated
 *
 *  Known issues:
 *  - Monitor ROM command `ss -r` doesn't show most register values. Not sure what mechanism the APmonitor uses to get the register dump.
 *    (TLB dump is also broken, but unlike the register dump, the TLB dump is broken on the real NWS-5000X too.
 *     Use the `mp` monitor command instead for a working version that shows the TLB correctly, both on real hardware and in emulation)
 *  - Reboot/halt+boot fails because the ESCC FIFO isn't reset properly. For now, the emulator must be hard reset.
 *    It prints out a message (`esccf0: ai->ai_addr points AProm`) and then tries to allocate and use a different FIFO (which fails)
 *  - The NetBSD installer has some weird issues (selects SCSI ID 0 regardless of the ID, some serial weirdness that NEWS-OS doesn't have, etc)
 *
 *  TODO (in no particular order):
 *  - More complete floppy support (only supports floppy boot at the moment)
 *  - NetBSD SCSI support (mostly/entirely changes needed in the SPIFI driver)
 *  - Sound (lots of undocumented hardware)
 *  - Parallel I/O
 *  - Framebuffer (and remaining kb/ms support)
 *  - APbus expansion slots
 *  - Triage and fix the known issues mentioned above
 *  - Full save state support
 */

#include "emu.h"

#include "cxd8442q.h"
#include "cxd8452aq.h"
#include "dmac3.h"
#include "news_hid.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"
#include "bus/rs232/rs232.h"
#include "cpu/mips/r4000.h"
#include "imagedev/floppy.h"
#include "machine/dp83932c.h"
#include "machine/nscsi_bus.h"
#include "machine/ram.h"
#include "machine/spifi3.h"
#include "machine/timekpr.h"
#include "machine/upd765.h"
#include "machine/z80scc.h"

#define LOG_INTERRUPT (1U << 1)
#define LOG_ALL_INTERRUPT (1U << 2)
#define LOG_LED (1U << 3)
#define LOG_MEMORY (1U << 4)
#define LOG_APBUS (1U << 5)
#define LOG_ESCC (1U << 6)

#define NEWS_R4K_INFO (LOG_LED)
#define NEWS_R4K_DEBUG (NEWS_R4K_INFO | LOG_GENERAL | LOG_INTERRUPT)
#define NEWS_R4K_TRACE (NEWS_R4K_DEBUG | LOG_ALL_INTERRUPT | LOG_APBUS)
#define NEWS_R4K_MAX (NEWS_R4K_TRACE | LOG_MEMORY | LOG_ESCC)

#define VERBOSE NEWS_R4K_INFO

#include "logmacro.h"

namespace {

class news_r4k_state : public driver_device
{
public:
	static constexpr feature_type unemulated_features() { return feature::GRAPHICS; }

	news_r4k_state(machine_config const &mconfig, device_type type, char const *tag) :
		driver_device(mconfig, type, tag),
		m_cpu(*this, "cpu"),
		m_ram(*this, "ram"),
		m_dma_ram(*this, "dmaram"),
		m_net_ram(*this, "netram"),
		m_rtc(*this, "rtc"),
		m_escc(*this, "escc"),
		m_serial(*this, "serial%u", 0U),
		m_fifo0(*this, "apfifo0"),
		m_fifo1(*this, "apfifo1"),
		m_sonic3(*this, "sonic3"),
		m_sonic(*this, "sonic"),
		m_fdc(*this, "fdc"),
		m_hid(*this, "hid"),
		m_dmac(*this, "dmac"),
		m_scsi0(*this, "scsi0:7:spifi3"),
		m_scsi1(*this, "scsi1:7:spifi3"),
		m_scsibus0(*this, "scsi0"),
		m_scsibus1(*this, "scsi1"),
		m_dip_switch(*this, "FRONT_PANEL"),
		m_led(*this, "led%u", 0U)
	{
	}

	void nws5000x(machine_config &config);

protected:

	enum irq0_number : uint32_t
	{
		DMAC = 0x01,
		SONIC = 0x02,
		FDC = 0x10
	};

	enum irq1_number : uint32_t
	{
		KBD = 0x01,
		ESCC = 0x02,
		AUDIO0 = 0x04,
		AUDIO1 = 0x08,
		PARALLEL = 0x20,
		FB = 0x80
	};

	enum irq2_number : uint32_t
	{
		TIMER0 = 0x01,
		TIMER1 = 0x02
	};

	enum irq4_number : uint32_t
	{
		APBUS = 0x01
	};

	enum escc_channel
	{
		CHA,
		CHB
	};

	enum APBUS_PTE_MASKS
	{
		ENTRY_VALID = 0x80000000,
		ENTRY_COHERENT = 0x40000000,
		ENTRY_PAD = 0x3FF00000,
		ENTRY_PAD_SHIFT = 20,
		ENTRY_PFNUM = 0xFFFFF
	};

	// APbus DMA page table entry structure
	// See https://github.com/NetBSD/src/blob/faa527952534272f8f4737347d9fc8c38e070c9c/sys/arch/newsmips/apbus/dmac3reg.h#L71
	struct apbus_pte
	{
		// Unused
		uint32_t pad;
		// Entry is OK to use
		bool valid;
		// DMA coherence enabled (don't care for emulation??)
		bool coherent;
		// Unused
		uint32_t pad2;
		// Page number (upper 20 bits of physical address)
		uint32_t pfnum;
	};

	// Interrupts and other platform state
	bool m_ram_map_shift = false;
	bool m_int_state[6] = {false, false, false, false, false, false};
	uint32_t m_inten[6] = {0, 0, 0, 0, 0, 0};
	uint32_t m_intst[6] = {0, 0, 0, 0, 0, 0};
	uint32_t escc1_int_status = 0;
	memory_access<64, 3, 0, ENDIANNESS_BIG>::cache m_main_cache;

	// Freerun timer (1us period)
	// NetBSD source code corroborates the period (https://github.com/NetBSD/src/blob/229cf3aa2cda57ba5f0c244a75ae83090e59c716/sys/arch/newsmips/newsmips/news5000.c#L259)
	emu_timer *m_freerun_timer;
	uint32_t m_freerun_timer_val;

	// Hardware timer TIMER0 (10ms period)
	// TODO: Is this programmable somehow? 100Hz is what the NetBSD kernel expects.
	emu_timer *m_timer0_timer;

	// MIPS R4400 CPU
	required_device<r4400_device> m_cpu;

	// Memory
	required_device<ram_device> m_ram;
	required_device<ram_device> m_dma_ram;
	required_device<ram_device> m_net_ram;

	// ST Micro M48T02 Timekeeper NVRAM + RTC
	required_device<m48t02_device> m_rtc;

	// Zilog ESCC and Sony CXD8421Q ESCC1 serial controller
	required_device<z80scc_device> m_escc;
	required_device_array<rs232_port_device, 2> m_serial;

	// Sony CXD8442Q APbus FIFOs
	required_device<cxd8442q_device> m_fifo0;
	required_device<cxd8442q_device> m_fifo1;

	// Sony CXD8452AQ WSC-SONIC3 APbus interface
	required_device<cxd8452aq_device> m_sonic3;

	// SONIC ethernet controller
	required_device<dp83932c_device> m_sonic;

	// National Semiconductor PC8477B floppy controller
	required_device<pc8477b_device> m_fdc;

	// NEWS keyboard and mouse
	required_device<news_hid_hle_device> m_hid;

	// DMAC3 DMA controller
	required_device<dmac3_device> m_dmac;

	// HP SPIFI3 SCSI controllers
	required_device<spifi3_device> m_scsi0;
	required_device<spifi3_device> m_scsi1;
	required_device<nscsi_bus_device> m_scsibus0;
	required_device<nscsi_bus_device> m_scsibus1;

	// Front panel
	required_ioport m_dip_switch;
	output_finder<6> m_led;

	// Constants
	static constexpr uint32_t ICACHE_SIZE = 0x4000;
	static constexpr uint32_t DCACHE_SIZE = 0x4000;
	static constexpr uint32_t SCACHE_SIZE = 0x100000;
	static constexpr int FREERUN_FREQUENCY = 1000000; // Hz
	static constexpr int TIMER0_FREQUENCY = 100;      // Hz
	static constexpr uint32_t APBUS_DMA_MAP_ADDRESS = 0x14c20000;
	static constexpr uint32_t APBUS_DMA_MAP_RAM_SIZE = 0x20000; // 128 kibibytes
	static constexpr const char* MAIN_MEMORY_DEFAULT = "64M";
	static constexpr int interrupt_map[6] = {0, 1, 2, 3, 4, 5};

	static constexpr std::string_view LED_MAP[6] = {"LED_POWER", "LED_DISK", "LED_FLOPPY", "LED_SEC", "LED_NET", "LED_CD"};
	std::map<offs_t, std::string_view> m_apbus_cmd_decoder
	{
		{0x0c, "INTMSK"},   // Interrupt mask
		{0x14, "INSTST"},   // Interrupt status
		{0x1c, "BER_ADDR"}, // Bus error address
		{0x34, "CFG_CTRL"}, // config control
		{0x5c, "DER_A"},    // DMA error address
		{0x6c, "DER_S"},    // DMA error slot
		{0x84, "DMA"}       // unmapped DMA coherency (?)
	};

	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void machine_common(machine_config &config);

	// address maps
	void cpu_map(address_map &map) ATTR_COLD;
	void sonic3_map(address_map &map) ATTR_COLD;
	void cpu_map_main_memory(address_map &map) ATTR_COLD;
	void cpu_map_debug(address_map &map) ATTR_COLD;

	// Interrupts
	// See news5000 section of https://github.com/NetBSD/src/blob/trunk/sys/arch/newsmips/include/adrsmap.h
	void inten_w(offs_t offset, uint32_t data);
	uint32_t inten_r(offs_t offset);
	uint32_t intst_r(offs_t offset);
	void intclr_w(offs_t offset, uint32_t data);
	void generic_irq_w(uint32_t irq, uint32_t mask, int state);

	template <irq0_number Number>
	void irq_w(int state) { generic_irq_w(0, Number, state); }
	template <irq1_number Number>
	void irq_w(int state) { generic_irq_w(1, Number, state); }
	template <irq2_number Number>
	void irq_w(int state) { generic_irq_w(2, Number, state); }
	template <irq4_number Number>
	void irq_w(int state) { generic_irq_w(4, Number, state); }
	void int_check();

	void led_state_w(offs_t offset, uint32_t data);
	uint64_t front_panel_r(offs_t offset);

	TIMER_CALLBACK_MEMBER(freerun_clock);
	uint32_t freerun_r(offs_t offset);
	void freerun_w(offs_t offset, uint32_t data);

	TIMER_CALLBACK_MEMBER(timer0_clock);
	void timer0_w(offs_t offset, uint32_t data);

	// APbus control (should be split into a device eventually)
	uint8_t apbus_cmd_r(offs_t offset);
	void apbus_cmd_w(offs_t offset, uint32_t data);
	uint32_t apbus_virt_to_phys(uint32_t v_address);

	// RAM accessors
	uint8_t ram_r(offs_t offset);
	void ram_w(offs_t offset, uint8_t data);

	// CXD8421Q glue logic and secondary control for the ESCC
	// This belongs in a separate device if the other features are ever implemented.
	uint32_t escc1_int_status_r(offs_t offset);
	void escc1_int_status_w(offs_t offset, uint32_t data);
	uint32_t escc_ch_read(escc_channel channel, offs_t offset);
	void escc_ch_write(escc_channel channel, offs_t offset, uint32_t data);
	template <escc_channel Channel>
	uint32_t escc_ch_read(offs_t offset) { return escc_ch_read(Channel, offset); }
	template <escc_channel Channel>
	void escc_ch_write(offs_t offset, uint32_t data) { escc_ch_write(Channel, offset, data); }
};

/*
 * news_scsi_devices
 *
 * Declares the SCSI devices supported by this driver.
 */
static void news_scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
	device.option_add("cdrom", NSCSI_CDROM_NEWS);
}

/*
 * machine_common
 *
 * Creates an emulated R4400-based NEWS platform.
 */
void news_r4k_state::machine_common(machine_config &config)
{
	// TODO: clock divisor support (config[30:28] = 0xb001) in r4000.cpp
	R4400(config, m_cpu, 75_MHz_XTAL, true, SCACHE_SIZE, 0x40);
	m_cpu->set_addrmap(AS_PROGRAM, &news_r4k_state::cpu_map);

	RAM(config, m_ram);
	m_ram->set_default_size(MAIN_MEMORY_DEFAULT);

	RAM(config, m_dma_ram);
	m_dma_ram->set_default_size("128KB");

	RAM(config, m_net_ram);
	m_net_ram->set_default_size("32KB");

	M48T02(config, m_rtc);

	SCC85230(config, m_escc, 9.8304_MHz_XTAL);

	RS232_PORT(config, m_serial[0], default_rs232_devices, "terminal");
	m_serial[0]->cts_handler().set(m_escc, FUNC(z80scc_device::ctsa_w));
	m_serial[0]->dcd_handler().set(m_escc, FUNC(z80scc_device::dcda_w));
	m_serial[0]->rxd_handler().set(m_escc, FUNC(z80scc_device::rxa_w));
	m_escc->out_rtsa_callback().set(m_serial[0], FUNC(rs232_port_device::write_rts));
	m_escc->out_txda_callback().set(m_serial[0], FUNC(rs232_port_device::write_txd));
	m_escc->out_dtra_callback().set(m_serial[0], FUNC(rs232_port_device::write_dtr));

	RS232_PORT(config, m_serial[1], default_rs232_devices, nullptr);
	m_serial[1]->cts_handler().set(m_escc, FUNC(z80scc_device::ctsb_w));
	m_serial[1]->dcd_handler().set(m_escc, FUNC(z80scc_device::dcdb_w));
	m_serial[1]->rxd_handler().set(m_escc, FUNC(z80scc_device::rxb_w));
	m_escc->out_rtsb_callback().set(m_serial[1], FUNC(rs232_port_device::write_rts));
	m_escc->out_txdb_callback().set(m_serial[1], FUNC(rs232_port_device::write_txd));
	m_escc->out_dtrb_callback().set(m_serial[1], FUNC(rs232_port_device::write_dtr));
	m_escc->out_int_callback().set(FUNC(news_r4k_state::irq_w<ESCC>));

	CXD8442Q(config, m_fifo0, 0);
	CXD8442Q(config, m_fifo1, 0);

	// Reverse polarity for ESCC DMA signals
	m_escc->out_dtra_callback().set(
			[this] (int status)
			{ m_fifo1->drq_w<cxd8442q_device::fifo_channel_number::CH2>(!status); });
	m_escc->out_wreqa_callback().set(
			[this] (int status)
			{ m_fifo1->drq_w<cxd8442q_device::fifo_channel_number::CH3>(!status); });
	m_fifo1->dma_w_cb<cxd8442q_device::fifo_channel_number::CH2>().set(
			[this] (uint32_t data)
			{ m_escc->da_w(0, data); });
	m_fifo1->dma_r_cb<cxd8442q_device::fifo_channel_number::CH3>().set(
			[this] ()
			{ return m_escc->da_r(0); });
	m_fifo1->out_int_callback().set(
			[this] (int status)
			{
				// Not sure if this is the actual mapping, or if sending any level 0 interrupt causes
				// NEWS-OS to scan the ESCC and FIFO registers. It seems to work OK, but should be
				// revisited if the exact details can be worked out.
				generic_irq_w(0, 0x4, status);
				escc1_int_status = status ? 0x8 : 0x0; // guess
			});

	CXD8452AQ(config, m_sonic3, 0);
	m_sonic3->set_addrmap(0, &news_r4k_state::sonic3_map);
	m_sonic3->irq_out().set(FUNC(news_r4k_state::irq_w<irq0_number::SONIC>));
	m_sonic3->set_bus(m_cpu, 0);
	m_sonic3->set_apbus_address_translator(FUNC(news_r4k_state::apbus_virt_to_phys));

	// TODO: actual clock frequency - there is a 20MHz crystal nearby on the board, but traces need to be confirmed
	DP83932C(config, m_sonic, 20'000'000);
	m_sonic->out_int_cb().set(m_sonic3, FUNC(cxd8452aq_device::irq_w));
	m_sonic->set_bus(m_sonic3, 1);

	// Unlike 68k and R3000 NEWS machines, the keyboard and mouse seem to share an interrupt
	// See https://github.com/NetBSD/src/blob/trunk/sys/arch/newsmips/apbus/ms_ap.c#L103
	// where the mouse interrupt handler is initialized using the Keyboard interrupt.
	NEWS_HID_HLE(config, m_hid);
	m_hid->irq_out<news_hid_hle_device::KEYBOARD>().set(FUNC(news_r4k_state::irq_w<KBD>));
	m_hid->irq_out<news_hid_hle_device::MOUSE>().set(FUNC(news_r4k_state::irq_w<KBD>));

	PC8477B(config, m_fdc, 24_MHz_XTAL, pc8477b_device::mode_t::PS2);
	FLOPPY_CONNECTOR(config, "fdc:0", "35hd", FLOPPY_35_HD, true, floppy_image_device::default_pc_floppy_formats).enable_sound(false);

	// Note - FDC IRQ might go through the FIFO first. Might need to be updated in the future.
	m_fdc->intrq_wr_callback().set(FUNC(news_r4k_state::irq_w<irq0_number::FDC>));
	m_fdc->drq_wr_callback().set(
			[this] (int status)
			{ m_fifo0->drq_w<cxd8442q_device::fifo_channel_number::CH2>(status); });
	m_fifo0->dma_r_cb<cxd8442q_device::fifo_channel_number::CH2>().set(
			[this] ()
			{ return uint32_t(m_fdc->dma_r()); });

	DMAC3(config, m_dmac, 0);
	m_dmac->set_apbus_address_translator(FUNC(news_r4k_state::apbus_virt_to_phys));
	m_dmac->set_bus(m_cpu, 0);
	m_dmac->irq_out().set(FUNC(news_r4k_state::irq_w<DMAC>));

	NSCSI_BUS(config, m_scsibus0);
	NSCSI_BUS(config, m_scsibus1);

	NSCSI_CONNECTOR(config, "scsi0:0", news_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi0:1", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:2", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:3", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:4", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:5", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi0:6", news_scsi_devices, "cdrom");

	NSCSI_CONNECTOR(config, "scsi1:0", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:1", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:2", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:3", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:4", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:5", news_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi1:6", news_scsi_devices, nullptr);

	// TODO: Actual SPIFI3 clock frequency
	NSCSI_CONNECTOR(config, "scsi0:7").option_set("spifi3", SPIFI3)
		.clock(16'000'000)
		.machine_config(
			[this](device_t *device)
			{
				spifi3_device &adapter = dynamic_cast<spifi3_device &>(*device);
				adapter.irq_handler_cb().set(m_dmac, FUNC(dmac3_device::irq_w<dmac3_device::CTRL0>));
				adapter.drq_handler_cb().set(m_dmac, FUNC(dmac3_device::drq_w<dmac3_device::CTRL0>));
			});
	NSCSI_CONNECTOR(config, "scsi1:7").option_set("spifi3", SPIFI3)
		.clock(16'000'000)
		.machine_config(
			[this](device_t *device)
			{
				spifi3_device &adapter = dynamic_cast<spifi3_device &>(*device);
				adapter.irq_handler_cb().set(m_dmac, FUNC(dmac3_device::irq_w<dmac3_device::CTRL1>));
				adapter.drq_handler_cb().set(m_dmac, FUNC(dmac3_device::drq_w<dmac3_device::CTRL1>));
			});

	m_dmac->dma_r_cb<dmac3_device::CTRL0>().set(m_scsi0, FUNC(spifi3_device::dma_r));
	m_dmac->dma_w_cb<dmac3_device::CTRL0>().set(m_scsi0, FUNC(spifi3_device::dma_w));
	m_dmac->dma_r_cb<dmac3_device::CTRL1>().set(m_scsi1, FUNC(spifi3_device::dma_r));
	m_dmac->dma_w_cb<dmac3_device::CTRL1>().set(m_scsi1, FUNC(spifi3_device::dma_w));

	SOFTWARE_LIST(config, "software_list").set_original("sony_news").set_filter("RISC,NWS5000");
}

void news_r4k_state::nws5000x(machine_config &config) { machine_common(config); }

/*
 * cpu_map
 *
 * Assign the address map for the CPU
 * References:
 *  - https://github.com/NetBSD/src/blob/trunk/sys/arch/newsmips/include/adrsmap.h
 *  - APmonitor device table (Run APmonitor command `ss -d` for details)
 */
void news_r4k_state::cpu_map(address_map &map)
{
	// Unmapping to high causes the APmonitor to wait a huge amount of time in between
	// printing serial characters - what is weird is that manually setting the unmapped
	// reads to return zero while unmapping everything high doesn't fix this issue, so
	// there must be a side effect caused by unmapping high somewhere.
	map.unmap_value_low();

	cpu_map_main_memory(map);

	// NEWS firmware
	map(0x1fc00000, 0x1fc3ffff).rom().region("mrom", 0);
	map(0x1f3c0000, 0x1f3c03ff).rom().region("idrom", 0);
	map(0x1e600000, 0x1e6003ff).rom().region("macrom", 0);

	// Front panel DIP switches
	map(0x1f3d0000, 0x1f3d0007).r(FUNC(news_r4k_state::front_panel_r));

	// Hardware timers
	map(0x1f800000, 0x1f800003).w(FUNC(news_r4k_state::timer0_w));
	map(0x1f840000, 0x1f840003).rw(FUNC(news_r4k_state::freerun_r), FUNC(news_r4k_state::freerun_w));

	// Timekeeper NVRAM and RTC
	map(0x1f880000, 0x1f881fff).rw(m_rtc, FUNC(m48t02_device::read), FUNC(m48t02_device::write)).umask32(0x000000ff);

	// Interrupt ports
	map(0x1f4e0000, 0x1f4e0017).w(FUNC(news_r4k_state::intclr_w));
	map(0x1fa00000, 0x1fa00017).rw(FUNC(news_r4k_state::inten_r), FUNC(news_r4k_state::inten_w));
	map(0x1fa00020, 0x1fa00037).r(FUNC(news_r4k_state::intst_r));

	// LEDs
	map(0x1f3f0000, 0x1f3f0017).w(FUNC(news_r4k_state::led_state_w));

	// APbus
	map(0x14c00000, 0x14c0008f).rw(FUNC(news_r4k_state::apbus_cmd_r), FUNC(news_r4k_state::apbus_cmd_w));
	map(0x14c20000, 0x14c3ffff).lrw8(NAME([this](offs_t offset)
										  { return m_dma_ram->read(offset); }),
									 NAME([this](offs_t offset, uint8_t data)
										  {
											  LOGMASKED(LOG_MEMORY, "Write to DMA map @ offset 0x%x: 0x%x (%s)\n", offset, data, machine().describe_context());
											  m_dma_ram->write(offset, data);
										  }));

	// ESCC + WSC-ESCC1 (CXD8421Q) serial controller
	map(0x1e900000, 0x1e93ffff).m(m_fifo1, FUNC(cxd8442q_device::map));
	map(0x1e940000, 0x1e94000f).rw(FUNC(news_r4k_state::escc_ch_read<CHB>), FUNC(news_r4k_state::escc_ch_write<CHB>));
	map(0x1e950000, 0x1e95000f).rw(FUNC(news_r4k_state::escc_ch_read<CHA>), FUNC(news_r4k_state::escc_ch_write<CHA>));
	map(0x1e960000, 0x1e960003).rw(FUNC(news_r4k_state::escc1_int_status_r), FUNC(news_r4k_state::escc1_int_status_w));

	// Interestingly, this is also the RAM that is used before main memory is initialized
	// It is also the only RAM avaliable if "no memory mode" is set (DIP switch #8)
	map(0x1e980000, 0x1e99ffff).m(m_fifo1, FUNC(cxd8442q_device::map_fifo_ram));

	// SONIC3 APbus interface and SONIC ethernet controller
	map(0x1e500000, 0x1e50003f).m(m_sonic3, FUNC(cxd8452aq_device::map));
	map(0x1e610000, 0x1e6101ff).m(m_sonic, FUNC(dp83932c_device::map)).umask64(0x000000000000ffff);
	map(0x1e620000, 0x1e627fff).lrw8(NAME([this](offs_t offset)
										  { return m_net_ram->read(offset); }),
									 NAME([this](offs_t offset, uint8_t data)
										  {
											  LOGMASKED(LOG_MEMORY, "Host write to net RAM @ offset 0x%x: 0x%x (%s)\n", offset, data, machine().describe_context());
											  m_net_ram->write(offset, data);
										  }));

	// DMAC3 DMA Controller
	map(0x1e200000, 0x1e200017).m(m_dmac, FUNC(dmac3_device::map<dmac3_device::CTRL0>));
	map(0x1e300000, 0x1e300017).m(m_dmac, FUNC(dmac3_device::map<dmac3_device::CTRL1>));

	// SPIFI SCSI controllers
	map(0x1e280000, 0x1e2803ff).m(m_scsi0, FUNC(spifi3_device::map));
	map(0x1e380000, 0x1e3803ff).m(m_scsi1, FUNC(spifi3_device::map));

	// HID (kb + ms)
	map(0x1f900000, 0x1f900047).m(m_hid, FUNC(news_hid_hle_device::map_apbus));

	// FDC controller register mapping
	// TODO: to be hardware accurate, these shouldn't be umasked.
	// instead, they should be duplicated across each 32-bit segment to emulate the open address lines
	// (i.e. status register A and B values of 56 c0 look like 56565656 c0c0c0c0)
	map(0x1ed60000, 0x1ed6001f).m(m_fdc, FUNC(pc8477b_device::map)).umask32(0x000000ff);

	// The NEWS monitor ROM FDC routines won't run if DRV2 is inactive.
	// TODO: The 5000X only has a single floppy drive. Why is this needed?
	map(0x1ed60000, 0x1ed60003).lr8(NAME([this](offs_t offset)
										 { return m_fdc->sra_r() & ~0x40; }))
		.umask32(0x000000ff);

	// TODO: Floppy aux registers
	map(0x1ed60200, 0x1ed60207).noprw();
	map(0x1ed60200, 0x1ed60207).lr8(NAME([](offs_t offset)
										 { return 0x1; }));

	// Map FDCAUX interrupt read (TODO: still need to implement button interrupt mask from 208-20b)
	map(0x1ed60208, 0x1ed6020f).lr32(NAME([this](offs_t offset)
										  { return (offset == 1) && ((m_intst[0] & irq0_number::FDC) > 0) ? 0x2 : 0x0; }));

	// Map FDC and sound FIFO register file
	map(0x1ed00000, 0x1ed3ffff).m(m_fifo0, FUNC(cxd8442q_device::map));
	map(0x1ed80000, 0x1ed9ffff).m(m_fifo0, FUNC(cxd8442q_device::map_fifo_ram));

	// TODO: DSC-39 xb framebuffer/video card (0x14900000)
	map(0x14900000, 0x149fffff).lr8(NAME([](offs_t offset)
										 { return 0xff; }));

	// TODO: sb sound subsystem (0x1ed00000)
	// TODO: lp line printer subsystem (0x1ed30000)

	// Assign debug mappings
	cpu_map_debug(map);
}

/*
 * cpu_map_main_memory
 *
 * Maps the main memory and supporting items into the provided address map.
 * TODO: Memory controller logic for non-64MB configurations
 */
void news_r4k_state::cpu_map_main_memory(address_map &map)
{
	// The mapping mechanism was determined experimentally and only supports the 64MB configuration for now.
	// This works for the monitor ROM, NEWS-OS kernel, and NetBSD kernel but will not work with other amounts of RAM.
	map(0x0, 0x7ffffff).rw(FUNC(news_r4k_state::ram_r), FUNC(news_r4k_state::ram_w));

	// Not 100% sure where the below lives on the system. This seems to be related to memory and perhaps APbus init.
	map(0x14400000, 0x14400003).lr32(NAME([](offs_t offset)
										  { return 0x0; }));
	map(0x14400004, 0x14400007).lr32(NAME([](offs_t offset)
										  { return 0x3ff17; }));
	map(0x14400024, 0x14400027).lr32(NAME([](offs_t offset)
										  { return 0x600a4; }));
	map(0x1440003c, 0x1440003f)
		.lw32(NAME([this](offs_t offset, uint32_t data)
				   {
					   if (data == 0x10001)
					   {
						   LOG("Enabling RAM map shift!\n");
						   m_ram_map_shift = true;
					   }
					   else
					   {
						   LOG("Disabling RAM map shift!\n");
						   m_ram_map_shift = false;
					   }
				   }));
}

/*
 * cpu_map_debug
 *
 * Method with temporary address map assignments. Everything in this function can be moved to the main memory
 * map function once it is understood. This separates the "real" mapping from the workarounds required to get
 * the monitor ROM to boot due to missing information or incomplete emulation.
 */
void news_r4k_state::cpu_map_debug(address_map &map)
{
	// APbus WBFLUSH + unknown
	map(0x1f520000, 0x1f520017).nopw();
	map(0x1f520000, 0x1f520017).lr8(NAME([](offs_t offset)
										 {
											 uint8_t value = 0x0;
											 if (offset == 7)
											 {
												 value = 0x1;
											 }
											 else if (offset == 11)
											 {
												 value = 0x1;
											 }
											 else if (offset == 15)
											 {
												 value = 0xc8;
											 }
											 else if (offset == 19)
											 {
												 value = 0x32;
											 }

											 return value;
										 }));

	// More onboard devices that needs to be mapped for the platform to boot
	map(0x1fe00000, 0x1fe03fff).ram().mirror(0x1fc000);
	map(0x1f3e0000, 0x1f3effff).lr8(NAME([](offs_t offset)
										 {
											 uint8_t value = 0x0;
											 if (offset % 4 == 2)
											 {
												 value = 0x6f;
											 }
											 else if (offset % 4 == 3)
											 {
												 value = 0xe0;
											 }

											 return value;
										 }));
}

/*
 * sonic3_map
 *
 * Memory accessor for the SONIC. The SONIC3 ASIC arbitrates memory access for the SONIC. The host platform can
 * access SONIC's memory directly, but SONIC may not access the bus directly.
 */
void news_r4k_state::sonic3_map(address_map &map)
{
	map(0x0, 0x7fff).lrw8(NAME([this](offs_t offset)
							   { return m_net_ram->read(offset); }),
						  NAME([this](offs_t offset, uint8_t data)
							   { m_net_ram->write(offset, data); }));
}

/*
 * ram_r
 *
 * Method that returns the byte at `offset` in main memory.
 * TODO: Memory controller logic for non-64MB configurations
 */
uint8_t news_r4k_state::ram_r(offs_t offset)
{
	uint8_t result = 0xff;
	if ((offset <= 0x1ffffff) || (m_ram_map_shift && offset <= 0x3ffffff))
	{
		result = m_ram->read(offset);
	}
	else if (!m_ram_map_shift && offset >= 0x7f00000) // upper 32MB before it is mapped into contiguous space
	{
		result = m_ram->read(offset - 0x5f00000);
	}
	else
	{
		LOG("Unmapped RAM read attempted at offset 0x%x\n", offset);
	}
	return result;
}

/*
 * ram_w
 *
 * Method that writes the byte `data` at `offset` in main memory.
 * TODO: Memory controller logic for non-64MB configurations
 */
void news_r4k_state::ram_w(offs_t offset, uint8_t data)
{
	if ((offset <= 0x1ffffff) || (m_ram_map_shift && offset <= 0x3ffffff))
	{
		m_ram->write(offset, data);
	}
	else if (!m_ram_map_shift && offset >= 0x7f00000) // upper 32MB before it is mapped into contiguous space
	{
		m_ram->write(offset - 0x5f00000, data);
	}
	else
	{
		LOG("Unmapped RAM write attempted at offset 0x%x (data: 0x%x)\n", offset, data);
	}
}

/*
 * machine_start
 *
 * Initialization method called when the machine first starts.
 */
void news_r4k_state::machine_start()
{
	// Init front panel LEDs
	m_led.resolve();

	// Save state support
	save_item(NAME(m_inten));
	save_item(NAME(m_intst));
	save_item(NAME(m_int_state));
	save_item(NAME(m_freerun_timer_val));

	// Allocate hardware timers
	m_freerun_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(news_r4k_state::freerun_clock), this));
	m_timer0_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(news_r4k_state::timer0_clock), this));

	// Cache for main bus access (APbus address translation)
	m_cpu->space(0).cache(m_main_cache);
}

/*
 * freerun_clock
 *
 * Method to handle a tick of the freerunning clock.
 */
TIMER_CALLBACK_MEMBER(news_r4k_state::freerun_clock)
{
	m_freerun_timer_val++;
}

/*
 * timer0_clock
 *
 * Method to handle a tick of the TIMER0 hardware clock.
 */
TIMER_CALLBACK_MEMBER(news_r4k_state::timer0_clock)
{
	// Whenever this timer elapses, we need to trigger the timer0 interrupt
	m_intst[2] |= irq2_number::TIMER0;
	int_check();
}

/*
 * machine_reset
 *
 * Method called whenever the machine is reset.
 */
void news_r4k_state::machine_reset()
{
	m_freerun_timer_val = 0;
	m_freerun_timer->adjust(attotime::zero, 0, attotime::from_hz(FREERUN_FREQUENCY));
	m_timer0_timer->adjust(attotime::zero, 0, attotime::from_hz(TIMER0_FREQUENCY));
}

/*
 * front_panel_r
 *
 * Method to read the state of the front panel DIP switches.
 */
uint64_t news_r4k_state::front_panel_r(offs_t offset)
{
	ioport_value dipsw = m_dip_switch->read();
	dipsw |= 0xff00; // Matches physical platform
	return ((uint64_t)dipsw << 32) | dipsw;
}

/*
 * led_state_w
 *
 * Sets and logs the status of the front panel LEDs.
 */
void news_r4k_state::led_state_w(offs_t offset, uint32_t data)
{
	if (m_led[offset] != data)
	{
		LOGMASKED(LOG_LED, "%s: %s\n", LED_MAP[offset], data ? "ON" : "OFF");
		m_led[offset] = data;
	}
}

/*
 * apbus_virt_to_phys
 *
 * The APbus has its own virtual->physical addressing scheme. Like the CPU's TLB, the host OS
 * is responsible for populating the APbus TLB. The `apbus_pte` struct defines what each entry looks like. Note that to avoid
 * bit-order dependencies and other platform-specific stuff, in this implementation, valid, coherent, pad2, and pfnum are
 * separate variables, but the actual register is packed like this:
 *
 * 0x xxxx xxxx xxxx xxxx
 *   |   pad   |  entry  |
 *
 * Entry is packed as follows:
 * 0b    x      x     xxxxxxxxxx xxxxxxxxxxxxxxxxxxxx
 *    |valid|coherent|    pad2  |       pfnum        |
 *
 * The APbus requires RAM to hold the TLB. On the NWS-5000X, this is 128KiB starting at physical address 0x14c20000 (and goes to 0x14c3ffff)
 *
 * For example, the monitor ROM populates the PTEs as follows in response to a `dl` command.
 * Addr       PTE1             PTE2
 * 0x14c20000 0000000080103ff5 0000000080103ff6
 *
 * It also loads the `address` register of the DMAC3 with 0xd60.
 *
 * This will cause the consuming ASIC (in this case, the DMAC3) to start mapping from virtual address 0xd60 to physical address 0x3ff5d60.
 * If the address register goes beyond 0xFFF, bit 12 will increment. This will increase the page number so the virtual address will be
 * 0x1000, and will cause the DMAC to use the next PTE (in this case, the next sequential page, 0x3ff6000).
 *
 * NetBSD splits the mapping RAM into two sections, one for each DMAC3 controller. If the OS does not keep track, the ASICs
 * could end up in a configuration that would cause them to overwrite each other's data. NEWS-OS makes even more extensive
 * use of APbus DMA, including the WSC-SONIC3 for network DMA.
 *
 * Another note: NetBSD mentions that the `pad2` section of the register is 10 bits. However, this might not be fully accurate.
 * On the NWS-5000X, the physical address bus is 36 bits because it has an R4400SC. The 32nd bit is sometimes set, depending
 * on the virtual address being used (maybe it goes to the memory controller). It doesn't impact the normal operation of the
 * computer, but does mean that the `pad2` section might only be 6 bits, not 10 bits.
 */
uint32_t news_r4k_state::apbus_virt_to_phys(uint32_t v_address)
{
	// Convert page number to PTE address and read raw PTE data from the APbus DMA mapping RAM
	uint32_t apbus_page_address = APBUS_DMA_MAP_ADDRESS + 8 * (v_address >> 12);
	if (apbus_page_address >= (APBUS_DMA_MAP_ADDRESS + APBUS_DMA_MAP_RAM_SIZE))
	{
		fatalerror("%s: APbus address decoder ran past the bounds of the DMA map RAM!", tag());
	}

	uint64_t raw_pte = m_main_cache.read_qword(apbus_page_address);

	// Marshal raw data to struct
	apbus_pte pte;
	pte.valid = raw_pte & ENTRY_VALID;
	pte.coherent = raw_pte & ENTRY_COHERENT;
	pte.pad2 = (raw_pte & ENTRY_PAD) >> ENTRY_PAD_SHIFT;
	pte.pfnum = raw_pte & ENTRY_PFNUM;

	// This might be able to trigger some kind of interrupt - for now, treat as a fatal error.
	// However, based on the NetBSD source, this would likely trigger INT4.
	if (!pte.valid)
	{
		fatalerror("%s: APbus DMA referenced invalid entry! Raw PTE (v_address = 0x%x, page_address = 0x%x): 0x%x\n", tag(), v_address, apbus_page_address, raw_pte);
	}

	// Since the entry is valid, use it to calculate the physical address
	return (pte.pfnum << 12) + (v_address & 0xFFF);
}

/*
 * apbus_cmd_r
 *
 * Emulates a call to the APbus controller (placeholder)
 */
uint8_t news_r4k_state::apbus_cmd_r(offs_t offset)
{
	// This function spoofs the APbus being fully initialized
	// More may have to be added to this method in the future
	uint8_t value = 0x0;
	if (offset == 7)
	{
		value = 0x1;
	}
	else if (offset == 11)
	{
		value = 0x1;
	}
	else if (offset == 15)
	{
		value = 0xc8;
	}
	else if (offset == 19)
	{
		value = 0x32;
	}

	LOGMASKED(LOG_APBUS, "APbus read triggered at offset 0x%x, returning 0x%x\n", offset, value);
	return value;
}

/*
 * apbus_cmd_w
 *
 * Emulates a call to the APbus controller (placeholder)
 */
void news_r4k_state::apbus_cmd_w(offs_t offset, uint32_t data)
{
	// Don't take the performance hit unless user cares about logs
#if (VERBOSE & LOG_APBUS) > 0
	auto cmd_iter = m_apbus_cmd_decoder.find(offset);
	if (cmd_iter != m_apbus_cmd_decoder.end())
	{
		LOGMASKED(LOG_APBUS, "APbus %s command invoked, data 0x%x\n", cmd_iter->second, data);
	}
	else
	{
		LOGMASKED(LOG_APBUS, "Unknown APbus command called, offset 0x%x, set to 0x%x\n", offset, data);
	}
#endif
}

/*
 * freerun_r
 *
 * Get the current value of the freerunning clock
 */
uint32_t news_r4k_state::freerun_r(offs_t offset)
{
	return m_freerun_timer_val;
}

/*
 * freerun_w
 *
 * Set the current value of the freerunning clock
 */
void news_r4k_state::freerun_w(offs_t offset, uint32_t data)
{
	LOGMASKED(LOG_INTERRUPT, "freerun_w: Set freerun timer to 0x%x\n", data);
	m_freerun_timer_val = data;
}

/*
 * timer0_w
 *
 * Clears the TIMER0 interrupt and starts the timer again.
 * See https://github.com/NetBSD/src/blob/trunk/sys/arch/newsmips/newsmips/news5000.c#L114
 */
void news_r4k_state::timer0_w(offs_t offset, uint32_t data)
{
	m_intst[2] &= ~0x1;
	int_check();
}

/*
 * inten_w
 *
 * Update the enable status of the interrupt indicated by `offset`.
 */
void news_r4k_state::inten_w(offs_t offset, uint32_t data)
{
	LOGMASKED(LOG_INTERRUPT, "inten_w: INTEN%d = 0x%x\n", offset, data);
	m_inten[offset] = data;
	int_check();
}

/*
 * inten_r
 *
 * Get the enable status of the interrupt indicated by `offset`.
 */
uint32_t news_r4k_state::inten_r(offs_t offset)
{
	LOGMASKED(LOG_ALL_INTERRUPT, "inten_r: INTEN%d = 0x%x\n", offset, m_inten[offset]);
	return m_inten[offset];
}

/*
 * intst_r
 *
 * Get the status of the interrupt indicated by `offset`.
 */
uint32_t news_r4k_state::intst_r(offs_t offset)
{
	LOGMASKED(LOG_ALL_INTERRUPT, "intst_r: INTST%d = 0x%x\n", offset, m_intst[offset]);
	return m_intst[offset];
}

/*
 * generic_irq_w
 *
 * Set or clear the provided interrupt using `mask`.
 */
void news_r4k_state::generic_irq_w(uint32_t irq, uint32_t mask, int state)
{
	LOGMASKED(LOG_INTERRUPT, "generic_irq_w: INTST%d IRQ %d set to %d\n", irq, mask, state);
	if (state)
	{
		m_intst[irq] |= mask;
	}
	else
	{
		m_intst[irq] &= ~mask;
	}
	int_check();
}

/*
 * intclr_w
 *
 * Clear the provided interrupt.
 */
void news_r4k_state::intclr_w(offs_t offset, uint32_t data)
{
	// TODO: Haven't found much that uses INTCLR. Might be some nuances to this that will be discovered later.
	LOGMASKED(LOG_INTERRUPT, "intclr_w: INTCLR%d = 0x%x\n", offset, data);
	m_intst[offset] &= ~data;
	int_check();
}

/*
 * int_check
 *
 * Observes the platform state and updates the R4400's interrupt lines if needed.
 */
void news_r4k_state::int_check()
{
	// The R4000 series has 6 bits in the interrupt register
	// These map to the 6 INTST/EN/CLR groups on the NEWS platform
	// See https://github.com/NetBSD/src/blob/trunk/sys/arch/newsmips/newsmips/news5000.c
	// and https://github.com/NetBSD/src/blob/trunk/sys/arch/newsmips/apbus/apbus.c
	// This has been tested with a few different devices and seems OK so far, but there might be some things missing.
	for (int i = 0; i < 6; i++)
	{
		bool state = m_intst[i] & m_inten[i];

		if (m_int_state[i] != state)
		{
			LOGMASKED(LOG_ALL_INTERRUPT, "Setting CPU input line %d to %d\n", interrupt_map[i], state ? 1 : 0);
			m_int_state[i] = state;
			m_cpu->set_input_line(interrupt_map[i], state ? 1 : 0);
		}
	}
}

/*
 * escc_ch_read
 *
 * Accesses the read port of the specified ESCC channel.
 */
uint32_t news_r4k_state::escc_ch_read(escc_channel channel, offs_t offset)
{
	if (offset < 2)
	{
		offset |= (channel == CHA ? 0x2 : 0x0);
		return m_escc->ab_dc_r(offset);
	}
	else
	{
		// Placeholder for ESCC1 extport and ctl registers
		LOG("escc1 ch%d r non-passthrough: 0x%x\n", channel, offset);
		return 0x0;
	}
}

/*
 * escc_ch_read
 *
 * Writes `data` to the ESCC specified by channel.
 */
void news_r4k_state::escc_ch_write(escc_channel channel, offs_t offset, uint32_t data)
{
	if (offset < 2)
	{
		offset |= (channel == CHA ? 0x2 : 0x0);
		m_escc->ab_dc_w(offset, data);
	}
	else
	{
		// Placeholder for ESCC1 extport and ctl registers
		LOG("escc1 ch%d w non-passthrough: 0x%x = 0x%x\n", channel, offset, data);
		return;
	}
}

/*
 * escc1_int_status_r
 *
 * Reads the ESCC1 interrupt status register. This is used in conjunction with the ESCC and FIFO chips
 * to provide asynchronous duplex serial communication.
 */
uint32_t news_r4k_state::escc1_int_status_r(offs_t offset)
{
	LOGMASKED(LOG_ESCC, "Read ESCC1 int status 0x%x (%s)\n", escc1_int_status, machine().describe_context());
	return escc1_int_status;
}

/*
 * escc1_int_status_w
 *
 * Clears the ESCC1 interrupt status register. This is used in conjunction with the ESCC and FIFO chips
 * to provide asynchronous duplex serial communication.
 */
void news_r4k_state::escc1_int_status_w(offs_t offset, uint32_t data)
{
	// assume clear for now
	LOGMASKED(LOG_ESCC, "Clear ESCC1 int status (%s)\n", machine().describe_context());
	escc1_int_status = 0;
}

/*
 * NWS-5000X DIP switches
 *
 * 1. Console - switch between serial and bitmap console. Bitmap is not implemented yet.
 * 2. Bitmap Disable - Enable or disable the internal video card
 * 3. Abort/Resume Enable - Unknown, NEWS-OS refers to this but doesn't elaborate as to what it does
 * 4. Clear NVRAM - Upon boot, clear NVRAM contents and restore default values if set
 * 5. Auto Boot - Upon boot, automatically attempt to boot from the disk specified by the bootdev NVRAM variable
 * 6. Run Diagnostic Test - Attempt to run diagnostic test after ROM monitor has booted
 * 7. External APbus Slot Probe Disable - If set, do not attempt to probe the expansion APbus slots
 * 8. No Memory Mode - If set, do not use the main memory (limits system to 128KiB)
 *
 * On real hardware, the default for sw7 is off (probe external APslots).
 * Since the APbus expansion slots are not emulated yet, setting this avoids a harmless error message during the monitor ROM boot sequence
 */
static INPUT_PORTS_START(nws5000)
	PORT_START("FRONT_PANEL")
		PORT_DIPNAME(0x01, 0x00, "Console")
			PORT_DIPLOCATION("FRONT_PANEL:1")
			PORT_DIPSETTING(0x00, "Serial Terminal")
			PORT_DIPSETTING(0x01, "Bitmap")
		PORT_DIPNAME(0x02, 0x00, "Bitmap Disable")
			PORT_DIPLOCATION("FRONT_PANEL:2")
			PORT_DIPSETTING(0x00, "Enable Built-in Bitmap")
			PORT_DIPSETTING(0x02, "Disable Built-in Bitmap")
		PORT_DIPNAME(0x04, 0x00, "Abort/Resume Enable")
			PORT_DIPLOCATION("FRONT_PANEL:3")
			PORT_DIPSETTING(0x00, "Disable Abort/Resume")
			PORT_DIPSETTING(0x04, "Enable Abort/Resume")
		PORT_DIPNAME(0x08, 0x00, "Clear NVRAM")
			PORT_DIPLOCATION("FRONT_PANEL:4")
			PORT_DIPSETTING(0x00, "Normal Operation (No Clear)")
			PORT_DIPSETTING(0x08, "Clear NVRAM")
		PORT_DIPNAME(0x10, 0x00, "Auto Boot")
			PORT_DIPLOCATION("FRONT_PANEL:5")
			PORT_DIPSETTING(0x00, "Auto Boot Disable")
			PORT_DIPSETTING(0x10, "Auto Boot Enable")
		PORT_DIPNAME(0x20, 0x00, "Run Diagnostic Test")
			PORT_DIPLOCATION("FRONT_PANEL:6")
			PORT_DIPSETTING(0x00, "No Diagnostic Test")
			PORT_DIPSETTING(0x20, "Run Diagnostic Test")
		PORT_DIPNAME(0x40, 0x40, "External APbus Slot Probe Disable")
			PORT_DIPLOCATION("FRONT_PANEL:7")
			PORT_DIPSETTING(0x00, "Enable External Slot Probe")
			PORT_DIPSETTING(0x40, "Disable External Slot Probe")
		PORT_DIPNAME(0x80, 0x00, "No Memory Mode")
			PORT_DIPLOCATION("FRONT_PANEL:8")
			PORT_DIPSETTING(0x00, "Main Memory Enabled")
			PORT_DIPSETTING(0x80, "Main Memory Disabled");
INPUT_PORTS_END

ROM_START(nws5000x)
	ROM_REGION64_BE(0x40000, "mrom", 0)
	ROM_SYSTEM_BIOS(0, "nws5000x", "APbus System Monitor Release 3.201")
	ROMX_LOAD("mpu-33__ver3.201__1994_sony.rom", 0x00000, 0x40000, CRC(8a6ca2b7) SHA1(72d52e24a554c56938d69f7d279b2e65e284fd59), ROM_BIOS(0))

	// idrom and macrom dumps include unmapped space that would ideally be umasked, but this is functionally the same.
	// Additionally, there is machine-specific information contained within each of these, so there are no "golden" full-chip hashes.
	ROM_REGION64_BE(0x400, "idrom", 0)
	ROM_LOAD("idrom.rom", 0x000, 0x400, CRC(28ff30c9) SHA1(288fd7d9133ac5e40d90a9b6e24db057cd6b05ad) BAD_DUMP)

	ROM_REGION64_BE(0x400, "macrom", 0)
	ROM_LOAD("macrom.rom", 0x000, 0x400, CRC(22d384d2) SHA1(b78a2861310929e92f16deab989ac51f9da3131a) BAD_DUMP)
ROM_END

} // anonymous namespace

// Machine definitions
//   YEAR  NAME      P  CM MACHINE   INPUT    CLASS           INIT        COMPANY FULLNAME                      FLAGS
COMP(1994, nws5000x, 0, 0, nws5000x, nws5000, news_r4k_state, empty_init, "Sony", "NET WORK STATION NWS-5000X", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_TIMING | MACHINE_NO_SOUND)



newton.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/***************************************************************************

    Apple Newton devices skeleton driver

    CPUs:
        Newton MessagePad:        ARM 610 (20MHz)
        ExpertPad PI-7000: ARM 610 (20MHz)
        Newton MessagePad 100:    ARM 610 (20MHz)
        Newton MessagePad 110:    ARM 610 (20MHz)
        Newton MessagePad 120:    ARM 610 (20MHz)
        Marco:             ARM 610 (20MHz)
        Newton MessagePad 130:    ARM 610 (20MHz)

        eMate 300:         ARM 710a (25MHz)

        Newton MessagePad 2000:   StrongARM SA-110 (162MHz)
        Newton MessagePad 2100:   StrongARM SA-110 (162MHz)

****************************************************************************/

#include "emu.h"
#include "cpu/arm7/arm7.h"
#include "machine/ram.h"
#include "emupal.h"
#include "screen.h"


namespace {

class newton_state : public driver_device
{
public:
	newton_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, "ram")
	{ }

	void gen1(machine_config &config);
	void mp120(machine_config &config);
	void marco(machine_config &config);
	void mp130(machine_config &config);
	void emate(machine_config &config);
	void mp2000(machine_config &config);
	void mp2100(machine_config &config);

protected:
	void mem_map(address_map &map) ATTR_COLD;

	uint32_t tick_count_r();

	required_device<arm7_cpu_device> m_maincpu;
	required_device<ram_device> m_ram;

	uint32_t m_ram_size = 0;
};

uint32_t newton_state::tick_count_r()
{
	return (uint32_t)m_maincpu->total_cycles();
}

void newton_state::mem_map(address_map &map)
{
	map(0x00000000, 0x007fffff).mirror(0x00800000).rom().region("maincpu", 0);
	map(0x02000000, 0x023fffff).ram(); // Actually Flash
	map(0x04000000, 0x04ffffff).rw(m_ram, FUNC(ram_device::read), FUNC(ram_device::write));
	map(0x0f181800, 0x0f181803).r(FUNC(newton_state::tick_count_r));
	map(0x0f001800, 0x0f001803).lrw32(NAME([this](){ return m_ram_size; }), NAME([this](uint32_t data) { m_ram_size = data; }));
}

static INPUT_PORTS_START( newton )
INPUT_PORTS_END

void newton_state::gen1(machine_config &config)
{
	ARM7(config, m_maincpu, XTAL(20'000'000)); // really ARM610
	m_maincpu->set_addrmap(AS_PROGRAM, &newton_state::mem_map);

	RAM(config, m_ram);
	m_ram->set_default_size("640K");
}

void newton_state::mp120(machine_config &config)
{
	gen1(config);
	m_ram->set_default_size("1M");
	m_ram->set_extra_options("2M");
}

void newton_state::marco(machine_config &config)
{
	gen1(config);
	m_ram->set_extra_options("687K");
}

void newton_state::mp130(machine_config &config)
{
	gen1(config);
	m_ram->set_default_size("2560K");
}

void newton_state::emate(machine_config &config)
{
	ARM710A(config, m_maincpu, 162000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &newton_state::mem_map);

	RAM(config, m_ram);
	m_ram->set_default_size("1M");
	m_ram->set_extra_options("2M");
}

void newton_state::mp2000(machine_config &config)
{
	ARM710A(config, m_maincpu, 162000000); // really SA110
	m_maincpu->set_addrmap(AS_PROGRAM, &newton_state::mem_map);

	RAM(config, m_ram);
	m_ram->set_default_size("1M");
}

void newton_state::mp2100(machine_config &config)
{
	mp2000(config);
	m_ram->set_default_size("4M");
}

/* ROM definition */
ROM_START( newtnotp )
	ROM_REGION32_LE( 0x800000, "maincpu", 0 )
	ROMX_LOAD( "v10b1.rom", 0x000000, 0x400000, CRC(9fec5b35) SHA1(87ae4afe72814117f9100b67c6fda7010463a0f8), ROM_REVERSE | ROM_GROUPDWORD )
ROM_END

ROM_START( newtonmp )
	ROM_REGION32_LE( 0x800000, "maincpu", 0 )
	ROM_SYSTEM_BIOS(0, "v100", "v1.00")
	ROMX_LOAD( "v100.rom", 0x000000, 0x400000, CRC(bab51f17) SHA1(5e754fe7db01ec3c331ff4d71b2dc4565eafce98), ROM_BIOS(0) | ROM_REVERSE | ROM_GROUPDWORD )
	ROM_SYSTEM_BIOS(1, "v13", "v1.3")
	ROMX_LOAD( "v13.rom", 0x000000, 0x400000, CRC(8976832c) SHA1(964d07743589cf854fbbcd5e51e3289b739d8050), ROM_BIOS(1) | ROM_REVERSE | ROM_GROUPDWORD )
ROM_END

ROM_START( spi7000 )
	ROM_REGION32_LE( 0x800000, "maincpu", 0 )
	ROMX_LOAD( "v110.rom", 0x000000, 0x400000, CRC(0a2e0d96) SHA1(1e8e4c74ca19eee120b2647b267e2c467b668f1f), ROM_REVERSE | ROM_GROUPDWORD )
ROM_END

ROM_START( mp110 )
	ROM_REGION32_LE( 0x800000, "maincpu", 0 )
	ROMX_LOAD( "v12.rom", 0x000000, 0x400000, CRC(291aac40) SHA1(517094fc26702b82d558d0c9d677a91de25d0b7f), ROM_REVERSE | ROM_GROUPDWORD )
ROM_END

ROM_START( mp120 )
	ROM_REGION32_LE( 0x800000, "maincpu", 0 )
	ROMX_LOAD( "v13.rom", 0x000000, 0x400000, CRC(d8a34419) SHA1(deda5023dbcb0c11bd6384e444a800c4a271312c), ROM_REVERSE | ROM_GROUPDWORD )
ROM_END

ROM_START( motmarco )
	ROM_REGION32_LE( 0x800000, "maincpu", 0 )
	ROMX_LOAD( "v13 444347.rom", 0x000000, 0x400000, CRC(ad79abc5) SHA1(5c4731008ac402b8f0be37158482b61b36e247cc), ROM_REVERSE | ROM_GROUPDWORD )
ROM_END

ROM_START( mp130 )
	ROM_REGION32_LE( 0x800000, "maincpu", 0 )
	ROMX_LOAD( "v2x.rom", 0x000000, 0x800000, CRC(88ac9c6c) SHA1(44e33b72328974ed0ac41a13fe0e56bf97d15be3), ROM_REVERSE | ROM_GROUPDWORD )
ROM_END

ROM_START( emate )
	ROM_REGION32_LE( 0x800000, "maincpu", 0 )
	ROMX_LOAD( "emate300.rom", 0x000000, 0x800000, CRC(782ea604) SHA1(dcc42e45a6914c7a771819856a1fa05892fe0519), ROM_REVERSE | ROM_GROUPDWORD )
ROM_END

ROM_START( mp2000 )
	ROM_REGION32_LE( 0x800000, "maincpu", 0 )
	ROMX_LOAD( "mp2000.rom", 0x000000, 0x800000, CRC(9001b0f8) SHA1(06751fa69b791febae7267e0486aa15eea933a53), ROM_REVERSE | ROM_GROUPDWORD )
ROM_END

ROM_START( mp2100 )
	ROM_REGION32_LE( 0x800000, "maincpu", 0 )
	ROMX_LOAD( "mp2100.rom", 0x000000, 0x800000, CRC(81d5efc6) SHA1(82a191652b2689ce0e254ee11c6f43c84b5185cc), ROM_REVERSE | ROM_GROUPDWORD )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT   CLASS         INIT        COMPANY           FULLNAME     FLAGS
CONS( 1992, newtnotp, 0,      0,      gen1,     newton, newton_state, empty_init, "Apple Computer", "Newton Notepad (prototype)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 1993, newtonmp, 0,      0,      gen1,     newton, newton_state, empty_init, "Apple Computer", "Newton MessagePad", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 1993, spi7000,  0,      0,      gen1,     newton, newton_state, empty_init, "Sharp", "ExpertPad PI-7000", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 1994, mp110,    0,      0,      gen1,     newton, newton_state, empty_init, "Apple Computer", "Newton MessagePad 110", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 1995, mp120,    0,      0,      mp120,    newton, newton_state, empty_init, "Apple Computer", "Newton MessagePad 120", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 1995, motmarco, 0,      0,      marco,    newton, newton_state, empty_init, "Motorola", "Marco", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 1996, mp130,    0,      0,      mp130,    newton, newton_state, empty_init, "Apple Computer", "Newton MessagePad 130", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 1997, emate,    0,      0,      emate,    newton, newton_state, empty_init, "Apple Computer", "eMate 300", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 1997, mp2000,   0,      0,      mp2000,   newton, newton_state, empty_init, "Apple Computer", "Newton MessagePad 2000", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 1997, mp2100,   0,      0,      mp2100,   newton, newton_state, empty_init, "Apple Computer", "Newton MessagePad 2100", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



next.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
/***************************************************************************

    NeXT

    TODO:

    - Hook up the sound output, it is shared with the keyboard port

    - Implement more of the scc and its dma interactions so that the
      start up test passes, but not before sound out is done (if the scc
      test passes all the other test pass up to sound out which
      infloops)

    - Really implement the MO, it's only faking it for the startup test right now

    - Fix the networking

    - Find out why netbsd goes to hell even before loading the kernel

    Memory map and other bits can be found here:
    http://fxr.watson.org/fxr/source/arch/next68k/include/cpu.h?v=NETBSD5#L366

****************************************************************************/


#include "emu.h"
#include "next.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"
#include "screen.h"
#include "softlist.h"

#include "formats/flopimg.h"


uint32_t next_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	// We don't handle partial updates, but we don't generate them either :-)
	if(cliprect.min_x || cliprect.min_y || cliprect.max_x+1 != screen_sx || cliprect.max_y+1 != screen_sy)
		return 0;

	// 0,1,2,4=0b000000, 3=2c000000, 5,8,9=0c000000
	// 1152x832 (0,1,2,3c,4)
	// 1120x832 (8)
	// 832x624 (5c,9c)
	if(screen_color) {
		const uint32_t *vr = vram;
		for(int y=0; y<screen_sy; y++) {
			uint32_t *pix = reinterpret_cast<uint32_t *>(bitmap.raw_pixptr(y));
			for(int x=0; x<screen_sx; x+=2) {
				uint32_t v = *vr++;
				for(int xi=0; xi<2; xi++) {
					uint16_t pen = (v >> (16-(xi*16))) & 0xffff;
					uint32_t r = (pen & 0xf000) >> 12;
					uint32_t g = (pen & 0x0f00) >> 8;
					uint32_t b = (pen & 0x00f0) >> 4;
					uint32_t col = (r << 20) | (r << 16) | (g << 12) | (g << 8) | (b << 4) | b;
					*pix++ = col;
				}
			}
			vr += screen_skip;
		}
	} else {
		static uint32_t colors[4] = { 0xffffff, 0xaaaaaa, 0x555555, 0x000000 };
		const uint32_t *vr = vram;
		for(int y=0; y<screen_sy; y++) {
			uint32_t *pix = reinterpret_cast<uint32_t *>(bitmap.raw_pixptr(y));
			for(int x=0; x<screen_sx; x+=16) {
				uint32_t v = *vr++;
				for(int xi=0; xi<16; xi++)
					*pix++ = colors[(v >> (30-(xi*2))) & 0x3];
			}
			vr += screen_skip;
		}
	}

	return 0;
}

/* map ROM at 0x01000000-0x0101ffff? */
uint32_t next_state::rom_map_r()
{
	if(0 && !machine().side_effects_disabled())
		printf("%08x ROM MAP?\n",maincpu->pc());
	return 0x01000000;
}

uint32_t next_state::scr2_r()
{
	if(0 && !machine().side_effects_disabled())
		printf("%08x\n",maincpu->pc());
	/*
	x--- ---- ---- ---- ---- ---- ---- ---- dsp reset
	-x-- ---- ---- ---- ---- ---- ---- ---- dsp block end
	--x- ---- ---- ---- ---- ---- ---- ---- dsp unpacked
	---x ---- ---- ---- ---- ---- ---- ---- dsp mode b
	---- x--- ---- ---- ---- ---- ---- ---- dsp mode a
	---- -x-- ---- ---- ---- ---- ---- ---- remote int
	---- ---x ---- ---- ---- ---- ---- ---- local int
	---- ---- ---x ---- ---- ---- ---- ---- dram 256k
	---- ---- ---- ---x ---- ---- ---- ---- dram 1m
	---- ---- ---- ---- x--- ---- ---- ---- "timer on ipl7"
	---- ---- ---- ---- -xxx ---- ---- ---- rom waitstates
	---- ---- ---- ---- ---- x--- ---- ---- ROM 1M
	---- ---- ---- ---- ---- -x-- ---- ---- MCS1850 rtdata
	---- ---- ---- ---- ---- --x- ---- ---- MCS1850 rtclk
	---- ---- ---- ---- ---- ---x ---- ---- MCS1850 rtce
	---- ---- ---- ---- ---- ---- x--- ---- rom overlay
	---- ---- ---- ---- ---- ---- -x-- ---- dsp ie
	---- ---- ---- ---- ---- ---- --x- ---- mem en
	---- ---- ---- ---- ---- ---- ---- ---x led

	68040-25, 100ns, 32M: 00000c80
	68040-25, 100ns, 20M: 00ff0c80

	*/

	uint32_t data = scr2 & 0xfffffbff;

	data |= rtc->sdo_r() << 10;

	return data;
}

void next_state::scr2_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	if(0 && !machine().side_effects_disabled())
		printf("scr2_w %08x (%08x)\n", data, maincpu->pc());
	COMBINE_DATA(&scr2);

	rtc->ce_w(BIT(scr2, 8));
	rtc->sdi_w(BIT(scr2, 10));
	rtc->sck_w(BIT(scr2, 9));
	irq_set(0, scr2 & 0x01000000);
	irq_set(1, scr2 & 0x02000000);
}

uint32_t next_state::scr1_r()
{
	/*
	    xxxx ---- ---- ---- ---- ---- ---- ---- slot ID
	    ---- ---- xxxx xxxx ---- ---- ---- ---- DMA type
	    ---- ---- ---- ---- xxxx ---- ---- ---- machine type
	    ---- ---- ---- ---- ---- xxxx ---- ---- board revision
	    ---- ---- ---- ---- ---- ---- -xx- ---- video mem speed
	    ---- ---- ---- ---- ---- ---- ---x x--- mem speed
	    ---- ---- ---- ---- ---- ---- ---- -xxx cpu speed 16/20/25/33/40/50/66/80

	machine types:
	0 NeXT_CUBE
	1 NeXT_WARP9
	2 NeXT_X15
	3 NeXT_WARP9C
	4 NeXT_Turbo
	5 NeXT_TurboC
	6 Unknown
	7 Unknown
	8 NeXT_TurboCube
	9 NeXT_TurboCubeC

	    68040-25: 00011102
	    68040-25: 00013002 (non-turbo, color)
	*/

	return scr1;
}

// Interrupt subsystem
// source    bit   level
// nmi       31    7 *
// pfail     30    7
// timer     29    6 *
// enetxdma  28    6 *
// enetrdma  27    6 *
// scsidma   26    6 *
// diskdma   25    6
// prndma    24    6 *
// sndoutdma 23    6
// sndindma  22    6
// sccdma    21    6
// dspdma    20    6
// m2rdma    19    6
// r2mdma    18    6
// scc       17    5
// remote    16    5 *
// bus       15    5 *
// dsp4      14    4
// disk/cvid 13    3
// scsi      12    3 *
// printer   11    3
// enetx     10    3 *
// enetr      9    3
// soundovr   8    3 *
// phone      7    3 * -- floppy
// dsp3       6    3
// video      5    3
// monitor    4    3
// kbdmouse   3    3 *
// power      2    3 *
// softint1   1    2 *
// softint0   0    1 *

void next_state::irq_set(int id, bool raise)
{
	uint32_t mask = 1U << id;
	uint32_t old_status = irq_status;
	if(raise)
		irq_status |= mask;
	else
		irq_status &= ~mask;
	if(old_status != irq_status)
		irq_check();
}


uint32_t next_state::irq_status_r()
{
	return irq_status;
}

uint32_t next_state::irq_mask_r()
{
	return irq_mask;
}

void next_state::irq_mask_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	COMBINE_DATA(&irq_mask);
	irq_check();
}

void next_state::irq_check()
{
	uint32_t act = irq_status & (irq_mask | 0x80000000);
	int bit;
	for(bit=31; bit >= 0 && !(act & (1U << bit)); bit--);

	int level;
	if     (bit <  0) level = 0;
	else if(bit <  1) level = 1;
	else if(bit <  2) level = 2;
	else if(bit < 14) level = 3;
	else if(bit < 15) level = 4;
	else if(bit < 18) level = 5;
	else if(bit < 30) level = 6;
	else              level = 7;

	logerror("IRQ info %08x/%08x - %d\n", irq_status, irq_mask, level);

	if(level != irq_level) {
		maincpu->set_input_line(irq_level, CLEAR_LINE);
		maincpu->set_input_line(level, ASSERT_LINE);
		irq_level = level;
	}
}

char const *const next_state::dma_targets[0x20] = {
	nullptr, "scsi", nullptr, nullptr, "soundout", "disk", nullptr, nullptr,
	"soundin", "printer", nullptr, nullptr, "scc", "dsp", nullptr, nullptr,
	"s-enetx", "enetx", nullptr, nullptr, "s-enetr", "enetr", nullptr, nullptr,
	"video", nullptr, nullptr, nullptr, "r2m", "m2r", nullptr, nullptr
};

int const next_state::dma_irqs[0x20] = {
	-1, 26, -1, -1, 23, 25, -1, -1, 22, 24, -1, -1, 21, 20, -1, -1,
	-1, 28, -1, -1, -1, 27, -1, -1,  5, -1, -1, -1, 18, 19, -1, -1
};

bool const next_state::dma_has_saved[0x20] = {
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, true,  false, false, false, true,  false, false,
	false, false, false, false, false, false, false, false,
};

std::string next_state::dma_name(int slot)
{
	if(dma_targets[slot])
		return dma_targets[slot];

	return util::string_format("<%02x>", slot);
}

void next_state::dma_drq_w(int slot, bool state)
{
	//  logerror("DMA drq_w %d, %d\n", slot, state);
	dma_slot &ds = dma_slots[slot];
	ds.drq = state;
	if(state && (ds.state & DMA_ENABLE)) {
		address_space &space = maincpu->space(AS_PROGRAM);
		if(ds.state & DMA_READ) {
			while(ds.drq) {
				dma_check_update(slot);
				uint8_t val;
				bool eof;
				bool err;
				dma_read(slot, val, eof, err);
				if(err) {
					ds.state = (ds.state & ~DMA_ENABLE) | DMA_BUSEXC;
					logerror("DMA: bus error on read slot %d\n", slot);
					return;
				}
				space.write_byte(ds.current++, val);
				dma_check_end(slot, eof);
				if(!(ds.state & DMA_ENABLE))
					return;
			}
		} else {
			while(ds.drq) {
				dma_check_update(slot);
				uint8_t val = space.read_byte(ds.current++);
				bool eof = ds.current == (ds.limit & 0x7fffffff) && (ds.limit & 0x80000000);
				bool err;
				dma_write(slot, val, eof, err);
				if(err) {
					ds.state = (ds.state & ~DMA_ENABLE) | DMA_BUSEXC;
					logerror("DMA: bus error on write slot %d\n", slot);
					return;
				}
				dma_check_end(slot, false);
				if(!(ds.state & DMA_ENABLE))
					return;
			}
		}
	}
}

void next_state::dma_read(int slot, uint8_t &val, bool &eof, bool &err)
{
	err = false;
	eof = false;
	switch(slot) {
	case 1:
		if(fdc && fdc->get_drq()) {
			val = fdc->dma_r();
			if(eof) {
				fdc->tc_w(true);
				fdc->tc_w(false);
			}
		} else
			val = scsi->dma_r();
		break;

	case 5:
		if(mo)
			val = mo->dma_r();
		break;

	case 21:
		net->rx_dma_r(val, eof);
		logerror("dma read net %02x %s\n", val, eof ? "eof" : "");
		break;

	default:
		err = true;
		val = 0;
		break;
	}
}

void next_state::dma_write(int slot, uint8_t data, bool eof, bool &err)
{
	err = false;
	switch(slot) {
	case 1:
		if(fdc && fdc->get_drq()) {
			fdc->dma_w(data);
			if(eof) {
				fdc->tc_w(true);
				fdc->tc_w(false);
			}
		} else
			scsi->dma_w(data);
		break;

	case 4:
		break;

	case 5:
		if(mo)
			mo->dma_w(data);
		break;

	case 17:
		net->tx_dma_w(data, eof);
		break;

	default:
		err = true;
		break;
	}
}

void next_state::dma_check_update(int slot)
{
	dma_slot &ds = dma_slots[slot];
	if(ds.restart) {
		ds.current = ds.start;
		ds.restart = false;
	}
}

void next_state::dma_end(int slot)
{
	dma_slot &ds = dma_slots[slot];
	if(dma_has_saved[slot]) {
		dma_slot &ds1 = dma_slots[(slot-1) & 31];
		ds1.current = ds.start;
		ds1.limit = ds.current;
	}

	if(!ds.supdate)
		ds.state &= ~DMA_ENABLE;
	else {
		ds.start = ds.chain_start;
		ds.limit = ds.chain_limit;
		ds.restart = true;
		ds.supdate = false;
		ds.state &= ~DMA_SUPDATE;
	}
	ds.state |= DMA_COMPLETE;
	logerror("dma end slot %d irq %d\n", slot, dma_irqs[slot]);
	if(dma_irqs[slot] >= 0)
		irq_set(dma_irqs[slot], true);
}

void next_state::dma_check_end(int slot, bool eof)
{
	dma_slot &ds = dma_slots[slot];
	if(eof || ds.current == (ds.limit & 0x7fffffff))
		dma_end(slot);
}

uint32_t next_state::dma_regs_r(offs_t offset)
{
	int slot = offset >> 2;
	int reg = offset & 3;

	uint32_t res;

	switch(reg) {
	case 0:
		res = dma_slots[slot].current;
		break;
	case 1:
		res = dma_slots[slot].limit;
		break;
	case 2:
		res = dma_slots[slot].chain_start;
		break;
	case 3: default:
		res = dma_slots[slot].chain_limit;
		break;
	}

	logerror("dma_regs_r %s:%d %08x (%08x)\n", dma_name(slot), reg, res, maincpu->pc());

	return res;
}

void next_state::dma_regs_w(offs_t offset, uint32_t data)
{
	int slot = offset >> 2;
	int reg = offset & 3;

	logerror("dma_regs_w %s:%d %08x (%08x)\n", dma_name(slot), reg, data, maincpu->pc());
	switch(reg) {
	case 0:
		dma_slots[slot].start = data;
		dma_slots[slot].current = data;
		break;
	case 1:
		dma_slots[slot].limit = data;
		break;
	case 2:
		dma_slots[slot].chain_start = data;
		break;
	case 3:
		dma_slots[slot].chain_limit = data;
		break;
	}
}

uint32_t next_state::dma_ctrl_r(offs_t offset)
{
	int slot = offset >> 2;
	int reg = offset & 3;

	if(maincpu->pc() != 0x409bb4e)
		logerror("dma_ctrl_r %s:%d %02x (%08x)\n", dma_name(slot), reg, dma_slots[slot].state, maincpu->pc());

	return reg ? 0 : dma_slots[slot].state << 24;
}

void next_state::dma_ctrl_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	int slot = offset >> 2;
	int reg = offset & 3;
	logerror("dma_ctrl_w %s:%d %08x @ %08x (%08x)\n", dma_name(slot), reg, data, mem_mask, maincpu->pc());
	if(!reg) {
		if(ACCESSING_BITS_16_23)
			dma_do_ctrl_w(slot, data >> 16);
		else if(ACCESSING_BITS_24_31)
			dma_do_ctrl_w(slot, data >> 24);
	}
}

void next_state::dma_do_ctrl_w(int slot, uint8_t data)
{
	const auto name = dma_name(slot);
#if 0
	logerror("dma_ctrl_w %s %02x (%08x)\n", name, data, maincpu->pc());

	logerror("  ->%s%s%s%s%s%s%s\n",
			data & DMA_SETENABLE ? " enable" : "",
			data & DMA_SETSUPDATE ? " supdate" : "",
			data & DMA_SETREAD ? " read" : "",
			data & DMA_CLRCOMPLETE ? " complete" : "",
			data & DMA_RESET ? " reset" : "",
			data & DMA_INITBUF ? " initbuf" : "",
			data & DMA_INITBUFTURBO ? " initbufturbo" : "");
#endif
	if(data & DMA_SETENABLE)
		logerror("dma enable %s %s %08x (%08x)\n", name, data & DMA_SETREAD ? "read" : "write", (dma_slots[slot].limit-dma_slots[slot].start) & 0x7fffffff, maincpu->pc());

	dma_slot &ds = dma_slots[slot];
	if(data & (DMA_RESET|DMA_INITBUF|DMA_INITBUFTURBO)) {
		ds.state = 0;
		if(dma_irqs[slot] >= 0)
			irq_set(dma_irqs[slot], false);
	}
	if(data & DMA_SETSUPDATE) {
		ds.state |= DMA_SUPDATE;
		ds.supdate = true;
	}
	if(data & DMA_SETREAD)
		ds.state |= DMA_READ;
	if(data & DMA_CLRCOMPLETE) {
		ds.state &= ~DMA_COMPLETE;
		if(dma_irqs[slot] >= 0)
			irq_set(dma_irqs[slot], false);
	}
	if(data & DMA_SETENABLE) {
		ds.state |= DMA_ENABLE;
		//      logerror("dma slot %d drq=%s\n", slot, ds.drq ? "on" : "off");
		if(ds.drq)
			dma_drq_w(slot, ds.drq);
	}
}

int const next_state::scsi_clocks[4] = { 10000000, 12000000, 20000000, 16000000 };

uint32_t next_state::scsictrl_r(offs_t offset, uint32_t mem_mask)
{
	uint32_t res = (scsictrl << 24) | (scsistat << 16);
	logerror("scsictrl_read %08x @ %08x (%08x)\n", res, mem_mask, maincpu->pc());
	return res;
}

void next_state::scsictrl_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	if(ACCESSING_BITS_24_31) {
		scsictrl = data >> 24;
		if(scsictrl & 0x02)
			scsi->reset();
		scsi->set_clock(scsi_clocks[scsictrl >> 6]);

		logerror("SCSIctrl %dMHz int=%s dma=%s dmadir=%s%s%s dest=%s (%08x)\n",
				scsi_clocks[scsictrl >> 6]/1000000,
				scsictrl & 0x20 ? "on" : "off",
				scsictrl & 0x10 ? "on" : "off",
				scsictrl & 0x08 ? "read" : "write",
				scsictrl & 0x04 ? " flush" : "",
				scsictrl & 0x02 ? " reset" : "",
				scsictrl & 0x01 ? "wd3392" : "ncr53c90",
				maincpu->pc());
	}
	if(ACCESSING_BITS_16_23) {
		scsistat = data >> 16;
		logerror("SCSIstat %02x (%08x)\n", data, maincpu->pc());
	}
}

uint32_t next_state::event_counter_r(offs_t offset, uint32_t mem_mask)
{
	// Event counters, around that time, are usually fixed-frequency counters.
	// This one being 1MHz seems to make sense

	// The v74 rom seems pretty convinced that it's 20 bits only.

	if(ACCESSING_BITS_24_31)
		eventc_latch = machine().time().as_ticks(1000000) & 0xfffff;
	return eventc_latch;
}

uint32_t next_state::dsp_r()
{
	return 0x7fffffff;
}

void next_state::fdc_control_w(uint32_t data)
{
	logerror("FDC write %02x (%08x)\n", data >> 24, maincpu->pc());
}

uint32_t next_state::fdc_control_r()
{
	// Type of floppy present
	// 0 = no floppy in drive
	// 1 = ed
	// 2 = hd
	// 3 = dd

	// The rom strangely can't boot on anything else than ED, it has
	// code for the other densities but forces ED for some mysterious
	// reason.  The kernel otoh behaves as expected.

	if(fdc) {
		floppy_image_device *fdev = floppy0->get_device();
		if(fdev->exists()) {
			uint32_t variant = fdev->get_variant();
			switch(variant) {
			case floppy_image::SSSD:
			case floppy_image::SSDD:
			case floppy_image::DSDD:
				return 3 << 24;

			case floppy_image::DSHD:
				return 2 << 24;

			case floppy_image::DSED:
				return 1 << 24;
			}
		}
	}

	return 0 << 24;
}

uint32_t next_state::phy_r(offs_t offset)
{
	logerror("phy_r %d %08x (%08x)\n", offset, phy[offset], maincpu->pc());
	return phy[offset] | (0 << 24);
}

void next_state::phy_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	COMBINE_DATA(phy+offset);
	logerror("phy_w %d %08x (%08x)\n", offset, phy[offset], maincpu->pc());
}

TIMER_CALLBACK_MEMBER(next_state::timer_tick)
{
	irq_set(29, true);
	timer_data = timer_next_data;
	if(timer_ctrl & 0x40000000)
		timer_start();
	else
		timer_ctrl &= 0x7fffffff;
}

uint32_t next_state::timer_data_r()
{
	if(timer_ctrl & 0x80000000)
		timer_update();
	return timer_data;
}

void next_state::timer_data_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	if(timer_ctrl & 0x80000000) {
		COMBINE_DATA(&timer_next_data);
		timer_next_data &= 0xffff0000;
	} else {
		COMBINE_DATA(&timer_data);
		timer_data &= 0xffff0000;
	}
}

uint32_t next_state::timer_ctrl_r()
{
	irq_set(29, false);
	return timer_ctrl;
}

void next_state::timer_ctrl_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	bool oldact = timer_ctrl & 0x80000000;
	COMBINE_DATA(&timer_ctrl);
	bool newact = timer_ctrl & 0x80000000;
	if(oldact != newact) {
		if(oldact) {
			timer_update();
			irq_set(29, false);
		} else {
			timer_next_data = timer_data;
			timer_start();
		}
	}
}

void next_state::timer_update()
{
	int delta = timer_vbase - (machine().time() - timer_tbase).as_ticks(1000000);
	if(delta < 0)
		delta = 0;
	timer_data = delta << 16;
}

void next_state::timer_start()
{
	timer_tbase = machine().time();
	timer_vbase = timer_data >> 16;
	timer_tm->adjust(attotime::from_usec(timer_vbase));
}

void next_state::scc_irq(int state)
{
	irq_set(17, state);
}

void next_state::keyboard_irq(int state)
{
	irq_set(3, state);
}

void next_state::power_irq(int state)
{
	irq_set(2, state);
}

void next_state::nmi_irq(int state)
{
	irq_set(31, state);
}

void next_state::fdc_irq(int state)
{
	irq_set(7, state);
}

void next_state::fdc_drq(int state)
{
	dma_drq_w(1, state);
}

void next_state::net_tx_irq(int state)
{
	irq_set(10, state);
}

void next_state::net_rx_irq(int state)
{
	irq_set(9, state);
}

void next_state::net_tx_drq(int state)
{
	dma_drq_w(17, state);
}

void next_state::net_rx_drq(int state)
{
	dma_drq_w(21, state);
}

void next_state::mo_irq(int state)
{
	irq_set(13, state);
}

void next_state::mo_drq(int state)
{
	dma_drq_w(5, state);
}

void next_state::scsi_irq(int state)
{
	irq_set(12, state);
}

void next_state::scsi_drq(int state)
{
	dma_drq_w(1, state);
}

void next_state::ramdac_w(offs_t offset, uint8_t data)
{
	switch(offset) {
	case 0:
		switch(data) {
		case 0x05:
			if(screen_color)
				irq_set(13, false);
			else
				irq_set(5, false);
			vbl_enabled = false;
			break;

		case 0x06:
			vbl_enabled = true;
			break;

		default:
			logerror("ramdac_w %d, %02x\n", offset, data);
			break;
		}
		break;

	default:
		logerror("ramdac_w %d, %02x\n", offset, data);
		break;
	}
}

void next_state::setup(uint32_t _scr1, int size_x, int size_y, int skip, bool color)
{
	scr1 = _scr1;
	screen_sx = size_x;
	screen_sy = size_y;
	screen_skip = skip;
	screen_color = color;
}

void next_state::machine_start()
{
	save_item(NAME(scr2));
	save_item(NAME(irq_status));
	save_item(NAME(irq_mask));
	save_item(NAME(irq_level));
	save_item(NAME(phy));
	save_item(NAME(scsictrl));
	save_item(NAME(scsistat));
	save_item(NAME(timer_tbase));
	save_item(NAME(timer_vbase));
	save_item(NAME(timer_data));
	save_item(NAME(timer_next_data));
	save_item(NAME(timer_ctrl));
	save_item(NAME(eventc_latch));
	save_item(NAME(esp));

	for(int i=0; i<0x20; i++) {
		save_item(NAME(dma_slots[i].start), i);
		save_item(NAME(dma_slots[i].limit), i);
		save_item(NAME(dma_slots[i].chain_start), i);
		save_item(NAME(dma_slots[i].chain_limit), i);
		save_item(NAME(dma_slots[i].current), i);
		save_item(NAME(dma_slots[i].state), i);
		save_item(NAME(dma_slots[i].supdate), i);
		save_item(NAME(dma_slots[i].restart), i);
		save_item(NAME(dma_slots[i].drq), i);
	}

	timer_tm = timer_alloc(FUNC(next_state::timer_tick), this);
}

void next_state::machine_reset()
{
	scr2 = 0;
	irq_status = 0;
	irq_mask = 0;
	irq_level = 0;
	esp = 0;
	scsictrl = 0;
	scsistat = 0;
	phy[0] = phy[1] = 0;
	eventc_latch = 0;
	timer_vbase = 0;
	timer_data = 0;
	timer_next_data = 0;
	timer_ctrl = 0;
	vbl_enabled = true;
	dma_drq_w(4, true); // soundout
}

void next_state::vblank_w(int state)
{
	if(vbl_enabled) {
		if(screen_color)
			irq_set(13, state);
		else
			irq_set(5, state);
	}
}

void next_state::next_mem(address_map &map)
{
	map(0x00000000, 0x0001ffff).rom().region("user1", 0);
	map(0x01000000, 0x0101ffff).rom().region("user1", 0);
	map(0x02000000, 0x020001ff).mirror(0x300200).rw(FUNC(next_state::dma_ctrl_r), FUNC(next_state::dma_ctrl_w));
	map(0x02004000, 0x020041ff).mirror(0x300200).rw(FUNC(next_state::dma_regs_r), FUNC(next_state::dma_regs_w));
	map(0x02006000, 0x0200600f).mirror(0x300000).m(net, FUNC(mb8795_device::map));
//  map(0x02006010, 0x02006013).mirror(0x300000); memory timing
	map(0x02007000, 0x02007003).mirror(0x300000).r(FUNC(next_state::irq_status_r));
	map(0x02007800, 0x02007803).mirror(0x300000).rw(FUNC(next_state::irq_mask_r), FUNC(next_state::irq_mask_w));
	map(0x02008000, 0x02008003).mirror(0x300000).r(FUNC(next_state::dsp_r));
	map(0x0200c000, 0x0200c003).mirror(0x300000).r(FUNC(next_state::scr1_r));
	map(0x0200c800, 0x0200c803).mirror(0x300000).r(FUNC(next_state::rom_map_r));
	map(0x0200d000, 0x0200d003).mirror(0x300000).rw(FUNC(next_state::scr2_r), FUNC(next_state::scr2_w));
//  map(0x0200d800, 0x0200d803).mirror(0x300000); RMTINT
	map(0x0200e000, 0x0200e00b).mirror(0x300000).m(keyboard, FUNC(nextkbd_device::amap));
//  map(0x0200f000, 0x0200f003).mirror(0x300000); printer
//  map(0x02010000, 0x02010003).mirror(0x300000); brightness
	map(0x02014000, 0x0201400f).mirror(0x300000).m(scsi, FUNC(ncr53c90_device::map));
	map(0x02014020, 0x02014023).mirror(0x300000).rw(FUNC(next_state::scsictrl_r), FUNC(next_state::scsictrl_w));
	map(0x02016000, 0x02016003).mirror(0x300000).rw(FUNC(next_state::timer_data_r), FUNC(next_state::timer_data_w));
	map(0x02016004, 0x02016007).mirror(0x300000).rw(FUNC(next_state::timer_ctrl_r), FUNC(next_state::timer_ctrl_w));
	map(0x02018000, 0x02018003).mirror(0x300000).rw(scc, FUNC(scc8530_device::dc_ab_r), FUNC(scc8530_device::dc_ab_w));
//  map(0x02018004, 0x02018007).mirror(0x300000); SCC CLK
//  map(0x02018190, 0x02018197).mirror(0x300000); warp 9c DRAM timing
//  map(0x02018198, 0x0201819f).mirror(0x300000); warp 9c VRAM timing
	map(0x0201a000, 0x0201a003).mirror(0x300000).r(FUNC(next_state::event_counter_r)); // EVENTC
//  map(0x020c0000, 0x020c0004).mirror(0x300000); BMAP
	map(0x020c0030, 0x020c0037).mirror(0x300000).rw(FUNC(next_state::phy_r), FUNC(next_state::phy_w));
	map(0x04000000, 0x07ffffff).ram(); //work ram
//  map(0x0c000000, 0x0c03ffff) video RAM w A+B-AB function
//  map(0x0d000000, 0x0d03ffff) video RAM w (1-A)B function
//  map(0x0e000000, 0x0e03ffff) video RAM w ceil(A+B) function
//  map(0x0f000000, 0x0f03ffff) video RAM w AB function
//  map(0x10000000, 0x1003ffff) main RAM w A+B-AB function
//  map(0x14000000, 0x1403ffff) main RAM w (1-A)B function
//  map(0x18000000, 0x1803ffff) main RAM w ceil(A+B) function
//  map(0x1c000000, 0x1c03ffff) main RAM w AB function
}

void next_state::next_mo_mem(address_map &map)
{
	map(0x02012000, 0x0201201f).mirror(0x300000).m(mo, FUNC(nextmo_device::map));
}

void next_state::next_0b_m_nofdc_mem(address_map &map)
{
	next_mem(map);
	next_mo_mem(map);
	map(0x0b000000, 0x0b03ffff).ram().share("vram");
}

void next_state::next_fdc_mem(address_map &map)
{
	next_mem(map);
	map(0x02014100, 0x02014107).mirror(0x300000).m(fdc, FUNC(n82077aa_device::map));
	map(0x02014108, 0x0201410b).mirror(0x300000).rw(FUNC(next_state::fdc_control_r), FUNC(next_state::fdc_control_w));
}

void next_state::next_0b_m_mem(address_map &map)
{
	next_fdc_mem(map);
	map(0x0b000000, 0x0b03ffff).ram().share("vram");
}

void next_state::next_0b_m_mo_mem(address_map &map)
{
	next_fdc_mem(map);
	next_mo_mem(map);
	map(0x0b000000, 0x0b03ffff).ram().share("vram");
}

void next_state::next_0c_m_mem(address_map &map)
{
	next_fdc_mem(map);
	map(0x0c000000, 0x0c1fffff).ram().share("vram");
}

void next_state::next_0c_c_mem(address_map &map)
{
	next_fdc_mem(map);
	map(0x0c000000, 0x0c1fffff).ram().share("vram");
	map(0x02018180, 0x02018183).mirror(0x300000).w(FUNC(next_state::ramdac_w));
}

void next_state::next_0c_m_mo_mem(address_map &map)
{
	next_fdc_mem(map);
	next_mo_mem(map);
	map(0x0c000000, 0x0c1fffff).ram().share("vram");
}

void next_state::next_0c_c_mo_mem(address_map &map)
{
	next_fdc_mem(map);
	next_mo_mem(map);
	map(0x0c000000, 0x0c1fffff).ram().share("vram");
	map(0x02018180, 0x02018183).mirror(0x300000).w(FUNC(next_state::ramdac_w));
}

void next_state::next_2c_c_mem(address_map &map)
{
	next_fdc_mem(map);
	map(0x2c000000, 0x2c1fffff).ram().share("vram");
	map(0x02018180, 0x02018183).mirror(0x300000).w(FUNC(next_state::ramdac_w));
}


/* Input ports */
static INPUT_PORTS_START( next )
INPUT_PORTS_END

static void next_floppies(device_slot_interface &device)
{
	device.option_add("35ed", FLOPPY_35_ED);
}

static void next_scsi_devices(device_slot_interface &device)
{
	device.option_add("cdrom", NSCSI_CDROM);
	device.option_add("harddisk", NSCSI_HARDDISK);
	device.option_add_internal("ncr53c90", NCR53C90);
}

void next_state::ncr53c90(device_t *device)
{
	ncr53c90_device &adapter = downcast<ncr53c90_device &>(*device);

	adapter.set_clock(10000000);
	adapter.irq_handler_cb().set(*this, FUNC(next_state::scsi_irq));
	adapter.drq_handler_cb().set(*this, FUNC(next_state::scsi_drq));
}

void next_state::next_base(machine_config &config)
{
	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(next_state::screen_update));
	screen.set_size(1120, 900);
	screen.set_visarea(0, 1120-1, 0, 832-1);
	screen.screen_vblank().set(FUNC(next_state::vblank_w));

	// devices
	NSCSI_BUS(config, "scsibus");

	MCCS1850(config, rtc, XTAL(32'768));

	SCC8530(config, scc, XTAL(25'000'000));
	scc->out_int_callback().set(FUNC(next_state::scc_irq));

	NEXTKBD(config, keyboard, 0);
	keyboard->int_change_wr_callback().set(FUNC(next_state::keyboard_irq));
	keyboard->int_power_wr_callback().set(FUNC(next_state::power_irq));
	keyboard->int_nmi_wr_callback().set(FUNC(next_state::nmi_irq));

	NSCSI_CONNECTOR(config, "scsibus:0", next_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsibus:1", next_scsi_devices, "cdrom");
	NSCSI_CONNECTOR(config, "scsibus:2", next_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:3", next_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:4", next_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:5", next_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:6", next_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:7", next_scsi_devices, "ncr53c90", true).set_option_machine_config("ncr53c90", [this] (device_t *device) { ncr53c90(device); });

	MB8795(config, net, 0);
	net->tx_irq().set(FUNC(next_state::net_tx_irq));
	net->rx_irq().set(FUNC(next_state::net_rx_irq));
	net->tx_drq().set(FUNC(next_state::net_tx_drq));
	net->rx_drq().set(FUNC(next_state::net_rx_drq));

	SOFTWARE_LIST(config, "cdrom_list").set_original("next_cdrom");
	SOFTWARE_LIST(config, "hdd_list").set_original("next_hdd");
}

void next_state::next_mo_config(machine_config &config)
{
	NEXTMO(config, mo, 0);
	mo->irq_wr_callback().set(FUNC(next_state::mo_irq));
	mo->drq_wr_callback().set(FUNC(next_state::mo_drq));
}

void next_state::next_fdc_config(machine_config &config)
{
	N82077AA(config, fdc, 24'000'000, n82077aa_device::mode_t::PS2);
	fdc->intrq_wr_callback().set(FUNC(next_state::fdc_irq));
	fdc->drq_wr_callback().set(FUNC(next_state::fdc_drq));
	FLOPPY_CONNECTOR(config, "fdc:0", next_floppies, "35ed", floppy_image_device::default_pc_floppy_formats);

	// software list
	SOFTWARE_LIST(config, "flop_list").set_original("next");
}

void next_state::next_mo_base(machine_config &config)
{
	next_base(config);
	next_mo_config(config);
}

void next_state::next_fdc_base(machine_config &config)
{
	next_base(config);
	next_fdc_config(config);
}

void next_state::next_mo_fdc_base(machine_config &config)
{
	next_base(config);
	next_mo_config(config);
	next_fdc_config(config);
}

void next_state::next(machine_config &config)
{
	next_mo_base(config);
	M68030(config, maincpu, XTAL(25'000'000));
	maincpu->set_addrmap(AS_PROGRAM, &next_state::next_0b_m_nofdc_mem);
}

void next_state::nextc(machine_config &config)
{
	next_mo_fdc_base(config);
	M68040(config, maincpu, XTAL(25'000'000));
	maincpu->set_addrmap(AS_PROGRAM, &next_state::next_0b_m_mo_mem);
}

void next_state::nexts(machine_config &config)
{
	next_fdc_base(config);
	M68040(config, maincpu, XTAL(25'000'000));
	maincpu->set_addrmap(AS_PROGRAM, &next_state::next_0b_m_mem);
}

void next_state::nexts2(machine_config &config)
{
	next_fdc_base(config);
	M68040(config, maincpu, XTAL(25'000'000));
	maincpu->set_addrmap(AS_PROGRAM, &next_state::next_0b_m_mem);
}

void next_state::nextsc(machine_config &config)
{
	next_fdc_base(config);
	M68040(config, maincpu, XTAL(25'000'000));
	maincpu->set_addrmap(AS_PROGRAM, &next_state::next_2c_c_mem);
}

void next_state::nextst(machine_config &config)
{
	next_fdc_base(config);
	M68040(config, maincpu, XTAL(33'000'000));
	maincpu->set_addrmap(AS_PROGRAM, &next_state::next_0b_m_mem);
}

void next_state::nextstc(machine_config &config)
{
	next_fdc_base(config);
	M68040(config, maincpu, XTAL(33'000'000));
	maincpu->set_addrmap(AS_PROGRAM, &next_state::next_0c_c_mem);
	subdevice<screen_device>("screen")->set_visarea(0, 832-1, 0, 624-1);
}

void next_state::nextct(machine_config &config)
{
	next_mo_fdc_base(config);
	M68040(config, maincpu, XTAL(33'000'000));
	maincpu->set_addrmap(AS_PROGRAM, &next_state::next_0c_m_mo_mem);
}

void next_state::nextctc(machine_config &config)
{
	next_mo_fdc_base(config);
	M68040(config, maincpu, XTAL(33'000'000));
	maincpu->set_addrmap(AS_PROGRAM, &next_state::next_0c_c_mo_mem);
	subdevice<screen_device>("screen")->set_visarea(0, 832-1, 0, 624-1);
}

/* ROM definition */
#define ROM_NEXT_V1 \
	ROM_REGION32_BE( 0x20000, "user1", ROMREGION_ERASEFF ) \
	ROM_SYSTEM_BIOS( 0, "v12", "v1.2" ) /* MAC address/serial number word at 0xC: 005AD0 */ \
	ROMX_LOAD( "rev_1.2.bin",     0x0000, 0x10000, CRC(7070bd78) SHA1(e34418423da61545157e36b084e2068ad41c9e24), ROM_BIOS(0)) /* Label: "(C) 1990 NeXT, Inc. // All Rights Reserved. // Release 1.2 // 1142.02", underlabel exists but unknown */ \
	ROM_SYSTEM_BIOS( 1, "v11", "v1.1 v42" ) /* MAC address/serial number word at 0xC: 0052D2 */ \
	ROMX_LOAD( "rev_1.1_v42.bin",     0x0000, 0x10000, CRC(6cabe43e) SHA1(19ca721af5e637a94bf96ff19729abd0c79e1638), ROM_BIOS(1)) \
	ROM_SYSTEM_BIOS( 2, "v10", "v1.0 v41" ) /* MAC address/serial number word at 0xC: 003090 */ \
	ROMX_LOAD( "rev_1.0_v41.bin", 0x0000, 0x10000, CRC(54df32b9) SHA1(06e3ecf09ab67a571186efd870e6b44028612371), ROM_BIOS(2)) /* Label: "(C) 1989 NeXT, Inc. // All Rights Reserved. // Release 1.0 // 1142.00", underlabel: "MYF // 1.0.41 // 0D5C" */ \
	ROM_SYSTEM_BIOS( 3, "v10p", "v1.0 v41 alternate" ) /* MAC address/serial number word at 0xC: 0023D9 */ \
	ROMX_LOAD( "rev_1.0_proto.bin", 0x0000, 0x10000, CRC(f44974f9) SHA1(09eaf9f5d47e379cfa0e4dc377758a97d2869ddc), ROM_BIOS(3)) /* Label: "(C) 1989 NeXT, Inc. // All Rights Reserved. // Release 1.0 // 1142.00", no underlabel */ \
	ROM_SYSTEM_BIOS( 4, "v10q", "v1.0 v40" ) /* MAC address/serial number word at 0xC: 001ED0 */ \
	ROMX_LOAD( "rev_1.0_v40.bin", 0x0000, 0x10000, CRC(9effb1b9) SHA1(94a3e30a92f7a9c7d44b90ac7954ed8ec5b7f6ca), ROM_BIOS(4)) \
	ROM_SYSTEM_BIOS( 5, "v08", "v0.8 v31" ) /* MAC address/serial number word at 0xC: 001A3D */ \
	ROMX_LOAD( "rev_0.8_v31.bin", 0x0000, 0x10000, CRC(907ee077) SHA1(cfd853c8b22b1a41d6b35609fc029a905482ad6d), ROM_BIOS(5))

#define ROM_NEXT_V2 \
	ROM_REGION32_BE( 0x20000, "user1", ROMREGION_ERASEFF ) \
	ROM_SYSTEM_BIOS( 0, "v25", "v2.5 v66" ) /* MAC address/serial number word at 0xC: 00F302 */ \
	ROMX_LOAD( "rev_2.5_v66.bin", 0x0000, 0x20000, CRC(f47e0bfe) SHA1(b3534796abae238a0111299fc406a9349f7fee24), ROM_BIOS(0)) \
	ROM_SYSTEM_BIOS( 1, "v24", "v2.4 v65" ) /* MAC address/serial number word at 0xC: 00A634 */ \
	ROMX_LOAD( "rev_2.4_v65.bin", 0x0000, 0x20000, CRC(74e9e541) SHA1(67d195351288e90818336c3a84d55e6a070960d2), ROM_BIOS(1)) \
	ROM_SYSTEM_BIOS( 2, "v22", "v2.2 v63" ) /* MAC address/serial number word at 0xC: 00894C */ \
	ROMX_LOAD( "rev_2.2_v63.bin", 0x0000, 0x20000, CRC(739d7c07) SHA1(48ffe54cf2038782a92a0850337c5c6213c98571), ROM_BIOS(2)) /* Label: "(C) 1990 NeXT Computer, Inc. // All Rights Reserved. // Release 2.1 // 2918.AB" */ \
	ROM_SYSTEM_BIOS( 3, "v21", "v2.1 v59" ) /* MAC address/serial number word at 0xC: 0072FE */ \
	ROMX_LOAD( "rev_2.1_v59.bin", 0x0000, 0x20000, CRC(f20ef956) SHA1(09586c6de1ca73995f8c9b99870ee3cc9990933a), ROM_BIOS(3)) \
	ROM_SYSTEM_BIOS( 4, "v20", "v2.0 v58" ) /* MAC address/serial number word at 0xC: 0063E4 */ \
	ROMX_LOAD( "rev_2.0_v58.bin", 0x0000, 0x20000, CRC(52a26b78) SHA1(6f168fbc354199ad5a6bfc82be00673751fc0119), ROM_BIOS(3)) \
	ROM_SYSTEM_BIOS( 5, "v12", "v1.2 v58" ) /* MAC address/serial number word at 0xC: 006372 */ \
	ROMX_LOAD( "rev_1.2_v58.bin", 0x0000, 0x20000, CRC(b815b6a4) SHA1(97d8b09d03616e1487e69d26609487486db28090), ROM_BIOS(5)) /* Label: "V58 // (C) 1990 NeXT, Inc. // All Rights Reserved // Release 1.2 // 1142.02" */

#define ROM_NEXT_V3 \
	ROM_REGION32_BE( 0x20000, "user1", ROMREGION_ERASEFF ) \
	ROM_SYSTEM_BIOS( 0, "v33", "v3.3 v74" ) /* MAC address/serial number word at 0xC: 123456 */ \
	ROMX_LOAD( "rev_3.3_v74.bin", 0x0000, 0x20000, CRC(fbc3a2cd) SHA1(a9bef655f26f97562de366e4a33bb462e764c929), ROM_BIOS(0)) /* Label: NeXT Computer // (C) 1992 // Rev 3.3 v74 */ \
	ROM_SYSTEM_BIOS( 1, "v32", "v3.2 v72" ) /* MAC address/serial number word at 0xC: 012F31 */ \
	ROMX_LOAD( "rev_3.2_v72.bin", 0x0000, 0x20000, CRC(e750184f) SHA1(ccebf03ed090a79c36f761265ead6cd66fb04329), ROM_BIOS(1)) /* Label: MYF // 2919 // REV 3.2V72 */ \
	ROM_SYSTEM_BIOS( 2, "v31", "v3.1 v71" ) /* MAC address/serial number word at 0xC: 0129F2 */ \
	ROMX_LOAD( "rev_3.1_v71.bin", 0x0000, 0x20000, CRC(0371daa7) SHA1(61fc4bec520022ff7a20e2c930d59fd9883e8917), ROM_BIOS(2)) \
	ROM_SYSTEM_BIOS( 3, "v30", "v3.0 v70" ) /* MAC address/serial number word at 0xC: 0106E8 */ \
	ROMX_LOAD( "rev_3.0_v70.bin", 0x0000, 0x20000, CRC(37250453) SHA1(a7e42bd6a25c61903c8ca113d0b9a624325ee6cf), ROM_BIOS(3)) /* Label: NeXT Computer, // Inc. (C) 1991 // Rev 3.0 v70 M */


ROM_START(next)
	ROM_NEXT_V1
ROM_END

ROM_START(nextc)
	ROM_NEXT_V2
ROM_END

ROM_START(nexts)
	ROM_NEXT_V2
ROM_END

ROM_START(nexts2)
	ROM_NEXT_V2
ROM_END

ROM_START(nextsc)
	ROM_NEXT_V2
ROM_END

ROM_START(nextst)
	ROM_NEXT_V3
ROM_END

ROM_START(nextstc)
	ROM_NEXT_V3
ROM_END

ROM_START(nextct)
	ROM_NEXT_V3
ROM_END

ROM_START(nextctc)
	ROM_NEXT_V3
ROM_END

void next_state::init_next()
{
	setup(0x00010002, 1120, 832, 2, false);
}

void next_state::init_nextc()
{
	setup(0x00010002, 1120, 832, 2, false);
}

void next_state::init_nexts()
{
	setup(0x00011002, 1120, 832, 2, false);
}

void next_state::init_nexts2()
{
	setup(0x00012102, 1120, 832, 2, false);
}

void next_state::init_nextsc()
{
	setup(0x00013102, 1120, 832, 16, true);
}

void next_state::init_nextst()
{
	setup(0x00014103, 1120, 832, 2, false);
}

void next_state::init_nextstc()
{
	setup(0x00015103,  832, 624, 0, true);
}

void next_state::init_nextct()
{
	setup(0x00018103, 1120, 832, 0, false);
}

void next_state::init_nextctc()
{
	setup(0x00019103,  832, 624, 0, true);
}

/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT          COMPANY              FULLNAME                     FLAGS
COMP( 1987, next,    0,      0,      next,    next,  next_state, init_next,    "Next Software Inc", "NeXT Computer",             MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1990, nexts,   0,      0,      nexts,   next,  next_state, init_nexts,   "Next Software Inc", "NeXTstation",               MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1990, nextc,   nexts,  0,      next,    next,  next_state, init_nextc,   "Next Software Inc", "NeXTcube",                  MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1990, nexts2,  nexts,  0,      nexts2,  next,  next_state, init_nexts2,  "Next Software Inc", "NeXTstation (X15 variant)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1990, nextsc,  nexts,  0,      nextsc,  next,  next_state, init_nextsc,  "Next Software Inc", "NeXTstation Color",         MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1990, nextst,  0,      0,      nextst,  next,  next_state, init_nextst,  "Next Software Inc", "NeXTstation Turbo",         MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1990, nextstc, nextst, 0,      nextstc, next,  next_state, init_nextstc, "Next Software Inc", "NeXTstation Turbo Color",   MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1992, nextct,  nextst, 0,      nextct,  next,  next_state, init_nextct,  "Next Software Inc", "NeXTcube Turbo",            MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1992, nextctc, nextst, 0,      nextctc, next,  next_state, init_nextctc, "Next Software Inc", "NeXTcube Turbo Color",      MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



nforcepc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Samuele Zannoli

/*
  Computer based on a motherboard utilizing the nForce chipset (also known as CRUSH11 or CRUSH12)

  Start with the following components:
  - An Asus A7N266-C motherboard using:
    - nForce 415-D northbridge
    - nForce MCP-D southbridge (with integrated APU)
    - ITE IT8703F-A SuperIO
    - Asus AS99127F chip
  - An AMD Athlon XP processor
  - An IDE hard disk
  - A floppy disk drive
  - A keyboard
  - A ddr dimm memory module
  Later add:
  - A nVidia NV25 based AGP video card

*/

#include "emu.h"
#include "nforcepc.h"

#include "xbox_pci.h"

#include "bus/ata/atadev.h"
#include "bus/rs232/hlemouse.h"
#include "bus/rs232/null_modem.h"
#include "bus/rs232/rs232.h"
#include "bus/rs232/sun_kbd.h"
#include "bus/rs232/terminal.h"
#include "cpu/i386/athlon.h"
#include "machine/pci-ide.h"
#include "machine/pckeybrd.h"
#include "bus/isa/isa.h"
#include "bus/pci/virge_pci.h"

#include "formats/naslite_dsk.h"


#if 1
// for now let's use this as the contents of the spd chip in the ddr dimm memory module
static const uint8_t test_spd_data[] = {
	0x80,0x08,0x07,0x0D,0x0A,0x01,0x40,0x00,0x04,0x75,0x75,0x00,0x82,0x10,0x00,0x01,
	0x0E,0x04,0x0C,0x01,0x02,0x20,0xC0,0xA0,0x75,0x00,0x00,0x50,0x3C,0x50,0x2D,0x40,
	0x90,0x90,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x41,0x4B,0x30,0x32,0x75,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xEA,
	0xAD,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x59,0x4D,0x44,0x35,0x33,0x32,
	0x4D,0x36,0x34,0x36,0x43,0x36,0x2D,0x48,0x20,0x20,0x20
};
#else
// From a HYS72D64320HU-5-C (512MB PC3200 (400mHz) DDR CL3 ECC)
static const uint8_t test_spd_data[] = {
	0x80,0x08,0x07,0x0d,0x0a,0x02,0x48,0x00,0x04,0x50,0x50,0x02,0x82,0x08,0x08,0x01,
	0x0e,0x04,0x1c,0x01,0x02,0x20,0xc1,0x60,0x50,0x75,0x50,0x3c,0x28,0x3c,0x28,0x40,
	0x60,0x60,0x40,0x40,0x00,0x00,0x00,0x00,0x00,0x37,0x41,0x28,0x28,0x50,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,
	0xc1,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x45,0x37,0x32,0x44,0x36,0x34,0x33,0x32,
	0x30,0x48,0x55,0x35,0x43,0x20,0x20,0x20,0x20,0x20,0x20,0x02,0x4c,0x05,0x44,0x01,
	0x08,0x56,0x26,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
#endif

/*
  Pci devices
*/

// NVIDIA Corporation nForce CPU bridge

DEFINE_DEVICE_TYPE(CRUSH11, crush11_host_device, "crush11", "NVIDIA Corporation nForce CPU bridge")

void crush11_host_device::config_map(address_map &map)
{
	pci_host_device::config_map(map);
	map(0x10, 0x27).rw(FUNC(pci_device::address_base_r), FUNC(pci_device::address_base_w));
	map(0x84, 0x87).rw(FUNC(crush11_host_device::ram_size_r), FUNC(crush11_host_device::ram_size_w));
	map(0xf0, 0xf0).rw(FUNC(crush11_host_device::unknown_r), FUNC(crush11_host_device::unknown_w));
}

uint8_t crush11_host_device::header_type_r()
{
	return 0x80; // from lspci dump
}

crush11_host_device::crush11_host_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: pci_host_device(mconfig, CRUSH11, tag, owner, clock)
	, cpu(*this, finder_base::DUMMY_TAG)
	, biosrom(*this, finder_base::DUMMY_TAG)
	, ram_size(0)
{
}

void crush11_host_device::device_start()
{
	pci_host_device::device_start();
	set_multifunction_device(true);
	set_spaces(&cpu->space(AS_DATA), &cpu->space(AS_IO));

	memory_window_start = 0;
	memory_window_end = 0xffffffff;
	memory_offset = 0;
	io_window_start = 0;
	io_window_end = 0xffff;
	io_offset = 0;
	status = 0x00b0;
	command = 0x0000;

	add_map(64 * 1024 * 1024, M_MEM | M_PREF, FUNC(crush11_host_device::aperture_map));
}

void crush11_host_device::reset_all_mappings()
{
	pci_host_device::reset_all_mappings();
}

void crush11_host_device::device_reset()
{
	pci_host_device::device_reset();
}

void crush11_host_device::map_extra(uint64_t memory_window_start, uint64_t memory_window_end, uint64_t memory_offset, address_space *memory_space,
	uint64_t io_window_start, uint64_t io_window_end, uint64_t io_offset, address_space *io_space)
{
	io_space->install_device(0, 0xffff, *static_cast<pci_host_device *>(this), &pci_host_device::io_configuration_access_map);
	memory_space->install_device(0, 0xffffffff, *this, &crush11_host_device::bios_map);
}

void crush11_host_device::bios_map(address_map &map)
{
	map(0x000c0000, 0x000fffff).rw(biosrom, FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0xfffc0000, 0xffffffff).rw(biosrom, FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
}

uint8_t crush11_host_device::unknown_r()
{
	return 4;
}

void crush11_host_device::unknown_w(uint8_t data)
{
	logerror("test = %02x\n", data);
}

uint32_t crush11_host_device::ram_size_r()
{
	return ram_size * 1024 * 1024 - 1;
}

void crush11_host_device::ram_size_w(uint32_t data)
{
	logerror("trying to set size = %d\n", data);
}

// For ddr ram

DEFINE_DEVICE_TYPE(CRUSH11_MEMORY, crush11_memory_device, "crush11_memory", "nForce memory")

void crush11_memory_device::config_map(address_map &map)
{
	pci_device::config_map(map);
	/*
	bit 31 of a0,a4,a8,ac,b0 and b4 must be 0
	bit 0 of c4 and c8 must be 0
	*/
	map(0xa0, 0xa3).nopr();
	map(0xa4, 0xa7).nopr();
	map(0xa8, 0xab).nopr();
	map(0xac, 0xaf).nopr();
	map(0xb0, 0xb3).nopr();
	map(0xb4, 0xb7).nopr();
	map(0xc4, 0xc7).nopr();
	map(0xc8, 0xcb).nopr();
}

crush11_memory_device::crush11_memory_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, uint32_t subsystem_id, int ram_size)
	: crush11_memory_device(mconfig, tag, owner, clock)
{
	set_ids(0x10de01ac, 0xb2, 0x050000, subsystem_id);
	set_ram_size(ram_size);
}

crush11_memory_device::crush11_memory_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: pci_device(mconfig, CRUSH11_MEMORY, tag, owner, clock)
{
}

void crush11_memory_device::device_start()
{
	device_t *r = owner()->subdevice("00.0");

	pci_device::device_start();
	set_multifunction_device(true);
	ram.resize(ddr_ram_size * 1024 * 1024 / 4);
	host = dynamic_cast<crush11_host_device *>(r);
	ram_space = host->get_cpu_space(AS_PROGRAM);
	status = 0x0020;
	command = 0x0000;
}

void crush11_memory_device::device_reset()
{
	pci_device::device_reset();
	host->set_ram_size(ddr_ram_size);
}

void crush11_memory_device::set_ram_size(int ram_size)
{
	ddr_ram_size = ram_size;
	if (ddr_ram_size < 16)
		ddr_ram_size = 16;
}

void crush11_memory_device::map_extra(uint64_t memory_window_start, uint64_t memory_window_end, uint64_t memory_offset, address_space *memory_space,
	uint64_t io_window_start, uint64_t io_window_end, uint64_t io_offset, address_space *io_space)
{
	ram_space->install_ram(0x00000000, ddr_ram_size * 1024 * 1024 - 1, &ram[0]);
}

/*
  Ddevices connected to SMBus
*/

// access logger

DEFINE_DEVICE_TYPE(SMBUS_LOGGER, smbus_logger_device, "smbus_logger", "SMBUS LOGGER")

smbus_logger_device::smbus_logger_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, SMBUS_LOGGER, tag, owner, clock)
{
}

int smbus_logger_device::execute_command(int command, int rw, int data)
{
	if (rw == 1)
	{
		logerror("smbus read from %02x R %02x\n", command, buffer[command]);
		return buffer[command];
	}
	buffer[command] = (uint8_t)data;
	logerror("smbus write to %02x W %02x\n", command, data);
	return 0;
}

void smbus_logger_device::device_start()
{
	memset(buffer, 0, sizeof(buffer));
}

void smbus_logger_device::device_reset()
{
}

// read-only data

DEFINE_DEVICE_TYPE(SMBUS_ROM, smbus_rom_device, "smbus_rom", "SMBUS ROM")

smbus_rom_device::smbus_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	device_t(mconfig, SMBUS_ROM, tag, owner, clock)
{
}

smbus_rom_device::smbus_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, const uint8_t *data, int size) :
	smbus_rom_device(mconfig, tag, owner, clock)
{
	buffer = data;
	buffer_size = size;
}

int smbus_rom_device::execute_command(int command, int rw, int data)
{
	if ((rw == 1) && (command < buffer_size) && (buffer != nullptr))
	{
		logerror("smbus rom read from %02x %02x\n", command, buffer[command]);
		return buffer[command];
	}
	return 0;
}

void smbus_rom_device::device_start()
{
}

void smbus_rom_device::device_reset()
{
}

// Asus AS99127F chip

DEFINE_DEVICE_TYPE(AS99127F, as99127f_device, "as99127f", "Asus AS99127F")

as99127f_device::as99127f_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, AS99127F, tag, owner, clock)
{
}

int as99127f_device::execute_command(int command, int rw, int data)
{
	if (rw == 1)
	{
		logerror("smbus read from %02x R %02x\n", command, buffer[command]);
		return buffer[command];
	}
	buffer[command] = (uint8_t)data;
	logerror("smbus write to %02x W %02x\n", command, data);
	return 0;
}

void as99127f_device::device_start()
{
	memset(buffer, 0, sizeof(buffer));
	// used to read voltages by bios, measured in mV
	buffer[0x20] = 0x70; // multiplied by 0x10
	buffer[0x2] = 0x7e; // multiplied by 0x10
	buffer[0x23] = 0x96; // multiplied by 0x540 then divided by 0x32
	buffer[0x24] = 0x9e; // multiplied by 0x260 then divided by 0xa
}

DEFINE_DEVICE_TYPE(AS99127F_SENSOR2, as99127f_sensor2_device, "as99127f_sensor2", "Asus AS99127F temperature sensor 2")

as99127f_sensor2_device::as99127f_sensor2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, AS99127F_SENSOR2, tag, owner, clock)
{
}

int as99127f_sensor2_device::execute_command(int command, int rw, int data)
{
	if (rw == 1)
	{
		logerror("smbus read from %02x R %02x\n", command, buffer[command]);
		return buffer[command];
	}
	buffer[command] = (uint8_t)data;
	logerror("smbus write to %02x W %02x\n", command, data);
	return 0;
}

void as99127f_sensor2_device::device_start()
{
	memset(buffer, 0, sizeof(buffer));
}

DEFINE_DEVICE_TYPE(AS99127F_SENSOR3, as99127f_sensor3_device, "as99127f_sensor3", "Asus AS99127F temperature sensor 3")

as99127f_sensor3_device::as99127f_sensor3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, AS99127F_SENSOR3, tag, owner, clock)
{
}

int as99127f_sensor3_device::execute_command(int command, int rw, int data)
{
	if (rw == 1)
	{
		logerror("smbus read from %02x R %02x\n", command, buffer[command]);
		return buffer[command];
	}
	buffer[command] = (uint8_t)data;
	logerror("smbus write to %02x W %02x\n", command, data);
	return 0;
}

void as99127f_sensor3_device::device_start()
{
	memset(buffer, 0, sizeof(buffer));
}

// ITE IT8703F-A SuperIO

DEFINE_DEVICE_TYPE(IT8703F, it8703f_device, "it8703f_device", "ITE IT8703F-A SuperIO")

it8703f_device::it8703f_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, IT8703F, tag, owner, clock)
	, mode(OperatingMode::Run)
	, config_key_step(0)
	, config_index(0)
	, logical_device(0)
	, pin_reset_callback(*this)
	, pin_gatea20_callback(*this)
	, m_txd1_callback(*this)
	, m_ndtr1_callback(*this)
	, m_nrts1_callback(*this)
	, m_txd2_callback(*this)
	, m_ndtr2_callback(*this)
	, m_nrts2_callback(*this)
	, floppy_controller_fdcdev(*this, "fdc")
	, pc_lpt_lptdev(*this, "lpt")
	, pc_serial1_comdev(*this, "uart_0")
	, pc_serial2_comdev(*this, "uart_1")
	, m_kbdc(*this, "pc_kbdc")
	, enabled_game_port(false)
	, enabled_midi_port(false)
{
	memset(global_configuration_registers, 0, sizeof(global_configuration_registers));
	global_configuration_registers[0x20] = 0x87; // identifies it8703f
	global_configuration_registers[0x21] = 1;
	global_configuration_registers[0x24] = 4;
	memset(configuration_registers, 0, sizeof(configuration_registers));
	configuration_registers[LogicalDevice::FDC][0x60] = 3;
	configuration_registers[LogicalDevice::FDC][0x61] = 0xf0;
	configuration_registers[LogicalDevice::FDC][0x70] = 6;
	configuration_registers[LogicalDevice::FDC][0x74] = 2;
	configuration_registers[LogicalDevice::FDC][0xf0] = 0xe;
	configuration_registers[LogicalDevice::FDC][0xf2] = 0xff;
	configuration_registers[LogicalDevice::Parallel][0x74] = 4;
	configuration_registers[LogicalDevice::Parallel][0xf0] = 0x3f;
	for (int n = 0; n < 13; n++)
		enabled_logical[n] = false;
}

void it8703f_device::device_start()
{
}

void it8703f_device::internal_memory_map(address_map &map)
{
}

void it8703f_device::internal_io_map(address_map &map)
{
	map(0x002e, 0x002f).rw(FUNC(it8703f_device::read_it8703f), FUNC(it8703f_device::write_it8703f));
}

uint16_t it8703f_device::get_base_address(int logical, int index)
{
	int position = index * 2 + 0x60;

	return ((uint16_t)configuration_registers[logical][position] << 8) + (uint16_t)configuration_registers[logical][position + 1];
}

void it8703f_device::device_add_mconfig(machine_config &config)
{
	// floppy disc controller
	smc37c78_device& fdcdev(SMC37C78(config, floppy_controller_fdcdev, 24'000'000));
	fdcdev.intrq_wr_callback().set(FUNC(it8703f_device::irq_floppy_w));
	fdcdev.drq_wr_callback().set(FUNC(it8703f_device::drq_floppy_w));

	// parallel port
	PC_LPT(config, pc_lpt_lptdev);
	pc_lpt_lptdev->irq_handler().set(FUNC(it8703f_device::irq_parallel_w));

	// serial ports
	NS16450(config, pc_serial1_comdev, XTAL(1'843'200)); // or NS16550 ?
	pc_serial1_comdev->out_int_callback().set(FUNC(it8703f_device::irq_serial1_w));
	pc_serial1_comdev->out_tx_callback().set(FUNC(it8703f_device::txd_serial1_w));
	pc_serial1_comdev->out_dtr_callback().set(FUNC(it8703f_device::dtr_serial1_w));
	pc_serial1_comdev->out_rts_callback().set(FUNC(it8703f_device::rts_serial1_w));

	NS16450(config, pc_serial2_comdev, XTAL(1'843'200));
	pc_serial2_comdev->out_int_callback().set(FUNC(it8703f_device::irq_serial2_w));
	pc_serial2_comdev->out_tx_callback().set(FUNC(it8703f_device::txd_serial2_w));
	pc_serial2_comdev->out_dtr_callback().set(FUNC(it8703f_device::dtr_serial2_w));
	pc_serial2_comdev->out_rts_callback().set(FUNC(it8703f_device::rts_serial2_w));

	// keyboard
	KBDC8042(config, m_kbdc);
	m_kbdc->set_keyboard_type(kbdc8042_device::KBDC8042_PS2);
	m_kbdc->input_buffer_full_callback().set(FUNC(it8703f_device::irq_keyboard_w));
	m_kbdc->system_reset_callback().set(FUNC(it8703f_device::kbdp20_gp20_reset_w));
	m_kbdc->gate_a20_callback().set(FUNC(it8703f_device::kbdp21_gp25_gatea20_w));
	m_kbdc->set_keyboard_tag("at_keyboard");

	at_keyboard_device &at_keyb(AT_KEYB(config, "at_keyboard", pc_keyboard_device::KEYBOARD_TYPE::AT, 1));
	at_keyb.keypress().set(m_kbdc, FUNC(kbdc8042_device::keyboard_w));
}

uint8_t it8703f_device::read_it8703f(offs_t offset)
{
	if (offset == 0)
	{
		if (mode == OperatingMode::Run)
			return 0;
		return config_index;
	}
	else if (offset == 1)
	{
		if (mode == OperatingMode::Run)
			return 0;
		if (config_index < 0x30)
			return read_global_configuration_register(config_index);
		else
			return read_logical_configuration_register(config_index);
	}
	else
		return 0;
}

void it8703f_device::write_it8703f(offs_t offset, uint8_t data)
{
	uint8_t byt;

	if (offset == 0)
	{
		byt = data & 0xff;
		if (mode == OperatingMode::Run)
		{
			if (byt != 0x87)
				return;
			config_key_step++;
			if (config_key_step > 1)
			{
				config_key_step = 0;
				mode = OperatingMode::Configuration;
			}
		}
		else
		{
			if (byt == 0xaa)
			{
				mode = OperatingMode::Run;
				return;
			}
			config_index = byt;
		}
	}
	else if (offset == 1)
	{
		if (mode == OperatingMode::Run)
			return;
		byt = data & 0xff;
		if (config_index < 0x30)
			write_global_configuration_register(config_index, byt);
		else
			write_logical_configuration_register(config_index, byt);
	}
	else
		return;
}

void it8703f_device::write_global_configuration_register(int index, int data)
{
	global_configuration_registers[index] = data;
	switch (index)
	{
	case 7:
		logical_device = data;
		logerror("Selected logical device %d\n", data);
		break;
	}
	logerror("Write global configuration register %02X = %02X\n", index, data);
}

void it8703f_device::write_logical_configuration_register(int index, int data)
{
	configuration_registers[logical_device][index] = data;
	switch (logical_device)
	{
	case LogicalDevice::FDC:
		write_fdd_configuration_register(index, data);
		break;
	case LogicalDevice::Serial1:
		write_serial1_configuration_register(index, data);
		break;
	case LogicalDevice::Serial2:
		write_serial2_configuration_register(index, data);
		break;
	case LogicalDevice::Parallel:
		write_parallel_configuration_register(index, data);
		break;
	case LogicalDevice::Keyboard:
		write_keyboard_configuration_register(index, data);
		break;
	case LogicalDevice::Gpio1:
		if (index == 0x30)
		{
			if (data & 1)
				enabled_game_port = true;
			if (data & 2)
				enabled_midi_port = true;
		}
		break;
	}
}

void it8703f_device::write_fdd_configuration_register(int index, int data)
{
	if (index == 0x30)
	{
		if (data & 1)
		{
			if (enabled_logical[LogicalDevice::FDC] == false)
			{
				enabled_logical[LogicalDevice::FDC] = true;
				lpchost->remap();
			}
			logerror("Enabled FDD at %04X\n", get_base_address(LogicalDevice::FDC, 0));
		}
		else
		{
			if (enabled_logical[LogicalDevice::FDC] == true)
			{
				enabled_logical[LogicalDevice::FDC] = false;
				lpchost->remap();
			}
			logerror("Disabled FDD at %04X\n", get_base_address(LogicalDevice::FDC, 0));
		}
	}
	if (index == 0x74)
	{
		assign_dma_channels();
		logerror("Set FDD dma channel %d\n", configuration_registers[LogicalDevice::FDC][0x74]);
	}
}

void it8703f_device::write_parallel_configuration_register(int index, int data)
{
	if (index == 0x30)
	{
		if (data & 1)
		{
			if (enabled_logical[LogicalDevice::Parallel] == false)
			{
				enabled_logical[LogicalDevice::Parallel] = true;
				lpchost->remap();
			}
			logerror("Enabled LPT at %04X\n", get_base_address(LogicalDevice::Parallel, 0));
		}
		else
		{
			if (enabled_logical[LogicalDevice::Parallel] == true)
			{
				enabled_logical[LogicalDevice::Parallel] = false;
				lpchost->remap();
			}
		}
	}
	if (index == 0x74)
	{
		assign_dma_channels();
		logerror("Set LPT dma channel %d\n", configuration_registers[LogicalDevice::Parallel][0x74]);
	}
}

void it8703f_device::write_serial1_configuration_register(int index, int data)
{
	if (index == 0x30)
	{
		if (data & 1)
		{
			if (enabled_logical[LogicalDevice::Serial1] == false)
			{
				enabled_logical[LogicalDevice::Serial1] = true;
				lpchost->remap();
			}
			logerror("Enabled Serial1 at %04X\n", get_base_address(LogicalDevice::Serial1, 0));
		}
		else
		{
			if (enabled_logical[LogicalDevice::Serial1] == true)
			{
				enabled_logical[LogicalDevice::Serial1] = false;
				lpchost->remap();
			}
		}
	}
}

void it8703f_device::write_serial2_configuration_register(int index, int data)
{
	if (index == 0x30)
	{
		if (data & 1)
		{
			if (enabled_logical[LogicalDevice::Serial2] == false)
			{
				enabled_logical[LogicalDevice::Serial2] = true;
				lpchost->remap();
			}
			logerror("Enabled Serial2 at %04X\n", get_base_address(LogicalDevice::Serial2, 0));
		}
		else
		{
			if (enabled_logical[LogicalDevice::Serial2] == true)
			{
				enabled_logical[LogicalDevice::Serial2] = false;
				lpchost->remap();
			}
		}
	}
}

void it8703f_device::write_keyboard_configuration_register(int index, int data)
{
	if (index == 0x30)
	{
		if (data & 1)
		{
			if (enabled_logical[LogicalDevice::Keyboard] == false)
			{
				enabled_logical[LogicalDevice::Keyboard] = true;
				lpchost->remap();
			}
			logerror("Enabled Keyboard\n");
		}
		else
		{
			if (enabled_logical[LogicalDevice::Keyboard] == true)
			{
				enabled_logical[LogicalDevice::Keyboard] = false;
				lpchost->remap();
			}
		}
	}
}

uint16_t it8703f_device::read_global_configuration_register(int index)
{
	uint16_t ret = 0;

	ret = global_configuration_registers[index];
	switch (index)
	{
	case 7:
		ret = logical_device;
		break;
	}
	logerror("Read global configuration register %02X = %02X\n", index, ret);
	return ret;
}

uint16_t it8703f_device::read_logical_configuration_register(int index)
{
	uint16_t ret = 0;

	switch (logical_device)
	{
	case LogicalDevice::FDC:
		ret = read_fdd_configuration_register(index);
		break;
	case LogicalDevice::Parallel:
		ret = read_parallel_configuration_register(index);
		break;
	case LogicalDevice::Serial1:
		ret = read_serial1_configuration_register(index);
		break;
	case LogicalDevice::Serial2:
		ret = read_serial2_configuration_register(index);
		break;
	case LogicalDevice::Keyboard:
		ret = read_keyboard_configuration_register(index);
		break;
	default:
		ret = configuration_registers[logical_device][index];
		break;
	}
	return ret;
}

void it8703f_device::irq_floppy_w(int state)
{
	if (enabled_logical[LogicalDevice::FDC] == false)
		return;
	lpchost->set_virtual_line(configuration_registers[LogicalDevice::FDC][0x70], state ? ASSERT_LINE : CLEAR_LINE);
}

void it8703f_device::drq_floppy_w(int state)
{
	if (enabled_logical[LogicalDevice::FDC] == false)
		return;
	lpchost->set_virtual_line(configuration_registers[LogicalDevice::FDC][0x74] + 16, state ? ASSERT_LINE : CLEAR_LINE);
}

void it8703f_device::irq_parallel_w(int state)
{
	if (enabled_logical[LogicalDevice::Parallel] == false)
		return;
	lpchost->set_virtual_line(configuration_registers[LogicalDevice::Parallel][0x70], state ? ASSERT_LINE : CLEAR_LINE);
}

void it8703f_device::drq_parallel_w(int state)
{
	if (enabled_logical[LogicalDevice::Parallel] == false)
		return;
	lpchost->set_virtual_line(configuration_registers[LogicalDevice::Parallel][0x74] + 16, state ? ASSERT_LINE : CLEAR_LINE);
}

void it8703f_device::irq_serial1_w(int state)
{
	if (enabled_logical[LogicalDevice::Serial1] == false)
		return;
	lpchost->set_virtual_line(configuration_registers[LogicalDevice::Serial1][0x70], state ? ASSERT_LINE : CLEAR_LINE);
}

void it8703f_device::txd_serial1_w(int state)
{
	if (enabled_logical[LogicalDevice::Serial1] == false)
		return;
	m_txd1_callback(state);
}

void it8703f_device::dtr_serial1_w(int state)
{
	if (enabled_logical[LogicalDevice::Serial1] == false)
		return;
	m_ndtr1_callback(state);
}

void it8703f_device::rts_serial1_w(int state)
{
	if (enabled_logical[LogicalDevice::Serial1] == false)
		return;
	m_nrts1_callback(state);
}

void it8703f_device::irq_serial2_w(int state)
{
	if (enabled_logical[LogicalDevice::Serial2] == false)
		return;
	lpchost->set_virtual_line(configuration_registers[LogicalDevice::Serial2][0x70], state ? ASSERT_LINE : CLEAR_LINE);
}

void it8703f_device::txd_serial2_w(int state)
{
	if (enabled_logical[LogicalDevice::Serial2] == false)
		return;
	m_txd2_callback(state);
}

void it8703f_device::dtr_serial2_w(int state)
{
	if (enabled_logical[LogicalDevice::Serial2] == false)
		return;
	m_ndtr2_callback(state);
}

void it8703f_device::rts_serial2_w(int state)
{
	if (enabled_logical[LogicalDevice::Serial2] == false)
		return;
	m_nrts2_callback(state);
}

void it8703f_device::rxd1_w(int state)
{
	pc_serial1_comdev->rx_w(state);
}

void it8703f_device::ndcd1_w(int state)
{
	pc_serial1_comdev->dcd_w(state);
}

void it8703f_device::ndsr1_w(int state)
{
	pc_serial1_comdev->dsr_w(state);
}

void it8703f_device::nri1_w(int state)
{
	pc_serial1_comdev->ri_w(state);
}

void it8703f_device::ncts1_w(int state)
{
	pc_serial1_comdev->cts_w(state);
}

void it8703f_device::rxd2_w(int state)
{
	pc_serial2_comdev->rx_w(state);
}

void it8703f_device::ndcd2_w(int state)
{
	pc_serial2_comdev->dcd_w(state);
}

void it8703f_device::ndsr2_w(int state)
{
	pc_serial2_comdev->dsr_w(state);
}

void it8703f_device::nri2_w(int state)
{
	pc_serial2_comdev->ri_w(state);
}

void it8703f_device::ncts2_w(int state)
{
	pc_serial2_comdev->cts_w(state);
}

void it8703f_device::irq_keyboard_w(int state)
{
	if (enabled_logical[LogicalDevice::Keyboard] == false)
		return;
	lpchost->set_virtual_line(configuration_registers[LogicalDevice::Keyboard][0x70], state ? ASSERT_LINE : CLEAR_LINE);
}

void it8703f_device::kbdp21_gp25_gatea20_w(int state)
{
	if (enabled_logical[LogicalDevice::Keyboard] == false)
		return;
	pin_gatea20_callback(state);
}

void it8703f_device::kbdp20_gp20_reset_w(int state)
{
	if (enabled_logical[LogicalDevice::Keyboard] == false)
		return;
	pin_reset_callback(state);
}

void it8703f_device::map_fdc_addresses()
{
	uint16_t base = get_base_address(LogicalDevice::FDC, 0);

	iospace->install_device(base, base + 7, *floppy_controller_fdcdev, &smc37c78_device::map);
}

void it8703f_device::map_lpt(address_map& map)
{
	map(0x0, 0x3).rw(FUNC(it8703f_device::lpt_read), FUNC(it8703f_device::lpt_write));
}

uint8_t it8703f_device::lpt_read(offs_t offset)
{
	return pc_lpt_lptdev->read(offset);
}

void it8703f_device::lpt_write(offs_t offset, uint8_t data)
{
	pc_lpt_lptdev->write(offset, data);
}

void it8703f_device::map_lpt_addresses()
{
	uint16_t base = get_base_address(LogicalDevice::Parallel, 0);

	iospace->install_device(base, base + 3, *this, &it8703f_device::map_lpt);
}

void it8703f_device::map_serial1(address_map& map)
{
	map(0x0, 0x7).rw(FUNC(it8703f_device::serial1_read), FUNC(it8703f_device::serial1_write));
}

uint8_t it8703f_device::serial1_read(offs_t offset)
{
	return pc_serial1_comdev->ins8250_r(offset);
}

void it8703f_device::serial1_write(offs_t offset, uint8_t data)
{
	pc_serial1_comdev->ins8250_w(offset, data);
}

void it8703f_device::map_serial1_addresses()
{
	uint16_t base = get_base_address(LogicalDevice::Serial1, 0);

	iospace->install_device(base, base + 7, *this, &it8703f_device::map_serial1);
}

void it8703f_device::map_serial2(address_map& map)
{
	map(0x0, 0x7).rw(FUNC(it8703f_device::serial2_read), FUNC(it8703f_device::serial2_write));
}

uint8_t it8703f_device::serial2_read(offs_t offset)
{
	return pc_serial2_comdev->ins8250_r(offset);
}

void it8703f_device::serial2_write(offs_t offset, uint8_t data)
{
	pc_serial2_comdev->ins8250_w(offset, data);
}

void it8703f_device::map_serial2_addresses()
{
	uint16_t base = get_base_address(LogicalDevice::Serial2, 0);

	iospace->install_device(base, base + 7, *this, &it8703f_device::map_serial2);
}

void it8703f_device::map_keyboard(address_map &map)
{
	map(0x0, 0x0).rw(FUNC(it8703f_device::at_keybc_r), FUNC(it8703f_device::at_keybc_w));
	map(0x4, 0x4).rw(FUNC(it8703f_device::keybc_status_r), FUNC(it8703f_device::keybc_command_w));
}

uint8_t it8703f_device::at_keybc_r(offs_t offset)
{
	switch (offset) //m_kbdc
	{
	case 0:
		return m_kbdc->data_r(0);
	}

	return 0xff;
}

void it8703f_device::at_keybc_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 0:
		m_kbdc->data_w(0, data);
	}
}

uint8_t it8703f_device::keybc_status_r()
{
	return m_kbdc->data_r(4);
}

void it8703f_device::keybc_command_w(uint8_t data)
{
	m_kbdc->data_w(4, data);
}

void it8703f_device::map_keyboard_addresses()
{
	uint16_t base = get_base_address(LogicalDevice::Keyboard, 0);

	iospace->install_device(base, base + 7, *this, &it8703f_device::map_keyboard);
}

void it8703f_device::map_extra(address_space *memory_space, address_space *io_space)
{
	memspace = memory_space;
	iospace = io_space;
	io_space->install_device(0, 0xffff, *this, &it8703f_device::internal_io_map);
	if (enabled_logical[LogicalDevice::FDC] == true)
		map_fdc_addresses();
	if (enabled_logical[LogicalDevice::Parallel] == true)
		map_lpt_addresses();
	if (enabled_logical[LogicalDevice::Serial1] == true)
		map_serial1_addresses();
	if (enabled_logical[LogicalDevice::Serial2] == true)
		map_serial2_addresses();
	if (enabled_logical[LogicalDevice::Keyboard] == true)
		map_keyboard_addresses();
}

void it8703f_device::set_host(int device_index, lpcbus_host_interface *host)
{
	lpchost = host;
	lpcindex = device_index;
	lpchost->assign_virtual_line(24 + configuration_registers[LogicalDevice::FDC][0x74], lpcindex);
	lpchost->assign_virtual_line(24 + configuration_registers[LogicalDevice::Parallel][0x74], lpcindex);
}

uint32_t it8703f_device::dma_transfer(int channel, dma_operation operation, dma_size size, uint32_t data)
{
	uint32_t ret = 0;

	if (enabled_logical[LogicalDevice::FDC] == true)
		if (channel == configuration_registers[LogicalDevice::FDC][0x74])
		{
			if (operation == dma_operation::WRITE)
				ret = (uint32_t)floppy_controller_fdcdev->dma_r();
			else if (operation == dma_operation::READ)
				floppy_controller_fdcdev->dma_w((uint8_t)data);
			else if (operation == dma_operation::END)
				floppy_controller_fdcdev->tc_w(data == ASSERT_LINE);
		}
	if (enabled_logical[LogicalDevice::Parallel] == true)
		if (channel == configuration_registers[LogicalDevice::Parallel][0x74])
			printf("it8703f_device::dma_transfer LPT channel %d op %d size %d\n", channel, (int)operation, (int)size);
	return ret;
}

void it8703f_device::assign_dma_channels()
{
	lpchost->assign_virtual_line(-1, lpcindex); // remove assigments
	lpchost->assign_virtual_line(24 + configuration_registers[LogicalDevice::FDC][0x74], lpcindex);
	lpchost->assign_virtual_line(24 + configuration_registers[LogicalDevice::Parallel][0x74], lpcindex);
}

/*
  Machine state
*/

class nforcepc_state : public driver_device
{
public:
	struct boot_state_info
	{
		uint8_t val;
		const char *const message;
	};

	static const boot_state_info boot_state_infos_award[];

	void nforcepc(machine_config &config);

	nforcepc_state(const machine_config &mconfig, device_type type, const char *tag);

private:
	void nforce_map(address_map &map) ATTR_COLD;
	void nforce_map_io(address_map &map) ATTR_COLD;
	void boot_state_award_w(uint8_t data);
	IRQ_CALLBACK_MEMBER(irq_callback);
	void maincpu_interrupt(int state);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<mcpx_isalpc_device> isalpc;
	required_device<as99127f_device> m_as99127f;
};

nforcepc_state::nforcepc_state(const machine_config &mconfig, device_type type, const char *tag) :
	driver_device(mconfig, type, tag),
	m_maincpu(*this, "maincpu"),
	isalpc(*this, "pci:01.0"),
	m_as99127f(*this, "pci:01.1:12d")
{
}

void nforcepc_state::machine_start()
{
	m_as99127f->get_buffer()[0x4f] = 0x12;
}

void nforcepc_state::machine_reset()
{
}

const nforcepc_state::boot_state_info nforcepc_state::boot_state_infos_award[] = {
	{ 0xC0, "First basic initialization" },
	{ 0, nullptr }
};

void nforcepc_state::boot_state_award_w(uint8_t data)
{
	const char *desc = "";
	for (int i = 0; boot_state_infos_award[i].message; i++)
		if (boot_state_infos_award[i].val == data)
		{
			desc = boot_state_infos_award[i].message;
			break;
		}
	logerror("Boot state %02x - %s\n", data, desc);
}

IRQ_CALLBACK_MEMBER(nforcepc_state::irq_callback)
{
	return isalpc->acknowledge();
}

void nforcepc_state::maincpu_interrupt(int state)
{
	m_maincpu->set_input_line(0, state ? HOLD_LINE : CLEAR_LINE);
}

void nforcepc_state::nforce_map(address_map &map)
{
	map.unmap_value_high();
}

void nforcepc_state::nforce_map_io(address_map &map)
{
	map.unmap_value_high();
}

static void isa_com(device_slot_interface& device)
{
	device.option_add("microsoft_mouse", MSFT_HLE_SERIAL_MOUSE);
	device.option_add("logitech_mouse", LOGITECH_HLE_SERIAL_MOUSE);
	device.option_add("wheel_mouse", WHEEL_HLE_SERIAL_MOUSE);
	device.option_add("msystems_mouse", MSYSTEMS_HLE_SERIAL_MOUSE);
	device.option_add("rotatable_mouse", ROTATABLE_HLE_SERIAL_MOUSE);
	device.option_add("terminal", SERIAL_TERMINAL);
	device.option_add("null_modem", NULL_MODEM);
	device.option_add("sun_kbd", SUN_KBD_ADAPTOR);
}

static void pc_hd_floppies(device_slot_interface& device)
{
	device.option_add("525hd", FLOPPY_525_HD);
	device.option_add("35hd", FLOPPY_35_HD);
	device.option_add("525dd", FLOPPY_525_DD);
	device.option_add("35dd", FLOPPY_35_DD);
}

static void floppy_formats(format_registration &fr)
{
	fr.add_pc_formats();
	fr.add(FLOPPY_NASLITE_FORMAT);
}

/*
  Machine configuration
*/

void nforcepc_state::nforcepc(machine_config &config)
{
	athlonxp_device &maincpu(ATHLONXP(config, "maincpu", 90000000));
	maincpu.set_addrmap(AS_PROGRAM, &nforcepc_state::nforce_map);
	maincpu.set_addrmap(AS_IO, &nforcepc_state::nforce_map_io);
	maincpu.set_irq_acknowledge_callback(FUNC(nforcepc_state::irq_callback));
	//maincpu.smiact().set("pci:01.0", FUNC(???_host_device::smi_act_w));

	PCI_ROOT(config, "pci", 0);
	CRUSH11(config, "pci:00.0", 0, "maincpu", "bios"); // 10de:01a4 NVIDIA Corporation nForce CPU bridge
	CRUSH11_MEMORY(config, "pci:00.1", 0, 0x10430c11, 2); // 10de:01ac NVIDIA Corporation nForce 220/420 Memory Controller
	// 10de:01ad NVIDIA Corporation nForce 220/420 Memory Controller
	// 10de:01ab NVIDIA Corporation nForce 420 Memory Controller (DDR)
	mcpx_isalpc_device &isa(MCPX_ISALPC(config, "pci:01.0", 0, 0x10430c11)); // 10de:01b2 NVIDIA Corporation nForce ISA Bridge (LPC bus)
	isa.smi().set_inputline(":maincpu", INPUT_LINE_SMI);
	isa.boot_state_hook().set(FUNC(nforcepc_state::boot_state_award_w));
	isa.interrupt_output().set(FUNC(nforcepc_state::maincpu_interrupt));
	isa.set_dma_space("maincpu", AS_OPCODES);
	it8703f_device &ite(IT8703F(config, "pci:01.0:0", 0));
	ite.pin_reset().set_inputline("maincpu", INPUT_LINE_RESET);
	ite.pin_gatea20().set_inputline("maincpu", INPUT_LINE_A20);
	ite.txd1().set("serport0", FUNC(rs232_port_device::write_txd));
	ite.ndtr1().set("serport0", FUNC(rs232_port_device::write_dtr));
	ite.nrts1().set("serport0", FUNC(rs232_port_device::write_rts));
	ite.txd2().set("serport1", FUNC(rs232_port_device::write_txd));
	ite.ndtr2().set("serport1", FUNC(rs232_port_device::write_dtr));
	ite.nrts2().set("serport1", FUNC(rs232_port_device::write_rts));
	MCPX_SMBUS(config, "pci:01.1", 0, 0x10430c11); // 10de:01b4 NVIDIA Corporation nForce PCI System Management (SMBus)
	SMBUS_ROM(config, "pci:01.1:050", 0, test_spd_data, sizeof(test_spd_data)); // these 3 are on smbus number 0
	SMBUS_LOGGER(config, "pci:01.1:051", 0);
	SMBUS_LOGGER(config, "pci:01.1:052", 0);
	SMBUS_LOGGER(config, "pci:01.1:108", 0); // these 4 are on smbus number 1
	AS99127F(config, "pci:01.1:12d", 0);
	AS99127F_SENSOR2(config, "pci:01.1:148", 0);
	AS99127F_SENSOR3(config, "pci:01.1:149", 0);
	mcpx_ohci_device &ohci(MCPX_OHCI(config, "pci:02.0", 0, 0x10430c11)); // 10de:01c2 NVIDIA Corporation nForce USB Controller
	ohci.interrupt_handler().set("pci:01.0", FUNC(mcpx_isalpc_device::irq1));
	MCPX_OHCI(config, "pci:03.0", 0, 0x10430c11); // 10de:01c2 NVIDIA Corporation nForce USB Controller
	MCPX_ETH(config, "pci:04.0", 0); // 10de:01c3 NVIDIA Corporation nForce Ethernet Controller
	MCPX_APU(config, "pci:05.0", 0, 0x10430c11, m_maincpu); // 10de:01b0 NVIDIA Corporation nForce Audio Processing Unit
	MCPX_AC97_AUDIO(config, "pci:06.0", 0, 0x10438384); // 10de:01b1 NVIDIA Corporation nForce AC'97 Audio Controller
	PCI_BRIDGE(config, "pci:08.0", 0, 0x10de01b8, 0xc2); // 10de:01b8 NVIDIA Corporation nForce PCI-to-PCI bridge
	// 10ec:8139 Realtek Semiconductor Co., Ltd. RTL-8139/8139C/8139C+ (behind bridge)
	mcpx_ide_device &ide(MCPX_IDE(config, "pci:09.0", 0, 0x10430c11)); // 10de:01bc NVIDIA Corporation nForce IDE
	ide.pri_interrupt_handler().set("pci:01.0", FUNC(mcpx_isalpc_device::irq14));
	ide.sec_interrupt_handler().set("pci:01.0", FUNC(mcpx_isalpc_device::irq15));
	ide.set_bus_master_space("maincpu", AS_OPCODES);
	ide.subdevice<ide_controller_32_device>("ide1")->options(ata_devices, "hdd", nullptr, true);
	ide.subdevice<ide_controller_32_device>("ide2")->options(ata_devices, "cdrom", nullptr, true);
	NV2A_AGP(config, "pci:1e.0", 0, 0x10de01b7, 0xb2); // 10de:01b7 NVIDIA Corporation nForce AGP to PCI Bridge
	PCI_SLOT(config, "pci:1", pci_cards, 10, 0, 1, 2, 3, "virgedx");
	SST_49LF020(config, "bios", 0);

	FLOPPY_CONNECTOR(config, "pci:01.0:0:fdc:0", pc_hd_floppies, "35hd", floppy_formats);
	FLOPPY_CONNECTOR(config, "pci:01.0:0:fdc:1", pc_hd_floppies, "35hd", floppy_formats);

	rs232_port_device& serport0(RS232_PORT(config, "serport0", isa_com, nullptr));
	serport0.rxd_handler().set("pci:01.0:0", FUNC(it8703f_device::rxd1_w));
	serport0.dcd_handler().set("pci:01.0:0", FUNC(it8703f_device::ndcd1_w));
	serport0.dsr_handler().set("pci:01.0:0", FUNC(it8703f_device::ndsr1_w));
	serport0.ri_handler().set("pci:01.0:0", FUNC(it8703f_device::nri1_w));
	serport0.cts_handler().set("pci:01.0:0", FUNC(it8703f_device::ncts1_w));

	rs232_port_device& serport1(RS232_PORT(config, "serport1", isa_com, nullptr));
	serport1.rxd_handler().set("pci:01.0:0", FUNC(it8703f_device::rxd2_w));
	serport1.dcd_handler().set("pci:01.0:0", FUNC(it8703f_device::ndcd2_w));
	serport1.dsr_handler().set("pci:01.0:0", FUNC(it8703f_device::ndsr2_w));
	serport1.ri_handler().set("pci:01.0:0", FUNC(it8703f_device::nri2_w));
	serport1.cts_handler().set("pci:01.0:0", FUNC(it8703f_device::ncts2_w));
}

ROM_START(nforcepc)
	ROM_REGION32_LE(0x40000, "bios", 0) /* PC bios */
	ROM_SYSTEM_BIOS(0, "a7n266c", "a7n266c") // Motherboard dump. Chip: SST49LF020 Package: PLCC32 Label had 3 lines of text: "A7NC3" "1001.D" "GSQ98"
	ROMX_LOAD("a7n266c.bin", 0, 0x40000, CRC(f4f0e4fc) SHA1(87f11545db178914623e41fb51e328da479a2efc), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "a7n266c1001d", "a7n266c1001d") // bios version 1001.D downloaded from Asus website
	ROMX_LOAD("a7nc101d.awd", 0, 0x40000, CRC(ead1147c) SHA1(27227df98e0c5fb9fecdb4bb6ef72df19766c330), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "a7n266c1001e", "a7n266c1001e") // bios version 1001.E
	ROMX_LOAD("a7nc101e.awd", 0, 0x40000, CRC(a029cd42) SHA1(e257915534bc725f389e57945b45c81e7ef40dcc), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "a7n266c1002c", "a7n266c1002c") // beta bios version 1002C.003 ?
	ROMX_LOAD("1002c.003", 0, 0x40000, CRC(57ced539) SHA1(525d15523be3b373a10c1f6ae355803613506ce2), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "a7n266c1003", "a7n266c1003") // bios version 1003
	ROMX_LOAD("a7nc1003.awd", 0, 0x40000, CRC(ac27f751) SHA1(96d5539ee2a40ea58a32013c16109159a3c41bb8), ROM_BIOS(4))
	ROM_SYSTEM_BIOS(5, "a7n266c1004", "a7n266c1004") // bios version 1004
	ROMX_LOAD("a7nc1004.awd", 0, 0x40000, CRC(04124e4f) SHA1(8778a6722eddaf83101f89834969f0037af65cb9), ROM_BIOS(5))
	ROM_SYSTEM_BIOS(6, "a7n266c1005", "a7n266c1005") // beta bios version 1005.005 ?
	ROMX_LOAD("1005nc.005", 0, 0x40000, CRC(9ca5a9c9) SHA1(5ffb57b9f1e0e163b33c093a3e017020b247b3d3), ROM_BIOS(6))
ROM_END

static INPUT_PORTS_START(nforcepc)
INPUT_PORTS_END

COMP(2002, nforcepc, 0, 0, nforcepc, nforcepc, nforcepc_state, empty_init, "Nvidia", "Nvidia nForce PC (CRUSH11/12)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



ngen.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Barry Rodewald
/*

    Convergent NGen series

    10-11-14 - Skeleton driver

    Interrupts based on patents:
    level 1 - SIO
    level 3 - timer (from PIT, presumably channel 0? Patent says "channel 3")
    level 4 - "interrupt detector" - keyboard, printer, RTC
    level 7 - floppy/hard disk

    DMA channels:
    channel 0 - communications (RS-232)
    channel 1 - X-Bus expansion modules (except disk and graphics)
    channel 2 - graphics?
    channel 3 - hard disk
    On the CP-001/B26 channels 4 on are handled by the 80186.
    channel 4 - floppy disk

    To get to "menu mode", press Space quickly after reset (might need good timing)
    The bootstrap ROM version number is displayed, along with "B,D,L,M,P,T:"
    You can press one of these keys for the following tests:
    B: Bootstrap
       Loads the system image file (from disk or master workstation)
    D: Dump
       RAM contents are dumped to a local disk drive or master workstation
    L: Load
       Loads the system image file, then enters the Panel Debugger.  Exiting the Panel
       Debugger will continue execution of the system image
    M: Memory Test
       Continuously performs the Memory Test until the system is reset.
    P: Panel Debugger
       Enters the Panel Debugger
    T: Type of Operating System
       Gives an "OS:" prompt, at which you can enter the number of the system image to
       load at the master workstation.

    Panel Debugger:
    - Open/Modify RAM
    Enter an address (seg:off) followed by a forward-slash, the contents of this word will
    appear, you can enter a value to set it to, or just press Next (default: Enter) to leave
    it as is.  It will then go on to the next word.  Pressing Return (scan code unknown
    currently) will return to the debugger prompt.
    - Open/Modify Register
    Enter the register only, and the contents will appear, you can leave it or alter it (you
    must enter all digits (eg: 0A03 if you're modifying DX) then press Return.
    - I/O to or from a port
    Input: Address (segment is ignored, and not required) followed by I, a byte is read from
    the port defined by the offset, and the byte is displayed.
    Output: Address followed by O, you are now prompted with an '='.  Enter the byte to send
    to the port, and press Return.
    - Set Haltpoint:
    Enter an address (seg:off) followed by H.  Sets a haltpoint at the specified address.  Does
    not work for ROM addresses.  Only one allowed at a time.  Haltpoint info is stored at
    0000:01F0.  Uses a software interrupt (INT 7C), rather than INT 3.

    To start or continue from the current address, enter P.
    To start from a specific address, enter the address (seg:off) followed by a G.
*/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/i386/i386.h"
#include "cpu/i86/i186.h"
#include "imagedev/floppy.h"
#include "imagedev/harddriv.h"
#include "machine/am9517a.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "ngen_kb.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/wd2010.h"
#include "machine/wd_fdc.h"
#include "machine/z80sio.h"
#include "video/mc6845.h"
#include "memarray.h"
#include "screen.h"


namespace {

class ngen_state : public driver_device
{
public:
	ngen_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this,"maincpu"),
		m_i386cpu(*this,"i386cpu"),
		m_crtc(*this,"crtc"),
		m_viduart(*this,"videouart"),
		m_iouart(*this,"iouart"),
		m_dmac(*this,"dmac"),
		m_pic(*this,"pic"),
		m_pit(*this,"pit"),
		m_hdc(*this,"hdc"),
		m_fdc(*this,"fdc"),
		m_fdc_timer(*this,"fdc_timer"),
		m_hdc_timer(*this,"hdc_timer"),
		m_disk_rom(*this,"disk"),
		m_fd0(*this,"fdc:0"),
		m_hd_buffer(*this,"hd_buffer_ram", 1024*8, ENDIANNESS_LITTLE)
	{
	}

	void ngen(machine_config &config);

protected:
	uint8_t hd_buffer_r(offs_t offset);
	void hd_buffer_w(offs_t offset, uint8_t data);

	void pit_out0_w(int state);
	void pit_out1_w(int state);
	void pit_out2_w(int state);

	void dma_hrq_changed(int state);
	void dma_eop_changed(int state);
	void dack0_w(int state);
	void dack1_w(int state);
	void dack2_w(int state);
	void dack3_w(int state);
	uint8_t dma_read_word(offs_t offset);
	void dma_write_word(offs_t offset, uint8_t data);
	// TODO: sort out what devices use which channels
	uint8_t dma_0_dack_r() { uint16_t ret = 0xffff; m_dma_high_byte = ret & 0xff00; return ret; }
	uint8_t dma_1_dack_r() { uint16_t ret = 0xffff; m_dma_high_byte = ret & 0xff00; return ret; }
	uint8_t dma_2_dack_r() { uint16_t ret = 0xffff; m_dma_high_byte = ret & 0xff00; return ret; }
	uint8_t dma_3_dack_r();
	void dma_0_dack_w(uint8_t data) { popmessage("IOW0: data %02x",data); }
	void dma_1_dack_w(uint8_t data) { }
	void dma_2_dack_w(uint8_t data) { }
	void dma_3_dack_w(uint8_t data) { popmessage("IOW3: data %02x",data); }

	MC6845_UPDATE_ROW(crtc_update_row);

	void timer_clk_out(int state);

	void fdc_irq_w(int state);

	void ngen386_io(address_map &map) ATTR_COLD;
	void ngen386_mem(address_map &map) ATTR_COLD;
	void ngen386i_mem(address_map &map) ATTR_COLD;

	optional_device<i80186_cpu_device> m_maincpu;
	optional_device<i386_device> m_i386cpu;
	required_device<mc6845_device> m_crtc;
	required_device<i8251_device> m_viduart;
	required_device<upd7201_device> m_iouart;
	required_device<am9517a_device> m_dmac;
	required_device<pic8259_device> m_pic;
	required_device<pit8254_device> m_pit;
	optional_device<wd2010_device> m_hdc;
	optional_device<wd2797_device> m_fdc;
	optional_device<pit8253_device> m_fdc_timer;
	optional_device<pit8253_device> m_hdc_timer;

private:
	void cpu_peripheral_cb(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	void peripheral_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t peripheral_r(offs_t offset, uint16_t mem_mask = ~0);
	void xbus_w(uint16_t data);
	uint16_t xbus_r();

	void cpu_timer_w(int state);

	void hfd_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t hfd_r(offs_t offset, uint16_t mem_mask = ~0);
	[[maybe_unused]] void fdc_drq_w(int state);
	void fdc_control_w(uint8_t data);
	uint8_t irq_cb();
	void hdc_control_w(uint8_t data);
	void disk_addr_ext(uint8_t data);

	uint16_t b38_keyboard_r(offs_t offset, uint16_t mem_mask = ~0);
	void b38_keyboard_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t b38_crtc_r(offs_t offset, uint16_t mem_mask = ~0);
	void b38_crtc_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	void ngen_io(address_map &map) ATTR_COLD;
	void ngen_mem(address_map &map) ATTR_COLD;

	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

	optional_memory_region m_disk_rom;
	memory_array m_vram;
	memory_array m_fontram;
	optional_device<floppy_connector> m_fd0;
	memory_share_creator<uint8_t> m_hd_buffer;

	void set_dma_channel(int channel, int state);

	uint8_t m_xbus_current;  // currently selected X-Bus module
	uint16_t m_peripheral;
	uint16_t m_upper;
	uint16_t m_middle;
	uint16_t m_port00;
	uint16_t m_periph141;
	uint8_t m_dma_offset[4];
	int8_t m_dma_channel;
	uint16_t m_dma_high_byte;
	uint16_t m_control;
	uint16_t m_disk_rom_ptr;
	uint8_t m_hdc_control;
	uint8_t m_disk_page;
};

class ngen386_state : public ngen_state
{
public:
	ngen386_state(const machine_config &mconfig, device_type type, const char *tag)
		: ngen_state(mconfig, type, tag)
		{}
		void ngen386(machine_config &config);
		void _386i(machine_config &config);
private:
};

void ngen_state::pit_out0_w(int state)
{
	m_pic->ir3_w(state);  // Timer interrupt
	popmessage("PIT Timer 0 state %i\n",state);
}

void ngen_state::pit_out1_w(int state)
{
	popmessage("PIT Timer 1 state %i\n",state);
	m_iouart->rxcb_w(state);
	m_iouart->txcb_w(state);  // channels in the correct order?
}

void ngen_state::pit_out2_w(int state)
{
	m_iouart->rxca_w(state);
	m_iouart->txca_w(state);
	popmessage("PIT Timer 2 state %i\n",state);
}

void ngen_state::cpu_timer_w(int state)
{
	if(state != 0)
		popmessage("80186 Timer 0 state %i\n",state);
	m_pic->ir5_w(state);
}

void ngen_state::timer_clk_out(int state)
{
	m_viduart->write_rxc(state);  // Keyboard UART Rx/Tx clocks
	m_viduart->write_txc(state);
	// 80186 timer pins also?  EXT bit is enabled for BTOS PIT test.
	if(m_maincpu)
	{
		m_maincpu->tmrin0_w(state);
		//m_maincpu->tmrin1_w(state);
	}
}

void ngen_state::cpu_peripheral_cb(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	uint32_t addr;

	switch(offset)
	{
	case 0:  // upper memory
		m_upper = data;
		break;
	case 2:  // peripheral
		m_peripheral = data;
		addr = (m_peripheral & 0xffc0) << 4;
		if(m_middle & 0x0040)
		{
			m_maincpu->space(AS_PROGRAM).install_readwrite_handler(addr, addr + 0x3ff, read16s_delegate(*this, FUNC(ngen_state::peripheral_r)), write16s_delegate(*this, FUNC(ngen_state::peripheral_w)));
			logerror("Mapped peripherals to memory 0x%08x\n",addr);
		}
		else
		{
			addr &= 0xffff;
			m_maincpu->space(AS_IO).install_readwrite_handler(addr, addr + 0x3ff, read16s_delegate(*this, FUNC(ngen_state::peripheral_r)), write16s_delegate(*this, FUNC(ngen_state::peripheral_w)));
			logerror("Mapped peripherals to I/O 0x%04x\n",addr);
		}
		break;
	case 4:
		m_middle = data;
		break;
	}
}

// 80186 peripheral space
// Largely guesswork at this stage
void ngen_state::peripheral_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	switch(offset)
	{
	case 0x00:
	case 0x01:
	case 0x02:
	case 0x03:
	case 0x04:
	case 0x05:
	case 0x06:
	case 0x07:
	case 0x08:
	case 0x09:
	case 0x0a:
	case 0x0b:
	case 0x0c:
	case 0x0d:
	case 0x0e:
	case 0x0f:
		if(ACCESSING_BITS_0_7)
			m_dmac->write(offset,data & 0xff);
		break;
	case 0x80: // DMA page offset?
	case 0x81:
	case 0x82:
	case 0x83:
		if(ACCESSING_BITS_0_7)
			m_dma_offset[offset-0x80] = data & 0xff;
		break;
	case 0xc0:  // X-Bus modules reset
		m_xbus_current = 0;
		break;
	case 0x10c:
		if(ACCESSING_BITS_0_7)
			m_pic->write(0,data & 0xff);
		break;
	case 0x10d:
		if(ACCESSING_BITS_0_7)
			m_pic->write(1,data & 0xff);
		break;
	case 0x110:
	case 0x111:
	case 0x112:
	case 0x113:
		if(ACCESSING_BITS_0_7)
			m_pit->write(offset-0x110,data & 0xff);
		break;
	case 0x141:
		// bit 1 enables speaker?
		COMBINE_DATA(&m_periph141);
		break;
	case 0x144:
		if(ACCESSING_BITS_0_7)
			m_crtc->address_w(data & 0xff);
		break;
	case 0x145:
		if(ACCESSING_BITS_0_7)
			m_crtc->register_w(data & 0xff);
		break;
	case 0x146:
	case 0x147:
		if(ACCESSING_BITS_0_7)
			m_viduart->write(offset & 1, data & 0xff);
		break;
	case 0x1a0:  // serial?
		logerror("Serial(?) 0x1a0 write offset %04x data %04x mask %04x\n",offset,data,mem_mask);
		break;
	default:
		logerror("Unknown 80186 peripheral write offset %04x data %04x mask %04x\n",offset,data,mem_mask);
	}
}

uint16_t ngen_state::peripheral_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t ret = 0xffff;
	switch(offset)
	{
	case 0x00:
	case 0x01:
	case 0x02:
	case 0x03:
	case 0x04:
	case 0x05:
	case 0x06:
	case 0x07:
	case 0x08:
	case 0x09:
	case 0x0a:
	case 0x0b:
	case 0x0c:
	case 0x0d:
	case 0x0e:
	case 0x0f:
		if(ACCESSING_BITS_0_7)
			ret = m_dmac->read(offset);
		logerror("DMA read offset %04x mask %04x returning %04x\n",offset,mem_mask,ret);
		break;
	case 0x80: // DMA page offset?
	case 0x81:
	case 0x82:
	case 0x83:
		if(ACCESSING_BITS_0_7)
			ret = m_dma_offset[offset-0x80] & 0xff;
		break;
	case 0x10c:
		if(ACCESSING_BITS_0_7)
			ret = m_pic->read(0);
		break;
	case 0x10d:
		if(ACCESSING_BITS_0_7)
			ret = m_pic->read(1);
		break;
	case 0x110:
	case 0x111:
	case 0x112:
	case 0x113:
		if(ACCESSING_BITS_0_7)
			ret = m_pit->read(offset-0x110);
		break;
	case 0x141:
		ret = m_periph141;
		break;
	case 0x144:
		if(ACCESSING_BITS_0_7)
			ret = m_crtc->status_r();
		break;
	case 0x145:
		if(ACCESSING_BITS_0_7)
			ret = m_crtc->register_r();
		break;
	case 0x146:
	case 0x147:  // keyboard UART
		// status expects bit 0 to be set (UART transmit ready)
		if(ACCESSING_BITS_0_7)
			ret = m_viduart->read(offset & 1);
		break;
	case 0x1a0:  // I/O control register?
		ret = m_control;  // end of DMA transfer? (maybe a per-channel EOP?) Bit 6 is set during a transfer?
		break;
//  default:
//      logerror("Unknown 80186 peripheral read offset %04x mask %04x returning %04x\n",offset,mem_mask,ret);
	}
	return ret;
}

// X-bus module select
// The bootstrap ROM creates a table at 0:FC9h, with a count, followed by the module IDs of each
// expansion module.  The base I/O address for the currently selected module is set by writing to
// this register (bits 0-7 are ignored)
// TODO: make expansion modules slot devices
void ngen_state::xbus_w(uint16_t data)
{
	uint16_t addr = (data & 0x00ff) << 8;
	cpu_device* cpu;

	if(m_maincpu)
		cpu = m_maincpu;
	else
		cpu = m_i386cpu;
	address_space& io = cpu->space(AS_IO);
	switch(m_xbus_current)
	{
		case 0x00:  // Floppy/Hard disk module
			io.install_readwrite_handler(addr,addr+0xff, read16s_delegate(*this, FUNC(ngen_state::hfd_r)), write16s_delegate(*this, FUNC(ngen_state::hfd_w)), 0xffffffff);
			break;
		default:
			cpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);  // reached end of the modules
			break;
	}
	if(addr != 0)
		logerror("SYS: X-Bus module %i address set %04x\n",m_xbus_current+1,addr);
	m_xbus_current++;
}

// returns X-bus module ID and info in the low byte (can indicate if the device is bootable, has a boot ROM (needs to be written to RAM via DMA), or if it supports a non-80186 CPU)
// bit 6, I think, indicates a bootable device
// Known module IDs:
//  0x1070 - Floppy/Hard disk module
//  0x3141 - QIC Tape module
uint16_t ngen_state::xbus_r()
{
	uint16_t ret = 0xffff;

	switch(m_xbus_current)
	{
		case 0x00:
			ret = 0x1070;  // Floppy/Hard disk module
			break;
		default:
			if(m_maincpu)
				m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);  // reached the end of the modules
			else
				m_i386cpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
			ret = 0x0080;
			break;
	}
	return ret;
}


// Floppy/Hard disk module
void ngen_state::hfd_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	switch(offset)
	{
		case 0x00:
		case 0x01:
		case 0x02:
			if(ACCESSING_BITS_0_7)
				m_fdc->write(offset,data & 0xff);
			break;
		case 0x03:
			if(ACCESSING_BITS_0_7)
			{
				m_fdc->write(offset,data & 0xff);
				m_fdc_timer->write_clk0(1);
				m_fdc_timer->write_clk0(0);  // Data register access clocks the FDC's PIT channel 0
			}
			break;
		case 0x04:
			if(ACCESSING_BITS_0_7)
				fdc_control_w(data & 0xff);
			break;
		case 0x05:
			if(ACCESSING_BITS_0_7)
				hdc_control_w(data & 0xff);
			break;
		case 0x07:
			if(ACCESSING_BITS_0_7)
				disk_addr_ext(data & 0xff);
			break;
		case 0x08:
		case 0x09:
		case 0x0a:
		case 0x0b:
			if(ACCESSING_BITS_0_7)
				m_fdc_timer->write(offset-0x08,data & 0xff);
			break;
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
			if(ACCESSING_BITS_0_7)
				m_hdc->write(offset-0x10,data & 0xff);
			logerror("WD1010 register %i write %02x mask %04x\n",offset-0x10,data & 0xff,mem_mask);
			break;
		case 0x18:
		case 0x19:
		case 0x1a:
		case 0x1b:
			if(ACCESSING_BITS_0_7)
				m_hdc_timer->write(offset-0x18,data & 0xff);
			break;
	}
}

uint16_t ngen_state::hfd_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t ret = 0xffff;

	switch(offset)
	{
		case 0x00:
		case 0x01:
		case 0x02:
			if(ACCESSING_BITS_0_7)
				ret = m_fdc->read(offset);
			break;
		case 0x03:
			if(ACCESSING_BITS_0_7)
			{
				ret = m_fdc->read(offset);
				m_fdc_timer->write_clk0(1);
				m_fdc_timer->write_clk0(0);  // Data register access clocks the FDC's PIT channel 0
			}
			break;
		case 0x08:
		case 0x09:
		case 0x0a:
		case 0x0b:
			if(ACCESSING_BITS_0_7)
				ret = m_fdc_timer->read(offset-0x08);
			break;
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
			if(ACCESSING_BITS_0_7)
				ret = m_hdc->read(offset-0x10);
			logerror("WD1010 register %i read, mask %04x\n",offset-0x10,mem_mask);
			break;
		case 0x18:
		case 0x19:
		case 0x1a:
		case 0x1b:
			if(ACCESSING_BITS_0_7)
				ret = m_hdc_timer->read(offset-0x18);
			break;
	}

	return ret;
}

void ngen_state::fdc_irq_w(int state)
{
	m_pic->ir7_w(state);
}

void ngen_state::fdc_drq_w(int state)
{
	m_dmac->dreq3_w(state);
}

// Floppy disk control register
// Bit 0 - enable drive and LED
// Bit 2 - floppy motor
// Bit 5 - side select
// Bit 6 - 1 = 2Mhz for seek, 0 = 1MHz for read/write
// Bit 7 - FDC reset
void ngen_state::fdc_control_w(uint8_t data)
{
	m_fdc->set_floppy(m_fd0->get_device());
	m_fd0->get_device()->mon_w(!BIT(data, 2));
	m_fd0->get_device()->ss_w(BIT(data, 5));
	m_fdc->mr_w(BIT(data, 7));
}

// Hard disk control register
// bit 0 - Drive select 0 - selects module hard disk
// bit 1 - Drive select 1 - selects expansion module hard disk (if available)
// bit 2 - enable DMA transfer of module ROM contents to X-Bus master memory
// bits 3-5 - select head / expansion module head
// bit 6 - write enable, must be set to write to a hard disk
// bit 7 - HDC reset
void ngen_state::hdc_control_w(uint8_t data)
{
	m_hdc_control = data;
	if(m_hdc_control & 0x04)
	{
		m_disk_rom_ptr = 0;
		popmessage("HDD: DMA ROM transfer start\n");
		m_dmac->dreq3_w(1);
		//m_dmac->dreq3_w(0);
	}
}

// page of system RAM to access
// bit 7 = disables read/write signals to the WD1010
void ngen_state::disk_addr_ext(uint8_t data)
{
	m_disk_page = data & 0x7f;
}

uint8_t ngen_state::hd_buffer_r(offs_t offset)
{
	return m_hd_buffer[offset];
}

void ngen_state::hd_buffer_w(offs_t offset, uint8_t data)
{
	m_hd_buffer[offset] = data;
}

void ngen_state::dma_hrq_changed(int state)
{
	if(m_maincpu)
		m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);
	else
		m_i386cpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);
}

void ngen_state::dma_eop_changed(int state)
{
	if(m_dma_channel == 0)
	{
		if(state)
			m_control |= 0x02;
		else
			m_control &= ~0x02;
	}
	if(m_dma_channel == 3)
	{
		if(state)
		{
			if(m_hdc_control & 0x04) // ROM transfer
				m_hdc_control &= ~0x04;  // switch it off when done
		}
	}
}

void ngen_state::set_dma_channel(int channel, int state)
{
	if(!state)
		m_dma_channel = channel;
	else if(m_dma_channel == channel)
		m_dma_channel = -1;
}

void ngen_state::dack0_w(int state) { set_dma_channel(0, state); }
void ngen_state::dack1_w(int state) { set_dma_channel(1, state); }
void ngen_state::dack2_w(int state) { set_dma_channel(2, state); }
void ngen_state::dack3_w(int state) { set_dma_channel(3, state); }

uint8_t ngen_state::dma_3_dack_r()
{
	uint16_t ret = 0xffff;

	if((m_hdc_control & 0x04) && m_disk_rom)
	{
		ret = m_disk_rom->base()[m_disk_rom_ptr++] << 8;
		printf("DMA3 DACK: returning %02x\n",ret);
		if(m_disk_rom_ptr < 0x1000)
		{
			m_dmac->dreq3_w(1);
			//m_dmac->dreq3_w(0);
		}
	}
	m_dma_high_byte = ret & 0xff00;
	return ret;
}

uint8_t ngen_state::dma_read_word(offs_t offset)
{
	cpu_device* cpu;
	uint16_t result;

	if(m_maincpu)
		cpu = m_maincpu;
	else
		cpu = m_i386cpu;
	address_space& prog_space = cpu->space(AS_PROGRAM); // get the right address space

	if(m_dma_channel == -1)
		return 0xff;
	offs_t page_offset = ((offs_t) m_dma_offset[m_dma_channel]) << 16;

	result = prog_space.read_word((page_offset & 0xfe0000) | (offset << 1));
	m_dma_high_byte = result & 0xFF00;
	popmessage("DMA byte address %06x read %04x\n", (page_offset & 0xfe0000) | (offset << 1),result);
	return result & 0xff;
}


void ngen_state::dma_write_word(offs_t offset, uint8_t data)
{
	cpu_device* cpu;

	if(m_maincpu)
		cpu = m_maincpu;
	else
		cpu = m_i386cpu;
	address_space& prog_space = cpu->space(AS_PROGRAM); // get the right address space

	if(m_dma_channel == -1)
		return;
	offs_t page_offset = ((offs_t) m_dma_offset[m_dma_channel]) << 16;

	prog_space.write_word((page_offset & 0xfe0000) | (offset << 1), data);
	popmessage("DMA byte address %06x write %04x\n", (page_offset & 0xfe0000) | (offset << 1), m_dma_high_byte | data);
}


MC6845_UPDATE_ROW( ngen_state::crtc_update_row )
{
	uint16_t addr = ma;

	for(int x=0;x<bitmap.width();x+=9)
	{
		uint8_t ch = m_vram.read16(addr++) & 0xff;
		for(int z=0;z<9;z++)
		{
			if(BIT(m_fontram.read16(ch*16+ra),8-z))
				bitmap.pix(y,x+z) = rgb_t(0,0xff,0);
			else
				bitmap.pix(y,x+z) = rgb_t(0,0,0);
		}
	}
}

uint8_t ngen_state::irq_cb()
{
	return m_pic->acknowledge();
}

uint16_t ngen_state::b38_keyboard_r(offs_t offset, uint16_t mem_mask)
{
	uint8_t ret = 0;
	switch(offset)
	{
	case 0:
	case 1:  // keyboard UART
		// status expects bit 0 to be set (UART transmit ready)
		if(ACCESSING_BITS_0_7)
			ret = m_viduart->read(offset & 1);
		break;
	}
	return ret;
}

void ngen_state::b38_keyboard_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	switch(offset)
	{
	case 0:
	case 1:
		if(ACCESSING_BITS_0_7)
			m_viduart->write(offset & 1, data & 0xff);
		break;
	}
}

uint16_t ngen_state::b38_crtc_r(offs_t offset, uint16_t mem_mask)
{
	uint8_t ret = 0;
	switch(offset)
	{
	case 0:
		if(ACCESSING_BITS_0_7)
			ret = m_crtc->register_r();
		break;
	case 1:
		if(ACCESSING_BITS_0_7)
			ret = m_viduart->data_r();
		break;
	}
	return ret;
}

void ngen_state::b38_crtc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	switch(offset)
	{
	case 0:
		if(ACCESSING_BITS_0_7)
			m_crtc->address_w(data & 0xff);
		break;
	case 1:
		if(ACCESSING_BITS_0_7)
			m_crtc->register_w(data & 0xff);
		break;
	}
}

void ngen_state::machine_start()
{
	memory_share* vidshare = memshare("vram");
	memory_share* fontshare = memshare("fontram");
	if(vidshare == nullptr || fontshare == nullptr)
		fatalerror("VRAM not found\n");
	m_vram.set(*vidshare,2);
	m_fontram.set(*fontshare,2);
}

void ngen_state::machine_reset()
{
	m_port00 = 0;
	m_control = 0;
	m_xbus_current = 0;
	m_viduart->write_dsr(0);
	m_viduart->write_cts(0);
	m_fd0->get_device()->set_rpm(300);
}

// boot ROMs from modules are not mapped anywhere, instead, they have to send the code from the boot ROM via DMA
void ngen_state::ngen_mem(address_map &map)
{
	map(0x00000, 0xf7fff).ram();
	map(0xf8000, 0xf9fff).ram().share("vram");
	map(0xfa000, 0xfbfff).ram().share("fontram");
	map(0xfc000, 0xfcfff).ram();
	map(0xfe000, 0xfffff).rom().region("bios", 0);
}

void ngen_state::ngen_io(address_map &map)
{
	map(0x0000, 0x0001).rw(FUNC(ngen_state::xbus_r), FUNC(ngen_state::xbus_w));

	// Floppy/Hard disk module
//  map(0x0100, 0x0107).rw("fdc", FUNC(wd2797_t::read), FUNC(wd2797_t::write)).umask16(0x00ff);  // a guess for now
//  map(0x0108, 0x0108).w(FUNC(ngen_state::fdc_control_w));
//  map(0x010a, 0x010a).w(FUNC(ngen_state::hdc_control_w));
//  map(0x010e, 0x010e).w(FUNC(ngen_state::disk_addr_ext));  // X-Bus extended address register
//  map(0x0110, 0x0117).rw("fdc_timer", FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	// 0x0120-0x012f - WD1010 Winchester disk controller (unemulated)
//  map(0x0130, 0x0137).rw("hdc_timer", FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);

}

void ngen_state::ngen386_mem(address_map &map)
{
	map(0x00000000, 0x000f7fff).ram();
	map(0x000f8000, 0x000f9fff).ram().share("vram");
	map(0x000fa000, 0x000fbfff).ram().share("fontram");
	map(0x000fc000, 0x000fcfff).ram();
	map(0x000fe000, 0x000fffff).rom().region("bios", 0);
	map(0x00100000, 0x00ffffff).ram();  // some extra RAM
	map(0xffffe000, 0xffffffff).rom().region("bios", 0);
}

void ngen_state::ngen386i_mem(address_map &map)
{
	map(0x00000000, 0x000f7fff).ram();
	map(0x000f8000, 0x000f9fff).ram().share("vram");
	map(0x000fa000, 0x000fbfff).ram().share("fontram");
	map(0x000fc000, 0x000fffff).rom().region("bios", 0);
	map(0x00100000, 0x00ffffff).ram();  // some extra RAM
	map(0xffffc000, 0xffffffff).rom().region("bios", 0);
}

void ngen_state::ngen386_io(address_map &map)
{
	map(0x0000, 0x0001).rw(FUNC(ngen_state::xbus_r), FUNC(ngen_state::xbus_w));
//  map(0xf800, 0xfeff).rw(FUNC(ngen_state::peripheral_r), FUNC(ngen_state::peripheral_w));
	map(0xfd08, 0xfd0b).rw(FUNC(ngen_state::b38_crtc_r), FUNC(ngen_state::b38_crtc_w));
	map(0xfd0c, 0xfd0f).rw(FUNC(ngen_state::b38_keyboard_r), FUNC(ngen_state::b38_keyboard_w));
}

static INPUT_PORTS_START( ngen )
INPUT_PORTS_END

static void keyboard(device_slot_interface &device)
{
	device.option_add("ngen", NGEN_KEYBOARD);
}

static void ngen_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

void ngen_state::ngen(machine_config &config)
{
	// basic machine hardware
	I80186(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &ngen_state::ngen_mem);
	m_maincpu->set_addrmap(AS_IO, &ngen_state::ngen_io);
	m_maincpu->chip_select_callback().set(FUNC(ngen_state::cpu_peripheral_cb));
	m_maincpu->tmrout0_handler().set(FUNC(ngen_state::cpu_timer_w));
	m_maincpu->read_slave_ack_callback().set(FUNC(ngen_state::irq_cb));

	PIC8259(config, m_pic, 0);
	m_pic->out_int_callback().set(m_maincpu, FUNC(i80186_cpu_device::int0_w));

	PIT8254(config, m_pit, 0);
	m_pit->set_clk<0>(78120/4);  // 19.53kHz, /4 of the CPU timer output?
	m_pit->out_handler<0>().set(FUNC(ngen_state::pit_out0_w));  // RS232 channel B baud rate
	m_pit->set_clk<1>(14.7456_MHz_XTAL / 12);  // correct? - based on patent
	m_pit->out_handler<1>().set(FUNC(ngen_state::pit_out1_w));  // RS232 channel A baud rate
	m_pit->set_clk<2>(14.7456_MHz_XTAL / 12);
	m_pit->out_handler<2>().set(FUNC(ngen_state::pit_out2_w));

	AM9517A(config, m_dmac, 14.7456_MHz_XTAL / 3);  // NEC D8237A, divisor unknown
	m_dmac->out_hreq_callback().set(FUNC(ngen_state::dma_hrq_changed));
	m_dmac->out_eop_callback().set(FUNC(ngen_state::dma_eop_changed));
	m_dmac->in_memr_callback().set(FUNC(ngen_state::dma_read_word));  // DMA is always 16-bit
	m_dmac->out_memw_callback().set(FUNC(ngen_state::dma_write_word));
	m_dmac->out_dack_callback<0>().set(FUNC(ngen_state::dack0_w));
	m_dmac->out_dack_callback<1>().set(FUNC(ngen_state::dack1_w));
	m_dmac->out_dack_callback<2>().set(FUNC(ngen_state::dack2_w));
	m_dmac->out_dack_callback<3>().set(FUNC(ngen_state::dack3_w));
	m_dmac->in_ior_callback<0>().set(FUNC(ngen_state::dma_0_dack_r));
	m_dmac->in_ior_callback<1>().set(FUNC(ngen_state::dma_1_dack_r));
	m_dmac->in_ior_callback<2>().set(FUNC(ngen_state::dma_2_dack_r));
	m_dmac->in_ior_callback<3>().set(FUNC(ngen_state::dma_3_dack_r));
	m_dmac->out_iow_callback<0>().set(FUNC(ngen_state::dma_0_dack_w));
	m_dmac->out_iow_callback<1>().set(FUNC(ngen_state::dma_1_dack_w));
	m_dmac->out_iow_callback<2>().set(FUNC(ngen_state::dma_2_dack_w));
	m_dmac->out_iow_callback<3>().set(FUNC(ngen_state::dma_3_dack_w));

	// I/O board
	UPD7201(config, m_iouart, 0); // clocked by PIT channel 2?
	m_iouart->out_txda_callback().set("rs232_a", FUNC(rs232_port_device::write_txd));
	m_iouart->out_txdb_callback().set("rs232_b", FUNC(rs232_port_device::write_txd));
	m_iouart->out_dtra_callback().set("rs232_a", FUNC(rs232_port_device::write_dtr));
	m_iouart->out_dtrb_callback().set("rs232_b", FUNC(rs232_port_device::write_dtr));
	m_iouart->out_rtsa_callback().set("rs232_a", FUNC(rs232_port_device::write_rts));
	m_iouart->out_rtsb_callback().set("rs232_b", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232_a", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_iouart, FUNC(upd7201_device::rxa_w));
	rs232a.cts_handler().set(m_iouart, FUNC(upd7201_device::ctsa_w));
	rs232a.dcd_handler().set(m_iouart, FUNC(upd7201_device::dcda_w));
	rs232a.ri_handler().set(m_iouart, FUNC(upd7201_device::synca_w));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232_b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_iouart, FUNC(upd7201_device::rxb_w));
	rs232b.cts_handler().set(m_iouart, FUNC(upd7201_device::ctsb_w));
	rs232b.dcd_handler().set(m_iouart, FUNC(upd7201_device::dcdb_w));
	rs232b.ri_handler().set(m_iouart, FUNC(upd7201_device::syncb_w));

	// TODO: SCN2652 MPCC (not implemented), used for RS-422 cluster communications?

	// video board
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_size(720, 348);
	screen.set_visarea(0, 719, 0, 347);
	screen.set_refresh_hz(60);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	MC6845(config, m_crtc, 19980000 / 9);  // divisor unknown -- /9 gives 60Hz output, so likely correct
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(9);
	m_crtc->set_update_row_callback(FUNC(ngen_state::crtc_update_row));

	// keyboard UART (patent says i8251 is used for keyboard communications, it is located on the video board)
	I8251(config, m_viduart, 0);  // main clock unknown, Rx/Tx clocks are 19.53kHz
//  m_viduart->txempty_handler().set(m_pic, FUNC(pic8259_device::ir4_w));
	m_viduart->txd_handler().set("keyboard", FUNC(rs232_port_device::write_txd));
	rs232_port_device &kbd(RS232_PORT(config, "keyboard", keyboard, "ngen"));
	kbd.rxd_handler().set(m_viduart, FUNC(i8251_device::write_rxd));

	CLOCK(config, "refresh_clock", 19200*16).signal_handler().set(FUNC(ngen_state::timer_clk_out)); // should be 19530Hz

	// floppy disk / hard disk module (WD2797 FDC, WD1010 HDC, plus an 8253 timer for each)
	WD2797(config, m_fdc, 20_MHz_XTAL / 20);
	m_fdc->intrq_wr_callback().set(FUNC(ngen_state::fdc_irq_w));
	m_fdc->drq_wr_callback().set(m_maincpu, FUNC(i80186_cpu_device::drq1_w));
	m_fdc->set_force_ready(true);

	PIT8253(config, m_fdc_timer, 0);
	m_fdc_timer->set_clk<0>(0);
	m_fdc_timer->out_handler<0>().set(m_pic, FUNC(pic8259_device::ir5_w));  // clocked on FDC data register access
	m_fdc_timer->set_clk<1>(20_MHz_XTAL / 20);
//  m_fdc_timer->out_handler<1>().set(m_pic, FUNC(pic8259_device::ir5_w));  // 1MHz
	m_fdc_timer->set_clk<2>(20_MHz_XTAL / 20);
//  m_fdc_timer->out_handler<2>().set(m_pic, FUNC(pic8259_device::ir5_w));

	// TODO: WD1010 HDC (not implemented), use WD2010 for now
	WD2010(config, m_hdc, 20_MHz_XTAL / 4);
	m_hdc->out_intrq_callback().set(m_pic, FUNC(pic8259_device::ir2_w));
	m_hdc->in_bcs_callback().set(FUNC(ngen_state::hd_buffer_r));
	m_hdc->out_bcs_callback().set(FUNC(ngen_state::hd_buffer_w));
	m_hdc->in_drdy_callback().set_constant(1);
	m_hdc->in_index_callback().set_constant(1);
	m_hdc->in_wf_callback().set_constant(1);
	m_hdc->in_tk000_callback().set_constant(1);
	m_hdc->in_sc_callback().set_constant(1);

	PIT8253(config, m_hdc_timer, 0);
	m_hdc_timer->set_clk<2>(20_MHz_XTAL / 10);  // 2MHz

	FLOPPY_CONNECTOR(config, "fdc:0", ngen_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	HARDDISK(config, "hard0", 0);
}

void ngen386_state::ngen386(machine_config &config)
{
	I386(config, m_i386cpu, 50_MHz_XTAL / 2);
	m_i386cpu->set_addrmap(AS_PROGRAM, &ngen386_state::ngen386_mem);
	m_i386cpu->set_addrmap(AS_IO, &ngen386_state::ngen386_io);
	m_i386cpu->set_irq_acknowledge_callback("pic", FUNC(pic8259_device::inta_cb));

	PIC8259(config, m_pic, 0);
	m_pic->out_int_callback().set_inputline(m_i386cpu, 0);

	PIT8254(config, m_pit, 0);
	m_pit->set_clk<0>(78120/4);  // 19.53kHz, /4 of the CPU timer output?
	m_pit->out_handler<0>().set(FUNC(ngen386_state::pit_out0_w));  // RS232 channel B baud rate
	m_pit->set_clk<1>(14.7456_MHz_XTAL / 12);  // correct? - based on patent
	m_pit->out_handler<1>().set(FUNC(ngen386_state::pit_out1_w));  // RS232 channel A baud rate
	m_pit->set_clk<2>(14.7456_MHz_XTAL / 12);
	m_pit->out_handler<2>().set(FUNC(ngen386_state::pit_out2_w));

	AM9517A(config, m_dmac, 14.7456_MHz_XTAL / 3);  // NEC D8237A, divisor unknown
	m_dmac->out_hreq_callback().set(FUNC(ngen386_state::dma_hrq_changed));
	m_dmac->out_eop_callback().set(FUNC(ngen386_state::dma_eop_changed));
	m_dmac->in_memr_callback().set(FUNC(ngen386_state::dma_read_word));  // DMA is always 16-bit
	m_dmac->out_memw_callback().set(FUNC(ngen386_state::dma_write_word));
	m_dmac->out_dack_callback<0>().set(FUNC(ngen386_state::dack0_w));
	m_dmac->out_dack_callback<1>().set(FUNC(ngen386_state::dack1_w));
	m_dmac->out_dack_callback<2>().set(FUNC(ngen386_state::dack2_w));
	m_dmac->out_dack_callback<3>().set(FUNC(ngen386_state::dack3_w));
	m_dmac->in_ior_callback<0>().set(FUNC(ngen386_state::dma_0_dack_r));
	m_dmac->in_ior_callback<1>().set(FUNC(ngen386_state::dma_1_dack_r));
	m_dmac->in_ior_callback<2>().set(FUNC(ngen386_state::dma_2_dack_r));
	m_dmac->in_ior_callback<3>().set(FUNC(ngen386_state::dma_3_dack_r));
	m_dmac->out_iow_callback<0>().set(FUNC(ngen386_state::dma_0_dack_w));
	m_dmac->out_iow_callback<1>().set(FUNC(ngen386_state::dma_1_dack_w));
	m_dmac->out_iow_callback<2>().set(FUNC(ngen386_state::dma_2_dack_w));
	m_dmac->out_iow_callback<3>().set(FUNC(ngen386_state::dma_3_dack_w));

	// I/O board
	UPD7201(config, m_iouart, 0); // clocked by PIT channel 2?
	m_iouart->out_txda_callback().set("rs232_a", FUNC(rs232_port_device::write_txd));
	m_iouart->out_txdb_callback().set("rs232_b", FUNC(rs232_port_device::write_txd));
	m_iouart->out_dtra_callback().set("rs232_a", FUNC(rs232_port_device::write_dtr));
	m_iouart->out_dtrb_callback().set("rs232_b", FUNC(rs232_port_device::write_dtr));
	m_iouart->out_rtsa_callback().set("rs232_a", FUNC(rs232_port_device::write_rts));
	m_iouart->out_rtsb_callback().set("rs232_b", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232_a", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_iouart, FUNC(upd7201_device::rxa_w));
	rs232a.cts_handler().set(m_iouart, FUNC(upd7201_device::ctsa_w));
	rs232a.dcd_handler().set(m_iouart, FUNC(upd7201_device::dcda_w));
	rs232a.ri_handler().set(m_iouart, FUNC(upd7201_device::synca_w));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232_b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_iouart, FUNC(upd7201_device::rxb_w));
	rs232b.cts_handler().set(m_iouart, FUNC(upd7201_device::ctsb_w));
	rs232b.dcd_handler().set(m_iouart, FUNC(upd7201_device::dcdb_w));
	rs232b.ri_handler().set(m_iouart, FUNC(upd7201_device::syncb_w));

	// TODO: SCN2652 MPCC (not implemented), used for RS-422 cluster communications?

	// video board
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_size(720, 348);
	screen.set_visarea(0, 719, 0, 347);
	screen.set_refresh_hz(60);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	MC6845(config, m_crtc, 19980000 / 9);  // divisor unknown -- /9 gives 60Hz output, so likely correct
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(9);
	m_crtc->set_update_row_callback(FUNC(ngen386_state::crtc_update_row));

	// keyboard UART (patent says i8251 is used for keyboard communications, it is located on the video board)
	I8251(config, m_viduart, 0);  // main clock unknown, Rx/Tx clocks are 19.53kHz
//  m_viduart->txempty_handler().set("pic", FUNC(pic8259_device::ir4_w));
	m_viduart->txd_handler().set("keyboard", FUNC(rs232_port_device::write_txd));
	rs232_port_device &kbd(RS232_PORT(config, "keyboard", keyboard, "ngen"));
	kbd.rxd_handler().set(m_viduart, FUNC(i8251_device::write_rxd));

	CLOCK(config, "refresh_clock", 19200*16).signal_handler().set(FUNC(ngen386_state::timer_clk_out)); // should be 19530Hz

	// floppy disk / hard disk module (WD2797 FDC, WD1010 HDC, plus an 8253 timer for each)
	WD2797(config, m_fdc, 20_MHz_XTAL / 20);
	m_fdc->intrq_wr_callback().set(FUNC(ngen386_state::fdc_irq_w));
	//m_fdc->drq_wr_callback().set(m_i386cpu, FUNC(i80186_cpu_device_device::drq1_w));
	m_fdc->set_force_ready(true);

	PIT8253(config, m_fdc_timer, 0);
	m_fdc_timer->set_clk<0>(0);
	m_fdc_timer->out_handler<0>().set(m_pic, FUNC(pic8259_device::ir5_w));  // clocked on FDC data register access
	m_fdc_timer->set_clk<1>(20_MHz_XTAL / 20);
//  m_fdc_timer->out_handler<1>().set(m_pic, FUNC(pic8259_device::ir5_w));  // 1MHz
	m_fdc_timer->set_clk<2>(20_MHz_XTAL / 20);
//  m_fdc_timer->out_handler<2>().set(m_pic, FUNC(pic8259_device::ir5_w));

	// TODO: WD1010 HDC (not implemented), use WD2010 for now
	WD2010(config, m_hdc, 20_MHz_XTAL / 4);
	m_hdc->out_intrq_callback().set(m_pic, FUNC(pic8259_device::ir2_w));
	m_hdc->in_bcs_callback().set(FUNC(ngen386_state::hd_buffer_r));
	m_hdc->out_bcs_callback().set(FUNC(ngen386_state::hd_buffer_w));
	m_hdc->in_drdy_callback().set_constant(1);
	m_hdc->in_index_callback().set_constant(1);
	m_hdc->in_wf_callback().set_constant(1);
	m_hdc->in_tk000_callback().set_constant(1);
	m_hdc->in_sc_callback().set_constant(1);

	PIT8253(config, m_hdc_timer, 0);
	m_hdc_timer->set_clk<2>(20_MHz_XTAL / 10);  // 2MHz

	FLOPPY_CONNECTOR(config, "fdc:0", ngen_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	HARDDISK(config, "hard0", 0);
}

void ngen386_state::_386i(machine_config &config)
{
	ngen386(config);
	m_i386cpu->set_addrmap(AS_PROGRAM, &ngen386_state::ngen386i_mem);
}

ROM_START( ngen )
	ROM_REGION16_LE( 0x2000, "bios", 0)
	ROM_LOAD16_BYTE( "72-00414_80186_cpu.bin",  0x000000, 0x001000, CRC(e1387a03) SHA1(ddca4eba67fbf8b731a8009c14f6b40edcbc3279) )  // bootstrap ROM v8.4
	ROM_LOAD16_BYTE( "72-00415_80186_cpu.bin",  0x000001, 0x001000, CRC(a6dde7d9) SHA1(b4d15c1bce31460ab5b92ff43a68c15ac5485816) )

	ROM_REGION16_LE( 0x2000, "vram", ROMREGION_ERASE00 )
	ROM_REGION16_LE( 0x2000, "fontram", ROMREGION_ERASE00 )

	ROM_REGION( 0x1000, "disk", 0)
	ROM_LOAD( "72-00422_10mb_disk.bin", 0x000000, 0x001000,  CRC(f5b046b6) SHA1(b303c6f6aa40504016de9826879bc316e44389aa) )

	ROM_REGION( 0x20, "disk_prom", 0)
	ROM_LOAD( "72-00422_10mb_disk_15d.bin", 0x000000, 0x000020,  CRC(121ee494) SHA1(9a8d3c336cc7378a71f9d48c99f88515eb236fbf) )
ROM_END

// not sure just how similar these systems are to the 80186 model, but are here at the moment to document the dumps
ROM_START( ngenb38 )
	ROM_REGION32_LE( 0x2000, "bios", 0)
	ROM_LOAD16_BYTE( "72-168_fpc_386_cpu.bin",  0x000000, 0x001000, CRC(250a3b68) SHA1(49c070514bac264fa4892f284f7d2c852ae6605d) )
	ROM_LOAD16_BYTE( "72-167_fpc_386_cpu.bin",  0x000001, 0x001000, CRC(4010cc4e) SHA1(74a3024d605569056484d08b63f19fbf8eaf31c6) )

	ROM_REGION16_LE( 0x2000, "vram", ROMREGION_ERASE00 )
	ROM_REGION16_LE( 0x2000, "fontram", ROMREGION_ERASE00 )
ROM_END

ROM_START( 386i )
	ROM_REGION32_LE( 0x4000, "bios", 0)
	ROM_LOAD16_BYTE( "72-1561o_386i_cpu.bin",  0x000000, 0x002000, CRC(b5efd768) SHA1(8b250d47d9c6eb82e1afaeb2244d8c4134ecbc47) )
	ROM_LOAD16_BYTE( "72-1562e_386i_cpu.bin",  0x000001, 0x002000, CRC(002d0d3a) SHA1(31de8592999377db9251acbeff348390a2d2602a) )

	ROM_REGION16_LE( 0x2000, "vram", ROMREGION_ERASE00 )
	ROM_REGION16_LE( 0x2000, "fontram", ROMREGION_ERASE00 )

	ROM_REGION( 0x2000, "video", 0)
	ROM_LOAD( "72-1630_gc-104_vga.bin",  0x000000, 0x002000, CRC(4e4d8ebe) SHA1(50c96ccb4d0bd1beb2d1aee0d18b2c462d25fc8f) )
ROM_END

} // anonymous namespace


COMP( 1983, ngen,    0,    0, ngen,    ngen, ngen_state,    empty_init, "Convergent Technologies",  "NGEN CP-001", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1991, ngenb38, ngen, 0, ngen386, ngen, ngen386_state, empty_init, "Financial Products Corp.", "B28/38",      MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1990, 386i,    ngen, 0, _386i,   ngen, ngen386_state, empty_init, "Convergent Technologies",  "386i",        MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



ngp.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/******************************************************************************

SNK NeoGeo Pocket driver

After setting the initial console settings power cycle the unit at least once,
otherwise the settings will not be saved properly.


The NeoGeo Pocket (Color) contains one big chip which contains the following
components:
- Toshiba TLCS-900/H cpu core with 64KB ROM
- Z80 cpu core
- K1GE (mono)/K2GE (color) graphics chip + lcd controller
- SN76489A compatible sound chip
- DAC for additional sound
- RTC
- RAM


The following TLCS-900/H input sources are known:
- INT0 - Alarm/RTC irq
- INT4 - VBlank irq
- INT5 - Interrupt from Z80
- AN0 - Analog input which is used to determine the battery level
- NMI - Triggered by power button
- TIO - HBlank signal


The following TLCS-900/H output destination are known:
- TO3 - connected to Z80 INT pin


The cartridges
==============

The cartridges used flash chips produced by Toshiba, Sharp or Samsung. These
are the only 3 manufacturers supported by the NeoGeo pocket bios. The device
IDs supported appear to be SNK exclusive. Most likely because of the factory
blocked game data areas on these chip.

These manufacturer IDs are supported: 0x98, 0xec, 0xb0
These device IDs are supported: 0xab, 0x2c, 0x2f

There is support for 3 different sizes of flash roms: 4Mbit, 8Mbit 16Mbit. The
32Mbit games appear to be 2 16Mbit flash chips in 2 different memory regions (?).

The flash chips have a couple of different sized blocks. When writing to a
cartridge the neogeo pocket bios will erase the proper block and write the data
to the block.

The relation between block number and flash chip is as follows:

 # |  16Mbit (2f)  |  8Mbit (2c) |  4Mbit (ab)
---+---------------+-------------+-------------
 0 | 000000-00ffff | 00000-0ffff | 00000-0ffff
 1 | 010000-01ffff | 10000-1ffff | 10000-1ffff
 2 | 020000-02ffff | 20000-2ffff | 20000-2ffff
 3 | 030000-03ffff | 30000-3ffff | 30000-3ffff
 4 | 040000-01ffff | 40000-4ffff | 40000-4ffff
 5 | 050000-01ffff | 50000-5ffff | 50000-5ffff
 6 | 060000-01ffff | 60000-6ffff | 60000-6ffff
 7 | 070000-01ffff | 70000-7ffff | 70000-77fff
 8 | 080000-01ffff | 80000-8ffff | 78000-79fff
 9 | 090000-01ffff | 90000-9ffff | 7a000-7bfff
10 | 0a0000-01ffff | a0000-affff | 7c000-7ffff
11 | 0b0000-01ffff | b0000-bffff |
12 | 0c0000-01ffff | c0000-cffff |
13 | 0d0000-01ffff | d0000-dffff |
14 | 0e0000-01ffff | e0000-effff |
15 | 0f0000-01ffff | f0000-f7fff |
16 | 100000-10ffff | f8000-f9fff |
17 | 110000-11ffff | fa000-fbfff |
18 | 120000-12ffff | fc000-fffff |
19 | 130000-13ffff |             |
20 | 140000-14ffff |             |
21 | 150000-15ffff |             |
22 | 160000-16ffff |             |
23 | 170000-17ffff |             |
24 | 180000-18ffff |             |
25 | 190000-19ffff |             |
26 | 1a0000-1affff |             |
27 | 1b0000-1bffff |             |
28 | 1c0000-1cffff |             |
29 | 1d0000-1dffff |             |
30 | 1e0000-1effff |             |
31 | 1f0000-1f7fff |             |
32 | 1f8000-1f9fff |             |
33 | 1fa000-1fbfff |             |
34 | 1fc000-1fffff |             |

The last block is always reserved for use by the system. The Neogeo Pocket Color
bios does some tests on this last block to see if the flash functionality is
working. It does this on every boot!

The Toshiba TC58FVT004 seems to have an interface similar to what is used in
the Neogeo Pocket.


******************************************************************************/


#include "emu.h"
#include "k1ge.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/tlcs900/tmp95c061.h"
#include "cpu/z80/z80.h"
#include "sound/t6w28.h"
#include "sound/dac.h"

#include "dirtc.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

enum flash_state
{
	F_READ,                     /* xxxx F0 or 5555 AA 2AAA 55 5555 F0 */
	F_PROG1,
	F_PROG2,
	F_COMMAND,
	F_ID_READ,                  /* 5555 AA 2AAA 55 5555 90 */
	F_AUTO_PROGRAM,             /* 5555 AA 2AAA 55 5555 A0 address data */
	F_AUTO_CHIP_ERASE,          /* 5555 AA 2AAA 55 5555 80 5555 AA 2AAA 55 5555 10 */
	F_AUTO_BLOCK_ERASE,         /* 5555 AA 2AAA 55 5555 80 5555 AA 2AAA 55 block_address 30 */
	F_BLOCK_PROTECT             /* 5555 AA 2AAA 55 5555 9A 5555 AA 2AAA 55 5555 9A */
};


class ngp_state : public driver_device, public device_nvram_interface, device_rtc_interface
{
public:
	ngp_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		device_nvram_interface(mconfig, *this),
		device_rtc_interface(mconfig, *this),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen"),
		m_z80(*this, "soundcpu"),
		m_t6w28(*this, "t6w28"),
		m_ldac(*this, "ldac"),
		m_rdac(*this, "rdac"),
		m_cart(*this, "cartslot"),
		m_mainram(*this, "mainram"),
		m_k1ge(*this, "k1ge"),
		m_io_controls(*this, "Controls"),
		m_io_power(*this, "Power")
	{ }

	void ngp_common(machine_config &config);
	void ngp(machine_config &config);
	void ngpc(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(power_callback);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second) override ATTR_COLD;

	virtual void nvram_default() override;
	virtual bool nvram_read(util::read_stream &file) override;
	virtual bool nvram_write(util::write_stream &file) override;

private:
	required_device<tmp95c061_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<cpu_device> m_z80;
	required_device<t6w28_device> m_t6w28;
	required_device<dac_byte_interface> m_ldac;
	required_device<dac_byte_interface> m_rdac;
	required_device<generic_slot_device> m_cart;
	required_shared_ptr<uint8_t> m_mainram;
	required_device<k1ge_device> m_k1ge;
	required_ioport m_io_controls;
	required_ioport m_io_power;

	uint8_t m_io_reg[0x40] = { };
	uint8_t m_old_to3 = 0;
	emu_timer* m_seconds_timer = nullptr;

	struct {
		bool      present = false;
		uint8_t   manufacturer_id = 0;
		uint8_t   device_id = 0;
		uint8_t   *data = nullptr;
		uint8_t   org_data[16] = { };
		int32_t   state = F_READ;
		uint8_t   command[2] = { };
	} m_flash_chip[2];

	bool m_nvram_loaded = false;

	uint8_t io_r(offs_t offset);
	void io_w(offs_t offset, uint8_t data);

	template <int Which> void flash_w(offs_t offset, uint8_t data);
	void flash0_w(offs_t offset, uint8_t data);
	void flash1_w(offs_t offset, uint8_t data);

	uint8_t z80_comm_r();
	void z80_comm_w(uint8_t data);
	void z80_signal_main_w(uint8_t data);

	void z80_clear_irq(uint8_t data);

	void vblank_pin_w(int state);
	void hblank_pin_w(int state);
	void tlcs900_porta(offs_t offset, uint8_t data);
	TIMER_CALLBACK_MEMBER(seconds_callback);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(load_ngp_cart);
	DECLARE_DEVICE_IMAGE_UNLOAD_MEMBER(unload_ngp_cart);

	void main_mem(address_map &map) ATTR_COLD;
	void z80_io(address_map &map) ATTR_COLD;
	void z80_mem(address_map &map) ATTR_COLD;
};


TIMER_CALLBACK_MEMBER(ngp_state::seconds_callback)
{
	advance_seconds();
}


uint8_t ngp_state::io_r(offs_t offset)
{
	uint8_t data = m_io_reg[offset];

	switch (offset)
	{
	case 0x11:  // year
		return convert_to_bcd(get_clock_register(RTC_YEAR) % 100);
	case 0x12:  // month
		return convert_to_bcd(get_clock_register(RTC_MONTH));
	case 0x13:  // day
		return convert_to_bcd(get_clock_register(RTC_DAY));
	case 0x14:  // hour
		return convert_to_bcd(get_clock_register(RTC_HOUR));
	case 0x15:  // minute
		return convert_to_bcd(get_clock_register(RTC_MINUTE));
	case 0x16:  // second
		return convert_to_bcd(get_clock_register(RTC_SECOND));
	case 0x30:  // Read controls
		return m_io_controls->read();
	case 0x31:
		data = m_io_power->read() & 0x01;
		// Sub-battery OK
		data |= 0x02;
		break;
	}
	return data;
}


void ngp_state::io_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 0x11:  // year
		set_clock_register(RTC_YEAR, bcd_to_integer(data));
		break;
	case 0x12:  // month
		set_clock_register(RTC_MONTH, bcd_to_integer(data));
		break;
	case 0x13:  // day
		set_clock_register(RTC_DAY, bcd_to_integer(data));
		break;
	case 0x14:  // hour
		set_clock_register(RTC_HOUR, bcd_to_integer(data));
		break;
	case 0x15:  // minute
		set_clock_register(RTC_MINUTE, bcd_to_integer(data));
		break;
	case 0x16:  // second
		set_clock_register(RTC_SECOND, bcd_to_integer(data));
		break;
	case 0x20:  // t6w28 "right"
	case 0x21:  // t6w28 "left"
		if (m_io_reg[0x38] == 0x55 && m_io_reg[0x39] == 0xAA)
			m_t6w28->write(0, data);
		break;

	case 0x22:  // DAC right
		m_rdac->write(data);
		break;
	case 0x23:  // DAC left
		m_ldac->write(data);
		break;

	case 0x36: // 50 written when system powers down, 05 written when system starts up
	case 0x37:
		break;
	case 0x38:  // Sound enable/disable.
		switch (data)
		{
		case 0x55:  // Enabled sound
			m_t6w28->set_enable(true);
			break;
		case 0xAA:  // Disable sound
			m_t6w28->set_enable(false);
			break;
		}
		break;

	case 0x39:  // Z80 enable/disable.
		switch (data)
		{
		case 0x55:  // Enable Z80
			m_z80->resume(SUSPEND_REASON_HALT);
			m_z80->reset();
			m_z80->set_input_line(0, CLEAR_LINE);
			break;
		case 0xAA:  // Disable Z80
			m_z80->suspend(SUSPEND_REASON_HALT, 1);
			break;
		}
		break;

	case 0x3a:  // Trigger Z80 NMI
		m_z80->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
		break;
	}
	m_io_reg[offset] = data;
}


template <int Which> void ngp_state::flash_w(offs_t offset, uint8_t data)
{
	if (!m_flash_chip[Which].present)
		return;

	switch (m_flash_chip[Which].state)
	{
	case F_READ:
		if (offset == 0x5555 && data == 0xaa)
			m_flash_chip[Which].state = F_PROG1;
		m_flash_chip[Which].command[0] = 0;
		break;
	case F_PROG1:
		if (offset == 0x2aaa && data == 0x55)
			m_flash_chip[Which].state = F_PROG2;
		else
			m_flash_chip[Which].state = F_READ;
		break;
	case F_PROG2:
		if (data == 0x30)
		{
			if (m_flash_chip[Which].command[0] == 0x80)
			{
				int size = 0x10000;
				uint8_t *block = m_flash_chip[Which].data;

				m_flash_chip[Which].state = F_AUTO_BLOCK_ERASE;
				switch (m_flash_chip[Which].device_id)
				{
				case 0xab:
					if (offset < 0x70000)
						block = m_flash_chip[Which].data + (offset & 0x70000);
					else
					{
						if (offset & 0x8000)
						{
							if (offset & 0x4000)
							{
								block = m_flash_chip[Which].data + (offset & 0x7c000);
								size = 0x4000;
							}
							else
							{
								block = m_flash_chip[Which].data + (offset & 0x7e000);
								size = 0x2000;
							}
						}
						else
						{
							block = m_flash_chip[Which].data + (offset & 0x78000);
							size = 0x8000;
						}
					}
					break;
				case 0x2c:
					if (offset < 0xf0000)
						block = m_flash_chip[Which].data + (offset & 0xf0000);
					else
					{
						if (offset & 0x8000)
						{
							if (offset & 0x4000)
							{
								block = m_flash_chip[Which].data + (offset & 0xfc000);
								size = 0x4000;
							}
							else
							{
								block = m_flash_chip[Which].data + (offset & 0xfe000);
								size = 0x2000;
							}
						}
						else
						{
							block = m_flash_chip[Which].data + (offset & 0xf8000);
							size = 0x8000;
						}
					}
					break;
				case 0x2f:
					if (offset < 0x1f0000)
						block = m_flash_chip[Which].data + (offset & 0x1f0000);
					else
					{
						if (offset & 0x8000)
						{
							if (offset & 0x4000)
							{
								block = m_flash_chip[Which].data + (offset & 0x1fc000);
								size = 0x4000;
							}
							else
							{
								block = m_flash_chip[Which].data + (offset & 0x1fe000);
								size = 0x2000;
							}
						}
						else
						{
							block = m_flash_chip[Which].data + (offset & 0x1f8000);
							size = 0x8000;
						}
					}
					break;
				}
				memset(block, 0xFF, size);
			}
			else
				m_flash_chip[Which].state = F_READ;
		}
		else if (offset == 0x5555)
		{
			switch (data)
			{
			case 0x80:
				m_flash_chip[Which].command[0] = 0x80;
				m_flash_chip[Which].state = F_COMMAND;
				break;
			case 0x90:
				m_flash_chip[Which].data[0x1fc000] = m_flash_chip[Which].manufacturer_id;
				m_flash_chip[Which].data[0xfc000] = m_flash_chip[Which].manufacturer_id;
				m_flash_chip[Which].data[0x7c000] = m_flash_chip[Which].manufacturer_id;
				m_flash_chip[Which].data[0] = m_flash_chip[Which].manufacturer_id;
				m_flash_chip[Which].data[0x1fc001] = m_flash_chip[Which].device_id;
				m_flash_chip[Which].data[0xfc001] = m_flash_chip[Which].device_id;
				m_flash_chip[Which].data[0x7c001] = m_flash_chip[Which].device_id;
				m_flash_chip[Which].data[1] = m_flash_chip[Which].device_id;
				m_flash_chip[Which].data[0x1fc002] = 0x02;
				m_flash_chip[Which].data[0xfc002] = 0x02;
				m_flash_chip[Which].data[0x7c002] = 0x02;
				m_flash_chip[Which].data[2] = 0x02;
				m_flash_chip[Which].data[0x1fc003] = 0x80;
				m_flash_chip[Which].data[0xfc003] = 0x80;
				m_flash_chip[Which].data[0x7c003] = 0x80;
				m_flash_chip[Which].data[3] = 0x80;
				m_flash_chip[Which].state = F_ID_READ;
				break;
			case 0x9a:
				if (m_flash_chip[Which].command[0] == 0x9a)
					m_flash_chip[Which].state = F_BLOCK_PROTECT;
				else
				{
					m_flash_chip[Which].command[0] = 0x9a;
					m_flash_chip[Which].state = F_COMMAND;
				}
				break;
			case 0xa0:
				m_flash_chip[Which].state = F_AUTO_PROGRAM;
				break;
			case 0xf0:
			default:
				m_flash_chip[Which].state = F_READ;
				break;
			}
		}
		else
			m_flash_chip[Which].state = F_READ;
		break;
	case F_COMMAND:
		if (offset == 0x5555 && data == 0xaa)
			m_flash_chip[Which].state = F_PROG1;
		else
			m_flash_chip[Which].state = F_READ;
		break;
	case F_ID_READ:
		if (offset == 0x5555 && data == 0xaa)
			m_flash_chip[Which].state = F_PROG1;
		else
			m_flash_chip[Which].state = F_READ;
		m_flash_chip[Which].command[0] = 0;
		break;
	case F_AUTO_PROGRAM:
		/* Only 1 -> 0 changes can be programmed */
		m_flash_chip[Which].data[offset] = m_flash_chip[Which].data[offset] & data;
		m_flash_chip[Which].state = F_READ;
		break;
	case F_AUTO_CHIP_ERASE:
		m_flash_chip[Which].state = F_READ;
		break;
	case F_AUTO_BLOCK_ERASE:
		m_flash_chip[Which].state = F_READ;
		break;
	case F_BLOCK_PROTECT:
		m_flash_chip[Which].state = F_READ;
		break;
	}

	if (m_flash_chip[Which].state == F_READ)
	{
		/* Exit command/back to normal operation*/
		m_flash_chip[Which].data[0] = m_flash_chip[Which].org_data[0];
		m_flash_chip[Which].data[1] = m_flash_chip[Which].org_data[1];
		m_flash_chip[Which].data[2] = m_flash_chip[Which].org_data[2];
		m_flash_chip[Which].data[3] = m_flash_chip[Which].org_data[3];
		m_flash_chip[Which].data[0x7c000] = m_flash_chip[Which].org_data[4];
		m_flash_chip[Which].data[0x7c001] = m_flash_chip[Which].org_data[5];
		m_flash_chip[Which].data[0x7c002] = m_flash_chip[Which].org_data[6];
		m_flash_chip[Which].data[0x7c003] = m_flash_chip[Which].org_data[7];
		m_flash_chip[Which].data[0xfc000] = m_flash_chip[Which].org_data[8];
		m_flash_chip[Which].data[0xfc001] = m_flash_chip[Which].org_data[9];
		m_flash_chip[Which].data[0xfc002] = m_flash_chip[Which].org_data[10];
		m_flash_chip[Which].data[0xfc003] = m_flash_chip[Which].org_data[11];
		m_flash_chip[Which].data[0x1fc000] = m_flash_chip[Which].org_data[12];
		m_flash_chip[Which].data[0x1fc001] = m_flash_chip[Which].org_data[13];
		m_flash_chip[Which].data[0x1fc002] = m_flash_chip[Which].org_data[14];
		m_flash_chip[Which].data[0x1fc003] = m_flash_chip[Which].org_data[15];
		m_flash_chip[Which].command[0] = 0;
	}
}


void ngp_state::flash0_w(offs_t offset, uint8_t data)
{
	flash_w<0>(offset, data);
}


void ngp_state::flash1_w(offs_t offset, uint8_t data)
{
	flash_w<1>(offset, data);
}


void ngp_state::main_mem(address_map &map)
{
	map(0x000080, 0x0000bf).rw(FUNC(ngp_state::io_r), FUNC(ngp_state::io_w));                        /* ngp/c specific i/o */
	map(0x004000, 0x006fff).ram().share(m_mainram);                              /* work ram */
	map(0x007000, 0x007fff).ram().share("soundram");                             /* shared with sound cpu */
	map(0x008000, 0x00bfff).rw(m_k1ge, FUNC(k1ge_device::read), FUNC(k1ge_device::write));       /* video chip */
	map(0x200000, 0x3fffff).w(FUNC(ngp_state::flash0_w));   /* cart area #1 */
	map(0x800000, 0x9fffff).w(FUNC(ngp_state::flash1_w));   /* cart area #2 */
	map(0xff0000, 0xffffff).rom().region("maincpu", 0);                          /* system rom */
}


uint8_t ngp_state::z80_comm_r()
{
	return m_io_reg[0x3c];
}


void ngp_state::z80_comm_w(uint8_t data)
{
	m_io_reg[0x3c] = data;
}


void ngp_state::z80_signal_main_w(uint8_t data)
{
	m_maincpu->set_input_line(TLCS900_INT5, ASSERT_LINE);
}


void ngp_state::z80_mem(address_map &map)
{
	map(0x0000, 0x0fff).ram().share("soundram");                    /* shared with tlcs900 */
	map(0x4000, 0x4001).w(m_t6w28, FUNC(t6w28_device::write));      /* sound chip (right, left) */
	map(0x8000, 0x8000).rw(FUNC(ngp_state::z80_comm_r), FUNC(ngp_state::z80_comm_w));  /* main-sound communication */
	map(0xc000, 0xc000).w(FUNC(ngp_state::z80_signal_main_w));               /* signal irq to main cpu */
}


void ngp_state::z80_clear_irq(uint8_t data)
{
	m_z80->set_input_line(0, CLEAR_LINE);

	/* I am not exactly sure what causes the maincpu INT5 signal to be cleared. This will do for now. */
	m_maincpu->set_input_line(TLCS900_INT5, CLEAR_LINE);
}


void ngp_state::z80_io(address_map &map)
{
	map(0x0000, 0xffff).w(FUNC(ngp_state::z80_clear_irq));
}


INPUT_CHANGED_MEMBER(ngp_state::power_callback)
{
	if (BIT(m_io_reg[0x33], 2))
		m_maincpu->set_input_line(TLCS900_NMI, (BIT(m_io_power->read(), 0) ? CLEAR_LINE : ASSERT_LINE));
}


static INPUT_PORTS_START(ngp)
	PORT_START("Controls")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP) PORT_NAME("Up")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN) PORT_NAME("Down")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT) PORT_NAME("Left")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_NAME("Right")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Button A")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Button B")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_SELECT) PORT_NAME("Option")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("Power")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_ON) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ngp_state::power_callback), 0)
INPUT_PORTS_END


void ngp_state::vblank_pin_w(int state)
{
	m_maincpu->set_input_line(TLCS900_INT4, state ? ASSERT_LINE : CLEAR_LINE);
}


void ngp_state::hblank_pin_w(int state)
{
	m_maincpu->set_input_line(TLCS900_TIO, state ? ASSERT_LINE : CLEAR_LINE);
}


void ngp_state::tlcs900_porta(offs_t offset, uint8_t data)
{
	int to3 = BIT(data,3);

	if (to3 && !m_old_to3)
		m_z80->set_input_line(0, ASSERT_LINE);

	m_old_to3 = to3;
}


void ngp_state::machine_start()
{
	if (m_cart->exists())
	{
		std::string region_tag;
		uint8_t *cart = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str())->base();

		m_maincpu->space(AS_PROGRAM).install_rom(0x200000, 0x3fffff, cart);
		m_maincpu->space(AS_PROGRAM).install_rom(0x800000, 0x9fffff, cart + 0x200000);

		m_flash_chip[0].data = cart;
		m_flash_chip[0].org_data[0] = m_flash_chip[0].data[0];
		m_flash_chip[0].org_data[1] = m_flash_chip[0].data[1];
		m_flash_chip[0].org_data[2] = m_flash_chip[0].data[2];
		m_flash_chip[0].org_data[3] = m_flash_chip[0].data[3];
		m_flash_chip[0].org_data[4] = m_flash_chip[0].data[0x7c000];
		m_flash_chip[0].org_data[5] = m_flash_chip[0].data[0x7c001];
		m_flash_chip[0].org_data[6] = m_flash_chip[0].data[0x7c002];
		m_flash_chip[0].org_data[7] = m_flash_chip[0].data[0x7c003];
		m_flash_chip[0].org_data[8] = m_flash_chip[0].data[0xfc000];
		m_flash_chip[0].org_data[9] = m_flash_chip[0].data[0xfc001];
		m_flash_chip[0].org_data[10] = m_flash_chip[0].data[0xfc002];
		m_flash_chip[0].org_data[11] = m_flash_chip[0].data[0xfc003];
		m_flash_chip[0].org_data[12] = m_flash_chip[0].data[0x1fc000];
		m_flash_chip[0].org_data[13] = m_flash_chip[0].data[0x1fc001];
		m_flash_chip[0].org_data[14] = m_flash_chip[0].data[0x1fc002];
		m_flash_chip[0].org_data[15] = m_flash_chip[0].data[0x1fc003];

		m_flash_chip[1].data = cart + 0x200000;
		m_flash_chip[1].org_data[0] = m_flash_chip[1].data[0];
		m_flash_chip[1].org_data[1] = m_flash_chip[1].data[1];
		m_flash_chip[1].org_data[2] = m_flash_chip[1].data[2];
		m_flash_chip[1].org_data[3] = m_flash_chip[1].data[3];
		m_flash_chip[1].org_data[4] = m_flash_chip[1].data[0x7c000];
		m_flash_chip[1].org_data[5] = m_flash_chip[1].data[0x7c001];
		m_flash_chip[1].org_data[6] = m_flash_chip[1].data[0x7c002];
		m_flash_chip[1].org_data[7] = m_flash_chip[1].data[0x7c003];
		m_flash_chip[1].org_data[8] = m_flash_chip[1].data[0xfc000];
		m_flash_chip[1].org_data[9] = m_flash_chip[1].data[0xfc001];
		m_flash_chip[1].org_data[10] = m_flash_chip[1].data[0xfc002];
		m_flash_chip[1].org_data[11] = m_flash_chip[1].data[0xfc003];
		m_flash_chip[1].org_data[12] = m_flash_chip[1].data[0x1fc000];
		m_flash_chip[1].org_data[13] = m_flash_chip[1].data[0x1fc001];
		m_flash_chip[1].org_data[14] = m_flash_chip[1].data[0x1fc002];
		m_flash_chip[1].org_data[15] = m_flash_chip[1].data[0x1fc003];
	}
	else
	{
		m_maincpu->space(AS_PROGRAM).unmap_read(0x200000, 0x3fffff);
		m_maincpu->space(AS_PROGRAM).unmap_read(0x800000, 0x9fffff);
	}

	m_seconds_timer = timer_alloc(FUNC(ngp_state::seconds_callback), this);
	m_seconds_timer->adjust(attotime::from_seconds(1), 0, attotime::from_seconds(1));

	save_item(NAME(m_io_reg));
	save_item(NAME(m_old_to3));
	// TODO: check if these are handled correctly...
	save_item(NAME(m_flash_chip[0].present));
	save_item(NAME(m_flash_chip[0].manufacturer_id));
	save_item(NAME(m_flash_chip[0].device_id));
	save_item(NAME(m_flash_chip[0].org_data));
	save_item(NAME(m_flash_chip[0].state));
	save_item(NAME(m_flash_chip[0].command));
	save_item(NAME(m_flash_chip[1].present));
	save_item(NAME(m_flash_chip[1].manufacturer_id));
	save_item(NAME(m_flash_chip[1].device_id));
	save_item(NAME(m_flash_chip[1].org_data));
	save_item(NAME(m_flash_chip[1].state));
	save_item(NAME(m_flash_chip[1].command));
}


void ngp_state::machine_reset()
{
	m_old_to3 = 0;

	m_z80->suspend(SUSPEND_REASON_HALT, 1);
	m_z80->set_input_line(0, CLEAR_LINE);

	if (m_nvram_loaded)
	{
		m_maincpu->set_state_int(TLCS900_PC, 0xFF1800);
	}
}


DEVICE_IMAGE_LOAD_MEMBER(ngp_state::load_ngp_cart)
{
	uint32_t size = m_cart->common_get_size("rom");

	if (size != 0x8000 && size != 0x8'0000 && size != 0x10'0000 && size != 0x20'0000 && size != 0x40'0000)
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported cartridge size (must be 32K, 512K, 1M, 2M or 4M)");

	// alloc 0x400000 ROM to simplify mapping in the address map
	m_cart->rom_alloc(0x400000, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	//printf("%2x%2x - %x - %x\n", (unsigned int) memregion("cart")->u8(0x20), (unsigned int) memregion("cart")->u8(0x21),
	//        (unsigned int) memregion("cart")->u8(0x22), (unsigned int) memregion("cart")->u8(0x23));
	m_flash_chip[0].manufacturer_id = 0x98;
	m_flash_chip[0].present = true;
	m_flash_chip[0].state = F_READ;

	switch (size)
	{
	case 0x8000:
	case 0x80000:
		m_flash_chip[0].device_id = 0xab;
		break;
	case 0x100000:
		m_flash_chip[0].device_id = 0x2c;
		break;
	case 0x200000:
	case 0x400000:
		m_flash_chip[0].device_id = 0x2f;
		break;
	}

	if (size == 0x400000)
	{
		m_flash_chip[1].manufacturer_id = 0x98;
		m_flash_chip[1].device_id = 0x2f;
		m_flash_chip[1].present = true;
		m_flash_chip[1].state = F_READ;
	}

	return std::make_pair(std::error_condition(), std::string());
}


DEVICE_IMAGE_UNLOAD_MEMBER(ngp_state::unload_ngp_cart)
{
	m_flash_chip[0].present = false;
	m_flash_chip[0].state = F_READ;

	m_flash_chip[1].present = false;
	m_flash_chip[1].state = F_READ;
}


void ngp_state::nvram_default()
{
}


bool ngp_state::nvram_read(util::read_stream &file)
{
	u8 data[0x3000 + 0x20];

	auto const [err, actual] = read(file, data, 0x3000 + 0x20);
	if (!err && (actual == 0x3000 + 0x20))
	{
		for (int i = 0; i < 0x3000; i++)
			m_mainram[i] = data[i];
		for (int i = 0; i < 0x20; i++)
			m_io_reg[i] = data[0x3000 + i];

		system_time curtime;
		machine().current_datetime(curtime);
		set_current_time(curtime);

		m_nvram_loaded = true;
		return true;
	}
	return false;
}


bool ngp_state::nvram_write(util::write_stream &file)
{
	u8 data[0x3000 + 0x20];

	for (int i = 0; i < 0x3000; i++)
		data[i] = m_mainram[i];
	for (int i = 0; i < 0x20; i++)
		data[0x3000 + i] = m_io_reg[i];

	auto const [err, actual] = write(file, data, 0x3000 + 0x20);
	return !err;
}


void ngp_state::rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second)
{
	m_io_reg[0x16] = convert_to_bcd(second);
	m_io_reg[0x15] = convert_to_bcd(minute);
	m_io_reg[0x14] = convert_to_bcd(hour);
	m_io_reg[0x13] = convert_to_bcd(day);
	m_io_reg[0x12] = convert_to_bcd(month);
	m_io_reg[0x11] = convert_to_bcd(year % 100);
}


void ngp_state::ngp_common(machine_config &config)
{
	TMP95C061(config, m_maincpu, 6.144_MHz_XTAL);
	m_maincpu->set_am8_16(1);
	m_maincpu->set_addrmap(AS_PROGRAM, &ngp_state::main_mem);
	m_maincpu->porta_write().set(FUNC(ngp_state::tlcs900_porta));
	m_maincpu->an_read<0>().set_constant(0x3ff); // main battery power

	z80_device &soundcpu(Z80(config, "soundcpu", 6.144_MHz_XTAL/2));
	soundcpu.set_addrmap(AS_PROGRAM, &ngp_state::z80_mem);
	soundcpu.set_addrmap(AS_IO, &ngp_state::z80_io);

	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_raw(6.144_MHz_XTAL, 515, 0, 160 /*480*/, 199, 0, 152);

	/* sound hardware */
	SPEAKER(config, "speaker", 2).front();

	T6W28(config, m_t6w28, 6.144_MHz_XTAL/2);
	m_t6w28->add_route(0, "speaker", 0.50, 0);
	m_t6w28->add_route(1, "speaker", 0.50, 1);

	DAC_8BIT_R2R(config, m_ldac, 0).add_route(ALL_OUTPUTS, "speaker", 0.25, 0); // unknown DAC
	DAC_8BIT_R2R(config, m_rdac, 0).add_route(ALL_OUTPUTS, "speaker", 0.25, 1); // unknown DAC
}


void ngp_state::ngp(machine_config &config)
{
	ngp_common(config);

	K1GE(config, m_k1ge, 6.144_MHz_XTAL, m_screen);
	m_k1ge->vblank_callback().set(FUNC(ngp_state::vblank_pin_w));
	m_k1ge->hblank_callback().set(FUNC(ngp_state::hblank_pin_w));

	m_screen->set_screen_update(m_k1ge, FUNC(k1ge_device::screen_update));
	m_screen->set_palette(m_k1ge);

	generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "ngp_cart", "bin,ngp,npc,ngc"));
	cartslot.set_device_load(FUNC(ngp_state::load_ngp_cart));
	cartslot.set_device_unload(FUNC(ngp_state::unload_ngp_cart));

	SOFTWARE_LIST(config, "cart_list").set_original("ngp");
	SOFTWARE_LIST(config, "ngpc_list").set_compatible("ngpc");
}


void ngp_state::ngpc(machine_config &config)
{
	ngp_common(config);
	K2GE(config, m_k1ge, 6.144_MHz_XTAL, m_screen);
	m_k1ge->vblank_callback().set(FUNC(ngp_state::vblank_pin_w));
	m_k1ge->hblank_callback().set(FUNC(ngp_state::hblank_pin_w));

	m_screen->set_screen_update(m_k1ge, FUNC(k1ge_device::screen_update));
	m_screen->set_palette(m_k1ge);

	generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "ngp_cart", "bin,ngp,npc,ngc"));
	cartslot.set_device_load(FUNC(ngp_state::load_ngp_cart));
	cartslot.set_device_unload(FUNC(ngp_state::unload_ngp_cart));

	SOFTWARE_LIST(config, "cart_list").set_original("ngpc");
	SOFTWARE_LIST(config, "ngp_list").set_compatible("ngp");
}


ROM_START(ngp)
	ROM_REGION(0x10000, "maincpu" , 0)
	ROM_LOAD("ngp_bios.ngp", 0x0000, 0x10000, CRC(6232df8d) SHA1(2f6429b68446536d8b03f35d02f1e98beb6460a0))
ROM_END


ROM_START(ngpc)
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD("ngpcbios.rom", 0x0000, 0x10000, CRC(6eeb6f40) SHA1(edc13192054a59be49c6d55f83b70e2510968e86))
ROM_END

} // anonymous namespace


//   YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  STATE      INIT        COMPANY  FULLNAME               FLAGS
CONS(1998, ngp,  0,      0,      ngp,     ngp,   ngp_state, empty_init, "SNK",   "NeoGeo Pocket",       MACHINE_SUPPORTS_SAVE)
CONS(1999, ngpc, ngp,    0,      ngpc,    ngp,   ngp_state, empty_init, "SNK",   "NeoGeo Pocket Color", MACHINE_SUPPORTS_SAVE)



nokia_3310.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/*
    Driver for Nokia phones based on Texas Instrument MAD2WD1 (ARM7TDMI + DSP)

    Driver based on documentations found here:
        http://nokix.sourceforge.net/help/blacksphere/sub_050main.htm
        http://tudor.rdslink.ro/MADos/

*/

// if anybody has solid information to aid in the emulation of this (or other phones) please contribute.

#include "emu.h"

#include "cpu/arm7/arm7.h"
#include "machine/intelfsh.h"
#include "video/pcd8544.h"

#include "debugger.h"
#include "emupal.h"
#include "screen.h"

#define LOG_MAD2_REGISTER_ACCESS    (1U << 1)
#define LOG_CCONT_REGISTER_ACCESS   (1U << 2)

#define VERBOSE (0)
#include "logmacro.h"


namespace {

class noki3310_state : public driver_device
{
public:
	noki3310_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_pcd8544(*this, "pcd8544"),
		m_keypad(*this, "COL.%u", 0),
		m_pwr(*this, "PWR")
	{ }

	void noki3330(machine_config &config);
	void noki3410(machine_config &config);
	void noki7110(machine_config &config);
	void noki6210(machine_config &config);
	void noki3310(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(key_irq);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	PCD8544_SCREEN_UPDATE(pcd8544_screen_update);

	uint8_t mad2_io_r(offs_t offset);
	void mad2_io_w(offs_t offset, uint8_t data);
	uint8_t mad2_dspif_r(offs_t offset);
	void mad2_dspif_w(offs_t offset, uint8_t data);
	uint8_t mad2_mcuif_r(offs_t offset);
	void mad2_mcuif_w(offs_t offset, uint8_t data);

	TIMER_CALLBACK_MEMBER(timer0);
	TIMER_CALLBACK_MEMBER(timer1);
	TIMER_CALLBACK_MEMBER(timer_watchdog);
	TIMER_CALLBACK_MEMBER(timer_fiq8);

	uint16_t ram_r(offs_t offset, uint16_t mem_mask = ~0) { return m_ram[offset] & mem_mask; }
	void ram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) { COMBINE_DATA(&m_ram[offset]); }
	uint16_t dsp_ram_r(offs_t offset);
	void dsp_ram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

	void noki3310_map(address_map &map) ATTR_COLD;

	void assert_fiq(int num);
	void assert_irq(int num);
	void ack_fiq(uint16_t mask);
	void ack_irq(uint16_t mask);
	void nokia_ccont_w(uint8_t data);
	uint8_t nokia_ccont_r();

	required_device<cpu_device> m_maincpu;
	required_device<pcd8544_device> m_pcd8544;
	required_ioport_array<5> m_keypad;
	required_ioport m_pwr;

	std::unique_ptr<uint16_t[]>   m_ram;
	std::unique_ptr<uint16_t[]>   m_dsp_ram;

	uint8_t       m_power_on;
	uint16_t      m_fiq_status;
	uint16_t      m_irq_status;
	uint16_t      m_timer1_counter;
	uint16_t      m_timer0_counter;

	emu_timer * m_timer0;
	emu_timer * m_timer1;
	emu_timer * m_timer_watchdog;
	emu_timer * m_timer_fiq8;

	// CCONT
	struct nokia_ccont
	{
		bool    dc;
		uint8_t   cmd;
		uint8_t   watchdog;
		uint8_t   regs[0x10];
	} m_ccont;

	uint8_t       m_mad2_regs[0x100];
};


static const char * nokia_mad2_reg_desc(uint8_t offset)
{
	switch(offset)
	{
	case 0x00:  return "[CTSI] DCT3 ASIC version Primary hardware version (r)";
	case 0x01:  return "[CTSI] MCU reset control register (rw)";
	case 0x02:  return "[CTSI] DSP reset control register (rw)";
	case 0x03:  return "[CTSI] ASIC watchdog write register (w)";
	case 0x04:  return "[CTSI] Sleep clock counter (MSB) (r)";
	case 0x05:  return "[CTSI] Sleep clock counter (LSB) (r)";
	case 0x06:  return "[CTSI] ? (sleep) clock destination (LSB) (r)";
	case 0x07:  return "[CTSI] ? (sleep) clock destination (MSB) (r)";
	case 0x08:  return "[CTSI] FIQ lines active (rw)";
	case 0x09:  return "[CTSI] IRQ lines active (rw)";
	case 0x0A:  return "[CTSI] FIQ lines mask (rw)";
	case 0x0B:  return "[CTSI] IRQ lines mask (rw)";
	case 0x0C:  return "[CTSI] Interrupt control register (rw)";
	case 0x0D:  return "[CTSI] Clock control register (rw)";
	case 0x0E:  return "[CTSI] Interrupt trigger register (r)";
	case 0x0F:  return "[CTSI] Programmable timer clock divider (rw)";
	case 0x10:  return "[CTSI] Programmable timer counter (MSB) (r)";
	case 0x11:  return "[CTSI] Programmable timer counter (LSB) (r)";
	case 0x12:  return "[CTSI] Programmable timer destination (MSB) (rw)";
	case 0x13:  return "[CTSI] Programmable timer destination (LSB) (rw)";
	case 0x15:  return "[PUP] PUP control (rw)";
	case 0x16:  return "[PUP] FIQ 8 (timer?) interrupt control (rw)";
	case 0x18:  return "[PUP] MBUS control (rw)";
	case 0x19:  return "[PUP] MBUS status (rw)";
	case 0x1A:  return "[PUP] MBUS RX/TX (rw)";
	case 0x1B:  return "[PUP] Vibrator (w)";
	case 0x1C:  return "[PUP] Buzzer clock divider (w)";
	case 0x1E:  return "[PUP] Buzzer volume (w)";
	case 0x20:  return "[PUP] McuGenIO signal lines (rw)";
	case 0x22:  return "[PUP] ? (?)";
	case 0x24:  return "[PUP] McuGenIO I/O direction (rw)";
	case 0x28:  return "[UIF/KBGPIO] Keyboard ROW signal lines (rw)";
	case 0x29:  return "[UIF/KBGPIO] Keyboard ROW ?? (rw)";
	case 0x2A:  return "[UIF/KBGPIO] Keyboard COL signal lines (rw)";
	case 0x2B:  return "[UIF/KBGPIO] Keyboard COL ?? (rw)";
	case 0x2C:  return "[UIF/GENSIO] CCont write (w)";
	case 0x2D:  return "[UIF/GENSIO] GENSIO start transaction (w)";
	case 0x2E:  return "[UIF/GENSIO] LCD data write (w)";
	case 0x32:  return "[UIF] CTRL I/O 2 (rw)";
	case 0x33:  return "[UIF] CTRL I/O 3 (rw)";
	case 0x36:  return "[SIMI] SIM UART TxD (w)";
	case 0x37:  return "[SIMI] SIM UART RxD (r)";
	case 0x38:  return "[SIMI] SIM UART Interrupt Identification (r)";
	case 0x39:  return "[SIMI] SIM Control (rw)";
	case 0x3A:  return "[SIMI] SIM Clock Control (rw)";
	case 0x3B:  return "[SIMI] SIM UART TxD Low Water Mark (?)";
	case 0x3C:  return "[SIMI] SIM UART RxD queue fill (r)";
	case 0x3D:  return "[SIMI] SIM RxD flags (?)";
	case 0x3E:  return "[SIMI] SIM TxD flags (?)";
	case 0x3F:  return "[SIMI] SIM UART TxD queue fill (r)";
	case 0x68:  return "[UIF/KBGPIO] Keyboard ROW ?? 2 (rw)";
	case 0x69:  return "[UIF/KBGPIO] Keyboard ROW interrupt (rw)";
	case 0x6A:  return "[UIF/KBGPIO] Keyboard COL ?? 2 (rw)";
	case 0x6B:  return "[UIF/KBGPIO] Keyboard COL interrupt mask (rw)";
	case 0x6C:  return "[UIF/GENSIO] CCont read (r)";
	case 0x6D:  return "[UIF/GENSIO] GENSIO status (r)";
	case 0x6E:  return "[UIF/GENSIO] LCD command write (w)";
	case 0x6F:  return "[UIF/GENSIO] GENSIO ?? (3/SELECT1) (?)";
	case 0x70:  return "[UIF] CTRL I/O 0 I/O direction (1) (rw)";
	case 0x71:  return "[UIF] CTRL I/O 1 I/O direction (1) (rw)";
	case 0x72:  return "[UIF] CTRL I/O 2 I/O direction (1) (rw)";
	case 0x73:  return "[UIF] CTRL I/O 3 I/O direction (1) (rw)";
	case 0xA8:  return "[UIF/KBGPIO] Keyboard ROW I/O direction (rw)";
	case 0xA9:  return "[UIF/KBGPIO] Keyboard ROW ?? 3 (rw)";
	case 0xAA:  return "[UIF/KBGPIO] Keyboard COL I/O direction 0=in 1=out (rw)";
	case 0xAB:  return "[UIF/KBGPIO] Keyboard COL ?? 3 (rw)";
	case 0xAD:  return "[UIF/GENSIO] GENSIO ?? (1/SELECT2) (?)";
	case 0xAE:  return "[UIF/GENSIO] GENSIO ?? (2/SELECT2) (?)";
	case 0xAF:  return "[UIF/GENSIO] GENSIO ?? (3/SELECT2) (?)";
	case 0xB0:  return "[UIF] CTRL I/O 0 I/O direction (2) (rw)";
	case 0xB1:  return "[UIF] CTRL I/O 1 I/O direction (2) (rw)";
	case 0xB2:  return "[UIF] CTRL I/O 2 I/O direction (2) (rw)";
	case 0xB3:  return "[UIF] CTRL I/O 3 I/O direction (2) (rw)";
	case 0xED:  return "[UIF/GENSIO] GENSIO ?? (1/SELECT3) (?)";
	case 0xEE:  return "[UIF/GENSIO] GENSIO ?? (2/SELECT3) (?)";
	case 0xEF:  return "[UIF/GENSIO] GENSIO ?? (3/SELECT3) (?)";
	case 0xF0:  return "[UIF] CTRL I/O 0 input (r)";
	case 0xF1:  return "[UIF] CTRL I/O 1 input (r)";
	case 0xF2:  return "[UIF] CTRL I/O 2 input (r)";
	case 0xF3:  return "[UIF] CTRL I/O 3 input (r)";
	default:    return "<Unknown>";
	}
}

static const char * nokia_ccont_reg_desc(uint8_t offset)
{
	switch(offset)
	{
	case 0x0:   return "Control register (w)";
	case 0x1:   return "PWM (charger) (w)";
	case 0x2:   return "A/D read (LSB) (r)";
	case 0x3:   return "A/D read (MSB) (rw)";
	case 0x4:   return "?";
	case 0x5:   return "Watchdog (WDReg) (w)";
	case 0x6:   return "RTC enabled (w)";
	case 0x7:   return "RTC second (rw)";
	case 0x8:   return "RTC minute (r)";
	case 0x9:   return "RTC hour (r)";
	case 0xA:   return "RTC day (rw)";
	case 0xB:   return "RTC alarm minute (rw)";
	case 0xC:   return "RTC alarm hour (rw)";
	case 0xD:   return "RTC calibration value (rw)";
	case 0xE:   return "Interrupt lines (rw)";
	case 0xF:   return "Interrupt mask (rw)";
	default:    return "<Unknown>";
	}
}

void noki3310_state::machine_start()
{
	m_ram = std::make_unique<uint16_t[]>(0x40000);
	m_dsp_ram = std::make_unique<uint16_t[]>(0x800);      // DSP shared RAM

	// allocate timers
	m_timer0 = timer_alloc(FUNC(noki3310_state::timer0), this);
	m_timer1 = timer_alloc(FUNC(noki3310_state::timer1), this);
	m_timer_watchdog = timer_alloc(FUNC(noki3310_state::timer_watchdog), this);
	m_timer_fiq8 = timer_alloc(FUNC(noki3310_state::timer_fiq8), this);
}

void noki3310_state::machine_reset()
{
	// according to the boot rom disassembly here http://www.nokix.pasjagsm.pl/help/blacksphere/sub_100hardware/sub_arm/sub_bootrom.htm
	// flash entry point is at 0x200040, we can probably reassemble the above code, but for now this should be enough.
	m_maincpu->set_state_int(arm7_cpu_device::ARM7_R15, 0x200040);

	memset(m_mad2_regs, 0, 0x100);
	m_mad2_regs[0x01] = 0x01;   // power-on flag
	m_mad2_regs[0x0c] = 0x0a;   // disable FIQ and IRQ
	m_mad2_regs[0x03] = 0xff;   // disable MAD2 watchdog
	m_ccont.watchdog  = 0;      // disable CCONT watchdog
	m_ccont.dc  = false;

	m_fiq_status = 0;
	m_irq_status = 0;
	m_timer1_counter = 0;
	m_timer0_counter = 0;

	m_timer0->adjust(attotime::from_hz(33055 / (255 + 1)), 0, attotime::from_hz(33055 / (255 + 1)));    // programmable through port 0x0f
	m_timer1->adjust(attotime::from_hz(1057), 0, attotime::from_hz(1057));
	m_timer_watchdog->adjust(attotime::from_hz(1), 0, attotime::from_hz(1));
	m_timer_fiq8->adjust(attotime::from_hz(1000), 0, attotime::from_hz(1000));

	// simulate power-on input
	if (machine().system().name[4] == '8' || machine().system().name[4] == '5')
		m_power_on = ~0x10;
	else
		m_power_on = ~0x02;
}

void noki3310_state::assert_fiq(int num)
{
	if ((m_mad2_regs[0x0c] & 0x01) == 0)    // check if FIQ is globally enabled
		return;

	if (num < 8)
	{
		int mask = 1 << num;
		if (!(m_mad2_regs[0x0a] & mask))
		{
			m_maincpu->set_input_line(1, ASSERT_LINE);
			m_fiq_status |= mask;
		}
	}
	else if (!(m_mad2_regs[0x16] & 0x04))
	{
		m_fiq_status |= 0x100;
		m_maincpu->set_input_line(1, ASSERT_LINE);
	}
}

void noki3310_state::assert_irq(int num)
{
	if ((m_mad2_regs[0x0c] & 0x04) == 0)    // check if IRQ is globally enabled
		return;

	if (num < 8)
	{
		int mask = 1 << num;
		if (!(m_mad2_regs[0x0b] & mask))
		{
			m_irq_status |= mask;
			m_maincpu->set_input_line(0, ASSERT_LINE);
		}
	}
	else if (!(m_mad2_regs[0x0c] & 0x40))
	{
		m_irq_status |= 0x100;
		m_maincpu->set_input_line(0, ASSERT_LINE);
	}
}

void noki3310_state::ack_fiq(uint16_t mask)
{
	m_fiq_status &= ~mask;

	if (m_fiq_status == 0)
		m_maincpu->set_input_line(1, CLEAR_LINE);
}

void noki3310_state::ack_irq(uint16_t mask)
{
	m_irq_status &= ~mask;

	if (m_irq_status == 0)
		m_maincpu->set_input_line(0, CLEAR_LINE);
}

void noki3310_state::nokia_ccont_w(uint8_t data)
{
	if (m_ccont.dc == false)
	{
		LOGMASKED(LOG_CCONT_REGISTER_ACCESS, "CCONT command %s %x\n", data & 4 ? "R" : "W", data>>3);
		m_ccont.cmd  = data;
	}
	else
	{
		uint8_t addr = (m_ccont.cmd >> 3) & 0x0f;

		switch(addr)
		{
			case 0x0:   // ADC
			{
				uint16_t ad_id = (data >> 4) & 0x07;
				uint16_t ad_value = 0;
				switch(ad_id)
				{
					case 0:     ad_value = 0x000;   break;  // Accessory Detect
					case 1:     ad_value = 0x3ff;   break;  // Received signal strength
					case 2:     ad_value = 0x3ff;   break;  // Battery voltage
					case 3:     ad_value = 0x280;   break;  // Battery type
					case 4:     ad_value = 0x000;   break;  // Battery temperature
					case 5:     ad_value = 0x000;   break;  // Charger voltage
					case 6:     ad_value = 0x000;   break;  // VCX0 (Voltage controlled oscilator) temperature
					case 7:     ad_value = 0x000;   break;  // Charging current
				}

				m_ccont.regs[addr] = data;
				m_ccont.regs[2] = ad_value & 0xff;
				m_ccont.regs[3] = ((ad_value >> 8) & 0x03);
				break;
			}
			case 0x5:   // CCONT watchdog
				if (data == 0x20)
					m_ccont.regs[addr] = data;
				else if (data == 0x31)
					m_ccont.watchdog = m_ccont.regs[addr];
				else if (data == 0x3f)
					m_ccont.watchdog = 0;
				else if (data == 0)
					printf("CCONT power-off\n");
				break;

			default:
				m_ccont.regs[addr] = data;
				break;
		}

		LOGMASKED(LOG_CCONT_REGISTER_ACCESS, "CCONT W %02x = %02x %s\n", addr, data, nokia_ccont_reg_desc(addr));
	}

	m_ccont.dc = !m_ccont.dc;
}

uint8_t noki3310_state::nokia_ccont_r()
{
	uint8_t addr = (m_ccont.cmd >> 3) & 0x0f;
	uint8_t data = m_ccont.regs[addr];

	system_time systime;
	machine().current_datetime(systime);

	switch(addr)
	{
		case 0x3:       data = 0xb0 | (m_ccont.regs[addr] & 0x03);  break;
		case 0x7:       data = systime.local_time.second;           break;
		case 0x8:       data = systime.local_time.minute;           break;
		case 0x9:       data = systime.local_time.hour;             break;
		case 0xa:       data = systime.local_time.mday;             break;
		case 0xe:       data |= 0x01;                               break;
	}

	m_ccont.dc = !m_ccont.dc;

	LOGMASKED(LOG_CCONT_REGISTER_ACCESS, "CCONT R %02x = %02x %s\n", addr, data, nokia_ccont_reg_desc(addr));
	return data;
}

PCD8544_SCREEN_UPDATE(noki3310_state::pcd8544_screen_update)
{
	for (int r = 0; r < 6; r++)
		for (int x = 0; x < 84; x++)
		{
			uint8_t gfx = vram[r*84 + x];

			for (int y = 0; y < 8; y++)
			{
				int p = BIT(gfx, y);
				bitmap.pix(r*8 + y, x) = p ^ inv;
			}
		}
}

TIMER_CALLBACK_MEMBER(noki3310_state::timer0)
{
	m_timer0_counter++;

	if (m_timer0_counter == ((m_mad2_regs[0x12] << 8) | m_mad2_regs[0x13]))
		assert_fiq(4);
}

TIMER_CALLBACK_MEMBER(noki3310_state::timer1)
{
	m_timer1_counter++;

	if (m_timer1_counter == 0x8000)
	{
		assert_fiq(5);
		m_timer1_counter = 0;
	}
}

TIMER_CALLBACK_MEMBER(noki3310_state::timer_fiq8)
{
	if (m_mad2_regs[0x16] & 0x01)
		assert_fiq(8);
}

TIMER_CALLBACK_MEMBER(noki3310_state::timer_watchdog)
{
	// CCONT watchdog
	if (m_ccont.watchdog != 0)
	{
		m_ccont.watchdog--;

		if (m_ccont.watchdog == 0)
		{
			m_maincpu->reset();
			machine_reset();
		}
	}

	// MAD2 watchdog
	if (m_mad2_regs[0x03] != 0xff)
	{
		m_mad2_regs[0x03]--;
		if (m_mad2_regs[0x03] == 0)
		{
			m_maincpu->reset();
			machine_reset();
			m_mad2_regs[0x01] |= 0x02;  // Last reset was by watchdog
		}
	}
}

uint16_t noki3310_state::dsp_ram_r(offs_t offset)
{
	// HACK: avoid hangs when ARM try to communicate with the DSP
	if (offset <= 0x004 >> 1)   return 0x01;
	if (offset == 0x0e0 >> 1)   return 0x00;
	if (offset == 0x0fe >> 1)   return 0x01;
	if (offset == 0x100 >> 1)   return 0x01;

	return m_dsp_ram[offset & 0x7ff];
}

void noki3310_state::dsp_ram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	COMBINE_DATA(&m_dsp_ram[offset & 0x7ff]);
}

uint8_t noki3310_state::mad2_io_r(offs_t offset)
{
	uint8_t data = m_mad2_regs[offset];

	switch(offset)
	{
		case 0x00:
			data = 0x40;    // ASIC version
			break;
		case 0x04:
			data = m_timer1_counter >> 8;
			break;
		case 0x05:
			data = m_timer1_counter;
			break;
		case 0x08:
			data = m_fiq_status & 0xff;
			break;
		case 0x09:
			data = m_irq_status & 0xff;
			break;
		case 0x0c:
			data = (data & (~0x20)) | ((m_irq_status >> 3) & 0x20);
			break;
		case 0x10:
			data = m_timer0_counter >> 8;
			break;
		case 0x11:
			data = m_timer0_counter;
			break;
		case 0x16:
			data = (data & (~0x02)) | ((m_fiq_status >> 7) & 0x02);
			break;
		case 0x18:
			data &= 0x7f;
			break;
		case 0x2a:
			data = 0xff;
			for(int i=0; i<5; i++)
				if (!(m_mad2_regs[0x28] & (1 <<i)))
					data &= m_keypad[i]->read();

			data &= m_pwr->read();

			if (m_power_on)
			{
				data &= m_power_on;
				m_power_on = 0;
			}
			break;
		case 0x6c:
			data = nokia_ccont_r();
			break;
		case 0x6d:
			data = 0x07;    // GENSIO ready
			break;
	}

	LOGMASKED(LOG_MAD2_REGISTER_ACCESS, "MAD2 R %02x = %02x %s\n", offset, data, nokia_mad2_reg_desc(offset));
	return data;
}

void noki3310_state::mad2_io_w(offs_t offset, uint8_t data)
{
	m_mad2_regs[offset] = data;

	switch(offset)
	{
		case 0x02:
			//printf("DSP %s\n", data & 1 ? "RUN" : "HOLD");
			//if (data & 0x01)  machine().debug_break();
			break;
		case 0x08:
			ack_fiq(data);
			break;
		case 0x09:
			ack_irq(data);
			break;
		case 0x0c:
			ack_irq((data << 3) & 0x100);
			break;
		case 0x0f:
			m_timer0->adjust(attotime::from_hz(33055 / (data + 1)), 0, attotime::from_hz(33055 / (data + 1)));
			break;
		case 0x16:
			ack_fiq((data << 7) & 0x100);
			break;
		case 0x2c:
			nokia_ccont_w(data);
			break;
		case 0x2e:
		case 0x6e:
			m_pcd8544->dc_w(offset & 0x40 ? CLEAR_LINE : ASSERT_LINE);
			for (int i=7; i>=0; i--)
			{
				m_pcd8544->sclk_w(CLEAR_LINE);
				m_pcd8544->sdin_w(BIT(data, i));
				m_pcd8544->sclk_w(ASSERT_LINE);
			}
			m_pcd8544->dc_w(ASSERT_LINE);
			break;
	}

	LOGMASKED(LOG_MAD2_REGISTER_ACCESS, "MAD2 W %02x = %02x %s\n", offset, data, nokia_mad2_reg_desc(offset));
}

uint8_t noki3310_state::mad2_dspif_r(offs_t offset)
{
	LOGMASKED(LOG_MAD2_REGISTER_ACCESS, "MAD2 R %02x DSPIF\n", offset);
	return 0;
}

void noki3310_state::mad2_dspif_w(offs_t offset, uint8_t data)
{
	LOGMASKED(LOG_MAD2_REGISTER_ACCESS, "MAD2 W %02x = %02x DSPIF\n", offset, data);
}

uint8_t noki3310_state::mad2_mcuif_r(offs_t offset)
{
	LOGMASKED(LOG_MAD2_REGISTER_ACCESS, "MAD2 R %02x MCUIF\n", offset);
	return 0;
}

void noki3310_state::mad2_mcuif_w(offs_t offset, uint8_t data)
{
	LOGMASKED(LOG_MAD2_REGISTER_ACCESS, "MAD2 W %02x = %02x MCUIF\n", offset, data);
}


void noki3310_state::noki3310_map(address_map &map)
{
	map.global_mask(0x00ffffff);
	map(0x00000000, 0x0000ffff).mirror(0x80000).rw(FUNC(noki3310_state::ram_r), FUNC(noki3310_state::ram_w));                // boot ROM / RAM
	map(0x00010000, 0x00010fff).mirror(0x8f000).rw(FUNC(noki3310_state::dsp_ram_r), FUNC(noki3310_state::dsp_ram_w));        // DSP shared memory
	map(0x00020000, 0x000200ff).mirror(0x8ff00).rw(FUNC(noki3310_state::mad2_io_r), FUNC(noki3310_state::mad2_io_w));         // IO (Primary I/O range, configures peripherals)
	map(0x00030000, 0x00030003).mirror(0x8fffc).rw(FUNC(noki3310_state::mad2_dspif_r), FUNC(noki3310_state::mad2_dspif_w));   // DSPIF (API control register)
	map(0x00040000, 0x00040003).mirror(0x8fffc).rw(FUNC(noki3310_state::mad2_mcuif_r), FUNC(noki3310_state::mad2_mcuif_w));   // MCUIF (Secondary I/O range, configures memory ranges)
	map(0x00100000, 0x0017ffff).rw(FUNC(noki3310_state::ram_r), FUNC(noki3310_state::ram_w));                                   // RAMSelX
	map(0x00200000, 0x005fffff).rw("flash", FUNC(intelfsh16_device::read), FUNC(intelfsh16_device::write));     // ROM1SelX
	map(0x00600000, 0x009fffff).unmaprw();                                                                   // ROM2SelX
	map(0x00a00000, 0x00dfffff).unmaprw();                                                                   // EEPROMSelX
	map(0x00e00000, 0x00ffffff).unmaprw();                                                                   // Reserved
}


INPUT_CHANGED_MEMBER( noki3310_state::key_irq )
{
	if (!newval)    // TODO: COL/ROW IRQ mask
		assert_irq(0);
}

static INPUT_PORTS_START( noki3310 )
	PORT_START("COL.0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_UP)       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(noki3310_state::key_irq), 0)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_0)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(noki3310_state::key_irq), 0)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL)      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(noki3310_state::key_irq), 0)

	PORT_START("COL.1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_DOWN)     PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(noki3310_state::key_irq), 0)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(noki3310_state::key_irq), 0)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(noki3310_state::key_irq), 0)

	PORT_START("COL.2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(noki3310_state::key_irq), 0)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(noki3310_state::key_irq), 0)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(noki3310_state::key_irq), 0)

	PORT_START("COL.3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_9)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(noki3310_state::key_irq), 0)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(noki3310_state::key_irq), 0)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(noki3310_state::key_irq), 0)

	PORT_START("COL.4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3)        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(noki3310_state::key_irq), 0)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(noki3310_state::key_irq), 0)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(noki3310_state::key_irq), 0)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(noki3310_state::key_irq), 0)

	PORT_START("PWR")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_SPACE)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(noki3310_state::key_irq), 0)
	PORT_BIT( 0x1d, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END


void noki3310_state::noki3310(machine_config &config)
{
	/* basic machine hardware */
	ARM7_BE(config, m_maincpu, 26000000 / 2);  // MAD2WD1 13 MHz, clock internally supplied to ARM core can be divided by 2, in sleep mode a 32768 Hz clock is used
	m_maincpu->set_addrmap(AS_PROGRAM, &noki3310_state::noki3310_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD, rgb_t::white()));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500) /* not accurate */);
	screen.set_size(84, 48);
	screen.set_visarea(0, 84-1, 0, 48-1);
	screen.set_screen_update("pcd8544", FUNC(pcd8544_device::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	PCD8544(config, m_pcd8544, 0);
	m_pcd8544->set_screen_update_cb(FUNC(noki3310_state::pcd8544_screen_update));

	INTEL_TE28F160(config, "flash");
}

void noki3310_state::noki3330(machine_config &config)
{
	noki3310(config);

	INTEL_TE28F320(config.replace(), "flash");
}

void noki3310_state::noki3410(machine_config &config)
{
	noki3330(config);

	subdevice<screen_device>("screen")->set_size(96, 65);    // Philips OM6206
}

void noki3310_state::noki7110(machine_config &config)
{
	noki3330(config);

	subdevice<screen_device>("screen")->set_size(96, 65);    // Epson SED1565
}

void noki3310_state::noki6210(machine_config &config)
{
	noki3330(config);

	subdevice<screen_device>("screen")->set_size(96, 60);
}


// MAD2 internal ROMS
#define MAD2_INTERNAL_ROMS \
	ROM_REGION16_BE(0x10000, "boot_rom", ROMREGION_ERASE00 )    \
	ROM_LOAD("boot_rom", 0x00000, 0x10000, NO_DUMP)             \
																\
	ROM_REGION16_BE(0x20000, "dsp", ROMREGION_ERASE00 )         \
	ROM_LOAD("dsp_prom" , 0x00000, 0xc000, NO_DUMP)             \
	ROM_LOAD("dsp_drom" , 0x0c000, 0x4000, NO_DUMP)             \
	ROM_LOAD("dsp_pdrom", 0x10000, 0x1000, NO_DUMP)

ROM_START( noki3210 )
	MAD2_INTERNAL_ROMS

	ROM_REGION16_BE(0x200000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "600", "v6.00")  // A 03-10-2000
	ROMX_LOAD("3210f600a.fls", 0x000000, 0x200000, CRC(6a978478) SHA1(6bdec2ec76aca15bc12b621be4402e455562454b), ROM_BIOS(0))

	ROM_REGION16_BE(0x04000, "eeprom", 0 )
	ROM_LOAD("3210 virgin eeprom,24c128.bin", 0x00000, 0x04000, CRC(af8d8f65) SHA1(33a24c04d81a2bd8abce4a6fd873029f0c633ecb))
ROM_END

ROM_START( noki3310 )
	MAD2_INTERNAL_ROMS

	ROM_REGION16_BE(0x200000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "607", "v6.07")  // C 17-06-2003
	ROM_SYSTEM_BIOS(1, "579", "v5.79")  // N 11-11-2002
	ROM_SYSTEM_BIOS(2, "513", "v5.13")  // C 11-01-2002
	ROMX_LOAD("3310_607_ppm_c.fls", 0x000000, 0x200000, CRC(5743f6ba) SHA1(0e80b5f1698909c9850be770c1289566582aa77a), ROM_BIOS(0))
	ROMX_LOAD("3310 nr1 v5.79.fls", 0x000000, 0x200000, CRC(26b4f0df) SHA1(649de05ed88205a080693b918cd1295ac691dff1), ROM_BIOS(1))
	ROMX_LOAD("3310 v. 5.13 c.fls", 0x000000, 0x1d0000, CRC(0f66d256) SHA1(04d8dabe2c454d6a1161f352d85c69c409895000), ROM_BIOS(2))
	ROM_LOAD("3310 virgin eeprom 003d0000.fls", 0x1d0000, 0x030000, CRC(8393b1f7) SHA1(ab6c05bfa54ecd7c2acbd172009ffe6c7f130cb8))

	// these 2 are apparently the 6.39 update firmware data
	ROM_REGION(0x0200000, "misc", 0 )
	ROM_LOAD( "nhm5ny06.390",   0x000000, 0x0131161, CRC(5dfb1af7) SHA1(3a8ad82dc239b0cd18be60f537c4d0e07881155d) )
	ROM_LOAD( "nhm5ny06.39i",   0x000000, 0x0090288, CRC(ec214ee4) SHA1(f5b3b9ceaa7280d5246dd70d5696f8f6983122fc) )
ROM_END

ROM_START( noki3330 )
	MAD2_INTERNAL_ROMS

	ROM_REGION16_BE(0x0400000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "450", "v4.50")  // C 12-10-2001
	ROMX_LOAD("3330f450c.fls", 0x000000, 0x350000, CRC(259313e7) SHA1(88bcc39d9358fd8a8562fe3a0280f0ce82f5897f), ROM_BIOS(0))
	ROM_LOAD("3330 virgin eeprom 005f0000.fls", 0x3f0000, 0x010000, CRC(23459c10) SHA1(68481effb39d90a1639e8f261009c66e97d3e668))
ROM_END

ROM_START( noki3410 )
	MAD2_INTERNAL_ROMS

	ROM_REGION16_BE(0x0400000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "506", "v5.06")  // C 29-11-2002
	ROMX_LOAD("3410_5-06c.fls", 0x000000, 0x370000, CRC(1483e094) SHA1(ef26026297c779de7b01923a364ded822e720c38), ROM_BIOS(0))
ROM_END

ROM_START( noki5210 )
	MAD2_INTERNAL_ROMS

	ROM_REGION16_BE(0x0400000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "540", "v5.40")  // C 11-10-2003
	ROM_SYSTEM_BIOS(1, "525", "v5.25")  // C 26-02-2003
	ROM_SYSTEM_BIOS(2, "520", "v5.20")  // C 12-08-2002
	ROMX_LOAD("5210_5.40_ppm_c.fls", 0x000000, 0x380000, CRC(e37d5beb) SHA1(726f000780dd67750b7d2859687f846ce17a1bf7), ROM_BIOS(0))
	ROMX_LOAD("5210_5.25_ppm_c.fls", 0x000000, 0x380000, CRC(13bba458) SHA1(3b5244244743fba48f9061e158f95fc46b86446e), ROM_BIOS(1))
	ROMX_LOAD("5210_520_c.fls", 0x000000, 0x380000, CRC(38648cd3) SHA1(9210e15e6bd780f86c467bec33ef54d6393abe5a), ROM_BIOS(2))
ROM_END

ROM_START( noki6210 )
	MAD2_INTERNAL_ROMS

	ROM_REGION16_BE(0x0400000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "556", "v5.56")  // C 25-01-2002
	ROMX_LOAD("6210_556c.fls", 0x000000, 0x3a0000, CRC(203fb962) SHA1(3d9ea319503e78ec69b60d72cda23e461e118ea9), ROM_BIOS(0))
	ROM_LOAD("6210 virgin eeprom 005fa000.fls", 0x3fa000, 0x006000, CRC(3c6d3437) SHA1(b3a527ede1be87bd715fb3741a81eef5bd422efa))
ROM_END

ROM_START( noki6250 )
	MAD2_INTERNAL_ROMS

	ROM_REGION16_BE(0x0400000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "503", "v5.03")  // C 06-12-2001
	ROMX_LOAD("6250-503mcuppmc.fls", 0x000000, 0x3a0000, CRC(8dffb91b) SHA1(95607ce39c383bda75f1e6aeae67a214b787b0a1), ROM_BIOS(0))
	ROM_LOAD("6250 virgin eeprom 005fa000.fls", 0x3fa000, 0x006000, CRC(6087ce70) SHA1(57c29c8387caf864603d94a22bfb63ace427b7f9))
ROM_END

ROM_START( noki7110 )
	MAD2_INTERNAL_ROMS

	ROM_REGION16_BE(0x0400000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "501", "v5.01")  // C 08-12-2000
	ROMX_LOAD("7110f501_ppmc.fls", 0x000000, 0x390000, CRC(919ac753) SHA1(53af8324919f455ba8199d2c05f7a921cfb811d5), ROM_BIOS(0))
	ROM_LOAD("7110 virgin eeprom 005fa000.fls", 0x3fa000, 0x006000, CRC(78e7d8c1) SHA1(8b4dd782fc9d1306268ba63124ee463ac646912b))
ROM_END

ROM_START( noki8210 )
	MAD2_INTERNAL_ROMS

	ROM_REGION16_BE(0x200000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "531", "v5.31")  // C 08-03-2002
	ROMX_LOAD("8210_5.31ppm_c.fls", 0x000000, 0x1d0000, CRC(927022b1) SHA1(c1a0fe95cedb89a92b19654208cc4855e1a4988e), ROM_BIOS(0))
	ROM_LOAD("8210 virgin eeprom 003d0000.fls", 0x1d0000, 0x030000, CRC(37fddeea) SHA1(1c01ad3948ff9919890498a84f31052369d93e1d))
ROM_END

ROM_START( noki8250 )
	MAD2_INTERNAL_ROMS

	ROM_REGION16_BE(0x200000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "502", "v5.02")  // K 28-01-2002
	ROMX_LOAD("8250-502mcuppmk.fls", 0x000000, 0x1d0000, CRC(2c58e48b) SHA1(f26c98ffcfffbbd5714889e10cfa41c5f6dd2529), ROM_BIOS(0))
	ROM_LOAD("8250 virgin eeprom 003d0000.fls", 0x1d0000, 0x030000, CRC(7ca585e0) SHA1(a974fb5fddcd0438ac4aaf32b431f1453e8d923c))
ROM_END

ROM_START( noki8850 )
	MAD2_INTERNAL_ROMS

	ROM_REGION16_BE(0x200000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "531", "v5.31")  // C 08-03-2002
	ROMX_LOAD("8850v531.fls", 0x000000, 0x1d0000, CRC(8864fcb3) SHA1(9f966787403b68a09530680ad911302403eb1521), ROM_BIOS(0))
	ROM_LOAD("8850 virgin eeprom 003d0000.fls", 0x1d0000, 0x030000, CRC(4823f27e) SHA1(b09455302d98fbedf35072c9ecfd7721a04924b0))
ROM_END

ROM_START( noki8890 )
	MAD2_INTERNAL_ROMS

	ROM_REGION16_BE(0x200000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "1220", "v12.20")    // C 19-03-2001
	ROMX_LOAD("8890_12.20_ppmc.fls", 0x000000, 0x1d0000, CRC(77206f78) SHA1(a214a0d69760ecd8eeca0b9d82f95c94bdfe70ed), ROM_BIOS(0))
	ROM_LOAD("8890 virgin eeprom 003d0000.fls", 0x1d0000, 0x030000, CRC(1d8ef3b5) SHA1(cc0924cfd4c0ce796fca157c640fc3183c2b5f2c))
ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY  FULLNAME      FLAGS
SYST( 1999, noki3210, 0,      0,      noki3310, noki3310, noki3310_state, empty_init, "Nokia", "Nokia 3210", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
SYST( 1999, noki7110, 0,      0,      noki7110, noki3310, noki3310_state, empty_init, "Nokia", "Nokia 7110", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
SYST( 1999, noki8210, 0,      0,      noki3310, noki3310, noki3310_state, empty_init, "Nokia", "Nokia 8210", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
SYST( 1999, noki8850, 0,      0,      noki3310, noki3310, noki3310_state, empty_init, "Nokia", "Nokia 8850", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
SYST( 2000, noki3310, 0,      0,      noki3310, noki3310, noki3310_state, empty_init, "Nokia", "Nokia 3310", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
SYST( 2000, noki6210, 0,      0,      noki6210, noki3310, noki3310_state, empty_init, "Nokia", "Nokia 6210", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
SYST( 2000, noki6250, 0,      0,      noki6210, noki3310, noki3310_state, empty_init, "Nokia", "Nokia 6250", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
SYST( 2000, noki8250, 0,      0,      noki3310, noki3310, noki3310_state, empty_init, "Nokia", "Nokia 8250", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
SYST( 2000, noki8890, 0,      0,      noki3310, noki3310, noki3310_state, empty_init, "Nokia", "Nokia 8890", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
SYST( 2001, noki3330, 0,      0,      noki3330, noki3310, noki3310_state, empty_init, "Nokia", "Nokia 3330", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
SYST( 2002, noki3410, 0,      0,      noki3410, noki3310, noki3310_state, empty_init, "Nokia", "Nokia 3410", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
SYST( 2002, noki5210, 0,      0,      noki3330, noki3310, noki3310_state, empty_init, "Nokia", "Nokia 5210", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



nordlead.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/**************************************************************************************************

Skeleton driver for Clavia Nord Lead/Rack series of virtual analog synthesizer.

**************************************************************************************************/

#include "emu.h"

#include "cpu/m68000/tmp68301.h"

#include "emupal.h"
#include "speaker.h"

namespace {

class nordlead_state : public driver_device
{
public:
	nordlead_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void nordle2x(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void host_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};


void nordlead_state::host_map(address_map &map)
{
	map(0x000000, 0x07ffff).rom();
	map(0x100000, 0x13ffff).ram();
//  map(0x200000, 0x200007) both DSPs
//  map(0x200008, 0x20000f) DSP A
//  map(0x200010, 0x200017) DSP B
//  map(0x202000, 0x2027ff) Front Panel CS6
//  map(0x202800, 0x202fff) Front Panel CS4
//  map(0x203000, 0x2037ff) keyboard
}


static INPUT_PORTS_START(nordle2x)
INPUT_PORTS_END

void nordlead_state::machine_start()
{
}

void nordlead_state::machine_reset()
{
}

void nordlead_state::nordle2x(machine_config &config)
{
	// TODO: actually TMP68331
	TMP68301(config, m_maincpu, XTAL(32'000'000) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &nordlead_state::host_map);

	// TODO: 2 x DSP56362, original Nord Lead with stock DSP56002
}


ROM_START(nordle2x)
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_LOAD("nord_lead_2x.bin", 0x00000, 0x80000, CRC(66188eb1) SHA1(549394349f7371e48ead57d8a0ec6b4010014d12))
ROM_END

} // anonymous namespace

//SYST(1995, nordlead, 0, 0, nordlead, nordlead, nordlead_state, empty_init, "Clavia", "Nord Lead", ...)
// ...
SYST(2003, nordle2x, 0, 0, nordle2x,  nordle2x, nordlead_state, empty_init, "Clavia", "Nord Lead 2X", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



notetaker.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Jonathan Gevaryahu
/* Xerox NoteTaker, 1978
 * Driver by Jonathan Gevaryahu

 *  Notetaker Team At Xerox PARC 1976-1981:
     Alan Kay - Team Lead
     Bruce Horn - BIOS code and NoteTaker Hardware/Electronics Design (Ethernet, others)
     Ted Kaehler - SmallTalk-76 code porting[2] and more ( http://tedkaehler.weather-dimensions.com/us/ted/index.html )
     Dan Ingalls - Later BitBlt engine and SmallTalk-76 kernel and more[3]
     Doug Fairbairn - NoteTaker Hardware/Electronics Design (EmulatorP, IOP, ADC, Alto Debug/Test Interface)
         ( http://www.computerhistory.org/atchm/author/dfairbairn/ )
     Ed Wakida - NoteTaker Hardware/Electronics Design (Tablet/Touch interface)
     Bob Nishimura - NoteTaker Hardware/Electronics Design (PSU/Cabling/Machining/Battery)
     James "Jim" Leung - NoteTaker Hardware/Electronics Design (Alto Debug/Test Interface)
     Ron Freeman - NoteTaker Hardware/Electronics Design (Keyboard, Disk/Display)
     Ben Sato - NoteTaker Hardware/Electronics Design (Memory/Timing, ECC)
     B. Wang - NoteTaker Hardware/Electronics Design (Keyboard)
     Larry Tesler - NoteTaker Hardware/Electronics Design (Ethernet, others)
     Dale Mann - NoteTaker Hardware/Electronics Design (Ethernet, Circuit Assembly)
     Lawrence "Larry" D. Stewart - NoteTaker Hardware/Electronics Design (Mouse)
     Jim Althoff - Smalltalk-78 and Smalltalk-80
     Adele Goldberg - Smalltalk team
     Diana Merry-Shapiro - Original BitBlt from Smalltalk-72
     Dave Robson - Smalltalk team
     Ted Strollo - IC/VLSI design (MPC580 cell library)
     Bert Sutherland - Manager of Systems Science Laboratory (SSL)
     Terri Doughty - Administration, Editing
     Chris Jeffers - Smalltalk music
     <there are probably others I've missed>

 * History of the machine can be found at http://freudenbergs.de/bert/publications/Ingalls-2014-Smalltalk78.pdf

 * The notetaker has an 8-slot backplane, with the following cards in it:
   * I/O Processor card (8086@8Mhz, 8259pic, 4k ROM, Keyboard UART, DAC1200 (multiplexed to 2 channels))
   * Emulation Processor card (8086@5Mhz, 8259pic, 8k of local RAM with Parity check logic)
   * Disk/Display card (WD1791 FDC, CRT5027 CRTC, EIA UART, AD571 ADC, 8->1 Analog Multiplexer)
   * Memory Control Module \_ (bus control, buffering, refresh, Parity/ECC/Syndrome logic lives on these boards)
   * Memory Data Module    /
   * Memory Storage Module x2 (the 4116 DRAMs live on these boards)
   * Battery Module *OR* debugger module type A or B (debugger module has an
     i8255 on it for alto<->notetaker comms, and allows alto to halt the cpus
     [type A and B can debug either the emulator cpu or the iop respectively]
     and dump registers to alto screen, etc)

   * In 1980-1981 an Ethernet card with another 8086 on it was developed, but
     it is unclear if this was ever fully functional, or if smalltalk-78
     could even use it.

 * Prototypes only, 10 units[2] manufactured 1978-1980
   Known surviving units:
   * One at CHM (missing? mouse, no media, has BIOP-2.0 roms)
   * One at Xerox Museum at PARC (with mouse and 2? floppies, floppies were not imaged to the best of my knowledge, unknown roms)
   * Rumor has it at least a few of the remaining units survived beyond these two.

 * The NoteTaker used the BitBlt graphical operation (from SmallTalk-76) to do most graphical functions, in order to fit the SmallTalk code and programs within 256K of RAM[2]. The actual BitBlt code lives in ROM[3].

 * As far as I am aware, no media (world disks/boot disks) for the NoteTaker have survived (except maybe the two disks at Xerox Museum at PARC), but an incomplete dump of the Smalltalk-76 'world' which was used to bootstrap Smalltalk-78 originally did survive on the Alto disks at CHM

 * We are missing the dump for the i8748 Keyboard MCU which does row-column scanning and mouse quadrature reading, and talks to the main system via serial

 * see http://bitsavers.informatik.uni-stuttgart.de/pdf/xerox/notetaker for additional information
 * see http://xeroxalto.computerhistory.org/Filene/Smalltalk-76/ for the smalltalk-76 dump
 * see http://xeroxalto.computerhistory.org/Indigo/BasicDisks/Smalltalk14.bfs!1_/ for more notetaker/smalltalk related files, including SmallTalk-80 files based on the notetaker smalltalk-78

 References:
 * [1] http://freudenbergs.de/bert/publications/Ingalls-2014-Smalltalk78.pdf
 * [2] "Smalltalk and Object Orientation: An Introduction" By John Hunt, pages 45-46 [ISBN 978-3-540-76115-0]
 * [3] http://bitsavers.trailing-edge.com/pdf/xerox/notetaker/memos/19790620_Z-IOP_1.5_ls.pdf
 * [4] http://xeroxalto.computerhistory.org/Filene/Smalltalk-76/
 * [5] http://bitsavers.trailing-edge.com/pdf/xerox/notetaker/memos/19790118_NoteTaker_System_Manual.pdf

TODO: everything below.
* Get smalltalk-78 loaded as a rom and forced into ram on startup, since no boot disks have survived (or if any survived, they are not dumped)
* Hack together a functional boot disk using the recovered EP bootloader and smalltalk engine code from the alto disks.
* Harris 6402 keyboard UART (within keyboard, next to MCU)
* The harris 6402 UART is pin compatible with WD TR1865 UART, as well as the AY-3-1015A/D
   (These are 5v-only versions of the older WD TR1602 and AY-5-1013 parts which used 5v and 12v)
* HLE for the missing i8748[5] MCU in the keyboard which reads the mouse quadratures and buttons and talks serially to the Keyboard UART
* floppy controller wd1791 and its interrupt
  According to [3] and [5] the format is double density/MFM, 128 bytes per sector, 16 sectors per track, 1 or 2 sided, for 170K or 340K per disk. Drive spins at 300RPM.
* hook up the DiskInt from the wd1791 either using m_fdc->intrq_r() polling or using device lines (latter is a much better idea)

WIP:
* crt5027 video controller - the iop side is hooked up, screen drawing 'works' but is scrambled due to not emulating the clock chain halting and clock changing yet. The crt5027 core also needs the odd/even interrupt hooked up, and proper interlace support as well as clock change/screen resize support (down to DC/no clock, which I guess should be a 1x1 single black pixel!)
* pic8259 interrupt controller - this is attached as a device, but only the vblank interrupt is hooked to it yet.
* Harris 6402 serial/EIA UART - connected to iop, other end isn't connected anywhere, interrupt is not connected
* Harris 6402 keyboard UART (within notetaker) - connected to iop, other end isn't connected anywhere, interrupt is not connected
* The DAC, its FIFO and its timer are hooked up and the v2.0 bios beeps, but the stereo latches are not hooked up at all, DAC is treated as mono for now

DONE:
* i/o cpu i/o area needs the memory map worked out per the schematics - done
* figure out the correct memory maps for the 256kB of shared ram, and what part of ram constitutes the framebuffer - done
  - 256k of shared ram maps at 00000-3ffff for both cpus with special mem regs at fffec,fffee. the ram mirrors 4 times on the emulatorcpu only, iop the 40000-fffff area is open bus.
  - framebuffer, at least for bios 1.5, lives from 0x4000-0xd5ff, exactly 640x480 pixels 1bpp, interlaced (even? plane is at 4000-8aff, odd? plane is at 8b00-d5ff); however the starting address of the framebuffer is configurable to any address within the 0x0000-0x1ffff range? (this exact range is unclear)
* figure out how the emulation-cpu boots and where its 8k of local ram maps to - done
  - both cpus boot, reset and system int controls are accessed at fffea from either cpu; emulatorcpu's 8k of ram lives at the beginning of its address space, but can be disabled in favor of mainram at the same addresses
* 82s147 DISKSEP PROM regenerated from original BCPL code
* SETMEMREQ PROM retyped from binary listing
* TIMING PROM retyped from binary listing
*/

#include "emu.h"
#include "cpu/i86/i86.h"
#include "imagedev/floppy.h"
#include "machine/ay31015.h"
#include "machine/clock.h"
#include "machine/pic8259.h"
#include "machine/wd_fdc.h"
#include "sound/dac.h"
#include "video/tms9927.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#define LOG_VIDEO           (1U << 1)
#define LOG_READOP_STATUS   (1U << 2)
#define LOG_FIFO            (1U << 3)
#define LOG_SPC_DSP         (1U << 4)
#define LOG_FIFO_VERBOSE    (1U << 5)

#define VERBOSE (0)
#include "logmacro.h"


namespace {

class notetaker_state : public driver_device
{
public:
	notetaker_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag) ,
		m_iop_cpu(*this, "iop_cpu"),
		m_iop_pic(*this, "iop_pic8259"),
		m_ep_cpu(*this, "ep_cpu"),
		m_ep_pic(*this, "ep_pic8259"),
		m_kbduart(*this, "kbduart"),
		m_eiauart(*this, "eiauart"),
		m_crtc(*this, "crt5027"),
		m_dac(*this, "dac"),
		m_fdc(*this, "wd1791"),
		m_floppy0(*this, "wd1791:0"),
		m_floppy(nullptr)
	{
	}

	void notetakr(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void driver_start() override;

	void iop_io(address_map &map) ATTR_COLD;
	void iop_mem(address_map &map) ATTR_COLD;
	void ep_io(address_map &map) ATTR_COLD;
	void ep_mem(address_map &map) ATTR_COLD;

	// devices
	required_device<cpu_device> m_iop_cpu;
	required_device<pic8259_device> m_iop_pic;
	required_device<cpu_device> m_ep_cpu;
	required_device<pic8259_device> m_ep_pic;
	required_device<ay31015_device> m_kbduart;
	required_device<ay31015_device> m_eiauart;
	required_device<crt5027_device> m_crtc;
	required_device<dac_word_interface> m_dac;
	required_device<fd1791_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	floppy_image_device *m_floppy;

	std::unique_ptr<uint16_t[]> m_mainram;

	// screen
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	// basic io
	void ipcon_reg_w(uint16_t data);
	void epcon_reg_w(uint16_t data);
	void fifo_reg_w(uint16_t data);
	void fifo_bus_w(uint16_t data);
	void disk_reg_w(uint16_t data);
	void load_disp_addr_w(uint16_t data);

	// uarts
	uint16_t read_op_status_r();
	void load_key_ctl_reg_w(uint16_t data);
	void key_data_reset_w(uint16_t data);
	void key_chip_reset_w(uint16_t data);
	uint16_t read_eia_status_r();
	void load_eia_ctl_reg_w(uint16_t data);
	void eia_data_reset_w(uint16_t data);
	void eia_chip_reset_w(uint16_t data);

	// mem map stuff
	uint16_t iop_r(offs_t offset);
	void iop_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t ep_mainram_r(offs_t offset, uint16_t mem_mask);
	void ep_mainram_w(offs_t offset, uint16_t data, uint16_t mem_mask);

	// IPConReg
	uint8_t m_boot_seq_done = 0;
	uint8_t m_proc_lock = 0;
	uint8_t m_char_ctr = 0;
	uint8_t m_disable_rom = 0;
	uint8_t m_corr_on_q = 0;
	uint8_t m_led_ind6 = 0;
	uint8_t m_led_ind7 = 0;
	uint8_t m_led_ind8 = 0;

	//  FIFOReg
	uint8_t m_tablet_y_on = 0;
	uint8_t m_tablet_x_on = 0;
	uint8_t m_fr_sel2 = 0;
	uint8_t m_fr_sel1 = 0;
	uint8_t m_fr_sel0 = 0;
	uint8_t m_sh_conb = 0;
	uint8_t m_sh_cona = 0;
	uint8_t m_set_sh = 0;

	// DiskReg
	uint8_t m_adc_spd0 = 0;
	uint8_t m_adc_spd1 = 0;
	uint8_t m_stopword_clock_q = 0;
	uint8_t m_clr_diskcont_q = 0;
	uint8_t m_prog_bitclk1 = 0;
	uint8_t m_prog_bitclk2 = 0;
	uint8_t m_prog_bitclk3 = 0;
	uint8_t m_an_sel4 = 0;
	uint8_t m_an_sel2 = 0;
	uint8_t m_an_sel1 = 0;
	uint8_t m_drive_sel1 = 0;
	uint8_t m_drive_sel2 = 0;
	uint8_t m_drive_sel3 = 0;
	uint8_t m_side_select = 0;
	uint8_t m_disk_5v_on = 0;
	uint8_t m_disk_12v_on = 0;

	// output fifo, for DAC
	uint16_t m_outfifo[16]; // technically three 74LS225 5bit*16stage FIFO chips, arranged as a 16 stage, 12-bit wide fifo (one bit unused per chip)
	uint8_t m_outfifo_count = 0;
	uint8_t m_outfifo_tail_ptr = 0;
	uint8_t m_outfifo_head_ptr = 0;

	// fifo timer
	emu_timer *m_fifo_timer = nullptr;
	TIMER_CALLBACK_MEMBER(timer_fifoclk);

	// framebuffer display starting address
	uint16_t m_disp_addr = 0;

	void iop_reset();
	void ep_reset();
};

TIMER_CALLBACK_MEMBER(notetaker_state::timer_fifoclk)
{
	//pop a value off the fifo and send it to the dac.
	if (m_outfifo_count == 0)
		LOGMASKED(LOG_FIFO_VERBOSE, "output fifo is EMPTY! repeating previous sample!\n");

	uint16_t data = m_outfifo[m_outfifo_tail_ptr];
	// if fifo is empty (tail ptr == head ptr), do not increment the tail ptr, otherwise do.
	if (m_outfifo_count > 0)
	{
		m_outfifo_tail_ptr++;
		m_outfifo_count--;
	}
	m_outfifo_tail_ptr &= 0xf;
	m_dac->write(data);
	m_fifo_timer->adjust(attotime::from_hz(((960_kHz_XTAL / 10) / 4) / ((m_fr_sel0 << 3) + (m_fr_sel1 << 2) + (m_fr_sel2 << 1) + 1)));
}

uint32_t notetaker_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	// have to figure out what resolution we're drawing to here and draw appropriately to screen
	// code borrowed/stolen from video/mac.cpp
	uint32_t const video_base = (m_disp_addr << 3) & 0x1ffff;
	uint16_t const *video_ram_field1 = &m_mainram[video_base / 2];
	uint16_t const *video_ram_field2 = &m_mainram[(video_base + 0x4b00) / 2];

	LOGMASKED(LOG_VIDEO, "Video Base = 0x%05x\n", video_base);

	for (int y = 0; y < 480; y++)
	{
		uint16_t *const line = &bitmap.pix(y);

		for (int x = 0; x < 640; x += 16)
		{
			uint16_t const word = *((y & 1) ? video_ram_field2 : video_ram_field1)++;
			for (int b = 0; b < 16; b++)
			{
				line[x + b] = BIT(word, 15 - b);
			}
		}
	}
	return 0;
}

void notetaker_state::ipcon_reg_w(uint16_t data)
{
	m_boot_seq_done = BIT(data, 7);
	m_proc_lock     = BIT(data, 6); // bus lock for this processor (hold other processor in wait state)
	m_char_ctr      = BIT(data, 5); // battery charge control (incorrectly called 'Char counter' in source code)
	m_disable_rom   = BIT(data, 4); // disable rom at 0000-0fff
	m_corr_on_q     = BIT(data, 3); // CorrectionOn (ECC correction enabled); also LedInd5
	m_led_ind6      = BIT(data, 2);
	m_led_ind7      = BIT(data, 1);
	m_led_ind8      = BIT(data, 0);
	popmessage("LEDS: CR1: %d, CR2: %d, CR3: %d, CR4: %d", BIT(data, 2), BIT(data, 3), BIT(data, 1), BIT(data, 0)); // cr1 and 2 are in the reverse order as expected, according to the schematic
}

/* handlers for the two system hd6402s (ay-5-1013 equivalent) */
/* * Keyboard hd6402 */
uint16_t notetaker_state::read_op_status_r() // 74ls368 hex inverter at #l7 provides 4 bits, inverted
{
	uint16_t data = 0xfff0;
	data |= (m_outfifo_count >= 1) ? 0 : 0x08; // m_fifo_out_rdy is true if the fifo has at least 1 word in it, false otherwise
	data |= (m_outfifo_count < 16) ? 0 : 0x04; // m_fifo_in_rdy is true if the fifo has less than 16 words in it, false otherwise
	// note /SWE is permanently enabled, so we don't enable it here for HD6402 reading
	data |= m_kbduart->dav_r()  ? 0 : 0x02; // DR - pin 19
	data |= m_kbduart->tbmt_r() ? 0 : 0x01; // TBRE - pin 22

	LOGMASKED(LOG_READOP_STATUS, "ReadOPStatus read, returning %04x\n", data);

	return data;
}

void notetaker_state::load_key_ctl_reg_w(uint16_t data)
{
	m_kbduart->write_cs(0);
	m_kbduart->write_np(BIT(data, 4)); // PI - pin 35
	m_kbduart->write_tsb(BIT(data, 3)); // SBS - pin 36
	m_kbduart->write_nb2(BIT(data, 2)); // CLS2 - pin 37
	m_kbduart->write_nb1(BIT(data, 1)); // CLS1 - pin 38
	m_kbduart->write_eps(BIT(data, 0)); // EPE - pin 39
	m_kbduart->write_cs(1);
}

void notetaker_state::key_data_reset_w(uint16_t data)
{
	m_kbduart->write_rdav(0); // DDR - pin 18
	m_kbduart->write_rdav(1); // ''
}

void notetaker_state::key_chip_reset_w(uint16_t data)
{
	m_kbduart->write_xr(0); // MR - pin 21
	m_kbduart->write_xr(1); // ''
}

/* FIFO (DAC) Stuff and ADC stuff */
void notetaker_state::fifo_reg_w(uint16_t data)
{
	m_set_sh = BIT(data, 15);
	m_sh_cona = BIT(data, 14);
	m_sh_conb = BIT(data, 13);
	m_fr_sel0 = BIT(data, 12);
	m_fr_sel1 = BIT(data, 11);
	m_fr_sel2 = BIT(data, 10);
	m_tablet_x_on = BIT(data, 9);
	m_tablet_y_on = BIT(data, 8);
	m_fifo_timer->adjust(attotime::from_hz(((960_kHz_XTAL / 10) / 4) / ((m_fr_sel0 << 3) + (m_fr_sel1 << 2) + (m_fr_sel2 << 1) + 1)));
	/* FIFO timer is clocked by 960khz divided by 10 (74ls162 decade counter),
	divided by 4 (mc14568B with divider 1 pins set to 4), divided by
	1,3,5,7,9,11,13,15 (or 0,2,4,6,8,10,12,14?)
	*/
	// todo: handle tablet and sample/hold stuff as well
	LOGMASKED(LOG_FIFO, "Write to 0x60 fifo_reg_w of %04x; fifo timer set to %d hz\n", data, ((960'000 / 10) / 4) / ((m_fr_sel0 << 3) + (m_fr_sel1 << 2) + (m_fr_sel2 << 1) + 1));
}

void notetaker_state::fifo_bus_w(uint16_t data)
{
	if (m_outfifo_count == 16)
	{
		LOGMASKED(LOG_SPC_DSP, "outfifo was full, write ignored!\n");
		return;
	}
	m_outfifo[m_outfifo_head_ptr] = data >> 4;
	m_outfifo_head_ptr++;
	m_outfifo_count++;
	m_outfifo_head_ptr&=0xF;
}

void notetaker_state::disk_reg_w(uint16_t data)
{
	/* See http://bitsavers.trailing-edge.com/pdf/xerox/notetaker/memos/19781023_More_NoteTaker_IO_Information.pdf
	   but note that bit 12 (called bit 3 in documentation) was changed between
	   oct 1978 and 1979 to reset the disk controller digital-PLL as
	   ClrDiskCont' rather than acting as ProgBitClk0, which is permanently
	   wired high instead, meaning only the 4.5Mhz - 18Mhz dot clocks are
	   available for the CRTC. */
	m_adc_spd0 = BIT(data, 15);
	m_adc_spd1 = BIT(data, 14);
	m_stopword_clock_q = BIT(data, 13);
	//if (!(m_clr_diskcont_q) && (data & 0x1000)) m_floppy->device_reset(); // reset on rising edge
	m_clr_diskcont_q = BIT(data, 12); // originally ProgBitClk0, but co-opted later to reset the FDC's external PLL
	m_prog_bitclk1 = BIT(data, 11);
	m_prog_bitclk2 = BIT(data, 10);
	m_prog_bitclk3 = BIT(data, 9);
	m_an_sel4 = BIT(data, 8);
	m_an_sel2 = BIT(data, 7);
	m_an_sel1 = BIT(data, 6);
	m_drive_sel1 = BIT(data, 5);
	m_drive_sel2 = BIT(data, 4); // drive 2 not present on hardware, but could work if present
	m_drive_sel3 = BIT(data, 3); // drive 3 not present on hardware, but could work if present
	m_side_select = BIT(data, 2);
	m_disk_5v_on = BIT(data, 1);
	m_disk_12v_on = BIT(data, 0);

	// ADC stuff
	//TODO

	// FDC stuff
	// first handle the motor stuff; we'll clobber whatever was in m_floppy, then reset it to what it should be
	m_floppy = m_floppy0->get_device();

	// Disk5VOn and 12VOn can be thought of as a crude MotorOn signal as the motor won't run with either? of them missing.
	// However, a tech note involves adding a patch so that MotorOn is only activated if the drive is actually selected.
	m_floppy->mon_w(!(m_disk_5v_on && m_disk_12v_on && m_drive_sel1));

	//m_floppy = m_floppy1->get_device();
	//m_floppy->mon_w(!(m_disk_5v_on && m_disk_12v_on && m_drive_sel2)); // Disk5VOn and 12VOn can be thought of as a crude MotorOn signal as the motor won't run with either? of them missing.
	//m_floppy = m_floppy2->get_device();
	//m_floppy->mon_w(!(m_disk_5v_on && m_disk_12v_on && m_drive_sel3)); // Disk5VOn and 12VOn can be thought of as a crude MotorOn signal as the motor won't run with either? of them missing.

	// now restore m_floppy state to what it should be
	if (m_drive_sel1)
		m_floppy = m_floppy0->get_device();
	else
		m_floppy = nullptr;

	m_fdc->set_floppy(m_floppy); // select the floppy
	if (m_floppy)
	{
		m_floppy->ss_w(m_side_select);
	}

	// CRTC clock rate stuff
	//TODO
}

void notetaker_state::load_disp_addr_w(uint16_t data)
{
	m_disp_addr = data;
	// for future low level emulation: clear the current counter position here as well, as well as empty/reset the display fifo, and the setmemrq state.
}

/* EIA hd6402 */
uint16_t notetaker_state::read_eia_status_r() // 74ls368 hex inverter at #f1 provides 2 bits, inverted
{
	uint16_t data = 0xfffc;
	// note /SWE is permanently enabled, so we don't enable it here for HD6402 reading
	data |= m_eiauart->dav_r()  ? 0 : 0x02; // DR - pin 19
	data |= m_eiauart->tbmt_r() ? 0 : 0x01; // TBRE - pin 22
	return data;
}

void notetaker_state::load_eia_ctl_reg_w(uint16_t data)
{
	m_eiauart->write_cs(0);
	m_eiauart->write_np(BIT(data, 4)); // PI - pin 35
	m_eiauart->write_tsb(BIT(data, 3)); // SBS - pin 36
	m_eiauart->write_nb2(BIT(data, 2)); // CLS2 - pin 37
	m_eiauart->write_nb1(BIT(data, 1)); // CLS1 - pin 38
	m_eiauart->write_eps(BIT(data, 0)); // EPE - pin 39
	m_eiauart->write_cs(1);
}

void notetaker_state::eia_data_reset_w(uint16_t data)
{
	m_eiauart->write_rdav(0); // DDR - pin 18
	m_eiauart->write_rdav(1); // ''
}

void notetaker_state::eia_chip_reset_w(uint16_t data)
{
	m_eiauart->write_xr(0); // MR - pin 21
	m_eiauart->write_xr(1); // ''
}


/* These next two members are memory map related for the iop */
uint16_t notetaker_state::iop_r(offs_t offset)
{
	uint16_t *rom = (uint16_t *)(memregion("iop")->base());
	rom += 0x7f800;
	uint16_t *ram = m_mainram.get();
	if (m_boot_seq_done == 0 || (m_disable_rom == 0 && (offset & 0x7f800) == 0))
	{
		rom += offset & 0x7ff;
		return *rom;
	}
	else
	{
		// are we in the FFFE8-FFFEF area where the parity/int/reset/etc stuff lives?
		if (offset >= 0x7fff4)
		{
			logerror("attempt to read processor control regs at %d\n", offset << 1);
			return 0xffff;
		}
		ram += offset;
		return *ram;
	}
}

void notetaker_state::iop_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	//uint16_t tempword;
	uint16_t *ram = m_mainram.get();
	if (m_boot_seq_done == 0 || (m_disable_rom == 0 && (offset & 0x7f800) == 0))
	{
		logerror("attempt to write %04X to ROM-mapped area at %06X ignored\n", data, offset << 1);
		return;
	}
	// are we in the FFFE8-FFFEF area where the parity/int/reset/etc stuff lives?
	if (offset >= 0x7fff4)
	{
		logerror("attempt to write processor control regs at %d with %02X ignored\n", offset << 1, data);
	}
	COMBINE_DATA(&ram[offset]);
}

/* Address map comes from http://bitsavers.informatik.uni-stuttgart.de/pdf/xerox/notetaker/schematics/19790423_Notetaker_IO_Processor.pdf
a19 a18 a17 a16  a15 a14 a13 a12  a11 a10 a9  a8   a7  a6  a5  a4   a3  a2  a1  a0   BootSeqDone  DisableROM
mode 0: (to get the boot vector and for maybe 1 or 2 instructions afterward)
x   x   x   x    x   x   x   x    *   *   *   *    *   *   *   *    *   *   *   *    0            x          R   ROM (writes are open bus)
mode 1: (during most of boot)
0   0   0   0    0   0   0   0    *   *   *   *    *   *   *   *    *   *   *   *    1            0          R   ROM (writes are open bus)
0   0   0   0    0   0   0   1    *   *   *   *    *   *   *   *    *   *   *   *    1            0          RW  RAM
0   0   0   0    0   0   1   *    *   *   *   *    *   *   *   *    *   *   *   *    1            0          RW  RAM
<anything not all zeroes>x   x    x   x   x   x    x   x   x   x    x   x   x   x    1            0          .   Open Bus
mode 2: (during load of the emulatorcpu's firmware to the first 8k of shared ram which is on the emulatorcpu board)
0   0   *   *    *   *   *   *    *   *   *   *    *   *   *   *    *   *   *   *    1            1          RW  RAM
<anything not all zeroes>x   x    x   x   x   x    x   x   x   x    x   x   x   x    1            1          .   Open Bus
   EXCEPT for the following, decoded by the memory address logic board:
1   1   1   1    1   1   1   1    1   1   1   1    1   1   1   0    1   0   0   x    1            x          .   Open Bus
1   1   1   1    1   1   1   1    1   1   1   1    1   1   1   0    1   0   1   x    1            x          W   FFFEA (Multiprocessor Control (reset/int/boot for each proc; data bits 3,2,1,0 = 0010 means IP, bits 3210 = 0111 means EP; all others ignored.))
1   1   1   1    1   1   1   1    1   1   1   1    1   1   1   0    1   1   0   x    1            x          R   FFFEC (Syndrome bits (gnd bit 15, parity bit 14, exp (ECC) bits 13-8, bits 7-0 are 'other' (?maybe highest address bits?)
1   1   1   1    1   1   1   1    1   1   1   1    1   1   1   0    1   1   1   x    1            x          R   FFFEE (Parity Error Address: row bits 15-8, column bits 7-0; reading this acknowledges a parity interrupt)

More or less:
BootSeqDone is 0, DisableROM is ignored, mem map is 0x00000-0xfffff reading is the 0x1000-long ROM, repeated every 0x1000 bytes. writing goes nowhere.
BootSeqDone is 1, DisableROM is 0,       mem map is 0x00000-0x00fff reading is the 0x1000-long ROM, remainder of memory map goes to RAM or open bus. writing the ROM area goes nowhere, writing RAM goes to RAM.
BootSeqDone is 1, DisableROM is 1,       mem map is entirely RAM or open bus for both reading and writing.
*/

void notetaker_state::iop_mem(address_map &map)
{
	/*
	map(0x00000, 0x00fff).rom().region("iop", 0xff000); // rom is here if either BootSeqDone OR DisableROM are zero. the 1.5 source code and the schematics implies writes here are ignored while rom is enabled; if disablerom is 1 this goes to mainram
	map(0x01000, 0x3ffff).ram().share("mainram"); // 256k of ram (less 8k), shared between both processors. rom goes here if bootseqdone is 0
	// note 4000-d5ff is the framebuffer for the screen, in two sets of fields for odd/even interlace?
	map(0xff000, 0xfffe7).rom().region("iop", 0xff000); // rom is only banked in here if bootseqdone is 0, so the reset vector is in the proper place. otherwise the memory control regs live at fffe8-fffef
	//map(0xfffea, 0xfffeb).w(FUNC(notetaker_state::cpuCtl_w));
	//map(0xfffec, 0xfffed).r(FUNC(notetaker_state::parityErrHi_r));
	//map(0xfffee, 0xfffef).r(this. FUNC(notetaker_state::parityErrLo_r));
	map(0xffff0, 0xfffff).rom().region("iop", 0xffff0);
	*/
	map(0x00000, 0xfffff).rw(FUNC(notetaker_state::iop_r), FUNC(notetaker_state::iop_w)); // bypass MAME's memory map system as we need finer grained control
}

/* iop memory map comes from http://bitsavers.informatik.uni-stuttgart.de/pdf/xerox/notetaker/memos/19790605_Definition_of_8086_Ports.pdf
   and from the schematic at http://bitsavers.informatik.uni-stuttgart.de/pdf/xerox/notetaker/schematics/19790423_Notetaker_IO_Processor.pdf
a19 a18 a17 a16  a15 a14 a13 a12  a11 a10 a9  a8   a7  a6  a5  a4   a3  a2  a1  a0
x   x   x   x    0   x   x   x    x   x   x   0    0   0   0   x    x   x   *   .       RW  IntCon (PIC8259)
x   x   x   x    0   x   x   x    x   x   x   0    0   0   1   x    x   x   x   .       W   IPConReg
x   x   x   x    0   x   x   x    x   x   x   0    0   1   0   x    0   0   0   .       .   KbdInt:Open Bus
x   x   x   x    0   x   x   x    x   x   x   0    0   1   0   x    0   0   1   .       R   KbdInt:ReadKeyData
x   x   x   x    0   x   x   x    x   x   x   0    0   1   0   x    0   1   0   .       R   KbdInt:ReadOPStatus
x   x   x   x    0   x   x   x    x   x   x   0    0   1   0   x    0   1   1   .       .   KbdInt:Open Bus
x   x   x   x    0   x   x   x    x   x   x   0    0   1   0   x    1   0   0   .       W   KbdInt:LoadKeyCtlReg
x   x   x   x    0   x   x   x    x   x   x   0    0   1   0   x    1   0   1   .       W   KbdInt:LoadKeyData
x   x   x   x    0   x   x   x    x   x   x   0    0   1   0   x    1   1   0   .       W   KbdInt:KeyDataReset
x   x   x   x    0   x   x   x    x   x   x   0    0   1   0   x    1   1   1   .       W   KbdInt:KeyChipReset
x   x   x   x    0   x   x   x    x   x   x   0    0   1   1   x    x   x   x   .       W   FIFOReg
x   x   x   x    0   x   x   x    x   x   x   0    1   0   0   x    x   x   x   .       .   Open Bus(originally ReadOpStatus)
x   x   x   x    0   x   x   x    x   x   x   0    1   0   1   x    x   *   *   .       RW  SelPIA (debugger board 8255 PIA)[Open Bus on normal units]
x   x   x   x    0   x   x   x    x   x   x   0    1   1   0   x    x   x   x   .       W   FIFOBus
x   x   x   x    0   x   x   x    x   x   x   0    1   1   1   x    x   x   x   .       .   Open Bus
x   x   x   x    0   x   x   x    x   x   x   1    0   0   0   x    x   x   x   .       RW  SelDiskReg
x   x   x   x    0   x   x   x    x   x   x   1    0   0   1   x    x   *   *   .       RW  SelDiskInt
x   x   x   x    0   x   x   x    x   x   x   1    0   1   0   *    *   *   *   .       W   SelCrtInt
x   x   x   x    0   x   x   x    x   x   x   1    0   1   1   x    x   x   x   .       W   LoadDispAddr
x   x   x   x    0   x   x   x    x   x   x   1    1   0   0   x    x   x   x   .       .   Open Bus(originally ReadEIAStatus)
x   x   x   x    0   x   x   x    x   x   x   1    1   0   1   x    0   0   0   .       R   SelEIA:ReadEIAStatus
x   x   x   x    0   x   x   x    x   x   x   1    1   0   1   x    0   0   1   .       R   SelEIA:ReadEIAData
x   x   x   x    0   x   x   x    x   x   x   1    1   0   1   x    0   1   0   .       .   SelEIA:Open Bus
x   x   x   x    0   x   x   x    x   x   x   1    1   0   1   x    0   1   1   .       .   SelEIA:Open Bus
x   x   x   x    0   x   x   x    x   x   x   1    1   0   1   x    1   0   0   .       W   SelEIA:LoadEIACtlReg
x   x   x   x    0   x   x   x    x   x   x   1    1   0   1   x    1   0   1   .       W   SelEIA:LoadEIAData
x   x   x   x    0   x   x   x    x   x   x   1    1   0   1   x    1   1   0   .       W   SelEIA:EIADataReset
x   x   x   x    0   x   x   x    x   x   x   1    1   0   1   x    1   1   1   .       W   SelEIA:EIAChipReset
x   x   x   x    0   x   x   x    x   x   x   1    1   1   0   x    x   x   x   .       R   SelADCHi
x   x   x   x    0   x   x   x    x   x   x   1    1   1   1   x    x   x   x   .       W   CRTSwitch
*/
void notetaker_state::iop_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x03).mirror(0x7e1c).rw(m_iop_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x20, 0x21).mirror(0x7e1e).w(FUNC(notetaker_state::ipcon_reg_w)); // I/O processor (rom mapping, etc) control register
	map(0x42, 0x42).mirror(0x7e10).r(m_kbduart, FUNC(ay31015_device::receive)); // read keyboard data
	map(0x44, 0x45).mirror(0x7e10).r(FUNC(notetaker_state::read_op_status_r)); // read keyboard fifo state
	map(0x48, 0x49).mirror(0x7e10).w(FUNC(notetaker_state::load_key_ctl_reg_w)); // kbd uart control register
	map(0x4a, 0x4a).mirror(0x7e10).w(m_kbduart, FUNC(ay31015_device::transmit)); // kbd uart data register
	map(0x4c, 0x4d).mirror(0x7e10).w(FUNC(notetaker_state::key_data_reset_w)); // kbd uart ddr switch (data reset)
	map(0x4e, 0x4f).mirror(0x7e10).w(FUNC(notetaker_state::key_chip_reset_w)); // kbd uart reset
	map(0x60, 0x61).mirror(0x7e1e).w(FUNC(notetaker_state::fifo_reg_w)); // DAC sample and hold and frequency setup
	//map(0xa0, 0xa1).mirror(0x7e18).rw("debug8255", FUNC(8255_device::read), FUNC(8255_device::write)); // debugger board 8255
	map(0xc0, 0xc1).mirror(0x7e1e).w(FUNC(notetaker_state::fifo_bus_w)); // DAC data write to FIFO
	map(0x100, 0x101).mirror(0x7e1e).w(FUNC(notetaker_state::disk_reg_w)); // I/O register (adc speed, crtc pixel clock and clock enable, +5 and +12v relays for floppy, etc)
	map(0x120, 0x127).mirror(0x7e18).rw(m_fdc, FUNC(fd1791_device::read), FUNC(fd1791_device::write)).umask16(0x00ff); // floppy controller
	map(0x140, 0x15f).mirror(0x7e00).rw(m_crtc, FUNC(crt5027_device::read), FUNC(crt5027_device::write)).umask16(0x00ff); // crt controller
	map(0x160, 0x161).mirror(0x7e1e).w(FUNC(notetaker_state::load_disp_addr_w)); // loads the start address for the display framebuffer
	map(0x1a0, 0x1a1).mirror(0x7e10).r(FUNC(notetaker_state::read_eia_status_r)); // read eia fifo state
	map(0x1a2, 0x1a2).mirror(0x7e10).r(m_eiauart, FUNC(ay31015_device::receive)); // read eia data
	map(0x1a8, 0x1a9).mirror(0x7e10).w(FUNC(notetaker_state::load_eia_ctl_reg_w)); // eia uart control register
	map(0x1aa, 0x1aa).mirror(0x7e10).w(m_eiauart, FUNC(ay31015_device::transmit)); // eia uart data register
	map(0x1ac, 0x1ad).mirror(0x7e10).w(FUNC(notetaker_state::eia_data_reset_w)); // eia uart ddr switch (data reset)
	map(0x1ae, 0x1af).mirror(0x7e10).w(FUNC(notetaker_state::eia_chip_reset_w)); // eia uart reset
	//map(0x1c0, 0x1c1).mirror(0x7e1e).r(FUNC(notetaker_state::SelADCHi_r)); // ADC read
	//map(0x1e0, 0x1e1).mirror(0x7e1e).r(FUNC(notetaker_state::CRTSwitch_w)); // CRT power enable?
}

/* iop_pic8259 interrupts:
irq0    parity error (parity error syndrome data will be in fffdx/fffex) - currently ignored
irq1    IPSysInt (interrupt triggered by the emulator cpu)
irq2    DiskInt (interrupt triggered by the IRQ or DRQ pins from the WD1791)
irq3    EIAInt  (interrupt triggered by the datareceived pin from the eiauart)
irq4    OddInt  (interrupt triggered by the O/E Odd/Even pin from the crt5027)
irq5    ADCInt  (interrupt triggered at the ADCSpd rate interrupt from 74c161 counter on the disk/display module to indicate adc conversion finished)
irq6    KbdInt  (interrupt triggered by the datareceived pin from the kbduart)
irq7    VSync   (interrupt from the VSYN VSync pin from the crt5027)
*/

/* writes during boot of io roms v2.0:
0x88 to port 0x020 (PCR; BootSeqDone(1), processor not locked(0), battery charger off(0), rom not disabled(0) correction off&cr4 off(1), cr3 on(0), cr2 on(0), cr1 on (0);)
0x0002 to port 0x100 (IOR write: enable 5v only relay control)
0x0003 to port 0x100 (IOR write: in addition to above, enable 12v relay control)
<dram memory 0x00000-0x3ffff is zeroed here>
0x13 to port 0x000 PIC (ICW1, 8085 vector 0b000[ignored], edge trigger mode, interval of 8, single mode (no cascade/ICW3), ICW4 needed )
0x08 to port 0x002 PIC (ICW2, T7-T3 = 0b00001)
0x0D to port 0x002 PIC (ICW4, SFNM OFF, Buffered Mode MASTER, Normal EOI, 8086 mode)
0xff to port 0x002 PIC (OCW1, All Ints Masked (disabled/suppressed))
0x0000 to port 0x04e (reset keyboard fifo/controller)
0x0000 to port 0x1ae (reset UART)
0x0016 to port 0x048 (kbd control reg write)
0x0005 to port 0x1a8 (UART control reg write)
0x5f to port 0x140 (reg0 95 horizontal lines)               \
0xf2 to port 0x142 (reg1 interlaced, hswidth=0xE, hsdelay=2) \
0x7d to port 0x144 (reg2 16 scans/row, 5 chars/datarow)       \
0x1d to port 0x146 (reg3 0 skew bits, 0x1D datarows/frame)     \_ set up CRTC
0x04 to port 0x148 (reg4 4 scan lines/frame)                   /
0x10 to port 0x14a (reg5 0x10 vdatastart)                     /
0x00 to port 0x154 (reset the crtc)                          /
0x1e to port 0x15a (reg8 load cursor line address = 0x1e)   /
0x0a03 to port 0x100 (IOR write: set bit clock to 12Mhz)
0x2a03 to port 0x100 (IOR write: enable crtc clock chain)
0x00 to port 0x15c (fire off crtc timing chain)
read from 0x0002 (byte wide) (IMR, read interrupt mask, will be 0xFF from above)
0xaf to port 0x002 PIC (mask out with 0xEF and 0xBF to unnmask interrupts IR4(OddInt) and IR6(KbdInt), and write back to IMR)
0x0400 to 0x060 (select DAC fifo frequency 2)
read from 0x44 (byte wide) to check input fifo status
... more stuff here missing relating to making the beep tone through fifo
(around pc=6b6) read keyboard uart until mouse button is clicked (WaitNoBug)
(around pc=6bc) read keyboard uart until mouse button is released (WaitBug)
0x2a23 to port 0x100 (select drive 1)
0x2a23 to port 0x100 (select drive 1)
0x3a23 to port 0x100 (unset disk separator clear (allow disk head reading))
0x3a27 to port 0x100 (select disk side 1)
0x3a07 to port 0x100 (unselect all drives)

*/



/* Emulator CPU */

void notetaker_state::epcon_reg_w(uint16_t data)
{
	/*m_EP_LED1 = m_EP_ParityError; // if parity checking is enabled AND the last access was to the low 8k AND there was a parity error, the parity error latch is latched here. It triggers an interrupt.
	m_EP_LED2 = (data&0x40)?1:0;
	m_EP_LED3 = (data&0x20)?1:0;
	m_EP_LED4 = (data&0x10)?1:0;
	m_EP_LED_SelROM_q = (data&0x08)?1:0; // this doesn't appear to be hooked anywhere, andjust drives an LED
	// originally, SelROM_q enabled two 2716 EPROMS, later 82s137 PROMS to map code to the FFC00-FFFFF area but this was dropped in the 1979 design revision in favor of having the IOP write the boot vectors for the EP to the shared ram instead. See below for how the top two address bits are disconnected to allow this to work with the way the shared ram is mapped.
	m_EP_ProcLock = (data&0x04)?1:0; // bus lock for this processor (hold other processor in wait state)
	m_EP_SetParity_q = (data&0x02)?1:0; // enable parity checking on local ram if low
	m_EP_DisLMem_q = (data&0x01)?1:0; // if low, the low 8k of local memory is disabled and accesses the shared memory instead.
	popmessage("EP LEDS: CR1: %d, CR2: %d, CR3: %d, CR4: %d", (data&0x80)>>2, (data&0x40)>>3, (data&0x20)>>1, (data&0x10));*/
}

/*
Emulator cpu mem map:
(The top two address bits are disconnected, to allow the ram board, which maps itself only at 00000-3ffff, to appear at "ffff0" to the ep processor when /reset is de-asserted by the iop)
a19 a18 a17 a16  a15 a14 a13 a12  a11 a10 a9  a8   a7  a6  a5  a4   a3  a2  a1  a0   DisLMem_q
x   x   0   0    0   0   0   *    *   *   *   *    *   *   *   *    *   *   *   *    0                       RW  Local (fast) RAM
x   x   0   0    0   0   0   *    *   *   *   *    *   *   *   *    *   *   *   *    1                       RW  System/Shared RAM
<anything not all zeroes >   *    *   *   *   *    *   *   *   *    *   *   *   *    x                       RW  System/Shared RAM
   EXCEPT for the following, decoded by the EP board and superseding above:
x   x   1   1    1   1   1   1    1   1   1   1    1   1   0   x    x   x   x   x    x                       RW  FFFC0-FFFDF (trigger ILLINST interrupt on EP, data ignored?)
   And the following, decoded by the memory address logic board:
x   x   1   1    1   1   1   1    1   1   1   1    1   1   1   0    1   0   0   x    x                       .   Open Bus
x   x   1   1    1   1   1   1    1   1   1   1    1   1   1   0    1   0   1   x    x                       W   FFFEA (Multiprocessor Control (reset(bit 6)/int(bit 5)/boot(bit 4) for each processor; data bits 3,2,1,0 are 'processor address'; 0010 means IP, 0111 means EP; all others ignored.))
x   x   1   1    1   1   1   1    1   1   1   1    1   1   1   0    1   1   0   x    x                       R   FFFEC (Syndrome bits (gnd bit 15, parity bit 14, exp(syndrome) bits 13-8, bits 7-0 are the highest address bits)
x   x   1   1    1   1   1   1    1   1   1   1    1   1   1   0    1   1   1   x    x                       R   FFFEE (Parity Error Address: row bits 15-8, column bits 7-0; reading this also acknowledges a parity interrupt)
*/

uint16_t notetaker_state::ep_mainram_r(offs_t offset, u16 mem_mask)
{
	return m_mainram[offset + 0x2000/2];
}

void notetaker_state::ep_mainram_w(offs_t offset, u16 data, u16 mem_mask)
{
	COMBINE_DATA(&m_mainram[offset + 0x2000/2]);
}

void notetaker_state::ep_mem(address_map &map)
{
	map(0x00000, 0x01fff).mirror(0xc0000).ram(); // actually a banked block of ram, 8kb (4kw)
	map(0x02000, 0x3ffff).mirror(0xc0000).rw(FUNC(notetaker_state::ep_mainram_r), FUNC(notetaker_state::ep_mainram_w)); // 256k of ram (less 8k), shared between both processors, mirrored 4 times
	//map(0xfffc0, 0xfffdf).mirror(0xc0000).rw(FUNC(notetaker_state::proc_illinst_r), FUNC(notetaker_state::proc_illinst_w));
	//map(0xfffe0, 0xfffef).mirror(0xc0000).rw(FUNC(notetaker_state::proc_control_r), FUNC(notetaker_state::proc_control_w));
}

/* note everything in the emulatorcpu's io range is incompletely decoded; so if
   0x1800 is accessed it will write to both the debug 8255 AND the pic8259!
   I'm not sure the code abuses this or not, but it might do so to both write
   registers and clear parity at once, or something similar. */
/*
Emulator cpu i/o map:
a19 a18 a17 a16  a15 a14 a13 a12  a11 a10 a9  a8   a7  a6  a5  a4   a3  a2  a1  a0   DisLMem_q
x   x   x   x    x   x   x   x    1   x   x   x    x   x   x   x    x   x   *   x    x                       RW  8259
x   x   x   x    x   x   x   1    x   x   x   x    x   x   x   x    x   *   *   x    x                       RW  EP debugger 8255, same exact interface on both cpu and alto side as the IOP debugger 8255
x   x   x   x    x   x   1   x    x   x   x   x    x   x   x   x    x   x   x   x    x                       W   EPConReg
x   x   x   x    x   1   x   x    x   x   x   x    x   x   x   x    x   x   x   x    x                       W   Writing anything here clears the parity error latch
*/

void notetaker_state::ep_io(address_map &map)
{
	map.unmap_value_high();
	map(0x800, 0x803).mirror(0x07fc).rw(m_ep_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	//map(0x1000, 0x1001).mirror(0x07fe).rw("debug8255", FUNC(8255_device::read), FUNC(8255_device::write)); // debugger board 8255, is this the same one as the iop accesses? or are these two 8255s on separate cards?
	map(0x2000, 0x2001).mirror(0x07fe).w(FUNC(notetaker_state::epcon_reg_w)); // emu processor control reg & leds
	//map(0x4000, 0x4001).mirror(0x07fe).w(FUNC(notetaker_state::EmuClearParity_w)); // writes here clear the local 8k-ram parity error register
}

/* Input ports */

/* Floppy Image Interface */
static void notetaker_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

/* Machine Start; allocate timers and savestate stuff */
void notetaker_state::machine_start()
{
	// allocate RAM
	m_mainram = make_unique_clear<uint16_t[]>(0x100000/2);

	// allocate the DAC timer, and set it to fire NEVER. We'll set it up properly in IPReset.
	m_fifo_timer = timer_alloc(FUNC(notetaker_state::timer_fifoclk), this);
	m_fifo_timer->adjust(attotime::never);

	// FDC: /DDEN is tied permanently LOW so MFM mode is ALWAYS ON
	m_fdc->dden_w(0);

	// Keyboard UART: /SWE is tied permanently LOW
	m_kbduart->write_swe(0); // status word outputs are permanently enabled (pin 16 SFD(SWE) tied low, active)

	// EIA UART: /SWE is tied permanently LOW
	m_eiauart->write_swe(0); // status word outputs are permanently enabled (pin 16 SFD(SWE) tied low, active)

	// TODO: register savestate items
}

/* Machine Reset; this emulates the full system reset, triggered by ExtReset' (cardcage pin <50>) or the PowerOnReset' circuit */
void notetaker_state::machine_reset()
{
	iop_reset();
	ep_reset();
}

/* IP Reset; this emulates the IPReset' signal */
void notetaker_state::iop_reset()
{
	// reset the Keyboard UART
	m_kbduart->write_xr(0); // MR - pin 21
	m_kbduart->write_xr(1); // ''

	// reset the EIA UART
	m_eiauart->write_xr(0); // MR - pin 21
	m_eiauart->write_xr(1); // ''

	// reset the IPConReg ls273 latch at #f1
	ipcon_reg_w(0x0000);

	// Clear the DAC FIFO
	for (int i = 0; i < 16; i++)
		m_outfifo[i] = 0;
	m_outfifo_count = 0;
	m_outfifo_tail_ptr = 0;
	m_outfifo_head_ptr = 0;

	// reset the FIFOReg latch at #h9
	fifo_reg_w(0x0000);

	// reset the DiskReg latches at #c4 and #b4 on the disk/display/eia controller board
	disk_reg_w(0x0000);

	// reset the framebuffer display address counter:
	m_disp_addr = 0;
}

/* EP Reset; this emulates the EPReset' signal */
void notetaker_state::ep_reset()
{
	// TODO: force ep into reset and hold it there, until the iop releases it.
	// there's 6 'state' bits controllable by the memory mapped cpu control reg, which need to be reset for epcpu and iocpu separately
}

/* Input ports */
static INPUT_PORTS_START( notetakr )
INPUT_PORTS_END

void notetaker_state::notetakr(machine_config &config)
{
	/* basic machine hardware */
	/* IO CPU: 8086@8MHz */
	I8086(config, m_iop_cpu, 24_MHz_XTAL / 3); /* iD8086-2 @ E4A; 24Mhz crystal divided down to 8Mhz by i8284 clock generator */
	m_iop_cpu->set_addrmap(AS_PROGRAM, &notetaker_state::iop_mem);
	m_iop_cpu->set_addrmap(AS_IO, &notetaker_state::iop_io);
	m_iop_cpu->set_irq_acknowledge_callback("iop_pic8259", FUNC(pic8259_device::inta_cb));

	PIC8259(config, m_iop_pic, 0); // iP8259A-2 @ E6
	m_iop_pic->out_int_callback().set_inputline(m_iop_cpu, 0);

	/* Emulator CPU: 8086@5MHz */
	I8086(config, m_ep_cpu, 15_MHz_XTAL / 3);
	m_ep_cpu->set_disable(); // TODO: implement the cpu control bits so this doesn't execute garbage/zeroes before its firmware gets loaded
	m_ep_cpu->set_addrmap(AS_PROGRAM, &notetaker_state::ep_mem);
	m_ep_cpu->set_addrmap(AS_IO, &notetaker_state::ep_io);
	m_ep_cpu->set_irq_acknowledge_callback("ep_pic8259", FUNC(pic8259_device::inta_cb));

	PIC8259(config, m_ep_pic, 0); // iP8259A-2 @ E6
	m_ep_pic->out_int_callback().set_inputline(m_ep_cpu, 0);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60.975);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(250));
	screen.set_screen_update(FUNC(notetaker_state::screen_update));
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* Devices */
	CRT5027(config, m_crtc, (36_MHz_XTAL / 4) / 8); // See below
	/* the clock for the crt5027 is configurable rate; 36MHz xtal divided by 1*,
	   2, 3, 4, 5, 6, 7, or 8 (* because this is a 74s163 this setting probably
	   means divide by 1; documentation at
	   http://bitsavers.trailing-edge.com/pdf/xerox/notetaker/memos/19790605_Definition_of_8086_Ports.pdf
	   claims it is 1.5, which makes no sense) and secondarily divided by 8
	   (again by two to load the 16 bit output shifters after this).
	   on reset, bitclk is 000 so divider is (36mhz/8)/8; during boot it is
	   written with 101, changing the divider to (36mhz/4)/8 */
	// TODO: for now, we just hack it to the latter setting from start; this should be handled correctly in iop_reset();
	m_crtc->set_char_width(8); //(8 pixels per column/halfword, 16 pixels per fullword)
	// TODO: below is HACKED to trigger the odd/even int ir4 instead of vblank int ir7 since ir4 is required for anything to be drawn to screen.
	// hence with the hack this interrupt triggers twice as often as it should
	m_crtc->vsyn_callback().set(m_iop_pic, FUNC(pic8259_device::ir4_w)); // note this triggers interrupts on both the iop (ir7) and emulatorcpu (ir4)
	m_crtc->set_screen("screen");

	AY31015(config, m_kbduart); // HD6402, == AY-3-1015D
	m_kbduart->write_dav_callback().set(m_iop_pic, FUNC(pic8259_device::ir6_w)); // DataRecvd = KbdInt

	clock_device &kbdclock(CLOCK(config, "kbdclock", 960_kHz_XTAL)); // hard-wired to 960KHz xtal #f11 (60000 baud, 16 clocks per baud)
	kbdclock.signal_handler().set(m_kbduart, FUNC(ay31015_device::write_rcp));
	kbdclock.signal_handler().append(m_kbduart, FUNC(ay31015_device::write_tcp));

	AY31015(config, m_eiauart); // HD6402, == AY-3-1015D
	m_eiauart->write_dav_callback().set(m_iop_pic, FUNC(pic8259_device::ir3_w)); // EIADataReady = EIAInt

	// hard-wired through an mc14568b divider set to divide by 4, the result set to divide by 5; this resulting 4800hz signal being 300 baud (16 clocks per baud)
	clock_device &eiaclock(CLOCK(config, "eiaclock", ((960_kHz_XTAL / 10) / 4) / 5));
	eiaclock.signal_handler().set(m_eiauart, FUNC(ay31015_device::write_rcp));
	eiaclock.signal_handler().append(m_eiauart, FUNC(ay31015_device::write_tcp));

	/* Floppy */
	FD1791(config, m_fdc, (((24_MHz_XTAL / 3) / 2) / 2)); // 2mhz, from 24mhz ip clock divided by 6 via 8284, an additional 2 by LS161 at #e1 on display/floppy board
	FLOPPY_CONNECTOR(config, "wd1791:0", notetaker_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);

	/* sound hardware */
	SPEAKER(config, "speaker", 2).front();
	// TODO: hook DAC up to two HA2425 (sample and hold) chips and hook those up to the speakers
	DAC1200(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.5, 0).add_route(ALL_OUTPUTS, "speaker", 0.5, 1); // unknown DAC
}

void notetaker_state::driver_start()
{
	// descramble the rom; the whole thing is a gigantic scrambled mess either to ease
	// interfacing with older xerox technologies which used A0 and D0 as the MSB bits
	// or maybe because someone screwed up somewhere along the line. we may never know.
	// see http://bitsavers.informatik.uni-stuttgart.de/pdf/xerox/notetaker/schematics/19790423_Notetaker_IO_Processor.pdf pages 12 and onward
	uint16_t *romsrc = (uint16_t *)(memregion("iopload")->base());
	uint16_t *romdst = (uint16_t *)(memregion("iop")->base());
	// leave the src pointer alone, since we've only used a 0x1000 long address space
	romdst += 0x7f800; // set the dest pointer to 0xff000 (>>1 because 16 bits data)
	for (int i = 0; i < 0x800; i++)
	{
		uint16_t wordtemp = bitswap<16>(*romsrc, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); // data bus is completely reversed
		uint16_t addrtemp = bitswap<11>(i, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); // address bus is completely reversed; 11-15 should always be zero
		uint16_t *temppointer = romdst + (addrtemp & 0x7ff);
		*temppointer = wordtemp;
		romsrc++;
	}
}

/* ROM definition */
/*
The notetaker, over its lifetime from 1978 to 1981, had three different classes of IOP roms, with multiple versions of each one.
These were:
BIOP - "Bootable", standalone "user" unit, running smalltalk-78 off of a boot disk, either single or double density; early notetakers used an fd1791 while later ones used a wd1791.
XIOP - "eXercizer" intended for initial testing of each NoteTaker system as assembled; only usable running tethered to a Xerox Alto running notex (notex.cm) as a hardware scripting language for system testing
MIOP - only bootable tethered to a Xerox Alto via a debug card, running smalltalk on the NoteTaker, but not booted off of the floppy disk.
The 'Z-iop' firmware 1.5 below seems to be a BIOP firmware.
*/

ROM_START( notetakr )
	ROM_REGION( 0x1000, "iopload", ROMREGION_ERASEFF ) // load roms here before descrambling
	ROM_SYSTEM_BIOS( 0, "v2.00", "Bootable IO Monitor v2.00" ) // dumped from Notetaker
	ROMX_LOAD( "biop__2.00_hi.b2716.h1", 0x0000, 0x0800, CRC(1119691d) SHA1(4c20b595b554e6f5489ab2c3fb364b4a052f05e3), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "biop__2.00_lo.b2716.g1", 0x0001, 0x0800, CRC(b72aa4c7) SHA1(85dab2399f906c7695dc92e7c18f32e2303c5892), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v1.50", "Bootable IO Monitor v1.50" ) // typed from the source listing at http://bitsavers.trailing-edge.com/pdf/xerox/notetaker/memos/19790620_Z-IOP_1.5_ls.pdf and scrambled
	ROMX_LOAD( "z-iop_1.50_hi.h1", 0x0000, 0x0800, CRC(122ffb5b) SHA1(b957fe24620e1aa98b3158dbcf459937dbd54bac), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "z-iop_1.50_lo.g1", 0x0001, 0x0800, CRC(2cb79a67) SHA1(692aafd2aeea27533f6288dbb1cb8678ea08fade), ROM_SKIP(1) | ROM_BIOS(1))

	ROM_REGION( 0x100000, "iop", ROMREGION_ERASEFF ) // area for descrambled roms
	// main ram, on 2 cards with parity/ecc/syndrome/timing/bus arbitration on another 2 cards

	// keyboard mcu which handles key scanning as well as reading the mouse quadratures, and issues state responses if requested by the iop
	ROM_REGION( 0x400, "kbmcu", ROMREGION_ERASEFF )
	ROM_LOAD( "keyboard.i8748.a10a", 0x000, 0x400, NO_DUMP )

	ROM_REGION( 0x500, "proms", ROMREGION_ERASEFF )
	/* disk data separator prom from the disk/display module board:
	   there are two different versions of this prom, both generated by BCPL programs,
	   one from mid 1978 (Single density only? seems very buggy and might not even work)
	   and one from 1979 (which should work and appears here).
	   Note that the bit order for the state counter (data bits 6,5,4,3) may be
	   reversed vs the real machine, but since the prom address bus is the only
	   thing that ever sees the prom data bus, this prom will work even if the
	   bit order for those bits is backwards.
	*/
	// 1979 version
	ROM_LOAD( "disksep.82s147.a4", 0x000, 0x200, CRC(38363714) SHA1(c995d2702573f5afb5fc919150d3a5661013f999) )

	// memory cas/ras/write state machine prom from the memory address logic board; the contents of this are listed in:
	// http://www.bitsavers.org/pdf/xerox/notetaker/schematics/19781027_Memory_Address_Timing.pdf
	ROM_LOAD( "timingprom.82s147.b1", 0x200, 0x200, CRC(3003b50a) SHA1(77d9ffe4716c2297708b8e5ebce7f930619c3cc3) )

	// SETMEMRQ memory timing prom from the disk/display module board; The equations for this one are actually listed on the schematic and the prom dump can be generated from these:
	ROM_LOAD( "memreqprom.82s126.d9", 0x400, 0x100, CRC(56b2be8b) SHA1(5df0579ed8afeb59113700be6f2982ef85f64b44) )

	/*
	SetMemRq:
	Address:
	76543210
	|||||||\- WCtr.0 (MSB)
	||||||\-- WCtr.1
	|||||\--- WCtr.2
	||||\---- WCtr.3 (LSB)
	|||\----- RCtr.0 (MSB)
	||\------ RCtr.1
	|\------- RCtr.2
	\-------- RCtr.3 (LSB)

	The schematic has an error here, showing the SetMemRq_q output coming from data bit 0, in reality based on the listing it comes from data bit 3
	Data:
	3210
	|\\\- N/C (always zero)
	\---- SetMemRq_q

	Equation: SETMEMRQ == (
	  ((Rctr == 0) && ((Wctr == 0)||(Wctr == 4)||(Wctr == 8)))
	||((Rctr == 4) && ((Wctr == 4)||(Wctr == 8)||(Wctr == 12)))
	||((Rctr == 8) && ((Wctr == 8)||(Wctr == 12)||(Wctr == 0)))
	||((Rctr == 12) && ((Wctr == 12)||(Wctr == 0)||(Wctr == 4)))
	)
	The PROM output is SetMemRq_q and is inverted compared to the equation above.
	*/
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS            INIT           COMPANY  FULLNAME     FLAGS
COMP( 1978, notetakr, 0,      0,      notetakr, notetakr, notetaker_state, empty_init, "Xerox", "NoteTaker", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



ns32kdb.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * National Semiconductor Series 32000 DB32016 Development Board
 *
 * Sources:
 *  - Series 32000, DB32016 Development Board User's Manual, National Semiconductor Corporation May 1985 (420310111-001A)
 *
 * TODO:
 *  - clock/timer routing
 *  - nmi/reset switches, layout
 *  - multibus interface
 */

#include "emu.h"

// cpus and memory
#include "cpu/ns32000/ns32000.h"

// various hardware
#include "machine/ns32081.h"
#include "machine/ns32082.h"
#include "machine/ns32202.h"

#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/pit8253.h"

#include "machine/clock.h"
#include "machine/74157.h"

// busses and connectors
#include "bus/rs232/rs232.h"
#include "speaker.h"
#include "imagedev/cassette.h"

#include "db32016.lh"

#define VERBOSE 0
#include "logmacro.h"

namespace {

class ns32kdb_state : public driver_device
{
public:
	ns32kdb_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_fpu(*this, "fpu")
		, m_mmu(*this, "mmu")
		, m_icu(*this, "icu")
		, m_pci(*this, "pci%u", 0U)
		, m_ppi(*this, "ppi")
		, m_pit(*this, "pit")
		, m_sdm(*this, "sdm%u", 0U)
		, m_cass(*this, "cassette")
		, m_cfg(*this, "S3")
		, m_led(*this, "DS%u", 1U)
	{
	}

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// address maps
	template <unsigned ST> void cpu_map(address_map &map) ATTR_COLD;

public:
	// machine config
	void db32016(machine_config &config);

protected:
	required_device<ns32016_device> m_cpu;
	required_device<ns32081_device> m_fpu;
	required_device<ns32082_device> m_mmu;
	required_device<ns32202_device> m_icu;

	required_device_array<i8251_device, 2> m_pci;
	required_device<i8255_device> m_ppi;
	required_device<pit8253_device> m_pit;

	// serial port diagnostic multiplexers
	required_device_array<ls157_device, 2> m_sdm;
	required_device<cassette_image_device> m_cass;

	required_ioport m_cfg;
	output_finder<4> m_led;
private:
	u8 pa_r();
	void pb_w(u8);
};

void ns32kdb_state::machine_start()
{
	m_led.resolve();
}

void ns32kdb_state::machine_reset()
{
	m_led[0] = 0;
	m_led[1] = 0;
	m_led[2] = 0;
	m_led[3] = 0;

	// FIXME: tied to TCU bus timeout
	m_led[3] = 1;
}

template <unsigned ST> void ns32kdb_state::cpu_map(address_map &map)
{
	map(0x000000, 0x007fff).rom().region("eprom", 0);

	map(0x008000, 0x027fff).ram();
	//map(0x028000, 0x7fffff); // off-board RAM
	//map(0x800000, 0x9fffff); // off-board I/O ports

	map(0xc00000, 0xc00003).rw(m_pci[0], FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);

	map(0xc00020, 0xc00027).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);

	map(0xc00030, 0xc00030).lr8([this]() { return m_cfg->read(); }, "cfg_r");
	map(0xc00032, 0xc00037).lw8([this](offs_t offset, u8 data) { m_led[2 - offset] = data; }, "led_w").umask16(0x00ff);
	map(0xc00038, 0xc00038).lw8([this](u8 data) { m_sdm[0]->select_w(data); m_sdm[1]->select_w(data); }, "sdm_w");

	map(0xc00040, 0xc00043).rw(m_pci[1], FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);

	map(0xc00050, 0xc00057).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);

	//map(0xc00060, 0xc0006f).umask16(0x00ff); // BLX J4 MCS0/
	//map(0xc00070, 0xc0007f).umask16(0x00ff); // BLX j4 MCS1/
	//map(0xc00060, 0xc0006f).umask16(0xff00); // BLX j4 MCS1

	//map(0xd00000, 0xd0ffff); // on-board ROM/EPROM expansion

	map(0xfffe00, 0xfffeff).m(m_icu, FUNC(ns32202_device::map<BIT(ST, 1)>)).umask16(0x00ff);
}

static INPUT_PORTS_START(db32016)
	PORT_START("S3")

	PORT_DIPUNUSED_DIPLOC(0x80, 0x00, "S3:8")

	PORT_DIPNAME(0x40, 0x00, "PPI Test") PORT_DIPLOCATION("S3:7")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x40, DEF_STR(On))

	PORT_DIPNAME(0x20, 0x00, "MMU") PORT_DIPLOCATION("S3:6")
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPSETTING(0x20, DEF_STR(Off))

	PORT_DIPNAME(0x10, 0x00, "FPU") PORT_DIPLOCATION("S3:5")
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPSETTING(0x10, DEF_STR(Off))

	PORT_DIPNAME(0x0f, 0x01, "Baud Rate") PORT_DIPLOCATION("S3:4,3,2,1")
	PORT_DIPSETTING(0x00, "19200")
	PORT_DIPSETTING(0x01, "9600")
	PORT_DIPSETTING(0x02, "7200")
	PORT_DIPSETTING(0x03, "4800")
	PORT_DIPSETTING(0x04, "3600")
	PORT_DIPSETTING(0x05, "2400")
	PORT_DIPSETTING(0x06, "2000")
	PORT_DIPSETTING(0x07, "1800")
	PORT_DIPSETTING(0x08, "1200")
	PORT_DIPSETTING(0x09, "600")
	PORT_DIPSETTING(0x0a, "300")
	PORT_DIPSETTING(0x0b, "150")
	PORT_DIPSETTING(0x0c, "134")
	PORT_DIPSETTING(0x0d, "110")
	PORT_DIPSETTING(0x0e, "75")
	PORT_DIPSETTING(0x0f, "50")
INPUT_PORTS_END

void ns32kdb_state::pb_w(u8 data)
{
	m_cass->output(BIT(data, 0) ? 1.0 : -1.0);
}

u8 ns32kdb_state::pa_r()
{
	return (m_cass->input() > 0.03) ? 0xfe : 0xff;
}

void ns32kdb_state::db32016(machine_config &config)
{
	NS32016(config, m_cpu, 10_MHz_XTAL);
	m_cpu->set_addrmap(0, &ns32kdb_state::cpu_map<0>);
	m_cpu->set_addrmap(4, &ns32kdb_state::cpu_map<4>);

	NS32081(config, m_fpu, 10_MHz_XTAL);
	m_cpu->set_fpu(m_fpu);

	NS32082(config, m_mmu, 10_MHz_XTAL);
	m_cpu->set_mmu(m_mmu);

	NS32202(config, m_icu, 18.432_MHz_XTAL / 10);
	m_icu->out_int().set_inputline(m_cpu, INPUT_LINE_IRQ0).invert();

	I8255(config, m_ppi);
	m_ppi->in_pa_callback().set(FUNC(ns32kdb_state::pa_r));
	m_ppi->out_pb_callback().set(FUNC(ns32kdb_state::pb_w));

	PIT8253(config, m_pit);
	m_pit->set_clk<0>(18.432_MHz_XTAL / 150);
	m_pit->set_clk<1>(18.432_MHz_XTAL / 15);
	m_pit->set_clk<2>(18.432_MHz_XTAL / 15);

	// HACK: serial clock should be configurable from ICU/pit
	clock_device &clk(CLOCK(config, "clock", 18.432_MHz_XTAL / 120));
	clk.signal_handler().set(m_pci[0], FUNC(i8251_device::write_txc));
	clk.signal_handler().append(m_pci[0], FUNC(i8251_device::write_rxc));
	clk.signal_handler().append(m_pci[1], FUNC(i8251_device::write_txc));
	clk.signal_handler().append(m_pci[1], FUNC(i8251_device::write_rxc));

	// serial port 0 is DCE
	I8251(config, m_pci[0], 18.432_MHz_XTAL / 10);
	rs232_port_device &j2(RS232_PORT(config, "j2", default_rs232_devices, "terminal"));
	LS157(config, m_sdm[0]);
	m_pci[0]->rts_handler().set(j2, FUNC(rs232_port_device::write_rts));
	m_pci[0]->rts_handler().append(m_sdm[0], FUNC(ls157_device::b0_w));
	m_pci[0]->txd_handler().set(j2, FUNC(rs232_port_device::write_txd));
	m_pci[0]->txd_handler().append(m_sdm[0], FUNC(ls157_device::b1_w));
	m_pci[0]->dtr_handler().set(j2, FUNC(rs232_port_device::write_dtr));

	j2.dsr_handler().set(m_pci[0], FUNC(i8251_device::write_dsr));
	j2.cts_handler().set(m_sdm[0], FUNC(ls157_device::a0_w));
	j2.rxd_handler().set(m_sdm[0], FUNC(ls157_device::a1_w));

	m_sdm[0]->out_callback().set(m_pci[0], FUNC(i8251_device::write_cts)).bit(0);
	m_sdm[0]->out_callback().append(m_pci[0], FUNC(i8251_device::write_rxd)).bit(1);
	//m_sdm[0]->out_callback().append(m_pci[0], FUNC(i8251_device::write_txc)).bit(2);
	//m_sdm[0]->out_callback().append(m_pci[0], FUNC(i8251_device::write_rxc)).bit(3);

	// serial port 1 is DTE
	I8251(config, m_pci[1], 18.432_MHz_XTAL / 10);
	rs232_port_device &j3(RS232_PORT(config, "j3", default_rs232_devices, nullptr));
	LS157(config, m_sdm[1]);
	m_pci[1]->rts_handler().set(j3, FUNC(rs232_port_device::write_rts));
	m_pci[1]->rts_handler().append(m_sdm[1], FUNC(ls157_device::b0_w));
	m_pci[1]->txd_handler().set(j3, FUNC(rs232_port_device::write_txd));
	m_pci[1]->txd_handler().append(m_sdm[1], FUNC(ls157_device::b1_w));
	m_pci[1]->dtr_handler().set(j3, FUNC(rs232_port_device::write_dtr));

	j3.cts_handler().set(m_sdm[1], FUNC(ls157_device::a0_w));
	j3.rxd_handler().set(m_sdm[1], FUNC(ls157_device::a1_w));

	m_sdm[1]->out_callback().set(m_pci[1], FUNC(i8251_device::write_cts)).bit(0);
	m_sdm[1]->out_callback().append(m_pci[1], FUNC(i8251_device::write_rxd)).bit(1);
	//m_sdm[1]->out_callback().append(m_pci[1], FUNC(i8251_device::write_txc)).bit(2);
	//m_sdm[1]->out_callback().append(m_pci[1], FUNC(i8251_device::write_rxc)).bit(3);

	// cassette
	SPEAKER(config, "mono").front_center();
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	config.set_default_layout(layout_db32016);
}

ROM_START(db32016)
	ROM_REGION16_LE(0x8000, "eprom", 0)

	ROM_SYSTEM_BIOS(0, "tds16", "Rev 2.00 24-NOV-83  DB32016  Version")
	ROMX_LOAD("007346__0025.u18", 0x0000, 0x4000, CRC(3d36eff5) SHA1(0a935e6299934a597e2ac24775cb1f082a38c8b3), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("007346__0018.u15", 0x0001, 0x4000, CRC(58ea003c) SHA1(62d81ff35c3eba8efa60d80326eb8264904676ec), ROM_BIOS(0) | ROM_SKIP(1))

	ROM_SYSTEM_BIOS(1, "1.1", "National DB16000 Monitor (Rev. 1.1) (tsang) Thu Sep 22 17:16:02 PDT 1983")
	ROMX_LOAD("u18.bin", 0x0000, 0x1000, CRC(05d0c876) SHA1(3e94589bbf30f41b0a704473ad15cffa08997f37), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("u15.bin", 0x0001, 0x1000, CRC(a9955f20) SHA1(2b9780f68c33ee72741472cde7104fb69baabc40), ROM_BIOS(1) | ROM_SKIP(1))

	ROM_SYSTEM_BIOS(2, "v200", "VERSION_2.00_10-FEB-83")
	ROMX_LOAD("950308221_001__rev_b.u9",  0x0001, 0x1000, CRC(28036c3f) SHA1(d4942cabac779855936b1b448630699bf83768fd), ROM_BIOS(2) | ROM_SKIP(1))
	ROMX_LOAD("950308221_002__rev_b.u11", 0x0000, 0x1000, CRC(e04edaf7) SHA1(e7173b916029c2f4b45c08e7b29b34c55e94c46f), ROM_BIOS(2) | ROM_SKIP(1))

	ROM_SYSTEM_BIOS(3, "vi03", "VERSION_I.03_25-DEC-81")
	ROMX_LOAD("6573_001.bin", 0x0001, 0x1000, CRC(fee2e343) SHA1(c8fa82a59372a304b62c5beb56b067dcb7a10c3c), ROM_BIOS(3) | ROM_SKIP(1))
	ROMX_LOAD("6573_002.bin", 0x0000, 0x1000, CRC(623c76c5) SHA1(4d088c2175456536c1db886b81aec70360ca9991), ROM_BIOS(3) | ROM_SKIP(1))
ROM_END

}

/*   YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY                   FULLNAME   FLAGS */
COMP(1984, db32016, 0,      0,      db32016, db32016, ns32kdb_state, empty_init, "National Semiconductor", "DB32016", MACHINE_NO_SOUND_HW)



ns5652.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    unknown National Semiconductor INS8900 Multibus card (980305652)

***************************************************************************/

#include "emu.h"
#include "cpu/pace/pace.h"
#include "machine/ins8250.h"


namespace {

class ns5652_state : public driver_device
{
public:
	ns5652_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ecprom(*this, "ecprom")
	{
	}

	void ns5652(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<pace_device> m_maincpu;
	required_region_ptr<u8> m_ecprom;
};


void ns5652_state::mem_map(address_map &map)
{
	map(0x0000, 0x0bff).mirror(0xd000).rom().region("program", 0);
	map(0xe800, 0xebff).ram(); // 4x MM2114J-3
	map(0xec00, 0xecff).lr16([this](offs_t offset) { return m_ecprom[offset]; }, "ecprom_r");
	map(0xf600, 0xf600).nopw(); // shift register outputs?
	map(0xf610, 0xf610).nopw();
	map(0xf620, 0xf620).nopw();
	map(0xf630, 0xf630).nopw();
	map(0xf640, 0xf640).nopw();
	map(0xf650, 0xf650).nopw();
	map(0xf660, 0xf660).nopw();
	map(0xf670, 0xf670).nopw();
	map(0xf680, 0xf680).nopw();
	map(0xf690, 0xf690).nopw();
	map(0xf6a0, 0xf6a0).nopw();
	map(0xf6b0, 0xf6b0).nopw();
	map(0xf6c0, 0xf6c0).nopw();
	map(0xf6d0, 0xf6d0).nopw();
	map(0xf6e0, 0xf6e0).nopw();
	map(0xf6f0, 0xf6f0).nopw();
	map(0xf700, 0xf700).nopw();
	map(0xf710, 0xf710).nopw();
	map(0xf720, 0xf720).nopw();
	map(0xf730, 0xf730).nopw();
	map(0xf740, 0xf740).nopw();
	map(0xf750, 0xf750).nopw();
	map(0xf760, 0xf760).nopw();
	map(0xf770, 0xf770).nopw();
	map(0xf780, 0xf780).nopw();
	map(0xf790, 0xf790).nopw();
}


static INPUT_PORTS_START(ns5652)
INPUT_PORTS_END

void ns5652_state::ns5652(machine_config &config)
{
	INS8900(config, m_maincpu, 1.8432_MHz_XTAL); // no other XTAL visible
	m_maincpu->set_addrmap(AS_PROGRAM, &ns5652_state::mem_map);

	INS8250(config, "ace", 1.8432_MHz_XTAL);
}

ROM_START(ns5652)
	ROM_REGION16_LE(0x1800, "program", 0) // all MM2708Q
	ROM_LOAD16_BYTE("5652_001b.bin", 0x0000, 0x0400, CRC(03acf738) SHA1(e512ccf64473e0b7291d8cc14f44858cac2048e6))
	ROM_LOAD16_BYTE("5652_004b.bin", 0x0001, 0x0400, CRC(b238b1ba) SHA1(90735194cc7f111fc7c1cdde1a9aab4945b00a7e))
	ROM_LOAD16_BYTE("5652_002b.bin", 0x0800, 0x0400, CRC(2fd33c25) SHA1(5f1bab6c149c19b8c57f9f014d7aecd5d287fae0))
	ROM_LOAD16_BYTE("5652_005b.bin", 0x0801, 0x0400, CRC(e1d559ed) SHA1(3093d28b661275c00de8145f8424f584a4854072))
	ROM_LOAD16_BYTE("5652_003b.bin", 0x1000, 0x0400, CRC(24abf1f8) SHA1(ef22ca58e59d8301aab9175ef7ac9dc97feae9ec))
	ROM_LOAD16_BYTE("5652_006b.bin", 0x1001, 0x0400, CRC(db1dca74) SHA1(05149e85237a742850446c01249c83ba373e66b3))

	ROM_REGION(0x100, "ecprom", 0) // 256 bytes to be packed into top 128 words of RAM
	ROM_LOAD("5930_001a.bin", 0x000, 0x100, NO_DUMP) // MM5203Q (256x8 organization)
ROM_END

} // anonymous namespace


COMP(19??, ns5652, 0, 0, ns5652, ns5652, ns5652_state, empty_init, "National Semiconductor", "unknown INS8900 Multibus card (980305652)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



o2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/**********************************************************************

    SGI O2 workstation skeleton driver

    To Do: Everything

    Memory map:
    00000000 - 0fffffff      RAM (256mbyte mirror)
    14000000 - 15ffffff      CRIME
    1f000000 - 1f3fffff      MACE
    1fc00000 - 1fc7ffff      Boot ROM
    40000000 - 7fffffff      RAM

NOTE: The default Sgi O2 Keyboard (Model No. RT6856T, Part No. 121472-101-B,
      Sgi No. 062-0002-001) has a Zilog "RT101+228A" MCU, which is really a
      Zilog Z8615 (a Z8-based PC keyboard controller) with 4K ROM (undumped).
      It might have a custom ROM, since it had special marking.

**********************************************************************/

#include "emu.h"
#include "cpu/mips/mips3.h"
#include "machine/ds17x85.h"
#include "mace.h"
#include "crime.h"


namespace {

class o2_state : public driver_device
{
public:
	o2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mace(*this, "mace")
		, m_crime(*this, "crime")
	{
	}

	void o2(machine_config &config);

protected:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<r5000be_device> m_maincpu;
	required_device<mace_device> m_mace;
	required_device<crime_device> m_crime;
};

void o2_state::mem_map(address_map &map)
{
	map(0x00000000, 0x01ffffff).ram().share("bank1");
	map(0x14000000, 0x15ffffff).m(m_crime, FUNC(crime_device::map));
	map(0x1f000000, 0x1f3fffff).m(m_mace, FUNC(mace_device::map));
	map(0x1fc00000, 0x1fc7ffff).rom().region("user1", 0);
	static const char* const bank_names[8] = { "bank1", "bank2", "bank3", "bank4", "bank5", "bank6", "bank7", "bank8" };
	for (uint32_t bank = 0; bank < 8; bank++)
	{
		const uint32_t base_addr = 0x40000000 + bank * 0x08000000;
		const uint32_t bank_size_32m = 0x02000000;
		for (uint32_t mirror = 0; mirror < 4; mirror++)
		{
			const uint32_t start_addr = base_addr + mirror * bank_size_32m;
			const uint32_t end_addr = base_addr + (mirror + 1) * bank_size_32m - 1;
			map(start_addr, end_addr).ram().share(bank_names[bank]);
		}
	}
}

static INPUT_PORTS_START( o2 )
INPUT_PORTS_END

void o2_state::o2(machine_config &config)
{
	R5000BE(config, m_maincpu, 60000000*3);
	m_maincpu->set_icache_size(32768);
	m_maincpu->set_dcache_size(32768);
	m_maincpu->set_addrmap(AS_PROGRAM, &o2_state::mem_map);
	m_maincpu->set_force_no_drc(true);

	SGI_MACE(config, m_mace, m_maincpu);
	m_mace->rtc_read_callback().set("rtc", FUNC(ds17x85_device::read_direct));
	m_mace->rtc_write_callback().set("rtc", FUNC(ds17x85_device::write_direct));

	SGI_CRIME(config, m_crime, m_maincpu);

	DS1687(config, "rtc", 32768);
}

ROM_START( o2 )
	ROM_REGION64_BE( 0x80000, "user1", 0 )

	ROM_SYSTEM_BIOS( 0, "ip32prom_4_3",  "IP32 PROM V4.3" )
	ROMX_LOAD( "ip32prom.rev4.3.bin",  0x000000, 0x080000, CRC(029f3d06) SHA1(cf5a31299131488d8aaf085054d2a6614bb16974), ROM_GROUPDWORD | ROM_BIOS(0))

	ROM_SYSTEM_BIOS( 1, "ip32prom_4_18", "IP32 PROM V4.18" )
	ROMX_LOAD( "ip32prom.rev4.18.bin", 0x000000, 0x080000, CRC(02b3c53d) SHA1(f2cfa7246d67f88fe5490e40dac6c04b1deb4d28), ROM_GROUPDWORD | ROM_BIOS(1))
ROM_END

} // anonymous namespace


//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS     INIT        COMPANY                  FULLNAME  FLAGS
COMP( 1996, o2,   0,      0,      o2,      o2,    o2_state, empty_init, "Silicon Graphics Inc.", "O2",     MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



ob68k1a.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

Omnibyte OB68K1A

PCB Layout
----------

REV-B

|-----------------------------------------------------------|
|   555     CN3     CN4         CN5   8116  SW5     10MHz   |
|                                       5.0688MHz           |
|                   PIA         PIA                         |
|           ACIA                            ROM1    ROM0    |
|                   ACIA        PTM         ROM3    ROM2    |
|           SW1                             ROM5    ROM4    |
|                       CPU                                 |
|           SW2                         4164 4164 4164 4164 |
|                                       4164 4164 4164 4164 |
|           SW3                         4164 4164 4164 4164 |
|                                   555 4164 4164 4164 4164 |
|           SW4                                 8409        |
|-|-------------CN1----------------|----|-------CN2-------|-|
  |--------------------------------|    |-----------------|

Notes:
    Relevant IC's shown.

    CPU     - Motorola MC68000L10
    PIA     - Motorola MC6821P
    ACIA    - Motorola MC6850P
    PTM     - Motorola MC6840P
    8116    - SMC COM8116
    8409    - National Semiconductor DP8409N DRAM Controller
    4164    - Fujitsu MB8264A-12 64Kx1 RAM
    555     - NE555N
    SW1     - RAM address DIP8
    SW2     - I/O address DIP8
    SW3     - ROM address DIP8
    SW4     - external RAM access address DIP8
    SW5     - push button
    CN1     - 2x43 PCB edge, IEEE 796 (Multibus)
    CN2     - 2x30 PCB edge, IEEE 796 (Multibus)
    CN3     - 2x25 header, ACIA 0
    CN4     - 2x25 header, ACIA 1
    CN5     - 2x50 header, PIA

*/

/*

    TODO:

    - interrupts
    - configuration switches
    - PIA odd/even byte access

*/


#include "emu.h"
#include "ob68k1a.h"



//**************************************************************************
//  READ/WRITE HANDLERS
//**************************************************************************

//-------------------------------------------------
//  pia_r - trampoline for PIA odd/even access
//-------------------------------------------------

uint8_t ob68k1a_state::pia_r(offs_t offset)
{
	if (offset) {
		return m_pia1->read(0);
	} else {
		return m_pia0->read(0);
	}
}


//-------------------------------------------------
//  pia_w - trampoline for PIA odd/even access
//-------------------------------------------------

void ob68k1a_state::pia_w(offs_t offset, uint8_t data)
{
	if (offset) {
		m_pia1->write(0,data);
	} else {
		m_pia0->write(0,data);
	}
}



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( ob68k1a_mem )
//-------------------------------------------------

void ob68k1a_state::ob68k1a_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x01ffff).ram();
	map(0xfe0000, 0xfeffff).rom().region(MC68000L10_TAG, 0);
	map(0xffff00, 0xffff03).rw(m_acia0, FUNC(acia6850_device::read), FUNC(acia6850_device::write)).umask16(0x00ff);
	map(0xffff10, 0xffff10).w(COM8116_TAG, FUNC(com8116_device::str_stt_w));
	map(0xffff20, 0xffff23).rw(m_acia1, FUNC(acia6850_device::read), FUNC(acia6850_device::write)).umask16(0x00ff);
//  map(0xffff40, 0xffff47).rw(MC6821_0_TAG, FUNC(pia6821_device::read), FUNC(pia6821_device::write)).umask16(0x00ff);
//  map(0xffff40, 0xffff47).rw(MC6821_1_TAG, FUNC(pia6821_device::read), FUNC(pia6821_device::write)).umask16(0xff00);
	map(0xffff40, 0xffff47).rw(FUNC(ob68k1a_state::pia_r), FUNC(ob68k1a_state::pia_w));
	map(0xffff60, 0xffff6f).rw(MC6840_TAG, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write)).umask16(0x00ff);
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( ob68k1a )
//-------------------------------------------------

INPUT_PORTS_START( ob68k1a )
INPUT_PORTS_END



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( ob68k1a )
//-------------------------------------------------

void ob68k1a_state::machine_start()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	// configure RAM
	switch (m_ram->size())
	{
	case 32*1024:
		program.unmap_readwrite(0x008000, 0x01ffff);
		break;
	}
}


void ob68k1a_state::machine_reset()
{
	// initialize COM8116
	m_dbrg->stt_w(0x0e);
	m_dbrg->str_w(0x0e);

	// set reset vector
	void *ram = m_maincpu->space(AS_PROGRAM).get_write_ptr(0);
	uint8_t *rom = memregion(MC68000L10_TAG)->base();

	memcpy(ram, rom, 8);
}



//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  machine_config( ob68k1a )
//-------------------------------------------------

void ob68k1a_state::ob68k1a(machine_config &config)
{
	// basic machine hardware
	M68000(config, m_maincpu, 10_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &ob68k1a_state::ob68k1a_mem);

	// devices
	PIA6821(config, m_pia0);
	PIA6821(config, m_pia1);
	PTM6840(config, MC6840_TAG, 10_MHz_XTAL/10).set_external_clocks(0, 0, 0);

	ACIA6850(config, m_acia0, 0);
	m_acia0->txd_handler().set(m_rs232a, FUNC(rs232_port_device::write_txd));
	m_acia0->rts_handler().set(m_rs232a, FUNC(rs232_port_device::write_rts));

	RS232_PORT(config, m_rs232a, default_rs232_devices, "terminal");
	m_rs232a->rxd_handler().set(m_acia0, FUNC(acia6850_device::write_rxd));
	m_rs232a->dcd_handler().set(m_acia0, FUNC(acia6850_device::write_dcd));
	m_rs232a->cts_handler().set(m_acia0, FUNC(acia6850_device::write_cts));

	ACIA6850(config, m_acia1, 0);
	m_acia1->txd_handler().set(m_rs232b, FUNC(rs232_port_device::write_txd));
	m_acia1->rts_handler().set(m_rs232b, FUNC(rs232_port_device::write_rts));

	RS232_PORT(config, m_rs232b, default_rs232_devices, nullptr);
	m_rs232b->rxd_handler().set(m_acia1, FUNC(acia6850_device::write_rxd));
	m_rs232b->dcd_handler().set(m_acia1, FUNC(acia6850_device::write_dcd));
	m_rs232b->cts_handler().set(m_acia1, FUNC(acia6850_device::write_cts));

	COM8116(config, m_dbrg, 5.0688_MHz_XTAL);
	m_dbrg->fr_handler().set(m_acia0, FUNC(acia6850_device::write_txc));
	m_dbrg->fr_handler().append(m_acia0, FUNC(acia6850_device::write_rxc));
	m_dbrg->ft_handler().set(m_acia1, FUNC(acia6850_device::write_txc));
	m_dbrg->ft_handler().append(m_acia1, FUNC(acia6850_device::write_rxc));

	// internal ram
	RAM(config, m_ram, 0);
	m_ram->set_default_size("32K");
	m_ram->set_extra_options("128K");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( ob68k1a )
//-------------------------------------------------

ROM_START( ob68k1a )
	ROM_REGION16_BE( 0x10000, MC68000L10_TAG, 0 )
	ROM_LOAD16_BYTE( "macsbug.u60",    0x0000, 0x2000, CRC(7c8905ff) SHA1(eba6c70f6b5b40d60e2885c2bd33dd93ec2aae48) )
	ROM_LOAD16_BYTE( "macsbug.u61",    0x0001, 0x2000, CRC(b5069252) SHA1(b310465d8ece944bd694cc9726d03fed0f4b2c0f) )
	ROM_LOAD16_BYTE( "idris_boot.u62", 0x4000, 0x2000, CRC(091e900e) SHA1(ea0c9f3ad5179eab2e743459c8afb707c059f0e2) )
	ROM_LOAD16_BYTE( "idris_boot.u63", 0x4001, 0x2000, CRC(a132259f) SHA1(34216bf1d22ff0f0af29699a1e4e0e57631f775d) )

	ROM_REGION( 0x100, "plds", 0 )
	ROM_LOAD( "1.0.u18", 0x000, 0x100, NO_DUMP ) // PAL16L8
	ROM_LOAD( "2.5.u20", 0x000, 0x100, NO_DUMP ) // PAL16L8
	ROM_LOAD( "3.2.u21", 0x000, 0x100, NO_DUMP ) // PAL16R4
	ROM_LOAD( "4.3.u49", 0x000, 0x100, NO_DUMP ) // PAL16R4
	ROM_LOAD( "5.1.u51", 0x000, 0x100, NO_DUMP ) // PAL16L8
	ROM_LOAD( "6.2.u55", 0x000, 0x100, NO_DUMP ) // PAL16L8
ROM_END



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY     FULLNAME   FLAGS
COMP( 1982, ob68k1a, 0,      0,      ob68k1a, ob68k1a, ob68k1a_state, empty_init, "Omnibyte", "OB68K1A Single Board Computer", MACHINE_NO_SOUND_HW )



ob8.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:m1macrophage
/*
The OB-8 is an 8-voice digitally-controlled analog synthesizer.

The firmware runs on a Z80.

The driver is based on the OB8 srevice manual and schematics, and is intended
as an educational tool.

This driver is very much an early-stage skeleton.

Board prefixes in component designations (e.g. PB:U54)
PB - Processor Board.
BB - Bend Board.
TB - Pot Board.
*/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/nvram.h"
#include "machine/output_latch.h"
#include "machine/pit8253.h"
#include "video/pwm.h"

namespace {

constexpr const char MAINCPU_TAG[] = "z80";
constexpr const char NVRAM_TAG[] = "nvram";

class ob8_state : public driver_device
{
public:
	ob8_state(const machine_config &mconfig, device_type type, const char *tag) ATTR_COLD
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, MAINCPU_TAG)
		, m_pit(*this, "pit_8253")
		, m_led_matrix_device(*this, "led_matrix_device")
		, m_switch_io(*this, "switch_column_%d", 0U)
		, m_b_switch_io(*this, "b_switch_%d", 0U)
	{
	}

	void ob8(machine_config &config) ATTR_COLD;

private:
	u8 switches_r(offs_t offset);
	u8 b_switches_r(offs_t offset);
	void leds_w(offs_t offset, u8 data);

	void memory_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_device<pit8253_device> m_pit;
	required_device<pwm_display_device> m_led_matrix_device;
	required_ioport_array<16> m_switch_io;
	required_ioport_array<2> m_b_switch_io;

	u8 m_selected_pot = 0;
	bool m_hold_pedal_enabled = false;
};

u8 ob8_state::switches_r(offs_t offset)
{
	const u8 selected_pot = offset & 0x1f;
	const u8 selected_column = offset & 0x0f;

	if (!machine().side_effects_disabled())
	{
		// A3-A4 select the MUX device, and A0-A2 select the MUX input.
		m_selected_pot = selected_pot;
		const u8 selected_mux_device = m_selected_pot >> 3;
		if (selected_mux_device == 3)
		{
			// MUX 3 (BB:U1) has its "C" input grounded, so A2 has no effect.
			m_selected_pot &= 0xfb;
		}

		m_hold_pedal_enabled = (selected_column == 9);
	}

	// Inverted by buffers PB:U53 and PB:U54.
	return ~m_switch_io[selected_column]->read();
}

u8 ob8_state::b_switches_r(offs_t offset)
{
	// BSWEN* further decoded by U56.
	// A0 == 0: BSW0*, A0 == 1: BSW1*.
	// Inverted by BB:U3 (80C98).
	// Only D0-D5 are relevant, D6-D7 are pulled high.
	return 0xc0 | (~m_b_switch_io[offset]->read() & 0x3f);
}

void ob8_state::leds_w(offs_t offset, u8 data)
{
	// The LED sources (rows) are controlled by D2-D7 (schematic signals LR2-7.
	// LR = Led Row). They are active low. Inverting 'data' because matrix()
	// expects an active-high mask.
	const u8 source_mask = (~data >> 2) & 0x3f;

	// The LED sink (column) is controlled by A0-A2, which are decoded by U1
	// (4028) and inverted by TB:U2. This creates a single-bit, active-low mask.
	// But matrix() requires an active-high mask.
	const u8 sink_mask = 1 << (offset & 0x07);

	m_led_matrix_device->matrix(source_mask, sink_mask);
}

void ob8_state::memory_map(address_map &map)
{
	// Signal names below (e.g. PROT*, IOR*) match those in the schematic.

	// ROM decoding done by PB:U32A (74LS139).
	map(0x0000, 0x3fff).rom();  // 4 x 2732, 4Kbyte ROMs (PB:U24-U21).

	// RAM decoding done by PB:U32B (74LS139).
	// RAMs powered by battery when there is no power.
	map(0x4000, 0x5fff).ram().share(NVRAM_TAG); // 4 x 6116 2KB RAMs (PB:U20-U17).
	// TODO: Is U17 populated?
	// TODO: Implement write protection for PB:U18-19.

	// TODO: map(0x6000, 0x7fff) // CHEN* // Decoded by PB:U41A (74LS139).

	map(0x7c00, 0x7c1f).mirror(0x0060).r(FUNC(ob8_state::switches_r));  // IOR*
	map(0x7c80, 0x7c87).mirror(0x0078).w(FUNC(ob8_state::leds_w));  // LEDS*

	// 0x7d00- 0x7d7f: SWTCH*. Further decoding done by PB:U55 (LS139)
	map(0x7d00, 0x7d00).mirror(0x001f).w("latch_bled0", FUNC(output_latch_device::write));  // BLED0*
	map(0x7d20, 0x7d20).mirror(0x001f).w("latch_bled1", FUNC(output_latch_device::write));  // BLED1*
	//TODO: map(0x7d40, 0x7d5f);  // MISC*
	map(0x7d60, 0x7d61).mirror(0x001e).r(FUNC(ob8_state::b_switches_r));  // BSWEN*

	map(0x7d80, 0x7d83).mirror(0x007c).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));  // TIMER*
	// TODO: map(0x7e00, 0x7e7f);  // STAT*

	map(0x7e80, 0x7e80).mirror(0x007f).w("latch_pb_u4", FUNC(output_latch_device::write));  // LATCH*

	// TODO: map(0x7f00, 0x7f7f);  // MISC2*
	// 0x7f80 - 0x7fff: unused. Decoder output not connected.
}

void ob8_state::io_map(address_map &map)
{
}

void ob8_state::ob8(machine_config &config)
{
	Z80(config, m_maincpu, 8_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &ob8_state::memory_map);
	m_maincpu->set_addrmap(AS_IO, &ob8_state::io_map);

	NVRAM(config, NVRAM_TAG, nvram_device::DEFAULT_ALL_0);

	PIT8253(config, m_pit);
	// TODO: clock 0 connected to OSC.
	m_pit->set_clk<1>(8_MHz_XTAL / 2);
	m_pit->set_clk<2>(8_MHz_XTAL / 2);
	// TODO: The rest of the connections.

	PWM_DISPLAY(config, m_led_matrix_device).set_size(6, 8);
	// TODO: Set up LED outputs.

	// 74HC174, 6-bit latch, PB:U4.
	output_latch_device &u4_pb(OUTPUT_LATCH(config, "latch_pb_u4"));
	u4_pb.bit_handler<0>().set_output("ARM");
	u4_pb.bit_handler<1>().set_output("AUTOST");
	// Bits 2-5 not connected.

	// 74LS174, 6-bit latch, BB:U5. Controls LEDs, active low.
	output_latch_device &bled0(OUTPUT_LATCH(config, "latch_bled0"));
	bled0.bit_handler<0>().set_output("led_osc1").invert();
	bled0.bit_handler<1>().set_output("led_osc2").invert();
	bled0.bit_handler<2>().set_output("led_osc2only").invert();
	bled0.bit_handler<3>().set_output("led_amount").invert();
	bled0.bit_handler<4>().set_output("led_down").invert();
	bled0.bit_handler<5>().set_output("led_up").invert();

	// 74LS174, 6-bit latch, BB:U4. Controls LEDs, active low.
	output_latch_device &bled1(OUTPUT_LATCH(config, "latch_bled1"));
	bled1.bit_handler<0>().set_output("led_rate").invert();
	bled1.bit_handler<1>().set_output("led_mode").invert();
	bled1.bit_handler<2>().set_output("led_lower").invert();
	bled1.bit_handler<3>().set_output("led_upper").invert();
	bled1.bit_handler<4>().set_output("led_arpegiate").invert();
	// Bit 5 is unused.
}

INPUT_PORTS_START(ob8)
	PORT_START("switch_column_0")  // C0 - G0 in schematic.
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_C2
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_CS2
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_D2
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_DS2
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_E2
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_F2
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_FS2
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_G2

	PORT_START("switch_column_1")  // G0# - D1#
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_GS2
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_A2
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_AS2
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_B2
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_C3
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_CS3
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_D3
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_DS3

	PORT_START("switch_column_2")  // E1 - B1
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_E3
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_F3
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_FS3
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_G3
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_GS3
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_A3
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_AS3
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_B3

	PORT_START("switch_column_3")  // C2 - G2
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_C4
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_CS4
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_D4
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_DS4
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_E4
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_F4
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_FS4
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_G4

	PORT_START("switch_column_4")  // G2# - D3#
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_GS4
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_A4
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_AS4
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_B4
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_C5
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_CS5
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_D5
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_DS5

	PORT_START("switch_column_5")  // E3 - B3
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_E5
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_F5
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_FS5
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_G5
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_GS5
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_A5
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_AS5
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_B5

	PORT_START("switch_column_6")  // C4 - G4
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_C6
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_CS6
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_D6
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_DS6
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_E6
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_F6
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_FS6
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_G6

	PORT_START("switch_column_7")  // G4# - C5
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_GS6
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_A6
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_AS6
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_B6
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_C7
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("switch_column_8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PRG1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PRG2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PRG3")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PRG4")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PRG5")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PRG6")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PRG7")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PRG8")

	PORT_START("switch_column_9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("HOLD")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC2 FM")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("FILTER FM")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("UNISON")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("LFO SIN")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("LFO SQR")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("LFO S/H")

	PORT_START("switch_column_10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC1 PWM")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC2 PWM")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC1 SAW")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC1 PULSE")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC2 SAW")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC2 PULSE")

	PORT_START("switch_column_11")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("TRACK")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC1 FULL")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC1 HALF")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC2 FULL")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("4 POLE")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("NOISE")

	PORT_START("switch_column_12")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("TEST1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("TEST2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC1 FM")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SYNC")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F-ENV")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("VCA")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("AUTO")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("CHORD")

	PORT_START("switch_column_13")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("LOWER")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("UPPER")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SPLIT")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("GRP A")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("GRP B")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("GRP C")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("GRP D")

	PORT_START("switch_column_14")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("DOUBLE")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("MANUAL")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("WRITE")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	// TODO: Find actual usage. See SWENF* in PB sheet 2/4.
	// (SWENF* is coming from TB).
	PORT_START("switch_column_15")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("b_switch_0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC2 ONLY")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("AMNT")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("DOWN")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("UP")

	PORT_START("b_switch_1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("WAVE")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("MODE")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("LOWER")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("UPPER")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("ARPEG")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("DEPTH ON/OFF")

	// 4051 MUX TB:U7 (Pot Board), enabled by POT0*.
	PORT_START("pot_0")
	PORT_ADJUSTER(50, "VCF REL")
	PORT_START("pot_1")
	PORT_ADJUSTER(50, "VCA REL")
	PORT_START("pot_2")
	PORT_ADJUSTER(50, "VCF DCY")
	PORT_START("pot_3")
	PORT_ADJUSTER(50, "VCA DCY")
	PORT_START("pot_4")
	PORT_ADJUSTER(50, "VCF ATK")
	PORT_START("pot_5")
	PORT_ADJUSTER(50, "VCA ATK")
	PORT_START("pot_6")
	PORT_ADJUSTER(50, "VCF SUS")
	PORT_START("pot_7")
	PORT_ADJUSTER(50, "VCA SUS")

	// 4051 MUX TB:U5 (Pot Board), enabled by POT1*.
	PORT_START("pot_8")
	PORT_ADJUSTER(50, "VCF MOD")
	PORT_START("pot_9")
	PORT_ADJUSTER(50, "VCF RES")
	PORT_START("pot_10")
	PORT_ADJUSTER(50, "VCO PW")
	PORT_START("pot_11")
	PORT_ADJUSTER(50, "LFO FREQ")
	PORT_START("pot_12")
	PORT_ADJUSTER(50, "FM AMNT")
	PORT_START("pot_13")
	PORT_ADJUSTER(50, "PWM AMNT")
	PORT_START("pot_14")
	PORT_ADJUSTER(50, "PORT AMNT")
	PORT_START("pot_15")
	PORT_ADJUSTER(50, "VCO2 DETUNE")

	// 4051 MUX TB:U6 (Pot Board), enabled by POT2*.
	PORT_START("pot_16")
	PORT_ADJUSTER(50, "VCF FREQ")
	PORT_START("pot_17")
	PORT_ADJUSTER(50, "VCO2 FREQ")
	PORT_START("pot_18")
	PORT_ADJUSTER(50, "VCO1 FREQ")
	PORT_START("pot_19")
	PORT_ADJUSTER(50, "BALANCER PROG VOL")
	PORT_START("pot_20")
	PORT_ADJUSTER(50, "MASTER TUNE")
	// The rest of the mux inputs are grounded.
	PORT_START("pot_21")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("pot_22");
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("pot_23")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	// 4051 MUX BB:U1 (Bend Board), enabled by POT3*.
	PORT_START("pot_24")
	PORT_ADJUSTER(50, "SPEED")
	PORT_START("pot_25")
	PORT_ADJUSTER(50, "DEPTH")
	PORT_START("pot_26")
	PORT_ADJUSTER(50, "BEND LEVER")
	PORT_START("pot_27")
	PORT_ADJUSTER(50, "VIBRATO LEVER")
	// The rest of the mux inputs are grounded, but they can never be addressed
	// Mux address "C" tied to ground, essentially mirroring the above 4 inputs.
INPUT_PORTS_END

ROM_START(ob8)
	ROM_REGION(0x4000, MAINCPU_TAG, 0)
	ROM_DEFAULT_BIOS("a8")

	ROM_SYSTEM_BIOS(0, "a8", "OB-8 A8 OS")
	ROMX_LOAD("ob8a80.u24", 0x000000, 0x001000, CRC(3d141a93) SHA1(4d9866687f5dfe09133da9a4feedd9af0862cfbe), ROM_BIOS(0))
	ROMX_LOAD("ob8a81.u23", 0x001000, 0x001000, CRC(fba31703) SHA1(487258baac9d5bb399c5ad1630249e41302305ba), ROM_BIOS(0))
	ROMX_LOAD("ob8a82.u22", 0x002000, 0x001000, CRC(e6e99305) SHA1(f2c4c28cf3feb77fb8e401e191b0f22af6b09e90), ROM_BIOS(0))
	ROMX_LOAD("ob8a83.u21", 0x003000, 0x001000, CRC(6912415d) SHA1(77108a9540e4d84833dc0fa8066025d812bb6e7c), ROM_BIOS(0))
ROM_END

}  // anonymous namespace

// 1983 - 1985.
SYST(1983, ob8, 0, 0, ob8, ob8, ob8_state, empty_init, "Oberheim", "OB8", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)




octane.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/**********************************************************************

    SGI Octane workstation skeleton driver

    To Do: Everything

    Memory map:
    1fc00000 - 1fc7ffff      Boot ROM

**********************************************************************/

#include "emu.h"
#include "cpu/mips/mips3.h"

#define LOG_UNKNOWN     (1U << 1)
#define LOG_ALL         (LOG_UNKNOWN)

#define VERBOSE         (0)
#include "logmacro.h"


namespace {

class octane_state : public driver_device
{
public:
	octane_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void octane(machine_config &config);

protected:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<r5000be_device> m_maincpu;
};

void octane_state::mem_map(address_map &map)
{
	map(0x1fc00000, 0x1fcfffff).rom().region("user1", 0);
}

static INPUT_PORTS_START( octane )
INPUT_PORTS_END

void octane_state::octane(machine_config &config)
{
	R5000BE(config, m_maincpu, 50000000*4); // NOTE: Wrong - should be R10000BE!
	m_maincpu->set_icache_size(32768);      // Unknown CPU cache size
	m_maincpu->set_dcache_size(32768);
	m_maincpu->set_addrmap(AS_PROGRAM, &octane_state::mem_map);
}

ROM_START( octane )
	ROM_REGION64_BE( 0x100000, "user1", 0 )
	ROMX_LOAD( "ip30prom.rev4.9.bin", 0x000000, 0x100000, CRC(10bafb52) SHA1(de250875c608add63749d3f9fb81a82cb58c3586), ROM_GROUPDWORD )
ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT    CLASS          INIT        COMPANY                 FULLNAME                                 FLAGS
COMP( 1997, octane,   0,      0,      octane,   octane,  octane_state,  empty_init, "Silicon Graphics Inc", "Octane (Version 6.5 Rev 4.9 05/22/03)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



octopus.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Barry Rodewald
/***************************************************************************

Hilger Analytical AB6089 Mk. 1 (LSI Octopus)

2013-07-26 Skeleton driver.

http://computers.mcbx.netne.net/8bit/hilger/index.htm

Below is an extract from the page:

The computer has 2 CPUs: Z80 and 8088. Most circuits are dated 1985-1986, display circuitry is made by Signetics.
Mainboard was manufactured by LSI Computers Ltd. under part numbers: 15000SS100 and 15000P4100. All steel parts
of casing are grounded by wires. It's graphics card works in pass-through mode: It takes picture from mainboard's
TTL output and adds image to it, then it puts it to monitor. Its ROM is prepared for hard disk and some type of
network, yet no HDD controller nor network interfaces are present inside - it seems that they were added as
expansion cards.

UPDATE: It's re-branded LSI Octopus computer, a very well-expandable machine which was designed to "grow with a
company". First stage was a computer which could be used even with TV set. As requirements increased, Octopus
could be equipped with hard disk controller, network adapter, multi-terminal serial port card to act as a terminal
server or even CPU cards to run concurrent systems. There were even tape backup devices for it. Octopus could run
CP/M, MP/M (concurrent - multitasking-like OS, even with terminals), or even MS-DOS - CP/M or MP/M could be used
with Z80 or 8080. There was also LSI ELSIE system, a concurrent DOS. Last British LSI machines were 386 computers
which could be used as servers for Octopus computers.

Manufacturer    Hilger Analytical / LSI Computers Ltd.

Origin  UK
Year of unit    1986?
Year of introduction    1985
End of production   ?
CPU     Z80, 8088
Speed   8MHz (8088) or 6MHz (Z80)
RAM     128kB or 256kB, expandable to 768kB
ROM     16kB (Basic)
Colors:     ??
Sound:  Speaker. Beeps :)
OS:     CP/M 80 or 86
MP/M 80 o 86
Concurrent CP/M
LSI ELSIE
MS-DOS
Text display: SCN2674B CRTC, SCB2675C for attributes
Graphics: ?? (option board, ROM is dumped)

Media:  Two internal 5.25" floppy disk drives, DS DD, 96tpi.
Probably hard disk

Power supply:
Built-in switching power supply.

I/O:    Serial port
2 parallel ports

Video TTL Output
Composite video output

Possible upgrades:  Many

Software accessibility:
Dedicated: Impossible.
CP/M - Good
DOS - Good.

It won't take XT nor AT keyboard, but pinout is quite similar. UPDATE: I saw a few photos of keyboard.
It's another Z80 computer! It has an EPROM, simple memory and CPU.

After powering on, it should perform POST writing:

TESTING...
    Main Processor
    PROM
    DMA Controllers
    RAM
    Interrupts
    Floppy Discs
    Hard Disc Controller   (optionally - if installed)

Waiting for hard Disc... (Optionally - if installed)

Firmware versions:

SYSTEM         18B (or other)
GRAPHICS      4    (if graphic card installed)

And probably it should boot or display:

Insert System Disk.

Or maybe:

Nowhere to boot from.

Load options:
    Floppy
    Pro Network
    Winchester
Enter selection:

This information was gained by studying boot ROM of the machine.

It's a very rare computer. It has 2 processors, Z80 and 8088, so it can run both MS-DOS and CP/M.

****************************************************************************/

#include "emu.h"

#include "cpu/i86/i86.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/am9517a.h"
#include "machine/bankdev.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/mc146818.h"
#include "octo_kbd.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/ram.h"
#include "machine/wd_fdc.h"
#include "machine/z80sio.h"
#include "sound/spkrdev.h"
#include "video/scn2674.h"

#include "bus/centronics/ctronics.h"
#include "bus/centronics/comxpl80.h"
#include "bus/centronics/epson_ex800.h"
#include "bus/centronics/epson_lx800.h"
#include "bus/centronics/epson_lx810l.h"
#include "bus/centronics/printer.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class octopus_state : public driver_device
{
public:
	octopus_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_subcpu(*this, "subcpu"),
		m_crtc(*this, "crtc"),
		m_vram(*this, "vram", 0x10000, ENDIANNESS_LITTLE),
		m_fontram(*this, "fram"),
		m_dma1(*this, "dma1"),
		m_dma2(*this, "dma2"),
		m_pic1(*this, "pic_master"),
		m_pic2(*this, "pic_slave"),
		m_rtc(*this, "rtc"),
		m_fdc(*this, "fdc"),
		m_floppy(*this, "fdc:%u", 0U),
		m_kb_uart(*this, "keyboard"),
		m_pit(*this, "pit"),
		m_ppi(*this, "ppi"),
		m_speaker(*this, "speaker"),
		m_serial(*this, "serial"),
		m_parallel(*this, "parallel"),
		m_z80_bankdev(*this, "z80_bank"),
		m_ram(*this, "ram"),
		m_dswa(*this, "DSWA"),
		m_current_dma(-1),
		m_speaker_active(false),
		m_beep_active(false),
		m_z80_active(false)
	{ }

	void octopus(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;
	SCN2674_DRAW_CHARACTER_MEMBER(display_pixels);
	uint8_t vram_r(offs_t offset);
	void vram_w(offs_t offset, uint8_t data);
	uint8_t get_slave_ack(offs_t offset);
	[[maybe_unused]] void fdc_drq(int state);
	uint8_t bank_sel_r(offs_t offset);
	void bank_sel_w(offs_t offset, uint8_t data);
	uint8_t dma_read(offs_t offset);
	void dma_write(offs_t offset, uint8_t data);
	void dma_hrq_changed(int state);
	uint8_t system_r(offs_t offset);
	void system_w(offs_t offset, uint8_t data);
	uint8_t cntl_r();
	void cntl_w(uint8_t data);
	uint8_t gpo_r();
	void gpo_w(uint8_t data);
	uint8_t vidcontrol_r();
	void vidcontrol_w(uint8_t data);
	uint8_t z80_io_r();
	void z80_io_w(uint8_t data);
	IRQ_CALLBACK_MEMBER(x86_irq_cb);
	uint8_t rtc_r();
	void rtc_w(uint8_t data);
	uint8_t z80_vector_r(offs_t offset);
	void z80_vector_w(offs_t offset, uint8_t data);
	uint8_t parallel_r(offs_t offset);
	void parallel_w(offs_t offset, uint8_t data);
	uint8_t video_latch_r(offs_t offset);
	void video_latch_w(offs_t offset, uint8_t data);

	void spk_w(int state);
	void spk_freq_w(int state);
	void beep_w(int state);
	void serial_clock_w(int state);
	void parallel_busy_w(int state) { m_printer_busy = state; }
	void parallel_slctout_w(int state) { m_printer_slctout = state; }

	void dack0_w(int state) { m_dma1->hack_w(state ? 0 : 1); }  // for all unused DMA channel?
	void dack1_w(int state) { if(!state) m_current_dma = 1; else if(m_current_dma == 1) m_current_dma = -1; }  // HD
	void dack2_w(int state) { if(!state) m_current_dma = 2; else if(m_current_dma == 2) m_current_dma = -1; }  // RAM refresh
	void dack3_w(int state) { m_dma1->hack_w(state ? 0 : 1); }
	void dack4_w(int state) { m_dma1->hack_w(state ? 0 : 1); }
	void dack5_w(int state) { if(!state) m_current_dma = 5; else if(m_current_dma == 5) m_current_dma = -1; }  // Floppy
	void dack6_w(int state) { m_dma1->hack_w(state ? 0 : 1); }
	void dack7_w(int state) { m_dma1->hack_w(state ? 0 : 1); }

	void octopus_io(address_map &map) ATTR_COLD;
	void octopus_mem(address_map &map) ATTR_COLD;
	void octopus_sub_io(address_map &map) ATTR_COLD;
	void octopus_sub_mem(address_map &map) ATTR_COLD;
	void octopus_vram(address_map &map) ATTR_COLD;

	TIMER_CALLBACK_MEMBER(beep_off);

	required_device<cpu_device> m_maincpu;
	required_device<cpu_device> m_subcpu;
	required_device<scn2674_device> m_crtc;
	memory_share_creator<uint8_t> m_vram;
	required_shared_ptr<uint8_t> m_fontram;
	required_device<am9517a_device> m_dma1;
	required_device<am9517a_device> m_dma2;
	required_device<pic8259_device> m_pic1;
	required_device<pic8259_device> m_pic2;
	required_device<mc146818_device> m_rtc;
	required_device<fd1793_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<i8251_device> m_kb_uart;
	required_device<pit8253_device> m_pit;
	required_device<i8255_device> m_ppi;
	required_device<speaker_sound_device> m_speaker;
	required_device<z80sio_device> m_serial;
	required_device<centronics_device> m_parallel;
	required_device<address_map_bank_device> m_z80_bankdev;
	required_device<ram_device> m_ram;
	required_ioport m_dswa;

	uint8_t m_hd_bank;  // HD bank select
	uint8_t m_fd_bank;  // Floppy bank select
	uint8_t m_z80_bank; // Z80 bank / RAM refresh
	int8_t m_current_dma;  // current DMA channel (-1 for none)
	uint8_t m_current_drive;
	uint8_t m_cntl;  // RTC / FDC control (PPI port B)
	uint8_t m_gpo;  // General purpose outputs (PPI port C)
	uint8_t m_vidctrl;
	bool m_speaker_active;
	bool m_beep_active;
	bool m_speaker_level;
	bool m_z80_active;
	bool m_rtc_address;
	bool m_rtc_data;
	uint8_t m_prev_cntl;
	uint8_t m_rs232_vector;
	uint8_t m_rs422_vector;
	bool m_printer_busy;
	bool m_printer_slctout;
	uint8_t m_char_latch_r;
	uint8_t m_attr_latch_r;
	uint8_t m_char_latch_w;
	uint8_t m_attr_latch_w;

	emu_timer* m_timer_beep;
};


void octopus_state::octopus_mem(address_map &map)
{
	map(0x00000, 0xcffff).rw(m_ram, FUNC(ram_device::read), FUNC(ram_device::write));
	//map(0xd0000, 0xdffff).ram().share("vram");
	map(0xe0000, 0xe3fff).noprw();
	map(0xe4000, 0xe5fff).ram().share("fram");
	map(0xe6000, 0xe7fff).rom().region("chargen", 0);
	map(0xe8000, 0xfbfff).noprw();
	map(0xfc000, 0xfffff).rom().region("user1", 0);
}

void octopus_state::octopus_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x0f).rw(m_dma1, FUNC(am9517a_device::read), FUNC(am9517a_device::write));
	map(0x10, 0x1f).rw(m_dma2, FUNC(am9517a_device::read), FUNC(am9517a_device::write));
	map(0x20, 0x20).portr("DSWA");
	map(0x21, 0x2f).rw(FUNC(octopus_state::system_r), FUNC(octopus_state::system_w));
	map(0x31, 0x33).rw(FUNC(octopus_state::bank_sel_r), FUNC(octopus_state::bank_sel_w));
	map(0x50, 0x51).rw(m_kb_uart, FUNC(i8251_device::read), FUNC(i8251_device::write));
	// 0x70-73: HD controller
	map(0x80, 0x83).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xa0, 0xa0).rw(m_serial, FUNC(z80sio_device::da_r), FUNC(z80sio_device::da_w));
	map(0xa1, 0xa1).rw(m_serial, FUNC(z80sio_device::ca_r), FUNC(z80sio_device::ca_w));
	map(0xa2, 0xa2).rw(m_serial, FUNC(z80sio_device::db_r), FUNC(z80sio_device::db_w));
	map(0xa3, 0xa3).rw(m_serial, FUNC(z80sio_device::cb_r), FUNC(z80sio_device::cb_w));
	map(0xb0, 0xb1).rw(m_pic1, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0xb4, 0xb5).rw(m_pic2, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0xc0, 0xc7).rw(m_crtc, FUNC(scn2674_device::read), FUNC(scn2674_device::write));
	map(0xc8, 0xc8).rw(FUNC(octopus_state::vidcontrol_r), FUNC(octopus_state::vidcontrol_w));
	map(0xc9, 0xca).rw(FUNC(octopus_state::video_latch_r), FUNC(octopus_state::video_latch_w));
	// 0xcf: mode control
	map(0xd0, 0xd3).rw(m_fdc, FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	map(0xe0, 0xe4).rw(FUNC(octopus_state::z80_vector_r), FUNC(octopus_state::z80_vector_w));
	map(0xf0, 0xf1).rw(FUNC(octopus_state::parallel_r), FUNC(octopus_state::parallel_w));
	map(0xf8, 0xff).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
}


void octopus_state::octopus_sub_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(m_z80_bankdev, FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
}

void octopus_state::octopus_sub_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(octopus_state::z80_io_r), FUNC(octopus_state::z80_io_w));
}

void octopus_state::octopus_vram(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(octopus_state::vram_r), FUNC(octopus_state::vram_w));
}

/* Input ports */
static INPUT_PORTS_START( octopus )
	PORT_START("DSWA")
	PORT_DIPNAME( 0x03, 0x02, "Number of floppy drives" ) PORT_DIPLOCATION("SWA:1,2")
	PORT_DIPSETTING( 0x00, "None" )
	PORT_DIPSETTING( 0x01, "1 Floppy" )
	PORT_DIPSETTING( 0x02, "2 Floppies" )
	PORT_DIPSETTING( 0x03, "Not used" )
	PORT_DIPNAME( 0x04, 0x00, "Quad drives" ) PORT_DIPLOCATION("SWA:3")
	PORT_DIPSETTING( 0x00, "Disabled" )
	PORT_DIPSETTING( 0x04, "Enabled" )
	PORT_DIPNAME( 0x38, 0x00, "Winchester drive type" ) PORT_DIPLOCATION("SWA:4,5,6")
	PORT_DIPSETTING( 0x00, "None" )
	PORT_DIPSETTING( 0x08, "RO201" )
	PORT_DIPSETTING( 0x10, "RO202" )
	PORT_DIPSETTING( 0x18, "Reserved" )
	PORT_DIPSETTING( 0x20, "RO204" )
	PORT_DIPSETTING( 0x28, "Reserved" )
	PORT_DIPSETTING( 0x30, "RO208" )
	PORT_DIPSETTING( 0x38, "Reserved" )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unused ) ) PORT_DIPLOCATION("SWA:7")
	PORT_DIPSETTING( 0x00, DEF_STR( Off ) )
	PORT_DIPSETTING( 0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, "Colour monitor connected" ) PORT_DIPLOCATION("SWA:8")
	PORT_DIPSETTING( 0x00, DEF_STR( No ) )
	PORT_DIPSETTING( 0x80, DEF_STR( Yes ) )
INPUT_PORTS_END

TIMER_CALLBACK_MEMBER(octopus_state::beep_off)
{
	m_beep_active = false;
}

void octopus_state::vram_w(offs_t offset, uint8_t data)
{
	m_vram[offset] = m_char_latch_w;
	m_vram[offset+0x1000] = m_attr_latch_w;
}

uint8_t octopus_state::vram_r(offs_t offset)
{
	m_char_latch_r = m_vram[offset];
	m_attr_latch_r = m_vram[offset+0x1000];
	return m_vram[offset];
}

void octopus_state::fdc_drq(int state)
{
	// TODO
}

uint8_t octopus_state::bank_sel_r(offs_t offset)
{
	switch(offset)
	{
	case 0:
		return m_hd_bank;
	case 1:
		return m_fd_bank;
	case 2:
		return m_z80_bank;
	}
	return 0xff;
}

void octopus_state::bank_sel_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
	case 0:
		m_hd_bank = data;
		logerror("HD bank = %i\n",data);
		break;
	case 1:
		m_fd_bank = data;
		logerror("Floppy bank = %i\n",data);
		break;
	case 2:
		m_z80_bank = data;
		m_z80_bankdev->set_bank(m_z80_bank & 0x0f);
		logerror("Z80/RAM bank = %i\n",data);
		break;
	}
}

// System control
// 0x20: read: System type, write: Z80 NMI
// 0x21: read: bit5=SLCTOUT from parallel interface, bit6=option board parity fail, bit7=main board parity fail
//       write: parity fail reset
// ports 0x20 and 0x21 read out the DIP switch configuration (the firmware function to get system config simply does IN AX,20h)
// 0x28: write: Z80 enable
void octopus_state::system_w(offs_t offset, uint8_t data)
{
	logerror("SYS: System control offset %i data %02x\n",offset+1,data);
	switch(offset)
	{
	case 7:  // enable Z80, halt 8088
		m_subcpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
		m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
		m_z80_active = true;
		break;
	}
}

uint8_t octopus_state::system_r(offs_t offset)
{
	uint8_t val = 0x00;
	switch(offset)
	{
	case 0:
		val = 0x1f;
		if(m_printer_slctout)
			val |= 0x20;
		return val;  // do bits 0-4 mean anything?  Language DIPs?
	}

	return 0xff;
}

// Any I/O cycle relinquishes control of the bus
uint8_t octopus_state::z80_io_r()
{
	z80_io_w(0);
	return 0x00;
}

void octopus_state::z80_io_w(uint8_t data)
{
	m_subcpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
	m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
	m_z80_active = false;
}

// Z80 vector for RS232 and RS422
uint8_t octopus_state::z80_vector_r(offs_t offset)
{
	switch(offset)
	{
		case 0:
			return m_rs232_vector;
		case 4:
			return m_rs422_vector;
		default:
			return 0xff;
	}
	return 0xff;
}

void octopus_state::z80_vector_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
		case 0:
			m_rs232_vector = data;
			logerror("RS232 vector set to %02x\n",data);
			break;
		case 4:
			m_rs422_vector = data;
			logerror("RS422 vector set to %02x\n",data);
			break;
		default:
			logerror("Read invalid vector port 0x%02x\n",0xe0 + offset);
	}
}

// RTC data and I/O - PPI port A
// bits 0-3 of RTC/FDC control go to control lines of the MC146818
// The technical manual does not mention what is connected to each bit
// This is an educated guess, based on the BIOS code
// bit 0 = ? (Pulsed low after writing to an RTC register)
// bit 1 = PPI Port A strobe?
// bit 2 = Data strobe?
// bit 3 = Address strobe?
uint8_t octopus_state::rtc_r()
{
	uint8_t ret = 0xff;

	if(m_rtc_data)
		ret = m_rtc->data_r();

	return ret;
}

void octopus_state::rtc_w(uint8_t data)
{
	if(m_rtc_data)
		m_rtc->data_w(data);
	else if(m_rtc_address)
		m_rtc->address_w(data);
}

// RTC/FDC control - PPI port B
// bits0-3: RTC control lines
// bit4-5: write precomp.
// bit6-7: drive select
uint8_t octopus_state::cntl_r()
{
	return m_cntl;
}

void octopus_state::cntl_w(uint8_t data)
{
	m_cntl = data;

	if((m_cntl & 0x08) && !(m_prev_cntl & 0x08))
	{
		m_rtc_address = true;
		m_rtc_data = false;
	}
	if((data & 0x04) && !(m_prev_cntl & 0x04))
	{
		m_rtc_address = false;
		m_rtc_data = true;
	}
	m_ppi->pc4_w(data & 0x02);
	m_prev_cntl = m_cntl;
	m_current_drive = (data & 0xc0) >> 6;
	switch(m_current_drive)
	{
	case 1:
		m_fdc->set_floppy(m_floppy[0]->get_device());
		m_floppy[0]->get_device()->mon_w(0);
		break;
	case 2:
		m_fdc->set_floppy(m_floppy[1]->get_device());
		m_floppy[1]->get_device()->mon_w(0);
		break;
	}
	logerror("Selected floppy drive %i (%02x)\n",m_current_drive,data);
}

// General Purpose Outputs - PPI port C
// bit 2 - floppy side select
// bit 1 - parallel data I/O (0 = output)
// bit 0 - parallel control I/O (0 = output)
uint8_t octopus_state::gpo_r()
{
	return m_gpo;
}

void octopus_state::gpo_w(uint8_t data)
{
	m_gpo = data;
	switch(m_current_drive)
	{
	case 1:
		m_floppy[0]->get_device()->ss_w((data & 0x04) >> 2);
		break;
	case 2:
		m_floppy[1]->get_device()->ss_w((data & 0x04) >> 2);
		break;
	default:
		logerror("Attempted to set side on unknown drive %i\n",m_current_drive);
	}
}

// Video control register
// bit 0 - video dot clock - 0=17.6MHz, 1=16MHz
// bit 2 - floppy DDEN line
// bit 3 - floppy FCLOCK line - 0=1MHz, 1=2MHz
// bits 4-5 - character width - 0=10 dots, 1=6 dots, 2=8 dots, 3=9 dots
// bit 6 - cursor mode (colour only) - 0=inverse cursor, 1=white cursor (normal)
// bit 7 - 1=monochrome mode, 0=colour mode
// Is bit 7 writable, or just mirrors DIP switch setting?  Tech manual is unclear.
uint8_t octopus_state::vidcontrol_r()
{
	return m_vidctrl;
}

void octopus_state::vidcontrol_w(uint8_t data)
{
	m_fdc->dden_w(BIT(data, 2));
	m_fdc->set_unscaled_clock(16_MHz_XTAL / (BIT(data, 3) ? 16 : 8));

	if (((m_vidctrl ^ data) & 0x31) != 0)
	{
		unsigned dots = 4 + ((data & 0x30) >> 3);
		if ((data & 0x30) == 0)
			dots = 10;
		else if ((data & 0x30) == 0x30)
			dots = 9;

		auto dotclk = BIT(data, 0) ? 16_MHz_XTAL : 17.6_MHz_XTAL;

		m_crtc->set_character_width(dots);
		m_crtc->set_unscaled_clock(dotclk / dots);
	}

	m_vidctrl = data;
}

// Sound hardware
// Sound level provided by i8253 timer 2
// Enabled by /DTR signal from i8251
// 100ms beep triggered by pulsing /CTS signal low on i8251
void octopus_state::spk_w(int state)
{
	m_speaker_active = !state;
	m_speaker->level_w(((m_speaker_active || m_beep_active) && m_speaker_level) ? 1 : 0);
}

void octopus_state::spk_freq_w(int state)
{
	m_speaker_level = state;
	m_speaker->level_w(((m_speaker_active || m_beep_active) && m_speaker_level) ? 1 : 0);
}

void octopus_state::beep_w(int state)
{
	if(!state)  // active low
	{
		m_beep_active = true;
		m_speaker->level_w(((m_speaker_active || m_beep_active) && m_speaker_level) ? 1 : 0);
		m_timer_beep->adjust(attotime::from_msec(100));
	}
}

void octopus_state::serial_clock_w(int state)
{
	m_serial->rxca_w(state);
	m_serial->txca_w(state);
}

// Parallel Centronics port
// 0xf0 : data
// 0xf1 : control
//      bit 2 = INIT?  On boot, bits 0 and 1 are set high, bit 2 is set low then high again, all other bits are set low
// can generate interrupts - tech manual suggests that Strobe, Init, Ack, and Busy can trigger an interrupt (IRQ14)
uint8_t octopus_state::parallel_r(offs_t offset)
{
	switch(offset)
	{
	case 0:
		return 0;
	case 1:
		return m_printer_busy ? 0x01 : 0x00;  // correct?  Tech manual doesn't explain which bit is which
	}
	return 0xff;
}

void octopus_state::parallel_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
	case 0:  // data
		if(!(m_gpo & 0x02))  // parallel data direction
		{
			m_parallel->write_data0(BIT(data,0));
			m_parallel->write_data1(BIT(data,1));
			m_parallel->write_data2(BIT(data,2));
			m_parallel->write_data3(BIT(data,3));
			m_parallel->write_data4(BIT(data,4));
			m_parallel->write_data5(BIT(data,5));
			m_parallel->write_data6(BIT(data,6));
			m_parallel->write_data7(BIT(data,7));
		}
		break;
	case 1:  // control (bit order unknown?)
		if(!(m_gpo & 0x01))  // parallel control direction
		{
			m_parallel->write_init(BIT(data,2));
			m_pic2->ir6_w(!BIT(data,2));
		}
		break;
	}
}

uint8_t octopus_state::dma_read(offs_t offset)
{
	uint8_t byte;
	address_space& prog_space = m_maincpu->space(AS_PROGRAM); // get the right address space
	if(m_current_dma == -1)
		return 0;
	byte = prog_space.read_byte((m_fd_bank << 16) + offset);
	return byte;
}

void octopus_state::dma_write(offs_t offset, uint8_t data)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM); // get the right address space
	if(m_current_dma == -1)
		return;
	prog_space.write_byte((m_fd_bank << 16) + offset, data);
}

void octopus_state::dma_hrq_changed(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);

	/* Assert HLDA */
	m_dma2->hack_w(state);
}

// Any interrupt will also give bus control back to the 8088
IRQ_CALLBACK_MEMBER(octopus_state::x86_irq_cb)
{
	uint8_t vector;
	m_subcpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
	m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
	m_z80_active = false;
	vector = m_pic1->inta_cb(device,irqline);
	if(vector == 0x61)  // if we have hit a serial comms IRQ, then also have the Z80SIO/2 acknowledge the interrupt
		vector = m_serial->m1_r();
	return vector;
}

void octopus_state::machine_start()
{
	m_timer_beep = timer_alloc(FUNC(octopus_state::beep_off), this);
	m_vidctrl = 0xff;

	// install RAM
	m_maincpu->space(AS_PROGRAM).install_ram(0x0000,m_ram->size()-1,m_ram->pointer());
	m_maincpu->space(AS_PROGRAM).nop_readwrite(m_ram->size(),0xcffff);
}

void octopus_state::machine_reset()
{
	m_subcpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);  // halt Z80 to start with
	m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
	m_z80_active = false;
	m_current_dma = -1;
	m_current_drive = 0;
	m_rtc_address = true;
	m_rtc_data = false;
	m_kb_uart->write_dsr(1);  // DSR is used to determine if a keyboard is connected?  If DSR is high, then the CHAR_OUT BIOS function will not output to the screen.
}

void octopus_state::video_start()
{
}

uint8_t octopus_state::video_latch_r(offs_t offset)
{
	if(offset & 0x01)
		return m_attr_latch_r;
	else
		return m_char_latch_r;
}

void octopus_state::video_latch_w(offs_t offset, uint8_t data)
{
	if(offset & 0x01)
		m_attr_latch_w = data;
	else
		m_char_latch_w = data;
}

SCN2674_DRAW_CHARACTER_MEMBER(octopus_state::display_pixels)
{
	// Attributes:
	//  - common bits
	// b7 : blink
	// b3 : underline
	//  - Monochrome
	// b6 : GP1 (general purpose)
	// b5 : reverse video
	// b4 : GP2 (general purpose)
	// b2 : High intensity
	// b1 : Grey background
	// b0 : Blank (TODO)
	//  - Colour
	// b6,5,4 : background colour (RGB)
	// b2,1,0 : foreground colour (RGB)
	if(!lg)
	{
		uint8_t tile = m_vram[address & 0x0fff];
		uint8_t attr = m_vram[(address & 0x0fff) + 0x1000];
		uint8_t data = m_fontram[(tile * 16) + linecount];
		rgb_t fg,bg;
		if(m_dswa->read() & 0x80)  // monochrome or colour mode is selected by switch 8 of system DIP switches
		{
			// colour (is the background at half intensity?)
			bg.set_r((attr & 0x40) ? 0x7f : 0x00);
			bg.set_g((attr & 0x20) ? 0x7f : 0x00);
			bg.set_b((attr & 0x10) ? 0x7f : 0x00);
			fg.set_r((attr & 0x04) ? 0xff : 0x00);
			fg.set_g((attr & 0x02) ? 0xff : 0x00);
			fg.set_b((attr & 0x01) ? 0xff : 0x00);
		}
		else
		{
			// monochrome
			if(attr & 0x02)
				fg = 0xffffff;
			else
				fg = 0x7f7f7f;
			if(attr & 0x04)
				bg = 0x7f7f7f;
			else
				bg = 0x000000;

			if(attr & 0x20)  // reverse video
				data = ~data;
		}
		if(ul && (attr & 0x08))
			data = 0xff;
		if(blink && (attr & 0x80))
			data = 0x00;
		if(cursor && !blink)
		{
			bool inverse = true;

			if(!(m_dswa->read() & 0x80))  // not available in monochrome mode
				inverse = false;
			if(m_vidctrl & 0x40)  // not enabled
				inverse = false;
			if(inverse)
				data = ~data;
			else
				data = 0xff;
		}
		for (int z=0;z<8;z++)
			bitmap.pix(y,x + z) = BIT(data,z) ? fg : bg;
	}
}

uint8_t octopus_state::get_slave_ack(offs_t offset)
{
	if (offset==7)
		return m_pic2->acknowledge();

	return 0x00;
}

static void octopus_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

static void keyboard(device_slot_interface &device)
{
	device.option_add("octopus", OCTOPUS_KEYBOARD);
}

void octopus_centronics_devices(device_slot_interface &device)
{
	device.option_add("pl80", COMX_PL80);
	device.option_add("ex800", EPSON_EX800);
	device.option_add("lx800", EPSON_LX800);
	device.option_add("lx810l", EPSON_LX810L);
	device.option_add("ap2000", EPSON_AP2000);
	device.option_add("printer", CENTRONICS_PRINTER);
}

void octopus_state::octopus(machine_config &config)
{
	/* basic machine hardware */
	I8088(config, m_maincpu, 24_MHz_XTAL / 3);  // 8MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &octopus_state::octopus_mem);
	m_maincpu->set_addrmap(AS_IO, &octopus_state::octopus_io);
	m_maincpu->set_irq_acknowledge_callback(FUNC(octopus_state::x86_irq_cb));

	Z80(config, m_subcpu, 24_MHz_XTAL / 4); // 6MHz
	m_subcpu->set_addrmap(AS_PROGRAM, &octopus_state::octopus_sub_mem);
	m_subcpu->set_addrmap(AS_IO, &octopus_state::octopus_sub_io);

	AM9517A(config, m_dma1, 24_MHz_XTAL / 6);  // 4MHz
	m_dma1->out_hreq_callback().set(m_dma2, FUNC(am9517a_device::dreq0_w));
	m_dma1->in_memr_callback().set(FUNC(octopus_state::dma_read));
	m_dma1->out_memw_callback().set(FUNC(octopus_state::dma_write));
	//m_dma1->in_ior_callback<0>().set_nop();
	//m_dma1->in_ior_callback<1>().set_nop();  // HDC
	//m_dma1->in_ior_callback<2>().set_nop();  // RAM Refresh
	//m_dma1->in_ior_callback<3>().set_nop();
	//m_dma1->out_iow_callback<0>().set_nop();
	//m_dma1->out_iow_callback<1>().set_nop();  // HDC
	//m_dma1->out_iow_callback<2>().set_nop();  // RAM Refresh
	//m_dma1->out_iow_callback<3>().set_nop();
	m_dma1->out_dack_callback<0>().set(FUNC(octopus_state::dack0_w));
	m_dma1->out_dack_callback<1>().set(FUNC(octopus_state::dack1_w));
	m_dma1->out_dack_callback<2>().set(FUNC(octopus_state::dack2_w));
	m_dma1->out_dack_callback<3>().set(FUNC(octopus_state::dack3_w));

	AM9517A(config, m_dma2, 24_MHz_XTAL / 6);  // 4MHz
	m_dma2->out_hreq_callback().set(FUNC(octopus_state::dma_hrq_changed));
	m_dma2->in_memr_callback().set(FUNC(octopus_state::dma_read));
	m_dma2->out_memw_callback().set(FUNC(octopus_state::dma_write));
	//m_dma2->in_ior_callback<0>().set_nop();
	m_dma2->in_ior_callback<1>().set(m_fdc, FUNC(fd1793_device::data_r));  // FDC
	//m_dma2->in_ior_callback<2>().set_nop();
	//m_dma2->in_ior_callback<3>().set_nop();
	//m_dma2->out_iow_callback<0>().set_nop();
	m_dma2->out_iow_callback<1>().set(m_fdc, FUNC(fd1793_device::data_w));  // FDC
	//m_dma2->out_iow_callback<2>().set_nop();
	//m_dma2->out_iow_callback<3>().set_nop();
	m_dma2->out_dack_callback<0>().set(FUNC(octopus_state::dack4_w));
	m_dma2->out_dack_callback<1>().set(FUNC(octopus_state::dack5_w));
	m_dma2->out_dack_callback<2>().set(FUNC(octopus_state::dack6_w));
	m_dma2->out_dack_callback<3>().set(FUNC(octopus_state::dack7_w));

	PIC8259(config, m_pic1, 0);
	m_pic1->out_int_callback().set_inputline(m_maincpu, 0);
	m_pic1->in_sp_callback().set_constant(1);
	m_pic1->read_slave_ack_callback().set(FUNC(octopus_state::get_slave_ack));

	PIC8259(config, m_pic2, 0);
	m_pic2->out_int_callback().set(m_pic1, FUNC(pic8259_device::ir7_w));
	m_pic2->in_sp_callback().set_constant(0);

	// RTC (MC146818 via i8255 PPI)
	I8255(config, m_ppi);
	m_ppi->in_pa_callback().set(FUNC(octopus_state::rtc_r));
	m_ppi->in_pb_callback().set(FUNC(octopus_state::cntl_r));
	m_ppi->in_pc_callback().set(FUNC(octopus_state::gpo_r));
	m_ppi->out_pa_callback().set(FUNC(octopus_state::rtc_w));
	m_ppi->out_pb_callback().set(FUNC(octopus_state::cntl_w));
	m_ppi->out_pc_callback().set(FUNC(octopus_state::gpo_w));

	MC146818(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->irq().set(m_pic2, FUNC(pic8259_device::ir2_w));

	// Keyboard UART
	I8251(config, m_kb_uart, 0);
	m_kb_uart->rxrdy_handler().set("pic_slave", FUNC(pic8259_device::ir4_w));
	m_kb_uart->dtr_handler().set(FUNC(octopus_state::spk_w));
	m_kb_uart->rts_handler().set(FUNC(octopus_state::beep_w));
	rs232_port_device &keyboard_port(RS232_PORT(config, "keyboard_port", keyboard, "octopus"));
	keyboard_port.rxd_handler().set(m_kb_uart, FUNC(i8251_device::write_rxd));
	clock_device &keyboard_clock_rx(CLOCK(config, "keyboard_clock_rx", 9600 * 64));
	keyboard_clock_rx.signal_handler().set(m_kb_uart, FUNC(i8251_device::write_rxc));
	clock_device &keyboard_clock_tx(CLOCK(config, "keyboard_clock_tx", 1200 * 64));
	keyboard_clock_tx.signal_handler().set(m_kb_uart, FUNC(i8251_device::write_txc));

	FD1793(config, m_fdc, 16_MHz_XTAL / 8);
	m_fdc->intrq_wr_callback().set(m_pic1, FUNC(pic8259_device::ir5_w));
	m_fdc->drq_wr_callback().set(m_dma2, FUNC(am9517a_device::dreq1_w));
	FLOPPY_CONNECTOR(config, m_floppy[0], octopus_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[1], octopus_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);
	SOFTWARE_LIST(config, "fd_list").set_original("octopus");

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(4.9152_MHz_XTAL / 2);  // DART channel A
	m_pit->out_handler<0>().set(FUNC(octopus_state::serial_clock_w));  // being able to write both Rx and Tx clocks at one time would be nice
	m_pit->set_clk<1>(4.9152_MHz_XTAL / 2);  // DART channel B
	m_pit->out_handler<1>().set(m_serial, FUNC(z80sio_device::rxtxcb_w));
	m_pit->set_clk<2>(4.9152_MHz_XTAL / 2);  // speaker frequency
	m_pit->out_handler<2>().set(FUNC(octopus_state::spk_freq_w));

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	Z80SIO(config, m_serial, 16_MHz_XTAL / 4); // clock rate not mentioned in tech manual
	m_serial->out_int_callback().set(m_pic1, FUNC(pic8259_device::ir1_w));
	m_serial->out_txda_callback().set("serial_a", FUNC(rs232_port_device::write_txd));
	m_serial->out_txdb_callback().set("serial_b", FUNC(rs232_port_device::write_txd));
	m_serial->out_rtsa_callback().set("serial_a", FUNC(rs232_port_device::write_rts));
	m_serial->out_rtsb_callback().set("serial_b", FUNC(rs232_port_device::write_rts));

	rs232_port_device &serial_a(RS232_PORT(config, "serial_a", default_rs232_devices, nullptr));
	serial_a.rxd_handler().set(m_serial, FUNC(z80sio_device::rxa_w));
	serial_a.cts_handler().set(m_serial, FUNC(z80sio_device::ctsa_w)).invert();
	//serial_a.ri_handler().set(m_serial, FUNC(z80sio_device::ria_w)).invert();
	rs232_port_device &serial_b(RS232_PORT(config, "serial_b", default_rs232_devices, nullptr));
	serial_b.rxd_handler().set(m_serial, FUNC(z80sio_device::rxb_w));
	serial_b.cts_handler().set(m_serial, FUNC(z80sio_device::ctsb_w)).invert();
	//serial_b.ri_handler().set(m_serial, FUNC(z80sio_device::rib_w)).invert();

	CENTRONICS(config, m_parallel, octopus_centronics_devices, "printer");
	m_parallel->busy_handler().set(FUNC(octopus_state::parallel_busy_w));
	m_parallel->select_handler().set(FUNC(octopus_state::parallel_slctout_w));
	// TODO: Winchester HD controller (Xebec/SASI compatible? uses TTL logic)

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(16_MHz_XTAL, 918, 0, 729, 350, 0, 325);
	//screen.set_raw(17.6_MHz_XTAL, 1008, 0, 792, 348, 0, 319);
	screen.set_screen_update("crtc", FUNC(scn2674_device::screen_update));

	SCN2674(config, m_crtc, 16_MHz_XTAL / 9); // dot clock and character width are both selectable
	m_crtc->intr_callback().set("pic_slave", FUNC(pic8259_device::ir0_w));
	m_crtc->set_character_width(9);
	m_crtc->set_display_callback(FUNC(octopus_state::display_pixels));
	m_crtc->set_addrmap(0, &octopus_state::octopus_vram);
	m_crtc->set_screen("screen");

	ADDRESS_MAP_BANK(config, m_z80_bankdev).set_map(&octopus_state::octopus_mem).set_options(ENDIANNESS_LITTLE, 8, 32, 0x10000);

	RAM(config, "ram").set_default_size("256K").set_extra_options("128K,512K,768K");
}

/* ROM definition */
ROM_START( octopus )
	ROM_REGION( 0x4000, "user1", 0 )
	ROM_LOAD( "octopus_main_prom", 0x0000, 0x4000, CRC(b5b4518d) SHA1(41b8729c4c9074914fd4ea181c8b6d4805ee2b93) )

	// This rom was on the graphics card (yes, it has slots)
	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "octopus_gfx_card",  0x0000, 0x2000, CRC(b2386534) SHA1(5e3c4682afb4eb222e48a7203269a16d26911836) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY                 FULLNAME       FLAGS
COMP( 1986, octopus, 0,      0,      octopus, octopus, octopus_state, empty_init, "Digital Microsystems", "LSI Octopus", MACHINE_NOT_WORKING)



odyssey2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Peter Trauner, Wilbert Pol, hap
/*******************************************************************************

Driver file to handle emulation of the Magnavox Odyssey 2 (stylized Odyssey²),
Philips Videopac G7000 and Philips Videopac+ G7400.

Magnavox was wholly owned by Philips at the time. The console had a limited
release late-1978 in Europe as the Philips G7000, but the launch was quickly
halted due to a hardware defect, wide release continued in 1979.

The 2 joysticks have no clear distinction between player 1 and 2, it differs
per game. And in MAME it's extra awkward due to the default input mapping
conflicting with the keyboard. An easy way to work around this if you're only
playing 1-player games, is to map both joysticks to the same inputs.

Videopac consoles:
- Philips Videopac G7000 (Europe, several models)
- Philips Videopac C52 (France)
- Philips Odyssey (Brazil)
- Magnavox Odyssey 2 (US)
- Radiola Jet 25 (France)
- Siera Videopac Computer G7000 (France)
- Schneider Videopac 7000 (Germany)
- Philips Videopac G7200 (Europe, Videopac with built-in B/W screen)
- Philips Videojeu N60 (France)

Videopac+ consoles:
- Philips Videopac+ G7400/G7401 (Europe)
- Magnavox Odyssey 3 Command Center (US, prototype)
- Brandt Jopac JO7400 (France)
- Schneider Videopac 74+ (Germany)

Odyssey 2/Videopac hardware notes:
- Intel 8048 (1KB internal ROM, 64 bytes internal RAM)
- 128 bytes RAM(6810)
- Intel 8244 for video and sound (8245 on PAL consoles)
- 2 joysticks(either hardwired, or connectors), keyboard

Videopac+ G7400 hardware notes:
- same base hardware
- Intel 8243 I/O expander
- EF9340 + EF9341 graphics chips + 6KB VRAM(3*2128, only 4KB used)
- larger keyboard

XTAL notes (differs per model):
- Odyssey 2: 7.15909MHz
- G7000: 17.734476MHz
- C52/N60: 17.812MHz
- G7200: 5.911MHz + 3.547MHz
- G7400: 5.911MHz + 8.867MHz
- JO7400: 5.911MHz + 3.5625MHz

TODO:
- backgamm doesn't draw all the chars/sprites, it does multiple screen updates
  and writes to the ptr/color registers, but does not increment the Y regs.
  Does it (ab)use an undocumented 8245 feature?
- G7400 helicopt sometimes locks up at the sea level, timing or IRQ related?
- testcartpl is French instead of English on G7400. It's due to inaccurate mcs48
  timer emulation. It sets up the timer to trigger an IRQ exactly 32 cycles later,
  but MAME is 2 cycles (or 1 opcode) too early, and a language check fails. It
  uses the internal timer, not the T1 timer, so it works reliably on the real
  console. Set bp 41a in the debugger to see.
- spaans has a keyboard debounce issue: if you push and hold a key after the game
  revealed the answer, it will be entered in the next input field. It's a prototype
  so it wouldn't be surprising if there are bugs, but this issue does not happen on
  a real Videopac or Odyssey 2. It's probably video timing related.
- screen resolution is not strictly defined, height(243) is correct, but
  horizontal overscan differs depending on monitor/tv? see syracuse for overscan
- 824x on the real console, overlapping major system characters with eachother
  (including transparent pixels) will cause glitches and instability, it can even
  overwrite the VDC color and pointer registers
  * gunfight: accidental usage, sometimes causes 1-frame glitches near bullet
  * powerlrd: occurs at pink mountain on the right, it's not 1:1 identical on MAME
  * several homebrews by Rafael: precisely placed overlap to force character
    color to change to white, see for example Piggyback Planet and Mean Santa
- 8245(PAL) video timing is not 100% accurate, though vtotal and htotal should
  be correct. The 8245 is put into slave mode at vblank, timing signals and
  vblank IRQ are taken over during it (the Videopac pcb even has extra TTL to
  catch the I/O read from 0xA1 to acknowledge the IRQ)
  * ppp(the tetris game) does not work properly on PAL, it does look like
    PAL/NTSC detection is working, see internal RAM $3D d7. So maybe it is due
    to inaccurate PAL video timing. The game does mid-scanline video updates.
  * gtwallst turns the display on too soon, the middle scroller is partially
    visible when it's not supposed to (also a bit glitchy on NTSC but not as bad)
- G7400 probably has different video timing too (not same as G7000)
- 4in1 and musician are not supposed to work on G7400, but work fine on MAME,
  caused by bus conflict or because they write to P2?
- according to tests, 8244 does not have a sound interrupt, but the Philips
  service test cartridge for 8245 tests for it and fails if it did not get an irq
- likewise, 8244 does not have a horizontal interrupt, but does 8245 have it?
- tests done on 8244 suggests that Y(0xa4) is latched when reading X, but that
  is inconsistent with the Philips service test cartridge: It reads X, Y, X,
  then waits for 1 scanline, and reads Y again. It expects Y to change. Latching Y
  will also cause video glitches to look different on some games when compared
  to the real console, for example powerlrd.
- verify odyssey3 cpu/video clocks
- problems with natural keyboard: videopacp has two enter keys, odyssey3 has
  alternate inputs for -, =, +
- partial screen updates aren't shown when using MAME's debugger, this is caused
  by a forced full screen update and a reset_partial_updates in emu/video.cpp.
  For the same reason, collision detection also won't work properly when stepping
  through the debugger

BTANB:
- a lot of PAL games have problems on NTSC (the other way around, not so much)
  * most-common cause is due to shorter vblank, less time to prepare frame
  * characters are not rendered near upper border on 8244 (eg. tutank, chezmxme)
- G7400 games don't look correct on odyssey3 and vice versa: ef934x graphics are
  placed lower on odyssey3
- Blackjack (Videopac 5) does not work on G7400, caused by a removed BIOS routine
- due to different XTAL ratio on Jopac JO7400, some games that do mid-screen video
  updates will have glitches on this machine, notably backgamm

Plenty games have minor bugs not worth mentioning here.

*******************************************************************************/

#include "emu.h"

#include "cpu/mcs48/mcs48.h"
#include "machine/i8243.h"
#include "video/ef9340_1.h"
#include "video/i8244.h"

#include "bus/odyssey2/slot.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class odyssey2_state : public driver_device
{
public:
	odyssey2_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_i8244(*this, "i8244"),
		m_screen(*this, "screen"),
		m_palette(*this, "palette"),
		m_cart(*this, "cartslot"),
		m_keyboard(*this, "KEY.%u", 0),
		m_joysticks(*this, "JOY.%u", 0)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(palette_changed) { adjust_palette(); }

	// Reset button is tied to 8048 RESET pin
	DECLARE_INPUT_CHANGED_MEMBER(reset_button) { m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE); }

	void odyssey2(machine_config &config);
	void videopac(machine_config &config);
	void videopacf(machine_config &config);

	void odyssey2_palette(palette_device &palette) const;

protected:
	required_device<i8048_device> m_maincpu;
	required_device<i8244_device> m_i8244;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<o2_cart_slot_device> m_cart;
	required_ioport_array<8> m_keyboard;
	required_ioport_array<2> m_joysticks;

	u8 m_ram[0x80];
	u8 m_p1 = 0xff;
	u8 m_p2 = 0xff;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void adjust_palette();

	virtual u8 io_read(offs_t offset);
	virtual void io_write(offs_t offset, u8 data);
	u8 bus_read();
	void p1_write(u8 data);
	u8 p2_read();
	void p2_write(u8 data);
	int t1_read();

	void odyssey2_io(address_map &map) ATTR_COLD;
	void odyssey2_mem(address_map &map) ATTR_COLD;

private:
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
};

class vpp_state : public odyssey2_state
{
public:
	vpp_state(const machine_config &mconfig, device_type type, const char *tag) :
		odyssey2_state(mconfig, type, tag),
		m_i8243(*this, "i8243"),
		m_ef934x(*this, "ef934x")
	{ }

	void g7400(machine_config &config);
	void jo7400(machine_config &config);
	void odyssey3(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

	virtual u8 io_read(offs_t offset) override;
	virtual void io_write(offs_t offset, u8 data) override;

private:
	required_device<i8243_device> m_i8243;
	required_device<ef9340_1_device> m_ef934x;

	u8 m_mix_i8244 = 0xff;
	u8 m_mix_ef934x = 0xff;
	u8 m_ef934x_extram[0x800];

	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void p2_write(u8 data);
	u8 io_vpp(offs_t offset, u8 data);
	template<int P> void i8243_port_w(u8 data);

	inline offs_t ef934x_extram_address(offs_t offset);
	u8 ef934x_extram_r(offs_t offset);
	void ef934x_extram_w(offs_t offset, u8 data);
};

void odyssey2_state::machine_start()
{
	memset(m_ram, 0, sizeof(m_ram));

	save_item(NAME(m_ram));
	save_item(NAME(m_p1));
	save_item(NAME(m_p2));
}

void odyssey2_state::machine_reset()
{
	adjust_palette();
}

void vpp_state::machine_start()
{
	odyssey2_state::machine_start();
	memset(m_ef934x_extram, 0, sizeof(m_ef934x_extram));

	save_item(NAME(m_mix_i8244));
	save_item(NAME(m_mix_ef934x));
	save_item(NAME(m_ef934x_extram));
}



/*******************************************************************************
    Video
*******************************************************************************/

constexpr rgb_t odyssey2_colors[] =
{
	// Background,Grid Dim
	{ 0x00, 0x00, 0x00 },   /* Black */                                         // i r g b
	{ 0x79, 0x00, 0x00 },   /* Red            - Calibrated To Real VideoPac */  // i R g b
	{ 0x00, 0x6d, 0x07 },   /* Green          - Calibrated To Real VideoPac */  // i r G b
	{ 0x77, 0x67, 0x0b },   /* Khaki          - Calibrated To Real VideoPac */  // i R g B
	{ 0x1a, 0x37, 0xbe },   /* Blue           - Calibrated To Real VideoPac */  // i r g B
	{ 0x94, 0x30, 0x9f },   /* Violet         - Calibrated To Real VideoPac */  // i R g B
	{ 0x2a, 0xaa, 0xbe },   /* Blue-Green     - Calibrated To Real VideoPac */  // i r G B
	{ 0xce, 0xce, 0xce },   /* Lt Grey */                                       // i R G B

	// Background,Grid Bright
	{ 0x67, 0x67, 0x67 },   /* Grey           - Calibrated To Real VideoPac */  // I R g B
	{ 0xc7, 0x51, 0x51 },   /* Lt Red         - Calibrated To Real VideoPac */  // I R g b
	{ 0x56, 0xc4, 0x69 },   /* Lt Green       - Calibrated To Real VideoPac */  // I R g B
	{ 0xc6, 0xb8, 0x6a },   /* Lt Yellow      - Calibrated To Real VideoPac */  // I R G b
	{ 0x5c, 0x80, 0xf6 },   /* Lt Blue        - Calibrated To Real VideoPac */  // I R g B
	{ 0xdc, 0x84, 0xe8 },   /* Lt Violet      - Calibrated To Real VideoPac */  // I R g B
	{ 0x77, 0xe6, 0xeb },   /* Lt Blue-Green  - Calibrated To Real VideoPac */  // I R g b
	{ 0xff, 0xff, 0xff }    /* White */                                         // I R G B
};

void odyssey2_state::odyssey2_palette(palette_device &palette) const
{
	palette.set_pen_colors(0, odyssey2_colors);
}

void odyssey2_state::adjust_palette()
{
	// JO7400 has an RGB port, on other consoles it's an optional homebrew modification
	if (ioport("CONF")->read() & 1)
		m_i8244->i8244_palette(*m_palette);
	else
		odyssey2_palette(*m_palette);
}

u32 odyssey2_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	m_i8244->screen_update(screen, bitmap, cliprect);

	u8 lum = ~m_p1 >> 4 & 0x08;

	// apply external LUM setting
	for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
		for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
			bitmap.pix(y, x) |= lum;

	return 0;
}

u32 vpp_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u8 lum = ~m_p1 >> 4 & 0x08;
	bitmap_ind16 *ef934x_bitmap = m_ef934x->get_bitmap();

	// apply external LUM setting
	for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
	{
		rectangle clip = cliprect;
		clip.min_y = clip.max_y = y;

		m_i8244->screen_update(screen, bitmap, clip);

		for (int x = clip.min_x; x <= clip.max_x; x++)
		{
			u16 d = bitmap.pix(y, x) & 7;
			u16 e = ef934x_bitmap->pix(y, x);

			// i8244 decoder enable is masked with cartridge pin B
			bool en = (e & 8) || !m_cart->b_read();
			e &= 7;

			// ef934x decoder output is tied to CX
			bool i2 = !BIT(m_mix_ef934x, e);
			m_i8244->write_cx(x, i2);

			if (en && BIT(m_mix_i8244, d))
			{
				// Use i8245 input
				bitmap.pix(y, x) |= lum;
			}
			else
			{
				// Use EF934x input
				bitmap.pix(y, x) = e | (i2 ? 8 : 0);
			}
		}
	}

	return 0;
}



/*******************************************************************************
    I/O
*******************************************************************************/

// 8048 ports

u8 odyssey2_state::io_read(offs_t offset)
{
	u8 data = m_cart->io_read(offset);

	// P14, A7: RAM _CS
	if (!(m_p1 & 0x10) && ~offset & 0x80)
		data &= m_ram[offset];

	// P13: 824x _CS, 824x RD is only enabled if P16 is low
	if ((m_p1 & 0x48) == 0)
		data &= m_i8244->read(offset);

	return data;
}

void odyssey2_state::io_write(offs_t offset, u8 data)
{
	// external write is only enabled if P16 is low
	if (!(m_p1 & 0x40))
	{
		m_cart->io_write(offset, data);
		if (!(m_p1 & 0x10) && ~offset & 0x80)
			m_ram[offset] = data;
	}

	if (!(m_p1 & 0x08))
		m_i8244->write(offset, data);
}

void odyssey2_state::p1_write(u8 data)
{
	// LUM changed
	if ((m_p1 ^ data) & 0x80)
		m_screen->update_now();

	m_p1 = data;
	m_cart->write_p1(m_p1 & 0x13);
}

u8 odyssey2_state::p2_read()
{
	u8 data = 0xff;

	if (!(m_p1 & 0x04))
	{
		// P12: 74156 keyboard decoder enable, 74156 inputs from P20-P22
		// 74148 priority encoder, GS to P24, outputs to P25-P27
		u8 inp = count_leading_zeros_32(m_keyboard[m_p2 & 0x07]->read()) - 24;
		if (inp < 8)
			data &= inp << 5 | 0xf;
	}

	return data;
}

void odyssey2_state::p2_write(u8 data)
{
	m_p2 = data;
	m_cart->write_p2(m_p2 & 0x0f);
}

u8 odyssey2_state::bus_read()
{
	u8 data = m_cart->bus_read();

	// same chip as keyboard
	if (!(m_p1 & 0x04))
	{
		u8 sel = m_p2 & 0x07;
		if (sel < 2)
			data &= ~m_joysticks[sel]->read();
	}

	return data;
}

int odyssey2_state::t1_read()
{
	return m_i8244->vblank() | m_i8244->hblank();
}


// G7400-specific

u8 vpp_state::io_read(offs_t offset)
{
	u8 data = odyssey2_state::io_read(offset);
	return io_vpp(offset, data);
}

void vpp_state::io_write(offs_t offset, u8 data)
{
	odyssey2_state::io_write(offset, data);
	io_vpp(offset, data);
}

u8 vpp_state::io_vpp(offs_t offset, u8 data)
{
	if (!(m_p1 & 0x20))
	{
		// A2 to R/W pin
		if (offset & 4)
			data &= m_ef934x->ef9341_read(offset & 0x02, offset & 0x01);
		else
			m_ef934x->ef9341_write(offset & 0x02, offset & 0x01, data);
	}

	return data;
}

void vpp_state::p2_write(u8 data)
{
	odyssey2_state::p2_write(data);
	m_i8243->p2_w(m_p2 & 0x0f);
}

template<int P>
void vpp_state::i8243_port_w(u8 data)
{
	// P4,P5: color mix I8244 side (IC674)
	// P6,P7: color mix EF9340 side (IC678)
	u8 mask = 0xf;
	if constexpr (~P & 1)
	{
		data <<= 4;
		mask <<= 4;
	}

	m_screen->update_now();

	if constexpr ((P & 2) != 0)
		m_mix_i8244 = (m_mix_i8244 & ~mask) | (data & mask);
	else
		m_mix_ef934x = (m_mix_ef934x & ~mask) | (data & mask);
}


// EF9341 extended RAM

offs_t vpp_state::ef934x_extram_address(offs_t offset)
{
	u8 latch = (offset >> 12 & 0x80) | (offset >> 4 & 0x7f);
	u16 address = (latch & 0x1f) | (offset << 9 & 0x200) | (latch << 3 & 0x400);

	if (offset & 8)
		return address | (latch & 0x60);
	else
		return address | (offset << 4 & 0x60) | (latch << 2 & 0x180);
}

u8 vpp_state::ef934x_extram_r(offs_t offset)
{
	return m_ef934x_extram[ef934x_extram_address(offset)];
}

void vpp_state::ef934x_extram_w(offs_t offset, u8 data)
{
	m_ef934x_extram[ef934x_extram_address(offset)] = data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void odyssey2_state::odyssey2_mem(address_map &map)
{
	map(0x0000, 0x03ff).rom();
	map(0x0400, 0x0bff).r(m_cart, FUNC(o2_cart_slot_device::read_rom04));
	map(0x0c00, 0x0fff).r(m_cart, FUNC(o2_cart_slot_device::read_rom0c));
}

void odyssey2_state::odyssey2_io(address_map &map)
{
	map(0x00, 0xff).rw(FUNC(odyssey2_state::io_read), FUNC(odyssey2_state::io_write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( o2 )
	PORT_START("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7')

	PORT_START("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('?')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')

	PORT_START("KEY.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR('+')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')

	PORT_START("KEY.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')

	PORT_START("KEY.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.')

	PORT_START("KEY.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR('-')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"×") PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR('*')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"÷") PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR('/')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y / Yes") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N / No") PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Clear") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)

	PORT_START("KEY.6")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("KEY.7")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("JOY.0") // left
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP)     PORT_PLAYER(1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT)  PORT_PLAYER(1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN)   PORT_PLAYER(1)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT)   PORT_PLAYER(1)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON1)         PORT_PLAYER(1)
	PORT_BIT(0xe0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("JOY.1") // right
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP)     PORT_PLAYER(2)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT)  PORT_PLAYER(2)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN)   PORT_PLAYER(2)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT)   PORT_PLAYER(2)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON1)         PORT_PLAYER(2)
	PORT_BIT(0xe0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(odyssey2_state::reset_button), 0)

	PORT_START("CONF")
	PORT_CONFNAME( 0x01, 0x00, "Color Output" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(odyssey2_state::palette_changed), 0)
	PORT_CONFSETTING(    0x00, "RF" )
	PORT_CONFSETTING(    0x01, "RGB" )
INPUT_PORTS_END

static INPUT_PORTS_START( vpp )
	PORT_INCLUDE( o2 )

	PORT_MODIFY("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0  #") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('#')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1  !") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2  \"") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"3  £") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR(U'£')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4  $") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5  %") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6  &") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7  '") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_MODIFY("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8  (") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9  )") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')

	PORT_MODIFY("KEY.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"+  \u2191") PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR('+') PORT_CHAR(UCHAR_MAMEKEY(UP)) // U+2191 = ↑
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')

	PORT_MODIFY("KEY.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')

	PORT_MODIFY("KEY.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')

	PORT_MODIFY("KEY.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"-  \u2193") PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR('-') PORT_CHAR(UCHAR_MAMEKEY(DOWN)) // U+2193 = ↓
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"×  \u2196") PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(U'×') PORT_CHAR(UCHAR_MAMEKEY(HOME)) // U+2196 = ↖
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"÷  \u2190") PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR(U'÷') PORT_CHAR(UCHAR_MAMEKEY(LEFT)) // U+2190 = ←
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"=  \u2192") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) // U+2192 = →
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y / Yes") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N / No") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Clear  ;") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_CHAR(';')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Enter  _") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(10) PORT_CHAR('_')

	PORT_MODIFY("KEY.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Ret") PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13) // clash with KEY.5 0x80
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(":  *") PORT_CODE(KEYCODE_COLON) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("|  @") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('|') PORT_CHAR('@')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("]  [") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('[')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"¨  ^") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(U'¨') PORT_CHAR('^')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(",  /") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('/')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("<  >") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('<') PORT_CHAR('>')

	PORT_MODIFY("KEY.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(PAUSE))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Cntl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
INPUT_PORTS_END

static INPUT_PORTS_START( o3 )
	PORT_INCLUDE( vpp )

	PORT_MODIFY("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0  )") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1  !") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2  @") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3  #") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6  ^") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7  &") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')

	PORT_MODIFY("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8  *") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9  (") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/  ?") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_MODIFY("KEY.4")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(".  >") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')

	PORT_MODIFY("KEY.5")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Clear") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)

	PORT_MODIFY("KEY.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("]  }") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(",  <") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(";  :") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("-  _") PORT_CODE(KEYCODE_PGDN) // clash with KEY.5 0x01
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("'  \"") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('\"')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("[  {") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("=  +") PORT_CODE(KEYCODE_PGUP) // clash with KEY.2 0x01 and KEY.5 0x08

	PORT_MODIFY("KEY.7")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void odyssey2_state::odyssey2(machine_config &config)
{
	// basic machine hardware
	I8048(config, m_maincpu, (7.15909_MHz_XTAL * 3) / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &odyssey2_state::odyssey2_mem);
	m_maincpu->set_addrmap(AS_IO, &odyssey2_state::odyssey2_io);
	m_maincpu->p1_out_cb().set(FUNC(odyssey2_state::p1_write));
	m_maincpu->p2_in_cb().set(FUNC(odyssey2_state::p2_read));
	m_maincpu->p2_out_cb().set(FUNC(odyssey2_state::p2_write));
	m_maincpu->bus_in_cb().set(FUNC(odyssey2_state::bus_read));
	m_maincpu->bus_out_cb().set(m_cart, FUNC(o2_cart_slot_device::bus_write));
	m_maincpu->t0_in_cb().set(m_cart, FUNC(o2_cart_slot_device::t0_read));
	m_maincpu->t1_in_cb().set(FUNC(odyssey2_state::t1_read));

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_screen_update(FUNC(odyssey2_state::screen_update));
	m_screen->set_video_attributes(VIDEO_ALWAYS_UPDATE);
	m_screen->set_palette("palette");

	PALETTE(config, "palette", FUNC(odyssey2_state::odyssey2_palette), 16);

	I8244(config, m_i8244, 7.15909_MHz_XTAL / 2);
	m_i8244->set_screen("screen");
	m_i8244->set_screen_size(360, 243);
	m_i8244->irq_cb().set_inputline(m_maincpu, MCS48_INPUT_IRQ);
	m_i8244->add_route(ALL_OUTPUTS, "mono", 0.25);

	SPEAKER(config, "mono").front_center();

	// cartridge
	O2_CART_SLOT(config, m_cart, o2_cart, nullptr).set_must_be_loaded(true);
	SOFTWARE_LIST(config, "cart_list").set_original("videopac").set_filter("O2");
}

void odyssey2_state::videopac(machine_config &config)
{
	odyssey2(config);

	// PAL video chip
	I8245(config.replace(), m_i8244, 17.734476_MHz_XTAL / 5);
	m_i8244->set_screen("screen");
	m_i8244->set_screen_size(360, 243);
	m_i8244->irq_cb().set_inputline(m_maincpu, MCS48_INPUT_IRQ);
	m_i8244->add_route(ALL_OUTPUTS, "mono", 0.25);

	m_maincpu->set_clock(17.734476_MHz_XTAL / 3);

	subdevice<software_list_device>("cart_list")->set_filter("VP");
}

void odyssey2_state::videopacf(machine_config &config)
{
	videopac(config);

	// different master XTAL
	m_maincpu->set_clock(17.812_MHz_XTAL / 3);
	m_i8244->set_clock(17.812_MHz_XTAL / 5);
}


void vpp_state::g7400(machine_config &config)
{
	// basic machine hardware
	I8048(config, m_maincpu, 5.911_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &vpp_state::odyssey2_mem);
	m_maincpu->set_addrmap(AS_IO, &vpp_state::odyssey2_io);
	m_maincpu->p1_out_cb().set(FUNC(vpp_state::p1_write));
	m_maincpu->p2_in_cb().set(FUNC(vpp_state::p2_read));
	m_maincpu->p2_out_cb().set(FUNC(vpp_state::p2_write));
	m_maincpu->bus_in_cb().set(FUNC(vpp_state::bus_read));
	m_maincpu->bus_out_cb().set(m_cart, FUNC(o2_cart_slot_device::bus_write));
	m_maincpu->t0_in_cb().set(m_cart, FUNC(o2_cart_slot_device::t0_read));
	m_maincpu->t1_in_cb().set(FUNC(vpp_state::t1_read));
	m_maincpu->prog_out_cb().set(m_i8243, FUNC(i8243_device::prog_w));

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_screen_update(FUNC(vpp_state::screen_update));
	m_screen->set_video_attributes(VIDEO_ALWAYS_UPDATE);
	m_screen->set_palette("palette");

	PALETTE(config, "palette", FUNC(odyssey2_state::odyssey2_palette), 16);

	I8243(config, m_i8243);
	m_i8243->p4_out_cb().set(FUNC(vpp_state::i8243_port_w<0>));
	m_i8243->p5_out_cb().set(FUNC(vpp_state::i8243_port_w<1>));
	m_i8243->p6_out_cb().set(FUNC(vpp_state::i8243_port_w<2>));
	m_i8243->p7_out_cb().set(FUNC(vpp_state::i8243_port_w<3>));

	EF9340_1(config, m_ef934x, (8.867_MHz_XTAL * 2) / 5, "screen");
	m_ef934x->set_offsets(15, 5);
	m_ef934x->read_exram().set(FUNC(vpp_state::ef934x_extram_r));
	m_ef934x->write_exram().set(FUNC(vpp_state::ef934x_extram_w));

	I8245(config, m_i8244, (8.867_MHz_XTAL * 2) / 5);
	m_i8244->set_screen("screen");
	m_i8244->set_screen_size(360, 243);
	m_i8244->irq_cb().set_inputline(m_maincpu, MCS48_INPUT_IRQ);
	m_i8244->add_route(ALL_OUTPUTS, "mono", 0.25);

	SPEAKER(config, "mono").front_center();

	// cartridge
	O2_CART_SLOT(config, m_cart, o2_cart, nullptr).set_must_be_loaded(true);
	SOFTWARE_LIST(config, "cart_list").set_original("videopac").set_filter("VPP");
}

void vpp_state::jo7400(machine_config &config)
{
	g7400(config);

	// different video clock
	m_i8244->set_clock(3.5625_MHz_XTAL);
	m_ef934x->set_clock(3.5625_MHz_XTAL);
}

void vpp_state::odyssey3(machine_config &config)
{
	g7400(config);

	// NTSC video chip
	I8244(config.replace(), m_i8244, 7.15909_MHz_XTAL / 2);
	m_i8244->set_screen("screen");
	m_i8244->set_screen_size(360, 243);
	m_i8244->irq_cb().set_inputline(m_maincpu, MCS48_INPUT_IRQ);
	m_i8244->add_route(ALL_OUTPUTS, "mono", 0.25);

	m_ef934x->set_clock(7.15909_MHz_XTAL / 2);
	m_ef934x->set_offsets(15, 15);

	m_maincpu->set_clock((7.15909_MHz_XTAL * 3) / 4);

	subdevice<software_list_device>("cart_list")->set_filter("O3");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START (videopac)
	ROM_REGION(0x0400, "maincpu", 0)
	ROM_LOAD("o2bios.rom", 0x0000, 0x0400, CRC(8016a315) SHA1(b2e1955d957a475de2411770452eff4ea19f4cee))
ROM_END

ROM_START (odyssey2)
	ROM_REGION(0x0400, "maincpu", 0)
	ROM_LOAD("o2bios.rom", 0x0000, 0x0400, CRC(8016a315) SHA1(b2e1955d957a475de2411770452eff4ea19f4cee))
ROM_END

ROM_START (videopacf)
	ROM_REGION(0x0400, "maincpu", 0)
	ROM_LOAD("c52.rom", 0x0000, 0x0400, CRC(a318e8d6) SHA1(a6120aed50831c9c0d95dbdf707820f601d9452e))
ROM_END


ROM_START (videopacp)
	ROM_REGION(0x0400, "maincpu", 0)
	ROM_LOAD("g7400.bin", 0x0000, 0x0400, CRC(e20a9f41) SHA1(5130243429b40b01a14e1304d0394b8459a6fbae))
ROM_END

ROM_START (jopac)
	ROM_REGION(0x0400, "maincpu", 0)
	ROM_LOAD("jopac.bin", 0x0000, 0x0400, CRC(11647ca5) SHA1(54b8d2c1317628de51a85fc1c424423a986775e4))
ROM_END

ROM_START (odyssey3)
	ROM_REGION(0x0400, "maincpu", 0)
	ROM_LOAD("odyssey3.bin", 0x0000, 0x0400, CRC(e2b23324) SHA1(0a38c5f2cea929d2fe0a23e5e1a60de9155815dc))
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT     COMPAT  MACHINE    INPUT  CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1978, videopac,  0,         0,      videopac,  o2,    odyssey2_state, empty_init, "Philips", "Videopac G7000 (Europe)", MACHINE_SUPPORTS_SAVE )
SYST( 1979, videopacf, videopac,  0,      videopacf, o2,    odyssey2_state, empty_init, "Philips", "Videopac C52 (France)", MACHINE_SUPPORTS_SAVE )
SYST( 1979, odyssey2,  videopac,  0,      odyssey2,  o2,    odyssey2_state, empty_init, "Magnavox", "Odyssey 2 (US)", MACHINE_SUPPORTS_SAVE )

SYST( 1983, videopacp, 0,         0,      g7400,     vpp,   vpp_state,      empty_init, "Philips", "Videopac+ G7400 (Europe)", MACHINE_SUPPORTS_SAVE )
SYST( 1983, jopac,     videopacp, 0,      jo7400,    vpp,   vpp_state,      empty_init, "Philips (Brandt license)", "Jopac JO7400 (France)", MACHINE_SUPPORTS_SAVE )
SYST( 1983, odyssey3,  videopacp, 0,      odyssey3,  o3,    vpp_state,      empty_init, "Philips", "Odyssey 3 Command Center (US, prototype)", MACHINE_SUPPORTS_SAVE )



okean240.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/*******************************************************************************************

Okeah-240 (Ocean-240).

2011-12-28 Skeleton driver.

Info from EMU80:

intctl : K580wn59
  irq[0]=kbd.irq2
  irq[1]=kbd.irq
  irq[4]=tim.out[0]

ppa40 : K580ww55
  portA=kbd.pressed.mask
  portB[2]=cas.playback
  portB[5]=kbd.key[58]
  portB[6]=kbd.key[59]
  portC[0-3]=kbd.pressed.row
  portC[4]=kbd.ack

ppaC0 : K580ww55
  portA=vid.scroll.y
  portB[0-3]=mem.frame[0].page
  portB[1-3]=mem.frame[1].page
  portB[4-5]=mm.page
  portC=vid.scroll.x

If you leave out the CPM80 roms, the machine will start up in a machine-language monitor.

Okean240: MONITOR 240/7  known commands:
A ?
B ?
D dump
F fill
G go
L load (via uart)
M move
R ?
S substitute
W ?
X registers

Okean240a: Turbo MONITOR by Alex Z. There are several emulation bugs noticed. Known commands:
H hex arithmetic
J Jamp (set address?)
M move
P ? (locks up)
R read
W write
tab switches between the hex and ascii sides
^C refresh display

NOTE ABOUT THE TEST ROM (okean240t):
- You need to press a key every so often, or hold down Insert.


ToDo:
- Find out if any unconnected keyboard entries are real keys
- Arrow keys (used in Turbo Monitor)
- Colours?
- Sound? (perhaps port E4 bit 3)
- Floppy disks and devices
- Cassette?
- Add memory banking (perhaps port C1)
- 80 column mode (used in Turbo Monitor)

Keyboard:
- okean240  - external ascii keyboard
- okean240a - internal keyboard
- okean240t - serial keyboard & screen

**********************************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/keyboard.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/timer.h"

#include "screen.h"
#include "emupal.h"


namespace {

class okean240_state : public driver_device
{
public:
	okean240_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_p_videoram(*this, "videoram")
		, m_io_keyboard(*this, "X%d", 0U)
		, m_io_modifiers(*this, "MODIFIERS")
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_ppikbd(*this, "ppikbd")
		, m_pic(*this, "pic")
	{ }

	void okean240a(machine_config &config);
	void okean240t(machine_config &config);
	void okean240(machine_config &config);

private:
	u8 okean240_port40_r();
	u8 okean240_port41_r();
	void okean240_port42_w(u8 data);
	u8 okean240a_port40_r();
	u8 okean240a_port41_r();
	u8 okean240a_port42_r();
	void okean240_porte2_w(u8 data);
	void kbd_put(u8 data);
	u32 screen_update_okean240(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void okean240_io(address_map &map) ATTR_COLD;
	void okean240_mem(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	TIMER_DEVICE_CALLBACK_MEMBER(timer_k);
	u8 m_term_data = 0U;
	u8 m_j = 0U;
	u8 m_scroll = 0U;
	u8 m_tog = 0U;
	bool m_key_pressed = false;
	u8 m_kbd_row = 0U;
	required_shared_ptr<u8> m_p_videoram;
	optional_ioport_array<11> m_io_keyboard;
	optional_ioport m_io_modifiers;
	memory_passthrough_handler m_rom_shadow_tap;
	required_device<i8080_cpu_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_device<i8255_device> m_ppikbd;
	required_device<pic8259_device> m_pic;
};


// okean240/t: process ascii keyboard
void okean240_state::kbd_put(u8 data)
{
	m_term_data = data;
	m_pic->ir1_w(1);
}

// okean240/t: port 40 (get ascii key)
u8 okean240_state::okean240_port40_r()
{
	u8 ret = m_term_data;
	m_term_data = 0;
	m_pic->ir1_w(0);
	return ret;
}

// okean240t: port 41 bit 1 (test rom status bit) @E0DB, E0E2
u8 okean240_state::okean240_port41_r()
{
	m_tog ^= 2;
	return m_tog;
}

// okean240a keyboard routines
TIMER_DEVICE_CALLBACK_MEMBER( okean240_state::timer_k )
{
	if (m_key_pressed)
		return;

	for (u8 i = 0; i < 11; i++)
	{
		if (m_io_keyboard[i]->read())
		{
			m_key_pressed = true;
			m_kbd_row = i;
			m_pic->ir1_w(1);
			return;
		}
	}
}


// port 40 (get a single key press)
u8 okean240_state::okean240a_port40_r()
{
	u8 j = m_io_keyboard[m_kbd_row]->read();
	if (j)
	{
		if (j==m_j) return 0;
		m_j=j;
		return j;
	}
	m_j=0;
	return 0;
}

// port 41 bits 6&7 (modifier keys)
u8 okean240_state::okean240a_port41_r()
{
	return m_io_modifiers->read();
}

// port 42 (get a row)
u8 okean240_state::okean240a_port42_r()
{
	return m_kbd_row;
}

// This is a keyboard acknowledge pulse, it goes high then
// straightaway low, if reading port 40 indicates a key is pressed.
// okean240: data bit 7
// okean240a: data bit 4
void okean240_state::okean240_port42_w(u8 data)
{
	m_pic->ir1_w(0);
	m_key_pressed = false;
}

void okean240_state::okean240_porte2_w(u8 data)
{
	m_pic->ir4_w(!BIT(data, 3));
}


void okean240_state::okean240_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).ram().share("mainram");
	map(0x4000, 0x7fff).ram().share("videoram");
	map(0x8000, 0xbfff).ram();
	map(0xc000, 0xffff).rom().region("maincpu", 0);
}

void okean240_state::okean240_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x20, 0x25).nopw();
	map(0x40, 0x43).rw(m_ppikbd, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x60, 0x63).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x80, 0x81).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0xa0, 0xa1).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xc0, 0xc3).rw("ppic", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xe0, 0xe3).rw("ppie", FUNC(i8255_device::read), FUNC(i8255_device::write));
	// map(0x00, 0x1f)=ppa00.data
	// map(0x20, 0x23)=dsk.data
	// map(0x24, 0x24)=dsk.wait
	// map(0x25, 0x25)=dskctl.data
	// map(0x40, 0x5f)=ppa40.data
	// map(0x60, 0x7f)=tim.data
	// map(0x80, 0x81)=intctl.data
	// map(0xa0, 0xa1)=comport.data
	// map(0xc0, 0xdf)=ppaC0.data
	// map(0xe0, 0xff)=ppaE0.data
}


/* Input ports */
static INPUT_PORTS_START( okean240 )
INPUT_PORTS_END


static INPUT_PORTS_START( okean240a )
	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED) // comma
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED) // minus
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED) // null
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED) // null
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED) // null
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("num7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("num8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("num9") PORT_CODE(KEYCODE_9_PAD)

	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED) // null
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED) // null
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED) // null
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("num0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("numdel") PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("numenter") PORT_CODE(KEYCODE_ENTER_PAD)

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("@ `") PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)        PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)        PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)        PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED) // null
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("num1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("num2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("num3") PORT_CODE(KEYCODE_3_PAD)

	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED) //9E
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1 !") PORT_CODE(KEYCODE_1)      PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)        PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)        PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("^ ~") PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("num4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("num5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("num6") PORT_CODE(KEYCODE_6_PAD)

	PORT_START("X4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED) //81
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2 \"") PORT_CODE(KEYCODE_2)     PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)        PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)        PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)        PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED) // plus
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED) //7F
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED) //03

	PORT_START("X5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED) //86
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3 #") PORT_CODE(KEYCODE_3)      PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)        PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)        PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)        PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE)    PORT_CHAR(8)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED) //99
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED) //8B

	PORT_START("X6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4 $") PORT_CODE(KEYCODE_4)      PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)        PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)        PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)        PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)    PORT_CHAR(' ')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED) //84
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER)    PORT_CHAR(13)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("X7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED) //92 prints # and line feed
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5 %") PORT_CODE(KEYCODE_5)      PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)        PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)        PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)        PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED) //98
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED) //85 cr and line feed
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("_") PORT_CODE(KEYCODE_END)

	PORT_START("X8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED) //83 cancel input
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6 &") PORT_CODE(KEYCODE_6)      PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)        PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)        PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)        PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(": *") PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("- =") PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-') PORT_CHAR('=')

	PORT_START("X9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7 '") PORT_CODE(KEYCODE_7)      PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("[ {") PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)        PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)        PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED) //93
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("\\ |") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)        PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)        PORT_CHAR('0')

	PORT_START("X10")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8 (") PORT_CODE(KEYCODE_8)      PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("] }") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)        PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("; +") PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)        PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)        PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9 )") PORT_CODE(KEYCODE_9)      PORT_CHAR('9') PORT_CHAR(')')

	PORT_START("MODIFIERS")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
INPUT_PORTS_END


void okean240_state::machine_start()
{
	save_item(NAME(m_term_data));
	save_item(NAME(m_j));
	save_item(NAME(m_scroll));
	save_item(NAME(m_tog));
	save_item(NAME(m_key_pressed));
	save_item(NAME(m_kbd_row));
}


void okean240_state::machine_reset()
{
	m_term_data = 0;
	m_j = 0;
	m_scroll = 0;
	m_key_pressed = false;

	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x0000, 0x07ff, m_rom+0x2000);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0xe000, 0xe7ff,
			"rom_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// reinstall RAM over the ROM shadow
					m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x07ff, m_ram);
				}
			},
			&m_rom_shadow_tap);
}

u32 okean240_state::screen_update_okean240(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (u16 y = 0; y < 256; y++)
	{
		u8 const ma = y + m_scroll; // ma must be 8-bit
		u16 *p = &bitmap.pix(y);

		for (u16 x = 0; x < 0x4000; x+=0x200)
		{
			u8 const gfx = m_p_videoram[x|ma] | m_p_videoram[x|ma|0x100];

			/* Display a scanline of a character */
			*p++ = BIT(gfx, 0);
			*p++ = BIT(gfx, 1);
			*p++ = BIT(gfx, 2);
			*p++ = BIT(gfx, 3);
			*p++ = BIT(gfx, 4);
			*p++ = BIT(gfx, 5);
			*p++ = BIT(gfx, 6);
			*p++ = BIT(gfx, 7);
		}
	}
	return 0;
}


/* F4 Character Displayer */
static const gfx_layout okean240_charlayout =
{
	8, 7,                   /* 8 x 7 characters */
	160,                    /* 160 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8 },
	8*7                 /* every char takes 7 bytes */
};

static GFXDECODE_START( gfx_okean240 )
	GFXDECODE_ENTRY( "maincpu", 0x2c08, okean240_charlayout, 0, 1 )
GFXDECODE_END

static GFXDECODE_START( gfx_okean240a )
	GFXDECODE_ENTRY( "maincpu", 0x2f63, okean240_charlayout, 0, 1 )
GFXDECODE_END


void okean240_state::okean240t(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, XTAL(12'000'000) / 6);
	m_maincpu->set_addrmap(AS_PROGRAM, &okean240_state::okean240_mem);
	m_maincpu->set_addrmap(AS_IO, &okean240_state::okean240_io);
	m_maincpu->in_inta_func().set("pic", FUNC(pic8259_device::acknowledge));

	i8251_device &uart(I8251(config, "uart", 0));
	uart.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	uart.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	uart.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("uart", FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set("uart", FUNC(i8251_device::write_dsr));
	rs232.cts_handler().set("uart", FUNC(i8251_device::write_cts));

	I8255(config, m_ppikbd);
	m_ppikbd->in_pa_callback().set(FUNC(okean240_state::okean240_port40_r));
	m_ppikbd->in_pb_callback().set(FUNC(okean240_state::okean240_port41_r));
	m_ppikbd->out_pc_callback().set(FUNC(okean240_state::okean240_port42_w));

	i8255_device &ppic(I8255(config, "ppic"));
	ppic.out_pa_callback().set([this] (u8 data) { m_scroll = data; });

	i8255_device &ppie(I8255(config, "ppie"));
	ppie.out_pc_callback().set(FUNC(okean240_state::okean240_porte2_w));

	pit8253_device &pit(PIT8253(config, "pit", 0));
	pit.set_clk<1>(3072000); // artificial rate
	pit.out_handler<1>().set("uart", FUNC(i8251_device::write_txc));
	pit.out_handler<1>().append("uart", FUNC(i8251_device::write_rxc));

	PIC8259(config, m_pic, 0);
	m_pic->out_int_callback().set_inputline(m_maincpu, 0);

	/* video hardware */
	screen_device &screen1(SCREEN(config, "screen1", SCREEN_TYPE_RASTER));
	screen1.set_refresh_hz(50);
	screen1.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen1.set_size(256, 256);
	screen1.set_visarea(0, 255, 0, 255);
	screen1.set_screen_update(FUNC(okean240_state::screen_update_okean240));
	screen1.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);
}

void okean240_state::okean240a(machine_config &config)
{
	okean240t(config);
	GFXDECODE(config, "gfxdecode", "palette", gfx_okean240a);
	subdevice<rs232_port_device>("rs232")->set_default_option(nullptr); // not used for keyboard

	m_ppikbd->in_pa_callback().set(FUNC(okean240_state::okean240a_port40_r));
	m_ppikbd->in_pb_callback().set(FUNC(okean240_state::okean240a_port41_r));
	m_ppikbd->in_pc_callback().set(FUNC(okean240_state::okean240a_port42_r));

	subdevice<pit8253_device>("pit")->set_clk<1>(1536000); // artificial rate
	TIMER(config, "timer_k").configure_periodic(FUNC(okean240_state::timer_k), attotime::from_hz(300)); // keyb scan
}

void okean240_state::okean240(machine_config &config)
{
	okean240t(config);
	GFXDECODE(config, "gfxdecode", "palette", gfx_okean240);
	subdevice<rs232_port_device>("rs232")->set_default_option(nullptr); // not used for keyboard
	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(okean240_state::kbd_put));
}

/* ROM definition */
ROM_START( okean240 )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "monitor.bin", 0x2000, 0x2000, CRC(587799bc) SHA1(1f677afa96722ca4ed2643eaca243548845fc854) )
	ROM_LOAD( "cpm80.bin",   0x0000, 0x2000, CRC(7378e4f9) SHA1(c3c06c6f2e953546452ca6f82140a79d0e4953b4) )
ROM_END

ROM_START( okean240a )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "fddmonitor.bin", 0x2000, 0x2000, CRC(bcac5ca0) SHA1(602ab824704d3d5d07b3787f6227ff903c33c9d5) )
	ROM_LOAD( "fddcpm80.bin",   0x0000, 0x2000, CRC(b89a7e16) SHA1(b8f937c04f430be18e48f296ed3ef37194171204) )
ROM_END

ROM_START( okean240t )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "test.bin",    0x2000, 0x0800, CRC(e9e2b7b9) SHA1(e4e0b6984a2514b6ba3e97500d487ea1a68b7577) )
ROM_END

} // anonymous namespace

/* Driver */

//    YEAR  NAME       PARENT    COMPAT  MACHINE    INPUT      CLASS           INIT           COMPANY      FULLNAME              FLAGS
COMP( 1986, okean240,  0,        0,      okean240,  okean240,  okean240_state, empty_init, "<unknown>", "Okeah-240",          MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )
COMP( 1986, okean240a, okean240, 0,      okean240a, okean240a, okean240_state, empty_init, "<unknown>", "Ocean-240 with FDD", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )
COMP( 1986, okean240t, okean240, 0,      okean240t, okean240,  okean240_state, empty_init, "<unknown>", "Ocean-240 Test ROM", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



olivpc1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Carl, rfka01

/*************************************************** Olivetti Prodest PC 1 ***

The PC 1 could be considered the Italian equivalent to the German Schneider Euro PC.
Form factor: home computer (keyboard, main unit and floppy drive in one case)
CPU: NEC V40 4.77/8MHz, software switchable, RAM: 256K/512K, expandable to 640K over the bus, ROM: 16K
Chipset: Video V6355D, M5L8255AP-5, Floppy: WD37C65B-PL, Keyboard: MBL8042H
OSC: 21.477270,  16.000
Keyboard: 83 keys, 10 function keys
Mass storage: 1 or 2 3.5" DS/DD floppy disk drive(s) 720K, external 5.25" DS/DD drive (D:) instead of the internal drive B: possible
There's a switch that activates the external drive chain. Drives are assigned their parameters via the DOS mechanism (DRIVPARM/DRIVER.SYS)
Video: CGA, with two monitor connectors (RGB/CGA and Ext. Video/SCART)
Mouse: Cursor emulation, on board: parallel, serial, speaker, audio connector("HIFI-Sound"), IBM compatible expansion bus, external 360K floppy drive
Options (from the manual): "BOX with a single slot of medium-small dimensions" which could house RAM, LAN, Modem, EGA, 3.5" harddisk,
Music Box, TV/teletext adapter

Olivetti Prodest PC 1 HD:  Different motherboard
http://www.ti99iuc.it/web/_upload/image/PC1-OnePage/Docs/GuidaCFModPC1HD/pc1CF-IDE-Guide.pdf
- on board XTA (8-bit IDE) port that was able to connect to either a Conner CP4024XT oder CP4044XT harddisk.
- 768K RAM of which 640K are accessible
Chipset: Video: V6355D, Floppy: WD37C65JM, VLSI ???43559, WD8250NJM, AMD N8255A-5, Keyboard: MBL8042H
OSC: 16.000, 21.477270, 1.8432

*****************************************************************************/

#include "emu.h"
#include "cpu/nec/v5x.h"
#include "bus/isa/isa.h"
#include "bus/isa/isa_cards.h"
#include "bus/isa/fdc.h"
#include "machine/pckeybrd.h"
#include "softlist_dev.h"
#include "machine/ram.h"
#include "machine/upd765.h"
#include "machine/bankdev.h"
#include "imagedev/floppy.h"
#include "sound/spkrdev.h"
#include "speaker.h"


namespace {

class olivpc1_state : public driver_device
{
public:
	olivpc1_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_isa(*this, "isa"),
		m_speaker(*this, "speaker"),
		m_keyboard(*this, "pc_keyboard"),
		m_fdc(*this, "fdc"),
		m_bank(*this, "bank")
	{ }
	void pc8_io(address_map &map) ATTR_COLD;
	void pc8_map(address_map &map) ATTR_COLD;
	void bank(address_map &map) ATTR_COLD;
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;
	void olivpc1(machine_config &config);
private:
	u8 port6x_r(offs_t addr);
	void port6x_w(offs_t addr, u8 data);
	void nmi();
	required_device<v40_device> m_maincpu;
	required_device<isa8_device> m_isa;
	required_device<speaker_sound_device> m_speaker;
	required_device<pc_keyboard_device> m_keyboard;
	required_device<wd37c65c_device> m_fdc;
	required_device<address_map_bank_device> m_bank;
	bool m_obf = false;
};

void olivpc1_state::machine_start()
{
	ram_device *ram = subdevice<ram_device>(RAM_TAG);
	m_bank->space(AS_PROGRAM).install_ram(0, 0x4000 - 1, ram->pointer());
	m_maincpu->space(AS_PROGRAM).install_ram(0x4000, ram->size() - 1, ram->pointer() + 0x4000);
}

void olivpc1_state::machine_reset()
{
	m_keyboard->enable(1);
	m_bank->set_bank(0);
}

void olivpc1_state::pc8_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x03fff).m(m_bank, FUNC(address_map_bank_device::amap8));
	map(0xfc000, 0xfffff).rom().region("bios", 0);
}

void olivpc1_state::bank(address_map &map)
{
	map.unmap_value_high();
	map(0x4000, 0x7fff).rom().region("bios", 0);
}

void olivpc1_state::pc8_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).lrw8(NAME([this]() { nmi(); return 0xff; }), NAME([this](u8 d) { nmi(); }));
	map(0x0060, 0x0067).rw(FUNC(olivpc1_state::port6x_r), FUNC(olivpc1_state::port6x_w));
	map(0x03f2, 0x03f2).w("fdc", FUNC(wd37c65c_device::dor_w));
	map(0x03f4, 0x03f5).m("fdc", FUNC(wd37c65c_device::map));
}

static INPUT_PORTS_START(olivpc1)
INPUT_PORTS_END

// TODO: replace with 8042 dump
u8 olivpc1_state::port6x_r(offs_t addr)
{
	switch(addr)
	{
		case 0:
			m_maincpu->set_input_line(INPUT_LINE_IRQ1, CLEAR_LINE);
			m_obf = false;
			return m_keyboard->read();
		case 4:
			return m_obf;
	}
	return 0xff;
}

void olivpc1_state::port6x_w(offs_t addr, u8 data)
{
	switch(addr)
	{
		case 7:
			//m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
			//m_bank->set_bank(0);
			break;
	}
}

void olivpc1_state::nmi()
{
	//m_bank->set_bank(1);
	//m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
}

static void pc1_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
	device.option_add("525dd", FLOPPY_525_DD);
}

void olivpc1_state::olivpc1(machine_config &config)
{
	V40(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &olivpc1_state::pc8_map);
	m_maincpu->set_addrmap(AS_IO, &olivpc1_state::pc8_io);
	m_maincpu->set_tclk(14.318181_MHz_XTAL / 12);
	m_maincpu->out_hreq_cb().set_inputline(m_maincpu, INPUT_LINE_HALT);
	m_maincpu->out_hreq_cb().append(m_maincpu, FUNC(v40_device::hack_w));
	m_maincpu->out_eop_cb().set([this](int state) { m_fdc->tc_w(state); });
	m_maincpu->in_memr_cb().set([this](offs_t offset) { return m_maincpu->space(AS_PROGRAM).read_byte(offset); });
	m_maincpu->out_memw_cb().set([this](offs_t offset, u8 data) { m_maincpu->space(AS_PROGRAM).write_byte(offset, data); });
	m_maincpu->in_ior_cb<0>().set([this]() { return m_isa->dack_r(0); });
	m_maincpu->out_iow_cb<0>().set([this](u8 data) { m_isa->dack_w(0, data); });
	m_maincpu->in_ior_cb<1>().set("fdc", FUNC(wd37c65c_device::dma_r));
	m_maincpu->out_iow_cb<1>().set("fdc", FUNC(wd37c65c_device::dma_w));
	m_maincpu->in_ior_cb<2>().set([this]() { return m_isa->dack_r(2); });
	m_maincpu->out_iow_cb<2>().set([this](u8 data) { m_isa->dack_w(2, data); });

	ADDRESS_MAP_BANK(config, m_bank).set_map(&olivpc1_state::bank).set_options(ENDIANNESS_LITTLE, 8, 15, 0x4000);

	WD37C65C(config, m_fdc, 16_MHz_XTAL);
	m_fdc->intrq_wr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ6);
	m_fdc->drq_wr_callback().set(m_maincpu, FUNC(v40_device::dreq_w<1>));
	FLOPPY_CONNECTOR(config, "fdc:0", pc1_floppies, "35dd", isa8_fdc_device::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", pc1_floppies, nullptr, isa8_fdc_device::floppy_formats).enable_sound(true);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.5);

	PC_KEYB(config, m_keyboard);
	m_keyboard->keypress().set([this](int state) { m_obf = state; m_maincpu->set_input_line(INPUT_LINE_IRQ1, state); });

	isa8_device &isa(ISA8(config, "isa", 0));
	isa.set_memspace("maincpu", AS_PROGRAM);
	isa.set_iospace("maincpu", AS_IO);
	isa.drq1_callback().set(m_maincpu, FUNC(v40_device::dreq_w<0>));
	isa.drq3_callback().set(m_maincpu, FUNC(v40_device::dreq_w<2>));
	isa.irq2_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ2);
	isa.irq3_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ3);
	isa.irq4_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ4);
	isa.irq5_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ5);
	isa.iochck_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);

	ISA8_SLOT(config, "intcga", 0, isa, pc_isa8_cards, "cga", true);
	ISA8_SLOT(config, "isa1", 0, isa, pc_isa8_cards, nullptr, false);
	RAM(config, RAM_TAG).set_default_size("512K").set_extra_options("128K, 256K, 384K");

	SOFTWARE_LIST(config, "pc_list").set_compatible("ibm5150");
}

ROM_START( olivpc1 )
	ROM_REGION(0x4000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "rev121", "Rev. 1.21")
	ROMX_LOAD("pc1_bios_1_21.bin", 0x0000, 0x4000, CRC(3c44cdbf) SHA1(46e6c8531ad1fe81cf4457f1edb7554a0eaed7e8), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "rev106", "Rev. 1.06 20/08/87")
	ROMX_LOAD("pc1_bios_1_06.bin", 0x0000, 0x4000, CRC(679d2235) SHA1(621d0abafcc1ee7edc090c01ca496f056c184410), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "rev107", "Rev. 1.07 12/10/88")
	ROMX_LOAD("pc1_bios_1_07.bin", 0x0000, 0x4000, CRC(3b84757c) SHA1(a2288ecc7616767d3ea5dd3262bfad98cf1b39d3), ROM_BIOS(2))

	ROM_REGION(0x2000, "gfx1", 0)
	ROM_LOAD("xu4600_pc1_prodest_font101.bin", 0x0000, 0x2000, CRC(5c21981e) SHA1(d0db791079ece9c9e50bb7f38b5b11024ba7ec99))
ROM_END

} // anonymous namespace


COMP( 1987, olivpc1,        ibm5150, 0,      olivpc1,        olivpc1,    olivpc1_state, empty_init,      "Olivetti",                        "Prodest PC 1",          MACHINE_NOT_WORKING )



olyboss.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Gabriele D'Antona
/*
    Olympia BOSS
    Made in Germany around 1981

    The BOSS series was not a great success, as its members differed too much to be compatible:
    First they were 8085 based, later machines used a Z80A.

    Other distinguishing features were the capacity of the disk drives:

    BOSS A: Two 128K floppy drives
    BOSS B: Two 256K disk drives
    BOSS C: Two 600K disk drives
    BOSS D: One 600K disk drive, one 5 MB harddisk
    BOSS M: M for multipost, up to four BOSS machines linked together for up to 20MB shared harddisk space

    Olympia favoured the French Prologue operating system over CPM (cf. Olympia People PC) and supplied BAL
    as a programming language with it.

    Video is 80x28

    There are no service manuals available (or no documentation in general), so everything is guesswork.

    - Ports 0x80 and 0x81 seem to be related to the graphics chip and cursor position
    The rom outs value 0x81 to port 0x81 and then the sequence <column> <row> (?) to port 0x80

    - The machine boots up and shows "BOSS .." on the screen. Every keystroke is repeated on screen.
    If you press <return>, the machine seems to go into a boot sequence (from the HD, probably)

    The harddisk controller is based on a MSC-9056.

    Links: http://www.old-computers.com/museum/computer.asp?c=95
*/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "cpu/i8085/i8085.h"
#include "imagedev/floppy.h"
#include "machine/keyboard.h"
#include "video/upd3301.h"
#include "machine/i8257.h"
#include "machine/i8255.h"
#include "machine/am9519.h"
#include "machine/upd765.h"
#include "machine/pic8259.h"
#include "machine/i8251.h"
#include "screen.h"


namespace {

#define UPD3301_TAG     "upd3301"
#define I8257_TAG       "i8257"
#define SCREEN_TAG      "screen"

//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class olyboss_state : public driver_device
{
public:
	olyboss_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_dma(*this, I8257_TAG)
		, m_crtc(*this, UPD3301_TAG)
		, m_fdc(*this, "fdc")
		, m_uic(*this, "uic")
		, m_pic(*this, "pic")
		, m_ppi(*this, "ppi")
		, m_fdd0(*this, "fdc:0")
		, m_fdd1(*this, "fdc:1")
		, m_rom(*this, "mainrom")
		, m_lowram(*this, "lowram")
		, m_char_rom(*this, UPD3301_TAG)
	{ }

public:
	void bossa85(machine_config &config);
	void bossb85(machine_config &config);
	void olybossb(machine_config &config);
	void olybossc(machine_config &config);
	void olybossd(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	TIMER_CALLBACK_MEMBER(toggle_tim);

private:
	u8 keyboard_read();

	UPD3301_DRAW_CHARACTER_MEMBER( olyboss_display_pixels );

	void hrq_w(int state);
	void tc_w(int state);
	void romdis_w(int state);
	u8 dma_mem_r(offs_t offset);
	void dma_mem_w(offs_t offset, u8 data);
	u8 fdcctrl_r();
	void fdcctrl_w(u8 data);
	void fdcctrl85_w(u8 data);
	u8 fdcdma_r();
	void fdcdma_w(u8 data);
	void crtcdma_w(u8 data);
	u8 rom_r(offs_t offset);
	void rom_w(offs_t offset, u8 data);
	void vchrmap_w(offs_t offset, u8 data);
	void vchrram_w(offs_t offset, u8 data);
	void vchrram85_w(offs_t offset, u8 data);
	void ppic_w(u8 data);
	void olyboss_io(address_map &map) ATTR_COLD;
	void olyboss_mem(address_map &map) ATTR_COLD;
	void olyboss85_io(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<i8257_device> m_dma;
	required_device<upd3301_device> m_crtc;
	required_device<upd765a_device> m_fdc;
	optional_device<am9519_device> m_uic;
	optional_device<pic8259_device> m_pic;
	optional_device<i8255_device> m_ppi;
	required_device<floppy_connector> m_fdd0;
	optional_device<floppy_connector> m_fdd1;
	required_memory_region m_rom;
	required_shared_ptr<u8> m_lowram;
	required_memory_region m_char_rom;

	bool m_keybhit;
	u8 m_keystroke;
	void keyboard_put(u8 data);
	void keyboard85_put(u8 data);
	u8 m_fdcctrl, m_fdctype;
	u8 m_channel, m_vchrmap, m_vchrpage;
	u16 m_vchraddr;
	u8 m_vchrram[0x800];
	bool m_romen, m_timstate;
	emu_timer *m_timer;
};

void olyboss_state::machine_reset()
{
	m_keybhit = false;
	m_romen = true;
	m_timstate = false;

	m_fdcctrl = 0;
	m_vchrmap = 0;
	m_vchrpage = 0;
	m_timer->adjust(attotime::from_hz(30), 0, attotime::from_hz(30)); // unknown timer freq, possibly com2651 BRCLK
}

TIMER_CALLBACK_MEMBER(olyboss_state::toggle_tim)
{
	m_timstate = !m_timstate;
	if(m_pic)
		m_pic->ir0_w(m_timstate);
	else
		m_uic->ireq7_w(m_timstate);
}

//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void olyboss_state::olyboss_mem(address_map &map)
{
	map(0x0000, 0x7ff).rw(FUNC(olyboss_state::rom_r), FUNC(olyboss_state::rom_w)).share("lowram");
	map(0x800, 0xffff).ram();
}

void olyboss_state::olyboss_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x08).rw(m_dma, FUNC(i8257_device::read), FUNC(i8257_device::write));
	map(0x10, 0x11).m(m_fdc, FUNC(upd765a_device::map));
	//map(0x20, 0x20) //beeper?
	map(0x30, 0x30).rw(m_uic, FUNC(am9519_device::data_r), FUNC(am9519_device::data_w));
	map(0x31, 0x31).rw(m_uic, FUNC(am9519_device::stat_r), FUNC(am9519_device::cmd_w));
	map(0x40, 0x43).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
	//map(0x50, 0x53) COM2651
	map(0x60, 0x60).rw(FUNC(olyboss_state::fdcctrl_r), FUNC(olyboss_state::fdcctrl_w));
	map(0x80, 0x81).rw(m_crtc, FUNC(upd3301_device::read), FUNC(upd3301_device::write));
	map(0x82, 0x84).w(FUNC(olyboss_state::vchrmap_w));
	map(0x90, 0x9f).w(FUNC(olyboss_state::vchrram_w));
}

void olyboss_state::olyboss85_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x0, 0x8).rw(m_dma, FUNC(i8257_device::read), FUNC(i8257_device::write));
	map(0x10, 0x11).m(m_fdc, FUNC(upd765a_device::map));
	map(0x20, 0x21).rw(m_crtc, FUNC(upd3301_device::read), FUNC(upd3301_device::write));
	map(0x30, 0x31).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x42, 0x42).r(FUNC(olyboss_state::keyboard_read));
	map(0x42, 0x44).w(FUNC(olyboss_state::vchrram85_w));
	map(0x45, 0x45).w(FUNC(olyboss_state::fdcctrl85_w));
}

static INPUT_PORTS_START( olyboss )
	PORT_START("DSW")
INPUT_PORTS_END

u8 olyboss_state::rom_r(offs_t offset)
{
	return m_romen ?  m_rom->as_u8(offset) : m_lowram[offset];
}

void olyboss_state::rom_w(offs_t offset, u8 data)
{
	m_lowram[offset] = data;
}

void olyboss_state::vchrram85_w(offs_t offset, u8 data)
{
	switch(offset)
	{
		case 0:
			m_vchraddr = (m_vchraddr & 0x00f) | (data << 4);
			break;
		case 1:
			m_vchraddr = (m_vchraddr & 0xff0) | (data & 0xf);
			break;
		case 2:
			m_vchrram[m_vchraddr] = data;
			break;
	}
}

void olyboss_state::vchrmap_w(offs_t offset, u8 data)
{
	switch(offset)
	{
		case 0:
			m_vchrmap = data;
			break;
		case 2:
			m_vchrpage = data & 0x7f;
			break;
	}
}

void olyboss_state::vchrram_w(offs_t offset, u8 data)
{
	m_vchrram[(m_vchrpage << 4) + (offset ^ 0xf)] = data;
}

void olyboss_state::romdis_w(int state)
{
	m_romen = state ? false : true;
}

//**************************************************************************
//  VIDEO
//**************************************************************************

UPD3301_DRAW_CHARACTER_MEMBER( olyboss_state::olyboss_display_pixels )
{
	u8 data = cc & 0x7f;
	if(cc & 0x80)
		data = m_vchrram[(data << 4) | lc];
	else
		data = m_char_rom->base()[(data << 4) | lc];

	//if (lc >= 8) return;
	if (csr)
		data = 0xff;

	for (int i = 0; i < 8; i++)
	{
		int color = BIT(data, 7);
		bitmap.pix(y, (sx * 8) + i) = color ? 0xffffff : 0;
		data <<= 1;
	}
}

//**************************************************************************
//  KEYBOARD
//**************************************************************************

u8 olyboss_state::keyboard_read()
{
	//logerror ("keyboard_read offs [%d]\n",offset);
	if (m_keybhit)
	{
		m_keybhit=false;
		if(m_pic)
			m_pic->ir1_w(CLEAR_LINE);
		return m_keystroke;
	}
	return 0x00;
}

void olyboss_state::ppic_w(u8 data)
{
	m_uic->ireq4_w(BIT(data, 5) ? CLEAR_LINE : ASSERT_LINE);
	m_fdcctrl = (m_fdcctrl & ~0x10) | (BIT(data, 5) ? 0x10 : 0);
}

void olyboss_state::machine_start()
{
	m_timer = timer_alloc(FUNC(olyboss_state::toggle_tim), this);
	const char *type = m_fdd0->get_device()->shortname();
	if(!strncmp(type, "floppy_525_qd", 13))
		m_fdctype = 0xa0;
	else
		m_fdctype = 0x80;
}

void olyboss_state::keyboard_put(u8 data)
{
	if (data)
	{
		//logerror("Keyboard stroke [%2x]\n",data);
		m_keystroke=data ^ 0xff;
		m_keybhit=true;
		m_ppi->pc4_w(ASSERT_LINE);
		m_ppi->pc4_w(CLEAR_LINE);
	}
}

void olyboss_state::keyboard85_put(u8 data)
{
	if(data)
	{
		m_pic->ir1_w(ASSERT_LINE);
		m_keybhit = true;
		m_keystroke = data;
	}
}

/* 8257 Interface */

void olyboss_state::hrq_w(int state)
{
	//logerror("hrq_w\n");
	m_maincpu->set_input_line(INPUT_LINE_HALT,state);
	m_dma->hlda_w(state);
}

void olyboss_state::tc_w(int state)
{
	if((m_channel == 0) && state)
	{
		m_fdc->tc_w(1);
		m_fdc->tc_w(0);
	}
}

u8 olyboss_state::dma_mem_r(offs_t offset)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	return program.read_byte(offset);
}

void olyboss_state::dma_mem_w(offs_t offset, u8 data)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.write_byte(offset, data);
}

u8 olyboss_state::fdcdma_r()
{
	m_channel = 0;
	return m_fdc->dma_r();
}

void olyboss_state::fdcdma_w(u8 data)
{
	m_channel = 0;
	m_fdc->dma_w(data);
}

void olyboss_state::crtcdma_w(u8 data)
{
	m_channel = 2;
	m_crtc->dack_w(data);
}

u8 olyboss_state::fdcctrl_r()
{
	return m_fdcctrl | m_fdctype; // 0xc0 seems to indicate an 8" drive, 0x80 a 5.25" dd drive, 0xa0 a 5.25" qd drive
}

void olyboss_state::fdcctrl_w(u8 data)
{
	m_fdcctrl = data;
	m_romen = (m_fdcctrl & 1) ? false : true;
	m_fdd0->get_device()->mon_w(!(data & 2));
	if(m_fdd1)
		m_fdd1->get_device()->mon_w(!(data & 4));
}

void olyboss_state::fdcctrl85_w(u8 data)
{
	m_fdcctrl = data;
	m_fdd0->get_device()->mon_w(!(data & 0x40));
	if(m_fdd1)
		m_fdd1->get_device()->mon_w(!(data & 0x80));
}

static void bossa_floppies(device_slot_interface &device)
{
	device.option_add("525ssdd", FLOPPY_525_SSDD);
}

static void bossb_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

static void bosscd_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

void olyboss_state::olybossd(machine_config &config)
{
	Z80(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &olyboss_state::olyboss_mem);
	m_maincpu->set_addrmap(AS_IO, &olyboss_state::olyboss_io);
	m_maincpu->set_irq_acknowledge_callback("uic", FUNC(am9519_device::iack_cb));

	/* video hardware */

	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_refresh_hz(60);
	screen.set_screen_update(UPD3301_TAG, FUNC(upd3301_device::screen_update));
	screen.set_size(80*8, 28*11);
	screen.set_visarea(0, (80*8)-1, 0, (28*11)-1);

	/* devices */

	AM9519(config, m_uic, 0);
	m_uic->out_int_callback().set_inputline("maincpu", 0);

	UPD765A(config, m_fdc, 8'000'000, true, true);
	m_fdc->intrq_wr_callback().set(m_uic, FUNC(am9519_device::ireq2_w)).invert();
	m_fdc->drq_wr_callback().set(m_dma, FUNC(i8257_device::dreq0_w));
	FLOPPY_CONNECTOR(config, m_fdd0, bosscd_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	m_fdd0->enable_sound(true);

	I8257(config, m_dma, XTAL(4'000'000));
	m_dma->out_hrq_cb().set(FUNC(olyboss_state::hrq_w));
	m_dma->in_memr_cb().set(FUNC(olyboss_state::dma_mem_r));
	m_dma->out_memw_cb().set(FUNC(olyboss_state::dma_mem_w));
	m_dma->in_ior_cb<0>().set(FUNC(olyboss_state::fdcdma_r));
	m_dma->out_iow_cb<0>().set(FUNC(olyboss_state::fdcdma_w));
	m_dma->out_iow_cb<2>().set(FUNC(olyboss_state::crtcdma_w));
	m_dma->out_tc_cb().set(FUNC(olyboss_state::tc_w));

	UPD3301(config, m_crtc, XTAL(14'318'181));
	m_crtc->set_character_width(8);
	m_crtc->set_display_callback(FUNC(olyboss_state::olyboss_display_pixels));
	m_crtc->set_attribute_fetch_callback(m_crtc, FUNC(upd3301_device::default_attr_fetch));
	m_crtc->drq_wr_callback().set(m_dma, FUNC(i8257_device::dreq2_w));
	m_crtc->int_wr_callback().set(m_uic, FUNC(am9519_device::ireq0_w)).invert();
	m_crtc->set_screen(SCREEN_TAG);

	I8255(config, m_ppi);
	m_ppi->in_pa_callback().set(FUNC(olyboss_state::keyboard_read));
	m_ppi->out_pc_callback().set(FUNC(olyboss_state::ppic_w));

	/* keyboard */
	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(olyboss_state::keyboard_put));
}

void olyboss_state::olybossb(machine_config &config)
{
	olybossd(config);
	config.device_remove("fdc:0");
	FLOPPY_CONNECTOR(config, "fdc:0", bossb_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", bossb_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
}

void olyboss_state::olybossc(machine_config &config)
{
	olybossd(config);
	FLOPPY_CONNECTOR(config, "fdc:1", bosscd_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
}

void olyboss_state::bossb85(machine_config &config)
{
	i8085a_cpu_device &maincpu(I8085A(config, m_maincpu, 4_MHz_XTAL));
	maincpu.set_addrmap(AS_PROGRAM, &olyboss_state::olyboss_mem);
	maincpu.set_addrmap(AS_IO, &olyboss_state::olyboss85_io);
	maincpu.in_inta_func().set(m_pic, FUNC(pic8259_device::acknowledge));
	maincpu.out_sod_func().set(FUNC(olyboss_state::romdis_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_refresh_hz(60);
	screen.set_screen_update(UPD3301_TAG, FUNC(upd3301_device::screen_update));
	screen.set_size(80*8, 28*11);
	screen.set_visarea(0, (80*8)-1, 0, (28*11)-1);

	/* devices */

	PIC8259(config, m_pic, 0);
	m_pic->out_int_callback().set_inputline(m_maincpu, 0);

	UPD765A(config, m_fdc, 8'000'000, true, true);
	m_fdc->intrq_wr_callback().set_inputline(m_maincpu, I8085_RST65_LINE);
	m_fdc->drq_wr_callback().set(m_dma, FUNC(i8257_device::dreq0_w));
	FLOPPY_CONNECTOR(config, "fdc:0", bossb_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", bossb_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	I8257(config, m_dma, XTAL(4'000'000));
	m_dma->out_hrq_cb().set(FUNC(olyboss_state::hrq_w));
	m_dma->in_memr_cb().set(FUNC(olyboss_state::dma_mem_r));
	m_dma->out_memw_cb().set(FUNC(olyboss_state::dma_mem_w));
	m_dma->in_ior_cb<0>().set(FUNC(olyboss_state::fdcdma_r));
	m_dma->out_iow_cb<0>().set(FUNC(olyboss_state::fdcdma_w));
	m_dma->out_iow_cb<2>().set(FUNC(olyboss_state::crtcdma_w));
	m_dma->out_tc_cb().set(FUNC(olyboss_state::tc_w));

	UPD3301(config, m_crtc, XTAL(14'318'181));
	m_crtc->set_character_width(8);
	m_crtc->set_display_callback(FUNC(olyboss_state::olyboss_display_pixels));
	m_crtc->set_attribute_fetch_callback(m_crtc, FUNC(upd3301_device::default_attr_fetch));
	m_crtc->drq_wr_callback().set(m_dma, FUNC(i8257_device::dreq2_w));
	m_crtc->int_wr_callback().set_inputline("maincpu", I8085_RST75_LINE);
	m_crtc->set_screen(SCREEN_TAG);

	/* keyboard */
	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(olyboss_state::keyboard85_put));
}

void olyboss_state::bossa85(machine_config &config)
{
	bossb85(config);
	FLOPPY_CONNECTOR(config.replace(), "fdc:0", bossa_floppies, "525ssdd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config.replace(), "fdc:1", bossa_floppies, "525ssdd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
}

//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************
ROM_START( bossa85 )
	ROM_REGION(0x800, "mainrom", ROMREGION_ERASEFF)
	ROM_LOAD( "boss_8085_bios.bin", 0x0000, 0x800, CRC(43030231) SHA1(a1f6546a9dc1066324e93e5eed886f2313678180) )

	ROM_REGION( 0x800, UPD3301_TAG, 0)
	ROM_LOAD( "olympia_boss_graphics_251-461.bin", 0x0000, 0x800, CRC(56149540) SHA1(b2b893bd219308fc98a38528beb7ddae391c7609) )
ROM_END

ROM_START( bossb85 )
	ROM_REGION(0x800, "mainrom", ROMREGION_ERASEFF)
	ROM_LOAD( "boss_8085_bios.bin", 0x0000, 0x800, CRC(43030231) SHA1(a1f6546a9dc1066324e93e5eed886f2313678180) )

	ROM_REGION( 0x800, UPD3301_TAG, 0)
	ROM_LOAD( "olympia_boss_graphics_251-461.bin", 0x0000, 0x800, CRC(56149540) SHA1(b2b893bd219308fc98a38528beb7ddae391c7609) )
ROM_END

ROM_START( olybossb )                                           // verified: BOSS B uses the same ROMs as D, so C is safe to assume as well
	ROM_REGION(0x800, "mainrom", ROMREGION_ERASEFF)
	ROM_LOAD( "olympia_boss_system_251-462.bin", 0x0000, 0x800, CRC(01b99609) SHA1(07b764c36337c12f7b40aa309b0805ceed8b22e2) )

	ROM_REGION( 0x800, UPD3301_TAG, 0)
	ROM_LOAD( "olympia_boss_graphics_251-461.bin", 0x0000, 0x800, CRC(56149540) SHA1(b2b893bd219308fc98a38528beb7ddae391c7609) )
ROM_END

ROM_START( olybossc )
	ROM_REGION(0x800, "mainrom", ROMREGION_ERASEFF)
	ROM_LOAD( "olympia_boss_system_251-462.bin", 0x0000, 0x800, CRC(01b99609) SHA1(07b764c36337c12f7b40aa309b0805ceed8b22e2) )

	ROM_REGION( 0x800, UPD3301_TAG, 0)
	ROM_LOAD( "olympia_boss_graphics_251-461.bin", 0x0000, 0x800, CRC(56149540) SHA1(b2b893bd219308fc98a38528beb7ddae391c7609) )
ROM_END

ROM_START( olybossd )
	ROM_REGION(0x800, "mainrom", ROMREGION_ERASEFF)
	ROM_LOAD( "olympia_boss_system_251-462.bin", 0x0000, 0x800, CRC(01b99609) SHA1(07b764c36337c12f7b40aa309b0805ceed8b22e2) )

	ROM_REGION( 0x800, UPD3301_TAG, 0)
	ROM_LOAD( "olympia_boss_graphics_251-461.bin", 0x0000, 0x800, CRC(56149540) SHA1(b2b893bd219308fc98a38528beb7ddae391c7609) )
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//   YEAR  NAME      PARENT    COMPAT  MACHINE   INPUT    CLASS          INIT        COMPANY                  FULLNAME               FLAGS
COMP(1981, bossa85,  olybossd, 0,      bossa85,  olyboss, olyboss_state, empty_init, "Olympia International", "Olympia BOSS A 8085", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP(1981, bossb85,  olybossd, 0,      bossb85,  olyboss, olyboss_state, empty_init, "Olympia International", "Olympia BOSS B 8085", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP(1981, olybossb, olybossd, 0,      olybossb, olyboss, olyboss_state, empty_init, "Olympia International", "Olympia BOSS B",      MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP(1981, olybossc, olybossd, 0,      olybossc, olyboss, olyboss_state, empty_init, "Olympia International", "Olympia BOSS C",      MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP(1981, olybossd, 0,        0,      olybossd, olyboss, olyboss_state, empty_init, "Olympia International", "Olympia BOSS D",      MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



olytext.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Robbbert
/*

    If you want to "own" this, go ahead, it's yours.

    Olympia Olytext 20 Screen-Typing System
    Made in Germany

    Major Chips: Z80A, Z80PIO x2, Z80SCC, WD1772, National Semiconductor NS405-B18N
    Crystals: 16.000, 18.000
    Piezo-Beeper
    RAM: 128k dynamic (16x 4164), 8k static (4x MB8128-10)
    Keyboard: 3870 with 4.000 crystal, 3 dipswitches

    The NS405 is a Terminal Management Processor which provides complete video functionality.
    It is modelled on the 8048, and requires an external rom. All other functions (cpu, timing,
    character generation, uart, dma, crtc, etc) are internal. Data is sent to it in parallel
    from the pio, something like a printer, but there's no handshake. Apparently the TMP is
    fast enough to accept all data as it arrives.

*/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/wd_fdc.h"
#include "machine/keyboard.h"
#include "emupal.h"
#include "screen.h"


namespace {

//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class olytext_state : public driver_device
{
public:
	olytext_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
//      , m_fdd0(*this, "fdc:0"),
//      , m_fdd1(*this, "fdc:1"),
//      , m_rom(*this, "mainrom"),
//      , m_lowram(*this, "lowram"),
	{ }

	void olytext(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void keyboard_put(u8 data);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
//  required_device<floppy_connector> m_fdd0;
//  optional_device<floppy_connector> m_fdd1;
//  required_memory_region m_rom;
//  required_shared_ptr<u8> m_lowram;
};

void olytext_state::machine_reset()
{
}


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void olytext_state::mem_map(address_map &map)
{
	map(0x0000, 0x0fff).rom();
	map(0x1000, 0xffff).ram();
}

void olytext_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x20, 0x23); // pio?  data, ctrl, data, ctrl // all text is sent as ascii (including control codes) to 0x20
	map(0x30, 0x30); // .w (device)  .r(get keyboard status, if bit 0 high then read keyboard data from port 0x31)
	map(0x32, 0x32); // .w (device)
	map(0x40, 0x40); // .w (banking?)
}


static INPUT_PORTS_START( olytext )
INPUT_PORTS_END


//**************************************************************************
//  VIDEO
//**************************************************************************

uint32_t olytext_state::screen_update( screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect )
{
	return 0;
}


//**************************************************************************
//  KEYBOARD
//**************************************************************************

void olytext_state::machine_start()
{
	//m_timer = timer_alloc();
	//const char *type = m_fdd0->get_device()->shortname();
	//if(!strncmp(type, "floppy_525_qd", 13))
		//m_fdctype = 0xa0;
	//else
		//m_fdctype = 0x80;
}

void olytext_state::keyboard_put(u8 data)
{
//  if (data)
//  {
//      m_keystroke=data;
//  }
}

//void olytext_state::fdcctrl_w(uint8_t data)
//{
//  m_fdcctrl = data;
//  m_romen = (m_fdcctrl & 1) ? false : true;
//  m_fdd0->get_device()->mon_w(!(data & 2));
//  if(m_fdd1)
//      m_fdd1->get_device()->mon_w(!(data & 4));
//}

static void olytext_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}


//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

void olytext_state::olytext(machine_config &config)
{
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &olytext_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &olytext_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_size(80*8, 28*11);
	screen.set_visarea(0, (80*8)-1, 0, (28*11)-1);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(olytext_state::screen_update));
	//screen.set_palette("palette");
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* devices */
	WD1772(config, "fdc", 16_MHz_XTAL / 8); // divisor guess
	FLOPPY_CONNECTOR(config, "fdc:0", olytext_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", olytext_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	/* keyboard */
	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(olytext_state::keyboard_put));
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( olytext )
	ROM_REGION( 0x1000, "maincpu", 0)
	ROM_LOAD( "1220-040_700.bin",   0x0000, 0x1000, CRC(1df4b31f) SHA1(af3a275d9853d8d23ef091549d7c659cbdb257f3) )

	ROM_REGION( 0x1000, "chargen", 0) // this is firmware for the NS405 cpu
	ROM_LOAD( "1220-041_33702.bin", 0x0000, 0x1000, CRC(a6d39c2a) SHA1(b7a4c65edc7d46d1ab8b0b3aa52141c61c66bf32) )
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//   YEAR  NAME      PARENT    COMPAT  MACHINE   INPUT    CLASS          INIT        COMPANY                  FULLNAME               FLAGS
COMP(1985, olytext,  0,       0,      olytext,  olytext, olytext_state, empty_init, "Olympia International", "Olympia Olytext 20", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



omar.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Tryom Omar, handheld backgammon computer

Hardware notes (Omar I):
- PCB label: AP0379 REV.8, PC103, JRH
- Fairchild 3870 MCU, custom label
- 9-digit 7seg led panel (3 unused), piezo

Omar II has an LCD instead of 7segs. PCB label is AN2379 REV.6, PC101.

There are more 'sequels', but assumed to be the same game. The newer ones didn't
include a backgammon board.

LED versions: Omar I, Omar IV, Cardinal Electronic Backgammon
LCD versions: Omar II, Omar III, Omar V, Tandy Computerized Backgammon

Confirmed to be the same MCU ROM: Omar II = Omar V.

BTANB:
- omar1 piezo buzzes when you hold down a button

*******************************************************************************/

#include "emu.h"

#include "cpu/f8/f8.h"
#include "machine/f3853.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

#include "multibyte.h"

#include "omar1.lh"
#include "omar2.lh"


namespace {

// Omar I / shared

class omar_state : public driver_device
{
public:
	omar_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_psu(*this, "psu"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void omar1(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

protected:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<f38t56_device> m_psu;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<5> m_inputs;

	u8 m_inp_mux = 0;

	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	void input1_w(u8 data);
	void input2_w(u8 data);
	void input3_w(u8 data);
	void input4_w(u8 data);
	u8 input_r();

	virtual void display1_w(u8 data);
	virtual void display2_w(u8 data);
};

void omar_state::machine_start()
{
	save_item(NAME(m_inp_mux));
}


// Omar II

class omar2_state : public omar_state
{
public:
	omar2_state(const machine_config &mconfig, device_type type, const char *tag) :
		omar_state(mconfig, type, tag)
	{ }

	void omar2(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	u8 m_lcd_com = 0;
	u8 m_lcd_data[4] = { };

	void update_lcd();

	virtual void display1_w(u8 data) override;
	virtual void display2_w(u8 data) override;
	void display3_w(u8 data);
	void display4_w(u8 data);
};

void omar2_state::machine_start()
{
	omar_state::machine_start();

	save_item(NAME(m_lcd_com));
	save_item(NAME(m_lcd_data));
}



/*******************************************************************************
    I/O
*******************************************************************************/

// common

void omar_state::input1_w(u8 data)
{
	// P00: input mux part
	m_inp_mux = (m_inp_mux & ~2) | (data << 1 & 2);

	// P07: speaker out
	m_dac->write(BIT(~data, 7));
}

void omar_state::input2_w(u8 data)
{
	// P11: input mux part
	m_inp_mux = (m_inp_mux & ~1) | (data >> 1 & 1);
}

void omar_state::input3_w(u8 data)
{
	// P40,P41: input mux part
	m_inp_mux = (m_inp_mux & ~0xc) | (data << 2 & 0xc);
}

void omar_state::input4_w(u8 data)
{
	// P55: input mux part
	m_inp_mux = (m_inp_mux & ~0x10) | (data >> 1 & 0x10);
}

u8 omar_state::input_r()
{
	u8 data = 0;

	// P42-P46: multiplexed inputs
	for (int i = 0; i < 5; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read();

	return data << 2;
}


// LED version

void omar_state::display1_w(u8 data)
{
	input1_w(data);

	// P01-P06: digit select
	m_display->write_my(~data >> 1 & 0x3f);
}

void omar_state::display2_w(u8 data)
{
	input2_w(data);

	// P10,P12-P17: digit data
	m_display->write_mx(bitswap<7>(~data,4,5,7,3,0,6,2));
}


// LCD version

void omar2_state::update_lcd()
{
	m_display->write_row(0, m_lcd_com ? ~get_u32le(m_lcd_data) : get_u32le(m_lcd_data));
}

void omar2_state::display1_w(u8 data)
{
	input1_w(data ^ 0x80);

	// P00-P06: LCD data
	m_lcd_data[0] = (m_lcd_data[0] & 0x80) | (data & 0x7f);
	update_lcd();
}

void omar2_state::display2_w(u8 data)
{
	input2_w(data);

	// P10: LCD common
	m_lcd_com = data & 1;

	// P11-P17: LCD data
	m_lcd_data[1] = (m_lcd_data[1] & 0x01) | (data & 0xfe);
	update_lcd();
}

void omar2_state::display3_w(u8 data)
{
	// P40,P47: LCD data
	m_lcd_data[0] = (m_lcd_data[0] & 0x7f) | (data & 0x80);
	m_lcd_data[1] = (m_lcd_data[1] & 0xfe) | (data & 0x01);

	// P41: 4015 data (4015 Q to LCD)
	m_lcd_data[3] = (m_lcd_data[3] << 1) | (BIT(data, 1));
	update_lcd();
}

void omar2_state::display4_w(u8 data)
{
	// P50-P57: LCD data
	m_lcd_data[2] = data;
	update_lcd();
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void omar_state::main_map(address_map &map)
{
	map.global_mask(0x7ff);
	map(0x0000, 0x07ff).rom();
}

void omar_state::main_io(address_map &map)
{
	map(0x00, 0x00).w(FUNC(omar_state::display1_w));
	map(0x01, 0x01).w(FUNC(omar_state::display2_w));
	map(0x04, 0x07).rw(m_psu, FUNC(f38t56_device::read), FUNC(f38t56_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( omar )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("BO")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("VR")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("DBL")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("ACC")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("ST")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Down")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("12")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_MINUS) PORT_NAME("11")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("10")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("EN")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_UP) PORT_NAME("Up")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("BAR")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CLR")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Dice")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("CUB")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void omar_state::omar1(machine_config &config)
{
	// basic machine hardware
	F8(config, m_maincpu, 2700000/2); // approximation
	m_maincpu->set_addrmap(AS_PROGRAM, &omar_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &omar_state::main_io);
	m_maincpu->set_irq_acknowledge_callback(m_psu, FUNC(f38t56_device::int_acknowledge));

	F38T56(config, m_psu, 2700000/2);
	m_psu->set_int_vector(0x20);
	m_psu->int_req_callback().set_inputline(m_maincpu, F8_INPUT_LINE_INT_REQ);
	m_psu->read_a().set(FUNC(omar_state::input_r));
	m_psu->write_a().set(FUNC(omar_state::input3_w));
	m_psu->write_b().set(FUNC(omar_state::input4_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(6, 7);
	m_display->set_segmask(0x3f, 0x7f);
	config.set_default_layout(layout_omar1);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void omar2_state::omar2(machine_config &config)
{
	omar1(config);

	// basic machine hardware
	m_psu->write_a().append(FUNC(omar2_state::display3_w));
	m_psu->write_b().append(FUNC(omar2_state::display4_w));

	// video hardware
	PWM_DISPLAY(config.replace(), m_display).set_size(1, 32);
	m_display->set_bri_levels(0.25);
	config.set_default_layout(layout_omar2);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/2.5, 412/2.5);
	screen.set_visarea_full();
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( omar1 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD("lom1ar279", 0x0000, 0x0800, CRC(e1bcee50) SHA1(658d6d8a0af3c1672610c651fea7d0289e4703f7) ) // 3870X-0245
ROM_END

ROM_START( omar2 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD("xom2ar1779", 0x0000, 0x0800, CRC(e0d6a119) SHA1(40e35020f483245bcc82443923763b037498a98d) ) // 3870X-0248

	ROM_REGION( 43303, "screen", 0 )
	ROM_LOAD("omar2.svg", 0, 43303, CRC(fc9e757d) SHA1(c52e2f7c01e174286b75efd0c4c137f42c46053f) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY, FULLNAME, FLAGS
SYST( 1979, omar1, 0,      0,      omar1,   omar,  omar_state,  empty_init, "Tryom", "Omar I", MACHINE_SUPPORTS_SAVE )
SYST( 1979, omar2, 0,      0,      omar2,   omar,  omar2_state, empty_init, "Tryom", "Omar II", MACHINE_SUPPORTS_SAVE )



omdisv22.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Devin Acker
/*
    Skeleton driver for the uPD78C14-based "Driver Information System" used by
    the circa-1990 Oldsmobile 98 and other models.

    Press 6, then Test/Reset, then enter a distance of 8192 to show a debug display
    with information about the current ADC readings. After that, press any button
    to display the ROM version.

    TODO:
    - identify/hook up the display hardware
    - properly hook up or at least figure out other inputs (port B, SCK/RX, CI, most ADCs, etc)
      The function at 09D5 handles reading the ADC values.
*/

#include "emu.h"

#include "cpu/upd7810/upd7810.h"

#include <algorithm>

namespace {

class omdisv22_state : public driver_device
{
public:
	omdisv22_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_inputs(*this, "IN%u", 0U)
	{ }

	void omdisv22(machine_config &config);

	void inputs_w(int state) { m_input_sel = state; }
	ioport_value inputs_r();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<upd78c14_device> m_maincpu;
	required_ioport_array<3> m_inputs;

	void pd_w(u8 data) { m_pd = data; }
	void pf_w(u8 data);

	ioport_value m_input_sel;

	u8 m_output_pos;
	u8 m_output[18];

	u8 m_pd, m_pf;
};

/**************************************************************************/
static INPUT_PORTS_START(omdisv22)
	PORT_START("PA")
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_OUTPUT  ) PORT_WRITE_LINE_MEMBER(FUNC(omdisv22_state::inputs_w))
	PORT_BIT( 0x78, IP_ACTIVE_HIGH, IPT_CUSTOM  ) PORT_CUSTOM_MEMBER(FUNC(omdisv22_state::inputs_r))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("AN5")
	PORT_CONFNAME( 0xff, 0xff, "Default Units" )
	PORT_CONFSETTING(    0xff, "English")
	PORT_CONFSETTING(    0x00, "Metric")

	PORT_START("IN0")
	PORT_BIT( 0x1, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("0 / E/M")   PORT_CODE(KEYCODE_0_PAD);
	PORT_BIT( 0x2, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("1 / RANGE") PORT_CODE(KEYCODE_1_PAD);
	PORT_BIT( 0x4, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("2 / ECON")  PORT_CODE(KEYCODE_2_PAD);
	PORT_BIT( 0x8, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("3 / GAGES") PORT_CODE(KEYCODE_3_PAD);

	PORT_START("IN1")
	PORT_BIT( 0x1, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("4 / OIL")  PORT_CODE(KEYCODE_4_PAD);
	PORT_BIT( 0x2, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("5 / FUEL") PORT_CODE(KEYCODE_5_PAD);
	PORT_BIT( 0x4, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("6 / DEST") PORT_CODE(KEYCODE_6_PAD);
	PORT_BIT( 0x8, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("7 / ETA")  PORT_CODE(KEYCODE_7_PAD);

	PORT_START("IN2")
	PORT_BIT( 0x1, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("8 / E/T")   PORT_CODE(KEYCODE_8_PAD);
	PORT_BIT( 0x2, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("9 / SPEED") PORT_CODE(KEYCODE_9_PAD);
	PORT_BIT( 0x4, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("RESET")     PORT_CODE(KEYCODE_ENTER_PAD);
	PORT_BIT( 0x8, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("DATE/TIME") PORT_CODE(KEYCODE_DEL_PAD);
INPUT_PORTS_END

/**************************************************************************/
void omdisv22_state::omdisv22(machine_config &config)
{
	UPD78C14(config, m_maincpu, 3'145'728); // 128*64*384, generates 1 Hz INTFT1 and 8192 Hz SCK
	m_maincpu->pa_in_cb().set_ioport("PA");
	m_maincpu->pa_out_cb().set_ioport("PA");
	m_maincpu->pb_in_cb().set_constant(0xff); // bit 1 = display ready
	m_maincpu->pc_in_cb().set_constant(0xff);
	m_maincpu->pd_out_cb().set(FUNC(omdisv22_state::pd_w));
	m_maincpu->pf_out_cb().set(FUNC(omdisv22_state::pf_w));
	m_maincpu->an0_func().set_constant(0xff); // fuel level sensor?
	m_maincpu->an1_func().set_constant(0xff); // unknown
	m_maincpu->an2_func().set_constant(0xff); // unknown (only tested whether on/off)
	m_maincpu->an3_func().set_constant(0xff); // reference/divisor for AN0
	m_maincpu->an4_func().set_constant(0xff); // affects scaling of AN0/AN3 (only tested whether on/off)
	m_maincpu->an5_func().set_ioport("AN5");
	m_maincpu->an6_func().set_constant(0xff); // similar to AN4
}

/**************************************************************************/
void omdisv22_state::machine_start()
{
	std::fill(std::begin(m_output), std::end(m_output), 0);

	save_item(NAME(m_input_sel));
	save_item(NAME(m_output_pos));
	save_item(NAME(m_output));
	save_item(NAME(m_pd));
	save_item(NAME(m_pf));
}

/**************************************************************************/
void omdisv22_state::machine_reset()
{
	m_maincpu->set_input_line(UPD7810_INTF2, ASSERT_LINE);

	m_input_sel = 0x7;

	m_output_pos = 0;
	m_pd = m_pf = 0xff;
}

/**************************************************************************/
ioport_value omdisv22_state::inputs_r()
{
	ioport_value val = 0xf;

	for (int i = 0; i < 3; i++)
		if (!BIT(m_input_sel, i))
			val &= m_inputs[i]->read();

	return val;
}

/**************************************************************************/
void omdisv22_state::pf_w(u8 data)
{
	// TODO: what kind of display is this, anyway?
	if (BIT(m_pf, 5) && BIT(m_pf, 4) && !BIT(data, 4))
	{
		if (m_pd == 0xf1)
		{
			m_output_pos = 0;
		}
		else if (m_pd == 0xf2)
		{
			m_output[m_output_pos] = 0;
			logerror("%s\n", (const char*)m_output);
			popmessage("%s", (const char*)m_output);
		}
		else if (m_output_pos < (sizeof(m_output) - 1))
		{
			m_output[m_output_pos++] = 0x30 + (m_pd / 5);
		}
	}

	m_pf = data;
}

/**************************************************************************/
ROM_START( omdisv22 )
	ROM_REGION(0x4000, "maincpu", 0) // date code 9020, upper half of ROM is empty/unused
	ROM_LOAD( "upd78c14g-443.bin", 0x0000, 0x4000, CRC(fab369b2) SHA1(7cbd5be32e475efd58db70c0a94b770c58fd9b8e) )
ROM_END

} // anonymous namespace

SYST( 1990?, omdisv22, 0, 0, omdisv22, omdisv22, omdisv22_state, empty_init, "General Motors", "Oldsmobile Driver Information System (version 2.2 CH)", MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )



ondra.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/*****************************************************************************

Ondra driver by Miodrag Milanovic

2008-09-08 Preliminary driver.

ToDo:
- Paste/Natural keyboard are useless because 3rd modifier key is not supported.
- Add 2x i8253 pits which are part of the video timing circuit. They are not
  connected to the data bus, and are always selected.
- The video is somewhat similar to the standard super80, in that the CPU is
  turned off by BUSRQ about half the time, so that the video can be drawn
  without causing snow. The CPU can gain full control by disabling the video.
- Sound is a speaker connected to a multivibrator circuit. There are 3 diodes
  from this circuit to allow a choice of 7 frequencies. We have used a buzzer
  with selected arbitrary frequencies, not having any idea what they should be.
- Ondrav: can load from softlist. Load the tape then press Enter.
- Ondrav: doesn't seem to accept any other commands, need instructions.
  You have to press Esc before trying again.
- Ondrat: Doesn't load from softlist
- Cassette: don't know how to save.

******************************************************************************/


#include "emu.h"
#include "ondra.h"
#include "cpu/z80/z80.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


/* Address maps */
void ondra_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).bankrw("bank1");
	map(0x4000, 0xdfff).bankrw("bank2");
	map(0xe000, 0xffff).bankrw("bank3");
}

void ondra_state::io_map(address_map &map)
{
	//map.global_mask(0x0b);
	map.unmap_value_high();
	map(0x03, 0x03).mirror(0xff00).w(FUNC(ondra_state::port03_w));
	//map(0x09, 0x09).mirror(0xff00).r(FUNC(ondra_state::port09_r));
	map(0x0a, 0x0a).mirror(0xff00).w(FUNC(ondra_state::port0a_w));
}

/* Input ports */
static INPUT_PORTS_START( ondra )
	PORT_START("LINE0")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R 4 $") PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r') PORT_CHAR('4') PORT_CHAR('$')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E 3 #") PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e') PORT_CHAR('3') PORT_CHAR('#')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W 2 \"") PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w') PORT_CHAR('2') PORT_CHAR('\"')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T 5 %") PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t') PORT_CHAR('5') PORT_CHAR('%')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q 1 !") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q') PORT_CHAR('1') PORT_CHAR('!')
	PORT_START("LINE1")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F ^") PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f') PORT_CHAR('^')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D =") PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d') PORT_CHAR('=')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S +") PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s') PORT_CHAR('+')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G _") PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g') PORT_CHAR('_')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A -") PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a') PORT_CHAR('-')
	PORT_START("LINE2")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C :") PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c') PORT_CHAR(':')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X /") PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x') PORT_CHAR('/')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z *") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z') PORT_CHAR('*')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V ;") PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v') PORT_CHAR(';')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_RSHIFT) //PORT_CHAR(UCHAR_SHIFT_3)  not supported yet
	PORT_START("LINE3")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("LINE4")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0-9") PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_SHIFT_2)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS")  PORT_CODE(KEYCODE_LALT)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("UpCase") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_START("LINE5")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J >") PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j') PORT_CHAR('>')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K [") PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k') PORT_CHAR('[')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L ]") PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l') PORT_CHAR(']')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H <") PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h') PORT_CHAR('<')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER)
	PORT_START("LINE6")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U 7 \'") PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u') PORT_CHAR('7') PORT_CHAR('\'')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I 8 (") PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i') PORT_CHAR('8') PORT_CHAR('(')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O 9 )") PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o') PORT_CHAR('9') PORT_CHAR(')')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y 6 &") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y') PORT_CHAR('6') PORT_CHAR('&')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P 0 @") PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p') PORT_CHAR('0') PORT_CHAR('@')
	PORT_START("LINE7")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N ,") PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n') PORT_CHAR(',')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M .") PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m') PORT_CHAR('.')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B ?") PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b') PORT_CHAR('?')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_START("LINE8")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
	PORT_START("LINE9")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(JOYCODE_Y_DOWN_SWITCH)  PORT_PLAYER(1)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(JOYCODE_X_LEFT_SWITCH)  PORT_PLAYER(1)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)   PORT_CODE(KEYCODE_8_PAD) PORT_CODE(JOYCODE_Y_UP_SWITCH)    PORT_PLAYER(1)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_BUTTON1)       PORT_CODE(KEYCODE_0_PAD) PORT_CODE(JOYCODE_BUTTON1)        PORT_PLAYER(1)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT)PORT_CODE(KEYCODE_6_PAD) PORT_CODE(JOYCODE_X_RIGHT_SWITCH) PORT_PLAYER(1)
	PORT_START("NMI")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("NMI") PORT_CODE(KEYCODE_ESC) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ondra_state::nmi_button), 0)
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(ondra_state::nmi_button)
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, newval ? ASSERT_LINE : CLEAR_LINE);
}

u32 ondra_state::screen_update_ondra(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u8 const *const r = m_ram->pointer();

	u8 code1=0,code2=0;
	int Vaddr = 0x2800;

	for (int x = 0; x < 40; x++)
	{
		for (int y = 127; y >=0; y--)
		{
			if (m_video_enable)
			{
				code1 = r[0xd700 + Vaddr + 0x80];
				code2 = r[0xd700 + Vaddr + 0x00];
			}
			for (int b = 0; b < 8; b++)
			{
				bitmap.pix(2*y, x*8+b) =  ((code1 << b) & 0x80) ? 1 : 0;
				bitmap.pix(2*y+1, x*8+b) =  ((code2 << b) & 0x80) ? 1 : 0;
			}
			Vaddr++;
		}
		Vaddr = (Vaddr - 128) - 256;
	}
	return 0;
}

void ondra_state::vblank_irq(int state)
{
	if (state)
		m_maincpu->set_input_line(0, HOLD_LINE);
}

/* Machine driver */
void ondra_state::ondra(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 8_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &ondra_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &ondra_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(320, 256);
	screen.set_visarea(0, 320-1, 0, 256-1);
	screen.set_screen_update(FUNC(ondra_state::screen_update_ondra));
	screen.set_palette("palette");
	screen.screen_vblank().set(FUNC(ondra_state::vblank_irq));

	PALETTE(config, "palette", palette_device::MONOCHROME);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 950); // guess
	m_beep->add_route(ALL_OUTPUTS, "mono", 0.25);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("ondra_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("ondra");

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("64K").set_default_value(0x00);
}

/* ROM definition */

ROM_START( ondrat )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "tesla_a.d22", 0x0000, 0x0800, CRC(6d56b815) SHA1(7feb4071d5142e4c2f891747b75fa4d48ccad262) )
	ROM_LOAD( "tesla_b.d21", 0x2000, 0x0800, CRC(5f145eaa) SHA1(c1eac68b13fedc4d0d6f98b15e2a5397f0139dc3) )

	ROM_REGION( 0x0040, "proms", 0 )
	ROM_LOAD( "mh74188.d27", 0x0000, 0x0040, CRC(7faceafe) SHA1(597f867e38b1c66d4622662cb01b3aefa680f234) )
ROM_END

ROM_START( ondrav )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "vili_a.d22",  0x0000, 0x0800, CRC(76932657) SHA1(1f3700f670f158e4bed256aed751e2c1331a28e8) )
	ROM_RELOAD(0x0800, 0x0800)
	ROM_RELOAD(0x1000, 0x0800)
	ROM_RELOAD(0x1800, 0x0800)
	ROM_LOAD( "vili_b.d21",  0x2000, 0x0800, CRC(03a6073f) SHA1(66f198e63f473e09350bcdbb10fe0cf440111bec) )
	ROM_RELOAD(0x2800, 0x0800)
	ROM_RELOAD(0x3000, 0x0800)
	ROM_RELOAD(0x3800, 0x0800)

	ROM_REGION( 0x0040, "proms", 0 )
	ROM_LOAD( "mh74188.d27", 0x0000, 0x0040, CRC(7faceafe) SHA1(597f867e38b1c66d4622662cb01b3aefa680f234) )
ROM_END

/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME      FLAGS
COMP( 1989, ondrat, 0,      0,      ondra,   ondra, ondra_state, empty_init, "Tesla", "Ondra",      MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1989, ondrav, ondrat, 0,      ondra,   ondra, ondra_state, empty_init, "ViLi",  "Ondra ViLi", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



onyx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***********************************************************************************************

Onyx C8002

2013-08-18 Skeleton Driver

The C8002 is one of the earliest minicomputers to use Unix as an operating system.

The system consists of a main CPU (Z8002), and a slave CPU for Mass Storage control (Z80)

The Z80 board contains a 19.6608 and 16 MHz crystals; 2x Z80CTC; 3x Z80SIO/0; Z80DMA; 3x Z80PIO;
2 eproms marked 459-3 and 460-3, plus 2 proms.

The Z8002 board contains a 16 MHz crystal; 3x Z80CTC; 5x Z80SIO/0; 3x Z80PIO; 2 eproms marked
466-E and 467E, plus the remaining 7 small proms.

The system can handle 8 RS232 terminals, 7 hard drives, a tape cartridge drive, parallel i/o,
and be connected to a RS422 network.

Status:
- Main screen prints an error with CTC (because there's no clock into it atm)
- Subcpu screen (after a while) prints various statuses then waits for the fdc to respond

To Do:
- Hook up daisy chains (see p8k.cpp for how to hook up a 16-bit chain)
  (keyboard input depends on interrupts)
- Remaining devices
- Whatever hooks up to the devices
- Eventually we'll need software
- Manuals / schematics would be nice

*************************************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "cpu/z8000/z8000.h"
#include "machine/clock.h"
#include "bus/rs232/rs232.h"
//#include "machine/z80daisy.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"
//#include "machine/z80dma.h"

class onyx_state : public driver_device
{
public:
	onyx_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_sio(*this, "sio%u", 1U)
		, m_ctc(*this, "ctc%u", 1U)
		, m_pio(*this, "pio%u", 1U)
	{ }

	void c8002(machine_config &config);
	void c5000(machine_config &config);
	void c5000_io(address_map &map) ATTR_COLD;

private:
	void c5000_mem(address_map &map) ATTR_COLD;
	void c8002_io(address_map &map) ATTR_COLD;
	void c8002_mem(address_map &map) ATTR_COLD;
	void subio(address_map &map) ATTR_COLD;
	void submem(address_map &map) ATTR_COLD;
	void z8002_m1_w(uint8_t data);

	required_device<cpu_device> m_maincpu; // z8002 or z80 depending on driver
	optional_device_array<z80sio_device, 5> m_sio;
	optional_device_array<z80ctc_device, 3> m_ctc;
	optional_device_array<z80pio_device, 2> m_pio;
};


/* Input ports */
static INPUT_PORTS_START( c8002 )
INPUT_PORTS_END


void onyx_state::c8002_mem(address_map &map)
{
	map(0x00000, 0x00fff).rom();
	map(0x01000, 0x07fff).ram();
	map(0x08000, 0x0ffff).ram(); // Z8002 has 64k memory
}

void onyx_state::z8002_m1_w(uint8_t data)
{
	// ED 4D (Z80 RETI opcode) is written here
	for (auto &sio : m_sio)
		sio->z80daisy_decode(data);
	for (auto &ctc : m_ctc)
		ctc->z80daisy_decode(data);
	for (auto &pio : m_pio)
		pio->z80daisy_decode(data);
}

void onyx_state::c8002_io(address_map &map)
{
	map(0xff00, 0xff07).rw(m_sio[0], FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w)).umask16(0x00ff);
	map(0xff08, 0xff0f).rw(m_sio[1], FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w)).umask16(0x00ff);
	map(0xff10, 0xff17).rw(m_sio[2], FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w)).umask16(0x00ff);
	map(0xff18, 0xff1f).rw(m_sio[3], FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w)).umask16(0x00ff);
	map(0xff20, 0xff27).rw(m_sio[4], FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w)).umask16(0x00ff);
	map(0xff30, 0xff37).rw(m_ctc[0], FUNC(z80ctc_device::read), FUNC(z80ctc_device::write)).umask16(0x00ff);
	map(0xff38, 0xff3f).rw(m_ctc[1], FUNC(z80ctc_device::read), FUNC(z80ctc_device::write)).umask16(0x00ff);
	map(0xff40, 0xff47).rw(m_ctc[2], FUNC(z80ctc_device::read), FUNC(z80ctc_device::write)).umask16(0x00ff);
	map(0xff50, 0xff57).rw(m_pio[0], FUNC(z80pio_device::read), FUNC(z80pio_device::write)).umask16(0x00ff);
	map(0xff58, 0xff5f).rw(m_pio[1], FUNC(z80pio_device::read), FUNC(z80pio_device::write)).umask16(0x00ff);
	map(0xffb9, 0xffb9).w(FUNC(onyx_state::z8002_m1_w));
}

void onyx_state::submem(address_map &map)
{
	map(0x0000, 0x0fff).rom();
	map(0x1000, 0xffff).ram();
}

void onyx_state::subio(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).rw("pio1s", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x04, 0x04).nopr();   // disk status?
	map(0x0c, 0x0f).rw("sio1s", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
}

/***************************************************************************

    Machine Drivers

****************************************************************************/

void onyx_state::c8002(machine_config &config)
{
	/* basic machine hardware */
	z8002_device& maincpu(Z8002(config, m_maincpu, XTAL(4'000'000)));
	//maincpu.set_daisy_config(main_daisy_chain);
	maincpu.set_addrmap(AS_PROGRAM, &onyx_state::c8002_mem);
	//maincpu.set_addrmap(AS_DATA, &onyx_state::c8002_data);
	maincpu.set_addrmap(AS_IO, &onyx_state::c8002_io);

	z80_device& subcpu(Z80(config, "subcpu", XTAL(4'000'000)));
	//subcpu.set_daisy_config(sub_daisy_chain);
	subcpu.set_addrmap(AS_PROGRAM, &onyx_state::submem);
	subcpu.set_addrmap(AS_IO, &onyx_state::subio);

	clock_device &sio1_clock(CLOCK(config, "sio1_clock", 307200));
	sio1_clock.signal_handler().set(m_sio[0], FUNC(z80sio_device::rxca_w));
	sio1_clock.signal_handler().append(m_sio[0], FUNC(z80sio_device::txca_w));

	/* peripheral hardware */
	Z80PIO(config, m_pio[0], XTAL(16'000'000)/4);
	//m_pio[0]->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	Z80PIO(config, m_pio[1], XTAL(16'000'000)/4);
	//m_pio[1]->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	Z80CTC(config, m_ctc[0], XTAL(16'000'000) /4);
	Z80CTC(config, m_ctc[1], XTAL(16'000'000) /4);
	Z80CTC(config, m_ctc[2], XTAL(16'000'000) /4);
	Z80SIO(config, m_sio[0], XTAL(16'000'000) /4);
	m_sio[0]->out_txda_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	m_sio[0]->out_dtra_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	m_sio[0]->out_rtsa_callback().set("rs232", FUNC(rs232_port_device::write_rts));
	Z80SIO(config, m_sio[1], XTAL(16'000'000) /4);
	Z80SIO(config, m_sio[2], XTAL(16'000'000) /4);
	Z80SIO(config, m_sio[3], XTAL(16'000'000) /4);
	Z80SIO(config, m_sio[4], XTAL(16'000'000) /4);

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_sio[0], FUNC(z80sio_device::rxa_w));
	rs232.dcd_handler().set(m_sio[0], FUNC(z80sio_device::dcda_w));
	rs232.cts_handler().set(m_sio[0], FUNC(z80sio_device::ctsa_w));

	Z80PIO(config, "pio1s", XTAL(16'000'000)/4);
	//z80pio_device& pio1s(Z80PIO(config, "pio1s", XTAL(16'000'000)/4));
	//pio1s->out_int_callback().set_inputline("subcpu", INPUT_LINE_IRQ0);

	clock_device &sio1s_clock(CLOCK(config, "sio1s_clock", 614400));
	sio1s_clock.signal_handler().set("sio1s", FUNC(z80sio_device::rxtxcb_w));
	//sio1s_clock.signal_handler().append("sio1s", FUNC(z80sio_device::txca_w));

	z80sio_device& sio1s(Z80SIO(config, "sio1s", XTAL(16'000'000) /4));
	sio1s.out_txdb_callback().set("rs232s", FUNC(rs232_port_device::write_txd));
	sio1s.out_dtrb_callback().set("rs232s", FUNC(rs232_port_device::write_dtr));
	sio1s.out_rtsb_callback().set("rs232s", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232s(RS232_PORT(config, "rs232s", default_rs232_devices, "terminal"));
	rs232s.rxd_handler().set("sio1s", FUNC(z80sio_device::rxb_w));
	rs232s.dcd_handler().set("sio1s", FUNC(z80sio_device::dcdb_w));
	rs232s.cts_handler().set("sio1s", FUNC(z80sio_device::ctsb_w));
}

/* ROM definition */
ROM_START( c8002 )
	ROM_REGION16_BE( 0x100000, "maincpu", 0 )
	ROM_LOAD16_BYTE("466-e", 0x0001, 0x0800, CRC(13534bcb) SHA1(976c76c69af40b0c0a5038e428a10b39a619a036))
	ROM_LOAD16_BYTE("467-e", 0x0000, 0x0800, CRC(0d5b557f) SHA1(0802bc6c2774f4e7de38a9c92e8558d710eed287))

	ROM_REGION( 0x10000, "subcpu", 0 )
	ROM_LOAD("459-3",   0x0000, 0x0800, CRC(c8906653) SHA1(7dea9fffa974479ef5926df567261f2aaa7a3283))
	ROM_LOAD("460-3",   0x0800, 0x0800, CRC(ce6c0214) SHA1(f69ee4c6c0d1e72574a9cf828dbb3e08f06d029a))

	ROM_REGION( 0x900, "proms", 0 )
	// for main cpu
	ROM_LOAD("468-a",  0x000, 0x100, CRC(89781491) SHA1(f874d0cf42a733eb2b92b15647aeac7178d7b9b1))
	ROM_LOAD("469-a",  0x100, 0x100, CRC(45e439de) SHA1(4f1af44332ae709d92e919c9e48433f29df5e632))
	ROM_LOAD("470a-3", 0x200, 0x100, CRC(c50622a9) SHA1(deda0df93fc4e4b5f4be313e4bfe0c5fc669a024))
	ROM_LOAD("471-a",  0x300, 0x100, CRC(c09ca06b) SHA1(cb99172f5342427c68a109ee108a0c49b44e7010))
	ROM_LOAD("472-a",  0x400, 0x100, CRC(e1316fed) SHA1(41ed2d822c74da4e1ce06eb229629576e7f5035f))
	ROM_LOAD("473-a",  0x500, 0x100, CRC(5e8efd7f) SHA1(647064e0c3b0d795a333febc57228472b1b32345))
	ROM_LOAD("474-a",  0x600, 0x100, CRC(0052edfd) SHA1(b5d18c9a6adce7a6d627ece40a60aab8c55a6597))
	// for sub cpu
	ROM_LOAD("453-a",  0x700, 0x100, CRC(7bc3871e) SHA1(6f75eb04911fa1ff66714276b8a88be62438a1b0))
	ROM_LOAD("454-a",  0x800, 0x100, CRC(aa2233cd) SHA1(4ec3a8c06cccda02f080e89831ecd8a9c96d3650))
ROM_END

/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY         FULLNAME  FLAGS
COMP( 1982, c8002, 0,      0,      c8002,   c8002, onyx_state, empty_init, "Onyx Systems", "C8002",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



/********************************************************************************************************************************

Onyx Systems C5000.
(says C8000 on the backplate)

Chips: 256k dynamic RAM, Z80A, Z80DMA, 5x Z80PIO, 2x Z80SIO/0, 2x Z80CTC, 5? undumped proms, 3 red leds, 1x 4-sw DIP
Crystals: 16.000000, 19.660800
Labels of proms: 339, 153, XMN4, 2_1, 1_2

*********************************************************************************************************************************/

void onyx_state::c5000_mem(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x2000, 0xffff).ram();
}

void onyx_state::c5000_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x10, 0x13).rw(m_sio[0], FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
}

void onyx_state::c5000(machine_config &config)
{
	/* basic machine hardware */
	z80_device& maincpu(Z80(config, m_maincpu, XTAL(16'000'000) / 4));
	//maincpu.set_daisy_config(sub_daisy_chain);
	maincpu.set_addrmap(AS_PROGRAM, &onyx_state::c5000_mem);
	maincpu.set_addrmap(AS_IO, &onyx_state::c5000_io);

	clock_device &sio1_clock(CLOCK(config, "sio1_clock", 614400));
	sio1_clock.signal_handler().set(m_sio[0], FUNC(z80sio_device::rxtxcb_w));
	//sio1_clock.signal_handler().append(m_sio[0], FUNC(z80sio_device::txca_w));

	/* peripheral hardware */
	//Z80PIO(config, m_pio[0], XTAL(16'000'000)/4);
	//m_pio[0]->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	//Z80PIO(config, m_pio[1], XTAL(16'000'000)/4);
	//m_pio[1]->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	//Z80CTC(config, m_ctc[0], XTAL(16'000'000) /4);
	//Z80CTC(config, m_ctc[1], XTAL(16'000'000) /4);
	//Z80CTC(config, m_ctc[2], XTAL(16'000'000) /4);

	Z80SIO(config, m_sio[0], XTAL(16'000'000) /4);
	m_sio[0]->out_txdb_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	m_sio[0]->out_dtrb_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	m_sio[0]->out_rtsb_callback().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_sio[0], FUNC(z80sio_device::rxb_w));
	rs232.dcd_handler().set(m_sio[0], FUNC(z80sio_device::dcdb_w));
	rs232.cts_handler().set(m_sio[0], FUNC(z80sio_device::ctsb_w));

	//Z80SIO(config, m_sio[1], XTAL(16'000'000) /4);
}


ROM_START( c5000 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "860-3.prom1", 0x0000, 0x1000, CRC(31b52df3) SHA1(e221c7829b4805805cde1bde763bd5a936e7db1a) )
	ROM_LOAD( "861-3.prom2", 0x1000, 0x1000, CRC(d1eba182) SHA1(850035497975b821fc1e51fbb73642cba3ff9784) )
ROM_END

/* Driver */

//    YEAR  NAME   PARENT  COMPAT   MACHINE    INPUT  CLASS       INIT        COMPANY          FULLNAME  FLAGS
COMP( 1981, c5000, 0,      0,       c5000,     c8002, onyx_state, empty_init, "Onyx Systems",  "C5000",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



optomaxv.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/*********************************************************************************************************

Optomax V image analyser, from AMS (Analytical Measuring Systems Ltd.).
The machine internally uses a CUBE EuroBEEB Single Board Computer (PCBs B6 and B7).

Back panel:
 - Camera input connector (DIN).
 - Camera video connector (3 x BNC).
 - VCR connector (BNC).
 - TGB monitor connector (DIN)
 - Printer connector (DB 26)
 - Data transfer serial connector (DB9).
 - Data transfer parallel connector (DB25).
 - Aux 1 connector (DIN).
 - Two sync mode switches (internal/external and interlace/non interlace).

Front panel:
 - Monitor (Philips M24-306GH/ED).
 - Control panel with the following layout:
 ______________________________________________________________________________________________
|           _____                                                    FIELD/                   |
|          /     \                     FRAME           TITLE          FEATURE         V (F1)  |
|         | DIAL  |        SET                                                                |
|         |       |       DETECTOR            CURRENT            INITIATE             W (F2)  |
|          \_____/                      EDIT          CALIBRATE         HOLD                  |
|                                                                                     X (F3)  |
|                                                                                             |
|     PREVIOUS    NEXT       A         B (7)   C (8)   D (9)     E (PRINT) F (ABORT)  Y (F4)  |
|                       (DETECT NULL)                                                         |
|             UP           G (HE)      H (4)   I (5)   J (6)     K (SPACE)     L      Z (F5)  |
|                                                                         (SEND DATA)         |
|      RIGHT HOME  LEFT    M (MR)      N (1)   O (2)   P (3)      Q (EXP)      R      / (F6)  |
|                                                                        (LOAD MATRIX)        |
|            DOWN           GREEN      S (0)   T (·)   U (DEL)     ENTER     GREEN    , (F7)  |
|_____________________________________________________________________________________________|


Six PCBs connected with two backplanes (Motorola VME 9 conns and a custom one with 13 conns):

OSD PCB (B6) (EURO-BEEB 65, CUBE EuroBEEB Single Board Computer PCB from Control Universal Ltd.)
 - Rockwell R6551AP ACIA
 - Rockwell R6522AP VIA
 - Toshiba TC5565PL SRAM
 - Xtal 1.8432
 - Hitachi HN613128PB05 Acorn BASIC Mask ROM (BASIC)
 - 2764 EPROM
 - M-3002-16PI Real Time Clock (RTC)
 - 2 x Signetics 82S147N PROMs
 - Rockwell R6502AP CPU
   __________________________________________
 _|                                          |_
| |__________________________________________| |
| Xtal                ________________________ |
| ___   ___________  | Rockwell              | |
||  |  |_82S147N__|  | R6502AP               | |
||  |   ___________  |_______________________| |
||  |  |_82S147N__|                            |
||_<-M-3002-16PI             _________________ |
|                           | EPROM          | |
|              __________   | M0             | |
|             |M74HC132B1   |________________| |
|              __________    _________________ |
|             |SN74S00N_|   | MASK (BASIC)   | |
|                           | M1             | |
|  _____                    |________________| |
| |    |                     _________________ |
| |BATT|                    | EMPTY          | |
| |2.4V|       __________   | M3             | |
| |____|      |_74F04N__|   |________________| |
| __________   __________    _________________ |
||AM26LS30PC  |AM26LS32PC   | TC5565PL-15    | |
|                  ..       |                | |
| Xtal 1.8432 MHz  ..       |________________| |
| _________________   ________________________ |
|| Rockwell       |  | Rockwell              | |
|| R6551AP        |  | R6522AP               | |
||________________|  |_______________________| |
|   _______      ___________________           |
|__| DIN  |_____||||||||||||||||||||___________|
   |______|


 TELETEXT PCB (B5) (CUBE TELETEXT Colour Video Interface from Control Universal Ltd.)
(this PCB lacks the buzzer present on other revisions)
 - Hitachi HD68B45SP CRT Controller
 - Xtal 6.000
 - 2 x NEC uPD2114LC SRAM
 - SAA 5050 Teletext Character Generator
 - Rockwell R6522-33 VIA
       _____________________
 _____|                     |__________________
|     |_____________________|                  |
|      __________     ____________________     |
|     |SN74LS174N    | Rockwell          |     |
|                    | R6522AP           |     |
|      __________    |___________________|     |
|     |_74LS86N_|      _________________       |
|                     |                |       |
|      __________     | SAA 5050       |       |
|     |_74LS04N_|     |________________|       |
|      __________   __________   __________    |
|     |_74LS04N_|  |_74LS92N_|  |uPD2114LC|    |
|        Xtal       __________   __________    |
|        6 MHz     |74LS245N_|  |uPD2114LC|    |
|      __________                              |
|     |_74LS00N_|     ____________________     |
|                    | Hitachi            |    |
|                    | HD46505SP-2        |    |
|                    |____________________|    |
|      __________   __________   __________    |
|     |74LS138N_|  |74LS157N_|  |74LS157N_|    |
|      __________   __________   __________    |
|     |74LS139N_|  |74LS136N_|  |74LS157N_|    |
|  __________________________________________  |
|_|                                          |_|
  |__________________________________________|


PCU PCB (PME 68-1B) (B4)
 - Signetics SCN68000 CPU
 - 16 x TMM41256P RAM
 - 2 x 27256 EPROM
 - 2 x 27128 EPROM
 - 3 x Hitachi HD46850P ACIA
 - AMPAL16L8 (labeled 0545)
 - PAL16L6C (labeled 0544)
 - 2 x PAL20L10C (labeled 0686 and 0620)
 - PAL12L10C (labeled 0546)
 - MM58167AN Real Time Clock (RTC)
 - MC68230L8 Parallel Interface / Timer 8MHz
 - Xtal 1.8432 MHz
 - MC14411P Bit Rate Generator
 - Xtal 32 MHz
      _____________________________________________________________________
     |    ________   _______________________________    __________        |
    _|_  |       |  |                              |   |SN74LS645-1N      |
 RES SW| |       |  |                              |    __________       _|_
    _|_  |       |  |  16 x TMM41256P-15           |   |SN74LS645-1N    |   |
 ABT SW| |       |  |                              |     ::::::         |   |
     |   |SCN68000  |______________________________|    __________      |   |
HTL LED  |       |  _______________  _______________   |SN74LS645-1N    |   |
    _|_  |       | | EPROM        | | EPROM        |    __________      |   |
   |   | |       | |              | |              |   |SN74LS645-1N    |   |
   |P3 | |       | |______________| |______________|    __________      |   |
   |DB25 |       |  _______________  _______________   |SN74LS645-1N    |   |
   |   | |       | | EPROM        | | EPROM        |    __________      |   |
   |___| |       | |              | |              |   |SN74LS645-1N    |   |
     |   |_______| |______________| |______________|    __________      |   |
    _|_  ..                           ::::::::         |SN74LS645-1N    |   |
   |   | ..   ____________   __________  ::::..         __________      |   |
   |P4 | ..  | HD46850P   | |SN74LS641_|  __________   |AMPAL16L8|      |   |
   |DB25 ..  |            | .. :::::::   |_74F74PC_|    __________      |___|
   |   | ..  |____________|                            |_74F241PC|        |
   |___| ..     __________   __________   __________    __________        |
     |         |_74S240N_|  |_PAL16L6_|  |PAL20l10_|   |SN74LS175N        |
    _|_  .. __________  __________  __________  __________  ___________   |
   |   | ..|_74S240N_| |SN74LS260N |SN74LS148N |SN74LS200N |MM58167AN |   |
   |P5 | .. __________  __________  __________  __________ |          |   |
   |SB25 ..|SN74LS393N |_74F74PC_| |SN74LS688N |_74F37N__| |__________|   |
   |   | .. ________    __________  :::::::::  ..... ::                  _|_
   |___| ..|SN74LS56P  |_74F241PC|       __________            .        |   |
     |      __________  __________      |PAL20L10_|            :        |   |
     |     |SN75188N_| |DM74LS01N|            ________________________  |   |
     |      ______  ______   __________      |                       |  |   |
     |     |     | |     |  |PAL12L10_|      | MC68230L8             |  |   |
     |     | 2 x HD46850P|   __________  ... |_______________________|  |   |
     |     |     | |     |  |SN75189N_| :::          Xtal               |   |
     |     |     | |     |   __________   _________1.8432 MHz_________  |   |
     | ..  |_____| |_____|  |SN75188N_|  |SN74LS556P       |SN74LS175N  |   |
     | ..  ______  ___  __________  __________  __________  __________  |   |
     | .. |     | |  | |_74F74PC_| |_SN74S08N| |_74F74PC_| |SN74LS32N|  |   |
     MC14411P-> | |  |  __________  __________  __________  __________  |   |
     | .. SN75189N->_| |_DL6311__| |_74F37N__| |_NE556N__| |_EP8304__|  |   |
     |    |     |       __________  __________  __________              |   |
     |    |_____|Xtal  |_74F74PC_| |_74F74PC_| |SN74LS56P|   ..         |___|
     |___________32 MHz___________________________________________________|


PCB (B3) (labeled "9000-0025 SS 1-1")
 - HM6116LP-3
 - MK48Z02B-20 NVRAM
 - 2 x HM6264LP-15
 - 3 x 27128 EPROM
 - Fujitsu MB7124H
         _______________________________________              _______________________________________
      __|                                       |____________|                                       |_
     |  |_______________________________________|            |_______________________________________| |
     |   ...                               :::::::                                                     |
     |    _______  __________  __________  __________  __________  __________  __________  __________  |
     |   |_P232_| |CD74HCT08E |SN74LS645| |SN74LS682N |SN74LS645N |SN74LS645N |SN74LS645N |SN74LS645N  |
     |             ______      ______     __________   ______    ______    ______    ______    ______  |
     |            |     |     |     |    |PC74HCT157P | <-HM6264LP-15 |   |     |   |     |   |     |  |
     |   HM6116LP-3->   |     |     |     __________  |     |   | <-HM6264LP-15 |   |     |   | <-EPROM|
     |            |  MK48Z02B-20->  |    |CD74HCT157E |     |   |     |   | <-EPROM |     |   |     |  |
     |            |     |     |     |     __________  |     |   |     |   |     |   | <-EPROM |     |  |
     |            |_____|     |_____|    |CD74HCT157E |_____|   |_____|   |____ |   |_____|   |_____|  |
     |  _________  __________  __________  __________  __________  __________  __________  __________  |
     | CD74HCT32E PC74HCT138P PC74HCT157P  CD74HCT04E PC74HCT164P CD74HCT273E |_MB7124H_|  CD74HCT32E  |
     |  ::::.                                                                                          |
     |  _________  __________  __________  __________  __________  __________  __________  __________  |
     | |__8429__| PC74HCT139P PC74HCT139P CD74HCT174E  CD74HCT74E  SN74LS645N PC74HCT374P  SN74LS645N  |
     |  _________  __________  __________     ::::                                                     |
     | CD74HCT04E  CD74HCT74E  CD74HCT08E                                           _____________      |
     |_____________________________________________________________________________|             |_____|
                                                                                   |_____________|

PCB MEASUREMENT (B2) (labeled "MEASUREMENT CIRCUIT 9000-0024")
 - AM2149-45DC
 - 12 x TMS4416-15NL
 - 2 x Intel P8254
 - Motorola MC68230P10
 - 3 x AM27S21APC PROM
 - Fujitsu MB7124H PROM


PCB VIDEO (B1) (labeled "OPTOMAX VIDEO PCB 9000-0022-2/2")
 - Xtal (unknown frequency, labeled "Xtal Hy-O 2562-50 GE01S")
 - 2716 EPROM
 - 2 x Toshiba TC5565PL-15
 - Motorola MC68230P8
                   ___     ___     ___     ___
                  |   |   |   |   |   |   |   |
   _______________|   |___|   |___|   |___|   |________________________________
  |               |___|   |___|   |___|   |___|                               |
  |                           4 x BNC                                         |
 _|_                ······    __________  __________  __________    ······    |
|   |                        |_74128N__| |PC74HCT04P |PC74HCT74P              |
|   |                         __________  __________  __________  __________  |
|   |                        |HEF4046BP| |SN74LS221N |PC74HCT74P |I1-524-5_|  |
|   |                         ________    __________                          |
|   |                        |CA3140E|   |SN74LS221N                          |
|   |                                     __________                          |
|   |                                    |PC74HCT32P                          |
|   |  _________                          __________                          |
|   | |SN74LS05N                         |PC74HCT08P                          |
|   |                         __________  __________  __________  ________    |
|   |                        |MC74HC04_| |SN74LS123N |_LM361N__| |CA3240E|    |
|   |                                                                         |
|   |                         __________                          __________  |
|   |                        |_LM361N__|                         |I1-201-5_|  |
|___|              _______    __________                                      |
  |               |CA3240E|  |_LM361N__|                                      |
  |    _________  __________  __________                                      |
 _|_ CD74HCT151E |CD74HCT86E |_LM361N__|                          __________  |
|   |             ________                                       |I1-201-5_|  |
|   |            |CA3240E|                                        ________    |
|   |  _________  __________  __________                         |CA3140E|    |
|   | PC74HCT08P |AD7528KN_| |PC74HCT04P                                      |
|   |        ___                          __________  __________  __________  |
|   |       |  |  ..   ________          |_ZN448E__| |__DAC08__| |CD74HCT00E  |
| SN74LS682N-> |  ..  |       |                       __________  __________  |
|   |       |__|  ..  | MC68230P8                    PC74HCT374P |SN74LS221N  |
|   |        ___      |       |                                               |
|   |       |  |      |       |         ____________  __________  __________  |
|   DS75451N->_|      |       |        |TC5565PL-15| CD74HCT393E |SN74LS221N  |
|   |        ___      |       |        |___________|                          |
|   |       |  |      |       |                       __________  __________  |
| SN74LS645N-> |      |       |         ____________ |PC74HCT74P |CD74HCT00E  |
|___|       |__|      |_______|        |TC5565PL-15|  __________  __________  |
  |   BATT             _____           |___________| CD74HCT4040E SN74LS221N  |
  |   __________  _____________  __________           __________              |
  |   PC74HCT32P | EPROM      | CD74HCT4040E         CD74HCT4040E             |
  |              |____________|                                               |
  |   __________   __________    __________           __________              |
  |  |PC74HCT74P  |PC74HCT74P   |CD74HCT00E          |SN74LS682N              |
  |   __________   __________    __________           __________              |
  |  |_ZNA134H_|  |SN74LC221N   |PC74HCT157P         |PC74LS157P              |
  |                                                                           |
  |   Xtal (labeled "Hy-O 2562-50 GE01S")                                     |
  |___________________________________________________________________________|

*********************************************************************************************************/

#include "emu.h"

#include "bus/acorn/bus.h"
#include "bus/rs232/rs232.h"
#include "cpu/m6502/m6502.h"
#include "cpu/m68000/m68000.h"
#include "machine/6522via.h"
#include "machine/68230pit.h"
#include "machine/6850acia.h"
#include "machine/input_merger.h"
#include "machine/m3002.h"
#include "machine/mc14411.h"
#include "machine/mm58167.h"
#include "machine/mos6551.h"
#include "machine/nvram.h"
#include "machine/pit8253.h"
#include "sound/adc.h"

#include "screen.h"


namespace {

class optomaxv_state : public driver_device
{
public:
	optomaxv_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)

		// CPU PCB (B4)
		, m_maincpu(*this, "maincpu")
		, m_b4_acia(*this, "b4_acia%u", 0U)
		, m_b4_brg(*this, "b4_brg")
		, m_b4_rtc(*this, "b4_rtc")
		, m_b4_pit(*this, "b4_pit")
		, m_b4_brf(*this, "BRF")
		, m_b4_baud_p3(*this, "BAUD_P3")
		, m_b4_baud_p4(*this, "BAUD_P4")
		, m_b4_baud_p5(*this, "BAUD_P5")

		// OSD PCB (B6)
		, m_eurocpu(*this, "eurocpu")
		, m_b6_acia(*this, "b6_acia")
		, m_b6_rtc(*this, "b6_rtc")
		, m_b6_via(*this, "b6_via")
		, m_b6_bus(*this, "b6_bus")

		// PCB (B3)
		, m_b3_view(*this, "b3_view")
		, m_b3_ram(*this, "b3_ram", 0x4000, ENDIANNESS_LITTLE)

		// Measurement PCB (B2)
		, m_b2_pit(*this, "b2_pit")
		, m_b2_pit8254(*this, "b2_pit8254_%u", 0U)

		// Video PCB (B1)
		, m_b1_pit(*this, "b1_pit")
	{ }

	void optomaxv(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// CPU PCB (B4)
	required_device<cpu_device> m_maincpu;
	required_device_array<acia6850_device, 3> m_b4_acia;
	required_device<mc14411_device> m_b4_brg;
	required_device<mm58167_device> m_b4_rtc;
	required_device<pit68230_device> m_b4_pit;
	required_ioport m_b4_brf;
	required_ioport m_b4_baud_p3;
	required_ioport m_b4_baud_p4;
	required_ioport m_b4_baud_p5;

	// OSD PCB (B6)
	required_device<m6502_device> m_eurocpu;
	required_device<mos6551_device> m_b6_acia;
	required_device<m3002_device> m_b6_rtc;
	required_device<via6522_device> m_b6_via;
	required_device<acorn_bus_device> m_b6_bus;

	// PCB (B3)
	memory_view m_b3_view;
	memory_share_creator<uint8_t> m_b3_ram;

	// Measurement PCB (B2)
	required_device<pit68230_device> m_b2_pit;
	required_device_array<pit8254_device, 2> m_b2_pit8254;

	// Video PCB (B1)
	required_device<pit68230_device> m_b1_pit;

	void mem_map(address_map &map) ATTR_COLD;
	void osd_map(address_map &map) ATTR_COLD;

	void write_acia_clocks(int id, int state);
	void write_f1_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F1, state); }
	void write_f3_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F3, state); }
	void write_f5_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F5, state); }
	void write_f7_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F7, state); }
	void write_f8_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F8, state); }
	void write_f9_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F9, state); }
	void write_f11_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F11, state); }
	void write_f13_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F13, state); }

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	uint8_t map_r();
	void map_w(uint8_t data);

	uint8_t m_map = 0x00;
};


void optomaxv_state::mem_map(address_map &map)
{
	// Main CPU PCB (B4)

	map(0x000000, 0x07ffff).ram();
	map(0x000000, 0x000007).rom().region("eprom_sys", 0);
	map(0x080000, 0x087fff).rom().region("eprom_sys", 0);
	map(0x0a0000, 0x0affff).rom().region("eprom_usr", 0);
	map(0x0c0040, 0x0c0043).rw(m_b4_acia[0], FUNC(acia6850_device::read), FUNC(acia6850_device::write)).umask16(0x00ff);
	map(0x0c0080, 0x0c0083).rw(m_b4_acia[1], FUNC(acia6850_device::read), FUNC(acia6850_device::write)).umask16(0xff00);
	map(0x0c0100, 0x0c0103).rw(m_b4_acia[2], FUNC(acia6850_device::read), FUNC(acia6850_device::write)).umask16(0x00ff);
	map(0x0c0400, 0x0c042f).rw(m_b4_rtc, FUNC(mm58167_device::read), FUNC(mm58167_device::write)).umask16(0x00ff);
	map(0x0e0000, 0x0e003f).rw(m_b4_pit, FUNC(pit68230_device::read), FUNC(pit68230_device::write)).umask16(0x00ff);

	// Measurement PCB (B2)

	map(0x100000, 0x117fff).ram();
	map(0x140000, 0x14003f).rw(m_b2_pit, FUNC(pit68230_device::read), FUNC(pit68230_device::write)).umask16(0xff00);
	//map(0x160000, 0x160007).rw(m_b2_pit8254, FUNC(pit8254_device::read), FUNC(pit8254_device::write)).umask16(0x00ff);
	//map(0x160008, 0x16000f).rw(m_b2_pit8254, FUNC(pit8254_device::read), FUNC(pit8254_device::write)).umask16(0x00ff);

	// VIDEO PCB (B1)

	// B1 2 x 8K static ram
	// B1 2K rom
	//map(0x200000, 0x201fff).ram();
	map(0x300000, 0x30003f).rw(m_b1_pit, FUNC(pit68230_device::read), FUNC(pit68230_device::write)).umask16(0x00ff);
}

void optomaxv_state::osd_map(address_map &map)
{
	// OSD PCB (B6)

	map(0x0000, 0x1fff).ram();                           // M3
	map(0x8000, 0xbfff).rom().region("b6_rom", 0x0000);  // M1
	map(0xe000, 0xffff).rom().region("b6_rom", 0x6000);  // M0
	map(0xd000, 0xdfff).lrw8(                    // I/O Block
		NAME([this](offs_t offset) { return m_b6_bus->read(offset | 0xd000); }),
		NAME([this](offs_t offset, uint8_t data) { m_b6_bus->write(offset | 0xd000, data); })
	);
	map(0xfe00, 0xfeff).lrw8(                   // I/O Page
		NAME([this](offs_t offset) { return m_b6_bus->read(offset | 0xfe00); }),
		NAME([this](offs_t offset, uint8_t data) { m_b6_bus->write(offset | 0xfe00, data); })
	);
	map(0xfe00, 0xfe0f).rw(m_b6_via, FUNC(via6522_device::read), FUNC(via6522_device::write));
	map(0xfe10, 0xfe17).rw(m_b6_acia, FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0xfe18, 0xfe1f).rw(m_b6_rtc, FUNC(m3002_device::read), FUNC(m3002_device::write));
	map(0xfe30, 0xfe30).nopw(); // usually ROM banking of region 0x8000-0xBFFF but unlikely to be relevant in this machine.

	// PCB (B3)

	map(0x2000, 0x7fff).rom().region("b3_rom", 0x2000);
	map(0x2000, 0x7fff).view(m_b3_view);
	m_b3_view[0](0x2000, 0x2fff).rom().region("b3_rom", 0xa000);
	m_b3_view[1](0x3000, 0x3fff).rom().region("b3_rom", 0xb000);
	m_b3_view[1](0x4000, 0x7fff).ram().share("b3_ram");
	map(0xc000, 0xc7ff).ram().share("nvram");
	map(0xc800, 0xcfff).ram();
	map(0xdc00, 0xdc00).rw(FUNC(optomaxv_state::map_r), FUNC(optomaxv_state::map_w));
}


void optomaxv_state::machine_start()
{
	save_item(NAME(m_map));
}

void optomaxv_state::machine_reset()
{
	// Set up the BRG divider. RSA is a jumper setting and RSB is always set High
	m_b4_brg->rsa_w(m_b4_brf->read() == 0x80 ? ASSERT_LINE : CLEAR_LINE);
	m_b4_brg->rsb_w(ASSERT_LINE);

	// Disable all configured timers, only enabling the used ones
	m_b4_brg->timer_disable_all();
	m_b4_brg->timer_enable((mc14411_device::timer_id) m_b4_baud_p3->read(), true);
	m_b4_brg->timer_enable((mc14411_device::timer_id) m_b4_baud_p4->read(), true);
	m_b4_brg->timer_enable((mc14411_device::timer_id) m_b4_baud_p5->read(), true);

	// Default memory map
	m_b3_view.disable();
}


uint8_t optomaxv_state::map_r()
{
	return m_map;
}

void optomaxv_state::map_w(uint8_t data)
{
	m_map = data;

	switch (m_map)
	{
	case 0x00:
		m_b3_view.disable();
		break;
	case 0x40:
		m_b3_view.select(0);
		break;
	case 0x80:
		m_b3_view.select(1);
		break;
	default:
		logerror("%s map_w: unknown %02x\n", machine().describe_context(), data);
		break;
	}
}


void optomaxv_state::write_acia_clocks(int id, int state)
{
	if (id == m_b4_baud_p3->read())
	{
		m_b4_acia[0]->write_txc(state);
		m_b4_acia[0]->write_rxc(state);
	}
	if (id == m_b4_baud_p4->read())
	{
		m_b4_acia[1]->write_txc(state);
		m_b4_acia[1]->write_rxc(state);
	}
	if (id == m_b4_baud_p5->read())
	{
		m_b4_acia[2]->write_txc(state);
		m_b4_acia[2]->write_rxc(state);
	}
}


uint32_t optomaxv_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}


static INPUT_PORTS_START(optomaxv)
	PORT_START("BRF")
	PORT_CONFNAME(0x80, 0x00, "Baud Rate Factor") // RSA pin on MC14411
	PORT_CONFSETTING(0x00, "1x (Lo)")
	PORT_CONFSETTING(0x80, "4x (Hi)")

	PORT_START("BAUD_P3")
	PORT_CONFNAME(0x0f, 0x00, "P3 Host Baud Lo/Hi") // F1-Fx pins on MC14411
	PORT_CONFSETTING(mc14411_device::TIMER_F1,  "9600/38400") // RSA=1x/16x
	PORT_CONFSETTING(mc14411_device::TIMER_F3,  "4800/19200")
	PORT_CONFSETTING(mc14411_device::TIMER_F5,  "2400/9600")
	PORT_CONFSETTING(mc14411_device::TIMER_F7,  "1200/4800")
	PORT_CONFSETTING(mc14411_device::TIMER_F8,  "600/2400")
	PORT_CONFSETTING(mc14411_device::TIMER_F9,  "300/1200")
	PORT_CONFSETTING(mc14411_device::TIMER_F11, "150/600")
	PORT_CONFSETTING(mc14411_device::TIMER_F13, "110/440")

	PORT_START("BAUD_P4")
	PORT_CONFNAME(0x0f, 0x00, "P4 Terminal Baud Lo/Hi") // F1-Fx pins on MC14411
	PORT_CONFSETTING(mc14411_device::TIMER_F1,  "9600/38400") // RSA=1x/16x
	PORT_CONFSETTING(mc14411_device::TIMER_F3,  "4800/19200")
	PORT_CONFSETTING(mc14411_device::TIMER_F5,  "2400/9600")
	PORT_CONFSETTING(mc14411_device::TIMER_F7,  "1200/4800")
	PORT_CONFSETTING(mc14411_device::TIMER_F8,  "600/2400")
	PORT_CONFSETTING(mc14411_device::TIMER_F9,  "300/1200")
	PORT_CONFSETTING(mc14411_device::TIMER_F11, "150/600")
	PORT_CONFSETTING(mc14411_device::TIMER_F13, "110/440")

	PORT_START("BAUD_P5")
	PORT_CONFNAME(0x0f, 0x00, "P5 Remote Baud Lo/Hi") // F1-Fx pins on MC14411
	PORT_CONFSETTING(mc14411_device::TIMER_F1,  "9600/38400") // RSA=1x/16x
	PORT_CONFSETTING(mc14411_device::TIMER_F3,  "4800/19200")
	PORT_CONFSETTING(mc14411_device::TIMER_F5,  "2400/9600")
	PORT_CONFSETTING(mc14411_device::TIMER_F7,  "1200/4800")
	PORT_CONFSETTING(mc14411_device::TIMER_F8,  "600/2400")
	PORT_CONFSETTING(mc14411_device::TIMER_F9,  "300/1200")
	PORT_CONFSETTING(mc14411_device::TIMER_F11, "150/600")
	PORT_CONFSETTING(mc14411_device::TIMER_F13, "110/440")
INPUT_PORTS_END


static DEVICE_INPUT_DEFAULTS_START(terminal)
	DEVICE_INPUT_DEFAULTS("RS232_RXBAUD", 0xff, RS232_BAUD_9600)
	DEVICE_INPUT_DEFAULTS("RS232_TXBAUD", 0xff, RS232_BAUD_9600)
	DEVICE_INPUT_DEFAULTS("RS232_DATABITS", 0xff, RS232_DATABITS_8)
	DEVICE_INPUT_DEFAULTS("RS232_PARITY", 0xff, RS232_PARITY_NONE)
	DEVICE_INPUT_DEFAULTS("RS232_STOPBITS", 0xff, RS232_STOPBITS_1)
DEVICE_INPUT_DEFAULTS_END


void optomaxv_state::optomaxv(machine_config &config)
{
	// Main CPU PCB (B4)

	M68000(config, m_maincpu, 32_MHz_XTAL / 4); // Signetics SCN68000
	m_maincpu->set_addrmap(AS_PROGRAM, &optomaxv_state::mem_map);

	ACIA6850(config, m_b4_acia[0], 0); // Hitachi HD46850P (Host)
	m_b4_acia[0]->txd_handler().set("rs232host", FUNC(rs232_port_device::write_txd));
	m_b4_acia[0]->rts_handler().set("rs232host", FUNC(rs232_port_device::write_rts));
	m_b4_acia[0]->irq_handler().set_inputline(m_maincpu, M68K_IRQ_2);

	rs232_port_device &rs232host(RS232_PORT(config, "rs232host", default_rs232_devices, nullptr));
	rs232host.rxd_handler().set(m_b4_acia[0], FUNC(acia6850_device::write_rxd));
	rs232host.cts_handler().set(m_b4_acia[0], FUNC(acia6850_device::write_cts));

	ACIA6850(config, m_b4_acia[1], 0); // Hitachi HD46850P (Terminal)
	m_b4_acia[1]->txd_handler().set("rs232term", FUNC(rs232_port_device::write_txd));
	m_b4_acia[1]->rts_handler().set("rs232term", FUNC(rs232_port_device::write_rts));
	m_b4_acia[1]->irq_handler().set_inputline(m_maincpu, M68K_IRQ_4);

	rs232_port_device &rs232term(RS232_PORT(config, "rs232term", default_rs232_devices, "terminal"));
	rs232term.rxd_handler().set(m_b4_acia[1], FUNC(acia6850_device::write_rxd));
	rs232term.cts_handler().set(m_b4_acia[1], FUNC(acia6850_device::write_cts));
	rs232term.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	ACIA6850(config, m_b4_acia[2], 0); // Hitachi HD46850P (Remote)
	m_b4_acia[2]->txd_handler().set("rs232remt", FUNC(rs232_port_device::write_txd));
	m_b4_acia[2]->rts_handler().set("rs232remt", FUNC(rs232_port_device::write_rts));
	m_b4_acia[2]->irq_handler().set_inputline(m_maincpu, M68K_IRQ_3);

	rs232_port_device &rs232remt(RS232_PORT(config, "rs232remt", default_rs232_devices, nullptr));
	rs232remt.rxd_handler().set(m_b4_acia[2], FUNC(acia6850_device::write_rxd));
	rs232remt.cts_handler().set(m_b4_acia[2], FUNC(acia6850_device::write_cts));

	MC14411(config, m_b4_brg, 1.8432_MHz_XTAL); // Motorola MC14411P
	m_b4_brg->out_f<1>().set(FUNC(optomaxv_state::write_f1_clock));
	m_b4_brg->out_f<3>().set(FUNC(optomaxv_state::write_f3_clock));
	m_b4_brg->out_f<5>().set(FUNC(optomaxv_state::write_f5_clock));
	m_b4_brg->out_f<7>().set(FUNC(optomaxv_state::write_f7_clock));
	m_b4_brg->out_f<8>().set(FUNC(optomaxv_state::write_f8_clock));
	m_b4_brg->out_f<9>().set(FUNC(optomaxv_state::write_f9_clock));
	m_b4_brg->out_f<11>().set(FUNC(optomaxv_state::write_f11_clock));
	m_b4_brg->out_f<13>().set(FUNC(optomaxv_state::write_f13_clock));

	MM58167(config, m_b4_rtc, 32.768_kHz_XTAL); // National Semiconductor MM58167AN
	m_b4_rtc->irq().set_inputline(m_maincpu, M68K_IRQ_6);

	PIT68230(config, m_b4_pit, 32_MHz_XTAL / 4); // Motorola MC68230L8
	m_b4_pit->port_irq_callback().set_inputline(m_maincpu, M68K_IRQ_5);

	// OSD PCB (B6) (CUBE EuroBEEB System CPU board)

	M6502(config, m_eurocpu, 2'000'000); // Rockwell R6502AP
	m_eurocpu->set_addrmap(AS_PROGRAM, &optomaxv_state::osd_map);

	INPUT_MERGER_ANY_HIGH(config, "irqs").output_handler().set_inputline(m_eurocpu, M6502_IRQ_LINE);

	MOS6522(config, m_b6_via, 2'000'000); // Rockwell R6522AP
	m_b6_via->irq_handler().set("irqs", FUNC(input_merger_device::in_w<0>));

	MOS6551(config, m_b6_acia, 0); // Rockwell R6551AP
	m_b6_acia->set_xtal(1.8432_MHz_XTAL);
	m_b6_acia->irq_handler().set("irqs", FUNC(input_merger_device::in_w<1>));
	m_b6_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_b6_acia->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "keyboard"));
	rs232.rxd_handler().set(m_b6_acia, FUNC(mos6551_device::write_rxd));
	rs232.dsr_handler().set(m_b6_acia, FUNC(mos6551_device::write_dsr));
	rs232.cts_handler().set(m_b6_acia, FUNC(mos6551_device::write_cts));

	M3002(config, m_b6_rtc, 32.768_kHz_XTAL); // uEM M-3002-16PI Real Time Clock

	ACORN_BUS(config, m_b6_bus, 1'000'000);
	m_b6_bus->out_irq_callback().set("irqs", FUNC(input_merger_device::in_w<2>));
	m_b6_bus->out_nmi_callback().set_inputline(m_eurocpu, M6502_NMI_LINE);

	// Teletext PCB (B5) (CUBE EuroBEEB System Teletext Video Card)

	ACORN_BUS_SLOT(config, "slot1", m_b6_bus, eurocube_bus_devices, "teletext").set_fixed(true);

	// PCB (B3)

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // Mostek MK48Z02B-20 ZeroPower

	// Measurement PCB (B2)

	PIT68230(config, m_b2_pit, 32_MHz_XTAL / 4); // Motorola MC68230P8, unknown xtal
	PIT8254(config, m_b2_pit8254[0]); // Intel P8254
	PIT8254(config, m_b2_pit8254[1]); // Intel P8254

	// VIDEO PCB (B1)

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.screen_vblank().set(m_b1_pit, FUNC(pit68230_device::h2_w));
	screen.set_size(1024, 768);
	screen.set_visarea(0, 704-1, 0, 560-1);
	screen.set_screen_update(FUNC(optomaxv_state::screen_update));

	PIT68230(config, m_b1_pit, 32_MHz_XTAL / 4); // Motorola MC68230P8, unknown xtal

	ZN449(config, "adc", 0); // ZN448
}


ROM_START(optomaxv)
	ROM_REGION16_BE(0x8000, "eprom_sys", 0)
	ROM_LOAD16_BYTE( "b4_hn4827128g.j24",               0x0000, 0x4000, CRC(a6399550) SHA1(602b7152a5a5cbe1f8f598e6622d2bdfccd5d322) )
	ROM_LOAD16_BYTE( "b4_hn4827128g.j40",               0x0001, 0x4000, CRC(b85f37f7) SHA1(dc532ba0af735233c5a7308a5fbc90be746ee08c) )

	ROM_REGION16_BE(0x10000, "eprom_usr", 0)
	ROM_LOAD16_BYTE( "b4_68up-12-01-sd-1-86_27256.j41", 0x0000, 0x8000, CRC(a3196db7) SHA1(06773dea886908673d9def406d4985a6eef71d0c) )
	ROM_LOAD16_BYTE( "b4_68lw-12-01-sd-1-86_27256.j25", 0x0001, 0x8000, CRC(c0a88d38) SHA1(836a616bb6df84e3e6dfb1d42cacd592f6c6b4e6) )

	ROM_REGION(0x8000, "b6_rom", 0)
	ROM_LOAD( "b6_hn613128pb05.m1",                     0x0000, 0x4000, CRC(79434781) SHA1(4a7393f3a45ea309f744441c16723e2ef447a281) ) // Acorn BASIC, on OSD PCB
	ROM_LOAD( "b6_abmon4-abci-12-12_hn482764g.m0",      0x6000, 0x2000, CRC(8f9ce214) SHA1(37fca4a5184025ed034acd5f20a4614163246779) ) // On OSD PCB

	ROM_REGION(0x10000, "b3_rom", 0)
	ROM_LOAD( "b3_6sr01-04-4477-1-86_hn4827128g.ic24",  0x0000, 0x4000, CRC(e9110243) SHA1(c96f6f526b0b1c36971f7a0fc03a6e57d02213e5) ) // On PCB (B3)
	ROM_LOAD( "b3_6sr02-04-8140-1-86_d27128d.ic23",     0x4000, 0x4000, CRC(58c04246) SHA1(7eb0b276206f546b34bc5f32e05d755f1a700294) ) // On PCB (B3)
	ROM_LOAD( "b3_6sr03-04-b03f-1-86_hn4827128g.ic22",  0x8000, 0x4000, CRC(705f0f4d) SHA1(5c836694d736b2d2a268e3a8acdfb76fde6698c4) ) // On PCB (B3)

	ROM_REGION(0x0800, "video", 0)
	ROM_LOAD( "b1_9000-vs-1.1_d2716d.ic46",             0x0000, 0x0800, CRC(ebaefb94) SHA1(ca6d194926a98b846443ce7393e3b44d3e5199f9) ) // On Video PCB (B1)

	ROM_REGION(0x0200, "proms", 0)
	ROM_LOAD( "b6_sp007mp_82s147n.ic9",                 0x0000, 0x0200, CRC(35aaa7a3) SHA1(ebc977ff748a19cd0e9d0626cf7cf97d07656f80) ) // On OSD PCB (B6), for CPU address decoding
	ROM_LOAD( "b6_502_82s147.ic10",                     0x0000, 0x0200, CRC(401fa579) SHA1(e6320f70da9dfed0daae47af7b6cf9f3a62313b2) ) // On OSD PCB (B6), for CPU address decoding. Same as the standard EuroBEEB
	ROM_LOAD( "b3_b515c1-1.ic15",                       0x0000, 0x0200, CRC(c4b02b5f) SHA1(e7b3363974b8a1b61169f543a672dff37e8e0e11) ) // On PCB (B3)
	ROM_LOAD( "b2_cpi-sl-1.3_am27s21a.ic29",            0x0000, 0x0100, CRC(897071f9) SHA1(912154fd24d3601bcfd7fbd61be5c1ade62c12f3) ) // On Measurement PCB (B2)
	ROM_LOAD( "b2_hb-11.2_mb7124h.ic6",                 0x0000, 0x0200, CRC(78bab798) SHA1(f5b88db41efed9c540801c367047d608fb086094) ) // On Measurement PCB (B2)
	ROM_LOAD( "b2_hgb-22.2_am27s21a.ic7",               0x0000, 0x0100, CRC(5c3f4be5) SHA1(1ead926a5c71232c75f20673fe0a7c36ff4480bb) ) // On Measurement PCB (B2)
	ROM_LOAD( "b2_hgw-1.2_am27s21a.ic8",                0x0000, 0x0100, CRC(b45f4d48) SHA1(7cfe7b19efc7d034a5795a99dda347ae742c904d) ) // On Measurement PCB (B2)

	ROM_REGION(0x0117, "plds", 0)
	ROM_LOAD( "b4_0544_pal16l6.j22",                    0x00000, 0x00054, CRC(7d325ea4) SHA1(723a9938b7e3a0edf38261d7b6349efe0443d2e0) ) // On main CPU PCB (B4)
	ROM_LOAD( "b4_0545_pal16l8.j66",                    0x00000, 0x00104, CRC(f1837b78) SHA1(8eb40c7320bd626ec6037662234c5befba88e116) ) // On main CPU PCB (B4)
	ROM_LOAD( "b4_0546_pal12l10c.j17",                  0x00000, 0x00040, CRC(b669fd4a) SHA1(f122719c62e797a1c514389d8ea013e86c8b2040) ) // On main CPU PCB (B4)
	ROM_LOAD( "b4_0620_pal20l10c.j76",                  0x00000, 0x000cc, CRC(51e963ee) SHA1(81f0ad7e2505d31eb00be6742146c80e5631f3f2) ) // On main CPU PCB (B4)
	ROM_LOAD( "b4_0686_pal20l10c.j54",                  0x00000, 0x000cc, CRC(7cf9018b) SHA1(dcbfb718ab076935eb74452ba97a505d0bd34546) ) // On main CPU PCB (B4)
ROM_END

} // anonymous namespace


//    YEAR  NAME       PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY  FULLNAME     FLAGS
SYST( 1986, optomaxv,  0,      0,      optomaxv, optomaxv, optomaxv_state, empty_init, "AMS",   "Optomax V", MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING ) // Hardware from 1985, software on ROM from 1986.



orao.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

Orao driver by Miodrag Milanovic

2008-02-22 Preliminary driver.
2008-02-23 Sound support added.
2008-03-01 Updated to work with latest SVN code

Driver is based on work of Josip Perusanec

Ctrl-V turns on keyclick
Ctrl-S turns on reversed video

BC starts BASIC. orao103: EXIT to quit. orao: unknown how to quit.
To load use LMEM""

Todo:
- When pasting, shift key doesn't work

****************************************************************************/

#include "emu.h"
#include "cpu/m6502/m6502.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/orao_cas.h"

#include "sound/spkrdev.h"
#include "imagedev/cassette.h"

namespace {

class orao_state : public driver_device
{
public:
	orao_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_memory(*this, "memory")
		, m_vram(*this, "videoram")
		, m_maincpu(*this, "maincpu")
		, m_speaker(*this, "speaker")
		, m_cassette(*this, "cassette")
		, m_io_keyboard(*this, "LINE.%d", 0)
	{ }

	void orao(machine_config &config);

	void init_orao();

private:
	u8 kbd_r(offs_t offset);
	void sound_w(offs_t offset, u8 data);
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	u32 screen_update_orao(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void mem_map(address_map &map) ATTR_COLD;

	required_shared_ptr<u8> m_memory;
	required_shared_ptr<u8> m_vram;
	required_device<cpu_device> m_maincpu;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cassette;
	required_ioport_array<20> m_io_keyboard;
	bool m_spr_bit = false;
};


/* Address maps */
void orao_state::mem_map(address_map &map)
{
	map(0x0000, 0x5fff).ram().share("memory");
	map(0x6000, 0x7fff).ram().share("videoram");
	map(0x8000, 0x87ff).r(FUNC(orao_state::kbd_r));
	map(0x8800, 0x8fff).w(FUNC(orao_state::sound_w));
	map(0xa000, 0xafff).ram();  // extension
	map(0xb000, 0xbfff).ram();  // DOS
	map(0xc000, 0xffff).rom().region("maincpu",0);
}


/* Input ports */
// bits 0-3 are masked out in the code
static INPUT_PORTS_START( orao )
	PORT_START("LINE.0")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

	PORT_START("LINE.1")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CR") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0xC0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE.2")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))

	PORT_START("LINE.3")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0xC0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE.4")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')

	PORT_START("LINE.5")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0xC0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE.6")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("LINE.7")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0xC0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE.8")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')

	PORT_START("LINE.9")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0xC0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE.10")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')

	PORT_START("LINE.11")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0xC0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE.12")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')

	PORT_START("LINE.13")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0xC0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE.14")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')

	PORT_START("LINE.15")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0xC0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE.16")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(U'Č')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(U'Ć')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR(U'Ž')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR(':') PORT_CHAR('*')

	PORT_START("LINE.17")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('^') PORT_CHAR('@')
	PORT_BIT(0xC0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE.18")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(U'Đ')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(U'Š')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(';') PORT_CHAR('+')

	PORT_START("LINE.19")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0xC0, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

/* Driver initialization */
void orao_state::init_orao()
{
	memset(m_memory,0xff,0x6000);
}

void orao_state::machine_reset()
{
	m_spr_bit = 0;
}

void orao_state::machine_start()
{
	save_item(NAME(m_spr_bit));
}

u8 orao_state::kbd_r(offs_t offset)
{
	switch(offset)
	{
		/* Keyboard*/
		case 0x07FC : return m_io_keyboard[0]->read();
		case 0x07FD : return m_io_keyboard[1]->read();
		case 0x07FA : return m_io_keyboard[2]->read();
		case 0x07FB : return m_io_keyboard[3]->read();
		case 0x07F6 : return m_io_keyboard[4]->read();
		case 0x07F7 : return m_io_keyboard[5]->read();
		case 0x07EE : return m_io_keyboard[6]->read();
		case 0x07EF : return m_io_keyboard[7]->read();
		case 0x07DE : return m_io_keyboard[8]->read();
		case 0x07DF : return m_io_keyboard[9]->read();
		case 0x07BE : return m_io_keyboard[10]->read();
		case 0x07BF : return m_io_keyboard[11]->read();
		case 0x077E : return m_io_keyboard[12]->read();
		case 0x077F : return m_io_keyboard[13]->read();
		case 0x06FE : return m_io_keyboard[14]->read();
		case 0x06FF : return m_io_keyboard[15]->read();
		case 0x05FE : return m_io_keyboard[16]->read();
		case 0x05FF : return m_io_keyboard[17]->read();
		case 0x03FE : return m_io_keyboard[18]->read();
		case 0x03FF : return m_io_keyboard[19]->read();
		/* Tape */
		case 0x07FF : return (m_cassette->input() >= 0) ? 0xff : 0;
	}

	return 0xff;
}


void orao_state::sound_w(offs_t offset, u8 data)
{
	m_speaker->level_w(m_spr_bit);
	m_cassette->output(m_spr_bit ? 1.0 : -1.0);
	m_spr_bit ^= 1;
}

// bitmapped graphics
u32 orao_state::screen_update_orao(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 addr = 0;
	for (u16 y = 0; y < 256; y++)
	{
		int horpos = 0;
		for (u8 x = 0; x < 32; x++)
		{
			u8 code = m_vram[addr++];
			for (u8 b = 0; b < 8; b++)
			{
				bitmap.pix(y, horpos++) =  (code >> b) & 0x01;
			}
		}
	}
	return 0;
}

/* Machine driver */
void orao_state::orao(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, 8_MHz_XTAL / 8);
	m_maincpu->set_addrmap(AS_PROGRAM, &orao_state::mem_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(256, 256);
	screen.set_visarea(0, 256-1, 0, 256-1);
	screen.set_screen_update(FUNC(orao_state::screen_update_orao));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* audio hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(orao_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("orao_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("orao");
}

/* ROM definition */
ROM_START( orao )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "bas12.ic5", 0x0000, 0x2000, CRC(42ae6f69) SHA1(b9d4a544fae13a9c492af027545178addd557111) )
	ROM_LOAD( "crt12.ic6", 0x2000, 0x2000, CRC(94ebdc94) SHA1(3959d717f96558823ccc806c842d2fb5ab0c3890) )
ROM_END

ROM_START( orao103 )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "bas13.ic5", 0x0000, 0x2000, CRC(35daf5da) SHA1(499c5a4bd930c26ec6226623c2793b4c7f771658) )
	ROM_LOAD( "crt13.ic6", 0x2000, 0x2000, CRC(e7076014) SHA1(0e213287b0b520440af6a2a6297788a9356818c2) )
ROM_END

} // Anonymous namespace

/* Driver */
//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT          COMPANY         FULLNAME    FLAGS
COMP( 1984, orao,    0,      0,      orao,    orao,  orao_state, init_orao,    "PEL Varazdin", "Orao 102", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1985, orao103, orao,   0,      orao,    orao,  orao_state, init_orao,    "PEL Varazdin", "Orao 103", MACHINE_SUPPORTS_SAVE )



oric.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
/*****************************************************************************************************

    drivers/oric.cpp

    Systems supported by this driver:

    Oric-1,
    Oric Atmos,
    Oric Telestrat,
    Pravetz 8D

    Pravetz is a Bulgarian copy of the Oric Atmos and uses
    Apple 2 disc drives for storage.

    This driver originally by Paul Cook, rewritten by Kevin Thacker,
    re-rewritten by Olivier Galibert.



*****************************************************************************************************/

#include "emu.h"

#include "bus/centronics/ctronics.h"
#include "bus/oricext/oricext.h"
#include "cpu/m6502/m6502.h"
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "machine/6522via.h"
#include "machine/mos6551.h"
#include "machine/timer.h"
#include "machine/wd_fdc.h"
#include "sound/ay8910.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "utf8.h"

#include "formats/oric_dsk.h"
#include "formats/oric_tap.h"


namespace {

class oric_state : public driver_device
{
public:
	// Permanent attributes (kept from one line to the other) and line
	// attributes (reset at start of line)
	enum {
		PATTR_50HZ  = 0x02,
		PATTR_HIRES = 0x04,
		LATTR_ALT   = 0x01,
		LATTR_DSIZE = 0x02,
		LATTR_BLINK = 0x04
	};

	oric_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_palette(*this, "palette")
		, m_psg(*this, "ay8912")
		, m_centronics(*this, "centronics")
		, m_cent_data_out(*this, "cent_data_out")
		, m_cassette(*this, "cassette")
		, m_via(*this, "via6522")
		, m_ram(*this, "ram")
		, m_rom(*this, "maincpu")
		, m_c000_view(*this, "c000")
		, m_config(*this, "CONFIG")
		, m_kbd_row(*this, "ROW%u", 0U)
		, m_ext(*this, "ext")
		, m_tape_timer(nullptr)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(nmi_pressed);
	void via_a_w(uint8_t data);
	void via_b_w(uint8_t data);
	void via_ca2_w(int state);
	void via_cb2_w(int state);
	void via_irq_w(int state);
	void ext_irq_w(int state);
	void psg_a_w(uint8_t data);
	TIMER_CALLBACK_MEMBER(update_tape);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;
	uint32_t screen_update_oric(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void vblank_w(int state);

	void oric_common(machine_config &config);
	void oric(machine_config &config);
	void prav8d(machine_config &config);
	void oric_mem(address_map &map) ATTR_COLD;

protected:
	required_device<m6502_device> m_maincpu;
	required_device<palette_device> m_palette;
	required_device<ay8910_device> m_psg;
	required_device<centronics_device> m_centronics;
	required_device<output_latch_device> m_cent_data_out;
	required_device<cassette_image_device> m_cassette;
	required_device<via6522_device> m_via;
	required_shared_ptr<uint8_t> m_ram;
	optional_memory_region m_rom;
	memory_view m_c000_view;
	required_ioport m_config;
	required_ioport_array<8> m_kbd_row;
	optional_device<oricext_connector> m_ext;

	emu_timer *m_tape_timer;

	int m_blink_counter;
	uint8_t m_pattr;
	uint8_t m_via_a, m_via_b, m_psg_a;
	bool m_via_ca2, m_via_cb2, m_via_irq;
	bool m_ext_irq;

	virtual void update_irq();
	void update_psg();
	void update_keyboard();
	void machine_start_common();
};

class telestrat_state : public oric_state
{
public:
	telestrat_state(const machine_config &mconfig, device_type type, const char *tag) :
		oric_state(mconfig, type, tag),
		m_via2(*this, "via6522_2"),
		m_fdc(*this, "fdc"),
		m_telmatic(*this, "telmatic"),
		m_teleass(*this, "teleass"),
		m_hyperbas(*this, "hyperbas"),
		m_telmon24(*this, "telmon24"),
		m_joy1(*this, "JOY1"),
		m_joy2(*this, "JOY2"),
		m_floppies(*this, "fdc:%d", 0U)
	{ }

	void via2_a_w(uint8_t data);
	void via2_b_w(uint8_t data);
	void via2_ca2_w(int state);
	void via2_cb2_w(int state);
	void via2_irq_w(int state);

	void acia_irq_w(int state);

	void fdc_irq_w(int state);
	void fdc_drq_w(int state);
	void fdc_hld_w(int state);

	static void floppy_formats(format_registration &fr);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void telstrat(machine_config &config);
	void telestrat_mem(address_map &map) ATTR_COLD;
protected:
	enum {
		P_IRQEN  = 0x01,
		P_DDS    = 0x04,
		P_DDEN   = 0x08,
		P_SS     = 0x10,
		P_DRIVE  = 0x60
	};

	required_device<via6522_device> m_via2;
	required_device<fd1793_device> m_fdc;
	required_memory_region m_telmatic;
	required_memory_region m_teleass;
	required_memory_region m_hyperbas;
	required_memory_region m_telmon24;
	required_ioport m_joy1;
	required_ioport m_joy2;

	required_device_array<floppy_connector, 4> m_floppies;

	uint8_t m_port_314;
	uint8_t m_via2_a, m_via2_b;
	bool m_via2_ca2, m_via2_cb2, m_via2_irq;
	bool m_acia_irq;
	bool m_fdc_irq, m_fdc_drq, m_fdc_hld;

	virtual void update_irq() override;
	void remap();
	void port_314_w(u8 data);
	u8 port_314_r();
	u8 port_318_r();
};

/* Ram is 64K, with 16K hidden by the rom.  The 300-3ff is also hidden by the i/o */
void oric_state::oric_mem(address_map &map)
{
	map(0x0000, 0xffff).ram().share(m_ram);
	map(0x0300, 0x030f).m(m_via, FUNC(via6522_device::map)).mirror(0xf0);
	map(0xc000, 0xffff).view(m_c000_view);
	m_c000_view[0](0xc000, 0xffff).rom().region(m_rom, 0);
	m_c000_view[0](0xc000, 0xffff).unmapw();
	m_c000_view[1]; // Ram range, ensure it exists
	m_c000_view[2]; // FDC rom range, ensure it exists
}

/*
The telestrat has the memory regions split into 16k blocks.
Memory region &c000-&ffff can be ram or rom. */
void telestrat_state::telestrat_mem(address_map &map)
{
	map(0x0000, 0xffff).ram().share("ram");
	map(0x0300, 0x030f).m(m_via, FUNC(via6522_device::map));
	map(0x0310, 0x0313).rw(m_fdc, FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	map(0x0314, 0x0314).rw(FUNC(telestrat_state::port_314_r), FUNC(telestrat_state::port_314_w));
	map(0x0318, 0x0318).r(FUNC(telestrat_state::port_318_r));
	map(0x031c, 0x031f).rw("acia", FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x0320, 0x032f).m(m_via2, FUNC(via6522_device::map));

	// Theoretically, these are cartridges.  There's no real point to
	// making them configurable, when only 4 existed and there are 7
	// slots.

	map(0xc000, 0xffff).view(m_c000_view);
	m_c000_view[0]; // Ram range, ensure it exists
	m_c000_view[1](0xc000, 0xffff).unmaprw(); // Nothing in that slot
	m_c000_view[2](0xc000, 0xffff).unmaprw(); // Nothing in that slot
	m_c000_view[3](0xc000, 0xffff).unmaprw(); // Nothing in that slot
	m_c000_view[4](0xc000, 0xffff).rom().region(m_telmatic, 0);
	m_c000_view[4](0xc000, 0xffff).unmapw();
	m_c000_view[5](0xc000, 0xffff).rom().region(m_teleass, 0);
	m_c000_view[5](0xc000, 0xffff).unmapw();
	m_c000_view[6](0xc000, 0xffff).rom().region(m_hyperbas, 0);
	m_c000_view[6](0xc000, 0xffff).unmapw();
	m_c000_view[7](0xc000, 0xffff).rom().region(m_telmon24, 0);
	m_c000_view[7](0xc000, 0xffff).unmapw();
}

uint32_t oric_state::screen_update_oric(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	bool blink_state = m_blink_counter & 0x20;
	m_blink_counter = (m_blink_counter + 1) & 0x3f;

	uint8_t pattr = m_pattr;

	for(int y=0; y<224; y++) {
		// Line attributes and current colors
		uint8_t lattr = 0;
		uint32_t fgcol = m_palette->pen_color(7);
		uint32_t bgcol = m_palette->pen_color(0);

		uint32_t *p = &bitmap.pix(y);

		for(int x=0; x<40; x++) {
			// Lookup the byte and, if needed, the pattern data
			uint8_t ch, pat;
			if((pattr & PATTR_HIRES) && y < 200)
				ch = pat = m_ram[0xa000 + y*40 + x];

			else {
				ch = m_ram[0xbb80 + (y>>3)*40 + x];
				int off = (lattr & LATTR_DSIZE ? y >> 1 : y ) & 7;
				const uint8_t *base;
				if(pattr & PATTR_HIRES)
					if(lattr & LATTR_ALT)
						base = m_ram + 0x9c00;
					else
						base = m_ram + 0x9800;
				else
					if(lattr & LATTR_ALT)
						base = m_ram + 0xb800;
					else
						base = m_ram + 0xb400;
				pat = base[((ch & 0x7f) << 3) | off];
			}

			// Handle state-chaging attributes
			if(!(ch & 0x60)) {
				pat = 0x00;
				switch(ch & 0x18) {
				case 0x00: fgcol = m_palette->pen_color(ch & 7); break;
				case 0x08: lattr = ch & 7; break;
				case 0x10: bgcol = m_palette->pen_color(ch & 7); break;
				case 0x18: pattr = ch & 7; break;
				}
			}

			// Pick up the colors for the pattern
			uint32_t c_fgcol = fgcol;
			uint32_t c_bgcol = bgcol;

			// inverse video
			if(ch & 0x80) {
				c_bgcol = c_bgcol ^ 0xffffff;
				c_fgcol = c_fgcol ^ 0xffffff;
			}
			// blink
			if((lattr & LATTR_BLINK) && blink_state)
				c_fgcol = c_bgcol;

			// Draw the pattern
			*p++ = pat & 0x20 ? c_fgcol : c_bgcol;
			*p++ = pat & 0x10 ? c_fgcol : c_bgcol;
			*p++ = pat & 0x08 ? c_fgcol : c_bgcol;
			*p++ = pat & 0x04 ? c_fgcol : c_bgcol;
			*p++ = pat & 0x02 ? c_fgcol : c_bgcol;
			*p++ = pat & 0x01 ? c_fgcol : c_bgcol;
		}
	}

	m_pattr = pattr;

	return 0;
}

void oric_state::update_keyboard()
{
	m_via->write_pb3((m_kbd_row[m_via_b & 7]->read() | m_psg_a) != 0xff);
}

void oric_state::update_psg()
{
	if(m_via_ca2)
		if(m_via_cb2)
			m_psg->address_w(m_via_a);
		else
			m_via->write_pa(m_psg->data_r());
	else if(m_via_cb2)
		m_psg->data_w(m_via_a);
}

void oric_state::update_irq()
{
	m_maincpu->set_input_line(m6502_device::IRQ_LINE, m_via_irq || m_ext_irq ? ASSERT_LINE : CLEAR_LINE);
}

INPUT_CHANGED_MEMBER(oric_state::nmi_pressed)
{
	m_maincpu->set_input_line(m6502_device::NMI_LINE, newval ? ASSERT_LINE : CLEAR_LINE);
}

void oric_state::via_a_w(uint8_t data)
{
	m_via_a = data;
	m_cent_data_out->write(m_via_a);
	update_psg();
}

void oric_state::via_b_w(uint8_t data)
{
	m_via_b = data;
	update_keyboard();
	m_centronics->write_strobe(data & 0x10 ? 1 : 0);
	m_cassette->change_state(data & 0x40 ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED,
								CASSETTE_MOTOR_DISABLED);
	m_cassette->output(data & 0x80 ? -1.0 : +1.0);
}

void oric_state::via_ca2_w(int state)
{
	m_via_ca2 = state;
	update_psg();
}

void oric_state::via_cb2_w(int state)
{
	m_via_cb2 = state;
	update_psg();
}

void oric_state::via_irq_w(int state)
{
	m_via_irq = state;
	update_irq();
}

void oric_state::ext_irq_w(int state)
{
	m_ext_irq = state;
	update_irq();
}

void oric_state::psg_a_w(uint8_t data)
{
	m_psg_a = data;
	update_keyboard();
}

TIMER_CALLBACK_MEMBER(oric_state::update_tape)
{
	if(!m_config->read())
		m_via->write_cb1(m_cassette->input() > 0.0038);
}

void oric_state::vblank_w(int state)
{
	if(m_config->read())
		m_via->write_cb1(state);
}

void oric_state::video_start()
{
	m_blink_counter = 0;
	m_pattr = 0;
}

void oric_state::machine_start_common()
{
	m_via_a = 0xff;
	m_via_b = 0xff;
	m_psg_a = 0x00;
	m_via_ca2 = false;
	m_via_cb2 = false;
	m_via_irq = false;
	m_ext_irq = false;

	if(!m_tape_timer)
		m_tape_timer = timer_alloc(FUNC(oric_state::update_tape), this);

	save_item(NAME(m_blink_counter));
	save_item(NAME(m_pattr));
	save_item(NAME(m_via_a));
	save_item(NAME(m_via_b));
	save_item(NAME(m_psg_a));
	save_item(NAME(m_via_ca2));
	save_item(NAME(m_via_cb2));
	save_item(NAME(m_via_irq));
	save_item(NAME(m_ext_irq));
}

void oric_state::machine_start()
{
	machine_start_common();

	m_c000_view.select(0);
	m_ext->set_view(m_c000_view);
	m_ext->map_io(m_maincpu->space(AS_PROGRAM));
	m_ext->map_rom();
}

void oric_state::machine_reset()
{
	m_ram[0xe000] = 0x42; // Microdisc needs a non-fully-zero high ram

	m_tape_timer->adjust(attotime::from_hz(4800), 0, attotime::from_hz(4800));
}

void telestrat_state::machine_start()
{
	machine_start_common();
	m_fdc_irq = m_fdc_drq = m_fdc_hld = false;
	m_acia_irq = false;

	save_item(NAME(m_port_314));
	save_item(NAME(m_via2_a));
	save_item(NAME(m_via2_b));
	save_item(NAME(m_via2_ca2));
	save_item(NAME(m_via2_cb2));
	save_item(NAME(m_via2_irq));
	save_item(NAME(m_acia_irq));
	save_item(NAME(m_fdc_irq));
	save_item(NAME(m_fdc_drq));
	save_item(NAME(m_fdc_hld));
}

void telestrat_state::machine_reset()
{
	m_tape_timer->adjust(attotime::from_hz(4800), 0, attotime::from_hz(4800));
	m_port_314 = 0x00;
	m_via2_a = 0xff;
	m_c000_view.select(7);
}

void telestrat_state::update_irq()
{
	m_maincpu->set_input_line(m6502_device::IRQ_LINE,
								m_via_irq ||
								m_ext_irq ||
								(m_fdc_irq && (m_port_314 & P_IRQEN)) ||
								m_via2_irq ||
								m_acia_irq ? ASSERT_LINE : CLEAR_LINE);
}

void telestrat_state::via2_a_w(uint8_t data)
{
	m_via2_a = data;
	m_c000_view.select(data & 7);
}

void telestrat_state::via2_b_w(uint8_t data)
{
	m_via2_b = data;
	uint8_t port = 0xff;
	if(!(m_via2_b & 0x40))
		port &= m_joy1->read();
	if(!(m_via2_b & 0x80))
		port &= m_joy2->read();

	m_via2->write_pb(port);
}

void telestrat_state::via2_ca2_w(int state)
{
	m_via2_ca2 = state;
}

void telestrat_state::via2_cb2_w(int state)
{
	m_via2_cb2 = state;
}

void telestrat_state::via2_irq_w(int state)
{
	m_via2_irq = state;
	update_irq();
}

void telestrat_state::port_314_w(u8 data)
{
	m_port_314 = data;
	floppy_image_device *floppy = m_floppies[(m_port_314 >> 5) & 3]->get_device();
	m_fdc->set_floppy(floppy);
	m_fdc->dden_w(m_port_314 & P_DDEN);
	if(floppy) {
		floppy->ss_w(m_port_314 & P_SS ? 1 : 0);
		floppy->mon_w(0);
	}
	update_irq();
}

u8 telestrat_state::port_314_r()
{
	return (m_fdc_irq && (m_port_314 & P_IRQEN)) ? 0x7f : 0xff;
}

u8 telestrat_state::port_318_r()
{
	return m_fdc_drq ? 0x7f : 0xff;
}


void telestrat_state::acia_irq_w(int state)
{
	m_acia_irq = state;
	update_irq();
}

void telestrat_state::fdc_irq_w(int state)
{
	m_fdc_irq = state;
	update_irq();
}

void telestrat_state::fdc_drq_w(int state)
{
	m_fdc_drq = state;
}

void telestrat_state::fdc_hld_w(int state)
{
	m_fdc_hld = state;
}


static INPUT_PORTS_START(oric)
	PORT_START("ROW0")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)           PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)           PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(24)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)           PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)           PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR(22)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)           PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)           PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(14)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)           PORT_CHAR('7') PORT_CHAR('&')

	PORT_START("ROW1")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)           PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR(4)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)           PORT_CHAR('q') PORT_CHAR('Q') PORT_CHAR(17)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)         PORT_CHAR(27)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)           PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR(6)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)           PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR(18)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)           PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(20)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)           PORT_CHAR('j') PORT_CHAR('J') PORT_CHAR(10)

	PORT_START("ROW2")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)           PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR(3)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)           PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)           PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(26)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)           PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)           PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR(2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)           PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)           PORT_CHAR('m') PORT_CHAR('M')

	PORT_START("ROW3")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)       PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)   PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)       PORT_CHAR('-') PORT_CHAR(0xA3)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)       PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)           PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)           PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR(11)

	PORT_START("ROW4")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)       PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)        PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)        PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)      PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)          PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)        PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)       PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)       PORT_CHAR(' ')

	PORT_START("ROW5")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)   PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)  PORT_CHAR(']') PORT_CHAR('}') PORT_CHAR(29)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del")               PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)           PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR(16)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)           PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(15)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)           PORT_CHAR('i') PORT_CHAR('I') PORT_CHAR(9)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)           PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(21)

	PORT_START("ROW6")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)           PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR(23)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)           PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR(19)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)           PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR(1)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)           PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(5)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)           PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR(7)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)           PORT_CHAR('h') PORT_CHAR('H') PORT_CHAR(8)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)           PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR(25)

	PORT_START("ROW7")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)      PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return")            PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)       PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)           PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)           PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR(12)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)           PORT_CHAR('8') PORT_CHAR('*')

	PORT_START("NMI")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("NMI") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oric_state::nmi_pressed), 0)

	/* vsync cable hardware. This is a simple cable connected to the video output
	to the monitor/television. The sync signal is connected to the cassette input
	allowing interrupts to be generated from the vsync signal. */
	PORT_START("CONFIG")
	PORT_CONFNAME(0x01, 0x00, "Tape input")
	PORT_CONFSETTING(   0x00, "Tape")
	PORT_CONFSETTING(   0x01, "VSync cable")
INPUT_PORTS_END

static INPUT_PORTS_START(orica)
	PORT_INCLUDE( oric )

	PORT_MODIFY("ROW5")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Funct") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT))
INPUT_PORTS_END

static INPUT_PORTS_START(prav8d)
	PORT_START("ROW0")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)                                   PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X \xd0\xac") PORT_CODE(KEYCODE_X)           PORT_CHAR('X')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)                                   PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V \xd0\x96") PORT_CODE(KEYCODE_V)           PORT_CHAR('V')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)                                   PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N \xd0\x9d") PORT_CODE(KEYCODE_N)           PORT_CHAR('N')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)                                   PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("ROW1")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D \xd0\x94") PORT_CODE(KEYCODE_D)           PORT_CHAR('D')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q \xd0\xaf") PORT_CODE(KEYCODE_Q)           PORT_CHAR('Q')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC)                PORT_CHAR(27)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F \xd0\xa4") PORT_CODE(KEYCODE_F)           PORT_CHAR('F')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R \xd0\xa0") PORT_CODE(KEYCODE_R)           PORT_CHAR('R')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T \xd0\xa2") PORT_CODE(KEYCODE_T)           PORT_CHAR('T')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J \xd0\x99") PORT_CODE(KEYCODE_J)           PORT_CHAR('J')

	PORT_START("ROW2")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C \xd0\xa6") PORT_CODE(KEYCODE_C)           PORT_CHAR('C')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)                                   PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z \xd0\x97") PORT_CODE(KEYCODE_Z)           PORT_CHAR('Z')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MK") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)                                   PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B \xd0\x91") PORT_CODE(KEYCODE_B)           PORT_CHAR('B')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)                                   PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M \xd0\x9c") PORT_CODE(KEYCODE_M)           PORT_CHAR('M')

	PORT_START("ROW3")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("] \xd0\xa9") PORT_CODE(KEYCODE_QUOTE)       PORT_CHAR(']')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)                           PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C/L") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) // this one is 5th line, 1st key from right
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)                               PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[ \xd0\xa8") PORT_CODE(KEYCODE_COLON)       PORT_CHAR('[')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)                                   PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K \xd0\x9a") PORT_CODE(KEYCODE_K)           PORT_CHAR('K')

	PORT_START("ROW4")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT)         PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN)           PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT)           PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)                              PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP)               PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)                                PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)                               PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)                               PORT_CHAR(' ')

	PORT_START("ROW5")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@ \xd0\xae") PORT_CODE(KEYCODE_OPENBRACE)   PORT_CHAR('@')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\ \xd0\xad") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('\\')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del") PORT_CODE(KEYCODE_DEL)                PORT_CHAR(8) // this one is 5th line, 1st key from left
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P \xd0\x9f") PORT_CODE(KEYCODE_P)           PORT_CHAR('P')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O \xd0\x9e") PORT_CODE(KEYCODE_O)           PORT_CHAR('O')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I \xd0\x98") PORT_CODE(KEYCODE_I)           PORT_CHAR('I')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U \xd0\xa3") PORT_CODE(KEYCODE_U)           PORT_CHAR('U')

	PORT_START("ROW6")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W \xd0\x92") PORT_CODE(KEYCODE_W)           PORT_CHAR('W')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S \xd0\xa1") PORT_CODE(KEYCODE_S)           PORT_CHAR('S')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A \xd0\x90") PORT_CODE(KEYCODE_A)           PORT_CHAR('A')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E \xd0\x95") PORT_CODE(KEYCODE_E)           PORT_CHAR('E')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G \xd0\x93") PORT_CODE(KEYCODE_G)           PORT_CHAR('G')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H \xd0\xa5") PORT_CODE(KEYCODE_H)           PORT_CHAR('H')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y \xd0\xaa") PORT_CODE(KEYCODE_Y)           PORT_CHAR('Y')

	PORT_START("ROW7")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)                              PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^ \xd0\xa7") PORT_CODE(KEYCODE_BACKSLASH)   PORT_CHAR('^') // this one would be on 2nd line, 3rd key from 'P'
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER)           PORT_CHAR(13)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)                               PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)                                   PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L \xd0\x9b") PORT_CODE(KEYCODE_L)           PORT_CHAR('L')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)                                   PORT_CHAR('8') PORT_CHAR('(')

	PORT_START("NMI")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("NMI") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oric_state::nmi_pressed), 0)

	/* vsync cable hardware. This is a simple cable connected to the video output
	to the monitor/television. The sync signal is connected to the cassette input
	allowing interrupts to be generated from the vsync signal. */
	PORT_START("CONFIG")
	PORT_CONFNAME(0x01, 0x00, "Tape input")
	PORT_CONFSETTING(   0x00, "Tape")
	PORT_CONFSETTING(   0x01, "VSync cable")
INPUT_PORTS_END

static INPUT_PORTS_START(telstrat)
	PORT_INCLUDE( orica )

// The telestrat does not have the NMI button
	PORT_MODIFY("NMI")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("JOY1")      /* left joystick port */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)  PORT_8WAY PORT_PLAYER(1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON1)                  PORT_PLAYER(1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)  PORT_8WAY PORT_PLAYER(1)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)    PORT_8WAY PORT_PLAYER(1)

	PORT_START("JOY2")      /* right joystick port */
	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_BUTTON1)                  PORT_PLAYER(2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)  PORT_8WAY PORT_PLAYER(2)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)    PORT_8WAY PORT_PLAYER(2)
INPUT_PORTS_END


void oric_state::oric_common(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, 12_MHz_XTAL / 12);
	m_maincpu->set_addrmap(AS_PROGRAM, &oric_state::oric_mem);

	config.set_maximum_quantum(attotime::from_hz(60));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(12_MHz_XTAL / 2, 64*6, 0, 40*6, 312, 0, 28*8); // 260 lines in 60 Hz mode
	screen.set_screen_update(FUNC(oric_state::screen_update_oric));
	screen.screen_vblank().set(FUNC(oric_state::vblank_w));

	PALETTE(config, m_palette, palette_device::RGB_3BIT);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	AY8912(config, m_psg, 12_MHz_XTAL / 12);
	m_psg->set_flags(AY8910_DISCRETE_OUTPUT);
	m_psg->set_resistors_load(4700, 4700, 4700);
	m_psg->port_a_write_callback().set(FUNC(oric_state::psg_a_w));
	m_psg->add_route(ALL_OUTPUTS, "mono", 0.25);

	/* printer */
	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set(m_via, FUNC(via6522_device::write_ca1));

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	/* cassette */
	CASSETTE(config, m_cassette, 0);
	m_cassette->set_formats(oric_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("oric1_cass");
	SOFTWARE_LIST(config, "oric1_cass").set_original("oric1_cass");

	/* via */
	MOS6522(config, m_via, 12_MHz_XTAL / 12);
	m_via->writepa_handler().set(FUNC(oric_state::via_a_w));
	m_via->writepb_handler().set(FUNC(oric_state::via_b_w));
	m_via->ca2_handler().set(FUNC(oric_state::via_ca2_w));
	m_via->cb2_handler().set(FUNC(oric_state::via_cb2_w));
	m_via->irq_handler().set(FUNC(oric_state::via_irq_w));
}

void oric_state::oric(machine_config &config)
{
	oric_common(config);

	/* extension port */
	ORICEXT_CONNECTOR(config, m_ext, oricext_intf, nullptr);
	m_ext->irq_callback().set(FUNC(oric_state::ext_irq_w));
	m_ext->reset_callback().set_inputline(m_maincpu, INPUT_LINE_RESET);
}

void oric_state::prav8d(machine_config &config)
{
	oric(config);
}

void telestrat_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_ORIC_DSK_FORMAT);
}

static void telestrat_floppies(device_slot_interface &device)
{
	device.option_add("3dsdd", FLOPPY_3_DSDD);
}

void telestrat_state::telstrat(machine_config &config)
{
	oric(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &telestrat_state::telestrat_mem);

	/* acia */
	mos6551_device &acia(MOS6551(config, "acia", 0));
	acia.set_xtal(1.8432_MHz_XTAL);
	acia.irq_handler().set(FUNC(telestrat_state::acia_irq_w));

	/* via */
	MOS6522(config, m_via2, 12_MHz_XTAL / 12);
	m_via2->writepa_handler().set(FUNC(telestrat_state::via2_a_w));
	m_via2->writepb_handler().set(FUNC(telestrat_state::via2_b_w));
	m_via2->ca2_handler().set(FUNC(telestrat_state::via2_ca2_w));
	m_via2->cb2_handler().set(FUNC(telestrat_state::via2_cb2_w));
	m_via2->irq_handler().set(FUNC(telestrat_state::via2_irq_w));

	/* microdisc */
	FD1793(config, m_fdc, 8_MHz_XTAL / 8);
	m_fdc->intrq_wr_callback().set(FUNC(telestrat_state::fdc_irq_w));
	m_fdc->drq_wr_callback().set(FUNC(telestrat_state::fdc_drq_w));
	m_fdc->hld_wr_callback().set(FUNC(telestrat_state::fdc_hld_w));
	m_fdc->set_force_ready(true);

	FLOPPY_CONNECTOR(config, "fdc:0", telestrat_floppies, "3dsdd", telestrat_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", telestrat_floppies, nullptr, telestrat_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:2", telestrat_floppies, nullptr, telestrat_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:3", telestrat_floppies, nullptr, telestrat_state::floppy_formats);
}


ROM_START(oric1)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_SYSTEM_BIOS( 0, "ver10", "Basic 1.0")
	ROMX_LOAD ("basic10.rom", 0, 0x04000, CRC(f18710b4) SHA1(333116e6884d85aaa4dfc7578a91cceeea66d016), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "ver10uk", "Basic 1.0 UK")
	ROMX_LOAD( "basic10uk.rom", 0, 0x04000, CRC(d6006a01) SHA1(84759cc8faae53070f1c8ce8408982a0edcb0796), ROM_BIOS(1))
ROM_END

ROM_START(orica)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_SYSTEM_BIOS( 0, "ver11", "Basic 1.1")
	ROMX_LOAD ("basic11b.rom", 0, 0x04000, CRC(c3a92bef) SHA1(9451a1a09d8f75944dbd6f91193fc360f1de80ac), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "ver12", "Basic 1.2 (Pascal Leclerc)")      // 1987/1999 - various enhancements and bugfixes
	ROMX_LOAD ("basic12.rom",  0, 0x04000, CRC(dc4f22dc) SHA1(845e1a893de3dc0f856fdf2f69c3b73770b4094f), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "ver121", "Basic 1.21 (Pascal Leclerc)")        // 07.1999 - DRAW enhancement
	ROMX_LOAD ("basic121.rom", 0, 0x04000, CRC(0a2860b1) SHA1(b727d5c3bbc8cb1d510f224eb1e0d90d609e8506), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "ver122", "Basic 1.22 (Pascal Leclerc)")        // 08.2001 - added EUR symbol
	ROMX_LOAD ("basic122.rom", 0, 0x04000, CRC(5ef2a861) SHA1(9ab6dc47b6e9dc65a4137ce0f0f12fc2b6ca8442), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "ver11de", "Basic 1.1 DE")
	ROMX_LOAD( "bas11_de.rom", 0, 0x04000, CRC(65233b2d) SHA1(b01cabb1a21980a6785a2fe37a8f8572c892123f), ROM_BIOS(4))
	ROM_SYSTEM_BIOS( 5, "ver11es", "Basic 1.1 ES")
	ROMX_LOAD( "bas11_es.rom", 0, 0x04000, CRC(47bf26c7) SHA1(4fdbadd68db9ab8ad1cd56b4e5cbe51a9c3f11ae), ROM_BIOS(5))
	ROM_SYSTEM_BIOS( 6, "ver11fr", "Basic 1.1 FR")
	ROMX_LOAD( "bas11_fr.rom", 0, 0x04000, CRC(603b1fbf) SHA1(2a4583df3b59ca454d67d5631f242c96ec4cf99a), ROM_BIOS(6))
	ROM_SYSTEM_BIOS( 7, "ver11se", "Basic 1.1 SE")
	ROMX_LOAD( "bas11_se.rom", 0, 0x04000, CRC(a71523ac) SHA1(ce53acf84baec6ab5cbac9f9cefa71b3efeb2ead), ROM_BIOS(7))
	ROM_SYSTEM_BIOS( 8, "ver11uk", "Basic 1.1 UK")
	ROMX_LOAD( "bas11_uk.rom", 0, 0x04000, CRC(303370d1) SHA1(589ff66fac8e06d65af3369491faa67a71f1322a), ROM_BIOS(8))
	ROM_SYSTEM_BIOS( 9, "ver12es", "Basic 1.2 ES")
	ROMX_LOAD( "bas12es_le.rom", 0, 0x04000, CRC(70de4aeb) SHA1(b327418aa7d8a5a03c135e3d8acdd511df625893), ROM_BIOS(9))
	ROM_SYSTEM_BIOS( 10, "ver12fr", "Basic 1.2 FR")
	ROMX_LOAD( "bas12fr_le.rom", 0, 0x04000, CRC(47a437fc) SHA1(70271bc3ed5c3bf4d339d6f5de3de8c3c50ff573), ROM_BIOS(10))
	ROM_SYSTEM_BIOS( 11, "ver12ge", "Basic 1.2 GE")
	ROMX_LOAD( "bas12ge_le.rom", 0, 0x04000, CRC(f5f0dd52) SHA1(75359302452ee7b19537698f124aaefd333688d0), ROM_BIOS(11))
	ROM_SYSTEM_BIOS( 12, "ver12sw", "Basic 1.2 SW")
	ROMX_LOAD( "bas12sw_le.rom", 0, 0x04000, CRC(100abe68) SHA1(6211d5969c4d7a6acb86ed19c5e51a33a3bef431), ROM_BIOS(12))
	ROM_SYSTEM_BIOS( 13, "ver12uk", "Basic 1.2 UK")
	ROMX_LOAD( "bas12uk_le.rom", 0, 0x04000, CRC(00fce8a6) SHA1(d40558bdf61b8aba6260293c9424fd463be7fad8), ROM_BIOS(13))
	ROM_SYSTEM_BIOS( 14, "ver121es", "Basic 1.211 ES")
	ROMX_LOAD( "bas121es_le.rom", 0, 0x04000, CRC(87ec679b) SHA1(5de6a5f5121f69069c9b93d678046e814b5b64e9), ROM_BIOS(14))
	ROM_SYSTEM_BIOS( 15, "ver121fr", "Basic 1.211 FR")
	ROMX_LOAD( "bas121fr_le.rom", 0, 0x04000, CRC(e683dec2) SHA1(20df7ebc0f13aa835f286d50137f1a7ff7430c29), ROM_BIOS(15))
	ROM_SYSTEM_BIOS( 16, "ver121ge", "Basic 1.211 GE")
	ROMX_LOAD( "bas121ge_le.rom", 0, 0x04000, CRC(94fe32bf) SHA1(1024776d20030d602e432e50014502524658643a), ROM_BIOS(16))
	ROM_SYSTEM_BIOS( 17, "ver121sw", "Basic 1.211 SW")
	ROMX_LOAD( "bas121sw_le.rom", 0, 0x04000, CRC(e6ad11c7) SHA1(309c94a9861fcb770636dcde1801a5c68ca819b4), ROM_BIOS(17))
	ROM_SYSTEM_BIOS( 18, "ver121uk", "Basic 1.211 UK")
	ROMX_LOAD( "bas121uk_le.rom", 0, 0x04000, CRC(75aa1aa9) SHA1(ca99e244d9cbef625344c2054023504a4f9dcfe4), ROM_BIOS(18))
	ROM_SYSTEM_BIOS( 19, "ver122es", "Basic 1.22 ES")
	ROMX_LOAD( "bas122es_le.rom", 0, 0x04000, CRC(9144f9e0) SHA1(acf2094078af057e74a31d90d7010be51b9033fa), ROM_BIOS(19))
	ROM_SYSTEM_BIOS( 20, "ver122fr", "Basic 1.22 FR")
	ROMX_LOAD( "bas122fr_le.rom", 0, 0x04000, CRC(370cfda4) SHA1(fad9a0661256e59bcc2915578647573e4128e1bb), ROM_BIOS(20))
	ROM_SYSTEM_BIOS( 21, "ver122ge", "Basic 1.22 GE")
	ROMX_LOAD( "bas122ge_le.rom", 0, 0x04000, CRC(9a42bd62) SHA1(8a9c80f314daf4e5e64fa202e583b8a65796db8b), ROM_BIOS(21))
	ROM_SYSTEM_BIOS( 22, "ver122sw", "Basic 1.22 SW")
	ROMX_LOAD( "bas122sw_le.rom", 0, 0x04000, CRC(e7fd57a4) SHA1(c75cbf7cfafaa02712dc7ca2f972220aef86fb8d), ROM_BIOS(22))
	ROM_SYSTEM_BIOS( 23, "ver122uk", "Basic 1.22 UK")
	ROMX_LOAD( "bas122uk_le.rom", 0, 0x04000, CRC(9865bcd7) SHA1(2a92e2d119463e682bf10647e3880e26656d65b5), ROM_BIOS(23))
ROM_END

ROM_START(telstrat)
	ROM_REGION(0x4000, "telmatic", 0)
	ROM_LOAD ("telmatic.rom", 0, 0x2000, CRC(94358dc6) SHA1(35f92a0477a88f5cf564971125047ffcfa02ec10) )
	ROM_RELOAD (0x2000, 0x2000)

	ROM_REGION(0x4000, "teleass", 0)
	ROM_LOAD ("teleass.rom",  0, 0x4000, CRC(68b0fde6) SHA1(9e9af51dae3199cccf49ab3f0d47e2b9be4ba97d) )

	ROM_REGION(0x4000, "hyperbas", 0)
	ROM_LOAD ("hyperbas.rom", 0, 0x4000, CRC(1d96ab50) SHA1(f5f70a0eb59f8cd6c261e179ae78ef906f68ed63) )

	ROM_REGION(0x4000, "telmon24", 0)
	ROM_LOAD ("telmon24.rom", 0, 0x4000, CRC(aa727c5d) SHA1(86fc8dc0932f983efa199e31ae05a4424772f959) )
ROM_END

ROM_START(prav8d)
	ROM_REGION(0x4000, "maincpu", 0)   /* 0x10000 + 0x04000 + 0x00100 + 0x00200 */
	ROM_LOAD( "pravetzt.rom", 0, 0x4000, CRC(58079502) SHA1(7afc276cb118adff72e4f16698f94bf3b2c64146) )
//  ROM_LOAD_OPTIONAL( "8ddoslo.rom", 0x014000, 0x0100, CRC(0c82f636) SHA1(b29d151a0dfa3c7cd50439b51d0a8f95559bc2b6) )
//  ROM_LOAD_OPTIONAL( "8ddoshi.rom", 0x014100, 0x0200, CRC(66309641) SHA1(9c2e82b3c4d385ade6215fcb89f8b92e6fd2bf4b) )
ROM_END

ROM_START(prav8dd)
	ROM_REGION(0x4000, "maincpu", 0)   /* 0x10000 + 0x04000 + 0x00100 + 0x00200 */
	ROM_SYSTEM_BIOS( 0, "default", "Disk ROM, 1989")
	ROMX_LOAD( "8d.rom",       0, 0x4000, CRC(b48973ef) SHA1(fd47c977fc215a3b577596a7483df53e8a1e9c83), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "radosoft", "RadoSoft Disk ROM, 1992")
	ROMX_LOAD( "pravetzd.rom", 0, 0x4000, CRC(f8d23821) SHA1(f87ad3c5832773b6e0614905552a80c98dc8e2a5), ROM_BIOS(1) )
//  ROM_LOAD_OPTIONAL( "8ddoslo.rom", 0x014000, 0x0100, CRC(0c82f636) SHA1(b29d151a0dfa3c7cd50439b51d0a8f95559bc2b6) )
//  ROM_LOAD_OPTIONAL( "8ddoshi.rom", 0x014100, 0x0200, CRC(66309641) SHA1(9c2e82b3c4d385ade6215fcb89f8b92e6fd2bf4b) )
ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS            INIT        COMPANY      FULLNAME                 FLAGS
COMP( 1983, oric1,    0,      0,      oric,     oric,     oric_state,      empty_init, "Tangerine", "Oric-1" ,               MACHINE_SUPPORTS_SAVE )
COMP( 1984, orica,    oric1,  0,      oric,     orica,    oric_state,      empty_init, "Tangerine", "Oric Atmos" ,           MACHINE_SUPPORTS_SAVE )
COMP( 1985, prav8d,   oric1,  0,      prav8d,   prav8d,   oric_state,      empty_init, "Pravetz",   "Pravetz 8D",            MACHINE_SUPPORTS_SAVE )
COMP( 1989, prav8dd,  oric1,  0,      prav8d,   prav8d,   oric_state,      empty_init, "Pravetz",   "Pravetz 8D (Disk ROM)", MACHINE_UNOFFICIAL | MACHINE_SUPPORTS_SAVE )
COMP( 1986, telstrat, oric1,  0,      telstrat, telstrat, telestrat_state, empty_init, "Tangerine", "Oric Telestrat",        MACHINE_SUPPORTS_SAVE )



orion.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

Orion driver by Miodrag Milanovic

2008-04-02 Preliminary driver.
2008-04-22 Orion Pro added

Orionide and orionidm produce ERROR at start, not sure why. The others
appear to function well, as long as you use the romdisk software item.
You can load tapes with the CH4 program which is found on the romdisk.

Orionpro has its own menu. If you choose Orion Pro, you need to have the
cpm370 disk in the drive. If you choose Orion 128, you need the romdisk.

It's unknown how to create a tape.

****************************************************************************/


#include "emu.h"
#include "orion.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/smx_dsk.h"
#include "formats/rk_cas.h"


/* Address maps */

/* Orion 128 */
void orion_state::mem_map(address_map &map)
{
	map(0x0000, 0xefff).bankrw("bank1");
	map(0xf000, 0xf3ff).bankrw("bank2");
	map(0xf400, 0xf4ff).rw(FUNC(orion_state::orion128_system_r), FUNC(orion_state::orion128_system_w));  // Keyboard and cassette
	map(0xf500, 0xf5ff).rw(FUNC(orion_state::orion128_romdisk_r), FUNC(orion_state::orion128_romdisk_w));
	map(0xf700, 0xf7ff).rw(FUNC(orion_state::orion128_floppy_r), FUNC(orion_state::orion128_floppy_w));
	map(0xf800, 0xffff).rom();
	map(0xf800, 0xf8ff).w(FUNC(orion_state::orion128_video_mode_w));
	map(0xf900, 0xf9ff).w(FUNC(orion_state::orion128_memory_page_w));
	map(0xfa00, 0xfaff).w(FUNC(orion_state::orion128_video_page_w));
}

/* Orion Z80 Card II */
void orion_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0xf8, 0xf8).w(FUNC(orion_state::orion128_video_mode_w));
	map(0xf9, 0xf9).w(FUNC(orion_state::orion128_memory_page_w));
	map(0xfa, 0xfa).w(FUNC(orion_state::orion128_video_page_w));
}

void orion_z80_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).bankrw("bank1");
	map(0x4000, 0xefff).bankrw("bank2");
	map(0xf000, 0xf3ff).bankrw("bank3");
	map(0xf400, 0xf7ff).bankrw("bank4");
	map(0xf800, 0xffff).bankrw("bank5");
}

/* Orion Pro */
void orion_z80_state::io_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(orion_z80_state::orionz80_io_r), FUNC(orion_z80_state::orionz80_io_w));
}

void orion_pro_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).bankrw("bank1");
	map(0x2000, 0x3fff).bankrw("bank2");
	map(0x4000, 0x7fff).bankrw("bank3");
	map(0x8000, 0xbfff).bankrw("bank4");
	map(0xc000, 0xefff).bankrw("bank5");
	map(0xf000, 0xf3ff).bankrw("bank6");
	map(0xf400, 0xf7ff).bankrw("bank7");
	map(0xf800, 0xffff).bankrw("bank8");
}

void orion_pro_state::io_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(orion_pro_state::orionpro_io_r), FUNC(orion_pro_state::orionpro_io_w));
}

void orion_state::orion_floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_SMX_FORMAT);
}

/* Machine driver */
void orion_state::orion128(machine_config &config)
{
	I8080(config, m_maincpu, 2000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &orion_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &orion_state::io_map);

	I8255A(config, m_ppi1);
	m_ppi1->in_pa_callback().set(FUNC(orion_state::orion_romdisk_porta_r));
	m_ppi1->out_pb_callback().set(FUNC(orion_state::orion_romdisk_portb_w));
	m_ppi1->out_pc_callback().set(FUNC(orion_state::orion_romdisk_portc_w));

	I8255A(config, m_ppi2);
	m_ppi2->out_pa_callback().set(FUNC(orion_state::radio86_8255_porta_w2));
	m_ppi2->in_pb_callback().set(FUNC(orion_state::radio86_8255_portb_r2));
	m_ppi2->in_pc_callback().set(FUNC(orion_state::radio86_8255_portc_r2));
	m_ppi2->out_pc_callback().set(FUNC(orion_state::radio86_8255_portc_w2));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_size(384, 256);
	m_screen->set_visarea(0, 384-1, 0, 256-1);
	m_screen->set_screen_update(FUNC(orion_state::screen_update_orion128));
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(orion_state::orion128_palette), 18);

	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(rko_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("orion_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("orion_cass");

	FD1793(config, m_fdc, 8_MHz_XTAL / 8);

	FLOPPY_CONNECTOR(config, "fd0", "525qd", FLOPPY_525_QD, true, orion_floppy_formats);
	FLOPPY_CONNECTOR(config, "fd1", "525qd", FLOPPY_525_QD, true, orion_floppy_formats);
	FLOPPY_CONNECTOR(config, "fd2", "525qd", FLOPPY_525_QD, true, orion_floppy_formats);
	FLOPPY_CONNECTOR(config, "fd3", "525qd", FLOPPY_525_QD, true, orion_floppy_formats);
	SOFTWARE_LIST(config, "flop_list").set_original("orion_flop");

	auto &cart(GENERIC_CARTSLOT(config, "cartslot"));
	generic_plain_slot(cart);
	cart.set_interface("orion_cart");

	SOFTWARE_LIST(config, "cart_list").set_original("orion_cart");

	/* internal ram */
	RAM(config, m_ram);
	m_ram->set_default_size("256K");
	m_ram->set_default_value(0x00);
}

void orion_state::orion128ms(machine_config &config)
{
	orion128(config);
	I8255A(config.replace(), m_ppi2);
	m_ppi2->out_pa_callback().set(FUNC(orion_state::radio86_8255_porta_w2));
	m_ppi2->in_pb_callback().set(FUNC(orion_state::radio86_8255_portb_r2));
	m_ppi2->in_pc_callback().set(FUNC(orion_state::rk7007_8255_portc_r));
	m_ppi2->out_pc_callback().set(FUNC(orion_state::radio86_8255_portc_w2));
}


void orion_z80_state::orionz80(machine_config &config)
{
	Z80(config, m_maincpu, 2500000);
	m_maincpu->set_addrmap(AS_PROGRAM, &orion_z80_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &orion_z80_state::io_map);
	m_maincpu->set_vblank_int("screen", FUNC(orion_z80_state::orionz80_interrupt));

	I8255A(config, m_ppi1);
	m_ppi1->in_pa_callback().set(FUNC(orion_z80_state::orion_romdisk_porta_r));
	m_ppi1->out_pb_callback().set(FUNC(orion_z80_state::orion_romdisk_portb_w));
	m_ppi1->out_pc_callback().set(FUNC(orion_z80_state::orion_romdisk_portc_w));

	I8255A(config, m_ppi2);
	m_ppi2->out_pa_callback().set(FUNC(orion_z80_state::radio86_8255_porta_w2));
	m_ppi2->in_pb_callback().set(FUNC(orion_z80_state::radio86_8255_portb_r2));
	m_ppi2->in_pc_callback().set(FUNC(orion_z80_state::radio86_8255_portc_r2));
	m_ppi2->out_pc_callback().set(FUNC(orion_z80_state::radio86_8255_portc_w2));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_size(384, 256);
	m_screen->set_visarea(0, 384-1, 0, 256-1);
	m_screen->set_screen_update(FUNC(orion_z80_state::screen_update_orion128));
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(orion_z80_state::orion128_palette), 18);

	MC146818(config, "rtc", 4.194304_MHz_XTAL);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 1.0);

	auto &ay8912(AY8912(config, "ay8912", 1773400));
	ay8912.add_route(ALL_OUTPUTS, "mono", 1.00);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(rko_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("orion_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("orion_cass");

	FD1793(config, m_fdc, 8_MHz_XTAL / 8);

	FLOPPY_CONNECTOR(config, "fd0", "525qd", FLOPPY_525_QD, true, orion_floppy_formats);
	FLOPPY_CONNECTOR(config, "fd1", "525qd", FLOPPY_525_QD, true, orion_floppy_formats);
	FLOPPY_CONNECTOR(config, "fd2", "525qd", FLOPPY_525_QD, true, orion_floppy_formats);
	FLOPPY_CONNECTOR(config, "fd3", "525qd", FLOPPY_525_QD, true, orion_floppy_formats);
	SOFTWARE_LIST(config, "flop_list").set_original("orion_flop");

	auto &cart(GENERIC_CARTSLOT(config, "cartslot"));
	generic_plain_slot(cart);
	cart.set_interface("orion_cart");

	SOFTWARE_LIST(config, "cart_list").set_original("orion_cart");

	/* internal ram */
	RAM(config, m_ram);
	m_ram->set_default_size("512K");
	m_ram->set_default_value(0x00);
}

void orion_z80_state::orionz80ms(machine_config &config)
{
	orionz80(config);

	I8255A(config.replace(), m_ppi2);
	m_ppi2->out_pa_callback().set(FUNC(orion_z80_state::radio86_8255_porta_w2));
	m_ppi2->in_pb_callback().set(FUNC(orion_z80_state::radio86_8255_portb_r2));
	m_ppi2->in_pc_callback().set(FUNC(orion_z80_state::rk7007_8255_portc_r));
	m_ppi2->out_pc_callback().set(FUNC(orion_z80_state::radio86_8255_portc_w2));
}

void orion_pro_state::orionpro(machine_config &config)
{
	Z80(config, m_maincpu, 5000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &orion_pro_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &orion_pro_state::io_map);

	I8255A(config, m_ppi1);
	m_ppi1->in_pa_callback().set(FUNC(orion_pro_state::orion_romdisk_porta_r));
	m_ppi1->out_pb_callback().set(FUNC(orion_pro_state::orion_romdisk_portb_w));
	m_ppi1->out_pc_callback().set(FUNC(orion_pro_state::orion_romdisk_portc_w));

	I8255A(config, m_ppi2);
	m_ppi2->out_pa_callback().set(FUNC(orion_pro_state::radio86_8255_porta_w2));
	m_ppi2->in_pb_callback().set(FUNC(orion_pro_state::radio86_8255_portb_r2));
	m_ppi2->in_pc_callback().set(FUNC(orion_pro_state::radio86_8255_portc_r2));
	m_ppi2->out_pc_callback().set(FUNC(orion_pro_state::radio86_8255_portc_w2));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_size(384, 256);
	m_screen->set_visarea(0, 384-1, 0, 256-1);
	m_screen->set_screen_update(FUNC(orion_pro_state::screen_update_orion128));
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(orion_pro_state::orion128_palette), 18);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 1.0);
	auto &ay8912(AY8912(config, "ay8912", 1773400));
	ay8912.add_route(ALL_OUTPUTS, "mono", 1.00);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(rko_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("orion_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("orion_cass");

	FD1793(config, m_fdc, 8_MHz_XTAL / 8);

	FLOPPY_CONNECTOR(config, "fd0", "525qd", FLOPPY_525_QD, true, orion_floppy_formats);
	FLOPPY_CONNECTOR(config, "fd1", "525qd", FLOPPY_525_QD, true, orion_floppy_formats);
	FLOPPY_CONNECTOR(config, "fd2", "525qd", FLOPPY_525_QD, true, orion_floppy_formats);
	FLOPPY_CONNECTOR(config, "fd3", "525qd", FLOPPY_525_QD, true, orion_floppy_formats);
	SOFTWARE_LIST(config, "flop_list").set_original("orionpro_flop");
	SOFTWARE_LIST(config, "flop128_list").set_compatible("orion_flop");

	auto &cart(GENERIC_CARTSLOT(config, "cartslot"));
	generic_plain_slot(cart);
	cart.set_interface("orion_cart");

	SOFTWARE_LIST(config, "cart_list").set_original("orion_cart");

	/* internal ram */
	RAM(config, m_ram);
	m_ram->set_default_size("512K");
	m_ram->set_default_value(0x00);
}

/* ROM definition */

ROM_START( orion128 )
	ROM_REGION( 0x30000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "m2rk", "Version 3.2 rk" )
	ROMX_LOAD( "m2rk.bin",    0x0f800, 0x0800, CRC(2025c234) SHA1(caf86918629be951fe698cddcdf4589f07e2fb96), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "m2_2rk", "Version 3.2.2 rk" )
	ROMX_LOAD( "m2_2rk.bin",  0x0f800, 0x0800, CRC(fc662351) SHA1(7c6de67127fae5869281449de1c503597c0c058e), ROM_BIOS(1) )
ROM_END

ROM_START( orionms )
	ROM_REGION( 0x30000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "ms7007.bin",   0x0f800, 0x0800, CRC(c6174ba3) SHA1(8f9a42c3e09684718fe4121a8408e7860129d26f) )
ROM_END

ROM_START( orionz80 )
	ROM_REGION( 0x30000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "m31", "Version 3.1" )
	ROMX_LOAD( "m31.bin",     0x0f800, 0x0800, CRC(007c6dc6) SHA1(338ff95497c820338f7f79c75f65bc540a5541c4), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "m32zrk", "Version 3.2 zrk" )
	ROMX_LOAD( "m32zrk.bin",  0x0f800, 0x0800, CRC(4ec3f012) SHA1(6b0b2bfc515a80e7caf72c3c33cf2dcf192d4711), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "m33zrkd", "Version 3.3 zrkd" )
	ROMX_LOAD( "m33zrkd.bin", 0x0f800, 0x0800, CRC(f404032d) SHA1(088cd9ed05f0dda4fa0a005c609208d9f57ad3d9), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "m34zrk", "Version 3.4 zrk" )
	ROMX_LOAD( "m34zrk.bin",  0x0f800, 0x0800, CRC(787c3903) SHA1(476c1c0b88e5efb582292eebec15e24d054c8851), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "m35zrkd", "Version 3.5 zrkd" )
	ROMX_LOAD( "m35zrkd.bin", 0x0f800, 0x0800, CRC(9368b38f) SHA1(64a77f22119d40c9b18b64d78ad12acc6fff9efb), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "peter", "Peterburg '91" )
	ROMX_LOAD( "peter.bin",   0x0f800, 0x0800, CRC(df9b1d8c) SHA1(c7f1e074e58ad1c1799cf522161b4f4cffa5aefa), ROM_BIOS(5) )
ROM_END

ROM_START( orionide )
	ROM_REGION( 0x30000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "m35zrkh.bin", 0x0f800, 0x0800, CRC(b7745f28) SHA1(c3bd3e662db7ec56ecbab54bf6b3a4c26200d0bb) )
ROM_END

ROM_START( orionzms )
	ROM_REGION( 0x30000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "m32zms", "Version 3.2 zms" )
	ROMX_LOAD( "m32zms.bin",  0x0f800, 0x0800, CRC(44cfd2ae) SHA1(84d53fbc249938c56be76ee4e6ab297f0461835b), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "m34zms", "Version 3.4 zms" )
	ROMX_LOAD( "m34zms.bin",  0x0f800, 0x0800, CRC(0f87a80b) SHA1(ab1121092e61268d8162ed8a7d4fd081016a409a), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "m35zmsd", "Version 3.5 zmsd" )
	ROMX_LOAD( "m35zmsd.bin", 0x0f800, 0x0800, CRC(f714ff37) SHA1(fbe9514adb3384aff146cbedd4fede37ce9591e1), ROM_BIOS(2) )
ROM_END

ROM_START( orionidm )
	ROM_REGION( 0x30000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "m35zmsh.bin", 0x0f800, 0x0800, CRC(01e66df4) SHA1(8c785a3c32fe3eacda73ec79157b41a6e4b63ba8) )
ROM_END

ROM_START( orionpro )
	ROM_REGION( 0x32000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "ver21", "Version 2.1" )
	ROMX_LOAD( "rom1-210.bin", 0x20000, 0x2000,  CRC(8e1a0c78) SHA1(61c8a5ed596ce7e3fd32da920dcc80dc5375b421), ROM_BIOS(0) )
	ROMX_LOAD( "rom2-210.bin", 0x22000, 0x10000, CRC(7cb7a49b) SHA1(601f3dd61db323407c4874fd7f23c10dccac0209), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "ver20", "Version 2.0" )
	ROMX_LOAD( "rom1-200.bin", 0x20000, 0x2000,  CRC(4fbe83cc) SHA1(9884d43770b4c0fbeb519b96618b01957c0b8511), ROM_BIOS(1) )
	ROMX_LOAD( "rom2-200.bin", 0x22000, 0x10000, CRC(618aaeb7) SHA1(3e7e5d3ff9d2c683708928558e69aa62db877811), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "ver10", "Version 1.0" )
	ROMX_LOAD( "rom1-100.bin", 0x20000, 0x2000, CRC(4fd6c408) SHA1(b0c2e4fb5be5a74a7efa9bba14b746865122af1d), ROM_BIOS(2) )
	ROMX_LOAD( "rom2-100.bin", 0x22000, 0x8000, CRC(370ffdca) SHA1(169e2acac2d0b382e2d0a144da0af18bfa38db5c), ROM_BIOS(2) )
ROM_END

/* Driver */

//    YEAR  NAME      PARENT    COMPAT  MACHINE     INPUT    CLASS            INIT        COMPANY      FULLNAME                                  FLAGS
COMP( 1990, orion128, 0,        0,      orion128,   radio86, orion_state,     empty_init, "<unknown>", "Orion 128",                              MACHINE_SUPPORTS_SAVE )
COMP( 1990, orionms,  orion128, 0,      orion128ms, ms7007,  orion_state,     empty_init, "<unknown>", "Orion 128 (MS7007)",                     MACHINE_SUPPORTS_SAVE )
COMP( 1990, orionz80, orion128, 0,      orionz80,   radio86, orion_z80_state, empty_init, "<unknown>", "Orion 128 + Z80 Card II",                MACHINE_SUPPORTS_SAVE )
COMP( 1990, orionide, orion128, 0,      orionz80,   radio86, orion_z80_state, empty_init, "<unknown>", "Orion 128 + Z80 Card II + IDE",          MACHINE_SUPPORTS_SAVE )
COMP( 1990, orionzms, orion128, 0,      orionz80ms, ms7007,  orion_z80_state, empty_init, "<unknown>", "Orion 128 + Z80 Card II (MS7007)",       MACHINE_SUPPORTS_SAVE )
COMP( 1990, orionidm, orion128, 0,      orionz80ms, ms7007,  orion_z80_state, empty_init, "<unknown>", "Orion 128 + Z80 Card II + IDE (MS7007)", MACHINE_SUPPORTS_SAVE )
COMP( 1994, orionpro, orion128, 0,      orionpro,   radio86, orion_pro_state, empty_init, "<unknown>", "Orion Pro",                              MACHINE_SUPPORTS_SAVE )



osbexec.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/***************************************************************************

    Osborne Executive driver file

***************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/z80daisy.h"
#include "machine/6821pia.h"
#include "machine/input_merger.h"
#include "machine/pit8253.h"
#include "machine/ram.h"
#include "machine/ripple_counter.h"
#include "machine/wd_fdc.h"
#include "machine/z80sio.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

#define MAIN_CLOCK  XTAL(23'961'600)

#define MODEM_PORT_TAG "modem"
#define PRINTER_PORT_TAG "printer"


class osbexec_state : public driver_device
{
public:
	osbexec_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_mb8877(*this, "mb8877")
		, m_messram( *this, RAM_TAG)
		, m_pia(*this, "pia_%u", 0U)
		, m_rtc(*this, "rtc")
		, m_sio(*this, "sio")
		, m_speaker(*this, "speaker")
		, m_floppy(*this, "mb8877:%u:525ssdd", 0U)
		, m_kbd_row(*this, "ROW%u", 0U)
	{ }

	void osbexec(machine_config &config);

	void init_osbexec();

private:
	required_device<z80_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<mb8877_device>  m_mb8877;
	required_device<ram_device> m_messram;
	required_device_array<pia6821_device, 2> m_pia;
	required_device<ripple_counter_device> m_rtc;
	required_device<z80sio_device> m_sio;
	required_device<speaker_sound_device>   m_speaker;
	required_device_array<floppy_image_device, 2> m_floppy;
	required_ioport_array<8> m_kbd_row;

	virtual void video_start() override ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	bitmap_ind16 m_bitmap;

	std::unique_ptr<uint8_t[]> m_fontram;
	std::unique_ptr<uint8_t[]> m_vram;
	uint8_t   *m_ram_0000 = nullptr;
	uint8_t   *m_ram_c000 = nullptr;
	uint8_t   m_temp_attr = 0;
	emu_timer *m_video_timer = nullptr;

	/* PIA 0 (UD12) */
	uint8_t   m_pia0_porta = 0;
	uint8_t   m_pia0_portb = 0;
	int     m_pia0_cb2 = 0;         /* 60/50 */

	/* PIA 1 (UD8) */

	void set_banks()
	{
		uint8_t *ram_ptr = m_messram->pointer();

		m_ram_0000 = ram_ptr;

		if ( m_pia0_porta & 0x01 )
			m_ram_0000 += 0x10000;

		membank( "0000" )->set_base( m_ram_0000 + 0x0000 );
		membank( "2000" )->set_base( m_ram_0000 + 0x2000 );
		membank( "4000" )->set_base( m_ram_0000 + 0x4000 );
		m_ram_c000 = m_ram_0000 + 0xc000;
		membank( "e000" )->set_base( m_ram_0000 + 0xe000 );

		if ( m_pia0_porta & 0x80 )
		{
			membank( "0000" )->set_base( memregion("maincpu")->base());
			/* When BIOS is enabled 2000-3FFF is set to the "ROM RAM" */
			membank( "2000" )->set_base( ram_ptr + 0x20000 );
		}

		if ( m_pia0_porta & 0x40 )
			m_ram_c000 = m_vram.get();
	}

	void osbexec_0000_w(offs_t offset, uint8_t data);
	uint8_t osbexec_c000_r(offs_t offset);
	void osbexec_c000_w(offs_t offset, uint8_t data);
	uint8_t osbexec_kbd_r(offs_t offset);
	uint8_t osbexec_rtc_r();
	virtual void machine_reset() override ATTR_COLD;
	TIMER_CALLBACK_MEMBER(osbexec_video_callback);
	uint8_t osbexec_pia0_a_r();
	void osbexec_pia0_a_w(uint8_t data);
	uint8_t osbexec_pia0_b_r();
	void osbexec_pia0_b_w(uint8_t data);
	void osbexec_pia0_ca2_w(int state);
	void osbexec_pia0_cb2_w(int state);
	void modem_txclk_w(int state);
	void modem_rxclk_w(int state);
	void modem_dsr_w(int state);
	void modem_ri_w(int state);
	void comm_clk_a_w(int state);
	void osbexec_io(address_map &map) ATTR_COLD;
	void osbexec_mem(address_map &map) ATTR_COLD;
};


void osbexec_state::osbexec_0000_w(offs_t offset, uint8_t data)
{
	/* Font RAM writing is enabled when ROM bank is enabled */
	if ( m_pia0_porta & 0x80 )
	{
		if ( offset < 0x1000 )
			m_fontram[ offset ] = data;
	}
	else
	{
		m_ram_0000[ offset ] = data;
	}
}


uint8_t osbexec_state::osbexec_c000_r(offs_t offset)
{
	uint8_t   data = m_ram_c000[offset];

	if ( ( m_pia0_porta & 0x40 ) && offset < 0x1000 )
	{
		m_temp_attr = m_ram_c000[ 0x1000 + offset ];
	}

	return data;
}


void osbexec_state::osbexec_c000_w(offs_t offset, uint8_t data)
{
	m_ram_c000[offset] = data;

	if ( ( m_pia0_porta & 0x40 ) && offset < 0x1000 )
	{
		m_ram_c000[ 0x1000 + offset ] = m_temp_attr;
	}
}


uint8_t osbexec_state::osbexec_kbd_r(offs_t offset)
{
	uint8_t data = 0xFF;

	for (int j = 0; j < 8; j++)
		if (BIT(offset, j + 8))
			data &= m_kbd_row[j]->read();

	return data;
}


uint8_t osbexec_state::osbexec_rtc_r()
{
	// 74LS244 buffer @ UF13
	return m_rtc->count();
}


void osbexec_state::osbexec_mem(address_map &map)
{
	map(0x0000, 0x1FFF).bankr("0000").w(FUNC(osbexec_state::osbexec_0000_w));   /* ROM and maybe also banked ram */
	map(0x2000, 0x3FFF).bankrw("2000");                               /* Banked RAM */
	map(0x4000, 0xBFFF).bankrw("4000");                               /* Banked RAM */
	map(0xC000, 0xDFFF).rw(FUNC(osbexec_state::osbexec_c000_r), FUNC(osbexec_state::osbexec_c000_w));    /* Video ram / Banked RAM */
	map(0xE000, 0xEFFF).bankrw("e000");                               /* Banked RAM */
	map(0xF000, 0xFFFF).ram();                                           /* 4KB of non-banked RAM for system stack etc */
}


void osbexec_state::osbexec_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x03).mirror(0xff00).rw(m_pia[0], FUNC(pia6821_device::read), FUNC(pia6821_device::write));       /* 6821 PIA @ UD12 */
	map(0x04, 0x07).mirror(0xff00).rw("ctc", FUNC(pit8253_device::read), FUNC(pit8253_device::write));          /* 8253 @UD1 */
	map(0x08, 0x0B).mirror(0xff00).rw(m_mb8877, FUNC(wd_fdc_device_base::read), FUNC(wd_fdc_device_base::write));  /* MB8877 @ UB17 input clock = 1MHz */
	map(0x0C, 0x0F).mirror(0xff00).rw(m_sio, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));    /* SIO @ UD4 */
	map(0x10, 0x13).mirror(0xff00).rw(m_pia[1], FUNC(pia6821_device::read), FUNC(pia6821_device::write));       /* 6821 PIA @ UD8 */
	map(0x14, 0x17).select(0xff00).r(FUNC(osbexec_state::osbexec_kbd_r));                                      /* KBD */
	map(0x18, 0x1b).mirror(0xff00).r(FUNC(osbexec_state::osbexec_rtc_r));                                      /* "RTC" @ UE13/UF13 */
	/* ?? - vid ? */
}


static INPUT_PORTS_START( osbexec )
	PORT_START("ROW0")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2)   PORT_CHAR('[') PORT_CHAR(']')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT)       PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)     PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)          PORT_CHAR('\t')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)          PORT_CHAR(UCHAR_MAMEKEY(ESC))

	PORT_START("ROW1")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)            PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)            PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)            PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)            PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)            PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)            PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)            PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)            PORT_CHAR('1') PORT_CHAR('!')

	PORT_START("ROW2")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)            PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)            PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(21)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)            PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR(25)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)            PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(20)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)            PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR(18)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)            PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(5)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)            PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR(23)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)            PORT_CHAR('q') PORT_CHAR('Q') PORT_CHAR(17)

	PORT_START("ROW3")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)            PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR(11)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)            PORT_CHAR('j') PORT_CHAR('J') PORT_CHAR(10)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)            PORT_CHAR('h') PORT_CHAR('H') PORT_CHAR(8)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)            PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR(7)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)            PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR(6)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)            PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR(4)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)            PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR(19)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)            PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR(1)

	PORT_START("ROW4")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)        PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)            PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)            PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(14)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)            PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR(2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)            PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR(22)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)            PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR(3)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)            PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(24)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)            PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(26)

	PORT_START("ROW5")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)            PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)            PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(15)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)            PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR(16)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)         PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)        PORT_CHAR(' ')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)            PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)         PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)           PORT_CHAR(UCHAR_MAMEKEY(UP))

	PORT_START("ROW6")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)       PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)            PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR(12)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)    PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)        PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)        PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)        PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)         PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)        PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

	PORT_START("ROW7")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_DIPNAME( 0x08, 0, "Alpha Lock" ) PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END


void osbexec_state::video_start()
{
	m_screen->register_screen_bitmap(m_bitmap);
}

uint32_t osbexec_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
	return 0;
}

/*
  UD12 - 6821 PIA

  Port A:
  PA7 - ROM BANK ENA
  PA6 - VRAM BANK ENA
  PA5 - BANK6ENA
  PA4 - BANK5ENA
  PA3 - BANK4ENA
  PA2 - BANK3ENA
  PA1 - BANK2ENA
  PA0 - BANK1ENA
  CA1 - DMA IRQ
  CA2 - KBD STB (i/o)

  Port B:
  PB7 - MODEM RI (input)
  PB6 - MODEM DSR (input)
  PB5 - TXC SEL
  PB4 - RXC SEL
  PB3 - speaker
  PB2 - DSEL2
  PB1 - DSEL1
  PB0 - DDEN
  CB1 - VBlank (input)
  CB2 - 60/50 (?)
*/

uint8_t osbexec_state::osbexec_pia0_a_r()
{
	return m_pia0_porta;
}


void osbexec_state::osbexec_pia0_a_w(uint8_t data)
{
	//logerror("osbexec_pia0_a_w: %02x\n", data );

	m_pia0_porta = data;

	set_banks();
}


uint8_t osbexec_state::osbexec_pia0_b_r()
{
	return m_pia0_portb;
}


void osbexec_state::osbexec_pia0_b_w(uint8_t data)
{
	m_pia0_portb = (m_pia0_portb & 0xc0) | (data & 0x3f);

	m_speaker->level_w(!BIT(data, 3));

	switch ( data & 0x06 )
	{
	case 0x02:
		m_mb8877->set_floppy(m_floppy[1].target());
		m_floppy[1]->mon_w(0);
		break;
	case 0x04:
		m_mb8877->set_floppy(m_floppy[0].target());
		m_floppy[0]->mon_w(0);
		break;
	default:
		m_mb8877->set_floppy(nullptr);
		break;
	}

	m_mb8877->dden_w(( data & 0x01 ) ? 1 : 0 );
}


void osbexec_state::osbexec_pia0_ca2_w(int state)
{
	logerror("osbexec_pia0_ca2_w: state = %d\n", state);
}


void osbexec_state::osbexec_pia0_cb2_w(int state)
{
	m_pia0_cb2 = state;
}


void osbexec_state::modem_txclk_w(int state)
{
	if (BIT(m_pia0_portb, 5))
		m_sio->txca_w(state);
}


void osbexec_state::modem_rxclk_w(int state)
{
	if (BIT(m_pia0_portb, 4))
		m_sio->rxca_w(state);
}


void osbexec_state::modem_dsr_w(int state)
{
	if (state)
		m_pia0_portb |= 0x40;
	else
		m_pia0_portb &= 0xbf;
}


void osbexec_state::modem_ri_w(int state)
{
	if (state)
		m_pia0_portb |= 0x80;
	else
		m_pia0_portb &= 0x7f;
}


void osbexec_state::comm_clk_a_w(int state)
{
	if (!BIT(m_pia0_portb, 5))
		m_sio->txca_w(state);
	if (!BIT(m_pia0_portb, 4))
		m_sio->rxca_w(state);
}


/*
 * The Osborne Executive supports the following disc formats: (TODO: Verify)
 * - Osborne single density: 40 tracks, 10 sectors per track, 256-byte sectors (100 KByte)
 * - Osborne double density: 40 tracks, 5 sectors per track, 1024-byte sectors (200 KByte)
 * - IBM Personal Computer: 40 tracks, 8 sectors per track, 512-byte sectors (160 KByte)
 * - Xerox 820 Computer: 40 tracks, 18 sectors per track, 128-byte sectors (90 KByte)
 * - DEC 1820 double density: 40 tracks, 9 sectors per track, 512-byte sectors (180 KByte)
 *
 */

static void osborne2_floppies(device_slot_interface &device)
{
	device.option_add("525ssdd", FLOPPY_525_SSDD);
}


TIMER_CALLBACK_MEMBER(osbexec_state::osbexec_video_callback)
{
	int y = m_screen->vpos();

	if ( y < 240 )
	{
		uint16_t row_addr = ( y / 10 ) * 128;
		uint16_t *const p = &m_bitmap.pix(y);
		uint8_t char_line = y % 10;

		for ( int x = 0; x < 80; x++ )
		{
			uint8_t ch = m_vram[ row_addr + x ];
			uint8_t attr = m_vram[ 0x1000 + row_addr + x ];
			uint8_t fg_col = ( attr & 0x80 ) ? 2 : 1;
			uint8_t font_bits = m_fontram[ ( ( attr & 0x10 ) ? 0x800 : 0 ) + ( ch & 0x7f ) * 16 + char_line ];

			/* Check for underline */
			if (BIT(attr, 6) && char_line == 9)
				font_bits = 0xFF;

			/* Check for blink */
			if (BIT(attr, 5) && BIT(m_rtc->count(), 4))
				font_bits = 0;

			/* Check for inverse video */
			if (BIT(ch, 7) && !BIT(attr, 4))
				font_bits ^= 0xFF;

			for ( int b = 0; b < 8; b++ )
			{
				p[ x * 8 + b ] = ( font_bits & 0x80 ) ? fg_col : 0;
				font_bits <<= 1;
			}
		}
	}

	m_video_timer->adjust( m_screen->time_until_pos( y + 1, 0 ) );
}


void osbexec_state::init_osbexec()
{
	m_vram = make_unique_clear<uint8_t[]>(0x2000);
	m_fontram = make_unique_clear<uint8_t[]>(0x1000);

	m_video_timer = timer_alloc(FUNC(osbexec_state::osbexec_video_callback), this);
}


void osbexec_state::machine_reset()
{
	m_pia0_porta = 0xC0;        /* Enable ROM and VRAM on reset */

	set_banks();

	m_video_timer->adjust( m_screen->time_until_pos( 0, 0 ) );

	// D0 cleared on interrupt acknowledge cycle by TTL gates at UC21 and UA18
	m_maincpu->set_input_line_vector(0, 0xfe); // Z80
}


static const z80_daisy_config osbexec_daisy_config[] =
{
	{ "sio" },
	{ nullptr }
};


void osbexec_state::osbexec(machine_config &config)
{
	Z80(config, m_maincpu, MAIN_CLOCK/6);
	m_maincpu->set_addrmap(AS_PROGRAM, &osbexec_state::osbexec_mem);
	m_maincpu->set_addrmap(AS_IO, &osbexec_state::osbexec_io);
	m_maincpu->set_daisy_config(osbexec_daisy_config);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_color(rgb_t::green());
	m_screen->set_screen_update(FUNC(osbexec_state::screen_update));
	m_screen->set_raw(MAIN_CLOCK/2, 768, 0, 640, 260, 0, 240);    // May not be correct
	m_screen->set_palette("palette");
	m_screen->screen_vblank().set(m_pia[0], FUNC(pia6821_device::cb1_w));
	m_screen->screen_vblank().append(m_rtc, FUNC(ripple_counter_device::clock_w)).invert();

	PALETTE(config, "palette", palette_device::MONOCHROME_HIGHLIGHT);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);

	PIA6821(config, m_pia[0]);
	m_pia[0]->readpa_handler().set(FUNC(osbexec_state::osbexec_pia0_a_r));
	m_pia[0]->readpb_handler().set(FUNC(osbexec_state::osbexec_pia0_b_r));
	m_pia[0]->writepa_handler().set(FUNC(osbexec_state::osbexec_pia0_a_w));
	m_pia[0]->writepb_handler().set(FUNC(osbexec_state::osbexec_pia0_b_w));
	m_pia[0]->ca2_handler().set(FUNC(osbexec_state::osbexec_pia0_ca2_w));
	m_pia[0]->cb2_handler().set(FUNC(osbexec_state::osbexec_pia0_cb2_w));
	m_pia[0]->irqa_handler().set("mainirq", FUNC(input_merger_device::in_w<0>));
	m_pia[0]->irqb_handler().set("mainirq", FUNC(input_merger_device::in_w<1>));

	PIA6821(config, m_pia[1]);
	m_pia[1]->irqa_handler().set("mainirq", FUNC(input_merger_device::in_w<2>));
	m_pia[1]->irqb_handler().set("mainirq", FUNC(input_merger_device::in_w<3>));

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, 0);

	RIPPLE_COUNTER(config, m_rtc); // 74LS393 @ UE13
	m_rtc->set_stages(8); // two halves cascaded

	Z80SIO(config, m_sio, MAIN_CLOCK/6);
	m_sio->out_txda_callback().set(MODEM_PORT_TAG, FUNC(rs232_port_device::write_txd));
	m_sio->out_dtra_callback().set(MODEM_PORT_TAG, FUNC(rs232_port_device::write_dtr));
	m_sio->out_rtsa_callback().set(MODEM_PORT_TAG, FUNC(rs232_port_device::write_rts));
	m_sio->out_txdb_callback().set(PRINTER_PORT_TAG, FUNC(rs232_port_device::write_txd));
	m_sio->out_dtrb_callback().set(PRINTER_PORT_TAG, FUNC(rs232_port_device::write_dtr));
	m_sio->out_rtsb_callback().set(PRINTER_PORT_TAG, FUNC(rs232_port_device::write_rts));
	m_sio->out_int_callback().set("mainirq", FUNC(input_merger_device::in_w<4>));

	pit8253_device &ctc(PIT8253(config, "ctc", 0));
	ctc.set_clk<0>(MAIN_CLOCK / 13); // divided by 74S161 @ UC25
	ctc.set_clk<1>(MAIN_CLOCK / 13); // divided by 74S161 @ UC25
	ctc.set_clk<2>(MAIN_CLOCK / 12);
	ctc.out_handler<0>().set(FUNC(osbexec_state::comm_clk_a_w));
	ctc.out_handler<0>().append(MODEM_PORT_TAG, FUNC(rs232_port_device::write_etc));
	ctc.out_handler<1>().set(m_sio, FUNC(z80sio_device::rxtxcb_w));
	//ctc.out_handler<2>().set(FUNC(osbexec_state::spindle_clk_w));

	rs232_port_device &modem_port(RS232_PORT(config, MODEM_PORT_TAG, default_rs232_devices, nullptr));
	modem_port.rxd_handler().set(m_sio, FUNC(z80sio_device::rxa_w));
	modem_port.dcd_handler().set(m_sio, FUNC(z80sio_device::dcda_w));
	modem_port.dsr_handler().set(FUNC(osbexec_state::modem_dsr_w));
	modem_port.ri_handler().set(FUNC(osbexec_state::modem_ri_w));
	modem_port.cts_handler().set(m_sio, FUNC(z80sio_device::ctsa_w));
	modem_port.txc_handler().set(FUNC(osbexec_state::modem_txclk_w));
	modem_port.rxc_handler().set(FUNC(osbexec_state::modem_rxclk_w));

	rs232_port_device &printer_port(RS232_PORT(config, PRINTER_PORT_TAG, default_rs232_devices, nullptr));
	printer_port.rxd_handler().set(m_sio, FUNC(z80sio_device::rxb_w));
	printer_port.dcd_handler().set(m_sio, FUNC(z80sio_device::dcdb_w));
	printer_port.cts_handler().set(m_sio, FUNC(z80sio_device::ctsb_w));

	MB8877(config, m_mb8877, MAIN_CLOCK/24);
	m_mb8877->intrq_wr_callback().set(m_pia[1], FUNC(pia6821_device::cb1_w));
	FLOPPY_CONNECTOR(config, "mb8877:0", osborne2_floppies, "525ssdd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "mb8877:1", osborne2_floppies, "525ssdd", floppy_image_device::default_mfm_floppy_formats);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("136K"); /* 128KB Main RAM + RAM in ROM bank (8) */

	/* software lists */
	SOFTWARE_LIST(config, "flop_list").set_original("osborne2");
}


ROM_START( osbexec )
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD( "execv12.ud18", 0x0000, 0x2000, CRC(70798c2f) SHA1(2145a72da563bed1d6d455c77e48cc011a5f1153) )    /* Checksum C6B2 */
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT          COMPANY    FULLNAME     FLAGS
COMP( 1982, osbexec, 0,      0,      osbexec, osbexec, osbexec_state, init_osbexec, "Osborne", "Executive", MACHINE_NOT_WORKING )



osborne1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol, Vas Crabb
/***************************************************************************

    Osborne-1 driver file

The Osborne-1 memory is divided into 3 "banks".

Bank 1 simply consists of 64KB of RAM. The upper 4KB is used for the lower 8
bit of video RAM entries.

Bank 2 holds the BIOS ROM and I/O area. Only addresses 0000-3FFF are used
by bank 2 (4000-FFFF mirrors bank 1). Bank 2 is divided as follows:
3000-3FFF Nominally unused but acts as mirror of 2000-2FFF
2C00-2C03 Video PIA
2A00-2A01 Serial interface
2900-2903 488 PIA
2400-2400 SCREEN-PAC (if present)
2201-2280 Keyboard
2100-2103 Floppy
1000-1FFF Nominally unused but acts as read mirror of BIOS ROM
0000-0FFF BIOS ROM

The logic is actually quite sloppy, and will cause bus fighting under many
circumstances since it doesn't actually check all four bits, just that two
are in the desired state.

Bank 3 has the ninth bit needed to complete the full Video RAM. These bits
are stored at F000-FFFF. Only the highest bit is used.

On bootup bank 2 is active.

Banking is controlled by writes to I/O space.  Only two low address bits are
used, and the value on the data bus is completley ignored.
00 - Activate bank 2 (also triggered by CPU reset)
01 - Activate bank 1
02 - Set BIT 9 signal (map bank 3 into F000-FFFF)
03 - Clear BIT 9 signal (map bank 1/2 into F000-FFFF)

Selecting between bank 1 and bank 2 is also affected by M1 and IRQACK
conditions using a set of three flipflops.

There are three IRQ sources:
- IRQ0 = IRQ from the serial ACIA
- IRQ1 = IRQA from the video PIA
- IRQ2 = IRQA from the IEEE488 PIA

The serial speed configuration implements wiring changes recommended in the
Osborne 1 Technical Manual.  There's no way for software to read the
selected baud rates, so it will always call the low speed "300" and the high
speed "1200".  You as the user have to keep this in mind using the system.

Serial communications can be flaky when 600/2400 is selected.  This is not a
bug in MAME.  I've checked and double-checked the schematics to confirm it's
an original bug.  The division ratio from the master clock to the baud rates
in this mode is effectively 16*24*64 or 16*24*16 giving actual data rates of
650 baud or 2600 baud, about 8.3% too fast (16*26*64 and 16*26*16 would give
the correct rates).  MAME's bitbanger seems to be able to accept the ACIA
output at this rate, but the ACIA screws up when consuming data from MAME's
bitbanger.

Schematics specify a WD1793 floppy controller, but we're using the Fujitsu
equivalent MB8877 here.  Is it known that the original machines used one or
the other exclusively?  In any case MAME emulates them identically.

Installation of the SCREEN-PAC requires the CPU and character generator ROM
to be transplanted to the add-on board, and cables run to the sockets that
previously held these chips.  It contains additional RAM clocked at twice
the speed of the main system RAM.  Writes to video memory get sent to this
RAM as well as the main system RAM, so there are actually two live copies
of video RAM at all times.  The SCREEN-PAC supports switching between
normal and double horizontal resolution (52x24 or 104x24) at exactly 60Hz.

The Nuevo Video board also requires the CPU to be transplanted to it and has
a pair of RAMs holding a copy of video memory.  However it has its own
character generator ROM, so the mainboard's character generator ROM doesn't
need to be moved.  It doesn't behave like the SCREEN-PAC.  It uses a
Synertek SY6545-1 with its pixel clock derived from a 12.288MHz crystal
mapped at 0x04/0x05 in I/O space.  It runs at 640x240 (80x24) at just below
60Hz and doesn't allow resolution switching.  We don't know how contention
for video RAM is handled, or whether the CRTC can generate VBL interrupts.


TODO:

* Hook up the port direction control bits in the IEEE488 interface properly
  and test it with some emulated peripheral.  Also the BIOS can speak
  Centronics parallel over the same physical interface, so this should be
  tested, too.

* Complete emulation of the Nuevo Video board (interrupts, CRTC video RAM
  updates).  It would be nice to get a schematic for this.

***************************************************************************/

#include "emu.h"

#include "bus/ieee488/ieee488.h"
#include "bus/rs232/rs232.h"

#include "cpu/z80/z80.h"

#include "imagedev/floppy.h"

#include "machine/6821pia.h"
#include "machine/6850acia.h"
#include "machine/wd_fdc.h"

#include "sound/spkrdev.h"

#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "tilemap.h"

#include <algorithm>


namespace {

static constexpr XTAL MAIN_CLOCK = 15.9744_MHz_XTAL;


class osborne1_state : public driver_device
{
public:
	osborne1_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_rom_view(*this, "bank_0000"),
		m_main_ram(*this, "mainram"),
		m_attr_ram(*this, "attrram"),
		m_screen(*this, "screen"),
		m_maincpu(*this, "maincpu"),
		m_pia0(*this, "pia_0"),
		m_pia1(*this, "pia_1"),
		m_gfxdecode(*this, "gfxdecode"),
		m_speaker(*this, "speaker"),
		m_acia(*this, "acia"),
		m_fdc(*this, "mb8877"),
		m_ieee(*this, IEEE488_TAG),
		m_floppy(*this, "mb8877:%u", 0U),
		m_keyb_row(*this, { "ROW0", "ROW1", "ROW3", "ROW4", "ROW5", "ROW2", "ROW6", "ROW7" }),
		m_btn_reset(*this, "RESET"),
		m_cnf(*this, "CNF"),
		m_vram_view(*this, "bank_fxxx"),
		m_p_chargen(*this, "chargen"),
		m_video_timer(nullptr),
		m_tilemap(nullptr),
		m_acia_rxc_txc_timer(nullptr)
	{
	}

	void osborne1(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(reset_key);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

	void osborne1_base(machine_config &config);

	void osborne1_mem(address_map &map) ATTR_COLD;
	void osborne1_op(address_map &map) ATTR_COLD;
	void osborne1_io(address_map &map) ATTR_COLD;

	u8 bank2_peripherals_r(offs_t offset);
	void bank2_peripherals_w(offs_t offset, u8 data);
	void videoram_w(offs_t offset, u8 data);
	void attrram_w(offs_t offset, u8 data);
	u8 opcode_r(offs_t offset);
	void bankswitch_w(offs_t offset, u8 data);
	void irqack_w(int state);

	bool rom_mode() const { return 0 != m_rom_mode; }
	u8 scroll_x() const { return m_scroll_x; }

	template <int Width, unsigned Scale> void draw_rows(uint16_t col, bitmap_ind16 &bitmap, const rectangle &cliprect);

	memory_view                             m_rom_view;
	required_shared_ptr<u8>                 m_main_ram;
	required_shared_ptr<u8>                 m_attr_ram;
	required_device<screen_device>          m_screen;
	required_device<z80_device>             m_maincpu;
	required_device<pia6821_device>         m_pia0;
	required_device<pia6821_device>         m_pia1;

private:
	u8 ieee_pia_pb_r();
	void ieee_pia_pb_w(u8 data);
	void ieee_pia_irq_a_func(int state);

	void video_pia_port_a_w(u8 data);
	void video_pia_port_b_w(u8 data);
	void video_pia_out_cb2_dummy(int state);
	void video_pia_irq_a_func(int state);

	void serial_acia_irq_func(int state);

	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	TIMER_CALLBACK_MEMBER(video_callback);
	TIMER_CALLBACK_MEMBER(acia_rxc_txc_callback);

	TILE_GET_INFO_MEMBER(get_tile_info);

	bool set_rom_mode(u8 value);
	void update_irq();
	void update_acia_rxc_txc();

	required_device<gfxdecode_device>       m_gfxdecode;
	required_device<speaker_sound_device>   m_speaker;
	required_device<acia6850_device>        m_acia;
	required_device<mb8877_device>          m_fdc;
	required_device<ieee488_device>         m_ieee;
	required_device_array<floppy_connector, 2> m_floppy;

	// user inputs
	required_ioport_array<8>    m_keyb_row;
	required_ioport             m_btn_reset;

	// fake inputs for hardware configuration and things that need rewiring
	required_ioport             m_cnf;

	// pieces of memory
	memory_view                 m_vram_view;
	required_region_ptr<u8>     m_p_chargen;

	// configuration (reloaded on reset)
	u8              m_acia_rxc_txc_div;
	u8              m_acia_rxc_txc_p_low;
	u8              m_acia_rxc_txc_p_high;

	// bank switch control bits
	u8              m_ub4a_q;
	u8              m_ub6a_q;
	u8              m_rom_mode;

	// onboard video state
	u8              m_scroll_x;
	u8              m_scroll_y;
	u8              m_beep_state;
	emu_timer       *m_video_timer;
	tilemap_t       *m_tilemap;

	// serial state
	u8              m_acia_irq_state;
	u8              m_acia_rxc_txc_state;
	emu_timer       *m_acia_rxc_txc_timer;

	memory_access<16, 0, 0, ENDIANNESS_LITTLE>::cache m_mem_cache;
};


class osborne1sp_state : public osborne1_state
{
public:
	osborne1sp_state(const machine_config &mconfig, device_type type, const char *tag) :
		osborne1_state(mconfig, type, tag)
	{
	}

	void osborne1sp(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void osborne1sp_mem(address_map &map) ATTR_COLD;

	u8 bank2_peripherals_r(offs_t offset);
	void bank2_peripherals_w(offs_t offset, u8 data);

private:
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	// SCREEN-PAC registers
	u8              m_resolution = 0;
	u8              m_hc_left = 0;
};


class osborne1nv_state : public osborne1_state
{
public:
	osborne1nv_state(const machine_config &mconfig, device_type type, const char *tag) :
		osborne1_state(mconfig, type, tag),
		m_palette(*this, "palette"),
		m_p_nuevo(*this, "nuevo")
	{
	}

	void osborne1nv(machine_config &config);

private:
	void osborne1nv_io(address_map &map) ATTR_COLD;

	MC6845_UPDATE_ROW(crtc_update_row);
	MC6845_ON_UPDATE_ADDR_CHANGED(crtc_update_addr_changed);

	required_device<palette_device> m_palette;
	required_region_ptr<u8>         m_p_nuevo;
};


u8 osborne1_state::bank2_peripherals_r(offs_t offset)
{
	// Since each peripheral only checks two bits, many addresses will
	// result in multiple peripherals attempting to drive the bus.  This is
	// simulated by ANDing all the values together.
	u8 data = 0xff;
	if ((offset & 0x900) == 0x100) // Floppy
		data &= m_fdc->read(offset & 0x03);
	if ((offset & 0x900) == 0x900) // IEEE488 PIA
		data &= m_pia0->read(offset & 0x03);
	if ((offset & 0xa00) == 0x200) // Keyboard
	{
		for (unsigned b = 0; 8 > b; ++b)
		{
			if (BIT(offset, b))
				data &= m_keyb_row[b]->read();
		}
	}
	if ((offset & 0xa00) == 0xa00) // Serial
	{
		data &= m_acia->read(offset & 0x01);
	}
	if ((offset & 0xc00) == 0xc00) // Video PIA
		data &= m_pia1->read(offset & 0x03);
	return data;
}

void osborne1_state::bank2_peripherals_w(offs_t offset, u8 data)
{
	// Handle writes to the I/O area
	if ((offset & 0x900) == 0x100) // Floppy
		m_fdc->write(offset & 0x03, data);
	if ((offset & 0x900) == 0x900) // IEEE488 PIA
		m_pia0->write(offset & 0x03, data);
	if ((offset & 0xa00) == 0xa00) // Serial
		m_acia->write(offset & 0x01, data);
	if ((offset & 0xc00) == 0xc00) // Video PIA
		m_pia1->write(offset & 0x03, data);
}

u8 osborne1sp_state::bank2_peripherals_r(offs_t offset)
{
	u8 data = osborne1_state::bank2_peripherals_r(offset);

	if ((offset & 0xc00) == 0x400) // SCREEN-PAC
		data &= 0xfb;

	return data;
}

void osborne1sp_state::bank2_peripherals_w(offs_t offset, u8 data)
{
	osborne1_state::bank2_peripherals_w(offset, data);

	if ((offset & 0xc00) == 0x400) // SCREEN-PAC
	{
		m_resolution = BIT(data, 0);
		m_hc_left = BIT(~data, 1);
	}
}

void osborne1_state::videoram_w(offs_t offset, u8 data)
{
	m_main_ram[0xf000 | offset] = data;
	m_tilemap->mark_tile_dirty(offset);
}

void osborne1_state::attrram_w(offs_t offset, u8 data)
{
	// Attribute RAM is only one bit wide - low seven bits are discarded and read back high
	m_attr_ram[offset] = data | 0x7f;
}

u8 osborne1_state::opcode_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
	{
		// Update the flipflops that control bank selection and NMI
		u8 const new_ub6a_q = (m_btn_reset->read() & 0x80) ? 1 : 0;
		if (!rom_mode())
		{
			set_rom_mode(m_ub4a_q ? 0 : 1);
			m_ub4a_q = m_ub6a_q;
		}
		m_ub6a_q = new_ub6a_q;
		m_maincpu->set_input_line(INPUT_LINE_NMI, m_ub6a_q ? CLEAR_LINE : ASSERT_LINE);
	}

	// Now that's sorted out we can call the normal read handler
	return m_mem_cache.read_byte(offset);
}

void osborne1_state::bankswitch_w(offs_t offset, u8 data)
{
	switch (offset)
	{
	case 0x00:
		if (set_rom_mode(1))
			m_ub4a_q = m_ub6a_q;
		break;
	case 0x01:
		m_ub4a_q = 1;
		m_ub6a_q = 1;
		set_rom_mode(0);
		m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
		break;
	case 0x02:
		m_vram_view.select(0);
		break;
	case 0x03:
		m_vram_view.disable();
		break;
	}
}

void osborne1_state::irqack_w(int state)
{
	// Update the flipflops that control bank selection and NMI
	if (!rom_mode())
		set_rom_mode(m_ub4a_q ? 0 : 1);
	m_ub4a_q = 0;
	m_ub6a_q = (m_btn_reset->read() & 0x80) ? 1 : 0;
	m_maincpu->set_input_line(INPUT_LINE_NMI, m_ub6a_q ? CLEAR_LINE : ASSERT_LINE);
}


u8 osborne1_state::ieee_pia_pb_r()
{
	/*
	    bit     description

	    0
	    1
	    2
	    3       EOI
	    4
	    5       DAV
	    6       NDAC
	    7       NRFD
	*/
	u8 data = 0;

	data |= m_ieee->eoi_r() << 3;
	data |= m_ieee->dav_r() << 5;
	data |= m_ieee->ndac_r() << 6;
	data |= m_ieee->nrfd_r() << 7;

	return data;
}

void osborne1_state::ieee_pia_pb_w(u8 data)
{
	/*
	    bit     description

	    0       0 = DATAn as output, 1 = DATAn as input
	    1       0 = NDAC/NRFD as output, 1 = NDAC/NRFD as input; also gates SRQ
	    2       0 = EOI/DAV as output, 1 = EOI/DAV as input
	    3       EOI
	    4       ATN
	    5       DAV
	    6       NDAC
	    7       NRFD
	*/
	m_ieee->host_eoi_w(BIT(data, 3));
	m_ieee->host_atn_w(BIT(data, 4));
	m_ieee->host_dav_w(BIT(data, 5));
	m_ieee->host_ndac_w(BIT(data, 6));
	m_ieee->host_nrfd_w(BIT(data, 7));
}

void osborne1_state::ieee_pia_irq_a_func(int state)
{
	update_irq();
}


void osborne1_state::video_pia_port_a_w(u8 data)
{
	m_scroll_x = data >> 1;

	m_fdc->dden_w(BIT(data, 0));
}

void osborne1_state::video_pia_port_b_w(u8 data)
{
	m_speaker->level_w((BIT(data, 5) && m_beep_state) ? 1 : 0);

	if (BIT(data, 6))
	{
		m_fdc->set_floppy(m_floppy[0]->get_device());
		m_floppy[0]->get_device()->mon_w(0);
	}
	else if (BIT(data, 7))
	{
		m_fdc->set_floppy(m_floppy[1]->get_device());
		m_floppy[1]->get_device()->mon_w(0);
	}
	else
	{
		m_fdc->set_floppy(nullptr);
	}
}

void osborne1_state::video_pia_out_cb2_dummy(int state)
{
}

void osborne1_state::video_pia_irq_a_func(int state)
{
	update_irq();
}


void osborne1_state::serial_acia_irq_func(int state)
{
	m_acia_irq_state = state;
	update_irq();
}


INPUT_CHANGED_MEMBER( osborne1_state::reset_key )
{
	// This key affects NMI
	if (!m_ub6a_q)
		m_maincpu->set_input_line(INPUT_LINE_NMI, newval ? CLEAR_LINE : ASSERT_LINE);
}


void osborne1_state::machine_start()
{
	m_video_timer = timer_alloc(FUNC(osborne1_state::video_callback), this);
	m_tilemap = &machine().tilemap().create(
			*m_gfxdecode,
			tilemap_get_info_delegate(*this, FUNC(osborne1_state::get_tile_info)), TILEMAP_SCAN_ROWS,
			8, 10, 128, 32);

	m_acia_rxc_txc_timer = timer_alloc(FUNC(osborne1_state::acia_rxc_txc_callback), this);

	m_maincpu->space(AS_PROGRAM).cache(m_mem_cache);

	save_item(NAME(m_acia_rxc_txc_div));
	save_item(NAME(m_acia_rxc_txc_p_low));
	save_item(NAME(m_acia_rxc_txc_p_high));

	save_item(NAME(m_ub4a_q));
	save_item(NAME(m_ub6a_q));
	save_item(NAME(m_rom_mode));

	save_item(NAME(m_scroll_x));
	save_item(NAME(m_scroll_y));
	save_item(NAME(m_beep_state));

	save_item(NAME(m_acia_irq_state));
	save_item(NAME(m_acia_rxc_txc_state));
}

void osborne1_state::machine_reset()
{
	// Refresh configuration
	switch (m_cnf->read() & 0x03)
	{
	case 0x00:
		m_acia_rxc_txc_div      = 16;
		m_acia_rxc_txc_p_low    = 23;
		m_acia_rxc_txc_p_high   = 29;
		break;
	case 0x01:
		m_acia_rxc_txc_div      = 16;
		m_acia_rxc_txc_p_low    = 9;
		m_acia_rxc_txc_p_high   = 15;
		break;
	case 0x02:
		m_acia_rxc_txc_div      = 16;
		m_acia_rxc_txc_p_low    = 5;
		m_acia_rxc_txc_p_high   = 8;
		break;
	case 0x03:
		m_acia_rxc_txc_div      = 8;
		m_acia_rxc_txc_p_low    = 5;
		m_acia_rxc_txc_p_high   = 8;
		break;
	}

	// Initialise memory configuration
	m_rom_mode = 0;
	set_rom_mode(1);
	m_vram_view.disable();

	// Reset serial state
	m_acia_irq_state = 0;
	m_acia_rxc_txc_state = 0;
	update_acia_rxc_txc();

	// The low bits of attribute RAM are not physically present and hence always read high
	for (u8 &b : m_attr_ram)
		b |= 0x7f;
}

void osborne1_state::video_start()
{
	m_video_timer->adjust(m_screen->time_until_pos(1, 0));
}

void osborne1sp_state::machine_start()
{
	osborne1_state::machine_start();

	save_item(NAME(m_resolution));
	save_item(NAME(m_hc_left));
}

void osborne1sp_state::machine_reset()
{
	osborne1_state::machine_reset();

	// Reset video hardware
	m_resolution = 0;
	m_hc_left = 1;
}

template <int Width, unsigned Scale>
inline void osborne1_state::draw_rows(uint16_t col, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (int y = cliprect.min_y; cliprect.max_y >= y; ++y)
	{
		// Vertical scroll is latched at the start of the visible area
		if (0 == y)
			m_scroll_y = m_pia1->b_output() & 0x1f;

		// Draw a line of the display
		u8 const ra = y % 10;
		uint16_t *p = &bitmap.pix(y);
		uint16_t const row = ((m_scroll_y + (y / 10)) << 7) & 0x0f80;

		for (uint16_t x = 0; Width > x; ++x)
		{
			uint16_t const offs = row | ((col + x) & 0x7f);
			u8 const chr = m_main_ram[0xf000 | offs];
			u8 const clr = BIT(m_attr_ram[offs], 7) ? 2 : 1;

			bool const underline = BIT(chr, 7) && (ra == 9);
			u8 const gfx = underline ? 0xff : m_p_chargen[(ra << 7) | (chr & 0x7f)];

			// Display a scanline of a character
			for (unsigned b = 0; 8 > b; ++b)
				p = std::fill_n(p, Scale, BIT(gfx, 7 - b) ? clr : 0);
		}
	}
}

// The derivation of the initial column is not obvious.  The 7-bit
// column counter is preloaded near the beginning of the horizontal
// blank period.  The initial column is offset by the number of
// character clock periods in the horizontal blank period minus one
// because it latches the value before it's displayed.  Using the
// standard video display, there are 12 character clock periods in
// the horizontal blank period, so subtracting 1 gives 0x0B.  Using
// the SCREEN-PAC's high-resolution mode, the character clock is
// twice the frequency giving 24 character clock periods in the
// horizontal blanking period, so subtracting 1 gives 0x17.  Using
// the standard video display, the column counter is preloaded with
// the high 7 bits of the value from PIA1 PORTB.  The SCREEN-PAC
// takes the two high bits of this value, but sets the low five bits
// to a fixed value of 1 or 9 depending on the value of the HC-LEFT
// signal (set by bit 1 of the value written to 0x2400).  Of course
// it depends on the value wrapping around to zero when it counts
// past 0x7F.

uint32_t osborne1_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	draw_rows<52, 1>(scroll_x() + 0x0b, bitmap, cliprect);

	return 0;
}

uint32_t osborne1sp_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	if (m_resolution)
		draw_rows<104, 1>((scroll_x() & 0x60) + (m_hc_left ? 0x09 : 0x01) + 0x17, bitmap, cliprect);
	else
		draw_rows<52, 2>(scroll_x() + 0x0b, bitmap, cliprect);

	return 0;
}


TIMER_CALLBACK_MEMBER(osborne1_state::video_callback)
{
	int const y = m_screen->vpos();
	u8 const ra = y % 10;

	// The beeper is gated so it's active four out of every ten scanlines
	m_beep_state = (ra & 0x04) ? 1 : 0;
	m_speaker->level_w((m_beep_state && BIT(m_pia1->b_output(), 5)) ? 1 : 0);

	int const next((y - ra) + ((ra < 4) ? 4 : (ra < 8) ? 8 : 14));
	m_video_timer->adjust(m_screen->time_until_pos((m_screen->height() > next) ? next : 0, 0));
}

TIMER_CALLBACK_MEMBER(osborne1_state::acia_rxc_txc_callback)
{
	m_acia_rxc_txc_state = m_acia_rxc_txc_state ? 0 : 1;
	update_acia_rxc_txc();
}


TILE_GET_INFO_MEMBER(osborne1_state::get_tile_info)
{
	// The gfxdecode and tilemap aren't actually used for drawing, they just look nice in the graphics viewer
	tileinfo.set(0, m_main_ram[0xf000 | tile_index] & 0x7f, 0, 0);
}


bool osborne1_state::set_rom_mode(u8 value)
{
	if (value != m_rom_mode)
	{
		m_rom_mode = value;
		if (m_rom_mode)
			m_rom_view.select(0);
		else
			m_rom_view.disable();
		return true;
	}
	else
	{
		return false;
	}
}

void osborne1_state::update_irq()
{
	if (m_pia0->irq_a_state())
		m_maincpu->set_input_line_and_vector(INPUT_LINE_IRQ0, ASSERT_LINE, 0xf0); // Z80
	else if (m_pia1->irq_a_state())
		m_maincpu->set_input_line_and_vector(INPUT_LINE_IRQ0, ASSERT_LINE, 0xf8); // Z80
	else if (m_acia_irq_state)
		m_maincpu->set_input_line_and_vector(INPUT_LINE_IRQ0, ASSERT_LINE, 0xfc); // Z80
	else
		m_maincpu->set_input_line_and_vector(INPUT_LINE_IRQ0, CLEAR_LINE, 0xfe); // Z80
}

void osborne1_state::update_acia_rxc_txc()
{
	m_acia->write_rxc(m_acia_rxc_txc_state);
	m_acia->write_txc(m_acia_rxc_txc_state);
	attoseconds_t const dividend = (ATTOSECONDS_PER_SECOND / 100) * (m_acia_rxc_txc_state ? m_acia_rxc_txc_p_high : m_acia_rxc_txc_p_low);
	attoseconds_t const divisor = (15974400 / 100) / m_acia_rxc_txc_div;
	m_acia_rxc_txc_timer->adjust(attotime(0, dividend / divisor));
}


MC6845_UPDATE_ROW(osborne1nv_state::crtc_update_row)
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	uint16_t const base = (ma >> 1) & 0xf80;
	uint32_t *p = &bitmap.pix(y);
	for (u8 x = 0; x < x_count; ++x)
	{
		uint16_t const offset = base | ((ma + x) & 0x7f);
		u8 const chr = m_main_ram[0xf000 | offset];
		u8 const clr = BIT(m_attr_ram[offset], 7) ? 2 : 1;

		u8 const gfx = ((chr & 0x80) && (ra == 9)) ? 0xff : m_p_nuevo[(ra << 7) | (chr & 0x7f)];

		for (unsigned bit = 0; 8 > bit; ++bit)
			*p++ = palette[BIT(gfx, 7 - bit) ? clr : 0];
	}
}

MC6845_ON_UPDATE_ADDR_CHANGED(osborne1nv_state::crtc_update_addr_changed)
{
}


void osborne1_state::osborne1_mem(address_map &map)
{
	map(0x0000, 0xffff).ram().share(m_main_ram);
	map(0xf000, 0xffff).w(FUNC(osborne1_state::videoram_w));

	map(0x0000, 0x3fff).view(m_rom_view);
	m_rom_view[0](0x0000, 0x0fff).mirror(0x1000).rom().region("maincpu", 0).unmapw();
	m_rom_view[0](0x2000, 0x2fff).mirror(0x1000).rw(FUNC(osborne1_state::bank2_peripherals_r), FUNC(osborne1_state::bank2_peripherals_w));

	map(0xf000, 0xffff).view(m_vram_view);
	m_vram_view[0](0xf000, 0xffff).ram().w(FUNC(osborne1_state::attrram_w)).share(m_attr_ram);
}

void osborne1sp_state::osborne1sp_mem(address_map &map)
{
	osborne1_mem(map);

	m_rom_view[0](0x2000, 0x2fff).mirror(0x1000).rw(FUNC(osborne1sp_state::bank2_peripherals_r), FUNC(osborne1sp_state::bank2_peripherals_w));
}


void osborne1_state::osborne1_op(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(osborne1_state::opcode_r));
}


void osborne1_state::osborne1_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x03);

	map(0x00, 0x03).w(FUNC(osborne1_state::bankswitch_w));
}

void osborne1nv_state::osborne1nv_io(address_map &map)
{
	osborne1_io(map);

	map.global_mask(0xff); // FIXME: complete guess

	map(0x04, 0x04).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x05, 0x05).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	// seems to be something at 0x06 as well, but no idea what - BIOS writes 0x07 on boot
}


static INPUT_PORTS_START( osborne1 )
	PORT_START("ROW0")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)                             PORT_CHAR('[') PORT_CHAR(']')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)                            PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)    PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13) PORT_NAME("Return")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT)   PORT_CODE(KEYCODE_LSHIFT)    PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)  PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)                                   PORT_CHAR('\t')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)                                   PORT_CHAR(UCHAR_MAMEKEY(ESC))

	PORT_START("ROW1")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)        PORT_CODE(KEYCODE_8_PAD)     PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)        PORT_CODE(KEYCODE_7_PAD)     PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)        PORT_CODE(KEYCODE_6_PAD)     PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)        PORT_CODE(KEYCODE_5_PAD)     PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)        PORT_CODE(KEYCODE_4_PAD)     PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)        PORT_CODE(KEYCODE_3_PAD)     PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)        PORT_CODE(KEYCODE_2_PAD)     PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)        PORT_CODE(KEYCODE_1_PAD)     PORT_CHAR('1') PORT_CHAR('!')

	PORT_START("ROW2")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)        PORT_CODE(KEYCODE_9_PAD)     PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)                                     PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)                                     PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)     PORT_CODE(KEYCODE_DEL_PAD)   PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)                                 PORT_CHAR(' ')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)        PORT_CODE(KEYCODE_0_PAD)     PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)     PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)                                    PORT_CHAR(UCHAR_MAMEKEY(UP))

	PORT_START("ROW3")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)                                     PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)                                     PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)                                     PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)                                     PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)                                     PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)                                     PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)                                     PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)                                     PORT_CHAR('q') PORT_CHAR('Q')

	PORT_START("ROW4")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)                                     PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)                                     PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)                                     PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)                                     PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)                                     PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)                                     PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)                                     PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)                                     PORT_CHAR('a') PORT_CHAR('A')

	PORT_START("ROW5")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)                                 PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)                                     PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)                                     PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)                                     PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)                                     PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)                                     PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)                                     PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)                                     PORT_CHAR('z') PORT_CHAR('Z')

	PORT_START("ROW6")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)                                PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)                                     PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)                             PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)                                 PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)                                 PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)                                 PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)                                  PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)                                 PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

	PORT_START("ROW7")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_TOGGLE PORT_NAME("Alpha Lock")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("RESET")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F12) PORT_NAME("RESET") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(osborne1_state::reset_key), 0)
	PORT_BIT(0x7f, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("CNF")
	PORT_BIT(0xfc, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_CONFNAME(0x03, 0x00, "Serial Speed")
	PORT_CONFSETTING(0x00, "300/1200")
	PORT_CONFSETTING(0x01, "600/2400")
	PORT_CONFSETTING(0x02, "1200/4800")
	PORT_CONFSETTING(0x03, "2400/9600")
INPUT_PORTS_END

INPUT_PORTS_START( osborne1nv )
	PORT_INCLUDE(osborne1)

	PORT_MODIFY("CNF")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END


/*
 * The Osborne-1 supports the following disc formats:
 * - Osborne single density: 40 tracks, 10 sectors per track, 256-byte sectors (100 KByte)
 * - Osborne double density: 40 tracks, 5 sectors per track, 1024-byte sectors (200 KByte)
 * - IBM Personal Computer: 40 tracks, 8 sectors per track, 512-byte sectors (160 KByte)
 * - Xerox 820 Computer: 40 tracks, 18 sectors per track, 128-byte sectors (90 KByte)
 * - DEC 1820 double density: 40 tracks, 9 sectors per track, 512-byte sectors (180 KByte)
 *
 */

static void osborne1_floppies(device_slot_interface &device)
{
	device.option_add("525sssd", FLOPPY_525_SSSD); // Siemens FDD 100-5, custom Osborne electronics
	device.option_add("525ssdd", FLOPPY_525_SSDD); // MPI 52(?), custom Osborne electronics
}


/* F4 Character Displayer */
static const gfx_layout osborne1_charlayout =
{
	8, 10,              // 8 x 10 characters
	128,                // 128 characters
	1,                  // 1 bits per pixel
	{ 0 },              // no bitplanes
	// x offsets
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	// y offsets
	{ 0*128*8, 1*128*8, 2*128*8, 3*128*8, 4*128*8, 5*128*8, 6*128*8, 7*128*8, 8*128*8, 9*128*8 },
	8                   // every char takes 16 x 1 bytes
};

static GFXDECODE_START( gfx_osborne1 )
	GFXDECODE_ENTRY("chargen", 0x0000, osborne1_charlayout, 0, 1)
GFXDECODE_END


void osborne1_state::osborne1_base(machine_config &config)
{
	Z80(config, m_maincpu, MAIN_CLOCK/4);
	m_maincpu->set_addrmap(AS_PROGRAM, &osborne1_state::osborne1_mem);
	m_maincpu->set_addrmap(AS_OPCODES, &osborne1_state::osborne1_op);
	m_maincpu->set_addrmap(AS_IO, &osborne1_state::osborne1_io);
	m_maincpu->irqack_cb().set(FUNC(osborne1_state::irqack_w));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER, rgb_t::green());

	GFXDECODE(config, m_gfxdecode, "palette", gfx_osborne1);
	PALETTE(config, "palette", palette_device::MONOCHROME_HIGHLIGHT);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 1.00);

	PIA6821(config, m_pia0);
	m_pia0->readpa_handler().set(m_ieee, FUNC(ieee488_device::dio_r));
	m_pia0->readpb_handler().set(FUNC(osborne1_state::ieee_pia_pb_r));
	m_pia0->writepa_handler().set(m_ieee, FUNC(ieee488_device::host_dio_w));
	m_pia0->writepb_handler().set(FUNC(osborne1_state::ieee_pia_pb_w));
	m_pia0->ca2_handler().set(m_ieee, FUNC(ieee488_device::host_ifc_w));
	m_pia0->cb2_handler().set(m_ieee, FUNC(ieee488_device::host_ren_w));
	m_pia0->irqa_handler().set(FUNC(osborne1_state::ieee_pia_irq_a_func));

	IEEE488(config, m_ieee, 0);
	m_ieee->srq_callback().set(m_pia0, FUNC(pia6821_device::ca2_w));

	PIA6821(config, m_pia1);
	m_pia1->writepa_handler().set(FUNC(osborne1_state::video_pia_port_a_w));
	m_pia1->writepb_handler().set(FUNC(osborne1_state::video_pia_port_b_w));
	m_pia1->cb2_handler().set(FUNC(osborne1_state::video_pia_out_cb2_dummy));
	m_pia1->irqa_handler().set(FUNC(osborne1_state::video_pia_irq_a_func));

	ACIA6850(config, m_acia);
	m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_acia->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_acia->irq_handler().set(FUNC(osborne1_state::serial_acia_irq_func));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_acia, FUNC(acia6850_device::write_rxd));
	rs232.dcd_handler().set(m_acia, FUNC(acia6850_device::write_dcd));
	rs232.cts_handler().set(m_acia, FUNC(acia6850_device::write_cts));
	rs232.ri_handler().set(m_pia1, FUNC(pia6821_device::ca2_w));

	MB8877(config, m_fdc, MAIN_CLOCK/16);
	m_fdc->set_force_ready(true);
	FLOPPY_CONNECTOR(config, m_floppy[0], osborne1_floppies, "525ssdd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[1], osborne1_floppies, "525ssdd", floppy_image_device::default_mfm_floppy_formats);

	SOFTWARE_LIST(config, "flop_list").set_original("osborne1");
}

void osborne1_state::osborne1(machine_config &config)
{
	osborne1_base(config);

	m_screen->set_screen_update(FUNC(osborne1_state::screen_update));
	m_screen->set_raw(MAIN_CLOCK / 2, 512, 0, 52 * 8, 260, 0, 24 * 10);
	m_screen->screen_vblank().set(m_pia1, FUNC(pia6821_device::ca1_w));
	m_screen->set_palette("palette");
	m_screen->set_video_attributes(VIDEO_UPDATE_SCANLINE | VIDEO_ALWAYS_UPDATE);
}

void osborne1sp_state::osborne1sp(machine_config &config)
{
	osborne1_base(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &osborne1sp_state::osborne1sp_mem);

	m_screen->set_screen_update(FUNC(osborne1sp_state::screen_update));
	m_screen->set_raw(MAIN_CLOCK, 1024, 0, 104 * 8, 260, 0, 24 * 10);
	m_screen->screen_vblank().set(m_pia1, FUNC(pia6821_device::ca1_w));
	m_screen->set_palette("palette");
	m_screen->set_video_attributes(VIDEO_UPDATE_SCANLINE | VIDEO_ALWAYS_UPDATE);
}

void osborne1nv_state::osborne1nv(machine_config &config)
{
	osborne1_base(config);

	m_maincpu->set_addrmap(AS_IO, &osborne1nv_state::osborne1nv_io);

	m_screen->set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	m_screen->set_raw(12.288_MHz_XTAL, (96 + 1) * 8, 0, 80 * 8, ((25 + 1) * 10) + 4, 0, 24 * 10);
	m_screen->screen_vblank().set(m_pia1, FUNC(pia6821_device::ca1_w)); // FIXME: AFAICT the PIA gets this from the (vestigial) onboard video circuitry

	sy6545_1_device &crtc(SY6545_1(config, "crtc", 12.288_MHz_XTAL / 8));
	crtc.set_screen(m_screen);
	crtc.set_show_border_area(false);
	crtc.set_char_width(8);
	crtc.set_update_row_callback(FUNC(osborne1nv_state::crtc_update_row));
	crtc.set_on_update_addr_change_callback(FUNC(osborne1nv_state::crtc_update_addr_changed));
}


ROM_START( osborne1 )
	ROM_DEFAULT_BIOS("ver144")
	ROM_SYSTEM_BIOS( 0, "vera",   "BIOS version A" )
	ROM_SYSTEM_BIOS( 1, "ver12",  "BIOS version 1.2" )
	ROM_SYSTEM_BIOS( 2, "ver121", "BIOS version 1.2.1" )
	ROM_SYSTEM_BIOS( 3, "ver13",  "BIOS version 1.3" )
	ROM_SYSTEM_BIOS( 4, "ver14",  "BIOS version 1.4" )
	ROM_SYSTEM_BIOS( 5, "ver143", "BIOS version 1.43" )
	ROM_SYSTEM_BIOS( 6, "ver144", "BIOS version 1.44" )

	ROM_REGION( 0x1000, "maincpu", 0 )
	ROMX_LOAD( "osba.bin",               0x0000, 0x1000, NO_DUMP,                                                      ROM_BIOS(0) )
	ROMX_LOAD( "osb12.bin",              0x0000, 0x1000, NO_DUMP,                                                      ROM_BIOS(1) )
	ROMX_LOAD( "osb121.bin",             0x0000, 0x1000, NO_DUMP,                                                      ROM_BIOS(2) )
	ROMX_LOAD( "osb13.bin",              0x0000, 0x1000, NO_DUMP,                                                      ROM_BIOS(3) )
	ROMX_LOAD( "rev1.40.ud11",           0x0000, 0x1000, CRC(3d966335) SHA1(0c60b97a3154a75868efc6370d26995eadc7d927), ROM_BIOS(4) )
	ROMX_LOAD( "rev1.43.ud11",           0x0000, 0x1000, CRC(91a48e3c) SHA1(c37b83f278d21e6e92d80f9c057b11f7f22d88d4), ROM_BIOS(5) )
	ROMX_LOAD( "3a10082-00rev-e.ud11",   0x0000, 0x1000, CRC(c0596b14) SHA1(ee6a9cc9be3ddc5949d3379351c1d58a175ce9ac), ROM_BIOS(6) )

	ROM_REGION( 0x800, "chargen", 0 )
	ROMX_LOAD( "char.ua15",      0x0000, 0x800, CRC(5297c109) SHA1(e1a59d87edd66e6c226102cb0688e9cb74dbb594), ROM_BIOS(0) ) // this is CHRROM from v1.4 BIOS MB
	ROMX_LOAD( "char.ua15",      0x0000, 0x800, CRC(5297c109) SHA1(e1a59d87edd66e6c226102cb0688e9cb74dbb594), ROM_BIOS(1) )
	ROMX_LOAD( "char.ua15",      0x0000, 0x800, CRC(5297c109) SHA1(e1a59d87edd66e6c226102cb0688e9cb74dbb594), ROM_BIOS(2) )
	ROMX_LOAD( "char.ua15",      0x0000, 0x800, CRC(5297c109) SHA1(e1a59d87edd66e6c226102cb0688e9cb74dbb594), ROM_BIOS(3) )
	ROMX_LOAD( "char.ua15",      0x0000, 0x800, CRC(5297c109) SHA1(e1a59d87edd66e6c226102cb0688e9cb74dbb594), ROM_BIOS(4) )
	ROMX_LOAD( "7a3007-00.ud15", 0x0000, 0x800, CRC(6c1eab0d) SHA1(b04459d377a70abc9155a5486003cb795342c801), ROM_BIOS(5) )
	ROMX_LOAD( "7a3007-00.ud15", 0x0000, 0x800, CRC(6c1eab0d) SHA1(b04459d377a70abc9155a5486003cb795342c801), ROM_BIOS(6) )
ROM_END

#define rom_osborne1sp rom_osborne1

ROM_START( osborne1nv )
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD( "monrom-rev1.51-12.ud11", 0x0000, 0x1000, CRC(298da402) SHA1(7fedd070936ccfe98f96d6e0ac71689666da79cb) )

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "7a3007-00.ud15", 0x0000, 0x800, CRC(6c1eab0d) SHA1(b04459d377a70abc9155a5486003cb795342c801) )

	ROM_REGION( 0x0800, "nuevo", 0 )
	ROM_LOAD( "character_generator_6-29-84.14", 0x0000, 0x800, CRC(6c1eab0d) SHA1(b04459d377a70abc9155a5486003cb795342c801) )
ROM_END

} // anonymous namespace


//    YEAR  NAME        PARENT    COMPAT  MACHINE     INPUT       CLASS             INIT        COMPANY          FULLNAME                     FLAGS
COMP( 1981, osborne1,   0,        0,      osborne1,   osborne1,   osborne1_state,   empty_init, "Osborne",       "Osborne-1",                 MACHINE_SUPPORTS_SAVE )
COMP( 1983, osborne1sp, osborne1, 0,      osborne1sp, osborne1,   osborne1sp_state, empty_init, "Osborne",       "Osborne-1 with SCREEN-PAC", MACHINE_SUPPORTS_SAVE )
COMP( 1984, osborne1nv, osborne1, 0,      osborne1nv, osborne1nv, osborne1nv_state, empty_init, "Osborne/Nuevo", "Osborne-1 (Nuevo Video)",   MACHINE_SUPPORTS_SAVE )



osi.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder, Robbbert, Wilbert Pol
/*

Ohio Scientific Superboard II Model 600

PCB Layout
----------

OHIO SCIENTIFIC MODEL 600 REV D

|-------------------------------------------------------------------------------------------|
|                   2114    2114                                                            |
|                                   LS138               CN1                                 |
|       IC1         2114    2114                                                            |
|                                   LS04            LS75    LS75                            |
|                   2114    2114                                                            |
|                                   LS138                                                   |
|                   2114    2114                    LS125   LS125                           |
|                                   LS138                                                   |
| -                 2114    2114                    8T28    8T28                            |
| |                                                                                         |
|C|                 2114    2114                                                            |
|N|                                                                                         |
|3|                 2114    2114                        6502                                |
| |                                                                                         |
| -                 2114    2114                                                            |
|                                                                                           |
|                           2114    8T28                ROM0                                |
|           LS163   LS157                   LS174                                           |
|                           2114    8T28                ROM1                                |
|   CA3130  LS163   LS157                   LS02                                            |
|                           2114    8T28                ROM2                                |
|   LS14    LS163   LS157                   LS04                                            |
|                                   SW1                 ROM3                                |
|   7417    7404    LS20    ROM5    7408    LS139                                           |
|                                                       ROM4                                |
| - 74123   7474    LS163   LS165   7474                                                    |
|C|                                                                                         |
|N|         7476    7400    LS157   LS93    LS04        6850                                |
|2|                                                                                         |
| - 7403    74123   3.7MHz  LS86    LS163   LS20                                    CN4     |
|-------------------------------------------------------------------------------------------|

Notes:
    All IC's shown.

    ROM0-5  -
    6502    -
    6850    - Asynchronous Communications Interface Adapter
    8T28    - 4-Bit Bidirectional Bus Transceiver
    CA3130  - Operational Amplifier
    CN1     - OSI-48 bus connector
    CN2     -
    CN3     -
    CN4     - sound connector
    SW1     -

*/

/*

Compukit UK101

PCB Layout
----------

|-------------------------------------------------------------------------------------------|
|                   2114    2114                                                            |
|                                   LS138               CN1                                 |
|       IC1         2114    2114                                                            |
|                                   7404            LS75    LS75                            |
|                   2114    2114                                                            |
|                                   LS138                                                   |
|                   2114    2114                    LS125   LS125                           |
|                                   LS138                                                   |
| -                 2114*   2114*                   8T28*   8T28*                           |
| |                                                                                         |
|C|                 2114*   2114*                                                           |
|N|     CN4                                                                                 |
|3|                 2114*   2114*                       6502                                |
| |                                                                                         |
| -                 2114*   2114*                                                           |
|                                                                                           |
|                           2114    8T28                ROM0                                |
|           74163   LS157                                                                   |
|                           2114    8T28                ROM1                                |
|   IC66'   74163   LS157                   7402                                            |
|                                                       ROM2                                |
|   IC67'   74163   LS157                   7404                                            |
|                                                       ROM3                                |
|   IC68'   7404    7420    ROM5            LS139                                           |
|                                                       ROM4                                |
| - LS123   7474    74163   74165   LS123                                                   |
|C|                                                                                         |
|N|         7476    7400            7493    7404        6850                                |
|2|                                                                                         |
| - 7403    LS123   8MHz            74163   LS20                                            |
|-------------------------------------------------------------------------------------------|

Notes:
    All IC's shown.

    ROM0-5  -
    6502    -
    6850    - Asynchronous Communications Interface Adapter
    8T28    - 4-Bit Bidirectional Bus Transceiver
    CN1     - OSI-48 Bus Connector
    CN2     -
    CN3     -
    CN4     - UHF Modulator Output
    *       - present when 8KB of RAM installed
    '       - present when cassette option installed

*/

/*

Ohio Scientific Single Sided Floppy Interface

PCB Layout
----------

OSI 470 REV B

|-------------------------------------------------------------------|
|                                                                   |
|                                               8T26    8T26        |
|                                                                   |
|       7417                                                        |
|                                                                   |
|                       6820                6850        7400  U2C   |
|       7417                                                        |
|                                                                   |
|                                                                   |
|       7417                                                7404    |
|                                                                   |
|                                                                   |
|       7417                                                        |
|                                                                   |
|                   7400    7404    74123   74123   7410            |
|   4MHz                                                            |
|                                                                   |
|                                                                   |
|   7400    7493    74390   74390   74390   7404    7430    7404    |
|                                                                   |
|                                                                   |
|                   U6A                                             |
|                                                                   |
|-------------------------------------------------------------------|

Notes:
    All IC's shown.

    6850    - Asynchronous Communications Interface Adapter
    6820    - Peripheral Interface Adapter
    8T26    - 4-Bit Bidirectional Bus Transceiver
    U2C     - PROTO?
    U6A     - PROTO?


TODO:

    - floppy PIA is actually a 6820
    - break key
    - power on reset
    - Superboard II revisions A/C/D
    - uk101 medium resolution graphics
    - uk101 ay-3-8910 sound
    - faster cassette
    - floppy
    - need floppies to test with
    - support for BAS files and other formats
    - wemon?
    - rs232

Notes added 2012-05-11 [Robbbert]
    General
    - Added F1 key to toggle the sound on 'sb2m600b', to silence the awful screech.
    - Added F3 key to simulate the RESET / BREAK key on the real keyboard.
    - These machines only uses uppercase input. Shift-Lock must be on at all times.
    - At boot you get a screen DCWM or similar:
      D = boot from floppy
      C = Cold boot & start BASIC
      W = Warm boot (don't use after turning on the machine) jump to 0000.
      M = Monitor (only options are to read/modify memory, Go, Load a tape).
    - The corrupt error messages in Basic are normal and are documented in the user
      manual. It's not a bug.

    Compukit UK101
    - The Monitor ROM that had always been there is for a 32x32 screen. I assume it
      was the prototype referred to in the first Practical Electronics construction
      article. Also, the keyboard doesn't work in Basic.
    - I found a working rom in another emulator, this runs fine on the 64x16 screen.
    - But, the proper rom that came with the kit needs to be found.
    - The proper rom (and the prototype) allow you to boot from floppy, but this is
      not normally fitted. It appears that it would work the same as in the other
      systems in this driver.

Notes added 2013-01-20
      Added a modified basic rom, which fixes the garbage collection problem.
      Try the following program with -bios 0 and -bios 1. It will work only
      with bios 1. You can copy/paste this code, but make sure you include the
      trailing blank line.

10 DIM A$(3)
RUN
PRINT FRE(0)


Keyboard notes and BTANBs:

- Keyboards on all models are identical, except on UK101 where ^ replaces LINE FEED.

- When run for the first time, the keyboard is in lowercase. Shift will correctly
  select uppercase. Special keys do weird things, but come good when Shift pressed.
  Since all input MUST be uppercase, the first thing to do is press Capslock (which
  is SHIFT LOCK on the real machine). This is saved in your cfg file on exit.

- After that, uppercase works correctly, as do the other keys. Pressing Shift will now
  do odd things to the letters, and they become random symbols.

- Natural keyboard is set up to take advantage of these oddities and get the maximum
  symbols available, however lowercase is not available. Natural keyboard will only
  work correctly when the SHIFT LOCK is engaged.

*/

#include "emu.h"
#include "osi.h"

#include "machine/clock.h"
#include "screen.h"
#include "speaker.h"


/* Sound */

static const discrete_dac_r1_ladder osi600_dac =
{
	4,          // size of ladder
	{ 180, 180, 180, 180, 0, 0, 0, 0 }, // R68, R69, R70, R71
	5,          // 5V
	RES_K(1),   // R67
	0,          // no rGnd
	0           // no cFilter
};

static DISCRETE_SOUND_START( osi600_discrete_interface )
	DISCRETE_INPUT_DATA(NODE_01)

	DISCRETE_DAC_R1(NODE_02, NODE_01, DEFAULT_TTL_V_LOGIC_1, &osi600_dac)
	DISCRETE_CRFILTER(NODE_03, NODE_02, (int)(1.0/(1.0/RES_K(1)+1.0/180+1.0/180+1.0/180+1.0/180)), CAP_U(0.1))
	DISCRETE_OUTPUT(NODE_03, 100)
	DISCRETE_GAIN(NODE_04, NODE_03, 32767.0/5)
	DISCRETE_OUTPUT(NODE_04, 100)
DISCRETE_SOUND_END

#if 0
static const discrete_dac_r1_ladder osi600c_dac =
{
	8,          // size of ladder
	{ RES_K(68), RES_K(33), RES_K(16), RES_K(8.2), RES_K(3.9), RES_K(2), RES_K(1), 510 }, // R73, R71, R70, R67, R68, R69, R75, R74
	5,          // 5V
	510,        // R86
	0,          // no rGnd
	CAP_U(33)   // C63
};
#endif

static DISCRETE_SOUND_START( osi600c_discrete_interface )
	DISCRETE_INPUT_DATA(NODE_01)
	DISCRETE_INPUT_LOGIC(NODE_10)

	DISCRETE_DAC_R1(NODE_02, NODE_01, DEFAULT_TTL_V_LOGIC_1, &osi600_dac)
	DISCRETE_ONOFF(NODE_03, NODE_10, NODE_02)
	DISCRETE_OUTPUT(NODE_03, 100)
DISCRETE_SOUND_END

/* Keyboard */

uint8_t sb2m600_state::keyboard_r()
{
	if (m_io_reset->read())
		m_maincpu->reset();

	u8 i, data = 0xff;

	for (i = 0; i < 8; i++)
		if (!BIT(m_keylatch, i))
			data &= m_io_keyboard[i]->read();

	return data;
}

void sb2m600_state::keyboard_w(uint8_t data)
{
	m_keylatch = data;

	if (m_io_sound->read())
		m_discrete->write(NODE_01, (data >> 2) & 0x0f);
}

void uk101_state::keyboard_w(uint8_t data)
{
	m_keylatch = data;
}

void sb2m600_state::ctrl_w(uint8_t data)
{
	/*

	    bit     signal          description

	    0       _32             screen size (0=32x32, 1=64x16)
	    1       COLOR EN        color enable
	    2       BK0
	    3       BK1
	    4       DAC DISABLE     DAC sound enable
	    5
	    6
	    7

	*/

	m_32 = BIT(data, 0);
	m_coloren = BIT(data, 1);

	m_discrete->write(NODE_10, BIT(data, 4));
}

void c1p_state::osi630_ctrl_w(uint8_t data)
{
	/*

	    bit     description

	    0       AC control enable
	    1       tone generator enable
	    2       modem select (0 = printer, 1 = modem)
	    3
	    4
	    5
	    6
	    7

	*/

	m_beeper->set_state(BIT(data, 1));
}

void c1p_state::osi630_sound_w(uint8_t data)
{
	if (data != 0)
		m_beeper->set_clock(49152 / data);
}

/* Disk Drive */

/*
    C000 FLOPIN         FLOPPY DISK STATUS PORT

    BIT FUNCTION
    0   DRIVE 0 READY (0 IF READY)
    1   TRACK 0 (0 IF AT TRACK 0)
    2   FAULT (0 IF FAULT)
    3
    4   DRIVE 1 READY (0 IF READY)
    5   WRITE PROTECT (0 IF WRITE PROTECT)
    6   DRIVE SELECT (1 = A OR C, 0 = B OR D)
    7   INDEX (0 IF AT INDEX HOLE)

    C002 FLOPOT         FLOPPY DISK CONTROL PORT

    BIT FUNCTION
    0   WRITE ENABLE (0 ALLOWS WRITING)
    1   ERASE ENABLE (0 ALLOWS ERASING)
        ERASE ENABLE IS ON 200us AFTER WRITE IS ON
        ERASE ENABLE IS OFF 530us AFTER WRITE IS OFF
    2   STEP BIT : INDICATES DIRECTION OF STEP (WAIT 10us FIRST)
        0 INDICATES STEP TOWARD 76
        1 INDICATES STEP TOWARD 0
    3   STEP (TRANSITION FROM 1 TO 0)
        MUST HOLD AT LEAST 10us, MIN 8us BETWEEN
    4   FAULT RESET (0 RESETS)
    5   SIDE SELECT (1 = A OR B, 0 = C OR D)
    6   LOW CURRENT (0 FOR TRKS 43-76, 1 FOR TRKS 0-42)
    7   HEAD LOAD (0 TO LOAD : MUST WAIT 40ms AFTER)

    C010 ACIA           DISK CONTROLLER ACIA STATUS PORT
    C011 ACIAIO         DISK CONTROLLER ACIA I/O PORT
*/

void sb2m600_state::floppy_index_callback(floppy_image_device *floppy, int state)
{
	m_fdc_index = state;
}

uint8_t c1pmf_state::osi470_pia_pa_r()
{
	/*

	    bit     description

	    0       _READY DRIVE 1
	    1       _TRACK 00
	    2       _FAULT
	    3       _SECTOR
	    4       _READY DRIVE 2
	    5       _WRITE PROTECT
	    6
	    7       _INDEX

	*/

	return (m_fdc_index << 7);
}

void c1pmf_state::osi470_pia_pa_w(uint8_t data)
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4
	    5
	    6       drive select
	    7

	*/
}

void c1pmf_state::osi470_pia_pb_w(uint8_t data)
{
	/*

	    bit     description

	    0       _WRITE ENABLE
	    1       _ERASE ENABLE
	    2       _STEP IN
	    3       _STEP
	    4       _FAULT RESET
	    5       side select
	    6       _LOW CURRENT
	    7       _HEAD LOAD

	*/
}

void c1pmf_state::osi470_pia_cb2_w(int state)
{
}

/* Memory Maps */

void sb2m600_state::osi600_mem(address_map &map)
{
	map(0x0000, 0x1fff).bankrw("bank1");
	map(0xa000, 0xbfff).rom();
	map(0xd000, 0xd3ff).ram().share("video_ram");
	map(0xdf00, 0xdf00).rw(FUNC(sb2m600_state::keyboard_r), FUNC(sb2m600_state::keyboard_w));
	map(0xf000, 0xf001).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xf800, 0xffff).rom();
}

void uk101_state::uk101_mem(address_map &map)
{
	map(0x0000, 0x1fff).bankrw("bank1");
	map(0xa000, 0xbfff).rom();
	map(0xd000, 0xd3ff).ram().share("video_ram");
	map(0xd400, 0xd7ff).noprw();  // bios sets this to spaces at boot
	map(0xdc00, 0xdfff).r(FUNC(uk101_state::keyboard_r)).w(FUNC(uk101_state::keyboard_w));
	map(0xf000, 0xf001).mirror(0x00fe).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xf800, 0xffff).rom();
}

void c1p_state::c1p_mem(address_map &map)
{
	map(0x0000, 0x4fff).bankrw("bank1");
	map(0xa000, 0xbfff).rom();
	map(0xc704, 0xc707).rw("pia_1", FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xc708, 0xc70b).rw("pia_2", FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xc70c, 0xc70f).rw("pia_3", FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xd000, 0xd3ff).ram().share("video_ram");
	map(0xd400, 0xd7ff).ram().share("color_ram");
	map(0xd800, 0xd800).w(FUNC(c1p_state::ctrl_w));
	map(0xdf00, 0xdf00).rw(FUNC(c1p_state::keyboard_r), FUNC(c1p_state::keyboard_w));
	map(0xf000, 0xf001).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xf7c0, 0xf7c0).w(FUNC(c1p_state::osi630_sound_w));
	map(0xf7e0, 0xf7e0).w(FUNC(c1p_state::osi630_ctrl_w));
	map(0xf800, 0xffff).rom();
}

void c1pmf_state::c1pmf_mem(address_map &map)
{
	map(0x0000, 0x4fff).bankrw("bank1");
	map(0xa000, 0xbfff).rom();
	map(0xc000, 0xc003).rw("pia_0", FUNC(pia6821_device::read), FUNC(pia6821_device::write)); // FDC
	map(0xc010, 0xc011).rw("acia_1", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xc704, 0xc707).rw("pia_1", FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xc708, 0xc70b).rw("pia_2", FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xc70c, 0xc70f).rw("pia_3", FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xd000, 0xd3ff).ram().share("video_ram");
	map(0xd400, 0xd7ff).ram().share("color_ram");
	map(0xd800, 0xd800).w(FUNC(c1pmf_state::ctrl_w));
	map(0xdf00, 0xdf00).rw(FUNC(c1pmf_state::keyboard_r), FUNC(c1pmf_state::keyboard_w));
	map(0xf000, 0xf001).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xf7c0, 0xf7c0).w(FUNC(c1pmf_state::osi630_sound_w));
	map(0xf7e0, 0xf7e0).w(FUNC(c1pmf_state::osi630_ctrl_w));
	map(0xf800, 0xffff).rom();
}

/* Input Ports */

static INPUT_PORTS_START( osi600 )
	PORT_START("ROW0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_TOGGLE
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RIGHT SHIFT") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LEFT SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_TAB) PORT_CHAR(27)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("REPEAT") PORT_CODE(KEYCODE_BACKSLASH)

	PORT_START("ROW1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('@')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')

	PORT_START("ROW2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR(']')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('^')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')

	PORT_START("ROW3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('[')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')

	PORT_START("ROW4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')

	PORT_START("ROW5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LINE FEED") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(10)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('_')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('\\')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')

	PORT_START("ROW6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RUB OUT") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')

	PORT_START("ROW7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')

	PORT_START("Sound")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_TOGGLE

	PORT_START("Reset")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RESET") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
INPUT_PORTS_END

static INPUT_PORTS_START( uk101 )
	PORT_INCLUDE(osi600)
	PORT_MODIFY("ROW5")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_BACKSLASH)
INPUT_PORTS_END

/* Machine Start */

TIMER_DEVICE_CALLBACK_MEMBER( sb2m600_state::kansas_w )
{
	m_cass_data[3]++;

	if (m_cassbit != m_cassold)
	{
		m_cass_data[3] = 0;
		m_cassold = m_cassbit;
	}

	if (m_cassbit)
		m_cass->output(BIT(m_cass_data[3], 0) ? -1.0 : +1.0); // 2400Hz
	else
		m_cass->output(BIT(m_cass_data[3], 1) ? -1.0 : +1.0); // 1200Hz
}

TIMER_DEVICE_CALLBACK_MEMBER( sb2m600_state::kansas_r)
{
	/* cassette - turn 1200/2400Hz to a bit */
	m_cass_data[1]++;
	uint8_t cass_ws = (m_cass->input() > +0.03) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		m_acia->write_rxd((m_cass_data[1] < 12) ? 1 : 0);
		m_cass_data[1] = 0;
	}
}

void sb2m600_state::machine_start()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	/* configure RAM banking */
	membank("bank1")->configure_entry(0, memregion(M6502_TAG)->base());
	membank("bank1")->set_entry(0);

	switch (m_ram->size())
	{
	case 4*1024:
		program.install_readwrite_bank(0x0000, 0x0fff, membank("bank1"));
		program.unmap_readwrite(0x1000, 0x1fff);
		break;

	case 8*1024:
		program.install_readwrite_bank(0x0000, 0x1fff, membank("bank1"));
		break;
	}

	/* register for state saving */
	save_item(NAME(m_keylatch));
	save_pointer(NAME(m_video_ram.target()), OSI600_VIDEORAM_SIZE);
	save_item(NAME(m_cass_data));
	save_item(NAME(m_cassbit));
	save_item(NAME(m_cassold));
	save_item(NAME(m_fdc_index));
	save_item(NAME(m_32));
	save_item(NAME(m_coloren));
}

void c1p_state::machine_start()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	/* configure RAM banking */
	membank("bank1")->configure_entry(0, memregion(M6502_TAG)->base());
	membank("bank1")->set_entry(0);

	switch (m_ram->size())
	{
	case 8*1024:
		program.install_readwrite_bank(0x0000, 0x1fff, membank("bank1"));
		program.unmap_readwrite(0x2000, 0x4fff);
		break;

	case 20*1024:
		program.install_readwrite_bank(0x0000, 0x4fff, membank("bank1"));
		break;
	}

	/* register for state saving */
	save_item(NAME(m_cass_data));
	save_item(NAME(m_cassbit));
	save_item(NAME(m_cassold));
	save_item(NAME(m_fdc_index));
	save_item(NAME(m_keylatch));
	save_item(NAME(m_32));
	save_item(NAME(m_coloren));
	save_pointer(NAME(m_video_ram.target()), OSI600_VIDEORAM_SIZE);
	save_pointer(NAME(m_color_ram.target()), OSI630_COLORRAM_SIZE);
}

void c1pmf_state::machine_start()
{
	c1p_state::machine_start();

	// drive select logic missing
	if (m_floppy[0]->get_device())
		m_floppy[0]->get_device()->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&c1pmf_state::floppy_index_callback, this));
}

// disk format: 1 head, 36 tracks (? - manual displays a directory listing with 40 tracks),
// 10 sectors, 256 byte sector length, first sector id 0
static void osi_floppies(device_slot_interface &device)
{
	device.option_add("ssdd", FLOPPY_525_SSDD);
}

/* F4 Character Displayer */
static const gfx_layout osi_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_osi )
	GFXDECODE_ENTRY( "chargen", 0x0000, osi_charlayout, 0, 1 )
GFXDECODE_END

/* Machine Drivers */

void sb2m600_state::osi600(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, X1/4); // .98304 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &sb2m600_state::osi600_mem);

	/* video hardware */
	osi600_video(config);
	GFXDECODE(config, "gfxdecode", "palette", gfx_osi);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	DISCRETE(config, m_discrete);
	m_discrete->set_intf(osi600_discrete_interface);
	m_discrete->add_route(ALL_OUTPUTS, "mono", 0.50);

	/* cassette ACIA */
	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set([this] (bool state) { m_cassbit = state; });

	clock_device &acia_clock(CLOCK(config, "acia_clock", 4'800)); // 300 baud x 16(divider) = 4800
	acia_clock.signal_handler().set(m_acia, FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append(m_acia, FUNC(acia6850_device::write_rxc));

	/* cassette */
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	TIMER(config, "kansas_w").configure_periodic(FUNC(sb2m600_state::kansas_w), attotime::from_hz(4800)); // cass write
	TIMER(config, "kansas_r").configure_periodic(FUNC(sb2m600_state::kansas_r), attotime::from_hz(40000)); // cass read

	/* internal ram */
	RAM(config, m_ram);
	m_ram->set_default_size("4K");
	m_ram->set_extra_options("8K");
}

void uk101_state::uk101(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, UK101_X1/8); // 1 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &uk101_state::uk101_mem);

	/* video hardware */
	uk101_video(config);
	GFXDECODE(config, "gfxdecode", "palette", gfx_osi);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	/* cassette ACIA */
	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set([this] (bool state) { m_cassbit = state; });

	clock_device &acia_clock(CLOCK(config, "acia_clock", 4'800)); // 300 baud x 16(divider) = 4800
	acia_clock.signal_handler().set(m_acia, FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append(m_acia, FUNC(acia6850_device::write_rxc));

	/* cassette */
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	TIMER(config, "kansas_w").configure_periodic(FUNC(uk101_state::kansas_w), attotime::from_hz(4800)); // cass write
	TIMER(config, "kansas_r").configure_periodic(FUNC(uk101_state::kansas_r), attotime::from_hz(40000)); // cass read

	/* internal ram */
	RAM(config, m_ram);
	m_ram->set_default_size("4K");
	m_ram->set_extra_options("8K");
}

void c1p_state::c1p(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, X1/4); // .98304 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &c1p_state::c1p_mem);

	/* video hardware */
	osi630_video(config);
	GFXDECODE(config, "gfxdecode", "palette", gfx_osi);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	DISCRETE(config, m_discrete);
	m_discrete->set_intf(osi600c_discrete_interface);
	m_discrete->add_route(ALL_OUTPUTS, "mono", 0.50);
	BEEP(config, "beeper", 300).add_route(ALL_OUTPUTS, "mono", 0.50);
	TIMER(config, m_beep_timer).configure_generic(FUNC(c1p_state::beep_timer));

	PIA6821(config, "pia_1");
	PIA6821(config, "pia_2");
	PIA6821(config, "pia_3");

	/* cassette ACIA */
	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set([this] (bool state) { m_cassbit = state; });

	clock_device &acia_clock(CLOCK(config, "acia_clock", 4'800)); // 300 baud x 16(divider) = 4800
	acia_clock.signal_handler().set(m_acia, FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append(m_acia, FUNC(acia6850_device::write_rxc));

	/* cassette */
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	TIMER(config, "kansas_w").configure_periodic(FUNC(c1p_state::kansas_w), attotime::from_hz(4800)); // cass write
	TIMER(config, "kansas_r").configure_periodic(FUNC(c1p_state::kansas_r), attotime::from_hz(40000)); // cass read

	/* internal ram */
	RAM(config, m_ram);
	m_ram->set_default_size("8K");
	m_ram->set_extra_options("20K");
}

void c1pmf_state::c1pmf(machine_config &config)
{
	c1p(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &c1pmf_state::c1pmf_mem);

	pia6821_device &pia0(PIA6821(config, "pia_0"));
	pia0.readpa_handler().set(FUNC(c1pmf_state::osi470_pia_pa_r));
	pia0.writepa_handler().set(FUNC(c1pmf_state::osi470_pia_pa_w));
	pia0.writepb_handler().set(FUNC(c1pmf_state::osi470_pia_pb_w));
	pia0.cb2_handler().set(FUNC(c1pmf_state::osi470_pia_cb2_w));

	/* floppy ACIA */
	ACIA6850(config, "acia_1", 0);

	CLOCK(config, "floppy_clock", XTAL(4'000'000)/8).signal_handler().set("acia_1", FUNC(acia6850_device::write_txc)); // 250 kHz

	FLOPPY_CONNECTOR(config, m_floppy[0], osi_floppies, "ssdd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[1], osi_floppies, nullptr,   floppy_image_device::default_mfm_floppy_formats);

	/* internal ram */
	m_ram->set_default_size("20K");
}

/* ROMs */



ROM_START( sb2m600b )
	ROM_REGION( 0x10000, M6502_TAG, 0 )
	ROM_LOAD( "basus01.u9",  0xa000, 0x0800, CRC(f4f5dec0) SHA1(b41bf24b4470b6e969d32fe48d604637276f846e) )
	ROM_LOAD( "basus02.u10", 0xa800, 0x0800, CRC(0039ef6a) SHA1(1397f0dc170c16c8e0c7d02e63099e986e86385b) )
	ROM_LOAD( "basus04.u12", 0xb800, 0x0800, CRC(8ee6030e) SHA1(71f210163e4268cba2dd78a97c4d8f5dcebf980e) )
	ROM_LOAD( "monde01.u13", 0xf800, 0x0800, CRC(95a44d2e) SHA1(4a0241c4015b94c436d0f0f58b3dd9d5207cd847) ) // also known as syn600.rom
	ROM_SYSTEM_BIOS(0, "original", "Original")
	ROMX_LOAD("basus03.u11", 0xb000, 0x0800, CRC(ca25f8c1) SHA1(f5e8ee93a5e0656657d0cc60ef44e8a24b8b0a80), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "fixed", "Fixed")
	ROMX_LOAD( "basic3.rom", 0xb000, 0x0800, CRC(ac37d575) SHA1(11407eb24d1ba7afb889b7677c987e8be1a61aab), ROM_BIOS(1) )

	ROM_REGION( 0x0800, "chargen",0)
	ROM_LOAD( "chgsup2.u41", 0x0000, 0x0800, CRC(735f5e0a) SHA1(87c6271497c5b00a974d905766e91bb965180594) ) // see below, is this the same rom, but on a different pcb/form factor?
	// Label: "<Synertek Logo> 7837E // C28339M // CARGENV1.0"; This mask ROM is on the OSI 540 PCB, as seen at http://www.classic-computers.org.nz/blog/images/2014-06-14-540-board.jpg at location ?E3?

	ROM_REGION( 0x0800, "user1",0)
	// Another bios rom
	ROM_LOAD( "c2 c4 synmon.rom", 0x0000, 0x0800, CRC(03cdbcc5) SHA1(5426ae14522ef485b6089472011db0ae1d192630) )
ROM_END

// same roms are used in Challenger 2P and 4P
#define rom_c1p rom_sb2m600b
#define rom_c1pmf rom_sb2m600b

ROM_START( uk101 )
	ROM_REGION( 0x10000, M6502_TAG, 0 )
	ROM_LOAD( "basuk01.ic9",   0xa000, 0x0800, CRC(9d3caa92) SHA1(b2c3d1af0c4f3cead1dbd44aaf5a11680880f772) )
	ROM_LOAD( "basus02.ic10",  0xa800, 0x0800, CRC(0039ef6a) SHA1(1397f0dc170c16c8e0c7d02e63099e986e86385b) )
	ROM_LOAD( "basuk03.ic11",  0xb000, 0x0800, CRC(0d011242) SHA1(54bd33522a5d1991086eeeff3a4f73c026be45b6) )
	ROM_LOAD( "basuk04.ic12",  0xb800, 0x0800, CRC(667223e8) SHA1(dca78be4b98317413376d69119942d692e39575a) )
	// This monitor came from another emulator and works well on the 64x16 screen
	ROM_SYSTEM_BIOS(0, "final", "64x16 screen final? rom")
	ROMX_LOAD( "monuk02.ic13",  0xf800, 0x0800, CRC(e5b7028d) SHA1(74f0934014fdf83d33c8d3579e562b53c0683270), ROM_BIOS(0) )
	// This monitor is for a 32x32 screen, and could be the prototype referred to in Practical Electronics
	ROM_SYSTEM_BIOS(1, "proto", "32x32 screen proto? rom")
	ROMX_LOAD( "monuk02_alt.ic13",  0xf800, 0x0800, CRC(04ac5822) SHA1(2bbbcd0ca18103fd68afcf64a7483653b925d83e), ROM_BIOS(1) )

	ROM_REGION( 0x800, "chargen", 0 )
	ROM_LOAD( "chguk101.ic41", 0x0000, 0x0800, CRC(fce2c84a) SHA1(baa66a7a48e4d62282671ef53abfaf450b888b70) )
ROM_END

/* Driver Initialization */

TIMER_DEVICE_CALLBACK_MEMBER(c1p_state::beep_timer)
{
	m_beeper->set_state(0);
	m_beeper->set_clock(300);
}

void c1p_state::init_c1p()
{
	m_beep_timer->adjust(attotime::zero);
}


/* System Drivers */

//    YEAR  NAME      PARENT    COMPAT  MACHINE  INPUT   CLASS          INIT        COMPANY            FULLNAME                            FLAGS
COMP( 1978, sb2m600b, 0,        0,      osi600,  osi600, sb2m600_state, empty_init, "Ohio Scientific", "Superboard II Model 600 (Rev. B)", MACHINE_SUPPORTS_SAVE )
//COMP( 1980, sb2m600c, 0,        0,      osi600c, osi600, sb2m600_state, empty_init, "Ohio Scientific", "Superboard II Model 600 (Rev. C)", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1980, c1p,      sb2m600b, 0,      c1p,     osi600, c1p_state,     init_c1p,   "Ohio Scientific", "Challenger 1P Series 2",           MACHINE_SUPPORTS_SAVE )
COMP( 1980, c1pmf,    sb2m600b, 0,      c1pmf,   osi600, c1pmf_state,   init_c1p,   "Ohio Scientific", "Challenger 1P MF Series 2",        MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1979, uk101,    sb2m600b, 0,      uk101,   uk101,  uk101_state,   empty_init, "Compukit",        "UK101",                            MACHINE_SUPPORTS_SAVE )



p112.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

P112 Single Board Computer

2010-08-30 Skeleton driver

The P112 is a stand-alone 8-bit CPU board. Typically running CP/M (tm) or a
similar operating system, it provides a Z80182 (Z-80 upgrade) CPU with up to
1MB of memory, serial, parallel and diskette IO, and realtime clock, in a
3.5-inch drive form factor. Powered solely from 5V, it draws 150mA (nominal:
not including disk drives) with a 16MHz CPU clock. Clock speeds up to 24.576MHz
are possible.

http://members.iinet.net.au/~daveb/p112/p112.html

Some of the parts:
 32kHz crystal          1       Y2              (RTC crystal)
 16MHz crystal          1       Y1              Parallel resonant (CPU clock)
 24MHz crystal          1       Y3              Parallel resonant (IO chip clock)
 28F256A                1       U4              (Intel Flash ROM, programmed)
 74HCT00                1       U5
 74ACT02                1       U8
 74ACT139               1       U11
 62256                  2       U2      U3      (Static RAM, 32kB)
 DS1202                 1       U6              (Dallas RTC chip)
 FDC37C665IR            1       U9              (SMC Super-IO chip)
 LT1133                 2       U7      U10     (Linear Technology RS232 Tx/Rx)
 NMF0512S               1       U12             (Newport Components Flash ROM power)
 TL7705ACP              1       U15             (Texas Insts. Reset genr.)
 Z8018216               1       U1              (Zilog CPU chip)

****************************************************************************/

#include "emu.h"
#include "cpu/z180/z180.h"
#include "emupal.h"
#include "screen.h"


namespace {

class p112_state : public driver_device
{
public:
	p112_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void p112(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	uint32_t screen_update_p112(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	required_device<cpu_device> m_maincpu;
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
};


void p112_state::mem_map(address_map &map)
{
	map(0x00000, 0x07fff).rom();
	map(0x08000, 0xfffff).ram();
}

void p112_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
}

/* Input ports */
static INPUT_PORTS_START( p112 )
INPUT_PORTS_END


void p112_state::machine_reset()
{
}

void p112_state::machine_start()
{
}

uint32_t p112_state::screen_update_p112(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void p112_state::p112(machine_config &config)
{
	/* basic machine hardware */
	Z80182(config, m_maincpu, XTAL(16'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &p112_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &p112_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(240, 320);
	screen.set_visarea(0, 240-1, 0, 320-1);
	screen.set_screen_update(FUNC(p112_state::screen_update_p112));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);
}

/* ROM definition */
ROM_START( p112 )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "960513", "ver 13-05-1996" )
	ROMX_LOAD( "960513.bin",  0x00000, 0x8000, CRC(077c1c40) SHA1(c1e6b4b0de50bba90f0d4667f9344815bb578b9b), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "970308", "ver 08-03-1997" )
	ROMX_LOAD( "970308.bin",  0x00000, 0x8000, CRC(15e61f0d) SHA1(66ba1af7da0450b69650086ab20230390ba23e17), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "4b1", "ver 4b1" )
	ROMX_LOAD( "romv4b1.bin", 0x00000, 0x8000, CRC(15d79beb) SHA1(f971f75a717e3f6d59b257eb3af369d4d2e0f301), ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "4b2", "ver 4b2" )
	ROMX_LOAD( "romv4b2.bin", 0x00000, 0x8000, CRC(9b9a8a5e) SHA1(c40151ee654008b9f803d6a05d692a5a3619a726), ROM_BIOS(3))
	ROM_SYSTEM_BIOS( 4, "4b3", "ver 4b3" )
	ROMX_LOAD( "romv4b3.bin", 0x00000, 0x8000, CRC(734031ea) SHA1(2e5e5ac6bd17d15cab24a36bc3da42ca49cbde92), ROM_BIOS(4))
	ROM_SYSTEM_BIOS( 5, "4b4", "ver 4b4" )
	ROMX_LOAD( "romv4b4.bin", 0x00000, 0x8000, CRC(cd419c40) SHA1(6002130d874387c9ec23b4363fe9f0ca78208878), ROM_BIOS(5))
	ROM_SYSTEM_BIOS( 6, "5", "ver 5" )
	ROMX_LOAD( "051103.bin",  0x00000, 0x8000, CRC(6c47ec13) SHA1(24f5bf1524425186fe10e1d29d05f6efbd3366d9), ROM_BIOS(6))
	ROM_SYSTEM_BIOS( 7, "5b1", "ver 5b1" )
	ROMX_LOAD( "romv5b1.bin", 0x00000, 0x8000, CRC(047296f7) SHA1(380f8e4237525636c605b7e37d989ace8437beb4), ROM_BIOS(7))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY        FULLNAME  FLAGS */
COMP( 1996, p112, 0,      0,      p112,    p112,  p112_state, empty_init, "Dave Brooks", "P112",   MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



p2000t.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Paul Daniels
/************************************************************************
Philips P2000 1 Memory map

    CPU: Z80
        0000-0fff   ROM
        1000-4fff   ROM (appl)
        5000-57ff   RAM (Screen T ver)
        5000-5fff   RAM (Screen M ver)
        6000-9fff   RAM (system)
        a000-ffff   RAM (extension)

    Interrupts:

    Ports:
        00-09       Keyboard input
        10-1f       Output ports
        20-2f       Input ports
        30-3f       Scroll reg (T ver)
        50-5f       Beeper
        70-7f       DISAS (M ver)
        88-8B       CTC
        8C-90       Floppy ctrl
        94          RAM Bank select

    Display: SAA5050

************************************************************************/

#include "emu.h"
#include "p2000t.h"

#include "screen.h"
#include "speaker.h"


/* port i/o functions */
void p2000t_state::p2000t_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x0f).r(FUNC(p2000t_state::p2000t_port_000f_r));
	map(0x10, 0x1f).w(FUNC(p2000t_state::p2000t_port_101f_w));
	map(0x20, 0x2f).r(FUNC(p2000t_state::p2000t_port_202f_r));
	map(0x30, 0x3f).w(FUNC(p2000t_state::p2000t_port_303f_w));
	map(0x50, 0x5f).w(FUNC(p2000t_state::p2000t_port_505f_w));
	map(0x70, 0x7f).w(FUNC(p2000t_state::p2000t_port_707f_w));
	map(0x88, 0x8b).w(FUNC(p2000t_state::p2000t_port_888b_w));
	map(0x8c, 0x90).w(FUNC(p2000t_state::p2000t_port_8c90_w));
	map(0x94, 0x94).w(FUNC(p2000t_state::p2000t_port_9494_w));
}

/* Memory w/r functions */
void p2000t_state::p2000t_mem(address_map &map)
{
	map(0x0000, 0x0fff).rom();
	map(0x1000, 0x4fff).rom();
	map(0x5000, 0x57ff).ram().share("videoram");
	map(0x5800, 0xdfff).ram();
	map(0xe000, 0xffff).bankrw(m_bank);
}

void p2000m_state::p2000m_mem(address_map &map)
{
	map(0x0000, 0x0fff).rom();
	map(0x1000, 0x4fff).rom();
	map(0x5000, 0x5fff).ram().share("videoram");
	map(0x6000, 0xdfff).ram();
	map(0xe000, 0xffff).bankrw(m_bank);
}

/* graphics output */

static const gfx_layout p2000m_charlayout =
{
	6, 10,
	256,
	1,
	{ 0 },
	{ 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8,
		5*8, 6*8, 7*8, 8*8, 9*8 },
	8 * 10
};

void p2000m_state::p2000m_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t::white()); // white
	palette.set_pen_color(1, rgb_t::black()); // black
	palette.set_pen_color(2, rgb_t::black()); // black
	palette.set_pen_color(3, rgb_t::white()); // white
}

static GFXDECODE_START( gfx_p2000m )
	GFXDECODE_ENTRY( "gfx1", 0x0000, p2000m_charlayout, 0, 2 )
GFXDECODE_END

/* Keyboard input */

/* 2008-05 FP:
TO DO: verify position of the following keys: '1/4 3/4', '-> <-', '@ up', 'Clrln'
Also, notice that pictures of p2000 units shows slightly different key mappings, suggesting
many different .chr roms could exist

Small note about natural keyboard support: currently,
- "Code" is mapped to 'F1'
- "Clrln" is mapped to 'F2'
*/

static INPUT_PORTS_START (p2000t)
	PORT_START("KEY.0")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)        PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)           PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)          PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)           PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)           PORT_CHAR('3') PORT_CHAR(0xA3)
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)           PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)           PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)           PORT_CHAR('4') PORT_CHAR('$')

	PORT_START("KEY.1")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)         PORT_CHAR('\t')
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)           PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)           PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)           PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)           PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)           PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)           PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)           PORT_CHAR('f') PORT_CHAR('F')

	PORT_START("KEY.2")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD)   PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD))
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)       PORT_CHAR(' ')
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD)     PORT_CHAR(UCHAR_MAMEKEY(00_PAD))
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)       PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("#  \xE2\x96\xAA") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('#')
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)        PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)       PORT_CHAR(',')
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)       PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

	PORT_START("KEY.3")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift Lock")        PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)           PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)       PORT_CHAR('<') PORT_CHAR('>')
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)           PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)           PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)           PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)           PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)           PORT_CHAR('v') PORT_CHAR('V')

	PORT_START("KEY.4")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Code") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)           PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)           PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)           PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)           PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)           PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)           PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)           PORT_CHAR('r') PORT_CHAR('R')

	PORT_START("KEY.5")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Clrln")             PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)           PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK)    PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD)) PORT_CHAR(UCHAR_MAMEKEY(ASTERISK))
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD)   PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD))
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)   PORT_CHAR(8)
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)           PORT_CHAR('0') PORT_CHAR('=')
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)           PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)       PORT_CHAR('-') PORT_CHAR(0xFF0D)

	PORT_START("KEY.6")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)       PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)           PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)       PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)       PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)       PORT_CHAR(13)
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)           PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)           PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@  \xE2\x86\x91")   PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@') PORT_CHAR('^')

	PORT_START("KEY.7")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)       PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)        PORT_CHAR('.')
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)       PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)       PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\xE2\x86\x92  \xE2\x86\x90") PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)       PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)           PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)           PORT_CHAR('2') PORT_CHAR('"')

	PORT_START("KEY.8")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)       PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)           PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)       PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)       PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)      PORT_CHAR(0x00BC) PORT_CHAR(0x00BE)
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)       PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)           PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)       PORT_CHAR(':') PORT_CHAR('*')

	PORT_START("KEY.9")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift (Left)") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N/C")
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N/C")
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N/C")
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N/C")
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N/C")
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N/C")
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift (Right)") PORT_CODE(KEYCODE_RSHIFT)
INPUT_PORTS_END


INTERRUPT_GEN_MEMBER(p2000t_state::p2000_interrupt)
{
	if (BIT(m_port_101f, 6))
		m_maincpu->set_input_line(0, HOLD_LINE);
}

uint8_t p2000t_state::videoram_r(offs_t offset)
{
	return m_videoram[offset];
}

/* Machine definition */
void p2000t_state::p2000t(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 2500000);
	m_maincpu->set_addrmap(AS_PROGRAM, &p2000t_state::p2000t_mem);
	m_maincpu->set_addrmap(AS_IO, &p2000t_state::p2000t_io);
	m_maincpu->set_vblank_int("screen", FUNC(p2000t_state::p2000_interrupt));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.set_size(40 * 12, 24 * 20);
	screen.set_visarea(0, 40 * 12 - 1, 0, 24 * 20 - 1);
	screen.set_screen_update("saa5050", FUNC(saa5050_device::screen_update));

	saa5050_device &saa5050(SAA5050(config, "saa5050", 6000000));
	saa5050.d_cb().set(FUNC(p2000t_state::videoram_r));
	saa5050.set_screen_size(40, 24, 80);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	/* the mini cassette driver */
	MDCR(config, m_mdcr, 0);

	/* internal ram */
	RAM(config, m_ram).set_default_size("16K").set_extra_options("16K,32K,48K,64K,80K,102K");
}


/* Machine definition */
void p2000m_state::p2000m(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 2500000);
	m_maincpu->set_addrmap(AS_PROGRAM, &p2000m_state::p2000m_mem);
	m_maincpu->set_addrmap(AS_IO, &p2000m_state::p2000t_io);
	m_maincpu->set_vblank_int("screen", FUNC(p2000m_state::p2000_interrupt));
	config.set_maximum_quantum(attotime::from_hz(60));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(80 * 12, 24 * 20);
	screen.set_visarea(0, 80 * 12 - 1, 0, 24 * 20 - 1);
	screen.set_screen_update(FUNC(p2000m_state::screen_update_p2000m));
	screen.set_palette(m_palette);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_p2000m);
	PALETTE(config, m_palette, FUNC(p2000m_state::p2000m_palette), 4);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	/* the mini cassette driver */
	MDCR(config, m_mdcr, 0);
	/* internal ram */
	RAM(config, m_ram).set_default_size("16K").set_extra_options("16K,32K,48K,64K,80K,102K");
}


ROM_START(p2000t)
	ROM_REGION(0x10000, "maincpu",0)
	ROM_LOAD("p2000.rom", 0x0000, 0x1000, CRC(650784a3) SHA1(4dbb28adad30587f2ea536ba116898d459faccac))
	ROM_LOAD("basic.rom", 0x1000, 0x4000, CRC(9d9d38f9) SHA1(fb5100436c99634a2592a10dff867f85bcff7aec))
ROM_END

ROM_START(p2000m)
	ROM_REGION(0x10000, "maincpu",0)
	ROM_LOAD("p2000.rom", 0x0000, 0x1000, CRC(650784a3) SHA1(4dbb28adad30587f2ea536ba116898d459faccac))
	ROM_LOAD("basic.rom", 0x1000, 0x4000, CRC(9d9d38f9) SHA1(fb5100436c99634a2592a10dff867f85bcff7aec))

	ROM_REGION(0x01000, "gfx1",0)
	ROM_LOAD("p2000.chr", 0x0140, 0x08c0, BAD_DUMP CRC(78c17e3e) SHA1(4e1c59dc484505de1dc0b1ba7e5f70a54b0d4ccc))
ROM_END

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY    FULLNAME          FLAGS
COMP( 1980, p2000t, 0,      0,      p2000t,  p2000t, p2000t_state, empty_init, "Philips", "Philips P2000T", 0 )
COMP( 1980, p2000m, p2000t, 0,      p2000m,  p2000t, p2000m_state, empty_init, "Philips", "Philips P2000M", 0 )



p8k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Fabio Priuli
/***************************************************************************

    P8000

    09/2009 Skeleton driver based on Matt Knoth's emulator.
    P8000emu (http://knothusa.net/Home.php) has been a great source of info.
    Other info from
      * http://www.pofo.de/P8000/notes/books/
      * http://www.pofo.de/P8000/

    P8000 memory layout
      * divided into 3 banks of 64k
      * bank A is for roms, only 0000-1FFF is populated
      * bank B is for static ram, only 2000-2FFF exists
      * bank C is for dynamic ram, all 64k is available.
      * selection is done with OUT(c), code
      * code = 0 for do nothing; 1 = bank A; 2 = bank B; 4 = bank C.
      * Reg C = 0; Reg B = start address of memory that is being switched,
        for example B = 20 indicates "bank2" in memory map, and also the
        corresponding address in bank A/B/C.

    P8000 monitor commands
      * B : ?
      * D : display and modify memory
      * F : fill memory
      * G : go to
      * M : move (copy) memory
      * N : dump registers
      * O : boot from floppy
      * P : ?
      * Q : ?
      * R : dump registers
      * S : boot from floppy
      * T : jump to ROM at CEF0
      * X : jump to ROM at DB00
      * return : boot from floppy disk

    P8000_16 : All input must be in uppercase.

    TODO:
      * properly implement Z80 daisy chain in 16 bit board
      * Find out how to enter hardware check on 16 bit board

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/z80daisy.h"
#include "cpu/z8000/z8000.h"
#include "imagedev/floppy.h"
#include "machine/upd765.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "machine/z80dma.h"
#include "machine/z80pio.h"
#include "sound/beep.h"
#include "speaker.h"
#include "machine/clock.h"
#include "bus/rs232/rs232.h"

class p8k_16_daisy_device : public device_t, public z80_daisy_chain_interface
{
public:
	p8k_16_daisy_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
	uint16_t viack_r() {
		device_z80daisy_interface *intf = daisy_get_irq_device();
		return intf ? intf->z80daisy_irq_ack() : 0;
	}
	void reti_w(uint8_t data) { if(data == 0x4d) daisy_call_reti_device(); }
protected:
	void device_start() override {}
};

DEFINE_DEVICE_TYPE(P8K_16_DAISY, p8k_16_daisy_device, "p8k_16_daisy", "P8000 16-bit daisy chain device")

p8k_16_daisy_device::p8k_16_daisy_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, P8K_16_DAISY, tag, owner, clock)
	, z80_daisy_chain_interface(mconfig, *this) {}


namespace {

class p8k_state : public driver_device
{
public:
	p8k_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_daisy(*this, "p8k_16_daisy")
		, m_pio2(*this, "pio2")
		, m_i8272(*this, "i8272")
	{ }

	void p8k(machine_config &config);
	void p8k_16(machine_config &config);

	void init_p8k();

private:
	uint8_t port0_r(offs_t offset);
	void port0_w(offs_t offset, uint8_t data);
	DECLARE_MACHINE_RESET(p8k);

	[[maybe_unused]] void fdc_irq(int state);
	void p8k_daisy_interrupt(int state);
	void p8k_dma_irq_w(int state);
	void p8k_16_daisy_interrupt(int state);
	uint8_t memory_read_byte(offs_t offset);
	void memory_write_byte(offs_t offset, uint8_t data);
	uint8_t io_read_byte(offs_t offset);
	void io_write_byte(offs_t offset, uint8_t data);

	void p8k_16_datamap(address_map &map) ATTR_COLD;
	void p8k_16_iomap(address_map &map) ATTR_COLD;
	void p8k_16_memmap(address_map &map) ATTR_COLD;
	void p8k_iomap(address_map &map) ATTR_COLD;
	void p8k_memmap(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu; // Z80 or Z8001 depending on the machine
	optional_device<p8k_16_daisy_device> m_daisy;
	optional_device<z80pio_device> m_pio2;
	optional_device<i8272a_device> m_i8272;
};

/***************************************************************************

    P8000 8bit

****************************************************************************/

void p8k_state::p8k_memmap(address_map &map)
{
	map(0x0000, 0x0FFF).bankrw("bank0");
	map(0x1000, 0x1FFF).bankrw("bank1");
	map(0x2000, 0x2FFF).bankrw("bank2");
	map(0x3000, 0x3FFF).bankrw("bank3");
	map(0x4000, 0x4FFF).bankrw("bank4");
	map(0x5000, 0x5FFF).bankrw("bank5");
	map(0x6000, 0x6FFF).bankrw("bank6");
	map(0x7000, 0x7FFF).bankrw("bank7");
	map(0x8000, 0x8FFF).bankrw("bank8");
	map(0x9000, 0x9FFF).bankrw("bank9");
	map(0xA000, 0xAFFF).bankrw("bank10");
	map(0xB000, 0xBFFF).bankrw("bank11");
	map(0xC000, 0xCFFF).bankrw("bank12");
	map(0xD000, 0xDFFF).bankrw("bank13");
	map(0xE000, 0xEFFF).bankrw("bank14");
	map(0xF000, 0xFFFF).bankrw("bank15");
}

void p8k_state::p8k_iomap(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x07).rw(FUNC(p8k_state::port0_r), FUNC(p8k_state::port0_w)); // MH7489
	map(0x08, 0x0b).rw("ctc0", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x0c, 0x0f).rw("pio0", FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x18, 0x1b).rw("pio1", FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x1c, 0x1f).rw(m_pio2, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x20, 0x21).m(m_i8272, FUNC(i8272a_device::map));
	map(0x24, 0x27).rw("sio", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x28, 0x2b).rw("sio1", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x2c, 0x2f).rw("ctc1", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x3c, 0x3c).rw("dma", FUNC(z80dma_device::read), FUNC(z80dma_device::write));
}



uint8_t p8k_state::port0_r(offs_t offset)
{
	return 0;
}

// see memory explanation above
void p8k_state::port0_w(offs_t offset, uint8_t data)
{
	uint8_t breg = m_maincpu->state_int(Z80_B) >> 4;
	if ((data==1) || (data==2) || (data==4))
	{
		char banknum[8];
		sprintf(banknum,"bank%d", breg);

		offset = 0;
		if (data==2)
			offset = 16;
		else
		if (data==4)
			offset = 32;

		offset += breg;

		membank(banknum)->set_entry(offset);
	}
	else
	if (data)
		printf("Invalid data %X for bank %d\n",data,breg);
}


/***************************************************************************

    P8000 8bit Peripherals

****************************************************************************/

void p8k_state::p8k_daisy_interrupt(int state)
{
	m_maincpu->set_input_line(0, state);
}

/* Z80 DMA */

void p8k_state::p8k_dma_irq_w(int state)
{
	m_i8272->tc_w(state);
	p8k_daisy_interrupt(state);
}

uint8_t p8k_state::memory_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	return prog_space.read_byte(offset);
}

void p8k_state::memory_write_byte(offs_t offset, uint8_t data)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	prog_space.write_byte(offset, data);
}

uint8_t p8k_state::io_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_IO);
	return prog_space.read_byte(offset);
}

void p8k_state::io_write_byte(offs_t offset, uint8_t data)
{
	address_space& prog_space = m_maincpu->space(AS_IO);
	prog_space.write_byte(offset, data);
}


/* Z80 Daisy Chain */

static const z80_daisy_config p8k_daisy_chain[] =
{
	{ "dma" },   /* FDC related */
	{ "pio2" },
	{ "ctc0" },
	{ "sio" },
	{ "sio1" },
	{ "pio0" },
	{ "pio1" },
	{ "ctc1" },
	{ nullptr }
};

/* Intel 8272 Interface */

void p8k_state::fdc_irq(int state)
{
	m_pio2->port_b_write(state ? 0x10 : 0x00);
}

static void p8k_floppies(device_slot_interface &device)
{
	device.option_add("525hd", FLOPPY_525_HD);
}

/* Input ports */
static INPUT_PORTS_START( p8k )
	PORT_START("DSW")
	PORT_BIT( 0x7f, 0x7f, IPT_UNUSED )
	PORT_DIPNAME( 0x80, 0x00, "Hardware Test")
	PORT_DIPSETTING(    0x00, DEF_STR(Off))
	PORT_DIPSETTING(    0x80, DEF_STR(On))
INPUT_PORTS_END


MACHINE_RESET_MEMBER(p8k_state,p8k)
{
	membank("bank0")->set_entry(0);
	membank("bank1")->set_entry(0);
	membank("bank2")->set_entry(0);
	membank("bank3")->set_entry(0);
	membank("bank4")->set_entry(0);
	membank("bank5")->set_entry(0);
	membank("bank6")->set_entry(0);
	membank("bank7")->set_entry(0);
	membank("bank8")->set_entry(0);
	membank("bank9")->set_entry(0);
	membank("bank10")->set_entry(0);
	membank("bank11")->set_entry(0);
	membank("bank12")->set_entry(0);
	membank("bank13")->set_entry(0);
	membank("bank14")->set_entry(0);
	membank("bank15")->set_entry(0);
}

void p8k_state::init_p8k()
{
	uint8_t *RAM = memregion("maincpu")->base();
	membank("bank0")->configure_entries(0, 48, &RAM[0x0000], 0x1000);
	membank("bank1")->configure_entries(0, 48, &RAM[0x0000], 0x1000);
	membank("bank2")->configure_entries(0, 48, &RAM[0x0000], 0x1000);
	membank("bank3")->configure_entries(0, 48, &RAM[0x0000], 0x1000);
	membank("bank4")->configure_entries(0, 48, &RAM[0x0000], 0x1000);
	membank("bank5")->configure_entries(0, 48, &RAM[0x0000], 0x1000);
	membank("bank6")->configure_entries(0, 48, &RAM[0x0000], 0x1000);
	membank("bank7")->configure_entries(0, 48, &RAM[0x0000], 0x1000);
	membank("bank8")->configure_entries(0, 48, &RAM[0x0000], 0x1000);
	membank("bank9")->configure_entries(0, 48, &RAM[0x0000], 0x1000);
	membank("bank10")->configure_entries(0, 48, &RAM[0x0000], 0x1000);
	membank("bank11")->configure_entries(0, 48, &RAM[0x0000], 0x1000);
	membank("bank12")->configure_entries(0, 48, &RAM[0x0000], 0x1000);
	membank("bank13")->configure_entries(0, 48, &RAM[0x0000], 0x1000);
	membank("bank14")->configure_entries(0, 48, &RAM[0x0000], 0x1000);
	membank("bank15")->configure_entries(0, 48, &RAM[0x0000], 0x1000);
}


/***************************************************************************

    P8000 16bit

****************************************************************************/

void p8k_state::p8k_16_memmap(address_map &map)
{
	map(0x00000, 0x03fff).rom().region("maincpu",0);
	map(0x04000, 0x07fff).ram().share("share1");
	map(0x08000, 0xfffff).ram().share("share2");
}

void p8k_state::p8k_16_datamap(address_map &map)
{
	map(0x00000, 0x03fff).rom().region("maincpu",0);
	map(0x04000, 0x07fff).ram().share("share1");
	map(0x08000, 0xfffff).ram().share("share2");
}


void p8k_state::p8k_16_iomap(address_map &map)
{
//  map(0x0fef0, 0x0feff) // clock
	map(0x0ff80, 0x0ff87).rw("sio", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w)).umask16(0x00ff);
	map(0x0ff88, 0x0ff8f).rw("sio1", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w)).umask16(0x00ff);
	map(0x0ff90, 0x0ff97).rw("pio0", FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt)).umask16(0x00ff);
	map(0x0ff98, 0x0ff9f).rw("pio1", FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt)).umask16(0x00ff);
	map(0x0ffa0, 0x0ffa7).rw(m_pio2, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt)).umask16(0x00ff);
	map(0x0ffa8, 0x0ffaf).rw("ctc0", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write)).umask16(0x00ff);
	map(0x0ffb0, 0x0ffb7).rw("ctc1", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write)).umask16(0x00ff);
//  map(0x0ffc0, 0x0ffc1) // SCR
//  map(0x0ffc8, 0x0ffc9) // SBR
//  map(0x0ffd0, 0x0ffd1) // NBR
//  map(0x0ffd8, 0x0ffd9) // SNVR
	map(0x0ffe1, 0x0ffe1).w(m_daisy, FUNC(p8k_16_daisy_device::reti_w));
//  map(0x0fff0, 0x0fff1) // TRPL
//  map(0x0fff8, 0x0fff9) // IF1L
}


/***************************************************************************

    P8000 16bit Peripherals

****************************************************************************/

void p8k_state::p8k_16_daisy_interrupt(int state)
{
	m_maincpu->set_input_line(z8001_device::VI_LINE, state ? ASSERT_LINE : CLEAR_LINE);
}


/* Z80 Daisy Chain */

static const z80_daisy_config p8k_16_daisy_chain[] =
{
	{ "ctc0" },
	{ "ctc1" },
	{ "sio" },
	{ "sio1" },
	{ "pio0" },
	{ "pio1" },
	{ "pio2" },
	{ nullptr }
};



#if 0
/* F4 Character Displayer */
static const gfx_layout p8k_charlayout =
{
	8, 12,                  /* 8 x 12 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( p8k )
	GFXDECODE_ENTRY( "chargen", 0x0000, p8k_charlayout, 0, 1 )
GFXDECODE_END
#endif


/***************************************************************************

    Machine Drivers

****************************************************************************/

void p8k_state::p8k(machine_config &config)
{
	/* basic machine hardware */
	z80_device& maincpu(Z80(config, "maincpu", 16_MHz_XTAL / 4));
	maincpu.set_daisy_config(p8k_daisy_chain);
	maincpu.set_addrmap(AS_PROGRAM, &p8k_state::p8k_memmap);
	maincpu.set_addrmap(AS_IO, &p8k_state::p8k_iomap);

	MCFG_MACHINE_RESET_OVERRIDE(p8k_state,p8k)

	/* peripheral hardware */
	z80dma_device& dma(Z80DMA(config, "dma", 16_MHz_XTAL / 4));
	dma.out_busreq_callback().set(FUNC(p8k_state::p8k_dma_irq_w));
	dma.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	dma.in_mreq_callback().set(FUNC(p8k_state::memory_read_byte));
	dma.out_mreq_callback().set(FUNC(p8k_state::memory_write_byte));
	dma.in_iorq_callback().set(FUNC(p8k_state::io_read_byte));
	dma.out_iorq_callback().set(FUNC(p8k_state::io_write_byte));

	clock_device &uart_clock(CLOCK(config, "uart_clock", 307200));
	uart_clock.signal_handler().set("sio", FUNC(z80sio_device::txcb_w));
	uart_clock.signal_handler().append("sio", FUNC(z80sio_device::rxcb_w));

	z80ctc_device& ctc0(Z80CTC(config, "ctc0", 1229000));    /* 1.22MHz clock */
	// to implement: callbacks!
	// manual states the callbacks should go to
	// Baud Gen 3, FDC, System-Kanal
	ctc0.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80ctc_device& ctc1(Z80CTC(config, "ctc1", 1229000));    /* 1.22MHz clock */
	// to implement: callbacks!
	// manual states the callbacks should go to
	// Baud Gen 0, Baud Gen 1, Baud Gen 2,
	ctc1.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80sio_device& sio(Z80SIO(config, "sio", 16_MHz_XTAL / 4));
	sio.out_txdb_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	sio.out_dtrb_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	sio.out_rtsb_callback().set("rs232", FUNC(rs232_port_device::write_rts));
	sio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("sio", FUNC(z80sio_device::rxb_w));
	rs232.cts_handler().set("sio", FUNC(z80sio_device::ctsb_w));

	z80sio_device& sio1(Z80SIO(config, "sio1", 16_MHz_XTAL / 4));
	sio1.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80pio_device& pio0(Z80PIO(config, "pio0", 1229000));
	pio0.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80pio_device& pio1(Z80PIO(config, "pio1", 1229000));
	pio1.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80PIO(config, m_pio2, 1229000);
	m_pio2->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio2->in_pa_callback().set_ioport("DSW");

	I8272A(config, m_i8272, 16_MHz_XTAL / 2, true);
	m_i8272->drq_wr_callback().set("dma", FUNC(z80dma_device::rdy_w));
	FLOPPY_CONNECTOR(config, "i8272:0", p8k_floppies, "525hd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "i8272:1", p8k_floppies, "525hd", floppy_image_device::default_mfm_floppy_formats);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	BEEP(config, "beeper", 3250).add_route(ALL_OUTPUTS, "mono", 0.5);
}

void p8k_state::p8k_16(machine_config &config)
{
	/* basic machine hardware */
	z8001_device &maincpu(Z8001(config, m_maincpu, XTAL(4'000'000)));
	maincpu.set_addrmap(AS_PROGRAM, &p8k_state::p8k_16_memmap);
	maincpu.set_addrmap(AS_DATA, &p8k_state::p8k_16_datamap);
	maincpu.set_addrmap(AS_IO, &p8k_state::p8k_16_iomap);
	maincpu.viack().set("p8k_16_daisy", FUNC(p8k_16_daisy_device::viack_r));

	P8K_16_DAISY(config, m_daisy, 0);
	m_daisy->set_daisy_config(p8k_16_daisy_chain);

	clock_device &uart_clock(CLOCK(config, "uart_clock", 307200));
	uart_clock.signal_handler().set("sio", FUNC(z80sio_device::txcb_w));
	uart_clock.signal_handler().append("sio", FUNC(z80sio_device::rxcb_w));

	/* peripheral hardware */
	z80ctc_device& ctc0(Z80CTC(config, "ctc0", XTAL(4'000'000)));
	ctc0.intr_callback().set(FUNC(p8k_state::p8k_16_daisy_interrupt));

	z80ctc_device& ctc1(Z80CTC(config, "ctc1", XTAL(4'000'000)));
	ctc1.intr_callback().set(FUNC(p8k_state::p8k_16_daisy_interrupt));

	z80sio_device& sio(Z80SIO(config, "sio", XTAL(4'000'000)));
	sio.out_txdb_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	sio.out_dtrb_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	sio.out_rtsb_callback().set("rs232", FUNC(rs232_port_device::write_rts));
	sio.out_int_callback().set(FUNC(p8k_state::p8k_16_daisy_interrupt));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("sio", FUNC(z80sio_device::rxb_w));
	rs232.cts_handler().set("sio", FUNC(z80sio_device::ctsb_w));

	z80sio_device& sio1(Z80SIO(config, "sio1", XTAL(4'000'000)));
	sio1.out_int_callback().set(FUNC(p8k_state::p8k_16_daisy_interrupt));

	z80pio_device& pio0(Z80PIO(config, "pio0", XTAL(4'000'000)));
	pio0.out_int_callback().set(FUNC(p8k_state::p8k_16_daisy_interrupt));

	z80pio_device& pio1(Z80PIO(config, "pio1", XTAL(4'000'000)));
	pio1.out_int_callback().set(FUNC(p8k_state::p8k_16_daisy_interrupt));

	Z80PIO(config, m_pio2, XTAL(4'000'000));
	m_pio2->out_int_callback().set(FUNC(p8k_state::p8k_16_daisy_interrupt));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, "beeper", 3250).add_route(ALL_OUTPUTS, "mono", 0.5);
}

/* ROM definition */
ROM_START( p8000 )
	ROM_REGION( 0x30000, "maincpu", 0 )
	ROM_LOAD("mon8_1_3.1",  0x0000, 0x1000, CRC(ad1bb118) SHA1(2332963acd74d5d1a009d9bce8a2b108de01d2a5))
	ROM_LOAD("mon8_2_3.1",  0x1000, 0x1000, CRC(daced7c2) SHA1(f1f778e72568961b448020fc543ed6e81bbe81b1))

	// this is for the p8000's terminal, not the p8000 itself
	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD("p8t_zs",    0x0000, 0x0800, CRC(f9321251) SHA1(a6a796b58d50ec4a416f2accc34bd76bc83f18ea))
	ROM_LOAD("p8tdzs.2",  0x0800, 0x0800, CRC(32736503) SHA1(6a1d7c55dddc64a7d601dfdbf917ce1afaefbb0a))

	ROM_REGION( 0x2000, "user1", 0 )
	ROM_LOAD( "wdc4.2_1-2c43.bin", 0x0000, 0x1000, CRC(2646f1ee) SHA1(f62574ad57a2c8ac55c5df89256a707c0cafc0eb) )
	ROM_LOAD( "wdc4.2_2-5d66.bin", 0x1000, 0x1000, CRC(5d496b65) SHA1(42166d7ec51fce086c65ea829d8a3d63088815ca) )
ROM_END

ROM_START( p8000_16 )
	ROM_REGION16_BE( 0x4000, "maincpu", 0 )
	ROM_LOAD16_BYTE("mon16_1h_3.1_udos",   0x0000, 0x1000, CRC(0c3c28da) SHA1(0cd35444c615b404ebb9cf80da788593e573ddb5))
	ROM_LOAD16_BYTE("mon16_1l_3.1_udos",   0x0001, 0x1000, CRC(e8857bdc) SHA1(f89c65cbc479101130c71806fd3ddc28e6383f12))
	ROM_LOAD16_BYTE("mon16_2h_3.1_udos",   0x2000, 0x1000, CRC(cddf58d5) SHA1(588bad8df75b99580459c7a8e898a3396907e3a4))
	ROM_LOAD16_BYTE("mon16_2l_3.1_udos",   0x2001, 0x1000, CRC(395ee7aa) SHA1(d72fadb1608cd0915cd5ce6440897303ac5a12a6))

	// this is for the p8000's terminal, not the p8000 itself
	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD("p8t_zs",    0x0000, 0x0800, CRC(f9321251) SHA1(a6a796b58d50ec4a416f2accc34bd76bc83f18ea))
	ROM_LOAD("p8tdzs.2",  0x0800, 0x0800, CRC(32736503) SHA1(6a1d7c55dddc64a7d601dfdbf917ce1afaefbb0a))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY                   FULLNAME               FLAGS
COMP( 1989, p8000,    0,      0,      p8k,     p8k,   p8k_state, init_p8k,   "EAW electronic Treptow", "P8000 (8bit Board)",  MACHINE_NOT_WORKING)
COMP( 1989, p8000_16, p8000,  0,      p8k_16,  p8k,   p8k_state, empty_init, "EAW electronic Treptow", "P8000 (16bit Board)", MACHINE_NOT_WORKING)



palestra.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev

/***************************************************************************

    Palestra-02 (a Pong clone)

    https://www.mobygames.com/game/dedicated-console/palestra-02
        database entry
    http://www.ccjvq.com/slydc/palestra02.rar
        schematics
    http://discreteconsoles.blogspot.com/2015/10/emulation-of-ay-3-8500-1-and-clones-soon.html
        photos
    https://www.youtube.com/watch?v=3XZxkTvOF4Y
        gameplay video

    To do:
    - write 74H53 device
    - trace the boards (schematic contains several errors)
    - hook up inputs

***************************************************************************/

#include "emu.h"

#include "machine/netlist.h"
#include "netlist/devices/net_lib.h"

#include "nl_palestra.h"
#include "video/fixfreq.h"

#include "screen.h"

#include <cmath>


namespace {

#define MASTER_CLOCK    (4000000)
#define V_TOTAL_PONG    315
#define H_TOTAL_PONG    256     // tbc

class palestra_state : public driver_device
{
public:
	palestra_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_video(*this, "fixfreq")
	{ }

	// devices
	required_device<netlist_mame_device> m_maincpu;
	required_device<fixedfreq_device> m_video;

	void palestra(machine_config &config);

protected:
	// driver_device overrides
	virtual void machine_start() override { };
	virtual void machine_reset() override { };

private:
};


static INPUT_PORTS_START(palestra)
INPUT_PORTS_END


void palestra_state::palestra(machine_config &config)
{
	NETLIST_CPU(config, m_maincpu, netlist::config::DEFAULT_CLOCK())
		.set_source(netlist_palestra);

	NETLIST_ANALOG_OUTPUT(config, "maincpu:vid0").set_params("videomix", m_video, FUNC(fixedfreq_device::update_composite_monochrome));

	SCREEN(config, "screen", SCREEN_TYPE_RASTER);
	FIXFREQ(config, m_video).set_screen("screen");
	m_video->set_monitor_clock(MASTER_CLOCK);
	m_video->set_horz_params(H_TOTAL_PONG-64,H_TOTAL_PONG-40,H_TOTAL_PONG-8,H_TOTAL_PONG);
	m_video->set_vert_params(V_TOTAL_PONG-19,V_TOTAL_PONG-16,V_TOTAL_PONG-12,V_TOTAL_PONG);
	m_video->set_fieldcount(1);
	m_video->set_threshold(1);
	m_video->set_gain(0.36);
}


ROM_START( palestra ) /* dummy to satisfy game entry*/
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
ROM_END

} // anonymous namespace


CONS(  1978, palestra, 0,   0, palestra,   palestra,    palestra_state,   empty_init, "LPO", "Palestra-02", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



palm.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, R. Belmont
/****************************************************************************

    drivers/palm.c
    Palm (MC68328) emulation

    Driver by Ryan Holtz

    Additional bug fixing by R. Belmont

****************************************************************************/

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "machine/mc68328.h"
#include "machine/ram.h"
#include "sound/dac.h"
#include "video/mc68328lcd.h"
#include "video/sed1375.h"

#include "screen.h"
#include "speaker.h"

#include "pilot1k.lh"

#define VERBOSE (0)
#include "logmacro.h"

namespace {

class palm_base_state : public driver_device
{
public:
	DECLARE_INPUT_CHANGED_MEMBER(pen_check);

protected:
	palm_base_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_screen(*this, "screen")
		, m_io_penx(*this, "PENX")
		, m_io_peny(*this, "PENY")
		, m_io_penb(*this, "PENB")
	{ }

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	offs_t dasm_override(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const util::disasm_interface::data_buffer &params);

	virtual int spi_from_hw();

	required_device<mc68328_base_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<screen_device> m_screen;
	required_ioport m_io_penx;
	required_ioport m_io_peny;
	required_ioport m_io_penb;

	u16 m_spim_data;
};

class palm_state : public palm_base_state
{
public:
	palm_state(const machine_config &mconfig, device_type type, const char *tag)
		: palm_base_state(mconfig, type, tag)
		, m_lcdctrl(*this, "lcdctrl")
		, m_io_portd(*this, "PORTD")
	{ }

	void palmiii(machine_config &config);
	void pilot1k(machine_config &config);
	void palmvx(machine_config &config);
	void palmv(machine_config &config);
	void palmpro(machine_config &config);
	void pilot5k(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(button_check);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void palm_base(machine_config &config);

	void mem_map(address_map &map) ATTR_COLD;

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void check_pen_adc_read();
	void adc_vcc_y_w(int state);
	void adc_gnd_y_w(int state);
	void adc_vcc_x_w(int state);
	void adc_gnd_x_w(int state);
	void adc_csn_w(int state);
	int power_nmi_r();

	enum : int
	{
		PORTF_Y_VCCN_BIT        = 0,
		PORTF_Y_GND_BIT         = 1,
		PORTF_X_VCCN_BIT        = 2,
		PORTF_X_GND_BIT         = 3,
		PORTF_ADC_CSN_BIT       = 7
	};

	required_device<mc68328_lcd_device> m_lcdctrl;
	required_ioport m_io_portd;

	u8 m_port_f_latch;
	bool m_adc_csn;
	bool m_adc_vcc_x;
	bool m_adc_gnd_x;
	bool m_adc_vcc_y;
	bool m_adc_gnd_y;
};

class palmez_base_state : public palm_base_state
{
public:
	DECLARE_INPUT_CHANGED_MEMBER(button_check);

protected:
	palmez_base_state(const machine_config &mconfig, device_type type, const char *tag, const u8 hardware_id)
		: palm_base_state(mconfig, type, tag)
		, m_rows(*this, "ROW%u", 0u)
		, m_hardware_id(hardware_id)
	{ }

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void palmez_base(machine_config &config);

	void mem_map(address_map &map) ATTR_COLD;

	void adc_enable_w(int state);

	template <int Line> void kbd_row_w(int state);
	template <int Line> void kbd_col_w(int state);
	template <int Line> int kbd_scan_r();

	void hardware_id_req_w(int state);

	void spi_to_hw(int state);
	virtual int spi_from_hw() override;

	required_ioport_array<3> m_rows;

	u8 m_key_row_mask;
	u8 m_key_col_mask;

	bool m_hardware_id_asserted;
	const u8 m_hardware_id;

	bool m_adc_enabled;
	u8 m_adc_cmd_bit_count;
	u8 m_adc_response_bit_count;
	u8 m_adc_cmd;
};

class palmiiic_state : public palmez_base_state
{
public:
	palmiiic_state(const machine_config &mconfig, device_type type, const char *tag)
		: palmez_base_state(mconfig, type, tag, 0x09)
		, m_sed1375(*this, "lcdctrl")
	{ }

	void palmiiic(machine_config &config);

protected:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<sed1375_device> m_sed1375;
};

class palmm100_state : public palmez_base_state
{
public:
	palmm100_state(const machine_config &mconfig, device_type type, const char *tag)
		: palmez_base_state(mconfig, type, tag, 0x05)
		, m_lcdctrl(*this, "lcdctrl")
	{ }

	void palmm100(machine_config &config);

protected:
	template <int Line> int hardware_subid_r();

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	required_device<mc68328_lcd_device> m_lcdctrl;
};


/***************************************************************************
    MACHINE HARDWARE
***************************************************************************/

// Shared platform hardware

void palm_base_state::machine_start()
{
	save_item(NAME(m_spim_data));
}

void palm_base_state::machine_reset()
{
	// Copy boot ROM
	u8* bios = memregion("bios")->base();
	memset(m_ram->pointer(), 0, m_ram->size());
	memcpy(m_ram->pointer(), bios, 0x20000);

	m_spim_data = 0xffff;
}

INPUT_CHANGED_MEMBER(palm_base_state::pen_check)
{
	m_maincpu->irq5_w(m_io_penb->read() & 1);
}

int palm_base_state::spi_from_hw()
{
	int out_state = BIT(m_spim_data, 15);
	m_spim_data <<= 1;
	m_spim_data |= 1;
	return out_state;
}


// First-generation Palm hardware ("IDT")

void palm_state::machine_start()
{
	palm_base_state::machine_start();
	address_space &space = m_maincpu->space(AS_PROGRAM);
	space.install_ram(0x000000, m_ram->size() - 1, m_ram->pointer());

	//save_item(NAME(m_port_f_latch));
	save_item(NAME(m_adc_csn));
	save_item(NAME(m_adc_vcc_x));
	save_item(NAME(m_adc_gnd_x));
	save_item(NAME(m_adc_vcc_y));
	save_item(NAME(m_adc_gnd_y));
}

void palm_state::machine_reset()
{
	palm_base_state::machine_reset();

	m_adc_csn = false;
	m_adc_vcc_x = false;
	m_adc_gnd_x = false;
	m_adc_vcc_y = false;
	m_adc_gnd_y = false;
}

u32 palm_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	m_lcdctrl->video_update(bitmap, cliprect);
	return 0;
}

INPUT_CHANGED_MEMBER(palm_state::button_check)
{
	const u8 button_state = m_io_portd->read();
	m_maincpu->port_d_in_w(BIT(button_state, param), param);
}

void palm_state::check_pen_adc_read()
{
	if (!m_adc_csn)
	{
		if (m_adc_vcc_x && m_adc_gnd_y)
		{
			m_spim_data = (0xff - m_io_penx->read()) * 2;
		}
		else if (m_adc_vcc_y && m_adc_gnd_x)
		{
			m_spim_data = (0xff - m_io_peny->read()) * 2;
		}
	}
}

void palm_state::adc_vcc_y_w(int state)
{
	m_adc_vcc_y = state;
	check_pen_adc_read();
}

void palm_state::adc_gnd_y_w(int state)
{
	m_adc_gnd_y = state;
	check_pen_adc_read();
}

void palm_state::adc_vcc_x_w(int state)
{
	m_adc_vcc_x = state;
	check_pen_adc_read();
}

void palm_state::adc_gnd_x_w(int state)
{
	m_adc_gnd_x = state;
	check_pen_adc_read();
}

void palm_state::adc_csn_w(int state)
{
	m_adc_csn = state;
	check_pen_adc_read();
}

int palm_state::power_nmi_r()
{
	return 1;
}


// Basic 68EZ328-based Palm hardware

void palmez_base_state::machine_start()
{
	palm_base_state::machine_start();
	address_space &space = m_maincpu->space(AS_PROGRAM);
	space.install_ram(0x000000, m_ram->size() - 1, m_ram->pointer());

	save_item(NAME(m_key_row_mask));
	save_item(NAME(m_key_col_mask));

	save_item(NAME(m_hardware_id_asserted));

	save_item(NAME(m_adc_enabled));
	save_item(NAME(m_adc_cmd_bit_count));
	save_item(NAME(m_adc_response_bit_count));
	save_item(NAME(m_adc_cmd));
}

void palmez_base_state::machine_reset()
{
	palm_base_state::machine_reset();

	m_key_row_mask = 0;
	m_key_col_mask = 0;

	m_hardware_id_asserted = false;

	m_adc_enabled = false;
	m_adc_cmd_bit_count = 8;
	m_adc_response_bit_count = 0;
	m_adc_cmd = 0;
}

INPUT_CHANGED_MEMBER(palmez_base_state::button_check)
{
	const u8 button_state = m_rows[param]->read();
	for (int bit = 0; bit < 4; bit++)
	{
		m_maincpu->port_d_in_w(BIT(button_state, bit), bit);
	}
}

void palmez_base_state::adc_enable_w(int state)
{
	const bool was_enabled = m_adc_enabled;
	m_adc_enabled = !state;
	if (!was_enabled && m_adc_enabled)
	{
		m_adc_cmd_bit_count = 8;
		m_spim_data = 0;
	}
}

template <int Line>
void palmez_base_state::kbd_row_w(int state)
{
	m_key_row_mask &= ~(1 << Line);
	m_key_row_mask |= !state << Line;
}

template <int Line>
void palmez_base_state::kbd_col_w(int state)
{
	m_key_col_mask &= ~(1 << Line);
	m_key_col_mask |= state << Line;
}

template <int Line>
int palmez_base_state::kbd_scan_r()
{
	if (m_hardware_id_asserted)
	{
		return BIT(~m_hardware_id, Line);
	}

	int state = 0;
	for (int i = 0; i < 3; i++)
	{
		if (BIT(m_key_row_mask, i))
		{
			state |= BIT(m_rows[i]->read(), Line);
		}
	}
	return state;
}

void palmez_base_state::spi_to_hw(int state)
{
	if (m_adc_enabled && m_adc_cmd_bit_count > 0)
	{
		m_adc_cmd_bit_count--;
		m_adc_cmd &= ~(1 << m_adc_cmd_bit_count);
		m_adc_cmd |= state << m_adc_cmd_bit_count;
	}
}

int palmez_base_state::spi_from_hw()
{
	int state = palm_base_state::spi_from_hw();
	if (m_adc_enabled && m_adc_cmd_bit_count == 0)
	{
		if (m_adc_response_bit_count == 0)
		{
			m_adc_response_bit_count = 16;
			const u8 channel = (m_adc_cmd >> 4) & 7;
			switch (channel)
			{
			case 1: // Pen Y
				m_spim_data = ((0xff - m_io_peny->read()) * 2) << 3;
				break;
			case 2: // Battery Level
				m_spim_data = 0x7ff8;
				break;
			case 5: // Pen X
				m_spim_data = ((0xff - m_io_penx->read()) * 2) << 3;
				break;
			case 6: // Dock Serial
				m_spim_data = 0;
				break;
			default:
				LOG("%s: Unknown ADC Channel: %d (command %02x)\n", machine().describe_context(), channel, m_adc_cmd);
				break;
			}
		}
		else
		{
			m_adc_response_bit_count--;
			if (m_adc_response_bit_count == 0)
			{
				m_adc_cmd_bit_count = 8;
			}
		}
	}
	return state;
}

void palmez_base_state::hardware_id_req_w(int state)
{
	m_hardware_id_asserted = !state;
}


// Palm m100 ("Brad") hardware

u32 palmm100_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	m_lcdctrl->video_update(bitmap, cliprect);
	return 0;
}

template <int Line> int palmm100_state::hardware_subid_r()
{
	return BIT(~0x00, Line);
}


/***************************************************************************
    ADDRESS MAPS
***************************************************************************/

void palm_state::mem_map(address_map &map)
{
	map(0xc00000, 0xe07fff).rom().region("bios", 0);
}

void palmez_base_state::mem_map(address_map &map)
{
	map(0x00c00000, 0x00e07fff).rom().region("bios", 0);
	map(0x10c00000, 0x10e07fff).rom().region("bios", 0);
}

void palmiiic_state::mem_map(address_map &map)
{
	palmez_base_state::mem_map(map);
	map(0x1f000000, 0x1f01ffff).m(m_sed1375, FUNC(sed1375_device::map));
}


/***************************************************************************
    INPUTS
***************************************************************************/

static INPUT_PORTS_START(palm_base)
	PORT_START("PENX")
	PORT_BIT(0xff, 0x50, IPT_LIGHTGUN_X) PORT_NAME("Pen X") PORT_MINMAX(0, 0xa0) PORT_SENSITIVITY(50) PORT_CROSSHAIR(X, 1.0, 0.0, 0)

	PORT_START("PENY")
	PORT_BIT(0xff, 0x50, IPT_LIGHTGUN_Y) PORT_NAME("Pen Y") PORT_MINMAX(0, 0xa0) PORT_SENSITIVITY(50) PORT_CROSSHAIR(Y, 1.0, 0.0, 0)

	PORT_START("PENB")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Pen Button") PORT_CODE(MOUSECODE_BUTTON1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palm_base_state::pen_check), 0)
INPUT_PORTS_END

static INPUT_PORTS_START(palm)
	PORT_INCLUDE(palm_base)

	PORT_START("PORTD")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Power") PORT_CODE(KEYCODE_D)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palm_state::button_check), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_NAME("Up") PORT_CODE(KEYCODE_Y)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palm_state::button_check), 1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_BUTTON4) PORT_NAME("Down") PORT_CODE(KEYCODE_H)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palm_state::button_check), 2)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_BUTTON5) PORT_NAME("Button 1") PORT_CODE(KEYCODE_F)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palm_state::button_check), 3)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON6) PORT_NAME("Button 2") PORT_CODE(KEYCODE_G)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palm_state::button_check), 4)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_BUTTON7) PORT_NAME("Button 3") PORT_CODE(KEYCODE_J)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palm_state::button_check), 5)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_BUTTON8) PORT_NAME("Button 4") PORT_CODE(KEYCODE_K)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palm_state::button_check), 6)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START(palmiiic)
	PORT_INCLUDE(palm_base)

	PORT_START("ROW0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Button 1") PORT_CODE(KEYCODE_F)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmiiic_state::button_check), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_NAME("Button 2") PORT_CODE(KEYCODE_G)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmiiic_state::button_check), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_BUTTON4) PORT_NAME("Button 3") PORT_CODE(KEYCODE_J)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmiiic_state::button_check), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_BUTTON5) PORT_NAME("Button 4") PORT_CODE(KEYCODE_K)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmiiic_state::button_check), 0)
	PORT_BIT(0xf0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("ROW1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON6) PORT_NAME("Up") PORT_CODE(KEYCODE_Y)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmiiic_state::button_check), 1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON7) PORT_NAME("Down") PORT_CODE(KEYCODE_H)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmiiic_state::button_check), 1)
	PORT_BIT(0xfc, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("ROW2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON8) PORT_NAME("Power") PORT_CODE(KEYCODE_D)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmiiic_state::button_check), 2)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON9) PORT_NAME("Contrast") PORT_CODE(KEYCODE_C)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmiiic_state::button_check), 2)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_BUTTON3)
	PORT_BIT(0xf8, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START(palmm100)
	PORT_INCLUDE(palm_base)

	PORT_START("ROW0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Button 1") PORT_CODE(KEYCODE_F)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmm100_state::button_check), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_NAME("Button 2") PORT_CODE(KEYCODE_G)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmm100_state::button_check), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_BUTTON4) PORT_NAME("Button 3") PORT_CODE(KEYCODE_J)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmm100_state::button_check), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_BUTTON5) PORT_NAME("Button 4") PORT_CODE(KEYCODE_K)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmm100_state::button_check), 0)
	PORT_BIT(0xf0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("ROW1")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON6) PORT_NAME("Down") PORT_CODE(KEYCODE_H)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmm100_state::button_check), 1)
	PORT_BIT(0xfd, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("ROW2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON7) PORT_NAME("Power") PORT_CODE(KEYCODE_D)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmm100_state::button_check), 2)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON8) PORT_NAME("Up") PORT_CODE(KEYCODE_Y)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmm100_state::button_check), 2)
	PORT_BIT(0xfc, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END


/***************************************************************************
    MACHINE/DEVICE DRIVERS
***************************************************************************/

void palm_state::palm_base(machine_config &config)
{
	/* basic machine hardware */
	MC68328(config, m_maincpu, 32768*506);        /* 16.580608 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &palm_state::mem_map);
	m_maincpu->set_dasm_override(FUNC(palm_state::dasm_override));

	m_maincpu->out_port_f<PORTF_Y_VCCN_BIT>().set(FUNC(palm_state::adc_vcc_y_w));
	m_maincpu->out_port_f<PORTF_Y_GND_BIT>().set(FUNC(palm_state::adc_gnd_y_w));
	m_maincpu->out_port_f<PORTF_X_VCCN_BIT>().set(FUNC(palm_state::adc_vcc_x_w));
	m_maincpu->out_port_f<PORTF_X_GND_BIT>().set(FUNC(palm_state::adc_gnd_x_w));
	m_maincpu->out_port_f<PORTF_ADC_CSN_BIT>().set(FUNC(palm_state::adc_csn_w));

	m_maincpu->in_port_c<4>().set(FUNC(palm_state::power_nmi_r));

	m_maincpu->in_port_d<0>().set_ioport(m_io_penb).bit(0);
	m_maincpu->in_port_d<1>().set_ioport(m_io_penb).bit(1);
	m_maincpu->in_port_d<2>().set_ioport(m_io_penb).bit(2);
	m_maincpu->in_port_d<3>().set_ioport(m_io_penb).bit(3);
	m_maincpu->in_port_d<4>().set_ioport(m_io_penb).bit(4);
	m_maincpu->in_port_d<5>().set_ioport(m_io_penb).bit(5);
	m_maincpu->in_port_d<6>().set_ioport(m_io_penb).bit(6);
	m_maincpu->in_port_d<7>().set_ioport(m_io_penb).bit(7);

	m_maincpu->out_pwm().set("dac", FUNC(dac_bit_interface::write));
	m_maincpu->in_spim().set(FUNC(palm_state::spi_from_hw));

	m_maincpu->out_flm().set(m_lcdctrl, FUNC(mc68328_lcd_device::flm_w));
	m_maincpu->out_llp().set(m_lcdctrl, FUNC(mc68328_lcd_device::llp_w));
	m_maincpu->out_lsclk().set(m_lcdctrl, FUNC(mc68328_lcd_device::lsclk_w));
	m_maincpu->out_ld().set(m_lcdctrl, FUNC(mc68328_lcd_device::ld_w));
	m_maincpu->set_lcd_info_changed(m_lcdctrl, FUNC(mc68328_lcd_device::lcd_info_changed));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(160, 220);
	m_screen->set_visarea(0, 160 - 1, 0, 220 - 1);
	m_screen->set_screen_update(FUNC(palm_state::screen_update));

	MC68328_LCD(config, m_lcdctrl, 0);

	/* audio hardware */
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, "dac", 0).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void palmiiic_state::palmiiic(machine_config &config)
{
	/* basic machine hardware */
	MC68EZ328(config, m_maincpu, 32768*506);        /* 16.580608 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &palmiiic_state::mem_map);
	m_maincpu->set_dasm_override(FUNC(palmiiic_state::dasm_override));

	m_maincpu->out_port_b<1>().set(FUNC(palmiiic_state::adc_enable_w));

	m_maincpu->out_port_c<0>().set(FUNC(palmiiic_state::kbd_row_w<0>));
	m_maincpu->out_port_c<1>().set(FUNC(palmiiic_state::kbd_row_w<1>));
	m_maincpu->out_port_c<2>().set(FUNC(palmiiic_state::kbd_row_w<2>));

	m_maincpu->out_port_d<0>().set(FUNC(palmiiic_state::kbd_col_w<0>));
	m_maincpu->out_port_d<1>().set(FUNC(palmiiic_state::kbd_col_w<1>));
	m_maincpu->out_port_d<2>().set(FUNC(palmiiic_state::kbd_col_w<2>));
	m_maincpu->out_port_d<3>().set(FUNC(palmiiic_state::kbd_col_w<3>));

	m_maincpu->out_port_g<2>().set(FUNC(palmiiic_state::hardware_id_req_w));

	m_maincpu->in_port_d<0>().set(FUNC(palmiiic_state::kbd_scan_r<0>));
	m_maincpu->in_port_d<1>().set(FUNC(palmiiic_state::kbd_scan_r<1>));
	m_maincpu->in_port_d<2>().set(FUNC(palmiiic_state::kbd_scan_r<2>));
	m_maincpu->in_port_d<3>().set(FUNC(palmiiic_state::kbd_scan_r<3>));
	m_maincpu->in_port_d<4>().set_constant(1); // Active-low, indicates hotsync/dock button press
	m_maincpu->in_port_d<6>().set_constant(1); // Active-low, indicates external adapter installed
	m_maincpu->in_port_d<7>().set_constant(1); // Active-low, indicates pending power failure

	m_maincpu->in_port_f<0>().set_constant(1); // Active-high, indicates LCD is at full power
	m_maincpu->in_port_f<6>().set_constant(1); // Active-high, indicates battery enabled
	m_maincpu->in_port_f<7>().set_constant(1); // Active-low, determines sync port attachment

	m_maincpu->out_pwm().set("dac", FUNC(dac_bit_interface::write));
	m_maincpu->in_spim().set(FUNC(palmiiic_state::spi_from_hw));
	m_maincpu->out_spim().set(FUNC(palmiiic_state::spi_to_hw));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("8M");

	/* video hardware */
	SED1375(config, m_sed1375);

	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(160, 262);
	m_screen->set_visarea(0, 160-1, 0, 220-1);
	m_screen->set_screen_update(m_sed1375, FUNC(sed1375_device::screen_update));

	/* audio hardware */
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, "dac", 0).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void palmm100_state::palmm100(machine_config &config)
{
	/* basic machine hardware */
	MC68EZ328(config, m_maincpu, 32768*506);        /* 16.580608 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &palmm100_state::mem_map);
	m_maincpu->set_dasm_override(FUNC(palmm100_state::dasm_override));

	m_maincpu->out_port_b<1>().set(FUNC(palmm100_state::kbd_row_w<0>));
	m_maincpu->out_port_b<3>().set(FUNC(palmm100_state::kbd_row_w<1>));
	m_maincpu->out_port_b<6>().set(FUNC(palmm100_state::kbd_row_w<2>));

	m_maincpu->out_port_d<0>().set(FUNC(palmm100_state::kbd_col_w<0>));
	m_maincpu->out_port_d<1>().set(FUNC(palmm100_state::kbd_col_w<1>));
	m_maincpu->out_port_d<2>().set(FUNC(palmm100_state::kbd_col_w<2>));
	m_maincpu->out_port_d<3>().set(FUNC(palmm100_state::kbd_col_w<3>));

	m_maincpu->out_port_g<2>().set(FUNC(palmm100_state::hardware_id_req_w));
	m_maincpu->out_port_g<5>().set(FUNC(palmm100_state::adc_enable_w));

	m_maincpu->in_port_d<0>().set(FUNC(palmm100_state::kbd_scan_r<0>));
	m_maincpu->in_port_d<1>().set(FUNC(palmm100_state::kbd_scan_r<1>));
	m_maincpu->in_port_d<2>().set(FUNC(palmm100_state::kbd_scan_r<2>));
	m_maincpu->in_port_d<3>().set(FUNC(palmm100_state::kbd_scan_r<3>));
	m_maincpu->in_port_d<4>().set_constant(1); // Active-low, indicates hotsync/dock button press
	m_maincpu->in_port_d<6>().set_constant(1); // Active-low, indicates external adapter installed
	m_maincpu->in_port_d<7>().set_constant(1); // Active-low, indicates pending power failure

	m_maincpu->in_port_e<0>().set(FUNC(palmm100_state::hardware_subid_r<0>));
	m_maincpu->in_port_e<1>().set(FUNC(palmm100_state::hardware_subid_r<1>));
	m_maincpu->in_port_e<2>().set(FUNC(palmm100_state::hardware_subid_r<2>));
	m_maincpu->in_port_e<3>().set(FUNC(palmm100_state::hardware_subid_r<3>));
	m_maincpu->in_port_e<4>().set(FUNC(palmm100_state::hardware_subid_r<4>));
	m_maincpu->in_port_e<5>().set(FUNC(palmm100_state::hardware_subid_r<5>));
	m_maincpu->in_port_e<6>().set(FUNC(palmm100_state::hardware_subid_r<6>));
	m_maincpu->in_port_e<7>().set(FUNC(palmm100_state::hardware_subid_r<7>));

	m_maincpu->in_port_f<0>().set_constant(1); // Active-high, indicates LCD is at full power
	m_maincpu->in_port_f<6>().set_constant(1); // Active-high, indicates battery enabled
	m_maincpu->in_port_f<7>().set_constant(1); // Active-low, determines sync port attachment

	m_maincpu->out_pwm().set("dac", FUNC(dac_bit_interface::write));
	m_maincpu->in_spim().set(FUNC(palmm100_state::spi_from_hw));
	m_maincpu->out_spim().set(FUNC(palmm100_state::spi_to_hw));

	m_maincpu->out_flm().set(m_lcdctrl, FUNC(mc68328_lcd_device::flm_w));
	m_maincpu->out_llp().set(m_lcdctrl, FUNC(mc68328_lcd_device::llp_w));
	m_maincpu->out_lsclk().set(m_lcdctrl, FUNC(mc68328_lcd_device::lsclk_w));
	m_maincpu->out_ld().set(m_lcdctrl, FUNC(mc68328_lcd_device::ld_w));
	m_maincpu->set_lcd_info_changed(m_lcdctrl, FUNC(mc68328_lcd_device::lcd_info_changed));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("2M");

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(160, 220);
	m_screen->set_visarea(0, 160 - 1, 0, 220 - 1);
	m_screen->set_screen_update(FUNC(palmm100_state::screen_update));

	MC68328_LCD(config, m_lcdctrl, 0);

	/* audio hardware */
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, "dac", 0).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void palm_state::pilot1k(machine_config &config)
{
	palm_base(config);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("128K").set_extra_options("512K,1M,2M,4M,8M");

	config.set_default_layout(layout_pilot1k);
}

void palm_state::pilot5k(machine_config &config)
{
	palm_base(config);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("512K").set_extra_options("1M,2M,4M,8M");
}

void palm_state::palmpro(machine_config &config)
{
	palm_base(config);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("1M").set_extra_options("2M,4M,8M");
}

void palm_state::palmiii(machine_config &config)
{
	palm_base(config);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("2M").set_extra_options("4M,8M");
}

void palm_state::palmv(machine_config &config)
{
	palm_base(config);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("2M").set_extra_options("4M,8M");
}

void palm_state::palmvx(machine_config &config)
{
	palm_base(config);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("8M");
}


/***************************************************************************

  Game driver(s)

***************************************************************************/

#define PALM_68328_BIOS \
	ROM_REGION16_BE( 0x208000, "bios", 0 )  \
	ROM_SYSTEM_BIOS( 0, "1.0e", "Palm OS 1.0 (English)" )   \
	ROMX_LOAD( "palmos10-en.rom", 0x000000, 0x080000, CRC(82030062) SHA1(00d85c6a0588133cc4651555e9605a61fc1901fc), ROM_GROUPWORD | ROM_BIOS(0) ) \
	ROM_SYSTEM_BIOS( 1, "2.0eper", "Palm OS 2.0 Personal (English)" ) \
	ROMX_LOAD( "palmos20-en-pers.rom", 0x000000, 0x100000, CRC(40ea8baa) SHA1(8e26e213de42da1317c375fb1f394bb945b9d178), ROM_GROUPWORD | ROM_BIOS(1) ) \
	ROM_SYSTEM_BIOS( 2, "2.0epro", "Palm OS 2.0 Professional (English)" ) \
	ROMX_LOAD( "palmos20-en-pro.rom", 0x000000, 0x100000, CRC(baa5b36a) SHA1(535bd9548365d300f85f514f318460443a021476), ROM_GROUPWORD | ROM_BIOS(2) ) \
	ROM_SYSTEM_BIOS( 3, "2.0eprod", "Palm OS 2.0 Professional (English, Debug)" ) \
	ROMX_LOAD( "palmis20-en-pro-dbg.rom", 0x000000, 0x100000, CRC(0d1d3a3b) SHA1(f18a80baa306d4d46b490589ee9a2a5091f6081c), ROM_GROUPWORD | ROM_BIOS(3) ) \
	ROM_SYSTEM_BIOS( 4, "3.0e", "Palm OS 3.0 (English)" ) \
	ROMX_LOAD( "palmos30-en.rom", 0x008000, 0x200000, CRC(6f461f3d) SHA1(7fbf592b4dc8c222be510f6cfda21d48ebe22413), ROM_GROUPWORD | ROM_BIOS(4) ) \
	ROM_RELOAD(0x000000, 0x004000)  \
	ROM_SYSTEM_BIOS( 5, "3.0ed", "Palm OS 3.0 (English, Debug)" ) \
	ROMX_LOAD( "palmos30-en-dbg.rom", 0x008000, 0x200000, CRC(4deda226) SHA1(1c67d6fee2b6a4acd51cda6ef3490305730357ad), ROM_GROUPWORD | ROM_BIOS(5) ) \
	ROM_RELOAD(0x000000, 0x004000)  \
	ROM_SYSTEM_BIOS( 6, "3.0g", "Palm OS 3.0 (German)" ) \
	ROMX_LOAD( "palmos30-de.rom", 0x008000, 0x200000, CRC(b991d6c3) SHA1(73e7539517b0d931e9fa99d6f6914ad46fb857b4), ROM_GROUPWORD | ROM_BIOS(6) ) \
	ROM_RELOAD(0x000000, 0x004000)  \
	ROM_SYSTEM_BIOS( 7, "3.0f", "Palm OS 3.0 (French)" ) \
	ROMX_LOAD( "palmos30-fr.rom", 0x008000, 0x200000, CRC(a2a9ff6c) SHA1(7cb119f896017e76e4680510bee96207d9d28e44), ROM_GROUPWORD | ROM_BIOS(7) ) \
	ROM_RELOAD(0x000000, 0x004000)  \
	ROM_SYSTEM_BIOS( 8, "3.0s", "Palm OS 3.0 (Spanish)" ) \
	ROMX_LOAD( "palmos30-sp.rom", 0x008000, 0x200000, CRC(63a595be) SHA1(f6e03a2fedf0cbe6228613f50f8e8717e797877d), ROM_GROUPWORD | ROM_BIOS(8) ) \
	ROM_RELOAD(0x000000, 0x004000)  \
	ROM_SYSTEM_BIOS( 9, "3.3e", "Palm OS 3.3 (English)" ) \
	ROMX_LOAD( "palmos33-en-iii.rom", 0x008000, 0x200000, CRC(1eae0253) SHA1(e4626f1d33eca8368284d906b2152dcd28b71bbd), ROM_GROUPWORD | ROM_BIOS(9) ) \
	ROM_RELOAD(0x000000, 0x004000)  \
	ROM_SYSTEM_BIOS( 10, "3.3f", "Palm OS 3.3 (French)" ) \
	ROMX_LOAD( "palmos33-fr-iii.rom", 0x008000, 0x200000, CRC(d7894f5f) SHA1(c7c90df814d4f97958194e0bc28c595e967a4529), ROM_GROUPWORD | ROM_BIOS(10) ) \
	ROM_RELOAD(0x000000, 0x004000)  \
	ROM_SYSTEM_BIOS( 11, "3.3g", "Palm OS 3.3 (German)" ) \
	ROMX_LOAD( "palmos33-de-iii.rom", 0x008000, 0x200000, CRC(a5a99c45) SHA1(209b0154942dab80b56d5e6e68fa20b9eb75f5fe), ROM_GROUPWORD | ROM_BIOS(11) ) \
	ROM_RELOAD(0x000000, 0x004000)

ROM_START( pilot1k )
	PALM_68328_BIOS
	ROM_DEFAULT_BIOS( "1.0e" )
ROM_END

ROM_START( pilot5k )
	PALM_68328_BIOS
	ROM_DEFAULT_BIOS( "1.0e" )
ROM_END

ROM_START( palmpers )
	PALM_68328_BIOS
	ROM_DEFAULT_BIOS( "2.0eper" )
ROM_END

ROM_START( palmpro )
	PALM_68328_BIOS
	ROM_DEFAULT_BIOS( "2.0epro" )
ROM_END

ROM_START( palmiii )
	PALM_68328_BIOS
	ROM_DEFAULT_BIOS( "3.0e" )
ROM_END

ROM_START( palmv )
	ROM_REGION16_BE( 0x208000, "bios", 0 )
	ROM_SYSTEM_BIOS( 0, "3.1e", "Palm OS 3.1 (English)" )
	ROMX_LOAD( "palmv31-en.rom", 0x008000, 0x200000, CRC(4656b2ae) SHA1(ec66a93441fbccfd8e0c946baa5d79c478c83e85), ROM_GROUPWORD | ROM_BIOS(0) )
	ROM_RELOAD(0x000000, 0x004000)
	ROM_SYSTEM_BIOS( 1, "3.1g", "Palm OS 3.1 (German)" )
	ROMX_LOAD( "palmv31-de.rom", 0x008000, 0x200000, CRC(a9631dcf) SHA1(63b44d4d3fc2f2196c96d3b9b95da526df0fac77), ROM_GROUPWORD | ROM_BIOS(1) )
	ROM_RELOAD(0x000000, 0x004000)
	ROM_SYSTEM_BIOS( 2, "3.1f", "Palm OS 3.1 (French)" )
	ROMX_LOAD( "palmv31-fr.rom", 0x008000, 0x200000, CRC(0d933a1c) SHA1(d0454f1159705d0886f8a68e1b8a5e96d2ca48f6), ROM_GROUPWORD | ROM_BIOS(2) )
	ROM_RELOAD(0x000000, 0x004000)
	ROM_SYSTEM_BIOS( 3, "3.1s", "Palm OS 3.1 (Spanish)" )
	ROMX_LOAD( "palmv31-sp.rom", 0x008000, 0x200000, CRC(cc46ca1f) SHA1(93bc78ca84d34916d7e122b745adec1068230fcd), ROM_GROUPWORD | ROM_BIOS(3) )
	ROM_RELOAD(0x000000, 0x004000)
	ROM_SYSTEM_BIOS( 4, "3.1j", "Palm OS 3.1 (Japanese)" )
	ROMX_LOAD( "palmv31-jp.rom", 0x008000, 0x200000, CRC(c786db12) SHA1(4975ff2af76892370c5d4d7d6fa87a84480e79d6), ROM_GROUPWORD | ROM_BIOS(4) )
	ROM_RELOAD(0x000000, 0x004000)
	ROM_SYSTEM_BIOS( 5, "3.1e2", "Palm OS 3.1 (English) v2" )
	ROMX_LOAD( "palmv31-en-2.rom", 0x008000, 0x200000, CRC(caced2bd) SHA1(95970080601f72a77a4c338203ed8809fab17abf), ROM_GROUPWORD | ROM_BIOS(5) )
	ROM_RELOAD(0x000000, 0x004000)
	ROM_DEFAULT_BIOS( "3.1e2" )
ROM_END

ROM_START( palmvx )
	ROM_REGION16_BE( 0x208000, "bios", 0 )
	ROM_SYSTEM_BIOS( 0, "3.3e", "Palm OS 3.3 (English)" )
	ROMX_LOAD( "palmvx33-en.rom", 0x000000, 0x200000, CRC(3fc0cc6d) SHA1(6873d5fa99ac372f9587c769940c9b3ac1745a0a), ROM_GROUPWORD | ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "4.0e", "Palm OS 4.0 (English)" )
	ROMX_LOAD( "palmvx40-en.rom", 0x000000, 0x200000, CRC(488e4638) SHA1(10a10fc8617743ebd5df19c1e99ca040ac1da4f5), ROM_GROUPWORD | ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "4.1e", "Palm OS 4.1 (English)" )
	ROMX_LOAD( "palmvx41-en.rom", 0x000000, 0x200000, CRC(e59f4dff) SHA1(5e3000db318eeb8cd1f4d9729d0c9ebca560fa4a), ROM_GROUPWORD | ROM_BIOS(2) )
	ROM_DEFAULT_BIOS( "4.1e" )
ROM_END

ROM_START( palmiiic )
	ROM_REGION16_BE( 0x208000, "bios", 0 )
	ROM_SYSTEM_BIOS( 0, "3.5eb", "Palm OS 3.5 (English) beta" )
	ROMX_LOAD( "palmiiic350-en-beta.rom", 0x008000, 0x200000, CRC(d58521a4) SHA1(508742ea1e078737666abd4283cf5e6985401c9e), ROM_GROUPWORD | ROM_BIOS(0) )
	ROM_RELOAD(0x000000, 0x004000)
	ROM_SYSTEM_BIOS( 1, "3.5c", "Palm OS 3.5 (Chinese)" )
	ROMX_LOAD( "palmiiic350-ch.rom", 0x008000, 0x200000, CRC(a9779f3a) SHA1(1541102cd5234665233072afe8f0e052134a5334), ROM_GROUPWORD | ROM_BIOS(1) )
	ROM_RELOAD(0x000000, 0x004000)
	ROM_SYSTEM_BIOS( 2, "4.0e", "Palm OS 4.0 (English)" )
	ROMX_LOAD( "palmiiic40-en.rom", 0x008000, 0x200000, CRC(6b2a5ad2) SHA1(54321dcaedcc80de57a819cfd599d8d1b2e26eeb), ROM_GROUPWORD | ROM_BIOS(2) )
	ROM_RELOAD(0x000000, 0x004000)
	ROM_DEFAULT_BIOS( "4.0e" )
ROM_END

ROM_START( palmm100 )
	ROM_REGION16_BE( 0x208000, "bios", 0 )
	ROM_SYSTEM_BIOS( 0, "3.51e", "Palm OS 3.5.1 (English)" )
	ROMX_LOAD( "palmm100-351-en.rom", 0x008000, 0x200000, CRC(ae8dda60) SHA1(c46248d6f05cb2f4337985610cedfbdc12ac47cf), ROM_GROUPWORD | ROM_BIOS(0) )
	ROM_RELOAD(0x000000, 0x004000)
	ROM_DEFAULT_BIOS( "3.51e" )
ROM_END

ROM_START( palmm130 )
	ROM_REGION16_BE( 0x408000, "bios", 0 )
	ROM_SYSTEM_BIOS( 0, "4.0e", "Palm OS 4.0 (English)" )
	ROMX_LOAD( "palmm130-40-en.rom", 0x008000, 0x400000, CRC(58046b7e) SHA1(986057010d62d5881fba4dede2aba0d4d5008b16), ROM_GROUPWORD | ROM_BIOS(0) )
	ROM_RELOAD(0x000000, 0x004000)
	ROM_DEFAULT_BIOS( "4.0e" )
ROM_END

ROM_START( palmm505 )
	ROM_REGION16_BE( 0x408000, "bios", 0 )
	ROM_SYSTEM_BIOS( 0, "4.0e", "Palm OS 4.0 (English)" )
	ROMX_LOAD( "palmos40-en-m505.rom", 0x008000, 0x400000, CRC(822a4679) SHA1(a4f5e9f7edb1926647ea07969200c5c5e1521bdf), ROM_GROUPWORD | ROM_BIOS(0) )
	ROM_RELOAD(0x000000, 0x004000)
	ROM_SYSTEM_BIOS( 1, "4.1e", "Palm OS 4.1 (English)" )
	ROMX_LOAD( "palmos41-en-m505.rom", 0x008000, 0x400000, CRC(d248202a) SHA1(65e1bd08b244c589b4cd10fe573e0376aba90e5f), ROM_GROUPWORD | ROM_BIOS(1) )
	ROM_RELOAD(0x000000, 0x004000)
	ROM_DEFAULT_BIOS( "4.1e" )
ROM_END

ROM_START( palmm515 )
	ROM_REGION16_BE( 0x408000, "bios", 0 )
	ROM_SYSTEM_BIOS( 0, "4.1e", "Palm OS 4.1 (English)" )
	ROMX_LOAD( "palmos41-en-m515.rom", 0x008000, 0x400000, CRC(6e143436) SHA1(a0767ea26cc493a3f687525d173903fef89f1acb), ROM_GROUPWORD | ROM_BIOS(0) )
	ROM_RELOAD(0x000000, 0x004000)
	ROM_DEFAULT_BIOS( "4.1e" )
ROM_END

ROM_START( visor )
	ROM_REGION16_BE( 0x208000, "bios", 0 )
	ROM_SYSTEM_BIOS( 0, "3.52e", "Palm OS 3.5.2 (English)" )
	ROMX_LOAD( "visor-352-en.rom", 0x008000, 0x200000, CRC(c9e55271) SHA1(749e9142f4480114c5e0d7f21ea354df7273ac5b), ROM_GROUPWORD | ROM_BIOS(0) )
	ROM_RELOAD(0x000000, 0x004000)
	ROM_DEFAULT_BIOS( "3.52e" )
ROM_END

ROM_START( spt1500 )
	ROM_REGION16_BE( 0x208000, "bios", 0 )
	ROM_SYSTEM_BIOS( 0, "4.1pim", "Version 4.1 (pim)" )
	ROMX_LOAD( "spt1500v41-pim.rom",      0x008000, 0x200000, CRC(29e50eaf) SHA1(3e920887bdf74f8f83935977b02f22d5217723eb), ROM_GROUPWORD | ROM_BIOS(0) )
	ROM_RELOAD(0x000000, 0x004000)
	ROM_SYSTEM_BIOS( 1, "4.1pimn", "Version 4.1 (pimnoft)" )
	ROMX_LOAD( "spt1500v41-pimnoft.rom",  0x008000, 0x200000, CRC(4b44f284) SHA1(4412e946444706628b94d2303b02580817e1d370), ROM_GROUPWORD | ROM_BIOS(1) )
	ROM_RELOAD(0x000000, 0x004000)
	ROM_SYSTEM_BIOS( 2, "4.1nopimn", "Version 4.1 (nopimnoft)" )
	ROMX_LOAD( "spt1500v41-nopimnoft.rom",0x008000, 0x200000, CRC(4ba19190) SHA1(d713c1390b82eb4e5fbb39aa10433757c5c49e02), ROM_GROUPWORD | ROM_BIOS(2) )
	ROM_RELOAD(0x000000, 0x004000)
ROM_END

ROM_START( spt1700 )
	ROM_REGION16_BE( 0x208000, "bios", 0 )
	ROM_SYSTEM_BIOS( 0, "1.03pim", "Version 1.03 (pim)" )
	ROMX_LOAD( "spt1700v103-pim.rom",    0x008000, 0x200000, CRC(9df4ee50) SHA1(243a19796f15219cbd73e116f7dfb236b3d238cd), ROM_GROUPWORD | ROM_BIOS(0) )
	ROM_RELOAD(0x000000, 0x004000)
ROM_END

ROM_START( spt1740 )
	ROM_REGION16_BE( 0x208000, "bios", 0 )
	ROM_SYSTEM_BIOS( 0, "1.03pim", "Version 1.03 (pim)" )
	ROMX_LOAD( "spt1740v103-pim.rom",    0x008000, 0x200000, CRC(c29f341c) SHA1(b56d7f8a0c15b1105972e24ed52c846b5e27b195), ROM_GROUPWORD | ROM_BIOS(0) )
	ROM_RELOAD(0x000000, 0x004000)
	ROM_SYSTEM_BIOS( 1, "1.03pimn", "Version 1.03 (pimnoft)" )
	ROMX_LOAD( "spt1740v103-pimnoft.rom", 0x008000, 0x200000, CRC(b2d49d5c) SHA1(c133dc021b6797cdb93b666c5b315b00b5bb0917), ROM_GROUPWORD | ROM_BIOS(1) )
	ROM_RELOAD(0x000000, 0x004000)
	ROM_SYSTEM_BIOS( 2, "1.03nopim", "Version 1.03 (nopim)" )
	ROMX_LOAD( "spt1740v103-nopim.rom",   0x008000, 0x200000, CRC(8ea7e652) SHA1(2a4b5d6a426e627b3cb82c47109cfe2497eba29a), ROM_GROUPWORD | ROM_BIOS(2) )
	ROM_RELOAD(0x000000, 0x004000)
ROM_END

} // anonymous namespace

//    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY          FULLNAME               FLAGS
COMP( 1996, pilot1k,  0,       0,      pilot1k,  palm,     palm_state,     empty_init, "U.S. Robotics", "Pilot 1000",          MACHINE_SUPPORTS_SAVE )
COMP( 1996, pilot5k,  pilot1k, 0,      pilot5k,  palm,     palm_state,     empty_init, "U.S. Robotics", "Pilot 5000",          MACHINE_SUPPORTS_SAVE )
COMP( 1997, palmpers, pilot1k, 0,      pilot5k,  palm,     palm_state,     empty_init, "U.S. Robotics", "Palm Pilot Personal", MACHINE_SUPPORTS_SAVE )
COMP( 1997, palmpro,  pilot1k, 0,      palmpro,  palm,     palm_state,     empty_init, "U.S. Robotics", "Palm Pilot Pro",      MACHINE_SUPPORTS_SAVE )
COMP( 1998, palmiii,  pilot1k, 0,      palmiii,  palm,     palm_state,     empty_init, "3Com",          "Palm III",            MACHINE_SUPPORTS_SAVE )
COMP( 1998, palmiiic, pilot1k, 0,      palmiiic, palmiiic, palmiiic_state, empty_init, "3Com",          "Palm IIIc",           MACHINE_SUPPORTS_SAVE )
COMP( 2000, palmm100, pilot1k, 0,      palmm100, palmm100, palmm100_state, empty_init, "3Com",          "Palm m100",           MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )
COMP( 2000, palmm130, pilot1k, 0,      palmiii,  palm,     palm_state,     empty_init, "3Com",          "Palm m130",           MACHINE_NOT_WORKING )
COMP( 2001, palmm505, pilot1k, 0,      palmiii,  palm,     palm_state,     empty_init, "3Com",          "Palm m505",           MACHINE_NOT_WORKING )
COMP( 2001, palmm515, pilot1k, 0,      palmiii,  palm,     palm_state,     empty_init, "3Com",          "Palm m515",           MACHINE_NOT_WORKING )
COMP( 1999, palmv,    pilot1k, 0,      palmv,    palm,     palm_state,     empty_init, "3Com",          "Palm V",              MACHINE_NOT_WORKING )
COMP( 1999, palmvx,   pilot1k, 0,      palmvx,   palm,     palm_state,     empty_init, "3Com",          "Palm Vx",             MACHINE_NOT_WORKING )
COMP( 2001, visor,    pilot1k, 0,      palmvx,   palm,     palm_state,     empty_init, "Handspring",    "Visor Edge",          MACHINE_NOT_WORKING )
COMP( 19??, spt1500,  pilot1k, 0,      palmvx,   palm,     palm_state,     empty_init, "Symbol",        "SPT 1500",            MACHINE_NOT_WORKING )
COMP( 19??, spt1700,  pilot1k, 0,      palmvx,   palm,     palm_state,     empty_init, "Symbol",        "SPT 1700",            MACHINE_NOT_WORKING )
COMP( 19??, spt1740,  pilot1k, 0,      palmvx,   palm,     palm_state,     empty_init, "Symbol",        "SPT 1740",            MACHINE_NOT_WORKING )

#include "palm_dbg.ipp"



palmz22.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Tim Schuerewegen
/*******************************************************************************

    Palm Z22

    (C) 2011 Tim Schuerewegen

*******************************************************************************/

/*

Hardware ...

Samsung OCEAN-L-20      (SoC)
Samsung K9F5608U0D-JIB0 (NAND)
Samsung K4S2816321-UL75 (RAM)

The following reads are harmless ...

'maincpu' (3060017C): (GPIO) 56000080 -> 00010020
'maincpu' (30600960): unmapped program memory read from A0000000 & FFFFFFFF
'maincpu' (30600960): unmapped program memory read from A0000020 & FFFFFFFF
'maincpu' (30600960): unmapped program memory read from A0000040 & FFFFFFFF
...
'maincpu' (306411D4): (GPIO) 56000000 -> 005E03FF
'maincpu' (306411E0): (GPIO) 56000004 -> 0041F000
'maincpu' (30641258): (GPIO) 56000010 -> 00000015
'maincpu' (30641264): (GPIO) 56000010 -> 00000005
'maincpu' (306412C4): (GPIO) 56000040 -> 00155400
'maincpu' (306412D0): (GPIO) 56000040 -> 00155000
'maincpu' (306412DC): (GPIO) 56000044 -> 00000442
'maincpu' (306412F0): (GPIO) 56000000 -> 005E03FF
'maincpu' (306412FC): (GPIO) 56000000 -> 001E03FF
'maincpu' (30641308): (GPIO) 56000004 -> 0041F000
'maincpu' (30641320): (GPIO) 56000010 -> 00000025
'maincpu' (30641330): (GPIO) 56000010 -> 00000024

The following reads are NOT harmless ...

'maincpu' (3060022C): (GPIO) 56000044 -> 00000400 ... bits 2..1 is tested

if bits 2..1 = 0 then
  'maincpu' (30600240): (GPIO) 56000040 <- 00154400
  'maincpu' (3060024C): (GPIO) 56000044 <- 00000000
  'maincpu' (30600258): (GPIO) 56000048 <- 000007A0
else
  'maincpu' (30600268): (GPIO) 56000040 <- 00155400
  'maincpu' (30600274): (GPIO) 56000044 <- 00000440
  'maincpu' (30600280): (GPIO) 56000048 <- 000007E6
end

'maincpu' (3060004C): (GPIO) 56000054 -> 000000FF
'maincpu' (30600060): (GPIO) 56000054 -> 000000FF
'maincpu' (30600074): (GPIO) 56000054 -> 000000FF
'maincpu' (30600088): (GPIO) 56000054 -> 000000FF
'maincpu' (3060009C): (GPIO) 56000054 -> 000000FF
'maincpu' (306000B0): (GPIO) 56000054 -> 000000FF
'maincpu' (306000C4): (GPIO) 56000054 -> 000000FF
'maincpu' (306000D8): (GPIO) 56000054 -> 000000FF ... check if keys are pressed? (8 bits)

'maincpu' (3064116C): unmapped program memory read from 4D000068 & FFFFFFFF ... should read 0 or else stuck in a loop?

*/

#include "emu.h"
#include "cpu/arm7/arm7.h"
#include "machine/nandflash.h"
#include "machine/s3c2410.h"
#include "screen.h"

#include <cstdarg>


namespace {

#define PALM_Z22_BATTERY_LEVEL  75

#define VERBOSE_LEVEL ( 0 )

class palmz22_state : public driver_device
{
public:
	palmz22_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_s3c2410(*this, "s3c2410")
		, m_nand(*this, "nand")
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(input_changed);

	void palmz22(machine_config &config);

	void init_palmz22();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<s3c2410_device> m_s3c2410;
	required_device<samsung_k9f5608u0dj_device> m_nand;

	uint32_t m_port[8];

	inline void verboselog(int n_level, const char *s_fmt, ...) ATTR_PRINTF(3,4);
	void s3c2410_nand_command_w(uint8_t data);
	void s3c2410_nand_address_w(uint8_t data);
	uint8_t s3c2410_nand_data_r();
	void s3c2410_nand_data_w(uint8_t data);
	uint32_t s3c2410_gpio_port_r(offs_t offset);
	void s3c2410_gpio_port_w(offs_t offset, uint32_t data);
	uint32_t s3c2410_core_pin_r(offs_t offset);
	uint32_t s3c2410_adc_data_r(offs_t offset);

	void map(address_map &map) ATTR_COLD;
};


inline void palmz22_state::verboselog(int n_level, const char *s_fmt, ...)
{
	if (VERBOSE_LEVEL >= n_level)
	{
		va_list v;
		char buf[32768];
		va_start( v, s_fmt);
		vsprintf( buf, s_fmt, v);
		va_end( v);
		logerror( "%s: %s", machine().describe_context(), buf);
	}
}

/***************************************************************************
    MACHINE HARDWARE
***************************************************************************/

// NAND

void palmz22_state::s3c2410_nand_command_w(uint8_t data)
{
	verboselog(9, "s3c2410_nand_command_w %02X\n", data);
	m_nand->command_w(data);
}

void palmz22_state::s3c2410_nand_address_w(uint8_t data)
{
	verboselog(9, "s3c2410_nand_address_w %02X\n", data);
	m_nand->address_w(data);
}

uint8_t palmz22_state::s3c2410_nand_data_r()
{
	uint8_t data = m_nand->data_r();
	verboselog(9, "s3c2410_nand_data_r %02X\n", data);
	return data;
}

void palmz22_state::s3c2410_nand_data_w(uint8_t data)
{
	verboselog(9, "s3c2410_nand_data_w %02X\n", data);
	m_nand->data_w(data);
}

/*
uint8_t palmz22_state::s3c2410_nand_busy_r()
{
    uint8_t data = m_nand->is_busy();
    verboselog(9, "s3c2410_nand_busy_r %02X\n", data);
    return data;
}
*/

// GPIO

uint32_t palmz22_state::s3c2410_gpio_port_r(offs_t offset)
{
	uint32_t data = m_port[offset];
	switch (offset)
	{
		case S3C2410_GPIO_PORT_E :
		{
			data = (data & ~(3 << 1)) | (0 << 1); // bits 2..1 = LCD related? (0 = clear 4D000010 bit 3, 1 = clear 560000000 bit 22)
			//data = 0x1F4;
		}
		break;
		case S3C2410_GPIO_PORT_F :
		{
			data = (data & ~0xFF) | (ioport( "PORT-F")->read() & 0xFF);
		}
		break;
		case S3C2410_GPIO_PORT_G :
		{
			data = (data & ~(1 << 1)) | (0 << 1); // bit 1 | 'maincpu' (200B56C0): (GPIO) 56000064 -> 00000000
			data = (data & ~(1 << 2)) | (0 << 2); // bit 2 | 'maincpu' (200B3EC4): (GPIO) 56000064 -> 00000000
		}
		break;
	}
	return data;
}

void palmz22_state::s3c2410_gpio_port_w(offs_t offset, uint32_t data)
{
	m_port[offset] = data;
}

// CORE

/*

OM[1:0] = 00b : Enable NAND flash controller auto boot mode

NAND flash memory page size should be 512Bytes.

NCON : NAND flash memory address step selection
0 : 3 Step addressing
1 : 4 Step addressing

*/

uint32_t palmz22_state::s3c2410_core_pin_r(offs_t offset)
{
	int data = 0;
	switch (offset)
	{
		case S3C2410_CORE_PIN_NCON : data = 0; break;
		case S3C2410_CORE_PIN_OM0  : data = 0; break;
		case S3C2410_CORE_PIN_OM1  : data = 0; break;
	}
	return data;
}

// ADC

uint32_t palmz22_state::s3c2410_adc_data_r(offs_t offset)
{
	uint32_t data = 0;
	switch (offset)
	{
		case 0 + 0 : data = 0x2EE + (PALM_Z22_BATTERY_LEVEL * 0xFF / 100); break;
		case 0 + 1 : data = 0; break;
		case 2 + 0 : data = ioport( "PENX")->read(); break;
		case 2 + 1 : data = 0x3FF - ioport( "PENY")->read(); break;
	}
	verboselog(5,  "s3c2410_adc_data_r %08X\n", data);
	return data;
}

// INPUT

INPUT_CHANGED_MEMBER(palmz22_state::input_changed)
{
	if (param == 0)
	{
		m_s3c2410->s3c2410_touch_screen( (newval & 0x01) ? 1 : 0);
	}
	else
	{
		m_s3c2410->s3c2410_request_eint( param - 1);
	}
}

// MACHINE

void palmz22_state::machine_start()
{
	save_item(NAME(m_port));
}

void palmz22_state::machine_reset()
{
	m_maincpu->reset();
	memset( m_port, 0, sizeof( m_port));
}

/***************************************************************************
    ADDRESS MAPS
***************************************************************************/

void palmz22_state::map(address_map &map)
{
	map(0x30000000, 0x31ffffff).ram();
}

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

void palmz22_state::init_palmz22()
{
}

void palmz22_state::palmz22(machine_config &config)
{
	ARM920T(config, m_maincpu, 266000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &palmz22_state::map);

	PALETTE(config, "palette").set_entries(32768);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(160, 160);
	screen.set_visarea(0, 160 - 1, 0, 160 - 1);
	screen.set_screen_update("s3c2410", FUNC(s3c2410_device::screen_update));

	S3C2410(config, m_s3c2410, 12000000);
	m_s3c2410->set_palette_tag("palette");
	m_s3c2410->set_screen_tag("screen");
	m_s3c2410->core_pin_r_callback().set(FUNC(palmz22_state::s3c2410_core_pin_r));
	m_s3c2410->gpio_port_r_callback().set(FUNC(palmz22_state::s3c2410_gpio_port_r));
	m_s3c2410->gpio_port_w_callback().set(FUNC(palmz22_state::s3c2410_gpio_port_w));
	m_s3c2410->adc_data_r_callback().set(FUNC(palmz22_state::s3c2410_adc_data_r));
	m_s3c2410->nand_command_w_callback().set(FUNC(palmz22_state::s3c2410_nand_command_w));
	m_s3c2410->nand_address_w_callback().set(FUNC(palmz22_state::s3c2410_nand_address_w));
	m_s3c2410->nand_data_r_callback().set(FUNC(palmz22_state::s3c2410_nand_data_r));
	m_s3c2410->nand_data_w_callback().set(FUNC(palmz22_state::s3c2410_nand_data_w));

	SAMSUNG_K9F5608U0DJ(config, m_nand, 0);
	m_nand->rnb_wr_callback().set(m_s3c2410, FUNC(s3c2410_device::frnb_w));
}

static INPUT_PORTS_START( palmz22 )
	PORT_START( "PENB" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Pen Button") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmz22_state::input_changed), 0) PORT_PLAYER(2)
	PORT_START( "PENX" )
	PORT_BIT( 0x3ff, 0x200, IPT_LIGHTGUN_X ) PORT_NAME("Pen X") PORT_SENSITIVITY(50) PORT_CROSSHAIR(X, 1, 0, 0) PORT_KEYDELTA(30) PORT_PLAYER(2)
	PORT_START( "PENY" )
	PORT_BIT( 0x3ff, 0x200, IPT_LIGHTGUN_Y ) PORT_NAME("Pen Y") PORT_SENSITIVITY(50) PORT_CROSSHAIR(Y, 1, 0, 0) PORT_KEYDELTA(30) PORT_PLAYER(2)
	PORT_START( "PORT-F" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON5        ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmz22_state::input_changed), 1) PORT_NAME("Power")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2        ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmz22_state::input_changed), 1) PORT_NAME("Contacts")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON4        ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmz22_state::input_changed), 1) PORT_NAME("Calendar")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1        ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmz22_state::input_changed), 1) PORT_NAME("Center")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP    ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmz22_state::input_changed), 1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN  ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmz22_state::input_changed), 1)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT  ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmz22_state::input_changed), 1)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(palmz22_state::input_changed), 1)
INPUT_PORTS_END

/***************************************************************************
    GAME DRIVERS
***************************************************************************/

ROM_START( palmz22 )
	ROM_REGION( 0x2100000, "nand", 0 )
	ROM_LOAD( "palmz22.bin", 0, 0x2100000, CRC(6d0320b3) SHA1(99297975fdad44faf69cc6eaf0fa2560d5579a4d) )
ROM_END

} // anonymous namespace


COMP( 2005, palmz22, 0, 0, palmz22, palmz22, palmz22_state, init_palmz22, "Palm", "Palm Z22", MACHINE_NO_SOUND)



pap2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Joseph Khilfeh

/*

Linksys PAP2 two-port analog telephone adapter

ESS Visba 3 ES3890F @ 27MHz (CPU/MCU)
Realtek RTL8019AS (NIC)
ICSI IC41LV16100S-50KG (2MB RAM)
SST39VF080-70-4C-EI (1MB flash ROM)
2x Silicon Labs Si3210-KT (line interfaces)

Four of the five LEDs are controlled by ES3890F GPIOs
Power LEDs are connected to AUX1[4] and AUX1[5] (pin 112 and 113)
Phone 1 LED is connected to AUX1[6] (pin 114)
Phone 2 LED is connected to AUX1[7] (pin 115)
Ethernet LED is connected to RTL8019AS somehow

RTL8019AS pin 65 is tied to VDD to disable PnP
RTL8019AS pin 96 is tied to ground to share the flash ROM's 8-bit data bus
RTL8019AS registers are selected when ES3890F pin 19 (/CS1) is low
It looks like the RTL8019AS EEPROM pins are connected to ES3890F AUX2[0-3]?


There are several ATAs based on essentially the same hardware:

Sipura SPA-2000
Sipura SPA-1000
Sipura SPA-3000 (ES3890F, RTL8019AS, 2MB RAM, 1MB flash, unknown line interfaces)
Sipura SPA-1001 (ES3890F, RTL8019AS, 2MB RAM, 1MB flash, 1x Si3210)
Sipura SPA-2100 (Sipura SIP316F @ 27MHz, 2x RTL8019AS, 8MB RAM, 2MB flash, unknown line interfaces)
Sipura SPA-2002
Linksys PAP2T (ES3890F, RTL8019AS, 2MB RAM, 1MB flash, 2x Si3215)
Linksys SPA2102?

There are also ATAs with similar names but probably very different hardware:

Linksys PAP2 V2
Cisco SPA112
Cisco SPA122

*/

#include "emu.h"

#include "cpu/mipsx/mipsx.h"


namespace {

class pap2_state : public driver_device

{
public:
	pap2_state(machine_config const &mconfig, device_type type, char const *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu")
	{
	}

	void pap2(machine_config &config) ATTR_COLD;

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<mipsx_cpu_device> m_maincpu;

	void mem(address_map &map) ATTR_COLD;
};


void pap2_state::machine_reset()
{
	m_maincpu->set_state_int(STATE_GENPC, 0x1cffff80); // might actually be 0x7fffff80 with a ROM mirror
}

void pap2_state::mem(address_map &map)
{
	map(0x00000000, 0x001fffff).ram();
	map(0x1c000000, 0x1c0fffff).mirror(0x00f00000).rom().region("maincpu", 0);
	// ES3890F registers at 0x20000000-0x2000ffff, ES6008 datasheet could be helpful
}

void pap2_state::pap2(machine_config &config)
{
	MIPSX(config, m_maincpu, 27_MHz_XTAL * 3); // guessing 3x from ES3880 datasheet
	m_maincpu->set_addrmap(AS_PROGRAM, &pap2_state::mem);
}

INPUT_PORTS_START(pap2)
INPUT_PORTS_END

ROM_START(pap2)
	ROM_REGION32_BE(0x100000, "maincpu", 0 )
	ROM_LOAD("linksys-pap2-2.0.12-ls.u51", 0x000000, 0x100000, BAD_DUMP CRC(4d0f1e5d) SHA1(73b163b00a3709a14f7419283c8515dd91009598) )
	// Original ROM label is unknown, if it had one. S/N FH900DC35989, MAC 00:12:17:FB:70:DC
ROM_END

} // anonymous namespace

SYST( 200?, pap2, 0, 0, pap2, pap2, pap2_state, empty_init, "Linksys (Cisco)", "PAP2", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



partner.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

Partner driver by Miodrag Milanovic

2008-06-09 Preliminary driver.

Works well with cassette. Need to find out how to access the built-in
rom programs. Also need to find out how to access floppy disks, including
those in the softlist.

Cursor is one position too far to the right.

****************************************************************************/


#include "emu.h"
#include "partner.h"

#include "cpu/i8085/i8085.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/rk_cas.h"
#include "formats/smx_dsk.h"


/* Address maps */
void partner_state::mem_map(address_map &map)
{
	map(0x0000, 0x07ff).bankrw("bank1");
	map(0x0800, 0x3fff).bankrw("bank2");
	map(0x4000, 0x5fff).bankrw("bank3");
	map(0x6000, 0x7fff).bankrw("bank4");
	map(0x8000, 0x9fff).bankrw("bank5");
	map(0xa000, 0xb7ff).bankrw("bank6");
	map(0xb800, 0xbfff).bankrw("bank7");
	map(0xc000, 0xc7ff).bankrw("bank8");
	map(0xc800, 0xcfff).bankrw("bank9");
	map(0xd000, 0xd7ff).bankrw("bank10");
	map(0xd800, 0xd8ff).rw("crtc", FUNC(i8275_device::read), FUNC(i8275_device::write));
	map(0xd900, 0xd9ff).rw(m_ppi1, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xda00, 0xdaff).w(FUNC(partner_state::mem_page_w));
	map(0xdb00, 0xdbff).w(m_dma, FUNC(i8257_device::write));
	map(0xdc00, 0xddff).bankrw("bank11");
	map(0xde00, 0xdeff).w(FUNC(partner_state::win_memory_page_w));
	map(0xe000, 0xe7ff).bankrw("bank12");
	map(0xe800, 0xffff).bankrw("bank13");
}

/* Input ports */
static INPUT_PORTS_START( partner )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC),27)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Back") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR(0xA4)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')

	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')

	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')

	PORT_START("LINE7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('^')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("LINE8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)  // This is a toggle to switch between Latin and Russian Keyboard!
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_1) // This is the real shift
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Rus/Lat") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT) // doesn't do anything useful
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

/* Machine driver */
void partner_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_SMX_FORMAT);
}

/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	512,                    /* 512 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_partner )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END


void partner_state::partner(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, 16_MHz_XTAL / 9);
	m_maincpu->set_addrmap(AS_PROGRAM, &partner_state::mem_map);

	I8255(config, m_ppi1);
	m_ppi1->out_pa_callback().set(FUNC(partner_state::radio86_8255_porta_w2));
	m_ppi1->in_pb_callback().set(FUNC(partner_state::radio86_8255_portb_r2));
	m_ppi1->in_pc_callback().set(FUNC(partner_state::radio86_8255_portc_r2));
	m_ppi1->out_pc_callback().set(FUNC(partner_state::radio86_8255_portc_w2));

	auto &i8275(I8275(config, "crtc", 16_MHz_XTAL / 12));
	i8275.set_character_width(6);
	i8275.set_display_callback(FUNC(partner_state::display_pixels));
	i8275.drq_wr_callback().set(m_dma, FUNC(i8257_device::dreq2_w));

	/* video hardware */
	auto &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_screen_update("crtc", FUNC(i8275_device::screen_update));
	screen.set_refresh_hz(50);
	screen.set_size(78*6, 30*10);
	screen.set_visarea(0, 78*6-1, 0, 30*10-1);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_partner);
	PALETTE(config, m_palette, FUNC(partner_state::radio86_palette), 3);

	SPEAKER(config, "mono").front_center();

	I8257(config, m_dma, 16_MHz_XTAL / 9);
	m_dma->out_hrq_cb().set(FUNC(partner_state::hrq_w));
	m_dma->in_memr_cb().set(FUNC(partner_state::memory_read_byte));
	m_dma->out_memw_cb().set(FUNC(partner_state::memory_write_byte));
	m_dma->in_ior_cb<0>().set("fdc", FUNC(fd1793_device::data_r));
	m_dma->out_iow_cb<0>().set("fdc", FUNC(fd1793_device::data_w));
	m_dma->out_iow_cb<2>().set("crtc", FUNC(i8275_device::dack_w));
	m_dma->set_reverse_rw_mode(true);

	auto &cassette(CASSETTE(config, "cassette"));
	cassette.set_formats(rkp_cassette_formats);
	cassette.set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	cassette.add_route(ALL_OUTPUTS, "mono", 0.05);
	cassette.set_interface("partner_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("partner_cass");

	FD1793(config, "fdc", 16_MHz_XTAL / 16);

	FLOPPY_CONNECTOR(config, "fd0", "525qd", FLOPPY_525_QD, true, floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fd1", "525qd", FLOPPY_525_QD, true, floppy_formats).enable_sound(true);

	SOFTWARE_LIST(config, "flop_list").set_original("partner_flop");

	/* internal ram */
	RAM(config, m_ram);
	m_ram->set_default_size("64K");
	m_ram->set_default_value(0x00);
}

/* ROM definition */
ROM_START( partner )
	ROM_REGION( 0xA000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "partner.rom", 0x0000, 0x2000, CRC(be1eaa10) SHA1(f9658d8055bf434240ec020d7892ea98cb5cbb76))
	ROM_LOAD( "basic.rom",   0x2000, 0x2000, CRC(1e9be0ec) SHA1(2c431f487cffddaac8413efddfc0527ad595f03b))
	ROM_LOAD( "mcpg.rom",    0x4000, 0x0800, CRC(3401225c) SHA1(6c252393ee73ed1a53d3e583547d86ab6718a533))
	ROM_LOAD( "fdd.rom",     0x6000, 0x0800, CRC(8ca350b5) SHA1(76fc92298726fb2840f4c19d7edc860d1ed86356))
	ROM_LOAD( "font.rom",    0x8000, 0x2000, CRC(dc4f1723) SHA1(6b8d5efb403cf0aeb3fd3197a0529d23c8e2f93c))

	ROM_REGION(0x2000, "chargen",0)
	ROM_LOAD ("partner.d25", 0x0000, 0x2000, CRC(2705f726) SHA1(3d7b33901ef098a405d7ddad924ba9677f6a9b15))
ROM_END

/* Driver */
//    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT    CLASS          INIT          COMPANY       FULLNAME         FLAGS
COMP( 1987, partner, radio86, 0,      partner, partner, partner_state, init_partner, "SAM SKB VM", "Partner-01.01", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



paso1600.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
/***************************************************************************

    Toshiba Pasopia 1600

    TODO:
    - charset ROM is WRONG! (needs a 8x16 or even a 16x16 one)
    - identify fdc type (needs a working floppy image)

****************************************************************************/

#include "emu.h"
#include "cpu/i86/i86.h"
#include "machine/am9517a.h"
#include "machine/pic8259.h"
#include "video/mc6845.h"
#include "emupal.h"
#include "screen.h"


namespace {

class paso1600_state : public driver_device
{
public:
	paso1600_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pic(*this, "pic8259")
		, m_dma(*this, "8237dma")
		, m_crtc(*this, "crtc")
		, m_p_vram(*this, "vram")
		, m_p_gvram(*this, "gvram")
		, m_p_chargen(*this, "chargen")
		, m_p_pcg(*this, "pcg")
		, m_gfxdecode(*this, "gfxdecode")
		, m_palette(*this, "palette")
	{
	}

	void paso1600(machine_config &config);

private:
	uint8_t paso1600_pcg_r(offs_t offset);
	void paso1600_pcg_w(offs_t offset, uint8_t data);
	void paso1600_6845_address_w(uint8_t data);
	void paso1600_6845_data_w(uint8_t data);
	uint8_t key_r(offs_t offset);
	void key_w(offs_t offset, uint8_t data);
	uint16_t test_hi_r();
	uint8_t pc_dma_read_byte(offs_t offset);
	void pc_dma_write_byte(offs_t offset, uint8_t data);
	uint32_t screen_update_paso1600(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void paso1600_io(address_map &map) ATTR_COLD;
	void paso1600_map(address_map &map) ATTR_COLD;

	uint8_t m_crtc_vreg[0x100]{}, m_crtc_index = 0;
	struct {
		uint8_t portb = 0;
	} m_keyb;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_device<pic8259_device> m_pic;
	required_device<am9517a_device> m_dma;
	required_device<mc6845_device> m_crtc;
	required_shared_ptr<uint16_t> m_p_vram;
	required_shared_ptr<uint16_t> m_p_gvram;
	required_region_ptr<u8> m_p_chargen;
	required_region_ptr<u8> m_p_pcg;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<palette_device> m_palette;
};

#define mc6845_h_char_total     (m_crtc_vreg[0])
#define mc6845_h_display        (m_crtc_vreg[1])
#define mc6845_h_sync_pos       (m_crtc_vreg[2])
#define mc6845_sync_width       (m_crtc_vreg[3])
#define mc6845_v_char_total     (m_crtc_vreg[4])
#define mc6845_v_total_adj      (m_crtc_vreg[5])
#define mc6845_v_display        (m_crtc_vreg[6])
#define mc6845_v_sync_pos       (m_crtc_vreg[7])
#define mc6845_mode_ctrl        (m_crtc_vreg[8])
#define mc6845_tile_height      (m_crtc_vreg[9]+1)
#define mc6845_cursor_y_start   (m_crtc_vreg[0x0a])
#define mc6845_cursor_y_end     (m_crtc_vreg[0x0b])
#define mc6845_start_addr       (((m_crtc_vreg[0x0c]<<8) & 0x3f00) | (m_crtc_vreg[0x0d] & 0xff))
#define mc6845_cursor_addr      (((m_crtc_vreg[0x0e]<<8) & 0x3f00) | (m_crtc_vreg[0x0f] & 0xff))
#define mc6845_light_pen_addr   (((m_crtc_vreg[0x10]<<8) & 0x3f00) | (m_crtc_vreg[0x11] & 0xff))
#define mc6845_update_addr      (((m_crtc_vreg[0x12]<<8) & 0x3f00) | (m_crtc_vreg[0x13] & 0xff))


uint32_t paso1600_state::screen_update_paso1600(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
#if 0
	static int test_x;

	if(machine().input().code_pressed(KEYCODE_Z))
		test_x++;

	if(machine().input().code_pressed(KEYCODE_X))
		test_x--;

	popmessage("%d",test_x);

	uint32_t count = 0;

	for(int y=0;y<475;y++)
	{
		count &= 0xffff;

		for(int x=0;x<test_x/16;x++)
		{
			for(int xi=0;xi<16;xi++)
			{
				int pen = (m_p_gvram[count] >> xi) & 1;

				if(y < 475 && x*16+xi < 640) /* TODO: safety check */
					bitmap.pix(y, x*16+xi) = m_palette->pen(pen);
			}

			count++;
		}
	}
#endif

//  popmessage("%d %d %d",mc6845_h_display,mc6845_v_display,mc6845_tile_height);

	for(int y=0;y<mc6845_v_display;y++)
	{
		for(int x=0;x<mc6845_h_display;x++)
		{
			int tile = m_p_vram[x+y*mc6845_h_display] & 0xff;
			int color = (m_p_vram[x+y*mc6845_h_display] & 0x700) >> 8;

			for(int yi=0;yi<19;yi++)
			{
				for(int xi=0;xi<8;xi++)
				{
					int pen = (m_p_chargen[tile*8+(yi >> 1)] >> (7-xi) & 1) ? color : -1;

					if(yi & 0x10)
						pen = -1;

					if(pen != -1)
						if(y*19 < 475 && x*8+xi < 640) /* TODO: safety check */
							bitmap.pix(y*19+yi, x*8+xi) = m_palette->pen(pen);
				}
			}
		}
	}

	/* quick and dirty way to do the cursor */
	if(0)
	for(int yi=0;yi<mc6845_tile_height;yi++)
	{
		for(int xi=0;xi<8;xi++)
		{
			if((mc6845_cursor_y_start & 0x60) != 0x20 && mc6845_h_display)
			{
				int x = mc6845_cursor_addr % mc6845_h_display;
				int y = mc6845_cursor_addr / mc6845_h_display;
				bitmap.pix(y*mc6845_tile_height+yi, x*8+xi) = m_palette->pen(7);
			}
		}
	}

	return 0;
}

uint8_t paso1600_state::paso1600_pcg_r(offs_t offset)
{
	return m_p_pcg[offset];
}

void paso1600_state::paso1600_pcg_w(offs_t offset, uint8_t data)
{
	m_p_pcg[offset] = data;
	m_gfxdecode->gfx(0)->mark_dirty(offset >> 3);
}

void paso1600_state::paso1600_6845_address_w(uint8_t data)
{
	m_crtc_index = data;
	m_crtc->address_w(data);
}

void paso1600_state::paso1600_6845_data_w(uint8_t data)
{
	m_crtc_vreg[m_crtc_index] = data;
	m_crtc->register_w(data);
}

uint8_t paso1600_state::key_r(offs_t offset)
{
	switch(offset)
	{
		case 3:
			if(m_keyb.portb == 1)
				return 0;
	}

	return 0xff;
}

void paso1600_state::key_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
		case 3: m_keyb.portb = data; break;
	}
}

uint16_t  paso1600_state::test_hi_r()
{
	return 0xffff;
}

void paso1600_state::paso1600_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x7ffff).ram();
	map(0xb0000, 0xb0fff).ram().share("vram"); // tvram
	map(0xbfff0, 0xbffff).rw(FUNC(paso1600_state::paso1600_pcg_r), FUNC(paso1600_state::paso1600_pcg_w));
	map(0xc0000, 0xdffff).ram().share("gvram");// gvram
	map(0xe0000, 0xeffff).rom().region("kanji", 0);// kanji rom, banked via port 0x93
	map(0xfe000, 0xfffff).rom().region("ipl", 0);
}

void paso1600_state::paso1600_io(address_map &map)
{
	map.unmap_value_low();
	map(0x0000, 0x000f).rw(m_dma, FUNC(am9517a_device::read), FUNC(am9517a_device::write));
	map(0x0010, 0x0011).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write)); // i8259
	map(0x001a, 0x001b).r(FUNC(paso1600_state::test_hi_r)); // causes RAM error otherwise?
	map(0x0030, 0x0033).rw(FUNC(paso1600_state::key_r), FUNC(paso1600_state::key_w)); //UART keyboard?
	map(0x0048, 0x0049).r(FUNC(paso1600_state::test_hi_r));
	map(0x0090, 0x0090).r(m_crtc, FUNC(mc6845_device::status_r)).w(FUNC(paso1600_state::paso1600_6845_address_w));
	map(0x0091, 0x0091).r(m_crtc, FUNC(mc6845_device::register_r)).w(FUNC(paso1600_state::paso1600_6845_data_w));
//  map(0x00d8,0x00df) //fdc, unknown type
// other undefined ports: 18, 1C, 92
}

/* Input ports */
static INPUT_PORTS_START( paso1600 )
INPUT_PORTS_END

static const gfx_layout paso1600_charlayout =
{
	8, 8,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP8(0,1) },
	{ STEP8(0,8) },
	8*8
};

static GFXDECODE_START( gfx_paso1600 )
	GFXDECODE_ENTRY( "pcg", 0x0000, paso1600_charlayout, 0, 4 )
	GFXDECODE_ENTRY( "chargen", 0x0000, paso1600_charlayout, 0, 4 )
GFXDECODE_END


void paso1600_state::machine_start()
{
}


void paso1600_state::machine_reset()
{
}

uint8_t paso1600_state::pc_dma_read_byte(offs_t offset)
{
	//offs_t page_offset = (((offs_t) m_dma_offset[0][m_dma_channel]) << 16)
	//  & 0xFF0000;

	address_space &program = m_maincpu->space(AS_PROGRAM);

	return program.read_byte(offset);
}


void paso1600_state::pc_dma_write_byte(offs_t offset, uint8_t data)
{
	//offs_t page_offset = (((offs_t) m_dma_offset[0][m_dma_channel]) << 16)
	//  & 0xFF0000;
	address_space &program = m_maincpu->space(AS_PROGRAM);

	program.write_byte(offset, data);
}

void paso1600_state::paso1600(machine_config &config)
{
	/* basic machine hardware */
	I8086(config, m_maincpu, 16000000/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &paso1600_state::paso1600_map);
	m_maincpu->set_addrmap(AS_IO, &paso1600_state::paso1600_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259", FUNC(pic8259_device::inta_cb));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	screen.set_screen_update(FUNC(paso1600_state::screen_update_paso1600));
	screen.set_palette(m_palette);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_paso1600);
	PALETTE(config, m_palette).set_entries(8);

	/* Devices */
	mc6845_device &crtc(MC6845(config, "crtc", 16000000/4));    /* unknown variant, unknown clock, hand tuned to get ~60 fps */
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(8);

	PIC8259(config, m_pic, 0);
	m_pic->out_int_callback().set_inputline(m_maincpu, 0);

	AM9517A(config, m_dma, 16000000/4);
	m_dma->in_memr_callback().set(FUNC(paso1600_state::pc_dma_read_byte));
	m_dma->out_memw_callback().set(FUNC(paso1600_state::pc_dma_write_byte));
}

ROM_START( paso1600 )
	ROM_REGION16_LE(0x2000,"ipl", 0)
	ROM_LOAD( "ipl.rom", 0x0000, 0x2000, CRC(cee4ebb7) SHA1(c23b30f8dc51f96c1c00e28aab61e77b50d261f0))

	ROM_REGION(0x2000,"pcg", ROMREGION_ERASE00)

	ROM_REGION( 0x800, "chargen", ROMREGION_ERASEFF )
	ROM_LOAD( "5t_2716.bin", 0x0000, 0x0800, BAD_DUMP CRC(b5693720) SHA1(d25327dfaa40b0f4144698e3bad43125fd8e46d0)) //stolen from pasopia - label and location unknown

	ROM_REGION16_LE( 0x20000, "kanji", ROMREGION_ERASEFF )
	ROM_LOAD( "kanji.rom", 0x0000, 0x20000, NO_DUMP)
ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY    FULLNAME        FLAGS
COMP( 198?, paso1600, 0,      0,      paso1600, paso1600, paso1600_state, empty_init, "Toshiba", "Pasopia 1600", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



pasogo.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Peter Trauner
/******************************************************************************

Pasogo Handheld Console
Koei 1996

This is a handheld console made by Koei in 1996. All of the games available on
this console are variations of the game 'Go'


Main PCB Layout
---------------
PT-GMAIN01D
|--------------|------------||
|POWER    VOL  |            ||
|       LM2937*|    CART    ||
|CE-0702       |    SLOT    ||
|              |    CN1     ||
|              |MC14071B*   ||
|              |            ||
| PIEZO_SPKR   |------------||
|CN5           74HC04*       |
|    X2                      |
|  |-------|              CN4|
|  |VADEM  |                 |
|  |VG230  |                 |
|  |       |     HM514800*   |
|  |       |              CN3|
|  |-------|     HM514800*   |
|CN2*  X1                    |
|----------------------------|
Notes: (all ICs shown)

       VG230    - Vadem VG230 single-chip PC platform. Contains 16 MHz NEC uPD70116H V30HL CPU
                  (which is a high-speed low-power 8086 variation), IBM PC/XT-compatible core
                  logic, LCD controller (CGA/AT&T640x400), keyboard matrix scanner, dual PCMCIA
                  2.1 card controller, EMS 4.0 hardware support for up to 64MB, built-in timer
                  PIC/DMA/UART/RTC controllers. The clock input is 32.2200MHz. An internal divider
                  creates a 16.11MHz clock for the V30HL CPU.
       HM514800 - Hitachi HM514800 512k x8-bit DRAM
       MC14071  - Motorola MC14071 Quad 2-input OR gate
       74HC04   - 74HC04 Hex inverter
       LM2937   - Texas Instruments LM2937ES-5 voltage regulator (Max 26V input, 5V output at 500mA)
       CE-0702  - TDK CE-0702 DC-DC converter for LCD in SIP9 package (5V input, -24V output at 25mA)
       POWER    - 9V DC power input from AC/DC adapter
       VOL      - Volume pot
       CN5      - 5 pin connector for 4-way control pad (up/down/left/right/ground)
       CN4      - 5 pin connector for on/off switch and 2 buttons
       CN3      - 2 pin power input from 6x AA-battery compartment (input voltage is 9V DC)
       CN2      - Flat cable connector for video out to LCD panel. When the LCD is powered on the pixels
                  are blue. The LCD panel PCB has part number 97-44264-8 LMG6912RPFC LMG6910RPGR
                  and contains the following parts.....
                  Matsushita 53008HEB-8
                  Sanyo LA6324N quad operational amplifier
                  Hitachi BD66285BFC LCD controller IC (x3)
                  Hitachi BD66284BFC LCD controller IC (x4)
                  The LCD flat cable has several wires but 2 of them have frequencies which measure
                  69.9161Hz and 16.7798kHz. These are assumed to be VSync and HSync
       CN1      - Cart slot
       X1       - Marked 322. Measures 32.21732MHz so this is a common 32.22MHz OSC.
       X2       - No markings. Measures 32.768kHz and used for the RTC
       *        - These parts are on the other side of the PCB


Carts
-----
All of the carts are identical. Most have only one surface mounted mask ROM. Either a
MX23C8100 (8M) or YRM0442 (4M). Some are populated with additional parts including a 62256
32kx8 SRAM, a 3v coin battery and a MM1081N reset chip plus a few resistors/capacitors and
a transistor. All parts are surface mounted.

PT-GMEM01B
PT-GMEM01C
|---------------|
|--     CR2016  |
|-- MM1081      |
|--             |
|--             |
|--       62256 |
|--             |
|--             |
|--    MX23C8100|
|-- or YRM0442  |
|---------------|
Notes:
      Carts containing just one mask ROM are KS-1002, KS-1003, KS1004 and KS-1009
      Carts containing RAM, battery and reset chip are KS-1001 and KS1010
      Carts KS-1005, KS-1006, KS-1007 and KS-1008 probably exist but are unknown.

===========================================================================================

 PeT mess@utanet.at march 2008

although it is very related to standard pc hardware, it is different enough
to make the standard pc driver one level more complex, so own driver

TODO:
- Make a separate device of the Vadem VG230 core (it is also used in the HP
  OmniGo 100 and 120).

******************************************************************************/


#include "emu.h"
#include "cpu/nec/nec.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "machine/bankdev.h"
#include "machine/genpc.h"
#include "machine/timer.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"

/*
  rtc interrupt irq 2
 */


namespace {

class pasogo_state : public driver_device
{
public:
	pasogo_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cart(*this, "cartslot")
		, m_ems(*this, "ems")
		, m_vram(*this, "vram")
		, m_palette(*this, "palette")
	{ }

	void pasogo(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(contrast);

private:
	required_device<cpu_device> m_maincpu;
	required_device<generic_slot_device> m_cart;
	required_device<address_map_bank_device> m_ems;
	required_shared_ptr<uint16_t> m_vram;
	required_device<palette_device> m_palette;

	uint16_t ems_r(offs_t offset, uint16_t mem_mask);
	void ems_w(offs_t offset, uint16_t data, uint16_t mem_mask);
	uint16_t emsram_r(offs_t offset, uint16_t mem_mask);
	void emsram_w(offs_t offset, uint16_t data, uint16_t mem_mask);
	uint8_t vg230_io_r(offs_t offset);
	void vg230_io_w(offs_t offset, uint8_t data);

	struct
	{
		uint8_t index = 0;
		uint8_t data[0x100]{};
		struct {
			uint16_t data = 0;
		} bios_timer; // 1.19 MHz tclk signal
		struct {
			int seconds = 0, minutes = 0, hours = 0, days = 0;
			int alarm_seconds = 0, alarm_minutes = 0, alarm_hours = 0, alarm_days = 0;

			int onehertz_interrupt_on = 0;
			int onehertz_interrupt_request = 0;
			int alarm_interrupt_on = 0;
			int alarm_interrupt_request = 0;
		} rtc;
		struct {
			int write_protected = 0;
		} pmu;
	} m_vg230;

	void machine_reset() override ATTR_COLD;
	void machine_start() override ATTR_COLD;

	uint32_t screen_update_pasogo(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	INTERRUPT_GEN_MEMBER(pasogo_interrupt);
	TIMER_DEVICE_CALLBACK_MEMBER(vg230_timer);

	memory_region *m_cart_rom = nullptr;
	uint8_t m_ems_index = 0;
	uint16_t m_ems_bank[28]{};
	void emsbank_map(address_map &map) ATTR_COLD;
	void pasogo_io(address_map &map) ATTR_COLD;
	void pasogo_mem(address_map &map) ATTR_COLD;
};


TIMER_DEVICE_CALLBACK_MEMBER(pasogo_state::vg230_timer)
{
	m_vg230.rtc.seconds += 1;
	if (m_vg230.rtc.seconds >= 60)
	{
		m_vg230.rtc.seconds = 0;
		m_vg230.rtc.minutes += 1;
		if (m_vg230.rtc.minutes >= 60)
		{
			m_vg230.rtc.minutes = 0;
			m_vg230.rtc.hours += 1;
			if (m_vg230.rtc.hours >= 24)
			{
				m_vg230.rtc.hours = 0;
				m_vg230.rtc.days = (m_vg230.rtc.days + 1) & 0xfff;
			}
		}
	}

	if (m_vg230.rtc.seconds == m_vg230.rtc.alarm_seconds
		&& m_vg230.rtc.minutes == m_vg230.rtc.alarm_minutes
		&& m_vg230.rtc.hours == m_vg230.rtc.alarm_hours
		&& (m_vg230.rtc.days & 0x1f) == m_vg230.rtc.alarm_hours)
	{
		// generate alarm
	}
}

void pasogo_state::machine_start()
{
	system_time systime;

	m_vg230 = decltype(m_vg230)();
	m_vg230.pmu.write_protected = true;
	machine().base_datetime(systime);

	m_vg230.rtc.seconds = systime.local_time.second;
	m_vg230.rtc.minutes = systime.local_time.minute;
	m_vg230.rtc.hours = systime.local_time.hour;
	m_vg230.rtc.days = 0;

	m_vg230.bios_timer.data=0x7200; // HACK
}

uint8_t pasogo_state::vg230_io_r(offs_t offset)
{
	int log = true;
	uint8_t data = 0;

	m_vg230.bios_timer.data += 0x100; //HACK
	if (offset&1)
	{
		data = m_vg230.data[m_vg230.index];
		switch (m_vg230.index)
		{
			case 0x09:
				break;

			case 0x0a:
				if (m_vg230.data[9] & 1)
					data=ioport("JOY")->read();
				else
					data = 0xff;
				break;

			case 0x30:
				data = m_vg230.bios_timer.data & 0xff;
				break;

			case 0x31:
				data = m_vg230.bios_timer.data >> 8;
				log = false;
				break;

			case 0x70:
				data = m_vg230.rtc.seconds;
				log = false;
				break;

			case 0x71:
				data = m_vg230.rtc.minutes;
				log = false;
				break;

			case 0x72:
				data = m_vg230.rtc.hours;
				log = false;
				break;

			case 0x73:
				data = m_vg230.rtc.days;
				break;

			case 0x74:
				data = m_vg230.rtc.days >> 8;
				break;

			case 0x79:
				// rtc mode
				log = false;
				break;

			case 0x7a:
				// rtc status
				data &= ~3;
				if (m_vg230.rtc.alarm_interrupt_request)
					data |= 1<<1;
				if (m_vg230.rtc.onehertz_interrupt_request)
					data |= 1<<0;
				break;

			case 0xc1:
				data &= ~1;
				if (m_vg230.pmu.write_protected)
					data |= 1;
				m_vg230.pmu.write_protected = false;
				log = false;
				break;
		}

		if (log)
			logerror("%.5x vg230 %02x read %.2x\n",(int) m_maincpu->pc(), m_vg230.index, data);
		//    data=machine.root_device().memregion("maincpu")->base()[0x4000+offset];
	}
	else
		data = m_vg230.index;

	return data;
}


void pasogo_state::vg230_io_w(offs_t offset, uint8_t data)
{
	int log = true;

	if (offset & 1)
	{
		//  machine.root_device().memregion("maincpu")->base()[0x4000+offset]=data;
		m_vg230.data[m_vg230.index] = data;
		switch (m_vg230.index)
		{
			case 0x09:
				break;

			case 0x70:
				m_vg230.rtc.seconds = data & 0x3f;
				break;

			case 0x71:
				m_vg230.rtc.minutes = data & 0x3f;
				break;

			case 0x72:
				m_vg230.rtc.hours = data & 0x1f;
				break;

			case 0x73:
				m_vg230.rtc.days = (m_vg230.rtc.days & ~0xff) | data;
				break;

			case 0x74:
				m_vg230.rtc.days = (m_vg230.rtc.days & 0xff) | ((data & 0xf) << 8);
				break;

			case 0x75:
				m_vg230.rtc.alarm_seconds = data & 0x3f;
				break;

			case 0x76:
				m_vg230.rtc.alarm_minutes = data & 0x3f;
				break;

			case 0x77:
				m_vg230.rtc.alarm_hours = data & 0x1f;
				break;

			case 0x78:
				m_vg230.rtc.alarm_days = data & 0x1f;
				break;

			case 0x79:
				m_vg230.rtc.onehertz_interrupt_on = data & 1;
				m_vg230.rtc.alarm_interrupt_on = data & 2;
				log = false;
				break;

			case 0x7a:
				if (data & 2)
				{
					m_vg230.rtc.alarm_interrupt_request = false;
					m_vg230.rtc.onehertz_interrupt_request = false; /* update interrupt */
				}
				break;
		}

		if (log)
			logerror("%.5x vg230 %02x write %.2x\n", (int)m_maincpu->pc(), m_vg230.index, data);
	}
	else
		m_vg230.index = data;
}


uint16_t pasogo_state::ems_r(offs_t offset, uint16_t mem_mask)
{
	uint8_t data = 0;
	uint8_t index;

	switch (offset)
	{
		case 0:
			data = m_ems_index;
			break;

		case 1:
			index = (m_ems_index >> 2) & 0x1f;
			data = m_ems_bank[index];
			break;
	}
	return data;
}


void pasogo_state::ems_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	uint8_t index;

	switch (offset)
	{
	case 0:
		m_ems_index = data;
		break;

	case 1:
		index = (m_ems_index >> 2) & 0x1f;
		if((index & ~1) == 10)
		{
			logerror("EMS mapping of CGA framebuffer\n");
			break;
		}
		else if(index >= 28)
		{
			logerror("EMS index out of range\n");
			break;
		}
		COMBINE_DATA(&m_ems_bank[index]);
		break;
	}
}

uint16_t pasogo_state::emsram_r(offs_t offset, uint16_t mem_mask)
{
	m_ems->set_bank(m_ems_bank[(offset >> 13) & 0x1f] & 0x7fff);
	return m_ems->read16(offset & 0x1fff, mem_mask);
}

void pasogo_state::emsram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	m_ems->set_bank(m_ems_bank[(offset >> 13) & 0x1f] & 0x7fff);
	m_ems->write16(offset & 0x1fff, data, mem_mask);
}

void pasogo_state::emsbank_map(address_map &map)
{
	map(0x04080000, 0x040fffff).ram();
	map(0x08000000, 0x080fffff).bankr("bank27");
	map(0x10000000, 0x1000ffff).ram(); // cart ram?
}

void pasogo_state::pasogo_mem(address_map &map)
{
	map(0x80000, 0xeffff).rw(FUNC(pasogo_state::emsram_r), FUNC(pasogo_state::emsram_w));
	map(0xb8000, 0xbffff).ram().share("vram");
	map(0xf0000, 0xfffff).bankr("bank27");
}


void pasogo_state::pasogo_io(address_map &map)
{
	map(0x0000, 0x00ff).m("mb", FUNC(ibm5160_mb_device::map));
	map(0x0026, 0x0027).rw(FUNC(pasogo_state::vg230_io_r), FUNC(pasogo_state::vg230_io_w));
	map(0x006c, 0x006f).rw(FUNC(pasogo_state::ems_r), FUNC(pasogo_state::ems_w));
}


static INPUT_PORTS_START( pasogo )
	PORT_START("JOY")
//  PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SELECT)  PORT_NAME("select")
//  PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START) PORT_NAME("start")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("O") /*?*/
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("X") /*?*/
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_UP   )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("a") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("b") PORT_CODE(KEYCODE_B)
	PORT_START("COLOR")
	PORT_CONFNAME(0x01, 0x01, "Contrast") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pasogo_state::contrast), 0)
	PORT_CONFSETTING(0x00, "Actual")
	PORT_CONFSETTING(0x01, "Enhanced")
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(pasogo_state::contrast)
{
	if(newval)
	{
		m_palette->set_pen_color(0, rgb_t(80, 130, 130));
		m_palette->set_pen_color(1, rgb_t(40, 60, 140));
	}
	else
	{
		m_palette->set_pen_color(0, rgb_t(100, 110, 100));
		m_palette->set_pen_color(1, rgb_t(90, 80, 110));
	}
}

uint32_t pasogo_state::screen_update_pasogo(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint8_t const *const vram = (uint8_t *)m_vram.target();
	for (int y=0; y<240; y++)
	{
		for (int x=0; x<(320/8); x++)
		{
			int const a = (y & 3) * 0x2000;
			uint8_t const d1 = vram[a + (y >> 2) * 80 + x];
			uint16_t *line = &bitmap.pix(y, x << 3);
			*line++ = BIT(d1, 7);
			*line++ = BIT(d1, 6);
			*line++ = BIT(d1, 5);
			*line++ = BIT(d1, 4);
			*line++ = BIT(d1, 3);
			*line++ = BIT(d1, 2);
			*line++ = BIT(d1, 1);
			*line++ = BIT(d1, 0);
		}
	}
	return 0;
}

INTERRUPT_GEN_MEMBER(pasogo_state::pasogo_interrupt)
{
//  m_maincpu->pulse_input_line(UPD7810_INTFE1, attotime::zero);
}

void pasogo_state::machine_reset()
{
	std::string region_tag;
	ioport_port *color = ioport("COLOR");
	m_cart_rom = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());
	if (!m_cart_rom) // even with mandatory carts, debug will crash without anything at the boot vector
		m_cart_rom = memregion("empty");

	membank("bank27")->set_base(m_cart_rom->base());
	m_ems_index = 0;
	memset(m_ems_bank, 0, sizeof(m_ems_bank));
	contrast(*color->fields().first(), 0, 0, color->read());
}

void pasogo_state::pasogo(machine_config &config)
{
	V30(config, m_maincpu, XTAL(32'220'000)/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &pasogo_state::pasogo_mem);
	m_maincpu->set_addrmap(AS_IO, &pasogo_state::pasogo_io);
	m_maincpu->set_vblank_int("screen", FUNC(pasogo_state::pasogo_interrupt));
	m_maincpu->set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	ADDRESS_MAP_BANK(config, "ems").set_map(&pasogo_state::emsbank_map).set_options(ENDIANNESS_LITTLE, 16, 32, 0x4000);

	ibm5160_mb_device &mb(IBM5160_MOTHERBOARD(config, "mb", 0));
	mb.set_cputag(m_maincpu);
	mb.int_callback().set_inputline(m_maincpu, 0);
	mb.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);

	RAM(config, RAM_TAG).set_default_size("512K");

	// It's a CGA device right so lets use isa_cga!  Well, not so much.
	// The carts use vg230 specific registers and mostly ignore the mc6845.
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_size(320, 240);
	screen.set_visarea(0, 320-1, 0, 240-1);
	screen.set_screen_update(FUNC(pasogo_state::screen_update_pasogo));
	screen.set_palette(m_palette);
	PALETTE(config, m_palette).set_entries(2);

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "pasogo_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_must_be_loaded(true);

	SOFTWARE_LIST(config, "cart_list").set_original("pasogo");

	TIMER(config, "vg230_timer").configure_periodic(FUNC(pasogo_state::vg230_timer), attotime::from_hz(1));
}

ROM_START( pasogo )
	ROM_REGION( 0x10000, "empty", ROMREGION_ERASEFF )
ROM_END

} // anonymous namespace


//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY  FULLNAME  FLAGS
CONS( 1996, pasogo, 0,      0,      pasogo,  pasogo, pasogo_state, empty_init, "KOEI",  "PasoGo", MACHINE_NO_SOUND|MACHINE_NOT_WORKING)



pasopia.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese, AJR
/***************************************************************************

    Toshiba Pasopia

    Cassette works.
    Sound uses the beeper, and works. Try SOUND a,b where a=1-82, b=1-255.

    TODO:
    - machine emulation needs merging with Pasopia 7 (video emulation is
      completely different tho)
    - Centronics printer interface
    - FDC and other I/O expansions


****************************************************************************/

#include "emu.h"
#include "pasopia.h"

#include "bus/pasopia/pac2.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/i8255.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "video/mc6845.h"
#include "imagedev/cassette.h"
#include "sound/spkrdev.h"
#include "speaker.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"

class pasopia_state : public driver_device
{
public:
	pasopia_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_p_basic(*this, "basic")
		, m_p_chargen(*this, "chargen")
		, m_p_ram(*this, "ram")
		, m_ppi0(*this, "ppi0")
		, m_ppi1(*this, "ppi1")
		, m_ppi2(*this, "ppi2")
		, m_ctc(*this, "ctc")
		, m_pio(*this, "pio")
		, m_crtc(*this, "crtc")
		, m_palette(*this, "palette")
		, m_io_keyboard(*this, "KEY.%d", 0)
		, m_cass(*this, "cassette")
		, m_rs232(*this, "rs232")
		, m_speaker(*this, "speaker")
	{ }

	void pasopia(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void pasopia_ctrl_w(u8 data);
	u8 memory_r(offs_t offset);
	void vram_addr_lo_w(u8 data);
	void vram_latch_w(u8 data);
	u8 vram_latch_r();
	u8 portb_1_r();
	u8 portb_2_r();
	void porta_2_w(u8 data);
	void portc_2_w(u8 data);
	void vram_addr_hi_w(u8 data);
	void screen_mode_w(u8 data);
	u8 rombank_r();
	u8 keyb_r();
	void mux_w(u8 data);
	void speaker_w(int state);
	MC6845_UPDATE_ROW(crtc_update_row);
	TIMER_CALLBACK_MEMBER(pio_timer);

	void pasopia_io(address_map &map) ATTR_COLD;
	void pasopia_map(address_map &map) ATTR_COLD;

	u8 m_hblank = 0;
	u16 m_vram_addr = 0;
	u16 m_vram_latch = 0;
	u8 m_gfx_mode = 0;
	u8 m_mux_data = 0;
	u8 m_porta_2 = 0;
	bool m_video_wl = false;
	bool m_ram_bank = false;
	bool m_spr_sw = false;
	u8 m_dclr = 0;
	emu_timer *m_pio_timer = nullptr;
	std::unique_ptr<u16[]> m_p_vram;

	required_device<z80_device> m_maincpu;
	required_region_ptr<u8> m_p_basic;
	required_region_ptr<u8> m_p_chargen;
	required_shared_ptr<u8> m_p_ram;
	required_device<i8255_device> m_ppi0;
	required_device<i8255_device> m_ppi1;
	required_device<i8255_device> m_ppi2;
	required_device<z80ctc_device> m_ctc;
	required_device<z80pio_device> m_pio;
	required_device<mc6845_device> m_crtc;
	required_device<palette_device> m_palette;
	required_ioport_array<12> m_io_keyboard;
	required_device<cassette_image_device> m_cass;
	required_device<rs232_port_device> m_rs232;
	required_device<speaker_sound_device> m_speaker;
};

// needed to scan the keyboard, as the pio emulation doesn't do it.
TIMER_CALLBACK_MEMBER( pasopia_state::pio_timer )
{
	m_pio->port_b_write(keyb_r());
}

MC6845_UPDATE_ROW( pasopia_state::crtc_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	rgb_t dclr = palette[m_dclr];
	const rgb_t bclr = palette[m_gfx_mode & 0x07];
	u32 *p = &bitmap.pix(y);
	const bool text = (m_gfx_mode & 0xc0) == 0;
	const u16 *const vram = &m_p_vram[text ? 0 : u16(BIT(m_gfx_mode, 7) ? ra & 7 : ra & 6) << 11];

	for (u16 x = 0; x < x_count; x++)
	{
		const u16 chr = vram[(ma + x) & 0x7ff];
		if ((chr & 0x1f8) == 0x0f8)
		{
			m_dclr = chr & 0x007;
			dclr = palette[m_dclr];
		}

		if (x == cursor_x)
		{
			// Cursor is always white
			std::fill_n(p, 8, palette[7]);
			p += 8;
		}
		else if (BIT(m_gfx_mode, 6) && BIT(chr, 8))
		{
			// DGR graphic characters: 3-bit colors for each half
			const rgb_t uclr(palette[BIT(chr, 4, 3)]);
			const rgb_t lclr(palette[BIT(chr, 0, 3)]);

			std::fill_n(p, 4, uclr);
			p += 4;
			std::fill_n(p, 4, lclr);
			p += 4;
		}
		else if (BIT(ra, 3) || (chr & 0x1f8) == 0x0f8)
		{
			std::fill_n(p, 8, bclr);
			p += 8;
		}
		else
		{
			// DHR graphic characters, normal or inverted text
			u8 gfx = BIT(chr, 8) && BIT(m_gfx_mode, 7) ? (chr & 0xff) : m_p_chargen[((chr & 0xff)<<3) | (ra & 7)];
			if (BIT(chr, 8) && text)
				gfx ^= 0xff;

			for (u8 xi = 0; xi < 8; xi++)
				*p++ = BIT(gfx, 7-xi) ? dclr : bclr;
		}
	}
}

void pasopia_state::pasopia_ctrl_w(u8 data)
{
	m_ram_bank = BIT(data, 1);
}

u8 pasopia_state::memory_r(offs_t offset)
{
	if (offset < 0x8000 && !m_ram_bank)
		return m_p_basic[offset];
	else
		return m_p_ram[offset];
}

void pasopia_state::pasopia_map(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(pasopia_state::memory_r));
	map(0x0000, 0xffff).writeonly().share("ram");
}


void pasopia_state::pasopia_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x03).rw(m_ppi0, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x08, 0x0b).rw(m_ppi1, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x10, 0x10).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x11, 0x11).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x18, 0x1b).rw("dtfcst", FUNC(pasopia_pac2_slot_device::read), FUNC(pasopia_pac2_slot_device::write));
	map(0x1c, 0x1f).rw("dtfunt", FUNC(pasopia_pac2_slot_device::read), FUNC(pasopia_pac2_slot_device::write));
	map(0x20, 0x23).rw(m_ppi2, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x28, 0x2b).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x30, 0x33).rw(m_pio, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
//  0x38 printer
	map(0x3c, 0x3c).w(FUNC(pasopia_state::pasopia_ctrl_w));
}

/* Input ports */
static INPUT_PORTS_START( pasopia )
	PASOPIA_KEYBOARD
INPUT_PORTS_END

void pasopia_state::machine_start()
{
	m_p_vram = make_unique_clear<u16[]>(0x4000);

	m_pio_timer = timer_alloc(FUNC(pasopia_state::pio_timer), this);
	m_pio_timer->adjust(attotime::from_hz(50), 0, attotime::from_hz(50));

	m_hblank = 0;
	m_spr_sw = false;
	m_dclr = 7;

	save_item(NAME(m_hblank));
	save_item(NAME(m_vram_addr));
	save_item(NAME(m_vram_latch));
	save_item(NAME(m_gfx_mode));
	save_item(NAME(m_mux_data));
	save_item(NAME(m_porta_2));
	save_item(NAME(m_video_wl));
	save_item(NAME(m_ram_bank));
	save_item(NAME(m_spr_sw));
	save_item(NAME(m_dclr));
	save_pointer(NAME(m_p_vram), 0x4000);
}

void pasopia_state::machine_reset()
{
	m_porta_2 = 0xFF;
	m_cass->change_state(CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
	m_ram_bank = false;
}

void pasopia_state::vram_addr_lo_w(u8 data)
{
	m_vram_addr = (m_vram_addr & 0x3f00) | data;
	if (!m_video_wl)
		m_p_vram[m_vram_addr] = m_vram_latch;
}

void pasopia_state::vram_latch_w(u8 data)
{
	m_vram_latch = (m_vram_latch & 0x100) | data;
	if (!m_video_wl)
		m_p_vram[m_vram_addr] = m_vram_latch;
}

u8 pasopia_state::vram_latch_r()
{
	return m_p_vram[m_vram_addr] & 0xff;
}

u8 pasopia_state::portb_1_r()
{
	/*
	x--- ---- attribute latch
	-x-- ---- hblank
	--x- ---- vblank
	---x ---- LCD system mode, active low
	*/
	u8 grph_latch,lcd_mode;

	m_hblank ^= 0x40; //TODO
	grph_latch = (m_p_vram[m_vram_addr] & 0x100) >> 1;
	lcd_mode = 0x10;

	return m_hblank | lcd_mode | grph_latch; //bit 4: LCD mode
}

u8 pasopia_state::portb_2_r()
{
	u8 result = (m_cass->input() > +0.04) ? 0x20 : 0;
	result |= m_rs232->dcd_r() << 3;
	result |= m_rs232->dsr_r() << 2;
	result |= m_rs232->cts_r() << 1;
	result |= m_rs232->rxd_r() << 0;
	return result;
}

void pasopia_state::porta_2_w(u8 data)
{
	m_cass->output(BIT(data, 4) ? -1.0 : +1.0);
	u8 changed = data ^ m_porta_2;
	m_porta_2 = data;
	if (BIT(changed, 5))
	{
		m_cass->change_state(BIT(data, 5) ? CASSETTE_MOTOR_DISABLED : CASSETTE_MOTOR_ENABLED, CASSETTE_MASK_MOTOR);
	}
}

void pasopia_state::portc_2_w(u8 data)
{
	m_rs232->write_dtr(BIT(data, 6));
	m_rs232->write_rts(BIT(data, 5));
	m_rs232->write_txd(BIT(data, 4));
}

void pasopia_state::speaker_w(int state)
{
	if (state)
	{
		m_spr_sw ^= 1;
		if (BIT(m_mux_data, 7))
			m_speaker->level_w(m_spr_sw);
	}
}

void pasopia_state::vram_addr_hi_w(u8 data)
{
	m_video_wl = BIT(data, 6);
	m_vram_addr = (m_vram_addr & 0xff) | ((data & 0x3f) << 8);
	m_vram_latch = u16(data & 0x80) << 1 | (m_vram_latch & 0xff);
	if (!m_video_wl)
		m_p_vram[m_vram_addr] = m_vram_latch;
}

void pasopia_state::screen_mode_w(u8 data)
{
	if (BIT(m_gfx_mode, 5) != BIT(data, 5))
		m_crtc->set_unscaled_clock(14.318181_MHz_XTAL / (BIT(data, 5) ? 8 : 16));
	m_gfx_mode = data & 0xe7;
}

u8 pasopia_state::rombank_r()
{
	return (m_ram_bank) ? 4 : 0;
}

u8 pasopia_state::keyb_r()
{
	u8 data = 0xff;
	for (u8 i = 0; i < 3; i++)
		if (BIT(m_mux_data, i+4))
			for (u8 j = 0; j < 4; j++)
				if (BIT(m_mux_data, j))
					data &= m_io_keyboard[i*4+j]->read();

	return data;
}

void pasopia_state::mux_w(u8 data)
{
	m_mux_data = data;
	m_pio->port_b_write(keyb_r());
}

static const gfx_layout p7_chars_8x8 =
{
	8,8,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};

static GFXDECODE_START( gfx_pasopia )
	GFXDECODE_ENTRY( "chargen", 0x0000, p7_chars_8x8, 0, 4 )
GFXDECODE_END

static const z80_daisy_config pasopia_daisy[] =
{
	{ "ctc" },
	{ "pio" },
//  { "fdc" }, /* TODO */
	{ nullptr }
};



void pasopia_state::pasopia(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 15.9744_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &pasopia_state::pasopia_map);
	m_maincpu->set_addrmap(AS_IO, &pasopia_state::pasopia_io);
	m_maincpu->set_daisy_config(pasopia_daisy);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(14.318181_MHz_XTAL / 2, 456, 0, 296, 262, 0, 192);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	GFXDECODE(config, "gfxdecode", m_palette, gfx_pasopia);
	PALETTE(config, m_palette, palette_device::BRG_3BIT);

	/* Devices */
	HD6845S(config, m_crtc, 14.318181_MHz_XTAL / 16);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(pasopia_state::crtc_update_row));

	I8255A(config, m_ppi0);
	m_ppi0->out_pa_callback().set(FUNC(pasopia_state::vram_addr_lo_w));
	m_ppi0->out_pb_callback().set(FUNC(pasopia_state::vram_latch_w));
	m_ppi0->in_pc_callback().set(FUNC(pasopia_state::vram_latch_r));

	I8255A(config, m_ppi1);
	m_ppi1->out_pa_callback().set(FUNC(pasopia_state::screen_mode_w));
	m_ppi1->in_pb_callback().set(FUNC(pasopia_state::portb_1_r));
	m_ppi1->out_pc_callback().set(FUNC(pasopia_state::vram_addr_hi_w));

	I8255A(config, m_ppi2);
	m_ppi2->out_pa_callback().set(FUNC(pasopia_state::porta_2_w));
	m_ppi2->in_pb_callback().set(FUNC(pasopia_state::portb_2_r));
	m_ppi2->in_pc_callback().set(FUNC(pasopia_state::rombank_r));
	m_ppi2->out_pc_callback().set(FUNC(pasopia_state::portc_2_w));

	Z80CTC(config, m_ctc, 15.9744_MHz_XTAL / 4);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->zc_callback<1>().set(FUNC(pasopia_state::speaker_w));
	m_ctc->zc_callback<2>().set(m_ctc, FUNC(z80ctc_device::trg3));

	Z80PIO(config, m_pio, 15.9744_MHz_XTAL / 4);
	m_pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio->out_pa_callback().set(FUNC(pasopia_state::mux_w));
	m_pio->in_pb_callback().set(FUNC(pasopia_state::keyb_r));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cass->set_interface("pasopia_cass");

	PASOPIA_PAC2(config, "dtfcst", pac2_default_devices, nullptr); // "Data File Cassette"
	PASOPIA_PAC2(config, "dtfunt", pac2_default_devices, nullptr); // "Data File Unit"

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);

	SOFTWARE_LIST(config, "cass_list").set_original("pasopia_cass");
}

/* ROM definition */
ROM_START( pasopia )
	ROM_REGION( 0x8000, "basic", 0 )
	ROM_LOAD( "tbasic.rom", 0x0000, 0x8000, CRC(f53774ff) SHA1(bbec45a3bad8d184505cc6fe1f6e2e60a7fb53f2))

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "5t_2716.ic79", 0x0000, 0x0800, CRC(b5693720) SHA1(d25327dfaa40b0f4144698e3bad43125fd8e46d0))
ROM_END

/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY    FULLNAME   FLAGS
COMP( 1982, pasopia, 0,      0,      pasopia, pasopia, pasopia_state, empty_init, "Toshiba", "Personal Computer Pasopia PA7010", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



pasopia7.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
/**************************************************************************************************

    Toshiba Pasopia 7 (c) 1983 Toshiba

    Preliminary driver by Angelo Salese.
    2019-10-14 added cassette & beeper [Robbbert]

    Machine unusable due to issues with sound.
    Cassette works.
    Beeper is only used for the keyclick, and it works.

    TODO:
    - floppy support (but floppy images are unobtainable at current time).
    - SOUND command uses the SN76489 chip, however no sound is produced, and the next SOUND command
      freezes the machine.
    - LCD version has gfx bugs, it must use a different ROM charset for instance (apparently a 8 x 4
      one, 40/80 x 8 tilemap).
    - Allow BIN files to be loaded (via a rampac presumably).
    - Allow CAS files to be loaded.

    Reading fdc has been commented out, until the code can be modified to
    work with new upd765 (was causing a hang at boot).

    Schematics: https://archive.org/details/Io19839/page/n331 (fdc system not included)

***************************************************************************************************/

#include "emu.h"
#include "pasopia.h"

#include "bus/pasopia/pac2.h"
#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "machine/i8255.h"
#include "machine/upd765.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "sound/sn76496.h"
#include "sound/spkrdev.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

//#define VERBOSE 1
#include "logmacro.h"


namespace {

class pasopia7_state : public driver_device
{
public:
	pasopia7_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_banks(*this, "bank%u", 0U)
		, m_screen(*this, "screen")
		, m_ppi0(*this, "ppi0")
		, m_ppi1(*this, "ppi1")
		, m_ppi2(*this, "ppi2")
		, m_ctc(*this, "ctc")
		, m_pio(*this, "pio")
		, m_crtc(*this, "crtc")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:0:525hd")
		, m_sn1(*this, "sn1")
		, m_sn2(*this, "sn2")
		, m_palette(*this, "palette")
		, m_keyboard(*this, "KEY.%d", 0)
		, m_cass(*this, "cassette")
		, m_pac2(*this, "pac2")
		, m_speaker(*this, "speaker")
		, m_font_rom(*this, "font")
	{ }

	void p7_base(machine_config &config) ATTR_COLD;
	void p7_lcd(machine_config &config) ATTR_COLD;
	void p7_raster(machine_config &config) ATTR_COLD;

	void init_p7_lcd() ATTR_COLD;
	void init_p7_raster() ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

private:
	u8 vram_r(offs_t offset);
	void vram_w(offs_t offset, u8 data);
	void memory_ctrl_w(u8 data);
	void ram_bank_w(offs_t offset, u8 data);
	void pasopia7_6845_w(offs_t offset, u8 data);
	u8 io_r(offs_t offset);
	void io_w(offs_t offset, u8 data);
	u8 fdc_r(offs_t offset);
	void fdc_w(offs_t offset, u8 data);
	u8 keyb_r();
	void mux_w(u8 data);
	u8 crtc_portb_r();
	void screen_mode_w(u8 data);
	void plane_reg_w(u8 data);
	void video_attr_w(u8 data);
	void video_misc_w(u8 data);
	void nmi_mask_w(u8 data);
	u8 unk_r();
	u8 nmi_reg_r();
	void nmi_reg_w(u8 data);
	u8 nmi_porta_r();
	u8 nmi_portb_r();
	void speaker_w(int state);
	TIMER_CALLBACK_MEMBER(pio_timer);
	void p7_lcd_palette(palette_device &palette) const;
	MC6845_UPDATE_ROW(update_row);

	void pasopia7_io(address_map &map) ATTR_COLD;
	void pasopia7_mem(address_map &map) ATTR_COLD;

	void fdc_irq(bool state);
	void draw_cg4_line(bitmap_rgb32 &bitmap, int y, int yi, int width, int count);
	void draw_tv_line(bitmap_rgb32 &bitmap, int y, int yi, int width, int count, int cursor_x);
	void draw_mixed_line(bitmap_rgb32 &bitmap, int y, int yi, int width, int count, int cursor_x);

	std::unique_ptr<u8[]> m_work_ram;
	std::unique_ptr<u8[]> m_vram;
	u8 m_vram_sel = 0;
	u8 m_mio_sel = 0;
	std::unique_ptr<u8[]> m_p7_pal;
	u8 m_bank_reg = 0;
	u8 m_cursor_blink = 0;
	u8 m_plane_reg = 0;
	u8 m_attr_data = 0;
	u8 m_attr_wrap = 0;
	u8 m_attr_latch = 0;
	u8 m_pal_sel = 0;
	u8 m_x_width = 0;
	u8 m_gfx_mode = 0;
	u8 m_nmi_mask = 0;
	u8 m_nmi_enable_reg = 0;
	u8 m_nmi_trap = 0;
	u8 m_nmi_reset = 0;
	u8 m_screen_type = 0;
	void pasopia_nmi_trap();
	u8 m_mux_data = 0;
	u8 m_porta_2 = 0;
	bool m_spr_sw = false;
	emu_timer *m_pio_timer = nullptr;

	required_device<z80_device> m_maincpu;
	required_memory_bank_array<2> m_banks;
	required_device<screen_device> m_screen;
	required_device<i8255_device> m_ppi0;
	required_device<i8255_device> m_ppi1;
	required_device<i8255_device> m_ppi2;
	required_device<z80ctc_device> m_ctc;
	required_device<z80pio_device> m_pio;
	required_device<mc6845_device> m_crtc;
	required_device<upd765a_device> m_fdc;
	required_device<floppy_image_device> m_floppy;
	required_device<sn76489a_device> m_sn1;
	required_device<sn76489a_device> m_sn2;
	required_device<palette_device> m_palette;
	required_ioport_array<12> m_keyboard;
	required_device<cassette_image_device> m_cass;
	required_device<pasopia_pac2_slot_device> m_pac2;
	required_device<speaker_sound_device> m_speaker;
	required_region_ptr<u8> m_font_rom;
};

#define VDP_CLOCK 14.318181_MHz_XTAL / 16
#define LCD_CLOCK VDP_CLOCK/10

void pasopia7_state::machine_start()
{
	m_work_ram = std::make_unique<u8[]>(0x10000);
	std::fill(&m_work_ram[0], &m_work_ram[0x10000], 0xff);

	m_vram = make_unique_clear<u8[]>(0x10000);

	u8 *work_ram = m_work_ram.get();
	u8 *basic = memregion("basic")->base();
	u8 *bios = memregion("bios")->base();
	// 0000-3FFF
	m_banks[0]->configure_entry(0, bios);
	m_banks[0]->configure_entry(1, basic);
	m_banks[0]->configure_entry(2, work_ram);
	// 4000-7FFF
	m_banks[1]->configure_entry(0, bios);
	m_banks[1]->configure_entry(1, basic+0x4000);
	m_banks[1]->configure_entry(2, work_ram+0x4000);

	m_banks[0]->set_entry(0);
	m_banks[1]->set_entry(0);
}

// needed to scan the keyboard, as the pio emulation doesn't do it.
TIMER_CALLBACK_MEMBER( pasopia7_state::pio_timer )
{
	m_pio->port_b_write(keyb_r());
}

void pasopia7_state::video_start()
{
	m_p7_pal = std::make_unique<u8[]>(0x10);
}

void pasopia7_state::draw_cg4_line(bitmap_rgb32 &bitmap, int y, int yi, int width, int count)
{
	for (int x = 0; x < 8*width; x += 8)
	{
		for (int xi = 0; xi < 8; xi++)
		{
			int pen_b = BIT(m_vram[count + yi + 0x0000], 7 - xi);
			int pen_r = BIT(m_vram[count + yi + 0x4000], 7 - xi);
			int pen_g = 0;//(m_vram[count + yi + 0x8000], 7 - xi);

			int color =  pen_g<<2 | pen_r<<1 | pen_b<<0;

			bitmap.pix(y, x + xi) = m_palette->pen(color);
		}
		count += 8;
	}
}

void pasopia7_state::draw_tv_line(bitmap_rgb32 &bitmap, int y, int yi, int width, int count, int cursor_x)
{
	for (int x = 0; x < width; x++)
	{
		int tile = m_vram[count + 0x8000];
		int attr = m_vram[count + 0xc000];
		int color = attr & 7;

		for (int xi = 0; xi < 8; xi++)
		{
			int pen = BIT(m_font_rom[tile*8 + yi], 7 - xi) ? color : 0;

			bitmap.pix(y, x*8 + xi) = m_palette->pen(pen);
		}

		// draw cursor
		if (cursor_x == x)
		{
			for (int xc = 0; xc < 8; xc++)
			{
				bitmap.pix(y, x*8 + xc) = m_palette->pen(7);
			}
		}
		count += 8;
	}
}

void pasopia7_state::draw_mixed_line(bitmap_rgb32 &bitmap, int y, int yi, int width, int count, int cursor_x)
{
	for (int x = 0; x < width; x++)
	{
		int tile = m_vram[count + 0x8000];
		int attr = m_vram[count + 0xc000 + yi];

		if (attr & 0x80)
		{
			for (int xi = 0; xi < 8; xi++)
			{
				int pen_b = BIT(m_vram[count + yi + 0x0000], 7 - xi);
				int pen_r = BIT(m_vram[count + yi + 0x4000], 7 - xi);
				int pen_g = BIT(m_vram[count + yi + 0x8000], 7 - xi);

				int pen =  pen_g<<2 | pen_r<<1 | pen_b<<0;

				bitmap.pix(y, x*8 + xi) = m_palette->pen(pen);
			}
		}
		else
		{
			int color = attr & 7;

			for (int xi = 0; xi < 8; xi++)
			{
				int pen = BIT(m_font_rom[tile*8 + yi], 7 - xi) ? color : 0;

				bitmap.pix(y, x*8 + xi) = m_palette->pen(pen);
			}
		}

		// draw cursor
		if (cursor_x == x)
		{
			for(int xc = 0; xc < 8; xc++)
			{
				bitmap.pix(y, x*8 + xc) = m_palette->pen(7);
			}
		}

		count += 8;
	}
}

MC6845_UPDATE_ROW(pasopia7_state::update_row)
{
	if (m_gfx_mode)
	{
		draw_mixed_line(bitmap, y, ra, x_count, ma * 8, cursor_x);
	}
	else
	{
		draw_cg4_line(bitmap, y, ra, x_count, ma * 8);
		draw_tv_line(bitmap, y, ra, x_count, ma * 8, cursor_x);
	}
}

u8 pasopia7_state::vram_r(offs_t offset)
{
	if (m_vram_sel == 0)
		return m_work_ram[offset + 0x8000];

	if (m_pal_sel && (m_plane_reg & 0x70) == 0x00)
		return m_p7_pal[offset & 0xf];

	u8 res = 0xff;

	if ((m_plane_reg & 0x11) == 0x11)
		res &= m_vram[offset | 0x0000];
	if ((m_plane_reg & 0x22) == 0x22)
		res &= m_vram[offset | 0x4000];
	if ((m_plane_reg & 0x44) == 0x44)
	{
		res &= m_vram[offset | 0x8000];
		m_attr_latch = m_vram[offset | 0xc000] & 0x87;
	}

	return res;
}

void pasopia7_state::vram_w(offs_t offset, u8 data)
{
	if (m_vram_sel)
	{
		if (m_pal_sel && (m_plane_reg & 0x70) == 0x00)
		{
			m_p7_pal[offset & 0xf] = data & 0xf;
			return;
		}

		if (m_plane_reg & 0x10)
			m_vram[(offset & 0x3fff) | 0x0000] = (m_plane_reg & 1) ? data : 0xff;
		if (m_plane_reg & 0x20)
			m_vram[(offset & 0x3fff) | 0x4000] = (m_plane_reg & 2) ? data : 0xff;
		if (m_plane_reg & 0x40)
		{
			m_vram[(offset & 0x3fff) | 0x8000] = (m_plane_reg & 4) ? data : 0xff;
			m_attr_latch = m_attr_wrap ? m_attr_latch : m_attr_data;
			m_vram[(offset & 0x3fff) | 0xc000] = m_attr_latch;
		}
	}
	else
	{
		m_work_ram[offset+0x8000] = data;
	}
}

void pasopia7_state::memory_ctrl_w(u8 data)
{
	switch(data & 3)
	{
		case 0:
		case 3: //select Basic ROM
			m_banks[0]->set_entry(1);
			m_banks[1]->set_entry(1);
			break;
		case 1: //select Basic ROM + BIOS ROM
			m_banks[0]->set_entry(1);
			m_banks[1]->set_entry(0);
			break;
		case 2: //select Work RAM
			m_banks[0]->set_entry(2);
			m_banks[1]->set_entry(2);
			break;
	}

	m_bank_reg = data & 3;
	m_vram_sel = data & 4;
	m_mio_sel = data & 8;

	// bank4 is always RAM

	LOG("m_vram_sel=%02x\n", m_vram_sel);
}

/* writes always occurs to the RAM banks, even if the ROMs are selected. */
void pasopia7_state::ram_bank_w(offs_t offset, u8 data)
{
	m_work_ram[offset] = data;
}

void pasopia7_state::pasopia7_6845_w(offs_t offset, u8 data)
{
	if (offset == 0)
	{
		m_crtc->address_w(data);
	}
	else
	{
		m_crtc->register_w(data);

		/* double pump the pixel clock if we are in 640 x 200 mode */
		if (m_screen_type == 1) // raster
			m_crtc->set_unscaled_clock(m_x_width ? (VDP_CLOCK * 2) : VDP_CLOCK);
		else // lcd
			m_crtc->set_unscaled_clock(m_x_width ? (LCD_CLOCK * 2) : LCD_CLOCK);
	}
}

void pasopia7_state::pasopia_nmi_trap()
{
	if (m_nmi_enable_reg)
	{
		m_nmi_trap |= 2;

		if (m_nmi_mask == 0)
			m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
	}
}

[[maybe_unused]] u8 pasopia7_state::fdc_r(offs_t offset)
{
	switch(offset)
	{
		case 4: return m_fdc->msr_r();
		case 5: return m_fdc->fifo_r();
		//case 6: bit 7 interrupt bit
	}

	return 0xff;
}

void pasopia7_state::fdc_w(offs_t offset, u8 data)
{
	switch (offset)
	{
		case 0: m_fdc->tc_w(false); break;
		case 2: m_fdc->tc_w(true); break;
		case 5: m_fdc->fifo_w(data); break;
		case 6:
			if (data & 0x80)
				m_fdc->reset();
			/* TODO */
			m_floppy->mon_w(data & 0x40 ? CLEAR_LINE : ASSERT_LINE);
			break;
	}
}


u8 pasopia7_state::io_r(offs_t offset)
{
	if (m_mio_sel)
	{
		address_space &ram_space = m_maincpu->space(AS_PROGRAM);

		if (!machine().side_effects_disabled())
			m_mio_sel = 0;
			// hack: this is used for reading the keyboard data, we can fake it a little ... (modify fda4)
		return ram_space.read_byte(offset);
	}

	const u8 io_port = offset & 0xff; //trim down to 8-bit bus

	if (io_port >= 0x08 && io_port <= 0x0b)
		return m_ppi0->read(io_port & 3);
	else if (io_port >= 0x0c && io_port <= 0x0f)
		return m_ppi1->read(io_port & 3);
	else if (io_port == 0x10)
		return m_crtc->status_r();
	else if (io_port == 0x11)
		return m_crtc->register_r();
	else if (io_port >= 0x18 && io_port <= 0x1b)
		return m_pac2->read(io_port & 3);
	else if (io_port >= 0x20 && io_port <= 0x23)
	{
		if (!machine().side_effects_disabled())
			pasopia_nmi_trap();
		return m_ppi2->read(io_port & 3);
	}
	else if (io_port >= 0x28 && io_port <= 0x2b)
		return m_ctc->read(io_port & 3);
	else if (io_port >= 0x30 && io_port <= 0x33)
		return m_pio->read(io_port & 3);
//  else if(io_port == 0x3a)                    { SN1 }
//  else if(io_port == 0x3b)                    { SN2 }
//  else if(io_port == 0x3c)                    { bankswitch }
	else
//  if(io_port >= 0xe0 && io_port <= 0xe6)
//      return fdc_r(offset & 7);
//  else
	{
		if (!machine().side_effects_disabled())
			logerror("(PC=%06x) Read i/o address %02x\n", m_maincpu->pc(), io_port);
	}

	return 0xff;
}

void pasopia7_state::io_w(offs_t offset, u8 data)
{
	if (m_mio_sel)
	{
		address_space &ram_space = m_maincpu->space(AS_PROGRAM);
		m_mio_sel = 0;
		ram_space.write_byte(offset, data);
		return;
	}

	const u8 io_port = offset & 0xff; //trim down to 8-bit bus

	if (io_port >= 0x08 && io_port <= 0x0b)
		m_ppi0->write(io_port & 3, data);
	else
	if (io_port >= 0x0c && io_port <= 0x0f)
		m_ppi1->write(io_port & 3, data);
	else
	if (io_port >= 0x10 && io_port <= 0x11)
		pasopia7_6845_w(io_port-0x10, data);
	else
	if (io_port >= 0x18 && io_port <= 0x1b)
		m_pac2->write(io_port & 3, data);
	else
	if (io_port >= 0x20 && io_port <= 0x23)
	{
		m_ppi2->write(io_port & 3, data);
		pasopia_nmi_trap();
	}
	else
	if (io_port >= 0x28 && io_port <= 0x2b)
		m_ctc->write(io_port & 3, data);
	else if (io_port >= 0x30 && io_port <= 0x33)
		m_pio->write(io_port & 3, data);
	else if (io_port == 0x3a)
		m_sn1->write(data);
	else if (io_port == 0x3b)
		m_sn2->write(data);
	else if (io_port == 0x3c)
		memory_ctrl_w(data);
	else if (io_port >= 0xe0 && io_port <= 0xe6)
		fdc_w(offset & 7, data);
	else
	{
		logerror("(PC=%06x) Write i/o address %02x = %02x\n",m_maincpu->pc(),offset,data);
	}
}

void pasopia7_state::pasopia7_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).w(FUNC(pasopia7_state::ram_bank_w));
	map(0x0000, 0x3fff).bankr(m_banks[0]);
	map(0x4000, 0x7fff).bankr(m_banks[1]);
	map(0x8000, 0xbfff).rw(FUNC(pasopia7_state::vram_r), FUNC(pasopia7_state::vram_w));
	map(0xc000, 0xffff).ram();
}

void pasopia7_state::pasopia7_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(pasopia7_state::io_r), FUNC(pasopia7_state::io_w));
}

static INPUT_PORTS_START( pasopia7 )
	PASOPIA_KEYBOARD
	PORT_MODIFY("KEY.2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("+") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("*") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_MODIFY("KEY.3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("RIGHT")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Label") PORT_CODE(KEYCODE_END)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("INS/DEL") PORT_CHAR(8)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TAB/ESC") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_MODIFY("KEY.4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_NAME("HOME/CLS")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP) PORT_NAME("Kanji") // guess? key has a Japanese label
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN) PORT_NAME("Copy")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL) PORT_NAME("Stop")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_NAME("LEFT")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_NAME("UP/DOWN")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_NAME("SPACE") PORT_CHAR(' ')
INPUT_PORTS_END

static const gfx_layout p7_chars_8x8 =
{
	8,8,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};

static GFXDECODE_START( gfx_pasopia7 )
	GFXDECODE_ENTRY( "font",   0x00000, p7_chars_8x8,    0, 0x10 )
GFXDECODE_END

u8 pasopia7_state::keyb_r()
{
	u8 data = 0xff;
	for (u8 j = 0; j < 3; j++)
	{
		if (BIT(m_mux_data, 4 + j))
		{
			for (u8 i = 0; i < 4; i++)
			{
				if (BIT(m_mux_data, i))
					data &= m_keyboard[j*4 + i]->read();
			}
		}
	}

	return data;
}

void pasopia7_state::mux_w(u8 data)
{
	m_mux_data = data;
}

static const z80_daisy_config p7_daisy[] =
{
	{ "ctc" },
	{ "pio" },
//  { "fdc" }, /* TODO */
	{ nullptr }
};

u8 pasopia7_state::crtc_portb_r()
{
	// --x- ---- vsync bit
	// ---x ---- hardcoded bit, defines if the system screen is raster (1) or LCD (0)
	// ---- x--- disp bit
	u8 vdisp = (m_screen->vpos() < (m_screen_type ? 200 : 28)) ? 0x08 : 0x00; //TODO: check LCD vpos trigger
	u8 vsync = vdisp ? 0x00 : 0x20;

	return 0x40 | (m_attr_latch & 0x87) | vsync | vdisp | (m_screen_type << 4);
}

void pasopia7_state::screen_mode_w(u8 data)
{
	if (data & 0x5f)
		logerror("GFX MODE %02x\n",data);

	m_x_width = data & 0x20;
	m_gfx_mode = data & 0x80;

	LOG("m_gfx_mode=%02x\n", m_gfx_mode);
}

void pasopia7_state::plane_reg_w(u8 data)
{
	//if(data & 0x11)
	//logerror("PLANE %02x\n",data);
	m_plane_reg = data;
}

void pasopia7_state::video_attr_w(u8 data)
{
	LOG("VIDEO ATTR %02x | TEXT_PAGE %02x\n", data & 0xf, data & 0x70);
	m_attr_data = (data & 0x7) | ((data & 0x8) << 4);
}

//#include "debugger.h"

void pasopia7_state::video_misc_w(u8 data)
{
	/*
	    --x- ---- blinking
	    ---x ---- attribute wrap
	    ---- x--- pal disable
	    ---- xx-- palette selector (both bits enables this, odd hook-up)
	*/
	//if(data & 2)
	//{
	//  logerror("VIDEO MISC %02x\n",data);
	//  machine().debug_break();
	//}
	m_cursor_blink = data & 0x20;
	m_attr_wrap = data & 0x10;
	//m_pal_sel = data & 0x02;
}

void pasopia7_state::nmi_mask_w(u8 data)
{
	/*
	--x- ---- tape motor
	---x ---- data rec out
	---- --x- sound off
	---- ---x reset NMI & trap
	*/
	LOG("SYSTEM MISC %02x\n",data);

	if (data & 1)
	{
		m_nmi_reset &= ~4;
		m_nmi_trap &= ~2;
		//m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE); //guess
	}
	m_cass->output(BIT(data, 4) ? -1.0 : +1.0);
	u8 changed = data ^ m_porta_2;
	m_porta_2 = data;
	if (BIT(changed, 5))
		m_cass->change_state(BIT(data, 5) ? CASSETTE_MOTOR_DISABLED : CASSETTE_MOTOR_ENABLED, CASSETTE_MASK_MOTOR);
}

/* TODO: investigate on these. */
u8 pasopia7_state::unk_r()
{
	return 0xff;//machine().rand();
}

u8 pasopia7_state::nmi_reg_r()
{
	//logerror("C\n");
	return 0xfc | m_bank_reg;//machine().rand();
}

void pasopia7_state::nmi_reg_w(u8 data)
{
	/*
	    x--- ---- NMI mask
	    -x-- ---- NMI enable trap on PPI8255 2 r/w
	*/
	m_nmi_mask = data & 0x80;
	m_nmi_enable_reg = data & 0x40;
}

u8 pasopia7_state::nmi_porta_r()
{
	return 0xff;
}

u8 pasopia7_state::nmi_portb_r()
{
	u8 data = (m_cass->input() > +0.04) ? 0x20 : 0;
	return 0xd9 | data | m_nmi_trap | m_nmi_reset;
}

void pasopia7_state::speaker_w(int state)
{
	if (state)
	{
		m_spr_sw ^= 1;
		if (BIT(m_mux_data, 7))
			m_speaker->level_w(m_spr_sw);
	}
}

void pasopia7_state::machine_reset()
{
	m_banks[0]->set_entry(0);
	m_banks[1]->set_entry(0);

	m_nmi_reset |= 4;
	m_porta_2 = 0xFF;
	m_cass->change_state(CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
}

// TODO: palette values are mostly likely to be wrong in there
void pasopia7_state::p7_lcd_palette(palette_device &palette) const
{
	palette.set_pen_color(0, 0xa0, 0xa8, 0xa0);

	for (int i = 1; i < 8; i++)
		palette.set_pen_color(i, 0x30, 0x38, 0x10);
}

[[maybe_unused]] void pasopia7_state::fdc_irq(bool state)
{
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, state ? ASSERT_LINE : CLEAR_LINE);
}

static void pasopia7_floppies(device_slot_interface &device)
{
	device.option_add("525hd", FLOPPY_525_HD);
}

void pasopia7_state::p7_base(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 15.9744_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &pasopia7_state::pasopia7_mem);
	m_maincpu->set_addrmap(AS_IO, &pasopia7_state::pasopia7_io);
	m_maincpu->set_daisy_config(p7_daisy);

	/* Audio */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	SN76489A(config, m_sn1, 15.9744_MHz_XTAL / 8).add_route(ALL_OUTPUTS, "mono", 0.50);

	SN76489A(config, m_sn2, 15.9744_MHz_XTAL / 8).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* Devices */
	Z80CTC(config, m_ctc, 15.9744_MHz_XTAL / 4);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->set_clk<0>(15.9744_MHz_XTAL / 4);
	m_ctc->set_clk<2>(15.9744_MHz_XTAL / 4);
	m_ctc->zc_callback<0>().set(m_ctc, FUNC(z80ctc_device::trg1));
	m_ctc->zc_callback<1>().set(FUNC(pasopia7_state::speaker_w));
	m_ctc->zc_callback<2>().set(m_ctc, FUNC(z80ctc_device::trg3));

	Z80PIO(config, m_pio, 15.9744_MHz_XTAL / 4);
	m_pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio->out_pa_callback().set(FUNC(pasopia7_state::mux_w));
	m_pio->in_pb_callback().set(FUNC(pasopia7_state::keyb_r));

	I8255(config, m_ppi0);
	m_ppi0->in_pa_callback().set(FUNC(pasopia7_state::unk_r));
	m_ppi0->out_pa_callback().set(FUNC(pasopia7_state::screen_mode_w));
	m_ppi0->in_pb_callback().set(FUNC(pasopia7_state::crtc_portb_r));

	I8255(config, m_ppi1);
	m_ppi1->out_pa_callback().set(FUNC(pasopia7_state::plane_reg_w));
	m_ppi1->out_pb_callback().set(FUNC(pasopia7_state::video_attr_w));
	m_ppi1->out_pc_callback().set(FUNC(pasopia7_state::video_misc_w));

	I8255(config, m_ppi2);
	m_ppi2->in_pa_callback().set(FUNC(pasopia7_state::nmi_porta_r));
	m_ppi2->out_pa_callback().set(FUNC(pasopia7_state::nmi_mask_w));
	m_ppi2->in_pb_callback().set(FUNC(pasopia7_state::nmi_portb_r));
	m_ppi2->in_pc_callback().set(FUNC(pasopia7_state::nmi_reg_r));
	m_ppi2->out_pc_callback().set(FUNC(pasopia7_state::nmi_reg_w));

	UPD765A(config, m_fdc, 8'000'000, true, true);
	FLOPPY_CONNECTOR(config, "fdc:0", pasopia7_floppies, "525hd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", pasopia7_floppies, "525hd", floppy_image_device::default_mfm_floppy_formats);

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cass->set_interface("pasopia7_cass");

	PASOPIA_PAC2(config, m_pac2, pac2_default_devices, nullptr);

	SOFTWARE_LIST(config, "cass_list").set_original("pasopia7_cass");
//  SOFTWARE_LIST(config, "flop_list").set_original("pasopia7_flop");
}

void pasopia7_state::p7_raster(machine_config &config)
{
	p7_base(config);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_size(640, 480);
	m_screen->set_visarea(0, 640-1, 0, 32-1);
	m_screen->set_screen_update(m_crtc, FUNC(mc6845_device::screen_update));

	PALETTE(config, m_palette, palette_device::BRG_3BIT);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_pasopia7);

	HD6845S(config, m_crtc, VDP_CLOCK); // HD46505S
	m_crtc->set_screen(m_screen);
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(pasopia7_state::update_row));
}


void pasopia7_state::p7_lcd(machine_config &config)
{
	p7_base(config);

	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_size(640, 480);
	m_screen->set_visarea(0, 640-1, 0, 200-1);
	m_screen->set_screen_update(m_crtc, FUNC(mc6845_device::screen_update));

	PALETTE(config, m_palette, FUNC(pasopia7_state::p7_lcd_palette), 8);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_pasopia7);

	HD6845S(config, m_crtc, LCD_CLOCK); /* unknown variant, unknown clock, hand tuned to get ~60 fps */
	m_crtc->set_screen(m_screen);
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(pasopia7_state::update_row));
}

/* ROM definition */
ROM_START( pasopia7 )
	ROM_REGION( 0x4000, "bios", ROMREGION_ERASEFF )
	ROM_LOAD( "bios.rom", 0x0000, 0x4000, CRC(b8111407) SHA1(ac93ae62db4c67de815f45de98c79cfa1313857d))

	ROM_REGION( 0x8000, "basic", ROMREGION_ERASEFF )
	ROM_LOAD( "basic.rom", 0x0000, 0x8000, CRC(8a58fab6) SHA1(5e1a91dfb293bca5cf145b0a0c63217f04003ed1))

	ROM_REGION( 0x1000, "font", ROMREGION_ERASEFF )
	ROM_LOAD( "2732.ic146", 0x0000, 0x1000, CRC(aabf66e8) SHA1(1ff5c2c35f07d7481c4c22a453192d9458590eb0))
ROM_END

/* using an identical ROMset from now, but the screen type is different */
ROM_START( pasopia7lcd )
	ROM_REGION( 0x4000, "bios", ROMREGION_ERASEFF )
	ROM_LOAD( "bios.rom", 0x0000, 0x4000, CRC(b8111407) SHA1(ac93ae62db4c67de815f45de98c79cfa1313857d))

	ROM_REGION( 0x8000, "basic", ROMREGION_ERASEFF )
	ROM_LOAD( "basic.rom", 0x0000, 0x8000, CRC(8a58fab6) SHA1(5e1a91dfb293bca5cf145b0a0c63217f04003ed1))

	ROM_REGION( 0x1000, "font", ROMREGION_ERASEFF )
	ROM_LOAD( "2732.ic146", 0x0000, 0x1000, BAD_DUMP CRC(aabf66e8) SHA1(1ff5c2c35f07d7481c4c22a453192d9458590eb0))
ROM_END


void pasopia7_state::init_p7_raster()
{
	m_screen_type = 1;
	m_pio_timer = timer_alloc(FUNC(pasopia7_state::pio_timer), this);
	m_pio_timer->adjust(attotime::from_hz(5000), 0, attotime::from_hz(5000));
}

void pasopia7_state::init_p7_lcd()
{
	m_screen_type = 0;
	m_pio_timer = timer_alloc(FUNC(pasopia7_state::pio_timer), this);
	m_pio_timer->adjust(attotime::from_hz(5000), 0, attotime::from_hz(5000));
}

} // anonymous namespace


/* Driver */

COMP( 1983, pasopia7,    0,        0, p7_raster, pasopia7, pasopia7_state, init_p7_raster, "Toshiba", "Pasopia 7 PA7007 (Raster)", MACHINE_NOT_WORKING )
COMP( 1983, pasopia7lcd, pasopia7, 0, p7_lcd,    pasopia7, pasopia7_state, init_p7_lcd,    "Toshiba", "Pasopia 7 PA7007 with PA7170 (LCD)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )



patgen.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders: stonedDiscord
/***************************************************************************

  Philips PM-5xxx test pattern generators

================

V12 80C31 Firmware
V101 Y Luminance
V103 Y Luminance
V105 Y Luminance
V107 Y Luminance
V109 Y Luminance
V201 V Chrominance
V203 V Chrominance
V301 U Chrominance
V303 U Chrominance

TODO: add GAL logic to route the ROMs to the DAC and output that onto a screen
*/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/saa1043.h"

#include "pm5644.lh"

//#define VERBOSE 1
#include "logmacro.h"

namespace {

class patgen_state : public driver_device
{
public:
	patgen_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_leds(*this, "led%u", 0U)
	{ }

	void patgen(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void i80c31_data(address_map &map) ATTR_COLD;
	void i80c31_io(address_map &map) ATTR_COLD;
	void i80c31_prg(address_map &map) ATTR_COLD;

	u8 i80c31_p1_r();
	u8 keyboard_r();

	void led_w(offs_t offset, uint8_t data);
	void control_w(uint8_t data);
	void ch1_w(uint8_t data);
	void ch23_w(uint8_t data);

	u8 m_port1;

	required_device<i80c31_device> m_maincpu;
	output_finder<28> m_leds;
};

void patgen_state::i80c31_prg(address_map &map)
{
	map(0x0000, 0xFFFF).rom();
}

void patgen_state::i80c31_io(address_map &map)
{
	map(0x0000, 0x1FFF).ram();
	map(0x8000, 0x8000).r(FUNC(patgen_state::keyboard_r));
	map(0x8000, 0x8002).w(FUNC(patgen_state::led_w));
	map(0x8004, 0x8004).w(FUNC(patgen_state::control_w));
	map(0x8005, 0x8005).w(FUNC(patgen_state::ch1_w));
	map(0x8006, 0x8006).w(FUNC(patgen_state::ch23_w));
}

void patgen_state::i80c31_data(address_map &map)
{
	map(0x000, 0x1FF).ram();
}

u8 patgen_state::i80c31_p1_r()
{
	m_port1 = ioport("DSW")->read();
	//P1.4 2-WIRE SELECT
	//P1.5 FIELD1
	m_port1 = m_port1 | 0xC0;
	//P1.6 SCL pullup
	//P1.7 SDA pullup
	return m_port1;
}

u8 patgen_state::keyboard_r()
{
	u8 col0 = ioport("COL0")->read();
	u8 col1 = ioport("COL1")->read();
	u8 col2 = ioport("COL2")->read();
	u8 col3 = ioport("COL3")->read();

	u8 kb_state = col0 && col1 && col2 && col3;

	if (col1 != 0xFF)
		kb_state = kb_state & 0xDF;
	if (col2 != 0xFF)
		kb_state = kb_state & 0xBF;
	if (col3 != 0xFF)
		kb_state = kb_state & 0x7F;

	return kb_state;
}

void patgen_state::led_w(offs_t offset, uint8_t data)
{
	for (int i = 0; i < 8; i++)
	{
		uint8_t led_index = ((offset & 0x0F) * 10) + i;
		bool led_value = BIT(data, i);
		LOG("LED %d is %d\n",led_index,led_value);
		m_leds[led_index] = !led_value;
	}
}

void patgen_state::control_w(uint8_t data)
{

}

void patgen_state::ch1_w(uint8_t data)
{

}

void patgen_state::ch23_w(uint8_t data)
{

}

void patgen_state::machine_start()
{
	m_leds.resolve();
	m_port1 = 0x00;
}

void patgen_state::machine_reset()
{
}

static INPUT_PORTS_START( patgen )
	PORT_START("DSW")
	PORT_DIPNAME(0x00, 0x01, "Sync mode select 1")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x01, DEF_STR(On))
	PORT_DIPNAME(0x00, 0x02, "Sync mode select 2")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x02, DEF_STR(On))
	PORT_DIPNAME(0x00, 0x04, "Test mode enable")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x04, DEF_STR(On))

	// D5 goes low when 3 7 11 or 15 are pressed
	// D6 goes low when 2 6 10 14 or 16 are pressed
	// D7 goes low when 1 5 9 13 17 are pressed

	PORT_START("COL0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("WINDOW")  PORT_CODE(KEYCODE_4)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SIN X/Y")  PORT_CODE(KEYCODE_8)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S12-A") PORT_CODE(KEYCODE_C) //unpopulated
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_BUTTON1)  PORT_NAME("VERT ID SELECT")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_BUTTON2)  PORT_NAME("SW")
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CROSS HATCH")  PORT_CODE(KEYCODE_3)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LINEARITY")  PORT_CODE(KEYCODE_7) //unpopulated
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S11-A") PORT_CODE(KEYCODE_B) //unpopulated
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S15-A") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("COLOR BAR")  PORT_CODE(KEYCODE_2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PULSE & BAR")  PORT_CODE(KEYCODE_6) //unpopulated
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("APL") PORT_CODE(KEYCODE_A) //unpopulated
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S14-A") PORT_CODE(KEYCODE_E) //unpopulated
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S16-A") PORT_CODE(KEYCODE_G) //unpopulated
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TEST PATTERN")  PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MULTI BURST")  PORT_CODE(KEYCODE_5)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LIVE")  PORT_CODE(KEYCODE_9)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S13-A") PORT_CODE(KEYCODE_D) //unpopulated
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S17-A") PORT_CODE(KEYCODE_H)
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

INPUT_PORTS_END

void patgen_state::patgen(machine_config &config)
{
	I80C31(config, m_maincpu, 16_MHz_XTAL); // Philips PCB80C31BH
	m_maincpu->set_addrmap(AS_PROGRAM, &patgen_state::i80c31_prg);
	m_maincpu->set_addrmap(AS_DATA, &patgen_state::i80c31_data);
	m_maincpu->set_addrmap(AS_IO, &patgen_state::i80c31_io);
	m_maincpu->port_in_cb<1>().set(FUNC(patgen_state::i80c31_p1_r));

	saa1043_device &saa1043(SAA1043(config, "saa1043", XTAL(5'000'000)));
	saa1043.ri_callback().set_inputline(m_maincpu, MCS51_INT1_LINE);

	config.set_default_layout(layout_pm5644);
}

ROM_START( pm5644g00 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "4008_102_59371_csum_a100.v12",   0x000000, 0x10000, CRC(2b98e429) SHA1(18919af966316dad7fe612e394f6c663f4b3fae2) )

	ROM_REGION( 0x50000, "luma", 0 )
	ROM_LOAD( "4008_102_56191_csum_9a6a.v101",  0x000000, 0x10000, CRC(e307189b) SHA1(dc4b670752d77ec24bdf7b8d4981e84cf9e4b43a) )
	ROM_LOAD( "4008_102_56201_csum_4e67.v103",  0x010000, 0x10000, CRC(b00dc5c5) SHA1(4535de542b232fae6be4dda465283c6a65b5a199) )
	ROM_LOAD( "4008_102_56211_csum_3172.v105",  0x020000, 0x10000, CRC(d8bb3fd1) SHA1(9e37dc93f2b0902acafd23667b1cfabc5df1f66e) )
	ROM_LOAD( "4008_102_56221_csum_7f95.v107",  0x030000, 0x10000, CRC(b7eadaf8) SHA1(b40d3209203eae34d477660b37c9142cb501b289) )
	ROM_LOAD( "4008_102_56231_csum_78c4.v109",  0x040000, 0x10000, CRC(da70f99d) SHA1(e2fbbb8c190e3dfa35c242e8809b5d328d695166) )

	ROM_REGION( 0x20000, "chromav", 0 )
	ROM_LOAD( "4008_102_56241_csum_f0df.v201",  0x000000, 0x10000, CRC(73043b38) SHA1(e7299534fc6e069ca750d4b1eda0ee8ca4fe525c) )
	ROM_LOAD( "4008_102_56251_csum_f397.v203",  0x010000, 0x10000, CRC(8df2f123) SHA1(62177b44437d5dc213543bc838c792877031c9de) )

	ROM_REGION( 0x20000, "chromau", 0 )
	ROM_LOAD( "4008_102_56261_csum_2da9.v301",  0x000000, 0x10000, CRC(b4986372) SHA1(80522bd161a9385efa93fddee4d2ee9171504a03) )
	ROM_LOAD( "4008_102_56271_csum_2e0f.v303",  0x010000, 0x10000, CRC(97afd029) SHA1(b35b8bd21f5f0d1817d3d14233629b600d59f177) )

	ROM_REGION( 0x15000, "pal", 0 )
	ROM_LOAD( "pal_4008_102_55651_csum_6af0.jed",  0x000000, 0x01c46, CRC(a48f406d) SHA1(47b88554ac5655b026df17e4ae2902852c0f60ee) )
	ROM_LOAD( "pal_4008_102_56141_csum_e9f8.jed",  0x002000, 0x01c45, CRC(7adefbeb) SHA1(d57021f1579aa3bbd0713b87a3bc7466d26620ea) )
	ROM_LOAD( "pal_4008_102_56151_csum_e248.jed",  0x004000, 0x01c45, CRC(36346688) SHA1(f4b5302c851f12cbf82878ee86fc6e4c7e5680ab) )
	ROM_LOAD( "pal_4008_102_56161_csum_87e0.v40.jed",  0x006000, 0x01c45, CRC(45b03157) SHA1(a0a8f1633ac3ba8b123e470259ee51098c2a857c) )
	ROM_LOAD( "pal_4008_102_56171_csum_18d9.v205.jed", 0x008000, 0x01c46, CRC(5a2d22a2) SHA1(f274e38aac896b802eb44f1004d72b9d8d090eef) )
	ROM_LOAD( "pal_4008_102_56181_csum_10a1.v305.jed", 0x010000, 0x01c46, CRC(2e331f24) SHA1(41ceb44b6af43abc4a19c679255fbc763e68e2c5) )

	ROM_LOAD( "peel_4008_102_55441_csum_4a64.jed",  0x012000, 0x00dc5, CRC(cbd86564) SHA1(21b8268ba93a52f9efe0f481276a2862d4fb58dd) )
	ROM_LOAD( "peel_4008_102_55661_csum_6055.jed",  0x014000, 0x00dc5, CRC(3418434f) SHA1(2570d8f2138ae9ca182ac4ac6f8fe9b0973f4bb9) )
ROM_END


ROM_START( pm5644g913 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "4008_102_58761_csum_1800.v12",   0x000000, 0x10000, CRC(a2091bcf) SHA1(53872f64a5f3e574731ce33f2cb0a6107b0d7b99) )

	ROM_REGION( 0x280000, "luma", 0 )
	ROM_LOAD( "4008_102_58802_csum_a64a.v101",  0x000000, 0x80000, CRC(3c1297fb) SHA1(9121da63c8990ed05c928f50d4027f06adb38606) )
	ROM_LOAD( "4008_102_58812_csum_61e5.v103",  0x080000, 0x80000, CRC(546d325e) SHA1(8896540598b9ad5969d714d634ca48f9056523ab) )
	ROM_LOAD( "4008_102_58822_csum_3fca.v105",  0x100000, 0x80000, CRC(67d34ad4) SHA1(6bae1bf35dab6ac56d1bf4f87b8b71644cfa3a57) )
	ROM_LOAD( "4008_102_58832_csum_757d.v107",  0x180000, 0x80000, CRC(aa0556ef) SHA1(12b2b01d0c77ac84c428eb2f037c649eb1189b52) )
	ROM_LOAD( "4008_102_58842_csum_e882.v109",  0x200000, 0x80000, CRC(086a26ad) SHA1(737a8575f09734feec8120cada62332cff420987) )

	ROM_REGION( 0x100000, "chromav", 0 )
	ROM_LOAD( "4008_102_58852_csum_81f9.v201",  0x000000, 0x80000, CRC(ead8c733) SHA1(b99677f0dd067f5b33d28421cb2128249cf46cca) )
	ROM_LOAD( "4008_102_58862_csum_81f9.v203",  0x080000, 0x80000, CRC(fec4ba06) SHA1(583473f70d16aff773b3433d8aa3874ab20535c0) )

	ROM_REGION( 0x100000, "chromau", 0 )
	ROM_LOAD( "4008_102_58872_csum_b4c3.v301",  0x000000, 0x80000, CRC(086a26ad) SHA1(737a8575f09734feec8120cada62332cff420987) )
	ROM_LOAD( "4008_102_58882_csum_b4c3.v303",  0x080000, 0x80000, CRC(5cf8ffa9) SHA1(0528803574a8a5240107b0ebb0aee2170c239311) )

	ROM_REGION( 0x15000, "pal", 0 )
	ROM_LOAD( "pal_4008_102_56171_csum_1809.v205.jed", 0x000000, 0x01c46, CRC(3163dc7b) SHA1(61788da7093836af4526d8586a35ee6139f81602) )
	ROM_LOAD( "pal_4008_102_56181_csum_10a1.v305.jed", 0x002000, 0x01c46, CRC(849ca310) SHA1(3e00d3cb11ce29b4a63679771a66cb9da2524302) )
	ROM_LOAD( "pal_4008_102_58291_csum_6653.v310.jed", 0x004000, 0x01c46, CRC(77bec308) SHA1(850a8362b0ce52ffdb598c8adc561a11a4a959fc) )
	ROM_LOAD( "pal_4008_102_58771_csum_e165.v37.jed",  0x006000, 0x01c93, CRC(1c6d421e) SHA1(8962207496c0434b63e2739c4fc996df39bfcbec) )
	ROM_LOAD( "pal_4008_102_58791_csum_681f.v40.jed",  0x008000, 0x01c45, CRC(e314503e) SHA1(4b5bf8a199203d7e45838e8221a26063a387bc97) )
	ROM_LOAD( "pal_4008_102_59681_csum_2c6d.v39.jed",  0x010000, 0x01c45, CRC(7c417cc2) SHA1(1015a222ed1c304ddc0923256c63c1b8b6519974) )

	ROM_LOAD( "peel_4008_102_55441_csum_4a64.jed",  0x012000, 0x00dc5, CRC(cbd86564) SHA1(21b8268ba93a52f9efe0f481276a2862d4fb58dd) )
	ROM_LOAD( "peel_4008_102_55661_csum_6055.jed",  0x014000, 0x00dc5, CRC(3418434f) SHA1(2570d8f2138ae9ca182ac4ac6f8fe9b0973f4bb9) )
ROM_END

ROM_START( pm5644g924 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "4008_002_00541_csum_b700.v12",  0x000000, 0x10000, CRC(837d2c2c) SHA1(09be6adc54bfb3ed832d56b38063c186956a0043) )

	ROM_REGION( 0x280000, "luma", 0 )
	ROM_LOAD( "4008_002_00551_csum_9f93.v101",  0x000000, 0x80000, CRC(1314362d) SHA1(1f38a6a3f5a51bcd00cdd6408087b2ce91754c00) )
	ROM_LOAD( "4008_002_00561_csum_3998.v103",  0x080000, 0x80000, CRC(141f9c8f) SHA1(73780908900e5cb5c8abd8a1b9014c0eb1ef127c) )
	ROM_LOAD( "4008_002_00571_csum_5608.v105",  0x100000, 0x80000, CRC(ded20782) SHA1(51de44186ef2f6f9ba72f5a9c4de1359d640acad) )
	ROM_LOAD( "4008_002_00581_csum_739c.v107",  0x180000, 0x80000, CRC(08319fa5) SHA1(c4fd8b28596884fc50217edeaf09460be77f1f57) )
	ROM_LOAD( "4008_002_00591_csum_1c84.v109",  0x200000, 0x80000, CRC(3d65b467) SHA1(638fc4f2c6e3a5f2a53b915f9c881147a0e38393) )

	ROM_REGION( 0x100000, "chromav", 0 )
	ROM_LOAD( "4008_002_00601_csum_f478.v201",  0x000000, 0x80000, CRC(7ee1f934) SHA1(11fa770b1a21f6a265ce97fc20f2069b404bf813) )
	ROM_LOAD( "4008_002_00611_csum_f167.v203",  0x080000, 0x80000, CRC(df5b0f23) SHA1(b2ab2c784162f11e3a2cd992bf3c5efe080ca4e5) )

	ROM_REGION( 0x100000, "chromau", 0 )
	ROM_LOAD( "4008_002_00621_csum_06e1.v301",  0x000000, 0x80000, CRC(25a095d6) SHA1(ba7d37683b8fa77122fa4b2b1df31911634254d7) )
	ROM_LOAD( "4008_002_00631_csum_ffb8.v303",  0x080000, 0x80000, CRC(af031008) SHA1(6dbaba28c9f959d8594fc0b8b874ba6e0a83edbf) )

	ROM_REGION( 0x15000, "pal", 0 )
	ROM_LOAD( "pal_4008_002_00641_csum_e123.v37.jed",   0x000000, 0x01c45, CRC(172ec12f) SHA1(2ab12bc8024c2a0b4ea7a0f8aa706e3293e366eb) )
	ROM_LOAD( "pal_4008_002_00651_csum_f80d.v38.jed",   0x002000, 0x01c45, CRC(3ce26ccb) SHA1(f16084ac0e4e19529dcd19fb091275c58c0c2be3) )
	ROM_LOAD( "pal_4008_102_56171_csum_18d9.v205.jed",  0x004000, 0x01c46, CRC(5a2d22a2) SHA1(f274e38aac896b802eb44f1004d72b9d8d090eef) )
	ROM_LOAD( "pal_4008_102_56181_csum_10a1.v305.jed",  0x006000, 0x01c46, CRC(2e331f24) SHA1(41ceb44b6af43abc4a19c679255fbc763e68e2c5) )
	ROM_LOAD( "pal_4008_102_58291_csum_6653.v310.jed",  0x008000, 0x01c46, CRC(60704fbf) SHA1(c1fa652927a610438f4eabffcbcf80922fb98e6b) )
	ROM_LOAD( "pal_4008_102_58791_csum_681f.v40.jed",   0x00a000, 0x01c45, CRC(f78297e1) SHA1(bb6c5430f66eb02f3133e95d7db268d4977a45c1) )
	ROM_LOAD( "pal_4008_102_59681_csum_2c6d.v39.jed",   0x00c000, 0x01c45, CRC(f0921c84) SHA1(81cb2f8aa5651c47b79ec25a852d6fb78e9811fe) )

	ROM_LOAD( "peel_4008_102_55441_csum_4a64.v15.jed",  0x00e000, 0x00dc5, CRC(cbd86564) SHA1(21b8268ba93a52f9efe0f481276a2862d4fb58dd) )
	ROM_LOAD( "peel_4008_102_55661_csum_6055.v111.jed", 0x010000, 0x00dc5, CRC(3418434f) SHA1(2570d8f2138ae9ca182ac4ac6f8fe9b0973f4bb9) )
ROM_END

ROM_START( pm5644g924b )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "4008_002_00542_csum_b800.v12",  0x000000, 0x10000, CRC(5a63d539) SHA1(c969cd4c214b9fa485aeac03f42d3d4d5c9f7ae0) )

	ROM_REGION( 0x280000, "luma", 0 )
	ROM_LOAD( "4008_002_00552_csum_9f93.v101",  0x000000, 0x80000, CRC(b6034502) SHA1(a3c0c9f4ddc56412eb20208999f86db10c6b860f) )
	ROM_LOAD( "4008_002_00562_csum_3b85.v103",  0x080000, 0x80000, CRC(f13e8861) SHA1(3881253065ac5ecb72971d8088ea0f702658a176) )
	ROM_LOAD( "4008_002_00572_csum_5608.v105",  0x100000, 0x80000, CRC(61178f37) SHA1(131f9936f349a5f5189a33adc96f776cb172a71a) )
	ROM_LOAD( "4008_002_00582_csum_7589.v107",  0x180000, 0x80000, CRC(5e8ffcb2) SHA1(c9e44e6c39256dcf897e414290c3c7083ef8f66e) )
	ROM_LOAD( "4008_002_00592_csum_c1e0.v109",  0x200000, 0x80000, CRC(7fac919a) SHA1(03493ae0e6c6c44dd2fcb8ed3368c802ae812d7c) )

	ROM_REGION( 0x100000, "chromav", 0 )
	ROM_LOAD( "4008_002_00602_csum_f4c8.v201",  0x000000, 0x80000, CRC(202b5d66) SHA1(64c6e92fdaddd79505e9acb4b0ab20ed25934c79) )
	ROM_LOAD( "4008_002_00612_csum_f167.v203",  0x080000, 0x80000, CRC(4ccbf510) SHA1(e26d5cc44f2725afed433a70f36b89c3841b3ed3) )

	ROM_REGION( 0x100000, "chromau", 0 )
	ROM_LOAD( "4008_002_00621_csum_06e1.v301",  0x000000, 0x80000, CRC(25a095d6) SHA1(ba7d37683b8fa77122fa4b2b1df31911634254d7) )
	ROM_LOAD( "4008_002_00631_csum_ffb8.v303",  0x080000, 0x80000, CRC(af031008) SHA1(6dbaba28c9f959d8594fc0b8b874ba6e0a83edbf) )

	ROM_REGION( 0x17000, "pal", 0 )
	ROM_LOAD( "pal_4008_002_00641_csum_e123.v37.jed",   0x000000, 0x01c45, CRC(172ec12f) SHA1(2ab12bc8024c2a0b4ea7a0f8aa706e3293e366eb) )
	ROM_LOAD( "pal_4008_002_00651_csum_f80d.v38.jed",   0x002000, 0x00e29, CRC(3aef572a) SHA1(0aa4d946b91550b102f7f098fe1484d2b226a017) )
	ROM_LOAD( "pal_4008_102_56171_csum_18d9.v205.jed",  0x004000, 0x01c46, CRC(5a2d22a2) SHA1(f274e38aac896b802eb44f1004d72b9d8d090eef) )
	ROM_LOAD( "pal_4008_102_56181_csum_10a1.v305.jed",  0x006000, 0x01c46, CRC(2e331f24) SHA1(41ceb44b6af43abc4a19c679255fbc763e68e2c5) )
	ROM_LOAD( "pal_4008_102_58291_csum_6653.v310.jed",  0x008000, 0x01c46, CRC(60704fbf) SHA1(c1fa652927a610438f4eabffcbcf80922fb98e6b) )
	ROM_LOAD( "pal_4008_102_58791_csum_681f.v40.jed",   0x010000, 0x01c45, CRC(f78297e1) SHA1(bb6c5430f66eb02f3133e95d7db268d4977a45c1) )
	ROM_LOAD( "pal_4008_102_59681_csum_2c6d.v39.jed",   0x012000, 0x01c45, CRC(f0921c84) SHA1(81cb2f8aa5651c47b79ec25a852d6fb78e9811fe) )
	ROM_LOAD( "peel_4008_102_55441_csum_4a64.v15.jed",  0x014000, 0x00dc5, CRC(cbd86564) SHA1(21b8268ba93a52f9efe0f481276a2862d4fb58dd) )
	ROM_LOAD( "peel_4008_102_55661_csum_6055.v111.jed", 0x016000, 0x00dc5, CRC(3418434f) SHA1(2570d8f2138ae9ca182ac4ac6f8fe9b0973f4bb9) )
ROM_END

ROM_START( pm5644l00 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "4008_002_01671_csum_cd00.v12",  0x000000, 0x10000, CRC(f06acb17) SHA1(9e7da23c3d6624190561cfc89f86fb5c9b563a63) )

	ROM_REGION( 0x280000, "luma", 0 )
	ROM_LOAD( "4008_002_01621_csum_c923.v101",  0x000000, 0x80000, CRC(5b2834d9) SHA1(483d285445e935cdbd05e4fef0e1c2cb5ce890c8) )
	ROM_LOAD( "4008_002_01631_csum_00d6.v103",  0x080000, 0x80000, CRC(ba4fb4d7) SHA1(acf357b0ffea7bdd3259dae3fc9292dcb799e481) )
	ROM_LOAD( "4008_002_01641_csum_c9d6.v105",  0x100000, 0x80000, CRC(b4a6e5c7) SHA1(329a93a8466eee4260e97a15c514d5da01211019) )
	ROM_LOAD( "4008_002_01651_csum_9c03.v107",  0x180000, 0x80000, CRC(2cd4931f) SHA1(1270f8ea23a68c14b2373a05dab394921c4ca11d) )
	ROM_LOAD( "4008_002_01661_csum_00e2.v109",  0x200000, 0x80000, CRC(1a85ff7b) SHA1(edab58f288f8be70a25488a38fe80ac899d9c21b) )

	ROM_REGION( 0x100000, "chromav", ROMREGION_ERASEFF )
	ROM_REGION( 0x100000, "chromau", ROMREGION_ERASEFF )

	ROM_REGION( 0x15000, "pal", 0 )
	ROM_LOAD( "pal_4008_102_56881_csum_0483.v37.jed", 0x000000, 0x01c46, CRC(c9e21167) SHA1(6a21419a0aa8f22b45cf03845c2e42d4ab4c28bf) )
	ROM_LOAD( "pal_4008_102_56891_csum_8669.v38.jed", 0x002000, 0x01c46, CRC(4b6be56d) SHA1(927fa0a6d3de55ec141df1ac5b76d1331f5175fe) )
	ROM_LOAD( "pal_4008_102_56901_csum_72e4.v40.jed", 0x004000, 0x01c45, CRC(354a13be) SHA1(d0b2965e3b24fa47fef345c9068c26118bf3bc45) )

	ROM_LOAD( "peel_4008_102_56301_csum_4ed3.jed",    0x006000, 0x00dc5, CRC(2a81d3e0) SHA1(62aa1647522940233de1f2d58103ee74f36b7d97) )
ROM_END

ROM_START( pm5644m00 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "4008_102_59401_csum_7300.v12",  0x000000, 0x10000, CRC(d02ddfde) SHA1(2431e0c4d006264c0fd97a03763666ecd4ef660a) )

	ROM_REGION( 0x50000, "luma", 0 )
	ROM_LOAD( "4008_102_56781_csum_8e52.v101",  0x000000, 0x10000, CRC(f43e33e0) SHA1(5586f87966d062e18cd205a9e17a992043b80585) )
	ROM_LOAD( "4008_102_56741_csum_0d4a.v103",  0x010000, 0x10000, CRC(d246990f) SHA1(7f9a8b7d43a7d585d68f46e007a087c984f358a2) )
	ROM_LOAD( "4008_102_56751_csum_f7b6.v105",  0x020000, 0x10000, CRC(adfbcd39) SHA1(168cb18c83a7d6855d7fcf48fa8e8ccc082ec8f9) )
	ROM_LOAD( "4008_102_56761_csum_03df.v107",  0x030000, 0x10000, CRC(90ad9096) SHA1(eaede5bd2698a69c934f6737ef75636f6ce9b835) )
	ROM_LOAD( "4008_102_56771_csum_1a71.v109",  0x040000, 0x10000, CRC(98cdaeae) SHA1(fd617bf16912c824d0e425cf12e19293161e2276) )

	ROM_REGION( 0x20000, "chromav", 0 )
	ROM_LOAD( "4008_102_56791_csum_c1ea.v201",  0x000000, 0x10000, CRC(ba80ead8) SHA1(89f1215acaaa06f583926ee5a790801546680ff4) )
	ROM_LOAD( "4008_102_56801_csum_c1d0.v203",  0x010000, 0x10000, CRC(1226f352) SHA1(fb9c6777fb78268d42ce5caed7d29ee4630f0bb8) )

	ROM_REGION( 0x20000, "chromau", 0 )
	ROM_LOAD( "4008_102_56811_csum_b3ac.v301",  0x000000, 0x10000, CRC(1107cd24) SHA1(e97c39cffab7d49805383ba28b6506538f087547) )
	ROM_LOAD( "4008_102_56821_csum_b3ec.v303",  0x010000, 0x10000, CRC(d0fd4cd6) SHA1(2af1313dc4a6f0193f6e499fef7665d0ac7a9aa4) )

	ROM_REGION( 0x15000, "pal", 0 )
	ROM_LOAD( "pal_4008_102_55651_csum_6af0.v310.jed",  0x000000, 0x01c46, CRC(a48f406d) SHA1(47b88554ac5655b026df17e4ae2902852c0f60ee) )
	ROM_LOAD( "pal_4008_102_56171_csum_18d9.v205.jed",  0x002000, 0x01c46, CRC(5a2d22a2) SHA1(f274e38aac896b802eb44f1004d72b9d8d090eef) )
	ROM_LOAD( "pal_4008_102_56181_csum_10a1.v305.jed",  0x004000, 0x01c46, CRC(2e331f24) SHA1(41ceb44b6af43abc4a19c679255fbc763e68e2c5) )
	ROM_LOAD( "pal_4008_102_56711_csum_e9f8.v37.jed",   0x006000, 0x01c45, CRC(a90ebee5) SHA1(660e153df9dc006a25de5b38d39d7ed6c8ef2820) )
	ROM_LOAD( "pal_4008_102_56721_csum_dc9a.v38.jed",   0x008000, 0x01c45, CRC(a5369c41) SHA1(bbc6341ed47f845b21bab70f1c6fc504f327e4fc) )
	ROM_LOAD( "pal_4008_102_56731_csum_879c.v40.jed",   0x010000, 0x01c45, CRC(78995893) SHA1(5e69ca70caa1d14330a0ec0f3cbcb075fcae7274) )

	ROM_LOAD( "peel_4008_102_55441_csum_4a64.v15.jed",  0x012000, 0x00dc5, CRC(cbd86564) SHA1(21b8268ba93a52f9efe0f481276a2862d4fb58dd) )
	ROM_LOAD( "peel_4008_102_55661_csum_6055.v111.jed", 0x014000, 0x00dc5, CRC(3418434f) SHA1(2570d8f2138ae9ca182ac4ac6f8fe9b0973f4bb9) )
ROM_END

ROM_START( pm5644p00 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "4008_102_59391_csum_0d00.v12",  0x000000, 0x10000, CRC(9e30f0dd) SHA1(3277db6f5c890897c6454b41a60103de6f652690) )

	ROM_REGION( 0x50000, "luma", 0 )
	ROM_LOAD( "4008_102_56831_csum_6c99.v101",  0x000000, 0x10000, CRC(dce2ec5a) SHA1(5fd53a091b39164a36c3109bac41d7f80f50c982) )
	ROM_LOAD( "4008_102_56841_csum_6cf9.v103",  0x010000, 0x10000, CRC(e31a3ebc) SHA1(24e7580ebf9d10f21a3a207b73b404f1c07300e0) )
	ROM_LOAD( "4008_102_56851_csum_0379.v105",  0x020000, 0x10000, CRC(73ef8961) SHA1(03f8703df4f1c13a70b0c397a26eff8308a27685) )
	ROM_LOAD( "4008_102_56861_csum_0393.v107",  0x030000, 0x10000, CRC(fe4477d4) SHA1(998c4a28c3a80e756cdaa1016b0019bc47e9995f) )
	ROM_LOAD( "4008_102_57161_csum_e601.v109",  0x040000, 0x10000, CRC(3e71ec87) SHA1(8342e0bbc768dd70ce5d484dd3e0f39a83c7b9a5) )

	ROM_REGION( 0x20000, "chromav", 0 )
	ROM_LOAD( "4008_102_57171_csum_d3b6.v201",  0x000000, 0x10000, CRC(6bab160c) SHA1(297f35e39adc6794bda82ca351cb9c7f8499f1f3) )
	ROM_LOAD( "4008_102_57181_csum_de42.v203",  0x010000, 0x10000, CRC(d849cbf4) SHA1(586251fc76f6ec5fa3bca87a3115d594536d41e3) )

	ROM_REGION( 0x20000, "chromau", 0 )
	ROM_LOAD( "4008_102_57191_csum_f178.v301",  0x000000, 0x10000, CRC(378419c7) SHA1(6a1aaf6c20e9ef13a1a72a787b1822a4118d3f68) )
	ROM_LOAD( "4008_102_57201_csum_e138.v303",  0x010000, 0x10000, CRC(80c9b2fe) SHA1(c0241458a51a9790ed4c977f667a94a40e612982) )

ROM_END

} // anonymous namespace

SYST( 1989, pm5644g00,   0,           0, patgen, patgen, patgen_state, empty_init, "Philips", "PM 5644 color pattern generator (PAL-G 4:3 Colour Circle)", MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING)
SYST( 1989, pm5644g913,  0,           0, patgen, patgen, patgen_state, empty_init, "Philips", "PM 5644 color pattern generator (PAL-G 4:3 Indian Head)", MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING)
SYST( 1989, pm5644g924,  pm5644g924b, 0, patgen, patgen, patgen_state, empty_init, "Philips", "PM 5644 color pattern generator (PAL 16:9 Colour Circle Rev 1)", MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING)
SYST( 1989, pm5644g924b, 0,           0, patgen, patgen, patgen_state, empty_init, "Philips", "PM 5644 color pattern generator (PAL 16:9 Colour Circle Rev 2)", MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING)
SYST( 1989, pm5644l00,   0,           0, patgen, patgen, patgen_state, empty_init, "Philips", "PM 5644 color pattern generator (SECAM 4:3 Colour Circle)", MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING)
SYST( 1989, pm5644m00,   0,           0, patgen, patgen, patgen_state, empty_init, "Philips", "PM 5644 color pattern generator (NTSC 4:3 Colour Circle)", MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING)
SYST( 1989, pm5644p00,   0,           0, patgen, patgen, patgen_state, empty_init, "Philips", "PM 5644 color pattern generator (PAL-M 4:3 Colour Circle)", MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING)



patinho_feio.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Felipe Sanches
/*
    Patinho Feio
*/

#include "emu.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "softlist.h"
#include "cpu/patinhofeio/patinhofeio_cpu.h"
#include "patinhofeio.h"
#include "patinho.lh"

/*
    driver init function
*/
void patinho_feio_state::init_patinho_feio()
{
	m_out = &output();
	m_prev_ACC = 0;
	m_prev_opcode = 0;
	m_prev_mem_data = 0;
	m_prev_mem_addr = 0;
	m_prev_PC = 0;
	m_prev_FLAGS = 0;
	m_prev_RC = 0;
}

void patinho_feio_state::update_panel(uint8_t ACC, uint8_t opcode, uint8_t mem_data, uint16_t mem_addr, uint16_t PC, uint8_t FLAGS, uint16_t RC, uint8_t mode){
	char lamp_id[11];
	static char const *const button_names[] = {
		"NORMAL",
		"CICLOUNICO",
		"INSTRUCAOUNICA",
		"ENDERECAMENTO",
		"ARMAZENAMENTO",
		"EXPOSICAO"
	};

	for (int i=0; i<6; i++){
		m_out->set_value(button_names[i], (mode == i) ? 1 : 0);
	}

	for (int i=0; i<8; i++){
		if ((m_prev_ACC ^ ACC) & (1 << i)){
			sprintf(lamp_id, "acc%d", i);
			m_out->set_value(lamp_id, (ACC >> i) & 1);
		}
		if ((m_prev_opcode ^ opcode) & (1 << i)){
			sprintf(lamp_id, "opcode%d", i);
			m_out->set_value(lamp_id, (opcode >> i) & 1);
		}
		if ((m_prev_mem_data ^ mem_data) & (1 << i)){
			sprintf(lamp_id, "mem_data%d", i);
			m_out->set_value(lamp_id, (mem_data >> i) & 1);
		}
	}
	m_prev_ACC = ACC;
	m_prev_opcode = opcode;
	m_prev_mem_data = mem_data;

	for (int i=0; i<12; i++){
		if ((m_prev_mem_addr ^ mem_addr) & (1 << i)){
			sprintf(lamp_id, "mem_addr%d", i);
			m_out->set_value(lamp_id, (mem_addr >> i) & 1);
		}
		if ((m_prev_PC ^ PC) & (1 << i)){
			sprintf(lamp_id, "pc%d", i);
			m_out->set_value(lamp_id, (PC >> i) & 1);
		}
		if ((m_prev_RC ^ RC) & (1 << i)){
			sprintf(lamp_id, "rc%d", i);
			m_out->set_value(lamp_id, (RC >> i) & 1);
		}
	}
	m_prev_mem_addr = mem_addr;
	m_prev_PC = PC;
	m_prev_RC = RC;

	if ((m_prev_FLAGS ^ FLAGS) & (1 << 0)) m_out->set_value("flags0", (FLAGS >> 0) & 1);
	if ((m_prev_FLAGS ^ FLAGS) & (1 << 1)) m_out->set_value("flags1", (FLAGS >> 1) & 1);
	m_prev_FLAGS = FLAGS;
}

void patinho_feio_state::decwriter_data_w(uint8_t data)
{
	m_decwriter->write(data);

	m_maincpu->set_iodev_status(0xA, IODEV_BUSY);

	if (data == 0x0D){
		m_decwriter_timer->adjust(attotime::from_hz(1/0.700)); //carriage return takes 700 msecs
	} else {
		m_decwriter_timer->adjust(attotime::from_hz(10)); //10 characters per second
	}
	m_decwriter_timer->enable(1); //start the timer
}

/*
    timer callback to generate decwriter char print completion signal
*/
TIMER_CALLBACK_MEMBER(patinho_feio_state::decwriter_callback)
{
	m_maincpu->set_iodev_status(0xA, IODEV_READY);
	m_decwriter_timer->enable(0); //stop the timer
}

void patinho_feio_state::decwriter_kbd_input(u8 data)
{
	m_maincpu->transfer_byte_from_external_device(0xA, ~data);
}

void patinho_feio_state::teletype_data_w(uint8_t data)
{
	m_tty->write(data);

	m_maincpu->set_iodev_status(0xB, IODEV_READY);
	m_teletype_timer->adjust(attotime::from_hz(10)); //10 characters per second
	m_teletype_timer->enable(1); //start the timer
}

/*
    timer callback to generate teletype char print completion signal
*/
TIMER_CALLBACK_MEMBER(patinho_feio_state::teletype_callback)
{
	m_maincpu->set_iodev_status(0xB, IODEV_READY);
	m_teletype_timer->enable(0); //stop the timer
}

void patinho_feio_state::teletype_kbd_input(u8 data)
{
	//I figured out that the data is provided inverted (2's complement)
	//based on a comment in the source code listing of the HEXAM program.
	//It is not clear though, if all I/O devices complement the data when
	//communicating with the computer, or if this behaviour is a particular
	//characteristic of the teletype.

	m_maincpu->transfer_byte_from_external_device(0xB, ~data);
}

/* The hardware does not perform this checking.
   This is implemented here only for debugging purposes.

   Also, proper punched paper tape emulation does
   not use this function at all.
*/
void patinho_feio_state::load_tape(const char* name){
	uint8_t *RAM = (uint8_t *) memshare("maincpu:internalram")->ptr();
	uint8_t *data = memregion(name)->base();
	unsigned int data_length = data[0];
	unsigned int start_address = data[1]*256 + data[2];
	int8_t expected_checksum = data[data_length + 3];
	int8_t checksum = 0;

	for (int i = 0; i < data_length + 3; i++){
		checksum -= (int8_t) data[i];
	}

	if (checksum != expected_checksum){
		printf("[WARNING] Tape \"%s\": checksum = 0x%02X (expected 0x%02X)\n",
			name, (unsigned char) checksum, (unsigned char) expected_checksum);
	}

	memcpy(&RAM[start_address], &data[3], data_length);
}

void patinho_feio_state::load_raw_data(const char* name, unsigned int start_address, unsigned int data_length){
	uint8_t *RAM = (uint8_t *) memshare("maincpu:internalram")->ptr();
	uint8_t *data = memregion(name)->base();

	memcpy(&RAM[start_address], data, data_length);
}

DEVICE_IMAGE_LOAD_MEMBER( patinho_feio_state::tape_load )
{
	if (image.loaded_through_softlist())
	{
		paper_tape_length = image.get_software_region_length("rom");
		paper_tape_data = image.get_software_region("rom");
		paper_tape_address = 0;
	}

	return std::make_pair(std::error_condition(), std::string());
}

void patinho_feio_state::machine_start(){
	m_teletype_timer = timer_alloc(FUNC(patinho_feio_state::teletype_callback), this);
	m_decwriter_timer = timer_alloc(FUNC(patinho_feio_state::decwriter_callback), this);

	// Copy some programs directly into RAM.
	// This is a hack for setting up the computer
	// while we don't support loading programs
	// from punched tape rolls...

	//"absolute program example" from page 16.7
	//    Prints "PATINHO FEIO" on the DECWRITER:
	load_tape("exemplo_16.7");

	//"absolute program example" from appendix G:
	//    Allows users to load programs from the
	//    console into the computer memory.
	load_raw_data("hexam", 0xE00, 0x0D5);

	load_raw_data("loader", 0xF80, 0x080);
	//load_raw_data("micro_pre_loader", 0x000, 0x02A); //this is still experimental
}

static INPUT_PORTS_START( patinho_feio )
	PORT_START("RC")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RC bit 0") PORT_CODE(KEYCODE_EQUALS) PORT_TOGGLE
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RC bit 1") PORT_CODE(KEYCODE_MINUS) PORT_TOGGLE
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RC bit 2") PORT_CODE(KEYCODE_0) PORT_TOGGLE
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RC bit 3") PORT_CODE(KEYCODE_9) PORT_TOGGLE
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RC bit 4") PORT_CODE(KEYCODE_8) PORT_TOGGLE
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RC bit 5") PORT_CODE(KEYCODE_7) PORT_TOGGLE
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RC bit 6") PORT_CODE(KEYCODE_6) PORT_TOGGLE
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RC bit 7") PORT_CODE(KEYCODE_5) PORT_TOGGLE
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RC bit 8") PORT_CODE(KEYCODE_4) PORT_TOGGLE
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RC bit 7") PORT_CODE(KEYCODE_3) PORT_TOGGLE
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RC bit 10") PORT_CODE(KEYCODE_2) PORT_TOGGLE
	PORT_BIT(0x800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RC bit 11") PORT_CODE(KEYCODE_1) PORT_TOGGLE

	PORT_START("BUTTONS")
	/* Modo de Operacao: EXECUCAO */
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("NORMAL") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CICLO UNICO") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("INSTRUCAO UNICA") PORT_CODE(KEYCODE_D)
	/* Modo de Operacao: MEMORIA */
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENDERECAMENTO") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ARMAZENAMENTO") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("EXPOSICAO") PORT_CODE(KEYCODE_C)
	/* Comando: */
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ESPERA") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("INTERRUPCAO") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("PARTIDA") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("PREPARACAO") PORT_CODE(KEYCODE_R)
	/* Switches */
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENDERECAMENTO (Fixo/Sequencial)") PORT_CODE(KEYCODE_N) PORT_TOGGLE
	PORT_BIT(0x800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MEMORIA (Liberada/Protegida)") PORT_CODE(KEYCODE_M) PORT_TOGGLE
INPUT_PORTS_END

void patinho_feio_state::patinho_feio(machine_config &config)
{
	/* basic machine hardware */
	/* CPU @ approx. 500 kHz (memory cycle time is 2usec) */
	PATO_FEIO_CPU(config, m_maincpu, 500000);
	m_maincpu->rc_read().set_ioport("RC");
	m_maincpu->buttons_read().set_ioport("BUTTONS");
	m_maincpu->set_update_panel_cb(FUNC(patinho_feio_state::update_panel));

	/* Printer */
//  m_maincpu->iodev_write<5>().set(FUNC(patinho_feio_state::printer_data_w));

	/* Papertape Puncher */
//  m_maincpu->iodev_write<8>().set(FUNC(patinho_feio_state::papertape_punch_data_w));

	/* Card Reader */
//  m_maincpu->iodev_read<9>().set(FUNC(patinho_feio_state::cardreader_data_r));

	/* DECWRITER
	   (max. speed: ?) */
	m_maincpu->iodev_write<10>().set(FUNC(patinho_feio_state::decwriter_data_w));

	/* Teleprinter
	   TeleType ASR33
	   (max. speed: 10 characters per second)
	   with paper tape reading (and optionally punching) capabilities */
	m_maincpu->iodev_write<11>().set(FUNC(patinho_feio_state::teletype_data_w));

	/* Papertape Reader
	   Hewlett-Packard HP-2737-A
	   Optical Papertape Reader (max. speed: 300 characters per second) */
//  m_maincpu->iodev_read<14>().set(FUNC(patinho_feio_state::papertapereader_data_r));

	/* DECWRITER */
	TELEPRINTER(config, m_decwriter, 0);
	m_decwriter->set_keyboard_callback(FUNC(patinho_feio_state::decwriter_kbd_input));

	/* Teletype */
	TELEPRINTER(config, m_tty, 1);
	m_tty->set_keyboard_callback(FUNC(patinho_feio_state::teletype_kbd_input));

	/* punched tape */
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "patinho_tape", "bin").set_device_load(FUNC(patinho_feio_state::tape_load));

	config.set_default_layout(layout_patinho);

	// software lists
//  SOFTWARE_LIST(config, "tape_list").set_original("patinho");
}

ROM_START( patinho )
	ROM_REGION( 0x0d5, "hexam", 0 )
	ROM_LOAD( "apendice_g__hexam.bin", 0x000, 0x0d5, CRC(e608f6d3) SHA1(3f76b5f91d9b2573e70919539d47752e7623e40a) )

	ROM_REGION( 0x0d5, "exemplo_16.7", 0 )
	ROM_LOAD( "exemplo_16.7.bin", 0x000, 0x028, CRC(0a87ac8d) SHA1(7c35ac3eed9ed239f2ef56c26e6f0c59f635e1ac) )

	ROM_REGION( 0x080, "loader", 0 )
	ROM_LOAD( "loader.bin", 0x000, 0x080, BAD_DUMP CRC(c2a8fa9d) SHA1(0ae4f711ef5d6e9d26c611fd2c8c8ac45ecbf9e7) )

	/* Micro pre-loader:
	   This was re-created by professor Joao Jose Neto based on his vague
	   recollection of sequences of opcode values from almost 40 years ago :-) */
	ROM_REGION( 0x02a, "micro_pre_loader", 0 )
	ROM_LOAD( "micro-pre-loader.bin", 0x000, 0x02a, CRC(1921feab) SHA1(bb063102e44e9ab963f95b45710141dc2c5046b0) )
ROM_END

//    YEAR  NAME     PARENT  COMPAT  MACHINE       INPUT         CLASS               INIT               COMPANY                                           FULLNAME         FLAGS
COMP( 1972, patinho, 0,      0,      patinho_feio, patinho_feio, patinho_feio_state, init_patinho_feio, "Escola Politecnica - Universidade de Sao Paulo", "Patinho Feio" , MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )



pb1000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

        Casio PB-1000 / PB-2000c driver

        Driver by Sandro Ronco

        TODO:
        - improve the pb2000c gate array emulation
        - i/o port

        Known issues:
        - the second memory card is not recognized by the HW
        - pb2000c, ai1000: the shift key has to be released before hitting
          the key that is to be shifted. Therefore natural keyboard/paste
          will misbehave for shifted characters.

        More info:
            http://www.itkp.uni-bonn.de/~wichmann/pb1000-wrobel.html

****************************************************************************/


#include "emu.h"
#include "cpu/hd61700/hd61700.h"
#include "machine/nvram.h"
#include "sound/beep.h"
#include "video/hd44352.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"

#include "utf8.h"


namespace {

class pb1000_state : public driver_device
{
public:
	pb1000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_beeper(*this, "beeper")
		, m_hd44352(*this, "hd44352")
		, m_card1(*this, "cardslot1")
		, m_card2(*this, "cardslot2")
		, m_io_keyboard(*this, "KO%u", 0U)
	{ }

	void pb2000c(machine_config &config);
	void pb1000(machine_config &config);

private:
	required_device<hd61700_cpu_device> m_maincpu;
	required_device<beep_device> m_beeper;
	required_device<hd44352_device> m_hd44352;
	optional_device<generic_slot_device> m_card1;
	optional_device<generic_slot_device> m_card2;
	required_ioport_array<13> m_io_keyboard;

	emu_timer *m_kb_timer = nullptr;
	uint8_t m_kb_matrix = 0;
	uint8_t m_gatearray[2]{};

	memory_region *m_rom_reg = nullptr;
	memory_region *m_card1_reg = nullptr;
	memory_region *m_card2_reg = nullptr;

	virtual void machine_start() override ATTR_COLD;
	void gatearray_w(offs_t offset, uint16_t data);
	uint16_t pb1000_kb_r();
	uint16_t pb2000c_kb_r();
	void kb_matrix_w(uint8_t data);
	uint8_t pb1000_port_r();
	uint8_t pb2000c_port_r();
	void port_w(uint8_t data);
	uint16_t read_touchscreen(uint8_t line);
	void pb1000_palette(palette_device &palette) const;
	TIMER_CALLBACK_MEMBER(keyboard_timer);
	void pb1000_mem(address_map &map) ATTR_COLD;
	void pb2000c_mem(address_map &map) ATTR_COLD;
};

void pb1000_state::pb1000_mem(address_map &map)
{
	map.unmap_value_low();
	map(0x00000, 0x00bff).rom();
	//map(0x00c00, 0x00c0f).noprw();   //I/O
	map(0x06000, 0x07fff).ram().share("nvram1");
	map(0x08000, 0x0ffff).bankr("bank1");
	map(0x18000, 0x1ffff).ram().share("nvram2");
}

void pb1000_state::pb2000c_mem(address_map &map)
{
	map.unmap_value_low();
	map(0x00000, 0x0ffff).bankr("bank1");
	map(0x00000, 0x00bff).rom();
	//map(0x00c00, 0x00c0f).noprw();   //I/O
	map(0x00c10, 0x00c11).w(FUNC(pb1000_state::gatearray_w));
	map(0x10000, 0x1ffff).ram().share("nvram1");
	map(0x20000, 0x27fff).r(m_card1, FUNC(generic_slot_device::read16_rom));
	map(0x28000, 0x2ffff).ram().share("nvram2");
}


/* Input ports */
static INPUT_PORTS_START( pb1000 )
	PORT_START("KO0")
	PORT_START("KO1")
		PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BRK")     PORT_CODE(KEYCODE_F10)          PORT_CHAR(UCHAR_MAMEKEY(F10))
		PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("OFF")     PORT_CODE(KEYCODE_7_PAD)
	PORT_START("KO2")
		PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("EXE")     PORT_CODE(KEYCODE_ENTER)        PORT_CHAR(13)
		PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("IN")      PORT_CODE(KEYCODE_F4)           PORT_CHAR(UCHAR_MAMEKEY(F4))
		PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MEMO IN") PORT_CODE(KEYCODE_0_PAD)
		PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(",  ?")    PORT_CODE(KEYCODE_COMMA)        PORT_CHAR(',')  PORT_CHAR('?')
		PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("\"  !")   PORT_CODE(KEYCODE_QUOTE)        PORT_CHAR('"')  PORT_CHAR('!')
		PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("$  #")    PORT_CODE(KEYCODE_1_PAD)        PORT_CHAR('$')  PORT_CHAR('#')
		PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("&  %")    PORT_CODE(KEYCODE_2_PAD)        PORT_CHAR('&')  PORT_CHAR('%')
		PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("=  '")    PORT_CODE(KEYCODE_EQUALS)       PORT_CHAR('=')  PORT_CHAR('\'')
		PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(";  <")    PORT_CODE(KEYCODE_COLON)        PORT_CHAR(';')  PORT_CHAR('<')
		PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(":  >")    PORT_CODE(KEYCODE_BACKSLASH2)   PORT_CHAR(':')  PORT_CHAR('>')
	PORT_START("KO3")
		PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT)       PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
		PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("OUT")     PORT_CODE(KEYCODE_F3)           PORT_CHAR(UCHAR_MAMEKEY(F3))
		PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MEMO")    PORT_CODE(KEYCODE_F11)          PORT_CHAR(UCHAR_MAMEKEY(F11))
		PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U")       PORT_CODE(KEYCODE_U)            PORT_CHAR('U')
		PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q")       PORT_CODE(KEYCODE_Q)            PORT_CHAR('Q')
		PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W")       PORT_CODE(KEYCODE_W)            PORT_CHAR('W')
		PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E")       PORT_CODE(KEYCODE_E)            PORT_CHAR('E')
		PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R")       PORT_CODE(KEYCODE_R)            PORT_CHAR('R')
		PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T")       PORT_CODE(KEYCODE_T)            PORT_CHAR('T')
		PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y")       PORT_CODE(KEYCODE_Y)            PORT_CHAR('Y')
	PORT_START("KO4")
		PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN)         PORT_CHAR(UCHAR_MAMEKEY(DOWN))
		PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CALC")    PORT_CODE(KEYCODE_HOME)
		PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CAL")     PORT_CODE(KEYCODE_F9)           PORT_CHAR(UCHAR_MAMEKEY(F9))
		PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H")       PORT_CODE(KEYCODE_H)            PORT_CHAR('H')
		PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CAPS")    PORT_CODE(KEYCODE_CAPSLOCK)
		PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A")       PORT_CODE(KEYCODE_A)            PORT_CHAR('A')
		PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S")       PORT_CODE(KEYCODE_S)            PORT_CHAR('S')
		PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D")       PORT_CODE(KEYCODE_D)            PORT_CHAR('D')
		PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F")       PORT_CODE(KEYCODE_F)            PORT_CHAR('F')
		PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G")       PORT_CODE(KEYCODE_G)            PORT_CHAR('G')
	PORT_START("KO5")
		PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_UP)   PORT_CODE(KEYCODE_UP)           PORT_CHAR(UCHAR_MAMEKEY(UP))
		PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT)         PORT_CHAR(UCHAR_MAMEKEY(LEFT))
		PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MENU")    PORT_CODE(KEYCODE_F5)           PORT_CHAR(UCHAR_MAMEKEY(F5))
		PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M")       PORT_CODE(KEYCODE_M)            PORT_CHAR('M')
		PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z")       PORT_CODE(KEYCODE_Z)            PORT_CHAR('Z')
		PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X")       PORT_CODE(KEYCODE_X)            PORT_CHAR('X')
		PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C")       PORT_CODE(KEYCODE_C)            PORT_CHAR('C')
		PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V")       PORT_CODE(KEYCODE_V)            PORT_CHAR('V')
		PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B")       PORT_CODE(KEYCODE_B)            PORT_CHAR('B')
		PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N")       PORT_CODE(KEYCODE_N)            PORT_CHAR('N')
	PORT_START("KO6")
		PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LCKEY")   PORT_CODE(KEYCODE_F1)           PORT_CHAR(UCHAR_MAMEKEY(F1))
		PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CONTRAST") PORT_CODE(KEYCODE_F2)          PORT_CHAR(UCHAR_MAMEKEY(F2))
		PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(")")       PORT_CODE(KEYCODE_PGDN)         PORT_CHAR(')')      PORT_CHAR(']')
		PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("STOP")    PORT_CODE(KEYCODE_F7)           PORT_CHAR(UCHAR_MAMEKEY(F7))
		PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("INS")     PORT_CODE(KEYCODE_INSERT)
		PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("NEW ALL") PORT_CODE(KEYCODE_ESC)          PORT_CHAR(27)
		PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BS")      PORT_CODE(KEYCODE_BACKSPACE)    PORT_CHAR(8)
		PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CLS")     PORT_CODE(KEYCODE_DEL)          PORT_CHAR(12)
	PORT_START("KO7")
		PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("^")       PORT_CODE(KEYCODE_3_PAD)        PORT_CHAR('^')
		PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/")       PORT_CODE(KEYCODE_SLASH)        PORT_CHAR('/')      PORT_CHAR('}')
		PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(")       PORT_CODE(KEYCODE_PGUP)         PORT_CHAR('(')      PORT_CHAR('[')
		PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9")       PORT_CODE(KEYCODE_9)            PORT_CHAR('9')      PORT_CHAR('\'')
		PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8")       PORT_CODE(KEYCODE_8)            PORT_CHAR('8')      PORT_CHAR('_')
		PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7")       PORT_CODE(KEYCODE_7)            PORT_CHAR('7')      PORT_CHAR('@')
		PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENG")     PORT_CODE(KEYCODE_F6)           PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_START("KO8")
		PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I")       PORT_CODE(KEYCODE_I)            PORT_CHAR('I')
		PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("*")       PORT_CODE(KEYCODE_ASTERISK)     PORT_CHAR('*')      PORT_CHAR('{')
		PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6")       PORT_CODE(KEYCODE_6)            PORT_CHAR('6')      PORT_CHAR('\\')
		PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5")       PORT_CODE(KEYCODE_5)            PORT_CHAR('5')      PORT_CHAR('~')
		PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4")       PORT_CODE(KEYCODE_4)            PORT_CHAR('4')      PORT_CHAR('|')
		PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P")       PORT_CODE(KEYCODE_P)            PORT_CHAR('P')
		PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O")       PORT_CODE(KEYCODE_O)            PORT_CHAR('O')
	PORT_START("KO9")
		PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J")       PORT_CODE(KEYCODE_J)            PORT_CHAR('J')
		PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+")       PORT_CODE(KEYCODE_PLUS_PAD)     PORT_CHAR('+')
		PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3")       PORT_CODE(KEYCODE_3)            PORT_CHAR('3')
		PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2")       PORT_CODE(KEYCODE_2)            PORT_CHAR('2')
		PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1")       PORT_CODE(KEYCODE_1)            PORT_CHAR('1')
		PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L")       PORT_CODE(KEYCODE_L)            PORT_CHAR('L')
		PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K")       PORT_CODE(KEYCODE_K)            PORT_CHAR('K')
	PORT_START("KO10")
		PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SPACE")   PORT_CODE(KEYCODE_SPACE)        PORT_CHAR(' ')
		PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("-")       PORT_CODE(KEYCODE_MINUS)        PORT_CHAR('-')
		PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("EXE")     PORT_CODE(KEYCODE_ENTER)
		PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ANS")     PORT_CODE(KEYCODE_END)
		PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(".")       PORT_CODE(KEYCODE_STOP)         PORT_CHAR('.')
		PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0")       PORT_CODE(KEYCODE_0)            PORT_CHAR('0')
		PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("??")      PORT_CODE(KEYCODE_5_PAD)
	PORT_START("KO11")
		PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SHIFT")   PORT_CODE(KEYCODE_LSHIFT)       PORT_CHAR(UCHAR_SHIFT_1)
	PORT_START("KO12")
		PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("White F") PORT_CODE(KEYCODE_LALT)

	//touchscreen
	PORT_START("POSX")
		PORT_BIT( 0xff, 0x00, IPT_MOUSE_X ) PORT_CROSSHAIR(X, 1, 0, 0) PORT_SENSITIVITY(20) PORT_KEYDELTA(0)
	PORT_START("POSY")
		PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y ) PORT_CROSSHAIR(Y, 1, 0, 0) PORT_SENSITIVITY(120) PORT_KEYDELTA(0)
	PORT_START("TOUCH")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(MOUSECODE_BUTTON1) PORT_NAME("Touchscreen")
INPUT_PORTS_END

static INPUT_PORTS_START( pb2000c )
	PORT_START("KO0")
	PORT_START("KO1")
		PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BRK")     PORT_CODE(KEYCODE_F10)             PORT_CHAR(UCHAR_MAMEKEY(F10))
		PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("OFF")     PORT_CODE(KEYCODE_7_PAD)
	PORT_START("KO2")
		PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TAB")     PORT_CODE(KEYCODE_TAB)             PORT_CHAR(9)
		PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("'")       PORT_CODE(KEYCODE_1_PAD)           PORT_CHAR(39) PORT_CHAR('!')
		PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CAPS")    PORT_CODE(KEYCODE_CAPSLOCK)
		PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z")       PORT_CODE(KEYCODE_Z)               PORT_CHAR('Z')
		PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A")       PORT_CODE(KEYCODE_A)               PORT_CHAR('A')
		PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q")       PORT_CODE(KEYCODE_Q)               PORT_CHAR('Q') PORT_CHAR('?')
		PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W")       PORT_CODE(KEYCODE_W)               PORT_CHAR('W') PORT_CHAR('@')
	PORT_START("KO3")
		PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(")       PORT_CODE(KEYCODE_PGUP)            PORT_CHAR('(') PORT_CHAR('"')
		PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(")")       PORT_CODE(KEYCODE_PGDN)            PORT_CHAR(')') PORT_CHAR('#')
		PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M1")      PORT_CODE(KEYCODE_F1)              PORT_CHAR(UCHAR_MAMEKEY(F1))
		PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X")       PORT_CODE(KEYCODE_X)               PORT_CHAR('X')
		PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C")       PORT_CODE(KEYCODE_C)               PORT_CHAR('C')
		PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S")       PORT_CODE(KEYCODE_S)               PORT_CHAR('S')
		PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D")       PORT_CODE(KEYCODE_D)               PORT_CHAR('D')
		PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E")       PORT_CODE(KEYCODE_E)               PORT_CHAR('E') PORT_CHAR('\\')
		PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R")       PORT_CODE(KEYCODE_R)               PORT_CHAR('R') PORT_CHAR('_')
	PORT_START("KO4")
		PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("[")       PORT_CODE(KEYCODE_OPENBRACE)       PORT_CHAR('[') PORT_CHAR('$')
		PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("]")       PORT_CODE(KEYCODE_CLOSEBRACE)      PORT_CHAR(']') PORT_CHAR('%')
		PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M2")      PORT_CODE(KEYCODE_F2)              PORT_CHAR(UCHAR_MAMEKEY(F2))
		PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V")       PORT_CODE(KEYCODE_V)               PORT_CHAR('V')
		PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B")       PORT_CODE(KEYCODE_B)               PORT_CHAR('B')
		PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F")       PORT_CODE(KEYCODE_F)               PORT_CHAR('F')
		PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G")       PORT_CODE(KEYCODE_G)               PORT_CHAR('G')
		PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T")       PORT_CODE(KEYCODE_T)               PORT_CHAR('T') PORT_CHAR('`')
		PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y")       PORT_CODE(KEYCODE_Y)               PORT_CHAR('Y') PORT_CHAR('{')
	PORT_START("KO5")
		PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("|")       PORT_CODE(KEYCODE_BACKSLASH)       PORT_CHAR('|') PORT_CHAR('&')
		PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MEMO")    PORT_CODE(KEYCODE_F11)             PORT_CHAR(UCHAR_MAMEKEY(F11))
		PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M3")      PORT_CODE(KEYCODE_F3)              PORT_CHAR(UCHAR_MAMEKEY(F3))
		PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N")       PORT_CODE(KEYCODE_N)               PORT_CHAR('N')
		PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M")       PORT_CODE(KEYCODE_M)               PORT_CHAR('M')
		PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H")       PORT_CODE(KEYCODE_H)               PORT_CHAR('H')
		PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J")       PORT_CODE(KEYCODE_J)               PORT_CHAR('J')
		PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U")       PORT_CODE(KEYCODE_U)               PORT_CHAR('U') PORT_CHAR('}')
		PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I")       PORT_CODE(KEYCODE_I)               PORT_CHAR('I') PORT_CHAR('~')
	PORT_START("KO6")
		PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("IN")      PORT_CODE(KEYCODE_F6)              PORT_CHAR(UCHAR_MAMEKEY(F6))
		PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("OUT")     PORT_CODE(KEYCODE_F7)              PORT_CHAR(UCHAR_MAMEKEY(F7))
		PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M4")      PORT_CODE(KEYCODE_F4)              PORT_CHAR(UCHAR_MAMEKEY(F4))
		PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(",")       PORT_CODE(KEYCODE_COMMA)           PORT_CHAR(',')
		PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SPACE")   PORT_CODE(KEYCODE_SPACE)           PORT_CHAR(' ')
		PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K")       PORT_CODE(KEYCODE_K)               PORT_CHAR('K')
		PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L")       PORT_CODE(KEYCODE_L)               PORT_CHAR('L')
		PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O")       PORT_CODE(KEYCODE_O)               PORT_CHAR('O') PORT_CHAR('<')
		PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P")       PORT_CODE(KEYCODE_P)               PORT_CHAR('P') PORT_CHAR('>')
	PORT_START("KO7")
		PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CALC")    PORT_CODE(KEYCODE_HOME)
		PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ETC")     PORT_CODE(KEYCODE_F12)             PORT_CHAR(UCHAR_MAMEKEY(F12))
		PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Red S")   PORT_CODE(KEYCODE_LSHIFT) // PORT_CHAR(UCHAR_SHIFT_1)  design of the computer breaks this
		PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ANS")     PORT_CODE(KEYCODE_END)
		PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(";")       PORT_CODE(KEYCODE_COLON)           PORT_CHAR(';')
		PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(":")       PORT_CODE(KEYCODE_BACKSLASH)       PORT_CHAR(':')
		PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("=")       PORT_CODE(KEYCODE_EQUALS)          PORT_CHAR('=') PORT_CHAR('^')
	PORT_START("KO8")
		PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("NEW ALL") PORT_CODE(KEYCODE_ESC)             PORT_CHAR(27)
		PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7")       PORT_CODE(KEYCODE_7)               PORT_CHAR('7')
		PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0")       PORT_CODE(KEYCODE_0)               PORT_CHAR('0')
		PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1")       PORT_CODE(KEYCODE_1)               PORT_CHAR('1')
		PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2")       PORT_CODE(KEYCODE_2)               PORT_CHAR('2')
		PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4")       PORT_CODE(KEYCODE_4)               PORT_CHAR('4')
		PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5")       PORT_CODE(KEYCODE_5)               PORT_CHAR('5')
	PORT_START("KO9")
		PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9")       PORT_CODE(KEYCODE_9)               PORT_CHAR('9')
		PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(".")       PORT_CODE(KEYCODE_STOP)            PORT_CHAR('.')
		PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("EXE")     PORT_CODE(KEYCODE_ENTER)           PORT_CHAR(13)
		PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3")       PORT_CODE(KEYCODE_3)               PORT_CHAR('3')
		PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+")       PORT_CODE(KEYCODE_PLUS_PAD)        PORT_CHAR('+')
		PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6")       PORT_CODE(KEYCODE_6)               PORT_CHAR('6')
		PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("-")       PORT_CODE(KEYCODE_MINUS)           PORT_CHAR('-')
	PORT_START("KO10")
		PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DEL")     PORT_CODE(KEYCODE_F8)              PORT_CHAR(UCHAR_MAMEKEY(F8))
		PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MENU")    PORT_CODE(KEYCODE_F5)              PORT_CHAR(UCHAR_MAMEKEY(F5))
		PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("*")       PORT_CODE(KEYCODE_ASTERISK)        PORT_CHAR('*')
		PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BS")      PORT_CODE(KEYCODE_BACKSPACE)       PORT_CHAR(8)
		PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/")       PORT_CODE(KEYCODE_SLASH)           PORT_CHAR('/')
		PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RIGHT")   PORT_CODE(KEYCODE_RIGHT)           PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
		PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CAL")     PORT_CODE(KEYCODE_F9)              PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_START("KO11")
		PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("INS")     PORT_CODE(KEYCODE_INSERT)
		PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UP")      PORT_CODE(KEYCODE_UP)              PORT_CHAR(UCHAR_MAMEKEY(UP))
		PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8")       PORT_CODE(KEYCODE_8)               PORT_CHAR('8')
		PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CLS")     PORT_CODE(KEYCODE_DEL)             PORT_CHAR(12)
		PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LEFT")    PORT_CODE(KEYCODE_LEFT)            PORT_CHAR(UCHAR_MAMEKEY(LEFT))
		PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DOWN")    PORT_CODE(KEYCODE_DOWN)            PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_START("KO12")
INPUT_PORTS_END

void pb1000_state::pb1000_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}


static const gfx_layout pb1000_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8},
	{ 0, 1, 2, 3, 4, 5, 6, 7},
	8*8                     /* 8 bytes */
};

static GFXDECODE_START( gfx_pb1000 )
	GFXDECODE_ENTRY( "hd44352", 0x0000, pb1000_charlayout, 0, 1 )
GFXDECODE_END

void pb1000_state::gatearray_w(offs_t offset, uint16_t data)
{
	m_gatearray[offset] = data&0xff;

	if (m_gatearray[0] && m_card1 && m_card1_reg)
		membank("bank1")->set_base(m_card1_reg->base());
	else if (m_gatearray[1] && m_card2 && m_card2_reg)
		membank("bank1")->set_base(m_card2_reg->base());
	else
		membank("bank1")->set_base(m_rom_reg->base());
}

uint16_t pb1000_state::read_touchscreen(uint8_t line)
{
	uint8_t x = ioport("POSX")->read()/0x40;
	uint8_t y = ioport("POSY")->read()/0x40;

	if (ioport("TOUCH")->read())
	{
		if (x == line-7)
			return (0x1000<<y);
	}

	return 0x0000;
}


uint16_t pb1000_state::pb1000_kb_r()
{
	uint16_t data = 0;
	u8 kb = m_kb_matrix & 15;

	if (kb == 13)
	{
		//Read all the input lines
		for (u8 line = 1; line < 13; line++)
		{
			data |= m_io_keyboard[line]->read();
			data |= read_touchscreen(line);
		}
	}
	else
	if (kb < 13)
	{
		data = m_io_keyboard[kb]->read();
		data |= read_touchscreen(kb);
	}

	return data;
}

uint16_t pb1000_state::pb2000c_kb_r()
{
	uint16_t data = 0;
	u8 kb = m_kb_matrix & 15;

	if (kb == 13)
	{
		//Read all the input lines
		for (u8 line = 1; line < 12; line++)
			data |= m_io_keyboard[line]->read();
	}
	else
	if (kb < 12)
		data = m_io_keyboard[kb]->read();

	return data;
}

void pb1000_state::kb_matrix_w(uint8_t data)
{
	if (BIT(data, 7))
	{
		if (!BIT(m_kb_matrix, 7))
			m_kb_timer->adjust(attotime::never, 0, attotime::never);
	}
	else
	{
		if (BIT(m_kb_matrix, 6) != BIT(data, 6))
		{
			if (BIT(data, 6))
				m_kb_timer->adjust(attotime::from_hz(32), 0, attotime::from_hz(32));
			else
				m_kb_timer->adjust(attotime::from_hz(256), 0, attotime::from_hz(256));
		}
	}

	m_kb_matrix = data;
}

uint8_t pb1000_state::pb1000_port_r()
{
	//TODO
	return 0x00;
}

uint8_t pb1000_state::pb2000c_port_r()
{
	//TODO
	return 0xfc;
}

void pb1000_state::port_w(uint8_t data)
{
	m_beeper->set_state((BIT(data,7) ^ BIT(data,6)));
	//printf("%x\n", data);
}


TIMER_CALLBACK_MEMBER(pb1000_state::keyboard_timer)
{
	m_maincpu->set_input_line(HD61700_KEY_INT, ASSERT_LINE);
	m_maincpu->set_input_line(HD61700_KEY_INT, CLEAR_LINE);
}

void pb1000_state::machine_start()
{
	std::string region_tag;
	m_rom_reg = memregion("rom");
	if (m_card1)
		m_card1_reg = memregion(region_tag.assign(m_card1->tag()).append(GENERIC_ROM_REGION_TAG).c_str());
	if (m_card2)
		m_card2_reg = memregion(region_tag.assign(m_card2->tag()).append(GENERIC_ROM_REGION_TAG).c_str());

	membank("bank1")->set_base(m_rom_reg->base());

	m_kb_timer = timer_alloc(FUNC(pb1000_state::keyboard_timer), this);
	m_kb_timer->adjust(attotime::from_hz(192), 0, attotime::from_hz(192));
}

void pb1000_state::pb1000(machine_config &config)
{
	/* basic machine hardware */
	HD61700(config, m_maincpu, 910000);
	m_maincpu->set_addrmap(AS_PROGRAM, &pb1000_state::pb1000_mem);
	m_maincpu->lcd_ctrl().set(m_hd44352, FUNC(hd44352_device::control_write));
	m_maincpu->lcd_read().set(m_hd44352, FUNC(hd44352_device::data_read));
	m_maincpu->lcd_write().set(m_hd44352, FUNC(hd44352_device::data_write));
	m_maincpu->kb_read().set(FUNC(pb1000_state::pb1000_kb_r));
	m_maincpu->kb_write().set(FUNC(pb1000_state::kb_matrix_w));
	m_maincpu->port_read().set(FUNC(pb1000_state::pb1000_port_r));
	m_maincpu->port_write().set(FUNC(pb1000_state::port_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("hd44352", FUNC(hd44352_device::screen_update));
	screen.set_size(192, 32);
	screen.set_visarea(0, 192-1, 0, 32-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(pb1000_state::pb1000_palette), 2);
	GFXDECODE(config, "gfxdecode", "palette", gfx_pb1000);

	HD44352(config, m_hd44352, 910000);
	m_hd44352->on_cb().set_inputline("maincpu", HD61700_ON_INT);

	NVRAM(config, "nvram1", nvram_device::DEFAULT_ALL_0);
	NVRAM(config, "nvram2", nvram_device::DEFAULT_ALL_0);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 3250).add_route(ALL_OUTPUTS, "mono", 1.00);
}

void pb1000_state::pb2000c(machine_config &config)
{
	pb1000(config);

	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &pb1000_state::pb2000c_mem);
	m_maincpu->kb_read().set(FUNC(pb1000_state::pb2000c_kb_r));
	m_maincpu->port_read().set(FUNC(pb1000_state::pb2000c_port_r));

	GENERIC_CARTSLOT(config, m_card1, generic_plain_slot, "pb2000c_card");
	GENERIC_CARTSLOT(config, m_card2, generic_plain_slot, "pb2000c_card");

	/* Software lists */
	SOFTWARE_LIST(config, "card_list").set_original("pb2000c");
}

/* ROM definition */

ROM_START( pb1000 )
	ROM_REGION( 0x30000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "hd61700.bin", 0x0000, 0x1800, CRC(b28c21a3) SHA1(be7ea62a15ff0c612f6efb2c95e6c2a11a738423))

	ROM_REGION( 0x10000, "rom", ROMREGION_ERASE )
	ROM_SYSTEM_BIOS(0, "basic", "BASIC")
	ROMX_LOAD( "pb1000.bin", 0x0000, 0x8000, CRC(8127a090) SHA1(067c1c2e7efb5249e95afa7805bb98543b30b630), ROM_BIOS(0) | ROM_SKIP(1))
	ROM_SYSTEM_BIOS(1, "basicj", "BASIC Jap")
	ROMX_LOAD( "pb1000j.bin", 0x0000, 0x8000, CRC(14a0df57) SHA1(ab47bb54eb2a24dcd9d2663462e9272d974fa7da), ROM_BIOS(1) | ROM_SKIP(1))

	ROM_REGION( 0x0800, "hd44352", 0 )
	ROM_LOAD( "charset.bin", 0x0000, 0x0800, CRC(7f144716) SHA1(a02f1ecc6dc0ac55b94f00931d8f5cb6b9ffb7b4))
ROM_END


ROM_START( pb2000c )
	ROM_REGION( 0x1800, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "hd61700.bin", 0x0000, 0x1800, CRC(25f9540c) SHA1(ecf98efadbdd4d1a74bc183eaf23f7113f2a12b1))

	ROM_REGION( 0x20000, "rom", ROMREGION_ERASE )
	ROMX_LOAD( "pb2000c.bin", 0x0000, 0x10000, CRC(41933631) SHA1(70b654dc375b647afa042baf8b0a139e7fa604e8), ROM_SKIP(1))

	ROM_REGION( 0x0800, "hd44352", 0 )
	ROM_LOAD( "charset.bin", 0x0000, 0x0800, CRC(7f144716) SHA1(a02f1ecc6dc0ac55b94f00931d8f5cb6b9ffb7b4))
ROM_END


ROM_START( ai1000 )
	ROM_REGION( 0x1800, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "hd61700.bin", 0x0000, 0x1800, CRC(25f9540c) SHA1(ecf98efadbdd4d1a74bc183eaf23f7113f2a12b1))

	ROM_REGION( 0x20000, "rom", ROMREGION_ERASE )
	ROMX_LOAD( "ai1000.bin", 0x0000, 0x10000, CRC(72aa3ee3) SHA1(ed1d0bc470902ea73bc4588147a589b1793afb40), ROM_SKIP(1))

	ROM_REGION( 0x0800, "hd44352", 0 )
	ROM_LOAD( "charset.bin", 0x0000, 0x0800, CRC(7f144716) SHA1(a02f1ecc6dc0ac55b94f00931d8f5cb6b9ffb7b4))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT    CLASS         INIT        COMPANY  FULLNAME    FLAGS */
COMP( 1987, pb1000,  0,       0,      pb1000,  pb1000,  pb1000_state, empty_init, "Casio", "PB-1000",  MACHINE_NOT_WORKING)
COMP( 1989, pb2000c, 0,       0,      pb2000c, pb2000c, pb1000_state, empty_init, "Casio", "PB-2000c", MACHINE_NOT_WORKING)
COMP( 1989, ai1000,  pb2000c, 0,      pb2000c, pb2000c, pb1000_state, empty_init, "Casio", "AI-1000",  MACHINE_NOT_WORKING)



pc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol, Miodrag Milanovic
/*****************************************************************************

    drivers/pc.c

Driver file for IBM PC, IBM PC XT, and related machines.

    PC-XT memory map

    00000-9FFFF   RAM
    A0000-AFFFF   NOP       or videoram EGA/VGA
    B0000-B7FFF   videoram  MDA, page #0
    B8000-BFFFF   videoram  CGA and/or MDA page #1, T1T mapped RAM
    C0000-C7FFF   NOP       or ROM EGA/VGA
    C8000-C9FFF   ROM       XT HDC #1
    CA000-CBFFF   ROM       XT HDC #2
    D0000-EFFFF   NOP       or 'adapter RAM'
    F0000-FDFFF   NOP       or ROM Basic + other Extensions
    FE000-FFFFF   ROM

******************************************************************************/

#include "emu.h"
#include "machine/genpc.h"
#include "machine/i8251.h"
#include "cpu/i86/i86.h"
#include "cpu/i86/i186.h"
#include "cpu/nec/nec.h"
#include "bus/isa/isa.h"
#include "bus/isa/isa_cards.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "softlist_dev.h"

/******************************************************* Generic PC with CGA ***/


namespace {

class pc_state : public driver_device
{
public:
	pc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu")
	{ }

	void ataripc1(machine_config &config);
	void coppc400(machine_config &config);
	void ncrpc4i(machine_config &config);
	void kaypro16(machine_config &config);
	void kaypropc(machine_config &config);
	void m15(machine_config &config);
	void bondwell(machine_config &config);
	void siemens(machine_config &config);
	void iskr3104(machine_config &config);
	void poisk2(machine_config &config);
	void dgone(machine_config &config);
	void pccga(machine_config &config);
	void mk88(machine_config &config);
	void eppc(machine_config &config);
	void olystar20f(machine_config &config);
	void olytext30(machine_config &config);
	void zenith(machine_config &config);
	void eagle1600(machine_config &config);
	void laser_turbo_xt(machine_config &config);
	void comport(machine_config &config);
	void mpc1600(machine_config &config);
	void ittxtra(machine_config &config);
	void cadd810(machine_config &config);
	void juko16(machine_config &config);
	void alphatp50(machine_config &config);
	void mbc16lt(machine_config &config);
	void modernxt(machine_config &config);
	void earthst(machine_config &config);
	void vpcii(machine_config &config);
	void fraking(machine_config &config);
	void ec1847(machine_config &config);

	void init_bondwell();

	DECLARE_INPUT_CHANGED_MEMBER(pc_turbo_callback);

private:
	required_device<cpu_device> m_maincpu;

	double m_turbo_off_speed = 0;

	static void cfg_dual_720K(device_t *device);
	static void cfg_single_360K(device_t *device);
	static void cfg_single_720K(device_t *device);

	void pc16_io(address_map &map) ATTR_COLD;
	void pc16_map(address_map &map) ATTR_COLD;
	void pc8_io(address_map &map) ATTR_COLD;
	void pc8_map(address_map &map) ATTR_COLD;
	void pc8_flash_map(address_map &map) ATTR_COLD;
	void zenith_map(address_map &map) ATTR_COLD;
};

void pc_state::pc8_map(address_map &map)
{
	map.unmap_value_high();
	map(0xf0000, 0xfffff).rom().region("bios", 0);
}

void pc_state::pc16_map(address_map &map)
{
	map.unmap_value_high();
	map(0xf0000, 0xfffff).rom().region("bios", 0);
}

void pc_state::pc8_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m("mb", FUNC(ibm5160_mb_device::map));
}

void pc_state::pc16_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m("mb", FUNC(ibm5160_mb_device::map));
	map(0x0070, 0x007f).ram(); // needed for Poisk-2
}

INPUT_CHANGED_MEMBER(pc_state::pc_turbo_callback)
{
	m_maincpu->set_clock_scale((newval & 2) ? 1 : m_turbo_off_speed);
}



static INPUT_PORTS_START( pccga )
	PORT_START("DSW1") /* IN2 */
	PORT_DIPNAME( 0x80, 0x80, "COM1: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x40, 0x40, "COM2: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x20, 0x00, "COM3: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x10, 0x00, "COM4: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x08, 0x08, "LPT1: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x04, 0x00, "LPT2: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x02, 0x00, "LPT3: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x01, 0x00, "Game port enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x01, DEF_STR( Yes ) )

	PORT_START("DSW2") /* IN3 */
	PORT_DIPNAME( 0x08, 0x08, "HDC1 (C800:0 port 320-323)")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x04, 0x04, "HDC2 (CA00:0 port 324-327)")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Yes ) )
	PORT_BIT( 0x02, 0x02,   IPT_UNUSED ) /* no turbo switch */
	PORT_BIT( 0x01, 0x01,   IPT_UNUSED )
INPUT_PORTS_END


static DEVICE_INPUT_DEFAULTS_START( pccga )
	DEVICE_INPUT_DEFAULTS("DSW0", 0x30, 0x20)
DEVICE_INPUT_DEFAULTS_END


// Floppy configurations
void pc_state::cfg_dual_720K(device_t *device)
{
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:0")).set_default_option("35dd");
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:1")).set_default_option("35dd");
}

void pc_state::cfg_single_360K(device_t *device)
{
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:0")).set_default_option("525dd");
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:0")).set_fixed(true);
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:1")).set_default_option(nullptr);
}

void pc_state::cfg_single_720K(device_t *device)
{
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:0")).set_default_option("35dd");
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:0")).set_fixed(true);
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:1")).set_default_option(nullptr);
}

void pc_state::pccga(machine_config &config)
{
	/* basic machine hardware */
	i8088_cpu_device &maincpu(I8088(config, "maincpu", XTAL(14'318'181)/3)); /* 4.77 MHz */
	maincpu.set_addrmap(AS_PROGRAM, &pc_state::pc8_map);
	maincpu.set_addrmap(AS_IO, &pc_state::pc8_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	ibm5160_mb_device &mb(IBM5160_MOTHERBOARD(config, "mb"));
	mb.set_cputag(m_maincpu);
	mb.int_callback().set_inputline(m_maincpu, 0);
	mb.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	mb.kbdclk_callback().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	mb.kbddata_callback().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));
	mb.set_input_default(DEVICE_INPUT_DEFAULTS_NAME(pccga));

	// FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa1", 0, "mb:isa", pc_isa8_cards, "cga", false);
	ISA8_SLOT(config, "isa2", 0, "mb:isa", pc_isa8_cards, "fdc_xt", false);
	ISA8_SLOT(config, "isa3", 0, "mb:isa", pc_isa8_cards, "lpt", false);
	ISA8_SLOT(config, "isa4", 0, "mb:isa", pc_isa8_cards, "com", false);
	ISA8_SLOT(config, "isa5", 0, "mb:isa", pc_isa8_cards, nullptr, false);

	/* keyboard */
	pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_IBM_PC_XT_83));
	kbd.out_clock_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_clock_w));
	kbd.out_data_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_data_w));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("640K").set_extra_options("64K, 128K, 256K, 512K");

	/* software lists */
	SOFTWARE_LIST(config, "disk_list").set_original("ibm5150");
}


/**************************************************************** Atari PC1 ***

Links:  http://www.ataripc.net/pc1-8088/ ; http://krap.pl/mirrorz/atari/www.atari-computermuseum.de/pc.htm ;
        http://www.atari-computermuseum.de/pc1.htm
Form Factor: Desktop
CPU: 8088 @ 4.77 MHz / 8 MHz
RAM: 512K / 640KB
Bus: ISA signals available on board, no slot
Video: Hercules/CGA/EGA
Mass storage: 1 5.25" 360K floppy
On board ports: floppy, graphics, parallel, serial, mouse, external floppy
Options: 8087 FPU
Expansion: Up to two external floppy drives: PCF554, SF314 or SF354

***************************************************************** Atari PC2 ***

Links:  http://www.binarydinosaurs.co.uk/Museum/atari/pc2.php ; http://www.ataripc.net/pc2-8088/ ;
        http://www.ataripc.net/components/
Info: The Atari PC2 mainboard has only one ISA slot, but is expanded via a four slot riser card. BIOS is identical to later PC1 and PC3
CPU: 8088 @ 4.77 MHz / 8 MHz
RAM: 512K / 640KB
Bus: 4x ISA
Video: Hercules/CGA/EGA
Mass storage: 1 5.25" 360K floppy and 1 5.25" 360K floppy or 20MB hard drive
On board ports: floppy, external floppy (Atari ST style), graphics, parallel, serial, mouse
Expansion: 8087 FPU

DIP switches:                      Sw.1  Sw.2  Sw.3  Sw.4
                EGA Monitor         OFF    ON   OFF   OFF
                Color Monitor        ON   OFF   OFF    ON
                Monochrome Monitor   ON   OFF    ON    ON

EGA.COM, CGA.COM, HGC.COM, MDA.COM, PALETTE.COM, HCOLOR.COM and CURSOR are utilities to change
the behavior of the integrated graphics card.

Turbo option: From DOS, commands "TURBO ON" and "TURRBO OFF or key combos [Ctrl][Alt][1] or
[Ctrl][Alt][+] for Turbo on, [Ctrl][Alt][2] or [Ctrl][Alt][-] for Turbo off
Keyboard click: From DOS, "CLICK ON" and "CLICK OFF" or key combos [Ctrl][Alt][<]
for click on, [Ctrl][Alt][>] for click off

******************************************************************************/

void pc_state::ataripc1(machine_config &config)
{
	pccga(config);
	subdevice<isa8_slot_device>("isa1")->set_default_option("ega");
	subdevice<isa8_slot_device>("isa2")->set_option_machine_config("fdc_xt", cfg_single_360K);
}

ROM_START ( ataripc1 )
	ROM_REGION(0x10000,"bios", 0)
	ROM_SYSTEM_BIOS( 0, "v3.06", "v3.06" )
	ROMX_LOAD("award_atari_pc_bios_3.06.bin", 0x8000, 0x8000, CRC(256427ce) SHA1(999f6af64b79f88c1d3492f386d9bee08efb50e7), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v3.07", "v3.07" )
	ROMX_LOAD("award_atari_pc_bios_3.07.bin", 0x8000, 0x8000, CRC(a73b80e6) SHA1(03af5902cdfd1cde217022b823162f24aba435ab), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "v3.08", "v3.08" )
	ROMX_LOAD("award_atari_pc_bios_3.08.bin", 0x8000, 0x8000, CRC(929a2443) SHA1(8e98f3c9180c55b1f5521727779c016083d27960), ROM_BIOS(2)) //same as on Atari PC3, also used on Atari PC2
ROM_END


/**************************************************************** Atari PC3 ***

Links:  http://www.atari-computermuseum.de/pc1.htm , http://trelohra.blogspot.de/2015/06/atari-pc3.html ,
        http://www.ataripc.net/pc3-8088/
Form Factor: Desktop
CPU: 8088 @ 4.77 MHz or 8 MHz
RAM: 640K
Bus: 5x ISA:    1) Adaptec ACB-2072 RLL Controller Card
Video: On-board MDA/CGA/Hercules/EGA
Mass storage: 1x 5.25" 360K floppy + 30MB RLL harddisk
On board ports: floppy, parallel, serial, mouse, speaker
Options: 8087 FPU
DIP switches:                    4   3   2   1
                EGA (smart on)  OFF OFF  ON OFF
                EGA (smart OFF) OFF  ON  ON OFF
                Color monitor    ON OFF OFF  ON
                monochrome       ON  ON OFF  ON

If you add a monochrome adapter board, set the switches to "Smart OFF", the HGC, MDA and HCOLOR
options are meaningless, then.
EGA.COM, CGA.COM, HGC.COM, MDA.COM, PALETTE.COM, HCOLOR.COM and CURSOR are utilities to change
the behavior of the integrated graphics card.

Turbo option: From DOS, commands "TURBO ON" and "TURRBO OFF or key combos [Ctrl][Alt][1] or
[Ctrl][Alt][+] for Turbo on, [Ctrl][Alt][2] or [Ctrl][Alt][-] for Turbo off
Keyboard click: From DOS, "CLICK ON" and "CLICK OFF" or key combos [Ctrl][Alt][<]
for click on, [Ctrl][Alt][>] for click off

******************************************************************************/

ROM_START( ataripc3 )
	ROM_REGION(0x10000,"bios", 0)
	ROM_LOAD("c101701-004 308.u61",0x8000, 0x8000, CRC(929a2443) SHA1(8e98f3c9180c55b1f5521727779c016083d27960))

	ROM_REGION(0x8000,"gfx1", 0)
	ROM_LOAD("5788005.u33", 0x00000, 0x2000, BAD_DUMP CRC(0bf56d70) SHA1(c2a8b10808bf51a3c123ba3eb1e9dd608231916f)) // not the real character ROM

	ROM_REGION(0x8000,"plds", 0)
	ROM_LOAD("c101681 6ffb.u60",0x000, 0x100, NO_DUMP ) // PAL20L10NC
ROM_END


/******************************************** Bondwell BW230 (Pro28 series) ***

Links:  http://gallery.fdd5-25.net/details.php?image_id=3463&sessionid=1eaeb42abdf2758a020b16204a2a8e5a ;
        http://www.zonadepruebas.com/viewtopic.php?t=3696 ;
        ftp://ftp.whtech.com/emulators/mess/old/Complete%20MESS%20Geneve%20emulation/mess/sysinfo/bondwell.htm
Info:   Info is hard to come by. A BW230 is nowhere to be found, the links about the Pro28 series suggest an XT
        compatible built around a passive backplane and a slot CPU. This is confirmed by the old MESS info.
Form Factor: Desktop
CPU: 8088 @ 4.77 MHz (MESS sysinfo: 3.75)/ 8 MHz
RAM: 512K / 640K
Bus: at least 2x ISA:   1)  CPU, RAM, Floppy controller
                        2)  Graphics, Game, Parallel
Video: Hercules/CGA
Mass storage: 1x 5.25" 360K and 20/30MB Harddisk.

******************************************************************************/

void pc_state::init_bondwell()
{
	m_turbo_off_speed = 4.77/12;
}

static INPUT_PORTS_START( bondwell )
	PORT_INCLUDE(pccga)

	PORT_MODIFY("DSW2") /* IN3 */
	PORT_DIPNAME( 0x02, 0x02, "Turbo Switch" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc_state::pc_turbo_callback), 0)
	PORT_DIPSETTING(    0x00, "Off (4.77 MHz)" )
	PORT_DIPSETTING(    0x02, "On (12 MHz)" )
INPUT_PORTS_END

void pc_state::bondwell(machine_config &config)
{
	pccga(config);

	i8088_cpu_device &maincpu(I8088(config.replace(), "maincpu", 4772720)); /* turbo? */
	maincpu.set_addrmap(AS_PROGRAM, &pc_state::pc8_map);
	maincpu.set_addrmap(AS_IO, &pc_state::pc8_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));
}

ROM_START( bw230 )
	ROM_REGION(0x10000,"bios", 0)
	ROM_LOAD("bondwell.bin", 0xe000, 0x2000, CRC(d435a405) SHA1(a57c705d1144c7b61940b6f5c05d785c272fc9bb))
ROM_END


/****************************************** Columbia Data Products MPC 1600 ***

Links:  https://www.old-computers.com/museum/computer.asp?st=1&c=633, https://winworldpc.com/download/6f07e280-9d12-7ae2-80a6-11c3a6e28094,
        http://www.minuszerodegrees.net/rom/rom.htm
Form Factor: Desktop
CPU: 8088 @ 4.77 MHz
RAM: 128K, up to 1MB
Bus: 8x ISA
Video: CGA
Mass storage: 2x 5.25" 320K
On board ports: Floppy, serial, console, Centronics, hard disk ("The Winchester disk interface is an 8 bit parallel data port with 4 control lines
    for byte and unit synchronization. This interface connects the MPC system board to the CDP cache buffered Winchester controller.")
Options: 5MB harddisk, light pen
ToDo: The ROM for the CGA is available (see ROM section)

If all of the testing is accomplished, the system will respond with a single 1/2 second tone and continue according to the input output media attached.
If a dumb terminal is used, another tone will sound and the system will produce another 1/2 second tone then wait until an ASCII period(.)is typed at the
terminal. The system uses the ASCII character to determine the baud-rate of the terminal device. If an ASCII(.)is not received in 5 seconds, the system
will default to 19200 baud. If a keyboard and monitor is attached, then no baud rate determination is required.
Note:Type[ESC] here to activate the ROM monitor for system testing. If a response is not made within five (5) seconds, the MPC will automatically
enter the system bootstrap sequence detailed below.

******************************************************************************/

void pc_state::mpc1600(machine_config &config)
{
	pccga(config);
	ISA8_SLOT(config, "isa6", 0, "mb:isa", pc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa7", 0, "mb:isa", pc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa8", 0, "mb:isa", pc_isa8_cards, nullptr, false);
	subdevice<ram_device>(RAM_TAG)->set_default_size("128K").set_extra_options("256K, 512K, 640K");
}

ROM_START( mpc1600 )
	ROM_REGION(0x10000,"bios", 0)
	ROM_LOAD("mpc4.34_u45.bin",  0xf000, 0x01000, CRC(ed9a11b3) SHA1(ca819579e6c2a06cddacf893e1f57c5b37723d90))
	ROM_LOAD("mpc4.34_u46.bin",  0xe000, 0x01000, CRC(33a87335) SHA1(a8ee188cbb93fe32c6cde881bdf3b9c783a59a5b))
	ROM_LOAD("mpc4.34_u47.bin",  0xd000, 0x01000, CRC(cc2e4c28) SHA1(3b02be4bebe2b57098102eca04f738df50a734a4))

	ROM_REGION(0x8000,"gfx1", 0)
	ROM_LOAD("mpc_vid-1.0.bin", 0x00000, 0x0800, CRC(a362ffe6) SHA1(1fddd01dcc0fa8c002ced3a1a94873dccdc88424)) // CGA Card
ROM_END


/********************************************************** Compaq Portable ***

Links:  https://en.wikipedia.org/wiki/Compaq_Portable , http://oldcomputers.net/compaqi.html ,
        http://www.digibarn.com/collections/systems/compaq/index.html ,
        http://www.old-computers.com/museum/computer.asp?c=547 , https://www.seasip.info/VintagePC/compaq.html
Form Factor: Luggable
CPU: 8088 @ 4.77 MHz
RAM: 128K, up to 640KB
Bus: 5x ISA
Video: CGA/MDA capable card, both fonts available, Ctrl+Alt+> switches between internal and external monitor
Mass storage: 1/2x 5.25" double sided/double density (320K/360K), Plus: 10-21MB harddisk

SW1: 1   2   3   4   5   6   7   8  Descr.
    OFF                             Not used/always OFF (def.)
     ON                             Coprocessor/always ON (def.)
            OFF OFF                 Processor board memory/always OFF (def.)
                     ON OFF         Compaq video display Controller board (def.)
                    OFF OFF         Optional monochrome video board
                             ON  ON 1 Diskette drive (def.)
                            OFF  ON 2
                             ON OFF 3
                            OFF OFF

SW2: 1   2   3   4   5   6   7   8  Descr.
     ON OFF  ON  ON OFF OFF OFF OFF 128 Kbyte total memory
     ON  ON OFF  ON OFF OFF OFF OFF 192 Kbyte total memory
     ON OFF OFF  ON OFF OFF OFF OFF 156 Kbyte total memory
     ON  ON  ON OFF OFF OFF OFF OFF 320 Kbyte total memory
     ON OFF  ON OFF OFF OFF OFF OFF 384 Kbyte total memory
     ON  ON OFF OFF OFF OFF OFF OFF 448 Kbyte total memory
     ON OFF OFF OFF OFF OFF OFF OFF 512 Kbyte total memory
    OFF OFF OFF OFF OFF OFF OFF OFF 544 Kbyte total memory

If the ROMs installed in socket(s) U40 (and U47 if available) are Revision C or above,
SW2 is ignored.  Therefore, on system boards ofRevision J or above, SW2 has been removed.
If Revision C ROMs or above are installed, 256K x 1 RAM chips may be usedinstead of 64K x 1 bit
RAM chips in banks 2 and 3 of the system board.  To dothis, however, a new decoder PROM must
be used in socket U35:o PN 101257-001 (No longer available) if banks 2 and 3 are filled with
256K x 1 RAM chips for a total of 640 Kbytes.o  PN 101256-001 if only bank 3 is filled with
256K x 1 RAM chips for a total of 448 Kbytes.

******************************************************************************/

void pc_state::comport(machine_config &config)
{
	pccga(config);
	subdevice<isa8_slot_device>("isa1")->set_default_option("ega");
	subdevice<ram_device>(RAM_TAG)->set_default_size("128K").set_extra_options("256K, 512K, 640K");
}

ROM_START( comport )
	ROM_REGION(0x10000, "bios", 0)
	ROM_DEFAULT_BIOS("rev.c") // set to use EGA until the proper graphics card is emulated, the earlier rev.b doesn't like that
	ROM_SYSTEM_BIOS( 0, "rev.b", "rev.b" )
	ROMX_LOAD("award_atari_pc_bios_3.06.bin", 0xe000, 0x2000, CRC(e9a055b2) SHA1(faa31687ef3d967c5e46d6b2546a28efb79a2097), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "rev.c", "rev.c" )
	ROMX_LOAD("compaq_portable_rev_c.bin", 0xe000, 0x2000, CRC(1d1f7c38) SHA1(d9782eb46cd1a7d40f3e8b37eb48db04ac588acb), ROM_BIOS(1))

	ROM_REGION(0x8000,"gfx1", 0)
	ROM_LOAD("compaq_portable_video_cpqvid.bin", 0x0000, 0x1ffe, BAD_DUMP CRC(3ae64565) SHA1(6eeb06620e588a2f7bfab72eb4fadbd70503ea94))
ROM_END

/*********************************************************** Compaq Deskpro ***
Links: https://www.atarimagazines.com/creative/v11n5/32_Compaq_Deskpro_versus_IBM.php
Info: Four equipment levels from factory
Form Factor: Desktop
CPU: 8086 @ 7.16 MHz
RAM: 128KB (models 1-3), 384KB (model 4), all expandable to 640KB on the motherboard
Bus: 8x ISA
Video: on board
Display: green or amber 12" CGA monitor
Mass storage: 1x5.25" 360K (model 1), 2x 5.25" 360K (model 2), 1x5.25" floppy and 1x10MB hard disk (model 3), model 4 adds a 10MB streamer unit
Ports: serial, parallel, ext. floppy, RTC (from model 3 up)

******************************************************************************/
ROM_START( comdesk ) // set to juko16 specs, changed those to EGA ... period correct and gets comdesk running while the original CGA isn't emulated yet
	ROM_REGION16_LE(0x10000,"bios", 0)
	ROM_LOAD("compaq_bios_revision_j_106265-002.bin", 0xe000, 0x2000, CRC(d861c857) SHA1(62b8f15e5eddc035b51196e79bbca7bb26d73d1f))
ROM_END

/************************************************** Data General One / DG-1 ***

Links: http://www.1000bit.it/ad/bro/datageneral/DG-ONE-PersonalSystem.pdf , http://www.1000bit.it/ad/bro/datageneral/DG-ONE-Interduction-PR.pdf ,
       http://www.oldcomputers.net/data-general-one.html , http://forums.bannister.org/ubbthreads.php?ubb=showflat&Number=30897&page=all
Info: According to the discussion in the thread, the ROM we have is from the original version. Specs for later permutations can be found on oldcomputers.net
Form Factor: Laptop
CPU: 80C88 @ 4 MHz
RAM: 128K - 256K - 384K - 512K internally
Bus: no internal slots
Video: On board, Text mode 80x25 with 8x8 or 8x10 characters, CGA
Display: non-backlit LCD 640x256 pixels
Mass storage: 1/2x Floppy 3.5" 720K
On board Ports: Floppy, RTC, 1x RS232C + 1x RS232C/RS422 via 8251, speaker
Options: ext. 5.25" Floppy, int. Bell 103A 300 Baud Modem, 8087 FPU
Expansion: Expansion box, with 5 ISA slots and space for a 5.25" drive and a harddisk; specifically mentioned are the 5.25" drive, color graphics and memory expansion via ISA cards

******************************************************************************/

void pc_state::dgone(machine_config &config)
{
	pccga(config);
	subdevice<isa8_slot_device>("isa2")->set_option_machine_config("fdc_xt", cfg_dual_720K);
}

ROM_START( dgone )
	ROM_REGION(0x10000,"bios", 0)
	ROM_LOAD( "dgone.bin",  0x8000, 0x08000, CRC(2c38c86e) SHA1(c0f85a000d1d13cd354965689e925d677822549e))
ROM_END


/*************************************************************** Eagle 1600 ***

Links: https://archive.org/details/bitsavers_eagleCompu00Brochure_9975235 , http://www.vcfed.org/forum/showthread.php?49510-Eagle-Computer-model-list ,
       http://bitsavers.trailing-edge.com/pdf/eagleComputer/1600/1600_Series_Training_Notes.pdf
Info:   Eagle 1620 - 8086/128K, 2 Quad density floppy drives, 4 Expansion slots available, ~1983, Eagle 1630 - 8086/128K,
        1 Quad density floppy drive, 10MB HD, 3 Expansion Slots available (Same as 1620 with hard drive), ~1983
        Eagle 1640 - 8086/512K, 1 Quad density floppy drive, 32MB HD, 3 Expansion Slots available, ~1984
        The native floppy format is 780K, 2 sides, 80 tracks/side, 1024 bytes/sector, 5 sectors per track. Standard 360K disks can be read
        Holding "T" and resetting starts a system diagnostics test
Form Factor: Desktop
CPU: 8086 @ 8 MHz
RAM: 128K / 512K
Bus: 8xISA:     1) SASI board, connects to a XEBEC Sl410 SASI => MFM bridge board
                2) Floppy controller
                3) empty
                4) Video/graphics controller board
                5) empty
                6) empty
                7) Serial board: 2x serial, one sync/async, one async only
                8) Parallel board
Video: 80x25 text mode, 720x352 pixel graphics mode
Mass storage: 1x 5.25" QD 780K floppy and 1x 5.25" QD 820K floppy or 10/30MB MFM harddisk
Options: 8087 FPU, EagleNet File server, EightPort serial card, High Resolution color board and video, Video Cassette Adapter board for 80MB backup on video cassette

******************************************************************************/

void pc_state::eagle1600(machine_config &config)
{
	pccga(config);

	i8086_cpu_device &maincpu(I8086(config.replace(), "maincpu", 8000000));
	maincpu.set_addrmap(AS_PROGRAM, &pc_state::pc16_map);
	maincpu.set_addrmap(AS_IO, &pc_state::pc16_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));
}

ROM_START( eagle1600 )
	ROM_REGION16_LE(0x10000, "bios", 0)
	ROMX_LOAD("eagle 1600 62-2732-001 rev e u403.bin",0xe000, 0x1000, CRC(3da1e96a) SHA1(77861ba5ebd056da1daf048f5abd459e0528666d), ROM_SKIP(1))
	ROMX_LOAD("eagle 1600 62-2732-002 rev e u404.bin",0xe001, 0x1000, CRC(be6492d4) SHA1(ef25faf33e8336121d030e38e177be39be8afb7a), ROM_SKIP(1))

	ROM_REGION(0x8000,"gfx1", 0)
	ROM_LOAD("eagle 1600 video char gen u301.bin", 0x00000, 0x1000, CRC(1a7e552f) SHA1(749058783eec9d96a70dc5fdbfccb56196f889dc))
ROM_END

/*************************************************************** Eagle PC-2 ***

Links: http://www.digibarn.com/collections/systems/eagle-pc/index.html , https://www.atarimagazines.com/creative/v10n2/28_Eagle_PC2.php http://www.old-computers.com/museum/computer.asp?st=1&c=529
Form Factor: Desktop

Error message: Cannot read boot sector

******************************************************************************/

ROM_START( eaglepc2 )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD("eagle_pc-2_bios_2.812_1986_u1101.bin", 0xe000, 0x2000, CRC(cd0fc034) SHA1(883cb4808c565f2582873a51cc637ab25b457f88))

	ROM_REGION(0x8000,"gfx1", 0)
	ROM_LOAD("eagle_pc-2_cga_char_rom_u401.bin", 0x00000, 0x1000, CRC(e85da08d) SHA1(176a7027bd14cc7efbb5cec5c2ac89ba002912d0))

ROM_END

/********************************************************** Eagle PC Spirit ***

Links: http://www.old-computers.com/museum/computer.asp?st=1&c=530 , https://archive.org/details/eagle_pc_spirit_users_guide_nov83
Form Factor: Luggable
CPU: 8088 @ 4.77 MHz
RAM: 128K, up to 640K
Video: CGA
Mass storage: 1/2x 5.25" 360KB floppy or 1x 360KB floppy and 10MB harddisk (XL model)

Pressing "T" after a hard reset brings up a ROM based test suite.

DIP switches:
SW801: Sw.1  Sw.2  Sw.3  Sw.4 Max.RAM     J13  Sw.5  Sw.6  Floppy  Sw.7  Sw.8  Display@
                              on mainbd.                   drives              powerup
         ON    ON    ON    ON    64K      OUT
        OFF    ON    ON    ON   128K      OUT
         ON   OFF    ON    ON   192K      OUT
        OFF   OFF    ON    ON   256K      OUT
        OFF    ON   OFF    ON   384K       IN
        OFF    ON    ON   OFF   640K       IN
                                                  ON    ON    1
                                                  ON   OFF    2
                                                 OFF    ON    3
                                                 OFF   OFF    4
                                                                      ON    ON    No Display
                                                                      ON   OFF    Color 40x35
                                                                     OFF    ON    Color 80x25
                                                                      OFF   OFF   Monochrome

******************************************************************************/

ROM_START( eaglespirit )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD("u1101.bin", 0xe000, 0x2000, CRC(3fef0b0b) SHA1(fa75e90c5595b72ef33d178f1f86511cbe08191d))
	ROM_LOAD("u1103.bin", 0xc000, 0x2000, CRC(efa2b0d9) SHA1(1fcd01dd2676539a0f6498ef866fb450caab1ac4))
ROM_END


/****************************************************** Elektronika MC-1702 ***

This is actually the PC compatibility board for the Soviet MC-0585 computer, a DEC Professional 350 clone.
An alternative ROM set shares the same corrupted pixels and some other changed locations in comparison with this dump.

******************************************************************************/

ROM_START( mc1702 )
	ROM_REGION16_LE(0x10000,"bios", 0)
	ROM_LOAD16_BYTE("2764_2,573rf4.rom", 0xc000,  0x2000, BAD_DUMP CRC(34a0c8fb) SHA1(88dc247f2e417c2848a2fd3e9b52258ad22a2c07))
	ROM_LOAD16_BYTE("2764_3,573rf4.rom", 0xc001, 0x2000, BAD_DUMP CRC(68ab212b) SHA1(f3313f77392877d28ce290ffa3432f0a32fc4619))
	ROM_LOAD("ba1m,573rf5.rom", 0x0000, 0x0800, CRC(08d938e8) SHA1(957b6c691dbef75c1c735e8e4e81669d056971e4))
ROM_END


/************************************************ Ericsson Portable PC - EPPC ***

Links: https://youtu.be/Qmke4L4Jls8 , https://youtu.be/yXK01gBQE6Q
Form Factor: Laptop
CPU: 8088 @ 4.77MHz
RAM: 256K
Bus: No internal slots
Video: Monochrome 80x25 character mode. 320x200 and 640x400 (CGA?) grahics modes
Display: Orange Gas Plasma (GP) display
Mass storage: half height 5.25" 360K
On board ports: Beeper,
Ports: serial, parallel, ext. floppy
Internal Options: 256K RAM, thermal printer
External Options: A disk cabinet with networking, 1200/300 accoustic modem, 256K Ergo disk electronic disk drive
Misc: No battery due to the power hungry GP display. 10-15.000 units sold

******************************************************************************/

ROM_START( eppc )
	ROM_REGION(0x10000,"bios", 0)
	ROM_LOAD( "eppcbios60605.bin",  0xc000, 0x04000, CRC(fe82e11b) SHA1(97ed48dc30f1ed0acce0a14b8085f13b84d4444b))
ROM_END


/***************************************************************** ITT XTRA ***

Links:  https://www.atarimagazines.com/creative/v10n12/71_ITT_Xtra_an_IBM_PC_compa.php
Info:   Model I: 128K RAM, 14" mono (green or amber) or 12" colour screen; Model II adds another floppy drive;
        Model III: 256K RAM, 1 floppy, 10MB harddisk
Form Factor: Desktop
CPU: 8088
RAM: 128K or 256K on board, expandable to 512K
Bus: 5xISA
Mass storage: 1/2x 5.25" floppy drives
Options: 8087 FPU, 10MB harddisk, combo board: "The optional Combo board plugs into one of the 62-pin motherboard expansion slots
    and provides an additional 128KB of memory, a battery backup real-time clock, and an 8-bit general purpose port which can be
    used for a parallel printer. Two "baby" add-on memory cards of 128KB each can also be plugged into the Combo board raising the
    available memory on the board to 384K.
On board connectors: Floppy, keyboard, serial, parallel
ToDo: find dump of original graphics card ROM

DIP switches:
SW1: 1   2   3   4   5   6   7   8   effect
        OFF                          FPU installed
         ON                          no FPU
            OFF  ON                  128K mainboard memory
             ON OFF                  192K
            OFF OFF                  256K
                     ON  ON          80x25 color monitor
                    OFF  ON          40x25 color monitor
                     ON OFF          color monitor in the 80x25 mode
                    OFF OFF          monochrome or both mono and color monitors
                              ON  ON 1 floppy drive
                             OFF  ON 2
                              ON OFF 3
                             OFF OFF 4

SW2: 1   2   3   4   5   6   7   8   effect
                     ON  ON          Screen Time out ON
                    OFF  ON          Screen Time out OFF
                            OFF      Power up Self Test ON
                             ON      Power up Self Test OFF
                                 OFF Normal Operation
                                  ON Factory Testing

The ROM contains a monitor program that can be activated by pressing "ESC" at the "Insert Diskette"  prompt
or by pressing [Ctrl]-[Alt]-[Esc]

******************************************************************************/

ROM_START( ittxtra )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD("bios_itt_xtra_5c00_v200_u92.bin", 0xc000, 0x4000, CRC(77056e54) SHA1(6a2d28340cb6be09c9f59bf6971e5d7fa693e66b))
	ROM_LOAD("bios_itt_xtra_bf00_v200_u93.bin", 0x8000, 0x4000, CRC(c5191343) SHA1(01f9feaf2adf118703479ead224271da55373a62))
ROM_END

/**************************************************************** Kaypro 16 ***

Links:  http://www.mofeel.net/679-comp-sys-ibm-pc-classic/309.aspx, https://groups.google.com/forum/#!topic/comp.os.cpm/HYQnpUOyQXg,
        https://amaus.org/static/S100/kaypro/systems/kaypro%2016/Kaypro%2016.pdf , http://ajordan.dpease.com/kaypro16/index.htm
Form Factor: Luggable
CPU: 8088 @ 4.77MHz
RAM: 256K, expandable to 512K and 640K
Mainboard with 4 ISA slots, video decoder circuitry to show 16 levels of grayscale on the internal monitor, interface to WD1002-HD0 harddisk controller
Bus: 4x ISA:    1) 8088 slot CPU, keyboard connector, reset switch,
                2) Floppy disk controller, serial, parallel, RAM expansion
                3) Kaypro CGA card with composite and colour TTL outputs, ROM 81-820 needs to be dumped
                4) empty
Video: CGA
Mass storage: 1x 5.25" 360K, 10MB harddisk (Seagate ST212)
Options: 8087 FPU
Misc: A Kaypro 16/2 is a configuration without harddisk but with two floppy disk drives (interface ics on mainboard were not populated)

DIP switches:
SW1 on the PROCESSOR CARD: Position 1 is used to specify the numeric processor option. Positions 2 and 3 are used to specify the size and type
of display interface. Positions 4 and 5 are used to specify the number of disk drives.
(1: on, 2: off, 3: on, 4: on, 5: on); Kapro 16/2: (1: on, 2: off, 3: on, 4: on, 5: off)
SW1 on the FLOPPY-RAM-I/O CARD: Positions 1,2,3 and 4 are used to specify the starting address for the RAM on the FLOPPY-RAM-I/O card (the
memory expansion). Positions 5 and 6 indicate the number of RAM banks on the FLOPPY-RAM-I/O card. Position 7 is used to specify whether those
banks contain 64K or 256K. Position 8 is used to enable or disable parity checking.
(1: off, 2: on, 3: on, 5: on, 5: on, 6: off, 7: on).
SW2 on the FLOPPY-RAM-I/O card: Positions 1 and 2 are used to select the serial port. Positions 3 and 4 are used to select the parallel port.
(1: on, 2: on, 3: on, 4: off).

******************************************************************************/

void pc_state::kaypro16(machine_config &config)
{
	pccga(config);
	subdevice<isa8_slot_device>("isa1")->set_fixed(true);
	subdevice<isa8_slot_device>("isa2")->set_fixed(true);
	subdevice<isa8_slot_device>("isa3")->set_fixed(true);
	subdevice<isa8_slot_device>("isa4")->set_fixed(true);
	subdevice<isa8_slot_device>("isa5")->set_default_option(nullptr);
	subdevice<ram_device>(RAM_TAG)->set_default_size("256K").set_extra_options("512K, 640K");
}

ROM_START( kaypro16 )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD("pc102782.bin", 0xe000, 0x2000, CRC(ade4ed14) SHA1(de6d87ae83a71728d60df6a5964e680487ea8400))
ROM_END

/**************************************************************** Kaypro PC ***

Links:  https://www.youtube.com/watch?v=2YAEOhYEZbc ,

DIP switches: 2 blocks of 8 switches on the FLOPPY-RAM-I/O board, 1 block of 5 switches on the CPU board

******************************************************************************/

ROM_START( kaypropc )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD("kpb203n.rom", 0xe000, 0x2000, CRC(49ea41e9) SHA1(14db6b8f302833f64f6e740a293d12f76e71f78f))
ROM_END

/******************************************************************** MK-88 ***

******************************************************************************/
// MK-88
void pc_state::mk88(machine_config &config)
{
	poisk2(config);
	subdevice<isa8_slot_device>("isa1")->set_default_option("cga_ec1841");
}

// MK-88
ROM_START( mk88 )
	ROM_REGION16_LE(0x10000,"bios", 0)
	ROM_DEFAULT_BIOS("v392")
	ROM_SYSTEM_BIOS(0, "v290", "v2.90")
	ROMX_LOAD("mk88m.bin", 0xc000, 0x2000, CRC(09c9da3b) SHA1(d1e7ad23b5f5b3576ad128c1198294129754f39f), ROM_BIOS(0))
	ROMX_LOAD("mk88b.bin", 0xe000, 0x2000, CRC(8a922476) SHA1(c19c3644ab92fd12e13f32b410cd26e3c844a03b), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v391", "v3.91")
	ROMX_LOAD("mkm.bin", 0xc000, 0x2000, CRC(65f979e8) SHA1(13e85be9bc8ceb5ab9e559e7d0089e26fbbb84fc), ROM_BIOS(1))
	ROMX_LOAD("mkb.bin", 0xe000, 0x2000, CRC(830a0447) SHA1(11bc200fdbcfbbe335f4c282020750c0b5ca4167), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v392", "v3.92")
	ROMX_LOAD("m88.bin", 0xc000, 0x2000, CRC(fe1b4e36) SHA1(fcb420af0ff09a7d43fcb9b7d0b0233a2071c159), ROM_BIOS(2))
	ROMX_LOAD("b88.bin", 0xe000, 0x2000, CRC(58a418df) SHA1(216398d4e4302ee7efcc2c8f9ff9d8a1161229ea), ROM_BIOS(2))
ROM_END


/***************************************************************** NCR PC4i ***

Links: http://www.minuszerodegrees.net/manuals/NCR/NCR%20PC4i%20-%20Technical%20Reference%20Manual%20-%20January%201986.pdf
Info:   The earlier PC4 is not quite IBM compatible, the "i" in PC4i indicates full IBM compatibility.
        The NCR Graphics card supports a special 640x400 video mode
Form Factor: All-in-one desktop
CPU: 8088 @ 4.77 MHz
RAM: 256K, expandable to 640K
Bus: 7x ISA:    1)  (optional) RAM expansion board
                2)  empty
                3)  32K Video/Graphics board (64K option)
                4)  (optional) Alpha board
                5)  empty
                6)  (optional) MFM harddisk controller
                7)  empty
Video: K510: 4KB Alpha for internal monitor; K511: 32KB Graphics for internal monitor; K512: 32KB upgrade for K512; K140: 16KB Graphics for external monitor; K141: 4KB Alpha for external monitor
Display: Mono or color CRT 640x400 pixel
Mass storage: 1x 5.25" 360K floppy and 1x 5.25" 360K floppy or 10 MB harddisk
On board ports: parallel, serial, speaker, floppy
Options: 8087 FPU, K101 memory upgrade in 64K steps, 1.2MB floppy and controller board

Regular motherboard, an alternate board using more integrated components exists.
Jumpers: JP1 closed: enable flex. disk drives, JP2 closed: enable standad serial I/O
JP3 closed: enable parallel interface OR just JP5: closed, enable standard serial I/O

DIP settings:  Sw.1  Sw.2  Sw.3  Sw.4  Sw.5  Sw.6  Sw.7  Sw.8  effect
                OFF                                            normal operation
                       ON                                      FPU not installed
                             OFF  OFF                          256KB RAM
                                        OFF   OFF              Alpha Controller
                                        OFF    ON              40x25 Graphics Controller
                                         ON   OFF              80x25 Graphics Controller
                                                     ON    ON  1 Flexible Disk Drive
                                                    OFF    ON  2 Flexible Disk Drives

******************************************************************************/

void pc_state::ncrpc4i(machine_config & config)
{
	pccga(config);
	ISA8_SLOT(config, "isa6", 0, "mb:isa", pc_isa8_cards, nullptr, false); // FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa7", 0, "mb:isa", pc_isa8_cards, nullptr, false);

	subdevice<ram_device>(RAM_TAG)->set_default_size("640K").set_extra_options("64K, 128K, 256K, 512K");
}

ROM_START( ncrpc4i )
	ROM_REGION(0x10000,"bios", 0)
	ROM_SYSTEM_BIOS(0, "v22", "V2.2") // this machine came with a "Intersil Display Adapter Color III", probably aftermarket, there's no card BIOS, just a chargen ROM
	ROMX_LOAD("ncr_pc4i_43928.bin",0xc000, 0x4000, CRC(e66a46b9) SHA1(f74f8f9226325d2a8b927de3847449db4c907b1d), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v23", "2.3") // this machine came with a NCR graphics card with a card BIOS and a chargen ROM
	ROMX_LOAD("ncr_pc4i_biosrom_1985.bin",0xc000, 0x4000, CRC(b9732648) SHA1(0d5d96fbc36089ca4d893b0db84faffa8043a5e4), ROM_BIOS(1))
ROM_END


/****************************************************************** NCR PC6 ***

Links:  https://www.1000bit.it/ad/bro/ncr/ncr-pc6.pdf
Info:   ID-Nr. 3285-1011 (256KB RAM, 5.25 360KB flex drive), 3285-1012 (256KB RAM, 2x360KB flex drives), 3285-1014 (512KB RAM, 360KB flex and 20MB fixed drive),
        3285-1015 (same as 1014, but adding a 10MB tape streamer); the motherboard is said to be the VLSI version of the PC4i mentioned there.
Form factor: Desktop
CPU: 8088-2 @ 4.77 MHz or 8 MHz
RAM: 256K / 512K, up to 640K on board, four banks
Bus: 8xISA8
On board ports: 2xserial, parallel, floppy, speaker
Video: Extended CGA, Hercules, EGA (mono or color)

DIP settings:
SW1:    SW1/1   SW1/2   SW1/3   SW1/4   SW1/5   SW1/6   SW1/7   SW1/8
        N/A
                ON                                                      FPU not installed
                        N/A     N/A
                                        OFF     OFF                     monochrome display
                                        OFF     ON                      color/graphics 40x25
                                        ON      OFF                     color/graphics 80x25
                                        ON      ON                      no display
                                                        ON      ON      1 flexible disk drive
                                                        OFF     ON      2 flexible disk drives
                                                        ON      OFF     3 flexible disk drives
                                                        OFF     OFF     4 flexible disk drives

SWA:    SWA/1   SWA/2   SWA/3   SWA/4   SWA/5   SWA/6   SWA/7   SWA/8
        OFF     OFF                                                     256K RAM, 4x64K banks
        OFF     ON                                                      256K in bank 0
        ON      OFF                                                     640K (64K-64K-256K-256K) or 448K (64K-64K-256K-64K)
        ON      ON                                                      640K (256K-256K-64K-64K) or 576K (256K-256K-64K) or 512K (256K-256K-0K-0K)
                        ON                                              serial ports enabled
                                ON                                      parallel port enabled
                                        ON                              XP installed (turbo switch to 8 MHz)
                                                N/A     N/A     N/A



******************************************************************************/

ROM_START( ncrpc6 )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD( "ncr_pc6_bios_27128a@dip28_01_v3.5.bin", 0xc000, 0x4000, CRC(602e756a) SHA1 (890c19f5007b53701ebe32d074c8ba60a1b2e1d2))
ROM_END


/************************************************************* Olivetti M15 ***

Links:  http://www.1000bit.it/ad/bro/olivetti/olivettiM15.pdf , http://electrickery.xs4all.nl/comp/m15/ ,
        http://electrickery.xs4all.nl/comp/m15/doc/M15_InstallationAndOperationsGuide.pdf
        http://www.museotecnologicamente.it/olivetti-m-15-1987/ , http://www.museotecnologicamente.it/wp-content/uploads/M15_Depliant_inglese.pdf
Info: The info brochure has a picture of a working M15. This shows the LCD display with a green background and blue text/graphics.
Form Factor: Laptop
CPU: 80C88 @ 4.77 MHz
RAM: 256K / 512K
Bus: no internal slots
Video: 80x25 text mode, CGA
Display: LCD
Mass storage: 2x 3.5" 720K drives
Ports: serial, parallel, ext. floppy, RTC
Expansion: External 5.25" 360K floppy drive

******************************************************************************/

static DEVICE_INPUT_DEFAULTS_START( m15 )
	DEVICE_INPUT_DEFAULTS("DSW0", 0x30, 0x20) // TODO: document correct dip settings
	DEVICE_INPUT_DEFAULTS("DSW0", 0x01, 0x00)
DEVICE_INPUT_DEFAULTS_END

void pc_state::m15(machine_config &config)
{
	pccga(config);
	subdevice<ibm5160_mb_device>("mb")->set_input_default(DEVICE_INPUT_DEFAULTS_NAME(m15));
	subdevice<isa8_slot_device>("isa2")->set_option_machine_config("fdc_xt", cfg_dual_720K);

	subdevice<ram_device>(RAM_TAG)->set_default_size("448K").set_extra_options("16K, 160K, 304K");
}

ROM_START( olivm15 )
	ROM_REGION(0x10000,"bios", 0)
	ROM_LOAD("oliv_m15.bin",0xc000, 0x04000, CRC(bf2ef795) SHA1(02d497131f5ca2c78f2accd38ab0eab6813e3ebf))
ROM_END


/*************************************************** AEG Olympia Olytext 30 ***

Form Factor: Desktop
CPU: NEC V20 @ 4.77MHz
RAM: 768K, not sure how to address the area above 640K
Bus: 8x ISA:    1) NEC V20 Slot CPU with 786K RAM, TI TACT80181FT chip
                2) Z180 CP/M emulation card, needed to run the proprietary Olytext 30 word processor)
                3) Monochrome graphics/color graphics card (possibly EGA capable) ICs: Chips P82C441 and P82A442A
                4) MFM hard disk controller HDC-770, ICs: HDC9224, HDC92C26, HDC9223,
                5) Floppy, serial and RTC DIO-770, ICs: 2x UM8250B, UM8272A, OKI M5832
Video: MDA/Hercules/CGA, possibly EGA
Mass storage: 1x 3.5" 720K, 20MB Miniscribe harddisk
On board ports: speaker
Options: 8087 FPU

DIP switches: block with six switches on the CPU board
******************************************************************************/

void pc_state::olytext30(machine_config &config)
{
	pccga(config);

	v20_device &maincpu(V20(config.replace(), "maincpu", XTAL(25'000'000)/3)); /* 8.33 MHz */ // determine divider, it's a 25MHz crystal and a 10MHz V20
	maincpu.set_addrmap(AS_PROGRAM, &pc_state::pc8_map);
	maincpu.set_addrmap(AS_IO, &pc_state::pc8_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	subdevice<isa8_slot_device>("isa2")->set_option_machine_config("fdc_xt", cfg_single_720K);
	subdevice<isa8_slot_device>("isa3")->set_default_option(nullptr);
	subdevice<isa8_slot_device>("isa5")->set_default_option("hdc");
	subdevice<ram_device>(RAM_TAG)->set_default_size("768K");
}

ROM_START( olytext30 )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD("o45995.bin", 0xe000, 0x2000, CRC(fdc05b4f) SHA1(abb94e75e7394be1e85ff706d4d8f3b9cdfea09f))
ROM_END


/****************************************************************** Poisk-2 ***

******************************************************************************/

void pc_state::poisk2(machine_config &config)
{
	/* basic machine hardware */
	i8086_cpu_device &maincpu(I8086(config, "maincpu", 4772720));
	maincpu.set_addrmap(AS_PROGRAM, &pc_state::pc16_map);
	maincpu.set_addrmap(AS_IO, &pc_state::pc16_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	ibm5160_mb_device &mb(IBM5160_MOTHERBOARD(config, "mb"));
	mb.set_cputag(m_maincpu);
	mb.int_callback().set_inputline(m_maincpu, 0);
	mb.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	mb.kbdclk_callback().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	mb.kbddata_callback().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));
	mb.set_input_default(DEVICE_INPUT_DEFAULTS_NAME(pccga));

	ISA8_SLOT(config, "isa1", 0, "mb:isa", pc_isa8_cards, "cga_poisk2", false); // FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa2", 0, "mb:isa", pc_isa8_cards, "fdc_xt", false);
	ISA8_SLOT(config, "isa3", 0, "mb:isa", pc_isa8_cards, "lpt", false);
	ISA8_SLOT(config, "isa4", 0, "mb:isa", pc_isa8_cards, "com", false);

	/* keyboard */
	pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_IBM_PC_XT_83));
	kbd.out_clock_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_clock_w));
	kbd.out_data_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_data_w));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("640K").set_extra_options("64K, 128K, 256K, 512K");
}

ROM_START( poisk2 )
	ROM_REGION16_LE(0x10000,"bios", 0)
	ROM_SYSTEM_BIOS(0, "v20", "v2.0")
	ROMX_LOAD("b_p2_20h.rf4", 0xc001, 0x2000, CRC(d53189b7) SHA1(ace40f1a40642b51fe5d2874acef81e48768b23b), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("b_p2_20l.rf4", 0xc000, 0x2000, CRC(2d61fcc9) SHA1(11873c8741ba37d6c2fe1f482296aece514b7618), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v21", "v2.1")
	ROMX_LOAD("b_p2_21h.rf4", 0xc001, 0x2000, CRC(22197297) SHA1(506c7e63027f734d62ef537f484024548546011f), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("b_p2_21l.rf4", 0xc000, 0x2000, CRC(0eb2ea7f) SHA1(67bb5fec53ebfa2a5cad2a3d3d595678d6023024), ROM_SKIP(1) | ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v24", "v2.4")
	ROMX_LOAD("b_p2_24h.rf4", 0xc001, 0x2000, CRC(ea842c9e) SHA1(dcdbf27374149dae0ef76d410cc6c615d9b99372), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD("b_p2_24l.rf4", 0xc000, 0x2000, CRC(02f21250) SHA1(f0b133fb4470bddf2f7bf59688cf68198ed8ce55), ROM_SKIP(1) | ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v21d", "v2.1d")
	ROMX_LOAD("opp2_1h.rf4", 0xc001, 0x2000, CRC(b7cd7f4f) SHA1(ac473822fb44d7b898d628732cf0a27fcb4d26d6), ROM_SKIP(1) | ROM_BIOS(3))
	ROMX_LOAD("opp2_1l.rf4", 0xc000, 0x2000, CRC(1971dca3) SHA1(ecd61cc7952af834d8abc11db372c3e70775489d), ROM_SKIP(1) | ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "v22d", "v2.2d")
	ROMX_LOAD("opp2_2h.rf4", 0xc001, 0x2000, CRC(b9e3a5cc) SHA1(0a28afbff612471ee81d69a98789e75253c57a30), ROM_SKIP(1) | ROM_BIOS(4))
	ROMX_LOAD("opp2_2l.rf4", 0xc000, 0x2000, CRC(6877aad6) SHA1(1d0031d044beb4f9f321e3c8fdedf57467958900), ROM_SKIP(1) | ROM_BIOS(4))
	ROM_SYSTEM_BIOS(5, "v23d", "v2.3d")
	ROMX_LOAD("opp2_3h.rf4", 0xc001, 0x2000, CRC(ac7d4f06) SHA1(858d6e084a38814280b3e29fb54971f4f532e484), ROM_SKIP(1) | ROM_BIOS(5))
	ROMX_LOAD("opp2_3l.rf4", 0xc000, 0x2000, CRC(3c877ea1) SHA1(0753168659653538311c0ad1df851cbbdba426f4), ROM_SKIP(1) | ROM_BIOS(5))
ROM_END


/****************************************************** Samsung Samtron 88S ***

******************************************************************************/
ROM_START( ssam88s )
	ROM_REGION(0x10000,"bios", 0)
	ROM_LOAD("samsung_samtron_88s_vers_2.0a.bin",  0x8000, 0x08000, CRC(d1252a91) SHA1(469d15b6ecd7b70234975dc12c6bda4212a66652))
ROM_END


/************************************************************* Sanyo MBC-16 ***

Links:
Info: In the MBC-16 I had, the graphics card had a Sanyo sticker on it, so I assume that was the original graphics card for the machine.
Form Factor: Desktop
CPU: 8088 @ 8MHz
RAM: 640KB
Bus: 3x ISA:    1)  ATI Graphics Solution SR https://sites.google.com/site/atiwonderseriesdatabase/
Video: MDA/CGA/Plantronics
Mass storage: 1 or 2 5.25" 360K floppies, MFM harddisk on hardcard or via separate controller
On board ports: serial, parallel, floppy

******************************************************************************/

ROM_START( mbc16 )
	ROM_REGION(0x10000,"bios", 0)
	ROM_LOAD("mbc16.bin", 0xc000, 0x4000, CRC(f3e0934a) SHA1(e4b91c3d395be0414e20f23ad4919b8ac52639b2))
	ROM_REGION(0x2000,"gfx1", 0)
	//ATI Graphics Solution SR (graphics card, need to make it ISA card)
	ROM_LOAD("atigssr.bin", 0x0000, 0x2000, CRC(aca81498) SHA1(0d84c89487ee7a6ac4c9e73fdb30c5fd8aa595f8))
ROM_END

/************************************************************ Sanyo SX-16 **

******************************************************************************/

ROM_START( sx16 )
	ROM_REGION(0x10000,"bios", 0)
	ROM_LOAD("tmm27128ad.bin",0xc000, 0x4000, CRC(f8543362) SHA1(fef625e260ca89ba02174584bdc12db609f0780e))
ROM_END


/***************************************************** Schetmash Iskra-3104 ***

******************************************************************************/

static DEVICE_INPUT_DEFAULTS_START( iskr3104 )
	DEVICE_INPUT_DEFAULTS("DSW0", 0x30, 0x00)
DEVICE_INPUT_DEFAULTS_END

void pc_state::iskr3104(machine_config &config)
{
	/* basic machine hardware */
	i8086_cpu_device &maincpu(I8086(config, "maincpu", 4772720));
	maincpu.set_addrmap(AS_PROGRAM, &pc_state::pc16_map);
	maincpu.set_addrmap(AS_IO, &pc_state::pc16_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	ibm5160_mb_device &mb(IBM5160_MOTHERBOARD(config, "mb"));
	mb.set_cputag(m_maincpu);
	mb.int_callback().set_inputline(m_maincpu, 0);
	mb.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	mb.kbdclk_callback().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	mb.kbddata_callback().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));
	mb.set_input_default(DEVICE_INPUT_DEFAULTS_NAME(iskr3104));

	ISA8_SLOT(config, "isa1", 0, "mb:isa", pc_isa8_cards, "ega", false).set_option_default_bios("ega", "iskr3104"); // FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa2", 0, "mb:isa", pc_isa8_cards, "fdc_xt", false);
	ISA8_SLOT(config, "isa3", 0, "mb:isa", pc_isa8_cards, "lpt", false);
	ISA8_SLOT(config, "isa4", 0, "mb:isa", pc_isa8_cards, "com", false);

	/* keyboard */
	pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_IBM_PC_XT_83));
	kbd.out_clock_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_clock_w));
	kbd.out_data_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_data_w));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("640K").set_extra_options("64K, 128K, 256K, 512K");
}

ROM_START( iskr3104 )
	ROM_REGION16_LE(0x10000,"bios", 0)
	ROMX_LOAD( "198.bin", 0xc000, 0x2000, CRC(bcfd8e41) SHA1(e21ddf78839aa51fa5feb23f511ff5e2da31b433),ROM_SKIP(1))
	ROMX_LOAD( "199.bin", 0xc001, 0x2000, CRC(2da5fe79) SHA1(14d5dccc141a0b3367f7f8a7188306fdf03c2b6c),ROM_SKIP(1))
	// EGA card from Iskra-3104
	//ROMX_LOAD( "143-03.bin", 0xc0001, 0x2000, CRC(d0706345) SHA1(e04bb40d944426a4ae2e3a614d3f4953d7132ede),ROM_SKIP(1))
	//ROMX_LOAD( "143-02.bin", 0xc0000, 0x2000, CRC(c8c18ebb) SHA1(fd6dac76d43ab8b582e70f1d5cc931d679036fb9),ROM_SKIP(1))
ROM_END


/************************************************************ Sharp PC-7000 ***

Links:  http://oldcomputers.net/sharp-pc7000.html , http://curtamania.com/curta/database/brand/sharp/Sharp%20PC-7000/index.html ,
        http://pcmuseum.de/pc7000.html
Form Factor: Luggable
CPU: 8086 @ 4.77 MHz or 7.37 MHz
RAM: 320K / 704K
Bus: no internal slots
Video: 80x24 text, 600x200 pixel graphics
Display: electroluminescent mono backlit (blue) LCD
Mass storage: 2x 5.25" 360K floppies
On board ports: serial, parallel
Options: Modem, color video output

******************************************************************************/

ROM_START( pc7000 )
	ROM_REGION16_LE(0x10000,"bios", 0)
	ROMX_LOAD("mitsubishi-m5l27128k-1.bin", 0x8000, 0x4000, CRC(9683957f) SHA1(4569eab6d88eb1bba0d553d1358e593c326978aa), ROM_SKIP(1))
	ROMX_LOAD("mitsubishi-m5l27128k-2.bin", 0x8001, 0x4000, CRC(99b229a4) SHA1(5800c8bafed26873d8cfcc79a05f93a780a31c91), ROM_SKIP(1))
ROM_END


/*************************************************** Siemens Sicomp PC16-05 ***

Links:  http://www.computerwoche.de/a/siemens-erweitert-pc-16-programm,1169752 ,
        http://www.phantom.sannata.ru/museum/siemens_pc_16_05.shtml
Info: Multitech PC/700 mainboard
Form Factor: Desktop
CPU: 8088 @ 4.77MHz / 8 MHz
RAM: 640KB
Bus: 6x ISA:    1) MDA/Hercules/CGA and parallel port
                2) Floppy, RTC and serial port
                3) (optional) MFM harddisk controller
Video: MDA/Hercules, exchangeable via ISA-slot
Mass storage: 1x 5.25" 360K floppy and 1x 5.25" 360K floppy or MFM hard drive (10MB or 20MB)
On board ports: parallel, serial, beeper
Options: 8087 FPU
OSC: 24MHz, 1843.200KHz

Two blocks of dip switches, 8 switches each
The same BIOS version is found in a Multitech Popular 500 PC

******************************************************************************/

static DEVICE_INPUT_DEFAULTS_START( siemens )
	DEVICE_INPUT_DEFAULTS("DSW0", 0x30, 0x30)
DEVICE_INPUT_DEFAULTS_END

void pc_state::siemens(machine_config &config)
{
	/* basic machine hardware */
	i8088_cpu_device &maincpu(I8088(config, "maincpu", XTAL(24'000'000)/3)); /* 8.00 MHz */ // Turbo, can be changed to 4.77MHz
	maincpu.set_addrmap(AS_PROGRAM, &pc_state::pc8_map);
	maincpu.set_addrmap(AS_IO, &pc_state::pc8_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	ibm5150_mb_device &mb(IBM5150_MOTHERBOARD(config, "mb"));
	mb.set_cputag(m_maincpu);
	mb.int_callback().set_inputline(m_maincpu, 0);
	mb.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	mb.kbdclk_callback().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	mb.kbddata_callback().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));
	mb.set_input_default(DEVICE_INPUT_DEFAULTS_NAME(siemens));

	// FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa1", 0, "mb:isa", pc_isa8_cards, "hercules", false);
	ISA8_SLOT(config, "isa2", 0, "mb:isa", pc_isa8_cards, "fdc_xt", false);
	ISA8_SLOT(config, "isa3", 0, "mb:isa", pc_isa8_cards, "lpt", false);
	ISA8_SLOT(config, "isa4", 0, "mb:isa", pc_isa8_cards, "com", false);
	ISA8_SLOT(config, "isa5", 0, "mb:isa", pc_isa8_cards, "hdc", false);
	ISA8_SLOT(config, "isa6", 0, "mb:isa", pc_isa8_cards, nullptr, false);

	/* keyboard */
	pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_IBM_PC_XT_83));
	kbd.out_clock_cb().set("mb", FUNC(ibm5150_mb_device::keyboard_clock_w));
	kbd.out_data_cb().set("mb", FUNC(ibm5150_mb_device::keyboard_data_w));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("640K").set_extra_options("64K, 128K, 256K, 512K");
}

ROM_START( sicpc1605 )
	ROM_REGION(0x10000,"bios", 0)
	ROM_LOAD("multitech pc-700 3.1.bin", 0xe000, 0x2000, CRC(0ac7a2e1) SHA1(b9c8504e21213d81a068dde9f51f9c973d726e7b))

	// ROM for INS8039N-11 keyboard MCU in Siemens KB-097B/SI keyboard
	ROM_REGION(0x8000,"kbd", 0)
	ROM_LOAD("kb097b-st_v1.0.bin", 0x0000, 0x2000, CRC(5fc5903f) SHA1(5fc14d12800e22bb354e4b329c6ffc25efa0397c))
ROM_END


/***************************************************** VTech Laser Turbo XT ***

Links: http://minuszerodegrees.net/manuals.htm#VTech , http://minuszerodegrees.net/manuals/VTech/VTech%20-%20Laser%20Turbo%20XT%20-%20Brochure.pdf
Form Factor: Desktop
CPU: 8088 @ 4.77 MHz or 10 MHz
RAM: 512K / 640K, additionally 512K or 1M EMS on board
Bus: 8xISA:     1) Monochrome graphics/color graphics card
                2) Multi I/O Card (Floppy, 2x serial, parallel, game, RTC)
                3) (optional) hard disk controller
Video: MDA/CGA/Hercules
Mass storage: 2x 5.25" 360K floppies and 1 or 2 harddisks (20MB / 30MB / 40MB)
On board ports: speaker
Options: 8087 FPU

DIP settings:
SW1: 1    2    3    4    5    6    7    8    effect
     OFF                                     normal operation
      ON                                     Factory Testing only
           ON                                FPU absent
          OFF                                FPU present
               OFF  ON                       512K conventional memory
               OFF OFF                       640K conventional memory
                          ON   ON            normally on
                                    ON   ON  1 floppy drive
                                   OFF   ON  2
                                    ON  OFF  3
                                   OFF  OFF  4

Switch SW2 is used for the starting address for the expanded memory in the Turbo XT. If you have up to
one megabyte of expanded memory, the settings are easy: 512K: 01111111 1MB 01110111
Expanded memory is broken into two bundles as you install it. For example, with 1MB you have 512K in both
bundle 1 and bundle 2.
The positions of switches 1, 2 and 3 determine the address of the first bundle of expanded memory. Likewise,
switches 4, 5 and 6 determine the second bundle's address. Each 512K must have a unique starting address

SW2: 1/4  2/5  3/6  effect
     OFF   ON   ON  208h I/O port expanded memory
      ON  OFF   ON  218h
     OFF  OFF   ON  258h
      ON   ON  OFF  268h
     OFF   ON  OFF  2A8h
      ON  OFF  OFF  2B8h
     OFF  OFF  OFF  2E8h
      ON   ON   ON  bundle disabled

At 4.77MHz, memory accesses take four clock cycles (840ns), while I/O accesses take five clock
cycles (1050ns). At 10MHz, the internal RAM accesses take four cycles (400ns) while all other
memory accesses take 5 cycles (500ns). I/O accesses still take 5 cycles. However, the clock is
slowed down to 4.77MHz for all I/O accesses. The same is true for DMA cycles. This ensures the
Turbo XT is compatible with most expansion cards even when running at 10MHz.

******************************************************************************/

ROM_START( laser_turbo_xt )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD("laser_turbo_xt.bin", 0x0e000, 0x02000, CRC(0a6121d3) SHA1(59b1f8dd6fe981ef9a7700adebf6e1adda7cee90)) // version 1.11 - 27c64d
ROM_END


/********************************************************* VTech Laser XT/3 ***

Links: http://minuszerodegrees.net/manuals.htm#VTech , http://th99.classic-computing.de/src/v/U-Z/52547.htm
Form Factor: Desktop
CPU: 8088 @ 4.77MHz or 10 MHz
RAM: 512K / 640K, additionally 512K or 1M EMS on board
Bus: 8x ISA:    1) Monochrome graphics/color graphics card http://th99.classic-computing.de/src/v/U-Z/52547.htm , alternatively an EGA card
                2) Multi I/O Card (Floppy, 2x serial, 1x parallel, game, RTC) http://th99.classic-computing.de/src/i/U-Z/52519.htm
                3) (optional) hard disk controller
Video: MDA/Hercules/CGA
Mass storage: 2x 5.25" 360K or 1x 5.25" 360K and 1x 3.5" 720K, additional harddisk optional
On board ports: speaker
Options: 8087 FPU

DIP settings:
SW1: 1    2    3    4    5    6    7    8    effect
     OFF                                     normal operation
      ON                                     Factory Testing only
           ON                                FPU absent
          OFF                                FPU present
                ON                           512K conventional memory
               OFF                           640K conventional memory
                    --                       not used
                          ON   ON            EGA or VGA
                         OFF   ON            CGA 40x25 mode
                          ON  OFF            CGA 80x25 mode
                         OFF  OFF            MDA or Hercules
                                    ON   ON  1 floppy drive
                                   OFF   ON  2
                                    ON  OFF  3
                                   OFF  OFF  4

SW2: 1    2    3    effect
     OFF   ON   ON  208h I/O port expanded memory
      ON  OFF   ON  218h
     OFF  OFF   ON  258h
      ON   ON  OFF  268h
     OFF   ON  OFF  2A8h
      ON  OFF  OFF  2B8h
     OFF  OFF  OFF  2E8h
      ON   ON   ON  expanded memory disabled

******************************************************************************/

ROM_START( laser_xt3 )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD("laser_xt3.bin", 0x0e000, 0x02000, CRC(b45a7dd3) SHA1(62f17c408be0036d00a182e94c5c88b83d46b625)) // version 1.26 - 27c64
ROM_END


/******************************************************** Zenith SuperSport ***

Links:  http://www.focushacks.com/zenith/myzenith.html , http://retro-computing.blogspot.de/2006/08/zds-supersport-laptop.html ,
        http://www.minuszerodegrees.net/manuals/Zenith%20Data%20Systems/ZDS%20SupersPort%20-%20Service%20Manual.pdf
        http://www.minuszerodegrees.net/manuals/Zenith%20Data%20Systems/ZDS%20SupersPort%20-%20User%20and%20Technical%20Manual.pdf
Info: ZWL-184 to distinguish it from the later 80286 based models
Form Factor: Laptop
CPU: 80C88 @ 4.77 MHz or 8 MHz
RAM: 640 KB
Bus: no internal slots
Video: CGA
Display: The second link has a picture of a working SuperSport. This shows the LCD display with a green background and blue text/graphics.
Mass storage: 1x 3.5" 720K floppy and 1x720K floppy or 20MB harddisk
On board ports: serial, parallel, ext. keyboard, ext. CGA video, ext. floppy
Options: 2400 Baud Modem, 8087 FPU
******************************************************************************/

void pc_state::zenith_map(address_map &map)
{
	map.unmap_value_high();
	map(0xf0000, 0xf7fff).ram();
	map(0xf8000, 0xfffff).rom().region("bios", 0x8000);
}

void pc_state::zenith(machine_config &config)
{
	/* basic machine hardware */
	i8088_cpu_device &maincpu(I8088(config, "maincpu", XTAL(14'318'181)/3)); /* 4.77 MHz */
	maincpu.set_addrmap(AS_PROGRAM, &pc_state::zenith_map);
	maincpu.set_addrmap(AS_IO, &pc_state::pc8_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	ibm5150_mb_device &mb(IBM5150_MOTHERBOARD(config, "mb"));
	mb.set_cputag(m_maincpu);
	mb.int_callback().set_inputline(m_maincpu, 0);
	mb.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	mb.kbdclk_callback().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	mb.kbddata_callback().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));
	mb.set_input_default(DEVICE_INPUT_DEFAULTS_NAME(pccga));

	ISA8_SLOT(config, "isa1", 0, "mb:isa", pc_isa8_cards, "cga", false); // FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa2", 0, "mb:isa", pc_isa8_cards, "fdc_xt", false).set_option_machine_config("fdc_xt", cfg_dual_720K);
	ISA8_SLOT(config, "isa3", 0, "mb:isa", pc_isa8_cards, "lpt", false);
	ISA8_SLOT(config, "isa4", 0, "mb:isa", pc_isa8_cards, "com", false);

	/* keyboard */
	pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_IBM_PC_XT_83));
	kbd.out_clock_cb().set("mb", FUNC(ibm5150_mb_device::keyboard_clock_w));
	kbd.out_data_cb().set("mb", FUNC(ibm5150_mb_device::keyboard_data_w));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("640K").set_extra_options("128K, 256K, 512K");

	/* software lists */
	SOFTWARE_LIST(config, "disk_list").set_original("ibm5150");
}

ROM_START( zdsupers )
	ROM_REGION(0x10000,"bios", 0)
	ROM_SYSTEM_BIOS( 0, "v31d", "v3.1D" )
	ROMX_LOAD("z184m v3.1d.10d", 0x8000, 0x8000, CRC(44012c3b) SHA1(f2f28979798874386ca8ba3dd3ead24ae7c2aeb4), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v29e", "v2.9E" )
	ROMX_LOAD("z184m v2.9e.10d", 0x8000, 0x8000, CRC(de2f200b) SHA1(ad5ce601669a82351e412fc6c1c70c47779a1e55), ROM_BIOS(1))
ROM_END

/****************************************************** Zenith Z-150 Series ***

Form factor: Desktop
Bus: 8 slot passive backplane: 1) CPU/memory card
                               2) Floppy/Video card (color and monochrome)
CPU: Intel 8088 4.77MHz/8MHz, FPU socket present
RAM: up to 640KB
Mass storage: 2xDSDD 5.25" floppy disks / DSDD 5.25" floppy disk + winchester
on board: speaker


DIP settings:
SW202: 1    2    3    4    effect
       ON                  60Hz display frequency
      OFF                  50Hz display frequency
            ON             autoboot floppy drive
           OFF             autoboot winchester
                 ON        floppy controller not installed
                OFF        floppy controller installed
                      ON   color video adapter
                     OFF   monochrome video adapter

Pres "ESC" during powerup to enter the ROM monitor

******************************************************************************/

ROM_START( zdz150 )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD("444-260-18.bin", 0x8000, 0x4000, CRC(685208fe) SHA1(a1384627e8ecfd93842f6eabda4a417dd92be6df))
	ROM_LOAD("444-229-18.bin", 0xc000, 0x4000, CRC(a6078b8a) SHA1(9a970013f5109a5003365eb2923cc26f08516dcb))
ROM_END

/****************************************************** Zenith Z-160 Series ***

Form factor: (Trans-)Portable

******************************************************************************/

ROM_START( zdz160 )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD("f800ffff.rom", 0x8000, 0x8000, CRC(46dd9695) SHA1(beaf6b45cecdadf630a94902fa84006bf00e2b3d))
ROM_END

/****************************************************** Zenith Z-180 Series ***

Links:  https://www.1000bit.it/ad/bro/zenith/z180.pdf
Form Factor: Laptop
CPU: 80C88 @ 4.77 MHz, FPU socket present
OSC: XTAL1 24 MHz, XTAL2 14.318 MHz, XTAL3 21.47727 MHz, XTAL4 16 MHz, XTAL5 1.8432 MHz (8570), XTAL6 4 MHz (HD6305V0)
RAM: 640 KB
Bus: no internal slots
Video: CGA
Display: LCD 80 x 25 characters, 600 x 200 pixels.
Mass storage: 1x 3.5" 720K floppy and 20MB harddisk
On board ports: serial, parallel, ext. keyboard, ext. CGA video, ext. floppy
HDD: OMTI 20509B, OMTI 20513, LH5764 (not dumped)
Modem: 80C31B @ 3.5795 MHz, INS82C50

******************************************************************************/

ROM_START( zdz180 )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD("101ad_b920.rom", 0x8000, 0x8000, CRC(2f40a6b3) SHA1(ef6eb3acdf7729308a1e89574f84509929910767))
	ROM_REGION(0x4000, "hd", 0)
	ROM_LOAD("hd.rom", 0x0000, 0x4000, CRC(35b7084a) SHA1(3b3b9e414d13143e7883256d32035f36a98c95f6))
	ROM_REGION(0x2000, "modem", 0)
	ROM_LOAD("abbeaa2_45e4_15-06-88.rom", 0x0000, 0x2000, CRC(98f9652f) SHA1(91e706067574666c8698c819ead7e84e55b9ba1a))
ROM_END

/************************************************************** CompuAdd 810 **

http://mkgraham.dx.am/810.html
https://smg.photobucket.com/user/zzm113/library?page=1

CPU: NEC V20@4.77MHZ/5.15MHZ/9.54 MHz / FPU socket provided
on board: dual disk drive controller, dual IDE hard drive interface, 2xser, game
Bus: 5 ISA slots on a riser
RAM: 640KB
mass storage: 5.25" DSDD floppy drive
options: 20MB/40MB hard drive, RTC

System has an AT style enhanced keyboard, despite providing that, the emulated 810
emits a steady beep and waits for F1 to be pressed.
SW1 and SW2 DIP switch blocks

******************************************************************************/

void pc_state::cadd810(machine_config &config)
{
	pccga(config);

	auto &kbd(*subdevice<pc_kbdc_device>("kbd"));
	kbd.option_reset();
	pc_at_keyboards(kbd);
	kbd.set_default_option(STR_KBD_IBM_PC_AT_101);
}

ROM_START( cadd810 )
	ROM_REGION(0x10000,"bios", 0) // continuous beep, complains about missing keyboard
	ROM_LOAD("compuadd810.bin",0xc000, 0x4000, CRC(39dc8f28) SHA1(c0d50186db30c924fad7d42d4aefb7ae8dd32c7d))
	ROM_REGION(0x2000,"ide", 0)
	ROM_LOAD("wd_ide_bios_rev_2.0.bin",0x0000,0x2000, NO_DUMP) //missing: dump of hd controller
ROM_END

/************************************************* Juko Nest 8 bit variants ***

CPU: 8088 or NEC V20

******************************************************************************/

ROM_START( juko8 )
	ROM_REGION(0x10000, "bios", 0)
	// 0: BIOS ver 2.00 VEGAS COMPUTER COMMUNICATIONS.
	ROM_SYSTEM_BIOS(0, "nestv200", "JUKO NEST v2.00")
	ROMX_LOAD( "jukoa.bin", 0xe000, 0x2000, CRC(7d78707e) SHA1(8b09a32658a850e7f03254d1328fe6e336e91871),ROM_BIOS(0))
	// 1: Flytek (Protek) ST-12 (a 15MHz ST-15 was also available)
	ROM_SYSTEM_BIOS(1, "st-12", "ST-12")
	ROMX_LOAD( "flytek_st-12_bios_ver_2.20_c_nel_electronics_ltd.bin", 0xe000, 0x2000, CRC(448c3089) SHA1(779d4138d841783d0e2e5ad29c83d9a8cb4497b6), ROM_BIOS(1))
	// 2: Juko ST BIOS ver 2.30 / Copyright 1988 Juko Electronics Industrial Co., Ltd.
	ROM_SYSTEM_BIOS(2, "nest230", "JUKO NEST v2.30")
	ROMX_LOAD( "juko_st_v2.30.bin", 0xe000, 0x2000, CRC(7a1c6dfa) SHA1(0b343f3028ca06c9e6dc69427d1b15a47c74b9fc),ROM_BIOS(2))
	// 3: BIOS Ver 2.32
	ROM_SYSTEM_BIOS(3, "nest232", "JUKO NEST v2.32")
	ROMX_LOAD( "xt-juko-st-2.32.bin", 0xe000, 0x2000, CRC(0768524e) SHA1(259520bb7a6796e5b987c2b9bef1acd501df1670),ROM_BIOS(3))
ROM_END

/**************************************** JUKO NEST N3 true 16 bit variants ***

https://www.vogons.org/viewtopic.php?f=46&t=60077
https://sites.google.com/site/misterzeropage/
http://www.vcfed.org/forum/showthread.php?67127-Juko-nest-n3

CPU: 8086 and V30, 4.77MHz/7.16MHz/10MHz hardware or software selectable
Bus: 8 ISA slots, dynamic bus speed control
RAM: 512K/640K/1MB on board, EMS 4.0 support (384K on board can be configured either as
RAMDISK in extended memory or EMS in expanded memory

key commands: [Ctrl]-[Alt]-[1]/[2]/[3] to select CPU speed after running CONTROL.COM

DIP switches: (SW3 to SW8 are autodetected by the NEST BIOS, they need to be set if another BIOS is used).
SWA: SW1  SW2  SW3  SW4  SW5  SW6  SW7  SW8  effect
     ---                                     reserved, ON/OFF don't matter
           ON                                no 8087
          OFF                                8087 present
                ON   ON                      0KB memory size
               OFF   ON                      512KB
                ON  OFF                      640KB
               OFF  OFF                      1MB
                          ON   ON            EGA
                         OFF   ON            CGA 40x25
                          ON  OFF            CGA 80x25
                         OFF  OFF            MDA
                                    ON   ON  1 diskette drive
                                   OFF   ON  2
                                    ON  OFF  3
                                   OFF  OFF  4
******************************************************************************/

void pc_state::juko16(machine_config &config)
{
	/* basic machine hardware */
	v30_device &maincpu(V30(config, "maincpu", 4772720));
	maincpu.set_addrmap(AS_PROGRAM, &pc_state::pc16_map);
	maincpu.set_addrmap(AS_IO, &pc_state::pc16_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	ibm5160_mb_device &mb(IBM5160_MOTHERBOARD(config, "mb"));
	mb.set_cputag(m_maincpu);
	mb.int_callback().set_inputline(m_maincpu, 0);
	mb.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	mb.kbdclk_callback().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	mb.kbddata_callback().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));
	mb.set_input_default(DEVICE_INPUT_DEFAULTS_NAME(pccga));

	ISA8_SLOT(config, "isa1", 0, "mb:isa", pc_isa8_cards, "ega", false); // FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa2", 0, "mb:isa", pc_isa8_cards, "fdc_xt", false);
	ISA8_SLOT(config, "isa3", 0, "mb:isa", pc_isa8_cards, "lpt", false);
	ISA8_SLOT(config, "isa4", 0, "mb:isa", pc_isa8_cards, "com", false);

	/* keyboard */
	pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_IBM_PC_XT_83));
	kbd.out_clock_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_clock_w));
	kbd.out_data_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_data_w));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("640K").set_extra_options("64K, 128K, 256K, 512K");
}

ROM_START( juko16 )
	ROM_REGION16_LE(0x10000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "v107", "v1.07")
	ROMX_LOAD("c22.bin", 0xc000, 0x2000, BAD_DUMP CRC(e947237b) SHA1(65e84675752a4deb0d0712e2aba8c0735959b43a),ROM_BIOS(0))
	ROMX_LOAD("c24.bin", 0xe000, 0x2000, BAD_DUMP CRC(1d3246e4) SHA1(4ff875d15b1231a2464dfe08e480c637fa0c4613),ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v201", "v2.01")
	ROMX_LOAD("juko_nest_odd.bin", 0xc000, 0x2000, CRC(2bfa545f) SHA1(1cdaf90323cbed3224b4b8863bf27e709be6a73a),ROM_BIOS(1))
	ROMX_LOAD("juko_nest_even.bin", 0xe000, 0x2000, CRC(2bfa545f) SHA1(1cdaf90323cbed3224b4b8863bf27e709be6a73a),ROM_BIOS(1))
ROM_END


/****************************************************** Hyosung Topstar 88T ***

http://minuszerodegrees.net/xt_clone_bios/xt_clone_bios.htm

CPU: 8088, FPU socket provided
RAM: 27xKM41256AP-15 (768KB)
Bus: 5xISA
on board:  'Paradise' CGA (ROM not dumped), floppy controller  (connector labelled DISC)  (supporting 4 drives on the one connector), RTC
    par(connector labelled PR), 2xser(connectors labelled ASYNC1 and ASYNC2), Light pen connector
OSC: 22.440000MHz, 14.31818, 16MHz, 1.8432MHz

DIP switches: one block of 8 DIP switches

******************************************************************************/

ROM_START( hyo88t )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD( "hyosung_topstar_88t_v3.0.bin", 0xc000, 0x4000, CRC(2429046b) SHA1(e2a8e1ffdd4c6ff84791f486df3204811fa5f589))
ROM_END

/*************************************************************** Kyocera XT ***

http://www.hampa.ch/pce/download.html

******************************************************************************/

ROM_START( kyoxt )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD( "kyocera.rom", 0xc000, 0x4000, CRC(cd732ac6) SHA1(7258fc18565135870e31962e4bd528a06d1ee0e0))
ROM_END

/*********************Panasonic Sr. Partner / *** Nixdorf 8810/25 CPC - PC01 ***

Luggable machine with a 9" monochrome enhanced CGA display and an electrostatic printer
ROM is identical between the Nixdorf and the Panasonic
Displays "PIT1 ERROR"

CPU: Intel 8088 @ 4.77MHz
RAM: 256KB
Monitor: 9" amber
Bus: 2xISA
mass storage: 2xDSDD 5.25"
integrated thermal printer, 80/132 characters per line, Epson MX 80 compatible
on board: parallel port, serial port, RGB port for color monitor

The version 8810/25 CPC has 256KB RAM on the mainboard, a harddisk and RAM can be expanded on the harddisk controller by 320/512KB
in addition to the DIP switches on the mainboard, DIP switches on the HD controller have to be set.

DIP switches: 1    2    3    4    5    6    7    8    effect
              ON  OFF   ON   ON   ON                  128 KB RAM
              ON  OFF  OFF   ON   ON                  256
              ON   ON   ON  OFF   ON                  320
              ON  OFF   ON  OFF   ON                  384
              ON   ON  OFF  OFF   ON                  448
              ON  OFF  OFF  OFF   ON                  512
              ON   ON   ON   ON  OFF                  576
              ON  OFF   ON   ON  OFF                  640
                                       OFF            8087 present
                                        ON            8087 absent
                                             OFF      80 char/line
                                              ON      40 char/line
                                                  OFF 1 FDD
                                                   ON 2 FDD

******************************************************************************/

ROM_START( nixpc01 )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD( "nx01.bin", 0xc000, 0x4000, CRC(b0a75d1f) SHA1(7c2890eced917969968fc2e7491cda90a9734e03))
ROM_END

/***************************************************** Leading Edge Model D ***

Those use an Intel Wildcard 88, a XT computer sans slots and DRAM on a SIMM like module
Chipset: Faraday FE2010A

0300-031F Clock port

******************************************************************************/

ROM_START( ledgmodd )
	ROM_REGION(0x10000, "bios", 0)
	// 0: blank display
	ROM_SYSTEM_BIOS(0, "le", "Leading Edge")
	ROMX_LOAD( "wildcard_88-the_leading_edge-model_d-le_303-27.bin", 0xc000, 0x4000, CRC(cc05347d) SHA1(c44f3ce56472e0894ab955a14f6a91a3fb876baf), ROM_BIOS(0) )
	// 1: blank display
	ROM_SYSTEM_BIOS(1, "daewoo", "Daewoo")
	ROMX_LOAD( "wildcard_88-the_leading_edge-model_d-daewoo-pn_23096023.bin", 0xc000, 0x4000, CRC(34f5fa32) SHA1(73c0489532a1f9a0b23bdd1865cd8b0c6f131ad9), ROM_BIOS(1) )
	// 2: Phoenix 8088 ROM BIOS Version 2.52 / P E Nelson - No scancode from keyboard
	ROM_SYSTEM_BIOS(2, "wildcard", "Wildcard")
	ROMX_LOAD( "wildcard7354-1001rev2.52.05.bin", 0x8000, 0x8000, CRC(ea0c4c2f) SHA1(d817f57dd5332a943b33826dbe67b23e4c94a6ca), ROM_BIOS(2) )
ROM_END

/******************************************************Leading Edge Model M ***

aka the Sperry PC, the "Sperry HT - 4.71 BIOS" that can be found online is identical to the v.4.71 below
E-TD10 - TOD Error
acording to http://www.o3one.org/hwdocs/bios_doc/dosref22.html this machine had AT-like RTC services
The "M" stood for a Mitsubishi made machine, the "Leading Edge Model D" was made by Daewoo
Works with the "siemens" config, so instead of duplicating it until more is known we'll use that.

Interrupt 1Ah  Time of Day, Function 02h, 03h, 04h, 05h are valid on the Model M

******************************************************************************/

ROM_START( ledgmodm )
	ROM_REGION(0x10000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "v330", "Version 3.30")
	ROMX_LOAD( "leading_edge-model_m-version_3.30.bin", 0xc000, 0x4000, CRC(386dd187) SHA1(848ccdc8209c24478a4f75dd941760c43d3bc732), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "v471", "Version 4.71")
	ROMX_LOAD( "leading_edge-model_m-version_4.71.bin", 0xc000, 0x4000, CRC(0d5d8bee) SHA1(6c35adf6a8da149e420b5aa8dd0e18e02488cfa0), ROM_BIOS(1) )
ROM_END

/************************************** CCI Micromint MPX-16 PC Motherboard ***

Circuit Cellar Project
The ROMs are marked "Micromint MPX16 5/8 PC/Term 3/1/84"
hangs on boot, maybe they are waiting for a serial connection

One block of eight DIP switches

******************************************************************************/

ROM_START( mpx16 )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD("mpx16u84.bin", 0xe000, 0x1000, CRC(8a557a25) SHA1(90f8112c094cc0ac44c2d5d43fbb577333dfc165))
	ROM_LOAD("mpx16u85.bin", 0xf000, 0x1000, CRC(42097571) SHA1(2acaca033242e35e512b30b2233da02bde561cc3))
ROM_END

/*************************************************** Vendex HeadStart Plus ***

Samsung manufactured - Chipset: Faraday FE2010A - "Keyboard Error or no keyboard present"
On-board: FDC

******************************************************************************/
ROM_START( hstrtpls )
	ROM_REGION(0x10000,"bios", 0)
	ROM_LOAD("bios.bin",  0xc000, 0x04000, CRC(19d705f8) SHA1(5e607fec6b533bc59d8d804e399bb9d438d6999d))
ROM_END

/************************************************* Philips NMS 9100 series ***
Desktop

*****************************************************************************/

ROM_START( nms9100 )
	ROM_REGION(0x10000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "pcrom12", "PC ROM 1.2") // there is also a 1.5 yet undumped
	ROMX_LOAD("philipsnms9100.bin", 0xc000, 0x4000, CRC(3c1cfa16) SHA1(d060501588b451b0f4a816bede65eafb514b9603), ROM_BIOS(0)) // Philips PC ROM 1.2
	ROM_SYSTEM_BIOS(1, "v313", "Philips ROM BIOS Version 3.13") // from a P3120, use Hercules
	ROMX_LOAD("philips_p3120.bin", 0x8000, 0x8000, CRC(0370e9e6) SHA1(61017e36b9f34f163970cdd2bb3ffd9f66e57382), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "5017", "5017") // no display
	ROMX_LOAD("philipsxt.bin", 0x8000, 0x8000, CRC(2f3135e7) SHA1(d2fc4c06cf09e2c5a62017f0977b084be8bf9bbd), ROM_BIOS(2))
ROM_END

/**************************************************************** EC-1847 ***
Desktop?
*****************************************************************************/

void pc_state::ec1847(machine_config &config)
{
	pccga(config);
//  subdevice<isa8_slot_device>("isa1")->set_default_option("hercules");
}

ROM_START( ec1847 )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD("308_d47_2764.bin",  0x8000, 0x2000, CRC(f06924f2) SHA1(83a5dedf1c06f875c598f087bbc087524bc9bfa3)) // hdc
	ROM_LOAD("188m_d47_2764.bin", 0x4000, 0x2000, CRC(bc8742c7) SHA1(3af09d14e891e976b7a9a2a6e1af63f0eabe5426))
	ROM_LOAD("188m_d48_2764.bin", 0xe000, 0x2000, CRC(7d290e95) SHA1(e73e6c8e19477fce5de3f95b89693dc6ad6781ab))

	ROM_REGION(0x2000, "gfx1", ROMREGION_ERASE00)
	ROM_LOAD("317_d28_2732.bin", 0x00000, 0x1000, CRC(8939599b) SHA1(53d02460cf93596882a96758ef4bac5fa1ce55b2)) // monochrome font
ROM_END

/************************************************* AEG Olympia Olystar 20F ***
Form Factor: Desktop
uses an Acer 710IIN motherboard, BIOS-Version 4.06
CPU: AMD P8088-1, FPU socket available
Chips: Acer M1101, 2201A, UM8250B, WD37C65B-PL , Paradise PVC4
OSC: 14.31818, 30.000000MHz, 16.000, 1.832
RAM: 640K (256K, 512K, 768K, 1024K)
Bus: two ISA8 slots on a riser card
Video: Hercules/CGA compatible, on board
Mass storage: Floppy 720KB, HD 20MB on WD MFM-controller
On board ports: parallel, serial, Video, keyboard (Mini-DIN)

*****************************************************************************/

void pc_state::olystar20f(machine_config &config)
{
	pccga(config);

	subdevice<isa8_slot_device>("isa2")->set_option_machine_config("fdc_xt", cfg_single_720K);
	subdevice<isa8_slot_device>("isa3")->set_default_option(nullptr);
	subdevice<isa8_slot_device>("isa5")->set_default_option("hdc");
	subdevice<ram_device>(RAM_TAG)->set_default_size("640K").set_extra_options("64K, 128K, 256K, 512K, 768K, 1024K"); // the BIOS detects 2432KB extension RAM in the 640K setting ...
}

ROM_START( olystar20f )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD("20f_ebios_u43_v4.06.bin", 0x8000, 0x8000, CRC(0dddb623) SHA1(d821f48ddc7c77868b3f5952fa12f41911bea406))

	ROM_REGION(0x2000,"gfx1", 0) // on board PVC4 based graphics card (similar to Commodore PC AGA and Schneider EuroPC)
	ROM_LOAD("20f_u11_v1.3.bin", 0x0000, 0x2000, CRC(d252ee8d) SHA1(035385521abc3d1b79967b5302a87d08f9383215))
ROM_END

/********************************************************* Cordata PPC-400 ***
Form factor: Luggable
Links: https://www.system-cfg.com/detailcollection.php?ident=243
CPU: 8088/4.77MHz
RAM: 256K or 512K
Mass storage: 1/2 floppy disks 5.25" DD, 10MB or 20MB harddisk
On board: serial, parallel, video (CGA, Hercules, 640x400 mode)
Monitor: 9" monochrome

*****************************************************************************/

void pc_state::coppc400(machine_config &config)
{
	pccga(config);

	subdevice<ram_device>(RAM_TAG)->set_default_size("512K").set_extra_options("256K");
	// the top 16K of the 512K are used for graphics even if a RAM expansion card is used
}

ROM_START( coppc400 )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD("f800ffff.rom", 0x8000, 0x8000, CRC(3d9b6594) SHA1(41f85e692e2020326fd580f7c436c23c76840119))
ROM_END

/************************************************************ Thomson TO16 ***
Form factor: Desktop
CPU: 8088 9.54MHz / 4.77MHz, FPU socket provided
RAM: 512KB-768KB
ROM: 32KB ROM BIOS, 16KB character generator (not dumped)
On board video: Plantronics Colorplus (MDA/Hercules/CGA/Plantronics), EGA card (GB100) on XPHD
On board: RS232C, parallel
Mass storage: 1x5.25" DS/DD (TO16PCDD / TO16 XP), additional 20MB HDD (TO16 XP HD)
Options: Modem (TO16 PCM, ISA card)
ISA8: 2 (PC, PCM), 4 (XPDD, XPHD)
RTC: on XPDD and XPHD

*****************************************************************************/

ROM_START( to16 )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD("to16_103.bin", 0x8000, 0x8000, CRC(a2d55e16) SHA1(fcc61bbfe49164c4b79c368fb782d1ecc17e0a42))
ROM_END

/********************************************************** Sanyo SPC-400D ***
ROM BIOS Version 1.18
*****************************************************************************/

ROM_START( spc400d )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD("fb896.u6", 0xc000, 0x4000, CRC(a6f3ad8c) SHA1(1ee012f9a1757eb68150fedc9db16ff356722f72))
ROM_END


/******************************************* Triumph-Adler Alphatronic P10 ***
Form factor: Desktop
Links: https://www.marcuslausch.de/2020/01/21/triumph-adler-alphatronic-p10/, http://www.cc-computerarchiv.de/CC-Archiv/bc-alt/gb-triad/gb-triad-6_87.html
CPU: 8088@4.77MHz on a motherboard branded Super-640
RAM: 640KB
Video: Hercules (branded MG-200), monitor: 12" amber
Mass storage: 2x5.25" DSDD, a single floppy/hdd version was called P20
Interfaces: V24, Centronics
On board: RTC
DIP switches: 1    2    3    4    5    6    7    8    effect
             OFF                                      default
                   ON                                 FPU present
                  OFF                                 FPU absent
                                  ON   ON             Display: none
                                 OFF  OFF             monochrome
                                 OFF   ON             Color 40x25
                                  ON  OFF             Color 80x25
                                            ON   ON   1 Floppy disk drive
                                           OFF   ON   2
                                            ON  OFF   3
                                           OFF  OFF   4

*****************************************************************************/

ROM_START( alphatp10 )
	ROM_REGION(0x10000,"bios", 0)
	ROM_LOAD("cgab01_04_06.bin", 0x8000, 0x4000, CRC(4f1048e9) SHA1(c5feee7c00fdb7466c6afec753363d11b32983b5))
	ROM_LOAD("cgab02_04_07.bin", 0xc000, 0x4000, CRC(a95998cb) SHA1(1d939f0b7ea3999c44f98b30c26d36e394b87503))
ROM_END


/******************************************* Triumph-Adler Alphatronic P50 ***
Form factor: Desktop
CPU: 80186@6MHz
RAM: 512KB
Mass storage: 2x5.25" DSDD-2, a single floppy/hdd (15MB unformatted) version was called P60-1,
machines with 5.25" DSQD drives (Panasonic  JU465-5 ALY, 720K) had the -2 suffix
Ports: Parallel, serial (V24) - OSC: 16.0MHz, 12.000MHz, 14.3181MHz - on board battery
ISA: 5 slots, one occupied by graphics card (P50)
Graphics: S230790/00 GEJA04, MC6845P based, OSC: 20.0000 MHz, modes: 160x100 (16col. incl. black and white),
320x200 or 320x400 (4col. altogether: 1/16 for the background, 1 for the foreground (red, green or brown,
alt. cobalt blue, violet or white), 640x200 or 640x400 (black, white and two intermediate hues)
Floppy controller: S131005/00A CE0121/8AJ00072 - NEC B9201C, Intel P8272A
Keyboard: has separate "Shift Locke" and "Caps Lock" keys, "Clear" key (Ctrl-Clear to clear the screen),
an "alpha" key and 18 function keys, it has no NumLock key.
If you load the "tw" utility and press Ctrl-Alpha, you switch the computer into typewriter mode,
and all typed text goes straight to the printer.

DIP switches: 1    2    3    4    5    6    7    8    effect
             OFF                                      load OS from floppy disk
              ON                                      load OS from hard disk
                       ON    ON                       128KB RAM
                      OFF    ON                       256KB
                       ON   OFF                       384KB
                      OFF   OFF                       512KB
                                  ON   ON             no monitor connected
                                 OFF   ON             color graphics monitor 40x25
                                  ON  OFF             color graphics monitor 80x25
                                 OFF  OFF             monochrome screen connected
                                           ON   ON    1 floppy disk drive
                                          OFF   ON    2, other positions of switches 7 and 8 are not allowed
*****************************************************************************/

void pc_state::alphatp50(machine_config &config)
{
	/* basic machine hardware */
	I80186(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc_state::pc16_map);
	m_maincpu->set_addrmap(AS_IO, &pc_state::pc16_io);
	downcast<i80186_cpu_device &>(*m_maincpu).set_irmx_irq_ack("mb:pic8259", FUNC(pic8259_device::inta_cb));

	ibm5160_mb_device &mb(IBM5160_MOTHERBOARD(config, "mb"));
	mb.set_cputag(m_maincpu);
	mb.int_callback().set(m_maincpu, FUNC(i80186_cpu_device::int0_w));
	mb.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	mb.kbdclk_callback().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	mb.kbddata_callback().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));
	mb.set_input_default(DEVICE_INPUT_DEFAULTS_NAME(pccga));

	// FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa1", 0, "mb:isa", pc_isa8_cards, "cga", false);
	ISA8_SLOT(config, "isa2", 0, "mb:isa", pc_isa8_cards, "fdc_xt", false);
	ISA8_SLOT(config, "isa3", 0, "mb:isa", pc_isa8_cards, "lpt", false);
	ISA8_SLOT(config, "isa4", 0, "mb:isa", pc_isa8_cards, "com", false);
	ISA8_SLOT(config, "isa5", 0, "mb:isa", pc_isa8_cards, nullptr, false);

	/* keyboard */
	pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_IBM_PC_XT_83));
	kbd.out_clock_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_clock_w));
	kbd.out_data_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_data_w));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("512K").set_extra_options("128K, 256K, 384K");

	/* software lists */
	SOFTWARE_LIST(config, "disk_list").set_original("ibm5150");
}

ROM_START( alphatp50 )
	ROM_REGION16_LE(0x10000, "bios", 0)
	ROMX_LOAD("pc50ii_even_103_16.4.87.bin", 0x8000, 0x4000, CRC(97067b5b) SHA1(260bdeb0a2640141d707eda7b55f2ad4e9c466cd), ROM_SKIP(1))
	ROMX_LOAD("pc50ii_odd_104_16.4.87.bin", 0x8001, 0x4000, CRC(a628a056) SHA1(0ea6b1bcb8fe9cdf85a570df5fb169abfd5cbbe8), ROM_SKIP(1))
ROM_END


/************************************************ Triumph-Adler TA 1700-PC ***
Another 80186 PC compatible from Triumph-Adler, looks very similar to the P50 that is already in MAME.
In this case, a hard card with a Lapine 20MB harddisk is inside, and the floppy controller is included
on the mainboard on the area that is left blank on the P50.

Both mainboards are B301-30747
*****************************************************************************/

ROM_START( ta1700pc )
	ROM_REGION16_LE(0x10000, "bios", 0)
	ROMX_LOAD("ta1700pc_ceab06_02_103.bin", 0x8000, 0x4000, CRC(2a6a116a) SHA1(459c7533f56c358a9f63469ad43904f3bdf851ae), ROM_SKIP(1))
	ROMX_LOAD("ta1700pc_ceab07_02_104.bin", 0x8001, 0x4000, CRC(4313d4db) SHA1(36ece348c2369be6bb2d6895fb7e99d3fa944ac5), ROM_SKIP(1))
ROM_END

/********************************************************** Sanyo MBC-16LT ***
Form factor: Laptop
Motherboard ID: SPC-500B, ROM BIOS Version 1.03, at least a Version 1.06 exists as well
CPU: i8088
Yamaha V6366B-F (graphics), Toshiba T4770, Sanyo MB622110 16LT-Dual, VL82C50A-QC
Mass storage: 2x3.5" DSDD floppy drives (720KB)
DIP switches: one block of four DIP switches

*****************************************************************************/

void pc_state::mbc16lt(machine_config &config)
{
	pccga(config);

	subdevice<isa8_slot_device>("isa1")->set_default_option("mda");
	subdevice<isa8_slot_device>("isa2")->set_option_machine_config("fdc_xt", cfg_dual_720K);
}

ROM_START( mbc16lt ) // screen remains blank
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD("fb1d4d.bin", 0xc000, 0x4000, CRC(476df338) SHA1(d04c3d0540de27781252bb70c7031a635e801433))

	// NMC27C64Q EPROM next to a M5M80C49H MCU next to the keyboard connector
	ROM_REGION(0x2000, "kbd", 0)
	ROM_LOAD("fc2x.bin", 0x0000, 0x2000, NO_DUMP)
ROM_END

/************************************************** DTK-Group PC-XT-Clones ***

DTK-Group is the manufacturer of those popular motherboards, utilising a BIOS developed by the Taiwanese
Industrial Technology Research Institute's Electronics Research and Service Organization (ERSO)

*****************************************************************************/

ROM_START( dtkerso )
	ROM_REGION(0x10000, "bios", 0)
	// 0: DTK Corp. COMPUTER XT / DTK/ERSO/BIOS 2.26 (C) 1986
	ROM_SYSTEM_BIOS(0, "dtk226", "XT DTK Erso bios 2.26")
	ROMX_LOAD( "dtk-ers0.rom", 0xe000, 0x2000, CRC(85fd5e10) SHA1(2ae152f042e7e43e27621f071af763e3f9dc68d2),ROM_BIOS(0))
	// 1: DTK Corp. COMPUTER '88 / DTK/ERSO/BIOS 2.37 (C) 1986
	ROM_SYSTEM_BIOS(1, "dtk237", "XT DTK Erso bios 2.37")
	ROMX_LOAD( "dtk2.37.bin", 0xe000, 0x2000, CRC(d29884a5) SHA1(217c949b4188f638a7ae82a408c5a18d77707009), ROM_BIOS(1))
	// 2: DTK Corp. COMPUTER '88 / DTK/ERSO/BIOS 2.38 (C) 1986
	ROM_SYSTEM_BIOS(2, "tava238", "Tava DTK Erso V2.38")
	ROMX_LOAD( "tava_dtk_erso_bios_2.38_u87.bin", 0xe000, 0x2000, CRC(34f5c0e5) SHA1(5a1590f948670a5ef85a1ee7cbb40387fced8a1f), ROM_BIOS(2))
	// 3: DTK Corp. COMPUTER '88 / DTK/ERSO/BIOS 2.40 (C) 1986
	ROM_SYSTEM_BIOS(3, "dtk240", "XT DTK Erso bios 2.40") // 8 MHz Turbo
	ROMX_LOAD( "dtk2.40.bin", 0xe000, 0x2000, CRC(a4ed27c3) SHA1(66b67540d94c0d049ebc14ee14eadd2ab7304818),ROM_BIOS(3))
	// 4: DTK Corp. COMPUTER '88 / DTK/ERSO/BIOS 2.42 (C) 1986
	ROM_SYSTEM_BIOS(4, "dtk242", "XT DTK Erso bios 2.42") // 10 MHz Turbo
	ROMX_LOAD( "dtk2.42.bin", 0xe000, 0x2000, CRC(3f2d2a76) SHA1(02fa057f2c22ab199a8d9795ab1ae570f2b13a36),ROM_BIOS(4))
ROM_END

/*********************************************************** Corona PPC-21 ***

identical to the Olivetti M18P (one online source shows a ROM version 3.06 with the Olivetti)
a BIOS version 1.53 exists

*****************************************************************************/

ROM_START( coppc21 )
	ROM_REGION(0x10000, "bios", 0)
	ROM_SYSTEM_BIOS( 0, "v3.10", "V3.10" )
	ROMX_LOAD( "corona_ppc_21_3.10_8k_rom.bin", 0xe000, 0x2000, CRC(4c243424) SHA1(55910035b49679beddb43a0728a10dc32c73e3e8), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v4.23cg", "V4.23CG" )
	ROMX_LOAD( "corona_ppc21_16k_4.23cg.bin", 0xc000, 0x4000, CRC(4fd3b8fa) SHA1(faeec1d91b7f83ebea05dc80a1961d7d6ddd1a67), ROM_BIOS(1))
ROM_END

/********************************* Sergey's XT, Micro 88, NuXT and NuXT 2.0 ***

Open source projects originating from http://www.malinov.com/Home/sergeys-projects
Various projects have been combined to offer a "modern" ATX XT
https://monotech.fwscart.com/
https://github.com/monotech/NuXTv2
https://github.com/monotech/NuXT

Sergey's XT: http://www.malinov.com/Home/sergeys-projects/sergey-s-xt
The mainboard is a 16bit (long) ISA card that has to be used in conjunction with a backplane - 8-bit ISA bus but some 16-bit ISA signal are
implemented: IRQ10 - IRQ15 lines, and non-latched address lines LA17-LA19. The latter are to enable compatibility with Cirrus Logic CL-GD54xx VGA cards.
Supported CPUs: 12 MHz: NEC uPD70108HCZ-16 (NEC V20HL), 10 MHz: NEC uPD70108HCZ-10 (NEC V20HL), NEC uPD70108C-10 (NEC V20), 8088-1 (AMD and Siemens)
8 MHz: NEC uPD70108C-8 (NEC V20), 80C88-2 (Intel and Harris), 8088-2 (Intel, Fujitsu, Siemens), 4.77 MHz: 80C88 (Harris), 8088 (Intel, NEC, Soviet clones)
AT keyboard controller, can use AT keyboards (VIA VT82C42N, Holtek HT6542B, Intel P8042AHP with AMI KB-BIOS-VER-F firmware)
1MB of SRAM (00000h-9FFFFh, 640K +  6 x 32 KB blocks that can be configured to reside between 0C0000h-0EFFFFh as UMB memory
128KB Flash memory that contains the BIOS ROM and can be used to hold BIOS extensions like the XT-IDE BIOS.
Two 8259 Programmable Interrupt Controllers (PICs in cascade configuration, like in IBM AT. This gives 15 hardware interrupts in total, 5 of them are routed
to the system board itself: IRQ0 - timer, IRQ1 - keyboard, IRQ8 - RTC, IRQ12 - PS/2 mouse, IRQ13 - 8087 co-processor.
The Rest are available on ISA bus., 8237 Direct Memory Access Controller (DMAC), 8254 Programmable Interval Timer (PIT), 8042 keyboard controller (AT-compatible)
1.193182 MHz clock for feeding the 8254 PIT is produced by a 74LS92 divide-by-12 counter, instead of using PCLK output of 8284. This makes PIT input frequency
independent from CPU speed which is an important consideration for turbo mode.
Turbo mode is implemented using F/C input and an oscillator connected to EFI input of 8284 clock generator. Turbo mode could be toggled either using a switch
or by software using 2nd bit of 61h port - DS12887A RTC (DS12885 is recommended)
on board connectors: PS/2 keyboard, PS/2 mouse, connectors for speaker, Reset and Turbo buttons and LED

Xi 8088 processor board: http://www.malinov.com/Home/sergeys-projects/xi-8088
An improved version of Sergey's XT

Micro 8088 processor board: https://github.com/skiselev/micro_8088 , uses https://github.com/skiselev/8088_bios
Micro 8088 is an easy to build IBM PC/XT compatible processor board. It uses a fairly common Faraday FE2010/FE2010A chipset, that implements most of IBM PC/XT LSIs
(Intel 8xxx ICs) and glue logic. Micro 8088 uses SRAM ICs to implement the system RAM, and a Flash ROM IC to store the BIOS, further reducing the number of components,
and simplifying the build process.

The codebase for the BIOS of the different projects has been unified, the code from https://github.com/skiselev/8088_bios now builds for Sergey's XT, Xi and Micro 8088

Monotech NuXT: https://github.com/monotech/NuXT , https://www.vogonswiki.com/index.php/NuXT
MicroATX "Turbo XT" Motherboard - BM PC/XT Compatible Motherboard - MicroATX form factor, 244 x 185 mm - Switchable 4.77MHz, 7.16MHz, and 9.55MHz CPU clock
640K Conventional Memory - Up to 192K Upper Memory Blocks - • Dual 64K System ROM – switchable with DIP switch - System BIOS is Sergey Kiselev’s Micro 8088 BIOS
Up to 32K usable as Option ROM space. XT-IDE BIOS uses half -  Option ROM socket with write support - PS/2 Keyboard Port - Implemented with AT to XT converter in a microcontroller.
ATX power input - -5V rail not needed. Is generated onboard for ISA slots - 20-pin connector. 24-pin connectors will fit too - Four 8-bit ISA Slots - Three of the four slots can fit 16-bit cards
Onboard peripherals: Advanced floppy controller - Supports most floppy drives, including HD and ED - Supports single-density (FM) disks - Serial port - 16550 UART with FIFO buffer
Selectable I/O address and IRQ - CompactFlash interface - Located at I/O port 300h - Super VGA graphics (TVGA9000i SVGA): Up to 1024 x 768 resolution / Up to 256 colours

// Monotech NuXTv2
New: real-time clock - PS/2 mouse port - parallel port - IDE interface - improved CF card compatibility - PC/104 platform - onboard VGA is now an optional PC/104 card.
the VGA port still remains in the I/O area - removed extra Option ROM socket

*****************************************************************************/

void pc_state::pc8_flash_map(address_map &map)
{
	map.unmap_value_high();
	map(0xe0000, 0xfffff).rom().region("bios", 0); // should be Flash memory 29F010/29C010 but can be partially swapped out as UMB for the underlying RAM
}

void pc_state::modernxt(machine_config &config) // this is just to load the ROMs properly, the XT/AT hardware combination resp. the FE2010A (see notes) needs to be set up
{
	/* basic machine hardware */
	v20_device &maincpu(V20(config, "maincpu", 8000000));
	maincpu.set_addrmap(AS_PROGRAM, &pc_state::pc8_flash_map);
	maincpu.set_addrmap(AS_IO, &pc_state::pc8_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	ibm5160_mb_device &mb(IBM5160_MOTHERBOARD(config, "mb"));
	mb.set_cputag(m_maincpu);
	mb.int_callback().set_inputline(m_maincpu, 0);
	mb.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	mb.kbdclk_callback().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	mb.kbddata_callback().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));
	mb.set_input_default(DEVICE_INPUT_DEFAULTS_NAME(pccga));

	ISA8_SLOT(config, "isa1", 0, "mb:isa", pc_isa8_cards, "vga", false); // FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa2", 0, "mb:isa", pc_isa8_cards, "fdc_at", false); // bios supports HD floppies
	ISA8_SLOT(config, "isa3", 0, "mb:isa", pc_isa8_cards, "lpt", false);
	ISA8_SLOT(config, "isa4", 0, "mb:isa", pc_isa8_cards, "com", false);
	ISA8_SLOT(config, "isa5", 0, "mb:isa", pc_isa8_cards, "xtide", false);

	/* keyboard */
	pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_IBM_PC_XT_83));
	kbd.out_clock_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_clock_w));
	kbd.out_data_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_data_w));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("640K").set_extra_options("512K");
}

ROM_START( sergeysxt )
	ROM_REGION(0x20000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "v0.7c", "v0.7c")
	ROMX_LOAD( "bios-0.7c.bin", 0x00000, 0x20000, CRC(bbc87eea) SHA1(2d7445cbbae87e6be860c063aceed34085718caf), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v0.7e", "v0.7e")
	ROMX_LOAD( "bios-0.7e-128k-1.0.bin", 0x00000, 0x20000, CRC(26a065c6) SHA1(3578ff4eb1c3f0cb9540798a2f609eb26c3cdf6f), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v0.8", "v0.8")
	ROMX_LOAD( "bios-0.8-128k-1.0.bin", 0x00000, 0x20000, CRC(4e841aa6) SHA1(a8554f7ef0ee233cd8748ace59523baf6cc44bec), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v0.8.1", "v0.81")
	ROMX_LOAD( "bios-0.8.1-128k-1.0.bin", 0x00000, 0x20000, CRC(6ebda8be) SHA1(b3291da7c0d43f06b2e9f46cb9a4fe0cb9ee8d56), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "v0.9", "v0.9")
	ROMX_LOAD( "bios-0.9-128k-1.0.bin", 0x00000, 0x20000, CRC(2010ff32) SHA1(b89a7a0ddb4686bfe17f122cd14e29ae0c121c7e), ROM_BIOS(4))
ROM_END


ROM_START( xiprocessor )
	ROM_REGION(0x20000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "v0.7e", "v0.7e")
	ROMX_LOAD( "bios-0.7e-128k-2.0.bin", 0x00000, 0x20000, CRC(ad041c73) SHA1(d9186cbcd98aa98e24efacdfa6c39b8666ba01eb), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v0.8", "v0.8")
	ROMX_LOAD( "bios-0.8-128k-2.0.bin", 0x00000, 0x20000, CRC(b75ebdf5) SHA1(f03bd56378535074a29612e0eadaa0257d77bb9f), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v0.8.1", "v0.8.1")
	ROMX_LOAD( "bios-0.8.1-128k-2.0.bin", 0x00000, 0x20000, CRC(5cb8ecd2) SHA1(1f9a9d35861480b19459f8ab48c459c77c7b97de), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v0.9", "v0.9")
	ROMX_LOAD( "bios-0.9-128k-2.0.bin", 0x00000, 0x20000, CRC(e7334d76) SHA1(fb4025accb47f191bc9b526fe5f050c929868ca8), ROM_BIOS(3))
ROM_END

ROM_START( micro88 ) // constant beep but boots, keyboard not working
	ROM_REGION(0x20000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "v0.9.2", "v0.9.2")
	ROMX_LOAD( "bios-0.9.2.bin", 0x10000, 0x10000, CRC(ce2da51b) SHA1(c6f7935464369502e42b42768c09e5a115c79d44), ROM_BIOS(0))
	ROM_IGNORE(0x10000)
	ROM_SYSTEM_BIOS(1, "0.9.3", "v0.9.3")
	ROMX_LOAD( "bios-0.9.3.bin", 0x10000, 0x10000, CRC(0f376e95) SHA1(6284922f85f7094c37a1ced5bd8e8b3ebdd0d153), ROM_BIOS(1))
	ROM_IGNORE(0x10000)
	ROM_SYSTEM_BIOS(2, "0.9.4", "v0.9.4")
	ROMX_LOAD( "bios-0.9.4.bin", 0x10000, 0x10000, CRC(15f8eb3f) SHA1(10d334c25eb6e44900acb5a2b76ac90dcec102a3), ROM_BIOS(2))
	ROM_IGNORE(0x10000)
	ROM_SYSTEM_BIOS(3, "0.9.5", "v0.9.5")
	ROMX_LOAD( "bios-0.9.5.bin", 0x10000, 0x10000, CRC(99c250fb) SHA1(4a740cebe091d42ccb1e8af2defb6d2325bd98c4), ROM_BIOS(3))
	ROM_IGNORE(0x10000)
	ROM_SYSTEM_BIOS(4, "0.9.6", "v0.9.6")
	ROMX_LOAD( "bios-0.9.6.bin", 0x10000, 0x10000, CRC(8ba6dfc5) SHA1(0652a131268853ac44284c334f340018d4e8659c), ROM_BIOS(4))
	ROM_IGNORE(0x10000)
ROM_END


ROM_START( mononuxt ) // constant beep but boots, keyboard not working
	ROM_REGION(0x20000, "bios", 0) // These systems have a CF card slot onboard and include the XT-IDE BIOS in the system BIOS
	ROM_LOAD( "micro8088_bios_0.9.6_plus_xt-ide-cf.bin", 0x00000, 0x20000, CRC(d58f2a1a) SHA1(01b0d75b0ee991c544b9cf40019a358287cafab0))
ROM_END

ROM_START( mononuxt2 ) // constant beep but boots, keyboard not working
	ROM_REGION(0x20000, "bios", 0) // first half is V20 compatible, second half 8088/V20 compatible. Other versions can be built
	ROM_LOAD( "nuxt_128k image_0.9.8_hybrid.bin", 0x00000, 0x20000, CRC(ca22cc53) SHA1(57e04285ca7920afe38366c90d6f0359b398367b))
ROM_END

/**************************************************** Alloy EarthStation-I ***

This is an x86-compatible, ARCnet based thin client built into an AT-style keyboard.
It needs to get a V40 CPU and an emulation of the Arcnet part.

Form factor: keyboard
CPU: NEC V40
RAM: 256K, 512K, 768KB
Video: NCR7280 based, Hercules, CGA, MCGA (an enhanced CGA mode, not the PS/2 one)
Connectors: LPT1, COM1, Arcnet (BNC)
Mass storage: No internal mass storage possible, boot via Arcnet or a project like BootLPT/86

*****************************************************************************/

void pc_state::earthst(machine_config &config)
{
	/* basic machine hardware */
	v20_device &maincpu(V20(config, "maincpu", 8000000));
	maincpu.set_addrmap(AS_PROGRAM, &pc_state::pc8_map);
	maincpu.set_addrmap(AS_IO, &pc_state::pc8_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	ibm5160_mb_device &mb(IBM5160_MOTHERBOARD(config, "mb"));
	mb.set_cputag(m_maincpu);
	mb.int_callback().set_inputline(m_maincpu, 0);
	mb.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	mb.kbdclk_callback().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	mb.kbddata_callback().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));
	mb.set_input_default(DEVICE_INPUT_DEFAULTS_NAME(pccga));

	ISA8_SLOT(config, "isa1", 0, "mb:isa", pc_isa8_cards, "cga", false); // FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa2", 0, "mb:isa", pc_isa8_cards, "lpt", false);
	ISA8_SLOT(config, "isa3", 0, "mb:isa", pc_isa8_cards, "com", false);

	/* keyboard */
	pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_KEYTRONIC_PC3270));
	kbd.out_clock_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_clock_w));
	kbd.out_data_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_data_w));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("768K").set_extra_options("512K");
}

ROM_START( earthst )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD("earthstation.bin", 0x8000, 0x8000, CRC(a56d3d6d) SHA1(98b17579b15e4da69054ab971ce6cebe06d05c51))
ROM_END


/*********************************************************** Victor VPC II ***

Some of the information found online is contradictory, especially when it comes to
distinguish between the VPC II, VPC IIc and VPC IIe models.
The computer was originally shipped with a Hercules/CGA card and a monochrome monitor.
It's possible that the "c" and "e" denote CGA and EGA color monitors respectively.

Form factor: desktop
CPU: 8086 at 4.77 or 7.16MHz
RAM: depending on the mainboard version, the RAM can be 64KB-256KB
     or up to 640KB
Video: CGA/Hercules or EGA
On board: ser, par, Bus mouse (c model)
Mass storage: 1 or 2 Floppy 5,25" 360KB, optional 15MB, 20MB or 30MB HD

*****************************************************************************/
void pc_state::vpcii(machine_config &config)
{
	pccga(config);

	i8086_cpu_device &maincpu(I8086(config.replace(), "maincpu", XTAL(14'318'181)/3)); // 4.77 MHz, Crystal needs to be verified; other examples ran at 7.16MHz
	maincpu.set_addrmap(AS_PROGRAM, &pc_state::pc16_map);
	maincpu.set_addrmap(AS_IO, &pc_state::pc16_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));
}

ROM_START( vpcii ) // The BIOS was dumped the "MESS" way using DOS DEBUG
	ROM_REGION16_LE(0x10000, "bios", 0)
	ROM_LOAD("victor_vpcii_bios.bin", 0xc000, 0x4000, CRC(6a214121) SHA1(5426ce641bd7dc03e8189b0a4736e0d70232335b))
ROM_END


/************************************************************** Frael King ***

Form factor: Desktop
CPU: NEC V20 @ 8MHz
RAM: 256KB (King 1) or 512KB (King 2), upgradeable to 768KB
Video: CGA (on board)
Keyboard: 84 keys with 10 function keys
On board: par, cass, RF out, beeper
Mass storage: none (King 1), 720KB floppy (King 2)
One ISA slot, riser card with 3 slots, one occupied by the floppy controller (King 2)

The original HD capable FD controller needs to be emulated in order to boot the course
disks for the IT class that the King was used for in Italy.
The King 1 used ROM Basic, with audio cassette as mass storage.
If you bought the King 1, you could send the computer in to the factory to have it upgraded
to King 2 specs, they would upgrade the RAM and add the floppy drive and controller.
No HD capable 8bit ISA Floppy controller present in MAME works with the King, so the floppy
crive is set to DD for the time being.

*****************************************************************************/

void pc_state::fraking(machine_config &config)
{
	pccga(config);

	v20_device &maincpu(V20(config.replace(), "maincpu", 8000000));
	maincpu.set_addrmap(AS_PROGRAM, &pc_state::pc8_map);
	maincpu.set_addrmap(AS_IO, &pc_state::pc8_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	subdevice<isa8_slot_device>("isa2")->set_option_machine_config("fdc_xt", cfg_single_720K);
	subdevice<isa8_slot_device>("isa3")->set_default_option(nullptr);
	subdevice<isa8_slot_device>("isa5")->set_default_option(nullptr);
	subdevice<ram_device>(RAM_TAG)->set_default_size("768K");
}

ROM_START( fraking ) // boots but doesn't fall back to ROM BASIC
	ROM_REGION(0x20000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "3.40", "3.40")
	ROMX_LOAD("king3-40.rom", 0x00000, 0x20000, CRC(7d64186c) SHA1(6e3c0d836903bda8c2512fde3bb2ba432705ce27), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "3.42", "3.42")
	ROMX_LOAD("king3-42.rom", 0x00000, 0x20000, CRC(7c040fda) SHA1(c5aaa795d773d41c086a80bc43ee7200f53c3a0c), ROM_BIOS(1))
ROM_END


/******************************************************* MY-COM MPU-9088-VF ***

*****************************************************************************/

ROM_START( mpu9088vf ) // From a motherboard marked MY-COM MPU-9088-VF SAN-MS94VO
	ROM_REGION(0x10000, "bios", 0) // ROM BASIC doesn't load
	ROM_LOAD( "27256-mpu-9088-vf_rom2.bin", 0x6000, 0x8000, CRC(e077c7c8) SHA1(f58f57f522b48f3aa4c381afb42e04795bcfbbad))
	ROM_LOAD( "27128-mpu-9088-vf_rom1.bin", 0xc000, 0x4000, CRC(a211e539) SHA1(1a45627fb34e38f6e3485c1526ff1d9a645c8683))
ROM_END

/****************************************************** Hyundai Super 16 T ***

Model: Hyundai Super 16 T
Form factor: Desktop
CPU: Intel 8088 @ 8 MHz, can be toggled to 4.77 MHz using CTRL-ALT-ENTER; FPU: socket provided
RAM: 640 KB
OSC: 1.843200 MHz, 14.31818, 24.000
Mass storage: 360K 5.25" floppy drive
Sound: Built in Speaker
Video: CGA Graphics
On board: 2 x serial, 1x parallel, floppy, RTC
ISA: 6 slots

*****************************************************************************/

ROM_START( hyu16t )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD( "hea_v1.12ta_1986.bin", 0xc000, 0x4000, CRC(66573361) SHA1(6d0ef7ef6cd0bfbe2917ee52602e470cd143075f))
ROM_END


/***************************************************** Hyundai Super 16 TE ***

Form factor: Desktop
CPU: Intel 8088 @ 10MHz, FPU: socket provided
RAM: 640 KB
OSC: 1.8432 MHz, 14.31818, 30.000000MHz
Mass storage: 360K 5.25" floppy drive, Seagate 20MB HDD
Sound: Built in Speaker
Video: CGA Graphics
On board: 2 x serial, 1x parallel, floppy, RTC
Keyboard: 101key
ISA: 5 slots

*****************************************************************************/

ROM_START( hyu16te )
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD( "v2.00id_1989.bin", 0xc000, 0x4000, CRC(9a7a9917) SHA1(e114fdcc7b8caf76070f633bdab8c792eaa7eda0))
ROM_END


/************************************************************ AUVA VIP 800 ***

Form factor: Desktop
Motherboard: Juko Baby BXM/10-III
CPU: NEC V20 @ 8 MHz
RAM: up to 1MB, the Juko utilities can use the extra RAM as a RAM Disk, printer Buffer or harddisk cache
OSC: 24.000 MHz, 14.31818
ISA: 8 slots

*****************************************************************************/

ROM_START( auvip800 ) // a v2.30 Juko BIOS exists on this MB, cf. juko8
	ROM_REGION(0x10000, "bios", 0)
	ROM_LOAD( "bxm10_phoenix_ver 2.52d.bin", 0xe000, 0x2000, CRC(9c964c80) SHA1(59a60425aa867abd33d30303300ed3c587969d2a))
ROM_END

} // anonymous namespace


/***************************************************************************

  Game driver(s)

***************************************************************************/

//    YEAR  NAME            PARENT   COMPAT  MACHINE         INPUT     CLASS     INIT           COMPANY                            FULLNAME                 FLAGS
COMP( 1989, mk88,           ibm5150, 0,      mk88,           pccga,    pc_state, empty_init,    "<unknown>",                       "MK-88",                 MACHINE_NOT_WORKING )
COMP( 1991, poisk2,         ibm5150, 0,      poisk2,         pccga,    pc_state, empty_init,    "<unknown>",                       "Poisk-2",               MACHINE_NOT_WORKING )
COMP( 1990, mc1702,         ibm5150, 0,      eagle1600,      pccga,    pc_state, empty_init,    "<unknown>",                       "Elektronika MC-1702",   MACHINE_NOT_WORKING )
COMP( 198?, olystar20f,     ibm5150, 0,      olystar20f,     pccga,    pc_state, empty_init,    "AEG Olympia",                     "Olystar 20F",           MACHINE_NOT_WORKING )
COMP( 198?, olytext30,      ibm5150, 0,      olytext30,      pccga,    pc_state, empty_init,    "AEG Olympia",                     "Olytext 30",            MACHINE_NOT_WORKING )
COMP( 1987, earthst,        ibm5150, 0,      earthst,        pccga,    pc_state, empty_init,    "Alloy",                           "EarthStation-I",        MACHINE_NOT_WORKING )
COMP( 1987, ataripc1,       ibm5150, 0,      ataripc1,       pccga,    pc_state, empty_init,    "Atari",                           "PC1",                   0 )
COMP( 1988, ataripc3,       ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "Atari",                           "PC3",                   0 )
COMP( 198?, auvip800,       ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "AUVA",                            "VIP 800",               MACHINE_NOT_WORKING )
COMP( 1985, bw230,          ibm5150, 0,      bondwell,       bondwell, pc_state, init_bondwell, "Bondwell Holding",                "BW230 (PRO28 Series)",  0 )
COMP( 1982, mpc1600,        ibm5150, 0,      mpc1600,        pccga,    pc_state, empty_init,    "Columbia Data Products",          "MPC 1600",              0 )
COMP( 198?, coppc21,        ibm5150, 0,      coppc400,       pccga,    pc_state, empty_init,    "Corona Data Systems, Inc.",       "Corona PPC-21",         MACHINE_NOT_WORKING )
COMP( 198?, coppc400,       ibm5150, 0,      coppc400,       pccga,    pc_state, empty_init,    "Corona Data Systems, Inc.",       "Cordata PPC-400",       MACHINE_NOT_WORKING )
COMP( 1984, comdesk,        ibm5150, 0,      juko16,         pccga,    pc_state, empty_init,    "Compaq",                          "Deskpro",               MACHINE_NOT_WORKING )
COMP( 1983, comport,        ibm5150, 0,      comport,        pccga,    pc_state, empty_init,    "Compaq",                          "Compaq Portable",       MACHINE_NOT_WORKING )
COMP( 198?, cadd810,        ibm5150, 0,      cadd810,        pccga,    pc_state, empty_init,    "CompuAdd",                        "810",                   MACHINE_NOT_WORKING )
COMP( 1984, dgone,          ibm5150, 0,      dgone,          pccga,    pc_state, empty_init,    "Data General",                    "Data General/One" ,     MACHINE_NOT_WORKING )
COMP( 198?, dtkerso,        ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "DTK Group", "PC-XT-Clones with DTK/ERSO-BIOS", 0 )
COMP( 1983, eagle1600,      ibm5150, 0,      eagle1600,      pccga,    pc_state, empty_init,    "Eagle",                           "Eagle 1600" ,           MACHINE_NOT_WORKING )
COMP( 1983, eaglespirit,    ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "Eagle",                           "Eagle PC Spirit",       MACHINE_NOT_WORKING )
COMP( 198?, eaglepc2,       ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "Eagle",                           "PC-2",                  MACHINE_NOT_WORKING )
COMP( 1990, ec1847,         ibm5150, 0,      ec1847,         pccga,    pc_state, empty_init,    "<unknown>",                       "EC-1847",               MACHINE_NOT_WORKING )
COMP( 1985, eppc,           ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "Ericsson Information System",     "Ericsson Portable PC",  MACHINE_NOT_WORKING )
COMP( 1989, fraking,        ibm5150, 0,      fraking,        pccga,    pc_state, empty_init,    "Frael",                           "King",                  MACHINE_NOT_WORKING )
COMP( 198?, hyo88t,         ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "Hyosung",                         "Topstar 88T",           MACHINE_NOT_WORKING )
COMP( 1986, hyu16t,         ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "Hyundai",                         "Super 16 T",            MACHINE_NOT_WORKING )
COMP( 1987, hyu16te,        ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "Hyundai",                         "Super 16 TE",           MACHINE_NOT_WORKING )
COMP( 1984, ittxtra,        ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "ITT Information Systems",         "ITT XTRA",              MACHINE_NOT_WORKING )
COMP( 198?, juko8,          ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "JUKO",                            "NEST 8088 and V20",     MACHINE_NOT_WORKING )
COMP( 198?, juko16,         ibm5150, 0,      juko16,         pccga,    pc_state, empty_init,    "JUKO",                            "NEST 8086 and V30",     MACHINE_NOT_WORKING )
COMP( 1985, kaypro16,       ibm5150, 0,      kaypro16,       pccga,    pc_state, empty_init,    "Kaypro Corporation",              "Kaypro 16",             0 )
COMP( 198?, kaypropc,       ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "Kaypro Corporation",              "PC",                    MACHINE_NOT_WORKING )
COMP( 198?, kyoxt,          ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "Kyocera",                         "XT",                    MACHINE_NOT_WORKING )
COMP( 198?, ledgmodd,       ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "Leading Edge Hardware Products, Inc.", "Model D",          MACHINE_NOT_WORKING )
COMP( 198?, ledgmodm,       ibm5150, 0,      siemens,        pccga,    pc_state, empty_init,    "Leading Edge Hardware Products, Inc.", "Model M",          MACHINE_NOT_WORKING )
COMP( 198?, mpx16,          ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "Micromint",                       "MPX-16",                MACHINE_NOT_WORKING )
COMP( 198?, mpu9088vf,      ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "MY-COM",                          "MPU-9088-VF",           MACHINE_NOT_WORKING )
COMP( 1985, ncrpc4i,        ibm5150, 0,      ncrpc4i,        pccga,    pc_state, empty_init,    "NCR",                             "PC4i",                  MACHINE_NOT_WORKING )
COMP( 1985, ncrpc6,         ibm5150, 0,      ncrpc4i,        pccga,    pc_state, empty_init,    "NCR",                             "PC6",                   MACHINE_NOT_WORKING )
COMP( 198?, nixpc01,        ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "Nixdorf Computer AG",             "8810/25 CPC - PC01",    MACHINE_NOT_WORKING )
COMP( 198?, olivm15,        ibm5150, 0,      m15,            pccga,    pc_state, empty_init,    "Olivetti",                        "M15",                   0 )
COMP( 198?, nms9100,        ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "Philips",                         "NMS 9100",              MACHINE_NOT_WORKING )
COMP( 1989, ssam88s,        ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "Samsung",                         "Samtron 88S",           MACHINE_NOT_WORKING )
COMP( 1988, sx16,           ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "Sanyo",                           "SX-16",                 MACHINE_NOT_WORKING )
COMP( 198?, mbc16,          ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "Sanyo",                           "MBC-16",                MACHINE_NOT_WORKING )
COMP( 198?, mbc16lt,        ibm5150, 0,      mbc16lt,        pccga,    pc_state, empty_init,    "Sanyo",                           "MBC-16LT",              MACHINE_NOT_WORKING )
COMP( 198?, spc400d,        ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "Sanyo",                           "SPC-400D",              MACHINE_NOT_WORKING )
COMP( 1992, iskr3104,       ibm5150, 0,      iskr3104,       pccga,    pc_state, empty_init,    "Schetmash",                       "Iskra 3104",            MACHINE_NOT_WORKING )
COMP( 1985, sicpc1605,      ibm5150, 0,      siemens,        pccga,    pc_state, empty_init,    "Siemens",                         "Sicomp PC16-05",        MACHINE_NOT_WORKING )
COMP( 1985, pc7000,         ibm5150, 0,      eagle1600,      pccga,    pc_state, empty_init,    "Sharp",                           "PC-7000",               MACHINE_NOT_WORKING )
COMP( 1987, to16,           ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "Thomson SIMIV",                   "TO16",                  MACHINE_NOT_WORKING )
COMP( 1985, alphatp10,      ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "Triumph-Adler",                   "Alphatronic P10",       0 )
COMP( 1985, alphatp50,      ibm5150, 0,      alphatp50,      pccga,    pc_state, empty_init,    "Triumph-Adler",                   "Alphatronic P50",       0 )
COMP( 1984, ta1700pc,       ibm5150, 0,      alphatp50,      pccga,    pc_state, empty_init,    "Triumph-Adler",                   "TA 1700-PC",            0 )
COMP( 198?, hstrtpls,       ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "Vendex",                          "HeadStart Plus",        MACHINE_NOT_WORKING )
COMP( 1987, vpcii,          ibm5150, 0,      vpcii,          pccga,    pc_state, empty_init,    "Victor",                          "VPC II",                MACHINE_NOT_WORKING )
COMP( 1988, laser_turbo_xt, ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "VTech",                           "Laser Turbo XT",        0 )
COMP( 1989, laser_xt3,      ibm5150, 0,      pccga,          pccga,    pc_state, empty_init,    "VTech",                           "Laser XT/3",            0 )
COMP( 1987, zdsupers,       ibm5150, 0,      zenith,         pccga,    pc_state, empty_init,    "Zenith Data Systems",             "SuperSport",            0 )
COMP( 198?, zdz150,         ibm5150, 0,      zenith,         pccga,    pc_state, empty_init,    "Zenith Data Systems",             "Z-150 Series",          0 )
COMP( 198?, zdz160,         ibm5150, 0,      zenith,         pccga,    pc_state, empty_init,    "Zenith Data Systems",             "Z-160 Series",          0 )
COMP( 1987, zdz180,         ibm5150, 0,      zenith,         pccga,    pc_state, empty_init,    "Zenith Data Systems",             "Z-180 Series",          MACHINE_NOT_WORKING )
COMP( 2010, sergeysxt,      ibm5150, 0,      modernxt,       pccga,    pc_state, empty_init,    "Sergey Kiselev",                  "Sergey's XT",           MACHINE_NOT_WORKING )
COMP( 2012, xiprocessor,    ibm5150, 0,      modernxt,       pccga,    pc_state, empty_init,    "Sergey Kiselev",                  "Xi processor board",    MACHINE_NOT_WORKING )
COMP( 2017, micro88,        ibm5150, 0,      modernxt,       pccga,    pc_state, empty_init,    "Sergey Kiselev",                  "Micro 8088",            MACHINE_NOT_WORKING )
COMP( 2019, mononuxt,       ibm5150, 0,      modernxt,       pccga,    pc_state, empty_init,    "Monotech",                        "NuXT",                  MACHINE_NOT_WORKING )
COMP( 2020, mononuxt2,      ibm5150, 0,      modernxt,       pccga,    pc_state, empty_init,    "Monotech",                        "NuXT v2",               MACHINE_NOT_WORKING )



pc_h98.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
/**************************************************************************************************

    NEC PC-H[yper] 98

    TODO:
    - NESA bus in place of C-Bus plus a billion of overrides from the base PC-98 ...
    - needs a specific chargen "FONT24.ROM" for anything that isn't a H98S;

**************************************************************************************************/

#include "emu.h"
#include "pc9801.h"

class pc_hyper98_state : public pc9801bx_state
{
public:
	pc_hyper98_state(const machine_config &mconfig, device_type type, const char *tag)
		: pc9801bx_state(mconfig, type, tag)
	{}

	void pc_h98s(machine_config &config);

protected:
	DECLARE_MACHINE_START(pc_h98);
	DECLARE_MACHINE_RESET(pc_h98);

	void pc_h98_map(address_map &map) ATTR_COLD;
	void pc_h98_io(address_map &map) ATTR_COLD;
};

void pc_hyper98_state::pc_h98_map(address_map &map)
{
	pc_hyper98_state::pc9801bx2_map(map);
//  map(0x080000, 0x0bffff).unmaprw(); // RAM window
	// TODO: bigger, needs fn mods
	map(0x0c0000, 0x0dffff).rw(FUNC(pc_hyper98_state::grcg_gvram0_r), FUNC(pc_hyper98_state::grcg_gvram0_w));
	map(0x0e0000, 0x0e3fff).rw(FUNC(pc_hyper98_state::tvram_r), FUNC(pc_hyper98_state::tvram_w));
	map(0x0e4000, 0x0e4fff).rw(FUNC(pc_hyper98_state::pc9801rs_knjram_r), FUNC(pc_hyper98_state::pc9801rs_knjram_w));
}

void pc_hyper98_state::pc_h98_io(address_map &map)
{
	pc_hyper98_state::pc9801bx2_io(map);
	// ...
//  map(0x4*a*, 0x4*a*) μPD72120 "AGDC"
}

// TODO: backported from pc9801_epson.cpp, needs mods
static INPUT_PORTS_START( pc_h98 )
	PORT_START("DSW1")
	PORT_DIPNAME( 0x01, 0x01, "Monitor Type" )
	PORT_DIPSETTING(    0x00, "Normal Display (15KHz)" )
	PORT_DIPSETTING(    0x01, "Hi-Res Display (24KHz)" )
	PORT_DIPNAME( 0x02, 0x00, "DSW1" )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, "Display Type" )
	PORT_DIPSETTING(    0x04, "RGB" )
	PORT_DIPSETTING(    0x00, "Plasma" )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, "Graphic Function" )
	PORT_DIPSETTING(    0x80, "Basic (8 Colors)" )
	PORT_DIPSETTING(    0x00, "Expanded (16/4096 Colors)" )

	PORT_START("DSW2")
	PORT_DIPNAME( 0x01, 0x00, "DSW2" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, "GDC clock" )
	PORT_DIPSETTING(    0x80, "2.5 MHz" )
	PORT_DIPSETTING(    0x00, "5 MHz" )

	PORT_START("DSW3")
	PORT_DIPNAME( 0x01, 0x01, "FDD Fix Mode" )
	PORT_DIPSETTING(    0x00, "Auto-Detection" )
	PORT_DIPSETTING(    0x01, "Fixed" )
	PORT_DIPNAME( 0x02, 0x02, "FDD Density Select" )
	PORT_DIPSETTING(    0x00, "2DD" )
	PORT_DIPSETTING(    0x02, "2HD" )
	PORT_DIPNAME( 0x04, 0x04, "DSW3" )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, "Conventional RAM size" )
	PORT_DIPSETTING(    0x40, "640 KB" )
	PORT_DIPSETTING(    0x00, "512 KB" )
	PORT_DIPNAME( 0x80, 0x00, "CPU Type" )
	PORT_DIPSETTING(    0x80, "V30" )
	PORT_DIPSETTING(    0x00, "I386" )

	PORT_START("MOUSE_X")
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_X ) PORT_SENSITIVITY(30) PORT_KEYDELTA(30)

	PORT_START("MOUSE_Y")
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y ) PORT_SENSITIVITY(30) PORT_KEYDELTA(30)

	PORT_START("MOUSE_B")
	PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_CODE(MOUSECODE_BUTTON2) PORT_NAME("Mouse Right Button")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_CODE(MOUSECODE_BUTTON3) PORT_NAME("Mouse Middle Button")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CODE(MOUSECODE_BUTTON1) PORT_NAME("Mouse Left Button")

	PORT_START("ROM_LOAD")
	PORT_BIT( 0x03, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_CONFNAME( 0x04, 0x04, "Load IDE BIOS" )
	PORT_CONFSETTING(    0x00, DEF_STR( Yes ) )
	PORT_CONFSETTING(    0x04, DEF_STR( No ) )
INPUT_PORTS_END

MACHINE_START_MEMBER(pc_hyper98_state,pc_h98)
{
	MACHINE_START_CALL_MEMBER(pc9801bx2);
}

MACHINE_RESET_MEMBER(pc_hyper98_state,pc_h98)
{
	MACHINE_RESET_CALL_MEMBER(pc9801bx2);

	// boots with DMA > 1MB on.
	m_dma_access_ctrl = 0xfb;
}

void pc_hyper98_state::pc_h98s(machine_config &config)
{
	pc9801bx2(config);
	const XTAL xtal = XTAL(20'000'000);
	I486(config.replace(), m_maincpu, xtal); // i486sx
	m_maincpu->set_addrmap(AS_PROGRAM, &pc_hyper98_state::pc_h98_map);
	m_maincpu->set_addrmap(AS_IO, &pc_hyper98_state::pc_h98_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	MCFG_MACHINE_START_OVERRIDE(pc_hyper98_state, pc_h98)
	MCFG_MACHINE_RESET_OVERRIDE(pc_hyper98_state, pc_h98)

	m_ram->set_default_size("14M");
	// TODO: extra options, 1.6MB up to 45.6MB
}

// Stolen from pc9821
#define LOAD_KANJI_ROMS \
	ROM_REGION( 0x80000, "raw_kanji", ROMREGION_ERASEFF ) \
	ROM_LOAD16_BYTE( "24256c-x01.bin", 0x00000, 0x4000, BAD_DUMP CRC(28ec1375) SHA1(9d8e98e703ce0f483df17c79f7e841c5c5cd1692) ) \
	ROM_CONTINUE(                      0x20000, 0x4000  ) \
	ROM_LOAD16_BYTE( "24256c-x02.bin", 0x00001, 0x4000, BAD_DUMP CRC(90985158) SHA1(78fb106131a3f4eb054e87e00fe4f41193416d65) ) \
	ROM_CONTINUE(                      0x20001, 0x4000  ) \
	ROM_LOAD16_BYTE( "24256c-x03.bin", 0x40000, 0x4000, BAD_DUMP CRC(d4893543) SHA1(eb8c1bee0f694e1e0c145a24152222d4e444e86f) ) \
	ROM_CONTINUE(                      0x60000, 0x4000  ) \
	ROM_LOAD16_BYTE( "24256c-x04.bin", 0x40001, 0x4000, BAD_DUMP CRC(5dec0fc2) SHA1(41000da14d0805ed0801b31eb60623552e50e41c) ) \
	ROM_CONTINUE(                      0x60001, 0x4000  ) \
	ROM_REGION( 0x100000, "kanji", ROMREGION_ERASEFF ) \
	ROM_REGION( 0x80000, "new_chargen", ROMREGION_ERASEFF )

ROM_START( pc_h98s )
	ROM_REGION( 0x40000, "biosrom", ROMREGION_ERASEFF )
	ROM_LOAD( "hcz7k_01.bin", 0x000000, 0x020000, CRC(e68834e8) SHA1(f57dfd67915715168e46907fd535277e30357742) )
	ROM_LOAD( "hcz8k_01.bin", 0x020000, 0x020000, CRC(39f82b02) SHA1(52e950f10faa0bedca3d6ea2ba6caceaeff66fc9) )

	// TODO: needs override around itf_43d_bank_w
	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	ROM_COPY( "biosrom", 0x20000, 0x08000, 0x10000 )
	ROM_COPY( "biosrom", 0x00000, 0x18000, 0x18000 )

	ROM_REGION( 0x80000, "chargen", 0 )
	// stolen from pc9821
	ROM_LOAD( "font.rom", 0x00000, 0x46800, BAD_DUMP CRC(a61c0649) SHA1(554b87377d176830d21bd03964dc71f8e98676b1) )

	LOAD_KANJI_ROMS
//  LOAD_IDE_ROM
ROM_END

COMP( 1991, pc_h98s, 0,   0, pc_h98s, pc_h98,   pc_hyper98_state, init_pc9801_kanji,   "NEC",   "PC-H98S model 8/U8", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



pc100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
// thanks-to: Carl
/***************************************************************************

    NEC PC-100

    preliminary driver by Angelo Salese
    Thanks to Carl for the i8259 tip;

    TODO:
    - i8259 works in edge triggering mode, kludged to work somehow.

    Notes:
    - First two POST checks are for the irqs, first one checks the timer irq:
    F8209: B8 FB 00                  mov     ax,0FBh
    F820C: E6 02                     out     2h,al
    F820E: B9 00 00                  mov     cx,0h
    F8211: FB                        sti
    F8212: 0A E4                     or      ah,ah <- irq fires here
    F8214: E1 FC                     loopz   0F8212h
    F8216: FA                        cli
    F8217: 0A E9                     or      ch,cl
    F8219: 74 15                     je      0F8230h
    - Second one is for the vblank irq timing:
        F8238: 8B D3                     mov     dx,bx
        F823A: 8B D9                     mov     bx,cx
        F823C: CF                        iret
    F824D: E4 02                     in      al,2h
    F824F: 8A E0                     mov     ah,al
    F8251: B0 EF                     mov     al,0EFh
    F8253: E6 02                     out     2h,al
    F8255: BB 00 00                  mov     bx,0h
    F8258: BA 00 00                  mov     dx,0h
    F825B: B9 20 4E                  mov     cx,4E20h
    F825E: FB                        sti
    F825F: E2 FE                     loop    0F825Fh ;calculates the vblank here
    F8261: FA                        cli
    F8262: 8A C4                     mov     al,ah
    F8264: E6 02                     out     2h,al
    F8266: 2B D3                     sub     dx,bx
    F8268: 81 FA 58 1B               cmp     dx,1B58h
    F826C: 78 06                     js      0F8274h ;error if DX is smaller than 0x1b58
    F826E: 81 FA 40 1F               cmp     dx,1F40h
    F8272: 78 0A                     js      0F827Eh ;error if DX is greater than 0x1f40
    F8274: B1 05                     mov     cl,5h
    F8276: E8 CB 03                  call    0F8644h
    F8279: E8 79 FF                  call    0F81F5h
    F827C: EB FE                     jmp     0F827Ch
    F827E: B0 FF                     mov     al,0FFh
    fwiw with current timings, we get DX=0x1f09, enough for passing the test;

****************************************************************************/

#include "emu.h"

#include "cpu/i86/i86.h"
#include "imagedev/floppy.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/msm58321.h"
#include "machine/pic8259.h"
#include "machine/timer.h"
#include "machine/upd765.h"
#include "sound/beep.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

namespace {

class pc100_state : public driver_device
{
public:
	pc100_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_beeper(*this, "beeper")
		, m_rtc(*this, "rtc")
		, m_fdc(*this, "fdc")
		, m_pic(*this, "pic8259")
		, m_palette(*this, "palette")
		, m_kanji_rom(*this, "kanji")
		, m_vram(*this, "vram")
		, m_keys(*this, "ROW.%u", 0U)
		, m_rtc_portc(0)
	{
	}
	void pc100(machine_config &config);
	DECLARE_INPUT_CHANGED_MEMBER(key_stroke);
protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;
private:
	required_device<cpu_device> m_maincpu;
	required_device<beep_device> m_beeper;
	required_device<msm58321_device> m_rtc;
	required_device<upd765a_device> m_fdc;
	required_device<pic8259_device> m_pic;
	required_device<palette_device> m_palette;
	required_region_ptr<uint16_t> m_kanji_rom;
	required_region_ptr<uint16_t> m_vram;
	required_ioport_array<6> m_keys;

	uint16_t pc100_vram_r(offs_t offset);
	void pc100_vram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t pc100_kanji_r();
	void pc100_kanji_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint8_t pc100_key_r(offs_t offset);
	void pc100_output_w(offs_t offset, uint8_t data);
	void pc100_tc_w(uint8_t data);
	uint8_t pc100_shift_r();
	void pc100_shift_w(uint8_t data);
	uint8_t pc100_vs_vreg_r(offs_t offset);
	void pc100_vs_vreg_w(offs_t offset, uint8_t data);
	void pc100_crtc_addr_w(uint8_t data);
	void pc100_crtc_data_w(uint8_t data);
	void lower_mask_w(uint8_t data);
	void upper_mask_w(uint8_t data);
	void crtc_bank_w(uint8_t data);
	void rtc_porta_w(uint8_t data);
	uint8_t rtc_portc_r();
	void rtc_portc_w(uint8_t data);
	void irqnmi_w(int state);
	void drqnmi_w(int state);
	uint16_t m_kanji_addr = 0;
	uint8_t m_timer_mode = 0;

	uint8_t m_bank_r = 0, m_bank_w = 0, m_key = 0;
	bool m_nmi_mask = false, m_irq_state = false, m_drq_state = false;

	struct{
		uint8_t shift = 0;
		uint16_t mask = 0;
		uint16_t cmd = 0;
		uint16_t vstart = 0;
		uint8_t addr = 0;
		uint8_t reg[8]{};
	}m_crtc;
	uint32_t screen_update_pc100(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	INTERRUPT_GEN_MEMBER(pc100_vblank_irq);
	TIMER_DEVICE_CALLBACK_MEMBER(pc100_600hz_irq);
	TIMER_DEVICE_CALLBACK_MEMBER(pc100_100hz_irq);
	TIMER_DEVICE_CALLBACK_MEMBER(pc100_50hz_irq);
	TIMER_DEVICE_CALLBACK_MEMBER(pc100_10hz_irq);


	void rtc_portc_0_w(int state) { m_rtc_portc = (m_rtc_portc & ~(1 << 0)) | ((state & 1) << 0); }
	void rtc_portc_1_w(int state) { m_rtc_portc = (m_rtc_portc & ~(1 << 1)) | ((state & 1) << 1); }
	void rtc_portc_2_w(int state) { m_rtc_portc = (m_rtc_portc & ~(1 << 2)) | ((state & 1) << 2); }
	void rtc_portc_3_w(int state) { m_rtc_portc = (m_rtc_portc & ~(1 << 3)) | ((state & 1) << 3); }
	uint8_t m_rtc_portc;
	void pc100_io(address_map &map) ATTR_COLD;
	void pc100_map(address_map &map) ATTR_COLD;
};

void pc100_state::video_start()
{
}

uint32_t pc100_state::screen_update_pc100(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	int count = (m_crtc.vstart + 0x20) * 0x40;

	for(int y=0;y<512;y++)
	{
		count &= 0xffff;

		for(int x=0;x<1024/16;x++)
		{
			for(int xi=0;xi<16;xi++)
			{
				if(m_crtc.cmd != 0xffff)
				{
					int pen[4];
					for(int pen_i=0;pen_i<4;pen_i++)
						pen[pen_i] = (m_vram[count+pen_i*0x10000] >> xi) & 1;

					int dot = 0;
					for(int pen_i=0;pen_i<4;pen_i++)
						dot |= pen[pen_i]<<pen_i;

					if(y < 512 && x*16+xi < 768) /* TODO: safety check */
						bitmap.pix(y, x*16+xi) = m_palette->pen(dot);
				}
				else
				{
					int dot = (m_vram[count] >> xi) & 1;

					if(y < 512 && x*16+xi < 768) /* TODO: safety check */
						bitmap.pix(y, x*16+xi) = m_palette->pen(dot ? 15 : 0);
				}
			}

			count++;
		}
	}

	return 0;
}

void pc100_state::irqnmi_w(int state)
{
	if(!m_nmi_mask)
		m_maincpu->set_input_line(INPUT_LINE_NMI, ((state == ASSERT_LINE) || m_drq_state) ? ASSERT_LINE : CLEAR_LINE);
	m_irq_state = state == ASSERT_LINE;
}

void pc100_state::drqnmi_w(int state)
{
	if(!m_nmi_mask)
		m_maincpu->set_input_line(INPUT_LINE_NMI, ((state == ASSERT_LINE) || m_irq_state) ? ASSERT_LINE : CLEAR_LINE);
	m_drq_state = state == ASSERT_LINE;
}

uint16_t pc100_state::pc100_vram_r(offs_t offset)
{
	return m_vram[offset+m_bank_r*0x10000];
}

void pc100_state::pc100_vram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	uint16_t old_vram;
	for(int i=0;i<4;i++)
	{
		if((m_bank_w >> i) & 1)
		{
			old_vram = m_vram[offset+i*0x10000];
			COMBINE_DATA(&m_vram[offset+i*0x10000]);
			m_vram[offset+i*0x10000] = ((m_vram[offset+i*0x10000]|(m_vram[offset+i*0x10000]<<16)) >> m_crtc.shift);
			if(ACCESSING_BITS_0_15)
				m_vram[offset+i*0x10000] = (m_vram[offset+i*0x10000] & ~m_crtc.mask) | (old_vram & m_crtc.mask);
			else if(ACCESSING_BITS_8_15)
				m_vram[offset+i*0x10000] = (m_vram[offset+i*0x10000] & ((~m_crtc.mask) & 0xff00)) | (old_vram & (m_crtc.mask|0xff));
			else if(ACCESSING_BITS_0_7)
				m_vram[offset+i*0x10000] = (m_vram[offset+i*0x10000] & ((~m_crtc.mask) & 0xff)) | (old_vram & (m_crtc.mask|0xff00));

		}
	}
}

void pc100_state::pc100_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0xbffff).ram(); // work ram
	map(0xc0000, 0xdffff).rw(FUNC(pc100_state::pc100_vram_r), FUNC(pc100_state::pc100_vram_w)); // vram, blitter based!
	map(0xf8000, 0xfffff).rom().region("ipl", 0);
}

uint16_t pc100_state::pc100_kanji_r()
{
	return m_kanji_rom[m_kanji_addr];
}


void pc100_state::pc100_kanji_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	COMBINE_DATA(&m_kanji_addr);
}

uint8_t pc100_state::pc100_key_r(offs_t offset)
{
	if(offset)
		return ioport("DSW")->read(); // bit 5: horizontal/vertical monitor dsw
	else
	{
		m_pic->ir3_w(0);
		return m_key;
	}

	return 0;
}

void pc100_state::pc100_output_w(offs_t offset, uint8_t data)
{
	if(offset == 0)
	{
		m_timer_mode = (data & 0x18) >> 3;
		m_beeper->set_state(((data & 0x40) >> 6) ^ 1);
		//printf("%02x\n",data & 0xc0);
	}
}

void pc100_state::pc100_tc_w(uint8_t data)
{
	m_fdc->tc_w(data & 0x40);
}

uint8_t pc100_state::pc100_shift_r()
{
	return m_crtc.shift;
}

void pc100_state::pc100_shift_w(uint8_t data)
{
	m_crtc.shift = data & 0xf;
}

uint8_t pc100_state::pc100_vs_vreg_r(offs_t offset)
{
	if(offset)
		return m_crtc.vstart >> 8;

	return m_crtc.vstart & 0xff;
}

void pc100_state::pc100_vs_vreg_w(offs_t offset, uint8_t data)
{
	if(offset)
		m_crtc.vstart = (m_crtc.vstart & 0xff) | (data << 8);
	else
		m_crtc.vstart = (m_crtc.vstart & 0xff00) | (data & 0xff);
}

void pc100_state::pc100_crtc_addr_w(uint8_t data)
{
	m_crtc.addr = data & 7;
}

void pc100_state::pc100_crtc_data_w(uint8_t data)
{
	m_crtc.reg[m_crtc.addr] = data;
	//printf("%02x %02x\n",m_crtc.addr,data);
}


/* everything is 8-bit bus wide */
void pc100_state::pc100_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff); // i8259
//  map(0x04, 0x07) i8237?
	map(0x08, 0x0b).m(m_fdc, FUNC(upd765a_device::map)).umask16(0x00ff); // upd765
	map(0x10, 0x17).rw("ppi8255_1", FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff); // i8255 #1
	map(0x18, 0x1f).rw("ppi8255_2", FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff); // i8255 #2
	map(0x20, 0x23).r(FUNC(pc100_state::pc100_key_r)).umask16(0x00ff); //i/o, keyboard, mouse
	map(0x22, 0x22).w(FUNC(pc100_state::pc100_output_w)); //i/o, keyboard, mouse
	map(0x24, 0x24).w(FUNC(pc100_state::pc100_tc_w)); //i/o, keyboard, mouse
	map(0x28, 0x2b).rw("uart8251", FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0x30, 0x30).rw(FUNC(pc100_state::pc100_shift_r), FUNC(pc100_state::pc100_shift_w)); // crtc shift
	map(0x38, 0x38).w(FUNC(pc100_state::pc100_crtc_addr_w)); //crtc address reg
	map(0x3a, 0x3a).w(FUNC(pc100_state::pc100_crtc_data_w)); //crtc data reg
	map(0x3c, 0x3f).rw(FUNC(pc100_state::pc100_vs_vreg_r), FUNC(pc100_state::pc100_vs_vreg_w)).umask16(0x00ff); //crtc vertical start position
	map(0x40, 0x5f).ram().w(m_palette, FUNC(palette_device::write16)).share("palette");
	map(0x60, 0x61).lrw16(
			NAME([this] () { return m_crtc.cmd; }),
			NAME([this] (u16 d) { m_crtc.cmd = d; }));
	map(0x80, 0x81).rw(FUNC(pc100_state::pc100_kanji_r), FUNC(pc100_state::pc100_kanji_w));
	map(0x82, 0x83).nopw(); //kanji-related?
	map(0x84, 0x87).nopw(); //kanji "strobe" signal 0/1
}

INPUT_CHANGED_MEMBER(pc100_state::key_stroke)
{
	if(newval != oldval)
	{
		m_key = uint8_t(param & 0xff);
		if(!((newval ^ oldval) & newval))
			m_key |= 0x80;
		m_pic->ir3_w(1);
	}
}

/* Input ports */
static INPUT_PORTS_START( pc100 )
	PORT_START("ROW.0")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x00)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x01)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x02)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x03)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x04)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x05)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x06)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x07)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x08)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x09)
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x0a)
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x0b)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x0c)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x0d)
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x0e)
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x0f)

	PORT_START("ROW.1")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x10)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x11)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x12)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x13)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x14)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x15)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x16)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x17)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x18)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x19)
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x1a)
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x1b)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x1c)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x1d)
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x1e)
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x1f)

	PORT_START("ROW.2")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x20)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x21)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x22)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x23)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('^') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x24)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("\xEF\xBF\xA5") PORT_CHAR(165) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x25) //Yen symbol
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x26)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x27)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x28)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x29)
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('@') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x2a)
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x2b)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x2c)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x2d)
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x2e)
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x2f)

	PORT_START("ROW.3")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x30)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x31)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x32)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x33)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x34)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x35)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x36)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR('[') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x37)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x38)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x39)
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x3a)
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x3b)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x3c)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(':') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x3d)
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHAR(']') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x3e)
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x3f)

	PORT_START("ROW.4")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x40)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ROW.4 0x41") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x41)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ROW.4 0x42") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x42)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x43)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x44)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x45)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x46)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x47)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ROW.4 0x48") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x48)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ROW.4 0x49") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x49)
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x4a)
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x4b)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ROW.4 0x4c") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x4c)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ROW.4 0x4d") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x4d)
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ROW.4 0x4e") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x4e)
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x4f)

	PORT_START("ROW.5")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x50)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x51)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x52)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x53)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x54)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x55)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x56)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x57)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x58)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(ASTERISK)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x59)
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x5a)
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ROW.5 0x5b") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x5b)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ROW.5 0x5c") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x5c)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ROW.5 0x5d") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x5d)
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ROW.5 0x5e") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x5e)
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ROW.5 0x5f") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc100_state::key_stroke), 0x5f)

	PORT_START("DSW")
	PORT_DIPNAME( 0x01, 0x01, "DSW" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, "Monitor" )
	PORT_DIPSETTING(    0x20, "Yoko Mode (Horizontal)" )
	PORT_DIPSETTING(    0x00, "Tate Mode (Vertical)" )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

static const gfx_layout kanji_layout =
{
	16, 16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 7,6,5,4,3,2,1,0,15,14,13,12,11,10,9,8 },
	{ STEP16(0,16) },
	16*16
};

static GFXDECODE_START( gfx_pc100 )
	GFXDECODE_ENTRY( "kanji", 0x0000, kanji_layout, 8, 1 )
GFXDECODE_END

/* TODO: untested */
void pc100_state::rtc_porta_w(uint8_t data)
{
/*
    ---- -x-- chip select
    ---- --x- read
    ---- ---x write
*/

	if(data != 0) // work around 8255 behavior that resets the whole chip on mode write
		m_fdc->subdevice<floppy_connector>("0")->get_device()->mon_w(!(data & 0x20));
	m_rtc->write_w((data >> 0) & 1);
	m_rtc->read_w((data >> 1) & 1);
	m_rtc->cs1_w((data >> 2) & 1);
}

void pc100_state::rtc_portc_w(uint8_t data)
{
	m_rtc->d0_w((data >> 0) & 1);
	m_rtc->d1_w((data >> 1) & 1);
	m_rtc->d2_w((data >> 2) & 1);
	m_rtc->d3_w((data >> 3) & 1);
}

uint8_t pc100_state::rtc_portc_r()
{
	return m_rtc_portc;
}

void pc100_state::lower_mask_w(uint8_t data)
{
	m_crtc.mask = (m_crtc.mask & 0xff00) | data;
}

void pc100_state::upper_mask_w(uint8_t data)
{
	m_crtc.mask = (m_crtc.mask & 0xff) | (data << 8);
}

void pc100_state::crtc_bank_w(uint8_t data)
{
	if(data & 0x80)
	{
		m_nmi_mask = false;
		m_maincpu->set_input_line(INPUT_LINE_NMI, (m_irq_state || m_drq_state) ? ASSERT_LINE : CLEAR_LINE);
	}
	else
	{
		m_nmi_mask = true;
		m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
	}
	m_bank_w = data & 0xf;
	m_bank_r = (data & 0x30) >> 4;
}

void pc100_state::machine_start()
{
	m_nmi_mask = true;
	m_irq_state = false;
	m_drq_state = false;
}

void pc100_state::machine_reset()
{
	m_beeper->set_state(0);
	m_key = 0xf0;
}

INTERRUPT_GEN_MEMBER(pc100_state::pc100_vblank_irq)
{
	m_pic->ir4_w(0);
	m_pic->ir4_w(1);
}

TIMER_DEVICE_CALLBACK_MEMBER(pc100_state::pc100_600hz_irq)
{
	if(m_timer_mode == 0)
	{
		m_pic->ir2_w(0);
		m_pic->ir2_w(1);
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(pc100_state::pc100_100hz_irq)
{
	if(m_timer_mode == 1)
	{
		m_pic->ir2_w(0);
		m_pic->ir2_w(1);
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(pc100_state::pc100_50hz_irq)
{
	if(m_timer_mode == 2)
	{
		m_pic->ir2_w(0);
		m_pic->ir2_w(1);
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(pc100_state::pc100_10hz_irq)
{
	if(m_timer_mode == 3)
	{
		m_pic->ir2_w(0);
		m_pic->ir2_w(1);
	}
}

static void pc100_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
	device.option_add("525sd", FLOPPY_525_SD);
}

#define MASTER_CLOCK 6988800

void pc100_state::pc100(machine_config &config)
{
	/* basic machine hardware */
	I8086(config, m_maincpu, MASTER_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc100_state::pc100_map);
	m_maincpu->set_addrmap(AS_IO, &pc100_state::pc100_io);
	m_maincpu->set_vblank_int("screen", FUNC(pc100_state::pc100_vblank_irq));
	m_maincpu->set_irq_acknowledge_callback("pic8259", FUNC(pic8259_device::inta_cb));

	TIMER(config, "600hz").configure_periodic(FUNC(pc100_state::pc100_600hz_irq), attotime::from_hz(MASTER_CLOCK/600));
	TIMER(config, "100hz").configure_periodic(FUNC(pc100_state::pc100_100hz_irq), attotime::from_hz(MASTER_CLOCK/100));
	TIMER(config, "50hz").configure_periodic(FUNC(pc100_state::pc100_50hz_irq), attotime::from_hz(MASTER_CLOCK/50));
	TIMER(config, "10hz").configure_periodic(FUNC(pc100_state::pc100_10hz_irq), attotime::from_hz(MASTER_CLOCK/10));

	i8255_device &ppi1(I8255(config, "ppi8255_1"));
	ppi1.out_pa_callback().set(FUNC(pc100_state::rtc_porta_w));
	ppi1.in_pc_callback().set(FUNC(pc100_state::rtc_portc_r));
	ppi1.out_pc_callback().set(FUNC(pc100_state::rtc_portc_w));

	i8255_device &ppi2(I8255(config, "ppi8255_2"));
	ppi2.out_pa_callback().set(FUNC(pc100_state::lower_mask_w));
	ppi2.out_pb_callback().set(FUNC(pc100_state::upper_mask_w));
	ppi2.out_pc_callback().set(FUNC(pc100_state::crtc_bank_w));

	PIC8259(config, m_pic, 0);
	m_pic->out_int_callback().set_inputline(m_maincpu, 0);
	m_pic->in_sp_callback().set_constant(0); // ???

	i8251_device &i8251(I8251(config, "uart8251", 0));
	//i8251.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	//i8251.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	//i8251.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	i8251.rxrdy_handler().set(m_pic, FUNC(pic8259_device::ir1_w));

	UPD765A(config, m_fdc, 8'000'000, true, true);
	m_fdc->intrq_wr_callback().set(FUNC(pc100_state::irqnmi_w));
	m_fdc->drq_wr_callback().set(FUNC(pc100_state::drqnmi_w));

	MSM58321(config, m_rtc, XTAL(32'768));
	m_rtc->d0_handler().set(FUNC(pc100_state::rtc_portc_0_w));
	m_rtc->d1_handler().set(FUNC(pc100_state::rtc_portc_1_w));
	m_rtc->d2_handler().set(FUNC(pc100_state::rtc_portc_2_w));
	m_rtc->d3_handler().set(FUNC(pc100_state::rtc_portc_3_w));

	FLOPPY_CONNECTOR(config, "fdc:0", pc100_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", pc100_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:2", pc100_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:3", pc100_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	/* TODO: Unknown Pixel Clock and CRTC is dynamic */
	screen.set_raw(MASTER_CLOCK*4, 1024, 0, 768, 264*2, 0, 512);
	screen.set_screen_update(FUNC(pc100_state::screen_update_pc100));
	screen.set_palette(m_palette);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_pc100);
	PALETTE(config, m_palette).set_format(palette_device::xBGR_333, 16);

	SPEAKER(config, "mono").front_center();

	BEEP(config, m_beeper, 2400).add_route(ALL_OUTPUTS, "mono", 0.50);

	SOFTWARE_LIST(config, "disk_list").set_original("pc100_flop");
}

/* ROM definition */
ROM_START( pc100 )
	ROM_REGION16_LE( 0x8000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "ipl.rom", 0x0000, 0x8000, CRC(fd54a80e) SHA1(605a1b598e623ba2908a14a82454b9d32ea3c331))

	ROM_REGION16_LE( 0x20000, "kanji", ROMREGION_ERASEFF )
	ROM_LOAD( "kanji.rom", 0x0000, 0x20000, BAD_DUMP CRC(29298591) SHA1(d10174553ceea556fc53fc4e685d939524a4f64b))

	ROM_REGION16_LE( 0x20000*4, "vram", ROMREGION_ERASEFF )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME  FLAGS
COMP( 198?, pc100, 0,      0,      pc100,   pc100, pc100_state, empty_init, "NEC",   "PC-100", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



pc1000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

        VTech PreComputer 1000

****************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "video/sed1200.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"


namespace {

class pc1000_state : public driver_device
{
public:
	pc1000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "sed1200")
		, m_screen(*this, "screen")
		, m_cart(*this, "cartslot")
		, m_bank0(*this, "bank0")
		, m_bank1(*this, "bank1")
		, m_in(*this, "IN%u", 0U)
	{ }

	void misterx(machine_config &config);
	void pc1000(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint8_t kb_r(offs_t offset);
	void lcdc_data_w(uint8_t data);
	uint8_t lcdc_busy_r();
	void lcdc_control_w(uint8_t data);
	void lcdc_deselect_w(uint8_t data);
	void rombank_w(uint8_t data);
	void output_w(uint8_t data);

	uint32_t sed1200_screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void pc1000_palette(palette_device &palette) const;
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	void pc1000_mem(address_map &map) ATTR_COLD;
	void pc1000_io(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<sed1200_device> m_lcdc;
	required_device<screen_device> m_screen;
	required_device<generic_slot_device> m_cart;
	required_memory_bank m_bank0;
	required_memory_bank m_bank1;
	required_ioport_array<9> m_in;
};

uint8_t pc1000_state::kb_r(offs_t offset)
{
	uint8_t data = 0xff;

	for (int line=0; line<9; line++)
		if (!(offset & (1<<line)))
			data &= m_in[line]->read();

	return data;
}

void pc1000_state::lcdc_data_w(uint8_t data)
{
	m_lcdc->cs_w(0);
	m_lcdc->data_w(data);
}

uint8_t pc1000_state::lcdc_busy_r()
{
	return m_lcdc->busy_r();
}

void pc1000_state::lcdc_control_w(uint8_t data)
{
	m_lcdc->cs_w(0);
	m_lcdc->control_w(data);
}

void pc1000_state::lcdc_deselect_w(uint8_t data)
{
	m_lcdc->cs_w(1);
}

void pc1000_state::rombank_w(uint8_t data)
{
	//logerror("%s: New bank = %02X\n", machine().describe_context(), data);
	m_bank0->set_entry(BIT(data, 0));
	m_bank1->set_entry(BIT(data, 1, 3));
}

void pc1000_state::output_w(uint8_t data)
{
	//logerror("%s: Output = %02X\n", machine().describe_context(), data);
}

uint32_t pc1000_state::sed1200_screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	const uint8_t *render_buf = m_lcdc->render();

	bitmap.fill(0);
	for (int i=0; i<20; i++)
		for (int y=0; y<8; y++)
			for (int x=0; x<5; x++)
				bitmap.pix(y, (i * 6) + x) = BIT(render_buf[(i * 8) + y], 4 - x);

	return 0;
}

void pc1000_state::pc1000_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).bankr("bank0");
	map(0x4000, 0x47ff).ram();
	map(0x8000, 0xffff).bankr("bank1");
}

void pc1000_state::pc1000_io(address_map &map)
{
	map(0x0000, 0x01ff).r(FUNC(pc1000_state::kb_r));
	map(0x4000, 0x4000).mirror(0xfe).rw(FUNC(pc1000_state::lcdc_busy_r), FUNC(pc1000_state::lcdc_control_w));
	map(0x4001, 0x4001).mirror(0xfe).w(FUNC(pc1000_state::lcdc_deselect_w));
	map(0x4100, 0x4100).mirror(0xfe).w(FUNC(pc1000_state::lcdc_data_w));
	map(0x8000, 0x8000).mirror(0xff).w(FUNC(pc1000_state::rombank_w));
	map(0xc000, 0xc000).mirror(0xff).w(FUNC(pc1000_state::output_w));
}

/* Input ports */
static INPUT_PORTS_START( pc1000 )
	PORT_START("IN0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Typing Course") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Computer Exercise") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("General Knowledge") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Math")       PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("History")    PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Geography")  PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Science")    PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Games")      PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))

	PORT_START("IN1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cartridge")  PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Off")        PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2190")   PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2192")   PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter")      PORT_CODE(KEYCODE_ENTER) PORT_CHAR(0x0d)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right Player  Del") PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Answer")     PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Calculator") PORT_CODE(KEYCODE_F9)

	PORT_START("IN2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Repeat  ^")  PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('^')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P")  PORT_CODE(KEYCODE_P) PORT_CHAR('P')

	PORT_START("IN3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O  [M-]")  PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K")  PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L")  PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I  [M+]")  PORT_CODE(KEYCODE_I) PORT_CHAR('I')

	PORT_START("IN4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('-')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U  [MR]")  PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H")  PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J")  PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N")  PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M")  PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y  [M-in]")  PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')

	PORT_START("IN5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"T  [÷]")  PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F")  PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G")  PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V")  PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B")  PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"R  [×]")  PORT_CODE(KEYCODE_R) PORT_CHAR('R')

	PORT_START("IN6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E  [-]")  PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S")  PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D")  PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X")  PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C")  PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W  [+]")  PORT_CODE(KEYCODE_W) PORT_CHAR('W')

	PORT_START("IN7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Lock  Break")  PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q  [=]")  PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A")  PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z")  PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Erase  [AC]") PORT_CODE(KEYCODE_LALT) PORT_CHAR(0x1b)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left Player  Ins") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START("IN8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Help") PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Multiple Choice") PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0xfc, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( misterx )
	PORT_START("IN0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Schreibmaschinenkurs") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Computer-Übungen") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Allgemeinwissen") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Mathe")      PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Geschichte") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Geographie") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Naturwissenschaften") PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Spiele")     PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))

	PORT_START("IN1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Kassette")   PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Aus")        PORT_CODE(KEYCODE_F12) PORT_CHAR(UCHAR_MAMEKEY(F12))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2190  <") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CHAR('<')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2192  >") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR('>')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Eingabe")    PORT_CODE(KEYCODE_ENTER) PORT_CHAR(0x0d)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Spieler Rechts  Del") PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Antwort  +") PORT_CODE(KEYCODE_RALT) PORT_CHAR('+')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Elektronik-Rechner")   PORT_CODE(KEYCODE_F9)

	PORT_START("IN2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('=')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Wiederholen  ^")  PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('^')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ö")  PORT_CODE(KEYCODE_COLON) PORT_CHAR(0x00d6)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ä")  PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(0x00d4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ü")  PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(0x00dc)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Umschalter") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P")  PORT_CODE(KEYCODE_P) PORT_CHAR('P')

	PORT_START("IN3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O  [M-]")  PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K")  PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L")  PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR(';')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(':')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I  [M+]")  PORT_CODE(KEYCODE_I) PORT_CHAR('I')

	PORT_START("IN4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('?')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('/')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U  [MR]")  PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H")  PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J")  PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N")  PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M")  PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z  [M-in]")  PORT_CODE(KEYCODE_Y) PORT_CHAR('Z')

	PORT_START("IN5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('\'')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"T  [÷]")  PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F")  PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G")  PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V")  PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B")  PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"R  [×]")  PORT_CODE(KEYCODE_R) PORT_CHAR('R')

	PORT_START("IN6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR(0x00df)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E  [-]")  PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S")  PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D")  PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X")  PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C")  PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W  [+]")  PORT_CODE(KEYCODE_W) PORT_CHAR('W')

	PORT_START("IN7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Lock  Break")  PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q  [=]")  PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A")  PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y")  PORT_CODE(KEYCODE_Z) PORT_CHAR('Y')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Löschen  [AC]") PORT_CODE(KEYCODE_LALT) PORT_CHAR(0x1b)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Spieler Links  Ins") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START("IN8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Hilfe  #") PORT_CODE(KEYCODE_TAB) PORT_CHAR('#')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Multiple Choice  *") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('*')
	PORT_BIT(0xfc, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( ordisava )
	PORT_START("IN0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cours Dactylo") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BASIC")      PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Connaissances Generales") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Mathematiques") PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Histoire")   PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Geographie") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Sciences")   PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Jeux")       PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))

	PORT_START("IN1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cartouche")  PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Arret")      PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2190  <") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CHAR('<')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2192  >") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR('>')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter")      PORT_CODE(KEYCODE_ENTER) PORT_CHAR(0x0d)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Joueur Droite  Del") PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Reponse  $") PORT_CODE(KEYCODE_RALT) PORT_CHAR('$')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Calculatrice") PORT_CODE(KEYCODE_F9)

	PORT_START("IN2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(0x00e0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Repete  ^")  PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('^')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"^  ¨")  PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M")  PORT_CODE(KEYCODE_COLON) PORT_CHAR('M')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(0x00f9) PORT_CHAR('!')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P")  PORT_CODE(KEYCODE_P) PORT_CHAR('P')

	PORT_START("IN3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('_')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(0x00e7)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O  [M-]")  PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K")  PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L")  PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(';') PORT_CHAR('.')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR(':') PORT_CHAR('/')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I  [M+]")  PORT_CODE(KEYCODE_I) PORT_CHAR('I')

	PORT_START("IN4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('-')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR(0x00e8)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U  [MR]")  PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H")  PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J")  PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N")  PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR(',') PORT_CHAR('?')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y  [M-in]")  PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')

	PORT_START("IN5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('\'')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"T  [÷]")  PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F")  PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G")  PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V")  PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B")  PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"R  [×]")  PORT_CODE(KEYCODE_R) PORT_CHAR('R')

	PORT_START("IN6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR(0x00e9)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('"')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E  [-]")  PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S  [)]")  PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D  [']")  PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X")  PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C")  PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z  [+]")  PORT_CODE(KEYCODE_W) PORT_CHAR('Z')

	PORT_START("IN7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Lock  Break")  PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('(')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A  [=]")  PORT_CODE(KEYCODE_Q) PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q  [(]")  PORT_CODE(KEYCODE_A) PORT_CHAR('Q')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W")  PORT_CODE(KEYCODE_Z) PORT_CHAR('W')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Erase  [AC]") PORT_CODE(KEYCODE_LALT) PORT_CHAR(0x1b)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Joueur Gauche  Ins") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START("IN8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Aide  #") PORT_CODE(KEYCODE_TAB) PORT_CHAR('#')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Choix Multiple  *") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('*')
	PORT_BIT(0xfc, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

void pc1000_state::machine_start()
{
	uint8_t *bios = memregion("bios")->base();
	memory_region *cart_region = memregion(std::string(m_cart->tag()) + GENERIC_ROM_REGION_TAG);
	uint8_t *cart = (cart_region != nullptr) ? cart_region->base() : memregion("bios")->base();

	m_bank0->configure_entries(0, 2, bios, 0x4000);
	m_bank1->configure_entries(0, 4, bios, 0x8000);
	m_bank1->configure_entries(4, 4, cart, 0x8000);
}

void pc1000_state::machine_reset()
{
	m_bank0->set_entry(0);
	m_bank1->set_entry(0);
}

void pc1000_state::pc1000_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}

DEVICE_IMAGE_LOAD_MEMBER( pc1000_state::cart_load )
{
	uint32_t const size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(0x20000, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

void pc1000_state::misterx(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'000'000)); /* probably not accurate */
	m_maincpu->set_addrmap(AS_PROGRAM, &pc1000_state::pc1000_mem);
	m_maincpu->set_addrmap(AS_IO, &pc1000_state::pc1000_io);
	m_maincpu->set_periodic_int(FUNC(pc1000_state::irq0_line_hold), attotime::from_hz(10));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_screen_update(FUNC(pc1000_state::sed1200_screen_update));
	m_screen->set_size(120, 9); //1x20 chars
	m_screen->set_visarea(0, 120-1, 0, 9-1);
	m_screen->set_palette("palette");

	SED1200D0B(config, m_lcdc, 0); // packaging variant not verified

	PALETTE(config, "palette", FUNC(pc1000_state::pc1000_palette), 2);

	/* sound hardware (TODO?) */

	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "genius_cart").set_device_load(FUNC(pc1000_state::cart_load));

	/* Software lists */
	SOFTWARE_LIST(config, "misterx_cart").set_original("misterx");
	SOFTWARE_LIST(config, "pc1000_cart").set_compatible("pc1000");
}

void pc1000_state::pc1000(machine_config &config)
{
	misterx(config);

	subdevice<software_list_device>("pc1000_cart")->set_original("pc1000");
	subdevice<software_list_device>("misterx_cart")->set_compatible("misterx");
}

/* ROM definition */
ROM_START( pc1000 )
	ROM_REGION( 0x20000, "bios", 0 )
	ROM_LOAD( "27-00780-002-002.u4", 0x000000, 0x020000, CRC(705170ae) SHA1(825ce0ff2c7d0a7b1e2577d1465a37f7e8da383b))
ROM_END

ROM_START( misterx )
	ROM_REGION( 0x20000, "bios", 0 )
	ROM_LOAD( "27-00882-001.bin", 0x000000, 0x020000, CRC(30e0dc94) SHA1(2f4675746a41399b3d9e3e8001a9b4a0dcc5b620))
ROM_END

ROM_START( ordisava )
	ROM_REGION( 0x20000, "bios", 0 )
	ROM_LOAD( "27-00874-001.u4", 0x000000, 0x020000, CRC(5e40764e) SHA1(636ea61d3d675e51c20f610aae6824369c01a804))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT  COMPAT MACHINE    INPUT     CLASS         INIT        COMPANY                    FULLNAME                                 FLAGS
COMP( 1988, pc1000,   0,      0,     pc1000,    pc1000,   pc1000_state, empty_init, "Video Technology",        "PreComputer 1000",                      MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1988, misterx,  0,      0,     misterx,   misterx,  pc1000_state, empty_init, "Video Technology / Yeno", "MisterX",                               MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1988, ordisava, 0,      0,     pc1000,    ordisava, pc1000_state, empty_init, "Video Technology / Yeno", "Ordisavant (France)",                   MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



pc1500.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

        Sharp pocket computers 1500

        Driver by Sandro Ronco

        More info:
            http://www.pc1500.com/

****************************************************************************/


#include "emu.h"
#include "cpu/lh5801/lh5801.h"
#include "machine/lh5810.h"
#include "machine/upd1990a.h"
#include "emupal.h"
#include "screen.h"

#include "pc1500.lh"


namespace {

class pc1500_state : public driver_device
{
public:
	pc1500_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rtc(*this, "upd1990a")
		, m_lcd_data(*this, "lcd_data")
		, m_keyboard(*this, "KEY.%u", 0)
		, m_io_on(*this, "ON")
		, m_busy(*this, "BUSY")
		, m_shift(*this, "SHIFT")
		, m_sml(*this, "SML")
		, m_small(*this, "SMALL")
		, m_iii(*this, "III")
		, m_ii(*this, "II")
		, m_i(*this, "I")
		, m_def(*this, "DEF")
		, m_de(*this, "DE")
		, m_g(*this, "G")
		, m_rad(*this, "RAD")
		, m_reserve(*this, "RESERVE")
		, m_pro(*this, "PRO")
		, m_run(*this, "RUN")
	{
	}

	void pc1500(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<lh5801_cpu_device> m_maincpu;
	required_device<upd1990a_device> m_rtc;

	required_shared_ptr<uint8_t> m_lcd_data;
	required_ioport_array<8> m_keyboard;
	required_ioport m_io_on;

	output_finder<> m_busy;
	output_finder<> m_shift;
	output_finder<> m_sml;
	output_finder<> m_small;
	output_finder<> m_iii;
	output_finder<> m_ii;
	output_finder<> m_i;
	output_finder<> m_def;
	output_finder<> m_de;
	output_finder<> m_g;
	output_finder<> m_rad;
	output_finder<> m_reserve;
	output_finder<> m_pro;
	output_finder<> m_run;

	uint8_t m_kb_matrix;

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void kb_matrix_w(uint8_t data);
	uint8_t port_a_r();
	uint8_t port_b_r();
	void port_c_w(uint8_t data);

	uint8_t pc1500_kb_r();
	void pc1500_palette(palette_device &palette) const;
	void pc1500_mem(address_map &map) ATTR_COLD;
	void pc1500_mem_io(address_map &map) ATTR_COLD;
};

void pc1500_state::pc1500_mem(address_map &map)
{
	map.unmap_value_high();
	//  map(0x0000, 0x3fff).rom();    //module ROM/RAM
	map(0x4000, 0x47ff).ram();    //user RAM
	map(0x4800, 0x6fff).ram();    //expansion RAM
	map(0x7000, 0x71ff).ram().mirror(0x0600).share("lcd_data");
	map(0x7800, 0x7bff).ram().mirror(0x0400);
	//  map(0xa000, 0xbfff).rom();    //expansion ROM
	map(0xc000, 0xffff).rom().region("maincpu", 0);    //system ROM
}

void pc1500_state::pc1500_mem_io(address_map &map)
{
	map.unmap_value_high();
	map(0xf000, 0xf00f).rw("lh5810", FUNC(lh5810_device::data_r), FUNC(lh5810_device::data_w));
}

uint8_t pc1500_state::pc1500_kb_r()
{
	uint8_t data = 0xff;

	if (!started()) return 0;

	for (int i = 0; i < 8; i++)
	{
		if (!BIT(m_kb_matrix, i))
			data &= m_keyboard[i]->read();
	}

	return data;
}

uint32_t pc1500_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	bitmap.fill(0, cliprect);

	for (int p=0; p<=1; p++)
		for (int a=0; a<0x4e; a++)
		{
			uint8_t data = m_lcd_data[a + (p<<8)];
			for (int b=0; b<8; b++)
			{
				if(b<4)
					bitmap.pix(b + 4 * (BIT( a, 0)), (a>>1) + 0x00 + 0x27*p) = BIT(data, b);
				else
					bitmap.pix(b - 4 * (BIT(~a, 0)), (a>>1) + 0x4e + 0x27*p) = BIT(data, b);
			}
		}

	m_busy =  BIT(m_lcd_data[0x4e], 0);
	m_shift = BIT(m_lcd_data[0x4e], 1);
	m_sml =   BIT(m_lcd_data[0x4e], 2);
	m_small = BIT(m_lcd_data[0x4e], 3);
	m_iii =   BIT(m_lcd_data[0x4e], 4);
	m_ii =    BIT(m_lcd_data[0x4e], 5);
	m_i =     BIT(m_lcd_data[0x4e], 6);
	m_def =   BIT(m_lcd_data[0x4e], 7);
	m_de =    BIT(m_lcd_data[0x4f], 0);
	m_g =     BIT(m_lcd_data[0x4f], 1);
	m_rad =   BIT(m_lcd_data[0x4f], 2);
	m_reserve = BIT(m_lcd_data[0x4f], 4);
	m_pro =   BIT(m_lcd_data[0x4f], 5);
	m_run =   BIT(m_lcd_data[0x4f], 6);

	return 0;
}

void pc1500_state::machine_start()
{
	m_busy.resolve();
	m_shift.resolve();
	m_sml.resolve();
	m_small.resolve();
	m_iii.resolve();
	m_ii.resolve();
	m_i.resolve();
	m_def.resolve();
	m_de.resolve();
	m_g.resolve();
	m_rad.resolve();
	m_reserve.resolve();
	m_pro.resolve();
	m_run.resolve();
}

void pc1500_state::machine_reset()
{
	m_kb_matrix = 0xff;
	m_rtc->cs_w(1);
}

static INPUT_PORTS_START( pc1500 )

	PORT_START("ON")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10)

	PORT_START("KEY.0")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("UP") PORT_CODE(KEYCODE_UP)

	PORT_START("KEY.1")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("OFF") PORT_CODE(KEYCODE_F11)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_X)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ST") PORT_CODE(KEYCODE_F8)

	PORT_START("KEY.2")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)

	PORT_START("KEY.3")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(")") PORT_CODE(KEYCODE_CLOSEBRACE)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F6") PORT_CODE(KEYCODE_F6)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("(") PORT_CODE(KEYCODE_OPENBRACE)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER)

	PORT_START("KEY.4")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("+") PORT_CODE(KEYCODE_PLUS_PAD)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("*") PORT_CODE(KEYCODE_ASTERISK)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH_PAD)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RCL") PORT_CODE(KEYCODE_F7)

	PORT_START("KEY.5")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_EQUALS)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LEFT") PORT_CODE(KEYCODE_LEFT)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE)

	PORT_START("KEY.6")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RIGHT") PORT_CODE(KEYCODE_RIGHT)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MODE") PORT_CODE(KEYCODE_HOME)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CL") PORT_CODE(KEYCODE_DEL)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DEF") PORT_CODE(KEYCODE_PGUP)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SML") PORT_CODE(KEYCODE_PGDN)

	PORT_START("KEY.7")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DOWN") PORT_CODE(KEYCODE_DOWN)
INPUT_PORTS_END


void pc1500_state::kb_matrix_w(uint8_t data)
{
	m_kb_matrix = data;
}

void pc1500_state::port_c_w(uint8_t data)
{
	m_rtc->data_in_w(BIT(data, 0));
	m_rtc->stb_w(BIT(data, 1));
	m_rtc->clk_w(BIT(data, 2));
	m_rtc->oe_w(BIT(data, 3));
	m_rtc->c0_w(BIT(data, 3));
	m_rtc->c1_w(BIT(data, 4));
	m_rtc->c2_w(BIT(data, 5));
}

uint8_t pc1500_state::port_b_r()
{
	/*
	x--- ---- ON/Break key
	-xx- ---- upd1990ac
	---x ---- GND
	---- x--- Japan: GND, export: VCC
	---- -x-- cassette in
	---- --xx connector
	*/
	uint8_t data = 0;

	data |= 0x08;

	data |= (m_rtc->tp_r()<<5);
	data |= (m_rtc->data_out_r()<<6);
	data |= (m_io_on->read()<<7);

	return data;
}

uint8_t pc1500_state::port_a_r()
{
	return 0xff;
}

void pc1500_state::pc1500_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}

void pc1500_state::pc1500(machine_config &config)
{
	LH5801(config, m_maincpu, 2.6_MHz_XTAL); // 1.3 MHz internally
	m_maincpu->set_addrmap(AS_PROGRAM, &pc1500_state::pc1500_mem);
	m_maincpu->set_addrmap(AS_IO, &pc1500_state::pc1500_mem_io);
	m_maincpu->in_func().set(FUNC(pc1500_state::pc1500_kb_r));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));  // not accurate
	screen.set_screen_update(FUNC(pc1500_state::screen_update));
	screen.set_size(156, 8);
	screen.set_visarea(0, 156-1, 0, 7-1);
	screen.set_palette("palette");

	config.set_default_layout(layout_pc1500);
	PALETTE(config, "palette", FUNC(pc1500_state::pc1500_palette), 2);

	lh5810_device &ioports(LH5810(config, "lh5810"));
	ioports.porta_r().set(FUNC(pc1500_state::port_a_r));
	ioports.porta_w().set(FUNC(pc1500_state::kb_matrix_w));
	ioports.portb_r().set(FUNC(pc1500_state::port_b_r));
	ioports.portc_w().set(FUNC(pc1500_state::port_c_w));
	ioports.out_int().set_inputline("maincpu", LH5801_LINE_MI);

	UPD1990A(config, m_rtc, 32.768_kHz_XTAL);
}


ROM_START( pc1500 )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "sys1500.rom", 0x0000, 0x4000, CRC(d480b50d) SHA1(4bf748ba4d7c2b7cd7da7f3fdefcdd2e4cd41c4e))
	ROM_REGION( 0x2000, "ce150", ROMREGION_ERASEFF )
	ROM_LOAD( "ce-150.rom", 0x0000, 0x2000, CRC(8fa1df6d) SHA1(a3aa02a641a46c27c0d4c0dc025b0dbe9b5b79c8))
ROM_END

} // anonymous namespace


//    YEAR  NAME    PARENT  COMPAT  MACHINE INPUT   CLASS         INIT        COMPANY  FULLNAME                FLAGS
COMP( 198?, pc1500, 0,      0,      pc1500, pc1500, pc1500_state, empty_init, "Sharp", "Pocket Computer 1500", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



pc1512.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

    Amstrad PC1512

    http://www.seasip.info/AmstradXT
    http://stason.org/TULARC/pc/hard-drives-hdd/tandon/TM262-21MB-5-25-HH-MFM-ST506.html

*/

/*

    TODO:

    - adjust mouse speed
    - pc1512 V3 VDU check fails
    - Amstrad SM2400 internal modem
    - Amstrad RP4 diagnostic ISA card (PC1512)
    - Amstrad RP5-2 diagnostic ISA card (PC1640)
    - 40291/8908 B ROM on PC1640 HD30 controller card

*/

/*
HARD DISC INSTALLATION INSTRUCTIONS

Applies to both ten and twenty megabyte versions.

1. Turn on machine
2. Insert disc 1 (the red disc) and press a key.
3. Type fdisk (RETURN)
4. At each prompt press the RETURN key (another three times).
5. The A> will now appear so now type format c:/s (RETURN).
6. Now push the Y key (RETURN).
7. The hard disc will now begin to format, if you have a 10Mb machine then it will count up to 305
   cylinders. If you have a 20 Mb machine then 610 cylinders will be counted.
8. Once this is done, take out disc 1 and insert disc 5 (the maroon disc).
9. Now type config(RETURN). This procedure will copy all five discs (order 5,1,2,3,4) onto the hard disc.
   Once finished the screen will show some information about the size of the disc and the number of files and
   directories present.
10. To now use the hard disc remove the floppy disc from the drive and store in a safe place with the other
    four discs then restart the computer by pushing Alt Ctrl and Del.
11. After a short while the AMSTRAD PC info will come up and tell you when the machine was last used
    and then after a little longer the screen will clear and will display this message,

F1=DOSPLUS.SYS
F2=DOS.SYS

Select operating system:

If you choose F1 then DOS Plus and GEM will be booted, or if you press F2 then MS-DOS will be booted.

PC1512-HD10: ?
PC1512-HD20: Tandon TM-262 [-chs 615,4,17 -ss 512]
*/

/*
HARD DISC INSTALLATION ON PC1640

PC1640 hard disc comes ready installed with the necessary software. That is discs 1 to 4 that are
supplied ready with the machine.

Howevere in the case of a disc failure it may be necessary to reinstall the supplied software.

This is done in the following way:
1. Put disc one into drive A: and boot up the system
2. Put disc four into drive A: and type CD\SUPPLEME and press return.
3. Type HDFORMAT and press return.
4. Answer YES to the screen prompt.
5. When HDFORMAT is completed remove disc four and replace with disc one.
6. Type CD\ and press return.
7. Type FDISK and press return.
8. Press return key every time you are asked a question.
9. With disc one still in drive A: type FORMAT C:/S and press return.
10. When formatting is finished replace disc one with disc four.
11. Type CD\SUPPLEME and press return.
12. Type CONFIG and press return.

After typing CONFIG the machine will proceed to copy the four system discs to the hard disc.
After copying each disc you will be prompted to insert the next disc.
You do not noeed to know in which order to insert the discs because the machine will tell you which disc is
needed next.

The system is now installed and should be tested by rebooting the machine.

It should be noted that if the hard disc is ok but the software has been corrupted or deleted you can
reinstall the software without reformatting.
This is done by following steps 11 and 12.

PC1640-HD20: Amstrad 40095 (Alps DRMD20A12A), Tandon TM-262 [-chs 615,4,17 -ss 512]
PC1640-HD30: Western Digital 95038 [-chs 615,6,17 -ss 512]
*/

#include "emu.h"
#include "pc1512.h"
#include "bus/rs232/rs232.h"
#include "bus/isa/ega.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


//**************************************************************************
//  SYSTEM STATUS REGISTER
//**************************************************************************

//-------------------------------------------------
//  system_r -
//-------------------------------------------------

uint8_t pc1512_base_state::system_r(offs_t offset)
{
	uint8_t data = 0;

	switch (offset)
	{
	case 0:
		if (BIT(m_port61, 7))
		{
			/*

			    bit     description

			    0       1
			    1       8087 NDP installed
			    2       1
			    3       1
			    4       DDM0
			    5       DDM1
			    6       second floppy disk drive installed
			    7       0

			*/

			data = m_status1;
		}
		else
		{
			data = m_kbd;
			m_kb_bits = 0;
			m_kb->data_w(1);
			m_pic->ir1_w(CLEAR_LINE);
		}
		break;

	case 1:
		data = m_port61;
		break;

	case 2:
		/*

		    bit     description

		    0       RAM0 / RAM4
		    1       RAM1
		    2       RAM2
		    3       RAM3
		    4       undefined
		    5       8253 PIT OUT2 output
		    6       external parity error (I/OCHCK from expansion bus)
		    7       on-board system RAM parity error

		*/

		if (BIT(m_port61, 2))
			data = m_status2 & 0x0f;
		else
			data = m_status2 >> 4;

		data |= m_pit2 << 5;
		break;
	}

	return data;
}


//-------------------------------------------------
//  system_w -
//-------------------------------------------------

void pc1512_base_state::system_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 1:
		/*

		    bit     description

		    0       8253 GATE 2 (speaker modulate)
		    1       speaker drive
		    2       enable port C LSB / disable MSB
		    3       undefined
		    4       disable parity checking of on-board RAM
		    5       prevent external parity errors from causing NMI
		    6       enable incoming Keyboard Clock
		    7       enable Status-1/Disable Keyboard Code on Port A

		*/

		m_port61 = data;

		m_pit->write_gate2(BIT(data, 0));

		m_speaker_drive = BIT(data, 1);
		update_speaker();

		m_kb->clock_w(BIT(data, 6));

		if (BIT(data, 7))
		{
			m_kb_bits = 0;
			m_kb->data_w(1);
			m_pic->ir1_w(CLEAR_LINE);
		}
		break;

	case 4:
		/*

		    bit     description

		    0
		    1       PA1 - 8087 NDP installed
		    2
		    3
		    4       PA4 - DDM0
		    5       PA5 - DDM1
		    6       PA6 - Second Floppy disk drive installed
		    7

		*/

		if (BIT(data, 7))
			m_status1 = data ^ 0x8d;
		else
			m_status1 = data;
		break;

	case 5:
		/*

		    bit     description

		    0       PC0 (LSB) - RAM0
		    1       PC1 (LSB) - RAM1
		    2       PC2 (LSB) - RAM2
		    3       PC3 (LSB) - RAM3
		    4       PC0 (MSB) - RAM4
		    5       PC1 (MSB) - Undefined
		    6       PC2 (MSB) - Undefined
		    7       PC3 (MSB) - Undefined

		*/

		m_status2 = data;
		break;

	case 6:
		machine_reset();
		break;
	}
}



//**************************************************************************
//  MOUSE
//**************************************************************************

//-------------------------------------------------
//  mouse_r -
//-------------------------------------------------

uint8_t pc1512_base_state::mouse_r(offs_t offset)
{
	uint8_t data = 0;

	switch (offset)
	{
	case 0:
		data = m_mouse_x;
		break;

	case 2:
		data = m_mouse_y;
		break;
	}

	return data;
}


//-------------------------------------------------
//  mouse_w -
//-------------------------------------------------

void pc1512_base_state::mouse_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 0:
		m_mouse_x = 0;
		break;

	case 2:
		m_mouse_y = 0;
		break;
	}
}



//**************************************************************************
//  DIRECT MEMORY ACCESS
//**************************************************************************

//-------------------------------------------------
//  dma_page_w -
//-------------------------------------------------

void pc1512_base_state::dma_page_w(offs_t offset, uint8_t data)
{
	/*

	    bit     description

	    0       Address bit A16
	    1       Address bit A17
	    2       Address bit A18
	    3       Address bit A19
	    4
	    5
	    6
	    7

	*/

	switch (offset)
	{
	case 1:
		m_dma_page[2] = data & 0x0f;
		break;

	case 2:
		m_dma_page[3] = data & 0x0f;
		break;

	case 3:
		m_dma_page[0] = m_dma_page[1] = data & 0x0f;
		break;
	}
}



//**************************************************************************
//  INTERRUPTS
//**************************************************************************

//-------------------------------------------------
//  nmi_mask_w -
//-------------------------------------------------

void pc1512_base_state::nmi_mask_w(uint8_t data)
{
	m_nmi_enable = BIT(data, 7);
}



//**************************************************************************
//  PRINTER
//**************************************************************************

//-------------------------------------------------
//  printer_r -
//-------------------------------------------------

uint8_t pc1512_base_state::printer_r(offs_t offset)
{
	uint8_t data = 0;

	switch (offset)
	{
	case 0:
		data = m_printer_data;
		break;

	case 1:
		/*

		    bit     description

		    0       LK1 fitted
		    1       LK2 fitted
		    2       LK3 fitted
		    3       printer error
		    4       printer selected
		    5       paper out
		    6       printer acknowledge
		    7       printer busy

		*/

		data |= m_lk->read() & 0x07;

		data |= m_centronics_fault << 3;
		data |= m_centronics_select << 4;
		data |= m_centronics_perror << 5;
		data |= m_centronics_ack << 6;
		data |= m_centronics_busy << 7;
		break;

	case 2:
		/*

		    bit     description

		    0       Data Strobe
		    1       Select Auto Feed
		    2       Reset Printer
		    3       Select Printer
		    4       Enable Int on ACK
		    5       1
		    6
		    7

		*/

		data = m_printer_control | 0x20;
		break;
	}

	return data;
}


//-------------------------------------------------
//  printer_r -
//-------------------------------------------------

uint8_t pc1640_state::printer_r(offs_t offset)
{
	uint8_t data = 0;

	switch (offset)
	{
	case 2:
		/*

		    bit     description

		    0       Data Strobe
		    1       Select Auto Feed
		    2       Reset Printer
		    3       Select Printer
		    4       Enable Int on ACK
		    5       OPT
		    6       SW6
		    7       SW7

		*/

		/*
		Bit D5 is the option (OPT) bit and can return one of three different pieces of
		information. Although not documented as such on the PC1512, Bit D5 was always a
		"1", however on the PC1640 it will always be a zero if immediately prior to the
		read of channel 037Ah the software performs an I/O read of an I/O channel
		implemented on the PC1512 main board, having address line A7 high (for example,
		the CGA channels). This is a simple test for software to detect whether it is
		running on a PC1512 or a PC1640. A PC1512 will give a 1, whereas a PC1640 will
		give a 0.

		In addition to being a test of machine type the OPT bit, D5, can also reflect
		the state of either SW9 or SW10. The OPT bit will reflect the state of switch
		SW9 by an I/O read operation to an I/O channel not implemented on the main
		board and having address lines A14 and A7 both low (for example channel 0278h)
		immediately prior to the reading of channel 037Ah. The OPT bit is set to the
		state of switch SW10 by an I/O read operation to an I/O channel not implemented
		on the main board having address lines A14 high and A7 low (for example channel
		4278h). Software testing OPT bit should disable interrupts before the initial
		(dummy) channel read and the I/O read of channel 037A in order to avoid
		additional (interrupt based) I/O operations between the setting and the testing
		of the information read back in the OPT bit. For switches SW9 and SW10, a logic
		"1" is returned when the switch is on the "on" position and a logic "0" if the
		switch is in the "off" position.
		*/
		data = m_printer_control;
		data |= m_opt << 5;
		data |= (m_sw->read() & 0x60) << 1;
		break;

	default:
		data = pc1512_base_state::printer_r(offset);
		break;
	}

	return data;
}


//-------------------------------------------------
//  printer_w -
//-------------------------------------------------

void pc1512_base_state::printer_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 0:
		m_printer_data = data;
		m_cent_data_out->write(data);
		break;

	case 2:
		/*

		    bit     description

		    0       Data Strobe
		    1       Select Auto Feed
		    2       Reset Printer
		    3       Select Printer
		    4       Enable Int on ACK
		    5
		    6
		    7

		*/

		m_printer_control = data & 0x1f;

		m_centronics->write_strobe(BIT(data, 0));
		m_centronics->write_autofd(BIT(data, 1));
		m_centronics->write_init(BIT(data, 2));

		m_ack_int_enable = BIT(data, 4);
		update_ack();
		break;
	}
}


//**************************************************************************
//  PC1640 I/O ACCESS
//**************************************************************************

//-------------------------------------------------
//  io_r -
//-------------------------------------------------

uint8_t pc1640_state::io_r(offs_t offset)
{
	uint8_t data = 0;
	offs_t addr = offset & 0x3ff;
	bool decoded = false;

	if      (                 addr <= 0x00f) { data = m_dmac->read(offset & 0x0f); decoded = true; }
	else if (addr >= 0x020 && addr <= 0x021) { data = m_pic->read(offset & 0x01); decoded = true; }
	else if (addr >= 0x040 && addr <= 0x043) { data = m_pit->read(offset & 0x03); decoded = true; }
	else if (addr >= 0x060 && addr <= 0x06f) { data = system_r(offset & 0x0f); decoded = true; }
	else if (addr == 0x071 || addr == 0x073) { data = m_rtc->data_r(); decoded = true; }
	else if (addr >= 0x078 && addr <= 0x07f) { data = mouse_r(offset & 0x07); decoded = true; }
	else if (addr >= 0x378 && addr <= 0x37b) { data = printer_r(offset & 0x03); decoded = true; }
	else if (addr >= 0x3b0 && addr <= 0x3df) { decoded = true; }
	else if (addr >= 0x3f4 && addr <= 0x3f4) { data = m_fdc->msr_r(); decoded = true; }
	else if (addr >= 0x3f5 && addr <= 0x3f5) { data = m_fdc->fifo_r(); decoded = true; }
	else if (addr >= 0x3f8 && addr <= 0x3ff) { data = m_uart->ins8250_r(offset & 0x07); decoded = true; }

	if (decoded)
	{
		if (BIT(offset, 7))
		{
			m_opt = 0;
			//logerror("OPT 0\n");
		}
	}
	else if (!BIT(offset, 7))
	{
		uint16_t sw = m_sw->read();

		if (!BIT(offset, 14))
		{
			m_opt = BIT(sw, 8);
			logerror("OPT SW9 %u\n", m_opt);
		}
		else
		{
			m_opt = BIT(sw, 9);
			logerror("OPT SW10 %u\n", m_opt);
		}
	}

	return data;
}



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( pc1512_mem )
//-------------------------------------------------

void pc1512_state::pc1512_mem(address_map &map)
{
	map(0x00000, 0x9ffff).ram();
	map(0xb8000, 0xbbfff).rw(m_vdu, FUNC(ams40041_device::video_ram_r), FUNC(ams40041_device::video_ram_w));
	map(0xfc000, 0xfffff).rom().region(I8086_TAG, 0);
}


//-------------------------------------------------
//  ADDRESS_MAP( pc1512_io )
//-------------------------------------------------

void pc1512_state::pc1512_io(address_map &map)
{
	// [RH] 29 Aug 2016: I can find no evidence to indicate that Amstrad had only 10 I/O lines, as the
	// schematic calls for a stock 8086 and the I/O and data lines are multiplexed onto the same bus,
	// plus address lines 20-10 are towards the middle of a standard ISA slot. If it turns out that this
	// is not in fact accurate to hardware, please add this back in.
	// map.global_mask(0x3ff);
	map(0x000, 0x00f).rw(m_dmac, FUNC(am9517a_device::read), FUNC(am9517a_device::write));
	map(0x020, 0x021).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x040, 0x043).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x060, 0x06f).rw(FUNC(pc1512_state::system_r), FUNC(pc1512_state::system_w));
	map(0x070, 0x070).mirror(0x02).w(m_rtc, FUNC(mc146818_device::address_w));
	map(0x071, 0x071).mirror(0x02).rw(m_rtc, FUNC(mc146818_device::data_r), FUNC(mc146818_device::data_w));
	map(0x078, 0x07f).rw(FUNC(pc1512_state::mouse_r), FUNC(pc1512_state::mouse_w));
	map(0x080, 0x083).w(FUNC(pc1512_state::dma_page_w));
	map(0x0a1, 0x0a1).w(FUNC(pc1512_state::nmi_mask_w));
	map(0x378, 0x37b).rw(FUNC(pc1512_state::printer_r), FUNC(pc1512_state::printer_w));
	map(0x3d0, 0x3df).rw(m_vdu, FUNC(ams40041_device::vdu_r), FUNC(ams40041_device::vdu_w));
	map(0x3f2, 0x3f2).w(FUNC(pc1512_state::drive_select_w));
	map(0x3f4, 0x3f5).m(m_fdc, FUNC(upd765a_device::map));
	map(0x3f8, 0x3ff).rw(m_uart, FUNC(ins8250_device::ins8250_r), FUNC(ins8250_device::ins8250_w));
}


//-------------------------------------------------
//  ADDRESS_MAP( pc1640_mem )
//-------------------------------------------------

void pc1640_state::pc1640_mem(address_map &map)
{
	map(0x00000, 0x9ffff).ram();
	map(0xf0000, 0xf3fff).mirror(0xc000).rom().region(I8086_TAG, 0);
}


//-------------------------------------------------
//  ADDRESS_MAP( pc1640_io )
//-------------------------------------------------

void pc1640_state::pc1640_io(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(pc1640_state::io_r));

	map(0x000, 0x00f).w(m_dmac, FUNC(am9517a_device::write));
	map(0x020, 0x021).w(m_pic, FUNC(pic8259_device::write));
	map(0x040, 0x043).w(m_pit, FUNC(pit8253_device::write));
	map(0x060, 0x06f).w(FUNC(pc1640_state::system_w));
	map(0x070, 0x070).mirror(0x02).w(m_rtc, FUNC(mc146818_device::address_w));
	map(0x071, 0x071).mirror(0x02).w(m_rtc, FUNC(mc146818_device::data_w));
	map(0x078, 0x07f).w(FUNC(pc1640_state::mouse_w));
	map(0x080, 0x083).w(FUNC(pc1640_state::dma_page_w));
	map(0x0a1, 0x0a1).w(FUNC(pc1640_state::nmi_mask_w));
	map(0x378, 0x37b).w(FUNC(pc1640_state::printer_w));
	map(0x3f2, 0x3f2).w(FUNC(pc1640_state::drive_select_w));
	map(0x3f5, 0x3f5).w(m_fdc, FUNC(upd765a_device::fifo_w));
	map(0x3f8, 0x3ff).w(m_uart, FUNC(ins8250_device::ins8250_w));
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( pc1512 )
//-------------------------------------------------

static INPUT_PORTS_START( pc1512 )
	PORT_START("LK")
	PORT_DIPNAME( 0x07, 0x07, DEF_STR( Language ) )
	PORT_DIPSETTING(    0x07, DEF_STR( English ) )
	PORT_DIPSETTING(    0x06, DEF_STR( German ) )
	PORT_DIPSETTING(    0x05, DEF_STR( French ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Spanish ) )
	PORT_DIPSETTING(    0x03, "Danish" )
	PORT_DIPSETTING(    0x02, "Swedish" )
	PORT_DIPSETTING(    0x01, DEF_STR( Italian ) )
	PORT_DIPSETTING(    0x00, "Diagnostic Mode" )
	PORT_DIPNAME( 0x08, 0x08, "Memory Size")
	PORT_DIPSETTING( 0x08, "512 KB" )
	PORT_DIPSETTING( 0x00, "640 KB" )
	PORT_DIPNAME( 0x10, 0x10, "ROM Size")
	PORT_DIPSETTING( 0x10, "16 KB" )
	PORT_DIPSETTING( 0x00, "32 KB" )
	PORT_DIPNAME( 0x60, 0x60, "Character Set")
	PORT_DIPSETTING( 0x60, "Default (Codepage 437)" )
	PORT_DIPSETTING( 0x40, "Portuguese (Codepage 865)" )
	PORT_DIPSETTING( 0x20, "Norwegian (Codepage 860)" )
	PORT_DIPSETTING( 0x00, "Greek")
	PORT_DIPNAME( 0x80, 0x80, "Floppy Ready Line")
	PORT_DIPSETTING( 0x80, "Connected" )
	PORT_DIPSETTING( 0x00, "Not connected" )
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( pc1640 )
//-------------------------------------------------

static INPUT_PORTS_START( pc1640 )
	PORT_START("LK")
	PORT_DIPNAME( 0x07, 0x07, DEF_STR( Language ) )
	PORT_DIPSETTING(    0x07, DEF_STR( English ) )
	PORT_DIPSETTING(    0x06, DEF_STR( German ) )
	PORT_DIPSETTING(    0x05, DEF_STR( French ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Spanish ) )
	PORT_DIPSETTING(    0x03, "Danish" )
	PORT_DIPSETTING(    0x02, "Swedish" )
	PORT_DIPSETTING(    0x01, DEF_STR( Italian ) )
	PORT_DIPSETTING(    0x00, "Diagnostic Mode" )

	PORT_START("SW")
	PORT_DIPNAME( 0x0f, 0x09, "Initial Display Mode" ) PORT_DIPLOCATION("SW:1,2,3,4") PORT_CONDITION("SW", 0x200, EQUALS, 0x200)
	PORT_DIPSETTING(    0x0b, "Internal MD, External CGA80" )
	PORT_DIPSETTING(    0x0a, "Internal MD, External CGA40" )
	PORT_DIPSETTING(    0x09, "Internal ECD350, External MDA/HERC" )
	PORT_DIPSETTING(    0x08, "Internal ECD200, External MDA/HERC" )
	PORT_DIPSETTING(    0x07, "Internal CD80, External MDA/HERC" )
	PORT_DIPSETTING(    0x06, "Internal CD40, External MDA/HERC" )
	PORT_DIPSETTING(    0x05, "External CGA80, Internal MD" )
	PORT_DIPSETTING(    0x04, "External CGA40, Internal MD" )
	PORT_DIPSETTING(    0x03, "External MDA/HERC, Internal ECD350" )
	PORT_DIPSETTING(    0x02, "External MDA/HERC, Internal ECD200" )
	PORT_DIPSETTING(    0x01, "External MDA/HERC, Internal CD80" )
	PORT_DIPSETTING(    0x00, "External MDA/HERC, Internal CD40" )
	PORT_DIPNAME( 0x10, 0x10, "MC6845 Mode" ) PORT_DIPLOCATION("SW:5") PORT_CONDITION("SW", 0x200, EQUALS, 0x200)
	PORT_DIPSETTING(    0x10, "EGA" )
	PORT_DIPSETTING(    0x00, "CGA/MDA/HERC" )
	PORT_DIPNAME( 0x60, 0x00, "Font" ) PORT_DIPLOCATION("SW:6,7") PORT_CONDITION("SW", 0x300, EQUALS, 0x300)
	PORT_DIPSETTING(    0x00, DEF_STR( English ) )
	PORT_DIPSETTING(    0x60, "Danish" )
	PORT_DIPSETTING(    0x40, "Portuguese" )
	PORT_DIPSETTING(    0x20, "Greek" )
	PORT_DIPNAME( 0x60, 0x60, "Default Display Mode" ) PORT_DIPLOCATION("SW:6,7") PORT_CONDITION("SW", 0x200, EQUALS, 0x000)
	PORT_DIPSETTING(    0x60, "External EGA" )
	PORT_DIPSETTING(    0x40, "External CGA in 40 Column Mode" )
	PORT_DIPSETTING(    0x20, "External CGA in 80 Column Mode" )
	PORT_DIPSETTING(    0x00, "External Monochrome Adapter" )
	PORT_DIPNAME( 0x80, 0x00, "Monitor" ) PORT_DIPLOCATION("SW:8") PORT_CONDITION("SW", 0x200, EQUALS, 0x200)
	PORT_DIPSETTING(    0x80, "CD (Standard RGB)" )
	PORT_DIPSETTING(    0x00, "ECD (Enhanced RGB)" )
	PORT_DIPNAME( 0x100, 0x100, "Foreign Fonts" ) PORT_DIPLOCATION("SW:9") PORT_CONDITION("SW", 0x200, EQUALS, 0x200)
	PORT_DIPSETTING(     0x100, "Enabled" )
	PORT_DIPSETTING(     0x000, "Disabled" )
	PORT_DIPNAME( 0x200, 0x200, "Internal Graphics Adapter" ) PORT_DIPLOCATION("SW:10")
	PORT_DIPSETTING(     0x200, "Enabled" )
	PORT_DIPSETTING(     0x000, "Disabled" )
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  PC1512_KEYBOARD_INTERFACE( kb_intf )
//-------------------------------------------------

void pc1512_base_state::kbdata_w(int state)
{
	m_kbdata = state;
}

void pc1512_base_state::kbclk_w(int state)
{
	if (!BIT(m_port61, 7) && m_kbclk && !state)
	{
		m_kbd <<= 1;
		m_kbd |= m_kbdata;
		m_kb_bits++;

		if (m_kb_bits == 8)
		{
			m_kb->data_w(0);
			m_pic->ir1_w(ASSERT_LINE);
		}
	}

	m_kbclk = state;
}

void pc1512_base_state::mouse_x_w(uint8_t data)
{
	if (data > m_mouse_x_old)
		m_mouse_x+=3;
	else
		m_mouse_x-=3;

	m_mouse_x_old = data;
}

void pc1512_base_state::mouse_y_w(uint8_t data)
{
	if (data > m_mouse_y_old)
		m_mouse_y-=3;
	else
		m_mouse_y+=3;

	m_mouse_y_old = data;
}

//-------------------------------------------------
//  I8237_INTERFACE( dmac_intf )
//-------------------------------------------------

void pc1512_base_state::update_fdc_tc()
{
	if (m_nden)
		m_fdc->tc_w(m_neop);
	else
		m_fdc->tc_w(false);
}

void pc1512_base_state::hrq_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);

	m_dmac->hack_w(state);
}

void pc1512_base_state::eop_w(int state)
{
	if (m_dma_channel == 2)
	{
		m_neop = !state;
		update_fdc_tc();
	}
}

uint8_t pc1512_base_state::memr_r(offs_t offset)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	offs_t page_offset = m_dma_page[m_dma_channel] << 16;

	return program.read_byte(page_offset + offset);
}

void pc1512_base_state::memw_w(offs_t offset, uint8_t data)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	offs_t page_offset = m_dma_page[m_dma_channel] << 16;

	program.write_byte(page_offset + offset, data);
}

uint8_t pc1512_base_state::ior1_r()
{
	return m_bus->dack_r(1);
}

uint8_t pc1512_base_state::ior2_r()
{
	if (m_nden)
		return m_fdc->dma_r();
	else
		return m_bus->dack_r(2);
}

uint8_t pc1512_base_state::ior3_r()
{
	return m_bus->dack_r(3);
}

void pc1512_base_state::iow0_w(uint8_t data)
{
	m_dreq0 = 0;
	m_dmac->dreq0_w(m_dreq0);
}

void pc1512_base_state::iow1_w(uint8_t data)
{
	m_bus->dack_w(1, data);
}

void pc1512_base_state::iow2_w(uint8_t data)
{
	if (m_nden)
		m_fdc->dma_w(data);
	else
		m_bus->dack_w(2, data);
}

void pc1512_base_state::iow3_w(uint8_t data)
{
	m_bus->dack_w(3, data);
}

void pc1512_base_state::dack0_w(int state)
{
	if (!state) m_dma_channel = 0;
}

void pc1512_base_state::dack1_w(int state)
{
	if (!state) m_dma_channel = 1;
}

void pc1512_base_state::dack2_w(int state)
{
	if (!state) m_dma_channel = 2;
}

void pc1512_base_state::dack3_w(int state)
{
	if (!state) m_dma_channel = 3;
}

//-------------------------------------------------
//  pit8253_config pit_intf
//-------------------------------------------------

void pc1512_base_state::update_speaker()
{
	m_speaker->level_w(m_speaker_drive & m_pit2);
}

void pc1512_base_state::pit1_w(int state)
{
	if (!m_pit1 && state && !m_dreq0)
	{
		m_dreq0 = 1;
		m_dmac->dreq0_w(m_dreq0);
	}

	m_pit1 = state;
}

void pc1512_base_state::pit2_w(int state)
{
	m_pit2 = state;
	update_speaker();
}

//-------------------------------------------------
//  upd765_interface fdc_intf
//-------------------------------------------------

void pc1512_base_state::update_fdc_int()
{
	if (m_nden)
		m_pic->ir6_w(m_dint);
	else
		m_pic->ir6_w(CLEAR_LINE);
}

void pc1512_base_state::update_fdc_drq()
{
	if (m_nden)
		m_dmac->dreq2_w(m_ddrq);
	else
		m_dmac->dreq2_w(0);
}

void pc1512_base_state::fdc_int_w(int state)
{
	m_dint = state;
	update_fdc_int();
}

void pc1512_base_state::fdc_drq_w(int state)
{
	m_ddrq = state;
	update_fdc_drq();
}

void pc1512_base_state::drive_select_w(uint8_t data)
{
	m_fdc->set_floppy((data & 0x03) < 2 ? m_floppy[data & 0x03]->get_device() : nullptr);
	for (int n = 0; n < 2; n++)
	{
		floppy_image_device *img = m_floppy[n]->get_device();
		if (img != nullptr)
			 img->mon_w((data & 0x03) == n && BIT(data, n + 4) ? 0 : 1);
	}

	m_fdc->reset_w(!BIT(data, 2));
	m_nden = BIT(data, 3);
	update_fdc_int();
	update_fdc_drq();
}

//-------------------------------------------------
//  centronics_interface centronics_intf
//-------------------------------------------------

void pc1512_base_state::update_ack()
{
	if (m_ack_int_enable)
		m_pic->ir7_w(m_centronics_ack);
	else
		m_pic->ir7_w(CLEAR_LINE);
}

void pc1512_base_state::write_centronics_ack(int state)
{
	m_centronics_ack = state;
	update_ack();
}

void pc1512_base_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

void pc1512_base_state::write_centronics_perror(int state)
{
	m_centronics_perror = state;
}

void pc1512_base_state::write_centronics_select(int state)
{
	m_centronics_select = state;
}

void pc1512_base_state::write_centronics_fault(int state)
{
	m_centronics_fault = state;
}


//-------------------------------------------------
//  isa8bus_interface isabus_intf
//-------------------------------------------------

void pc1640_isa8_cards(device_slot_interface &device)
{
	device.option_add_internal("iga", ISA8_PC1640_IGA);
}

static void pc1512_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD); // Tandon TM65-2L
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

void pc1512_base_state::machine_start()
{
	save_item(NAME(m_pit1));
	save_item(NAME(m_pit2));
	save_item(NAME(m_status1));
	save_item(NAME(m_status2));
	save_item(NAME(m_port61));
	save_item(NAME(m_nmi_enable));
	save_item(NAME(m_kbd));
	save_item(NAME(m_kb_bits));
	save_item(NAME(m_kbclk));
	save_item(NAME(m_kbdata));
	save_item(NAME(m_mouse_x));
	save_item(NAME(m_mouse_y));
	save_item(NAME(m_mouse_x_old));
	save_item(NAME(m_mouse_y_old));
	save_item(NAME(m_dma_page));
	save_item(NAME(m_dma_channel));
	save_item(NAME(m_dreq0));
	save_item(NAME(m_nden));
	save_item(NAME(m_dint));
	save_item(NAME(m_ddrq));
	save_item(NAME(m_neop));
	save_item(NAME(m_ack_int_enable));
	save_item(NAME(m_centronics_ack));
	save_item(NAME(m_centronics_busy));
	save_item(NAME(m_centronics_perror));
	save_item(NAME(m_centronics_select));
	save_item(NAME(m_centronics_fault));
	save_item(NAME(m_printer_data));
	save_item(NAME(m_printer_control));
	save_item(NAME(m_speaker_drive));
}

void pc1512_state::machine_start()
{
	pc1512_base_state::machine_start();

	// set RAM size
	size_t ram_size = m_ram->size();

	if (ram_size < 640 * 1024)
	{
		address_space &program = m_maincpu->space(AS_PROGRAM);
		program.unmap_readwrite(ram_size, 0x9ffff);
	}
}

void pc1512_base_state::machine_reset()
{
	m_nmi_enable = 0;
	drive_select_w(0);

	m_kb_bits = 0;
	m_kb->data_w(1);
	m_pic->ir1_w(CLEAR_LINE);
}

void pc1512_state::machine_reset()
{
	pc1512_base_state::machine_reset();

	m_pit2 = 1;
}


//-------------------------------------------------
//  MACHINE_START( pc1640 )
//-------------------------------------------------

void pc1640_state::machine_start()
{
	pc1512_base_state::machine_start();

	// state saving
	save_item(NAME(m_opt));
}


//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

//-------------------------------------------------
//  machine_config( pc1512 )
//-------------------------------------------------

void pc1512_state::pc1512(machine_config &config)
{
	I8086(config, m_maincpu, 24_MHz_XTAL / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc1512_state::pc1512_mem);
	m_maincpu->set_addrmap(AS_IO, &pc1512_state::pc1512_io);
	m_maincpu->set_irq_acknowledge_callback(I8259A2_TAG, FUNC(pic8259_device::inta_cb));

	// video
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_raw(28.636363_MHz_XTAL, 912, 0, 910, 262, 0, 260);
	screen.set_screen_update(m_vdu, FUNC(ams40041_device::screen_update));

	AMS40041(config, m_vdu, 28.636363_MHz_XTAL);
	m_vdu->set_screen(SCREEN_TAG);
	m_vdu->set_show_border_area(true);

	// sound
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.80);

	// devices
	PC1512_KEYBOARD(config, m_kb, 0);
	m_kb->clock_wr_callback().set(FUNC(pc1512_state::kbclk_w));
	m_kb->data_wr_callback().set(FUNC(pc1512_state::kbdata_w));

	pc1512_mouse_port_device &mouse(PC1512_MOUSE_PORT(config, "mous", pc1512_mouse_port_devices, "mouse"));
	mouse.x_wr_callback().set(FUNC(pc1512_base_state::mouse_x_w));
	mouse.y_wr_callback().set(FUNC(pc1512_base_state::mouse_y_w));
	mouse.m1_wr_callback().set(m_kb, FUNC(pc1512_keyboard_device::m1_w));
	mouse.m2_wr_callback().set(m_kb, FUNC(pc1512_keyboard_device::m2_w));

	AM9517A(config, m_dmac, 24_MHz_XTAL / 6);
	m_dmac->out_hreq_callback().set(FUNC(pc1512_state::hrq_w));
	m_dmac->out_eop_callback().set(FUNC(pc1512_state::eop_w));
	m_dmac->in_memr_callback().set(FUNC(pc1512_state::memr_r));
	m_dmac->out_memw_callback().set(FUNC(pc1512_state::memw_w));
	m_dmac->in_ior_callback<1>().set(FUNC(pc1512_state::ior1_r));
	m_dmac->in_ior_callback<2>().set(FUNC(pc1512_state::ior2_r));
	m_dmac->in_ior_callback<3>().set(FUNC(pc1512_state::ior3_r));
	m_dmac->out_iow_callback<0>().set(FUNC(pc1512_state::iow0_w));
	m_dmac->out_iow_callback<1>().set(FUNC(pc1512_state::iow1_w));
	m_dmac->out_iow_callback<2>().set(FUNC(pc1512_state::iow2_w));
	m_dmac->out_iow_callback<3>().set(FUNC(pc1512_state::iow3_w));
	m_dmac->out_dack_callback<0>().set(FUNC(pc1512_state::dack0_w));
	m_dmac->out_dack_callback<1>().set(FUNC(pc1512_state::dack1_w));
	m_dmac->out_dack_callback<2>().set(FUNC(pc1512_state::dack2_w));
	m_dmac->out_dack_callback<3>().set(FUNC(pc1512_state::dack3_w));

	PIC8259(config, m_pic);
	m_pic->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	PIT8253(config, m_pit);
	m_pit->set_clk<0>(28.636363_MHz_XTAL / 24);
	m_pit->out_handler<0>().set(m_pic, FUNC(pic8259_device::ir0_w));
	m_pit->set_clk<1>(28.636363_MHz_XTAL / 24);
	m_pit->out_handler<1>().set(FUNC(pc1512_state::pit1_w));
	m_pit->set_clk<2>(28.636363_MHz_XTAL / 24);
	m_pit->out_handler<2>().set(FUNC(pc1512_state::pit2_w));

	MC146818(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->irq().set(m_pic, FUNC(pic8259_device::ir2_w));

	UPD765A(config, m_fdc, 24_MHz_XTAL / 6, false, false);
	// SED9420CAC (dedicated 16 MHz XTAL) is used as read data separator only
	m_fdc->intrq_wr_callback().set(FUNC(pc1512_state::fdc_int_w));
	m_fdc->drq_wr_callback().set(FUNC(pc1512_state::fdc_drq_w));
	FLOPPY_CONNECTOR(config, m_floppy[0], pc1512_floppies, "525dd", floppy_image_device::default_pc_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[1], pc1512_floppies, nullptr, floppy_image_device::default_pc_floppy_formats);

	INS8250(config, m_uart, 1.8432_MHz_XTAL);
	m_uart->out_tx_callback().set(RS232_TAG, FUNC(rs232_port_device::write_txd));
	m_uart->out_dtr_callback().set(RS232_TAG, FUNC(rs232_port_device::write_dtr));
	m_uart->out_rts_callback().set(RS232_TAG, FUNC(rs232_port_device::write_rts));
	m_uart->out_int_callback().set(m_pic, FUNC(pic8259_device::ir4_w));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set(FUNC(pc1512_state::write_centronics_ack));
	m_centronics->busy_handler().set(FUNC(pc1512_state::write_centronics_busy));
	m_centronics->perror_handler().set(FUNC(pc1512_state::write_centronics_perror));
	m_centronics->select_handler().set(FUNC(pc1512_state::write_centronics_select));
	m_centronics->fault_handler().set(FUNC(pc1512_state::write_centronics_fault));

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	rs232_port_device &rs232(RS232_PORT(config, RS232_TAG, default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_uart, FUNC(ins8250_uart_device::rx_w));
	rs232.dcd_handler().set(m_uart, FUNC(ins8250_uart_device::dcd_w));
	rs232.dsr_handler().set(m_uart, FUNC(ins8250_uart_device::dsr_w));
	rs232.ri_handler().set(m_uart, FUNC(ins8250_uart_device::ri_w));
	rs232.cts_handler().set(m_uart, FUNC(ins8250_uart_device::cts_w));

	// ISA8 bus
	isa8_device &isa(ISA8(config, ISA_BUS_TAG, 0));
	isa.set_memspace(m_maincpu, AS_PROGRAM);
	isa.set_iospace(m_maincpu, AS_IO);
	isa.irq2_callback().set(m_pic, FUNC(pic8259_device::ir2_w));
	isa.irq3_callback().set(m_pic, FUNC(pic8259_device::ir3_w));
	isa.irq4_callback().set(m_pic, FUNC(pic8259_device::ir4_w));
	isa.irq5_callback().set(m_pic, FUNC(pic8259_device::ir5_w));
	isa.irq6_callback().set(m_pic, FUNC(pic8259_device::ir6_w));
	isa.irq7_callback().set(m_pic, FUNC(pic8259_device::ir7_w));
	isa.drq1_callback().set(I8237A5_TAG, FUNC(am9517a_device::dreq1_w));
	isa.drq2_callback().set(I8237A5_TAG, FUNC(am9517a_device::dreq2_w));
	isa.drq3_callback().set(I8237A5_TAG, FUNC(am9517a_device::dreq3_w));
	ISA8_SLOT(config, "isa1", 0, ISA_BUS_TAG, pc_isa8_cards, nullptr, false); // FIXME: determine ISA clock
	ISA8_SLOT(config, "isa2", 0, ISA_BUS_TAG, pc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa3", 0, ISA_BUS_TAG, pc_isa8_cards, nullptr, false);

	// internal ram
	RAM(config, RAM_TAG).set_default_size("512K").set_extra_options("544K,576K,608K,640K");

	// software list
	SOFTWARE_LIST(config, "flop_list").set_original("pc1512_flop");
	SOFTWARE_LIST(config, "hdd_list").set_original("pc1512_hdd");
}


//-------------------------------------------------
//  machine_config( pc1512dd )
//-------------------------------------------------

void pc1512_state::pc1512dd(machine_config &config)
{
	pc1512(config);
	m_floppy[1]->set_default_option("525dd");
}


//-------------------------------------------------
//  machine_config( pc1512hd )
//-------------------------------------------------

void pc1512_state::pc1512hd(machine_config &config)
{
	pc1512(config);
	//subdevice<isa8_slot_device>("isa1")->set_default_option("wdxt_gen");
	subdevice<isa8_slot_device>("isa1")->set_default_option("hdc");
}


//-------------------------------------------------
//  machine_config( pc1640 )
//-------------------------------------------------

void pc1640_state::pc1640(machine_config &config)
{
	I8086(config, m_maincpu, 24_MHz_XTAL / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc1640_state::pc1640_mem);
	m_maincpu->set_addrmap(AS_IO, &pc1640_state::pc1640_io);
	m_maincpu->set_irq_acknowledge_callback(I8259A2_TAG, FUNC(pic8259_device::inta_cb));

	// sound
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.80);

	// devices
	PC1512_KEYBOARD(config, m_kb, 0);
	m_kb->clock_wr_callback().set(FUNC(pc1512_base_state::kbclk_w));
	m_kb->data_wr_callback().set(FUNC(pc1512_base_state::kbdata_w));

	pc1512_mouse_port_device &mouse(PC1512_MOUSE_PORT(config, "mous", pc1512_mouse_port_devices, "mouse"));
	mouse.x_wr_callback().set(FUNC(pc1512_base_state::mouse_x_w));
	mouse.y_wr_callback().set(FUNC(pc1512_base_state::mouse_y_w));
	mouse.m1_wr_callback().set(m_kb, FUNC(pc1512_keyboard_device::m1_w));
	mouse.m2_wr_callback().set(m_kb, FUNC(pc1512_keyboard_device::m2_w));

	AM9517A(config, m_dmac, 24_MHz_XTAL / 6);
	m_dmac->out_hreq_callback().set(FUNC(pc1640_state::hrq_w));
	m_dmac->out_eop_callback().set(FUNC(pc1640_state::eop_w));
	m_dmac->in_memr_callback().set(FUNC(pc1640_state::memr_r));
	m_dmac->out_memw_callback().set(FUNC(pc1640_state::memw_w));
	m_dmac->in_ior_callback<1>().set(FUNC(pc1640_state::ior1_r));
	m_dmac->in_ior_callback<2>().set(FUNC(pc1640_state::ior2_r));
	m_dmac->in_ior_callback<3>().set(FUNC(pc1640_state::ior3_r));
	m_dmac->out_iow_callback<0>().set(FUNC(pc1640_state::iow0_w));
	m_dmac->out_iow_callback<1>().set(FUNC(pc1640_state::iow1_w));
	m_dmac->out_iow_callback<2>().set(FUNC(pc1640_state::iow2_w));
	m_dmac->out_iow_callback<3>().set(FUNC(pc1640_state::iow3_w));
	m_dmac->out_dack_callback<0>().set(FUNC(pc1640_state::dack0_w));
	m_dmac->out_dack_callback<1>().set(FUNC(pc1640_state::dack1_w));
	m_dmac->out_dack_callback<2>().set(FUNC(pc1640_state::dack2_w));
	m_dmac->out_dack_callback<3>().set(FUNC(pc1640_state::dack3_w));

	PIC8259(config, m_pic);
	m_pic->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	PIT8253(config, m_pit);
	m_pit->set_clk<0>(28.636363_MHz_XTAL / 24);
	m_pit->out_handler<0>().set(m_pic, FUNC(pic8259_device::ir0_w));
	m_pit->set_clk<1>(28.636363_MHz_XTAL / 24);
	m_pit->out_handler<1>().set(FUNC(pc1512_base_state::pit1_w));
	m_pit->set_clk<2>(28.636363_MHz_XTAL / 24);
	m_pit->out_handler<2>().set(FUNC(pc1512_base_state::pit2_w));

	MC146818(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->irq().set(m_pic, FUNC(pic8259_device::ir2_w));

	UPD765A(config, m_fdc, 24_MHz_XTAL / 6, false, false);
	// FDC91C36 (clocked by CK8K) is used as read data separator only
	m_fdc->intrq_wr_callback().set(FUNC(pc1512_base_state::fdc_int_w));
	m_fdc->drq_wr_callback().set(FUNC(pc1512_base_state::fdc_drq_w));
	FLOPPY_CONNECTOR(config, m_floppy[0], pc1512_floppies, "525dd", floppy_image_device::default_pc_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[1], pc1512_floppies, nullptr, floppy_image_device::default_pc_floppy_formats);

	INS8250(config, m_uart, 1.8432_MHz_XTAL);
	m_uart->out_tx_callback().set(RS232_TAG, FUNC(rs232_port_device::write_txd));
	m_uart->out_dtr_callback().set(RS232_TAG, FUNC(rs232_port_device::write_dtr));
	m_uart->out_rts_callback().set(RS232_TAG, FUNC(rs232_port_device::write_rts));
	m_uart->out_int_callback().set(m_pic, FUNC(pic8259_device::ir4_w));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set(FUNC(pc1512_state::write_centronics_ack));
	m_centronics->busy_handler().set(FUNC(pc1512_state::write_centronics_busy));
	m_centronics->perror_handler().set(FUNC(pc1512_state::write_centronics_perror));
	m_centronics->select_handler().set(FUNC(pc1512_state::write_centronics_select));
	m_centronics->fault_handler().set(FUNC(pc1512_state::write_centronics_fault));

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	rs232_port_device &rs232(RS232_PORT(config, RS232_TAG, default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_uart, FUNC(ins8250_uart_device::rx_w));
	rs232.dcd_handler().set(m_uart, FUNC(ins8250_uart_device::dcd_w));
	rs232.dsr_handler().set(m_uart, FUNC(ins8250_uart_device::dsr_w));
	rs232.ri_handler().set(m_uart, FUNC(ins8250_uart_device::ri_w));
	rs232.cts_handler().set(m_uart, FUNC(ins8250_uart_device::cts_w));

	// ISA8 bus
	isa8_device &isa(ISA8(config, ISA_BUS_TAG, 0));
	isa.set_memspace(m_maincpu, AS_PROGRAM);
	isa.set_iospace(m_maincpu, AS_IO);
	isa.irq2_callback().set(m_pic, FUNC(pic8259_device::ir2_w));
	isa.irq3_callback().set(m_pic, FUNC(pic8259_device::ir3_w));
	isa.irq4_callback().set(m_pic, FUNC(pic8259_device::ir4_w));
	isa.irq5_callback().set(m_pic, FUNC(pic8259_device::ir5_w));
	isa.irq6_callback().set(m_pic, FUNC(pic8259_device::ir6_w));
	isa.irq7_callback().set(m_pic, FUNC(pic8259_device::ir7_w));
	isa.drq1_callback().set(I8237A5_TAG, FUNC(am9517a_device::dreq1_w));
	isa.drq2_callback().set(I8237A5_TAG, FUNC(am9517a_device::dreq2_w));
	isa.drq3_callback().set(I8237A5_TAG, FUNC(am9517a_device::dreq3_w));
	ISA8_SLOT(config, "isa1", 0, ISA_BUS_TAG, pc_isa8_cards, nullptr, false); // FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa2", 0, ISA_BUS_TAG, pc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa3", 0, ISA_BUS_TAG, pc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa4", 0, ISA_BUS_TAG, pc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa5", 0, ISA_BUS_TAG, pc1640_isa8_cards, "iga", false);

	// internal ram
	RAM(config, RAM_TAG).set_default_size("640K");

	// software list
	SOFTWARE_LIST(config, "flop_list").set_original("pc1640_flop");
	SOFTWARE_LIST(config, "hdd_list").set_original("pc1640_hdd");
}


//-------------------------------------------------
//  machine_config( pc1640dd )
//-------------------------------------------------

void pc1640_state::pc1640dd(machine_config &config)
{
	pc1640(config);
	m_floppy[1]->set_default_option("525dd");
}


//-------------------------------------------------
//  machine_config( pc1640hd )
//-------------------------------------------------

void pc1640_state::pc1640hd(machine_config &config)
{
	pc1640(config);
	//subdevice<isa8_slot_device>("isa1")->set_default_option("wdxt_gen");
	subdevice<isa8_slot_device>("isa1")->set_default_option("hdc");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( pc1512 )
//-------------------------------------------------

ROM_START( pc1512 )
	ROM_REGION16_LE( 0x4000, I8086_TAG, 0)
	ROM_SYSTEM_BIOS( 0, "v1", "Version 1" )
	ROMX_LOAD( "40044.ic132", 0x0000, 0x2000, CRC(f72f1582) SHA1(7781d4717917262805d514b331ba113b1e05a247), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "40043.ic129", 0x0001, 0x2000, CRC(668fcc94) SHA1(74002f5cc542df442eec9e2e7a18db3598d8c482), ROM_SKIP(1) | ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v2", "Version 2" )
	ROMX_LOAD( "40044v2.ic132", 0x0000, 0x2000, CRC(1aec54fa) SHA1(b12fd73cfc35a240ed6da4dcc4b6c9910be611e0), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "40043v2.ic129", 0x0001, 0x2000, CRC(d2d4d2de) SHA1(c376fd1ad23025081ae16c7949e88eea7f56e1bb), ROM_SKIP(1) | ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v3", "Version 3" )
	ROMX_LOAD( "40044-2.ic132", 0x0000, 0x2000, CRC(ea527e6e) SHA1(b77fa44767a71a0b321a88bb0a394f1125b7c220), ROM_SKIP(1) | ROM_BIOS(2) )
	ROMX_LOAD( "40043-2.ic129", 0x0001, 0x2000, CRC(532c3854) SHA1(18a17b710f9eb079d9d7216d07807030f904ceda), ROM_SKIP(1) | ROM_BIOS(2) )

	ROM_REGION( 0x2000, AMS40041_TAG, 0 )
	ROMX_LOAD( "40045.ic127", 0x0000, 0x2000, CRC(dd5e030f) SHA1(7d858bbb2e8d6143aa67ab712edf5f753c2788a7), ROM_BIOS(0) )
	ROMX_LOAD( "40078.ic127", 0x0000, 0x2000, CRC(ae9c0d04) SHA1(bc8dc4dcedeea5bc1c04986b1f105ad93cb2ebcd), ROM_BIOS(1) )
	ROMX_LOAD( "40078.ic127", 0x0000, 0x2000, CRC(ae9c0d04) SHA1(bc8dc4dcedeea5bc1c04986b1f105ad93cb2ebcd), ROM_BIOS(2) )
ROM_END

#define rom_pc1512dd    rom_pc1512
#define rom_pc1512hd10  rom_pc1512
#define rom_pc1512hd20  rom_pc1512


//-------------------------------------------------
//  ROM( pc1640 )
//-------------------------------------------------

ROM_START( pc1640 )
	ROM_REGION16_LE( 0x4000, I8086_TAG, 0)
	ROM_SYSTEM_BIOS( 0, "8809", "Week 9/1988" )
	ROMX_LOAD( "40044-1 8809.ic132", 0x0000, 0x2000, CRC(f1c074f3) SHA1(a055ea7e933d137623c22fe24004e870653c7952), ROM_SKIP(1) | ROM_BIOS(0) ) // 8809 B
	ROMX_LOAD( "40043-1 8809.ic129", 0x0001, 0x2000, CRC(e40a1513) SHA1(447eff2057e682e51b1c7593cb6fad0e53879fa8), ROM_SKIP(1) | ROM_BIOS(0) ) // 8809 B
	ROM_SYSTEM_BIOS( 1, "8738", "Week 38/1987" )
	ROMX_LOAD( "40044 8738.ic132", 0x0000, 0x2000, CRC(43832ea7) SHA1(eea4a8836f966940a88c88de6c5cc14852545f7d), ROM_SKIP(1) | ROM_BIOS(1) ) // 8738 D F
	ROMX_LOAD( "40043 8738.ic129", 0x0001, 0x2000, CRC(768498f9) SHA1(ac48cb892417d7998d604f3b79756140c554f476), ROM_SKIP(1) | ROM_BIOS(1) ) // 8738 D F
	ROM_SYSTEM_BIOS( 2, "88xx", "Week ?/1988" )
	ROMX_LOAD( "40044 88xx.ic132", 0x0000, 0x2000, CRC(6090f782) SHA1(e21ae524d5b4d00696d293dbd4fe4d7bca22e277), ROM_SKIP(1) | ROM_BIOS(2) )
	ROMX_LOAD( "40043 88xx.ic129", 0x0001, 0x2000, CRC(9219d0aa) SHA1(dde1a46c8f83e413d7070f1356fc91b9f595a8b6), ROM_SKIP(1) | ROM_BIOS(2) )
ROM_END

#define rom_pc1640dd    rom_pc1640
#define rom_pc1640hd20  rom_pc1640
#define rom_pc1640hd30  rom_pc1640



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME        PARENT  COMPAT  MACHINE   INPUT   CLASS         INIT        COMPANY        FULLNAME       FLAGS
COMP( 1986, pc1512,     0,      0,      pc1512,   pc1512, pc1512_state, empty_init, "Amstrad plc", "PC1512 SD",   MACHINE_SUPPORTS_SAVE )
COMP( 1986, pc1512dd,   pc1512, 0,      pc1512dd, pc1512, pc1512_state, empty_init, "Amstrad plc", "PC1512 DD",   MACHINE_SUPPORTS_SAVE )
COMP( 1986, pc1512hd10, pc1512, 0,      pc1512hd, pc1512, pc1512_state, empty_init, "Amstrad plc", "PC1512 HD10", MACHINE_SUPPORTS_SAVE )
COMP( 1986, pc1512hd20, pc1512, 0,      pc1512hd, pc1512, pc1512_state, empty_init, "Amstrad plc", "PC1512 HD20", MACHINE_SUPPORTS_SAVE )
COMP( 1987, pc1640,     0,      0,      pc1640,   pc1640, pc1640_state, empty_init, "Amstrad plc", "PC1640 SD",   MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
COMP( 1987, pc1640dd,   pc1640, 0,      pc1640dd, pc1640, pc1640_state, empty_init, "Amstrad plc", "PC1640 DD",   MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
COMP( 1987, pc1640hd20, pc1640, 0,      pc1640hd, pc1640, pc1640_state, empty_init, "Amstrad plc", "PC1640 HD20", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
COMP( 1987, pc1640hd30, pc1640, 0,      pc1640hd, pc1640, pc1640_state, empty_init, "Amstrad plc", "PC1640 HD30", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )



pc2000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

        VTech PreComputer 1000 / 2000

        04/12/2009 Skeleton driver.

        TODO:
        - dump the chargen
        - change PC2000 to use a SED1278F-0B instead of an HD44780 (different charset)

****************************************************************************

Other known international versions:
- Genio 2000 (Spanish version of Genius Leader 4000 Quadro)
- Genius 4000 (French version of Genius Leader 4000 Quadro)
- Genius 5000 (French version of Genius Leader 5000)
- Genius 6000 (French version of Genius Leader 6000 SL)
- Genius 6500 Duo (alternate French version of Genius Leader 6000 SL)
- Genius Notebook (French version of Genius Leader 3000 S)
- PreComputer Power Pad (English version of Genius Leader 4000 Quadro)
- PreComputer Power Pad Plus (English version of Genius Leader 5000)
- Talking Whiz-Kid Einstein (English version of Genius Leader Power Notebook)
- Talking Whiz-Kid Einstein Mouse (alternate English version of Genius Leader Power Notebook)

****************************************************************************/


#include "emu.h"

#include "cpu/z80/z80.h"
#include "sound/beep.h"
#include "video/hd44780.h"
#include "video/sed1520.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "gl3000s.lh"


namespace {

class pc2000_state : public driver_device
{
public:
	pc2000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "hd44780")
		, m_screen(*this, "screen")
		, m_beep(*this, "beeper")
		, m_cart(*this, "cartslot")
		, m_bank0(*this, "bank0")
		, m_bank1(*this, "bank1")
		, m_bank2(*this, "bank2")
		, m_rows{ { *this, "IN%X", 0 }, { *this, "IN%X", 8 } }
	{ }

	void pc2000(machine_config &config);
	void pc2000eur(machine_config &config);
	void gl2000(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint8_t key_matrix_r(offs_t offset);
	void key_matrix_w(uint8_t data);
	void rombank0_w(uint8_t data);
	void rombank1_w(uint8_t data);
	void rombank2_w(uint8_t data);
	uint8_t beep_r();
	void beep_w(uint8_t data);
	void pc2000_palette(palette_device &palette) const;
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	void pc2000_io(address_map &map) ATTR_COLD;
	void pc2000_mem(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	optional_device<hd44780_device> m_lcdc;
	required_device<screen_device> m_screen;
	required_device<beep_device> m_beep;
	required_device<generic_slot_device> m_cart;
	optional_memory_bank m_bank0;
	required_memory_bank m_bank1;
	optional_memory_bank m_bank2;

	void pc2000gen(machine_config &config);

private:
	optional_ioport_array<8> m_rows[2];

	uint8_t m_mux_data;
	uint8_t m_beep_state;
};

class gl3000s_state : public pc2000_state
{
public:
	gl3000s_state(const machine_config &mconfig, device_type type, const char *tag)
		: pc2000_state(mconfig, type, tag)
		, m_lcdc_r(*this, "sed1520_r")
		, m_lcdc_l(*this, "sed1520_l")
		, m_lev_out(*this, "LEV%u", 1U)
		, m_try_out(*this, "TRY%u", 1U)
		, m_tick_out(*this, "TICK%u", 0U)
		, m_time_out(*this, "TIME%u", 0U)
		, m_points_out(*this, "P%u%u", 1U, 0U)
	{ }

	void gl3000s(machine_config &config);
	void gl5000(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void gl3000s_io(address_map &map) ATTR_COLD;
	void gl5000_io(address_map &map) ATTR_COLD;

private:
	int sed1520_screen_update(bitmap_ind16 &bitmap, const rectangle &cliprect, uint8_t *vram, int start_line, int adc, int start_x, int max_y);
	SED1520_UPDATE_CB(screen_update_right);
	SED1520_UPDATE_CB(screen_update_left);
	SED1520_UPDATE_CB(gl5000_screen_update_right);
	SED1520_UPDATE_CB(gl5000_screen_update_left);

	uint8_t rombank1_r();
	uint8_t rombank2_r();

	required_device<sed1520_device> m_lcdc_r;
	required_device<sed1520_device> m_lcdc_l;
	output_finder<4> m_lev_out;
	output_finder<3> m_try_out;
	output_finder<8> m_tick_out;
	output_finder<3> m_time_out;
	output_finder<2, 3> m_points_out;
};

class gl4004_state : public pc2000_state
{
public:
	gl4004_state(const machine_config &mconfig, device_type type, const char *tag)
		: pc2000_state(mconfig, type, tag)
	{ }

	void gl4000(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	HD44780_PIXEL_UPDATE(gl4000_pixel_update);
};


// TODO: put a breakpoint at 1625 and test the inputs, writes at dce4 are the scancode values
uint8_t pc2000_state::key_matrix_r(offs_t offset)
{
	uint8_t data = 0xff;
	for (int line = 0; line < 8; line++)
		if (BIT(m_mux_data, line))
			data &= m_rows[offset & 1][line]->read();

	return data;
}

void pc2000_state::key_matrix_w(uint8_t data)
{
	m_mux_data = data;
}

void pc2000_state::rombank0_w(uint8_t data)
{
	m_bank0->set_entry(data & 0x1f);
}

void pc2000_state::rombank1_w(uint8_t data)
{
	m_bank1->set_entry(data & 0x1f);
}

void pc2000_state::rombank2_w(uint8_t data)
{
	if (data & 0x80)
		m_bank2->set_entry(data & 0x8f);   //cartridge
	else
		m_bank2->set_entry(data & 0x1f);
}

uint8_t pc2000_state::beep_r()
{
	return m_beep_state;
}

void pc2000_state::beep_w(uint8_t data)
{
	m_beep->set_state(BIT(data, 3));
	m_beep_state = data;
}

void pc2000_state::pc2000_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).bankr("bank0");
	map(0x4000, 0x7fff).bankr("bank1");
	map(0x8000, 0xbfff).bankr("bank2");    //0x8000 - 0xbfff tests a cartridge, header is 0x55 0xaa 0x59 0x45, if it succeeds a jump at 0x8004 occurs
	map(0xc000, 0xdfff).ram();
}

void pc2000_state::pc2000_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x00).w(FUNC(pc2000_state::rombank0_w));
	map(0x01, 0x01).w(FUNC(pc2000_state::rombank1_w));
	map(0x03, 0x03).w(FUNC(pc2000_state::rombank2_w));
	map(0x0a, 0x0b).rw(m_lcdc, FUNC(hd44780_device::read), FUNC(hd44780_device::write));
	map(0x10, 0x11).rw(FUNC(pc2000_state::key_matrix_r), FUNC(pc2000_state::key_matrix_w));
	map(0x12, 0x12).rw(FUNC(pc2000_state::beep_r), FUNC(pc2000_state::beep_w));
}


uint32_t gl3000s_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	bitmap.fill(0);
	m_lcdc_l->screen_update(screen, bitmap, cliprect);
	m_lcdc_r->screen_update(screen, bitmap, cliprect);
	return 0;
}

int gl3000s_state::sed1520_screen_update(bitmap_ind16 &bitmap, const rectangle &cliprect, uint8_t *vram, int start_line, int adc, int start_x, int max_y)
{
	for (int y=0; y<max_y; y++)
	{
		int row_pos = 0;
		for (int x=0; x<61; x++)
		{
			int addr = (y + (start_line >> 3)) * 80 + row_pos;
			for (int yi=0; yi<8; yi++)
			{
				int px = start_x - (adc ? (80 - x) : x);
				int py = 8 + y*8 + yi;

				if (cliprect.contains(px, py))
					bitmap.pix(py, px) = (vram[addr % 0x140] >> yi) & 1;
			}

			row_pos++;
		}
	}
	return 0;
}

SED1520_UPDATE_CB(gl3000s_state::screen_update_right)
{
	if (lcd_on)
		return sed1520_screen_update(bitmap, cliprect, dram, start_line, adc, 119, 2);

	bitmap.fill(0, cliprect);
	return 0;
}

SED1520_UPDATE_CB(gl3000s_state::screen_update_left)
{
	uint8_t sec[5];
	uint8_t points[2][5];
	memset(sec, 0, sizeof(sec));
	memset(points, 0, sizeof(points));

	for (int y=0; y<2; y++)
		for (int x=59; x<85; x++)
		{
			uint8_t data = 0;
			if (lcd_on)
				data = dram[((y + (start_line >> 3)) * 80 + x) & 0x1ff];

			int32_t dpos = (x - 74) / 2;
			if (dpos < 0)
			{
				dpos = 0;
			}

			for (int yi=0; yi<8; yi++)
			{
				int state = (data >> yi) & 1;

				if (y == 0 && (x == 74 || x == 76 || x == 78) && yi == 7)         sec[dpos] |= (state << 0);
				else if (y == 0 && (x == 74 || x == 76 || x == 78) && yi == 0)    sec[dpos] |= (state << 2);
				else if (y == 0 && (x == 75 || x == 77 || x == 79) && yi == 7)    sec[dpos] |= (state << 5);
				else if (y == 0 && (x == 75 || x == 77 || x == 79) && yi == 0)    sec[dpos] |= (state << 4);
				else if (y == 0 && (x == 75 || x == 77 || x == 79) && yi == 1)    sec[dpos] |= (state << 3);
				else if (y == 1 && (x == 74 || x == 76 || x == 78) && yi == 7)    sec[dpos] |= (state << 1);
				else if (y == 1 && (x == 75 || x == 77 || x == 79) && yi == 7)    sec[dpos] |= (state << 6);

				else if ((x == 74 || x == 76 || x == 78) && yi == 3)    points[y][dpos] |= (state << 0);
				else if ((x == 74 || x == 76 || x == 78) && yi == 4)    points[y][dpos] |= (state << 1);
				else if ((x == 74 || x == 76 || x == 78) && yi == 5)    points[y][dpos] |= (state << 2);
				else if ((x == 75 || x == 77 || x == 79) && yi == 3)    points[y][dpos] |= (state << 5);
				else if ((x == 75 || x == 77 || x == 79) && yi == 4)    points[y][dpos] |= (state << 6);
				else if ((x == 75 || x == 77 || x == 79) && yi == 5)    points[y][dpos] |= (state << 4);
				else if ((x == 75 || x == 77 || x == 79) && yi == 6)    points[y][dpos] |= (state << 3);

				else if (y == 1 && x >= 65 && x <= 68 && yi == 7)       m_lev_out[x - 65] = state;
				else if (x >= 59  && x <= 60 && yi == 7)                m_try_out[x - 59 + (y ? 0 : 1)] = state;
				else if (y == 1 && x >= 61 && x <= 64 && yi == 7)       m_tick_out[x - 59] = state;
				else if (y == 0 && x >= 61 && x <= 64 && yi == 7)       m_tick_out[62 - x + (x >= 63 ? 8 : 0)] = state;

				else if (x < 74 && yi < 7)
				{
					int cx = x - 59;
					bitmap.pix(yi, (y ? 0 : 89) + (16 - (cx + cx / 5))) = state;
				}
			}
		}

	for (int i = 0; i < 3; i++)
	{
		m_time_out[i] = sec[i];
		m_points_out[0][i] = points[1][i];
		m_points_out[1][i] = points[0][i];
	}

	if (lcd_on)
		return sed1520_screen_update(bitmap, cliprect, dram, start_line, adc, 58, 2);

	bitmap.fill(0, cliprect);
	return 0;
}

SED1520_UPDATE_CB(gl3000s_state::gl5000_screen_update_left)
{
	if (lcd_on)
		return sed1520_screen_update(bitmap, cliprect, dram, start_line, adc, 119, 4);

	bitmap.fill(0, cliprect);
	return 0;
}

SED1520_UPDATE_CB(gl3000s_state::gl5000_screen_update_right)
{
	if (lcd_on)
		return sed1520_screen_update(bitmap, cliprect, dram, start_line, adc, 58, 4);

	bitmap.fill(0, cliprect);
	return 0;
}

void gl3000s_state::gl3000s_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x01, 0x01).w(FUNC(gl3000s_state::rombank1_w));
	map(0x03, 0x03).w(FUNC(gl3000s_state::rombank2_w));
	map(0x08, 0x09).rw(m_lcdc_r, FUNC(sed1520_device::read), FUNC(sed1520_device::write));
	map(0x0a, 0x0b).rw(m_lcdc_l, FUNC(sed1520_device::read), FUNC(sed1520_device::write));
	map(0x10, 0x11).rw(FUNC(gl3000s_state::key_matrix_r), FUNC(gl3000s_state::key_matrix_w));
}

uint8_t gl3000s_state::rombank1_r()
{
	return m_bank1->entry();
}

uint8_t gl3000s_state::rombank2_r()
{
	return m_bank2->entry();
}

void gl3000s_state::gl5000_io(address_map &map)
{
	gl3000s_io(map);
	map(0x01, 0x01).r(FUNC(gl3000s_state::rombank1_r));
	map(0x03, 0x03).r(FUNC(gl3000s_state::rombank2_r));
	map(0x12, 0x12).nopr(); // ?
}

/* Input ports */
static INPUT_PORTS_START( pc2000 )
	PORT_START("IN0")
	PORT_DIPNAME( 0x01, 0x01, "IN0" ) //0x83
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Caps Lock")  PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1")          PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q")          PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A")          PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z")          PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\")         PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space")      PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IN2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2")  PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3")  PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E")  PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S")  PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D")  PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X")  PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C")  PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W")  PORT_CODE(KEYCODE_W) PORT_CHAR('W')

	PORT_START("IN3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4")  PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5")  PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T")  PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F")  PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G")  PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V")  PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B")  PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R")  PORT_CODE(KEYCODE_R) PORT_CHAR('R')

	PORT_START("IN4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6")  PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7")  PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U")  PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H")  PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J")  PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N")  PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M")  PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y")  PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')

	PORT_START("IN5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8")  PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9")  PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O")  PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K")  PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L")  PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",")  PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".")  PORT_CODE(KEYCODE_STOP) PORT_CHAR('.')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I")  PORT_CODE(KEYCODE_I) PORT_CHAR('I')

	PORT_START("IN6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0")  PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("HOME")   PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("=")  PORT_CODE(KEYCODE_EQUALS)   PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";")  PORT_CODE(KEYCODE_COLON) PORT_CHAR(';')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\'") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/")  PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P")  PORT_CODE(KEYCODE_P) PORT_CHAR('P')

	PORT_START("IN7")
	PORT_BIT(0x03, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LEFT")   PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RIGHT")  PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER")  PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DEL")    PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ESC")    PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IN8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Scramble")           PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Guess Rork")         PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Missing Letter")     PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Letter Hunt")        PORT_CODE(KEYCODE_F4)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IN9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Letter Zapper")      PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Letter Switch")      PORT_CODE(KEYCODE_F6)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Sentence Jumble")    PORT_CODE(KEYCODE_F7)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Grammar Challenge")  PORT_CODE(KEYCODE_F8)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("INA")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Plurals")            PORT_CODE(KEYCODE_F9)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Past Tense")         PORT_CODE(KEYCODE_F10)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Synonyms")           PORT_CODE(KEYCODE_F11)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Antonyms")           PORT_CODE(KEYCODE_F12)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("INB")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Basic Math")         PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Advanced Math")      PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Math Riddles")       PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Number Challenge")   PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("INC")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ecology")            PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("History")            PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Geography")          PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Super Power")        PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IND")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Typing Game")        PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Alpha Jumble")       PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Word Flash")         PORT_CODE(KEYCODE_PGUP)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Sign Search")        PORT_CODE(KEYCODE_PGDN)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("INE")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PC2000 Basic")       PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Spell Checker")      PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Calculator")         PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Load Cartridge")     PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("INF")
	PORT_DIPNAME( 0x01, 0x01, "INF" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(DEF_STR( Level_Select )) PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num Players")        PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( gl3000s )
	PORT_START("IN0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Einstellen Antwort")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)

	PORT_START("IN1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Hilfe")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)  PORT_CHAR(' ')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')

	PORT_START("IN2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Druck Symbol")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')

	PORT_START("IN3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR(U'Ä')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)

	PORT_START("IN4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')

	PORT_START("IN5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)  PORT_CHAR(U'ß')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)  PORT_CHAR(U'Ö')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.')

	PORT_START("IN6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR(U'Ü')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR('+')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',')

	PORT_START("IN7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')

	PORT_START("IN8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Off")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BASIC")              PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Logische Folgen")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Bruch / Dezimalrechnen")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Addition")
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IN9")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Textverarbeitung")   PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Buchstabenschlüssel")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Bruch / Prozentrechnen") PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Subtraktion")        PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0xe1, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("INA")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Taschenrechner") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Gleitpfeile")    PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Algebra")        PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Multiplikation") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0xe1, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("INB")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ZusatzKassette") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Stufe")          PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Spieler")        PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Zeichensuche")   PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Division")       PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("INC")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Präteritum")   PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Synonyme")       PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tipp Dich Fit")  PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Super Schlau-Quiz")  PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0xe1, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IND")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Silbentrennung")     PORT_CODE(KEYCODE_F12)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Knifflige Grammatik")  PORT_CODE(KEYCODE_F11)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Textaufgaben")       PORT_CODE(KEYCODE_F10)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Geographie-Quiz")    PORT_CODE(KEYCODE_F9)
	PORT_BIT(0xe1, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("INE")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Worträtsel")       PORT_CODE(KEYCODE_F8)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Verdrehte Sätze")  PORT_CODE(KEYCODE_F7)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Blitz-Sätze")      PORT_CODE(KEYCODE_F6)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Geschichte-Quiz")    PORT_CODE(KEYCODE_F5)
	PORT_BIT(0xe1, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("INF")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Verrückte Rätsel")  PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Wortarten")           PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Wortschlange")        PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Naturwissenschaften-Quiz")   PORT_CODE(KEYCODE_F1)
	PORT_BIT(0xe1, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

void pc2000_state::machine_start()
{
	uint8_t *bios = memregion("bios")->base();
	memory_region *cart_region = memregion(std::string(m_cart->tag()) + GENERIC_ROM_REGION_TAG);
	uint8_t *cart = cart_region ? cart_region->base() : bios;

	m_bank0->configure_entries(0, 0x10, bios, 0x4000);
	m_bank1->configure_entries(0, 0x10, bios, 0x4000);
	m_bank2->configure_entries(0, 0x10, bios, 0x4000);
	m_bank2->configure_entries(0x80, 0x10, cart, 0x4000);
}

void gl3000s_state::machine_start()
{
	uint8_t *bios = memregion("bios")->base();
	memory_region *cart_region = memregion(std::string(m_cart->tag()) + GENERIC_ROM_REGION_TAG);
	uint8_t *cart = (cart_region != nullptr) ? cart_region->base() : memregion("bios")->base();

	m_bank0->configure_entries(0, 0x20, bios, 0x4000);
	m_bank1->configure_entries(0, 0x20, bios, 0x4000);
	m_bank2->configure_entries(0, 0x20, bios, 0x4000);
	m_bank2->configure_entries(0x80, 0x10, cart, 0x4000);

	m_lev_out.resolve();
	m_try_out.resolve();
	m_tick_out.resolve();
	m_time_out.resolve();
	m_points_out.resolve();
}

void gl4004_state::machine_start()
{
	uint8_t *bios = memregion("bios")->base();
	memory_region *cart_region = memregion(std::string(m_cart->tag()) + GENERIC_ROM_REGION_TAG);
	uint8_t *cart = (cart_region != nullptr) ? cart_region->base() : memregion("bios")->base();

	m_bank0->configure_entries(0, 0x20, bios, 0x4000);
	m_bank1->configure_entries(0, 0x20, bios, 0x4000);
	m_bank2->configure_entries(0, 0x20, bios, 0x4000);
	m_bank2->configure_entries(0x80, 0x10, cart, 0x4000);
}

void pc2000_state::machine_reset()
{
	//set the initial bank
	m_bank0->set_entry(0);
	m_bank1->set_entry(0);
	m_bank2->set_entry(0);
}

void pc2000_state::pc2000_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}

static const gfx_layout hd44780_charlayout =
{
	5, 8,                   /* 5 x 8 characters */
	256,                    /* 256 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	{ 3, 4, 5, 6, 7},
	{ 0, 8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8},
	8*8                     /* 8 bytes */
};

static GFXDECODE_START( gfx_pc2000 )
	GFXDECODE_ENTRY( "hd44780:cgrom", 0x0000, hd44780_charlayout, 0, 1 )
GFXDECODE_END


DEVICE_IMAGE_LOAD_MEMBER( pc2000_state::cart_load )
{
	uint32_t const size = m_cart->common_get_size("rom");

	// we always allocate a 0x40000 region, even if most carts span only 0x20000,
	// because the bankswitch code accesses up to 16 x 16K banks...
	m_cart->rom_alloc(0x40000, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

void pc2000_state::pc2000gen(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'000'000)); /* probably not accurate */
	m_maincpu->set_addrmap(AS_PROGRAM, &pc2000_state::pc2000_mem);
	m_maincpu->set_addrmap(AS_IO, &pc2000_state::pc2000_io);
	m_maincpu->set_periodic_int(FUNC(pc2000_state::irq0_line_hold), attotime::from_hz(50));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_screen_update("hd44780", FUNC(hd44780_device::screen_update));
	m_screen->set_size(120, 18); //2x20 chars
	m_screen->set_visarea_full();
	m_screen->set_palette("palette");

	PALETTE(config, "palette", FUNC(pc2000_state::pc2000_palette), 2);
	GFXDECODE(config, "gfxdecode", "palette", gfx_pc2000);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 3250).add_route(ALL_OUTPUTS, "mono", 1.00);

	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "genius_cart").set_device_load(FUNC(pc2000_state::cart_load));

	SOFTWARE_LIST(config, "pc1000_cart").set_compatible("pc1000");
}

void pc2000_state::pc2000(machine_config &config)
{
	pc2000gen(config);

	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 20);
}

void pc2000_state::pc2000eur(machine_config &config)
{
	pc2000gen(config);

	SED1278(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_default_bios_tag("0b");
	m_lcdc->set_lcd_size(2, 20);
}

void pc2000_state::gl2000(machine_config &config)
{
	pc2000(config);

	SOFTWARE_LIST(config, "cart_list").set_original("gl2000");
	SOFTWARE_LIST(config, "misterx_cart").set_compatible("misterx");
}

HD44780_PIXEL_UPDATE(gl4004_state::gl4000_pixel_update)
{
	if (pos < 40)
	{
		static const uint8_t gl4000_display_layout[] =
		{
			0x00, 0x01, 0x02, 0x03, 0x28, 0x29, 0x2a, 0x2b, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x2c, 0x2d, 0x2e, 0x2f,
			0x30, 0x31, 0x32, 0x33, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
			0x14, 0x15, 0x16, 0x17, 0x3c, 0x3d, 0x3e, 0x3f, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x40, 0x41, 0x42, 0x43,
			0x44, 0x45, 0x46, 0x47, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
		};

		uint8_t char_pos = gl4000_display_layout[line*40 + pos];
		bitmap.pix((char_pos / 20) * 9 + y, (char_pos % 20) * 6 + x) = state;
	}
}

void gl3000s_state::gl3000s(machine_config &config)
{
	pc2000(config);

	m_maincpu->set_addrmap(AS_IO, &gl3000s_state::gl3000s_io);

	config.device_remove("hd44780");
	SED1520(config, "sed1520_l").set_screen_update_cb(FUNC(gl3000s_state::screen_update_left));  // left panel is 59 pixels (0-58)
	SED1520(config, "sed1520_r").set_screen_update_cb(FUNC(gl3000s_state::screen_update_right)); // right panel is 61 pixels (59-119)

	m_screen->set_size(120, 24);
	m_screen->set_visarea_full();
	m_screen->set_screen_update(FUNC(gl3000s_state::screen_update));

	config.set_default_layout(layout_gl3000s);
	config.device_remove("gfxdecode");

	SOFTWARE_LIST(config, "gl2000_cart").set_compatible("gl2000");
	SOFTWARE_LIST(config, "misterx_cart").set_compatible("misterx");
}

void gl3000s_state::gl5000(machine_config &config)
{
	gl3000s(config);

	m_maincpu->set_addrmap(AS_IO, &gl3000s_state::gl5000_io);
	m_maincpu->remove_periodic_int(); // IM 2 vectored, not understood yet

	m_screen->set_size(120, 40);
	m_screen->set_visarea_full();

	subdevice<sed1520_device>("sed1520_l")->set_screen_update_cb(FUNC(gl3000s_state::gl5000_screen_update_left));
	subdevice<sed1520_device>("sed1520_r")->set_screen_update_cb(FUNC(gl3000s_state::gl5000_screen_update_right));
}

void gl4004_state::gl4000(machine_config &config)
{
	pc2000(config);

	m_screen->set_size(120, 36); // 4x20 chars
	m_screen->set_visarea_full();

	m_lcdc->set_lcd_size(4, 20);
	m_lcdc->set_pixel_update_cb(FUNC(gl4004_state::gl4000_pixel_update));

	SOFTWARE_LIST(config, "gl2000_cart").set_compatible("gl2000");
	SOFTWARE_LIST(config, "misterx_cart").set_compatible("misterx");
}

/* ROM definition */
ROM_START( pc2000 )
	ROM_REGION( 0x40000, "bios", 0 )
	ROM_LOAD( "lh532hee_9344_d.u4", 0x000000, 0x040000, CRC(0b03bf33) SHA1(cb344b94b14975c685041d3e669f386e8a21909f))
ROM_END

ROM_START( pc2000s )
	ROM_REGION( 0x40000, "bios", 0 )
	ROM_LOAD( "lh531h74_9527_d.u4", 0x000000, 0x040000, CRC(b729d10a) SHA1(b0260649c9337f3cdf0fe619e6c8fa50f7b4803a))
ROM_END

ROM_START( gl2000 )
	ROM_REGION( 0x40000, "bios", 0 )
	ROM_LOAD( "lh532hez_9416_d.bin", 0x000000, 0x040000, CRC(532f219e) SHA1(4044f0cf098087af4cc9d1b2a80c3c9ec06f154e))
ROM_END

ROM_START( gl2000c )
	ROM_REGION( 0x40000, "bios", 0 )
	ROM_LOAD( "27-5491-00", 0x000000, 0x020000, CRC(cbb9fe90) SHA1(a2a7a8afb027fe764a5998e3b35e87f291c24df1))
ROM_END

ROM_START( gl2000p )
	ROM_REGION( 0x40000, "bios", 0 )
	ROM_LOAD( "27-5615-00_9534_d.bin", 0x000000, 0x040000, CRC(481c1000) SHA1(da6f60e5bb25145ec5239310296bedaabeeaee28))
ROM_END

ROM_START( gl3000s )
	ROM_REGION(0x40000, "bios", 0)
	ROM_LOAD( "27-5713-00", 0x000000, 0x040000, CRC(18b113e0) SHA1(27a12893c38068efa35a99fa97a260dbfbd497e3) )
ROM_END

ROM_START( gl4000 )
	ROM_REGION(0x80000, "bios", 0)
	ROM_LOAD( "27-5480-00",   0x000000, 0x040000, CRC(8de047d3) SHA1(bb1d869954773bb7b8b51caa54531015d6b751ec) )
ROM_END

ROM_START( gl4004 )
	ROM_REGION(0x80000, "bios", 0)
	ROM_LOAD( "27-5762-00.u2", 0x000000, 0x080000, CRC(fb242f0f) SHA1(aae1beeb94873e29920726ad35475641d9f1e94e) )
ROM_END

ROM_START( gl5000 )
	ROM_REGION(0x80000, "bios", 0)
	ROM_LOAD( "27-5912-00.u1", 0x000000, 0x080000, CRC(9fe4c04a) SHA1(823d1d46e49e21f921260296874bc3ee5f718a5f) )
ROM_END

ROM_START( gl5005x )
	ROM_REGION(0x200000, "bios", 0)
	ROM_LOAD( "27-6426-00.u1", 0x000000, 0x200000, CRC(adde3581) SHA1(80f2bde7c5c339534614f24a9ca6ea362ee2f816) )
ROM_END

ROM_START( glpn )
	ROM_REGION( 0x200000, "bios", 0 )
	ROM_LOAD( "27-5755-01.u1", 0x00000, 0x80000, CRC(dc28346b) SHA1(148fe664bef5b2f68c6702c74462802b76900ca0) )
ROM_END

ROM_START( gmtt )
	ROM_REGION(0x100000, "bios", 0)
	ROM_LOAD( "27-6154-00.u4", 0x000000, 0x100000, CRC(e908262d) SHA1(a7964c9f9d304b6b2cce61822e8c6151b50388be) )
ROM_END

ROM_START( gbs5505x )
	ROM_REGION(0x200000, "bios", 0)
	ROM_LOAD( "27-7006-00.u5", 0x000000, 0x200000, CRC(28af3ca7) SHA1(5df7063c7327263c23d5ac2aac3aa66f7e0821c5) )
ROM_END

ROM_START( lexipcm )
	ROM_REGION( 0x200000, "bios", 0 )
	ROM_LOAD( "epoxy.u3", 0x00000, 0x100000, CRC(0a410790) SHA1(be04d5f74208a2f3b200daed75e04e966f64b545) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT  COMPAT MACHINE    INPUT    CLASS         INIT        COMPANY                    FULLNAME                                  FLAGS
COMP( 1993, pc2000,   0,      0,     pc2000,    pc2000,  pc2000_state, empty_init, "Video Technology",        "PreComputer 2000",                       MACHINE_NOT_WORKING )
COMP( 1993, pc2000s,  pc2000, 0,     pc2000eur, pc2000,  pc2000_state, empty_init, "Video Technology",        "PreComputer 2000 (Spain)",               MACHINE_NOT_WORKING )
COMP( 1993, gl2000,   0,      0,     gl2000,    pc2000,  pc2000_state, empty_init, "Video Technology",        "Genius Leader 2000",                     MACHINE_NOT_WORKING )
COMP( 1994, gl2000c,  gl2000, 0,     gl2000,    pc2000,  pc2000_state, empty_init, "Video Technology",        "Genius Leader 2000 Compact",             MACHINE_NOT_WORKING )
COMP( 1995, gl2000p,  gl2000, 0,     gl2000,    pc2000,  pc2000_state, empty_init, "Video Technology",        "Genius Leader 2000 Plus",                MACHINE_NOT_WORKING )
COMP( 1996, gl3000s,  0,      0,     gl3000s,   gl3000s, gl3000s_state,empty_init, "Video Technology",        "Genius Leader 3000S (Germany)",          MACHINE_NOT_WORKING )
COMP( 1994, gl4000,   0,      0,     gl4000,    pc2000,  gl4004_state, empty_init, "Video Technology",        "Genius Leader 4000 Quadro (Germany)",    MACHINE_NOT_WORKING )
COMP( 1996, gl4004,   0,      0,     gl4000,    pc2000,  gl4004_state, empty_init, "Video Technology",        "Genius Leader 4004 Quadro L (Germany)",  MACHINE_NOT_WORKING )
COMP( 1997, gl5000,   0,      0,     gl5000,    gl3000s, gl3000s_state,empty_init, "Video Technology",        "Genius Leader 5000 (Germany)",           MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1997, gl5005x,  0,      0,     pc2000,    pc2000,  pc2000_state, empty_init, "Video Technology",        "Genius Leader 5005X (Germany)",          MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1997, glpn,     0,      0,     gl5000,    gl3000s, gl3000s_state,empty_init, "Video Technology",        "Genius Leader Power Notebook (Germany)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1998, gmtt ,    0,      0,     gl4000,    pc2000,  gl4004_state, empty_init, "Video Technology",        "Genius Master Table Top (Germany)",      MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 2001, gbs5505x, 0,      0,     pc2000,    pc2000,  pc2000_state, empty_init, "Video Technology",        "Genius BrainStation 5505X (Germany)",    MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1999, lexipcm,  0,      0,     pc2000,    pc2000,  pc2000_state, empty_init, "Lexibook",                "LexiPC Mega 2000 (Germany)",             MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



pc4.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

    VTech Laser PC4

    Driver by Sandro Ronco

    I found no documentations about this, if you have documentation or schematic
    please let me know.

    Notes:
    - the LCD controller seems similar to a HD44780, but with more ddram and
      uses the bit 0 and 2 of the "Function Set" (0x2?) that are unused in
      the specifications of the HD44780 datasheet.
    - INT and NMI interrupt lines cause "LOW BATTERY" signal
    - chargen are taken from other driver, a real dump is required

    More info:
        http://www.8bit-micro.com/laser.htm
        http://www.euskalnet.net/ingepal/images/Vtech_Laser_PC4_1988.jpg
        http://www.oldcomputermuseum.com/laser_pc4.html

    Some characters are corrupted in MAME (ok on real machine). These
    are ~ 4 \ @

****************************************************************************/


#include "emu.h"
#include "pc4.h"

#include "cpu/z80/z80.h"
#include "machine/rp5c01.h"

#include "screen.h"
#include "speaker.h"

#include "utf8.h"


uint8_t pc4_state::kb_r(offs_t offset)
{
	uint8_t data = 0xff;

	for (int line=0; line<8; line++)
	{
		if (!(offset & (1<<line)))
		{
			data &= io_port[line]->read();
		}
	}

	return data;
}

void pc4_state::bank_w(uint8_t data)
{
	//printf("set bank %x\n", data);
	m_rombank->set_entry(data&0x07);
}

void pc4_state::beep_w(uint8_t data)
{
	m_beep->set_state(data&0x40);
}

void pc4_state::pc4_mem(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	map(0x4000, 0x7fff).bankr("rombank");
	map(0x8000, 0xffff).ram();
}

void pc4_state::pc4_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x000f).rw("rtc", FUNC(rp5c01_device::read), FUNC(rp5c01_device::write));
	map(0x1000, 0x1000).w(FUNC(pc4_state::beep_w));
	map(0x1fff, 0x1fff).w(FUNC(pc4_state::bank_w));

	map(0x3000, 0x3000).w(FUNC(pc4_state::lcd_control_w));
	map(0x3001, 0x3001).w(FUNC(pc4_state::lcd_data_w));
	map(0x3002, 0x3002).r(FUNC(pc4_state::lcd_control_r));
	map(0x3003, 0x3003).r(FUNC(pc4_state::lcd_data_r));
	map(0x3005, 0x3005).w(FUNC(pc4_state::lcd_offset_w));

	//keyboard read, offset used as matrix
	map(0x5000, 0x50ff).r(FUNC(pc4_state::kb_r));
}

static INPUT_PORTS_START( pc4 )
	PORT_START("LINE0")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_UP)  PORT_CODE(KEYCODE_UP)           PORT_CHAR(UCHAR_MAMEKEY(UP))
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER")  PORT_CODE(KEYCODE_ENTER)        PORT_CHAR(13)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ESC")    PORT_CODE(KEYCODE_ESC)          PORT_CHAR(UCHAR_MAMEKEY(ESC))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT")  PORT_CODE(KEYCODE_LSHIFT)       PORT_CHAR(UCHAR_SHIFT_1)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL")   PORT_CODE(KEYCODE_LCONTROL)     PORT_CHAR(UCHAR_SHIFT_2)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q")      PORT_CODE(KEYCODE_Q)            PORT_CHAR('q')  PORT_CHAR('Q')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1")      PORT_CODE(KEYCODE_1)            PORT_CHAR('1')  PORT_CHAR('!')  PORT_CHAR('|')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE1")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",")      PORT_CODE(KEYCODE_COMMA)        PORT_CHAR(',')  PORT_CHAR('/')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K")      PORT_CODE(KEYCODE_K)            PORT_CHAR('k')  PORT_CHAR('K')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CAPSLOCK")   PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z")      PORT_CODE(KEYCODE_Z)            PORT_CHAR('z')  PORT_CHAR('Z')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A")      PORT_CODE(KEYCODE_A)            PORT_CHAR('a')  PORT_CHAR('A')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W")      PORT_CODE(KEYCODE_W)            PORT_CHAR('w')  PORT_CHAR('W')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2")      PORT_CODE(KEYCODE_2)            PORT_CHAR('2')  PORT_CHAR('@')  PORT_CHAR('`')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE2")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".")      PORT_CODE(KEYCODE_STOP)         PORT_CHAR('.')  PORT_CHAR('?')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O")      PORT_CODE(KEYCODE_O)            PORT_CHAR('o')  PORT_CHAR('O')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[")      PORT_CODE(KEYCODE_OPENBRACE)    PORT_CHAR('[')  PORT_CHAR('{')  PORT_CHAR('<')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X")      PORT_CODE(KEYCODE_X)            PORT_CHAR('x')  PORT_CHAR('X')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S")      PORT_CODE(KEYCODE_S)            PORT_CHAR('s')  PORT_CHAR('S')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E")      PORT_CODE(KEYCODE_E)            PORT_CHAR('e')  PORT_CHAR('E')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3")      PORT_CODE(KEYCODE_3)            PORT_CHAR('3')  PORT_CHAR('#')  PORT_CHAR('~')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE3")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";")      PORT_CODE(KEYCODE_COLON)        PORT_CHAR(';')  PORT_CHAR(':')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-")      PORT_CODE(KEYCODE_MINUS)        PORT_CHAR('-')  PORT_CHAR('=')  PORT_CHAR('_')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]")      PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_CHAR(']')  PORT_CHAR('}')  PORT_CHAR('>')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C")      PORT_CODE(KEYCODE_C)            PORT_CHAR('c')  PORT_CHAR('C')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D")      PORT_CODE(KEYCODE_D)            PORT_CHAR('d')  PORT_CHAR('D')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R")      PORT_CODE(KEYCODE_R)            PORT_CHAR('r')  PORT_CHAR('R')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4")      PORT_CODE(KEYCODE_4)            PORT_CHAR('4')  PORT_CHAR('$')  PORT_CHAR('&')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE4")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("'")      PORT_CODE(KEYCODE_QUOTE)        PORT_CHAR('\'') PORT_CHAR('\"')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Delete") PORT_CODE(KEYCODE_BACKSPACE)    PORT_CHAR(UCHAR_MAMEKEY(DEL),8)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE")  PORT_CODE(KEYCODE_SPACE)        PORT_CHAR(' ')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V")      PORT_CODE(KEYCODE_V)            PORT_CHAR('v')  PORT_CHAR('V')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F")      PORT_CODE(KEYCODE_F)            PORT_CHAR('f')  PORT_CHAR('F')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T")      PORT_CODE(KEYCODE_T)            PORT_CHAR('t')  PORT_CHAR('T')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5")      PORT_CODE(KEYCODE_5)            PORT_CHAR('5')  PORT_CHAR('%')  PORT_CHAR('\\')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE5")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L")      PORT_CODE(KEYCODE_L)            PORT_CHAR('l')  PORT_CHAR('L')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P")      PORT_CODE(KEYCODE_P)            PORT_CHAR('p')  PORT_CHAR('P')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT)        PORT_CHAR(UCHAR_MAMEKEY(LEFT))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B")      PORT_CODE(KEYCODE_B)            PORT_CHAR('b')  PORT_CHAR('B')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G")      PORT_CODE(KEYCODE_G)            PORT_CHAR('g')  PORT_CHAR('G')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y")      PORT_CODE(KEYCODE_Y)            PORT_CHAR('y')  PORT_CHAR('Y')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6")      PORT_CODE(KEYCODE_6)            PORT_CHAR('6')  PORT_CHAR('^')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE6")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0")      PORT_CODE(KEYCODE_0)            PORT_CHAR('0')  PORT_CHAR(')')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N")      PORT_CODE(KEYCODE_N)            PORT_CHAR('n')  PORT_CHAR('N')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H")      PORT_CODE(KEYCODE_H)            PORT_CHAR('h')  PORT_CHAR('H')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U")      PORT_CODE(KEYCODE_U)            PORT_CHAR('u')  PORT_CHAR('U')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7")      PORT_CODE(KEYCODE_7)            PORT_CHAR('7')  PORT_CHAR('+')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE7")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NumLock") PORT_CODE(KEYCODE_NUMLOCK)     PORT_CHAR(UCHAR_MAMEKEY(NUMLOCK))
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9")      PORT_CODE(KEYCODE_9)            PORT_CHAR('9')  PORT_CHAR('(')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN)        PORT_CHAR(UCHAR_MAMEKEY(DOWN))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M")      PORT_CODE(KEYCODE_M)            PORT_CHAR('m')  PORT_CHAR('M')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J")      PORT_CODE(KEYCODE_J)            PORT_CHAR('j')  PORT_CHAR('J')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I")      PORT_CODE(KEYCODE_I)            PORT_CHAR('i')  PORT_CHAR('I')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8")      PORT_CODE(KEYCODE_8)            PORT_CHAR('8')  PORT_CHAR('*')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

void pc4_state::pc4_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}

static const gfx_layout pc4_charlayout =
{
	5, 8,                   /* 5 x 8 characters */
	256,                    /* 256 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	{ 3, 4, 5, 6, 7},
	{ 0, 8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8},
	8*8                     /* 8 bytes */
};

static GFXDECODE_START( gfx_pc4 )
	GFXDECODE_ENTRY( "charset", 0x0000, pc4_charlayout, 0, 1 )
GFXDECODE_END

void pc4_state::machine_start()
{
	static const char *const bitnames[] = {"LINE0", "LINE1", "LINE2", "LINE3", "LINE4", "LINE5", "LINE6", "LINE7"};
	uint8_t* rom_base = (uint8_t *)memregion("maincpu")->base();

	m_rombank->configure_entries(0, 8, rom_base, 0x4000);
	m_rombank->set_entry(0);

	m_busy_timer = timer_alloc(FUNC(pc4_state::clear_busy_flag), this);
	m_blink_timer = timer_alloc(FUNC(pc4_state::blink_tick), this);
	m_blink_timer->adjust(attotime::from_msec(409), 0, attotime::from_msec(409));

	for (int i=0; i<8; i++)
	{
		io_port[i] = ioport(bitnames[i]);
	}

	m_ac = 0;
	m_ac_mode = 0;
	m_data_bus_flag = 0;
	m_cursor_pos = 0;
	m_display_on = 0;
	m_cursor_on = 0;
	m_shift_on = 0;
	m_blink_on = 0;
	m_direction = 1;
	m_disp_shift = 0;
	m_blink = 0;
	m_busy_flag = 0;
}

void pc4_state::pc4(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &pc4_state::pc4_mem);
	m_maincpu->set_addrmap(AS_IO, &pc4_state::pc4_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(72);
	screen.set_screen_update(FUNC(pc4_state::screen_update));
	screen.set_size(240, 36);
	screen.set_visarea(0, 240-1, 0, 36-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(pc4_state::pc4_palette), 2);
	GFXDECODE(config, "gfxdecode", "palette", gfx_pc4);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 3250).add_route(ALL_OUTPUTS, "mono", 1.00);

	RP5C01(config, "rtc", XTAL(32'768));
}

ROM_START( pc4 )
	ROM_REGION( 0x20000, "maincpu", 0 )
	ROM_LOAD( "laser pc4 v4.14a.u2", 0x00000, 0x20000, CRC(f8dabf5d) SHA1(6988517b3ccb42df2b8d6e1517ff04b24458d146) )
	ROM_REGION( 0x0860, "charset", ROMREGION_ERASE )
	ROM_LOAD( "44780a00.bin",    0x0000, 0x0860,  BAD_DUMP CRC(3a89024c) SHA1(5a87b68422a916d1b37b5be1f7ad0b3fb3af5a8d))
ROM_END

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY           FULLNAME     FLAGS
COMP( 1990, pc4,  0,      0,      pc4,     pc4,   pc4_state, empty_init, "Laser Computer", "Laser PC4", MACHINE_NOT_WORKING )



pc532.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * George Scolaro and Dave Rand's 532 Baby AT, aka pc532.
 *
 * Sources:
 *  - http://www.cpu-ns32k.net/PC532.html
 *  - https://www.netbsd.org/ports/pc532/faq.html
 *  - https://www.nic.funet.fi/pub/misc/pc532/
 *
 * TODO:
 *  - et532
 *
 * WIP:
 *  - NetBSD 1.5.3 boots from install floppy
 */

#include "emu.h"

// cpu cluster
#include "cpu/ns32000/ns32000.h"
#include "machine/ns32081.h"
#include "machine/ns32202.h"

// other devices
#include "machine/aic6250.h"
#include "machine/ds1215.h"
#include "machine/input_merger.h"
#include "machine/mc68681.h"
#include "machine/ncr5380.h"
#include "machine/nscsi_bus.h"

// busses and connectors
#include "bus/rs232/rs232.h"
#include "bus/nscsi/hd.h"

#define VERBOSE 0
#include "logmacro.h"

namespace {

class pc532_state : public driver_device
{
public:
	pc532_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_fpu(*this, "fpu")
		, m_icu(*this, "icu")
		, m_rtc(*this, "rtc")
		, m_dp8490(*this, "slot:7:dp8490")
		, m_aic6250(*this, "scsi:0:aic6250")
		, m_duart(*this, "duart%u", 0U)
		, m_serial(*this, "serial%u", 0U)
		, m_duar(*this, "duar%u", 0U)
		, m_eprom(*this, "eprom")
		, m_swap(*this, "swap")
		, m_select(*this, "select")
	{
	}

	void pc532(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	template <unsigned ST> void cpu_map(address_map &map) ATTR_COLD;

	required_device<ns32532_device> m_cpu;
	required_device<ns32381_device> m_fpu;
	required_device<ns32202_device> m_icu;

	required_device<ds1216e_device> m_rtc;

	required_device<dp8490_device> m_dp8490;
	required_device<aic6250_device> m_aic6250;

	required_device_array<scn2681_device, 4> m_duart;
	required_device_array<rs232_port_device, 8> m_serial;
	required_device_array<input_merger_any_low_device, 4> m_duar;

	required_region_ptr<u32> m_eprom;

private:
	void drq_w(int state);
	void irq_w(int state);
	u32 dma_r(offs_t offset, u32 mem_mask);
	void dma_w(offs_t offset, u32 data, u32 mem_mask);

	memory_view m_swap;
	memory_view m_select;

	bool m_drq;
	bool m_irq;
	u32 m_dma;
	enum dma_state : unsigned
	{
		IDLE, // pseudo-DMA buffer is empty

		WR1,  // 1 byte remains in output buffer
		WR2,  // 2 bytes remain in output buffer
		WR3,  // 3 bytes remain in output buffer

		RD1,  // 1 byte available in input buffer
		RD2,  // 2 bytes available in input buffer
		RD3,  // 3 bytes available in input buffer
		RD4,  // input buffer is full
	}
	m_state;
};

void pc532_state::machine_start()
{
	// install phantom rtc using memory taps
	// TODO: not tested
	m_cpu->space(AS_PROGRAM).install_read_tap(0x1000'0000, 0x1000'0007, "rtc_r",
		[this](offs_t offset, u32 &data, u32 mem_mask)
		{
			if (m_rtc->ceo_r())
				data = m_rtc->read(offset);
			else
				m_rtc->read(offset);
		});
}

void pc532_state::machine_reset()
{
	m_drq = false;
	m_irq = false;
	m_dma = 0;
	m_state = IDLE;

	// /int15 is tied high
	m_icu->ir_w<15>(1);
}

/*
 * The pc532 relies on the dynamic bus sizing and cycle extension features of
 * the NS32532 to implement CPU-driven pseudo-DMA for the SCSI controller(s).
 * In software, the CPU checks the DRQ and IRQ state of the SCSI controller
 * before performing doubleword accesses to the pseudo-DMA memory region, which
 * activates the necessary glue hardware. The hardware responds by signalling
 * an 8-bit dynamic bus size, which causes the CPU to execute a total of four
 * byte-wide accesses corresponding to each byte in the double word, while also
 * asserting the /RDY line as needed to synchronize the data transfers with the
 * DRQ line from the SCSI chip. /RDY is deasserted on SCSI interrupt, allowing
 * transfers of less than 4 bytes to be completed (e.g., at phase change).
 */
void pc532_state::drq_w(int state)
{
	if (state)
	{
		switch (m_state)
		{
		case RD1: m_dma |= u32(m_dp8490->dma_r()) << 8; m_state = RD2; break;
		case RD2: m_dma |= u32(m_dp8490->dma_r()) << 16; m_state = RD3; break;
		case RD3: m_dma |= u32(m_dp8490->dma_r()) << 24; m_state = RD4; break;

		case WR3: m_dp8490->dma_w(m_dma >> 8); m_state = WR2; break;
		case WR2: m_dp8490->dma_w(m_dma >> 16); m_state = WR1; break;
		case WR1: m_dp8490->dma_w(m_dma >> 24); m_state = IDLE; break;

		default:
			break;
		}
	}

	m_drq = state;
}

void pc532_state::irq_w(int state)
{
	if (state)
	{
		switch (m_state)
		{
		case RD1:
		case RD2:
		case RD3:
			// mark buffer full to enable reads of less than 4 bytes to complete
			m_state = RD4;
			break;

		case WR3:
		case WR2:
		case WR1:
			// discard untransferred buffer data for writes of less than 4 bytes
			m_state = IDLE;
			break;

		default:
			break;
		}
	}

	m_irq = state;
}

// TODO: byte and word accesses
// TODO: A22 -> EOP
u32 pc532_state::dma_r(offs_t offset, u32 mem_mask)
{
	u32 data = 0;

	if (!machine().side_effects_disabled())
	{
		switch (m_state)
		{
		case IDLE:
			if (m_drq && !m_irq)
			{
				// buffer empty and SCSI ready to transfer; read SCSI data, enter the read state, and signal the CPU to wait
				m_dma = m_dp8490->dma_r();
				m_state = RD1;

				m_cpu->rdy_w(1);
			}
			break;

		case RD1:
		case RD2:
		case RD3:
			// buffer partly full; signal the CPU to wait
			m_cpu->rdy_w(1);
			break;

		case RD4:
			// buffer full; return the data to the CPU and enter idle state
			data = m_dma;
			m_state = IDLE;
			break;

		default:
			break;
		}
	}

	return data;
}

// TODO: byte and word accesses
// TODO: A22 -> EOP
void pc532_state::dma_w(offs_t offset, u32 data, u32 mem_mask)
{
	if (m_state == IDLE)
	{
		if (m_drq)
		{
			m_dma = data;
			m_dp8490->dma_w(m_dma >> 0);
			m_state = WR3;
		}
	}
	else
		m_cpu->rdy_w(1);
}

template <unsigned ST> void pc532_state::cpu_map(address_map &map)
{
	if (ST == AS_PROGRAM)
	{
		map(0x0000'0000, 0x01ff'ffff).view(m_swap);
		m_swap[0](0x0000'0000, 0x01ff'ffff).ram().share("ram");
		m_swap[1](0x0000'0000, 0x0000'7fff).rom().region("eprom", 0);
	}
	else
		map(0x0000'0000, 0x01ff'ffff).ram().share("ram");

	map(0x1000'0000, 0x1000'7fff).rom().region("eprom", 0);

	map(0x2800'0000, 0x2800'000f).rw(m_duart[0], FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0x2800'0010, 0x2800'001f).rw(m_duart[1], FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0x2800'0020, 0x2800'002f).rw(m_duart[2], FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0x2800'0030, 0x2800'003f).rw(m_duart[3], FUNC(scn2681_device::read), FUNC(scn2681_device::write));

	map(0x2800'0050, 0x2800'0053).noprw(); // clear NMI

	if (ST == ns32000::ST_ODT)
	{
		map(0x3000'0000, 0x3fff'ffff).view(m_select);
		m_select[0](0x3000'0000, 0x3000'0007).m(m_dp8490, FUNC(dp8490_device::map));
		m_select[0](0x3800'0000, 0x3fff'ffff).rw(FUNC(pc532_state::dma_r), FUNC(pc532_state::dma_w));
		m_select[1](0x3000'0000, 0x3000'0001).rw(m_aic6250, FUNC(aic6250_device::read), FUNC(aic6250_device::write));
	}

	map(0xffff'fe00, 0xffff'feff).m(m_icu, FUNC(ns32202_device::map<BIT(ST, 1)>));
}

static void scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
}

void pc532_state::pc532(machine_config &config)
{
	NS32532(config, m_cpu, 50_MHz_XTAL / 2);
	m_cpu->set_addrmap(AS_PROGRAM, &pc532_state::cpu_map<0>);
	m_cpu->set_addrmap(ns32000::ST_IAM, &pc532_state::cpu_map<ns32000::ST_IAM>);
	// ODT required because system software uses explicit MOV instead of RETI instructions
	m_cpu->set_addrmap(ns32000::ST_ODT, &pc532_state::cpu_map<ns32000::ST_ODT>);

	NS32381(config, m_fpu, 50_MHz_XTAL / 2);
	m_cpu->set_fpu(m_fpu);

	NS32202(config, m_icu, 3.6864_MHz_XTAL);
	m_icu->out_int().set_inputline(m_cpu, INPUT_LINE_IRQ0).invert();
	m_icu->out_g<0>().set([this](int state) { m_swap.select(state); });
	m_icu->out_g<7>().set([this](int state) { m_select.select(state); });

	DS1216E(config, m_rtc);

	NSCSI_BUS(config, "slot");
	NSCSI_CONNECTOR(config, "slot:0", scsi_devices, "harddisk", false);
	NSCSI_CONNECTOR(config, "slot:1", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "slot:2", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "slot:3", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "slot:7").option_set("dp8490", DP8490).machine_config( // DP8490V
		[this](device_t *device)
		{
			dp8490_device &dp8490(downcast<dp8490_device &>(*device));

			dp8490.drq_handler().set(*this, FUNC(pc532_state::drq_w));
			dp8490.irq_handler().append(m_icu, FUNC(ns32202_device::ir_w<4>));
			dp8490.irq_handler().append(*this, FUNC(pc532_state::irq_w));
		});

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0").option_set("aic6250", AIC6250).machine_config(
		[this](device_t *device)
		{
			aic6250_device &aic6250(downcast<aic6250_device &>(*device));

			aic6250.set_clock(20_MHz_XTAL);
			aic6250.int_cb().set(m_icu, FUNC(ns32202_device::ir_w<5>));
			// TODO: drq
		});
	NSCSI_CONNECTOR(config, "scsi:1", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:2", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:3", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:4", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:5", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:6", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:7", scsi_devices, nullptr, false);

	SCN2681(config, m_duart[0], 3.6864_MHz_XTAL).irq_cb().set(m_icu, FUNC(ns32202_device::ir_w<13>)).invert();
	SCN2681(config, m_duart[1], 3.6864_MHz_XTAL).irq_cb().set(m_icu, FUNC(ns32202_device::ir_w<11>)).invert();
	SCN2681(config, m_duart[2], 3.6864_MHz_XTAL).irq_cb().set(m_icu, FUNC(ns32202_device::ir_w<9>)).invert();
	SCN2681(config, m_duart[3], 3.6864_MHz_XTAL).irq_cb().set(m_icu, FUNC(ns32202_device::ir_w<7>)).invert();

	RS232_PORT(config, m_serial[0], default_rs232_devices, "terminal");
	RS232_PORT(config, m_serial[1], default_rs232_devices, nullptr);
	RS232_PORT(config, m_serial[2], default_rs232_devices, nullptr);
	RS232_PORT(config, m_serial[3], default_rs232_devices, nullptr);
	RS232_PORT(config, m_serial[4], default_rs232_devices, nullptr);
	RS232_PORT(config, m_serial[5], default_rs232_devices, nullptr);
	RS232_PORT(config, m_serial[6], default_rs232_devices, nullptr);
	RS232_PORT(config, m_serial[7], default_rs232_devices, nullptr);

	INPUT_MERGER_ANY_LOW(config, m_duar[0]).output_handler().set(m_icu, FUNC(ns32202_device::ir_w<12>)).invert();
	INPUT_MERGER_ANY_LOW(config, m_duar[1]).output_handler().set(m_icu, FUNC(ns32202_device::ir_w<10>)).invert();
	INPUT_MERGER_ANY_LOW(config, m_duar[2]).output_handler().set(m_icu, FUNC(ns32202_device::ir_w<8>)).invert();
	INPUT_MERGER_ANY_LOW(config, m_duar[3]).output_handler().set(m_icu, FUNC(ns32202_device::ir_w<6>)).invert();

	for (unsigned i = 0; i < std::size(m_duart); i++)
	{
		// channel A outputs
		m_duart[i]->a_tx_cb().set(m_serial[i * 2 + 0], FUNC(rs232_port_device::write_txd));
		m_duart[i]->outport_cb().append(m_serial[i * 2 + 0], FUNC(rs232_port_device::write_rts)).bit(0);
		m_duart[i]->outport_cb().append(m_serial[i * 2 + 0], FUNC(rs232_port_device::write_dtr)).bit(2);
		m_duart[i]->outport_cb().append(m_duar[i], FUNC(input_merger_any_low_device::in_w<0>)).bit(4);

		// channel A inputs
		m_serial[i * 2 + 0]->rxd_handler().set(m_duart[i], FUNC(scn2681_device::rx_a_w));
		m_serial[i * 2 + 0]->cts_handler().set(m_duart[i], FUNC(scn2681_device::ip0_w));
		m_serial[i * 2 + 0]->dcd_handler().set(m_duart[i], FUNC(scn2681_device::ip3_w));

		// channel B outputs
		m_duart[i]->b_tx_cb().set(m_serial[i * 2 + 1], FUNC(rs232_port_device::write_txd));
		m_duart[i]->outport_cb().append(m_serial[i * 2 + 1], FUNC(rs232_port_device::write_rts)).bit(1);
		m_duart[i]->outport_cb().append(m_serial[i * 2 + 1], FUNC(rs232_port_device::write_dtr)).bit(3);
		m_duart[i]->outport_cb().append(m_duar[i], FUNC(input_merger_any_low_device::in_w<1>)).bit(5);

		// channel B inputs
		m_serial[i * 2 + 1]->rxd_handler().set(m_duart[i], FUNC(scn2681_device::rx_b_w));
		m_serial[i * 2 + 1]->cts_handler().set(m_duart[i], FUNC(scn2681_device::ip1_w));
		m_serial[i * 2 + 1]->dcd_handler().set(m_duart[i], FUNC(scn2681_device::ip2_w));
	}
}

ROM_START(pc532)
	ROM_REGION32_LE(0x8000, "eprom", 0)

	ROM_SYSTEM_BIOS(0, "911113-9600", "Wed Nov 13 21:45:02 1991, Phil Nelson, 9600bps")
	ROMX_LOAD("911105_9600.u44", 0x0000, 0x8000, CRC(e927cac7) SHA1(90bf5d0e1e86f2a75f7abd4ced7edf8794fa89e5), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "900328-9600", "Wed Mar 28 09:31:00 PST 1990, Bruce Culbertson, 9600bps")
	ROMX_LOAD("900328_9600.u44", 0x0000, 0x8000, CRC(63caac86) SHA1(5c7011684b1bce3dd6b5fcf3c81479e40c61c4e3), ROM_BIOS(1))
ROM_END

} // anonymous namespace

/*   YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY           FULLNAME  FLAGS */
COMP(1989, pc532, 0,      0,      pc532,   0,     pc532_state, empty_init, "George Scolaro", "pc532",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)



pc6001.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
/**************************************************************************************************

    PC-6001 series (c) 1981 NEC

    driver by Angelo Salese

    TODO:
    - Move MCU HLE simulation in a device or even make an handcrafted LLE version
      (assuming we'll never get the MCS48 dump). Additionally remove the 8255 hacks;
    - Proper support for .cas/.wav/.p6/.p6t file formats used by the cassette interface;
    - Make FDC to actually load images, also fix .dsk identification;
    - Confirm irq model daisy chain behaviour, and add missing irqs and features
      (namely the irq dispatch for SR mode should really honor I/O $fb and fallback to legacy
       behaviour if masked);
    - Several games are decidedly too fast, down to missing waitstates, no screen raw
      parameters, crtkill signal and bus request;
    - PC-6001mkII: refactor memory model to use address_map_bank_device;
    - PC-6001mkII: confirm optional FDC used mapped at 0xd0-0xd3
      (PC-6031? It looks like a 5'25 single drive with 8255 protocol, presumably earlier revision
      of PC-80S31 with no dump available);
    - PC-6601: current regression caused by an internal FDC sense interrupt status that expects a
      DIO high that never occurs;
    - PC-6601: mon r-0 type games doesn't seem to work at all on this system?
    - PC-6001SR: Implement MK-2 compatibility mode via view handler(s)
      (it changes the memory map to behave like the older versions);
    - Merge PC-6001 video emulation with MC6847 (is it really one or rather a M5C6847P-1?);
    - Pinpoint what VDG supersets PC-6001mkII and SR models really uses;
    - upd7752 voice speech device needs to be properly emulated (device is currently a skeleton),
      Chrith game is a good test case, it's supposed to talk before title screen;
    - Video Telopper (superimposer) & TV tuner functions for later machines;

    TODO (game specific):
    - (several AX* games, namely Galaxy Mission Part 1/2 and others): inputs doesn't work;
    - AX6 - Demo: When AY-based speech talks, other emus emulates the screen drawing to be
       a solid green (plain PC-6001) or solid white (Mk2 version), but according to an
       original video reference, that screen should actually some kind of weird garbage on it;
    - AX6 - Powered Knight: doesn't work too well, according to the asm code it asks the
       player to press either 'B' or 'C' then a number but nothing is shown on screen,
       other emus behaves the same, bad dump?
    (Mk2 mode 5 games)
    - 3D Golf Simulation Super Version: gameplay / inputs seems broken;
    - American Truck: Screen is offset at the loading screen, loading bug?
    - Castle Excellent: copyright text drawing is quite bogus, scans text in vertical
       instead of horizontal?
    - Dezeni Land (ALL versions) / Hurry Fox 1/2: asks you to "load something", can't do it
       with current cassette kludge, also, for Dezeni Land(s) keyboard irqs doesn't seem to
       work too well with halt opcode execution?
    - Dezeni Land 1/4: dies after loading of main program;
    - Dezeni Land 2: dies at the "load something" screen with presumably wrong stack opcodes
    - (MyCom BASIC games with multiple files): most of them refuses to run ... how to load them?
    - Grobda: when "get ready" speech plays, screen should be full white but instead it's all
       black, same issue as AX-6 Demo?
    - Pac-Man / Tiny Xevious 2: gameplay is too fast (unrelated with timer irq);
    - Salad no Kunino Tomato-Hime: can't start a play;
    - Space Harrier: very sensitive with sub irq triggers, keyboard joy triggers doesn't work
      properly (select F1 after loading), draws garbage on vanilla pc6001 and eventually crashes
      MAME;
    - The Black Onyx: dies when it attempts to save the character, that obviously means saving
       on the tape;
    - Yakyukyo / Punchball Mario: waits for an irq (fixed, wrong timer enable behaviour);

===================================================================================================

    PC-6001 (1981-09):

     * CPU: Z80A @ 4 MHz
     * ROM: 16KB + 4KB (chargen) - no kanji
     * RAM: 16KB, it can be expanded to 32KB
     * Text Mode: 32x16 and 2 colors
     * Graphic Modes: 64x48 (9 colors), 128x192 (4 colors), 256x192 (2 colors)
     * Sound: BEEP + PSG - Optional Voice Synth Cart
     * Keyboard: JIS Keyboard with 5 function keys, control key, TAB key,
            HOME/CLR key, INS key, DEL key, GRAPH key, Japanese syllabary
            key, page key, STOP key, and cursor key (4 directions)
     * 1 cartslot, optional floppy drive, optional serial 232 port, 2
            joystick ports


    PC-6001 mkII (1983-07):

     * CPU: Z80A @ 4 MHz
     * ROM: 32KB + 16KB (chargen) + 32KB (kanji) + 16KB (Voice Synth)
     * RAM: 64KB
     * Text Mode: same as PC-6001 with N60-BASIC; 40x20 and 15 colors with
            N60M-BASIC
     * Graphic Modes: same as PC-6001 with N60-BASIC; 80x40 (15 colors),
            160x200 (15 colors), 320x200 (4 colors) with N60M-BASIC
     * Sound: BEEP + PSG
     * Keyboard: JIS Keyboard with 5 function keys, control key, TAB key,
            HOME/CLR key, INS key, DEL key, CAPS key, GRAPH key, Japanese
            syllabary key, page key, mode key, STOP key, and cursor key (4
            directions)
     * 1 cartslot, floppy drive, optional serial 232 port, 2 joystick ports


    PC-6001 mkIISR (1984-12):

     * CPU: Z80A @ 3.58 MHz
     * ROM: 64KB + 16KB (chargen) + 32KB (kanji) + 32KB (Voice Synth)
     * RAM: 64KB
     * Text Mode: same as PC-6001/PC-6001mkII with N60-BASIC; 40x20, 40x25,
            80x20, 80x25 and 15 colors with N66SR-BASIC
     * Graphic Modes: same as PC-6001/PC-6001mkII with N60-BASIC; 80x40 (15 colors),
            320x200 (15 colors), 640x200 (15 colors) with N66SR-BASIC
     * Sound: BEEP + PSG + FM
     * Keyboard: JIS Keyboard with 5 function keys, control key, TAB key,
            HOME/CLR key, INS key, DEL key, CAPS key, GRAPH key, Japanese
            syllabary key, page key, mode key, STOP key, and cursor key (4
            directions)
     * 1 cartslot, floppy drive, optional serial 232 port, 2 joystick ports


    info from http://www.geocities.jp/retro_zzz/machines/nec/6001/spc60.html

===================================================================================================

PC-6001 irq table:
irq vector 0x00: writes 0x00 to [$fa19]                                                     ;(unused)
irq vector 0x02: (A = 0, B = 0) tests ppi port c, does something with ay ports (plus more?) ;keyboard data ready, no kanji lock, no caps lock
irq vector 0x04:                                                                            ;uart irq
irq vector 0x06: operates with $fa28, $fa2e, $fd1b                                          ;timer irq
irq vector 0x08: tests ppi port c, puts port A to $fa1d,puts 0x02 to [$fa19]                ;tape data ready
irq vector 0x0a: writes 0x00 to [$fa19]                                                     ;(unused)
irq vector 0x0c: writes 0x00 to [$fa19]                                                     ;(unused)
irq vector 0x0e: same as 2, (A = 0x03, B = 0x00)                                            ;keyboard data ready, unknown type
irq vector 0x10: same as 2, (A = 0x03, B = 0x00)                                            ;(unused)
irq vector 0x12: writes 0x10 to [$fa19]                                                     ;end of tape reached
irq vector 0x14: same as 2, (A = 0x00, B = 0x01)                                            ;keyboard control irq (function keys)
irq vector 0x16: tests ppi port c, writes the result to $feca.                              ;joystick / sub irq trigger
irq vector 0x18:                                                                            ;TVR (?)
irq vector 0x1a:                                                                            ;Date
irq vector 0x1c:                                                                            ;(unused)
irq vector 0x1e:                                                                            ;(unused)
irq vector 0x20:                                                                            ;uPD7752 voice irq
irq vector 0x22:                                                                            ;VRTC (SR only?)
irq vector 0x24:                                                                            ;(unused)
irq vector 0x26:                                                                            ;(unused)

***************************************************************************************************/

#include "emu.h"
#include "pc6001.h"

#include "softlist_dev.h"

#define LOG_IRQ    (1U << 1)

//#define VERBOSE (LOG_IRQ)
#define VERBOSE (0)
#include "logmacro.h"

#define LOGIRQ(...)     LOGMASKED(LOG_IRQ, __VA_ARGS__)

inline void pc6001_state::cassette_latch_control(bool new_state)
{
	// 0 -> 1 transition: send PLAY tape cmd to i8049
	if((!(m_sys_latch & 8)) && new_state == true) //PLAY tape cmd
	{
		m_cas_switch = 1;
		//m_cassette->change_state(CASSETTE_MOTOR_ENABLED,CASSETTE_MASK_MOTOR);
		//m_cassette->change_state(CASSETTE_PLAY,CASSETTE_MASK_UISTATE);
	}
	// 1 -> 0 transition: send STOP tape cmd to i8049
	if((m_sys_latch & 8) && new_state == false) //STOP tape cmd
	{
		m_cas_switch = 0;
		//m_cassette->change_state(CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR);
		//m_cassette->change_state(CASSETTE_STOPPED,CASSETTE_MASK_UISTATE);
	}
}

// TODO: this is explicitly needed for making all machines to boot
inline void pc6001_state::ppi_control_hack_w(uint8_t data)
{
	if(data & 1)
		m_port_c_8255 |=   1<<((data>>1)&0x07);
	else
		m_port_c_8255 &= ~(1<<((data>>1)&0x07));

#if 0
	// this switch-case is overwritten below anyway!?
	switch(data)
	{
		case 0x08: m_port_c_8255 |= 0x88; break;
		case 0x09: m_port_c_8255 &= 0xf7; break;
		case 0x0c: m_port_c_8255 |= 0x28; break;
		case 0x0d: m_port_c_8255 &= 0xf7; break;
		default: break;
	}
#endif

	m_port_c_8255 |= 0xa8;
}

inline void pc6001_state::set_subcpu_irq_vector(u8 vector_num)
{
	// TODO: if anything the new vector must never override an old one
	// sub CPU INT pin is directly connected to the OBFA (PC7) on PPI while WR is connected to INTRA (PC3),
	// former should act as an handshake flag to inhibit further irqs until current is processed.
	// (Also note tight loops in main CPU code that checks against masks 0x28 or 0x88 for I/O $92)
	LOGIRQ("%s: sub CPU new irq vector old: %02x new: %02x\n", machine().describe_context(), m_sub_vector, vector_num);
	m_sub_vector = vector_num;
	set_irq_level(SUB_CPU_IRQ);
}

inline void pc6001_state::set_irq_level(int which)
{
	m_irq_pending |= 1 << which;
	m_maincpu->set_input_line(0, ASSERT_LINE);
	LOGIRQ("%s: assert %d, state %02x\n", machine().describe_context(), which, m_irq_pending);
}

void pc6001_state::system_latch_w(uint8_t data)
{
	static const uint16_t startaddr[] = {0xC000, 0xE000, 0x8000, 0xA000 };

	m_video_base = &m_ram[startaddr[(data >> 1) & 0x03] - 0x8000];

	cassette_latch_control((data & 8) == 8);
	m_sys_latch = data;

	m_timer_enable = !(data & 1);
	set_timer_divider();
}


uint8_t pc6001_state::nec_ppi8255_r(offs_t offset)
{
	if (offset==2)
		return m_port_c_8255;
	else if(offset==0)
	{
		uint8_t res;
		res = m_cur_keycode;
		//m_cur_keycode = 0;
		return res;
	}

	return m_ppi->read(offset);
}

void pc6001_state::nec_ppi8255_w(offs_t offset, uint8_t data)
{
	if (offset==3)
	{
		ppi_control_hack_w(data);
		//printf("%02x\n",data);

		if ((data & 0x0f) == 0x05 && m_cart_rom)
			m_bank1->set_base(m_cart_rom->base() + 0x2000);
		if ((data & 0x0f) == 0x04)
			m_bank1->set_base(m_region_gfx1->base());
	}

	m_ppi->write(offset,data);
}

uint8_t pc6001_state::joystick_r()
{
	uint8_t data = m_joymux->output_r();

	// FIXME: bits 6 and 7 are supposed to be nHSYNC and nVSYNC
	if (m_screen->hblank())
		data &= 0xbf;
	else
		data |= 0x40;
	if (m_screen->vblank())
		data &= 0x7f;
	else
		data |= 0x80;

	return data;
}

uint8_t pc6001_state::joystick_out_r()
{
	return m_joystick_out;
}

void pc6001_state::joystick_out_w(uint8_t data)
{
	// bit 7 is output enable for first part of 74LS367 buffer
	m_joy[1]->pin_6_w(BIT(data, 7) ? 1 : BIT(data, 0));
	m_joy[1]->pin_7_w(BIT(data, 7) ? 1 : BIT(data, 1));
	m_joy[0]->pin_6_w(BIT(data, 7) ? 1 : BIT(data, 2));
	m_joy[0]->pin_7_w(BIT(data, 7) ? 1 : BIT(data, 3));
	m_joy[1]->pin_8_w(BIT(data, 4));
	m_joy[0]->pin_8_w(BIT(data, 5));
	m_joymux->select_w(BIT(data, 6));

	m_joystick_out = data;
}

void pc6001_state::pc6001_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).rom().nopw();
//  map(0x4000, 0x5fff) // mapped by the cartslot
	map(0x6000, 0x7fff).bankr("bank1");
	map(0x8000, 0xffff).ram().share("ram");
}

void pc6001_state::pc6001_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x80, 0x81).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x90, 0x93).mirror(0x0c).rw(FUNC(pc6001_state::nec_ppi8255_r), FUNC(pc6001_state::nec_ppi8255_w));
	map(0xa0, 0xa0).mirror(0x0c).w(m_ay, FUNC(ay8910_device::address_w));
	map(0xa1, 0xa1).mirror(0x0c).w(m_ay, FUNC(ay8910_device::data_w));
	map(0xa2, 0xa2).mirror(0x0c).r(m_ay, FUNC(ay8910_device::data_r));
	map(0xa3, 0xa3).mirror(0x0c).nopw();
	map(0xb0, 0xb0).mirror(0x0f).w(FUNC(pc6001_state::system_latch_w));
}

/*****************************************
 *
 * PC-6001Mk2 specific i/o
 *
 ****************************************/

/*
    ROM_REGION( 0x28000, "maincpu", ROMREGION_ERASEFF )
    ROM_LOAD( "basicrom.62", 0x10000, 0x8000, CRC(950ac401) SHA1(fbf195ba74a3b0f80b5a756befc96c61c2094182) )
    ROM_LOAD( "voicerom.62", 0x18000, 0x4000, CRC(49b4f917) SHA1(1a2d18f52ef19dc93da3d65f19d3abbd585628af) )
    ROM_LOAD( "cgrom60.62",  0x1c000, 0x2000, CRC(81eb5d95) SHA1(53d8ae9599306ff23bf95208d2f6cc8fed3fc39f) )
    ROM_LOAD( "cgrom60m.62", 0x1e000, 0x2000, CRC(3ce48c33) SHA1(f3b6c63e83a17d80dde63c6e4d86adbc26f84f79) )
    ROM_LOAD( "kanjirom.62", 0x20000, 0x8000, CRC(20c8f3eb) SHA1(4c9f30f0a2ebbe70aa8e697f94eac74d8241cadd) )
*/

#define BASICROM(_v_) \
	0x10000+0x2000*_v_
#define VOICEROM(_v_) \
	0x18000+0x2000*_v_
#define TVROM(_v_) \
	0x1c000+0x2000*_v_
#define KANJIROM(_v_) \
	0x20000+0x2000*_v_
#define WRAM(_v_) \
	0x28000+0x2000*_v_
#define EXWRAM(_v_) \
	0x38000+0x2000*_v_
#define EXROM(_v_) \
	0x48000+0x2000*_v_
#define INVALID(_v_) \
	0x4c000+0x2000*_v_
// TODO: rewrite using bankdev
// TODO: some comments aren't right
static const uint32_t banksw_table_r0[0x10*4][4] = {
	/* 0 */
	{ INVALID(0),   INVALID(0),     INVALID(0),     INVALID(0)  },  //0x00: <invalid setting>
	{ BASICROM(0),  BASICROM(1),    BASICROM(2),    BASICROM(3) },  //0x01: basic rom 0 & 1 / basic rom 2 & 3
	{ TVROM(0),     TVROM(1),       VOICEROM(0),    VOICEROM(1) },  //0x02: tv rom 0 & 1 / voice rom 0 & 1
	{ EXROM(1),     EXROM(1),       EXROM(1),       EXROM(1)    },  //0x03: ex rom 1 & 1 / ex rom 1 & 1
	{ EXROM(0),     EXROM(0),       EXROM(0),       EXROM(0)    },  //0x04: ex rom 0 & 0 / ex rom 0 & 0
	{ TVROM(1),     BASICROM(1),    VOICEROM(0),    BASICROM(3) },  //0x05: tv rom 1 & basic rom 1 / voice rom 0 & basic 3
	{ BASICROM(0),  TVROM(2),       BASICROM(2),    VOICEROM(1) },  //0x06: basic rom 0 & tv rom 2 / basic rom 2 & voice 1
	{ EXROM(0),     EXROM(1),       EXROM(0),       EXROM(1)    },  //0x07: ex rom 0 & ex rom 1 / ex rom 0 & ex rom 1
	{ EXROM(1),     EXROM(0),       EXROM(1),       EXROM(0)    },  //0x08: ex rom 1 & ex rom 0 / ex rom 1 & ex rom 0
	{ EXROM(1),     BASICROM(1),    EXROM(1),       BASICROM(3) },  //0x09: ex rom 1 & basic rom 1 / ex rom 1 & basic 3
	{ BASICROM(0),  EXROM(1),       BASICROM(2),    EXROM(1)    },  //0x0a: basic rom 0 & ex rom 1 / basic rom 2 & ex rom 1
	{ EXROM(0),     TVROM(2),       EXROM(0),       VOICEROM(1) },  //0x0b: ex rom 0 & tv rom 2 / ex rom 0 & voice 1
	{ TVROM(1),     EXROM(0),       VOICEROM(0),    EXROM(0)    },  //0x0c: tv rom 1 & ex rom 0 / voice rom 0 & ex rom 0
	{ WRAM(0),      WRAM(1),        WRAM(2),        WRAM(3)     },  //0x0d: ram 0 & 1 / ram 2 & 3
	{ EXWRAM(0),    EXWRAM(1),      EXWRAM(2),      EXWRAM(3)   },  //0x0e: exram 0 & 1 / exram 2 & 3
	{ INVALID(0),   INVALID(0),     INVALID(0),     INVALID(0)  },  //0x0f: <invalid setting>
	/* 1 */
	{ INVALID(0),   INVALID(0),     INVALID(0),     INVALID(0)  },  //0x00: <invalid setting>
	{ BASICROM(0),  BASICROM(1),    BASICROM(2),    BASICROM(3) },  //0x01: basic rom 0 & 1 / basic rom 2 & 3
	{ KANJIROM(0),  KANJIROM(1),    KANJIROM(0),    KANJIROM(1) },  //0x02: tv rom 0 & 1 / voice rom 0 & 1
	{ EXROM(1),     EXROM(1),       EXROM(1),       EXROM(1)    },  //0x03: ex rom 1 & 1 / ex rom 1 & 1
	{ EXROM(0),     EXROM(0),       EXROM(0),       EXROM(0)    },  //0x04: ex rom 0 & 0 / ex rom 0 & 0
	{ KANJIROM(0),  BASICROM(1),    KANJIROM(0),    BASICROM(3) },  //0x05: tv rom 1 & basic rom 1 / voice rom 0 & basic 3
	{ BASICROM(0),  KANJIROM(1),    BASICROM(2),    KANJIROM(1) },  //0x06: basic rom 0 & tv rom 2 / basic rom 2 & voice 1
	{ EXROM(0),     EXROM(1),       EXROM(0),       EXROM(1)    },  //0x07: ex rom 0 & ex rom 1 / ex rom 0 & ex rom 1
	{ EXROM(1),     EXROM(0),       EXROM(1),       EXROM(0)    },  //0x08: ex rom 1 & ex rom 0 / ex rom 1 & ex rom 0
	{ EXROM(1),     BASICROM(1),    EXROM(1),       BASICROM(3) },  //0x09: ex rom 1 & basic rom 1 / ex rom 1 & basic 3
	{ BASICROM(0),  EXROM(1),       BASICROM(2),    EXROM(1)    },  //0x0a: basic rom 0 & ex rom 1 / basic rom 2 & ex rom 1
	{ EXROM(0),     KANJIROM(1),    EXROM(0),       KANJIROM(1) },  //0x0b: ex rom 0 & tv rom 2 / ex rom 0 & voice 1
	{ KANJIROM(0),  EXROM(0),       KANJIROM(0),    EXROM(0)    },  //0x0c: tv rom 1 & ex rom 0 / voice rom 0 & ex rom 0
	{ WRAM(0),      WRAM(1),        WRAM(2),        WRAM(3)     },  //0x0d: ram 0 & 1 / ram 2 & 3
	{ EXWRAM(0),    EXWRAM(1),      EXWRAM(2),      EXWRAM(3)   },  //0x0e: exram 0 & 1 / exram 2 & 3
	{ INVALID(0),   INVALID(0),     INVALID(0),     INVALID(0)  },  //0x0f: <invalid setting>
	/* 2 */
	{ INVALID(0),   INVALID(0),     INVALID(0),     INVALID(0)  },  //0x00: <invalid setting>
	{ BASICROM(0),  BASICROM(1),    BASICROM(2),    BASICROM(3) },  //0x01: basic rom 0 & 1 / basic rom 2 & 3
	{ TVROM(0),     TVROM(1),       VOICEROM(0),    VOICEROM(1) },  //0x02: tv rom 0 & 1 / voice rom 0 & 1
	{ EXROM(1),     EXROM(1),       EXROM(1),       EXROM(1)    },  //0x03: ex rom 1 & 1 / ex rom 1 & 1
	{ EXROM(0),     EXROM(0),       EXROM(0),       EXROM(0)    },  //0x04: ex rom 0 & 0 / ex rom 0 & 0
	{ TVROM(1),     BASICROM(1),    VOICEROM(0),    BASICROM(3) },  //0x05: tv rom 1 & basic rom 1 / voice rom 0 & basic 3
	{ BASICROM(0),  TVROM(2),       BASICROM(2),    VOICEROM(1) },  //0x06: basic rom 0 & tv rom 2 / basic rom 2 & voice 1
	{ EXROM(0),     EXROM(1),       EXROM(0),       EXROM(1)    },  //0x07: ex rom 0 & ex rom 1 / ex rom 0 & ex rom 1
	{ EXROM(1),     EXROM(0),       EXROM(1),       EXROM(0)    },  //0x08: ex rom 1 & ex rom 0 / ex rom 1 & ex rom 0
	{ EXROM(1),     BASICROM(1),    EXROM(1),       BASICROM(3) },  //0x09: ex rom 1 & basic rom 1 / ex rom 1 & basic 3
	{ BASICROM(0),  EXROM(1),       BASICROM(2),    EXROM(1)    },  //0x0a: basic rom 0 & ex rom 1 / basic rom 2 & ex rom 1
	{ EXROM(0),     TVROM(2),       EXROM(0),       VOICEROM(1) },  //0x0b: ex rom 0 & tv rom 2 / ex rom 0 & voice 1
	{ TVROM(1),     EXROM(0),       VOICEROM(0),    EXROM(0)    },  //0x0c: tv rom 1 & ex rom 0 / voice rom 0 & ex rom 0
	{ WRAM(0),      WRAM(1),        WRAM(2),        WRAM(3)     },  //0x0d: ram 0 & 1 / ram 2 & 3
	{ EXWRAM(0),    EXWRAM(1),      EXWRAM(2),      EXWRAM(3)   },  //0x0e: exram 0 & 1 / exram 2 & 3
	{ INVALID(0),   INVALID(0),     INVALID(0),     INVALID(0)  },  //0x0f: <invalid setting>
	/* 3 */
	{ INVALID(0),   INVALID(0),     INVALID(0),     INVALID(0)  },  //0x00: <invalid setting>
	{ BASICROM(0),  BASICROM(1),    BASICROM(2),    BASICROM(3) },  //0x01: basic rom 0 & 1 / basic rom 2 & 3
	{ KANJIROM(2),  KANJIROM(3),    KANJIROM(2),    KANJIROM(3) },  //0x02: tv rom 0 & 1 / voice rom 0 & 1
	{ EXROM(1),     EXROM(1),       EXROM(1),       EXROM(1)    },  //0x03: ex rom 1 & 1 / ex rom 1 & 1
	{ EXROM(0),     EXROM(0),       EXROM(0),       EXROM(0)    },  //0x04: ex rom 0 & 0 / ex rom 0 & 0
	{ KANJIROM(2),  BASICROM(1),    KANJIROM(2),    BASICROM(3) },  //0x05: tv rom 1 & basic rom 1 / voice rom 0 & basic 3
	{ BASICROM(0),  KANJIROM(3),    BASICROM(2),    KANJIROM(3) },  //0x06: basic rom 0 & tv rom 2 / basic rom 2 & voice 1
	{ EXROM(0),     EXROM(1),       EXROM(0),       EXROM(1)    },  //0x07: ex rom 0 & ex rom 1 / ex rom 0 & ex rom 1
	{ EXROM(1),     EXROM(0),       EXROM(1),       EXROM(0)    },  //0x08: ex rom 1 & ex rom 0 / ex rom 1 & ex rom 0
	{ EXROM(1),     BASICROM(1),    EXROM(1),       BASICROM(3) },  //0x09: ex rom 1 & basic rom 1 / ex rom 1 & basic 3
	{ BASICROM(0),  EXROM(1),       BASICROM(2),    EXROM(1)    },  //0x0a: basic rom 0 & ex rom 1 / basic rom 2 & ex rom 1
	{ EXROM(0),     KANJIROM(3),    EXROM(0),       KANJIROM(3) },  //0x0b: ex rom 0 & tv rom 2 / ex rom 0 & voice 1
	{ KANJIROM(2),  EXROM(0),       KANJIROM(2),    EXROM(0)    },  //0x0c: tv rom 1 & ex rom 0 / voice rom 0 & ex rom 0
	{ WRAM(0),      WRAM(1),        WRAM(2),        WRAM(3)     },  //0x0d: ram 0 & 1 / ram 2 & 3
	{ EXWRAM(0),    EXWRAM(1),      EXWRAM(2),      EXWRAM(3)   },  //0x0e: exram 0 & 1 / exram 2 & 3
	{ INVALID(0),   INVALID(0),     INVALID(0),     INVALID(0)  }   //0x0f: <invalid setting>
};

static const uint32_t banksw_table_r1[0x10*4][4] = {
	/* 0 */
	{ INVALID(0),   INVALID(0),     INVALID(0),     INVALID(0)  },  //0x00: <invalid setting>
	{ BASICROM(0),  BASICROM(1),    BASICROM(2),    BASICROM(3) },  //0x01: basic rom 0 & 1 / basic rom 2 & 3
	{ VOICEROM(0),  VOICEROM(1),    VOICEROM(0),    VOICEROM(1) },  //0x02: voice rom 0 & 1 / voice rom 0 & 1
	{ EXROM(1),     EXROM(1),       EXROM(1),       EXROM(1)    },  //0x03: ex rom 1 & 1 / ex rom 1 & 1
	{ EXROM(0),     EXROM(0),       EXROM(0),       EXROM(0)    },  //0x04: ex rom 0 & 0 / ex rom 0 & 0
	{ VOICEROM(0),  BASICROM(1),    VOICEROM(0),    BASICROM(3) },  //0x05: voice rom 0 & basic rom 1 / voice rom 0 & basic 3
	{ BASICROM(0),  VOICEROM(1),    BASICROM(2),    VOICEROM(1) },  //0x06: basic rom 0 & voice rom 1 / basic rom 2 & voice 1
	{ EXROM(0),     EXROM(1),       EXROM(0),       EXROM(1)    },  //0x07: ex rom 0 & ex rom 1 / ex rom 0 & ex rom 1
	{ EXROM(1),     EXROM(0),       EXROM(1),       EXROM(0)    },  //0x08: ex rom 1 & ex rom 0 / ex rom 1 & ex rom 0
	{ EXROM(1),     BASICROM(1),    EXROM(1),       BASICROM(3) },  //0x09: ex rom 1 & basic rom 1 / ex rom 1 & basic 3
	{ BASICROM(0),  EXROM(1),       BASICROM(2),    EXROM(1)    },  //0x0a: basic rom 0 & ex rom 1 / basic rom 2 & ex rom 1
	{ EXROM(0),     VOICEROM(1),    EXROM(0),       VOICEROM(1) },  //0x0b: ex rom 0 & voice rom 1 / ex rom 0 & voice 1
	{ VOICEROM(0),  EXROM(0),       VOICEROM(0),    EXROM(0)    },  //0x0c: voice rom 1 & ex rom 0 / voice rom 0 & ex rom 0
	{ WRAM(4),      WRAM(5),        WRAM(6),        WRAM(7)     },  //0x0d: ram 4 & 5 / ram 6 & 7
	{ EXWRAM(4),    EXWRAM(5),      EXWRAM(6),      EXWRAM(7)   },  //0x0e: exram 4 & 5 / exram 6 & 7
	{ INVALID(0),   INVALID(0),     INVALID(0),     INVALID(0)  },  //0x0f: <invalid setting>
	/* 1 */
	{ INVALID(0),   INVALID(0),     INVALID(0),     INVALID(0)  },  //0x00: <invalid setting>
	{ BASICROM(0),  BASICROM(1),    BASICROM(2),    BASICROM(3) },  //0x01: basic rom 0 & 1 / basic rom 2 & 3
	{ KANJIROM(0),  KANJIROM(1),    KANJIROM(0),    KANJIROM(1) },  //0x02: kanji rom 0 & 1 / kanji rom 0 & 1
	{ EXROM(1),     EXROM(1),       EXROM(1),       EXROM(1)    },  //0x03: ex rom 1 & 1 / ex rom 1 & 1
	{ EXROM(0),     EXROM(0),       EXROM(0),       EXROM(0)    },  //0x04: ex rom 0 & 0 / ex rom 0 & 0
	{ KANJIROM(0),  BASICROM(1),    KANJIROM(0),    BASICROM(3) },  //0x05: voice rom 0 & basic rom 1 / voice rom 0 & basic 3
	{ BASICROM(0),  KANJIROM(1),    BASICROM(2),    KANJIROM(1) },  //0x06: basic rom 0 & voice rom 1 / basic rom 2 & voice 1
	{ EXROM(0),     EXROM(1),       EXROM(0),       EXROM(1)    },  //0x07: ex rom 0 & ex rom 1 / ex rom 0 & ex rom 1
	{ EXROM(1),     EXROM(0),       EXROM(1),       EXROM(0)    },  //0x08: ex rom 1 & ex rom 0 / ex rom 1 & ex rom 0
	{ EXROM(1),     BASICROM(1),    EXROM(1),       BASICROM(3) },  //0x09: ex rom 1 & basic rom 1 / ex rom 1 & basic 3
	{ BASICROM(0),  EXROM(1),       BASICROM(2),    EXROM(1)    },  //0x0a: basic rom 0 & ex rom 1 / basic rom 2 & ex rom 1
	{ EXROM(0),     KANJIROM(1),    EXROM(0),       KANJIROM(1) },  //0x0b: ex rom 0 & voice rom 1 / ex rom 0 & voice 1
	{ KANJIROM(0),  EXROM(0),       KANJIROM(0),    EXROM(0)    },  //0x0c: voice rom 1 & ex rom 0 / voice rom 0 & ex rom 0
	{ WRAM(4),      WRAM(5),        WRAM(6),        WRAM(7)     },  //0x0d: ram 4 & 5 / ram 6 & 7
	{ EXWRAM(4),    EXWRAM(5),      EXWRAM(6),      EXWRAM(7)   },  //0x0e: exram 4 & 5 / exram 6 & 7
	{ INVALID(0),   INVALID(0),     INVALID(0),     INVALID(0)  },  //0x0f: <invalid setting>
	/* 2 */
	{ INVALID(0),   INVALID(0),     INVALID(0),     INVALID(0)  },  //0x00: <invalid setting>
	{ BASICROM(0),  BASICROM(1),    BASICROM(2),    BASICROM(3) },  //0x01: basic rom 0 & 1 / basic rom 2 & 3
	{ VOICEROM(0),  VOICEROM(1),    VOICEROM(0),    VOICEROM(1) },  //0x02: voice rom 0 & 1 / voice rom 0 & 1
	{ EXROM(1),     EXROM(1),       EXROM(1),       EXROM(1)    },  //0x03: ex rom 1 & 1 / ex rom 1 & 1
	{ EXROM(0),     EXROM(0),       EXROM(0),       EXROM(0)    },  //0x04: ex rom 0 & 0 / ex rom 0 & 0
	{ VOICEROM(0),  BASICROM(1),    VOICEROM(0),    BASICROM(3) },  //0x05: voice rom 0 & basic rom 1 / voice rom 0 & basic 3
	{ BASICROM(0),  VOICEROM(1),    BASICROM(2),    VOICEROM(1) },  //0x06: basic rom 0 & voice rom 1 / basic rom 2 & voice 1
	{ EXROM(0),     EXROM(1),       EXROM(0),       EXROM(1)    },  //0x07: ex rom 0 & ex rom 1 / ex rom 0 & ex rom 1
	{ EXROM(1),     EXROM(0),       EXROM(1),       EXROM(0)    },  //0x08: ex rom 1 & ex rom 0 / ex rom 1 & ex rom 0
	{ EXROM(1),     BASICROM(1),    EXROM(1),       BASICROM(3) },  //0x09: ex rom 1 & basic rom 1 / ex rom 1 & basic 3
	{ BASICROM(0),  EXROM(1),       BASICROM(2),    EXROM(1)    },  //0x0a: basic rom 0 & ex rom 1 / basic rom 2 & ex rom 1
	{ EXROM(0),     VOICEROM(1),    EXROM(0),       VOICEROM(1) },  //0x0b: ex rom 0 & voice rom 1 / ex rom 0 & voice 1
	{ VOICEROM(0),  EXROM(0),       VOICEROM(0),    EXROM(0)    },  //0x0c: voice rom 1 & ex rom 0 / voice rom 0 & ex rom 0
	{ WRAM(4),      WRAM(5),        WRAM(6),        WRAM(7)     },  //0x0d: ram 4 & 5 / ram 6 & 7
	{ EXWRAM(4),    EXWRAM(5),      EXWRAM(6),      EXWRAM(7)   },  //0x0e: exram 4 & 5 / exram 6 & 7
	{ INVALID(0),   INVALID(0),     INVALID(0),     INVALID(0)  },  //0x0f: <invalid setting>
	/* 3 */
	{ INVALID(0),   INVALID(0),     INVALID(0),     INVALID(0)  },  //0x00: <invalid setting>
	{ BASICROM(0),  BASICROM(1),    BASICROM(2),    BASICROM(3) },  //0x01: basic rom 0 & 1 / basic rom 2 & 3
	{ KANJIROM(2),  KANJIROM(3),    KANJIROM(2),    KANJIROM(3) },  //0x02: kanji rom 0 & 1 / kanji rom 0 & 1
	{ EXROM(1),     EXROM(1),       EXROM(1),       EXROM(1)    },  //0x03: ex rom 1 & 1 / ex rom 1 & 1
	{ EXROM(0),     EXROM(0),       EXROM(0),       EXROM(0)    },  //0x04: ex rom 0 & 0 / ex rom 0 & 0
	{ KANJIROM(2),  BASICROM(1),    KANJIROM(2),    BASICROM(3) },  //0x05: voice rom 0 & basic rom 1 / voice rom 0 & basic 3
	{ BASICROM(0),  KANJIROM(3),    BASICROM(2),    KANJIROM(3) },  //0x06: basic rom 0 & voice rom 1 / basic rom 2 & voice 1
	{ EXROM(0),     EXROM(1),       EXROM(0),       EXROM(1)    },  //0x07: ex rom 0 & ex rom 1 / ex rom 0 & ex rom 1
	{ EXROM(1),     EXROM(0),       EXROM(1),       EXROM(0)    },  //0x08: ex rom 1 & ex rom 0 / ex rom 1 & ex rom 0
	{ EXROM(1),     BASICROM(1),    EXROM(1),       BASICROM(3) },  //0x09: ex rom 1 & basic rom 1 / ex rom 1 & basic 3
	{ BASICROM(0),  EXROM(1),       BASICROM(2),    EXROM(1)    },  //0x0a: basic rom 0 & ex rom 1 / basic rom 2 & ex rom 1
	{ EXROM(0),     KANJIROM(3),    EXROM(0),       KANJIROM(3) },  //0x0b: ex rom 0 & voice rom 1 / ex rom 0 & voice 1
	{ KANJIROM(2),  EXROM(0),       KANJIROM(2),    EXROM(0)    },  //0x0c: voice rom 1 & ex rom 0 / voice rom 0 & ex rom 0
	{ WRAM(4),      WRAM(5),        WRAM(6),        WRAM(7)     },  //0x0d: ram 4 & 5 / ram 6 & 7
	{ EXWRAM(4),    EXWRAM(5),      EXWRAM(6),      EXWRAM(7)   },  //0x0e: exram 4 & 5 / exram 6 & 7
	{ INVALID(0),   INVALID(0),     INVALID(0),     INVALID(0)  }   //0x0f: <invalid setting>
};

void pc6001mk2_state::mk2_bank_r0_w(uint8_t data)
{
	uint8_t *ROM = m_region_maincpu->base();
	uint8_t *gfx_data = m_region_gfx1->base();

//  bankaddress = 0x10000 + (0x4000 * ((data & 0x40)>>6));
//  membank(1)->set_base(&ROM[bankaddress]);

	m_bank_r0 = data;

//  printf("%02x BANK | %02x\n",data,m_bank_opt);
	m_bank1->set_base(&ROM[banksw_table_r0[(data & 0xf)+(m_bank_opt*0x10)][0]]);
	m_bank2->set_base(&ROM[banksw_table_r0[(data & 0xf)+(m_bank_opt*0x10)][1]]);
	m_bank3->set_base(&ROM[banksw_table_r0[((data & 0xf0)>>4)+(m_bank_opt*0x10)][2]]);
	if(!m_gfx_bank_on)
		m_bank4->set_base(&ROM[banksw_table_r0[((m_bank_r0 & 0xf0)>>4)+(m_bank_opt*0x10)][3]]);
	else
		m_bank4->set_base(&gfx_data[m_cgrom_bank_addr]);
}

void pc6001mk2_state::mk2_bank_r1_w(uint8_t data)
{
	uint8_t *ROM = m_region_maincpu->base();

//  bankaddress = 0x10000 + (0x4000 * ((data & 0x40)>>6));
//  membank(1)->set_base(&ROM[bankaddress]);

	m_bank_r1 = data;

//  printf("%02x BANK\n",data);
	m_bank5->set_base(&ROM[banksw_table_r1[(data & 0xf)+(m_bank_opt*0x10)][0]]);
	m_bank6->set_base(&ROM[banksw_table_r1[(data & 0xf)+(m_bank_opt*0x10)][1]]);
	m_bank7->set_base(&ROM[banksw_table_r1[((data & 0xf0)>>4)+(m_bank_opt*0x10)][2]]);
	m_bank8->set_base(&ROM[banksw_table_r1[((data & 0xf0)>>4)+(m_bank_opt*0x10)][3]]);
}

void pc6001mk2_state::mk2_bank_w0_w(uint8_t data)
{
	m_bank_w = data;
}

void pc6001mk2_state::mk2_opt_bank_w(uint8_t data)
{
	uint8_t *ROM = m_region_maincpu->base();
	uint8_t *gfx_data = m_region_gfx1->base();

	/*
	0 - TVROM / VOICE ROM
	1 - KANJI ROM bank 0
	2 - KANJI ROM bank 1
	3 - TVROM / VOICE ROM
	*/
	m_bank_opt = data & 3;

	m_bank1->set_base(&ROM[banksw_table_r0[(m_bank_r0 & 0xf)+(m_bank_opt*0x10)][0]]);
	m_bank2->set_base(&ROM[banksw_table_r0[(m_bank_r0 & 0xf)+(m_bank_opt*0x10)][1]]);
	m_bank3->set_base(&ROM[banksw_table_r0[((m_bank_r0 & 0xf0)>>4)+(m_bank_opt*0x10)][2]]);
	if(!m_gfx_bank_on)
		m_bank4->set_base(&ROM[banksw_table_r0[((m_bank_r0 & 0xf0)>>4)+(m_bank_opt*0x10)][3]]);
	else
		m_bank4->set_base(&gfx_data[m_cgrom_bank_addr]);
	m_bank4->set_base(&ROM[banksw_table_r0[((m_bank_r0 & 0xf0)>>4)+(m_bank_opt*0x10)][3]]);
	m_bank5->set_base(&ROM[banksw_table_r1[(m_bank_r1 & 0xf)+(m_bank_opt*0x10)][0]]);
	m_bank6->set_base(&ROM[banksw_table_r1[(m_bank_r1 & 0xf)+(m_bank_opt*0x10)][1]]);
	m_bank7->set_base(&ROM[banksw_table_r1[((m_bank_r1 & 0xf0)>>4)+(m_bank_opt*0x10)][2]]);
	m_bank8->set_base(&ROM[banksw_table_r1[((m_bank_r1 & 0xf0)>>4)+(m_bank_opt*0x10)][3]]);

}

void pc6001mk2_state::mk2_work_ram0_w(offs_t offset, uint8_t data)
{
	uint8_t *ROM = m_region_maincpu->base();
	ROM[offset+((m_bank_w & 0x01) ? WRAM(0) : EXWRAM(0))] = data;
}

void pc6001mk2_state::mk2_work_ram1_w(offs_t offset, uint8_t data)
{
	uint8_t *ROM = m_region_maincpu->base();
	ROM[offset+((m_bank_w & 0x01) ? WRAM(1) : EXWRAM(1))] = data;
}

void pc6001mk2_state::mk2_work_ram2_w(offs_t offset, uint8_t data)
{
	uint8_t *ROM = m_region_maincpu->base();
	ROM[offset+((m_bank_w & 0x04) ? WRAM(2) : EXWRAM(2))] = data;
}

void pc6001mk2_state::mk2_work_ram3_w(offs_t offset, uint8_t data)
{
	uint8_t *ROM = m_region_maincpu->base();
	ROM[offset+((m_bank_w & 0x04) ? WRAM(3) : EXWRAM(3))] = data;
}

void pc6001mk2_state::mk2_work_ram4_w(offs_t offset, uint8_t data)
{
	uint8_t *ROM = m_region_maincpu->base();
	ROM[offset+((m_bank_w & 0x10) ? WRAM(4) : EXWRAM(4))] = data;
}

void pc6001mk2_state::mk2_work_ram5_w(offs_t offset, uint8_t data)
{
	uint8_t *ROM = m_region_maincpu->base();
	ROM[offset+((m_bank_w & 0x10) ? WRAM(5) : EXWRAM(5))] = data;
}

void pc6001mk2_state::mk2_work_ram6_w(offs_t offset, uint8_t data)
{
	uint8_t *ROM = m_region_maincpu->base();
	ROM[offset+((m_bank_w & 0x40) ? WRAM(6) : EXWRAM(6))] = data;
}

void pc6001mk2_state::mk2_work_ram7_w(offs_t offset, uint8_t data)
{
	uint8_t *ROM = m_region_maincpu->base();
	ROM[offset+((m_bank_w & 0x40) ? WRAM(7) : EXWRAM(7))] = data;
}


void pc6001mk2_state::necmk2_ppi8255_w(offs_t offset, uint8_t data)
{
	if (offset==3)
	{
		ppi_control_hack_w(data);

		if((data & 0x0f) == 0x05)
		{
			uint8_t *ROM = m_region_maincpu->base();

			m_gfx_bank_on = 0;
			m_bank4->set_base(&ROM[banksw_table_r0[((m_bank_r0 & 0xf0)>>4)+(m_bank_opt*0x10)][3]]);
		}

		if((data & 0x0f) == 0x04)
		{
			uint8_t *gfx_data = m_region_gfx1->base();

			m_gfx_bank_on = 1;
			m_bank4->set_base(&gfx_data[m_cgrom_bank_addr]);
		}
	}

	m_ppi->write(offset,data);
}

void pc6001mk2_state::vram_bank_change(uint8_t vram_bank)
{
	uint32_t bank_base_values[8] = { 0x8000, 0xc000, 0xc000, 0xe000, 0x0000, 0x8000, 0x4000, 0xa000 };
	uint8_t vram_bank_index = ((vram_bank & 0x60) >> 4) | ((vram_bank & 2) >> 1);
//  uint8_t *work_ram = m_region_maincpu->base();

//  bit 2 of vram_bank sets up 4 color mode
	set_videoram_bank(0x28000 + bank_base_values[vram_bank_index]);

//  popmessage("%02x",vram_bank);
}

void pc6001mk2_state::mk2_system_latch_w(uint8_t data)
{
	cassette_latch_control((data & 8) == 8);
	m_sys_latch = data;

	m_timer_enable = !(data & 1);
	set_timer_divider();

	vram_bank_change((m_ex_vram_bank & 0x06) | ((m_sys_latch & 0x06) << 4));
}

inline void pc6001mk2_state::refresh_crtc_params()
{
	/* Apparently bitmap modes changes the screen res to 320 x 200 */
	rectangle visarea = m_screen->visible_area();
	int y_height;

	y_height = (m_exgfx_bitmap_mode || m_exgfx_2bpp_mode) ? 200 : 240;

	visarea.set(0, (320) - 1, 0, (y_height) - 1);

	m_screen->configure(m_screen->width(), m_screen->height(), visarea, m_screen->frame_period().attoseconds());
}

void pc6001mk2_state::mk2_vram_bank_w(uint8_t data)
{
	//static const uint32_t startaddr[] = {WRAM(6), WRAM(6), WRAM(0), WRAM(4) };

	m_ex_vram_bank = data;
	vram_bank_change((m_ex_vram_bank & 0x06) | ((m_sys_latch & 0x06) << 4));

	m_exgfx_text_mode = ((data & 2) == 0);
	m_cgrom_bank_addr = (data & 2) ? 0x0000 : 0x2000;

	m_exgfx_bitmap_mode = (data & 8);
	m_exgfx_2bpp_mode = ((data & 6) == 0);

	refresh_crtc_params();

//  popmessage("%02x",data);

//  m_video_base = work_ram + startaddr[(data >> 1) & 0x03];
}

void pc6001mk2_state::mk2_col_bank_w(uint8_t data)
{
	m_bgcol_bank = (data & 7);
}


void pc6001mk2_state::mk2_0xf3_w(uint8_t data)
{
	/*
	x--- ---- M1 (?) wait setting
	-x-- ---- ROM wait setting
	--x- ---- RAM wait setting
	---x ---- custom irq 2 address output
	---- x--- custom irq 1 address output
	---- -x-- timer irq mask 2 (mirror?)
	---- --x- custom irq 2 mask
	---- ---x custom irq 1 mask
	*/
	m_timer_irq_mask = BIT(data, 2);
}

inline u8 pc6001_state::get_timer_base_divider()
{
	return 4;
}

inline u8 pc6001mk2sr_state::get_timer_base_divider()
{
//  if (sr_mode == false)
//      return pc6001mk2_state::get_timer_base_divider();
	return 0x80;
}

inline void pc6001_state::set_timer_divider()
{
	if (m_timer_enable == false)
	{
		m_timer_irq_timer->adjust(attotime::never);
		return;
	}
	attotime period = attotime::from_hz((487.5 * get_timer_base_divider()) / (m_timer_hz_div+1));
	m_timer_irq_timer->adjust(period,  0, period);
}

void pc6001mk2_state::mk2_timer_adj_w(uint8_t data)
{
	m_timer_hz_div = data;
	set_timer_divider();
}

void pc6001mk2_state::mk2_timer_irqv_w(uint8_t data)
{
	m_timer_irq_vector = data;
}

uint8_t pc6001mk2_state::mk2_bank_r0_r()
{
	return m_bank_r0;
}

uint8_t pc6001mk2_state::mk2_bank_r1_r()
{
	return m_bank_r1;
}

uint8_t pc6001mk2_state::mk2_bank_w0_r()
{
	return m_bank_w;
}

void pc6001mk2_state::pc6001mk2_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).bankr("bank1").w(FUNC(pc6001mk2_state::mk2_work_ram0_w));
	map(0x2000, 0x3fff).bankr("bank2").w(FUNC(pc6001mk2_state::mk2_work_ram1_w));
	map(0x4000, 0x5fff).bankr("bank3").w(FUNC(pc6001mk2_state::mk2_work_ram2_w));
	map(0x6000, 0x7fff).bankr("bank4").w(FUNC(pc6001mk2_state::mk2_work_ram3_w));
	map(0x8000, 0x9fff).bankr("bank5").w(FUNC(pc6001mk2_state::mk2_work_ram4_w));
	map(0xa000, 0xbfff).bankr("bank6").w(FUNC(pc6001mk2_state::mk2_work_ram5_w));
	map(0xc000, 0xdfff).bankr("bank7").w(FUNC(pc6001mk2_state::mk2_work_ram6_w));
	map(0xe000, 0xffff).bankr("bank8").w(FUNC(pc6001mk2_state::mk2_work_ram7_w));
}

void pc6001mk2_state::pc6001mk2_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x80, 0x81).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));

	map(0x90, 0x93).mirror(0x0c).rw(FUNC(pc6001mk2_state::nec_ppi8255_r), FUNC(pc6001mk2_state::necmk2_ppi8255_w));

	map(0xa0, 0xa0).mirror(0x0c).w(m_ay, FUNC(ay8910_device::address_w));
	map(0xa1, 0xa1).mirror(0x0c).w(m_ay, FUNC(ay8910_device::data_w));
	map(0xa2, 0xa2).mirror(0x0c).r(m_ay, FUNC(ay8910_device::data_r));
	map(0xa3, 0xa3).mirror(0x0c).noprw();

	map(0xb0, 0xb0).mirror(0x0f).w(FUNC(pc6001mk2_state::mk2_system_latch_w));

	map(0xc0, 0xc0).w(FUNC(pc6001mk2_state::mk2_col_bank_w));
	map(0xc1, 0xc1).w(FUNC(pc6001mk2_state::mk2_vram_bank_w));
	map(0xc2, 0xc2).w(FUNC(pc6001mk2_state::mk2_opt_bank_w));

	map(0xe0, 0xe3).mirror(0x0c).rw("upd7752", FUNC(upd7752_device::read), FUNC(upd7752_device::write));

	map(0xf0, 0xf0).rw(FUNC(pc6001mk2_state::mk2_bank_r0_r), FUNC(pc6001mk2_state::mk2_bank_r0_w));
	map(0xf1, 0xf1).rw(FUNC(pc6001mk2_state::mk2_bank_r1_r), FUNC(pc6001mk2_state::mk2_bank_r1_w));
	map(0xf2, 0xf2).rw(FUNC(pc6001mk2_state::mk2_bank_w0_r), FUNC(pc6001mk2_state::mk2_bank_w0_w));
	map(0xf3, 0xf3).w(FUNC(pc6001mk2_state::mk2_0xf3_w));
//  map(0xf4
//  map(0xf5
	map(0xf6, 0xf6).w(FUNC(pc6001mk2_state::mk2_timer_adj_w));
	map(0xf7, 0xf7).w(FUNC(pc6001mk2_state::mk2_timer_irqv_w));
}

/*****************************************
 *
 * PC-6601 specific i/o
 *
 ****************************************/

// disk device I/F
void pc6601_state::fdc_sel_w(uint8_t data)
{
	// bit 2 selects between internal (0) and external (1) FDC interfaces
	// other bits unknown purpose
	m_fdc_intf_view.select((data & 4) >> 2);
}

u8 pc6601_state::fdc_mon_r()
{
	// bit 0 reads the motor line status (active low)
	return 0;
}

void pc6601_state::fdc_mon_w(u8 data)
{
	m_floppy->get_device()->mon_w(!BIT(data,0));
}

// TODO: 0xd0, 0xd3 FIFO data buffer from/to FDC (external DMA?)
// PC-6001mk2SR / PC-6601SR tests these FIFO ports first,
// ditching attempt to floppy load if reading doesn't match 0x55aa previous writes.
// PC-6601 ignores this.

// TODO: hangs at first internal FDC command sense irq status in PC-6601 (and later machines if data buffer is enabled)
// It recalibrate and expects that uPD765 returns a DIO high that never happens.

void pc6601_state::pc6601_fdc_io(address_map &map)
{
	map(0xb1, 0xb1).mirror(0x4).w(FUNC(pc6601_state::fdc_sel_w));
	map(0xb2, 0xb2).lr8([this]() { return m_fdc->get_irq() ? 1 : 0; }, "FDCINT");
	//map(0xb3, 0xb3).w  b3 is written when b2 is read
	map(0xd0, 0xdf).view(m_fdc_intf_view);
	m_fdc_intf_view[0](0xd4, 0xd4).r(FUNC(pc6601_state::fdc_mon_r));
	m_fdc_intf_view[0](0xd6, 0xd6).w(FUNC(pc6601_state::fdc_mon_w));
	m_fdc_intf_view[0](0xdc, 0xdd).m(m_fdc, FUNC(upd765a_device::map));

	m_fdc_intf_view[1](0xd0, 0xd3).mirror(0x4).m(m_pc80s31, FUNC(pc80s31_device::host_map));
}

void pc6601_state::pc6601_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	pc6001mk2_io(map);

	pc6601_fdc_io(map);
}

/*****************************************
 *
 * PC-6001 SR specific i/o
 *
 ****************************************/

u8 pc6001mk2sr_state::sr_bank_reg_r(offs_t offset)
{
	return m_sr_bank_reg[offset];
}

// offset & 8 maps to bank writes
void pc6001mk2sr_state::sr_bank_reg_w(offs_t offset, u8 data)
{
	// TODO: is bit 0 truly a NOP?
	m_sr_bank_reg[offset] = data & 0xfe;
	m_sr_bank[offset]->set_bank(m_sr_bank_reg[offset] >> 1);
}

void pc6001mk2sr_state::sr_bitmap_yoffs_w(u8 data)
{
	m_bitmap_yoffs = data;
}

void pc6001mk2sr_state::sr_bitmap_xoffs_w(u8 data)
{
	m_bitmap_xoffs = data;

	if (data)
		popmessage("xoffs write %02x", data);
}

u8 pc6001mk2sr_state::work_ram_r(offs_t offset)
{
//  if (m_sr_text_mode == false && (offset & 0xe000) == 0) && m_sr_mode)
	if (m_sr_text_mode == false && (offset & 0xe000) == 0)
		return sr_gvram_r(offset);

	return m_ram[offset];
}

void pc6001mk2sr_state::work_ram_w(offs_t offset, u8 data)
{
//  if (m_sr_text_mode == false && (offset & 0xe000) == 0) && m_sr_mode)
	if (m_sr_text_mode == false && (offset & 0xe000) == 0)
	{
		sr_gvram_w(offset, data);
		return;
	}

	m_ram[offset] = data;
}

// TODO: does this maps to the work RAM in an alt fashion?
// Games that uses GVRAM never writes outside 0-0x13f, and xoffs is unconfirmed.
// Games also expects that GVRAM is read-back as 4-bit, otherwise fill issues happens
// By logic it should be a 320x204 8-bit area -> 0x7f80
// The interface is otherwise pretty inconvenient,
// and it is unconfirmed if raster effect over SR text mode is even possible (which would halve the time required for drawing)
// or if double buffering is possible (that would require EXRAM installed)
u8 pc6001mk2sr_state::sr_gvram_r(offs_t offset)
{
	uint32_t real_offs = (m_bitmap_xoffs*16+m_bitmap_yoffs)*320;
	real_offs += offset;

	return m_gvram[real_offs];
}

void pc6001mk2sr_state::sr_gvram_w(offs_t offset, u8 data)
{
	uint32_t real_offs = (m_bitmap_xoffs*16+m_bitmap_yoffs)*320;
	real_offs += offset;

	m_gvram[real_offs] = data;
}

void pc6001mk2sr_state::sr_mode_w(u8 data)
{
	// if 1 text mode else bitmap mode
	m_sr_text_mode = bool(BIT(data, 3));
	// in theory we need a view,
	// in practice this approach doesn't work cause we can't mix views with address banks
//  m_gvram_view.select(m_sr_text_mode == false);

	m_sr_text_rows = data & 4 ? 20 : 25;
	refresh_crtc_params();

	// bit 1: bus request
	// bit 4: VRAM bank select
//  if (data & 0x10)
//      popmessage("VRAM bank select enabled");

	if(data & 1)
		throw emu_fatalerror("PC-6601SR in Mk-2 compatibility mode not yet supported!");
}

void pc6001mk2sr_state::sr_vram_bank_w(u8 data)
{
	m_video_base = &m_ram[(data & 0x0f) * 0x1000];

//  set_videoram_bank(0x70000 + ((data & 0x0f)*0x1000));
}

void pc6001mk2sr_state::sr_system_latch_w(u8 data)
{
	cassette_latch_control((data & 8) == 8);
	m_sys_latch = data;

	m_timer_enable = !(data & 1);
	set_timer_divider();
	//vram_bank_change((m_ex_vram_bank & 0x06) | ((m_sys_latch & 0x06) << 4));

	//printf("%02x B0\n",data);
}

void pc6001mk2sr_state::necsr_ppi8255_w(offs_t offset, u8 data)
{
	if (offset==3)
	{
		ppi_control_hack_w(data);

#if 0
		{
			//printf("%02x\n",data);

			if ((data & 0x0f) == 0x05 && m_cart_rom)
				m_bank1->set_base(m_cart_rom->base() + 0x2000);
			if ((data & 0x0f) == 0x04)
				m_bank1->set_base(m_region_gfx1->base());
		}
#endif
	}

	m_ppi->write(offset,data);
}

u8 pc6001mk2sr_state::hw_rev_r()
{
	// bit 1 is active for pc6601sr (and shows the "PC6601SR World" screen in place of the "PC6001mkIISR World"),
	// causes a direct jump to "video telopper" for pc6001mk2sr
	// bit 0 is related to FDC irq status
	return 0 | 1;
}

u8 pc6601sr_state::hw_rev_r()
{
	return 2 | 1;
}

void pc6001mk2sr_state::crt_mode_w(u8 data)
{
//  m_bgcol_bank = (data & 8) ^ 8;
	m_width80 = !BIT(data, 1);
	refresh_crtc_params();
}

inline void pc6001mk2sr_state::refresh_crtc_params()
{
	/* Apparently bitmap modes changes the screen res to 320 x 200 */
	rectangle visarea = m_screen->visible_area();
	const int y_height = (m_sr_text_mode) ? 240 : 200;
	const int x_width = (m_sr_text_mode) ? (m_width80 ? 640 : 320) : 320;

	visarea.set(0, (x_width) - 1, 0, (y_height) - 1);

	m_screen->configure(m_screen->width(), m_screen->height(), visarea, m_screen->frame_period().attoseconds());
}

void pc6001mk2sr_state::pc6001mk2sr_map(address_map &map)
{
	map.unmap_value_high();
	for (int bank = 0; bank < 8; bank++)
	{
		map(bank << 13, (bank << 13) | 0x1fff).r(m_sr_bank[bank], FUNC(address_map_bank_device::read8));
		map(bank << 13, (bank << 13) | 0x1fff).w(m_sr_bank[bank+8], FUNC(address_map_bank_device::write8));
	}
}

void pc6001mk2sr_state::sr_banked_map(address_map &map)
{
//  map(0x00000, 0x0ffff).view(m_gvram_view);
//  m_gvram_view[0](0x0000, 0xffff).ram();
//  m_gvram_view[1](0x0000, 0x1fff).rw(FUNC(pc6001mk2sr_state::sr_gvram_r), FUNC(pc6001mk2sr_state::sr_gvram_w));
	map(0x00000, 0x0ffff).rw(FUNC(pc6001mk2sr_state::work_ram_r), FUNC(pc6001mk2sr_state::work_ram_w)).share("ram");

	// exram0
	map(0x20000, 0x2ffff).ram();
	// exrom0
	map(0xb4000, 0xb7fff).r(m_cart, FUNC(generic_slot_device::read_rom));
	// exrom1
//  map(0xc0000, 0xcffff).rom();
	// cgrom 1
	map(0xd0000, 0xd3fff).rom().region("cgrom", 0);
	// sysrom 1 / 2
	map(0xe0000, 0xfffff).rom().region("sr_sysrom", 0);
}

void pc6001mk2sr_state::pc6001mk2sr_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x40, 0x43).nopw(); // palette CLUTs
	map(0x60, 0x6f).rw(FUNC(pc6001mk2sr_state::sr_bank_reg_r), FUNC(pc6001mk2sr_state::sr_bank_reg_w));

	map(0x80, 0x81).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));

	map(0x90, 0x93).mirror(0x0c).rw(FUNC(pc6001mk2sr_state::nec_ppi8255_r), FUNC(pc6001mk2sr_state::necsr_ppi8255_w));

	map(0xa0, 0xa0).mirror(0x0c).w(m_ym, FUNC(ym2203_device::address_w));
	map(0xa1, 0xa1).mirror(0x0c).w(m_ym, FUNC(ym2203_device::data_w));
	map(0xa2, 0xa2).mirror(0x0c).r(m_ym, FUNC(ym2203_device::data_r));
	map(0xa3, 0xa3).mirror(0x0c).r(m_ym, FUNC(ym2203_device::status_r));

	map(0xb0, 0xb0).w(FUNC(pc6001mk2sr_state::sr_system_latch_w));

	pc6601_fdc_io(map);
	map(0xb2, 0xb2).r(FUNC(pc6001mk2sr_state::hw_rev_r));

	map(0xb8, 0xbf).ram().share("irq_vectors");
//  map(0xc0, 0xc0).w(FUNC(pc6001mk2sr_state::mk2_col_bank_w));
	map(0xc1, 0xc1).w(FUNC(pc6001mk2sr_state::crt_mode_w));
//  map(0xc2, 0xc2).w(FUNC(pc6001mk2sr_state::opt_bank_w));

	map(0xc8, 0xc8).w(FUNC(pc6001mk2sr_state::sr_mode_w));
	map(0xc9, 0xc9).w(FUNC(pc6001mk2sr_state::sr_vram_bank_w));
	// TODO: confirm readback
	map(0xca, 0xcb).ram().share("sr_scrollx");
	map(0xcc, 0xcc).ram().share("sr_scrolly");
	map(0xce, 0xce).w(FUNC(pc6001mk2sr_state::sr_bitmap_yoffs_w));
	map(0xcf, 0xcf).w(FUNC(pc6001mk2sr_state::sr_bitmap_xoffs_w));

	map(0xe0, 0xe3).mirror(0x0c).rw("upd7752", FUNC(upd7752_device::read), FUNC(upd7752_device::write));

//  map(0xf0, 0xf0).rw(FUNC(pc6001mk2sr_state::mk2_bank_r0_r), FUNC(pc6001mk2sr_state::mk2_bank_r0_w));
//  map(0xf1, 0xf1).rw(FUNC(pc6001mk2sr_state::mk2_bank_r1_r), FUNC(pc6001mk2sr_state::mk2_bank_r1_w));
//  map(0xf2, 0xf2).rw(FUNC(pc6001mk2sr_state::mk2_bank_w0_r), FUNC(pc6001mk2sr_state::mk2_bank_w0_w));
	map(0xf3, 0xf3).w(FUNC(pc6001mk2sr_state::mk2_0xf3_w));
//  map(0xf4
//  map(0xf5
	map(0xf6, 0xf6).w(FUNC(pc6001mk2sr_state::mk2_timer_adj_w));
	map(0xf7, 0xf7).w(FUNC(pc6001mk2sr_state::mk2_timer_irqv_w));
}

/* Input ports */
static INPUT_PORTS_START( pc6001 )
	// TODO: is this really a DSW? bit arrangement is also unknown if so.
	PORT_START("MODE4_DSW")
	PORT_DIPNAME( 0x07, 0x00, "Mode 4 GFX colors" )
	PORT_DIPSETTING(    0x00, "Monochrome" )
	PORT_DIPSETTING(    0x01, "Red/Blue" )
	PORT_DIPSETTING(    0x02, "Blue/Red" )
	PORT_DIPSETTING(    0x03, "Pink/Green" )
	PORT_DIPSETTING(    0x04, "Green/Pink" )
	//5-6-7 is presumably invalid

	PORT_START("key1") //0x00-0x1f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_UNUSED) //0x00 null
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_UNUSED) //0x01 soh
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_UNUSED) //0x02 stx
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_UNUSED) //0x03 etx
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_UNUSED) //0x04 etx
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_UNUSED) //0x05 eot
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_UNUSED) //0x06 enq
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_UNUSED) //0x07 ack
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_UNUSED) //0x0a
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_UNUSED) //0x0b lf
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x0c vt
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x0e cr
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x0f so

	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x10 si
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x11 dle
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x12 dc1
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x13 dc2
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x14 dc3
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x15 dc4
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x16 nak
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x17 syn
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x18 etb
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x19 cancel
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x1a em
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x1b sub
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("RIGHT") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("LEFT") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("UP") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("DOWN") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))

	PORT_START("key2") //0x20-0x3f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_UNUSED) //0x21 !
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_UNUSED) //0x22 "
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_UNUSED) //0x23 #
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_UNUSED) //0x24 $
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_UNUSED) //0x25 %
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_UNUSED) //0x26 &
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_UNUSED) //0x27 '
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_UNUSED) //0x28 (
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_UNUSED) //0x29 )
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("*") PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR('*')
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("+") PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR('+')
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') //0x2c
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') //0x2e
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') //0x2f

	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x3a :
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x3b ;
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x3c <
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x3d =
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x3e >
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x3f ?

	PORT_START("key3") //0x40-0x5f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@')
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[')
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_UNUSED)
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(']')
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^')
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("_")

	PORT_START("key_fn")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F1 / F6") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F2 / F7") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F3 / F8") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F4 / F9") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F5 / F10") PORT_CODE(KEYCODE_F5)

	PORT_START("key_modifiers")
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("KANA") PORT_CODE(KEYCODE_RCONTROL) PORT_TOGGLE
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("CAPS") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("GRPH") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("STOP") PORT_CODE(KEYCODE_ESC)
INPUT_PORTS_END

TIMER_CALLBACK_MEMBER(pc6001_state::audio_callback)
{
	// TODO: shouldn't really need the cas switch check, different thread
	if(m_cas_switch == 0 && m_timer_irq_mask == false)
		set_irq_level(TIMER_IRQ);
}

TIMER_CALLBACK_MEMBER(pc6001_state::sub_trig_callback)
{
	m_cur_keycode = check_joy_press();
	// TODO: is sub CPU the actual source of this?
	set_irq_level(JOYSTICK_IRQ);
}

INTERRUPT_GEN_MEMBER(pc6001mk2sr_state::sr_vrtc_irq)
{
	set_irq_level(VRTC_IRQ);
}

u8 pc6001_state::sub_ack()
{
	return m_sub_vector;
}

u8 pc6001_state::joystick_ack()
{
	return 0x16;
}

u8 pc6001_state::timer_ack()
{
	return m_timer_irq_vector;
}

u8 pc6001_state::vrtc_ack()
{
	// not present for PC-6001
	throw emu_fatalerror("Vanilla PC-6001 doesn't have VRTC irq ack");
}

u8 pc6001mk2_state::vrtc_ack()
{
	// TODO: currently unimplemented, find and verify any software that needs this
	return 0x22;
}

u8 pc6001mk2sr_state::vrtc_ack()
{
	// TODO: bit 0 of sr_mode_w
//  if (sr_mode == false)
//      return pc6001mk2_state::vrtc_ack();

	return m_sr_irq_vectors[VRTC_IRQ];
}

IRQ_CALLBACK_MEMBER(pc6001_state::irq_callback)
{
	u8 result_vector = 0x00;
	for (int i = 0; i < 8; i++)
	{
		u8 mask = m_irq_pending & (1 << i);
		if (mask)
		{
			// TODO: understand how HW implements daisy chain if at all
			// priority should be right, i.e. lower level gets serviced first
			switch(i)
			{
				case SUB_CPU_IRQ: result_vector = sub_ack(); break;
				case JOYSTICK_IRQ: result_vector = joystick_ack(); break;
				case TIMER_IRQ: result_vector = timer_ack(); break;
				case VRTC_IRQ: result_vector = vrtc_ack(); break;
				default:
					throw emu_fatalerror("Unhandled irq ack %d", i);
			}
			m_irq_pending &= ~mask;
			if (m_irq_pending == 0)
				device.execute().set_input_line(0, CLEAR_LINE);
			LOGIRQ("%s: ack %d, state %02x, vector %02x\n", machine().describe_context(), i, m_irq_pending, result_vector);

			return result_vector;
		}
	}

	// check if this ever happens
	LOGIRQ("%s: spurious IRQ fired!\n", machine().describe_context());
	return 0x06;
}

uint8_t pc6001_state::ppi_porta_r()
{
	return 0;
}

void pc6001_state::ppi_porta_w(uint8_t data)
{
	// sub command
	// [0x06]: trigger a 0x16 irq
	// [0x19/0x39]: Cassette PLAY
	// [0x1a]: Cassette STOP
	// [0x1d/0x3d]: Cassette baud select 600
	// [0x1e/0x3e]: Cassette baud select 1200
	// [0x38]: Cassette RECord

	if (data == 0x6)
	{
		// (timing is unknown, 0.1 msec is way too short for Space Harrier)
		m_sub_trig_timer->reset();
		m_sub_trig_timer->adjust(attotime::from_usec(3000));
	}
}

uint8_t pc6001_state::ppi_portb_r()
{
	return 0;
}

void pc6001_state::ppi_portb_w(uint8_t data)
{
	//printf("ppi_portb_w %02x\n",data);
}

void pc6001_state::ppi_portc_w(uint8_t data)
{
	//printf("ppi_portc_w %02x\n",data);
}

uint8_t pc6001_state::ppi_portc_r()
{
	return 0x88;
}

// TODO: move to own device, and add remapping tables depending on the mode used
uint8_t pc6001_state::check_keyboard_press()
{
	int i, port_i, scancode;
	u8 shift_pressed, caps_lock;
	u8 io_fn = m_io_fn_keys->read();
	scancode = 0;

	shift_pressed = (m_io_key_modifiers->read() & 2)>>1;
	caps_lock = (m_io_key_modifiers->read() & 8)>>3;

	if (io_fn)
	{
		for(i = 0; i < 5; i++)
			if (BIT(io_fn, i))
				return (i + 0xf0) + shift_pressed * 5;
	}

	for(port_i = 0; port_i < 3; port_i++)
	{
		for(i=0;i<32;i++)
		{
			if((m_io_keys[port_i]->read()>>i) & 1)
			{
				if((shift_pressed != caps_lock) && scancode >= 0x41 && scancode <= 0x5f)
					scancode+=0x20;

				if(shift_pressed && scancode >= 0x31 && scancode <= 0x39)
					scancode-=0x10;

				if(shift_pressed && scancode == 0x30) // '0' / '='
					scancode = 0x3d;

				if(shift_pressed && scancode == 0x2c) // ',' / ';'
					scancode = 0x3b;

				if(shift_pressed && scancode == 0x2f) // '/' / '?'
					scancode = 0x3f;

				if(shift_pressed && scancode == 0x2e) // '.' / ':'
					scancode = 0x3a;

				return scancode;
			}
			scancode++;
		}
	}

	return 0;
}

uint8_t pc6001_state::check_joy_press()
{
	// TODO: this may really just rearrange keyboard key presses in a joystick like fashion, somehow akin to Sharp X1 mode
	uint8_t p1_key = m_joymux->output_r() ^ 0xff;
	uint8_t shift_key = m_io_key_modifiers->read() & 0x02;
	uint8_t space_key = m_io_keys[1]->read() & 0x01;
	uint8_t joy_press;

		/*
		    PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY
		    PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY
		    PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY
		    PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY
		    PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 )
		    PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNKNOWN )
		    PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN )
		    PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )
		*/

	joy_press = 0;

	switch(p1_key & 0xf)
	{
		case 0x01: joy_press = 0x04; break; //up
		case 0x02: joy_press = 0x08; break; //down
		case 0x04: joy_press = 0x20; break;
		case 0x05: joy_press = 0x24; break; //up-left
		case 0x06: joy_press = 0x28; break; //down-left
		case 0x08: joy_press = 0x10; break; //right
		case 0x09: joy_press = 0x14; break; //up-right
		case 0x0a: joy_press = 0x18; break; //down-right
	}

	if(p1_key & 0x10 || space_key) { joy_press |= 0x80; } //button 1 (space)
	if(p1_key & 0x20 || shift_key) { joy_press |= 0x01; } //button 2 (shift)

	return joy_press;
}

TIMER_DEVICE_CALLBACK_MEMBER(pc6001_state::cassette_callback)
{
	if(m_cas_switch == 1)
	{
		#if 0
			static uint8_t cas_data_i = 0x80,cas_data_poll;
			//m_cur_keycode = gfx_data[m_cas_offset++];
			if(m_cassette->input() > 0.03)
				cas_data_poll|= cas_data_i;
			else
				cas_data_poll&=~cas_data_i;
			if(cas_data_i == 1)
			{
				m_cur_keycode = cas_data_poll;
				cas_data_i = 0x80;
				/* data ready, poll irq */
				set_subcpu_irq_vector(0x08);
			}
			else
				cas_data_i>>=1;
		#else
			m_cur_keycode = m_cas_hack->read_rom(m_cas_offset++);
			popmessage("%04x %04x", m_cas_offset, m_cas_maxsize);
			if(m_cas_offset > m_cas_maxsize)
			{
				m_cas_offset = 0;
				m_cas_switch = 0;
				// Tape-E
				set_subcpu_irq_vector(0x12);
			}
			else
			{
				// Tape-D
				set_subcpu_irq_vector(0x08);
			}
		#endif
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(pc6001_state::keyboard_callback)
{
	uint32_t key1 = m_io_keys[0]->read();
	uint32_t key2 = m_io_keys[1]->read();
	uint32_t key3 = m_io_keys[2]->read();
	u8 key_fn = m_io_fn_keys->read();
//  uint8_t p1_key = m_joy[0]->read();

	if(m_cas_switch == 0)
	{
		if((key1 != m_old_key1) || (key2 != m_old_key2) || (key3 != m_old_key3) || (key_fn != m_old_key_fn))
		{
			m_cur_keycode = check_keyboard_press();
			const u8 key_vector = (m_cur_keycode & 0xf0) == 0xf0 ? 0x14 : 0x02;
			set_subcpu_irq_vector(key_vector);
			m_old_key1 = key1;
			m_old_key2 = key2;
			m_old_key3 = key3;
			m_old_key_fn = key_fn;
		}
	}
}

void pc6001_state::machine_start()
{
	m_timer_irq_timer = timer_alloc(FUNC(pc6001_state::audio_callback), this);
	m_sub_trig_timer = timer_alloc(FUNC(pc6001_state::sub_trig_callback), this);
}

inline void pc6001_state::set_videoram_bank(uint32_t offs)
{
	m_video_base = m_region_maincpu->base() + offs;
}

inline void pc6001_state::default_cartridge_reset()
{
	std::string region_tag;
	m_cart_rom = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());
}

inline void pc6001_state::default_cassette_hack_reset()
{
	m_cas_switch = 0;
	m_cas_offset = 0;
	m_cas_maxsize = (m_cas_hack->exists()) ? m_cas_hack->get_rom_size() : 0;
}

inline void pc6001_state::default_keyboard_hle_reset()
{
	m_port_c_8255 = 0;
	m_old_key1 = m_old_key2 = m_old_key3 = 0;
	m_old_key_fn = 0;
}

void pc6001_state::irq_reset(u8 timer_default_setting)
{
	m_timer_enable = false;
	m_timer_irq_mask = false;
	// timer irq vector is fixed in plain PC-6001
	m_timer_irq_vector = 0x06;
	m_irq_pending = 0;
	m_timer_hz_div = timer_default_setting;
	set_timer_divider();
}

void pc6001_state::machine_reset()
{
	set_videoram_bank(0xc000);

	default_cartridge_reset();
	if (m_cart->exists())
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x4000, 0x5fff, read8sm_delegate(*m_cart, FUNC(generic_slot_device::read_rom)));

	default_cassette_hack_reset();
	irq_reset(3);
	default_keyboard_hle_reset();
}

void pc6001mk2_state::machine_reset()
{
//  pc6001_state::machine_reset();
	set_videoram_bank(0xc000 + 0x28000);

	default_cartridge_reset();
	// TODO: hackish way to simplify bankswitch handling
	if (m_cart->exists())
		memcpy(m_region_maincpu->base() + 0x48000, m_cart_rom->base(), 0x4000);

	default_cassette_hack_reset();
	irq_reset(3);
	default_keyboard_hle_reset();

	/* set default bankswitch */
	{
		uint8_t *ROM = m_region_maincpu->base();
		m_bank_r0 = 0x71;
		m_bank1->set_base(&ROM[BASICROM(0)]);
		m_bank2->set_base(&ROM[BASICROM(1)]);
		m_bank3->set_base(&ROM[EXROM(0)]);
		m_bank4->set_base(&ROM[EXROM(1)]);
		m_bank_r1 = 0xdd;
		m_bank5->set_base(&ROM[WRAM(4)]);
		m_bank6->set_base(&ROM[WRAM(5)]);
		m_bank7->set_base(&ROM[WRAM(6)]);
		m_bank8->set_base(&ROM[WRAM(7)]);
		m_bank_opt = 0x02; //tv rom
		m_bank_w = 0x50; //enable write to work ram 4,5,6,7
		m_gfx_bank_on = 0;

		m_bgcol_bank = 0;
	}

//  refresh_crtc_params();
}

void pc6601_state::machine_start()
{
	pc6001mk2_state::machine_start();

	m_fdc->set_rate(250000);
	m_floppy->get_device()->set_rpm(300);
}

void pc6001mk2sr_state::machine_reset()
{
//  pc6001_state::machine_reset();
	default_cassette_hack_reset();

//  set_videoram_bank(0x70000);
	m_video_base = &m_ram[0];

	default_cartridge_reset();
	// TODO: checkout where cart actually maps in SR model
	// should be mirrored into the EXROM regions?
	// hard to tell without an actual SR cart dump
//  std::string region_tag;
//  m_cart_rom = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());
	default_cassette_hack_reset();
	irq_reset(0x7f);
	default_keyboard_hle_reset();

	// default to text mode
	m_sr_text_mode = true;
	m_sr_text_rows = 20;
	m_width80 = 0;

	/* set default bankswitch */
	{
		// TODO: confirm this arrangement
		u8 default_banks[16] = {
			// read default:
			// 0x0000 - 0x3fff sysrom 1 0x8000 - 0xbfff
			// 0x4000 - 0x5fff exrom 1 0x0000 - 0x1fff
			// 0x6000 - 0x7fff exrom 0 0x0000 - 0x1fff
			// 0x8000 - 0xffff RAM 0x8000 - 0xffff
			0xf8, 0xfa, 0xc0, 0xb0, 0x08, 0x0a, 0x0c, 0x0e,
			// enable all work RAM writes
			0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e
		};

		for (int i = 0; i < 16; i++)
		{
			m_sr_bank_reg[i] = default_banks[i] & 0xfe;
			m_sr_bank[i]->set_bank(m_sr_bank_reg[i] >> 1);
		}
//      m_gfx_bank_on = 0;
	}
}


static const gfx_layout char_layout =
{
	8, 16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8,8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16
};


static const gfx_layout kanji_layout =
{
	16, 16,
	RGN_FRAC(1,2),
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7,
	0+RGN_FRAC(1,2), 1+RGN_FRAC(1,2), 2+RGN_FRAC(1,2), 3+RGN_FRAC(1,2), 4+RGN_FRAC(1,2), 5+RGN_FRAC(1,2), 6+RGN_FRAC(1,2), 7+RGN_FRAC(1,2)  },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8,8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16
};

static GFXDECODE_START( gfx_pc6001m2 )
	GFXDECODE_ENTRY( "gfx1", 0x0000, char_layout, 2, 1 )
	GFXDECODE_ENTRY( "gfx2", 0x0000, kanji_layout, 2, 1 )
GFXDECODE_END

// TODO: same as PC-88 / PC-98 31'948'800 ?
#define PC6001_MAIN_CLOCK 7987200

void pc6001_state::pc6001(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, PC6001_MAIN_CLOCK / 2); // PD 780C-1, ~4 Mhz
	m_maincpu->set_addrmap(AS_PROGRAM, &pc6001_state::pc6001_map);
	m_maincpu->set_addrmap(AS_IO, &pc6001_state::pc6001_io);
//  m_maincpu->set_vblank_int("screen", FUNC(pc6001_state::vrtc_irq));
	m_maincpu->set_irq_acknowledge_callback(FUNC(pc6001_state::irq_callback));

//  I8049(config, "subcpu", 7987200);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_pc6001m2);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_screen_update(FUNC(pc6001_state::screen_update));
	m_screen->set_size(320, 25+192+26);
	m_screen->set_visarea(0, 319, 0, 239);
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(pc6001_state::pc6001_palette), 16+4);

	I8255(config, m_ppi);
	m_ppi->in_pa_callback().set(FUNC(pc6001_state::ppi_porta_r));
	m_ppi->out_pa_callback().set(FUNC(pc6001_state::ppi_porta_w));
	m_ppi->in_pb_callback().set(FUNC(pc6001_state::ppi_portb_r));
	m_ppi->out_pb_callback().set(FUNC(pc6001_state::ppi_portb_w));
	m_ppi->in_pc_callback().set(FUNC(pc6001_state::ppi_portc_r));
	m_ppi->out_pc_callback().set(FUNC(pc6001_state::ppi_portc_w));

	/* uart */
	I8251(config, "uart", 0);

	MSX_GENERAL_PURPOSE_PORT(config, m_joy[0], msx_general_purpose_port_devices, "joystick");
	MSX_GENERAL_PURPOSE_PORT(config, m_joy[1], msx_general_purpose_port_devices, "joystick");

	LS157_X2(config, m_joymux);
	m_joymux->a_in_callback().set(m_joy[1], FUNC(msx_general_purpose_port_device::read));
	m_joymux->b_in_callback().set(m_joy[0], FUNC(msx_general_purpose_port_device::read));

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "pc6001_cart");
	SOFTWARE_LIST(config, "cart_list_pc6001").set_original("pc6001_cart");

//  CASSETTE(config, m_cassette);
//  m_cassette->set_formats(pc6001_cassette_formats);
//  m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
//  m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	GENERIC_CARTSLOT(config, m_cas_hack, generic_plain_slot, "pc6001_cass", "cas,p6");

	SPEAKER(config, "mono").front_center();
	AY8910(config, m_ay, PC6001_MAIN_CLOCK/4);
	m_ay->port_a_read_callback().set(FUNC(pc6001_state::joystick_r));
	m_ay->port_b_read_callback().set(FUNC(pc6001_state::joystick_out_r));
	m_ay->port_b_write_callback().set(FUNC(pc6001_state::joystick_out_w));
	m_ay->add_route(ALL_OUTPUTS, "mono", 1.00);

	// TODO: accurate timing on this
	TIMER(config, "keyboard_timer").configure_periodic(FUNC(pc6001_state::keyboard_callback), attotime::from_hz(250));
	TIMER(config, "cassette_timer").configure_periodic(FUNC(pc6001_state::cassette_callback), attotime::from_hz(1200/12));
}

void pc6001mk2_state::pc6001mk2(machine_config &config)
{
	pc6001(config);
	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &pc6001mk2_state::pc6001mk2_map);
	m_maincpu->set_addrmap(AS_IO, &pc6001mk2_state::pc6001mk2_io);
	m_maincpu->set_irq_acknowledge_callback(FUNC(pc6001mk2_state::irq_callback));

//  MCFG_MACHINE_RESET_OVERRIDE(pc6001mk2_state,pc6001mk2)

	m_screen->set_screen_update(FUNC(pc6001mk2_state::screen_update));

	m_palette->set_entries(16+16);
	m_palette->set_init(FUNC(pc6001mk2_state::pc6001mk2_palette));

	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_pc6001m2);

	UPD7752(config, "upd7752", PC6001_MAIN_CLOCK / 4).add_route(ALL_OUTPUTS, "mono", 1.00);
}

void pc6601_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	// TODO: cannot identify .dsk images anyway
	// (HxC reports them to be MSX based images)
	fr.add(FLOPPY_MSX_FORMAT);
	fr.add(FLOPPY_DSK_FORMAT);
}

static void pc6601_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
	device.option_add("35ssdd", FLOPPY_35_SSDD);
}

void pc6601_state::pc6601_fdc_config(machine_config &config)
{
	UPD765A(config, m_fdc, 8'000'000, true, true);
	FLOPPY_CONNECTOR(config, m_floppy, pc6601_floppies, "35ssdd", pc6601_state::floppy_formats).enable_sound(true);

	// TODO: slotify external I/F
	// PC-6031 mini disk unit (single 5'25 2D drive)
	PC80S31(config, m_pc80s31, XTAL(4'000'000));
	config.set_perfect_quantum(m_maincpu);
//  config.set_perfect_quantum("pc80s31:fdc_cpu");
}

void pc6601_state::pc6601(machine_config &config)
{
	pc6001mk2(config);

	/* basic machine hardware */
	Z80(config.replace(), m_maincpu, PC6001_MAIN_CLOCK / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc6601_state::pc6001mk2_map);
	m_maincpu->set_addrmap(AS_IO, &pc6601_state::pc6601_io);
//  m_maincpu->set_vblank_int("screen", FUNC(pc6001_state::vrtc_irq));
	m_maincpu->set_irq_acknowledge_callback(FUNC(pc6601_state::irq_callback));

	pc6601_fdc_config(config);
}

void pc6001mk2sr_state::pc6001mk2sr(machine_config &config)
{
	pc6001mk2(config);

	/* basic machine hardware */
	// PC-6001SR clock is actually slower than older models (better waitstates tho?)
	Z80(config.replace(), m_maincpu, XTAL(3'579'545));
	m_maincpu->set_addrmap(AS_PROGRAM, &pc6001mk2sr_state::pc6001mk2sr_map);
	m_maincpu->set_addrmap(AS_IO, &pc6001mk2sr_state::pc6001mk2sr_io);
	m_maincpu->set_vblank_int("screen", FUNC(pc6001mk2sr_state::sr_vrtc_irq));
	m_maincpu->set_irq_acknowledge_callback(FUNC(pc6001mk2sr_state::irq_callback));

	for (auto &bank : m_sr_bank)
	{
		ADDRESS_MAP_BANK(config, bank).set_map(&pc6001mk2sr_state::sr_banked_map).set_options(ENDIANNESS_LITTLE, 8, 20, 0x2000);
	}

//  MCFG_MACHINE_RESET_OVERRIDE(pc6001mk2sr_state,pc6001mk2sr)

	m_screen->set_screen_update(FUNC(pc6001mk2sr_state::screen_update));

	pc6601_fdc_config(config);

	config.device_remove("aysnd");
	YM2203(config, m_ym, 4_MHz_XTAL);
	m_ym->port_a_read_callback().set(FUNC(pc6001mk2sr_state::joystick_r));
	m_ym->port_b_read_callback().set(FUNC(pc6001mk2sr_state::joystick_out_r));
	m_ym->port_b_write_callback().set(FUNC(pc6001mk2sr_state::joystick_out_w));
	m_ym->add_route(ALL_OUTPUTS, "mono", 1.00);

	// TODO: 1D 3'5" floppy drive
	// TODO: telopper board (system explicitly asks for missing tape dump tho)
}

void pc6601sr_state::pc6601sr(machine_config &config)
{
	pc6001mk2sr(config);

	FLOPPY_CONNECTOR(config.replace(), m_floppy, pc6601_floppies, "35dd", pc6601sr_state::floppy_formats);

	// TODO: IR keyboard (does it have functional differences wrt normal PC-6001?)
	// TODO: TV tuner
}

// TODO: all labels needs to be checked up
/* ROM definition */
ROM_START( pc6001 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "basicrom.60", 0x0000, 0x4000, CRC(54c03109) SHA1(c622fefda3cdc2b87a270138f24c05828b5c41d2) )

	ROM_REGION( 0x800, "mcu", 0 )
	ROM_LOAD( "upd8049.ic17", 0x000, 0x800, CRC(6682ec41) SHA1(ea739be6178c0f2ef48a3a33a3f2a3438ed2ca61) )

	ROM_REGION( 0x2000, "gfx1", 0 )
	ROM_LOAD( "cgrom60.60", 0x0000, 0x1000, CRC(b0142d32) SHA1(9570495b10af5b1785802681be94b0ea216a1e26) )
	ROM_RELOAD(             0x1000, 0x1000 )

	ROM_REGION( 0x8000, "gfx2", ROMREGION_ERASEFF )
ROM_END

ROM_START( pc6001a )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "basicrom.60a", 0x0000, 0x4000, CRC(fa8e88d9) SHA1(c82e30050a837e5c8ffec3e0c8e3702447ffd69c) )

	ROM_REGION( 0x800, "mcu", 0 )
	ROM_LOAD( "upd8049.ic17", 0x000, 0x800, CRC(6682ec41) SHA1(ea739be6178c0f2ef48a3a33a3f2a3438ed2ca61) )

	ROM_REGION( 0x1000, "gfx1", 0 )
	ROM_LOAD( "cgrom60.60a", 0x0000, 0x1000, CRC(49c21d08) SHA1(9454d6e2066abcbd051bad9a29a5ca27b12ec897) )

	ROM_REGION( 0x8000, "gfx2", ROMREGION_ERASEFF )
ROM_END

ROM_START( pc6001mk2 )
	ROM_REGION( 0x50000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "basicrom.62", 0x10000, 0x8000, CRC(950ac401) SHA1(fbf195ba74a3b0f80b5a756befc96c61c2094182) )
	ROM_LOAD( "voicerom.62", 0x18000, 0x4000, CRC(49b4f917) SHA1(1a2d18f52ef19dc93da3d65f19d3abbd585628af) )
	ROM_LOAD( "cgrom60.62",  0x1c000, 0x2000, CRC(81eb5d95) SHA1(53d8ae9599306ff23bf95208d2f6cc8fed3fc39f) )
	ROM_LOAD( "cgrom60m.62", 0x1e000, 0x2000, CRC(3ce48c33) SHA1(f3b6c63e83a17d80dde63c6e4d86adbc26f84f79) )
	ROM_LOAD( "kanjirom.62", 0x20000, 0x8000, CRC(20c8f3eb) SHA1(4c9f30f0a2ebbe70aa8e697f94eac74d8241cadd) )
	// work ram              0x28000,0x10000
	// extended work ram     0x38000,0x10000
	// exrom                 0x48000, 0x4000
	// <invalid>             0x4c000, 0x4000

	ROM_REGION( 0x1000, "mcu", ROMREGION_ERASEFF )
	ROM_LOAD( "i8049", 0x0000, 0x1000, NO_DUMP )

	ROM_REGION( 0x4000, "gfx1", 0 )
	ROM_COPY( "maincpu", 0x1c000, 0x00000, 0x4000 )

	ROM_REGION( 0x8000, "gfx2", 0 )
	ROM_COPY( "maincpu", 0x20000, 0x00000, 0x8000 )
ROM_END

/* Variant of pc6001mk2 */
ROM_START( pc6601 )
	ROM_REGION( 0x50000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "basicrom.66", 0x10000, 0x8000, CRC(c0b01772) SHA1(9240bb6b97fe06f5f07b5d65541c4d2f8758cc2a) )
	ROM_LOAD( "voicerom.66", 0x18000, 0x4000, CRC(91d078c1) SHA1(6a93bd7723ef67f461394530a9feee57c8caf7b7) )
	ROM_LOAD( "cgrom60.66",  0x1c000, 0x2000, CRC(d2434f29) SHA1(a56d76f5cbdbcdb8759abe601eab68f01b0a8fe8) )
	ROM_LOAD( "cgrom66.66",  0x1e000, 0x2000, CRC(3ce48c33) SHA1(f3b6c63e83a17d80dde63c6e4d86adbc26f84f79) )
	ROM_LOAD( "kanjirom.66", 0x20000, 0x8000, CRC(20c8f3eb) SHA1(4c9f30f0a2ebbe70aa8e697f94eac74d8241cadd) )
	// exrom                 0x48000, 0x4000

	ROM_REGION( 0x1000, "mcu", ROMREGION_ERASEFF )
	ROM_LOAD( "i8049", 0x0000, 0x1000, NO_DUMP )

	ROM_REGION( 0x4000, "gfx1", 0 )
	ROM_COPY( "maincpu", 0x1c000, 0x00000, 0x4000 )

	ROM_REGION( 0x8000, "gfx2", 0 )
	ROM_COPY( "maincpu", 0x20000, 0x00000, 0x8000 )
ROM_END

ROM_START( pc6001mk2sr )
	ROM_REGION( 0x20000, "sr_sysrom", ROMREGION_ERASEFF )
	ROM_LOAD( "systemrom1.64", 0x10000, 0x10000, CRC(b6fc2db2) SHA1(dd48b1eee60aa34780f153359f5da7f590f8dff4) )
	ROM_LOAD( "systemrom2.64", 0x00000, 0x10000, CRC(55a62a1d) SHA1(3a19855d290fd4ac04e6066fe4a80ecd81dc8dd7) )

	ROM_REGION( 0x1000, "mcu", ROMREGION_ERASEFF )
	ROM_LOAD( "i8049", 0x0000, 0x1000, NO_DUMP )

	ROM_REGION( 0x4000, "cgrom", 0 )
	ROM_LOAD( "cgrom68.64", 0x0000, 0x4000, CRC(73bc3256) SHA1(5f80d62a95331dc39b2fb448a380fd10083947eb) )

	ROM_REGION( 0x4000, "gfx1", 0)
	ROM_COPY( "cgrom", 0, 0, 0x4000 )

	ROM_REGION( 0x8000, "gfx2", 0 )
	ROM_COPY( "sr_sysrom", 0x18000, 0x00000, 0x8000 )
ROM_END

ROM_START( pc6601sr )
	ROM_REGION( 0x20000, "sr_sysrom", ROMREGION_ERASEFF )
	// Note: identical to pc6001mk2sr?
	ROM_LOAD( "systemrom1.68", 0x10000, 0x010000, CRC(b6fc2db2) SHA1(dd48b1eee60aa34780f153359f5da7f590f8dff4) )
	ROM_LOAD( "systemrom2.68", 0x00000, 0x010000, CRC(55a62a1d) SHA1(3a19855d290fd4ac04e6066fe4a80ecd81dc8dd7) )

	// mkII compatible ROMs
	ROM_REGION( 0x20000, "mk2", ROMREGION_ERASEFF )
	ROM_LOAD( "basicrom.68",  0x00000, 0x008000, CRC(516b1be3) SHA1(e9977fc13f65f009f03d0340b1f1eb9a3e586739) )
	ROM_LOAD( "voicerom.68",  0x08000, 0x004000, CRC(37ff3829) SHA1(f887e95e29d071df8329168b48c07b78e492c837) )
	ROM_LOAD( "cgrom60.68",   0x0c000, 0x002000, CRC(331473a9) SHA1(361836f9758d6d9b5133c9dc7860a7c74f9cf596) )
	ROM_LOAD( "cgrom66.68",   0x0e000, 0x002000, CRC(03ba2cf1) SHA1(6fb32a4332b26aba2f28c3d8872cac5606be3998) )
	ROM_LOAD( "sysrom2.68",   0x10000, 0x002000, CRC(07318218) SHA1(061f3e7d6c85a560846856feb55fdc0a1f561548) )

	ROM_REGION( 0x800, "mcu", 0 )
	ROM_LOAD( "d8049hc-016.bin", 0x000, 0x800, CRC(65394e8d) SHA1(761397cbd812623367ef1df5561c6dddb7ebdab7) )

	ROM_REGION( 0x4000, "cgrom", 0 )
	ROM_LOAD( "cgrom68.68",   0x000000, 0x004000, CRC(73bc3256) SHA1(5f80d62a95331dc39b2fb448a380fd10083947eb) )

	ROM_REGION( 0x4000, "gfx1", 0)
	ROM_COPY( "cgrom", 0, 0, 0x4000 )

	ROM_REGION( 0x8000, "gfx2", 0 )
	ROM_COPY( "sr_sysrom", 0x18000, 0x00000, 0x8000 )
ROM_END

COMP( 1981, pc6001,       0,      0,        pc6001,      pc6001, pc6001_state,       empty_init, "NEC",   "PC-6001 (Japan)",              MACHINE_NOT_WORKING )
COMP( 1981, pc6001a,      pc6001, 0,        pc6001,      pc6001, pc6001_state,       empty_init, "NEC",   "PC-6001A \"NEC Trek\" (US)",   MACHINE_NOT_WORKING )
COMP( 1983, pc6001mk2,    0,      0,        pc6001mk2,   pc6001, pc6001mk2_state,    empty_init, "NEC",   "PC-6001mkII (Japan)",          MACHINE_NOT_WORKING )
COMP( 1983, pc6601,       pc6001, 0,        pc6601,      pc6001, pc6601_state,       empty_init, "NEC",   "PC-6601 (Japan)",              MACHINE_NOT_WORKING )
COMP( 1984, pc6001mk2sr,  0,      0,        pc6001mk2sr, pc6001, pc6001mk2sr_state,  empty_init, "NEC",   "PC-6001mkIISR (Japan)",        MACHINE_NOT_WORKING )
COMP( 1984, pc6601sr,     pc6001, 0,        pc6601sr,    pc6001, pc6601sr_state,     empty_init, "NEC",   "PC-6601SR \"Mr. PC\" (Japan)", MACHINE_NOT_WORKING )



pc8001.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder, Angelo Salese
/*

References:
- http://www2.odn.ne.jp/~haf09260/Pc80/EnrPc.htm
- http://home1.catvmics.ne.jp/~kanemoto/n80/inside.html
- http://www.geocities.jp/retro_zzz/machines/nec/8001/index.html
- https://oldcrap.org/2024/07/08/nec-pc-8001/

*/

/*

    TODO:

    - uPD3301 attributes;
    - PCG-1000;
    - Intel 8251;
    - cassette;
    - dip-switches;
    - PC-8011 (expansion unit)
    - PC-8021;
    - PC-8031 (mini disk unit, in progress)
    - pc8001mk2sr: verify how much needs to be ported from pc8801.cpp code
      (Has 3 bitplane GVRAM like PC-8801 V1 mode);
    - waitstates & DMA penalty (some games are suspciously fast);
    - buzzer has pretty ugly aliasing in places;

    Notes:
    - pc8001 v1.01 / v1.02 sports a buggy readout of the expansion ROM at PC=17a1:
      It expects an header read of 0x41-0x42 at offset $6000-6001, but second read at
      PC=0x17aa is just a comparison to $6000 == 0x42, which is impossible at that point
      unless external aid is given. This has been fixed in v1.10;
    - Color Magical (pc8001gp:flop5 option 7) transfers two 8 color screens at
      even/odd frame intervals, effectively boosting the number of available colors to 27.
      This trick is kinda flickery even on real HW, no wonder it looks ugly in MAME,
      can it be improved?

*/

#include "emu.h"
#include "pc8001.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "utf8.h"

void pc8001_base_state::crtc_reverse_w(int state)
{
	// rvv acts as a global flip for reverse attribute meaning
	// (does not act on underlying palette)
	// TODO: what happens if RVV changes mid-frame?
	// I suspect monitor resync more likely than rasters.
	m_screen_reverse = state;
}

UPD3301_FETCH_ATTRIBUTE( pc8001_base_state::attr_fetch )
{
	const u8 attr_max_size = 80;
	const bool is_color_mode = gfx_mode == 0x2;
	std::array<u16, attr_max_size> attr_extend_info = m_crtc->default_attr_fetch(attr_row, gfx_mode, y, attr_fifo_size, row_size);

	// further extend the attributes if we are in color mode
	if (is_color_mode)
	{
		// flgworld (pc8001) gameplay sets up:
		// - 0x00 0x00 0x02 0x88 on playfield
		// \- (wanting the default from the first defined color)
		// - 0x00 0x00 0x00 0x48 0x12 0x88 for first row
		// \- (Expecting "FLAG WORLD" wording to be red while the "P"s in green)
		// undermon (pc8001) instruction screen sets up:
		// - 0x00 0x00 0x06 0xb8
		// \- (expecting blue fill up to 0x06)
		// xak2 & cancanb (pc8801) really expects that the color / decoration is implictly
		// latched from previous line (uses semigfx black tiles for masking)
		if (y == 0)
		{
			// TODO: default values for line 0
			m_attr_color = 0xe8;
			m_attr_decoration = 0x00;
		}

		for (int ex = 0; ex < row_size; ex++)
		{
			u16 cur_attr = attr_extend_info[ex];
			if (BIT(cur_attr, 3))
				m_attr_color = cur_attr;
			else
				m_attr_decoration = cur_attr;
			attr_extend_info[ex] = (m_attr_color << 8) | m_attr_decoration;
		}
	}

	return attr_extend_info;
}

UPD3301_DRAW_CHARACTER_MEMBER( pc8001_base_state::draw_text )
{
	// punt if we are in width 40 (discarded on this end)
	if (sx % 2 && !m_width80)
		return;

	const bool is_color_mode = gfx_mode == 0x2;
	u8 tile;
	const u8 tile_width = m_width80 ? 8 : 16;
	const u8 dot_width = (m_width80 ^ 1) + 1;
	const u8 y_double = m_screen_is_24KHz ? 2 : 1;
	const u8 y_height = y_double * 8;

	bool semigfx_tile, reverse, attr_blink, secret;
	bool upperline, lowerline;
	u8 color;

	if (is_color_mode)
	{
		color = (attr & 0xe000) >> 13;
		semigfx_tile = bool(BIT(attr, 12));
		// bit 7 is used by 2001spc and many others, no effect?
	}
	else
	{
		color = 7;
		semigfx_tile = bool(BIT(attr, 7));
	}

	lowerline = bool(BIT(attr, 5));
	// FIXME: ninjakmb (pc8801) sets 0x50 0x50 for all attribute strips
	// bit 6 is undefined in the specs, is it for masking decoration(s)?
	upperline = bool(BIT(attr, 4));
	reverse = bool(BIT(attr, 2));
	attr_blink = bool(BIT(attr, 1));
	secret = bool(BIT(attr, 0));

	if (semigfx_tile)
		tile = cc;
	else
	{
		if (lc > y_height - 1)
			tile = 0;
		else
			tile = m_cgrom->base()[(cc << 3) | (lc >> (y_double-1))];
	}

	// secret blacks out every tile connections,
	// has lower priority over blinking and other attribute decorations
	if (secret)
		tile = 0;

	if (csr)
		tile ^= 0xff;
	else if (attr_blink_on && attr_blink)
		tile = 0;

	// upper/lower line aren't affected by secret and blinking, only reverse
	// TODO: should downshift chars by one
	if (lc == 0 && upperline)
		tile = 0xff;

	if (is_lowestline && lowerline)
		tile = 0xff;

	if (reverse ^ m_screen_reverse)
		tile ^= 0xff;

//  if (m_width80)
	{
		u8 pen_dot;

		for (int xi = 0; xi < tile_width; xi += dot_width)
		{
			int res_x = (sx * 8) + xi;
			if (semigfx_tile)
			{
				u8 mask = (xi & (4 << (dot_width - 1))) ? 0x10 : 0x01;
				mask <<= (lc & (0x3 << y_double)) >> y_double;
				pen_dot = tile & mask;
			}
			else
			{
				pen_dot = tile;
				pen_dot = (pen_dot >> (7 - (xi >> (dot_width - 1)))) & 1;
			}

			if (!pen_dot)
				continue;

			for (int di = 0; di < dot_width; di++)
				bitmap.pix(y, res_x + di) = m_crtc_palette->pen(color);
		}
	}
}

uint32_t pc8001_state::screen_update( screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	bitmap.fill(0, cliprect);
	// TODO: superimposing
	// TODO: merging with previous frame for Color Magical (is it driver area?)
	m_crtc->screen_update(screen, bitmap, cliprect);
	return 0;
}

/* Read/Write Handlers */

/*
 * ---- ---x RTC C0
 * ---- --x- RTC C1
 * ---- -x-- RTC C2
 * ---- x--- RTC DATA IN
 */
void pc8001_base_state::port10_w(uint8_t data)
{
	// RTC
	m_rtc->c0_w(BIT(data, 0));
	m_rtc->c1_w(BIT(data, 1));
	m_rtc->c2_w(BIT(data, 2));
	m_rtc->data_in_w(BIT(data, 3));

	// centronics
	m_cent_data_out->write(data);
}

/*
 * I/O Port $30 (w/o) "System Control Port (1)"
 * N88-BASIC buffer port $e6c0
 *
 * Virtually same between PC-8001 and PC-8801
 *
 * --xx ---- BS2, BS1: USART channel control
 * --00 ----           CMT 600 bps
 * --01 ----           CMT 1200 bps
 * --10 ----           RS-232C async mode
 * --11 ----           RS-232C sync mode
 * ---- x--- MTON: CMT motor control (active high)
 * ---- -x-- CDS: CMT carrier control (1) mark (0) space
 * ---- --x- /COLOR: CRT display mode control (0) color mode (1) monochrome
 * ---- ---x /40: CRT display format control (1) 80 chars per line (0) 40 chars
 *
 */
void pc8001_base_state::port30_w(uint8_t data)
{
	m_width80 = BIT(data, 0);
	m_color = BIT(data, 1);

	m_cassette->change_state(BIT(data, 3) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
}

void pc8001mk2_state::port31_w(uint8_t data)
{
	/*

	    bit     description

	    0       expansion ROM (0=expansion, 1=N80)
	    1       memory mode (0=ROM, 1=RAM)
	    2       color mode (0=attribute, 1=B&W)
	    3       graphics enable
	    4       resolution (0=640x200, 1=320x200)
	    5       background color
	    6       background color
	    7       background color

	*/
	membank("bank2")->set_entry(data & 1);
}

void pc8001_base_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

void pc8001_base_state::write_centronics_ack(int state)
{
	m_centronics_ack = state;
}

uint8_t pc8001_state::port40_r()
{
	/*

	    bit     description

	    0       BUSY
	    1       ACK
	    2       CMT CDIN
	    3       EXP /EXTON
	    4       RTC DATA OUT
	    5       VRTC
	    6
	    7

	*/

	uint8_t data = 0x00;

	data |= m_centronics_busy;
	data |= m_centronics_ack << 1;
	data |= m_rtc->data_out_r() << 4;
	data |= m_crtc->vrtc_r() << 5;
	// TODO: enable line from pc80s31k (bit 3, active_low)

	return data;
}

/*
 * --x- ---- SPEAKER
 * ---- x--- CRT /CLDS CLK
 * ---- -x-- RTC CLK
 * ---- --x- RTC STB
 * ---- ---x Centronics STROBE
 */
void pc8001_state::port40_w(uint8_t data)
{
	m_centronics->write_strobe(BIT(data, 0));

	m_rtc->stb_w(BIT(data, 1));
	m_rtc->clk_w(BIT(data, 2));

	m_beep->set_state(BIT(data, 5));
}

/* Memory Maps */

void pc8001_state::pc8001_map(address_map &map)
{
	map(0x0000, 0x5fff).bankrw("bank1");
	map(0x6000, 0x7fff).bankrw("bank2");
	map(0x8000, 0xffff).bankrw("bank3");
}

void pc8001_state::pc8001_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x00).portr("Y0");
	map(0x01, 0x01).portr("Y1");
	map(0x02, 0x02).portr("Y2");
	map(0x03, 0x03).portr("Y3");
	map(0x04, 0x04).portr("Y4");
	map(0x05, 0x05).portr("Y5");
	map(0x06, 0x06).portr("Y6");
	map(0x07, 0x07).portr("Y7");
	map(0x08, 0x08).portr("Y8");
	map(0x09, 0x09).portr("Y9");
	map(0x10, 0x10).mirror(0x0f).w(FUNC(pc8001_state::port10_w));
	map(0x20, 0x21).mirror(0x0e).rw(I8251_TAG, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x30, 0x30).mirror(0x0f).w(FUNC(pc8001_state::port30_w));
	map(0x40, 0x40).mirror(0x0f).rw(FUNC(pc8001_state::port40_r), FUNC(pc8001_state::port40_w));
	map(0x50, 0x51).rw(m_crtc, FUNC(upd3301_device::read), FUNC(upd3301_device::write));
	map(0x60, 0x68).rw(m_dma, FUNC(i8257_device::read), FUNC(i8257_device::write));
//  map(0x70, 0x7f) unused
//  map(0x80, 0x80).mirror(0x0f).w(FUNC(pc8001_state::pc8011_ext0_w));
//  map(0x90, 0x90).mirror(0x0f).w(FUNC(pc8001_state::pc8011_ext1_w));
//  map(0xa0, 0xa0).mirror(0x0f).w(FUNC(pc8001_state::pc8011_ext2_w));
//  map(0xb0, 0xb0).r(FUNC(pc8001_state::pc8011_gpio8_r));
//  map(0xb1, 0xb1).w(FUNC(pc8001_state::pc8011_gpio8_w));
//  map(0xb2, 0xb2).r(FUNC(pc8001_state::pc8011_gpio4_r));
//  map(0xb3, 0xb3).w(FUNC(pc8001_state::pc8011_gpio4_w));
//  map(0xc0, 0xc0).rw(PC8011_CH1_I8251_TAG, FUNC(i8251_device::data_r), FUNC(i8251_device::data_w));
//  map(0xc1, 0xc1).rw(PC8011_CH1_I8251_TAG, FUNC(i8251_device::status_r), FUNC(i8251_device::control_w));
//  map(0xc2, 0xc2).rw(PC8011_CH2_I8251_TAG, FUNC(i8251_device::data_r), FUNC(i8251_device::data_w));
//  map(0xc3, 0xc3).rw(PC8011_CH2_I8251_TAG, FUNC(i8251_device::status_r), FUNC(i8251_device::control_w));
//  map(0xc8, 0xc8) RS-232 output enable?
//  map(0xca, 0xca) RS-232 output disable?
//  map(0xd0, 0xd3).rw(PC8011_IEEE488_I8255A_TAG, FUNC(i8255_device::read), FUNC(i8255_device::write));
//  map(0xd8, 0xd8).r(FUNC(pc8001_state::pc8011_ieee488_control_signal_input_r));
//  map(0xda, 0xda).r(FUNC(pc8001_state::pc8011_ieee488_bus_address_mode_r));
//  map(0xdc, 0xdc).w(FUNC(pc8001_state::pc8011_ieee488_nrfd_w));
//  map(0xde, 0xde).w(FUNC(pc8001_state::pc8011_ieee488_bus_mode_control_w));
//  map(0xe0, 0xe3).w(FUNC(pc8001_state::expansion_storage_mode_w));
//  map(0xe4, 0xe4).mirror(0x01).w(FUNC(pc8001_state::irq_level_w));
//  map(0xe6, 0xe6).w(FUNC(pc8001_state::irq_mask_w));
//  map(0xe7, 0xe7).w(FUNC(pc8001_state::pc8012_memory_mode_w));
//  map(0xe8, 0xfb) unused
	map(0xfc, 0xff).m(m_pc80s31, FUNC(pc80s31_device::host_map));
}

void pc8001mk2_state::pc8001mk2_map(address_map &map)
{
	map(0x0000, 0x5fff).bankrw("bank1");
	map(0x6000, 0x7fff).bankrw("bank2");
	map(0x8000, 0xbfff).bankrw("bank3");
	map(0xc000, 0xffff).bankrw("bank4");
}

void pc8001mk2_state::pc8001mk2_io(address_map &map)
{
	pc8001_io(map);
	map(0x30, 0x30).portr("DSW1").w(FUNC(pc8001mk2_state::port30_w));
	map(0x31, 0x31).portr("DSW2").w(FUNC(pc8001mk2_state::port31_w));
//  map(0x5c, 0x5c).w(FUNC(pc8001mk2_state::gram_on_w));
//  map(0x5f, 0x5f).w(FUNC(pc8001mk2_state::gram_off_w));
//  map(0xe8, 0xe8) kanji_address_lo_w, kanji_data_lo_r
//  map(0xe9, 0xe9) kanji_address_hi_w, kanji_data_hi_r
//  map(0xea, 0xea) kanji_readout_start_w
//  map(0xeb, 0xeb) kanji_readout_end_w
//  map(0xf3, 0xf3) DMA type disk unit interface selection port
//  map(0xf4, 0xf4) DMA type 8 inch control
//  map(0xf5, 0xf5) DMA type 8 inch margin control
//  map(0xf6, 0xf6) DMA type 8 inch FDC status
//  map(0xf7, 0xf7) DMA type 8 inch FDC data register
//  map(0xf8, 0xf8) DMA type 5 inch control
//  map(0xf9, 0xf9) DMA type 5 inch margin control
//  map(0xfa, 0xfa) DMA type 5 inch FDC status
//  map(0xfb, 0xfb) DMA type 5 inch FDC data register
}

void pc8001mk2sr_state::port33_w(u8 data)
{
	// TODO: needs progressive flush
#ifdef UNUSED_FUNCTION
	if (data & 0x80)
	{
		membank("bank1")->set_entry(2);
		membank("bank2")->set_entry(2 | (m_n80sr_bank & 1));
	}
	else
	{
		membank("bank1")->set_entry(0);
		membank("bank2")->set_entry(0);
	}
#endif
}

u8 pc8001mk2sr_state::port71_r()
{
	return m_n80sr_bank;
}

void pc8001mk2sr_state::port71_w(u8 data)
{
	m_n80sr_bank = data;
}

void pc8001mk2sr_state::pc8001mk2sr_io(address_map &map)
{
	pc8001mk2_io(map);
	map(0x33, 0x33).w(FUNC(pc8001mk2sr_state::port33_w));
	map(0x71, 0x71).rw(FUNC(pc8001mk2sr_state::port71_r), FUNC(pc8001mk2sr_state::port71_w));
}

/* Input Ports */

static INPUT_PORTS_START( pc8001 )
	PORT_START("Y0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0_PAD)      PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1_PAD)      PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2_PAD)      PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3_PAD)      PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4_PAD)      PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5_PAD)      PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6_PAD)      PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7_PAD)      PORT_CHAR(UCHAR_MAMEKEY(7_PAD))

	PORT_START("Y1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8_PAD)      PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9_PAD)      PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ASTERISK)   PORT_CHAR(UCHAR_MAMEKEY(ASTERISK))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD)   PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PGUP)       PORT_CHAR(UCHAR_MAMEKEY(EQUALS_PAD))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PGDN)       PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL_PAD)    PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13)

	PORT_START("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('@')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)          PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)          PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)          PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)          PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)          PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)          PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)          PORT_CHAR('g') PORT_CHAR('G')

	PORT_START("Y3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)          PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)          PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)          PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)          PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)          PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)          PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)          PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("Y4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)          PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)          PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)          PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)          PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)          PORT_CHAR('w') PORT_CHAR('W')

	PORT_START("Y5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)          PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR(0xA5)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR(']')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('^')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-') PORT_CHAR('=')

	PORT_START("Y6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0)          PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("Y7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("  _") PORT_CODE(KEYCODE_DEL)           PORT_CHAR(0xff) PORT_CHAR('_')

	PORT_START("Y8")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Clr Home") PORT_CODE(KEYCODE_HOME)     PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP)  PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT)    PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Del Ins") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(UCHAR_MAMEKEY(DEL)) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Grph") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Kana") PORT_CODE(KEYCODE_LCONTROL) PORT_TOGGLE
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RCONTROL)                       PORT_CHAR(UCHAR_SHIFT_2)

	PORT_START("Y9")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Stop") PORT_CODE(KEYCODE_TILDE)        PORT_CHAR(3)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1)                             PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2)                             PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3)                             PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F4)                             PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F5)                             PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE)                          PORT_CHAR(' ')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ESC)                            PORT_CHAR(27)

//  PORT_START("DSW1")
INPUT_PORTS_END

static INPUT_PORTS_START( pc8001mk2 )
	PORT_INCLUDE( pc8001 )

	PORT_START("DSW1")
	PORT_DIPNAME( 0x01, 0x00, "Boot Mode" )
	PORT_DIPSETTING(    0x00, "N-BASIC" )
	PORT_DIPSETTING(    0x01, "N80-BASIC" )
	PORT_DIPNAME( 0x02, 0x02, "DSW1" )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("DSW2")
	PORT_DIPNAME( 0x01, 0x01, "DSW2" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( pc8001mk2sr )
	PORT_INCLUDE( pc8001mk2 )

	PORT_MODIFY("DSW1")
	// This is really a tri-state dip on front panel
	// BIOS just expects bit 1 to be off for SR mode
	PORT_DIPNAME( 0x03, 0x02, "Boot Mode" )
	PORT_DIPSETTING(    0x00, "N80SR-BASIC (duplicate)")
	PORT_DIPSETTING(    0x01, "N80SR-BASIC" )
	PORT_DIPSETTING(    0x02, "N-BASIC" )
	PORT_DIPSETTING(    0x03, "N80-BASIC" )
	PORT_DIPNAME( 0x04, 0x04, "DSW1" )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

/* 8257 Interface */

void pc8001_base_state::hrq_w(int state)
{
	/* HACK - this should be connected to the BUSREQ line of Z80 */
	m_maincpu->set_input_line(INPUT_LINE_HALT, state);

	/* HACK - this should be connected to the BUSACK line of Z80 */
	m_dma->hlda_w(state);
}

uint8_t pc8001_base_state::dma_mem_r(offs_t offset)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	return program.read_byte(offset);
}

/* Machine Initialization */

void pc8001_base_state::machine_start()
{
	m_screen_reverse = false;

	/* initialize RTC */
	m_rtc->cs_w(1);
	m_rtc->oe_w(1);

	save_item(NAME(m_width80));
	save_item(NAME(m_color));
	save_item(NAME(m_screen_reverse));
	save_item(NAME(m_screen_is_24KHz));
}

void pc8001_state::machine_start()
{
	pc8001_base_state::machine_start();

	address_space &program = m_maincpu->space(AS_PROGRAM);

	/* initialize DMA */
	m_dma->ready_w(1);

	/* setup memory banking */
	uint8_t *ram = m_ram->pointer();

	membank("bank1")->configure_entry(1, m_rom->base());
	program.install_read_bank(0x0000, 0x5fff, membank("bank1"));
	program.unmap_write(0x0000, 0x5fff);
	membank("bank2")->configure_entry(1, m_rom->base() + 0x6000);

	switch (m_ram->size())
	{
	case 16*1024:
		membank("bank3")->configure_entry(0, ram);
		program.unmap_readwrite(0x8000, 0xbfff);
		program.install_readwrite_bank(0xc000, 0xffff, membank("bank3"));
		break;

	case 32*1024:
		membank("bank3")->configure_entry(0, ram);
		program.unmap_readwrite(0x8000, 0xbfff);
		program.install_readwrite_bank(0x8000, 0xffff, membank("bank3"));
		break;

	case 64*1024:
		membank("bank1")->configure_entry(0, ram);
		membank("bank2")->configure_entry(0, ram + 0x6000);
		membank("bank3")->configure_entry(0, ram + 0x8000);
		program.install_readwrite_bank(0x0000, 0x5fff, membank("bank1"));
		program.install_readwrite_bank(0x6000, 0xbfff, membank("bank2"));
		program.install_readwrite_bank(0x8000, 0xffff, membank("bank3"));
//      membank("bank2")->set_entry(0);
		break;
	}

	// PC8001 is 15KHz only
	set_screen_frequency(false);
}

void pc8001_state::machine_reset()
{
	membank("bank1")->set_entry(1);
	membank("bank2")->set_entry(1);
	membank("bank3")->set_entry(0);
}

void pc8001mk2sr_state::machine_start()
{
	pc8001_state::machine_start();

	membank("bank1")->configure_entry(2, m_n80sr_rom->base());
	membank("bank2")->configure_entry(2, m_n80sr_rom->base() + 0x6000);
	membank("bank2")->configure_entry(3, m_n80sr_rom->base() + 0x8000);

	save_item(NAME(m_n80sr_bank));
}

void pc8001mk2sr_state::machine_reset()
{
	pc8001_state::machine_reset();

	//membank("bank1")->set_entry(2);
	//membank("bank2")->set_entry(2);
}

/* Machine Drivers */

void pc8001_state::pc8001(machine_config &config)
{
	constexpr XTAL MASTER_CLOCK = XTAL(4'000'000);
	constexpr XTAL VIDEO_CLOCK = XTAL(14'318'181);

	/* basic machine hardware */
	Z80(config, m_maincpu, MASTER_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc8001_state::pc8001_map);
	m_maincpu->set_addrmap(AS_IO, &pc8001_state::pc8001_io);

	PC80S31(config, m_pc80s31, MASTER_CLOCK);
	config.set_perfect_quantum(m_maincpu);
	config.set_perfect_quantum("pc80s31:fdc_cpu");

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(VIDEO_CLOCK, 896, 0, 640, 260, 0, 200);
	m_screen->set_screen_update(FUNC(pc8001_state::screen_update));
//  m_screen->set_palette(m_crtc_palette);

	PALETTE(config, m_crtc_palette, palette_device::BRG_3BIT);

	UPD3301(config, m_crtc, VIDEO_CLOCK);
	m_crtc->set_character_width(8);
	m_crtc->set_display_callback(FUNC(pc8001_state::draw_text));
	m_crtc->set_attribute_fetch_callback(FUNC(pc8001_state::attr_fetch));
	m_crtc->drq_wr_callback().set(m_dma, FUNC(i8257_device::dreq2_w));
	m_crtc->rvv_wr_callback().set(FUNC(pc8001_state::crtc_reverse_w));
	m_crtc->set_screen(m_screen);

	I8257(config, m_dma, MASTER_CLOCK);
	m_dma->out_hrq_cb().set(FUNC(pc8001_state::hrq_w));
	m_dma->in_memr_cb().set(FUNC(pc8001_state::dma_mem_r));
	m_dma->out_iow_cb<2>().set(m_crtc, FUNC(upd3301_device::dack_w));

	/* devices */
	I8251(config, I8251_TAG, 0);

	UPD1990A(config, m_rtc);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set(FUNC(pc8001_state::write_centronics_ack));
	m_centronics->busy_handler().set(FUNC(pc8001_state::write_centronics_busy));

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	RAM(config, RAM_TAG).set_default_size("16K").set_extra_options("32K,64K");

	SOFTWARE_LIST(config, "disk_n_list").set_original("pc8001_flop");

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	// TODO: unknown clock, is it really a beeper?
	BEEP(config, m_beep, 2400).add_route(ALL_OUTPUTS, "mono", 0.25);
}

void pc8001mk2_state::pc8001mk2(machine_config &config)
{
	pc8001(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc8001mk2_state::pc8001mk2_map);
	m_maincpu->set_addrmap(AS_IO, &pc8001mk2_state::pc8001mk2_io);

	// TODO: video HW has extra GVRAM setup

	RAM(config.replace(), RAM_TAG).set_default_size("64K");

	SOFTWARE_LIST(config, "disk_n80_list").set_original("pc8001mk2_flop");
}

void pc8001mk2sr_state::pc8001mk2sr(machine_config &config)
{
	pc8001mk2(config);
	m_maincpu->set_addrmap(AS_IO, &pc8001mk2sr_state::pc8001mk2sr_io);

	// TODO: mods for SR mode support

	SOFTWARE_LIST(config, "disk_n80sr_list").set_original("pc8001mk2sr_flop");
}

/* ROMs */

ROM_START( pc8001 )
	ROM_REGION( 0x8000, Z80_TAG, ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS("v110")
	// PCB pictures shows divided by 3 ROMs (and 4th socket unpopulated)
	// D2364C ROMs from a pc8001b PCB:
	// - p12019-106.ic10 072NBASIC
	// - p11219-105.ic11 073NBASIC
	// - p12029-106.ic12 171NBASIC
	ROM_SYSTEM_BIOS( 0, "v101", "N-BASIC v1.01" )
	ROMX_LOAD( "n80v101.rom", 0x00000, 0x6000, BAD_DUMP CRC(a2cc9f22) SHA1(6d2d838de7fea20ddf6601660d0525d5b17bf8a3), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v102", "N-BASIC v1.02" )
	ROMX_LOAD( "n80v102.rom", 0x00000, 0x6000, BAD_DUMP CRC(ed01ca3f) SHA1(b34a98941499d5baf79e7c0e5578b81dbede4a58), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v110", "N-BASIC v1.10" )
	ROMX_LOAD( "n80v110.rom", 0x00000, 0x6000, BAD_DUMP CRC(1e02d93f) SHA1(4603cdb7a3833e7feb257b29d8052c872369e713), ROM_BIOS(2) )
	// empty socket, cfr. notes in header for usage instructions
	ROM_LOAD_OPTIONAL( "exprom.ic13", 0x6000, 0x2000, NO_DUMP )

	ROM_REGION( 0x800, CGROM_TAG, 0)
	ROM_LOAD( "font.rom", 0x000, 0x800, CRC(56653188) SHA1(84b90f69671d4b72e8f219e1fe7cd667e976cf7f) )
ROM_END

ROM_START( pc8001mk2 )
	ROM_REGION( 0x8000, Z80_TAG, 0 )
	// N-BASIC v1.3
	// N80-BASIC v1.0
	ROM_LOAD( "n80_2.rom", 0x0000, 0x8000, CRC(03cce7b6) SHA1(c12d34e42021110930fed45a8af98db52136f1fb) )

	ROM_REGION( 0x800, CGROM_TAG, 0)
	ROM_LOAD( "font.rom", 0x0000, 0x0800, CRC(56653188) SHA1(84b90f69671d4b72e8f219e1fe7cd667e976cf7f) )

	ROM_REGION( 0x20000, "kanji", 0)
	ROM_LOAD( "kanji1.rom", 0x00000, 0x20000, CRC(6178bd43) SHA1(82e11a177af6a5091dd67f50a2f4bafda84d6556) )
ROM_END

ROM_START( pc8001mk2sr )
	ROM_REGION( 0x8000, Z80_TAG, 0 )
	// N-BASIC v1.6
	// N80-BASIC v1.2
	ROM_LOAD( "n80_2sr.rom", 0x0000, 0x8000, CRC(dcb71282) SHA1(e8db5dc5eae11da14e48656d324874e59f2e3844) )

	ROM_REGION (0x10000, N80SR_ROM_TAG, ROMREGION_ERASEFF )
	// N80SR-BASIC v1.0
	ROM_LOAD( "n80_3.rom",    0x0000, 0xa000, BAD_DUMP CRC(d99ef247) SHA1(9bfa5009d703cd31caa734d932d2a847d74cbfa6) )
	// TODO: empty socket at ic77

	ROM_REGION( 0x2000, CGROM_TAG, 0)
	ROM_LOAD( "font80sr.rom", 0x000000, 0x001000, CRC(784c0b17) SHA1(565dc8e5e46b1633cb434d12b4d8b3a662546b33) )
	ROM_LOAD( "fonthira.rom", 0x001000, 0x000800, CRC(fe7059d5) SHA1(10c5f85adcce540cbd0a11352e2c38a84c989a26) )
	ROM_LOAD( "fontkata.rom", 0x001800, 0x000800, CRC(56653188) SHA1(84b90f69671d4b72e8f219e1fe7cd667e976cf7f) )

	ROM_REGION( 0x20000, "kanji", 0)
	ROM_LOAD( "kanji1.rom", 0x00000, 0x20000, CRC(6178bd43) SHA1(82e11a177af6a5091dd67f50a2f4bafda84d6556) )
ROM_END

/* System Drivers */

//    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT   CLASS            INIT        COMPANY  FULLNAME       FLAGS
// 1978?, pc8001g, Wirewrapped prototype version
COMP( 1979, pc8001,      0,      0,      pc8001,      pc8001,      pc8001_state,      empty_init, "NEC",   "PC-8001",     MACHINE_NOT_WORKING )
// 1981 pc8001a, US version of PC-8001 with Greek alphabet instead of Kana
COMP( 1983, pc8001mk2,   pc8001, 0,      pc8001mk2,   pc8001mk2,   pc8001mk2_state,   empty_init, "NEC",   "PC-8001mkII", MACHINE_NOT_WORKING )
COMP( 1985, pc8001mk2sr, pc8001, 0,      pc8001mk2sr, pc8001mk2sr, pc8001mk2sr_state, empty_init, "NEC",   "PC-8001mkIISR", MACHINE_NOT_WORKING )



pc8401a.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder, Angelo Salese
/*

    NEC PC-8401A-LS "Starlet"
    NEC PC-8500 "Studley"

    TODO:

    - blinking, uses unimplemented cursor in graphics mode for SED1330;
    - RTC TP pulse;
    - sleep mode ignores wake up, does it needs an alarm from RTC?
    - some unclear bits in the banking scheme;
    - mirror e800-ffff to 6800-7fff (why? -AS);
    - soft power on/off;
    - idle sleep timer off by a bunch of seconds ("option" -> "power" to test);
    - complete keyboard mapping;
    \- ALT key is guessed: it seems to match manual (ALT mode for math keys, ALT + SHIFT for symbols),
       but PC-8500 seems to have no support for those so it just sends regular keyboard chars.
       Note: holding 0-0 & 0-1 at boot will draw the aforementioned special chars *only*.
       Is it related to not having CALC app as well? (-> verify when PC-8401* dump surfaces)
    \- LEDs, if any;
    - No sound, uses beeper, likely at the same position as PC-8001/PC-8801;
    - 8251 USART;
    - modem (OKI M6946);
    - PC-8431A FDC is same family as PC-80S31K, basically the 3.5" version of it.
      Likely none of the available BIOSes fits here.

    Notes:
    - Need to format internal RAM before using WS, "format -> F1 -> y"

    - peripherals
        * PC-8431A Dual Floppy Drive
        * PC-8441A CRT / Disk Interface (MC6845, monochrome & color variants)
        * PC-8461A 1200 Baud Modem
        * PC-8407A 128KB RAM Expansion
        * PC-8508A ROM/RAM Cartridge (32K & 128K versions)

    - Use the 600 baud save rate (PIP CAS2:=A:<filename.ext> this is more reliable than the 1200
      baud (PIP CAS:=A:<filename.ext> rate.

===================================================================================================

NEC PC-8401BD, NEC, 1984
Hardware info by Guru
---------------------

This is a portable laptop computer made by NEC in 1984.

Main Board
----------

72404553 PDS-E-0154 NEC-14T MADE IN JAPAN PAT,P-END
Sticker: AIA 606 0325
|------------------------------------------------------------|
|   CMT     PRINTER      RS232        TEL  LINE  RES  DC_IN  |
|                                                            |
|                     D71051G                          PWR_SW|
|                                                    SW1     |
|                                     VR2   POWERI           |
|                        X1                  3.6V_NICAD      |
|--|                       LCD          BATTL                |
   |        BX7307          BEEPER           4P           VR1|
   |        X            32.786kHz                           |
 |-|        Y     D1990      VR3                             |
 | |                                                         |
 | |      |--SUB-BOARD-ABOVE---|                             |
 | |      | IC14               |       PC817 PC817           |
 | |SB    |                    |       PC817 PC817           |
 | |      |                    |                             |
 | |      |CN1              CN2|                             |
 | |      |        IC17        |         X3     BX7309A      |
 |-|      |                    | IC20  M6926                 |
   |      |--------------------|                             |
   |                                                         |
|--|       D70008C    D23C1000                     KEY       |
|                                                            |
|------------------------------------------------------------|
Notes:
      D71051G - NEC D71051G Serial Control Unit (CMOS USART)
         IC14 - NEC D65002G066 uPD65000-series CMOS Gate Array
         IC17 - SMOS Systems SLA6140F1G SLA 6000-series CMOS Gate Array
          CMT - Cassette/Tape Recorder Port
     TEL/LINE - RJ11 Sockets for Telephone Line In (for use with internal modem) and Telephone Handset Pass-Through
       PWR_SW - Power On/Off Switch
           SB - System Bus Connector. This allows connection of a RAM cart, a ROM cart (do any exist?) and various plug-in expansions.
                Known Expansions:
                                PC-8281A Data Recorder (for storing data on tape/cassette)
                                PC-8405A ROM Cartridge (for what software exactly?)
                                PC-8406A 32kB Battery-Backed RAM Cartridge. This allows total system RAM 64kB + 32kB external RAM for file storage.
                                PC-8407A 128kB Battery-Backed RAM Cartridge. Q: Does this increase system RAM or only battery-backed file/data storage?
                                PC-8431A 3 1/2" Micro Floppy Disk Unit
                                PC-8433A Disk Adapter
                                PC-8441A CRT/Disk Adapter. Provides Monochrome and RGB CRT video outputs with the PC-8433A Disk Adapter.
                                PC-8461A 1200 Baud Modem
      BX7309A - ROHM custom SIL module. Purpose unknown, possibly keyboard or modem related
       BX7307 - ROHM BX7307 SIL module. Purpose unknown, possibly reset-related
       POWERI - Power-related SIL Module
        BATTL - Connector for 4x C-cell battery power
          LCD - 16-pin connector for LCD panel flat cable
            X - SIL Module. Purpose unknown, possibly CMT-related
            Y - SIL Module. Purpose unknown, possibly RS232-related
          RES - Fujisoku AP1C 61 Momentary Push Button. This is a manual reset switch.
      D70008C - NEC D70008C Z80-compatible Microprocessor. This doesn't have higher speed-rated markings so clock input
                is likely to be 3.9935MHz [7.987/2]
     D23C1000 - NEC D23C1000 28-pin 128kBx8-bit mask ROM at location IC24. No label present on ROM. Board marked 'MRIO'
                This contains the system BIOS, CP/M Operating System and some built-in applications including Wordstar.
        M6926 - OKI M6926 300-Baud Single Chip Modem, compatible with ITU-T V.21. Clock input 3.579545MHz
        D1990 - NEC uPD1990AC Serial I/O Calender and Clock CMOS LSI. Clock input 32.768kHz
         IC20 - NEC uPC358C Dual Operational Amplifier
           4P - 4-pin connector for battery-low and sleep LEDs that are located on the top right side above the keyboard
           X1 - 7.987MHz Oscillator
           X2 - 32.768kHz Crystal
           X3 - 3.579545MHz Crystal
       BEEPER - Taiyo Piezo Beeper/Speaker
        DC_IN - 9V DC Power Input Barrel Jack
        PC817 - Sharp PC817 Optocoupler
          VR1 - LCD Contrast Adjustment Potentiometer/Wheel
          VR2 - Potentiometer for ?. Possibly 5V Adjustment?
          VR3 - Adjustable Capacitor Potentiometer for RTC 32.768kHz frequency fine tuning
      PRINTER - DB-25 Parallel Port
        RS232 - 2x DE-9 Serial Ports
          KEY - 15-pin connector for keyboard cable
          SW1 - 2-position switch on bottom of main board. Switching it resets and clears the RAM disk.
                The switch actually disconnects the RAM from VCC and connects it to GND thus fully draining any charge from the RAMs and
                setting the contents to be uninitialized. The battery-backed RAM must be formated again to use it.



Internal RAM Board
------------------

This plugs into CN1 and CN2 on the main board and provides 64kB RAM.
RAM is split between a RAM disk and available memory.
32kB is battery-backed for file/data storage used with the built-in applications
and 32kB is for the system.

When a RAM expansion cart is plugged in, the system RAM becomes 64kB and
data/files are stored on the cart battery-backed RAM.
The 32kB RAM expansion can be used in one of 2 ways....
1) In 32kB mode: Two RAM disks of 32kB each accessible as Drive A: and Drive B:
   When swapping drives the memory available to the system is conditional on the RAM usage in the bank selected.
2) In 64kB Mode: Drive A: is on the RAM cart (you can store a maximum of 32kB there). However memory is stacked
   in this mode. The more free space you have on the cart the higher available to the system.

If the floppy disk unit or any non-RAM cart is used the RAM expansion cart can't be used since all expansions
share the same main board expansion bus connector. None of the known expansions contain any battery-backed RAM
and none of the known expansions have a pass-through connector therefore using a RAM expansion cart and also
any other bus connector expansion together is not possible.
However it is assumed that when using the floppy disk unit the RAM memory map switches to '64kB Mode' and
drives A: and B: are changed automatically to the floppy disk unit.

PWD-456  D6CA090   7240456
Sticker: 606 0325
|-----------------------|
|                       |
|D4364 D4364 D4364 D4364|
|                       |
|                       |
|CN1                 CN2|
|                       |
|                       |
|D4364 D4364 D4364 D4364|
|                       |
|-----------------------|
Notes:
      D4364 - NEC uPD4364-20L 8kBx8-bit CMOS Static RAM.
              Total 64kB system memory with 32kB devoted to battery-backed file storage.
              However the system memory map changes depending on the type of expansion plugged in.

Memory Map for ROM Program Cart Mode:
FFFFH -------------------
            BIOS             2.5kBytes
F600H -------------------
            BDOS             3.5kBytes
E800H -------------------
            TPA              26kBytes
8100H -------------------
          ZERO PAGE          256bytes
8000H -------------------
             ROM             32kBytes
0000H -------------------


Memory Map for 32kB CP/M Mode:
FFFFH -------------------
            BIOS             2.5kBytes
F600H -------------------
            BDOS             3.5kBytes
E800H -------------------
          AVAILABLE          32kBytes
6800H -------------------
            TPA              26kBytes
0100H -------------------
          ZERO PAGE          256bytes
0000H -------------------


Memory Map for 64kB CP/M Mode:
FFFFH -------------------
            BIOS             2.5kBytes
F600H -------------------
            BDOS             3.5kBytes
E800H -------------------
            TPA              58kBytes
0100H -------------------
          ZERO PAGE          256bytes
0000H -------------------


LCD Panel
---------

The LCD panel provides an 80-column x 16-line text display.

EPSON P300012000
Sticker: EPSON ECM-A0053-1 663C44 MADE IN JAPAN
|------------------------------------------------|
|SED1180F  SED1180F  SED1180F  SED1180F  SED1190F|
|                                                |
|                                                |
|SED1180F  SED1180F  SED1180F  SED1180F  SED1190F|
|                                                |
|                                                |
|                                                |
|                          E-1330                |
|                                 4364  4364     |
|------------------------------------------------|
Notes:
      SED1180F - Epson SED1180F 64-dot LCD Segment Column Driver (x8) \ Based on the number of chips and pixel drivers this might
      SED1190F - Epson SED1190F 64-dot LCD Segment Row Driver (x2)    / give a total of 512 x 128 pixels?
       E-1330F - Epson E-1330F LCD Graphics and Display Controller with built-in 4kB character ROM
          4364 - NEC uPD4364-20L 8kBx8-bit CMOS Static RAM Display Memory. Total Video RAM = 16kB


Keyboard layout is only slightly non-standard and most of the keys are in the standard position....
|-------------- |-------------------------------------------------------|
|REMOVABLE PANEL|                                            SLEEP LED  |
| FOR BATTERIES |                                       LOW BATTERY LED |
|---------------|                                                 ^     |
|                                                              <    >   | <--arrow keys
|STOP ESC   F1        F2        F3        F4        F5           \/     |
|~     !    @    #    $    %    ^    &    *    (    )    _   +   INS    |
|`     1    2    3    4    5    6    7    8    9    0    -   =   DEL    |
|                                                 {   }                 |
|TAB     Q    W   E   R   T   Y   U   I   O   P   [   ]                 |
|                                              :   "                    |
|CTRL CAPS A   S   D   F   G   H   J   K   L   ;   '        ENTER       |
|                                       <   >   ?                       |
|SHIFT      Z   X   C   V   B   N   M   ,   .   /           SHIFT       |
|           |                                                           |
|           \        S---P---A---C---E          ALT   NUM               |
|                                                                       |
|                                                                       |
|-----------------------------------------------------------------------|
Notes:
      Removable panel on top left gives access to a battery bay holding 4x C-cell batteries. Total voltage is 6V.

      LOW BATTERY LED lights on solid when internal C-cell battery voltage is low.
      The RAM requires at least 2.0V to retain data so the trigger voltage would typically be ~2.5V (for the RAM) but
      is likely to be around 4.75V due to the C-cell batteries also powering the whole computer when the AC-DC adapter
      is not connected. The computer likely will not power up unless it has at least 4.75V at the battery terminals.
      When the C cell battery power is exhausted the main board will power off and the 3.6V nicad will continue to power
      the battery-backed RAM and RTC chip.

      SLEEP LED flashes when in sleep mode.
      To set sleep mode first set a wake-up time and after that you can trigger standby. The PC will then wake up at the
      requested time. Everything stops until it wakes up. Outside of standby mode, while on battery power the computer
      will turn off at a set period of time, anywhere from 1 to 25 minutes. When in sleep mode the LCD is off to save power
      and the battery powers the whole main board and RAM to keep the running programs alive. When wake-up is activated
      the computer resumes at the same point it was at before sleep was activated.

      SHIFT + F-keys 1-5 enable F6-F10

      When NUM is locked down the following keys are used as the numpad...
      M = 0
      J = 1
      K = 2
      L = 3
      U = 4
      I = 5
      O = 6
      7,8,9 remain the same


*/

#include "emu.h"
#include "bus/rs232/rs232.h"

#include "cpu/z80/z80.h"
#include "machine/bankdev.h"
#include "machine/i8255.h"
#include "machine/i8251.h"
#include "machine/nvram.h"
#include "machine/timer.h"
#include "machine/upd1990a.h"
#include "video/mc6845.h"
#include "video/sed1330.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "emupal.h"
#include "screen.h"
#include "utf8.h"

#define SCREEN_TAG      "screen"

#define IPL_TAG         "ipl"
#define UPD1990A_TAG    "upd1990a"
#define AY8910_TAG      "ay8910"
#define SED1330_TAG     "sed1330"
#define MC6845_TAG      "mc6845"
#define I8251_TAG       "i8251"
#define RS232_TAG       "rs232"

namespace {

class pc8401a_state : public driver_device
{
public:
	pc8401a_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rtc(*this, UPD1990A_TAG)
		, m_lcdc(*this, SED1330_TAG)
		, m_screen(*this, SCREEN_TAG)
		, m_cart(*this, "cartslot")
		, m_io_cart(*this, "io_cart")
		, m_nvram(*this, "nvram")
		, m_rom(*this, IPL_TAG)
		, m_io_y(*this, "Y.%u", 0)
		, m_bankdev0(*this, "bankdev0")
		, m_bankdev8(*this, "bankdev8")
		, m_crt_view(*this, "crt_view")
	{ }

	void pc8401a(machine_config &config);
	void pc8500(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<upd1990a_device> m_rtc;
	required_device<sed1330_device> m_lcdc;
	required_device<screen_device> m_screen;
	required_device<generic_slot_device> m_cart;
	required_device<generic_slot_device> m_io_cart;
	required_device<nvram_device> m_nvram;
	required_memory_region m_rom;
	required_ioport_array<10> m_io_y;
	required_device<address_map_bank_device> m_bankdev0;
	required_device<address_map_bank_device> m_bankdev8;
	memory_view m_crt_view;

	std::unique_ptr<uint8_t[]> m_internal_nvram;
	memory_region *m_cart_rom = nullptr;

	void mmr_w(uint8_t data);
	uint8_t mmr_r();
	void port31_w(uint8_t data);
	uint8_t port31_r();
	uint8_t rtc_r();
	void rtc_cmd_w(uint8_t data);
	void rtc_ctrl_w(uint8_t data);
	uint8_t io_rom_data_r();
	void io_rom_addr_w(offs_t offset, uint8_t data);
	uint8_t port70_r();
	uint8_t port71_r();
	void port70_w(uint8_t data);
	void port71_w(uint8_t data);
	void palette_init(palette_device &palette) const;

	void scan_keyboard();
	void bankswitch(uint8_t data);

	// keyboard state
	int m_key_strobe = 0;           // key pressed

	// memory state
	uint8_t m_mmr = 0;                // memory mapping register
	uint8_t m_ext_mmr = 0;
	uint32_t m_io_addr = 0;           // I/O ROM address counter

	uint8_t m_key_latch = 0;
	bool m_key_irq_enable = false;
	TIMER_DEVICE_CALLBACK_MEMBER(keyboard_tick);
	void pc8401a_lcdc(address_map &map) ATTR_COLD;
	void pc8401a_mem(address_map &map) ATTR_COLD;
	void pc8500_io(address_map &map) ATTR_COLD;

	void bankdev0_map(address_map &map) ATTR_COLD;
	void bankdev8_map(address_map &map) ATTR_COLD;

	template <unsigned StartBase> uint8_t ram_r(address_space &space, offs_t offset)
	{
		return m_internal_nvram[StartBase + offset];
	}

	template <unsigned StartBase> void ram_w(offs_t offset, uint8_t data)
	{
		m_internal_nvram[StartBase + offset] = data;
	}
};


void pc8401a_state::palette_init(palette_device &palette) const
{
	// TODO: actual values
	palette.set_pen_color(0, rgb_t(160, 168, 160));
	palette.set_pen_color(1, rgb_t(48, 56, 16));
}

void pc8401a_state::pc8401a_lcdc(address_map &map)
{
	map.global_mask(0x3fff);
	map(0x0000, 0x3fff).ram();
}

void pc8401a_state::scan_keyboard()
{
	/* scan keyboard */
	// TODO: is this just a generic key pressed shortcut rather than being MCU based?
	for (int row = 0; row < 10; row++)
	{
		uint8_t data = m_io_y[row]->read();

		if (data != 0xff)
		{
			//strobe = 1;
			m_key_latch |= 1;
		}
	}

	m_maincpu->set_input_line_and_vector(INPUT_LINE_IRQ0, ASSERT_LINE, 0xef); // Z80 - RST 28h
	m_key_strobe = 1;
}

TIMER_DEVICE_CALLBACK_MEMBER(pc8401a_state::keyboard_tick)
{
	if (m_key_irq_enable)
		scan_keyboard();
}

/*
 *
 * bit     description
 * 0       key pressed
 * 1
 * 2
 * 3       <unknown>, disables all keys if on
 * 4       must be 1 or CPU goes to HALT (power status)
 * 5
 * 6
 * 7
 *
 */
uint8_t pc8401a_state::port70_r()
{
	return 0x10 | (m_key_strobe & 1);
}

uint8_t pc8401a_state::port71_r()
{
	return m_key_latch;
}

void pc8401a_state::port70_w(uint8_t data)
{
	m_key_strobe = 0;
}

void pc8401a_state::port71_w(uint8_t data)
{
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);

	// bit 3 definitely enables/disables irq (without latter it will crash as soon as user presses shift key)
	if (BIT(data, 3) && !BIT(m_key_latch, 3))
		m_key_irq_enable = true;

	if (!BIT(data, 3) && BIT(m_key_latch, 3))
		m_key_irq_enable = false;

	m_key_latch = data;
}

void pc8401a_state::bankswitch(uint8_t data)
{
	// set up A0/A1 memory banking
	m_bankdev0->set_bank(data & 0xf);

	// A2
	m_bankdev8->set_bank((data >> 4) & 0x03);

	// A3
	m_crt_view.select(BIT(data, 6));

	if (BIT(data, 7))
		throw emu_fatalerror("Unknown bank bit 7 set");
}

/*
 *
 * bit     description
 * 0       ROM section bit 0
 * 1       ROM section bit 1
 * 2       mapping for CPU addresses 0000H to 7FFFH bit 0
 * 3       mapping for CPU addresses 0000H to 7FFFH bit 1
 * 4       mapping for CPU addresses 8000H to BFFFH bit 0
 * 5       mapping for CPU addresses 8000H to BFFFH bit 1
 * 6       mapping for CPU addresses C000H to E7FFH
 * 7
 */
void pc8401a_state::mmr_w(uint8_t data)
{
	if (data != m_mmr)
	{
		bankswitch(data);
	}

	m_mmr = data;
}

uint8_t pc8401a_state::mmr_r()
{
	return m_mmr;
}

void pc8401a_state::port31_w(uint8_t data)
{
	m_ext_mmr = data & 7;
	//membank("extram_bank")->set_entry(m_ext_mmr);
	if (data & 0xf8)
		throw emu_fatalerror("Unknown ext bank %02x set", data);
}

uint8_t pc8401a_state::port31_r()
{
	return m_ext_mmr;
}

/*
 * bit     description
 * 0       RTC TP?
 * 1       RTC DATA OUT
 * 2       ?
 * 3
 * 4
 * 5
 * 6
 * 7
 */
uint8_t pc8401a_state::rtc_r()
{
	return (m_rtc->data_out_r() << 1) | (m_rtc->tp_r() << 2);
}

// Virtually same as pc8001_state::port10_w
void pc8401a_state::rtc_cmd_w(uint8_t data)
{
	m_rtc->c0_w(BIT(data, 0));
	m_rtc->c1_w(BIT(data, 1));
	m_rtc->c2_w(BIT(data, 2));
	m_rtc->data_in_w(BIT(data, 3));

	// TODO: centronics port?
}

/*
 * ---- -x-- RTC CLK
 * ---- --x- RTC STB
 * ---- ---x RTC OE?
 */
void pc8401a_state::rtc_ctrl_w(uint8_t data)
{
	m_rtc->oe_w(BIT(data, 0));
	m_rtc->stb_w(BIT(data, 1));
	m_rtc->clk_w(BIT(data, 2));
}

uint8_t pc8401a_state::io_rom_data_r()
{
	//logerror("I/O ROM read from %05x\n", m_io_addr);
	return m_io_cart->read_rom(m_io_addr);
}

void pc8401a_state::io_rom_addr_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 0: /* A17..A16 */
		m_io_addr = ((data & 0x03) << 16) | (m_io_addr & 0xffff);
		break;

	case 1: /* A15..A8 */
		m_io_addr = (m_io_addr & 0x300ff) | (data << 8);
		break;

	case 2: /* A7..A0 */
		m_io_addr = (m_io_addr & 0x3ff00) | data;
		break;

	case 3:
		/* the same data is written here as to 0xb2, maybe this latches the address value? */
		break;
	}
}

void pc8401a_state::bankdev0_map(address_map &map)
{
	map(0x00000, 0x17fff).rom().region(IPL_TAG, 0);
	map(0x18000, 0x1ffff).r(m_cart, FUNC(generic_slot_device::read_rom));
	map(0x20000, 0x2ffff).rw(FUNC(pc8401a_state::ram_r<0x0000>), FUNC(pc8401a_state::ram_w<0x0000>));
	map(0x30000, 0x3ffff).unmaprw();
}

void pc8401a_state::bankdev8_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x0bfff).rw(FUNC(pc8401a_state::ram_r<0x0000>), FUNC(pc8401a_state::ram_w<0x0000>));
	map(0x0c000, 0x0ffff).unmaprw(); // external RAM cartridge
}

void pc8401a_state::pc8401a_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).m(m_bankdev0, FUNC(address_map_bank_device::amap8));
	map(0x8000, 0xbfff).m(m_bankdev8, FUNC(address_map_bank_device::amap8));
	map(0xc000, 0xe7ff).view(m_crt_view);
	m_crt_view[0](0xc000, 0xe7ff).rw(FUNC(pc8401a_state::ram_r<0xc000>), FUNC(pc8401a_state::ram_w<0xc000>));
	m_crt_view[1](0xc000, 0xdfff).unmaprw(); // RAM for PC-8441A?
	m_crt_view[1](0xe000, 0xe7ff).unmaprw();
	// TODO: correct? May as well view select with the 0xc*** range ...
	map(0xe800, 0xffff).rw(FUNC(pc8401a_state::ram_r<0xe800>), FUNC(pc8401a_state::ram_w<0xe800>));
}

void pc8401a_state::pc8500_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x00).portr("Y.0");
	map(0x01, 0x01).portr("Y.1");
	map(0x02, 0x02).portr("Y.2");
	map(0x03, 0x03).portr("Y.3");
	map(0x04, 0x04).portr("Y.4");
	map(0x05, 0x05).portr("Y.5");
	map(0x06, 0x06).portr("Y.6");
	map(0x07, 0x07).portr("Y.7");
	map(0x08, 0x08).portr("Y.8");
	map(0x09, 0x09).portr("Y.9");
	map(0x10, 0x10).w(FUNC(pc8401a_state::rtc_cmd_w));
	map(0x20, 0x21).rw(I8251_TAG, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x30, 0x30).rw(FUNC(pc8401a_state::mmr_r), FUNC(pc8401a_state::mmr_w));
	map(0x31, 0x31).rw(FUNC(pc8401a_state::port31_r), FUNC(pc8401a_state::port31_w));
	map(0x40, 0x40).rw(FUNC(pc8401a_state::rtc_r), FUNC(pc8401a_state::rtc_ctrl_w));
//  map(0x41, 0x41)
//  map(0x50, 0x51)
	map(0x60, 0x60).rw(m_lcdc, FUNC(sed1330_device::status_r), FUNC(sed1330_device::data_w));
	map(0x61, 0x61).rw(m_lcdc, FUNC(sed1330_device::data_r), FUNC(sed1330_device::command_w));
	map(0x70, 0x70).rw(FUNC(pc8401a_state::port70_r), FUNC(pc8401a_state::port70_w));
	map(0x71, 0x71).rw(FUNC(pc8401a_state::port71_r), FUNC(pc8401a_state::port71_w));
//  map(0x80, 0x80) modem status, set to 0xff to boot
//  map(0x8b, 0x8b)
//  map(0x90, 0x93) CRTC system comms?
//  map(0x98, 0x98).w(m_crtc, FUNC(mc6845_device::address_w));
//  map(0x99, 0x99).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x98, 0x99).noprw();
//  map(0xa0, 0xa1)
	map(0xb0, 0xb3).w(FUNC(pc8401a_state::io_rom_addr_w));
	map(0xb3, 0xb3).r(FUNC(pc8401a_state::io_rom_data_r));
//  map(0xc8, 0xc8)
//  map(0xfc, 0xff).rw(I8255A_TAG, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xfc, 0xff).noprw();
}

static INPUT_PORTS_START( pc8401a )
	PORT_START("Y.0")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("STOP") // PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("0-6")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("0-5")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("0-4")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("0-3")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("0-2")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("0-1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("0-0")

	PORT_START("Y.1")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("Y.2")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') PORT_CODE(KEYCODE_6_PAD)  PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')

	PORT_START("Y.3")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')

	PORT_START("Y.4")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('`') PORT_CHAR('~')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')

	PORT_START("Y.5")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&') PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')

	PORT_START("Y.6")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(') PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*') PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))

	PORT_START("Y.7")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("7-0") // ^C

	PORT_START("Y.8")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("NUM") PORT_CODE(KEYCODE_NUMLOCK) PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(NUMLOCK))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ALT?") PORT_CODE(KEYCODE_RALT)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CAPS") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL)

	PORT_START("Y.9")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("9-7")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("9-6")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("9-5")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("9-4")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("9-3")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INS DEL") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
INPUT_PORTS_END

void pc8401a_state::machine_start()
{
	std::string region_tag;
	m_cart_rom = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());

	/* initialize RTC */
	m_rtc->cs_w(1);

	// PC-8401A & PC-8500 ships with 64K of internal RAM
	m_internal_nvram = std::make_unique<uint8_t[]>(0x10000);
	m_nvram->set_base(m_internal_nvram.get(), 0x10000);

	/* register for state saving */
	save_item(NAME(m_mmr));
	save_item(NAME(m_io_addr));
}

void pc8401a_state::machine_reset()
{
	m_key_irq_enable = false;
	bankswitch(0);
}

void pc8401a_state::pc8401a(machine_config &config)
{
	Z80(config, m_maincpu, 7.987_MHz_XTAL / 2); // NEC uPD70008C
	m_maincpu->set_addrmap(AS_PROGRAM, &pc8401a_state::pc8401a_mem);
	m_maincpu->set_addrmap(AS_IO, &pc8401a_state::pc8500_io);

	// unknown frequency, roughly fits idle sleep mode
	TIMER(config, "keyboard").configure_periodic(FUNC(pc8401a_state::keyboard_tick), attotime::from_hz(60));

	UPD1990A(config, m_rtc);

	i8251_device &uart(I8251(config, I8251_TAG, 0));
	uart.txd_handler().set(RS232_TAG, FUNC(rs232_port_device::write_txd));
	uart.dtr_handler().set(RS232_TAG, FUNC(rs232_port_device::write_dtr));
	uart.rts_handler().set(RS232_TAG, FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, RS232_TAG, default_rs232_devices, nullptr));
	rs232.rxd_handler().set(I8251_TAG, FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set(I8251_TAG, FUNC(i8251_device::write_dsr));

	PALETTE(config, "palette", FUNC(pc8401a_state::palette_init), 2);

	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(44);
	m_screen->set_screen_update(SED1330_TAG, FUNC(sed1330_device::screen_update));
	m_screen->set_size(480, 130);
	m_screen->set_visarea(0, 480-1, 0, 128-1);
	m_screen->set_palette("palette");

	SED1330(config, m_lcdc, 7.987_MHz_XTAL);
	m_lcdc->set_screen(SCREEN_TAG);
	m_lcdc->set_addrmap(0, &pc8401a_state::pc8401a_lcdc);

	NVRAM(config, m_nvram, nvram_device::DEFAULT_ALL_1);

	ADDRESS_MAP_BANK(config, m_bankdev0).set_map(&pc8401a_state::bankdev0_map).set_options(ENDIANNESS_LITTLE, 8, 15 + 8, 0x8000);
	ADDRESS_MAP_BANK(config, m_bankdev8).set_map(&pc8401a_state::bankdev8_map).set_options(ENDIANNESS_LITTLE, 8, 16, 0x4000);

	/* option ROM cartridge */
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, nullptr, "bin,rom");

	/* I/O ROM cartridge */
	GENERIC_CARTSLOT(config, m_io_cart, generic_linear_slot, nullptr, "bin,rom");

	// TODO: wrong, should touch external cart only and have 32K & 128K options, plus be a slot NVRAM anyway.
	// RAM(config, RAM_TAG).set_default_size("64K").set_extra_options("96K,192K");
}

void pc8401a_state::pc8500(machine_config &config)
{
	pc8401a(config);
	m_screen->set_size(480, 208);
	m_screen->set_visarea(0, 480-1, 0, 200-1);
}

ROM_START( pc8401bd )
	ROM_REGION( 0x20000, IPL_TAG, ROMREGION_ERASEFF )
	ROM_LOAD( "d23c1000c.ic24", 0x0000, 0x20000, CRC(524c2313) SHA1(975874e22927dd8eaf82e8d13f479130d3e09da3) )

	//ROM_REGION( 0x1000, "chargen", 0 )
	//ROM_LOAD( "pc8441a.bin", 0x0000, 0x1000, NO_DUMP )
ROM_END

ROM_START( pc8500 )
	ROM_REGION( 0x20000, IPL_TAG, ROMREGION_ERASEFF )
	ROM_LOAD( "pc8500.bin", 0x0000, 0x10000, CRC(c2749ef0) SHA1(f766afce9fda9ec84ed5b39ebec334806798afb3) )

	//ROM_REGION( 0x1000, "chargen", 0 )
	//ROM_LOAD( "pc8441a.bin", 0x0000, 0x1000, NO_DUMP )
ROM_END

} // anonymous namespace

/* System Drivers */

/*    YEAR  NAME      PARENT   COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY  FULLNAME */
//COMP( 1984, pc8401a,  0,       0,      pc8401a, pc8401a, pc8401a_state, empty_init, "NEC",   "PC-8401A-LS", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
// TODO: clone of above
COMP( 1984, pc8401bd, 0,       0,      pc8401a, pc8401a, pc8401a_state, empty_init, "NEC",   "PC-8401BD", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1985, pc8500,   0,       0,      pc8500,  pc8401a, pc8401a_state, empty_init, "NEC",   "PC-8500", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



pc8801.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Angelo Salese
// thanks-to: Alex Marshall
/**************************************************************************************************

    PC-8801 (c) 1981 NEC

    driver by Angelo Salese, original MESS PC-88SR driver by ???

    TODO:
    - cassette support;
    - waitstates;
    - support for partial palette updates (pretty off in p8suite analog RGB test if enabled);
    - understand why i8214 needs a dis hack setter (depends on attached i8212?);
    - clean-ups:
      - slotify extended work RAM, make sure that p8suite memtest88 detects it properly;
      - better state machine isolation of features between various models.
        Vanilla PC-8801 doesn't have analog palette, PC80S31 device as default
        (uses external minidisk), only model with working border color, other misc banking bits.
      - refactor memory banking to use address maps & views;
      - double check dipswitches;
      - Slotify PC80S31K, also needed by PC-6601SR, PC-88VA, (vanilla & optional) PC-9801. **partially done**
        Also notice that there are common points with SPC-1000 and TF-20 FDDs;
      - backport/merge what is portable to PC-8001;
      - Kanji LV1/LV2 ROM hookups needs to be moved at slot level.
        Needs identification effort about what's internal to machine models and what instead
        can be optionally installed;
    - implement proper joypad / mouse (PC-8872) DB9 port connector, consider deriving from vcs_ctrl;
    - Pinpoint number of EXPansion slots for each machine (currently hardwired to 1),
      guessing from the back panels seems that each model can install between 1 to 3 cards.
      Also note: most cards aren't compatible between each other;

    Notes:
    - Later models have washed out palette with some SWs, with no red component.
      This is because you have to set up the V1 / V2 DIP-SW to V1 mode for those games
      (BIOS sets up analog palette and never changes back otherwise).
      cfr. SW list usage SW notes that specifically needs V1.

======================================================================================================================================

    PC-88xx Models (and similar machines like PC-80xx and PC-98DO)

    Model            | release |      CPU     |                      BIOS components                        |       |
                     |         |     clock    | N-BASIC | N88-BASIC | N88-BASIC Enh |  Sound  |  CD |  Dict |  Disk | Notes
    ==================================================================================================================================
    PC-8001          | 1979-03 |   z80A @ 4   |    X    |     -     |       -       |    -    |  -  |   -   |   -   |
    PC-8001A         |   ??    |   z80A @ 4   |    X    |     -     |       -       |    -    |  -  |   -   |   -   | (U)
    PC-8801          | 1981-11 |   z80A @ 4   |    X    |     X     |       -       |    -    |  -  |   -   |   -   | (KO)
    PC-8801A         |   ??    |   z80A @ 4   |    X    |     X     |       -       |    -    |  -  |   -   |   -   | (U)
    PC-8001 mkII     | 1983-03 |   z80A @ 4   |    X    |     -     |       -       |    -    |  -  |   -   |   -   | (GE),(KO)
    PC-8001 mkIIA    |   ??    |   z80A @ 4   |    X    |     -     |       -       |    -    |  -  |   -   |   -   | (U),(GE)
    PC-8801 mkII     | 1983-11 |   z80A @ 4   |    X    |     X     |       -       |    -    |  -  |   -   | (FDM) | (K1)
    PC-8001 mkII SR  | 1985-01 |   z80A @ 4   |    X    |     -     |       -       |    -    |  -  |   -   |   -   | (GE),(NE),(KO)
    PC-8801 mkII SR  | 1985-03 |   z80A @ 4   |    X    |     X     |       X       |    X    |  -  |   -   | (FDM) | (K1)
    PC-8801 mkII TR  | 1985-10 |   z80A @ 4   |    X    |     X     |       X       |    X    |  -  |   -   | (FD2) | (K1)
    PC-8801 mkII FR  | 1985-11 |   z80A @ 4   |    X    |     X     |       X       |    X    |  -  |   -   | (FDM) | (K1)
    PC-8801 mkII MR  | 1985-11 |   z80A @ 4   |    X    |     X     |       X       |    X    |  -  |   -   | (FDH) | (K2)
    PC-8801 FH       | 1986-11 |  z80H @ 4/8  |    X    |     X     |       X       |    X    |  -  |   -   | (FDM) | (K2)
    PC-8801 MH       | 1986-11 |  z80H @ 4/8  |    X    |     X     |       X       |    X    |  -  |   -   | (FDH) | (K2)
    PC-88 VA         | 1987-03 | z80H+v30 @ 8 |    -    |     X     |       X       |    X    |  -  |   X   | (FDH) | (K2)
    PC-8801 FA       | 1987-11 |  z80H @ 4/8  |    X    |     X     |       X       |    X    |  -  |   -   | (FD2) | (K2)
    PC-8801 MA       | 1987-11 |  z80H @ 4/8  |    X    |     X     |       X       |    X    |  -  |   X   | (FDH) | (K2)
    PC-88 VA2        | 1988-03 | z80H+v30 @ 8 |    -    |     X     |       X       |    X    |  -  |   X   | (FDH) | (K2)
    PC-88 VA3        | 1988-03 | z80H+v30 @ 8 |    -    |     X     |       X       |    X    |  -  |   X   | (FD3) | (K2)
    PC-8801 FE       | 1988-10 |  z80H @ 4/8  |    X    |     X     |       X       |    X    |  -  |   -   | (FD2) | (K2)
    PC-8801 MA2      | 1988-10 |  z80H @ 4/8  |    X    |     X     |       X       |    X    |  -  |   X   | (FDH) | (K2)
    PC-98 DO         | 1989-06 |   z80H @ 8   |    X    |     X     |       X       |    X    |  -  |   -   | (FDH) | (KE)
    PC-8801 FE2      | 1989-10 |  z80H @ 4/8  |    X    |     X     |       X       |    X    |  -  |   -   | (FD2) | (K2)
    PC-8801 MC       | 1989-11 |  z80H @ 4/8  |    X    |     X     |       X       |    X    |  X  |   X   | (FDH) | (K2)
    PC-98 DO+        | 1990-10 |   z80H @ 8   |    X    |     X     |       X       |    X    |  -  |   -   | (FDH) | (KE)

    info for PC-98 DO & DO+ refers to their 88-mode

    Disk Drive options:
    (FDM): there exist three model of this computer: Model 10 (base model, only optional floppy drive), Model 20
        (1 floppy drive for 5.25" 2D disks) and Model 30 (2 floppy drive for 5.25" 2D disks)
    (FD2): 2 floppy drive for 5.25" 2D disks
    (FDH): 2 floppy drive for both 5.25" 2D disks and 5.25" HD disks
    (FD3): 2 floppy drive for both 5.25" 2D disks and 5.25" HD disks + 1 floppy drive for 3.5" 2TD disks

    Notes:
    (U): US version
    (GE): Graphic Expansion for PC-8001
    (NE): N-BASIC Expansion for PC-8001 (similar to N88-BASIC Expansion for PC-88xx)
    (KO): Optional Kanji ROM
    (K1): Kanji 1st Level ROM
    (K2): Kanji 2nd Level ROM
    (KE): Kanji Enhanced ROM

    Memory mounting locations:
     * N-BASIC 0x0000 - 0x5fff, N-BASIC Expansion & Graph Enhhancement 0x6000 - 0x7fff
     * N-BASIC 0x0000 - 0x5fff, N-BASIC Expansion & Graph Enhhancement 0x6000 - 0x7fff
     * N88-BASIC 0x0000 - 0x7fff, N88-BASIC Expansion & Graph Enhhancement 0x6000 - 0x7fff
     * Sound BIOS: 0x6000 - 0x7fff
     * CD-ROM BIOS: 0x0000 - 0x7fff
     * Dictionary: 0xc000 - 0xffff (32 Banks)

    References:
    - https://retrocomputerpeople.web.fc2.com/machines/nec/8801/
    - http://mydocuments.g2.xrea.com/html/p8/vraminfo.html
    - http://www7b.biglobe.ne.jp/~crazyunit/pc88.html
    - http://www.maroon.dti.ne.jp/youkan/pc88/index.html

*************************************************************************************************************************************/


#include "emu.h"
#include "pc8801.h"

#include "softlist_dev.h"

#include "utf8.h"


#define PC8801FH_OSC1   XTAL(28'636'363)
#define PC8801FH_OSC2   XTAL(42'105'200)
#define PC8801FH_OSC3   XTAL(31'948'800)    // called OSC1 on PC-8801FE board

#define MASTER_CLOCK (PC8801FH_OSC3 / 8)
// Crazy Unit page shows following measurements on N88 BASIC:
// 15kHz 25 lines 62.422 Hz, 20 lines 61.462 Hz
#define PIXEL_CLOCK_15KHz (PC8801FH_OSC1 / 2)
// 24kHz 25 lines 55.416 Hz, 20 lines 56.424 Hz
#define PIXEL_CLOCK_24KHz (PC8801FH_OSC2 / 2)
// Note: games may set these up differently, namely xak2 in 15 kHz going 68-ish Hz

void pc8801_state::video_start()
{
	m_screen->register_screen_bitmap(m_text_bitmap);

	save_item(NAME(m_attr_info));
}

void pc8801_state::palette_reset()
{
	int i;

	// bitmap init
	for (i = 0; i < 8; i ++)
	{
		m_palram[i].b = i & 1 ? 7 : 0;
		m_palram[i].r = i & 2 ? 7 : 0;
		m_palram[i].g = i & 4 ? 7 : 0;
		m_palette->set_pen_color(i, pal1bit(i >> 1), pal1bit(i >> 2), pal1bit(i >> 0));
	}
	m_palette->set_pen_color(BGPAL_PEN, 0, 0, 0);
	m_palette->set_pen_color(BORDER_PEN, 0, 0, 0);
}

UPD3301_FETCH_ATTRIBUTE( pc8801_state::attr_fetch )
{
	const u8 attr_max_size = 80;
	std::array<u16, attr_max_size> attr_extend_info = pc8001_base_state::attr_fetch(attr_row, gfx_mode, y, attr_fifo_size, row_size);
	// In case we are in a b&w mode copy the attribute structure in an internal buffer for color processing.
	// It's unknown at this time if decoration attributes applies to bitmap as well
	if ((m_gfx_ctrl & 0x18) == 0x08 && gfx_mode == 2)
	{
		for (int ey = y; ey < y + m_crtc->lines_per_char(); ey ++)
			for (int ex = 0; ex < attr_max_size; ex ++)
				m_attr_info[ey][ex] = attr_extend_info[ex];
	}
	return attr_extend_info;
}

void pc8801_state::draw_bitmap(bitmap_rgb32 &bitmap, const rectangle &cliprect, palette_device *palette, std::function<u8(u32 bitmap_offset, int y, int x, int xi)> dot_func)
{
	uint16_t y_double = get_screen_frequency();
	if ((m_gfx_ctrl & 0x11) == 0)
		y_double = 0;
	int32_t y_line_size = y_double + 1;

	for(int y = cliprect.min_y; y <= cliprect.max_y; y += y_line_size)
	{
		for(int x = cliprect.min_x; x <= cliprect.max_x; x += 8)
		{
			u8 x_char = (x >> 3);
			u32 bitmap_offset = (y >> y_double) * 80 + x_char;
			for(int xi = 0; xi < 8; xi++)
			{
				u8 pen_dot = dot_func(bitmap_offset, y, x_char, 7 - xi);

				if (pen_dot == 0)
					continue;

				// TODO: some real HW snaps implies that output is only even or odd line when in 3bpp mode, verify
				// 3301 skip line? interlace artifact? other?
				for (int yi = 0; yi < y_line_size; yi ++)
				{
					int res_x = x + xi;
					int res_y = y + yi;
					// still need to check against cliprect,
					// in the rare case that 3301 CRTC is set to non-canon values (such as any width != 640).
					if (cliprect.contains(res_x, res_y))
						bitmap.pix(res_y, res_x) = palette->pen(pen_dot);
				}
			}
		}
	}
}

uint32_t pc8801_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if(m_gfx_ctrl & 8)
	{
		// BG Pal applies to 1bpp mode only, sharrier draws blue backdrop with pen #0
		const bool bitmap_color_mode = bool(m_gfx_ctrl & 0x10);
		bitmap.fill(m_palette->pen(bitmap_color_mode ? 0 : BGPAL_PEN), cliprect);

		if(bitmap_color_mode)
			draw_bitmap(bitmap, cliprect, m_palette, [&](u32 bitmap_offset, int y, int x, int xi){
				u8 res = 0;

				// note: layer masking doesn't occur in 3bpp mode, bugattac relies on this
				for (int plane = 0; plane < 3; plane ++)
					res |= ((m_gvram[bitmap_offset + plane * 0x4000] >> xi) & 1) << plane;

				return res;
			});
		else
		{
			if (m_gfx_ctrl & 1)
			{
				// b&w 640x200x3
				draw_bitmap(bitmap, cliprect, m_palette, [&](u32 bitmap_offset, int y, int x, int xi){
					u8 res = 0;

					// in this mode all three planes can potentially form the output
					// it's the only place where I/O $53 bits 1-3 have an actual effect
					for (int plane = 0; plane < 3; plane ++)
					{
						u8 mask = (m_bitmap_layer_mask >> plane) & 1;
						res |= ((m_gvram[bitmap_offset + plane * 0x4000] >> xi) & mask);
					}

					if (!res)
						return 0;

					return m_crtc->is_gfx_color_mode() ? (m_attr_info[y][x] >> 13) & 7 : 7;
				});
			}
			else
			{
				// true 400 line mode, 640x400x1
				// - p1demo2d expects to use CRTC palette on demonstration
				//   (white text that is set to black on previous title screen animation,
				//    that runs in 3bpp)
				// - byoin set a transparent text layer (ASCII=0x20 / attribute = 0x80 0x00)
				//   but it's in gfx_mode = 0 (b&w) so it just draw white from here.
				draw_bitmap(bitmap, cliprect, m_crtc_palette, [&](u32 bitmap_offset, int y, int x, int xi){
					u8 res = 0;
					// HW pick ups just the first two planes (R and B), G is unused for drawing purposes.
					// Plane switch happens at half screen, VRAM areas 0x3e80-0x3fff is unused again.
					// TODO: confirm that a 15 kHz monitor cannot work with this
					// - jettermi just uses the other b&w mode;
					// - casablan/byoin doesn't bother in changing resolution so only the upper part is drawn.
					// Update: real HW capture shows an ugly overlap with the two layers,
					// implying that the second plane just latches on the same signals as the first,
					// YAGNI unless found in concrete example.
					int plane_offset = y >= 200 ? 384 : 0;

					res |= ((m_gvram[bitmap_offset + plane_offset] >> xi) & 1);
					if (!res)
						return 0;

					return m_crtc->is_gfx_color_mode() ? (m_attr_info[y][x] >> 13) & 7 : 7;
				});
			}
		}
	}
	else
		bitmap.fill(0, cliprect);

	if(!m_text_layer_mask)
	{
		m_text_bitmap.fill(0, cliprect);
		m_crtc->screen_update(screen, m_text_bitmap, cliprect);
		copybitmap_trans(bitmap, m_text_bitmap, 0, 0, 0, 0, cliprect, 0);
	}

	return 0;
}

uint8_t pc8801_state::dma_mem_r(offs_t offset)
{
	// TODO: TVRAM readback
	return m_work_ram[offset & 0xffff];
}

uint8_t pc8801_state::alu_r(offs_t offset)
{
	uint8_t b, r, g;

	/* store data to ALU regs */
	for(int i = 0; i < 3; i++)
		m_alu_reg[i] = m_gvram[i*0x4000 + offset];

	b = m_gvram[offset + 0x0000];
	r = m_gvram[offset + 0x4000];
	g = m_gvram[offset + 0x8000];
	if(!(m_alu_ctrl2 & 1)) { b^=0xff; }
	if(!(m_alu_ctrl2 & 2)) { r^=0xff; }
	if(!(m_alu_ctrl2 & 4)) { g^=0xff; }

	return b & r & g;
}

void pc8801_state::alu_w(offs_t offset, uint8_t data)
{
	int i;

	// ALU write mode
	switch(m_alu_ctrl2 & 0x30)
	{
		// logic operation
		case 0x00:
		{
			uint8_t logic_op;

			for(i = 0; i < 3; i++)
			{
				logic_op = (m_alu_ctrl1 & (0x11 << i)) >> i;

				switch(logic_op)
				{
					case 0x00: { m_gvram[i*0x4000 + offset] &= ~data; } break;
					case 0x01: { m_gvram[i*0x4000 + offset] |= data; } break;
					case 0x10: { m_gvram[i*0x4000 + offset] ^= data; } break;
					case 0x11: break; // NOP
				}
			}
		}
		break;

		// restore data from ALU regs
		case 0x10:
		{
			for(i = 0; i < 3; i++)
				m_gvram[i*0x4000 + offset] = m_alu_reg[i];
		}
		break;

		// swap ALU reg 1 into R GVRAM
		case 0x20:
			m_gvram[0x0000 + offset] = m_alu_reg[1];
			break;

		// swap ALU reg 0 into B GVRAM
		case 0x30:
			m_gvram[0x4000 + offset] = m_alu_reg[0];
			break;
	}
}


uint8_t pc8801_state::wram_r(offs_t offset)
{
	return m_work_ram[offset];
}

void pc8801_state::wram_w(offs_t offset, uint8_t data)
{
	m_work_ram[offset] = data;
}

uint8_t pc8801_state::ext_wram_r(offs_t offset)
{
	if(offset < m_extram_size)
		return m_ext_work_ram[offset];

	return 0xff;
}

void pc8801_state::ext_wram_w(offs_t offset, uint8_t data)
{
	if(offset < m_extram_size)
		m_ext_work_ram[offset] = data;
}

uint8_t pc8801_state::nbasic_rom_r(offs_t offset)
{
	return m_n80rom[offset];
}

uint8_t pc8801_state::n88basic_rom_r(offs_t offset)
{
	return m_n88rom[offset];
}

uint8_t pc8801_state::gvram_r(offs_t offset)
{
	return m_gvram[offset];
}

void pc8801_state::gvram_w(offs_t offset, uint8_t data)
{
	m_gvram[offset] = data;
}

uint8_t pc8801_state::high_wram_r(offs_t offset)
{
	return m_hi_work_ram[offset];
}

void pc8801_state::high_wram_w(offs_t offset, uint8_t data)
{
	m_hi_work_ram[offset] = data;
}

// TODO: remove these virtual trampolines once we modernize memory map
// Needs confirmation about really not being there tho, given the design
// may be that both dictionary and CD-ROM are generic slots instead.
inline uint8_t pc8801_state::dictionary_rom_r(offs_t offset)
{
	return 0xff;
}

inline bool pc8801_state::dictionary_rom_enable()
{
	return false;
}

inline uint8_t pc8801_state::cdbios_rom_r(offs_t offset)
{
	return 0xff;
}

inline bool pc8801_state::cdbios_rom_enable()
{
	return false;
}

uint8_t pc8801_state::mem_r(offs_t offset)
{
	if(offset <= 0x7fff)
	{
		if(m_extram_mode & 1)
			return ext_wram_r(offset | (m_extram_bank * 0x8000));

		if(m_gfx_ctrl & 2)
			return wram_r(offset);

		if(cdbios_rom_enable())
			return cdbios_rom_r(offset & 0x7fff);

		if(m_gfx_ctrl & 4)
			return nbasic_rom_r(offset);

		if(offset >= 0x6000 && offset <= 0x7fff && ((m_ext_rom_bank & 1) == 0))
			return n88basic_rom_r(0x8000 + (offset & 0x1fff) + (0x2000 * (m_misc_ctrl & 3)));

		return n88basic_rom_r(offset);
	}
	else if(offset >= 0x8000 && offset <= 0x83ff) // work RAM window
	{
		uint32_t window_offset;

		// work RAM read select or N-Basic select always banks this as normal work RAM
		if(m_gfx_ctrl & 6)
			return wram_r(offset);

		window_offset = (offset & 0x3ff) + (m_window_offset_bank << 8);

		// castlex and imenes accesses this
		// TODO: high TVRAM even
		if(((window_offset & 0xf000) == 0xf000) && (m_misc_ctrl & 0x10))
			return high_wram_r(window_offset & 0xfff);

		return wram_r(window_offset);
	}
	else if(offset >= 0x8400 && offset <= 0xbfff)
	{
		return wram_r(offset);
	}
	else if(offset >= 0xc000 && offset <= 0xffff)
	{
		if(dictionary_rom_enable())
			return dictionary_rom_r(offset & 0x3fff);

		if(m_misc_ctrl & 0x40)
		{
			if(!machine().side_effects_disabled())
				m_vram_sel = 3;

			if(m_alu_ctrl2 & 0x80)
				return alu_r(offset & 0x3fff);
		}

		if(m_vram_sel == 3)
		{
			if(offset >= 0xf000 && offset <= 0xffff && (m_misc_ctrl & 0x10))
				return high_wram_r(offset & 0xfff);

			return wram_r(offset);
		}

		return gvram_r((offset & 0x3fff) + (0x4000 * m_vram_sel));
	}

	return 0xff;
}

void pc8801_state::mem_w(offs_t offset, uint8_t data)
{
	if(offset <= 0x7fff)
	{
		if(m_extram_mode & 0x10)
			ext_wram_w(offset | (m_extram_bank * 0x8000),data);
		else
			wram_w(offset,data);

		return;
	}
	else if(offset >= 0x8000 && offset <= 0x83ff)
	{
		// work RAM read select or N-Basic select always banks this as normal work RAM
		if(m_gfx_ctrl & 6)
			wram_w(offset,data);
		else
		{
			uint32_t window_offset;

			window_offset = (offset & 0x3ff) + (m_window_offset_bank << 8);

			// castlex and imenes accesses this
			// TODO: high TVRAM even
			// μPD3301 DMAs from this instead of the regular work RAM in later models
			// to resolve a bus bottleneck.
			if(((window_offset & 0xf000) == 0xf000) && (m_misc_ctrl & 0x10))
				high_wram_w(window_offset & 0xfff,data);
			else
				wram_w(window_offset,data);
		}

		return;
	}
	else if(offset >= 0x8400 && offset <= 0xbfff)
	{
		wram_w(offset,data);
		return;
	}
	else if(offset >= 0xc000 && offset <= 0xffff)
	{
		if(m_misc_ctrl & 0x40)
		{
			if(!machine().side_effects_disabled())
				m_vram_sel = 3;

			if(m_alu_ctrl2 & 0x80)
			{
				alu_w(offset & 0x3fff,data);
				return;
			}
		}

		if(m_vram_sel == 3)
		{
			if(offset >= 0xf000 && offset <= 0xffff && (m_misc_ctrl & 0x10))
			{
				high_wram_w(offset & 0xfff,data);
				return;
			}

			wram_w(offset,data);
			return;
		}

		gvram_w((offset & 0x3fff) + (0x4000 * m_vram_sel),data);
		return;
	}
}

void pc8801_state::main_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(pc8801_state::mem_r), FUNC(pc8801_state::mem_w));
}

uint8_t pc8801_state::ext_rom_bank_r()
{
	return m_ext_rom_bank;
}

void pc8801_state::ext_rom_bank_w(uint8_t data)
{
	// TODO: bits 1 to 3 written to at POST
	// selection for EXP slot ROMs?
	m_ext_rom_bank = data;
}

// inherited from pc8001.cpp
#if 0
void pc8801_state::port30_w(uint8_t data)
{
	m_txt_width = data & 1;
	m_txt_color = data & 2;

	m_cassette->change_state(BIT(data, 3) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
}
#endif

/*
 * I/O Port $31 (w/o) "System Control Port (2)"
 * N88-BASIC buffer port $e6c2
 *
 * --x- ---- 25LINE: line control in high speed CRT mode (1) 25 lines (0) 20 lines
 * ---x ---- HCOLOR: color graphic display mode
 * ---1 ----         color mode
 * ---0 ----         monochrome mode
 * ---- x--- GRPH: Graphic display mode yes (1) / no (0)
 * ---- -x-- RMODE: ROM mode control N-BASIC (1, ROM 1 & 2) / N88-BASIC (0, ROM 3 & 4)
 * ---- --x- MMODE: RAM mode control yes (1, full RAM) / no (0, ROM/RAM mixed)
 * ---- ---x 200LINE: 200 lines (1) / 400 lines (0) in 1bpp mode
 *
 */
void pc8801_state::port31_w(uint8_t data)
{
	m_gfx_ctrl = data;

//  set_screen_frequency((data & 0x11) != 0x11);
//  dynamic_res_change();
}

/*
 * I/O Port $40 reads "Strobe Port"
 *
 * 1--- ---- UOP2: SW1-8
 * -1-- ---- UOP1:
 * --x- ---- VRTC: vblank signal (0) display (1) vblank
 * ---x ---- CDI: upd1990a data read
 * ---- x--- /EXTON: Minidisc unit connection signal (SW2-7)
 * ---- -x-- DCD: SIO Data Carrier Detect signal (0) no carrier (1) with
 * ---- --x- /SHG: monitor resolution mode (0) high res (1) normal res
 * ---- ---x BUSY: printer (0) READY (1) BUSY
 *
 */
uint8_t pc8801_state::port40_r()
{
	// TODO: merge with PC8001
	uint8_t data = 0x00;

	data |= m_centronics_busy;
//  data |= m_centronics_ack << 1;
	data |= ioport("CTRL")->read() & 0xca;
	data |= m_rtc->data_out_r() << 4;
	data |= m_crtc->vrtc_r() << 5;
	// TODO: enable line from pc80s31k (bit 3, active_low)

	return data;
}

/*
 * I/O Port $40 writes "Strobe Port"
 * N88-BASIC buffer port $e6c1
 *
 * x--- ---- UOP2: general purpose output 2 / sound port
 *                 SING (buzzer mask?)
 * -x-- ---- UOP1: general purpose output 1
 *                 generally used for mouse latch (JOP1, routes on OPN sound port A)
 * --x- ---- BEEP: beeper enable
 * ---x ---- FLASH: flash mode control (active high)
 * ---- x--- /CLDS: "CRT I/F sync control" (init CRT and controller sync pulses?)
 * ---- -x-- CCK: upd1990a clock bit
 * ---- --x- CSTB: upd1990a strobe bit
 * ---- ---x /PSTB: printer strobe (active low)
 *
 */
void pc8801_state::port40_w(uint8_t data)
{
	// TODO: merge (and fix) from pc8001.cpp
	m_centronics->write_strobe(BIT(data, 0));

	m_rtc->stb_w(BIT(data, 1));
	m_rtc->clk_w(BIT(data, 2));

	if(((m_device_ctrl_data & 0x20) == 0x00) && ((data & 0x20) == 0x20))
		m_beeper->set_state(1);

	if(((m_device_ctrl_data & 0x20) == 0x20) && ((data & 0x20) == 0x00))
		m_beeper->set_state(0);

	m_mouse_port->pin_8_w(BIT(data, 6));

	// TODO: is SING a buzzer mask? bastard leaves beeper to ON state otherwise
	if(m_device_ctrl_data & 0x80)
		m_beeper->set_state(0);

	m_device_ctrl_data = data;
}

uint8_t pc8801_state::vram_select_r()
{
	return 0xf8 | ((m_vram_sel == 3) ? 0 : (1 << m_vram_sel));
}

void pc8801_state::vram_select_w(offs_t offset, uint8_t data)
{
	m_vram_sel = offset & 3;
}

void pc8801_state::irq_level_w(uint8_t data)
{
	m_pic->b_sgs_w(~data);
}

/*
 * ---- -x-- /RXMF RXRDY irq mask
 * ---- --x- /VRMF VRTC irq mask
 * ---- ---x /RTMF Real-time clock irq mask
 *
 */
void pc8801_state::irq_mask_w(uint8_t data)
{
	m_irq_state.enable &= ~7;
	// mapping reversed to the correlated irq levels
	m_irq_state.enable |= bitswap<3>(data & 7, 0, 1, 2);

	check_irq(RXRDY_IRQ_LEVEL);
	check_irq(VRTC_IRQ_LEVEL);
	check_irq(CLOCK_IRQ_LEVEL);
}


uint8_t pc8801_state::window_bank_r()
{
	return m_window_offset_bank;
}

void pc8801_state::window_bank_w(uint8_t data)
{
	m_window_offset_bank = data;
}

void pc8801_state::window_bank_inc_w(uint8_t data)
{
	m_window_offset_bank ++;
	m_window_offset_bank &= 0xff;
}

/*
 * I/O Port $32 (R/W)
 * Not on vanilla PC-8801 (mkII onward)
 *
 * x--- ---- sound irq mask (0) irq enabled (1) irq masked
 * -x-- ---- Graphic VRAM access mode (0) independent access mode (1) ALU mode
 * --x- ---- analog (1) / digital (0) palette select
 * ---x ---- high speed RAM select (for TVRAM) (1) main RAM bank (0) dedicated Text RAM
 * ---- xx-- Screen output mode
 * ---- 00-- TV / video mode
 * ---- 01-- None (as in disabling the screen entirely?)
 * ---- 10-- Analog RGB mode
 * ---- 11-- Optional mode
 * ---- --xx internal EROM selection
 */
uint8_t pc8801_state::misc_ctrl_r()
{
	return m_misc_ctrl;
}

void pc8801_state::misc_ctrl_w(uint8_t data)
{
	m_misc_ctrl = data;

	m_sound_irq_enable = ((data & 0x80) == 0);

	// Note: this will map to no irq anyway if there isn't any device interested in INT4
	if (m_sound_irq_enable)
		int4_irq_w(m_sound_irq_pending);
}

/*
 * I/O Port $52 "Border and background color control"
 *
 * -RGB ---- BGx: Background color, index for pen #0
 * ---- -RGB Rx: Border color
 *
 * NB: according to several sources a non-vanilla PC8801 hardwires border to black,
 *     leaving this portion unconnected.
 *     For debugging reasons we leave it in for every machine instead.
 *
 */
void pc8801_state::bgpal_w(uint8_t data)
{
	// sorcerml uses BG Pal extensively:
	// - On bootup message it sets register $54 to white and bgpal to 0, expecting the layer to be transparent;
	// - On playlist sets BG Pal to 0x10 (blue background);
	m_palette->set_pen_color(BGPAL_PEN, pal1bit(BIT(data, 6)), pal1bit(BIT(data, 5)), pal1bit(BIT(data, 4)));
	m_palette->set_pen_color(BORDER_PEN, pal1bit(BIT(data, 2)), pal1bit(BIT(data, 1)), pal1bit(BIT(data, 0)));
}

void pc8801_state::palram_w(offs_t offset, uint8_t data)
{
	if(m_misc_ctrl & 0x20) //analog palette
	{
		if((data & 0x40) == 0)
		{
			m_palram[offset].b = data & 0x7;
			m_palram[offset].r = (data & 0x38) >> 3;
		}
		else
		{
			m_palram[offset].g = data & 0x7;
		}
	}
	else //digital palette
	{
		m_palram[offset].b = data & 1 ? 7 : 0;
		m_palram[offset].r = data & 2 ? 7 : 0;
		m_palram[offset].g = data & 4 ? 7 : 0;
	}

	// TODO: What happens to the palette contents when the analog/digital palette mode changes?
	// Preserve content? Translation? Undefined?
	m_palette->set_pen_color(offset, pal3bit(m_palram[offset].r), pal3bit(m_palram[offset].g), pal3bit(m_palram[offset].b));
	// TODO: at least analog mode can do rasters, unconfirmed for digital mode
	// p8suite Analog RGB test cross bars (reportedly works in 24 kHz / 80 column only)
	// NB: it uses a bunch of non-waitstate related opcodes to cycle time it right,
	// implying a stress-test for Z80 opcode cycles.
	m_screen->update_partial(m_screen->vpos());
}


/*
 * ---- x--- green gvram masked flag
 * ---- -x-- red gvram masked flag
 * ---- --x- blue gvram masked flag
 * ---- ---x text vram masked
 */
void pc8801_state::layer_masking_w(uint8_t data)
{
	m_text_layer_mask = bool(BIT(data, 0));
	m_bitmap_layer_mask = ((data & 0xe) >> 1) ^ 7;
}

uint8_t pc8801_state::extram_mode_r()
{
	return (m_extram_mode ^ 0x11) | 0xee;
}

void pc8801_state::extram_mode_w(uint8_t data)
{
	/*
	---x ---- Write EXT RAM access at 0x0000 - 0x7fff
	---- ---x Read EXT RAM access at 0x0000 - 0x7fff
	*/

	m_extram_mode = data & 0x11;
}

uint8_t pc8801_state::extram_bank_r()
{
	return m_extram_bank;
}

void pc8801_state::extram_bank_w(uint8_t data)
{
	// TODO: bits 2 and 3 also accesses bank for PC-8801-17 "VAB" card
	m_extram_bank = data;
}

void pc8801_state::alu_ctrl1_w(uint8_t data)
{
	m_alu_ctrl1 = data;
}

void pc8801_state::alu_ctrl2_w(uint8_t data)
{
	m_alu_ctrl2 = data;
}

/*
 * $e8-$eb kanji LV1
 * $ec-$ef kanji LV2
 *
 */
template <unsigned kanji_level> uint8_t pc8801_state::kanji_r(offs_t offset)
{
	if((offset & 2) == 0)
	{
		const u8 *kanji_rom = kanji_level ? m_kanji_lv2_rom : m_kanji_rom;
		const u32 kanji_address = (m_knj_addr[kanji_level] * 2) + ((offset & 1) ^ 1);
		return kanji_rom[kanji_address];
	}

	return 0xff;
}

template <unsigned kanji_level> void pc8801_state::kanji_w(offs_t offset, uint8_t data)
{
	if((offset & 2) == 0)
	{
		m_knj_addr[kanji_level] = (
			((offset & 1) == 0) ?
			((m_knj_addr[kanji_level] & 0xff00) | (data & 0xff)) :
			((m_knj_addr[kanji_level] & 0x00ff) | (data << 8))
		);
	}
	// TODO: document and implement what the upper two regs does
	// read latches on write? "read start/end sign" according to
	// https://retrocomputerpeople.web.fc2.com/machines/nec/8801/io_map88.html
}


/*
 * PC8801FH overrides (CPU clock switch)
 */

uint8_t pc8801fh_state::cpuclock_r()
{
	return 0x10 | m_clock_setting;
}

uint8_t pc8801fh_state::baudrate_r()
{
	return 0xf0 | m_baudrate_val;
}

void pc8801fh_state::baudrate_w(uint8_t data)
{
	m_baudrate_val = data & 0xf;
}

/*
 * PC8801MA overrides (dictionary)
 */

inline uint8_t pc8801ma_state::dictionary_rom_r(offs_t offset)
{
	return m_dictionary_rom[offset + ((m_dic_bank & 0x1f) * 0x4000)];
}

inline bool pc8801ma_state::dictionary_rom_enable()
{
	return m_dic_ctrl;
}

void pc8801ma_state::dic_bank_w(uint8_t data)
{
	m_dic_bank = data & 0x1f;
}

void pc8801ma_state::dic_ctrl_w(uint8_t data)
{
	m_dic_ctrl = (data ^ 1) & 1;
}

/*
 * PC8801MC overrides (CD-ROM)
 */

inline uint8_t pc8801mc_state::cdbios_rom_r(offs_t offset)
{
	return m_cdrom_bios[offset | ((m_gfx_ctrl & 4) ? 0x8000 : 0x0000)];
}

inline bool pc8801mc_state::cdbios_rom_enable()
{
	return m_cdrom_bank;
}

void pc8801_state::main_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x00).portr("KEY0");
	map(0x01, 0x01).portr("KEY1");
	map(0x02, 0x02).portr("KEY2");
	map(0x03, 0x03).portr("KEY3");
	map(0x04, 0x04).portr("KEY4");
	map(0x05, 0x05).portr("KEY5");
	map(0x06, 0x06).portr("KEY6");
	map(0x07, 0x07).portr("KEY7");
	map(0x08, 0x08).portr("KEY8");
	map(0x09, 0x09).portr("KEY9");
	map(0x0a, 0x0a).portr("KEY10");
	map(0x0b, 0x0b).portr("KEY11");
	map(0x0c, 0x0c).portr("KEY12");
	map(0x0d, 0x0d).portr("KEY13");
	map(0x0e, 0x0e).portr("KEY14");
	map(0x0f, 0x0f).portr("KEY15");
	map(0x10, 0x10).w(FUNC(pc8801_state::port10_w));
	map(0x20, 0x21).mirror(0x0e).rw(m_usart, FUNC(i8251_device::read), FUNC(i8251_device::write)); // CMT / RS-232C ch. 0
	map(0x30, 0x30).portr("DSW1").w(FUNC(pc8801_state::port30_w));
	map(0x31, 0x31).portr("DSW2").w(FUNC(pc8801_state::port31_w));
	map(0x32, 0x32).rw(FUNC(pc8801_state::misc_ctrl_r), FUNC(pc8801_state::misc_ctrl_w));
//  map(0x33, 0x33) PC8001mkIISR port, mirror on PC8801?
	// TODO: ALU not installed on pre-mkIISR machines
	// NB: anything after 0x32 reads 0xff on a PC8801MA real HW test
	map(0x34, 0x34).w(FUNC(pc8801_state::alu_ctrl1_w));
	map(0x35, 0x35).w(FUNC(pc8801_state::alu_ctrl2_w));
//  map(0x35, 0x35).r <unknown>, accessed by cancanb during OP, mistake? Mirror for intended HW?
	map(0x40, 0x40).rw(FUNC(pc8801_state::port40_r), FUNC(pc8801_state::port40_w));
//  map(0x44, 0x47).rw internal OPN/OPNA sound card for 8801mkIISR and beyond
//  uPD3301
	map(0x50, 0x51).rw(m_crtc, FUNC(upd3301_device::read), FUNC(upd3301_device::write));

	map(0x52, 0x52).w(FUNC(pc8801_state::bgpal_w));
	map(0x53, 0x53).w(FUNC(pc8801_state::layer_masking_w));
	map(0x54, 0x5b).w(FUNC(pc8801_state::palram_w));
	map(0x5c, 0x5c).r(FUNC(pc8801_state::vram_select_r));
	map(0x5c, 0x5f).w(FUNC(pc8801_state::vram_select_w));
//  i8257
	map(0x60, 0x68).rw(m_dma, FUNC(i8257_device::read), FUNC(i8257_device::write));

//  map(0x6e, 0x6f) clock settings (8801FH and later)
	map(0x70, 0x70).rw(FUNC(pc8801_state::window_bank_r), FUNC(pc8801_state::window_bank_w));
	map(0x71, 0x71).rw(FUNC(pc8801_state::ext_rom_bank_r), FUNC(pc8801_state::ext_rom_bank_w));
	map(0x78, 0x78).w(FUNC(pc8801_state::window_bank_inc_w));
//  map(0x82, 0x82).w access window for PC8801-16
//  map(0x8e, 0x8e).r <unknown>, accessed by scruiser on boot (a board ID?)
//  map(0x90, 0x9f) PC-8801-31 CD-ROM i/f (8801MC)
//  map(0xa0, 0xa3) GSX-8800 or network board
//  map(0xa8, 0xad).rw expansion OPN (Sound Board) or OPNA (Sound Board II)
//  map(0xb0, 0xb3) General Purpose I/O
//  map(0xb4, 0xb4) PC-8801-17 Video art board
//  map(0xb5, 0xb5) PC-8801-18 Video digitizing unit
//  map(0xbc, 0xbf) External mini floppy disk I/F (i8255), PC-8801-13 / -20 / -22
//  map(0xc0, 0xc3) USART RS-232C ch. 1 / ch. 2
//  map(0xc4, 0xc7) PC-8801-10 Music interface board (MIDI), GSX-8800 PIT?
//  map(0xc8, 0xc8) RS-232C ch. 1 "prohibited gate" (?)
//  map(0xca, 0xca) RS-232C ch. 2 "prohibited gate" (?)
//  map(0xc8, 0xcd) JMB-X1 OPM / SSG chips
//  map(0xd0, 0xdf) GP-IB
//  map(0xd3, 0xd4) PC-8801-10 Music interface board (MIDI)
//  map(0xdc, 0xdf) PC-8801-12 MODEM (built-in for mkIITR)
	// $e2-$e3 are standard for mkIIMR, MH / MA / MA2 / MC
	// also used by expansion boards -02 / -02N, -22,
	// and -17 video art board (transfers from RAM?)
	map(0xe2, 0xe2).rw(FUNC(pc8801_state::extram_mode_r), FUNC(pc8801_state::extram_mode_w));
	map(0xe3, 0xe3).rw(FUNC(pc8801_state::extram_bank_r), FUNC(pc8801_state::extram_bank_w));
	map(0xe4, 0xe4).w(FUNC(pc8801_state::irq_level_w));
	map(0xe6, 0xe6).w(FUNC(pc8801_state::irq_mask_w));
//  map(0xe7, 0xe7).noprw(); /* arcus writes here, mirror of above? */
	map(0xe8, 0xeb).rw(FUNC(pc8801_state::kanji_r<0>), FUNC(pc8801_state::kanji_w<0>));
	map(0xec, 0xef).rw(FUNC(pc8801_state::kanji_r<1>), FUNC(pc8801_state::kanji_w<1>));
//  map(0xf0, 0xf1) dictionary bank (8801MA and later)
//  map(0xf3, 0xf3) DMA floppy (direct access like PC88VA?)
//  map(0xf4, 0xf7) DMA 5'25-inch floppy (?)
//  map(0xf8, 0xfb) DMA 8-inch floppy (?)
	map(0xfc, 0xff).m(m_pc80s31, FUNC(pc80s31_device::host_map));
}

void pc8801mk2sr_state::main_io(address_map &map)
{
	pc8801_state::main_io(map);
	map(0x44, 0x45).rw(m_opn, FUNC(ym2203_device::read), FUNC(ym2203_device::write));
}

void pc8801fh_state::main_io(address_map &map)
{
	pc8801_state::main_io(map);
	map(0x44, 0x47).rw(m_opna, FUNC(ym2608_device::read), FUNC(ym2608_device::write));

	map(0x6e, 0x6e).r(FUNC(pc8801fh_state::cpuclock_r));
	map(0x6f, 0x6f).rw(FUNC(pc8801fh_state::baudrate_r), FUNC(pc8801fh_state::baudrate_w));
}

void pc8801ma_state::main_io(address_map &map)
{
	pc8801fh_state::main_io(map);
	map(0xf0, 0xf0).w(FUNC(pc8801ma_state::dic_bank_w));
	map(0xf1, 0xf1).w(FUNC(pc8801ma_state::dic_ctrl_w));
}

void pc8801mc_state::main_io(address_map &map)
{
	pc8801ma_state::main_io(map);
	map(0x90, 0x9f).m(m_cdrom_if, FUNC(pc8801_31_device::amap));
}

void pc8801fh_state::opna_map(address_map &map)
{
	// TODO: confirm it really is ROMless
	// TODO: confirm size
	map(0x000000, 0x1fffff).ram();
}

/* Input Ports */

// TODO: move to a pc8801_keyboard_device, merge with pc8001.cpp implementation
/* 2008-05 FP:
Small note about the strange default mapping of function keys:
the top line of keys in PC8801 keyboard is as follows
[STOP][COPY]      [F1][F2][F3][F4][F5]      [ROLL UP][ROLL DOWN]
Therefore, in Full Emulation mode, "F1" goes to 'F3' and so on

Also, the Keypad has 16 keys, making impossible to map it in a satisfactory
way to a PC keypad. Therefore, default settings for these keys in Full
Emulation are currently based on the effect of the key rather than on
their real position

About natural keyboards: currently,
- "Stop" is mapped to 'Pause'
- "Copy" is mapped to 'Print Screen'
- "Kana" is mapped to 'F6'
- "Grph" is mapped to 'F7'
- "Roll Up" and "Roll Down" are mapped to 'Page Up' and 'Page Down'
- "Help" is mapped to 'F8'
 */

static INPUT_PORTS_START( pc8801 )
	PORT_START("KEY0")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)       PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)       PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)       PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)       PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)       PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)       PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)       PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)       PORT_CHAR(UCHAR_MAMEKEY(7_PAD))

	PORT_START("KEY1")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)       PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)       PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK)    PORT_CHAR(UCHAR_MAMEKEY(ASTERISK))
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD)    PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP)        PORT_CHAR(UCHAR_MAMEKEY(EQUALS_PAD))
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN)        PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD))
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD)     PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13)

	PORT_START("KEY2")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)   PORT_CHAR('@')
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)           PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)           PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)           PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)           PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)           PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)           PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)           PORT_CHAR('g') PORT_CHAR('G')

	PORT_START("KEY3")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)           PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)           PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)           PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)           PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)           PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)           PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)           PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)           PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("KEY4")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)           PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)           PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)           PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)           PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)           PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)           PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)           PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)           PORT_CHAR('w') PORT_CHAR('W')

	PORT_START("KEY5")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)           PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)           PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)           PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)  PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2)  PORT_CHAR(0xA5) PORT_CHAR('|')
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)   PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)      PORT_CHAR('^')
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)       PORT_CHAR('-') PORT_CHAR('=')

	PORT_START("KEY6")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)           PORT_CHAR('0')
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)           PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)           PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)           PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)           PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)           PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)           PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)           PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("KEY7")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)           PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)           PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)       PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)       PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)       PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)        PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)       PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("  _") PORT_CODE(KEYCODE_DEL)            PORT_CHAR(0) PORT_CHAR('_')

	PORT_START("KEY8")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Clr Home") PORT_CODE(KEYCODE_HOME)      PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP)   PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del Ins") PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(UCHAR_MAMEKEY(DEL)) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Grph") PORT_CODE(KEYCODE_LALT)  PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Kana") PORT_CODE(KEYCODE_LCONTROL) PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RCONTROL)                        PORT_CHAR(UCHAR_SHIFT_2)

	PORT_START("KEY9")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Stop") PORT_CODE(KEYCODE_F1)            PORT_CHAR(UCHAR_MAMEKEY(PAUSE))
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)                              PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)                              PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)                              PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)                              PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)                              PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)                           PORT_CHAR(' ')
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)                             PORT_CHAR(UCHAR_MAMEKEY(ESC))

	PORT_START("KEY10")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)                             PORT_CHAR('\t')
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN)   PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT)   PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Help") PORT_CODE(KEYCODE_END)           PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Copy") PORT_CODE(KEYCODE_F2)            PORT_CHAR(UCHAR_MAMEKEY(PRTSCR))
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD)                       PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD)                       PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD))
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Caps") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))

	PORT_START("KEY11")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Roll Up") PORT_CODE(KEYCODE_F8)         PORT_CHAR(UCHAR_MAMEKEY(PGUP))
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Roll Down") PORT_CODE(KEYCODE_F9)       PORT_CHAR(UCHAR_MAMEKEY(PGDN))
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY12")     /* port 0x0c */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY13")     /* port 0x0d */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY14")     /* port 0x0e */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY15")     /* port 0x0f */
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("DSW1")
	PORT_DIPNAME( 0x01, 0x01, "BASIC" ) PORT_DIPLOCATION("SW4:1")
	PORT_DIPSETTING(    0x01, "N88-BASIC" )
	PORT_DIPSETTING(    0x00, "N-BASIC" )
	PORT_DIPNAME( 0x02, 0x02, "Terminal mode" ) PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, "Text width" ) PORT_DIPLOCATION("SW1:2")
	PORT_DIPSETTING(    0x04, "40 chars/line" )
	PORT_DIPSETTING(    0x00, "80 chars/line" )
	PORT_DIPNAME( 0x08, 0x00, "Text height" ) PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(    0x08, "20 lines/screen" )
	PORT_DIPSETTING(    0x00, "25 lines/screen" )
	PORT_DIPNAME( 0x10, 0x10, "Enable S parameter" ) PORT_DIPLOCATION("SW1:4")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, "Enable DEL code" ) PORT_DIPLOCATION("SW1:5")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	// TODO: these really maps to "general purpose inputs" UIP1 / UIP2
	PORT_DIPNAME( 0x40, 0x40, "Memory wait" )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, "Disable CMD SING" )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("DSW2")
	PORT_DIPNAME( 0x01, 0x01, "Parity generate" ) PORT_DIPLOCATION("SW2:1")
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, "Parity type" ) PORT_DIPLOCATION("SW2:2")
	PORT_DIPSETTING(    0x00, "Even" )
	PORT_DIPSETTING(    0x02, "Odd" )
	PORT_DIPNAME( 0x04, 0x00, "Serial character length" ) PORT_DIPLOCATION("SW2:3")
	PORT_DIPSETTING(    0x04, "7 bits/char" )
	PORT_DIPSETTING(    0x00, "8 bits/char" )
	PORT_DIPNAME( 0x08, 0x08, "Stop bit length" ) PORT_DIPLOCATION("SW2:4")
	PORT_DIPSETTING(    0x08, "1" )
	PORT_DIPSETTING(    0x00, "2" )
	PORT_DIPNAME( 0x10, 0x10, "Enable X parameter" ) PORT_DIPLOCATION("SW2:5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, "Duplex" ) PORT_DIPLOCATION("SW2:6")
	PORT_DIPSETTING(    0x20, "Half" )
	PORT_DIPSETTING(    0x00, "Full" )
	// TODO: vanilla PC8801 and mkII doesn't have V2
	PORT_DIPNAME( 0x40, 0x40, "BASIC speed select" ) PORT_DIPLOCATION("SW3:1") // actually SW3:0!
	PORT_DIPSETTING(    0x40, "High Speed Mode (V1H, V2)" )
	PORT_DIPSETTING(    0x00, "Standard Mode (V1S)" )
	PORT_DIPNAME( 0x80, 0x00, "BASIC Version select" ) PORT_DIPLOCATION("SW4:2")
	PORT_DIPSETTING(    0x80, "V1 Mode" )
	PORT_DIPSETTING(    0x00, "V2 Mode" )

	PORT_START("CTRL")
	PORT_DIPNAME( 0x02, 0x02, "Monitor Type" )
	PORT_DIPSETTING(    0x02, "15 KHz" )
	PORT_DIPSETTING(    0x00, "24 KHz" )
//  PORT_BIT 0x04 USART DCD signal carrier
	PORT_DIPNAME( 0x08, 0x00, "Auto-boot floppy at start-up" )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
//  PORT_BIT( 0x10, IP_ACTIVE_HIGH,IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("upd1990a", FUNC(upd1990a_device::data_out_r))
//  PORT_BIT( 0x20, IP_ACTIVE_HIGH,IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("screen", FUNC(screen_device::vblank))
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNKNOWN )

	// TODO: Coming from the old legacy driver as "EXSWITCH", where this maps?
	PORT_START("CFG")
	#if 0
	PORT_DIPNAME( 0x0f, 0x08, "Serial speed" )
	PORT_DIPSETTING(    0x01, "75bps" )
	PORT_DIPSETTING(    0x02, "150bps" )
	PORT_DIPSETTING(    0x03, "300bps" )
	PORT_DIPSETTING(    0x04, "600bps" )
	PORT_DIPSETTING(    0x05, "1200bps" )
	PORT_DIPSETTING(    0x06, "2400bps" )
	PORT_DIPSETTING(    0x07, "4800bps" )
	PORT_DIPSETTING(    0x08, "9600bps" )
	PORT_DIPSETTING(    0x09, "19200bps" )
	#endif
	// TODO: unemulated waitstate weight
	PORT_DIPNAME( 0x40, 0x40, "Speed mode" )
	PORT_DIPSETTING(    0x00, "Slow" )
	PORT_DIPSETTING(    0x40, DEF_STR( High ) )

	PORT_START("MEM")
	PORT_CONFNAME( 0x0f, 0x0a, "Extension memory" )
	PORT_CONFSETTING(    0x00, DEF_STR( None ) )
	PORT_CONFSETTING(    0x01, "32KB (PC-8012-02 x 1)" )
	PORT_CONFSETTING(    0x02, "64KB (PC-8012-02 x 2)" )
	PORT_CONFSETTING(    0x03, "128KB (PC-8012-02 x 4)" )
	PORT_CONFSETTING(    0x04, "128KB (PC-8801-02N x 1)" )
	PORT_CONFSETTING(    0x05, "256KB (PC-8801-02N x 2)" )
	PORT_CONFSETTING(    0x06, "512KB (PC-8801-02N x 4)" )
	PORT_CONFSETTING(    0x07, "1M (PIO-8234H-1M x 1)" )
	PORT_CONFSETTING(    0x08, "2M (PIO-8234H-2M x 1)" )
	PORT_CONFSETTING(    0x09, "4M (PIO-8234H-2M x 2)" )
	PORT_CONFSETTING(    0x0a, "8M (PIO-8234H-2M x 4)" )
	PORT_CONFSETTING(    0x0b, "1.1M (PIO-8234H-1M x 1 + PC-8801-02N x 1)" )
	PORT_CONFSETTING(    0x0c, "2.1M (PIO-8234H-2M x 1 + PC-8801-02N x 1)" )
	PORT_CONFSETTING(    0x0d, "4.1M (PIO-8234H-2M x 2 + PC-8801-02N x 1)" )

	PORT_START("BOARD_CONFIG")
	// TODO: extend both via slot options
//  PORT_CONFNAME( 0x01, 0x01, "Sound Board" )
//  PORT_CONFSETTING(    0x00, "OPN (YM2203)" )
//  PORT_CONFSETTING(    0x01, "OPNA (YM2608)" )
INPUT_PORTS_END

static INPUT_PORTS_START( pc8801fh )
	PORT_INCLUDE( pc8801 )

	// TODO: KEY12, KEY13 and KEY14 have extended meaning
	// "KEY12" F6 - F10, BS, INS, DEL
	// "KEY13" kanji control (lower 4 bits)
	// "KEY14" Normal & Numpad RETURN, Left Shift, Right Shift.
	//         bit 7 acts as extension identifier (0 for FH+ keyboards).

	PORT_MODIFY("CFG")
	PORT_DIPNAME( 0x80, 0x80, "Main CPU clock" )
	PORT_DIPSETTING(    0x80, "4MHz" )
	PORT_DIPSETTING(    0x00, "8MHz" )
INPUT_PORTS_END

/* Graphics Layouts */

static const gfx_layout char_layout =
{
	8, 8,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP8(0,1) },
	{ STEP8(0,8) },
	8*8
};

static const gfx_layout kanji_layout =
{
	16, 16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP16(0,1) },
	{ STEP16(0,16) },
	16*16
};

// debugging only
static GFXDECODE_START( gfx_pc8801 )
	GFXDECODE_ENTRY( "cgrom",     0, char_layout,  0, 1 )
	GFXDECODE_ENTRY( "kanji",     0, kanji_layout, 0, 1 )
	GFXDECODE_ENTRY( "kanji_lv2", 0, kanji_layout, 0, 1 )
GFXDECODE_END

void pc8801_state::machine_start()
{
	pc8001_base_state::machine_start();

	// TODO: ready signal connected to FDRDY, presumably for the floppy ch.0 and 1
	m_dma->ready_w(1);

	m_work_ram = make_unique_clear<uint8_t[]>(0x10000);
	m_hi_work_ram = make_unique_clear<uint8_t[]>(0x1000);
	m_ext_work_ram = make_unique_clear<uint8_t[]>(0x8000*0x100);
	m_gvram = make_unique_clear<uint8_t[]>(0xc000);

	save_pointer(NAME(m_work_ram), 0x10000);
	save_pointer(NAME(m_hi_work_ram), 0x1000);
	save_pointer(NAME(m_ext_work_ram), 0x8000*0x100);
	save_pointer(NAME(m_gvram), 0xc000);
	save_item(NAME(m_gfx_ctrl));
	save_item(NAME(m_ext_rom_bank));
	save_item(NAME(m_vram_sel));
	save_item(NAME(m_misc_ctrl));
	save_item(NAME(m_device_ctrl_data));
	save_item(NAME(m_window_offset_bank));
	save_item(NAME(m_text_layer_mask));
	save_item(NAME(m_bitmap_layer_mask));
	save_pointer(NAME(m_alu_reg), 3);
	save_item(NAME(m_alu_ctrl1));
	save_item(NAME(m_alu_ctrl2));
	save_item(NAME(m_extram_mode));
	save_item(NAME(m_extram_bank));
	save_item(NAME(m_extram_size));
	save_pointer(NAME(m_knj_addr), 2);
	save_item(STRUCT_MEMBER(m_palram, r));
	save_item(STRUCT_MEMBER(m_palram, g));
	save_item(STRUCT_MEMBER(m_palram, b));
	save_item(STRUCT_MEMBER(m_irq_state, enable));
	save_item(STRUCT_MEMBER(m_irq_state, pending));
	save_item(NAME(m_sound_irq_enable));
	save_item(NAME(m_sound_irq_pending));
}

void pc8801_state::machine_reset()
{
	#define kB 1024
	#define MB 1024*1024
	const uint32_t extram_type[] = { 0*kB, 32*kB,64*kB,128*kB,128*kB,256*kB,512*kB,1*MB,2*MB,4*MB,8*MB,1*MB+128*kB,2*MB+128*kB,4*MB+128*kB, 0*kB, 0*kB };
	#undef kB
	#undef MB

	m_ext_rom_bank = 0xff;
	m_gfx_ctrl = 0x31;
	m_window_offset_bank = 0x80;
	m_misc_ctrl = 0x80;
	// N-BASIC never pings $53, definitely expects text layer to be enabled on default
	m_text_layer_mask = false;
	m_bitmap_layer_mask = 0;
	m_vram_sel = 3;

	// initialize ALU
	for(int i = 0; i < 3; i++)
		m_alu_reg[i] = 0x00;

	m_beeper->set_state(0);

	// initialize irq section
	{
		m_pic->etlg_w(1);
		m_pic->inte_w(1);
		m_irq_state.pending = 0;
		m_irq_state.enable = 0;
		m_sound_irq_enable = false;
		m_sound_irq_pending = false;
	}

	{
		m_extram_bank = 0;
		m_extram_mode = 0;
	}

	palette_reset();

	m_extram_size = extram_type[ioport("MEM")->read() & 0x0f];
//  m_has_opna = ioport("BOARD_CONFIG")->read() & 1;

	const bool pixel_clock_setting = bool(!BIT(ioport("CTRL")->read(), 1));
	set_screen_frequency(pixel_clock_setting);
	m_crtc->set_unscaled_clock(pixel_clock_setting ? PIXEL_CLOCK_24KHz : PIXEL_CLOCK_15KHz);
}

void pc8801fh_state::machine_start()
{
	pc8801mk2sr_state::machine_start();

	save_item(NAME(m_clock_setting));
	save_item(NAME(m_baudrate_val));
}

void pc8801fh_state::machine_reset()
{
	pc8801_state::machine_reset();

	m_clock_setting = ioport("CFG")->read() & 0x80;

	m_maincpu->set_unscaled_clock(m_clock_setting ? (PC8801FH_OSC3 / 8) : (PC8801FH_OSC3 / 4));
	// TODO: FDC board shouldn't be connected to the clock setting, verify
//  m_fdccpu->set_unscaled_clock(m_clock_setting ?  XTAL(4'000'000) : XTAL(8'000'000));
	m_baudrate_val = 0;
}

void pc8801ma_state::machine_start()
{
	pc8801fh_state::machine_start();

	save_item(NAME(m_dic_bank));
	save_item(NAME(m_dic_ctrl));
}

void pc8801ma_state::machine_reset()
{
	pc8801fh_state::machine_reset();

	m_dic_bank = 0;
	m_dic_ctrl = 0;
}

void pc8801mc_state::machine_start()
{
	pc8801ma_state::machine_start();

	save_item(NAME(m_cdrom_bank));
}

void pc8801mc_state::machine_reset()
{
	pc8801ma_state::machine_reset();

	// Hold STOP during boot to bypass CDROM BIOS at POST (PC=0x10)
	m_cdrom_bank = true;
}

// DE-9 mouse port on front panel (labelled "マウス") - MSX-compatible
uint8_t pc8801mk2sr_state::opn_porta_r()
{
	return BIT(m_mouse_port->read(), 0, 4) | 0xf0;
}

uint8_t pc8801mk2sr_state::opn_portb_r()
{
	return BIT(m_mouse_port->read(), 4, 2) | 0xfc;
}

void pc8801mk2sr_state::opn_portb_w(uint8_t data)
{
	m_mouse_port->pin_6_w(BIT(data, 0));
	m_mouse_port->pin_7_w(BIT(data, 1));
}

// Cassette Configuration
void pc8801_state::txdata_callback(int state)
{
	//m_cassette->output( (state) ? 1.0 : -1.0);
}

void pc8801_state::rxrdy_irq_w(int state)
{
	if (state)
		assert_irq(RXRDY_IRQ_LEVEL);
}

/*
 * 0 RXRDY
 * 1 VRTC
 * 2 CLOCK
 * 3 INT3 (GSX-8800)
 * 4 INT4 (any OPN, external boards included with different irq mask at $aa)
 * 5 INT5
 * 6 FDCINT1
 * 7 FDCINT2
 *
 */
IRQ_CALLBACK_MEMBER(pc8801_state::int_ack_cb)
{
	// TODO: schematics sports a μPB8212 too, with DI2-DI4 connected to 8214 A0-A2
	// Seems just an intermediate bridge for translating raw levels to vectors
	// with no access from outside world?
	u8 level = m_pic->a_r();
	m_pic->r_w(level, 1);

	return (7 - level) * 2;
}

void pc8801_state::int4_irq_w(int state)
{
	bool irq_state = m_sound_irq_enable & state;

	// remember current setting so that an enable reg variation will pick up
	// particularly needed by Telenet games (xzr2, valis2)
	// TODO: understand how exactly the external irq source works out (Sound Board II)
	// has a separate irq mask for secondary OPNA but still sends INT4s,
	// we separate the logic from the others since this exact function needs templatized array for enable and pending anyway
	// (and won't otherwise work for xzr2 anyway).
	m_pic->r_w(7 ^ INT4_IRQ_LEVEL, !irq_state);
	m_sound_irq_pending = state;
}

// FIXME: convert to pure write-line-style member
// Works with 0 -> 1 F/F transitions
TIMER_DEVICE_CALLBACK_MEMBER(pc8801_state::clock_irq_w)
{
	// TODO: castlex sound notes in BGM loop are pretty erratic
	// (uses clock irq instead of the dedicated INT4, started happening on last OPN rewrite, is it just missing some interpolation in the sound core?
	assert_irq(CLOCK_IRQ_LEVEL);
}

void pc8801_state::check_irq(u8 level)
{
	u8 mask = 1 << level;

	// megamit and babylon are particularly fussy if the VRTC irq isn't disabled when requested
	// - megamit jumps to PC=0
	// - babylon has just a ret coded in the VRTC irq, so accepting that will wreck the program flow and hang at title screen with no sound (because it expects INT4s)
	if (!(m_irq_state.enable & mask))
		m_pic->r_w(7 ^ level, 1);
	else if (m_irq_state.enable & m_irq_state.pending & mask)
		assert_irq(level);
}

void pc8801_state::assert_irq(u8 level)
{
	u8 mask = 1 << level;

	if (mask & m_irq_state.enable)
	{
		m_irq_state.pending &= ~mask;
		m_pic->r_w(7 ^ level, 0);
	}
	else
		m_irq_state.pending |= mask;
}

void pc8801_state::vrtc_irq_w(int state)
{
//  bool irq_state = m_vrtc_irq_enable & state;
	if (state)
	{
		assert_irq(VRTC_IRQ_LEVEL);
	}
}

void pc8801_state::irq_w(int state)
{
	m_maincpu->set_input_line(0, state ? ASSERT_LINE : CLEAR_LINE);
}


void pc8801_state::pc8801(machine_config &config)
{
	Z80(config, m_maincpu, MASTER_CLOCK); // ~4 MHz, selectable to ~8 MHz on late models
	m_maincpu->set_addrmap(AS_PROGRAM, &pc8801_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &pc8801_state::main_io);
	m_maincpu->set_irq_acknowledge_callback(FUNC(pc8801_state::int_ack_cb));

	PC80S31(config, m_pc80s31, MASTER_CLOCK);
	config.set_perfect_quantum(m_maincpu);
	config.set_perfect_quantum("pc80s31:fdc_cpu");

//  config.set_maximum_quantum(attotime::from_hz(MASTER_CLOCK/1024));

	I8214(config, m_pic, MASTER_CLOCK);
	m_pic->int_wr_callback().set(FUNC(pc8801_state::irq_w));
	m_pic->set_int_dis_hack(true);

	UPD1990A(config, m_rtc);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set(FUNC(pc8801_state::write_centronics_ack));
	m_centronics->busy_handler().set(FUNC(pc8801_state::write_centronics_busy));

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	// TODO: needs T88 format support
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->set_interface("pc8801_cass");


	// TODO: clock, receiver handler, DCD?
	I8251(config, m_usart, 0);
	m_usart->txd_handler().set(FUNC(pc8801_state::txdata_callback));
	m_usart->rxrdy_handler().set(FUNC(pc8801_state::rxrdy_irq_w));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
//  m_screen->set_raw(PIXEL_CLOCK_24KHz,848,0,640,448,0,400);
	m_screen->set_raw(PIXEL_CLOCK_15KHz, 896, 0, 640, 260, 0, 200);
	m_screen->set_screen_update(FUNC(pc8801_state::screen_update));
//  m_screen->set_palette(m_palette);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_pc8801);
	PALETTE(config, m_palette, palette_device::BLACK, 0x8 + 2); // +2 for BG Pal and border colors
	PALETTE(config, m_crtc_palette, palette_device::BRG_3BIT);

	UPD3301(config, m_crtc, PIXEL_CLOCK_15KHz);
	m_crtc->set_character_width(8);
	m_crtc->set_display_callback(FUNC(pc8801_state::draw_text));
	m_crtc->set_attribute_fetch_callback(FUNC(pc8801_state::attr_fetch));
	m_crtc->drq_wr_callback().set(m_dma, FUNC(i8257_device::dreq2_w));
	m_crtc->rvv_wr_callback().set(FUNC(pc8801_state::crtc_reverse_w));
//  Note: 3301 isn't actually connected to INT so its internal irq mask doesn't have any effect in PC88
	m_crtc->vrtc_wr_callback().set(FUNC(pc8801_state::vrtc_irq_w));
	m_crtc->set_screen(m_screen);

	I8257(config, m_dma, MASTER_CLOCK);
	m_dma->out_hrq_cb().set(FUNC(pc8801_state::hrq_w));
	m_dma->in_memr_cb().set(FUNC(pc8801_state::dma_mem_r));
	// CH0: 5-inch floppy DMA
	// CH1: 8-inch floppy DMA, SCSI CD-ROM interface (on MA/MC)
	m_dma->out_iow_cb<2>().set(m_crtc, FUNC(upd3301_device::dack_w));
	// CH3: <autoload only?>

	TIMER(config, "rtc_timer").configure_periodic(FUNC(pc8801_state::clock_irq_w), attotime::from_hz(600));

	// Note: original models up to OPNA variants really have an internal mono speaker,
	// but user eventually can have a stereo mixing audio card mounted so for simplicity we MCM here.
	SPEAKER(config, m_speaker, 2).front();

	// TODO: DAC_1BIT
	// 2400 Hz according to schematics, unaffected by clock speed setting (confirmed on real HW)
	BEEP(config, m_beeper, MASTER_CLOCK / 16 / 13 / 8);

	m_cassette->add_route(ALL_OUTPUTS, m_speaker, 0.025, 0);
	m_cassette->add_route(ALL_OUTPUTS, m_speaker, 0.025, 1);
	m_beeper->add_route(ALL_OUTPUTS, m_speaker, 0.10, 0);
	m_beeper->add_route(ALL_OUTPUTS, m_speaker, 0.10, 1);

	MSX_GENERAL_PURPOSE_PORT(config, m_mouse_port, msx_general_purpose_port_devices, "joystick");

	PC8801_EXP_SLOT(config, m_exp, pc8801_exp_devices, nullptr);
	m_exp->set_iospace(m_maincpu, AS_IO);
	m_exp->int3_callback().set([this] (bool state) { m_pic->r_w(7 ^ INT3_IRQ_LEVEL, !state); });
	m_exp->int4_callback().set([this] (bool state) { m_pic->r_w(7 ^ INT4_IRQ_LEVEL, !state); });
	m_exp->int5_callback().set([this] (bool state) { m_pic->r_w(7 ^ INT5_IRQ_LEVEL, !state); });

	SOFTWARE_LIST(config, "tape_list").set_original("pc8801_cass");
	SOFTWARE_LIST(config, "disk_n88_list").set_original("pc8801_flop");
	SOFTWARE_LIST(config, "disk_n_list").set_compatible("pc8001_flop");
	SOFTWARE_LIST(config, "flop_generic_list").set_compatible("generic_flop_525").set_filter("pc8801");
}

void pc8801mk2sr_state::pc8801mk2sr(machine_config &config)
{
	pc8801(config);

	YM2203(config, m_opn, MASTER_CLOCK);
	m_opn->irq_handler().set(FUNC(pc8801mk2sr_state::int4_irq_w));
	m_opn->port_a_read_callback().set(FUNC(pc8801mk2sr_state::opn_porta_r));
	m_opn->port_b_read_callback().set(FUNC(pc8801mk2sr_state::opn_portb_r));
	m_opn->port_b_write_callback().set(FUNC(pc8801mk2sr_state::opn_portb_w));

	// TODO: per-channel mixing is unconfirmed
	m_opn->add_route(0, m_speaker, 0.125, 0);
	m_opn->add_route(1, m_speaker, 0.125, 0);
	m_opn->add_route(2, m_speaker, 0.125, 0);
	m_opn->add_route(3, m_speaker, 0.125, 0);
	m_opn->add_route(0, m_speaker, 0.125, 1);
	m_opn->add_route(1, m_speaker, 0.125, 1);
	m_opn->add_route(2, m_speaker, 0.125, 1);
	m_opn->add_route(3, m_speaker, 0.125, 1);
}

void pc8801mk2sr_state::pc8801mk2mr(machine_config &config)
{
	pc8801mk2sr(config);
	PC80S31K(config.replace(), m_pc80s31, MASTER_CLOCK);
}

void pc8801fh_state::pc8801fh(machine_config &config)
{
	pc8801mk2mr(config);

	config.device_remove("opn");

	YM2608(config, m_opna, MASTER_CLOCK*2);
	m_opna->set_addrmap(0, &pc8801fh_state::opna_map);
	m_opna->irq_handler().set(FUNC(pc8801fh_state::int4_irq_w));
	m_opna->port_a_read_callback().set(FUNC(pc8801fh_state::opn_porta_r));
	m_opna->port_b_read_callback().set(FUNC(pc8801fh_state::opn_portb_r));
	m_opna->port_b_write_callback().set(FUNC(pc8801fh_state::opn_portb_w));

	// TODO: per-channel mixing is unconfirmed
	m_opna->add_route(0, m_speaker, 0.75, 0);
	m_opna->add_route(0, m_speaker, 0.75, 1);
	m_opna->add_route(1, m_speaker, 0.75, 0);
	m_opna->add_route(2, m_speaker, 0.75, 1);

	// TODO: add possible configuration override for baudrate here
	// ...
}

void pc8801ma_state::pc8801ma(machine_config &config)
{
	pc8801fh(config);
	// TODO: option slot for CD-ROM bus
	// ...
}

void pc8801mc_state::pc8801mc(machine_config &config)
{
	pc8801ma(config);

	PC8801_31(config, m_cdrom_if, 0);
	m_cdrom_if->rom_bank_cb().set([this](bool state) { m_cdrom_bank = state; });
}

ROM_START( pc8801 )
	ROM_REGION( 0x8000,  "n80rom", ROMREGION_ERASEFF ) // 1.2
	ROM_LOAD( "n80.rom",   0x0000, 0x8000, CRC(5cb8b584) SHA1(063609dd518c124a4fc9ba35d1bae35771666a34) )

	ROM_REGION( 0x10000, "n88rom", ROMREGION_ERASEFF ) // 1.0 V1
	ROM_LOAD( "n88.rom",   0x0000, 0x8000, CRC(ffd68be0) SHA1(3518193b8207bdebf22c1380c2db8c554baff329) )
	ROM_LOAD( "n88_0.rom", 0x8000, 0x2000, CRC(61984bab) SHA1(d1ae642aed4f0584eeb81ff50180db694e5101d4) )

	ROM_REGION( 0x20000, "kanji", ROMREGION_ERASEFF )
	ROM_LOAD_OPTIONAL( "kanji1.rom", 0x00000, 0x20000, CRC(6178bd43) SHA1(82e11a177af6a5091dd67f50a2f4bafda84d6556) )

	ROM_REGION( 0x20000, "kanji_lv2", ROMREGION_ERASEFF )

	ROM_REGION( 0x800, "cgrom", 0)
	ROM_LOAD( "font.rom", 0x0000, 0x0800, CRC(56653188) SHA1(84b90f69671d4b72e8f219e1fe7cd667e976cf7f) )
ROM_END

/*
 * The dump only included "maincpu".
 * Other roms arbitrariely taken from PC-8801 & PC-8801 MkIISR
 * (there should be at least 1 Kanji ROM).
 */
ROM_START( pc8801mk2 )
	ROM_REGION( 0x8000,  "n80rom", ROMREGION_ERASEFF ) // 1.4
	ROM_LOAD( "m2_n80.rom",   0x0000, 0x8000, CRC(91d84b1a) SHA1(d8a1abb0df75936b3fc9d226ccdb664a9070ffb1) )

	ROM_REGION( 0x10000, "n88rom", ROMREGION_ERASEFF ) // 1.3 V1
	ROM_LOAD( "m2_n88.rom",   0x0000, 0x8000, CRC(f35169eb) SHA1(ef1f067f819781d9fb2713836d195866f0f81501) )
	ROM_LOAD( "m2_n88_0.rom", 0x8000, 0x2000, CRC(5eb7a8d0) SHA1(95a70af83b0637a5a0f05e31fb0452bb2cb68055) )

	ROM_REGION( 0x20000, "kanji", ROMREGION_ERASEFF )
	ROM_LOAD_OPTIONAL( "kanji1.rom", 0x00000, 0x20000, CRC(6178bd43) SHA1(82e11a177af6a5091dd67f50a2f4bafda84d6556) )

	ROM_REGION( 0x20000, "kanji_lv2", ROMREGION_ERASEFF )

	ROM_REGION( 0x800, "cgrom", 0)
	ROM_COPY( "kanji", 0x1000, 0x0000, 0x800 )
ROM_END

ROM_START( pc8801mk2sr )
	ROM_REGION( 0x8000,  "n80rom", ROMREGION_ERASEFF ) // 1.5
	ROM_LOAD( "mk2sr_n80.rom",   0x0000, 0x8000, CRC(27e1857d) SHA1(5b922ed9de07d2a729bdf1da7b57c50ddf08809a) )

	ROM_REGION( 0x10000, "n88rom", ROMREGION_ERASEFF ) // 2.0 V2 / 1.4 V1
	ROM_LOAD( "mk2sr_n88.rom",   0x0000, 0x8000, CRC(a0fc0473) SHA1(3b31fc68fa7f47b21c1a1cb027b86b9e87afbfff) )
	ROM_LOAD( "mk2sr_n88_0.rom", 0x8000, 0x2000, CRC(710a63ec) SHA1(d239c26ad7ac5efac6e947b0e9549b1534aa970d) )
	ROM_LOAD( "n88_1.rom",       0xa000, 0x2000, CRC(c0bd2aa6) SHA1(8528eef7946edf6501a6ccb1f416b60c64efac7c) )
	ROM_LOAD( "n88_2.rom",       0xc000, 0x2000, CRC(af2b6efa) SHA1(b7c8bcea219b77d9cc3ee0efafe343cc307425d1) )
	ROM_LOAD( "n88_3.rom",       0xe000, 0x2000, CRC(7713c519) SHA1(efce0b51cab9f0da6cf68507757f1245a2867a72) )

	ROM_REGION( 0x20000, "kanji", ROMREGION_ERASEFF )
	ROM_LOAD( "kanji1.rom", 0x00000, 0x20000, CRC(6178bd43) SHA1(82e11a177af6a5091dd67f50a2f4bafda84d6556) )

	ROM_REGION( 0x20000, "kanji_lv2", ROMREGION_ERASEFF )
	// not on stock mkIISR
	ROM_LOAD_OPTIONAL( "kanji2.rom", 0x00000, 0x20000, CRC(154803cc) SHA1(7e6591cd465cbb35d6d3446c5a83b46d30fafe95) )

	ROM_REGION( 0x800, "cgrom", 0)
	ROM_COPY( "kanji", 0x1000, 0x0000, 0x800 )
ROM_END

ROM_START( pc8801mk2fr )
	ROM_REGION( 0x8000,  "n80rom", ROMREGION_ERASEFF ) // 1.5
	ROM_LOAD( "m2fr_n80.rom",   0x0000, 0x8000, CRC(27e1857d) SHA1(5b922ed9de07d2a729bdf1da7b57c50ddf08809a) )

	ROM_REGION( 0x10000, "n88rom", ROMREGION_ERASEFF ) // 2.1 V2 / 1.5 V1
	ROM_LOAD( "m2fr_n88.rom",   0x0000, 0x8000, CRC(b9daf1aa) SHA1(696a480232bcf8c827c7aeea8329db5c44420d2a) )
	ROM_LOAD( "m2fr_n88_0.rom", 0x8000, 0x2000, CRC(710a63ec) SHA1(d239c26ad7ac5efac6e947b0e9549b1534aa970d) )
	ROM_LOAD( "m2fr_n88_1.rom", 0xa000, 0x2000, CRC(e3e78a37) SHA1(85ecd287fe72b56e54c8b01ea7492ca4a69a7470) )
	ROM_LOAD( "m2fr_n88_2.rom", 0xc000, 0x2000, CRC(98c3a7b2) SHA1(fc4980762d3caa56964d0ae583424756f511d186) )
	ROM_LOAD( "m2fr_n88_3.rom", 0xe000, 0x2000, CRC(0ca08abd) SHA1(a5a42d0b7caa84c3bc6e337c9f37874d82f9c14b) )

	ROM_REGION( 0x20000, "kanji", ROMREGION_ERASEFF )
	ROM_LOAD( "kanji1.rom", 0x00000, 0x20000, CRC(6178bd43) SHA1(82e11a177af6a5091dd67f50a2f4bafda84d6556) )

	ROM_REGION( 0x20000, "kanji_lv2", ROMREGION_ERASEFF )

	ROM_REGION( 0x800, "cgrom", 0)
	ROM_COPY( "kanji", 0x1000, 0x0000, 0x800 )
ROM_END

ROM_START( pc8801mk2mr )
	ROM_REGION( 0x8000,  "n80rom", ROMREGION_ERASEFF ) // 1.8
	ROM_LOAD( "m2mr_n80.rom",   0x0000, 0x8000, CRC(f074b515) SHA1(ebe9cf4cf57f1602c887f609a728267f8d953dce) )

	ROM_REGION( 0x10000, "n88rom", ROMREGION_ERASEFF ) // 2.2 V2 / 1.7 V1
	ROM_LOAD( "m2mr_n88.rom",   0x0000, 0x8000, CRC(69caa38e) SHA1(3c64090237152ee77c76e04d6f36bad7297bea93) )
	ROM_LOAD( "m2mr_n88_0.rom", 0x8000, 0x2000, CRC(710a63ec) SHA1(d239c26ad7ac5efac6e947b0e9549b1534aa970d) )
	ROM_LOAD( "m2mr_n88_1.rom", 0xa000, 0x2000, CRC(e3e78a37) SHA1(85ecd287fe72b56e54c8b01ea7492ca4a69a7470) )
	ROM_LOAD( "m2mr_n88_2.rom", 0xc000, 0x2000, CRC(11176e0b) SHA1(f13f14f3d62df61498a23f7eb624e1a646caea45) )
	ROM_LOAD( "m2mr_n88_3.rom", 0xe000, 0x2000, CRC(0ca08abd) SHA1(a5a42d0b7caa84c3bc6e337c9f37874d82f9c14b) )

	ROM_REGION( 0x20000, "kanji", ROMREGION_ERASEFF )
	ROM_LOAD( "kanji1.rom",      0x00000, 0x20000, CRC(6178bd43) SHA1(82e11a177af6a5091dd67f50a2f4bafda84d6556) )

	ROM_REGION( 0x20000, "kanji_lv2", ROMREGION_ERASEFF )
	ROM_LOAD( "m2mr_kanji2.rom", 0x00000, 0x20000, CRC(376eb677) SHA1(bcf96584e2ba362218b813be51ea21573d1a2a78) )

	ROM_REGION( 0x800, "cgrom", 0)
	ROM_COPY( "kanji", 0x1000, 0x0000, 0x800 )
ROM_END

ROM_START( pc8801mh )
	ROM_REGION( 0x8000,  "n80rom", ROMREGION_ERASEFF ) // 1.8, but different BIOS code?
	ROM_LOAD( "mh_n80.rom",   0x0000, 0x8000, CRC(8a2a1e17) SHA1(06dae1db384aa29d81c5b6ed587877e7128fcb35) )

	ROM_REGION( 0x10000, "n88rom", ROMREGION_ERASEFF ) // 2.3 V2 / 1.8 V1
	ROM_LOAD( "mh_n88.rom",   0x0000, 0x8000, CRC(64c5d162) SHA1(3e0aac76fb5d7edc99df26fa9f365fd991742a5d) )
	ROM_LOAD( "mh_n88_0.rom", 0x8000, 0x2000, CRC(deb384fb) SHA1(5f38cafa8aab16338038c82267800446fd082e79) )
	ROM_LOAD( "mh_n88_1.rom", 0xa000, 0x2000, CRC(7ad5d943) SHA1(4ae4d37409ff99411a623da9f6a44192170a854e) )
	ROM_LOAD( "mh_n88_2.rom", 0xc000, 0x2000, CRC(6aa6b6d8) SHA1(2a077ab444a4fd1470cafb06fd3a0f45420c39cc) )
	ROM_LOAD( "mh_n88_3.rom", 0xe000, 0x2000, CRC(692cbcd8) SHA1(af452aed79b072c4d17985830b7c5dca64d4b412) )

	ROM_REGION( 0x20000, "kanji", ROMREGION_ERASEFF )
	ROM_LOAD( "kanji1.rom",    0x00000, 0x20000, CRC(6178bd43) SHA1(82e11a177af6a5091dd67f50a2f4bafda84d6556) )

	ROM_REGION( 0x20000, "kanji_lv2", ROMREGION_ERASEFF )
	ROM_LOAD( "mh_kanji2.rom", 0x00000, 0x20000, CRC(376eb677) SHA1(bcf96584e2ba362218b813be51ea21573d1a2a78) )

	ROM_REGION( 0x800, "cgrom", 0)
	ROM_COPY( "kanji", 0x1000, 0x0000, 0x0800 )
ROM_END

ROM_START( pc8801fa )
	ROM_REGION( 0x8000,  "n80rom", ROMREGION_ERASEFF ) // 1.8
	ROM_LOAD( "fa_n80.rom",   0x0000, 0x8000, CRC(8a2a1e17) SHA1(06dae1db384aa29d81c5b6ed587877e7128fcb35) )

	ROM_REGION( 0x10000, "n88rom", ROMREGION_ERASEFF ) // 2.3 V2 / 1.9 V1
	ROM_LOAD( "fa_n88.rom",   0x0000, 0x8000, CRC(73573432) SHA1(9b1346d44044eeea921c4cce69b5dc49dbc0b7e9) )
	ROM_LOAD( "fa_n88_0.rom", 0x8000, 0x2000, CRC(a72697d7) SHA1(5aedbc5916d67ef28767a2b942864765eea81bb8) )
	ROM_LOAD( "fa_n88_1.rom", 0xa000, 0x2000, CRC(7ad5d943) SHA1(4ae4d37409ff99411a623da9f6a44192170a854e) )
	ROM_LOAD( "fa_n88_2.rom", 0xc000, 0x2000, CRC(6aee9a4e) SHA1(e94278682ef9e9bbb82201f72c50382748dcea2a) )
	ROM_LOAD( "fa_n88_3.rom", 0xe000, 0x2000, CRC(692cbcd8) SHA1(af452aed79b072c4d17985830b7c5dca64d4b412) )

	ROM_REGION( 0x20000, "kanji", ROMREGION_ERASEFF )
	ROM_LOAD( "kanji1.rom",    0x00000, 0x20000, CRC(6178bd43) SHA1(82e11a177af6a5091dd67f50a2f4bafda84d6556) )

	ROM_REGION( 0x20000, "kanji_lv2", ROMREGION_ERASEFF )
	ROM_LOAD( "fa_kanji2.rom", 0x00000, 0x20000, CRC(376eb677) SHA1(bcf96584e2ba362218b813be51ea21573d1a2a78) )

	ROM_REGION( 0x800, "cgrom", 0)
	ROM_COPY( "kanji", 0x1000, 0x0000, 0x0800 )
ROM_END

// newer floppy BIOS and Jisyo (dictionary) ROM, otherwise same as FA
ROM_START( pc8801ma )
	ROM_REGION( 0x8000,  "n80rom", ROMREGION_ERASEFF ) // 1.8
	ROM_LOAD( "ma_n80.rom",   0x0000, 0x8000, CRC(8a2a1e17) SHA1(06dae1db384aa29d81c5b6ed587877e7128fcb35) )

	ROM_REGION( 0x10000, "n88rom", ROMREGION_ERASEFF ) // 2.3 V2 / 1.9 V1
	ROM_LOAD( "ma_n88.rom",   0x0000, 0x8000, CRC(73573432) SHA1(9b1346d44044eeea921c4cce69b5dc49dbc0b7e9) )
	ROM_LOAD( "ma_n88_0.rom", 0x8000, 0x2000, CRC(a72697d7) SHA1(5aedbc5916d67ef28767a2b942864765eea81bb8) )
	ROM_LOAD( "ma_n88_1.rom", 0xa000, 0x2000, CRC(7ad5d943) SHA1(4ae4d37409ff99411a623da9f6a44192170a854e) )
	ROM_LOAD( "ma_n88_2.rom", 0xc000, 0x2000, CRC(6aee9a4e) SHA1(e94278682ef9e9bbb82201f72c50382748dcea2a) )
	ROM_LOAD( "ma_n88_3.rom", 0xe000, 0x2000, CRC(692cbcd8) SHA1(af452aed79b072c4d17985830b7c5dca64d4b412) )

	ROM_REGION( 0x20000, "kanji", ROMREGION_ERASEFF )
	ROM_LOAD( "kanji1.rom",    0x00000, 0x20000, CRC(6178bd43) SHA1(82e11a177af6a5091dd67f50a2f4bafda84d6556) )

	ROM_REGION( 0x20000, "kanji_lv2", ROMREGION_ERASEFF )
	ROM_LOAD( "ma_kanji2.rom", 0x00000, 0x20000, CRC(376eb677) SHA1(bcf96584e2ba362218b813be51ea21573d1a2a78) )

	ROM_REGION( 0x800, "cgrom", 0)
	ROM_COPY( "kanji", 0x1000, 0x0000, 0x0800 )

	ROM_REGION( 0x80000, "dictionary", 0 )
	ROM_LOAD( "ma_jisyo.rom", 0x00000, 0x80000, CRC(a6108f4d) SHA1(3665db538598abb45d9dfe636423e6728a812b12) )
ROM_END

ROM_START( pc8801ma2 )
	ROM_REGION( 0x8000,  "n80rom", ROMREGION_ERASEFF ) // 1.8
	ROM_LOAD( "ma2_n80.rom",   0x0000, 0x8000, CRC(8a2a1e17) SHA1(06dae1db384aa29d81c5b6ed587877e7128fcb35) )

	ROM_REGION( 0x10000, "n88rom", ROMREGION_ERASEFF ) // 2.3 (2.31?) V2 / 1.91 V1
	ROM_LOAD( "ma2_n88.rom",   0x0000, 0x8000, CRC(ae1a6ebc) SHA1(e53d628638f663099234e07837ffb1b0f86d480d) )
	ROM_LOAD( "ma2_n88_0.rom", 0x8000, 0x2000, CRC(a72697d7) SHA1(5aedbc5916d67ef28767a2b942864765eea81bb8) )
	ROM_LOAD( "ma2_n88_1.rom", 0xa000, 0x2000, CRC(7ad5d943) SHA1(4ae4d37409ff99411a623da9f6a44192170a854e) )
	ROM_LOAD( "ma2_n88_2.rom", 0xc000, 0x2000, CRC(1d6277b6) SHA1(dd9c3e50169b75bb707ef648f20d352e6a8bcfe4) )
	ROM_LOAD( "ma2_n88_3.rom", 0xe000, 0x2000, CRC(692cbcd8) SHA1(af452aed79b072c4d17985830b7c5dca64d4b412) )

	ROM_REGION( 0x20000, "kanji", ROMREGION_ERASEFF )
	ROM_LOAD( "kanji1.rom",     0x00000, 0x20000, CRC(6178bd43) SHA1(82e11a177af6a5091dd67f50a2f4bafda84d6556) )

	ROM_REGION( 0x20000, "kanji_lv2", ROMREGION_ERASEFF )
	ROM_LOAD( "ma2_kanji2.rom", 0x00000, 0x20000, CRC(376eb677) SHA1(bcf96584e2ba362218b813be51ea21573d1a2a78) )

	ROM_REGION( 0x800, "cgrom", 0)
	ROM_COPY( "kanji", 0x1000, 0x0000, 0x0800 )

	ROM_REGION( 0x80000, "dictionary", 0 )
	ROM_LOAD( "ma2_jisyo.rom", 0x00000, 0x80000, CRC(856459af) SHA1(06241085fc1d62d4b2968ad9cdbdadc1e7d7990a) )
ROM_END

ROM_START( pc8801mc )
	ROM_REGION( 0x08000, "n80rom", ROMREGION_ERASEFF ) // 1.8
	ROM_LOAD( "mc_n80.rom",   0x0000, 0x8000, CRC(8a2a1e17) SHA1(06dae1db384aa29d81c5b6ed587877e7128fcb35) )

	ROM_REGION( 0x10000, "n88rom", ROMREGION_ERASEFF ) // 2.3 (2.33?) V2 / 1.93 V1
	ROM_LOAD( "mc_n88.rom",   0x0000, 0x8000, CRC(356d5719) SHA1(5d9ba80d593a5119f52aae1ccd61a1457b4a89a1) )
	ROM_LOAD( "mc_n88_0.rom", 0x8000, 0x2000, CRC(a72697d7) SHA1(5aedbc5916d67ef28767a2b942864765eea81bb8) )
	ROM_LOAD( "mc_n88_1.rom", 0xa000, 0x2000, CRC(7ad5d943) SHA1(4ae4d37409ff99411a623da9f6a44192170a854e) )
	ROM_LOAD( "mc_n88_2.rom", 0xc000, 0x2000, CRC(1d6277b6) SHA1(dd9c3e50169b75bb707ef648f20d352e6a8bcfe4) )
	ROM_LOAD( "mc_n88_3.rom", 0xe000, 0x2000, CRC(692cbcd8) SHA1(af452aed79b072c4d17985830b7c5dca64d4b412) )

	ROM_REGION( 0x10000, "cdrom_bios", 0 )
	ROM_LOAD( "cdbios.rom", 0x0000, 0x10000, CRC(5c230221) SHA1(6394a8a23f44ea35fcfc3e974cf940bc8f84d62a) )

	ROM_REGION( 0x20000, "kanji", ROMREGION_ERASEFF )
	ROM_LOAD( "kanji1.rom",    0x00000, 0x20000, CRC(6178bd43) SHA1(82e11a177af6a5091dd67f50a2f4bafda84d6556) )

	ROM_REGION( 0x20000, "kanji_lv2", ROMREGION_ERASEFF )
	ROM_LOAD( "mc_kanji2.rom", 0x00000, 0x20000, CRC(376eb677) SHA1(bcf96584e2ba362218b813be51ea21573d1a2a78) )

	ROM_REGION( 0x800, "cgrom", 0)
	ROM_COPY( "kanji", 0x1000, 0x0000, 0x0800 )

	ROM_REGION( 0x80000, "dictionary", 0 )
	ROM_LOAD( "mc_jisyo.rom", 0x00000, 0x80000, CRC(bd6eb062) SHA1(deef0cc2a9734ba891a6d6c022aa70ffc66f783e) )
ROM_END


COMP( 1981, pc8801,      0,      0,      pc8801,      pc8801, pc8801_state, empty_init,      "NEC",   "PC-8801",       MACHINE_NOT_WORKING | MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_GRAPHICS )
// PC-8801A (120V, USA & Canada) / PC-8801B (240V, Export?) for Western markets according to a NEC brochure
COMP( 1983, pc8801mk2,   pc8801, 0,      pc8801,      pc8801, pc8801_state, empty_init,      "NEC",   "PC-8801mkII",   MACHINE_NOT_WORKING | MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_GRAPHICS )

// internal OPN
COMP( 1985, pc8801mk2sr, 0,           0,      pc8801mk2sr, pc8801, pc8801mk2sr_state, empty_init, "NEC",   "PC-8801mkIISR", MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_GRAPHICS )
//COMP( 1985, pc8801mk2tr, pc8801mk2sr, 0,      pc8801mk2sr, pc8801, pc8801mk2sr_state, empty_init, "NEC",   "PC-8801mkIITR", MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1985, pc8801mk2fr, pc8801mk2sr, 0,      pc8801mk2sr, pc8801, pc8801mk2sr_state, empty_init, "NEC",   "PC-8801mkIIFR", MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1985, pc8801mk2mr, pc8801mk2sr, 0,      pc8801mk2mr, pc8801, pc8801mk2sr_state, empty_init, "NEC",   "PC-8801mkIIMR", MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_GRAPHICS )

// internal OPNA
//COMP( 1986, pc8801fh,    pc8801mh, 0,      pc8801fh,    pc8801fh, pc8801fh_state, empty_init, "NEC",   "PC-8801FH",     MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1986, pc8801mh,    0,        0,      pc8801fh,    pc8801fh, pc8801fh_state, empty_init, "NEC",   "PC-8801MH",     MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1987, pc8801fa,    pc8801mh, 0,      pc8801fh,    pc8801fh, pc8801fh_state, empty_init, "NEC",   "PC-8801FA",     MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1987, pc8801ma,    0,        0,      pc8801ma,    pc8801fh, pc8801ma_state, empty_init, "NEC",   "PC-8801MA",     MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_GRAPHICS )
//COMP( 1988, pc8801fe,    pc8801ma, 0,      pc8801fa,    pc8801fh, pc8801ma_state, empty_init, "NEC",   "PC-8801FE",     MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1988, pc8801ma2,   pc8801ma, 0,      pc8801ma,    pc8801fh, pc8801ma_state, empty_init, "NEC",   "PC-8801MA2",    MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_GRAPHICS )
//COMP( 1989, pc8801fe2,   pc8801ma, 0,      pc8801fa,    pc8801fh, pc8801ma_state, empty_init, "NEC",   "PC-8801FE2",    MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1989, pc8801mc,    pc8801ma, 0,      pc8801mc,    pc8801fh, pc8801mc_state, empty_init, "NEC",   "PC-8801MC",     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_GRAPHICS )

// PC98DO (PC88+PC98, V33 + μPD70008AC)
// belongs to own driver
//COMP( 1989, pc98do,      0,      0,      pc98do,      pc98do, pc8801_state, empty_init, "NEC",   "PC-98DO",       MACHINE_NOT_WORKING )
//COMP( 1990, pc98dop,     0,      0,      pc98do,      pc98do, pc8801_state, empty_init, "NEC",   "PC-98DO+",      MACHINE_NOT_WORKING )



pc88va.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
// thanks-to: Fujix
/**************************************************************************************************

PC-88VA (c) 1987 NEC

Here be dragons, a mostly compatible PC-8801 with extra V3 Mode for superset.

TODO:
- pc88va (stock version) has two bogus opcodes.
  One is at 0xf0b15 (0x0f 0xfe), another at 0xf0b31 (br 1000h:0c003h).
  Latter will make the program flow to jump to lalaland.
  This also happens if you load a regular V1/V2 game assuming you have FDC PIO properly
  hooked up, is the first opcode actually a Z80 mode switch?
- pc88va is also known to have a slightly different banking scheme and
  regular YM2203 as default sound board.
- video emulation is lacking many features, cfr. pc88va_v.cpp;
- keyboard runs on undumped MCU, we currently stick irqs together on
  selected keys in order to have an easier QoL while testing this.
- Backport from PC-8801 main map, apply supersets where applicable;
  \- IDP has EMUL for upd3301
  \- In emulation mode HW still relies to a i8214, so it bridges thru
     main ICU in cascaded mode via IRQ7;
  \- beeper or dac1bit (to be confirmed);
  \- (other stuff ...)
- Convert FDC usage to pc88va2_fd_if_device, we also need PIO comms for sorcer anyway;
- irq dispatch needs to be revisited, too many instances of sound irq failing for example.
  The current hook-ups aren't legal, V50 core bug?
- Very inconsistent SW boot behaviours, either down to:
  \- the current hack in FDC PIO port returning RNG;
  \- V50 timings;
  \- FDC;
- Every PC Engine OS boot tries to write TVRAM ASCII data on every boot to
  $exxxx ROM region, banking bug?
- all N88 BASIC entries tries to do stuff with EMM, more banking?
- Share SASI i/f (PC-9801-07?) as C-Bus option;

(old notes, to be reordered)
- fdc "intelligent mode" has 0x7f as irq vector ... 0x7f is ld a,a and it IS NOT correctly
  hooked up by the current z80 core
- Fix floppy motor hook-up (floppy believes to be always in even if empty drive);
- Support for PC8801 compatible mode & PC80S31K (floppy interface);

Notes:
- hold F8 at POST to bring software dip settings menu, F5 to cycle between pages;
- PC-88VA-91 is a ROM upgrade kit for a PC-88VA -> VA2/VA3.
  Has four roms, marked by VAEG as VUROM00.ROM, VUROM08.ROM, VUROM1.ROM, VUDIC.ROM.

References:
- PC-88VAテクニカルマニュアル
- http://www.pc88.gr.jp/vafaq/view.php/articlelist/88va/vafaq
- I/O magazine 1987 08 (schematics)

===================================================================================================

irq table (line - vector - source):
ICU
irq 0  - 08h - timer 1
irq 1  - 09h - keyboard irq
irq 2  - 0Ah - VRTC
irq 3  - 0Bh - UINT0 (B24) C-Bus IR3
irq 4  - 0Ch - RS-232C
irq 5  - 0Dh - UINT1 (B25) C-Bus IR5
irq 6  - 0Eh - UINT2 (B26) C-Bus IR6
irq 7  - N/A - Slave (either secondary i8259 or i8214)
i8259 slave
irq 8  - 10H - SGP
irq 9  - 11H - UINT3 (HDD, B27) C-Bus IR9
irq 10 - 12H - UINT4 (B28) C-Bus IR10
irq 11 - 13H - FDC
irq 12 - 14H - Sound
irq 13 - 15H - General timer 3 (mouse)
irq 14 - 16H - <reserved>
irq 15 - 17H - <reserved>

trap list (brief, for quick consultation):
brk 82h AH=01h <undocumented>, animefrm uses it
brk 8Ch AH=02h read calendar clock -> CH = hour, CL = minutes, DH = seconds, DL = 0

**************************************************************************************************/

#include "emu.h"
#include "pc88va.h"

#include "softlist_dev.h"

#include <iostream>
#include "utf8.h"

#define LOG_FDC      (1U << 2) // $1b0-$1b2 accesses
#define LOG_FDC2     (1U << 3) // $1b4-$1b6 accesses (verbose)
#define LOG_GFXCTRL  (1U << 4) // $5xx accesses

#define VERBOSE (LOG_GENERAL | LOG_FDC)
//#define LOG_OUTPUT_STREAM std::cout

#include "logmacro.h"

#define LOGFDC(...)      LOGMASKED(LOG_FDC, __VA_ARGS__)
#define LOGFDC2(...)     LOGMASKED(LOG_FDC2, __VA_ARGS__)
#define LOGGFXCTRL(...)  LOGMASKED(LOG_GFXCTRL, __VA_ARGS__)

// TODO: verify clocks
#define MASTER_CLOCK    (XTAL(31'948'800) / 4) // (based on PC-8801 and PC-9801)
#define FM_CLOCK        (XTAL(31'948'800) / 4) // 3993600, / 8 for regular pc88va



uint8_t pc88va_state::kanji_ram_r(offs_t offset)
{
	return m_kanji_ram[offset];
}

// TODO: settings area should be write protected depending on the m_backupram_wp bit, separate from this
void pc88va_state::kanji_ram_w(offs_t offset, uint8_t data)
{
	m_kanji_ram[offset] = data;
	m_gfxdecode->gfx(2)->mark_dirty(offset / 8);
	m_gfxdecode->gfx(3)->mark_dirty(offset / 32);
}

u8 pc88va_state::port40_r()
{
	u8 data = 0;
	// vrtc
	data = m_screen->vblank() << 5;
	data |= m_rtc->data_out_r() << 4;
	data |= (ioport("DSW")->read() & 1) ? 2 : 0;

	return data | 0xc0;
}

void pc88va_state::port40_w(offs_t offset, u8 data)
{
	m_rtc->stb_w((data & 2) >> 1);
	m_rtc->clk_w((data & 4) >> 2);

	m_mouse_port->pin_8_w(BIT(data, 6));

	m_device_ctrl_data = data;
}


// DE-9 mouse port (labelled "マウス") - MSX-compatible
uint8_t pc88va_state::opn_porta_r()
{
	return BIT(m_mouse_port->read(), 0, 4) | 0xf0;
}

uint8_t pc88va_state::opn_portb_r()
{
	return BIT(m_mouse_port->read(), 4, 2) | 0xfc;
}

void pc88va_state::opn_portb_w(u8 data)
{
	m_mouse_port->pin_6_w(BIT(data, 0));
	m_mouse_port->pin_7_w(BIT(data, 1));
}

void pc88va_state::rtc_w(offs_t offset, u8 data)
{
	m_rtc->c0_w((data & 1) >> 0);
	m_rtc->c1_w((data & 2) >> 1);
	m_rtc->c2_w((data & 4) >> 2);
	m_rtc->data_in_w((data & 8) >> 3);

	// TODO: remaining bits
}

/*
 * $152
 * -x-- ---- ---- ---- SMM compatibility mode (1) V3 (0) V1/V2
 * ---x ---- ---- ---- GMSP VRAM drawing mode (0) multiplane (1) single plane
 * ---- xxxx ---- ---- SMBC (0xa0000 - 0xdffff RAM bank)
 * ---- ---- xxxx ---- RBC13-RBC10 (0xf0000 - 0xfffff ROM bank)
 * ---- ---- 0xxx ---- internal ROM entry
 *                     \- settings 2 to 5 are <prohibited>
 *                     \- settings 6 and 7 are reserved
 * ---- ---- 1xxx ---- select bus slot ROM
 * ---- ---- ---- xxxx RBC03-RBC00 (0xe0000 - 0xeffff ROM bank)
 * ---- ---- ---- 0xxx internal ROM entry
 * ---- ---- ---- 1xxx select bus slot ROM
 */
void pc88va_state::bios_bank_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	COMBINE_DATA(&m_bank_reg);
	m_gmsp_view.select(BIT(m_bank_reg, 12));

	/* SMBC */
	m_sysbank->set_bank((m_bank_reg & 0xf00) >> 8);

	/* RBC1 */
	{
		uint8_t *ROM10 = memregion("rom10")->base();

		if((m_bank_reg & 0xe0) == 0x00)
			membank("rom10_bank")->set_base(&ROM10[(m_bank_reg & 0x10) ? 0x10000 : 0x00000]);
	}

	/* RBC0 */
	{
		uint8_t *ROM00 = memregion("rom00")->base();
		membank("rom00_bank")->set_base(&ROM00[(m_bank_reg & 0xf) * 0x10000]);
	}
}

uint16_t pc88va_state::bios_bank_r()
{
	return m_bank_reg;
}

// TODO: status for bus slot ROM banking, at 0xf0000-0xfffff
uint8_t pc88va_state::rom_bank_r()
{
	// bit 7 low is PC-88VA-91 rom bank status
	return 0xff;
}

uint8_t pc88va_state::key_r(offs_t offset)
{
	static const char *const keynames[] = { "KEY0", "KEY1", "KEY2", "KEY3",
											"KEY4", "KEY5", "KEY6", "KEY7",
											"KEY8", "KEY9", "KEYA", "KEYB",
											"KEYC", "KEYD", "KEYE", "KEYF" };

	return ioport(keynames[offset])->read();
}

void pc88va_state::backupram_wp_1_w(uint16_t data)
{
	m_backupram_wp = 1;
}

void pc88va_state::backupram_wp_0_w(uint16_t data)
{
	m_backupram_wp = 0;
}

/*
 * $190 system port 5
 * ---x ---- FBEEP Force BEEP (1) allow
 * ---- xx-- AVC2/AVC1 video output control
 * ---- 00-- TV/video mode, offline (?)
 * ---- 10-- Analog RGB mode (at reset, default)
 * ---- x1-- <prohibited>
 * ---- ---x RSTMD reset status
 *           \- works as software Power On Reset flag during VA POST
 */
void pc88va_state::sys_port5_w(u8 data)
{
	m_rstmd = bool(BIT(data, 0));
	LOG("I/O $190 %02x\n", data);
}

u8 pc88va_state::sys_port5_r()
{
	return (m_rstmd ? 1 : 0) | 8;
}

// TODO: convert to pc80s31k family
uint8_t pc88va_state::fake_subfdc_r()
{
	return machine().rand();
}

uint8_t pc88va_state::pc88va_fdc_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		LOGFDC("Unhandled read $%04x\n", (offset << 1) + 0x1b0);

	switch(offset << 1)
	{
		case 0x00: return 0; // FDC mode register
		case 0x02: return 0; // FDC control port 0
		case 0x04: return 0; // FDC control port 1
		/* ---x ---- RDY: (0) Busy (1) Ready */
		case 0x06: // FDC control port 2
			return 0;
	}

	return 0xff;
}

TIMER_CALLBACK_MEMBER(pc88va_state::pc88va_fdc_timer)
{
	if(m_xtmask)
	{
		m_pic2->ir3_w(0);
		m_pic2->ir3_w(1);
	}

	m_fdc_timer->adjust(attotime::from_msec(100));
}

TIMER_CALLBACK_MEMBER(pc88va_state::pc88va_fdc_motor_start_0)
{
	m_fdd[0]->get_device()->mon_w(0);
}

TIMER_CALLBACK_MEMBER(pc88va_state::pc88va_fdc_motor_start_1)
{
	m_fdd[1]->get_device()->mon_w(0);
}

void pc88va_state::pc88va_fdc_update_ready(floppy_image_device *, int)
{
	if (!BIT(m_fdc_ctrl_2, 5))
		return;
	bool force_ready = (BIT(m_fdc_ctrl_2, 6));

	floppy_image_device *floppy0, *floppy1;
	floppy0 = m_fdd[0]->get_device();
	floppy1 = m_fdd[1]->get_device();
	if (!floppy0 && !floppy1)
		force_ready = false;

	//if(floppy && force_ready)
	//  ready = floppy->ready_r();

	//if(floppy && force_ready)
	//  ready = floppy->ready_r();

	LOGFDC2("Force ready signal %d\n", force_ready);

	if (force_ready)
	{
		m_fdc->set_ready_line_connected(0);
		m_fdc->ready_w(0);
	}
	else
		m_fdc->set_ready_line_connected(1);
}

void pc88va_state::pc88va_fdc_w(offs_t offset, uint8_t data)
{
	switch(offset << 1)
	{
		/*
		---- ---x MODE: FDC op mode (0) Intelligent (1) DMA
		*/
		case 0x00: // FDC mode register
			m_fdc_mode = data & 1;
			LOGFDC("$1b0 FDC op mode (%02x) %s mode\n"
				, data
				, m_fdc_mode ? "DMA" : "Intelligent (PIO)"
			);
			break;
		/*
		--x- ---- CLK: FDC clock selection (0) 4.8MHz (1) 8 MHz
		---x ---- DS1: Prohibition of the drive selection of FDC (0) Permission (1) Prohibition
		---- xx-- TD1/TD0: Drive 1/0 track density (0) 48 TPI (1) 96 TPI
		---- --xx RV1/RV0: Drive 1/0 mode selection (0) 2D and 2DD mode (1) 2HD mode
		*/
		case 0x02: // FDC control port 0
		{
			const bool clk = bool(BIT(data, 5));
			const bool rv1 = bool(BIT(data, 1));
			const bool rv0 = bool(BIT(data, 0));
			LOGFDC("$1b2 FDC control port 0 (%02x) %s CLK| %d DS1| %d%d TD1/TD0| %d%d RV1/RV0\n"
				, data
				, clk ? "  8 MHz" : "4.8 MHz"
				, !bool(BIT(data, 4))
				, bool(BIT(data, 3))
				, bool(BIT(data, 2))
				, rv1
				, rv0
			);
			m_fdd[0]->get_device()->set_rpm(rv0 ? 360 : 300);
			m_fdd[1]->get_device()->set_rpm(rv1 ? 360 : 300);

			//m_fdd[0]->get_device()->ds_w(!BIT(data, 4));
			//m_fdd[1]->get_device()->ds_w(!BIT(data, 4));

			// TODO: is this correct? sounds more like a controller clock change, while TD1/TD0 should do the rate change
			m_fdc->set_rate(clk ? 500000 : 250000);
			break;
		}
		/*
		---- x--- PCM: precompensation control (1) on
		---- --xx M1/M0: Drive 1/0 motor control (0) NOP (1) Change motor status
		*/
		case 0x04:
		{
			const bool m0 = bool(BIT(data, 0));
			const bool m1 = bool(BIT(data, 1));

			LOGFDC2("$1b4 FDC control port 1 (%02x) %d PCM| %d%d M1/M0\n"
				, data
				, bool(BIT(data, 3))
				, m1
				, m0
			);

			// TODO: fine grain motor timings
			// docs claims 600 msecs, must be more complex than that
			if( m0 )
				m_motor_start_timer[0]->adjust(attotime::from_msec(505));
			else
				m_fdd[0]->get_device()->mon_w(1);


			if( m1 )
				m_motor_start_timer[1]->adjust(attotime::from_msec(505));
			else
				m_fdd[1]->get_device()->mon_w(1);

			break;
		}

		/*
		 * FDC control port 2
		 * x--- ---- FDCRST: FDC Reset
		 * -xx- ---- FDCFRY FRYCEN: FDC force ready control
		 * -x0- ---- ignored
		 * -01- ---- force ready release
		 * -11- ---- force ready assert
		 * ---x ---- DMAE: DMA Enable (0) Prohibit DMA (1) Enable DMA
		 * ---- -x-- XTMASK: FDC timer IRQ mask (0) Disable (1) Enable
		 * ---- ---x TTRG: FDC timer trigger (0) FDC timer clearing (1) FDC timer start
		 */
		case 0x06:
		{
			const bool fdcrst = bool(BIT(data, 7));
			const bool ttrg = bool(BIT(data, 0));
			const bool cur_xtmask = bool(BIT(data, 2));
			LOGFDC2("$1b6 FDC control port 2 (%02x) %d FDCRST| %d%d FDCFRY| %d DMAE| %d XTMASK| %d TTRG\n"
				, data
				, fdcrst
				, bool(BIT(data, 6))
				, bool(BIT(data, 5))
				, bool(BIT(data, 4))
				, cur_xtmask
				, ttrg
			);

			if( ttrg && !BIT(m_fdc_ctrl_2, 0) )
				m_fdc_timer->adjust(attotime::from_msec(100));
			else if (!ttrg && BIT(m_fdc_ctrl_2, 0) )
				m_fdc_timer->adjust(attotime::never);

			m_xtmask = cur_xtmask;

			//if (!BIT(m_fdc_ctrl_2, 4) && BIT(data, 4))
			//  m_maincpu->dreq_w<2>(1);
			//m_dmac->dreq2_w(1);

			// TODO: 0 -> 1 transition?
			if( fdcrst )
				m_fdc->reset();

			m_fdc_ctrl_2 = data;

			//m_fdd[0]->get_device()->mon_w(!(BIT(data, 5)));

			pc88va_fdc_update_ready(nullptr, 0);

			break;
		}
	}
}


uint16_t pc88va_state::sysop_r()
{
	uint8_t sys_op;

	sys_op = ioport("SYSOP_SW")->read() & 3;

	return 0xfffc | sys_op; // docs says all the other bits are high
}

/*
 * x--- ---- MINTEN (TCU irq enable)
 * ---- --xx MTP1/MTP0 general purpose timer 3 interval
 * ---- --00 120 Hz
 * ---- --01 60 Hz
 * ---- --10 30 Hz
 * ---- --11 15 Hz
 */
void pc88va_state::timer3_ctrl_reg_w(uint8_t data)
{
	m_timer3_io_reg = data;

	if(data & 0x80)
		m_t3_mouse_timer->adjust(attotime::from_hz(120 >> (m_timer3_io_reg & 3)));
	else
	{
		// TODO: confirm me
		//m_pic2->ir5_w(0);
		m_t3_mouse_timer->adjust(attotime::never);
	}
}

TIMER_CALLBACK_MEMBER(pc88va_state::t3_mouse_callback)
{
	if(m_timer3_io_reg & 0x80)
	{
		m_pic2->ir5_w(0);
		m_pic2->ir5_w(1);
		m_t3_mouse_timer->adjust(attotime::from_hz(120 >> (m_timer3_io_reg & 3)));
	}
}


uint8_t pc88va_state::backupram_dsw_r(offs_t offset)
{
	if(offset == 0)
		return m_kanji_ram[0x1fc2 / 2] & 0xff;

	return m_kanji_ram[0x1fc6 / 2] & 0xff;
}

// TODO: pc8801_state::port31_w
void pc88va_state::sys_port1_w(uint8_t data)
{
	LOG("I/O $31 %02x\n", data);
}

uint8_t pc88va_state::misc_ctrl_r()
{
	return m_misc_ctrl;
}

void pc88va_state::misc_ctrl_w(uint8_t data)
{
	m_misc_ctrl = data;

	m_sound_irq_enable = ((data & 0x80) == 0);

	if (m_sound_irq_enable)
		int4_irq_w(m_sound_irq_pending);
}


uint8_t pc88va_state::sasi_data_r()
{
	uint8_t data = m_sasi_data_in->read();

	if(m_sasi_ctrl_in->read() & 0x80)
		m_sasibus->write_ack(1);
	return data;
}

void pc88va_state::sasi_data_w(uint8_t data)
{
	m_sasi_data = data;

	if (m_sasi_data_enable)
	{
		m_sasi_data_out->write(m_sasi_data);
		if(m_sasi_ctrl_in->read() & 0x80)
			m_sasibus->write_ack(1);
	}
}

void pc88va_state::write_sasi_io(int state)
{
	m_sasi_ctrl_in->write_bit2(state);

	m_sasi_data_enable = !state;

	if (m_sasi_data_enable)
	{
		m_sasi_data_out->write(m_sasi_data);
	}
	else
	{
		m_sasi_data_out->write(0);
	}
	if((m_sasi_ctrl_in->read() & 0x9c) == 0x8c)
		m_pic2->ir1_w(m_sasi_ctrl & 1);
	else
		m_pic2->ir1_w(0);
}

void pc88va_state::write_sasi_req(int state)
{
	m_sasi_ctrl_in->write_bit7(state);

	if (!state)
		m_sasibus->write_ack(0);

	if((m_sasi_ctrl_in->read() & 0x9C) == 0x8C)
		m_pic2->ir1_w(m_sasi_ctrl & 1);
	else
		m_pic2->ir1_w(0);

	m_maincpu->dreq_w<0>(!(state && !(m_sasi_ctrl_in->read() & 8) && (m_sasi_ctrl & 2)));
}

/*
 * read status when NRDSW=1
 * x--- ---- REQ
 * -x-- ---- ACK
 * --x- ---- BSY
 * ---x ---- MSG
 * ---- x--- CD
 * ---- -x-- IO
 * ---- ---x INT?
 *
 * read drive info NRDSW=0
 *
 * x--- ---- CT0 HDD #1 sector length (1=512, 0=256)
 * -x-- ---- CT1 HDD #2 sector length
 * --xx x--- DT02-DT01-DT00 HDD #1 capacity
 * --11 1--- <unconnected>
 * --11 0--- 40MB
 * --10 0--- 20MB
 * --00 1--- 10MB
 * --00 0--- 5MB
 * ---- -xxx DT12-DT11-DT10 HDD #2 capacity
 */
uint8_t pc88va_state::sasi_status_r()
{
	uint8_t res = 0;

	if(m_sasi_ctrl & 0x40)
	{
		res |= m_sasi_ctrl_in->read();
	}
	else
	{
		// TODO: configurable dips
		// currently hardwiring to 512 sectors + 40MB layout for HDD#0
		// (theoretically matching a chdman -tp 7)
		// unconnected for HDD#1 (would show up as HDFORM D: option otherwise)
		res |= 0x80 | (6 << 3) | 7;
	}
	return res;
}

/*
 * x--- ---- channel enable
 * -x-- ---- NRDSW read switch
 * --x- ---- sel
 * ---- x--- reset line
 * ---- --x- dma enable
 * ---- ---x irq enable
 */
void pc88va_state::sasi_ctrl_w(uint8_t data)
{

	m_sasibus->write_sel(BIT(data, 5));

	if(m_sasi_ctrl & 8 && ((data & 8) == 0)) // 1 -> 0 transition
	{
		m_sasibus->write_rst(1);
//      m_timer_rst->adjust(attotime::from_nsec(100));
	}
	else
		m_sasibus->write_rst(0); // TODO

	m_sasi_ctrl = data;

//  m_sasibus->write_sel(BIT(data, 0));
}



/****************************************
 * Address maps
 ***************************************/

void pc88va_state::main_map(address_map &map)
{
	map(0x00000, 0x7ffff).ram().share("workram");
//  map(0x80000, 0x9ffff).ram(); // EMM
	map(0xa0000, 0xdffff).m(m_sysbank, FUNC(address_map_bank_device::amap16));
	map(0xe0000, 0xeffff).bankr("rom00_bank");
	map(0xf0000, 0xfffff).bankr("rom10_bank");
}

void pc88va_state::sysbank_map(address_map &map)
{
	// 0 select C-bus slot
	// 1 tvram
	map(0x040000, 0x04ffff).ram().share("tvram");
	// FIXME: BASIC and pacmana expects to r/w to 0x60000-0x7ffff on loading, assume mirror if not a core bug.
	map(0x050000, 0x07ffff).ram();
	// 4 gvram
	map(0x100000, 0x13ffff).view(m_gmsp_view);
	m_gmsp_view[0](0x100000, 0x13ffff).rw(FUNC(pc88va_state::gvram_multiplane_r), FUNC(pc88va_state::gvram_multiplane_w));
	m_gmsp_view[1](0x100000, 0x13ffff).rw(FUNC(pc88va_state::gvram_singleplane_r), FUNC(pc88va_state::gvram_singleplane_w));
	// 8-9 kanji
	// Kanji ROM
	map(0x200000, 0x23ffff).rom().region("kanji", 0x00000);
	// ANK ROM
	map(0x240000, 0x24ffff).rom().region("kanji", 0x40000);
	// Backup RAM & PCG
	map(0x250000, 0x253fff).rw(FUNC(pc88va_state::kanji_ram_r),FUNC(pc88va_state::kanji_ram_w));
	// c-d dictionary
	map(0x300000, 0x37ffff).rom().region("dictionary", 0);
}

// SGP has its own window space about how and what it can see on RMW
void pc88va_state::sgp_map(address_map &map)
{
	map(0x000000, 0x07ffff).ram().share("workram");
//  map(0x080000, 0x09ffff) more main RAM or EMM
//  map(0x0a0000, 0x0fffff) EMM $a0000 to $fffff (?)
	map(0x100000, 0x13ffff).rom().region("kanji", 0x00000);
	map(0x140000, 0x14ffff).rom().region("kanji", 0x40000);
	map(0x150000, 0x153fff).rw(FUNC(pc88va_state::kanji_ram_r),FUNC(pc88va_state::kanji_ram_w));
	map(0x180000, 0x18ffff).ram().share("tvram");
	// Assume just raw writes to GVRAM
	map(0x200000, 0x23ffff).lrw8(
		NAME([this] (offs_t offset) { return m_gvram[offset]; }),
		NAME([this] (offs_t offset, u8 data) { m_gvram[offset] = data; })
	);
}

// TODO: I/O 0x00xx is almost same as pc8801
// (*) are specific N88 V1 / V2 ports
void pc88va_state::io_map(address_map &map)
{
	map(0x0000, 0x000f).r(FUNC(pc88va_state::key_r)); // Keyboard ROW reading
	map(0x0010, 0x0010).w(FUNC(pc88va_state::rtc_w)); // Printer / Calendar Clock Interface
	map(0x0020, 0x0021).noprw(); // RS-232C
	map(0x0030, 0x0031).rw(FUNC(pc88va_state::backupram_dsw_r), FUNC(pc88va_state::sys_port1_w)); // 0x30 (R) DSW1 (W) Text Control Port 0 / 0x31 (R) DSW2 (W) System Port 1
	map(0x0032, 0x0032).rw(FUNC(pc88va_state::misc_ctrl_r), FUNC(pc88va_state::misc_ctrl_w));
//  map(0x0034, 0x0034) GVRAM Control Port 1
//  map(0x0035, 0x0035) GVRAM Control Port 2
	map(0x0040, 0x0040).rw(FUNC(pc88va_state::port40_r), FUNC(pc88va_state::port40_w)); // (R) System Port 4 (W) System port 3 (strobe port)
	map(0x0044, 0x0047).rw(m_opna, FUNC(ym2608_device::read), FUNC(ym2608_device::write));
//  map(0x0050, 0x005b) CRTC/backdrop on PC8801, causes HW trap on VA
//  map(0x005c, 0x005c) (R) GVRAM status
//  map(0x005c, 0x005f) (W) GVRAM selection
//  map(0x0060, 0x0068) DMA on PC8801, causes HW trap on VA
//  map(0x0070, 0x0070) ? (*)
//  map(0x0071, 0x0071) Expansion ROM select (*)
//  map(0x0078, 0x0078) Memory offset increment (*)
	map(0x0080, 0x0080).rw(FUNC(pc88va_state::sasi_data_r), FUNC(pc88va_state::sasi_data_w));
	map(0x0082, 0x0082).rw(FUNC(pc88va_state::sasi_status_r), FUNC(pc88va_state::sasi_ctrl_w));
//  map(0x00bc, 0x00bf) d8255 1
//  map(0x00e2, 0x00e3) Expansion RAM selection (*)
//  map(0x00e4, 0x00e4) 8214 IRQ control (*)
//  map(0x00e6, 0x00e6) 8214 IRQ mask (*)
//  map(0x00e8, 0x00e9) ? (*)
//  map(0x00ec, 0x00ed) ? (*)
	map(0x00fc, 0x00ff).r(FUNC(pc88va_state::fake_subfdc_r)).nopw();

	map(0x0100, 0x0101).rw(FUNC(pc88va_state::screen_ctrl_r), FUNC(pc88va_state::screen_ctrl_w)); // Screen Control Register
	map(0x0102, 0x0103).rw(FUNC(pc88va_state::gfx_ctrl_r), FUNC(pc88va_state::gfx_ctrl_w));
	map(0x0106, 0x0109).w(FUNC(pc88va_state::video_pri_w)); // Palette Control Register (priority) / Direct Color Control Register (priority)
//  map(0x010a, 0x010b) Picture Mask Mode Register
	map(0x010c, 0x010d).w(FUNC(pc88va_state::color_mode_w)); // Color Palette Mode Register
//  map(0x010e, 0x010f) Backdrop Color Register
//  map(0x0110, 0x0111) Color Code/Plain Mask Register
//  map(0x0124, 0x0125) ? (related to Transparent Color of Graphic Screen 0)
//  map(0x0126, 0x0127) ? (related to Transparent Color of Graphic Screen 1)
	map(0x012e, 0x012f).w(FUNC(pc88va_state::text_transpen_w));
	map(0x0130, 0x0137).w(FUNC(pc88va_state::picture_mask_w));
	map(0x0142, 0x0142).rw(FUNC(pc88va_state::idp_status_r), FUNC(pc88va_state::idp_command_w)); //Text Controller (IDP) - (R) Status (W) command
	map(0x0146, 0x0146).w(FUNC(pc88va_state::idp_param_w)); //Text Controller (IDP) - (R/W) Parameter
	map(0x0148, 0x0148).w(FUNC(pc88va_state::text_control_1_w));
//  map(0x014c, 0x014f) Kanji CG Port, animefrm
	map(0x014c, 0x014d).w(FUNC(pc88va_state::kanji_cg_address_w));
	map(0x014e, 0x014e).r(FUNC(pc88va_state::kanji_cg_r));
	map(0x014f, 0x014f).w(FUNC(pc88va_state::kanji_cg_raster_w));
	map(0x0150, 0x0151).r(FUNC(pc88va_state::sysop_r)); // System Operational Mode
	map(0x0152, 0x0153).rw(FUNC(pc88va_state::bios_bank_r), FUNC(pc88va_state::bios_bank_w)); // Memory Map Register
//  map(0x0154, 0x0155) Refresh Register (wait states)
	map(0x0156, 0x0156).r(FUNC(pc88va_state::rom_bank_r)); // ROM bank status
//  map(0x0158, 0x0159) Interruption Mode Modification (strobe), changes i8214 mode to i8259, cannot be changed back
//  map(0x015c, 0x015f) NMI mask port (strobe port)
//  map(0x0160, 0x016f) V50 DMAC
//  map(0x0180, 0x0180) read by Olteus
	map(0x0184, 0x0187).rw("pic8259_slave", FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
//  map(0x0188, 0x018b) V50 ICU
//  map(0x0190, 0x0191) System Port 5
	map(0x0190, 0x0190).rw(FUNC(pc88va_state::sys_port5_r), FUNC(pc88va_state::sys_port5_w));
//  map(0x0196, 0x0197) Keyboard sub CPU command port
	map(0x0198, 0x0199).w(FUNC(pc88va_state::backupram_wp_1_w)); //Backup RAM write inhibit
	map(0x019a, 0x019b).w(FUNC(pc88va_state::backupram_wp_0_w)); //Backup RAM write permission
//  map(0x01a0, 0x01a7) V50 TCU
	map(0x01a8, 0x01a8).w(FUNC(pc88va_state::timer3_ctrl_reg_w)); // General-purpose timer 3 control port
	map(0x01b0, 0x01b7).rw(FUNC(pc88va_state::pc88va_fdc_r), FUNC(pc88va_state::pc88va_fdc_w)).umask16(0x00ff); // FDC related (765)
	map(0x01b8, 0x01bb).m(m_fdc, FUNC(upd765a_device::map)).umask16(0x00ff);
//  map(0x01c0, 0x01c1) keyboard scan code, polled thru IRQ1 ...
	map(0x01c1, 0x01c1).lr8(NAME([this] () { return m_keyb.data; }));
	map(0x01c6, 0x01c7).nopw(); // ???
	map(0x01c8, 0x01cf).rw("d8255_3", FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0xff00); //i8255 3 (byte access)
//  map(0x01d0, 0x01d1) Expansion RAM bank selection
	map(0x0200, 0x027f).ram().share("fb_regs"); // Frame buffer 0-1-2-3 control parameter
	// TODO: shinraba writes to 0x340-0x37f on transition between opening and title screens (mirror? core bug?)
	map(0x0300, 0x033f).ram().w(FUNC(pc88va_state::palette_ram_w)).share("palram"); // Palette RAM (xBBBBxRRRRxGGGG format)

	map(0x0500, 0x0507).m(m_sgp, FUNC(pc88va_sgp_device::sgp_io));
	// GVRAM multiplane access regs (ROP section)
	// TODO: register are locked with GMSP = 1
	map(0x0510, 0x0510).lrw8(
		NAME([this] (offs_t offset) {
			LOGGFXCTRL("AACC extend access mode R\n");
			return m_multiplane.aacc;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_multiplane.aacc = !!BIT(data, 0);
			LOGGFXCTRL("AACC extend access mode W %02x\n", data);
		})
	);
	map(0x0512, 0x0512).lrw8(
		NAME([this] (offs_t offset) {
			LOGGFXCTRL("GMAP block switch R\n");
			return m_multiplane.gmap;
		}),
		NAME([this] (offs_t offset, u8 data) {
			LOGGFXCTRL("GMAP block switch W %02x\n", data);
			m_multiplane.gmap = !!BIT(data, 0);
		})
	);
	map(0x0514, 0x0514).lrw8(
		NAME([this] (offs_t offset) {
			LOGGFXCTRL("XRPMn plane readback select R\n");
			return m_multiplane.xrpm | 0xf0;
		}),
		NAME([this] (offs_t offset, u8 data) {
			LOGGFXCTRL("XRPMn plane readback select W %02x\n", data);
			m_multiplane.xrpm = data & 0xf;
		})
	);
	map(0x0516, 0x0516).lrw8(
		NAME([this] (offs_t offset) {
			LOGGFXCTRL("XWPMn plane write select R\n");
			return m_multiplane.xwpm | 0xf0;
		}),
		NAME([this] (offs_t offset, u8 data) {
			LOGGFXCTRL("XWPMn plane write select W %02x\n", data);
			m_multiplane.xwpm = data & 0xf;
		})
	);
	map(0x0518, 0x0518).lrw8(
		NAME([this] (offs_t offset) {
			// TODO: rbusy reads (bit 7)
			return (m_multiplane.cmpen << 5) | (m_multiplane.wss << 3) | (m_multiplane.pmod << 0);
		}),
		NAME([this] (offs_t offset, u8 data) {
			LOGGFXCTRL("Multiplane Mode W %02x\n", data);
			// PMOD bit 2 1 -> 0 transitions resets pattern pointers
			if (BIT(m_multiplane.pmod, 2) && !BIT(data, 2))
			{
				m_multiplane.prrp = 0;
				m_multiplane.prwp = 0;
			}

			m_multiplane.cmpen = !!BIT(data, 5);
			m_multiplane.wss = (data >> 3) & 3;
			m_multiplane.pmod = (data >> 0) & 7;
		})
	);
	map(0x0520, 0x0527).umask16(0x00ff).lrw8(
		NAME([this] (offs_t offset) {
			LOGGFXCTRL("CMPR extended access bit comparison R\n");
			return m_multiplane.cmpr[offset];
		}),
		NAME([this] (offs_t offset, u8 data) {
			LOGGFXCTRL("CMPR extended access bit comparison W %02x\n", data);
			m_multiplane.cmpr[offset] = data;
		})
	);
//  map(0x0528, 0x0528) extended access plane comparison
	map(0x0530, 0x0537).umask16(0x00ff).lrw8(
		NAME([this] (offs_t offset) {
			LOGGFXCTRL("Multiplane PATRL%d R\n", offset);
			return m_multiplane.patr[offset][0];
		}),
		NAME([this] (offs_t offset, u8 data) {
			LOGGFXCTRL("Multiplane PATRL%d W %02x\n", offset, data);
			m_multiplane.patr[offset][0] = data;
		})
	);
	map(0x0540, 0x0547).umask16(0x00ff).lrw8(
		NAME([this] (offs_t offset) {
			LOGGFXCTRL("Multiplane PATRH%d R\n", offset);
			return m_multiplane.patr[offset][1];
		}),
		NAME([this] (offs_t offset, u8 data) {
			LOGGFXCTRL("Multiplane PATRH%d W %02x\n", offset, data);
			m_multiplane.patr[offset][1] = data;
		})
	);
	map(0x0550, 0x0550).umask16(0x00ff).lrw8(
		NAME([this] (offs_t offset) {
			return m_multiplane.prrp | 0xf0;
		}),
		NAME([this] (offs_t offset, u8 data) {
			LOGGFXCTRL("PRRPn plane pattern usage start byte on read %02x\n", data);
			m_multiplane.prrp = data & 0xf;
		})
	);
	map(0x0552, 0x0552).umask16(0x00ff).lrw8(
		NAME([this] (offs_t offset) {
			return m_multiplane.prwp | 0xf0;
		}),
		NAME([this] (offs_t offset, u8 data) {
			LOGGFXCTRL("PRWPn plane pattern usage start byte on write %02x\n", data);
			m_multiplane.prwp = data & 0xf;
		})
	);
	map(0x0560, 0x0567).umask16(0x00ff).lrw8(
		NAME([this] (offs_t offset) {
			return m_multiplane.rop[offset];
		}),
		NAME([this] (offs_t offset, u8 data) {
			LOGGFXCTRL("Multiplane ROP %d W %02x\n", offset, data);
			m_multiplane.rop[offset] = data;
		})
	);
	// GVRAM single plane access regs
	// TODO: register are locked with GMSP = 0
	map(0x0580, 0x0580).lrw8(
		NAME([this] (offs_t offset) {
			// TODO: rbusy reads (bit 7)
			return (m_singleplane.wss << 3);
		}),
		NAME([this] (offs_t offset, u8 data) {
			LOGGFXCTRL("Singleplane Mode W %02x\n", data);
			m_singleplane.wss = (data >> 3) & 3;
		})
	);
	map(0x0590, 0x0593).umask16(0x00ff).lrw8(
		NAME([this] (offs_t offset) {
			LOGGFXCTRL("Singleplane PATR%d R\n", offset);
			return m_singleplane.patr[offset];
		}),
		NAME([this] (offs_t offset, u8 data) {
			LOGGFXCTRL("Singleplane PATR%d W %02x\n", offset, data);
			m_singleplane.patr[offset] = data;
		})
	);
	map(0x05a0, 0x05a3).umask16(0x00ff).lrw8(
		NAME([this] (offs_t offset) {
			return m_singleplane.rop[offset];
		}),
		NAME([this] (offs_t offset, u8 data) {
			LOGGFXCTRL("Singleplane ROP %d W %02x\n", offset, data);
			m_singleplane.rop[offset] = data;
		})
	);

//  map(0x1000, 0xfeff) PC-88VA expansion boards
//  map(0xe2d2, 0xe2d2) MIDI status in micromus
//  map(0xff00, 0xffff).noprw(); // CPU internal use
}

void pc88va_state::opna_map(address_map &map)
{
	// TODO: confirm it really is ROMless
	// TODO: confirm size
	map(0x000000, 0x1fffff).ram();
}

// TODO: quick and dirty support
// should really inherit from the PC8001/PC8801 family as a device, applying the fact that is running on (undumped) MCU instead
INPUT_CHANGED_MEMBER(pc88va_state::key_stroke)
{
	if(newval && !oldval)
	{
		m_keyb.data = uint8_t(param & 0xff);
		//m_keyb.status &= ~1;
		m_maincpu->set_input_line(INPUT_LINE_IRQ1, CLEAR_LINE);
		m_maincpu->set_input_line(INPUT_LINE_IRQ1, ASSERT_LINE);
	}

	// TODO: eventually thrown away by the MCU after set time
	if(oldval && !newval)
	{
		m_keyb.data = 0xff;
		//m_keyb.status |= 1;
	}
}

#define VA_PORT_SCAN(_scancode_) \
	PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pc88va_state::key_stroke), _scancode_)

static INPUT_PORTS_START( pc88va )
	PORT_START("KEY0")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)       PORT_CHAR(UCHAR_MAMEKEY(0_PAD)) VA_PORT_SCAN(0x4e)
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)       PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) VA_PORT_SCAN(0x4a)
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)       PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) VA_PORT_SCAN(0x4b)
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)       PORT_CHAR(UCHAR_MAMEKEY(3_PAD)) VA_PORT_SCAN(0x4c)
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)       PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) VA_PORT_SCAN(0x46)
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)       PORT_CHAR(UCHAR_MAMEKEY(5_PAD)) VA_PORT_SCAN(0x47)
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)       PORT_CHAR(UCHAR_MAMEKEY(6_PAD)) VA_PORT_SCAN(0x48)
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)       PORT_CHAR(UCHAR_MAMEKEY(7_PAD)) VA_PORT_SCAN(0x42)

	PORT_START("KEY1")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)       PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) VA_PORT_SCAN(0x43)
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)       PORT_CHAR(UCHAR_MAMEKEY(9_PAD)) VA_PORT_SCAN(0x44)
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK)    PORT_CHAR(UCHAR_MAMEKEY(ASTERISK)) VA_PORT_SCAN(0x45)
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD)    PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD)) VA_PORT_SCAN(0x49)
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP)        PORT_CHAR(UCHAR_MAMEKEY(EQUALS_PAD)) VA_PORT_SCAN(0x4d)
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN)        PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD)) VA_PORT_SCAN(0x4f)
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD)     PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD)) VA_PORT_SCAN(0x39)
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)       PORT_CHAR(13) VA_PORT_SCAN(0x1c)

	PORT_START("KEY2")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)   PORT_CHAR('@') VA_PORT_SCAN(0x1a)
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)           PORT_CHAR('a') PORT_CHAR('A') VA_PORT_SCAN(0x1d)
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)           PORT_CHAR('b') PORT_CHAR('B') VA_PORT_SCAN(0x2d)
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)           PORT_CHAR('c') PORT_CHAR('C') VA_PORT_SCAN(0x2b)
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)           PORT_CHAR('d') PORT_CHAR('D') VA_PORT_SCAN(0x1f)
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)           PORT_CHAR('e') PORT_CHAR('E') VA_PORT_SCAN(0x12)
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)           PORT_CHAR('f') PORT_CHAR('F') VA_PORT_SCAN(0x20)
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)           PORT_CHAR('g') PORT_CHAR('G') VA_PORT_SCAN(0x21)

	PORT_START("KEY3")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)           PORT_CHAR('h') PORT_CHAR('H') VA_PORT_SCAN(0x22)
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)           PORT_CHAR('i') PORT_CHAR('I') VA_PORT_SCAN(0x17)
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)           PORT_CHAR('j') PORT_CHAR('J') VA_PORT_SCAN(0x23)
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)           PORT_CHAR('k') PORT_CHAR('K') VA_PORT_SCAN(0x24)
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)           PORT_CHAR('l') PORT_CHAR('L') VA_PORT_SCAN(0x25)
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)           PORT_CHAR('m') PORT_CHAR('M') VA_PORT_SCAN(0x2f)
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)           PORT_CHAR('n') PORT_CHAR('N') VA_PORT_SCAN(0x2e)
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)           PORT_CHAR('o') PORT_CHAR('O') VA_PORT_SCAN(0x18)

	PORT_START("KEY4")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)           PORT_CHAR('p') PORT_CHAR('P') VA_PORT_SCAN(0x19)
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)           PORT_CHAR('q') PORT_CHAR('Q') VA_PORT_SCAN(0x10)
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)           PORT_CHAR('r') PORT_CHAR('R') VA_PORT_SCAN(0x13)
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)           PORT_CHAR('s') PORT_CHAR('S') VA_PORT_SCAN(0x1e)
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)           PORT_CHAR('t') PORT_CHAR('T') VA_PORT_SCAN(0x14)
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)           PORT_CHAR('u') PORT_CHAR('U') VA_PORT_SCAN(0x16)
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)           PORT_CHAR('v') PORT_CHAR('V') VA_PORT_SCAN(0x2c)
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)           PORT_CHAR('w') PORT_CHAR('W') VA_PORT_SCAN(0x11)

	PORT_START("KEY5")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)           PORT_CHAR('x') PORT_CHAR('X') VA_PORT_SCAN(0x2a)
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)           PORT_CHAR('y') PORT_CHAR('Y') VA_PORT_SCAN(0x15)
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)           PORT_CHAR('z') PORT_CHAR('Z') VA_PORT_SCAN(0x29)
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)  PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2)  PORT_CHAR(0xA5) PORT_CHAR('|')
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)   PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)      PORT_CHAR('^')
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)       PORT_CHAR('-') PORT_CHAR('=')

	PORT_START("KEY6")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)           PORT_CHAR('0')
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)           PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)           PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)           PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)           PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)           PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)           PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)           PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("KEY7")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)           PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)           PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)       PORT_CHAR(':') PORT_CHAR('*') VA_PORT_SCAN(0x27)
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)       PORT_CHAR(';') PORT_CHAR('+') VA_PORT_SCAN(0x26)
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)       PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)        PORT_CHAR('.') PORT_CHAR('>') VA_PORT_SCAN(0x50)
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)       PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("  _") PORT_CODE(KEYCODE_DEL)            PORT_CHAR(0) PORT_CHAR('_')

	PORT_START("KEY8")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Clr Home") PORT_CODE(KEYCODE_HOME)      PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP)   PORT_CHAR(UCHAR_MAMEKEY(UP)) VA_PORT_SCAN(0x3a)
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) VA_PORT_SCAN(0x3c)
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del Ins") PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(UCHAR_MAMEKEY(DEL)) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Grph") PORT_CODE(KEYCODE_LALT)  PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Kana") PORT_CODE(KEYCODE_LCONTROL) PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RCONTROL)                        PORT_CHAR(UCHAR_SHIFT_2)

	PORT_START("KEY9")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Stop")                                  PORT_CHAR(UCHAR_MAMEKEY(PAUSE)) VA_PORT_SCAN(0x60)
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)                              PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)                              PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)                              PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)                              PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)                              PORT_CHAR(UCHAR_MAMEKEY(F5)) VA_PORT_SCAN(0x66)
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)                           PORT_CHAR(' ') VA_PORT_SCAN(0x34)
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)                             PORT_CHAR(UCHAR_MAMEKEY(ESC))

	PORT_START("KEYA")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)                             PORT_CHAR('\t')
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN)   PORT_CHAR(UCHAR_MAMEKEY(DOWN)) VA_PORT_SCAN(0x3d)
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT)   PORT_CHAR(UCHAR_MAMEKEY(LEFT)) VA_PORT_SCAN(0x3b)
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Help") PORT_CODE(KEYCODE_END)           PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Copy") PORT_CODE(KEYCODE_F2)            PORT_CHAR(UCHAR_MAMEKEY(PRTSCR))
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD)                       PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD)                       PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD))
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Caps") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))

	PORT_START("KEYB")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Roll Up") PORT_CODE(KEYCODE_F8)         PORT_CHAR(UCHAR_MAMEKEY(PGUP))
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Roll Down") PORT_CODE(KEYCODE_F9)       PORT_CHAR(UCHAR_MAMEKEY(PGDN))
	// TODO: definitely other bits in here, cfr. pc8801ma extra keys
	PORT_BIT(0xfc,IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEYC")
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) VA_PORT_SCAN(0x62)
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) VA_PORT_SCAN(0x63)
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) VA_PORT_SCAN(0x64)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) VA_PORT_SCAN(0x65)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5) VA_PORT_SCAN(0x66)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) VA_PORT_SCAN(0x0e)
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("INS") PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL)

	PORT_START("KEYD")
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F6") PORT_CODE(KEYCODE_F6) VA_PORT_SCAN(0x67)
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F7") PORT_CODE(KEYCODE_F7) VA_PORT_SCAN(0x68)
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F8") PORT_CODE(KEYCODE_F8) VA_PORT_SCAN(0x69)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F9") PORT_CODE(KEYCODE_F9) VA_PORT_SCAN(0x6a)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F10") PORT_CODE(KEYCODE_F10) VA_PORT_SCAN(0x6b)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) // Conversion?
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) // Decision?
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Space") // ?

	PORT_START("KEYE")
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad Enter") PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13) VA_PORT_SCAN(0x79)
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD)
	PORT_BIT(0xc0,IP_ACTIVE_LOW,IPT_UNUSED)

	PORT_START("KEYF")
	PORT_BIT(0xff,IP_ACTIVE_LOW,IPT_UNUSED)

	PORT_START("DSW")
	PORT_DIPNAME( 0x01, 0x00, "CRT Mode" )
	PORT_DIPSETTING(    0x01, "15.7 KHz" )
	PORT_DIPSETTING(    0x00, "24.8 KHz" )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("SPEED_SW")
	PORT_DIPNAME( 0x01, 0x01, "Speed Mode" )
	PORT_DIPSETTING(    0x01, "H Mode" )
	PORT_DIPSETTING(    0x00, "S Mode" )

	PORT_START("SYSOP_SW")
	PORT_DIPNAME( 0x03, 0x01, "System Operational Mode" )
//  PORT_DIPSETTING(    0x00, "Reserved" )
	PORT_DIPSETTING(    0x01, "N88 V2 Mode" )
	PORT_DIPSETTING(    0x02, "N88 V1 Mode" )
//  PORT_DIPSETTING(    0x03, "???" )
INPUT_PORTS_END

static const gfx_layout pc88va_chars_8x8 =
{
	8,8,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP8(0,1) },
	{ STEP8(0,8) },
	8*8
};

static const gfx_layout pc88va_chars_16x16 =
{
	16,16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP16(0,1) },
	{ STEP16(0,16) },
	16*16
};

// same as above but with static size
static const gfx_layout pc88va_kanji_8x8 =
{
	8,8,
	0x4000/8,
	1,
	{ 0 },
	{ STEP8(0,1) },
	{ STEP8(0,8) },
	8*8
};

static const gfx_layout pc88va_kanji_16x16 =
{
	16,16,
	0x4000/32,
	1,
	{ 0 },
	{ STEP16(0,1) },
	{ STEP16(0,16) },
	16*16
};

// debug only
static GFXDECODE_START( gfx_pc88va )
	GFXDECODE_ENTRY( "kanji",   0x00000, pc88va_chars_8x8,    0, 16 )
	GFXDECODE_ENTRY( "kanji",   0x00000, pc88va_chars_16x16,  0, 16 )
	GFXDECODE_RAM(   nullptr,   0x00000, pc88va_kanji_8x8,    0, 16 )
	GFXDECODE_RAM(   nullptr,   0x00000, pc88va_kanji_16x16,  0, 16 )
GFXDECODE_END


uint8_t pc88va_state::r232_ctrl_porta_r()
{
	uint8_t sw5, sw4, sw3, sw2,speed_sw;

	speed_sw = (ioport("SPEED_SW")->read() & 1) ? 0x20 : 0x00;
	sw5 = (ioport("DSW")->read() & 0x10);
	sw4 = (ioport("DSW")->read() & 0x08);
	sw3 = (ioport("DSW")->read() & 0x04);
	sw2 = (ioport("DSW")->read() & 0x02);

	return 0xc1 | sw5 | sw4 | sw3 | sw2 | speed_sw;
}

uint8_t pc88va_state::r232_ctrl_portb_r()
{
	uint8_t xsw1;

	xsw1 = (ioport("DSW")->read() & 1) ? 0 : 8;

	return 0xf7 | xsw1;
}

uint8_t pc88va_state::r232_ctrl_portc_r()
{
	return 0xff;
}

void pc88va_state::r232_ctrl_porta_w(uint8_t data)
{
	// ...
}

void pc88va_state::r232_ctrl_portb_w(uint8_t data)
{
	// ...
}

void pc88va_state::r232_ctrl_portc_w(uint8_t data)
{
	// ...
}

/****************************************
 * IRQ lines
 ***************************************/

uint8_t pc88va_state::get_slave_ack(offs_t offset)
{
	if (offset == 7) {
		return m_pic2->acknowledge();
	}
	return 0x00;
}

TIMER_DEVICE_CALLBACK_MEMBER(pc88va_state::vrtc_irq)
{
	int scanline = param;

	// upo and ballbrkr have a working vrtc irq routine and a vblank $40 readback at same time.
	// there's no clear /VRMF write to $e6 so presuming the irq happening later than default
	// screen_device first line after visible.
	if (scanline == m_vrtc_irq_line)
	{
		// TODO: verify when ack should happen
		m_maincpu->set_input_line(INPUT_LINE_IRQ2, CLEAR_LINE);
		m_maincpu->set_input_line(INPUT_LINE_IRQ2, ASSERT_LINE);
	}
}

void pc88va_state::fdc_irq(int state)
{
	if(m_fdc_mode && state)
	{
		m_pic2->ir3_w(0);
		m_pic2->ir3_w(1);
	}
}

void pc88va_state::int4_irq_w(int state)
{
	bool irq_state = m_sound_irq_enable & state;

	if (irq_state)
	{
		m_pic2->ir4_w(0);
		m_pic2->ir4_w(1);
	}
//  m_pic->r_w(7 ^ INT4_IRQ_LEVEL, !irq_state);
	m_sound_irq_pending = state;
}

void pc88va_state::tc_w(int state)
{
	m_fdc->tc_w(state);
}

void pc88va_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_XDF_FORMAT);
	fr.add(FLOPPY_PC98FDI_FORMAT);
}

static void pc88va_floppies(device_slot_interface &device)
{
	device.option_add("525hd", FLOPPY_525_HD);
}

void pc88va_state::machine_start()
{
	m_rtc->cs_w(1);
	m_rtc->oe_w(1);

	m_fdc_timer = timer_alloc(FUNC(pc88va_state::pc88va_fdc_timer), this);
	m_fdc_timer->adjust(attotime::never);

	m_motor_start_timer[0] = timer_alloc(FUNC(pc88va_state::pc88va_fdc_motor_start_0), this);
	m_motor_start_timer[1] = timer_alloc(FUNC(pc88va_state::pc88va_fdc_motor_start_1), this);
	m_motor_start_timer[0]->adjust(attotime::never);
	m_motor_start_timer[1]->adjust(attotime::never);

	m_t3_mouse_timer = timer_alloc(FUNC(pc88va_state::t3_mouse_callback), this);
	m_t3_mouse_timer->adjust(attotime::never);

	floppy_image_device *floppy;
	floppy = m_fdd[0]->get_device();
	if(floppy)
		floppy->setup_ready_cb(floppy_image_device::ready_cb(&pc88va_state::pc88va_fdc_update_ready, this));

	floppy = m_fdd[1]->get_device();
	if(floppy)
		floppy->setup_ready_cb(floppy_image_device::ready_cb(&pc88va_state::pc88va_fdc_update_ready, this));

	m_fdd[0]->get_device()->set_rpm(300);
	m_fdd[1]->get_device()->set_rpm(300);
	m_fdc->set_rate(250000);
}

void pc88va_state::machine_reset()
{
	uint8_t *ROM00 = memregion("rom00")->base();
	uint8_t *ROM10 = memregion("rom10")->base();

	membank("rom10_bank")->set_base(&ROM10[0x00000]);
	membank("rom00_bank")->set_base(&ROM00[0x00000]);

	m_bank_reg = 0x4100;
	m_sysbank->set_bank(1);
	m_backupram_wp = 1;
	m_rstmd = false;

	m_tsp.tvram_vreg_offset = 0;

	m_fdc_mode = 0;
	m_xtmask = false;

	// shinraba never write to port $32,
	// and it expects that the sound irq actually runs otherwise it enters in debug mode
	m_misc_ctrl = 0x00;
	m_sound_irq_enable = true;
	m_sound_irq_pending = false;
}

// TODO: add just a subset for now, all needs to be verified if compatible with C-Bus.
static void pc88va_cbus_devices(device_slot_interface &device)
{
	device.option_add("pc9801_55u", PC9801_55U);
	device.option_add("pc9801_55l", PC9801_55L);
	device.option_add("mif_201",    MIF201);
	device.option_add("mpu_pc98",   MPU_PC98);
}

// TODO: make it to work and backport to C-Bus
void pc88va_state::pc88va_sasi(machine_config &config)
{
	SCSI_PORT(config, m_sasibus, 0);
	m_sasibus->set_data_input_buffer("sasi_data_in");
	m_sasibus->io_handler().set(FUNC(pc88va_state::write_sasi_io)); // bit2
	m_sasibus->cd_handler().set("sasi_ctrl_in", FUNC(input_buffer_device::write_bit3));
	m_sasibus->msg_handler().set("sasi_ctrl_in", FUNC(input_buffer_device::write_bit4));
	m_sasibus->bsy_handler().set("sasi_ctrl_in", FUNC(input_buffer_device::write_bit5));
	m_sasibus->ack_handler().set("sasi_ctrl_in", FUNC(input_buffer_device::write_bit6));
	m_sasibus->req_handler().set(FUNC(pc88va_state::write_sasi_req));
	m_sasibus->set_slot_device(1, "harddisk", PC9801_SASI, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_0));

	output_latch_device &sasi_out(OUTPUT_LATCH(config, "sasi_data_out"));
	m_sasibus->set_output_latch(sasi_out);
	INPUT_BUFFER(config, "sasi_data_in");
	INPUT_BUFFER(config, "sasi_ctrl_in");

	m_maincpu->in_ior_cb<0>().set(FUNC(pc88va_state::sasi_data_r));
	m_maincpu->out_iow_cb<0>().set(FUNC(pc88va_state::sasi_data_w));
}


// NOTE: PC-88VA implementation omits some C-Bus lines compared to PC-98.
// - doesn't have ir12 and ir13, i.e. covers INT0 to INT4 only
// - no /CPUKILL pin support
// cfr. schematics pg. 260, "external bus, videoboard connector"
void pc88va_state::pc88va_cbus(machine_config &config)
{
	PC9801CBUS_SLOT(config, m_cbus[0], pc88va_cbus_devices, nullptr);
	m_cbus[0]->set_memspace(m_maincpu, AS_PROGRAM);
	m_cbus[0]->set_iospace(m_maincpu, AS_IO);
	m_cbus[0]->int_cb<0>().set("ir3", FUNC(input_merger_device::in_w<0>));
	m_cbus[0]->int_cb<1>().set("ir5", FUNC(input_merger_device::in_w<0>));
	m_cbus[0]->int_cb<2>().set("ir6", FUNC(input_merger_device::in_w<0>));
	m_cbus[0]->int_cb<3>().set("ir9", FUNC(input_merger_device::in_w<0>));
	m_cbus[0]->int_cb<4>().set("ir10", FUNC(input_merger_device::in_w<0>));

	PC9801CBUS_SLOT(config, m_cbus[1], pc88va_cbus_devices, nullptr);
	m_cbus[1]->set_memspace(m_maincpu, AS_PROGRAM);
	m_cbus[1]->set_iospace(m_maincpu, AS_IO);
	m_cbus[1]->int_cb<0>().set("ir3", FUNC(input_merger_device::in_w<1>));
	m_cbus[1]->int_cb<1>().set("ir5", FUNC(input_merger_device::in_w<1>));
	m_cbus[1]->int_cb<2>().set("ir6", FUNC(input_merger_device::in_w<1>));
	m_cbus[1]->int_cb<3>().set("ir9", FUNC(input_merger_device::in_w<1>));
	m_cbus[1]->int_cb<4>().set("ir10", FUNC(input_merger_device::in_w<1>));

	// TODO: check actual number of slots for each VA iteration

	INPUT_MERGER_ANY_HIGH(config, "ir3").output_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ3);
	INPUT_MERGER_ANY_HIGH(config, "ir5").output_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ5);
	INPUT_MERGER_ANY_HIGH(config, "ir6").output_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ6);
	INPUT_MERGER_ANY_HIGH(config, "ir9").output_handler().set("pic8259_slave", FUNC(pic8259_device::ir1_w));
	INPUT_MERGER_ANY_HIGH(config, "ir10").output_handler().set("pic8259_slave", FUNC(pic8259_device::ir2_w));
}

void pc88va_state::pc88va(machine_config &config)
{
	V50(config, m_maincpu, MASTER_CLOCK); // μPD9002, aka V50 + μPD70008AC (for PC8801 compatibility mode) in place of 8080
	m_maincpu->set_addrmap(AS_PROGRAM, &pc88va_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &pc88va_state::io_map);
	TIMER(config, "scantimer").configure_scanline(FUNC(pc88va_state::vrtc_irq), "screen", 0, 1);
	m_maincpu->icu_slave_ack_cb().set(m_pic2, FUNC(pic8259_device::acknowledge));
	m_maincpu->set_tclk(MASTER_CLOCK);
	// "timer 1"
	m_maincpu->tout1_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	// ch2 is FDC, ch0/3 are "user". ch1 is unused
	m_maincpu->out_hreq_cb().set(m_maincpu, FUNC(v50_device::hack_w));
	m_maincpu->out_eop_cb().set(FUNC(pc88va_state::tc_w));
	m_maincpu->in_ior_cb<2>().set(m_fdc, FUNC(upd765a_device::dma_r));
	m_maincpu->out_iow_cb<2>().set(m_fdc, FUNC(upd765a_device::dma_w));
	m_maincpu->in_memr_cb().set([this] (offs_t offset) { return m_maincpu->space(AS_PROGRAM).read_byte(offset); });
	m_maincpu->out_memw_cb().set([this] (offs_t offset, u8 data) { m_maincpu->space(AS_PROGRAM).write_byte(offset, data); });

	pc88va_cbus(config);

	// TODO: pc80s31k here

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(XTAL(42'105'200) / 2, 848, 0, 640, 448, 0, 400);
	m_screen->set_screen_update(FUNC(pc88va_state::screen_update));

	PALETTE(config, m_palette, FUNC(pc88va_state::palette_init)).set_entries(32);
	GFXDECODE(config, m_gfxdecode, m_palette, gfx_pc88va);

	PC88VA_SGP(config, m_sgp);
	m_sgp->set_map(&pc88va_state::sgp_map);

	i8255_device &d8255_3(I8255(config, "d8255_3"));
	d8255_3.in_pa_callback().set(FUNC(pc88va_state::r232_ctrl_porta_r));
	d8255_3.out_pa_callback().set(FUNC(pc88va_state::r232_ctrl_porta_w));
	d8255_3.in_pb_callback().set(FUNC(pc88va_state::r232_ctrl_portb_r));
	d8255_3.out_pb_callback().set(FUNC(pc88va_state::r232_ctrl_portb_w));
	d8255_3.in_pc_callback().set(FUNC(pc88va_state::r232_ctrl_portc_r));
	d8255_3.out_pc_callback().set(FUNC(pc88va_state::r232_ctrl_portc_w));

	// external PIC
	PIC8259(config, m_pic2, 0);
	m_pic2->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ7);
	m_pic2->in_sp_callback().set_constant(0);

	pc88va_sasi(config);

	UPD765A(config, m_fdc, 4000000, true, true);
	m_fdc->intrq_wr_callback().set(FUNC(pc88va_state::fdc_irq));
	m_fdc->drq_wr_callback().set(m_maincpu, FUNC(v50_device::dreq_w<2>));
	FLOPPY_CONNECTOR(config, m_fdd[0], pc88va_floppies, "525hd", pc88va_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_fdd[1], pc88va_floppies, "525hd", pc88va_state::floppy_formats).enable_sound(true);

	UPD4990A(config, m_rtc);

	ADDRESS_MAP_BANK(config, "sysbank").set_map(&pc88va_state::sysbank_map).set_options(ENDIANNESS_LITTLE, 16, 22, 0x40000);

	MSX_GENERAL_PURPOSE_PORT(config, m_mouse_port, msx_general_purpose_port_devices, "joystick");

	SPEAKER(config, m_speaker, 2).front();

	// TODO: YM2203 for vanilla pc88va
	// PC-88VA-12 "Sound Board II", YM2608B
	YM2608(config, m_opna, FM_CLOCK);
	m_opna->set_addrmap(0, &pc88va_state::opna_map);
	m_opna->irq_handler().set(FUNC(pc88va_state::int4_irq_w));
	m_opna->port_a_read_callback().set(FUNC(pc88va_state::opn_porta_r));
	m_opna->port_b_read_callback().set(FUNC(pc88va_state::opn_portb_r));
	m_opna->port_b_write_callback().set(FUNC(pc88va_state::opn_portb_w));
	// TODO: per-channel mixing is unconfirmed
	m_opna->add_route(0, m_speaker, 0.75, 0);
	m_opna->add_route(0, m_speaker, 0.75, 1);
	m_opna->add_route(1, m_speaker, 0.75, 0);
	m_opna->add_route(2, m_speaker, 0.75, 1);

	// TODO: set pc98 compatible
	// Needs a MS-Engine disk dump first, that applies an overlay on PC Engine OS so that it can run PC-98 software
	SOFTWARE_LIST(config, "disk_list").set_original("pc88va");
}


ROM_START( pc88va )
	ROM_REGION( 0x100000, "maincpu", ROMREGION_ERASEFF )

	// In pc80s31k device
//  ROM_REGION( 0x100000, "fdccpu", ROMREGION_ERASEFF )
//  ROM_LOAD( "vasubsys.rom", 0x0000, 0x2000, CRC(08962850) SHA1(a9375aa480f85e1422a0e1385acb0ea170c5c2e0) )

	ROM_REGION( 0x100000, "rom00", ROMREGION_ERASEFF ) // 0xe0000 - 0xeffff
	ROM_LOAD( "varom00.rom", 0x00000, 0x80000, CRC(8a853b00) SHA1(1266ba969959ff25433ecc900a2caced26ef1a9e))
	ROM_LOAD( "varom08.rom", 0x80000, 0x20000, CRC(154803cc) SHA1(7e6591cd465cbb35d6d3446c5a83b46d30fafe95))

	ROM_REGION( 0x20000, "rom10", ROMREGION_ERASEFF ) // 0xf0000 - 0xfffff
	ROM_LOAD( "varom1.rom", 0x00000, 0x20000, CRC(0783b16a) SHA1(54536dc03238b4668c8bb76337efade001ec7826))

	// TODO: identify this, likely don't even belong to pc88va internals
	ROM_REGION( 0x2000, "audiocpu", 0)
	ROM_LOAD( "soundbios.rom", 0x0000, 0x2000, NO_DUMP )

	// TODO: identify this
	ROM_REGION( 0x1000, "mcu", 0)
	ROM_LOAD( "kbd.rom", 0x0000, 0x1000, NO_DUMP )

	ROM_REGION16_LE( 0x80000, "kanji", ROMREGION_ERASEFF )
	ROM_LOAD( "vafont.rom", 0x0000, 0x50000, BAD_DUMP CRC(faf7c466) SHA1(196b3d5b7407cb4f286ffe5c1e34ebb1f6905a8c)) // should be splitted

	ROM_REGION16_LE( 0x80000, "dictionary", 0 )
	ROM_LOAD( "vadic.rom",  0x0000, 0x80000, CRC(f913c605) SHA1(5ba1f3578d0aaacdaf7194a80e6d520c81ae55fb))
ROM_END

ROM_START( pc88va2 )
	ROM_REGION( 0x100000, "maincpu", ROMREGION_ERASEFF )

//  ROM_REGION( 0x100000, "fdccpu", ROMREGION_ERASEFF )
//  ROM_LOAD( "vasubsys.rom", 0x0000, 0x2000, CRC(08962850) SHA1(a9375aa480f85e1422a0e1385acb0ea170c5c2e0) )

	ROM_REGION( 0x100000, "rom00", ROMREGION_ERASEFF ) // 0xe0000 - 0xeffff
	ROM_LOAD( "varom00_va2.rom",   0x00000, 0x80000, CRC(98c9959a) SHA1(bcaea28c58816602ca1e8290f534360f1ca03fe8) )
	ROM_LOAD( "varom08_va2.rom",   0x80000, 0x20000, CRC(eef6d4a0) SHA1(47e5f89f8b0ce18ff8d5d7b7aef8ca0a2a8e3345) )

	ROM_REGION( 0x20000, "rom10", ROMREGION_ERASEFF ) // 0xf0000 - 0xfffff
	ROM_LOAD( "varom1_va2.rom",    0x00000, 0x20000, CRC(7e767f00) SHA1(dd4f4521bfbb068f15ab3bcdb8d47c7d82b9d1d4) )

	// TODO: identify this, likely don't even belong to pc88va internals
	ROM_REGION( 0x2000, "audiocpu", 0)
	ROM_LOAD( "soundbios.rom", 0x0000, 0x2000, NO_DUMP )

	// TODO: identify this
	ROM_REGION( 0x1000, "mcu", 0)
	ROM_LOAD( "kbd.rom", 0x0000, 0x1000, NO_DUMP )

	ROM_REGION16_LE( 0x80000, "kanji", ROMREGION_ERASEFF )
	ROM_LOAD( "vafont_va2.rom", 0x00000, 0x50000, BAD_DUMP CRC(b40d34e4) SHA1(a0227d1fbc2da5db4b46d8d2c7e7a9ac2d91379f) ) // should be splitted

	ROM_REGION16_LE( 0x80000, "dictionary", 0 )
	ROM_LOAD( "vadic_va2.rom", 0x00000, 0x80000, CRC(a6108f4d) SHA1(3665db538598abb45d9dfe636423e6728a812b12) )
ROM_END

COMP( 1987, pc88va,  0,      0, pc88va, pc88va, pc88va_state, empty_init, "NEC", "PC-88VA",  MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_SOUND )
COMP( 1988, pc88va2, pc88va, 0, pc88va, pc88va, pc88va_state, empty_init, "NEC", "PC-88VA2", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_SOUND )
// VA3 has 3.5" 2TD drives with about 9.3 MB capacity
//COMP( 1988, pc88va3, pc88va, 0, pc88va, pc88va, pc88va_state, empty_init, "NEC", "PC-88VA3", MACHINE_NOT_WORKING )



pc9801.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese,Carl
/**************************************************************************************************

    PC-9801 (c) 1981 NEC

    TODO:
    - text scrolling, μPD52611 (cfr. clipping in edge & arcus2, madoum* too?);
    - Abnormal 90 Hz refresh rate adjust for normal display mode (15KHz).
      Should really be 61.xx instead, understand how CRTC really switches clock;
    - AGDC emulation, μPD72120;
    - GP-IB emulation, μPD7210;
    - DAC1BIT has a bit of clicking with start/end of samples, is it fixable or just a btanb?
    - Write a PC80S31K device for 2d type floppies
      (also used on PC-6601SR, PC-8801 and PC-88VA, it's the FDC + Z80 sub-system);
    - FDC (note: epdiag FDC test looks a good candidate for all this):
        - Has on board dip-switches, we currently just return 2HD/2DD autodetect;
        - 3'5 floppy disks don't load at all ($4be I/O port is the extra accessor);
        - fix FDC duplication: according to docs I/O ports $90-$95 are basically mirrors
          with a subset of the drive related flags.
          Sounds like a afterthought of having 2HD/2DD separate boards from vanilla class;
        - Move vanilla FDC 2HD/2DD to a separate (legacy?) bus, and split pc9801f (default: 2DD)
          from pc9801m (2HD) and vanilla pc9801 (none);
        - floppy sounds never silences when drive is idle (disabled for the time being);
        - epdiag throws ID invalid when run with PORT EXC on (DIP-SW 3-1 -> 0);
    - CMT support (-03/-13/-36 i/f or cbus only, supported by i86/V30 fully compatible machines
      only);
    - SASI/SCSI support (fully supported by now?);
    - IDE sports an hack to not make 512 to 256 sector byte translations.
      Apparently a SDIP setting is responsible for this?
    - Remove kludge for POR bit in a20_ctrl_w fn;
    - I/O:
        - HW Dip-switches (where applicable) needs a serious clean-up and naming/position
          fixing in some cases;
        - Export mouse support to an actual PC9871 device;
        - Implement IF-SEGA/98 support (Sega Saturn peripheral compatibility for Windows,
          cfr. BeOS PnP driver);
    - clean-up functions/variables naming by actual documentation nomenclature;
    - derive machine configs & romsets by actual default options, examples:
        - 3.5 built-in floppy drives vs. default 5.25;
        - separate pc9801f (2DD) available romset to pc9801 (none) & pc9801m (2HD), and
          remove the correlated machine config option;
        - cbus available number of slots & built-in or provided boards;
        - separate machines HDD hooks by SASI/SCSI/IDE;
        - load actual IDE bioses from IPL romsets where applicable
          (late era 9801 and 9821 class machines);
        - pinpoint machines that uses GRCG instead of EGC, we are currently too lenient and support
          latter on most (use dbuster and hypbingo to checkout);
        - Improve opacity of video flip/flop registers, consider using an address space instead of
          current array format;

    TODO (PC-9801F)
    - it currently hooks up half size kanji ROMs, causing missing text in many games;

    TODO (PC-9801RS):
    - several unemulated extra f/f features;
    - keyboard shift doesn't seem to disable properly (fixed by now?);
    - Several games hangs with stuck note by misfired/not catched up -26 / -86 irq;
    - clean-up duplicate code;

    TODO (PC-9801RX?):
    - Identify model type, it clearly accesses PCI, the extended 3'5 floppy I/O at 0x4be and
      it's not a 286 CPU;
    - Floppy boot fails;

    TODO (PC-9801US / PC-9801FS):
    - "Invalid Command Byte 13" for bitmap upd7220 at POST (?)
    - RAM check detects more RAM than what's really installed (and saves previous detection in MEMSW);
    - pc9801fs at least: Crashes with Japanese error for "HDD failure" when mounted with IDE BIOS,
      incompatible with 512 bps or IDE itself?

    TODO (PC-9801BX2)
    - "SYSTEM SHUTDOWN" at POST, SDIP related, soft reset to bypass;
    - Accesses $8f0-$8f2 PMC area, shared with 98NOTE machines;
    - A non-fatal "MEMORY ERROR" is always thrown no matter the RAM size afterwards, related?
    - unemulated conventional or EMS RAM bank, definitely should have one given the odd minimum RAM
      size;

    TODO: (PC-9801UX)
    - "I/O Error" on any 3.5" floppy, specific to this romset (i.e. those works on pc9821).
      It never access $4be, it must fail earlier than that.

===================================================================================================

    This series features a huge number of models released between 1982 and 1997. They
    were not IBM PC-compatible, but they had similar hardware (and software: in the
    1990s, they run MS Windows as OS)
    TODO: move this table in a markdown, upgrade

    Models:

                      |  CPU                          |   RAM    |            Drives                                     | CBus| Release |
    PC-9801           |  8086 @ 5                     |  128 KB  | -                                                     |  6  | 1982/10 |
    PC-9801F1         |  8086-2 @ 5/8                 |  128 KB  | 5"2DDx1                                               |  4  | 1983/10 |
    PC-9801F2         |  8086-2 @ 5/8                 |  128 KB  | 5"2DDx2                                               |  4  | 1983/10 |
    PC-9801E          |  8086-2 @ 5/8                 |  128 KB  | -                                                     |  6  | 1983/11 |
    PC-9801F3         |  8086-2 @ 5/8                 |  256 KB  | 5"2DDx1, 10M SASI HDD                                 |  2  | 1984/10 |
    PC-9801M2         |  8086-2 @ 5/8                 |  256 KB  | 5"2HDx2                                               |  4  | 1984/11 |
    PC-9801M3         |  8086-2 @ 5/8                 |  256 KB  | 5"2HDx1, 20M SASI HDD                                 |  3  | 1985/02 |
    PC-9801U2         |  V30 @ 8                      |  128 KB  | 3.5"2HDx2                                             |  2  | 1985/05 |
    PC-98XA1          |  80286 @ 8                    |  512 KB  | -                                                     |  6  | 1985/05 |
    PC-98XA2          |  80286 @ 8                    |  512 KB  | 5"2DD/2HDx2                                           |  6  | 1985/05 |
    PC-98XA3          |  80286 @ 8                    |  512 KB  | 5"2DD/2HDx1, 20M SASI HDD                             |  6  | 1985/05 |
    PC-9801VF2        |  V30 @ 8                      |  384 KB  | 5"2DDx2                                               |  4  | 1985/07 |
    PC-9801VM0        |  V30 @ 8/10                   |  384 KB  | -                                                     |  4  | 1985/07 |
    PC-9801VM2        |  V30 @ 8/10                   |  384 KB  | 5"2DD/2HDx2                                           |  4  | 1985/07 |
    PC-9801VM4        |  V30 @ 8/10                   |  384 KB  | 5"2DD/2HDx2, 20M SASI HDD                             |  4  | 1985/10 |
    PC-98XA11         |  80286 @ 8                    |  512 KB  | -                                                     |  6  | 1986/05 |
    PC-98XA21         |  80286 @ 8                    |  512 KB  | 5"2DD/2HDx2                                           |  6  | 1986/05 |
    PC-98XA31         |  80286 @ 8                    |  512 KB  | 5"2DD/2HDx1, 20M SASI HDD                             |  6  | 1986/05 |
    PC-9801UV2        |  V30 @ 8/10                   |  384 KB  | 3.5"2DD/2HDx2                                         |  2  | 1986/05 |
    PC-98LT1          |  V50 @ 8                      |  384 KB  | 3.5"2DD/2HDx1                                         |  0  | 1986/11 |
    PC-98LT2          |  V50 @ 8                      |  384 KB  | 3.5"2DD/2HDx1                                         |  0  | 1986/11 |
    PC-9801VM21       |  V30 @ 8/10                   |  640 KB  | 5"2DD/2HDx2                                           |  4  | 1986/11 |
    PC-9801VX0        |  80286 @ 8 & V30 @ 8/10       |  640 KB  | -                                                     |  4  | 1986/11 |
    PC-9801VX2        |  80286 @ 8 & V30 @ 8/10       |  640 KB  | 5"2DD/2HDx2                                           |  4  | 1986/11 |
    PC-9801VX4        |  80286 @ 8 & V30 @ 8/10       |  640 KB  | 5"2DD/2HDx2, 20M SASI HDD                             |  4  | 1986/11 |
    PC-9801VX4/WN     |  80286 @ 8 & V30 @ 8/10       |  640 KB  | 5"2DD/2HDx2, 20M SASI HDD                             |  4  | 1986/11 |
    PC-98XL1          |  80286 @ 8 & V30 @ 8/10       | 1152 KB  | -                                                     |  4  | 1986/12 |
    PC-98XL2          |  80286 @ 8 & V30 @ 8/10       | 1152 KB  | 5"2DD/2HDx2                                           |  4  | 1986/12 |
    PC-98XL4          |  80286 @ 8 & V30 @ 8/10       | 1152 KB  | 5"2DD/2HDx1, 20M SASI HDD                             |  4  | 1986/12 |
    PC-9801VX01       |  80286-10 @ 8/10 & V30 @ 8/10 |  640 KB  | -                                                     |  4  | 1987/06 |
    PC-9801VX21       |  80286-10 @ 8/10 & V30 @ 8/10 |  640 KB  | 5"2DD/2HDx2                                           |  4  | 1987/06 |
    PC-9801VX41       |  80286-10 @ 8/10 & V30 @ 8/10 |  640 KB  | 5"2DD/2HDx2, 20M SASI HDD                             |  4  | 1987/06 |
    PC-9801UV21       |  V30 @ 8/10                   |  640 KB  | 3.5"2DD/2HDx2                                         |  2  | 1987/06 |
    PC-98XL^2         |  i386DX-16 @ 16 & V30 @ 8     |  1.6 MB  | 5"2DD/2HDx2, 40M SASI HDD                             |  4  | 1987/10 |
    PC-98LT11         |  V50 @ 8                      |  640 KB  | 3.5"2DD/2HDx1                                         |  0  | 1987/10 |
    PC-98LT21         |  V50 @ 8                      |  640 KB  | 3.5"2DD/2HDx1                                         |  0  | 1987/10 |
    PC-9801UX21       |  80286-10 @ 10 & V30 @ 8      |  640 KB  | 3.5"2DD/2HDx2                                         |  3  | 1987/10 |
    PC-9801UX41       |  80286-10 @ 10 & V30 @ 8      |  640 KB  | 3.5"2DD/2HDx2, 20M SASI HDD                           |  3  | 1987/10 |
    PC-9801LV21       |  V30 @ 8/10                   |  640 KB  | 3.5"2DD/2HDx2                                         |  0  | 1988/03 |
    PC-9801CV21       |  V30 @ 8/10                   |  640 KB  | 3.5"2DD/2HDx2                                         |  2  | 1988/03 |
    PC-9801UV11       |  V30 @ 8/10                   |  640 KB  | 3.5"2DD/2HDx2                                         |  2  | 1988/03 |
    PC-9801RA2        |  i386DX-16 @ 16 & V30 @ 8     |  1.6 MB  | 5"2DD/2HDx2                                           |  4  | 1988/07 |
    PC-9801RA5        |  i386DX-16 @ 16 & V30 @ 8     |  1.6 MB  | 5"2DD/2HDx2, 40M SASI HDD                             |  4  | 1988/07 |
    PC-9801RX2        |  80286-12 @ 10/12 & V30 @ 8   |  640 KB  | 5"2DD/2HDx2                                           |  4  | 1988/07 |
    PC-9801RX4        |  80286-12 @ 10/12 & V30 @ 8   |  640 KB  | 5"2DD/2HDx2, 20M SASI HDD                             |  4  | 1988/07 |
    PC-98LT22         |  V50 @ 8                      |  640 KB  | 3.5"2DD/2HDx1                                         |  0  | 1988/11 |
    PC-98LS2          |  i386SX-16 @ 16 & V30 @ 8     |  1.6 MB  | 5"2DD/2HDx2                                           |  0  | 1988/11 |
    PC-98LS5          |  i386SX-16 @ 16 & V30 @ 8     |  1.6 MB  | 5"2DD/2HDx2, 40M SASI HDD                             |  0  | 1988/11 |
    PC-9801VM11       |  V30 @ 8/10                   |  640 KB  | 5"2DD/2HDx2                                           |  4  | 1988/11 |
    PC-9801LV22       |  V30 @ 8/10                   |  640 KB  | 3.5"2DD/2HDx2                                         |  0  | 1989/01 |
    PC-98RL2          |  i386DX-20 @ 16/20 & V30 @ 8  |  1.6 MB  | 5"2DD/2HDx2                                           |  4  | 1989/02 |
    PC-98RL5          |  i386DX-20 @ 16/20 & V30 @ 8  |  1.6 MB  | 5"2DD/2HDx2, 40M SASI HDD                             |  4  | 1989/02 |
    PC-9801EX2        |  80286-12 @ 10/12 & V30 @ 8   |  640 KB  | 3.5"2DD/2HDx2                                         |  3  | 1989/04 |
    PC-9801EX4        |  80286-12 @ 10/12 & V30 @ 8   |  640 KB  | 3.5"2DD/2HDx2, 20M SASI HDD                           |  3  | 1989/04 |
    PC-9801ES2        |  i386SX-16 @ 16 & V30 @ 8     |  1.6 MB  | 3.5"2DD/2HDx2                                         |  3  | 1989/04 |
    PC-9801ES5        |  i386SX-16 @ 16 & V30 @ 8     |  1.6 MB  | 3.5"2DD/2HDx2, 40M SASI HDD                           |  3  | 1989/04 |
    PC-9801LX2        |  80286-12 @ 10/12 & V30 @ 8   |  640 KB  | 3.5"2DD/2HDx2                                         |  0  | 1989/04 |
    PC-9801LX4        |  80286-12 @ 10/12 & V30 @ 8   |  640 KB  | 3.5"2DD/2HDx2, 20M SASI HDD                           |  0  | 1989/04 |
    PC-9801LX5        |  80286-12 @ 10/12 & V30 @ 8   |  640 KB  | 3.5"2DD/2HDx2, 40M SASI HDD                           |  0  | 1989/06 |
    PC-98DO           |  V30 @ 8/10                   |  640 KB  | 5"2DD/2HDx2                                           |  1  | 1989/06 |
    PC-9801LX5C       |  80286-12 @ 10/12 & V30 @ 8   |  640 KB  | 3.5"2DD/2HDx2, 40M SASI HDD                           |  0  | 1989/06 |
    PC-9801RX21       |  80286-12 @ 10/12 & V30 @ 8   |  640 KB  | 5"2DD/2HDx2                                           |  4  | 1989/10 |
    PC-9801RX51       |  80286-12 @ 10/12 & V30 @ 8   |  640 KB  | 5"2DD/2HDx2, 40M SASI HDD                             |  4  | 1989/10 |
    PC-9801RA21       |  i386DX-20 @ 16/20 & V30 @ 8  |  1.6 MB  | 5"2DD/2HDx2                                           |  4  | 1989/11 |
    PC-9801RA51       |  i386DX-20 @ 16/20 & V30 @ 8  |  1.6 MB  | 5"2DD/2HDx2, 40M SASI HDD                             |  4  | 1989/11 |
    PC-9801RS21       |  i386SX-16 @ 16 & V30 @ 8     |  640 KB  | 5"2DD/2HDx2                                           |  4  | 1989/11 |
    PC-9801RS51       |  i386SX-16 @ 16 & V30 @ 8     |  640 KB  | 5"2DD/2HDx2, 40M SASI HDD                             |  4  | 1989/11 |
    PC-9801N          |  V30 @ 10                     |  640 KB  | 3.5"2DD/2HDx1                                         |  0  | 1989/11 |
    PC-9801TW2        |  i386SX-20 @ 20 & V30 @ 8     |  640 KB  | 3.5"2DD/2HDx2                                         |  2  | 1990/02 |
    PC-9801TW5        |  i386SX-20 @ 20 & V30 @ 8     |  1.6 MB  | 3.5"2DD/2HDx2, 40M SASI HDD                           |  2  | 1990/02 |
    PC-9801TS5        |  i386SX-20 @ 20 & V30 @ 8     |  1.6 MB  | 3.5"2DD/2HDx2, 40M SASI HDD                           |  2  | 1990/06 |
    PC-9801NS         |  i386SX-12 @ 12               |  1.6 MB  | 3.5"2DD/2HDx1                                         |  0  | 1990/06 |
    PC-9801TF5        |  i386SX-20 @ 20 & V30 @ 8     |  1.6 MB  | 3.5"2DD/2HDx2, 40M SASI HDD                           |  2  | 1990/07 |
    PC-9801NS-20      |  i386SX-12 @ 12               |  1.6 MB  | 3.5"2DD/2HDx1, 20M SASI HDD                           |  0  | 1990/09 |
    PC-98RL21         |  i386DX-20 @ 20 & V30 @ 8     |  1.6 MB  | 5"2DD/2HDx2                                           |  4  | 1990/09 |
    PC-98RL51         |  i386DX-20 @ 20 & V30 @ 8     |  1.6 MB  | 5"2DD/2HDx1, 40M SASI HDD                             |  4  | 1990/09 |
    PC-98DO+          |  V33A @ 8/16                  |  640 KB  | 5"2DD/2HDx2                                           |  1  | 1990/10 |
    PC-9801DX2        |  80286-12 @ 10/12 & V30 @ 8   |  640 KB  | 5"2DD/2HDx2                                           |  4  | 1990/11 |
    PC-9801DX/U2      |  80286-12 @ 10/12 & V30 @ 8   |  640 KB  | 3.5"2DD/2HDx2                                         |  4  | 1990/11 |
    PC-9801DX5        |  80286-12 @ 10/12 & V30 @ 8   |  640 KB  | 5"2DD/2HDx2, 40M SASI HDD                             |  4  | 1990/11 |
    PC-9801DX/U5      |  80286-12 @ 10/12 & V30 @ 8   |  640 KB  | 3.5"2DD/2HDx2, 40M SASI HDD                           |  4  | 1990/11 |
    PC-9801NV         |  V30HL @ 8/16                 |  1.6 MB  | 3.5"2DD/2HDx1                                         |  0  | 1990/11 |
    PC-9801DS2        |  i386SX-16 @ 16 & V30 @ 8     |  640 KB  | 5"2DD/2HDx2                                           |  4  | 1991/01 |
    PC-9801DS/U2      |  i386SX-16 @ 16 & V30 @ 8     |  640 KB  | 3.5"2DD/2HDx2                                         |  4  | 1991/01 |
    PC-9801DS5        |  i386SX-16 @ 16 & V30 @ 8     |  640 KB  | 5"2DD/2HDx2, 40M SASI HDD                             |  4  | 1991/01 |
    PC-9801DS/U5      |  i386SX-16 @ 16 & V30 @ 8     |  640 KB  | 3.5"2DD/2HDx2, 40M SASI HDD                           |  4  | 1991/01 |
    PC-9801DA2        |  i386DX-20 @ 16/20 & V30 @ 8  |  1.6 MB  | 5"2DD/2HDx2                                           |  4  | 1991/01 |
    PC-9801DA/U2      |  80286-12 @ 10/12 & V30 @ 8   |  640 KB  | 3.5"2DD/2HDx2                                         |  4  | 1991/01 |
    PC-9801DA5        |  80286-12 @ 10/12 & V30 @ 8   |  640 KB  | 5"2DD/2HDx2, 40M SASI HDD                             |  4  | 1991/01 |
    PC-9801DA/U5      |  80286-12 @ 10/12 & V30 @ 8   |  640 KB  | 3.5"2DD/2HDx2, 40M SASI HDD                           |  4  | 1991/01 |
    PC-9801DA7        |  80286-12 @ 10/12 & V30 @ 8   |  640 KB  | 5"2DD/2HDx2, 100M SCSI HDD                            |  4  | 1991/02 |
    PC-9801DA/U7      |  80286-12 @ 10/12 & V30 @ 8   |  640 KB  | 3.5"2DD/2HDx2, 100M SCSI HDD                          |  4  | 1991/02 |
    PC-9801UF         |  V30 @ 8/16                   |  640 KB  | 3.5"2DD/2HDx2                                         |  2  | 1991/02 |
    PC-9801UR         |  V30 @ 8/16                   |  640 KB  | 3.5"2DD/2HDx1, 1.25MB RAM Disk                        |  2  | 1991/02 |
    PC-9801UR/20      |  V30 @ 8/16                   |  640 KB  | 3.5"2DD/2HDx1, 1.25MB RAM Disk, 20M SASI HDD          |  2  | 1991/02 |
    PC-9801NS/E       |  i386SX-16 @ 16               |  1.6 MB  | 3.5"2DD/2HDx1, 1.25MB RAM Disk                        |  0  | 1991/06 |
    PC-9801NS/E20     |  i386SX-16 @ 16               |  1.6 MB  | 3.5"2DD/2HDx1, 1.25MB RAM Disk, 20M SASI HDD          |  0  | 1991/06 |
    PC-9801NS/E40     |  i386SX-16 @ 16               |  1.6 MB  | 3.5"2DD/2HDx1, 1.25MB RAM Disk, 40M SASI HDD          |  0  | 1991/06 |
    PC-9801TW7        |  i386SX-20 @ 20 & V30 @ 8     |  1.6 MB  | 3.5"2DD/2HDx2, 100M SCSI HDD                          |  2  | 1991/07 |
    PC-9801TF51       |  i386SX-20 @ 20 & V30 @ 8     |  1.6 MB  | 3.5"2DD/2HDx2, 40M SASI HDD                           |  2  | 1991/07 |
    PC-9801TF71       |  i386SX-20 @ 20 & V30 @ 8     |  1.6 MB  | 3.5"2DD/2HDx2, 100M SCSI HDD                          |  2  | 1991/07 |
    PC-9801NC         |  i386SX-20 @ 20               |  2.6 MB  | 3.5"2DD/2HDx1, 1.25MB RAM Disk                        |  0  | 1991/10 |
    PC-9801NC40       |  i386SX-20 @ 20               |  2.6 MB  | 3.5"2DD/2HDx1, 1.25MB RAM Disk, 40M SASI HDD          |  0  | 1991/10 |
    PC-9801CS2        |  i386SX-16 @ 16               |  640 KB  | 3.5"2DD/2HDx2                                         |  2  | 1991/10 |
    PC-9801CS5        |  i386SX-16 @ 16               |  640 KB  | 3.5"2DD/2HDx2, 40M SASI HDD                           |  2  | 1991/10 |
    PC-9801CS5/W      |  i386SX-16 @ 16               |  3.6 MB  | 3.5"2DD/2HDx2, 40M SASI HDD                           |  2  | 1991/11 |
    PC-98GS1          |  i386SX-20 @ 20 & V30 @ 8     |  2.6 MB  | 3.5"2DD/2HDx2, 40M SASI HDD                           |  3  | 1991/11 |
    PC-98GS2          |  i386SX-20 @ 20 & V30 @ 8     |  2.6 MB  | 3.5"2DD/2HDx2, 40M SASI HDD, 1xCD-ROM                 |  3  | 1991/11 |
    PC-9801FA2        |  i486SX-16 @ 16               |  1.6 MB  | 5"2DD/2HDx2                                           |  4  | 1992/01 |
    PC-9801FA/U2      |  i486SX-16 @ 16               |  1.6 MB  | 3.5"2DD/2HDx2                                         |  4  | 1992/01 |
    PC-9801FA5        |  i486SX-16 @ 16               |  1.6 MB  | 5"2DD/2HDx2, 40M SCSI HDD                             |  4  | 1992/01 |
    PC-9801FA/U5      |  i486SX-16 @ 16               |  1.6 MB  | 3.5"2DD/2HDx2, 40M SCSI HDD                           |  4  | 1992/01 |
    PC-9801FA7        |  i486SX-16 @ 16               |  1.6 MB  | 5"2DD/2HDx2, 100M SCSI HDD                            |  4  | 1992/01 |
    PC-9801FA/U7      |  i486SX-16 @ 16               |  1.6 MB  | 3.5"2DD/2HDx2, 100M SCSI HDD                          |  4  | 1992/01 |
    PC-9801FS2        |  i386SX-20 @ 16/20            |  1.6 MB  | 5"2DD/2HDx2                                           |  4  | 1992/05 |
    PC-9801FS/U2      |  i386SX-20 @ 16/20            |  1.6 MB  | 3.5"2DD/2HDx2                                         |  4  | 1992/05 |
    PC-9801FS5        |  i386SX-20 @ 16/20            |  1.6 MB  | 5"2DD/2HDx2, 40M SCSI HDD                             |  4  | 1992/05 |
    PC-9801FS/U5      |  i386SX-20 @ 16/20            |  1.6 MB  | 3.5"2DD/2HDx2, 40M SCSI HDD                           |  4  | 1992/05 |
    PC-9801FS7        |  i386SX-20 @ 16/20            |  1.6 MB  | 5"2DD/2HDx2, 100M SCSI HDD                            |  4  | 1992/01 |
    PC-9801FS/U7      |  i386SX-20 @ 16/20            |  1.6 MB  | 3.5"2DD/2HDx2, 100M SCSI HDD                          |  4  | 1992/01 |
    PC-9801NS/T       |  i386SL(98) @ 20              |  1.6 MB  | 3.5"2DD/2HDx1, 1.25MB RAM Disk                        |  0  | 1992/01 |
    PC-9801NS/T40     |  i386SL(98) @ 20              |  1.6 MB  | 3.5"2DD/2HDx1, 1.25MB RAM Disk, 40M SASI HDD          |  0  | 1992/01 |
    PC-9801NS/T80     |  i386SL(98) @ 20              |  1.6 MB  | 3.5"2DD/2HDx1, 1.25MB RAM Disk, 80M SASI HDD          |  0  | 1992/01 |
    PC-9801NL         |  V30H @ 8/16                  |  640 KB  | 1.25 MB RAM Disk                                      |  0  | 1992/01 |
    PC-9801FX2        |  i386SX-12 @ 10/12            |  1.6 MB  | 5"2DD/2HDx2                                           |  4  | 1992/05 |
    PC-9801FX/U2      |  i386SX-12 @ 10/12            |  1.6 MB  | 3.5"2DD/2HDx2                                         |  4  | 1992/05 |
    PC-9801FX5        |  i386SX-12 @ 10/12            |  1.6 MB  | 5"2DD/2HDx2, 40M SCSI HDD                             |  4  | 1992/05 |
    PC-9801FX/U5      |  i386SX-12 @ 10/12            |  1.6 MB  | 3.5"2DD/2HDx2, 40M SCSI HDD                           |  4  | 1992/05 |
    PC-9801US         |  i386SX-16 @ 16               |  1.6 MB  | 3.5"2DD/2HDx2                                         |  2  | 1992/07 |
    PC-9801US40       |  i386SX-16 @ 16               |  1.6 MB  | 3.5"2DD/2HDx2, 40M SASI HDD                           |  2  | 1992/07 |
    PC-9801US80       |  i386SX-16 @ 16               |  1.6 MB  | 3.5"2DD/2HDx2, 80M SASI HDD                           |  2  | 1992/07 |
    PC-9801NS/L       |  i386SX-20 @ 10/20            |  1.6 MB  | 3.5"2DD/2HDx1, 1.25MB RAM Disk                        |  0  | 1992/07 |
    PC-9801NS/L40     |  i386SX-20 @ 10/20            |  1.6 MB  | 3.5"2DD/2HDx1, 1.25MB RAM Disk, 40M SASI HDD          |  0  | 1992/07 |
    PC-9801NA         |  i486SX-20 @ 20               |  2.6 MB  | 3.5"2DD/2HDx1, 1.25MB RAM Disk                        |  0  | 1992/11 |
    PC-9801NA40       |  i486SX-20 @ 20               |  2.6 MB  | 3.5"2DD/2HDx1, 1.25MB RAM Disk, 40M SASI HDD          |  0  | 1992/11 |
    PC-9801NA120      |  i486SX-20 @ 20               |  2.6 MB  | 3.5"2DD/2HDx1, 1.25MB RAM Disk, 120M SASI HDD         |  0  | 1992/11 |
    PC-9801NA/C       |  i486SX-20 @ 20               |  2.6 MB  | 3.5"2DD/2HDx1, 1.25MB RAM Disk                        |  0  | 1992/11 |
    PC-9801NA40/C     |  i486SX-20 @ 20               |  2.6 MB  | 3.5"2DD/2HDx1, 1.25MB RAM Disk, 40M SASI HDD          |  0  | 1992/11 |
    PC-9801NA120/C    |  i486SX-20 @ 20               |  2.6 MB  | 3.5"2DD/2HDx1, 1.25MB RAM Disk, 120M SASI HDD         |  0  | 1992/11 |
    PC-9801NS/R       |  i486SX(J) @ 20               |  1.6 MB  | 3.5"2DD/2HDx1 (3mode), 1.25MB RAM Disk                |  0  | 1993/01 |
    PC-9801NS/R40     |  i486SX(J) @ 20               |  1.6 MB  | 3.5"2DD/2HDx1 (3mode), 1.25MB RAM Disk, 40M SASI HDD  |  0  | 1993/01 |
    PC-9801NS/R120    |  i486SX(J) @ 20               |  1.6 MB  | 3.5"2DD/2HDx1 (3mode), 1.25MB RAM Disk, 120M SASI HDD |  0  | 1993/01 |
    PC-9801BA/U2      |  i486DX2-40 @ 40              |  1.6 MB  | 3.5"2DD/2HDx2                                         |  3  | 1993/01 |
    PC-9801BA/U6      |  i486DX2-40 @ 40              |  3.6 MB  | 3.5"2DD/2HDx1, 40M SASI HDD                           |  3  | 1993/01 |
    PC-9801BA/M2      |  i486DX2-40 @ 40              |  1.6 MB  | 5"2DD/2HDx2                                           |  3  | 1993/01 |
    PC-9801BX/U2      |  i486SX-20 @ 20               |  1.6 MB  | 3.5"2DD/2HDx2                                         |  3  | 1993/01 |
    PC-9801BX/U6      |  i486SX-20 @ 20               |  3.6 MB  | 3.5"2DD/2HDx1, 40M SASI HDD                           |  3  | 1993/01 |
    PC-9801BX/M2      |  i486SX-20 @ 20               |  1.6 MB  | 5"2DD/2HDx2                                           |  3  | 1993/01 |
    PC-9801NX/C       |  i486SX(J) @ 20               |  1.6 MB  | 3.5"2DD/2HDx1, 1.25MB RAM Disk                        |  0  | 1993/07 |
    PC-9801NX/C120    |  i486SX(J) @ 20               |  3.6 MB  | 3.5"2DD/2HDx1, 1.25MB RAM Disk                        |  0  | 1993/07 |
    PC-9801P40/D      |  i486SX(J) @ 20               |  5.6 MB  | 40MB IDE HDD                                          |  0  | 1993/07 |
    PC-9801P80/W      |  i486SX(J) @ 20               |  7.6 MB  | 80MB IDE HDD                                          |  0  | 1993/07 |
    PC-9801P80/P      |  i486SX(J) @ 20               |  7.6 MB  | 80MB IDE HDD                                          |  0  | 1993/07 |
    PC-9801BA2/U2     |  i486DX2-66 @ 66              |  3.6 MB  | 3.5"2DD/2HDx2                                         |  3  | 1993/11 |
    PC-9801BA2/U7     |  i486DX2-66 @ 66              |  3.6 MB  | 3.5"2DD/2HDx1, 120MB IDE HDD                          |  3  | 1993/11 |
    PC-9801BA2/M2     |  i486DX2-66 @ 66              |  3.6 MB  | 5"2DD/2HDx2                                           |  3  | 1993/11 |
    PC-9801BS2/U2     |  i486SX-33 @ 33               |  3.6 MB  | 3.5"2DD/2HDx2                                         |  3  | 1993/11 |
    PC-9801BS2/U7     |  i486SX-33 @ 33               |  3.6 MB  | 3.5"2DD/2HDx1, 120MB IDE HDD                          |  3  | 1993/11 |
    PC-9801BS2/M2     |  i486SX-33 @ 33               |  3.6 MB  | 5"2DD/2HDx2                                           |  3  | 1993/11 |
    PC-9801BX2/U2     |  i486SX-25 @ 25               |  1.8 MB  | 3.5"2DD/2HDx2                                         |  3  | 1993/11 |
    PC-9801BX2/U7     |  i486SX-25 @ 25               |  3.6 MB  | 3.5"2DD/2HDx1, 120MB IDE HDD                          |  3  | 1993/11 |
    PC-9801BX2/M2     |  i486SX-25 @ 25               |  1.8 MB  | 5"2DD/2HDx2                                           |  3  | 1993/11 |
    PC-9801BA3/U2     |  i486DX-66 @ 66               |  3.6 MB  | 3.5"2DD/2HDx2                                         |  3  | 1995/01 |
    PC-9801BA3/U2/W   |  i486DX-66 @ 66               |  7.6 MB  | 3.5"2DD/2HDx2, 210MB IDE HDD                          |  3  | 1995/01 |
    PC-9801BX3/U2     |  i486SX-33 @ 33               |  1.6 MB  | 3.5"2DD/2HDx2                                         |  3  | 1995/01 |
    PC-9801BX3/U2/W   |  i486SX-33 @ 33               |  5.6 MB  | 3.5"2DD/2HDx2, 210MB IDE HDD                          |  3  | 1995/01 |
    PC-9801BX4/U2     |  AMD/i 486DX2-66 @ 66         |  2 MB    | 3.5"2DD/2HDx2                                         |  3  | 1995/07 |
    PC-9801BX4/U2/C   |  AMD/i 486DX2-66 @ 66         |  2 MB    | 3.5"2DD/2HDx2, 2xCD-ROM                               |  3  | 1995/07 |
    PC-9801BX4/U2-P   |  Pentium ODP @ 66             |  2 MB    | 3.5"2DD/2HDx2                                         |  3  | 1995/09 |
    PC-9801BX4/U2/C-P |  Pentium ODP @ 66             |  2 MB    | 3.5"2DD/2HDx2, 2xCD-ROM                               |  3  | 1995/09 |

    For more info (e.g. optional hardware), see http://www.geocities.jp/retro_zzz/machines/nec/9801/mdl98cpu.html

    PC-9821 Series

    PC-9821 (1992) - aka 98MULTi, desktop computer, 386 based
    PC-9821A series (1993->1994) - aka 98MATE A, desktop computers, 486 based
    PC-9821B series (1993) - aka 98MATE B, desktop computers, 486 based
    PC-9821C series (1993->1996) - aka 98MULTi CanBe, desktop & tower computers, various CPU
    PC-9821Es (1994) - aka 98FINE, desktop computer with integrated LCD, successor of the PC-98T
    PC-9821X series (1994->1995) - aka 98MATE X, desktop computers, Pentium based
    PC-9821V series (1995) - aka 98MATE Valuestar, desktop computers, Pentium based
    PC-9821S series (1995->1996) - aka 98Pro, tower computers, PentiumPro based
    PC-9821R series (1996->2000) - aka 98MATE R, desktop & tower & server computers, various CPU
    PC-9821C200 (1997) - aka CEREB, desktop computer, Pentium MMX based
    PC-9821 Ne/Ns/Np/Nm (1993->1995) - aka 98NOTE, laptops, 486 based
    PC-9821 Na/Nb/Nw (1995->1997) - aka 98NOTE Lavie, laptops, Pentium based
    PC-9821 Lt/Ld (1995) - aka 98NOTE Light, laptops, 486 based
    PC-9821 La/Ls (1995->1997) - aka 98NOTE Aile, laptops, Pentium based

====

Documentation notes (for unemulated stuff, courtesy of T. Kodaka and T. Kono):

IDE:
(r/w)
0x430: IDE drive switch
0x432: IDE drive switch
0x435: <unknown>

                                                    (ISA correlated i/o)
----------------------------------------------------------
0x0640      |WORD|R/W|Data Register                |01F0h
0x0642      |BYTE| R |Error Register               |01F1h
0x0642      |BYTE| W |Write Precomp Register       |01F1h
0x0644      |BYTE|R/W|Sector Count                 |01F2h
0x0646      |BYTE|R/W|Sector Number                |01F3h
0x0648      |BYTE|R/W|Cylinder Low                 |01F4h
0x064A      |BYTE|R/W|Cylinder High                |01F5h
0x064C      |BYTE|R/W|SDH Register                 |01F6h
0x064E      |BYTE| R |Status Register              |01F7h
0x064E      |BYTE| W |Command Register             |01F7h
0x074C      |BYTE| R |Alternate Status Register    |03F6h
0x074C      |BYTE| W |Digital Output Register      |03F6h
0x074E      |BYTE| R |Digital Input Register       |03F7h

Video F/F (i/o 0x68):
KAC mode (video ff = 5) is basically how the kanji ROM could be accessed, 1=thru the CG window ports, 0=thru the kanji
window RAM at 0xa4***.
My guess is that the system locks up or doesn't have any data if the wrong port is being accessed.

Ext Video F/F (i/o 0x6a):
0000 011x enables EGC
0000 111x enables PC-98GS
0010 000x enables multicolor (a.k.a. 256 colors mode)
0010 001x enables 65'536 colors
0010 010x 64k color palette related (?)
0010 011x full screen reverse (?)
0010 100x text and gfxs synthesis (?)
0010 101x 256 color palette registers fast write (?)
0010 110x 256 color overscan (?)
0100 000x (0) CRT (1) Plasma/LCD
0100 001x text and gfxs right shifted one dot (undocumented behaviour)
0100 010x hi-res mode in PC-9821
0110 000x EEGC mode
0110 001x VRAM config (0) plain (1) packed
0110 011x AGDC mode
0110 100x 480 lines
0110 110x VRAM bitmap orientation (0) MSB left-to-right LSB (1) LSB left-to-right MSB
1000 001x CHR GDC clock (0) 2,5 MHz (1) 5 MHz
1000 010x BMP GDC clock
1000 111x related to GFX accelerator cards (like Vision864)
1100 010x chart GDC operating mode (?)
(everything else is undocumented / unknown)

**************************************************************************************************/

#include "emu.h"
#include "pc9801.h"

#include "machine/input_merger.h"

void pc98_base_state::rtc_w(uint8_t data)
{
	m_rtc->c0_w(BIT(data, 0));
	m_rtc->c1_w(BIT(data, 1));
	m_rtc->c2_w(BIT(data, 2));
	m_rtc->stb_w(BIT(data, 3));
	m_rtc->clk_w(BIT(data, 4));
	m_rtc->data_in_w(BIT(data, 5));
	if(data & 0xc0)
		logerror("RTC write to undefined bits %02x\n",data & 0xc0);
}

void pc9801_state::dmapg4_w(offs_t offset, uint8_t data)
{
	if(offset < 4)
		m_dma_offset[(offset+1) & 3] = data & 0x0f;
}

void pc9801vm_state::dmapg8_w(offs_t offset, uint8_t data)
{
	if(offset == 4)
		m_dma_autoinc[data & 3] = (data >> 2) & 3;
	else if(offset < 4)
		m_dma_offset[(offset+1) & 3] = data;
}

void pc9801_state::nmi_ctrl_w(offs_t offset, uint8_t data)
{
	m_nmi_ff = offset;
}

void pc9801_state::vrtc_clear_w(uint8_t data)
{
	m_pic1->ir2_w(0);
}

u8 pc9801_state::fdc_2hd_ctrl_r()
{
	return 0x44;
}

void pc9801_state::fdc_2hd_ctrl_w(u8 data)
{
	//logerror("%02x ctrl\n",data);
	m_fdc_2hd->reset_w(BIT(data, 7));

	m_fdc_2hd_ctrl = data;
	if(data & 0x40)
	{
		m_fdc_2hd->set_ready_line_connected(0);
		m_fdc_2hd->ready_w(0);
	}
	else
		m_fdc_2hd->set_ready_line_connected(1);

	if(!m_sys_type) // required for 9801f 2hd adapter bios
	{
		m_fdc_2hd->subdevice<floppy_connector>("0")->get_device()->mon_w(data & 8 ? ASSERT_LINE : CLEAR_LINE);
		m_fdc_2hd->subdevice<floppy_connector>("1")->get_device()->mon_w(data & 8 ? ASSERT_LINE : CLEAR_LINE);
	}
}

bool pc9801_state::fdc_drive_ready_r(upd765a_device *fdc)
{
	floppy_image_device *floppy0 = fdc->subdevice<floppy_connector>("0")->get_device();
	floppy_image_device *floppy1 = fdc->subdevice<floppy_connector>("1")->get_device();

	return (!floppy0->ready_r() || !floppy1->ready_r());
}

uint8_t pc9801_state::fdc_2dd_ctrl_r()
{
	u8 ret = 0;

	// 2dd BIOS specifically tests if a disk is in any drive
	// (does not happen on 2HD standalone)
	ret |= fdc_drive_ready_r(m_fdc_2dd) << 4;

	//popmessage("%d %d %02x", floppy0->ready_r(), floppy1->ready_r(), ret);

	// TODO: dips et al.
	return ret | 0x40;
}

void pc9801_state::fdc_2dd_ctrl_w(uint8_t data)
{
	logerror("%02x ctrl\n",data);
	m_fdc_2dd->reset_w(BIT(data, 7));

	m_fdc_2dd_ctrl = data;
	m_fdc_2dd->subdevice<floppy_connector>("0")->get_device()->mon_w(data & 8 ? CLEAR_LINE : ASSERT_LINE);
	m_fdc_2dd->subdevice<floppy_connector>("1")->get_device()->mon_w(data & 8 ? CLEAR_LINE : ASSERT_LINE);
}

u8 pc9801vm_state::ide_ctrl_hack_r()
{
	if (!machine().side_effects_disabled())
	{
		// HACK: RS IDE driver will try to do 512 to 256 byte sector translations
		// MEMSW has no setting for this, is it concealed?
		// SDIP based machines don't need this (they will default to 512 bps, shadowed from
		// gaiji $ac403 bit 6).
		address_space &ram = m_maincpu->space(AS_PROGRAM);
		ram.write_byte(0x457, ram.read_byte(0x457) | 0xc0);
	}
	return m_ide_sel;
}

u8 pc9801vm_state::ide_ctrl_r()
{
	return m_ide_sel;
}

void pc9801vm_state::ide_ctrl_w(u8 data)
{
	if(!(data & 0x80))
		m_ide_sel = data & 1;
}

uint16_t pc9801vm_state::ide_cs0_r(offs_t offset, uint16_t mem_mask)
{
	return m_ide[m_ide_sel]->cs0_r(offset, mem_mask);
}

void pc9801vm_state::ide_cs0_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	m_ide[m_ide_sel]->cs0_w(offset, data, mem_mask);
}

uint16_t pc9801vm_state::ide_cs1_r(offs_t offset, uint16_t mem_mask)
{
	return m_ide[m_ide_sel]->cs1_r(offset, mem_mask);
}

void pc9801vm_state::ide_cs1_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	m_ide[m_ide_sel]->cs1_w(offset, data, mem_mask);
}

uint8_t pc9801_state::sasi_data_r()
{
	uint8_t data = m_sasi_data_in->read();

	if(m_sasi_ctrl_in->read() & 0x80)
		m_sasibus->write_ack(1);
	return data;
}

void pc9801_state::sasi_data_w(uint8_t data)
{
	m_sasi_data = data;

	if (m_sasi_data_enable)
	{
		m_sasi_data_out->write(m_sasi_data);
		if(m_sasi_ctrl_in->read() & 0x80)
			m_sasibus->write_ack(1);
	}
}

void pc9801_state::write_sasi_io(int state)
{
	m_sasi_ctrl_in->write_bit2(state);

	m_sasi_data_enable = !state;

	if (m_sasi_data_enable)
	{
		m_sasi_data_out->write(m_sasi_data);
	}
	else
	{
		m_sasi_data_out->write(0);
	}
	if((m_sasi_ctrl_in->read() & 0x9c) == 0x8c)
		m_pic2->ir1_w(m_sasi_ctrl & 1);
	else
		m_pic2->ir1_w(0);
}

void pc9801_state::write_sasi_req(int state)
{
	m_sasi_ctrl_in->write_bit7(state);

	if (!state)
		m_sasibus->write_ack(0);

	if((m_sasi_ctrl_in->read() & 0x9C) == 0x8C)
		m_pic2->ir1_w(m_sasi_ctrl & 1);
	else
		m_pic2->ir1_w(0);

	m_dmac->dreq0_w(!(state && !(m_sasi_ctrl_in->read() & 8) && (m_sasi_ctrl & 2)));
}


uint8_t pc9801_state::sasi_status_r()
{
	uint8_t res = 0;

	if(m_sasi_ctrl & 0x40) // read status
	{
	/*
	    x--- ---- REQ
	    -x-- ---- ACK
	    --x- ---- BSY
	    ---x ---- MSG
	    ---- x--- CD
	    ---- -x-- IO
	    ---- ---x INT?
	*/
		res |= m_sasi_ctrl_in->read();
	}
	else // read drive info
	{
/*
        xx-- ---- unknown but tested
        --xx x--- SASI-1 media type
        ---- -xxx SASI-2 media type
*/
		//res |= 7 << 3; // read mediatype SASI-1
		//res |= 7;   // read mediatype SASI-2
	}
	return res;
}

void pc9801_state::sasi_ctrl_w(uint8_t data)
{
	/*
	    x--- ---- channel enable
	    -x-- ---- read switch
	    --x- ---- sel
	    ---- x--- reset line
	    ---- --x- dma enable
	    ---- ---x irq enable
	*/

	m_sasibus->write_sel(BIT(data, 5));

	if(m_sasi_ctrl & 8 && ((data & 8) == 0)) // 1 -> 0 transition
	{
		m_sasibus->write_rst(1);
//      m_timer_rst->adjust(attotime::from_nsec(100));
	}
	else
		m_sasibus->write_rst(0); // TODO

	m_sasi_ctrl = data;

//  m_sasibus->write_sel(BIT(data, 0));
}

uint8_t pc9801_state::f0_r(offs_t offset)
{
	if(offset == 0)
	{
		// iterate thru all devices to check if an AMD98 is present
		// TODO: move to cbus
		// TODO: is this really part of PC-98 spec or it's coming from the device itself, as dip/jumper?
		for (amd98_device &amd98 : device_type_enumerator<amd98_device>(machine().root_device()))
		{
			logerror("%s: Read AMD98 ID %s\n", machine().describe_context(), amd98.tag());
			return 0x18; // return the right ID
		}

		logerror("%s: Read port 0 from 0xf0 (AMD98 check?)\n", machine().describe_context());
		return 0; // card not present
	}

	return 0xff;
}

void pc9801_state::pc9801_map(address_map &map)
{
	map(0xa0000, 0xa3fff).rw(FUNC(pc9801_state::tvram_r), FUNC(pc9801_state::tvram_w)); //TVRAM
	map(0xa8000, 0xbffff).rw(FUNC(pc9801_state::gvram_r), FUNC(pc9801_state::gvram_w)); //bitmap VRAM
//  map(0xcc000, 0xcffff).rom().region("sound_bios", 0); //sound BIOS
	map(0xd6000, 0xd6fff).rom().region("fdc_bios_2dd", 0); //floppy BIOS 2dd
	map(0xd7000, 0xd7fff).rom().region("fdc_bios_2hd", 0); //floppy BIOS 2hd
	map(0xe8000, 0xfffff).rom().region("ipl", 0);
}

/* first device is even offsets, second one is odd offsets */
void pc9801_state::pc9801_common_io(address_map &map)
{
//  map.unmap_value_high();
	map(0x0000, 0x001f).rw(m_dmac, FUNC(am9517a_device::read), FUNC(am9517a_device::write)).umask16(0xff00);
	map(0x0000, 0x001f).rw(FUNC(pc9801_state::pic_r), FUNC(pc9801_state::pic_w)).umask16(0x00ff); // i8259 PIC (bit 3 ON slave / master) / i8237 DMA
	map(0x0020, 0x0020).w(FUNC(pc9801_state::rtc_w));
	map(0x0030, 0x0037).rw(m_ppi_sys, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0xff00);
	map(0x0030, 0x0033).rw(m_sio_rs, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff); //i8251 RS232c / i8255 system port
	map(0x0040, 0x0047).rw(m_ppi_prn, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
	map(0x0040, 0x0043).rw(m_sio_kbd, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0xff00); //i8255 printer port / i8251 keyboard
	map(0x0050, 0x0057).lr8(NAME([] (offs_t offset) { return 0xff; })).umask16(0xff00);
	map(0x0050, 0x0053).w(FUNC(pc9801_state::nmi_ctrl_w)).umask16(0x00ff); // NMI FF / host FDD 2d (PC-80S31K)
	map(0x0060, 0x0063).rw(m_hgdc[0], FUNC(upd7220_device::read), FUNC(upd7220_device::write)).umask16(0x00ff); //upd7220 character ports / <undefined>
	map(0x0064, 0x0064).w(FUNC(pc9801_state::vrtc_clear_w));
//  map(0x006c, 0x006f) border color / <undefined>
	map(0x006c, 0x006f).w(FUNC(pc9801_state::border_color_w)).umask16(0x00ff);
	// TODO: PC-98Bible suggests that $73 timer #1 is unavailable on non-vanilla models (verify on HW)
	// (can be accessed only thru the $3fdb alias)
	map(0x0070, 0x0077).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0xff00);
	map(0x0070, 0x007f).rw(FUNC(pc9801_state::txt_scrl_r), FUNC(pc9801_state::txt_scrl_w)).umask16(0x00ff); //display registers / i8253 pit
//  map(0x0090, 0x0093).rw(m_sio_cmt, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0xff00); // CMT SIO (optional, C-Bus)
	map(0x7fd8, 0x7fdf).rw(m_ppi_mouse, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0xff00);
}

void pc9801_state::pc9801_io(address_map &map)
{
	pc9801_common_io(map);
	map(0x0020, 0x002f).w(FUNC(pc9801_state::dmapg4_w)).umask16(0xff00);
	map(0x0068, 0x0068).w(FUNC(pc9801_state::pc9801_video_ff_w)); //mode FF / <undefined>
	map(0x0080, 0x0080).rw(FUNC(pc9801_state::sasi_data_r), FUNC(pc9801_state::sasi_data_w));
	map(0x0082, 0x0082).rw(FUNC(pc9801_state::sasi_status_r), FUNC(pc9801_state::sasi_ctrl_w));
	map(0x0090, 0x0090).r(m_fdc_2hd, FUNC(upd765a_device::msr_r));
	map(0x0092, 0x0092).rw(m_fdc_2hd, FUNC(upd765a_device::fifo_r), FUNC(upd765a_device::fifo_w));
	map(0x0094, 0x0094).rw(FUNC(pc9801_state::fdc_2hd_ctrl_r), FUNC(pc9801_state::fdc_2hd_ctrl_w));
	map(0x00a0, 0x00af).rw(FUNC(pc9801_state::pc9801_a0_r), FUNC(pc9801_state::pc9801_a0_w)); //upd7220 bitmap ports / display registers
	map(0x00c8, 0x00cb).m(m_fdc_2dd, FUNC(upd765a_device::map)).umask16(0x00ff);
	map(0x00cc, 0x00cc).rw(FUNC(pc9801_state::fdc_2dd_ctrl_r), FUNC(pc9801_state::fdc_2dd_ctrl_w)); //upd765a 2dd / <undefined>
	map(0x00f0, 0x00ff).r(FUNC(pc9801_state::f0_r)).umask16(0x00ff);
}

/*************************************
 *
 * PC-9801RS specific handlers (IA-32)
 *
 ************************************/

// TODO: convert to device
// TODO: having this non-linear makes the system to boot in BASIC for PC-9821. Perhaps it stores settings? How to change these?
uint8_t pc9801vm_state::pc9801rs_knjram_r(offs_t offset)
{
	uint32_t pcg_offset;

	if(!(m_font_addr & 0xff))
	{
		int char_size = m_video_ff[FONTSEL_REG];
		return m_char_rom[(m_font_addr >> 8) * (8 << char_size) + (char_size * 0x800) + ((offset >> 1) & 0xf)];
	}

	pcg_offset = (m_font_addr & 0x7f7f) << 5;
	pcg_offset|= offset & 0x1e;

	// 8x16 charset selector
	// telenetm defintely mirrors offset & 1 for 8x16 romaji title songs, would otherwise blank them out
	if((m_font_addr & 0x7c00) == 0x0800)
		return m_kanji_rom[pcg_offset | 0];

	// rxtrain wants the LR setting for PCG area ...
	if((m_font_addr & 0xff00) == 0x5600 || (m_font_addr & 0xff00) == 0x5700)
	{
		pcg_offset |= (!m_video_ff[KAC_REG] << 12);
		return m_kanji_rom[pcg_offset | m_font_lr];
	}

	// ... but mezaset2 don't, implying it just read this linearly
	return m_kanji_rom[pcg_offset | (offset & 1)];
}

void pc9801vm_state::pc9801rs_knjram_w(offs_t offset, uint8_t data)
{
	uint32_t pcg_offset;

	pcg_offset = (m_font_addr & 0x7f7f) << 5;
	pcg_offset|= offset & 0x1e;
	pcg_offset|= m_font_lr;

	if((m_font_addr & 0xff00) == 0x5600 || (m_font_addr & 0xff00) == 0x5700)
	{
		// HACK: don't know yet how KAC works
		// 0=code access, 1=dot access
		// os2warp3 expects a kanjiram flag to RAM $596 -> PCG offset 000ac429,
		// will otherwise moan for CPU not set High.
		// This traces back by POST routines setting that location with 0x80, then it successively
		// wipes out a good chunk of the area with KAC mode enabled ...
		pcg_offset |= (!m_video_ff[KAC_REG] << 12);
		m_kanji_rom[pcg_offset] = data;
		m_gfxdecode->gfx(2)->mark_dirty(pcg_offset >> 5);
	}
}

void pc9801vm_state::itf_43d_bank_w(offs_t offset, uint8_t data)
{
	if((data & 0xf0) == 0x00 || (data & 0xf0) == 0x10)
	{
		if((data & 0xed) == 0x00)
		{
			m_ipl->set_bank((data & 2) >> 1);
			return;
		}
	}

	logerror("Unknown ITF $43d ROM bank setting %02x\n",data);
}

void pc9801vm_state::cbus_43f_bank_w(offs_t offset, uint8_t data)
{
	if((data & 0xf0) == 0x20)
		m_vram_bank = (data & 2) >> 1;
	else
	{
		logerror("Unknown C-Bus $43f bank setting %02x\n",data);
	}
}


uint8_t pc9801vm_state::a20_ctrl_r(offs_t offset)
{
	if(offset == 0x01)
		return (m_gate_a20 ^ 1) | 0xfe;
	else if(offset == 0x03)
		return (m_gate_a20 ^ 1) | (m_nmi_ff << 1);

	return f0_r(offset);
}

void pc9801vm_state::a20_ctrl_w(offs_t offset, uint8_t data)
{
	if(offset == 0x00)
	{
		uint8_t por;
		/* reset POR bit */
		// TODO: is there any other way that doesn't involve direct r/w of ppi address?
		por = m_ppi_sys->read(2) & ~0x20;
		m_ppi_sys->write(2, por);
		m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
		m_gate_a20 = 0;
	}

	if(offset == 0x01)
		m_gate_a20 = 1;

	if(offset == 0x03)
	{
		if(data == 0x02)
			m_gate_a20 = 1;
		else if(data == 0x03)
			m_gate_a20 = 0;
		else
			logerror("CPU port $00f6: unmapped data write %02x\n", data);
	}
	m_maincpu->set_input_line(INPUT_LINE_A20, m_gate_a20);
}

uint8_t pc9801_state::grcg_r(offs_t offset)
{
	if(offset == 6)
	{
		logerror("GRCG mode R\n");
		return 0xff;
	}
	else if(offset == 7)
	{
		logerror("GRCG tile R\n");
		return 0xff;
	}
	return txt_scrl_r(offset);
}

void pc9801_state::grcg_w(offs_t offset, uint8_t data)
{
	if(offset == 6)
	{
//      logerror("%02x GRCG MODE\n",data);
		m_grcg.mode = data;
		m_grcg.tile_index = 0;
		return;
	}
	else if(offset == 7)
	{
//      logerror("%02x GRCG TILE %02x\n",data,m_grcg.tile_index);
		m_grcg.tile[m_grcg.tile_index] = bitswap<8>(data,0,1,2,3,4,5,6,7);
		m_grcg.tile_index ++;
		m_grcg.tile_index &= 3;
		return;
	}

	txt_scrl_w(offset,data);
}

void pc9801vm_state::pc9801rs_a0_w(offs_t offset, uint8_t data)
{
	if((offset & 1) == 0 && offset & 8 && m_ex_video_ff[ANALOG_16_MODE])
	{
		switch(offset)
		{
			case 0x08: m_analog16.pal_entry = data & 0xf; break;
			case 0x0a: m_analog16.g[m_analog16.pal_entry] = data & 0xf; break;
			case 0x0c: m_analog16.r[m_analog16.pal_entry] = data & 0xf; break;
			case 0x0e: m_analog16.b[m_analog16.pal_entry] = data & 0xf; break;
		}

		m_palette->set_pen_color(
			m_analog16.pal_entry + 0x10,
			pal4bit(m_analog16.r[m_analog16.pal_entry]),
			pal4bit(m_analog16.g[m_analog16.pal_entry]),
			pal4bit(m_analog16.b[m_analog16.pal_entry])
		);
		// lemmings raster effects
		m_screen->update_partial(m_screen->vpos());
		return;
	}

	pc9801_a0_w(offset,data);
}

void pc9801vm_state::egc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if((m_egc.regs[1] & 0x6000) && (offset == 4)) // why?
		return;
	COMBINE_DATA(&m_egc.regs[offset]);
	switch(offset)
	{
		case 4:
			m_egc.mask = bitswap<16>(m_egc.regs[4],8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7);
			break;
		case 2:
		case 6:
		case 7:
			m_egc.leftover[0] = m_egc.leftover[1] = m_egc.leftover[2] = m_egc.leftover[3] = 0;
			m_egc.count = (m_egc.regs[7] & 0xfff) + 1;
			m_egc.first = true;
			m_egc.start = false;
			m_egc.loaded = false;
			break;
	}
}

uint16_t pc9801vm_state::grcg_gvram_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t ret = upd7220_grcg_r((offset + 0x4000) | (m_vram_bank << 16), mem_mask);
	return bitswap<16>(ret,8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7);
}

void pc9801vm_state::grcg_gvram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	data = bitswap<16>(data,8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7);
	upd7220_grcg_w((offset + 0x4000) | (m_vram_bank << 16), data, mem_mask);
}

uint16_t pc9801vm_state::grcg_gvram0_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t ret = upd7220_grcg_r(offset | (m_vram_bank << 16), mem_mask);
	return bitswap<16>(ret,8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7);
}

void pc9801vm_state::grcg_gvram0_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	data = bitswap<16>(data,8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7);
	upd7220_grcg_w(offset | (m_vram_bank << 16), data, mem_mask);
}

/*
 * FDC MODE control
 *
 * ???? ---- <undefined>
 * ---- x--- (r/o) DIP-SW 3-2 built-in FDC spec (1) OFF 2HD (0) ON 2DD
 * ---- -x-- (r) DIP-SW 3-1 FDC FIXed mode (1) ON
 *           (w) Enable motor ON (1) defined by 0x94 bit 3 (0) always on
 * ---- --x- (r/w) FDD EXC access mode status (1) 2HD (0) 2DD
 * ---- ---x (r/w) PORT EXC I/F mode select (1) 2HD (0) 2DD
 *           (Disables I/O port access)
 *
 * NB: high-reso class diverges here:
 * - XA/XL themselves have no way to access any of this;
 * - post-XA/XL just has Enable motor ON writes;
 */
uint8_t pc9801vm_state::fdc_mode_r()
{
	return (m_fdc_mode & 3) | 0xf0 | (m_dsw3->read() & 3) << 2;
}

void pc9801vm_state::fdc_set_density_mode(bool is_2hd)
{
	floppy_image_device *floppy0 = m_fdc_2hd->subdevice<floppy_connector>("0")->get_device();
	floppy_image_device *floppy1 = m_fdc_2hd->subdevice<floppy_connector>("1")->get_device();

	floppy0->set_rpm(is_2hd ? 360 : 300);
	floppy1->set_rpm(is_2hd ? 360 : 300);

	m_fdc_2hd->set_rate(is_2hd ? 500000 : 250000);
//  printf("FDC set new mode %s\n", is_2hd ? "2HD" : "2DD");
	logerror("%s: FDC set new mode %s\n", machine().describe_context(), is_2hd ? "2HD" : "2DD");
}

void pc9801vm_state::fdc_mode_w(uint8_t data)
{
	const bool old_mode = bool(BIT(m_fdc_mode, 1));
	const bool new_mode = bool(BIT(data, 1));

	if (old_mode != new_mode)
		fdc_set_density_mode(new_mode);

	m_fdc_mode = data;

	if(BIT(m_fdc_mode, 2))
	{
		m_fdc_2hd->subdevice<floppy_connector>("0")->get_device()->mon_w(CLEAR_LINE);
		m_fdc_2hd->subdevice<floppy_connector>("1")->get_device()->mon_w(CLEAR_LINE);
	}

	//if(data & 0xfc)
	//  logerror("FDC ctrl called with %02x\n",data);
}

TIMER_CALLBACK_MEMBER(pc9801vm_state::fdc_trigger)
{
	// TODO: sorcer/hydlide definitely expects the XTMASK irq to be taken
	// NOTE: should probably trigger the FDC irq depending on mode, i.e. use fdc_irq_w fn
	if (BIT(m_fdc_2hd_ctrl, 2))
	{
		m_pic2->ir2_w(0);
		m_pic2->ir2_w(1);
	}
}

// TODO: undefined/disallow read/writes if I/F mode doesn't match
// (and that applies to FDC mapping too!)
// id port 0 -> 2DD
// id port 1 -> 2HD
template <unsigned port> u8 pc9801vm_state::fdc_2hd_2dd_ctrl_r()
{
	u8 res = fdc_2hd_ctrl_r();
	if (port == 0)
	{
		res |= 0x20;
		res |= fdc_drive_ready_r(m_fdc_2hd) << 4;
	}
	return res;
}

template <unsigned port> void pc9801vm_state::fdc_2hd_2dd_ctrl_w(u8 data)
{
	bool prev_trig = false;
	bool cur_trig = false;

	if (port == 0 && bool(BIT(m_fdc_mode, 0)) == false)
	{
		prev_trig = bool(BIT(m_fdc_2hd_ctrl, 0));
		cur_trig = bool(BIT(data, 0));
	}

	fdc_2hd_ctrl_w(data);

	// TODO: Enable motor ON is reversed compared to the docs
	if(!(m_fdc_mode & 4)) // required for 9821
	{
		m_fdc_2hd->subdevice<floppy_connector>("0")->get_device()->mon_w(data & 8 ? CLEAR_LINE : ASSERT_LINE);
		m_fdc_2hd->subdevice<floppy_connector>("1")->get_device()->mon_w(data & 8 ? CLEAR_LINE : ASSERT_LINE);
	}

	// TODO: this looks awfully similar to pc88va DMA mode, including same bits for trigger and irq mask.
	// NOTE: 100 msec too slow
	if (port == 0 && !prev_trig && cur_trig)
	{
		m_fdc_timer->reset();
		m_fdc_timer->adjust(attotime::from_msec(100));
	}
}

// TODO: some machines (which?) mirror 0x00be to 0x04be
u8 pc9801vm_state::fdc_3mode_r(offs_t offset)
{
	// freebsd21 expects 0-fill rather than the more logical 1-fill
	// on 5.25" floppies/ext. drive id checks, open bus?
	// TODO: external FDD
	if (m_fdc_3mode.dev_sel & 2)
		return 0;

	const bool is_35hd = m_fdc_2hd->subdevice<floppy_connector>(m_fdc_3mode.dev_sel ? "1" : "0")->get_device()->get_form_factor() == floppy_image::FF_35;

	if (!is_35hd)
		return 0;

	u8 res = 0xee;

	// Check if drive is in 2HD/1MB mode
	if (BIT(m_fdc_mode, 1))
		res |= 1 << 4;

	if (m_fdc_3mode.access_144mb)
		res |= 1 << 0;

	return res;
}

/*
 * -xx- ---- Drive specification
 * -00- ---- First internal drive
 * -01- ---- Second internal drive
 * -10- ---- External drive
 * ---x ---- Operation mode specification
 * ---0 ---- no-op
 * ---1 ---- Access Mode valid
 * ---- ---x Access Mode specification
 * ---- ---0 1MB/640KB
 * ---- ---1 1.44MB
 */
void pc9801vm_state::fdc_3mode_w(offs_t offset, uint8_t data)
{
	//logerror("$4be: W %02x\n", data);
	m_fdc_3mode.dev_sel = (data & 0x60) >> 5;

	// TODO: external FDD
	if (m_fdc_3mode.dev_sel & 2)
		return;

	if (BIT(data, 4))
	{
		const bool is_35hd = m_fdc_2hd->subdevice<floppy_connector>(m_fdc_3mode.dev_sel ? "1" : "0")->get_device()->get_form_factor() == floppy_image::FF_35;

		if (!is_35hd)
			return;

		floppy_image_device *floppy = m_fdc_2hd->subdevice<floppy_connector>(m_fdc_3mode.dev_sel ? "1" : "0")->get_device();
		m_fdc_3mode.access_144mb = !!(BIT(data, 0));
		if (m_fdc_3mode.access_144mb)
		{
			floppy->set_rpm(300);
			m_fdc_2hd->set_rate(500000);
		}
		else
		{
			fdc_set_density_mode(!!BIT(m_fdc_mode, 1));
		}
	}
}

void pc9801vm_state::pc9801rs_video_ff_w(offs_t offset, uint8_t data)
{
	if(offset == 1)
	{
		if((data & 0xf0) == 0) /* disable any PC-9821 specific HW regs */
			m_ex_video_ff[(data & 0xfe) >> 1] = data & 1;

		if(0)
		{
			static const char *const ex_video_ff_regnames[] =
			{
				"16 colors mode",   // 0
				"<unknown>",        // 1
				"EGC related",      // 2
				"<unknown>"         // 3
			};

			logerror("Write to extended video FF register %s -> %02x\n",ex_video_ff_regnames[(data & 0x06) >> 1],data & 1);
		}
		//else
		//  logerror("Write to extended video FF register %02x\n",data);

		return;
	}

	pc9801_video_ff_w(data);
}

/*
 * DMA access control (I/O $439)
 *
 * x--- ---- Mate A: Printer select (1) built-in (0) PC-9821-E02
 *           ^ on 98Fellow with 0 it disconnects the printer interface,
 *           ^ may still select anything but -E02?
 * --?? ---- <unknown>
 * ---- -x-- DMA address mask (1) inhibit DMA access for anything higher than 1MB
 *           ^ defaults to 0 on high-reso equipped machines (?), 1 otherwise
 * ---- --x- Graph LIO BIOS select (?), on 9801VX21
 * ---- ---x Mate A: selects high-reso mode
 *           ^ unknown otherwise
 *
 * TODO: why BIOS has to read it twice during cold boot?
 * Later 9821 machines will add a mov bh,al on second read, is there some kind of side
 * effect for reading?
 */
u8 pc9801vm_state::dma_access_ctrl_r(offs_t offset)
{
	logerror("%s: DMA access control read\n", machine().describe_context());
	return m_dma_access_ctrl;
}

void pc9801vm_state::dma_access_ctrl_w(offs_t offset, u8 data)
{
	logerror("%s: DMA access control write %02x\n", machine().describe_context(), data);
	m_dma_access_ctrl = data;
}

// ARTIC device

/*
 * [0] read bits 15-0 of the counter device
 * [1] read bits 23-8 of the counter device
 *
 * FreeDOS(98) Kernel will test [1] a whole lot during HMA allocation
 */
uint16_t pc9801vm_state::timestamp_r(offs_t offset)
{
	return (m_maincpu->total_cycles() >> (8 * offset));
}

void pc9801vm_state::artic_wait_w(u8 data)
{
	// 0.6 μsec
	m_maincpu->spin_until_time(attotime::from_nsec(600));
}

uint8_t pc9801vm_state::midi_r()
{
	/* unconnect, needed by Amaranth KH to boot */
	return 0xff;
}

uint8_t pc9801_state::pic_r(offs_t offset)
{
	return ((offset >= 4) ? m_pic2 : m_pic1)->read(offset & 3);
}

void pc9801_state::pic_w(offs_t offset, uint8_t data)
{
	((offset >= 4) ? m_pic2 : m_pic1)->write(offset & 3, data);
}

void pc9801_state::ipl_bank(address_map &map)
{
	map(0x00000, 0x2ffff).rom().region("ipl", 0);
}

void pc9801vm_state::pc9801ux_map(address_map &map)
{
	map(0x0a0000, 0x0a3fff).rw(FUNC(pc9801vm_state::tvram_r), FUNC(pc9801vm_state::tvram_w));
	map(0x0a4000, 0x0a4fff).rw(FUNC(pc9801vm_state::pc9801rs_knjram_r), FUNC(pc9801vm_state::pc9801rs_knjram_w));
	map(0x0a8000, 0x0bffff).rw(FUNC(pc9801vm_state::grcg_gvram_r), FUNC(pc9801vm_state::grcg_gvram_w));
	map(0x0e0000, 0x0e7fff).rw(FUNC(pc9801vm_state::grcg_gvram0_r), FUNC(pc9801vm_state::grcg_gvram0_w));
	map(0x0e8000, 0x0fffff).m(m_ipl, FUNC(address_map_bank_device::amap16));
}

void pc9801vm_state::pc9801ux_io(address_map &map)
{
//  map.unmap_value_high();
	pc9801_common_io(map);
	map(0x0020, 0x002f).w(FUNC(pc9801vm_state::dmapg8_w)).umask16(0xff00);
//  map(0x0050, 0x0057).noprw(); // 2dd ppi?
	map(0x005c, 0x005f).r(FUNC(pc9801vm_state::timestamp_r)); // artic
	map(0x005f, 0x005f).w(FUNC(pc9801vm_state::artic_wait_w));
	map(0x0068, 0x006b).w(FUNC(pc9801vm_state::pc9801rs_video_ff_w)).umask16(0x00ff); //mode FF / <undefined>
	map(0x0070, 0x007f).rw(FUNC(pc9801vm_state::grcg_r), FUNC(pc9801vm_state::grcg_w)).umask16(0x00ff); //display registers "GRCG" / i8253 pit
	map(0x0090, 0x0090).r(m_fdc_2hd, FUNC(upd765a_device::msr_r));
	map(0x0092, 0x0092).rw(m_fdc_2hd, FUNC(upd765a_device::fifo_r), FUNC(upd765a_device::fifo_w));
	map(0x0094, 0x0094).rw(FUNC(pc9801vm_state::fdc_2hd_2dd_ctrl_r<1>), FUNC(pc9801vm_state::fdc_2hd_2dd_ctrl_w<1>));
	map(0x00a0, 0x00af).rw(FUNC(pc9801vm_state::pc9801_a0_r), FUNC(pc9801vm_state::pc9801rs_a0_w)); //upd7220 bitmap ports / display registers
	map(0x00be, 0x00be).rw(FUNC(pc9801vm_state::fdc_mode_r), FUNC(pc9801vm_state::fdc_mode_w));
	map(0x00c8, 0x00cb).m(m_fdc_2hd, FUNC(upd765a_device::map)).umask16(0x00ff);
	map(0x00cc, 0x00cc).rw(FUNC(pc9801vm_state::fdc_2hd_2dd_ctrl_r<0>), FUNC(pc9801vm_state::fdc_2hd_2dd_ctrl_w<0>));
	map(0x00f0, 0x00ff).rw(FUNC(pc9801vm_state::a20_ctrl_r), FUNC(pc9801vm_state::a20_ctrl_w)).umask16(0x00ff);
	map(0x0439, 0x0439).rw(FUNC(pc9801vm_state::dma_access_ctrl_r), FUNC(pc9801vm_state::dma_access_ctrl_w));
	map(0x043d, 0x043d).w(FUNC(pc9801vm_state::itf_43d_bank_w));
	map(0x043f, 0x043f).w(FUNC(pc9801vm_state::cbus_43f_bank_w));
	map(0x04a0, 0x04af).w(FUNC(pc9801vm_state::egc_w));
	map(0x04be, 0x04be).rw(FUNC(pc9801vm_state::fdc_3mode_r), FUNC(pc9801vm_state::fdc_3mode_w));
	map(0x3fd8, 0x3fdf).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0xff00);
}

void pc9801vm_state::pc9801rs_map(address_map &map)
{
	pc9801ux_map(map);
//  map(0x0d8000, 0x0d9fff).rom().region("ide",0);
	map(0x0da000, 0x0dbfff).ram(); // ide ram
	map(0xee8000, 0xefffff).m(m_ipl, FUNC(address_map_bank_device::amap16));
	map(0xfe8000, 0xffffff).m(m_ipl, FUNC(address_map_bank_device::amap16));
}

void pc9801vm_state::pc9801rs_io(address_map &map)
{
//  map.unmap_value_high();
	pc9801ux_io(map);
	map(0x0430, 0x0433).rw(FUNC(pc9801vm_state::ide_ctrl_hack_r), FUNC(pc9801vm_state::ide_ctrl_w)).umask16(0x00ff);
	map(0x0640, 0x064f).rw(FUNC(pc9801vm_state::ide_cs0_r), FUNC(pc9801vm_state::ide_cs0_w));
	map(0x0740, 0x074f).rw(FUNC(pc9801vm_state::ide_cs1_r), FUNC(pc9801vm_state::ide_cs1_w));
	map(0x1e8c, 0x1e8f).noprw(); // temp
	map(0xbfdb, 0xbfdb).w(FUNC(pc9801vm_state::mouse_freq_w));
	map(0xe0d0, 0xe0d3).r(FUNC(pc9801vm_state::midi_r));
}

void pc9801us_state::pc9801us_io(address_map &map)
{
	pc9801rs_io(map);
	map(0x0430, 0x0433).rw(FUNC(pc9801us_state::ide_ctrl_r), FUNC(pc9801us_state::ide_ctrl_w)).umask16(0x00ff);
	map(0x00f6, 0x00f6).lw8(NAME([this] (offs_t offset, u8 data) {
		// despite what undocumented mem claims US and FS actually access this for SDIP banking
		if (data == 0xa0 || data == 0xe0)
			m_sdip->bank_w(BIT(data, 6));
		else
			a20_ctrl_w(3, data);
	}));

	// 0x841e ~ 0x8f1e SDIP I/O mapping
	// NOTE: split in half for pleasing emumem
	map(0x841e, 0x841e).select(0x300).lrw8(
		NAME([this] (offs_t offset) { return m_sdip->read(offset >> 8); }),
		NAME([this] (offs_t offset, u8 data) { m_sdip->write(offset >> 8, data); })
	);
	map(0x881e, 0x881e).select(0x700).lrw8(
		NAME([this] (offs_t offset) { return m_sdip->read((offset >> 8) + 4); }),
		NAME([this] (offs_t offset, u8 data) { m_sdip->write((offset >> 8) + 4, data); })
	);
//  map(0x8f1f, 0x8f1f).w(m_sdip, FUNC(pc98_sdip_device::bank_w));
}

void pc9801bx_state::pc9801bx2_map(address_map &map)
{
	pc9801rs_map(map);
//  map(0x000a0000, 0x000a3fff).rw(FUNC(pc9801_state::tvram_r), FUNC(pc9801_state::tvram_w));
//  map(0x000a4000, 0x000a4fff).rw(FUNC(pc9801_state::pc9801rs_knjram_r), FUNC(pc9801_state::pc9801rs_knjram_w));
//  map(0x000a8000, 0x000bffff).rw(FUNC(pc9821_state::pc9821_grcg_gvram_r), FUNC(pc9821_state::pc9821_grcg_gvram_w));
//  map(0x000cc000, 0x000cffff).rom().region("sound_bios", 0); //sound BIOS
//  map(0x000d8000, 0x000d9fff).rom().region("ide",0)
//  map(0x000da000, 0x000dbfff).ram(); // ide ram (declared in RS)
//  map(0x000e0000, 0x000e7fff).rw(FUNC(pc9821_state::pc9821_grcg_gvram0_r), FUNC(pc9821_state::pc9821_grcg_gvram0_w));
	map(0x000e8000, 0x000fffff).m(m_ipl, FUNC(address_map_bank_device::amap16));
	map(0xffee8000, 0xffefffff).m(m_ipl, FUNC(address_map_bank_device::amap16));
	map(0xfffe8000, 0xffffffff).m(m_ipl, FUNC(address_map_bank_device::amap16));
}

u8 pc9801bx_state::i486_cpu_mode_r(offs_t offset)
{
	/*
	 * x--- ---- 33 MHz/25 MHz switch (9821 Bp, Bs, Be, Cs2, Ce2 only)
	 * ---- ---x (1) MIDDLE or LOW mode (?), (0) High mode
	 */
	// 9821 MULTi checks this and refuses to boot if finds bit 0 high
	return 0;
}

/*
 * GDC 31 kHz register
 * R/W on PC-98GS, H98, all PC-9821 except PC-9821Ts,
 * 9801BA2, 9801BS2, 9801BX2
 *
 * x--- ---- unknown, if set high then DAC1BIT goes berserk at POST.
 * ---- --xx (R/W) horizontal frequency
 * ---- --1x ^ "setting prohibited" (?)
 * ---- --01 ^ 31.47kHz
 * ---- --00 ^ 24.83kHz
 *
 * PC-9801NS/A can also r/w this but with different meaning
 */
u8 pc9801bx_state::gdc_31kHz_r(offs_t offset)
{
	return 0;
}

void pc9801bx_state::gdc_31kHz_w(offs_t offset, u8 data)
{
	// Repeatedly switches 0 to 3 during POST, sync monitor check to guess support?
//  if (data)
//      popmessage("31kHz register set %02x, contact MAMEdev", data);
}

void pc9801bx_state::pc9801bx2_io(address_map &map)
{
	pc9801us_io(map);
	// NOP legacy SDIP bank access
	map(0x00f6, 0x00f6).lw8(NAME([this] (offs_t offset, u8 data) { a20_ctrl_w(3, data); }));
	map(0x0534, 0x0534).r(FUNC(pc9801bx_state::i486_cpu_mode_r));
	map(0x09a8, 0x09a8).rw(FUNC(pc9801bx_state::gdc_31kHz_r), FUNC(pc9801bx_state::gdc_31kHz_w));
	map(0x8f1f, 0x8f1f).lw8(NAME([this] (offs_t offset, u8 data) {
		// BA2 onward and every PC-9821 uses this method for SDIP bank
		if (data == 0x80 || data == 0xc0)
			m_sdip->bank_w(BIT(data, 6));
		else
			logerror("SDIP: I/O $8f1f unrecognized write %02x\n", data);
	}));
}

/*uint8_t pc9801_state::winram_r(offs_t offset)
{
    offset = (offset & 0x1ffff) | (m_pc9821_window_bank & 0xfe) * 0x10000;
    return
}


void pc9801_state::winram_w(offs_t offset, uint8_t data)
{
    offset = (offset & 0x1ffff) | (m_pc9821_window_bank & 0xfe) * 0x10000;
}*/


void pc9801_state::upd7220_1_map(address_map &map)
{
	map(0x00000, 0x03fff).ram().share("video_ram_1");
}

void pc9801_state::upd7220_2_map(address_map &map)
{
	map(0x00000, 0x3ffff).ram().share("video_ram_2");
}

void pc9801vm_state::upd7220_grcg_2_map(address_map &map)
{
	map(0x00000, 0x3ffff).rw(FUNC(pc9801vm_state::upd7220_grcg_r), FUNC(pc9801vm_state::upd7220_grcg_w)).share("video_ram_2");
}

ioport_value pc98_base_state::system_type_r()
{
//  System Type (0x00 stock PC-9801, 0xc0 PC-9801U / PC-98LT, PC-98HA, 0x80 others)
	return m_sys_type;
}

static INPUT_PORTS_START( pc9801 )
	PORT_START("DSW1")
	PORT_DIPNAME( 0x0001, 0x0001, "Display Type" ) PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(      0x0000, "Normal Display (15KHz)" )
	PORT_DIPSETTING(      0x0001, "Hi-Res Display (24KHz)" )
	// TODO: "GFX" screen selections (routing?) for vanilla class
	PORT_DIPUNKNOWN_DIPLOC( 0x002, 0x002, "SW1:2" )
	PORT_DIPUNKNOWN_DIPLOC( 0x004, 0x004, "SW1:3" )
	PORT_DIPUNKNOWN_DIPLOC( 0x008, 0x008, "SW1:4" )
	PORT_DIPUNKNOWN_DIPLOC( 0x010, 0x010, "SW1:5" )
	PORT_DIPUNKNOWN_DIPLOC( 0x020, 0x000, "SW1:6" )
	// TODO: built-in RXC / TXC clocks for RS-232C (routing?) for vanilla class
	PORT_DIPUNKNOWN_DIPLOC( 0x040, 0x000, "SW1:7" )
	PORT_DIPUNKNOWN_DIPLOC( 0x080, 0x080, "SW1:8" )
	PORT_DIPUNKNOWN_DIPLOC( 0x100, 0x000, "SW1:9" )
	PORT_DIPUNKNOWN_DIPLOC( 0x200, 0x200, "SW1:10" )

	PORT_START("DSW2")
	PORT_DIPNAME( 0x01, 0x01, "System Specification" ) PORT_DIPLOCATION("SW2:1") //jumps to daa00 if off, presumably some card booting
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, "Terminal Mode" ) PORT_DIPLOCATION("SW2:2")
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, "Text width" ) PORT_DIPLOCATION("SW2:3")
	PORT_DIPSETTING(    0x04, "40 chars/line" )
	PORT_DIPSETTING(    0x00, "80 chars/line" )
	PORT_DIPNAME( 0x08, 0x00, "Text height" ) PORT_DIPLOCATION("SW2:4")
	PORT_DIPSETTING(    0x08, "20 lines/screen" )
	PORT_DIPSETTING(    0x00, "25 lines/screen" )
	PORT_DIPNAME( 0x10, 0x00, "Memory Switch Init" ) PORT_DIPLOCATION("SW2:5")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) ) //Fix memory switch condition
	PORT_DIPSETTING(    0x10, DEF_STR( Yes ) ) //Initialize Memory Switch with the system default
	PORT_DIPUNUSED_DIPLOC( 0x20, 0x20, "SW2:6" )
	PORT_DIPUNUSED_DIPLOC( 0x40, 0x40, "SW2:7" )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW2:8" )

	PORT_START("MOUSE_X")
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_X ) PORT_SENSITIVITY(30) PORT_KEYDELTA(30)

	PORT_START("MOUSE_Y")
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y ) PORT_SENSITIVITY(30) PORT_KEYDELTA(30)

	PORT_START("MOUSE_B")
	PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_CODE(MOUSECODE_BUTTON2) PORT_NAME("Mouse Right Button")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_CODE(MOUSECODE_BUTTON3) PORT_NAME("Mouse Middle Button")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CODE(MOUSECODE_BUTTON1) PORT_NAME("Mouse Left Button")

	PORT_START("ROM_LOAD")
	PORT_CONFNAME( 0x01, 0x01, "Load floppy 2HD BIOS" )
	PORT_CONFSETTING(    0x00, DEF_STR( Yes ) )
	PORT_CONFSETTING(    0x01, DEF_STR( No ) )
	PORT_CONFNAME( 0x02, 0x02, "Load floppy 2DD BIOS" )
	PORT_CONFSETTING(    0x00, DEF_STR( Yes ) )
	PORT_CONFSETTING(    0x02, DEF_STR( No ) )
INPUT_PORTS_END

static INPUT_PORTS_START( pc9801rs )
	PORT_INCLUDE( pc9801 )

	PORT_MODIFY("DSW1")
	// LCD display, 98DO Demo explicitly wants it to be non-Plasma
	PORT_DIPNAME( 0x04, 0x04, "Monitor Type" ) PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(    0x04, "RGB" )
	PORT_DIPSETTING(    0x00, "Plasma" )
	PORT_DIPNAME( 0x80, 0x00, "Graphic Function" ) PORT_DIPLOCATION("SW1:8")
	PORT_DIPSETTING(    0x80, "Basic (8 Colors)" )
	PORT_DIPSETTING(    0x00, "Expanded (16/4096 Colors)" )
	PORT_BIT(0x300, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_MODIFY("DSW2")
	PORT_DIPNAME( 0x80, 0x80, "GDC clock" ) PORT_DIPLOCATION("SW2:8")
	PORT_DIPSETTING(    0x80, "2.5 MHz" )
	PORT_DIPSETTING(    0x00, "5 MHz" )

	PORT_START("DSW3")
	PORT_DIPNAME( 0x01, 0x01, "FDD Fix Mode" ) PORT_DIPLOCATION("SW3:1")
	// with this OFF enables PORT EXC (fdc mode bit 0)
	PORT_DIPSETTING(    0x00, "Auto-Detection" )
	PORT_DIPSETTING(    0x01, "Fixed" )
	PORT_DIPNAME( 0x02, 0x02, "FDD Density Select" ) PORT_DIPLOCATION("SW3:!2")
	PORT_DIPSETTING(    0x00, "2DD" )
	PORT_DIPSETTING(    0x02, "2HD" )
	PORT_DIPUNUSED_DIPLOC( 0x04, 0x04, "SW3:3" )
	PORT_DIPUNUSED_DIPLOC( 0x08, 0x08, "SW3:4" )
	PORT_DIPUNUSED_DIPLOC( 0x10, 0x10, "SW3:5" )
	PORT_DIPNAME( 0x20, 0x20, "Conventional RAM size" ) PORT_DIPLOCATION("SW3:6")
	PORT_DIPSETTING(    0x20, "640 KB" )
	PORT_DIPSETTING(    0x00, "512 KB" )
	PORT_DIPUNUSED_DIPLOC( 0x40, 0x40, "SW3:7" )
	PORT_DIPNAME( 0x80, 0x00, "CPU Type" ) PORT_DIPLOCATION("SW3:8")
	PORT_DIPSETTING(    0x80, "V30" )
	PORT_DIPSETTING(    0x00, "I386" )

	PORT_MODIFY("ROM_LOAD")
	PORT_BIT( 0x03, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_CONFNAME( 0x04, 0x04, "Load IDE BIOS" )
	PORT_CONFSETTING(    0x00, DEF_STR( Yes ) )
	PORT_CONFSETTING(    0x04, DEF_STR( No ) )
INPUT_PORTS_END

//static INPUT_PORTS_START( pc9801vm11 )
//  PORT_INCLUDE( pc9801rs )
//
//  PORT_MODIFY("DSW3")
//  // TODO: "CPU Add Waitstate Penalty"?
//  // specific for PC-98DO, CV21, UV11 and VM11
//  PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("SW3:!5")
//  PORT_DIPSETTING(    0x00, DEF_STR( No ) )
//  PORT_DIPSETTING(    0x10, DEF_STR( Yes ) )
//INPUT_PORTS_END


static const gfx_layout charset_8x8 =
{
	8,8,
	256,
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};

static const gfx_layout charset_8x16 =
{
	8,16,
	256,
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8,8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16
};

static const gfx_layout charset_16x16 =
{
	16,16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP16(0,1) },
	{ STEP16(0,16) },
	16*16
};

static GFXDECODE_START( gfx_pc9801 )
	GFXDECODE_ENTRY( "chargen",     0x00000, charset_8x8,     0x000, 0x01 )
	GFXDECODE_ENTRY( "chargen",     0x00800, charset_8x16,    0x000, 0x01 )
	GFXDECODE_ENTRY( "kanji",       0x00000, charset_16x16,   0x000, 0x01 )
	GFXDECODE_ENTRY( "raw_kanji",   0x00000, charset_16x16,   0x000, 0x01 )
	GFXDECODE_ENTRY( "new_chargen", 0x00000, charset_16x16,   0x000, 0x01 )
GFXDECODE_END

/****************************************
*
* I8259 PIC interface
*
****************************************/

/*
irq assignment (PC-9801F):

8259 master:
ir0 PIT
ir1 keyboard
ir2 vblank
ir3 expansion bus INT0
ir4 rs-232c
ir5 expansion bus INT1
ir6 expansion bus INT2
ir7 PIC slave

8259 slave:
ir0 printer
ir1 expansion bus INT3 (HDD)
ir2 expansion bus INT41 (2dd floppy irq)
ir3 expansion bus INT42 (2hd floppy irq)
ir4 expansion bus INT5 (usually FM sound board)
ir5 expansion bus INT6 (mouse)
ir6 NDP coprocessor (up to V30 CPU)
ir7 <gnd>
*/


uint8_t pc9801_state::get_slave_ack(offs_t offset)
{
	if (offset==7) { // IRQ = 7
		return m_pic2->acknowledge();
	}
	return 0x00;
}

/****************************************
*
* I8253 PIT interface
*
****************************************/

/* These rates do NOT appear to represent actual XTALs. They are likely obtained in
   different ways on different PC-98 models as divisions of extant XTAL frequencies
   such as 14.7456 MHz, 15.9744 MHz, 19.6608 MHz and 23.9616 MHz.
   PC-9801RS needs X1 for the pit, otherwise Uchiyama Aki no Chou Bangai has sound pitch bugs
   PC-9821 definitely needs X2, otherwise there's a timer error at POST. Unless it needs a different clock anyway ...
   */
#define BASE_CLOCK      XTAL(31'948'800)    // verified to be used by PC-98RS/98FA by wd40yasu
#define MAIN_CLOCK_X1   (BASE_CLOCK / 16)   // 1.9968 MHz
#define MAIN_CLOCK_X2   (BASE_CLOCK / 13)   // 2.4576 MHz

/****************************************
*
* I8237 DMA interface
*
****************************************/

void pc9801_state::dma_hrq_changed(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);

	m_dmac->hack_w(state);

//  logerror("%02x HLDA\n",state);
}

void pc9801_state::tc_w(int state)
{
	/* floppy terminal count */
	m_fdc_2hd->tc_w(state);
	if(m_fdc_2dd)
		m_fdc_2dd->tc_w(state);

//  logerror("TC %02x\n",state);
}

uint8_t pc9801_state::dma_read_byte(offs_t offset)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	offs_t addr = (m_dma_offset[m_dack] << 16) | offset;

	if(offset == 0xffff)
	{
		switch(m_dma_autoinc[m_dack])
		{
			case 1:
			{
				uint8_t page = m_dma_offset[m_dack];
				m_dma_offset[m_dack] = ((page + 1) & 0xf) | (page & 0xf0);
				break;
			}
			case 3:
				m_dma_offset[m_dack]++;
				break;
		}
	}

//  logerror("%08x %02x\n",addr, m_dma_access_ctrl);
	return program.read_byte(addr);
}


void pc9801_state::dma_write_byte(offs_t offset, uint8_t data)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	offs_t addr = (m_dma_offset[m_dack] << 16) | offset;

	if(offset == 0xffff)
	{
		switch(m_dma_autoinc[m_dack])
		{
			case 1:
			{
				uint8_t page = m_dma_offset[m_dack];
				m_dma_offset[m_dack] = ((page + 1) & 0xf) | (page & 0xf0);
				break;
			}
			case 3:
				m_dma_offset[m_dack]++;
				break;
		}
	}
//  logerror("%08x %02x %02x\n",addr,data, m_dma_access_ctrl);

	program.write_byte(addr, data);
}

uint8_t pc9801vm_state::dma_read_byte(offs_t offset)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	offs_t addr = (m_dma_offset[m_dack] << 16) | offset;

	if (BIT(m_dma_access_ctrl, 2))
		addr &= 0xfffff;

	if(offset == 0xffff)
	{
		switch(m_dma_autoinc[m_dack])
		{
			case 1:
			{
				uint8_t page = m_dma_offset[m_dack];
				m_dma_offset[m_dack] = ((page + 1) & 0xf) | (page & 0xf0);
				break;
			}
			case 3:
				m_dma_offset[m_dack]++;
				break;
		}
	}

//  logerror("%08x %02x\n",addr, m_dma_access_ctrl);
	return program.read_byte(addr);
}


void pc9801vm_state::dma_write_byte(offs_t offset, uint8_t data)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	offs_t addr = (m_dma_offset[m_dack] << 16) | offset;

	if (BIT(m_dma_access_ctrl, 2))
		addr &= 0xfffff;

	if(offset == 0xffff)
	{
		switch(m_dma_autoinc[m_dack])
		{
			case 1:
			{
				uint8_t page = m_dma_offset[m_dack];
				m_dma_offset[m_dack] = ((page + 1) & 0xf) | (page & 0xf0);
				break;
			}
			case 3:
				m_dma_offset[m_dack]++;
				break;
		}
	}
//  logerror("%08x %02x %02x\n",addr,data, m_dma_access_ctrl);

	program.write_byte(addr, data);
}

void pc9801_state::set_dma_channel(int channel, int state)
{
	if (!state) m_dack = channel;
}

/*
ch1 cs-4231a
ch2 FDC
ch3 SCSI
*/

void pc9801_state::dack0_w(int state) { /*logerror("%02x 0\n",state);*/ set_dma_channel(0, state); }
void pc9801_state::dack1_w(int state) { /*logerror("%02x 1\n",state);*/ set_dma_channel(1, state); }
void pc9801_state::dack2_w(int state) { /*logerror("%02x 2\n",state);*/ set_dma_channel(2, state); }
void pc9801_state::dack3_w(int state) { /*logerror("%02x 3\n",state);*/ set_dma_channel(3, state); }

/*
 * PPI "system" I/F
 *
 * Port A:
 * xxxx xxxx DIP SW 2
 *           ^ treated as RAM on pc98lt/ha and 9821Ap, As, Ae
 *
 * Port B:
 * x--- ---- RS-232C CI signal (active low)
 * -x-- ---- RS-232C CS signal (active low)
 * --x- ---- RS-232C CD signal (active low)
 * (normal)
 * ---x ---- INT3 status for expansion bus
 * ---- x--- DIP SW 1-1 (0) 15 kHz (1) 24 kHz
 * ---- -x-- IMCK internal RAM parity error
 * ---- --x- EMCK expansion RAM parity error
 * ---- ---x CDAT RTC data read
 * H98 overrides bits 4, 3 with SHUTx status
 *
 * Port C:
 * x--- ---- (286+ machines) SHUT0 status
 * -x-- ---- PSTBM printer PSTB mask
 * --x- ---- (286+ machines) SHUT1 status
 *           ^ pc98lt/ha: ROM drive write protection (active low)
 * ---x ---- MCHKEN RAM parity check (0) invalid
 * ---- x--- buzzer/DAC1BIT mute
 * ---- -x-- TXRE interrupt enable for TXRDY in RS-232C
 * ---- --x- TXEE interrupt enable for TXEMPTY in RS-232C
 * ---- ---x RXRE interrupt enable for RXRDY in RS-232C
 *
 */

u8 pc9801_state::ppi_sys_portb_r()
{
	// TODO: should be active low for rs232
	u8 res = 0;

	res |= BIT(m_dsw1->read(), 0) << 3;
	res |= m_rtc->data_out_r();

	return res;
}

void pc9801_state::uart_irq_check()
{
	m_pic1->ir4_w(m_uart_irq_pending & m_uart_irq_mask ? 1 : 0);
}

template <unsigned N> void pc98_base_state::update_uart_irq(int state)
{
	if (state)
		m_uart_irq_pending |= 1 << N;
	else
		m_uart_irq_pending &= ~(1 << N);
	uart_irq_check();
}

void pc98_base_state::ppi_sys_beep_portc_w(uint8_t data)
{
	m_beeper->set_state(!(data & 0x08));
	m_uart_irq_mask = data & 7;
	uart_irq_check();
}

void pc9801vm_state::ppi_sys_dac_portc_w(uint8_t data)
{
	m_dac1bit_disable = BIT(data, 3);
	// TODO: some models have a finer grained volume control at I/O port 0xae8e
	// (98NOTE only?)
	m_dac1bit->set_output_gain(0, m_dac1bit_disable ? 0.0 : 1.0);
	m_uart_irq_mask = data & 7;
	uart_irq_check();
}

/*
 * PPI "printer" I/F
 *
 * Port B:
 * xx-- ---- TYP1, 0 system type
 * 11-- ---- PC-9801U, PC98LT/HA
 * 10-- ---- <everything else>
 * 01-- ---- <undefined>
 * 00-- ---- vanilla PC-9801
 * --x- ---- MOD system clock
 * --1- ---- CPU 8 MHz Timer 2 MHz
 * --0- ---- CPU 5/10 MHz Timer 2.5 MHz
 * ---x ---- DIP SW 1-3 plasma display (notebooks & pc98lt/ha only)
 * ---- x--- DIP SW 1-8 analog 16 enable
 * ---- -x-- Printer BUSY signal (active low)
 * ---- --x- (normal) CPUT V30 mode
 *           ^ (pc98lt/ha) CPUT system type
 * ---- ---x VF flag (PC-9801VF, PC-9801U)
 *           ^ ? only for models with built-in 2DD
 *
 */

u8 pc9801_state::ppi_prn_portb_r()
{
	u8 res = 0;

//  res |= BIT(m_dsw1->read(), 7) << 3;
//  res |= BIT(m_dsw1->read(), 2) << 4;
	res |= m_sys_type << 6;

	return res;
}

u8 pc9801vm_state::ppi_prn_portb_r()
{
	u8 res = pc9801_state::ppi_prn_portb_r();

	res |= BIT(m_dsw1->read(), 7) << 3;
	res |= BIT(m_dsw1->read(), 2) << 4;

	return res;
}

/*
 * Mouse 8255 I/F
 *
 * Port A:
 * x--- ---- LEFT mouse button
 * -x-- ---- MIDDLE mouse button
 *           \- Undocumented, most PC98 mice don't have it
 * --x- ---- RIGHT mouse button
 * ---? ---- <unused>
 * ---- xxxx MD3-0 mouse direction latch
 *
 * Port B:
 * x--- ---- H98 only: DIP SW 1-4 (FDD external drive select)
 * -x-- ---- DIP SW 3-6: conventional RAM size (RAMKL)
 * ---x ---- PC98RL only: DIP SW 3-3 SASI DMA channel 0
 * ---- --x- SPDSW readout: selects CPU clock speed for 286/386 equipped machines
 * ---- ---x SPDSW readout for H98
 *
 * Port C:
 *
 * x--- ---- HC Latch Mode (1=read latch, 0=read delta)
 *           \- on 0->1 transition reset delta
 * -x-- ---- SXY Axis select (1=Y 0=X)
 * --x- ---- SHL Read nibble select (1) upper (0) lower
 * ---x ---- INT # (1) disable (0) enable
 * ---- x--- (r/o) H98 only: MODSW (0) High-reso mode (1) Normal mode
 * ---- -x-- (r/o) CPUSW: DIP SW 3-8 (1) V30 compatible mode
 * ---- --xx (r/o) RS-232C sync mode settings, DIP SWs 1-6 & 1-5
 *
 * Reading Port B and Port C low nibble are misc DIPSW selectors,
 * their meaning diverges on XA/XL/RL classes vs. the rest.
 *
 */

u8 pc9801_state::ppi_mouse_porta_r()
{
	u8 res = ioport("MOUSE_B")->read() & 0xf0;
	const u8 isporthi = ((m_mouse.control & 0x20) >> 5)*4;

	if ((m_mouse.control & 0x80) == 0)
	{
		if (m_mouse.control & 0x40)
			res |= (m_mouse.dy >> isporthi) & 0xf;
		else
			res |= (m_mouse.dx >> isporthi) & 0xf;
	}
	else
	{
		if (m_mouse.control & 0x40)
			res |= (m_mouse.ly >> isporthi) & 0xf;
		else
			res |= (m_mouse.lx >> isporthi) & 0xf;
	}

//  logerror("A\n");
	return res;
}

void pc9801_state::ppi_mouse_porta_w(uint8_t data)
{
//  logerror("A %02x\n",data);
}

u8 pc9801vm_state::ppi_mouse_portb_r()
{
	return (BIT(m_dsw3->read(), 5) << 6) | 2;
}

void pc9801_state::ppi_mouse_portb_w(uint8_t data)
{
	logerror("%s: PPI mouse port B %02x\n", machine().describe_context() ,data);
}

u8 pc9801vm_state::ppi_mouse_portc_r()
{
	return (BIT(m_dsw3->read(), 7) << 2);
}

void pc9801_state::ppi_mouse_portc_w(uint8_t data)
{
	// fsmoon:   0x00 -> 0x80 -> 0xa0 -> 0xc0 -> 0xf0
	//           (read latch as relative)
	// prinmak2: 0x00 -> 0x20 -> 0x40 -> 0x60 -> 0x60
	//           (keeps reading "delta" but never reset it, absolute mode)
	// biblems2: 0x0f -> 0x2f -> 0x4f -> 0x6f -> 0xef
	//           (latches a delta reset then reads delta diff, relative mode)

	const u8 mouse_x = ioport("MOUSE_X")->read();
	const u8 mouse_y = ioport("MOUSE_Y")->read();
	m_mouse.dx = (mouse_x - m_mouse.prev_dx) & 0xff;
	m_mouse.dy = (mouse_y - m_mouse.prev_dy) & 0xff;

	if ((m_mouse.control & 0x80) == 0 && data & 0x80)
	{
		m_mouse.lx = m_mouse.dx & 0xff;
		m_mouse.ly = m_mouse.dy & 0xff;
		m_mouse.prev_dx = mouse_x;
		m_mouse.prev_dy = mouse_y;
	}

	m_mouse.control = data;
}

// extended port $bfdb
u8 pc9801vm_state::mouse_freq_r(offs_t offset)
{
	// TODO: not all models support read-back
	// PC-9801DA: 0xff
	// H-98S: reads frequency only, '1' everything else
	// PC-9801US: frequency & irq cycle, '0' everything else
	// PC-9801Xa: frequency, '0' everything else
	popmessage("Read mouse $bfdb, contact MAMEdev");
	return m_mouse.freq_reg;
}

void pc9801vm_state::mouse_freq_w(offs_t offset, u8 data)
{
	// Port unavailable on PC-9871 1st gen & PC-9871/K, PC-9801F3, PC-9801M2, PC-9801M3
	// Some if not all of those have hardcoded HW switch for changing the timing instead
	/*
	 * xxxx xx-- mouse irq cycle setting (edge selection?)
	 * 0000 10-- ^ set on POST
	 * 0000 00-- ^ set by MS-DOS 6.20
	 * 0010 0111 ^ jastrike (main menu)
	 * 1010 0100 ^ jastrike (gameplay)
	 * ---- --xx mouse irq frequency setting (120 >> x) Hz
	 */
	logerror("%s: mouse $bfdb register write %02x\n",machine().describe_context(), data);

	// jastrike definitely don't intend to write to frequency too when
	// feeding 0x27/0xa* values at PC=156ec
	// (without this options menu barely detects any mouse input)
	if (data & 0xfc)
		return;

	m_mouse.freq_reg = data & 3;
	m_mouse.freq_index = 0;
}

/****************************************
*
* UPD765 interface
*
****************************************/

static void pc9801_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
	device.option_add("525hd", FLOPPY_525_HD);
	device.option_add("35hd", FLOPPY_35_HD);
}

static void pc9801_cbus_devices(device_slot_interface &device)
{
	// official HW
//  PC-9801-14
	device.option_add("pc9801_26",  PC9801_26);
	device.option_add("pc9801_55u", PC9801_55U);
	device.option_add("pc9801_55l", PC9801_55L);
	device.option_add("pc9801_86",  PC9801_86);
	device.option_add("pc9801_118", PC9801_118);
	device.option_add("pc9801_spb", PC9801_SPEAKBOARD);
//  Spark Board
	device.option_add("amd98",      AMD98);
	device.option_add("mpu_pc98",   MPU_PC98);
	device.option_add("sb16",       SB16_CT2720);

	// doujinshi HW
// MAD Factory / Doujin Hard (同人ハード)
// MAD Factory Chibi-Oto: an ADPCM override for -86
// MAD Factory Otomi-chan: "TORIE9211 MAD FACTORY" printed on proto PCB, just overrides for ADPCM for -86?
	device.option_add("otomichan_kai", OTOMICHAN_KAI);
}

//  Jast Sound, could be installed independently

void pc9801_state::fdc_2dd_irq(int state)
{
	logerror("IRQ 2DD %d\n",state);

	// TODO: does this mask applies to the specific timer irq trigger only?
	// (bit 0 of control)
	if(m_fdc_2dd_ctrl & 8)
	{
		m_pic2->ir2_w(state);
	}
}

void pc9801vm_state::fdc_irq_w(int state)
{
	if(m_fdc_mode & 1)
		m_pic2->ir3_w(state);
	else
		m_pic2->ir2_w(state);
}

void pc9801vm_state::fdc_drq_w(int state)
{
	if(m_fdc_mode & 1)
		m_dmac->dreq2_w(state ^ 1);
	else
		m_dmac->dreq3_w(state ^ 1);
}

uint32_t pc9801vm_state::a20_286(bool state)
{
	return (state ? 0xffffff : 0x0fffff);
}

/****************************************
*
* Init emulation status
*
****************************************/

void pc9801_state::pc9801_palette(palette_device &palette) const
{
	for(int i = 0; i < 8; i++)
		palette.set_pen_color(i, pal1bit(i >> 1), pal1bit(i >> 2), pal1bit(i >> 0));

	for(int i = 8; i < palette.entries(); i++)
		palette.set_pen_color(i, rgb_t::black());
}

MACHINE_START_MEMBER(pc9801_state,pc9801_common)
{
	m_rtc->cs_w(1);
	m_rtc->oe_w(1);

	int ram_size = m_ram->size() - (640*1024);

	address_space& space = m_maincpu->space(AS_PROGRAM);
	space.install_ram(0, (ram_size < 0) ? m_ram->size() - 1 : (640*1024) - 1, m_ram->pointer());
	if(ram_size > 0)
		space.install_ram(1024*1024, (1024*1024) + ram_size - 1, &m_ram->pointer()[(640*1024)]);

	save_item(NAME(m_sasi_data));
	save_item(NAME(m_sasi_data_enable));
	save_item(NAME(m_sasi_ctrl));
}

MACHINE_START_MEMBER(pc9801_state,pc9801f)
{
	MACHINE_START_CALL_MEMBER(pc9801_common);

	m_fdc_2hd->set_rate(500000);
	m_fdc_2dd->set_rate(250000);
	// TODO: set_rpm for m_fdc_2dd?
	m_sys_type = 0x00 >> 6;
}

MACHINE_START_MEMBER(pc9801vm_state,pc9801rs)
{
	MACHINE_START_CALL_MEMBER(pc9801_common);

	m_sys_type = 0x80 >> 6;

	m_fdc_timer = timer_alloc(FUNC(pc9801vm_state::fdc_trigger), this);

	save_item(NAME(m_dac1bit_disable));

	save_item(NAME(m_dma_access_ctrl));

	save_item(NAME(m_egc.regs));
	save_item(NAME(m_egc.pat));
	save_item(NAME(m_egc.src));
	save_item(NAME(m_egc.count));
	save_item(NAME(m_egc.leftover));
	save_item(NAME(m_egc.first));
	save_item(NAME(m_egc.start));
	save_item(NAME(m_egc.mask));

	save_item(NAME(m_grcg.mode));
	save_item(NAME(m_vram_bank));
}

MACHINE_START_MEMBER(pc9801us_state,pc9801us)
{
	MACHINE_START_CALL_MEMBER(pc9801rs);
}

MACHINE_START_MEMBER(pc9801bx_state,pc9801bx2)
{
	MACHINE_START_CALL_MEMBER(pc9801us);

	// ...
}

MACHINE_RESET_MEMBER(pc9801_state,pc9801_common)
{
	memset(m_tvram.get(), 0, sizeof(uint16_t) * 0x2000);

	m_nmi_ff = 0;
	m_mouse.control = 0xff;
	m_mouse.freq_reg = 0;
	m_mouse.freq_index = 0;
	m_mouse.lx = m_mouse.ly = m_mouse.prev_dx = m_mouse.prev_dy = m_mouse.dx = m_mouse.dy = 0;
	m_dma_autoinc[0] = m_dma_autoinc[1] = m_dma_autoinc[2] = m_dma_autoinc[3] = 0;
}

MACHINE_RESET_MEMBER(pc9801_state,pc9801f)
{
	MACHINE_RESET_CALL_MEMBER(pc9801_common);

	uint8_t op_mode;
	uint8_t *ROM;
	uint8_t *PRG = memregion("fdc_data")->base();

	// TODO: this loading shouldn't happen dynamically but actually be tied to specific floppy configs
	// pc9801 has no floppy as default
	// pc9801f has an internal 2DD disk drive
	// pc9801m has an internal 2HD
	// and ofc you can actually mount external units,
	// cfr. PC-9801-08 (2dd), PC-9801-15 (8' unit) and likely others.
	ROM = memregion("fdc_bios_2dd")->base();
	op_mode = (ioport("ROM_LOAD")->read() & 2) >> 1;

	for(int i=0;i<0x1000;i++)
		ROM[i] = PRG[i+op_mode*0x8000];

	ROM = memregion("fdc_bios_2hd")->base();
	op_mode = ioport("ROM_LOAD")->read() & 1;

	for(int i=0;i<0x1000;i++)
		ROM[i] = PRG[i+op_mode*0x8000+0x10000];

	m_beeper->set_state(0);
}

MACHINE_RESET_MEMBER(pc9801vm_state,pc9801rs)
{
	MACHINE_RESET_CALL_MEMBER(pc9801_common);

	m_gate_a20 = 0;
	m_fdc_mode = 3;
	m_fdc_3mode.dev_sel = 2;
	m_fdc_3mode.access_144mb = false;
	fdc_set_density_mode(true); // 2HD
	// 0xfb on PC98XL
	// TODO: breaks UART setup for pc9801rs
	// m_dma_access_ctrl = 0xfe;
	m_dma_access_ctrl = 0;
	m_ide_sel = 0;
	m_maincpu->set_input_line(INPUT_LINE_A20, m_gate_a20);

	if(memregion("ide"))
	{
		if(!(ioport("ROM_LOAD")->read() & 4))
			m_maincpu->space(AS_PROGRAM).install_rom(0xd8000, 0xd9fff, memregion("ide")->base());
		else
			m_maincpu->space(AS_PROGRAM).install_rom(0xd8000, 0xd9fff, memregion("ide")->base() + 0x2000);
	}

	m_dac1bit_disable = true;
}

MACHINE_RESET_MEMBER(pc9801bx_state,pc9801bx2)
{
	MACHINE_RESET_CALL_MEMBER(pc9801rs);

	// TODO: if returning default 0xfe / 0xfb then it never ever surpass the "SYSTEM SHUTDOWN" even with a soft reset
	m_dma_access_ctrl = 0x00;
}

void pc9801_state::vrtc_irq(int state)
{
	if(state)
		m_pic1->ir2_w(1);
}


void pc98_base_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_PC98_FORMAT);
	fr.add(FLOPPY_PC98FDI_FORMAT);
	fr.add(FLOPPY_FDD_FORMAT);
	fr.add(FLOPPY_DCP_FORMAT);
	fr.add(FLOPPY_DIP_FORMAT);
	fr.add(FLOPPY_NFD_FORMAT);
}

TIMER_DEVICE_CALLBACK_MEMBER( pc9801_state::mouse_irq_cb )
{
	// irq mask
	if((m_mouse.control & 0x10) == 0)
	{
		m_mouse.freq_index ++;

//      logerror("%02x\n",m_mouse.freq_index);
		if(m_mouse.freq_index > m_mouse.freq_reg)
		{
//          logerror("irq %02x\n",m_mouse.freq_reg);
			m_mouse.freq_index = 0;
			m_pic2->ir5_w(0);
			m_pic2->ir5_w(1);
		}
	}
}

void pc9801_atapi_devices(device_slot_interface &device)
{
	device.option_add("pc98_cd", PC98_CD);
}

void pc9801_state::config_video(machine_config &config)
{
	m_hgdc[0]->set_addrmap(0, &pc9801_state::upd7220_1_map);
	m_hgdc[0]->set_draw_text(FUNC(pc9801_state::hgdc_draw_text));
	m_hgdc[0]->vsync_wr_callback().set(m_hgdc[1], FUNC(upd7220_device::ext_sync_w));
	m_hgdc[0]->vsync_wr_callback().append(FUNC(pc9801_state::vrtc_irq));

	m_hgdc[1]->set_addrmap(0, &pc9801_state::upd7220_2_map);
	m_hgdc[1]->set_display_pixels(FUNC(pc9801_state::hgdc_display_pixels));
}


void pc9801_state::config_keyboard(machine_config &config)
{
	I8251(config, m_sio_kbd, 0);
	m_sio_kbd->txd_handler().set("keyb", FUNC(pc98_kbd_device::input_txd));
	m_sio_kbd->dtr_handler().set("keyb", FUNC(pc98_kbd_device::input_rty));
	m_sio_kbd->rts_handler().set("keyb", FUNC(pc98_kbd_device::input_kbde));
	m_sio_kbd->write_cts(0);
	m_sio_kbd->write_dsr(0);
	m_sio_kbd->rxrdy_handler().set(m_pic1, FUNC(pic8259_device::ir1_w));

	clock_device &kbd_clock(CLOCK(config, "kbd_clock", 19'200));
	kbd_clock.signal_handler().set(m_sio_kbd, FUNC(i8251_device::write_rxc));
	kbd_clock.signal_handler().append(m_sio_kbd, FUNC(i8251_device::write_txc));

	PC98_KBD(config, m_keyb, 0);
	m_keyb->rxd_callback().set("sio_kbd", FUNC(i8251_device::write_rxd));
}

void pc9801_state::pit_clock_config(machine_config &config, const XTAL clock)
{
	m_pit->set_clk<0>(clock);
	m_pit->set_clk<1>(clock);
	m_pit->set_clk<2>(clock);
}

void pc9801_state::pc9801_mouse(machine_config &config)
{
	I8255(config, m_ppi_mouse);
	m_ppi_mouse->in_pa_callback().set(FUNC(pc9801_state::ppi_mouse_porta_r));
	m_ppi_mouse->out_pa_callback().set(FUNC(pc9801_state::ppi_mouse_porta_w));
	// Regular vanilla doesn't have readouts of these
	// (since mouse isn't built-in but comes from an external board at best)
//  m_ppi_mouse->in_pb_callback().set_ioport("DSW3");
	m_ppi_mouse->in_pb_callback().set_constant(0xff);
	m_ppi_mouse->out_pb_callback().set(FUNC(pc9801_state::ppi_mouse_portb_w));
//  m_ppi_mouse->in_pc_callback().set_ioport("DSW4");
	m_ppi_mouse->in_pc_callback().set_constant(0xff);
	m_ppi_mouse->out_pc_callback().set(FUNC(pc9801_state::ppi_mouse_portc_w));

	// TODO: timing is configurable
	TIMER(config, "mouse_timer").configure_periodic(FUNC(pc9801_state::mouse_irq_cb), attotime::from_hz(120));
}

void pc9801_state::pc9801_cbus(machine_config &config)
{
	PC9801CBUS_SLOT(config, m_cbus[0], pc9801_cbus_devices, "pc9801_26");
	m_cbus[0]->set_memspace(m_maincpu, AS_PROGRAM);
	m_cbus[0]->set_iospace(m_maincpu, AS_IO);
	m_cbus[0]->int_cb<0>().set("ir3", FUNC(input_merger_device::in_w<0>));
	m_cbus[0]->int_cb<1>().set("ir5", FUNC(input_merger_device::in_w<0>));
	m_cbus[0]->int_cb<2>().set("ir6", FUNC(input_merger_device::in_w<0>));
	m_cbus[0]->int_cb<3>().set("ir9", FUNC(input_merger_device::in_w<0>));
	m_cbus[0]->int_cb<4>().set("pic8259_slave", FUNC(pic8259_device::ir2_w));
	m_cbus[0]->int_cb<5>().set("ir12", FUNC(input_merger_device::in_w<0>));
	m_cbus[0]->int_cb<6>().set("ir13", FUNC(input_merger_device::in_w<0>));

	PC9801CBUS_SLOT(config, m_cbus[1], pc9801_cbus_devices, nullptr);
	m_cbus[1]->set_memspace(m_maincpu, AS_PROGRAM);
	m_cbus[1]->set_iospace(m_maincpu, AS_IO);
	m_cbus[1]->int_cb<0>().set("ir3", FUNC(input_merger_device::in_w<1>));
	m_cbus[1]->int_cb<1>().set("ir5", FUNC(input_merger_device::in_w<1>));
	m_cbus[1]->int_cb<2>().set("ir6", FUNC(input_merger_device::in_w<1>));
	m_cbus[1]->int_cb<3>().set("ir9", FUNC(input_merger_device::in_w<1>));
	m_cbus[1]->int_cb<4>().set("pic8259_slave", FUNC(pic8259_device::ir3_w));
	m_cbus[1]->int_cb<5>().set("ir12", FUNC(input_merger_device::in_w<1>));
	m_cbus[1]->int_cb<6>().set("ir13", FUNC(input_merger_device::in_w<1>));
//  TODO: six max slots

	INPUT_MERGER_ANY_HIGH(config, "ir3").output_handler().set("pic8259_master", FUNC(pic8259_device::ir3_w));
	INPUT_MERGER_ANY_HIGH(config, "ir5").output_handler().set("pic8259_master", FUNC(pic8259_device::ir5_w));
	INPUT_MERGER_ANY_HIGH(config, "ir6").output_handler().set("pic8259_master", FUNC(pic8259_device::ir6_w));
	INPUT_MERGER_ANY_HIGH(config, "ir9").output_handler().set("pic8259_slave", FUNC(pic8259_device::ir1_w));
	INPUT_MERGER_ANY_HIGH(config, "ir12").output_handler().set("pic8259_slave", FUNC(pic8259_device::ir4_w));
	INPUT_MERGER_ANY_HIGH(config, "ir13").output_handler().set("pic8259_slave", FUNC(pic8259_device::ir5_w));
}

void pc9801_state::pc9801_sasi(machine_config &config)
{
	SCSI_PORT(config, m_sasibus, 0);
	m_sasibus->set_data_input_buffer("sasi_data_in");
	m_sasibus->io_handler().set(FUNC(pc9801_state::write_sasi_io)); // bit2
	m_sasibus->cd_handler().set("sasi_ctrl_in", FUNC(input_buffer_device::write_bit3));
	m_sasibus->msg_handler().set("sasi_ctrl_in", FUNC(input_buffer_device::write_bit4));
	m_sasibus->bsy_handler().set("sasi_ctrl_in", FUNC(input_buffer_device::write_bit5));
	m_sasibus->ack_handler().set("sasi_ctrl_in", FUNC(input_buffer_device::write_bit6));
	m_sasibus->req_handler().set(FUNC(pc9801_state::write_sasi_req));
	m_sasibus->set_slot_device(1, "harddisk", PC9801_SASI, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_0));

	output_latch_device &sasi_out(OUTPUT_LATCH(config, "sasi_data_out"));
	m_sasibus->set_output_latch(sasi_out);
	INPUT_BUFFER(config, "sasi_data_in");
	INPUT_BUFFER(config, "sasi_ctrl_in");

	m_dmac->in_ior_callback<0>().set(FUNC(pc9801_state::sasi_data_r));
	m_dmac->out_iow_callback<0>().set(FUNC(pc9801_state::sasi_data_w));
}

void pc9801vm_state::cdrom_headphones(device_t *device)
{
	cdda_device *cdda = device->subdevice<cdda_device>("cdda");
	cdda->add_route(0, "^^headphone", 1.0, 0);
	cdda->add_route(1, "^^headphone", 1.0, 1);
}

void pc9801vm_state::pc9801_ide(machine_config &config)
{
	SPEAKER(config, "headphone", 2).front();
	ATA_INTERFACE(config, m_ide[0]).options(ata_devices, "hdd", nullptr, false);
	m_ide[0]->irq_handler().set("ideirq", FUNC(input_merger_device::in_w<0>));
	ATA_INTERFACE(config, m_ide[1]).options(pc9801_atapi_devices, "pc98_cd", nullptr, false);
	m_ide[1]->irq_handler().set("ideirq", FUNC(input_merger_device::in_w<1>));
	m_ide[1]->slot(0).set_option_machine_config("pc98_cd", cdrom_headphones);

	INPUT_MERGER_ANY_HIGH(config, "ideirq").output_handler().set("pic8259_slave", FUNC(pic8259_device::ir1_w));

	SOFTWARE_LIST(config, "hdd_list").set_original("pc98_hdd");
	SOFTWARE_LIST(config, "cd_list").set_original("pc98_cd");
}

void pc98_base_state::pc9801_serial(machine_config &config)
{
	// clocked by PIT channel 2
	I8251(config, m_sio_rs, 0);
	m_sio_rs->txd_handler().set("serial", FUNC(rs232_port_device::write_txd));
	m_sio_rs->rts_handler().set("serial", FUNC(rs232_port_device::write_rts));
	m_sio_rs->dtr_handler().set("serial", FUNC(rs232_port_device::write_dtr));
	m_sio_rs->rxrdy_handler().set([this] (int state) { update_uart_irq<0>(state); });
	m_sio_rs->txempty_handler().set([this] (int state) { update_uart_irq<1>(state); });
	m_sio_rs->txrdy_handler().set([this] (int state) { update_uart_irq<2>(state); });

	rs232_port_device &rs232(RS232_PORT(config, "serial", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_sio_rs, FUNC(i8251_device::write_rxd));
	rs232.cts_handler().set(m_sio_rs, FUNC(i8251_device::write_cts));
	rs232.dsr_handler().set(m_sio_rs, FUNC(i8251_device::write_dsr));
}

void pc9801_state::pc9801_common(machine_config &config)
{
	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(MAIN_CLOCK_X1); // heartbeat IRQ
	m_pit->out_handler<0>().set(m_pic1, FUNC(pic8259_device::ir0_w));
	m_pit->set_clk<1>(MAIN_CLOCK_X1); // Memory Refresh
	m_pit->set_clk<2>(MAIN_CLOCK_X1); // RS-232C
	m_pit->out_handler<2>().set(m_sio_rs, FUNC(i8251_device::write_txc));
	m_pit->out_handler<2>().append(m_sio_rs, FUNC(i8251_device::write_rxc));

	AM9517A(config, m_dmac, 5000000); // unknown clock, TODO: check channels 0 - 1
	m_dmac->dreq_active_low();
	m_dmac->out_hreq_callback().set(FUNC(pc9801_state::dma_hrq_changed));
	m_dmac->out_eop_callback().set(FUNC(pc9801_state::tc_w));
	m_dmac->in_memr_callback().set(FUNC(pc9801_state::dma_read_byte));
	m_dmac->out_memw_callback().set(FUNC(pc9801_state::dma_write_byte));
	m_dmac->in_ior_callback<2>().set(m_fdc_2hd, FUNC(upd765a_device::dma_r));
	m_dmac->out_iow_callback<2>().set(m_fdc_2hd, FUNC(upd765a_device::dma_w));
	m_dmac->out_dack_callback<0>().set(FUNC(pc9801_state::dack0_w));
	m_dmac->out_dack_callback<1>().set(FUNC(pc9801_state::dack1_w));
	m_dmac->out_dack_callback<2>().set(FUNC(pc9801_state::dack2_w));
	m_dmac->out_dack_callback<3>().set(FUNC(pc9801_state::dack3_w));

	PIC8259(config, m_pic1, 0);
	m_pic1->out_int_callback().set_inputline(m_maincpu, 0);
	m_pic1->in_sp_callback().set_constant(1);
	m_pic1->read_slave_ack_callback().set(FUNC(pc9801_state::get_slave_ack));

	PIC8259(config, m_pic2, 0);
	m_pic2->out_int_callback().set(m_pic1, FUNC(pic8259_device::ir7_w)); // TODO: Check ir7_w
	m_pic2->in_sp_callback().set_constant(0);

	I8255(config, m_ppi_sys, 0);
	m_ppi_sys->in_pa_callback().set_ioport("DSW2");
	m_ppi_sys->in_pb_callback().set(FUNC(pc9801_state::ppi_sys_portb_r));
	m_ppi_sys->in_pc_callback().set_constant(0xa0); // 0x80 cpu triple fault reset flag?
//  m_ppi_sys->out_pc_callback().set(FUNC(pc9801_state::ppi_sys_portc_w));

	I8255(config, m_ppi_prn, 0);
	// TODO: other ports
	m_ppi_prn->in_pb_callback().set(FUNC(pc9801_state::ppi_prn_portb_r));

	config_keyboard(config);
	pc9801_mouse(config);
	pc9801_cbus(config);

	pc9801_serial(config);

	PC98_MEMSW(config, m_memsw, 0);

	UPD765A(config, m_fdc_2hd, 8'000'000, true, true);
	m_fdc_2hd->intrq_wr_callback().set(m_pic2, FUNC(pic8259_device::ir3_w));
	m_fdc_2hd->drq_wr_callback().set(m_dmac, FUNC(am9517a_device::dreq2_w)).invert();
	FLOPPY_CONNECTOR(config, "upd765_2hd:0", pc9801_floppies, "525hd", pc9801_state::floppy_formats);//.enable_sound(true);
	FLOPPY_CONNECTOR(config, "upd765_2hd:1", pc9801_floppies, "525hd", pc9801_state::floppy_formats);//.enable_sound(true);

	SOFTWARE_LIST(config, "disk_list").set_original("pc98");

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(21.0526_MHz_XTAL, 848, 0, 640, 440, 0, 400);
	m_screen->set_screen_update(FUNC(pc9801_state::screen_update));

	UPD7220(config, m_hgdc[0], 21.0526_MHz_XTAL / 8, "screen");
	UPD7220(config, m_hgdc[1], 21.0526_MHz_XTAL / 8, "screen");
	config_video(config);

	SPEAKER(config, "mono").front_center();

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_pc9801);
}

void pc9801_state::config_floppy_525hd(machine_config &config)
{
	FLOPPY_CONNECTOR(config.replace(), "upd765_2hd:0", pc9801_floppies, "525hd", pc9801_state::floppy_formats);
	FLOPPY_CONNECTOR(config.replace(), "upd765_2hd:1", pc9801_floppies, "525hd", pc9801_state::floppy_formats);
}

void pc9801_state::config_floppy_35hd(machine_config &config)
{
	FLOPPY_CONNECTOR(config.replace(), "upd765_2hd:0", pc9801_floppies, "35hd", pc9801_state::floppy_formats);
	FLOPPY_CONNECTOR(config.replace(), "upd765_2hd:1", pc9801_floppies, "35hd", pc9801_state::floppy_formats);
}

void pc9801_state::pc9801(machine_config &config)
{
	I8086(config, m_maincpu, 5000000); // 5 MHz for vanilla, 8 MHz for direct children
	m_maincpu->set_addrmap(AS_PROGRAM, &pc9801_state::pc9801_map);
	m_maincpu->set_addrmap(AS_IO, &pc9801_state::pc9801_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	pc9801_common(config);
	m_ppi_sys->out_pc_callback().set(FUNC(pc9801_state::ppi_sys_beep_portc_w));

	MCFG_MACHINE_START_OVERRIDE(pc9801_state, pc9801f)
	MCFG_MACHINE_RESET_OVERRIDE(pc9801_state, pc9801f)

	// TODO: maybe force dips to avoid beep error
	RAM(config, m_ram).set_default_size("640K").set_extra_options("128K,256K,384K,512K");

	UPD765A(config, m_fdc_2dd, 8'000'000, false, true);
	m_fdc_2dd->intrq_wr_callback().set(FUNC(pc9801_state::fdc_2dd_irq));
	m_fdc_2dd->drq_wr_callback().set(m_dmac, FUNC(am9517a_device::dreq3_w)).invert();
	FLOPPY_CONNECTOR(config, "upd765_2dd:0", pc9801_floppies, "525dd", pc9801_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "upd765_2dd:1", pc9801_floppies, "525dd", pc9801_state::floppy_formats);

	pc9801_sasi(config);
	UPD1990A(config, m_rtc);

	m_dmac->in_ior_callback<3>().set(m_fdc_2dd, FUNC(upd765a_device::dma_r));
	m_dmac->out_iow_callback<3>().set(m_fdc_2dd, FUNC(upd765a_device::dma_w));

	BEEP(config, m_beeper, 2400).add_route(ALL_OUTPUTS, "mono", 0.15);
	PALETTE(config, m_palette, FUNC(pc9801_state::pc9801_palette), 16);
}

void pc9801vm_state::pc9801rs(machine_config &config)
{
	I386SX(config, m_maincpu, MAIN_CLOCK_X1*8); // unknown clock
	m_maincpu->set_addrmap(AS_PROGRAM, &pc9801vm_state::pc9801rs_map);
	m_maincpu->set_addrmap(AS_IO, &pc9801vm_state::pc9801rs_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	pc9801_common(config);
	m_ppi_sys->out_pc_callback().set(FUNC(pc9801vm_state::ppi_sys_dac_portc_w));
	// TODO: verify if it needs invert();
	m_pit->out_handler<1>().set( m_dac1bit, FUNC(speaker_sound_device::level_w));

	m_ppi_mouse->in_pb_callback().set(FUNC(pc9801vm_state::ppi_mouse_portb_r));
	m_ppi_mouse->in_pc_callback().set(FUNC(pc9801vm_state::ppi_mouse_portc_r));

	ADDRESS_MAP_BANK(config, m_ipl).set_map(&pc9801vm_state::ipl_bank).set_options(ENDIANNESS_LITTLE, 16, 18, 0x18000);

	MCFG_MACHINE_START_OVERRIDE(pc9801vm_state, pc9801rs)
	MCFG_MACHINE_RESET_OVERRIDE(pc9801vm_state, pc9801rs)

	m_dmac->set_clock(MAIN_CLOCK_X1*8); // unknown clock

	pc9801_ide(config);
	UPD4990A(config, m_rtc);

	RAM(config, m_ram).set_default_size("1664K").set_extra_options("640K,3712K,7808K,14M");

	m_fdc_2hd->intrq_wr_callback().set(FUNC(pc9801vm_state::fdc_irq_w));
	m_fdc_2hd->drq_wr_callback().set(FUNC(pc9801vm_state::fdc_drq_w));
	// ch. 3 used when in 2DD mode (mightyhd, rogue)
	// TODO: should lock as everything else depending on mode bit 0
	m_dmac->in_ior_callback<3>().set(m_fdc_2hd, FUNC(upd765a_device::dma_r));
	m_dmac->out_iow_callback<3>().set(m_fdc_2hd, FUNC(upd765a_device::dma_w));

	m_hgdc[1]->set_addrmap(0, &pc9801vm_state::upd7220_grcg_2_map);

//  DAC_1BIT(config, m_dac1bit, 0).set_output_range(-1, 1).add_route(ALL_OUTPUTS, "mono", 0.15);
	SPEAKER_SOUND(config, m_dac1bit).add_route(ALL_OUTPUTS, "mono", 0.40);
	PALETTE(config, m_palette, FUNC(pc9801vm_state::pc9801_palette), 16 + 16);
}

void pc9801vm_state::pc9801vm(machine_config &config)
{
	pc9801rs(config);
	V30(config.replace(), m_maincpu, 10000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc9801vm_state::pc9801ux_map);
	m_maincpu->set_addrmap(AS_IO, &pc9801vm_state::pc9801ux_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	m_ram->set_default_size("640K").set_extra_options("640K"); // ???

	MCFG_MACHINE_START_OVERRIDE(pc9801vm_state, pc9801rs)
	MCFG_MACHINE_RESET_OVERRIDE(pc9801vm_state, pc9801_common)
}

// UV is essentially a VM with 3.5 drives
// Released as UV2 (384KB RAM), UV21 (640KB RAM) then UV11 (UV21 but smaller?)
void pc9801vm_state::pc9801uv(machine_config &config)
{
	pc9801vm(config);

	config_floppy_35hd(config);

	m_ram->set_default_size("640K").set_extra_options("384K");
}

void pc9801vm_state::pc9801ux(machine_config &config)
{
	pc9801rs(config);
	i80286_cpu_device &maincpu(I80286(config.replace(), m_maincpu, 10000000));
	maincpu.set_addrmap(AS_PROGRAM, &pc9801vm_state::pc9801ux_map);
	maincpu.set_addrmap(AS_IO, &pc9801vm_state::pc9801ux_io);
	maincpu.set_a20_callback(FUNC(pc9801vm_state::a20_286));
	maincpu.set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	config_floppy_35hd(config);
//  AM9157A(config, "i8237", 10000000); // unknown clock
}

void pc9801vm_state::pc9801dx(machine_config &config)
{
	pc9801rs(config);
	i80286_cpu_device &maincpu(I80286(config.replace(), m_maincpu, 12000000));
	maincpu.set_addrmap(AS_PROGRAM, &pc9801vm_state::pc9801ux_map);
	maincpu.set_addrmap(AS_IO, &pc9801vm_state::pc9801ux_io);
	maincpu.set_a20_callback(FUNC(pc9801vm_state::a20_286));
	maincpu.set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	config_floppy_525hd(config);
//  AM9157A(config, "i8237", 10000000); // unknown clock
}

void pc9801vm_state::pc9801vx(machine_config &config)
{
	pc9801ux(config);

	config_floppy_525hd(config);

	// TODO: EGC initial buggy revision
	// Reportedly has a bug with a RMW op, details TBD
	// ...

	// minimum RAM: 640 kB
	// maximum RAM: 8.6 MB
	// GDC & EGC, DAC1BIT built-in
	// Either 2x 5.25 or 2x 3.5 internal floppy drives
	// 4x C-Bus slots (3x plus 1x dedicated RAM?)
}

void pc9801us_state::pc9801us(machine_config &config)
{
	pc9801rs(config);
	const XTAL xtal = BASE_CLOCK / 2; // ~16 MHz
	I386SX(config.replace(), m_maincpu, xtal);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc9801us_state::pc9801rs_map);
	m_maincpu->set_addrmap(AS_IO, &pc9801us_state::pc9801us_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	config_floppy_35hd(config);

	pit_clock_config(config, xtal / 4);

	PC98_119_KBD(config.replace(), m_keyb, 0);
	m_keyb->rxd_callback().set("sio_kbd", FUNC(i8251_device::write_rxd));

	PC98_SDIP(config, "sdip", 0);
}

void pc9801us_state::pc9801fs(machine_config &config)
{
	pc9801rs(config);
	const XTAL xtal = XTAL(20'000'000); // ~20 MHz
	I386SX(config.replace(), m_maincpu, xtal);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc9801us_state::pc9801rs_map);
	m_maincpu->set_addrmap(AS_IO, &pc9801us_state::pc9801us_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	// optional 3'5 floppies x2
	config_floppy_525hd(config);

	// optional SCSI HDD

	pit_clock_config(config, xtal / 4);

//  PC98_119_KBD(config.replace(), m_keyb, 0);
//  m_keyb->rxd_callback().set("sio_kbd", FUNC(i8251_device::write_rxd));

	PC98_SDIP(config, "sdip", 0);
}

void pc9801bx_state::pc9801bx2(machine_config &config)
{
	pc9801rs(config);
	const XTAL xtal = XTAL(25'000'000);
	I486(config.replace(), m_maincpu, xtal); // i486sx, ODP upgradeable
	m_maincpu->set_addrmap(AS_PROGRAM, &pc9801bx_state::pc9801bx2_map);
	m_maincpu->set_addrmap(AS_IO, &pc9801bx_state::pc9801bx2_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	MCFG_MACHINE_START_OVERRIDE(pc9801bx_state, pc9801bx2)
	MCFG_MACHINE_RESET_OVERRIDE(pc9801bx_state, pc9801bx2)

	pit_clock_config(config, xtal / 4); // unknown, fixes timer error at POST, /4 ~ /7

	PC98_SDIP(config, "sdip", 0);

	// minimum RAM: 1.8 / 3.6 MB (?)
	// maximum RAM: 19.6 MB
	// GDC & EGC, DAC1BIT built-in
	// 2x 3.5/5.25 internal floppy drives or 1x 3.5 and 120MB IDE HDD
	// 1x mountable File Bay
	// 3x C-Bus slots
}

/* took from "raw" memory dump */
#define LOAD_IDE_ROM \
	ROM_REGION( 0x4000, "ide", ROMREGION_ERASEVAL(0xcb) ) \
	ROM_LOAD( "d8000.rom", 0x0000, 0x2000, BAD_DUMP CRC(5dda57cc) SHA1(d0dead41c5b763008a4d777aedddce651eb6dcbb) ) \
	ROM_IGNORE( 0x2000 ) \
	ROM_IGNORE( 0x2000 ) \
	ROM_IGNORE( 0x2000 )

// all of these are half size :/
#define LOAD_KANJI_ROMS \
	ROM_REGION( 0x80000, "raw_kanji", ROMREGION_ERASEFF ) \
	ROM_LOAD16_BYTE( "24256c-x01.bin", 0x00000, 0x4000, BAD_DUMP CRC(28ec1375) SHA1(9d8e98e703ce0f483df17c79f7e841c5c5cd1692) ) \
	ROM_CONTINUE(                      0x20000, 0x4000  ) \
	ROM_LOAD16_BYTE( "24256c-x02.bin", 0x00001, 0x4000, BAD_DUMP CRC(90985158) SHA1(78fb106131a3f4eb054e87e00fe4f41193416d65) ) \
	ROM_CONTINUE(                      0x20001, 0x4000  ) \
	ROM_LOAD16_BYTE( "24256c-x03.bin", 0x40000, 0x4000, BAD_DUMP CRC(d4893543) SHA1(eb8c1bee0f694e1e0c145a24152222d4e444e86f) ) \
	ROM_CONTINUE(                      0x60000, 0x4000  ) \
	ROM_LOAD16_BYTE( "24256c-x04.bin", 0x40001, 0x4000, BAD_DUMP CRC(5dec0fc2) SHA1(41000da14d0805ed0801b31eb60623552e50e41c) ) \
	ROM_CONTINUE(                      0x60001, 0x4000  ) \
	ROM_REGION( 0x100000, "kanji", ROMREGION_ERASEFF ) \
	ROM_REGION( 0x80000, "new_chargen", ROMREGION_ERASEFF )

/*
"vanilla" - μPD8086, 5 MHz?
*/

ROM_START( pc9801 )
	ROM_REGION16_LE( 0x18000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD16_BYTE( "ruq2g 06.bin", 0x14000, 0x2000, CRC(ee7b336b) SHA1(0691ebfcb9a8ce56ca303c552f634e953bb2ea7c) )
	ROM_LOAD16_BYTE( "ruq4f 06.bin", 0x14001, 0x2000, CRC(1e5c38c4) SHA1(5a8681ae0b1c3248d81a5b6707595d85cabe6bc8) )
	ROM_LOAD16_BYTE( "ruq2f 06.bin", 0x10000, 0x2000, CRC(be95afa5) SHA1(137fc4dd10ecc9f058b819df89a67df819a6509c) )
	ROM_LOAD16_BYTE( "ruq4e 06.bin", 0x10001, 0x2000, CRC(bc425f21) SHA1(f688ef89ebe3993dcbf70608d996067e92176be1) )
	ROM_LOAD16_BYTE( "ruq2e 06.bin", 0x0c000, 0x2000, CRC(16a3eaca) SHA1(345c1764e1b8aa5f3baa876658cf4cd224351fae) )
	ROM_LOAD16_BYTE( "ruq4d 06.bin", 0x0c001, 0x2000, CRC(0ca07388) SHA1(bdd564d19fcfa3dff8a695e2386c94defadcb164) )
	ROM_LOAD16_BYTE( "ruq2d 06.bin", 0x08000, 0x2000, CRC(907d0263) SHA1(30ba910424c99ae4c54eb2be5472258a3d5e4f29) )
	ROM_LOAD16_BYTE( "ruq4b 06.bin", 0x08001, 0x2000, CRC(b41d15e7) SHA1(321ec22e50fd2ee69f73c1e3f11c9fd07afa46fc) )
	ROM_LOAD16_BYTE( "ruq1f 06.bin", 0x00000, 0x2000, CRC(12d6ea62) SHA1(3a612428aaf3120ec00c10d709674535668f1d65) )
	ROM_LOAD16_BYTE( "ruq4h 06.bin", 0x00001, 0x2000, CRC(348992b9) SHA1(f5735f57305fd6585b2db1c81d34bb7ba2ed7510) )
	ROM_LOAD16_BYTE( "ruq1g 06.bin", 0x04000, 0x2000, CRC(d4ea8a62) SHA1(c899ea64ce8652a5b6976d62466efe2864cfb049) )
	ROM_LOAD16_BYTE( "ruq4g 06.bin", 0x04001, 0x2000, CRC(c1470ae5) SHA1(4eb31b2ad0b8f0dfad99bb67ada9e5853d5af4a1) )

	ROM_REGION16_LE( 0x1000, "fdc_bios_2dd", ROMREGION_ERASEFF )

	ROM_REGION16_LE( 0x1000, "fdc_bios_2hd", ROMREGION_ERASEFF )

	ROM_REGION( 0x20000, "fdc_data", ROMREGION_ERASEFF )

	ROM_REGION( 0x800, "kbd_mcu", ROMREGION_ERASEFF)
	ROM_LOAD( "mcu.bin", 0x0000, 0x0800, NO_DUMP ) //connected through a i8251 UART, needs decapping

	ROM_REGION( 0x80000, "chargen", 0 )
	// TODO: original dump, needs heavy bitswap mods
	ROM_LOAD( "sfz4w 00.bin",   0x00000, 0x02000, CRC(11197271) SHA1(8dbd2f25daeed545ea2c74d849f0a209ceaf4dd7) )
	// taken from i386 model
	ROM_LOAD( "d23128c-17.bin", 0x00000, 0x00800, BAD_DUMP CRC(eea57180) SHA1(4aa037c684b72ad4521212928137d3369174eb1e) )
	// bad dump, 8x16 charset? (it's on the kanji board)
	ROM_LOAD("hn613128pac8.bin",0x00800, 0x01000, BAD_DUMP CRC(b5a15b5c) SHA1(e5f071edb72a5e9a8b8b1c23cf94a74d24cb648e) )

	LOAD_KANJI_ROMS
ROM_END


/*
F - 8086 8
*/

ROM_START( pc9801f )
	ROM_REGION16_LE( 0x18000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD16_BYTE( "urm01-02.bin", 0x00000, 0x4000, CRC(cde04615) SHA1(8f6fb587c0522af7a8131b45d13f8ae8fc60e8cd) )
	ROM_LOAD16_BYTE( "urm02-02.bin", 0x00001, 0x4000, CRC(9e39b8d1) SHA1(df1f3467050a41537cb9d071e4034f0506f07eda) )
	ROM_LOAD16_BYTE( "urm03-02.bin", 0x08000, 0x4000, CRC(95e79064) SHA1(c27d96949fad82aeb26e316200c15a4891e1063f) )
	ROM_LOAD16_BYTE( "urm04-02.bin", 0x08001, 0x4000, CRC(e4855a53) SHA1(223f66482c77409706cfc64c214cec7237c364e9) )
	ROM_LOAD16_BYTE( "urm05-02.bin", 0x10000, 0x4000, CRC(ffefec65) SHA1(106e0d920e857e59da12225a489ca2756ca405c1) )
	ROM_LOAD16_BYTE( "urm06-02.bin", 0x10001, 0x4000, CRC(1147760b) SHA1(4e0299091dfd53ac7988d40c5a6775a10389faac) )

	ROM_REGION16_LE( 0x1000, "fdc_bios_2dd", ROMREGION_ERASEFF )

	ROM_REGION16_LE( 0x1000, "fdc_bios_2hd", ROMREGION_ERASEFF )

	ROM_REGION( 0x20000, "fdc_data", ROMREGION_ERASEFF )
	// 2dd fdc bios, presumably bad size (should be 0x800 for each rom)
	ROM_LOAD16_BYTE( "urf01-01.bin", 0x00000, 0x4000, BAD_DUMP CRC(2f5ae147) SHA1(69eb264d520a8fc826310b4fce3c8323867520ee) )
	ROM_LOAD16_BYTE( "urf02-01.bin", 0x00001, 0x4000, BAD_DUMP CRC(62a86928) SHA1(4160a6db096dbeff18e50cbee98f5d5c1a29e2d1) )
	ROM_LOAD( "2hdif.rom", 0x10000, 0x1000, BAD_DUMP CRC(9652011b) SHA1(b607707d74b5a7d3ba211825de31a8f32aec8146) ) // needs dumping from a board

	ROM_REGION( 0x800, "kbd_mcu", ROMREGION_ERASEFF)
	ROM_LOAD( "mcu.bin", 0x0000, 0x0800, NO_DUMP ) //connected through a i8251 UART, needs decapping

	ROM_REGION( 0x80000, "chargen", 0 )
	// note: ROM labels of following two may be swapped
	//original is a bad dump, this is taken from i386 model
	ROM_LOAD( "d23128c-17.bin", 0x00000, 0x00800, BAD_DUMP CRC(eea57180) SHA1(4aa037c684b72ad4521212928137d3369174eb1e) )
	//bad dump, 8x16 charset? (it's on the kanji board)
	ROM_LOAD("hn613128pac8.bin",0x00800, 0x01000, BAD_DUMP CRC(b5a15b5c) SHA1(e5f071edb72a5e9a8b8b1c23cf94a74d24cb648e) )

	LOAD_KANJI_ROMS
ROM_END

/*
VM - V30 8/10

TODO: missing itf roms, if they exist
*/

ROM_START( pc9801vm )
	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
//  ROM_LOAD( "itf_ux.rom",  0x10000, 0x08000, BAD_DUMP CRC(c7942563) SHA1(61bb210d64c7264be939b11df1e9cd14ffeee3c9) )
//  ROM_LOAD( "bios_vm.rom", 0x18000, 0x18000, CRC(2e2d7cee) SHA1(159549f845dc70bf61955f9469d2281a0131b47f) )
	// bios
	ROM_LOAD16_BYTE( "cpu_board_1a_23128e.bin",   0x10001, 0x4000, CRC(9965c914) SHA1(1ed318b774340bd532ef02ac02f39a012354dbf8) )
	ROM_LOAD16_BYTE( "cpu_board_4a_d23128ec.bin", 0x10000, 0x4000, CRC(e7c24a70) SHA1(cc9584b8e56b391f103e9d559d397d0bc6d00b35) )
	ROM_LOAD16_BYTE( "cpu_board_2a_d23c256ec.bin", 0x08001, 0x4000, CRC(3874970d) SHA1(e50ec5ae38f00dbfd156288dd42c7f2a2bf8bc35) )
	ROM_CONTINUE( 0x00001, 0x4000 )
	ROM_LOAD16_BYTE( "cpu_board_3a_23c256e.bin",   0x08000, 0x4000, CRC(4128276e) SHA1(32acb7eee779a31838a17ce51b05a9a987af4099) )
	ROM_CONTINUE( 0x00000, 0x4000 )

	ROM_REGION( 0x80000, "chargen", 0 )
//  ROM_LOAD( "font_vm.rom",     0x000000, 0x046800, BAD_DUMP CRC(456d9fc7) SHA1(78ba9960f135372825ab7244b5e4e73a810002ff) )
	// TODO: contains 8x8 "graphics" characters but we don't use them
	ROM_LOAD( "main_board_12f_d2364ec.bin", 0x000000, 0x002000, CRC(11197271) SHA1(8dbd2f25daeed545ea2c74d849f0a209ceaf4dd7) )

	ROM_REGION( 0x80000, "raw_kanji", ROMREGION_ERASEFF )
	// on main board, uPD23100 type roms
	// kanji and most other 16x16 characters
	ROM_LOAD16_BYTE( "main_board_12h_231000.bin", 0x00000, 0x20000, CRC(ecc2c062) SHA1(36c935c0f26c02a2b1ea46f5b6cd03fc11c7b003) )
	ROM_LOAD16_BYTE( "main_board_10h_231000.bin", 0x00001, 0x20000, CRC(91d78281) SHA1(85a18ad40e281e68071f91800201e43d78fb4f1c) )
	// 8x16 characters and the remaining 16x16 characters, with inverted bit order like 12f
	ROM_LOAD16_BYTE( "main_board_8h_d23256ac.bin", 0x40000, 0x04000, CRC(62a32ba6) SHA1(cdab480ae0dad9d128e52afb15e6c0b2b122cc3f) )
	ROM_CONTINUE( 0x40001, 0x04000 )

	ROM_REGION( 0x100000, "kanji", ROMREGION_ERASEFF )
	ROM_REGION( 0x80000, "new_chargen", ROMREGION_ERASEFF )

//  LOAD_KANJI_ROMS
//  LOAD_IDE_ROM
ROM_END

ROM_START( pc9801uv2 )
	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD16_BYTE( "d23c128ec-195.bin", 0x10000, 0x4000, CRC(082c86eb) SHA1(6f503b75906fd4932152f45c6d37c1e230934773) )
	ROM_LOAD16_BYTE( "d23c128ec-196.bin", 0x10001, 0x4000, CRC(d90b730b) SHA1(27f9b67c0454ee6107db20912f08f87ff682adcc) )
	ROM_LOAD16_BYTE( "d23c256ec-164.bin", 0x08000, 0x4000, CRC(d6cd9fef) SHA1(4ade6f891ee4c5ccb31031a520ab5ba757a6944c) )
	ROM_CONTINUE( 0x00000, 0x4000 )
	ROM_LOAD16_BYTE( "d23c256ec-165.bin", 0x08001, 0x4000, CRC(2d348381) SHA1(a1c7ebb7727380bcb879b2c609a1fe6cd5bfa0bb) )
	ROM_CONTINUE( 0x00001, 0x4000 )

	// borrowed from pc9801vm
	ROM_REGION( 0x80000, "chargen", 0 )
//  ROM_LOAD( "font_vm.rom",     0x000000, 0x046800, BAD_DUMP CRC(456d9fc7) SHA1(78ba9960f135372825ab7244b5e4e73a810002ff) )
	// TODO: contains 8x8 "graphics" characters but we don't use them
	ROM_LOAD( "main_board_12f_d2364ec.bin", 0x000000, 0x002000, CRC(11197271) SHA1(8dbd2f25daeed545ea2c74d849f0a209ceaf4dd7) )

	ROM_REGION( 0x80000, "raw_kanji", ROMREGION_ERASEFF )
	// on main board, uPD23100 type roms
	// kanji and most other 16x16 characters
	ROM_LOAD16_BYTE( "main_board_12h_231000.bin", 0x00000, 0x20000, CRC(ecc2c062) SHA1(36c935c0f26c02a2b1ea46f5b6cd03fc11c7b003) )
	ROM_LOAD16_BYTE( "main_board_10h_231000.bin", 0x00001, 0x20000, CRC(91d78281) SHA1(85a18ad40e281e68071f91800201e43d78fb4f1c) )
	// 8x16 characters and the remaining 16x16 characters, with inverted bit order like 12f
	ROM_LOAD16_BYTE( "main_board_8h_d23256ac.bin", 0x40000, 0x04000, CRC(62a32ba6) SHA1(cdab480ae0dad9d128e52afb15e6c0b2b122cc3f) )
	ROM_CONTINUE( 0x40001, 0x04000 )

	ROM_REGION( 0x100000, "kanji", ROMREGION_ERASEFF )
	ROM_REGION( 0x80000, "new_chargen", ROMREGION_ERASEFF )

//  LOAD_KANJI_ROMS
//  LOAD_IDE_ROM
ROM_END

/*
VX - 80286 10 + V30 8

UVPROM label on extension board for CPU board (4 * NEC D27C256D-15):
YLL01/YLL02/YLL03/YLL04
00
(C) '86 NEC

Dump coming from a dead machine
*/

ROM_START( pc9801vx )
	ROM_REGION16_LE( 0x20000, "biosrom", ROMREGION_ERASEFF )
	ROM_LOAD16_BYTE( "nec_d27c256d-15_cpu_extboard_yll01.bin", 0x00000, 0x08000, CRC(1b235313) SHA1(d2c5e2cea3ee0a643d3c5d384d134404b58db793) )
	ROM_LOAD16_BYTE( "nec_d27c256d-15_cpu_extboard_yll03.bin", 0x00001, 0x08000, CRC(33605ae3) SHA1(f644ff15c54c8568e643324f38aa3b6211912af0) )
	ROM_LOAD16_BYTE( "nec_d27c256d-15_cpu_extboard_yll02.bin", 0x10000, 0x08000, CRC(948f8658) SHA1(674378d4e90fee715ccfdd49378cd5c2fe8d7f62) )
	ROM_LOAD16_BYTE( "nec_d27c256d-15_cpu_extboard_yll04.bin", 0x10001, 0x08000, CRC(2ce2101b) SHA1(2158d022d5424daf6981bf4da0ab9613bf9646f5) )

	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	ROM_COPY( "biosrom", 0x18000, 0x10000, 0x08000 )  //ITF ROM
	ROM_COPY( "biosrom", 0x08000, 0x18000, 0x08000 )  //BIOS ROM
	ROM_COPY( "biosrom", 0x00000, 0x20000, 0x08000 )
	ROM_COPY( "biosrom", 0x10000, 0x28000, 0x08000 )

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font_ux.rom",     0x000000, 0x046800, BAD_DUMP CRC(19a76eeb) SHA1(96a006e8515157a624599c2b53a581ae0dd560fd) )

	LOAD_KANJI_ROMS
//  LOAD_IDE_ROM
ROM_END

/*
UX - 80286 10 + V30 8
*/

ROM_START( pc9801ux )
	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "itf_ux.rom",  0x10000, 0x08000, CRC(c7942563) SHA1(61bb210d64c7264be939b11df1e9cd14ffeee3c9) )
	ROM_LOAD( "bios_ux.rom", 0x18000, 0x18000, BAD_DUMP CRC(97375ca2) SHA1(bfe458f671d90692104d0640730972ca8dc0a100) )

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font_ux.rom",     0x000000, 0x046800, BAD_DUMP CRC(19a76eeb) SHA1(96a006e8515157a624599c2b53a581ae0dd560fd) )

	LOAD_KANJI_ROMS
//  LOAD_IDE_ROM
ROM_END

/*
DX - 80286 12 + V30 8
*/

ROM_START( pc9801dx )
	ROM_REGION16_LE( 0x40000, "biosrom", ROMREGION_ERASEFF )
	ROM_LOAD16_BYTE( "hdk01_02.bin",  0x000000, 0x020000, CRC(bf8b25fd) SHA1(e86eb6b46d73aad1cc96945bd34bd728d882583e) )
	ROM_LOAD16_BYTE( "hdk02_02.bin",  0x000001, 0x020000, CRC(37f21929) SHA1(1bb7c2f09eed399a78c3668f4193429a1980acc9) )

	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	ROM_COPY( "biosrom", 0x20000, 0x10000, 0x08000 )  //ITF ROM
	ROM_COPY( "biosrom", 0x28000, 0x18000, 0x08000 )  //BIOS ROM
	ROM_COPY( "biosrom", 0x30000, 0x20000, 0x08000 )
	ROM_COPY( "biosrom", 0x38000, 0x28000, 0x08000 )

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font_ux.rom",     0x000000, 0x046800, BAD_DUMP CRC(19a76eeb) SHA1(96a006e8515157a624599c2b53a581ae0dd560fd) )

	LOAD_KANJI_ROMS
//  LOAD_IDE_ROM
ROM_END

/*
US - i386SX @ 16 MHz
*/

ROM_START( pc9801us )
	ROM_REGION16_LE( 0x80000, "biosrom", ROMREGION_ERASEFF )
	ROM_LOAD( "lrh8a00.bin",  0x000000, 0x080000, CRC(a86d8cdb) SHA1(01c805274ca943c1febedda5ad85ba532aac949c) )

	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	// 0x0c000-0x0ffff sound ROM BIOS
	// 0x10000-0x13fff ^ mirror
	// 0x14000-0x17fff <empty>
	// 0x18000-0x191ff unknown, disk BIOS?
	// 0x20000-0x27fff ITF ROM
	// 0x40000-0x7ffff <empty>
	ROM_COPY( "biosrom", 0x20000, 0x10000, 0x08000 )  //ITF ROM
	ROM_COPY( "biosrom", 0x28000, 0x18000, 0x08000 )  //BIOS ROM
	ROM_COPY( "biosrom", 0x30000, 0x20000, 0x08000 )
	ROM_COPY( "biosrom", 0x38000, 0x28000, 0x08000 )

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font_ux.rom",     0x000000, 0x046800, BAD_DUMP CRC(19a76eeb) SHA1(96a006e8515157a624599c2b53a581ae0dd560fd) )

	LOAD_KANJI_ROMS
	// SASI HDDs
//  LOAD_IDE_ROM
ROM_END

/*
FS - 80386 20
*/

ROM_START( pc9801fs )
	ROM_REGION16_LE( 0x40000, "biosrom", ROMREGION_ERASEFF )
	ROM_LOAD16_BYTE( "kqx01_00.bin",  0x000000, 0x020000, CRC(4713d388) SHA1(9ae48fbe7b8ab7144e045e183ed88d2544d9a61c) )
	ROM_LOAD16_BYTE( "kqx02_00.bin",  0x000001, 0x020000, CRC(f55e42d6) SHA1(2ab0ae817e9abed984544c920182689127550ce3) )

	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	// 0x0c000-0x0ffff sound ROM BIOS
	// 0x10000-0x13fff ^ mirror
	// 0x14000-0x16fff <unknown>
	// 0x17000-0x17fff SCSI disk BIOS?
	// 0x18000-0x1ffff <empty>
	ROM_COPY( "biosrom", 0x20000, 0x10000, 0x08000 )  //ITF ROM
	ROM_COPY( "biosrom", 0x28000, 0x18000, 0x08000 )  //BIOS ROM
	ROM_COPY( "biosrom", 0x30000, 0x20000, 0x08000 )
	ROM_COPY( "biosrom", 0x38000, 0x28000, 0x08000 )

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font_ux.rom",     0x000000, 0x046800, BAD_DUMP CRC(19a76eeb) SHA1(96a006e8515157a624599c2b53a581ae0dd560fd) )

	LOAD_KANJI_ROMS
//  LOAD_IDE_ROM
ROM_END

/*
RX - 80286 12 (no V30?)

The bios is from a 386 model not an RX
*/

ROM_START( pc9801rx )
	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "itf_rs.rom",  0x10000, 0x08000, BAD_DUMP CRC(c1815325) SHA1(a2fb11c000ed7c976520622cfb7940ed6ddc904e) )
	ROM_LOAD( "bios_rx.rom", 0x18000, 0x18000, BAD_DUMP CRC(0a682b93) SHA1(76a7360502fa0296ea93b4c537174610a834d367) )
	// fix csum
	ROM_FILL(0x2fffe, 1, 0x0d)

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font_rx.rom",     0x000000, 0x046800, BAD_DUMP CRC(456d9fc7) SHA1(78ba9960f135372825ab7244b5e4e73a810002ff) )

	LOAD_KANJI_ROMS
	LOAD_IDE_ROM
ROM_END

/*
RS - 386SX @ 16 MHz

(note: might be a different model!)
*/

ROM_START( pc9801rs )
	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "itf_rs.rom",  0x10000, 0x08000, CRC(c1815325) SHA1(a2fb11c000ed7c976520622cfb7940ed6ddc904e) )
	ROM_LOAD( "bios_rs.rom", 0x18000, 0x18000, BAD_DUMP CRC(315d2703) SHA1(4f208d1dbb68373080d23bff5636bb6b71eb7565) )

	/* following is an emulator memory dump, should be checked and eventually nuked if nothing worth is there */
	ROM_REGION( 0x100000, "memory", 0 )
	ROM_LOAD( "00000.rom", 0x00000, 0x8000, CRC(6e299128) SHA1(d0e7d016c005cdce53ea5ecac01c6f883b752b80) )
	ROM_LOAD( "c0000.rom", 0xc0000, 0x8000, CRC(1b43eabd) SHA1(ca711c69165e1fa5be72993b9a7870ef6d485249) )  // 0xff everywhere
	ROM_LOAD( "c8000.rom", 0xc8000, 0x8000, CRC(f2a262b0) SHA1(fe97d2068d18bbb7425d9774e2e56982df2aa1fb) )
	ROM_LOAD( "d0000.rom", 0xd0000, 0x8000, CRC(1b43eabd) SHA1(ca711c69165e1fa5be72993b9a7870ef6d485249) )  // 0xff everywhere
	ROM_LOAD( "e8000.rom", 0xe8000, 0x8000, CRC(4e32081e) SHA1(e23571273b7cad01aa116cb7414c5115a1093f85) )  // contains n-88 basic (86) v2.0
	ROM_LOAD( "f0000.rom", 0xf0000, 0x8000, CRC(4da85a6c) SHA1(18dccfaf6329387c0c64cc4c91b32c25cde8bd5a) )
	ROM_LOAD( "f8000.rom", 0xf8000, 0x8000, CRC(2b1e45b1) SHA1(1fec35f17d96b2e2359e3c71670575ad9ff5007e) )

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font_rs.rom", 0x00000, 0x46800, BAD_DUMP CRC(da370e7a) SHA1(584d0c7fde8c7eac1f76dc5e242102261a878c5e) )

	LOAD_KANJI_ROMS
	LOAD_IDE_ROM
ROM_END

/*
BX2/U2 - 486SX @ 25 MHz

Yet another franken-romset done with direct memory dump, shrug

*/

ROM_START( pc9801bx2 )
	ROM_REGION16_LE( 0x40000, "biosrom", ROMREGION_ERASEFF )
	ROM_LOAD( "pc98bank0.bin",  0x00000, 0x08000, BAD_DUMP CRC(bfd100cc) SHA1(cf8e6a5679cca7761481abef0ba4b35ead39efdb) )
	ROM_LOAD( "pc98bank1.bin",  0x08000, 0x08000, BAD_DUMP CRC(d0562af8) SHA1(2c4fd27eb598f4b8a00f3e86941ba27007d58e47) )
	ROM_LOAD( "pc98bank2.bin",  0x10000, 0x08000, BAD_DUMP CRC(12818a14) SHA1(9c31e8ac85d78fa779d6bbc2095557065294ec09) )
	ROM_LOAD( "pc98bank3.bin",  0x18000, 0x08000, BAD_DUMP CRC(d0bda44e) SHA1(c1022a3b2be4d2a1e43914df9e4605254e5f99d5) )
	ROM_LOAD( "pc98bank4.bin",  0x20000, 0x08000, BAD_DUMP CRC(be8092f4) SHA1(12c8a166b8c6ebbef85568b67e1f098562883365) )
	ROM_LOAD( "pc98bank5.bin",  0x28000, 0x08000, BAD_DUMP CRC(4e32081e) SHA1(e23571273b7cad01aa116cb7414c5115a1093f85) )
	ROM_LOAD( "pc98bank6.bin",  0x30000, 0x08000, BAD_DUMP CRC(f878c160) SHA1(cad47f09075ffe4f7b51bb937c9f716c709d4596) )
	ROM_LOAD( "pc98bank7.bin",  0x38000, 0x08000, BAD_DUMP CRC(1bd6537b) SHA1(ff9ee1c976a12b87851635ce8991ac4ad607675b) )

	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	// 0x1a000: setup mode
	ROM_COPY( "biosrom", 0x20000, 0x10000, 0x8000 ) // ITF ROM
	ROM_COPY( "biosrom", 0x28000, 0x18000, 0x8000 ) // BIOS ROM
	ROM_COPY( "biosrom", 0x30000, 0x20000, 0x8000 )
	ROM_COPY( "biosrom", 0x38000, 0x28000, 0x8000 )

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font_rs.rom", 0x00000, 0x46800, BAD_DUMP CRC(da370e7a) SHA1(584d0c7fde8c7eac1f76dc5e242102261a878c5e) )

	LOAD_KANJI_ROMS
	LOAD_IDE_ROM
ROM_END

void pc9801_state::init_pc9801_kanji()
{
	#define copy_kanji_strip(_dst,_src,_fill_type) \
	for (uint32_t i = _dst, k = _src; i < _dst + 0x20; i++, k++) \
	{ \
		for (uint32_t j = 0; j < 0x20; j++) \
			kanji[j+(i << 5)] = _fill_type ? new_chargen[j+(k << 5)] : 0; \
	}
	uint32_t pcg_tile;
	uint8_t *kanji = memregion("kanji")->base();
	uint8_t *raw_kanji = memregion("raw_kanji")->base();
	uint8_t *new_chargen = memregion("new_chargen")->base();
	uint8_t *chargen = memregion("chargen")->base();

	/* Convert the ROM bitswap here from the original structure */
	/* TODO: kanji bitswap should be completely wrong, will check it out once that a dump is remade. */
	for (uint32_t i = 0; i < 0x80000 / 0x20; i++)
	{
		for (uint32_t j = 0; j < 0x20; j++)
		{
			pcg_tile = bitswap<16>(i,15,14,13,12,11,7,6,5,10,9,8,4,3,2,1,0) << 5;
			kanji[j+(i << 5)] = raw_kanji[j+pcg_tile];
		}
	}

	/* convert charset into even/odd structure */
	for (uint32_t i = 0; i < 0x80000 / 0x20; i++)
	{
		for (uint32_t j = 0; j < 0x10; j++)
		{
			new_chargen[j*2 + (i << 5)] = chargen[j + (i << 5)];
			new_chargen[j*2 + (i << 5) + 1] = chargen[j + (i << 5) + 0x10];
		}
	}

	/* now copy the data from the fake roms into our kanji struct */
	copy_kanji_strip(0x0800,   -1,0); copy_kanji_strip(0x0820,   -1,0); copy_kanji_strip(0x0840,   -1,0); copy_kanji_strip(0x0860,   -1,0);
	copy_kanji_strip(0x0900,   -1,0); copy_kanji_strip(0x0920,0x3c0,1); copy_kanji_strip(0x0940,0x3e0,1); copy_kanji_strip(0x0960,0x400,1);
	copy_kanji_strip(0x0a00,   -1,0); copy_kanji_strip(0x0a20,0x420,1); copy_kanji_strip(0x0a40,0x440,1); copy_kanji_strip(0x0a60,0x460,1);
	copy_kanji_strip(0x0b00,   -1,0); copy_kanji_strip(0x0b20,0x480,1); copy_kanji_strip(0x0b40,0x4a0,1); copy_kanji_strip(0x0b60,0x4c0,1);
	copy_kanji_strip(0x0c00,   -1,0); copy_kanji_strip(0x0c20,0x4e0,1); copy_kanji_strip(0x0c40,0x500,1); copy_kanji_strip(0x0c60,0x520,1);
	copy_kanji_strip(0x0d00,   -1,0); copy_kanji_strip(0x0d20,0x540,1); copy_kanji_strip(0x0d40,0x560,1); copy_kanji_strip(0x0d60,0x580,1);
	copy_kanji_strip(0x0e00,   -1,0); copy_kanji_strip(0x0e20,   -1,0); copy_kanji_strip(0x0e40,   -1,0); copy_kanji_strip(0x0e60,   -1,0);
	copy_kanji_strip(0x0f00,   -1,0); copy_kanji_strip(0x0f20,   -1,0); copy_kanji_strip(0x0f40,   -1,0); copy_kanji_strip(0x0f60,   -1,0);
	{
		int src_1,dst_1;

		for(src_1=0x1000,dst_1=0x660;src_1<0x8000;src_1+=0x100,dst_1+=0x60)
		{
			copy_kanji_strip(src_1,             -1,0);
			copy_kanji_strip(src_1+0x20,dst_1+0x00,1);
			copy_kanji_strip(src_1+0x40,dst_1+0x20,1);
			copy_kanji_strip(src_1+0x60,dst_1+0x40,1);
		}
	}
	#undef copy_kanji_strip
}

void pc9801vm_state::init_pc9801vm_kanji()
{
	uint32_t raw_tile;
	uint8_t *chargen = memregion("chargen")->base();
	uint8_t *raw_kanji = memregion("raw_kanji")->base();
	uint8_t *kanji = memregion("kanji")->base();

	/* swap bits for 8x8 characters, discard 8x8 "graphics" characters */
	/* TODO: should we keep and use the "graphics" characters? */
	for( uint32_t i = 0; i < 0x100; i++ )
	{
		for( uint32_t j = 0; j < 8; j++ )
		{
			chargen[i*8+j] = bitswap<8>(chargen[i*0x10+j],0,1,2,3,4,5,6,7);
		}
	}
	/* swap bits for 8x16 characters */
	for( uint32_t i = 0; i < 0x100; i++ )
	{
		for( uint32_t j = 0; j < 0x10; j++ )
		{
			chargen[0x100*8+i*0x10+j] = bitswap<8>(chargen[0x100*0x10+i*0x10+j],0,1,2,3,4,5,6,7);
		}
	}
	/* 16x16 0x0020-0x077f */
	for( uint32_t hibyte = 0x00; hibyte <= 0x07; hibyte++ )
	{
		for( uint32_t lobyte = 0x20; lobyte <= 0x7f; lobyte++ )
		{
			raw_tile = bitswap<16>(hibyte*0x100+lobyte,15,14,13,12,11,7,6,5,10,9,8,4,3,2,1,0) * 0x20;
			for( uint32_t line = 0; line < 0x20; line++ )
			{
				kanji[(hibyte*0x100+lobyte)*0x20+line] = raw_kanji[raw_tile+line];
			}
		}
	}
	/* 16x16 0x0820-0x0f7f (swapped bits) */
	for( uint32_t hibyte = 0x08; hibyte <= 0x0f; hibyte++ )
	{
		for( uint32_t lobyte = 0x20; lobyte <= 0x7f; lobyte++ )
		{
			raw_tile = bitswap<16>((hibyte-0x08)*0x100+lobyte,15,14,13,12,11,7,6,5,10,9,8,4,3,2,1,0) * 0x20 + 0x2000 * 0x20;
			for( uint32_t line = 0; line < 0x20; line++ )
			{
				kanji[(hibyte*0x100+lobyte)*0x20+line] = bitswap<8>(raw_kanji[raw_tile+line],0,1,2,3,4,5,6,7);
			}
		}
	}
	/* 16x16 0x1020-0x4f7f */
	for( uint32_t hibyte = 0x10; hibyte <= 0x4f; hibyte++ )
	{
		for( uint32_t lobyte = 0x20; lobyte <= 0x7f; lobyte++ )
		{
			raw_tile = bitswap<16>((hibyte-0x10)*0x100+lobyte,15,14,7,13,6,5,12,11,10,9,8,4,3,2,1,0) * 0x20;
			for( uint32_t line = 0; line < 0x20; line++ )
			{
				kanji[(hibyte*0x100+lobyte)*0x20+line] = raw_kanji[raw_tile+line];
			}
		}
	}
	/* 16x16 0x5020-0x537f */
	for( uint32_t hibyte = 0x50; hibyte <= 0x53; hibyte++ )
	{
		for( uint32_t lobyte = 0x20; lobyte <= 0x7f; lobyte++ )
		{
			raw_tile = bitswap<16>((hibyte-0x50)*0x100+lobyte,15,14,13,12,11,7,6,5,10,9,8,4,3,2,1,0) * 0x20 + 0x1000 * 0x20;
			for( uint32_t line = 0; line < 0x20; line++ )
			{
				kanji[(hibyte*0x100+lobyte)*0x20+line] = raw_kanji[raw_tile+line];
			}
		}
	}
}

// We keep track of anything undumped of note that belongs to the "PC-98" family tree here, and try to give a
// sub-tree code in state machine if not obvious from the naming suffix.
// This is also repeated in SW list reports, i.e. you have to use an "On RS class xxx" format to indicate a bug report
// specifically happening for PC9801RS. This will be hopefully put into stone with driver splits at some point in future.

// "vanilla" class (i86, E/F/M)
COMP( 1982, pc9801,     0,        0, pc9801,    pc9801,   pc9801_state, init_pc9801_kanji,   "NEC",   "PC-9801",   MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND ) // genuine dump
COMP( 1983, pc9801f,    pc9801,   0, pc9801,    pc9801,   pc9801_state, init_pc9801_kanji,   "NEC",   "PC-9801F",  MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND ) // genuine dump

// N5200 (started as a vanilla PC-98 business line derivative,
//        eventually diverged into its own thing and incorporated various Hyper 98 features.
//        Runs proprietary PTOS)
// APC III (US version of either vanilla PC9801 or N5200, aimed at business market. Runs MS-DOS 2.11/3.xx or PC-UX)
// ...

// 文豪 a.k.a. "Bungo" (A full family of desktop & notebook word processors)
// NWP-20N (OG 1981 model)
// HWP "Hyper 7" (business model)
// DP "DP NOTE" ("Document Processor", based off various flavours of N5820 & 9821NOTE combo)
// DP-70F (based off N5820-70FA & 9821ap2)
// DP-70S (Document Filing Server)
// Mini 5 -> cfr. bungo.cpp
// Mini 7
// JX series
// ...

// VM class (V30)
COMP( 1985, pc9801vm,   0,        0, pc9801vm,  pc9801rs, pc9801vm_state, init_pc9801vm_kanji, "NEC",   "PC-9801VM",                     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND ) // genuine dump
// UV class (V30)
COMP( 1986, pc9801uv2,  pc9801vm, 0, pc9801uv,  pc9801rs, pc9801vm_state, init_pc9801vm_kanji, "NEC",   "PC-9801UV2",                     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND ) // genuine dump

// UX class (i286)
COMP( 1987, pc9801ux,   0,        0, pc9801ux,  pc9801rs, pc9801vm_state, init_pc9801_kanji,   "NEC",   "PC-9801UX",                     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// VX class (first model using an EGC)
// original VX0/VX2/VX4 released in Nov 1986, minor updates with OS pre-installed etc. in 1987
COMP( 1986, pc9801vx,   0,        0, pc9801vx,  pc9801rs, pc9801vm_state, init_pc9801_kanji,   "NEC",   "PC-9801VX",                     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// XA/XL class (1120 x 750 true color, nicknamed "High-reso")
// ...

// PC-H98 (Hyper 98, '90-'93 high end line with High-reso, proprietary NESA bus, E²GC)
// PC-H98S cfr. pc_h98.cpp
// PC-H98T (LCD Hyper 98)
// SV-H98 "98SERVER" (i486, later Hyper 98 revision)
// SV-98 (Pentium based, second gen of 98SERVER)
// OP-98 ("Office Processor", released around '91. Reports claims to be H98-like, with extra connectivity with NEC 7200 workstation)
// ...

// CV class (V30, compact version with monitor built-in like a Macintosh)
// ...

// RS class (i386SX)
COMP( 1988, pc9801rx,   pc9801rs, 0, pc9801rs,  pc9801rs, pc9801vm_state, init_pc9801_kanji,   "NEC",   "PC-9801RX",                     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
COMP( 1989, pc9801rs,   0,        0, pc9801rs,  pc9801rs, pc9801vm_state, init_pc9801_kanji,   "NEC",   "PC-9801RS",                     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
// DX class (i286)
COMP( 1990, pc9801dx,   0,        0, pc9801dx,  pc9801rs, pc9801vm_state, init_pc9801_kanji,   "NEC",   "PC-9801DX",                     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
// DA class (i386SX + SDIP and EMS)
// ...
// UF/UR/US class (i386SX + SDIP, optional high-reso according to BIOS? Derivatives of UX)
COMP( 1992, pc9801us,   0,        0, pc9801us,  pc9801rs, pc9801us_state, init_pc9801_kanji,   "NEC",   "PC-9801US",                     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
// FS class (i386SX + ?)
COMP( 1992, pc9801fs,   0,        0, pc9801fs,  pc9801rs, pc9801us_state, init_pc9801_kanji,   "NEC",   "PC-9801FS",                     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
// FA class (i486SX)
// ...
// BX class (official nickname "98 FELLOW", last releases prior to 9821 line)
COMP( 1993, pc9801bx2,  0,        0, pc9801bx2, pc9801rs, pc9801bx_state, init_pc9801_kanji,   "NEC",   "PC-9801BX2/U2 (98 FELLOW)",                 MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// PC-98GS (Multimedia PC, exclusive video mode "Extended Screen Graphics", -73 sound board (a superset of later -86), superimposition)
// ...

// Epson class knockoffs -> cfr. pc9801_epson.cpp

// PC9821 -> cfr. pc9821.cpp

// PC98DO (PC88+PC98, V33 + μPD70008AC)
// ...

// PC-98LT / PC-98HA -> cfr. pc98ha.cpp
// PC-9801N "98NOTE" (V30 based, EMS + 3.5" floppy, 8.9" FL blue LCD).
// PC-9801N* (98NOTE upgrades)
// PC-9801T (i386SX, extremely expensive TFT or LCD laptop with C-Bus slots, de-facto a "portable" desktop machine)
// PC-9801LX (i286, belongs to pc98ha.cpp?)
// PC-9801LS (i386SX, Plasma laptop)
// RC-9801 (portable (color?) LCD, i386SX, wireless 9600bps modem)
// PC-9801P (LCD with light pen)
// DB-P1 (1993 b&w LCD touch tablet / eBook)

// DtermPC (V30/V40/V50 LCD terminal telephone aimed at digital PBX exchanges)

// FC-9801 (FC stands for "Factory Computer". Aimed at industrial automation, has similarly named models from 9801 to 9821 and H98.
//          Uses a FC-9801-06 RAS "Reliability, Availability and Serviceability" specific expansion)
// FC98-NX (Evolution of FC-9821Ka, first model FC20C released in 1998, branch is still running to date.
//          Most likely just DOS/V compatible and not going to fit here except for RAS capabilities)

// TWINPOS ("Point Of Sale" from NEC, originally based off PC-98 arch, eventually switched to DOS/V too?)

// Metrologie BFM 186 (speculated, French PAL CAD machine with dual text and gfx 7220 + other NEC parts & Basic 86, fabricated by Ye DATA Japan)



pc9801_epson.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese,Carl
/**************************************************************************************************

    Epson PC98[01] class machine

    TODO (PC-286VS):
    - Verify A20 gate usage, seems to reuse the same hookup as later Epson variants;

    TODO (PC-386M):
    - Incomplete shadow IPL banking, we currently never bankswitch to the other ROM bank
      (which barely contains program code);
    - "ERR:VR" at POST (GFX VRAM)
      Sub-routine that throws this is at PC=0xfd9bc. Notice that you can actually skip this
      with eip=0x1bf in debugger and make the system to actually checkout memory installed.
      (Shorthand: "bp fd9bc,eip=0x1bf")
    - POST throws non-fatal "ERR:PA" (page fault, "Protected Address"?) after checking memory
      installed. Non-fatal as in POST will checkout bootable devices afterward.

    TODO: (PC-486SE/PC-486MU):
    - Verify ROM bankswitch;
      On PC-486SE sets up what is normally IPL bankswitch at PC=0xf5115, successive opcode
      is a jmp 0xf8000, pretty unlikely it delays bankswitch so assume it reloads
      the same bank.
    - Remove IDE regression hack at I/O $74e;
    - Regressed with a ERR:RA (conventional memory!?) when moving driver to
      stand-alone file;
    - Eventually errors with a ERR:VR (GFX VRAM);

    Notes:
    - A detailed list of Epson PC98s can be seen from here:
      http://www.pc-9800.net/db_epson/index.htm

    - Being these knockoffs means that there isn't 100% compatibility with all SWs.
      Additionally NEC introduced the so called "EPSON Protect" / "EPSON check" (エプソンチェック)
      starting with MS-DOS 3.3 onward, which checks the presence of NEC / Microsoft copyright
      string at E800:0DD8 and refuses to boot if not satisfied.
      cfr. https://github.com/joncampbell123/dosbox-x/issues/682
      Epson offcially provided PC "Software Installation Program" (SIP) floppy disks
      (the "epinstal*" in SW list?) that counteracts with the protection check.
      There's alternatively a freeware user released "Dispell!" program tool that can be used for
      the same purpose, which also works for 32-bit DOS/V machines.

**************************************************************************************************/

#include "emu.h"
#include "pc9801_epson.h"

template <unsigned which> void pc98_epson_state::shadow_ipl_w(offs_t offset, u16 data, u16 mem_mask)
{
	// TODO: shadow register 0x6a may actually be write deprotect
	COMBINE_DATA(&m_shadow_ipl[which][offset]);
}

/**************************************************************************************************

    Control port for Epson shadow IPL

    If any of these isn't right system throws "ERR:BR" at boot (BIOS loader error).
    Executes some code in text VRAM area (PC=$a006e), trying to setup a writeable RAM bank
    to IPL window area.

**************************************************************************************************/
void pc98_epson_state::epson_ipl_bank_w(offs_t offset, u8 data)
{
	m_shadow_ipl_bank = data;
	switch(m_shadow_ipl_bank)
	{
		case 0x2a:
			m_ipl->set_bank(2);
			break;
		case 0xe6:
			m_ipl->set_bank(0);
			break;
		default:
			// TODO: at least 0xa6 used, what for?
			logerror("%s: unknown Epson shadow IPL bank setting set %02x\n", machine().describe_context(), data);
			break;
	}
}

// overrides original PC98 $43c-$43f ports
void pc98_epson_state::epson_itf_bank_w(offs_t offset, u8 data)
{
	// $43f
	if (offset == 1)
	{
		switch(data)
		{
			case 0x40: m_itf_bank_enable = false; break;
			case 0x42: m_itf_bank_enable = true; break;
			default:
				logerror("%s: unknown ITF enable setting %02x\n", machine().describe_context(), data);
				break;
		}
	}

	// $43d
	if (offset == 0)
	{
		switch(data)
		{
			case 0x00:
			case 0x02:
				if (m_itf_bank_enable == true)
					m_ipl->set_bank((data & 2) >> 1);
				break;
			default:
				// TODO: 0x10 - 0x12 setting, which should be same as NEC PC98
				// (i.e. reversed compared to above)
				logerror("%s: unknown ITF bank setting %02x\n", machine().describe_context(), data);
				break;
		}
	}
}

void pc98_epson_state::epson_a20_w(offs_t offset, u8 data)
{
	// pc386m PC=0xfd9b3
	// if this isn't mapped then POST throws "ERR:RA" (conventional memory -> "Real Address"?)
	m_gate_a20 = data & 1;
	m_maincpu->set_input_line(INPUT_LINE_A20, m_gate_a20 ? ASSERT_LINE : CLEAR_LINE);
	logerror("%s: Epson gate a20 %02x\n", machine().describe_context(), data);
}

void pc98_epson_state::epson_vram_bank_w(offs_t offset, u8 data)
{
//  m_vram_bank = (data & 1) ^ 1;
	// accessed in the same routine that actually throws ERR:VR
	logerror("%s: Epson $c06 write %02x\n", machine().describe_context(), data);
}

void pc98_epson_state::pc286vs_map(address_map &map)
{
	pc9801ux_map(map);
	map(0x0e8000, 0x0fffff).m(m_ipl, FUNC(address_map_bank_device::amap16));
	map(0xee8000, 0xefffff).m(m_ipl, FUNC(address_map_bank_device::amap16));
	map(0xfe8000, 0xffffff).m(m_ipl, FUNC(address_map_bank_device::amap16));
}

void pc98_epson_state::pc386m_map(address_map &map)
{
	pc9801rs_map(map);
	// TODO: is shadow RAM physically mapped here?
//  map(0xd0000, 0xd**ff).ram();
}

void pc98_epson_state::pc486se_map(address_map &map)
{
	pc386m_map(map);
	map(0x000e8000, 0x000fffff).m(m_ipl, FUNC(address_map_bank_device::amap16));
	map(0xffee8000, 0xffefffff).m(m_ipl, FUNC(address_map_bank_device::amap16));
	map(0xfffe8000, 0xffffffff).m(m_ipl, FUNC(address_map_bank_device::amap16));
}


void pc98_epson_state::epson_base_io(address_map &map)
{
//  map(0x0c03, 0x0c03).r Epson CPU mode, 'R' for Real mode, 'P' for Protected mode (lolwut)
	map(0x0c05, 0x0c05).w(FUNC(pc98_epson_state::epson_a20_w));
	map(0x0c06, 0x0c06).w(FUNC(pc98_epson_state::epson_vram_bank_w));
	map(0x0c07, 0x0c07).w(FUNC(pc98_epson_state::epson_ipl_bank_w));
//  map(0x0c13, 0x0c13).r Epson <unknown> readback
//  map(0x0c14, 0x0c14).r Epson <unknown> readback
}

void pc98_epson_state::pc286vs_io(address_map &map)
{
	pc9801ux_io(map);
	epson_base_io(map);
}

void pc98_epson_state::pc386m_io(address_map &map)
{
	pc9801rs_io(map);
	epson_base_io(map);
}

void pc98_epson_state::pc486se_io(address_map &map)
{
	pc386m_io(map);

	map(0x0082, 0x0082).lr8(NAME([]() -> u8 { return 0x00; }));

	map(0x043c, 0x043f).w(FUNC(pc98_epson_state::epson_itf_bank_w)).umask16(0xff00);

	map(0x0c42, 0x0c43).lr16(NAME([]() -> u16 { return 0x0000; }));

//  map(0x0640, 0x064f).rw(FUNC(pc9801_state::ide_cs0_r), FUNC(pc9801_state::ide_cs0_w));
//  map(0x0740, 0x074f).rw(FUNC(pc9801_state::ide_cs1_r), FUNC(pc9801_state::ide_cs1_w));
	// HACK: avoid POST moaning for misconfigured HDDs (!?)
	map(0x074e, 0x074e).lr8(NAME([]() -> u8 { return 0xff; }));

	// (R) bit 0: expected to go off at PC=0xf8c52 (pc486se)
	// (W) commands for?
	map(0x0c09, 0x0c09).lr8(NAME([]() -> u8 { return 0x00; }));
	map(0x0c0b, 0x0c0b).lr8(NAME([]() -> u8 { return 0x00; }));
}

void pc98_epson_state::pc386m_ipl_bank(address_map &map)
{
	map(0x00000, 0x17fff).rom().region("ipl", 0x00000).w(FUNC(pc98_epson_state::shadow_ipl_w<0>));
	map(0x18000, 0x2ffff).rom().region("ipl", 0x18000).w(FUNC(pc98_epson_state::shadow_ipl_w<1>));
	map(0x30000, 0x47fff).ram().share("shadow_ipl_0");
	map(0x48000, 0x5ffff).ram().share("shadow_ipl_1");
}

static INPUT_PORTS_START( pc386m )
	// in tracer bullet fashion we intentionally separated all dips/mouse switches
	// since there's no real need to actually INPUT_PORTS_EXTERN here:
	// - mouse should really be a device interface anyway;
	// - dips may really diverge from NEC specs or even be unmapped (needs verification).
	//   In both cases those needs a major cleanup, haven't yet found actual documentation for these specifically.
	//   Notice that Epson eventually inherited SDIP chip as well.
	PORT_START("DSW1")
	PORT_DIPNAME( 0x01, 0x01, "Monitor Type" )
	PORT_DIPSETTING(    0x00, "Normal Display (15KHz)" )
	PORT_DIPSETTING(    0x01, "Hi-Res Display (24KHz)" )
	PORT_DIPNAME( 0x02, 0x00, "DSW1" )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, "Display Type" )
	PORT_DIPSETTING(    0x04, "RGB" )
	PORT_DIPSETTING(    0x00, "Plasma" )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, "Graphic Function" )
	PORT_DIPSETTING(    0x80, "Basic (8 Colors)" )
	PORT_DIPSETTING(    0x00, "Expanded (16/4096 Colors)" )

	PORT_START("DSW2")
	PORT_DIPNAME( 0x01, 0x00, "DSW2" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, "GDC clock" )
	PORT_DIPSETTING(    0x80, "2.5 MHz" )
	PORT_DIPSETTING(    0x00, "5 MHz" )

	PORT_START("DSW3")
	PORT_DIPNAME( 0x01, 0x01, "FDD Fix Mode" )
	PORT_DIPSETTING(    0x00, "Auto-Detection" )
	PORT_DIPSETTING(    0x01, "Fixed" )
	PORT_DIPNAME( 0x02, 0x02, "FDD Density Select" )
	PORT_DIPSETTING(    0x00, "2DD" )
	PORT_DIPSETTING(    0x02, "2HD" )
	PORT_DIPNAME( 0x04, 0x04, "DSW3" )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, "Conventional RAM size" )
	PORT_DIPSETTING(    0x40, "640 KB" )
	PORT_DIPSETTING(    0x00, "512 KB" )
	PORT_DIPNAME( 0x80, 0x00, "CPU Type" )
	PORT_DIPSETTING(    0x80, "V30" )
	PORT_DIPSETTING(    0x00, "I386" )

	PORT_START("MOUSE_X")
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_X ) PORT_SENSITIVITY(30) PORT_KEYDELTA(30)

	PORT_START("MOUSE_Y")
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y ) PORT_SENSITIVITY(30) PORT_KEYDELTA(30)

	PORT_START("MOUSE_B")
	PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_CODE(MOUSECODE_BUTTON2) PORT_NAME("Mouse Right Button")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_CODE(MOUSECODE_BUTTON3) PORT_NAME("Mouse Middle Button")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CODE(MOUSECODE_BUTTON1) PORT_NAME("Mouse Left Button")

	PORT_START("ROM_LOAD")
	PORT_BIT( 0x03, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_CONFNAME( 0x04, 0x04, "Load IDE BIOS" )
	PORT_CONFSETTING(    0x00, DEF_STR( Yes ) )
	PORT_CONFSETTING(    0x04, DEF_STR( No ) )
INPUT_PORTS_END

MACHINE_START_MEMBER(pc98_epson_state, pc98_epson)
{
	MACHINE_START_CALL_MEMBER(pc9801rs);
}

MACHINE_RESET_MEMBER(pc98_epson_state, pc98_epson)
{
	MACHINE_RESET_CALL_MEMBER(pc9801rs);
	m_ipl->set_bank(0);
}

void pc98_epson_state::config_base_epson(machine_config &config)
{
	m_ipl->set_addrmap(AS_PROGRAM, &pc98_epson_state::pc386m_ipl_bank);
	// TODO: 19 or 20 address lines?
	// 20 may be used in case that mixed up ROM & shadow IPL loads are actually possible
	m_ipl->set_options(ENDIANNESS_LITTLE, 16, 19, 0x18000);

	MCFG_MACHINE_START_OVERRIDE(pc98_epson_state, pc98_epson)
	MCFG_MACHINE_RESET_OVERRIDE(pc98_epson_state, pc98_epson)
}

void pc98_epson_state::pc286vs(machine_config &config)
{
	pc9801vx(config);
	i80286_cpu_device &maincpu(I80286(config.replace(), m_maincpu, 10000000));
	maincpu.set_addrmap(AS_PROGRAM, &pc98_epson_state::pc286vs_map);
	maincpu.set_addrmap(AS_IO, &pc98_epson_state::pc286vs_io);
	maincpu.set_a20_callback(FUNC(pc98_epson_state::a20_286));
	maincpu.set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	config_base_epson(config);

	// TODO: DMA type & clock
}

void pc98_epson_state::pc286u(machine_config &config)
{
	pc9801vm(config);
	config_base_epson(config);

	// TODO: DMA type & clock
}


void pc98_epson_state::pc386m(machine_config &config)
{
	pc9801rs(config);
	I386SX(config.replace(), m_maincpu, 16000000); // i386SX 16MHz, switchable to 10/6 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &pc98_epson_state::pc386m_map);
	m_maincpu->set_addrmap(AS_IO, &pc98_epson_state::pc386m_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	config_base_epson(config);

	// RAM: 640KB + 14.6MB max
	// 2 3.5 floppy drives
	// ...
}

void pc98_epson_state::pc486se(machine_config &config)
{
	pc386m(config);
	const XTAL xtal = XTAL(25'000'000);
	I486(config.replace(), m_maincpu, xtal); // i486SX, switchable to 10/5 MHz, supports overdrive
	m_maincpu->set_addrmap(AS_PROGRAM, &pc98_epson_state::pc486se_map);
	m_maincpu->set_addrmap(AS_IO, &pc98_epson_state::pc486se_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	pit_clock_config(config, xtal/8); // unknown, passes "ERR:TM" test

	// RAM: 1.6 MB (!) + 17.6 max
	// "dedicated internal memory slot x 1"
	// "dedicated video board" slot
}

void pc98_epson_state::pc486mu(machine_config &config)
{
	pc386m(config);
	const XTAL xtal = XTAL(33'000'000);
	I486(config.replace(), m_maincpu, xtal); // i486SX, switchable to I386DX 10MHz/5MHz, Pentium ODP compatible
	m_maincpu->set_addrmap(AS_PROGRAM, &pc98_epson_state::pc486se_map);
	m_maincpu->set_addrmap(AS_IO, &pc98_epson_state::pc386m_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	pit_clock_config(config, xtal/8); // unknown, passes "ERR:TM" test

	// CL-GD5428
	// RAM: 5.6 + 61.6MB max
	// 2 x 3.5 floppy drives
}


// backported from pc98, of course both aren't 100% identical to the NEC counterpart
#define LOAD_IDE_ROM \
	ROM_REGION( 0x4000, "ide", ROMREGION_ERASEVAL(0xcb) ) \
	ROM_LOAD( "epson_ide_bios.rom", 0x0000, 0x2000, NO_DUMP ) \
	ROM_IGNORE( 0x2000 ) \
	ROM_IGNORE( 0x2000 ) \
	ROM_IGNORE( 0x2000 )

#define LOAD_KANJI_ROMS \
	ROM_REGION( 0x80000, "raw_kanji", ROMREGION_ERASEFF ) \
	ROM_LOAD16_BYTE( "24256c-x01.bin", 0x00000, 0x4000, BAD_DUMP CRC(28ec1375) SHA1(9d8e98e703ce0f483df17c79f7e841c5c5cd1692) ) \
	ROM_CONTINUE(                      0x20000, 0x4000  ) \
	ROM_LOAD16_BYTE( "24256c-x02.bin", 0x00001, 0x4000, BAD_DUMP CRC(90985158) SHA1(78fb106131a3f4eb054e87e00fe4f41193416d65) ) \
	ROM_CONTINUE(                      0x20001, 0x4000  ) \
	ROM_LOAD16_BYTE( "24256c-x03.bin", 0x40000, 0x4000, BAD_DUMP CRC(d4893543) SHA1(eb8c1bee0f694e1e0c145a24152222d4e444e86f) ) \
	ROM_CONTINUE(                      0x60000, 0x4000  ) \
	ROM_LOAD16_BYTE( "24256c-x04.bin", 0x40001, 0x4000, BAD_DUMP CRC(5dec0fc2) SHA1(41000da14d0805ed0801b31eb60623552e50e41c) ) \
	ROM_CONTINUE(                      0x60001, 0x4000  ) \
	ROM_REGION( 0x100000, "kanji", ROMREGION_ERASEFF ) \
	ROM_REGION( 0x80000, "new_chargen", ROMREGION_ERASEFF )

/*
Epson PC-286VS
i286 @ 10 (selectable between 6 and 10 MHz)
NB: pc-9801.net reports @ 16, assume mistake
640 KB conventional memory + 14.6 MB
5.25"2DD/2HDx2
CBus: 4slots
*/

ROM_START( pc286vs )
	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "a2_wvs.2e",    0x10000, 0x08000, CRC(318d6bbe) SHA1(f3ba85f3144e361257d6f7129e7f29fff17c2e1e) )
	ROM_CONTINUE(             0x00000, 0x10000 )
	ROM_CONTINUE(             0x28000, 0x08000 ) // bank 1, unconfirmed

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font_286vs.rom", 0x0000, 0x46800, BAD_DUMP CRC(456d9fc7) SHA1(78ba9960f135372825ab7244b5e4e73a810002ff))

	LOAD_KANJI_ROMS
	LOAD_IDE_ROM
ROM_END


/*
Epson PC-286U
μPD70116 (V30) @ 10/8 MHz
640 KB conventional memory + 8.6 MB
3.5"2DD/2HDx2
CBus: 2 internal slots + 2 external slots

NOTE: was mislabeled as pc9801vm11, this is a best guess
(contains Epson strings in place of KBCRT identifier, at ipl address $23270)

*/

ROM_START( pc286u )
	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "itf.rom",     0x10000, 0x08000, NO_DUMP )
	ROM_LOAD( "bios_vm.rom", 0x18000, 0x18000, CRC(2e2d7cee) SHA1(159549f845dc70bf61955f9469d2281a0131b47f) )

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font_vm.rom",     0x000000, 0x046800, BAD_DUMP CRC(456d9fc7) SHA1(78ba9960f135372825ab7244b5e4e73a810002ff) )

	LOAD_KANJI_ROMS
//  LOAD_IDE_ROM
ROM_END


/*
Epson PC-386M

i386SX-16 @ 16
1MB
3.5"2DD/2HDx2
CBus: 3slots
*/

ROM_START( pc386m )
	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	// bank 0: definitely wants this ROM mapping otherwise POST throws "ERR:R0" (BIOS checksum)
	ROM_LOAD( "cwma-a02.bin", 0x10000, 0x08000,  CRC(d2c357a4) SHA1(819c9a1fc92124a8d6a85339c74651add7efaf92) )
	ROM_CONTINUE(             0x00000, 0x10000 )
	ROM_CONTINUE(             0x28000, 0x08000 ) // bank 1, unconfirmed

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font_486mu.rom", 0x0000, 0x46800, BAD_DUMP CRC(456d9fc7) SHA1(78ba9960f135372825ab7244b5e4e73a810002ff))

	LOAD_KANJI_ROMS
	LOAD_IDE_ROM
ROM_END

/*
Epson PC-486SE

i486SX @ 25 MHz
1.6 MB of conventional memory (???)
17.6 MB
CBus: 2slots
*/

ROM_START( pc486se )
	ROM_REGION16_LE( 0x20000, "biosrom", ROMREGION_ERASEFF )
	ROM_LOAD( "1699ma_cw99-a03.bin", 0x00000, 0x20000,   CRC(f03df711) SHA1(88614746e01c7d3cff9f3b8ce0a598830a77d1dc) )

	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	// this is quite convoluted
	ROM_COPY( "biosrom", 0x08000, 0x00000, 0x08000 )
	ROM_COPY( "biosrom", 0x00000, 0x10000, 0x08000 )
	ROM_COPY( "biosrom", 0x10000, 0x08000, 0x08000 )
//  ROM_FILL(                     0x18000, 0x08000, 0x90) // untested by BIOS
	ROM_COPY( "biosrom", 0x10000, 0x20000, 0x08000 )
	ROM_COPY( "biosrom", 0x18000, 0x28000, 0x08000 )

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font_486mu.rom", 0x0000, 0x46800, BAD_DUMP CRC(456d9fc7) SHA1(78ba9960f135372825ab7244b5e4e73a810002ff))

	LOAD_KANJI_ROMS
	LOAD_IDE_ROM
ROM_END

/*
Epson PC-486MU
i486SX-33 @ 33
8MB RAM
3.5'2DD/2HDx2, 2xCD-ROM
CBus: 3 slots
*/

ROM_START( pc486mu )
	ROM_REGION16_LE( 0x20000, "biosrom", ROMREGION_ERASEFF )
	ROM_LOAD( "pc-486mu_hn27c1024.bin", 0x00000, 0x20000, CRC(113268e1) SHA1(2a630abc825b2808f9f8fb65c6cb1fb7e7f6c710))
//  ROM_LOAD( "bios_486mu.rom", 0x00000, 0x18000, BAD_DUMP CRC(57b5d701) SHA1(15029800842e93e07615b0fd91fb9f2bfe3e3c24))

	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	// backported from pc486se
	ROM_COPY( "biosrom", 0x08000, 0x00000, 0x08000 )
	ROM_COPY( "biosrom", 0x00000, 0x10000, 0x08000 )
	ROM_COPY( "biosrom", 0x10000, 0x08000, 0x08000 )
//  ROM_FILL(                     0x18000, 0x08000, 0x90) // untested by BIOS
	ROM_COPY( "biosrom", 0x10000, 0x20000, 0x08000 )
	ROM_COPY( "biosrom", 0x18000, 0x28000, 0x08000 )


	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font_486mu.rom", 0x0000, 0x46800, BAD_DUMP CRC(456d9fc7) SHA1(78ba9960f135372825ab7244b5e4e73a810002ff))

	LOAD_KANJI_ROMS
	LOAD_IDE_ROM
ROM_END

// Epson PC98 desktop line

// PC-286 (i286, first model released in Oct 1987)
COMP( 1989, pc286vs,     0,       0, pc286vs,    pc386m, pc98_epson_state, init_pc9801_kanji, "Epson", "PC-286VS", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// PC-286U (same as above except running on V30)
COMP( 1987, pc286u,     0,        0, pc286u,     pc386m, pc98_epson_state, init_pc9801_kanji, "Epson", "PC-286U", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND ) // Revised BIOS 1988

// PC-286C "PC Club" (same as PC-286?)
// ...

// PC-386 (i386)
COMP( 1990, pc386m,     0,        0, pc386m,    pc386m, pc98_epson_state, init_pc9801_kanji, "Epson", "PC-386M",  MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// PC-486 (i486SX/DX)
COMP( 1994, pc486mu,    0,        0, pc486se,   pc386m, pc98_epson_state, init_pc9801_kanji, "Epson", "PC-486MU", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
COMP( 1993, pc486se,    pc486mu,  0, pc486se,   pc386m, pc98_epson_state, init_pc9801_kanji, "Epson", "PC-486SE", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
// PRO-486 (first actual version with i486dx? Supports High-reso)
// PC-486P/Win (same as a PC-486P but with Windows 3.0a + MS-DOS 3.3 HDD pre-installed?)

// PC-586 (Pentium/Pentium ODP compatibles)
// ...

// Epson PC98 L[aptop] line
// PC-286B (80C286)
// PC-286L* (V30 or 80C286)
// ...

// PC-386BL* (i386sx)
// PC-386LS* (just bigger version of above?)
// ...

// Epson PC98 NOTE[book] line
// PC-*86N* (runs on correlated CPU as above)
// PC-486PT ("P[or]T[able]" SL enhanced i486, wallet-like dimensions, supports light pen, runs under "PenDOS")
// ...



pc9821.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese,Carl
/**************************************************************************************************

NEC PC-9821

follow-up to PC-9801 for the consumer market

TODO (PC-9821):
- non-fatal "cache error" at POST for all machines listed here;
- undumped IDE ROM, kludged to work;
- further state machine breakdowns;

TODO (PC-9821Ap2):
- Lack of key repeat makes it unable to enter SETUP mode normally.
  bp 0xf8a32,1,{esi|=40;g} to simulate holding HELP key at power-on/reset;

TODO (PC-9821As):
- unimplemented SDIP specific access;
- "SYSTEM SHUTDOWN" while accessing above;
- Update: it never goes into above after I changed default m_dma_access_ctrl to 0xfe?

TODO (PC-9821Cx3):
- "MICON ERROR" at POST, we currently return a ready state in remote control register
  to bypass it, is it expected behaviour?
- Hangs normally with "Set the SDIP" message, on soft reset tries to r/w I/Os
  $b00-$b03, kanji RAM $a9 and $f0 (mostly bit 5, built-in 27 inches HDD check?) then keeps
  looping;
- 0xfa2c8 contains ITF test routines, to access it's supposedly CTRL+CAPS+KANA,
  which currently doesn't work. It also never returns a valid processor or CPU clock,
  is it a debug side-effect or supposed to be read somehow?
- Expects 0xc0000-0xdffff to be r/w at PC=0x104e8, currently failing for inner C-Bus mappings.
  Is PCI supposed to overlay the C-Bus section?
- Eventually jump off the weeds by taking an invalid irq in timer test;
- Reportedly should display a CanBe logo at POST (always blue with white fg?),
  at least pc9821cx3 ROM has some VRAM data in first half of BIOS ROM.
  Where this is mapped is currently unknown;

TODO (PC-9821Xa16/PC-9821Ra20/PC-9821Ra266/PC-9821Ra333):
- "MICON ERROR" at POST (processor microcode detection fails, basically down to a more
  involved bankswitch with Pentium based machines);

TODO: (PC-9821Nr15/PC-9821Nr166)
- Tests conventional RAM then keeps polling $03c4 (should be base VGA regs read);
- Skipping that will eventually die with a "MEMORY ERROR" (never reads extended memory);

**************************************************************************************************/

#include "emu.h"
#include "pc9821.h"

// TODO: remove me, cfr. pc9801.cpp; verify that 9801 clocks are correct for 9821 series as well
#define BASE_CLOCK      XTAL(31'948'800)    // verified for PC-9801RS/FA
#define MAIN_CLOCK_X1   (BASE_CLOCK / 16)   // 1.9968 MHz
#define MAIN_CLOCK_X2   (BASE_CLOCK / 13)   // 2.4576 MHz

uint32_t pc9821_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	bitmap.fill(m_palette->black_pen(), cliprect);

	if(m_video_ff[DISPLAY_REG] != 0)
	{
		if (m_ex_video_ff[ANALOG_256_MODE])
		{
			rgb_t const *const palette = m_palette->palette()->entry_list_raw();
			u8 *ext_gvram = (u8 *)m_ext_gvram.target();
			int base_y = cliprect.min_y;
			u32 base_address;
			u16 pitch;
			u8 im;
			// 0x68: VRAM 800/480 line mode (os2warp3)
			const u32 vram_base = BIT(m_ex_video_ff[0x68 >> 1], 0) ? 0 : m_vram_disp * 0x40000;

			for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
			{
				const int rel_y = y - base_y;

				std::tie(base_address, pitch, im) = m_hgdc[1]->get_area_partition_props(rel_y);
				// aitd sets PITCH=40 (320), guess: assume im == 0 doing the trick
				const u8 pitch_shift = im ^ 1;

				for (int x = cliprect.min_x; x <= cliprect.max_x; x += 8)
				{
					// Mask is confirmed by flashb:
					// does double buffering at SAD $38fc and $fffc, except when
					// it goes single on title, drawing on current bank displayed.
					u32 address = (((base_address << 1) + (rel_y) * (pitch << pitch_shift) + (x >> 3)) & 0xffff) << 3;
					for(int xi = 0; xi < 8; xi ++)
					{
						int res_x = x + xi;
						int res_y = y;

						u16 pen = ext_gvram[((address + xi) + vram_base) & 0x7ffff];

						bitmap.pix(res_y, res_x) = palette[(pen & 0xff) + 0x20];
					}
				}
			}
		}
		else
			m_hgdc[1]->screen_update(screen, bitmap, cliprect);
		m_hgdc[0]->screen_update(screen, bitmap, cliprect);
	}

	return 0;
}

// old code, for consultation
#if 0
UPD7220_DISPLAY_PIXELS_MEMBER( pc9821_state::pegc_display_pixels )
{
	if(m_ex_video_ff[ANALOG_256_MODE])
	{
		rgb_t const *const palette = m_palette->palette()->entry_list_raw();
		u16 *ext_gvram = (u16 *)m_ext_gvram.target();

		for(int xi=0;xi<8;xi+=2)
		{
			int res_x = (x >> 1) + xi;
			int res_y = y;

			u16 pen = ext_gvram[(address << 2) + (xi >> 1) + (m_vram_disp*0x20000)];

			bitmap.pix(res_y, res_x) = palette[(pen & 0xff) + 0x20];
			bitmap.pix(res_y, res_x+1) = palette[(pen >> 8) + 0x20];
		}
	}
	else
		pc9801_state::hgdc_display_pixels(bitmap, y, x, address);
}
#endif

void pc9821_state::pc9821_egc_w(offs_t offset, u16 data, u16 mem_mask)
{
	if(!m_ex_video_ff[2])
		return;

	egc_w(offset, data, mem_mask);
}

void pc9821_state::pc9821_video_ff_w(offs_t offset, uint8_t data)
{
	if(offset == 1)
	{
		if(((data & 0xfe) == 4) && !m_ex_video_ff[3]) // TODO: many other settings are protected
			return;
		m_ex_video_ff[(data & 0xfe) >> 1] = data & 1;

		if((data & 0xfe) == 0x20)
		{
			if (data & 1)
				m_pegc_mmio_view.select(0);
			else
				m_pegc_mmio_view.disable();
		}
	}

	/* Intentional fall-through */
	pc9801rs_video_ff_w(offset,data);
}

uint8_t pc9821_state::pc9821_a0_r(offs_t offset)
{
	if((offset & 1) == 0 && offset & 8)
	{
		if(m_ex_video_ff[ANALOG_256_MODE])
		{
			logerror("256 color mode [%02x] R\n",offset);
			return 0;
		}
		else if(m_ex_video_ff[ANALOG_16_MODE]) //16 color mode, readback possible there
		{
			uint8_t res = 0;

			switch(offset)
			{
				case 0x08: res = m_analog16.pal_entry & 0xf; break;
				case 0x0a: res = m_analog16.g[m_analog16.pal_entry] & 0xf; break;
				case 0x0c: res = m_analog16.r[m_analog16.pal_entry] & 0xf; break;
				case 0x0e: res = m_analog16.b[m_analog16.pal_entry] & 0xf; break;
			}

			return res;
		}
	}

	return pc9801_a0_r(offset);
}

void pc9821_state::pc9821_a0_w(offs_t offset, uint8_t data)
{
	if((offset & 1) == 0 && offset & 8 && m_ex_video_ff[ANALOG_256_MODE])
	{
		switch(offset)
		{
			case 0x08: m_pegc.pal_entry = data & 0xff; break;
			case 0x0a: m_pegc.g[m_pegc.pal_entry] = data & 0xff; break;
			case 0x0c: m_pegc.r[m_pegc.pal_entry] = data & 0xff; break;
			case 0x0e: m_pegc.b[m_pegc.pal_entry] = data & 0xff; break;
		}

		m_palette->set_pen_color(
			m_pegc.pal_entry + 0x20,
			m_pegc.r[m_pegc.pal_entry],
			m_pegc.g[m_pegc.pal_entry],
			m_pegc.b[m_pegc.pal_entry]
		);
		return;
	}

	pc9801rs_a0_w(offset,data);
}

uint8_t pc9821_state::window_bank_r(offs_t offset)
{
	if(offset == 1)
		return m_pc9821_window_bank & 0xfe;

	return 0xff;
}

void pc9821_state::window_bank_w(offs_t offset, uint8_t data)
{
	if(offset == 1)
		m_pc9821_window_bank = data & 0xfe;
	else
		logerror("PC-9821 $f0000 window bank %02x\n",data);
}

/* basically a read-back of various registers */
// bit 1: GDC clock select (port 0x6a, selects with 0x84 & bit 0)
// bit 0: current setting
uint8_t pc9821_state::ext2_video_ff_r()
{
	uint8_t res;

	res = 0;

	switch(m_ext2_ff)
	{
//      case 0x00: ?
//      case 0x01: 200 line color / b&w mode (i/o 0x68 -> 0x02)
//      case 0x02: Odd-numbered raster mask  (i/o 0x68 -> 0x08)
		case 0x03: res = m_video_ff[DISPLAY_REG]; break; // display reg
//      case 0x04: palette mode (i/o 0x6a -> 0x00)
//      case 0x05: GDC sync mode (i/o 0x6a -> 0x40)
//      case 0x06: unknown (i/o 0x6a -> 0x44)
//      case 0x07: EGC compatibility mode (i/o 0x6a -> 0x04)
//      case 0x08: Protected mode f/f (i/o 0x6a -> 0x06)
//      case 0x09: GDC clock #0 (i/o 0x6a -> 0x82)
		case 0x0a: res = m_ex_video_ff[ANALOG_256_MODE]; break; // 256 color mode
//      case 0x0b: VRAM access mode (i/o 0x6a -> 0x62)
//      case 0x0c: unknown
//      case 0x0d: VRAM boundary mode (i/o 0x6a -> 0x68)
//      case 0x0e: 65,536 color GFX mode (i/o 0x6a -> 0x22)
//      case 0x0f: 65,536 color palette mode (i/o 0x6a -> 0x24)
//      case 0x10: unknown (i/o 0x6a -> 0x6a)
//      case 0x11: Reverse mode related (i/o 0x6a -> 0x26)
//      case 0x12: 256 color overscan color (i/o 0x6a -> 0x2c)
//      case 0x13: Reverse mode related (i/o 0x6a -> 0x28)
//      case 0x14: AGDC Drawing processor selection (i/o 0x6a -> 0x66)
//      case 0x15: unknown (i/o 0x6a -> 0x60)
//      case 0x16: unknown (i/o 0x6a -> 0xc2)
//      case 0x17: bitmap config direction (i/o 0x6a -> 0x6c)
//      case 0x18: High speed palette write (i/o 0x6a -> 0x2a)
//      case 0x19: unknown (i/o 0x6a -> 0x48)
//      case 0x1a: unknown (i/o 0x6a -> 0xc8)
//      case 0x1b: unknown (i/o 0x6a -> 0x2e)
//      case 0x1c: unknown (i/o 0x6a -> 0x6e)
//      case 0x1d: unknown (i/o 0x6a -> 0xc0)
//      case 0x1e: unknown (i/o 0x6a -> 0x80 or 0x46?)
//      case 0x1f: unknown (i/o 0x6a -> 0x08)
		default:
			if(m_ext2_ff < 0x20)
				popmessage("PC-9821: read ext2 f/f with value %02x",m_ext2_ff);
			break;
	}

	res |= (m_ex_video_ff[GDC_IS_5MHz] << 1);

	return res;
}

void pc9821_state::ext2_video_ff_w(uint8_t data)
{
	m_ext2_ff = data;
}

// TODO: rename ANALOG_256 refs to pegc
// (it's seemingly the official NEC naming)
uint16_t pc9821_state::pc9821_grcg_gvram_r(offs_t offset, uint16_t mem_mask)
{
	if(m_ex_video_ff[ANALOG_256_MODE])
	{
		u16 *ext_gvram = (u16 *)m_ext_gvram.target();
		int bank = offset >> 14;
		if(bank <= 1)
			return ext_gvram[((m_pegc.bank[bank])*0x4000) + (offset & 0x3fff)];
		return 0xffff;
	}

	return grcg_gvram_r(offset, mem_mask);
}

void pc9821_state::pc9821_grcg_gvram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if(m_ex_video_ff[ANALOG_256_MODE])
	{
		u16 *ext_gvram = (u16 *)m_ext_gvram.target();
		int bank = offset >> 14;
		if(bank <= 1)
			COMBINE_DATA(&ext_gvram[((m_pegc.bank[bank])*0x4000) + (offset & 0x3fff)]);
		return;
	}

	grcg_gvram_w(offset,data,mem_mask);
}


void pc9821_state::pc9821_mode_ff_w(u8 data)
{
	const u8 mode_ff = data & 0xfe;
	const u8 setting = BIT(data, 0);
	// 24/31 kHz Monitor setting
	// BA / BX / PC-H98 / PC-9821 / 98NOTE uses this f/f in place of 15/24 kHz switch
	// TODO: better compose
	// TODO: both frequencies needs to be verified
	// TODO: os2warp3 still runs at wrong clock, why?
	// 31 kHz from standard VGA clock (flashb)
	if (mode_ff == 0x20)
	{
		const XTAL screen_clock = (setting ? XTAL(25'175'000) : XTAL(21'052'600)) / 8;

		m_hgdc[0]->set_unscaled_clock(screen_clock);
		m_hgdc[1]->set_unscaled_clock(screen_clock);
	}
	else
		logerror("Mode f/f $0068: [%02x] -> %02x\n", mode_ff, setting);
}

// $e0000 base
void pc9821_state::pegc_mmio_map(address_map &map)
{
	map(0x004, 0x004).select(2).lrw8(
		NAME([this] (offs_t offset) {
			return m_pegc.bank[offset];
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_pegc.bank[offset] = data & 0xf;
		})
	);
	map(0x100, 0x100).lw8(
		NAME([this] (u8 data) {
			m_pegc.packed_mode = bool(BIT(data, 0));
			logerror("$e0100 packed mode %02x\n", data);
		})
	);
//  map(0x102, 0x102) enable pegc linear VRAM at upper addresses
	// $4a0 alias
	map(0x104, 0x104).lw8(
		NAME([this] (u8 data) {
			pc9821_egc_w(0x0 / 2, data, 0x00ff);
		})
	);
	// $4a4 alias
	map(0x108, 0x109).lw16(
		NAME([this] (u16 data, u16 mem_mask) {
			pc9821_egc_w(0x4 / 2, data, mem_mask);
		})
	);
//  map(0x10a, 0x10a) enable color comparator when reading VRAM
	// $4a8 alias (mask)
	// TODO: verify what happens on 32-bit accesses
	map(0x10c, 0x10d).lw16(
		NAME([this] (u16 data, u16 mem_mask) {
			pc9821_egc_w(0x8 / 2, data, mem_mask);
		})
	);
	// $4ae alias (block transfer)
	map(0x110, 0x111).lw16(
		NAME([this] (u16 data, u16 mem_mask) {
			pc9821_egc_w(0xe / 2, data, mem_mask);
		})
	);
	// $4ac alias (shift reg)
	map(0x112, 0x113).lw16(
		NAME([this] (u16 data, u16 mem_mask) {
			pc9821_egc_w(0xc / 2, data, mem_mask);
		})
	);
	// $4a6 alias (foreground color)
	map(0x114, 0x114).lw8(
		NAME([this] (u8 data) {
			pc9821_egc_w(0x6 / 2, data, 0xff);
		})
	);
	// $4aa alias (background color)
	map(0x118, 0x118).lw8(
		NAME([this] (u8 data) {
			pc9821_egc_w(0xa / 2, data, 0xff);
		})
	);
//  map(0x120, 0x19f) pattern register (image_xfer? relates to bit 15 of $108)
}

// make sure the latch read doesn't occur mid cycle, 8253 needs this?
void pc9821_state::pit_latch_delay(offs_t offset, uint8_t data)
{
	if((offset == 3) && !(data & 0x30))
	{

		m_pit_delay->adjust(attotime::from_hz(MAIN_CLOCK_X1/2));
		m_maincpu->spin_until_time(attotime::from_hz(MAIN_CLOCK_X1/2));
		m_maincpu->abort_timeslice();
		m_pit_latch_cmd = data;
		return;
	}
	m_pit->write(offset, data);
}

TIMER_CALLBACK_MEMBER(pc9821_state::pit_delay)
{
	m_pit->write(3, m_pit_latch_cmd);
}

void pc9821_state::pc9821_map(address_map &map)
{
	pc9801bx2_map(map);
	map(0x000a8000, 0x000bffff).rw(FUNC(pc9821_state::pc9821_grcg_gvram_r), FUNC(pc9821_state::pc9821_grcg_gvram_w));
//  map(0x000cc000, 0x000cffff).rom().region("sound_bios", 0); //sound BIOS
//  map(0x000d8000, 0x000d9fff).rom().region("ide",0)
//  map(0x000da000, 0x000dbfff).ram(); // ide ram
	map(0x000e0000, 0x000e7fff).rw(FUNC(pc9821_state::grcg_gvram0_r), FUNC(pc9821_state::grcg_gvram0_w));
	map(0x000e0000, 0x000e7fff).view(m_pegc_mmio_view);
	m_pegc_mmio_view[0](0x000e0000, 0x000e7fff).m(*this, FUNC(pc9821_state::pegc_mmio_map));
	map(0x00f00000, 0x00f9ffff).ram().share("ext_gvram");
	map(0xfff00000, 0xfff9ffff).ram().share("ext_gvram");
}

void pc9821_state::pc9821_io(address_map &map)
{
	// later SW expects unmapped C-Bus accesses to return high for proper card detection
	// cfr. entax, amarankh, freebsd21
	map.unmap_value_high();
	pc9801bx2_io(map);
	map(0x0000, 0x001f).rw(m_dmac, FUNC(am9517a_device::read), FUNC(am9517a_device::write)).umask32(0xff00ff00);
	map(0x0000, 0x001f).lr8(NAME([this] (offs_t o) { return BIT(o, 1) ? 0xff : pic_r(o); })).umask32(0x00ff00ff);
	map(0x0000, 0x001f).w(FUNC(pc9821_state::pic_w)).umask32(0x00ff00ff);  // i8259 PIC (bit 3 ON slave / master) / i8237 DMA
	map(0x0020, 0x0020).w(FUNC(pc9821_state::rtc_w));
	map(0x0022, 0x0022).lw8(NAME([this] (offs_t offset, u8 data) {
		// TODO: r/w to both ports, superset of uPD4990A
		// Reportedly buggy with DOS/Win95 off the bat, can use HRTIMER.SYS/BCKWHEAT.SYS as fallback
		if (BIT(data, 4))
			popmessage("rtc_w: extended uPD4993(A) mode enable %02x", data);
	}));
	map(0x0020, 0x002f).w(FUNC(pc9821_state::dmapg8_w)).umask32(0xff00ff00);
	map(0x0030, 0x0037).rw(m_ppi_sys, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask32(0xff00ff00); //i8251 RS232c / i8255 system port
	map(0x0040, 0x0047).rw(m_ppi_prn, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask32(0x00ff00ff);
	map(0x0040, 0x0043).rw(m_sio_kbd, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0xff00); //i8255 printer port / i8251 keyboard
//  map(0x0050, 0x0053).w(FUNC(pc9821_state::nmi_ctrl_w)).umask32(0x00ff00ff);
//  map(0x005c, 0x005f).r(FUNC(pc9821_state::timestamp_r)).nopw(); // artic
	map(0x005c, 0x005f).lw8([this](offs_t o) { m_maincpu->spin_until_time(attotime::from_hz(MAIN_CLOCK_X1/3)); m_maincpu->abort_timeslice();}, "bus delay"); // simulate bus delay
//  map(0x0060, 0x0063).rw(m_hgdc[0], FUNC(upd7220_device::read), FUNC(upd7220_device::write)).umask32(0x00ff00ff); //upd7220 character ports / <undefined>
//  map(0x0060, 0x0063).r(FUNC(pc9821_state::unk_r)).umask32(0xff00ff00); // mouse related (unmapped checking for AT keyb controller\PS/2 mouse?)
//  map(0x0064, 0x0064).w(FUNC(pc9821_state::vrtc_clear_w));
	map(0x0068, 0x006b).w(FUNC(pc9821_state::pc9821_video_ff_w)).umask32(0x00ff00ff); //mode FF / <undefined>
	map(0x006c, 0x006d).w(FUNC(pc9821_state::border_color_w)).umask16(0x00ff);
	map(0x006e, 0x006f).w(FUNC(pc9821_state::pc9821_mode_ff_w)).umask16(0x00ff);
	map(0x0070, 0x007f).r(m_pit, FUNC(pit8253_device::read)).umask16(0xff00);
	map(0x0070, 0x007f).w(FUNC(pc9821_state::pit_latch_delay)).umask16(0xff00);
//  map(0x0070, 0x007f).rw(FUNC(pc9821_state::grcg_r), FUNC(pc9821_state::grcg_w)).umask32(0x00ff00ff); //display registers "GRCG" / i8253 pit
	map(0x0090, 0x0093).m(m_fdc_2hd, FUNC(upd765a_device::map)).umask32(0x00ff00ff);
	map(0x0094, 0x0094).rw(FUNC(pc9821_state::fdc_2hd_ctrl_r), FUNC(pc9821_state::fdc_2hd_ctrl_w));
	map(0x00a0, 0x00af).rw(FUNC(pc9821_state::pc9821_a0_r), FUNC(pc9821_state::pc9821_a0_w)); //upd7220 bitmap ports / display registers
//  map(0x00b0, 0x00b3) PC9861k (serial port?)
//  map(0x00b9, 0x00b9) PC9861k
//  map(0x00bb, 0x00bb) PC9861k
//  map(0x00bc, 0x00bf).rw(FUNC(pc9821_state::fdc_mode_ctrl_r), FUNC(pc9821_state::fdc_mode_ctrl_w));
	map(0x00c8, 0x00cb).m(m_fdc_2hd, FUNC(upd765a_device::map)).umask32(0x00ff00ff);
//  map(0x00cc, 0x00cc).rw(FUNC(pc9821_state::fdc_2hd_ctrl_r), FUNC(pc9821_state::fdc_2hd_ctrl_w));
	//  map(0x00d8, 0x00df) AMD98 (sound?) board
//  map(0x00f0, 0x00ff).rw(FUNC(pc9821_state::a20_ctrl_r), FUNC(pc9821_state::a20_ctrl_w)).umask32(0x00ff00ff);
//  map(0x0188, 0x018f).rw(FUNC(pc9821_state::pc9801_opn_r), FUNC(pc9821_state::pc9801_opn_w)); //ym2203 opn / <undefined>
//  map(0x018c, 0x018f) YM2203 OPN extended ports / <undefined>
//  map(0x0430, 0x0433).rw(FUNC(pc9821_state::ide_ctrl_r), FUNC(pc9821_state::ide_ctrl_w)).umask32(0x00ff00ff);
//  map(0x0438, 0x043b).rw(FUNC(pc9821_state::access_ctrl_r), FUNC(pc9821_state::access_ctrl_w));
//  map(0x043c, 0x043f).w(FUNC(pc9821_state::pc9801rs_bank_w)); //ROM/RAM bank (NEC)
//  map(0x043c, 0x043f) ROM/RAM bank (EPSON)
	map(0x0460, 0x0463).rw(FUNC(pc9821_state::window_bank_r), FUNC(pc9821_state::window_bank_w));
	map(0x04a0, 0x04af).w(FUNC(pc9821_state::pc9821_egc_w));
	map(0x04be, 0x04be).rw(FUNC(pc9821_state::fdc_3mode_r), FUNC(pc9821_state::fdc_3mode_w));
//  map(0x0640, 0x064f).rw(FUNC(pc9821_state::ide_cs0_r), FUNC(pc9821_state::ide_cs0_w));
//  map(0x0740, 0x074f).rw(FUNC(pc9821_state::ide_cs1_r), FUNC(pc9821_state::ide_cs1_w));
//  map(0x08e0, 0x08ea) <undefined> / EMM SIO registers
	map(0x09a0, 0x09a0).rw(FUNC(pc9821_state::ext2_video_ff_r), FUNC(pc9821_state::ext2_video_ff_w)); // GDC extended register r/w
//  map(0x09a8, 0x09a8) GDC 31KHz register r/w
//  map(0x0c07, 0x0c07) EPSON register w
//  map(0x0c03, 0x0c03) EPSON register 0 r
//  map(0x0c13, 0x0c14) EPSON register 1 r
//  map(0x0c24, 0x0c24) cs4231 PCM board register control
//  map(0x0c2b, 0x0c2b) cs4231 PCM board low byte control
//  map(0x0c2d, 0x0c2d) cs4231 PCM board hi byte control
	map(0x0ca0, 0x0ca0).lr8(NAME([] () { return 0xff; })); // high reso detection
//  map(0x0cc0, 0x0cc7) SCSI interface / <undefined>
//  map(0x0cfc, 0x0cff) PCI bus
	map(0x1e8c, 0x1e8f).noprw(); // TODO: IDE RAM switch
	map(0x2ed0, 0x2edf).lr8(NAME([] (address_space &s, offs_t o, u8 mm) { return 0xff; })).umask32(0xffffffff); // unknown sound related
	map(0x3fd8, 0x3fdf).r(m_pit, FUNC(pit8253_device::read)).umask16(0xff00);
	map(0x3fd8, 0x3fdf).w(FUNC(pc9821_state::pit_latch_delay)).umask16(0xff00);
//  map(0x7fd8, 0x7fdf).rw(m_ppi_mouse, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask32(0xff00ff00);
//  map(0x841c, 0x8f1f).rw(FUNC(pc9821_state::sdip_r<0x0>), FUNC(pc9821_state::sdip_w<0x0>));
//  map(0xa460, 0xa46f) cs4231 PCM extended port / <undefined>
	map(0xbfdb, 0xbfdb).rw(FUNC(pc9821_state::mouse_freq_r), FUNC(pc9821_state::mouse_freq_w));
//  map(0xc0d0, 0xc0d3) MIDI port, option 0 / <undefined>
//  map(0xc4d0, 0xc4d3) MIDI port, option 1 / <undefined>
//  map(0xc8d0, 0xc8d3) MIDI port, option 2 / <undefined>
//  map(0xccd0, 0xccd3) MIDI port, option 3 / <undefined>
//  map(0xd0d0, 0xd0d3) MIDI port, option 4 / <undefined>
//  map(0xd4d0, 0xd4d3) MIDI port, option 5 / <undefined>
//  map(0xd8d0, 0xd8d3) MIDI port, option 6 / <undefined>
//  map(0xdcd0, 0xdcd3) MIDI port, option 7 / <undefined>
//  map(0xe0d0, 0xe0d3).r(FUNC(pc9821_state::midi_r)); // MIDI port, option 8 / <undefined>
//  map(0xe4d0, 0xe4d3) MIDI port, option 9 / <undefined>
//  map(0xe8d0, 0xe8d3) MIDI port, option A / <undefined>
//  map(0xecd0, 0xecd3) MIDI port, option B / <undefined>
//  map(0xf0d0, 0xf0d3) MIDI port, option C / <undefined>
//  map(0xf4d0, 0xf4d3) MIDI port, option D / <undefined>
//  map(0xf8d0, 0xf8d3) MIDI port, option E / <undefined>
//  map(0xfcd0, 0xfcd3) MIDI port, option F / <undefined>
}

/*
 * 98MATE A overrides
 */
// TODO: SDIP extended access for 9821Ap, As, Ae
// Undocumented, it never r/w the conventional ports, at least on POST.
// I also suspect a few ports here not being direct RAM r/w but actual regs instead.
// 0xf834f: checks against aa55, throws "ROM SUM ERROR" if bypassed
// 0xf8363: sum8 contents of [0x02], 10 times (???)
u8 pc9821_mate_a_state::ext_sdip_data_r(offs_t offset)
{
	logerror("%s: EXT SDIP access read %02x %02x\n", machine().describe_context(), m_ext_sdip_addr, m_ext_sdip[m_ext_sdip_addr]);
	return m_ext_sdip[m_ext_sdip_addr];
}

void pc9821_mate_a_state::ext_sdip_data_w(offs_t offset, u8 data)
{
	logerror("%s: EXT SDIP access write [%02x] -> %02x\n", machine().describe_context(), m_ext_sdip_addr, data);

	m_ext_sdip[m_ext_sdip_addr] = data;
}

void pc9821_mate_a_state::ext_sdip_access_w(offs_t offset, u8 data)
{
	// access enable?
}

void pc9821_mate_a_state::ext_sdip_address_w(offs_t offset, uint8_t data)
{
	m_ext_sdip_addr = data;
}

void pc9821_mate_a_state::itf_43d_bank_w(offs_t offset, uint8_t data)
{
	// assume overlay disabled on writes to $43d
	m_bios_view.disable();
	pc9801vm_state::itf_43d_bank_w(offset, data);
}

void pc9821_mate_a_state::cbus_43f_bank_w(offs_t offset, uint8_t data)
{
	if ((data & 0xf8) == 0xe0)
	{
		logerror("C-Bus overlay set %02x\n", data);
		m_bios_view.select(data & 0x7);
		return;
	}

	// Exit setup mode disarms overlay with a 0xe8 write
	// (or writes are >> 1 and undocumented mem is wrong?)
	if ((data & 0xf8) == 0xe8)
	{
		logerror("C-Bus overlay disable (%02x)\n", data);
		m_bios_view.disable();
		return;
	}

	pc9801vm_state::cbus_43f_bank_w(offset, data);
}

void pc9821_mate_a_state::pc9821as_map(address_map &map)
{
	pc9821_map(map);
	map(0x000f8000, 0x000fffff).view(m_bios_view);
	// TODO: remaining settings
	// pc9821as uses this
	m_bios_view[4](0x000f8000, 0x000fffff).rom().region("biosrom", 0x10000);
	// setup mode
	m_bios_view[6](0x000f8000, 0x000fffff).rom().region("biosrom", 0x18000);
}

void pc9821_mate_a_state::pc9821as_io(address_map &map)
{
	pc9821_io(map);
	map(0x0468, 0x0468).rw(FUNC(pc9821_mate_a_state::ext_sdip_data_r), FUNC(pc9821_mate_a_state::ext_sdip_data_w));
	map(0x046a, 0x046a).w(FUNC(pc9821_mate_a_state::ext_sdip_access_w));
	map(0x046c, 0x046c).w(FUNC(pc9821_mate_a_state::ext_sdip_address_w));
	// TODO: specific MATE A local bus (overlays just like C-Bus?)
}

/*
 * CanBe overrides
 */

/*
 * CanBe Remote control
 * I/O $f4a: remote index (write only?)
 * I/O $f4b: remote data r/w
 * [0x00] <unknown>
 * [0x01] Windows Sound System related
 * ---- xxxx irq select
 * ---- 1000 INT8
 * ---- 0011 INT0
 * ---- 0010 INT41
 * ---- 0000 INT5
 * [0x02] <unknown>
 * [0x03] Remote control reset
 * ---- -x-- (1) Mic through (loopback?)
 * ---- ---x (1) Remote control reset
 * [0x04] Mute control
 * ---- ---x (Global?) sound mute
 * [0x10] Remote control data status
 * ---- --x- (1) device ready (0) busy
 * ---- ---x (1) received data available
 * [0x11] Remote control code
 * <returns the button pressed in 2 bytes form, 2nd byte is the XOR-ed version of 1st>
 * [0x12] <unknown>
 * [0x13] Power control
 * <succession of bytes to power on/off>
 * [0x14] remote irq control
 * ---- -x-- irq enable
 * ---- --xx irq select
 * ---- --11 INT41
 * ---- --10 INT1
 * ---- --01 INT2
 * ---- --00 INT0
 * [0x30] VOL1,2 YMF288 Left sound output
 * [0x31] VOL1,2 YMF288 Right sound output
 * [0x32] VOL3,4 Line Left input
 * [0x33] VOL3,4 Line Right input
 * [0x34] <unknown>
 * [0x35] <unknown>
 */
// TODO: export remote control to an actual device, and pinpoint actual name
void pc9821_canbe_state::remote_addr_w(offs_t offset, u8 data)
{
	m_remote.index = data;
}

u8 pc9821_canbe_state::remote_data_r(offs_t offset)
{
	uint8_t res;

	res = 0;

	logerror("%s: remote control reg read %02x\n", machine().describe_context(), m_remote.index);

	switch(m_remote.index)
	{
		case 0x10:
			res |= 2; // POST will throw "MICOM ERROR" otherwise
			break;
	}
	return res;
}

void pc9821_canbe_state::remote_data_w(offs_t offset, u8 data)
{
	switch(m_remote.index)
	{
		default:
			logerror("%s: remote control reg write %02x %02x\n", machine().describe_context(), m_remote.index, data);
	}
}

void pc9821_canbe_state::pc9821cx3_map(address_map &map)
{
	pc9821_map(map);
	// TODO: overwritten by C-bus mapping, but definitely tested as RAM on POST
	map(0x000c0000, 0x000dffff).ram();
}

void pc9821_canbe_state::pc9821cx3_io(address_map &map)
{
	pc9821_io(map);
	map(0x0f4a, 0x0f4a).w(FUNC(pc9821_canbe_state::remote_addr_w));
	map(0x0f4b, 0x0f4b).rw(FUNC(pc9821_canbe_state::remote_data_r), FUNC(pc9821_canbe_state::remote_data_w));
}

static INPUT_PORTS_START( pc9821 )
	PORT_START("DSW1")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_DEVICE_MEMBER("sdip", FUNC(pc98_sdip_device::dsw1_r))

	// HACK: should read from SDIP dsw2_r
	// will break pc9821 for parity check, cfr. dump notes
	PORT_START("DSW2")
	PORT_DIPNAME( 0x01, 0x00, "DSW2" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, "GDC clock" )
	PORT_DIPSETTING(    0x80, "2.5 MHz" )
	PORT_DIPSETTING(    0x00, "5 MHz" )
	// PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_DEVICE_MEMBER("sdip", FUNC(pc98_sdip_device::dsw2_r))

	PORT_START("DSW3")
	PORT_BIT( 0x23, IP_ACTIVE_LOW, IPT_CUSTOM ) //PORT_CUSTOM_DEVICE_MEMBER("sdip", FUNC(pc98_sdip_device::dsw3_r))

	// TODO: make a mouse device, not unlike pc9801_epson.cpp
	PORT_START("MOUSE_X")
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_X ) PORT_SENSITIVITY(30) PORT_KEYDELTA(30)

	PORT_START("MOUSE_Y")
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y ) PORT_SENSITIVITY(30) PORT_KEYDELTA(30)

	PORT_START("MOUSE_B")
	PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_CODE(MOUSECODE_BUTTON2) PORT_NAME("Mouse Right Button")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_CODE(MOUSECODE_BUTTON3) PORT_NAME("Mouse Middle Button")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CODE(MOUSECODE_BUTTON1) PORT_NAME("Mouse Left Button")

	PORT_START("ROM_LOAD")
	PORT_BIT( 0x03, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_CONFNAME( 0x04, 0x04, "Load IDE BIOS" )
	PORT_CONFSETTING(    0x00, DEF_STR( Yes ) )
	PORT_CONFSETTING(    0x04, DEF_STR( No ) )
INPUT_PORTS_END

MACHINE_START_MEMBER(pc9821_state,pc9821)
{
	m_pit_delay = timer_alloc(FUNC(pc9821_state::pit_delay), this);
	MACHINE_START_CALL_MEMBER(pc9801bx2);

	// ...
}

MACHINE_START_MEMBER(pc9821_mate_a_state,pc9821ap2)
{
	MACHINE_START_CALL_MEMBER(pc9821);

	// ...
}

MACHINE_START_MEMBER(pc9821_canbe_state,pc9821_canbe)
{
	MACHINE_START_CALL_MEMBER(pc9821);

	// ...
}

MACHINE_RESET_MEMBER(pc9821_state,pc9821)
{
	MACHINE_RESET_CALL_MEMBER(pc9801rs);

	m_pc9821_window_bank = 0x08;
	m_pegc_mmio_view.disable();
}

// TODO: setter for DMAC clock should follow up whatever is the CPU clock

void pc9821_state::pc9821(machine_config &config)
{
	// TODO: specs for a vanilla MULTi doesn't match
	// should be 386sx at 20 MHz, this may be "just" a FA/BX class instead
	pc9801rs(config);
	const auto xtal = BASE_CLOCK / 2;
	I486(config.replace(), m_maincpu, xtal); // unknown clock
	m_maincpu->set_addrmap(AS_PROGRAM, &pc9821_state::pc9821_map);
	m_maincpu->set_addrmap(AS_IO, &pc9821_state::pc9821_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	m_pit->set_clk<0>(MAIN_CLOCK_X2);
	m_pit->set_clk<1>(MAIN_CLOCK_X2);
	m_pit->set_clk<2>(MAIN_CLOCK_X2);

	m_cbus[0]->set_default_option("pc9801_86");

	MCFG_MACHINE_START_OVERRIDE(pc9821_state, pc9821)
	MCFG_MACHINE_RESET_OVERRIDE(pc9821_state, pc9821)

	m_dmac->set_clock(xtal); // unknown clock

	PALETTE(config.replace(), m_palette, FUNC(pc9821_state::pc9801_palette), 16 + 16 + 256);

	// win95 expects to be A revision, otherwise it will overlay startup text prompts over desktop GFX
	// NOTE: possibly this bump happened around PC-9801BX series
	UPD7220A(config.replace(), m_hgdc[0], 21.0526_MHz_XTAL / 8, "screen");
	UPD7220A(config.replace(), m_hgdc[1], 21.0526_MHz_XTAL / 8, "screen");
	config_video(config);

	PC98_119_KBD(config.replace(), m_keyb, 0);
	m_keyb->rxd_callback().set("sio_kbd", FUNC(i8251_device::write_rxd));

//  m_hgdc[1]->set_display_pixels(FUNC(pc9821_state::pegc_display_pixels));

	PC98_SDIP(config, "sdip", 0);
}

void pc9821_mate_a_state::pc9821as(machine_config &config)
{
	pc9821(config);
	const XTAL xtal = XTAL(33'000'000);
	I486(config.replace(), m_maincpu, xtal); // i486dx
	m_maincpu->set_addrmap(AS_PROGRAM, &pc9821_mate_a_state::pc9821as_map);
	m_maincpu->set_addrmap(AS_IO, &pc9821_mate_a_state::pc9821as_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	// 9821Ap, As, Ae has no dips to port A but reads back written content
	m_ppi_sys->in_pa_callback().set(m_ppi_sys, FUNC(i8255_device::pa_r));

	MCFG_MACHINE_START_OVERRIDE(pc9821_mate_a_state, pc9821ap2)
}

void pc9821_mate_a_state::pc9821ap2(machine_config &config)
{
	pc9821(config);
	const XTAL xtal = XTAL(66'000'000);
	I486(config.replace(), m_maincpu, xtal); // i486dx2
	m_maincpu->set_addrmap(AS_PROGRAM, &pc9821_mate_a_state::pc9821as_map);
	m_maincpu->set_addrmap(AS_IO, &pc9821_mate_a_state::pc9821_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	//pit_clock_config(config, xtal / 4); // unknown, fixes timer error at POST

	MCFG_MACHINE_START_OVERRIDE(pc9821_mate_a_state, pc9821ap2)
}

void pc9821_canbe_state::pc9821ce2(machine_config &config)
{
	pc9821(config);
	const XTAL xtal = XTAL(25'000'000);
	I486(config.replace(), m_maincpu, xtal); // i486sx
	m_maincpu->set_addrmap(AS_PROGRAM, &pc9821_canbe_state::pc9821_map);
	m_maincpu->set_addrmap(AS_IO, &pc9821_canbe_state::pc9821_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	//pit_clock_config(config, xtal / 4); // unknown, fixes timer error at POST

	m_cbus[0]->set_default_option("pc9801_118");

	MCFG_MACHINE_START_OVERRIDE(pc9821_canbe_state, pc9821_canbe);
}

void pc9821_canbe_state::pc9821cx3(machine_config &config)
{
	pc9821(config);
	const XTAL xtal = XTAL(100'000'000); // Pentium Pro, 512 kB second cache option RAM
	PENTIUM(config.replace(), m_maincpu, xtal);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc9821_canbe_state::pc9821cx3_map);
	m_maincpu->set_addrmap(AS_IO, &pc9821_canbe_state::pc9821cx3_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	//pit_clock_config(config, xtal / 4); // unknown, fixes timer error at POST

//  m_cbus[0]->set_default_option(nullptr);
	m_cbus[0]->set_default_option("pc9801_118");

	MCFG_MACHINE_START_OVERRIDE(pc9821_canbe_state, pc9821_canbe);

	// VLSI Supercore594 (Wildcat) PCI 2.0
	// GD5440
	// built-in 3.5 floppy x 1
	// file bay with built-in CD-Rom (4x)
	// HDD with pre-installed software (850MB, 1.2GB)
	// minimum RAM: 16MB
	// maximum RAM: 128MB
	// C-Bus x 3
	// PC-9821CB-B04, on dedicated bus (Fax/Modem 14'400 bps) and IrDA board (115'200 bps)
	// Optional PC-9821C3-B02 MIDI board, on dedicated bus
}

void pc9821_mate_x_state::pc9821xs(machine_config &config)
{
	pc9821(config);
	const XTAL xtal = XTAL(66'000'000);
	I486(config.replace(), m_maincpu, xtal); // i486dx2
	m_maincpu->set_addrmap(AS_PROGRAM, &pc9821_mate_x_state::pc9821_map);
	m_maincpu->set_addrmap(AS_IO, &pc9821_mate_x_state::pc9821_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));
}

void pc9821_mate_x_state::pc9821xa16(machine_config &config)
{
	pc9821(config);
	PENTIUM(config.replace(), m_maincpu, 166000000); // Pentium P54C
	m_maincpu->set_addrmap(AS_PROGRAM, &pc9821_mate_x_state::pc9821_map);
	m_maincpu->set_addrmap(AS_IO, &pc9821_mate_x_state::pc9821_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));
}

void pc9821_mate_r_state::pc9821ra20(machine_config &config)
{
	pc9821(config);
	PENTIUM_PRO(config.replace(), m_maincpu, XTAL(200'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &pc9821_mate_r_state::pc9821_map);
	m_maincpu->set_addrmap(AS_IO, &pc9821_mate_r_state::pc9821_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));
}

void pc9821_mate_r_state::pc9821ra266(machine_config &config)
{
	pc9821(config);
	const double xtal = 266000000;
	PENTIUM2(config.replace(), m_maincpu, xtal);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc9821_mate_r_state::pc9821_map);
	m_maincpu->set_addrmap(AS_IO, &pc9821_mate_r_state::pc9821_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	// 512KB CPU cache RAM
	// Trident TGUI9682XGi + integrated 98 gfx card
	// 3x cbus + 2x PCI slots
	// 3GB HDD
	// built-in ethernet 100BASE-TX/10BASE-T
}

void pc9821_mate_r_state::pc9821ra333(machine_config &config)
{
	pc9821(config);
	const double xtal = 333000000;
	PENTIUM2(config.replace(), m_maincpu, xtal); // actually a Celeron
	m_maincpu->set_addrmap(AS_PROGRAM, &pc9821_mate_r_state::pc9821_map);
	m_maincpu->set_addrmap(AS_IO, &pc9821_mate_r_state::pc9821_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	// 128KB CPU cache RAM
	// Trident TGUI9682XGi + integrated 98 gfx card
	// 3x cbus + 2x PCI slots
	// 6GB HDD
	// built-in ethernet 100BASE-TX/10BASE-T
}

// 9821 NOTE machine configs

void pc9821_note_lavie_state::pc9821nr15(machine_config &config)
{
	pc9821(config);
//  const XTAL xtal = XTAL(150'000'000);
	const double xtal = 150000000;
	PENTIUM_PRO(config.replace(), m_maincpu, xtal); // unsure if normal or pro, clock and cache size suggests latter
	m_maincpu->set_addrmap(AS_PROGRAM, &pc9821_note_lavie_state::pc9821_map);
	m_maincpu->set_addrmap(AS_IO, &pc9821_note_lavie_state::pc9821_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	// 256KB CPU cache RAM
	// TFT 12.1 screen with 800x600 max resolution
	// Trident Cyber9385 Flat Panel Controller (SVGA, PCI?)
	// -86 board
	// PCI TypeII x 2 (Type III x 1)
	// C-Bus x 1 or ZV port (?)
	// 1GB or 1.4GB HDD
	// 8x/11x CD-ROM
	// Optional FAX
}

void pc9821_note_lavie_state::pc9821nr166(machine_config &config)
{
	pc9821(config);
	const double xtal = 166000000;
	PENTIUM_MMX(config.replace(), m_maincpu, xtal);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc9821_note_lavie_state::pc9821_map);
	m_maincpu->set_addrmap(AS_IO, &pc9821_note_lavie_state::pc9821_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	// 256KB CPU cache RAM
	// TFT 13.3 screen with 1024x768 resolution
	// Trident Cyber9385 Flat Panel Controller (SVGA, PCI?)
	// PCI TypeII x 2 (Type III x 1)
	// C-Bus x 1 or ZV port (?)
	// 3GB HDD
	// Has FAX or ethernet 100BASE-TX/10BASE-T (depending on submodel type)
}

void pc9821_note_lavie_state::pc9821nw150(machine_config &config)
{
	pc9821(config);
	const double xtal = 150000000;
	PENTIUM_MMX(config.replace(), m_maincpu, xtal);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc9821_note_lavie_state::pc9821_map);
	m_maincpu->set_addrmap(AS_IO, &pc9821_note_lavie_state::pc9821_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	// 256KB CPU cache RAM
	// TFT 12.1 screen with 800x600 resolution & true color
	// Trident Cyber9385-1 Flat Panel Controller (SVGA, PCI?)
	// built-in CD-Rom x16
	// equivalent sound board -86 built-in
	// PCI TypeII x 2 (Type III x 1)
	// C-Bus x 1 or ZV port (?)
	// 2GB HDD (max 31.51GB)
	// has composite video output
	// Has 33.6Kbps FAX
	// Ni-cd (?) battery with 1.5 ~ 2.3 hours of duration
}

/* took from "raw" memory dump */
#define LOAD_IDE_ROM \
	ROM_REGION( 0x4000, "ide", ROMREGION_ERASEVAL(0xcb) ) \
	ROM_LOAD( "d8000.rom", 0x0000, 0x2000, BAD_DUMP CRC(5dda57cc) SHA1(d0dead41c5b763008a4d777aedddce651eb6dcbb) ) \
	ROM_IGNORE( 0x2000 ) \
	ROM_IGNORE( 0x2000 ) \
	ROM_IGNORE( 0x2000 )

// all of these are half size :/
#define LOAD_KANJI_ROMS \
	ROM_REGION( 0x80000, "raw_kanji", ROMREGION_ERASEFF ) \
	ROM_LOAD16_BYTE( "24256c-x01.bin", 0x00000, 0x4000, BAD_DUMP CRC(28ec1375) SHA1(9d8e98e703ce0f483df17c79f7e841c5c5cd1692) ) \
	ROM_CONTINUE(                      0x20000, 0x4000  ) \
	ROM_LOAD16_BYTE( "24256c-x02.bin", 0x00001, 0x4000, BAD_DUMP CRC(90985158) SHA1(78fb106131a3f4eb054e87e00fe4f41193416d65) ) \
	ROM_CONTINUE(                      0x20001, 0x4000  ) \
	ROM_LOAD16_BYTE( "24256c-x03.bin", 0x40000, 0x4000, BAD_DUMP CRC(d4893543) SHA1(eb8c1bee0f694e1e0c145a24152222d4e444e86f) ) \
	ROM_CONTINUE(                      0x60000, 0x4000  ) \
	ROM_LOAD16_BYTE( "24256c-x04.bin", 0x40001, 0x4000, BAD_DUMP CRC(5dec0fc2) SHA1(41000da14d0805ed0801b31eb60623552e50e41c) ) \
	ROM_CONTINUE(                      0x60001, 0x4000  ) \
	ROM_REGION( 0x100000, "kanji", ROMREGION_ERASEFF ) \
	ROM_REGION( 0x80000, "new_chargen", ROMREGION_ERASEFF )

/*
98MATE A - 80486SX 25

TODO: should access SDIP from $00f6, most likely a partial A Mate dump instead
*/

ROM_START( pc9821 )
	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	// baddump: has no code for setup mode
	ROM_LOAD( "itf.rom",  0x10000, 0x08000, BAD_DUMP CRC(dd4c7bb8) SHA1(cf3aa193df2722899066246bccbed03f2e79a74a) )
	ROM_LOAD( "bios.rom", 0x18000, 0x18000, BAD_DUMP CRC(34a19a59) SHA1(2e92346727b0355bc1ec9a7ded1b444a4917f2b9) )
	ROM_FILL(0x24c40, 4, 0) // hide the _32_ marker until we have a 32-bit clean IDE bios otherwise windows tries to
							// make a 32-bit call into 16-bit code
	ROM_FILL(0x27ffe, 1, 0x92)
	ROM_FILL(0x27fff, 1, 0xd7)

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font.rom", 0x00000, 0x46800, BAD_DUMP CRC(a61c0649) SHA1(554b87377d176830d21bd03964dc71f8e98676b1) )

	LOAD_KANJI_ROMS
	LOAD_IDE_ROM
ROM_END

/*
As - 80486DX 33
*/

ROM_START( pc9821as )
	ROM_REGION( 0x80000, "biosrom", ROMREGION_ERASEFF )
//  ROM_LOAD( "itf.rom",     0x10000, 0x08000, BAD_DUMP CRC(dd4c7bb8) SHA1(cf3aa193df2722899066246bccbed03f2e79a74a) )
//  ROM_LOAD( "bios_as.rom", 0x18000, 0x18000, BAD_DUMP CRC(0a682b93) SHA1(76a7360502fa0296ea93b4c537174610a834d367) )
	ROM_LOAD( "mvs0100-1.bin", 0x00000, 0x80000, CRC(ca37b631) SHA1(8c481dd0608d6c27235bc88bd77e345628dc28a1) )

	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	// 0x00000-0x04fff KBCRT X47 891105
	// 0x0c000-0x0ffff sound BIOS
	// 0x10000-0x13fff ^ mirror
	// 0x14000-0x16fff <unknown>
	// 0x17000-0x17fff SCSI disk BIOS?
	// 0x18000-0x19fff <unknown>
	// 0x1a000-0x1ffff setup menu
	ROM_COPY( "biosrom", 0x20000, 0x10000, 0x08000 ) // ITF
	ROM_COPY( "biosrom", 0x28000, 0x18000, 0x18000 ) // BIOS
	// 0x50000-0x57fff ? (copies stuff from $e4000, jumps there)

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font_as.rom",     0x000000, 0x046800, BAD_DUMP CRC(456d9fc7) SHA1(78ba9960f135372825ab7244b5e4e73a810002ff) )

	LOAD_KANJI_ROMS
	LOAD_IDE_ROM
ROM_END

/*
PC-9821AP2/U8W
80486DX2 66MHz
DOS 5.0, Windows 3.1
5.6MB RAM, up to 73.6MB
340MB HD
Expansion slot C-BUS4 (4)
Graphics controller S3 86C928
*/

ROM_START( pc9821ap2 )
	ROM_REGION( 0x80000, "biosrom", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS("phd0104")
	ROM_SYSTEM_BIOS(0, "phd0104",  "PHD0104")
	ROMX_LOAD( "phd0104.rom",     0x000000, 0x80000, CRC(da73b372) SHA1(2c15b63a0869b81ef7f04972dbb0975f4e77d384), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "phd0102",  "PHD0102")
	ROMX_LOAD( "phd0102.rom",     0x000000, 0x80000, CRC(3036774c) SHA1(59856a348f156adf5eca06326f967aca54ff871c), ROM_BIOS(1) )

	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	// 0x00000-0x04ff0 <unknown>
	// 0x0c000-0x0ffff internal sound BIOS?
	// 0x10000-0x13fff ^ mirror of above?
	// 0x14000-0x14ff0 <unknown>
	// 0x16000-0x19fff contains refs to 765 and HDDs "Conner Peripherals", IDE BIOS?
	// 0x1a000-0x1ffff setup menu
	ROM_COPY( "biosrom", 0x20000, 0x10000, 0x08000 ) // ITF
	ROM_COPY( "biosrom", 0x28000, 0x18000, 0x18000 ) // BIOS
	// 0x40000-0x4ffff empty
	// 0x50000-0x57fff extended BIOS check?
	// 0x58000-0x65fff empty
	// 0x66000-0x77fff mirrors of IDE or sound BIOS (left-overs?)
	// 0x78000-0x7ffff Has a (c) 1986, more left-over?

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font.rom", 0x00000, 0x46800, BAD_DUMP CRC(a61c0649) SHA1(554b87377d176830d21bd03964dc71f8e98676b1) )

	LOAD_KANJI_ROMS

	ROM_REGION( 0x4000, "ide", ROMREGION_ERASEVAL(0xcb) )
	ROM_COPY( "biosrom", 0x18000, 0x00000, 0x02000 )
ROM_END


/*
98NOTE - i486SX 33

NOTE: regular Ne shouldn't have Pico|Power Redwood PT86C768, and bios_ne.rom accesses one.
Incomplete dump, will require standalone driver out of interactions with PMC so removed.

cfr. https://github.com/angelosa/mame_scratch/blob/main/src/redwood1.cpp

*/

//ROM_START( pc9821ne )
//  ROM_LOAD( "itf.rom",     0x10000, 0x08000, BAD_DUMP CRC(dd4c7bb8) SHA1(cf3aa193df2722899066246bccbed03f2e79a74a) )
//  ROM_LOAD( "bios_ne.rom", 0x18000, 0x18000, BAD_DUMP CRC(2ae070c4) SHA1(d7963942042bfd84ed5fc9b7ba8f1c327c094172) )
//  ROM_LOAD( "font_ne.rom", 0x00000, 0x46800, BAD_DUMP CRC(fb213757) SHA1(61525826d62fb6e99377b23812faefa291d78c2e) )

/*
98MULTi Ce2 - 80486SX 25
*/

ROM_START( pc9821ce2 )
	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	// baddump: missing setup menu bank
	ROM_LOAD( "itf_ce2.rom",  0x10000, 0x008000, BAD_DUMP CRC(273e9e88) SHA1(9bca7d5116788776ed0f297bccb4dfc485379b41) )
	ROM_LOAD( "bios_ce2.rom", 0x18000, 0x018000, BAD_DUMP CRC(76affd90) SHA1(910fae6763c0cd59b3957b6cde479c72e21f33c1) )

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font_ce2.rom", 0x00000, 0x046800, BAD_DUMP CRC(d1c2702a) SHA1(e7781e9d35b6511d12631641d029ad2ba3f7daef) )

	LOAD_KANJI_ROMS
	LOAD_IDE_ROM
ROM_END

/*
PC-9821CX3

Pentium @ 100 MHz
16MB, max 128 MB
3.5x1, 4xCD-ROM
CL GD5440
PCI VLSI Supercore594 (Wildcat), PCI Rev. 2.0
*/

ROM_START( pc9821cx3 )
	ROM_REGION16_LE( 0x80000, "biosrom", ROMREGION_ERASEFF )
	// ROM BIOS rev. 0.13
	ROM_LOAD( "pc-9821cx3.bin", 0x000000, 0x080000, CRC(0360ed78) SHA1(2e4fe059d001d980add1656816bda97cd11ef331) )

	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	// TODO: all of the 512k space seems valid
	// all GFXs are 1bpp packed with different pitches
	// 0 - 0x56xx: CanBe mascot GFX animations
	// 0x1fda8 (?) - 0x2458f: monitor GFXs
	// 0x24590 - 0x36xxx: more CanBe mascot GFX animations
	// 0x3c000: NEC & CanBe logo GFXs
	// 0x40000: IDE BIOS (NEC D3766 / Caviar CP30344 / WDC AC2340H)
	// 0x42000: setup menu
	ROM_COPY( "biosrom", 0x78000, 0x10000, 0x08000 ) // ITF
	ROM_COPY( "biosrom", 0x70000, 0x18000, 0x08000 ) // BIOS, probably wrong (reset vector at 0x67ff0)
	ROM_COPY( "biosrom", 0x68000, 0x20000, 0x08000 )
	ROM_COPY( "biosrom", 0x60000, 0x28000, 0x08000 )

	// "microcode" memory dump, probably identical to above but shuffled
	// left for consultation
	ROM_REGION16_LE( 0x80000, "memory", ROMREGION_ERASEFF )
	ROM_LOAD( "bank0.bin",    0x000000, 0x008000, CRC(bccc5233) SHA1(1246203bebf9f04e3bac2df7fc64719304f9f1bd) )
	ROM_LOAD( "bank1.bin",    0x008000, 0x008000, CRC(190b448b) SHA1(01d555cb1044ea280759c5fb724b24ca38ba67f7) )
	ROM_LOAD( "bank2.bin",    0x010000, 0x008000, CRC(2a73461c) SHA1(745d48a33766b7f4dab84faa87014fd6f4c8ce34) )
	ROM_LOAD( "bank3.bin",    0x018000, 0x008000, CRC(13ffa799) SHA1(dba02689de02d4c2d6bbf10efe86a37653f3aa86) )
	ROM_LOAD( "bank4.bin",    0x020000, 0x008000, CRC(b5fa9408) SHA1(fd94a0da767b5de2c10da154ae336c14a1d70e4f) )
	ROM_LOAD( "bank5.bin",    0x028000, 0x008000, CRC(4e32081e) SHA1(e23571273b7cad01aa116cb7414c5115a1093f85) )
	ROM_LOAD( "bank6.bin",    0x030000, 0x008000, CRC(1021ccca) SHA1(2154b03c8650e66700a9ea83f1eb2bb32a2bad46) )
	ROM_LOAD( "bank7.bin",    0x038000, 0x008000, CRC(d339d36f) SHA1(fdb5e2d8bfcc723a86d4706c7c00e3adb1bc421b) )
	ROM_LOAD( "bank8.bin",    0x040000, 0x008000, CRC(41263c3a) SHA1(fa415639882b266e663f36b22a8f6336258b7f93) )
	ROM_LOAD( "bank9.bin",    0x048000, 0x008000, CRC(28ccca78) SHA1(44b10f0cfae71b34221306db5d93d5d5aaec3cd3) )
	ROM_LOAD( "banka.bin",    0x050000, 0x008000, CRC(1b43eabd) SHA1(ca711c69165e1fa5be72993b9a7870ef6d485249) )
	ROM_LOAD( "bankb.bin",    0x058000, 0x008000, CRC(2ec7b657) SHA1(34b9ba199b13c71d4facc4215931574d59afd928) )
	ROM_LOAD( "bankc.bin",    0x060000, 0x008000, CRC(b3559c78) SHA1(d8f177b75e96ad22393933534b0432bc0018eae5) )
	ROM_LOAD( "bankd.bin",    0x068000, 0x008000, CRC(9bc44372) SHA1(baa917086edd578d88b730ab1dca1899beb0525d) )
	ROM_LOAD( "banke.bin",    0x070000, 0x008000, CRC(911003f4) SHA1(a35b72130b2c3c3822a1648d1f470a6973262c73) )
	ROM_LOAD( "bankf.bin",    0x078000, 0x008000, CRC(77f6a5c7) SHA1(b8fbf104dd8a8e00855be18f0c3b71da79b6a841) )

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font_ce2.rom", 0x00000, 0x046800, BAD_DUMP CRC(d1c2702a) SHA1(e7781e9d35b6511d12631641d029ad2ba3f7daef) )

	LOAD_KANJI_ROMS
	LOAD_IDE_ROM
ROM_END

/*
98MATE X - 486/Pentium based
*/

ROM_START( pc9821xs )
	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	// "ROM SUM ERROR"
	ROM_LOAD( "itf.rom",         0x10000, 0x008000, BAD_DUMP CRC(dd4c7bb8) SHA1(cf3aa193df2722899066246bccbed03f2e79a74a) )
	ROM_LOAD( "bios_xs.rom",     0x18000, 0x018000, BAD_DUMP CRC(0a682b93) SHA1(76a7360502fa0296ea93b4c537174610a834d367) )

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font_xs.rom",     0x00000, 0x046800, BAD_DUMP CRC(c9a77d8f) SHA1(deb8563712eb2a634a157289838b95098ba0c7f2) )

	LOAD_KANJI_ROMS
	LOAD_IDE_ROM
ROM_END


/*
9821Xa16

Pentium P54C @ 166
32MB
3.5"2DD/2HDx1, 8xCD-ROM
CBus: 3 slots

*/

ROM_START( pc9821xa16 )
	ROM_REGION16_LE( 0x40000, "biosrom", ROMREGION_ERASEFF )
	ROM_LOAD( "pc-9821xa16_g8yewa_a1_wsg8b01_ab28f200b5-t.bin", 0x00000, 0x040000, CRC(f99c8ce2) SHA1(2bc328d2c496046f6f4f39b0637e90b713a63155) ) // SOP44

	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	// TODO: all of the 256k space seems valid
	ROM_COPY( "biosrom", 0x28000, 0x00000, 0x18000 )
	ROM_COPY( "biosrom", 0x20000, 0x28000, 0x08000 )
	ROM_COPY( "biosrom", 0x18000, 0x20000, 0x08000 )
	ROM_COPY( "biosrom", 0x10000, 0x18000, 0x08000 )

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font.rom", 0x00000, 0x46800, BAD_DUMP CRC(a61c0649) SHA1(554b87377d176830d21bd03964dc71f8e98676b1) )

	LOAD_KANJI_ROMS
	LOAD_IDE_ROM
ROM_END

/*
PC-9821Ra20 (98MATE R)

Pentium Pro @ 200
32MB
3.5"2DD/2HDx1, 8xCD-ROM
CBus: 3 slots
*/

ROM_START( pc9821ra20 )
	ROM_REGION16_LE( 0x40000, "biosrom", ROMREGION_ERASEFF )
	ROM_LOAD( "g8wtp_a13_wtp8b01_ab28f200b5-t.bin", 0x00000, 0x040000, CRC(cd3acc5c) SHA1(746490d7f3d8d0e8df865315adaaae65f3fd0425) ) // SOP44

	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	// TODO: all of the 256k space seems valid
	ROM_COPY( "biosrom", 0x28000, 0x00000, 0x18000 )
	ROM_COPY( "biosrom", 0x00000, 0x18000, 0x18000 )

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font.rom", 0x00000, 0x46800, BAD_DUMP CRC(a61c0649) SHA1(554b87377d176830d21bd03964dc71f8e98676b1) )

	LOAD_KANJI_ROMS
	LOAD_IDE_ROM
ROM_END

/*
PC-9821Ra266

Pentium II @ 266 MHz
Trident TGUI9682XGi
32MB, max 256 MB (ECC EDO RAM)
3.5x1, 16xCD-ROM
CBus: 3 slots, PCI: 2 slots
PCI Intel 440FX (Natoma), PCI Rev. 2.1

PC-9821Ra266/M30R has been re-released in 1998,
unknown differences other than having Win98 pre-installed
*/

ROM_START( pc9821ra266 )
	ROM_REGION16_LE( 0x40000, "biosrom", ROMREGION_ERASEFF )
	ROM_LOAD( "g8ykkw.bin", 0x000000, 0x040000, CRC(d73a2795) SHA1(65d4e1e438e91c1646bcc06a9868aa474faf0ccf) ) // SOP44

	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	// TODO: all of the 256k space seems valid
	ROM_COPY( "biosrom", 0x28000, 0x00000, 0x18000 )
	ROM_COPY( "biosrom", 0x00000, 0x18000, 0x18000 )

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font.rom", 0x00000, 0x46800, BAD_DUMP CRC(a61c0649) SHA1(554b87377d176830d21bd03964dc71f8e98676b1) )

	LOAD_KANJI_ROMS
	LOAD_IDE_ROM
ROM_END

/*
PC-9821Ra333 (98MATE R)

Celeron @ 333
32MB, max 256 MB (ECC EDO RAM)
3.5x1, 24xCD-ROM
CBus: 3 slots, PCI: 2 slots
*/

ROM_START( pc9821ra333 )
	ROM_REGION16_LE( 0x40000, "biosrom", ROMREGION_ERASEFF )
	ROM_LOAD( "g8ykkw.bin", 0x00000, 0x040000, CRC(c605ef31) SHA1(3779aed757f21eb75093c1bfcbf18a232c198ee6) )

	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	// TODO: all of the 256k space seems valid
	ROM_COPY( "biosrom", 0x28000, 0x00000, 0x18000 )
	ROM_COPY( "biosrom", 0x00000, 0x18000, 0x18000 )

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font.rom", 0x00000, 0x46800, BAD_DUMP CRC(a61c0649) SHA1(554b87377d176830d21bd03964dc71f8e98676b1) )

	LOAD_KANJI_ROMS
	LOAD_IDE_ROM
ROM_END


/*
98MATE VALUESTAR - Pentium based

Both bad dumps, requires separate PCI-based driver anyway.
*/

//ROM_START( pc9821v13 )
//  "ROM SUM ERROR"
//  ROM_LOAD( "itf.rom",      0x10000, 0x08000, BAD_DUMP CRC(dd4c7bb8) SHA1(cf3aa193df2722899066246bccbed03f2e79a74a) )
//  ROM_LOAD( "bios_v13.rom", 0x18000, 0x18000, BAD_DUMP CRC(0a682b93) SHA1(76a7360502fa0296ea93b4c537174610a834d367) )

//ROM_START( pc9821v20 )
//  ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
//  "ROM SUM ERROR"
//  ROM_LOAD( "itf.rom",      0x10000, 0x08000, BAD_DUMP CRC(dd4c7bb8) SHA1(cf3aa193df2722899066246bccbed03f2e79a74a) )
//  Not an ITF ROM
//  ROM_LOAD( "itf_v20.rom",  0x10000, 0x08000, CRC(10e52302) SHA1(f95b8648e3f5a23e507a9fbda8ab2e317d8e5151) )
//  ROM_LOAD( "bios_v20.rom", 0x18000, 0x18000, BAD_DUMP CRC(d5d1f13b) SHA1(bf44b5f4e138e036f1b848d6616fbd41b5549764) )


/*
PC-9821Nr15

Pentium [Pro?] @ 150 MHz
16MB, max 128 MB
3.5x1, 8xCD-ROM
Trident Cyber9385
TFT 12.1 inches @ 800x600

Nr15/S14F has:
11xCD-ROM
FAX
*/

ROM_START( pc9821nr15 )
	ROM_REGION16_LE( 0x40000, "biosrom", ROMREGION_ERASEFF )
	ROM_LOAD( "g8xwa_xwa00_ab28f200bx-t.bin", 0x000000, 0x040000, CRC(17b91850) SHA1(755eb8767c08980d0f1b6e32638e9fdd616a1b26) ) // SOP44

	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	// TODO: all of the 256k space seems valid
	ROM_COPY( "biosrom", 0x28000, 0x00000, 0x18000 )
	ROM_COPY( "biosrom", 0x00000, 0x18000, 0x18000 )

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font.rom", 0x00000, 0x46800, BAD_DUMP CRC(a61c0649) SHA1(554b87377d176830d21bd03964dc71f8e98676b1) )

	LOAD_KANJI_ROMS
	LOAD_IDE_ROM
ROM_END

/*
PC-9821Nr166

Pentium MMX @ 166 MHz
32MB, max 128 MB
3.5x1, 11xCD-ROM
Trident Cyber9385
TFT 13.3 inches, 1024x768
Optional FAX or LAN (depending on model sub-type)
*/

ROM_START( pc9821nr166 )
	ROM_REGION16_LE( 0x40000, "biosrom", ROMREGION_ERASEFF )
	ROM_LOAD( "pc-9821nr166.bin", 0x000000, 0x040000, CRC(6a137f51) SHA1(1b8264ff525cfda5b367bf85570ff53a6ad42cd4) )

	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	// TODO: all of the 256k space seems valid
	ROM_COPY( "biosrom", 0x28000, 0x00000, 0x18000 )
	ROM_COPY( "biosrom", 0x00000, 0x18000, 0x18000 )

	// "microcode" memory dump, probably identical to above but shuffled
	// left for consultation
	ROM_REGION16_LE( 0x40000, "memory", ROMREGION_ERASEFF )
	ROM_LOAD( "bank0.bin",    0x000000, 0x008000, CRC(c08b4f76) SHA1(bc352826e33566b0e9f5e54c391d7690b6d8fff0) )
	ROM_LOAD( "bank1.bin",    0x008000, 0x008000, CRC(62dfab8b) SHA1(e95cd8e6f385a8074fc311dcfc982b42ce1f1a7c) )
	ROM_LOAD( "bank2.bin",    0x010000, 0x008000, CRC(1df9ebcb) SHA1(91db85ac60ab7c9100aa95c945f50564f4933776) )
	ROM_LOAD( "bank3.bin",    0x018000, 0x008000, CRC(e2b44219) SHA1(f02449f37a2b3d7a22551c3c4fd018d426059829) )
	ROM_LOAD( "bank4.bin",    0x020000, 0x008000, CRC(507d66df) SHA1(f29434ce472ea49b87a17f195fef7d31b7f9ba67) )
	ROM_LOAD( "bank5.bin",    0x028000, 0x008000, CRC(4e32081e) SHA1(e23571273b7cad01aa116cb7414c5115a1093f85) )
	ROM_LOAD( "bank6.bin",    0x030000, 0x008000, CRC(c0f0495b) SHA1(5fd9db08f61faadc8d5b004e41005f113c480ee5) )
	ROM_LOAD( "bank7.bin",    0x038000, 0x008000, CRC(cf92cf6b) SHA1(880350ce71fcf5a039155062d6065566f0c8fa46) )

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font.rom", 0x00000, 0x46800, BAD_DUMP CRC(a61c0649) SHA1(554b87377d176830d21bd03964dc71f8e98676b1) )

	LOAD_KANJI_ROMS
	LOAD_IDE_ROM
ROM_END

ROM_START( pc9821nw150 )
	ROM_REGION16_LE( 0x40000, "biosrom", ROMREGION_ERASEFF )
	ROM_LOAD( "g8yxb_b7a_yxb00_ab28f200bx-t.bin", 0x000000, 0x040000, CRC(75f547f6) SHA1(5ff610f3796a3742b674151e4e5a750ded48c951) ) // SOP44

	ROM_REGION16_LE( 0x30000, "ipl", ROMREGION_ERASEFF )
	// TODO: all of the 256k space seems valid
	ROM_COPY( "biosrom", 0x28000, 0x00000, 0x18000 )
	ROM_COPY( "biosrom", 0x00000, 0x18000, 0x18000 )

	ROM_REGION( 0x80000, "chargen", 0 )
	ROM_LOAD( "font.rom", 0x00000, 0x46800, BAD_DUMP CRC(a61c0649) SHA1(554b87377d176830d21bd03964dc71f8e98676b1) )

	LOAD_KANJI_ROMS
	LOAD_IDE_ROM
ROM_END


// PC9821 [desktop] class
// NB: several of these sub-models don't have the final letter in the original flyers but present in later docs as an afterthought.
// To mark this we intentionally add square brackets here as optional omission notation.

// 98MULTi (i386, desktop)
COMP( 1992, pc9821,      0,          0, pc9821,        pc9821,    pc9821_state,        init_pc9801_kanji,   "NEC",   "PC-9821 (98MULTi)",             MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// 98MATE [A] (i486, desktop, has 98 MATE local bus "ML", with optional RL-like high-reso)
COMP( 1993, pc9821as,    0,          0, pc9821as,      pc9821,    pc9821_mate_a_state, init_pc9801_kanji,   "NEC",   "PC-9821As (98MATE A)",          MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
COMP( 1993, pc9821ap2,   pc9821as,   0, pc9821ap2,     pc9821,    pc9821_mate_a_state, init_pc9801_kanji,   "NEC",   "PC-9821Ap2/U8W (98MATE A)",     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// SC-9821A (rebranded MATE A machines with minor differences such as SW power control)
// ...

// 98MATE [B] (i486, desktop, has GD5428 and no built-in sound)
// ...

// 98MULTi CanBe (i486/Pentium, desktop & tower, Multimedia PC with optional TV Tuner & remote control function, Fax, Modem, MPEG-2, FX-98IF for PC-FX compatibility etc. etc.)
COMP( 1994, pc9821ce2,   0,           0, pc9821ce2,    pc9821,   pc9821_canbe_state, init_pc9801_kanji,   "NEC",   "PC-9821Ce2 (98MULTi CanBe)",    MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
COMP( 1995, pc9821cx3,   pc9821ce2,   0, pc9821cx3,    pc9821,   pc9821_canbe_state, init_pc9801_kanji,   "NEC",   "PC-9821Cx3 (98MULTi CanBe)",    MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// 98MULTi CanBe Jam (Pentium Pro equipped, laptop, Multimedia PC as above + JEIDA 4.2/PCMCIA 2.1)
// ...

// CEREB (Pentium MMX based CanBe follow-up. Multimedia PC with a Sony DDU100E DVD-ROM drive as most notable addition.
//        Form factor is way more akin of a DVD player than a PC)
// ...

// 98MATE X (i486sx/Pentium/Pentium Pro, has PCI, comes with various SVGA cards)
COMP( 1994, pc9821xs,    0,           0, pc9821xs,     pc9821,   pc9821_mate_x_state, init_pc9801_kanji,   "NEC",   "PC-9821Xs (98MATE X)",          MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
COMP( 1996, pc9821xa16,  pc9821xs,    0, pc9821xa16,   pc9821,   pc9821_mate_x_state, init_pc9801_kanji,   "NEC",   "PC-9821Xa16 (98MATE X)",        MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// 98MATE VALUESTAR (Pentium, comes with Windows 95 and several programs pre-installed)
//COMP( 1998, pc9821v13,   0,           0, pc9821v13,    pc9821,   pc9821_valuestar_state, init_pc9801_kanji,   "NEC",   "PC-9821V13 (98MATE VALUESTAR)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
//COMP( 1998, pc9821v20,   pc9821v13,   0, pc9821v20,    pc9821,   pc9821_valuestar_state, init_pc9801_kanji,   "NEC",   "PC-9821V20 (98MATE VALUESTAR)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// 98MATE R (Pentium Pro, otherwise same as 98MATE X?)
COMP( 1996, pc9821ra20,  0,            0, pc9821ra20,  pc9821,   pc9821_mate_r_state, init_pc9801_kanji,   "NEC",   "PC-9821Ra20 (98MATE R)",        MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
COMP( 1997, pc9821ra266, pc9821ra20,   0, pc9821ra266, pc9821,   pc9821_mate_r_state, init_pc9801_kanji,   "NEC",   "PC-9821Ra266 (98MATE R)",       MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
COMP( 1998, pc9821ra333, pc9821ra20,   0, pc9821ra333, pc9821,   pc9821_mate_r_state, init_pc9801_kanji,   "NEC",   "PC-9821Ra333 (98MATE R)",       MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// 98MATE SERVER, pc9821rs* (Server variant of 98MATE R. Inherits concepts from SV-H98 98SERVER)
// ...

// PC-9821 NOTE[book] class
// 98NOTE
//COMP( 1994, pc9821ne,    0,            0, pc9821ne,    pc9821,   pc9821_note_state,       init_pc9801_kanji,   "NEC",   "PC-9821Ne (98NOTE)",              MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// 98NOTE Lavie
COMP( 1996, pc9821nr15,  0,            0, pc9821nr15,  pc9821,   pc9821_note_lavie_state, init_pc9801_kanji,   "NEC",   "PC-9821Nr15 (98NOTE Lavie)",    MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
COMP( 1997, pc9821nr166, pc9821nr15,   0, pc9821nr166, pc9821,   pc9821_note_lavie_state, init_pc9801_kanji,   "NEC",   "PC-9821Nr166 (98NOTE Lavie)",   MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
COMP( 1997, pc9821nw150, pc9821nr15,   0, pc9821nw150, pc9821,   pc9821_note_lavie_state, init_pc9801_kanji,   "NEC",   "PC-9821Nw150 (98NOTE Lavie)",   MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// 98NOTE Light
// ...

// 98NOTE Aile
// ...

// 98FiNE
// ...



pc98ha.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
/**************************************************************************************************

    PC98LT/HA class machine "Handy98" aka 1st Gen LCD PC98

    TODO:
    - pc98lt: remove timer hack:
        - definitely incorrect given the erratic cursor blinking in N88BASIC;
    - identify LCDC used here, reg 2 is clearly H display (0x4f+1)*8=640
    - merge from base pc98 class (WIP);
    - when idle for some time buzzer farts until a key is pressed (?);
    - add NVRAM saving:
    - pinpoint NVRAM init switch source:
        - first port C read (pc98lt: i/o 0x35, PC=0xf841f) tests for bit 7,
          which initializes battery backup if on, but port C is in output mode there.
          Somehow obf irq is on at boot if battery failed?
    - power handling;
    - pc98ha specifics:
        - RTC is upd4991a (partially done), it's parallel instead of serial and incompatible with
          everything else ugh;
        - EMS fails at boot, it's never ever really checked;
        - MSDOS cannot detect EMS properly, is there a flag somewhere?
        - JEIDA memory card interface (68pin cfr. "Super Daisenryaku HA",
          most likely same as NeoGeo JEIDA 3.0 memory cards);
        - optional docking station (for floppy device only or can mount other stuff too?);

**************************************************************************************************/

#include "emu.h"
#include "pc98ha.h"

void pc98lt_state::lt_palette(palette_device &palette) const
{
	// TODO: confirm values
	palette.set_pen_color(0, 160, 168, 160);
	palette.set_pen_color(1, 48, 56, 16);
}

uint32_t pc98lt_state::screen_update( screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect )
{
	for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
		for (int x = cliprect.min_x; x <= cliprect.max_x; x += 16)
		{
			u16 pen = bitswap<16>(m_gvram[(y*640+x)/16], 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7);

			for (int xi = 0; xi < 16; xi++)
			{
				u8 dot = (pen >> xi) & 1;
				bitmap.pix(y, x+xi) = m_palette->pen(dot);
			}
		}

	return 0;
}

/*
 * Power Status Register
 *
 * x--- ---- docking station connected (HA only?)
 * -x-- ---- AC power supply connected
 * ---x ---- alarm enabled
 * ---- x--- unknown
 * ---- -x-- Lithium battery low (HA only?)
 * ---- --x- battery low
 * ---- ---x power off
 */
u8 pc98lt_state::power_status_r()
{
	return 0x80;
}

void pc98lt_state::power_control_w(offs_t offset, u8 data)
{
	// TODO: happens pretty often, supposed to halt the system and wake up on arbitrary event?
	if (BIT(data, 2))
		logerror("%s: power_control_w standby signal ON\n", machine().describe_context());

	// pc98lt: go to prompt (type "command" in main menu) and execute "poweroff.com"
	if (BIT(data, 0))
		logerror("%s: power_control_w power off signal ON\n", machine().describe_context());

	// pc98ha bit 1: flips between 0->1 on system boot failure, in tandem with standby mode held
	if (data & ~0x07)
		logerror("%s: power_control_w unknown signal sent %02x\n", machine().describe_context(), data);
}

// TODO: intentionally repeated from base pc98 until I understand what's going on here.
// (supposedly should be same from base pc98 minus the V50 integrations and whatever the "docking station" really adds up)
u8 pc98lt_state::floppy_mode_r(offs_t offset)
{
	// floppy "mode" identifies drive capabilities, if 2dd/2hd exclusive or mixed type.
	// and to my understanding it doesn't really read from write reg ...
	return (m_floppy_mode & 3) | 0x08;
}

void pc98lt_state::floppy_mode_w(offs_t offset, u8 data)
{
	// bit 1: selects between 2hd and 2dd, not unlike base PC98
	m_floppy_mode = data & 3;
	m_fdc->subdevice<floppy_connector>("0")->get_device()->set_rpm(data & 0x02 ? 360 : 300);
	m_fdc->subdevice<floppy_connector>("1")->get_device()->set_rpm(data & 0x02 ? 360 : 300);

	m_fdc->set_rate(data & 0x02 ? 500000 : 250000);
}

u8 pc98lt_state::fdc_ctrl_r(offs_t offset)
{
	// TODO: doesn't work as intended, bit 4 is supposedly if the drive has a disk in or not according to documentation.
	int ret = (m_fdc->subdevice<floppy_connector>("0")->get_device()->ready_r()) ? 0x10 : 0;
	ret |= (m_fdc->subdevice<floppy_connector>("1")->get_device()->ready_r()) ? 0x10 : 0;
	return ret | 0x64;
}

void pc98lt_state::fdc_ctrl_w(offs_t offset, u8 data)
{
	m_fdc->reset_w(BIT(data, 7));

	m_fdc_ctrl = data;
	if(data & 0x40)
	{
		m_fdc->set_ready_line_connected(0);
		m_fdc->ready_w(0);
	}
	else
		m_fdc->set_ready_line_connected(1);

	m_fdc->subdevice<floppy_connector>("0")->get_device()->mon_w(data & 8 ? ASSERT_LINE : CLEAR_LINE);
	m_fdc->subdevice<floppy_connector>("1")->get_device()->mon_w(data & 8 ? ASSERT_LINE : CLEAR_LINE);
}

void pc98lt_state::lt_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x5ffff).ram(); // 384 KB
	map(0x60000, 0x9ffff).noprw();

	// no TVRAM
	map(0xa8000, 0xaffff).ram().share("gvram");
//  0xb0000-0xbffff unmapped GVRAM or mirror, check me
	map(0xc0000, 0xcffff).unmaprw(); // EMS area, not present here but checked
	map(0xd0000, 0xd3fff).bankrw("bram_bank");
	map(0xd4000, 0xd7fff).bankr("dict_bank");
	map(0xd8000, 0xdbfff).bankr("kanji_bank");
	map(0xe0000, 0xeffff).bankr("romdrv_bank");
	map(0xf0000, 0xfffff).rom().region("ipl", 0);
}

void pc98lt_state::lt_io(address_map &map)
{
	map.unmap_value_high();
//  map(0x0000, 0x001f) // PIC (bit 3 ON slave / master), V50 internal / <undefined>
	map(0x0020, 0x0020).w(FUNC(pc98lt_state::rtc_w));
	map(0x0030, 0x0037).rw(m_ppi_sys, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0xff00);
	map(0x0030, 0x0033).rw(m_sio_rs, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff); //i8251 RS232c / i8255 system port
	map(0x0040, 0x0047).rw(m_ppi_prn, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
	map(0x0040, 0x0043).rw(m_sio_kbd, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0xff00); //i8255 printer port / i8251 keyboard
//  map(0x0070, 0x007f) // PIT, V50 internal

	// floppy actually requires a docking station on PC98HA, density should be 2dd given the mapping
	map(0x00be, 0x00be).rw(FUNC(pc98lt_state::floppy_mode_r), FUNC(pc98lt_state::floppy_mode_w));
	map(0x00c8, 0x00cb).m(m_fdc, FUNC(upd765a_device::map)).umask16(0x00ff);
	map(0x00cc, 0x00cc).rw(FUNC(pc98lt_state::fdc_ctrl_r), FUNC(pc98lt_state::fdc_ctrl_w));

//  map(0x00e0, 0x00ef) // uPD71071, V50 internal

//  map(0x0810, 0x0810) // <unknown device data>, LCDC?
//  map(0x0812, 0x0812) // <unknown device address> & 0xf

	map(0x0c10, 0x0c10).lrw8(
		NAME([this] () { return (m_bram_bank_reg & (m_bram_banks - 1)) | 0x40; }),
		NAME([this] (u8 data) { m_bram_bank_reg = data & (m_bram_banks - 1); m_bram_bank->set_entry(m_bram_bank_reg); })
	);
//  map(0x0f8e, 0x0f8e) // card slot status 1 (undefined on pc98lt?)
//  map(0x4810, 0x4810) // ?
	map(0x4c10, 0x4c10).lrw8(
		NAME([this] () { return (m_dict_bank_reg & 0x3f) | 0x40; }),
		NAME([this] (u8 data) { m_dict_bank_reg = data & 0x3f; m_dict_bank->set_entry(m_dict_bank_reg); })
	);
//  map(0x5e8e, 0x5e8e) // card slot status 2
//  map(0x6e8e, 0x6e8e) // modem control 1
//  map(0x7e8e, 0x7e8e) // modem control 2
	map(0x8810, 0x8810).rw(FUNC(pc98lt_state::power_status_r), FUNC(pc98lt_state::power_control_w));
	map(0x8c10, 0x8c10).lw8(NAME([this] (u8 data) { m_kanji_bank->set_entry(data & 0x0f); }));
//  map(0xc810, 0xc810) // ?
	map(0xcc10, 0xcc10).lrw8(
		NAME([this] () { return (m_romdrv_bank_reg & 0xf) | 0x40; }),
		NAME([this] (u8 data) { m_romdrv_bank_reg = data & 0xf; m_romdrv_bank->set_entry(m_romdrv_bank_reg); })
	);
}

/************************************
 *
 * 98HA specifics
 *
 ***********************************/

void pc98ha_state::ext_view_bank_w(offs_t offset, u8 data)
{
	if (m_ext_view_sel == 0x81)
		m_ramdrv_bank->set_entry(data & 0x7f);
	else
		logerror("External view SEL bank set %02x (view=%02x)\n", data, m_ext_view_sel);
}

void pc98ha_state::ext_view_sel_w(offs_t offset, u8 data)
{
	m_ext_view_sel = data;
	// either bit 7 ON or writing 0x80 to this port disables the external view.
	if (data & 0x80)
		m_ext_view.select(data & 0x3);
	if (data != 0x81)
		logerror("External view SEL line set %02x\n", data);
}

void pc98ha_state::ems_bank_w(offs_t offset, u8 data)
{
	m_ems_banks[offset]->set_entry(data & 0x7f);
}

u8 pc98ha_state::memcard_status_1_r(offs_t offset)
{
	// TODO: identify exact type
	// 0x0e: memory card present
	// bit 3 is checked at boot, PC=f82a5
	// mask 0xf0 is checked at PC=f8956 then periodically polled at PC=0xfd110
	// NeoGeo uses v3
	return 0x04;
}

u8 pc98ha_state::memcard_status_2_r(offs_t offset)
{
	// 0x46: memory card present
	return 0x40;
}

void pc98ha_state::ha_map(address_map &map)
{
	lt_map(map);
	map(0x00000, 0x9ffff).ram(); // 640 KB

	map(0xc0000, 0xc3fff).bankrw("ems_bank1");
	map(0xc4000, 0xc7fff).bankrw("ems_bank2");
	map(0xc8000, 0xcbfff).bankrw("ems_bank3");
	map(0xcc000, 0xcffff).bankrw("ems_bank4");

	map(0xdc000, 0xdffff).view(m_ext_view);
	m_ext_view[0](0xdc000, 0xdffff).unmaprw(); // unknown, accessed on MSDOS boot
	m_ext_view[1](0xdc000, 0xdffff).bankrw("ramdrv_bank");
	m_ext_view[2](0xdc000, 0xdffff).unmaprw(); // JEIDA memory card
	m_ext_view[3](0xdc000, 0xdffff).unmaprw();
}

void pc98ha_state::ha_io(address_map &map)
{
	lt_io(map);
	map(0x0020, 0x002f).unmaprw();
	map(0x0022, 0x0022).w(m_rtc_pio, FUNC(upd4991a_device::address_w));
	map(0x0023, 0x0023).rw(m_rtc_pio, FUNC(upd4991a_device::data_r), FUNC(upd4991a_device::data_w));
	map(0x08e0, 0x08e7).w(FUNC(pc98ha_state::ems_bank_w)).umask16(0xff00);
	map(0x0e8e, 0x0e8e).w(FUNC(pc98ha_state::ext_view_bank_w));
	map(0x0f8e, 0x0f8e).r(FUNC(pc98ha_state::memcard_status_1_r));
	map(0x1e8e, 0x1e8e).w(FUNC(pc98ha_state::ext_view_sel_w));
	map(0x5f8e, 0x5f8e).r(FUNC(pc98ha_state::memcard_status_2_r));
}


static INPUT_PORTS_START( pc98lt )
	PORT_START("SYSB")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER(RTC_TAG, FUNC(upd1990a_device::data_out_r))
	PORT_DIPNAME( 0x02, 0x00, "SYSB" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )

	PORT_START("SYSC")
	PORT_DIPNAME( 0x01, 0x00, "SYSC" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )

	PORT_START("PRNB")
	PORT_DIPNAME( 0x01, 0x00, "PRNB" ) // checked on boot
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // CPUT LT/HA switch
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) ) // checked on boot
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(pc98lt_state::system_type_r))
INPUT_PORTS_END

static INPUT_PORTS_START( pc98ha )
	PORT_INCLUDE( pc98lt )

	PORT_MODIFY("SYSB")
	PORT_DIPNAME( 0x01, 0x00, "<rtc empty signal>" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )

	PORT_MODIFY("PRNB")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

// debug
static const gfx_layout gfx_16x16x1 =
{
	16,16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP16(0,1) },
	{ STEP16(0,16) },
	16*16
};

static GFXDECODE_START( gfx_pc98lt )
	GFXDECODE_ENTRY( "kanji",   0x00000, gfx_8x8x1,   0x000, 0x01 )
	GFXDECODE_ENTRY( "kanji",   0x00000, gfx_16x16x1, 0x000, 0x01 )
GFXDECODE_END

void pc98lt_state::machine_start()
{
	// TODO: make this and NVRAM saving to co-exist
	// we have a 16-bit host bus with a banked NVRAM window that also has different sizes depending on the model,
	// may consider to encapsulate instead.
	const u32 bram_size = memregion("backup")->bytes() / 2;
	uint16_t *bram = (uint16_t *)memregion("backup")->base();
	m_bram_banks = (bram_size * 2) / 0x4000;
	m_bram_ptr = make_unique_clear<uint16_t[]>(bram_size);

	for (int i = 0; i < bram_size; i++)
		m_bram_ptr[i] = bram[i];

	m_kanji_bank->configure_entries(  0, 0x10,                 memregion("kanji")->base(),   0x4000);
	m_bram_bank->configure_entries(   0, m_bram_banks,         m_bram_ptr.get(),             0x4000);
	m_romdrv_bank->configure_entries( 0, 0x10,                 memregion("romdrv")->base(), 0x10000);
	m_dict_bank->configure_entries(   0, 0x40,                 memregion("dict")->base(),    0x4000);

	if (m_rtc != nullptr)
	{
		m_rtc->cs_w(1);
		m_rtc->oe_w(1);
	}
	m_sys_type = 0xc0 >> 6;

	save_item(NAME(m_bram_bank_reg));
	save_item(NAME(m_romdrv_bank_reg));
	save_item(NAME(m_dict_bank_reg));
	save_pointer(NAME(m_bram_ptr), bram_size);
}

void pc98ha_state::machine_start()
{
	pc98lt_state::machine_start();
	const u32 ems_banks = 0x80;
	const u32 ems_size = (ems_banks * 0x4000) / 2;

	m_ramdrv_bank->configure_entries(0, 0x80,                 memregion("ramdrv")->base(), 0x4000);

	m_ems_ram = make_unique_clear<uint16_t[]>(ems_size);
	for (int i = 0; i < 4; i++)
		m_ems_banks[i]->configure_entries(0, ems_banks, m_ems_ram.get(), 0x4000);

	save_item(NAME(m_ext_view_sel));
	save_pointer(NAME(m_ems_ram), ems_size);
}

static void pc9801_floppies(device_slot_interface &device)
{
//  device.option_add("525dd", FLOPPY_525_DD);
	device.option_add("525hd", FLOPPY_525_HD);
//  device.option_add("35hd", FLOPPY_35_HD);
}

void pc98lt_state::uart_irq_check()
{
	m_maincpu->set_input_line(4, m_uart_irq_pending & m_uart_irq_mask ? ASSERT_LINE : CLEAR_LINE);
}



void pc98lt_state::lt_config(machine_config &config)
{
	const XTAL xtal = XTAL(8'000'000);
	V50(config, m_maincpu, xtal); // µPD70216
	m_maincpu->set_addrmap(AS_PROGRAM, &pc98lt_state::lt_map);
	m_maincpu->set_addrmap(AS_IO, &pc98lt_state::lt_io);
	// TODO: jumps off the weeds if divided by / 4 after timer check, DMA issue?
//  m_maincpu->set_tclk(xtal / 4);
	m_maincpu->set_tclk(xtal / 100);
//  m_pit->out_handler<0>().set(m_pic1, FUNC(pic8259_device::ir0_w));
	m_maincpu->tout2_cb().set(m_sio_rs, FUNC(i8251_device::write_txc));
	m_maincpu->tout2_cb().append(m_sio_rs, FUNC(i8251_device::write_rxc));
//  m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	pc9801_serial(config);

	I8251(config, m_sio_kbd, 0);
	m_sio_kbd->txd_handler().set("keyb", FUNC(pc98_kbd_device::input_txd));
	m_sio_kbd->rxrdy_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ1);
	m_sio_kbd->write_cts(0);
	m_sio_kbd->write_dsr(0);

	clock_device &kbd_clock(CLOCK(config, "kbd_clock", 19'200));
	kbd_clock.signal_handler().set(m_sio_kbd, FUNC(i8251_device::write_rxc));
	kbd_clock.signal_handler().append(m_sio_kbd, FUNC(i8251_device::write_txc));

	PC98_KBD(config, m_keyb, 0);
	m_keyb->rxd_callback().set("sio_kbd", FUNC(i8251_device::write_rxd));

	I8255(config, m_ppi_sys, 0);
	// PC98LT/HA has no dips, port A acts as a RAM storage
	m_ppi_sys->in_pa_callback().set(m_ppi_sys, FUNC(i8255_device::pa_r));
	m_ppi_sys->in_pb_callback().set_ioport("SYSB");
//  m_ppi_sys->in_pc_callback().set_constant(0xa0); // 0x80 cpu triple fault reset flag?
	m_ppi_sys->out_pc_callback().set(FUNC(pc98lt_state::ppi_sys_beep_portc_w));

	I8255(config, m_ppi_prn, 0);
	m_ppi_prn->in_pb_callback().set_ioport("PRNB");

	UPD1990A(config, m_rtc);

	UPD765A(config, m_fdc, 8'000'000, false, true);
	m_fdc->intrq_wr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ6);
	m_fdc->drq_wr_callback().set(m_maincpu, FUNC(v50_device::dreq_w<2>)).invert();
//  m_fdc->drq_wr_callback().set(m_maincpu, FUNC(v50_device::dreq_w<3>)).invert(); // 2dd
	FLOPPY_CONNECTOR(config, "upd765:0", pc9801_floppies, "525hd", pc9801_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "upd765:1", pc9801_floppies, "525hd", pc9801_state::floppy_formats);

	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	// TODO: copied verbatim from base PC98, verify clock et al.
	m_screen->set_raw(21.0526_MHz_XTAL, 848, 0, 640, 440, 0, 400);
	m_screen->set_screen_update(FUNC(pc98lt_state::screen_update));
//  m_screen->screen_vblank().set(FUNC(pc9801_state::vrtc_irq));

	PALETTE(config, m_palette, FUNC(pc98lt_state::lt_palette), 2);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_pc98lt);

	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 2400).add_route(ALL_OUTPUTS, "mono", 0.05);
}

void pc98ha_state::ha_config(machine_config &config)
{
	lt_config(config);
	const XTAL xtal = XTAL(10'000'000);
	V50(config.replace(), m_maincpu, xtal);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc98ha_state::ha_map);
	m_maincpu->set_addrmap(AS_IO, &pc98ha_state::ha_io);
	m_maincpu->set_tclk(xtal / 4);
//  m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	config.device_remove("rtc");
	UPD4991A(config, m_rtc_pio, 32'768);
}

// all ROMs in both sets needs at least chip renaming, and I haven't seen a single PCB pic from the net.
// dict.rom and ramdrv.bin definitely won't fit an even ROM size regardless,
// also backup.bin may not be factory default.

ROM_START( pc98lt )
	ROM_REGION16_LE( 0x10000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "ipl.rom",      0x000000, 0x010000, BAD_DUMP CRC(b6a6a382) SHA1(3f1767cccc1ae02b3e48f6ee327d3ef4fad05750) )

	ROM_REGION( 0x40000, "kanji", ROMREGION_ERASEFF )
	ROM_LOAD( "kanji.rom",    0x000000, 0x040000, BAD_DUMP CRC(26a81aa2) SHA1(bf12e40c608ef6ef1ac38f6b0b3ca79260a50cef) )

	ROM_REGION16_LE( 0x10000, "backup", ROMREGION_ERASEFF )
	ROM_LOAD( "backup.bin",   0x000000, 0x010000, BAD_DUMP CRC(56d7ca00) SHA1(d17942e166f98af1d484e497e97d31da515973f7) )

	ROM_REGION( 0x100000, "dict", ROMREGION_ERASEFF )
	ROM_LOAD( "dict.rom",     0x000000, 0x080000, BAD_DUMP CRC(421278ee) SHA1(f6066fc5085de521395ce1a8bb040536c1454c7e) )

	ROM_REGION( 0x100000, "romdrv", ROMREGION_ERASEFF )
	ROM_LOAD( "romdrv.rom",   0x000000, 0x080000, BAD_DUMP CRC(282ff6eb) SHA1(f4833e49dd9089ec40f5e86a713e08cd8c598578) )
ROM_END

ROM_START( pc98ha )
	ROM_REGION16_LE( 0x10000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "ipl.rom",      0x000000, 0x010000, BAD_DUMP CRC(2f552bb9) SHA1(7f53bf95181d65b2f9942285da669d92c61247a3) )

	ROM_REGION( 0x40000, "kanji", ROMREGION_ERASEFF )
	ROM_LOAD( "kanji.rom",    0x000000, 0x040000, BAD_DUMP CRC(4be5ff2f) SHA1(261d28419a2ddebe3177a282952806d7bb036b40) )

	ROM_REGION16_LE( 0x40000, "backup", ROMREGION_ERASEFF )
	ROM_LOAD( "backup.bin",   0x000000, 0x040000, BAD_DUMP CRC(3c5b2a99) SHA1(f8e2f5a4c7601d4e81d5e9c83621107ed3f5a29a) )

	ROM_REGION( 0x100000, "dict", ROMREGION_ERASEFF )
	ROM_LOAD( "dict.rom",     0x000000, 0x0c0000, BAD_DUMP CRC(6dc8493c) SHA1(3e04cdc3403a814969b6590cd78e239e72677fe5) )

	ROM_REGION( 0x100000, "romdrv", ROMREGION_ERASEFF )
	ROM_LOAD( "romdrv.rom",   0x000000, 0x100000, BAD_DUMP CRC(2f59127f) SHA1(932cb970c2b22408f7895dbf9df6dbc47f8e055b) )

	// $00 filled with odd size
	ROM_REGION( 0x200000, "ramdrv", ROMREGION_ERASEFF )
	ROM_LOAD( "ramdrv.bin",   0x000000, 0x160000, BAD_DUMP CRC(f2cec994) SHA1(c986ad6d8f810ac0a9657c1af26b6fec712d56ed) )
ROM_END


COMP( 1989, pc98lt,      0,        0, lt_config,         pc98lt,   pc98lt_state,        empty_init,   "NEC",   "PC-98LT", MACHINE_NOT_WORKING )
COMP( 1990, pc98ha,      0,        0, ha_config,         pc98ha,   pc98ha_state,        empty_init,   "NEC",   "PC-98HA (Handy98)", MACHINE_NOT_WORKING )



pcd.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Dirk Best, Carl
/***************************************************************************

    Siemens PC-D

    For PC-X HDD should have 306,4,9 chs at 1024Bps or 18 at 512Bps

***************************************************************************/

#include "emu.h"
#include "pcd_kbd.h"
#include "pcd.h"

#include "bus/rs232/rs232.h"
#include "bus/scsi/omti5100.h"
#include "cpu/i86/i186.h"
#include "imagedev/floppy.h"
#include "machine/mc146818.h"
#include "machine/nvram.h"
#include "machine/pic8259.h"
#include "machine/scn_pci.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "machine/wd_fdc.h"
#include "sound/spkrdev.h"

#include "speaker.h"


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class pcd_state : public driver_device
{
public:
	pcd_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pic1(*this, "pic1")
		, m_pic2(*this, "pic2")
		, m_speaker(*this, "speaker")
		, m_fdc(*this, "fdc")
		, m_rtc(*this, "rtc")
		, m_usart(*this, "usart%u", 1U)
		, m_scsi(*this, "scsi")
		, m_scsi_data_out(*this, "scsi_data_out")
		, m_scsi_data_in(*this, "scsi_data_in")
		, m_ram(*this, "ram")
	{ }

	void pcx(machine_config &config);
	void pcd(machine_config &config);

private:
	uint8_t irq_callback(offs_t offset);
	TIMER_DEVICE_CALLBACK_MEMBER( timer0_tick );
	void i186_timer1_w(int state);

	uint8_t nmi_io_r(address_space &space, offs_t offset);
	void nmi_io_w(address_space &space, offs_t offset, uint8_t data);
	uint8_t stat_r();
	void stat_w(uint8_t data);
	uint8_t led_r();
	void led_w(uint8_t data);
	uint16_t dskctl_r();
	void dskctl_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

	uint8_t scsi_r(offs_t offset);
	void scsi_w(offs_t offset, uint8_t data);
	uint16_t mmu_r(offs_t offset);
	void mmu_w(offs_t offset, uint16_t data);
	uint16_t mem_r(address_space &space, offs_t offset);
	void mem_w(address_space &space, offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

	void write_scsi_bsy(int state);
	void write_scsi_cd(int state);
	void write_scsi_io(int state);
	void write_scsi_msg(int state);
	void write_scsi_req(int state);

	void pcd_io(address_map &map) ATTR_COLD;
	void pcd_map(address_map &map) ATTR_COLD;
	void pcx_io(address_map &map) ATTR_COLD;

	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<i80186_cpu_device> m_maincpu;
	required_device<pic8259_device> m_pic1;
	required_device<pic8259_device> m_pic2;
	required_device<speaker_sound_device> m_speaker;
	required_device<wd2793_device> m_fdc;
	required_device<mc146818_device> m_rtc;
	required_device_array<scn2661b_device, 3> m_usart;
	required_device<scsi_port_device> m_scsi;
	required_device<output_latch_device> m_scsi_data_out;
	required_device<input_buffer_device> m_scsi_data_in;
	required_device<ram_device> m_ram;
	uint8_t m_stat = 0, m_led = 0;
	int m_msg = 0, m_bsy = 0, m_io = 0, m_cd = 0, m_req = 0, m_rst = 0;
	uint16_t m_dskctl = 0;
	struct {
		uint16_t ctl = 0;
		uint16_t regs[1024]{};
		int type = 0;
		bool sc = 0;
	} m_mmu;

	void check_scsi_irq();
};


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void pcd_state::machine_start()
{
	save_item(NAME(m_mmu.ctl));
	save_item(NAME(m_mmu.regs));
}

void pcd_state::machine_reset()
{
	m_stat = 0;
	m_led = 0;
	m_dskctl = 0;
	m_rst = 0;
	m_mmu.ctl = 0;
	m_mmu.sc = false;
	if(ioport("mmu"))
		m_mmu.type = ioport("mmu")->read();
	else
		m_mmu.type = 0;
}

uint8_t pcd_state::irq_callback(offs_t offset)
{
	return (offset ? m_pic2 : m_pic1)->acknowledge();
}

TIMER_DEVICE_CALLBACK_MEMBER( pcd_state::timer0_tick )
{
	m_maincpu->tmrin0_w(0);
	m_maincpu->tmrin0_w(1);
}

void pcd_state::i186_timer1_w(int state)
{
	if(m_dskctl & 0x20)
		m_speaker->level_w(state);
}

uint8_t pcd_state::nmi_io_r(address_space &space, offs_t offset)
{
	if(machine().side_effects_disabled())
		return 0;
	logerror("%s: unmapped %s %04x\n", machine().describe_context(), space.name(), offset);
	m_stat |= 0xfd;
	m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
	return 0;
}

void pcd_state::nmi_io_w(address_space &space, offs_t offset, uint8_t data)
{
	if(machine().side_effects_disabled())
		return;
	logerror("%s: unmapped %s %04x\n", machine().describe_context(), space.name(), offset);
	m_stat |= 0xfd;
	m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}

uint8_t pcd_state::stat_r()
{
	return m_stat;
}

void pcd_state::stat_w(uint8_t data)
{
	m_stat &= ~data;
}

uint16_t pcd_state::dskctl_r()
{
	return m_dskctl;
}

void pcd_state::dskctl_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	floppy_image_device *floppy0 = m_fdc->subdevice<floppy_connector>("0")->get_device();
	floppy_image_device *floppy1 = m_fdc->subdevice<floppy_connector>("1")->get_device();

	COMBINE_DATA(&m_dskctl);

	if((m_dskctl & 1) && floppy0)
		m_fdc->set_floppy(floppy0);
	if((m_dskctl & 2) && floppy1)
		m_fdc->set_floppy(floppy1);

	if(floppy0)
	{
		floppy0->mon_w(!(m_dskctl & 4));
		floppy0->ss_w((m_dskctl & 8) != 0);
	}
	if(floppy1)
	{
		floppy1->mon_w(!(m_dskctl & 4));
		floppy1->ss_w((m_dskctl & 8) != 0);
	}
	m_fdc->dden_w((m_dskctl & 0x10) ? 1 : 0);
}

uint8_t pcd_state::led_r()
{
	// DIPs?
	// 0x01 no mmu
	// 0x10 enter monitor after post
	// 0x20 enter monitor before post
	return 0x01;
}

void pcd_state::led_w(uint8_t data)
{
	for(int i = 0; i < 6; i++)
		logerror("%c", (data & (1 << i)) ? '-' : '*');
	logerror("\n");
	m_led = data;
}

uint16_t pcd_state::mmu_r(offs_t offset)
{
	uint16_t data = m_mmu.regs[((m_mmu.ctl & 0x1f) << 5) | ((offset >> 2) & 0x1f)];
	//logerror("%s: mmu read %04x %04x\n", machine().describe_context(), (offset << 1) + 0x8000, data);
	if(!offset)
		return m_mmu.ctl;
	else if((offset >= 0x200) && (offset < 0x400) && !(offset & 3))
		return (data << 4) | (data >> 12) | (m_mmu.sc && (offset == 0x200) ? 0xc0 : 0);
	else if(offset == 0x400)
	{
		m_mmu.sc = false;
		m_pic1->ir0_w(CLEAR_LINE);
	}
	return 0;
}

void pcd_state::mmu_w(offs_t offset, uint16_t data)
{
	//logerror("%s: mmu write %04x %04x\n", machine().describe_context(), (offset << 1) + 0x8000, data);
	if(!offset)
		m_mmu.ctl = data;
	else if((offset >= 0x200) && (offset < 0x400) && !(offset & 3))
		m_mmu.regs[((m_mmu.ctl & 0x1f) << 5) | ((offset >> 2) & 0x1f)] = (data >> 4) | (data << 12);
	else if(offset == 0x400)
	{
		m_mmu.sc = true;
		m_pic1->ir0_w(ASSERT_LINE);
	}
}

uint8_t pcd_state::scsi_r(offs_t offset)
{
	uint8_t ret = 0;

	switch(offset)
	{
		case 0:
		case 2:
			ret = m_scsi_data_in->read();
			m_scsi->write_ack(1);
			if(!offset)
				m_maincpu->drq0_w(0);
			break;

		case 1:
			ret = (m_cd << 7) | (m_req << 5) | (m_bsy << 4);
			break;
	}

	return ret;
}

void pcd_state::scsi_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
		case 0:
			m_scsi_data_out->write(data);
			m_scsi->write_ack(1);
			m_maincpu->drq0_w(0);
			break;
		case 1:
			if(data & 4)
			{
				m_rst = 1;
				m_scsi->write_rst(1);
				break;
			}
			if(m_rst)
			{
				m_rst = 0;
				m_scsi->write_rst(0);
				break;
			}

			if(!m_bsy)
			{
				m_scsi_data_out->write(0);
				m_scsi->write_sel(1);
			}
			break;
	}
}

void pcd_state::check_scsi_irq()
{
	m_pic1->ir5_w(m_io && m_cd && m_req);
}

void pcd_state::write_scsi_bsy(int state)
{
	m_bsy = state ? 1 : 0;
	m_scsi->write_sel(0);
}
void pcd_state::write_scsi_cd(int state)
{
	m_cd = state ? 1 : 0;
	check_scsi_irq();
}
void pcd_state::write_scsi_io(int state)
{
	m_io = state ? 1 : 0;
	if(state)
		m_scsi_data_out->write(0);
	check_scsi_irq();
}
void pcd_state::write_scsi_msg(int state)
{
	m_msg = state ? 1 : 0;
}

void pcd_state::write_scsi_req(int state)
{
	m_req = state ? 1 : 0;
	if(state)
	{
		if(!m_cd)
		{
			m_maincpu->drq0_w(1);
		}
		else if(m_msg)
		{
			m_scsi_data_in->read();
			m_scsi->write_ack(1);
		}
	}
	else
	{
		m_scsi->write_ack(0);
	}
	check_scsi_irq();
}

void pcd_state::mem_w(address_space &space, offs_t offset, uint16_t data, uint16_t mem_mask)
{
	uint16_t *ram = (uint16_t *)m_ram->pointer();
	if((m_mmu.ctl & 0x20) && m_mmu.type)
	{
		uint16_t reg;
		if(m_mmu.type == 2)
			reg = m_mmu.regs[((offset >> 10) & 0xff) | ((m_mmu.ctl & 0x18) << 5)];
		else
			reg = m_mmu.regs[((offset >> 10) & 0x7f) | ((m_mmu.ctl & 0x1c) << 5)];
		if(!(reg & 1) && !machine().side_effects_disabled())
		{
			offset <<= 1;
			logerror("%s: Null mmu entry %06x\n", machine().describe_context(), offset);
			nmi_io_w(space, offset, data);
			return;
		}
		offset = ((reg << 3) & 0x7fc00) | (offset & 0x3ff);
	}
	COMBINE_DATA(&ram[offset]);
}

uint16_t pcd_state::mem_r(address_space &space, offs_t offset)
{
	uint16_t *ram = (uint16_t *)m_ram->pointer();
	if((m_mmu.ctl & 0x20) && m_mmu.type)
	{
		uint16_t reg;
		if(m_mmu.type == 2)
			reg = m_mmu.regs[((offset >> 10) & 0xff) | ((m_mmu.ctl & 0x18) << 5)];
		else
			reg = m_mmu.regs[((offset >> 10) & 0x7f) | ((m_mmu.ctl & 0x1c) << 5)];
		if(!(reg & 2) && !machine().side_effects_disabled())
		{
			offset <<= 1;
			logerror("%s: Null mmu entry %06x\n", machine().describe_context(), offset);
			return nmi_io_r(space, offset);
		}
		offset = ((reg << 3) & 0x7fc00) | (offset & 0x3ff);
	}
	return ram[offset];
}

//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void pcd_state::pcd_map(address_map &map)
{
	map(0x00000, 0xfffff).rw(FUNC(pcd_state::nmi_io_r), FUNC(pcd_state::nmi_io_w));
	map(0x00000, 0x7ffff).rw(FUNC(pcd_state::mem_r), FUNC(pcd_state::mem_w));
	map(0xfc000, 0xfffff).rom().region("bios", 0);
}

void pcd_state::pcd_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xefff).rw(FUNC(pcd_state::nmi_io_r), FUNC(pcd_state::nmi_io_w));
	map(0xf000, 0xf7ff).ram().share("nvram");
	map(0xf800, 0xf801).rw(m_pic1, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0xf820, 0xf821).rw(m_pic2, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0xf840, 0xf840).rw(FUNC(pcd_state::stat_r), FUNC(pcd_state::stat_w));
	map(0xf841, 0xf841).rw(FUNC(pcd_state::led_r), FUNC(pcd_state::led_w));
	map(0xf880, 0xf8bf).rw(m_rtc, FUNC(mc146818_device::read_direct), FUNC(mc146818_device::write_direct));
	map(0xf900, 0xf903).rw(m_fdc, FUNC(wd2793_device::read), FUNC(wd2793_device::write));
	map(0xf904, 0xf905).rw(FUNC(pcd_state::dskctl_r), FUNC(pcd_state::dskctl_w));
	map(0xf940, 0xf943).rw(FUNC(pcd_state::scsi_r), FUNC(pcd_state::scsi_w));
	map(0xf980, 0xf9bf).m("video", FUNC(pcdx_video_device::map));
	map(0xf9c0, 0xf9c3).rw(m_usart[0], FUNC(scn2661b_device::read), FUNC(scn2661b_device::write));  // UARTs
	map(0xf9d0, 0xf9d3).rw(m_usart[1], FUNC(scn2661b_device::read), FUNC(scn2661b_device::write));
	map(0xf9e0, 0xf9e3).rw(m_usart[2], FUNC(scn2661b_device::read), FUNC(scn2661b_device::write));
//  map(0xfa00, 0xfa7f) // pcs4-n (peripheral chip select)
	map(0xfb00, 0xfb00).rw(FUNC(pcd_state::nmi_io_r), FUNC(pcd_state::nmi_io_w));
	map(0xfb02, 0xffff).rw(FUNC(pcd_state::nmi_io_r), FUNC(pcd_state::nmi_io_w));
}

void pcd_state::pcx_io(address_map &map)
{
	map.unmap_value_high();
	pcd_io(map);
	map(0x8000, 0x8fff).rw(FUNC(pcd_state::mmu_r), FUNC(pcd_state::mmu_w));
	map(0xfb01, 0xfb01).rw(FUNC(pcd_state::nmi_io_r), FUNC(pcd_state::nmi_io_w));
}

//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

static void pcd_floppies(device_slot_interface &device)
{
	device.option_add("55f", TEAC_FD_55F); // 80 tracks
	device.option_add("55g", TEAC_FD_55G); // 77 tracks
}

static INPUT_PORTS_START(pcx)
	PORT_START("mmu")
	PORT_CONFNAME(0x03, 0x00, "MMU Type")
	PORT_CONFSETTING(0x00, "None")
	PORT_CONFSETTING(0x01, "SINIX 1.0")
	PORT_CONFSETTING(0x02, "SINIX 1.2")
INPUT_PORTS_END

void pcd_state::pcd(machine_config &config)
{
	I80186(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &pcd_state::pcd_map);
	m_maincpu->set_addrmap(AS_IO, &pcd_state::pcd_io);
	m_maincpu->tmrout1_handler().set(FUNC(pcd_state::i186_timer1_w));
	m_maincpu->read_slave_ack_callback().set(FUNC(pcd_state::irq_callback));

	TIMER(config, "timer0_tick").configure_periodic(FUNC(pcd_state::timer0_tick), attotime::from_hz(16_MHz_XTAL / 24)); // adjusted to pass post

	PIC8259(config, m_pic1, 0);
	m_pic1->out_int_callback().set(m_maincpu, FUNC(i80186_cpu_device::int0_w));

	PIC8259(config, m_pic2, 0);
	m_pic2->out_int_callback().set(m_maincpu, FUNC(i80186_cpu_device::int1_w));

	PCD_VIDEO(config, "video", 0);

	RAM(config, RAM_TAG).set_default_size("1M");

	// nvram
	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	// floppy disk controller
	WD2793(config, m_fdc, 16_MHz_XTAL / 8);
	m_fdc->intrq_wr_callback().set(m_pic1, FUNC(pic8259_device::ir6_w));
	m_fdc->drq_wr_callback().set(m_maincpu, FUNC(i80186_cpu_device::drq1_w));
	m_fdc->enmf_rd_callback().set_constant(0);

	// floppy drives
	FLOPPY_CONNECTOR(config, "fdc:0", pcd_floppies, "55f", floppy_image_device::default_pc_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", pcd_floppies, "55f", floppy_image_device::default_pc_floppy_formats);

	// usart
	SCN2661B(config, m_usart[0], 4.9152_MHz_XTAL);
	m_usart[0]->rxrdy_handler().set(m_pic1, FUNC(pic8259_device::ir3_w));
	m_usart[0]->txrdy_handler().set(m_pic1, FUNC(pic8259_device::ir3_w));
	m_usart[0]->txd_handler().set("rs232_1", FUNC(rs232_port_device::write_txd));

	SCN2661B(config, m_usart[1], 4.9152_MHz_XTAL);
	m_usart[1]->rxrdy_handler().set(m_pic1, FUNC(pic8259_device::ir2_w));
	//m_usart[1]->.txrdy_handler().set(m_pic1, FUNC(pic8259_device::ir2_w)); // this gets stuck high causing the keyboard to not work
	m_usart[1]->txd_handler().set("keyboard", FUNC(pcd_keyboard_device::t0_w));

	SCN2661B(config, m_usart[2], 4.9152_MHz_XTAL);
	m_usart[2]->rxrdy_handler().set(m_pic1, FUNC(pic8259_device::ir4_w));
	m_usart[2]->txrdy_handler().set(m_pic1, FUNC(pic8259_device::ir4_w));
	m_usart[2]->txd_handler().set("rs232_2", FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232_1(RS232_PORT(config, "rs232_1", default_rs232_devices, nullptr));
	rs232_1.rxd_handler().set(m_usart[0], FUNC(scn2661b_device::rxd_w));
	rs232_port_device &rs232_2(RS232_PORT(config, "rs232_2", default_rs232_devices, nullptr));
	rs232_2.rxd_handler().set(m_usart[2], FUNC(scn2661b_device::rxd_w));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	// rtc
	MC146818(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->irq().set(m_pic1, FUNC(pic8259_device::ir7_w));
	m_rtc->set_binary(true);
	m_rtc->set_epoch(1900);
	m_rtc->set_24hrs(true);

	pcd_keyboard_device &keyboard(PCD_KEYBOARD(config, "keyboard", 0));
	keyboard.out_tx_handler().set(m_usart[1], FUNC(scn2661b_device::rxd_w));

	SCSI_PORT(config, m_scsi, 0);
	m_scsi->set_data_input_buffer("scsi_data_in");
	m_scsi->msg_handler().set(FUNC(pcd_state::write_scsi_msg));
	m_scsi->bsy_handler().set(FUNC(pcd_state::write_scsi_bsy));
	m_scsi->io_handler().set(FUNC(pcd_state::write_scsi_io));
	m_scsi->cd_handler().set(FUNC(pcd_state::write_scsi_cd));
	m_scsi->req_handler().set(FUNC(pcd_state::write_scsi_req));

	output_latch_device &scsi_out(OUTPUT_LATCH(config, "scsi_data_out", 0));
	m_scsi->set_output_latch(scsi_out);

	INPUT_BUFFER(config, "scsi_data_in", 0);
	m_scsi->set_slot_device(1, "harddisk", OMTI5100, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_0));

	SOFTWARE_LIST(config, "flop_list").set_original("pcd_flop");
}

void pcd_state::pcx(machine_config &config)
{
	pcd(config);
	m_maincpu->set_addrmap(AS_IO, &pcd_state::pcx_io);

	pcx_video_device &video(PCX_VIDEO(config.replace(), "video", 0));
	video.txd_handler().set("keyboard", FUNC(pcd_keyboard_device::t0_w));

	subdevice<pcd_keyboard_device>("keyboard")->out_tx_handler().set("video", FUNC(pcx_video_device::rx_w));

	m_usart[1]->txd_handler().set_nop();

	config.device_remove("flop_list");
	SOFTWARE_LIST(config, "flop_ls").set_original("pcx_flop");
}

//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( pcd )
	ROM_REGION16_LE(0x4000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "v2", "V2 GS")  // from mainboard SYBAC S26361-D359 V2 GS
	ROMX_LOAD("s26361-d359.d42", 0x0001, 0x2000, CRC(e20244dd) SHA1(0ebc5ddb93baacd9106f1917380de58aac64fe73), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("s26361-d359.d43", 0x0000, 0x2000, CRC(e03db2ec) SHA1(fcae8b0c9e7543706817b0a53872826633361fda), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v3", "V3 GS4") // from mainboard SYBAC S26361-D359 V3 GS4
	ROMX_LOAD("361d0359.d42", 0x0001, 0x2000, CRC(5b4461e4) SHA1(db6756aeabb2e6d3921dc7571a5bed3497b964bf), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("361d0359.d43", 0x0000, 0x2000, CRC(71c3189d) SHA1(e8dd6c632bfc833074d3a833ea7f59bb5460f313), ROM_SKIP(1) | ROM_BIOS(1))
ROM_END

ROM_START( pcx )
	ROM_REGION16_LE(0x4000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "v2", "V2 GS")  // from mainboard SYBAC S26361-D359 V2 GS
	ROMX_LOAD("s26361-d359.d42", 0x0001, 0x2000, CRC(e20244dd) SHA1(0ebc5ddb93baacd9106f1917380de58aac64fe73), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("s26361-d359.d43", 0x0000, 0x2000, CRC(e03db2ec) SHA1(fcae8b0c9e7543706817b0a53872826633361fda), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v3", "V3 GS4") // from mainboard SYBAC S26361-D359 V3 GS4
	ROMX_LOAD("361d0359.d42", 0x0001, 0x2000, CRC(5b4461e4) SHA1(db6756aeabb2e6d3921dc7571a5bed3497b964bf), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("361d0359.d43", 0x0000, 0x2000, CRC(71c3189d) SHA1(e8dd6c632bfc833074d3a833ea7f59bb5460f313), ROM_SKIP(1) | ROM_BIOS(1))
ROM_END

//**************************************************************************
//  GAME DRIVERS
//**************************************************************************

COMP( 1984, pcd, 0,   0, pcd, 0,   pcd_state, empty_init, "Siemens", "PC-D", MACHINE_NOT_WORKING )
COMP( 1984, pcx, pcd, 0, pcx, pcx, pcd_state, empty_init, "Siemens", "PC-X", MACHINE_NOT_WORKING )



pce.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Charles MacDonald, Wilbert Pol, Angelo Salese
/****************************************************************************

 PC-Engine / Turbo Grafx 16 driver
 by Charles Mac Donald
 E-Mail: cgfm2@hooked.net

 Thanks to David Shadoff and Brian McPhail for help with the driver.

****************************************************************************/

/**********************************************************************
          To-Do List:
- convert h6280-based drivers to internal memory map for the I/O region
- test sprite collision and overflow interrupts
- sprite precaching
- rewrite the base renderer loop
- Add expansion port support
- Add 263 line mode
- Sprite DMA should use vdc VRAM functions
- properly implement the pixel clocks instead of the simple scaling we do now

Banking
=======

Normally address spacebanks 00-F6 are assigned to regular HuCard ROM space. There
are a couple of special situations:

Street Fighter II:
  - address space banks 40-7F switchable by writing to 1FF0-1FF3
    1FF0 - select rom banks 40-7F
    1FF1 - select rom banks 80-BF
    1FF2 - select rom banks C0-FF
    1FF3 - select rom banks 100-13F

Populous:
  - address space banks 40-43 contains 32KB RAM

CDRom units:
  - address space banks 80-87 contains 64KB RAM

Super System Card:
  - address space banks 68-7F contains 192KB RAM

**********************************************************************/

/**********************************************************************
                          Known Bugs
***********************************************************************
- Cadash: After choosing character and name, the game starts and the display 'jiggles' like tracking if off a VCR
- Fighting Run: has color and sprite issues during gameplay;
**********************************************************************/

#include "emu.h"
#include "pce.h"

#include "bus/pce/pce_acard.h"
#include "bus/pce/pce_rom.h"
#include "bus/pce/pce_scdsys.h"
#include "cpu/h6280/h6280.h"
#include "sound/cdda.h"
#include "sound/msm5205.h"
#include "video/huc6202.h"
#include "video/huc6270.h"

#include "screen.h"
#include "softlist.h"
#include "speaker.h"


static constexpr XTAL MAIN_CLOCK = XTAL(21'477'272);

static constexpr uint8_t TG_16_JOY_SIG = 0x00;
static constexpr uint8_t PCE_JOY_SIG   = 0x40;
//static constexpr uint8_t NO_CD_SIG     = 0x80;
//static constexpr uint8_t CD_SIG        = 0x00;
/* these might be used to indicate something, but they always seem to return 1 */
static constexpr uint8_t CONST_SIG     = 0x30;

// hucard pachikun gives you option to select pachinko controller after pressing start, likely because it doesn't have a true header id
static INPUT_PORTS_START( pce )
INPUT_PORTS_END

void pce_state::controller_w(u8 data)
{
	m_port_ctrl->sel_w(BIT(data, 0));
	m_port_ctrl->clr_w(BIT(data, 1));
}

u8 pce_state::controller_r()
{
	return (m_port_ctrl->port_r() & 0x0f) | m_io_port_options;
}


void pce_state::cd_intf_w(offs_t offset, u8 data)
{
	m_cd->update();

	m_cd->intf_w(offset, data);

	m_cd->update();
}

u8 pce_state::cd_intf_r(offs_t offset)
{
	m_cd->update();

	return m_cd->intf_r(offset);
}

void pce_state::pce_mem(address_map &map)
{
	map(0x100000, 0x10ffff).ram().share("cd_ram");
	map(0x110000, 0x1edfff).noprw();
	map(0x1ee000, 0x1ee7ff).rw(m_cd, FUNC(pce_cd_device::bram_r), FUNC(pce_cd_device::bram_w));
	map(0x1ee800, 0x1effff).noprw();
	map(0x1f0000, 0x1f1fff).ram().mirror(0x6000);
	map(0x1fe000, 0x1fe3ff).rw("huc6270", FUNC(huc6270_device::read), FUNC(huc6270_device::write));
	map(0x1fe400, 0x1fe7ff).rw(m_huc6260, FUNC(huc6260_device::read), FUNC(huc6260_device::write));
	map(0x1ff800, 0x1ffbff).rw(FUNC(pce_state::cd_intf_r), FUNC(pce_state::cd_intf_w));
}

void pce_state::pce_io(address_map &map)
{
	map(0x00, 0x03).rw("huc6270", FUNC(huc6270_device::read), FUNC(huc6270_device::write));
}


void pce_state::sgx_mem(address_map &map)
{
	map(0x100000, 0x10ffff).ram().share("cd_ram");
	map(0x110000, 0x1edfff).noprw();
	map(0x1ee000, 0x1ee7ff).rw(m_cd, FUNC(pce_cd_device::bram_r), FUNC(pce_cd_device::bram_w));
	map(0x1ee800, 0x1effff).noprw();
	map(0x1f0000, 0x1f7fff).ram();
	map(0x1fe000, 0x1fe007).rw("huc6270_0", FUNC(huc6270_device::read), FUNC(huc6270_device::write)).mirror(0x03e0);
	map(0x1fe008, 0x1fe00f).rw("huc6202", FUNC(huc6202_device::read), FUNC(huc6202_device::write)).mirror(0x03e0);
	map(0x1fe010, 0x1fe017).rw("huc6270_1", FUNC(huc6270_device::read), FUNC(huc6270_device::write)).mirror(0x03e0);
	map(0x1fe400, 0x1fe7ff).rw(m_huc6260, FUNC(huc6260_device::read), FUNC(huc6260_device::write));
	map(0x1ff800, 0x1ffbff).rw(FUNC(pce_state::cd_intf_r), FUNC(pce_state::cd_intf_w));
}


void pce_state::sgx_io(address_map &map)
{
	map(0x00, 0x03).rw("huc6202", FUNC(huc6202_device::io_read), FUNC(huc6202_device::io_write));
}


uint32_t pce_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	m_huc6260->video_update(bitmap, cliprect);
	return 0;
}


void pce_state::machine_start()
{
	if (m_cd)
		m_cd->late_setup();

	// saving is only partially supported: it should be fine with cart games
	// OTOH CD states are saved but not correctly restored!
	save_item(NAME(m_io_port_options));
}

void pce_state::machine_reset()
{
}

static void pce_cart(device_slot_interface &device)
{
	device.option_add_internal("rom", PCE_ROM_STD);
	device.option_add_internal("cdsys3u", PCE_ROM_CDSYS3U);
	device.option_add_internal("cdsys3j", PCE_ROM_CDSYS3J);
	device.option_add_internal("populous", PCE_ROM_POPULOUS);
	device.option_add_internal("sf2", PCE_ROM_SF2);
	device.option_add_internal("tennokoe", PCE_ROM_TENNOKOE);
	device.option_add_internal("acard_duo", PCE_ROM_ACARD_DUO);
	device.option_add_internal("acard_pro", PCE_ROM_ACARD_PRO);
}

void pce_state::pce_common(machine_config &config)
{
	// basic machine hardware
	H6280(config, m_maincpu, MAIN_CLOCK/3);
	m_maincpu->set_addrmap(AS_PROGRAM, &pce_state::pce_mem);
	m_maincpu->set_addrmap(AS_IO, &pce_state::pce_io);
	m_maincpu->port_in_cb().set(FUNC(pce_state::controller_r));
	m_maincpu->port_out_cb().set(FUNC(pce_state::controller_w));
	m_maincpu->add_route(0, "speaker", 1.00, 0);
	m_maincpu->add_route(1, "speaker", 1.00, 1);

	config.set_maximum_quantum(attotime::from_hz(60));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(MAIN_CLOCK, huc6260_device::WPF, 64, 64 + 1024 + 64, huc6260_device::LPF, 18, 18 + 242);
	screen.set_screen_update(FUNC(pce_state::screen_update));
	screen.set_palette(m_huc6260);

	HUC6260(config, m_huc6260, MAIN_CLOCK);
	m_huc6260->next_pixel_data().set("huc6270", FUNC(huc6270_device::next_pixel));
	m_huc6260->time_til_next_event().set("huc6270", FUNC(huc6270_device::time_until_next_event));
	m_huc6260->vsync_changed().set("huc6270", FUNC(huc6270_device::vsync_changed));
	m_huc6260->hsync_changed().set("huc6270", FUNC(huc6270_device::hsync_changed));

	huc6270_device &huc6270(HUC6270(config, "huc6270", 0));
	huc6270.set_vram_size(0x10000);
	huc6270.irq().set_inputline(m_maincpu, 0);

	SPEAKER(config, "speaker", 2).front();

	PCE_CONTROL_PORT(config, m_port_ctrl, pce_control_port_devices, "joypad2");

	// TODO: expansion port not emulated
	PCE_CD(config, m_cd, 0);
	m_cd->irq().set_inputline(m_maincpu, 1);
	m_cd->set_maincpu(m_maincpu);
	m_cd->add_route(0, "speaker", 1.0, 0);
	m_cd->add_route(1, "speaker", 1.0, 1);

	SOFTWARE_LIST(config, "cd_list").set_original("pcecd");
}


void pce_state::pce(machine_config &config)
{
	pce_common(config);
	PCE_CART_SLOT(config, m_cartslot, pce_cart, nullptr, "pce_cart").set_must_be_loaded(true);
	m_cartslot->set_address_space(m_maincpu, AS_PROGRAM);
	SOFTWARE_LIST(config, "cart_list").set_original("pce");

	// bundled pad (in white PC engine) has not support autofire
}


void pce_state::tg16(machine_config &config)
{
	pce_common(config);
	PCE_CART_SLOT(config, m_cartslot, pce_cart, nullptr, "tg16_cart").set_must_be_loaded(true);
	m_cartslot->set_address_space(m_maincpu, AS_PROGRAM);
	SOFTWARE_LIST(config, "cart_list").set_original("tg16");

	// turbo pad bundled
	m_port_ctrl->set_default_option("joypad2_turbo");
}


void pce_state::sgx(machine_config &config)
{
	// basic machine hardware
	H6280(config, m_maincpu, MAIN_CLOCK/3);
	m_maincpu->set_addrmap(AS_PROGRAM, &pce_state::sgx_mem);
	m_maincpu->set_addrmap(AS_IO, &pce_state::sgx_io);
	m_maincpu->port_in_cb().set(FUNC(pce_state::controller_r));
	m_maincpu->port_out_cb().set(FUNC(pce_state::controller_w));
	m_maincpu->add_route(0, "speaker", 1.00, 0);
	m_maincpu->add_route(1, "speaker", 1.00, 1);

	config.set_maximum_quantum(attotime::from_hz(60));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(MAIN_CLOCK, huc6260_device::WPF, 64, 64 + 1024 + 64, huc6260_device::LPF, 18, 18 + 242);
	screen.set_screen_update(FUNC(pce_state::screen_update));
	screen.set_palette(m_huc6260);

	HUC6260(config, m_huc6260, MAIN_CLOCK);
	m_huc6260->next_pixel_data().set("huc6202", FUNC(huc6202_device::next_pixel));
	m_huc6260->time_til_next_event().set("huc6202", FUNC(huc6202_device::time_until_next_event));
	m_huc6260->vsync_changed().set("huc6202", FUNC(huc6202_device::vsync_changed));
	m_huc6260->hsync_changed().set("huc6202", FUNC(huc6202_device::hsync_changed));

	huc6270_device &huc6270_0(HUC6270(config, "huc6270_0", 0));
	huc6270_0.set_vram_size(0x10000);
	huc6270_0.irq().set_inputline(m_maincpu, 0); // needs input merger?

	huc6270_device &huc6270_1(HUC6270(config, "huc6270_1", 0));
	huc6270_1.set_vram_size(0x10000);
	huc6270_1.irq().set_inputline(m_maincpu, 0); // needs input merger?

	huc6202_device &huc6202(HUC6202(config, "huc6202", 0 ));
	huc6202.next_pixel_0_callback().set("huc6270_0", FUNC(huc6270_device::next_pixel));
	huc6202.time_til_next_event_0_callback().set("huc6270_0", FUNC(huc6270_device::time_until_next_event));
	huc6202.vsync_changed_0_callback().set("huc6270_0", FUNC(huc6270_device::vsync_changed));
	huc6202.hsync_changed_0_callback().set("huc6270_0", FUNC(huc6270_device::hsync_changed));
	huc6202.read_0_callback().set("huc6270_0", FUNC(huc6270_device::read));
	huc6202.write_0_callback().set("huc6270_0", FUNC(huc6270_device::write));
	huc6202.next_pixel_1_callback().set("huc6270_1", FUNC(huc6270_device::next_pixel));
	huc6202.time_til_next_event_1_callback().set("huc6270_1", FUNC(huc6270_device::time_until_next_event));
	huc6202.vsync_changed_1_callback().set("huc6270_1", FUNC(huc6270_device::vsync_changed));
	huc6202.hsync_changed_1_callback().set("huc6270_1", FUNC(huc6270_device::hsync_changed));
	huc6202.read_1_callback().set("huc6270_1", FUNC(huc6270_device::read));
	huc6202.write_1_callback().set("huc6270_1", FUNC(huc6270_device::write));

	SPEAKER(config, "speaker", 2).front();

	// turbo pad bundled
	PCE_CONTROL_PORT(config, m_port_ctrl, pce_control_port_devices, "joypad2_turbo");

	PCE_CART_SLOT(config, m_cartslot, pce_cart, nullptr, "pce_cart").set_must_be_loaded(true);
	m_cartslot->set_address_space(m_maincpu, AS_PROGRAM);
	SOFTWARE_LIST(config, "cart_list").set_original("sgx");
	SOFTWARE_LIST(config, "pce_list").set_compatible("pce");

	// TODO: expansion port not emulated
	PCE_CD(config, m_cd, 0);
	m_cd->irq().set_inputline(m_maincpu, 1);
	m_cd->set_maincpu(m_maincpu);
	m_cd->add_route(0, "speaker", 1.0, 0);
	m_cd->add_route(1, "speaker", 1.0, 1);

	SOFTWARE_LIST(config, "cd_list").set_original("pcecd");
}

/***************************************************************************

  Game driver(s)

***************************************************************************/

ROM_START( pce )
	ROM_REGION( 0x100000, "maincpu", ROMREGION_ERASEFF )
ROM_END

#define rom_tg16 rom_pce
#define rom_sgx rom_pce

void pce_state::init_pce()
{
	m_io_port_options = PCE_JOY_SIG | CONST_SIG;
}

void pce_state::init_tg16()
{
	m_io_port_options = TG_16_JOY_SIG | CONST_SIG;
}

CONS( 1987, pce,  0,   0, pce,  pce, pce_state, init_pce,  "NEC / Hudson Soft", "PC Engine",     MACHINE_IMPERFECT_SOUND )
CONS( 1989, tg16, pce, 0, tg16, pce, pce_state, init_tg16, "NEC / Hudson Soft", "TurboGrafx-16", MACHINE_IMPERFECT_SOUND )
CONS( 1989, sgx,  pce, 0, sgx,  pce, pce_state, init_pce,  "NEC / Hudson Soft", "SuperGrafx",    MACHINE_IMPERFECT_SOUND )
// TODO: TurboGrafx for PAL region?



pce220.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese, Sandro Ronco
/***************************************************************************

    Sharp PC-E220

    preliminary driver by Angelo Salese
    improvements by Sandro Ronco

    Notes:
    - NVRAM works only if the machine is turned off (with OFF key) before closing MAME
    - Holding SHIFT + COMMA on boot loads the Test Menu

    TODO:
    - 11 pin interface for extensions (printer, cassette).
    - LCD contrast.
    - Add other models that have a similar hardware.

    More info:
      http://wwwhomes.uni-bielefeld.de/achim/pc-e220.html
      http://www.akiyan.com/pc-g850_technical_data (in Japanese)

****************************************************************************/

#include "emu.h"

#include "pce220_ser.h"

#include "cpu/z80/z80.h"
#include "machine/input_merger.h"
#include "machine/nvram.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "sound/beep.h"
#include "video/hd61202.h"
#include "video/sed1520.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "pce220.lh"
#include "pcg850v.lh"

namespace {

class pce220_state : public driver_device
{
public:
	pce220_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_ram(*this, RAM_TAG)
		, m_banks(*this, "bank%u", 1)
		, m_beep(*this, "beeper")
		, m_lcdc(*this, "hd61202")
		, m_input_merger(*this, "input_merger")
		, m_serial(*this, "serial")
		, m_keyboard(*this, "LINE%u", 0)
		, m_io_on(*this, "ON")
		, m_battery(*this, "BATTERY")
		, m_lcd_symbols(*this, "sym.%u", 0U)
	{ }

	void pce220(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(kb_irq);
	DECLARE_INPUT_CHANGED_MEMBER(on_irq);

protected:

	// Interrupt flags
	enum : uint8_t
	{
		IRQ_FLAG_KEY   = 0x01,
		IRQ_FLAG_ON    = 0x02,
		IRQ_FLAG_TIMER = 0x04,
	};

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<ram_device> m_ram;
	required_memory_bank_array<4> m_banks;
	required_device<beep_device> m_beep;
	optional_device<hd61202_device> m_lcdc;
	required_device<input_merger_device> m_input_merger;
	required_device<pce220_serial_device> m_serial;

	required_ioport_array<10> m_keyboard;
	required_ioport m_io_on;
	required_ioport m_battery;

	output_finder<18> m_lcd_symbols;

	//basic machine
	uint8_t m_bank_num;
	uint8_t m_irq_mask;
	uint8_t m_irq_flag;
	uint8_t m_timer_status;
	uint16_t m_kb_matrix;
	uint8_t m_power_state;

	uint8_t m_port15;
	uint8_t m_port18;
	uint8_t m_battery_sel;

	virtual void device_resolve_objects() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	TIMER_DEVICE_CALLBACK_MEMBER(pce220_timer_callback);

	void pce220_palette(palette_device &palette) const;

	HD61202_UPDATE_CB(hd61202_update);
	uint8_t rom_bank_r();
	void rom_bank_w(uint8_t data);
	void ram_bank_w(uint8_t data);
	uint8_t timer_r();
	void timer_w(uint8_t data);
	void boot_bank_w(uint8_t data);
	uint8_t port15_r();
	void port15_w(uint8_t data);
	uint8_t port18_r();
	void port18_w(uint8_t data);
	void battery_w(uint8_t data);
	uint8_t battery_r();
	uint8_t port1f_r();
	void kb_matrix_w(offs_t offset, uint8_t data);
	uint8_t kb_r();
	uint8_t irq_status_r();
	void irq_ack_w(uint8_t data);
	void irq_mask_w(uint8_t data);
	void install_bootrom();

	void pce220_io_common(address_map &map) ATTR_COLD;
	void pce220_io(address_map &map) ATTR_COLD;
	void pce220_mem(address_map &map) ATTR_COLD;
};


class pcg815_state : public pce220_state
{
public:
	pcg815_state(const machine_config &mconfig, device_type type, const char *tag)
		: pce220_state(mconfig, type, tag)
		, m_lcdc2(*this, "hd61202_2")
		{ }

	void pcg815(machine_config &config);

private:
	required_device<hd61202_device> m_lcdc2;

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void lcdc_control_w(uint8_t data);
	void lcdc_data_w(uint8_t data);
	HD61202_UPDATE_CB(hd61202_1_update);
	HD61202_UPDATE_CB(hd61202_2_update);
	void pcg815_io(address_map &map) ATTR_COLD;
};


class pcg850v_state : public pce220_state
{
public:
	pcg850v_state(const machine_config &mconfig, device_type type, const char *tag)
		: pce220_state(mconfig, type, tag)
		{ }

	void pcg850v(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	uint8_t m_g850v_bank_num = 0;

	SED1560_UPDATE_CB(sed1560_update);
	uint8_t g850v_bank_r();
	void g850v_rom_bank_w(uint8_t data);
	void g850v_bank_w(uint8_t data);
	void pcg850v_io(address_map &map) ATTR_COLD;
};


HD61202_UPDATE_CB(pce220_state::hd61202_update)
{
	if (lcd_on)
	{
		bitmap.fill(2, cliprect);
		for (int y = 0; y < 4 * 8; y++)
		{
			if ((y & 7) == 7)
				continue;

			int ys = start_line + y;
			for (int x = 0; x < 12 * 5; x++)
			{
				int xdest = x + (x / 5);
				int bitpos = (ys & ~7) * 64 + x * 8 + (ys & 7);

				//first 12 columns
				bitmap.pix(y, xdest) = BIT(ddr[(bitpos / 8) & 0x1ff], bitpos & 7);

				//last 12 columns
				bitmap.pix(y, 142 - xdest) = BIT(ddr[(0x100 + bitpos / 8) & 0x1ff], bitpos & 7);
			}
		}
	}
	else
		bitmap.fill(0, cliprect);


	static const uint16_t lcd_symbol_bits[] =
	{
		//BUSY  CAPS  KANA  SHO   2ndF   DE     G     RAD    CONST   M     E     BATT   RUN    PRO    CASL   TEXT   STAT   PRINT
		480,    481,  482,  483,  484,  2534,  2533,  2532,  2531, 2530,  2529,  2528,  2022,  2021,  2020,  2019,  4069,  4070
	};

	for (int i=0; i<18; i++)
	{
		int bitpos = ((start_line & ~7) * 64 + lcd_symbol_bits[i]) + (start_line & 7);
		m_lcd_symbols[i] = !lcd_on ? 0 : BIT(ddr[(bitpos / 8) & 0x1ff], bitpos & 7);
	}

	return 0;
}

HD61202_UPDATE_CB(pcg815_state::hd61202_1_update)
{
	if (lcd_on)
	{
		for (int y = 0; y < 4 * 8; y++)
		{
			int ys = start_line + y;
			for (int x = 0; x < 2 * 6; x++)
			{
				int bitpos = (ys & ~7) * 64 + x * 8 + (ys & 7);
				bitmap.pix(y, 60 + x) = BIT(ddr[(bitpos / 8) & 0x1ff], bitpos & 7);
				bitmap.pix(y, 83 - x) = BIT(ddr[(0x100 + bitpos / 8) & 0x1ff], bitpos & 7);
			}
		}
	}

	static const uint16_t lcd_symbol_bits[] =
	{
		//BUSY  CAPS   KANA   SHO    2ndF    DE     G     RAD    CONST   M      E     BATT   RUN   PRO   CASL  TEXT  STAT  PRINT
		504,    2555,  2553,  2552,  2556,  3068,  3067,  3066,  2554,  3065,  1529,  3576,  505,  506,  507,  510,  509,  3064
	};

	for (int i=0; i<18; i++)
	{
		int bitpos = ((start_line & ~7) * 64 + lcd_symbol_bits[i]) + (start_line & 7);
		m_lcd_symbols[i] = !lcd_on ? 0 : BIT(ddr[(bitpos / 8) & 0x1ff], bitpos & 7);
	}

	return 0;
}


HD61202_UPDATE_CB(pcg815_state::hd61202_2_update)
{
	if (lcd_on)
	{
		for (int y = 0; y < 4 * 8; y++)
		{
			int ys = start_line + y;
			for (int x = 0; x < 10 * 6; x++)
			{
				int bitpos = (ys & ~7) * 64 + x * 8 + (ys & 7);
				bitmap.pix(y, x) = BIT(ddr[(bitpos / 8) & 0x1ff], bitpos & 7);
				bitmap.pix(y, 143 - x) = BIT(ddr[(0x100 + bitpos / 8) & 0x1ff], bitpos & 7);
			}
		}
	}

	return 0;
}


uint32_t pcg815_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	bitmap.fill(0, cliprect);
	m_lcdc->screen_update(screen, bitmap, cliprect);
	m_lcdc2->screen_update(screen, bitmap, cliprect);
	return 0;
}


SED1560_UPDATE_CB(pcg850v_state::sed1560_update)
{
	if (lcd_on)
	{
		uint16_t color0 = reverse ? 1 : 0;
		uint16_t color1 = reverse ? 0 : 1;

		for (int y = 0; y < 6 * 8; y++)
		{
			int ys = start_line + y;
			for (int x = 0; x < 24 * 6; x++)
			{
				int bitpos = ((ys & ~7) * 166 + x * 8 + (ys & 7)) % (64 * 166);
				bitmap.pix(y, x) = BIT(dram[bitpos / 8], bitpos & 7) ? color1 : color0;
			}
		}
	}
	else
		bitmap.fill(0, cliprect);


	static const uint16_t lcd_symbol_bits[] =
	{
		//BUSY   BATT   RUN    PRO    TEXT   CASL   STAT   2ndF    M     CAPS   KANA   SHO     DE     G     RAD    CONST  PRINT
		7798,    7799,  1153,  1155,  1158,  2483,  3808,  3813,  3815,  5138,  5143,  6465,  6468,  6470,  7792,  7795,  7796,
	};

	for (int i=0; i<17; i++)
	{
		int bitpos = (((start_line & ~7) * 166 + lcd_symbol_bits[i]) + (start_line & 7)) % (64 * 166);
		m_lcd_symbols[i] = !lcd_on ? 0 : BIT(dram[bitpos / 8], bitpos & 7);
	}

	return 0;
}

void pcg815_state::lcdc_control_w(uint8_t data)
{
	m_lcdc->control_w(data);
	m_lcdc2->control_w(data);
}

void pcg815_state::lcdc_data_w(uint8_t data)
{
	m_lcdc->data_w(data);
	m_lcdc2->data_w(data);
}

uint8_t pce220_state::rom_bank_r()
{
	return m_bank_num;
}

void pce220_state::rom_bank_w(uint8_t data)
{
	m_bank_num = data;

	m_banks[2]->set_entry((data & 0x70) >> 4);  // bits 4,5,6
	m_banks[3]->set_entry(data & 0x0f);         // bits 0,1,2,3
}

void pcg850v_state::g850v_rom_bank_w(uint8_t data)
{
	pce220_state::rom_bank_w(data);
	m_g850v_bank_num = m_bank_num & 0x0f;
}

void pce220_state::ram_bank_w(uint8_t data)
{
	uint8_t bank = BIT(data,2);

	m_banks[0]->set_entry(bank);
	m_banks[1]->set_entry(bank);
}

uint8_t pce220_state::timer_r()
{
	return m_timer_status;
}

void pce220_state::timer_w(uint8_t data)
{
	m_input_merger->in_w<2>(CLEAR_LINE);
	m_timer_status = 0;
}

void pce220_state::boot_bank_w(uint8_t data)
{
	// set to 1 after boot for restore the ram in the first bank
	if (data & 0x01)
	{
		address_space &space_prg = m_maincpu->space(AS_PROGRAM);
		space_prg.install_readwrite_bank(0x0000, 0x3fff, m_banks[0]);
		m_banks[0]->set_entry(0);
	}
}

uint8_t pce220_state::port15_r()
{
	/*
	x--- ---- XIN input enabled
	---- ---0
	*/
	return m_port15;
}

void pce220_state::port15_w(uint8_t data)
{
	m_serial->enable_interface(BIT(data, 7));

	m_port15 = data;
}

uint8_t pce220_state::port18_r()
{
	/*
	x--- ---- XOUT/TXD
	---- --x- DOUT
	---- ---x BUSY/CTS
	*/

	return m_port18;
}

void pce220_state::port18_w(uint8_t data)
{
	m_beep->set_state(BIT(data, 7));

	m_serial->out_busy(BIT(data, 0));
	m_serial->out_dout(BIT(data, 1));
	m_serial->out_xout(BIT(data, 7));

	m_port18 = data;
}

void pce220_state::battery_w(uint8_t data)
{
	m_battery_sel = data;
}

uint8_t pce220_state::battery_r()
{
	if (m_battery_sel & 2)
		return BIT(m_battery->read(), 1);
	if (m_battery_sel & 1)
		return BIT(m_battery->read(), 0);

	return 0;
}

uint8_t pce220_state::port1f_r()
{
	/*
	x--- ---- ON - resp. break key status (?)
	---- -x-- XIN/RXD
	---- --x- ACK/RTS
	---- ---x DIN
	*/

	uint8_t data = 0;

	data |= m_serial->in_din()<<0;
	data |= m_serial->in_ack()<<1;
	data |= m_serial->in_xin()<<2;

	data |= m_io_on->read()<<7;

	return data;
}

void pce220_state::kb_matrix_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 0:
		m_kb_matrix = (m_kb_matrix & 0x300) | data;
		break;
	case 1:
		m_kb_matrix = (m_kb_matrix & 0xff) | ((data&0x03)<<8);
		break;
	}
}

uint8_t pce220_state::kb_r()
{
	uint8_t data = 0x00;

	for (int i = 0; i < 10; i++)
		if (BIT(m_kb_matrix, i))
			data |= m_keyboard[i]->read();

	return data;
}

uint8_t pce220_state::irq_status_r()
{
	/*
	---- -x-- timer
	---- --x- ON-Key
	---- ---x keyboard
	*/
	return m_irq_flag;
}

void pce220_state::irq_ack_w(uint8_t data)
{
	m_irq_flag &= ~data;
}

void pce220_state::irq_mask_w(uint8_t data)
{
	m_irq_mask = data;
}

uint8_t pcg850v_state::g850v_bank_r()
{
	return m_g850v_bank_num;
}

void pcg850v_state::g850v_bank_w(uint8_t data)
{
	address_space &space_prg = m_maincpu->space(AS_PROGRAM);

	if (data < 0x16)
	{
		space_prg.install_read_bank(0xc000, 0xffff, m_banks[3]);
		m_banks[3]->set_entry(data);
	}
	else
	{
		space_prg.unmap_read(0xc000, 0xffff);
	}

	m_g850v_bank_num = data;

	m_bank_num = (m_bank_num & 0xf0) | (m_g850v_bank_num & 0x0f);
}

void pce220_state::pce220_mem(address_map &map)
{
	map(0x0000, 0x3fff).bankrw("bank1");
	map(0x4000, 0x7fff).bankrw("bank2");
	map(0x8000, 0xbfff).bankr("bank3");
	map(0xc000, 0xffff).bankr("bank4");
}

void pce220_state::pce220_io_common(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x10, 0x10).r(FUNC(pce220_state::kb_r));
	map(0x11, 0x12).w(FUNC(pce220_state::kb_matrix_w));
	map(0x13, 0x13).portr("SHIFT");
	map(0x14, 0x14).rw(FUNC(pce220_state::timer_r), FUNC(pce220_state::timer_w));
	map(0x15, 0x15).rw(FUNC(pce220_state::port15_r), FUNC(pce220_state::port15_w));
	map(0x16, 0x16).rw(FUNC(pce220_state::irq_status_r), FUNC(pce220_state::irq_ack_w));
	map(0x17, 0x17).w(FUNC(pce220_state::irq_mask_w));
	map(0x18, 0x18).rw(FUNC(pce220_state::port18_r), FUNC(pce220_state::port18_w));
	map(0x19, 0x19).rw(FUNC(pce220_state::rom_bank_r), FUNC(pce220_state::rom_bank_w));
	map(0x1a, 0x1a).w(FUNC(pce220_state::boot_bank_w));
	map(0x1b, 0x1b).w(FUNC(pce220_state::ram_bank_w));
	map(0x1c, 0x1c).lw8(NAME([this](uint32_t data) { m_power_state = data; })); //peripheral reset
	map(0x1d, 0x1d).r(FUNC(pce220_state::battery_r));
	map(0x1e, 0x1e).w(FUNC(pce220_state::battery_w));
	map(0x1f, 0x1f).r(FUNC(pce220_state::port1f_r));
}

void pce220_state::pce220_io(address_map &map)
{
	pce220_io_common(map);
	map(0x58, 0x58).w(m_lcdc, FUNC(hd61202_device::control_w));
	map(0x59, 0x59).r(m_lcdc, FUNC(hd61202_device::status_r));
	map(0x5a, 0x5a).w(m_lcdc, FUNC(hd61202_device::data_w));
	map(0x5b, 0x5b).r(m_lcdc, FUNC(hd61202_device::data_r));
}

void pcg815_state::pcg815_io(address_map &map)
{
	pce220_io_common(map);
	map(0x50, 0x50).w(FUNC(pcg815_state::lcdc_control_w));
	map(0x52, 0x52).w(FUNC(pcg815_state::lcdc_data_w));
	map(0x54, 0x54).w(m_lcdc2, FUNC(hd61202_device::control_w));
	map(0x55, 0x55).r(m_lcdc2, FUNC(hd61202_device::status_r));
	map(0x56, 0x56).w(m_lcdc2, FUNC(hd61202_device::data_w));
	map(0x57, 0x57).r(m_lcdc2, FUNC(hd61202_device::data_r));
	map(0x58, 0x58).w(m_lcdc, FUNC(hd61202_device::control_w));
	map(0x59, 0x59).r(m_lcdc, FUNC(hd61202_device::status_r));
	map(0x5a, 0x5a).w(m_lcdc, FUNC(hd61202_device::data_w));
	map(0x5b, 0x5b).r(m_lcdc, FUNC(hd61202_device::data_r));
}

void pcg850v_state::pcg850v_io(address_map &map)
{
	pce220_io_common(map);
	map(0x19, 0x19).rw(FUNC(pcg850v_state::rom_bank_r), FUNC(pcg850v_state::g850v_rom_bank_w));
	map(0x40, 0x41).mirror(0x1e).rw("sed1560", FUNC(sed1560_device::read), FUNC(sed1560_device::write));
	map(0x69, 0x69).rw(FUNC(pcg850v_state::g850v_bank_r), FUNC(pcg850v_state::g850v_bank_w));
	map(0x74, 0x74).nopr();
}

INPUT_CHANGED_MEMBER(pce220_state::kb_irq)
{
	if (m_irq_mask & IRQ_FLAG_KEY)
	{
		m_input_merger->in_w<0>(newval ? ASSERT_LINE : CLEAR_LINE);

		m_irq_flag = (m_irq_flag & 0xfe) | (newval & 0x01);
	}
}

INPUT_CHANGED_MEMBER(pce220_state::on_irq)
{
	if (m_irq_mask & IRQ_FLAG_ON)
	{
		if (!(m_power_state & 1))
		{
			install_bootrom();
			m_maincpu->reset();
		}

		m_input_merger->in_w<1>(newval ? ASSERT_LINE : CLEAR_LINE);

		m_irq_flag = (m_irq_flag & 0xfd) | ((newval & 0x01)<<1);
	}
}

/* Input ports */
static INPUT_PORTS_START( pce220 )
	PORT_START("BATTERY")
	PORT_CONFNAME( 0x03, 0x00, "Battery Status" )
	PORT_CONFSETTING( 0x00, DEF_STR( Normal ) )
	PORT_CONFSETTING( 0x01, "Low" )
	PORT_CONFSETTING( 0x02, "Empty" )

	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F10)          PORT_NAME("OFF")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_Q)            PORT_NAME("Q")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('Q')  PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_W)            PORT_NAME("W")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('W')  PORT_CHAR('"')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_E)            PORT_NAME("E")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('E')  PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_R)            PORT_NAME("R")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('R')  PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_T)            PORT_NAME("T")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('T')  PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_Y)            PORT_NAME("Y")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('Y')  PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_U)            PORT_NAME("U")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('U')  PORT_CHAR('\'')
	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_A)            PORT_NAME("A")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('A')  PORT_CHAR('[')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_S)            PORT_NAME("S")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('S')  PORT_CHAR(']')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_D)            PORT_NAME("D")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('D')  PORT_CHAR('{')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F)            PORT_NAME("F")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('F')  PORT_CHAR('}')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_G)            PORT_NAME("G")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('G')  PORT_CHAR('\\')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_H)            PORT_NAME("H")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('H')  PORT_CHAR('|')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_J)            PORT_NAME("J")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('J')  PORT_CHAR('~')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_K)            PORT_NAME("K")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('K')  PORT_CHAR('_')
	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_Z)            PORT_NAME("Z")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('Z')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_X)            PORT_NAME("X")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('X')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_C)            PORT_NAME("C")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('C')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_V)            PORT_NAME("V")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('V')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_B)            PORT_NAME("B")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('B')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_N)            PORT_NAME("N")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('N')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_M)            PORT_NAME("M")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('M')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_COMMA)        PORT_NAME(",")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(',')  PORT_CHAR('?')
	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F1)           PORT_NAME("CAL")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F2)           PORT_NAME("BAS")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_CAPSLOCK)     PORT_NAME("CAPS")       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_HOME)         PORT_NAME("ANS")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_TAB)          PORT_NAME("TAB")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_SPACE)        PORT_NAME("SPACE")      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(' ')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_DOWN)         PORT_NAME("DOWN")       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_UP)           PORT_NAME("UP")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_LEFT)         PORT_NAME("LEFT")       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_RIGHT)        PORT_NAME("RIGHT")      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F4)           PORT_NAME("CONS")       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_0)            PORT_NAME("0")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('0')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_STOP)         PORT_NAME(".")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('.')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_BACKSLASH)    PORT_NAME("+/-")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_PLUS_PAD)     PORT_NAME("+")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('+')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_ENTER)        PORT_NAME("RET")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(13)
	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_L)            PORT_NAME("L")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('L')  PORT_CHAR('=')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_COLON)        PORT_NAME(";")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(';')  PORT_CHAR(':')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_DEL)          PORT_NAME("DEL")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_1)            PORT_NAME("1")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('1')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_2)            PORT_NAME("2")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('2')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_3)            PORT_NAME("3")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('3')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_MINUS)        PORT_NAME("-")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('-')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F5)           PORT_NAME("M+")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_I)            PORT_NAME("I")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('I')  PORT_CHAR('<')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_O)            PORT_NAME("O")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('O')  PORT_CHAR('>')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_INSERT)       PORT_NAME("INS")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_4)            PORT_NAME("4")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('4')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_5)            PORT_NAME("5")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('5')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_6)            PORT_NAME("6")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('6')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_ASTERISK)     PORT_NAME("*")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('*')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F6)           PORT_NAME("RM")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_START("LINE7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_P)            PORT_NAME("P")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('P')  PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_BACKSPACE)    PORT_NAME("BS")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(UCHAR_MAMEKEY(BACKSPACE),8)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F7)           PORT_NAME("n!")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_7)            PORT_NAME("7")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('7')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_8)            PORT_NAME("8")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('8')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_9)            PORT_NAME("9")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('9')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_SLASH)        PORT_NAME("/")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('/')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_NAME(")")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(')')
	PORT_START("LINE8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_1_PAD)        PORT_NAME("hyp")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_2_PAD)        PORT_NAME("DEG")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_3_PAD)        PORT_NAME("y^x")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('^')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_4_PAD)        PORT_NAME("sqrt")       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_5_PAD)        PORT_NAME("x^2")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_OPENBRACE)    PORT_NAME("(")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('(')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_6_PAD)        PORT_NAME("1/x")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_7_PAD)        PORT_NAME("MDF")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_START("LINE9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_LCONTROL)     PORT_NAME("2nd")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_8_PAD)        PORT_NAME("sin")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_9_PAD)        PORT_NAME("cos")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_0_PAD)        PORT_NAME("ln")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F8)           PORT_NAME("log")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F9)           PORT_NAME("tan")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F11)          PORT_NAME("FSE")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_ESC)          PORT_NAME("CCE")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(27)
	PORT_START("SHIFT")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)     PORT_NAME("Shift")      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(UCHAR_SHIFT_1)
	PORT_START("ON")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_PGUP)         PORT_NAME("ON")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::on_irq), 0)
INPUT_PORTS_END

static INPUT_PORTS_START( pcg850v )
	PORT_START("BATTERY")
	PORT_CONFNAME( 0x03, 0x00, "Battery Status" )
	PORT_CONFSETTING( 0x00, DEF_STR( Normal ) )
	PORT_CONFSETTING( 0x01, "Low" )
	PORT_CONFSETTING( 0x02, "Empty" )

	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F10)          PORT_NAME("OFF")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_Q)            PORT_NAME("Q")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('Q')  PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_W)            PORT_NAME("W")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('W')  PORT_CHAR('"')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_E)            PORT_NAME("E")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('E')  PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_R)            PORT_NAME("R")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('R')  PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_T)            PORT_NAME("T")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('T')  PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_Y)            PORT_NAME("Y")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('Y')  PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_U)            PORT_NAME("U")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('U')  PORT_CHAR('\'')
	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_A)            PORT_NAME("A")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('A')  PORT_CHAR('[')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_S)            PORT_NAME("S")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('S')  PORT_CHAR(']')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_D)            PORT_NAME("D")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('D')  PORT_CHAR('{')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F)            PORT_NAME("F")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('F')  PORT_CHAR('}')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_G)            PORT_NAME("G")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_H)            PORT_NAME("H")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('H')  PORT_CHAR('|')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_J)            PORT_NAME("J")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('J')  PORT_CHAR('~')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_K)            PORT_NAME("K")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('K')  PORT_CHAR('_')
	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_Z)            PORT_NAME("Z")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('Z')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_X)            PORT_NAME("X")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('X')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_C)            PORT_NAME("C")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('C')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_V)            PORT_NAME("V")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('V')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_B)            PORT_NAME("B")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('B')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_N)            PORT_NAME("N")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('N')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_M)            PORT_NAME("M")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('M')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_COMMA)        PORT_NAME(",")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(',')  PORT_CHAR('?')
	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F1)           PORT_NAME("BAS")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F2)           PORT_NAME("TEXT")       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_CAPSLOCK)     PORT_NAME("CAPS")       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_HOME)         PORT_NAME("KANA")       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_TAB)          PORT_NAME("TAB")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_SPACE)        PORT_NAME("SPACE")      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(' ')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_DOWN)         PORT_NAME("DOWN")       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_UP)           PORT_NAME("UP")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_LEFT)         PORT_NAME("LEFT")       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_RIGHT)        PORT_NAME("RIGHT")      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F4)           PORT_NAME("CONS")       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_0)            PORT_NAME("0")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('0')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_STOP)         PORT_NAME(".")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('.')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_EQUALS)       PORT_NAME("=")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('=')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_PLUS_PAD)     PORT_NAME("+")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('+')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_ENTER)        PORT_NAME("RET")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(13)
	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_L)            PORT_NAME("L")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('L')  PORT_CHAR('=')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_COLON)        PORT_NAME(";")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(';')  PORT_CHAR(':')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_DEL)          PORT_NAME("DEL")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_1)            PORT_NAME("1")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('1')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_2)            PORT_NAME("2")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('2')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_3)            PORT_NAME("3")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('3')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_MINUS)        PORT_NAME("-")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('-')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F5)           PORT_NAME("M+")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_I)            PORT_NAME("I")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('I')  PORT_CHAR('<')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_O)            PORT_NAME("O")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('O')  PORT_CHAR('>')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_INSERT)       PORT_NAME("INS")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_4)            PORT_NAME("4")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('4')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_5)            PORT_NAME("5")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('5')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_6)            PORT_NAME("6")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('6')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_ASTERISK)     PORT_NAME("*")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('*')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F6)           PORT_NAME("RM")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_START("LINE7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_P)            PORT_NAME("P")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('P')  PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_BACKSPACE)    PORT_NAME("BS")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(UCHAR_MAMEKEY(BACKSPACE),8)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F7)           PORT_NAME("pi")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_7)            PORT_NAME("7")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('7')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_8)            PORT_NAME("8")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('8')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_9)            PORT_NAME("9")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('9')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_SLASH)        PORT_NAME("/")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('/')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_NAME(")")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(')')
	PORT_START("LINE8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_1_PAD)        PORT_NAME("nPr")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_2_PAD)        PORT_NAME("DEG")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_3_PAD)        PORT_NAME("SQR")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_4_PAD)        PORT_NAME("SQU")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_5_PAD)        PORT_NAME("x^y")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('^')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_OPENBRACE)    PORT_NAME("(")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR('(')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_6_PAD)        PORT_NAME("1/x")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_7_PAD)        PORT_NAME("MDF")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_START("LINE9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_LCONTROL)     PORT_NAME("2nd")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_8_PAD)        PORT_NAME("sin")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_9_PAD)        PORT_NAME("cos")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_0_PAD)        PORT_NAME("ln")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F8)           PORT_NAME("log")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F9)           PORT_NAME("tan")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_F11)          PORT_NAME("FSE")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_ESC)          PORT_NAME("CCE")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(27)
	PORT_START("SHIFT")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)     PORT_NAME("Shift")      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::kb_irq), 0)  PORT_CHAR(UCHAR_SHIFT_1)
	PORT_START("ON")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)    PORT_CODE(KEYCODE_PGUP)         PORT_NAME("ON")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pce220_state::on_irq), 0)
INPUT_PORTS_END

void pce220_state::device_resolve_objects()
{
	m_lcd_symbols.resolve();
}

void pce220_state::machine_start()
{
	uint8_t *rom = memregion("user1")->base();
	uint8_t *ram = m_ram->pointer();

	m_banks[0]->configure_entries(0, 2, ram + 0x0000, 0x8000);
	m_banks[1]->configure_entries(0, 2, ram + 0x4000, 0x8000);
	m_banks[2]->configure_entries(0, 8, rom, 0x4000);
	m_banks[3]->configure_entries(0, 16, rom, 0x4000);

	subdevice<nvram_device>("nvram")->set_base(ram, m_ram->size());

	save_item(NAME(m_bank_num));
	save_item(NAME(m_irq_mask));
	save_item(NAME(m_irq_flag));
	save_item(NAME(m_timer_status));
	save_item(NAME(m_kb_matrix));
	save_item(NAME(m_power_state));
	save_item(NAME(m_port15));
	save_item(NAME(m_port18));
	save_item(NAME(m_battery_sel));
}

void pcg850v_state::machine_start()
{
	pce220_state::machine_start();

	uint8_t *rom = memregion("user1")->base();
	m_banks[3]->configure_entries(0, 22, rom, 0x4000);

	save_item(NAME(m_g850v_bank_num));
}

void pce220_state::install_bootrom()
{
	address_space &space = m_maincpu->space(AS_PROGRAM);
	space.unmap_write(0x0000, 0x3fff);

	// install the boot code into the first bank
	space.install_rom(0x0000, 0x3fff, memregion("user1")->base() + 0x0000);
}

void pce220_state::machine_reset()
{
	install_bootrom();
	m_bank_num = 0;
	m_irq_mask = 0;
	m_irq_flag = 0;
	m_timer_status = 0;
	m_kb_matrix = 0;
	m_power_state = 0;
	m_port15 = 0;
	m_port18 = 0;
	m_battery_sel = 0;
}

void pcg850v_state::machine_reset()
{
	pce220_state::machine_reset();

	m_g850v_bank_num = 0;
}

TIMER_DEVICE_CALLBACK_MEMBER(pce220_state::pce220_timer_callback)
{
	m_timer_status = 1;
	if (m_irq_mask & IRQ_FLAG_TIMER)
	{
		m_input_merger->in_w<2>(ASSERT_LINE);

		m_irq_flag = (m_irq_flag & 0xfb) | (m_timer_status<<2);
	}
}

void pce220_state::pce220_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(131, 136, 139)); // lcd pixel off
	palette.set_pen_color(1, rgb_t( 51,  42,  43)); // lcd pixel on
	palette.set_pen_color(2, rgb_t(138, 146, 148)); // background
}


void pce220_state::pce220(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 3.072_MHz_XTAL); // CMOS-SC7852
	m_maincpu->set_addrmap(AS_PROGRAM, &pce220_state::pce220_mem);
	m_maincpu->set_addrmap(AS_IO, &pce220_state::pce220_io);

	INPUT_MERGER_ANY_HIGH(config, m_input_merger);
	m_input_merger->output_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	/* video hardware */
	// 4 lines x 24 characters, resp. 144 x 32 pixel
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_screen_update(m_lcdc, FUNC(hd61202_device::screen_update));
	m_screen->set_size(24*6, 4*8);
	m_screen->set_visarea_full();
	m_screen->set_palette("palette");

	PALETTE(config, "palette", FUNC(pce220_state::pce220_palette), 3);

	HD61202(config, m_lcdc);
	m_lcdc->set_screen_update_cb(FUNC(pce220_state::hd61202_update));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 3250).add_route(ALL_OUTPUTS, "mono", 0.50);

	TIMER(config, "pce220_timer").configure_periodic(FUNC(pce220_state::pce220_timer_callback), attotime::from_msec(468));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("64K"); // 32K internal + 32K external card

	PCE220SERIAL(config, m_serial, 0);
	config.set_default_layout(layout_pce220);
}

void pcg815_state::pcg815(machine_config &config)
{
	pce220(config);

	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_IO, &pcg815_state::pcg815_io);

	/* video hardware */
	// 4 lines x 24 characters, resp. 144 x 32 pixel
	m_screen->set_screen_update(FUNC(pcg815_state::screen_update));

	m_lcdc->set_screen_update_cb(FUNC(pcg815_state::hd61202_1_update));

	HD61202(config, m_lcdc2);
	m_lcdc2->set_screen_update_cb(FUNC(pcg815_state::hd61202_2_update));
}

void pcg850v_state::pcg850v(machine_config &config)
{
	pce220(config);

	/* basic machine hardware */
	m_maincpu->set_clock(8_MHz_XTAL); // CMOS-SC7852
	m_maincpu->set_addrmap(AS_IO, &pcg850v_state::pcg850v_io);

	/* video hardware */
	// 6 lines x 24 characters, resp. 144 x 48 pixel
	m_screen->set_screen_update("sed1560", FUNC(sed1560_device::screen_update));
	m_screen->set_size(144, 48);
	m_screen->set_visarea_full();

	config.device_remove("hd61202");

	sed1560_device &lcdc(SED1560(config, "sed1560"));
	lcdc.set_screen_update_cb(FUNC(pcg850v_state::sed1560_update));

	config.set_default_layout(layout_pcg850v);
}

/* ROM definition */
ROM_START( pce220 )
	ROM_REGION( 0x40000, "user1", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v1", "v 0.1")
	ROM_SYSTEM_BIOS( 1, "v2", "v 0.2")
	ROM_LOAD(  "bank0.bin",      0x0000, 0x4000, CRC(1fa94d11) SHA1(24c54347dbb1423388360a359aa09db47d2057b7))
	ROM_LOAD(  "bank1.bin",      0x4000, 0x4000, CRC(0f9864b0) SHA1(6b7301c96f1a865e1931d82872a1ed5d1f80644e))
	ROM_LOAD(  "bank2.bin",      0x8000, 0x4000, CRC(1625e958) SHA1(090440600d461aa7efe4adbf6e975aa802aabeec))
	ROM_LOAD(  "bank3.bin",      0xc000, 0x4000, CRC(ed9a57f8) SHA1(58087dc64103786a40325c0a1e04bd88bfd6da57))
	ROM_LOAD(  "bank4.bin",     0x10000, 0x4000, CRC(e37665ae) SHA1(85f5c84f69f79e7ac83b30397b2a1d9629f9eafa))
	ROMX_LOAD( "bank5.bin",     0x14000, 0x4000, CRC(6b116e7a) SHA1(b29f5a070e846541bddc88b5ee9862cc36b88eee), ROM_BIOS(1))
	ROMX_LOAD( "bank5_0.1.bin", 0x14000, 0x4000, CRC(13c26eb4) SHA1(b9cd0efd6b195653b9610e20ad8aab541824a689), ROM_BIOS(0))
	ROMX_LOAD( "bank6.bin",     0x18000, 0x4000, CRC(4fbfbd18) SHA1(e5aab1df172dcb94aa90e7d898eacfc61157ff15), ROM_BIOS(1))
	ROMX_LOAD( "bank6_0.1.bin", 0x18000, 0x4000, CRC(e2cda7a6) SHA1(01b1796d9485fde6994cb5afbe97514b54cfbb3a), ROM_BIOS(0))
	ROMX_LOAD( "bank7.bin",     0x1c000, 0x4000, CRC(5e98b5b6) SHA1(f22d74d6a24f5929efaf2983caabd33859232a94), ROM_BIOS(1))
	ROMX_LOAD( "bank7_0.1.bin", 0x1c000, 0x4000, CRC(d8e821b2) SHA1(18245a75529d2f496cdbdc28cdf40def157b20c0), ROM_BIOS(0))
ROM_END

ROM_START( pcg815 )
	ROM_REGION( 0x40000, "user1", ROMREGION_ERASEFF )
	ROM_LOAD( "rom00.bin",    0x000000, 0x004000, CRC(43767d51) SHA1(0d8b8b88a5d084f750daffc0abbb6b868fd3f144) )
	ROM_LOAD( "rom01.bin",    0x004000, 0x004000, CRC(b0e32ace) SHA1(e84c84c18c09dd2920fa38e77d8049b980692d29) )
	ROM_LOAD( "rom02.bin",    0x008000, 0x004000, CRC(f5cb78ba) SHA1(dfb2e415a5603a820b960d810995dd1109d84547) )
	ROM_LOAD( "rom03.bin",    0x00c000, 0x004000, CRC(5eff142b) SHA1(71d8d0a1bd0bcdef43f951036d9e1da0377d6a33) )
	ROM_LOAD( "rom04.bin",    0x010000, 0x004000, CRC(cb91bab5) SHA1(5b354ba6fc48043f03b573ad470518e9bb48cf99) )
	ROM_LOAD( "rom05.bin",    0x014000, 0x004000, CRC(ccaa1876) SHA1(404bf36c3832ee1a4602fa6a917d1ef2db5411c1) )
	ROM_LOAD( "rom06.bin",    0x018000, 0x004000, CRC(9d80802f) SHA1(aec989b8763d1346313a13c761ed0306b4f84257) )
	ROM_LOAD( "rom07.bin",    0x01c000, 0x004000, CRC(618a7be8) SHA1(5f16e47eeeb9c153bb95c6d6e3af276576b8274e) )
	ROM_LOAD( "rom08.bin",    0x020000, 0x004000, CRC(d8ba21a9) SHA1(a5580f22864afee8d4a8f1ecf6231acbde16b612) )
	ROM_LOAD( "rom09.bin",    0x024000, 0x004000, CRC(f6f49677) SHA1(e0d70a11b7dd21662cb444ac93d4b43f584909b5) )
	ROM_LOAD( "rom0a.bin",    0x028000, 0x004000, CRC(ecbabb64) SHA1(14beef60dfd1f18375459e9acb9527589e09e3ce) )
	ROM_LOAD( "rom0b.bin",    0x02c000, 0x004000, CRC(b4000f62) SHA1(eb12db023ff4b3946ab4aa9481a7e96695af0e4c) )
	ROM_LOAD( "rom0c.bin",    0x030000, 0x004000, CRC(e2d40305) SHA1(262aa749ed39fdfa1204297dd6e6c3d4abb9fd65) )
	ROM_LOAD( "rom0d.bin",    0x034000, 0x004000, CRC(3bb1dc8e) SHA1(ee9831e07f028b37b9acdb807becafccfb15d583) )
ROM_END

ROM_START( pcg850v )
	ROM_REGION( 0x58000, "user1", ROMREGION_ERASEFF )
	ROM_LOAD( "rom00.bin", 0x00000, 0x4000, CRC(c41a7a3e) SHA1(8b85d07e6f2fa048f8a958c261ab3344df750bd2))
	ROM_LOAD( "rom01.bin", 0x04000, 0x4000, CRC(45cafcaf) SHA1(e6142ac2ddb50c90bf1afd03ee2d2741abde8b76))
	ROM_LOAD( "rom02.bin", 0x08000, 0x4000, CRC(c81a804e) SHA1(322b9c6ca5cb7bc41a55c7874838e601ef50644e))
	ROM_LOAD( "rom03.bin", 0x0c000, 0x4000, CRC(9301e937) SHA1(b152186577abf86a7fb535cd63985f2a67e154b9))
	ROM_LOAD( "rom04.bin", 0x10000, 0x4000, CRC(6bf11755) SHA1(fe7458758d26fabfcca34e791fe2425104830b7b))
	ROM_LOAD( "rom05.bin", 0x14000, 0x4000, CRC(8a808f5e) SHA1(448caf0cc30de483d876c31785c8bb27305860b8))
	ROM_LOAD( "rom06.bin", 0x18000, 0x4000, CRC(3902f135) SHA1(40fbd51718a830e3f374843990522e29508376fd))
	ROM_LOAD( "rom07.bin", 0x1c000, 0x4000, CRC(618a7be8) SHA1(5f16e47eeeb9c153bb95c6d6e3af276576b8274e))
	ROM_LOAD( "rom08.bin", 0x20000, 0x4000, CRC(bf05b34d) SHA1(8556d64ddd89191c9dc048280e384f9146d81a16))
	ROM_LOAD( "rom09.bin", 0x24000, 0x4000, CRC(b75dbd0a) SHA1(ca149134a819fa52591ae1733270f8b3c811a18a))
	ROM_LOAD( "rom0a.bin", 0x28000, 0x4000, CRC(e7c5ec8f) SHA1(70cac20d4f01b5b2fdd76837659bc4947a1ebd21))
	ROM_LOAD( "rom0b.bin", 0x2c000, 0x4000, CRC(bdc4f889) SHA1(154d6223e8e750fcbffdb069215fee9492685747))
	ROM_LOAD( "rom0c.bin", 0x30000, 0x4000, CRC(6ef5560a) SHA1(5b2ad8e36354f448fe01acba1d2f69630de345a7))
	ROM_LOAD( "rom0d.bin", 0x34000, 0x4000, CRC(4295a44e) SHA1(0f8dfc759ed17f2b0acc1dc4912e881b1f0f65aa))
	ROM_LOAD( "rom0e.bin", 0x38000, 0x4000, CRC(0c8212e1) SHA1(8145728d87ea5a5e29c0e98c3d2d4278b1070359))
	ROM_LOAD( "rom0f.bin", 0x3c000, 0x4000, CRC(33c30dba) SHA1(296fb6fa3921822c3458acde0962c3a25cb19a1e))
	ROM_LOAD( "rom10.bin", 0x40000, 0x4000, CRC(553a0926) SHA1(2149827f9a2eb879756b3da609e17fc5b1c9bd78))
	ROM_LOAD( "rom11.bin", 0x44000, 0x4000, CRC(385a128d) SHA1(eda1192a75e4cfb50c0ac058f230a84cbfe67d51))
	ROM_LOAD( "rom12.bin", 0x48000, 0x4000, CRC(cd12dfca) SHA1(50a5defdeb4da9b2eac196816559056fc55e1dca))
	ROM_LOAD( "rom13.bin", 0x4c000, 0x4000, CRC(9bca873b) SHA1(4ce43553fb15c0ca866e9582a3ef8dc22a2795b4))
	ROM_LOAD( "rom14.bin", 0x50000, 0x4000, CRC(a2938c40) SHA1(d2c24401eea5e56268ef6eadcc612c8bbaa3342a))
	ROM_LOAD( "rom15.bin", 0x54000, 0x4000, CRC(53a0bf0a) SHA1(bf27baeaf208628fbe3c959d623cc08de21cf9f8))
ROM_END

} // anonymous namespace

/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY  FULLNAME    FLAGS
COMP( 1991, pce220,  0,      0,      pce220,  pce220,  pce220_state,  empty_init, "Sharp", "PC-E220",  MACHINE_SUPPORTS_SAVE )
COMP( 1992, pcg815,  0,      0,      pcg815,  pcg850v, pcg815_state,  empty_init, "Sharp", "PC-G815",  MACHINE_SUPPORTS_SAVE )
COMP( 2001, pcg850v, 0,      0,      pcg850v, pcg850v, pcg850v_state, empty_init, "Sharp", "PC-G850V", MACHINE_NOT_WORKING )



pcfx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/**************************************************************************************************

  pcfx.cpp

  Driver file to handle emulation of the NEC PC-FX.

**************************************************************************************************/


#include "emu.h"
#include "cpu/v810/v810.h"
#include "sound/huc6230.h"
#include "video/huc6261.h"
#include "video/huc6270.h"
#include "video/huc6271.h"
#include "video/huc6272.h"
#include "screen.h"
#include "speaker.h"


namespace {

// TODO: should really inherit from a common pcfx_motherboard_device
// - pcfxga needs to be exposed as a ISA16 and C-Bus boards, it's not a real stand-alone driver.
class pcfx_state : public driver_device
{
public:
	pcfx_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_huc6261(*this, "huc6261"),
		m_pads(*this, "P%u", 1U) { }

	void pcfx(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	uint16_t irq_read(offs_t offset);
	void irq_write(offs_t offset, uint16_t data);
	uint16_t pad_r(offs_t offset);
	void pad_w(offs_t offset, uint16_t data);
	[[maybe_unused]] uint8_t extio_r(offs_t offset);
	[[maybe_unused]] void extio_w(offs_t offset, uint8_t data);

	[[maybe_unused]] void irq8_w(int state);
	[[maybe_unused]] void irq9_w(int state);
	[[maybe_unused]] void irq10_w(int state);
	[[maybe_unused]] void irq11_w(int state);
	void irq12_w(int state);
	void irq13_w(int state);
	void irq14_w(int state);
	[[maybe_unused]] void irq15_w(int state);
	template <int Pad> TIMER_CALLBACK_MEMBER(pad_func);

	void pcfx_io(address_map &map) ATTR_COLD;
	void pcfx_mem(address_map &map) ATTR_COLD;

	// Interrupt controller (component unknown)
	uint16_t m_irq_mask = 0;
	uint16_t m_irq_pending = 0;
	uint8_t m_irq_priority[8]{};

	struct pcfx_pad_t
	{
		uint8_t ctrl[2]{};
		uint8_t status[2]{};
		uint32_t latch[2]{};
	};

	pcfx_pad_t m_pad;
	emu_timer *m_pad_timers[2];

	inline void check_irqs();
	inline void set_irq_line(int line, int state);

	required_device<cpu_device> m_maincpu;
	required_device<huc6261_device> m_huc6261;
	required_ioport_array<2> m_pads;
};


uint8_t pcfx_state::extio_r(offs_t offset)
{
	address_space &io_space = m_maincpu->space(AS_IO);

	return io_space.read_byte(offset);
}

void pcfx_state::extio_w(offs_t offset, uint8_t data)
{
	address_space &io_space = m_maincpu->space(AS_IO);

	io_space.write_byte(offset, data);
}

uint16_t pcfx_state::pad_r(offs_t offset)
{
	uint16_t res;
	uint8_t port_type = ((offset<<1) & 0x80) >> 7;

	if(((offset<<1) & 0x40) == 0)
	{
		// status
		/*
		---- x---
		---- ---x incoming data state (0=available)
		*/
		res = m_pad.status[port_type];
		//printf("STATUS %d\n",port_type);
	}
	else
	{
		// received data
		res = m_pad.latch[port_type] >> (((offset<<1) & 2) ? 16 : 0);

		if(((offset<<1) & 0x02) == 0)
		{
			m_pad.status[port_type] &= ~8; // clear latch on LSB read according to docs
			set_irq_line(11, 0);
		}
	}

	return res;
}

template <int Pad>
TIMER_CALLBACK_MEMBER(pcfx_state::pad_func)
{
	m_pad.latch[Pad] = m_pads[Pad]->read();
	m_pad.status[Pad] |= 8;
	m_pad.ctrl[Pad] &= ~1; // ack TX line
	// TODO: pad IRQ
	set_irq_line(11, 1);
}

void pcfx_state::pad_w(offs_t offset, uint16_t data)
{
	uint8_t port_type = ((offset<<1) & 0x80) >> 7;

	if(((offset<<1) & 0x40) == 0)
	{
		// control
		/*
		---- -x-- receiver enable
		---- --x- enable multi-tap
		---- ---x enable send (0->1 transition)
		*/
		if(data & 1 && (!(m_pad.ctrl[port_type] & 1)))
		{
			m_pad_timers[port_type]->adjust(attotime::from_usec(100)); // TODO: time
		}

		m_pad.ctrl[port_type] = data & 7;
		//printf("%04x CONTROL %d\n",data,port_type);
	}
	else
	{
		// transmitted data
		//printf("%04x TX %d\n",data,port_type);
	}
}

void pcfx_state::pcfx_mem(address_map &map)
{
	map(0x00000000, 0x001FFFFF).ram();   /* RAM */
//  map(0x80000000, 0x80FFFFFF).rw(FUNC(pcfx_state::extio_r), FUNC(pcfx_state::extio_w));    /* EXTIO */
//  map(0x80700000, 0x807FFFFF).rom().region("scsi_rom", 0); // EXTIO ROM area
	map(0xE0000000, 0xE7FFFFFF).noprw();   /* BackUp RAM */
	map(0xE8000000, 0xE9FFFFFF).noprw();   /* Extended BackUp RAM */
//  map(0xF8000000, 0xF8000007).noprw();   /* PIO, needed by pcfxga for reading backup "RAM" from DOS/V host */
	map(0xFFF00000, 0xFFFFFFFF).rom().region("ipl", 0);  /* ROM */
}

void pcfx_state::pcfx_io(address_map &map)
{
	map(0x00000000, 0x000000FF).rw(FUNC(pcfx_state::pad_r), FUNC(pcfx_state::pad_w)); /* PAD */
	map(0x00000100, 0x000001FF).w("huc6230", FUNC(huc6230_device::write)).umask32(0x00ff00ff);   /* HuC6230 */
	map(0x00000200, 0x000002FF).m("huc6271", FUNC(huc6271_device::regs)).umask32(0x0000ffff);   /* HuC6271 */
	map(0x00000300, 0x000003FF).rw(m_huc6261, FUNC(huc6261_device::read), FUNC(huc6261_device::write)).umask32(0x0000ffff);  /* HuC6261 */
	map(0x00000400, 0x000004FF).rw("huc6270_a", FUNC(huc6270_device::read), FUNC(huc6270_device::write)).umask32(0x0000ffff); /* HuC6270-A */
	map(0x00000500, 0x000005FF).rw("huc6270_b", FUNC(huc6270_device::read), FUNC(huc6270_device::write)).umask32(0x0000ffff); /* HuC6270-B */
	map(0x00000600, 0x00000607).mirror(0xf8).m("huc6272", FUNC(huc6272_device::amap)); // King
//  map(0x00000C80, 0x00000C83).noprw(); // backup RAM control
	map(0x00000E00, 0x00000EFF).rw(FUNC(pcfx_state::irq_read), FUNC(pcfx_state::irq_write)).umask32(0x0000ffff);    /* Interrupt controller */
//  map(0x00000F00, 0x00000FFF).noprw(); // Timer
//  map(0x00600000, 0x006FFFFF).r(FUNC(pcfx_state::scsi_ctrl_r)); // SCSI control for EXTIO
//  map(0x00700000, 0x007FFFFF).rom().region("scsi_rom", 0); // EXTIO ROM area
	map(0x80500000, 0x805000FF).noprw(); // pcfxga Kubota/Hudson HuC6273 "Aurora" 3d controller
}


static INPUT_PORTS_START( pcfx )
	/*
	xxxx ---- ---- ---- ID (0xf = 6 button pad, 0xe = multitap, 0xd = mouse, 0 = none)
	*/
	PORT_START("P1")
	PORT_BIT( 0xf0000000, IP_ACTIVE_LOW, IPT_UNKNOWN ) // ID pad
	// pcfxga expects latches to go active high on main menu
	PORT_BIT( 0x00000001, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("P1 Button I")
	PORT_BIT( 0x00000002, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("P1 Button II")
	PORT_BIT( 0x00000004, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("P1 Button III")
	PORT_BIT( 0x00000008, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("P1 Button IV")
	PORT_BIT( 0x00000010, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_PLAYER(1) PORT_NAME("P1 Button V")
	PORT_BIT( 0x00000020, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_PLAYER(1) PORT_NAME("P1 Button VI")
	PORT_BIT( 0x00000040, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1) PORT_NAME("P1 Select Button")
	PORT_BIT( 0x00000080, IP_ACTIVE_HIGH, IPT_START1 ) PORT_PLAYER(1) PORT_NAME("P1 RUN Button")
	PORT_BIT( 0x00000100, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_NAME("P1 Up")
	PORT_BIT( 0x00000200, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("P1 Right")
	PORT_BIT( 0x00000400, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_NAME("P1 Down")
	PORT_BIT( 0x00000800, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_NAME("P1 Left")
	PORT_BIT( 0x00001000, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_PLAYER(1) PORT_NAME("P1 Switch 1")
	PORT_BIT( 0x00004000, IP_ACTIVE_HIGH, IPT_BUTTON8 ) PORT_PLAYER(1) PORT_NAME("P1 Switch 2")
	PORT_BIT( 0x0fffa000, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0xf0000000, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // ID unconnect
	PORT_BIT( 0x0fffffff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


uint16_t pcfx_state::irq_read(offs_t offset)
{
	uint16_t data = 0;

	switch( offset )
	{
		// Interrupts pending
		// Same bit order as mask
		case 0x00/4:
			data = m_irq_pending;
			break;

		// Interrupt mask
		case 0x40/4:
			data = m_irq_mask;
			break;

		// Interrupt priority 0
		case 0x80/4:
			data = m_irq_priority[4] | ( m_irq_priority[5] << 3 ) | ( m_irq_priority[6] << 6 ) | ( m_irq_priority[7] << 9 );
			break;

		// Interrupt priority 1
		case 0xC0/4:
			data = m_irq_priority[0] | ( m_irq_priority[1] << 3 ) | ( m_irq_priority[2] << 6 ) | ( m_irq_priority[3] << 9 );
			break;
	}

	return data;
}


void pcfx_state::irq_write(offs_t offset, uint16_t data)
{
	switch( offset )
	{
		// Interrupts pending
		case 0x00/4:
			logerror("irq_write: Attempt to write to irq pending register\n");
			break;

		// Interrupt mask
		// --------x------- Mask interrupt level 8  (Unknown)
		// ---------x------ Mask interrupt level 9  (Timer)
		// ----------x----- Mask interrupt level 10 (Unknown)
		// -----------x---- Mask interrupt level 11 (Pad)
		// ------------x--- Mask interrupt level 12 (HuC6270-A)
		// -------------x-- Mask interrupt level 13 (HuC6272)
		// --------------x- Mask interrupt level 14 (HuC6270-B)
		// ---------------x Mask interrupt level 15 (HuC6273)
		// 0 - allow, 1 - ignore interrupt
		case 0x40/4:
			m_irq_mask = data;
			check_irqs();
			break;

		// Interrupt priority 0
		// ----xxx--------- Priority level interrupt 12
		// -------xxx------ Priority level interrupt 13
		// ----------xxx--- Priority level interrupt 14
		// -------------xxx Priority level interrupt 15
		case 0x80/4:
			m_irq_priority[7] = ( data >> 0 ) & 0x07;
			m_irq_priority[6] = ( data >> 3 ) & 0x07;
			m_irq_priority[5] = ( data >> 6 ) & 0x07;
			m_irq_priority[4] = ( data >> 9 ) & 0x07;
			check_irqs();
			break;

		// Interrupt priority 1
		// ----xxx--------- Priority level interrupt 8
		// -------xxx------ Priority level interrupt 9
		// ----------xxx--- Priority level interrupt 10
		// -------------xxx Priority level interrupt 11
		case 0xC0/4:
			m_irq_priority[3] = ( data >> 0 ) & 0x07;
			m_irq_priority[2] = ( data >> 3 ) & 0x07;
			m_irq_priority[1] = ( data >> 6 ) & 0x07;
			m_irq_priority[0] = ( data >> 9 ) & 0x07;
			check_irqs();
			break;
	}
}


inline void pcfx_state::check_irqs()
{
	uint16_t active_irqs = m_irq_pending & ~m_irq_mask;
	int highest_prio = -1;

	for (auto & elem : m_irq_priority)
	{
		if ( active_irqs & 0x80 )
		{
			if ( elem >= highest_prio )
			{
				highest_prio = elem;
			}
		}
		active_irqs <<= 1;
	}

	if ( highest_prio >= 0 )
	{
		m_maincpu->set_input_line(8 + highest_prio, ASSERT_LINE );
	}
	else
	{
		m_maincpu->set_input_line(0, CLEAR_LINE );
	}
}


inline void pcfx_state::set_irq_line(int line, int state)
{
	if ( state )
	{
//printf("Setting irq line %d\n", line);
		m_irq_pending |= ( 1 << ( 15 - line ) );
	}
	else
	{
//printf("Clearing irq line %d\n", line);
		m_irq_pending &= ~( 1 << ( 15 - line ) );
	}
	check_irqs();
}

void pcfx_state::irq8_w(int state)
{
	set_irq_line(8, state);
}

void pcfx_state::irq9_w(int state)
{
	set_irq_line(9, state);
}

void pcfx_state::irq10_w(int state)
{
	set_irq_line(10, state);
}

void pcfx_state::irq11_w(int state)
{
	set_irq_line(11, state);
}

void pcfx_state::irq12_w(int state)
{
	set_irq_line(12, state);
}

void pcfx_state::irq13_w(int state)
{
	set_irq_line(13, state);
}

void pcfx_state::irq14_w(int state)
{
	set_irq_line(14, state);
}

void pcfx_state::irq15_w(int state)
{
	set_irq_line(15, state);
}


void pcfx_state::machine_start()
{
	for (int i = 0; i < 2; i++)
	{
		m_pad.ctrl[i] = 0;
		m_pad.status[i] = 0;
		m_pad.latch[i] = 0;
	};

	m_pad_timers[0] = timer_alloc(FUNC(pcfx_state::pad_func<0>), this);
	m_pad_timers[1] = timer_alloc(FUNC(pcfx_state::pad_func<1>), this);
}

void pcfx_state::machine_reset()
{
	m_irq_mask = 0xFF;
	m_irq_pending = 0;
}


uint32_t pcfx_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	m_huc6261->video_update( bitmap, cliprect );
	return 0;
}


void pcfx_state::pcfx(machine_config &config)
{
	V810(config, m_maincpu, XTAL(21'477'272));
	m_maincpu->set_addrmap(AS_PROGRAM, &pcfx_state::pcfx_mem);
	m_maincpu->set_addrmap(AS_IO, &pcfx_state::pcfx_io);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_screen_update(FUNC(pcfx_state::screen_update));
	screen.set_raw(XTAL(21'477'272), huc6261_device::WPF, 64, 64 + 1024 + 64, huc6261_device::LPF, 18, 18 + 242);

	huc6270_device &huc6270_a(HUC6270(config, "huc6270_a", 0));
	huc6270_a.set_vram_size(0x20000);
	huc6270_a.irq().set(FUNC(pcfx_state::irq12_w));

	huc6270_device &huc6270_b(HUC6270(config, "huc6270_b", 0));
	huc6270_b.set_vram_size(0x20000);
	huc6270_b.irq().set(FUNC(pcfx_state::irq14_w));

	HUC6261(config, m_huc6261, XTAL(21'477'272));
	m_huc6261->set_vdc1_tag("huc6270_a");
	m_huc6261->set_vdc2_tag("huc6270_b");
	m_huc6261->set_king_tag("huc6272");

	huc6272_device &huc6272(HUC6272(config, "huc6272", XTAL(21'477'272)));
	huc6272.irq_changed_callback().set(FUNC(pcfx_state::irq13_w));
	huc6272.set_rainbow_tag("huc6271");

	HUC6271(config, "huc6271", XTAL(21'477'272));

	SOFTWARE_LIST(config, "cd_list").set_original("pcfx");

	/* sound hardware */
	SPEAKER(config, "speaker", 2).front();

	huc6230_device &huc6230(HuC6230(config, "huc6230", XTAL(21'477'272)));
	huc6230.adpcm_update_cb<0>().set("huc6272", FUNC(huc6272_device::adpcm_update_0));
	huc6230.adpcm_update_cb<1>().set("huc6272", FUNC(huc6272_device::adpcm_update_1));
	huc6230.vca_callback().set("huc6272", FUNC(huc6272_device::cdda_update));
	huc6230.add_route(0, "speaker", 1.0, 0);
	huc6230.add_route(1, "speaker", 1.0, 1);
}


ROM_START( pcfx )
	ROM_REGION32_LE( 0x100000, "ipl", 0 )
	ROM_SYSTEM_BIOS( 0, "v100", "BIOS v1.00 - 2 Sep 1994" )
	ROMX_LOAD( "pcfxbios.bin", 0x000000, 0x100000, CRC(76ffb97a) SHA1(1a77fd83e337f906aecab27a1604db064cf10074), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v101", "BIOS v1.01 - 5 Dec 1994" )
	ROMX_LOAD( "pcfxv101.bin", 0x000000, 0x100000, CRC(236102c9) SHA1(8b662f7548078be52a871565e19511ccca28c5c8), ROM_BIOS(1) )

	ROM_REGION32_LE( 0x100000, "scsi_rom", ROMREGION_ERASEFF )
	// TODO: "PC-FX EXTIO Boot", really belongs to FX-SCSI PC-FX expansion board
	// r/w to I/O $600000 SCSI if ROM enabled in both memory and I/O areas,
	// allows PC-FX to act as a CD drive for a PC-98 host ...
	ROM_LOAD( "fx-scsi.rom", 0x00000, 0x80000, CRC(f3e60e5e) SHA1(65482a23ac5c10a6095aee1db5824cca54ead6e5) )
	ROM_RELOAD( 0x80000, 0x80000 )
ROM_END


ROM_START( pcfxga )
	ROM_REGION32_LE( 0x100000, "ipl", 0 )
	ROM_LOAD( "pcfxga.rom", 0x000000, 0x100000, CRC(41c3776b) SHA1(a9372202a5db302064c994fcda9b24d29bb1b41c) )

	ROM_REGION32_LE( 0x100000, "scsi_rom", ROMREGION_ERASEFF )
ROM_END

} // Anonymous namespace


/***************************************************************************

  Game driver(s)

***************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY  FULLNAME                  FLAGS
CONS( 1994, pcfx,   0,      0,      pcfx,    pcfx,  pcfx_state, empty_init, "NEC",   "PC-FX",                  MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
CONS( 199?, pcfxga, pcfx,   0,      pcfx,    pcfx,  pcfx_state, empty_init, "NEC",   "PC-FX/GA (PC ISA Card)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



pchess.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

CXG Pocketchess (model 219)

It says "Pocket Chess" on the front of the handheld itself, "Pocketchess" on the
backside label, manual, box, and advertising.

TODO:
- save switch does not work since MCU emulation doesn't support NVRAM

Hardware notes:
- PCB label: CXG 219 600 001
- Hitachi HD44820 @ ~400kHz (100K resistor on Fidelity MCC)
- LCD with 4 7segs and custom segments, piezo

HD44820B63 MCU is used in:
- CXG Pocketchess
- Fidelity Micro Chess Challenger (12 buttons) (Fidelity brand Pocketchess)

*******************************************************************************/

#include "emu.h"

#include "cpu/hmcs40/hmcs40.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "cxg_pchess.lh"


namespace {

class pchess_state : public driver_device
{
public:
	pchess_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void pchess(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<hmcs40_cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<4> m_inputs;

	u8 m_inp_mux = 0;
	u8 m_lcd_com = 0;
	u32 m_lcd_segs = 0;

	// I/O handlers
	void update_lcd();
	template<int N> void seg_w(u8 data);
	u16 input_r();
	void control_w(u16 data);
};

void pchess_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_lcd_com));
	save_item(NAME(m_lcd_segs));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void pchess_state::update_lcd()
{
	// LCD common is analog (voltage level)
	const u8 com = population_count_32(m_lcd_com & 3);
	const u64 data = (com == 0) ? m_lcd_segs : (com == 2) ? ~m_lcd_segs : 0;
	m_display->write_row(0, data);
}

template<int N>
void pchess_state::seg_w(u8 data)
{
	// R0x-R6x: LCD segments
	const u8 shift = N * 4;
	m_lcd_segs = (m_lcd_segs & ~(0xf << shift)) | (data << shift);
	update_lcd();
}

u16 pchess_state::input_r()
{
	u16 data = 0;

	// D13-D15: read buttons
	for (int i = 0; i < 4; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read() << 13;

	return ~data;
}

void pchess_state::control_w(u16 data)
{
	// D1: speaker out
	m_dac->write(BIT(data, 1));

	// D3,D4: LCD common
	m_lcd_com = data >> 3 & 3;

	// D7,D8: 2 more LCD segments
	const u32 mask = 3 << 28;
	m_lcd_segs = (m_lcd_segs & ~mask) | (data << 21 & mask);
	update_lcd();

	// D9-D12: input mux
	m_inp_mux = ~data >> 9 & 0xf;
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( pchess )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("A1")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("E5")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("LV")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("B2")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("F6")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("MO")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("C3")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("G7")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("EN")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("D4")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("H8")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void pchess_state::pchess(machine_config &config)
{
	// basic machine hardware
	HD44820(config, m_maincpu, 400'000); // approximation
	m_maincpu->write_r<0>().set(FUNC(pchess_state::seg_w<0>));
	m_maincpu->write_r<1>().set(FUNC(pchess_state::seg_w<1>));
	m_maincpu->write_r<2>().set(FUNC(pchess_state::seg_w<2>));
	m_maincpu->write_r<3>().set(FUNC(pchess_state::seg_w<3>));
	m_maincpu->write_r<4>().set(FUNC(pchess_state::seg_w<4>));
	m_maincpu->write_r<5>().set(FUNC(pchess_state::seg_w<5>));
	m_maincpu->write_r<6>().set(FUNC(pchess_state::seg_w<6>));
	m_maincpu->write_d().set(FUNC(pchess_state::control_w));
	m_maincpu->read_d().set(FUNC(pchess_state::input_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 30);
	m_display->set_bri_levels(0.05);
	config.set_default_layout(layout_cxg_pchess);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/4, 914/4);
	screen.set_visarea_full();

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( pchess )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD("1985_white_and_allcock_hd44820b63", 0x0000, 0x2000, CRC(8decfb8f) SHA1(ac216663fe72cc98607ce44c033bc4b13b309ad1) )

	ROM_REGION( 57412, "screen", 0 )
	ROM_LOAD("pchess.svg", 0, 57412, CRC(7859b1ac) SHA1(518c5cd08fa8562628345e8e28048c01c9e4edd6) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1986, pchess, 0,      0,      pchess,  pchess, pchess_state, empty_init, "CXG Systems / Newcrest Technology / Intelligent Chess Software", "Pocketchess (CXG)", MACHINE_SUPPORTS_SAVE )



pcipc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert

/*
  Sandbox experiment on a new-pci pc

  Virtual hardware:
  - A pentium as main CPU
  - A Micronics M55HI-Plus PCI/ISA motherboard without integrated sound
  -> intel 430hx, aka 82439hx northbridge (pci, no agp)
  -> intel piix3, aka 82371sb southbridge (pci-isa bridge, ide, ioapic, timer, irq, dma, usb)
  -> smsc fdc37c93x superio (isa-connected, keyboard, rtc, fdc, rs232, ide)
  - A Matrox Millennium PCI video card

  We'll see about sound, networking, etc later

*/


#include "emu.h"

#include "bus/isa/isa_cards.h"
#include "bus/pci/pci_slot.h"
#include "bus/rs232/hlemouse.h"
#include "bus/rs232/null_modem.h"
#include "bus/rs232/rs232.h"
#include "bus/rs232/sun_kbd.h"
#include "bus/rs232/terminal.h"
#include "cpu/i386/i386.h"
#include "machine/fdc37c93x.h"
#include "machine/i82371eb_acpi.h"
#include "machine/i82371eb_ide.h"
#include "machine/i82371eb_isa.h"
#include "machine/i82371eb_usb.h"
#include "machine/i82371sb.h"
#include "machine/i82439hx.h"
#include "machine/i82439tx.h"
#include "machine/i82443bx_host.h"
#include "machine/pci.h"
#include "machine/pci-ide.h"
#include "machine/w83977tf.h"

#include "softlist_dev.h"

// enable ISA verbose messaging at I/O $80
// NOTE: xubuntu 6.10 will ping the port a lot once it gets to GNOME.
#define VERBOSE_ISA_DEBUG 0

namespace {

class pcipc_state : public driver_device
{
public:
	struct boot_state_info {
		uint8_t val;
		const char *const message;
	};

	static const boot_state_info boot_state_infos_phoenix[];
	static const boot_state_info boot_state_infos_phoenix_ver40_rev6[];
	static const boot_state_info boot_state_infos_award[];

	void pcipc(machine_config &config);
	void pcipcs7(machine_config &config);
	void pcipctx(machine_config &config);
	void pcinv3(machine_config &config);
	void pciagp(machine_config &config);

	pcipc_state(const machine_config &mconfig, device_type type, const char *tag);

protected:
	void x86_softlists(machine_config &config);

private:
	void pcipc_map(address_map &map) ATTR_COLD;
	void pcipc_map_io(address_map &map) ATTR_COLD;
	[[maybe_unused]] void boot_state_phoenix_w(uint8_t data);
	void boot_state_phoenix_ver40_rev6_w(uint8_t data);
	void boot_state_award_w(uint8_t data);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	static void smc_superio_config(device_t *device);
	static void winbond_superio_config(device_t *device);
};

pcipc_state::pcipc_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag)
{
}

void pcipc_state::machine_start()
{
}

void pcipc_state::machine_reset()
{
}

const pcipc_state::boot_state_info pcipc_state::boot_state_infos_phoenix[] = {
	{ 0x02, "Verify Real Mode." },
	{ 0x04, "Get CPU type." },
	{ 0x06, "Initialize system hardware." },
	{ 0x08, "Initialize chipset registers with initial POST values." },
	{ 0x09, "Get in POST Reg." },
	{ 0x0A, "Initialize CPU registers." },
	{ 0x0C, "Initialize cache initial POST values." },
	{ 0x0E, "Initialize I/O." },
	{ 0x0F, "Initialize the localbus IDE." },
	{ 0x10, "Initialize Power Management." },
	{ 0x11, "Load alternate registers with initial POST values." },
	{ 0x12, "Jump to UserPatch0." },
	{ 0x14, "Initialize keyboard controller." },
	{ 0x16, "BIOS ROM checksum." },
	{ 0x18, "8254 timer initialization." },
	{ 0x1A, "8237 DMA controller initialization." },
	{ 0x1C, "Reset Programmable Interrupt Controller." },
	{ 0x20, "Test DRAM refresh." },
	{ 0x22, "Test 8742 Keyboard Controller." },
	{ 0x24, "Set ES segment register to 4 GB." },
	{ 0x28, "Autosize DRAM." },
	{ 0x2A, "Clear 512K base RAM." },
	{ 0x2C, "Test 512K base address lines." },
	{ 0x2E, "Test 512K base memory." },
	{ 0x32, "Test CPU bus-clock frequency." },
	{ 0x34, "Test CMOS RAM." },
	{ 0x35, "Initialize alternate chipset registers." },
	{ 0x37, "Reinitialize the chipset (MB only)." },
	{ 0x38, "Shadow system BIOS ROM." },
	{ 0x39, "Reinitialize the cache (MB only)." },
	{ 0x3A, "Autosize cache." },
	{ 0x3C, "Configure advanced chipset registers." },
	{ 0x3D, "Load alternate registers with CMOS values." },
	{ 0x40, "Set initial CPU speed." },
	{ 0x42, "Initialize interrupt vectors." },
	{ 0x44, "Initialize BIOS interrupts." },
	{ 0x46, "Check ROM copyright notice." },
	{ 0x47, "Initialize manager for PCI Option ROMs." },
	{ 0x48, "Check video configuration against CMOS." },
	{ 0x49, "Initialize PCI bus and devices." },
	{ 0x4A, "Initialize all video adapters in system." },
	{ 0x4C, "Shadow video BIOS ROM." },
	{ 0x4E, "Display copyright notice." },
	{ 0x50, "Display CPU type and speed." },
	{ 0x51, "Initialize EISA board." },
	{ 0x52, "Test keyboard." },
	{ 0x54, "Set key click if enabled." },
	{ 0x56, "Enable keyboard." },
	{ 0x58, "Test for unexpected interrupts." },
	{ 0x5A, "Display prompt \"Press F2 to enter SETUP\"." },
	{ 0x5C, "Test RAM between 512 and 640k." },
	{ 0x60, "Test extended memory." },
	{ 0x62, "Test extended memory address lines." },
	{ 0x64, "Jump to UserPatch1." },
	{ 0x66, "Configure advanced cache registers." },
	{ 0x68, "Enable external and CPU caches." },
	{ 0x69, "Initialize SMI handler."},
	{ 0x6A, "Display external cache size." },
	{ 0x6C, "Display shadow message." },
	{ 0x6E, "Display non-disposable segments." },
	{ 0x70, "Display error messages." },
	{ 0x72, "Check for configuration errors." },
	{ 0x74, "Test real-time clock." },
	{ 0x76, "Check for keyboard errors." },
	{ 0x7C, "Set up hardware interrupt vectors." },
	{ 0x7E, "Test coprocessor if present." },
	{ 0x80, "Disable onboard I/O ports." },
	{ 0x82, "Detect and install external RS232 ports." },
	{ 0x84, "Detect and install external parallel ports." },
	{ 0x86, "Re-initialize on-board I/O ports." },
	{ 0x88, "Initialize BIOSData Area." },
	{ 0x8A, "Initialize Extended BIOS Data Area." },
	{ 0x8C, "Initialize floppy controller." },
	{ 0x90, "Initialize hard-disk controller." },
	{ 0x91, "Initialize localbus hard-disk controller." },
	{ 0x92, "Jump to UserPatch2." },
	{ 0x93, "Build MPTABLE for multi-processor boards." },
	{ 0x94, "Disable A20 address line." },
	{ 0x96, "Clear huge ES segment register." },
	{ 0x98, "Search for option ROMs." },
	{ 0x9A, "Shadow option ROMs." },
	{ 0x9C, "Set up Power Management." },
	{ 0x9E, "Enable hardware interrupts." },
	{ 0xA0, "Set time of day." },
	{ 0xA2, "Check key lock." },
	{ 0xA4, "Initialize typematic rate." },
	{ 0xA8, "Erase F2 prompt." },
	{ 0xAA, "Scan for F2 keystroke." },
	{ 0xAC, "Enter SETUP." },
	{ 0xAE, "Clear in-POST flag." },
	{ 0xB0, "Check for errors." },
	{ 0xB2, "POST done - prepare to boot operating system." },
	{ 0xB4, "One beep." },
	{ 0xB6, "Check password (optional)." },
	{ 0xB8, "Clear global descriptor table." },
	{ 0xBC, "Clear parity checkers." },
	{ 0xBE, "Clear screen (optional)." },
	{ 0xBF, "Check virus and backup reminders." },
	{ 0xC0, "Try to boot with INT 19." },
	{ 0xD0, "Interrupt handler error." },
	{ 0xD2, "Unknown interrupt error." },
	{ 0xD4, "Pending Interrupt." },
	{ 0xD6, "Initialize option ROM error." },
	{ 0xD8, "Shutdown error." },
	{ 0xDA, "Extended Block Move." },
	{ 0xDC, "Shutdown 10 error." },
	{ 0xE2, "Initialize the chipset." },
	{ 0xE3, "Initialize refresh counter." },
	{ 0xE4, "Check for Forced Flash." },
	{ 0xE5, "Check HW status of ROM." },
	{ 0xE6, "BIOS ROM is OK." },
	{ 0xE7, "Do a complete RAM test." },
	{ 0xE8, "Do OEM initialization." },
	{ 0xE9, "Initialize interrupt controller." },
	{ 0xEA, "Read in the bootstrap code." },
	{ 0xEB, "Initialize all vectors." },
	{ 0xEC, "Boot the Flash program." },
	{ 0xED, "Initialize the boot device." },
	{ 0xEE, "Boot code was read OK." },
	{ 0, nullptr }
};

const pcipc_state::boot_state_info pcipc_state::boot_state_infos_phoenix_ver40_rev6[] = {
	// Code, "(Beeps) POST Routine Description"
	{ 0x02, "Verify Real Mode." },
	{ 0x03, "Disable Non-Maskable Interrupt (NMI)." },
	{ 0x04, "Get CPU type." },
	{ 0x06, "Initialize system hardware." },
	{ 0x07, "Disable shadow and execute code from the ROM." },
	{ 0x08, "Initialize chipset with initial POST values." },
	{ 0x09, "Set IN POST flag." },
	{ 0x0A, "Initialize CPU registers." },
	{ 0x0B, "Enable CPU cache." },
	{ 0x0C, "Initialize caches to initial POST values." },
	{ 0x0E, "Initialize I/O component." },
	{ 0x0F, "Initialize the local bus IDE." },
	{ 0x10, "Initialize Power Management." },
	{ 0x11, "Load alternate registers with initial POST values." },
	{ 0x12, "Restore CPU control word during warm boot." },
	{ 0x13, "Initialize PCI Bus Mastering devices." },
	{ 0x14, "Initialize keyboard controller." },
	{ 0x16, "(1-2-2-3) BIOS ROM checksum." },
	{ 0x17, "Initialize cache before memory Auto size." },
	{ 0x18, "8254 timer initialization." },
	{ 0x1A, "8237 DMA controller initialization." },
	{ 0x1C, "Reset Programmable Interrupt Controller." },
	{ 0x20, "(1-3-1-1) Test DRAM refresh." },
	{ 0x22, "(1-3-1-3) Test 8742 Keyboard Controller." },
	{ 0x24, "Set ES segment register to 4 GB." },
	{ 0x28, "Auto size DRAM." },
	{ 0x29, "Initialize POST Memory Manager." },
	{ 0x2A, "Clear 512 kB base RAM." },
	{ 0x2C, "(1-3-4-1) RAM failure on address line xxxx*." },
	{ 0x2E, "(1-3-4-3) RAM failure on data bits xxxx* of low byte of memory bus." },
	{ 0x2F, "Enable cache before system BIOS shadow." },
	{ 0x32, "Test CPU bus-clock frequency." },
	{ 0x33, "Initialize Phoenix Dispatch Manager." },
	{ 0x36, "Warm start shut down." },
	{ 0x38, "Shadow system BIOS ROM." },
	{ 0x3A, "Auto size cache." },
	{ 0x3C, "Advanced configuration of chipset registers." },
	{ 0x3D, "Load alternate registers with CMOS values." },
	{ 0x41, "Initialize extended memory for RomPilot." },
	{ 0x42, "Initialize interrupt vectors." },
	{ 0x45, "POST device initialization." },
	{ 0x46, "(2-1-2-3) Check ROM copyright notice." },
	{ 0x47, "Initialize I20 support." },
	{ 0x48, "Check video configuration against CMOS." },
	{ 0x49, "Initialize PCI bus and devices." },
	{ 0x4A, "Initialize all video adapters in system." },
	{ 0x4B, "QuietBoot start (optional)." },
	{ 0x4C, "Shadow video BIOS ROM." },
	{ 0x4E, "Display BIOS copyright notice." },
	{ 0x4F, "Initialize MultiBoot." },
	{ 0x50, "Display CPU type and speed." },
	{ 0x51, "Initialize EISA board." },
	{ 0x52, "Test keyboard." },
	{ 0x54, "Set key click if enabled." },
	{ 0x55, "Enable USB devices." },
	{ 0x58, "(2-2-3-1) Test for unexpected interrupts." },
	{ 0x59, "Initialize POST display service." },
	{ 0x5A, "Display prompt 'Press F2 to enter SETUP'." },
	{ 0x5B, "Disable CPU cache." },
	{ 0x5C, "Test RAM between 512 and 640 kB." },
	{ 0x60, "Test extended memory." },
	{ 0x62, "Test extended memory address lines." },
	{ 0x64, "Jump to UserPatch1." },
	{ 0x66, "Configure advanced cache registers." },
	{ 0x67, "Initialize Multi Processor APIC." },
	{ 0x68, "Enable external and CPU caches." },
	{ 0x69, "Setup System Management Mode (SMM) area." },
	{ 0x6A, "Display external L2 cache size." },
	{ 0x6B, "Load custom defaults (optional)." },
	{ 0x6C, "Display shadow-area message." },
	{ 0x6E, "Display possible high address for UMB recovery." },
	{ 0x70, "Display error messages." },
	{ 0x72, "Check for configuration errors." },
	{ 0x76, "Check for keyboard errors." },
	{ 0x7C, "Set up hardware interrupt vectors." },
	{ 0x7D, "Initialize Intelligent System Monitoring." },
	{ 0x7E, "Initialize coprocessor if present." },
	{ 0x80, "Disable onboard Super I/O ports and IRQs." },
	{ 0x81, "Late POST device initialization." },
	{ 0x82, "Detect and install external RS232 ports." },
	{ 0x83, "Configure non-MCD IDE controllers." },
	{ 0x84, "Detect and install external parallel ports." },
	{ 0x85, "Initialize PC-compatible PnP ISA devices." },
	{ 0x86, "Re-initialize onboard I/O ports." },
	{ 0x87, "Configure Motherboard Configurable Devices (optional)." },
	{ 0x88, "Initialize BIOS Data Area." },
	{ 0x89, "Enable Non-Maskable Interrupts (NMIs)." },
	{ 0x8A, "Initialize Extended BIOS Data Area." },
	{ 0x8B, "Test and initialize PS/2 mouse." },
	{ 0x8C, "Initialize floppy controller." },
	{ 0x8F, "Determine number of ATA drives (optional)." },
	{ 0x90, "Initialize hard-disk controllers." },
	{ 0x91, "Initialize local-bus hard-disk controllers." },
	{ 0x92, "Jump to UserPatch2." },
	{ 0x93, "Build MPTABLE for multi-processor boards." },
	{ 0x95, "Install CD ROM for boot." },
	{ 0x96, "Clear huge ES segment register." },
	{ 0x97, "Fix up Multi Processor table." },
	{ 0x98, "(1-2) Search for option ROMs. One long, two short beeps on checksum failure." },
	{ 0x99, "Check for SMART Drive (optional)." },
	{ 0x9A, "Shadow option ROMs." },
	{ 0x9C, "Set up Power Management." },
	{ 0x9D, "Initialize security engine (optional)." },
	{ 0x9E, "Enable hardware interrupts." },
	{ 0x9F, "Determine number of ATA and SCSI drives." },
	{ 0xA0, "Set time of day." },
	{ 0xA2, "Check key lock." },
	{ 0xA4, "Initialize typematic rate." },
	{ 0xA8, "Erase F2 prompt." },
	{ 0xAA, "Scan for F2 key stroke." },
	{ 0xAC, "Enter SETUP." },
	{ 0xAE, "Clear Boot flag." },
	{ 0xB0, "Check for errors." },
	{ 0xB1, "Inform RomPilot about the end of POST." },
	{ 0xB2, "POST done - prepare to boot operating system." },
	{ 0xB4, "1 One short beep before boot." },
	{ 0xB5, "Terminate QuietBoot (optional)." },
	{ 0xB6, "Check password (optional)." },
	{ 0xB7, "Initialize ACPI BIOS." },
	{ 0xB9, "Prepare Boot." },
	{ 0xBA, "Initialize SMBIOS." },
	{ 0xBB, "Initialize PnP Option ROMs." },
	{ 0xBC, "Clear parity checkers." },
	{ 0xBD, "Display MultiBoot menu." },
	{ 0xBE, "Clear screen (optional)." },
	{ 0xBF, "Check virus and backup reminders." },
	{ 0xC0, "Try to boot with INT 19." },
	{ 0xC1, "Initialize POST Error Manager (PEM)." },
	{ 0xC2, "Initialize error logging." },
	{ 0xC3, "Initialize error display function." },
	{ 0xC4, "Initialize system error handler." },
	{ 0xC5, "PnPnd dual CMOS (optional)." },
	{ 0xC6, "Initialize note dock (optional)." },
	{ 0xC7, "Initialize note dock late." },
	{ 0xC8, "Force check (optional)." },
	{ 0xC9, "Extended checksum (optional)." },
	{ 0xCA, "Redirect Int 15h to enable remote keyboard." },
	{ 0xCB, "Redirect Int 13h to Memory Technologies Devices such as ROM, RAM, PCMCIA, and serial disk." },
	{ 0xCC, "Redirect Int 10h to enable remote serial video." },
	{ 0xCD, "Re-map I/O and memory for PCMCIA." },
	{ 0xCE, "Initialize digitizer and display message." },
	{ 0xD2, "Unknown interrupt." },
	// The following are for boot block in Flash ROM
	{ 0xE0, "Initialize the chipset." },
	{ 0xE1, "Initialize the bridge." },
	{ 0xE2, "Initialize the CPU." },
	{ 0xE3, "Initialize system timer." },
	{ 0xE4, "Initialize system I/O." },
	{ 0xE5, "Check force recovery boot." },
	{ 0xE6, "Checksum BIOS ROM." },
	{ 0xE7, "Go to BIOS." },
	{ 0xE8, "Set Huge Segment." },
	{ 0xE9, "Initialize Multi Processor." },
	{ 0xEA, "Initialize OEM special code." },
	{ 0xEB, "Initialize PIC and DMA." },
	{ 0xEC, "Initialize Memory type." },
	{ 0xED, "Initialize Memory size." },
	{ 0xEE, "Shadow Boot Block." },
	{ 0xEF, "System memory test." },
	{ 0xF0, "Initialize interrupt vectors." },
	{ 0xF1, "Initialize Run Time Clock." },
	{ 0xF2, "Initialize video." },
	{ 0xF3, "Initialize System Management Manager." },
	{ 0xF4, "Output one beep." },
	{ 0xF5, "Clear Huge Segment." },
	{ 0xF6, "Boot to Mini DOS." },
	{ 0xF7, "Boot to Full DOS." },
	{ 0, nullptr }
};

const pcipc_state::boot_state_info pcipc_state::boot_state_infos_award[] = {
	{0x01, "Processor test; Processor status verification" },
	{0x02, "Processor test 2; Read/Write and verify all CPU registers" },
	{0x03, "Initialize chips; Disable NMI, PIE, AIE, UEI, SQWV.  Disable video, parity checking, DMA.  Reset math coprocessor.  Clear all page registers and CMOS shutdown.  Initialize DMA controller 0 and 1.  Initialize interrupt controllers 0 and 1." },
	{0x04, "Test memory refresh toggle" },
	{0x05, "Blank video, initialize keyboard; Keyboard controller initialization" },
	{0x07, "Test CMOS interface and battery" },
	{0x08, "Set up low memory; Early chipset initialization, memory presence test, OEM chipset routines, clear low 64K memory, test first 64K memory" },
	{0x09, "Early cache initialization; Cyrix CPU specific, CPU and cache initialization" },
	{0x0A, "Set up interrupt vector table; Initialize first 120 interrupt vectors" },
	{0x0B, "Test CMOS RAM checksum" },
	{0x0C, "Initialize keyboard; Detect the type of keyboard controller" },
	{0x0D, "Initialize video interface; Detect CPU clock, read CMOS location 14h to find the type of video in use, detect and initialize video adapter" },
	{0x0E, "Test video memory; Write sign-on message to screen, setup shadow RAM" },
	{0x0F, "Test DMA controller 0; BIOS checksum test, keyboard detect and initialization" },
	{0x10, "Test DMA controller 1" },
	{0x11, "Test DMA page registers" },
	//{0x12-13, "Reserved" },
	{0x14, "Test timer counter 2" },
	{0x15, "Test 8259-1 mask bits" },
	{0x16, "Test 8259-2 mask bits" },
	{0x17, "Test stuck 8259 interrupt bits; Test stuck key" },
	{0x18, "Test 8259 interrupt functionality" },
	{0x19, "Test stuck NMI bits (parity I/O check)" },
	{0x1A, "Benchmark; Display CPU clock" },
	//{0x1B-1E, "Reserved" },
	{0x1F, "Set EISA mode; If the EISA memory checksum is good then EISA is initialized.  If it's not good then ISA tests and clear EISA mode flag" },
	{0x20, "Enable slot 0; System board" },
	//{0x21-2F, "Enable slots 1-15" },
	{0x30, "Size base and extended memory; Size the base memory from 256K to 640K and the extended memory above 1MB" },
	{0x31, "Test base and extended memory; Test the base memory from 256K to 640K and the extended memory above 1MB using various bit patterns" },
	{0x32, "Test EISA extended memory" },
	//{0x33-3B, "Reserved" },
	{0x3C, "Setup enabled" },
	{0x3D, "Initialize and install mouse if present" },
	{0x3E, "Setup cache controller" },
	{0x40, "Display virus protect disable or enable" },
	{0x41, "Initialize floppy" },
	{0x42, "Initialize hard drive" },
	{0x43, "Detect & Init. serial & parallel ports" },
	{0x44, "Reserved" },
	{0x45, "Detect and Init. math coprocessor" },
	{0x46, "Reserved" },
	{0x47, "Reserved" },
	//{0x48-4D, "Reserved" },
	{0x4E, "Mfg. POST loop, or display messages" },
	{0x4F, "Security password" },
	{0x50, "Write CMOS; Write CMOS back to RAM and clear screen" },
	{0x51, "Pre-boot enable; Enable parity checking, enable NMI, enable cache before boot" },
	{0x52, "Initialize option ROM's; Initialize and ROM's present at locations C800h to EFFFFh" },
	{0x53, "Initialize time value" },
	{0x60, "Setup virus protect" },
	{0x61, "Set boot speed" },
	{0x62, "Setup numlock" },
	{0x63, "Boot attempt" },
	{0xB0, "Spurious" },
	{0xB1, "Unclaimed NMI" },
	{0xBE, "Chipset default initialization; Program chipset registers and power-on BIOS defaults." },
	{0xBF, "Chipset initialization; Reserved" },
	{0xC0, "Turn off chipset cache" },
	{0xC1, "Memory presence test; OEM specific, test the size of on-board memory" },
	{0xC5, "Early shadow; OEM specific, early shadow enable for fast boot" },
	{0xC6, "Cache presence test; External cache-size detection test" },
	//{0xE1-EF, "Setup pages" },
	{0xFF, "Boot loader" },
	{ 0, nullptr }
};

void pcipc_state::boot_state_phoenix_w(uint8_t data)
{
#if VERBOSE_ISA_DEBUG
	const char *desc = "";
	for(int i=0; boot_state_infos_phoenix[i].message; i++)
		if(boot_state_infos_phoenix[i].val == data) {
			desc = boot_state_infos_phoenix[i].message;
			break;
		}
	logerror("Boot state %02x - %s\n", data, desc);
#endif
}

void pcipc_state::boot_state_phoenix_ver40_rev6_w(uint8_t data)
{
#if VERBOSE_ISA_DEBUG
	const char *desc = "";
	for(int i=0; boot_state_infos_phoenix_ver40_rev6[i].message; i++)
		if(boot_state_infos_phoenix_ver40_rev6[i].val == data) {
			desc = boot_state_infos_phoenix_ver40_rev6[i].message;
			break;
		}
	logerror("Boot state %02x - %s\n", data, desc);
#endif
}


void pcipc_state::boot_state_award_w(uint8_t data)
{
#if VERBOSE_ISA_DEBUG
	const char *desc = "";
	for(int i=0; boot_state_infos_award[i].message; i++)
		if(boot_state_infos_award[i].val == data) {
			desc = boot_state_infos_award[i].message;
			break;
		}
	logerror("Boot state %02x - %s\n", data, desc);
#endif
}

static void isa_internal_devices(device_slot_interface &device)
{
	device.option_add("fdc37c93x", FDC37C93X);
	device.option_add("w83977tf", W83977TF);
}

static void isa_com(device_slot_interface &device)
{
	device.option_add("microsoft_mouse", MSFT_HLE_SERIAL_MOUSE);
	device.option_add("logitech_mouse", LOGITECH_HLE_SERIAL_MOUSE);
	device.option_add("wheel_mouse", WHEEL_HLE_SERIAL_MOUSE);
	device.option_add("msystems_mouse", MSYSTEMS_HLE_SERIAL_MOUSE);
	device.option_add("rotatable_mouse", ROTATABLE_HLE_SERIAL_MOUSE);
	device.option_add("terminal", SERIAL_TERMINAL);
	device.option_add("null_modem", NULL_MODEM);
	device.option_add("sun_kbd", SUN_KBD_ADAPTOR);
}

void pcipc_state::smc_superio_config(device_t *device)
{
	fdc37c93x_device &fdc = *downcast<fdc37c93x_device *>(device);
	fdc.set_sysopt_pin(1);
	fdc.gp20_reset().set_inputline(":maincpu", INPUT_LINE_RESET);
	fdc.gp25_gatea20().set_inputline(":maincpu", INPUT_LINE_A20);
	fdc.irq1().set(":pci:07.0", FUNC(i82371sb_isa_device::pc_irq1_w));
	fdc.irq8().set(":pci:07.0", FUNC(i82371sb_isa_device::pc_irq8n_w));
	fdc.txd1().set(":serport0", FUNC(rs232_port_device::write_txd));
	fdc.ndtr1().set(":serport0", FUNC(rs232_port_device::write_dtr));
	fdc.nrts1().set(":serport0", FUNC(rs232_port_device::write_rts));
	fdc.txd2().set(":serport1", FUNC(rs232_port_device::write_txd));
	fdc.ndtr2().set(":serport1", FUNC(rs232_port_device::write_dtr));
	fdc.nrts2().set(":serport1", FUNC(rs232_port_device::write_rts));
}

void pcipc_state::winbond_superio_config(device_t *device)
{
	w83977tf_device &fdc = *downcast<w83977tf_device *>(device);
//  fdc.set_sysopt_pin(1);
	fdc.gp20_reset().set_inputline(":maincpu", INPUT_LINE_RESET);
	fdc.gp25_gatea20().set_inputline(":maincpu", INPUT_LINE_A20);
	fdc.irq1().set(":pci:07.0", FUNC(i82371eb_isa_device::pc_irq1_w));
	fdc.irq8().set(":pci:07.0", FUNC(i82371eb_isa_device::pc_irq8n_w));
//  fdc.txd1().set(":serport0", FUNC(rs232_port_device::write_txd));
//  fdc.ndtr1().set(":serport0", FUNC(rs232_port_device::write_dtr));
//  fdc.nrts1().set(":serport0", FUNC(rs232_port_device::write_rts));
//  fdc.txd2().set(":serport1", FUNC(rs232_port_device::write_txd));
//  fdc.ndtr2().set(":serport1", FUNC(rs232_port_device::write_dtr));
//  fdc.nrts2().set(":serport1", FUNC(rs232_port_device::write_rts));
}


void pcipc_state::pcipc_map(address_map &map)
{
	map.unmap_value_high();
}

void pcipc_state::pcipc_map_io(address_map &map)
{
	map.unmap_value_high();
}

void pcipc_state::x86_softlists(machine_config &config)
{
	/* software lists */
	SOFTWARE_LIST(config, "pc_disk_list").set_original("ibm5150");
	SOFTWARE_LIST(config, "at_disk_list").set_original("ibm5170");
	SOFTWARE_LIST(config, "at_cdrom_list").set_original("ibm5170_cdrom");
	SOFTWARE_LIST(config, "at_hdd_list").set_original("ibm5170_hdd");
	SOFTWARE_LIST(config, "midi_disk_list").set_compatible("midi_flop");
	SOFTWARE_LIST(config, "photocd_list").set_compatible("photo_cd");
}

void pcipc_state::pcipc(machine_config &config)
{
	pentium_device &maincpu(PENTIUM(config, "maincpu", 90000000));
	maincpu.set_addrmap(AS_PROGRAM, &pcipc_state::pcipc_map);
	maincpu.set_addrmap(AS_IO, &pcipc_state::pcipc_map_io);
	maincpu.set_irq_acknowledge_callback("pci:07.0:pic8259_master", FUNC(pic8259_device::inta_cb));
	maincpu.smiact().set("pci:00.0", FUNC(i82439hx_host_device::smi_act_w));

	PCI_ROOT(config, "pci", 0);
	I82439HX(config, "pci:00.0", 0, "maincpu", 256*1024*1024);

	i82371sb_isa_device &isa(I82371SB_ISA(config, "pci:07.0", 0, "maincpu"));
	isa.boot_state_hook().set(FUNC(pcipc_state::boot_state_phoenix_ver40_rev6_w));
	isa.smi().set_inputline("maincpu", INPUT_LINE_SMI);

	i82371sb_ide_device &ide(I82371SB_IDE(config, "pci:07.1", 0, "maincpu"));
	ide.irq_pri().set("pci:07.0", FUNC(i82371sb_isa_device::pc_irq14_w));
	ide.irq_sec().set("pci:07.0", FUNC(i82371sb_isa_device::pc_mirq0_w));

	PCI_SLOT(config, "pci:1", pci_cards, 15, 0, 1, 2, 3, nullptr);
	PCI_SLOT(config, "pci:2", pci_cards, 16, 1, 2, 3, 0, nullptr);
	PCI_SLOT(config, "pci:3", pci_cards, 17, 2, 3, 0, 1, nullptr);
	PCI_SLOT(config, "pci:4", pci_cards, 18, 3, 0, 1, 2, "virge");

	ISA16_SLOT(config, "board4", 0, "pci:07.0:isabus", isa_internal_devices, "fdc37c93x", true).set_option_machine_config("fdc37c93x", smc_superio_config);
	ISA16_SLOT(config, "isa1", 0, "pci:07.0:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa2", 0, "pci:07.0:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa3", 0, "pci:07.0:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa4", 0, "pci:07.0:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa5", 0, "pci:07.0:isabus", pc_isa16_cards, nullptr, false);

	rs232_port_device &serport0(RS232_PORT(config, "serport0", isa_com, nullptr));
	serport0.rxd_handler().set("board4:fdc37c93x", FUNC(fdc37c93x_device::rxd1_w));
	serport0.dcd_handler().set("board4:fdc37c93x", FUNC(fdc37c93x_device::ndcd1_w));
	serport0.dsr_handler().set("board4:fdc37c93x", FUNC(fdc37c93x_device::ndsr1_w));
	serport0.ri_handler().set("board4:fdc37c93x", FUNC(fdc37c93x_device::nri1_w));
	serport0.cts_handler().set("board4:fdc37c93x", FUNC(fdc37c93x_device::ncts1_w));

	rs232_port_device &serport1(RS232_PORT(config, "serport1", isa_com, nullptr));
	serport1.rxd_handler().set("board4:fdc37c93x", FUNC(fdc37c93x_device::rxd2_w));
	serport1.dcd_handler().set("board4:fdc37c93x", FUNC(fdc37c93x_device::ndcd2_w));
	serport1.dsr_handler().set("board4:fdc37c93x", FUNC(fdc37c93x_device::ndsr2_w));
	serport1.ri_handler().set("board4:fdc37c93x", FUNC(fdc37c93x_device::nri2_w));
	serport1.cts_handler().set("board4:fdc37c93x", FUNC(fdc37c93x_device::ncts2_w));

	//  SW1000XG(config, "pci:11.0");

	x86_softlists(config);
}

void pcipc_state::pcipcs7(machine_config &config)
{
	pcipc_state::pcipc(config);
	pentium_mmx_device &maincpu(PENTIUM_MMX(config.replace(), "maincpu", 266'000'000)); // socket 7 CPU
	maincpu.set_addrmap(AS_PROGRAM, &pcipc_state::pcipc_map);
	maincpu.set_addrmap(AS_IO, &pcipc_state::pcipc_map_io);
	maincpu.set_irq_acknowledge_callback("pci:07.0:pic8259_master", FUNC(pic8259_device::inta_cb));
	maincpu.smiact().set("pci:00.0", FUNC(i82439hx_host_device::smi_act_w));
}

void pcipc_state::pcipctx(machine_config &config)
{
	pentium_device &maincpu(PENTIUM(config, "maincpu", 60000000));
	maincpu.set_irq_acknowledge_callback("pci:07.0:pic8259_master", FUNC(pic8259_device::inta_cb));

	PCI_ROOT(config, "pci", 0);
	I82439TX(config, "pci:00.0", 0, "maincpu", 256*1024*1024);

	i82371sb_isa_device &isa(I82371SB_ISA(config, "pci:07.0", 0, "maincpu"));
	isa.boot_state_hook().set(FUNC(pcipc_state::boot_state_award_w));
//  IDE_PCI(config, "pci:07.1", 0, 0x80867010, 0x03, 0x00000000);

	PCI_SLOT(config, "pci:1", pci_cards, 15, 0, 1, 2, 3, nullptr);
	PCI_SLOT(config, "pci:2", pci_cards, 16, 1, 2, 3, 0, nullptr);
	PCI_SLOT(config, "pci:3", pci_cards, 17, 2, 3, 0, 1, nullptr);
	PCI_SLOT(config, "pci:4", pci_cards, 18, 3, 0, 1, 2, "mga2064w");

	x86_softlists(config);
}

void pcipc_state::pciagp(machine_config &config)
{
	// TODO: starts at 233'000'000, consider adding FSB & AGP clocks here
	pentium2_device &maincpu(PENTIUM2(config, "maincpu", 90'000'000));
	maincpu.set_addrmap(AS_PROGRAM, &pcipc_state::pcipc_map);
	maincpu.set_addrmap(AS_IO, &pcipc_state::pcipc_map_io);
	maincpu.set_irq_acknowledge_callback("pci:07.0:pic8259_master", FUNC(pic8259_device::inta_cb));
	maincpu.smiact().set("pci:00.0", FUNC(i82443bx_host_device::smi_act_w));

	PCI_ROOT(config, "pci", 0);
	I82443BX_HOST(config, "pci:00.0", 0, "maincpu", 128*1024*1024);
	I82443BX_BRIDGE(config, "pci:01.0", 0 );

	i82371eb_isa_device &isa(I82371EB_ISA(config, "pci:07.0", 0, "maincpu"));
	isa.boot_state_hook().set(FUNC(pcipc_state::boot_state_award_w));
	isa.smi().set_inputline("maincpu", INPUT_LINE_SMI);

	i82371eb_ide_device &ide(I82371EB_IDE(config, "pci:07.1", 0, "maincpu"));
	ide.irq_pri().set("pci:07.0", FUNC(i82371eb_isa_device::pc_irq14_w));
	ide.irq_sec().set("pci:07.0", FUNC(i82371eb_isa_device::pc_mirq0_w));

	I82371EB_USB (config, "pci:07.2", 0);
	I82371EB_ACPI(config, "pci:07.3", 0);
	LPC_ACPI     (config, "pci:07.3:acpi", 0);
	SMBUS        (config, "pci:07.3:smbus", 0);

	ISA16_SLOT(config, "board4", 0, "pci:07.0:isabus", isa_internal_devices, "w83977tf", true).set_option_machine_config("w83977tf", winbond_superio_config);
	ISA16_SLOT(config, "isa1", 0, "pci:07.0:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa2", 0, "pci:07.0:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa3", 0, "pci:07.0:isabus", pc_isa16_cards, nullptr, false);

#if 0
	rs232_port_device &serport0(RS232_PORT(config, "serport0", isa_com, nullptr));
	serport0.rxd_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::rxd1_w));
	serport0.dcd_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::ndcd1_w));
	serport0.dsr_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::ndsr1_w));
	serport0.ri_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::nri1_w));
	serport0.cts_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::ncts1_w));

	rs232_port_device &serport1(RS232_PORT(config, "serport1", isa_com, nullptr));
	serport1.rxd_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::rxd2_w));
	serport1.dcd_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::ndcd2_w));
	serport1.dsr_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::ndsr2_w));
	serport1.ri_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::nri2_w));
	serport1.cts_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::ncts2_w));
#endif

	// FIXME: int mapping is unchecked for all slots
	PCI_SLOT(config, "pci:01.0:1", agp_cards, 1, 0, 1, 2, 3, "riva128");

	PCI_SLOT(config, "pci:1", pci_cards, 15, 1, 2, 3, 0, nullptr);
	PCI_SLOT(config, "pci:2", pci_cards, 16, 1, 2, 3, 0, nullptr);
	PCI_SLOT(config, "pci:3", pci_cards, 17, 2, 3, 0, 1, nullptr);
	PCI_SLOT(config, "pci:4", pci_cards, 18, 3, 0, 1, 2, nullptr);

	x86_softlists(config);
}

ROM_START(pcipc)
	ROM_REGION32_LE(0x40000, "pci:07.0", 0) /* PC bios */
	ROM_SYSTEM_BIOS(0, "m55ns04", "m55ns04") // Micronics M55HI-Plus with no sound
	ROMX_LOAD("m55-04ns.rom", 0x20000, 0x20000, CRC(0116b2b0) SHA1(19b0203decfd4396695334517488d488aec3ccde), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "m55s04", "m55s04") // with sound
	ROMX_LOAD("m55-04s.rom", 0x20000, 0x20000, CRC(34a7422e) SHA1(68753fe373c97844beff83ea75c634c77cfedb8f), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "crisis", "Version 07/01/98, for flash recovery")
	ROMX_LOAD("crisis.rom", 0x00000, 0x40000, CRC(38a1458a) SHA1(8881ac336392cca79a772b4168f63efc31f953dd), ROM_BIOS(2) )
	// FIXME: this is incompatible, it's a Gigabyte GA-586HX with W83877F Super I/O
	ROM_SYSTEM_BIOS(3, "5hx29", "5hx29")
	ROMX_LOAD("5hx29.bin",   0x20000, 0x20000, CRC(07719a55) SHA1(b63993fd5186cdb4f28c117428a507cd069e1f68), ROM_BIOS(3) )
//  ROM_REGION(0x8000,"ibm_vga", 0)
//  ROM_LOAD("ibm-vga.bin", 0x00000, 0x8000, BAD_DUMP CRC(74e3fadb) SHA1(dce6491424f1726203776dfae9a967a98a4ba7b5) )
ROM_END

ROM_START(pcipctx)
	ROM_REGION32_LE(0x40000, "pci:07.0", 0) /* PC bios */
	ROM_SYSTEM_BIOS(0, "ga586t2", "Gigabyte GA-586T2") // ITE 8679 I/O
	ROMX_LOAD("gb_ga586t2.bin",  0x20000, 0x20000, CRC(3a50a6e1) SHA1(dea859b4f1492d0d08aacd260ed1e83e00ebac08), ROM_BIOS(0))

	ROM_REGION(0x8000,"ibm_vga", 0)
	ROM_LOAD("ibm-vga.bin", 0x00000, 0x8000, BAD_DUMP CRC(74e3fadb) SHA1(dce6491424f1726203776dfae9a967a98a4ba7b5) )
ROM_END

ROM_START(pciagp)
	ROM_REGION32_LE(0x40000, "pci:07.0", 0) /* PC bios */
	// a.k.a. the BIOS present in savquest.cpp
	ROM_SYSTEM_BIOS(0, "dfi_p2xbl", "Octek Rhino BX-ATX")
	ROMX_LOAD( "p2xbl_award_451pg.bin", 0x00000, 0x040000, CRC(37d0030e) SHA1(c6773d0e02325116f95c497b9953f59a9ac81317), ROM_BIOS(0) )
ROM_END

#define rom_pcipcs7    rom_pcipc

static INPUT_PORTS_START(pcipc)
INPUT_PORTS_END

} // anonymous namespace


COMP(1998, pcipc,    0,     0, pcipc,   pcipc, pcipc_state, empty_init, "Hack Inc.", "Sandbox PCI PC (430HX)", MACHINE_NO_SOUND )
COMP(1998, pcipcs7,  pcipc, 0, pcipcs7, pcipc, pcipc_state, empty_init, "Hack Inc.", "Sandbox PCI PC (430HX, Socket 7 CPU)", MACHINE_NO_SOUND ) // alternative of above, for running already installed OSes at their nominal speed + fiddling with MMX
COMP(1998, pcipctx,  0,     0, pcipctx, pcipc, pcipc_state, empty_init, "Hack Inc.", "Sandbox PCI PC (430TX)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING) // unemulated super I/O
COMP(1999, pciagp,   0,     0, pciagp,  pcipc, pcipc_state, empty_init, "Hack Inc.", "Sandbox PCI/AGP PC (440BX)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING) // errors out with ISA state $05 (keyboard, blame 8042kbdc.cpp) bp e140c,1,{eax&=~1;g}) does stuff if bypassed but eventually PnP breaks OS booting



pcipc_sis.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
/*
 * Sandbox for SiS based 496/497 x86 PCs, targeting the new PCI model
 *
 * Notes:
 * - sis85c471 doesn't belong here, it's a full on ISA PC/AT
 *
 * TODO:
 * - Finish porting sis85c496 from at.cpp;
 * - Reports slower CPU speeds in Award BIOSes (i.e. "66 MHz" for actual 75);
 * - (Hack Inc.) Glue in a Voodoo 1 hookup;
 * - (Hack Inc.) Identify motherboard name(s);
 *
 */

#include "emu.h"
#include "bus/isa/isa_cards.h"
#include "bus/pci/pci_slot.h"
#include "bus/rs232/hlemouse.h"
#include "bus/rs232/null_modem.h"
#include "bus/rs232/rs232.h"
#include "bus/rs232/sun_kbd.h"
#include "bus/rs232/terminal.h"
#include "cpu/i386/i386.h"
#include "machine/pci.h"
#include "machine/sis85c496.h"
#include "machine/w83787f.h"
#include "video/voodoo_pci.h"


class sis496_state : public driver_device
{
public:
	sis496_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void sis496(machine_config &config);

protected:
	required_device<i486dx4_device> m_maincpu;

private:
	void main_io(address_map &map) ATTR_COLD;
	void main_map(address_map &map) ATTR_COLD;

	static void winbond_superio_config(device_t *device);
};

#define PCI_ID_VIDEO    "pci:09.0"

class sis496_voodoo1_state : public sis496_state
{
public:
	sis496_voodoo1_state(const machine_config &mconfig, device_type type, const char *tag)
		: sis496_state(mconfig, type, tag)
		, m_voodoo(*this, PCI_ID_VIDEO)
		, m_screen(*this, "voodoo_screen")
	{ }

	void sis496_voodoo1(machine_config &config);

protected:
	required_device<voodoo_1_pci_device> m_voodoo;
	required_device<screen_device> m_screen;
};

void sis496_state::main_map(address_map &map)
{
	map.unmap_value_high();
}

void sis496_state::main_io(address_map &map)
{
	map.unmap_value_high();
}

static void isa_com(device_slot_interface &device)
{
	device.option_add("microsoft_mouse", MSFT_HLE_SERIAL_MOUSE);
	device.option_add("logitech_mouse", LOGITECH_HLE_SERIAL_MOUSE);
	device.option_add("wheel_mouse", WHEEL_HLE_SERIAL_MOUSE);
	device.option_add("msystems_mouse", MSYSTEMS_HLE_SERIAL_MOUSE);
	device.option_add("rotatable_mouse", ROTATABLE_HLE_SERIAL_MOUSE);
	device.option_add("terminal", SERIAL_TERMINAL);
	device.option_add("null_modem", NULL_MODEM);
	device.option_add("sun_kbd", SUN_KBD_ADAPTOR);
}

static void isa_internal_devices(device_slot_interface &device)
{
	device.option_add("w83787f", W83787F);
}

void sis496_state::winbond_superio_config(device_t *device)
{
	w83787f_device &fdc = *downcast<w83787f_device *>(device);
//  fdc.set_sysopt_pin(1);
//  fdc.gp20_reset().set_inputline(":maincpu", INPUT_LINE_RESET);
//  fdc.gp25_gatea20().set_inputline(":maincpu", INPUT_LINE_A20);
	fdc.irq1().set(":pci:05.0", FUNC(sis85c496_host_device::pc_irq1_w));
	fdc.irq8().set(":pci:05.0", FUNC(sis85c496_host_device::pc_irq8n_w));
	fdc.txd1().set(":serport0", FUNC(rs232_port_device::write_txd));
	fdc.ndtr1().set(":serport0", FUNC(rs232_port_device::write_dtr));
	fdc.nrts1().set(":serport0", FUNC(rs232_port_device::write_rts));
	fdc.txd2().set(":serport1", FUNC(rs232_port_device::write_txd));
	fdc.ndtr2().set(":serport1", FUNC(rs232_port_device::write_dtr));
	fdc.nrts2().set(":serport1", FUNC(rs232_port_device::write_rts));
}

void sis496_state::sis496(machine_config &config)
{
	I486DX4(config, m_maincpu, 75000000); // I486DX4, 75 or 100 Mhz
	m_maincpu->set_addrmap(AS_PROGRAM, &sis496_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &sis496_state::main_io);
	m_maincpu->set_irq_acknowledge_callback("pci:05.0:pic8259_master", FUNC(pic8259_device::inta_cb));

	PCI_ROOT(config, "pci", 0);
	SIS85C496_HOST(config, "pci:05.0", 0, "maincpu", 32*1024*1024);

	ISA16_SLOT(config, "board4", 0, "pci:05.0:isabus", isa_internal_devices, "w83787f", true).set_option_machine_config("w83787f", winbond_superio_config);
	ISA16_SLOT(config, "isa1", 0, "pci:05.0:isabus",  pc_isa16_cards, "wd90c31_lr", false);
	ISA16_SLOT(config, "isa2", 0, "pci:05.0:isabus",  pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa3", 0, "pci:05.0:isabus",  pc_isa16_cards, nullptr, false);

	rs232_port_device &serport0(RS232_PORT(config, "serport0", isa_com, "logitech_mouse"));
	serport0.rxd_handler().set("board4:w83787f", FUNC(w83787f_device::rxd1_w));
	serport0.dcd_handler().set("board4:w83787f", FUNC(w83787f_device::ndcd1_w));
	serport0.dsr_handler().set("board4:w83787f", FUNC(w83787f_device::ndsr1_w));
	serport0.ri_handler().set("board4:w83787f", FUNC(w83787f_device::nri1_w));
	serport0.cts_handler().set("board4:w83787f", FUNC(w83787f_device::ncts1_w));

	rs232_port_device &serport1(RS232_PORT(config, "serport1", isa_com, nullptr));
	serport1.rxd_handler().set("board4:w83787f", FUNC(w83787f_device::rxd2_w));
	serport1.dcd_handler().set("board4:w83787f", FUNC(w83787f_device::ndcd2_w));
	serport1.dsr_handler().set("board4:w83787f", FUNC(w83787f_device::ndsr2_w));
	serport1.ri_handler().set("board4:w83787f", FUNC(w83787f_device::nri2_w));
	serport1.cts_handler().set("board4:w83787f", FUNC(w83787f_device::ncts2_w));

	// TODO: 9-10-11-12 for PCI_SLOT (according to BIOS)
}

void sis496_voodoo1_state::sis496_voodoo1(machine_config &config)
{
	sis496_state::sis496(config);

	VOODOO_1_PCI(config, m_voodoo, 0, m_maincpu, m_screen);
	m_voodoo->set_fbmem(2);
	m_voodoo->set_tmumem(4, 0);
	// TODO: games are very annoyed with Direct3D 5 init/teardown fns around this.
	m_voodoo->set_status_cycles(1000);

	// TODO: wrong, needs VGA passthru
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(57);
	m_screen->set_size(640, 480);
	m_screen->set_visarea(0, 640 - 1, 0, 480 - 1);
	m_screen->set_screen_update(PCI_ID_VIDEO, FUNC(voodoo_1_pci_device::screen_update));

	PCI_SLOT(config, "pci:1", pci_cards, 10, 0, 1, 2, 3, nullptr);
	PCI_SLOT(config, "pci:2", pci_cards, 11, 1, 2, 3, 0, nullptr);
}

// generic placeholder for unknown BIOS types
// Funworld BIOS is temporary until we rewrite funworld/photoply.cpp
ROM_START( sis85c496 )
	ROM_REGION32_LE(0x20000, "pci:05.0", 0)
	ROM_SYSTEM_BIOS(0, "funworld", "Award 486e BIOS with W83787 (photoply)")
	// Photoplay BIOS
	// Lucky Star LS-486EF REV:B
	ROMX_LOAD("funworld_award_486e_w83787.bin", 0x000000, 0x20000, BAD_DUMP CRC(af7ff1d4) SHA1(72eeecf798a03817ce7ba4d65cd4128ed3ef7e68), ROM_BIOS(0) ) // 486E 96/7/19 W83787 PLUG & PLAY BIOS, AT29C010A

	// MegaTouch XL BIOSes
	// 09/11/96-SiS-496-SMC665-2A4IBU41C-00
	ROM_SYSTEM_BIOS(1, "merit", "Award 486e BIOS Telco (mtouchxl)")
	ROMX_LOAD( "094572516 bios - 486.bin", 0x000000, 0x020000, CRC(1c0b3ba0) SHA1(ff86dd6e476405e716ac7a4de4a216d2d2b49f15), ROM_BIOS(1) )
	// AMI BIOS, Jetway branded MB?
	// 40-040B-001276-00101111-040493-OP495SLC-0
	//ROMX_LOAD("prom.mb", 0x10000, 0x10000, BAD_DUMP CRC(e44bfd3c) SHA1(c07ec94e11efa30e001f39560010112f73cc0016) )

	// Chipset: SiS 85C496/85C497 - CPU: Socket 3 - RAM: 2xSIMM72, Cache - Keyboard-BIOS: JETkey V5.0
	// ISA16: 3, PCI: 3 - BIOS: SST29EE010 (128k) AMI 486DX ISA BIOS AA2558003 - screen remains blank
	ROM_SYSTEM_BIOS(2, "4sim002", "AMI ISA unknown BIOS")
	ROMX_LOAD( "4sim002.bin", 0x00000, 0x20000, BAD_DUMP CRC(ea898f85) SHA1(7236cd2fc985985f21979e4808cb708be8d0445f), ROM_BIOS(2) )
ROM_END

// A-Trend ATC-1425A - Chipset: SiS 85C496, 85C497 - RAM: 4xSIMM72, Cache: 4x32pin + TAG - ISA16: 4, PCI: 3
// on board: 2xIDE, Floppy, 2xser, par - BIOS: 32pin
ROM_START( atc1425a )
	ROM_REGION32_LE(0x20000, "pci:05.0", 0)
	// 0: Boot block - BIOS-String: 09/07/95-SiS-496-497/A/B-2A4IBA2HC-00 / 1425 SIS 496/7 BIOS VER : 1.8N   1995/09/25
	ROM_SYSTEM_BIOS(0, "ver18n", "ver1.8N")
	ROMX_LOAD( "atc-1425a_original.bin", 0x00000, 0x20000, CRC(040ebc6c) SHA1(266ed07ef13c363234c7a2a88719badeeed9dc4c), ROM_BIOS(0))
	// 1: Boot block - BIOS-String: 11/03/95-SiS-496-497/A/B-2A4IBA2HC-00 / ATC-1425A SIS496/7 BIOS VER:2.0N  11-04-95
	ROM_SYSTEM_BIOS(1, "ver20n", "ver2.0N")
	ROMX_LOAD( "atc-1425a_ver2_0n.bin", 0x00000, 0x20000, CRC(0af2f6c0) SHA1(a1ce34bdee5119b9ae1d8530fcf611ca2f9d592e), ROM_BIOS(1))
ROM_END

// A-Trend ATC-1425B - BIOS Version: Award 4.51PG 04/18/96 - Chipset: SiS 85C496/85C497, Winbond - Keyboard BIOS: Holtek HT6542B - CPU: Socket 3
// RAM: 4xSIMM72, Cache: 4xUM61512AK-15, 1xISSI IS61C256AH-15N - on board: 2xIDE, Floppy, par, 2xser - ISA16: 4, PCI: 3
ROM_START( atc1425b ) // Boot block - BIOS String: 04/18/96-SiS-496-497/A/B-2A4IBA2BC-00
	ROM_REGION32_LE(0x20000, "pci:05.0", 0)
	ROM_LOAD( "1425b231.rom", 0x00000, 0x20000, CRC(1a19f34d) SHA1(09bb5e35ef07b57942cbca933f2a0334615a687e))
ROM_END

// Abit AB-PI4(T) - BIOS: 32pin - Keyboard-BIOS: Winbond 83C42 - CPU: Socket 3 - ISA16: 4, PCI: 3 - Chipset: SiS 85C496, 85C497
// RAM: 4xSIMM72, Cache: 9x32pin (occupied: 4xW24512AK-20, 1xW2457AK) - On board: 2xIDE
ROM_START( abpi4 )
	ROM_REGION32_LE(0x20000, "pci:05.0", 0)
	// 0: BIOS-String: &09/25/95-SiS-496-497/A/B-2A4IBA11C-0B / GREEN 486 PCI SYSTEM BIOS - boots into "boot block" rescue BIOS
	ROM_SYSTEM_BIOS(0, "pi4092595", "AB-PI4(T) 09/25/95")
	ROMX_LOAD( "pi4_0b.bin", 0x00000, 0x20000, CRC(2cd67f19) SHA1(4cf0b4ff10645371361d3782c8be06c463e70219), ROM_BIOS(0))
	// 1: 486IP-B-2-A (ABIT PI4/PI4T PCI clone) REV:2B.31 - Chipset : SiS 496/497 (NV/NU) - BIOS : AWARD 2a4ibb61 - Keyboard BIOS: JETkey V5.0G
	// RAM: 4xSIMM72, Cache: 9x32pin DIP (filled: 9xUM61256FK-15 CPU: Socket 3 - on board: 2xIDE - ISA16: 4, PCI: 3
	// BIOS-String : 10/02/95-SiS-496-497/A/B-2A4IBB61C-00 - boots into "boot block" rescue BIOS
	ROM_SYSTEM_BIOS(1, "486ipb2a", "486IP-B-2-A")
	ROMX_LOAD( "486ip-b-2-a.bin", 0x00000, 0x20000, CRC(8b1e3094) SHA1(84e8269f310b53497e63791fd3c081d7f631b686), ROM_BIOS(1))
ROM_END

// Abit AB-PM4
// BIOS-String: 09/04/95-SiS-496-497/A/B-2A4IBA13C-0C / GREEN 486 PCI SYSTEM BIOS
ROM_START( abpm4 )
	ROM_REGION32_LE(0x20000, "pci:05.0", 0)
	ROM_LOAD( "pm4_0c.bin", 0x00000, 0x20000, CRC(eaad7812) SHA1(81670c44e30fa8b8ac0aa28a5c367819ff1ca73c))
ROM_END

// Abit AB-PV4
// BIOS-String: 09/26/95-SiS-496-497/A/B-2A4IBA12C-0A / GREEN 486 PCI SYSTEM BIOS
ROM_START( abpv4 )
	ROM_REGION32_LE(0x20000, "pci:05.0", 0)
	ROM_LOAD( "pv4v_0a.bin", 0x00000, 0x20000, CRC(91de48d5) SHA1(2e873de152870270f51b5b2c4a30f2611364e739))
ROM_END

// Aopen AP43 - CPU: Socket 3 - Chipset: SiS 85C496, 85C497, SMC FDC37C665GT - RAM: SIMM72x4, Cache: 9x32pin, used: 9xUM61256FK-15
// BIOS: 32pin - Keyboard-BIOS: AMIKEY-2 - on board: IDEx2, Floppy, par, 2xser
ROM_START( aoap43 )
	ROM_REGION32_LE(0x20000, "pci:05.0", 0)
	// 0:
	ROM_SYSTEM_BIOS(0, "original", "original")
	ROMX_LOAD( "aopen_ap43_original.bin", 0x00000, 0x20000, CRC(65075fe4) SHA1(9b150e0b37b4ff3cbfcd8bd2286e1e575c34de02), ROM_BIOS(0))
	// 1:
	ROM_SYSTEM_BIOS(1, "updated", "updated")
	ROMX_LOAD( "aopen_ap43_updated.rom", 0x00000, 0x20000, CRC(68a5595e) SHA1(94551037e9d0b3fb644726b7ba66e676aa58b81a), ROM_BIOS(1))
ROM_END

// ASUS PCI/I-A486S (4xSIMM72, Cache: 128/256/512KB, 1 EISA) - BIOS: 32pin
// SiS 85C496/85C497 chipset; SMC 37C665 I/O; AMIKEY-2, S3 Trio 64 on board VGA, the manual also mentions Trio 32
ROM_START( aa486s )
	ROM_REGION32_LE(0x20000, "pci:05.0", 0)
	// 0: BIOS-String: 05/22/95/SiS-496-497B-PCI-A486-0-00 / #401A0-0203
	ROM_SYSTEM_BIOS(0, "v203", "ASUS PCI/I-A486S V2.03")
	ROMX_LOAD( "si4a0203.awd", 0x00000, 0x20000, CRC(95fcb7c6) SHA1(c19164d67af18c774e6eb06bd1570d95a24b2856), ROM_BIOS(0))
	// 1: BIOS-String: 11/27/95-SiS-496-497B-PI-A486SC-00 / #401A0-0304 -  boots into "boot block" rescue BIOS
	ROM_SYSTEM_BIOS(1, "v304", "ASUS PCI/I-A486S V3.04")
	ROMX_LOAD( "si4a0304.awd", 0x00000, 0x20000, CRC(a00ad907) SHA1(598d97ea29f930a9359429dc540d27bfdd0fcd20), ROM_BIOS(1))
ROM_END

// ASUS PVI-486SP3 (Socket 3, 2xSIMM72, Cache: 128/256/512KB, 2 IDE, 3 PCI, 4 ISA, 1 VLB)
// SiS 85C496 + 85C497; UMC UM8669F; AMIKEY-2; BIOS: 29EE010 (32pin)
ROM_START( a486sp3 )
	ROM_REGION32_LE(0x20000, "pci:05.0", 0) // Winbond W29EE011-15
	// 0: BIOS-String: 07/22/94-SATURN-P/I-4SP3-00 / #401A0-0207
	ROM_SYSTEM_BIOS(0, "v207", "ASUS PVI-486SP3 V2.07")
	ROMX_LOAD( "awsi0207.bin", 0x00000, 0x20000, CRC(0cb862aa) SHA1(7ffead05c1df47ec36afba395191145279c5e789), ROM_BIOS(0))
	// 1: BIOS-String: 07/22/94-SATURN-P/I-4SP3-00 / #401A0-0207
	ROM_SYSTEM_BIOS(1, "v2737", "ASUS PVI-486SP3 V2.07 #2")
	ROMX_LOAD( "awsi2737.bin", 0x00000, 0x20000, CRC(8cd9a89c) SHA1(6c68c23cc5e8ae66261e9fe931f2ce07efe767b6), ROM_BIOS(1))
	// 2: BIOS-String: 06/25/96-SiS-496-497B-PVI-4SP3C-00 / #401A0-0306 - boots to Award BootBlock BIOS V1.0
	ROM_SYSTEM_BIOS(2, "v306", "ASUS PVI-486SP3 V3.06")
	ROMX_LOAD( "si4i0306.awd", 0x00000, 0x20000, CRC(fc70371a) SHA1(96b10cfa97c5d1d023687f01e8acb54f263069b2), ROM_BIOS(2))
	// 3: BIOS-String: 02/11/98-SiS-496-497B-PVI-4SP3C-00 / #401A0-0307 - boots to Award BootBlock BIOS V1.0
	ROM_SYSTEM_BIOS(3, "v307", "ASUS PVI-486SP3 V3.07")
	ROMX_LOAD( "si4i0307h.bin", 0x00000, 0x20000, CRC(99473cc0) SHA1(a01d253cf434a31e0ca6f6cd2b9026ca424eb463), ROM_BIOS(3))
	// 4: BIOS-String: 08/08/95-SiS-496-497B-PVI-4SP3C-00 / #401A0-0301 - boots to Award BootBlock BIOS
	ROM_SYSTEM_BIOS(4, "v301", "ASUS PVI-486SP3 V3.01")
	ROMX_LOAD( "4siw003.bin", 0x00000, 0x20000, CRC(47a1d815) SHA1(370bfb895646518884a2a82881721efc3aeb04d1), ROM_BIOS(4))
	// 5: BIOS-String: 11/23/94-SiS-496-497-PVI-4SP3-00 / #401A0-0101
	ROM_SYSTEM_BIOS(5, "v10101", "ASUS PVI-486SP3 V1.01 #1")
	ROMX_LOAD( "0101.bin", 0x00000, 0x20000, CRC(7862ca56) SHA1(e609585893b23db10c4ae7d2abd17cc9dda964b6), ROM_BIOS(5))
	// 6: BIOS-String: 11/23/94-SiS-496-497-PVI-4SP3-00 / #401A0-0101 - screen remains blank
	ROM_SYSTEM_BIOS(6, "v10102", "ASUS PVI-486SP3 V1.01 #2")
	ROMX_LOAD( "si4i0101.awd", 0x00000, 0x20000, CRC(18652037) SHA1(7460e90b0a9c825d2e47943a714049fe9e943760), ROM_BIOS(6))
	// 7: BIOS-String: 07/15/95-SiS-496-497B-PVI-4SP3C-00 / #401A0-0205 - boots to Award BootBlock BIOS
	ROM_SYSTEM_BIOS(7, "v205", "ASUS PVI-486SP3 V2.05")
	ROMX_LOAD( "si4i0205.awd", 0x00000, 0x20000, CRC(d90d91b0) SHA1(043151d121780ff56ce32b9a48e9bbccd324625f), ROM_BIOS(7))
	// 8: BIOS-String: 04/05/96-SiS-496-497B-PVI-4SP3C-00 / #401A0-0305 - boots to Award BootBlock BIOS
	ROM_SYSTEM_BIOS(8, "v305", "ASUS PCI/I-486SP3 V3.05")
	ROMX_LOAD( "si4i0305.awd", 0x00000, 0x20000, CRC(2f90e63e) SHA1(a4f16753b5a57d65fba7702ca28e44f10bd5bb6c), ROM_BIOS(8))
ROM_END

// Chaintech 486SPM - CPU: Socket 3 - Chipset: SiS 85C497, 85C496, UMC UM8663BF - RAM: 4xSIMM72, Cache: 8xIS61C1024-10N, W24512AK-10
// BIOS: Award E0822859 - Keyboard-BIOS: VIA VT82C42N - on board: 2xISA, Floppy, 2xser, par - ISA16: 4, PCI: 3
ROM_START( ch486spm )
	ROM_REGION32_LE(0x20000, "pci:05.0", 0)
	// 0: BIOS-String: 12/21/95-SiS-496-497/A/B-2A4IBC3IC-00 - stack crawl
	ROM_SYSTEM_BIOS(0, "original", "original")
	ROMX_LOAD( "chaintech_486spm.bin", 0x00000, 0x20000, CRC(a0c9045a) SHA1(1d0b1994574437549c13541d4b65374d94c9a648), ROM_BIOS(0))
	// 1: 12/21/95-SiS-496-497/A/B-2A4IBC3IC-00 / Chaintech 486SPM v2015 PS/2
	ROM_SYSTEM_BIOS(1, "ps2", "PS2 mouse enabled")
	ROMX_LOAD( "486spm-p.bin", 0x00000, 0x20000, CRC(35b5cb76) SHA1(965b212b28a5badd8d8f4769aa9edc88e47bc925), ROM_BIOS(1))
ROM_END

// Chaintech 4SPI - Chipset: SiS 85C496 85C497 - BIOS Version: Award v4.50G E0671975 - Keyboard BIOS: Lance Green LT38C41
// CPU: Socket 3 - RAM: 4xSIMM72, Cache: 9x32pin DIP (used: 4xW24512AK-15, 1xEM51256-15PL) - On board: 2xIDE
// ISA6: 5, PCI: 3
ROM_START( ch4spi ) // BIOS String: 02/16/95-SiS-496-497/A/B-2A4IBC31-B2 / 02/17/1995 / stack crawl
	ROM_REGION32_LE(0x20000, "pci:05.0", 0)
	ROM_LOAD( "4spibckp.bin", 0x00000, 0x20000, CRC(29b15737) SHA1(e9cb5402eb25a100a15d5ccc520cfa76c7be99a6))
ROM_END

// Freetech 486F55 - Chipset: SiS 496/497 - CPU: Socket 3 - RAM: 4xSIMM72, Cache: 128KB/256KB/512KB - ISA16: 4, PCI: 3 -
// On board: 2xser, par, 2xIDE, Floppy - BIOS: Award
ROM_START( ft486f55 )
	ROM_REGION32_LE(0x20000, "pci:05.0", 0)
	// 0:
	ROM_SYSTEM_BIOS(0, "xsf", "XS-F")
	ROMX_LOAD( "55xs-f.bin", 0x00000, 0x20000, CRC(b7ee53af) SHA1(6357241ac3c317f60465bf5ad77d821a7dc68b3b), ROM_BIOS(0))
	// 1:
	ROM_SYSTEM_BIOS(1, "xsg", "XS-G")
	ROMX_LOAD( "55xs-g.bin", 0x00000, 0x20000, CRC(adaa3a28) SHA1(27c36b564d11f1dc9a8c6f6d075eeaf850944c08), ROM_BIOS(1))
ROM_END

// Jamicon KM-S4-1 VER 1.1 - Chipset: SiS 85C496/85C497 (PR/NU revision), Winbond W837F - BIOS/Version: KM-S4-1 VER:4.2 - AWARD
// BIOS: Award PCI/PNP 486 S/N:024893105 - Keyboard BIOS: Winbond W83C42 - CPU: P24T - RAM: 4xSIMM72, Cache: 4xUM61512AK-15, 1xW24257AK-15
// on board: 2xser, Floppy, par, 2xIDE - ISA16: 3, PCI: 3
ROM_START( jakms41 ) // BIOS String: 10/30/95-SiS-496-497/A/B-2A4IBR22C-00
	ROM_REGION32_LE(0x20000, "pci:05.0", 0)
	ROM_LOAD( "km-s4-1.bin", 0x00000, 0x20000, CRC(0271356a) SHA1(893048c3390a23810a2af14da30520fbea10ad2f))
ROM_END

// Jetway J-446A - Chipset: SiS 85C497, 82C496 - CPU: Socket 3 - RAM: 4xSIMM72, Cache: 4+1 sockets - BIOS: 32pin
// Keyboard-BIOS: HOLTEK HT6542B - ISA16: 3, PCI: 3 - On board: 2xIDE, Floppy, par, 2xser
ROM_START( jwj446a )
	ROM_REGION32_LE(0x20000, "pci:05.0", 0)
	// 0:
	ROM_SYSTEM_BIOS( 0, "no1", "J-446A #1")
	ROMX_LOAD( "j446a_original.bin", 0x00000, 0x20000, BAD_DUMP CRC(79d2e360) SHA1(8bf3befa1c869e298ec346cc784fcbc2193e3912), ROM_BIOS(0))
	// 1: 02/02/96-SiS-496-497/A/B-2A4IBJ19C-00 / V.446 RP5 2-2-1996
	ROM_SYSTEM_BIOS( 1, "no2", "J-446A #2")
	ROMX_LOAD( "j446a.rom", 0x00000, 0x20000, CRC(3e3c6abd) SHA1(04952dc143baa7b51cb6fc5eb1961007ecf36aaf), ROM_BIOS(1))
ROM_END

// LuckyStar LS-486E  - Chipset : SiS496, SiS497, SMC FDC37C665GT - CPU: AMD 486DX4-100 (Socket 3) - RAM: 4xSIMM72, Cache: 4 sockets (UM61512AK-15)+1
// BIOS : AMIBIOS 486PCI ISA 393824, on a 27C010 type ROM chip - Keyboard-BIOS: AMIKEY-2 - ID string : 41-PH0D-001256-00101111-101094-SIS496AB-H
// On board: 2xISA, Floppy, par, 2xser - ISA16: 4, PCI: 3
ROM_START( ls486e )
	ROM_REGION32_LE(0x20000, "pci:05.0", 0)
	// 0: Rev:C
	ROM_SYSTEM_BIOS( 0, "revc01", "Rev.C #1")
	ROMX_LOAD( "ls486e_revc.bin", 0x00000, 0x20000, CRC(d678a26e) SHA1(603e03171b28f73bdb6ce27b0bbae2a4cfb13517), ROM_BIOS(0))
	// 1: LS486E Rev.D SiS496/497(PR/NU) EDO Support AWARD 10/21/96 - 10/21/96-SiS-496-497/A/B-2A4IBL12C-00 - 486E 96/10/24 UMC8669 PLUG & PLAY BIOS
	ROM_SYSTEM_BIOS( 1, "revd01", "Rev.D #1")
	ROMX_LOAD( "ls486-d.awa", 0x00000, 0x20000, CRC(5a51a3a3) SHA1(6712ab742676156802fdfc4d08d687c1482f2702), ROM_BIOS(1))
	// 2: Lucky Star LS486E rev.C,Winbond,SiS496/497  - BIOS Award PNP v4.50PG (486E 96/5/17 W83787) - BIOS-String: 03/14/96-SiS-496-497/A/B-2A4IBL13C-00 / 486E 96/5/17 W83787
	ROM_SYSTEM_BIOS( 2, "revc02", "Rev.C #2")
	ROMX_LOAD( "ls486e-c.awd", 0x00000, 0x20000, CRC(8c290f20) SHA1(33d9a96e5d6b3bd5776480f5535bb1eb1d7cff57), ROM_BIOS(2))
	//3: BIOS-String: 03/14/96-SiS-496-497/A/B-2A4IBL13C-00 / 486E 96/7/19 W83787 PLUG & PLAY BIOS - boots to BootBlock BIOS
	ROM_SYSTEM_BIOS( 3, "revc1", "Rev.C1") // also on a Rev.C2 board
	ROMX_LOAD( "ls486ec1.bin", 0x00000, 0x20000, CRC(e96d1bbc) SHA1(64d0726c4e9ecee8fddf4cc39d92aecaa8184d5c), ROM_BIOS(3))
	// 4: BootBlock BIOS
	ROM_SYSTEM_BIOS( 4, "lh5", "LH5")
	ROMX_LOAD( "ls-486e.bin", 0x00000, 0x20000, CRC(03ca4a97) SHA1(f9e5e2f2fabcb47960dfa91c37bf74fa93398092), ROM_BIOS(4))
	// 5: BIOS-String: 03/14/96-SiS-496-497/A/B-2A4IBL13C-00
	ROM_SYSTEM_BIOS( 5, "ls486eb", "LS-486E(B)")
	ROMX_LOAD( "4siw001.bin", 0x00000, 0x20000, CRC(d81d722d) SHA1(bb18324b3679b7419c230244891b626a61006486), ROM_BIOS(5))
ROM_END

// MSI MS-4144 - Chipset: SiS 85C497, 85C496, Winbond W83787F, W83758F - CPU: Socket 3 - RAM: 4xSIMM72, Cache: 8+1 sockets
// On board: 2xIDE, Floppy, 2xser, par - ISA16: 4, PCI: 3
ROM_START( ms4144 )
	ROM_REGION32_LE(0x20000, "pci:05.0", 0)
	// 0: no display
	ROM_SYSTEM_BIOS(0, "af53", "AF53")
	ROMX_LOAD( "ms-4144_af53.rom", 0x00000, 0x20000, CRC(931ebb7d) SHA1(fa7cf64c07a6404518e12c41c197354c7d05b2d2), ROM_BIOS(0))
	// 1: no display
	ROM_SYSTEM_BIOS(1, "af54", "AF54")
	ROMX_LOAD( "ms-4144_af54s.rom", 0x00000, 0x20000, CRC(1eb02779) SHA1(b18cc771fc5a820437a4daca06806188ee1a27a5), ROM_BIOS(1))
	// 2: BIOS-String: 03/20/96-SiS-496-497/A/B-2A4IBM49C-00 / WF53S 032096
	ROM_SYSTEM_BIOS(2, "wf53", "WF53")
	ROMX_LOAD( "ms-4144_wf53s.bin", 0x00000, 0x20000, CRC(df83f099) SHA1(b7dc61a2cb71754cddd06d12d3bf81ffce442c89), ROM_BIOS(2))
	// 3: BIOS-String: 02/07/96-SiS-496-497/A/B-2A4IBM49C-00 / WF54S 020896
	ROM_SYSTEM_BIOS(3, "wf54", "WF54")
	ROMX_LOAD( "ms-4144_wf54s.bin", 0x00000, 0x20000, CRC(c0ff31df) SHA1(4e138558781a220b340977d56ccbfa61a907d4f5), ROM_BIOS(3))
	// 4: no display - VER 2.1 - BIOS: AMI 486DX ISA BIOS AC8999569 (32pin)- Keyboard-BIOS: AMIKEY-2
	ROM_SYSTEM_BIOS(4, "v21", "Ver 2.1")
	ROMX_LOAD( "486-pci-ms4144.bin", 0x00000, 0x20000, CRC(8bd50381) SHA1(c9853642ac0946c2b1a7e469bcfacbb3351c4067), ROM_BIOS(4))
ROM_END

// SOYO 30H - CPU: Socket 3 - RAM: SIMM72x4 - Cache: 256K, 512K or 1024K - ISA16: 4, PCI: 3 - on board: 2xIDE
// BIOS-String: 12/07/95-SiS-496-497/A/B-2A4IBS2AC-00 / REV B2
ROM_START( so30h )
	ROM_REGION32_LE(0x20000, "pci:05.0", 0)
	ROM_LOAD( "30h-b2.bin", 0x00000, 0x20000, CRC(1dd22cef) SHA1(dd0ac15e7a792e8fba2f55d6a1b35256e74bcf4e))
ROM_END

// SOYO SY-4SAW2 - Chipset: SiS 85C497, 85C496, Winbond W83787F - CPU: Socket 3 - RAM: 4xSIMM72, Cache: 4xUM61512AK-15+W24129AK-15
// BIOS: Award (32pin) - Keyboard-BIOS: Via VT82C42N - ISA16: 3, ISA16/VL: 1, PCI: 4 - On board: 2xser, par, 2xIDE, Floppy
// keeping the ROMs for the 4SA boards here until the differences between the boards are clear, e.g. difference between SY-4SAW and 4SA2: L2-cache
ROM_START( so4saw2 )
	ROM_REGION32_LE(0x20000, "pci:05.0", 0)
	// 0: BIOS-String: 04/15/95-SiS-496-497/A/B-2A4IBS22-00 / REV IO-A
	ROM_SYSTEM_BIOS(0, "ioa", "IO-A")
	ROMX_LOAD( "4sa2_bios_isa_486_488755.bin", 0x00000, 0x20000, CRC(21708d9c) SHA1(be4596507df1f5cc8a4e1baafce52b96417ac029), ROM_BIOS(0))
	// 1: BIOS-String: 08/22/95-SiS-496-497/A/B-2A4IBS2hC-00 / REV IO-B1 (4SA2, http://www.elhvb.com/supportbios.info/Archives/BIOS/R-S/SOYO/4SA2/index.html)
	ROM_SYSTEM_BIOS(1, "iob1", "IO-B1")
	ROMX_LOAD( "4sa-iob1.bin", 0x00000, 0x20000, CRC(a74891b6) SHA1(974c3a854a4e83202555bcbcba191f902527b577), ROM_BIOS(1))
	// 2: BIOS-String: 07/30/97-SiS-496-497/A/B-2A4IBS2hC-00 / SA-0730 (4SA, http://www.elhvb.com/supportbios.info/Archives/BIOS/R-S/SOYO/4SA/index.html)
	ROM_SYSTEM_BIOS(2, "0730", "0730")
	ROMX_LOAD( "4sa0730.bin", 0x00000, 0x20000, CRC(dea32658) SHA1(2c89500d9904f61a5426de5f1351ca8004c9920b), ROM_BIOS(2))
	// 3: BIOS-String: 07/03/96-SiS-496-497/A/B-2A4IBS29C-00 / REV WA53 (4SAW/4SAW2)
	ROM_SYSTEM_BIOS(3, "wa53", "WA53")
	ROMX_LOAD( "4saw53.bin", 0x00000, 0x20000, CRC(2265a9d1) SHA1(bd625f0f11e64d2620648cf14e6b6faf09df80bc), ROM_BIOS(3))
	// 4: BIOS-String: 12/05/95-SiS-496-497/A/B-2A4IBS29C-00 / REV WA3 (4SAW/4SAW2)
	ROM_SYSTEM_BIOS(4, "wa3", "WA3")
	ROMX_LOAD( "4saw-wa3.bin", 0x00000, 0x20000, CRC(d47e727e) SHA1(c6ba38e72575127b763a8e5ead49dbaaef85ab06), ROM_BIOS(4))
	// 5: BIOS-String: 09/11/97-SiS-496-497/A/B-2A4IBS29C-00 / REV WA0911 (4SAW/4SAW2 http://www.elhvb.com/supportbios.info/Archives/BIOS/R-S/SOYO/4SAW/index.html)
	ROM_SYSTEM_BIOS(5, "0911", "0911")
	ROMX_LOAD( "4saw0911.bin", 0x00000, 0x20000, CRC(4056b35e) SHA1(bca2d2507b15800ad13bd8f8c6699b49b8e87011), ROM_BIOS(5))
	// 6: BIOS-String: 09/11/97-SiS-496-497/A/B-2A4IBS29C-00 / REV WA0911 128GB BETA ROM (4SAW)
	ROM_SYSTEM_BIOS(6, "0911b", "0911b")
	ROMX_LOAD( "4saw0911b.bin", 0x00000, 0x20000, CRC(000fca3e) SHA1(46ceb550ed08fb013f02e51e1d428a60e220ede6), ROM_BIOS(6))
ROM_END

// ZIDA Tomato board 4DPS - Chipset: SIS 85C497, SIS 85C496, Winbond W83787IF, W83768F, MX8318-01PC - CPU: 486/5x86 - BIOS: Winbond W29EE011-15 / AWARD PCI/PNP
// Keyboard-BIOS: HOLTEK HT6542B or AMIKEY-2 - ISA16: 3, PCI: 3 - OSC: 24.000 - On board: 2xIDE, Floppy, 2xCOM, 1xPRN, Mouse, GAME
// from v4.00 onward it needs FLASH instead of EPROM to update the ESCD at boot time
ROM_START( zito4dps )
	ROM_REGION32_LE(0x20000, "pci:05.0", 0)
	// 0: BIOS-String: 01/10/96-SiS-496-497/A/B-2A4IBZ11C-00 / 4DPS  VER 1.5 (2301952A4IBZ11)
	ROM_SYSTEM_BIOS( 0, "4dps01", "Tomato 4DPS #1")
	ROMX_LOAD( "4siw004.bin", 0x00000, 0x20000, CRC(0c57cc33) SHA1(04ce27dc89ae15d70c14076ad4f82b50a4f1e6dd), ROM_BIOS(0))
	// 1: BIOS-String: 06/17/1998-SiS-496-497/A-2A4IBZ11C-00 / 4DPS V4.00A (17/06/98)
	ROM_SYSTEM_BIOS( 1, "4dps02", "Tomato 4DPS #2")
	ROMX_LOAD( "4dps02.bin", 0x00000, 0x20000, CRC(757a5ef7) SHA1(e35146f34329a6a7033b1ed9d95a77692826a060), ROM_BIOS(1))
	// 2: BIOS-String: 10/17/96-SiS-496-497/A/B-2A4IBZ11C-00 / 4DPS  VER 1.71 (1710962A4IBZ11)
	ROM_SYSTEM_BIOS( 2, "171", "Tomato 4DPS v1.71")
	ROMX_LOAD( "4dps_170.bin", 0x00000, 0x20000, CRC(10b43a85) SHA1(d77bb2420b98c030add5de52fc90c88384b2036b), ROM_BIOS(2))
	// 3: BIOS-String: 07/08/97-SiS-496-497/A/B-2A4IBZ1AC-00 / 4DPS VER 1.72F (10072A4IBZ1A)
	ROM_SYSTEM_BIOS( 3, "172f", "Tomato 4DPS v1.72f")
	ROMX_LOAD( "4dps172g.bin", 0x00000, 0x20000, CRC(184eeeba) SHA1(248555567e35d4d6a0cfad5abc989e8193a72351), ROM_BIOS(3))
	// 4: BIOS-String: 06/17/1998-SiS-496-497/A-2A4IBZ11C-00 / 4DPS V4.00A (17/06/98)
	ROM_SYSTEM_BIOS( 4, "400a", "Tomato 4DPS v4.00a")
	ROMX_LOAD( "4dps400a.bin", 0x00000, 0x20000, CRC(494da2da) SHA1(9dcae9aa403627df03d5777c1b4de0b9f98bb24f), ROM_BIOS(4))
	// 5: BIOS-String: 01/10/96-SiS-496-497/A/B-2A4IBZ11C-00 / Tomato 4DPS v4.01 (Y2K ready)
	ROM_SYSTEM_BIOS( 5, "401e", "Tomato 4DPS v4.01e")
	ROMX_LOAD( "4dps401e.bin", 0x00000, 0x20000, CRC(e84b2bb2) SHA1(5dd8e801decf87af90ff90e3096819354f657b5a), ROM_BIOS(5))
	// 6: v2.11, also marked v400a - BIOS-String: 06/17/1998-SiS-496-497/A-2A4IBZ11C-00 / 4DPS V4.00A (17/06/98)
	ROM_SYSTEM_BIOS( 6, "4dps03", "Tomato 4DPS #3")
	ROMX_LOAD( "4dps400b.bin", 0x00000, 0x20000, CRC(5910fa95) SHA1(934845038298d2d50f5bd4b20e0a4ccd9aa74e82), ROM_BIOS(6))
	// 7: BIOS-String: 11/23/95-SiS-496-497/A/B-2A4IBZ11C-00
	ROM_SYSTEM_BIOS( 7, "4dps04", "Tomato 4DPS #4")
	ROMX_LOAD( "4dps04.bin", 0x00000, 0x20000, CRC(f704be6a) SHA1(536c17c2a26e8a0f3bc3ddf6b8daa2f694905c24), ROM_BIOS(7))
	// 8: 01/10/96-SiS496-497/A/B-2A4IBZ11C-00 / 4DPS VER 1.6 (2005962A4IBZ11)
	ROM_SYSTEM_BIOS( 8, "160", "Tomato 4DPS v1.6")
	ROMX_LOAD( "4dps_160.bin", 0x00000, 0x20000, CRC(27d23966) SHA1(3fea7573c1897a4bd6d09e4ffc4e26372a25e43a), ROM_BIOS(8))
ROM_END


/***************************************************************************
  Game driver(s)
***************************************************************************/

//    YEAR  NAME       PARENT   COMPAT   MACHINE    INPUT  CLASS         INIT            COMPANY        FULLNAME                FLAGS
COMP( 199?, sis85c496, 0, 0, sis496_voodoo1, 0, sis496_voodoo1_state, empty_init, "Hack Inc.", "486 motherboards using the SiS 85C496/85C497 chipset + 3dfx Voodoo 1", MACHINE_NOT_WORKING ) // 4sim002 crashes while enabling cache?

COMP( 1995, atc1425a,  0, 0, sis496, 0, sis496_state, empty_init, "A-Trend", "ATC-1425A (SiS 85C496/85C497)", MACHINE_NOT_WORKING ) // -bios 2 punts to Award BootBlock, -bios 0 and 1 crashes
COMP( 1996, atc1425b,  0, 0, sis496, 0, sis496_state, empty_init, "A-Trend", "ATC-1425B (SiS 85C496/85C497)", MACHINE_NOT_WORKING ) // punts to Award BootBlock

COMP( 1995, abpi4,     0, 0, sis496, 0, sis496_state, empty_init, "ABit", "AB-PI4 / AB-PI4T (SiS 85C496/85C497)", MACHINE_NOT_WORKING ) // hangs during irq check
COMP( 1995, abpm4,     0, 0, sis496, 0, sis496_state, empty_init, "ABit", "AB-PM4 (SiS 85C496/85C497)", MACHINE_NOT_WORKING ) // hangs during irq check
COMP( 1995, abpv4,     0, 0, sis496, 0, sis496_state, empty_init, "ABit", "AB-PV4 (SiS 85C496/85C497)", MACHINE_NOT_WORKING ) // hangs during irq check

COMP( 199?, aoap43,    0, 0, sis496, 0, sis496_state, empty_init, "AOpen", "AP43 (SiS 85C496/85C497)",  MACHINE_NOT_WORKING ) // crashes while enabling cache?

COMP( 1994, a486sp3,   0, 0, sis496, 0, sis496_state, empty_init, "Asus", "PVI-486SP3 (SiS 85C496/85C497)", MACHINE_NOT_WORKING ) // hangs during irq check
COMP( 1995, aa486s,    0, 0, sis496, 0, sis496_state, empty_init, "Asus", "PCI/I-A486S (SiS 85C496/85C497)", MACHINE_NOT_WORKING ) // -bios 0 crashes on boot, -bios 1 hardlocks MAME

COMP( 199?, ch486spm,  0, 0, sis496, 0, sis496_state, empty_init, "Chaintech", "486SPM", MACHINE_NOT_WORKING )  // both versions used to show Award BootBlock, now show a black screen
COMP( 199?, ch4spi,    0, 0, sis496, 0, sis496_state, empty_init, "Chaintech", "4SPI", MACHINE_NOT_WORKING )    // used to come up, now black screen

COMP( 1995, ft486f55,  0, 0, sis496, 0, sis496_state, empty_init, "Freetech", "486FT55", MACHINE_NOT_WORKING )  // used to show Award BootBlow, now black screen

COMP( 199?, jakms41,   0, 0, sis496, 0, sis496_state, empty_init, "Jamicon", "KM-S4-1 VER 1.1", MACHINE_NOT_WORKING )
COMP( 199?, jwj446a,   0, 0, sis496, 0, sis496_state, empty_init, "Jetway",  "J-446A", MACHINE_NOT_WORKING )    // BIOS 0 shows BootBlock, but hangs on beep, BIOS 1 hangs, used to show BootBlock

COMP( 199?, ls486e,    0, 0, sis496, 0, sis496_state, empty_init, "LuckyStar",   "LS-486E Rev:C", MACHINE_NOT_WORKING ) // All versions POST, except LH5 (BootBlock)

COMP( 199?, ms4144,    0, 0, sis496, 0, sis496_state, empty_init, "MSI",         "MS-4144", MACHINE_NOT_WORKING ) // WF53 and WF54 used to show Award BootBlock, now hang

COMP( 199?, so30h,     0, 0, sis496, 0, sis496_state, empty_init, "SOYO", "30H", MACHINE_NOT_WORKING ) // used to show Award BootBlock, now hangs
COMP( 199?, so4saw2,   0, 0, sis496, 0, sis496_state, empty_init, "SOYO", "SY-4SAW2", MACHINE_NOT_WORKING ) // BIOS 0-3 show a black screen (2-3 used to display BootBlock), #4 exits MAME with "Called modrm_to_EA with modrm value ED!"

COMP( 199?, zito4dps,  0, 0, sis496, 0, sis496_state, empty_init, "ZIDA", "Tomato board 4DPS", MACHINE_NOT_WORKING ) // BIOS 0,2,5,7,8 POST, BIOS 1,3,4,6 show a black screen




pcksurfer.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***************************************************************************************************

Datawind Pocket Surfer.

Pre-smartphone era pocket Internet terminal.

https://web.archive.org/web/20060221032558/http://www.datawind.com/
https://web.archive.org/web/20060221032551/http://www.datawind.com/specs.html
https://uk.pcmag.com/first-looks/7579/datawind-pocketsurfer

CPU Sharp LH79524-NOF (package BGA-208).
SIM300 v7.03 GSM/GPRS modem.

Tear down of a production unit: https://www.youtube.com/watch?v=lQi8veu3ugU

PCB:
                                 _____
  _______________________________|   |__  _______________
 _|               _____________  |USB|  |_|              \_____________________
_|    ________    |            |                                _______        |
|     |DW-    |   |   SIM      |                                |STB5610       |
|     |SBTM1  |   |  SOCKET    |                                |      |       |
|     |Bluetooth  |   AMD      |                                |______|       |
|     |_______|   |  COMMS     | ___         _____________            _______  |
|   ___________   |            ||___|  ___   | IS42S16400B|           |      | |
|   |U4 Flash  |  |SIM300 v7.03|       |  |  | DRAM       | ________  |IS42S16400B
|   |          |  |  GSM/GPRS  | LVX457->_|  |____________| |SHARP  | |DRAM  | |
|   |__________|  |____________|       ___                  |LH79524| |      | |
|                           ___        |  |                 |NOF    | |      | |
|                    LCX125->__|       |_<-LCX125           |_______| |______| |
|______________________________________________________________________________|

Chip labeled "31314 3A05U 511AD" inside the DW-SBTM1 module.


    TODO:
    - Everything
*/

#include "emu.h"

#include "cpu/arm7/arm7.h"


namespace {

class pcksurfer_state : public driver_device
{
public:
	pcksurfer_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void pcksurfer(machine_config &config);

protected:
	required_device<cpu_device> m_maincpu;
};


static INPUT_PORTS_START( pcksurfer )
INPUT_PORTS_END


void pcksurfer_state::pcksurfer(machine_config &config)
{
	// Basic machine hardware
	ARM7(config, m_maincpu, 76'205'000); // Sharp LH79524-NOF (BGA-208)
}

// ROM definitions

ROM_START( pcksurfer )
	ROM_REGION32_LE( 0x800100, "maincpu", 0 )
	ROM_LOAD( "mxl29lv640mbt.u4", 0x000000, 0x800100, CRC(39896d0b) SHA1(98904409483b22c77adb9495147c2e433a607e61) )

	ROM_REGION32_LE( 0x2000, "bootrom", 0 )
	ROM_LOAD( "lh79524.bootrom.bin", 0x0000, 0x2000, NO_DUMP )

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD( "attiny28l.bin", 0x000, 0x800, NO_DUMP ) // 2K bytes of Flash
ROM_END

} // anonymous namespace

//    YEAR  NAME       PARENT COMPAT MACHINE    INPUT      CLASS            INIT        COMPANY     FULLNAME                     FLAGS
COMP( 2006, pcksurfer, 0,     0,     pcksurfer, pcksurfer, pcksurfer_state, empty_init, "Datawind", "Pocket Surfer (prototype)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



pcm.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************

PC/M by Miodrag Milanovic

http://www.li-pro.net/pcm.phtml  (in German)

2009-05-12 Preliminary driver.
2011-02-14 Added keyboard (from terminal).
2020-07-15 Added banking

Commands:
1 select memory bank 1
2 select memory bank 2
B
C start cp/m from the inbuilt CCP
D Debugger
Fx Format disk A or B
G  Jump to address
I List files on tape
L filename.typ  Load file from tape
R read from disk
S filename aaaa / bbbb save a file to tape
V verify
W write to disk
X
Z set tape baud (1200, 2400, 3600 (default), 4800)
filename   start running this .COM file

Therefore if you enter random input, it will lock up while trying to
load up a file of that name. Filenames on disk and tape are of the
standard 8.3 format. You must specify an extension.

Here is an example of starting the debugger, executing a command in
it, then exiting back to the monitor.

D
U
Q

In practice, the I and R commands produce an error, while all disk
commands are directed to tape. The F command lists the files on a
tape.

ToDo:
- Add NMI generator
- Find out if there really is any floppy-disk feature - the schematic
  has no mention of it. (it might be the ram drives)
- Add the 6 LEDs.
- Need software
- MNW until we can be sure it works as intended.

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/z80daisy.h"
#include "imagedev/cassette.h"
#include "k7659kb.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "machine/z80pio.h"
#include "machine/ram.h"
#include "sound/spkrdev.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class pcm_state : public driver_device
{
public:
	pcm_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_pio_s(*this, "pio_s")
		, m_pio_u(*this, "pio_u")
		, m_ctc_s(*this, "ctc_s")
		, m_ctc_u(*this, "ctc_u")
		, m_speaker(*this, "speaker")
		, m_cass(*this, "cassette")
		, m_p_chargen(*this, "chargen")
		, m_bank(*this, {"bankr", "bankw", "bank2", "bank3"})
	{ }

	void pcm(machine_config &config);

private:
	u8 port85_r();
	void port82_w(int state);
	void port85_w(u8 data);
	void port94_w(u8 data);
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	bool m_cone = 0;
	u8 m_port85 = 0U;
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;
	required_device<z80_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<z80pio_device> m_pio_s;
	required_device<z80pio_device> m_pio_u;
	required_device<z80ctc_device> m_ctc_s;
	required_device<z80ctc_device> m_ctc_u;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cass;
	required_region_ptr<u8> m_p_chargen;
	required_memory_bank_array<4> m_bank;
};


void  pcm_state::port82_w(int state)
{
	if (state)
	{
		m_cone ^= 1;
		m_speaker->level_w(m_cone);
	}
}


/* PIO connections as far as i could decipher

PortA is input and connects to the keyboard
PortB is mostly output and connects to a series of LEDs,
      but also carries the cassette control & data lines.

A0-A6 ascii codes from the keyboard
A7 strobe, high while a key is pressed
B0 power indicator LED
B1 Run/Stop LED
B2 Sound on/off LED
B3 n/c
B4 High=Save, Low=Load LED
B5 Motor On LED
B6 Save data
B7 Load data
There is also a HALT LED, connected directly to the processor.
*/


u8 pcm_state::port85_r()
{
	u8 data = m_port85 & 0x7f;

	if ((m_cass)->input() > 0.03)
		data |= 0x80;

	return data;
}

void pcm_state::port85_w(u8 data)
{
	m_cass->change_state(BIT(data, 5) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
	m_cass->output( BIT(data, 6) ? -1.0 : +1.0);
	m_port85 = data;
}

/* Banking:
d0,d1,d2 - select which 64k bank (first 3 are internal)
d3,d4,d5 - external
d6 - force c000-ffff of 64k bank
d7(low) - switch in the roms to 0000-1fff
Real hardware can disable all ram and crash the system. We don't emulate that. */

void pcm_state::port94_w(u8 data)
{
	u8 bank = BIT(data, 0, 3);
	if (bank < 3)
	{
		m_bank[0]->set_entry(bank);
		m_bank[1]->set_entry(bank);
		m_bank[2]->set_entry(bank);
		m_bank[3]->set_entry(bank);
	}

	if (BIT(data, 6))
		m_bank[3]->set_entry(0); // via D13 and D51.1

	if (!BIT(data, 7))
	{
		m_bank[0]->set_entry(3); // via D12 and D53.1
		m_bank[1]->set_entry(3);
	}
}

void pcm_state::mem_map(address_map &map)
{
	map(0x0000, 0x1fff).bankr("bankr").bankw("bankw");
	map(0x2000, 0xbfff).bankrw("bank2");
	map(0xc000, 0xffff).bankrw("bank3");
}

void pcm_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x80, 0x83).rw(m_ctc_s, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write)); // system CTC
	map(0x84, 0x87).rw(m_pio_s, FUNC(z80pio_device::read), FUNC(z80pio_device::write)); // system PIO
	map(0x88, 0x8B).rw("sio", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w)); // SIO
	map(0x8C, 0x8F).rw(m_ctc_u, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write)); // user CTC
	map(0x90, 0x93).rw(m_pio_u, FUNC(z80pio_device::read), FUNC(z80pio_device::write)); // user PIO
	map(0x94, 0x97).w(FUNC(pcm_state::port94_w));
	//map(0x98, 0x9B) // NMI generator for debugging
	map(0x9C, 0x9F); // io ports available to the user
	// disk controller?
}

/* Input ports */
static INPUT_PORTS_START( pcm )
INPUT_PORTS_END

void pcm_state::machine_start()
{
	u8* r = m_ram->pointer();
	u8 *m = memregion("maincpu")->base();

	save_item(NAME(m_cone));
	save_item(NAME(m_port85));

	m_bank[0]->configure_entries(0, 3, r, 0x10000);
	m_bank[1]->configure_entries(0, 4, r, 0x10000);
	m_bank[2]->configure_entries(0, 3, r+0x2000, 0x10000);
	m_bank[3]->configure_entries(0, 3, r+0xc000, 0x10000);
	m_bank[0]->configure_entry(3, m);
}

void pcm_state::machine_reset()
{
	port94_w(0);   // setup initial state of banks
}

u32 pcm_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 sy=0,ma=0x400;

	for (u8 y = 0; y < 16; y++)
	{
		for (u8 ra = 0; ra < 8; ra++)
		{
			u16 *p = &bitmap.pix(sy++);

			for (u16 x = ma; x < ma + 64; x++)
			{
				u8 const chr = m_ram->pointer()[x + 0xf800];

				u8 const gfx = m_p_chargen[(chr<<3) | ra];

				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma+=64;
	}
	return 0;
}


static const z80_daisy_config daisy_chain[] =
{
	{ "ctc_s" },     /* System ctc */
	{ "pio_s" },     /* System pio */
	{ "sio" },       /* sio */
	{ "pio_u" },     /* User pio */
	{ "ctc_u" },     /* User ctc */
	{ nullptr }
};


/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_pcm )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END

void pcm_state::pcm(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(10'000'000) /4);
	m_maincpu->set_addrmap(AS_PROGRAM, &pcm_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &pcm_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(pcm_state::screen_update));
	screen.set_size(64*8, 16*8);
	screen.set_visarea(0, 64*8-1, 0, 16*8-1);
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_pcm);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* Sound */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	/* Devices */
	K7659_KEYBOARD(config, K7659_KEYBOARD_TAG, 0);
	CASSETTE(config, m_cass);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	Z80PIO(config, m_pio_u, XTAL(10'000'000)/4);
	m_pio_u->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80PIO(config, m_pio_s, XTAL(10'000'000)/4);
	m_pio_s->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio_s->in_pa_callback().set(K7659_KEYBOARD_TAG, FUNC(k7659_keyboard_device::read));
	m_pio_s->in_pb_callback().set(FUNC(pcm_state::port85_r));
	m_pio_s->out_pb_callback().set(FUNC(pcm_state::port85_w));

	Z80SIO(config, "sio", XTAL(10'000'000) /4);

	Z80CTC(config, m_ctc_u, 10_MHz_XTAL / 4);
	m_ctc_u->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80CTC(config, m_ctc_s, 10_MHz_XTAL / 4);
	m_ctc_s->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc_s->zc_callback<0>().set("sio", FUNC(z80sio_device::rxca_w));
	m_ctc_s->zc_callback<0>().append("sio", FUNC(z80sio_device::txca_w));
	m_ctc_s->zc_callback<1>().set("sio", FUNC(z80sio_device::rxtxcb_w));
	m_ctc_s->zc_callback<2>().set(FUNC(pcm_state::port82_w));  // speaker

	// internal ram: bank0 for cp/m; banks 1 and 2 are ram-drives; last 2k is dummy for rom-write
	RAM(config, RAM_TAG).set_default_size("194K");
}

/* ROM definition */
ROM_START( pcm )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "v202", "Version 2.02" )
	ROMX_LOAD( "bios_v202.d14", 0x0000, 0x0800, CRC(27c24892) SHA1(a97bf9ef075de91330dc0c7cfd3bb6c7a88bb585), ROM_BIOS(0))
	ROMX_LOAD( "bios_v202.d15", 0x0800, 0x0800, CRC(e9cedc70) SHA1(913c526283d9289d0cb2157985bb48193df7aa16), ROM_BIOS(0))
	ROMX_LOAD( "bios_v202.d16", 0x1000, 0x0800, CRC(abe12001) SHA1(d8f0db6b141736d7715d588384fa49ab386bcc55), ROM_BIOS(0))
	ROMX_LOAD( "bios_v202.d17", 0x1800, 0x0800, CRC(2d48d1cc) SHA1(36a825140124dbe10d267fdf28b3eacec6f6d556), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v210", "Version 2.10" )
	ROMX_LOAD( "bios_v210.d14", 0x0000, 0x0800, CRC(45923112) SHA1(dde922533ebd0f6ac06d25b9786830ee3c7178b9), ROM_BIOS(1))
	ROMX_LOAD( "bios_v210.d15", 0x0800, 0x0800, CRC(e9cedc70) SHA1(913c526283d9289d0cb2157985bb48193df7aa16), ROM_BIOS(1))
	ROMX_LOAD( "bios_v210.d16", 0x1000, 0x0800, CRC(ee9ed77b) SHA1(12ea18e3e280f2a0657ff11c7bcdd658d325235c), ROM_BIOS(1))
	ROMX_LOAD( "bios_v210.d17", 0x1800, 0x0800, CRC(2d48d1cc) SHA1(36a825140124dbe10d267fdf28b3eacec6f6d556), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "v330", "Version 3.30" )
	ROMX_LOAD( "bios_v330.d14", 0x0000, 0x0800, CRC(9bbfee10) SHA1(895002f2f4c711278f1e2d0e2a987e2d31472e4f), ROM_BIOS(2))
	ROMX_LOAD( "bios_v330.d15", 0x0800, 0x0800, CRC(4f8d5b40) SHA1(440b0be4cf45a5d450f9eb7684ceb809450585dc), ROM_BIOS(2))
	ROMX_LOAD( "bios_v330.d16", 0x1000, 0x0800, CRC(93fd0d91) SHA1(c8f1bbb63eca3c93560622581ecbb588716aeb91), ROM_BIOS(2))
	ROMX_LOAD( "bios_v330.d17", 0x1800, 0x0800, CRC(d8c7ce33) SHA1(9030d9a73ef1c12a31ac2cb9a593fb2a5097f24d), ROM_BIOS(2))

	ROM_REGION(0x0800, "chargen",0)
	ROM_LOAD( "charrom.d113", 0x0000, 0x0800, CRC(5684b3c3) SHA1(418054aa70a0fd120611e32059eb2051d3b82b5a))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY          FULLNAME  FLAGS */
COMP( 1988, pcm,  0,      0,      pcm,     pcm,   pcm_state, empty_init, "Mugler/Mathes", "PC/M",   MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



pcmx2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * Siemens PC-MX2
 *
 * Sources:
 *  - http://www.cpu-ns32k.net/Siemens.html
 *  - https://oldcomputers-ddns.org/public/pub/rechner/siemens/mx-rm/pc-mx2/manuals/pc-mx2_betriebsanleitung_1987_(v2-1a).pdf
 *  - https://oldcomputers-ddns.org/public/pub/rechner/siemens/mx-rm/pc-mx2/manuals/pc-mx2_pc2000_9780_logik.pdf
 *
 * TODO:
 *  - front panel
 *  - storager
 */
/*
 * WIP notes:
 *  - currently there's no disk controller, so only possible to boot into the monitor
 *  - start with a terminal connected to SERAD port 0, i.e.: mame pcmx2 -slot:3:serad:port0 terminal
 *  - configure terminal for 38400 bps, 7 data bits, odd parity
 *  - set CPUAP board Boot Option to Monitor
 *  - press Enter at the "*" prompt to display monitor help
 */

#include "emu.h"

#include "bus/multibus/multibus.h"
#include "bus/multibus/cpuap.h"
//#include "bus/multibus/dueai.h"
#include "bus/multibus/serad.h"

#define VERBOSE 0
#include "logmacro.h"

namespace {

class pcmx2_state : public driver_device
{
public:
	pcmx2_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_bus(*this, "bus")
	{
	}

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

public:
	// machine config
	void pcmx2(machine_config &config);

private:
	required_device<multibus_device> m_bus;
};

void pcmx2_state::machine_start()
{
}

void pcmx2_state::machine_reset()
{
}

static void pcmx2_cards(device_slot_interface &device)
{
	device.option_add("cpuap", CPUAP);
	//device.option_add("dueai", DUEAI);
	device.option_add("serad", SERAD);
}

void pcmx2_state::pcmx2(machine_config &config)
{
	MULTIBUS(config, m_bus, 20_MHz_XTAL / 2);

	MULTIBUS_SLOT(config, "slot1", m_bus, pcmx2_cards, nullptr, false); // DTC 86-1 or Storager
	MULTIBUS_SLOT(config, "slot2", m_bus, pcmx2_cards, nullptr, false);
	MULTIBUS_SLOT(config, "slot3", m_bus, pcmx2_cards, "serad", false);
	MULTIBUS_SLOT(config, "slot4", m_bus, pcmx2_cards, nullptr, false);
	MULTIBUS_SLOT(config, "slot5", m_bus, pcmx2_cards, nullptr, false);
	MULTIBUS_SLOT(config, "slot6", m_bus, pcmx2_cards, "cpuap", false);
	MULTIBUS_SLOT(config, "slot7", m_bus, pcmx2_cards, nullptr, false); // MEM
	MULTIBUS_SLOT(config, "slot8", m_bus, pcmx2_cards, nullptr, false);
}

ROM_START(pcmx2)
ROM_END

}

/*   YEAR   NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY    FULLNAME  FLAGS */
COMP(1985,  pcmx2, 0,      0,      pcmx2,   0,     pcmx2_state, empty_init, "Siemens", "PC-MX2", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)



pcw.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Kevin Thacker
/******************************************************************************

    pcw.cpp
    system driver

    Kevin Thacker [MESS driver]

    Thankyou to Jacob Nevins, Richard Fairhurst and Cliff Lawson,
    for their documentation that I used for the development of this
    driver.

    PCW came in 4 forms.
    PCW8256, PCW8512, PCW9256, PCW9512

    These systems were nicknamed "Joyce", apparently after a secretary who worked at
    Amstrad plc.

    These machines were designed for wordprocessing and other business applications.

    The computer came with Locoscript (wordprocessor by Locomotive Software Ltd),
    and CP/M+ (3.1).

    The original PCW8256 system came with a keyboard, green-screen monitor
    (which had 2 3" 80 track, double sided disc drives mounted vertically in it),
    and a dedicated printer. The other systems had different design but shared the
    same hardware.

    Since it was primarily designed as a wordprocessor, there were not many games
    written.
    Some of the games available:
     - Head Over Heels
     - Infocom adventures (Hitchhikers Guide to the Galaxy etc)

    However, it can use the CP/M OS, there is a large variety of CPM software that will
    run on it!!!!!!!!!!!!!!

    Later systems had:
        - black/white monitor,
        - dedicated printer was removed, and support for any printer was added
        - 3" internal drive replaced by a 3.5" drive

    All the logic for the system, except the FDC was found in a Amstrad designed custom
    chip.

    In the original PCW8256, there was no boot-rom. A boot-program was stored in the printer
    chip, and this was activated when the PCW was first switched on. AFAIK there are no
    dumps of this program, so I have written my own replacement.

    The boot-program performs a simple task: Load sector 0, track 0 to &f000 in ram, and execute
    &f010.

    From here CP/M is booted, and the appropriate programs can be run.

    The hardware:
       - Z80 CPU running at 3.4 MHz
       - UPD765 FDC
       - mono display
       - beep (a fixed Hz tone which can be turned on/off)
       - 720x256 (PAL) bitmapped display, 720x200 (NTSC) bitmapped display
       - Amstrad CPC6128 style keyboard

  If there are special roms for any of the PCW series I would be interested in them
  so I can implement the driver properly.

  From comp.sys.amstrad.8bit FAQ:

  "Amstrad made the following PCW systems :

  - 1) PCW8256
  - 2) PCW8512
  - 3) PCW9512
  - 4) PCW9512+
  - 5) PcW10
  - 6) PcW16

  1 had 180K drives, 2 had a 180K A drive and a 720K B drive, 3 had only
  720K drives. All subsequent models had 3.5" disks using CP/M format at
  720K until 6 when it switched to 1.44MB in MS-DOS format. The + of
  model 4 was that it had a "real" parallel interface so could be sold
  with an external printer such as the Canon BJ10. The PcW10 wasn't
  really anything more than 4 in a more modern looking case.

  The PcW16 is a radical digression who's sole "raison d'etre" was to
  make a true WYSIWYG product but this meant a change in the screen and
  processor (to 16MHz) etc. which meant that it could not be kept
  compatible with the previous models (though documents ARE compatible)"


  TODO:
  - Printer hardware emulation (8256 etc)
  - Parallel port emulation (9512, 9512+, 10)
  - emulation of serial hardware
  - emulation of other hardware...?
 ******************************************************************************/
#include "emu.h"
// pcw video hardware
#include "pcw.h"

#include "cpu/z80/z80.h"
#include "machine/i8243.h"
#include "machine/upd765.h"
// pcw/pcw16 beeper
#include "sound/beep.h"
#include "machine/ram.h"
#include "machine/rescap.h"
#include "render.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "pcw.lh"

#define LOG_PRN      (1U << 1)
#define LOG_STROBE   (1U << 2)
#define LOG_PAR      (1U << 3)
#define LOG_EXP      (1U << 4)
#define LOG_MEM      (1U << 5)
#define LOG_SYS      (1U << 6)
#define LOG_BANK     (1U << 7)
#define LOG_RRAM     (1U << 8)
#define LOG_IRQ      (1U << 9)

//#define VERBOSE (LOG_SYS)
//#define LOG_OUTPUT_FUNC printf
#include "logmacro.h"

#define LOGPRN(...)     LOGMASKED(LOG_PRN,      __VA_ARGS__)
#define LOGSTROBE(...)  LOGMASKED(LOG_STROBE,   __VA_ARGS__)
#define LOGPAR(...)     LOGMASKED(LOG_PAR,      __VA_ARGS__)
#define LOGEXP(...)     LOGMASKED(LOG_EXP,      __VA_ARGS__)
#define LOGMEM(...)     LOGMASKED(LOG_MEM,      __VA_ARGS__)
#define LOGSYS(...)     LOGMASKED(LOG_SYS,      __VA_ARGS__)
#define LOGBANK(...)    LOGMASKED(LOG_BANK,     __VA_ARGS__)
#define LOGRRAM(...)    LOGMASKED(LOG_RRAM,     __VA_ARGS__)
#define LOGIRQ(...)     LOGMASKED(LOG_IRQ,      __VA_ARGS__)

static const uint8_t half_step_table[4] = { 0x01, 0x02, 0x04, 0x08 };
static const uint8_t full_step_table[4] = { 0x03, 0x06, 0x0c, 0x09 };

void pcw_state::pcw_update_interrupt_counter()
{
	/* never increments past 15! */
	if (m_interrupt_counter==0x0f)
		return;

	/* increment count */
	m_interrupt_counter++;
}


// set/reset INT and NMI lines
void pcw_state::pcw_update_irqs()
{
	// set NMI line, remains set until FDC interrupt type is changed
	if(m_nmi_flag != 0)
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
	else
		m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);

	// set IRQ line, timer pulses IRQ line, all other devices hold it as necessary
	if(m_fdc_interrupt_code == 1 && (m_system_status & 0x20))
	{
		m_maincpu->set_input_line(0, ASSERT_LINE);
		return;
	}

	if(m_timer_irq_flag != 0)
	{
		m_maincpu->set_input_line(0, ASSERT_LINE);
		return;
	}

	m_maincpu->set_input_line(0, CLEAR_LINE);
}

TIMER_CALLBACK_MEMBER(pcw_state::pcw_timer_pulse)
{
	m_timer_irq_flag = 0;
	pcw_update_irqs();
}

/* callback for 1/300ths of a second interrupt */
TIMER_DEVICE_CALLBACK_MEMBER(pcw_state::pcw_timer_interrupt)
{
	pcw_update_interrupt_counter();

	m_timer_irq_flag = 1;
	pcw_update_irqs();
	m_pulse_timer->adjust(attotime::from_usec(100), 0, attotime::from_usec(100));
}

/* PCW uses UPD765 in NON-DMA mode. FDC Ints are connected to /INT or
 * /NMI depending on choice (see system control below)
 * fdc interrupt callback. set/clear fdc int */
void pcw_state::pcw_fdc_interrupt(int state)
{
	if (!state)
		m_system_status &= ~(1<<5);
	else
	{
		m_system_status |= (1<<5);
		if(m_fdc_interrupt_code == 0)  // NMI is held until interrupt type is changed
			m_nmi_flag = 1;
	}
}


/* Memory is banked in 16k blocks.

    The upper 16 bytes of block 3, contains the keyboard
    state. This is updated by the hardware.

    block 3 could be paged into any bank, and this explains the
    setup of the memory below.
*/
void pcw_state::pcw_map(address_map &map)
{
	map(0x0000, 0x3fff).bankr(m_rdbanks[0]).bankw(m_wrbanks[0]);
	map(0x4000, 0x7fff).bankr(m_rdbanks[1]).bankw(m_wrbanks[1]);
	map(0x8000, 0xbfff).bankr(m_rdbanks[2]).bankw(m_wrbanks[2]);
	map(0xc000, 0xffff).bankr(m_rdbanks[3]).bankw(m_wrbanks[3]);
}


/* Keyboard is read by the MCU and sent as serial data to the gate array ASIC */
uint8_t pcw_state::pcw_keyboard_r(offs_t offset)
{
	return m_iptlines[offset]->read();
}

uint8_t pcw_state::pcw_keyboard_data_r(offs_t offset)
{
	return m_mcu_keyboard_data[offset];
}

/* -----------------------------------------------------------------------
 * PCW Banking
 * ----------------------------------------------------------------------- */

void pcw_state::pcw_update_read_memory_block(int block, int bank)
{
	address_space &space = m_maincpu->space(AS_PROGRAM);

	/* bank 3? */
	if (bank == 3)
	{
		/* when upper 16 bytes are accessed use keyboard read handler */
		space.install_read_handler( block * 0x04000 + 0x3ff0, block * 0x04000 + 0x3fff, read8sm_delegate(*this, FUNC(pcw_state::pcw_keyboard_data_r)));
		LOGMEM("MEM: read block %i -> bank %i\n", block, bank);
	}
	else
	{
		/* restore bank handler across entire block */
		space.install_read_bank(block * 0x04000 + 0x0000, block * 0x04000 + 0x3fff, m_rdbanks[block].target());
		LOGMEM("MEM: read block %i -> bank %i\n", block, bank);
	}
	m_rdbanks[block]->set_base(m_ram->pointer() + ((bank * 0x4000) % m_ram->size()));
}

void pcw_state::pcw_update_write_memory_block(int block, int bank)
{
	m_wrbanks[block]->set_base(m_ram->pointer() + ((bank * 0x4000) % m_ram->size()));
	LOGMEM("MEM: write block %i -> bank %i\n", block, bank);
}


/* ----------------------------------------------------------------------- */

/* &F4 O  b7-b4: when set, force memory reads to access the same bank as
writes for &C000, &0000, &8000, and &4000 respectively */

void pcw_state::pcw_update_mem(int block, int data)
{
	/* expansion ram select.
	    if block is 0-7, selects internal ram instead for read/write
	    */
	if (data & 0x080)
	{
		int bank;

		/* same bank for reading and writing */
		bank = data & 0x7f;

		pcw_update_read_memory_block(block, bank);
		pcw_update_write_memory_block(block, bank);
	}
	else
	{
		/* specify a different bank for reading and writing */
		int write_bank;
		int read_bank;
		int mask=0;

		switch (block)
		{
			case 0:
			{
				mask = (1<<6);
			}
			break;

			case 1:
			{
				mask = (1<<4);
			}
			break;

			case 2:
			{
				mask = (1<<5);
			}
			break;

			case 3:
			{
				mask = (1<<7);
			}
			break;
		}

		if (m_bank_force & mask)
		{
			read_bank = data & 0x07;
		}
		else
		{
			read_bank = (data>>4) & 0x07;
		}

		pcw_update_read_memory_block(block, read_bank);

		write_bank = data & 0x07;
		pcw_update_write_memory_block(block, write_bank);
	}

	/* if boot is active, page in fake ROM */
/*  if ((m_boot) && (block==0))
    {
        unsigned char *FakeROM;

        FakeROM = &memregion("maincpu")->base()[0x010000];

        m_rdbanks[0]->set_base(FakeROM);
    }*/
}

/* from Jacob Nevins docs */
int pcw_state::pcw_get_sys_status()
{
	return
		m_interrupt_counter
		| (m_screen->vblank() ? 0x40 : 0x00)
		| (ioport("EXTRA")->read() & 0x010)
		| (m_system_status & 0x20);
}

uint8_t pcw_state::pcw_interrupt_counter_r()
{
	int data;

	/* from Jacob Nevins docs */

	/* get data */
	data = pcw_get_sys_status();
	/* clear int counter */
	m_interrupt_counter = 0;
	/* check interrupts */
	pcw_update_irqs();
	/* return data */
	LOGIRQ("IRQ counter read, returning %02x\n", data);
	return data;
}


void pcw_state::pcw_bank_select_w(offs_t offset, uint8_t data)
{
	LOGBANK("BANK: %2x %x\n", offset, data);
	m_banks[offset] = data;

	pcw_update_mem(offset, data);
	LOGBANK("RAM Banks: %02x %02x %02x %02x Lock:%02x",m_banks[0],m_banks[1],m_banks[2],m_banks[3],m_bank_force);
}

void pcw_state::pcw_bank_force_selection_w(uint8_t data)
{
	m_bank_force = data;

	pcw_update_mem(0, m_banks[0]);
	pcw_update_mem(1, m_banks[1]);
	pcw_update_mem(2, m_banks[2]);
	pcw_update_mem(3, m_banks[3]);
}


void pcw_state::pcw_roller_ram_addr_w(uint8_t data)
{
	/*
	Address of roller RAM. b7-5: bank (0-7). b4-1: address / 512. */

	m_roller_ram_addr = (((data>>5) & 0x07)<<14) |
							((data & 0x01f)<<9);
	LOGRRAM("Roller-RAM: Address set to 0x%05x\n", m_roller_ram_addr);
}

void pcw_state::pcw_pointer_table_top_scan_w(uint8_t data)
{
	m_roller_ram_offset = data;
	LOGRRAM("Roller-RAM: offset set to 0x%05x\n", m_roller_ram_offset);
}

void pcw_state::pcw_vdu_video_control_register_w(uint8_t data)
{
	m_vdu_video_control_register = data;
	LOGRRAM("Roller-RAM: control reg set to 0x%02x\n", data);
}

void pcw_state::pcw_system_control_w(uint8_t data)
{
	LOGSYS("SYSTEM CONTROL: %d\n", data);

	switch (data)
	{
		/* end bootstrap */
		case 0:
		{
			m_boot = 0;
			pcw_update_mem(0, m_banks[0]);
		}
		break;

		/* reboot */
		case 1:
		{
			m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
			LOGSYS("SYS: Reboot");
		}
		break;

		/* connect fdc interrupt to nmi */
		case 2:
		{
			int fdc_previous_interrupt_code = m_fdc_interrupt_code;

			m_fdc_interrupt_code = 0;

			/* previously connected to INT? */
			if (fdc_previous_interrupt_code == 1)
			{
				/* yes */

				pcw_update_irqs();
			}

		}
		break;


		/* connect fdc interrupt to interrupt */
		case 3:
		{
			int fdc_previous_interrupt_code = m_fdc_interrupt_code;

			/* connect to INT */
			m_fdc_interrupt_code = 1;

			/* previously connected to NMI? */
			if (fdc_previous_interrupt_code == 0)
			{
				/* yes */

				/* clear nmi interrupt */
				m_nmi_flag = 0;
			}

			/* re-issue interrupt */
			pcw_update_irqs();
		}
		break;


		/* connect fdc interrupt to neither */
		case 4:
		{
			int fdc_previous_interrupt_code = m_fdc_interrupt_code;

			m_fdc_interrupt_code = 2;

			/* previously connected to NMI or INT? */
			if ((fdc_previous_interrupt_code == 0) || (fdc_previous_interrupt_code == 1))
			{
				/* yes */

				/* Clear NMI */
				m_nmi_flag = 0;
			}
			pcw_update_irqs();

		}
		break;

		/* set fdc terminal count */
		case 5:
		{
			m_fdc->tc_w(true);
		}
		break;

		/* clear fdc terminal count */
		case 6:
		{
			m_fdc->tc_w(false);
		}
		break;

		/* screen on */
		case 7:
		{
		}
		break;

		/* screen off */
		case 8:
		{
		}
		break;

		/* disc motor on */
		case 9:
		{
			floppy_image_device *floppy;
			floppy = m_floppy[0]->get_device();
			if(floppy)
				floppy->mon_w(0);
			floppy = m_floppy[1]->get_device();
			if(floppy)
				floppy->mon_w(0);
		}
		break;

		/* disc motor off */
		case 10:
		{
			floppy_image_device *floppy;
			floppy = m_floppy[0]->get_device();
			if(floppy)
				floppy->mon_w(1);
			floppy = m_floppy[1]->get_device();
			if(floppy)
				floppy->mon_w(1);
		}
		break;

		/* beep on */
		case 11:
		{
			m_beeper->set_state(1);
		}
		break;

		/* beep off */
		case 12:
		{
			m_beeper->set_state(0);
		}
		break;

	}
}

uint8_t pcw_state::pcw_system_status_r()
{
	/* from Jacob Nevins docs */
	uint8_t ret = pcw_get_sys_status();

	return ret;
}

/* read from expansion hardware - additional hardware not part of
the PCW custom ASIC */
uint8_t pcw_state::pcw_expansion_r(offs_t offset)
{
	LOGEXP("pcw expansion r: %04x\n", offset+0x080);

	switch (offset+0x080)
	{
		case 0x0e0:
		{
			/* spectravideo joystick */
			if (ioport("EXTRA")->read() & 0x020)
			{
				return ioport("SPECTRAVIDEO")->read();
			}
			else
			{
				return 0x0ff;
			}
		}

		case 0x09f:
		{
			/* kempston joystick */
			return ioport("KEMPSTON")->read();
		}

		case 0x0e1:
		case 0x0e3:
		{
			return 0x7f;
		}

		case 0x085:
		{
			return 0x0fe;
		}

		case 0x087:
		{
			return 0x0ff;
		}

	}

	/* result from floating bus/no peripheral at this port */
	return 0x0ff;
}

/* write to expansion hardware - additional hardware not part of
the PCW custom ASIC */
void pcw_state::pcw_expansion_w(offs_t offset, uint8_t data)
{
	LOGEXP("pcw expansion w: %04x %02x\n", offset+0x080, data);
}

void pcw_state::pcw_printer_fire_pins(uint16_t pins)
{
	int x,line;
	int32_t feed = (m_paper_feed / 2);

	for(x=feed+PCW_PRINTER_HEIGHT-16;x<feed+PCW_PRINTER_HEIGHT-7;x++)
	{
		line = x % PCW_PRINTER_HEIGHT;
		if((pins & 0x01) == 0)
			m_prn_output->pix(line, m_printer_headpos) = (uint16_t)(pins & 0x01);
		pins >>= 1;
	}
//  if(m_printer_headpos < PCW_PRINTER_WIDTH)
//      m_printer_headpos++;
}

// print error type
// should return 0xF8 if there are no errors
// 0 = underrun
// 1 = printer controller RAM fault
// 3 = bad command
// 5 = print error
// anything else = no printer

// printer status
// bit 7 - bail bar in
// bit 6 - not currently executing a command
// bit 5 - printer RAM is full
// bit 4 - print head is not at left margin
// bit 3 - sheet feeder is present
// bit 2 - paper is present
// bit 1 - busy
// bit 0 - controller fault

/* MCU handlers */
/* I/O ports: (likely to be completely wrong...)
 * (write)
 * all are active low
 * P1: pins 0-7
 * P2 bit 0: pin 8
 * P2 bit 1: serial shift/store clock
 * P2 bit 2: serial shift store data
 * P2 bit 3: serial shift/store strobe
 * P2 bit 4: print head motor
 * P2 bit 5: paper feed motor
 * P2 bit 6: fire pins
 * (read)
 * P2 bit 7: bail bar status (0 if out)
 * T0: Paper sensor (?)
 * T1: Print head location (1 if not at left margin)
 */
TIMER_CALLBACK_MEMBER(pcw_state::pcw_stepper_callback)
{
	LOGPRN("PRN: P2 bits %s %s %s\nSerial: %02x\nHeadpos: %i",
		   m_printer_p2 & 0x40 ? " " : "6",
		   m_printer_p2 & 0x20 ? " " : "5",
		   m_printer_p2 & 0x10 ? " " : "4",
		   m_printer_shift_output,m_printer_headpos);

	if((m_printer_p2 & 0x10) == 0)  // print head motor active
	{
		uint8_t stepper_state = (m_printer_shift_output >> 4) & 0x0f;
		if(stepper_state == full_step_table[(m_head_motor_state + 1) & 0x03])
		{
			m_printer_headpos += 2;
			m_head_motor_state++;
			LOGPRN("Printer head moved forward by 2 to %i\n", m_printer_headpos);
		}
		if(stepper_state == half_step_table[(m_head_motor_state + 1) & 0x03])
		{
			m_printer_headpos += 1;
			m_head_motor_state++;
			LOGPRN("Printer head moved forward by 1 to %i\n", m_printer_headpos);
		}
		if(stepper_state == full_step_table[(m_head_motor_state - 1) & 0x03])
		{
			m_printer_headpos -= 2;
			m_head_motor_state--;
			LOGPRN("Printer head moved back by 2 to %i\n", m_printer_headpos);
		}
		if(stepper_state == half_step_table[(m_head_motor_state - 1) & 0x03])
		{
			m_printer_headpos -= 1;
			m_head_motor_state--;
			LOGPRN("Printer head moved back by 1 to %i\n", m_printer_headpos);
		}
		if(m_printer_headpos < 0)
			m_printer_headpos = 0;
		if(m_printer_headpos > PCW_PRINTER_WIDTH)
			m_printer_headpos = PCW_PRINTER_WIDTH;
		m_head_motor_state &= 0x03;
		m_printer_p2 |= 0x10;
	}
	if((m_printer_p2 & 0x20) == 0)  // line feed motor active
	{
		uint8_t stepper_state = m_printer_shift_output & 0x0f;
		if(stepper_state == full_step_table[(m_linefeed_motor_state + 1) & 0x03])
		{
			m_paper_feed++;
			if(m_paper_feed > PCW_PRINTER_HEIGHT*2)
				m_paper_feed = 0;
			m_linefeed_motor_state++;
		}
		if(stepper_state == half_step_table[(m_linefeed_motor_state + 1) & 0x03])
		{
			m_paper_feed++;
			if(m_paper_feed > PCW_PRINTER_HEIGHT*2)
				m_paper_feed = 0;
			m_linefeed_motor_state++;
		}
		m_linefeed_motor_state &= 0x03;
		m_printer_p2 |= 0x20;
	}
}

TIMER_CALLBACK_MEMBER(pcw_state::pcw_pins_callback)
{
	pcw_printer_fire_pins(m_printer_pins);
	m_printer_p2 |= 0x40;
}

uint8_t pcw_state::mcu_printer_p1_r()
{
	LOGPRN("PRN: MCU reading data from P1\n");
	return m_printer_pins & 0x00ff;
}

void pcw_state::mcu_printer_p1_w(uint8_t data)
{
	m_printer_pins = (m_printer_pins & 0x0100) | data;
	LOGPRN("PRN: Print head position = %i", m_printer_headpos);
	LOGPRN("PRN: MCU writing %02x to P1 [%03x/%03x]\n", data,m_printer_pins, ~m_printer_pins & 0x1ff);
}

uint8_t pcw_state::mcu_printer_p2_r()
{
	uint8_t ret = 0x00;
	LOGPRN("PRN: MCU reading data from P2\n");
	ret |= 0x80;  // make sure bail bar is in
	ret |= (m_printer_p2 & 0x70);
	ret |= (m_printer_pins & 0x100) ? 0x01 : 0x00;  // ninth pin
	ret |= 0x0e;
	return ret;
}

void pcw_state::mcu_printer_p2_w(uint8_t data)
{
	LOGPRN("PRN: MCU writing %02x to P2\n", data);
	m_printer_p2 = data & 0x70;

	// handle shift/store
	m_printer_serial = data & 0x04;  // data
	if (!BIT(m_printer_p2_prev, 1) && BIT(data, 1))  // only update when clock goes positive
	{
		m_printer_shift <<= 1;
		if(m_printer_serial == 0)
			m_printer_shift &= ~0x01;
		else
			m_printer_shift |= 0x01;
	}
	if((data & 0x08) != 0)  // strobe
	{
		LOGSTROBE("Strobe active [%02x]\n",m_printer_shift);
		m_printer_shift_output = m_printer_shift;
		m_prn_stepper->adjust(PERIOD_OF_555_MONOSTABLE(22000,0.00000001));
	}

	if(data & 0x40)
		m_prn_pins->adjust(PERIOD_OF_555_MONOSTABLE(22000,0.0000000068));

	if(data & 0x01)
		m_printer_pins |= 0x0100;
	else
		m_printer_pins &= ~0x0100;
	m_printer_p2_prev = data;
}

// Paper sensor
int pcw_state::mcu_printer_t1_r()
{
	return 1;
}

// Print head location (0 if at left margin, otherwise 1)
int pcw_state::mcu_printer_t0_r()
{
	if(m_printer_headpos == 0)
		return 0;
	else
		return 1;
}

/*
 *  Keyboard MCU (i8048)
 *  P1 = keyboard scan row select (low 8 bits)
 *  P2 = bits 7-4: keyboard scan row select (high 4 bits)
 *       bit 1: keyboard serial clock
 *       bit 0: keyboard serial data
 */
void pcw_state::mcu_transmit_serial(uint8_t bit)
{
	int seq;

	/* Keyboard data is sent in serial from the MCU through the keyboard port, to the ASIC
	   Sends a string of 12-bit sequences, first 4 bits are the RAM location (from &3ff0),
	   then 8 bits for the data to be written there. */
	seq = m_mcu_transmit_count % 12;
	if(seq < 4)
	{
		if(bit == 0)
			m_mcu_selected &= ~(8 >> seq);
		else
			m_mcu_selected |= (8 >> seq);
	}
	else
	{
		seq -= 4;
		if(bit == 0)
			m_mcu_buffer &= ~(128 >> seq);
		else
			m_mcu_buffer |= (128 >> seq);
	}
	m_mcu_transmit_count++;
	if(m_mcu_transmit_count >= 12)
	{
		m_mcu_keyboard_data[m_mcu_selected] = m_mcu_buffer;
		m_mcu_transmit_count = 0;
	}
}

uint8_t pcw_state::mcu_kb_scan_r()
{
	return m_kb_scan_row & 0xff;
}

void pcw_state::mcu_kb_scan_w(uint8_t data)
{
	m_kb_scan_row = (m_kb_scan_row & 0xff00) | data;
}

uint8_t pcw_state::mcu_kb_scan_high_r()
{
	return (m_kb_scan_row & 0xff00) >> 8;
}

void pcw_state::mcu_kb_scan_high_w(uint8_t data)
{
	if((m_mcu_prev & 0x02) && !(data & 0x02))  // bit is transmitted on high-to-low clock transition
	{
		mcu_transmit_serial(data & 0x01);
		m_mcu_transmit_reset_seq = 0;
	}

	if((m_mcu_prev & 0x01) != (data & 0x01))  // two high->low transitions on the data pin signals the beginning of a new transfer
	{
		m_mcu_transmit_reset_seq++;
		if(m_mcu_transmit_reset_seq > 3)
			m_mcu_transmit_count = 0;
	}

	m_kb_scan_row = (m_kb_scan_row & 0x00ff) | ((data & 0xff) << 8);
	m_mcu_prev = data;
}

uint8_t pcw_state::mcu_kb_data_r()
{
	uint16_t scan_bits = ((m_kb_scan_row & 0xf000) >> 4) | (m_kb_scan_row & 0xff);
	int x;

	for(x=0;x<12;x++)
	{
		if(!(scan_bits & 1))
			return pcw_keyboard_r(x);
		else
			scan_bits >>= 1;
	}
	return 0xff;
}

int pcw_state::mcu_kb_t1_r()
{
	return 1;
}

int pcw_state::mcu_kb_t0_r()
{
	return 0;
}

/* TODO: Implement parallel port! */
uint8_t pcw_state::pcw9512_parallel_r(offs_t offset)
{
	if (offset==1)
	{
		return 0xff^0x020;
	}

	LOGPAR("pcw9512 parallel r: offs: %04x\n", (int) offset);
	return 0x00;
}

/* TODO: Implement parallel port! */
void pcw_state::pcw9512_parallel_w(offs_t offset, uint8_t data)
{
	LOGPAR("pcw9512 parallel w: offs: %04x data: %02x\n", offset, data);
}

void pcw_state::pcw_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x000, 0x001).mirror(0x7e).m(m_fdc, FUNC(upd765a_device::map));
	map(0x080, 0x0ef).rw(FUNC(pcw_state::pcw_expansion_r), FUNC(pcw_state::pcw_expansion_w));
	map(0x0f0, 0x0f3).w(FUNC(pcw_state::pcw_bank_select_w));
	map(0x0f4, 0x0f4).rw(FUNC(pcw_state::pcw_interrupt_counter_r), FUNC(pcw_state::pcw_bank_force_selection_w));
	map(0x0f5, 0x0f5).w(FUNC(pcw_state::pcw_roller_ram_addr_w));
	map(0x0f6, 0x0f6).w(FUNC(pcw_state::pcw_pointer_table_top_scan_w));
	map(0x0f7, 0x0f7).w(FUNC(pcw_state::pcw_vdu_video_control_register_w));
	map(0x0f8, 0x0f8).rw(FUNC(pcw_state::pcw_system_status_r), FUNC(pcw_state::pcw_system_control_w));
	map(0x0fc, 0x0fd).rw(m_printer_mcu, FUNC(upi41_cpu_device::upi41_master_r), FUNC(upi41_cpu_device::upi41_master_w));
}



void pcw_state::pcw9512_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x000, 0x001).mirror(0x7e).m(m_fdc, FUNC(upd765a_device::map));
	map(0x080, 0x0ef).rw(FUNC(pcw_state::pcw_expansion_r), FUNC(pcw_state::pcw_expansion_w));
	map(0x0f0, 0x0f3).w(FUNC(pcw_state::pcw_bank_select_w));
	map(0x0f4, 0x0f4).rw(FUNC(pcw_state::pcw_interrupt_counter_r), FUNC(pcw_state::pcw_bank_force_selection_w));
	map(0x0f5, 0x0f5).w(FUNC(pcw_state::pcw_roller_ram_addr_w));
	map(0x0f6, 0x0f6).w(FUNC(pcw_state::pcw_pointer_table_top_scan_w));
	map(0x0f7, 0x0f7).w(FUNC(pcw_state::pcw_vdu_video_control_register_w));
	map(0x0f8, 0x0f8).rw(FUNC(pcw_state::pcw_system_status_r), FUNC(pcw_state::pcw_system_control_w));
	map(0x0fc, 0x0fd).rw(FUNC(pcw_state::pcw9512_parallel_r), FUNC(pcw_state::pcw9512_parallel_w));
}


TIMER_CALLBACK_MEMBER(pcw_state::setup_beep)
{
	m_beeper->set_state(0);
}


void pcw_state::machine_start()
{
	m_fdc_interrupt_code = 2;
	m_vdu_video_control_register = 0;
	m_nmi_flag = 0;
}

void pcw_state::machine_reset()
{
	uint8_t* code = memregion("printer_mcu")->base();
	int x;
	/* ram paging is actually undefined at power-on */

	m_bank_force = 0xf0;  // banks are locked for CPC-style paging - Batman expects this

	m_banks[0] = 0x80;
	m_banks[1] = 0x81;
	m_banks[2] = 0x82;
	m_banks[3] = 0x83;

	pcw_update_mem(0, m_banks[0]);
	pcw_update_mem(1, m_banks[1]);
	pcw_update_mem(2, m_banks[2]);
	pcw_update_mem(3, m_banks[3]);

	m_boot = 0;   // System starts up in bootstrap mode, disabled until it's possible to emulate it.

	/* copy boot code into RAM - yes, it's skipping a step */
	memset(m_ram->pointer(),0x00,m_ram->size());
	for(x=0;x<256;x++)
		m_ram->pointer()[x+2] = code[x+0x300];

	/* and hack our way past the MCU side of the boot process */
	code[0x01] = 0x40;

	m_printer_headpos = 0x00; // bring printer head to left margin
	m_printer_shift = 0;
	m_printer_shift_output = 0;
	machine().render().first_target()->set_view(0);
}

void pcw_state::init_pcw()
{
	m_maincpu->set_input_line_vector(0, 0x0ff); // Z80

	/* lower 4 bits are interrupt counter */
	m_system_status = 0x000;
	m_system_status &= ~((1<<6) | (1<<5) | (1<<4));

	m_interrupt_counter = 0;

	m_roller_ram_offset = 0;

	/* timer interrupt */
	m_beep_setup_timer = timer_alloc(FUNC(pcw_state::setup_beep), this);
	m_beep_setup_timer->adjust(attotime::zero);

	m_prn_stepper = timer_alloc(FUNC(pcw_state::pcw_stepper_callback), this);
	m_prn_pins = timer_alloc(FUNC(pcw_state::pcw_pins_callback), this);
	m_pulse_timer = timer_alloc(FUNC(pcw_state::pcw_timer_pulse), this);
}


/*
b7:   k2     k1     [+]    .      ,      space  V      X      Z      del<   alt
b6:   k3     k5     1/2    /      M      N      B      C      lock          k.
b5:   k6     k4     shift  ;      K      J      F      D      A             enter
b4:   k9     k8     k7     ??      L      H      G      S      tab           f8
b3:   paste  copy   #      P      I      Y      T      W      Q             [-]
b2:   f2     cut    return [      O      U      R      E      stop          can
b1:   k0     ptr    ]      -      9      7      5      3      2             extra
b0:   f4     exit   del>   =      0      8      6      4      1             f6
      &3FF0  &3FF1  &3FF2  &3FF3  &3FF4  &3FF5  &3FF6  &3FF7  &3FF8  &3FF9  &3FFA

2008-05 FP:
Small note about Natural keyboard: currently,
- "Paste" is mapped to 'F9'
- "Exit" is mapped to 'F10'
- "Ptr" is mapped to 'Print Screen'
- "Cut" is mapped to 'F11'
- "Copy" is mapped to 'F12'
- "Stop" is mapped to 'Esc'
- [+] is mapped to 'Page Up'
- [-] is mapped to 'Page Down'
- "Extra" is mapped to 'Home'
- "Can" is mapped to 'Insert'
*/

static INPUT_PORTS_START(pcw)
	/* keyboard "ports". These are poked automatically into the PCW address space */

	PORT_START("LINE0")     /* 0x03ff0 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3  F4") PORT_CODE(KEYCODE_F3)       PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)                        PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1  F2") PORT_CODE(KEYCODE_F1)       PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Paste") PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)                        PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)                        PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)                        PORT_CHAR(UCHAR_MAMEKEY(3_PAD)) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)                        PORT_CHAR(UCHAR_MAMEKEY(2_PAD))

	PORT_START("LINE1")     /* 0x03ff1 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Exit") PORT_CODE(KEYCODE_PGDN)       PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ptr") PORT_CODE(KEYCODE_BACKSLASH)   PORT_CHAR(UCHAR_MAMEKEY(PRTSCR))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cut") PORT_CODE(KEYCODE_SLASH_PAD)   PORT_CHAR(UCHAR_MAMEKEY(F11))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Copy") PORT_CODE(KEYCODE_ASTERISK)   PORT_CHAR(UCHAR_MAMEKEY(F12))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)                        PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)                        PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)                        PORT_CHAR(UCHAR_MAMEKEY(5_PAD)) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)                        PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) PORT_CHAR(UCHAR_MAMEKEY(LEFT))

	PORT_START("LINE2")     /* 0x03ff2 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DEL>") PORT_CODE(KEYCODE_DEL)        PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)                   PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER)    PORT_CHAR(13)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)                        PORT_CHAR('#') PORT_CHAR('>')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)                        PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_END)                          PORT_CHAR(189) PORT_CHAR('@')   // (½ @) between slash and rshift
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[+]") PORT_CODE(KEYCODE_F2)          PORT_CHAR(UCHAR_MAMEKEY(PGUP))  // 1st key on the left from 'Spacebar'

	PORT_START("LINE3")     /* 0x03ff3 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)                       PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)                        PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)                    PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)                            PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)                        PORT_CHAR(0xA7) PORT_CHAR('<')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)                        PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)                        PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)                     PORT_CHAR('.')

	PORT_START("LINE4")     /* 0x03ff4 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)                            PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)                            PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)                            PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)                            PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)                            PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)                            PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)                            PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)                        PORT_CHAR(',')

	PORT_START("LINE5")     /* 0x03ff5 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)                            PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)                            PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)                            PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)                            PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)                            PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)                            PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)                            PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)                        PORT_CHAR(' ')

	PORT_START("LINE6")     /* 0x03ff6 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)                            PORT_CHAR('6') PORT_CHAR('\'')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)                            PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)                            PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)                            PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)                            PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)                            PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)                            PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)                            PORT_CHAR('v') PORT_CHAR('V')

	PORT_START("LINE7")     /* 0x03ff7 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)                            PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)                            PORT_CHAR('3') PORT_CHAR(0xA3)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)                            PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)                            PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)                            PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)                            PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)                            PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)                            PORT_CHAR('x') PORT_CHAR('X')

	PORT_START("LINE8")     /* 0x03ff8 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)                            PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)                            PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Stop") PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)                            PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)                      PORT_CHAR('\t')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)                            PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)                            PORT_CHAR('z') PORT_CHAR('Z')

	PORT_START("LINE9")     /* 0x03ff9 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F5  F6") PORT_CODE(KEYCODE_F5)       PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Extra") PORT_CODE(KEYCODE_LCONTROL)  PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Can") PORT_CODE(KEYCODE_PGUP)        PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[-]") PORT_CODE(KEYCODE_F4)          PORT_CHAR(UCHAR_MAMEKEY(PGDN))  // 1st key on the right from 'Spacebar'
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F7  F8") PORT_CODE(KEYCODE_F7)       PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD)                    PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD)                  PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD)) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DEL<") PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(8)

	PORT_START("LINE10")    /* 0x03ffa */
	PORT_BIT(0x07f,0x7f, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Alt") PORT_CODE(KEYCODE_LALT)        PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(LALT)) PORT_CHAR(UCHAR_MAMEKEY(RALT))

	/* at this point the following reflect the above key combinations but in a incomplete
	way. No details available at this time */
	PORT_START("LINE11")    /* 0x03ffb */
	PORT_BIT(0xff, 0xff,     IPT_UNUSED)

	PORT_START("LINE12")    /* 0x03ffc */
	PORT_BIT(0xff, 0xff,     IPT_UNUSED)

	/* 2008-05  FP: not sure if this key is correct, "Caps Lock" is already mapped above.
	For now, I let it with no default mapping. */
	PORT_START("LINE13")    /* 0x03ffd */
	PORT_BIT(0xff, 0xff, IPT_UNUSED)
//  PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT LOCK") //PORT_CODE(KEYCODE_CAPSLOCK)
//  PORT_BIT(0x80, 0xff, IPT_UNUSED)

	PORT_START("LINE14")    /* 0x03ffe */
	PORT_BIT ( 0xff, 0xff,   IPT_UNUSED )

	PORT_START("LINE15")    /* 0x03fff */
	PORT_BIT ( 0xff, 0xff,   IPT_UNUSED )

	/* from here on are the pretend dipswitches for machine config etc */
	PORT_START("EXTRA")
	/* frame rate option */
	PORT_DIPNAME( 0x10, 0x010, "50/60Hz Frame Rate Option")
	PORT_DIPSETTING(    0x00, "60Hz")
	PORT_DIPSETTING(    0x10, "50Hz" )
	/* spectravideo joystick enabled */
	PORT_DIPNAME( 0x20, 0x020, "Spectravideo Joystick Enabled")
	PORT_DIPSETTING(    0x00, DEF_STR(No))
	PORT_DIPSETTING(    0x20, DEF_STR(Yes) )

	/* Spectravideo joystick */
	/* bit 7: 0
	6: 1
	5: 1
	4: 1 if in E position
	3: 1 if N
	2: 1 if W
	1: 1 if fire pressed
	0: 1 if S
	*/

	PORT_START("SPECTRAVIDEO")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_BUTTON1)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT)
	PORT_BIT(0x020, 0x020, IPT_UNUSED)
	PORT_BIT(0x040, 0x040, IPT_UNUSED)
	PORT_BIT(0x080, 0x00, IPT_UNUSED)

	/* Kempston joystick */
	PORT_START("KEMPSTON")
	PORT_BIT( 0xff, 0x00,    IPT_UNUSED)
INPUT_PORTS_END

static void pcw_ssfloppies(device_slot_interface &device)
{
	device.option_add("3ssdd", FLOPPY_3_SSDD);
}

static void pcw_dsfloppies(device_slot_interface &device)
{
	device.option_add("3dsqd", FLOPPY_3_DSQD);
}

static void pcw_35floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
}

/* PCW8256, PCW8512, PCW9256 */
void pcw_state::pcw(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(32'000'000) / 8); /* clock supplied to chip, but in reality it is 3.4 MHz due to z80 wait cycles*/
	m_maincpu->set_addrmap(AS_PROGRAM, &pcw_state::pcw_map);
	m_maincpu->set_addrmap(AS_IO, &pcw_state::pcw_io);

	I8041AH(config, m_printer_mcu, 11000000);  // 11MHz
	m_printer_mcu->p2_in_cb().set(FUNC(pcw_state::mcu_printer_p2_r));
	m_printer_mcu->p2_out_cb().set(FUNC(pcw_state::mcu_printer_p2_w));
	m_printer_mcu->p1_in_cb().set(FUNC(pcw_state::mcu_printer_p1_r));
	m_printer_mcu->p1_out_cb().set(FUNC(pcw_state::mcu_printer_p1_w));
	m_printer_mcu->t1_in_cb().set(FUNC(pcw_state::mcu_printer_t1_r));
	m_printer_mcu->t0_in_cb().set(FUNC(pcw_state::mcu_printer_t0_r));

	I8048(config, m_keyboard_mcu, 5000000); // 5MHz
	m_keyboard_mcu->p1_in_cb().set(FUNC(pcw_state::mcu_kb_scan_r));
	m_keyboard_mcu->p1_out_cb().set(FUNC(pcw_state::mcu_kb_scan_w));
	m_keyboard_mcu->p2_in_cb().set(FUNC(pcw_state::mcu_kb_scan_high_r));
	m_keyboard_mcu->p2_out_cb().set(FUNC(pcw_state::mcu_kb_scan_high_w));
	m_keyboard_mcu->t1_in_cb().set(FUNC(pcw_state::mcu_kb_t1_r));
	m_keyboard_mcu->t0_in_cb().set(FUNC(pcw_state::mcu_kb_t0_r));
	m_keyboard_mcu->bus_in_cb().set(FUNC(pcw_state::mcu_kb_data_r));

//  config.set_maximum_quantum(attotime::from_hz(50));
	config.set_perfect_quantum(m_maincpu);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(32_MHz_XTAL / 3, 720 + 20, 8, 720 + 8, 256 + 32, 8, 256 + 8); // Hand tuned to get 50Hz, it is all in the Amstrad ASIC, 32MHz in and video out
	m_screen->set_screen_update(FUNC(pcw_state::screen_update_pcw));
	m_screen->set_video_attributes(VIDEO_ALWAYS_UPDATE);
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(pcw_state::set_9xxx_palette), PCW_NUM_COLOURS);
	PALETTE(config, m_ppalette, FUNC(pcw_state::set_printer_palette), PCW_NUM_COLOURS);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 3750).add_route(ALL_OUTPUTS, "mono", 1.00);

	UPD765A(config, m_fdc, 4'000'000, true, true);
	m_fdc->intrq_wr_callback().set(FUNC(pcw_state::pcw_fdc_interrupt));

	SOFTWARE_LIST(config, "disk_list").set_original("pcw");

	/* internal ram */
	RAM(config, m_ram).set_default_size("256K");

	TIMER(config, "pcw_timer", 0).configure_periodic(FUNC(pcw_state::pcw_timer_interrupt), attotime::from_hz(300));
}

void pcw_state::pcw8256(machine_config &config)
{
	pcw(config);
	m_palette->set_init(FUNC(pcw_state::set_8xxx_palette));

	FLOPPY_CONNECTOR(config, "upd765:0", pcw_ssfloppies, "3ssdd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "upd765:1", pcw_dsfloppies, nullptr, floppy_image_device::default_mfm_floppy_formats);

	screen_device &printer(SCREEN(config, "printer", SCREEN_TYPE_RASTER));
	printer.set_refresh_hz(50);
	printer.set_size(PCW_PRINTER_WIDTH, PCW_PRINTER_HEIGHT);
	printer.set_visarea(0, PCW_PRINTER_WIDTH-1, 0, PCW_PRINTER_HEIGHT-1);
	printer.set_screen_update(FUNC(pcw_state::screen_update_pcw_printer));
	printer.set_palette(m_ppalette);

	config.set_default_layout(layout_pcw);
}

void pcw_state::pcw8512(machine_config &config)
{
	pcw(config);
	m_palette->set_init(FUNC(pcw_state::set_8xxx_palette));

	FLOPPY_CONNECTOR(config, "upd765:0", pcw_ssfloppies, "3ssdd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "upd765:1", pcw_dsfloppies, "3dsqd", floppy_image_device::default_mfm_floppy_formats);

	screen_device &printer(SCREEN(config, "printer", SCREEN_TYPE_RASTER));
	printer.set_refresh_hz(50);
	printer.set_size(PCW_PRINTER_WIDTH, PCW_PRINTER_HEIGHT);
	printer.set_visarea(0, PCW_PRINTER_WIDTH-1, 0, PCW_PRINTER_HEIGHT-1);
	printer.set_screen_update(FUNC(pcw_state::screen_update_pcw_printer));
	printer.set_palette(m_ppalette);

	config.set_default_layout(layout_pcw);

	/* internal ram */
	m_ram->set_default_size("512K");
}

/* PCW9512 */
void pcw_state::pcw9512(machine_config &config)
{
	pcw(config);
	m_palette->set_init(FUNC(pcw_state::set_9xxx_palette));

	FLOPPY_CONNECTOR(config, "upd765:0", pcw_dsfloppies, "3dsqd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "upd765:1", pcw_dsfloppies, nullptr, floppy_image_device::default_mfm_floppy_formats);

	m_maincpu->set_addrmap(AS_IO, &pcw_state::pcw9512_io);

	/* internal ram */
	m_ram->set_default_size("512K");
}

/* PCW9256 */
void pcw_state::pcw9256(machine_config &config)
{
	pcw(config);
	m_palette->set_init(FUNC(pcw_state::set_9xxx_palette));

	FLOPPY_CONNECTOR(config, "upd765:0", pcw_35floppies, "35dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "upd765:1", pcw_35floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);

	m_maincpu->set_addrmap(AS_IO, &pcw_state::pcw9512_io);
}

/* PCW9512+ */
void pcw_state::pcw9512p(machine_config &config)
{
	pcw(config);
	m_palette->set_init(FUNC(pcw_state::set_9xxx_palette));

	FLOPPY_CONNECTOR(config, "upd765:0", pcw_35floppies, "35dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "upd765:1", pcw_35floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);

	m_maincpu->set_addrmap(AS_IO, &pcw_state::pcw9512_io);

	/* internal ram */
	m_ram->set_default_size("512K");
}

/* PCW10 - essentially the same as pcw9512+ */
void pcw_state::pcw10(machine_config &config)
{
	pcw(config);
	m_palette->set_init(FUNC(pcw_state::set_9xxx_palette));

	FLOPPY_CONNECTOR(config, "upd765:0", pcw_35floppies, "35dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "upd765:1", pcw_35floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);

	m_maincpu->set_addrmap(AS_IO, &pcw_state::pcw9512_io);

	/* internal ram */
	m_ram->set_default_size("512K");
}

/***************************************************************************

  Game driver(s)

***************************************************************************/

ROM_START(pcw8256)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_FILL(0x0000,0x10000,0x00)
	ROM_REGION(0x400,"printer_mcu",0)  // i8041 9-pin dot-matrix
	ROM_LOAD("40026.ic701", 0, 0x400, CRC(ee8890ae) SHA1(91679cc5e07464ac55ef9a10f7095b2438223332))
	ROM_REGION(0x400,"keyboard_mcu",0) // i8048
	ROM_LOAD("40027.ic801", 0, 0x400, CRC(25260958) SHA1(210e7e25228c79d2920679f217d68e4f14055825))
ROM_END

ROM_START(pcw8512)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_FILL(0x0000,0x10000,0x00)
	ROM_REGION(0x400,"printer_mcu",0)  // i8041 9-pin dot-matrix
	ROM_LOAD("40026.ic701", 0, 0x400, CRC(ee8890ae) SHA1(91679cc5e07464ac55ef9a10f7095b2438223332))
	ROM_REGION(0x400,"keyboard_mcu",0) // i8048
	ROM_LOAD("40027.ic801", 0, 0x400, CRC(25260958) SHA1(210e7e25228c79d2920679f217d68e4f14055825))
ROM_END

ROM_START(pcw9256)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_FILL(0x0000,0x10000,0x00)
	ROM_REGION(0x2000,"printer_mcu",0) // i8041 9-pin dot-matrix
	ROM_LOAD("40026.ic701", 0, 0x400, CRC(ee8890ae) SHA1(91679cc5e07464ac55ef9a10f7095b2438223332))
	ROM_REGION(0x400,"keyboard_mcu",0) // i8048
	ROM_LOAD("40027.ic801", 0, 0x400, CRC(25260958) SHA1(210e7e25228c79d2920679f217d68e4f14055825))
ROM_END

ROM_START(pcw9512)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_FILL(0x0000,0x10000,0x00)
	ROM_REGION(0x2000,"printer_mcu",0) // i8041 daisywheel (schematics say i8039?)
	ROM_LOAD("40103.ic109", 0, 0x2000, CRC(a64d450a) SHA1(ebbf0ef19d39912c1c127c748514dd299915f88b))
	ROM_REGION(0x400,"keyboard_mcu",0) // i8048
	ROM_LOAD("40027.ic801", 0, 0x400, CRC(25260958) SHA1(210e7e25228c79d2920679f217d68e4f14055825))
ROM_END

ROM_START(pcw9512p)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_FILL(0x0000,0x10000,0x00)
	ROM_REGION(0x2000,"printer_mcu",0) // i8041 daisywheel (schematics say i8039?)
	ROM_LOAD("40103.ic109", 0, 0x2000, CRC(a64d450a) SHA1(ebbf0ef19d39912c1c127c748514dd299915f88b))
	ROM_REGION(0x400,"keyboard_mcu",0) // i8048
	ROM_LOAD("40027.ic801", 0, 0x400, CRC(25260958) SHA1(210e7e25228c79d2920679f217d68e4f14055825))
ROM_END

ROM_START(pcw10)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_FILL(0x0000,0x10000,0x00)
	ROM_REGION(0x2000,"printer_mcu",0) // i8041 9-pin dot matrix
	ROM_LOAD("40026.ic701", 0, 0x400, CRC(ee8890ae) SHA1(91679cc5e07464ac55ef9a10f7095b2438223332))
	ROM_REGION(0x400,"keyboard_mcu",0) // i8048
	ROM_LOAD("40027.ic801", 0, 0x400, CRC(25260958) SHA1(210e7e25228c79d2920679f217d68e4f14055825))
ROM_END


/* these are all variants on the pcw design */
/* major difference is memory configuration and drive type */
/*    YEAR  NAME      PARENT   COMPAT  MACHINE  INPUT CLASS      INIT      COMPANY        FULLNAME */
COMP( 1985, pcw8256,  0,       0,      pcw8256,  pcw,  pcw_state, init_pcw, "Amstrad plc", "PCW8256",       MACHINE_NOT_WORKING)
COMP( 1985, pcw8512,  pcw8256, 0,      pcw8512,  pcw,  pcw_state, init_pcw, "Amstrad plc", "PCW8512",       MACHINE_NOT_WORKING)
COMP( 1987, pcw9512,  pcw8256, 0,      pcw9512,  pcw,  pcw_state, init_pcw, "Amstrad plc", "PCW9512",       MACHINE_NOT_WORKING)
COMP( 1991, pcw9256,  pcw8256, 0,      pcw9256,  pcw,  pcw_state, init_pcw, "Amstrad plc", "PCW9256",       MACHINE_NOT_WORKING)
COMP( 1991, pcw9512p, pcw8256, 0,      pcw9512p, pcw,  pcw_state, init_pcw, "Amstrad plc", "PCW9512 (+)",   MACHINE_NOT_WORKING)
COMP( 1993, pcw10,    pcw8256, 0,      pcw10,    pcw,  pcw_state, init_pcw, "Amstrad plc", "PCW10",         MACHINE_NOT_WORKING)



pcw16.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Kevin Thacker
/******************************************************************************

    pcw16.c
    system driver

    Kevin Thacker [MESS driver]

  Thankyou to:

    - Cliff Lawson @ Amstrad plc for his documentation (Anne ASIC documentation),
                    and extensive help.
            (web.ukonline.co.uk/cliff.lawson/)
            (www.amstrad.com)
    - John Elliot for his help and tips
            (he's written a CP/M implementation for the PCW16)
            (www.seasip.deomon.co.uk)
    - and others who offered their help (Richard Fairhurst, Richard Wildey)

    Hardware:
        - 2mb dram max,
        - 2mb flash-file memory max (in 2 1mb chips),
        - 16MHz Z80 (core combined in Anne ASIC),
        - Anne ASIC (keyboard interface, video (colours), dram/flash/rom paging,
        real time clock, "glue" logic for Super I/O)
        - Winbond Super I/O chip (PC type hardware - FDC, Serial, LPT, Hard-drive)
        - PC/AT keyboard - some keys are coloured to identify special functions, but
        these are the same as normal PC keys
        - PC Serial Mouse - uses Mouse System Mouse protocol
        - PC 1.44MB Floppy drive

    Primary Purpose:
        - built as a successor to the PCW8526/PCW9512 series
        - wordprocessor system (also contains spreadsheet and other office applications)
        - 16MHz processor used so proportional fonts and enhanced wordprocessing features
          are possible, true WYSIWYG wordprocessing.
        - flash-file can store documents.

    To Do:
        - reduce memory usage so it is more MESSD friendly
        - different configurations
        - implement configuration register
        - extract game-port hardware from pc driver - used in any PCW16 progs?
        - extract hard-drive code from PC driver and use in this driver
        - implement printer
        - .. anything else that requires implementing

     Info:
       - to use this driver you need a OS rescue disc.
       (HINT: This also contains the boot-rom)
      - the OS will be installed from the OS rescue disc into the Flash-ROM

    Uses "MEMCARD" dir to hold flash-file data.
    To use the power button, flick the dip switch off/on

 From comp.sys.amstrad.8bit FAQ:

  "Amstrad made the following PCW systems :

  - 1) PCW8256
  - 2) PCW8512
  - 3) PCW9512
  - 4) PCW9512+
  - 5) PcW10
  - 6) PcW16

  1 had 180K drives, 2 had a 180K A drive and a 720K B drive, 3 had only
  720K drives. All subsequent models had 3.5" disks using CP/M format at
  720K until 6 when it switched to 1.44MB in MS-DOS format. The + of
  model 4 was that it had a "real" parallel interface so could be sold
  with an external printer such as the Canon BJ10. The PcW10 wasn't
  really anything more than 4 in a more modern looking case.

  The PcW16 is a radical digression who's sole "raison d'etre" was to
  make a true WYSIWYG product but this meant a change in the screen and
  processor (to 16MHz) etc. which meant that it could not be kept
  compatible with the previous models (though documents ARE compatible)"


TODO:
- Verfiy uart model.


 ******************************************************************************/
/* PeT 19.October 2000
   added/changed printer support
   not working reliable, seams to expect parallelport in epp/ecp mode
   epp/ecp modes in parallel port not supported yet
   so ui disabled */

#include "emu.h"
#include "pcw16.h"
#include "bus/rs232/hlemouse.h"
#include "bus/rs232/rs232.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

// interrupt counter
/* controls which bank of 2mb address space is paged into memory */

// output of 4-bit port from Anne ASIC
// code defining which INT fdc is connected to
// interrupt bits
// bit 7: ??
// bit 6: fdc
// bit 5: ??
// bit 3,4; Serial
// bit 2: Vsync state
// bit 1: keyboard int
// bit 0: Display ints

// debugging - write ram as seen by cpu
void pcw16_state::pcw16_refresh_ints()
{
	/* any bits set excluding vsync */
	if ((m_system_status & (~0x04))!=0)
	{
		m_maincpu->set_input_line(0, HOLD_LINE);
	}
	else
	{
		m_maincpu->set_input_line(0, CLEAR_LINE);
	}
}


TIMER_DEVICE_CALLBACK_MEMBER(pcw16_state::pcw16_timer_callback)
{
	/* do not increment past 15 */
	if (m_interrupt_counter!=15)
	{
		m_interrupt_counter++;
		/* display int */
		m_system_status |= (1<<0);
	}

	if (m_interrupt_counter!=0)
	{
		pcw16_refresh_ints();
	}
}

void pcw16_state::pcw16_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(pcw16_state::pcw16_mem_r), FUNC(pcw16_state::pcw16_mem_w));
}


void pcw16_state::pcw16_palette_w(offs_t offset, uint8_t data)
{
	m_colour_palette[offset & 0x0f] = data & 31;
}

uint8_t pcw16_state::read_bank_data(uint8_t type, uint16_t offset)
{
	if(type & 0x80) // DRAM
	{
		return m_ram->pointer()[((type & 0x7f)*0x4000) + offset];
	}
	else  // ROM / Flash
	{
		if(type < 4)
		{
			return m_region_rom->base()[((type & 0x03)*0x4000)+offset+0x10000];
		}
		if(type < 0x40)  // first flash
		{
			return m_flash0->read(((type & 0x3f)*0x4000)+offset);
		}
		else  // second flash
		{
			return m_flash1->read(((type & 0x3f)*0x4000)+offset);
		}
	}
}

void pcw16_state::write_bank_data(uint8_t type, uint16_t offset, uint8_t data)
{
	if(type & 0x80) // DRAM
	{
		m_ram->pointer()[((type & 0x7f)*0x4000) + offset] = data;
	}
	else  // ROM / Flash
	{
		if(type < 4)
			return;  // first four sectors are write protected
		if(type < 0x40)  // first flash
		{
			m_flash0->write(((type & 0x3f)*0x4000)+offset, data);
		}
		else  // second flash
		{
			m_flash1->write(((type & 0x3f)*0x4000)+offset, data);
		}
	}
}

uint8_t pcw16_state::pcw16_read_mem(uint8_t bank, uint16_t offset)
{
	switch(bank)
	{
	case 0:
		return read_bank_data(m_banks[bank],offset);
	case 1:
		return read_bank_data(m_banks[bank],offset);
	case 2:
		return read_bank_data(m_banks[bank],offset);
	case 3:
		return read_bank_data(m_banks[bank],offset);
	}
	return 0xff;
}

void pcw16_state::pcw16_write_mem(uint8_t bank, uint16_t offset, uint8_t data)
{
	switch(bank)
	{
	case 0:
		write_bank_data(m_banks[bank],offset,data);
		break;
	case 1:
		write_bank_data(m_banks[bank],offset,data);
		break;
	case 2:
		write_bank_data(m_banks[bank],offset,data);
		break;
	case 3:
		write_bank_data(m_banks[bank],offset,data);
		break;
	}
}

uint8_t pcw16_state::pcw16_mem_r(offs_t offset)
{
	if(offset < 0x4000)
		return pcw16_read_mem(0,offset);
	if(offset >= 0x4000 && offset < 0x8000)
		return pcw16_read_mem(1,offset-0x4000);
	if(offset >= 0x8000 && offset < 0xc000)
		return pcw16_read_mem(2,offset-0x8000);
	if(offset >= 0xc000 && offset < 0x10000)
		return pcw16_read_mem(3,offset-0xc000);

	return 0xff;
}

void pcw16_state::pcw16_mem_w(offs_t offset, uint8_t data)
{
	if(offset < 0x4000)
		pcw16_write_mem(0,offset,data);
	if(offset >= 0x4000 && offset < 0x8000)
		pcw16_write_mem(1,offset-0x4000,data);
	if(offset >= 0x8000 && offset < 0xc000)
		pcw16_write_mem(2,offset-0x8000,data);
	if(offset >= 0xc000 && offset < 0x10000)
		pcw16_write_mem(3,offset-0xc000,data);
}

uint8_t pcw16_state::pcw16_bankhw_r(offs_t offset)
{
//  logerror("bank r: %d \n", offset);

	return m_banks[offset];
}

void pcw16_state::pcw16_bankhw_w(offs_t offset, uint8_t data)
{
	//logerror("bank w: %d block: %02x\n", offset, data);

	m_banks[offset] = data;
}

void pcw16_state::pcw16_video_control_w(uint8_t data)
{
	//logerror("video control w: %02x\n", data);

	m_video_control = data;
}

/* PCW16 KEYBOARD */

//unsigned char pcw16_keyboard_status;



#define PCW16_KEYBOARD_PARITY_MASK  (1<<7)
#define PCW16_KEYBOARD_STOP_BIT_MASK (1<<6)
#define PCW16_KEYBOARD_START_BIT_MASK (1<<5)
#define PCW16_KEYBOARD_BUSY_STATUS  (1<<4)
#define PCW16_KEYBOARD_FORCE_KEYBOARD_CLOCK (1<<1)
#define PCW16_KEYBOARD_TRANSMIT_MODE (1<<0)

#define PCW16_KEYBOARD_RESET_INTERFACE (1<<2)

#define PCW16_KEYBOARD_DATA (1<<1)
#define PCW16_KEYBOARD_CLOCK (1<<0)

/* parity table. Used to set parity bit in keyboard status register */

void pcw16_state::pcw16_keyboard_init()
{
	int i;
	int b;

	/* if sum of all bits in the byte is even, then the data
	has even parity, otherwise it has odd parity */
	for (i=0; i<256; i++)
	{
		int data;
		int sum;

		sum = 0;
		data = i;

		for (b=0; b<8; b++)
		{
			sum+=data & 0x01;

			data = data>>1;
		}

		m_keyboard_parity_table[i] = sum & 0x01;
	}


	/* clear int */
	pcw16_keyboard_int(0);
	/* reset state */
	m_keyboard_state = 0;
	/* reset ready for transmit */
	pcw16_keyboard_reset();
}

void pcw16_state::pcw16_keyboard_refresh_outputs()
{
	/* generate output bits */
	m_keyboard_bits_output = m_keyboard_bits;

	/* force clock low? */
	if (m_keyboard_state & PCW16_KEYBOARD_FORCE_KEYBOARD_CLOCK)
	{
		m_keyboard_bits_output &= ~PCW16_KEYBOARD_CLOCK;
	}
}

void pcw16_state::pcw16_keyboard_set_clock_state(int state)
{
	m_keyboard_bits &= ~PCW16_KEYBOARD_CLOCK;

	if (state)
	{
		m_keyboard_bits |= PCW16_KEYBOARD_CLOCK;
	}

	pcw16_keyboard_refresh_outputs();
}

void pcw16_state::pcw16_keyboard_int(int state)
{
	m_system_status &= ~(1<<1);

	if (state)
	{
		m_system_status |= (1<<1);
	}

	pcw16_refresh_ints();
}

void pcw16_state::pcw16_keyboard_reset()
{
	/* clock set to high */
	pcw16_keyboard_set_clock_state(1);
}

/* interfaces to a pc-at keyboard */
uint8_t pcw16_state::pcw16_keyboard_data_shift_r()
{
	//logerror("keyboard data shift r: %02x\n", m_keyboard_data_shift);
	m_keyboard_state &= ~(PCW16_KEYBOARD_BUSY_STATUS);

	pcw16_keyboard_int(0);
	/* reset for reception */
	pcw16_keyboard_reset();

	/* read byte */
	return m_keyboard_data_shift;
}

/* if force keyboard clock is low it is safe to send */
int pcw16_state::pcw16_keyboard_can_transmit()
{
	/* clock is not forced low */
	/* and not busy - i.e. not already sent a char */
	return ((m_keyboard_bits_output & PCW16_KEYBOARD_CLOCK)!=0);
}

#ifdef UNUSED_FUNCTION
/* issue a begin byte transfer */
void ::pcw16_begin_byte_transfer(void)
{
}
#endif

/* signal a code has been received */
void pcw16_state::pcw16_keyboard_signal_byte_received(int data)
{
	/* clear clock */
	pcw16_keyboard_set_clock_state(0);

	/* set code in shift register */
	m_keyboard_data_shift = data;
	/* busy */
	m_keyboard_state |= PCW16_KEYBOARD_BUSY_STATUS;

	/* initialise start, stop and parity bits */
	m_keyboard_state &= ~PCW16_KEYBOARD_START_BIT_MASK;
	m_keyboard_state |=PCW16_KEYBOARD_STOP_BIT_MASK;

	/* "Keyboard data has odd parity, so the parity bit in the
	status register should only be set when the shift register
	data itself has even parity. */

	m_keyboard_state &= ~PCW16_KEYBOARD_PARITY_MASK;

	/* if data has even parity, set parity bit */
	if ((m_keyboard_parity_table[data])==0)
		m_keyboard_state |= PCW16_KEYBOARD_PARITY_MASK;

	pcw16_keyboard_int(1);
}


void pcw16_state::pcw16_keyboard_data_shift_w(uint8_t data)
{
	//logerror("Keyboard Data Shift: %02x\n", data);
	/* writing to shift register clears parity */
	/* writing to shift register clears start bit */
	m_keyboard_state &= ~(
		PCW16_KEYBOARD_PARITY_MASK |
		PCW16_KEYBOARD_START_BIT_MASK);

	/* writing to shift register sets stop bit */
	m_keyboard_state |= PCW16_KEYBOARD_STOP_BIT_MASK;

	m_keyboard_data_shift = data;

}

uint8_t pcw16_state::pcw16_keyboard_status_r()
{
	/* bit 2,3 are bits 8 and 9 of vdu pointer */
	return (m_keyboard_state &
		(PCW16_KEYBOARD_PARITY_MASK |
			PCW16_KEYBOARD_STOP_BIT_MASK |
			PCW16_KEYBOARD_START_BIT_MASK |
			PCW16_KEYBOARD_BUSY_STATUS |
			PCW16_KEYBOARD_FORCE_KEYBOARD_CLOCK |
			PCW16_KEYBOARD_TRANSMIT_MODE));
}

void pcw16_state::pcw16_keyboard_control_w(uint8_t data)
{
	//logerror("Keyboard control w: %02x\n",data);

	m_keyboard_previous_state = m_keyboard_state;

	/* if set, set parity */
	if (data & 0x080)
	{
		m_keyboard_state |= PCW16_KEYBOARD_PARITY_MASK;
	}

	/* clear read/write bits */
	m_keyboard_state &=
		~(PCW16_KEYBOARD_FORCE_KEYBOARD_CLOCK |
			PCW16_KEYBOARD_TRANSMIT_MODE);
	/* set read/write bits from data */
	m_keyboard_state |= (data & 0x03);

	if (data & PCW16_KEYBOARD_RESET_INTERFACE)
	{
		pcw16_keyboard_reset();
	}

	if (data & PCW16_KEYBOARD_TRANSMIT_MODE)
	{
		/* force clock changed */
		if (((m_keyboard_state^m_keyboard_previous_state) & PCW16_KEYBOARD_FORCE_KEYBOARD_CLOCK)!=0)
		{
			/* just cleared? */
			if ((m_keyboard_state & PCW16_KEYBOARD_FORCE_KEYBOARD_CLOCK)==0)
			{
				/* write */
				/* busy */
				m_keyboard_state |= PCW16_KEYBOARD_BUSY_STATUS;
				/* keyboard takes data */
				m_keyboard->write(m_keyboard_data_shift);
				/* set clock low - no furthur transmissions */
				pcw16_keyboard_set_clock_state(0);
				/* set int */
				pcw16_keyboard_int(1);
			}
		}


	}

	if (((m_keyboard_state^m_keyboard_previous_state) & PCW16_KEYBOARD_TRANSMIT_MODE)!=0)
	{
		if ((m_keyboard_state & PCW16_KEYBOARD_TRANSMIT_MODE)==0)
		{
			if ((m_system_status & (1<<1))!=0)
			{
				pcw16_keyboard_int(0);
			}
		}
	}

	pcw16_keyboard_refresh_outputs();
}


void pcw16_state::pcw16_keyboard_callback(int state)
{
	if(!state)
		return;

	if (pcw16_keyboard_can_transmit())
	{
		int data;

		data = m_keyboard->read();

		if (data)
		{
//          if (data==4)
//          {
//              pcw16_dump_cpu_ram();
//          }

			pcw16_keyboard_signal_byte_received(data);
		}
	}
}


static const int rtc_days_in_each_month[]=
{
	0, //dummy value
	31,/* jan */
	28, /* feb */
	31, /* march */
	30, /* april */
	31, /* may */
	30, /* june */
	31, /* july */
	31, /* august */
	30, /* september */
	31, /* october */
	30, /* november */
	31  /* december */
};

static const int rtc_days_in_february[] =
{
	29, 28, 28, 28
};

void pcw16_state::rtc_setup_max_days()
{
	/* february? */
	if (m_rtc_months == 2)
	{
		/* low two bits of year select number of days in february */
		m_rtc_days_max = rtc_days_in_february[m_rtc_years & 0x03];
	}
	else
	{
		m_rtc_days_max = (unsigned char)rtc_days_in_each_month[m_rtc_months];
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(pcw16_state::rtc_timer_callback)
{
	int fraction_of_second;

	/* halt counter? */
	if ((m_rtc_control & 0x01)!=0)
	{
		/* no */

		/* increment 256th's of a second register */
		fraction_of_second = m_rtc_256ths_seconds+1;
		/* add bit 8 = overflow */
		m_rtc_seconds+=(fraction_of_second>>8);
		/* ensure counter is in range 0-255 */
		m_rtc_256ths_seconds = fraction_of_second & 0x0ff;
	}

	if (m_rtc_seconds>59)
	{
		m_rtc_seconds = 0;

		m_rtc_minutes++;

		if (m_rtc_minutes>59)
		{
			m_rtc_minutes = 0;

			m_rtc_hours++;

			if (m_rtc_hours>23)
			{
				m_rtc_hours = 0;

				m_rtc_days++;

				if (m_rtc_days > m_rtc_days_max)
				{
					m_rtc_days = 1;

					m_rtc_months++;

					if (m_rtc_months>12)
					{
						m_rtc_months = 1;

						/* 7 bit year counter */
						m_rtc_years = (m_rtc_years + 1) & 0x07f;

					}

					rtc_setup_max_days();
				}

			}


		}
	}
}

uint8_t pcw16_state::rtc_year_invalid_r()
{
	/* year in lower 7 bits. RTC Invalid status is m_rtc_control bit 0
	inverted */
	return (m_rtc_years & 0x07f) | (((m_rtc_control & 0x01)<<7)^0x080);
}

uint8_t pcw16_state::rtc_month_r()
{
	return m_rtc_months;
}

uint8_t pcw16_state::rtc_days_r()
{
	return m_rtc_days;
}

uint8_t pcw16_state::rtc_hours_r()
{
	return m_rtc_hours;
}

uint8_t pcw16_state::rtc_minutes_r()
{
	return m_rtc_minutes;
}

uint8_t pcw16_state::rtc_seconds_r()
{
	return m_rtc_seconds;
}

uint8_t pcw16_state::rtc_256ths_seconds_r()
{
	return m_rtc_256ths_seconds;
}

void pcw16_state::rtc_control_w(uint8_t data)
{
	/* write control */
	m_rtc_control = data;
}

void pcw16_state::rtc_seconds_w(uint8_t data)
{
	/* TODO: Writing register could cause next to increment! */
	m_rtc_seconds = data;
}

void pcw16_state::rtc_minutes_w(uint8_t data)
{
	/* TODO: Writing register could cause next to increment! */
	m_rtc_minutes = data;
}

void pcw16_state::rtc_hours_w(uint8_t data)
{
	/* TODO: Writing register could cause next to increment! */
	m_rtc_hours = data;
}

void pcw16_state::rtc_days_w(uint8_t data)
{
	/* TODO: Writing register could cause next to increment! */
	m_rtc_days = data;
}

void pcw16_state::rtc_month_w(uint8_t data)
{
	/* TODO: Writing register could cause next to increment! */
	m_rtc_months = data;

	rtc_setup_max_days();
}


void pcw16_state::rtc_year_w(uint8_t data)
{
	/* TODO: Writing register could cause next to increment! */
	m_rtc_hours = data;

	rtc_setup_max_days();
}


void pcw16_state::trigger_fdc_int()
{
	int state;

	state = m_system_status & (1<<6);

	switch (m_fdc_int_code)
	{
		/* nmi */
		case 0:
		{
			/* I'm assuming that the nmi is edge triggered */
			/* a interrupt from the fdc will cause a change in line state, and
			the nmi will be triggered, but when the state changes because the int
			is cleared this will not cause another nmi */
			/* I'll emulate it like this to be sure */

			if (state!=m_previous_fdc_int_state)
			{
				if (state)
				{
					/* I'll pulse it because if I used hold-line I'm not sure
					it would clear - to be checked */
					m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
				}
			}
		}
		break;

		/* attach fdc to int */
		case 1:
		{
			pcw16_refresh_ints();
		}
		break;

		/* do not interrupt */
		default:
			break;
	}

	m_previous_fdc_int_state = state;
}

uint8_t pcw16_state::pcw16_system_status_r()
{
//  logerror("system status r: \n");

	return m_system_status | (m_io_extra->read() & 0x04);
}

uint8_t pcw16_state::pcw16_timer_interrupt_counter_r()
{
	int data;

	data = m_interrupt_counter;

	m_interrupt_counter = 0;
	/* clear display int */
	m_system_status &= ~(1<<0);

	pcw16_refresh_ints();

	return data;
}


void pcw16_state::pcw16_system_control_w(uint8_t data)
{
	//logerror("0x0f8: function: %d\n",data);

	/* lower 4 bits define function code */
	switch (data & 0x0f)
	{
		/* no effect */
		case 0x00:
		case 0x09:
		case 0x0a:
		case 0x0d:
		case 0x0e:
			break;

		/* system reset */
		case 0x01:
			break;

		/* connect IRQ6 input to /NMI */
		case 0x02:
		{
			m_fdc_int_code = 0;
		}
		break;

		/* connect IRQ6 input to /INT */
		case 0x03:
		{
			m_fdc_int_code = 1;
		}
		break;

		/* dis-connect IRQ6 input from /NMI and /INT */
		case 0x04:
		{
			m_fdc_int_code = 2;
		}
		break;

		/* set terminal count */
		case 0x05:
		{
			m_fdc->tc_w(true);
		}
		break;

		/* clear terminal count */
		case 0x06:
		{
			m_fdc->tc_w(false);
		}
		break;

		/* bleeper on */
		case 0x0b:
		{
			m_beeper->set_state(1);
		}
		break;

		/* bleeper off */
		case 0x0c:
		{
			m_beeper->set_state(0);
		}
		break;

		/* drive video outputs */
		case 0x07:
		{
		}
		break;

		/* float video outputs */
		case 0x08:
		{
		}
		break;

		/* set 4-bit output port to value X */
		case 0x0f:
		{
			/* bit 7 - ?? */
			/* bit 6 - ?? */
			/* bit 5 - green/red led (1==green)*/
			/* bit 4 - monitor on/off (1==on) */

			m_4_bit_port = data>>4;


		}
		break;
	}
}

void pcw16_state::fdc_interrupt(int state)
{
	/* IRQ6 */
	/* bit 6 of PCW16 system status indicates floppy ints */
	if (state)
		m_system_status |= (1<<6);
	else
		m_system_status &= ~(1<<6);

	trigger_fdc_int();
}


void pcw16_state::pcw16_com_interrupt_1(int state)
{
	m_system_status &= ~(1 << 4);

	if ( state ) {
		m_system_status |= (1 << 4);
	}

	pcw16_refresh_ints();
}


void pcw16_state::pcw16_com_interrupt_2(int state)
{
	m_system_status &= ~(1 << 3);

	if ( state ) {
		m_system_status |= (1 << 3);
	}

	pcw16_refresh_ints();
}

static void pcw16_floppies(device_slot_interface &device)
{
	device.option_add("35hd", FLOPPY_35_HD);
}


void pcw16_state::pcw16_io(address_map &map)
{
	map.global_mask(0xff);
	/* super i/o chip */
	map(0x018, 0x01f).m(m_fdc, FUNC(pc_fdc_superio_device::map));
	map(0x020, 0x027).rw("ns16550_1", FUNC(ns16550_device::ins8250_r), FUNC(ns16550_device::ins8250_w));
	map(0x028, 0x02f).rw(m_uart2, FUNC(ns16550_device::ins8250_r), FUNC(ns16550_device::ins8250_w));
	map(0x038, 0x03a).rw("lpt", FUNC(pc_lpt_device::read), FUNC(pc_lpt_device::write));
	/* anne asic */
	map(0x0e0, 0x0ef).w(FUNC(pcw16_state::pcw16_palette_w));
	map(0x0f0, 0x0f3).rw(FUNC(pcw16_state::pcw16_bankhw_r), FUNC(pcw16_state::pcw16_bankhw_w));
	map(0x0f4, 0x0f4).rw(FUNC(pcw16_state::pcw16_keyboard_data_shift_r), FUNC(pcw16_state::pcw16_keyboard_data_shift_w));
	map(0x0f5, 0x0f5).rw(FUNC(pcw16_state::pcw16_keyboard_status_r), FUNC(pcw16_state::pcw16_keyboard_control_w));
	map(0x0f7, 0x0f7).rw(FUNC(pcw16_state::pcw16_timer_interrupt_counter_r), FUNC(pcw16_state::pcw16_video_control_w));
	map(0x0f8, 0x0f8).rw(FUNC(pcw16_state::pcw16_system_status_r), FUNC(pcw16_state::pcw16_system_control_w));
	map(0x0f9, 0x0f9).rw(FUNC(pcw16_state::rtc_256ths_seconds_r), FUNC(pcw16_state::rtc_control_w));
	map(0x0fa, 0x0fa).rw(FUNC(pcw16_state::rtc_seconds_r), FUNC(pcw16_state::rtc_seconds_w));
	map(0x0fb, 0x0fb).rw(FUNC(pcw16_state::rtc_minutes_r), FUNC(pcw16_state::rtc_minutes_w));
	map(0x0fc, 0x0fc).rw(FUNC(pcw16_state::rtc_hours_r), FUNC(pcw16_state::rtc_hours_w));
	map(0x0fd, 0x0fd).rw(FUNC(pcw16_state::rtc_days_r), FUNC(pcw16_state::rtc_days_w));
	map(0x0fe, 0x0fe).rw(FUNC(pcw16_state::rtc_month_r), FUNC(pcw16_state::rtc_month_w));
	map(0x0ff, 0x0ff).rw(FUNC(pcw16_state::rtc_year_invalid_r), FUNC(pcw16_state::rtc_year_w));
}


void pcw16_state::machine_reset()
{
	/* initialise defaults */
	m_fdc_int_code = 2;
	/* clear terminal count */
	m_fdc->tc_w(false);
	/* select first rom page */
	m_banks[0] = 0;
//  pcw16_update_memory(machine);

	/* temp rtc setup */
	m_rtc_seconds = 0;
	m_rtc_minutes = 0;
	m_rtc_hours = 0;
	m_rtc_days_max = 0;
	m_rtc_days = 1;
	m_rtc_months = 1;
	m_rtc_years = 0;
	m_rtc_control = 1;
	m_rtc_256ths_seconds = 0;

	pcw16_keyboard_init();
	m_uart2->ri_w(0);
}


void pcw16_state::machine_start()
{
	m_system_status = 0;
	m_interrupt_counter = 0;

	m_beeper->set_state(0);
}

static INPUT_PORTS_START(pcw16)
	PORT_START("EXTRA")
	/* vblank */
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("screen", FUNC(screen_device::vblank))
	/* power switch - default is on */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE) PORT_NAME("Power Switch/Suspend") PORT_WRITE_LINE_DEVICE_MEMBER("ns16550_2", FUNC(ins8250_uart_device::ri_w)) PORT_TOGGLE
INPUT_PORTS_END

static void pcw16_com(device_slot_interface &device)
{
	device.option_add("msystems_mouse", MSYSTEMS_HLE_SERIAL_MOUSE);
}

void pcw16_state::pcw16(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 16000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &pcw16_state::pcw16_map);
	m_maincpu->set_addrmap(AS_IO, &pcw16_state::pcw16_io);
	config.set_maximum_quantum(attotime::from_hz(60));

	ns16550_device &uart1(NS16550(config, "ns16550_1", XTAL(1'843'200)));     /* TODO: Verify uart model */
	uart1.out_tx_callback().set("serport1", FUNC(rs232_port_device::write_txd));
	uart1.out_dtr_callback().set("serport1", FUNC(rs232_port_device::write_dtr));
	uart1.out_rts_callback().set("serport1", FUNC(rs232_port_device::write_rts));
	uart1.out_int_callback().set(FUNC(pcw16_state::pcw16_com_interrupt_1));

	rs232_port_device &serport1(RS232_PORT(config, "serport1", pcw16_com, "msystems_mouse"));
	serport1.rxd_handler().set(uart1, FUNC(ins8250_uart_device::rx_w));
	serport1.dcd_handler().set(uart1, FUNC(ins8250_uart_device::dcd_w));
	serport1.dsr_handler().set(uart1, FUNC(ins8250_uart_device::dsr_w));
	serport1.ri_handler().set(uart1, FUNC(ins8250_uart_device::ri_w));
	serport1.cts_handler().set(uart1, FUNC(ins8250_uart_device::cts_w));

	NS16550(config, m_uart2, XTAL(1'843'200));     /* TODO: Verify uart model */
	m_uart2->out_tx_callback().set("serport2", FUNC(rs232_port_device::write_txd));
	m_uart2->out_dtr_callback().set("serport2", FUNC(rs232_port_device::write_dtr));
	m_uart2->out_rts_callback().set("serport2", FUNC(rs232_port_device::write_rts));
	m_uart2->out_int_callback().set(FUNC(pcw16_state::pcw16_com_interrupt_2));

	rs232_port_device &serport2(RS232_PORT(config, "serport2", pcw16_com, nullptr));
	serport2.rxd_handler().set(m_uart2, FUNC(ins8250_uart_device::rx_w));
	serport2.dcd_handler().set(m_uart2, FUNC(ins8250_uart_device::dcd_w));
	serport2.dsr_handler().set(m_uart2, FUNC(ins8250_uart_device::dsr_w));
	serport2.cts_handler().set(m_uart2, FUNC(ins8250_uart_device::cts_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(PCW16_SCREEN_WIDTH, PCW16_SCREEN_HEIGHT);
	screen.set_visarea(0, PCW16_SCREEN_WIDTH-1, 0, PCW16_SCREEN_HEIGHT-1);
	screen.set_screen_update(FUNC(pcw16_state::screen_update_pcw16));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(pcw16_state::pcw16_colours), PCW16_NUM_COLOURS);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 3750).add_route(ALL_OUTPUTS, "mono", 1.00);

	/* printer */
	pc_lpt_device &lpt(PC_LPT(config, "lpt"));
	lpt.irq_handler().set_inputline(m_maincpu, 0);

	PC_FDC_SUPERIO(config, m_fdc, 48_MHz_XTAL / 2);
	m_fdc->intrq_wr_callback().set(FUNC(pcw16_state::fdc_interrupt));
	FLOPPY_CONNECTOR(config, "fdc:0", pcw16_floppies, "35hd", floppy_image_device::default_pc_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", pcw16_floppies, "35hd", floppy_image_device::default_pc_floppy_formats);

	SOFTWARE_LIST(config, "disk_list").set_original("pcw16");

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("2M");

	INTEL_E28F008SA(config, "flash0");
	INTEL_E28F008SA(config, "flash1");

	AT_KEYB(config, m_keyboard, pc_keyboard_device::KEYBOARD_TYPE::AT, 3);
	m_keyboard->keypress().set(FUNC(pcw16_state::pcw16_keyboard_callback));

	/* video ints */
	TIMER(config, "video_timer").configure_periodic(FUNC(pcw16_state::pcw16_timer_callback), attotime::from_usec(5830));
	/* rtc timer */
	TIMER(config, "rtc_timer").configure_periodic(FUNC(pcw16_state::rtc_timer_callback), attotime::from_hz(256));
}

/***************************************************************************

  Game driver(s)

***************************************************************************/

/* the lower 64k of the flash-file memory is write protected. This contains the boot
    rom. The boot rom is also on the OS rescue disc. Handy! */
ROM_START(pcw16)
	ROM_REGION((0x010000+524288), "maincpu",0)
	ROM_LOAD("pcw045.sys",0x10000, 524288, CRC(c642f498) SHA1(8a5c05de92e7b2c5acdfb038217503ad363285b5))
ROM_END


/*    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY        FULLNAME */
COMP( 1995, pcw16, 0,      0,      pcw16,   pcw16, pcw16_state, empty_init, "Amstrad plc", "PcW16", MACHINE_NOT_WORKING )



pda600.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

        Amstrad PenPad PDA 600

    PCB Layout (front):
         /-----------------------------------------------------------------------\
        /                U4                                          +---+        \
       /          X1  HD64610FP       U9       U6                    | C |         \
      /       U3                    HC157A   HC157A        U12       | N |          \
     |    KM681000ALT-8                                 HD64646FS    | 3 |           |
     |                                                               +---+           |
     |    U36          U2                                                            |
     |  TL061AC       42069                                      U35       U27      /
     |                                          U5             TCM5089   LP324M    /
     | +---+                                   41857                              |
     | | C |                 U1                            U31         U33        |
     | | N |             Z8S18016FSC             U43      41863    KM62256BLG-10  |
     | | 2 |                                    HC541                             |
     | +---+                                                          U32          \
     |        U30                                               U37  HC574A         \
     |     MAX222CWN                                   U24     HC20           +---+  |
     |                                                41864                   | C |  |
     |     U21           U25                                                  | N |  |
     |  MAX731CWE       MAX641                                                | 4 |  |
     |                                                                        +---+  |
     +-------------------------------------------------------------------------------+

    PCB Layout (back):
     +-------------------------------------------------------------------------------+
     |  BAT-           +-----+                                                 BAT+  |
     |                /       \                                                      |
     |               |   3V    |                               U23                   |
     |               |   Bat.  |                              HC00A        U20       |
     |                \       /      +------+       U26                  14052B      |
     |                 +-----+       |      |     3226NUT                           /
     |       U10        U41          |      |                                      /
     |     HC245A      HC541         |      |                     U28             |
     |                          X2   |      |                  MC145053D          |
     |                  U42          | CN1  |                                     |
     |   U22           HC541         |      |                                     |
     | 7673CBA                       |      |                                      \
     |          +----+               |      |                                       \
     |         / Beep \              |      |                                        |
     |         \      /              +------+                                        |
     |          +----+                           U11                                 |
      \        U18         U8      U7         KM62256BLG                            /
       \      HC00A      HC157A  HC157A                                            /
        \                                                                         /
         \-----------------------------------------------------------------------/


    Parts:
     U2  - Amstrad 42069 ROM, probably 27C1001
     U5  - Amstrad 41857 100 Pin, unknown
     U31 - Amstrad 41863 44 Pin, unknown
     U24 - Amstrad 41864 18 Pin, unknown
     X1  - 32768 XTAL
     X2  - 28.6MHz XTAL

    Connectors:
     CN1 - PCMCIA
     CN2 - 8 Pin Serial
     CN3 - LCD
     CN4 - Digitizer

    TODO:
    - Sound (DTMF tone generator).
    - Refactor the HD64646FS LCD controller into a device.
    - Dump the character recognition MCU (possible?).
    - Serial port doesn't work.

****************************************************************************/

#include "emu.h"

#include "pda600_copro.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "bus/rs232/rs232.h"
#include "cpu/z180/z180.h"
#include "machine/nvram.h"
#include "machine/hd64610.h"
#include "machine/timer.h"
#include "sound/beep.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "pda600.lh"


//**************************************************************************
//  CONSTANTS
//**************************************************************************

static constexpr u32 PDA600_SCREEN_X = 38;
static constexpr u32 PDA600_SCREEN_Y = 54;
static constexpr u32 PDA600_SCREEN_W = (PDA600_SCREEN_X * 2 + 240);
static constexpr u32 PDA600_SCREEN_H = (PDA600_SCREEN_Y * 2 + 320);
static constexpr u32 PDA600_CSIO_RATE = 48000;
static constexpr u32 PDA600_SERIAL_PORT_RATE = 9600;


namespace {

class pda600_state : public driver_device
{
public:
	pda600_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_copro(*this, "copro")
		, m_card(*this, "pcmcia")
		, m_beep(*this, "beeper")
		, m_pen(*this, {"PEN", "PENX", "PENY"})
		, m_battery(*this, "BATTERY")
		, m_video_ram(*this, "videoram")
	{
	}

	void pda600(machine_config &config);
	void power_off_w(int state) { m_maincpu->set_input_line(Z180_INPUT_LINE_IRQ1, state); }

private:
	required_device<z180_device> m_maincpu;
	required_device<pda600_copro_device> m_copro;
	required_device<generic_slot_device> m_card;
	required_device<beep_device> m_beep;
	required_ioport_array<3> m_pen;
	required_ioport m_battery;
	required_shared_ptr<u8> m_video_ram;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void io_w(offs_t offset, u8 data);
	u8 io_r(offs_t offset);
	void pcmcia_w(offs_t offset, u8 data);
	u8 pcmcia_r(offs_t offset);
	void tone_w(u8 data);
	TIMER_CALLBACK_MEMBER(csio_clk_timer);
	TIMER_CALLBACK_MEMBER(serl_clk_timer);
	TIMER_DEVICE_CALLBACK_MEMBER(pen_update_timer);
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(card_load);
	DECLARE_DEVICE_IMAGE_UNLOAD_MEMBER(card_unload);

	void pda600_io(address_map &map) ATTR_COLD;
	void pda600_mem(address_map &map) ATTR_COLD;

	emu_timer * m_csio_clk_timer;
	emu_timer * m_serl_clk_timer;
	u8   m_pen_data[6]  = {};
	u8   m_pen_shift    = 0;
	u8   m_pen_cnt      = 0;
	u32  m_card_size    = 0;
	u8   m_rtc_irq      = 0;
	u8   m_serl_clk     = 0;
	u8   m_io_regs[4]   = {};
	u8   m_lcd_ar       = 0;
	u8   m_lcd_regs[32] = {};
};


void pda600_state::tone_w(u8 data)
{
	// xxxx ---- TCM5089 Column 1-4
	// ---- xxxx TCM5089 Row 1-4

	// TODO: DTMF tone encoder

	if (data & 0x7f)
		popmessage("DTMF tone %02X", data);

	m_beep->set_state(BIT(data, 7));
}


u8 pda600_state::io_r(offs_t offset)
{
	return m_io_regs[offset];
}


void pda600_state::io_w(offs_t offset, u8 data)
{
	switch (offset)
	{
	case 0:
		// xx-- ---- PCMCIA bank select
		// ---x -x-- Serial
		if (BIT(m_io_regs[offset] ^ data, 4))
		{
			auto period = attotime::zero;
			if (BIT(data, 4))
				period = attotime::from_hz(PDA600_SERIAL_PORT_RATE * 16 * 2);

			m_serl_clk_timer->adjust(period, 0, period);
		}
		break;
	case 1:
		break;
	case 2:
		// ---- xxxx LCD contrast level
		// ---x ---- Reset the coprocessor
		// --x- ---- Wake up the coprocessor
		if ((m_io_regs[offset] ^ data) & 0x0f)
			logerror("Set LCD contrast level: %d\n", data & 0x0f);

		m_copro->wakeup_w(BIT(data, 4));
		m_copro->reset_w(BIT(data, 5));
		break;
	case 3:
		// --X- ---- ??
		break;
	}

	m_io_regs[offset] = data;
}


u8 pda600_state::pcmcia_r(offs_t offset)
{
	offset |= (u32)(m_io_regs[0] & 0xc0) << 13;
	if (offset < m_card_size)
		return m_card->read_ram(offset);

	return 0xff;
}


void pda600_state::pcmcia_w(offs_t offset, u8 data)
{
	offset |= (u32)(m_io_regs[0] & 0xc0) << 13;
	if (offset < m_card_size)
		m_card->write_ram(offset, data);
}


void pda600_state::pda600_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x1ffff).rom();
	map(0x20000, 0x9ffff).rw(FUNC(pda600_state::pcmcia_r), FUNC(pda600_state::pcmcia_w));
	map(0xa0000, 0xa7fff).ram().share("videoram");
	map(0xe0000, 0xfffff).ram().share("nvram");
}

void pda600_state::pda600_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x3f).noprw(); /* Z180 internal registers */
	map(0x40, 0x43).rw(FUNC(pda600_state::io_r), FUNC(pda600_state::io_w));
	map(0x80, 0x8f).rw("rtc", FUNC(hd64610_device::read), FUNC(hd64610_device::write));
	map(0xc0, 0xc0).lw8(NAME([this](u8 data) { m_lcd_ar = data & 0x1f; }));
	map(0xc1, 0xc1).lw8(NAME([this](u8 data) { m_lcd_regs[m_lcd_ar] = data; }));
}

/* Input ports */
static INPUT_PORTS_START( pda600 )
	PORT_START("POWER")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_WRITE_LINE_MEMBER(FUNC(pda600_state::power_off_w))

	PORT_START("BATTERY")
	PORT_CONFNAME(0x0f, 0x0f, "Main battery status")
	PORT_CONFSETTING(0x0f, "Good")
	PORT_CONFSETTING(0x0c, "Fair")
	PORT_CONFSETTING(0x06, "Low")
	PORT_CONFSETTING(0x00, "Replace")
	PORT_CONFNAME(0x10, 0x00, "Backup battery status")
	PORT_CONFSETTING(0x00, "Good")
	PORT_CONFSETTING(0x10, "Replace")

	PORT_START("PENX")
	PORT_BIT(0x3ff, 0x000, IPT_LIGHTGUN_X) PORT_SENSITIVITY(40) PORT_CROSSHAIR(X, 1, 0, 0) PORT_MINMAX(0, 0x3ff) PORT_KEYDELTA(1)

	PORT_START("PENY")
	PORT_BIT(0x3ff, 0x000, IPT_LIGHTGUN_Y) PORT_SENSITIVITY(40) PORT_CROSSHAIR(Y, 1, 0, 0) PORT_MINMAX(0, 0x3ff) PORT_KEYDELTA(1)

	PORT_START("PEN")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Pen")
INPUT_PORTS_END


void pda600_state::machine_start()
{
	// state saving
	save_item(NAME(m_pen_data));
	save_item(NAME(m_pen_shift));
	save_item(NAME(m_pen_cnt));
	save_item(NAME(m_card_size));
	save_item(NAME(m_rtc_irq));
	save_item(NAME(m_serl_clk));
	save_item(NAME(m_io_regs));
	save_item(NAME(m_lcd_ar));
	save_item(NAME(m_lcd_regs));

	m_csio_clk_timer = timer_alloc(FUNC(pda600_state::csio_clk_timer), this);
	m_serl_clk_timer = timer_alloc(FUNC(pda600_state::serl_clk_timer), this);
}


void pda600_state::machine_reset()
{
	m_pen_shift = -1;
	m_pen_cnt   = 0;
	m_lcd_ar    = 0;
	m_rtc_irq   = 0;
	m_serl_clk  = 0;
	std::fill(std::begin(m_pen_data), std::end(m_pen_data), 0U);
	std::fill(std::begin(m_io_regs), std::end(m_io_regs), 0U);
	std::fill(std::begin(m_lcd_regs), std::end(m_lcd_regs), 0U);
}


u32 pda600_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	bitmap.fill(1);

	if (!BIT(m_lcd_regs[0x16], 4))
		return 0;

	const u16 width  = (m_lcd_regs[0x11] << 8) | m_lcd_regs[0x12];
	const u16 height = (m_lcd_regs[0x13] << 8) | m_lcd_regs[0x14];

	for (int y = 0; y <= height; y++)
		for (int x = 0; x < width; x++)
		{
			const s32 dst_y = PDA600_SCREEN_Y + y;
			const s32 dst_x = PDA600_SCREEN_X + x * 8;
			u8 data = m_video_ram[y * m_lcd_regs[1] + x] ^ 0xff;

			for (int px = 0; px < 8; px++)
			{
				if (cliprect.contains(dst_x + px, dst_y))
					bitmap.pix(dst_y, dst_x + px) = BIT(data, 7);

				data <<= 1;
			}
		}

	return 0;
}

static const gfx_layout pda600_charlayout_8 =
{
	8, 8,
	49,
	1,
	{ 0 },
	{ 0,1,2,3,4,5,6,7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};

static const gfx_layout pda600_charlayout_13 =
{
	8, 13,
	123,
	1,
	{ 0 },
	{ 0,1,2,3,4,5,6,7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 8*9, 8*10, 8*11, 8*12 },
	8*13
};

static const gfx_layout pda600_charlayout_13a =
{
	8, 13,
	132,
	1,
	{ 0 },
	{ 0,1,2,3,4,5,6,7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 8*9, 8*10, 8*11, 8*12 },
	8*13
};

static const gfx_layout pda600_charlayout_19 =
{
	8, 19,
	32,
	1,
	{ 0 },
	{ 0,1,2,3,4,5,6,7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 8*9, 8*10, 8*11, 8*12, 8*13, 8*14, 8*15, 8*16, 8*17, 8*18 },
	8*19
};

static const gfx_layout pda600_charlayout_19a =
{
	8, 19,
	11,
	1,
	{ 0 },
	{ 0,1,2,3,4,5,6,7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 8*9, 8*10, 8*11, 8*12, 8*13, 8*14, 8*15, 8*16, 8*17, 8*18 },
	8*19
};

static GFXDECODE_START( gfx_pda600 )
	GFXDECODE_ENTRY( "maincpu", 0x45cd, pda600_charlayout_19, 0, 1 )
	GFXDECODE_ENTRY( "maincpu", 0x4892, pda600_charlayout_19a, 0, 1 )
	GFXDECODE_ENTRY( "maincpu", 0x4d73, pda600_charlayout_8, 0, 1 )
	GFXDECODE_ENTRY( "maincpu", 0x5b8f, pda600_charlayout_13, 0, 1 )
	GFXDECODE_ENTRY( "maincpu", 0x61d3, pda600_charlayout_13a, 0, 1 )
GFXDECODE_END


TIMER_DEVICE_CALLBACK_MEMBER(pda600_state::pen_update_timer)
{
	u8 pen = m_pen[0]->read();

	// Reduce the update rate when the pen is not held down
	if (!pen && ++m_pen_cnt < 20)
		return;
	else
		m_pen_cnt = 0;

	m_pen_data[0] = 0x80;               // Start of new data
	m_pen_data[0] |= pen << 6;          // Pen up/down
	m_pen_data[0] |= m_rtc_irq << 5;    // RTC IRQ status
	m_pen_data[0] |= m_battery->read(); // Battery status

	// Pen position (updated only when the pen is down)
	if (pen)
	{
		u16 penx = m_pen[1]->read();
		u16 peny = m_pen[2]->read();
		m_pen_data[1] = penx & 0x7f;
		m_pen_data[2] = (penx >> 7) & 0x7f;
		m_pen_data[3] = peny & 0x7f;
		m_pen_data[4] = (peny >> 7) & 0x7f;
	}

	// Data checksum
	m_pen_data[5] = m_pen_data[0] + m_pen_data[1] + m_pen_data[2] + m_pen_data[3] + m_pen_data[4];

	// Start CSIO clock
	m_pen_shift = 0;
	m_csio_clk_timer->adjust(attotime::zero);
}


TIMER_CALLBACK_MEMBER(pda600_state::csio_clk_timer)
{
	if (m_pen_shift < 48)
	{
		m_maincpu->cks_w(0);
		m_maincpu->rxs_cts1_w(BIT(m_pen_data[m_pen_shift / 8], m_pen_shift & 7));
		m_maincpu->cks_w(1);
		m_pen_shift++;

		// If there is still data to send, reschedule the timer
		if (m_pen_shift < 48)
		{
			auto delay = attotime::from_hz(PDA600_CSIO_RATE);

			// A delay is added after each byte to allow the maincpu to read the transmitted data
			if (!(m_pen_shift & 7))
				delay += attotime::from_usec(150);

			m_csio_clk_timer->adjust(delay);
		}
	}
}


TIMER_CALLBACK_MEMBER(pda600_state::serl_clk_timer)
{
	// External clock for the Z180 ASCI0
	m_serl_clk ^= 1;
	m_maincpu->cka0_w(m_serl_clk);
}


DEVICE_IMAGE_LOAD_MEMBER(pda600_state::card_load)
{
	if (!image.loaded_through_softlist())
	{
		const u64 size = image.length();
		m_card->ram_alloc(size);

		if (size != image.fread(m_card->get_ram_base(), size))
			return std::make_pair(image_error::UNSPECIFIED, std::string());

		m_card_size = size;
	}
	else
	{
		m_card_size = image.get_software_region_length("rom");

		if (m_card_size == 0)
			return std::make_pair(image_error::BADSOFTWARE, "rom data area is missing or empty");

		m_card->ram_alloc(m_card_size);
		memcpy(m_card->get_ram_base(), image.get_software_region("rom"), m_card_size);
	}

	m_card->battery_load(m_card->get_ram_base(), m_card_size, nullptr);
	return std::make_pair(std::error_condition(), std::string());
}


DEVICE_IMAGE_UNLOAD_MEMBER(pda600_state::card_unload)
{
	m_card->battery_save(m_card->get_ram_base(), m_card_size);
	memset(m_card->get_ram_base(), 0xff, m_card_size);
	m_card_size = 0;
}


void pda600_state::pda600(machine_config &config)
{
	/* basic machine hardware */
	Z8S180(config, m_maincpu, 28'636'363_Hz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &pda600_state::pda600_mem);
	m_maincpu->set_addrmap(AS_IO, &pda600_state::pda600_io);
	m_maincpu->txa0_wr_callback().set("serial", FUNC(rs232_port_device::write_txd));
	m_maincpu->rts0_wr_callback().set("serial", FUNC(rs232_port_device::write_rts));
	m_maincpu->txa1_wr_callback().set(m_copro, FUNC(pda600_copro_device::write_txd));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(PDA600_SCREEN_W, PDA600_SCREEN_H);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(pda600_state::screen_update));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_pda600);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	// NVRAM needs to be filled with random data to fail the checksum and be initialized correctly
	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	hd64610_device &rtc(HD64610(config, "rtc", 32.768_kHz_XTAL));
	rtc.irq().set([this](int state) { m_rtc_irq = state; }).invert();

	TIMER(config, "pen_update_timer").configure_periodic(FUNC(pda600_state::pen_update_timer), attotime::from_hz(100));

	GENERIC_CARTSLOT(config, m_card, generic_romram_plain_slot, "pda600", "bin");
	m_card->set_device_load(FUNC(pda600_state::card_load));
	m_card->set_device_unload(FUNC(pda600_state::card_unload));
	m_card->set_interface("pcmcia");

	PDA600_COPRO_HLE(config, m_copro, 28'636'363_Hz_XTAL / 2);
	m_copro->tx_callback().set(m_maincpu, FUNC(z180_device::rxa1_w));
	m_copro->tone_callback().set(FUNC(pda600_state::tone_w));

	rs232_port_device &rs232(RS232_PORT(config, "serial", default_rs232_devices, "printer"));
	rs232.rxd_handler().set(m_maincpu, FUNC(z180_device::rxa0_w));
	rs232.cts_handler().set(m_maincpu, FUNC(z180_device::cts0_w));
	rs232.cts_handler().append_inputline(m_maincpu, Z180_INPUT_LINE_DREQ0).invert();

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 1633).add_route(ALL_OUTPUTS, "mono", 0.80);    // TODO: replace with TCM5089

	// software lists
	SOFTWARE_LIST(config, "card_list").set_original("pda600");

	config.set_default_layout(layout_pda600);
}

/* ROM definition */
ROM_START( pda600 )
	ROM_REGION( 0x20000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "pdarom.bin", 0x00000, 0x20000, CRC(f793a6c5) SHA1(ab14b0fdcedb927c66357368a2bfff605ba758fb))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY        FULLNAME          FLAGS */
COMP( 1993, pda600, 0,      0,      pda600,  pda600, pda600_state, empty_init, "Amstrad plc", "PenPad PDA 600", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



pdp1.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Raphael Nabet
/*

Driver for a PDP1 emulator.

    Digital Equipment Corporation
    Brian Silverman (original Java Source)
    Vadim Gerasimov (original Java Source)
    Chris Salomon (MESS driver)
    Raphael Nabet (MESS driver)

Initially, this was a conversion of a JAVA emulator
(although code has been edited extensively ever since).
I have tried contacting the author, but heard as yet nothing of him,
so I don't know if it all right with him, but after all -> he did
release the source, so hopefully everything will be fine (no his
name is not Marat).

Note: naturally I have no PDP1, I have never seen one, nor have I any
programs for it.

The first supported program was:

SPACEWAR!

The first Videogame EVER!

When I saw the java emulator, running that game I was quite intrigued to
include a driver for MESS.
I think the historical value of SPACEWAR! is enormous.

Two other programs are supported: Munching squares and LISP.

Added Debugging and Disassembler...


Also:
ftp://minnie.cs.adfa.oz.au/pub/PDP-11/Sims/Supnik_2.3/software/lispswre.tar.gz
Is a packet which includes the original LISP as source and
binary form plus a macro assembler for PDP1 programs.

For more documentation look at the source for the driver,
and the cpu/pdp1/pdp1.c file (information about the whereabouts of information
and the java source).


To load and play a game:
- Load a .rim file into the first tape reader
- Hold down Left Control, and press Enter. Let go.
- The lights will flash while the paper tape is being read.
- At the end, the game will start.



*/

#include "emu.h"
#include "pdp1.h"

#include "cpu/pdp1/pdp1.h"

#include "screen.h"
#include "softlist_dev.h"

#define LOG_IOT_EXTRA (1U << 1)

/* IOT completion may take much more than the 5us instruction time.  A possible programming
error would be to execute an IOT before the last IOT of the same kind is over: such a thing
would confuse the targeted IO device, which might ignore the latter IOT, execute either
or both IOT incompletely, or screw up completely.  I insist that such an error can be caused
by a pdp-1 programming error, even if there is no emulator error. */
#define LOG_IOT_OVERLAP (1U << 2)

#define VERBOSE (0)
#include "logmacro.h"


/*
 *
 * The loading storing OS... is not emulated (I haven't a clue where to
 * get programs for the machine)
 *
 */


void pdp1_state::pdp1_map(address_map &map)
{
	map(0x00000, 0x3ffff).ram();
}

static INPUT_PORTS_START( pdp1 )
	PORT_START("SPACEWAR")      /* 0: spacewar controllers */
	PORT_BIT( ROTATE_LEFT_PLAYER1, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT) PORT_NAME("Spin Left Player 1") PORT_CODE(KEYCODE_A) PORT_CODE(JOYCODE_X_LEFT_SWITCH)
	PORT_BIT( ROTATE_RIGHT_PLAYER1, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_NAME("Spin Right Player 1") PORT_CODE(KEYCODE_S) PORT_CODE(JOYCODE_X_RIGHT_SWITCH)
	PORT_BIT( THRUST_PLAYER1, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Thrust Player 1") PORT_CODE(KEYCODE_D) PORT_CODE(JOYCODE_BUTTON1)
	PORT_BIT( FIRE_PLAYER1, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Fire Player 1") PORT_CODE(KEYCODE_F) PORT_CODE(JOYCODE_BUTTON2)
	PORT_BIT( ROTATE_LEFT_PLAYER2, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT) PORT_NAME("Spin Left Player 2") PORT_CODE(KEYCODE_LEFT) PORT_CODE(JOYCODE_X_LEFT_SWITCH ) PORT_PLAYER(2)
	PORT_BIT( ROTATE_RIGHT_PLAYER2, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_NAME("Spin Right Player 2") PORT_CODE(KEYCODE_RIGHT) PORT_CODE(JOYCODE_X_RIGHT_SWITCH ) PORT_PLAYER(2)
	PORT_BIT( THRUST_PLAYER2, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Thrust Player 2") PORT_CODE(KEYCODE_UP) PORT_CODE(JOYCODE_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( FIRE_PLAYER2, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Fire Player 2") PORT_CODE(KEYCODE_DOWN) PORT_CODE(JOYCODE_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( HSPACE_PLAYER1, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_NAME("Hyperspace Player 1") PORT_CODE(KEYCODE_Z) PORT_CODE(JOYCODE_BUTTON3)
	PORT_BIT( HSPACE_PLAYER2, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_NAME("Hyperspace Player 2") PORT_CODE(KEYCODE_SLASH) PORT_CODE(JOYCODE_BUTTON3 ) PORT_PLAYER(2)

	PORT_START("CSW")       /* 1: various pdp1 operator control panel switches */
	PORT_BIT(pdp1_control, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("control panel key") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(pdp1_extend, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("extend") PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(pdp1_start_nobrk, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("start (sequence break disabled)") PORT_CODE(KEYCODE_U)
	PORT_BIT(pdp1_start_brk, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("start (sequence break enabled)") PORT_CODE(KEYCODE_I)
	PORT_BIT(pdp1_stop, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("stop") PORT_CODE(KEYCODE_O)
	PORT_BIT(pdp1_continue, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("continue") PORT_CODE(KEYCODE_P)
	PORT_BIT(pdp1_examine, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("examine") PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(pdp1_deposit, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("deposit") PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(pdp1_read_in, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("read in") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(pdp1_reader, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("reader")
	PORT_BIT(pdp1_tape_feed, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("tape feed")
	PORT_BIT(pdp1_single_step, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("single step") PORT_CODE(KEYCODE_STOP)
	PORT_BIT(pdp1_single_inst, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("single inst") PORT_CODE(KEYCODE_SLASH)

	PORT_START("SENSE")     /* 2: operator control panel sense switches */
	PORT_DIPNAME(     040, 000, "Sense Switch 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_DIPSETTING(    000, DEF_STR( Off ) )
	PORT_DIPSETTING(    040, DEF_STR( On ) )
	PORT_DIPNAME(     020, 000, "Sense Switch 2") PORT_CODE(KEYCODE_2_PAD)
	PORT_DIPSETTING(    000, DEF_STR( Off ) )
	PORT_DIPSETTING(    020, DEF_STR( On ) )
	PORT_DIPNAME(     010, 000, "Sense Switch 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_DIPSETTING(    000, DEF_STR( Off ) )
	PORT_DIPSETTING(    010, DEF_STR( On ) )
	PORT_DIPNAME(     004, 000, "Sense Switch 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_DIPSETTING(    000, DEF_STR( Off ) )
	PORT_DIPSETTING(    004, DEF_STR( On ) )
	PORT_DIPNAME(     002, 002, "Sense Switch 5") PORT_CODE(KEYCODE_5_PAD)
	PORT_DIPSETTING(    000, DEF_STR( Off ) )
	PORT_DIPSETTING(    002, DEF_STR( On ) )
	PORT_DIPNAME(     001, 000, "Sense Switch 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_DIPSETTING(    000, DEF_STR( Off ) )
	PORT_DIPSETTING(    001, DEF_STR( On ) )

	PORT_START("TSTADD")        /* 3: operator control panel test address switches */
	PORT_BIT( 0100000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Extension Test Address Switch 3") PORT_CODE(KEYCODE_1)
	PORT_BIT( 0040000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Extension Test Address Switch 4") PORT_CODE(KEYCODE_2)
	PORT_BIT( 0020000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Extension Test Address Switch 5") PORT_CODE(KEYCODE_3)
	PORT_BIT( 0010000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Extension Test Address Switch 6") PORT_CODE(KEYCODE_4)
	PORT_BIT( 0004000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 7") PORT_CODE(KEYCODE_5)
	PORT_BIT( 0002000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 8") PORT_CODE(KEYCODE_6)
	PORT_BIT( 0001000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 9") PORT_CODE(KEYCODE_7)
	PORT_BIT( 0000400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 10") PORT_CODE(KEYCODE_8)
	PORT_BIT( 0000200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 11") PORT_CODE(KEYCODE_9)
	PORT_BIT( 0000100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 12") PORT_CODE(KEYCODE_0)
	PORT_BIT( 0000040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 13") PORT_CODE(KEYCODE_MINUS)
	PORT_BIT( 0000020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 14") PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT( 0000010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 15") PORT_CODE(KEYCODE_Q)
	PORT_BIT( 0000004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 16") PORT_CODE(KEYCODE_W)
	PORT_BIT( 0000002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 17") PORT_CODE(KEYCODE_E)
	PORT_BIT( 0000001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 18") PORT_CODE(KEYCODE_R)

	PORT_START("TWDMSB")        /* 4: operator control panel test word switches MSB */
	PORT_BIT(    0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 1") PORT_CODE(KEYCODE_A)
	PORT_BIT(    0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 2") PORT_CODE(KEYCODE_S)

	PORT_START("TWDLSB")        /* 5: operator control panel test word switches LSB */
	PORT_BIT( 0100000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 3") PORT_CODE(KEYCODE_D)
	PORT_BIT( 0040000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 4") PORT_CODE(KEYCODE_F)
	PORT_BIT( 0020000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 5") PORT_CODE(KEYCODE_G)
	PORT_BIT( 0010000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 6") PORT_CODE(KEYCODE_H)
	PORT_BIT( 0004000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 7") PORT_CODE(KEYCODE_J)
	PORT_BIT( 0002000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 8") PORT_CODE(KEYCODE_K)
	PORT_BIT( 0001000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 9") PORT_CODE(KEYCODE_L)
	PORT_BIT( 0000400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 10") PORT_CODE(KEYCODE_COLON)
	PORT_BIT( 0000200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 11") PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT( 0000100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 12") PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT( 0000040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 13") PORT_CODE(KEYCODE_Z)
	PORT_BIT( 0000020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 14") PORT_CODE(KEYCODE_X)
	PORT_BIT( 0000010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 15") PORT_CODE(KEYCODE_C)
	PORT_BIT( 0000004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 16") PORT_CODE(KEYCODE_V)
	PORT_BIT( 0000002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 17") PORT_CODE(KEYCODE_B)
	PORT_BIT( 0000001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 18") PORT_CODE(KEYCODE_N)

	/*
	    Note that I can see 2 additional keys whose purpose is unknown to me.
	    The caps look like "MAR REL" for the leftmost one and "MAR SET" for
	    rightmost one: maybe they were used to set the margin (I don't have the
	    manual for the typewriter). */

	PORT_START("TWR.0")      /* 6: typewriter codes 00-17 */
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(Space)") PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1 \"") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2 '") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3 ~") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4 (implies)") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5 (or)") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6 (and)") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7 <") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8 >") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9 (up arrow)") PORT_CODE(KEYCODE_9)

	PORT_START("TWR.1")      /* 7: typewriter codes 20-37 */
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0 (right arrow)") PORT_CODE(KEYCODE_0)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(", =") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab Key") PORT_CODE(KEYCODE_TAB)

	PORT_START("TWR.2")      /* 8: typewriter codes 40-57 */
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(non-spacing middle dot) _") PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("- +") PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(") ]") PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(non-spacing overstrike) |") PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("( [") PORT_CODE(KEYCODE_MINUS)

	PORT_START("TWR.3")      /* 9: typewriter codes 60-77 */
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Lower Case") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(". (multiply)") PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Upper case") PORT_CODE(KEYCODE_RSHIFT)
	/* hack to support my macintosh which does not differentiate the  Right Shift key */
	/* PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Upper case") PORT_CODE(KEYCODE_CAPSLOCK) */
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER)

	PORT_START("CFG")       /* 10: pseudo-input port with config */
	PORT_DIPNAME( 0x0003, 0x0002, "RAM size")
	PORT_DIPSETTING(   0x0000, "4kw" )
	PORT_DIPSETTING(   0x0001, "32kw")
	PORT_DIPSETTING(   0x0002, "64kw")
	PORT_DIPNAME( 0x0004, 0x0000, "Hardware multiply")
	PORT_DIPSETTING(   0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(   0x0004, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0000, "Hardware divide")
	PORT_DIPSETTING(   0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(   0x0008, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0000, "Type 20 sequence break system")
	PORT_DIPSETTING(   0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(   0x0010, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0000, "Type 32 light pen") PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_DIPSETTING(   0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(   0x0020, DEF_STR( On ) )

	PORT_START("LIGHTPEN")  /* 11: pseudo-input port with lightpen status */
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("select larger light pen tip") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("select smaller light pen tip") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("light pen down")

	PORT_START("LIGHTX") /* 12: lightpen - X AXIS */
	PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1) PORT_RESET

	PORT_START("LIGHTY") /* 13: lightpen - Y AXIS */
	PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1) PORT_RESET
INPUT_PORTS_END


static const gfx_layout fontlayout =
{
	6, 8,           /* 6*8 characters */
	pdp1_charnum,   /* 96+4 characters */
	1,              /* 1 bit per pixel */
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* straightforward layout */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8 /* every char takes 8 consecutive bytes */
};


/*
    The static palette only includes the pens for the control panel and
    the typewriter, as the CRT palette is generated dynamically.

    The CRT palette defines various levels of intensity between white and
    black.  Grey levels follow an exponential law, so that decrementing the
    color index periodically will simulate the remanence of a cathode ray tube.
*/
static constexpr rgb_t pdp1_colors[] =
{
	{0x00, 0x00, 0x00 }, // black
	{0xff, 0xff, 0xff }, // white
	{0x00, 0xff, 0x00 }, // green
	{0x00, 0x40, 0x00 }, // dark green
	{0xff, 0x00, 0x00 }, // red
	{0x80, 0x80, 0x80 }  // light gray
};

static constexpr uint8_t pdp1_pens[] =
{
	pen_panel_bg, pen_panel_caption,
	pen_typewriter_bg, pen_black,
	pen_typewriter_bg, pen_red
};

static const uint8_t total_colors_needed = pen_crt_num_levels + sizeof(pdp1_colors) / 3;

static GFXDECODE_START( gfx_pdp1 )
	GFXDECODE_ENTRY( "gfx1", 0, fontlayout, pen_crt_num_levels + sizeof(pdp1_colors) / 3, 3 )
GFXDECODE_END

/* Initialise the palette */
void pdp1_state::pdp1_palette(palette_device &palette) const
{
	// rgb components for the two color emissions
	constexpr double r1 = .1, g1 = .1, b1 = .924, r2 = .7, g2 = .7, b2 = .076;
	// half period in seconds for the two color emissions
	constexpr double half_period_1 = .05, half_period_2 = .20;
	// refresh period in seconds
	constexpr double update_period = 1./refresh_rate;
	double decay_1, decay_2;
	double cur_level_1, cur_level_2;

	// initialize CRT palette

	// compute the decay factor per refresh frame
	decay_1 = pow(.5, update_period / half_period_1);
	decay_2 = pow(.5, update_period / half_period_2);

	cur_level_1 = cur_level_2 = 255.;   // start with maximum level

	for (int i = pen_crt_max_intensity; i>0; i--)
	{
		// compute the current color
		int const r = int((r1*cur_level_1 + r2*cur_level_2) + .5);
		int const g = int((g1*cur_level_1 + g2*cur_level_2) + .5);
		int const b = int((b1*cur_level_1 + b2*cur_level_2) + .5);
		// write color in palette
		palette.set_indirect_color(i, rgb_t(r, g, b));
		// apply decay for next iteration
		cur_level_1 *= decay_1;
		cur_level_2 *= decay_2;
	}

	palette.set_indirect_color(0, rgb_t(0, 0, 0));

	// load static palette
	for (int i = 0; i < 6; i++)
		palette.set_indirect_color(pen_crt_num_levels + i, pdp1_colors[i]);

	// copy colortable to palette
	for (int i = 0; i < total_colors_needed; i++)
		palette.set_pen_indirect(i, i);

	// set up palette for text
	for (int i = 0; i < 6; i++)
		palette.set_pen_indirect(total_colors_needed + i, pdp1_pens[i]);
}


/*
    pdp1 machine code

    includes emulation for I/O devices (with the IOT opcode) and control panel functions

    TODO:
    * typewriter out should overwrite the typewriter buffer
    * improve emulation of the internals of tape reader?
    * improve puncher timing?
*/


/* This flag makes the emulated pdp-1 trigger a sequence break request when a character has been
typed on the typewriter keyboard.  It is useful in order to test sequence break, but we need
to emulate a connection box in which we can connect each wire to any interrupt line.  Also,
we need to determine the exact relationship between the status register and the sequence break
system (both standard and type 20). */
#define USE_SBS 0

/*
    devices which are known to generate a completion pulse (source: maintenance manual 9-??,
    and 9-20, 9-21):
    emulated:
    * perforated tape reader
    * perforated tape punch
    * typewriter output
    * CRT display
    unemulated:
    * card punch (pac: 43)
    * line printer (lpr, lsp, but NOT lfb)

    This list should probably include additional optional devices (card reader comes to mind).
*/

/* IO status word */

/* defines for io_status bits */
enum
{
	io_st_pen = 0400000,    /* light pen: light has hit the pen */
	io_st_ptr = 0200000,    /* perforated tape reader: reader buffer full */
	io_st_tyo = 0100000,    /* typewriter out: device ready */
	io_st_tyi = 0040000,    /* typewriter in: new character in buffer */
	io_st_ptp = 0020000     /* perforated tape punch: device ready */
};







/* crt display timer */

/* light pen config */



#define PARALLEL_DRUM_WORD_TIME attotime::from_nsec(8500)
#define PARALLEL_DRUM_ROTATION_TIME attotime::from_nsec(8500*4096)


static pdp1_reset_param_t pdp1_reset_param =
{
	0,  /* extend mode support defined in input ports and pdp1_init_machine */
	0,  /* hardware multiply/divide support defined in input ports and pdp1_init_machine */
	0   /* type 20 sequence break system support defined in input ports and pdp1_init_machine */
};

void pdp1_state::machine_reset()
{
	int cfg = m_cfg->read();

	pdp1_reset_param.extend_support = (cfg >> pdp1_config_extend_bit) & pdp1_config_extend_mask;
	pdp1_reset_param.hw_mul_div = (cfg >> pdp1_config_hw_mul_div_bit) & pdp1_config_hw_mul_div_mask;
	pdp1_reset_param.type_20_sbs = (cfg >> pdp1_config_type_20_sbs_bit) & pdp1_config_type_20_sbs_mask;

	/* reset device state */
	m_tape_reader->m_rcl = m_tape_reader->m_rc = 0;
	m_io_status = io_st_tyo | io_st_ptp;
	m_lightpen.active = m_lightpen.down = 0;
	m_lightpen.x = m_lightpen.y = 0;
	m_lightpen.radius = 10; /* ??? */
	pdp1_update_lightpen_state(&m_lightpen);
}


void pdp1_state::pdp1_machine_stop()
{
	/* the core will take care of freeing the timers, BUT we must set the variables
	to NULL if we don't want to risk confusing the tape image init function */
	m_tape_reader->m_timer = m_tape_puncher->m_timer = m_typewriter->m_tyo_timer = m_dpy_timer = nullptr;
}


/*
    driver init function

    Set up the pdp1_memory pointer, and generate font data.
*/
void pdp1_state::machine_start()
{
	uint8_t *dst;

	static const unsigned char fontdata6x8[pdp1_fontdata_size] =
	{   /* ASCII characters */
		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,
		0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0xf8,0x50,0xf8,0x50,0x00,0x00,
		0x20,0x70,0xc0,0x70,0x18,0xf0,0x20,0x00,0x40,0xa4,0x48,0x10,0x20,0x48,0x94,0x08,
		0x60,0x90,0xa0,0x40,0xa8,0x90,0x68,0x00,0x10,0x20,0x40,0x00,0x00,0x00,0x00,0x00,
		0x20,0x40,0x40,0x40,0x40,0x40,0x20,0x00,0x10,0x08,0x08,0x08,0x08,0x08,0x10,0x00,
		0x20,0xa8,0x70,0xf8,0x70,0xa8,0x20,0x00,0x00,0x20,0x20,0xf8,0x20,0x20,0x00,0x00,
		0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x60,0x00,0x00,0x00,0xf8,0x00,0x00,0x00,0x00,
		0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,
		0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x10,0x30,0x10,0x10,0x10,0x10,0x10,0x00,
		0x70,0x88,0x08,0x10,0x20,0x40,0xf8,0x00,0x70,0x88,0x08,0x30,0x08,0x88,0x70,0x00,
		0x10,0x30,0x50,0x90,0xf8,0x10,0x10,0x00,0xf8,0x80,0xf0,0x08,0x08,0x88,0x70,0x00,
		0x70,0x80,0xf0,0x88,0x88,0x88,0x70,0x00,0xf8,0x08,0x08,0x10,0x20,0x20,0x20,0x00,
		0x70,0x88,0x88,0x70,0x88,0x88,0x70,0x00,0x70,0x88,0x88,0x88,0x78,0x08,0x70,0x00,
		0x00,0x00,0x30,0x30,0x00,0x30,0x30,0x00,0x00,0x00,0x30,0x30,0x00,0x30,0x30,0x60,
		0x10,0x20,0x40,0x80,0x40,0x20,0x10,0x00,0x00,0x00,0xf8,0x00,0xf8,0x00,0x00,0x00,
		0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x00,0x70,0x88,0x08,0x10,0x20,0x00,0x20,0x00,
		0x70,0x88,0xb8,0xa8,0xb8,0x80,0x70,0x00,0x70,0x88,0x88,0xf8,0x88,0x88,0x88,0x00,
		0xf0,0x88,0x88,0xf0,0x88,0x88,0xf0,0x00,0x70,0x88,0x80,0x80,0x80,0x88,0x70,0x00,
		0xf0,0x88,0x88,0x88,0x88,0x88,0xf0,0x00,0xf8,0x80,0x80,0xf0,0x80,0x80,0xf8,0x00,
		0xf8,0x80,0x80,0xf0,0x80,0x80,0x80,0x00,0x70,0x88,0x80,0x98,0x88,0x88,0x70,0x00,
		0x88,0x88,0x88,0xf8,0x88,0x88,0x88,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,
		0x08,0x08,0x08,0x08,0x88,0x88,0x70,0x00,0x88,0x90,0xa0,0xc0,0xa0,0x90,0x88,0x00,
		0x80,0x80,0x80,0x80,0x80,0x80,0xf8,0x00,0x88,0xd8,0xa8,0x88,0x88,0x88,0x88,0x00,
		0x88,0xc8,0xa8,0x98,0x88,0x88,0x88,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00,
		0xf0,0x88,0x88,0xf0,0x80,0x80,0x80,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x08,
		0xf0,0x88,0x88,0xf0,0x88,0x88,0x88,0x00,0x70,0x88,0x80,0x70,0x08,0x88,0x70,0x00,
		0xf8,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00,
		0x88,0x88,0x88,0x88,0x88,0x50,0x20,0x00,0x88,0x88,0x88,0x88,0xa8,0xd8,0x88,0x00,
		0x88,0x50,0x20,0x20,0x20,0x50,0x88,0x00,0x88,0x88,0x88,0x50,0x20,0x20,0x20,0x00,
		0xf8,0x08,0x10,0x20,0x40,0x80,0xf8,0x00,0x30,0x20,0x20,0x20,0x20,0x20,0x30,0x00,
		0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x30,0x10,0x10,0x10,0x10,0x10,0x30,0x00,
		0x20,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfc,
		0x40,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x08,0x78,0x88,0x78,0x00,
		0x80,0x80,0xf0,0x88,0x88,0x88,0xf0,0x00,0x00,0x00,0x70,0x88,0x80,0x80,0x78,0x00,
		0x08,0x08,0x78,0x88,0x88,0x88,0x78,0x00,0x00,0x00,0x70,0x88,0xf8,0x80,0x78,0x00,
		0x18,0x20,0x70,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x78,0x88,0x88,0x78,0x08,0x70,
		0x80,0x80,0xf0,0x88,0x88,0x88,0x88,0x00,0x20,0x00,0x20,0x20,0x20,0x20,0x20,0x00,
		0x20,0x00,0x20,0x20,0x20,0x20,0x20,0xc0,0x80,0x80,0x90,0xa0,0xe0,0x90,0x88,0x00,
		0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xf0,0xa8,0xa8,0xa8,0xa8,0x00,
		0x00,0x00,0xb0,0xc8,0x88,0x88,0x88,0x00,0x00,0x00,0x70,0x88,0x88,0x88,0x70,0x00,
		0x00,0x00,0xf0,0x88,0x88,0xf0,0x80,0x80,0x00,0x00,0x78,0x88,0x88,0x78,0x08,0x08,
		0x00,0x00,0xb0,0xc8,0x80,0x80,0x80,0x00,0x00,0x00,0x78,0x80,0x70,0x08,0xf0,0x00,
		0x20,0x20,0x70,0x20,0x20,0x20,0x18,0x00,0x00,0x00,0x88,0x88,0x88,0x98,0x68,0x00,
		0x00,0x00,0x88,0x88,0x88,0x50,0x20,0x00,0x00,0x00,0xa8,0xa8,0xa8,0xa8,0x50,0x00,
		0x00,0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00,0x00,0x88,0x88,0x88,0x78,0x08,0x70,
		0x00,0x00,0xf8,0x10,0x20,0x40,0xf8,0x00,0x08,0x10,0x10,0x20,0x10,0x10,0x08,0x00,
		0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x40,0x20,0x20,0x10,0x20,0x20,0x40,0x00,
		0x00,0x68,0xb0,0x00,0x00,0x00,0x00,0x00,0x20,0x50,0x20,0x50,0xa8,0x50,0x00,0x00,

		/* non-spacing middle dot */
		0x00,
		0x00,
		0x00,
		0x20,
		0x00,
		0x00,
		0x00,
		0x00,
		/* non-spacing overstrike */
		0x00,
		0x00,
		0x00,
		0x00,
		0xfc,
		0x00,
		0x00,
		0x00,
		/* implies */
		0x00,
		0x00,
		0x70,
		0x08,
		0x08,
		0x08,
		0x70,
		0x00,
		/* or */
		0x88,
		0x88,
		0x88,
		0x50,
		0x50,
		0x50,
		0x20,
		0x00,
		/* and */
		0x20,
		0x50,
		0x50,
		0x50,
		0x88,
		0x88,
		0x88,
		0x00,
		/* up arrow */
		0x20,
		0x70,
		0xa8,
		0x20,
		0x20,
		0x20,
		0x20,
		0x00,
		/* right arrow */
		0x00,
		0x20,
		0x10,
		0xf8,
		0x10,
		0x20,
		0x00,
		0x00,
		/* multiply */
		0x00,
		0x88,
		0x50,
		0x20,
		0x50,
		0x88,
		0x00,
		0x00,
	};

	/* set up our font */
	dst = memregion("gfx1")->base();
	memcpy(dst, fontdata6x8, pdp1_fontdata_size);

	machine().add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&pdp1_state::pdp1_machine_stop,this));

	m_dpy_timer = timer_alloc(FUNC(pdp1_state::dpy_callback), this);
}


/*
    perforated tape handling
*/

DEFINE_DEVICE_TYPE(PDP1_READTAPE, pdp1_readtape_image_device, "pdp1_readtape_image", "PDP-1 Tape Reader")

pdp1_readtape_image_device::pdp1_readtape_image_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: paper_tape_reader_device(mconfig, PDP1_READTAPE, tag, owner, clock)
	, m_maincpu(*this, "^maincpu")
	, m_st_ptr(*this)
	, m_timer(nullptr)
{
}

void pdp1_readtape_image_device::device_start()
{
	m_timer = timer_alloc(FUNC(pdp1_readtape_image_device::reader_callback), this);
	m_timer->adjust(attotime::zero, 0, attotime::from_hz(2500));
	m_timer->enable(0);
}


DEFINE_DEVICE_TYPE(PDP1_PUNCHTAPE, pdp1_punchtape_image_device, "pdp1_punchtape_image_device", "PDP-1 Tape Puncher")

pdp1_punchtape_image_device::pdp1_punchtape_image_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: paper_tape_punch_device(mconfig, PDP1_PUNCHTAPE, tag, owner, clock)
	, m_maincpu(*this, "^maincpu")
	, m_st_ptp(*this)
	, m_timer(nullptr)
{
}

void pdp1_punchtape_image_device::device_start()
{
	m_timer = timer_alloc(FUNC(pdp1_punchtape_image_device::puncher_callback), this);
}


DEFINE_DEVICE_TYPE(PDP1_TYPEWRITER, pdp1_typewriter_device, "pdp1_typewriter_image", "PDP-1 Typewriter")

pdp1_typewriter_device::pdp1_typewriter_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, PDP1_TYPEWRITER, tag, owner, clock)
	, device_image_interface(mconfig, *this)
	, m_maincpu(*this, "^maincpu")
	, m_driver_state(*this, DEVICE_SELF_OWNER)
	, m_twr(*this, "^TWR.%u", 0)
	, m_st_tyo(*this)
	, m_st_tyi(*this)
	, m_tyo_timer(nullptr)
{
}

void pdp1_typewriter_device::device_start()
{
	m_tyo_timer = timer_alloc(FUNC(pdp1_typewriter_device::tyo_callback), this);

	m_color = m_pos = m_case_shift = 0;
}


DEFINE_DEVICE_TYPE(PDP1_CYLINDER, pdp1_cylinder_image_device, "pdp1_cylinder_image", "PDP-1 Cylinder")

pdp1_cylinder_image_device::pdp1_cylinder_image_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, PDP1_CYLINDER, tag, owner, clock)
	, device_image_interface(mconfig, *this)
	, m_maincpu(*this, "^maincpu")
{
}

void pdp1_cylinder_image_device::device_start()
{
}

/*
    Open a perforated tape image
*/
std::pair<std::error_condition, std::string> pdp1_readtape_image_device::call_load()
{
	// start motor
	m_motor_on = 1;

		/* restart reader IO when necessary */
		/* note that this function may be called before pdp1_init_machine, therefore
		before m_timer is allocated.  It does not matter, as the clutch is never
		down at power-up, but we must not call timer_enable with a nullptr parameter! */

	if (m_timer)
	{
		if (m_motor_on && m_rcl)
		{
			m_timer->enable(1);
		}
		else
		{
			m_timer->enable(0);
		}
	}

	return std::make_pair(std::error_condition(), std::string());
}

void pdp1_readtape_image_device::call_unload()
{
	// stop motor
	m_motor_on = 0;

	if (m_timer)
		m_timer->enable(0);
}

/*
    Read a byte from perforated tape
*/
int pdp1_readtape_image_device::tape_read(uint8_t *reply)
{
	if (is_loaded() && (fread(reply, 1) == 1))
		return 0;   /* unit OK */
	else
		return 1;   /* unit not ready */
}


/*
    common code for tape read commands (RPA, RPB, and read-in mode)
*/
void pdp1_readtape_image_device::begin_tape_read(int binary, int nac)
{
	m_rb = 0;
	m_rcl = 1;
	m_rc = (binary) ? 1 : 3;
	m_rby = (binary) ? 1 : 0;
	m_rcp = nac;

	if (m_timer->enable(0))
		LOGMASKED(LOG_IOT_OVERLAP, "Error: overlapped perforated tape reads (Read-in mode, RPA/RPB instruction)\n");

	/* set up delay if tape is advancing */
	if (m_motor_on && m_rcl)
	{
		/* delay is approximately 1/400s */
		m_timer->enable(1);
	}
	else
	{
		m_timer->enable(0);
	}
}

/*
    timer callback to simulate reader IO
*/
TIMER_CALLBACK_MEMBER(pdp1_readtape_image_device::reader_callback)
{
	if (m_rc)
	{
		uint8_t data;
		int not_ready = tape_read(&data);
		if (not_ready)
		{
			m_motor_on = 0; // let us stop the motor
		}
		else
		{
			if ((!m_rby) || (data & 0200))
			{
				m_rb |= (m_rby) ? (data & 077) : data;

				if (m_rc != 3)
				{
					m_rb <<= 6;
				}

				m_rc = (m_rc+1) & 3;

				if (m_rc == 0)
				{   // IO complete
					m_rcl = 0;
					if (m_rcp)
					{
						m_maincpu->set_state_int(PDP1_IO, m_rb);  // transfer reader buffer to IO
						m_maincpu->set_state_int(PDP1_IOS,1);
					}
					else
						m_st_ptr(1);
				}
			}
		}
	}

	if (m_motor_on && m_rcl)
	{
		m_timer->enable(1);
	}
	else
	{
		m_timer->enable(0);
	}
}

/*
    perforated tape reader iot callbacks
*/

/*
    rpa iot callback

    op2: iot command operation (equivalent to mb & 077)
    nac: nead a completion pulse
    mb: contents of the MB register
    io: pointer on the IO register
    ac: contents of the AC register
*/
/*
 * Read Perforated Tape, Alphanumeric
 * rpa Address 0001
 *
 * This instruction reads one line of tape (all eight Channels) and transfers the resulting 8-bit code to
 * the Reader Buffer. If bits 5 and 6 of the rpa function are both zero (720001), the contents of the
 * Reader Buffer must be transferred to the IO Register by executing the rrb instruction. When the Reader
 * Buffer has information ready to be transferred to the IO Register, Status Register Bit 1 is set to
 * one. If bits 5 and 6 are different (730001 or 724001) the 8-bit code read from tape is automatically
 * transferred to the IO Register via the Reader Buffer and appears as follows:
 *
 * IO Bits        10 11 12 13 14 15 16 17
 * TAPE CHANNELS  8  7  6  5  4  3  2  1
 */
void pdp1_readtape_image_device::iot_rpa(int op2, int nac, int mb, int &io, int ac)
{
	LOGMASKED(LOG_IOT_EXTRA, "Warning, RPA instruction not fully emulated: mb=0%06o, (%s)\n", (unsigned) mb, machine().describe_context());

	begin_tape_read(0, nac);
}

/*
    rpb iot callback
*/
/*
 * Read Perforated Tape, Binary rpb
 * Address 0002
 *
 * The instruction reads three lines of tape (six Channels per line) and assembles
 * the resulting 18-bit word in the Reader Buffer.  For a line to be recognized by
 * this instruction Channel 8 must be punched (lines with Channel 8 not punched will
 * be skipped over).  Channel 7 is ignored.  The instruction sub 5137, for example,
 * appears on tape and is assembled by rpb as follows:
 *
 * Channel 8 7 6 5 4 | 3 2 1
 * Line 1  X   X     |   X
 * Line 2  X   X   X |     X
 * Line 3  X     X X | X X X
 * Reader Buffer 100 010 101 001 011 111
 *
 * (Vertical dashed line indicates sprocket holes and the symbols "X" indicate holes
 * punched in tape).
 *
 * If bits 5 and 6 of the rpb instruction are both zero (720002), the contents of
 * the Reader Buffer must be transferred to the IO Register by executing
 * a rrb instruction.  When the Reader Buffer has information ready to be transferred
 * to the IO Register, Status Register Bit 1 is set to one.  If bits 5 and 6 are
 * different (730002 or 724002) the 18-bit word read from tape is automatically
 * transferred to the IO Register via the Reader Buffer.
 *
 * The rpb command pulse is also asserted for each word transfer in read-in mode.
 */
void pdp1_readtape_image_device::iot_rpb(int op2, int nac, int mb, int &io, int ac)
{
	LOGMASKED(LOG_IOT_EXTRA, "Warning, RPB instruction not fully emulated: mb=0%06o, (%s)\n", (unsigned) mb, machine().describe_context());

	begin_tape_read(1, nac);
}

/*
    rrb iot callback
*/
void pdp1_readtape_image_device::iot_rrb(int op2, int nac, int mb, int &io, int ac)
{
	LOGMASKED(LOG_IOT_EXTRA, "RRB instruction: mb=0%06o, (%s)\n", (unsigned) mb, machine().describe_context());

	io = m_rb;
	m_st_ptr(0);
}


std::pair<std::error_condition, std::string> pdp1_punchtape_image_device::call_load()
{
	return std::make_pair(std::error_condition(), std::string());
}

void pdp1_punchtape_image_device::call_unload()
{
}

/*
    Write a byte to perforated tape
*/
void pdp1_punchtape_image_device::tape_write(uint8_t data)
{
	if (is_loaded())
		fwrite(&data, 1);
}

/*
    timer callback to generate punch completion pulse
*/
TIMER_CALLBACK_MEMBER(pdp1_punchtape_image_device::puncher_callback)
{
	int nac = param;
	m_st_ptp(1);
	if (nac)
	{
		m_maincpu->set_state_int(PDP1_IOS,1);
	}
}

/*
    perforated tape punch iot callbacks
*/

/*
    ppa iot callback
*/
/*
 * Punch Perforated Tape, Alphanumeric
 * ppa Address 0005
 *
 * For each In-Out Transfer instruction one line of tape is punched. In-Out Register
 * Bit 17 conditions Hole 1. Bit 16 conditions Hole 2, etc. Bit 10 conditions Hole 8
 */
void pdp1_punchtape_image_device::iot_ppa(int op2, int nac, int mb, int &io, int ac)
{
	LOGMASKED(LOG_IOT_EXTRA, "PPA instruction: mb=0%06o, (%s)\n", (unsigned) mb, machine().describe_context());

	tape_write(io & 0377);
	m_st_ptp(0);

	if (m_timer->enable(0))
		LOGMASKED(LOG_IOT_OVERLAP, "Error: overlapped PPA/PPB instructions: mb=0%06o, (%s)\n", (unsigned) mb, machine().describe_context());

	// delay is approximately 1/63.3 second
	m_timer->adjust(attotime::from_usec(15800), nac);
}

/*
    ppb iot callback
*/
/*
 * Punch Perforated Tape, Binary
 * ppb Addres 0006
 *
 * For each In-Out Transfer instruction one line of tape is punched. In-Out Register
 * Bit 5 conditions Hole 1. Bit 4 conditions Hole 2, etc. Bit 0 conditions Hole 6.
 * Hole 7 is left blank. Hole 8 is always punched in this mode.
 */
void pdp1_punchtape_image_device::iot_ppb(int op2, int nac, int mb, int &io, int ac)
{
	LOGMASKED(LOG_IOT_EXTRA, "PPB instruction: mb=0%06o, (%s)\n", (unsigned) mb, machine().describe_context());

	tape_write((io >> 12) | 0200);
	m_st_ptp(0);

	if (m_timer->enable(0))
		LOGMASKED(LOG_IOT_OVERLAP, "Error: overlapped PPA/PPB instructions: mb=0%06o, (%s)\n", (unsigned) mb, machine().describe_context());

	/* delay is approximately 1/63.3 second */
	m_timer->adjust(attotime::from_usec(15800), nac);
}


/*
    Typewriter handling

    The alphanumeric on-line typewriter is a standard device on pdp-1: it
    can both handle keyboard input and print output text.
*/

/*
    Open a file for typewriter output
*/
std::pair<std::error_condition, std::string> pdp1_typewriter_device::call_load()
{
	m_st_tyo(1);

	return std::make_pair(std::error_condition(), std::string());
}

void pdp1_typewriter_device::call_unload()
{
}

/*
    Write a character to typewriter
*/
void pdp1_typewriter_device::typewriter_out(uint8_t data)
{
	LOGMASKED(LOG_IOT_EXTRA, "typewriter output %o\n", data);

	drawchar(data);
	if (is_loaded())
#if 1
		fwrite(& data, 1);
#else
	{
		static const char ascii_table[2][64] =
		{   /* n-s = non-spacing */
			{   /* lower case */
				' ',                '1',                '2',                '3',
				'4',                '5',                '6',                '7',
				'8',                '9',                '*',                '*',
				'*',                '*',                '*',                '*',
				'0',                '/',                's',                't',
				'u',                'v',                'w',                'x',
				'y',                'z',                '*',                ',',
				'*',/*black*/       '*',/*red*/         '*',/*Tab*/         '*',
				'\200',/*n-s middle dot*/'j',           'k',                'l',
				'm',                'n',                'o',                'p',
				'q',                'r',                '*',                '*',
				'-',                ')',                '\201',/*n-s overstrike*/'(',
				'*',                'a',                'b',                'c',
				'd',                'e',                'f',                'g',
				'h',                'i',                '*',/*Lower Case*/  '.',
				'*',/*Upper Case*/  '*',/*Backspace*/   '*',                '*'/*Carriage Return*/
			},
			{   /* upper case */
				' ',                '"',                '\'',               '~',
				'\202',/*implies*/  '\203',/*or*/       '\204',/*and*/      '<',
				'>',                '\205',/*up arrow*/ '*',                '*',
				'*',                '*',                '*',                '*',
				'\206',/*right arrow*/'?',              'S',                'T',
				'U',                'V',                'W',                'X',
				'Y',                'Z',                '*',                '=',
				'*',/*black*/       '*',/*red*/         '\t',/*Tab*/        '*',
				'_',/*n-s???*/      'J',                'K',                'L',
				'M',                'N',                'O',                'P',
				'Q',                'R',                '*',                '*',
				'+',                ']',                '|',/*n-s???*/      '[',
				'*',                'A',                'B',                'C',
				'D',                'E',                'F',                'G',
				'H',                'I',                '*',/*Lower Case*/  '\207',/*multiply*/
				'*',/*Upper Case*/  '\b',/*Backspace*/  '*',                '*'/*Carriage Return*/
			}
		};

		data &= 0x3f;

		switch (data)
		{
		case 034:
			// Black: ignore
			//color = color_typewriter_black;
			{
				static const char black[5] = { '\033', '[', '3', '0', 'm' };
				fwrite(black, sizeof(black));
			}
			break;

		case 035:
			// Red: ignore
			//color = color_typewriter_red;
			{
				static const char red[5] = { '\033', '[', '3', '1', 'm' };
				fwrite(red, sizeof(red));
			}
			break;

		case 072:
			// Lower case
			m_case_shift = 0;
			break;

		case 074:
			// Upper case
			m_case_shift = 1;
			break;

		case 077:
			// Carriage Return
			{
				static const char line_end[2] = { '\r', '\n' };
				fwrite(line_end, sizeof(line_end));
			}
			break;

		default:
			// Any printable character...

			if ((data != 040) && (data != 056)) // 040 and 056 are non-spacing characters: don't try to print right now
				/* print character (lookup ASCII equivalent in table) */
				fwrite(&ascii_table[m_case_shift][data], 1);

			break;
		}
	}
#endif
}

/*
    timer callback to generate typewriter completion pulse
*/
TIMER_CALLBACK_MEMBER(pdp1_typewriter_device::tyo_callback)
{
	int nac = param;
	m_st_tyo(1);
	if (nac)
	{
		m_maincpu->set_state_int(PDP1_IOS,1);
	}
}

/*
    tyo iot callback
*/
void pdp1_typewriter_device::iot_tyo(int op2, int nac, int mb, int &io, int ac)
{
	LOGMASKED(LOG_IOT_EXTRA, "Warning, TYO instruction not fully emulated: mb=0%06o, (%s)\n", (unsigned) mb, machine().describe_context());

	int ch = io & 077;

	typewriter_out(ch);
	m_st_tyo(0);

	/* compute completion delay (source: maintenance manual 9-12, 9-13 and 9-14) */
	int delay;
	switch (ch)
	{
	case 072:   /* lower-case */
	case 074:   /* upper-case */
	case 034:   /* black */
	case 035:   /* red */
		delay = 175;    /* approximately 175ms (?) */
		break;

	case 077:   /* carriage return */
		delay = 205;    /* approximately 205ms (?) */
		break;

	default:
		delay = 105;    /* approximately 105ms */
		break;
	}

	if (m_tyo_timer->enable(0))
		LOGMASKED(LOG_IOT_OVERLAP, "Error: overlapped TYO instruction: mb=0%06o, (%s)\n", (unsigned) mb, machine().describe_context());

	m_tyo_timer->adjust(attotime::from_msec(delay), nac);
}

/*
    tyi iot callback
*/
/* When a typewriter key is struck, the code for the struck key is placed in the
 * typewriter buffer, Program Flag 1 is set, and the type-in status bit is set to
 * one. A program designed to accept typed-in data would periodically check
 * Program Flag 1, and if found to be set, an In-Out Transfer Instruction with
 * address 4 could be executed for the information to be transferred to the
 * In-Out Register. This In-Out Transfer should not use the optional in-out wait.
 * The information contained in the typewriter buffer is then transferred to the
 * right six bits of the In-Out Register. The tyi instruction automatically
 * clears the In-Out Register before transferring the information and also clears
 * the type-in status bit.
 */
void pdp1_typewriter_device::iot_tyi(int op2, int nac, int mb, int &io, int ac)
{
	LOGMASKED(LOG_IOT_EXTRA, "Warning, TYI instruction not fully emulated: mb=0%06o, (%s)\n", (unsigned) mb, machine().describe_context());

	io = m_tb;
	if (! (m_driver_state->m_io_status & io_st_tyi))
		io |= 0100;
	else
		m_st_tyi(0);
}


/*
 * PRECISION CRT DISPLAY (TYPE 30)
 *
 * This sixteen-inch cathode ray tube display is intended to be used as an on-line output device for the
 * PDP-1. It is useful for high speed presentation of graphs, diagrams, drawings, and alphanumerical
 * information. The unit is solid state, self-powered and completely buffered. It has magnetic focus and
 * deflection.
 *
 * Display characteristics are as follows:
 *
 *     Random point plotting
 *     Accuracy of points +/-3 per cent of raster size
 *     Raster size 9.25 by 9.25 inches
 *     1024 by 1024 addressable locations
 *     Fixed origin at center of CRT
 *     Ones complement binary arithmetic
 *     Plots 20,000 points per second
 *
 * Resolution is such that 512 points along each axis are discernable on the face of the tube.
 *
 * One instruction is added to the PDP-1 with the installation of this display:
 *
 * Display One Point On CRT
 * dpy Address 0007
 *
 * This instruction clears the light pen status bit and displays one point using bits 0 through 9 of the
 * AC to represent the (signed) X coordinate of the point and bits 0 through 9 of the IO as the (signed)
 * Y coordinate.
 *
 * Many variations of the Type 30 Display are available. Some of these include hardware for line and
 * curve generation.
 */


/*
    timer callback to generate crt completion pulse
*/
TIMER_CALLBACK_MEMBER(pdp1_state::dpy_callback)
{
	m_maincpu->set_state_int(PDP1_IOS,1);
}


/*
    dpy iot callback

    Light on one pixel on CRT
*/
void pdp1_state::iot_dpy(int op2, int nac, int mb, int &io, int ac)
{
	int x = ((ac+0400000) & 0777777) >> 8;
	int y = ((io+0400000) & 0777777) >> 8;
	pdp1_plot(x, y);

	/* light pen 32 support */
	m_io_status &= ~io_st_pen;

	if (m_lightpen.down && ((x-m_lightpen.x)*(x-m_lightpen.x)+(y-m_lightpen.y)*(y-m_lightpen.y) < m_lightpen.radius*m_lightpen.radius))
	{
		m_io_status |= io_st_pen;

		m_maincpu->set_state_int(PDP1_PF3, 1);
	}

	if (nac)
	{
		/* note that overlap detection is incomplete: it will only work if both DPY
		instructions require a completion pulse */
		if (m_dpy_timer->enable(0))
			LOGMASKED(LOG_IOT_OVERLAP, "Error: overlapped DPY instruction: mb=0%06o, (%s)\n", (unsigned) mb, machine().describe_context());

		/* 50us delay */
		m_dpy_timer->adjust(attotime::from_usec(50));
	}
}



/*
    MIT parallel drum (variant of type 23)
*/

void pdp1_cylinder_image_device::set_il(int il)
{
	m_il = il;

	attotime il_phase = ((PARALLEL_DRUM_WORD_TIME * il) - m_rotation_timer->elapsed());
	if (il_phase < attotime::zero)
		il_phase = il_phase + PARALLEL_DRUM_ROTATION_TIME;
	m_il_timer->adjust(il_phase, 0, PARALLEL_DRUM_ROTATION_TIME);
}

TIMER_CALLBACK_MEMBER(pdp1_cylinder_image_device::rotation_timer_callback)
{
}

TIMER_CALLBACK_MEMBER(pdp1_cylinder_image_device::il_timer_callback)
{
	if (m_dba)
	{
		/* set break request and status bit 5 */
		/* ... */
	}
}

void pdp1_cylinder_image_device::parallel_drum_init()
{
	m_rotation_timer = timer_alloc(FUNC(pdp1_cylinder_image_device::rotation_timer_callback), this);
	m_rotation_timer->adjust(PARALLEL_DRUM_ROTATION_TIME, 0, PARALLEL_DRUM_ROTATION_TIME);

	m_il_timer = timer_alloc(FUNC(pdp1_cylinder_image_device::il_timer_callback), this);
	set_il(0);
}

/*
    Open a file for drum
*/
std::pair<std::error_condition, std::string> pdp1_cylinder_image_device::call_load()
{
	return std::make_pair(std::error_condition(), std::string());
}

void pdp1_cylinder_image_device::call_unload()
{
}

void pdp1_cylinder_image_device::iot_dia(int op2, int nac, int mb, int &io, int ac)
{
	m_wfb = (io & 0370000) >> 12;
	set_il(io & 0007777);

	m_dba = 0; /* right? */
}

void pdp1_cylinder_image_device::iot_dba(int op2, int nac, int mb, int &io, int ac)
{
	m_wfb = (io & 0370000) >> 12;
	set_il(io & 0007777);

	m_dba = 1;
}

/*
    Read a word from drum
*/
uint32_t pdp1_cylinder_image_device::drum_read(int field, int position)
{
	int offset = (field*4096+position)*3;
	uint8_t buf[3];

	if (is_loaded() && (!fseek(offset, SEEK_SET)) && (fread(buf, 3) == 3))
		return ((buf[0] << 16) | (buf[1] << 8) | buf[2]) & 0777777;

	return 0;
}

/*
    Write a word to drum
*/
void pdp1_cylinder_image_device::drum_write(int field, int position, uint32_t data)
{
	int offset = (field*4096+position)*3;
	uint8_t buf[3];

	if (is_loaded())
	{
		buf[0] = data >> 16;
		buf[1] = data >> 8;
		buf[2] = data;

		fseek(offset, SEEK_SET);
		fwrite(buf, 3);
	}
}

void pdp1_cylinder_image_device::iot_dcc(int op2, int nac, int mb, int &io, int ac)
{
	m_rfb = (io & 0370000) >> 12;
	m_wc = - (io & 0007777);

	m_wcl = ac & 0177777/*0007777???*/;

	m_dba = 0; /* right? */
	/* clear status bit 5... */

	/* do transfer */
	attotime delay = m_il_timer->remaining();
	int dc = m_il;
	do
	{
		if ((m_wfb >= 1) && (m_wfb <= 22))
		{
			drum_write(m_wfb-1, dc, (signed)m_maincpu->space(AS_PROGRAM).read_dword(m_wcl));
		}

		if ((m_rfb >= 1) && (m_rfb <= 22))
		{
			m_maincpu->space(AS_PROGRAM).write_dword(m_wcl, drum_read(m_rfb-1, dc));
		}

		m_wc = (m_wc+1) & 07777;
		m_wcl = (m_wcl+1) & 0177777/*0007777???*/;
		dc = (dc+1) & 07777;
		if (m_wc)
			delay = delay + PARALLEL_DRUM_WORD_TIME;
	} while (m_wc);
	m_maincpu->adjust_icount(-m_maincpu->attotime_to_cycles(delay));
	/* if no error, skip */
	m_maincpu->set_state_int(PDP1_PC, m_maincpu->state_int(PDP1_PC)+1);
}

void pdp1_cylinder_image_device::iot_dra(int op2, int nac, int mb, int &io, int ac)
{
	io = (m_rotation_timer->elapsed() *
		  (ATTOSECONDS_PER_SECOND / (PARALLEL_DRUM_WORD_TIME.as_attoseconds()))).seconds() & 0007777;

	/* set parity error and timing error... */
}



/*
    iot 11 callback

    Read state of Spacewar! controllers

    Not documented, except a few comments in the Spacewar! source code:
        it should leave the control word in the io as follows.
        high order 4 bits, rotate ccw, rotate cw, (both mean hyperspace)
        fire rocket, and fire torpedo. low order 4 bits, same for
        other ship. routine is entered by jsp cwg.
*/
void pdp1_state::iot_011(int op2, int nac, int mb, int &io, int ac)
{
	int key_state = m_spacewar->read();
	int reply;


	reply = 0;

	if (key_state & ROTATE_RIGHT_PLAYER2)
		reply |= 0400000;
	if (key_state & ROTATE_LEFT_PLAYER2)
		reply |= 0200000;
	if (key_state & THRUST_PLAYER2)
		reply |= 0100000;
	if (key_state & FIRE_PLAYER2)
		reply |= 0040000;
	if (key_state & HSPACE_PLAYER2)
		reply |= 0600000;

	if (key_state & ROTATE_RIGHT_PLAYER1)
		reply |= 010;
	if (key_state & ROTATE_LEFT_PLAYER1)
		reply |= 004;
	if (key_state & THRUST_PLAYER1)
		reply |= 002;
	if (key_state & FIRE_PLAYER1)
		reply |= 001;
	if (key_state & HSPACE_PLAYER1)
		reply |= 014;

	io = reply;
}


/*
    cks iot callback

    check IO status
*/
void pdp1_state::iot_cks(int op2, int nac, int mb, int &io, int ac)
{
	LOGMASKED(LOG_IOT_EXTRA, "CKS instruction: mb=0%06o, (%s)\n", (unsigned) mb, machine().describe_context());

	io = m_io_status;
}

template <int Mask>
void pdp1_state::io_status_w(int state)
{
	if (state)
		m_io_status |= Mask;
	else
		m_io_status &= ~Mask;

	if (USE_SBS && Mask == io_st_tyi)
		m_maincpu->set_input_line(0, state ? ASSERT_LINE : CLEAR_LINE); /* PDP1 - interrupt it, baby */
}


/*
    callback which is called when Start Clear is pulsed

    IO devices should reset
*/
void pdp1_state::io_start_clear()
{
	m_tape_reader->m_rcl = m_tape_reader->m_rc = 0;
	if (m_tape_reader->m_timer)
		m_tape_reader->m_timer->enable(0);

	if (m_tape_puncher->m_timer)
		m_tape_puncher->m_timer->enable(0);

	if (m_typewriter->m_tyo_timer)
		m_typewriter->m_tyo_timer->enable(0);

	if (m_dpy_timer)
		m_dpy_timer->enable(0);

	m_io_status = io_st_tyo | io_st_ptp;
}


/*
    typewriter keyboard handler
*/
void pdp1_typewriter_device::pdp1_keyboard()
{
	int i;
	int j;

	int typewriter_keys[4];

	int typewriter_transitions;


	for (i=0; i<4; i++)
	{
		typewriter_keys[i] = m_twr[i]->read();
	}

	for (i=0; i<4; i++)
	{
		typewriter_transitions = typewriter_keys[i] & (~ m_old_typewriter_keys[i]);
		if (typewriter_transitions)
		{
			for (j=0; (((typewriter_transitions >> j) & 1) == 0) /*&& (j<16)*/; j++)
				;
			m_tb = (i << 4) + j;
			m_st_tyi(1);
			m_maincpu->set_state_int(PDP1_PF1, 1);
			drawchar(m_tb);  /* we want to echo input */
			break;
		}
	}

	for (i=0; i<4; i++)
		m_old_typewriter_keys[i] = typewriter_keys[i];
}

void pdp1_state::pdp1_lightpen()
{
	int x_delta, y_delta;
	int current_state = m_io_lightpen->read();

	m_lightpen.active = (m_cfg->read() >> pdp1_config_lightpen_bit) & pdp1_config_lightpen_mask;

	/* update pen down state */
	m_lightpen.down = m_lightpen.active && (current_state & pdp1_lightpen_down);

	/* update size of pen tip hole */
	if ((current_state & ~m_old_lightpen) & pdp1_lightpen_smaller)
	{
		m_lightpen.radius --;
		if (m_lightpen.radius < 0)
			m_lightpen.radius = 0;
	}
	if ((current_state & ~m_old_lightpen) & pdp1_lightpen_larger)
	{
		m_lightpen.radius ++;
		if (m_lightpen.radius > 32)
			m_lightpen.radius = 32;
	}

	m_old_lightpen = current_state;

	/* update pen position */
	x_delta = m_lightx->read();
	y_delta = m_lighty->read();

	if (x_delta >= 0x80)
		x_delta -= 0x100;
	if (y_delta >= 0x80)
		y_delta -= 256;

	m_lightpen.x += x_delta;
	m_lightpen.y += y_delta;

	if (m_lightpen.x < 0)
		m_lightpen.x = 0;
	if (m_lightpen.x > 1023)
		m_lightpen.x = 1023;
	if (m_lightpen.y < 0)
		m_lightpen.y = 0;
	if (m_lightpen.y > 1023)
		m_lightpen.y = 1023;

	pdp1_update_lightpen_state(&m_lightpen);
}

/*
    Not a real interrupt - just handle keyboard input
*/
INTERRUPT_GEN_MEMBER(pdp1_state::pdp1_interrupt)
{
	int control_keys;
	int tw_keys;
	int ta_keys;

	int control_transitions;
	int tw_transitions;
	int ta_transitions;


	m_maincpu->set_state_int(PDP1_SS, m_sense->read());

	/* read new state of control keys */
	control_keys = m_csw->read();

	if (control_keys & pdp1_control)
	{
		/* compute transitions */
		control_transitions = control_keys & (~ m_old_control_keys);

		if (control_transitions & pdp1_extend)
		{
			m_maincpu->set_state_int(PDP1_EXTEND_SW, ! m_maincpu->state_int(PDP1_EXTEND_SW));
		}
		if (control_transitions & pdp1_start_nobrk)
		{
			m_maincpu->pulse_start_clear();    /* pulse Start Clear line */
			m_maincpu->set_state_int(PDP1_EXD, m_maincpu->state_int(PDP1_EXTEND_SW));
			m_maincpu->set_state_int(PDP1_SBM, (uint64_t)0);
			m_maincpu->set_state_int(PDP1_OV, (uint64_t)0);
			m_maincpu->set_state_int(PDP1_PC, m_maincpu->state_int(PDP1_TA));
			m_maincpu->set_state_int(PDP1_RUN, 1);
		}
		if (control_transitions & pdp1_start_brk)
		{
			m_maincpu->pulse_start_clear();    /* pulse Start Clear line */
			m_maincpu->set_state_int(PDP1_EXD, m_maincpu->state_int(PDP1_EXTEND_SW));
			m_maincpu->set_state_int(PDP1_SBM, 1);
			m_maincpu->set_state_int(PDP1_OV, (uint64_t)0);
			m_maincpu->set_state_int(PDP1_PC, m_maincpu->state_int(PDP1_TA));
			m_maincpu->set_state_int(PDP1_RUN, 1);
		}
		if (control_transitions & pdp1_stop)
		{
			m_maincpu->set_state_int(PDP1_RUN, (uint64_t)0);
			m_maincpu->set_state_int(PDP1_RIM, (uint64_t)0);  /* bug : we stop after reading an even-numbered word
			                                (i.e. data), whereas a real pdp-1 stops after reading
			                                an odd-numbered word (i.e. dio instruciton) */
		}
		if (control_transitions & pdp1_continue)
		{
			m_maincpu->set_state_int(PDP1_RUN, 1);
		}
		if (control_transitions & pdp1_examine)
		{
			m_maincpu->pulse_start_clear();    /* pulse Start Clear line */
			m_maincpu->set_state_int(PDP1_PC, m_maincpu->state_int(PDP1_TA));
			m_maincpu->set_state_int(PDP1_MA, m_maincpu->state_int(PDP1_PC));
			m_maincpu->set_state_int(PDP1_IR, pdp1_device::LAC); /* this instruction is actually executed */

			m_maincpu->set_state_int(PDP1_MB, (signed)m_maincpu->space(AS_PROGRAM).read_dword(PDP1_MA));
			m_maincpu->set_state_int(PDP1_AC, m_maincpu->state_int(PDP1_MB));
		}
		if (control_transitions & pdp1_deposit)
		{
			m_maincpu->pulse_start_clear();    /* pulse Start Clear line */
			m_maincpu->set_state_int(PDP1_PC, m_maincpu->state_int(PDP1_TA));
			m_maincpu->set_state_int(PDP1_MA, m_maincpu->state_int(PDP1_PC));
			m_maincpu->set_state_int(PDP1_AC, m_maincpu->state_int(PDP1_TW));
			m_maincpu->set_state_int(PDP1_IR, pdp1_device::DAC); /* this instruction is actually executed */

			m_maincpu->set_state_int(PDP1_MB, m_maincpu->state_int(PDP1_AC));
			m_maincpu->space(AS_PROGRAM).write_dword(m_maincpu->state_int(PDP1_MA), m_maincpu->state_int(PDP1_MB));
		}
		if (control_transitions & pdp1_read_in)
		{   /* set cpu to read instructions from perforated tape */
			m_maincpu->pulse_start_clear();    /* pulse Start Clear line */
			m_maincpu->set_state_int(PDP1_PC, (  m_maincpu->state_int(PDP1_TA) & 0170000)
										|  (m_maincpu->state_int(PDP1_PC) & 0007777));  /* transfer ETA to EPC */
			/*m_maincpu->set_state_int(PDP1_MA, m_maincpu->state_int(PDP1_PC));*/
			m_maincpu->set_state_int(PDP1_EXD, m_maincpu->state_int(PDP1_EXTEND_SW));
			m_maincpu->set_state_int(PDP1_OV, (uint64_t)0);       /* right??? */
			m_maincpu->set_state_int(PDP1_RUN, (uint64_t)0);
			m_maincpu->set_state_int(PDP1_RIM, 1);
		}
		if (control_transitions & pdp1_reader)
		{
		}
		if (control_transitions & pdp1_tape_feed)
		{
		}
		if (control_transitions & pdp1_single_step)
		{
			m_maincpu->set_state_int(PDP1_SNGL_STEP, ! m_maincpu->state_int(PDP1_SNGL_STEP));
		}
		if (control_transitions & pdp1_single_inst)
		{
			m_maincpu->set_state_int(PDP1_SNGL_INST, ! m_maincpu->state_int(PDP1_SNGL_INST));
		}

		/* remember new state of control keys */
		m_old_control_keys = control_keys;


		/* handle test word keys */
		tw_keys = (m_twdmsb->read() << 16) | m_twdlsb->read();

		/* compute transitions */
		tw_transitions = tw_keys & (~ m_old_tw_keys);

		if (tw_transitions)
			m_maincpu->set_state_int(PDP1_TW, m_maincpu->state_int(PDP1_TW) ^ tw_transitions);

		/* remember new state of test word keys */
		m_old_tw_keys = tw_keys;


		/* handle address keys */
		ta_keys = m_tstadd->read();

		/* compute transitions */
		ta_transitions = ta_keys & (~ m_old_ta_keys);

		if (ta_transitions)
			m_maincpu->set_state_int(PDP1_TA, m_maincpu->state_int(PDP1_TA) ^ ta_transitions);

		/* remember new state of test word keys */
		m_old_ta_keys = ta_keys;

	}
	else
	{
		m_old_control_keys = 0;
		m_old_tw_keys = 0;
		m_old_ta_keys = 0;

		m_typewriter->pdp1_keyboard();
	}

	pdp1_lightpen();
}


void pdp1_state::pdp1(machine_config &config)
{
	/* basic machine hardware */
	/* PDP1 CPU @ 200 kHz (no master clock, but the instruction and memory rate is 200 kHz) */
	PDP1(config, m_maincpu, 1000000); /* the CPU core uses microsecond counts */
	m_maincpu->set_reset_param(&pdp1_reset_param);
	m_maincpu->set_addrmap(AS_PROGRAM, &pdp1_state::pdp1_map);
	m_maincpu->set_vblank_int("screen", FUNC(pdp1_state::pdp1_interrupt));   /* dummy interrupt: handles input */
	/* external iot handlers.  00, 50 to 56 and 74 are internal to the cpu core. */
	/* I put a ? when the source is the handbook, since a) I have used the maintenance manual
	as the primary source (as it goes more into details) b) the handbook and the maintenance
	manual occasionally contradict each other. */
	/*  (iot)       rpa         rpb         tyo         tyi         ppa         ppb         dpy */
	/*              spacewar                                                                 */
	/*                          lag                                             glf?/jsp?   gpl?/gpr?/gcf? */
	/*  rrb         rcb?        rcc?        cks         mcs         mes         mel          */
	/*  cad?        rac?        rbc?        pac                     lpr/lfb/lsp swc/sci/sdf?/shr?   scv? */
	/*  (dsc)       (asc)       (isb)       (cac)       (lsm)       (esm)       (cbs)        */
	/*  icv?        dia         dba         dcc         dra                     mri|rlc?    mrf/inr?/ccr? */
	/*  mcb|dur?    mwc|mtf?    mrc|sfc?... msm|cgo?    (eem/lem)   mic         muf          */
	m_maincpu->set_iot_callback<001>(m_tape_reader, FUNC(pdp1_readtape_image_device::iot_rpa));
	m_maincpu->set_iot_callback<002>(m_tape_reader, FUNC(pdp1_readtape_image_device::iot_rpb));
	m_maincpu->set_iot_callback<003>(m_typewriter, FUNC(pdp1_typewriter_device::iot_tyo));
	m_maincpu->set_iot_callback<004>(m_typewriter, FUNC(pdp1_typewriter_device::iot_tyi));
	m_maincpu->set_iot_callback<005>(m_tape_puncher, FUNC(pdp1_punchtape_image_device::iot_ppa));
	m_maincpu->set_iot_callback<006>(m_tape_puncher, FUNC(pdp1_punchtape_image_device::iot_ppb));
	m_maincpu->set_iot_callback<007>(FUNC(pdp1_state::iot_dpy));
	m_maincpu->set_iot_callback<011>(FUNC(pdp1_state::iot_011));
	m_maincpu->set_iot_callback<030>(m_tape_reader, FUNC(pdp1_readtape_image_device::iot_rrb));
	m_maincpu->set_iot_callback<033>(FUNC(pdp1_state::iot_cks));
	/* dia, dba, dcc, dra are documented in MIT PDP-1 COMPUTER MODIFICATION
	BULLETIN no. 2 (drumInstrWriteup.bin/drumInstrWriteup.txt), and are
	similar to IOT documented in Parallel Drum Type 23 Instruction Manual. */
	m_maincpu->set_iot_callback<061>(m_parallel_drum, FUNC(pdp1_cylinder_image_device::iot_dia));
	m_maincpu->set_iot_callback<062>(m_parallel_drum, FUNC(pdp1_cylinder_image_device::iot_dba));
	m_maincpu->set_iot_callback<063>(m_parallel_drum, FUNC(pdp1_cylinder_image_device::iot_dcc));
	m_maincpu->set_iot_callback<064>(m_parallel_drum, FUNC(pdp1_cylinder_image_device::iot_dra));
	m_maincpu->set_io_sc_callback(FUNC(pdp1_state::io_start_clear));

	/* video hardware (includes the control panel and typewriter output) */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(refresh_rate);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(virtual_width, virtual_height);
	screen.set_visarea(0, virtual_width-1, 0, virtual_height-1);
	screen.set_screen_update(FUNC(pdp1_state::screen_update_pdp1));
	screen.screen_vblank().set(FUNC(pdp1_state::screen_vblank_pdp1));
	screen.set_palette(m_palette);

	CRT(config, m_crt, 0);
	m_crt->set_num_levels(pen_crt_num_levels);
	m_crt->set_offsets(crt_window_offset_x, crt_window_offset_y);
	m_crt->set_size(crt_window_width, crt_window_height);

	PDP1_READTAPE(config, m_tape_reader);
	m_tape_reader->st_ptr().set(FUNC(pdp1_state::io_status_w<io_st_ptr>));

	PDP1_PUNCHTAPE(config, m_tape_puncher);
	m_tape_puncher->st_ptp().set(FUNC(pdp1_state::io_status_w<io_st_ptp>));

	PDP1_TYPEWRITER(config, m_typewriter);
	m_typewriter->st_tyo().set(FUNC(pdp1_state::io_status_w<io_st_tyo>));
	m_typewriter->st_tyi().set(FUNC(pdp1_state::io_status_w<io_st_tyi>));

	PDP1_CYLINDER(config, m_parallel_drum);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_pdp1);
	PALETTE(config, m_palette, FUNC(pdp1_state::pdp1_palette), total_colors_needed + std::size(pdp1_pens), total_colors_needed);

	SOFTWARE_LIST(config, "ptp_list").set_original("pdp1_ptp");
}

/*
    pdp1 can address up to 65336 18 bit words when extended (4096 otherwise).
*/
ROM_START(pdp1)
	ROM_REGION(pdp1_fontdata_size, "gfx1", ROMREGION_ERASEFF)
	/* space filled with our font */
ROM_END

/***************************************************************************

  Game driver(s)

***************************************************************************/

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT CLASS       INIT        COMPANY                           FULLNAME  FLAGS
COMP( 1961, pdp1, 0,      0,      pdp1,    pdp1, pdp1_state, empty_init, "Digital Equipment Corporation",  "PDP-1",  MACHINE_NO_SOUND_HW )



pdp11.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

        PDP-11

        Unibus models
        ==================
        PDP-11/20 and PDP-11/15 - The original, non-microprogrammed processor;
                designed by Jim O'Loughlin. Floating point was supported by
                peripheral options using various data formats.
        PDP-11/35 and PDP-11/40 - A microprogrammed successor to the PDP-11/20;
                the design team was led by Jim O'Loughlin.
        PDP-11/45, PDP-11/50, and PDP-11/55 ? A much faster microprogrammed processor
                that could use up to 256 kB of semiconductor memory instead of or in
                addition to core memory. First model to support an optional FP11
                floating-point coprocessor, which established the format used in
                later models.
        PDP-11/70 - The 11/45 architecture expanded to allow 4 MB of physical memory
                segregated onto a private memory bus, 2 kB of cache memory, and much
                faster I/O devices connected via the Massbus.[9]
        PDP-11/05 and PDP-11/10 - A cost-reduced successor to the PDP-11/20.
        PDP-11/34 and PDP-11/04 - Cost-reduced follow-on products to the 11/35
                and 11/05; the PDP-11/34 concept was created by Bob Armstrong.
                The 11/34 supported up to 256 kB of Unibus memory. The PDP-11/34a
                supported a fast floating-point option, and the 11/34c supported a
                cache memory option.
        PDP-11/60 - A PDP-11 with user-writable microcontrol store; this was
                designed by another team led by Jim O'Loughlin.
        PDP-11/44 - Replacement for the 11/45 and 11/70 that supported optional cache
                memory and floating-point processor, and included a sophisticated serial
                console interface and support for 4 MB of physical memory. The design
                team was managed by John Sofio.
        PDP-11/24 - First VLSI PDP-11 for Unibus, using the "Fonz-11" (F11) chip set
                with a Unibus adapter.
        PDP-11/84 - Using the VLSI "Jaws-11" (J11) chip set with a Unibus adapter.
        PDP-11/94 - J11-based, faster than 11/84.

        Q-bus models
        ==============
        PDP-11/03 (also known as the LSI-11/03) - The first LSI PDP-11, this system
                used a chipset from Western Digital and supported 60 kB of memory.
        PDP-11/23 - Second generation of LSI (F-11). Early units supported
                only 248 kB of memory.
        PDP-11/23+/MicroPDP-11/23 - Improved 11/23 with more functions on the
                (larger) processor card.
        MicroPDP-11/73 - The third generation LSI-11, this system used the
                faster "Jaws-11" (J-11) chip set and supported up to 4 MB of memory.
        MicroPDP-11/53 - Slower 11/73 with on-board memory.
        MicroPDP-11/83 - Faster 11/73 with PMI (private memory interconnect).
        MicroPDP-11/93 - Faster 11/83; final DEC Q-Bus PDP-11 model.
        KXJ11 - QBUS card (M7616) with PDP-11 based peripheral processor and
                DMA controller. Based on a J11 CPU equipped with 512 kB of RAM,
                64 kB of ROM, and parallel and serial interfaces.
        Mentec M100 - Mentec redesign of the 11/93, with J-11 chipset at 19.66 MHz,
                four on-board serial ports, 1-4 MB of on-board memory, and optional FPU.
        Mentec M11 - Processor upgrade board; microcode implementation of PDP-11
                instruction set by Mentec, using the TI 8832 ALU and TI 8818
                microsequencer from Texas Instruments.
        Mentec M1 - Processor upgrade board; microcode implementation of
                PDP-11 instruction set by Mentec, using Atmel 0.35 ?m ASIC.[10]
        Quickware QED-993 - High performance PDP-11/93 processor upgrade board.
        DECserver 500 and 550 LAT terminal servers DSRVS-BA using the KDJ11-SB chipset

        All PDP-11's execept the first one (11/15 and 11/20) are microprogrammed.

        23/02/2009 Skeleton driver.

        Memory Map (converted from the annoying octal format from the manuals):
        0x0000 - 0x00ff: irq vectors
        0xe000: ROM
        0xff68: "high speed reader and punch device status and buffer register"
        0xff70 - 0xff7e: "teletype keyboard and punch device status and buffer register"
        PDP-11 internal registers:
        0xff80 - 0xffbf: "reserved for expansion of processor registers"
        0xffc0: R0
        0xffc2: R1
        0xffc4: R2
        0xffc6: R3
        0xffc8: R4
        0xffca: R5
        0xffcc: R6 / SP
        0xffce: R7 / PC
        0xfffe: PSW

        SMS-1000:
        Claims to be 100% compatible with DEC PDP-11. Added as a skeleton.

****************************************************************************/

#include "emu.h"
#include "bus/qbus/qbus.h"
#include "bus/rs232/rs232.h"
#include "cpu/t11/t11.h"
#include "cpu/i86/i186.h"
#include "machine/dl11.h"
#include "rx01.h"


namespace {

class pdp11_state : public driver_device
{
public:
	pdp11_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_dl11(*this, "dl11")
		, m_rs232(*this, "rs232")
		, m_qbus(*this, "qbus")
	{ }

	void pdp11ub2(machine_config &config);
	void pdp11(machine_config &config);
	void pdp11qb(machine_config &config);
	void sms1000(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<t11_device> m_maincpu;
	required_device<dl11_device> m_dl11;
	required_device<rs232_port_device> m_rs232;
	required_device<qbus_device> m_qbus;
	DECLARE_MACHINE_RESET(pdp11ub2);
	DECLARE_MACHINE_RESET(pdp11qb);
	void load9312prom(uint8_t *desc, uint8_t *src, int size);
	void pdp11_mem(address_map &map) ATTR_COLD;
	void pdp11qb_mem(address_map &map) ATTR_COLD;
	void sms1000_mem_188(address_map &map) ATTR_COLD;
};

void pdp11_state::pdp11_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xdfff).ram();  // RAM
	map(0xea00, 0xfeff).rom();
	map(0xff70, 0xff77).rw(m_dl11, FUNC(dl11_device::read), FUNC(dl11_device::write));

	map(0xfe78, 0xfe7b).w("rx01", FUNC(rx01_device::write));
}

void pdp11_state::pdp11qb_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xe9ff).ram();  // RAM
	map(0xea00, 0xefff).rom();
	map(0xf000, 0xffff).ram();
}

void pdp11_state::sms1000_mem_188(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x027ff).ram();
	map(0x03000, 0x037ff).rom().region("prom", 0);
	map(0x40000, 0x47fff).rom().region("subcpu", 0);
	map(0x50000, 0x5ffff).nopr();
	map(0xf8000, 0xfffff).rom().region("subcpu", 0);
}

#define M9312_PORT_CONFSETTING \
PORT_CONFSETTING ( 0x00, "'DL' BOOT prom for RL11 controller") \
PORT_CONFSETTING ( 0x01, "'DM' BOOT prom for RK06/07 controller") \
PORT_CONFSETTING ( 0x02, "'DX' BOOT prom for RX01 compatible controller") \
PORT_CONFSETTING ( 0x03, "'DP/DB' BOOT prom for RP02/03,RP04/5/6 RM02/3 controller") \
PORT_CONFSETTING ( 0x04, "'DK/DT' BOOT prom for RK03/05,TU55/56 controllers") \
PORT_CONFSETTING ( 0x05, "'MM' BOOT prom for TU16/E16 TM02/3 controllers") \
PORT_CONFSETTING ( 0x06, "'MT' BOOT prom for TU10/TS03 controller") \
PORT_CONFSETTING ( 0x07, "'DS' BOOT prom for RS03/RS04 controller") \
PORT_CONFSETTING ( 0x08, "'PR/TT' BOOT prom for PC05,LO SPD RDR controllers") \
PORT_CONFSETTING ( 0x09, "'CT' BOOT prom for TA11/TU60 controller") \
PORT_CONFSETTING ( 0x0a, "'RS' BOOT prom for RS11, RS64 controller") \
PORT_CONFSETTING ( 0x0b, "'CR' BOOT prom for CR11 card reader") \
PORT_CONFSETTING ( 0x0c, "'MS' BOOT prom for TS11/TS04/TU80 compatible controller") \
PORT_CONFSETTING ( 0x0d, "'DD' BOOT prom for TU58 DECtapeII serial tape controller") \
PORT_CONFSETTING ( 0x0e, "'DU' BOOT prom for MSCP compatible controller") \
PORT_CONFSETTING ( 0x0f, "'XX' Unknown 1/3") \
PORT_CONFSETTING ( 0x10, "'XX' Unknown 2/3") \
PORT_CONFSETTING ( 0x11, "'XX' Unknown 3/3") \
PORT_CONFSETTING ( 0x12, "'DY' BOOT prom for RX02 compatible controller") \
PORT_CONFSETTING ( 0x13, "'XM' DECNET 1/3 (DECnet DDCMP DMC11/DMR11)") \
PORT_CONFSETTING ( 0x14, "'XM' DECNET 2/3 (DECnet DDCMP DMC11/DMR11)") \
PORT_CONFSETTING ( 0x15, "'XM' DECNET 3/3 (DECnet DDCMP DMC11/DMR11)") \
PORT_CONFSETTING ( 0x16, "'XU' DECNET 1/3 (DECnet DDCMP DU11)") \
PORT_CONFSETTING ( 0x17, "'XU' DECNET 2/3 (DECnet DDCMP DU11)") \
PORT_CONFSETTING ( 0x18, "'XU' DECNET 3/3 (DECnet DDCMP DU11)") \
PORT_CONFSETTING ( 0x19, "'XW' DECNET 1/3 (DECnet DDCMP DUP11)") \
PORT_CONFSETTING ( 0x1a, "'XW' DECNET 2/3 (DECnet DDCMP DUP11)") \
PORT_CONFSETTING ( 0x1b, "'XW' DECNET 3/3 (DECnet DDCMP DUP11)") \
PORT_CONFSETTING ( 0x1c, "'XL' DECNET 1/3 (DECnet DDCMP DL11-E)") \
PORT_CONFSETTING ( 0x1d, "'XL' DECNET 2/3 (DECnet DDCMP DL11-E)") \
PORT_CONFSETTING ( 0x1e, "'XL' DECNET 3/3 (DECnet DDCMP DL11-E)") \
PORT_CONFSETTING ( 0x1f, "'XE' DEUNA DECnet Ethernet") \
PORT_CONFSETTING ( 0x20, "'MU' TMSCP tapes, including TK50, TU81")
/* Input ports */
static INPUT_PORTS_START( pdp11 )
	PORT_START("S1")
	PORT_DIPNAME( 0x01, 0x01, "S1-1" )
	PORT_DIPSETTING(    0x00, "Direct boot" )
	PORT_DIPSETTING(    0x01, "Console mode" )
	PORT_DIPNAME( 0x02, 0x02, "S1-2 Boot")
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_START("S1_2")
	PORT_DIPNAME( 0x80, 0x00, "S1-3" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, "S1-4" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, "S1-5" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, "S1-6" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "S1-7" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, "S1-8" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, "S1-9" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x01, 0x00, "S1-10" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_START( "CONSPROM" )
	PORT_CONFNAME ( 0x01, 0, "Console PROM" )
	PORT_CONFSETTING ( 0x00, "11/04/05/34/35/40/45/50/55" )
	PORT_CONFSETTING ( 0x01, "11/60-70" )
	PORT_START( "DEVPROM1" )
	PORT_CONFNAME ( 0x2f, 0x02, "Device 1 PROM" )
	M9312_PORT_CONFSETTING
	PORT_START( "DEVPROM2" )
	PORT_CONFNAME ( 0x2f, 0x00, "Device 2 PROM" )
	M9312_PORT_CONFSETTING
	PORT_START( "DEVPROM3" )
	PORT_CONFNAME ( 0x2f, 0x0d, "Device 3 PROM" )
	M9312_PORT_CONFSETTING
	PORT_START( "DEVPROM4" )
	PORT_CONFNAME ( 0x2f, 0x04, "Device 4 PROM" )
	M9312_PORT_CONFSETTING
INPUT_PORTS_END

void pdp11_state::machine_start()
{
}

void pdp11_state::machine_reset()
{
	// Load M9301-YA
	uint8_t* user1 = memregion("user1")->base();
	uint8_t* maincpu = memregion("maincpu")->base();
	int i;

	for(i=0;i<0x100;i++) {
		uint8_t nib1 = user1[i+0x000] ^ 0x00;
		uint8_t nib2 = user1[i+0x200] ^ 0x01;
		uint8_t nib3 = user1[i+0x400] ^ 0x0f;
		uint8_t nib4 = user1[i+0x600] ^ 0x0e;

		maincpu[0xea00 + i*2 + 1] = (nib1 << 4) + nib2;
		maincpu[0xea00 + i*2 + 0] = (nib3 << 4) + nib4;
	}
	for(i=0x100;i<0x200;i++) {
		uint8_t nib1 = user1[i+0x000] ^ 0x00;
		uint8_t nib2 = user1[i+0x200] ^ 0x01;
		uint8_t nib3 = user1[i+0x400] ^ 0x0f;
		uint8_t nib4 = user1[i+0x600] ^ 0x0e;

		maincpu[0xf600 + (i-0x100)*2 + 1] = (nib1 << 4) + nib2;
		maincpu[0xf600 + (i-0x100)*2 + 0] = (nib3 << 4) + nib4;
	}
}

void pdp11_state::load9312prom(uint8_t *desc, uint8_t *src, int size)
{
	//   3   2   1   8
	//   7   6   5   4
	// ~11 ~10   9   0
	//  15  14  13 ~12
	for(int i=0;i<size;i++) {
		uint8_t nib1 = src[i*4+0];
		uint8_t nib2 = src[i*4+1];
		uint8_t nib3 = src[i*4+2];
		uint8_t nib4 = src[i*4+3];

		desc[i*2 + 0] = (nib2 << 4) + ((nib1 & 0x0e) | (nib3 & 1));
		desc[i*2 + 1] = ((nib4 ^ 0x01)<<4) + ((nib1 & 0x01) | ((nib3 ^ 0x0c) & 0x0e));
	}
}

MACHINE_RESET_MEMBER(pdp11_state,pdp11ub2)
{
	// Load M9312
	uint8_t* user1 = memregion("consproms")->base() + ioport("CONSPROM")->read() * 0x0400;
	uint8_t* maincpu = memregion("maincpu")->base();

	//0165000
	load9312prom(maincpu + 0165000,user1,0x100);

	uint8_t s1 = ioport("S1")->read();

	if (s1 & 0x02) { // if boot enabled
		uint16_t addr = 0173000;
		if (s1 & 1) {
			addr = 0165000;
		}
		addr += ioport("S1_2")->read() * 2;
		m_maincpu->set_state_int(T11_PC, addr);
	}

	//0173000
	load9312prom(maincpu + 0173000,memregion("devproms")->base() + ioport("DEVPROM1")->read() * 0x0200,0x080);
	//0173200
	load9312prom(maincpu + 0173200,memregion("devproms")->base() + ioport("DEVPROM2")->read() * 0x0200,0x080);
	//0173400
	load9312prom(maincpu + 0173400,memregion("devproms")->base() + ioport("DEVPROM3")->read() * 0x0200,0x080);
	//0173600
	load9312prom(maincpu + 0173600,memregion("devproms")->base() + ioport("DEVPROM4")->read() * 0x0200,0x080);

}

MACHINE_RESET_MEMBER(pdp11_state,pdp11qb)
{
	m_maincpu->set_state_int(T11_PC, 0xea00);
}


void pdp11_state::pdp11(machine_config &config)
{
	/* basic machine hardware */
	T11(config, m_maincpu, 4'000'000); // Need proper CPU here
	m_maincpu->set_initial_mode(6 << 13);
	m_maincpu->set_addrmap(AS_PROGRAM, &pdp11_state::pdp11_mem);

	DL11(config, m_dl11, XTAL(4'608'000));
	m_dl11->set_rxc(9600);
	m_dl11->set_txc(9600);
	m_dl11->set_rxvec(060);
	m_dl11->set_txvec(064);
	m_dl11->txd_wr_callback().set(m_rs232, FUNC(rs232_port_device::write_txd));
// future
//  m_dl11->txrdy_wr_callback().set_inputline(m_maincpu, t11_device::CP0_LINE);
//  m_dl11->rxrdy_wr_callback().set_inputline(m_maincpu, t11_device::CP0_LINE);

	RS232_PORT(config, m_rs232, default_rs232_devices, "terminal");
	m_rs232->rxd_handler().set(m_dl11, FUNC(dl11_device::rx_w));

	RX01(config, "rx01", 0);
	QBUS(config, m_qbus, 0);
	m_qbus->set_space(m_maincpu, AS_PROGRAM);
	m_qbus->birq4().set_inputline(m_maincpu, t11_device::CP0_LINE);
	QBUS_SLOT(config, "qbus" ":1", qbus_cards, "pc11");
	QBUS_SLOT(config, "qbus" ":2", qbus_cards, nullptr);
	QBUS_SLOT(config, "qbus" ":3", qbus_cards, nullptr);
	QBUS_SLOT(config, "qbus" ":4", qbus_cards, nullptr);
}

void pdp11_state::pdp11ub2(machine_config &config)
{
	pdp11(config);
	MCFG_MACHINE_RESET_OVERRIDE(pdp11_state,pdp11ub2)
}

void pdp11_state::pdp11qb(machine_config &config)
{
	pdp11(config); // FIXME: MXV11-B requires a F-11 or J-11 CPU
	MCFG_MACHINE_RESET_OVERRIDE(pdp11_state,pdp11qb)

	m_maincpu->set_initial_mode(0 << 13);
	m_maincpu->set_addrmap(AS_PROGRAM, &pdp11_state::pdp11qb_mem);
}

void pdp11_state::sms1000(machine_config &config)
{
	pdp11qb(config);

	i80188_cpu_device &subcpu(I80188(config, "subcpu", 12.288_MHz_XTAL));
	subcpu.set_addrmap(AS_PROGRAM, &pdp11_state::sms1000_mem_188);
}

/* ROM definition */
ROM_START( pdp11ub )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_REGION( 0x1000, "user1", ROMREGION_ERASEFF )
	ROM_LOAD( "23-034a9.bin", 0x0000, 0x0200, CRC(01c5d78d) SHA1(b447c67bfd5134c142240a919f23a949e1953fb2))
	ROM_LOAD( "23-035a9.bin", 0x0200, 0x0200, CRC(c456df6c) SHA1(188c8ece6a2d67911016f55dd22b698a40aff515))
	ROM_LOAD( "23-036a9.bin", 0x0400, 0x0200, CRC(208ff511) SHA1(27198a1110319b70674a72fd03a798dfa2c2109a))
	ROM_LOAD( "23-037a9.bin", 0x0600, 0x0200, CRC(d248b282) SHA1(ea638de6bde8342654d3e62b7810aa041e111913))
ROM_END

ROM_START( pdp11ub2 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_REGION( 0x800, "consproms", ROMREGION_ERASEFF )
	ROM_LOAD( "23-248f1.bin", 0x0000, 0x0400, CRC(ecda1a6d) SHA1(b2bf770dda349fdd469235871564280baf06301d)) // M9312 11/04/05/34/35/40/45/50/55  Console/Diagnostic PROM
	ROM_LOAD( "23-616f1.bin", 0x0400, 0x0400, CRC(a3dfb5aa) SHA1(7f06c624ae3fbb49535258b8722b5a3c548da3ba)) // M9312 11/60-70 Diagnostic/Console ROM
	ROM_REGION( 0x4200, "devproms", ROMREGION_ERASEFF )
	ROM_LOAD( "23-751a9.bin", 0x0000, 0x0200, CRC(15bebc6a) SHA1(a621c5b1cebebbb110ee646a8c36ee4c606e269b)) // M9312 'DL' BOOT prom for RL11 controller
	ROM_LOAD( "23-752a9.bin", 0x0200, 0x0200, CRC(6cf1f859) SHA1(7c876eda2f0d74d6f5d876256c28dbd56c405ca7)) // M9312 'DM' BOOT prom for RK06/07 controller
	ROM_LOAD( "23-753a9.bin", 0x0400, 0x0200, CRC(f4c4b40c) SHA1(a0bdb922c722d439f35ba8149a8f657ffcc8fb54)) // M9312 'DX' BOOT prom for RX01 compatible controller
	ROM_LOAD( "23-755a9.bin", 0x0600, 0x0200, CRC(ed06b35c) SHA1(d972c6a743d73ce9244d2bcfdd40eea2bb22e717)) // M9312 'DP/DB' BOOT prom for RP02/03,RP04/5/6 RM02/3 controller
	ROM_LOAD( "23-756a9.bin", 0x0800, 0x0200, CRC(12271ab2) SHA1(f0ff42a8fd839dd75d6c1a25cc82d0933fd09dbc)) // M9312 'DK/DT' BOOT prom for RK03/05,TU55/56 controllers
	ROM_LOAD( "23-757a9.bin", 0x0a00, 0x0200, CRC(af251aab) SHA1(4d760ec3f6ff5f4e2cafcb44b275183872b69cb6)) // M9312 'MM' BOOT prom for TU16/E16 TM02/3 controllers
	ROM_LOAD( "23-758a9.bin", 0x0c00, 0x0200, CRC(b71e8878) SHA1(f45c47c702c94a70c36732c12173ce60d0be1a11)) // M9312 'MT' BOOT prom for TU10/TS03 controller
	ROM_LOAD( "23-759a9.bin", 0x0e00, 0x0200, CRC(29a93448) SHA1(0b549170c6a3f49c1587adb6cc691786111c0dd3)) // M9312 'DS' BOOT prom for RS03/RS04 controller
	ROM_LOAD( "23-760a9.bin", 0x1000, 0x0200, CRC(ea093648) SHA1(3875a0147c43db1a5a381bbe85937a5628e6220c)) // M9312 'PR/TT' BOOT prom for PC05,LO SPD RDR controllers
	ROM_LOAD( "23-761a9.bin", 0x1200, 0x0200, CRC(4310ebe8) SHA1(a3144f96819ea57acfac5de5e19961294e7d4ad9)) // M9312 'CT' BOOT prom for TA11/TU60 controller
	ROM_LOAD( "23-762a9.bin", 0x1400, 0x0200, NO_DUMP)                                                      // M9312 'RS' BOOT prom for RS11, RS64 controller
	ROM_LOAD( "23-763a9.bin", 0x1600, 0x0200, NO_DUMP)                                                      // M9312 'CR' BOOT prom for CR11 card reader
	ROM_LOAD( "23-764a9.bin", 0x1800, 0x0200, CRC(7c8b7ed4) SHA1(ba0c9f03027eb3dafcc0936e877637d3c9947f94)) // M9312 'MS' BOOT prom for TS11/TS04/TU80 compatible controller
	ROM_LOAD( "23-765a9.bin", 0x1a00, 0x0200, CRC(702dfeb2) SHA1(0d37bdd3846de4b104b8968a0e83ed81abd7f9ae)) // M9312 'DD' BOOT prom for TU58 DECtapeII serial tape controller
	ROM_LOAD( "23-767a9.bin", 0x1c00, 0x0200, CRC(4b94e3fa) SHA1(3cf92c2f64f95e8cc3abb8af2526cc65ce53ca8a)) // M9312 'DU' BOOT prom for MSCP compatible controller (UDA50/RA50/RC25/RAxx)
	ROM_LOAD( "23-786a9.bin", 0x1e00, 0x0200, CRC(a5326664) SHA1(238f97fc5b2b540948ea1e27a4cd1dcf18255b21)) // M9312 'XX' Unknown 1/3
	ROM_LOAD( "23-787a9.bin", 0x2000, 0x0200, CRC(025debf9) SHA1(8ea2faf2e2d78be0ad2f77e61bae0dfb9c3b4b01)) // M9312 'XX' Unknown 2/3
	ROM_LOAD( "23-788a9.bin", 0x2200, 0x0200, CRC(3c7ed364) SHA1(519ffac2e4878490128e754a0473502c767a94e2)) // M9312 'XX' Unknown 3/3
	ROM_LOAD( "23-811a9.bin", 0x2400, 0x0200, CRC(9aa8499a) SHA1(11b040e0908d7492dcc450cbb72d76633dd687ca)) // M9312 'DY' BOOT prom for RX02 compatible controller
	ROM_LOAD( "23-862a9.bin", 0x2600, 0x0200, CRC(38dbd994) SHA1(c5db671e6b70f3b4d345a02b46e0ea7566160d04)) // M9312 'XM' DECNET 1/3 (DECnet DDCMP DMC11/DMR11)
	ROM_LOAD( "23-863a9.bin", 0x2800, 0x0200, CRC(bbef2f41) SHA1(f472b7a8bd4c0a49dc3ec38f886755910f73fe66)) // M9312 'XM' DECNET 2/3 (DECnet DDCMP DMC11/DMR11)
	ROM_LOAD( "23-864a9.bin", 0x2a00, 0x0200, CRC(85cc17dc) SHA1(371dbd3c672fe4b1819762c3082c4217a7597547)) // M9312 'XM' DECNET 3/3 (DECnet DDCMP DMC11/DMR11)
	ROM_LOAD( "23-865a9.bin", 0x2c00, 0x0200, NO_DUMP)                                                      // M9312 'XU' DECNET 1/3 (DECnet DDCMP DU11)
	ROM_LOAD( "23-866a9.bin", 0x2e00, 0x0200, NO_DUMP)                                                      // M9312 'XU' DECNET 2/3 (DECnet DDCMP DU11)
	ROM_LOAD( "23-867a9.bin", 0x3000, 0x0200, NO_DUMP)                                                      // M9312 'XU' DECNET 3/3 (DECnet DDCMP DU11)
	ROM_LOAD( "23-868a9.bin", 0x3200, 0x0200, NO_DUMP)                                                      // M9312 'XW' DECNET 1/3 (DECnet DDCMP DUP11)
	ROM_LOAD( "23-869a9.bin", 0x3400, 0x0200, NO_DUMP)                                                      // M9312 'XW' DECNET 2/3 (DECnet DDCMP DUP11)
	ROM_LOAD( "23-870a9.bin", 0x3600, 0x0200, NO_DUMP)                                                      // M9312 'XW' DECNET 3/3 (DECnet DDCMP DUP11)
	ROM_LOAD( "23-926a9.bin", 0x3800, 0x0200, NO_DUMP)                                                      // M9312 'XL' DECNET 1/3 (DECnet DDCMP DL11-E)
	ROM_LOAD( "23-927a9.bin", 0x3a00, 0x0200, NO_DUMP)                                                      // M9312 'XL' DECNET 2/3 (DECnet DDCMP DL11-E)
	ROM_LOAD( "23-928a9.bin", 0x3c00, 0x0200, NO_DUMP)                                                      // M9312 'XL' DECNET 3/3 (DECnet DDCMP DL11-E)
	ROM_LOAD( "23-e22a9.bin", 0x3e00, 0x0200, NO_DUMP)                                                      // M9312 'XE' DEUNA DECnet Ethernet
	ROM_LOAD( "23-e39a9.bin", 0x4000, 0x0200, CRC(4b94e3fa) SHA1(3cf92c2f64f95e8cc3abb8af2526cc65ce53ca8a)) // M9312 'MU' TMSCP tapes, including TK50, TU81

ROM_END

ROM_START( pdp11qb )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_BYTE( "m7195fa.1", 0xc000, 0x2000, CRC(0fa58752) SHA1(4bcd006790a60f2998ee8377ac5e2c18ef330930))
	ROM_LOAD16_BYTE( "m7195fa.2", 0xc001, 0x2000, CRC(15b6f60c) SHA1(80dd4f8ca3c27babb5e75111b04241596a07c53a))
ROM_END

ROM_START( sms1000 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "21251001u",    0x00000, 0x010000, CRC(eec3ccbb) SHA1(69eedb2c3bffe0a2988b1c066df1fea195618087) )

	ROM_REGION( 0x8000, "subcpu", 0 )
	ROM_LOAD( "21251000u",    0x00000, 0x008000, CRC(68db0afc) SHA1(577124bc64f6ddc9771e11b483120a175bfcf8c5) )

	ROM_REGION( 0x800, "prom", 0 )
	ROM_LOAD( "21251002u",    0x00000, 0x000800, CRC(66ca0eaf) SHA1(8141f64f81d9954169bcff6c79fd9f85e91f98e0) )

	ROM_REGION( 0x20000, "user1", ROMREGION_ERASEFF )
	ROM_LOAD( "2123001",      0x00000, 0x000800, CRC(7eb10e9b) SHA1(521ce8b8a79075c30ad92d810141c725d26fc50e) )
	ROM_LOAD( "2115001.jed",  0x01000, 0x000b19, CRC(02170f78) SHA1(afe50d165b39bff1cadae4290344341376729fda) )
	// no idea how large these undumped proms are
	ROM_LOAD( "2096001",      0x1f000, 0x000100, NO_DUMP )
	ROM_LOAD( "2097002",      0x1f000, 0x000100, NO_DUMP )
	ROM_LOAD( "20982000f",    0x1f000, 0x000100, NO_DUMP )
	ROM_LOAD( "20982001f",    0x1f000, 0x000100, NO_DUMP )
	ROM_LOAD( "2099002b",     0x1f000, 0x000100, NO_DUMP )
	ROM_LOAD( "2108001",      0x1f000, 0x000100, NO_DUMP )
	ROM_LOAD( "2109001",      0x1f000, 0x000100, NO_DUMP )
	ROM_LOAD( "2110001",      0x1f000, 0x000100, NO_DUMP )
	ROM_LOAD( "2111001",      0x1f000, 0x000100, NO_DUMP )
	ROM_LOAD( "2116001",      0x1f000, 0x000100, NO_DUMP )
	ROM_LOAD( "2117001",      0x1f000, 0x000100, NO_DUMP )
	ROM_LOAD( "2118001",      0x1f000, 0x000100, NO_DUMP )
	ROM_LOAD( "2119001",      0x1f000, 0x000100, NO_DUMP )
	ROM_LOAD( "2120001",      0x1f000, 0x000100, NO_DUMP )
	ROM_LOAD( "2121001",      0x1f000, 0x000100, NO_DUMP )
	ROM_LOAD( "2122001",      0x1f000, 0x000100, NO_DUMP )
	ROM_LOAD( "2124008",      0x1f000, 0x000100, NO_DUMP )
	ROM_LOAD( "2124009",      0x1f000, 0x000100, NO_DUMP )
	ROM_LOAD( "2124010",      0x1f000, 0x000100, NO_DUMP )
	ROM_LOAD( "2127001b",     0x1f000, 0x000100, NO_DUMP ) // has 3 of these
ROM_END

} // Anonymous namespace


/* Driver */

/*    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT  CLASS        INIT        COMPANY                          FULLNAME                            FLAGS */
COMP( 197?, pdp11ub,  0,       0,      pdp11,    pdp11, pdp11_state, empty_init, "Digital Equipment Corporation", "PDP-11 [Unibus](M9301-YA)",        MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
COMP( 197?, pdp11ub2, pdp11ub, 0,      pdp11ub2, pdp11, pdp11_state, empty_init, "Digital Equipment Corporation", "PDP-11 [Unibus](M9312)",           MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
COMP( 198?, pdp11qb,  pdp11ub, 0,      pdp11qb,  pdp11, pdp11_state, empty_init, "Digital Equipment Corporation", "PDP-11 [Q-BUS] (M7195 - MXV11-B)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
COMP( 1987, sms1000,  0,       0,      sms1000,  pdp11, pdp11_state, empty_init, "Scientific Micro Systems",      "SMS-1000",                         MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



pdt3100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Skeleton driver for Symbol PDT 3100 handheld computer.

    Parts side
MCU NEC V25 D70320GJ-8
3 x Flash Intel N28F010
Xtal R160SHAA7
SRAM Hitachi 658512ALFP-8
    LCD side
CMOS Pseudo Static RAM Toshiba TC518128CFTL-80
Display controller? STI 13130-018
Real-Time Clock plus RAM with Serial Interface MC68HC68T1

Installed program: "Scan3000" from the Spanish company "CB IBERSOFT".

*******************************************************************************/

#include "emu.h"
#include "cpu/nec/v25.h"


namespace {

class pdt3100_state : public driver_device
{
public:
	pdt3100_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void pdt3100(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<v25_device> m_maincpu;
};

void pdt3100_state::mem_map(address_map &map)
{
	map(0x00000, 0x9ffff).ram();
	map(0xa0000, 0xbffff).rom().region("flash3", 0);
	map(0xc0000, 0xdffff).rom().region("flash2", 0);
	map(0xe0000, 0xfffff).rom().region("flash1", 0);
}

void pdt3100_state::io_map(address_map &map)
{
	map(0x01e7, 0x01e7).lr8([]() { return 0xff; }, "ff_r");
}

static INPUT_PORTS_START(pdt3100)
INPUT_PORTS_END

void pdt3100_state::pdt3100(machine_config &config)
{
	V25(config, m_maincpu, 8'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &pdt3100_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &pdt3100_state::io_map);
}

ROM_START(pdt3100)
	ROM_REGION(0x20000, "flash1", 0)
	ROM_LOAD("03-11061-24_e973_j._l_62605-12.hex_03-11-98.u9", 0x00000, 0x20000, CRC(527831fc) SHA1(370b62491564107b468f43824d21d898eaaf5967))

	ROM_REGION(0x20000, "flash2", 0)
	ROM_LOAD("03-13466-06_0de5_12-30-97.u8", 0x00000, 0x20000, CRC(c2bb5418) SHA1(418cbbfef609e61f3fb474d83b4faf75c68211c8))

	ROM_REGION(0x20000, "flash3", 0)
	ROM_LOAD("03-13467-06_c62e_02-02-98.u11", 0x00000, 0x20000, CRC(4ff6396c) SHA1(c4bc8e8e0991fa7f7852726c280f9aba57fef3f4))
ROM_END

} // anonymous namespace


COMP(1998, pdt3100, 0, 0, pdt3100, pdt3100, pdt3100_state, empty_init, "Symbol", "PDT 3100 (v1.10-00)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



pecom.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

Pecom driver by Miodrag Milanovic

2008-11-08 Preliminary driver.

- All commands to be in UPPERCASE.
- Change background colour: SCR n
- Enter monitor: PROB     (B to exit)
- If Capslock is engaged, then Shift doesn't work.
- Control hangs the machine while it is pressed. It doesn't work in the
  expected way.
- Don't touch the Shift key while loading a tape because it will corrupt
  the data.
- The screen will flash in a crazy epileptic fashion while loading a tape.
  Beware!

TODO:
- Cassette: can load its own recordings, but not those from software list
  (software-list tapes are slower & wobbly)
- Both machines currently have 32k ram.
- Autorepeat seems a bit fast

****************************************************************************/

#include "emu.h"
#include "cpu/cosmac/cosmac.h"
#include "imagedev/cassette.h"
#include "sound/cdp1869.h"

#include "softlist_dev.h"
#include "speaker.h"

namespace {

class pecom_state : public driver_device
{
public:
	pecom_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cdp1869(*this, "cdp1869")
		, m_cassette(*this, "cassette")
		, m_bank1(*this, "bank1")
		, m_bank3(*this, "bank3")
		, m_bank4(*this, "bank4")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_io_cnt(*this, "CNT")
		, m_io_keyboard(*this, "LINE%d", 0U)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(ef_w);
	void pecom64(machine_config &config);

private:
	void bank_w(uint8_t data);
	uint8_t keyboard_r();
	void cdp1869_w(offs_t offset, uint8_t data);
	TIMER_CALLBACK_MEMBER(reset_tick);
	int clear_r();
	int ef2_r();
	void q_w(int state);
	void sc_w(uint8_t data);
	void prd_w(int state);
	CDP1869_CHAR_RAM_READ_MEMBER(char_ram_r);
	CDP1869_CHAR_RAM_WRITE_MEMBER(char_ram_w);
	CDP1869_PCB_READ_MEMBER(pcb_r);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	void cdp1869_page_ram(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	std::unique_ptr<uint8_t[]> m_charram;           /* character generator ROM */
	bool m_reset = false;                /* CPU mode */
	bool m_dma = false;              /* memory refresh DMA */

	/* timers */
	emu_timer *m_reset_timer = nullptr;   /* power on reset timer */

	required_device<cosmac_device> m_maincpu;
	required_device<cdp1869_device> m_cdp1869;
	required_device<cassette_image_device> m_cassette;
	required_memory_bank m_bank1;
	required_memory_bank m_bank3;
	required_memory_bank m_bank4;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_ioport m_io_cnt;
	required_ioport_array<26> m_io_keyboard;
};

TIMER_CALLBACK_MEMBER(pecom_state::reset_tick)
{
	m_reset = true;
}

void pecom_state::machine_reset()
{
	address_space &space = m_maincpu->space(AS_PROGRAM);
	m_bank1->set_entry(1);

	space.unmap_write(0xf000, 0xffff);
	space.install_read_bank (0xf000, 0xf7ff, m_bank3);
	space.install_read_bank (0xf800, 0xffff, m_bank4);
	m_bank3->set_base(m_rom + 0x7000);
	m_bank4->set_base(m_rom + 0x7800);

	m_reset = false;
	m_dma = false;
	m_reset_timer->adjust(attotime::from_msec(5));
}

void pecom_state::bank_w(uint8_t data)
{
	address_space &space = m_maincpu->space(AS_PROGRAM);
	m_bank1->set_entry(0);

	if (data==2)
	{
		space.install_read_handler (0xf000, 0xf7ff, read8sm_delegate(m_cdp1869, FUNC(cdp1869_device::char_ram_r)));
		space.install_write_handler(0xf000, 0xf7ff, write8sm_delegate(m_cdp1869, FUNC(cdp1869_device::char_ram_w)));
		space.install_read_handler (0xf800, 0xffff, read8sm_delegate(m_cdp1869, FUNC(cdp1869_device::page_ram_r)));
		space.install_write_handler(0xf800, 0xffff, write8sm_delegate(m_cdp1869, FUNC(cdp1869_device::page_ram_w)));
	}
	else
	{
		space.unmap_write(0xf000, 0xffff);
		space.install_read_bank (0xf000, 0xf7ff, m_bank3);
		space.install_read_bank (0xf800, 0xffff, m_bank4);
		m_bank3->set_base(m_rom + 0x7000);
		m_bank4->set_base(m_rom + 0x7800);
	}
}

uint8_t pecom_state::keyboard_r()
{
	/*
	   INP command BUS -> M(R(X)) BUS -> D
	   so on each input, address is also set, 8 lower bits are used as input for keyboard
	   Address is available on address bus during reading of value from port, and that is
	   used to determine keyboard line reading
	*/
	uint16_t addr = m_maincpu->state_int(cosmac_device::COSMAC_R0 + m_maincpu->state_int(cosmac_device::COSMAC_X));
	/* just in case someone is reading non existing ports */
	if (addr<0x7cca || addr>0x7ce3) return 0;
	return m_io_keyboard[addr - 0x7cca]->read();
}

/* CDP1802 Interface */

int pecom_state::clear_r()
{
	return m_reset;
}

int pecom_state::ef2_r()
{
	bool shift = BIT(m_io_cnt->read(), 1);
	double cas = false;//m_cassette->input();

	return (cas < -0.02) | shift; // touching shift kills cassette load
}

void pecom_state::q_w(int state)
{
	m_cassette->output(state ? -1.0 : +1.0);
}

void pecom_state::sc_w(uint8_t data)
{
	switch (data)
	{
		case COSMAC_STATE_CODE_S2_DMA:
			// DMA acknowledge clears the DMAOUT request
			m_maincpu->set_input_line(COSMAC_INPUT_LINE_DMAOUT, CLEAR_LINE);
			break;

		default:
			break;
	}
}

void pecom_state::cdp1869_w(offs_t offset, uint8_t data)
{
	uint16_t ma = m_maincpu->get_memory_address();

	switch (offset + 3)
	{
	case 3:
		m_cdp1869->out3_w(data);
		break;

	case 4:
		m_cdp1869->out4_w(ma);
		break;

	case 5:
		m_cdp1869->out5_w(ma);
		break;

	case 6:
		m_cdp1869->out6_w(ma);
		break;

	case 7:
		m_cdp1869->out7_w(ma);
		break;
	}
}

CDP1869_CHAR_RAM_READ_MEMBER(pecom_state::char_ram_r )
{
	uint8_t column = pmd & 0x7f;
	uint16_t charaddr = (column << 4) | cma;

	return m_charram[charaddr];
}

CDP1869_CHAR_RAM_WRITE_MEMBER(pecom_state::char_ram_w )
{
	uint8_t column = pmd & 0x7f;
	uint16_t charaddr = (column << 4) | cma;

	m_charram[charaddr] = data;
}

CDP1869_PCB_READ_MEMBER(pecom_state::pcb_r )
{
	return BIT(pmd, 7);
}

void pecom_state::prd_w(int state)
{
	// every other PRD triggers a DMAOUT request
	if (m_dma)
		m_maincpu->set_input_line(COSMAC_INPUT_LINE_DMAOUT, HOLD_LINE);

	m_dma = !m_dma;
}

void pecom_state::machine_start()
{
	m_bank1->configure_entry(0, m_ram);
	m_bank1->configure_entry(1, m_rom);

	/* allocate memory */
	m_charram = std::make_unique<uint8_t[]>(0x0800);

	/* register for state saving */
	save_item(NAME(m_reset));
	save_item(NAME(m_dma));
	save_pointer(NAME(m_charram), 0x0800);
	m_reset_timer = timer_alloc(FUNC(pecom_state::reset_tick), this);
}

/* Address maps */
void pecom_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).ram().share("mainram");
	map(0x0000, 0x3fff).bankr("bank1");
	map(0x8000, 0xefff).rom().region("maincpu",0);
	map(0xf000, 0xf7ff).bankrw("bank3"); // CDP1869 / ROM
	map(0xf800, 0xffff).bankrw("bank4"); // CDP1869 / ROM
}

void pecom_state::io_map(address_map &map)
{
	map(0x01, 0x01).w(FUNC(pecom_state::bank_w));
	map(0x03, 0x03).r(FUNC(pecom_state::keyboard_r));
	map(0x03, 0x07).w(FUNC(pecom_state::cdp1869_w));
}

void pecom_state::cdp1869_page_ram(address_map &map)
{
	map(0x000, 0x3ff).mirror(0x400).ram();
}

/* Input ports */
/* Pecom 64 keyboard layout is as follows

    1!     2"     3#     4$     5%     6&     7'     8(     9)     0     BREAK

   DEL  Q      W      E      R      T      Y      U      I      O      P   ESC

    CAPS    A      S      D      F      G      H      J      K      L   RETURN

  CTRL  ,<     Z      X      C      V      B      N      M      :*     /?   LF

     SHIFT  .>    Down   Left       SPACEBAR       Right    Up      ;+    =-

Being keys distributed on four lines, it makes a bit difficult to accurately remap them
on modern keyboards. Hence, we move by default Up/Down/Left/Right to Cursor Keys and
use Left/Right Ctrl/Alt keys for the remaining keys. Due to the unnatural emulated keyboard
mappings, this is another situation where natural keyboard comes very handy!          */

INPUT_CHANGED_MEMBER(pecom_state::ef_w)
{
	m_maincpu->set_input_line((int)param, newval);
}

static INPUT_PORTS_START( pecom )
	PORT_START("LINE0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_COLON) PORT_CHAR(13)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Line Feed") PORT_CODE(KEYCODE_SLASH)

	PORT_START("LINE1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Esc") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) // Actually this is again / ? - same key connected as on SLASH

	PORT_START("LINE2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')

	PORT_START("LINE3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')

	PORT_START("LINE4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')

	PORT_START("LINE5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("LINE6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')

	PORT_START("LINE7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RALT) PORT_CHAR(';') PORT_CHAR('+')

	PORT_START("LINE8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR('=') PORT_CHAR('-')

	PORT_START("LINE9")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LALT) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("LINE10")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')

	PORT_START("LINE11")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')

	PORT_START("LINE12")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')

	PORT_START("LINE13")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')

	PORT_START("LINE14")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')

	PORT_START("LINE15")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')

	PORT_START("LINE16")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')

	PORT_START("LINE17")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')

	PORT_START("LINE18")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')

	PORT_START("LINE19")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')

	PORT_START("LINE20")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')

	PORT_START("LINE21")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')

	PORT_START("LINE22")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')

	PORT_START("LINE23")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(u8"Š  š  \u2193") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(U'Š') PORT_CHAR(U'š')

	PORT_START("LINE24")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(u8"Đ  đ  \u2190") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(U'Đ') PORT_CHAR(U'đ')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(u8"Ć  ć  \u2192") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(U'Ć') PORT_CHAR(U'ć')

	PORT_START("LINE25")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(u8"Č  č  \u2191") PORT_CODE(KEYCODE_UP) PORT_CHAR(U'Č') PORT_CHAR(U'č')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Del") PORT_CODE(KEYCODE_TAB) PORT_CHAR(UCHAR_MAMEKEY(DEL))

	PORT_START("CNT")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pecom_state::ef_w), COSMAC_INPUT_LINE_EF1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Caps") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_TOGGLE PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pecom_state::ef_w), COSMAC_INPUT_LINE_EF3)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Break") PORT_CODE(KEYCODE_MINUS) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pecom_state::ef_w), COSMAC_INPUT_LINE_EF4)
INPUT_PORTS_END

/* Machine driver */
void pecom_state::pecom64(machine_config &config)
{
	/* basic machine hardware */
	CDP1802(config, m_maincpu, cdp1869_device::DOT_CLK_PAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &pecom_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &pecom_state::io_map);
	m_maincpu->wait_cb().set_constant(1);
	m_maincpu->clear_cb().set(FUNC(pecom_state::clear_r));
	m_maincpu->ef2_cb().set(FUNC(pecom_state::ef2_r));
	m_maincpu->q_cb().set(FUNC(pecom_state::q_w));
	m_maincpu->sc_cb().set(FUNC(pecom_state::sc_w));

	SPEAKER(config, "mono").front_center();

	CDP1869(config, m_cdp1869, cdp1869_device::DOT_CLK_PAL, &pecom_state::cdp1869_page_ram);
	m_cdp1869->add_pal_screen(config, "screen", cdp1869_device::DOT_CLK_PAL);
	m_cdp1869->set_color_clock(cdp1869_device::COLOR_CLK_PAL);
	m_cdp1869->set_pcb_read_callback(FUNC(pecom_state::pcb_r));
	m_cdp1869->set_char_ram_read_callback(FUNC(pecom_state::char_ram_r));
	m_cdp1869->set_char_ram_write_callback(FUNC(pecom_state::char_ram_w));
	m_cdp1869->pal_ntsc_callback().set_constant(1);
	m_cdp1869->prd_callback().set(FUNC(pecom_state::prd_w));
	m_cdp1869->add_route(ALL_OUTPUTS, "mono", 0.25);

	// devices
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("pecom_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("pecom_cass");
}

/* ROM definition */
ROM_START( pecom32 )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "090786.bin", 0x0000, 0x4000, CRC(b3b1ea23) SHA1(de69f22568161ced801973345fa39d6d207b9e8c) )
ROM_END

ROM_START( pecom64 )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "ver4", "version 4")
	ROMX_LOAD( "rom_1_g_24.02.88_l.bin", 0x0000, 0x4000, CRC(9a433b47) SHA1(dadb8c399e0a25a2693e10e42a2d7fc2ea9ad427), ROM_BIOS(0) )
	ROMX_LOAD( "rom_2_g_24.02.88_d.bin", 0x4000, 0x4000, CRC(2116cadc) SHA1(03f11055cd221d438a40a41874af8fba0fa116d9), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "ver1", "version 1")
	ROMX_LOAD( "170887-rom1.bin", 0x0000, 0x4000, CRC(43710fb4) SHA1(f84f75061c9ac3e34af93141ecabd3c955881aa2), ROM_BIOS(1) )
	ROMX_LOAD( "170887-rom2.bin", 0x4000, 0x4000, CRC(d0d34f08) SHA1(7baab17d1e68771b8dcef97d0fffc655beabef28), ROM_BIOS(1) )
ROM_END

} // Anonymous namespace

/* Driver */

/*    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY   FULLNAME     FLAGS */
COMP( 1986, pecom32, 0,       0,      pecom64, pecom, pecom_state, empty_init, "Ei Nis (Elektronska Industrija Nis)", "Pecom 32", MACHINE_SUPPORTS_SAVE )
COMP( 1987, pecom64, pecom32, 0,      pecom64, pecom, pecom_state, empty_init, "Ei Nis (Elektronska Industrija Nis)", "Pecom 64", MACHINE_SUPPORTS_SAVE )



pegasos2.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    PEGASOS II

    Hardware:
    - PowerPC 750CXe (600 MHz) or MPC7447 (1 GHz)
    - Marvell Discovery II MV64361 northbridge
    - VIA 8231 southbridge
    - W83194 clock generator
    - DDR SDRAM memory, up to 2 GB
    - 1x AGP, 3x PCI slots
    - 2xLAN (Gigabit from northbridge, 100 MBit from southbridge)
    - VIA VT6306 (firewire)
    - AC97 sound (Sigmatel STAC 9766 Codec)
    - Floppy
    - PS/2 keyboard/mouse
    - Joystick

    TODO:
    - Everything

    Notes:
    - Designed by bplan GmbH

***************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/powerpc/ppc.h"
#include "machine/mv6436x.h"
#include "machine/pci.h"
#include "machine/vt8231_isa.h"


namespace {


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class pegasos2_state : public driver_device
{
public:
	pegasos2_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_syscon(*this, "syscon")
	{
	}

	void pegasos2(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<ppc_device> m_maincpu;
	required_device<mv64361_device> m_syscon;

	void mem_map(address_map &map) ATTR_COLD;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void pegasos2_state::mem_map(address_map &map)
{
	map(0xfff00000, 0xfff7ffff).rom().region("maincpu", 0);
}


//**************************************************************************
//  INPUT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( pegasos2 )
INPUT_PORTS_END


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void pegasos2_state::machine_start()
{
}

void pegasos2_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

static DEVICE_INPUT_DEFAULTS_START( com1_defaults )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_115200 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_115200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void pegasos2_state::pegasos2(machine_config &config)
{
	PPC604(config, m_maincpu, 100000000); // wrong cpu/clock
	m_maincpu->set_addrmap(AS_PROGRAM, &pegasos2_state::mem_map);

	MV64361(config, m_syscon, 0, m_maincpu, "pci0:00.0", "pci1:00.0");

	PCI_ROOT(config, "pci0", 0);
	MV64361_PCI_HOST(config, "pci0:00.0", 0);

	PCI_ROOT(config, "pci1", 0);
	MV64361_PCI_HOST(config, "pci1:00.0", 0);
	vt8231_isa_device &isa(VT8231_ISA(config, "pci1:0c.0", 0));
	isa.com1_txd_cb().set("com1", FUNC(rs232_port_device::write_txd));
	isa.com1_dtr_cb().set("com1", FUNC(rs232_port_device::write_dtr));
	isa.com1_rts_cb().set("com1", FUNC(rs232_port_device::write_rts));

	rs232_port_device &com1(RS232_PORT(config, "com1", default_rs232_devices, "terminal"));
	com1.set_option_device_input_defaults("null_modem", DEVICE_INPUT_DEFAULTS_NAME(com1_defaults));
	com1.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(com1_defaults));
	com1.rxd_handler().set("pci1:0c.0", FUNC(vt8231_isa_device::com1_rxd_w));
	com1.dcd_handler().set("pci1:0c.0", FUNC(vt8231_isa_device::com1_dcd_w));
	com1.dsr_handler().set("pci1:0c.0", FUNC(vt8231_isa_device::com1_dsr_w));
	com1.ri_handler().set("pci1:0c.0", FUNC(vt8231_isa_device::com1_ri_w));
	com1.cts_handler().set("pci1:0c.0", FUNC(vt8231_isa_device::com1_cts_w));
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( pegasos2 )
	ROM_REGION(0x80000, "maincpu", ROMREGION_64BIT | ROMREGION_BE)
	// extracted from 'up050404'
	ROM_LOAD("pegasos2.rom", 0x00000, 0x80000, CRC(7e992266) SHA1(08dc28afb3d10fb223376a28eebfd07c9f8df9fa))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY   FULLNAME      FLAGS
COMP( 2003, pegasos2, 0,      0,      pegasos2, pegasos2, pegasos2_state, empty_init, "Genesi", "PEGASOS II", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



pegasus.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

    Aamber Pegasus computer (New Zealand)

    http://web.mac.com/lord_philip/aamber_pegasus/Aamber_Pegasus.html

    Each copy of the monitor rom was made for an individual machine.
    The early bios versions checked that it was running on that
    particular computer.

    This computer has no sound.

    The usual way of loading a new rom was to plug it into the board.
    We have replaced this with cartslots, to save having to recompile
    whenever a new rom is found. Single rom programs will usually work in
    any slot (if it is going to work at all). A working rom will appear
    in the menu. Press the first letter to run it.

    If a machine language program is loaded via cassette, do it in the
    Monitor (L command), when loaded press Enter, and it will be in the
    menu.

    Basic cassettes are loaded in the usual way, that is, start Basic,
    type LOAD, press Enter. When done, RUN or LIST as needed.

    The Aamber Pegasus uses a MCM66710P, which is functionally
    equivalent to the MCM6571.

    Note that datasheet is incorrect for the number "9".
    The first byte is really 0x3E rather than 0x3F, confirmed on real
    hardware.

    TO DO:
    - Work on the other non-working programs

****************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/m6809/m6809.h"
#include "imagedev/cassette.h"
#include "machine/6821pia.h"
#include "machine/timer.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class pegasus_state : public driver_device
{
public:
	pegasus_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cass(*this, "cassette")
		, m_pia_s(*this, "pia_s")
		, m_pia_u(*this, "pia_u")
		, m_exp_00(*this, "exp00")
		, m_exp_01(*this, "exp01")
		, m_exp_02(*this, "exp02")
		, m_exp_0c(*this, "exp0c")
		, m_exp_0d(*this, "exp0d")
		, m_vram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
		, m_io_keyboard(*this, "KEY.%u", 0U)
	{ }

	void pegasusm(machine_config &config);
	void pegasus(machine_config &config);

	void init_pegasus();

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	u8 pegasus_keyboard_r();
	u8 pegasus_protection_r();
	u8 pegasus_pcg_r(offs_t offset);
	void pegasus_controls_w(u8 data);
	void pegasus_keyboard_w(u8 data);
	void pegasus_pcg_w(offs_t offset, u8 data);
	int pegasus_cassette_r();
	void pegasus_cassette_w(int state);
	void pegasus_firq_clr(int state);
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	TIMER_DEVICE_CALLBACK_MEMBER(pegasus_firq);
	std::pair<std::error_condition, std::string> load_cart(device_image_interface &image, generic_slot_device *slot, const char *reg_tag);
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(exp00_load) { return load_cart(image, m_exp_00, "0000"); }
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(exp01_load) { return load_cart(image, m_exp_01, "1000"); }
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(exp02_load) { return load_cart(image, m_exp_02, "2000"); }
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(exp0c_load) { return load_cart(image, m_exp_0c, "c000"); }
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(exp0d_load) { return load_cart(image, m_exp_0d, "d000"); }

	void pegasus_mem(address_map &map) ATTR_COLD;
	void pegasusm_mem(address_map &map) ATTR_COLD;

	u8 m_kbd_row = 0U;
	u8 m_control_bits = 0U;
	std::unique_ptr<u8[]> m_pcg;
	void pegasus_decrypt_rom(u8 *ROM);
	required_device<cpu_device> m_maincpu;
	required_device<cassette_image_device> m_cass;
	required_device<pia6821_device> m_pia_s;
	required_device<pia6821_device> m_pia_u;
	required_device<generic_slot_device> m_exp_00;
	required_device<generic_slot_device> m_exp_01;
	required_device<generic_slot_device> m_exp_02;
	required_device<generic_slot_device> m_exp_0c;
	required_device<generic_slot_device> m_exp_0d;
	required_shared_ptr<u8> m_vram;
	required_region_ptr<u8> m_p_chargen;
	required_ioport_array<8> m_io_keyboard;
};

TIMER_DEVICE_CALLBACK_MEMBER(pegasus_state::pegasus_firq)
{
	m_maincpu->set_input_line(M6809_FIRQ_LINE, HOLD_LINE);
}

void pegasus_state::pegasus_firq_clr(int state)
{
	m_maincpu->set_input_line(M6809_FIRQ_LINE, CLEAR_LINE);
}

u8 pegasus_state::pegasus_keyboard_r()
{
	u8 data = 0xff;
	for (unsigned i = 0; i < 8; i++)
		if (!BIT(m_kbd_row, i)) data &= m_io_keyboard[i]->read();

	m_pia_s->cb1_w((data == 0xff) ? 1 : 0);
	if (BIT(m_control_bits, 3))
		data <<= 4;
	return data;
}

void pegasus_state::pegasus_keyboard_w(u8 data)
{
	m_kbd_row = data;
}

void pegasus_state::pegasus_controls_w(u8 data)
{
/*  d0,d2 - not emulated
    d0 - Blank - Video blanking
    d1 - Char - select character rom or ram
    d2 - Page - enables writing to video ram
    d3 - Asc - Select which half of the keyboard to read
*/

	m_control_bits = data;
}

int pegasus_state::pegasus_cassette_r()
{
	return m_cass->input();
}

void pegasus_state::pegasus_cassette_w(int state)
{
	m_cass->output(state ? 1 : -1);
}

u8  pegasus_state::pegasus_pcg_r(offs_t offset)
{
	u8 code = m_vram[offset] & 0x7f;
	return m_pcg[(code << 4) | (~m_kbd_row & 15)];
}

void pegasus_state::pegasus_pcg_w(offs_t offset, u8 data)
{
//  if (BIT(m_control_bits, 1))
	{
		u8 code = m_vram[offset] & 0x7f;
		m_pcg[(code << 4) | (~m_kbd_row & 15)] = data;
	}
}

/* Must return the A register except when it is doing a rom search */
u8 pegasus_state::pegasus_protection_r()
{
	u8 data = m_maincpu->state_int(M6809_A);
	if (data == 0x20) data = 0xff;
	return data;
}

void pegasus_state::pegasus_mem(address_map &map)
{
	map.unmap_value_high();
	//map(0x0000, 0x2fff)      // mapped by the cartslots 1-3
	map(0xb000, 0xbdff).ram();
	map(0xbe00, 0xbfff).ram().share("videoram");
	//map(0xc000, 0xdfff)      // mapped by the cartslots 4-5
	map(0xe000, 0xe1ff).r(FUNC(pegasus_state::pegasus_protection_r));
	map(0xe200, 0xe3ff).rw(FUNC(pegasus_state::pegasus_pcg_r), FUNC(pegasus_state::pegasus_pcg_w));
	map(0xe400, 0xe403).mirror(0x1fc).rw(m_pia_u, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xe600, 0xe603).mirror(0x1fc).rw(m_pia_s, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xf000, 0xffff).rom().region("maincpu", 0);
}

void pegasus_state::pegasusm_mem(address_map &map)
{
	map.unmap_value_high();
	pegasus_mem(map);
	map(0x5000, 0xafff).ram();
}

/* Input ports */
static INPUT_PORTS_START( pegasus )
	PORT_START("KEY.0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("= +") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("0 )") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BackSpace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_CHAR(8)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Break") PORT_CODE(KEYCODE_NUMLOCK) PORT_CHAR(20)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\\ |") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("; :") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l') PORT_CHAR(13)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p') PORT_CHAR(16)

	PORT_START("KEY.1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("- _") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Tab") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("[ ]") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR(']')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Delete") PORT_CODE(KEYCODE_DEL) PORT_CHAR(127) PORT_CHAR('_')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\' \"") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i') PORT_CHAR(9)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k') PORT_CHAR(11)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o') PORT_CHAR(15)

	PORT_START("KEY.2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("5 %") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("8 *") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("6 ^") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("` ~") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('`') PORT_CHAR('~')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j') PORT_CHAR(10)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u') PORT_CHAR(21)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g') PORT_CHAR(7)

	PORT_START("KEY.3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t') PORT_CHAR(20)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("7 &") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("4 $") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED ) // outputs a space
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ShiftR") PORT_NAME("ShiftR") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h') PORT_CHAR(8)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y') PORT_CHAR(25)

	PORT_START("KEY.4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r') PORT_CHAR(18)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w') PORT_CHAR(23)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED ) // outputs a space
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c') PORT_CHAR(3)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f') PORT_CHAR(6)

	PORT_START("KEY.5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e') PORT_CHAR(5)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q') PORT_CHAR(17)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED ) // REPEAT key which is disconnected - outputs a space
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("1 !") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LineFeed") PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(10)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m') PORT_CHAR(13)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d') PORT_CHAR(4)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n') PORT_CHAR(14)

	PORT_START("KEY.6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v') PORT_CHAR(22)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a') PORT_CHAR(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x') PORT_CHAR(24)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BlankL") PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(16)

	PORT_START("KEY.7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b') PORT_CHAR(2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s') PORT_CHAR(19)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CapsLock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(19)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ShiftL") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BlankR") PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(21)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z') PORT_CHAR(26)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("{ }") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('{') PORT_CHAR('}')
INPUT_PORTS_END

static const u8 mcm6571a_shift[] =
{
	0,1,1,0,0,0,1,0,0,0,0,1,0,0,0,0,
	1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,
	1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0
};


u32 pegasus_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 sy=0, ma=0;
	const bool pcg_mode = BIT(m_control_bits, 1);

	for (u8 y = 0; y < 16; y++)
	{
		for (u8 ra = 0; ra < 16; ra++)
		{
			u16 *p = &bitmap.pix(sy++);

			for (u16 x = ma; x < ma + 32; x++)
			{
				u8 inv = 0xff;
				u8 chr = m_vram[x];

				if (BIT(chr, 7))
				{
					inv = 0;
					chr &= 0x7f;
				}

				u8 gfx;
				if (pcg_mode)
				{
					gfx = m_pcg[(chr << 4) | ra] ^ inv;
				}
				else if (mcm6571a_shift[chr])
				{
					if (ra < 3)
						gfx = inv;
					else
						gfx = m_p_chargen[(chr<<4) | (ra-3) ] ^ inv;
				}
				else
				{
					if (ra < 10)
						gfx = m_p_chargen[(chr<<4) | ra ] ^ inv;
					else
						gfx = inv;
				}

				/* Display a scanline of a character */
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma+=32;
	}
	return 0;
}

/* F4 Character Displayer */
static const gfx_layout pegasus_charlayout =
{
	8, 16,                  // text = 7 x 9, pcg = 8 x 16
	128,                    // 128 characters
	1,                      // 1 bits per pixel
	{ 0 },                  // no bitplanes
	// x offsets
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	// y offsets
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16                    // every char takes 16 bytes
};

static GFXDECODE_START( gfx_pegasus )
	GFXDECODE_ENTRY( "chargen", 0x0000, pegasus_charlayout, 0, 1 )
	//GFXDECODE_ENTRY( "pcg", 0x0000, pegasus_charlayout, 0, 1 )
GFXDECODE_END


// An encrypted single rom starts with 02, decrypted with 20.
// The 2nd and 3rd part of a multi-rom set will have no obvious byte,
// so we check the first 4 bytes for a signature, and decrypt if found.
void pegasus_state::pegasus_decrypt_rom(u8 *ROM)
{
	bool doit = false;

	if (ROM[0] == 0x02) doit = true;
	if (ROM[0] == 0x1e && ROM[1] == 0xfa && ROM[2] == 0x60 && ROM[3] == 0x71) doit = true; // xbasic 2nd rom
	if (ROM[0] == 0x72 && ROM[1] == 0x62 && ROM[2] == 0xc6 && ROM[3] == 0x36) doit = true; // xbasic 3rd rom
	if (ROM[0] == 0xf0 && ROM[1] == 0x40 && ROM[2] == 0xce && ROM[3] == 0x80) doit = true; // forth 2nd rom (both sets)
	if (ROM[0] == 0x80 && ROM[1] == 0x06 && ROM[2] == 0x68 && ROM[3] == 0x14) doit = true; // pascal 2nd rom

	if (doit)
	{
		std::vector<u8> temp_copy;
		temp_copy.resize(0x1000);
		for (int i = 0; i < 0x1000; i++)
		{
			const u16 j = bitswap<16>(i, 15, 14, 13, 12, 11, 10, 9, 8, 0, 1, 2, 3, 4, 5, 6, 7);
			const u8 b = bitswap<8>(ROM[i], 3, 2, 1, 0, 7, 6, 5, 4);
			temp_copy[j & 0xfff] = b;
		}
		memcpy(ROM, &temp_copy[0], 0x1000);
	}
}

std::pair<std::error_condition, std::string> pegasus_state::load_cart(device_image_interface &image, generic_slot_device *slot, const char *reg_tag)
{
	u32 size = slot->common_get_size(reg_tag);
	bool any_socket = false;

	if (size > 0x1000)
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported cartridge size (must be no larger than 4K)");

	if (image.loaded_through_softlist() && size == 0)
	{
		// we might be loading a cart compatible with all sockets!
		// so try to get region "rom"
		size = slot->common_get_size("rom");
		any_socket = true;

		if (size == 0)
		{
			// FIXME: multi-line message may not be displayed properly
			return std::make_pair(
					image_error::INVALIDIMAGE,
					"Attempted to load a file that does not work in this socket.\n"
					"Please check \"Usage\" field in the software list for the correct socket(s) to use.");
		}
	}

	slot->rom_alloc(0x1000, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE); // we alloc 0x1000 also for smaller ROMs!
	slot->common_load_rom(slot->get_rom_base(), size, any_socket ? "rom" : reg_tag);

	// raw images have to be decrypted (in particular the ones from softlist)
	pegasus_decrypt_rom(slot->get_rom_base());

	return std::make_pair(std::error_condition(), std::string());
}

void pegasus_state::machine_start()
{
	if (m_exp_00->exists())
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x0000, 0x0fff, read8sm_delegate(*m_exp_00, FUNC(generic_slot_device::read_rom)));
	if (m_exp_01->exists())
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x1000, 0x1fff, read8sm_delegate(*m_exp_01, FUNC(generic_slot_device::read_rom)));
	if (m_exp_02->exists())
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x2000, 0x2fff, read8sm_delegate(*m_exp_02, FUNC(generic_slot_device::read_rom)));
	if (m_exp_0c->exists())
		m_maincpu->space(AS_PROGRAM).install_read_handler(0xc000, 0xcfff, read8sm_delegate(*m_exp_0c, FUNC(generic_slot_device::read_rom)));
	if (m_exp_0d->exists())
		m_maincpu->space(AS_PROGRAM).install_read_handler(0xd000, 0xdfff, read8sm_delegate(*m_exp_0d, FUNC(generic_slot_device::read_rom)));
	m_pcg = make_unique_clear<u8[]>(0x0800);
	save_pointer(NAME(m_pcg), 0x0800);
	save_item(NAME(m_kbd_row));
	save_item(NAME(m_control_bits));
}

void pegasus_state::machine_reset()
{
	m_kbd_row = 0;
	m_pia_s->cb1_w(1);
	m_control_bits = 0;
}

void pegasus_state::init_pegasus()
{
	// decrypt monitor
	u8 *base = memregion("maincpu")->base();
	pegasus_decrypt_rom(base);
}

void pegasus_state::pegasus(machine_config &config)
{
	/* basic machine hardware */
	MC6809(config, m_maincpu, XTAL(4'000'000));  // actually a 6809C - 4MHZ clock coming in, 1MHZ internally
	m_maincpu->set_addrmap(AS_PROGRAM, &pegasus_state::pegasus_mem);

	TIMER(config, "pegasus_firq").configure_periodic(FUNC(pegasus_state::pegasus_firq), attotime::from_hz(400));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(pegasus_state::screen_update));
	screen.set_size(32*8, 16*16);
	screen.set_visarea(0, 32*8-1, 0, 16*16-1);
	screen.set_palette("palette");
	GFXDECODE(config, "gfxdecode", "palette", gfx_pegasus);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	/* devices */
	PIA6821(config, m_pia_s);
	m_pia_s->readpb_handler().set(FUNC(pegasus_state::pegasus_keyboard_r));
	m_pia_s->readca1_handler().set(FUNC(pegasus_state::pegasus_cassette_r));
	m_pia_s->writepa_handler().set(FUNC(pegasus_state::pegasus_keyboard_w));
	m_pia_s->writepb_handler().set(FUNC(pegasus_state::pegasus_controls_w));
	m_pia_s->ca2_handler().set(FUNC(pegasus_state::pegasus_cassette_w));
	m_pia_s->cb2_handler().set(FUNC(pegasus_state::pegasus_firq_clr));
	m_pia_s->irqa_handler().set_inputline("maincpu", M6809_IRQ_LINE);
	m_pia_s->irqb_handler().set_inputline("maincpu", M6809_IRQ_LINE);

	PIA6821(config, m_pia_u);
	m_pia_u->irqa_handler().set_inputline("maincpu", M6809_IRQ_LINE);
	m_pia_u->irqb_handler().set_inputline("maincpu", M6809_IRQ_LINE);

	GENERIC_SOCKET(config, "exp00", generic_plain_slot, "pegasus_cart").set_device_load(FUNC(pegasus_state::exp00_load));
	GENERIC_SOCKET(config, "exp01", generic_plain_slot, "pegasus_cart").set_device_load(FUNC(pegasus_state::exp01_load));
	GENERIC_SOCKET(config, "exp02", generic_plain_slot, "pegasus_cart").set_device_load(FUNC(pegasus_state::exp02_load));
	GENERIC_SOCKET(config, "exp0c", generic_plain_slot, "pegasus_cart").set_device_load(FUNC(pegasus_state::exp0c_load));
	GENERIC_SOCKET(config, "exp0d", generic_plain_slot, "pegasus_cart").set_device_load(FUNC(pegasus_state::exp0d_load));

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED|CASSETTE_MOTOR_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	/* Software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("pegasus_cart");
}

void pegasus_state::pegasusm(machine_config &config)
{
	pegasus(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &pegasus_state::pegasusm_mem);
}


/* ROM definition */
ROM_START( pegasus )
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "11r2674", "Monitor 1.1 r2674")
	ROMX_LOAD( "mon11_2674.bin",  0x0000, 0x1000, CRC(1640ff7e) SHA1(8199643749fb40fb8be05e9f311c75620ca939b1), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "10r2569", "Monitor 1.0 r2569")
	ROMX_LOAD( "mon10_2569.bin",  0x0000, 0x1000, CRC(910fc930) SHA1(a4f6bbe5def0268cc49ee7045616a39017dd8052), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS(2, "11r2569", "Monitor 1.1 r2569")
	ROMX_LOAD( "mon11_2569.bin",  0x0000, 0x1000, CRC(07b92002) SHA1(3c434601120870c888944ecd9ade5186432ddbc2), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS(3, "11r2669", "Monitor 1.1 r2669")
	ROMX_LOAD( "mon11_2669.bin",  0x0000, 0x1000, CRC(f3ee23c8) SHA1(3ac96935668f5e53799c90db5140393c2ef9ce36), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS(4, "22r2856", "Monitor 2.2 r2856")
	ROMX_LOAD( "mon22_2856.bin",  0x0000, 0x1000, CRC(5f5f688a) SHA1(3719eecc347e158dd027ea7aa8a068cdafc00d9b), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS(5, "22br2856", "Monitor 2.2B r2856")
	ROMX_LOAD( "mon22b_2856.bin", 0x0000, 0x1000, CRC(a47b0308) SHA1(f215e51aa8df6aed99c10f3df6a3589cb9f63d46), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS(6, "23r2601", "Monitor 2.3 r2601")
	ROMX_LOAD( "mon23_2601.bin",  0x0000, 0x1000, CRC(0e024222) SHA1(9950cba08996931b9d5a3368b44c7309638b4e08), ROM_BIOS(6) )
	ROM_SYSTEM_BIOS(7, "23ar2569", "Monitor 2.3A r2569")
	ROMX_LOAD( "mon23a_2569.bin", 0x0000, 0x1000, CRC(248e62c9) SHA1(adbde27e69b38b29ff89bacf28d0240a8e5d90f3), ROM_BIOS(7) )

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "6571.bin", 0x0000, 0x0800, CRC(5a25144b) SHA1(7b9fee0c8ef2605b85d12b6d9fe8feb82418c63a) )
ROM_END

#define rom_pegasusm rom_pegasus

} // anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT    CLASS          INIT          COMPANY      FULLNAME                                  FLAGS
COMP( 1981, pegasus,  0,       0,      pegasus,  pegasus, pegasus_state, init_pegasus, "Technosys", "Aamber Pegasus",                         MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1981, pegasusm, pegasus, 0,      pegasusm, pegasus, pegasus_state, init_pegasus, "Technosys", "Aamber Pegasus with RAM expansion unit", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



pencil2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

    Hanimex Pencil II
    Manufactured by Soundic, Hong Kong.

    2012-11-06 [Robbbert]

    Computer kindly donated for MAME by Ian Farquhar.

    Accessories:
    - PEN-216 : 16k RAM expansion
    - PEN-264 : 64k RAM expansion
    - PEN-511 : Data Cassette Recorder
    - ???     : Printer
    - ???     : Floppy Disk Drive (5.25)
    - ???     : Floppy Disk Controller
    - ???     : RS-232C Serial Interface
    - ???     : Coleco Adapter*
    - PEN-8xx : Various software on Cassette or Floppy Disk
    - ???     : Game Controller (joystick and 14 buttons)
    - PEN-7xx : Various software in Cartridge format
    - PEN-430 : Modem
    - PEN-902 : Computer power supply
    - PEN-962 : Monitor cable

    * The cart slot is the same as that found on the Colecovision console. By plugging the
      Coleco Adapter into the expansion slot, Colecovision cartridges can be plugged into the
      cart slot and played.

Information found by looking inside the computer
------------------------------------------------
Main Board PEN-002 11-50332-10

J1 Expansion slot
J2 Cart slot
J3 Memory expansion slot
J4 Printer slot
J5,J6 Joystick ports

XTAL 10.738 MHz

Output is to a TV on Australian Channel 1.

U1     uPD780C-1 (Z80A)
U2     Video chip with heatsink stuck on top, TMS9929
U3     SN76489AN
U4     2764 bios rom
U5     uPD4016C-2 (assumed to be equivalent of 6116 2K x 8bit static RAM)
U6     74LS04
U7     74LS74A
U8-10  74LS138
U11    74LS00
U12    74LS273
U13    74LS74A
U14-21 TMM416P-3 (4116-3 16k x 1bit dynamic RAM)
U22    74LS05
U23-24 SN74LS541


SD-BASIC usage:
All commands must be in uppercase, which is the default at boot.
The 'capslock' is done by pressing Shift and Esc together, and the
cursor will change to a checkerboard pattern.

The above key combination is not available to natural keyboard, so
press ~ instead. Natural keyboard & Paste assume this has been done,
to enable lowercase.


MEMORY MAP
0000-1FFF bios rom
2000-5FFF available for expansion
6000-7FFF static RAM (2K mirrored)
8000-FFFF cart slot

The 16k dynamic RAM holds the BASIC program and the video/gfx etc
but is banked out of view of a BASIC program.


ToDo:
- Joysticks (no info)

****************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "sound/sn76496.h"
#include "video/tms9928a.h"

#include "bus/centronics/ctronics.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "softlist_dev.h"
#include "speaker.h"


namespace {

class pencil2_state : public driver_device
{
public:
	pencil2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_centronics(*this, "centronics")
		, m_cass(*this, "cassette")
		, m_cart(*this, "cartslot")
	{}

	void pencil2(machine_config &config);

	int printer_ready_r();
	int printer_ack_r();

private:
	void port10_w(u8 data);
	void port30_w(u8 data);
	void port80_w(u8 data);
	void portc0_w(u8 data);
	u8 porte2_r();
	void write_centronics_ack(int state);
	void write_centronics_busy(int state);
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	int m_centronics_busy = 0;
	int m_centronics_ack = 0;
	bool m_cass_state = false;
	required_device<cpu_device> m_maincpu;
	required_device<centronics_device> m_centronics;
	required_device<cassette_image_device> m_cass;
	required_device<generic_slot_device> m_cart;
};

void pencil2_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom();
	map(0x2000, 0x5fff).nopw();  // stop error log filling up
	map(0x6000, 0x67ff).mirror(0x1800).ram();
	//map(0x8000, 0xffff)      // mapped by the cartslot
}

void pencil2_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x0f).w("cent_data_out", FUNC(output_latch_device::write));
	map(0x10, 0x1f).w(FUNC(pencil2_state::port10_w));
	map(0x30, 0x3f).w(FUNC(pencil2_state::port30_w));
	map(0x80, 0x9f).w(FUNC(pencil2_state::port80_w));
	map(0xa0, 0xa1).mirror(0x1e).rw("tms9928a", FUNC(tms9928a_device::read), FUNC(tms9928a_device::write));
	map(0xc0, 0xdf).w(FUNC(pencil2_state::portc0_w));
	map(0xe0, 0xff).w("sn76489a", FUNC(sn76489a_device::write));
	map(0xe0, 0xe0).portr("E0");
	map(0xe1, 0xe1).portr("E1");
	map(0xe2, 0xe2).r(FUNC(pencil2_state::porte2_r));
	map(0xe3, 0xe3).portr("E3");
	map(0xe4, 0xe4).portr("E4");
	map(0xe6, 0xe6).portr("E6");
	map(0xe8, 0xe8).portr("E8");
	map(0xea, 0xea).portr("EA");
	map(0xf0, 0xf0).portr("F0");
	map(0xf2, 0xf2).portr("F2");
}

u8 pencil2_state::porte2_r()
{
	return (m_cass->input() > 0.1) ? 0xff : 0x7f;
}

void pencil2_state::port10_w(u8 data)
{
	m_centronics->write_strobe(BIT(data, 0));
}

void pencil2_state::port30_w(u8 data)
{
	m_cass_state ^= 1;
	m_cass->output( m_cass_state ? -1.0 : +1.0);
}

void pencil2_state::port80_w(u8 data)
{
}

void pencil2_state::portc0_w(u8 data)
{
}

void pencil2_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

int pencil2_state::printer_ready_r()
{
	return m_centronics_busy;
}

void pencil2_state::write_centronics_ack(int state)
{
	m_centronics_ack = state;
}

int pencil2_state::printer_ack_r()
{
	return m_centronics_ack;
}


/* Input ports */
static INPUT_PORTS_START( pencil2 )
	PORT_START("E0")
	// port_custom MUST be ACTIVE_HIGH to work
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(pencil2_state::printer_ready_r))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Break") PORT_CODE(KEYCODE_END)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(pencil2_state::printer_ack_r))

	PORT_START("E1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') PORT_CHAR('@')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("E3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ESC) PORT_CHAR(27) PORT_CHAR('~') // natural keyboard: press ~ to enable lowercase
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(32)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("E4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("E6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR(39)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("E8")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR('/')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR('?')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("EA")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("F0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('^')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("F2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END


void pencil2_state::machine_start()
{
	save_item(NAME(m_centronics_busy));
	save_item(NAME(m_centronics_ack));
	save_item(NAME(m_cass_state));

	if (m_cart->exists())
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x8000, 0xffff, read8sm_delegate(*m_cart, FUNC(generic_slot_device::read_rom)));
}

void pencil2_state::pencil2(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(10'738'635)/3);
	m_maincpu->set_addrmap(AS_PROGRAM, &pencil2_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &pencil2_state::io_map);

	/* video hardware */
	tms9929a_device &vdp(TMS9929A(config, "tms9928a", XTAL(10'738'635)));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	vdp.int_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SN76489A(config, "sn76489a", XTAL(10'738'635)/3).add_route(ALL_OUTPUTS, "mono", 1.00); // guess

	/* cassette */
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	/* cartridge */
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "pencil2_cart");

	/* printer */
	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set(FUNC(pencil2_state::write_centronics_ack));
	m_centronics->busy_handler().set(FUNC(pencil2_state::write_centronics_busy));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	/* Software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("pencil2");
}

/* ROM definition */
ROM_START( pencil2 )
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD( "mt.u4", 0x0000, 0x2000, CRC(338d7b59) SHA1(2f89985ac06971e00210ff992bf1e30a296d10e7) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY    FULLNAME     FLAGS
COMP( 1983, pencil2, 0,      0,      pencil2, pencil2, pencil2_state, empty_init, "Hanimex", "Pencil II", MACHINE_SUPPORTS_SAVE )



pensebem.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Felipe Sanches
/*
    Pense Bem (TecToy 2017)
    driver by Felipe Correa da Silva Sanches <juca@members.fsf.org>

---------------------------------------------------------------------

    In the 80s Tec Toy released Pense Bem in Brazil. It is most
    likely identical to VTech Smart Start since I think they run the
    exact same Z8 code. The only difference must be branding in the
    handheld case.

    In 2017 Tec Toy re-released Pense Bem in Brazil but this time
    using an Atmel ATMEGA168PB chip. It is not clear if the ATMEGA
    code contains a copy of the Z8 ROM and emulates the Z8, or if
    Tec Toy ported the original Z8 code retargetting it to the
    Atmel chip. Or even if they simply reimplemented the full
    functionality from scratch.

    Inspecting the ATMEGA disasm, it does not look like an emulation
    of the original Z8 code, but further research would be needed
    to be sure.

    As of October 2020, there's still no successfull ROM dump of the
    original Pense Bem's Z8 ROM code, so this driver only emulates
    the 2017 re-release.

---------------------------------------------------------------------

    The 2017 edition of TecToy's Pense Bem has a 2x4 programming
    pin-header at position CN4:

    CN4 - ATMEGA
      1 -  4 VCC
      2 - 15 MOSI
      3 - 31 TXD
      4 - 16 MISO
      5 - 30 RXD
      6 - 17 SCK
      7 -  5 GND
      8 - 29 RESET

    R34 is the pull-up resistor for the RESET signal

---------------------------------------------------------------------

    Changelog:

    2020 OCT 27 [Felipe Sanches]:
        * Fixed keyboard inputs, display & buzzer.
        * Implementation of AVR8 Timer 1 Output Compare Match A is
          sub-optimal resulting in bad sound quality when emulating
          the buzzer.

    2017 OCT 07 [Felipe Sanches]:
        * Initial driver skeleton

---------------------------------------------------------------------

== Notes about the hardware: ==
=== Select keypad rows: ===
keypad pin 1 - PORT_B5
keypad pin 2 - PORT_B3
keypad pin 12 - PORT_B2
keypad pin 13 - PORT_B4

=== Read keypad Columns: ===
keypad pin 3 - PORT_E0
keypad pin 4 - PORT_D2
keypad pin 5 - PORT_E1
keypad pin 6 - PORT_D7
keypad pin 7 - PORT_D6
keypad pin 8 - PORT_D5
keypad pin 9 - PORT_D4
keypad pin 10 - PORT_D3

=== Display digits: ===
digit_7 - PORT_E2
digit_6 - PORT_E3
digit_5 - PORT_C0
digit_4 - PORT_C1
digit_3 - PORT_C2
digit_2 - PORT_C3
digit_1 - PORT_C4
digit_0 - PORT_C5

=== Display segments: ===
In parentheses are the resistor reference numbers on
the PCB for each of these signals.

seg_7 (R11) - PORT_D7
seg_6 (R12) - PORT_D6
seg_5 (R17) - PORT_D5
seg_4 (R13) - PORT_D4
seg_3 (R14) - PORT_D3
seg_2 (R15) - PORT_D2
seg_1 (R18) - PORT_E1
seg_0 (R16) - PORT_E0

=== Piezo buzzer: ===
Port B, bit 1

--------------------------------------------------------------------- */

#include "emu.h"
#include "cpu/avr8/avr8.h"
#include "video/pwm.h"
#include "sound/dac.h"
#include "screen.h"
#include "speaker.h"


namespace {

class pensebem2017_state : public driver_device
{
public:
	pensebem2017_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_dac(*this, "dac"),
		m_display(*this, "display"),
		m_keyb_rows(*this, "ROW%u", 0U)
	{
	}

	void pensebem2017(machine_config &config);

private:
	void prg_map(address_map &map) ATTR_COLD;
	void data_map(address_map &map) ATTR_COLD;

	uint8_t port_b_r();
	void port_b_w(uint8_t data);
	void port_c_w(uint8_t data);
	void port_d_w(uint8_t data);
	void port_e_w(uint8_t data);

	void update_display();

	uint8_t m_port_b = 0;
	uint8_t m_port_c = 0;
	uint8_t m_port_d = 0;
	uint8_t m_port_e = 0;

	required_device<atmega168_device> m_maincpu;
	required_device<dac_bit_interface> m_dac;
	required_device<pwm_display_device> m_display;
	required_ioport_array<4> m_keyb_rows;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
};

void pensebem2017_state::machine_start()
{
	save_item(NAME(m_port_b));
	save_item(NAME(m_port_c));
	save_item(NAME(m_port_d));
	save_item(NAME(m_port_e));
}

void pensebem2017_state::machine_reset()
{
	m_port_b = 0;
	m_port_c = 0;
	m_port_d = 0;
	m_port_e = 0;
}

uint8_t pensebem2017_state::port_b_r()
{
	uint8_t value = m_port_b & 0xc3;
	int bit;
	for (bit=0; bit<8; bit++)
	{
		if (bit < 2 && !BIT(m_port_e, bit)) break;
		if (bit >= 2 && !BIT(m_port_d, bit)) break;
	}
	if (BIT(m_keyb_rows[0]->read(), bit)) value |= (1 << 5);
	if (BIT(m_keyb_rows[1]->read(), bit)) value |= (1 << 3);
	if (BIT(m_keyb_rows[2]->read(), bit)) value |= (1 << 2);
	if (BIT(m_keyb_rows[3]->read(), bit)) value |= (1 << 4);
	return value;
}

void pensebem2017_state::port_b_w(uint8_t data) // buzzer + keyboard select rows
{
	m_port_b = data;
	m_dac->write(BIT(data, 1));
}

void pensebem2017_state::port_c_w(uint8_t data) // display
{
	m_port_c = data;
	update_display();
}

void pensebem2017_state::port_d_w(uint8_t data) // display
{
	m_port_d = data;
}

void pensebem2017_state::port_e_w(uint8_t data) // display
{
	m_port_e = data;
	update_display();
}

void pensebem2017_state::update_display()
{
	m_display->matrix(
		~bitswap<8>((m_port_c << 2 & 0xfc) | (m_port_e >> 2 & 0x03), 7,6,5,4,3,2,1,0),
		~((m_port_d & 0xfc) | (m_port_e & 0x03))
	);
}

void pensebem2017_state::prg_map(address_map &map)
{
	map(0x0000, 0x3fff).rom().region("maincpu", 0); /* 16 kbytes of Flash ROM */
}

void pensebem2017_state::data_map(address_map &map)
{
	map(0x0000, 0x03ff).ram(); /* ATMEGA168PB Internal 1024 bytes of SRAM */
	map(0x0400, 0xffff).ram(); /* Some additional SRAM ? This is likely an exagerated amount ! */
}

static INPUT_PORTS_START( pensebem2017 )
	PORT_START("ROW0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Desliga") PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Livro") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("D") PORT_CODE(KEYCODE_D)

	PORT_START("ROW1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME(u8"Adição") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME(u8"Subtração") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME(u8"÷") PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME(u8"×") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("+") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("0") PORT_CODE(KEYCODE_0)

	PORT_START("ROW2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME(u8"Multiplicação") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME(u8"Aritmética") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME(u8"Divisão") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME(u8"Adivinhe o Número") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME(u8"Número do Meio") PORT_CODE(KEYCODE_O)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME(u8"Memória Tons") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME(u8"Siga-me") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME(u8"Operação") PORT_CODE(KEYCODE_Y)

	PORT_START("ROW3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("9") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
INPUT_PORTS_END

void pensebem2017_state::pensebem2017(machine_config &config)
{
	/* CPU */
	ATMEGA168(config, m_maincpu, 16_MHz_XTAL); /* Actual chip is an Atmel ATMEGA168PB */
	m_maincpu->set_addrmap(AS_PROGRAM, &pensebem2017_state::prg_map);
	m_maincpu->set_addrmap(AS_DATA, &pensebem2017_state::data_map);
	m_maincpu->set_eeprom_tag("eeprom");
	m_maincpu->set_low_fuses(0xf7);
	m_maincpu->set_high_fuses(0xdd);
	m_maincpu->set_extended_fuses(0xf9);
	m_maincpu->set_lock_bits(0x0f);
	m_maincpu->gpio_in<atmega168_device::GPIOB>().set(FUNC(pensebem2017_state::port_b_r));
	m_maincpu->gpio_out<atmega168_device::GPIOB>().set(FUNC(pensebem2017_state::port_b_w));
	m_maincpu->gpio_out<atmega168_device::GPIOC>().set(FUNC(pensebem2017_state::port_c_w));
	m_maincpu->gpio_out<atmega168_device::GPIOD>().set(FUNC(pensebem2017_state::port_d_w));
	m_maincpu->gpio_out<atmega168_device::GPIOE>().set(FUNC(pensebem2017_state::port_e_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(50);
	screen.set_size(1490, 1080);
	screen.set_visarea_full();
	PWM_DISPLAY(config, m_display).set_size(8, 8);
	m_display->set_segmask(0xff, 0xff);

	/* sound hardware */
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.5);
}

ROM_START( pbem2017 )
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_DEFAULT_BIOS("sept2017")

	/* September 2017 release */
	ROM_SYSTEM_BIOS(0, "sept2017", "SEPT/2017")
	ROMX_LOAD("pensebem-2017.bin", 0x0000, 0x35b6, CRC(d394279e) SHA1(5576599394231c1f83817dd55992e3b5838ab003), ROM_BIOS(0))

	/* on-die 4kbyte eeprom */
	ROM_REGION(0x1000, "eeprom", ROMREGION_ERASEFF)

	ROM_REGION(0x42e1a, "screen", 0)
	ROM_LOAD("pensebem.svg", 0, 0x42e1a, CRC(7146c0db) SHA1(966e95742acdda05028ee7b0c1352c88abb35041))
ROM_END

} // anonymous namespace


/*   YEAR  NAME    PARENT    COMPAT    MACHINE        INPUT         STATE                INIT         COMPANY    FULLNAME */
COMP(2017, pbem2017,    0,        0,   pensebem2017,  pensebem2017, pensebem2017_state,  empty_init,  "TecToy",  "Pense Bem (2017)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)



pentagon.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, MetalliC
#include "emu.h"
#include "spec128.h"

#include "bus/spectrum/ay/slot.h"
#include "beta_m.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/tzx_cas.h"


namespace {

class pentagon_state : public spectrum_128_state
{
public:
	pentagon_state(const machine_config &mconfig, device_type type, const char *tag)
		: spectrum_128_state(mconfig, type, tag)
		, m_beta(*this, BETA_DISK_TAG)
	{ }

	void pentagon(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;
	virtual void pentagon_update_memory();

	required_device<beta_disk_device> m_beta;

private:
	rectangle get_screen_area() override;

	u8 beta_neutral_r(offs_t offset);
	u8 beta_enable_r(offs_t offset);
	u8 beta_disable_r(offs_t offset);
	INTERRUPT_GEN_MEMBER(pentagon_interrupt);
	void port_7ffd_w(offs_t offset, u8 data);

	void pentagon_io(address_map &map) ATTR_COLD;
	void pentagon_mem(address_map &map) ATTR_COLD;
	void pentagon_switch(address_map &map) ATTR_COLD;
};

class pent1024_state : public pentagon_state
{
public:
	pent1024_state(const machine_config &mconfig, device_type type, const char *tag)
		: pentagon_state(mconfig, type, tag)
	{ }

	void pent1024(machine_config &config);

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;
	void pentagon_update_memory() override;
};

void pentagon_state::pentagon_update_memory()
{
	m_screen->update_now();
	m_screen_location = m_ram->pointer() + ((BIT(m_port_7ffd_data, 3) ? 7 : 5) << 14);

	m_bank_ram[3]->set_entry(m_port_7ffd_data & 0x07);
	m_bank_rom[0]->set_entry(BIT(m_port_7ffd_data, 4));
}

void pentagon_state::port_7ffd_w(offs_t offset, u8 data)
{
	/* disable paging */
	if (m_port_7ffd_data & 0x20)
		return;

	m_port_7ffd_data = data;
	pentagon_update_memory();
}

rectangle pentagon_state::get_screen_area()
{
	return rectangle{136, 136 + 255, 80, 80 + 191};
}

INTERRUPT_GEN_MEMBER(pentagon_state::pentagon_interrupt)
{
	m_irq_on_timer->adjust(m_maincpu->clocks_to_attotime(3));
}

u8 pentagon_state::beta_neutral_r(offs_t offset)
{
	return m_program.read_byte(offset);
}

u8 pentagon_state::beta_enable_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
	{
		if (m_bank_rom[0]->entry() == 1)
		{
			m_beta->enable();
			m_bank_rom[0]->set_entry(3);
		}
	}
	return m_program.read_byte(offset + 0x3d00);
}

u8 pentagon_state::beta_disable_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
	{
		if (m_beta->is_active())
		{
			m_beta->disable();
			m_bank_rom[0]->set_entry(BIT(m_port_7ffd_data, 4));
		}
	}
	return m_program.read_byte(offset + 0x4000);
}

void pentagon_state::pentagon_mem(address_map &map)
{
	map(0x0000, 0x3fff).bankr(m_bank_rom[0]);
	map(0x4000, 0x7fff).bankr(m_bank_ram[1]).w(FUNC(pentagon_state::spectrum_128_ram_w<1>));
	map(0x8000, 0xbfff).bankrw(m_bank_ram[2]);
	map(0xc000, 0xffff).bankr(m_bank_ram[3]).w(FUNC(pentagon_state::spectrum_128_ram_w<3>));
}

void pentagon_state::pentagon_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0000).mirror(0x7ffd).w(FUNC(pentagon_state::port_7ffd_w));  // (A15 | A1) == 0
	map(0x001f, 0x001f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::status_r), FUNC(beta_disk_device::command_w));
	map(0x003f, 0x003f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::track_r), FUNC(beta_disk_device::track_w));
	map(0x005f, 0x005f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::sector_r), FUNC(beta_disk_device::sector_w));
	map(0x007f, 0x007f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::data_r), FUNC(beta_disk_device::data_w));
	map(0x00fe, 0x00fe).select(0xff00).rw(FUNC(pentagon_state::spectrum_ula_r), FUNC(pentagon_state::spectrum_ula_w));
	map(0x00ff, 0x00ff).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::state_r), FUNC(beta_disk_device::param_w));
	map(0x8000, 0x8000).mirror(0x3ffd).w("ay_slot", FUNC(ay_slot_device::data_w));
	map(0xc000, 0xc000).mirror(0x3ffd).rw("ay_slot", FUNC(ay_slot_device::data_r), FUNC(ay_slot_device::address_w));
}

void pentagon_state::pentagon_switch(address_map &map)
{
	map(0x0000, 0x3fff).r(FUNC(pentagon_state::beta_neutral_r)); // Overlap with next because we want real addresses on the 3e00-3fff range
	map(0x3d00, 0x3dff).r(FUNC(pentagon_state::beta_enable_r));
	map(0x4000, 0xffff).r(FUNC(pentagon_state::beta_disable_r));
}

void pentagon_state::machine_start()
{
	spectrum_128_state::machine_start();
	m_bank_rom[0]->configure_entries(3, 1, memregion("beta:beta")->base(), 0x4000);
}

void pentagon_state::machine_reset()
{
	m_port_7ffd_data = 0;
	m_port_1ffd_data = -1;
	pentagon_update_memory();
}

void pentagon_state::video_start()
{
	spectrum_128_state::video_start();
}

static const gfx_layout spectrum_charlayout =
{
	8, 8,            // 8 x 8 characters
	96,              // 96 characters
	1,               // 1 bits per pixel
	{ 0 },           // no bitplanes
	{ STEP8(0, 1) }, // x offsets
	{ STEP8(0, 8) }, // y offsets
	8 * 8            // every char takes 8 bytes
};

static GFXDECODE_START( gfx_pentagon )
	GFXDECODE_ENTRY( "maincpu", 0x17d00, spectrum_charlayout, 7, 1 )
GFXDECODE_END


void pentagon_state::pentagon(machine_config &config)
{
	spectrum_128(config);

	m_maincpu->set_clock(14_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &pentagon_state::pentagon_mem);
	m_maincpu->set_addrmap(AS_IO, &pentagon_state::pentagon_io);
	m_maincpu->set_addrmap(AS_OPCODES, &pentagon_state::pentagon_switch);
	m_maincpu->set_vblank_int("screen", FUNC(pentagon_state::pentagon_interrupt));
	m_maincpu->refresh_cb().remove();
	m_maincpu->nomreq_cb().remove();

	m_screen->set_raw(14_MHz_XTAL / 2, 448, 320, {get_screen_area().left() - 48, get_screen_area().right() + 48, get_screen_area().top() - 48, get_screen_area().bottom() + 48});
	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_pentagon);
	SPECTRUM_ULA_UNCONTENDED(config.replace(), m_ula);

	BETA_DISK(config, m_beta, 0);

	SPEAKER(config.replace(), "speakers", 2).front();

	AY_SLOT(config.replace(), "ay_slot", 14_MHz_XTAL / 8, default_ay_slot_devices, "ay_ay8912")
		.add_route(0, "speakers", 0.50, 0)
		.add_route(1, "speakers", 0.25, 0)
		.add_route(1, "speakers", 0.25, 1)
		.add_route(2, "speakers", 0.50, 1);

	config.device_remove("exp");

	SOFTWARE_LIST(config, "cass_list_pen").set_original("pentagon_cass");
	SOFTWARE_LIST(config, "betadisc_list").set_original("spectrum_betadisc_flop");
}


//-------------------------------------------------
//  Pentagon 1024
//-------------------------------------------------

void pent1024_state::pent1024(machine_config &config)
{
	pentagon(config);
	m_ram->set_default_size("1024K");
}

void pent1024_state::machine_start()
{
	pentagon_state::machine_start();
	m_bank_rom[0]->configure_entries(2, 1, memregion("maincpu")->base() + 0x18000, 0x4000);
}

void pent1024_state::machine_reset()
{
	m_beta->enable();
	m_bank_rom[0]->set_entry(3);

	pentagon_state::machine_reset();
}

void pent1024_state::pentagon_update_memory()
{
	pentagon_state::pentagon_update_memory();

	m_bank_ram[3]->set_entry(m_bank_ram[3]->entry() | ((m_port_7ffd_data & 0xc0) >> 3));
	if (m_beta->is_active() && !BIT(m_port_7ffd_data, 4))
		m_bank_rom[0]->set_entry(2);
}


/***************************************************************************

  Game driver(s)

***************************************************************************/

ROM_START(pentagon)
	ROM_REGION(0x01c000, "maincpu", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "v1", "Pentagon 128K")
	ROMX_LOAD("128p-0.rom", 0x010000, 0x4000, CRC(124ad9e0) SHA1(d07fcdeca892ee80494d286ea9ea5bf3928a1aca), ROM_BIOS(0))
	ROMX_LOAD("128p-1.rom", 0x014000, 0x4000, CRC(b96a36be) SHA1(80080644289ed93d71a1103992a154cc9802b2fa), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v2", "Pentagon 128K 93")
	ROMX_LOAD("128tr93.rom",0x010000, 0x4000, CRC(08ad241c) SHA1(16daba547c644ef01ce76d2686ccfbff72e13dbe), ROM_BIOS(1))
	ROMX_LOAD("128p-1.rom", 0x014000, 0x4000, CRC(b96a36be) SHA1(80080644289ed93d71a1103992a154cc9802b2fa), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v3", "Pentagon 128K (joined)")
	ROMX_LOAD("pentagon.rom", 0x010000, 0x8000, CRC(aa1ce4bd) SHA1(a584272f21dc82c14b7d4f1ed440e23a976e71f0), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v4", "Pentagon 128K Spanish")
	ROMX_LOAD("pent-es.rom", 0x010000, 0x8000, CRC(34d04bae) SHA1(6782c8c0ee77c40d6d3170a254894dae44ddc93e), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "v5", "Pentagon 128K SOS89R Monitor")
	ROMX_LOAD("128p-0.rom", 0x010000, 0x4000, CRC(124ad9e0) SHA1(d07fcdeca892ee80494d286ea9ea5bf3928a1aca), ROM_BIOS(4))
	ROMX_LOAD("sos89r.rom", 0x014000, 0x4000, CRC(09c9e7e1) SHA1(29c567921abd377d2f9c088352c392a5a0858651), ROM_BIOS(4))
	ROM_SYSTEM_BIOS(5, "v6", "Pentagon 128K 1990 Monitor")
	ROMX_LOAD("128p-0.rom",  0x010000, 0x4000, CRC(124ad9e0) SHA1(d07fcdeca892ee80494d286ea9ea5bf3928a1aca), ROM_BIOS(5))
	ROMX_LOAD("basic90.rom", 0x014000, 0x4000, CRC(a41575ba) SHA1(44c5de86e765172b0af154fe3934643ce40bf378), ROM_BIOS(5))
	ROM_SYSTEM_BIOS(6, "v7", "Pentagon 128K RaK(c) 1991 Monitor")
	ROMX_LOAD("128p-0.rom", 0x010000, 0x4000, CRC(124ad9e0) SHA1(d07fcdeca892ee80494d286ea9ea5bf3928a1aca), ROM_BIOS(6))
	ROMX_LOAD("sos48.rom",  0x014000, 0x4000, CRC(ceb4005d) SHA1(d56c01ea7abdca178efb2b1c6b2866a9a38274ee), ROM_BIOS(6))
	ROM_SYSTEM_BIOS(7, "v8", "Pentagon 128K Dynaelectronics 1989")
	ROMX_LOAD("128p-0.rom", 0x010000, 0x4000, CRC(124ad9e0) SHA1(d07fcdeca892ee80494d286ea9ea5bf3928a1aca), ROM_BIOS(7))
	ROMX_LOAD("m48a.rom",   0x014000, 0x4000, CRC(a3b4def6) SHA1(7ad59ca373876d452b0cf0ed5edb0e93c3176f1a), ROM_BIOS(7))
	ROM_SYSTEM_BIOS(8, "v9", "ZXVGS v0.22 by Yarek")
	ROMX_LOAD("zxvgs-22-0.rom", 0x010000, 0x4000, CRC(63041c61) SHA1(f6718097d939afa8881b4436741a5a23d7e93d78), ROM_BIOS(8))
	ROMX_LOAD("zxvgs-22-1.rom", 0x014000, 0x4000, CRC(f3736047) SHA1(f3739bf460a57e3f10e8dfb1e7120842938d27ea), ROM_BIOS(8))
	ROM_SYSTEM_BIOS(9, "v10", "ZXVGS v0.29 by Yarek")
	ROMX_LOAD("zxvg-29-0.rom", 0x010000, 0x4000, CRC(3b66f433) SHA1(d21df9e7f1ee99d8b38c2e6a32727aac0f1d5dc6), ROM_BIOS(9))
	ROMX_LOAD("zxvg-1.rom",    0x014000, 0x4000, CRC(a8baca3e) SHA1(f2f131eaa4de832eda76290e48f86e465d28ded7), ROM_BIOS(9))
	ROM_SYSTEM_BIOS(10, "v11", "ZXVGS v0.30 by Yarek")
	ROMX_LOAD("zxvg-30-0.rom", 0x010000, 0x4000, CRC(533e0f26) SHA1(b5f157c5d0da414ec77e445fdc40b78450129709), ROM_BIOS(10))
	ROMX_LOAD("zxvg-1.rom",    0x014000, 0x4000, CRC(a8baca3e) SHA1(f2f131eaa4de832eda76290e48f86e465d28ded7), ROM_BIOS(10))
	ROM_SYSTEM_BIOS(11, "v12", "ZXVGS v0.31 by Yarek")
	ROMX_LOAD("zxvg-31-0.rom", 0x010000, 0x4000, CRC(76f43500) SHA1(1c7cd52894847668418876d55b93b213d89d92ee), ROM_BIOS(11))
	ROMX_LOAD("zxvg-1.rom",    0x014000, 0x4000, CRC(a8baca3e) SHA1(f2f131eaa4de832eda76290e48f86e465d28ded7), ROM_BIOS(11))
	ROM_SYSTEM_BIOS(12, "v13", "ZXVGS v0.35 by Yarek")
	ROMX_LOAD("zxvg-35-0.rom", 0x010000, 0x4000, CRC(5cc8b3b1) SHA1(6c6d0ef1b65d7dc4f607d17204488264575ce48c), ROM_BIOS(12))
	ROMX_LOAD("zxvg-1.rom",    0x014000, 0x4000, CRC(a8baca3e) SHA1(f2f131eaa4de832eda76290e48f86e465d28ded7), ROM_BIOS(12))
	ROM_SYSTEM_BIOS(13, "v14", "NeOS 512")
	ROMX_LOAD("neos_512.rom", 0x010000, 0x4000, CRC(1657fa43) SHA1(647545f06257bce9b1919fcb86b2a49a21c851a7), ROM_BIOS(13))
	ROMX_LOAD("128p-1.rom",   0x014000, 0x4000, CRC(b96a36be) SHA1(80080644289ed93d71a1103992a154cc9802b2fa), ROM_BIOS(13))
ROM_END

ROM_START(pent1024)
	ROM_REGION(0x01c000, "maincpu", ROMREGION_ERASEFF)
	ROM_LOAD("128p-0.rom", 0x010000, 0x4000, CRC(124ad9e0) SHA1(d07fcdeca892ee80494d286ea9ea5bf3928a1aca))
	ROM_LOAD("128p-1.rom", 0x014000, 0x4000, CRC(b96a36be) SHA1(80080644289ed93d71a1103992a154cc9802b2fa))
	ROM_SYSTEM_BIOS(0, "v1", "Gluk 6.3r")
	ROMX_LOAD("gluk63r.rom",0x018000, 0x4000, CRC(ca321d79) SHA1(015eb96dafb273d4f4512c467e9b43c305fd1bc4), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v2", "Gluk 5.2i")
	ROMX_LOAD("gluk52i.rom", 0x018000, 0x4000, CRC(fe44b86a) SHA1(9099d8a0f99a818849ca67ae1a8d3e7eacf06e65), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v3", "Gluk 5.3")
	ROMX_LOAD("gluk53.rom",  0x018000, 0x4000, CRC(479515ef) SHA1(ed656cd4faa36de2e31b38102bcbd8cee12e7976), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v4", "Gluk 5.4")
	ROMX_LOAD("gluk54r.rom", 0x018000, 0x4000, CRC(f4c1e975) SHA1(7e9e116750e1398572695b9cf8a120e47066256e), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "v5", "Gluk 5.5r")
	ROMX_LOAD("gluk55r.rom", 0x018000, 0x4000, CRC(3658c1ee) SHA1(4a5c8ca1e090cfb0168796f0d695310fa5c955d3), ROM_BIOS(4))
	ROM_SYSTEM_BIOS(5, "v6", "Gluk 5.5rr")
	ROMX_LOAD("gluk55rr.rom",0x018000, 0x4000, CRC(6b60b818) SHA1(9d606275d17770c9341b33b43f40aee227078827), ROM_BIOS(5))
	ROM_SYSTEM_BIOS(6, "v7", "Gluk 6.0r")
	ROMX_LOAD("gluk60r.rom", 0x018000, 0x4000, CRC(d114a032) SHA1(5db3462ce7a51b473a3a7056e67c11a62cc1cc2a), ROM_BIOS(6))
	ROM_SYSTEM_BIOS(7, "v8", "Gluk 6.0-1r")
	ROMX_LOAD("gluk601r.rom", 0x018000, 0x4000, CRC(daf6310b) SHA1(b8945168d4d136b731b33ec4758f8510c47fb8c4), ROM_BIOS(7))
	ROM_SYSTEM_BIOS(8, "v9", "Gluk 5.1")
	ROMX_LOAD("gluk51.rom",   0x018000, 0x4000, CRC(ea8c760b) SHA1(adaab28066ca46fbcdcf084c3b53d5a1b82d94a9), ROM_BIOS(8))
ROM_END

} // Anonymous namespace


//    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT        CLASS           INIT        COMPANY             FULLNAME           FLAGS
COMP( 1991, pentagon, spec128, 0,      pentagon, spec_plus2a, pentagon_state, empty_init, "Vladimir Drozdov", "Pentagon 128K",   0 )
COMP( 2005, pent1024, spec128, 0,      pent1024, spec_plus2a, pent1024_state, empty_init, "Alex Zhabin",      "Pentagon 1024SL", 0 )



pentevo.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Andrei I. Holub
/***************************************************************************

ZX Evolution: BASECONF machine driver.
Implementation: Revision C

Hobby computer ZX Evolution is Spectrum-compatible with extensions.

Hardware (ZX Evolution):
- Z80 3.5 MHz (classic mode)/ 7 MHz (turbo mode without CPU wait cycles)/ 14 MHz (mega turbo with CPU wait cycles);
- 4 Mb RAM, 512Kb ROM;
- MiniITX board (172x170mm), 2 ZXBUS slots, power ATX or +5,+12V;
- Based on fpga (Altera EP1K50);
- Peripheral MCU ATMEGA128;
- PS/2 keyboard and mouse support;
- Floppy (WDC1793) Beta-disk compatible interface, IDE (one channel, up to 2 devices on master/slave mode), SD(HC) card, RS232;
- Sound: AY, Beeper, Covox (PWM);
- Real-time clock.

Refs:
ZxEvo: http://nedopc.com/zxevo/zxevo_eng.php
        Principal scheme (rev. C) :: http://nedopc.com/zxevo/zxevo_sch_revc.pdf
        Montage scheme (rev. C) :: http://nedopc.com/zxevo/zxevo_mon_revc.pdf

TODO:
    * Keyboard enabled
    * zx 16c

*******************************************************************************************/

#include "emu.h"
#include "atm.h"
#include "glukrs.h"

#include "bus/spectrum/ay/slot.h"
#include "bus/spectrum/zxbus/bus.h"
#include "machine/pckeybrd.h"
#include "machine/spi_sdcard.h"
#include "machine/timer.h"
#include "speaker.h"

#define LOG_MEM   (1U << 1)
#define LOG_VIDEO (1U << 2)
#define LOG_WARN  (1U << 3)

#define VERBOSE ( /*LOG_GENERAL | LOG_MEM | LOG_VIDEO |*/ LOG_WARN )
#include "logmacro.h"

#define LOGMEM(...)   LOGMASKED(LOG_MEM,   __VA_ARGS__)
#define LOGVIDEO(...) LOGMASKED(LOG_VIDEO, __VA_ARGS__)
#define LOGWARN(...)  LOGMASKED(LOG_WARN,  __VA_ARGS__)

namespace {

class pentevo_state : public atm_state
{
public:
	pentevo_state(const machine_config &mconfig, device_type type, const char *tag)
		: atm_state(mconfig, type, tag)
		, m_gfxdecode(*this, "gfxdecode")
		, m_char_ram(*this, "char_ram")
		, m_io_dos_view(*this, "io_dos_view")
		, m_glukrs(*this, "glukrs")
		, m_sdcard(*this, "sdcard")
		, m_keyboard(*this, "pc_keyboard")
		, m_io_mouse(*this, "mouse_input%u", 1U)
	{ }

	void pentevo(machine_config &config);

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;
	void video_start() override ATTR_COLD;

private:
	void init_mem_write();
	void pentevo_io(address_map &map) ATTR_COLD;

	void atm_port_ff_w(offs_t offset, u8 data) override;
	void pentevo_port_7f7_w(offs_t offset, u8 data);
	void pentevo_port_bf7_w(offs_t offset, u8 data);
	void pentevo_port_eff7_w(offs_t offset, u8 data);
	u8 pentevo_port_bf_r(offs_t offset);
	void pentevo_port_bf_w(offs_t offset, u8 data);
	u8 pentevo_port_0nbd_r(offs_t offset);
	u8 pentevo_port_1nbd_r(offs_t offset);
	void pentevo_port_1nbd_w(offs_t offset, u8 data);

	void spi_port_77_w(offs_t offset, u8 data);
	u8 spi_port_57_r(offs_t offset);
	void spi_port_57_w(offs_t offset, u8 data);
	void spi_miso_w(u8 data);
	u8 nemo_ata_r(u8 cmd);
	void nemo_ata_w(u8 cmd, u8 data);
	u8 gluk_data_r(offs_t offset);
	void gluk_data_w(offs_t offset, u8 data);

	TIMER_DEVICE_CALLBACK_MEMBER(nmi_check_callback);
	void nmi_on();

	void atm_update_cpu() override;
	void atm_update_io() override;
	u8 merge_ram_with_7ffd(u8 ram_page) override;
	bool is_port_7ffd_locked() override { return !is_pent1024() && BIT(m_port_7ffd_data, 5); }
	bool is_pent1024() { return !BIT(m_port_eff7_data, 2); }

	void spectrum_update_screen(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) override;
	void pentevo_update_screen_zxhw(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void pentevo_update_screen_zx16(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void pentevo_update_screen_tx(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	u16 atm_update_memory_get_page(u8 bank) override;
	INTERRUPT_GEN_MEMBER(pentevo_interrupt);

	required_device<gfxdecode_device> m_gfxdecode;
	required_device<ram_device> m_char_ram;
	memory_view m_io_dos_view;

	required_device<glukrs_device> m_glukrs;
	required_device<spi_sdcard_device> m_sdcard;
	required_device<at_keyboard_device> m_keyboard;
	required_ioport_array<3> m_io_mouse;

	u8 m_port_bf_data;
	u8 m_port_eff7_data;
	u8 m_beta_drive_virtual;

	u8 m_gluk_ext;
	bool m_ata_data_hi_ready;
	u16 m_nmi_trap_offset;
	bool m_nmi_active;
	u8 m_nmi_active_flip_countdown;

	u8 m_zctl_di = 0;
	u8 m_zctl_cs = 0;
};


/******************************************************************************
 * ZX Evolution: BASECONF
 * ***************************************************************************/

void pentevo_state::atm_update_cpu()
{
	u8 multiplier = BIT(m_port_77_data, 3) ? 4 : (2 - BIT(m_port_eff7_data, 4));
	m_maincpu->set_clock(X1_128_SINCLAIR / 10 * multiplier);
}

void pentevo_state::atm_update_io()
{
	if (BIT(m_port_bf_data, 0) || is_dos_active())
	{
		m_io_view.select(1);
		if (m_beta_drive_selected && m_beta_drive_virtual == m_beta_drive_selected)
			m_io_dos_view.disable();
		else
			m_io_dos_view.select(0);

		m_glukrs->enable();
	}
	else
	{
		m_io_view.select(0);
		if (BIT(m_port_eff7_data, 7))
			m_glukrs->enable();
		else
			m_glukrs->disable();
	}
}

u16 pentevo_state::atm_update_memory_get_page(u8 bank)
{
	if (bank == 0)
	{
		if (BIT(m_port_eff7_data, 3))
			return ~PEN_RAMNROM_MASK & 0x00;
		else if (m_nmi_active)
			return PEN_RAMNROM_MASK | 0xff;
		else if (m_beta_drive_selected && m_beta_drive_virtual == m_beta_drive_selected)
			return PEN_RAMNROM_MASK | 0xfe;
	}
	return atm_state::atm_update_memory_get_page(bank);
}

void pentevo_state::atm_port_ff_w(offs_t offset, u8 data)
{
	if (BIT(m_port_bf_data, 5) && !m_pen2)
	{
		u8 pen = 0x0f & get_border_color(m_screen->hpos(), m_screen->vpos());
		m_palette_data[pen] = data;
		m_palette->set_pen_color(pen,
			(BIT(~data, 1) * 0x88) | (BIT(~data, 6) * 0x44) | (BIT(~offset,  9) * 0x22) | (BIT(~offset, 14) * 0x11),
			(BIT(~data, 4) * 0x88) | (BIT(~data, 7) * 0x44) | (BIT(~offset, 12) * 0x22) | (BIT(~offset, 15) * 0x11),
			(BIT(~data, 0) * 0x88) | (BIT(~data, 5) * 0x44) | (BIT(~offset,  8) * 0x22) | (BIT(~offset, 13) * 0x11));
	}
	else
	{
		atm_state::atm_port_ff_w(offset, data);
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(pentevo_state::nmi_check_callback)
{
	if ((m_io_nmi->read() & 0x01) && !m_nmi_active)
		nmi_on();
}

void pentevo_state::nmi_on()
{
	m_nmi_active = true;
	m_nmi_active_flip_countdown = 0;
	atm_update_memory();
	m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}

u8 pentevo_state::merge_ram_with_7ffd(u8 ram_page)
{
	u8 page = atm_state::merge_ram_with_7ffd(ram_page);
	if (is_pent1024())
		page = (page & ~0x38) | ((m_port_7ffd_data & 0xe0) >> 2);

	return page;
}

void pentevo_state::pentevo_port_eff7_w(offs_t offset, u8 data)
{
	u8 changed = m_port_eff7_data ^ data;
	m_port_eff7_data = data;

	if (BIT(changed, 3)) atm_update_memory();
	if (BIT(changed, 4)) atm_update_cpu();
	if (BIT(changed, 7)) atm_update_io();
}

void pentevo_state::pentevo_port_7f7_w(offs_t offset, u8 data)
{
	u8 bank = offset >> 14;
	u16 page = (pen_page(bank) & PEN_DOS7FFD_MASK) | PEN_RAMNROM_MASK | u8(~data);

	LOGMEM("EVO%s=%X RAM%d%s%02X\n", BIT(m_port_7ffd_data, 4), data, bank, (page & PEN_DOS7FFD_MASK) ? "+" : " ", page & ram_pages_mask);
	pen_page(bank) = page;
	atm_update_memory();
}

void pentevo_state::pentevo_port_bf7_w(offs_t offset, u8 data)
{
	u8 bank = offset >> 14;
	if (BIT(data, 0))
		pen_page(bank) |= PEN_WRDISBL_MASK;
	else
		pen_page(bank) &= ~PEN_WRDISBL_MASK;
}

void pentevo_state::pentevo_port_bf_w(offs_t offset, u8 data)
{
	if (BIT(m_port_bf_data, 3) && !BIT(data, 3)) // 1>0
		// Due to the fact current z80 handles NMI detection before (not after) instruction OUT(#BF),0:HALT freezes driver.
		// With fixed z80 this must be replaced with:
		// nmi_on()
		m_nmi_active_flip_countdown = 1;

	m_port_bf_data = data;
	atm_update_io();
}

u8 pentevo_state::pentevo_port_bf_r(offs_t offset)
{
	return m_port_bf_data & 0x1f;
}

u8 pentevo_state::pentevo_port_0nbd_r(offs_t offset)
{
	u8 opt = (offset >> 8) & 0x0f;
	if (opt <= 0x07)
		return ~(m_pages_map[BIT(opt, 2)][opt & 0x03] & 0xff);
	else if (opt == 0x08 || opt == 0x09)
	{
		u8 data = 0;
		for (s8 i = 7; i >= 0; i--)
			data = (data << 1) | bool(m_pages_map[BIT(i, 2)][i & 0x03] & (opt == 0x08 ? PEN_RAMNROM_MASK : PEN_DOS7FFD_MASK));
		return data;
	}
	else if (opt == 0x0a)
		return m_port_7ffd_data;
	else if (opt == 0x0b)
		return m_port_eff7_data;
	else if (opt == 0x0c)
		return (m_pen2 << 7) | (m_cpm_n << 6) | (m_pen << 5) | ((m_nmi_active && m_beta->is_active()) << 4) | (m_port_77_data & 0x0f);
	else if (opt == 0x0d)
		return m_palette_data[get_border_color(m_screen->hpos(), m_screen->vpos())];
	else if (opt == 0x0e)
	{
		u8* screen_location = m_ram->pointer() + ((BIT(m_port_7ffd_data, 3) ? 10 : 8) << 14);
		u16 y = m_screen->vpos() - get_screen_area().top();
		u16 x = m_screen->hpos() - get_screen_area().left();
		u8 *symb_location = screen_location + 0x1c0 + (x >> 4) + ((y >> 3) * 64);
		if (BIT(x, 3)) symb_location += 0x1000;
		return m_char_ram->read((*symb_location << 3) + (y & 0x07));
	}
	else //if (opt == 0x0f)
		return get_border_color(m_screen->hpos(), m_screen->vpos());
}

u8 pentevo_state::pentevo_port_1nbd_r(offs_t offset)
{
	u8 opt = (offset >> 8) & 0x03;
	if (opt == 0x00)
		return m_nmi_trap_offset & 0x0f;
	else if (opt == 0x01)
		return (m_nmi_trap_offset & 0xf0) >> 8;
	else if (opt == 0x02)
	{
		u8 data = 0;
		for (s8 i = 7; i >= 0; i--)
			data = (data << 1) | bool(m_pages_map[BIT(i, 2)][i & 0x03] & PEN_WRDISBL_MASK);
		return data;
	}
	else //if (opt == 0x03)
		return m_beta_drive_virtual;
}

void pentevo_state::pentevo_port_1nbd_w(offs_t offset, u8 data)
{
	u8 opt = (offset >> 8) & 0x03;
	if (opt == 0x00)
		m_nmi_trap_offset = (m_nmi_trap_offset & 0xf0) | data;
	else if (opt == 0x01)
		m_nmi_trap_offset = (m_nmi_trap_offset & 0x0f) | (data << 8);
	else if (opt == 0x02)
	{
		u8 data = 0;
		for (s8 i = 7; i >= 0; i--)
		{
			m_pages_map[BIT(i, 2)][i & 0x03] &= ~PEN_WRDISBL_MASK;
			if (BIT(data, i)) m_pages_map[BIT(i, 2)][i & 0x03] |= PEN_WRDISBL_MASK;
		}
	}
	else if (opt == 0x03)
		m_beta_drive_virtual = data & 0x0f;
}

INTERRUPT_GEN_MEMBER(pentevo_state::pentevo_interrupt)
{
	// 17989=80*224+69 z80(3.5Hz) clocks between INT and screen paper begins. Screen clock is 7Hz.
	m_irq_on_timer->adjust(m_screen->time_until_pos(80 - 80, 80) - m_screen->clocks_to_attotime(128));
}

void pentevo_state::spectrum_update_screen(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	if (m_rg == 0b011 && (BIT(m_port_eff7_data, 0) || BIT(m_port_eff7_data, 5)))
	{
		if (BIT(m_port_eff7_data, 5))
			pentevo_update_screen_zxhw(screen, bitmap, cliprect);
		else
			pentevo_update_screen_zx16(screen, bitmap, cliprect);
	}
	else if (m_rg == 0b111)
		pentevo_update_screen_tx(screen, bitmap, cliprect);
	else
		atm_state::spectrum_update_screen(screen, bitmap, cliprect);
}

void pentevo_state::pentevo_update_screen_zxhw(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	bool invert_attrs = u64(screen.frame_number() / m_frame_invert_count) & 1;
	for (u16 vpos = cliprect.top(); vpos <= cliprect.bottom(); vpos++)
	{
		u16 hpos = cliprect.left();
		u16 x = hpos - get_screen_area().left();
		u16 y = vpos - get_screen_area().top();
		u8 *scr = &m_screen_location[((y & 7) << 8) | ((y & 0x38) << 2) | ((y & 0xc0) << 5) | (x >> 3)];
		u8 *attr = &scr[0x2000];
		u16 *pix = &(bitmap.pix(vpos, hpos));

		while (hpos <= cliprect.right())
		{
			u16 ink = ((*attr >> 3) & 0x08) | (*attr & 0x07);
			u16 pap = (*attr >> 3) & 0x0f;
			u8 pix8 = (invert_attrs && (*attr & 0x80)) ? ~*scr : *scr;

			for (u8 b = (0x80 >> (x % 8)); b; b >>= 1, x++, hpos++)
				*pix++ = (pix8 & b) ? ink : pap;
			scr++;
			attr++;
		}
	}
}

void pentevo_state::pentevo_update_screen_zx16(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	// TODO attrs not decoded
	for (u16 vpos = cliprect.top(); vpos <= cliprect.bottom(); vpos++)
	{
		u16 hpos = cliprect.left();
		u16 x = hpos - get_screen_area().left();
		u16 y = vpos - get_screen_area().top();
		u8 *scr = &m_screen_location[((y & 7) << 8) | ((y & 0x38) << 2) | ((y & 0xc0) << 5) | (x >> 3)];
		u16 *pix = &(bitmap.pix(vpos, hpos));

		while (hpos <= cliprect.right())
		{
			u16 ink = 0;
			u16 pap = 7;
			u8 pix8 = *scr;

			for (u8 b = (0x80 >> (x % 8)); b; b >>= 1, x++, hpos++)
				*pix++ = (pix8 & b) ? ink : pap;
			scr++;
		}
	}
}

void pentevo_state::pentevo_update_screen_tx(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u8* screen_location = m_ram->pointer() + ((BIT(m_port_7ffd_data, 3) ? 10 : 8) << 14);
	for (u16 vpos = cliprect.top(); vpos <= cliprect.bottom(); vpos++)
	{
		u16 y = vpos - get_screen_area().top();
		for (u16 hpos = cliprect.left() & 0xfff8; hpos <= cliprect.right();)
		{
			u16 x = hpos - get_screen_area().left();
			u8 *symb_location = screen_location + 0x1c0 + (x >> 4) + ((y >> 3) * 64);
			u8 *attr_location = symb_location + 0x2000 + BIT(x, 3);
			if (BIT(x, 3))
				symb_location += 0x1000;
			else
				attr_location += 0x1000;

			u8 attr = *attr_location;
			u8 fg = ((attr & 0x40) >> 3) | (attr & 0x07);
			u8 bg = (((attr & 0x80) >> 1) | (attr & 0x38)) >> 3;

			u8 chunk = m_char_ram->read((*symb_location << 3) + (y & 0x07));
			for (u8 i = 0x80; i; i >>= 1)
			{
				bitmap.pix(vpos, hpos++) = (chunk & i) ? fg : bg;
			}
		}
	}
}

u8 pentevo_state::nemo_ata_r(u8 cmd)
{
	if (machine().side_effects_disabled())
		return 0xff;

	bool data_read = (cmd & 0x7) == 0;
	u8 data;
	if (data_read && m_ata_data_hi_ready)
	{
		data = m_ata_data_latch;
		m_ata_data_hi_ready = false;
	}
	else
	{
		data = atm_state::ata_r(cmd << 5);
		m_ata_data_hi_ready = data_read;
	}

	return data;
}

void pentevo_state::nemo_ata_w(u8 cmd, u8 data)
{
	bool data_write = (cmd & 0x7) == 0;
	if (data_write && !m_ata_data_hi_ready)
	{
		m_ata_data_latch = data;
		m_ata_data_hi_ready = true;
	}
	else
	{
		atm_state::ata_w(cmd << 5, data);
		m_ata_data_hi_ready = false;
	}
}

void pentevo_state::spi_port_77_w(offs_t offset, u8 data)
{
	m_sdcard->spi_ss_w(BIT(data, 0));
	m_zctl_cs = BIT(data, 1);
}

u8 pentevo_state::spi_port_57_r(offs_t offset)
{
	if (m_zctl_cs)
		return 0xff;

	u8 din = m_zctl_di;
	if (!machine().side_effects_disabled())
		spi_port_57_w(0, 0xff);

	return din;
}

void pentevo_state::spi_port_57_w(offs_t offset, u8 data)
{
	if (!m_zctl_cs)
	{
		for (u8 m = 0x80; m; m >>= 1)
		{
			m_sdcard->spi_clock_w(CLEAR_LINE); // 0-S R
			m_sdcard->spi_mosi_w(data & m ? 1 : 0);
			m_sdcard->spi_clock_w(ASSERT_LINE); // 1-L W
		}
	}
}

void pentevo_state::spi_miso_w(u8 data)
{
	m_zctl_di <<= 1;
	m_zctl_di |= data;
}

u8 pentevo_state::gluk_data_r(offs_t offset)
{
	if (m_glukrs->is_active() && (m_gluk_ext == 2))
		return m_keyboard->read();

	return m_glukrs->data_r(); // returns 0xff if inactive
}

void pentevo_state::gluk_data_w(offs_t offset, u8 data)
{
	if (!m_glukrs->is_active())
		return;

	const u8 addr = m_glukrs->address_r();
	if (addr >= 0xf0 && addr <= 0xf0)
	{
		m_gluk_ext = data;
		u8 m_fx[0xf] = {0x00};
		if (data == 0 || data == 1) // BASECONF_VERSION + BOOTLOADER_VERSION
		{
			strcpy((char *)m_fx, "MAME");
			PAIR16 m_ver;
			m_ver.w = ((25 << 9) | (7 << 5) | 31); // y.m.d
			m_fx[0x0c] = m_ver.b.l;
			m_fx[0x0d] = m_ver.b.h;
		}

		for (u8 i = 0; i < 0xf; i++)
		{
			m_glukrs->write_direct(0xf0 + i, m_fx[i]);
		}
	}
	else
	{
		m_glukrs->data_w(data);
	}
}

void pentevo_state::pentevo_io(address_map &map)
{
	map.unmap_value_high();

	// PORTS: Always
	map(0x00f6, 0x00f6).select(0xff08).rw(FUNC(pentevo_state::spectrum_ula_r), FUNC(pentevo_state::atm_ula_w));
	map(0x00fb, 0x00fb).mirror(0xff00).w("cent_data_out", FUNC(output_latch_device::write));
	map(0x00fd, 0x00fd).mirror(0xff00).w(FUNC(pentevo_state::atm_port_7ffd_w));

	// Gluk
	map(0xdff7, 0xdff7).lw8(NAME([this](offs_t offset, u8 data) { m_glukrs->address_w(data); } ));
	map(0xbff7, 0xbff7).rw(FUNC(pentevo_state::gluk_data_r), FUNC(pentevo_state::gluk_data_w));

	// Configuration
	map(0xeff7, 0xeff7).w(FUNC(pentevo_state::pentevo_port_eff7_w));
	map(0x00bf, 0x00bf).select(0xff00).rw(FUNC(pentevo_state::pentevo_port_bf_r), FUNC(pentevo_state::pentevo_port_bf_w));
	map(0x00be, 0x00be).select(0x0f00).r(FUNC(pentevo_state::pentevo_port_0nbd_r));
	map(0x00bd, 0x00bd).select(0x0f00).r(FUNC(pentevo_state::pentevo_port_0nbd_r));
	map(0x10be, 0x10be).select(0x0f00).r(FUNC(pentevo_state::pentevo_port_1nbd_r));
	map(0x10bd, 0x10bd).select(0x0f00).rw(FUNC(pentevo_state::pentevo_port_1nbd_r), FUNC(pentevo_state::pentevo_port_1nbd_w));
	map(0x00be, 0x00be).select(0xff00).lw8(NAME([this](offs_t offset) { m_nmi_active_flip_countdown = 2; }));

	// AY
	map(0x8000, 0x8000).mirror(0x3ffd).w("ay_slot", FUNC(ay_slot_device::data_w));
	map(0xc000, 0xc000).mirror(0x3ffd).rw("ay_slot", FUNC(ay_slot_device::data_r), FUNC(ay_slot_device::address_w));

	// HDD: NEMO
	map(0x0010, 0x0010).select(0xffe0).lrw8(NAME([this](offs_t offset) { return nemo_ata_r(offset >> 5); })
		, NAME([this](offs_t offset, u8 data) { nemo_ata_w(offset >> 5, data); }));
	map(0x0011, 0x0011).mirror(0xff00).lrw8(NAME([this]() { m_ata_data_hi_ready = false; return m_ata_data_latch; })
		, NAME([this](offs_t offset, u8 data) { m_ata_data_hi_ready = true; m_ata_data_latch = data; }));
	map(0x00c8, 0x00c8).mirror(0xff00).lrw8(NAME([this]() { return m_ata->cs1_r(6 /* ? */); })
		, NAME([this](offs_t offset, u8 data) { m_ata->cs1_w(6, data); }));

	// SPI
	map(0x0077, 0x0077).select(0xff00).lr8(NAME([]() { return 0x00; })).w(FUNC(pentevo_state::spi_port_77_w));
	map(0x0057, 0x0057).select(0xff00).rw(FUNC(pentevo_state::spi_port_57_r), FUNC(pentevo_state::spi_port_57_w));

	// Mouse
	map(0xfadf, 0xfadf).lr8(NAME([this]() -> u8 { return 0x80 | (m_io_mouse[2]->read() & 0x07); }));
	map(0xfbdf, 0xfbdf).lr8(NAME([this]() -> u8 { return  m_io_mouse[0]->read(); }));
	map(0xffdf, 0xffdf).lr8(NAME([this]() -> u8 { return ~m_io_mouse[1]->read(); }));
	map(0x001f, 0x001f).mirror(0xff00).lr8(NAME([]() -> u8 { return 0x00; })); // TODO Kepmston Joystick

	map(0x0000, 0xffff).view(m_io_view);
	m_io_view[0];

	// PORTS: Shadow
	m_io_view[1](0x0000, 0xffff).view(m_io_dos_view);
	m_io_dos_view[0](0x001f, 0x001f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::status_r), FUNC(beta_disk_device::command_w));
	m_io_dos_view[0](0x003f, 0x003f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::track_r), FUNC(beta_disk_device::track_w));
	m_io_dos_view[0](0x005f, 0x005f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::sector_r), FUNC(beta_disk_device::sector_w));
	m_io_dos_view[0](0x007f, 0x007f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::data_r), FUNC(beta_disk_device::data_w));
	m_io_dos_view[0](0x00ff, 0x00ff).select(0xff00).r(m_beta, FUNC(beta_disk_device::state_r));

	m_io_view[1](0x00ff, 0x00ff).select(0xff00).w(FUNC(pentevo_state::atm_port_ff_w));
	m_io_view[1](0x0077, 0x0077).select(0xff00).lr8(NAME([]() { return 0xff; })).w(FUNC(pentevo_state::atm_port_77_w));
	m_io_view[1](0x3ff7, 0x3ff7).select(0xc000).w(FUNC(pentevo_state::atm_port_f7_w));      // ATM
	m_io_view[1](0x37f7, 0x37f7).select(0xc000).w(FUNC(pentevo_state::pentevo_port_7f7_w)); // PENTEVO
	m_io_view[1](0x3bf7, 0x3bf7).select(0xc000).w(FUNC(pentevo_state::pentevo_port_bf7_w)); // RO

	// SPI
	m_io_view[1](0x0057, 0x0057).select(0xff00)
		.lw8(NAME([this](offs_t offset, u8 data) { if (BIT(offset, 15)) spi_port_77_w(offset, data); else spi_port_57_w(offset, data); }));

	// Gluk
	m_io_view[1](0xdef7, 0xdef7).lw8(NAME([this](offs_t offset, u8 data) { m_glukrs->address_w(data); } ));
	m_io_view[1](0xbef7, 0xbef7).rw(FUNC(pentevo_state::gluk_data_r), FUNC(pentevo_state::gluk_data_w));

	subdevice<zxbus_device>("zxbus")->set_io_space(m_io_view[0], m_io_view[1]);
}

void pentevo_state::init_mem_write()
{
	address_space &mem = m_maincpu->space(AS_PROGRAM);
	mem.install_write_tap(0x0000, 0xffff, "charrom_w", [this](offs_t offset, u8 &data, u8 mem_mask)
	{
		if (!machine().side_effects_disabled())
		{
			if (BIT(m_port_bf_data, 2))
			{
				m_char_ram->write(offset & 0x7ff, data);
				m_gfxdecode->gfx(0)->mark_dirty((offset & 0x7ff) / 8);
			}
		}
		return data;
	});

	address_space &opc = m_maincpu->space(AS_OPCODES);
	opc.install_read_tap(0x0000, 0xffff, "nmi_exit", [this](offs_t offset, u8 &data, u8 mem_mask)
	{
		if (!machine().side_effects_disabled())
		{
			if (m_nmi_active_flip_countdown)
			{
				if(--m_nmi_active_flip_countdown == 0)
				{
					if (m_nmi_active)
					{
						m_nmi_active = false;
						atm_update_memory();
					}
					else
						nmi_on(); // see: pentevo_port_bf_w()
				}
			}
		}
	});
}

void pentevo_state::machine_start()
{
	atm_state::machine_start();

	save_item(NAME(m_port_bf_data));
	save_item(NAME(m_port_eff7_data));
	save_item(NAME(m_beta_drive_virtual));
	save_item(NAME(m_gluk_ext));
	save_item(NAME(m_ata_data_hi_ready));
	save_item(NAME(m_nmi_trap_offset));
	save_item(NAME(m_nmi_active));
	save_item(NAME(m_nmi_active_flip_countdown));
	save_item(NAME(m_zctl_di));
	save_item(NAME(m_zctl_cs));

	init_mem_write();
}

void pentevo_state::machine_reset()
{
	m_nmi_active = false;
	m_port_eff7_data = 0;
	atm_state::machine_reset();

	m_port_bf_data = 0;
	m_nmi_active_flip_countdown = 0;
	m_beta_drive_virtual = 0;

	m_ata_data_hi_ready = false;
	m_gluk_ext = 0xff;
	m_zctl_cs = 1;
	m_zctl_di = 0xff;

	m_keyboard->write(0xff);
	while (m_keyboard->read() != 0) { /* invalidate buffer */ }
}

void pentevo_state::video_start()
{
	atm_state::video_start();
	m_char_location = m_char_ram->pointer();
	m_gfxdecode->gfx(0)->set_source(m_char_location);
}

INPUT_PORTS_START( pentevo )
	PORT_INCLUDE( spec_plus )

	PORT_START("mouse_input1")
	PORT_BIT(0xff, 0, IPT_MOUSE_X) PORT_SENSITIVITY(30)

	PORT_START("mouse_input2")
	PORT_BIT(0xff, 0, IPT_MOUSE_Y) PORT_SENSITIVITY(30)

	PORT_START("mouse_input3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON4) PORT_NAME("Left mouse button") PORT_CODE(MOUSECODE_BUTTON1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON5) PORT_NAME("Right mouse button") PORT_CODE(MOUSECODE_BUTTON2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON6) PORT_NAME("Middle mouse button") PORT_CODE(MOUSECODE_BUTTON3)
INPUT_PORTS_END

void pentevo_state::pentevo(machine_config &config)
{
	atmtb2(config);
	m_maincpu->set_addrmap(AS_IO, &pentevo_state::pentevo_io);
	m_maincpu->set_vblank_int("screen", FUNC(pentevo_state::pentevo_interrupt));
	TIMER(config, "nmi_timer").configure_periodic(FUNC(pentevo_state::nmi_check_callback), attotime::from_hz(50));

	m_screen->set_raw(X1_128_SINCLAIR / 5, 448, 320, {get_screen_area().left() - 40, get_screen_area().right() + 40, get_screen_area().top() - 40, get_screen_area().bottom() + 40});

	m_ram->set_default_size("4M");
	RAM(config, m_char_ram).set_default_size("2048").set_default_value(0);

	GLUKRS(config, m_glukrs, 32.768_kHz_XTAL);
	SPI_SDCARD(config, m_sdcard, 0);
	m_sdcard->set_prefer_sdhc();
	m_sdcard->spi_miso_callback().set(FUNC(pentevo_state::spi_miso_w));

	SPEAKER(config.replace(), "speakers", 2).front();

	AY_SLOT(config.replace(), "ay_slot", 14_MHz_XTAL / 8, default_ay_slot_devices, "ay_ym2149")
		.add_route(0, "speakers", 0.50, 0)
		.add_route(1, "speakers", 0.25, 0)
		.add_route(1, "speakers", 0.25, 1)
		.add_route(2, "speakers", 0.50, 1);

	AT_KEYB(config, m_keyboard, pc_keyboard_device::KEYBOARD_TYPE::AT, 3);

	zxbus_device &zxbus(ZXBUS(config, "zxbus", 0));
	ZXBUS_SLOT(config, "zxbus1", 0, zxbus, zxbus_cards, nullptr);
}


ROM_START( pentevo )
	ROM_REGION(0x090000, "maincpu", ROMREGION_ERASEFF)
	ROM_DEFAULT_BIOS("v0.60.02")

	// http://svn.zxevo.ru/revision.php?repname=pentevo&path=%2From%2Fzxevo_fe.rom
	ROM_SYSTEM_BIOS(0, "v0.59.02fe", "ERS v0.59.02 (FE), NEO-DOS v0.53")
	ROMX_LOAD( "zxevo_05902fe.rom", 0x010000, 0x80000, CRC(df144c82) SHA1(e48b8a95576e0123764ff8cc34d9373dc95159bf), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v0.59.04", "ERS v0.59.04, NEO-DOS v0.53")
	ROMX_LOAD( "zxevo_05904.rom", 0x010000, 0x80000, CRC(8cae52eb) SHA1(992a0dc17fc7283bbfad5c5ebe254cab68bf0d9a), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v0.59.12", "ERS v0.59.12, NEO-DOS v0.57")
	ROMX_LOAD( "zxevo_05912.rom", 0x010000, 0x80000, CRC(e0e95f9f) SHA1(b27b9d61aaf47a73bdd07027df0aad5b88be0fb9), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v0.59.12fe", "ERS v0.59.12 (FE), NEO-DOS v0.57")
	ROMX_LOAD( "zxevo_05912fe.rom", 0x010000, 0x80000, CRC(4c9300b1) SHA1(b6511f1c24a094930a559f3673b322b24b848a03), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "v0.59.13", "ERS v0.59.13, NEO-DOS v0.58")
	ROMX_LOAD( "zxevo_05913.rom", 0x010000, 0x80000, CRC(b75bf957) SHA1(6880493ee248cad1f82683f8b9cc69fb78fe5682), ROM_BIOS(4))
	ROM_SYSTEM_BIOS(5, "v0.59.13fe", "ERS v0.59.13 (FE), NEO-DOS v0.58")
	ROMX_LOAD( "zxevo_05913fe.rom", 0x010000, 0x80000, CRC(a4de8eb8) SHA1(508667d5ef42a1d0353866f3a1de4e61a230fc86), ROM_BIOS(5))
	ROM_SYSTEM_BIOS(6, "v0.60.02", "ERS v0.60.02, NEO-DOS v0.60")
	ROMX_LOAD( "zxevo_06002.rom", 0x010000, 0x80000, CRC(0c828b6c) SHA1(c70361b98f2d42d4ab60a63139bb1de4eecd6dd1), ROM_BIOS(6))
	ROM_SYSTEM_BIOS(7, "v0.60.02fe", "ERS v0.60.02 (FE), NEO-DOS v0.60")
	ROMX_LOAD( "zxevo_06002fe.rom", 0x010000, 0x80000, CRC(b7ac7a2d) SHA1(5a86046d12d4aad52947caec8550db605b37ca29), ROM_BIOS(7))

	// http://svn.zxevo.ru/revision.php?repname=pentevo&path=%2Fcfgs%2Fstandalone_base_trdemu%2Ftrunk%2Fzxevo_fw.bin&rev=994&peg=1021
	ROM_REGION(0x0C280, "fw", ROMREGION_ERASEFF)
	ROM_LOAD( "zxevo_fw.bin", 0x0000, 0xC280, CRC(aefbd8e5) SHA1(ac9a551ba15eeead76b5527fd5d23d824ae5176f))
ROM_END

} // Anonymous namespace


/*    YEAR  NAME        PARENT   COMPAT MACHINE  INPUT    CLASS          INIT        COMPANY     FULLNAME                  FLAGS */
COMP( 2009, pentevo,    spec128, 0,     pentevo, pentevo, pentevo_state, empty_init, "NedoPC",   "ZX Evolution: BASECONF", 0)



peoplepc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Carl

/*********************************************************************************************
The keyboard has a sticker that proclaims it was made by Fujitsu Limited.

[ASC] [ESC] [F1 ] [F2 ] [F3 ] [F4 ] [F5 ] [F6 ] [F7 ] [F8 ] [F9 ] [F10] [F11] [F11] [PRN]
[mu°] [1 !] [2 "] [3 §] [4 $] [5 %] [6 &] [7 /] [8 (] [9 )] [0 =] [ß ?] [´ `] [BKS] [ C ]   [ 7 ] [ 8 ] [ 9 ] [<- ] [ ->]
[ TAB ]  [ Q ] [ W ] [ E ] [ R ] [ T ] [ Z ] [ U ] [ I ] [ O ] [ P ] [ Ü ] [+ *]  [ DEL ]   [ 4 ] [ 5 ] [ 5 ] [ - ] [HOM]
[SHIFTLOCK] [ A ] [ S ] [ D ] [ F ] [ G ] [ H ] [ J ] [ K ] [ L ] [ Ö ] [ Ä ] [# '] [RET]   [ 1 ] [ 2 ] [ 3 ] [RET] [UP ]
[SHIFT] [2 3]  [ Y ] [ X ] [ C ] [ V ] [ B ] [ N ] [ M ] [, ;] [. :] [- _] [ Ü ]    [URN]   [    0    ] [ . ] [URN] [DWN]
[ CTRL  ] [                    SPACE                     ]

* The ASC key top left has a red LED, "Depressing the ASCII key, [...], switches off the LED indicator. This key acts as a
  toggle between the ASCII character set, and your country's standard (NATIONAL) character set. You would use this feature if
  you wanted to work with another character set, such as French or Spanish."
* the mu° has the Greek lowercase mu and the degree characters
* PRN is PRINT
* BKS is the full word BACKSPACE
* the C to the right of BKS has a red keycap, it deletes the complete line
* the five direction keys are all marked with arrows, HOME being diagonally to the top left
* SHIFTLOCK also has an LED indicator, and is released by pressing one of the SHIFT keys

**********************************************************************************************/

#include "emu.h"
#include "cpu/i86/i86.h"
#include "cpu/mcs48/mcs48.h"
#include "imagedev/floppy.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/i8255.h"
#include "machine/i8251.h"
#include "machine/i8257.h"
#include "machine/upd765.h"
#include "video/mc6845.h"
#include "bus/rs232/rs232.h"
#include "bus/rs232/keyboard.h"
#include "emupal.h"
#include "screen.h"


namespace {

class peoplepc_state : public driver_device
{
public:
	peoplepc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_palette(*this, "palette"),
		m_pic_1(*this, "pic8259_1"),
		m_8251key(*this, "i8251_0"),
		m_8251ser(*this, "i8251_1"),
		m_fdc(*this, "upd765"),
		m_flop0(*this, "upd765:0"),
		m_flop1(*this, "upd765:1"),
		m_dmac(*this, "i8257"),
		m_crtc(*this, "h46505"),
		m_gfxdecode(*this, "gfxdecode"),
		m_gvram(*this, "gvram"),
		m_cvram(*this, "cvram"),
		m_charram(4*1024)
	{ }

	void olypeopl(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	required_device<palette_device> m_palette;
	required_device<pic8259_device> m_pic_1;
	required_device<i8251_device> m_8251key;
	required_device<i8251_device> m_8251ser;
	required_device<upd765a_device> m_fdc;
	required_device<floppy_connector> m_flop0;
	required_device<floppy_connector> m_flop1;
	required_device<i8257_device> m_dmac;
	required_device<hd6845s_device> m_crtc;
	required_device<gfxdecode_device> m_gfxdecode;
	required_shared_ptr<uint16_t> m_gvram;
	required_shared_ptr<uint16_t> m_cvram;
	std::vector<uint8_t> m_charram;

	MC6845_UPDATE_ROW(update_row);
	uint8_t get_slave_ack(offs_t offset);
	void charram_w(offs_t offset, uint16_t data);
	void dmapg_w(uint8_t data);
	void p7c_w(uint8_t data);
	void tc_w(int state);
	void hrq_w(int state);
	uint8_t memory_read_byte(offs_t offset);
	void memory_write_byte(offs_t offset, uint8_t data);
	void floppy_load(floppy_image_device *dev);
	void floppy_unload(floppy_image_device *dev);

	uint8_t m_dma0pg = 0, m_p7c = 0;
	void peoplepc_io(address_map &map) ATTR_COLD;
	void peoplepc_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
};

static const gfx_layout peoplepc_charlayout =
{
	8, 19,                   /* 8 x 19 characters */
	128,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 7, 6, 5, 4, 3, 2, 1, 0},
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8, 16*8, 17*8, 18*8 },
	8*32
};

MC6845_UPDATE_ROW(peoplepc_state::update_row)
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();

	for(int i = 0; i < x_count; i++)
	{
		if(BIT(m_p7c, 1))
		{
			uint16_t data = m_gvram[((((ma / 40) * 16) + ra) * 64) + i];

			for(int j = 15; j >= 0; j--)
				bitmap.pix(y, (i * 16) + j) = palette[BIT(data, j)];
		}
		else
		{
			uint16_t data = m_cvram[(ma + i) & 0x3fff];
			uint8_t chr = m_charram[(data & 0x7f) * 32 + ra];
			if(data & 0x1000)
				chr ^= 0xff;
			if(((data & 0x800) && (ra > 14)) || (i == cursor_x))
				chr = 0xff;
			for(int j = 0; j < 8; j++)
				bitmap.pix(y, (i * 8) + j) = palette[BIT(chr, j)];
		}
	}
}

uint8_t peoplepc_state::get_slave_ack(offs_t offset)
{
	if (offset == 7)
		return m_pic_1->acknowledge();

	return 0x00;
}

void peoplepc_state::charram_w(offs_t offset, uint16_t data)
{
	m_charram[offset] = data;
	m_gfxdecode->gfx(0)->mark_dirty(offset/16);
}

void peoplepc_state::dmapg_w(uint8_t data)
{
	m_dma0pg = data;
}

void peoplepc_state::p7c_w(uint8_t data)
{
	m_p7c = data;
	m_crtc->set_hpixels_per_column(BIT(data, 1) ? 16 : 8);
}

void peoplepc_state::tc_w(int state)
{
	m_fdc->tc_w(state);
}

void peoplepc_state::hrq_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state);
	m_dmac->hlda_w(state);
}

uint8_t peoplepc_state::memory_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	return prog_space.read_byte(offset | (m_dma0pg << 16));
}

void peoplepc_state::memory_write_byte(offs_t offset, uint8_t data)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	prog_space.write_byte(offset | (m_dma0pg << 16), data);
}

void peoplepc_state::floppy_load(floppy_image_device *dev)
{
	dev->mon_w(0);
}

void peoplepc_state::floppy_unload(floppy_image_device *dev)
{
	dev->mon_w(1);
}

void peoplepc_state::machine_reset()
{
	m_flop0->get_device()->mon_w(!m_flop0->get_device()->exists());
	m_flop1->get_device()->mon_w(!m_flop1->get_device()->exists());
}

void peoplepc_state::machine_start()
{
	m_gfxdecode->set_gfx(0, std::make_unique<gfx_element>(m_palette, peoplepc_charlayout, &m_charram[0], 0, 1, 0));
	m_dma0pg = 0;

	// FIXME: cheat as there no docs about how or obvious ports that set to control the motor
	m_flop0->get_device()->setup_load_cb(floppy_image_device::load_cb(&peoplepc_state::floppy_load, this));
	m_flop0->get_device()->setup_unload_cb(floppy_image_device::unload_cb(&peoplepc_state::floppy_unload, this));
	m_flop1->get_device()->setup_load_cb(floppy_image_device::load_cb(&peoplepc_state::floppy_load, this));
	m_flop1->get_device()->setup_unload_cb(floppy_image_device::unload_cb(&peoplepc_state::floppy_unload, this));
}

void peoplepc_state::peoplepc_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x7ffff).ram();
	map(0xc0000, 0xdffff).ram().share("gvram");
	map(0xe0000, 0xe3fff).ram().share("cvram");
	map(0xe4000, 0xe5fff).w(FUNC(peoplepc_state::charram_w));
	map(0xfe000, 0xfffff).rom().region("maincpu", 0);
}

void peoplepc_state::peoplepc_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0014, 0x0017).rw(m_pic_1, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x0018, 0x001b).rw("pic8259_0", FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x0020, 0x0031).rw(m_dmac, FUNC(i8257_device::read), FUNC(i8257_device::write)).umask16(0x00ff);
	map(0x0040, 0x0047).rw("ppi8255", FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
	map(0x0048, 0x004f).rw("pit8253", FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0x0054, 0x0057).rw(m_8251key, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0x005c, 0x005f).rw(m_8251ser, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0x0064, 0x0067).m(m_fdc, FUNC(upd765a_device::map)).umask16(0x00ff);
	map(0x006c, 0x006c).w("h46505", FUNC(mc6845_device::address_w));
	map(0x006e, 0x006e).rw("h46505", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x0070, 0x0070).w(FUNC(peoplepc_state::dmapg_w));
	map(0x007c, 0x007c).w(FUNC(peoplepc_state::p7c_w));
}

static void peoplepc_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

void peoplepc_keyboard_devices(device_slot_interface &device)
{
	device.option_add("keyboard", SERIAL_KEYBOARD);
}

static DEVICE_INPUT_DEFAULTS_START(keyboard)
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_1200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void peoplepc_state::olypeopl(machine_config &config)
{
	/* basic machine hardware */
	I8086(config, m_maincpu, XTAL(14'745'600)/3);
	m_maincpu->set_addrmap(AS_PROGRAM, &peoplepc_state::peoplepc_map);
	m_maincpu->set_addrmap(AS_IO, &peoplepc_state::peoplepc_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_0", FUNC(pic8259_device::inta_cb));

	pit8253_device &pit8253(PIT8253(config, "pit8253"));
	pit8253.set_clk<0>(XTAL(14'745'600)/6);
	pit8253.out_handler<0>().set(m_8251key, FUNC(i8251_device::write_txc));
	pit8253.out_handler<0>().append(m_8251key, FUNC(i8251_device::write_rxc));
	pit8253.out_handler<0>().append("pit8253", FUNC(pit8253_device::write_clk2));
	pit8253.set_clk<1>(XTAL(14'745'600)/6);
	pit8253.out_handler<1>().set(m_8251ser, FUNC(i8251_device::write_txc));
	pit8253.out_handler<1>().append(m_8251ser, FUNC(i8251_device::write_rxc));
	pit8253.out_handler<2>().set("pic8259_0", FUNC(pic8259_device::ir0_w));

	pic8259_device &pic8259_0(PIC8259(config, "pic8259_0"));
	pic8259_0.out_int_callback().set_inputline(m_maincpu, 0);
	pic8259_0.in_sp_callback().set_constant(1);
	pic8259_0.read_slave_ack_callback().set(FUNC(peoplepc_state::get_slave_ack));

	PIC8259(config, m_pic_1);
	m_pic_1->out_int_callback().set("pic8259_0", FUNC(pic8259_device::ir7_w));
	m_pic_1->in_sp_callback().set_constant(0);

	I8255(config, "ppi8255");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_raw(XTAL(22'000'000), 640, 0, 640, 475, 0, 475);
	screen.set_screen_update("h46505", FUNC(mc6845_device::screen_update));

	GFXDECODE(config, m_gfxdecode, m_palette, gfxdecode_device::empty);
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	HD6845S(config, m_crtc, XTAL(22'000'000)/8); // HD46505SP according to User's Guide
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(peoplepc_state::update_row));

	I8257(config, m_dmac, XTAL(14'745'600)/3);
	m_dmac->out_hrq_cb().set(FUNC(peoplepc_state::hrq_w));
	m_dmac->out_tc_cb().set(FUNC(peoplepc_state::tc_w));
	m_dmac->in_memr_cb().set(FUNC(peoplepc_state::memory_read_byte));
	m_dmac->out_memw_cb().set(FUNC(peoplepc_state::memory_write_byte));
	m_dmac->in_ior_cb<0>().set("upd765", FUNC(upd765a_device::dma_r));
	m_dmac->out_iow_cb<0>().set("upd765", FUNC(upd765a_device::dma_w));

	UPD765A(config, m_fdc, XTAL(8'000'000)/2, true, true);
	m_fdc->intrq_wr_callback().set("pic8259_0", FUNC(pic8259_device::ir2_w));
	m_fdc->drq_wr_callback().set(m_dmac, FUNC(i8257_device::dreq0_w));
	FLOPPY_CONNECTOR(config, "upd765:0", peoplepc_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "upd765:1", peoplepc_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);

	I8251(config, m_8251key, XTAL(14'745'600)/6);
	m_8251key->rxrdy_handler().set("pic8259_1", FUNC(pic8259_device::ir1_w));
	m_8251key->txd_handler().set("kbd", FUNC(rs232_port_device::write_txd));

	rs232_port_device &kbd(RS232_PORT(config, "kbd", peoplepc_keyboard_devices, "keyboard"));
	kbd.rxd_handler().set(m_8251key, FUNC(i8251_device::write_rxd));
	kbd.set_option_device_input_defaults("keyboard", DEVICE_INPUT_DEFAULTS_NAME(keyboard));

	I8048(config, "kbdmcu", 4'608'000).set_disable(); // XTAL unknown

	I8251(config, m_8251ser, XTAL(14'745'600)/6);
	m_8251ser->rxrdy_handler().set("pic8259_0", FUNC(pic8259_device::ir5_w));
	m_8251ser->txrdy_handler().set("pic8259_0", FUNC(pic8259_device::ir6_w));
	m_8251ser->txd_handler().set("rs232c", FUNC(rs232_port_device::write_txd));
	m_8251ser->rts_handler().set("rs232c", FUNC(rs232_port_device::write_rts));
	m_8251ser->dtr_handler().set("rs232c", FUNC(rs232_port_device::write_dtr));

	rs232_port_device &rs232c(RS232_PORT(config, "rs232c", default_rs232_devices, nullptr));
	rs232c.rxd_handler().set(m_8251ser, FUNC(i8251_device::write_rxd));
	rs232c.dsr_handler().set(m_8251ser, FUNC(i8251_device::write_dsr));
	rs232c.cts_handler().set(m_8251ser, FUNC(i8251_device::write_cts));
}

ROM_START( olypeopl )
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_SYSTEM_BIOS(0, "hd",  "HD ROM")
	ROMX_LOAD( "u01271c0.bin", 0x00000, 0x1000, CRC(8e0ef114) SHA1(774bab0a3e29853e9f6b951cf73082063ea61e6d), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "u01271d0.bin", 0x00001, 0x1000, CRC(e2419bf9) SHA1(d88381f8709c91e2adba08f378e29bd0d19ee5ae), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "2fd",  "2 FD ROM")
	ROMX_LOAD( "u01277f3.bin", 0x00000, 0x1000, CRC(428ff135) SHA1(ec11f0e43455570c40f5dc4b84f8420da5939368), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "u01277g3.bin", 0x00001, 0x1000, CRC(3295691c) SHA1(7d7ade62117d11656b8dd86cf0703127616d55bc), ROM_SKIP(1) | ROM_BIOS(1))

	ROM_REGION(0x400, "kbdmcu", 0)
	ROM_LOAD( "m1.bin", 0x000, 0x400, NO_DUMP )
ROM_END

} // anonymous namespace


COMP( 1983, olypeopl, 0, 0, olypeopl, 0, peoplepc_state, empty_init, "Olympia International", "People PC", MACHINE_NOT_WORKING|MACHINE_NO_SOUND)



perq.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************************

2017-10-28 Skeleton

Three Rivers / ICL - PERQ 1A. CPU is discrete components including 74S181 bit-slice ALUs, and AM2910 microcode sequencer.
A Z80 handles most of the I/O. Display is 768x1024. B&W (black on white).

ROMS came from PERQemu by Josh Dersch.

************************************************************************************************************************************/

#include "emu.h"


namespace {

class perq_state : public driver_device
{
public:
	perq_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		//      , m_maincpu(*this, "maincpu")
	{ }

	void perq(machine_config &config);
private:
	//  required_device<cpu_device> m_maincpu;
};

static INPUT_PORTS_START( perq )
INPUT_PORTS_END

void perq_state::perq(machine_config &config)
{
}

ROM_START( perq )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "rti02.rom",     0x0000, 0x0200, CRC(cad04f75) SHA1(7a789c6ab1f7627f14953b28231aec0ea49f7945) )
	ROM_LOAD( "t1bootrom.rom", 0x0000, 0x0c00, CRC(cd75f925) SHA1(eb8d5381d87c366052ba1f01f803403e313dc29a) )
	ROM_LOAD( "boot.bin",      0x0000, 0x0d98, CRC(a2b9b7ea) SHA1(75b4b7743e4e65fc14b9f3dbb08421c16cde0f11) )
	ROM_LOAD( "rds00.rom",     0x0000, 0x0400, CRC(77650e0a) SHA1(e507cbc0a1fa56054ce178f7600004d3669961ce) )
	ROM_LOAD( "rsc03.rom",     0x0000, 0x0100, CRC(d66f1f1f) SHA1(5ccccb68dc59dbcabab99adf8a57af0af545bfc5) )
	ROM_LOAD( "rsh00.rom",     0x0000, 0x0400, CRC(815d92bf) SHA1(b87bdea13de391e5615c474ba96af4b28b7f8f38) )
ROM_END

} // anonymous namespace


COMP( 1979, perq, 0, 0, perq, perq, perq_state, empty_init, "Three Rivers Company Corporation", "PERQ 1A", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



pes.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Jonathan Gevaryahu
/******************************************************************************
*
*  Pacific Educational Systems 'PES' Speech box
*  Part number VPU-1 V1
*  By Kevin 'kevtris' Horton and Jonathan Gevaryahu AKA Lord Nightmare
*
*  Pacific Educational Systems, 915 Woodhall Drive, Victoria, Canada.
*
*  RE work done by Kevin Horton and Jonathan Gevaryahu
*
*  DONE:
*  compiles correctly
*  rom loads correctly
*  interface with tms5220 is done
*  rts and cts bits are stored in struct
*  serial is attached to terminal
*
*  TODO:
*  serial receive clear should happen after delay of one cpu cycle, not ASSERT and then CLEAR immediately after
*  figure out how to attach serial to external socket
*
***********************************************************************
This is almost as simple as hardware gets from the digital side:
Hardware consists of:
10.245Mhz xtal
80c31 cpu/mcu
27c64 rom (holds less than 256 bytes of code)
unpopulated 6164 sram, which isn't used
TSP5220C speech chip (aka tms5220c)
mc145406 RS232 driver/receiver (+-24v to 0-5v converter for serial i/o)
74hc573b1 octal tri-state D-latch (part of bus interface for ROM)
74hc74b1 dual d-flipflop with set/reset, positive edge trigger (?)
74hc02b1 quad 2-input NOR gate (wired up to decode address 0, and data 0 and 1 to produce /RS and /WS)
mc14520b dual 4-bit binary up counter (used as a chopper for the analog filter)
Big messy analog section to allow voice output to be filtered extensively by a 4th order filter

Address map:
80C31 ADDR:
  0000-1FFF: ROM
  2000-3FFF: open bus (would be ram)
  4000-ffff: open bus
80C31 IO:
  00 W: d0 and d1 are the /RS and /WS bits
  port 1.x: tms5220 bus
  port 2.x: unused
  port 3.0: RxD serial receive
  port 3.1: TxD serial send
  port 3.2: read, from /INT on tms5220c
  port 3.3: read, from /READY on tms5220c
  port 3.4: read, from the serial RTS line
  port 3.5: read/write, to the serial CTS line, inverted (effectively /CTS)
  port 3.6: write, /WR (general) and /WE (pin 27) for unpopulated 6164 SRAM
  port 3.7: write, /RD (general) and /OE (pin 22) for unpopulated 6164 SRAM


Current status:
- bios 0 : working. Hold down various letters for fragments of sound. g makes a beep. Status is reflected on the screen (eg space,@,`).
- bios 1 : working as above, however it does not get the tms5220 status and it does not write to the screen.

*/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "sound/tms5220.h"
#include "bus/rs232/terminal.h"
#include "bus/rs232/rs232.h"
#include "speaker.h"


namespace {

class pes_state : public driver_device
{
public:
	pes_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_serial(*this, "serial")
		, m_speech(*this, "tms5220")
	{ }

	void pes(machine_config &config);

private:

	u8 m_port3 = 0U;
	virtual void machine_reset() override ATTR_COLD;
	void port3_w(u8 data);
	u8 port3_r();
	void rx_w(int state);
	void io_map(address_map &map) ATTR_COLD;
	void prg_map(address_map &map) ATTR_COLD;
	required_device<i80c31_device> m_maincpu;
	required_device<rs232_port_device> m_serial;
	required_device<tms5220_device> m_speech;
};



/* Port Handlers */
void pes_state::port3_w(u8 data)
{
	// preserve RXD
	m_port3 = (m_port3 & 0x01) | (data & ~0x01);
#if 0
	logerror("port3 write: control data written: %02X; ", data);
	logerror("RXD: %d; ", BIT(data,0));
	logerror("TXD: %d; ", BIT(data,1));
	logerror("/INT: %d; ", BIT(data,2));
	logerror("/RDY: %d; ", BIT(data,3));
	logerror("RTS: %d; ", BIT(data,4));
	logerror("CTS: %d; ", BIT(data,5));
	logerror("WR: %d; ", BIT(data,6));
	logerror("RD: %d;\n", BIT(data,7));
#endif
	m_serial->write_txd(BIT(data, 1));
}

u8 pes_state::port3_r()
{
	uint8_t data = m_port3 & 0xE3; // return last written state with rts, /rdy and /int masked out
	// check rts state; if virtual fifo is nonzero, rts is set, otherwise it is cleared
	//data |= 0x10; // set RTS bit
	data |= (m_speech->intq_r()<<2);
	data |= (m_speech->readyq_r()<<3);
	return data;
}

void pes_state::rx_w(int state)
{
	if (state)
		m_port3 |= 1;
	else
		m_port3 &= ~1;
}


/* Reset */
void pes_state::machine_reset()
{
	m_port3 = 0; // reset the openbus state of port 3
	m_speech->reset(); // reset the 5220
}


/******************************************************************************
 Address Maps
******************************************************************************/

void pes_state::prg_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom(); /* 27C64 ROM */
	// map(0x2000, 0x3fff).ram(); /* 6164 8k SRAM, not populated */
}

void pes_state::io_map(address_map &map)
{
	map(0x0000, 0x0000).w(m_speech, FUNC(tms5220_device::combined_rsq_wsq_w)); // /WS(0) and /RS(1)
}

/******************************************************************************
 Input Ports
******************************************************************************/
static INPUT_PORTS_START( pes )
INPUT_PORTS_END

/******************************************************************************
 Machine Drivers
******************************************************************************/

static void serial_devices(device_slot_interface &device)
{
	device.option_add("terminal", SERIAL_TERMINAL);
}

void pes_state::pes(machine_config &config)
{
	/* basic machine hardware */
	I80C31(config, m_maincpu, XTAL(10'245'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &pes_state::prg_map);
	m_maincpu->set_addrmap(AS_IO, &pes_state::io_map);
	m_maincpu->port_in_cb<1>().set(m_speech, FUNC(tms5220_device::status_r));
	m_maincpu->port_out_cb<1>().set(m_speech, FUNC(tms5220_device::data_w));
	m_maincpu->port_in_cb<3>().set(FUNC(pes_state::port3_r));
	m_maincpu->port_out_cb<3>().set(FUNC(pes_state::port3_w));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	TMS5220C(config, m_speech, 720000); /* 720Khz clock, 9khz sample-rate, adjustable with 10-turn trimpot */
	m_speech->add_route(ALL_OUTPUTS, "mono", 1.0);

	RS232_PORT(config, m_serial, serial_devices, "terminal");
	m_serial->rxd_handler().set(FUNC(pes_state::rx_w));
}

/******************************************************************************
 ROM Definitions
******************************************************************************/
ROM_START( pes )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_DEFAULT_BIOS("kevbios")
	ROM_SYSTEM_BIOS( 0, "orig", "PES box with original firmware v2.5")
	ROMX_LOAD( "vpu_2-5.bin",   0x0000, 0x2000, CRC(b27cfdf7) SHA1(c52acf9c080823de5ef26ac55abe168ad53a7d38), ROM_BIOS(0)) // original firmware, rather buggy, 4800bps serial, buggy RTS/CTS flow control, no buffer
	ROM_SYSTEM_BIOS( 1, "kevbios", "PES box with kevtris' rewritten firmware")
	ROMX_LOAD( "pes.bin",   0x0000, 0x2000, CRC(22c1c4ec) SHA1(042e139cd0cf6ffafcd88904f1636c6fa1b38f25), ROM_BIOS(1)) // rewritten firmware by kevtris, 4800bps serial, RTS/CTS plus XON/XOFF flow control, 64 byte buffer
ROM_END

} // anonymous namespace


/******************************************************************************
 Drivers
******************************************************************************/

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY                        FULLNAME             FLAGS
COMP( 1987, pes,  0,      0,      pes,     pes,   pes_state, empty_init, "Pacific Educational Systems", "VPU-01 Speech box", 0 )



pet.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

PET = Personal Electronic Transactor


http://www.6502.org/users/andre/petindex/boards.html

Static Board (PET 2001)
-----------------------

Four variations based on type of RAM(6550 or 2114) and ROM(6540 or 2316B).
4K or 8K static RAM (selected by jumper).
40 column display
A video interrupt interferes with disk drive operation.
Display timing not compatible with Basic 4.0.
ROM sockets:  A2  2K character      ROM sockets:  A2  2K character
 (2316B)      H1  C000-CFFF           (6540)       H1  C000-C7FF
              H2  D000-DFFF                        H2  D000-D7FF
              H3  E000-E7FF                        H3  E000-E7FF
              H4  F000-FFFF                        H4  F000-F7FF
              H5  C000-CFFF                        H5  C800-CFFF
              H6  D000-DFFF                        H6  D800-DFFF
              H7  F000-FFFF                        H7  F800-FFFF


           IEEE user tape #2
     +------####-####--##-+
     !                    #
     !                    #
     !                    # exp
     !                    # bus
     !                    #
     !                    #    2000 Series
     !                    !       circa 1977/78  Max RAM - 8k
     !       (2k) ROMS    !       [w/daughter board exp to 32k shown]
     !      F F E D D C C !
     !      8 0 0 8 0 8 0 !
     !                    !
tape #       RAM MEMORY   !
 #1  #                    !
     +--------------------+


Dynamic Board (PET/CBM 2001-N/2001-B/4000)
------------------------------------------

4K, 8K, 16K or 32K dynamic RAM (selected by jumper).
40 column display
Can run all versions of 40 column Basic (Basic 1 must be copied to 4K ROMs)
Can be jumpered to replace the older board.
ROM sockets:  UD3   9000-9FFF
              UD4   A000-AFFF
              UD5   B000-BFFF
              UD6   C000-CFFF
              UD7   D000-DFFF
              UD8   E000-E7FF
              UD9   F000-FFFF
              UF10  2K character


            IEEE user tape #1
     +------####-####--##-+
     !                   #!
     !                   #!
     !                   #! exp
     !        ROMS       #! bus
     !    F E D C B A 9  #!
     !                   #!    3000, 4000 Series
     !                    !       (3000 series is European version)
     !                    !       circa 1979/80  Max RAM - 32k
     !                    !
     !                    !
     !                    !
tape #      RAM MEMORY    !
 #2  #                    !
     +--------------------+


80 Column Board (CBM 8000)
--------------------------

16K or 32K RAM (selected by jumper).
Uses CTRC to generate 80 column display.
Can only run the 80 column version of Basic 4.0.
Not compatible with older boards.
ROM sockets:  UA3   2K or 4K character
              UD6   F000-FFFF
              UD7   E000-E7FF
              UD8   D000-DFFF
              UD9   C000-CFFF
              UD10  B000-BFFF
              UD11  A000-AFFF
              UD12  9000-9FFF

The layout is the same of the one used in Universal Boards below.


Universal Board (CBM 8000/PET 4000-12)
--------------------------------------

This is an 80 column board with jumpers for different configurations.
16K or 32K RAM (selected by jumper).
Uses CTRC to generate 40 or 80 column display (selected by jumpers).
Can only run Basic 4.0 versions that support the CRTC.
Can be jumpered to replace all older boards.
ROM sockets:  UA3   2K or 4K character
              UD6   F000-FFFF
              UD7   E000-E7FF
              UD8   D000-DFFF
              UD9   C000-CFFF
              UD10  B000-BFFF
              UD11  A000-AFFF
              UD12  9000-9FFF


           IEEE user tape #1
     +------####-####--##-+
     !                  # # tape
     !                  # #  #2
     !  R       exp bus # !
     !  A                #!
     !  M             9  #!
     !                A  #!     4000, 8000 Series
     !  M          R  B   !        circa 1981     Max RAM - 32k*
     !  E          O  C   !       [8296 layout not shown]
     !  M          M  D   !
     !  O          S  E   !
     !  R             F   !
     !  Y                 !
     !                spkr!
     +--------------------+
*/

/*

    TODO:

    - accurate video timing for non-CRTC models
    - High Speed Graphics board
    - keyboard layouts
        - Swedish
        - German
    - SuperPET
        - 6809
        - OS/9 MMU
    - 8296
        - PLA dumps
        - high resolution graphics
        - Malvern Particle Sizer OEM variant

*/

#include "emu.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "bus/ieee488/c2040.h"
#include "bus/ieee488/c8050.h"
#include "bus/ieee488/ieee488.h"
#include "bus/pet/cass.h"
#include "bus/pet/exp.h"
#include "bus/pet/user.h"
#include "cpu/m6502/m6502.h"
#include "imagedev/snapquik.h"
#include "machine/6522via.h"
#include "machine/6821pia.h"
#include "cbm_snqk.h"
#include "machine/input_merger.h"
#include "machine/pla.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "sound/spkrdev.h"
#include "video/mc6845.h"


namespace {

#define M6502_TAG       "f3"
#define M6522_TAG       "a5"
#define M6520_1_TAG     "g8"
#define M6520_2_TAG     "b8"
#define MC6845_TAG      "ub13"
#define SCREEN_TAG      "screen"
#define PLA1_TAG        "ue6"
#define PLA2_TAG        "ue5"
#define PET_USER_PORT_TAG "user"

class pet_state : public driver_device
{
public:
	pet_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, M6502_TAG),
		m_via(*this, M6522_TAG),
		m_pia1(*this, M6520_1_TAG),
		m_pia2(*this, M6520_2_TAG),
		m_crtc(*this, MC6845_TAG),
		m_screen(*this, SCREEN_TAG),
		m_ieee(*this, IEEE488_TAG),
		m_palette(*this, "palette"),
		m_cassette(*this, PET_DATASSETTE_PORT_TAG),
		m_cassette2(*this, PET_DATASSETTE_PORT2_TAG),
		m_exp(*this, "exp"),
		m_user(*this, PET_USER_PORT_TAG),
		m_speaker(*this, "speaker"),
		m_cart_9000(*this, "cart_9000"),
		m_cart_a000(*this, "cart_a000"),
		m_cart_b000(*this, "cart_b000"),
		m_ram(*this, RAM_TAG),
		m_rom(*this, M6502_TAG),
		m_char_rom(*this, "charom"),
		m_video_ram(*this, "video_ram", 0x800, ENDIANNESS_LITTLE),
		m_row(*this, "ROW%u", 0),
		m_lock(*this, "LOCK"),
		m_sync_timer(nullptr),
		m_sync_period(attotime::zero),
		m_key(0),
		m_sync(0),
		m_graphic(0),
		m_blanktv(0),
		m_user_diag(1)
	{ }

	void base_pet_devices(machine_config &config, const char *default_drive);
	void _4k(machine_config &config);
	void _8k(machine_config &config);
	void _16k(machine_config &config);
	void _32k(machine_config &config);
	void pet(machine_config &config);
	void pet2001n(machine_config &config, bool with_b000 = true);
	void pet2001n8(machine_config &config);
	void cbm3032(machine_config &config);
	void pet20018(machine_config &config);
	void pet2001n16(machine_config &config);
	void cbm3016(machine_config &config);
	void cbm3000(machine_config &config);
	void cbm4000(machine_config &config);
	void cbm4032f(machine_config &config);
	void cbm4032(machine_config &config);
	void cbm4016(machine_config &config);
	void cbm3008(machine_config &config);
	void pet2001(machine_config &config);
	void pet2001n32(machine_config &config);

	uint8_t read(offs_t offset);
	void write(offs_t offset, uint8_t data);

	void via_pa_w(uint8_t data);
	uint8_t via_pb_r();
	void via_pb_w(uint8_t data);
	void via_ca2_w(int state);
	void via_cb2_w(int state);

	uint8_t pia1_pa_r();
	uint8_t pia1_pb_r();
	void pia1_pa_w(uint8_t data);
	void pia1_ca2_w(int state);

	void user_diag_w(int state);

	MC6845_BEGIN_UPDATE( pet_begin_update );
	MC6845_UPDATE_ROW( pet40_update_row );

	TIMER_CALLBACK_MEMBER( sync_tick );

	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_pet);

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	DECLARE_MACHINE_START( pet40 );
	DECLARE_MACHINE_RESET( pet40 );

	void pet2001_mem(address_map &map) ATTR_COLD;

protected:
	required_device<m6502_device> m_maincpu;
	required_device<via6522_device> m_via;
	required_device<pia6821_device> m_pia1;
	required_device<pia6821_device> m_pia2;
	optional_device<mc6845_device> m_crtc;
	required_device<screen_device> m_screen;
	required_device<ieee488_device> m_ieee;
	required_device<palette_device> m_palette;
	required_device<pet_datassette_port_device> m_cassette;
	required_device<pet_datassette_port_device> m_cassette2;
	required_device<pet_expansion_slot_device> m_exp;
	required_device<pet_user_port_device> m_user;
	optional_device<speaker_sound_device> m_speaker;
	optional_device<generic_slot_device> m_cart_9000;
	optional_device<generic_slot_device> m_cart_a000;
	optional_device<generic_slot_device> m_cart_b000;
	required_device<ram_device> m_ram;
	required_memory_region m_rom;
	required_memory_region m_char_rom;
	memory_share_creator<uint8_t> m_video_ram;
	required_ioport_array<10> m_row;
	required_ioport m_lock;

	emu_timer *m_sync_timer;
	attotime m_sync_period;

	DECLARE_MACHINE_START( pet );
	DECLARE_MACHINE_START( pet2001 );
	DECLARE_MACHINE_RESET( pet );

	void update_speaker();

	enum
	{
		SEL0 = 0,
		SEL1,
		SEL2,
		SEL3,
		SEL4,
		SEL5,
		SEL6,
		SEL7,
		SEL8,
		SEL9,
		SELA,
		SELB,
		SELC,
		SELD,
		SELE,
		SELF
	};

	// keyboard state
	uint8_t m_key;

	// video state
	int m_sync;
	int m_graphic;
	int m_blanktv;
	int m_video_ram_size;

	// sound state
	int m_via_cb2;
	int m_pia1_pa7;

	uint8_t m_via_pa;

	int m_user_diag;
};


class pet2001b_state : public pet_state
{
public:
	pet2001b_state(const machine_config &mconfig, device_type type, const char *tag) :
		pet_state(mconfig, type, tag)
	{ }

	void pet2001b(machine_config &config, bool with_b000 = true);
	void pet2001b32(machine_config &config);
	void pet4000(machine_config &config);
	void pet4000b(machine_config &config);
	void pet4001b(machine_config &config);
	void pet4032f(machine_config &config);
	void pet4032b(machine_config &config);
	void pet4032(machine_config &config);
	void pet2001b16(machine_config &config);
	void cbm3032b(machine_config &config);
	void cbm4000b(machine_config &config);
	void cbm4032b(machine_config &config);
	void pet2001b8(machine_config &config);
	void pet4016(machine_config &config);

protected:
	uint8_t pia1_pb_r();

};


class pet80_state : public pet2001b_state
{
public:
	pet80_state(const machine_config &mconfig, device_type type, const char *tag) :
		pet2001b_state(mconfig, type, tag)
	{ }

	void pet80(machine_config &config);
	void pet8032(machine_config &config);

	DECLARE_MACHINE_START( pet80 );
	DECLARE_MACHINE_RESET( pet80 );

	MC6845_UPDATE_ROW( cbm8296_update_row );

protected:
	MC6845_UPDATE_ROW( pet80_update_row );
};


class superpet_state : public pet80_state
{
public:
	superpet_state(const machine_config &mconfig, device_type type, const char *tag)
		: pet80_state(mconfig, type, tag)
	{ }
	void superpet(machine_config &config);
};


class cbm8096_state : public pet80_state
{
public:
	cbm8096_state(const machine_config &mconfig, device_type type, const char *tag) :
		pet80_state(mconfig, type, tag)
	{ }
	void cbm8096(machine_config &config);
};


class cbm8296_state : public pet80_state
{
public:
	cbm8296_state(const machine_config &mconfig, device_type type, const char *tag) :
		pet80_state(mconfig, type, tag),
		m_basic_rom(*this, "basic"),
		m_editor_rom(*this, "editor"),
		m_ue5_rom(*this, "ue5_eprom"),
		m_ue6_rom(*this, "ue6_eprom"),
		m_pla1(*this, PLA1_TAG),
		m_pla2(*this, PLA2_TAG)
	{ }

	void cbm8296d(machine_config &config);
	void cbm8296(machine_config &config);

private:
	required_memory_region m_basic_rom;
	required_memory_region m_editor_rom;
	required_memory_region m_ue5_rom;
	required_memory_region m_ue6_rom;
	required_device<pla_device> m_pla1;
	required_device<pla_device> m_pla2;

	DECLARE_MACHINE_START( cbm8296 );
	DECLARE_MACHINE_RESET( cbm8296 );

	[[maybe_unused]] void read_pla1(offs_t offset, int phi2, int brw, int noscreen, int noio, int ramsela, int ramsel9, int ramon, int norom,
						int &cswff, int &cs9, int &csa, int &csio, int &cse, int &cskb, int &fa12, int &casena1);
	[[maybe_unused]] void read_pla2(offs_t offset, int phi2, int brw, int casena1, int &endra, int &noscreen, int &casena2, int &fa15);

	void read_pla1_eprom(offs_t offset, int phi2, int brw, int noscreen, int noio, int ramsela, int ramsel9, int ramon, int norom,
		int &cswff, int &cs9, int &csa, int &csio, int &cse, int &cskb, int &fa12, int &casena1);
	void read_pla2_eprom(offs_t offset, int phi2, int brw, int casena1, int &endra, int &noscreen, int &casena2, int &fa15);

	uint8_t read(offs_t offset);
	void write(offs_t offset, uint8_t data);

	uint8_t m_cr;
	void cbm8296_mem(address_map &map) ATTR_COLD;
};


static void cbm_pet_quick_sethiaddress( address_space &space, uint16_t hiaddress )
{
	space.write_byte(0x2e, hiaddress & 0xff);
	space.write_byte(0x2c, hiaddress & 0xff);
	space.write_byte(0x2a, hiaddress & 0xff);
	space.write_byte(0x2f, hiaddress >> 8);
	space.write_byte(0x2d, hiaddress >> 8);
	space.write_byte(0x2b, hiaddress >> 8);
}

QUICKLOAD_LOAD_MEMBER(pet_state::quickload_pet)
{
	return general_cbm_loadsnap(image, m_maincpu->space(AS_PROGRAM), 0, cbm_pet_quick_sethiaddress);
}



//**************************************************************************
//  INTERRUPTS
//**************************************************************************

//-------------------------------------------------
//  update_speaker -
//-------------------------------------------------

void pet_state::update_speaker()
{
	if (m_speaker)
	{
		int level = m_via_cb2 && m_pia1_pa7;

		m_speaker->level_w(level);
	}
}


//-------------------------------------------------
//  read -
//-------------------------------------------------

uint8_t pet_state::read(offs_t offset)
{
	int sel = offset >> 12;
	int norom = m_exp->norom_r(offset, sel);
	uint8_t data = 0;

	data = m_exp->read(offset, data, sel);

	switch (sel)
	{
	case SEL0: case SEL1: case SEL2: case SEL3: case SEL4: case SEL5: case SEL6: case SEL7:
		if (offset < m_ram->size())
		{
			data = m_ram->pointer()[offset];
		}
		break;

	case SEL8:
		if (!(offset & 0x800))
		{
			data = m_video_ram[offset & (m_video_ram_size - 1)];
		}
		break;

	case SEL9:
		if (norom)
		{
			if (m_cart_9000 && m_cart_9000->exists())
				data = m_cart_9000->read_rom(offset & 0xfff);
			else
				data = m_rom->base()[offset - 0x9000];
		}
		break;

	case SELA:
		if (norom)
		{
			if (m_cart_a000 && m_cart_a000->exists())
				data = m_cart_a000->read_rom(offset & 0xfff);
			else
				data = m_rom->base()[offset - 0x9000];
		}
		break;

	case SELB:
		if (norom)
		{
			if (m_cart_b000 && m_cart_b000->exists())
				data = m_cart_b000->read_rom(offset & 0xfff);
			else
				data = m_rom->base()[offset - 0x9000];
		}
		break;

	case SELC: case SELD: case SELF:
		if (norom)
		{
			data = m_rom->base()[offset - 0x9000];
		}
		break;

	case SELE:
		if (BIT(offset, 11))
		{
			data = 0xff;

			if (BIT(offset, 4))
			{
				data &= m_pia1->read(offset & 0x03);
			}
			if (BIT(offset, 5))
			{
				data &= m_pia2->read(offset & 0x03);
			}
			if (BIT(offset, 6))
			{
				data &= m_via->read(offset & 0x0f);
			}
			if (m_crtc && BIT(offset, 7) && BIT(offset, 0))
			{
				data &= m_crtc->register_r();
			}
		}
		else if (norom)
		{
			data = m_rom->base()[offset - 0x9000];
		}
		break;
	}

	return data;
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

void pet_state::write(offs_t offset, uint8_t data)
{
	int sel = offset >> 12;

	m_exp->write(offset, data, sel);

	switch (sel)
	{
	case SEL0: case SEL1: case SEL2: case SEL3: case SEL4: case SEL5: case SEL6: case SEL7:
		if (offset < m_ram->size())
		{
			m_ram->pointer()[offset] = data;
		}
		break;

	case SEL8:
		if (!(offset & 0x800))
		{
			m_video_ram[offset & (m_video_ram_size - 1)] = data;
		}
		break;

	case SELE:
		if (BIT(offset, 11))
		{
			if (BIT(offset, 4))
			{
				m_pia1->write(offset & 0x03, data);
			}
			if (BIT(offset, 5))
			{
				m_pia2->write(offset & 0x03, data);
			}
			if (BIT(offset, 6))
			{
				m_via->write(offset & 0x0f, data);
			}
			if (m_crtc && BIT(offset, 7))
			{
				if (BIT(offset, 0))
				{
					m_crtc->register_w(data);
				}
				else
				{
					m_crtc->address_w(data);
				}
			}
		}
		break;
	}
}

//-------------------------------------------------
//  read_pla1 -
//-------------------------------------------------

void cbm8296_state::read_pla1(offs_t offset, int phi2, int brw, int noscreen, int noio, int ramsela, int ramsel9, int ramon, int norom,
	int &cswff, int &cs9, int &csa, int &csio, int &cse, int &cskb, int &fa12, int &casena1)
{
	uint32_t input = (offset & 0xff00) | phi2 << 7 | brw << 6 | noscreen << 5 | noio << 4 | ramsela << 3 | ramsel9 << 2 | ramon << 1 | norom;
	uint32_t data = m_pla1->read(input);

	cswff = BIT(data, 0);
	cs9 = BIT(data, 1);
	csa = BIT(data, 2);
	csio = BIT(data, 3);
	cse = BIT(data, 4);
	cskb = BIT(data, 5);
	fa12 = BIT(data, 6);
	casena1 = BIT(data, 7);
}

void cbm8296_state::read_pla1_eprom(offs_t offset, int phi2, int brw, int noscreen, int noio, int ramsela, int ramsel9, int ramon, int norom,
	int &cswff, int &cs9, int &csa, int &csio, int &cse, int &cskb, int &fa12, int &casena1)
{
	// PLA-EPROM adapter by Joachim Nemetz (Jogi)

	uint32_t input = (offset & 0xff00) | phi2 << 7 | brw << 6 | noscreen << 5 | noio << 4 | ramsela << 3 | ramsel9 << 2 | ramon << 1 | norom;
	input = bitswap<16>(input,13,8,9,7,12,14,11,10,6,5,4,3,2,1,0,15);

	uint8_t data = m_ue6_rom->base()[input];
	data = bitswap<8>(data,7,0,1,2,3,4,5,6);

	cswff = BIT(data, 0);
	cs9 = BIT(data, 1);
	csa = BIT(data, 2);
	csio = BIT(data, 3);
	cse = BIT(data, 4);
	cskb = BIT(data, 5);
	fa12 = BIT(data, 6);
	casena1 = BIT(data, 7);
}


//-------------------------------------------------
//  read_pla2 -
//-------------------------------------------------

void cbm8296_state::read_pla2(offs_t offset, int phi2, int brw, int casena1, int &endra, int &noscreen, int &casena2, int &fa15)
{
	uint32_t input = bitswap<8>(m_cr, 0,1,2,3,4,5,6,7) << 8 | ((offset >> 8) & 0xf8) | brw << 2 | phi2 << 1 | casena1;
	uint32_t data = m_pla2->read(input);

	endra = BIT(data, 4);
	noscreen = BIT(data, 5);
	casena2 = BIT(data, 6);
	fa15 = BIT(data, 7);
}

void cbm8296_state::read_pla2_eprom(offs_t offset, int phi2, int brw, int casena1, int &endra, int &noscreen, int &casena2, int &fa15)
{
	// PLA-EPROM adapter by Joachim Nemetz (Jogi)

	uint32_t input = bitswap<8>(m_cr, 0,1,2,3,4,5,6,7) << 8 | ((offset >> 8) & 0xf8) | brw << 2 | phi2 << 1 | casena1;
	input = bitswap<16>(input,13,8,9,7,12,14,11,10,6,5,4,3,2,1,0,15);

	uint8_t data = m_ue5_rom->base()[input];
	data = bitswap<8>(data,7,0,1,2,3,4,5,6);

	endra = BIT(data, 4);
	noscreen = BIT(data, 5);
	casena2 = BIT(data, 6);
	fa15 = BIT(data, 7);
}


//-------------------------------------------------
//  read -
//-------------------------------------------------

uint8_t cbm8296_state::read(offs_t offset)
{
	int norom = m_exp->norom_r(offset, offset >> 12) && !BIT(m_cr, 7);
	int phi2 = 1, brw = 1, noscreen = 1, noio = BIT(m_cr, 6);
	int ramsela = BIT(m_via_pa, 0), ramsel9 = BIT(m_via_pa, 1), ramon = BIT(m_via_pa, 2);
	int cswff = 1, cs9 = 1, csa = 1, csio = 1, cse = 1, cskb = 1, fa12 = 1, fa15 = 1, casena1 = 1, casena2 = 1, endra = 1;

	read_pla1_eprom(offset, phi2, brw, noscreen, noio, ramsela, ramsel9, ramon, norom,
		cswff, cs9, csa, csio, cse, cskb, fa12, casena1);

	read_pla2_eprom(offset, phi2, brw, casena1, endra, noscreen, casena2, fa15);

	read_pla1_eprom(offset, phi2, brw, noscreen, noio, ramsela, ramsel9, ramon, norom,
		cswff, cs9, csa, csio, cse, cskb, fa12, casena1);

	//logerror("%s read  %04x : norom %u noio %u ramsela %u ramsel9 %u ramon %u / cswff %u cs9 %u csa %u csio %u cse %u cskb %u fa12 %u casena1 %u endra %u noscreen %u casena2 %u fa15 %u\n",machine().describe_context(),offset,norom,noio,ramsela,ramsel9,ramon,cswff,cs9,csa,csio,cse,cskb,fa12,casena1,endra,noscreen,casena2,fa15);

	uint8_t data = 0;

	offs_t drma = fa15 << 15 | (offset & 0x7e00) | BIT(offset, 0) << 8 | (offset & 0x1fe) >> 1;

	if (!endra && !casena1)
	{
		data = m_ram->pointer()[drma];
	}
	if (casena2)
	{
		data = m_ram->pointer()[0x10000 | drma];
	}
	if (!cs9)
	{
		if (m_cart_9000 && m_cart_9000->exists())
			data = m_cart_9000->read_rom(offset & 0xfff);
		else
			data = m_rom->base()[offset & 0xfff];
	}
	if (!csa)
	{
		if (m_cart_a000 && m_cart_a000->exists())
			data = m_cart_a000->read_rom(offset & 0xfff);
		else
			data = m_rom->base()[0x1000 | (offset & 0xfff)];
	}
	if (!cse)
	{
		data = m_editor_rom->base()[offset & 0xfff];
	}
	if (!cskb)
	{
		data = m_basic_rom->base()[(offset & 0x2fff) | fa12 << 12];
	}
	if (!csio)
	{
		data = 0xff;

		if (BIT(offset, 4))
		{
			data &= m_pia1->read(offset & 0x03);
		}
		if (BIT(offset, 5))
		{
			data &= m_pia2->read(offset & 0x03);
		}
		if (BIT(offset, 6))
		{
			data &= m_via->read(offset & 0x0f);
		}
		if (BIT(offset, 7) && BIT(offset, 0))
		{
			data &= m_crtc->register_r();
		}
	}

	return data;
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

void cbm8296_state::write(offs_t offset, uint8_t data)
{
	int norom = m_exp->norom_r(offset, offset >> 12) && !BIT(m_cr, 7);
	int phi2 = 1, brw = 0, noscreen = 1, noio = BIT(m_cr, 6);
	int ramsela = BIT(m_via_pa, 0), ramsel9 = BIT(m_via_pa, 1), ramon = BIT(m_via_pa, 2);
	int cswff = 1, cs9 = 1, csa = 1, csio = 1, cse = 1, cskb = 1, fa12 = 1, fa15 = 1, casena1 = 1, casena2 = 1, endra = 1;

	read_pla1_eprom(offset, phi2, brw, noscreen, noio, ramsela, ramsel9, ramon, norom,
		cswff, cs9, csa, csio, cse, cskb, fa12, casena1);

	read_pla2_eprom(offset, phi2, brw, casena1, endra, noscreen, casena2, fa15);

	read_pla1_eprom(offset, phi2, brw, noscreen, noio, ramsela, ramsel9, ramon, norom,
		cswff, cs9, csa, csio, cse, cskb, fa12, casena1);

	//logerror("%s write %04x : norom %u noio %u ramsela %u ramsel9 %u ramon %u / cswff %u cs9 %u csa %u csio %u cse %u cskb %u fa12 %u casena1 %u endra %u noscreen %u casena2 %u fa15 %u\n",machine().describe_context(),offset,norom,noio,ramsela,ramsel9,ramon,cswff,cs9,csa,csio,cse,cskb,fa12,casena1,endra,noscreen,casena2,fa15);

	offs_t drma = fa15 << 15 | (offset & 0x7e00) | BIT(offset, 0) << 8 | (offset & 0x1fe) >> 1;

	if (!endra && !casena1)
	{
		m_ram->pointer()[drma] = data;
	}
	if (casena2)
	{
		m_ram->pointer()[0x10000 | drma] = data;
	}
	if (!csio)
	{
		if (BIT(offset, 4))
		{
			m_pia1->write(offset & 0x03, data);
		}
		if (BIT(offset, 5))
		{
			m_pia2->write(offset & 0x03, data);
		}
		if (BIT(offset, 6))
		{
			m_via->write(offset & 0x0f, data);
		}
		if (BIT(offset, 7))
		{
			if (BIT(offset, 0))
			{
				m_crtc->register_w(data);
			}
			else
			{
				m_crtc->address_w(data);
			}
		}
	}
	if (cswff && ((offset & 0xff) == 0xf0))
	{
		m_cr = data;
	}
}



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void pet_state::pet2001_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(pet_state::read), FUNC(pet_state::write));
}


void cbm8296_state::cbm8296_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(cbm8296_state::read), FUNC(cbm8296_state::write));
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

static INPUT_PORTS_START( pet )
	PORT_START( "ROW0" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Crsr Right Left") PORT_CODE(KEYCODE_PGDN) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Home  Clr Screen") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\xE2\x86\x90") PORT_CODE(KEYCODE_MINUS) PORT_CHAR(0x2190)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9)          PORT_CHAR('(')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7)          PORT_CHAR('&')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5)          PORT_CHAR('%')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)          PORT_CHAR('#')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1)          PORT_CHAR('!')

	PORT_START( "ROW1" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Del  Inst") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Crsr Down Up") PORT_CODE(KEYCODE_PGUP) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0)          PORT_CHAR(')')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8)          PORT_CHAR('\\')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6)          PORT_CHAR('\'')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)          PORT_CHAR('$')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2)          PORT_CHAR('"')

	PORT_START( "ROW2" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9_PAD)      PORT_CHAR('9')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7_PAD)      PORT_CHAR('7')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\xE2\x86\x91 Pi") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('^') PORT_CHAR(0x03C0)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)          PORT_CHAR('O')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)          PORT_CHAR('U')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)          PORT_CHAR('T')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)          PORT_CHAR('E')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)          PORT_CHAR('Q')

	PORT_START( "ROW3" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH_PAD)  PORT_CHAR('/')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8_PAD)      PORT_CHAR('8')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)          PORT_CHAR('P')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)          PORT_CHAR('I')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)          PORT_CHAR('Y')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)          PORT_CHAR('R')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)          PORT_CHAR('W')

	PORT_START( "ROW4" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6_PAD)      PORT_CHAR('6')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4_PAD)      PORT_CHAR('4')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)          PORT_CHAR('L')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)          PORT_CHAR('J')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)          PORT_CHAR('G')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)          PORT_CHAR('D')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)          PORT_CHAR('A')

	PORT_START( "ROW5" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ASTERISK)   PORT_CHAR('*')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5_PAD)      PORT_CHAR('5')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(':')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)          PORT_CHAR('K')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)          PORT_CHAR('H')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)          PORT_CHAR('F')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)          PORT_CHAR('S')

	PORT_START( "ROW6" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3_PAD)      PORT_CHAR('3')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1_PAD)      PORT_CHAR('1')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)       PORT_CHAR(';')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)          PORT_CHAR('M')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)          PORT_CHAR('B')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)          PORT_CHAR('C')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)          PORT_CHAR('Z')

	PORT_START( "ROW7" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD)   PORT_CHAR('+')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2_PAD)      PORT_CHAR('2')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('?')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)          PORT_CHAR('N')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)          PORT_CHAR('V')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)          PORT_CHAR('X')

	PORT_START( "ROW8" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS_PAD)  PORT_CHAR('-')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0_PAD)      PORT_CHAR('0')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Shift (Right)") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('>')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL)  PORT_CHAR(']')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('@')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Shift (Left)") PORT_CODE(KEYCODE_LSHIFT)

	PORT_START( "ROW9" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad =") PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR('=')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL_PAD)    PORT_CHAR('.')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Stop Run") PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('<')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('[')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Rvs Off") PORT_CODE(KEYCODE_TAB)

	PORT_START( "LOCK" )
	PORT_BIT( 0xfe, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
INPUT_PORTS_END


INPUT_PORTS_START( petb )
	PORT_START( "ROW0" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Crsr Right Left") PORT_CODE(KEYCODE_PGUP) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8_PAD)      PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('"')

	PORT_START( "ROW1" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9_PAD)      PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\xE2\x86\x91") PORT_CODE(KEYCODE_DEL) PORT_CHAR('^')

	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7_PAD)      PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0_PAD)      PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')

	PORT_START( "ROW2" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5_PAD)      PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)          PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR(']')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)          PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)          PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)          PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC))

	PORT_START( "ROW3" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6_PAD)      PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR('@')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)          PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)          PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)          PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)          PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)          PORT_CHAR('a') PORT_CHAR('A')

	PORT_START( "ROW4" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Del  Inst") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('\\')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)          PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)          PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TAB)        PORT_CHAR('\t')

	PORT_START( "ROW5" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4_PAD)      PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)          PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Crsr Down Up") PORT_CODE(KEYCODE_PGDN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)          PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)          PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q') PORT_CHAR('Q')

	PORT_START( "ROW6" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3_PAD)      PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Shift (Right)") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL_PAD)    PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)          PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)          PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Shift (Left)") PORT_CODE(KEYCODE_LSHIFT)

	PORT_START( "ROW7" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2_PAD)      PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Repeat") PORT_CODE(KEYCODE_LALT)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0)          PORT_CHAR('0')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)          PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)          PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z') PORT_CHAR('Z')

	PORT_START( "ROW8" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1_PAD)      PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Home  Clr Screen") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)          PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)          PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Rvs Off") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)

	PORT_START( "ROW9" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Stop Run") PORT_CODE(KEYCODE_END)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\xE2\x86\x90") PORT_CODE(KEYCODE_TILDE) PORT_CHAR(0x2190)

	PORT_START( "LOCK" )
	PORT_BIT( 0xfe, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
INPUT_PORTS_END


INPUT_PORTS_START( petb_de )
	PORT_INCLUDE( petb )
INPUT_PORTS_END


INPUT_PORTS_START( petb_fr )
	PORT_INCLUDE( petb )
INPUT_PORTS_END


INPUT_PORTS_START( petb_se )
	PORT_INCLUDE( petb )
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

void pet_state::via_pa_w(uint8_t data)
{
	m_user->write_c((data>>0)&1);
	m_user->write_d((data>>1)&1);
	m_user->write_e((data>>2)&1);
	m_user->write_f((data>>3)&1);
	m_user->write_h((data>>4)&1);
	m_user->write_j((data>>5)&1);
	m_user->write_k((data>>6)&1);
	m_user->write_l((data>>7)&1);

	m_via_pa = data;
}

uint8_t pet_state::via_pb_r()
{
	/*

	  bit     description

	  PB0     _NDAC IN
	  PB1
	  PB2
	  PB3
	  PB4
	  PB5     SYNC IN
	  PB6     _NRFD IN
	  PB7     _DAV IN

	*/

	uint8_t data = 0;

	// video sync
	data |= (m_crtc ? m_crtc->vsync_r() : m_sync) << 5;

	// IEEE-488
	data |= m_ieee->ndac_r();
	data |= m_ieee->nrfd_r() << 6;
	data |= m_ieee->dav_r() << 7;

	return data;
}

void pet_state::via_pb_w(uint8_t data)
{
	/*

	  bit     description

	  PB0
	  PB1     _NRFD OUT
	  PB2     _ATN OUT
	  PB3     CASS WRITE
	  PB4     #2 CASS MOTOR
	  PB5
	  PB6
	  PB7

	*/

	// IEEE-488
	m_ieee->host_nrfd_w(BIT(data, 1));
	m_ieee->host_atn_w(BIT(data, 2));

	// cassette
	m_cassette->write(BIT(data, 3));
	m_cassette2->write(BIT(data, 3));
	m_cassette2->motor_w(BIT(data, 4));
}

void pet_state::via_ca2_w(int state)
{
	m_graphic = state;
}

void pet_state::via_cb2_w(int state)
{
	m_via_cb2 = state;
	update_speaker();

	m_user->write_m(state);
}


uint8_t pet_state::pia1_pa_r()
{
	/*

	  bit     description

	  PA0     KEY A
	  PA1     KEY B
	  PA2     KEY C
	  PA3     KEY D
	  PA4     #1 CASS SWITCH
	  PA5     #2 CASS SWITCH
	  PA6     _EOI IN
	  PA7     DIAG JUMPER

	*/

	uint8_t data = 0;

	// keyboard
	data |= m_key;

	// cassette
	data |= m_cassette->sense_r() << 4;
	data |= m_cassette2->sense_r() << 5;

	// IEEE-488
	data |= m_ieee->eoi_r() << 6;

	// diagnostic jumper
	data |= (m_user_diag && m_exp->diag_r()) << 7;

	return data;
}

void pet_state::pia1_pa_w(uint8_t data)
{
	/*

	  bit     description

	  PA0     KEY A
	  PA1     KEY B
	  PA2     KEY C
	  PA3     KEY D
	  PA4
	  PA5
	  PA6
	  PA7     SPEAKER

	*/

	// keyboard
	m_key = data & 0x0f;

	// speaker
	m_pia1_pa7 = BIT(data, 7);
	update_speaker();
}

uint8_t pet_state::pia1_pb_r()
{
	uint8_t data = 0xff;

	switch (m_key)
	{
	case 0: data &= m_row[0]->read(); break;
	case 1: data &= m_row[1]->read(); break;
	case 2: data &= m_row[2]->read(); break;
	case 3: data &= m_row[3]->read(); break;
	case 4: data &= m_row[4]->read(); break;
	case 5: data &= m_row[5]->read(); break;
	case 6: data &= m_row[6]->read(); break;
	case 7: data &= m_row[7]->read(); break;
	case 8: data &= m_row[8]->read() & m_lock->read(); break;
	case 9: data &= m_row[9]->read(); break;
	}

	return data;
}

uint8_t pet2001b_state::pia1_pb_r()
{
	uint8_t data = 0xff;

	switch (m_key)
	{
	case 0: data &= m_row[0]->read(); break;
	case 1: data &= m_row[1]->read(); break;
	case 2: data &= m_row[2]->read(); break;
	case 3: data &= m_row[3]->read(); break;
	case 4: data &= m_row[4]->read(); break;
	case 5: data &= m_row[5]->read(); break;
	case 6: data &= m_row[6]->read() & m_lock->read(); break;
	case 7: data &= m_row[7]->read(); break;
	case 8: data &= m_row[8]->read(); break;
	case 9: data &= m_row[9]->read(); break;
	}

	return data;
}

void pet_state::pia1_ca2_w(int state)
{
	m_ieee->host_eoi_w(state);

	m_blanktv = state;
}


void pet_state::user_diag_w(int state)
{
	m_user_diag = state;
}



//**************************************************************************
//  VIDEO
//**************************************************************************

TIMER_CALLBACK_MEMBER( pet_state::sync_tick )
{
	m_sync = !m_sync;

	m_pia1->cb1_w(m_sync);
}


//-------------------------------------------------
//  SCREEN_UPDATE( pet2001 )
//-------------------------------------------------

uint32_t pet_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	pen_t const *const pen = m_palette->pens();

	for (int y = 0; y < 200; y++)
	{
		for (int sx = 0; sx < 40; sx++)
		{
			int const sy = y / 8;
			offs_t const video_addr = (sy * 40) + sx;
			uint8_t const lsd = m_video_ram[video_addr];

			int const ra = y & 0x07;
			offs_t const char_addr = (m_graphic << 10) | ((lsd & 0x7f) << 3) | ra;
			uint8_t data = m_char_rom->base()[char_addr];

			for (int x = 0; x < 8; x++, data <<= 1)
			{
				int const color = (BIT(data, 7) ^ BIT(lsd, 7)) && m_blanktv;
				bitmap.pix(y, (sx * 8) + x) = pen[color];
			}
		}
	}

	return 0;
}


//-------------------------------------------------
//  MC6845 PET80
//-------------------------------------------------

MC6845_BEGIN_UPDATE( pet_state::pet_begin_update )
{
	bitmap.fill(rgb_t::black(), cliprect);
}

MC6845_UPDATE_ROW( pet80_state::pet80_update_row )
{
	int x = 0;
	int char_rom_mask = m_char_rom->bytes() - 1;
	const pen_t *pen = m_palette->pens();
	hbp = 80;

	for (int column = 0; column < x_count; column++)
	{
		uint8_t lsd = 0, data = 0;
		uint8_t rra = ra & 0x07;
		int no_row = !(BIT(ra, 3) || BIT(ra, 4));
		int invert = BIT(ma, 12);
		int chr_option = BIT(ma, 13);

		// even character

		lsd = m_video_ram[((ma + column) << 1) & 0x7ff];

		offs_t char_addr = (chr_option << 11) | (m_graphic << 10) | ((lsd & 0x7f) << 3) | rra;
		data = m_char_rom->base()[char_addr & char_rom_mask];

		for (int bit = 0; bit < 8; bit++, data <<= 1)
		{
			int const video = (!((BIT(data, 7) ^ BIT(lsd, 7)) && no_row) ^ invert) && de;
			bitmap.pix(vbp + y, hbp + x++) = pen[video];
		}

		// odd character

		lsd = m_video_ram[(((ma + column) << 1) + 1) & 0x7ff];

		char_addr = (chr_option << 11) | (m_graphic << 10) | ((lsd & 0x7f) << 3) | rra;
		data = m_char_rom->base()[char_addr & char_rom_mask];

		for (int bit = 0; bit < 8; bit++, data <<= 1)
		{
			int const video = (!((BIT(data, 7) ^ BIT(lsd, 7)) && no_row) ^ invert) && de;
			bitmap.pix(vbp + y, hbp + x++) = pen[video];
		}
	}
}


//-------------------------------------------------
//  MC6845 PET40
//-------------------------------------------------

MC6845_UPDATE_ROW( pet_state::pet40_update_row )
{
	int x = 0;
	int char_rom_mask = m_char_rom->bytes() - 1;
	const pen_t *pen = m_palette->pens();
	hbp = 41;

	for (int column = 0; column < x_count; column++)
	{
		uint8_t lsd = 0, data = 0;
		uint8_t rra = ra & 0x07;
		int no_row = !(BIT(ra, 3) || BIT(ra, 4));
		int invert = BIT(ma, 12);
		int chr_option = BIT(ma, 13);

		lsd = m_video_ram[(ma + column) & 0x3ff];

		offs_t char_addr = (chr_option << 11) | (m_graphic << 10) | ((lsd & 0x7f) << 3) | rra;
		data = m_char_rom->base()[char_addr & char_rom_mask];

		for (int bit = 0; bit < 8; bit++, data <<= 1)
		{
			int const video = (!((BIT(data, 7) ^ BIT(lsd, 7)) && no_row) ^ invert) && de;
			bitmap.pix(vbp + y, hbp + x++) = pen[video];
		}
	}
}


//-------------------------------------------------
//  MC6845 CBM8296
//-------------------------------------------------

MC6845_UPDATE_ROW( pet80_state::cbm8296_update_row )
{
	int x = 0;
	int char_rom_mask = m_char_rom->bytes() - 1;
	const pen_t *pen = m_palette->pens();

	for (int column = 0; column < x_count; column++)
	{
		uint8_t lsd = 0, data = 0;
		uint8_t rra = ra & 0x07;
		int no_row = !BIT(ra, 3);
		int ra4 = BIT(ra, 4);
		int chr_option = BIT(ma, 13);
		offs_t vma = (ma + column) & 0x1fff;
		offs_t drma = 0x8000 | ra4 << 14 | ((vma & 0xf00) << 1) | (vma & 0xff);

		// even character

		lsd = m_ram->pointer()[drma];

		offs_t char_addr = (chr_option << 11) | (m_graphic << 10) | ((lsd & 0x7f) << 3) | rra;
		data = m_char_rom->base()[char_addr & char_rom_mask];

		for (int bit = 0; bit < 8; bit++, data <<= 1)
		{
			int const video = (((BIT(data, 7) ^ BIT(lsd, 7)) && no_row) && de);
			bitmap.pix(vbp + y, hbp + x++) = pen[video];
		}

		// odd character

		lsd = m_ram->pointer()[drma | 0x100];

		char_addr = (chr_option << 11) | (m_graphic << 10) | ((lsd & 0x7f) << 3) | rra;
		data = m_char_rom->base()[char_addr & char_rom_mask];

		for (int bit = 0; bit < 8; bit++, data <<= 1)
		{
			int const video = (((BIT(data, 7) ^ BIT(lsd, 7)) && no_row) && de);
			bitmap.pix(vbp + y, hbp + x++) = pen[video];
		}
	}
}


void cbm8296d_ieee488_devices(device_slot_interface &device)
{
	device.option_add("c8250lp", C8250LP);
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

MACHINE_START_MEMBER( pet_state, pet )
{
	// initialize memory
	uint8_t data = 0xff;

	for (offs_t offset = 0; offset < m_ram->size(); offset++)
	{
		m_ram->pointer()[offset] = data;
		if (!(offset % 64)) data ^= 0xff;
	}

	data = 0xff;

	for (offs_t offset = 0; offset < m_video_ram_size; offset++)
	{
		m_video_ram[offset] = data;
		if (!(offset % 64)) data ^= 0xff;
	}

	if (!m_sync_timer)
		m_sync_timer = timer_alloc(FUNC(pet_state::sync_tick), this);

	// state saving
	save_item(NAME(m_key));
	save_item(NAME(m_sync));
	save_item(NAME(m_graphic));
	save_item(NAME(m_blanktv));
	save_item(NAME(m_user_diag));
}


MACHINE_START_MEMBER( pet_state, pet2001 )
{
	m_video_ram_size = 0x400;

	MACHINE_START_CALL_MEMBER(pet);
}


MACHINE_RESET_MEMBER( pet_state, pet )
{
	m_maincpu->reset();

	m_via->reset();
	m_pia1->reset();
	m_pia2->reset();

	m_exp->reset();

	m_ieee->host_ren_w(0);

	if (m_sync_period != attotime::zero)
		m_sync_timer->adjust(machine().time() + m_sync_period, 0, m_sync_period);
}


MACHINE_START_MEMBER( pet_state, pet40 )
{
	m_video_ram_size = 0x400;

	MACHINE_START_CALL_MEMBER(pet);
}


MACHINE_RESET_MEMBER( pet_state, pet40 )
{
	MACHINE_RESET_CALL_MEMBER(pet);

	m_crtc->reset();
}


MACHINE_START_MEMBER( pet80_state, pet80 )
{
	m_video_ram_size = 0x800;

	MACHINE_START_CALL_MEMBER(pet);
}


MACHINE_RESET_MEMBER( pet80_state, pet80 )
{
	MACHINE_RESET_CALL_MEMBER(pet);

	m_crtc->reset();
}


MACHINE_START_MEMBER( cbm8296_state, cbm8296 )
{
	MACHINE_START_CALL_MEMBER(pet80);

	// state saving
	save_item(NAME(m_cr));
	save_item(NAME(m_via_pa));
}


MACHINE_RESET_MEMBER( cbm8296_state, cbm8296 )
{
	MACHINE_RESET_CALL_MEMBER(pet80);

	m_cr = 0;
	m_via_pa = 0xff;
}



//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

void pet_state::_4k(machine_config &config)
{
	RAM(config, m_ram);
	m_ram->set_default_size("4K");
	m_ram->set_extra_options("8K,16K,32K");
}

void pet_state::_8k(machine_config &config)
{
	RAM(config, m_ram);
	m_ram->set_default_size("8K");
	m_ram->set_extra_options("16K,32K");
}

void pet_state::_16k(machine_config &config)
{
	RAM(config, m_ram);
	m_ram->set_default_size("16K");
	m_ram->set_extra_options("32K");
}

void pet_state::_32k(machine_config &config)
{
	RAM(config, m_ram);
	m_ram->set_default_size("32K");
}

void pet_state::base_pet_devices(machine_config &config, const char *default_drive)
{
	input_merger_device &mainirq(INPUT_MERGER_ANY_HIGH(config, "mainirq")); // open collector
	mainirq.output_handler().set_inputline(m_maincpu, m6502_device::IRQ_LINE);
	mainirq.output_handler().append(m_exp, FUNC(pet_expansion_slot_device::irq_w));

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	MOS6522(config, m_via, XTAL(16'000'000)/16);
	m_via->readpb_handler().set(FUNC(pet_state::via_pb_r));
	m_via->writepa_handler().set(FUNC(pet_state::via_pa_w));
	m_via->writepb_handler().set(FUNC(pet_state::via_pb_w));
	m_via->ca2_handler().set(FUNC(pet_state::via_ca2_w));
	m_via->cb2_handler().set(FUNC(pet_state::via_cb2_w));
	m_via->irq_handler().set("mainirq", FUNC(input_merger_device::in_w<0>));

	PIA6821(config, m_pia1);
	m_pia1->readpa_handler().set(FUNC(pet_state::pia1_pa_r));
	m_pia1->readpb_handler().set(FUNC(pet_state::pia1_pb_r));
	m_pia1->writepa_handler().set(FUNC(pet_state::pia1_pa_w));
	m_pia1->ca2_handler().set(FUNC(pet_state::pia1_ca2_w));
	m_pia1->cb2_handler().set(PET_DATASSETTE_PORT_TAG, FUNC(pet_datassette_port_device::motor_w));
	m_pia1->irqa_handler().set("mainirq", FUNC(input_merger_device::in_w<1>));
	m_pia1->irqb_handler().set("mainirq", FUNC(input_merger_device::in_w<2>));

	PIA6821(config, m_pia2);
	m_pia2->readpa_handler().set(IEEE488_TAG, FUNC(ieee488_device::dio_r));
	m_pia2->writepb_handler().set(IEEE488_TAG, FUNC(ieee488_device::host_dio_w));
	m_pia2->ca2_handler().set(IEEE488_TAG, FUNC(ieee488_device::host_ndac_w));
	m_pia2->cb2_handler().set(IEEE488_TAG, FUNC(ieee488_device::host_dav_w));
	m_pia2->irqa_handler().set("mainirq", FUNC(input_merger_device::in_w<3>));
	m_pia2->irqb_handler().set("mainirq", FUNC(input_merger_device::in_w<4>));

	ieee488_slot_device::add_cbm_defaults(config, default_drive);
	IEEE488(config, m_ieee, 0);
	m_ieee->srq_callback().set(m_pia2, FUNC(pia6821_device::cb1_w));
	m_ieee->atn_callback().set(m_pia2, FUNC(pia6821_device::ca1_w));

	PET_DATASSETTE_PORT(config, PET_DATASSETTE_PORT_TAG, cbm_datassette_devices, "c2n").read_handler().set(m_pia1, FUNC(pia6821_device::ca1_w));
	PET_DATASSETTE_PORT(config, PET_DATASSETTE_PORT2_TAG, cbm_datassette_devices, nullptr).read_handler().set(M6522_TAG, FUNC(via6522_device::write_cb1));

	PET_EXPANSION_SLOT(config, m_exp, XTAL(16'000'000)/16, pet_expansion_cards, nullptr);
	m_exp->dma_read_callback().set(FUNC(pet_state::read));
	m_exp->dma_write_callback().set(FUNC(pet_state::write));

	PET_USER_PORT(config, m_user, pet_user_port_cards, nullptr);
	m_user->pb_handler().set(m_via, FUNC(via6522_device::write_ca1));
	m_user->pc_handler().set(m_via, FUNC(via6522_device::write_pa0));
	m_user->pd_handler().set(m_via, FUNC(via6522_device::write_pa1));
	m_user->pe_handler().set(m_via, FUNC(via6522_device::write_pa2));
	m_user->pf_handler().set(m_via, FUNC(via6522_device::write_pa3));
	m_user->ph_handler().set(m_via, FUNC(via6522_device::write_pa4));
	m_user->pj_handler().set(m_via, FUNC(via6522_device::write_pa5));
	m_user->pk_handler().set(m_via, FUNC(via6522_device::write_pa6));
	m_user->pl_handler().set(m_via, FUNC(via6522_device::write_pa7));
	m_user->pm_handler().set(m_via, FUNC(via6522_device::write_cb2));

	quickload_image_device &quickload(QUICKLOAD(config, "quickload", "p00,prg", CBM_QUICKLOAD_DELAY));
	quickload.set_load_callback(FUNC(pet_state::quickload_pet));
	quickload.set_interface("cbm_quik");

	SOFTWARE_LIST(config, "cass_list").set_original("pet_cass");
	SOFTWARE_LIST(config, "flop_list").set_original("pet_flop");
	SOFTWARE_LIST(config, "hdd_list").set_original("pet_hdd");
	SOFTWARE_LIST(config, "quik_list").set_original("pet_quik");
}

void pet_state::pet(machine_config &config)
{
	base_pet_devices(config, "c4040");

	MCFG_MACHINE_START_OVERRIDE(pet_state, pet2001)
	MCFG_MACHINE_RESET_OVERRIDE(pet_state, pet)

	// basic machine hardware
	M6502(config, m_maincpu, XTAL(8'000'000)/8);
	m_maincpu->set_addrmap(AS_PROGRAM, &pet_state::pet2001_mem);

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_color(rgb_t::green());
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	m_screen->set_size(320, 200);
	m_screen->set_visarea(0, 320-1, 0, 200-1);
	m_screen->set_screen_update(FUNC(pet_state::screen_update));
	m_sync_period = attotime::from_hz(120);

	m_user->p5_handler().set(FUNC(pet_state::user_diag_w));
}

void pet_state::pet2001(machine_config &config)
{
	pet(config);
	_4k(config);
}

void pet_state::pet20018(machine_config &config)
{
	pet(config);
	_8k(config);
}

void pet_state::pet2001n(machine_config &config, bool with_b000)
{
	pet(config);

	GENERIC_CARTSLOT(config, "cart_9000", generic_linear_slot, "pet_9000_rom", "bin,rom");
	GENERIC_CARTSLOT(config, "cart_a000", generic_linear_slot, "pet_a000_rom", "bin,rom");
	if (with_b000)
		GENERIC_CARTSLOT(config, "cart_b000", generic_linear_slot, "pet_b000_rom", "bin,rom");

	SOFTWARE_LIST(config, "rom_list").set_original("pet_rom");
}


void pet_state::pet2001n8(machine_config &config)
{
	pet2001n(config);
	_8k(config);
}

void pet_state::pet2001n16(machine_config &config)
{
	pet2001n(config);
	_16k(config);
}

void pet_state::pet2001n32(machine_config &config)
{
	pet2001n(config);
	_32k(config);
}

void pet_state::cbm3000(machine_config &config)
{
	pet2001n(config, false);
	// video hardware
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	m_screen->set_size(320, 200);
	m_screen->set_visarea(0, 320-1, 0, 200-1);
	m_screen->set_screen_update(FUNC(pet_state::screen_update));
	m_sync_period = attotime::from_hz(100);
}

void pet_state::cbm3008(machine_config &config)
{
	cbm3000(config);
	_8k(config);
}

void pet_state::cbm3016(machine_config &config)
{
	cbm3000(config);
	_16k(config);
}

void pet_state::cbm3032(machine_config &config)
{
	cbm3000(config);
	_32k(config);
}

void pet2001b_state::pet2001b(machine_config &config, bool with_b000)
{
	pet2001n(config, with_b000);
	m_pia1->readpb_handler().set(FUNC(pet2001b_state::pia1_pb_r));
}

void pet2001b_state::pet2001b8(machine_config &config)
{
	pet2001b(config);
	_8k(config);
}

void pet2001b_state::pet2001b16(machine_config &config)
{
	pet2001b(config);
	_16k(config);
}

void pet2001b_state::pet2001b32(machine_config &config)
{
	pet2001b(config);
	_32k(config);
}

void pet2001b_state::cbm3032b(machine_config &config)
{
	pet2001b(config);
	// video hardware
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	m_screen->set_size(320, 200);
	m_screen->set_visarea(0, 320-1, 0, 200-1);
	m_screen->set_screen_update(FUNC(pet_state::screen_update));
	m_sync_period = attotime::from_hz(100);

	_32k(config);
}

void pet2001b_state::pet4000(machine_config &config)
{
	pet2001n(config, false);
}

void pet2001b_state::pet4016(machine_config &config)
{
	pet4000(config);
	// RAM not upgradeable
	RAM(config, m_ram);
	m_ram->set_default_size("16K");
}

void pet2001b_state::pet4032(machine_config &config)
{
	pet4000(config);
	_32k(config);
}

void pet2001b_state::pet4032f(machine_config &config)
{
	pet4000(config);
	MCFG_MACHINE_START_OVERRIDE(pet_state, pet40)
	MCFG_MACHINE_RESET_OVERRIDE(pet_state, pet40)

	// video hardware
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	m_screen->set_size(320, 250);
	m_screen->set_visarea(0, 320 - 1, 0, 250 - 1);
	m_screen->set_screen_update(MC6845_TAG, FUNC(mc6845_device::screen_update));
	m_sync_period = attotime::never;

	MC6845(config, m_crtc, XTAL(16'000'000)/16);
	m_crtc->set_screen(SCREEN_TAG);
	m_crtc->set_show_border_area(true);
	m_crtc->set_char_width(8);
	m_crtc->set_begin_update_callback(FUNC(pet_state::pet_begin_update));
	m_crtc->set_update_row_callback(FUNC(pet_state::pet40_update_row));
	m_crtc->out_vsync_callback().set(M6520_1_TAG, FUNC(pia6821_device::cb1_w));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);

	_32k(config);
}

void pet_state::cbm4000(machine_config &config)
{
	pet2001n(config, false);
	// video hardware
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	m_screen->set_size(320, 200);
	m_screen->set_visarea(0, 320-1, 0, 200-1);
	m_screen->set_screen_update(FUNC(pet_state::screen_update));
	m_sync_period = attotime::from_hz(100);
}

void pet_state::cbm4016(machine_config &config)
{
	cbm4000(config);
	// RAM not upgradeable
	RAM(config, m_ram);
	m_ram->set_default_size("16K");
}

void pet_state::cbm4032(machine_config &config)
{
	cbm4000(config);
	_32k(config);
}

void pet_state::cbm4032f(machine_config &config)
{
	cbm4000(config);
	MCFG_MACHINE_START_OVERRIDE(pet_state, pet40)
	MCFG_MACHINE_RESET_OVERRIDE(pet_state, pet40)

	// video hardware
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	m_screen->set_size(320, 250);
	m_screen->set_visarea(0, 320 - 1, 0, 250 - 1);
	m_screen->set_screen_update(MC6845_TAG, FUNC(mc6845_device::screen_update));
	m_sync_period = attotime::never;

	MC6845(config, m_crtc, XTAL(16'000'000)/16);
	m_crtc->set_screen(SCREEN_TAG);
	m_crtc->set_show_border_area(true);
	m_crtc->set_char_width(8);
	m_crtc->set_begin_update_callback(FUNC(pet_state::pet_begin_update));
	m_crtc->set_update_row_callback(FUNC(pet_state::pet40_update_row));
	m_crtc->out_vsync_callback().set(M6520_1_TAG, FUNC(pia6821_device::cb1_w));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);

	_32k(config);
}

void pet2001b_state::pet4000b(machine_config &config)
{
	pet2001b(config, false);
}

void pet2001b_state::pet4032b(machine_config &config)
{
	pet4000b(config);
	_32k(config);
}

void pet2001b_state::cbm4000b(machine_config &config)
{
	pet2001b(config, false);
	// video hardware
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	m_screen->set_size(320, 200);
	m_screen->set_visarea(0, 320-1, 0, 200-1);
	m_screen->set_screen_update(FUNC(pet_state::screen_update));
	m_sync_period = attotime::from_hz(100);
}

void pet2001b_state::cbm4032b(machine_config &config)
{
	cbm4000b(config);
	_32k(config);
}

void pet80_state::pet80(machine_config &config)
{
	base_pet_devices(config, "c8050");

	MCFG_MACHINE_START_OVERRIDE(pet80_state, pet80)
	MCFG_MACHINE_RESET_OVERRIDE(pet80_state, pet80)

	// basic machine hardware
	M6502(config, m_maincpu, XTAL(16'000'000)/16);
	m_maincpu->set_addrmap(AS_PROGRAM, &pet_state::pet2001_mem);

	// video hardware
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.set_size(640, 250);
	screen.set_visarea(0, 640 - 1, 0, 250 - 1);
	screen.set_screen_update(MC6845_TAG, FUNC(mc6845_device::screen_update));

	MC6845(config, m_crtc, XTAL(16'000'000)/16);
	m_crtc->set_screen(SCREEN_TAG);
	m_crtc->set_show_border_area(true);
	m_crtc->set_char_width(2*8);
	m_crtc->set_begin_update_callback(FUNC(pet_state::pet_begin_update));
	m_crtc->set_update_row_callback(FUNC(pet80_state::pet80_update_row));
	m_crtc->out_vsync_callback().set(M6520_1_TAG, FUNC(pia6821_device::cb1_w));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);

	// devices
	GENERIC_CARTSLOT(config, "cart_9000", generic_linear_slot, "pet_9000_rom", "bin,rom");
	GENERIC_CARTSLOT(config, "cart_a000", generic_linear_slot, "pet_a000_rom", "bin,rom");

	// software lists
	SOFTWARE_LIST(config, "rom_list").set_original("pet_rom");
}

void pet80_state::pet8032(machine_config &config)
{
	pet80(config);
	_32k(config);
}

void superpet_state::superpet(machine_config &config)
{
	pet8032(config);
	m_exp->set_default_option("superpet");
	SOFTWARE_LIST(config, "flop_list2").set_original("superpet_flop");
}

void cbm8096_state::cbm8096(machine_config &config)
{
	pet80(config);
	m_exp->set_default_option("64k");

	RAM(config, m_ram);
	m_ram->set_default_size("96K");

	SOFTWARE_LIST(config, "flop_list2").set_original("cbm8096_flop");
}

void cbm8296_state::cbm8296(machine_config &config)
{
	pet80(config);
	MCFG_MACHINE_START_OVERRIDE(cbm8296_state, cbm8296)
	MCFG_MACHINE_RESET_OVERRIDE(cbm8296_state, cbm8296)

	m_maincpu->set_addrmap(AS_PROGRAM, &cbm8296_state::cbm8296_mem);

	PLS100(config, PLA1_TAG);
	PLS100(config, PLA2_TAG);

	m_crtc->set_clock(XTAL(16'000'000)/16);
	m_crtc->set_show_border_area(true);
	m_crtc->set_char_width(2*8);
	m_crtc->set_update_row_callback(FUNC(pet80_state::cbm8296_update_row));
	m_crtc->out_vsync_callback().set(M6520_1_TAG, FUNC(pia6821_device::cb1_w));

	subdevice<ieee488_slot_device>("ieee8")->set_default_option("c8250");

	RAM(config, m_ram);
	m_ram->set_default_size("128K");

	SOFTWARE_LIST(config, "flop_list2").set_original("cbm8296_flop");
}

void cbm8296_state::cbm8296d(machine_config &config)
{
	cbm8296(config);
	ieee488_slot_device &ieee8(*subdevice<ieee488_slot_device>("ieee8"));
	cbm8296d_ieee488_devices(ieee8);
	ieee8.set_default_option("c8250lp");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( pet2001 )
//-------------------------------------------------

ROM_START( pet2001 )
	ROM_REGION( 0x7000, M6502_TAG, 0 )
	ROM_DEFAULT_BIOS( "basic1r" )
	ROM_SYSTEM_BIOS( 0, "basic1o", "Original" )
	ROMX_LOAD( "901447-01.h1", 0x3000, 0x0800, CRC(a055e33a) SHA1(831db40324113ee996c434d38b4add3fd1f820bd), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "basic1r", "Revised" )
	ROMX_LOAD( "901447-09.h1", 0x3000, 0x0800, CRC(03cf16d0) SHA1(1330580c0614d3556a389da4649488ba04a60908), ROM_BIOS(1) )
	ROM_LOAD( "901447-02.h5", 0x3800, 0x0800, CRC(69fd8a8f) SHA1(70c0f4fa67a70995b168668c957c3fcf2c8641bd) )
	ROM_LOAD( "901447-03.h2", 0x4000, 0x0800, CRC(d349f2d4) SHA1(4bf2c20c51a63d213886957485ebef336bb803d0) )
	ROM_LOAD( "901447-04.h6", 0x4800, 0x0800, CRC(850544eb) SHA1(d293972d529023d8fd1f493149e4777b5c253a69) )
	ROM_LOAD( "901447-05.h3", 0x5000, 0x0800, CRC(9e1c5cea) SHA1(f02f5fb492ba93dbbd390f24c10f7a832dec432a) )
	ROM_LOAD( "901447-06.h4", 0x6000, 0x0800, CRC(661a814a) SHA1(960717282878e7de893d87242ddf9d1512be162e) )
	ROM_LOAD( "901447-07.h7", 0x6800, 0x0800, CRC(c4f47ad1) SHA1(d440f2510bc52e20c3d6bc8b9ded9cea7f462a9c) )

	ROM_REGION( 0x800, "charom", 0 )
	ROM_LOAD( "901447-08.a2", 0x000, 0x800, CRC(54f32f45) SHA1(3e067cc621e4beafca2b90cb8f6dba975df2855b) )
ROM_END

#define rom_pet20018 rom_pet2001


//-------------------------------------------------
//  ROM( pet2001n )
//-------------------------------------------------

ROM_START( pet2001n )
	ROM_REGION( 0x7000, M6502_TAG, 0 )
	ROM_LOAD( "901465-01.ud6", 0x3000, 0x1000, CRC(63a7fe4a) SHA1(3622111f486d0e137022523657394befa92bde44) )   // BASIC 2
	ROM_LOAD( "901465-02.ud7", 0x4000, 0x1000, CRC(ae4cb035) SHA1(1bc0ebf27c9bb62ad71bca40313e874234cab6ac) )   // BASIC 2
	ROM_LOAD( "901447-24.ud8", 0x5000, 0x0800, CRC(e459ab32) SHA1(5e5502ce32f5a7e387d65efe058916282041e54b) )   // Screen Editor (40 columns, no CRTC, Normal Keyb)
	ROM_LOAD( "901465-03.ud9", 0x6000, 0x1000, CRC(f02238e2) SHA1(38742bdf449f629bcba6276ef24d3daeb7da6e84) )   // Kernal

	ROM_REGION( 0x800, "charom", 0 )
	ROM_LOAD( "901447-10.uf10", 0x000, 0x800, CRC(d8408674) SHA1(0157a2d55b7ac4eaeb38475889ebeea52e2593db) )   // Character Generator
ROM_END

#define rom_pet2001n16 rom_pet2001n
#define rom_pet2001n32 rom_pet2001n
#define rom_cbm3008 rom_pet2001n
#define rom_cbm3016 rom_pet2001n
#define rom_cbm3032 rom_pet2001n


//-------------------------------------------------
//  ROM( pet2001b )
//-------------------------------------------------

ROM_START( pet2001b )
	ROM_REGION( 0x7000, M6502_TAG, 0 )
	ROM_LOAD( "901465-01.ud6", 0x3000, 0x1000, CRC(63a7fe4a) SHA1(3622111f486d0e137022523657394befa92bde44) )   // BASIC 2
	ROM_LOAD( "901465-02.ud7", 0x4000, 0x1000, CRC(ae4cb035) SHA1(1bc0ebf27c9bb62ad71bca40313e874234cab6ac) )   // BASIC 2
	ROM_LOAD( "901474-01.ud8", 0x5000, 0x0800, CRC(05db957e) SHA1(174ace3a8c0348cd21d39cc864e2adc58b0101a9) )   // Screen Editor (40 columns, no CRTC, Business Keyb)
	ROM_LOAD( "901465-03.ud9", 0x6000, 0x1000, CRC(f02238e2) SHA1(38742bdf449f629bcba6276ef24d3daeb7da6e84) )   // Kernal

	ROM_REGION( 0x800, "charom", 0 )
	ROM_LOAD( "901447-10.uf10", 0x000, 0x800, CRC(d8408674) SHA1(0157a2d55b7ac4eaeb38475889ebeea52e2593db) )   // Character Generator
ROM_END

#define rom_pet2001b16 rom_pet2001b
#define rom_pet2001b32 rom_pet2001b
#define rom_cbm3032b rom_pet2001b


//-------------------------------------------------
//  ROM( pet4016 )
//-------------------------------------------------

ROM_START( pet4016 )
	ROM_REGION( 0x7000, M6502_TAG, 0 )
	ROM_DEFAULT_BIOS( "basic4r" )
	ROM_SYSTEM_BIOS( 0, "basic4", "Original" )
	ROMX_LOAD( "901465-19.ud5", 0x2000, 0x1000, CRC(3a5f5721) SHA1(bc2b7c99495fea3eda950ee9e3d6cabe448a452b), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "basic4r", "Revised" )
	ROMX_LOAD( "901465-23.ud5", 0x2000, 0x1000, CRC(ae3deac0) SHA1(975ee25e28ff302879424587e5fb4ba19f403adc), ROM_BIOS(1) ) // BASIC 4
	ROM_LOAD( "901465-20.ud6", 0x3000, 0x1000, CRC(0fc17b9c) SHA1(242f98298931d21eaacb55fe635e44b7fc192b0a) )   // BASIC 4
	ROM_LOAD( "901465-21.ud7", 0x4000, 0x1000, CRC(36d91855) SHA1(1bb236c72c726e8fb029c68f9bfa5ee803faf0a8) )   // BASIC 4
	ROM_LOAD( "901447-29.ud8", 0x5000, 0x0800, CRC(e5714d4c) SHA1(e88f56e5c54b0e8d8d4e8cb39a4647c803c1f51c) )   // Screen Editor (40 columns, no CRTC, Normal Keyb)
	ROM_LOAD( "901465-22.ud9", 0x6000, 0x1000, CRC(cc5298a1) SHA1(96a0fa56e0c937da92971d9c99d504e44e898806) )   // Kernal

	ROM_REGION( 0x800, "charom", 0 )
	ROM_LOAD( "901447-10.uf10", 0x000, 0x800, CRC(d8408674) SHA1(0157a2d55b7ac4eaeb38475889ebeea52e2593db) )   // Character Generator
ROM_END

#define rom_pet4032 rom_pet4016


//-------------------------------------------------
//  ROM( pet4032f )
//-------------------------------------------------

ROM_START( pet4032f )
	ROM_REGION( 0x7000, M6502_TAG, 0 )
	ROM_DEFAULT_BIOS( "basic4r" )
	ROM_SYSTEM_BIOS( 0, "basic4", "Original" )
	ROMX_LOAD( "901465-19.ud5", 0x2000, 0x1000, CRC(3a5f5721) SHA1(bc2b7c99495fea3eda950ee9e3d6cabe448a452b), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "basic4r", "Revised" )
	ROMX_LOAD( "901465-23.ud5", 0x2000, 0x1000, CRC(ae3deac0) SHA1(975ee25e28ff302879424587e5fb4ba19f403adc), ROM_BIOS(1) ) // BASIC 4
	ROM_LOAD( "901465-20.ud6", 0x3000, 0x1000, CRC(0fc17b9c) SHA1(242f98298931d21eaacb55fe635e44b7fc192b0a) )   // BASIC 4
	ROM_LOAD( "901465-21.ud7", 0x4000, 0x1000, CRC(36d91855) SHA1(1bb236c72c726e8fb029c68f9bfa5ee803faf0a8) )   // BASIC 4
	ROM_LOAD( "901499-01.ud7", 0x5000, 0x0800, CRC(5f85bdf8) SHA1(8cbf086c1ce4dfb2a2fe24c47476dfb878493dee) )   // Screen Editor (40 columns, CRTC 60Hz, Normal Keyb?)
	ROM_LOAD( "901465-22.ud9", 0x6000, 0x1000, CRC(cc5298a1) SHA1(96a0fa56e0c937da92971d9c99d504e44e898806) )   // Kernal

	ROM_REGION( 0x800, "charom", 0 )
	ROM_LOAD( "901447-10.uf10", 0x000, 0x800, CRC(d8408674) SHA1(0157a2d55b7ac4eaeb38475889ebeea52e2593db) )   // Character Generator
ROM_END


//-------------------------------------------------
//  ROM( cbm4016 )
//-------------------------------------------------

ROM_START( cbm4016 )
	ROM_REGION( 0x7000, M6502_TAG, 0 )
	ROM_DEFAULT_BIOS( "basic4r" )
	ROM_SYSTEM_BIOS( 0, "basic4", "Original" )
	ROMX_LOAD( "901465-19.ud5", 0x2000, 0x1000, CRC(3a5f5721) SHA1(bc2b7c99495fea3eda950ee9e3d6cabe448a452b), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "basic4r", "Revised" )
	ROMX_LOAD( "901465-23.ud5", 0x2000, 0x1000, CRC(ae3deac0) SHA1(975ee25e28ff302879424587e5fb4ba19f403adc), ROM_BIOS(1) ) // BASIC 4
	ROM_LOAD( "901465-20.ud6", 0x3000, 0x1000, CRC(0fc17b9c) SHA1(242f98298931d21eaacb55fe635e44b7fc192b0a) )   // BASIC 4
	ROM_LOAD( "901465-21.ud7", 0x4000, 0x1000, CRC(36d91855) SHA1(1bb236c72c726e8fb029c68f9bfa5ee803faf0a8) )   // BASIC 4
	ROM_LOAD( "901447-29.ud8", 0x5000, 0x0800, CRC(e5714d4c) SHA1(e88f56e5c54b0e8d8d4e8cb39a4647c803c1f51c) )   // Screen Editor (40 columns, no CRTC, Normal Keyb)
	ROM_LOAD( "901465-22.ud9", 0x6000, 0x1000, CRC(cc5298a1) SHA1(96a0fa56e0c937da92971d9c99d504e44e898806) )   // Kernal

	ROM_REGION( 0x800, "charom", 0 )
	ROM_LOAD( "901447-10.uf10", 0x000, 0x800, CRC(d8408674) SHA1(0157a2d55b7ac4eaeb38475889ebeea52e2593db) )   // Character Generator
ROM_END

#define rom_cbm4032 rom_cbm4016


//-------------------------------------------------
//  ROM( cbm4032f )
//-------------------------------------------------

ROM_START( cbm4032f )
	ROM_REGION( 0x7000, M6502_TAG, 0 )
	ROM_DEFAULT_BIOS( "basic4r" )
	ROM_SYSTEM_BIOS( 0, "basic4", "Original" )
	ROMX_LOAD( "901465-19.ud5", 0x2000, 0x1000, CRC(3a5f5721) SHA1(bc2b7c99495fea3eda950ee9e3d6cabe448a452b), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "basic4r", "Revised" )
	ROMX_LOAD( "901465-23.ud5", 0x2000, 0x1000, CRC(ae3deac0) SHA1(975ee25e28ff302879424587e5fb4ba19f403adc), ROM_BIOS(1) ) // BASIC 4
	ROM_LOAD( "901465-20.ud6", 0x3000, 0x1000, CRC(0fc17b9c) SHA1(242f98298931d21eaacb55fe635e44b7fc192b0a) )   // BASIC 4
	ROM_LOAD( "901465-21.ud7", 0x4000, 0x1000, CRC(36d91855) SHA1(1bb236c72c726e8fb029c68f9bfa5ee803faf0a8) )   // BASIC 4
	ROM_LOAD( "901498-01.ud7", 0x5000, 0x0800, CRC(3370e359) SHA1(05af284c914d53a52987b5f602466de75765f650) )   // Screen Editor (40 columns, CRTC 50Hz, Normal Keyb?)
	ROM_LOAD( "901465-22.ud9", 0x6000, 0x1000, CRC(cc5298a1) SHA1(96a0fa56e0c937da92971d9c99d504e44e898806) )   // Kernal

	ROM_REGION( 0x800, "charom", 0 )
	ROM_LOAD( "901447-10.uf10", 0x000, 0x800, CRC(d8408674) SHA1(0157a2d55b7ac4eaeb38475889ebeea52e2593db) )   // Character Generator
ROM_END


//-------------------------------------------------
//  ROM( pet4032b )
//-------------------------------------------------

ROM_START( pet4032b )
	ROM_REGION( 0x7000, M6502_TAG, 0 )
	ROM_DEFAULT_BIOS( "basic4r" )
	ROM_SYSTEM_BIOS( 0, "basic4", "Original" )
	ROMX_LOAD( "901465-19.ud5", 0x2000, 0x1000, CRC(3a5f5721) SHA1(bc2b7c99495fea3eda950ee9e3d6cabe448a452b), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "basic4r", "Revised" )
	ROMX_LOAD( "901465-23.ud5", 0x2000, 0x1000, CRC(ae3deac0) SHA1(975ee25e28ff302879424587e5fb4ba19f403adc), ROM_BIOS(1) ) // BASIC 4
	ROM_LOAD( "901465-20.ud6", 0x3000, 0x1000, CRC(0fc17b9c) SHA1(242f98298931d21eaacb55fe635e44b7fc192b0a) )   // BASIC 4
	ROM_LOAD( "901465-21.ud7", 0x4000, 0x1000, CRC(36d91855) SHA1(1bb236c72c726e8fb029c68f9bfa5ee803faf0a8) )   // BASIC 4
	ROM_LOAD( "901474-02.ud8", 0x5000, 0x0800, CRC(75ff4af7) SHA1(0ca5c4e8f532f914cb0bf86ea9900f20f0a655ce) )   // Screen Editor (40 columns, no CRTC, Business Keyb)
	ROM_LOAD( "901465-22.ud9", 0x6000, 0x1000, CRC(cc5298a1) SHA1(96a0fa56e0c937da92971d9c99d504e44e898806) )   // Kernal

	ROM_REGION( 0x800, "charom", 0 )
	ROM_LOAD( "901447-10.uf10", 0x000, 0x800, CRC(d8408674) SHA1(0157a2d55b7ac4eaeb38475889ebeea52e2593db) )   // Character Generator
ROM_END

#define rom_cbm4032b rom_pet4032b


//-------------------------------------------------
//  ROM( pet8032 )
//-------------------------------------------------

ROM_START( pet8032 )
	ROM_REGION( 0x7000, M6502_TAG, 0 )
	ROM_LOAD( "901465-23.ud10", 0x2000, 0x1000, CRC(ae3deac0) SHA1(975ee25e28ff302879424587e5fb4ba19f403adc) )  // BASIC 4
	ROM_LOAD( "901465-20.ud9", 0x3000, 0x1000, CRC(0fc17b9c) SHA1(242f98298931d21eaacb55fe635e44b7fc192b0a) )   // BASIC 4
	ROM_LOAD( "901465-21.ud8", 0x4000, 0x1000, CRC(36d91855) SHA1(1bb236c72c726e8fb029c68f9bfa5ee803faf0a8) )   // BASIC 4
	ROM_LOAD( "901474-03.ud7", 0x5000, 0x0800, CRC(5674dd5e) SHA1(c605fa343fd77c73cbe1e0e9567e2f014f6e7e30) )   // Screen Editor (80 columns, CRTC 60Hz, Business Keyb)
	ROM_LOAD( "901465-22.ud6", 0x6000, 0x1000, CRC(cc5298a1) SHA1(96a0fa56e0c937da92971d9c99d504e44e898806) )   // Kernal

	ROM_REGION( 0x800, "charom", 0 )
	ROM_LOAD( "901447-10.ua3", 0x000, 0x800, CRC(d8408674) SHA1(0157a2d55b7ac4eaeb38475889ebeea52e2593db) )    // Character Generator
ROM_END


//-------------------------------------------------
//  ROM( cbm8032 )
//-------------------------------------------------

ROM_START( cbm8032 )
	ROM_REGION( 0x7000, M6502_TAG, 0 )
	ROM_LOAD( "901465-23.ud10", 0x2000, 0x1000, CRC(ae3deac0) SHA1(975ee25e28ff302879424587e5fb4ba19f403adc) )  // BASIC 4
	ROM_LOAD( "901465-20.ud9", 0x3000, 0x1000, CRC(0fc17b9c) SHA1(242f98298931d21eaacb55fe635e44b7fc192b0a) )   // BASIC 4
	ROM_LOAD( "901465-21.ud8", 0x4000, 0x1000, CRC(36d91855) SHA1(1bb236c72c726e8fb029c68f9bfa5ee803faf0a8) )   // BASIC 4
	ROM_LOAD( "901474-04.ud7", 0x5000, 0x0800, CRC(abb000e7) SHA1(66887061b6c4ebef7d6efb90af9afd5e2c3b08ba) )   // Screen Editor (80 columns, CRTC 50Hz, Business Keyb)
	ROM_LOAD( "901465-22.ud6", 0x6000, 0x1000, CRC(cc5298a1) SHA1(96a0fa56e0c937da92971d9c99d504e44e898806) )   // Kernal

	ROM_REGION( 0x800, "charom", 0 )
	ROM_LOAD( "901447-10.ua3", 0x000, 0x800, CRC(d8408674) SHA1(0157a2d55b7ac4eaeb38475889ebeea52e2593db) )    // Character Generator
ROM_END

#define rom_cbm8096 rom_cbm8032


//-------------------------------------------------
//  ROM( cbm8032_de )
//-------------------------------------------------

ROM_START( cbm8032_de )
	ROM_REGION( 0x7000, M6502_TAG, 0 )
	ROM_LOAD( "901465-23.ud10", 0x2000, 0x1000, CRC(ae3deac0) SHA1(975ee25e28ff302879424587e5fb4ba19f403adc) )  // BASIC 4
	ROM_LOAD( "901465-20.ud9", 0x3000, 0x1000, CRC(0fc17b9c) SHA1(242f98298931d21eaacb55fe635e44b7fc192b0a) )   // BASIC 4
	ROM_LOAD( "901465-21.ud8", 0x4000, 0x1000, CRC(36d91855) SHA1(1bb236c72c726e8fb029c68f9bfa5ee803faf0a8) )   // BASIC 4
	ROM_LOAD( "german.bin",    0x5000, 0x0800, CRC(1c1e597d) SHA1(7ac75ed73832847623c9f4f197fe7fb1a73bb41c) )
	ROM_LOAD( "901465-22.ud6", 0x6000, 0x1000, CRC(cc5298a1) SHA1(96a0fa56e0c937da92971d9c99d504e44e898806) )   // Kernal

	ROM_REGION( 0x800, "charom", 0 )
	ROM_LOAD( "chargen.de", 0x0000, 0x800, CRC(3bb8cb87) SHA1(a4f0df13473d7f9cd31fd62cfcab11318e2fb1dc) )
ROM_END


//-------------------------------------------------
//  ROM( cbm8032_fr )
//-------------------------------------------------

ROM_START( cbm8032_fr )
	ROM_REGION( 0x7000, M6502_TAG, 0 )
	ROM_LOAD( "901465-23.ud10", 0x2000, 0x1000, CRC(ae3deac0) SHA1(975ee25e28ff302879424587e5fb4ba19f403adc) )  // BASIC 4
	ROM_LOAD( "901465-20.ud9", 0x3000, 0x1000, CRC(0fc17b9c) SHA1(242f98298931d21eaacb55fe635e44b7fc192b0a) )   // BASIC 4
	ROM_LOAD( "901465-21.ud8", 0x4000, 0x1000, CRC(36d91855) SHA1(1bb236c72c726e8fb029c68f9bfa5ee803faf0a8) )   // BASIC 4
	ROM_LOAD( "8032_editor_80c_fr_a1ab.ud7", 0x5000, 0x1000, CRC(4d3d9918) SHA1(eedac298a201a28ad86a5939b569a46f9f12c16f) )
	ROM_LOAD( "901465-22.ud6", 0x6000, 0x1000, CRC(cc5298a1) SHA1(96a0fa56e0c937da92971d9c99d504e44e898806) )   // Kernal

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "8032_chargen_80c_fr_b9c1.ua3", 0x0000, 0x1000, CRC(d7424620) SHA1(e74c0eabb921d4a34032b57a7ee61ce61ec8a10c) )
ROM_END


//-------------------------------------------------
//  ROM( cbm8032_se )
//-------------------------------------------------

ROM_START( cbm8032_se )
	ROM_REGION( 0x7000, M6502_TAG, 0 )
	ROM_LOAD( "901465-23.ud10", 0x2000, 0x1000, CRC(ae3deac0) SHA1(975ee25e28ff302879424587e5fb4ba19f403adc) )  // BASIC 4
	ROM_LOAD( "901465-20.ud9", 0x3000, 0x1000, CRC(0fc17b9c) SHA1(242f98298931d21eaacb55fe635e44b7fc192b0a) )   // BASIC 4
	ROM_LOAD( "901465-21.ud8", 0x4000, 0x1000, CRC(36d91855) SHA1(1bb236c72c726e8fb029c68f9bfa5ee803faf0a8) )   // BASIC 4
	ROM_LOAD( "8000-ud7, screen-04.ud7", 0x5000, 0x0800, CRC(75901dd7) SHA1(2ead0d83255a344a42bb786428353ca48d446d03) )
	ROM_LOAD( "901465-22.ud6", 0x6000, 0x1000, CRC(cc5298a1) SHA1(96a0fa56e0c937da92971d9c99d504e44e898806) )   // Kernal

	ROM_REGION( 0x800, "charom", 0 )
	ROM_LOAD( "901447-14.ua3", 0x0000, 0x800, CRC(48c77d29) SHA1(aa7c8ff844d16ec05e2b32acc586c58d9e35388c) )    // Character Generator
ROM_END


//-------------------------------------------------
//  ROM( superpet )
//-------------------------------------------------

ROM_START( superpet )
	ROM_REGION( 0x7000, M6502_TAG, 0 )
	ROM_LOAD( "901465-23.ud10", 0x2000, 0x1000, CRC(ae3deac0) SHA1(975ee25e28ff302879424587e5fb4ba19f403adc) )  // BASIC 4
	ROM_LOAD( "901465-20.ud9", 0x3000, 0x1000, CRC(0fc17b9c) SHA1(242f98298931d21eaacb55fe635e44b7fc192b0a) )   // BASIC 4
	ROM_LOAD( "901465-21.ud8", 0x4000, 0x1000, CRC(36d91855) SHA1(1bb236c72c726e8fb029c68f9bfa5ee803faf0a8) )   // BASIC 4
	ROM_LOAD( "901474-04.ud7", 0x5000, 0x0800, CRC(abb000e7) SHA1(66887061b6c4ebef7d6efb90af9afd5e2c3b08ba) )   // Screen Editor (80 columns, CRTC 50Hz, Business Keyb)
	ROM_LOAD( "901465-22.ud6", 0x6000, 0x1000, CRC(cc5298a1) SHA1(96a0fa56e0c937da92971d9c99d504e44e898806) )   // Kernal

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901640-01.ua3", 0x0000, 0x1000, CRC(ee8229c4) SHA1(bf346f11595a3e65e55d6aeeaa2c0cec807b66c7) )
ROM_END

#define rom_mmf9000 rom_superpet


//-------------------------------------------------
//  ROM( mmf9000_se )
//-------------------------------------------------

ROM_START( mmf9000_se )
	ROM_REGION( 0x7000, M6502_TAG, 0 )
	ROM_LOAD( "901465-23.ud10", 0x2000, 0x1000, CRC(ae3deac0) SHA1(975ee25e28ff302879424587e5fb4ba19f403adc) )  // BASIC 4
	ROM_LOAD( "901465-20.ud9", 0x3000, 0x1000, CRC(0fc17b9c) SHA1(242f98298931d21eaacb55fe635e44b7fc192b0a) )   // BASIC 4
	ROM_LOAD( "901465-21.ud8", 0x4000, 0x1000, CRC(36d91855) SHA1(1bb236c72c726e8fb029c68f9bfa5ee803faf0a8) )   // BASIC 4
	ROM_LOAD( "8000-ud7, screen-04.ud7", 0x5000, 0x0800, CRC(75901dd7) SHA1(2ead0d83255a344a42bb786428353ca48d446d03) )
	ROM_LOAD( "901465-22.ud6", 0x6000, 0x1000, CRC(cc5298a1) SHA1(96a0fa56e0c937da92971d9c99d504e44e898806) )   // Kernal

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901640-01 skand.gen.ua3", 0x0000, 0x1000, CRC(da1cd630) SHA1(35f472114ff001259bdbae073ae041b0759e32cb) )
ROM_END


//-------------------------------------------------
//  ROM( cbm8296 )
//-------------------------------------------------

ROM_START( cbm8296 )
	ROM_REGION( 0x2000, M6502_TAG, ROMREGION_ERASE00 )

	ROM_REGION( 0x4000, "basic", 0 )
	ROM_LOAD( "324746-01.ue7", 0x0000, 0x4000, CRC(03a25bb4) SHA1(e3e1431969bf317c885e47f3790e0bcbdf61fe77) )   // BASIC 4

	ROM_REGION( 0x1000, "editor", 0 )
	ROM_LOAD( "8296.ue8", 0x000, 0x800, CRC(a3475de6) SHA1(b715db83fd26458dfd254bef5c4aae636753f7f5) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901447-10.uc5", 0x000, 0x800, CRC(d8408674) SHA1(0157a2d55b7ac4eaeb38475889ebeea52e2593db) )    // Character Generator

	ROM_REGION( 0x20, "prom", 0 )
	ROM_LOAD( "74s288.uc2", 0x00, 0x20, CRC(06030665) SHA1(19dc91ca49ecc20e66c646ba480d2c3bc70a62e6) ) // video/RAM timing

	ROM_REGION( 0x10000, "ue5_eprom", 0 )
	ROM_LOAD( "ue5.bin", 0x00000, 0x10000, CRC(f70b7b37) SHA1(fe0fbb0fa71775f3780134aa11dac5b761526148) )

	ROM_REGION( 0x10000, "ue6_eprom", 0 )
	ROM_LOAD( "ue6.bin", 0x00000, 0x10000, CRC(36952256) SHA1(e94d3e744a6aaff553bf260f25da0286436265d1) )

	ROM_REGION( 0xf5, PLA1_TAG, 0 )
	ROM_LOAD( "324744-01.ue6", 0x00, 0xf5, NO_DUMP ) // 8700-009

	ROM_REGION( 0xf5, PLA2_TAG, 0 )
	ROM_LOAD( "324745-01.ue5", 0x00, 0xf5, NO_DUMP ) // 8700-008
ROM_END


//-------------------------------------------------
//  ROM( cbm8296ed )
//-------------------------------------------------

ROM_START( cbm8296ed )
	ROM_REGION( 0x2000, M6502_TAG, 0 )
	ROM_LOAD( "oracle.ue10", 0x0000, 0x1000, CRC(1ee9485d) SHA1(f876933087c9633b0fafff4d1dc198017f250267) )  // Oracle 3.03
	ROM_LOAD( "paperclip.ue9", 0x1000, 0x1000, CRC(8fb11d4b) SHA1(1c0f883cd3b8ded42ec00d83f7e7f0887f91fec0) )  // Paperclip 2.84

	ROM_REGION( 0x4000, "basic", 0 )
	ROM_LOAD( "324746-01.ue7", 0x0000, 0x4000, CRC(03a25bb4) SHA1(e3e1431969bf317c885e47f3790e0bcbdf61fe77) )   // BASIC 4

	ROM_REGION( 0x1000, "editor", 0 )
	ROM_LOAD( "execudesk.ue8", 0x0000, 0x1000, CRC(bef0eaa1) SHA1(7ea63a2d651f516e96b8725195c13542ea495ebd) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901447-10.uc5", 0x000, 0x800, CRC(d8408674) SHA1(0157a2d55b7ac4eaeb38475889ebeea52e2593db) )    // Character Generator

	ROM_REGION( 0x20, "prom", 0 )
	ROM_LOAD( "74s288.uc2", 0x00, 0x20, CRC(06030665) SHA1(19dc91ca49ecc20e66c646ba480d2c3bc70a62e6) ) // video/RAM timing

	ROM_REGION( 0x10000, "ue5_eprom", 0 )
	ROM_LOAD( "ue5.bin", 0x00000, 0x10000, CRC(f70b7b37) SHA1(fe0fbb0fa71775f3780134aa11dac5b761526148) )

	ROM_REGION( 0x10000, "ue6_eprom", 0 )
	ROM_LOAD( "ue6.bin", 0x00000, 0x10000, CRC(36952256) SHA1(e94d3e744a6aaff553bf260f25da0286436265d1) )

	ROM_REGION( 0xf5, PLA1_TAG, 0 )
	ROM_LOAD( "324744-01.ue6", 0x00, 0xf5, NO_DUMP ) // 8700-009

	ROM_REGION( 0xf5, PLA2_TAG, 0 )
	ROM_LOAD( "324745-01.ue5", 0x00, 0xf5, NO_DUMP ) // 8700-008
ROM_END


//-------------------------------------------------
//  ROM( cbm8296d )
//-------------------------------------------------

ROM_START( cbm8296d )
	ROM_REGION( 0x2000, M6502_TAG, ROMREGION_ERASE00 )

	ROM_REGION( 0x4000, "basic", 0 )
	ROM_LOAD( "324746-01.ue7", 0x0000, 0x4000, CRC(03a25bb4) SHA1(e3e1431969bf317c885e47f3790e0bcbdf61fe77) )   // BASIC 4

	ROM_REGION( 0x1000, "editor", 0 )
	ROM_LOAD( "324243-01.ue8", 0x0000, 0x1000, CRC(4000e833) SHA1(dafbdf8ba0a1fe7d7b9586ffbfc9e5390c0fcf6f) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901447-10.uc5", 0x000, 0x800, CRC(d8408674) SHA1(0157a2d55b7ac4eaeb38475889ebeea52e2593db) )    // Character Generator

	ROM_REGION( 0x20, "prom", 0 )
	ROM_LOAD( "74s288.uc2", 0x00, 0x20, CRC(06030665) SHA1(19dc91ca49ecc20e66c646ba480d2c3bc70a62e6) ) // video/RAM timing

	ROM_REGION( 0x10000, "ue5_eprom", 0 )
	ROM_LOAD( "ue5.bin", 0x00000, 0x10000, CRC(f70b7b37) SHA1(fe0fbb0fa71775f3780134aa11dac5b761526148) )

	ROM_REGION( 0x10000, "ue6_eprom", 0 )
	ROM_LOAD( "ue6.bin", 0x00000, 0x10000, CRC(36952256) SHA1(e94d3e744a6aaff553bf260f25da0286436265d1) )

	ROM_REGION( 0xf5, PLA1_TAG, 0 )
	ROM_LOAD( "324744-01.ue6", 0x00, 0xf5, NO_DUMP ) // 8700-009

	ROM_REGION( 0xf5, PLA2_TAG, 0 )
	ROM_LOAD( "324745-01.ue5", 0x00, 0xf5, NO_DUMP ) // 8700-008
ROM_END


//-------------------------------------------------
//  ROM( cbm8296d_de )
//-------------------------------------------------

ROM_START( cbm8296d_de )
	ROM_REGION( 0x2000, M6502_TAG, ROMREGION_ERASE00 )

	ROM_REGION( 0x4000, "basic", 0 )
	ROM_LOAD( "324746-01.ue7", 0x0000, 0x4000, CRC(03a25bb4) SHA1(e3e1431969bf317c885e47f3790e0bcbdf61fe77) )   // BASIC 4

	ROM_REGION( 0x1000, "editor", 0 )
	ROM_LOAD( "324243-04.ue8", 0x0000, 0x1000, CRC(3fe48897) SHA1(c218ff3168514f1d5e7822ae1b1ac3e161523b33) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "324242-10.uc5", 0x0000, 0x1000, CRC(a5632a0f) SHA1(9616f7f18757cccefb702a945f954b644d5b17d1) )

	ROM_REGION( 0x20, "prom", 0 )
	ROM_LOAD( "74s288.uc2", 0x00, 0x20, CRC(06030665) SHA1(19dc91ca49ecc20e66c646ba480d2c3bc70a62e6) ) // video/RAM timing

	ROM_REGION( 0x10000, "ue5_eprom", 0 )
	ROM_LOAD( "ue5.bin", 0x00000, 0x10000, CRC(f70b7b37) SHA1(fe0fbb0fa71775f3780134aa11dac5b761526148) )

	ROM_REGION( 0x10000, "ue6_eprom", 0 )
	ROM_LOAD( "ue6.bin", 0x00000, 0x10000, CRC(36952256) SHA1(e94d3e744a6aaff553bf260f25da0286436265d1) )

	ROM_REGION( 0xf5, PLA1_TAG, 0 )
	ROM_LOAD( "324744-01.ue6", 0x00, 0xf5, NO_DUMP ) // 8700-009

	ROM_REGION( 0xf5, PLA2_TAG, 0 )
	ROM_LOAD( "324745-01.ue5", 0x00, 0xf5, NO_DUMP ) // 8700-008
ROM_END


//-------------------------------------------------
//  ROM( cbm8296gd )
//-------------------------------------------------

ROM_START( cbm8296gd )
	ROM_REGION( 0x2000, M6502_TAG, 0 )
	ROM_LOAD( "324992-02.ue10", 0x0000, 0x1000, CRC(2bac5baf) SHA1(03aa866e4bc4e38e95983a6a82ba925e710bede8) ) // HiRes Emulator
	ROM_LOAD( "324993-02.ue9", 0x1000, 0x1000, CRC(57444531) SHA1(74aa39888a6bc95762de767fce883203daca0d34) ) // HiRes BASIC

	ROM_REGION( 0x4000, "basic", 0 )
	ROM_LOAD( "324746-01.ue7", 0x0000, 0x4000, CRC(03a25bb4) SHA1(e3e1431969bf317c885e47f3790e0bcbdf61fe77) )   // BASIC 4

	ROM_REGION( 0x1000, "editor", 0 )
	ROM_LOAD( "324243-01.ue8", 0x0000, 0x1000, CRC(4000e833) SHA1(dafbdf8ba0a1fe7d7b9586ffbfc9e5390c0fcf6f) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901447-10.uc5", 0x000, 0x800, CRC(d8408674) SHA1(0157a2d55b7ac4eaeb38475889ebeea52e2593db) )    // Character Generator

	ROM_REGION( 0x20, "prom", 0 )
	ROM_LOAD( "74s288.uc2", 0x00, 0x20, CRC(06030665) SHA1(19dc91ca49ecc20e66c646ba480d2c3bc70a62e6) ) // video/RAM timing

	ROM_REGION( 0x10000, "ue5_eprom", 0 )
	ROM_LOAD( "ue5.bin", 0x00000, 0x10000, CRC(f70b7b37) SHA1(fe0fbb0fa71775f3780134aa11dac5b761526148) )

	ROM_REGION( 0x10000, "ue6_eprom", 0 )
	ROM_LOAD( "ue6.bin", 0x00000, 0x10000, CRC(36952256) SHA1(e94d3e744a6aaff553bf260f25da0286436265d1) )

	ROM_REGION( 0xf5, PLA1_TAG, 0 )
	ROM_LOAD( "324744-01.ue6", 0x00, 0xf5, NO_DUMP ) // 8700-009

	ROM_REGION( 0xf5, PLA2_TAG, 0 )
	ROM_LOAD( "324745-01.ue5", 0x00, 0xf5, NO_DUMP ) // 8700-008
ROM_END


//-------------------------------------------------
//  ROM( cbm8296dgv_de )
//-------------------------------------------------

ROM_START( cbm8296dgv_de ) // SER.NO.WG 8947
	ROM_REGION( 0x2000, M6502_TAG, 0 )
	ROM_LOAD( "io gv.ue9", 0x1000, 0x1000, CRC(7adf50a0) SHA1(4f7abc5286e51f34cde98238410274715e766b31) ) // I/O MASTER (C)1982 J.PFEIFER

	ROM_REGION( 0x4000, "basic", 0 )
	ROM_LOAD( "324746-01.ue7", 0x0000, 0x4000, CRC(03a25bb4) SHA1(e3e1431969bf317c885e47f3790e0bcbdf61fe77) )

	ROM_REGION( 0x1000, "editor", 0 )
	ROM_LOAD( "ue8gv.ue8", 0x0000, 0x1000, CRC(8ad1fca9) SHA1(3c939092e51549696754c308b2a09f47c5d4d277) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "ua3gv.uc5", 0x000, 0x1000, CRC(d8035dc4) SHA1(cdf520a7dabf1b18aed15455b1dbefac15ff91f3) )

	ROM_REGION( 0x20, "prom", 0 )
	ROM_LOAD( "74s288.uc2", 0x00, 0x20, CRC(06030665) SHA1(19dc91ca49ecc20e66c646ba480d2c3bc70a62e6) )

	ROM_REGION( 0x10000, "ue5_eprom", 0 )
	ROM_LOAD( "ue5.bin", 0x00000, 0x10000, CRC(f70b7b37) SHA1(fe0fbb0fa71775f3780134aa11dac5b761526148) )

	ROM_REGION( 0x10000, "ue6_eprom", 0 )
	ROM_LOAD( "ue6.bin", 0x00000, 0x10000, CRC(36952256) SHA1(e94d3e744a6aaff553bf260f25da0286436265d1) )

	ROM_REGION( 0xf5, PLA1_TAG, 0 )
	ROM_LOAD( "324744-01.ue6", 0x00, 0xf5, NO_DUMP ) // 8700-009

	ROM_REGION( 0xf5, PLA2_TAG, 0 )
	ROM_LOAD( "324745-01.ue5", 0x00, 0xf5, NO_DUMP ) // 8700-008
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME           PARENT    COMPAT  MACHINE     INPUT    CLASS           INIT        COMPANY                        FULLNAME        FLAGS
COMP( 1977, pet2001,       0,        0,      pet2001,    pet,     pet_state,      empty_init, "Commodore Business Machines", "PET 2001-4",   MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1977, pet20018,      pet2001,  0,      pet20018,   pet,     pet_state,      empty_init, "Commodore Business Machines", "PET 2001-8",   MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1979, pet2001n,      0,        0,      pet2001n8,  pet,     pet_state,      empty_init, "Commodore Business Machines", "PET 2001-N8",  MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1979, pet2001n16,    pet2001n, 0,      pet2001n16, pet,     pet_state,      empty_init, "Commodore Business Machines", "PET 2001-N16", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1979, pet2001n32,    pet2001n, 0,      pet2001n32, pet,     pet_state,      empty_init, "Commodore Business Machines", "PET 2001-N32", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1979, cbm3008,       pet2001n, 0,      cbm3008,    pet,     pet_state,      empty_init, "Commodore Business Machines", "CBM 3008",     MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1979, cbm3016,       pet2001n, 0,      cbm3016,    pet,     pet_state,      empty_init, "Commodore Business Machines", "CBM 3016",     MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1979, cbm3032,       pet2001n, 0,      cbm3032,    pet,     pet_state,      empty_init, "Commodore Business Machines", "CBM 3032",     MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1979, pet2001b,      0,        0,      pet2001b8,  petb,    pet2001b_state, empty_init, "Commodore Business Machines", "PET 2001-B8",  MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1979, pet2001b16,    pet2001b, 0,      pet2001b16, petb,    pet2001b_state, empty_init, "Commodore Business Machines", "PET 2001-B16", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1979, pet2001b32,    pet2001b, 0,      pet2001b32, petb,    pet2001b_state, empty_init, "Commodore Business Machines", "PET 2001-B32", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1979, cbm3032b,      pet2001b, 0,      cbm3032b,   petb,    pet2001b_state, empty_init, "Commodore Business Machines", "CBM 3032B",    MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1980, pet4016,       0,        0,      pet4016,    pet,     pet2001b_state, empty_init, "Commodore Business Machines", "PET 4016",     MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1980, pet4032,       pet4016,  0,      pet4032,    pet,     pet2001b_state, empty_init, "Commodore Business Machines", "PET 4032",     MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1980, pet4032f,      pet4016,  0,      pet4032f,   pet,     pet2001b_state, empty_init, "Commodore Business Machines", "PET 4032 (Fat 40)",     MACHINE_SUPPORTS_SAVE )
COMP( 1980, cbm4016,       pet4016,  0,      cbm4016,    pet,     pet_state,      empty_init, "Commodore Business Machines", "CBM 4016",     MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1980, cbm4032,       pet4016,  0,      cbm4032,    pet,     pet_state,      empty_init, "Commodore Business Machines", "CBM 4032",     MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1980, cbm4032f,      pet4016,  0,      cbm4032f,   pet,     pet_state,      empty_init, "Commodore Business Machines", "CBM 4032 (Fat 40)",     MACHINE_SUPPORTS_SAVE )
COMP( 1980, pet4032b,      0,        0,      pet4032b,   petb,    pet2001b_state, empty_init, "Commodore Business Machines", "PET 4032B",    MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1980, cbm4032b,      pet4032b, 0,      cbm4032b,   petb,    pet2001b_state, empty_init, "Commodore Business Machines", "CBM 4032B",    MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1980, pet8032,       0,        0,      pet8032,    petb,    pet80_state,    empty_init, "Commodore Business Machines", "PET 8032",     MACHINE_SUPPORTS_SAVE )
COMP( 1981, cbm8032,       pet8032,  0,      pet8032,    petb,    pet80_state,    empty_init, "Commodore Business Machines", "CBM 8032",     MACHINE_SUPPORTS_SAVE )
COMP( 1981, cbm8032_de,    pet8032,  0,      pet8032,    petb_de, pet80_state,    empty_init, "Commodore Business Machines", "CBM 8032 (Germany)",        MACHINE_SUPPORTS_SAVE )
COMP( 1981, cbm8032_fr,    pet8032,  0,      pet8032,    petb_fr, pet80_state,    empty_init, "Commodore Business Machines", "CBM 8032 (France)",         MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1981, cbm8032_se,    pet8032,  0,      pet8032,    petb_se, pet80_state,    empty_init, "Commodore Business Machines", "CBM 8032 (Sweden/Finland)", MACHINE_SUPPORTS_SAVE )
COMP( 1981, superpet,      pet8032,  0,      superpet,   petb,    superpet_state, empty_init, "Commodore Business Machines", "SuperPET SP-9000",          MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1981, mmf9000,       pet8032,  0,      superpet,   petb,    superpet_state, empty_init, "Commodore Business Machines", "MicroMainFrame 9000",       MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1981, mmf9000_se,    pet8032,  0,      superpet,   petb_se, superpet_state, empty_init, "Commodore Business Machines", "MicroMainFrame 9000 (Sweden/Finland)",         MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1981, cbm8096,       pet8032,  0,      cbm8096,    petb,    cbm8096_state,  empty_init, "Commodore Business Machines", "CBM 8096",     MACHINE_SUPPORTS_SAVE )
COMP( 1984, cbm8296,       0,        0,      cbm8296,    petb,    cbm8296_state,  empty_init, "Commodore Business Machines", "CBM 8296",     MACHINE_SUPPORTS_SAVE )
COMP( 1984, cbm8296ed,     cbm8296,  0,      cbm8296d,   petb,    cbm8296_state,  empty_init, "Commodore Business Machines", "CBM 8296 ExecuDesk",        MACHINE_SUPPORTS_SAVE )
COMP( 1984, cbm8296d,      cbm8296,  0,      cbm8296d,   petb,    cbm8296_state,  empty_init, "Commodore Business Machines", "CBM 8296-D",   MACHINE_SUPPORTS_SAVE )
COMP( 1984, cbm8296d_de,   cbm8296,  0,      cbm8296d,   petb_de, cbm8296_state,  empty_init, "Commodore Business Machines", "CBM 8296-D (Germany)",      MACHINE_SUPPORTS_SAVE )
COMP( 1984, cbm8296gd,     cbm8296,  0,      cbm8296d,   petb,    cbm8296_state,  empty_init, "Commodore Business Machines", "CBM 8296GD",   MACHINE_SUPPORTS_SAVE )
COMP( 1984, cbm8296dgv_de, cbm8296,  0,      cbm8296d,   petb,    cbm8296_state,  empty_init, "Commodore Business Machines", "CBM 8296-D GV? (Germany)",  MACHINE_SUPPORTS_SAVE )



pg685.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: rfka01
/***************************************************************************

    Siemens Simatic PG-675 and PG-685

    driver skeleton by rfka01
    more skeleton by R. Belmont

****************************************************************************

The Simatic PG-685 is a programming device for Siemens' S5 line of industrial
controllers. They ran PCP/M-86 and MS-DOS specially adapted for the architecture.

http://oldcomputer.info/portables/pg685/index.htm

The portable case contains a monochrome monitor (with a socket provided to
drive an external monitor), a 5,25" floppy drive with 720KB capacity (DS,80
tracks, 9 sectors p.t., 512 Byters p.s.) and a MFM hard disk drive.
The PC is made up of several boards on a non-ISA bus backplane.

There are at least two versions and several options. The PG-685's settings are
contained in NVRAM, and have to be updated using a testdisk if the two AA
batteries run out.

For this, a key switch with a reset setting plays a crucial role. Set the key
to reset, insert disk in drive but don't close. Switch on machine, close drive
and set the switch to normal operation to start the setup.

Backplane: SCN2661B, D8253C-2, SAB 8259AP


6ES5685-OUA11

John Elliott's kindly analyzed the ROM of this machine; his findings are represented
in the preliminary memory map.

This machine only has a textmode screen, Tandon TM262 hard disk drive on a WD1010 controller,
Teac FD-55FV-13-U floppy drive on a Siemens (WD)-1797-02P controller, 768KB of RAM, HD68A45SP
display controller, upd8279c-25 keyboard controller.
Ports: Printer, V24, Module, AG-S5, Sinec H1, External Monitor

CPU/Video:      16KB BIOS/CHAR EPROM, NEC V20 CPU, SAB 8259AP, 12.288 MHz crystal, 2xHM6116LP-3,
                HD46505SP-1 (HD68A45SP), D8279C-2, D8251AFC
Module/Floppy:  2xP8255A, 4xHM6116LP-3, D8251AFC, 4.000000 MHz crystal, SAB 1797-02P, MM58167AN
HD:             4xD4016C, WD1010A-AL, 10,000000 MHz crystal
Memory:         27xTMS27C256-15, 9 empty sockets, 36 unsoldered pads


6ES5685-OUA12

This machine has the BMG (bit mapped graphics) option, that John Elliott described as a memory mapped
hercules card. There is a GEM/3 display driver that was indeed derived from the Hercules one.
The screen buffer starts at E000, the video card is at F9F0:80h, the beeper frequency at F9F0:36h,
the serial port at F9F0:38h.

Graphics screen, MiniScribe 8425 hard disk drive on a WD2010B-AL controller, Teac FD-55FR 511-U floppy drive
on a Siemens (WD)-1797-02P controller, 896KB of RAM, HD68A45SP display controller, upd8279c-25
keyboard controller
Ports: Printer, V24, Module, AG-S5, Sinec H1, External Monitor, E1

CPU/Mem.:       iR80286-10 CPU, N82C288, 19,660800 MHz crystal, 2x16KB EPROM (BIOS/CHAR), 24MHz crystal
                18.189 MHz crystal, D71059L, HD46505SP-1 (HD68A45SP), D8279C-2, N8251A, 2xSRM20256LM,
                RAM daughterbd:    4x514256-10
Module/Floppy:  2xi8255A, 4xHM6116LP-3, D8251AFC, 4.000000 MHz crystal, SAB 1797-02P, MM58167AN
HD:             SRM2064C-15, WD2010B-AL, 10,000000 MHz crystal


6ES5675-OUA11

The PG-675 shares the housing with the PG-685, but uses dual 48 tpi floppy drives instead of the harddisk/96 tpi
drive combo.

CPU/Video:      8KB BIOS/CHAR EPROM, Intel 8088 CPU, SAB 8259AP, 12,288 MHz crystal, 2xHM6116LP-3,
                HD46505SP-1 (HD68A45SP), D8279C-5, D8251AFC
Module/Floppy:  Crystal 4.000 MHz, SAB 1797-02P, 2xP8255A, MM58167AN, 4xHM6116LP-3, D8251AFC
Memory:         54x 64KBit RAM, 18 empty sockets, 9 bit and 4 bit wire straps

****************************************************************************/

#include "emu.h"
#include "cpu/i86/i286.h"
#include "cpu/i86/i86.h"
#include "cpu/nec/nec.h"
#include "imagedev/floppy.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/i8279.h"
#include "machine/mm58167.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/scn_pci.h"
#include "machine/wd2010.h"
#include "machine/wd_fdc.h"
#include "video/mc6845.h"
#include "screen.h"


namespace {

//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class pg685_state : public driver_device
{
public:
	pg685_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_vram(*this, "framebuffer"),
		m_vram16(*this, "framebuffer16"),
		m_fontram(*this, "charcopy"),
		m_bppit(*this, "bppit"),
		m_fdc(*this, "fdc"),
		m_floppy0(*this, "fdc:0"),
		m_floppy1(*this, "fdc:1")
		{ }

	void pg685_backplane(machine_config &config);
	void pg685_module(machine_config &config);
	void pg685(machine_config &config);
	void pg675(machine_config &config);
	void pg685oua12(machine_config &config);

private:
	MC6845_UPDATE_ROW(crtc_update_row);
	MC6845_UPDATE_ROW(crtc_update_row_oua12);

	uint8_t f9f04_r();
	void f9f04_w(uint8_t data);
	uint8_t f9f24_r();
	void f9f24_w(uint8_t data);
	void f9f32_w(uint8_t data);
	uint8_t f9f33_r();
	void f9f3e_w(uint8_t data);
	uint8_t f9f3f_r();
	uint8_t f9f78_r();
	void f9f78_w(uint8_t data);
	void f9f79_w(uint8_t data);
	void fdc_drq_w(int state);
	void fdc_intrq_w(int state);

	void pg675_mem(address_map &map) ATTR_COLD;
	void pg685_mem(address_map &map) ATTR_COLD;
	void pg685oua12_mem(address_map &map) ATTR_COLD;

	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	optional_shared_ptr<uint8_t> m_vram;
	optional_shared_ptr<uint16_t> m_vram16;
	optional_shared_ptr<uint8_t> m_fontram;
	optional_device<pit8253_device> m_bppit;
	required_device<fd1797_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	optional_device<floppy_connector> m_floppy1;
};

//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void pg685_state::pg675_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0xbffff).ram();
	map(0xf0000, 0xf1fff).ram();
	map(0xf9f00, 0xf9f01).rw("kbdc", FUNC(i8279_device::read), FUNC(i8279_device::write));
	map(0xf9f02, 0xf9f02).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0xf9f03, 0xf9f03).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0xf9f04, 0xf9f04).rw(FUNC(pg685_state::f9f04_r), FUNC(pg685_state::f9f04_w));
	map(0xf9f06, 0xf9f07).rw("mainpic", FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0xf9f08, 0xf9f09).rw("mainuart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xf9f20, 0xf9f23).rw(m_fdc, FUNC(fd1797_device::read), FUNC(fd1797_device::write));
	map(0xf9f24, 0xf9f24).rw(FUNC(pg685_state::f9f24_r), FUNC(pg685_state::f9f24_w));
	map(0xf9f28, 0xf9f2b).rw("modppi1", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xf9f2c, 0xf9f2f).rw("modppi2", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xf9f30, 0xf9f31).rw("moduart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xf9f32, 0xf9f32).w(FUNC(pg685_state::f9f32_w));
	map(0xf9f33, 0xf9f33).r(FUNC(pg685_state::f9f33_r));
	map(0xf9f40, 0xf9f5f).rw("rtc", FUNC(mm58167_device::read), FUNC(mm58167_device::write));
	map(0xfa000, 0xfa7ff).ram().share("charcopy");
	map(0xfb000, 0xfb7ff).ram().share("framebuffer");
	map(0xfc000, 0xfffff).rom().region("bios", 0);
}

void pg685_state::pg685_mem(address_map &map)
{
	map.unmap_value_high();
	pg675_mem(map);
	map(0xf9f34, 0xf9f37).rw(m_bppit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xf9f38, 0xf9f3b).rw("bpuart", FUNC(scn2661b_device::read), FUNC(scn2661b_device::write));
	map(0xf9f3c, 0xf9f3d).rw("bppic", FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0xf9f3e, 0xf9f3e).w(FUNC(pg685_state::f9f3e_w));
	map(0xf9f70, 0xf9f77).rw("hdc", FUNC(wd2010_device::read), FUNC(wd2010_device::write));
	map(0xf9f78, 0xf9f78).rw(FUNC(pg685_state::f9f78_r), FUNC(pg685_state::f9f78_w));
	map(0xf9f79, 0xf9f79).w(FUNC(pg685_state::f9f79_w));
}

void pg685_state::pg685oua12_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0xdffff).ram();
	map(0xe0000, 0xeffff).ram().share("framebuffer16");
	map(0xf0000, 0xf1fff).ram();
	map(0xf9f00, 0xf9f01).rw("kbdc", FUNC(i8279_device::read), FUNC(i8279_device::write));
	map(0xf9f04, 0xf9f04).rw(FUNC(pg685_state::f9f04_r), FUNC(pg685_state::f9f04_w));
	map(0xf9f06, 0xf9f07).rw("mainpic", FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0xf9f08, 0xf9f09).rw("mainuart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xf9f20, 0xf9f23).rw(m_fdc, FUNC(fd1797_device::read), FUNC(fd1797_device::write));
	map(0xf9f24, 0xf9f24).rw(FUNC(pg685_state::f9f24_r), FUNC(pg685_state::f9f24_w));
	map(0xf9f28, 0xf9f2b).rw("modppi1", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xf9f2c, 0xf9f2f).rw("modppi2", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xf9f30, 0xf9f31).rw("moduart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xf9f32, 0xf9f32).w(FUNC(pg685_state::f9f32_w));
	map(0xf9f33, 0xf9f33).r(FUNC(pg685_state::f9f33_r));
	map(0xf9f34, 0xf9f37).rw(m_bppit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xf9f38, 0xf9f3b).rw("bpuart", FUNC(scn2661b_device::read), FUNC(scn2661b_device::write));
	map(0xf9f3c, 0xf9f3d).rw("bppic", FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0xf9f3e, 0xf9f3e).w(FUNC(pg685_state::f9f3e_w));
	map(0xf9f3f, 0xf9f3f).r(FUNC(pg685_state::f9f3f_r));
	map(0xf9f40, 0xf9f5f).rw("rtc", FUNC(mm58167_device::read), FUNC(mm58167_device::write));
	map(0xf9f70, 0xf9f77).rw("hdc", FUNC(wd2010_device::read), FUNC(wd2010_device::write));
	map(0xf9f78, 0xf9f78).rw(FUNC(pg685_state::f9f78_r), FUNC(pg685_state::f9f78_w));
	map(0xf9f79, 0xf9f79).w(FUNC(pg685_state::f9f79_w));
	map(0xf9f80, 0xf9f80).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0xf9f81, 0xf9f81).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0xfc000, 0xfffff).ram();    // BIOS RAM shadow
	map(0xffc000, 0xffffff).rom().region("bios", 0);
}


//**************************************************************************
//  I/O
//*************************************************************************

static INPUT_PORTS_START( pg685 )
INPUT_PORTS_END

uint8_t pg685_state::f9f04_r()
{
	// PCP/M-86 keyboard handling code also checks a couple of bits read
	logerror("Reading byte from F9F04\n");
	return 0xff;
}

void pg685_state::f9f04_w(uint8_t data)
{
	// PCP/M-86 keyboard handling code also checks a couple of bits read
	logerror("Writing %02X to F9F04\n", data);
}

void pg685_state::f9f32_w(uint8_t data)
{
	// 1D written at startup
	logerror("Writing %02X to F9F32\n", data);
}

uint8_t pg685_state::f9f33_r()
{
	// Printer present?
	logerror("Reading from F9F33\n");
	return 0xff;
}

void pg685_state::f9f3e_w(uint8_t data)
{
	m_bppit->write_gate0(BIT(data, 6));
	m_bppit->write_gate1(BIT(data, 7));

	// On PC16-11, D5 is AND-ed with the PIT's OUT2, and other bits are used to select the baud rate for a 8251.
}

uint8_t pg685_state::f9f3f_r()
{
	logerror("Reading from F9F3F\n");
	return 0xff;
}

//**************************************************************************
//  FLOPPY
//**************************************************************************

static void pg675_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

static void pg685_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}


uint8_t pg685_state::f9f24_r()
{
	logerror("Reading from F9F24\n");
	return 0xff;
}

void pg685_state::f9f24_w(uint8_t data)
{
	logerror("Writing %02X to F9F24\n", data);
}


//**************************************************************************
//  HARDDISK
//**************************************************************************

uint8_t pg685_state::f9f78_r()
{
	logerror("Reading from F9F78\n");
	return 0xff;
}

void pg685_state::f9f78_w(uint8_t data)
{
	// WD 1010 separate drive/head select register
	logerror("Writing %02X to F9F78\n", data);
}

void pg685_state::f9f79_w(uint8_t data)
{
	// another write-only register (possibly reset or interrupt control)
	logerror("Writing %02X to F9F79\n", data);
}

//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void pg685_state::machine_reset()
{
}

void pg685_state::video_start()
{
}

MC6845_UPDATE_ROW( pg685_state::crtc_update_row )
{
	static const uint32_t palette[2] = { 0x00d000, 0 };
	uint32_t  *p = &bitmap.pix(y);
	uint16_t  chr_base = ra;
	uint8_t const *const vram = (uint8_t *)m_vram.target();
	uint8_t const *const fontram = (uint8_t *)m_fontram.target();

	for ( int i = 0; i < x_count; i++ )
	{
		uint16_t offset = ( ma + i ) & 0x7ff;
		uint8_t chr = vram[ offset ];
		uint8_t data = fontram[ chr_base + chr * 16 ];
		uint8_t fg = 1;
		uint8_t bg = 0;

		*p++ = palette[( data & 0x80 ) ? fg : bg];
		*p++ = palette[( data & 0x40 ) ? fg : bg];
		*p++ = palette[( data & 0x20 ) ? fg : bg];
		*p++ = palette[( data & 0x10 ) ? fg : bg];
		*p++ = palette[( data & 0x08 ) ? fg : bg];
		*p++ = palette[( data & 0x04 ) ? fg : bg];
		*p++ = palette[( data & 0x02 ) ? fg : bg];
		*p++ = palette[( data & 0x01 ) ? fg : bg];
	}
}

MC6845_UPDATE_ROW( pg685_state::crtc_update_row_oua12 )
{
	static const uint32_t palette[2] = { 0x00d000, 0 };
	uint32_t  *p = &bitmap.pix(y);
	uint16_t  chr_base = ra;
	uint16_t const *const vram = (uint16_t *)m_vram16.target();
	uint8_t const *const fontram = (uint8_t *)memregion("chargen")->base();

	for ( int i = 0; i < x_count; i++ )
	{
		uint16_t offset = ( ma + i ) & 0x7ff;
		uint16_t chr = vram[ offset ] & 0xff;
		uint8_t data = fontram[ chr_base + chr * 16 ];
		uint8_t fg = 1;
		uint8_t bg = 0;

		*p++ = palette[( data & 0x80 ) ? fg : bg];
		*p++ = palette[( data & 0x40 ) ? fg : bg];
		*p++ = palette[( data & 0x20 ) ? fg : bg];
		*p++ = palette[( data & 0x10 ) ? fg : bg];
		*p++ = palette[( data & 0x08 ) ? fg : bg];
		*p++ = palette[( data & 0x04 ) ? fg : bg];
		*p++ = palette[( data & 0x02 ) ? fg : bg];
		*p++ = palette[( data & 0x01 ) ? fg : bg];
	}
}

//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

void pg685_state::pg685_backplane(machine_config &config)
{
	PIT8253(config, m_bppit, 0);
	m_bppit->set_clk<0>(XTAL(12'288'000) / 10); // same input clock as for PC16-11?
	m_bppit->set_clk<1>(XTAL(12'288'000) / 10);
	m_bppit->set_clk<2>(XTAL(12'288'000) / 10);

	pic8259_device &bppic(PIC8259(config, "bppic", 0));
	bppic.out_int_callback().set_nop(); // configured in single 8086 mode?

	SCN2661B(config, "bpuart", 4915200);
}

void pg685_state::pg685_module(machine_config &config)
{
	FD1797(config, m_fdc, XTAL(4'000'000) / 2); // divider guessed
	m_fdc->intrq_wr_callback().set("mainpic", FUNC(pic8259_device::ir4_w));

	I8255(config, "modppi1", 0);
	I8255(config, "modppi2", 0);

	I8251(config, "moduart", XTAL(4'000'000) / 2); // divider guessed

	MM58167(config, "rtc", XTAL(32'768));
}

void pg685_state::pg675(machine_config &config)
{
	// main cpu
	I8088(config, m_maincpu, XTAL(15'000'000) / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &pg685_state::pg675_mem);
	m_maincpu->set_irq_acknowledge_callback("mainpic", FUNC(pic8259_device::inta_cb));

	pic8259_device &mainpic(PIC8259(config, "mainpic", 0));
	mainpic.out_int_callback().set_inputline(m_maincpu, 0);
	mainpic.in_sp_callback().set_constant(1);

	// i/o cpu

	// ram

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(12288000, 882, 0, 720, 370, 0, 350 ); // not real values
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	mc6845_device &crtc(MC6845(config, "crtc", 12288000));
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(8);
	crtc.set_update_row_callback(FUNC(pg685_state::crtc_update_row));

	// sound hardware

	// devices
	pg685_module(config);

	I8251(config, "mainuart", XTAL(12'288'000) / 6); // divider guessed

	// rs232 port

	// keyboard
	i8279_device &kbdc(I8279(config, "kbdc", XTAL(12'288'000) / 6)); // divider guessed
	kbdc.out_irq_callback().set("mainpic", FUNC(pic8259_device::ir0_w));

	// printer

	// floppy
	// m_fdc->intrq_wr_callback(FUNC(zorba_state::fdc_intrq_w));
	// m_fdc->drq_wr_callback(FUNC(zorba_state::fdc_drq_w));
	FLOPPY_CONNECTOR(config, "fdc:0", pg675_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", pg675_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
}

void pg685_state::pg685(machine_config &config)
{
	// main cpu
	V20(config, m_maincpu, XTAL(15'000'000) / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &pg685_state::pg685_mem);
	m_maincpu->set_irq_acknowledge_callback("mainpic", FUNC(pic8259_device::inta_cb));

	pic8259_device &mainpic(PIC8259(config, "mainpic", 0));
	mainpic.out_int_callback().set_inputline(m_maincpu, 0);
	mainpic.in_sp_callback().set_constant(1);

	// i/o cpu

	// ram

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(12288000, 882, 0, 720, 370, 0, 350 ); // not real values
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	mc6845_device &crtc(MC6845(config, "crtc", 12288000));
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(8);
	crtc.set_update_row_callback(FUNC(pg685_state::crtc_update_row));

	// sound hardware

	// devices
	pg685_backplane(config);
	pg685_module(config);

	I8251(config, "mainuart", XTAL(12'288'000) / 6); // divider guessed

	// rs232 port

	// keyboard
	i8279_device &kbdc(I8279(config, "kbdc", XTAL(12'288'000) / 6)); // divider guessed
	kbdc.out_irq_callback().set("mainpic", FUNC(pic8259_device::ir0_w));

	// printer

	// floppy

	// m_fdc->drq_wr_callback(FUNC(zorba_state::fdc_drq_w));
	FLOPPY_CONNECTOR(config, "fdc:0", pg685_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	// harddisk
	wd2010_device& hdc(WD2010(config, "hdc", XTAL(10'000'000) / 2)); // divider guessed
	hdc.out_intrq_callback().set("mainpic", FUNC(pic8259_device::ir3_w));
}

void pg685_state::pg685oua12(machine_config &config)
{
	// main cpu
	I80286(config, m_maincpu, XTAL(20'000'000) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &pg685_state::pg685oua12_mem);
	m_maincpu->set_irq_acknowledge_callback("mainpic", FUNC(pic8259_device::inta_cb));

	pic8259_device &mainpic(PIC8259(config, "mainpic", 0));
	mainpic.out_int_callback().set_inputline(m_maincpu, 0);
	mainpic.in_sp_callback().set_constant(1);

	// i/o cpu

	// ram

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(12288000, 882, 0, 720, 370, 0, 350 ); // not real values
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	mc6845_device &crtc(MC6845(config, "crtc", 12288000));
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(8);
	crtc.set_update_row_callback(FUNC(pg685_state::crtc_update_row_oua12));

	// sound hardware

	// devices
	pg685_backplane(config);
	pg685_module(config);

	I8251(config, "mainuart", 12288000 / 6); // wrong

	// rs232 port

	// keyboard
	i8279_device &kbdc(I8279(config, "kbdc", 12288000 / 6)); // wrong
	kbdc.out_irq_callback().set("mainpic", FUNC(pic8259_device::ir0_w));

	// printer

	// floppy
	// m_fdc->drq_wr_callback(FUNC(zorba_state::fdc_drq_w));
	FLOPPY_CONNECTOR(config, "fdc:0", pg685_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	// harddisk
	wd2010_device& hdc(WD2010(config, "hdc", XTAL(10'000'000) / 2)); // divider guessed
	hdc.out_intrq_callback().set("mainpic", FUNC(pic8259_device::ir3_w));
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( pg675 )
	ROM_REGION( 0x4000, "bios", ROMREGION_ERASEFF )
	ROM_LOAD( "p79004-a7021 a2-a1.bin", 0x2000, 0x2000, CRC(c7602d28) SHA1(a470e0457cc83f989995cfbca1ebce0878a3c4e3) )
ROM_END

ROM_START( pg685 )
	ROM_REGION( 0x4000, "bios", ROMREGION_ERASEFF )
	ROM_LOAD( "pg685_oua11_s79200-g2_a901-03.bin", 0x0000, 0x4000, CRC(db13f2db) SHA1(5f65ab14d9c8acdcc5482b27e727ca43b1a7daf3) )
ROM_END

ROM_START( pg685oua12 )
	ROM_REGION16_LE( 0x4000, "bios", ROMREGION_ERASEFF )
	ROM_LOAD( "pg685_oua12_bios.bin", 0x0000, 0x4000, CRC(94b8499b) SHA1(e29086a88f1f9fa17921c3d157cce725d4591328))

	ROM_REGION( 0x4000, "chargen", 0 )
	ROM_LOAD( "pg685_oua12_s79200-g39_a901-01.bin", 0x0000, 0x4000, CRC(fa722110) SHA1(b57ee67a77ff45a2544a2ae5203bc2199adfe023))
ROM_END

} // anonymous namespace


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************
//    YEAR  NAME        PARENT  COMPAT  MACHINE     INPUT  CLASS        INIT        COMPANY    FULLNAME               FLAGS
COMP( 198?, pg675,      0,      0,      pg675,      pg685, pg685_state, empty_init, "Siemens", "Simatic PG675",       MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 198?, pg685,      0,      0,      pg685,      pg685, pg685_state, empty_init, "Siemens", "Simatic PG685 OUA11", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 198?, pg685oua12, pg685,  0,      pg685oua12, pg685, pg685_state, empty_init, "Siemens", "Simatic PG685 OUA12", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



phantom.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap, Sandro Ronco
// thanks-to:Berger
/*******************************************************************************

Fidelity Phantom (model 6100)

Fidelity licensed (or perhaps bought) the design of Milton Bradley's Grand·Master
motorized chessboard and released their own version. It has a small LCD panel added,
the rest looks nearly the same from the outside. After Fidelity was taken over by H+G,
it was rereleased in 1990 as the Mephisto Phantom. This is assumed to be identical.

At boot-up, the computer will do a self-test, the user can start playing after the
motor has moved to the upper-right corner. The computer will continue positioning
pieces though, so it may be a bit distracting. Or, just hold INSERT (on PC) for a
while to speed up MAME before starting a new game.

After the user captures a piece, select the captured piece from the MAME sensorboard
spawn block and place it anywhere on a free spot at the designated box at the
edge of the chessboard.

Hardware notes:
- PCB label: 510.1128A01
- R65C02P4, XTAL marked 4.915200
- 2*32KB ROM 27C256-15, 8KB RAM MS6264L-10
- LCD driver, display panel for digits
- magnetized x/y DC motors under chessboard, chesspieces have magnet underneath
- piezo speaker, LEDs, 8*8 chessboard buttons

Chesster Phantom is on the same base hardware, and adds the Chesster voice to it,
using the same ROM as the original Chesster. Model 6124 extra hardware is on a
daughterboard, the housing is the same as model 6100, except for button labels.
Model 6126 has a dedicated PCB, this version also has a motion sensor at the front
and 2 leds to mimic eyes, and the housing color theme is green instead of beige.

TODO:
- sensorboard undo buffer goes out of control, probably not worth solving this issue

BTANB:
- cphantom: As the manual suggests, the computer's move should be displayed on the
  LCD while it's moving the piece just like in fphantom, but this is often too brief
  or not displayed at all. (This may seem like a minor bug in the game, but it
  actually makes it more difficult to write a MAME UCI plugin for this driver.)

*******************************************************************************/

#include "emu.h"

#include "cpu/m6502/r65c02.h"
#include "machine/sensorboard.h"
#include "machine/timer.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "fidel_cphantom.lh"
#include "fidel_phantom.lh"


namespace {

// Phantom 6100 / shared

class phantom_state : public driver_device
{
public:
	phantom_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_rombank(*this, "rombank"),
		m_dac(*this, "dac"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_inputs(*this, "IN.%u", 0),
		m_piece_hand(*this, "cpu_hand"),
		m_out_motor(*this, "motor%u", 0U),
		m_out_pos(*this, "pos_%c", unsigned('x'))
	{ }

	void phantom(machine_config &config);
	void init_phantom();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_memory_bank m_rombank;
	optional_device<dac_1bit_device> m_dac;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	optional_ioport_array<2> m_inputs;
	output_finder<> m_piece_hand;
	output_finder<5> m_out_motor;
	output_finder<2> m_out_pos;

	u8 m_mux = 0;
	u8 m_select = 0;
	u32 m_lcd_data = 0;

	u8 m_motors_ctrl = 0;
	int m_hmotor_pos = 0;
	int m_vmotor_pos = 0;
	bool m_vmotor_sensor0_ff = false;
	bool m_vmotor_sensor1_ff = false;
	bool m_hmotor_sensor0_ff = false;
	bool m_hmotor_sensor1_ff = false;
	u8 m_pieces_map[0x80][0x80] = { };

	// address maps
	virtual void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_lcd(u8 select);
	virtual void control_w(offs_t offset, u8 data);
	void lcd_w(offs_t offset, u8 data);
	void motors_w(u8 data);
	virtual u8 input_r(offs_t offset);
	u8 motors_r(offs_t offset);
	u8 irq_ack_r();
	u8 hmotor_ff_clear_r();
	u8 vmotor_ff_clear_r();

	void init_board(u8 data);
	void clear_board(u8 data);
	void check_rotation();
	TIMER_DEVICE_CALLBACK_MEMBER(motors_timer);
	void update_pieces_position(int state);
	void output_magnet_pos();
};

void phantom_state::machine_start()
{
	m_hmotor_pos = 0x23;
	m_vmotor_pos = 0x0e;

	// resolve outputs
	m_piece_hand.resolve();
	m_out_motor.resolve();
	m_out_pos.resolve();

	// register for savestates
	save_item(NAME(m_mux));
	save_item(NAME(m_select));
	save_item(NAME(m_lcd_data));
	save_item(NAME(m_motors_ctrl));
	save_item(NAME(m_hmotor_pos));
	save_item(NAME(m_vmotor_pos));
	save_item(NAME(m_vmotor_sensor0_ff));
	save_item(NAME(m_vmotor_sensor1_ff));
	save_item(NAME(m_hmotor_sensor0_ff));
	save_item(NAME(m_hmotor_sensor1_ff));
	save_item(NAME(m_pieces_map));
}

void phantom_state::machine_reset()
{
	m_rombank->set_entry(0);
	output_magnet_pos();
}

void phantom_state::init_board(u8 data)
{
	m_board->preset_chess(data);

	// reposition pieces if board will be rotated
	if (data & 2)
	{
		for (int y = 0; y < 8; y++)
			for (int x = 7; x >= 0; x--)
			{
				m_board->write_piece(x + 4, y, m_board->read_piece(x, y));
				m_board->write_piece(x, y, 0);
			}
	}
}

void phantom_state::clear_board(u8 data)
{
	memset(m_pieces_map, 0, sizeof(m_pieces_map));
	m_piece_hand = 0;
	m_board->clear_board(data);
}

void phantom_state::init_phantom()
{
	int numbanks = memregion("rombank")->bytes() / 0x4000;
	m_rombank->configure_entries(0, numbanks, memregion("rombank")->base(), 0x4000);
}


// Chesster Phantom

class chessterp_state : public phantom_state
{
public:
	chessterp_state(const machine_config &mconfig, device_type type, const char *tag) :
		phantom_state(mconfig, type, tag),
		m_speech(*this, "speech"),
		m_eye_led(*this, "eye_led")
	{ }

	void cphantom(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<dac_8bit_r2r_device> m_speech;
	output_finder<> m_eye_led;

	virtual void main_map(address_map &map) override ATTR_COLD;

	TIMER_DEVICE_CALLBACK_MEMBER(nmi_timer);
	virtual void control_w(offs_t offset, u8 data) override;
	virtual u8 input_r(offs_t offset) override;

	u8 m_select2 = 0;
};

void chessterp_state::machine_start()
{
	phantom_state::machine_start();

	m_eye_led.resolve();
	save_item(NAME(m_select2));
}

void chessterp_state::machine_reset()
{
	phantom_state::machine_reset();
	m_speech->write(0x80);
}

TIMER_DEVICE_CALLBACK_MEMBER(chessterp_state::nmi_timer)
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
}



/*******************************************************************************
    Motor Sim
*******************************************************************************/

void phantom_state::check_rotation()
{
	if (m_vmotor_pos != 0 && m_vmotor_pos != 0x88)
	{
		if (m_motors_ctrl & 0x03) m_vmotor_sensor0_ff = true;
		if (m_motors_ctrl & 0x02) m_vmotor_sensor1_ff = true;
	}
	if (m_hmotor_pos != 0 && m_hmotor_pos != 0xc0)
	{
		if (m_motors_ctrl & 0x0c) m_hmotor_sensor0_ff = true;
		if (m_motors_ctrl & 0x04) m_hmotor_sensor1_ff = true;
	}
}

void phantom_state::output_magnet_pos()
{
	// put active state on x bit 8
	const int active = BIT(m_motors_ctrl, 4) ? 0x100 : 0;
	m_out_pos[0] = m_hmotor_pos | active;
	m_out_pos[1] = m_vmotor_pos;
}

TIMER_DEVICE_CALLBACK_MEMBER(phantom_state::motors_timer)
{
	check_rotation();

	// simulate 1 rotation gap per each tick
	if ((m_motors_ctrl & 0x01) && m_vmotor_pos > 0x00) m_vmotor_pos--;
	if ((m_motors_ctrl & 0x02) && m_vmotor_pos < 0x88) m_vmotor_pos++;
	if ((m_motors_ctrl & 0x04) && m_hmotor_pos > 0x00) m_hmotor_pos--;
	if ((m_motors_ctrl & 0x08) && m_hmotor_pos < 0xc0) m_hmotor_pos++;

	check_rotation();
	output_magnet_pos();
}

void phantom_state::update_pieces_position(int state)
{
	int mx = m_hmotor_pos / 3;
	int my = m_vmotor_pos / 3;

	// convert motors position into board coordinates
	int x = m_hmotor_pos / 16 - 2;
	int y = m_vmotor_pos / 16;

	if (x < 0)
		x += 12;

	// check if the magnet is in the center of a square
	const bool valid_pos = ((m_hmotor_pos & 0x0f) > 0 && (m_hmotor_pos & 0x0f) <= 7) && ((m_vmotor_pos & 0x0f) > 8 && (m_vmotor_pos & 0x0f) <= 0xf);

	if (state)
	{
		if (valid_pos)
		{
			// pick up piece, unless it was picked up by the user
			const int pos = (y << 4 & 0xf0) | (x & 0x0f);
			if (pos != m_board->get_handpos())
			{
				m_piece_hand = m_board->read_piece(x, y);

				if (m_piece_hand != 0)
				{
					m_board->write_piece(x, y, 0);
					m_board->refresh();
				}
			}
		}
		else
		{
			int count = 0;

			// check surrounding area for piece
			for (int sy = my - 1; sy <= my + 1; sy++)
				for (int sx = mx - 1; sx <= mx + 1; sx++)
					if (sy >= 0 && sx >= 0 && m_pieces_map[sy][sx] != 0)
					{
						m_piece_hand = m_pieces_map[sy][sx];
						m_pieces_map[sy][sx] = 0;
						count++;
					}

			// more than one piece found (shouldn't happen)
			if (count > 1)
				popmessage("Internal collision!");
		}
	}
	else if (m_piece_hand != 0)
	{
		if (valid_pos)
		{
			// collision with piece on board (user interference)
			if (m_board->read_piece(x, y) != 0)
				popmessage("Collision at %c%d!", x + 'A', y + 1);
			else
			{
				m_board->write_piece(x, y, m_piece_hand);
				m_board->refresh();
			}
		}
		else
		{
			// collision with internal pieces map (shouldn't happen)
			if (m_pieces_map[my][mx] != 0)
				popmessage("Internal collision!");
			else
				m_pieces_map[my][mx] = m_piece_hand;
		}

		m_piece_hand = 0;
	}
}



/*******************************************************************************
    I/O
*******************************************************************************/

void phantom_state::update_lcd(u8 select)
{
	// update lcd at any edge
	if ((select ^ m_select) & 0x80)
	{
		u8 mask = (m_select & 0x80) ? 0xff : 0;
		for (int i = 0; i < 4; i++)
			m_display->write_row(i+1, (m_lcd_data >> (8*i) & 0xff) ^ mask);
	}
}

void phantom_state::control_w(offs_t offset, u8 data)
{
	// a0-a2,d1: 74259
	u8 prev = m_select;
	u8 mask = 1 << offset;
	m_select = (m_select & ~mask) | ((data & 0x02) ? mask : 0);

	// 74259 Q0-Q3: 7442 a0-a3
	// 7442 0-8: led data, input mux
	// 74259 Q4: led select
	m_mux = m_select & 0xf;
	m_display->matrix_partial(0, 1, BIT(~m_select, 4), 1 << m_mux);

	// 74259 Q6: bookrom bank
	m_rombank->set_entry(BIT(m_select, 6));

	// 74259 Q7: lcd polarity
	update_lcd(prev);
}

void chessterp_state::control_w(offs_t offset, u8 data)
{
	// chesster version has two 74259, more I/O
	u8 lcd_prev = m_select;
	u8 nmi_prev = m_select2;

	// a0-a2,d0,d1: 2*74259
	u8 mask = 1 << offset;
	m_select = (m_select & ~mask) | ((data & 1) ? mask : 0);
	m_select2 = (m_select2 & ~mask) | ((data & 2) ? mask : 0);

	// 74259(both) Q0,Q1: 7442 a0-a3
	// 7442 0-8: led data, input mux
	// 74259(1) Q2: led select
	m_mux = BIT(m_select, 0) | BIT(m_select2, 0) << 1 | BIT(m_select, 1) << 2 | BIT(m_select2, 1) << 3;
	m_display->matrix_partial(0, 1, BIT(~m_select, 2), 1 << m_mux);

	// 74259(2) Q2: eye leds
	m_eye_led = BIT(~m_select2, 2);

	// 74259(2) Q3 rising edge: nmi clear
	if (~nmi_prev & m_select2 & 8)
		m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);

	// 74259(1) Q4,Q5, 74259(2) Q4: speechrom bank
	m_rombank->set_entry(BIT(m_select, 4) | (BIT(m_select2, 4) << 1) | (BIT(m_select, 5) << 2));

	// 74259(1) Q7: lcd polarity
	update_lcd(lcd_prev);
}

void phantom_state::motors_w(u8 data)
{
	// d0: vertical motor down
	// d1: vertical motor up
	// d2: horizontal motor left
	// d3: horizontal motor right
	// d4: electromagnet
	for (int i = 0; i < 5; i++)
		m_out_motor[i] = BIT(data, i);

	if ((m_motors_ctrl ^ data) & 0x10)
		update_pieces_position(BIT(data, 4));

	m_motors_ctrl = data;

	// d5: speaker (not for chesster version, though it still writes to it)
	if (m_dac != nullptr)
		m_dac->write(BIT(data, 5));
}

void phantom_state::lcd_w(offs_t offset, u8 data)
{
	// a0-a2,d0,d2,d4,d6: 4*74259 to lcd digit segments
	u32 mask = bitswap<8>(1 << offset,3,7,6,0,1,2,4,5);
	for (int i = 0; i < 4; i++)
	{
		m_lcd_data = (m_lcd_data & ~mask) | (BIT(data, i * 2) ? mask : 0);
		mask <<= 8;
	}
}

u8 phantom_state::input_r(offs_t offset)
{
	u8 data = 0xff;

	// buttons
	if (m_mux == 8)
	{
		if (BIT(m_inputs[0]->read(), offset * 2 + 1)) data &= ~0x40;
		if (BIT(m_inputs[0]->read(), offset * 2 + 0)) data &= ~0x80;
	}

	// chessboard sensors
	else if (offset < 4)
	{
		if (BIT(m_board->read_file(offset * 2 + 1), m_mux)) data &= ~0x40;
		if (BIT(m_board->read_file(offset * 2 + 0), m_mux)) data &= ~0x80;
	}

	// captured pieces
	else
	{
		if (BIT(m_board->read_file( 8 + (offset & 1)), m_mux)) data &= ~0x40; // black
		if (BIT(m_board->read_file(11 - (offset & 1)), m_mux)) data &= ~0x80; // white
	}

	return data;
}

u8 chessterp_state::input_r(offs_t offset)
{
	u8 data = phantom_state::input_r(offset) & 0xfe;

	// d0: motion sensor (simulated here with an arbitrary timer)
	int motion = ((machine().time().as_ticks(50) & 0xff) > 0) ? 1 : 0;
	return data | motion | (m_inputs[1]->read() & 1);
}

u8 phantom_state::motors_r(offs_t offset)
{
	u8 data = 0xff;

	// optical rotation sensors
	switch (offset)
	{
	case 0:
		if (!m_vmotor_sensor1_ff) data &= ~0x40;
		if (!m_hmotor_sensor1_ff) data &= ~0x80;
		break;
	case 1:
		if (!m_vmotor_sensor0_ff) data &= ~0x40;
		if (!m_hmotor_sensor0_ff) data &= ~0x80;
		break;
	}

	return data;
}

u8 phantom_state::irq_ack_r()
{
	if (!machine().side_effects_disabled())
		m_maincpu->set_input_line(R65C02_IRQ_LINE, CLEAR_LINE);

	return 0;
}

u8 phantom_state::hmotor_ff_clear_r()
{
	if (!machine().side_effects_disabled())
		m_hmotor_sensor1_ff = m_hmotor_sensor0_ff = false;

	return 0;
}

u8 phantom_state::vmotor_ff_clear_r()
{
	if (!machine().side_effects_disabled())
		m_vmotor_sensor1_ff = m_vmotor_sensor0_ff = false;

	return 0;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void phantom_state::main_map(address_map &map)
{
	map(0x0000, 0x1fff).ram();
	map(0x2000, 0x2007).mirror(0x00f8).w(FUNC(phantom_state::control_w));
	map(0x2100, 0x2107).mirror(0x00f8).w(FUNC(phantom_state::lcd_w)).nopr();
	map(0x2200, 0x2200).mirror(0x00ff).w(FUNC(phantom_state::motors_w));
	map(0x2400, 0x2405).mirror(0x00f8).r(FUNC(phantom_state::input_r));
	map(0x2406, 0x2407).mirror(0x00f8).r(FUNC(phantom_state::motors_r));
	map(0x2500, 0x2500).mirror(0x00ff).r(FUNC(phantom_state::hmotor_ff_clear_r));
	map(0x2600, 0x2600).mirror(0x00ff).r(FUNC(phantom_state::vmotor_ff_clear_r));
	map(0x2700, 0x2700).mirror(0x00ff).r(FUNC(phantom_state::irq_ack_r));
	map(0x4000, 0x7fff).bankr(m_rombank);
	map(0x8000, 0xffff).rom();
}

void chessterp_state::main_map(address_map &map)
{
	phantom_state::main_map(map);
	map(0x2300, 0x2300).mirror(0x00ff).w(m_speech, FUNC(dac_8bit_r2r_device::data_w));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( phantom )
	PORT_START("IN.0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("Verify / Problem")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Option / Time")
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level / New")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back / Replay")
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Hint / Info")
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Move / Alternate")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Auto / Stop")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Clear")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Shift")
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x800, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( cphantom )
	PORT_INCLUDE( phantom )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Option / Replay")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Info / Auto")
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back / Repeat")
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Hint / Yes")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Move / No/Stop")

	PORT_START("IN.1") // motion sensor is inverted here, eg. hold down key to pretend that nobody's there
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_F1) PORT_NAME("Motion Sensor")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void phantom_state::phantom(machine_config &config)
{
	// basic machine hardware
	R65C02(config, m_maincpu, 4.9152_MHz_XTAL); // R65C02P4 or RP65C02G
	m_maincpu->set_addrmap(AS_PROGRAM, &phantom_state::main_map);

	const attotime irq_period = attotime::from_hz(4.9152_MHz_XTAL / 0x2000); // 4060, 600Hz
	m_maincpu->set_periodic_int(FUNC(phantom_state::irq0_line_assert), irq_period);

	TIMER(config, "motors_timer").configure_periodic(FUNC(phantom_state::motors_timer), irq_period * 9);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->set_size(8+4, 8);
	m_board->clear_cb().set(FUNC(phantom_state::clear_board));
	m_board->init_cb().set(FUNC(phantom_state::init_board));
	m_board->set_delay(attotime::from_msec(100));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1+4, 9);
	m_display->set_segmask(0x1e, 0x7f);

	config.set_default_layout(layout_fidel_phantom);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void chessterp_state::cphantom(machine_config &config)
{
	phantom(config);

	// basic machine hardware
	const attotime nmi_period = attotime::from_hz(4.9152_MHz_XTAL / 0x200); // 4060, 9.6kHz
	timer_device &nmi_clock(TIMER(config, "nmi_clock"));
	nmi_clock.configure_periodic(FUNC(chessterp_state::nmi_timer), nmi_period);
	nmi_clock.set_start_delay(nmi_period / 2); // interleaved with irq_period

	config.set_default_layout(layout_fidel_cphantom);

	// sound hardware
	config.device_remove("dac");
	DAC_8BIT_R2R(config, m_speech).add_route(ALL_OUTPUTS, "speaker", 0.5);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( fphantom ) // model 6100, PCB label 510.1128A01
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("u_3c_yellow.u3", 0x8000, 0x8000, CRC(fb7c38ae) SHA1(a1aa7637705052cb4eec92644dc79aee7ba4d77c) ) // 27C256

	ROM_REGION( 0x8000, "rombank", 0 )
	ROM_LOAD("u_4_white.u4",  0x0000, 0x8000, CRC(e4181ba2) SHA1(1f77d1867c6f566be98645fc252a01108f412c96) ) // 27C256
ROM_END


ROM_START( cphantom ) // model 6126, PCB label 510.1128D01
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("pv.u3", 0x8000, 0x8000, CRC(450a9ab5) SHA1(8392c76cf18cd6f8b17c8b12fac40c5cea874941) ) // 27C256

	ROM_REGION( 0x20000, "rombank", 0 )
	ROM_LOAD("101-1091b02.u4", 0x0000, 0x20000, CRC(fa370e88) SHA1(a937c8f1ec295cf9539d12466993974e40771493) ) // AMI, 27C010 or equivalent
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS            INIT          COMPANY, FULLNAME, FLAGS
SYST( 1988, fphantom, 0,      0,      phantom,  phantom,  phantom_state,   init_phantom, "Fidelity International", "Phantom (Fidelity)", MACHINE_SUPPORTS_SAVE | MACHINE_MECHANICAL | MACHINE_IMPERFECT_CONTROLS )

SYST( 1991, cphantom, 0,      0,      cphantom, cphantom, chessterp_state, init_phantom, "Fidelity Electronics International", "Chesster Phantom (model 6126)", MACHINE_SUPPORTS_SAVE | MACHINE_MECHANICAL | MACHINE_IMPERFECT_CONTROLS )



phc20.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/***************************************************************************

    Sanyo PHC-20

    TODO:
    - screen attribute bit 7 is unknown, does this machine support semi-graphics?

*****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "video/mc6847.h"

#include "imagedev/cassette.h"
#include "speaker.h"


namespace {

class phc20_state : public driver_device
{
public:
	phc20_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_vdg(*this, "mc6847")
		, m_vram(*this, "videoram")
		, m_cassette(*this, "cassette")
	{ }

	void phc20(machine_config &config);

protected:
	void machine_start() override { }

private:
	required_device<cpu_device> m_maincpu;
	required_device<mc6847_base_device> m_vdg;
	required_shared_ptr<uint8_t> m_vram;
	required_device<cassette_image_device> m_cassette;

	void mem_map(address_map &map) ATTR_COLD;

	uint8_t system_r();
	void system_w(uint8_t data);
	uint8_t video_ram_r(offs_t offset);
};


void phc20_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom().region("maincpu", 0);
	map(0x2000, 0x2fff).ram();
	map(0x3800, 0x3800).portr("KEY0");
	map(0x3801, 0x3801).portr("KEY1");
	map(0x3802, 0x3802).portr("KEY2");
	map(0x3803, 0x3803).portr("KEY3");
	map(0x3804, 0x3804).portr("KEY4");
	map(0x3805, 0x3805).portr("KEY5");
	map(0x3806, 0x3806).portr("KEY6");
	map(0x3807, 0x3807).portr("KEY7");
	map(0x3808, 0x3808).portr("KEY8");
	map(0x4000, 0x43ff).ram().share("videoram");
	map(0x6000, 0x6000).rw(FUNC(phc20_state::system_r), FUNC(phc20_state::system_w));
}


static INPUT_PORTS_START( phc20 )
	PORT_START("KEY0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2193") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) // U+2193 = ↓
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')

	PORT_START("KEY1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("KEY2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2192") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) // U+2192 = →
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2191") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) // U+2191 = ↑
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(']')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("KEY4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INS") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START("KEY5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2190") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) // U+2190 = ←
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(DEL))

	PORT_START("KEY7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')

	PORT_START("KEY8")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END


uint8_t phc20_state::system_r()
{
	/*

	    bit     description

	    0       cassette read
	    1       vertical sync

	*/

	uint8_t data = 0x00;

	data |= (m_cassette->input() > +0.3) << 0;
	data |= m_vdg->fs_r() << 1;

	return data;
}

void phc20_state::system_w(uint8_t data)
{
	/*

	    bit     description

	    0       cassette output

	*/

	m_cassette->output(BIT(data, 0) ? +1.0 : -1.0);
}


uint8_t phc20_state::video_ram_r(offs_t offset)
{
	uint8_t data = m_vram[offset];

	m_vdg->inv_w(BIT(data, 6)); // cursor attribute

	return data;
}


void phc20_state::phc20(machine_config &config)
{
	Z80(config, m_maincpu, 3.579545_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &phc20_state::mem_map);

	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	M5C6847P1(config, m_vdg, 3.579545_MHz_XTAL);
	m_vdg->set_screen("screen");
	m_vdg->input_callback().set(FUNC(phc20_state::video_ram_r));
	m_vdg->set_black_and_white(true);

	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.1);
}


ROM_START( phc20 )
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("phc20.ic3", 0x0000, 0x2000, CRC(2236cd7e) SHA1(49422700b9da3ba4e28a8b36dbdc463b3cca04bd))
ROM_END

} // anonymous namespace


//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS        INIT        COMPANY  FULLNAME     FLAGS
COMP( 1982, phc20,  0,      0,      phc20,   phc20,  phc20_state, empty_init, "Sanyo", "PHC-20",    MACHINE_SUPPORTS_SAVE )



phc25.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder, Robbbert
/***************************************************************************

        Sanyo PHC-25

    https://web.archive.org/web/20180107213100/http://www.geocities.jp/sanyo_phc_25/
    http://www.phc25.com/

    Z80 @ 4 MHz
    MC6847 video
    3x 8KB BIOS ROM
    1x 4KB chargen ROM
    16KB RAM
    6KB video RAM

    LOCK key (CAPSLOCK) selects upper-case/lower-case on international version
    (phc25), and selects hiragana/upper-case on Japanese version (phc25j).


    TODO: PHC-25
    - sound is strange, volume is often low to non-existent.
    - colours and graphics are different to those shown at
      http://www.phc25.com/collection.htm - who is correct?
    - screen attribute bit 7 is unknown in alphanumeric/semigraphics mode (0x6800-0x6BFF)
    - Japanese keyboard labels for phc25j.

    TODO: MAP-1010
    - tape control from IO port 0x00 not implemented.
    - dump the Demo tape which contains both audio and data, and
      makes use of tape control.


10 SCREEN3,1,1:COLOR,,1:CLS
20 X1=INT(RND(1)*256):Y1=INT(RND(1)*192):X2=INT(RND(1)*256):Y2=INT(RND(1)*192):C=INT(RND(1)*4)+1:LINE(X1,Y1)-(X2,Y2),C:GOTO 20
RUN


10 SCREEN2,1,1:CLS:FORX=0TO8:LINE(X*24,0)-(X*24+16,191),X,BF:NEXT
RUN

*****************************************************************************/

#include "emu.h"

#include "bus/centronics/ctronics.h"
#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "machine/upd1990a.h"
#include "sound/ay8910.h"
#include "video/mc6847.h"

#include "formats/phc25_cas.h"

#include "softlist_dev.h"
#include "speaker.h"


namespace {

class phc25_state : public driver_device
{
public:
	phc25_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_vram(*this, "videoram")
		, m_maincpu(*this, "maincpu")
		, m_chargen(*this, "chargen")
		, m_vdg(*this, "vdg")
		, m_centronics(*this, "centronics")
		, m_cent_data_out(*this, "cent_data_out")
		, m_cassette(*this, "cassette")
	{ }

	void phc25(machine_config &config);
	void phc25j(machine_config &config);

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

	virtual void port00_w(uint8_t data);
	virtual uint8_t port40_r();
	virtual void port40_w(uint8_t data);

	required_shared_ptr<uint8_t> m_vram;
	required_device<cpu_device> m_maincpu;
	required_region_ptr<uint8_t> m_chargen;
	required_device<m5c6847p1_device> m_vdg;
	required_device<centronics_device> m_centronics;
	required_device<output_latch_device> m_cent_data_out;
	required_device<cassette_image_device> m_cassette;

private:
	void phc25_mem(address_map &map) ATTR_COLD;
	void phc25_io(address_map &map) ATTR_COLD;

	uint8_t video_ram_r(offs_t offset);
	MC6847_GET_CHARROM_MEMBER(char_rom_r);
	void fsync_irq_w(int state);

	uint8_t m_port40 = 0U;
	int m_centronics_busy = 0;
};

class map1010_state : public phc25_state
{
public:
	map1010_state(const machine_config &mconfig, device_type type, const char *tag)
		: phc25_state(mconfig, type, tag)
		, m_rtc(*this, "rtc")
		, m_key(*this, "KEY%u", 0U)
		, m_control(*this, "CONTROL")
		, m_monitor(*this, "MONITOR")
		, m_rts(*this, "RTS")
	{ }

	void map1010(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(monitor_changed);
	DECLARE_INPUT_CHANGED_MEMBER(rts_changed);

protected:
	void machine_start() override ATTR_COLD;

	virtual void port00_w(uint8_t data) override;
	virtual uint8_t port40_r() override;

private:
	required_device<upd1990a_device> m_rtc;
	required_ioport_array<12> m_key;
	required_ioport m_control;
	required_ioport m_monitor;
	required_ioport m_rts;

	void update_monitor();

	void port20_w(uint8_t data);

	void map1010_mem(address_map &map) ATTR_COLD;
	void map1010_io(address_map &map) ATTR_COLD;
};

/* Read/Write Handlers */

void phc25_state::port00_w(uint8_t data)
{
	m_cent_data_out->write(data);
}

void map1010_state::port00_w(uint8_t data)
{
	// TODO: port used for both printer and tape control.

	phc25_state::port00_w(data);
}


void map1010_state::port20_w(uint8_t data)
{
	m_rtc->c0_w(BIT(data, 0));
	m_rtc->c1_w(BIT(data, 1));
	m_rtc->c2_w(BIT(data, 2));
	m_rtc->data_in_w(BIT(data, 3));
	m_rtc->clk_w(BIT(data, 4));
	m_rtc->stb_w(BIT(data, 7));
}


uint8_t phc25_state::port40_r()
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4       vertical sync
	    5       cassette read
	    6       centronics busy
	    7       horizontal sync

	*/

	uint8_t data = 0;

	/* vertical sync */
	data |= !m_vdg->fs_r() << 4;

	/* cassette read */
	data |= (m_cassette->input() > +0.3) << 5;

	/* centronics busy */
	data |= m_centronics_busy << 6;

	/* horizontal sync */
	data |= !m_vdg->hs_r() << 7;

	return data;
}

uint8_t map1010_state::port40_r()
{
	uint8_t data = phc25_state::port40_r();

	/* rtc data */
	data |= m_rtc->data_out_r();

	return data;
}

void phc25_state::port40_w(uint8_t data)
{
	/*

	    bit     description

	    0       cassette output
	    1       cassette motor
	    2       LED in the LOCK button (on = capslock)
	    3       centronics strobe
	    4       MC6847 GM1
	    5       MC6847 GM0
	    6       MC6847 CSS
	    7       MC6847 A/G

	*/

	/* cassette output */
	m_cassette->output( BIT(data, 0) ? +1.0 : -1.0);

	/* cassette motor */
	m_cassette->change_state(BIT(data, 1) ? CASSETTE_MOTOR_DISABLED : CASSETTE_MOTOR_ENABLED, CASSETTE_MASK_MOTOR);

	/* centronics strobe */
	m_centronics->write_strobe(BIT(data, 3));

	/* MC6847 */
	m_vdg->gm1_w(BIT(data, 4));
	m_vdg->gm0_w(BIT(data, 5));
	m_vdg->css_w(BIT(data, 6));
	m_vdg->ag_w(BIT(data, 7));
	m_port40 = data;
}

/* Memory Maps */

void phc25_state::phc25_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x5fff).rom().region("maincpu", 0);
	map(0x6000, 0x77ff).ram().share("videoram");
	map(0xc000, 0xffff).ram();
}

void phc25_state::phc25_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x00).w(FUNC(phc25_state::port00_w));
	map(0x40, 0x40).rw(FUNC(phc25_state::port40_r), FUNC(phc25_state::port40_w));
	map(0x80, 0x80).portr("KEY0");
	map(0x81, 0x81).portr("KEY1");
	map(0x82, 0x82).portr("KEY2");
	map(0x83, 0x83).portr("KEY3");
	map(0x84, 0x84).portr("KEY4");
	map(0x85, 0x85).portr("KEY5");
	map(0x86, 0x86).portr("KEY6");
	map(0x87, 0x87).portr("KEY7");
	map(0x88, 0x88).portr("KEY8");
	map(0xc0, 0xc0).w("ay8910", FUNC(ay8910_device::data_w));
	map(0xc1, 0xc1).rw("ay8910", FUNC(ay8910_device::data_r), FUNC(ay8910_device::address_w));
}


void map1010_state::map1010_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x5fff).rom().region("maincpu", 0);
	map(0x6000, 0x77ff).ram().share("videoram");
	map(0x7800, 0x785f).lr8(NAME([this](offs_t offset) { return BIT(m_key[offset >> 3]->read(), offset & 7); }));
	map(0x8000, 0xffff).ram();
}

void map1010_state::map1010_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x00).w(FUNC(map1010_state::port00_w));
	map(0x20, 0x20).w(FUNC(map1010_state::port20_w));
	map(0x40, 0x40).rw(FUNC(map1010_state::port40_r), FUNC(map1010_state::port40_w));
	map(0xc0, 0xc0).w("ay8910", FUNC(ay8910_device::data_w));
	map(0xc1, 0xc1).rw("ay8910", FUNC(ay8910_device::data_r), FUNC(ay8910_device::address_w));
}

/* Input Ports */

static INPUT_PORTS_START( joyports )
	PORT_START("JOY0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("JOY1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( phc25 )
	PORT_START("KEY0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2191") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) // U+2191 = ↑
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INS DEL") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) // unlabeled key

	PORT_START("KEY1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2193") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) // U+2193 = ↓
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("KEY2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2190") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) // U+2190 = ←
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2192") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) // U+2192 = →
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(']')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("KEY4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')

	PORT_START("KEY8")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GRAPH") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_INCLUDE( joyports )
INPUT_PORTS_END

static INPUT_PORTS_START( phc25j )
	PORT_INCLUDE( phc25 )
INPUT_PORTS_END

static INPUT_PORTS_START( map1010 )
	PORT_START("KEY0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')

	PORT_START("KEY1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)  PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('_')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13) // are RETURN and keypad RET the same key?

	PORT_START("KEY2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')

	PORT_START("KEY3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS_PAD) PORT_CHAR(UCHAR_MAMEKEY(EQUALS_PAD))

	PORT_START("KEY4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')

	PORT_START("KEY5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(ASTERISK))

	PORT_START("KEY6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')

	PORT_START("KEY7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('_') PORT_CHAR('|')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INS DEL") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))

	PORT_START("KEY8")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))

	PORT_START("KEY9")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2190") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))   // U+2190 = ←
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2192") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) // U+2192 = →
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2191") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))       // U+2191 = ↑
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(u8"\u2193") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))   // U+2193 = ↓
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD))

	PORT_START("KEY10")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LSHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RSHIFT") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GRAPH") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY11")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("KANA") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_INCLUDE( joyports )

	PORT_START("CONTROL")
	PORT_CONFNAME( 0x01, 0x00, "Control Selector" )
	PORT_CONFSETTING( 0x00, "On Line")
	PORT_CONFSETTING( 0x01, "Manual (Print)" )

	PORT_START("MONITOR")
	PORT_CONFNAME( 0x01, 0x01, "Monitor Selector" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(map1010_state::monitor_changed), 0)
	PORT_CONFSETTING( 0x01, "B")
	PORT_CONFSETTING( 0x00, "A" )

	PORT_START("RTS")
	PORT_CONFNAME( 0x01, 0x00, "RTS" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(map1010_state::rts_changed), 0)
	PORT_CONFSETTING( 0x00, "A")
	PORT_CONFSETTING( 0x01, "B" )
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(map1010_state::monitor_changed)
{
	/* Channel A/B select to monitor */
	update_monitor();
}

INPUT_CHANGED_MEMBER(map1010_state::rts_changed)
{
	/* Channel A/B select for data */
	m_cassette->set_channel(newval);

	update_monitor();
}

void map1010_state::update_monitor()
{
	/* RTS (unknown meaning) seems to invert the selected monitor channel */
	uint32_t output = m_monitor->read() ^ m_rts->read();

	m_cassette->reset_routes();
	m_cassette->add_route(output, "mono", 0.70);
}

/* Video */

uint8_t phc25_state::video_ram_r(offs_t offset)
{
	if (BIT(m_port40, 7)) // graphics
	{
		return m_vram[offset];
	}
	else    // text
	{
		offset &= 0x7ff;
		m_vdg->inv_w(BIT(m_vram[offset | 0x800], 0)); // cursor attribute
		m_vdg->as_w(BIT(m_vram[offset | 0x800], 1));  // screen2 lores attribute
		m_vdg->css_w(BIT(m_vram[offset | 0x800], 2)); // css attribute
		// bit 7 is set for all text (not spaces), meaning is unknown
		return m_vram[offset];
	}
}

MC6847_GET_CHARROM_MEMBER(phc25_state::char_rom_r)
{
	return m_chargen[(ch * 16 + line) & 0xfff];
}

void phc25_state::fsync_irq_w(int state)
{
	if (state == 0)
	{
		m_maincpu->pulse_input_line(INPUT_LINE_IRQ0, attotime::from_usec(100));
	}
}

void phc25_state::machine_reset()
{
	m_port40 = 0;
}

void phc25_state::machine_start()
{
	save_item(NAME(m_port40));
	save_item(NAME(m_centronics_busy));
}

void map1010_state::machine_start()
{
	phc25_state::machine_start();

	m_rtc->cs_w(1);
	m_rtc->oe_w(1);
}

/* Machine Driver */

void phc25_state::phc25(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &phc25_state::phc25_mem);
	m_maincpu->set_addrmap(AS_IO, &phc25_state::phc25_io);

	/* video hardware */
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	M5C6847P1(config, m_vdg, XTAL(4'433'619), true);
	m_vdg->set_screen("screen");
	m_vdg->fsync_wr_callback().set(FUNC(phc25_state::fsync_irq_w));
	m_vdg->input_callback().set(FUNC(phc25_state::video_ram_r));
	m_vdg->set_get_char_rom(FUNC(phc25_state::char_rom_r));
	m_vdg->set_get_fixed_mode(mc6847_device::MODE_GM2 | mc6847_device::MODE_GM1 | mc6847_device::MODE_INTEXT);
	// other lines not connected

	/* sound hardware (Synthesizer PSG-01 add-on) */
	SPEAKER(config, "mono").front_center();
	ay8910_device &psg(AY8910(config, "ay8910", 1'000'000));
	psg.port_a_read_callback().set_ioport("JOY0");
	psg.port_b_read_callback().set_ioport("JOY1");
	psg.add_route(ALL_OUTPUTS, "mono", 2.00);

	/* devices */
	CASSETTE(config, m_cassette);
	m_cassette->set_formats(phc25_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.1);
	m_cassette->set_interface("phc25_cass");

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set([this](int state) { m_centronics_busy = state; });

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	/* software lists */
	SOFTWARE_LIST(config, "cass_list").set_original("phc25_cass");
}

void phc25_state::phc25j(machine_config &config)
{
	phc25(config);

	M5C6847P1(config.replace(), m_vdg, XTAL(3'579'545));
	m_vdg->set_screen("screen");
	m_vdg->fsync_wr_callback().set(FUNC(phc25_state::fsync_irq_w));
	m_vdg->input_callback().set(FUNC(phc25_state::video_ram_r));
	m_vdg->set_get_char_rom(FUNC(phc25_state::char_rom_r));
	m_vdg->set_get_fixed_mode(mc6847_device::MODE_GM2 | mc6847_device::MODE_GM1 | mc6847_device::MODE_INTEXT);
	// other lines not connected
}

void map1010_state::map1010(machine_config &config)
{
	phc25j(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &map1010_state::map1010_mem);
	m_maincpu->set_addrmap(AS_IO, &map1010_state::map1010_io);

	UPD1990A(config, m_rtc, 32.768_kHz_XTAL);

	// TODO: TC9144 device for tape deck control

	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->set_stereo();
	m_cassette->reset_routes();
	m_cassette->set_channel(0);             // Channel A contains data
	m_cassette->add_route(1, "mono", 0.70); // Channel B contains audio

	subdevice<software_list_device>("cass_list")->set_original("");
	subdevice<software_list_device>("cass_list")->set_compatible("phc25_cass");
}

/* ROMs */

ROM_START( phc25 )
	ROM_REGION( 0x6000, "maincpu", 0 )
	ROM_LOAD( "phc25rom.0", 0x0000, 0x2000, CRC(fa28336b) SHA1(582376bee455e124de24ba4ac02326c8a592fa5a)) // 031_00aa.ic13 ?
	ROM_LOAD( "phc25rom.1", 0x2000, 0x2000, CRC(38fd578b) SHA1(dc3db78c0cdc89f1605200d39535be65a4091705)) // 031_01a.ic14 ?
	ROM_LOAD( "phc25rom.2", 0x4000, 0x2000, CRC(54392b27) SHA1(1587827fe9438780b50164727ce3fdea1b98078a)) // 031_02a.ic15 ?

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "031_04a.ic6", 0x0000, 0x1000, CRC(e56fb8c5) SHA1(6fc388c17fb43debfbc1464f767d0ce1375ce27b))
ROM_END

ROM_START( phc25j )
	ROM_REGION( 0x6000, "maincpu", 0 )
	ROM_LOAD( "phc25-11.0", 0x0000, 0x2000, CRC(287e83b0) SHA1(9fe960a8245f28efc04defeeeaceb1e5ec6793b8))
	ROM_LOAD( "phc25-11.1", 0x2000, 0x2000, CRC(6223f945) SHA1(5d44b883b6264cb5d2e21b2269308630c62e0e56))
	ROM_LOAD( "phc25-11.2", 0x4000, 0x2000, CRC(da859ae4) SHA1(6121e85947921e434d0157c378de3d81537f6b9f))
	//ROM_LOAD( "022_00aa.ic", 0x0000, 0x2000, NO_DUMP )
	//ROM_LOAD( "022_01aa.ic", 0x2000, 0x2000, NO_DUMP )
	//ROM_LOAD( "022_02aa.ic", 0x4000, 0x2000, NO_DUMP )

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "022_04a.ic6", 0x0000, 0x1000, CRC(ddb24f2b) SHA1(88b5095a5d3f09f9f508c8779c02823a887b7ba6))
ROM_END

ROM_START( map1010 )
	ROM_REGION( 0x6000, "maincpu", 0 )
	ROM_LOAD( "030_01ba.bin", 0x0000, 0x2000, CRC(cc3fb33a) SHA1(60dc8afe1310a3bb8fd425b6a021df9c2a6a33f4))
	ROM_LOAD( "030_02ba.bin", 0x2000, 0x2000, CRC(1aaaeb2b) SHA1(e906ee69da543b2bfa3850574443eee57555cdda))
	ROM_LOAD( "030_03bb.bin", 0x4000, 0x2000, CRC(a7b01c17) SHA1(f4a24b64331e13f94bdf2421b0a2feb7d2ca8677))

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "chargen.bin", 0x0000, 0x1000, CRC(ddb24f2b) SHA1(88b5095a5d3f09f9f508c8779c02823a887b7ba6))
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY  FULLNAME           FLAGS
COMP( 1983, phc25,   0,      0,      phc25,   phc25,   phc25_state,   empty_init, "Sanyo", "PHC-25 (Europe)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
COMP( 1983, phc25j,  phc25,  0,      phc25j,  phc25j,  phc25_state,   empty_init, "Sanyo", "PHC-25 (Japan)",  MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
COMP( 1983, map1010, 0,      0,      map1010, map1010, map1010_state, empty_init, "Seiko", "MAP-1010",        MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING )



philipsbo.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/******************************************************************************

    Philips BO "Videosynthesizer Prototype" skeleton

*******************************************************************************/

#include "emu.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68010.h"
#include "imagedev/floppy.h"
#include "machine/am79c90.h"
#include "machine/hd63450.h"
#include "machine/mc68681.h"
#include "machine/ncr5380.h"
#include "machine/nscsi_bus.h"
#include "machine/wd_fdc.h"
#include "machine/z80scc.h"

#define VERBOSE         (1)
#include "logmacro.h"

namespace {

class pbo_state : public driver_device
{
public:
	pbo_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_netcpu(*this, "netcpu")
		, m_scc(*this, "scc")
		, m_rs232(*this, "rs232%u", 0U)
		, m_fdc(*this, "fdc")
		, m_hdc(*this, "scsi:7:ncr5380")
		, m_dmac(*this, "dmac")
		, m_lance(*this, "lance")
		, m_main_ram_share(*this, "main_ram")
		, m_net_ram_share(*this, "net_ram")
	{ }

	void pbo(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void main_map(address_map &map) ATTR_COLD;
	void net_map(address_map &map) ATTR_COLD;

	required_device<m68010_device> m_maincpu;
	required_device<m68010_device> m_netcpu;
	required_device<scc85c30_device> m_scc;
	required_device_array<rs232_port_device, 2> m_rs232;
	required_device<mb8877_device> m_fdc;
	required_device<ncr5380_device> m_hdc;
	required_device<hd63450_device> m_dmac;
	required_device<am7990_device> m_lance;
	required_shared_ptr<uint16_t> m_main_ram_share;
	required_shared_ptr<uint16_t> m_net_ram_share;

	uint16_t berr_r(offs_t offset);
	void berr_w(offs_t offset, uint16_t data);
	void scsi_irq_w(int state);
	void dma_irq_w(int state);
	void fdc_irq_w(int state);
	void scc_irq_w(int state);
	void net_irq_w(int state);
	void floppy_select_w(uint8_t data);
	uint8_t fa0101_read();
	uint8_t net_fe00e1_read();
};

void pbo_state::main_map(address_map &map)
{
	map(0x000000, 0x07ffff).ram().share(m_main_ram_share);
	map(0x080000, 0xbfffff).rw(FUNC(pbo_state::berr_r), FUNC(pbo_state::berr_w));
	map(0xf80000, 0xf8ffff).rom().region("maincpu", 0);
	map(0xfa0000, 0xfa0007).rw(m_fdc, FUNC(mb8877_device::read), FUNC(mb8877_device::write)).umask16(0x00ff);
	map(0xfa0011, 0xfa0011).w(FUNC(pbo_state::floppy_select_w));
	map(0xfa0030, 0xfa0037).rw(m_scc, FUNC(scc85c30_device::ab_dc_r), FUNC(scc85c30_device::ab_dc_w)).umask16(0x00ff);
	map(0xfa8400, 0xfa840f).rw(m_hdc, FUNC(ncr5380_device::read), FUNC(ncr5380_device::write)).umask16(0x00ff);
	map(0xfa0100, 0xfa0101).r(FUNC(pbo_state::fa0101_read)).umask16(0x00ff);
}

void pbo_state::net_map(address_map &map)
{
	map(0x000000, 0x000fff).ram().share(m_net_ram_share); // Unknown RAM size
	map(0xf00000, 0xf1ffff).ram();
	map(0xf80000, 0xf83fff).rom().region("netcpu", 0);
	map(0xfe00e1, 0xfe00e1).r(FUNC(pbo_state::net_fe00e1_read));
	map(0xfe0100, 0xfe011f).rw("duart", FUNC(mc68681_device::read), FUNC(mc68681_device::write)).umask16(0x00ff);
}

uint16_t pbo_state::berr_r(offs_t offset)
{
	if(!machine().side_effects_disabled())
	{
		m_maincpu->set_buserror_details(0x080000 + offset*2, true, m_maincpu->get_fc());
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
	}
	return 0xff;
}

void pbo_state::berr_w(offs_t offset, uint16_t data)
{
	if(!machine().side_effects_disabled())
	{
		m_maincpu->set_buserror_details(0x080000 + offset*2, false, m_maincpu->get_fc());
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
	}
}

void pbo_state::scsi_irq_w(int state)
{
	LOG("SCSI IRQ: %d\n", state);
	m_maincpu->set_input_line(M68K_IRQ_5, state);
}

void pbo_state::dma_irq_w(int state)
{
	LOG("DMA IRQ: %d\n", state);
}

void pbo_state::fdc_irq_w(int state)
{
	LOG("FDC IRQ: %d\n", state);
}

void pbo_state::scc_irq_w(int state)
{
	LOG("DMA IRQ: %d\n", state);
}

void pbo_state::net_irq_w(int state)
{
	LOG("LANCE IRQ: %d\n", state);
}

void pbo_state::floppy_select_w(uint8_t data)
{
	LOG("Floppy select: %02X\n", data);
}

// It's unclear what hardware this location corresponds to on the actual board.
// If it's unmapped, which returns 0 by default, the system hangs with very little
//   meaningful external access.
// If bit 7 is set, the system defaults to using the serial console.
uint8_t pbo_state::fa0101_read()
{
	LOG("Read from FA0101: 80\n");
	return 0x80;
}

uint8_t pbo_state::net_fe00e1_read()
{
	return 0;
}

static INPUT_PORTS_START( pbo )
INPUT_PORTS_END

void pbo_state::machine_start()
{
}

void pbo_state::machine_reset()
{
	uint16_t *src = (uint16_t*)memregion("maincpu")->base();
	uint16_t *dst = &m_main_ram_share[0];
	memcpy(dst, src, 8);

	src = (uint16_t*)memregion("netcpu")->base();
	dst = &m_net_ram_share[0];
	memcpy(dst, src, 8);
}

static void pbo_scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
}

static void pbo_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
}

void pbo_state::pbo(machine_config &config)
{
	M68010(config, m_maincpu, 8_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &pbo_state::main_map);

	SCC85C30(config, m_scc, 10_MHz_XTAL); // Unknown PCLK
	m_scc->configure_channels(4'915'200, 4'915'200, 4'915'200, 4'915'200);
	m_scc->out_txda_callback().set(m_rs232[0], FUNC(rs232_port_device::write_txd));
	m_scc->out_dtra_callback().set(m_rs232[0], FUNC(rs232_port_device::write_dtr));
	m_scc->out_rtsa_callback().set(m_rs232[0], FUNC(rs232_port_device::write_rts));
	m_scc->out_txdb_callback().set(m_rs232[1], FUNC(rs232_port_device::write_txd));
	m_scc->out_dtrb_callback().set(m_rs232[1], FUNC(rs232_port_device::write_dtr));
	m_scc->out_rtsb_callback().set(m_rs232[1], FUNC(rs232_port_device::write_rts));
	m_scc->out_int_callback().set(FUNC(pbo_state::scc_irq_w));

	RS232_PORT(config, m_rs232[0], default_rs232_devices, "terminal");
	m_rs232[0]->cts_handler().set(m_scc[0], FUNC(scc85c30_device::ctsa_w));
	m_rs232[0]->dcd_handler().set(m_scc[0], FUNC(scc85c30_device::dcda_w));
	m_rs232[0]->rxd_handler().set(m_scc[0], FUNC(scc85c30_device::rxa_w));

	RS232_PORT(config, m_rs232[1], default_rs232_devices, nullptr);
	m_rs232[1]->cts_handler().set(m_scc[0], FUNC(scc85c30_device::ctsb_w));
	m_rs232[1]->dcd_handler().set(m_scc[0], FUNC(scc85c30_device::dcdb_w));
	m_rs232[1]->rxd_handler().set(m_scc[0], FUNC(scc85c30_device::rxb_w));

	HD63450(config, m_dmac, 8_MHz_XTAL, m_maincpu); // MC68450 compatible
	m_dmac->set_clocks(attotime::from_usec(32), attotime::from_nsec(450), attotime::from_usec(4), attotime::from_hz(15625/2)); // Guesses
	m_dmac->set_burst_clocks(attotime::from_usec(32), attotime::from_nsec(450), attotime::from_nsec(50), attotime::from_nsec(50)); // Guesses
	m_dmac->irq_callback().set(FUNC(pbo_state::dma_irq_w));
	//m_dmac->dma_read<0>().set(m_hdc, FUNC(ncr5380_device::dma_r));
	//m_dmac->dma_write<0>().set(m_hdc, FUNC(ncr5380_device::dma_w));

	MB8877(config, m_fdc, 8_MHz_XTAL / 8); // Unknown clock
	//m_fdc->set_force_ready(true);
	m_fdc->intrq_wr_callback().set(FUNC(pbo_state::fdc_irq_w));
	//m_fdc->drq_wr_callback().set(FUNC(pbo_state::fdc_drq_w));
	FLOPPY_CONNECTOR(config, "fdc:0", pbo_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(false);

	NSCSI_BUS(config, "scsi");

	NSCSI_CONNECTOR(config, "scsi:0", pbo_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:1", pbo_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", pbo_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", pbo_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", pbo_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", pbo_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", pbo_scsi_devices, nullptr);

	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr5380", NCR5380).machine_config(
		[this](device_t *device)
		{
			ncr5380_device &adapter = downcast<ncr5380_device &>(*device);

			adapter.irq_handler().set(*this, FUNC(pbo_state::scsi_irq_w));
		});

	M68010(config, m_netcpu, 20_MHz_XTAL / 2); // Confirmed
	m_netcpu->set_addrmap(AS_PROGRAM, &pbo_state::net_map);

	AM7990(config, m_lance);
	m_lance->intr_out().set(FUNC(pbo_state::net_irq_w));

	MC68681(config, "duart", 3'686'400);
}

ROM_START( pbo )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD16_BYTE( "even_4.2.bin", 0x0000, 0x8000, CRC(4a10eac1) SHA1(77ef3b17565cd58ccf86df753b30e4ed6212d729) )
	ROM_LOAD16_BYTE( "odd_4.2.bin", 0x0001, 0x8000, CRC(bed19ea4) SHA1(f69264c192965f65ecc6e39130f3a4861a475f92) )

	ROM_REGION(0x4000, "netcpu", 0)
	ROM_LOAD16_BYTE( "knlrom10_4.2_h.bin", 0x0000, 0x2000, CRC(28ecadca) SHA1(0eca5b407c7d4fc1312b96ccb486952206d627a9) )
	ROM_LOAD16_BYTE( "knlrom10_4.2_l.bin", 0x0001, 0x2000, CRC(40bba894) SHA1(924979ea7e383cb76bc68c785933bb5595446eae) )
ROM_END

} // Anonymous namespace

COMP( 1987, pbo,      0,      0,      pbo,      pbo,      pbo_state, empty_init, "Philips",    "BO (Videosynthesizer Prototype)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



phunsy.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/***************************************************************************

PHUNSY (Philipse Universal System)

2010-11-04 Skeleton driver.
2012-05-24 Cassette added.
2014-01-13 Quickload added.

http://www.tubedata.info/phunsy/index.html

Baud Rate ~ 6000 baud
W command to save data, eg 800-8FFW
R command to read data, eg 1100R to load the file at 1100,
   or R to load the file where it came from.
The tape must already be playing the leader when you press the Enter
   key, or it errors immediately.

Rom banking (in U bank):
 0U: RAM
 1U: MDCR program
 2U: Disassembler
 3U: Label handler


****************************************************************************/

#include "emu.h"

#include "cpu/s2650/s2650.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "machine/keyboard.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#define VERBOSE 1
#include "logmacro.h"


namespace {

class phunsy_state : public driver_device
{
public:
	phunsy_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_speaker(*this, "speaker")
		, m_cass(*this, "cassette")
		, m_p_videoram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
	{
	}

	void phunsy(machine_config &config);

	void init_phunsy();

private:
	uint8_t phunsy_data_r();
	void phunsy_ctrl_w(uint8_t data);
	void phunsy_data_w(uint8_t data);
	void kbd_put(u8 data);
	int cass_r();
	void cass_w(int state);
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);
	void phunsy_palette(palette_device &palette) const;
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void phunsy_data(address_map &map) ATTR_COLD;
	void phunsy_io(address_map &map) ATTR_COLD;
	void phunsy_mem(address_map &map) ATTR_COLD;

	uint8_t       m_data_out = 0U;
	uint8_t       m_keyboard_input = 0U;
	virtual void machine_reset() override ATTR_COLD;
	required_device<s2650_device> m_maincpu;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cass;
	required_shared_ptr<uint8_t> m_p_videoram;
	required_region_ptr<u8> m_p_chargen;
};


void phunsy_state::cass_w(int state)
{
	m_cass->output(state ? -1.0 : +1.0);
}

int phunsy_state::cass_r()
{
	return (m_cass->input() > 0.03) ? 0 : 1;
}

void phunsy_state::phunsy_mem(address_map &map)
{
	map(0x0000, 0x07ff).rom().region("roms", 0);
	map(0x0800, 0x0fff).ram();
	map(0x1000, 0x17ff).ram().share("videoram"); // Video RAM
	map(0x1800, 0x1fff).bankr("bankru").bankw("bankwu"); // Banked RAM/ROM
	map(0x2000, 0x3fff).ram();
	map(0x4000, 0x7fff).bankrw("bankq"); // Banked RAM
}

void phunsy_state::phunsy_io(address_map &map)
{
	map.unmap_value_high();
}

void phunsy_state::phunsy_data(address_map &map)
{
	map.unmap_value_high();
	map(S2650_CTRL_PORT, S2650_CTRL_PORT).w(FUNC(phunsy_state::phunsy_ctrl_w));
	map(S2650_DATA_PORT, S2650_DATA_PORT).rw(FUNC(phunsy_state::phunsy_data_r), FUNC(phunsy_state::phunsy_data_w));
}


void phunsy_state::phunsy_ctrl_w(uint8_t data)
{
	LOG("%s: phunsy_ctrl_w %02x\n", machine().describe_context(), data);

	// Q-bank
	membank("bankq")->set_entry(data & 15);

	// U-bank
	data >>= 4;

	if (data < 4)
		membank("bankru")->set_entry(data);
}


void phunsy_state::phunsy_data_w(uint8_t data)
{
	LOG("%s: phunsy_data_w %02x\n", machine().describe_context(), data);

	m_data_out = data;

	/* b0 - TTY out */
	/* b1 - select MDCR / keyboard */
	/* b2 - Clear keyboard strobe signal */
	if ( data & 0x04 )
	{
		m_keyboard_input |= 0x80;
	}

	/* b3 - speaker output (manual says it is bit 1)*/
	m_speaker->level_w(BIT(data, 1));

	/* b4 - -REV MDCR output */
	/* b5 - -FWD MDCR output */
	/* b6 - -WCD MDCR output */
	/* b7 - WDA MDCR output */
}


uint8_t phunsy_state::phunsy_data_r()
{
	uint8_t data = 0xff;

	//LOG("%s: phunsy_data_r\n", machine().describe_context());

	if ( m_data_out & 0x02 )
	{
		/* MDCR selected */
		/* b0 - TTY input */
		/* b1 - SK1 switch input */
		/* b2 - SK2 switch input */
		/* b3 - -WEN MDCR input */
		/* b4 - -CIP MDCR input */
		/* b5 - -BET MDCR input */
		/* b6 - RDA MDCR input */
		/* b7 - RDC MDCR input */
		data = 0xFF;
	}
	else
	{
		/* Keyboard selected */
		/* b0-b6 - ASCII code from keyboard */
		/* b7    - strobe signal */
		data = m_keyboard_input;
	}

	return data;
}


/* Input ports */
static INPUT_PORTS_START( phunsy )
INPUT_PORTS_END


void phunsy_state::kbd_put(u8 data)
{
	if (data)
		m_keyboard_input = data;
}


void phunsy_state::machine_reset()
{
	membank("bankru")->set_entry(0); // point at ram
	membank("bankq" )->set_base( memregion("ram_4000")->base() );
	m_keyboard_input = 0xFF;
	m_data_out = 0;
}


void phunsy_state::phunsy_palette(palette_device &palette) const
{
	for (int i = 0; i < 8; i++)
		palette.set_pen_color(i, pal3bit(i), pal3bit(i), pal3bit(i));
}


uint32_t phunsy_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint16_t sy=0,ma=0;

	for (uint8_t y = 0; y < 32; y++)
	{
		for (uint8_t ra = 0; ra < 8; ra++)
		{
			uint16_t *p = &bitmap.pix(sy++);

			for (uint16_t x = ma; x < ma+64; x++)
			{
				uint8_t const chr = m_p_videoram[x];

				uint8_t gfx,col;
				if (BIT(chr, 7))
				{
					/* Graphics mode */
					gfx = 0;
					col = ( chr >> 4 ) & 7;
					if ( (BIT(chr, 0) && (!BIT(ra, 2))) || (BIT(chr, 2) && (BIT(ra, 2))) )
						gfx = 0x38;
					if ( (BIT(chr, 1) && (!BIT(ra, 2))) || (BIT(chr, 3) && (BIT(ra, 2))) )
						gfx |= 7;
				}
				else
				{
					/* ASCII mode */
					gfx = m_p_chargen[(chr<<3) | ra];
					col = 7;
				}

				/* Display a scanline of a character (6 pixels) */
				*p++ = BIT( gfx, 5 ) ? col : 0;
				*p++ = BIT( gfx, 4 ) ? col : 0;
				*p++ = BIT( gfx, 3 ) ? col : 0;
				*p++ = BIT( gfx, 2 ) ? col : 0;
				*p++ = BIT( gfx, 1 ) ? col : 0;
				*p++ = BIT( gfx, 0 ) ? col : 0;
			}
		}
		ma+=64;
	}
	return 0;
}

/* F4 Character Displayer */
static const gfx_layout phunsy_charlayout =
{
	5, 7,                   /* 6 x 8 characters */
	128,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_phunsy )
	GFXDECODE_ENTRY( "chargen", 0x0000, phunsy_charlayout, 1, 3 )
GFXDECODE_END

// quickloads can start from various addresses, and the files have no header.
QUICKLOAD_LOAD_MEMBER(phunsy_state::quickload_cb)
{
	int const quick_length = image.length();
	if (quick_length > 0x4000)
		return std::make_pair(image_error::INVALIDLENGTH, "File too long (must be no larger than 16K)");

	std::vector<uint8_t> quick_data;
	quick_data.resize(quick_length);
	if (image.fread(&quick_data[0], quick_length) != quick_length)
		return std::make_pair(image_error::UNSPECIFIED, "Cannot read the file");

	constexpr uint16_t QUICK_ADDR = 0x1800;
	constexpr uint16_t EXEC_ADDR = QUICK_ADDR + 2;

	membank("bankru")->set_entry(0); // point at RAM
	address_space &space = m_maincpu->space(AS_PROGRAM);
	for (uint16_t i = 0; i < quick_length; i++)
		space.write_byte(i + QUICK_ADDR, quick_data[i]);

	// display a message about the loaded quickload
	image.message(" Quickload: size=%04X : exec=%04X", quick_length, EXEC_ADDR);

	// Start the quickload
	m_maincpu->set_state_int(S2650_R0, EXEC_ADDR >> 8);
	m_maincpu->set_state_int(S2650_R1, 0x08);
	m_maincpu->set_state_int(S2650_R2, 0xe0);
	m_maincpu->set_state_int(S2650_R3, 0x83);
	m_maincpu->set_state_int(S2650_PC, EXEC_ADDR);

	return std::make_pair(std::error_condition(), std::string());
}

void phunsy_state::init_phunsy()
{
	uint8_t *main = memregion("maincpu")->base();
	uint8_t *roms = memregion("roms")->base();
	uint8_t *ram = memregion("ram_4000")->base();

	membank("bankru")->configure_entry(0, &main[0x1800]);
	membank("bankwu")->configure_entry(0, &main[0x1800]);
	membank("bankru")->configure_entries(1, 3, &roms[0x800], 0x800);
	membank("bankq")->configure_entries(0, 16, &ram[0], 0x4000);

	membank("bankru")->set_entry(0); // point at ram
	membank("bankwu")->set_entry(0); // always write to ram
	membank("bankq")->set_entry(0);
}

void phunsy_state::phunsy(machine_config &config)
{
	/* basic machine hardware */
	S2650(config, m_maincpu, XTAL(1'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &phunsy_state::phunsy_mem);
	m_maincpu->set_addrmap(AS_IO, &phunsy_state::phunsy_io);
	m_maincpu->set_addrmap(AS_DATA, &phunsy_state::phunsy_data);
	m_maincpu->sense_handler().set(FUNC(phunsy_state::cass_r));
	m_maincpu->flag_handler().set(FUNC(phunsy_state::cass_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	/* Display (page 12 of pdf)
	   - 8Mhz clock
	   - 64 6 pixel characters on a line.
	   - 16us not active, 48us active: ( 64 * 6 ) * 60 / 48 => 480 pixels wide
	   - 313 line display of which 256 are displayed.
	*/
	screen.set_raw(XTAL(8'000'000), 480, 0, 64*6, 313, 0, 256);
	screen.set_screen_update(FUNC(phunsy_state::screen_update));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_phunsy);
	PALETTE(config, "palette", FUNC(phunsy_state::phunsy_palette), 8);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* Devices */
	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(phunsy_state::kbd_put));

	CASSETTE(config, m_cass);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	/* quickload */
	QUICKLOAD(config, "quickload", "bin", attotime::from_seconds(2)).set_load_callback(FUNC(phunsy_state::quickload_cb));
}


/* ROM definition */
ROM_START( phunsy )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASE00 )

	ROM_REGION( 0x2000, "roms", 0 )
	ROM_LOAD( "phunsy_bios.bin", 0x0000, 0x0800, CRC(a789e82e) SHA1(b1c130ab2b3c139fd16ddc5dc7bdcaf7a9957d02))
	ROM_LOAD( "pdcr.bin",        0x0800, 0x0800, CRC(74bf9d0a) SHA1(8d2f673615215947f033571f1221c6aa99c537e9))
	ROM_LOAD( "dass.bin",        0x1000, 0x0800, CRC(13380140) SHA1(a999201cb414abbf1e10a7fcc1789e3e000a5ef1))
	ROM_LOAD( "labhnd.bin",      0x1800, 0x0800, CRC(1d5a106b) SHA1(a20d09e32e21cf14db8254cbdd1d691556b473f0))

	ROM_REGION( 0x0400, "chargen", 0 )
	ROM_LOAD( "ph_char1.bin", 0x0200, 0x0100, CRC(a7e567fc) SHA1(b18aae0a2d4f92f5a7e22640719bbc4652f3f4ee))
	ROM_CONTINUE(0x0100, 0x0100)
	ROM_LOAD( "ph_char2.bin", 0x0000, 0x0100, CRC(3d5786d3) SHA1(8cf87d83be0b5e4becfa9fd6e05b01250a2feb3b))
	ROM_CONTINUE(0x0300, 0x0100)

	/* 16 x 16KB RAM banks */
	ROM_REGION( 0x40000, "ram_4000", ROMREGION_ERASEFF )
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT         COMPANY            FULLNAME  FLAGS */
COMP( 1980, phunsy, 0,      0,      phunsy,  phunsy, phunsy_state, init_phunsy, "J.F.P. Philipse", "PHUNSY", MACHINE_NOT_WORKING )



phusion.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Devin Acker

/*
    VTech Phusion (2000)

    This handheld organizer/PDA runs on an unknown CR16B SoC.
    It's probably similar (but not identical) to the one described in glcx.cpp.

    Main board:
        U1:  Analog Integrations AIC1652CS
        U2:  8Mbit flash (Toshiba TC58FVT800FT-85)
        U3:  Hyundai GM76FV18ALLFW70
        U10: unknown CR16B-based SoC (epoxy blob)
        U11: National Semiconductor DS14C232CM
        U12: National Semiconductor LM4882
        U14: 16Mbit mask ROM (Sharp LH53V16500)

*/

#include "emu.h"

#include "cpu/cr16b/cr16b.h"
#include "machine/intelfsh.h"
#include "screen.h"
#include "speaker.h"

namespace {

class phusion_state : public driver_device
{
public:
	phusion_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void phusion(machine_config &config);

private:
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void phusion_map(address_map &map) ATTR_COLD;

	required_device<cr16b_device> m_maincpu;
};

uint32_t phusion_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void phusion_state::phusion_map(address_map &map)
{
	/*
	TODO: this is a placeholder - flash, RAM, etc should also be in here somewhere
	the instruction at $000050 branches to $080054, which is supposed to mirror the next instruction(s) at $000054
	*/
	map(0x000000, 0x1fffff).rom();
}

INPUT_PORTS_START( phusion )
INPUT_PORTS_END

void phusion_state::phusion(machine_config &config)
{
	CR16B(config, m_maincpu, 10000000); // FIXME: determine exact type and clock
	m_maincpu->set_addrmap(AS_PROGRAM, &phusion_state::phusion_map);

	TC58FVT800(config, "flash");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(160, 160);
	screen.set_visarea(0, 160-1, 0, 160-1);
	screen.set_screen_update(FUNC(phusion_state::screen_update));

	SPEAKER(config, "speaker", 0).front_center();
}

ROM_START( phusion )
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_LOAD( "27-06559-0-0.u14", 0x000000, 0x200000, CRC(afaee544) SHA1(b9cd5e708e122a9ada6b5aa92ef516cf1f026f72)) // LHMV7FNU

	ROM_REGION( 0x100000, "flash", ROMREGION_ERASEFF )
ROM_END

} // anonymous namespace

SYST( 2000, phusion, 0, 0, phusion, phusion, phusion_state, empty_init, "VTech", "Phusion", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



pickytlk.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:QUFB
/***************************************************************************

    Driver for Casio Picky Talk and Casio Plet's

    TODO:

    - Communication port;
    - Panel active buttons display;
    - Review PORT/OPT callbacks copied from CFX9850G;
    - Fix unk_F8 loop when viewing calendar in JD-363/JD-364 models;
    - Fix busy loop @ 20:2981 in Plet's models;
    - Keyboard input for Plet's models;

    Hardware
    --------

    Super Picky Talk - Forest of Gurutan (JD-370):

    - PCB revision: A140947-1 Z835-1
    - LSI1 (CPU): Unknown (instruction set compatible with Hitachi HCD62121)
    - LSI3 (Static RAM): NEC D441000LGZ (1M-bit, 128K-word by 8-bit)
    - LSI5 (Mask ROM): NEC D23C8000XGX-C64 (8M-bit, 1M-word by 8-bit, pin compatible with AMD AM29F800B)

    Plet's (MK-300):

    - PCB revision: A141252-1 Z836-1
    - LSI1 (CPU): Unknown (instruction set compatible with Hitachi HCD62121)
    - LSI3 (Static RAM): Toshiba TC551001BFL-10V (1M-bit, 128K-word by 8-bit)
    - LSI5 (Mask ROM): NEC D23C8000XGX-C77 (8M-bit, 1M-word by 8-bit, pin compatible with AMD AM29F800B)

    Plet's (MK-350):

    - PCB revision: A141252-1 Z836-1
    - LSI1 (CPU): Unknown (instruction set compatible with Hitachi HCD62121)
    - LSI3 (Static RAM): NEC D441000LGW-B85X
    - LSI5 (Mask ROM): Oki M538032E-48

    Hidden Features
    ---------------

    [JD-370] On the debugger, run "bpset 20adb2,,{ ip=20ad27; g; }" to access an unused test program.

***************************************************************************/

#include "emu.h"

#include "cpu/hcd62121/hcd62121.h"

#include "crsshair.h"
#include "emupal.h"
#include "screen.h"

#include "pickytlk.lh"

#define LOG_IO     (1U << 1)
#define LOG_TABLET (1U << 2)

//#define VERBOSE (LOG_IO | LOG_TABLET)
#include "logmacro.h"

namespace {

class pickytlk_base_state : public driver_device
{
public:
	DECLARE_CROSSHAIR_MAPPER_MEMBER(pen_y_mapper);
	ioport_value pen_y_rescale_r();
	ioport_value pen_target_r();

protected:
	enum pen_target : u8
	{
		PEN_TARGET_LCD = 0,
		PEN_TARGET_PANEL = 1,
	};

	enum pen_state : u8
	{
		PEN_RELEASE = 0,
		PEN_PRESS = 1,
		PEN_HOLD = 2,
	};

	pickytlk_base_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_display_ram(*this, "display_ram")
		, m_maincpu(*this, "maincpu")
		, m_io_buttons(*this, "BUTTONS")
		, m_io_pen_x(*this, "PEN_X")
		, m_io_pen_y(*this, "PEN_Y")
		, m_io_pen_y_rescale(*this, "PEN_Y_RESCALE")
		, m_ko(0)
		, m_port(0)
		, m_opt(0)
	{ }

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void pickytlk(machine_config &config) ATTR_COLD;

	void kol_w(u8 data);
	void koh_w(u8 data);
	void port_w(u8 data);
	void opt_w(u8 data);
	u8 ki_r();
	u8 in0_r();
	u8 input_flag_read();

	TIMER_CALLBACK_MEMBER(io_timer_tick);
	u8 io_pen_x_read();
	u8 io_pen_y_read();
	virtual u8 tablet_read(offs_t offset) = 0;
	void tablet_write(offs_t offset, u8 data);

	void update_crosshair(screen_device &screen);
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void pickytlk_mem(address_map &map) ATTR_COLD;

	static constexpr float rescale(float x, float min_x, float max_x, float a, float b)
	{
		// Rescaling (min-max normalization) from [min_x..max_x] to [a..b].
		return a + (((x - min_x) * (b - a)) / (max_x - min_x));
	}

	required_shared_ptr<u8> m_display_ram;
	required_device<hcd62121_cpu_device> m_maincpu;

	required_ioport m_io_buttons;
	required_ioport m_io_pen_x;
	required_ioport m_io_pen_y;
	required_ioport m_io_pen_y_rescale;

	u16 m_ko;   // KO lines
	u8 m_port;  // PORT lines (serial I/O)
	u8 m_opt;   // OPT lines (contrast)
	std::unique_ptr<u8[]> m_io_tablet_regs;
	emu_timer *m_io_timer;
	u8 m_pen_state;
	u8 m_pen_target;
};

void pickytlk_base_state::machine_start()
{
	save_item(NAME(m_ko));
	save_item(NAME(m_port));
	save_item(NAME(m_opt));
	m_io_tablet_regs = make_unique_clear<u8[]>(0x100);
	save_pointer(NAME(m_io_tablet_regs), 0x100);
	m_io_timer = timer_alloc(FUNC(pickytlk_base_state::io_timer_tick), this);
	save_item(NAME(m_pen_state));
	save_item(NAME(m_pen_target));
}

void pickytlk_base_state::machine_reset()
{
	memset(m_io_tablet_regs.get(), 0, 0x100);
	m_io_timer->reset(attotime::never);
	m_pen_state = PEN_RELEASE;
	m_pen_target = PEN_TARGET_LCD;
}

CROSSHAIR_MAPPER_MEMBER(pickytlk_base_state::pen_y_mapper)
{
	// Parameter `linear_value` is ignored, since we will read the input port directly
	// for adjustments, just need to return that value in the expected range [0.0f..1.0f].
	return (float) pen_y_rescale_r() / 0xff;
}

ioport_value pickytlk_base_state::pen_y_rescale_r()
{
	/*
	    There are two distinct areas that can be interacted with the pen:
	    - LCD screen visible area: pen coordinates in [0x20..0xa0];
	    - Bottom panel: pen coordinates in [0xa0..0xf0];
	    In order to transparently map coordinates between each area, we split
	    the value across these areas, but rescaled to the input port's full range.
	*/
	const s16 io_pen_y_min = m_io_pen_y->field(0xff)->minval();
	const s16 io_pen_y_max = m_io_pen_y->field(0xff)->maxval();
	const s16 screen_y_max = io_pen_y_max * 0.6f;
	s16 adjusted_value = m_io_pen_y->read();
	if (adjusted_value > screen_y_max)
	{
		adjusted_value = rescale(adjusted_value, screen_y_max, io_pen_y_max, io_pen_y_min, io_pen_y_max);
		m_pen_target = PEN_TARGET_PANEL;
	}
	else
	{
		adjusted_value = rescale(adjusted_value, io_pen_y_min, screen_y_max, io_pen_y_min, io_pen_y_max);
		m_pen_target = PEN_TARGET_LCD;
	}

	return adjusted_value;
}

ioport_value pickytlk_base_state::pen_target_r()
{
	return m_pen_target;
}

TIMER_CALLBACK_MEMBER(pickytlk_base_state::io_timer_tick)
{
	if (m_pen_state == PEN_PRESS)
	{
		m_pen_state = PEN_HOLD;
	}
}

u8 pickytlk_base_state::io_pen_x_read()
{
	// Pen callibration tests seem to check coordinates relative to the center of the LCD screen,
	// and those offsets also align with the LCD position relative to the full tablet surface.
	s16 io_pen_x_min = m_io_pen_x->field(0xff)->minval();
	s16 io_pen_x_max = m_io_pen_x->field(0xff)->maxval();
	s16 io_pen_x_pos = m_io_pen_x->read();
	return rescale(io_pen_x_pos, io_pen_x_min, io_pen_x_max, 0x20, 0xdf);
}

u8 pickytlk_base_state::io_pen_y_read()
{
	s16 io_pen_y_min = m_io_pen_y->field(0xff)->minval();
	s16 io_pen_y_max = m_io_pen_y->field(0xff)->maxval();
	s16 io_pen_y_pos = pen_y_rescale_r();
	return (m_pen_target == PEN_TARGET_LCD)
		? rescale(io_pen_y_pos, io_pen_y_min, io_pen_y_max, 0x20, 0xa0)
		: rescale(io_pen_y_pos, io_pen_y_min, io_pen_y_max, 0xa0, 0xf0);
}

void pickytlk_base_state::tablet_write(offs_t offset, u8 data)
{
	LOGMASKED(LOG_TABLET, "%s: tablet_write [%02x] = %02x\n", machine().describe_context(), offset, data);
	m_io_tablet_regs[offset] = data;
}

void pickytlk_base_state::kol_w(u8 data)
{
	m_ko = (m_ko & 0xff00) | data;
	LOGMASKED(LOG_IO, "%s: KO = %04x\n", machine().describe_context(), m_ko);
}

void pickytlk_base_state::koh_w(u8 data)
{
	m_ko = (m_ko & 0x00ff) | (u16(data) << 8);
	LOGMASKED(LOG_IO, "%s: KO = %04x\n", machine().describe_context(), m_ko);
}

void pickytlk_base_state::port_w(u8 data)
{
	m_port = data;
	LOGMASKED(LOG_IO, "%s: PORT = %02x\n", machine().describe_context(), m_port);
}

void pickytlk_base_state::opt_w(u8 data)
{
	m_opt = data;
	LOGMASKED(LOG_IO, "%s: OPT = %02x\n", machine().describe_context(), m_opt);
}

u8 pickytlk_base_state::ki_r()
{
	if (BIT(m_io_buttons->read(), 6))
	{
		if (m_pen_state == PEN_RELEASE)
		{
			m_pen_state = PEN_PRESS;
			// FIXME: Adjust delay when more accurate instruction timings are implemented.
			// Program code waits for input flag to be stable by executing `mov DSIZE,0xff`
			// then `movq R00,R00` 15 times (see pickytlk ROM @ 2015f6).
			m_io_timer->adjust(attotime::from_msec(20), 0, attotime::never);
		}
	}
	else
	{
		m_pen_state = PEN_RELEASE;
		m_io_timer->reset(attotime::never);
	}

	return (m_pen_state == PEN_PRESS) ? 0x80 : 0;
}

u8 pickytlk_base_state::in0_r()
{
	// battery level?
	// bit4 -> if reset CPU keeps restarting (several unknown instructions before jumping to 0)
	//         perhaps a battery present check?
	// bit 5 -> 0 = low battery

	// --XX ---- VDET
	// ---- -X-- data-in
	return 0x30 & ~0x00;
}

u8 pickytlk_base_state::input_flag_read()
{
	return (m_pen_state == PEN_PRESS || m_pen_state == PEN_HOLD) ? 0 : 1;
}

void pickytlk_base_state::update_crosshair(screen_device &screen)
{
	// Either screen crosshair or layout view's cursor should be visible at a time.
	machine().crosshair().get_crosshair(0).set_screen(m_pen_target ? CROSSHAIR_SCREEN_NONE : &screen);
}

u32 pickytlk_base_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	update_crosshair(screen);

	u16 offset = 0;

	for (int i = 0; i < 16; i++)
	{
		int const x = i * 8;

		for (int j = 0; j < 64; j++)
		{
			u16 *const row = &bitmap.pix(63 - j);

			u8 const data1 = m_display_ram[offset];
			u8 const data2 = m_display_ram[offset + 0x400];

			for (int b = 0; b < 8; b++)
			{
				if (x + b < 127)
				{
					row[x + b] = (BIT(data1, b) << 1) | BIT(data2, b);
				}
			}

			offset++;
		}
	}

	return 0;
}


static INPUT_PORTS_START(pickytlk)
	// TODO: On/Off/Reset
	PORT_START("BUTTONS")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("Pen Down")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(pickytlk_base_state::pen_target_r))

	PORT_START("PEN_X")
	PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(30) PORT_KEYDELTA(10) PORT_MINMAX(0, 255) PORT_PLAYER(1) PORT_NAME("Pen X")

	PORT_START("PEN_Y")
	PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_SENSITIVITY(30) PORT_KEYDELTA(10) PORT_MINMAX(0, 255) PORT_PLAYER(1) PORT_NAME("Pen Y") PORT_CROSSHAIR_MAPPER_MEMBER(FUNC(pickytlk_base_state::pen_y_mapper))

	PORT_START("PEN_Y_RESCALE")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(pickytlk_base_state::pen_y_rescale_r))
INPUT_PORTS_END


void pickytlk_base_state::pickytlk(machine_config &config)
{
	HCD62121(config, m_maincpu, 4300000); /* X1 - 4.3 MHz */
	m_maincpu->kol_cb().set(FUNC(pickytlk_base_state::kol_w));
	m_maincpu->koh_cb().set(FUNC(pickytlk_base_state::koh_w));
	m_maincpu->port_cb().set(FUNC(pickytlk_base_state::port_w));
	m_maincpu->opt_cb().set(FUNC(pickytlk_base_state::opt_w));
	m_maincpu->ki_cb().set(FUNC(pickytlk_base_state::ki_r));
	m_maincpu->in0_cb().set(FUNC(pickytlk_base_state::in0_r));
	m_maincpu->input_flag_cb().set(FUNC(pickytlk_base_state::input_flag_read));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_size(127, 64);
	screen.set_visarea(0, 126, 0, 63);
	screen.set_screen_update(FUNC(pickytlk_base_state::screen_update));
	screen.set_palette("palette");
}


class pickytlk_monocolor_state : public pickytlk_base_state
{
public:
	pickytlk_monocolor_state(const machine_config &mconfig, device_type type, const char *tag)
		: pickytlk_base_state(mconfig, type, tag)
	{ }

	void pickytlk_monocolor(machine_config &config) ATTR_COLD;

private:
	virtual u8 tablet_read(offs_t offset) override;
	void pickytlk_mem(address_map &map) ATTR_COLD;
	void pickytlk_palette(palette_device &palette) const ATTR_COLD;
};

u8 pickytlk_monocolor_state::tablet_read(offs_t offset)
{
	LOGMASKED(LOG_TABLET, "%s: tablet_read [%02x] = %02x\n", machine().describe_context(), offset, m_io_tablet_regs[offset]);

	if (BIT(offset, 0))
	{
		u8 y = BIT(m_ko, 3) ? io_pen_y_read() : 0;
		LOGMASKED(LOG_TABLET, "%s: pen y = %02x\n", machine().describe_context(), y);
		return BIT(m_ko, 0) ? y : (0xff - y);
	}
	else
	{
		u8 x = BIT(m_ko, 2) ? io_pen_x_read() : 0;
		LOGMASKED(LOG_TABLET, "%s: pen x = %02x\n", machine().describe_context(), x);
		return BIT(m_ko, 0) ? x : (0xff - x);
	}
}

void pickytlk_monocolor_state::pickytlk_mem(address_map &map)
{
	map(0x000000, 0x00bfff).rom();
//  map(0x040000, 0x04ffff) // Unknown
	map(0x080000, 0x0807ff).ram();
//  map(0x100000, 0x10ffff) // Unknown
	map(0x200000, 0x2fffff).rom().region("mask_rom", 0);
	map(0x400000, 0x4007ff).ram().share("display_ram");
	map(0x400800, 0x41ffff).ram();
	// Read after pen callibration
	map(0x800030, 0x80003f).rw(FUNC(pickytlk_monocolor_state::tablet_read), FUNC(pickytlk_monocolor_state::tablet_write));
	// Read before pen callibration
	map(0x800040, 0x80004f).rw(FUNC(pickytlk_monocolor_state::tablet_read), FUNC(pickytlk_monocolor_state::tablet_write));
//  map(0xe00000, 0xe0ffff) // LCD I/O
}

void pickytlk_monocolor_state::pickytlk_palette(palette_device &palette) const
{
	palette.set_pen_color(0, 0xee, 0xee, 0xcc);
	palette.set_pen_color(1, 0x11, 0x11, 0x11);
	palette.set_pen_color(2, 0x11, 0x11, 0x11);
	palette.set_pen_color(3, 0x11, 0x11, 0x11);
}

void pickytlk_monocolor_state::pickytlk_monocolor(machine_config &config)
{
	pickytlk_base_state::pickytlk(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &pickytlk_monocolor_state::pickytlk_mem);

	// TODO: Verify palette. Colors can be changed by changing the contrast.
	PALETTE(config, "palette", FUNC(pickytlk_monocolor_state::pickytlk_palette), 4);

	config.set_default_layout(layout_pickytlk);
}


class pickytlk_multicolor_state : public pickytlk_base_state
{
public:
	pickytlk_multicolor_state(const machine_config &mconfig, device_type type, const char *tag)
		: pickytlk_base_state(mconfig, type, tag)
	{ }

	void plets(machine_config &config) ATTR_COLD;
	void pickytlk_multicolor(machine_config &config) ATTR_COLD;

private:
	virtual u8 tablet_read(offs_t offset) override;
	void pickytlk_mem(address_map &map) ATTR_COLD;
	void pickytlk_palette(palette_device &palette) const ATTR_COLD;
};

u8 pickytlk_multicolor_state::tablet_read(offs_t offset)
{
	/*
	    Pen coordinates can return a mirrored value when bit 4 is not set.
	    Both pairs of values <x1,x2>, <y1,y2> are approximated from these tests:
	    - General case:
	        - x1 + x2 > 0xc8
	        - y1 + y2 > 0xc8
	    - Reset screen with "OK" prompt:
	        - x1 > 0x7d
	        - y1 > 0x7d
	    - Pen callibration:
	        - x1 < 0x72
	        - x2 < 0x72
	        - y1 < 0x64
	        - y1 > 0x68
	*/
	LOGMASKED(LOG_TABLET, "%s: tablet_read [%02x] = %02x\n", machine().describe_context(), offset, m_io_tablet_regs[offset]);
	switch (offset)
	{
	case 0:
		{
			u8 y = BIT(m_ko, 7) ? io_pen_y_read() : 0;
			LOGMASKED(LOG_TABLET, "%s: pen y = %02x\n", machine().describe_context(), y);
			return BIT(m_ko, 4) ? y : (0xff - y);
		}
	case 1:
		{
			u8 x = BIT(m_ko, 6) ? io_pen_x_read() : 0;
			LOGMASKED(LOG_TABLET, "%s: pen x = %02x\n", machine().describe_context(), x);
			return BIT(m_ko, 4) ? x : (0xff - x);
		}
	case 4:
		// Can return 0 if other values are not stable/ready?
		return 0x80;
	default:
		return m_io_tablet_regs[offset];
	}
}

void pickytlk_multicolor_state::pickytlk_mem(address_map &map)
{
	map(0x000000, 0x007fff).rom();
	map(0x080000, 0x0807ff).ram();
	map(0x080300, 0x08030f).rw(FUNC(pickytlk_multicolor_state::tablet_read), FUNC(pickytlk_multicolor_state::tablet_write));
//  map(0x100000, 0x10ffff) // Unknown
//  map(0x110000, 0x11ffff) // LCD I/O
	map(0x200000, 0x2fffff).rom().region("mask_rom", 0);
	map(0x400000, 0x4007ff).ram().share("display_ram");
	map(0x400800, 0x41ffff).ram();
//  map(0xe10000, 0xe1ffff) // LCD I/O
}

void pickytlk_multicolor_state::pickytlk_palette(palette_device &palette) const
{
	palette.set_pen_color(0, 0xee, 0xee, 0xcc);
	palette.set_pen_color(1, 0x11, 0x33, 0x99);
	palette.set_pen_color(2, 0x33, 0xcc, 0x77);
	palette.set_pen_color(3, 0xee, 0x77, 0x33);
}

void pickytlk_multicolor_state::plets(machine_config &config)
{
	pickytlk_base_state::pickytlk(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &pickytlk_multicolor_state::pickytlk_mem);

	// TODO: Verify palette. Colors can be changed by changing the contrast.
	PALETTE(config, "palette", FUNC(pickytlk_multicolor_state::pickytlk_palette), 4);
}

void pickytlk_multicolor_state::pickytlk_multicolor(machine_config &config)
{
	plets(config);

	config.set_default_layout(layout_pickytlk);
}


#define CPU_ROM_MONOCOLOR \
	ROM_REGION(0xc000, "maincpu", 0) \
	ROM_LOAD("cpu.lsi1", 0x0000, 0xc000, CRC(67c76253) SHA1(2f7d368e584608d0ed3135159c36f35f02cdd8c4))

#define CPU_ROM_MULTICOLOR \
	ROM_REGION(0x8000, "maincpu", 0) \
	ROM_LOAD("cpu.lsi1", 0x0000, 0x8000, CRC(d58efff9) SHA1(a8d2c2a331d79c5299274e2f2d180deda60a5aed))


ROM_START(jd363)
	CPU_ROM_MONOCOLOR

	ROM_REGION(0x100000, "mask_rom", 0)
	ROM_LOAD("d23c8000lwgx-c11.lsi5", 0x00000, 0x100000, CRC(d4c6e7d2) SHA1(ef199725f04da977cd47293dba8086011f156baf))
ROM_END

ROM_START(pickydis)
	CPU_ROM_MONOCOLOR

	ROM_REGION(0x100000, "mask_rom", 0)
	ROM_LOAD("d23c8000lwgx-c14.lsi5", 0x00000, 0x100000, CRC(1a6273c7) SHA1(f0601873503272d9a620789b5c9798f6e1baf4e7))
ROM_END

ROM_START(jd364)
	CPU_ROM_MULTICOLOR

	ROM_REGION(0x100000, "mask_rom", 0)
	ROM_LOAD("d23c8000lwgx-c12.lsi5", 0x00000, 0x100000, CRC(c023b709) SHA1(0c081a62f00d0fbee496a5c9067fb145cb79c8cd))
ROM_END

ROM_START(jd366)
	CPU_ROM_MULTICOLOR

	ROM_REGION(0x100000, "mask_rom", 0)
	ROM_LOAD("d23c8000xgx-c15.lsi5", 0x00000, 0x100000, CRC(d8a84c6a) SHA1(6a64dff0070c0016457c69fc0518df57da9c1b75))
ROM_END

ROM_START(jd368)
	CPU_ROM_MULTICOLOR

	ROM_REGION(0x100000, "mask_rom", 0)
	ROM_LOAD("d23c8000xgx-c42.lsi5", 0x00000, 0x100000, CRC(c396bafb) SHA1(0d5610d288ab2474017c05ed54fc816d2e82525f))
ROM_END

ROM_START(pickytlk)
	CPU_ROM_MULTICOLOR

	ROM_REGION(0x100000, "mask_rom", 0)
	ROM_LOAD("d23c8000xgx-c64.lsi5", 0x00000, 0x100000, CRC(6ed6feae) SHA1(f9a63db3d048da0954cab052690deb01ec384b22))
ROM_END

ROM_START(plets300)
	CPU_ROM_MULTICOLOR

	ROM_REGION(0x100000, "mask_rom", 0)
	ROM_LOAD("d23c8000xgx-c77.lsi5", 0x00000, 0x100000, CRC(50ecb853) SHA1(5f2564ccb6ff7e0e5a21064ca32626f35dc81506))
ROM_END

ROM_START(plets350)
	CPU_ROM_MULTICOLOR

	ROM_REGION(0x100000, "mask_rom", 0)
	ROM_LOAD("m538032e-48.lsi5", 0x00000, 0x100000, CRC(42068a99) SHA1(24f8dc15a51a391d4c35cce7332d55f1fa4d8160))
ROM_END

} // anonymous namespace


// Release date 1995-09 from "Casio Game Perfect Catalogue"
COMP(1995, jd363, 0, 0, pickytlk_monocolor, pickytlk, pickytlk_monocolor_state, empty_init, "Casio", "Picky Talk - Super Denshi Techou", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// ROM date 9539K7001
COMP(1995?, pickydis, 0, 0, pickytlk_monocolor, pickytlk, pickytlk_monocolor_state, empty_init, "Tsukuda Original", "Disney Characters - Tegaki Electronic Note", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// Release date 1995-10 from "Casio Game Perfect Catalogue"
COMP(1995, jd364, 0, 0, pickytlk_multicolor, pickytlk, pickytlk_multicolor_state, empty_init, "Casio", "Color Picky Talk - Super Denshi Techou", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// ROM date 9635K7021
COMP(1996?, jd366, 0, 0, pickytlk_multicolor, pickytlk, pickytlk_multicolor_state, empty_init, "Casio", "Super Picky Talk - My room fantasy", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// ROM date 9737K7041
COMP(1997?, jd368, 0, 0, pickytlk_multicolor, pickytlk, pickytlk_multicolor_state, empty_init, "Casio", "Super Picky Talk - Access Pet", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// Release date 1998-07 from "Casio Game Perfect Catalogue"
COMP(1998, pickytlk, 0, 0, pickytlk_multicolor, pickytlk, pickytlk_multicolor_state, empty_init, "Casio", "Super Picky Talk - Forest of Gurutan", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// Release date 1999-09 from "Casio Game Perfect Catalogue"
COMP(1999, plets300, 0, 0, plets, pickytlk, pickytlk_multicolor_state, empty_init, "Casio", "Plet's (MK-300)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)

// ROM date 0019K7001
COMP(2000?, plets350, 0, 0, plets, pickytlk, pickytlk_multicolor_state, empty_init, "Casio", "Plet's (MK-350)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



picno.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/******************************************************************************************************************************

Konami Picno and Picno2

Skeleton driver started on 2017-11-30, can be claimed by anyone interested.

Information provided by Team Europe.

Chips: HD6435328F10 (H8/532 CPU with inbuilt ROM), HN62334BP (27c040 ROM), Konami custom chip 054715 (rectangular 100 pins),
       HM538121JP-10, M514256B-70J, OKI M6585.
Crystals: D200L2 (Y1) and D214A3 (Y2), frequencies unknown.

The hardware of the Picno 1 and Picno 2 is completely the same. The Picno 1 has an Audio-Line-Out, which the Picno 2 does not have.

Maskrom of PICNO 1: RX001-Z8-V3J
Maskrom of PICNO 2: RX001-Z8-V4J

The size of the address space and other things is controlled by the 3 mode pins. It's assumed we are in Mode 4.

Can't do anything until the internal ROM is dumped.

******************************************************************************************************************************/

#include "emu.h"
#include "cpu/h8500/h8532.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "softlist_dev.h"

//#include "sound/multipcm.h"
//#include "screen.h"
//#include "speaker.h"


namespace {

class picno_state : public driver_device
{
public:
	picno_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void picno(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<h8532_device> m_maincpu;
};

void picno_state::mem_map(address_map &map)
{
	//map(0x00000, 0x07fff).rom().region("roms", 0); // 32kb internal rom
	//map(0x0fb80, 0x0ff7f).ram(); // internal ram
	//map(0x0ff80, 0x0ffff); // internal controls
	map(0x10000, 0x8ffff).rom().region("roms", 0); // guess
}

static INPUT_PORTS_START( picno )
INPUT_PORTS_END

void picno_state::picno(machine_config &config)
{
	/* basic machine hardware */
	HD6435328(config, m_maincpu, 20'000'000); // TODO: clock is a guess, divided by 2 in the cpu
	m_maincpu->set_addrmap(AS_PROGRAM, &picno_state::mem_map);

	//SPEAKER(config, "speaker", 2).front(); // no speaker in the unit, but there's a couple of sockets on the back
	//sound.add_route(0, "speaker", 1.0, 0);
	//sound.add_route(1, "speaker", 1.0, 1);

	GENERIC_CARTSLOT(config, "cartslot", generic_linear_slot, "picno_cart");

	SOFTWARE_LIST(config, "cart_list").set_original("picno");
}

ROM_START( picno )
	ROM_REGION(0x8000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD( "hd6435328f10.u5", 0x00000, 0x08000, NO_DUMP ) // internal rom

	ROM_REGION(0x80000, "roms", 0)
	ROM_LOAD( "rx001-z8-v3j.u2", 0x00000, 0x80000, CRC(e3c8929d) SHA1(1716f09b0a594b3782d257330282d77b6ca6fa0d) ) //HN62334BP
ROM_END

ROM_START( picno2 )
	ROM_REGION(0x8000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD( "hd6435328f10.u5", 0x00000, 0x08000, NO_DUMP ) // internal rom

	ROM_REGION(0x80000, "roms", 0)
	ROM_LOAD( "rx001-z8-v4j.u2", 0x00000, 0x80000, CRC(ae89a9a5) SHA1(51ed458ffd151e19019beb23517263efce4be272) ) //HN62334BP
ROM_END

} // anonymous namespace


//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY   FULLNAME   FLAGS
CONS( 1993, picno,  0,      0,      picno,   picno, picno_state, empty_init, "Konami", "Picno",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
CONS( 1993, picno2, 0,      0,      picno,   picno, picno_state, empty_init, "Konami", "Picno 2", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



pimps.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

P.I.M.P.S. (Personal Interactive MicroProcessor System)

2009-12-06 Skeleton driver.

No schematics or hardware info available.

http://www.classiccmp.org/dunfield/pimps/index.htm

Commands:
A xxxx xxxx   - Assemble (from editor file): origin destination
D xxxx,xxxx   - Dump memory to host port
E             - Enter editor
 A            - Append at end of file
 C            - Clear memory file (reqires ESCAPE to confirm)
 D xx         - Delete line number xx
 I xx         - Insert at line xx
 L xx xx      - List range of lines
 Q            - Query: Display highest used address
 X            - eXit editor (requires ESCAPE to confirm)
 $ xxxx xxxx  - exit directly to assembler
F             - set for Full-duplex host operation
G xxxx        - Go (execute) at address
H             - set for Half-duplex host operation
M xxxx,xxxx   - Display a range of memory (hex dump)
P xx xx-[xx.] - display/Edit I/O Port
S xxxx        - Substitute data into memory
T             - Transparent link to host (Ctrl-A exits)
U             - set host Uart parameters (8251 control registers)
V             - Virtual memory (Not used on PIMPS board)

Notes:

The 'D'ump command outputs memory to the host in some form of Intel
HEX records, and waits for line-feed from the host before proceeding.

The 'V'irtual memory function was to control an EPROM emulator which
was part of the original design (see Chucks notes below) and was not
used on the PIMPS board.

Editor:
 Operates in HEXIDECIMAL line numbers. Only supports up to 256 lines
 (01-00). You must enter full two-digit number when prompted.
 You MUST 'C'lear the memory file before you begin, otherwise you will
 be editing random memory content.

Assembler:
 Comment is an INSTRUCTION! - this means that you need to put at least
 one space before and after ';' when entering a line comment.

 Does not understand DECIMAL numbers. It understands character constants
 ('c' and 'cc') and hex numbers ($xx or $xxxx).

 8-bit values MUST contain two hex digits or one quoted character. 16-bit
 constants MUST contain four hex digits or two quoted characters.

 Use 'S' instead of 'SP', eg: LXI S,$1000

 Only EQU, DB, DW and END directives are supported. An END statement is
 REQUIRED (otherwise you get the message '?tab-ful' as it fills the symbol
 table with garbage occuring in memory after the end of the file).

 RST instructions are implemented as 8 separate 'RST0'-'RST8' memonics.

****************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/i8085/i8085.h"
#include "machine/clock.h"
#include "machine/i8251.h"


namespace {

class pimps_state : public driver_device
{
public:
	pimps_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "roms")
		, m_ram(*this, "mainram")
	{ }

	void pimps(machine_config &config);

private:
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	memory_passthrough_handler m_rom_shadow_tap;
	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
};


void pimps_state::mem_map(address_map &map)
{
	map(0x0000, 0xefff).ram().share("mainram");
	map(0xf000, 0xffff).rom().region("roms", 0);
}

void pimps_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0xf0, 0xf1).rw("uart1", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xf2, 0xf3).rw("uart2", FUNC(i8251_device::read), FUNC(i8251_device::write));
}

/* Input ports */
static INPUT_PORTS_START( pimps )
INPUT_PORTS_END


void pimps_state::machine_reset()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x0000, 0x07ff, m_rom);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0xf000, 0xf7ff,
			"rom_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// reinstall RAM over the ROM shadow
					m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x07ff, m_ram);
				}
			},
			&m_rom_shadow_tap);
}

// baud is not documented, we will use 9600
static DEVICE_INPUT_DEFAULTS_START( terminal ) // set up terminal to default to 9600, 7 bits, even parity
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END

void pimps_state::pimps(machine_config &config)
{
	I8085A(config, m_maincpu, 2_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &pimps_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &pimps_state::io_map);

	clock_device &uart_clock(CLOCK(config, "uart_clock", 153'600));
	uart_clock.signal_handler().set("uart1", FUNC(i8251_device::write_txc));
	uart_clock.signal_handler().append("uart1", FUNC(i8251_device::write_rxc));
	uart_clock.signal_handler().append("uart2", FUNC(i8251_device::write_txc));
	uart_clock.signal_handler().append("uart2", FUNC(i8251_device::write_rxc));

	i8251_device &uart1(I8251(config, "uart1", 0));
	uart1.txd_handler().set("rs232a", FUNC(rs232_port_device::write_txd));
	uart1.dtr_handler().set("rs232a", FUNC(rs232_port_device::write_dtr));
	uart1.rts_handler().set("rs232a", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, "terminal"));
	rs232a.rxd_handler().set("uart1", FUNC(i8251_device::write_rxd));
	rs232a.dsr_handler().set("uart1", FUNC(i8251_device::write_dsr));
	rs232a.cts_handler().set("uart1", FUNC(i8251_device::write_cts));
	rs232a.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal)); // must be exactly here

	i8251_device &uart2(I8251(config, "uart2", 0));
	uart2.txd_handler().set("rs232b", FUNC(rs232_port_device::write_txd));
	uart2.dtr_handler().set("rs232b", FUNC(rs232_port_device::write_dtr));
	uart2.rts_handler().set("rs232b", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set("uart2", FUNC(i8251_device::write_rxd));
	rs232b.dsr_handler().set("uart2", FUNC(i8251_device::write_dsr));
	rs232b.cts_handler().set("uart2", FUNC(i8251_device::write_cts));
}

/* ROM definition */
ROM_START( pimps )
	ROM_REGION( 0x1000, "roms", 0 )
	ROM_LOAD( "pimps.bin", 0x0000, 0x1000, CRC(5da1898f) SHA1(d20e31d0981a1f54c83186dbdfcf4280e49970d0))
ROM_END

} // anonymous namespace

/* Driver */

/*    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  STATE        INIT        COMPANY          FULLNAME      FLAGS */
COMP( 197?, pimps, 0,      0,      pimps,   pimps, pimps_state, empty_init, "Henry Colford", "P.I.M.P.S.", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE ) // terminal beeps



pipbug.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

PIPBUG

2010-04-08 Skeleton driver.
2012-05-16 Connected to serial terminal.. working
2019-07-17 Added cassette onto L command

All input must be in UPPER case.

Commands:
A - See and alter memory
B - Set breakpoint (2 permitted)
C - Clear breakpoint
D - Dump memory to paper tape
G - Go to address, run
L - Load memory from paper tape
S - See and alter registers

PIPBUG isn't a computer; it is a the name of the bios used
in a number of small 2650-based computers from 1976 to 1978.
Examples include Baby 2650, Eurocard 2650, etc., plus Signetics
own PC1001, PC1500, and KT9500 systems. PIPBUG was written by Signetics.

The sole means of communication is via a serial terminal.
PIPBUG uses the SENSE and FLAG pins as serial lines, thus
there is no need for a UART. The baud rate is 110.

The Baby 2650 (featured in Electronics Australia magazine in
March 1977) has 256 bytes of RAM.

The terminal is expected to have a papertape device attached, and
use it to save and load programs. PIPBUG still thinks it is talking
to the terminal, when in fact the data is flowing to the papertape
reader and punch.

Cassette:
There is software available at 110 baud, using 1200/2400 Hz. This has
been hooked up to the otherwise-useless L command. Baud rate = 110.
After load completes, G440 to run.

****************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/s2650/s2650.h"
#include "imagedev/cassette.h"
#include "machine/terminal.h"
#include "imagedev/snapquik.h"
#include "machine/timer.h"
#include "speaker.h"


namespace {

class pipbug_state : public driver_device
{
public:
	pipbug_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_rs232(*this, "rs232")
		, m_maincpu(*this, "maincpu")
		, m_cass(*this, "cassette")
	{ }

	void pipbug(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	void pipbug_ctrl_w(u8 data);
	int serial_r();
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);
	required_device<rs232_port_device> m_rs232;
	required_device<s2650_device> m_maincpu;
	required_device<cassette_image_device> m_cass;
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);
	void data_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	u8 m_cass_data[4]{};
	bool m_cassold = false, m_cassinbit = false;
};

void pipbug_state::pipbug_ctrl_w(u8 data)
{
// 0x80 is written here - not connected in the baby 2650
}

TIMER_DEVICE_CALLBACK_MEMBER( pipbug_state::kansas_r )
{
	// no tape - set to idle
	m_cass_data[1]++;
	if (m_cass_data[1] > 32)
	{
		m_cass_data[1] = 32;
		m_cassinbit = 1;
	}

	if ((m_cass->get_state() & CASSETTE_MASK_UISTATE) != CASSETTE_PLAY)
		return;

	/* cassette - turn 1200/2400Hz to a bit */
	bool cass_ws = (m_cass->input() > +0.04) ? 1 : 0;

	if (cass_ws != m_cassold)
	{
		m_cassold = cass_ws;
		m_cassinbit = (m_cass_data[1] < 12) ? 1 : 0;
		m_cass_data[1] = 0;
	}
}

int pipbug_state::serial_r()
{
	return m_rs232->rxd_r() & m_cassinbit;
}

void pipbug_state::machine_start()
{
	save_item(NAME(m_cass_data));
	save_item(NAME(m_cassold));
	save_item(NAME(m_cassinbit));
}

void pipbug_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x03ff).rom();
	map(0x0400, 0x7fff).ram();
}

void pipbug_state::data_map(address_map &map)
{
//  map.unmap_value_high();
	map(S2650_CTRL_PORT, S2650_CTRL_PORT).w(FUNC(pipbug_state::pipbug_ctrl_w));
}

/* Input ports */
static INPUT_PORTS_START( pipbug )
INPUT_PORTS_END

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_110 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_110 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

QUICKLOAD_LOAD_MEMBER(pipbug_state::quickload_cb)
{
	int const quick_length = image.length();
	if (quick_length < 0x0444)
	{
		return std::make_pair(image_error::INVALIDLENGTH, "File too short");
	}
	else if (quick_length > 0x8000)
	{
		return std::make_pair(image_error::INVALIDLENGTH, "File too long");
	}

	std::vector<u8> quick_data;
	quick_data.resize(quick_length);
	int const read_ = image.fread(&quick_data[0], quick_length);
	if (read_ != quick_length)
	{
		return std::make_pair(image_error::UNSPECIFIED, "Cannot read the file");
	}
	else if (quick_data[0] != 0xc4)
	{
		return std::make_pair(image_error::INVALIDIMAGE, "Invalid header");
	}

	int const exec_addr = quick_data[1] * 256 + quick_data[2];

	if (exec_addr >= quick_length)
	{
		return std::make_pair(
				image_error::INVALIDIMAGE,
				util::string_format("Exec address %04X beyond end of file %04X", exec_addr, quick_length));
	}

	constexpr int QUICK_ADDR = 0x440;

	address_space &space = m_maincpu->space(AS_PROGRAM);
	for (int i = QUICK_ADDR; i < read_; i++)
		space.write_byte(i, quick_data[i]);

	// display a message about the loaded quickload
	image.message(" Quickload: size=%04X : exec=%04X", quick_length, exec_addr);

	// Start the quickload
	m_maincpu->set_state_int(S2650_PC, exec_addr);

	return std::make_pair(std::error_condition(), std::string());
}

void pipbug_state::pipbug(machine_config &config)
{
	/* basic machine hardware */
	S2650(config, m_maincpu, XTAL(1'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &pipbug_state::mem_map);
	m_maincpu->set_addrmap(AS_DATA, &pipbug_state::data_map);
	m_maincpu->flag_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_maincpu->sense_handler().set(FUNC(pipbug_state::serial_r));

	/* video hardware */
	RS232_PORT(config, m_rs232, default_rs232_devices, "terminal");
	m_rs232->set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	/* quickload */
	QUICKLOAD(config, "quickload", "pgm", attotime::from_seconds(1)).set_load_callback(FUNC(pipbug_state::quickload_cb));

	SPEAKER(config, "mono").front_center();

	/* Cassette */
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	TIMER(config, "kansas_r").configure_periodic(FUNC(pipbug_state::kansas_r), attotime::from_hz(40000));
}


/* ROM definition */
ROM_START( pipbug )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "pipbug.rom", 0x0000, 0x0400, CRC(f242b93e) SHA1(f82857cc882e6b5fc9f00b20b375988024f413ff))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY      FULLNAME  FLAGS
COMP( 1979, pipbug, 0,      0,      pipbug,  pipbug, pipbug_state, empty_init, "Signetics", "PIPBUG", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



pippin.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

  pippin.cpp: Apple/Bandai Pippin
  Preliminary driver by R. Belmont (based on pippin.c skeleton by Angelo Salese)

  Apple ASICs identified:
  -----------------------
  343S0152    Aspen (Bandit derived PCI host bridge and RAM controller)
  343S0153    Taos (640x480 framebuffer with double-buffering and convolution to fix interlace flicker)
  343S1125    Grand Central (SWIM III, Sound, VIA)
  341S0060    Cuda (68HC05 MCU, handles ADB and parameter ("CMOS") RAM)
  343S1146    ??? (likely SCSI due to position on board)
  343S1191(x2) Athens Prime PLL Clock Generator

  Other chips
  -----------
  Z8530 SCC
  CS4217 audio DAC
  Bt856 video DAC

  Pippin-type map
  00000000 : RAM
  80000000 : PCI memory (to EFFFFFFF)
  F0000000 : 1 MB video RAM
  F0100000 : Space for second 1 MB of video RAM (unused in Pippin)
  (2 MB VRAM space mirrors every 0x00200000 until F0800000)
  F0800000 : Taos registers
  F1000000 : Palette for 256 color mode
  F3000000 : Grand Central I/O controller
  F3008000 : DMA channel registers
  F3010000 : SCSI0
  F3011000 : MACE Ethernet
  F3012000 : SCC (68K Mac style addressing)
  F3013000 : SCC (MacRISC addressing)
  F3014000 : AWACS audio
  F3015000 : SWIM III floppy
  F3016000 : VIA1 (interface to Cuda)
  F3018000 : SCSI1

  NOTE: the PowerPC starts off disabled; the Cuda 68HC05 starts it up once it's booted.

  NOTE 2: goes off into the weeds in the subroutine at fff05010

****************************************************************************/

#include "emu.h"
#include "cpu/powerpc/ppc.h"
#include "cpu/mn1880/mn1880.h"
#include "imagedev/cdromimg.h"
#include "machine/ram.h"
#include "sound/cdda.h"

#include "awacs_macrisc.h"
#include "bandit.h"
#include "cuda.h"
#include "heathrow.h"
#include "macadb.h"
#include "softlist.h"
#include "speaker.h"

class pippin_state : public driver_device
{
public:
	void pippin(machine_config &config);

	pippin_state(const machine_config &mconfig, device_type type, const char *tag);

	required_device<ppc_device> m_maincpu;
	required_device<aspen_host_device> m_aspen;
	required_device<cuda_device> m_cuda;
	required_device<macadb_device> m_macadb;
	required_device<ram_device> m_ram;

private:
	void pippin_map(address_map &map) ATTR_COLD;
	void cdmcu_mem(address_map &map) ATTR_COLD;
	void cdmcu_data(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void cuda_reset_w(int state)
	{
		m_maincpu->set_input_line(INPUT_LINE_HALT, state);
		m_maincpu->set_input_line(INPUT_LINE_RESET, state);
	}

	void irq_w(int state)
	{
		m_maincpu->set_input_line(PPC_IRQ, state);
	}
};

pippin_state::pippin_state(const machine_config &mconfig, device_type type, const char *tag) :
	driver_device(mconfig, type, tag),
	m_maincpu(*this, "maincpu"),
	m_aspen(*this, "pci:00.0"),
	m_cuda(*this, "cuda"),
	m_macadb(*this, "macadb"),
	m_ram(*this, RAM_TAG)
{
}

void pippin_state::machine_start()
{
}

void pippin_state::machine_reset()
{
	// the PPC can't run until Cuda's ready
	m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
}

void pippin_state::pippin_map(address_map &map)
{
	map(0x00000000, 0x005fffff).ram();

	/* writes at 0x0*c01000 the string "Mr. Kesh" and wants it to be read back, true color VRAMs perhaps? */
	map(0x00c00000, 0x00c01007).ram();
	map(0x01c00000, 0x01c01007).ram();
	map(0x02c00000, 0x02c01007).ram();
	map(0x03c00000, 0x03c01007).ram();

	map(0x40000000, 0x403fffff).rom().region("bootrom", 0).mirror(0x0fc00000);   // mirror of ROM for 680x0 emulation
	map(0x40000000, 0x40000000).lr8(NAME([]() { return 0x80; }));   // hack to make flash ROM status check pass (causes 0xe1 to be written to VRAM, which is important later)
	map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a7001; }));

	map(0xf0000000, 0xf00fffff).ram();  // VRAM

	map(0xffc00000, 0xffffffff).rom().region("bootrom", 0);
}

void pippin_state::cdmcu_mem(address_map &map)
{
	map(0x0000, 0xffff).rom().region("cdrom", 0);
}

void pippin_state::cdmcu_data(address_map &map)
{
	map(0x0000, 0x0001).noprw();
	map(0x0003, 0x0003).noprw();
	map(0x0004, 0x0008).nopw();
	map(0x0008, 0x0008).nopr();
	map(0x0009, 0x0009).noprw();
	map(0x000f, 0x000f).noprw();
	map(0x001f, 0x0021).nopw();
	map(0x0031, 0x0031).noprw();
	map(0x0033, 0x0033).nopw();
	map(0x0036, 0x0036).noprw();
	map(0x0060, 0x031f).ram();
	map(0x802a, 0x802a).nopw();
	map(0x802f, 0x8034).nopw();
	map(0x8032, 0x8032).nopr();
	map(0x8035, 0x8035).lr8(NAME([]() { return 0x40; }));
	map(0x8037, 0x8037).noprw();
	map(0x8038, 0x8039).nopw();
}

/* Input ports */
static INPUT_PORTS_START( pippin )
INPUT_PORTS_END

void pippin_state::pippin(machine_config &config)
{
	/* basic machine hardware */
	PPC603(config, m_maincpu, 66000000);
	m_maincpu->ppcdrc_set_options(PPCDRC_COMPATIBLE_OPTIONS);
	m_maincpu->set_addrmap(AS_PROGRAM, &pippin_state::pippin_map);

	PCI_ROOT(config, "pci", 0);
	ASPEN(config, m_aspen, 66000000, "maincpu").set_dev_offset(1);

	cdrom_image_device &cdrom(CDROM(config, "cdrom", 0));
	cdrom.set_interface("cdrom");
	SOFTWARE_LIST(config, "cd_list").set_original("pippin");

	RAM(config, m_ram);
	m_ram->set_default_size("32M");

	grandcentral_device &grandcentral(GRAND_CENTRAL(config, "pci:0d.0", 0));
	grandcentral.set_maincpu_tag("maincpu");
	grandcentral.irq_callback().set(FUNC(pippin_state::irq_w));

	awacs_macrisc_device &awacs(AWACS_MACRISC(config, "codec", 45.1584_MHz_XTAL / 2));
	awacs.dma_output().set(grandcentral, FUNC(heathrow_device::codec_dma_read));

	grandcentral.codec_r_callback().set(awacs, FUNC(awacs_macrisc_device::read_macrisc));
	grandcentral.codec_w_callback().set(awacs, FUNC(awacs_macrisc_device::write_macrisc));

	SPEAKER(config, "speaker", 2).front();
	awacs.add_route(0, "speaker", 1.0, 0);
	awacs.add_route(1, "speaker", 1.0, 1);

	MACADB(config, m_macadb, 15.6672_MHz_XTAL);

	CUDA_V2XX(config, m_cuda, XTAL(32'768));
	m_cuda->set_default_bios_tag("341s0060");
	m_cuda->reset_callback().set(FUNC(pippin_state::cuda_reset_w));
	m_cuda->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
	m_cuda->via_clock_callback().set(grandcentral, FUNC(heathrow_device::cb1_w));
	m_cuda->via_data_callback().set(grandcentral, FUNC(heathrow_device::cb2_w));
	m_macadb->adb_data_callback().set(m_cuda, FUNC(cuda_device::set_adb_line));
	config.set_perfect_quantum(m_maincpu);

	grandcentral.pb3_callback().set(m_cuda, FUNC(cuda_device::get_treq));
	grandcentral.pb4_callback().set(m_cuda, FUNC(cuda_device::set_byteack));
	grandcentral.pb5_callback().set(m_cuda, FUNC(cuda_device::set_tip));
	grandcentral.cb2_callback().set(m_cuda, FUNC(cuda_device::set_via_data));

	mn1880_device &cdmcu(MN1880(config, "cdmcu", 8388608)); // type and clock unknown
	cdmcu.set_addrmap(AS_PROGRAM, &pippin_state::cdmcu_mem);
	cdmcu.set_addrmap(AS_DATA, &pippin_state::cdmcu_data);
	cdmcu.set_disable();
}

/* ROM definition */
/*

    BIOS versions

    dev
    monitor 341S0241 - 245,247,248,250
    1.0     341S0251..254               U1-U4
    1.2     341S0297..300               U15,U20,U31,U35
    1.3     341S0328..331               U1/U31, U2/U35, U3/U15 and U4/U20

*/

ROM_START( pippin )
	ROM_REGION( 0x400000, "bootrom",  ROMREGION_64BIT | ROMREGION_BE )
	ROM_SYSTEM_BIOS(0, "v13", "Kinka v 1.3")
	ROMX_LOAD( "bandai pippin,19960920 - kinka 1.3,- 3e6b3ee4-a52528e9ce8c.rom", 0x000000, 0x400000, CRC(87a1337d) SHA1(8e512af6e34dd823f3defec77d43ecbff1ecad54), ROM_BIOS(0) )

	ROM_SYSTEM_BIOS(1, "v12", "Kinka v 1.2")
	ROMX_LOAD( "bandai pippin,19960628 - kinka 1.2,- 3e10e14c-72c40c1af23a.rom", 0x000000, 0x400000, CRC(4fead4b3) SHA1(3fa02e9b0fa702ac6e02edc08911eac8b50e2d1f), ROM_BIOS(1) )

	ROM_SYSTEM_BIOS(2, "v1", "Kinka v 1.0")
	ROMX_LOAD( "341s0251.u1", 0x000006, 0x100000, CRC(aaea2449) SHA1(2f63e215260a42fb7c5f2364682d5e8c0604646f),ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(6) | ROM_BIOS(2))
	ROMX_LOAD( "341s0252.u2", 0x000004, 0x100000, CRC(3d584419) SHA1(e29c764816755662693b25f1fb3c24faef4e9470),ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(6) | ROM_BIOS(2))
	ROMX_LOAD( "341s0253.u3", 0x000002, 0x100000, CRC(d8ae5037) SHA1(d46ce4d87ca1120dfe2cf2ba01451f035992b6f6),ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(6) | ROM_BIOS(2))
	ROMX_LOAD( "341s0254.u4", 0x000000, 0x100000, CRC(3e2851ba) SHA1(7cbf5d6999e890f5e9ab2bc4b10ca897c4dc2016),ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(6) | ROM_BIOS(2))

	ROM_SYSTEM_BIOS(3, "vgm", "Kinka GM version")
	ROMX_LOAD( "bandai pippin,19960128 - kinka gm flash,- 2bf65931-318e40f6a1f4.rom", 0x000000, 0x400000, CRC(4ff875e6) SHA1(eb8739cab1807c6c7c51acc7f4a3afc1f9c6ddbb), ROM_BIOS(3) )

	ROM_SYSTEM_BIOS(4, "pre", "Kinka pre-release")
	ROMX_LOAD( "kinka-pre.rom", 0x000000, 0x400000, CRC(4ff875e6) SHA1(eb8739cab1807c6c7c51acc7f4a3afc1f9c6ddbb),ROM_BIOS(4) )

	ROM_REGION( 0x10000, "cdrom", 0 ) /* MATSUSHITA CR504-L OEM */
	ROM_LOAD( "504par4.0i.ic7", 0x0000, 0x10000, CRC(25f7dd46) SHA1(ec3b3031742807924c6259af865e701827208fec) )
ROM_END

/* Driver */

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY           FULLNAME        FLAGS */
COMP( 1996, pippin, 0,      0,      pippin,  pippin, pippin_state, empty_init, "Apple / Bandai", "Pippin @mark", MACHINE_NOT_WORKING)



pixtermu.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:QUFB
/******************************************************************************

    Skeleton driver for Pixter Multi-Media.

    Currently hangs after reset:

    - If executing boot ROM, with boot configuration set to load NOR Flash or SRAM, it will reach an infinite loop at 0x1f0;
    - If executing nCS1 ROM, it will wait indefinitely due to unimplemented timer controls;

    References:

    - https://elinux.org/Pixter_Multimedia
    - https://www.nxp.com/docs/en/data-sheet/LH79524_525_N.pdf
    - https://www.nxp.com/docs/en/user-guide/LH79524-LH79525_UG_V1_3.pdf
    - https://lh79525.wordpress.com/

    Hardware
    --------

    Model H4651/J4287/J4288:

    - PCB Revision: PT1543A-BGA-4F2C 2005/04/30 Rev 4.2b
    - CPU: Sharp LH79524-NOE (ARM720T)
    - RAM: ICSI IC42S16100-7T (2MB each, 4MB total)
    - ROM: Chip-On-Board, selected by pin CS1

    JTAG
    ----

    To place the LH79524 into ARM ICE debug mode you will need to connect TEST1 via a 4.7K Ohm resistor to ground.

    Pinout:

    - D2: nTRST (Reset Input)
    - P4: TMS (Mode Select Input)
    - T3: TCK (Clock Input)
    - T1: TDI (Serial Data Input)
    - P3: TDO (Data Serial Output)
    - TEST1: ARM ICE debug mode

*******************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/arm7/arm7.h"

#include "softlist_dev.h"

namespace {

class pixter_multimedia_state : public driver_device
{
public:
	pixter_multimedia_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_cart(*this, "cartslot")
		, m_maincpu(*this, "maincpu")
		, m_ndcs0(*this, "ndcs0")
		, m_internal_sram(*this, "internal_sram")
		, m_apb(*this, "apb", 0x27000, ENDIANNESS_LITTLE)
		, m_remap_view(*this, "remap")
	{ }

	void pixter_multimedia(machine_config &config);

private:
	// Remap Control, mapped at 0xfffe2008, offset 0x22008/4
	static inline constexpr uint32_t APB_REMAP = 34818;
	// Power-up Boot Configuration, mapped at 0xfffe6000, offset 0x26000/4
	static inline constexpr uint32_t APB_PBC = 38912;
	// nCS1 Override, mapped at 0xfffe6004, offset 0x26004/4
	static inline constexpr uint32_t APB_CS1OV = 38913;
	// External Peripheral Mapping, mapped at 0xfffe6008, offset 0x26008/4
	static inline constexpr uint32_t APB_EPM = 38914;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	DEVICE_IMAGE_LOAD_MEMBER(cart_load);
	void arm7_map(address_map &map) ATTR_COLD;
	void apb_bridge_w(offs_t offset, uint32_t data, uint32_t mem_mask);
	void apb_remap(uint32_t data);

	required_device<generic_slot_device> m_cart;
	required_device<arm7_cpu_device> m_maincpu;
	required_shared_ptr<uint32_t> m_ndcs0;
	required_shared_ptr<uint32_t> m_internal_sram;
	memory_share_creator<uint32_t> m_apb;
	memory_view m_remap_view;
};

void pixter_multimedia_state::apb_remap(uint32_t data)
{
	// User's Guide - 1.6 Memory Interface Architecture
	if (data == 0) {
		m_remap_view.select((m_apb[APB_PBC] & 0b0100) && (m_apb[APB_CS1OV] & 0b1) ? 0 : 3);
	} else if (data < 3) {
		m_remap_view.select(data);
	} else {
		logerror("Unexpected remap %d\n", data);
	}
}

void pixter_multimedia_state::machine_start()
{
	if (m_cart->exists()) {
		memory_region *const cart_rom = m_cart->memregion("rom");
		device_generic_cart_interface::install_non_power_of_two<0>(
				cart_rom->bytes(),
				0x03ff'ffff,
				0,
				0x4800'0000,
				[this, cart_rom] (offs_t begin, offs_t end, offs_t mirror, offs_t src) {
					m_maincpu->space(AS_PROGRAM).install_rom(begin, end, mirror, cart_rom->base() + src);
				});
	}
}

void pixter_multimedia_state::machine_reset()
{
	m_apb[APB_PBC] = 0b0000; // Boot from NOR Flash or SRAM, 16-bit data bus width, nBLEx LOW for reads
	m_apb[APB_CS1OV] = 0b0; // nCS1 is routed for normal operation
	m_apb[APB_EPM] = 0b1111; // All external devices are accessible following reset

	m_apb[APB_REMAP] = 0b00; // Map nCS1
	apb_remap(m_apb[APB_REMAP]);
}

DEVICE_IMAGE_LOAD_MEMBER(pixter_multimedia_state::cart_load)
{
	uint64_t length;
	memory_region *cart_rom = nullptr;
	if (m_cart->loaded_through_softlist()) {
		cart_rom = m_cart->memregion("rom");
		if (!cart_rom) {
			return std::make_pair(image_error::BADSOFTWARE, "Software list item has no 'rom' data area");
		}
		length = cart_rom->bytes();
	} else {
		length = m_cart->length();
	}

	if (!length) {
		return std::make_pair(image_error::INVALIDLENGTH, "Cartridges must not be empty");
	}
	if (length & 3) {
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported cartridge size (must be a multiple of 4 bytes)");
	}

	if (!m_cart->loaded_through_softlist()) {
		cart_rom = machine().memory().region_alloc(m_cart->subtag("rom"), length, 4, ENDIANNESS_LITTLE);
		if (!cart_rom) {
			return std::make_pair(std::errc::not_enough_memory, std::string());
		}

		uint32_t *const base = reinterpret_cast<uint32_t *>(cart_rom->base());
		if (m_cart->fread(base, length) != length) {
			return std::make_pair(std::errc::io_error, "Error reading cartridge file");
		}

		if (ENDIANNESS_NATIVE != ENDIANNESS_LITTLE) {
			for (uint64_t i = 0; (length / 4) > i; ++i)
				base[i] = swapendian_int32(base[i]);
		}
	}

	return std::make_pair(std::error_condition(), std::string());
}

void pixter_multimedia_state::arm7_map(address_map &map)
{
	// Remap Bank
	map(0x0000'0000, 0x003f'ffff).view(m_remap_view);
	m_remap_view[0](0x0000'0000, 0x0000'1fff).rom().region("bootrom", 0);
	m_remap_view[1](0x0000'0000, 0x003f'ffff).ram().share("ndcs0");
	m_remap_view[2](0x0000'0000, 0x0000'3fff).ram().share("internal_sram");
	m_remap_view[3](0x0000'0000, 0x003f'ffff).rom().region("ncs1", 0);

	// External SRAM
	map(0x2000'0000, 0x203f'ffff).ram().share("ndcs0");
	// nCS0 (Unused NAND Flash?)
	// map(0x40000000, 0x403fffff)
	// nCS1 (Chip-On-Board ROM)
	map(0x4400'0000, 0x443f'ffff).mirror(0x03c0'0000).rom().region("ncs1", 0);
	// nCS2 (Cart ROM)
	// map(0x4800'0000, 0x483f'ffff)
	// nCS3 (Unused?)
	// map(0x4c00'0000, 0x4c3f'ffff)

	// Internal SRAM
	map(0x6000'0000, 0x6000'3fff).mirror(0x0fff'c000).ram().share("internal_sram");

	// Boot ROM
	map(0x8000'0000, 0x8000'1fff).rom().region("bootrom", 0);

	// APB Bridge
	map(0xfffc'0000, 0xfffe'6fff).ram().share("apb").w(FUNC(pixter_multimedia_state::apb_bridge_w));
	// External Memory Control
	map(0xffff'1000, 0xffff'1fff).ram();
	// Color LCD Control
	map(0xffff'4000, 0xffff'4fff).ram();
	// USB Device
	map(0xffff'5000, 0xffff'5fff).ram();
	// Interrupt Vector Control
	map(0xffff'f000, 0xffff'ffff).ram();
}

void pixter_multimedia_state::apb_bridge_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	if (offset == m_apb[APB_REMAP]) {
		apb_remap(data);
	}

	COMBINE_DATA(&m_apb[offset]);
}

static INPUT_PORTS_START( pixter_multimedia )
INPUT_PORTS_END

void pixter_multimedia_state::pixter_multimedia(machine_config &config)
{
	// User's Guide - 1.3 Clock Strategy - AHB Fast CPU Clock (FCLK)
	ARM7(config, m_maincpu, 76'205'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &pixter_multimedia_state::arm7_map);

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "pixter_cart");
	m_cart->set_endian(ENDIANNESS_LITTLE);
	m_cart->set_width(GENERIC_ROM32_WIDTH);
	m_cart->set_device_load(FUNC(pixter_multimedia_state::cart_load));
	m_cart->set_must_be_loaded(false);

	SOFTWARE_LIST(config, "cart_list").set_original("pixter_cart");
}

ROM_START( pixtermu )
	ROM_REGION32_LE( 0x400000, "ncs1", 0 )
	ROM_LOAD( "cs1.bin", 0x00000000, 0x400000, CRC(9d06745a) SHA1(c85ffd1777ffee4e99e5a208e3707a39b0dfc3aa) )

	ROM_REGION32_LE( 0x2000, "bootrom", 0 )
	ROM_LOAD( "lh79524.bootrom.bin", 0x00000000, 0x2000, CRC(5314a9e3) SHA1(23ed1914c7e7cc875cbb9f9b3d511a60a7324abd) )
ROM_END

} // anonymous namespace


//    year, name,     parent,  compat, machine,           input,             class,                   init,       company,  fullname,             flags
CONS( 2005, pixtermu, 0,       0,      pixter_multimedia, pixter_multimedia, pixter_multimedia_state, empty_init, "Mattel", "Pixter Multi-Media", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



pk32.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Andrei I. Holub
/**********************************************************************
    Elektronika PK-32

* Terminal Commands:
E <memoryType> <address>          (E)xamine
                                  <memoryType>: /G - General purpose reg, /S - System mem, /I - Internal reg, /P - Phisical mem, /V - Virtual mem
                                  Example: E /S 0
D <memoryType> <address> <value>  (D)eposit
                                  <memoryType>: see E
                                  Example: D /S 0 44332211
C                                 (C)ontinue user programm
B                                 (B)oot
T [<num>]                         (T)est All or <num>=1-6
I                                 (I)nit


* Sample User Program:
2000: MOVB #20,R0  | 908f2050
2004: MOVL #23,R6  | d08f2300000056
200B: MOVL #22,R7  | d08f2200000057
2012: MFPR R7,R8   | db5758
2015: BITB #80,R8  | 938f8058
2019: BEQL 2012    | 13f7
201B: NOP          | 01
201C: NOP          | 01
201D: NOP          | 01
201E: MTPR R0,R6   | da5056
2021: INCB R0      | 9650
2023: BICB2 #C0,R0 | 8a8fc050
2027: BISB2 #30,R0 | 888f3050
202B: BRB 2012     | 11e5

** Use terminal:
D 2000 50208F90
D 2004 00238FD0
D 2008 D0560000
D 200C 0000228F
D 2010 57DB5700
D 2014 808F9358
D 2018 01f71358
D 201C 50DA0101
D 2020 8A509656
D 2024 8850C08f
D 2028 1150308f
D 202C 010101E5
D /G F 2000
C

** Or MAME debugger:
d@2000=50208F90
...
pc=2000

**********************************************************************/

#include "emu.h"

#include "cpu/mpk1839/kl1839vm1.h"
#include "machine/ram.h"
#include "machine/terminal.h"


#define LOG_IO    (1U << 1)
#define LOG_MEM   (1U << 2)
#define LOG_TERM  (1U << 3)

#define VERBOSE ( LOG_GENERAL /*| LOG_IO | LOG_MEM | LOG_TERM*/ )
#include "logmacro.h"

#define LOGIO(...)   LOGMASKED(LOG_IO,   __VA_ARGS__)
#define LOGMEM(...)  LOGMASKED(LOG_MEM,  __VA_ARGS__)
#define LOGTERM(...) LOGMASKED(LOG_TERM, __VA_ARGS__)


namespace {

class pk32_state : public driver_device
{
public:
	pk32_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_terminal(*this, "terminal")
		, m_io_rnp(*this, "RNP")
	{}

	void pk32(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void pk32_map_microcode(address_map &map) ATTR_COLD;
	void pk32_map_sysram(address_map &map) ATTR_COLD;
	void pk32_map_ram(address_map &map) ATTR_COLD;
	void pk32_map_io(address_map &map) ATTR_COLD;

	memory_access<24, 0, 0, ENDIANNESS_LITTLE>::specific m_program;
	required_device<kl1839vm1_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<generic_terminal_device> m_terminal;

	required_ioport m_io_rnp; // Initial Start Register

	u32 term_rx_db_r();
	void term_tx_db_w(u32 data);
	u32 term_rx_cs_r();
	void term_rx_cs_w(u32 data);
	u32 term_tx_cs_r();
	void term_tx_cs_w(u32 data);
	void kbd_put(u8 data);

	u8 m_term_data = 0;
	u16 m_term_status = 0;

};


u32 pk32_state::term_rx_db_r()
{
	LOGTERM("RX DB -> %X\n", m_term_data);
	m_term_status = 0x00;
	return m_term_data;
}

void pk32_state::term_tx_db_w(u32 data)
{
	LOGTERM("RX DB <- %X\n", data);
	m_terminal->write(data);
}

u32 pk32_state::term_rx_cs_r()
{
	//LOGTERM("RX CS -> %X\n", m_term_status);
	return m_term_status;
}

void pk32_state::term_rx_cs_w(u32 data)
{
	LOGTERM("RX CS <- %X\n", data);
	m_term_status = data;
}

u32 pk32_state::term_tx_cs_r()
{
	LOGTERM("TX CS -> %X\n", 0xff);
	// always ready
	return 0xff;
}

void pk32_state::term_tx_cs_w(u32 data)
{
	LOGTERM("TX CS <- %X\n", data);
}

void pk32_state::kbd_put(u8 data)
{
	m_term_data = data;
	m_term_status = 0xff;
}


void pk32_state::pk32_map_microcode(address_map &map)
{
	map(0x0000, 0x3fff).rom().region("microcode", 0);
}

void pk32_state::pk32_map_sysram(address_map &map)
{
	map(0x000000, 0x001fff).ram();
	map(0x800000, 0x800000).lr8(NAME([this](offs_t offset) { return m_io_rnp->read() | ((m_ram->size() >> 20) - 1); }));
}

void pk32_state::pk32_map_ram(address_map &map)
{
	map(0x000000, 0xffffff).ram();
}

void pk32_state::pk32_map_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x3f).unmaprw();

	map(0x20, 0x20).r(FUNC(pk32_state::term_rx_cs_r)).w(FUNC(pk32_state::term_rx_cs_w));
	map(0x21, 0x21).r(FUNC(pk32_state::term_rx_db_r));
	map(0x22, 0x22).r(FUNC(pk32_state::term_tx_cs_r)).w(FUNC(pk32_state::term_tx_cs_w));
	map(0x23, 0x23).w(FUNC(pk32_state::term_tx_db_w));
}

/* Input ports */
static INPUT_PORTS_START( pk32 )
	PORT_START("RNP")
	PORT_CONFNAME( 0xf0, 0x10, "Boot" )
	PORT_CONFSETTING(0x00, "Terminal w/ test")
	PORT_CONFSETTING(0x10, "Terminal w/o test")
	PORT_CONFSETTING(0x20, "OS from MSCP w/ test")
	PORT_CONFSETTING(0x30, "OS from MSCP w/o test")
	PORT_CONFSETTING(0x40, "OS from user's device w/ test")
	PORT_CONFSETTING(0x50, "User's program from 0")
	PORT_CONFSETTING(0x60, "Resident devices")
	PORT_CONFSETTING(0x80, "Accelerator ON")
INPUT_PORTS_END


void pk32_state::machine_start()
{
	m_maincpu->space(AS_PROGRAM).specific(m_program);

	save_item(NAME(m_term_data));
	save_item(NAME(m_term_status));
}

void pk32_state::machine_reset()
{
}

void pk32_state::pk32(machine_config &config)
{
	/* basic machine hardware */
	KL1839VM1(config, m_maincpu, XTAL(10'000'000));
	m_maincpu->set_addrmap(AS_OPCODES, &pk32_state::pk32_map_microcode);
	m_maincpu->set_addrmap(AS_DATA, &pk32_state::pk32_map_sysram);
	m_maincpu->set_addrmap(AS_PROGRAM, &pk32_state::pk32_map_ram);
	m_maincpu->set_addrmap(AS_IO, &pk32_state::pk32_map_io);

	RAM(config, m_ram).set_default_size("16M").set_extra_options("8M,4M");

	/* video hardware */
	GENERIC_TERMINAL(config, m_terminal, 0);
	m_terminal->set_keyboard_callback(FUNC(pk32_state::kbd_put));
}

ROM_START(pk32)
	ROM_REGION32_BE(0x10000, "microcode", ROMREGION_ERASE00)
	ROM_DEFAULT_BIOS("v1")

	ROM_SYSTEM_BIOS(0, "v1", "1839src")
	ROMX_LOAD("fw-1839-src.bin", 0x00000, 0x10000, CRC(78c4d298) SHA1(eb3828718991968b4121e6819ae4c6859a8a3a5a), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "v2", "1839isa")
	ROMX_LOAD("fw-1839-isa.bin", 0x00000, 0x10000, CRC(180930a7) SHA1(dbcc7d665d28011b9c2beba3cc0e4073f34d7fc6), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "v3", "1839pe1")
	ROMX_LOAD("fw-1839pe1-006.bin", 0x00000, 0x10000, CRC(e6bb7639) SHA1(759993afc9b61d9cf83acf3e361dcec571beccf1), ROM_BIOS(2))
ROM_END


} // Anonymous namespace

/*    YEAR  NAME   PARENT COMPAT MACHINE INPUT CLASS       INIT        COMPANY        FULLNAME FLAGS */
COMP( 1991, pk32,  0,     0,     pk32,   pk32, pk32_state, empty_init, "Elektronika", "PK-32", MACHINE_NOT_WORKING)



pk8000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************

PK-8000

2009-05-12 Skeleton driver.
2009-07-18 Mostly working driver

****************************************************************************/

#include "emu.h"

#include "pk8000_v.h"

#include "cpu/i8085/i8085.h"
#include "machine/i8255.h"
#include "imagedev/cassette.h"
#include "sound/spkrdev.h"
#include "machine/ram.h"

#include "screen.h"
#include "speaker.h"

#include "formats/fmsx_cas.h"


namespace {

class pk8000_state : public pk8000_base_state
{
public:
	pk8000_state(const machine_config &mconfig, device_type type, const char *tag)
		: pk8000_base_state(mconfig, type, tag)
		, m_cassette(*this, "cassette")
		, m_ram(*this, RAM_TAG)
		, m_speaker(*this, "speaker")
		, m_region_maincpu(*this, "maincpu")
		, m_banks(*this, "bank%u", 0U)
		, m_io_joy1(*this, "JOY1")
		, m_io_joy2(*this, "JOY2")
		, m_keyboard(*this, "LINE%u", 0U)
	{ }

	void pk8000(machine_config &config);

private:
	u8 m_keyboard_line = 0U;

	u8 joy_1_r();
	u8 joy_2_r();
	void _80_porta_w(u8 data);
	u8 _80_portb_r();
	void _80_portc_w(u8 data);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	INTERRUPT_GEN_MEMBER(interrupt);
	IRQ_CALLBACK_MEMBER(irq_callback);

	void pk8000_io(address_map &map) ATTR_COLD;
	void pk8000_mem(address_map &map) ATTR_COLD;

	required_device<cassette_image_device> m_cassette;
	required_device<ram_device> m_ram;
	required_device<speaker_sound_device> m_speaker;
	required_memory_region m_region_maincpu;
	required_memory_bank_array<8> m_banks;
	required_ioport m_io_joy1;
	required_ioport m_io_joy2;
	required_ioport_array<10> m_keyboard;

	void set_bank(u8 data);
};



void pk8000_state::set_bank(u8 data)
{
	u8 *rom = m_region_maincpu->base();
	u8 *ram = m_ram->pointer();
	u8 block1 = data & 3;
	u8 block2 = (data >> 2) & 3;
	u8 block3 = (data >> 4) & 3;
	u8 block4 = (data >> 6) & 3;

	switch(block1) {
		case 0:
				m_banks[0]->set_base(rom);
				m_banks[4]->set_base(ram);
				break;
		case 1: break;
		case 2: break;
		case 3:
				m_banks[0]->set_base(ram);
				m_banks[4]->set_base(ram);
				break;
	}

	switch(block2) {
		case 0:
				m_banks[1]->set_base(rom + 0x4000);
				m_banks[5]->set_base(ram + 0x4000);
				break;
		case 1: break;
		case 2: break;
		case 3:
				m_banks[1]->set_base(ram + 0x4000);
				m_banks[5]->set_base(ram + 0x4000);
				break;
	}
	switch(block3) {
		case 0:
				m_banks[2]->set_base(rom + 0x8000);
				m_banks[6]->set_base(ram + 0x8000);
				break;
		case 1: break;
		case 2: break;
		case 3:
				m_banks[2]->set_base(ram + 0x8000);
				m_banks[6]->set_base(ram + 0x8000);
				break;
	}
	switch(block4) {
		case 0:
				m_banks[3]->set_base(rom + 0xc000);
				m_banks[7]->set_base(ram + 0xc000);
				break;
		case 1: break;
		case 2: break;
		case 3:
				m_banks[3]->set_base(ram + 0xc000);
				m_banks[7]->set_base(ram + 0xc000);
				break;
	}
}
void pk8000_state::_80_porta_w(u8 data)
{
	set_bank(data);
}

u8 pk8000_state::_80_portb_r()
{
	if(m_keyboard_line>9) {
		return 0xff;
	}
	return m_keyboard[m_keyboard_line]->read();
}

void pk8000_state::_80_portc_w(u8 data)
{
	m_keyboard_line = data & 0x0f;

	m_speaker->level_w(BIT(data, 7));

	m_cassette->change_state((BIT(data, 4)) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
	m_cassette->output((BIT(data, 6)) ? +1.0 : 0.0);
}

u8 pk8000_state::joy_1_r()
{
	u8 retVal = (m_cassette->input() > 0.0038 ? 0x80 : 0);
	retVal |= m_io_joy1->read() & 0x7f;
	return retVal;
}
u8 pk8000_state::joy_2_r()
{
	u8 retVal = (m_cassette->input() > 0.0038 ? 0x80 : 0);
	retVal |= m_io_joy2->read() & 0x7f;
	return retVal;
}

void pk8000_state::pk8000_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).bankr("bank0").bankw("bank4");
	map(0x4000, 0x7fff).bankr("bank1").bankw("bank5");
	map(0x8000, 0xbfff).bankr("bank2").bankw("bank6");
	map(0xc000, 0xffff).bankr("bank3").bankw("bank7");
}

void pk8000_state::pk8000_io(address_map &map)
{
	map.unmap_value_high();
	map(0x80, 0x83).rw("ppi8255_1", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x84, 0x87).rw("ppi8255_2", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x88, 0x88).rw(FUNC(pk8000_state::video_color_r), FUNC(pk8000_state::video_color_w));
	map(0x8c, 0x8c).r(FUNC(pk8000_state::joy_1_r));
	map(0x8d, 0x8d).r(FUNC(pk8000_state::joy_2_r));
	map(0x90, 0x90).rw(FUNC(pk8000_state::text_start_r), FUNC(pk8000_state::text_start_w));
	map(0x91, 0x91).rw(FUNC(pk8000_state::chargen_start_r), FUNC(pk8000_state::chargen_start_w));
	map(0x92, 0x92).rw(FUNC(pk8000_state::video_start_r), FUNC(pk8000_state::video_start_w));
	map(0x93, 0x93).rw(FUNC(pk8000_state::color_start_r), FUNC(pk8000_state::color_start_w));
	map(0xa0, 0xbf).rw(FUNC(pk8000_state::color_r), FUNC(pk8000_state::color_w));
	// extras for pk8002
	//map(0x8c, 0x8f).   // cassette in   2x KR1533KP11
	//map(0x94, 0x97).   // video colour
	//map(0x98, 0x9b).w  // audio  KR1533TM9, KR572PA1A, 2x KR555IR8
	//map(0x9c, 0x9f).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));  // part of the audio
}

/*   Input ports */
static INPUT_PORTS_START( pk8000 )
	PORT_START("LINE0")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
	PORT_START("LINE1")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_QUOTE)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_SLASH)
	PORT_START("LINE2")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("|") PORT_CODE(KEYCODE_BACKSLASH)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_CLOSEBRACE)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("~") PORT_CODE(KEYCODE_TILDE)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_EQUALS)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_MINUS_PAD)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
	PORT_START("LINE3")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
	PORT_START("LINE4")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
	PORT_START("LINE5")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
	PORT_START("LINE6")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Rg") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Upr") PORT_CODE(KEYCODE_LCONTROL)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Graf") PORT_CODE(KEYCODE_F10)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Alf") PORT_CODE(KEYCODE_F9)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("???") PORT_CODE(KEYCODE_LALT)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3)
	PORT_START("LINE7")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Stop") PORT_CODE(KEYCODE_F12)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("VSh") PORT_CODE(KEYCODE_BACKSPACE)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Sel") PORT_CODE(KEYCODE_RCONTROL)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER)
	PORT_START("LINE8")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Probel") PORT_CODE(KEYCODE_SPACE)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cls") PORT_CODE(KEYCODE_HOME)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ins") PORT_CODE(KEYCODE_INSERT)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del") PORT_CODE(KEYCODE_DEL)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
	PORT_START("LINE9")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up-Left") PORT_CODE(KEYCODE_7_PAD)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down-Right") PORT_CODE(KEYCODE_3_PAD)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Menu") PORT_CODE(KEYCODE_5_PAD)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Begin") PORT_CODE(KEYCODE_4_PAD)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("End") PORT_CODE(KEYCODE_6_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("End page") PORT_CODE(KEYCODE_PGDN)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Begin page") PORT_CODE(KEYCODE_PGUP)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("JOY1")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP) PORT_PLAYER(1)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN) PORT_PLAYER(1)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT) PORT_PLAYER(1)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_PLAYER(1)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_PLAYER(1)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_PLAYER(1)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("JOY2")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP) PORT_PLAYER(2)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN) PORT_PLAYER(2)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT) PORT_PLAYER(2)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_PLAYER(2)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_PLAYER(2)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_PLAYER(2)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

INTERRUPT_GEN_MEMBER(pk8000_state::interrupt)
{
	device.execute().set_input_line(0, HOLD_LINE);
}

IRQ_CALLBACK_MEMBER(pk8000_state::irq_callback)
{
	return 0xff;
}


void pk8000_state::machine_start()
{
	save_item(NAME(m_keyboard_line));
	save_item(NAME(m_text_start));
	save_item(NAME(m_chargen_start));
	save_item(NAME(m_video_start));
	save_item(NAME(m_color_start));
	save_item(NAME(m_video_mode));
	save_item(NAME(m_video_color));
	save_item(NAME(m_color));
	save_item(NAME(m_video_enable));
}

void pk8000_state::machine_reset()
{
	set_bank(0);
}

u32 pk8000_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return video_update(screen, bitmap, cliprect, m_ram->pointer());
}

void pk8000_state::pk8000(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, 1780000); // pk8002 = 10'220'000 / 8
	m_maincpu->set_addrmap(AS_PROGRAM, &pk8000_state::pk8000_mem);
	m_maincpu->set_addrmap(AS_IO, &pk8000_state::pk8000_io);
	m_maincpu->set_vblank_int("screen", FUNC(pk8000_state::interrupt));
	m_maincpu->set_irq_acknowledge_callback(FUNC(pk8000_state::irq_callback));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(256+32, 192+32);
	screen.set_visarea(0, 256+32-1, 0, 192+32-1);
	screen.set_screen_update(FUNC(pk8000_state::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(pk8000_state::pk8000_palette), 16);

	i8255_device &ppi1(I8255(config, "ppi8255_1"));
	ppi1.out_pa_callback().set(FUNC(pk8000_state::_80_porta_w));
	ppi1.in_pb_callback().set(FUNC(pk8000_state::_80_portb_r));
	ppi1.out_pc_callback().set(FUNC(pk8000_state::_80_portc_w));

	i8255_device &ppi2(I8255(config, "ppi8255_2"));
	ppi2.in_pa_callback().set(FUNC(pk8000_state::_84_porta_r));
	ppi2.out_pa_callback().set(FUNC(pk8000_state::_84_porta_w));
	ppi2.out_pc_callback().set(FUNC(pk8000_state::_84_portc_w));

	/* audio hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(fmsx_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("64K");
}

/* ROM definition */
ROM_START( vesta )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "vesta.rom", 0x0000, 0x4000, CRC(fbf7e2cc) SHA1(4bc5873066124bd926c3c6aa2fd1a062c87af339))
ROM_END

ROM_START( hobby )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "hobby.rom", 0x0000, 0x4000, CRC(a25b4b2c) SHA1(0d86e6e4be8d1aa389bfa9dd79e3604a356729f7))
ROM_END

ROM_START( pk8002 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	// there is actually 1 rom 0000-07ff (U3) mirrored to 0800,1000,1800, then another rom 2000-27ff (U4) mirrored to 2800,3000,3800
	ROM_LOAD( "pk8002.rom", 0x0000, 0x4000, CRC(07b9ae71) SHA1(2137a41cc095c7aba58b7b109fce63f30a4568b2))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY      FULLNAME             FLAGS */
COMP( 1987, vesta,  0,      0,      pk8000,  pk8000, pk8000_state, empty_init, "BP EVM",    "PK8000 Vesta",      MACHINE_SUPPORTS_SAVE )
COMP( 1987, hobby,  vesta,  0,      pk8000,  pk8000, pk8000_state, empty_init, "BP EVM",    "PK8000 Sura/Hobby", MACHINE_SUPPORTS_SAVE )
COMP( 1987, pk8002, vesta,  0,      pk8000,  pk8000, pk8000_state, empty_init, "<unknown>", "PK8002 Elf",        MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



pk8020.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, AJR
/***************************************************************************

PK-8020 driver by Miodrag Milanovic
    based on work of Sergey Erokhin from pk8020.narod.ru

2008-07-18 Preliminary driver.

Cassette is "best guess", as I was unable to locate any recordings, and
also do not know the commands to save and load. SAVE and LOAD appear when
F2 or shift-F2 pressed (in Korvet), but only produce errors.

Status as at 2019-09-18:
Korvet, Neiva - largely working. Error after running something from B drive.
              - floppy operation is very slow.
Kontur - needs to boot from a floppy, not working.
BK8T - Keys to navigate initial config screen are mostly unknown
       (space - change value; Esc - go to next screen).
     - Next screen: wants the date and time. You can press enter here.
     - Wait a while, ignore the big message. You get a menu.
     - Press 1 for a typewriter thing, or 6 for another menu.
     - Not sure about the choices; needs someone who can read Russian.


****************************************************************************/


#include "emu.h"
#include "pk8020.h"

#include "bus/rs232/rs232.h"
#include "cpu/i8085/i8085.h"
#include "machine/clock.h"
#include "machine/i8255.h"
#include "machine/pit8253.h"
#include "formats/pk8020_dsk.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

/* Address maps */
void pk8020_state::pk8020_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(pk8020_state::memory_r), FUNC(pk8020_state::memory_w));
}

void pk8020_state::pk8020_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
}

void pk8020_state::devices_map(address_map &map)
{
	map(0x00, 0x03).mirror(4).rw("ct", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x08, 0x0b).mirror(4).rw("iop3", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x10, 0x11).mirror(6).rw("ios1", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x18, 0x1b).mirror(4).rw(m_fdc, FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	map(0x20, 0x21).mirror(6).rw("ios2", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x28, 0x29).mirror(6).rw(m_inr, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x30, 0x33).mirror(4).rw("iop2", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x38, 0x3b).mirror(4).rw("iop1", FUNC(i8255_device::read), FUNC(i8255_device::write));
}

/* Input ports */
static INPUT_PORTS_START( pk8020 )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_F9)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("_") PORT_CODE(KEYCODE_EQUALS)

	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)

	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_SLASH)

	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("STRN") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("STOP") PORT_CODE(KEYCODE_F12)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("IZ") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("VZ") PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)

	PORT_START("LINE7")
	// All keys in this line are reversed logic
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Alf") PORT_CODE(KEYCODE_F11)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Graf") PORT_CODE(KEYCODE_F10)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Prf") PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Sel") PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Upr") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("OO") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT)

	PORT_START("LINE8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 7") PORT_CODE(KEYCODE_7_PAD)

	PORT_START("LINE9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num .") PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE11")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE12")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE13")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE14")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE15")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END


/* F4 Character Displayer */
static const gfx_layout pk8020_charlayout =
{
	8, 16,                  /* 8 x 16 characters */
	512,                    /* 512 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_pk8020 )
	GFXDECODE_ENTRY( "gfx1", 0x0000, pk8020_charlayout, 0, 4 )
GFXDECODE_END


void pk8020_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_PK8020_FORMAT);
}

static void pk8020_floppies(device_slot_interface &device)
{
	device.option_add("qd", FLOPPY_525_QD);
}

/*
 * interrupts
 *
 * 0    external devices
 * 1    uart rx ready
 * 2    uart tx ready
 * 3    lan
 * 4    vblank
 * 5    timer ch2
 * 6    printer
 * 7    floppy
 */
/* Machine driver */
void pk8020_state::pk8020(machine_config &config)
{
	/* basic machine hardware */
	i8080a_cpu_device &maincpu(I8080A(config, m_maincpu, 20_MHz_XTAL / 8)); // КР580ВМ80А
	maincpu.set_addrmap(AS_PROGRAM, &pk8020_state::pk8020_mem);
	maincpu.set_addrmap(AS_IO, &pk8020_state::pk8020_io);
	maincpu.set_vblank_int("screen", FUNC(pk8020_state::pk8020_interrupt));
	maincpu.in_inta_func().set("inr", FUNC(pic8259_device::acknowledge));

	PLS100(config, m_decplm); // КР556РТ2 (82S100 equivalent; D31)

	ADDRESS_MAP_BANK(config, m_devbank);
	m_devbank->set_addrmap(0, &pk8020_state::devices_map);
	m_devbank->set_data_width(8);
	m_devbank->set_addr_width(6);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(20_MHz_XTAL / 2, 640, 0, 512, 312, 0, 256);
	screen.set_screen_update(FUNC(pk8020_state::screen_update_pk8020));
	screen.set_palette(m_palette);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_pk8020);
	PALETTE(config, m_palette, FUNC(pk8020_state::pk8020_palette), 16);

	i8255_device &iop1(I8255(config, "iop1")); // КР580ВВ55А (D17)
	iop1.in_pa_callback().set(FUNC(pk8020_state::ppi_porta_r));
	iop1.out_pb_callback().set(FUNC(pk8020_state::floppy_control_w));
	iop1.out_pc_callback().set(FUNC(pk8020_state::video_page_w));

	i8255_device &iop2(I8255(config, "iop2")); // КР580ВВ55А (D16)
	iop2.out_pa_callback().set(m_printer, FUNC(centronics_device::write_data0)).bit(0).invert();
	iop2.out_pa_callback().append(m_printer, FUNC(centronics_device::write_data1)).bit(1).invert();
	iop2.out_pa_callback().append(m_printer, FUNC(centronics_device::write_data2)).bit(2).invert();
	iop2.out_pa_callback().append(m_printer, FUNC(centronics_device::write_data3)).bit(3).invert();
	iop2.out_pa_callback().append(m_printer, FUNC(centronics_device::write_data4)).bit(4).invert();
	iop2.out_pa_callback().append(m_printer, FUNC(centronics_device::write_data5)).bit(5).invert();
	iop2.out_pa_callback().append(m_printer, FUNC(centronics_device::write_data6)).bit(6).invert();
	iop2.out_pa_callback().append(m_printer, FUNC(centronics_device::write_data7)).bit(7).invert();
	iop2.out_pc_callback().set(FUNC(pk8020_state::ppi_2_portc_w));

	I8255(config, "iop3"); // КР580ВВ55А (D2)

	pit8253_device &ct(PIT8253(config, "ct")); // КР580ВИ53
	ct.set_clk<0>(20_MHz_XTAL / 10);
	ct.out_handler<0>().set(FUNC(pk8020_state::pit_out0));
	ct.set_clk<1>(20_MHz_XTAL / 10);
	ct.out_handler<1>().set("ios1", FUNC(i8251_device::write_txc));
	ct.out_handler<1>().append("ios1", FUNC(i8251_device::write_rxc));
	ct.set_clk<2>((20_MHz_XTAL / 8) / 164);
	ct.out_handler<2>().set(m_inr, FUNC(pic8259_device::ir5_w));

	PIC8259(config, m_inr); // КР580ВН59
	m_inr->out_int_callback().set_inputline(m_maincpu, 0);

	i8251_device &ios1(I8251(config, "ios1", 20_MHz_XTAL / 10)); // КР580ВВ51А (D10)
	ios1.txd_handler().set("v24", FUNC(rs232_port_device::write_txd));
	ios1.dtr_handler().set("v24", FUNC(rs232_port_device::write_rts));
	ios1.rxrdy_handler().set(m_inr, FUNC(pic8259_device::ir1_w));
	ios1.txrdy_handler().set(m_inr, FUNC(pic8259_device::ir2_w));

	rs232_port_device &v24(RS232_PORT(config, "v24", default_rs232_devices, nullptr));
	v24.rxd_handler().set("ios1", FUNC(i8251_device::write_rxd));
	v24.dsr_handler().set("ios1", FUNC(i8251_device::write_dsr));

	i8251_device &ios2(I8251(config, "ios2", 20_MHz_XTAL / 10)); // КР580ВВ51А (D11)
	ios2.txd_handler().set("line", FUNC(rs232_port_device::write_txd));
	ios2.rxrdy_handler().set(m_inr, FUNC(pic8259_device::ir3_w));

	rs232_port_device &line(RS232_PORT(config, "line", default_rs232_devices, nullptr));
	line.rxd_handler().set("ios2", FUNC(i8251_device::write_rxd));

	clock_device &c1(CLOCK(config, "c1", 20_MHz_XTAL / 8 / 4));
	c1.signal_handler().set("ios2", FUNC(i8251_device::write_txc));
	c1.signal_handler().append("ios2", FUNC(i8251_device::write_rxc));

	KR1818VG93(config, m_fdc, 20_MHz_XTAL / 20); // КР1818ВГ93
	m_fdc->intrq_wr_callback().set(m_inr, FUNC(pic8259_device::ir7_w));

	for (auto &floppy : m_floppy)
		FLOPPY_CONNECTOR(config, floppy, pk8020_floppies, "qd", pk8020_state::floppy_formats);

	SOFTWARE_LIST(config, "flop_list").set_original("korvet_flop");

	/* audio hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	CENTRONICS(config, m_printer, centronics_devices, nullptr);
	m_printer->busy_handler().set(m_inr, FUNC(pic8259_device::ir6_w)).invert();

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("258K").set_default_value(0x00); // 64 + 4*48 + 2 = 258
}

/* ROM definition */

ROM_START( korvet )
	ROM_REGION(0x6000, "maincpu", ROMREGION_ERASEFF)
	ROM_DEFAULT_BIOS("v11")
	ROM_SYSTEM_BIOS(0, "v11", "v1.1")
	ROMX_LOAD("korvet11.rom", 0x0000, 0x6000, CRC(81bdc2af) SHA1(c3484c3f1f3d252475979283c073286b8661d2b9), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v20", "v2.0")
	ROMX_LOAD("korvet20.rom", 0x0000, 0x6000, CRC(d6c36a45) SHA1(dba67e63457251814ad5c0fe6bb6d584eea5c7d2), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "cpm", "cpm")
	ROMX_LOAD("cpm.rom",      0x0000, 0x4000, CRC(7a38d7f6) SHA1(fec6623291a38990b003e818683cd5edfb494c36), ROM_BIOS(2))

	ROM_REGION(0x2000, "gfx1", 0)
	ROM_LOAD("korvet2.fnt", 0x0000, 0x2000, CRC(fb1cd3d4) SHA1(58f1d6e393253b1e8b497ce0880b6eff6d85b42a))

	ROM_REGION(0xf5, "decplm", 0)
	ROM_LOAD("kr556rt2.d31", 0x00, 0xf5, CRC(3eae3879) SHA1(87f419e26d73d7b2f937c3fcae0415b74da37d97) BAD_DUMP) // devised from documentation
ROM_END

ROM_START( neiva )
	ROM_REGION(0x6000, "maincpu", ROMREGION_ERASEFF)
	ROM_LOAD("neiva_d22.bin", 0x0000, 0x2000, CRC(9cc28f67) SHA1(68f390e846e1290df68419d522088d5325682945))
	ROM_LOAD("neiva_d23.bin", 0x2000, 0x2000, CRC(31b53dc4) SHA1(607f2a2d8b1de469125c6c02b9ffc65649b753a2))
	ROM_LOAD("neiva_d24.bin", 0x4000, 0x2000, CRC(d05c80df) SHA1(1ec2fa9983be5579abff7247fc9b98fe50661bd9))

	ROM_REGION(0x2000, "gfx1", 0)
	ROM_LOAD("neiva_d21.bin", 0x0000, 0x2000, CRC(fb1cd3d4) SHA1(58f1d6e393253b1e8b497ce0880b6eff6d85b42a))

	ROM_REGION(0xf5, "decplm", 0)
	ROM_LOAD("kr556rt2.d31", 0x00, 0xf5, CRC(3eae3879) SHA1(87f419e26d73d7b2f937c3fcae0415b74da37d97) BAD_DUMP)
ROM_END

ROM_START( kontur )
	ROM_REGION(0x6000, "maincpu", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "v1", "v1")
	ROMX_LOAD("kontur.rom",  0x0000, 0x2000, CRC(92cd441e) SHA1(9a0f9079256cefc6169ae4ba2114841d1f380480), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v2", "v2")
	ROMX_LOAD("kontur2.rom", 0x0000, 0x2000, CRC(5256d101) SHA1(22022a3c6882dbc5ea28d7815f00c182bbaef9e1), ROM_BIOS(1))

	ROM_REGION(0x2000, "gfx1", 0)
	ROM_LOAD("kontur.fnt", 0x0000, 0x2000, CRC(14d33790) SHA1(6d5fcb214805c5fc44ef98a97219158ff7826ac0))

	ROM_REGION(0xf5, "decplm", 0)
	ROM_LOAD("kr556rt2.d31", 0x00, 0xf5, CRC(3eae3879) SHA1(87f419e26d73d7b2f937c3fcae0415b74da37d97) BAD_DUMP)
ROM_END

ROM_START( bk8t )
	ROM_REGION(0x6000, "maincpu", ROMREGION_ERASEFF)
	ROM_LOAD("kor1.bin", 0x0000, 0x2000, CRC(f1e16ddc) SHA1(e3a10c9ce3f333928eb0d5f9b84e159e41fae6ca))
	ROM_LOAD("kor2.bin", 0x2000, 0x2000, CRC(d4431d97) SHA1(08f79785846369d410a4183f0d60b856d6d70199))
	ROM_LOAD("kor3.bin", 0x4000, 0x2000, CRC(74781903) SHA1(caaa638afe80eb83fc30b07dd6d1e40b66ddc6d1))

	ROM_REGION(0x2000, "gfx1", 0)
	ROM_LOAD("kor4.bin", 0x0000, 0x2000, CRC(d164bada) SHA1(c334e50fd31b1f42c7668b89772487971a6875cb))

	ROM_REGION(0xf5, "decplm", 0)
	ROM_LOAD("kr556rt2.d31", 0x00, 0xf5, CRC(3eae3879) SHA1(87f419e26d73d7b2f937c3fcae0415b74da37d97) BAD_DUMP)
ROM_END

/* Driver */

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY      FULLNAME         FLAGS */
COMP( 1987, korvet, 0,      0,      pk8020,  pk8020, pk8020_state, empty_init, "<unknown>", "PK8020 Korvet", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)
COMP( 1987, neiva,  korvet, 0,      pk8020,  pk8020, pk8020_state, empty_init, "<unknown>", "PK8020 Neiva",  MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)
COMP( 1987, kontur, korvet, 0,      pk8020,  pk8020, pk8020_state, empty_init, "<unknown>", "PK8020 Kontur", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)
COMP( 1987, bk8t,   korvet, 0,      pk8020,  pk8020, pk8020_state, empty_init, "<unknown>", "BK-8T",         MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)



plan80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************

Plan-80

2009-12-06 Skeleton driver.

Summary of Monitor commands:

D - dump memory
F - fill memory
G - go (execute program at address)
I - in from a port and display
M - move?
O - out to a port
S - edit memory

ToDo:
- fix autorepeat on the keyboard
- Add missing devices
- Picture of unit shows graphics, possibly a PCG

****************************************************************************/

#include "emu.h"

#include "cpu/i8085/i8085.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class plan80_state : public driver_device
{
public:
	plan80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_vram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
		, m_speaker(*this, "speaker")
	{ }

	void plan80(machine_config &config);

private:
	u8 port04_r();
	void port09_w(u8 data);
	void port10_w(u8 data);
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	u8 m_kbd_row = 0U;
	bool m_spk_pol = false;
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	memory_passthrough_handler m_rom_shadow_tap;
	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_shared_ptr<u8> m_vram;
	required_region_ptr<u8> m_p_chargen;
	required_device<speaker_sound_device> m_speaker;
};

u8 plan80_state::port04_r()
{
	u8 data = 0xff;

	if (m_kbd_row == 0xfe)
		data = ioport("LINE0")->read();
	else
	if (m_kbd_row == 0xfd)
		data = ioport("LINE1")->read();
	else
	if (m_kbd_row == 0xfb)
		data = ioport("LINE2")->read();
	else
	if (m_kbd_row == 0xf7)
		data = ioport("LINE3")->read();
	else
	if (m_kbd_row == 0xef)
		data = ioport("LINE4")->read();

	return data;
}

void plan80_state::port09_w(u8 data)
{
	m_kbd_row = data;
}

void plan80_state::port10_w(u8 data)
{
	m_spk_pol ^= 1;
	m_speaker->level_w(m_spk_pol);
}


void plan80_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xefff).ram().share("mainram");
	map(0xf000, 0xf7ff).ram().share("videoram");
	map(0xf800, 0xffff).rom().region("maincpu", 0);
}

void plan80_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x04, 0x04).r(FUNC(plan80_state::port04_r));
	map(0x09, 0x09).w(FUNC(plan80_state::port09_w));
	map(0x10, 0x10).w(FUNC(plan80_state::port10_w));
}

/* Input ports */
static INPUT_PORTS_START( plan80 ) // Keyboard was worked out by trial & error;'F' keys produce foreign symbols
	PORT_START("LINE0") /* FE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("??") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("??") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A -") PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('-')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q ! 1") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('!') PORT_CHAR('1')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F-shift") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P ?? 0") PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('0')
	PORT_START("LINE1") /* FD */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Numbers") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X /") PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('/')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D =") PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('=')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E # 3") PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('#') PORT_CHAR('3')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M .") PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('.')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K [") PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('[')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I ( 8") PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('(') PORT_CHAR('8')
	PORT_START("LINE2") /* FB */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V ;") PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR(';')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G _") PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('_')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T % 5") PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('%') PORT_CHAR('5')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B ?") PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('?')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H <") PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('<')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y & 6") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('&') PORT_CHAR('6')
	PORT_START("LINE3") /* F7 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C :") PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR(':')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F ^") PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('^')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R $ 4") PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('$') PORT_CHAR('4')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N ,") PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR(',')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J >") PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('>')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U \' 7") PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('\'') PORT_CHAR('7')
	PORT_START("LINE4") /* EF */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("??") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("??") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z *") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S +") PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('+')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W \" 2") PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('\"') PORT_CHAR('2')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("??") PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L ]") PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR(']')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O ) 9") PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR(')') PORT_CHAR('9')
INPUT_PORTS_END


void plan80_state::machine_reset()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x0000, 0x07ff, m_rom);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0xf800, 0xffff,
			"rom_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// reinstall RAM over the ROM shadow
					m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x07ff, m_ram);
				}
			},
			&m_rom_shadow_tap);
}

void plan80_state::machine_start()
{
	save_item(NAME(m_kbd_row));
	save_item(NAME(m_spk_pol));
}

u32 plan80_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 sy=0,ma=0;

	for (u8 y = 0; y < 32; y++)
	{
		for (u8 ra = 0; ra < 8; ra++)
		{
			u16 *p = &bitmap.pix(sy++);

			for (u16 x = ma; x < ma+48; x++)
			{
				u8 chr = m_vram[x];
				u8 gfx = m_p_chargen[(chr << 3) | ra] ^ (BIT(chr, 7) ? 0xff : 0);

				/* Display a scanline of a character */
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
			}
		}
		ma+=64;
	}
	return 0;
}

/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_plan80 )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END


void plan80_state::plan80(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, 2048000);
	m_maincpu->set_addrmap(AS_PROGRAM, &plan80_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &plan80_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(plan80_state::screen_update));
	screen.set_size(48*6, 32*8);
	screen.set_visarea(0, 48*6-1, 0, 32*8-1);
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_plan80);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);
}

/* ROM definition */
ROM_START( plan80 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "pl80mon.bin", 0x0000, 0x0800, CRC(433fb685) SHA1(43d53c35544d3a197ab71b6089328d104535cfa5) )

	// This rom 2nd half is missing, so useless. It uses the videoram address range, so it might have been for some other system
	//ROM_REGION( 0x10000, "spare", 0 )
	//ROM_LOAD_OPTIONAL( "pl80mod.bin", 0xf000, 0x0800, CRC(6bdd7136) SHA1(721eab193c33c9330e0817616d3d2b601285fe50))

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "pl80gzn.bin", 0x0000, 0x0800, CRC(b4ddbdb6) SHA1(31bf9cf0f2ed53f48dda29ea830f74cea7b9b9b2) )
ROM_END

} // anonymous namespace

/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT         COMPANY        FULLNAME   FLAGS
COMP( 1988, plan80, 0,      0,      plan80,  plan80, plan80_state, empty_init, "Tesla Eltos", "Plan-80", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



plus4.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

    TODO:

    - c16 function ROM test fails
    - clean up TED
    - verify PLA
    - T6721 speech chip

*/

#include "emu.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "bus/cbmiec/cbmiec.h"
#include "bus/pet/c2n.h"
#include "bus/pet/cass.h"
#include "bus/pet/diag264_lb_tape.h"
#include "bus/plus4/exp.h"
#include "bus/plus4/user.h"
#include "bus/vcs_ctrl/ctrl.h"
#include "cpu/m6502/m7501.h"
#include "imagedev/snapquik.h"
#include "cbm_snqk.h"
#include "machine/input_merger.h"
#include "machine/mos6529.h"
#include "machine/mos6551.h"
#include "machine/mos8706.h"
#include "machine/pla.h"
#include "machine/ram.h"
#include "sound/mos7360.h"
#include "sound/t6721a.h"


namespace {

#define MOS7360_TAG         "u1"
#define MOS6551_TAG         "u3"
#define MOS6529_USER_TAG    "u5"
#define MOS6529_KB_TAG      "u27"
#define T6721A_TAG          "t6721a"
#define MOS8706_TAG         "mos8706"
#define PLA_TAG             "u19"
#define SCREEN_TAG          "screen"
#define CONTROL1_TAG        "joy1"
#define CONTROL2_TAG        "joy2"
#define PET_USER_PORT_TAG   "user"

class plus4_state : public driver_device
{
public:
	plus4_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "u2"),
		m_pla(*this, PLA_TAG),
		m_ted(*this, MOS7360_TAG),
		m_acia(*this, MOS6551_TAG),
		m_spi_user(*this, MOS6529_USER_TAG),
		m_spi_kb(*this, MOS6529_KB_TAG),
		m_vslsi(*this, MOS8706_TAG),
		m_iec(*this, CBM_IEC_TAG),
		m_joy1(*this, CONTROL1_TAG),
		m_joy2(*this, CONTROL2_TAG),
		m_exp(*this, "exp"),
		m_user(*this, PET_USER_PORT_TAG),
		m_ram(*this, RAM_TAG),
		m_cassette(*this, PET_DATASSETTE_PORT_TAG),
		m_kernal(*this, "kernal"),
		m_function(*this, "function"),
		m_c2(*this, "c2"),
		m_row(*this, "ROW%u", 0),
		m_lock(*this, "LOCK"),
		m_addr(0)
	{ }

	void plus4(machine_config &config);

	void cpu_w(uint8_t data);

protected:
	required_device<m7501_device> m_maincpu;
	required_device<pla_device> m_pla;
	required_device<mos7360_device> m_ted;
	optional_device<mos6551_device> m_acia;
	optional_device<mos6529_device> m_spi_user;
	required_device<mos6529_device> m_spi_kb;
	optional_device<mos8706_device> m_vslsi;
	required_device<cbm_iec_device> m_iec;
	required_device<vcs_control_port_device> m_joy1;
	required_device<vcs_control_port_device> m_joy2;
	required_device<plus4_expansion_slot_device> m_exp;
	optional_device<pet_user_port_device> m_user;
	required_device<ram_device> m_ram;
	required_device<pet_datassette_port_device> m_cassette;
	required_memory_region m_kernal;
	optional_memory_region m_function;
	optional_memory_region m_c2;
	required_ioport_array<8> m_row;
	required_ioport m_lock;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void bankswitch(offs_t offset, int phi0, int mux, int ras, int *scs, int *phi2, int *user, int *_6551, int *addr_clk, int *keyport, int *kernal);
	uint8_t read_memory(offs_t offset, int ba, int scs, int phi2, int user, int _6551, int addr_clk, int keyport, int kernal);

	uint8_t read(offs_t offset);
	void write(offs_t offset, uint8_t data);
	uint8_t ted_videoram_r(offs_t offset);

	uint8_t cpu_r();

	uint8_t ted_k_r(offs_t offset);

	void write_kb0(int state) { if (state) m_kb |= 1; else m_kb &= ~1; }
	void write_kb1(int state) { if (state) m_kb |= 2; else m_kb &= ~2; }
	void write_kb2(int state) { if (state) m_kb |= 4; else m_kb &= ~4; }
	void write_kb3(int state) { if (state) m_kb |= 8; else m_kb &= ~8; }
	void write_kb4(int state) { if (state) m_kb |= 16; else m_kb &= ~16; }
	void write_kb5(int state) { if (state) m_kb |= 32; else m_kb &= ~32; }
	void write_kb6(int state) { if (state) m_kb |= 64; else m_kb &= ~64; }
	void write_kb7(int state) { if (state) m_kb |= 128; else m_kb &= ~128; }

	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_c16);

	enum
	{
		CS0_BASIC = 0,
		CS0_FUNCTION_LO,
		CS0_C1_LOW,
		CS0_C2_LOW
	};

	enum
	{
		CS1_KERNAL = 0,
		CS1_FUNCTION_HI,
		CS1_C1_HIGH,
		CS1_C2_HIGH
	};

	// memory state
	uint8_t m_addr;

	// keyboard state
	uint8_t m_kb;

	void plus4_mem(address_map &map) ATTR_COLD;
	void ted_videoram_map(address_map &map) ATTR_COLD;
};


class c16_state : public plus4_state
{
public:
	c16_state(const machine_config &mconfig, device_type type, const char *tag)
		: plus4_state(mconfig, type, tag)
	{ }

	void v364(machine_config &config);
	void c16n(machine_config &config);
	void c16p(machine_config &config);
	void c232(machine_config &config);
	void plus4p(machine_config &config);
	void plus4n(machine_config &config);

private:
	uint8_t cpu_r();
};



//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

#define BA15 BIT(offset, 15)
#define BA14 BIT(offset, 14)
#define BA13 BIT(offset, 13)
#define BA12 BIT(offset, 12)
#define BA11 BIT(offset, 11)
#define BA10 BIT(offset, 10)
#define BA9 BIT(offset, 9)
#define BA8 BIT(offset, 8)
#define BA7 BIT(offset, 7)
#define BA6 BIT(offset, 6)
#define BA5 BIT(offset, 5)
#define BA4 BIT(offset, 4)



QUICKLOAD_LOAD_MEMBER(plus4_state::quickload_c16)
{
	return general_cbm_loadsnap(image, m_maincpu->space(AS_PROGRAM), 0, cbm_quick_sethiaddress);
}

//**************************************************************************
//  MEMORY MANAGEMENT
//**************************************************************************

void plus4_state::bankswitch(offs_t offset, int phi0, int mux, int ras, int *scs, int *phi2, int *user, int *_6551, int *addr_clk, int *keyport, int *kernal)
{
	uint16_t i = ras << 15 | BA10 << 14 | BA11 << 13 | BA13 << 12 | BA9 << 11 | BA8 << 10 | BA14 << 9 | mux << 8 | BA12 << 7 | BA7 << 6 | BA6 << 5 | BA5 << 4 | BA4 << 3 | BA15 << 2 | phi0 << 1 | 1;
/*  uint8_t data = m_pla->read(i);

    *scs = BIT(data, 0);
    *phi2 = BIT(data, 1);
    *user = BIT(data, 2);
    *_6551 = BIT(data, 3);
    *addr_clk = BIT(data, 4);
    *keyport = BIT(data, 5);
    *kernal = BIT(data, 6);
    *f7 = BIT(data, 7);*/

	// the following code is on loan from http://www.zimmers.net/anonftp/pub/cbm/firmware/computers/plus4/pla.c until we get the PLA dumped

	#define I(b) (!!((i) & (1 << b)))

	#define I0_F7   I(0)
	#define PHI0    I(1)
	#define A15 I(2)
	#define A4  I(3)
	#define A5  I(4)
	#define A6  I(5)
	#define A7  I(6)
	#define A12 I(7)
	#define MUX I(8)
	#define A14 I(9)
	#define A8  I(10)
	#define A9  I(11)
	#define A13 I(12)
	#define A11 I(13)
	#define A10 I(14)
	#define RAS_    I(15)

	/* unused_  0 when 0111 011x 1001 011x */
	#define F0  RAS_ || !A10 || !A11 || !A13 || A9 || !A8 || !A14 ||    \
			!A12 || A7 || A6 || !A5 || A4 || !A15 || !PHI0
	/* PHI2     1 when 0xxx xxxx xxxx xx11 */
	#define F1  !RAS_ && PHI0 && I0_F7
	/* USER_    0 when 0111 011x 1000 1111 */
	#define F2  RAS_ || !A10 || !A11 || !A13 || A9 || !A8 || !A14 ||     \
			!A12 || A7 || A6 || A5 || !A4 || !A15 || !PHI0 || !I0_F7
	/* 6551_    0 when x111 011x 1000 011x */
	#define F3  !A10 || !A11 || !A13 || A9 || !A8 || !A14 ||    \
			!A12 || A7 || A6 || A5 || A4 || !A15 || !PHI0
	/* ADDR_CLK 0 when 1111 011x 1110 1111 */
	#define F4  RAS_ || !A10 || !A11 || !A13 || A9 || !A8 || !A14 ||    \
			!A12 || !A7 || !A6 || A5 || !A4 || !A15 || !PHI0 || !I0_F7
	/* KEYPORT_ 0 when 0111 011x 1001 1111 */
	#define F5  RAS_ || !A10 || !A11 || !A13 || A9 || !A8 || !A14 ||    \
			!A12 || A7 || A6 || !A5 || !A4 || !A15 || !PHI0 || !I0_F7
	/* KERNAL_  1 when x111 001x 1xxx x1xx */
	#define F6  A10 && A11 && A13 && !A9 && !A8 && A14 &&   \
			A12 && A15
	/* I0_F7    1 when xxxx xxx1 xxxx xxxx or
	          when 0xxx xxxx xxxx xx11 */
	#define F7  MUX || (F1)

	*scs = F0;
	*phi2 = F1;
	*user = F2;
	*_6551 = F3;
	*addr_clk = F4;
	*keyport = F5;
	*kernal = F6;
}


//-------------------------------------------------
//  read_memory -
//-------------------------------------------------

uint8_t plus4_state::read_memory(offs_t offset, int ba, int scs, int phi2, int user, int _6551, int addr_clk, int keyport, int kernal)
{
	int cs0 = 1, cs1 = 1, c1l = 1, c1h = 1, c2l = 1, c2h = 1;
	uint8_t data = m_ted->read(offset, cs0, cs1);

	//logerror("offset %04x user %u 6551 %u addr_clk %u keyport %u kernal %u cs0 %u cs1 %u\n", offset,user,_6551,addr_clk,keyport,kernal,cs0,cs1);

	if (!scs && m_vslsi)
	{
		data = m_vslsi->read(offset & 0x03);
	}
	else if (!user)
	{
		if (m_spi_user)
		{
			data = m_spi_user->read();
		}

		data &= ~0x04;
		data |= m_cassette->sense_r() << 2;
	}
	else if (!_6551 && m_acia)
	{
		data = m_acia->read(offset & 0x03);
	}
	else if (!keyport)
	{
		data = m_spi_kb->read();
	}
	else if (!cs0)
	{
		switch (m_addr & 0x03)
		{
		case CS0_BASIC:
			data = m_kernal->base()[offset & 0x7fff];
			break;

		case CS0_FUNCTION_LO:
			if (m_function != nullptr)
			{
				data = m_function->base()[offset & 0x7fff];
			}
			break;

		case CS0_C1_LOW:
			c1l = 0;
			break;

		case CS0_C2_LOW:
			c2l = 0;

			if (m_c2 != nullptr)
			{
				data = m_c2->base()[offset & 0x7fff];
			}
			break;
		}
	}
	else if (!cs1)
	{
		if (kernal)
		{
			data = m_kernal->base()[offset & 0x7fff];
		}
		else
		{
			switch ((m_addr >> 2) & 0x03)
			{
			case CS1_KERNAL:
				data = m_kernal->base()[offset & 0x7fff];
				break;

			case CS1_FUNCTION_HI:
				if (m_function != nullptr)
				{
					data = m_function->base()[offset & 0x7fff];
				}
				break;

			case CS1_C1_HIGH:
				c1h = 0;
				break;

			case CS1_C2_HIGH:
				c2h = 0;

				if (m_c2 != nullptr)
				{
					data = m_c2->base()[offset & 0x7fff];
				}
				break;
			}
		}
	}
	else if (offset < 0xfd00 || offset >= 0xff20)
	{
		data = m_ram->pointer()[offset & m_ram->mask()];
	}

	return m_exp->cd_r(offset, data, ba, cs0, c1l, c1h, cs1, c2l, c2h);
}


//-------------------------------------------------
//  read -
//-------------------------------------------------

uint8_t plus4_state::read(offs_t offset)
{
	int phi0 = 1, mux = 0, ras = 0, ba = 1;
	int scs, phi2, user, _6551, addr_clk, keyport, kernal;

	bankswitch(offset, phi0, mux, ras, &scs, &phi2, &user, &_6551, &addr_clk, &keyport, &kernal);

	return read_memory(offset, ba, scs, phi2, user, _6551, addr_clk, keyport, kernal);
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

void plus4_state::write(offs_t offset, uint8_t data)
{
	int scs, phi2, user, _6551, addr_clk, keyport, kernal;
	int phi0 = 1, mux = 0, ras = 0, ba = 1;
	int cs0 = 1, cs1 = 1, c1l = 1, c1h = 1, c2l = 1, c2h = 1;

	bankswitch(offset, phi0, mux, ras, &scs, &phi2, &user, &_6551, &addr_clk, &keyport, &kernal);

	m_ted->write(offset, data, cs0, cs1);

	//logerror("write offset %04x data %02x user %u 6551 %u addr_clk %u keyport %u kernal %u cs0 %u cs1 %u\n", offset,data,user,_6551,addr_clk,keyport,kernal,cs0,cs1);

	if (!scs && m_vslsi)
	{
		m_vslsi->write(offset & 0x03, data);
	}
	else if (!user && m_spi_user)
	{
		m_spi_user->write(data);
	}
	else if (!_6551 && m_acia)
	{
		m_acia->write(offset & 0x03, data);
	}
	else if (!addr_clk)
	{
		m_addr = offset & 0x0f;
	}
	else if (!keyport)
	{
		m_spi_kb->write(data);
	}
	else if (offset < 0xfd00 || offset >= 0xff20)
	{
		m_ram->pointer()[offset & m_ram->mask()] = data;
	}

	m_exp->cd_w(offset, data, ba, cs0, c1l, c1h, cs1, c2l, c2h);
}


//-------------------------------------------------
//  ted_videoram_r -
//-------------------------------------------------

uint8_t plus4_state::ted_videoram_r(offs_t offset)
{
	int phi0 = 1, mux = 0, ras = 1, ba = 0;
	int scs, phi2, user, _6551, addr_clk, keyport, kernal;

	bankswitch(offset, phi0, mux, ras, &scs, &phi2, &user, &_6551, &addr_clk, &keyport, &kernal);

	return read_memory(offset, ba, scs, phi2, user, _6551, addr_clk, keyport, kernal);
}



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( plus4_mem )
//-------------------------------------------------

void plus4_state::plus4_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(plus4_state::read), FUNC(plus4_state::write));
}


//-------------------------------------------------
//  ADDRESS_MAP( ted_videoram_map )
//-------------------------------------------------

void plus4_state::ted_videoram_map(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(plus4_state::ted_videoram_r));
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( plus4 )
//-------------------------------------------------

static INPUT_PORTS_START( plus4 )
	PORT_START( "ROW0" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("@") PORT_CODE(KEYCODE_OPENBRACE)              PORT_CHAR('@')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3)                                    PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2)                                    PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1)                                    PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("HELP F7") PORT_CODE(KEYCODE_F4)               PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE)                            PORT_CHAR(0xA3)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER)             PORT_CHAR(13)
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INST DEL") PORT_CODE(KEYCODE_BACKSPACE)       PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START( "ROW1" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Shift (Left & Right)") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)         PORT_CHAR('E')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)         PORT_CHAR('S')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)         PORT_CHAR('Z')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)         PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)         PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)         PORT_CHAR('W')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)         PORT_CHAR('3') PORT_CHAR('#')

	PORT_START( "ROW2" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)         PORT_CHAR('X')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)         PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)         PORT_CHAR('F')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)         PORT_CHAR('C')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6)         PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)         PORT_CHAR('D')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)         PORT_CHAR('R')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5)         PORT_CHAR('5') PORT_CHAR('%')

	PORT_START( "ROW3" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)         PORT_CHAR('V')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)         PORT_CHAR('U')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)         PORT_CHAR('H')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)         PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8)         PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)         PORT_CHAR('G')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)         PORT_CHAR('Y')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7)         PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START( "ROW4" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)         PORT_CHAR('N')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)         PORT_CHAR('O')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)         PORT_CHAR('K')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)         PORT_CHAR('M')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("0  \xE2\x86\x91") PORT_CODE(KEYCODE_0)        PORT_CHAR('0') PORT_CHAR('^')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)         PORT_CHAR('J')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)         PORT_CHAR('I')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9)         PORT_CHAR('9') PORT_CHAR(')')

	PORT_START( "ROW5" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA)     PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS)                                PORT_CHAR('-')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)     PORT_CHAR(':') PORT_CHAR('[')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)      PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_UP)                                    PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)         PORT_CHAR('L')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)         PORT_CHAR('P')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DOWN)                                  PORT_CHAR(UCHAR_MAMEKEY(DOWN))

	PORT_START( "ROW6" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH)                             PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)                             PORT_CHAR('+')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("=  Pi  \xE2\x86\x90") PORT_CODE(KEYCODE_BACKSLASH)   PORT_CHAR('=') PORT_CHAR(0x03C0) PORT_CHAR(0x2190)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ESC)                               PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RIGHT)                             PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE)                             PORT_CHAR(';') PORT_CHAR(']')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_INSERT)                            PORT_CHAR('*')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LEFT)                              PORT_CHAR(UCHAR_MAMEKEY(LEFT))

	PORT_START( "ROW7" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RUN STOP") PORT_CODE(KEYCODE_HOME)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)                                 PORT_CHAR('Q')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CBM") PORT_CODE(KEYCODE_LALT)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE)                             PORT_CHAR(' ')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2)                                 PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Control") PORT_CODE(KEYCODE_TAB)          PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Home Clear") PORT_CODE(KEYCODE_DEL)       PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1)                                 PORT_CHAR('1') PORT_CHAR('!')

	PORT_START( "LOCK" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( c16 )
//-------------------------------------------------

static INPUT_PORTS_START( c16 )
	PORT_INCLUDE( plus4 )

	PORT_MODIFY( "ROW0" )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_INSERT)                                PORT_CHAR(0xA3)

	PORT_MODIFY( "ROW5" )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)                  PORT_CHAR('-')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_UP)                                    PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PGUP)                                  PORT_CHAR(UCHAR_MAMEKEY(DOWN))

	PORT_MODIFY( "ROW6" )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE)                            PORT_CHAR('+')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("=  Pi  \xE2\x86\x90") PORT_CODE(KEYCODE_PGDN) PORT_CHAR('=') PORT_CHAR(0x03C0) PORT_CHAR(0x2190)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS)                                PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH)                             PORT_CHAR('*')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)                                 PORT_CHAR(UCHAR_MAMEKEY(LEFT))
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  M6510_INTERFACE( cpu_intf )
//-------------------------------------------------

uint8_t plus4_state::cpu_r()
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4       CST RD
	    5
	    6       IEC CLK IN
	    7       IEC DATA IN, CST SENSE

	*/

	uint8_t data = 0x2f;

	// cassette read
	data |= m_cassette->read() << 4;

	// serial clock
	data |= m_iec->clk_r() << 6;

	// serial data, cassette sense
	data |= (m_iec->data_r() && m_cassette->sense_r()) << 7;

	return data;
}

uint8_t c16_state::cpu_r()
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4       CST RD
	    5
	    6       IEC CLK IN
	    7       IEC DATA IN

	*/

	uint8_t data = 0;

	// cassette read
	data |= m_cassette->read() << 4;

	// serial clock
	data |= m_iec->clk_r() << 6;

	// serial data
	data |= m_iec->data_r() << 7;

	return data;
}

void plus4_state::cpu_w(uint8_t data)
{
	/*

	    bit     description

	    0       IEC DATA
	    1       IEC CLK, CST WR
	    2       IEC ATN
	    3       CST MTR
	    4
	    5
	    6       (CST WR)
	    7

	*/

	//logerror("%s cpu write %02x\n", machine().describe_context(), data);

	// serial data
	m_iec->host_data_w(!BIT(data, 0));

	// serial clock
	m_iec->host_clk_w(!BIT(data, 1));

	// serial attention
	m_iec->host_atn_w(!BIT(data, 2));

	// cassette motor
	m_cassette->motor_w(BIT(data, 3));

	// cassette write
	m_cassette->write(!BIT(data, 1));
}


//-------------------------------------------------
//  ted7360_interface ted_intf
//-------------------------------------------------

uint8_t plus4_state::ted_k_r(offs_t offset)
{
	/*

	    bit     description

	    0       JOY A0, JOY B0
	    1       JOY A1, JOY B1
	    2       JOY A2, JOY B2
	    3       JOY A3, JOY B3
	    4
	    5
	    6       BTN A
	    7       BTN B

	*/

	uint8_t data = 0xff;

	// joystick
	if (!BIT(offset, 2))
	{
		uint8_t joy_a = m_joy1->read_joy();

		data &= (0xf0 | (joy_a & 0x0f));
		data &= ~(!BIT(joy_a, 5) << 6);
	}

	if (!BIT(offset, 1))
	{
		uint8_t joy_b = m_joy2->read_joy();

		data &= (0xf0 | (joy_b & 0x0f));
		data &= ~(!BIT(joy_b, 5) << 7);
	}

	// keyboard
	if (!BIT(m_kb, 7)) data &= m_row[7]->read();
	if (!BIT(m_kb, 6)) data &= m_row[6]->read();
	if (!BIT(m_kb, 5)) data &= m_row[5]->read();
	if (!BIT(m_kb, 4)) data &= m_row[4]->read();
	if (!BIT(m_kb, 3)) data &= m_row[3]->read();
	if (!BIT(m_kb, 2)) data &= m_row[2]->read();
	if (!BIT(m_kb, 1)) data &= m_row[1]->read() & m_lock->read();
	if (!BIT(m_kb, 0)) data &= m_row[0]->read();

	return data;
}



//-------------------------------------------------
//  SLOT_INTERFACE( cbm_datassette_devices )
//-------------------------------------------------

void plus4_datassette_devices(device_slot_interface &device)
{
	device.option_add("c1531", C1531);
	device.option_add("diag264", DIAG264_CASSETTE_LOOPBACK);
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( plus4 )
//-------------------------------------------------

void plus4_state::machine_start()
{
	// initialize memory
	uint8_t data = 0xff;

	for (offs_t offset = 0; offset < m_ram->size(); offset++)
	{
		m_ram->pointer()[offset] = data;
		if (!(offset % 64)) data ^= 0xff;
	}

	// state saving
	save_item(NAME(m_addr));
	save_item(NAME(m_kb));

	if (m_acia)
	{
		m_acia->write_cts(0);
	}

	m_spi_kb->write_p0(1);
	m_spi_kb->write_p1(1);
	m_spi_kb->write_p2(1);
	m_spi_kb->write_p3(1);
	m_spi_kb->write_p4(1);
	m_spi_kb->write_p5(1);
	m_spi_kb->write_p6(1);
	m_spi_kb->write_p7(1);
}


void plus4_state::machine_reset()
{
	m_maincpu->reset();

	m_iec->reset();

	if (m_acia)
	{
		m_acia->reset();
	}

	m_exp->reset();

	if (m_user)
	{
		m_user->write_3(0);
		m_user->write_3(1);
	}

	m_addr = 0;
}



//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

//-------------------------------------------------
//  machine_config( plus4 )
//-------------------------------------------------

void plus4_state::plus4(machine_config &config)
{
	// basic machine hardware
	M7501(config, m_maincpu, 0);
	m_maincpu->set_addrmap(AS_PROGRAM, &plus4_state::plus4_mem);
	m_maincpu->read_callback().set(FUNC(plus4_state::cpu_r));
	m_maincpu->write_callback().set(FUNC(plus4_state::cpu_w));
	m_maincpu->set_pulls(0x00, 0xc0);
	config.set_perfect_quantum(m_maincpu);

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, m7501_device::IRQ_LINE);

	// video and sound hardware
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(mos7360_device::PAL_VRETRACERATE);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.set_size(336, 216);
	screen.set_visarea(0, 336 - 1, 0, 216 - 1);
	screen.set_screen_update(MOS7360_TAG, FUNC(mos7360_device::screen_update));

	SPEAKER(config, "mono").front_center();

	MOS7360(config, m_ted, 0);
	m_ted->set_addrmap(0, &plus4_state::ted_videoram_map);
	m_ted->set_screen(SCREEN_TAG);
	m_ted->write_irq_callback().set("mainirq", FUNC(input_merger_device::in_w<0>));
	m_ted->read_k_callback().set(FUNC(plus4_state::ted_k_r));
	m_ted->add_route(ALL_OUTPUTS, "mono", 0.25);

	// devices
	PLS100(config, m_pla);

	PET_USER_PORT(config, m_user, plus4_user_port_cards, nullptr);
	m_user->p4_handler().set(m_spi_user, FUNC(mos6529_device::write_p2)); // cassette sense
	m_user->p5_handler().set(m_spi_user, FUNC(mos6529_device::write_p3));
	m_user->p6_handler().set(m_spi_user, FUNC(mos6529_device::write_p4));
	m_user->p7_handler().set(m_spi_user, FUNC(mos6529_device::write_p5));
	m_user->p8_handler().set(m_acia, FUNC(mos6551_device::write_rxc));
	m_user->pb_handler().set(m_spi_user, FUNC(mos6529_device::write_p0));
	m_user->pc_handler().set(m_acia, FUNC(mos6551_device::write_rxd));
	m_user->pf_handler().set(m_spi_user, FUNC(mos6529_device::write_p7));
	m_user->ph_handler().set(m_acia, FUNC(mos6551_device::write_dcd)).invert(); // TODO: add missing pull up before inverter
	m_user->pj_handler().set(m_spi_user, FUNC(mos6529_device::write_p6));
	m_user->pk_handler().set(m_spi_user, FUNC(mos6529_device::write_p1));
	m_user->pl_handler().set(m_acia, FUNC(mos6551_device::write_dsr)).invert(); // TODO: add missing pull up before inverter

	MOS6551(config, m_acia, 0);
	m_acia->set_xtal(1.8432_MHz_XTAL);
	m_acia->rxc_handler().set(m_user, FUNC(pet_user_port_device::write_8));
	m_acia->rts_handler().set(m_user, FUNC(pet_user_port_device::write_d)).invert();
	m_acia->dtr_handler().set(m_user, FUNC(pet_user_port_device::write_e)).invert();
	m_acia->txd_handler().set(m_user, FUNC(pet_user_port_device::write_m));
	m_acia->irq_handler().set("mainirq", FUNC(input_merger_device::in_w<1>));

	MOS6529(config, m_spi_user, 0);
	m_spi_user->p_handler<0>().set(m_user, FUNC(pet_user_port_device::write_b));
	m_spi_user->p_handler<1>().set(m_user, FUNC(pet_user_port_device::write_k));
	m_spi_user->p_handler<2>().set(m_user, FUNC(pet_user_port_device::write_4));
	m_spi_user->p_handler<3>().set(m_user, FUNC(pet_user_port_device::write_5));
	m_spi_user->p_handler<4>().set(m_user, FUNC(pet_user_port_device::write_6));
	m_spi_user->p_handler<5>().set(m_user, FUNC(pet_user_port_device::write_7));
	m_spi_user->p_handler<6>().set(m_user, FUNC(pet_user_port_device::write_j));
	m_spi_user->p_handler<7>().set(m_user, FUNC(pet_user_port_device::write_f));

	MOS6529(config, m_spi_kb, 0);
	m_spi_kb->p_handler<0>().set(FUNC(plus4_state::write_kb0));
	m_spi_kb->p_handler<1>().set(FUNC(plus4_state::write_kb1));
	m_spi_kb->p_handler<2>().set(FUNC(plus4_state::write_kb2));
	m_spi_kb->p_handler<3>().set(FUNC(plus4_state::write_kb3));
	m_spi_kb->p_handler<4>().set(FUNC(plus4_state::write_kb4));
	m_spi_kb->p_handler<5>().set(FUNC(plus4_state::write_kb5));
	m_spi_kb->p_handler<6>().set(FUNC(plus4_state::write_kb6));
	m_spi_kb->p_handler<7>().set(FUNC(plus4_state::write_kb7));

	PET_DATASSETTE_PORT(config, m_cassette, plus4_datassette_devices, "c1531");
	m_cassette->read_handler().set_nop();

	cbm_iec_slot_device::add(config, m_iec, "c1541");
	m_iec->atn_callback().set(m_user, FUNC(pet_user_port_device::write_9));

	VCS_CONTROL_PORT(config, m_joy1, vcs_control_port_devices, nullptr);
	VCS_CONTROL_PORT(config, m_joy2, vcs_control_port_devices, "joy");

	PLUS4_EXPANSION_SLOT(config, m_exp, XTAL(14'318'181)/16, plus4_expansion_cards, nullptr);
	m_exp->irq_wr_callback().set("mainirq", FUNC(input_merger_device::in_w<2>));
	m_exp->cd_rd_callback().set(FUNC(plus4_state::read));
	m_exp->cd_wr_callback().set(FUNC(plus4_state::write));
	m_exp->aec_wr_callback().set_inputline(m_maincpu, INPUT_LINE_HALT);

	QUICKLOAD(config, "quickload", "p00,prg", CBM_QUICKLOAD_DELAY).set_load_callback(FUNC(plus4_state::quickload_c16));

	// internal ram
	RAM(config, m_ram).set_default_size("64K");
}


//-------------------------------------------------
//  machine_config( plus4p )
//-------------------------------------------------

void c16_state::plus4p(machine_config &config)
{
	plus4(config);
	m_maincpu->set_clock(XTAL(17'734'470)/20);
	m_ted->set_clock(XTAL(17'734'470)/5);

	// software list
	SOFTWARE_LIST(config, "cart_list").set_original("plus4_cart");
	SOFTWARE_LIST(config, "cass_list").set_original("plus4_cass");
	SOFTWARE_LIST(config, "flop_list").set_original("plus4_flop");
	subdevice<software_list_device>("cart_list")->set_filter("PAL");
	subdevice<software_list_device>("cass_list")->set_filter("PAL");
	subdevice<software_list_device>("flop_list")->set_filter("PAL");
}

//-------------------------------------------------
//  machine_config( plus4n )
//-------------------------------------------------

void c16_state::plus4n(machine_config &config)
{
	plus4(config);
	m_maincpu->set_clock(XTAL(14'318'181)/16);
	m_ted->set_clock(XTAL(14'318'181)/4);

	// software list
	SOFTWARE_LIST(config, "cart_list").set_original("plus4_cart");
	SOFTWARE_LIST(config, "cass_list").set_original("plus4_cass");
	SOFTWARE_LIST(config, "flop_list").set_original("plus4_flop");
	subdevice<software_list_device>("cart_list")->set_filter("NTSC");
	subdevice<software_list_device>("cass_list")->set_filter("NTSC");
	subdevice<software_list_device>("flop_list")->set_filter("NTSC");
}


//-------------------------------------------------
//  machine_config( c16n )
//-------------------------------------------------

void c16_state::c16n(machine_config &config)
{
	plus4n(config);
	m_maincpu->read_callback().set(FUNC(c16_state::cpu_r));
	m_maincpu->write_callback().set(FUNC(plus4_state::cpu_w));
	m_maincpu->set_pulls(0x00, 0xc0);

	config.device_remove(MOS6551_TAG);
	config.device_remove(MOS6529_USER_TAG);
	config.device_remove(PET_USER_PORT_TAG);

	m_iec->atn_callback().set_nop();

	m_ram->set_default_size("16K").set_extra_options("64K");
}


//-------------------------------------------------
//  machine_config( c16p )
//-------------------------------------------------

void c16_state::c16p(machine_config &config)
{
	plus4p(config);
	m_maincpu->read_callback().set(FUNC(c16_state::cpu_r));
	m_maincpu->write_callback().set(FUNC(plus4_state::cpu_w));
	m_maincpu->set_pulls(0x00, 0xc0);

	config.device_remove(MOS6551_TAG);
	config.device_remove(MOS6529_USER_TAG);
	config.device_remove(PET_USER_PORT_TAG);

	m_iec->atn_callback().set_nop();

	m_ram->set_default_size("16K").set_extra_options("64K");
}


void c16_state::c232(machine_config &config)
{
	c16p(config);
	m_ram->set_default_size("32K");
}


//-------------------------------------------------
//  machine_config( v364 )
//-------------------------------------------------

void c16_state::v364(machine_config &config)
{
	plus4n(config);
	T6721A(config, T6721A_TAG, XTAL(640'000)).add_route(ALL_OUTPUTS, "mono", 0.25);

	MOS8706(config, m_vslsi, XTAL(14'318'181)/16);
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( c264 )
//-------------------------------------------------

ROM_START( c264 )
	ROM_REGION( 0x8000, "kernal", 0 )
	ROM_LOAD( "basic-264.bin", 0x0000, 0x4000, CRC(6a2fc8e3) SHA1(473fce23afa07000cdca899fbcffd6961b36a8a0) )
	ROM_LOAD( "kernal-264.bin", 0x4000, 0x4000, CRC(8f32abe7) SHA1(d481faf5fcbb331878dc7851c642d04f26a32873) )

	ROM_REGION( 0x8000, "function", ROMREGION_ERASE00 )
	// TODO: add cart slots to mount EPROMs here

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "251641-02", 0x00, 0xf5, CRC(83be2076) SHA1(a89b18b2261233443c933c8b4663b108e7630924) )
ROM_END


//-------------------------------------------------
//  ROM( c232 )
//-------------------------------------------------

ROM_START( c232 )
	ROM_REGION( 0x8000, "kernal", 0 )
	ROM_LOAD( "318006-01.u4", 0x0000, 0x4000, CRC(74eaae87) SHA1(161c96b4ad20f3a4f2321808e37a5ded26a135dd) )
	ROM_LOAD( "318004-01.u5", 0x4000, 0x4000, CRC(dbdc3319) SHA1(3c77caf72914c1c0a0875b3a7f6935cd30c54201) )

	ROM_REGION( 0x8000, "function", ROMREGION_ERASE00 )
	// TODO: add cart slots to mount EPROMs here

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "251641-02.u7", 0x00, 0xf5, CRC(83be2076) SHA1(a89b18b2261233443c933c8b4663b108e7630924) )
ROM_END


//-------------------------------------------------
//  ROM( v364 )
//-------------------------------------------------

ROM_START( v364 )
	ROM_REGION( 0x8000, "kernal", 0 )
	ROM_LOAD( "318006-01", 0x0000, 0x4000, CRC(74eaae87) SHA1(161c96b4ad20f3a4f2321808e37a5ded26a135dd) )
	ROM_LOAD( "kern364p", 0x4000, 0x4000, CRC(84fd4f7a) SHA1(b9a5b5dacd57ca117ef0b3af29e91998bf4d7e5f) )

	ROM_REGION( 0x8000, "function", 0 )
	ROM_LOAD( "317053-01", 0x0000, 0x4000, CRC(4fd1d8cb) SHA1(3b69f6e7cb4c18bb08e203fb18b7dabfa853390f) )
	ROM_LOAD( "317054-01", 0x4000, 0x4000, CRC(109de2fc) SHA1(0ad7ac2db7da692d972e586ca0dfd747d82c7693) )

	ROM_REGION( 0x8000, "c2", 0 )
	ROM_LOAD( "spk3cc4.bin", 0x0000, 0x4000, CRC(5227c2ee) SHA1(59af401cbb2194f689898271c6e8aafa28a7af11) )

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "251641-02", 0x00, 0xf5, CRC(83be2076) SHA1(a89b18b2261233443c933c8b4663b108e7630924) )
ROM_END


//-------------------------------------------------
//  ROM( plus4 )
//-------------------------------------------------

ROM_START( plus4 )
	ROM_REGION( 0x8000, "kernal", 0 )
	ROM_DEFAULT_BIOS("r5")
	ROM_SYSTEM_BIOS( 0, "r4", "Revision 4" )
	ROMX_LOAD( "318005-04.u24", 0x4000, 0x4000, CRC(799a633d) SHA1(5df52c693387c0e2b5d682613a3b5a65477311cf), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "r5", "Revision 5" )
	ROMX_LOAD( "318005-05.u24", 0x4000, 0x4000, CRC(70295038) SHA1(a3d9e5be091b98de39a046ab167fb7632d053682), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "jiffydos", "JiffyDOS v6.01" )
	ROMX_LOAD( "jiffydos plus4.u24", 0x0000, 0x8000, CRC(818d3f45) SHA1(9bc1b1c3da9ca642deae717905f990d8e36e6c3b), ROM_BIOS(2) ) // first half contains R5 kernal

	ROM_LOAD( "318006-01.u23", 0x0000, 0x4000, CRC(74eaae87) SHA1(161c96b4ad20f3a4f2321808e37a5ded26a135dd) )

	ROM_REGION( 0x8000, "function", 0 )
	ROM_LOAD( "317053-01.u25", 0x0000, 0x4000, CRC(4fd1d8cb) SHA1(3b69f6e7cb4c18bb08e203fb18b7dabfa853390f) )
	ROM_LOAD( "317054-01.u26", 0x4000, 0x4000, CRC(109de2fc) SHA1(0ad7ac2db7da692d972e586ca0dfd747d82c7693) )

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "251641-02.u19", 0x00, 0xf5, CRC(83be2076) SHA1(a89b18b2261233443c933c8b4663b108e7630924) )
ROM_END


//-------------------------------------------------
//  ROM( plus4p )
//-------------------------------------------------

ROM_START( plus4p )
	ROM_REGION( 0x8000, "kernal", 0 )
	ROM_LOAD( "318006-01.u23", 0x0000, 0x4000, CRC(74eaae87) SHA1(161c96b4ad20f3a4f2321808e37a5ded26a135dd) )

	ROM_DEFAULT_BIOS("r5")
	ROM_SYSTEM_BIOS( 0, "r3", "Revision 3" )
	ROMX_LOAD( "318004-03.u24", 0x4000, 0x4000, CRC(77bab934) SHA1(97814dab9d757fe5a3a61d357a9a81da588a9783), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "r4", "Revision 4" )
	ROMX_LOAD( "318004-04.u24", 0x4000, 0x4000, CRC(be54ed79) SHA1(514ad3c29d01a2c0a3b143d9c1d4143b1912b793), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "r5", "Revision 5" )
	ROMX_LOAD( "318004-05.u24", 0x4000, 0x4000, CRC(71c07bd4) SHA1(7c7e07f016391174a557e790c4ef1cbe33512cdb), ROM_BIOS(2) )

	ROM_REGION( 0x8000, "function", 0 )
	ROM_LOAD( "317053-01.u25", 0x0000, 0x4000, CRC(4fd1d8cb) SHA1(3b69f6e7cb4c18bb08e203fb18b7dabfa853390f) )
	ROM_LOAD( "317054-01.u26", 0x4000, 0x4000, CRC(109de2fc) SHA1(0ad7ac2db7da692d972e586ca0dfd747d82c7693) )

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "251641-02.u19", 0x00, 0xf5, CRC(83be2076) SHA1(a89b18b2261233443c933c8b4663b108e7630924) )
ROM_END


//-------------------------------------------------
//  ROM( c16 )
//-------------------------------------------------

ROM_START( c16 )
	ROM_REGION( 0x8000, "kernal", 0 )
	ROM_DEFAULT_BIOS("r5")
	ROM_SYSTEM_BIOS( 0, "r4", "Revision 4" )
	ROMX_LOAD( "318005-04.u24", 0x4000, 0x4000, CRC(799a633d) SHA1(5df52c693387c0e2b5d682613a3b5a65477311cf), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "r5", "Revision 5" )
	ROMX_LOAD( "318005-05.u24", 0x4000, 0x4000, CRC(70295038) SHA1(a3d9e5be091b98de39a046ab167fb7632d053682), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "jiffydos", "JiffyDOS v6.01" )
	ROMX_LOAD( "jiffydos plus4.u24", 0x0000, 0x8000, CRC(818d3f45) SHA1(9bc1b1c3da9ca642deae717905f990d8e36e6c3b), ROM_BIOS(2) ) // first half contains R5 kernal

	ROM_LOAD( "318006-01.u23", 0x0000, 0x4000, CRC(74eaae87) SHA1(161c96b4ad20f3a4f2321808e37a5ded26a135dd) )

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "251641-02.u19", 0x00, 0xf5, CRC(83be2076) SHA1(a89b18b2261233443c933c8b4663b108e7630924) )
ROM_END


//-------------------------------------------------
//  ROM( c16p )
//-------------------------------------------------

ROM_START( c16p )
	ROM_REGION( 0x8000, "kernal", 0 )
	ROM_LOAD( "318006-01.u3", 0x0000, 0x4000, CRC(74eaae87) SHA1(161c96b4ad20f3a4f2321808e37a5ded26a135dd) )

	ROM_DEFAULT_BIOS("r5")
	ROM_SYSTEM_BIOS( 0, "r3", "Revision 3" )
	ROMX_LOAD( "318004-03.u4", 0x4000, 0x4000, CRC(77bab934) SHA1(97814dab9d757fe5a3a61d357a9a81da588a9783), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "r4", "Revision 4" )
	ROMX_LOAD( "318004-04.u4", 0x4000, 0x4000, CRC(be54ed79) SHA1(514ad3c29d01a2c0a3b143d9c1d4143b1912b793), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "r5", "Revision 5" )
	ROMX_LOAD( "318004-05.u4", 0x4000, 0x4000, CRC(71c07bd4) SHA1(7c7e07f016391174a557e790c4ef1cbe33512cdb), ROM_BIOS(2) )

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "251641-02.u16", 0x00, 0xf5, CRC(83be2076) SHA1(a89b18b2261233443c933c8b4663b108e7630924) )
ROM_END


//-------------------------------------------------
//  ROM( c16_hu )
//-------------------------------------------------

ROM_START( c16_hu )
	ROM_REGION( 0x8000, "kernal", 0 )
	ROM_LOAD( "318006-01.u3", 0x0000, 0x4000, CRC(74eaae87) SHA1(161c96b4ad20f3a4f2321808e37a5ded26a135dd) )

	ROM_DEFAULT_BIOS("r2")
	ROM_SYSTEM_BIOS( 0, "r1", "Revision 1" )
	ROMX_LOAD( "318030-01.u4", 0x4000, 0x4000, NO_DUMP, ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "r2", "Revision 2" )
	ROMX_LOAD( "318030-02.u4", 0x4000, 0x4000, CRC(775f60c5) SHA1(20cf3c4bf6c54ef09799af41887218933f2e27ee), ROM_BIOS(1) )

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "251641-02.u16", 0x00, 0xf5, CRC(83be2076) SHA1(a89b18b2261233443c933c8b4663b108e7630924) )
ROM_END


//-------------------------------------------------
//  ROM( c116 )
//-------------------------------------------------

ROM_START( c116 )
	ROM_REGION( 0x8000, "kernal", 0 )
	ROM_LOAD( "318006-01.u3", 0x0000, 0x4000, CRC(74eaae87) SHA1(161c96b4ad20f3a4f2321808e37a5ded26a135dd) )

	ROM_DEFAULT_BIOS("r5")
	ROM_SYSTEM_BIOS( 0, "r3", "Revision 3" )
	ROMX_LOAD( "318004-03.u4", 0x4000, 0x4000, CRC(77bab934) SHA1(97814dab9d757fe5a3a61d357a9a81da588a9783), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "r4", "Revision 4" )
	ROMX_LOAD( "318004-04.u4", 0x4000, 0x4000, CRC(be54ed79) SHA1(514ad3c29d01a2c0a3b143d9c1d4143b1912b793), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "r5", "Revision 5" )
	ROMX_LOAD( "318004-05.u4", 0x4000, 0x4000, CRC(71c07bd4) SHA1(7c7e07f016391174a557e790c4ef1cbe33512cdb), ROM_BIOS(2) )

	ROM_REGION( 0xf5, PLA_TAG, 0 )
	ROM_LOAD( "251641-02.u101", 0x00, 0xf5, CRC(83be2076) SHA1(a89b18b2261233443c933c8b4663b108e7630924) )
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY                        FULLNAME                      FLAGS
COMP( 1984, c264,   0,      0,      plus4n,  plus4, c16_state, empty_init, "Commodore Business Machines", "Commodore 264 (Prototype)",  MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
COMP( 1984, c232,   c264,   0,      c232,    plus4, c16_state, empty_init, "Commodore Business Machines", "Commodore 232 (Prototype)",  MACHINE_SUPPORTS_SAVE )
COMP( 1984, v364,   c264,   0,      v364,    plus4, c16_state, empty_init, "Commodore Business Machines", "Commodore V364 (Prototype)", MACHINE_SUPPORTS_SAVE )
COMP( 1984, plus4,  c264,   0,      plus4n,  plus4, c16_state, empty_init, "Commodore Business Machines", "Plus/4 (NTSC)",              MACHINE_SUPPORTS_SAVE )
COMP( 1984, plus4p, c264,   0,      plus4p,  plus4, c16_state, empty_init, "Commodore Business Machines", "Plus/4 (PAL)",               MACHINE_SUPPORTS_SAVE )
COMP( 1984, c16,    c264,   0,      c16n,    c16,   c16_state, empty_init, "Commodore Business Machines", "Commodore 16 (NTSC)",        MACHINE_SUPPORTS_SAVE )
COMP( 1984, c16p,   c264,   0,      c16p,    c16,   c16_state, empty_init, "Commodore Business Machines", "Commodore 16 (PAL)",         MACHINE_SUPPORTS_SAVE )
COMP( 1984, c16_hu, c264,   0,      c16p,    c16,   c16_state, empty_init, "Commodore Business Machines", "Commodore 16 (Hungary)",     MACHINE_SUPPORTS_SAVE )
COMP( 1984, c116,   c264,   0,      c16p,    c16,   c16_state, empty_init, "Commodore Business Machines", "Commodore 116",              MACHINE_SUPPORTS_SAVE )



pm68k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Callan PM68K Unix mainframe.

2013-09-04 Skeleton driver

Status: Boots into monitor, some commands work, some freeze.

****************************************************************************/

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "bus/rs232/rs232.h"
#include "machine/am9513.h"
#include "machine/z80sio.h"


namespace {

class pm68k_state : public driver_device
{
public:
	pm68k_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_p_base(*this, "rambase")
		, m_maincpu(*this, "maincpu")
	{ }

	void pm68k(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	required_shared_ptr<uint16_t> m_p_base;
	required_device<cpu_device> m_maincpu;
};


void pm68k_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x1fffff).ram().share("rambase");
	map(0x200000, 0x205fff).rom().region("roms", 0);
	map(0x600000, 0x600007).rw("mpsc", FUNC(i8274_device::ba_cd_r), FUNC(i8274_device::ba_cd_w)).umask16(0xff00);
	map(0x800000, 0x800003).rw("stc", FUNC(am9513_device::read16), FUNC(am9513_device::write16));
}


/* Input ports */
static INPUT_PORTS_START( pm68k )
INPUT_PORTS_END


void pm68k_state::machine_reset()
{
	uint8_t* ROM = memregion("roms")->base();
	memcpy(m_p_base, ROM, 8);
}

void pm68k_state::pm68k(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, 8000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &pm68k_state::mem_map);

	i8274_device& mpsc(I8274(config, "mpsc", 0));
	mpsc.out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
	mpsc.out_dtra_callback().set("rs232a", FUNC(rs232_port_device::write_dtr));
	mpsc.out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts));
	mpsc.out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
	mpsc.out_dtrb_callback().set("rs232b", FUNC(rs232_port_device::write_dtr));
	mpsc.out_rtsb_callback().set("rs232b", FUNC(rs232_port_device::write_rts));

	am9513_device &stc(AM9513(config, "stc", 4000000));
	stc.out4_cb().set("mpsc", FUNC(i8274_device::rxca_w));
	stc.out4_cb().append("mpsc", FUNC(i8274_device::txca_w));
	stc.out5_cb().set("mpsc", FUNC(i8274_device::rxcb_w));
	stc.out5_cb().append("mpsc", FUNC(i8274_device::txcb_w));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, "terminal"));
	rs232a.rxd_handler().set("mpsc", FUNC(i8274_device::rxa_w));
	rs232a.dsr_handler().set("mpsc", FUNC(i8274_device::dcda_w));
	rs232a.cts_handler().set("mpsc", FUNC(i8274_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set("mpsc", FUNC(i8274_device::rxb_w));
	rs232b.dsr_handler().set("mpsc", FUNC(i8274_device::dcdb_w));
	rs232b.cts_handler().set("mpsc", FUNC(i8274_device::ctsb_w));
}

/* ROM definition */
ROM_START( pm68k )
	ROM_REGION16_BE(0x6000, "roms", 0)
	ROM_LOAD16_BYTE("u103", 0x00000, 0x1000, CRC(86d32d6c) SHA1(ce9c54b62c64c37ae9106fb06b8a2b2152d1ddf6) )
	ROM_LOAD16_BYTE("u101", 0x00001, 0x1000, CRC(66607e54) SHA1(06f380fdeba13dc3aee826dd166f4bd3031febb9) )
	ROM_LOAD16_BYTE("u104", 0x02000, 0x2000, CRC(ccd2ba4d) SHA1(5cdcf875e136aa9af5f150e0102cd209c496885e) )
	ROM_LOAD16_BYTE("u102", 0x02001, 0x2000, CRC(48182abd) SHA1(a6e4fb62c5f04cb397c6c3294723ec1f7bc3b680) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE INPUT  CLASS        INIT        COMPANY                FULLNAME  FLAGS
COMP( 198?, pm68k, 0,      0,      pm68k,  pm68k, pm68k_state, empty_init, "Callan Data Systems", "PM68K",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



pmd85.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Krzysztof Strzecha
/*******************************************************************************

PMD-85 driver by Krzysztof Strzecha

What's new:
-----------

27.06.2004  Mato crash fixed.
21.05.2004  V.24 / Tape switch added. V.24 is not emulated.
25.04.2004  PMD-85.1 tape emulation with support for .pmd format files added.
19.04.2004  Verified PMD-85.1 and PMD-85.2 monitor roms and replaced with
        unmodified ones.
        Memory system cleanups.
03.04.2004  PMD-85.3 and Mato (PMD-85.2 clone) drivers.
        Preliminary and not working tape support.
        Reset key fixed. PMD-85.1 fixed.
15.03.2004  Added drivers for: PMD-85.2, PMD-85.2A, PMD-85.2B and Didaktik
        Alfa (PMD-85.1 clone). Keyboard finished. Natural keyboard added.
        Memory system rewritten. I/O system rewritten. Support for Basic
        ROM module added. Video emulation rewritten.
30.11.2002  Memory mapping improved.
06.07.2002  Preliminary driver.

Notes on emulation status and to do list:
-----------------------------------------

1.  V.24.
2.  Flash video attribute.
3.  External interfaces connectors (K2-K5).
4.  C2717PMD is marked MNW, but there's nothing to say why.
5.  Verify PMD-85.2A, PMD-85.3, Didaktik Alfa and Mato monitor roms.
6.  Verify all Basic roms.
7.  8251 in Didaktik Alfa.
8.  Colors (if any). Seems most versions supported 4 colours via RGB connector. But, the TV modulator only carried the G line.
    The video signals are developed in a forest of gates which may take a while to work out.
9.  PMD-85, Didaktik Alfa 2 and Didaktik Beta (ROMs and documentation needed).
10. FDD interface (ROMs and disk images needed).
11. "Duch & Pampuch" Mato game displays scores with incorrect characters.


PMD-85 technical information
============================

Memory map:
-----------

    PMD-85.1, PMD-85.2
    ------------------

    start-up map (cleared by the first I/O write operation done by the CPU):
    0000-0fff ROM mirror #1
    1000-1fff not mapped
    2000-2fff ROM mirror #2
    3000-3fff not mapped
    4000-7fff Video RAM mirror #1
    8000-8fff ROM
    9000-9fff not mapped
    a000-afff ROM mirror #3
    b000-bfff not mapped
    c000-ffff Video RAM

    normal map:
    0000-7fff RAM
    8000-8fff ROM
    9000-9fff not mapped
    a000-afff ROM mirror #1
    b000-bfff not mapped
    c000-ffff Video RAM

    Didaktik Alfa (PMD-85.1 clone)
    ------------------------------

    start-up map (cleared by the first I/O write operation done by the CPU):
    0000-0fff ROM mirror
    1000-33ff BASIC mirror
    3400-3fff not mapped
    4000-7fff Video RAM mirror
    8000-8fff ROM
    9000-b3ff BASIC
    b400-bfff not mapped
    c000-ffff Video RAM

    normal map:
    0000-7fff RAM
    8000-8fff ROM
    9000-b3ff BASIC
    b400-bfff not mapped
    c000-ffff Video RAM

    PMD-85.2A, PMD-85.2B
    --------------------

    start-up map (cleared by the first I/O write operation done by the CPU):
    0000-0fff ROM mirror #1
    1000-1fff RAM #2 mirror
    2000-2fff ROM mirror #2
    3000-3fff RAM #3 mirror
    4000-7fff Video RAM mirror #1
    8000-8fff ROM
    9000-9fff RAM #2
    a000-afff ROM mirror #3
    b000-bfff RAM #3
    c000-ffff Video RAM

    normal map:
    0000-7fff RAM #1
    8000-8fff ROM
    9000-9fff RAM #2
    a000-afff ROM mirror #1
    b000-bfff RAM #3
    c000-ffff Video RAM

    PMD-85.3
    --------

    start-up map (cleared by the first I/O write operation done by the CPU):
    0000-1fff ROM mirror #1 read, RAM write
    2000-3fff ROM mirror #2 read, RAM write
    4000-5fff ROM mirror #3 read, RAM write
    6000-7fff ROM mirror #4 read, RAM write
    8000-9fff ROM mirror #5 read, RAM write
    a000-bfff ROM mirror #6 read, RAM write
    c000-dfff ROM mirror #7 read, Video RAM #1 write
    e000-ffff ROM, Video RAM #2 write

    normal map:
    0000-bfff RAM
    c000-dfff Video RAM #1
    e000-ffff Video RAM #2 / ROM read, Video RAM #2 write

    Mato
    ----

    start-up map (cleared by the first I/O write operation done by the CPU):
    0000-3fff ROM mirror #1
    4000-7fff Video RAM mirror #1
    8000-bfff ROM
    c000-ffff Video RAM

    normal map:
    0000-7fff RAM
    8000-bfff ROM
    c000-ffff Video RAM

I/O ports
---------

    I/O board
    ---------
    1xxx11aa    external interfaces connector (K2)

    0xxx11aa    I/O board interfaces
        000111aa    8251 (casette recorder, V24)
        010011aa    8255 (GPIO/0, GPIO/1)
        010111aa    8253
        011111aa    8255 (IMS-2)
    I/O board is not supported by Mato.

    Motherboard
    -----------
    1xxx01aa    8255 (keyboard, speaker, LEDs)
            PMD-85.3 memory banking
            Mato cassette recorder

    ROM Module
    ----------
    1xxx10aa    8255 (ROM reading)
    ROM module is not supported by Didaktik Alfa and Mato.


Commands
--------
DUMP aaaa - dump memory from aaaa
JUMP aaaa - start program at aaaa
MEM aaaa  - show 16 bytes from aaaa-up
MGLD xx   - load file number xx from cassette
MGSV xx aaaa bbbb yyyyyyyy - save memory range aaaa to bbbb to cassette with file number xx and filename yyyyyyyy
MGEND - ?
SUB aaaa bb cc dd... - write bytes to memory starting at aaaa with bb,cc,dd...


Cassette
--------

Cassettes tested with Basic
- pmd852,pmd852a,pmd852b,pmd853,c2717,c2717pmd - these can save and load back their own files
- pmd851,mato,alfa - not tested yet.

Getting into Basic:
- pmd852,pmd852a,pmd852b,pmd853 : In the Machine Configuration, enable Basic and Reset.
- pmd851,alfa : type BASIC 00 (or any parameter)
- mato : type BASIC
- c2717,c2717pmd : you're already in Basic.
- How to get out? Unknown.

Loading a tape:
- In the monitor : type MGLD <fn>, where <fn> is the file number, usually 00. File numbers will be shown in the
  software list usage.
- In Basic : type LOAD <fn>, where <fn> is the same as above.
- The tape can only be loaded into the environment it's meant for. Otherwise, you'll only get header information
  followed by a freeze.

Header information from what I can understand: xx/z yyyyyyyy
yyyyyyyy = filename
xx = file number
z = status code (guesses below)
    > - only loadable by Basic
    ? - only loadable by the monitor - it gives no clue as to the exec address, but should start by itself if it's
        been saved correctly.
    x - other codes are added by the user and have program-specific meanings.

Software list items: Some work, some don't, and some only work on certain machines. A test with "bludiste"
(a monitor program) produced the following:
- pmd851,pmd852,pmd852a,pmd852b,alfa - loads and runs
- c2717,c2717pmd - recognises and prints the header, but then doesn't load the tape, because the machine is
  stuck in Basic.
- mato,pmd853 - does nothing

Some software items will crash MAME, for example "bdash".

*******************************************************************************************************************/

#include "emu.h"
#include "pmd85.h"
#include "cpu/i8085/i8085.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "formats/pmd_cas.h"


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

uint32_t pmd85_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (int y = 0; y < 256; y++)
	{
		// address of current line in PMD-85 video memory
		uint8_t *line = m_ram->pointer() + 0xc000 + 0x40 * y;

		for (int x = 0; x < 288/6; x++)
		{
			int pen = BIT(line[x], 7) ? 1 : 2;

			bitmap.pix(y, x * 6 + 0) = BIT(line[x], 0) ? pen : 0;
			bitmap.pix(y, x * 6 + 1) = BIT(line[x], 1) ? pen : 0;
			bitmap.pix(y, x * 6 + 2) = BIT(line[x], 2) ? pen : 0;
			bitmap.pix(y, x * 6 + 3) = BIT(line[x], 3) ? pen : 0;
			bitmap.pix(y, x * 6 + 4) = BIT(line[x], 4) ? pen : 0;
			bitmap.pix(y, x * 6 + 5) = BIT(line[x], 5) ? pen : 0;
		}

	}

	return 0;
}


/* I/O ports */

void pmd85_state::pmd85_io(address_map &map)
{
	map(0x00, 0xff).rw(FUNC(pmd85_state::io_r), FUNC(pmd85_state::io_w));
}

void pmd85_state::mato_io(address_map &map)
{
	map(0x00, 0xff).rw(FUNC(pmd85_state::mato_io_r), FUNC(pmd85_state::mato_io_w));
}

/* memory w/r functions */

void pmd85_state::pmd85_mem(address_map &map)
{
	map(0x0000, 0x0fff).bankrw("bank1");
	map(0x1000, 0x1fff).bankrw("bank2");
	map(0x2000, 0x2fff).bankrw("bank3");
	map(0x3000, 0x3fff).bankrw("bank4");
	map(0x4000, 0x7fff).bankrw("bank5");
	map(0x8000, 0x8fff).bankr("bank6");
	map(0xa000, 0xafff).bankr("bank7");
	map(0xc000, 0xffff).bankrw("bank8");
}

void pmd85_state::pmd852a_mem(address_map &map)
{
	map(0x0000, 0x0fff).bankrw("bank1");
	map(0x1000, 0x1fff).bankrw("bank2");
	map(0x2000, 0x2fff).bankrw("bank3");
	map(0x3000, 0x3fff).bankrw("bank4");
	map(0x4000, 0x7fff).bankrw("bank5");
	map(0x8000, 0x8fff).bankr("bank6");
	map(0x9000, 0x9fff).bankrw("bank7");
	map(0xa000, 0xafff).bankr("bank8");
	map(0xb000, 0xbfff).bankrw("bank9");
	map(0xc000, 0xffff).bankrw("bank10");
}

void pmd85_state::pmd853_mem(address_map &map)
{
	map(0x0000, 0x1fff).bankr("bank1").bankw("bank9");
	map(0x2000, 0x3fff).bankr("bank2").bankw("bank10");
	map(0x4000, 0x5fff).bankr("bank3").bankw("bank11");
	map(0x6000, 0x7fff).bankr("bank4").bankw("bank12");
	map(0x8000, 0x9fff).bankr("bank5").bankw("bank13");
	map(0xa000, 0xbfff).bankr("bank6").bankw("bank14");
	map(0xc000, 0xdfff).bankr("bank7").bankw("bank15");
	map(0xe000, 0xffff).bankr("bank8").bankw("bank16");
}

void pmd85_state::alfa_mem(address_map &map)
{
	map(0x0000, 0x0fff).bankrw("bank1");
	map(0x1000, 0x33ff).bankrw("bank2");
	map(0x3400, 0x3fff).bankrw("bank3");
	map(0x4000, 0x7fff).bankrw("bank4");
	map(0x8000, 0x8fff).bankr("bank5");
	map(0x9000, 0xb3ff).bankr("bank6");
	map(0xc000, 0xffff).bankrw("bank7");
}

void pmd85_state::mato_mem(address_map &map)
{
	map(0x0000, 0x3fff).bankrw("bank1");
	map(0x4000, 0x7fff).bankrw("bank2");
	map(0x8000, 0xbfff).bankr("bank3");
	map(0xc000, 0xffff).bankrw("bank4");
}

void pmd85_state::c2717_mem(address_map &map)
{
	map(0x0000, 0x3fff).bankrw("bank1");
	map(0x4000, 0x7fff).bankrw("bank2");
	map(0x8000, 0xbfff).bankr("bank3");
	map(0xc000, 0xffff).bankrw("bank4");
}

/* keyboard input */

static INPUT_PORTS_START( pmd85 )
	PORT_START("KEY0") /* port 0x00 */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("K0") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(F1))
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q') PORT_CHAR('Q')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)          PORT_CHAR('a') PORT_CHAR('A')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY1") /* port 0x01 */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("K1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F2))
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('"')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)          PORT_CHAR('w') PORT_CHAR('W')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)          PORT_CHAR('s') PORT_CHAR('S')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z') PORT_CHAR('Z')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY2") /* port 0x02 */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("K2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F3))
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('#')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)          PORT_CHAR('e') PORT_CHAR('E')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)          PORT_CHAR('d') PORT_CHAR('D')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)          PORT_CHAR('x') PORT_CHAR('X')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY3") /* port 0x03 */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("K3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F4))
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)          PORT_CHAR('r') PORT_CHAR('R')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)          PORT_CHAR('f') PORT_CHAR('F')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)          PORT_CHAR('c') PORT_CHAR('C')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY4") /* port 0x04 */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("K4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F5))
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5)          PORT_CHAR('5') PORT_CHAR('%')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)          PORT_CHAR('t') PORT_CHAR('T')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)          PORT_CHAR('g') PORT_CHAR('G')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)          PORT_CHAR('v') PORT_CHAR('V')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY5") /* port 0x05 */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("K5") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F6))
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('&')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)          PORT_CHAR('h') PORT_CHAR('H')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)          PORT_CHAR('b') PORT_CHAR('B')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY6") /* port 0x06 */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("K6") PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F7))
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('\'')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)          PORT_CHAR('j') PORT_CHAR('J')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)          PORT_CHAR('n') PORT_CHAR('N')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY7") /* port 0x07 */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("K7") PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F8))
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('(')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)          PORT_CHAR('k') PORT_CHAR('K')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)          PORT_CHAR('m') PORT_CHAR('M')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY8") /* port 0x08 */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("K8") PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F9))
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)          PORT_CHAR('o') PORT_CHAR('O')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)          PORT_CHAR('l') PORT_CHAR('L')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR('<')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY9") /* port 0x09 */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("K9") PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F10))
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0)          PORT_CHAR('0') PORT_CHAR('-')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR('+')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR('>')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY10") /* port 0x0a */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("K10") PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F11))
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('_') PORT_CHAR('=')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('@') PORT_CHAR('`')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(':') PORT_CHAR('*')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/') PORT_CHAR('?')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY11") /* port 0x0b */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("K11") PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F12))
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('{') PORT_CHAR('}')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('\\') PORT_CHAR('^')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('[') PORT_CHAR(']')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY12") /* port 0x0c */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("WRK") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INS PTL") PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(INSERT)) PORT_CHAR(UCHAR_MAMEKEY(TAB))
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("<-") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("|<-") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT))
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY13") /* port 0x0d */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C-D") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DEL") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(DEL))
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("^\\") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(HOME))
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("END") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(END))
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("EOL1") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(UCHAR_MAMEKEY(ENTER))
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY14") /* port 0x0e */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CLR") PORT_CODE(KEYCODE_PGUP) PORT_CHAR(UCHAR_MAMEKEY(PGUP))
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RCL") PORT_CODE(KEYCODE_PGDN) PORT_CHAR(UCHAR_MAMEKEY(PGDN))
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("->") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("->|") PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(RALT))
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("EOL2") PORT_CODE(KEYCODE_TAB) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("KEY15") /* port 0x0f */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_MAMEKEY(RSHIFT))
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Stop") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("RESET") /* port 0x10 */
		PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RST") PORT_CODE(KEYCODE_1_PAD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pmd85_state::pmd85_reset), 0)

	PORT_START("DSW0") /* port 0x11 */
		PORT_CONFNAME( 0x01, 0x00, "Basic ROM Module" )
			PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
			PORT_CONFSETTING( 0x01, DEF_STR( On ) )
		PORT_CONFNAME( 0x02, 0x00, "Tape/V.24" )
			PORT_CONFSETTING( 0x00, "Tape" )
			PORT_CONFSETTING( 0x02, "V.24" )
INPUT_PORTS_END


static INPUT_PORTS_START( alfa )
	PORT_INCLUDE( pmd85 )

	PORT_MODIFY( "DSW0" )
		PORT_BIT( 0x01, 0x01, IPT_UNUSED )
INPUT_PORTS_END


static INPUT_PORTS_START (mato)
	PORT_START("KEY0") /* port 0x00 */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('\"')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('#')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5)          PORT_CHAR('5') PORT_CHAR('%')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('&')
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('\'')
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("EOL") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_START("KEY1") /* port 0x01 */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q') PORT_CHAR('Q')
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)          PORT_CHAR('w') PORT_CHAR('W')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)          PORT_CHAR('e') PORT_CHAR('E')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)          PORT_CHAR('r') PORT_CHAR('R')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)          PORT_CHAR('t') PORT_CHAR('T')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U')
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("EOL") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_START("KEY2") /* port 0x02 */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)          PORT_CHAR('a') PORT_CHAR('A')
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)          PORT_CHAR('s') PORT_CHAR('S')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)          PORT_CHAR('d') PORT_CHAR('D')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)          PORT_CHAR('f') PORT_CHAR('F')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)          PORT_CHAR('g') PORT_CHAR('G')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)          PORT_CHAR('h') PORT_CHAR('H')
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)          PORT_CHAR('j') PORT_CHAR('J')
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("EOL") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_START("KEY3") /* port 0x03 */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z') PORT_CHAR('Z')
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)          PORT_CHAR('x') PORT_CHAR('X')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)          PORT_CHAR('c') PORT_CHAR('C')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)          PORT_CHAR('v') PORT_CHAR('V')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)          PORT_CHAR('b') PORT_CHAR('B')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)          PORT_CHAR('n') PORT_CHAR('N')
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)          PORT_CHAR('m') PORT_CHAR('M')
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("EOL") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_START("KEY4") /* port 0x04 */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('=') PORT_CHAR('_')
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0)          PORT_CHAR('0') PORT_CHAR('-')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('\\') PORT_CHAR('^')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('(')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR('<')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)          PORT_CHAR('k') PORT_CHAR('K')
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("EOL") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_START("KEY5") /* port 0x05 */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P')
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/') PORT_CHAR('?')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR('+')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)           PORT_CHAR('.') PORT_CHAR('<')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)          PORT_CHAR('l') PORT_CHAR('L')
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)          PORT_CHAR('o') PORT_CHAR('O')
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("EOL") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_START("KEY6") /* port 0x06 */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('@')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LEFT)           PORT_CHAR(UCHAR_MAMEKEY(LEFT))
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(':') PORT_CHAR('*')
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("EOL") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_START("KEY7") /* port 0x07 */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("EOL") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_START("KEY8") /* port 0x08 */
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Stop") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_MAMEKEY(RSHIFT))
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Continue") PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_START("RESET") /* port 0x09 */
		PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RST") PORT_CODE(KEYCODE_1_PAD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pmd85_state::pmd85_reset), 0)
INPUT_PORTS_END



//static const struct CassetteOptions pmd85_cassette_options =
//{
//  1,      /* channels */
//  16,     /* bits per sample */
//  7200    /* sample frequency */
//};

/* machine definition */
void pmd85_state::pmd85(machine_config &config, bool with_uart)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, XTAL(18'432'000)/9);
	m_maincpu->set_addrmap(AS_PROGRAM, &pmd85_state::pmd85_mem);
	m_maincpu->set_addrmap(AS_IO, &pmd85_state::pmd85_io);
	config.set_maximum_quantum(attotime::from_hz(60));


/*******************************************************************************

    I/O board 8253
    --------------

    Timer 0:
        OUT0    - external interfaces connector (K2)
        CLK0    - external interfaces connector (K2)
        GATE0   - external interfaces connector (K2), default = 1
    Timer 1:
        OUT1    - external interfaces connector (K2), i8251 (for V24 only)
        CLK1    - hardwired to 2 MHz system clock
        GATE1   - external interfaces connector (K2), default = 1
    Timer 2:
        OUT2    - unused
        CLK2    - hardwired to 1HZ signal generator
        GATE2   - hardwired to 5V, default = 1

*******************************************************************************/

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(0);
	m_pit->set_clk<1>(XTAL(18'432'000)/9);
	m_pit->set_clk<2>(1);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_refresh_hz(50);
	screen.set_vblank_time(0);
	screen.set_size(288, 256);
	screen.set_visarea(0, 288-1, 0, 256-1);
	screen.set_screen_update(FUNC(pmd85_state::screen_update));
	screen.set_palette(m_palette);

	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* cassette */
	CASSETTE(config, m_cassette);
	m_cassette->set_formats(pmd85_cassette_formats);
//  m_cassette->set_create_opts(&pmd85_cassette_options);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("pmd85_cass");

	/* software lists */
	SOFTWARE_LIST(config, "cass_list").set_original("pmd85_cass");

	/* uart */
	if (with_uart)
	{
		I8251(config, m_uart, 0);
		m_uart->txd_handler().set([this] (bool state) { m_txd = state; });
		m_uart->rts_handler().set([this] (bool state) { m_uart->write_cts(state); });
	}

	/* internal ram */
	RAM(config, m_ram).set_default_size("64K").set_default_value(0x00);
}

void pmd85_state::pmd851(machine_config &config)
{
	pmd85(config);

	I8255(config, m_ppi0, 0);
	m_ppi0->in_pa_callback().set(FUNC(pmd85_state::ppi0_porta_r));
	m_ppi0->in_pb_callback().set(FUNC(pmd85_state::ppi0_portb_r));
	m_ppi0->in_pc_callback().set(FUNC(pmd85_state::ppi0_portc_r));
	m_ppi0->out_pa_callback().set(FUNC(pmd85_state::ppi0_porta_w));
	m_ppi0->out_pb_callback().set(FUNC(pmd85_state::ppi0_portb_w));
	m_ppi0->out_pc_callback().set(FUNC(pmd85_state::ppi0_portc_w));

	I8255(config, m_ppi1, 0);
	m_ppi1->in_pa_callback().set(FUNC(pmd85_state::ppi1_porta_r));
	m_ppi1->in_pb_callback().set(FUNC(pmd85_state::ppi1_portb_r));
	m_ppi1->in_pc_callback().set(FUNC(pmd85_state::ppi1_portc_r));
	m_ppi1->out_pa_callback().set(FUNC(pmd85_state::ppi1_porta_w));
	m_ppi1->out_pb_callback().set(FUNC(pmd85_state::ppi1_portb_w));
	m_ppi1->out_pc_callback().set(FUNC(pmd85_state::ppi1_portc_w));

	I8255(config, m_ppi2, 0);
	m_ppi2->in_pa_callback().set(FUNC(pmd85_state::ppi2_porta_r));
	m_ppi2->in_pb_callback().set(FUNC(pmd85_state::ppi2_portb_r));
	m_ppi2->in_pc_callback().set(FUNC(pmd85_state::ppi2_portc_r));
	m_ppi2->out_pa_callback().set(FUNC(pmd85_state::ppi2_porta_w));
	m_ppi2->out_pb_callback().set(FUNC(pmd85_state::ppi2_portb_w));
	m_ppi2->out_pc_callback().set(FUNC(pmd85_state::ppi2_portc_w));

	I8255(config, m_ppi3, 0);
	m_ppi3->in_pa_callback().set(FUNC(pmd85_state::ppi3_porta_r));
	m_ppi3->in_pb_callback().set(FUNC(pmd85_state::ppi3_portb_r));
	m_ppi3->in_pc_callback().set(FUNC(pmd85_state::ppi3_portc_r));
	m_ppi3->out_pa_callback().set(FUNC(pmd85_state::ppi3_porta_w));
	m_ppi3->out_pb_callback().set(FUNC(pmd85_state::ppi3_portb_w));
	m_ppi3->out_pc_callback().set(FUNC(pmd85_state::ppi3_portc_w));
}

void pmd85_state::pmd852a(machine_config &config)
{
	pmd851(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &pmd85_state::pmd852a_mem);
}

void pmd85_state::pmd853(machine_config &config)
{
	pmd851(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &pmd85_state::pmd853_mem);
}

void pmd85_state::alfa(machine_config &config)
{
	pmd85(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &pmd85_state::alfa_mem);

	I8255(config, m_ppi0, 0);
	m_ppi0->in_pa_callback().set(FUNC(pmd85_state::ppi0_porta_r));
	m_ppi0->in_pb_callback().set(FUNC(pmd85_state::ppi0_portb_r));
	m_ppi0->in_pc_callback().set(FUNC(pmd85_state::ppi0_portc_r));
	m_ppi0->out_pa_callback().set(FUNC(pmd85_state::ppi0_porta_w));
	m_ppi0->out_pb_callback().set(FUNC(pmd85_state::ppi0_portb_w));
	m_ppi0->out_pc_callback().set(FUNC(pmd85_state::ppi0_portc_w));

	I8255(config, m_ppi1, 0);
	m_ppi1->in_pa_callback().set(FUNC(pmd85_state::ppi1_porta_r));
	m_ppi1->in_pb_callback().set(FUNC(pmd85_state::ppi1_portb_r));
	m_ppi1->in_pc_callback().set(FUNC(pmd85_state::ppi1_portc_r));
	m_ppi1->out_pa_callback().set(FUNC(pmd85_state::ppi1_porta_w));
	m_ppi1->out_pb_callback().set(FUNC(pmd85_state::ppi1_portb_w));
	m_ppi1->out_pc_callback().set(FUNC(pmd85_state::ppi1_portc_w));

	I8255(config, m_ppi2, 0);
	m_ppi2->in_pa_callback().set(FUNC(pmd85_state::ppi2_porta_r));
	m_ppi2->in_pb_callback().set(FUNC(pmd85_state::ppi2_portb_r));
	m_ppi2->in_pc_callback().set(FUNC(pmd85_state::ppi2_portc_r));
	m_ppi2->out_pa_callback().set(FUNC(pmd85_state::ppi2_porta_w));
	m_ppi2->out_pb_callback().set(FUNC(pmd85_state::ppi2_portb_w));
	m_ppi2->out_pc_callback().set(FUNC(pmd85_state::ppi2_portc_w));
}

void pmd85_state::mato(machine_config &config)
{
	pmd85(config, false); /* no uart */
	m_maincpu->set_addrmap(AS_PROGRAM, &pmd85_state::mato_mem);
	m_maincpu->set_addrmap(AS_IO, &pmd85_state::mato_io);

	I8255(config, m_ppi0, 0);
	m_ppi0->in_pa_callback().set(FUNC(pmd85_state::ppi0_porta_r));
	m_ppi0->out_pa_callback().set(FUNC(pmd85_state::ppi0_porta_w));
	m_ppi0->in_pb_callback().set(FUNC(pmd85_state::mato_ppi0_portb_r));
	m_ppi0->out_pb_callback().set(FUNC(pmd85_state::ppi0_portb_w));
	m_ppi0->in_pc_callback().set(FUNC(pmd85_state::mato_ppi0_portc_r));
	m_ppi0->out_pc_callback().set(FUNC(pmd85_state::mato_ppi0_portc_w));
}

void pmd85_state::c2717(machine_config &config)
{
	pmd851(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &pmd85_state::c2717_mem);
}


ROM_START(pmd851)
	ROM_REGION(0x1000,"maincpu",0)
	ROM_LOAD("pmd85-1.bin", 0x0000, 0x1000, CRC(ef50b416) SHA1(afa3ec0d03228adc5287a4cba905ce7ad0497dff))

	ROM_REGION(0x2400,"user1",0)
	ROM_LOAD("pmd85-1.bas", 0x0000, 0x2400, CRC(4fc37d45) SHA1(3bd0f92f37a3f2ee539916dc75508bda37433a72))
ROM_END

ROM_START(pmd852)
	ROM_REGION(0x1000,"maincpu",0)
	ROM_LOAD("pmd85-2.bin", 0x0000, 0x1000, CRC(d4786f63) SHA1(6facdf37bb012714244b012a0c4bd715a956e42b))

	ROM_REGION(0x2400,"user1",0)
	ROM_LOAD("pmd85-2.bas", 0x0000, 0x2400, CRC(fc4a3ebf) SHA1(3bfc0e9a5cd5187da573b5d539d7246358125a88))
ROM_END

ROM_START(pmd852a)
	ROM_REGION(0x1000,"maincpu",0)
	ROM_LOAD("pmd85-2a.bin", 0x0000, 0x1000, CRC(5a9a961b) SHA1(7363341596367d08b9a98767c6585ce18dfd03af))

	ROM_REGION(0x2400,"user1",0)
	ROM_LOAD("pmd85-2a.bas", 0x0000, 0x2400, CRC(6ff379ad) SHA1(edcaf2420cac9771596ead5c86c41116b228eca3))
ROM_END

ROM_START(pmd852b)
	ROM_REGION(0x1000,"maincpu",0)
	ROM_LOAD("pmd85-2a.bin", 0x0000, 0x1000, CRC(5a9a961b) SHA1(7363341596367d08b9a98767c6585ce18dfd03af))

	ROM_REGION(0x2400,"user1",0)
	ROM_LOAD("pmd85-2a.bas", 0x0000, 0x2400, CRC(6ff379ad) SHA1(edcaf2420cac9771596ead5c86c41116b228eca3))
ROM_END

ROM_START(pmd853)
	ROM_REGION(0x2000,"maincpu",0)
	ROM_LOAD("pmd85-3.bin", 0x0000, 0x2000, CRC(83e22c47) SHA1(5f131e27ae3ec8907adbe5cd228c67d131066084))

	ROM_REGION(0x2800,"user1",0)
	ROM_LOAD("pmd85-3.bas", 0x0000, 0x2800, CRC(1e30e91d) SHA1(d086040abf4c0a7e5da8cf4db7d1668a1d9309a4))
ROM_END

ROM_START(alfa)
	ROM_REGION(0x3400,"maincpu",0)
	ROM_LOAD("alfa.bin", 0x0000, 0x1000, CRC(e425eedb) SHA1(db93b5de1e16b5ae71be08feb083a2ac15759495))
	ROM_LOAD("alfa.bas", 0x1000, 0x2400, CRC(9a73bfd2) SHA1(74314d989846f64e715f64deb84cb177fa62f4a9))
ROM_END

ROM_START(mato)
	ROM_REGION(0x4000,"maincpu",0)
	ROM_SYSTEM_BIOS(0, "default", "BASIC")
	ROMX_LOAD("mato.bin",  0x0000, 0x4000, CRC(574110a6) SHA1(4ff2cd4b07a1a700c55f92e5b381c04f758fb461), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "ru", "Russian")
	ROMX_LOAD("mato-ru.rom",  0x0000, 0x4000, CRC(44b68be4) SHA1(0d9ea9a9380e2af011a2f0b64c534dd0eb0a1fac), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "lan", "BASIC LAN")
	ROMX_LOAD("mato-lan.rom",  0x0000, 0x4000, CRC(422cddde) SHA1(2a3dacf8e3e7637109c9d267f589a00881e9a5f4), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "games", "Games v1")
	ROMX_LOAD("matoh.bin", 0x0000, 0x4000, CRC(ca25880d) SHA1(38ce0b6a26d48a09fdf96863c3eaf3705aca2590), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "gamesen", "Games v2 EN")
	ROMX_LOAD("matogmen.rom", 0x0000, 0x4000, CRC(47e039c8) SHA1(6cc73a6b58921b33691d2751dee28428456eb222), ROM_BIOS(4))
	ROM_SYSTEM_BIOS(5, "gamessk", "Games v2 SK")
	ROMX_LOAD("matogmsk.rom", 0x0000, 0x4000, CRC(d0c9b1e7) SHA1(9e7289d971a957bf161c317e5fa76db3289ee23c), ROM_BIOS(5))
	ROM_SYSTEM_BIOS(6, "games3", "Games v3")
	ROMX_LOAD("matogm3.rom", 0x0000, 0x4000, CRC(9352f2c1) SHA1(b3e45c56d2800c69a0bb02febda6fa715f1afbc3), ROM_BIOS(6))
ROM_END

ROM_START(c2717)
	ROM_REGION(0x4000,"maincpu",0)
	ROM_LOAD("c2717.rom", 0x0000, 0x4000, CRC(da1703b1) SHA1(9fb93e6cae8b551064c7175bf3b4e3113429ce73))
ROM_END

ROM_START(c2717pmd)
	ROM_REGION(0x4000,"maincpu",0)
	ROM_LOAD( "c2717-pmd32.rom", 0x00000, 0x4000, CRC(cbdd323c) SHA1(ee9fea11be8bd4f945c583b0ae5684269906d0ce))
ROM_END


//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT          COMPANY             FULLNAME                     FLAGS
COMP( 1985, pmd851,   0,      0,      pmd851,  alfa,  pmd85_state, init_pmd851,  "Tesla",            "PMD-85.1",                  MACHINE_SUPPORTS_SAVE )
COMP( 1985, pmd852,   pmd851, 0,      pmd851,  pmd85, pmd85_state, init_pmd852,  "Tesla",            "PMD-85.2",                  MACHINE_SUPPORTS_SAVE )
COMP( 1985, pmd852a,  pmd851, 0,      pmd852a, pmd85, pmd85_state, init_pmd852a, "Tesla",            "PMD-85.2A",                 MACHINE_SUPPORTS_SAVE )
COMP( 1985, pmd852b,  pmd851, 0,      pmd852a, pmd85, pmd85_state, init_pmd852a, "Tesla",            "PMD-85.2B",                 MACHINE_SUPPORTS_SAVE )
COMP( 1988, pmd853,   pmd851, 0,      pmd853,  pmd85, pmd85_state, init_pmd853,  "Tesla",            "PMD-85.3",                  MACHINE_SUPPORTS_SAVE )
COMP( 1986, alfa,     pmd851, 0,      alfa,    alfa,  pmd85_state, init_alfa,    "Didaktik Skalica", "Didaktik Alfa",             MACHINE_SUPPORTS_SAVE )
COMP( 1985, mato,     pmd851, 0,      mato,    mato,  pmd85_state, init_mato,    "Statny",           "Mato",                      MACHINE_SUPPORTS_SAVE )
COMP( 1989, c2717,    pmd851, 0,      c2717,   pmd85, pmd85_state, init_c2717,   "Zbrojovka Brno",   "Consul 2717",               MACHINE_SUPPORTS_SAVE )
COMP( 1989, c2717pmd, pmd851, 0,      c2717,   pmd85, pmd85_state, init_c2717,   "Zbrojovka Brno",   "Consul 2717 (with PMD-32)", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



pmi80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/********************************************************************************************

Tesla PMI-80

2009-05-12 Skeleton driver.
2011-06-20 Made mostly working
2019-06-27 More work done


ToDo:
- cassette (coded per schematic, but not working ("SPAT" error))
- I button (should cause an interrupt and jump to 0038 - currently nothing happens)


Notes:
- Keyboard consists of 16 black hex keys, and 9 blue function keys
- The hex keys are 0 through 9, A through F on our keyboard
- The function key labels are RE, I, EX, R, BR, M, L, S, =
- The letter M shows as an inverted U in the display
- Turn it on, it says ''PMI -80''
- Press any key, it shows a ? at the left
- Press the function key corresponding to what you want to do
- Press the numbers to select an address or whatever
- For example, press M then enter an address, press =, enter data,
   press =  to increment to next address or to scan through them.
- PPI1 is programmed to mode 0, Port A output, Port B input, PC0-3 output, PC4-7 input.

Paste test:
    Paste --1C00^11^22^33^44^55^66^77^88^99^---1C00^
    Press = to verify the data that was entered.

*******************************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8255.h"
#include "imagedev/cassette.h"
#include "machine/timer.h"
#include "speaker.h"
#include "pmi80.lh"


namespace {

class pmi80_state : public driver_device
{
public:
	pmi80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ppi1(*this, "ppi1")
		, m_cass(*this, "cassette")
		, m_io_keyboard(*this, "X%u", 0U)
		, m_digits(*this, "digit%u", 0U)
	{ }

	void pmi80(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(reset_button);
	DECLARE_INPUT_CHANGED_MEMBER(int_button);

private:
	uint8_t keyboard_r();
	void keyboard_w(uint8_t data);
	void leds_w(uint8_t data);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	uint8_t m_keyrow = 0U;
	bool m_ledready = false;
	bool m_cassbit = false, m_cassold = false;
	u16 m_cass_cnt = 0U;
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_device<i8255_device> m_ppi1;
	required_device<cassette_image_device> m_cass;
	required_ioport_array<9> m_io_keyboard;
	output_finder<9> m_digits;
};


uint8_t pmi80_state::keyboard_r()
{
	u8 data = 0x7f;
	if (m_keyrow > 0xF6)
		data &= m_io_keyboard[m_keyrow-0xf7]->read();

	data |= (m_cassbit) ? 0x80 : 0;
	return data;
}

void pmi80_state::keyboard_w(uint8_t data)
{
	m_keyrow = data;
	m_ledready = true;
}

void pmi80_state::leds_w(uint8_t data)
{
	if (m_ledready)
	{
		m_ledready = false;
		if (m_keyrow > 0xF6)
			m_digits[m_keyrow^0xff] = data^0xff;
	}

	data &= 0xc0;
	m_cass->output((data == 0xc0) ? -1.0 : +1.0);
}

TIMER_DEVICE_CALLBACK_MEMBER( pmi80_state::kansas_r )
{
	// cassette - pulses = 1; no pulses = 0
	m_cass_cnt++;
	bool cass_ws = (m_cass->input() > +0.04) ? 1 : 0;

	if (cass_ws != m_cassold)
	{
		m_cassold = cass_ws;
		m_cassbit = (m_cass_cnt < 40) ? 1 : 0;
		m_cass_cnt = 0;
	}
}


void pmi80_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xdfff); // A13 not used
	map(0x0000, 0x07ff).rom();
	map(0x1c00, 0x1fff).ram();
}

void pmi80_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x0f);
	map(0x04, 0x07).rw("ppi2", FUNC(i8255_device::read), FUNC(i8255_device::write)); // io12, 0xf4-0xf7
	map(0x08, 0x0b).rw("ppi1", FUNC(i8255_device::read), FUNC(i8255_device::write)); // io8,  0xf8-0xfb
}

/* Input ports */
static INPUT_PORTS_START( pmi80 )
	PORT_START("SP")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RE") PORT_CODE(KEYCODE_LALT) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pmi80_state::reset_button), 0) PORT_CHAR('W')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pmi80_state::int_button), 0) PORT_CHAR('I')
	PORT_START("X0")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("=") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_START("X1")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_START("X2")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B BC") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 HL") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_START("X3")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('-')// Memory mode
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_START("X4")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BR") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') // Breakpoint
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D DE") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_START("X5")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R') // Registers
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("EX") PORT_CODE(KEYCODE_G) PORT_CHAR('X') // Go
	PORT_START("X6")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('L') // Load tape
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A AF") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 SP") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_START("X7")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('S') // Save tape
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_START("X8")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(pmi80_state::reset_button)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, oldval ? ASSERT_LINE : CLEAR_LINE);
}

INPUT_CHANGED_MEMBER(pmi80_state::int_button)
{
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, oldval ? ASSERT_LINE : CLEAR_LINE);  // FIXME: jump to 0x0038
}


void pmi80_state::machine_reset()
{
}

void pmi80_state::machine_start()
{
	m_digits.resolve();

	save_item(NAME(m_keyrow));
	save_item(NAME(m_ledready));
	save_item(NAME(m_cassbit));
	save_item(NAME(m_cassold));
	save_item(NAME(m_cass_cnt));
}


void pmi80_state::pmi80(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, XTAL(10'000'000)/9);   // 10MHz divided by 9 (by MH8224)
	m_maincpu->set_addrmap(AS_PROGRAM, &pmi80_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &pmi80_state::io_map);

	I8255A(config, m_ppi1);
	m_ppi1->out_pa_callback().set(FUNC(pmi80_state::leds_w));
	m_ppi1->in_pc_callback().set(FUNC(pmi80_state::keyboard_r));
	m_ppi1->out_pc_callback().set(FUNC(pmi80_state::keyboard_w));

	I8255A(config, "ppi2");   // User PPI

	SPEAKER(config, "mono").front_center();

	// cassette
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	TIMER(config, "kansas_r").configure_periodic(FUNC(pmi80_state::kansas_r), attotime::from_hz(40000));

	/* video hardware */
	config.set_default_layout(layout_pmi80);
}

/* ROM definition */
ROM_START( pmi80 )
	ROM_REGION( 0x0800, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "pmi80_monitor.io5", 0x0000, 0x0400, CRC(b93f4407) SHA1(43153441070ed0572f33d2815635eb7bae878e38))
	//ROM_LOAD("expansion.io11", 0x0400, 0x0400, NO_DUMP)   Empty ROM socket
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME  FLAGS
COMP( 1982, pmi80, 0,      0,      pmi80,   pmi80, pmi80_state, empty_init, "Tesla", "PMI-80", MACHINE_SUPPORTS_SAVE )



pmicrodx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Chess King Pocket Micro De-Luxe

Hardware notes:
- Hitachi HD44868 @ ~800kHz (33K resistor)
- LCD with 4 7segs and custom segments, piezo

HD44868A07 MCU is used in:
- Chess King Pocket Micro De-Luxe
- Chess King Mighty Midget De-Luxe
- Mephisto Teufelchen (H+G brand Pocket Micro De-Luxe)

TODO:
- dump/add the first version (Pocket Micro), does it fit in this driver?

*******************************************************************************/

#include "emu.h"

#include "cpu/hmcs40/hmcs40.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "cking_pmicrodx.lh"


namespace {

class pmicrodx_state : public driver_device
{
public:
	pmicrodx_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void pmicrodx(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(input_changed) { refresh_irq(1 << param); }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD { refresh_irq(); }

private:
	// devices/pointers
	required_device<hmcs40_cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<5> m_inputs;

	u8 m_inp_mux = 0;
	u8 m_lcd_com = 0;
	u64 m_lcd_segs = 0;

	// I/O handlers
	void update_lcd();
	template<int N> void seg_w(u8 data);
	u8 read_buttons();
	void refresh_irq(u8 mask = ~0);
	u16 input_r();
	void control_w(u16 data);
};

void pmicrodx_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_lcd_com));
	save_item(NAME(m_lcd_segs));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void pmicrodx_state::update_lcd()
{
	// LCD common is analog (voltage level)
	const u8 com = population_count_32(m_lcd_com & 3);
	const u64 data = (com == 0) ? m_lcd_segs : (com == 2) ? ~m_lcd_segs : 0;
	m_display->write_row(0, data);
}

template<int N>
void pmicrodx_state::seg_w(u8 data)
{
	// R0x-R6x: LCD segments
	const u8 shift = N * 4;
	m_lcd_segs = (m_lcd_segs & ~(u64(0xf << shift))) | (u64(data) << shift);
	update_lcd();
}

u8 pmicrodx_state::read_buttons()
{
	u8 data = 0;

	for (int i = 0; i < 4; i++)
		if (m_inp_mux & m_inputs[i]->read())
			data |= 1 << i;

	return data;
}

void pmicrodx_state::refresh_irq(u8 mask)
{
	// half of the buttons are tied to MCU interrupt pins
	mask &= read_buttons();
	for (int i = 0; i < 2; i++)
		m_maincpu->set_input_line(i, BIT(mask, i) ? CLEAR_LINE : ASSERT_LINE);
}

u16 pmicrodx_state::input_r()
{
	// D0: battery status
	u16 data = m_inputs[4]->read() & 1;

	// D2,D3: read buttons
	data |= read_buttons() & 0xc;
	return ~data;
}

void pmicrodx_state::control_w(u16 data)
{
	// D1: speaker out
	m_dac->write(BIT(data, 1));

	// D4-D6: input mux
	m_inp_mux = ~data >> 4 & 7;
	refresh_irq();

	// D7,D8: LCD common
	m_lcd_com = data >> 7 & 3;

	// D9-D15: more LCD segments
	m_lcd_segs = (m_lcd_segs & 0x0fff'ffffULL) | (u64(data & 0xfe00) << 19);
	update_lcd();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

#define INPUT_CHANGED(x) \
	PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pmicrodx_state::input_changed), x)

static INPUT_PORTS_START( pmicrodx )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) INPUT_CHANGED(0) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("A1")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) INPUT_CHANGED(0) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("E5")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) INPUT_CHANGED(0) PORT_CODE(KEYCODE_L) PORT_NAME("LV")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) INPUT_CHANGED(1) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("B2")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) INPUT_CHANGED(1) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("F6")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) INPUT_CHANGED(1) PORT_CODE(KEYCODE_T) PORT_NAME("TB")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("C3")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("G7")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("EN")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("D4")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("H8")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE")

	PORT_START("IN.4")
	PORT_CONFNAME( 0x01, 0x01, "Battery Status" )
	PORT_CONFSETTING(    0x00, "Low" )
	PORT_CONFSETTING(    0x01, DEF_STR( Normal ) )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void pmicrodx_state::pmicrodx(machine_config &config)
{
	// basic machine hardware
	HD44868(config, m_maincpu, 800'000); // approximation
	m_maincpu->write_r<0>().set(FUNC(pmicrodx_state::seg_w<0>));
	m_maincpu->write_r<1>().set(FUNC(pmicrodx_state::seg_w<1>));
	m_maincpu->write_r<2>().set(FUNC(pmicrodx_state::seg_w<2>));
	m_maincpu->write_r<3>().set(FUNC(pmicrodx_state::seg_w<3>));
	m_maincpu->write_r<4>().set(FUNC(pmicrodx_state::seg_w<4>));
	m_maincpu->write_r<5>().set(FUNC(pmicrodx_state::seg_w<5>));
	m_maincpu->write_r<6>().set(FUNC(pmicrodx_state::seg_w<6>));
	m_maincpu->write_d().set(FUNC(pmicrodx_state::control_w));
	m_maincpu->read_d().set(FUNC(pmicrodx_state::input_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(1, 35);
	m_display->set_bri_levels(0.05);
	config.set_default_layout(layout_cking_pmicrodx);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/3, 851/3);
	screen.set_visarea_full();

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( pmicrodx )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD("chessking_mark_2_hd44868a07", 0x0000, 0x2000, CRC(aef47e60) SHA1(97cb7b51ce354c54c6f0faa903d5bd70d5a108ba) )
	ROM_IGNORE( 0x2000 ) // ignore factory test banks

	ROM_REGION( 74477, "screen", 0 )
	ROM_LOAD("pmicrodx.svg", 0, 74477, CRC(34563496) SHA1(dcca2223cc35d54955caead8ff14e6f96b4155ce) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1984, pmicrodx, 0,      0,      pmicrodx, pmicrodx, pmicrodx_state, empty_init, "Chess King / Intelligent Software", "Pocket Micro De-Luxe", MACHINE_SUPPORTS_SAVE )



pmp11.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/t11/t11.h"
#include "imagedev/floppy.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/wd_fdc.h"

namespace {

class pmp11_state : public driver_device
{
public:
	pmp11_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_uart1(*this, "uart1")
		, m_uart2(*this, "uart2")
		, m_fdc(*this, "fdc")
	{ }

	void pmp11(machine_config &config) ATTR_COLD;

private:
	void pdp11_mem(address_map &map) ATTR_COLD;

	required_device<t11_device> m_maincpu;
	required_device<i8251_device> m_uart1;
	required_device<i8251_device> m_uart2;
	required_device<wd2797_device> m_fdc;
};

void pmp11_state::pdp11_mem(address_map &map)
{
	map.unmap_value_high();
	map(0000000, 0167777).ram();
	map(0170000, 0173777).rom().region("maincpu", 0);
	map(0174000, 0175777).ram();
	map(0176500, 0176500).r(m_uart2, FUNC(i8251_device::status_r));
	map(0176502, 0176502).r(m_uart2, FUNC(i8251_device::data_r));
	map(0176504, 0176507).nopr();
	map(0176504, 0176504).w(m_uart2, FUNC(i8251_device::control_w));
	map(0176506, 0176506).w(m_uart2, FUNC(i8251_device::data_w));
	map(0177560, 0177560).r(m_uart1, FUNC(i8251_device::status_r));
	map(0177562, 0177562).r(m_uart1, FUNC(i8251_device::data_r));
	map(0177564, 0177567).nopr();
	map(0177564, 0177564).w(m_uart1, FUNC(i8251_device::control_w));
	map(0177566, 0177566).w(m_uart1, FUNC(i8251_device::data_w));
}

/* Input ports */
static INPUT_PORTS_START( pmp11 )
INPUT_PORTS_END

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void pmp11_state::pmp11(machine_config &config)
{
	T11(config, m_maincpu, 8_MHz_XTAL);
	m_maincpu->set_initial_mode(7 << 13);
	m_maincpu->set_addrmap(AS_PROGRAM, &pmp11_state::pdp11_mem);

	clock_device &uart_clock(CLOCK(config, "uart_clock", 8_MHz_XTAL / 4 / 13));
	uart_clock.signal_handler().set(m_uart1, FUNC(i8251_device::write_txc));
	uart_clock.signal_handler().append(m_uart1, FUNC(i8251_device::write_rxc));
	uart_clock.signal_handler().append(m_uart2, FUNC(i8251_device::write_txc));
	uart_clock.signal_handler().append(m_uart2, FUNC(i8251_device::write_rxc));

	I8251(config, m_uart1, 0);
	m_uart1->txd_handler().set("rs232a", FUNC(rs232_port_device::write_txd));
	m_uart1->dtr_handler().set("rs232a", FUNC(rs232_port_device::write_dtr));
	m_uart1->rts_handler().set("rs232a", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, "terminal"));
	rs232a.rxd_handler().set("uart1", FUNC(i8251_device::write_rxd));
	rs232a.dsr_handler().set("uart1", FUNC(i8251_device::write_dsr));
	rs232a.cts_handler().set("uart1", FUNC(i8251_device::write_cts));
	rs232a.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	I8251(config, m_uart2, 0);
	m_uart2->txd_handler().set("rs232b", FUNC(rs232_port_device::write_txd));
	m_uart2->dtr_handler().set("rs232b", FUNC(rs232_port_device::write_dtr));
	m_uart2->rts_handler().set("rs232b", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set("uart2", FUNC(i8251_device::write_rxd));
	rs232b.dsr_handler().set("uart2", FUNC(i8251_device::write_dsr));
	rs232b.cts_handler().set("uart2", FUNC(i8251_device::write_cts));

	WD2797(config, m_fdc, 8_MHz_XTAL / 4);
}

ROM_START( pmp11 )
	ROM_REGION( 0x0800, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v20", "v 2.0 - 1867" ) // ODT 2.0 - from museum item #1867, no markings on EPROM
	ROMX_LOAD( "rom.bin", 0x0000, 0x0800, CRC(2cfdc3a3) SHA1(50ffa2a3bd0b75c1ecb4ab6c691796ab0d85dd4e), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "alt", "v 2.0 - 1113" ) // ODT 2.0 - from museum item #1113
	ROMX_LOAD( "odt_2.0.bin", 0x0000, 0x0800, CRC(0e970130) SHA1(811b40eb6ca78d7a1b0701c5d62738bd31026db8), ROM_BIOS(1))
ROM_END

} // Anonymous namespace


/* Driver */

/*    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT  CLASS        INIT        COMPANY                  FULLNAME     FLAGS */
COMP( 1985, pmp11,    0,       0,      pmp11,    pmp11, pmp11_state, empty_init, "Institut Jožef Stefan", "PMP-11",    MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



pn8800fxb.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/****************************************************************************

    Brother PN-8800FXB "Super PowerNote"

    Hardware:
    - HD64180RF6X CPU
    - TC551001BFL-70 (128k RAM)
    - 2x HY6264A (2x 8k, VRAM?)
    - HG62F33R32FH UC2836-A (gate array)
    - HD63266F FDC
    - RC224ATF (modem)
    - TC8521AM RTC
    - XTAL XT1 16.000312 MHz (near modem), XT2 12.228MHz (near CPU)
    - XTAL XT3 32.768kHz (near RTC), XT4 18.0MHz (near gate array)
    - XTAL XT5 16.0MHz (near FDC)

    TODO:
    - Unknown memory change bits: DUA, RAMIN, DICSEL
    - Improve video emulation (need test cases)
    - Floppy self-test fails (but works otherwise)
    - Modem
    - Soft power off/on
    - RAM contents should be retained when turning off
    - Bookman (unlikely to make progress, extra PCB contains custom CPU)

    Notes:
    - The PN-8500MDS uses the same gate array. ROM id "UC8247-A-PN87".
    - There is a serially connected daughterbord containing the Bookman logic
    - Press CODE+SHIFT+BS on the main menu for a self-test screen
    - Currently the system always does a cold boot. This means that the
      century byte at $6180b will be invalid, causing the system to program
      the RTC with default values.

****************************************************************************/

#include "emu.h"

#include "bus/centronics/ctronics.h"
#include "cpu/z180/z180.h"
#include "imagedev/floppy.h"
#include "machine/rp5c01.h"
#include "machine/timer.h"
#include "machine/upd765.h"
#include "sound/beep.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class pn8800fxb_state : public driver_device
{
public:
	pn8800fxb_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_rombank(*this, "rombank"),
		m_palette(*this, "palette"),
		m_gfxdecode(*this, "gfxdecode"),
		m_keys(*this, "KO%u", 0U),
		m_buzzer(*this, "buzzer"),
		m_fdc(*this, "fdc"),
		m_floppy(*this, "fdc:0"),
		m_centronics(*this, "centronics"),
		m_lomem_view(*this, "lomem")
	{ }

	int floppy_index_r();

	void pn8800fxb(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<z80180_device> m_maincpu;
	required_memory_bank m_rombank;
	required_device<palette_device> m_palette;
	required_device<gfxdecode_device> m_gfxdecode;
	required_ioport_array<9> m_keys;
	required_device<beep_device> m_buzzer;
	required_device<hd63266f_device> m_fdc;
	required_device<floppy_connector> m_floppy;
	required_device<centronics_device> m_centronics;
	memory_view m_lomem_view;

	uint8_t m_key_select;

	std::unique_ptr<uint8_t[]> m_vram;
	uint16_t m_cursor_addr;
	uint16_t m_video_addr;
	uint8_t m_video_ctrl;
	uint8_t m_video_offset;
	uint8_t m_cursor_ctrl;

	bool m_fdc_drq;

	bool m_centronics_busy;
	bool m_centronics_select;

	uint8_t m_mem_change;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	uint8_t keyboard_r();
	void keyboard_w(uint8_t data);

	void palette(palette_device &palette) const;
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void cursor_addr_lo_w(uint8_t data);
	void cursor_addr_hi_w(uint8_t data);
	void video_addr_lo_w(uint8_t data);
	void video_addr_hi_w(uint8_t data);
	uint8_t video_data_r();
	void video_data_w(uint8_t data);
	uint8_t video_ctrl_r();
	void video_ctrl_w(uint8_t data);
	void video_offset_w(uint8_t data);
	void cursor_ctrl_w(uint8_t data);

	void buzzer_w(uint8_t data);

	void fdc_mode_select_w(uint8_t data);
	void fdc_drq_w(int state);
	uint8_t fdc_data_r();
	void fdc_data_w(uint8_t data);
	int floppy_dskchg_r();

	uint8_t cdcc_ctrl_r();
	void cdcc_ctrl_w(uint8_t);

	void mem_change_w(uint8_t data);
	void bank_select_w(uint8_t data);
	void int_ack_w(uint8_t data);
	TIMER_DEVICE_CALLBACK_MEMBER(int1_timer);
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void pn8800fxb_state::mem_map(address_map &map)
{
	map(0x00000, 0x01fff).rom();
	map(0x02000, 0x05fff).ram().share("window1");
	map(0x06000, 0x07fff).view(m_lomem_view);
	m_lomem_view[0](0x06000, 0x07fff).rom().region("maincpu", 0x06000);
	m_lomem_view[1](0x06000, 0x07fff).ram().share("window2");
	map(0x08000, 0x3ffff).rom();
	map(0x40000, 0x41fff).unmaprw();
	map(0x42000, 0x45fff).rom().region("maincpu", 0x02000);
	map(0x46000, 0x4ffff).unmaprw();
	map(0x50000, 0x5ffff).bankr(m_rombank);
	map(0x60000, 0x61fff).ram();
	map(0x62000, 0x65fff).ram().share("window1");
	map(0x66000, 0x67fff).ram().share("window2");
	map(0x68000, 0x7ffff).ram();
}

void pn8800fxb_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x3f).noprw(); // z180 internal
	map(0x70, 0x70).w(FUNC(pn8800fxb_state::cursor_addr_lo_w));
	map(0x71, 0x71).w(FUNC(pn8800fxb_state::cursor_addr_hi_w));
	map(0x72, 0x72).w(FUNC(pn8800fxb_state::video_addr_lo_w));
	map(0x73, 0x73).w(FUNC(pn8800fxb_state::video_addr_hi_w));
	map(0x74, 0x74).rw(FUNC(pn8800fxb_state::video_data_r), FUNC(pn8800fxb_state::video_data_w));
	map(0x75, 0x75).rw(FUNC(pn8800fxb_state::video_ctrl_r), FUNC(pn8800fxb_state::video_ctrl_w));
	map(0x76, 0x76).w(FUNC(pn8800fxb_state::video_offset_w));
	map(0x77, 0x77).w(FUNC(pn8800fxb_state::cursor_ctrl_w));
	map(0x78, 0x78).mirror(0x07).w(FUNC(pn8800fxb_state::fdc_mode_select_w));
	map(0x80, 0x80).mirror(0x04).rw(m_fdc, FUNC(hd63266f_device::msr_r), FUNC(hd63266f_device::abort_w));
	map(0x81, 0x81).mirror(0x04).rw(FUNC(pn8800fxb_state::fdc_data_r), FUNC(pn8800fxb_state::fdc_data_w));
	map(0x82, 0x82).mirror(0x04).r(m_fdc, FUNC(hd63266f_device::extstat_r));
	map(0x88, 0x88).mirror(0x07).portr("misc");
	map(0x90, 0x90).mirror(0x07).w(FUNC(pn8800fxb_state::mem_change_w));
	// 98-9f rs break
	// a0-a7 rs232
	map(0xa8, 0xa8).mirror(0x07).nopr(); // "not used" - but read often
	map(0xb0, 0xb0).mirror(0x07).portr("power");
	map(0xb8, 0xb8).mirror(0x07).rw(FUNC(pn8800fxb_state::keyboard_r), FUNC(pn8800fxb_state::keyboard_w));
	map(0xc0, 0xc0).mirror(0x07).w("centronics_latch", FUNC(output_latch_device::write));
	map(0xc8, 0xc8).mirror(0x07).rw(FUNC(pn8800fxb_state::cdcc_ctrl_r), FUNC(pn8800fxb_state::cdcc_ctrl_w));
	map(0xd0, 0xdf).rw("rtc", FUNC(tc8521_device::read), FUNC(tc8521_device::write));
	map(0xe0, 0xe0).mirror(0x07).w(FUNC(pn8800fxb_state::bank_select_w));
	// e8-ef ga test
	map(0xf0, 0xf0).mirror(0x07).w(FUNC(pn8800fxb_state::buzzer_w));
	map(0xf8, 0xf8).mirror(0x07).w(FUNC(pn8800fxb_state::int_ack_w));
}


//**************************************************************************
//  INPUT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( pn8800fxb )
	PORT_START("misc")
	// 0x01 sp3
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(pn8800fxb_state::floppy_index_r))
	PORT_CONFNAME(0x04, 0x00, "AC Adaptor")
	PORT_CONFSETTING(   0x00, "Good")
	PORT_CONFSETTING(   0x04, "Not Good")
	// 0x08 sp1

	PORT_START("power")
	PORT_CONFNAME(0x01, 0x00, "Li Battery")
	PORT_CONFSETTING(   0x00, "Good")
	PORT_CONFSETTING(   0x01, "Not Good")
	PORT_CONFNAME(0x06, 0x00, "NiCd Battery")
	PORT_CONFSETTING(   0x00, "Good")
	PORT_CONFSETTING(   0x02, "Low")
	PORT_CONFSETTING(   0x04, "Not Good")

	PORT_START("KO0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)    PORT_CODE(KEYCODE_RSHIFT)      PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)      PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_NAME(u8"\u2190  EXPR") // ←
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)                   PORT_NAME("BS")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)     PORT_CHAR(13)                  PORT_NAME("RETURN  IND CLR  =")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)        PORT_CHAR(UCHAR_MAMEKEY(UP))   PORT_NAME(u8"\u2191  PRE S") // ↑
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)     PORT_CHAR(' ')

	PORT_START("KO1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[') PORT_CHAR(']') PORT_NAME(u8"[  ]  ½  ¼")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('=') PORT_CHAR('+') PORT_NAME("=  +  L OUT")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_CHAR('0') PORT_CHAR(')') PORT_NAME("0  )  W OUT")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-') PORT_CHAR('_') PORT_NAME("-  _  -")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P') PORT_NAME("p  P  PRINT  +")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/') PORT_CHAR('?') PORT_NAME("/  ?  /")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KO2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)  PORT_CHAR(UCHAR_MAMEKEY(DOWN))                 PORT_NAME(u8"\u2193  NEXT S") // ↓
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)     PORT_CHAR('l')  PORT_CHAR('L')                 PORT_NAME("l  L  L IND  3")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)     PORT_CHAR('9')  PORT_CHAR('(')                 PORT_NAME("9  (  T CLR  9")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)     PORT_CHAR('o')  PORT_CHAR('O')                 PORT_NAME("o  O  JUST  6")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)  PORT_CHAR('.')  PORT_CHAR('>')  PORT_CHAR(']') PORT_NAME(".  >  ]  .")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';')  PORT_CHAR(':')                 PORT_NAME(";  :  *")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))                PORT_NAME(u8"\u2192  RELOC") // →

	PORT_START("KO3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)     PORT_CHAR('k') PORT_CHAR('K')                PORT_NAME("k  K  KB  2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)     PORT_CHAR('8') PORT_CHAR('*')                PORT_NAME("8  *  DT SET  8")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)     PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)     PORT_CHAR('7') PORT_CHAR('&')                PORT_NAME("7  &  T SET  7")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)     PORT_CHAR('i') PORT_CHAR('I')                PORT_NAME("i  I  INSERT  5")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<') PORT_CHAR('[')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KO4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')                PORT_NAME("j  J  LAYOUT  1")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR('{') PORT_NAME("y  Y  {  <")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR('}') PORT_NAME("u  U  }  >")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')                PORT_NAME("h  H  HELP")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')                PORT_NAME("m  M  M CODE  0")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KO5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')                PORT_NAME("t  T  TEMP")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')                PORT_NAME("g  G  GOTO")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR(U'`')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KO6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)  PORT_CHAR(27)                 PORT_NAME("CANCEL")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)    PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)    PORT_CHAR('3') PORT_CHAR('#') PORT_NAME("3  #  M REL")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)    PORT_CHAR('5') PORT_CHAR('%') PORT_NAME("5  %  R MAR")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)    PORT_CHAR('4') PORT_CHAR('$') PORT_NAME("4  $  L MAR")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)    PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)    PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT)                               PORT_NAME("MENU  FILE")

	PORT_START("KO7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)   PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR('|')  PORT_NAME(u8"x  X  |  ²")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)   PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR(U'¢')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)                                  PORT_NAME("TAB  P IND")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)   PORT_CHAR('2') PORT_CHAR('@')                 PORT_NAME("2  @  LINE")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)   PORT_CHAR('s') PORT_CHAR('S')                 PORT_NAME("s  S  CALC")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KO8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)        PORT_CHAR('a')    PORT_CHAR('A')                 PORT_NAME("a  A  ABBR")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)        PORT_CHAR('q')    PORT_CHAR('Q')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)    PORT_CHAR(U'`')   PORT_CHAR('~')                 PORT_NAME(u8"`  ~  SPELL  ±  °")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)        PORT_CHAR('1')    PORT_CHAR('!')                 PORT_NAME("1  !  PITCH")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))               PORT_NAME("CAPS  SHIFT LOCK")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)        PORT_CHAR('z')    PORT_CHAR('Z') PORT_CHAR('\\') PORT_NAME(u8"z  Z  \\  §")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)                         PORT_NAME("CODE")
INPUT_PORTS_END

uint8_t pn8800fxb_state::keyboard_r()
{
	if (m_key_select < 9)
		return m_keys[m_key_select]->read();
	else
		logerror("keyboard_r from %02x\n", m_key_select);

	return 0xff;
}

void pn8800fxb_state::keyboard_w(uint8_t data)
{
	m_key_select = data;
}


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

void pn8800fxb_state::palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(86, 228, 94));
	palette.set_pen_color(1, rgb_t(58, 86, 143));
}

uint32_t pn8800fxb_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	m_gfxdecode->gfx(0)->mark_all_dirty();

	const pen_t *const pen = m_palette->pens();

	// 22 line text mode doesn't cover the full screen
	bitmap.fill(pen[0], cliprect);

	if (BIT(m_video_ctrl, 0))
	{
		if (BIT(m_video_ctrl, 3))
		{
			// graphics mode
			uint8_t *vram = m_vram.get();

			// left 512 pixels of screen
			for (int y = 0; y < 200; y++)
				for (int x = 0; x < 512; x += 8, vram++)
					for (int p = 0; p < 8; p++)
						bitmap.pix(y, x + p) = pen[BIT(*vram, 7 - p)];

			// right 128 pixels of screen
			for (int y = 0; y < 200; y++)
				for (int x = 0; x < 128; x += 8, vram++)
					for (int p = 0; p < 8; p++)
						bitmap.pix(y, 512 + x + p) = pen[BIT(*vram, 7 - p)];
		}
		else
		{
			// text mode
			int lines = BIT(m_video_ctrl, 5) ? 25 : 22;

			for (int y = 0; y < lines; y++)
			{
				for (int x = 0; x < 80; x++)
				{
					// attributes
					//
					// 7-------  unknown
					// -6------  subscript
					// --5-----  superscript
					// ---4----  invert
					// ----3---  vertical line
					// -----2--  code high bit
					// ------1-  overline
					// -------0  underline

					uint8_t attr = m_vram[y * 0x100 + x * 2 + 0];
					uint16_t code = m_vram[y * 0x100 + x * 2 + 1];

					// apply code high bit (high table contains bold characters)
					code |= (BIT(attr, 2) << 8) | code;

					bool cursor_active = (y * 0x100 + x * 2) == (m_cursor_addr << 1);

					// draw 8 or 9 lines
					for (int l = 0; l < 9; l++)
					{
						uint8_t data = 0x00;

						// skip empty line in 25 line mode
						if (lines == 25 && l == 0)
							continue;

						// character data in lines 1 to 8
						if (l > 0)
						{
							data = m_vram[0x3000 + (code << 3) + l - 1];

							if (cursor_active)
							{
								// cursor active for upper lines (5 to 8)?
								if (BIT(m_cursor_ctrl, 7) && l >= 5)
									data ^= 0xff;

								// cursor active for lower lines (1 to 4)?
								if (BIT(m_cursor_ctrl, 6) && l <= 4)
									data ^= 0xff;
							}
						}

						// underline?
						if (BIT(attr, 0) && l == 8)
							data = 0xff;

						// overline?
						if (BIT(attr, 1) && l == 0)
							data = 0xff;

						// vertical line?
						if (BIT(attr, 3))
							data |= 0x01;

						// invert?
						if (BIT(attr, 4))
							data ^= 0xff;

						// draw 8 pixels of the character
						for (int b = 0; b < 8; b++)
						{
							if (lines == 25)
								bitmap.pix(y * 8 + l - 1, x * 8 + b) = pen[BIT(data, 7 - b)];
							else
								bitmap.pix(y * 9 + l, x * 8 + b) = pen[BIT(data, 7 - b)];
						}
					}
				}
			}
		}
	}

	return 0;
}

void pn8800fxb_state::cursor_addr_lo_w(uint8_t data)
{
	m_cursor_addr = (m_cursor_addr & 0xff00) | (data << 0);
}

void pn8800fxb_state::cursor_addr_hi_w(uint8_t data)
{
	data &= 0x0f;
	m_cursor_addr = (data << 8) | (m_cursor_addr & 0x00ff);
}

void pn8800fxb_state::video_addr_lo_w(uint8_t data)
{
	m_video_addr = (m_video_addr & 0xff00) | (data << 0);
}

void pn8800fxb_state::video_addr_hi_w(uint8_t data)
{
	data &= 0x7f;
	m_video_addr = (data << 8) | (m_video_addr & 0x00ff);
}

uint8_t pn8800fxb_state::video_data_r()
{
	return m_vram[m_video_addr];
}

void pn8800fxb_state::video_data_w(uint8_t data)
{
	m_vram[m_video_addr] = data;

	// auto-increment, assume wrap
	m_video_addr = (m_video_addr + 1) & 0x7fff;
}

uint8_t pn8800fxb_state::video_ctrl_r()
{
	// 7-------  mm
	// -6------  not used
	// --5-----  8r (in text mode, 8 pixel height line instead of 9?)
	// ---4----  blp
	// ----3---  grph
	// -----2--  not used
	// ------1-  rev
	// -------0  disp

	return m_video_ctrl;
}

void pn8800fxb_state::video_ctrl_w(uint8_t data)
{
	if (0)
		logerror("video_ctrl_w: %02x\n", data);

	m_video_ctrl = data;
}

void pn8800fxb_state::video_offset_w(uint8_t data)
{
	// 765-----  not used
	// ---4----  oy
	// ----3210  ox6 to ox3

	m_video_offset = data;
}

void pn8800fxb_state::cursor_ctrl_w(uint8_t data)
{
	// 7-------  curu
	// -6------  curl
	// --5-----  cbkh
	// ---4----  crev
	// ----3---  curb
	// -----210  not used

	m_cursor_ctrl = data;
}

static const gfx_layout charlayout =
{
	8, 8,
	512,
	1,
	{ 0 },
	{ STEP8(0,1) },
	{ STEP16(0,8) },
	8*8
};

static GFXDECODE_START( gfx )
GFXDECODE_END


//**************************************************************************
//  AUDIO EMULATION
//**************************************************************************

void pn8800fxb_state::buzzer_w(uint8_t data)
{
	// 7-------  buzzer
	// -654321-  not used
	// -------0  4khzoff

	m_buzzer->set_state(BIT(data, 7));
}


//**************************************************************************
//  FLOPPY
//**************************************************************************

static void hd_floppy(device_slot_interface &device)
{
	device.option_add("35hd", FLOPPY_35_HD);
}

void pn8800fxb_state::fdc_mode_select_w(uint8_t data)
{
	// 7654321-  not used
	// -------0  fdc data rate

	m_fdc->rate_w(BIT(data, 0));
}

void pn8800fxb_state::fdc_drq_w(int state)
{
	m_fdc_drq = bool(state);
	m_maincpu->set_input_line(Z180_INPUT_LINE_DREQ1, state);
}

uint8_t pn8800fxb_state::fdc_data_r()
{
	if (m_fdc_drq)
		return m_fdc->dma_r();
	else
		return m_fdc->fifo_r();
}

void pn8800fxb_state::fdc_data_w(uint8_t data)
{
	if (m_fdc_drq)
		m_fdc->dma_w(data);
	else
		m_fdc->fifo_w(data);
}

int pn8800fxb_state::floppy_index_r()
{
	if (m_floppy->get_device())
		return m_floppy->get_device()->idx_r();

	return 0;
}

int pn8800fxb_state::floppy_dskchg_r()
{
	if (m_floppy->get_device())
		return m_floppy->get_device()->dskchg_r();

	return 0;
}


//**************************************************************************
//  CDCC / CENTRONICS
//**************************************************************************

uint8_t pn8800fxb_state::cdcc_ctrl_r()
{
	// 7-------  centronics busy
	// -6------  centronics select
	// --54321-  not used
	// -------0  centronics strobe (write)

	uint8_t data = 0x3f;

	data |= (!m_centronics_select) << 6;
	data |= (!m_centronics_busy) << 7;

	return data;
}

void pn8800fxb_state::cdcc_ctrl_w(uint8_t data)
{
	m_centronics->write_strobe(BIT(data, 0));
}


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void pn8800fxb_state::mem_change_w(uint8_t data)
{
	// 7-------  dua
	// -6------  ramin
	// --5432--  not used
	// ------1-  dicsel
	// -------0  rgex

	if ((data & 0xfe) != 0x40)
		logerror("mem_change_w: dua %d ramin %d dicsel %d rgex %d\n", BIT(data, 7), BIT(data, 6), BIT(data, 1), BIT(data, 0));

	m_mem_change = data;
	m_lomem_view.select(BIT(data, 0));
}

void pn8800fxb_state::bank_select_w(uint8_t data)
{
	// 7654----  not used
	// ----3210  bank selection

	if (BIT(data, 3))
		m_rombank->set_entry((data & 0x03) + 0); // code
	else
		m_rombank->set_entry((data & 0x07) + 4); // dictionary
}

void pn8800fxb_state::int_ack_w(uint8_t data)
{
	m_maincpu->set_input_line(INPUT_LINE_IRQ1, CLEAR_LINE);
}

TIMER_DEVICE_CALLBACK_MEMBER( pn8800fxb_state::int1_timer )
{
	m_maincpu->set_input_line(INPUT_LINE_IRQ1, ASSERT_LINE);
}

void pn8800fxb_state::machine_start()
{
	// allocate space for vram
	m_vram = std::make_unique<uint8_t[]>(0x4000);

	// init gfxdecode
	m_gfxdecode->set_gfx(0, std::make_unique<gfx_element>(m_palette, charlayout, m_vram.get() + 0x3000, 0, 1, 0));

	// configure rom banking (first 0x40000 fixed)
	m_rombank->configure_entries(0, 12, memregion("maincpu")->base() + 0x40000, 0x10000);

	// register for save states
	save_item(NAME(m_key_select));
	save_pointer(NAME(m_vram), 0x4000);
	save_item(NAME(m_cursor_addr));
	save_item(NAME(m_video_addr));
	save_item(NAME(m_video_ctrl));
	save_item(NAME(m_video_offset));
	save_item(NAME(m_cursor_ctrl));
	save_item(NAME(m_fdc_drq));
	save_item(NAME(m_centronics_busy));
	save_item(NAME(m_centronics_select));
	save_item(NAME(m_mem_change));
}

void pn8800fxb_state::machine_reset()
{
	m_fdc_drq = false;
	m_mem_change = 0x00;
	m_lomem_view.select(0);
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void pn8800fxb_state::pn8800fxb(machine_config &config)
{
	Z80180(config, m_maincpu, 12.288_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &pn8800fxb_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &pn8800fxb_state::io_map);
	m_maincpu->tend1_wr_callback().set(m_fdc, FUNC(hd63266f_device::tc_line_w));

	TIMER(config, "1khz").configure_periodic(FUNC(pn8800fxb_state::int1_timer), attotime::from_hz(1000));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_size(640, 200);
	screen.set_visarea_full();
	screen.set_refresh_hz(70.23);
	screen.set_screen_update(FUNC(pn8800fxb_state::screen_update));

	PALETTE(config, m_palette, FUNC(pn8800fxb_state::palette), 2);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_buzzer, 4'000).add_route(ALL_OUTPUTS, "mono", 1.0); // 4.0 kHz generated by the gate array

	// floppy
	HD63266F(config, m_fdc, 16_MHz_XTAL);
	m_fdc->set_ready_line_connected(false);
	m_fdc->drq_wr_callback().set(FUNC(pn8800fxb_state::fdc_drq_w));
	m_fdc->inp_rd_callback().set(FUNC(pn8800fxb_state::floppy_dskchg_r));
	FLOPPY_CONNECTOR(config, "fdc:0", hd_floppy, "35hd", floppy_image_device::default_pc_floppy_formats);

	// centronics
	output_latch_device &centronics_latch(OUTPUT_LATCH(config, "centronics_latch"));

	CENTRONICS(config, m_centronics, centronics_devices, nullptr);
	m_centronics->set_output_latch(centronics_latch);
	m_centronics->busy_handler().set([this](int state) { m_centronics_busy = bool(state); });
	m_centronics->select_handler().set([this](int state) { m_centronics_select = bool(state); });

	TC8521(config, "rtc", XTAL(32'768));
	// alarm output connected to auto power-on

	SOFTWARE_LIST(config, "floppy_list").set_original("brother_pn");
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( pn8800 )
	ROM_REGION(0x100000, "maincpu", 0)
	ROM_LOAD("uc8254-a-pn88.5", 0x000000, 0x100000, CRC(d9601c1a) SHA1(1699714befeaf2fe17232c1b4f49d4242f5367f4))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME    PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY     FULLNAME      FLAGS
COMP( 1996, pn8800, 0,      0,      pn8800fxb, pn8800fxb, pn8800fxb_state, empty_init, "Brother",  "PN-8800FXB", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )



pockchal.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/*********************************************************************

Similar to https://www.youtube.com/watch?v=FmyR-kL-QWo

base unit contains

1x Toshiba TMP90C845AF

1x SANYO LC21003 BLA5

3x SEC C941A KS0108B

1x Toshiba T9842B

(system has no bios ROM)

Cart sizes: 1MB, 2MB, 4MB

********************************************************************/

#include "emu.h"

#include "cpu/tlcs90/tlcs90.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"


namespace {

class pockchalv1_state : public driver_device
{
public:
	pockchalv1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cart(*this, "cartslot")
	{ }

	void pockchalv1(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

private:
	uint32_t screen_update_pockchalv1(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
	void pockchalv1_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<generic_slot_device> m_cart;
	uint32_t m_rom_size = 0;
};


DEVICE_IMAGE_LOAD_MEMBER( pockchalv1_state::cart_load )
{
	m_rom_size = m_cart->common_get_size("rom");
	m_cart->rom_alloc(m_rom_size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), m_rom_size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

void pockchalv1_state::video_start()
{
}

uint32_t pockchalv1_state::screen_update_pockchalv1(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}


void pockchalv1_state::pockchalv1_map(address_map &map)
{
	map(0xc000, 0xffff).ram();
}


static INPUT_PORTS_START( pockchalv1 )
INPUT_PORTS_END



void pockchalv1_state::machine_start()
{
	address_space &space = m_maincpu->space(AS_PROGRAM);
	if (m_cart->exists())
		space.install_read_handler(0x0000, 0x7fff, read8sm_delegate(*m_cart, FUNC(generic_slot_device::read_rom)));
}

void pockchalv1_state::machine_reset()
{
}


void pockchalv1_state::pockchalv1(machine_config &config)
{
	/* basic machine hardware */
	TMP90845(config, m_maincpu, 8000000);         /* ? MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &pockchalv1_state::pockchalv1_map);
//  m_maincpu->->set_vblank_int("screen", FUNC(pockchalv1_state::irq0_line_hold));

	// wrong, it's a b&w / greyscale thing
	PALETTE(config, "palette").set_format(palette_device::xRGB_444, 0x100).set_endianness(ENDIANNESS_BIG);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(0));
	screen.set_size(256, 256);
	screen.set_visarea(0, 256-1, 16, 256-16-1);
	screen.set_screen_update(FUNC(pockchalv1_state::screen_update_pockchalv1));
	screen.set_palette("palette");

	generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "pockchalw_cart", "bin"));
	cartslot.set_device_load(FUNC(pockchalv1_state::cart_load));
	cartslot.set_must_be_loaded(true);

	SOFTWARE_LIST(config, "pc1_list").set_compatible("pockchalw");
}



ROM_START( pockchal )
ROM_END

} // Anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE     INPUT       CLASS             INIT        COMPANY                FULLNAME                      FLAGS
CONS( 199?, pockchal, 0,      0,      pockchalv1, pockchalv1, pockchalv1_state, empty_init, "Benesse Corporation", "Pocket Challenge W (Japan)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



pocketc.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Peter Trauner
/******************************************************************************
    Sharp pocket computers
    PC1401/PC1403
    PeT mess@utanet.at May 2000

    pc1403 and pc1403h can be convinced to work if you hit Enter once or twice
    as soon as it is started.

******************************************************************************/

#include "emu.h"
#include "pocketc.h"
#include "pc1401.h"
#include "pc1251.h"
#include "pc1350.h"
#include "pc1403.h"

#include "machine/ram.h"

/* PC1430 lacks peek/poke operations */

/* PC1280?? */

/* PC126x
   port
   1 ?
   2 +6v
   3 gnd
   4 f0
   5 f1
   6 load
   7 save
   8 ib7
   9 ib6
  10 ib5
  11 ib4 */

/* PC1350 other keyboard,
   f2 instead f0 at port
   port
   1 ?
   2 +6v
   3 gnd
   4 f2
   5 f1
   6 load
   7 save
   8 ib7
   9 ib6
  10 ib5
  11 ib4
*/

/* Similar Computers:
   - PC1260/1261
   - PC1402/1403
   - PC1421 */
/* PC140x
   a,b0..b5 keyboard matrix
   b0 off key
   c0 display on
   c1 counter reset
   c2 cpu halt
   c3 computer off
   c4 beeper frequency (1 4kHz, 0 2kHz), or (c5=0) membrane pos1/pos2
   c5 beeper on
   c6 beeper control

   port
   1 ?
   2 +6v
   3 gnd
   4 f0
   5 f1
   6 load
   7 save
   8 ib7
   9 ib6
  10 ?
  11 ?
*/

/* Special Leys Red C-CE and Reset; warm boot, program NOT lost */

void pc1401_state::pc1401_mem(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x3800, 0x47ff).ram();
	map(0x6000, 0x67ff).rw(FUNC(pc1401_state::lcd_read), FUNC(pc1401_state::lcd_write)).mirror(0x1000);
	map(0x8000, 0xffff).rom();
}

void pc1401_state::pc1402_mem(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x2000, 0x47ff).ram();
	map(0x6000, 0x67ff).rw(FUNC(pc1401_state::lcd_read), FUNC(pc1401_state::lcd_write)).mirror(0x1000);
	map(0x8000, 0xffff).rom();
}

void pc1251_state::pc1250_mem(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x4000, 0x7fff).rom();
	map(0xc000, 0xc7ff).ram(); // 2KB RAM
	map(0xf800, 0xf8ff).rw(FUNC(pc1251_state::lcd_read), FUNC(pc1251_state::lcd_write));
}

void pc1251_state::pc1251_mem(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x4000, 0x7fff).rom();
	map(0xb800, 0xc7ff).ram(); // 4KB RAM
	map(0xf800, 0xf8ff).rw(FUNC(pc1251_state::lcd_read), FUNC(pc1251_state::lcd_write));
}

void pc1251_state::pc1255_mem(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x4000, 0x7fff).rom();
	map(0xa000, 0xc7ff).ram(); // 10KB RAM
	map(0xf800, 0xf8ff).rw(FUNC(pc1251_state::lcd_read), FUNC(pc1251_state::lcd_write));
}

void pc1251_state::pc1260_mem(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x2000, 0x20ff).rw(FUNC(pc1251_state::lcd_read), FUNC(pc1251_state::lcd_write));
	map(0x5800, 0x67ff).ram(); // 4KB RAM
	map(0x8000, 0xffff).rom();
}

void pc1251_state::pc1261_mem(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x2000, 0x20ff).rw(FUNC(pc1251_state::lcd_read), FUNC(pc1251_state::lcd_write));
	map(0x4000, 0x67ff).ram(); // 10KB RAM
	map(0x8000, 0xffff).rom();
}

void pc1350_state::pc1350_mem(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x7000, 0x7eff).rw(FUNC(pc1350_state::lcd_read), FUNC(pc1350_state::lcd_write));
	map(0x8000, 0xffff).rom();
}

void pc1403_state::pc1403_mem(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x3000, 0x30bf).rw(FUNC(pc1403_state::lcd_read), FUNC(pc1403_state::lcd_write));
	map(0x3800, 0x3fff).rw(FUNC(pc1403_state::asic_read), FUNC(pc1403_state::asic_write));
	map(0x4000, 0x7fff).bankr("bank1");
	map(0xe000, 0xffff).ram();
}

void pc1403_state::pc1403h_mem(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x3000, 0x30bf).rw(FUNC(pc1403_state::lcd_read), FUNC(pc1403_state::lcd_write));
	map(0x3800, 0x3fff).rw(FUNC(pc1403_state::asic_read), FUNC(pc1403_state::asic_write));
	map(0x4000, 0x7fff).bankr("bank1");
	map(0x8000, 0xffff).ram();
}

#if 0
void pc1403_state::pc1421_readmem(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x3800, 0x47ff).ram();
	map(0x8000, 0xffff).rom();
}

void pc1403_state::pc1421_writemem(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x2000, 0x37ff).ram();
	map(0x3800, 0x47ff).ram();
	map(0x8000, 0xffff).rom();
}
#endif


/* 2008-05 FP: the following input ports are based on the way ports are read, but I would like
   to have confirmation from technical docs before considering these correct.
   If they need to be changed, you must also update the clickable artwork.
*/

static INPUT_PORTS_START( pc1401 )
	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+/-   y'") PORT_CODE(KEYCODE_NUMLOCK)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8     Sy") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2     a") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5     Sx") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CAL") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q     !") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A     INPUT") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z     PRINT") PORT_CODE(KEYCODE_Z)

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(".     DRG") PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9     sigmay") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3     b") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6     sigmax") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BASIC") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W     \"") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S     IF") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X     USING") PORT_CODE(KEYCODE_X)

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+     ^") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("-     >") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("*     <") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DEF") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E     #") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D     THEN") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C     GOSUB") PORT_CODE(KEYCODE_C)

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(")     n!") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(     ->xy") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("sqr   tri%   D")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("sqrt  3root  C")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("y^x   xROOTy B")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("EXP   Pi     A")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X->M  Sum y  Sum y^2")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("=") PORT_CODE(KEYCODE_ENTER_PAD)

	PORT_START("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("chnge STAT")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1/x   ->r theta")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LOG   10^x   F")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LN    e^x    E")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("->DEG ->D.MS")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("->HEX ->DEC")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M+    DATA   CD")

	PORT_START("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C-CE  CA") PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F<>E  TAB")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TAN   TAN^-1")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("COS   COS^-1")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SIN   SIN^-1")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("HYP   ARCHYP")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RM    (x,y)")

	PORT_START("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7     y mean") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1     r") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4     x mean") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("down") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R     $") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F     GOTO") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V     RETURN") PORT_CODE(KEYCODE_V)

	PORT_START("KEY7")
	PORT_BIT(0x03, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(",     RUN") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P     ;") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("up") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T     %") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G     FOR") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B     DIM") PORT_CODE(KEYCODE_B)

	PORT_START("KEY8")
	PORT_BIT(0x07, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O     :") PORT_CODE(KEYCODE_O)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("left  DEL") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y     &") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H     TO") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N     END") PORT_CODE(KEYCODE_N)

	PORT_START("KEY9")
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("right INS") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U     ?") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J     STEP") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M     CSAVE") PORT_CODE(KEYCODE_M)

	PORT_START("KEY10")
	PORT_BIT(0x1f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I     @") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K     NEXT") PORT_CODE(KEYCODE_K)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SPC   CLOAD") PORT_CODE(KEYCODE_SPACE)

	PORT_START("KEY11")
	PORT_BIT(0x3f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L     LIST") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENTER N<>NP") PORT_CODE(KEYCODE_ENTER)

	PORT_START("KEY12")
	PORT_BIT(0x7f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0     x'") PORT_CODE(KEYCODE_0_PAD)

	PORT_START("EXTRA")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BRK   ON") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_F3)
	PORT_DIPNAME( 0x04, 0x00,  "Power")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("DSW0")
	PORT_DIPNAME( 0x07, 0x01, "Contrast")
	PORT_DIPSETTING(    0x00, "0/Low" )
	PORT_DIPSETTING(    0x01, "1" )
	PORT_DIPSETTING(    0x02, "2" )
	PORT_DIPSETTING(    0x03, "3" )
	PORT_DIPSETTING(    0x04, "4" )
	PORT_DIPSETTING(    0x05, "5" )
	PORT_DIPSETTING(    0x06, "6" )
	PORT_DIPSETTING(    0x07, "7/High" )
INPUT_PORTS_END

static INPUT_PORTS_START( pc1403 )
	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7     y mean") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8     Sy") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9     sigmay") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X->M  Sum y  Sum y^2")
	PORT_BIT(0xe0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4     x mean") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5     Sx") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6     sigmax") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("*     <") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RM    (x,y)")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DEF") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SML") PORT_CODE(KEYCODE_CAPSLOCK)

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1     r") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2     a") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3     b") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("-     >") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M+    DATA   CD")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q     !") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A     INPUT") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z     PRINT") PORT_CODE(KEYCODE_Z)

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0     x'") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+/-   y'") PORT_CODE(KEYCODE_NUMLOCK)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(".     DRG") PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+     ^") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("=") PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W     \"") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S     IF") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X     USING") PORT_CODE(KEYCODE_X)

	PORT_START("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("HYP   ARCHYP")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SIN   SIN^-1")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("COS   COS^-1")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TAN   TAN^-1")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)      /* toggles indicator 3c bit 0 japan? */
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E     #") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D     THEN") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C     GOSUB") PORT_CODE(KEYCODE_C)

	PORT_START("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("->HEX ->DEC")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("->DEG ->D.MS")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LN    e^x    E")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LOG   10^x   F")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)      /* tilde? */
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R     $") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F     GOTO") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V     RETURN") PORT_CODE(KEYCODE_V)

	PORT_START("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("EXP   Pi     A")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("y^x   xROOTy B")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("sqrt  3root  C")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("sqr   tri%   D")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)      /* yen? */
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T     %") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G     FOR") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B     DIM") PORT_CODE(KEYCODE_B)

	PORT_START("KEY7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C-CE  CA") PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("chnge STAT")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F<>E  TAB")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y     &") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H     TO") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N     END") PORT_CODE(KEYCODE_N)

	PORT_START("KEY8")
	PORT_BIT(0x03, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(")     n!") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1/x   ->r theta")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U     ?") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J     STEP") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M     CSAVE") PORT_CODE(KEYCODE_M)

	PORT_START("KEY9")
	PORT_BIT(0x07, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(     ->x y") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left  DEL") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I     @") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K     NEXT") PORT_CODE(KEYCODE_K)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SPC   CLOAD") PORT_CODE(KEYCODE_SPACE)

	PORT_START("KEY10")
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right INS") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O     :") PORT_CODE(KEYCODE_O)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L     LIST") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENTER N<>NP") PORT_CODE(KEYCODE_ENTER)

	PORT_START("KEY11")
	PORT_BIT(0x1f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P     ;") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(",     RUN") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BASIC") PORT_CODE(KEYCODE_F2)

	PORT_START("KEY12")
	PORT_BIT(0x3f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)      /* shift lock */
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CAL") PORT_CODE(KEYCODE_F1)

	PORT_START("KEY13")
	PORT_BIT(0x7f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_DIPNAME( 0x80, 0x00,  "Power")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("EXTRA")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BRK   ON") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_F3)

	PORT_START("DSW0")
	// normally no contrast control!
	PORT_DIPNAME( 0x07, 0x01, "Contrast")
	PORT_DIPSETTING(    0x00, "0/Low" )
	PORT_DIPSETTING(    0x01, "1" )
	PORT_DIPSETTING(    0x02, "2" )
	PORT_DIPSETTING(    0x03, "3" )
	PORT_DIPSETTING(    0x04, "4" )
	PORT_DIPSETTING(    0x05, "5" )
	PORT_DIPSETTING(    0x06, "6" )
	PORT_DIPSETTING(    0x07, "7/High" )
INPUT_PORTS_END

static INPUT_PORTS_START( pc1251 )
	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("-     >") PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CL    CA") PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("*     <") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/     ^") PORT_CODE(KEYCODE_SLASH) PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DOWN  (") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E     #") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+     Exp") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3     @") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W     \"") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(".     SquareRoot") PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DEF") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q     !") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UP    )") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R     $") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)

	PORT_START("KEY4")
	PORT_BIT(0x03, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("=") PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P     ;") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LEFT  DEL") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T     %") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)

	PORT_START("KEY5")
	PORT_BIT(0x07, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O     ,") PORT_CODE(KEYCODE_O)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RIGHT INS") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y     &") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)

	PORT_START("KEY6")
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)  /* down? */
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U     ?") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)

	PORT_START("KEY7")
	PORT_BIT(0x1f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I     :") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("      SPC") PORT_CODE(KEYCODE_SPACE)

	PORT_START("KEY8")
	PORT_BIT(0x3f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENTER N<>NP") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)

	PORT_START("KEY9")
	PORT_BIT(0x7f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0     Pi") PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD)

	PORT_START("EXTRA")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BRK   ON") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_F3)

	PORT_START("MODE")
	PORT_DIPNAME( 0x07, 0x00, "Mode")
	PORT_DIPSETTING(    0x04, DEF_STR(Off) )
	PORT_DIPSETTING(    0x00, "On/RUN" )
	PORT_DIPSETTING(    0x02, "On/PRO" )
	PORT_DIPSETTING(    0x01, "On/RSV" )

	PORT_START("DSW0")
	PORT_DIPNAME( 0x07, 0x01, "Contrast")
	PORT_DIPSETTING(    0x00, "0/Low" )
	PORT_DIPSETTING(    0x01, "1" )
	PORT_DIPSETTING(    0x02, "2" )
	PORT_DIPSETTING(    0x03, "3" )
	PORT_DIPSETTING(    0x04, "4" )
	PORT_DIPSETTING(    0x05, "5" )
	PORT_DIPSETTING(    0x06, "6" )
	PORT_DIPSETTING(    0x07, "7/High" )
INPUT_PORTS_END

static INPUT_PORTS_START( pc1350 )
	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(")     >") PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SML") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DEF") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_RSHIFT)   /* are both Shifts connected here? or is Left Shift missing */

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(     <") PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH) PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("*") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("-     ^") PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q     !") PORT_CODE(KEYCODE_Q)

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W     \"") PORT_CODE(KEYCODE_W)

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E     #") PORT_CODE(KEYCODE_E)

	PORT_START("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R     $") PORT_CODE(KEYCODE_R)

	PORT_START("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UP") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DOWN") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LEFT") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RIGHT") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T     %") PORT_CODE(KEYCODE_T)

	PORT_START("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)      /* 1 ?*/
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("INS") PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y     &") PORT_CODE(KEYCODE_Y)

	PORT_START("KEY7")
	PORT_BIT(0x03, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)      /* 2 ?*/
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MODE") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U     ?") PORT_CODE(KEYCODE_U)

	PORT_START("KEY8")
	PORT_BIT(0x07, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CLS   CA") PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SPC") PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I     Pi") PORT_CODE(KEYCODE_I)

	PORT_START("KEY9")
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENTER P<->NP") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O     Squareroot") PORT_CODE(KEYCODE_O)

	PORT_START("KEY10")
	PORT_BIT(0x1f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("=") PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P     Alpha") PORT_CODE(KEYCODE_P)

	PORT_START("KEY11")
	PORT_DIPNAME( 0xc0, 0x00, "Power")
	PORT_DIPSETTING(    0xc0, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("EXTRA")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BRK   ON") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) // temporarily here, but not read...

	PORT_START("DSW0")
	PORT_DIPNAME( 0x07, 0x07, "Contrast")
	PORT_DIPSETTING(    0x00, "0/Low" )
	PORT_DIPSETTING(    0x01, "1" )
	PORT_DIPSETTING(    0x02, "2" )
	PORT_DIPSETTING(    0x03, "3" )
	PORT_DIPSETTING(    0x04, "4" )
	PORT_DIPSETTING(    0x05, "5" )
	PORT_DIPSETTING(    0x06, "6" )
	PORT_DIPSETTING(    0x07, "7/High" )
INPUT_PORTS_END


static const gfx_layout pc1401_charlayout =
{
	2,21,
	128,                                    /* 256 characters */
	1,                      /* 1 bits per pixel */
	{ 0,0 },                  /* no bitplanes; 1 bit per pixel */
	/* x offsets */
	{ 0,0 },
	/* y offsets */
	{
		7, 7, 7,
		6, 6, 6,
		5, 5, 5,
		4, 4, 4,
		3, 3, 3,
		2, 2, 2,
		1, 1, 1
	},
	1*8
};

static const gfx_layout pc1251_charlayout =
{
	3,21,
	128,                                    /* 256 characters */
	1,                      /* 1 bits per pixel */
	{ 0, },                  /* no bitplanes; 1 bit per pixel */
	/* x offsets */
	{ 0,0,0 },
	/* y offsets */
	{
		7, 7, 7,
		6, 6, 6,
		5, 5, 5,
		4, 4, 4,
		3, 3, 3,
		2, 2, 2,
		1, 1, 1
	},
	1*8
};

static GFXDECODE_START( gfx_pc1401 )
	GFXDECODE_ENTRY( "gfx1", 0x0000, pc1401_charlayout, 0, 8 )
GFXDECODE_END

static GFXDECODE_START( gfx_pc1251 )
	GFXDECODE_ENTRY( "gfx1", 0x0000, pc1251_charlayout, 0, 8 )
GFXDECODE_END

void pocketc_state::pocketc_base(machine_config &config)
{
	config.set_maximum_quantum(attotime::from_hz(60));

	NVRAM(config, "cpu_nvram", nvram_device::DEFAULT_ALL_0);
	NVRAM(config, "ram_nvram", nvram_device::DEFAULT_ALL_0);

	// TODO: Convert to an SVG
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(20);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_size(594, 273);
	m_screen->set_visarea(0, 594-1, 0, 273-1);
	m_screen->set_palette(m_palette);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_pc1401);
	PALETTE(config, m_palette, FUNC(pocketc_state::pocketc_palette), 8*2, 6);
}

void pc1401_state::pc1401(machine_config &config)
{
	pocketc_base(config);
	SC61860(config, m_maincpu, 192000);        /* 7.8336 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &pc1401_state::pc1401_mem);
	m_maincpu->reset_cb().set(FUNC(pc1401_state::reset_r));
	m_maincpu->brk_cb().set(FUNC(pc1401_state::brk_r));
	m_maincpu->x_cb().set_constant(0);
	m_maincpu->in_a_cb().set(FUNC(pc1401_state::in_a_r));
	m_maincpu->out_a_cb().set(FUNC(pc1401_state::out_a_w));
	m_maincpu->in_b_cb().set(FUNC(pc1401_state::in_b_r));
	m_maincpu->out_b_cb().set(FUNC(pc1401_state::out_b_w));
	m_maincpu->out_c_cb().set(FUNC(pc1401_state::out_c_w));

	m_screen->set_screen_update(FUNC(pc1401_state::screen_update));
}

void pc1401_state::pc1402(machine_config &config)
{
	pc1401(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc1401_state::pc1402_mem);
}

void pc1251_state::pc1250(machine_config &config)
{
	pocketc_base(config);
	SC61860(config, m_maincpu, 192000);        /* 7.8336 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &pc1251_state::pc1250_mem);
	m_maincpu->reset_cb().set_constant(0);
	m_maincpu->brk_cb().set(FUNC(pc1251_state::brk_r));
	m_maincpu->x_cb().set_constant(0);
	m_maincpu->in_a_cb().set(FUNC(pc1251_state::in_a_r));
	m_maincpu->out_a_cb().set(FUNC(pc1251_state::out_a_w));
	m_maincpu->in_b_cb().set(FUNC(pc1251_state::in_b_r));
	m_maincpu->out_b_cb().set(FUNC(pc1251_state::out_b_w));
	m_maincpu->out_c_cb().set(FUNC(pc1251_state::out_c_w));

	/* video hardware */
	m_screen->set_size(608, 300);
	m_screen->set_visarea(0, 608-1, 0, 300-1);
	m_screen->set_screen_update(FUNC(pc1251_state::screen_update));
	m_gfxdecode->set_info(gfx_pc1251);
}

void pc1251_state::pc1251(machine_config &config)
{
	pc1250(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc1251_state::pc1251_mem);
}

void pc1251_state::pc1255(machine_config &config)
{
	pc1250(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc1251_state::pc1255_mem);
}

void pc1260_state::pc1260(machine_config &config)
{
	pc1250(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc1260_state::pc1260_mem);
}

void pc1260_state::pc1261(machine_config &config)
{
	pc1260(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc1260_state::pc1261_mem);
}

void pc1350_state::pc1350(machine_config &config)
{
	pocketc_base(config);
	SC61860(config, m_maincpu, 192000);        /* 7.8336 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &pc1350_state::pc1350_mem);
	m_maincpu->reset_cb().set_constant(0);
	m_maincpu->brk_cb().set(FUNC(pc1350_state::brk_r));
	m_maincpu->x_cb().set_constant(0);
	m_maincpu->in_a_cb().set(FUNC(pc1350_state::in_a_r));
	m_maincpu->out_a_cb().set(FUNC(pc1350_state::out_a_w));
	m_maincpu->in_b_cb().set(FUNC(pc1350_state::in_b_r));
	m_maincpu->out_b_cb().set(FUNC(pc1350_state::out_b_w));
	m_maincpu->out_c_cb().set(FUNC(pc1350_state::out_c_w));

	/* video hardware */
	m_screen->set_size(640, 252);
	m_screen->set_visarea(0, 640-1, 0, 252-1);
	m_screen->set_screen_update(FUNC(pc1350_state::screen_update));

	/* internal ram */
	RAM(config, m_ram).set_default_size("4K").set_extra_options("12K,20K");
}

void pc1403_state::pc1403(machine_config &config)
{
	pocketc_base(config);
	SC61860(config, m_maincpu, 256000);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc1403_state::pc1403_mem);
	m_maincpu->reset_cb().set_constant(0);
	m_maincpu->brk_cb().set(FUNC(pc1403_state::brk_r));
	m_maincpu->x_cb().set_constant(0);
	m_maincpu->in_a_cb().set(FUNC(pc1403_state::in_a_r));
	m_maincpu->out_a_cb().set(FUNC(pc1403_state::out_a_w));
	m_maincpu->in_b_cb().set_constant(0);
	m_maincpu->out_b_cb().set_nop();
	m_maincpu->out_c_cb().set(FUNC(pc1403_state::out_c_w));

	/* video hardware */
	m_screen->set_size(848, 320);
	m_screen->set_visarea(0, 848-1, 0, 320-1);
	m_screen->set_screen_update(FUNC(pc1403_state::screen_update));
}

void pc1403_state::pc1403h(machine_config &config)
{
	pc1403(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &pc1403_state::pc1403h_mem);
}


ROM_START(pc1401)
	ROM_REGION(0x10000,"maincpu",0)
	/* SC61860A08 5H 13LD cpu with integrated rom*/
	ROM_LOAD("sc61860.a08", 0x0000, 0x2000, CRC(44bee438) SHA1(c5106bc8d848be1b49494ace30a26eeb1cc5e504))
/* 5S1 SC613256 D30
   or SC43536LD 5G 13 (LCD chip?) */
	ROM_LOAD("sc613256.d30", 0x8000, 0x8000, CRC(69b9d587) SHA1(fa0602e7dfee548546f801fb4cca7d73da2d8f18))
	ROM_REGION(0x80,"gfx1",ROMREGION_ERASEFF)
ROM_END

#define rom_pc1402 rom_pc1401

ROM_START(pc1245)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD( "cpu1245.rom", 0x0000, 0x2000, CRC(e0964069) SHA1(293c57b233d55944b308191fd72ecff81979cda7))
	ROM_LOAD( "bas1245.rom", 0x4000, 0x4000, CRC(f6253a0d) SHA1(bcac9b4f5a88eb952e81b3ee28f922c06bace18e))
	ROM_REGION(0x80,"gfx1",ROMREGION_ERASEFF)
ROM_END

ROM_START(pc1250)
	ROM_REGION(0x10000,"maincpu",0)
	/* sc61860a13 6c 13 ld */
	ROM_LOAD("cpu1250.rom", 0x0000, 0x2000, CRC(f7287aca) SHA1(19bfa778e3e05ea06bdca15cd9dfbba9b971340e))
	ROM_LOAD("bas1250.rom", 0x4000, 0x4000, CRC(93ecb629) SHA1(0fe0ad419053ee7814600b0be320dd2e8eb2ec92))
	ROM_REGION(0x80,"gfx1",ROMREGION_ERASEFF)
ROM_END

#define rom_pc1251 rom_pc1250
#define rom_pc1255 rom_pc1250
#define rom_trs80pc3 rom_pc1250


ROM_START(pc1260)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD( "cpu1260.rom", 0x0000, 0x2000, CRC(f46d23d3) SHA1(e00c9194570048185ec8358732adeba151c56b33))
	ROM_LOAD( "bas1260.rom", 0x8000, 0x8000, CRC(6c7e017d) SHA1(e2ae717438cea59416b0670e2a53989c147fb362))
	ROM_REGION(0x80,"gfx1",ROMREGION_ERASEFF)
ROM_END

#define rom_pc1261 rom_pc1260

ROM_START(pc1350)
	ROM_REGION(0x10000,"maincpu",0)
	/* sc61860a13 6c 13 ld */
	ROM_LOAD("cpu.rom", 0x0000, 0x2000, CRC(79a924bc) SHA1(2eaef0d53d85863ca70a41c8e1eddc5915136b99))
	ROM_LOAD("basic.rom", 0x8000, 0x8000, CRC(158b28e2) SHA1(b63b37dd510b3c4d9f16d224f87ae2efb3bcc51f))
	ROM_REGION(0x100,"gfx1",ROMREGION_ERASEFF)
ROM_END

ROM_START( pc1360 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "cpu-1360.rom", 0x0000, 0x2000, CRC(8fc9bf75) SHA1(f803cfecb8179f47031901c4d5bd546ac93118b1))
	ROM_REGION(0x20000,"user1",0)
	ROM_LOAD( "b0-1360.rom", 0x00000, 0x4000, CRC(afe1d3d6) SHA1(7178fd30dcc7f2777f0539897ef7245832f26152))
	ROM_LOAD( "b1-1360.rom", 0x04000, 0x4000, CRC(83f7e4dd) SHA1(ae6d133fdb586df8660dd9315c1fdf80b7b8dd57))
	ROM_LOAD( "b2-1360.rom", 0x08000, 0x4000, CRC(007cf5a6) SHA1(311d3ceaaed61479bdb00d8ade358fd6a99da170))
	ROM_LOAD( "b3-1360.rom", 0x0c000, 0x4000, CRC(4170849f) SHA1(70eeaa29c008131137bd28e4e957694cdf312413))
	ROM_LOAD( "b4-1360.rom", 0x10000, 0x4000, CRC(0d311e21) SHA1(55987b7b00ddc666db30f35cbf23500b49e7ff09))
	ROM_LOAD( "b5-1360.rom", 0x14000, 0x4000, CRC(f945f3f7) SHA1(26116d8277212e14bddb64c531f134fbb5c86f9e))
	ROM_LOAD( "b6-1360.rom", 0x18000, 0x4000, CRC(ae823112) SHA1(9ab458e70752cacfd5d3ed36a8e89f96c63a6f50))
	ROM_LOAD( "b7-1360.rom", 0x1c000, 0x4000, CRC(ba7384b6) SHA1(95396d506f1d71e66c3ae2d47ffb4b6d10b31401))
	ROM_REGION(0x100,"gfx1",ROMREGION_ERASEFF)
ROM_END

ROM_START(pc1403)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("introm.bin", 0x0000, 0x2000, CRC(588c500b) SHA1(2fed9ebede27e20a8ee4b4b03b9f8cd7808ada5c))
	ROM_REGION(0x10000,"user1",0)
	ROM_LOAD("extrom08.bin", 0x0000, 0x4000, CRC(1fa65140) SHA1(f22a9f114486f69733fc43dfec26fb210643aeff))
	ROM_LOAD("extrom09.bin", 0x4000, 0x4000, CRC(4a7da6ab) SHA1(b50fe8a4ca821244c119147b3ff04cee0fd6ad5c))
	ROM_LOAD("extrom0a.bin", 0x8000, 0x4000, CRC(9925174f) SHA1(793a79142cd170ed7ac3f7ecb1b6e6f92c8fa4e0))
	ROM_LOAD("extrom0b.bin", 0xc000, 0x4000, CRC(fa5df9ec) SHA1(6ff62c215f510a3a652d61823f54cd4018d6a771))
	ROM_REGION(0x100,"gfx1",ROMREGION_ERASEFF)
ROM_END

#define rom_pc1403h rom_pc1403

// disk drive support
#define io_pc1403 io_pc1401
#define io_pc1403h io_pc1403

/* ROM definition */
ROM_START( pc1450 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "cpu-1450.rom", 0x0000, 0x2000, CRC(dead7be5) SHA1(f55ba8cb823eb16b514032e96d2068d028964c1f))
	ROM_LOAD( "bas-1450.rom", 0x8000, 0x8000, CRC(d207cae9) SHA1(a88ef79bc38ea264165f800e4e386050d4d461b2))
	ROM_REGION(0x100,"gfx1",ROMREGION_ERASEFF)
ROM_END

/*    YEAR  NAME      PARENT    MACHINE   INPUT     INIT      MONITOR   COMPANY   FULLNAME */

/* cpu sc43177, sc43178 (4bit!)
   pc 1211
   clone tandy trs80 pocket computer
   pc1246/pc1247
*/

/* cpu lh5801
   pc1500
   clone tandy pc2 look into drivers/pc1500.c
   pc1600
*/

//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT         COMPANY  FULLNAME                     FLAGS
// cpu sc61860
COMP( 1982, pc1245,   0,      0,      pc1250,  pc1251, pc1251_state, empty_init, "Sharp", "Pocket Computer 1245",      MACHINE_NOT_WORKING  | MACHINE_NO_SOUND )
COMP( 1982, pc1250,   0,      0,      pc1250,  pc1251, pc1251_state, empty_init, "Sharp", "Pocket Computer 1250",      MACHINE_NO_SOUND)
COMP( 1982, pc1251,   pc1250, 0,      pc1251,  pc1251, pc1251_state, empty_init, "Sharp", "Pocket Computer 1251",      MACHINE_NO_SOUND)
COMP( 1982, pc1255,   pc1250, 0,      pc1255,  pc1251, pc1251_state, empty_init, "Sharp", "Pocket Computer 1255",      MACHINE_NO_SOUND)
COMP( 1983, trs80pc3, pc1250, 0,      pc1251,  pc1251, pc1251_state, empty_init, "Tandy Radio Shack", "TRS-80 Pocket Computer PC-3", MACHINE_NO_SOUND)

COMP( 1982, pc1260,   0,      0,      pc1260,  pc1251, pc1260_state, empty_init, "Sharp", "Pocket Computer 1260",      MACHINE_NOT_WORKING  | MACHINE_NO_SOUND )
COMP( 1982, pc1261,   pc1260, 0,      pc1261,  pc1251, pc1260_state, empty_init, "Sharp", "Pocket Computer 1261/1262", MACHINE_NOT_WORKING  | MACHINE_NO_SOUND)

// pc1261/pc1262
COMP( 1984, pc1350,   0,      0,      pc1350,  pc1350, pc1350_state, empty_init, "Sharp", "Pocket Computer 1350",      MACHINE_NO_SOUND )
COMP( 198?, pc1450,   0,      0,      pc1350,  pc1350, pc1350_state, empty_init, "Sharp", "Pocket Computer 1450",      MACHINE_NOT_WORKING | MACHINE_NO_SOUND )

COMP( 1983, pc1401,   0,      0,      pc1401,  pc1401, pc1401_state, empty_init, "Sharp", "Pocket Computer 1401",      MACHINE_NO_SOUND)
COMP( 1984, pc1402,   pc1401, 0,      pc1402,  pc1401, pc1401_state, empty_init, "Sharp", "Pocket Computer 1402",      MACHINE_NO_SOUND)
COMP( 198?, pc1360,   pc1401, 0,      pc1401,  pc1401, pc1401_state, empty_init, "Sharp", "Pocket Computer 1360",      MACHINE_NOT_WORKING | MACHINE_NO_SOUND )

// 72kb rom, 32kb ram, cpu? pc1360
COMP( 1986, pc1403,   0,      0,      pc1403,  pc1403, pc1403_state, empty_init, "Sharp", "Pocket Computer 1403",      MACHINE_NOT_WORKING  | MACHINE_NO_SOUND)
COMP( 198?, pc1403h,  pc1403, 0,      pc1403h, pc1403, pc1403_state, empty_init, "Sharp", "Pocket Computer 1403H",     MACHINE_NOT_WORKING  | MACHINE_NO_SOUND)



pockstat.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/***************************************************************************

    Sony PocketStation

    PocketStation games were downloaded from PS1 games into flash RAM after
    the unit had been inserted in the memory card slot, and so this should
    be emulated alongside the PS1.  However, as many flash dumps exist, it
    is possible to emulate the PocketStation in the meantime.

    CPU: ARM7T (32 bit RISC Processor)
    Memory: 2Kbytes of SRAM, 128Kbytes of FlashROM
    Graphics: 32x32 monochrome LCD
    Sound: 1 12-bit PCM channel
    Input: 5 input buttons, 1 reset button
    Infrared communication: Bi-directional and uni-directional comms
    Other: 1 LED indicator

    Currently, a handful of games run, but some die due to odd hardware
    issues.

    To start a game:
    - Wait for the set-date screen to appear
    - Press Down
    - Set date with directional controls (optional)
    - Press Button 1, wait, press Button 1, press Right, game starts

    If you do nothing for about 20 secs, it turns itself off (screen goes white).

****************************************************************************/

#include "emu.h"
#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/arm7/arm7.h"
#include "sound/dac.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#define LOG_UNKNOWN (1U << 1)
#define LOG_FTLB    (1U << 2)
#define LOG_IRQS    (1U << 3)
#define LOG_INTC    (1U << 4)
#define LOG_TIMER   (1U << 5)
#define LOG_CLOCK   (1U << 6)
#define LOG_RTC     (1U << 7)
#define LOG_LCD     (1U << 8)
#define LOG_AUDIO   (1U << 9)
#define LOG_ALL     (LOG_UNKNOWN | LOG_FTLB | LOG_IRQS | LOG_INTC | LOG_TIMER | LOG_CLOCK | LOG_RTC | LOG_LCD | LOG_AUDIO)
#define LOG_DEFAULT LOG_ALL

#define VERBOSE     (0)
#include "logmacro.h"


namespace {

class pockstat_state : public driver_device
{
public:
	pockstat_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_lcd_buffer(*this, "lcd_buffer"),
		m_maincpu(*this, "maincpu"),
		m_cart(*this, "cartslot")
	{ }

	void pockstat(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(input_update);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void mem_map(address_map &map) ATTR_COLD;

	uint32_t screen_update_pockstat(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void set_interrupt_line(uint32_t line, int state);
	uint32_t get_interrupt_line(uint32_t line);

	void timer_start(int index);

	required_shared_ptr<uint32_t> m_lcd_buffer;
	required_device<cpu_device> m_maincpu;
	required_device<generic_slot_device> m_cart;
	memory_region *m_cart_rom = nullptr;

	static constexpr uint32_t TIMER_COUNT = 3;

	enum : uint32_t
	{
		CLOCK_STEADY = 0x10
	};

	enum
	{
		INT_BTN_ACTION     = 0x00000001, // "Action button"
		INT_BTN_RIGHT      = 0x00000002, // "Right button"
		INT_BTN_LEFT       = 0x00000004, // "Left button"
		INT_BTN_DOWN       = 0x00000008, // "Down button"
		INT_BTN_UP         = 0x00000010, // "Up button"
		INT_UNKNOWN        = 0x00000020, // "Unknown"
		INT_COM            = 0x00000040, // "COM" ???
		INT_TIMER0         = 0x00000080, // "Timer 0"
		INT_TIMER1         = 0x00000100, // "Timer 1"
		INT_RTC            = 0x00000200, // "RTC"
		INT_BATTERY        = 0x00000400, // "Battery Monitor"
		INT_IOP            = 0x00000800, // "IOP"
		INT_IRDA           = 0x00001000, // "IrDA"
		INT_TIMER2         = 0x00002000, // "Timer 2"
		INT_IRQ_MASK       = 0x00001fbf,
		INT_FIQ_MASK       = 0x00002040,
		INT_STATUS_MASK    = 0x0000021f
	};

	struct ftlb_regs_t
	{
		uint32_t control = 0;
		uint32_t stat = 0;
		uint32_t valid = 0;
		uint32_t wait1 = 0;
		uint32_t wait2 = 0;
		uint32_t entry[16]{};
		uint32_t serial = 0;
	} m_ftlb_regs;

	struct intc_regs_t
	{
		uint32_t hold = 0;
		uint32_t status = 0;
		uint32_t enable = 0;
		uint32_t mask = 0;
	} m_intc_regs;

	struct timer_t
	{
		uint32_t period = 0;
		uint32_t count = 0;
		uint32_t control = 0;
		emu_timer *timer = nullptr;
	} m_timers[TIMER_COUNT];

	struct clock_regs_t
	{
		uint32_t mode = 0;
		uint32_t control = 0;
	} m_clock_regs;

	struct rtc_regs_t
	{
		uint32_t mode = 0;
		uint32_t control = 0;
		uint32_t time = 0;
		uint32_t date = 0;
		emu_timer *timer = nullptr;
	} m_rtc_regs;

	uint32_t m_lcd_control = 0;
	int32_t m_flash_write_enable_count = 0;
	int32_t m_flash_write_count = 0;

	uint32_t ftlb_r(offs_t offset, uint32_t mem_mask = ~0);
	void ftlb_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t intc_r(offs_t offset, uint32_t mem_mask = ~0);
	void intc_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t timer_r(offs_t offset, uint32_t mem_mask = ~0);
	void timer_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t clock_r(offs_t offset, uint32_t mem_mask = ~0);
	void clock_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t rtc_r(offs_t offset, uint32_t mem_mask = ~0);
	void rtc_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t lcd_r(offs_t offset, uint32_t mem_mask = ~0);
	void lcd_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t rombank_r(offs_t offset, uint32_t mem_mask = ~0);
	uint32_t flash_r(offs_t offset, uint32_t mem_mask = ~0);
	void flash_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t audio_r(offs_t offset, uint32_t mem_mask = ~0);
	void audio_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	TIMER_CALLBACK_MEMBER(timer_tick);
	TIMER_CALLBACK_MEMBER(rtc_tick);
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(flash_load);

	static const int CPU_FREQ[16];
};

const int pockstat_state::CPU_FREQ[16] =
{
	0x00f800,
	0x01f000,
	0x03e000,
	0x07c000,
	0x0f8000,
	0x1e8000,
	0x3d0000,
	0x7a0000,
	0x7a0000,
	0x7a0000,
	0x7a0000,
	0x7a0000,
	0x7a0000,
	0x7a0000,
	0x7a0000,
	0x7a0000
};

uint32_t pockstat_state::ftlb_r(offs_t offset, uint32_t mem_mask)
{
	switch(offset)
	{
	case 0x0000/4:
		LOGMASKED(LOG_FTLB, "%s: FlashROM TLB Control = %08x & %08x\n", machine().describe_context(), m_ftlb_regs.control, mem_mask);
		return m_ftlb_regs.control | 1; // ???
	case 0x0004/4:
		LOGMASKED(LOG_FTLB, "%s: Unknown (F_STAT) = %08x & %08x\n", machine().describe_context(), m_ftlb_regs.stat, mem_mask);
		return m_ftlb_regs.stat;
	case 0x0008/4:
		LOGMASKED(LOG_FTLB, "%s: FlashROM TLB Valid Tag = %08x & %08x\n", machine().describe_context(), m_ftlb_regs.valid, mem_mask);
		return m_ftlb_regs.valid;
	case 0x000c/4:
		LOGMASKED(LOG_FTLB, "%s: Unknown (F_WAIT1) = %08x & %08x\n", machine().describe_context(), m_ftlb_regs.wait1, mem_mask);
		return m_ftlb_regs.wait1;
	case 0x0010/4:
		LOGMASKED(LOG_FTLB, "%s: Unknown (F_WAIT2) = %08x & %08x\n", machine().describe_context(), m_ftlb_regs.wait2 | 0x04, mem_mask);
		return m_ftlb_regs.wait2 | 0x04;
	case 0x0100/4:
	case 0x0104/4:
	case 0x0108/4:
	case 0x010c/4:
	case 0x0110/4:
	case 0x0114/4:
	case 0x0118/4:
	case 0x011c/4:
	case 0x0120/4:
	case 0x0124/4:
	case 0x0128/4:
	case 0x012c/4:
	case 0x0130/4:
	case 0x0134/4:
	case 0x0138/4:
	case 0x013c/4:
		LOGMASKED(LOG_FTLB, "%s: FlashROM TLB Entry %d = %08x & %08x\n", machine().describe_context(), offset - 0x100/4, m_ftlb_regs.entry[offset - 0x100/4],
			mem_mask);
		return m_ftlb_regs.entry[offset - 0x100/4];
	case 0x0300/4:
		LOGMASKED(LOG_FTLB, "%s: Unknown (F_SN) = %08x & %08x\n", machine().describe_context(), m_ftlb_regs.serial, mem_mask);
		return m_ftlb_regs.serial;
	default:
		LOGMASKED(LOG_FTLB | LOG_UNKNOWN, "%s: Unknown Register %08x & %08x\n", machine().describe_context(), 0x06000000 + (offset << 2), mem_mask);
		break;
	}
	return 0;
}

void pockstat_state::ftlb_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	switch(offset)
	{
	case 0x0000/4:
		LOGMASKED(LOG_FTLB, "%s: FlashROM TLB Control = %08x & %08x\n", machine().describe_context(), data, mem_mask);
		COMBINE_DATA(&m_ftlb_regs.control);
		break;
	case 0x0004/4:
		LOGMASKED(LOG_FTLB, "%s: Unknown (F_STAT) = %08x & %08x\n", machine().describe_context(), data, mem_mask);
		COMBINE_DATA(&m_ftlb_regs.stat);
		break;
	case 0x0008/4:
		LOGMASKED(LOG_FTLB, "%s: FlashROM TLB Valid Tag = %08x & %08x\n", machine().describe_context(), data, mem_mask);
		COMBINE_DATA(&m_ftlb_regs.valid);
		break;
	case 0x000c/4:
		LOGMASKED(LOG_FTLB, "%s: Unknown (F_WAIT1) = %08x & %08x\n", machine().describe_context(), data, mem_mask);
		COMBINE_DATA(&m_ftlb_regs.wait1);
		break;
	case 0x0010/4:
		LOGMASKED(LOG_FTLB, "%s: Unknown (F_WAIT2) = %08x & %08x\n", machine().describe_context(), data, mem_mask);
		COMBINE_DATA(&m_ftlb_regs.wait2);
		break;
	case 0x0100/4:
	case 0x0104/4:
	case 0x0108/4:
	case 0x010c/4:
	case 0x0110/4:
	case 0x0114/4:
	case 0x0118/4:
	case 0x011c/4:
	case 0x0120/4:
	case 0x0124/4:
	case 0x0128/4:
	case 0x012c/4:
	case 0x0130/4:
	case 0x0134/4:
	case 0x0138/4:
	case 0x013c/4:
		LOGMASKED(LOG_FTLB, "%s: FlashROM TLB Entry %d = %08x & %08x\n", machine().describe_context(), offset - 0x100/4, data, mem_mask);
		COMBINE_DATA(&m_ftlb_regs.entry[offset - 0x100/4]);
		break;
	case 0x0300/4:
		LOGMASKED(LOG_FTLB, "%s: Unknown (F_SN) = %08x & %08x\n", machine().describe_context(), data, mem_mask);
		COMBINE_DATA(&m_ftlb_regs.serial);
		break;
	default:
		LOGMASKED(LOG_FTLB | LOG_UNKNOWN, "%s: Unknown Register %08x = %08x & %08x\n", machine().describe_context(), 0x06000000 + (offset << 2), data, mem_mask);
		break;
	}
}

uint32_t pockstat_state::get_interrupt_line(uint32_t line)
{
	return m_intc_regs.status & line;
}

void pockstat_state::set_interrupt_line(uint32_t line, int state)
{
	if (line)
	{
		if (state)
		{
			m_intc_regs.status |= line & INT_STATUS_MASK;
			m_intc_regs.hold |= line &~ INT_STATUS_MASK;
			LOGMASKED(LOG_IRQS, "Setting IRQ line %08x, status = %08x, hold = %08x\n", line, m_intc_regs.status, m_intc_regs.hold);
		}
		else
		{
			m_intc_regs.status &= ~line;
			m_intc_regs.hold &= ~line;
			LOGMASKED(LOG_IRQS, "Clearing IRQ line %08x, status = %08x, hold = %08x\n", line, m_intc_regs.status, m_intc_regs.hold);
		}
	}

	const uint32_t new_irq = m_intc_regs.hold & m_intc_regs.enable & INT_IRQ_MASK;
	if (new_irq)
	{
		m_maincpu->set_input_line(arm7_cpu_device::ARM7_IRQ_LINE, ASSERT_LINE);
	}
	else
	{
		m_maincpu->set_input_line(arm7_cpu_device::ARM7_IRQ_LINE, CLEAR_LINE);
	}

	const uint32_t new_fiq = m_intc_regs.hold & m_intc_regs.enable & INT_FIQ_MASK;
	if (new_fiq)
	{
		m_maincpu->set_input_line(arm7_cpu_device::ARM7_FIRQ_LINE, ASSERT_LINE);
	}
	else
	{
		m_maincpu->set_input_line(arm7_cpu_device::ARM7_FIRQ_LINE, CLEAR_LINE);
	}
}

uint32_t pockstat_state::intc_r(offs_t offset, uint32_t mem_mask)
{
	switch(offset)
	{
	case 0x0000/4:
		LOGMASKED(LOG_INTC, "%s: Held Interrupt Read: %08x & %08x\n", machine().describe_context(), m_intc_regs.hold, mem_mask);
		return m_intc_regs.hold;
	case 0x0004/4:
		LOGMASKED(LOG_INTC, "%s: Interrupt Status Read: %08x & %08x\n", machine().describe_context(), m_intc_regs.status, mem_mask);
		return m_intc_regs.status;
	case 0x0008/4:
		LOGMASKED(LOG_INTC, "%s: Interrupt Enable Read: %08x & %08x\n", machine().describe_context(), m_intc_regs.enable, mem_mask);
		return m_intc_regs.enable;
	case 0x000c/4:
		LOGMASKED(LOG_INTC, "%s: Interrupt Mask Read (Invalid): %08x & %08x\n", machine().describe_context(), 0, mem_mask);
		return 0;
	case 0x0010/4:
		LOGMASKED(LOG_INTC, "%s: Interrupt Acknowledge Read (Invalid): %08x & %08x\n", machine().describe_context(), 0, mem_mask);
		return 0;
	default:
		LOGMASKED(LOG_INTC | LOG_UNKNOWN, "%s: Unknown Register Read: %08x & %08x\n", machine().describe_context(), 0x0a000000 + (offset << 2), mem_mask);
		break;
	}
	return 0;
}

void pockstat_state::intc_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	switch(offset)
	{
	case 0x0000/4:
		LOGMASKED(LOG_INTC, "%s: Held Interrupt (Invalid Write) = %08x & %08x\n", machine().describe_context(), data, mem_mask);
		break;
	case 0x0004/4:
		LOGMASKED(LOG_INTC, "%s: Interrupt Status (Invalid Write) = %08x & %08x\n", machine().describe_context(), data, mem_mask);
		break;
	case 0x0008/4:
		LOGMASKED(LOG_INTC, "%s: Interrupt Enable = %08x & %08x\n", machine().describe_context(), data, mem_mask);
		m_intc_regs.enable |= data;
		//COMBINE_DATA(&m_intc_regs.enable);
		//m_intc_regs.status &= m_intc_regs.enable;
		//m_intc_regs.hold &= m_intc_regs.enable;
		set_interrupt_line(0, 0);
		break;
	case 0x000c/4:
		LOGMASKED(LOG_INTC, "%s: Interrupt Mask = %08x & %08x\n", machine().describe_context(), data, mem_mask);
		m_intc_regs.enable &= ~data;
		COMBINE_DATA(&m_intc_regs.mask);
		//m_intc_regs.status &= m_intc_regs.enable;
		//m_intc_regs.hold &= m_intc_regs.enable;
		set_interrupt_line(0, 0);
		break;
	case 0x0010/4:
		LOGMASKED(LOG_INTC, "%s: Interrupt Acknowledge = %08x & %08x\n", machine().describe_context(), data, mem_mask);
		m_intc_regs.hold &= ~data;
		m_intc_regs.status &= ~data;
		set_interrupt_line(0, 0);
		//COMBINE_DATA(&m_intc_regs.acknowledge);
		break;
	default:
		LOGMASKED(LOG_INTC | LOG_UNKNOWN, "%s: Unknown Register %08x = %08x & %08x\n", machine().describe_context(), 0x0a000000 + (offset << 2), data, mem_mask);
		break;
	}
}

TIMER_CALLBACK_MEMBER(pockstat_state::timer_tick)
{
	set_interrupt_line(param == 2 ? INT_TIMER2 : (param == 1 ? INT_TIMER1 : INT_TIMER0), 1);
	m_timers[param].count = m_timers[param].period;
	timer_start(param);
}

void pockstat_state::timer_start(int index)
{
	int divisor = 1;
	attotime period;
	switch (m_timers[index].control & 3)
	{
	case 0:
	case 3:
		divisor = 1;
		break;
	case 1:
		divisor = 16;
		break;
	case 2:
		divisor = 256;
		break;
	}
	period = attotime::from_hz(CPU_FREQ[m_clock_regs.mode & 0x0f] / 2) * divisor * m_timers[index].count;
	m_timers[index].timer->adjust(period, index);
}

uint32_t pockstat_state::timer_r(offs_t offset, uint32_t mem_mask)
{
	switch (offset)
	{
	case 0x0000/4:
	case 0x0010/4:
	case 0x0020/4:
	{
		const uint32_t index = offset / (0x10/4);
		LOGMASKED(LOG_TIMER, "%s: Timer %d Period Read: %08x & %08x\n", machine().describe_context(), index, m_timers[index].period, mem_mask);
		return m_timers[index].period;
	}
	case 0x0004/4:
	case 0x0014/4:
	case 0x0024/4:
	{
		const uint32_t index = offset / (0x10/4);
		LOGMASKED(LOG_TIMER, "%s: Timer %d Count Read: %08x & %08x\n", machine().describe_context(), index, m_timers[index].count, mem_mask);
		if(m_timers[index].control & 4)
		{
			m_timers[index].count--;
			if (m_timers[index].count > m_timers[index].period)
			{
				m_timers[index].count = m_timers[index].period;
			}
			return --m_timers[index].count;
		}
		return m_timers[index].count;
	}
	case 0x0008/4:
	case 0x0018/4:
	case 0x0028/4:
	{
		const uint32_t index = offset / (0x10/4);
		LOGMASKED(LOG_TIMER, "%s: Timer %d Control = %08x & %08x\n", machine().describe_context(), index, m_timers[index].control, mem_mask);
		return m_timers[index].control;
	}
	default:
		LOGMASKED(LOG_TIMER | LOG_UNKNOWN, "%s: Unknown Register %08x & %08x\n", machine().describe_context(), 0x0a800000 + (offset << 2), mem_mask);
		break;
	}
	return 0;
}

void pockstat_state::timer_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	switch (offset)
	{
	case 0x0000/4:
	case 0x0010/4:
	case 0x0020/4:
	{
		const uint32_t index = offset / (0x10/4);
		LOGMASKED(LOG_TIMER, "%s: Timer %d Period = %08x & %08x\n", machine().describe_context(), index, data, mem_mask);
		COMBINE_DATA(&m_timers[index].period);
		break;
	}
	case 0x0004/4:
	case 0x0014/4:
	case 0x0024/4:
	{
		const uint32_t index = offset / (0x10/4);
		LOGMASKED(LOG_TIMER, "%s: Timer %d Count = %08x & %08x\n", machine().describe_context(), index, data, mem_mask);
		COMBINE_DATA(&m_timers[index].count);
		break;
	}
	case 0x0008/4:
	case 0x0018/4:
	case 0x0028/4:
	{
		const uint32_t index = offset / (0x10/4);
		LOGMASKED(LOG_TIMER, "%s: Timer %d Control = %08x & %08x\n", machine().describe_context(), index, data, mem_mask);
		COMBINE_DATA(&m_timers[index].control);
		if(m_timers[index].control & 4)
		{
			timer_start(index);
		}
		else
		{
			m_timers[index].timer->adjust(attotime::never, index);
		}
		break;
	}
	default:
		LOGMASKED(LOG_TIMER | LOG_UNKNOWN, "%s: Unknown Register %08x = %08x & %08x\n", machine().describe_context(), 0x0a800000 + (offset << 2), data, mem_mask);
		break;
	}
}

uint32_t pockstat_state::clock_r(offs_t offset, uint32_t mem_mask)
{
	switch(offset)
	{
		case 0x0000/4:
			LOGMASKED(LOG_CLOCK, "%s: Clock Mode Read: %08x & %08x\n", machine().describe_context(), m_clock_regs.mode | 0x10, mem_mask);
			return m_clock_regs.mode | CLOCK_STEADY;
		case 0x0004/4:
			LOGMASKED(LOG_CLOCK, "%s: Clock Control Read: %08x & %08x\n", machine().describe_context(), m_clock_regs.control, mem_mask);
			return m_clock_regs.control;
		default:
			LOGMASKED(LOG_CLOCK | LOG_UNKNOWN, "%s: Unknown Register %08x & %08x\n", machine().describe_context(), 0x0b000000 + (offset << 2), mem_mask);
			break;
	}
	return 0;
}

void pockstat_state::clock_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	switch(offset)
	{
		case 0x0000/4:
			LOGMASKED(LOG_CLOCK, "%s: Clock Mode = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			COMBINE_DATA(&m_clock_regs.mode);
			m_maincpu->set_unscaled_clock(CPU_FREQ[m_clock_regs.mode & 0x0f]);
			break;
		case 0x0004/4:
			LOGMASKED(LOG_CLOCK, "%s: Clock Control = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			COMBINE_DATA(&m_clock_regs.control);
			break;
		default:
			LOGMASKED(LOG_CLOCK | LOG_UNKNOWN, "%s: Unknown Register %08x = %08x & %08x\n", machine().describe_context(), 0x0b000000 + (offset << 2), data, mem_mask);
			break;
	}
}

TIMER_CALLBACK_MEMBER(pockstat_state::rtc_tick)
{
	set_interrupt_line(INT_RTC, get_interrupt_line(INT_RTC) ? 0 : 1);
	if (!(m_rtc_regs.mode & 1))
	{
		m_rtc_regs.time++;
		if ((m_rtc_regs.time & 0x0000000f) == 0x0000000a)
		{
			m_rtc_regs.time &= 0xfffffff0;
			m_rtc_regs.time += 0x00000010;
			if ((m_rtc_regs.time & 0x000000ff) == 0x00000060)
			{
				m_rtc_regs.time &= 0xffffff00;
				m_rtc_regs.time += 0x00000100;
				if ((m_rtc_regs.time & 0x00000f00) == 0x00000a00)
				{
					m_rtc_regs.time &= 0xfffff0ff;
					m_rtc_regs.time += 0x00001000;
					if ((m_rtc_regs.time & 0x0000ff00) == 0x00006000)
					{
						m_rtc_regs.time &= 0xffff00ff;
						m_rtc_regs.time += 0x00010000;
						if ((m_rtc_regs.time & 0x00ff0000) == 0x00240000)
						{
							m_rtc_regs.time &= 0xff00ffff;
							m_rtc_regs.time += 0x01000000;
							if ((m_rtc_regs.time & 0x0f000000) == 0x08000000)
							{
								m_rtc_regs.time &= 0xf0ffffff;
								m_rtc_regs.time |= 0x01000000;
							}
						}
						else if ((m_rtc_regs.time & 0x000f0000) == 0x000a0000)
						{
							m_rtc_regs.time &= 0xfff0ffff;
							m_rtc_regs.time += 0x00100000;
						}
					}
				}
			}
		}
	}
	m_rtc_regs.timer->adjust(attotime::from_hz(1));
}

uint32_t pockstat_state::rtc_r(offs_t offset, uint32_t mem_mask)
{
	switch(offset)
	{
	case 0x0000/4:
		LOGMASKED(LOG_RTC, "%s: RTC Mode Read: %08x & %08x\n", machine().describe_context(), m_rtc_regs.mode, mem_mask);
		return m_rtc_regs.mode;
	case 0x0004/4:
		LOGMASKED(LOG_RTC, "%s: RTC Control Read: %08x & %08x\n", machine().describe_context(), m_rtc_regs.control, mem_mask);
		return m_rtc_regs.control;
	case 0x0008/4:
		LOGMASKED(LOG_RTC, "%s: RTC Time Read: %08x & %08x\n", machine().describe_context(), m_rtc_regs.time, mem_mask);
		return m_rtc_regs.time;
	case 0x000c/4:
		LOGMASKED(LOG_RTC, "%s: RTC Date Read: %08x & %08x\n", machine().describe_context(), m_rtc_regs.date, mem_mask);
		return m_rtc_regs.date;
	default:
		LOGMASKED(LOG_RTC | LOG_UNKNOWN, "%s: Unknown Register %08x & %08x\n", machine().describe_context(), 0x0b800000 + (offset << 2), mem_mask);
		break;
	}
	return 0;
}

void pockstat_state::rtc_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	switch (offset)
	{
	case 0x0000/4:
		LOGMASKED(LOG_RTC, "%s: RTC Mode = %08x & %08x\n", machine().describe_context(), data, mem_mask);
		COMBINE_DATA(&m_rtc_regs.mode);
		break;
	case 0x0004/4:
		LOGMASKED(LOG_RTC, "%s: RTC Control = %08x & %08x\n", machine().describe_context(), data, mem_mask);
		if (m_rtc_regs.control == 1 && data == 1)
		{
			switch (m_rtc_regs.mode >> 1)
			{
			case 0: // Seconds
				m_rtc_regs.time += 0x00000001;
				if ((m_rtc_regs.time & 0x0000000f) == 0x0000000a)
				{
					m_rtc_regs.time &= 0xfffffff0;
					m_rtc_regs.time += 0x00000010;
					if ((m_rtc_regs.time & 0x000000ff) == 0x00000060)
					{
						m_rtc_regs.time &= 0xffffff00;
					}
				}
				break;
			case 1: // Minutes
				m_rtc_regs.time += 0x00000100;
				if ((m_rtc_regs.time & 0x00000f00) == 0x00000a00)
				{
					m_rtc_regs.time &= 0xfffff0ff;
					m_rtc_regs.time += 0x00001000;
					if ((m_rtc_regs.time & 0x0000ff00) == 0x00006000)
					{
						m_rtc_regs.time &= 0xffff00ff;
					}
				}
				break;
			case 2: // Hours
				m_rtc_regs.time += 0x00010000;
				if ((m_rtc_regs.time & 0x00ff0000) == 0x00240000)
				{
					m_rtc_regs.time &= 0xff00ffff;
				}
				else if ((m_rtc_regs.time & 0x000f0000) == 0x000a0000)
				{
					m_rtc_regs.time &= 0xfff0ffff;
					m_rtc_regs.time += 0x00100000;
				}
				break;
			case 3: // Day of the week
				m_rtc_regs.time += 0x01000000;
				if ((m_rtc_regs.time & 0x0f000000) == 0x08000000)
				{
					m_rtc_regs.time &= 0xf0ffffff;
					m_rtc_regs.time |= 0x01000000;
				}
				break;
			case 4: // Day
				m_rtc_regs.date += 0x00000001;
				if ((m_rtc_regs.date & 0x000000ff) == 0x00000032)
				{
					m_rtc_regs.date &= 0xffffff00;
				}
				else if ((m_rtc_regs.date & 0x0000000f) == 0x0000000a)
				{
					m_rtc_regs.date &= 0xfffffff0;
					m_rtc_regs.date += 0x00000010;
				}
				break;
			case 5: // Month
				m_rtc_regs.date += 0x00000100;
				if ((m_rtc_regs.date & 0x0000ff00) == 0x00001300)
				{
					m_rtc_regs.date &= 0xffffff00;
					m_rtc_regs.date |= 0x00000001;
				}
				else if ((m_rtc_regs.date & 0x00000f00) == 0x00000a00)
				{
					m_rtc_regs.date &= 0xfffff0ff;
					m_rtc_regs.date += 0x00001000;
				}
				break;
			case 6: // Year (LSB)
				m_rtc_regs.date += 0x00010000;
				if ((m_rtc_regs.date & 0x000f0000) == 0x000a0000)
				{
					m_rtc_regs.date &= 0xfff0ffff;
					m_rtc_regs.date += 0x00100000;
					if ((m_rtc_regs.date & 0x00f00000) == 0x00a00000)
					{
						m_rtc_regs.date &= 0xff00ffff;
					}
				}
				break;
			case 7: // Year (MSB)
				break;
			}
			m_rtc_regs.control = 0;
		}
		else if(m_rtc_regs.control == 0)
		{
			COMBINE_DATA(&m_rtc_regs.control);
		}
		break;
	default:
		LOGMASKED(LOG_RTC | LOG_UNKNOWN, "%s: Unknown Register %08x = %08x & %08x\n", machine().describe_context(), 0x0b800000 + (offset << 2), data, mem_mask);
		break;
	}
}


uint32_t pockstat_state::lcd_r(offs_t offset, uint32_t mem_mask)
{
	switch (offset)
	{
	case 0x0000/4:
		LOGMASKED(LOG_LCD, "%s: LCD Control Read: %08x & %08x\n", machine().describe_context(), m_lcd_control | 0x100, mem_mask);
		return m_lcd_control;
	default:
		LOGMASKED(LOG_LCD | LOG_UNKNOWN, "%s: Unknown Register %08x & %08x\n", machine().describe_context(), 0x0d000000 + (offset << 2), mem_mask);
		break;
	}
	return 0;
}

void pockstat_state::lcd_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	switch (offset)
	{
	case 0x0000/4:
		LOGMASKED(LOG_LCD, "%s: LCD Control = %08x & %08x\n", machine().describe_context(), data, mem_mask);
		COMBINE_DATA(&m_lcd_control);
		break;
	default:
		LOGMASKED(LOG_LCD | LOG_UNKNOWN, "%s: Unknown Register %08x = %08x & %08x\n", machine().describe_context(), 0x0d000000 + (offset << 2), data, mem_mask);
		break;
	}
}

INPUT_CHANGED_MEMBER(pockstat_state::input_update)
{
	uint32_t buttons = ioport("BUTTONS")->read();

	set_interrupt_line(INT_BTN_ACTION, (buttons &  1) ? 1 : 0);
	set_interrupt_line(INT_BTN_RIGHT,  (buttons &  2) ? 1 : 0);
	set_interrupt_line(INT_BTN_LEFT,   (buttons &  4) ? 1 : 0);
	set_interrupt_line(INT_BTN_DOWN,   (buttons &  8) ? 1 : 0);
	set_interrupt_line(INT_BTN_UP,     (buttons & 16) ? 1 : 0);
}

uint32_t pockstat_state::rombank_r(offs_t offset, uint32_t mem_mask)
{
	int32_t bank = (offset >> 11) & 0x0f;
	for (int index = 0; index < 32; index++)
	{
		if (m_ftlb_regs.valid & (1 << index))
		{
			if (m_ftlb_regs.entry[index] == bank)
			{
				return m_cart->read32_rom(index * (0x2000/4) + (offset & (0x1fff/4)), mem_mask);
			}
		}
	}
	return m_cart->read32_rom(offset & 0x7fff, mem_mask);
}


// Horrible hack, probably wrong
void pockstat_state::flash_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	if (offset == (0x55a8/4))
	{
		m_flash_write_enable_count++;
		return;
	}
	if (offset == (0x2a54/4))
	{
		m_flash_write_enable_count++;
		return;
	}
	if (m_flash_write_enable_count == 3)
	{
		m_flash_write_enable_count = 0;
		m_flash_write_count = 0x40;
		return;
	}
	if (m_flash_write_count)
	{
		m_flash_write_count--;
		COMBINE_DATA(&((uint32_t*)(m_cart_rom->base()))[offset]);
	}
}

uint32_t pockstat_state::flash_r(offs_t offset, uint32_t mem_mask)
{
	return m_cart->read32_rom(offset, mem_mask);
}

uint32_t pockstat_state::audio_r(offs_t offset, uint32_t mem_mask)
{
	LOGMASKED(LOG_AUDIO | LOG_UNKNOWN, "%s: Unknown Audio Read: %08x = %08x & %08x\n", machine().describe_context(), 0xd800000 + (offset << 2), 0x10, mem_mask);
	return 0;
}

void pockstat_state::audio_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOGMASKED(LOG_AUDIO | LOG_UNKNOWN, "%s: Unknown Audio Write: %08x = %08x & %08x\n", machine().describe_context(), 0xd800000 + (offset << 2), data, mem_mask);
}

void pockstat_state::mem_map(address_map &map)
{
	map(0x00000000, 0x000007ff).ram();
	map(0x02000000, 0x02ffffff).r(FUNC(pockstat_state::rombank_r));
	map(0x04000000, 0x04003fff).rom().region("maincpu", 0);
	map(0x06000000, 0x06000307).rw(FUNC(pockstat_state::ftlb_r), FUNC(pockstat_state::ftlb_w));
	map(0x08000000, 0x0801ffff).rw(FUNC(pockstat_state::flash_r), FUNC(pockstat_state::flash_w));
	map(0x0a000000, 0x0a000013).rw(FUNC(pockstat_state::intc_r), FUNC(pockstat_state::intc_w));
	map(0x0a800000, 0x0a80002b).rw(FUNC(pockstat_state::timer_r), FUNC(pockstat_state::timer_w));
	map(0x0b000000, 0x0b000007).rw(FUNC(pockstat_state::clock_r), FUNC(pockstat_state::clock_w));
	map(0x0b800000, 0x0b80000f).rw(FUNC(pockstat_state::rtc_r), FUNC(pockstat_state::rtc_w));
	map(0x0d000000, 0x0d000003).rw(FUNC(pockstat_state::lcd_r), FUNC(pockstat_state::lcd_w));
	map(0x0d000100, 0x0d00017f).ram().share("lcd_buffer");
	map(0x0d80000c, 0x0d80000f).rw(FUNC(pockstat_state::audio_r), FUNC(pockstat_state::audio_w));
	map(0x0d800014, 0x0d800015).w("dac", FUNC(dac_word_interface::data_w));
}

/* Input ports */
static INPUT_PORTS_START( pockstat )
	PORT_START("BUTTONS")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1)        PORT_NAME("Action Button")  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pockstat_state::input_update), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_NAME("Right")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pockstat_state::input_update), 0)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT)  PORT_NAME("Left")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pockstat_state::input_update), 0)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN)  PORT_NAME("Down")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pockstat_state::input_update), 0)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP)    PORT_NAME("Up")             PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(pockstat_state::input_update), 0)
	PORT_BIT( 0xe0, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

void pockstat_state::machine_start()
{
	for (uint32_t index = 0; index < TIMER_COUNT; index++)
	{
		m_timers[index].timer = timer_alloc(FUNC(pockstat_state::timer_tick), this);
		m_timers[index].timer->adjust(attotime::never, index);
		m_timers[index].count = 0;
	}

	m_rtc_regs.time = 0x01000000;
	m_rtc_regs.date = 0x19990101;

	m_rtc_regs.timer = timer_alloc(FUNC(pockstat_state::rtc_tick), this);
	m_rtc_regs.timer->adjust(attotime::from_hz(1), TIMER_COUNT);

	std::string region_tag;
	m_cart_rom = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());

	save_item(NAME(m_ftlb_regs.control));
	save_item(NAME(m_ftlb_regs.stat));
	save_item(NAME(m_ftlb_regs.valid));
	save_item(NAME(m_ftlb_regs.wait1));
	save_item(NAME(m_ftlb_regs.wait2));
	save_item(NAME(m_ftlb_regs.entry));

	save_item(NAME(m_intc_regs.hold));
	save_item(NAME(m_intc_regs.status));
	save_item(NAME(m_intc_regs.enable));
	save_item(NAME(m_intc_regs.mask));

	save_item(NAME(m_timers[0].period));
	save_item(NAME(m_timers[0].count));
	save_item(NAME(m_timers[0].control));
	save_item(NAME(m_timers[1].period));
	save_item(NAME(m_timers[1].count));
	save_item(NAME(m_timers[1].control));
	save_item(NAME(m_timers[2].period));
	save_item(NAME(m_timers[2].count));
	save_item(NAME(m_timers[2].control));

	save_item(NAME(m_clock_regs.mode));
	save_item(NAME(m_clock_regs.control));

	save_item(NAME(m_rtc_regs.mode));
	save_item(NAME(m_rtc_regs.control));
	save_item(NAME(m_rtc_regs.time));
	save_item(NAME(m_rtc_regs.date));

	save_item(NAME(m_flash_write_enable_count));
	save_item(NAME(m_flash_write_count));

	save_item(NAME(m_lcd_control));
}

void pockstat_state::machine_reset()
{
	m_maincpu->set_state_int(arm7_cpu_device::ARM7_R15, 0x4000000);

	m_flash_write_enable_count = 0;
	m_flash_write_count = 0;
}

uint32_t pockstat_state::screen_update_pockstat(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	for (int y = 0; y < 32; y++)
	{
		uint32_t *const scanline = &bitmap.pix(y);
		for (int x = 0; x < 32; x++)
		{
			if (m_lcd_control != 0) // Hack
			{
				if (BIT(m_lcd_buffer[y], x))
					scanline[x] = 0x00000000;
				else
					scanline[x] = 0x00ffffff;
			}
			else
				scanline[x] = 0x00ffffff;
		}
	}
	return 0;
}

DEVICE_IMAGE_LOAD_MEMBER(pockstat_state::flash_load)
{
	static const char *gme_id = "123-456-STD";
	char cart_id[0xf40];
	uint32_t const size = image.length();

	if (size != 0x20f40)
		return std::make_pair(image_error::INVALIDLENGTH, std::string());

	image.fread(cart_id, 0xf40);

	for (int i = 0; i < strlen(gme_id); i++)
	{
		if (cart_id[i] != gme_id[i])
			return std::make_pair(image_error::INVALIDIMAGE, std::string());
	}

	m_cart->rom_alloc(0x20000, GENERIC_ROM32_WIDTH, ENDIANNESS_LITTLE);
	image.fread(m_cart->get_rom_base(), 0x20000);

	return std::make_pair(std::error_condition(), std::string());
}

void pockstat_state::pockstat(machine_config &config)
{
	static constexpr uint32_t DEFAULT_CLOCK = 2000000;

	/* basic machine hardware */
	ARM7(config, m_maincpu, DEFAULT_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &pockstat_state::mem_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(32, 32);
	screen.set_visarea(0, 32-1, 0, 32-1);
	screen.set_screen_update(FUNC(pockstat_state::screen_update_pockstat));

	PALETTE(config, "palette", palette_device::MONOCHROME);

	SPEAKER(config, "speaker").front_center();
	DAC_16BIT_R2R_TWOS_COMPLEMENT(config, "dac", 0).add_route(ALL_OUTPUTS, "speaker", 0.5); // unknown DAC

	/* cartridge */
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "pockstat_cart", "gme");
	m_cart->set_width(GENERIC_ROM32_WIDTH);
	m_cart->set_endian(ENDIANNESS_LITTLE);
	m_cart->set_device_load(FUNC(pockstat_state::flash_load));
}

/* ROM definition */
ROM_START( pockstat )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "kernel.bin", 0x0000, 0x4000, CRC(5fb47dd8) SHA1(6ae880493ddde880827d1e9f08e9cb2c38f9f2ec) )
ROM_END

} // Anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY                            FULLNAME              FLAGS
CONS( 1999, pockstat, 0,      0,      pockstat, pockstat, pockstat_state, empty_init, "Sony Computer Entertainment Inc", "Sony PocketStation", MACHINE_SUPPORTS_SAVE )



pofo.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

    Atari Portfolio

    http://portfolio.wz.cz/
    http://www.pofowiki.de/doku.php
    http://www.best-electronics-ca.com/portfoli.htm
    http://www.atari-portfolio.co.uk/pfnews/pf9.txt

    Command line for dual RAM expansion with A: File Manager ROM card and B: RAM card
    ./mess64 pofo -exp ram -exp:ram:exp ram2 -cart1 fileman -exp:ram:ccmb ram

*/

/*

    TODO:

    - where do CDET and NMD1 connect to ??
    - i/o port 8051
    - screen contrast
    - system tick frequency selection (1 or 128 Hz)
    - soft power off
    - LCD board
        - HD61830A00
        - 5816 2Kx8 RAM
        - 27C256 32Kx8 EPROM
        - PCD3311T DTMF generator @ 3.578640MHz

*/

#include "emu.h"

#include "cpu/i86/i86.h"
#include "bus/pofo/ccm.h"
#include "bus/pofo/exp.h"
#include "machine/nvram.h"
#include "pofo_kbd.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "sound/pcd3311.h"
#include "video/hd61830.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

#define LOG 0

#define M80C88A_TAG     "u1"
#define HD61830_TAG     "hd61830"
#define PCD3311T_TAG    "pcd3311t"
#define TIMER_TICK_TAG  "tick"
#define SCREEN_TAG      "screen"

static const uint8_t INTERRUPT_VECTOR[] = { 0x08, 0x09, 0x00 };



//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class portfolio_state : public driver_device
{
public:
	portfolio_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, M80C88A_TAG),
		m_lcdc(*this, HD61830_TAG),
		m_keyboard(*this, "keyboard"),
		m_dtmf(*this, PCD3311T_TAG),
		m_ccm(*this, PORTFOLIO_MEMORY_CARD_SLOT_A_TAG),
		m_exp(*this, "exp"),
		m_timer_tick(*this, TIMER_TICK_TAG),
		m_nvram(*this, "nvram"),
		m_ram(*this, "ram"),
		m_rom(*this, M80C88A_TAG),
		m_char_rom(*this, HD61830_TAG),
		m_battery(*this, "BATTERY")
	{ }

	void portfolio(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void portfolio_io(address_map &map) ATTR_COLD;
	void portfolio_lcdc(address_map &map) ATTR_COLD;
	void portfolio_mem(address_map &map) ATTR_COLD;

	void check_interrupt();
	void trigger_interrupt(int level);

	enum
	{
		INT_TICK = 0,
		INT_KEYBOARD,
		INT_ERROR,
		INT_EXTERNAL
	};

	enum
	{
		ROM_APP = 0b000, // 0
		CCM_A   = 0b011, // 3
		CCM_B   = 0b111, // 7
		ROM_EXT = 0b010, // 2
	};

	uint8_t mem_r(offs_t offset);
	void mem_w(offs_t offset, uint8_t data);

	uint8_t io_r(offs_t offset);
	void io_w(offs_t offset, uint8_t data);

	uint8_t irq_status_r();
	uint8_t battery_r();
	uint8_t counter_r(offs_t offset);

	void irq_mask_w(uint8_t data);
	void dtmf_w(uint8_t data);
	void power_w(uint8_t data);
	void select_w(uint8_t data);
	void counter_w(offs_t offset, uint8_t data);
	void contrast_w(uint8_t data);

	void eint_w(int state);
	void wake_w(int state);
	void keyboard_int_w(int state);

	void portfolio_palette(palette_device &palette) const;
	TIMER_DEVICE_CALLBACK_MEMBER(system_tick);
	TIMER_DEVICE_CALLBACK_MEMBER(counter_tick);
	uint8_t hd61830_rd_r(offs_t offset);
	IRQ_CALLBACK_MEMBER(portfolio_int_ack);

	required_device<cpu_device> m_maincpu;
	required_device<hd61830_device> m_lcdc;
	required_device<pofo_keyboard_device> m_keyboard;
	required_device<pcd3311_device> m_dtmf;
	required_device<portfolio_memory_card_slot_device> m_ccm;
	required_device<portfolio_expansion_slot_device> m_exp;
	required_device<timer_device> m_timer_tick;
	required_device<nvram_device> m_nvram;
	required_device<ram_device> m_ram;
	required_region_ptr<uint8_t> m_rom;
	required_region_ptr<uint8_t> m_char_rom;
	required_ioport m_battery;

	uint8_t m_ip = 0;
	uint8_t m_ie = 0;
	uint16_t m_counter = 0;
	int m_rom_b = 0;
};



//**************************************************************************
//  INTERRUPTS
//**************************************************************************

//-------------------------------------------------
//  check_interrupt - check interrupt status
//-------------------------------------------------

void portfolio_state::check_interrupt()
{
	int level = (m_ip & m_ie) ? ASSERT_LINE : CLEAR_LINE;

	m_maincpu->set_input_line(INPUT_LINE_INT0, level);
	m_exp->iint_w(level);
}


//-------------------------------------------------
//  trigger_interrupt - trigger interrupt request
//-------------------------------------------------

void portfolio_state::trigger_interrupt(int level)
{
	// set interrupt pending bit
	m_ip |= 1 << level;

	check_interrupt();
}


//-------------------------------------------------
//  eint_w - external interrupt
//-------------------------------------------------

void portfolio_state::eint_w(int state)
{
	if (state)
	{
		trigger_interrupt(INT_EXTERNAL);
	}
}


//-------------------------------------------------
//  wake_w - wake
//-------------------------------------------------

void portfolio_state::wake_w(int state)
{
	// TODO
}

void portfolio_state::keyboard_int_w(int state)
{
	if (state)
	{
		trigger_interrupt(INT_KEYBOARD);
	}
}

//-------------------------------------------------
//  irq_status_r - interrupt status read
//-------------------------------------------------

uint8_t portfolio_state::irq_status_r()
{
	uint8_t data = m_ip;
	/*
	    The BIOS interrupt 11h (Equipment list) reports that the second floppy drive (B:) is
	    installed if the 3rd bit is set (which is also the external interrupt line).
	    It is not clear if the ~NMD1 line is OR or XORed or muxed with the interrupt line,
	    but this way seems to work.
	*/
	data |= !m_exp->nmd1_r() << 3;

	return data;
}

//-------------------------------------------------
//  irq_mask_w - interrupt enable mask
//-------------------------------------------------

void portfolio_state::irq_mask_w(uint8_t data)
{
	m_ie = data;

	if (LOG) logerror("%s %s IE %01x\n", machine().time().as_string(), machine().describe_context(), data);

	check_interrupt();
}


//-------------------------------------------------
//  IRQ_CALLBACK_MEMBER( portfolio_int_ack )
//-------------------------------------------------

IRQ_CALLBACK_MEMBER(portfolio_state::portfolio_int_ack)
{
	uint8_t vector = 0;

	for (int i = 0; i < 4; i++)
	{
		if (BIT(m_ip, i))
		{
			// clear interrupt pending bit
			m_ip &= ~(1 << i);

			if (LOG) logerror("%s %s IP %01x\n", machine().time().as_string(), machine().describe_context(), m_ip);

			if (i == 3)
				vector = m_exp->eack_r();
			else
				vector = INTERRUPT_VECTOR[i];

			break;
		}
	}

	check_interrupt();

	return vector;
}

//**************************************************************************
//  SOUND
//**************************************************************************

//-------------------------------------------------
//  dtmf_w -
//-------------------------------------------------

void portfolio_state::dtmf_w(uint8_t data)
{
	/*

	    bit     description

	    0       PCD3311T D0
	    1       PCD3311T D1
	    2       PCD3311T D2
	    3       PCD3311T D3
	    4       PCD3311T D4
	    5       PCD3311T D5
	    6       PCD3311T STROBE
	    7       PCD3311T VDD,MODE,A0

	*/

	if (LOG) logerror("%s %s DTMF %02x\n", machine().time().as_string(), machine().describe_context(), data);

	m_dtmf->mode_w(!BIT(data, 7));
	m_dtmf->a0_w(!BIT(data, 7));
	m_dtmf->write(data & 0x3f);
	m_dtmf->strobe_w(BIT(data, 6));
}



//**************************************************************************
//  POWER MANAGEMENT
//**************************************************************************

//-------------------------------------------------
//  power_w - power management
//-------------------------------------------------

void portfolio_state::power_w(uint8_t data)
{
	/*

	    bit     description

	    0
	    1       1=power off
	    2
	    3
	    4
	    5
	    6
	    7

	*/

	if (LOG) logerror("%s %s POWER %02x\n", machine().time().as_string(), machine().describe_context(), data);

	if (BIT(data, 1))
	{
		// TODO power off
	}
}


//-------------------------------------------------
//  battery_r - battery status
//-------------------------------------------------

uint8_t portfolio_state::battery_r()
{
	/*

	    bit     signal      description

	    0       ?           bit 0 from bus select (m_rom_b)
	    1       ?
	    2       ?           bit 2 from bus select (m_rom_b)
	    3       ?
	    4       ?
	    5       PDET        1=peripheral connected
	    6       LOWB        0=battery low
	    7       BDET?       1=cold boot

	*/

	uint8_t data = 0;

	/*
	    Partially stores what has been written into this port.
	    Used by interrupt 61h service 24h (Get ROM/CCM state).
	    Setting bit 1 here causes the BIOS to permanently wedge the external ROM
	    select on, so mask it out as a workaround.
	*/
	data |= (m_rom_b & 0b101);

	// peripheral detect
	data |= m_exp->pdet_r() << 5;

	// battery status
	data |= (m_battery->read() & 0x03) << 6;

	return data;
}


//-------------------------------------------------
//  select_w -
//-------------------------------------------------

void portfolio_state::select_w(uint8_t data)
{
	/*

	    bit     description

	    0       ?
	    1       ?
	    2       ?
	    3       ?
	    4
	    5
	    6       ?
	    7       ?

	*/

	if (LOG) logerror("%s %s SELECT %02x\n", machine().time().as_string(), machine().describe_context(), data);

	m_rom_b = data & 0x0f;
}



//**************************************************************************
//  SYSTEM TIMERS
//**************************************************************************

//-------------------------------------------------
//  TIMER_DEVICE_CALLBACK_MEMBER( system_tick )
//-------------------------------------------------

TIMER_DEVICE_CALLBACK_MEMBER(portfolio_state::system_tick)
{
	//trigger_interrupt(INT_TICK);
}


//-------------------------------------------------
//  TIMER_DEVICE_CALLBACK_MEMBER( counter_tick )
//-------------------------------------------------

TIMER_DEVICE_CALLBACK_MEMBER(portfolio_state::counter_tick)
{
	m_counter++;
}


//-------------------------------------------------
//  counter_r - counter register read
//-------------------------------------------------

uint8_t portfolio_state::counter_r(offs_t offset)
{
	uint8_t data = 0;

	switch (offset)
	{
	case 0:
		data = m_counter & 0xff;
		break;

	case 1:
		data = m_counter >> 8;
		break;
	}

	return data;
}


//-------------------------------------------------
//  counter_w - counter register write
//-------------------------------------------------

void portfolio_state::counter_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 0:
		m_counter = (m_counter & 0xff00) | data;
		break;

	case 1:
		m_counter = (data << 8) | (m_counter & 0xff);
		break;
	}
}



//**************************************************************************
//  MEMORY MAPPING
//**************************************************************************

//-------------------------------------------------
//  mem_r -
//-------------------------------------------------

uint8_t portfolio_state::mem_r(offs_t offset)
{
	uint8_t data = 0;

	int iom = 0;
	int bcom = 1;
	int ncc1 = 1;

	if (offset < 0x1f000)
	{
		data = m_ram->read(offset);
	}
	else if (offset >= 0xb0000 && offset < 0xc0000)
	{
		data = m_ram->read(0x1f000 + (offset & 0xfff));
	}
	else if (offset >= 0xc0000 && offset < 0xe0000)
	{
		switch (m_rom_b)
		{
		case ROM_APP:
			data = m_rom[offset & 0x3ffff];
			break;

		case CCM_A:
			if (LOG) logerror("%s %s CCM0 read %05x\n", machine().time().as_string(), machine().describe_context(), offset & 0x1ffff);

			data = m_ccm->nrdi_r(offset & 0x1ffff);
			break;

		case CCM_B:
			ncc1 = 0;
			break;

		case ROM_EXT:
			// TODO
			break;

		default:
			logerror("%s %s Invalid bus read %05x\n", machine().time().as_string(), machine().describe_context(), offset & 0x1ffff);
			break;
		}
	}
	else if (offset >= 0xe0000)
	{
		data = m_rom[offset & 0x3ffff];
	}

	data = m_exp->nrdi_r(offset, data, iom, bcom, ncc1);

	return data;
}


//-------------------------------------------------
//  mem_w -
//-------------------------------------------------

void portfolio_state::mem_w(offs_t offset, uint8_t data)
{
	int iom = 0;
	int bcom = 1;
	int ncc1 = 1;

	if (offset < 0x1f000)
	{
		m_ram->write(offset, data);
	}
	else if (offset >= 0xb0000 && offset < 0xc0000)
	{
		m_ram->write(0x1f000 + (offset & 0xfff), data);
	}
	else if (offset >= 0xc0000 && offset < 0xe0000)
	{
		switch (m_rom_b)
		{
		case CCM_A:
			if (LOG) logerror("%s %s CCM0 write %05x:%02x\n", machine().time().as_string(), machine().describe_context(), offset & 0x1ffff, data);

			m_ccm->nwri_w(offset & 0x1ffff, data);
			break;

		case CCM_B:
			ncc1 = 0;
			break;

		case ROM_EXT:
		case ROM_APP:
			break;

		default:
			logerror("%s %s Invalid bus write %05x\n", machine().time().as_string(), machine().describe_context(), offset & 0x1ffff);
			break;
		}
	}

	m_exp->nwri_w(offset, data, iom, bcom, ncc1);
}


//-------------------------------------------------
//  io_r -
//-------------------------------------------------

uint8_t portfolio_state::io_r(offs_t offset)
{
	uint8_t data = 0;

	int iom = 1;
	int bcom = 1;
	int ncc1 = 0;

	if ((offset & 0xff00) == 0x8000)
	{
		switch ((offset >> 4) & 0x0f)
		{
		case 0:
			data = m_keyboard->read();
			break;

		case 1:
			if (offset & 0x01)
			{
				data = m_lcdc->status_r();
			}
			else
			{
				data = m_lcdc->data_r();
			}
			break;

		case 4:
			data = counter_r(offset & 0x01);
			break;

		case 5:
			if (offset & 0x01)
			{
				data = battery_r();
			}
			else
			{
				data = irq_status_r();
			}
			break;

		case 7:
			bcom = 0;
			break;
		}
	}
	else if (offset == 0x61)
	{
		// Magic port to detect the Pofo
		data = 0x61;
	}

	data = m_exp->nrdi_r(offset, data, iom, bcom, ncc1);

	return data;
}


//-------------------------------------------------
//  io_w -
//-------------------------------------------------

void portfolio_state::io_w(offs_t offset, uint8_t data)
{
	int iom = 1;
	int bcom = 1;
	int ncc1 = 0;

	if ((offset & 0xff00) == 0x8000)
	{
		switch ((offset >> 4) & 0x0f)
		{
		case 1:
			if (offset & 0x01)
			{
				m_lcdc->control_w(data);
			}
			else
			{
				m_lcdc->data_w(data);
			}
			break;

		case 2:
			dtmf_w(data);
			break;

		case 3:
			power_w(data);
			break;

		case 4:
			counter_w(offset & 0x01, data);
			break;

		case 5:
			if (offset & 0x01)
			{
				select_w(data);
			}
			else
			{
				irq_mask_w(data);
			}
			break;

		case 6:
			contrast_w(data);
			break;

		case 7:
			bcom = 0;
			break;
		}
	}

	m_exp->nwri_w(offset, data, iom, bcom, ncc1);
}



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( portfolio_mem )
//-------------------------------------------------

void portfolio_state::portfolio_mem(address_map &map)
{
	map(0x00000, 0xfffff).rw(FUNC(portfolio_state::mem_r), FUNC(portfolio_state::mem_w));
}


//-------------------------------------------------
//  ADDRESS_MAP( portfolio_io )
//-------------------------------------------------

void portfolio_state::portfolio_io(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(portfolio_state::io_r), FUNC(portfolio_state::io_w));
}


//-------------------------------------------------
//  ADDRESS_MAP( portfolio_lcdc )
//-------------------------------------------------

void portfolio_state::portfolio_lcdc(address_map &map)
{
	map.global_mask(0x7ff);
	map(0x0000, 0x07ff).ram();
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( portfolio )
//-------------------------------------------------

static INPUT_PORTS_START( portfolio )
	PORT_START("BATTERY")
	PORT_CONFNAME( 0x01, 0x01, "Battery Status" )
	PORT_CONFSETTING( 0x01, DEF_STR( Normal ) )
	PORT_CONFSETTING( 0x00, "Low Battery" )
	PORT_CONFNAME( 0x02, 0x00, "Boot" )
	PORT_CONFSETTING( 0x02, "Cold" )
	PORT_CONFSETTING( 0x00, "Warm" )
INPUT_PORTS_END



//**************************************************************************
//  VIDEO
//**************************************************************************

//-------------------------------------------------
//  contrast_w -
//-------------------------------------------------

void portfolio_state::contrast_w(uint8_t data)
{
	if (LOG) logerror("%s %s CONTRAST %02x\n", machine().time().as_string(), machine().describe_context(), data);
}


//-------------------------------------------------
//  PALETTE_INIT( portfolio )
//-------------------------------------------------

void portfolio_state::portfolio_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(142, 193, 172));
	palette.set_pen_color(1, rgb_t(67, 71, 151));
}


//-------------------------------------------------
//  HD61830_INTERFACE( lcdc_intf )
//-------------------------------------------------

uint8_t portfolio_state::hd61830_rd_r(offs_t offset)
{
	offs_t address = ((offset & 0xff) << 4) | ((offset >> 12) & 0x0f);
	uint8_t data = m_char_rom[address];

	return data;
}


//-------------------------------------------------
//  gfx_layout charlayout
//-------------------------------------------------

static const gfx_layout charlayout =
{
	6, 8,
	256,
	1,
	{ 0 },
	{ 7, 6, 5, 4, 3, 2 },
	{ STEP8(0,8) },
	8*8
};


//-------------------------------------------------
//  GFXDECODE( portfolio )
//-------------------------------------------------

static GFXDECODE_START( gfx_portfolio )
	GFXDECODE_ENTRY( HD61830_TAG, 0, charlayout, 0, 2 )
GFXDECODE_END



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  machine_start
//-------------------------------------------------

void portfolio_state::machine_start()
{
	m_nvram->set_base(m_ram->pointer(), m_ram->size());

	// state saving
	save_item(NAME(m_ip));
	save_item(NAME(m_ie));
	save_item(NAME(m_counter));
	save_item(NAME(m_rom_b));

	m_ip = 0;
}

//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  machine_config( portfolio )
//-------------------------------------------------

void portfolio_state::portfolio(machine_config &config)
{
	// basic machine hardware
	I8088(config, m_maincpu, XTAL(4'915'200));
	m_maincpu->set_addrmap(AS_PROGRAM, &portfolio_state::portfolio_mem);
	m_maincpu->set_addrmap(AS_IO, &portfolio_state::portfolio_io);
	m_maincpu->set_irq_acknowledge_callback(FUNC(portfolio_state::portfolio_int_ack));

	// video hardware
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_LCD));
	screen.set_refresh_hz(72);
	screen.set_screen_update(HD61830_TAG, FUNC(hd61830_device::screen_update));
	screen.set_size(240, 64);
	screen.set_visarea(0, 240-1, 0, 64-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(portfolio_state::portfolio_palette), 2);

	GFXDECODE(config, "gfxdecode", "palette", gfx_portfolio);

	HD61830(config, m_lcdc, XTAL(4'915'200)/2/2);
	m_lcdc->set_addrmap(0, &portfolio_state::portfolio_lcdc);
	m_lcdc->rd_rd_callback().set(FUNC(portfolio_state::hd61830_rd_r));
	m_lcdc->set_screen(SCREEN_TAG);

	POFO_KEYBOARD(config, m_keyboard);
	m_keyboard->int_handler().set(FUNC(portfolio_state::keyboard_int_w));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	PCD3311(config, m_dtmf, XTAL(3'578'640)).add_route(ALL_OUTPUTS, "mono", 0.25);

	// devices
	PORTFOLIO_MEMORY_CARD_SLOT(config, m_ccm, portfolio_memory_cards, nullptr);

	PORTFOLIO_EXPANSION_SLOT(config, m_exp, XTAL(4'915'200), portfolio_expansion_cards, nullptr);
	m_exp->eint_wr_callback().set(FUNC(portfolio_state::eint_w));
	m_exp->nmio_wr_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_exp->wake_wr_callback().set(FUNC(portfolio_state::wake_w));

	TIMER(config, "counter").configure_periodic(FUNC(portfolio_state::counter_tick), attotime::from_hz(XTAL(32'768)/16384));
	TIMER(config, TIMER_TICK_TAG).configure_periodic(FUNC(portfolio_state::system_tick), attotime::from_hz(XTAL(32'768)/32768));

	// software list
	SOFTWARE_LIST(config, "cart_list").set_original("pofo");

	// internal ram
	RAM(config, RAM_TAG).set_default_size("128K");

	NVRAM(config, "nvram", nvram_device::DEFAULT_RANDOM);
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( pofo )
//-------------------------------------------------

ROM_START( pofo )
	ROM_REGION( 0x40000, M80C88A_TAG, 0 )
	ROM_SYSTEM_BIOS( 0, "dip1072", "DIP DOS 1.072" )
	ROMX_LOAD( "c101782-007.u4", 0x00000, 0x20000, CRC(c9852766) SHA1(c74430281bc717bd36fd9b5baec1cc0f4489fe82), ROM_BIOS(0) )
	ROMX_LOAD( "c101781-007.u3", 0x20000, 0x20000, CRC(b8fb730d) SHA1(1b9d82b824cab830256d34912a643a7d048cd401), ROM_BIOS(0) )

	ROM_REGION( 0x8000, HD61830_TAG, 0 )
	ROM_LOAD( "c101783-001a-01.u3", 0x0000, 0x8000, CRC(61fdaff1) SHA1(5eb99e7a19af7b8d77ea8a2f1f554e6e3d382fa2) )
ROM_END

} // Anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME  PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY  FULLNAME     FLAGS
COMP( 1989, pofo, 0,      0,      portfolio, portfolio, portfolio_state, empty_init, "Atari", "Portfolio", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )



poisk1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************

    drivers/poisk1.c

    Driver file for Poisk-1

    to do:
    - cassette i/o and softlist
    - use palette rom
    - monochrome output
    - trap: does memory always get written or it's up to NMI ISR to complete writes?
    - keyboard layout for earliest revision (v89r0)

    slot devices:
    - hard disk controllers
    - network cards
    - joystick, mouse, serial, parallel ports
    - sound card

***************************************************************************/

#include "emu.h"
#include "kb_poisk1.h"

#include "bus/isa/isa.h"
#include "bus/isa/p1_fdc.h"
#include "bus/isa/xsu_cards.h"
#include "cpu/i86/i86.h"
#include "imagedev/cassette.h"
#include "machine/i8255.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "sound/spkrdev.h"
#include "video/cgapal.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


#define LOG_KEYBOARD  (1U << 1)
#define LOG_DEBUG     (1U << 2)

//#define VERBOSE (LOG_DEBUG)
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"

#define LOGKBD(...) LOGMASKED(LOG_KEYBOARD, __VA_ARGS__)
#define LOGDBG(...) LOGMASKED(LOG_DEBUG, __VA_ARGS__)


namespace {

#define CGA_PALETTE_SETS 83
/* one for colour, one for mono, 81 for colour composite */

#define BG_COLOR(x) (((x)&7) | (((x)&0x10) >> 1))

#define POISK1_UPDATE_ROW(name) \
	void name(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint8_t *videoram, uint16_t ma, uint8_t ra, uint8_t stride)


class p1_state : public driver_device
{
public:
	p1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pic8259(*this, "pic8259")
		, m_pit8253(*this, "pit8253")
		, m_ppi8255n1(*this, "ppi8255n1")
		, m_ppi8255n2(*this, "ppi8255n2")
		, m_isabus(*this, "isa")
		, m_speaker(*this, "speaker")
		, m_cassette(*this, "cassette")
		, m_ram(*this, RAM_TAG)
		, m_screen(*this, "screen")
		, m_palette(*this, "palette")
		, m_kbdio(*this, "Y%u", 1)
	{ }

	void poisk1(machine_config &config);

	void init_poisk1();

	void fdc_config(device_t *device);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

	void vsync_changed(int state);
	TIMER_DEVICE_CALLBACK_MEMBER(hsync_changed);

private:
	required_device<cpu_device> m_maincpu;
	required_device<pic8259_device> m_pic8259;
	required_device<pit8253_device> m_pit8253;
	required_device<i8255_device> m_ppi8255n1;
	required_device<i8255_device> m_ppi8255n2;
	required_device<isa8_device> m_isabus;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cassette;
	required_device<ram_device> m_ram;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;

	required_ioport_array<8> m_kbdio;

	uint8_t m_p1_spkrdata = 0;
	uint8_t m_p1_input = 0;

	uint8_t m_kbpoll_mask = 0;
	uint8_t m_vsync = 0;
	uint8_t m_hsync = 0;

	struct
	{
		uint8_t trap[4]{};
		std::unique_ptr<uint8_t[]> videoram_base;
		uint8_t *videoram = nullptr;
		uint8_t mode_control_6a = 0;
		uint8_t color_select_68 = 0;
		uint8_t palette_lut_2bpp[4]{};
		int stride = 0;
		void *update_row(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint8_t *videoram, uint16_t ma, uint8_t ra, uint8_t stride);
	} m_video;

	void p1_palette(palette_device &palette) const;
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void set_palette_luts();
	POISK1_UPDATE_ROW(cga_gfx_2bpp_update_row);
	POISK1_UPDATE_ROW(cga_gfx_1bpp_update_row);
	POISK1_UPDATE_ROW(poisk1_gfx_1bpp_update_row);

	void p1_pit8253_out2_changed(int state);
	void p1_speaker_set_spkrdata(int state);
	uint8_t p1_trap_r(offs_t offset);
	void p1_trap_w(offs_t offset, uint8_t data);
	uint8_t p1_cga_r(offs_t offset);
	void p1_cga_w(offs_t offset, uint8_t data);
	void p1_vram_w(offs_t offset, uint8_t data);

	uint8_t p1_ppi_r(offs_t offset);
	void p1_ppi_w(offs_t offset, uint8_t data);
	void p1_ppi_porta_w(uint8_t data);
	uint8_t p1_ppi_portb_r();
	uint8_t p1_ppi_portc_r();
	void p1_ppi_portc_w(uint8_t data);
	void p1_ppi2_porta_w(uint8_t data);
	void p1_ppi2_portb_w(uint8_t data);
	uint8_t p1_ppi2_portc_r();

	void poisk1_io(address_map &map) ATTR_COLD;
	void poisk1_map(address_map &map) ATTR_COLD;
};

/*
 * onboard devices:
 */

/*
 * Poisk-1 doesn't have a mc6845 and always runs in graphics mode.  Text mode is emulated by BIOS;
 * NMI is triggered on access to video memory and to mc6845 ports.  Address and data are latched into:
 *
 * Port 28H (offset 0) -- lower 8 bits of address
 * Port 29H (offset 1) -- high  -//- and mode bits
 * Port 2AH (offset 2) -- data
 */

uint8_t p1_state::p1_trap_r(offs_t offset)
{
	uint8_t data = m_video.trap[offset];
	LOG("trap R %.2x $%02x\n", 0x28 + offset, data);
	if (offset == 0) m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
	return data;
}

void p1_state::p1_trap_w(offs_t offset, uint8_t data)
{
	LOG("trap W %.2x $%02x\n", 0x28 + offset, data);
}

uint8_t p1_state::p1_cga_r(offs_t offset)
{
	switch (offset)
	{
		case 4: case 5: case 8: case 9:
			LOG("cga R %.4x\n", offset + 0x3d0);
			m_video.trap[2] = 0;
			m_video.trap[1] = 0x43;
			m_video.trap[0] = offset + 0xd0;
			m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
			break;

		case 10:
			return m_ppi8255n2->read(2);

		default:
			break;
	}
	return 0;
}

void p1_state::p1_cga_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
		case 4: case 5: case 8: case 9:
			LOG("cga W %.4x $%02x\n", offset + 0x3d0, data);
			m_video.trap[2] = data;
			m_video.trap[1] = 0xC3;
			m_video.trap[0] = offset + 0xd0;
			m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
			break;

		default:
			break;
	}
}

void p1_state::p1_vram_w(offs_t offset, uint8_t data)
{
	LOGDBG("vram W %.4x $%02x\n", offset, data);
	if (m_video.videoram_base) m_video.videoram_base[offset] = data;
	m_video.trap[2] = data;
	m_video.trap[1] = 0x80 | ((offset >> 8) & 0x3f);
	m_video.trap[0] = offset & 255;
	m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
}

// CGA emulator
/*
068h    D42 0..2    R, G, B     XXX Foreground/Background color
        3   NMI DISABLE NMI trap  1: Disabled  0: Enabled
        4   PALETTE     XXX Colour palette  0: XXX  1: XXX
        5   I (INTENS)  XXX Foreground/Background color intensity
        6   DISPLAY BANK    XXX Video RAM page
        7   HIRES       1: 640x200  0: 320x200
*/

void p1_state::p1_ppi2_porta_w(uint8_t data)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	LOG("color_select_68 W $%02x\n", data);

	// NMI DISABLE
	if (BIT((data ^ m_video.color_select_68), 3))
	{
		if (BIT(data, 3))
			program.install_writeonly(0xb8000, 0xbbfff, m_video.videoram_base.get());
		else
			program.install_write_handler(0xb8000, 0xbbfff, write8sm_delegate(*this, FUNC(p1_state::p1_vram_w)));
	}
	// DISPLAY BANK
	if (BIT((data ^ m_video.color_select_68), 6))
	{

		if (BIT(data, 6))
			m_video.videoram = m_video.videoram_base.get() + 0x4000;
		else
			m_video.videoram = m_video.videoram_base.get();
	}
	// HIRES -- XXX
	if (BIT((data ^ m_video.color_select_68), 7))
	{
		if (BIT(data, 7))
			m_screen->set_visible_area(0, 640 - 1, 0, 200 - 1);
		else
			m_screen->set_visible_area(0, 320 - 1, 0, 200 - 1);
	}
	m_video.color_select_68 = data;
	set_palette_luts();
}

/*
06Ah    Dxx 6   Enable/Disable color burst (?)
        7   Enable/Disable D7H/D7L
*/

void p1_state::p1_ppi_portc_w(uint8_t data)
{
	LOG("mode_control_6a W $%02x\n", data);

	m_video.mode_control_6a = data;
	set_palette_luts();
}

void p1_state::set_palette_luts(void)
{
	/* Setup 2bpp palette lookup table */
	// HIRES
	if (m_video.color_select_68 & 0x80)
	{
		m_video.palette_lut_2bpp[0] = 0;
	}
	else
	{
		m_video.palette_lut_2bpp[0] = BG_COLOR(m_video.color_select_68);
	}
	// B&W -- XXX
/*
    if ( m_video.mode_control_6a & 0x40 )
    {
        m_video.palette_lut_2bpp[1] = ( ( m_video.color_select_68 & 0x20 ) >> 2 ) | 3;
        m_video.palette_lut_2bpp[2] = ( ( m_video.color_select_68 & 0x20 ) >> 2 ) | 4;
        m_video.palette_lut_2bpp[3] = ( ( m_video.color_select_68 & 0x20 ) >> 2 ) | 7;
    }
    else
*/
	{
		// PALETTE
		if ( m_video.color_select_68 & 0x20 )
		{
			m_video.palette_lut_2bpp[1] = ( ( m_video.color_select_68 & 0x20 ) >> 2 ) | 3;
			m_video.palette_lut_2bpp[2] = ( ( m_video.color_select_68 & 0x20 ) >> 2 ) | 5;
			m_video.palette_lut_2bpp[3] = ( ( m_video.color_select_68 & 0x20 ) >> 2 ) | 7;
		}
		else
		{
			m_video.palette_lut_2bpp[1] = ( ( m_video.color_select_68 & 0x20 ) >> 2 ) | 2;
			m_video.palette_lut_2bpp[2] = ( ( m_video.color_select_68 & 0x20 ) >> 2 ) | 4;
			m_video.palette_lut_2bpp[3] = ( ( m_video.color_select_68 & 0x20 ) >> 2 ) | 6;
		}
	}
}

/***************************************************************************
  Draw graphics mode with 320x200 pixels (default) with 2 bits/pixel.
  Even scanlines are from CGA_base + 0x0000, odd from CGA_base + 0x2000
  cga fetches 2 byte per mc6845 access.
***************************************************************************/

POISK1_UPDATE_ROW(p1_state::cga_gfx_2bpp_update_row)
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	uint32_t *p = &bitmap.pix(ra);

	if (ra == 0) LOGDBG("cga_gfx_2bpp_update_row\n");
	uint16_t odd = (ra & 1) << 13;
	uint16_t offset = (ma & 0x1fff) | odd;
	for (int i = 0; i < stride; i++)
	{
		uint8_t data = videoram[ offset++ ];

		*p = palette[m_video.palette_lut_2bpp[ ( data >> 6 ) & 0x03 ]]; p++;
		*p = palette[m_video.palette_lut_2bpp[ ( data >> 4 ) & 0x03 ]]; p++;
		*p = palette[m_video.palette_lut_2bpp[ ( data >> 2 ) & 0x03 ]]; p++;
		*p = palette[m_video.palette_lut_2bpp[   data        & 0x03 ]]; p++;
	}
}

/***************************************************************************
  Draw graphics mode with 640x200 pixels (default).
  The cell size is 1x1 (1 scanline is the real default)
  Even scanlines are from CGA_base + 0x0000, odd from CGA_base + 0x2000
***************************************************************************/

POISK1_UPDATE_ROW(p1_state::cga_gfx_1bpp_update_row)
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	uint32_t *p = &bitmap.pix(ra);
	uint8_t fg = 15, bg = BG_COLOR(m_video.color_select_68);

	if (ra == 0) LOGDBG("cga_gfx_1bpp_update_row bg %d\n", bg);
	uint16_t odd = (ra & 1) << 13;
	uint16_t offset = (ma & 0x1fff) | odd;
	for (int i = 0; i < stride; i++)
	{
		uint8_t data = videoram[ offset++ ];

		*p = palette[( data & 0x80 ) ? fg : bg ]; p++;
		*p = palette[( data & 0x40 ) ? fg : bg ]; p++;
		*p = palette[( data & 0x20 ) ? fg : bg ]; p++;
		*p = palette[( data & 0x10 ) ? fg : bg ]; p++;
		*p = palette[( data & 0x08 ) ? fg : bg ]; p++;
		*p = palette[( data & 0x04 ) ? fg : bg ]; p++;
		*p = palette[( data & 0x02 ) ? fg : bg ]; p++;
		*p = palette[( data & 0x01 ) ? fg : bg ]; p++;
	}
}

/***************************************************************************
  Draw graphics mode with 640x200 pixels + extra highlight color for text
  mode emulation
  Even scanlines are from CGA_base + 0x0000, odd from CGA_base + 0x2000
***************************************************************************/

POISK1_UPDATE_ROW(p1_state::poisk1_gfx_1bpp_update_row)
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	uint32_t *p = &bitmap.pix(ra);
	uint8_t bg = BG_COLOR(m_video.color_select_68);

	if (ra == 0) LOGDBG("poisk1_gfx_1bpp_update_row bg %d\n", bg);
	uint16_t odd = (ra & 1) << 13;
	uint16_t offset = (ma & 0x1fff) | odd;
	for (int i = 0; i < stride; i++)
	{
		uint8_t data = videoram[ offset++ ];

		uint8_t fg = (data & 0x80) ? ( (m_video.color_select_68 & 0x20) ? 10 : 11 ) : 15; // XXX
		*p = palette[bg]; p++;
		*p = palette[( data & 0x40 ) ? fg : bg ]; p++;
		*p = palette[( data & 0x20 ) ? fg : bg ]; p++;
		*p = palette[( data & 0x10 ) ? fg : bg ]; p++;
		*p = palette[( data & 0x08 ) ? fg : bg ]; p++;
		*p = palette[( data & 0x04 ) ? fg : bg ]; p++;
		*p = palette[( data & 0x02 ) ? fg : bg ]; p++;
		*p = palette[( data & 0x01 ) ? fg : bg ]; p++;
	}
}

// Initialise the cga palette
void p1_state::p1_palette(palette_device &palette) const
{
	for (int i = 0; i < CGA_PALETTE_SETS * 16; i++)
		palette.set_pen_color(i, cga_palette[i][0], cga_palette[i][1], cga_palette[i][2]);
}

void p1_state::video_start()
{
	address_space &space = m_maincpu->space(AS_PROGRAM);

	m_video = decltype(m_video)();
	m_video.videoram_base = std::make_unique<uint8_t[]>(0x8000);
	m_video.videoram = m_video.videoram_base.get();
	m_video.stride = 80;

	space.install_ram(0xb8000, 0xbffff, m_video.videoram);
}

void p1_state::vsync_changed(int state)
{
	m_vsync = state ? 9 : 0;
}

TIMER_DEVICE_CALLBACK_MEMBER(p1_state::hsync_changed)
{
	m_hsync = param & 1;
}

uint32_t p1_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	uint16_t ra, ma = 0;

	if (!m_video.stride || !m_video.videoram) return 0;

	// bit 6 of 6Ah disables color burst -- not implemented
	for (ra = cliprect.min_y; ra <= cliprect.max_y; ra++)
	{
		if (BIT(m_video.color_select_68, 7))
		{
			if (BIT(m_video.mode_control_6a, 7))
			{
				cga_gfx_1bpp_update_row(bitmap, cliprect, m_video.videoram, ma, ra, m_video.stride);
			}
			else
			{
				poisk1_gfx_1bpp_update_row(bitmap, cliprect, m_video.videoram, ma, ra, m_video.stride);
			}
		}
		else
		{
			cga_gfx_2bpp_update_row(bitmap, cliprect, m_video.videoram, ma, ra, m_video.stride);
		}
		if (ra & 1) ma += m_video.stride;
	}

	return 0;
}

// Timer.  Poisk-1 uses single XTAL for everything? -- check

void p1_state::p1_speaker_set_spkrdata(int state)
{
	m_p1_spkrdata = state ? 1 : 0;
	m_speaker->level_w(m_p1_spkrdata & m_p1_input);
}

void p1_state::p1_pit8253_out2_changed(int state)
{
	m_p1_input = state ? 1 : 0;
	m_speaker->level_w(m_p1_spkrdata & m_p1_input);
}

// Keyboard (via PPI)

void p1_state::p1_ppi_porta_w(uint8_t data)
{
	m_kbpoll_mask = data;
	LOGDBG("p1_ppi_porta_w %02X <- %02X\n", m_kbpoll_mask, data);
}

uint8_t p1_state::p1_ppi_portb_r()
{
	uint16_t key = 0xffff;
	uint8_t ret = 0;

	for (int i = 0; i < 8; i++)
	{
		if (BIT(m_kbpoll_mask, i))
		{
			key &= m_kbdio[i]->read();
		}
	}

	ret = key & 0xff;
//  LOG("p1_ppi_portb_r = %02X\n", ret);
	return ret;
}

uint8_t p1_state::p1_ppi_portc_r()
{
	uint16_t key = 0xffff;
	uint8_t ret = 0;

	for (int i = 0; i < 8; i++)
	{
		if (BIT(m_kbpoll_mask, i))
		{
			key &= m_kbdio[i]->read();
		}
	}

	ret = (key >> 8) & 0xff;
	LOGDBG("p1_ppi_portc_r = %02X\n", ret);
	return ret;
}

// XXX

uint8_t p1_state::p1_ppi2_portc_r()
{
	int data = m_vsync | m_hsync | 0xc6;
	double tap_val = m_cassette->input();

	data |= (tap_val < 0 ? 0x10 : 0x00);
	data |= (m_p1_input << 5);

	LOGDBG("p1_ppi2_portc_r = %02X (tap_val %f) at %s\n", data, tap_val, machine().describe_context());
	return data;
}

void p1_state::p1_ppi2_portb_w(uint8_t data)
{
	m_pit8253->write_gate2(BIT(data, 0));
	p1_speaker_set_spkrdata(data & 0x02);
}

uint8_t p1_state::p1_ppi_r(offs_t offset)
{
	switch (offset)
	{
	case 0:
		return m_ppi8255n1->read(0);
	case 9:
	case 10:
	case 11:
		return m_ppi8255n1->read(offset - 8);
	case 8:
		return m_ppi8255n2->read(0);
	case 1:
	case 2:
	case 3:
		return m_ppi8255n2->read(offset);
	default:
		LOG("p1ppi R %.2x (unimp)\n", 0x60 + offset);
		return 0xff;
	}
}

void p1_state::p1_ppi_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 0:
		return m_ppi8255n1->write(0, data);
	case 9:
	case 10:
	case 11:
		return m_ppi8255n1->write(offset - 8, data);
	case 8:
		return m_ppi8255n2->write(0, data);
	case 1:
	case 2:
	case 3:
		return m_ppi8255n2->write(offset, data);
	default:
		LOG("p1ppi W %.2x $%02x (unimp)\n", 0x60 + offset, data);
		return;
	}
}

/**********************************************************
 *
 * NMI handling
 *
 **********************************************************/

void p1_state::init_poisk1()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	program.install_ram(0, m_ram->size() - 1, m_ram->pointer());
}

void p1_state::machine_start()
{
}

void p1_state::machine_reset()
{
	m_kbpoll_mask = 0;
	m_hsync = 0;
	m_vsync = 0;
}

/*
 * macros
 */

void p1_state::fdc_config(device_t *device)
{
	p1_fdc_device &fdc = *downcast<p1_fdc_device*>(device);
	fdc.set_cpu(m_maincpu);
}

void p1_state::poisk1_map(address_map &map)
{
	map.unmap_value_high();
	map(0xfc000, 0xfffff).rom().region("bios", 0xc000);
}

// address decoder PROM maps 20-21, 28-2A, 40-43, 60-63, 68-6B, 3D4-3D5, and 3D8-3DA
void p1_state::poisk1_io(address_map &map)
{
	map(0x0020, 0x0021).rw(m_pic8259, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x0028, 0x002A).rw(FUNC(p1_state::p1_trap_r), FUNC(p1_state::p1_trap_w));
	map(0x0040, 0x0043).rw(m_pit8253, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	// can't use regular rw(), because 1st PPI is mapped to 60, 69-6B and 2nd PPI -- to 68, 61-63
	map(0x0060, 0x006B).rw(FUNC(p1_state::p1_ppi_r), FUNC(p1_state::p1_ppi_w));
	map(0x03D0, 0x03DA).rw(FUNC(p1_state::p1_cga_r), FUNC(p1_state::p1_cga_w));
}

static INPUT_PORTS_START( poisk1 )
	PORT_INCLUDE( poisk1_keyboard_v91 )
INPUT_PORTS_END

void p1_state::poisk1(machine_config &config)
{
	/* basic machine hardware */
	I8088(config, m_maincpu, 5000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &p1_state::poisk1_map);
	m_maincpu->set_addrmap(AS_IO, &p1_state::poisk1_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259", FUNC(pic8259_device::inta_cb));

	PIT8253(config, m_pit8253);
	m_pit8253->set_clk<0>(XTAL(15'000'000)/12); /* heartbeat IRQ */
	m_pit8253->out_handler<0>().set(m_pic8259, FUNC(pic8259_device::ir0_w));
	m_pit8253->set_clk<1>(XTAL(15'000'000)/12); /* keyboard poll -- XXX edge or level triggered? */
	m_pit8253->out_handler<1>().set(m_pic8259, FUNC(pic8259_device::ir6_w));
	m_pit8253->set_clk<2>(XTAL(15'000'000)/12); /* pio port c pin 4, and speaker polling enough */
	m_pit8253->out_handler<2>().set(FUNC(p1_state::p1_pit8253_out2_changed));

	PIC8259(config, m_pic8259);
	m_pic8259->out_int_callback().set_inputline(m_maincpu, 0);

	I8255A(config, m_ppi8255n1);
	m_ppi8255n1->out_pa_callback().set(FUNC(p1_state::p1_ppi_porta_w)); // 60h
	m_ppi8255n1->in_pb_callback().set(FUNC(p1_state::p1_ppi_portb_r)); // 69h
	m_ppi8255n1->in_pc_callback().set(FUNC(p1_state::p1_ppi_portc_r)); // 6Ah
	m_ppi8255n1->out_pc_callback().set(FUNC(p1_state::p1_ppi_portc_w));

	I8255A(config, m_ppi8255n2);
	m_ppi8255n2->out_pa_callback().set(FUNC(p1_state::p1_ppi2_porta_w)); // 68h
	m_ppi8255n2->out_pb_callback().set(FUNC(p1_state::p1_ppi2_portb_w)); // 61h
	m_ppi8255n2->in_pc_callback().set(FUNC(p1_state::p1_ppi2_portc_r)); // 62h and 3DAh

	ISA8(config, m_isabus, 0);
	m_isabus->set_memspace("maincpu", AS_PROGRAM);
	m_isabus->set_iospace("maincpu", AS_IO);
	m_isabus->irq2_callback().set(m_pic8259, FUNC(pic8259_device::ir2_w));
	m_isabus->irq3_callback().set(m_pic8259, FUNC(pic8259_device::ir3_w));
	m_isabus->irq4_callback().set(m_pic8259, FUNC(pic8259_device::ir4_w));
	m_isabus->irq5_callback().set(m_pic8259, FUNC(pic8259_device::ir5_w));
	m_isabus->irq7_callback().set(m_pic8259, FUNC(pic8259_device::ir7_w));
	m_isabus->iochrdy_callback().set_inputline(m_maincpu, INPUT_LINE_HALT);

	// FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa1", 0, m_isabus, p1_isa8_cards, "fdc", false).set_option_machine_config("fdc", [this](device_t *device) { fdc_config(device); });
	ISA8_SLOT(config, "isa2", 0, m_isabus, p1_isa8_cards, nullptr, false).set_option_machine_config("fdc", [this](device_t *device) { fdc_config(device); });
	ISA8_SLOT(config, "isa3", 0, m_isabus, p1_isa8_cards, nullptr, false).set_option_machine_config("fdc", [this](device_t *device) { fdc_config(device); });
	ISA8_SLOT(config, "isa4", 0, m_isabus, p1_isa8_cards, nullptr, false).set_option_machine_config("fdc", [this](device_t *device) { fdc_config(device); });

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);

	// fake hsync
	TIMER(config, "scantimer").configure_scanline(FUNC(p1_state::hsync_changed), "screen", 0, 1);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(XTAL(15'000'000), 912,0,640, 262,0,200);
	m_screen->set_screen_update(FUNC(p1_state::screen_update));
	m_screen->screen_vblank().set(FUNC(p1_state::vsync_changed));

	/* XXX verify palette */
	PALETTE(config, m_palette, FUNC(p1_state::p1_palette), CGA_PALETTE_SETS * 16);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	SOFTWARE_LIST(config, "flop_list").set_original("poisk1_flop");
//  SOFTWARE_LIST(config, "cass_list").set_original("poisk1_cass");

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("512K");
}

ROM_START( poisk1 )
	ROM_REGION(0x10000, "bios", 0)

	ROM_DEFAULT_BIOS("v91")
	ROM_SYSTEM_BIOS(0, "v89r0", "1989r0")
	ROMX_LOAD("bios.rf6", 0xe000, 0x2000, CRC(c0f333e3) SHA1(a44f355b7deae3693e1462d57543a42944fd0969), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v89", "1989")
	ROMX_LOAD("biosp1s.rf4", 0xe000, 0x2000, CRC(1a85f671) SHA1(f0e59b2c4d92164abca55a96a58071ce869ff988), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v91", "1991")
	ROMX_LOAD("poisk_1991.bin", 0xe000, 0x2000, CRC(d61c56fd) SHA1(de202e1f7422d585a1385a002a4fcf9d756236e5), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v91r2", "1991r2")
	ROMX_LOAD("p_bios_nm.bin", 0xe000, 0x2000, CRC(84430b4f) SHA1(3e477962be3cea09662cb2e3ad9966ad01c7455d), ROM_BIOS(3))

	ROM_SYSTEM_BIOS(4, "test1", "Test 1")
	ROMX_LOAD("test1.rf6", 0x00000, 0x2000, CRC(a5f05dff) SHA1(21dd0cea605bd7be22e94f8355d86b2478d9527e), ROM_BIOS(4))
	ROM_SYSTEM_BIOS(5, "test2", "Test 2")
	ROMX_LOAD("test2.rf6", 0x00000, 0x2000, CRC(eff730e4) SHA1(fcbc08de9b8592c974eaea837839f1a9caf36a75), ROM_BIOS(5))
	ROM_SYSTEM_BIOS(6, "test3", "Test 3")
	ROMX_LOAD("test3.rf6", 0x00000, 0x2000, CRC(23025dc9) SHA1(dca4cb580162bb28f6e49ff625b677001d40d573), ROM_BIOS(6))
	ROM_SYSTEM_BIOS(7, "test4", "Test 4")
	ROMX_LOAD("test4.rf6", 0x00000, 0x2000, CRC(aac8fc5e) SHA1(622abb5ac66d38a474ee54fe016aff0ba0b5794f), ROM_BIOS(7))
	ROM_SYSTEM_BIOS(8, "test5", "Test 5")
	ROMX_LOAD("test5.rf6", 0x00000, 0x2000, CRC(f308e679) SHA1(37bd35f62015d338b3347fd4e3ec455eab048b66), ROM_BIOS(8))

	// 0xc0000, sets 80x25 text and loops asking for 'Boot from hard disk (Y or N)?'
	ROM_LOAD("boot_net.rf4", 0x00000, 0x2000, CRC(316c2030) SHA1(d043325596455772252e465b85321f1b5c529d0b)) // NET BIOS
	// 0xc0000, accesses ports 0x90..0x97
	ROM_LOAD("pois_net.bin", 0x00000, 0x2000, CRC(cf9dd80a) SHA1(566bcb40c0cb2c8bfd5b485f0db689fdeaca3e86)) // ??? BIOS

	ROM_REGION(0x2000,"gfx1", ROMREGION_ERASE00)
	ROM_LOAD("poisk.cga", 0x0000, 0x0800, CRC(f6eb39f0) SHA1(0b788d8d7a8e92cc612d044abcb2523ad964c200))
ROM_END

} // anonymous namespace


/***************************************************************************

  Game driver(s)

***************************************************************************/

//    YEAR  NAME    PARENT   COMPAT  MACHINE  INPUT   CLASS     INIT         COMPANY         FULLNAME   FLAGS
COMP( 1989, poisk1, ibm5150, 0,      poisk1,  poisk1, p1_state, init_poisk1, "Electronmash", "Poisk-1", 0 )



pokemini.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/********************************************************************

Driver file to handle emulation of the Nintendo Pokemon Mini handheld
by Wilbert Pol.

The LCD is likely to be a SSD1828 LCD.

********************************************************************/

#include "emu.h"
#include "cpu/minx/minx.h"
#include "machine/i2cmem.h"
#include "sound/spkrdev.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class pokemini_state : public driver_device
{
public:
	pokemini_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_p_ram(*this, "p_ram")
		, m_speaker(*this, "speaker")
		, m_i2cmem(*this, "i2cmem")
		, m_cart(*this, "cartslot")
		, m_inputs(*this, "INPUTS")
	{ }

	void pokemini(machine_config &config);

protected:
	virtual void video_start() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	struct PRC
	{
		uint8_t     colors_inverted = 0;
		uint8_t     background_enabled = 0;
		uint8_t     sprites_enabled = 0;
		uint8_t     copy_enabled = 0;
		uint8_t     map_size = 0;
		uint8_t     map_size_x = 0;
		uint8_t     frame_count = 0;
		uint8_t     max_frame_count = 0;
		uint32_t    bg_tiles = 0;
		uint32_t    spr_tiles = 0;
		uint8_t     count = 0;
		emu_timer   *count_timer = nullptr;
	};


	struct TIMERS
	{
		emu_timer   *seconds_timer = nullptr;
		emu_timer   *hz256_timer = nullptr;
		emu_timer   *timer1 = nullptr;                // Timer 1 low or 16bit
		emu_timer   *timer1_hi = nullptr;             // Timer 1 hi
		emu_timer   *timer2 = nullptr;                // Timer 2 low or 16bit
		emu_timer   *timer2_hi = nullptr;             // Timer 2 high
		emu_timer   *timer3 = nullptr;                // Timer 3 low or 16bit
		emu_timer   *timer3_hi = nullptr;             // Timer 3 high
	};

	uint8_t m_pm_reg[0x100]{};
	PRC m_prc;
	TIMERS m_timers;
	bitmap_ind16 m_bitmap;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_shared_ptr<uint8_t> m_p_ram;
	required_device<speaker_sound_device> m_speaker;
	required_device<i2cmem_device> m_i2cmem;
	required_device<generic_slot_device> m_cart;
	required_ioport m_inputs;

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void pokemini_palette(palette_device &palette) const;
	void hwreg_w(offs_t offset, uint8_t data);
	uint8_t hwreg_r(offs_t offset);
	uint8_t rom_r(offs_t offset);
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	void pokemini_mem_map(address_map &map) ATTR_COLD;

	void check_irqs();
	void update_sound();
	TIMER_CALLBACK_MEMBER(seconds_timer_callback);
	TIMER_CALLBACK_MEMBER(timer_256hz_callback);
	TIMER_CALLBACK_MEMBER(timer1_callback);
	TIMER_CALLBACK_MEMBER(timer1_hi_callback);
	TIMER_CALLBACK_MEMBER(timer2_callback);
	TIMER_CALLBACK_MEMBER(timer2_hi_callback);
	TIMER_CALLBACK_MEMBER(timer3_callback);
	TIMER_CALLBACK_MEMBER(timer3_hi_callback);
	TIMER_CALLBACK_MEMBER(prc_counter_callback);
};


uint8_t pokemini_state::rom_r(offs_t offset)
{
	offset += 0x2100;
	return m_cart->read_rom(offset & 0x1fffff);
}

void pokemini_state::pokemini_mem_map(address_map &map)
{
	map(0x000000, 0x000fff).rom();                            /* bios */
	map(0x001000, 0x001fff).ram().share("p_ram");          /* VRAM/RAM */
	map(0x002000, 0x0020ff).rw(FUNC(pokemini_state::hwreg_r), FUNC(pokemini_state::hwreg_w));    /* hardware registers */
	map(0x002100, 0x1fffff).r(FUNC(pokemini_state::rom_r));                    /* cartridge area */
}


static INPUT_PORTS_START( pokemini )
	PORT_START("INPUTS")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("Button A")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("Button B")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3) PORT_NAME("Button C")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP) PORT_NAME("Up")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN) PORT_NAME("Down")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT) PORT_NAME("Left")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_NAME("Right")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START1) PORT_NAME("Power")
INPUT_PORTS_END


void pokemini_state::pokemini_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(0xff, 0xfb, 0x87));
	palette.set_pen_color(1, rgb_t(0xb1, 0xae, 0x4e));
	palette.set_pen_color(2, rgb_t(0x84, 0x80, 0x4e));
	palette.set_pen_color(3, rgb_t(0x4e, 0x4e, 0x4e));
}


void pokemini_state::check_irqs()
{
	int irq_set[4] = { 1, 0, 0, 0 };
	int prio, vector;

	/* Check IRQ $03-$04 */
	prio = ( m_pm_reg[0x20] >> 6 ) & 0x03;
	if ( ! irq_set[prio] )
	{
		if ( m_pm_reg[0x23] & m_pm_reg[0x27] & 0x40 )
			irq_set[prio] = 0x04;

		if ( m_pm_reg[0x23] & m_pm_reg[0x27] & 0x80 )
			irq_set[prio] = 0x03;
	}

	/* Check IRQ $05-$06 */
	prio = ( m_pm_reg[0x20] >> 4 ) & 0x03;
	if ( ! irq_set[prio] )
	{
		if ( m_pm_reg[0x23] & m_pm_reg[0x27] & 0x10 )
			irq_set[prio] = 0x06;

		if ( m_pm_reg[0x23] & m_pm_reg[0x27] & 0x20 )
			irq_set[prio] = 0x05;
	}

	/* Check IRQ $07-$08 */
	prio = ( m_pm_reg[0x20] >> 2 ) & 0x03;
	if ( ! irq_set[prio] )
	{
		if ( m_pm_reg[0x23] & m_pm_reg[0x27] & 0x04 )
			irq_set[prio] = 0x08;

		if ( m_pm_reg[0x23] & m_pm_reg[0x27] & 0x08 )
			irq_set[prio] = 0x07;
	}

	/* Check IRQ $09-$0A */
	prio = ( m_pm_reg[0x20] >> 0 ) & 0x03;
	if ( ! irq_set[prio] )
	{
		if ( m_pm_reg[0x23] & m_pm_reg[0x27] & 0x01 )
			irq_set[prio] = 0x0A;

		if ( m_pm_reg[0x23] & m_pm_reg[0x27] & 0x02 )
			irq_set[prio] = 0x09;
	}

	/* Check IRQ $0B-$0E */
	prio = ( m_pm_reg[0x21] >> 6 ) & 0x03;
	if ( ! irq_set[prio] )
	{
		if ( m_pm_reg[0x24] & m_pm_reg[0x28] & 0x04 )
			irq_set[prio] = 0x0E;

		if ( m_pm_reg[0x24] & m_pm_reg[0x28] & 0x08 )
			irq_set[prio] = 0x0D;

		if ( m_pm_reg[0x24] & m_pm_reg[0x28] & 0x10 )
			irq_set[prio] = 0x0C;

		if ( m_pm_reg[0x24] & m_pm_reg[0x28] & 0x20 )
			irq_set[prio] = 0x0B;
	}

	/* Check IRQ $0F-$10 */
	prio = ( m_pm_reg[0x22] >> 0 ) & 0x03;
	if ( ! irq_set[prio] )
	{
		if ( m_pm_reg[0x26] & m_pm_reg[0x2A] & 0x40 )
			irq_set[prio] = 0x10;

		if ( m_pm_reg[0x26] & m_pm_reg[0x2A] & 0x80 )
			irq_set[prio] = 0x0F;
	}

	/* Check IRQ $13-$14 */
	prio = ( m_pm_reg[0x21] >> 4 ) & 0x03;
	if ( ! irq_set[prio] )
	{
		if ( m_pm_reg[0x24] & m_pm_reg[0x28] & 0x01 )
			irq_set[prio] = 0x14;

		if ( m_pm_reg[0x24] & m_pm_reg[0x28] & 0x02 )
			irq_set[prio] = 0x13;
	}

	/* Check IRQ $15-$1C */
	prio = ( m_pm_reg[0x21] >> 2 ) & 0x03;
	if ( ! irq_set[prio] )
	{
		if ( m_pm_reg[0x25] & m_pm_reg[0x29] & 0x01 )
			irq_set[prio] = 0x1C;

		if ( m_pm_reg[0x25] & m_pm_reg[0x29] & 0x02 )
			irq_set[prio] = 0x1B;

		if ( m_pm_reg[0x25] & m_pm_reg[0x29] & 0x04 )
			irq_set[prio] = 0x1A;

		if ( m_pm_reg[0x25] & m_pm_reg[0x29] & 0x08 )
			irq_set[prio] = 0x19;

		if ( m_pm_reg[0x25] & m_pm_reg[0x29] & 0x10 )
			irq_set[prio] = 0x18;

		if ( m_pm_reg[0x25] & m_pm_reg[0x29] & 0x20 )
			irq_set[prio] = 0x17;

		if ( m_pm_reg[0x25] & m_pm_reg[0x29] & 0x40 )
			irq_set[prio] = 0x16;

		if ( m_pm_reg[0x25] & m_pm_reg[0x29] & 0x80 )
			irq_set[prio] = 0x15;
	}

	/* Check IRQ $1D-$1F */
	prio = ( m_pm_reg[0x21] >> 0 ) & 0x03;
	if ( ! irq_set[prio] && ( m_pm_reg[0x26] & m_pm_reg[0x2A] & 0x07 ) )
	{
		if ( m_pm_reg[0x26] & m_pm_reg[0x2A] & 0x01 )
			irq_set[prio] = 0x1F;

		if ( m_pm_reg[0x26] & m_pm_reg[0x2A] & 0x02 )
			irq_set[prio] = 0x1E;

		if ( m_pm_reg[0x26] & m_pm_reg[0x2A] & 0x04 )
			irq_set[prio] = 0x1D;
	}

	/* Determine vector */
	vector = 0;
	if ( irq_set[1] )
		vector = irq_set[1];
	if ( irq_set[2] )
		vector = irq_set[2];
	if ( irq_set[3] )
		vector = irq_set[3];

	if ( vector )
	{
		//logerror("Triggering IRQ with vector %02x\n", vector );
		/* Trigger interrupt and set vector */
		m_maincpu->set_input_line_and_vector(0, ASSERT_LINE, vector ); // MINX
	}
	else
	{
		m_maincpu->set_input_line(0, CLEAR_LINE );
	}
}


void pokemini_state::update_sound()
{
	/* Check if sound should be muted */
	if ( m_pm_reg[0x70] & 0x03 )
	{
		m_speaker->level_w(0);
	}
	else
	{
		///static const int levels[4] = { 0, 1, 1, 2 };
		int level; /// silence clang warning/// = levels[ m_pm_reg[0x71] & 0x03 ];

//      if ( ( ( m_pm_reg[0x48] & 0x80 ) && ( m_pm_reg[0x4E] | ( m_pm_reg[0x4F] << 8 ) ) > ( m_pm_reg[0x4C] | ( m_pm_reg[0x4D] << 8 ) ) )
//        || ( ( m_pm_reg[0x48] & 0x80 ) && m_pm_reg[0x4F] > m_pm_reg[0x4D] ) )
//      {
			level = 0;
//      }

		m_speaker->level_w(level);
	}
}


TIMER_CALLBACK_MEMBER(pokemini_state::seconds_timer_callback)
{
	if ( m_pm_reg[0x08] & 0x01 )
	{
		m_pm_reg[0x09] += 1;
		if ( ! m_pm_reg[0x09] )
		{
			m_pm_reg[0x0A] += 1;
			if ( ! m_pm_reg[0x0A] )
			{
				m_pm_reg[0x0B] += 1;
			}
		}
	}
}


TIMER_CALLBACK_MEMBER(pokemini_state::timer_256hz_callback)
{
	if ( m_pm_reg[0x40] & 0x01 )
	{
		m_pm_reg[0x41] += 1;
		/* Check if the 32Hz IRQ should be triggered */
		if ( ! ( m_pm_reg[0x41] & 0x07 ) )
		{
			m_pm_reg[0x28] |= 0x20;

			/* Check if the 8Hz IRQ should be triggered */
			if ( ! ( m_pm_reg[0x41] & 0x1F ) )
			{
				m_pm_reg[0x28] |= 0x10;

				/* Check if the 2Hz IRQ should be triggered */
				if ( ! ( m_pm_reg[0x41] & 0x7F ) )
				{
					m_pm_reg[0x28] |= 0x08;

					/* Check if the 1Hz IRQ should be triggered */
					if ( ! m_pm_reg[0x41] )
					{
						m_pm_reg[0x28] |= 0x04;
					}
				}
			}

			check_irqs();
		}
	}
}


TIMER_CALLBACK_MEMBER(pokemini_state::timer1_callback)
{
	m_pm_reg[0x36] -= 1;
	/* Check for underflow of timer */
	if ( m_pm_reg[0x36] == 0xFF )
	{
		/* Check if timer1 is running in 16bit mode */
		if ( m_pm_reg[0x30] & 0x80 )
		{
			m_pm_reg[0x37] -= 1;
			if ( m_pm_reg[0x37] == 0xFF )
			{
				m_pm_reg[0x27] |= 0x08;
				check_irqs();
				m_pm_reg[0x36] = m_pm_reg[0x32];
				m_pm_reg[0x37] = m_pm_reg[0x33];
			}
		}
		else
		{
			m_pm_reg[0x27] |= 0x04;
			check_irqs();
			m_pm_reg[0x36] = m_pm_reg[0x32];
		}
	}
}


TIMER_CALLBACK_MEMBER(pokemini_state::timer1_hi_callback)
{
	m_pm_reg[0x37] -= 1;
	/* Check for underflow of timer */
	if ( m_pm_reg[0x37] == 0xFF )
	{
		m_pm_reg[0x27] |= 0x08;
		check_irqs();
		m_pm_reg[0x37] = m_pm_reg[0x33];
	}
}


TIMER_CALLBACK_MEMBER(pokemini_state::timer2_callback)
{
	m_pm_reg[0x3E] -= 1;
	/* Check for underflow of timer */
	if ( m_pm_reg[0x3E] == 0xFF )
	{
		/* Check if timer2 is running in 16bit mode */
		if ( m_pm_reg[0x38] & 0x80 )
		{
			m_pm_reg[0x3F] -= 1;
			if ( m_pm_reg[0x3F] == 0xFF )
			{
				m_pm_reg[0x27] |= 0x20;
				check_irqs();
				m_pm_reg[0x3E] = m_pm_reg[0x3A];
				m_pm_reg[0x3F] = m_pm_reg[0x3B];
			}
		}
		else
		{
			m_pm_reg[0x27] |= 0x10;
			check_irqs();
			m_pm_reg[0x3E] = m_pm_reg[0x3A];
		}
	}
}


TIMER_CALLBACK_MEMBER(pokemini_state::timer2_hi_callback)
{
	m_pm_reg[0x3F] -= 1;
	/* Check for underfow of timer */
	if ( m_pm_reg[0x3F] == 0xFF )
	{
		m_pm_reg[0x27] |= 0x20;
		check_irqs();
		m_pm_reg[0x3F] = m_pm_reg[0x3B];
	}
}


TIMER_CALLBACK_MEMBER(pokemini_state::timer3_callback)
{
	m_pm_reg[0x4E] -= 1;
	/* Check for underflow of timer */
	if ( m_pm_reg[0x4E] == 0xFF )
	{
		/* Check if timer3 is running in 16bit mode */
		if ( m_pm_reg[0x48] & 0x80 )
		{
			m_pm_reg[0x4F] -= 1;
			if ( m_pm_reg[0x4F] == 0xFF )
			{
				m_pm_reg[0x27] |= 0x02;
				check_irqs();
				m_pm_reg[0x4E] = m_pm_reg[0x4A];
				m_pm_reg[0x4F] = m_pm_reg[0x4B];
			}
		}
		else
		{
			m_pm_reg[0x4E] = m_pm_reg[0x4A];
		}
	}

	if ( m_pm_reg[0x48] & 0x80 )
	{
		if (  ( m_pm_reg[0x4E] == m_pm_reg[0x4C] ) && ( m_pm_reg[0x4F] == m_pm_reg[0x4D] ) )
		{
			m_pm_reg[0x27] |= 0x01;
			check_irqs();
		}
		update_sound();
	}
}


TIMER_CALLBACK_MEMBER(pokemini_state::timer3_hi_callback)
{
	m_pm_reg[0x4F] -= 1;
	/* Check for underflow of timer */
	if ( m_pm_reg[0x4F] == 0xFF )
	{
		m_pm_reg[0x27] |= 0x02;
		check_irqs();
		m_pm_reg[0x4F] = m_pm_reg[0x4B];
	}

	if ( ! ( m_pm_reg[0x48] & 0x80 ) )
	{
		if( m_pm_reg[0x4F] == m_pm_reg[0x4D] )
		{
			m_pm_reg[0x27] |= 0x01;
			check_irqs();
		}
		update_sound();
	}
}


void pokemini_state::hwreg_w(offs_t offset, uint8_t data)
{
	static const int timer_to_cycles_fast[8] = { 2, 8, 32, 64, 128, 256, 1024, 4096 };
	static const int timer_to_cycles_slow[8] = { 128, 256, 512, 1024, 2048, 4096, 8192, 16384 };

	//logerror( "%0X: Write to hardware address: %02X, %02X\n", m_maincpu->pc(), offset, data );

	switch( offset )
	{
	case 0x00:  /* start-up contrast
	           Bit 0-1 R/W Must be 1(?)
	           Bit 2-7 R/W Start up contrast (doesn't affect contrast until after reboot)
	        */
	case 0x01:  /* CPU related?
	           Bit 0-7 R/W Unknown
	        */
	case 0x02:  /* CPU related?
	           Bit 0-7 R/W Unknown
	        */
		logerror( "%0X: Write to unknown hardware address: %02X, %02X\n", m_maincpu->pc(), offset, data );
		break;
	case 0x08:  /* Seconds-timer control
	           Bit 0   R/W Timer enable
	           Bit 1   W   Timer reset
	           Bit 2-7     Unused
	        */
		if ( data & 0x02 )
		{
			m_pm_reg[0x09] = 0x00;
			m_pm_reg[0x0A] = 0x00;
			m_pm_reg[0x0B] = 0x00;
			data &= ~0x02;
		}
		break;
	case 0x09:  /* Seconds-timer (low), read only
	           Bit 0-7 R   Seconds timer bit 0-7
	        */
		return;
	case 0x0A:  /* Seconds-timer (mid), read only
	           Bit 0-7 R   Seconds timer bit 8-15
	        */
		return;
	case 0x0B:  /* Seconds-timer (high), read only
	           Bit 0-7 R   Seconds timer bit 16-23
	        */
		return;
	case 0x10:  /* Low power detector
	           Bit 0-4 R/W Unknown
	           Bit 5   R   Battery status: 0 - battery OK, 1 - battery low
	           Bit 6-7     Unused
	        */
		logerror( "%0X: Write to unknown hardware address: %02X, %02X\n", m_maincpu->pc(), offset, data );
		break;
	case 0x18:  /* Timer 1 pre-scale + enable
	           Bit 0-2 R/W low timer 1 prescaler select
	                       000 - 2 or 128 cycles
	                       001 - 8 or 256 cycles
	                       010 - 32 or 512 cycles
	                       011 - 64 or 1024 cycles
	                       100 - 128 or 2048 cycles
	                       101 - 256 or 4096 cycles
	                       110 - 1024 or 8192 cycles
	                       111 - 4096 or 16384 cycles
	           Bit 3   R/W Enable low counting
	           Bit 4-6 R/W high timer 1 prescaler select
	           Bit 7   R/W Enable high counting
	        */
		/* Check for prescaler change for the low counter */
		if ( ( data & 0x07 ) != ( m_pm_reg[0x18] & 0x07 ) )
		{
			int index = data & 0x07;
			int cycles = ( m_pm_reg[0x19] & 0x01 ) ? timer_to_cycles_slow[index] : timer_to_cycles_fast[index];

			m_timers.timer1->adjust(attotime::zero, 0, m_maincpu->cycles_to_attotime(cycles));
		}

		/* Check for prescaler change for the high counter */
		if ( ( data & 0x70 ) != ( m_pm_reg[0x18] & 0x70 ) )
		{
			int index = ( data >> 4 ) & 0x07;
			int cycles = ( m_pm_reg[0x19] & 0x02 ) ? timer_to_cycles_slow[index] : timer_to_cycles_fast[index];

			m_timers.timer1_hi->adjust(attotime::zero, 0, m_maincpu->cycles_to_attotime(cycles));
		}

		/* Check if timer1 low should be enabled */
		if ( ( data & 0x08 ) && ( m_pm_reg[0x30] & 0x04 ) &&
				( ( ( m_pm_reg[0x19] & 0x10 ) && ( m_pm_reg[0x19] & 0x01 ) ) ||
				( ( m_pm_reg[0x19] & 0x20 ) && ! ( m_pm_reg[0x19] & 0x01 ) ) ) )
		{
			m_timers.timer1->enable( 1 );
		}
		else
		{
			m_timers.timer1->enable( 0 );
		}

		/* Check if timer1 high should be enabled */
		if ( ( data & 0x80 ) && ( m_pm_reg[0x31] & 0x04 ) && ! ( m_pm_reg[0x30] & 0x80 ) &&
				( ( ( m_pm_reg[0x19] & 0x10 ) && ( m_pm_reg[0x19] & 0x02 ) ) ||
				( ( m_pm_reg[0x19] & 0x20 ) && ! ( m_pm_reg[0x19] & 0x02 ) ) ) )
		{
			m_timers.timer1_hi->enable( 1 );
		}
		else
		{
			m_timers.timer1_hi->enable( 0 );
		}
		break;
	case 0x19:  /* Timers 1 speed
	           Bit 0   R/W Select slow timer for timer 1 lo
	           Bit 1   R/W Select slow timer for timer 1 hi
	           Bit 2-3     Unused
	           Bit 4   R/W Enable slow timers
	           Bit 5   R/W Enable fast timers
	           Bit 6-7     Unused
	        */
		/* Check for prescaler change for the high counter */
		if ( ( data & 0x01 ) != ( m_pm_reg[0x19] & 0x01 ) )
		{
			int index = m_pm_reg[0x18] & 0x07;
			int cycles = ( data & 0x01 ) ? timer_to_cycles_slow[index] : timer_to_cycles_fast[index];

			m_timers.timer1->adjust(attotime::zero, 0, m_maincpu->cycles_to_attotime(cycles));
		}

		/* Check for prescaler change for the low counter */
		if ( ( data & 0x02 ) != ( m_pm_reg[0x19] & 0x02 ) )
		{
			int index = ( m_pm_reg[0x18] >> 4 ) & 0x07;
			int cycles = ( data & 0x02 ) ? timer_to_cycles_slow[index] : timer_to_cycles_fast[index];

			m_timers.timer1_hi->adjust(attotime::zero, 0, m_maincpu->cycles_to_attotime(cycles));
		}

		{
			int timer1_enable = 0, timer1_hi_enable = 0;
			int timer2_enable = 0, timer2_hi_enable = 0;
			int timer3_enable = 0, timer3_hi_enable = 0;

			/* Check which fast timers should be enabled */
			if ( data & 0x20 )
			{
				if ( ( m_pm_reg[0x18] & 0x08 ) && ( m_pm_reg[0x30] & 0x04 ) && ! ( data & 0x01 ) )
					timer1_enable = 1;

				if ( ( m_pm_reg[0x18] & 0x80 ) && ( m_pm_reg[0x31] & 0x04 ) && ! ( m_pm_reg[0x30] & 0x80 ) && ! ( data & 0x02 ) )
					timer1_hi_enable = 1;

				if ( ( m_pm_reg[0x1A] & 0x08 ) && ( m_pm_reg[0x38] & 0x04 ) && ! ( m_pm_reg[0x1B] & 0x01 ) )
					timer2_enable = 1;

				if ( ( m_pm_reg[0x1A] & 0x80 ) && ( m_pm_reg[0x39] & 0x04 ) && ! ( m_pm_reg[0x38] & 0x80 ) && ! ( m_pm_reg[0x1B] & 0x02 ) )
					timer2_hi_enable = 1;

				if ( ( m_pm_reg[0x1C] & 0x08 ) && ( m_pm_reg[0x48] & 0x04 ) && ! ( m_pm_reg[0x1D] & 0x01 ) )
					timer3_enable = 1;

				if ( ( m_pm_reg[0x1C] & 0x80 ) && ( m_pm_reg[0x49] & 0x04 ) && ! ( m_pm_reg[0x48] & 0x80 ) && ! ( m_pm_reg[0x1D] & 0x02 ) )
					timer3_hi_enable = 1;
			}

			/* Check which slow timers should be enabled */
			if ( data & 0x10 )
			{
				if ( ( m_pm_reg[0x18] & 0x08 ) && ( data & 0x01 ) )
					timer1_enable = 1;

				if ( ( m_pm_reg[0x1A] & 0x08 ) && ( m_pm_reg[0x1B] & 0x01 ) )
					timer2_enable = 1;

				if ( ( m_pm_reg[0x1C] & 0x08 ) && ( m_pm_reg[0x1D] & 0x01 ) )
					timer3_enable = 1;
			}
			m_timers.timer1->enable( timer1_enable );
			m_timers.timer1_hi->enable( timer1_hi_enable );
			m_timers.timer2->enable( timer2_enable );
			m_timers.timer2_hi->enable( timer2_hi_enable );
			m_timers.timer3->enable( timer3_enable );
			m_timers.timer3_hi->enable( timer3_hi_enable );
		}
		break;
	case 0x1A:  /* Timer 2 pre-scale + enable
	           Bit 0-2 R/W low timer 2 prescaler select
	                       000 - 2 or 128 cycles
	                       001 - 8 or 256 cycles
	                       010 - 32 or 512 cycles
	                       011 - 64 or 1024 cycles
	                       100 - 128 or 2048 cycles
	                       101 - 256 or 4096 cycles
	                       110 - 1024 or 8192 cycles
	                       111 - 4096 or 16384 cycles
	           Bit 3   R/W Enable low counting
	           Bit 4-6 R/W high timer 2 prescaler select
	           Bit 7   R/W Enable high counting
	        */
		/* Check for prescaler change for the low counter */
		if ( ( data & 0x07 ) != ( m_pm_reg[0x1A] & 0x07 ) )
		{
			int index = data & 0x07;
			int cycles = ( m_pm_reg[0x1B] & 0x01 ) ? timer_to_cycles_slow[index] : timer_to_cycles_fast[index];

			m_timers.timer2->adjust(attotime::zero, 0, m_maincpu->cycles_to_attotime(cycles));
		}

		/* Check for prescaler change for the high counter */
		if ( ( data & 0x70 ) != ( m_pm_reg[0x1A] & 0x70 ) )
		{
			int index = ( data >> 4 ) & 0x07;
			int cycles = ( m_pm_reg[0x1B] & 0x02 ) ? timer_to_cycles_slow[index] : timer_to_cycles_fast[index];

			m_timers.timer2_hi->adjust(attotime::zero, 0, m_maincpu->cycles_to_attotime(cycles));
		}

		/* Check if timer2 low should be enabled */
		if ( ( data & 0x08 ) && ( m_pm_reg[0x38] & 0x04 ) &&
				( ( ( m_pm_reg[0x19] & 0x10 ) && ( m_pm_reg[0x1B] & 0x01 ) ) ||
				( ( m_pm_reg[0x19] & 0x20 ) && ! ( m_pm_reg[0x1B] & 0x01 ) ) ) )
		{
			m_timers.timer2->enable( 1 );
		}
		else
		{
			m_timers.timer2->enable( 0 );
		}

		/* Check if timer2 high should be enabled */
		if ( ( data & 0x80 ) && ( m_pm_reg[0x39] & 0x04 ) && ! ( m_pm_reg[0x38] & 0x80 ) &&
				( ( ( m_pm_reg[0x19] & 0x10 ) && ( m_pm_reg[0x1B] & 0x02 ) ) ||
				( ( m_pm_reg[0x19] & 0x20 ) && ! ( m_pm_reg[0x1B] & 0x02 ) ) ) )
		{
			m_timers.timer2_hi->enable( 1 );
		}
		else
		{
			m_timers.timer2_hi->enable( 0 );
		}
		break;
	case 0x1B:  /* Timer 2 speeds
	           Bit 0   R/W Select slow timer for timer 2 lo
	           Bit 1   R/W Select slow timer for timer 2 hi
	        */
		/* Check for prescaler change for the high counter */
		if ( ( data & 0x01 ) != ( m_pm_reg[0x1B] & 0x01 ) )
		{
			int index = m_pm_reg[0x1A] & 0x07;
			int cycles = ( data & 0x01 ) ? timer_to_cycles_slow[index] : timer_to_cycles_fast[index];

			m_timers.timer2->adjust(attotime::zero, 0, m_maincpu->cycles_to_attotime(cycles));

			if ( ( m_pm_reg[0x1A] & 0x08 ) && ( m_pm_reg[0x38] & 0x04 ) &&
					( ( ( m_pm_reg[0x19] & 0x10 ) && ( data & 0x01 ) ) ||
					( ( m_pm_reg[0x19] & 0x20 ) && ! ( data & 0x01 ) ) ) )
			{
				m_timers.timer2->enable( 1 );
			}
			else
			{
				m_timers.timer2->enable( 0 );
			}
		}

		/* Check for prescaler change for the low counter */
		if ( ( data & 0x02 ) != ( m_pm_reg[0x1B] & 0x02 ) )
		{
			int index = ( m_pm_reg[0x1A] >> 4 ) & 0x07;
			int cycles = ( data & 0x02 ) ? timer_to_cycles_slow[index] : timer_to_cycles_fast[index];

			m_timers.timer2_hi->adjust(attotime::zero, 0, m_maincpu->cycles_to_attotime(cycles));

			if ( ( m_pm_reg[0x1A] & 0x80 ) && ( m_pm_reg[0x39] & 0x04 ) && ! ( m_pm_reg[0x38] & 0x80 ) &&
					( ( ( m_pm_reg[0x19] & 0x10 ) && ( data & 0x02 ) ) ||
					( ( m_pm_reg[0x19] & 0x20 ) && ! ( data & 0x02 ) ) ) )
			{
				m_timers.timer2_hi->enable( 1 );
			}
			else
			{
				m_timers.timer2_hi->enable( 0 );
			}
		}
		break;
	case 0x1C:  /* Timer 3 pre-scale + enable
	           Bit 0-2 R/W low timer 3 prescaler select
	                       000 - 2 or 128 cycles
	                       001 - 8 or 256 cycles
	                       010 - 32 or 512 cycles
	                       011 - 64 or 1024 cycles
	                       100 - 128 or 2048 cycles
	                       101 - 256 or 4096 cycles
	                       110 - 1024 or 8192 cycles
	                       111 - 4096 or 16384 cycles
	           Bit 3   R/W Enable low counting
	           Bit 4-6 R/W high timer 3 prescaler select
	           Bit 7   R/W Enable high counting
	        */
		/* Check for prescaler change for the low counter */
		if ( ( data & 0x07 ) != ( m_pm_reg[0x1C] & 0x07 ) )
		{
			int index = data & 0x07;
			int cycles = ( m_pm_reg[0x1D] & 0x01 ) ? timer_to_cycles_slow[index] : timer_to_cycles_fast[index];

			m_timers.timer3->adjust(attotime::zero, 0, m_maincpu->cycles_to_attotime(cycles));
		}

		/* Check for prescaler change for the high counter */
		if ( ( data & 0x70 ) != ( m_pm_reg[0x1C] & 0x70 ) )
		{
			int index = ( data >> 4 ) & 0x07;
			int cycles = ( m_pm_reg[0x1D] & 0x02 ) ? timer_to_cycles_slow[index] : timer_to_cycles_fast[index];

			m_timers.timer3_hi->adjust(attotime::zero, 0, m_maincpu->cycles_to_attotime(cycles));
		}

		/* Check if timer2 low should be enabled */
		if ( ( data & 0x08 ) && ( m_pm_reg[0x48] & 0x04 ) &&
				( ( ( m_pm_reg[0x19] & 0x10 ) && ( m_pm_reg[0x1D] & 0x01 ) ) ||
				( ( m_pm_reg[0x19] & 0x20 ) && ! ( m_pm_reg[0x1D] & 0x01 ) ) ) )
		{
			m_timers.timer3->enable( 1 );
		}
		else
		{
			m_timers.timer3->enable( 0 );
		}

		/* Check if timer2 high should be enabled */
		if ( ( data & 0x80 ) && ( m_pm_reg[0x49] & 0x04 ) && ! ( m_pm_reg[0x48] & 0x80 ) &&
				( ( ( m_pm_reg[0x19] & 0x10 ) && ( m_pm_reg[0x1D] & 0x02 ) ) ||
				( ( m_pm_reg[0x19] & 0x20 ) && ! ( m_pm_reg[0x1D] & 0x02 ) ) ) )
		{
			m_timers.timer3_hi->enable( 1 );
		}
		else
		{
			m_timers.timer3_hi->enable( 0 );
		}
		break;
	case 0x1D:  /* Timer 3 speeds
	           Bit 0   R/W Select slow timer for timer 3 lo
	           Bit 1   R/W Select slow timer for timer 3 hi
	        */
		/* Check for prescaler change for the high counter */
		if ( ( data & 0x01 ) != ( m_pm_reg[0x1D] & 0x01 ) )
		{
			int index = m_pm_reg[0x1C] & 0x07;
			int cycles = ( data & 0x01 ) ? timer_to_cycles_slow[index] : timer_to_cycles_fast[index];

			m_timers.timer3->adjust(attotime::zero, 0, m_maincpu->cycles_to_attotime(cycles));

			if ( ( m_pm_reg[0x1C] & 0x08 ) && ( m_pm_reg[0x48] & 0x04 ) &&
					( ( ( m_pm_reg[0x19] & 0x10 ) && ( data & 0x01 ) ) ||
					( ( m_pm_reg[0x19] & 0x20 ) && ! ( data & 0x01 ) ) ) )
			{
				m_timers.timer3->enable( 1 );
			}
			else
			{
				m_timers.timer3->enable( 0 );
			}
		}

		/* Check for prescaler change for the low counter */
		if ( ( data & 0x02 ) != ( m_pm_reg[0x1D] & 0x02 ) )
		{
			int index = ( m_pm_reg[0x1C] >> 4 ) & 0x07;
			int cycles = ( data & 0x02 ) ? timer_to_cycles_slow[index] : timer_to_cycles_fast[index];

			m_timers.timer3_hi->adjust(attotime::zero, 0, m_maincpu->cycles_to_attotime(cycles));

			if ( ( m_pm_reg[0x1C] & 0x80 ) && ( m_pm_reg[0x49] & 0x04 ) && ! ( m_pm_reg[0x48] & 0x80 ) &&
					( ( ( m_pm_reg[0x19] & 0x10 ) && ( data & 0x02 ) ) ||
					( ( m_pm_reg[0x19] & 0x20 ) && ! ( data & 0x02 ) ) ) )
			{
				m_timers.timer3_hi->enable( 1 );
			}
			else
			{
				m_timers.timer3_hi->enable( 0 );
			}
		}
		break;
	case 0x20:  /* Event #1-#8 priority
	           Bit 0-1 R/W Timer 3 overflow Interrupt #7-#8
	           Bit 2-3 R/W Timer 1 overflow Interrupt #5-#6
	           Bit 4-5 R/W Timer 2 overflow Interrupt #3-#4
	           Bit 6-7 R/W VDraw/VBlank trigger Interrupt #1-#2
	        */
		m_pm_reg[0x20] = data;
		check_irqs();
		break;
	case 0x21:  /* Event #15-#22 priority
	           Bit 0-1 R/W Unknown
	           Bit 2-3 R/W All keypad interrupts - Interrupt #15-#22
	           Bit 4-7 R/W Unknown
	        */
		m_pm_reg[0x21] = data;
		check_irqs();
		break;
	case 0x22:  /* Event #9-#14 priority
	           Bit 0-1 R/W All #9 - #14 events - Interrupt #9-#14
	           Bit 2-7     Unused
	        */
		m_pm_reg[0x22] = data;
		check_irqs();
		break;
	case 0x23:  /* Event #1-#8 enable
	           Bit 0   R/W Timer 3 overflow (mirror) - Enable Interrupt #8
	           Bit 1   R/W Timer 3 overflow - Enable Interrupt #7
	           Bit 2   R/W Not called... - Enable Interrupt #6
	           Bit 3   R/W Timer 1 overflow - Enable Interrupt #5
	           Bit 4   R/W Not called... - Enable Interrupt #4
	           Bit 5   R/W Timer 2 overflow - Enable Interrupt #3
	           Bit 6   R/W V-Draw trigger - Enable Interrupt #2
	           Bit 7   R/W V-Blank trigger - Enable Interrupt #1
	        */
		m_pm_reg[0x23] = data;
		check_irqs();
		break;
	case 0x24:  /* Event #9-#12 enable
	           Bit 0-5 R/W Unknown
	           Bit 6-7     Unused
	        */
		m_pm_reg[0x24] = data;
		check_irqs();
		break;
	case 0x25:  /* Event #15-#22 enable
	           Bit 0   R/W Press key "A" event - Enable interrupt #22
	           Bit 1   R/W Press key "B" event - Enable interrupt #21
	           Bit 2   R/W Press key "C" event - Enable interrupt #20
	           Bit 3   R/W Press D-pad up key event - Enable interrupt #19
	           Bit 4   R/W Press D-pad down key event - Enable interrupt #18
	           Bit 5   R/W Press D-pad left key event - Enable interrupt #17
	           Bit 6   R/W Press D-pad right key event - Enable interrupt #16
	           Bit 7   R/W Press power button event - Enable interrupt #15
	        */
		m_pm_reg[0x25] = data;
		check_irqs();
		break;
	case 0x26:  /* Event #13-#14 enable
	           Bit 0-2 R/W Unknown
	           Bit 3       Unused
	           Bit 4-5 R/W Unknown
	           Bit 6   R/W Shock detector trigger - Enable interrupt #14
	           Bit 7   R/W IR receiver - low to high trigger - Enable interrupt #13
	        */
		m_pm_reg[0x26] = data;
		check_irqs();
		break;
	case 0x27:  /* Interrupt active flag #1-#8
	           Bit 0       Timer 3 overflow (mirror) / Clear interrupt #8
	           Bit 1       Timer 3 overflow / Clear interrupt #7
	           Bit 2       Not called ... / Clear interrupt #6
	           Bit 3       Timer 1 overflow / Clear interrupt #5
	           Bit 4       Not called ... / Clear interrupt #4
	           Bit 5       Timer 2 overflow / Clear interrupt #3
	           Bit 6       VDraw trigger / Clear interrupt #2
	           Bit 7       VBlank trigger / Clear interrupt #1
	        */
		m_pm_reg[0x27] &= ~data;
		check_irqs();
		return;
	case 0x28:  /* Interrupt active flag #9-#12
	           Bit 0-1     Unknown
	           Bit 2       Unknown / Clear interrupt #12
	           Bit 3       Unknown / Clear interrupt #11
	           Bit 4       Unknown / Clear interrupt #10
	           Bit 5       Unknown / Clear interrupt #9
	           Bit 6-7     Unknown
	        */
		m_pm_reg[0x28] &= ~data;
		check_irqs();
		return;
	case 0x29:  /* Interrupt active flag #15-#22
	           Bit 0       Press key "A" event / Clear interrupt #22
	           Bit 1       Press key "B" event / Clear interrupt #21
	           Bit 2       Press key "C" event / Clear interrupt #20
	           Bit 3       Press D-pad up key event / Clear interrupt #19
	           Bit 4       Press D-pad down key event / Clear interrupt #18
	           Bit 5       Press D-pad left key event / Clear interrupt #17
	           Bit 6       Press D-pad right key event / Clear interrupt #16
	           Bit 7       Press power button event / Clear interrupt #15
	        */
		m_pm_reg[0x29] &= ~data;
		check_irqs();
		return;
	case 0x2A:  /* Interrupt active flag #13-#14
	           Bit 0-5     Unknown
	           Bit 6       Shock detector trigger / Clear interrupt #14
	           Bit 7       Unknown / Clear interrupt #13
	        */
		m_pm_reg[0x2A] &= ~data;
		check_irqs();
		return;
	case 0x30:  /* Timer 1 control 1
	           Bit 0   R/W Unknown
	           Bit 1   W   Reset low counter
	           Bit 2   R/W Enable high counter
	           Bit 3   R/W Unknown
	           Bit 4-6     Unused
	           Bit 7   R/W Enable 16bit mode
	        */
		if ( data & 0x02 )
		{
			m_pm_reg[0x36] = m_pm_reg[0x32];
			data &= ~0x02;
		}

		if ( ( data & 0x04 ) && ( m_pm_reg[0x18] & 0x08 ) &&
				( ( ( m_pm_reg[0x19] & 0x20 ) && ! ( m_pm_reg[0x19] & 0x01 ) ) ||
				( ( m_pm_reg[0x19] & 0x10 ) && ( m_pm_reg[0x19] & 0x01 ) ) ) )
		{
			m_timers.timer1->enable( 1 );
		}
		else
		{
			m_timers.timer1->enable( 0 );
		}

		if ( ( m_pm_reg[0x31] & 0x04 ) && ! ( data & 0x80 ) && ( m_pm_reg[0x18] & 0x80 ) &&
				( ( ( m_pm_reg[0x19] & 0x20 ) && ! ( m_pm_reg[0x19] & 0x02 ) ) ||
				( ( m_pm_reg[0x19] & 0x10 ) && ( m_pm_reg[0x19] & 0x02 ) ) ) )
		{
			m_timers.timer1_hi->enable( 1 );
		}
		else
		{
			m_timers.timer1_hi->enable( 0 );
		}
		break;
	case 0x31:  /* Timer 1 control 2
	           Bit 0   R/W Unknown
	           Bit 1   W   Reset hi counter
	           Bit 2   R/W Enable high counter
	           Bit 3   R/W Unknown
	           Bit 4-7     Unused
	        */
		if ( data & 0x02 )
		{
			m_pm_reg[0x37] = m_pm_reg[0x33];
			data &= ~0x02;
		}

		if ( ( data & 0x04 ) && ! ( m_pm_reg[0x30] & 0x80 ) && ( m_pm_reg[0x18] & 0x80 ) &&
				( ( ( m_pm_reg[0x19] & 0x20 ) && ! ( m_pm_reg[0x19] & 0x02 ) ) ||
				( ( m_pm_reg[0x19] & 0x10 ) && ( m_pm_reg[0x19] & 0x02 ) ) ) )
		{
			m_timers.timer1_hi->enable( 1 );
		}
		else
		{
			m_timers.timer1_hi->enable( 0 );
		}
		break;
	case 0x32:  /* Timer 1 preset value (low)
	           Bit 0-7 R/W Timer 1 preset value bit 0-7
	        */
		break;
	case 0x33:  /* Timer 1 preset value (high)
	           Bit 0-7 R/W Timer 1 preset value bit 8-15
	        */
		break;
	case 0x34:  /* Timer 1 sound-pivot (low, unused)
	        */
	case 0x35:  /* Timer 1 sound-pivot (high, unused)
	        */
		logerror( "%0X: Write to unknown hardware address: %02X, %02X\n", m_maincpu->pc(), offset, data );
		break;
	case 0x36:  /* Timer 1 counter (low), read only
	        */
		return;
	case 0x37:  /* Timer 1 counter (high), read only
	        */
		return;
	case 0x38:  /* Timer 2 control 1
	           Bit 0   R/W Unknown
	           Bit 1   W   Reset low counter
	           Bit 2   R/W Enable high counter
	           Bit 3   R/W Unknown
	           Bit 4-6     Unused
	           Bit 7   R/W Enable 16bit mode
	        */
		if ( data & 0x02 )
		{
			m_pm_reg[0x3E] = m_pm_reg[0x3A];
			data &= ~0x02;
		}

		if ( ( data & 0x04 ) && ( m_pm_reg[0x1A] & 0x08 ) &&
				( ( ( m_pm_reg[0x19] & 0x20 ) && ! ( m_pm_reg[0x1A] & 0x01 ) ) ||
				( ( m_pm_reg[0x19] & 0x10 ) && ( m_pm_reg[0x1A] & 0x01 ) ) ) )
		{
			m_timers.timer2->enable( 1 );
		}
		else
		{
			m_timers.timer2->enable( 0 );
		}
		if ( ( m_pm_reg[0x39] & 0x04 ) && ! ( data & 0x80 ) && ( m_pm_reg[0x1A] & 0x80 ) &&
				( ( ( m_pm_reg[0x19] & 0x20 ) && ! ( m_pm_reg[0x1B] & 0x02 ) ) ||
				( ( m_pm_reg[0x19] & 0x10 ) && ( m_pm_reg[0x1B] & 0x02 ) ) ) )
		{
			m_timers.timer2_hi->enable( 1 );
		}
		else
		{
			m_timers.timer2_hi->enable( 0 );
		}
		break;
	case 0x39:  /* Timer 2 control 2
	           Bit 0   R/W Unknown
	           Bit 1   W   Reset hi counter
	           Bit 2   R/W Enable high counter
	           Bit 3   R/W Unknown
	           Bit 4-7     Unused
	        */
		if ( data & 0x02 )
		{
			m_pm_reg[0x3F] = m_pm_reg[0x3A];
			data &= ~0x02;
		}

		if ( ( data & 0x04 ) && ! ( m_pm_reg[0x38] & 0x80 ) && ( m_pm_reg[0x1A] & 0x80 ) &&
				( ( ( m_pm_reg[0x19] & 0x20 ) && ! ( m_pm_reg[0x1B] & 0x02 ) ) ||
				( ( m_pm_reg[0x19] & 0x10 ) && ( m_pm_reg[0x1B] & 0x02 ) ) ) )
		{
			m_timers.timer2_hi->enable( 1 );
		}
		else
		{
			m_timers.timer2_hi->enable( 0 );
		}
		break;
	case 0x3A:  /* Timer 2 preset value (low)
	           Bit 0-7 R/W Timer 2 preset value bit 0-7
	        */
		break;
	case 0x3B:  /* Timer 2 preset value (high)
	           Bit 0-7 R/W Timer 2 preset value bit 8-15
	        */
		break;
	case 0x3C:  /* Timer 2 sound-pivot (low, unused)
	        */
	case 0x3D:  /* Timer 2 sound-pivot (high, unused)
	        */
		logerror( "%0X: Write to unknown hardware address: %02X, %02X\n", m_maincpu->pc(), offset, data );
		break;
	case 0x3E:  /* Timer 2 counter (low), read only
	           Bit 0-7 R/W Timer 2 counter value bit 0-7
	        */
		return;
	case 0x3F:  /* Timer 2 counter (high), read only
	           Bit 0-7 R/W Timer 2 counter value bit 8-15
	        */
		return;
	case 0x40:  /* 256Hz timer control
	           Bit 0   R/W Enable Timer
	           Bit 1   W   Reset Timer
	           Bit 2-7     Unused
	        */
		if ( data & 0x02 )
		{
			m_pm_reg[0x41] = 0;
			data &= ~0x02;
		}
		break;
	case 0x41:  /* 256Hz timer counter
	           Bit 0-7 R   256Hz timer counter
	        */
		return;
	case 0x48:  /* Timer 3 control 1
	           Bit 0   R/W Unknown
	           Bit 1   W   Reset low counter
	           Bit 2   R/W Enable high counter
	           Bit 3   R/W Unknown
	           Bit 4-6     Unused
	           Bit 7   R/W Enable 16bit mode
	        */
		if ( data & 0x02 )
		{
			m_pm_reg[0x4E] = m_pm_reg[0x4A];
			data &= ~0x02;
		}

		if ( ( data & 0x04 ) && ( m_pm_reg[0x1C] & 0x08 ) &&
				( ( ( m_pm_reg[0x19] & 0x20 ) && ! ( m_pm_reg[0x1D] & 0x01 ) ) ||
				( ( m_pm_reg[0x19] & 0x10 ) && ( m_pm_reg[0x1D] & 0x01 ) ) ) )
		{
			m_timers.timer3->enable( 1 );
		}
		else
		{
			m_timers.timer3->enable( 0 );
		}
		if ( ( m_pm_reg[0x49] & 0x04 ) && ! ( data & 0x80 ) && ( m_pm_reg[0x1C] & 0x80 ) &&
				( ( ( m_pm_reg[0x19] & 0x20 ) && ! ( m_pm_reg[0x1D] & 0x02 ) ) ||
				( ( m_pm_reg[0x19] & 0x10 ) && ( m_pm_reg[0x1D] & 0x02 ) ) ) )
		{
			m_timers.timer3_hi->enable( 1 );
		}
		else
		{
			m_timers.timer3_hi->enable( 0 );
		}
		m_pm_reg[0x48] = data;
		update_sound();
		break;
	case 0x49:  /* Timer 3 control 2
	           Bit 0   R/W Unknown
	           Bit 1   W   Reset hi counter
	           Bit 2   R/W Enable high counter
	           Bit 3   R/W Unknown
	           Bit 4-7     Unused
	        */
		if ( data & 0x02 )
		{
			m_pm_reg[0x4F] = m_pm_reg[0x4B];
			data &= ~0x02;
		}

		if ( ( data & 0x04 ) && ! ( m_pm_reg[0x48] & 0x80 ) && ( m_pm_reg[0x1C] & 0x80 ) &&
				( ( ( m_pm_reg[0x19] & 0x20 ) && ! ( m_pm_reg[0x1D] & 0x02 ) ) ||
				( ( m_pm_reg[0x19] & 0x10 ) && ( m_pm_reg[0x1D] & 0x02 ) ) ) )
		{
			m_timers.timer3_hi->enable( 1 );
		}
		else
		{
			m_timers.timer3_hi->enable( 0 );
		}
		m_pm_reg[0x49] = data;
		update_sound();
		break;
	case 0x4A:  /* Timer 3 preset value (low)
	           Bit 0-7 R/W Timer 3 preset value bit 0-7
	        */
		m_pm_reg[0x4A] = data;
		update_sound();
		break;
	case 0x4B:  /* Timer 3 preset value (high)
	           Bit 0-7 R/W Timer 3 preset value bit 8-15
	        */
		m_pm_reg[0x4B] = data;
		update_sound();
		break;
	case 0x4C:  /* Timer 3 sound-pivot (low)
	           Bit 0-7 R/W Timer 3 sound-pivot value bit 0-7
	        */
		m_pm_reg[0x4C] = data;
		update_sound();
		break;
	case 0x4D:  /* Timer 3 sound-pivot (high)
	           Bit 0-7 R/W Timer 3 sound-pivot value bit 8-15

	           Sound-pivot location:
	           Pulse-Width of 0% = 0x0000
	           Pulse-Width of 50% = Half of preset-value
	           Pulse-Width of 100% = Same as preset-value
	        */
		m_pm_reg[0x4D] = data;
		update_sound();
		break;
	case 0x4E:  /* Timer 3 counter (low), read only
	           Bit 0-7 R/W Timer 3 counter value bit 0-7
	        */
		return;
	case 0x4F:  /* Timer 3 counter (high), read only
	           Bit 0-7 R/W Timer 3 counter value bit 8-15
	        */
		return;
	case 0x52:  /* Keypad status
	           Bit 0   R   Key "A"
	           Bit 1   R   Key "B"
	           Bit 2   R   Key "C"
	           Bit 3   R   D-pad up
	           Bit 4   R   D-pad down
	           Bit 5   R   D-pad left
	           Bit 6   R   D-pad right
	           Bit 7   R   Power button
	        */
		return;
	case 0x60:  /* I/O peripheral circuit select
	           Bit 0   R/W Unknown
	           bit 1   R/W IR receive / transmit
	           Bit 2   R/W EEPROM / RTC data
	           Bit 3   R/W EEPROM / RTC clock
	           Bit 4   R/W Rumble controller
	           Bit 5   R/W IR enable/disable
	           Bit 6   R/W Unknown
	           Bit 7   R/W Unknown
	        */
		break;
	case 0x61:  /* I/O peripheral status control
	           Bit 0   R/W IR received bit (if device not selected: 0)
	           Bit 1   R/W IR transmit (if device not selected: 0)
	           Bit 2   R/W EEPROM / RTC data (if device not selected: 1)
	           Bit 3   R/W EEPROM / RTC clock (if device not selected: 0)
	           Bit 4   R/W Rumble on/off (if device not selected: 0)
	           Bit 5   R/W IR disable (receive & transmit) (if device not selected: 1)
	           Bit 6       Always 1
	           Bit 7   R/W IR received bit (mirror, if device not selected: 0)
	        */
		if ( m_pm_reg[0x60] & 0x04 )
			m_i2cmem->write_sda( ( data & 0x04 ) ? 1 : 0 );

		if ( m_pm_reg[0x60] & 0x08 )
			m_i2cmem->write_scl( ( data & 0x08 ) ? 1 : 0 );
		break;
	case 0x70:  /* Sound related */
		m_pm_reg[0x70] = data;
		update_sound();
		break;
	case 0x71:  /* Sound volume
	           Bit 0-1 R/W Sound volume
	                       00 - 0%
	                       01 - 50%
	                       10 - 50%
	                       11 - 100%
	           Bit 2   R/W Always set to 0
	           Bit 3-7     Unused
	        */
		m_pm_reg[0x71] = data;
		update_sound();
		break;
	case 0x80:  /* LCD control
	           Bit 0   R/W Invert colors; 0 - normal, 1 - inverted
	           Bit 1   R/W Enable rendering of background
	           Bit 2   R/W Enable rendering of sprites
	           Bit 3   R/W Enable copy to LCD ram
	           Bit 4-5 R/W Map size
	                       00 - 12x16
	                       01 - 16x12
	                       10 - 24x8
	                       11 - 24x8 (prohibited code)
	          Bit 6-7      Unused
	        */
		m_prc.colors_inverted = ( data & 0x01 ) ? 1 : 0;
		m_prc.background_enabled = ( data & 0x02 ) ? 1 : 0;
		m_prc.sprites_enabled = ( data & 0x04 ) ? 1 : 0;
		m_prc.copy_enabled = ( data & 0x08 ) ? 1 : 0;
		m_prc.map_size = ( data >> 4 ) & 0x03;
		switch( m_prc.map_size )
		{
		case 0:
			m_prc.map_size_x = 12; break;
		case 1:
			m_prc.map_size_x = 16; break;
		case 2:
		case 3:
			m_prc.map_size_x = 24; break;
		}
		break;
	case 0x81:  /* LCD render refresh rate
	           Bit 0   R/W Unknown
	           Bit 1-3 R/W LCD refresh rate divider
	                       000 - 60Hz / 3 = 20Hz (0 - 2)
	                       001 - 60Hz / 6 = 10Hz (0 - 5)
	                       010 - 60Hz / 9 = 6,6Hz (0 - 8)
	                       011 - 60Hz / 12 = 5Hz (0 - B)
	                       100 - 60Hz / 2 = 30Hz (0 - 1)
	                       101 - 60Hz / 4 = 15Hz (0 - 3)
	                       110 - 60Hz / 6 = 10Hz (0 - 5)
	                       111 - 60Hz / 8 = 7,5Hz (0 - 7)
	           Bit 4-7 R   Divider position, when overflow the LCD is updated
	        */
		switch ( data & 0x0E )
		{
		case 0x00:  m_prc.max_frame_count = 3; break;
		case 0x02:  m_prc.max_frame_count = 6; break;
		case 0x04:  m_prc.max_frame_count = 9; break;
		case 0x06:  m_prc.max_frame_count = 12; break;
		case 0x08:  m_prc.max_frame_count = 2; break;
		case 0x0A:  m_prc.max_frame_count = 4; break;
		case 0x0C:  m_prc.max_frame_count = 6; break;
		case 0x0E:  m_prc.max_frame_count = 8; break;
		}
		break;
	case 0x82:  /* BG tile data memory offset (low)
	           Bit 0-2     Always "0"
	           Bit 3-7 R/W BG tile data memory offset bit 3-7
	        */
		data &= 0xF8;
		m_prc.bg_tiles = ( m_prc.bg_tiles & 0xFFFF00 ) | data;
		break;
	case 0x83:  /* BG tile data memory offset (mid)
	           Bit 0-7 R/W BG tile data memory offset bit 8-15
	        */
		m_prc.bg_tiles = ( m_prc.bg_tiles & 0xFF00FF ) | ( data << 8 );
		break;
	case 0x84:  /* BG tile data memory offset (high)
	           Bit 0-4 R/W BG tile data memory offset bit 16-20
	           Bit 5-7     Unused
	        */
		data &= 0x1F;
		m_prc.bg_tiles = ( m_prc.bg_tiles & 0x00FFFF ) | ( data << 16 );
		break;
	case 0x85:  /* BG vertical move
	           Bit 0-6 R/W Move the background up, move range:
	                       Map size 0: 0x00 to 0x40
	                       Map size 1: 0x00 to 0x20
	                       Map size 2: move ignored
	           Bit 7       Unused
	        */
	case 0x86:  /* BG horizontal move
	           Bit 0-6 R/W Move the background left, move range:
	                       Map size 0: move ignored
	                       Map size 1: 0x00 to 0x20
	                       Map size 2: 0x00 to 0x60
	           Bit 7       Unused
	        */
		logerror( "%0X: Write to unknown hardware address: %02X, %02X\n", m_maincpu->pc(), offset, data );
		break;
	case 0x87:  /* Sprite tile data memory offset (low)
	           Bit 0-5     Always "0"
	           Bit 6-7 R/W Sprite tile data memory offset bit 6-7
	        */
		data &= 0xC0;
		m_prc.spr_tiles = ( m_prc.spr_tiles & 0xFFFF00 ) | data;
		break;
	case 0x88:  /* Sprite tile data memory offset (med)
	           Bit 0-7 R/W Sprite tile data memory offset bit 8-15
	        */
		m_prc.spr_tiles = ( m_prc.spr_tiles & 0xFF00FF ) | ( data << 8 );
		break;
	case 0x89:  /* Sprite tile data memory offset (high)
	           Bit 0-4 R/W Sprite tile data memory offset bit 16-20
	           Bit 5-7     Unused
	        */
		data &= 0x1F;
		m_prc.spr_tiles = ( m_prc.spr_tiles & 0x00FFFF ) | ( data << 16 );
		break;
	case 0x8A:  /* LCD status
	           Bit 0   R   Unknown
	           Bit 1   R   Unknown
	           Bit 2   R   Unknown
	           Bit 3   R   Unknown
	           Bit 4   R   LCD during V-Sync / Rendering circuitry active or not ( 1 = not active)
	           Bit 5   R   Unknown
	           Bit 6-7     Unused
	        */
	case 0xFE:  /* Direct LCD control / data
	           Bit 0-7 R/W Direct LCD command or data
	        */
//      lcd_command_w( data );
		break;
	case 0xFF:  /* Direct LCD data
	           Bit 0-7 R/W Direct LCD data
	        */
//      lcd_data_w( data );
		break;
	default:
		logerror( "%0X: Write to unknown hardware address: %02X, %02X\n", m_maincpu->pc(), offset, data );
		break;
	}
	m_pm_reg[offset] = data;
}

uint8_t pokemini_state::hwreg_r(offs_t offset)
{
	uint8_t data = m_pm_reg[offset];

	switch( offset )
	{
	case 0x52:  return m_inputs->read();
	case 0x61:
		if ( ! ( m_pm_reg[0x60] & 0x04 ) )
		{
			data = ( data & ~ 0x04 ) | ( m_i2cmem->read_sda() ? 0x04 : 0x00 );
		}

		if ( ! ( m_pm_reg[0x60] & 0x08 ) )
		{
			data &= ~0x08;
		}
		break;
	case 0x81:  return ( m_pm_reg[offset] & 0x0F ) | ( m_prc.frame_count << 4 );
	case 0x8A:  return m_prc.count;
	}
	return data;
}

DEVICE_IMAGE_LOAD_MEMBER( pokemini_state::cart_load )
{
	uint32_t size = m_cart->common_get_size("rom");

	/* Verify that the image is big enough */
	if (size <= 0x2100)
		return std::make_pair(image_error::INVALIDLENGTH, "ROM image is too small");

	/* Verify that the image is not too big */
	if (size > 0x1fffff)
		return std::make_pair(image_error::INVALIDLENGTH, "ROM image is too big");

	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}


TIMER_CALLBACK_MEMBER(pokemini_state::prc_counter_callback)
{
	address_space &space = m_maincpu->space( AS_PROGRAM );
	m_prc.count++;

	/* Check for overflow */
	if ( m_prc.count >= 0x42 )
	{
		m_prc.count = 0;
		m_prc.frame_count++;
	}
	else
	{
		if ( m_prc.count == 0x18 && m_prc.frame_count >= m_prc.max_frame_count )
		{
			m_prc.frame_count = 0;

			/* Check if the background should be drawn */
			if ( m_prc.background_enabled )
			{
				for ( int y = 0; y < 8; y++ ) {
					for ( int x = 0; x < 12; x++ ) {
						uint8_t tile = m_p_ram[ 0x360 + ( y * m_prc.map_size_x ) + x ];
						for( int i = 0; i < 8; i++ ) {
							m_p_ram[ ( y * 96 ) + ( x * 8 ) + i ] = space.read_byte( m_prc.bg_tiles + ( tile * 8 ) + i );
						}
					}
				}
			}

			/* Check if the sprites should be drawn */
			if ( m_prc.sprites_enabled )
			{
				for ( uint16_t spr = 0x35C; spr >= 0x300; spr -= 4 )
				{
					int     spr_x = ( m_p_ram[ spr + 0 ] & 0x7F ) - 16;
					int     spr_y = ( m_p_ram[ spr + 1 ] & 0x7F ) - 16;
					uint8_t   spr_tile = m_p_ram[ spr + 2 ];
					uint8_t   spr_flag = m_p_ram[ spr + 3 ];

					if ( spr_flag & 0x08 )
					{
						uint32_t  spr_base = m_prc.spr_tiles + spr_tile * 64;

						for ( int i = 0; i < 16; i++ )
						{
							if ( spr_x + i >= 0 && spr_x + i < 96 )
							{
								int rel_x = ( spr_flag & 0x01 ) ? 15 - i : i;
								uint32_t  s = spr_base + ( ( rel_x & 0x08 ) << 2 ) + ( rel_x & 0x07 );

								uint16_t mask = ~ ( space.read_byte( s ) | ( space.read_byte( s + 8 ) << 8 ) );
								uint16_t gfx = space.read_byte( s + 16 ) | ( space.read_byte( s + 24 ) << 8 );

								/* Are the colors inverted? */
								if ( spr_flag & 0x04 )
								{
									gfx = ~gfx;
								}

								for ( int j = 0; j < 16; j++ )
								{
									if ( spr_y + j >= 0 && spr_y + j < 64 )
									{
										uint16_t  ram_addr = ( ( ( spr_y + j ) >> 3 ) * 96 ) + spr_x + i;

										if ( spr_flag & 0x02 )
										{
											if ( mask & 0x8000 )
											{
												m_p_ram[ ram_addr ] &= ~ ( 1 << ( ( spr_y + j ) & 0x07 ) );
												if ( gfx & 0x8000 )
												{
													m_p_ram[ ram_addr ] |= ( 1 << ( ( spr_y + j ) & 0x07 ) );
												}
											}
											mask <<= 1;
											gfx <<= 1;
										}
										else
										{
											if ( mask & 0x0001 )
											{
												m_p_ram[ ram_addr ] &= ~ ( 1 << ( ( spr_y + j ) & 0x07 ) );
												if ( gfx & 0x0001 )
												{
													m_p_ram[ ram_addr ] |= ( 1 << ( ( spr_y + j ) & 0x07 ) );
												}
											}
											mask >>= 1;
											gfx >>= 1;
										}
									}
								}
							}
						}
					}
				}
			}

			/* Set PRC Render interrupt */
			m_pm_reg[0x27] |= 0x40;
			check_irqs();

			/* Check if the rendered data should be copied to the LCD */
			if ( m_prc.copy_enabled )
			{
				for( int y = 0; y < 64; y += 8 ) {
					for( int x = 0; x < 96; x++ ) {
						uint8_t data = m_p_ram[ ( y * 12 ) + x ];

						m_bitmap.pix(y + 0, x) = ( data & 0x01 ) ? 3 : 0;
						m_bitmap.pix(y + 1, x) = ( data & 0x02 ) ? 3 : 0;
						m_bitmap.pix(y + 2, x) = ( data & 0x04 ) ? 3 : 0;
						m_bitmap.pix(y + 3, x) = ( data & 0x08 ) ? 3 : 0;
						m_bitmap.pix(y + 4, x) = ( data & 0x10 ) ? 3 : 0;
						m_bitmap.pix(y + 5, x) = ( data & 0x20 ) ? 3 : 0;
						m_bitmap.pix(y + 6, x) = ( data & 0x40 ) ? 3 : 0;
						m_bitmap.pix(y + 7, x) = ( data & 0x80 ) ? 3 : 0;
					}
				}

				/* Set PRC Copy interrupt */
				m_pm_reg[0x27] |= 0x80;
				check_irqs();
			}
		}

		/* Set possible input irqs */
		m_pm_reg[0x29] |= ~ m_inputs->read();
	}
}


void pokemini_state::machine_start()
{
	/* Clear internal structures */
	m_prc = PRC();
	m_timers = TIMERS();
	memset( m_pm_reg, 0, sizeof(m_pm_reg) );

	/* Set up timers */
	m_timers.seconds_timer = timer_alloc(FUNC(pokemini_state::seconds_timer_callback), this);
	m_timers.seconds_timer->adjust(attotime::zero, 0, attotime::from_seconds(1));

	m_timers.hz256_timer = timer_alloc(FUNC(pokemini_state::timer_256hz_callback), this);
	m_timers.hz256_timer->adjust(attotime::zero, 0, attotime::from_hz(256));

	m_timers.timer1 = timer_alloc(FUNC(pokemini_state::timer1_callback), this);
	m_timers.timer1_hi = timer_alloc(FUNC(pokemini_state::timer1_hi_callback), this);
	m_timers.timer2 = timer_alloc(FUNC(pokemini_state::timer2_callback), this);
	m_timers.timer2_hi = timer_alloc(FUNC(pokemini_state::timer2_hi_callback), this);
	m_timers.timer3 = timer_alloc(FUNC(pokemini_state::timer3_callback), this);
	m_timers.timer3_hi = timer_alloc(FUNC(pokemini_state::timer3_hi_callback), this);

	/* Set up the PRC */
	m_prc.max_frame_count = 2;
	m_prc.count_timer = timer_alloc(FUNC(pokemini_state::prc_counter_callback), this);
	m_prc.count_timer->adjust( attotime::zero, 0, m_maincpu->cycles_to_attotime(55640 / 65) );
}


static const double speaker_levels[] = {-1.0, 0.0, 1.0};

void pokemini_state::video_start()
{
	m_screen->register_screen_bitmap(m_bitmap);
}


uint32_t pokemini_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
	return 0;
}


void pokemini_state::pokemini(machine_config &config)
{
	/* basic machine hardware */
	MINX(config, m_maincpu, 4000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &pokemini_state::pokemini_mem_map);

	config.set_maximum_quantum(attotime::from_hz(60));

	I2C_24C64(config, m_i2cmem, 0); // ?

	/* This still needs to be improved to actually match the hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_screen_update(FUNC(pokemini_state::screen_update));
	m_screen->set_size(96, 64);
	m_screen->set_visarea(0, 95, 0, 63);
	m_screen->set_refresh_hz(72);
	m_screen->set_palette("palette");

	PALETTE(config, "palette", FUNC(pokemini_state::pokemini_palette), 4);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->set_levels(3, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.50);

	/* cartridge */
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "pokemini_cart", "bin,min").set_device_load(FUNC(pokemini_state::cart_load));

	/* Software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("pokemini");
}

ROM_START( pokemini )
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_LOAD( "bios.min", 0x0000, 0x1000, CRC(aed3c14d) SHA1(daad4113713ed776fbd47727762bca81ba74915f) )
ROM_END

} // anonymous namespace


CONS( 2001, pokemini, 0, 0, pokemini, pokemini, pokemini_state, empty_init, "Nintendo", "Pokemon Mini", MACHINE_NO_SOUND )



polgar.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco, hap
/*******************************************************************************

Mephisto Polgar

Hardware notes:
- RP65C02G @ 4.91MHz
- 64KB ROM (25% unused)
- Mephisto modular display module
- Mephisto modular chessboard

The 10MHz version has a W65C02P-8 @ 9.83MHz.

*******************************************************************************/

#include "emu.h"

#include "mmboard.h"
#include "mmdisplay2.h"

#include "cpu/m6502/r65c02.h"
#include "cpu/m6502/w65c02.h"
#include "machine/74259.h"
#include "machine/nvram.h"

// internal artwork
#include "mephisto_polgar.lh"


namespace {

class polgar_state : public driver_device
{
public:
	polgar_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_keys(*this, "KEY")
	{ }

	void polgar(machine_config &config);
	void polgar10(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	required_ioport m_keys;

	void polgar_mem(address_map &map) ATTR_COLD;

	u8 keys_r(offs_t offset);
};



/*******************************************************************************
    I/O
*******************************************************************************/

u8 polgar_state::keys_r(offs_t offset)
{
	return ~(BIT(m_keys->read(), offset) << 7);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void polgar_state::polgar_mem(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("nvram");
	map(0x2000, 0x2000).w("display", FUNC(mephisto_display2_device::latch_w));
	map(0x2004, 0x2004).w("display", FUNC(mephisto_display2_device::io_w));
	map(0x2400, 0x2400).w("board", FUNC(mephisto_board_device::led_w));
	map(0x2800, 0x2800).w("board", FUNC(mephisto_board_device::mux_w));
	map(0x2c00, 0x2c07).r(FUNC(polgar_state::keys_r));
	map(0x3000, 0x3000).r("board", FUNC(mephisto_board_device::input_r));
	map(0x3400, 0x3407).w("outlatch", FUNC(hc259_device::write_d7));
	map(0x4000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( polgar )
	PORT_START("KEY")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("TRN / Pawn")      PORT_CODE(KEYCODE_T)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("INFO / Knight")   PORT_CODE(KEYCODE_I)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("MEM / Bishop")    PORT_CODE(KEYCODE_M)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("POS / Rook")      PORT_CODE(KEYCODE_P)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("LEV / Queen")     PORT_CODE(KEYCODE_L)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("FCT / King")      PORT_CODE(KEYCODE_F)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("ENT / New Game")  PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CODE(KEYCODE_F1) // combine for NEW GAME
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("CL / New Game")   PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_F1) // "
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void polgar_state::polgar(machine_config &config)
{
	// basic machine hardware
	R65C02(config, m_maincpu, 4.9152_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &polgar_state::polgar_mem);

	const attotime nmi_period = attotime::from_hz(4.9152_MHz_XTAL / 0x2000);
	m_maincpu->set_periodic_int(FUNC(polgar_state::nmi_line_pulse), nmi_period);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	hc259_device &outlatch(HC259(config, "outlatch"));
	outlatch.q_out_cb<0>().set_output("led100");
	outlatch.q_out_cb<1>().set_output("led101");
	outlatch.q_out_cb<2>().set_output("led102");
	outlatch.q_out_cb<3>().set_output("led103");
	outlatch.q_out_cb<4>().set_output("led104");
	outlatch.q_out_cb<5>().set_output("led105");

	MEPHISTO_SENSORS_BOARD(config, "board");
	MEPHISTO_DISPLAY_MODULE2(config, "display");
	config.set_default_layout(layout_mephisto_polgar);
}

void polgar_state::polgar10(machine_config &config)
{
	polgar(config);

	// basic machine hardware
	W65C02(config.replace(), m_maincpu, 9.8304_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &polgar_state::polgar_mem);

	const attotime nmi_period = attotime::from_hz(9.8304_MHz_XTAL / 0x2000);
	m_maincpu->set_periodic_int(FUNC(polgar_state::nmi_line_pulse), nmi_period);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( polgar )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("polgar_1.5_01.02.1990", 0x0000, 0x10000, CRC(88d55c0f) SHA1(e86d088ec3ac68deaf90f6b3b97e3e31b1515913) )
ROM_END

ROM_START( polgara )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("polgar_1.10_04.08.1989", 0x0000, 0x10000, CRC(a4519c55) SHA1(35463a4cbcf20ebbd5ac5bc7664a862b1557c65f) ) // TC57512AD-15
ROM_END

ROM_START( polgar101 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("polg_101.bin", 0x0000, 0x10000, CRC(8fb6afa4) SHA1(d1cf868302a665ff351686b26a149ced0045fc81) )
ROM_END

ROM_START( polgar10 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("polg.10mhz_v_10.0", 0x0000, 0x10000, CRC(7c1960d4) SHA1(4d15b51f9e6f7943815945cd56078ca512a964d4) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT   COMPAT  MACHINE   INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1990, polgar,    0,       0,      polgar,   polgar, polgar_state, empty_init, "Hegener + Glaser", "Mephisto Polgar (v1.50)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, polgara,   polgar,  0,      polgar,   polgar, polgar_state, empty_init, "Hegener + Glaser", "Mephisto Polgar (v1.10)", MACHINE_SUPPORTS_SAVE )
SYST( 1990, polgar101, polgar,  0,      polgar10, polgar, polgar_state, empty_init, "Hegener + Glaser", "Mephisto Polgar 10 MHz (v10.1)", MACHINE_SUPPORTS_SAVE )
SYST( 1990, polgar10,  polgar,  0,      polgar10, polgar, polgar_state, empty_init, "Hegener + Glaser", "Mephisto Polgar 10 MHz (v10.0)", MACHINE_SUPPORTS_SAVE )



poly.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert,Nigel Barnes
// thanks-to:Andrew Trotman
/***************************************************************************

    Poly 1/2 (New Zealand)

    10/07/2011 Skeleton driver.

    http://www.cs.otago.ac.nz/homepages/andrew/poly/Poly.htm

    Andrew has supplied the roms for -bios 1

    It uses a 6809 for all main functions. There is a Z80 for CP/M, but all
    of the roms are 6809 code.

    The keyboard controller is one of those custom XR devices.
    Will use the terminal keyboard instead.

    With bios 1, after entering your userid and password, you get a black
    screen. This is normal, because it joins to a network which isn't there.

    TODO:
    - Connect up the device ports & lines
    - Find out about graphics mode and how it is selected
    - Fix Keyboard with KR2376-12 encoder
    - Poly 2 and Poly 1 (early) probably require a different keyboard matrix
    - Find out how to make 2nd teletext screen to display
    - Improve MC6854 emulation for network

****************************************************************************/

#include "emu.h"
#include "poly.h"
#include "formats/flex_dsk.h"
#include "softlist_dev.h"
#include "utf8.h"


void poly_state::poly_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).view(m_memview);
	/* System mode */
	m_memview[0](0x0000, 0xffff).rw(FUNC(poly_state::logical_mem_r), FUNC(poly_state::logical_mem_w)); // Logical Memory
	m_memview[0](0xe000, 0xe003).rw(m_pia[0], FUNC(pia6821_device::read), FUNC(pia6821_device::write));      // Video control PIA 6821
	m_memview[0](0xe004, 0xe005).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write));      // Optional RS232 Interface
	m_memview[0](0xe006, 0xe006).w(FUNC(poly_state::baud_rate_w));                                     // Baud rate controller
	m_memview[0](0xe00c, 0xe00f).rw(m_pia[1], FUNC(pia6821_device::read), FUNC(pia6821_device::write));      // Keyboard PIA 6821
	m_memview[0](0xe020, 0xe027).rw(m_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write));         // Timer 6840
	m_memview[0](0xe030, 0xe036).rw(FUNC(poly_state::network_r), FUNC(poly_state::network_w));         // Data Link Controller 6854
	m_memview[0](0xe040, 0xe040).w(FUNC(poly_state::set_protect_w));                                   // Set protect flip-flop after 1 E-cycle
	m_memview[0](0xe050, 0xe05f).ram().share("dat");                                                         // Dynamic Address Translator
	m_memview[0](0xe060, 0xe060).rw(FUNC(poly_state::select_map_r), FUNC(poly_state::select_map1_w));  // Select Map 1
	m_memview[0](0xe070, 0xe070).rw(FUNC(poly_state::select_map_r), FUNC(poly_state::select_map2_w));  // Select Map 2
	m_memview[0](0xe800, 0xefff).ram().share("videoram");                                                    // Teletext screens and System data
	m_memview[0](0xf000, 0xffff).rom().region("system", 0);                                                  // System Program ROM
	/* User mode */
	m_memview[1](0x0000, 0xffff).rw(FUNC(poly_state::logical_mem_r), FUNC(poly_state::logical_mem_w)); // Logical Memory
}

void polydev_state::poly_mem(address_map &map)
{
	poly_state::poly_mem(map);
	m_memview[0](0xe014, 0xe014).rw(FUNC(polydev_state::drive_register_r), FUNC(polydev_state::drive_register_w)); // Drive register
	m_memview[0](0xe018, 0xe01b).rw(FUNC(polydev_state::fdc_inv_r), FUNC(polydev_state::fdc_inv_w));               // Floppy controller
}


static INPUT_PORTS_START( poly1 )
	PORT_START("MODIFIERS")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Ctrl")       PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)             PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift")      PORT_CODE(KEYCODE_LSHIFT)   PORT_CODE(KEYCODE_RSHIFT)               PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Caps Lock")  PORT_CODE(KEYCODE_CAPSLOCK)                             PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))

	PORT_START("X0")
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)      PORT_NAME("Keypad 1 Red")
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)      PORT_NAME("Keypad 0")
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME)       PORT_NAME("Help Calc")
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD)    PORT_NAME("Keypad .")
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X1")
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)      PORT_NAME("Keypad 3 Yellow")
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)      PORT_NAME("Keypad 4 Blue")
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)      PORT_NAME("Keypad 2 Green")
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT)     PORT_NAME("Ins Char Line")
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN)       PORT_NAME("Repeat Next")
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X2")
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)      PORT_NAME("Keypad 6 Cyan")
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)      PORT_NAME("Keypad 7 White")
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)      PORT_NAME("Keypad 5 Magenta")
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)        PORT_NAME("Del Char Line")
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP)       PORT_NAME("Back Exit")
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X3")
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)      PORT_NAME("Keypad 9")
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)      PORT_NAME("Keypad 8 Flash")
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR('@')   PORT_CHAR(0xa3)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_END)        PORT_NAME("Pause")
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)         PORT_NAME(UTF8_UP)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)      PORT_NAME(UTF8_RIGHT)
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)       PORT_NAME(UTF8_DOWN)
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)       PORT_NAME(UTF8_LEFT)

	PORT_START("X4")
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z')   PORT_CHAR('Z')
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)          PORT_CHAR('x')   PORT_CHAR('X')
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)          PORT_CHAR('c')   PORT_CHAR('C')
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)          PORT_CHAR('v')   PORT_CHAR('V')
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)          PORT_CHAR('b')   PORT_CHAR('B')
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)          PORT_CHAR('n')   PORT_CHAR('N')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)          PORT_CHAR('m')   PORT_CHAR('M')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')   PORT_CHAR('<')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')   PORT_CHAR('>')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/')   PORT_CHAR('?')
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X5")
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('a')   PORT_CHAR('A')
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)          PORT_CHAR('s')   PORT_CHAR('S')
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)          PORT_CHAR('d')   PORT_CHAR('D')
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)          PORT_CHAR('f')   PORT_CHAR('F')
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)          PORT_CHAR('g')   PORT_CHAR('G')
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)          PORT_CHAR('h')   PORT_CHAR('H')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)          PORT_CHAR('j')   PORT_CHAR('J')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)          PORT_CHAR('k')   PORT_CHAR('K')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)          PORT_CHAR('l')   PORT_CHAR('L')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';')   PORT_CHAR('+')
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(':')   PORT_CHAR('*')

	PORT_START("X6")
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q')   PORT_CHAR('Q')
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)          PORT_CHAR('w')   PORT_CHAR('W')
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)          PORT_CHAR('e')   PORT_CHAR('E')
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)          PORT_CHAR('r')   PORT_CHAR('R')
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)          PORT_CHAR('t')   PORT_CHAR('T')
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y')   PORT_CHAR('Y')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)          PORT_CHAR('u')   PORT_CHAR('U')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)          PORT_CHAR('i')   PORT_CHAR('I')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)          PORT_CHAR('o')   PORT_CHAR('O')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)          PORT_CHAR('p')   PORT_CHAR('P')
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_NAME("EXP \xE2\x80\x96")

	PORT_START("X7")
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)          PORT_CHAR('1')   PORT_CHAR('!')
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)          PORT_CHAR('2')   PORT_CHAR('"')
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)          PORT_CHAR('3')   PORT_CHAR('#')
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)          PORT_CHAR('4')   PORT_CHAR('$')
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)          PORT_CHAR('5')   PORT_CHAR('%')
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_CHAR('6')   PORT_CHAR('&')
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7')   PORT_CHAR('\'')
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8')   PORT_CHAR('(')
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_CHAR('9')   PORT_CHAR(')')
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_CHAR('0')
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-')   PORT_CHAR('=')
INPUT_PORTS_END

static INPUT_PORTS_START( poly1e )
	PORT_INCLUDE(poly1)
	// TODO: unknown matrix, contains extra user definable keys
INPUT_PORTS_END

static INPUT_PORTS_START( poly2 )
	PORT_INCLUDE(poly1)
	// TODO: unknown matrix, seems to differ from poly1
INPUT_PORTS_END


void poly_state::machine_start()
{
	m_dat_bank = 0;
	m_protect_timer = timer_alloc(FUNC(poly_state::set_protect), this);
}


void poly_state::machine_reset()
{
	m_protect_timer->adjust(attotime::never);

	//m_kr2376->set_input_pin(kr2376_device::KR2376_DSII, 0);
	//m_kr2376->set_input_pin(kr2376_device::KR2376_PII, 0);

	/* system mode is entered on Reset */
	m_memview.select(0);
}


void poly_state::init_poly()
{
	uint8_t bitswapped[0x4000];
	uint8_t *rom;

	/* basic rom region */
	rom = m_user->base() + 0xc000;

	/* decrypt rom data lines */
	for (int i = 0x0000; i<0x4000; i++)
		bitswapped[i] = bitswap<8>(rom[i], 3, 4, 2, 5, 1, 6, 0, 7);

	/* decrypt rom address lines */
	for (int i = 0x0000; i<0x4000; i++)
		rom[i] = bitswapped[bitswap<16>(i, 15, 14, 13, 12, 10, 8, 4, 2, 0, 1, 3, 5, 6, 7, 9, 11)];

	/* system rom region */
	rom = m_system->base();

	/* decrypt rom data lines */
	for (int i = 0x0000; i<0x1000; i++)
		bitswapped[i] = bitswap<8>(rom[i], 3, 4, 2, 5, 1, 6, 0, 7);

	/* decrypt rom address lines */
	for (int i = 0x0000; i<0x1000; i++)
		rom[i] = bitswapped[bitswap<16>(i, 15, 14, 13, 12, 10, 8, 4, 2, 0, 1, 3, 5, 6, 7, 9, 11)];
}


void polydev_state::floppy_formats(format_registration &fr)
{
	fr.add_fm_containers();
	fr.add(FLOPPY_FLEX_FORMAT);
}

static void poly_floppies(device_slot_interface &device)
{
	device.option_add("8dssd", FLOPPY_8_DSSD);
	device.option_add("525sd", FLOPPY_525_SD);
}


void poly_state::poly(machine_config &config)
{
	/* basic machine hardware */
	MC6809(config, m_maincpu, 12.0576_MHz_XTAL / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &poly_state::poly_mem);
	m_maincpu->interrupt_vector_read().set(FUNC(poly_state::vector_r));

	INPUT_MERGER_ANY_HIGH(config, "irqs").output_handler().set_inputline(m_maincpu, M6809_IRQ_LINE);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.set_size(40 * 12, 24 * 20);
	screen.set_visarea(0, 40 * 12 - 1, 0, 24 * 20 - 1);
	screen.set_screen_update(FUNC(poly_state::screen_update));

	SAA5050(config, m_trom[0], 12.0576_MHz_XTAL / 2);
	m_trom[0]->d_cb().set(FUNC(poly_state::videoram_1_r));
	m_trom[0]->set_screen_size(40, 24, 40);

	SAA5050(config, m_trom[1], 12.0576_MHz_XTAL / 2);
	m_trom[1]->d_cb().set(FUNC(poly_state::videoram_2_r));
	m_trom[1]->set_screen_size(40, 24, 40);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* internal ram */
	RAM(config, m_ram).set_default_size("128K").set_extra_options("64K");

	/* network */
	MC6854(config, m_adlc);
	//m_adlc->out_txd_cb().set(NETWORK_TAG, FUNC(poly_network_device::data_w));
	//m_adlc->out_irq_cb().set("irqs", FUNC(input_merger_device::in_w<0>));

	//MCFG_POLY_NETWORK_ADD()
	//MCFG_POLY_NETWORK_CLK_CB(WRITELINE(*this, poly_state, network_clk_w))
	//MCFG_POLY_NETWORK_DATA_CB(WRITELINE("mc6854", mc6854_device, set_rx))
	//MCFG_POLY_NETWORK_SLOT_ADD("netup", poly_network_devices, "proteus")
	//MCFG_POLY_NETWORK_SLOT_ADD("netdown", poly_network_devices, nullptr)

	/* timer */
	PTM6840(config, m_ptm, 12.0576_MHz_XTAL / 3);
	m_ptm->set_external_clocks(0, 0, 0);
	m_ptm->o2_callback().set(FUNC(poly_state::ptm_o2_callback));
	m_ptm->o3_callback().set(FUNC(poly_state::ptm_o3_callback));
	//m_ptm->irq_callback().set("irqs", FUNC(input_merger_device::in_w<1>));

	/* keyboard encoder */
	//KR2376_12(config, m_kr2376, 50000);
	//m_kr2376->x<0>().set_ioport("X0");
	//m_kr2376->x<1>().set_ioport("X1");
	//m_kr2376->x<2>().set_ioport("X2");
	//m_kr2376->x<3>().set_ioport("X3");
	//m_kr2376->x<4>().set_ioport("X4");
	//m_kr2376->x<5>().set_ioport("X5");
	//m_kr2376->x<6>().set_ioport("X6");
	//m_kr2376->x<7>().set_ioport("X7");
	//m_kr2376->shift().set(FUNC(poly_state::kbd_shift_r));
	//m_kr2376->control().set(FUNC(poly_state::kbd_control_r));
	//m_kr2376->strobe().set("pia1", FUNC(pia6821_device::cb1_w));

	/* generic keyboard until ROM in KR2376-12 is known */
	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(poly_state::kbd_put));

	/* video control */
	PIA6821(config, m_pia[0]);
	m_pia[0]->writepa_handler().set(FUNC(poly_state::pia0_pa_w));
	m_pia[0]->writepb_handler().set(FUNC(poly_state::pia0_pb_w));
	m_pia[0]->irqa_handler().set("irqs", FUNC(input_merger_device::in_w<2>));
	m_pia[0]->irqb_handler().set("irqs", FUNC(input_merger_device::in_w<3>));

	/* keyboard PIA */
	PIA6821(config, m_pia[1]);
	m_pia[1]->readpb_handler().set(FUNC(poly_state::pia1_b_in));
	m_pia[1]->irqa_handler().set("irqs", FUNC(input_merger_device::in_w<4>));
	m_pia[1]->irqb_handler().set("irqs", FUNC(input_merger_device::in_w<5>));

	/* optional rs232 interface */
	ACIA6850(config, m_acia, 0);
	//m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	//m_acia->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_acia->irq_handler().set("irqs", FUNC(input_merger_device::in_w<6>));

	CLOCK(config, m_acia_clock, 153600);
	m_acia_clock->signal_handler().set(m_acia, FUNC(acia6850_device::write_txc));
	m_acia_clock->signal_handler().append(m_acia, FUNC(acia6850_device::write_rxc));

	/* software lists */
	SOFTWARE_LIST(config, "flop_list").set_original("poly_flop").set_filter("POLY1");
}


void poly_state::poly2(machine_config &config)
{
	poly(config);

	/* internal ram */
	m_ram->set_default_size("128K");

	/* software lists */
	subdevice<software_list_device>("flop_list")->set_filter("POLY2");
}


void polydev_state::polydev(machine_config &config)
{
	poly(config);

	/* fdc */
	FD1771(config, m_fdc, 12.0_MHz_XTAL / 12);
	m_fdc->hld_wr_callback().set(FUNC(polydev_state::motor_w));
	m_fdc->set_force_ready(true);

	FLOPPY_CONNECTOR(config, "fdc:0", poly_floppies, "525sd", polydev_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", poly_floppies, nullptr, polydev_state::floppy_formats).enable_sound(true);

	/* remove devices*/
	//config.device_remove("netup");
	//config.device_remove("netdown");
}


/* ROM definition */
ROM_START( poly1 )
	ROM_REGION( 0x20000, "user", 0 )
	ROM_SYSTEM_BIOS(0, "bas34", "PolyBASIC 3.4")
	ROMX_LOAD( "bas1.u92",       0xc000, 0x1000, CRC(04d96d81) SHA1(8d2d0980afb8c447cf235325e4dc15ed2a200f16), ROM_BIOS(0) )
	ROMX_LOAD( "bas2.u91",       0xd000, 0x1000, CRC(3e12f823) SHA1(0b37f2dfa241fac1bf06ca93b19e44a660c7758e), ROM_BIOS(0) )
	ROMX_LOAD( "bas3.u90",       0xe000, 0x1000, CRC(22759a84) SHA1(d22edea312567596b4ef92290ffe52a25de01487), ROM_BIOS(0) )
	ROMX_LOAD( "bas4.u89",       0xf000, 0x1000, CRC(30650d92) SHA1(4e41ea2ec127b9ed277f5a62d52cb3432d64aa84), ROM_BIOS(0) )

	ROM_REGION( 0x1000, "system", 0 )
	ROMX_LOAD( "bios.u86",       0x0000, 0x1000, CRC(fc97cc6a) SHA1(103dce01a86a47e7e235c9d2f820fa1501ab9800), ROM_BIOS(0) )
ROM_END

ROM_START( poly1e )
	ROM_REGION( 0x20000, "user", 0 )
	ROM_SYSTEM_BIOS(0, "bas23", "PolyBASIC 2.3")
	ROMX_LOAD( "v3bas1.u92",     0xc000, 0x1000, CRC(ee25fe89) SHA1(af1a73434c9f5524c5a1a5e19d06500ad1b643d1), ROM_BIOS(0) )
	ROMX_LOAD( "v3bas2.u91",     0xd000, 0x1000, CRC(6ca4a8b5) SHA1(54e71e34b55a5ee41a9e0da05d9c9cbcb2fb80c2), ROM_BIOS(0) )
	ROMX_LOAD( "v3bas3.u90",     0xe000, 0x1000, CRC(6021fc00) SHA1(214aab19e096ddfd417993d48c11f81ec139fef3), ROM_BIOS(0) )
	ROMX_LOAD( "v3bas4.u89",     0xf000, 0x1000, CRC(df071e52) SHA1(41482517a5dfc64f3728732b3907cee636674de4), ROM_BIOS(0) )

	ROM_REGION( 0x1000, "system", 0 )
	ROMX_LOAD( "plrt16v3e9.u86", 0x0000, 0x1000, CRC(f7e3aa86) SHA1(b642c281e54ad9698cfaec19508ae8b4df50c296), ROM_BIOS(0) )
ROM_END

ROM_START( poly2 )
	ROM_DEFAULT_BIOS("bas31")
	ROM_SYSTEM_BIOS(0, "bas31", "PolyBASIC 3.1")
	ROM_SYSTEM_BIOS(1, "bas30", "PolyBASIC 3.0")

	ROM_REGION( 0x20000, "user", 0 )
	ROMX_LOAD( "bas1.u92",           0xc000, 0x1000, CRC(340b7d75) SHA1(330d31b5c90c82c08c62e2df40669ca62c8fffed), ROM_BIOS(0) )
	ROMX_LOAD( "bas2.u91",           0xd000, 0x1000, CRC(45152d26) SHA1(7da2663f253031a587705b9db9a18e93ba4db2cf), ROM_BIOS(0) )
	ROMX_LOAD( "bas3.u90",           0xe000, 0x1000, CRC(a6f70e62) SHA1(7912a9da29d682bb5922f3adf6136b4efc0494dc), ROM_BIOS(0) )
	ROMX_LOAD( "bas4.u89",           0xf000, 0x1000, CRC(72d21cea) SHA1(d413490d043845ce4a9cf5cc70bd92ddc931c837), ROM_BIOS(0) )

	ROMX_LOAD( "bas1-12-12-84.u92",  0xc000, 0x1000, CRC(a3791342) SHA1(e801db28419eedf6eebdbb5a7eb551ba36c43cd6), ROM_BIOS(1) )
	ROMX_LOAD( "bas2-12-12-84.u91",  0xd000, 0x1000, CRC(3bb5849e) SHA1(71c47a0d3dba096a6a79300edf56ffedce276ac6), ROM_BIOS(1) )
	ROMX_LOAD( "bas3-12-12-84.u90",  0xe000, 0x1000, CRC(a6f70e62) SHA1(7912a9da29d682bb5922f3adf6136b4efc0494dc), ROM_BIOS(1) )
	ROMX_LOAD( "bas4-12-12-84.u89",  0xf000, 0x1000, CRC(8f736cee) SHA1(3ec5cc49a426fc921bbadb2bcc1b86b4768627fa), ROM_BIOS(1) )


	ROM_REGION( 0x1000, "system", 0 )
	ROMX_LOAD( "sys31.u86",          0x0000, 0x1000, CRC(fb54c36e) SHA1(934f84a7a99a76b0a017379a6ecd8a8e444cd085), ROM_BIOS(0) )
	ROMX_LOAD( "plrt17-5-11-84.u86", 0x0000, 0x1000, CRC(896165dd) SHA1(005584310f1c689a9b1bb549989c5fedabead6c4), ROM_BIOS(1) )
ROM_END

ROM_START( polydev )
	ROM_REGION( 0x20000, "user", 0 )
	ROM_SYSTEM_BIOS(0, "bas34", "PolyBASIC 3.4")
	ROMX_LOAD( "v2bas1.bin",         0xc000, 0x1000, CRC(04d96d81) SHA1(8d2d0980afb8c447cf235325e4dc15ed2a200f16), ROM_BIOS(0) )
	ROMX_LOAD( "v2bas2.bin",         0xd000, 0x1000, CRC(3e12f823) SHA1(0b37f2dfa241fac1bf06ca93b19e44a660c7758e), ROM_BIOS(0) )
	ROMX_LOAD( "v2bas3.bin",         0xe000, 0x1000, CRC(22759a84) SHA1(d22edea312567596b4ef92290ffe52a25de01487), ROM_BIOS(0) )
	ROMX_LOAD( "v2bas4.bin",         0xf000, 0x1000, CRC(30650d92) SHA1(4e41ea2ec127b9ed277f5a62d52cb3432d64aa84), ROM_BIOS(0) )

	ROM_REGION( 0x1000, "system", 0 )
	ROMX_LOAD( "slrt15_00f9.bin",    0x0000, 0x1000, CRC(046a9aef) SHA1(c5c74b0f66e8969c12db03899244e18228be38eb), ROM_BIOS(0) )
ROM_END

/* Driver */

/*    YEAR   NAME     PARENT  COMPAT   MACHINE    INPUT   CLASS          INIT       COMPANY     FULLNAME                               FLAGS */
COMP( 1981,  poly1,   0,      0,       poly,      poly1,  poly_state,    init_poly, "Polycorp", "Poly 1 Educational Computer",         MACHINE_NOT_WORKING )
COMP( 1981,  poly1e,  poly1,  0,       poly,      poly1e, poly_state,    init_poly, "Polycorp", "Poly 1 Educational Computer (early)", MACHINE_NOT_WORKING )
COMP( 1984,  poly2,   poly1,  0,       poly2,     poly2,  poly_state,    init_poly, "Polycorp", "Poly 2 Learning System",              MACHINE_NOT_WORKING )
COMP( 1983,  polydev, poly1,  0,       polydev,   poly1,  polydev_state, init_poly, "Polycorp", "Poly Development System",             MACHINE_NOT_WORKING )



poly61.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Korg Poly-61

****************************************************************************/

#include "emu.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/i8255.h"
#include "machine/nvram.h"
#include "machine/pit8253.h"


namespace {

class poly61_state : public driver_device
{
public:
	poly61_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_progmcu(*this, "progmcu")
		, m_keymcu(*this, "keymcu")
		, m_ppi(*this, "ppi%u", 0U)
		, m_pit(*this, "pit%u", 0U)
	{
	}

	virtual void machine_start() override ATTR_COLD;

	required_device<mcs48_cpu_device> m_progmcu;
	required_device<mcs48_cpu_device> m_keymcu;
	std::unique_ptr<u8[]> m_nvram_ptr;

	void poly61(machine_config &config);

private:
	u8 ext_r(offs_t offset);
	void ext_w(offs_t offset, u8 data);

	void prog_ext_map(address_map &map) ATTR_COLD;

	required_device_array<i8255_device, 3> m_ppi;
	required_device_array<pit8253_device, 4> m_pit;
};

void poly61_state::machine_start()
{
	m_nvram_ptr = make_unique_clear<u8[]>(0x400);
	subdevice<nvram_device>("nvram")->set_base(&m_nvram_ptr[0], 0x400);

	save_pointer(NAME(m_nvram_ptr), 0x400);
}

u8 poly61_state::ext_r(offs_t offset)
{
	if (!BIT(m_progmcu->p2_r(), 5))
	{
		u8 p1 = m_progmcu->p1_r();
		return m_nvram_ptr[(p1 & 1) | (offset << 1) | (p1 & 2) << 8] | 0xf0;
	}

	return 0xff;
}

void poly61_state::ext_w(offs_t offset, u8 data)
{
	u8 p2 = m_progmcu->p2_r();

	if (!BIT(p2, 5))
	{
		u8 p1 = m_progmcu->p1_r();
		m_nvram_ptr[(p1 & 1) | (offset << 1) | (p1 & 2) << 8] = data & 0x0f;
	}

	if (!BIT(p2, 6))
	{
		if (!BIT(offset, 5) && (offset & 0x0c) != 0x0c)
			m_ppi[(offset & 0x0c) >> 2]->write(offset & 0x03, data);
		if (!BIT(offset, 6))
			m_pit[(offset & 0x0c) >> 2]->write(offset & 0x03, data);
	}
}

void poly61_state::prog_ext_map(address_map &map)
{
	map(0x00, 0xff).rw(FUNC(poly61_state::ext_r), FUNC(poly61_state::ext_w));
}

static INPUT_PORTS_START(poly61)
INPUT_PORTS_END

void poly61_state::poly61(machine_config &config)
{
	I8049(config, m_progmcu, 6_MHz_XTAL);
	m_progmcu->set_addrmap(AS_IO, &poly61_state::prog_ext_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // M58981P-45 + battery

	I8255(config, m_ppi[0]);
	I8255(config, m_ppi[1]);
	I8255(config, m_ppi[2]);

	PIT8253(config, m_pit[0]);
	PIT8253(config, m_pit[1]);
	PIT8253(config, m_pit[2]);
	PIT8253(config, m_pit[3]);

	I8049(config, m_keymcu, 6_MHz_XTAL);
}

ROM_START(poly61)
	ROM_REGION(0x800, "progmcu", 0)
	ROM_LOAD("d8049c-337.bin", 0x000, 0x800, CRC(51edf723) SHA1(9001687b25a841b7a5f78bbae03745f3e995fa83))

	ROM_REGION(0x800, "keymcu", 0)
	ROM_DEFAULT_BIOS("new")
	ROM_SYSTEM_BIOS(0, "old", "Older MCU")
	ROMX_LOAD("d8049c-217.bin", 0x000, 0x800, CRC(246d7767) SHA1(5b608c750e7fe7832070a53a74df416fd132ecb7), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "new", "Newer MCU")
	ROMX_LOAD("d8049c-384.bin", 0x000, 0x800, CRC(bbc421b5) SHA1(be683cdce5cbf867c5b8a52802288c1296f5fbd1), ROM_BIOS(1))
ROM_END

} // anonymous namespace


SYST(1982, poly61,  0, 0, poly61,  poly61, poly61_state,  empty_init, "Korg", "Poly-61 Programmable Polyphonic Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



poly800.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Korg Poly-800 synthesizer.

****************************************************************************/

#include "emu.h"
#include "bus/midi/midi.h"
#include "cpu/i8085/i8085.h"
#include "machine/6850acia.h"
#include "machine/i8155.h"
#include "machine/nvram.h"
//#include "sound/msm5232.h"


namespace {

class poly800_state : public driver_device
{
public:
	poly800_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void poly800(machine_config &config);
	void poly800mdk(machine_config &config);

protected:
	void common_map(address_map &map) ATTR_COLD;

private:
	void mem_map(address_map &map) ATTR_COLD;
	void mdk_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

protected:
	required_device<i8085a_cpu_device> m_maincpu;
};

class poly800ii_state : public poly800_state
{
public:
	poly800ii_state(const machine_config &mconfig, device_type type, const char *tag)
		: poly800_state(mconfig, type, tag)
	{
	}

	void poly800ii(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;
};

void poly800_state::common_map(address_map &map)
{
	map(0x4000, 0x40ff).mirror(0x1f00).rw("pio", FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w));
	map(0xe000, 0xe001).mirror(0x1f7e).w("acia", FUNC(acia6850_device::write));
	map(0xe080, 0xe081).mirror(0x1f7e).r("acia", FUNC(acia6850_device::read));
}

void poly800_state::mem_map(address_map &map)
{
	map(0x0000, 0x1fff).rom().region("klm596", 0);
	map(0x2000, 0x27ff).mirror(0x1800).ram().share("nvram");
	common_map(map);
}

void poly800_state::mdk_map(address_map &map)
{
	map(0x0000, 0x2fff).rom().region("klm596", 0);
	map(0x3000, 0x37ff).mirror(0x0800).ram().share("nvram");
	common_map(map);
}

void poly800ii_state::mem_map(address_map &map)
{
	map(0x0000, 0x27ff).rom().region("klm779", 0);
	map(0x2800, 0x37ff).ram().share("nvram");
	common_map(map);
}

void poly800_state::io_map(address_map &map)
{
	map(0x40, 0x47).mirror(0x18).rw("pio", FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
}

static INPUT_PORTS_START(poly800)
INPUT_PORTS_END

void poly800_state::poly800(machine_config &config)
{
	I8085A(config, m_maincpu, 5_MHz_XTAL); // MSM80C85ARS
	m_maincpu->set_addrmap(AS_PROGRAM, &poly800_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &poly800_state::io_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // HM6116LP-4 + battery

	i8155_device &pio(I8155(config, "pio", 5_MHz_XTAL / 2)); // MSM81C55RS
	pio.out_to_callback().set("acia", FUNC(acia6850_device::write_rxc));
	pio.out_to_callback().append("acia", FUNC(acia6850_device::write_txc));

	acia6850_device &acia(ACIA6850(config, "acia")); // HD63B50P
	acia.txd_handler().set("midi_out", FUNC(midi_port_device::write_txd));
	acia.irq_handler().set_inputline(m_maincpu, I8085_RST65_LINE);

	//MSM5232(config, "msm"); // MSM-5232RS

	MIDI_PORT(config, "midi_in", midiin_slot, "midiin").rxd_handler().set("acia", FUNC(acia6850_device::write_rxd));
	MIDI_PORT(config, "midi_out", midiout_slot, "midiout");
}

void poly800_state::poly800mdk(machine_config &config)
{
	poly800(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &poly800_state::mdk_map);
}

void poly800ii_state::poly800ii(machine_config &config)
{
	poly800(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &poly800ii_state::mem_map);

	// NVRAM is uPD4464C-15 + battery with A12 tied to GND
}

ROM_START(poly800)
	ROM_REGION(0x2002, "klm596", 0)
	ROM_SYSTEM_BIOS(0, "v36", "Version 36")
	ROM_SYSTEM_BIOS(1, "v30", "Version 30")
	ROMX_LOAD("830236.ic22", 0x0000, 0x2000, CRC(16cd416d) SHA1(a1f6f5be4f70b47ea4d1dccd723179c9991f4a92), ROM_BIOS(0))
	ROMX_LOAD("830230.ic22", 0x0000, 0x2002, CRC(1ba81f55) SHA1(94a0d45680d107ab4d1b542e8378a599ded36f06), ROM_BIOS(1))
	// Where did the extra 2 bytes at the end come from?
ROM_END

ROM_START(poly800mdk)
	ROM_REGION(0x4000, "klm596", 0)
	ROM_LOAD("mdk.ic22", 0x0000, 0x4000, CRC(a25ab16a) SHA1(a9294bf90a8fa81dd825bcbf9fcd3b5014045936)) // 27C128
ROM_END

ROM_START(poly800ii)
	ROM_REGION(0x4000, "klm779", 0)
	ROM_LOAD("851005.ic24", 0x0000, 0x4000, CRC(09ae4fc5) SHA1(1e8a418919ef61334fb55aefa8aaebcaefc28ebd)) // 27C128 (last 3/8ths not addressable)
ROM_END

} // anonymous namespace


SYST(1984, poly800,    0, 0, poly800,    poly800, poly800_state,   empty_init, "Korg", "Poly-800 Programmable Polyphonic Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1984, poly800mdk, 0, 0, poly800mdk, poly800, poly800_state,   empty_init, "Korg", "Poly-800 Programmable Polyphonic Synthesizer (MIDI Dump Kit)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1986, poly800ii,  0, 0, poly800ii,  poly800, poly800ii_state, empty_init, "Korg", "Poly-800II Programmable Polyphonic Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



poly88.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

Poly-88 driver by Miodrag Milanovic

2009-05-18 Initial implementation
2019-05-25 Poly8813 new roms

All input must be UPPERcase.

11K is the minimal amount of user RAM required to run the later version of
BASIC. PolyMorphic's "System 16" package shipped with 16K of RAM (as did
the 8813), though their earlier systems had only 8K or less.

ToDo:
- More accurate interrupt emulation.
- Single-step control.
- .CAS file format support (http://deramp.com/polymorphic-computers/emu88.html).

Poly-8813 is a disk-based computer with 3 mini-floppy drives.
Booting is done by pressing the "Load" button, mounted on the
front panel near the power switch. Although user manuals are easy
to obtain, technical information and drive controller schematics
are not. The disk format is known to be 256 bytes per sector, 10
sectors per track, 35 tracks, single sided, for a total of 89600
bytes.

Notes for old poly8813 roms:

The Poly-8813 BIOS makes use of undocumented instructions which we
do not currently emulate. These are at 006A (print a character
routine - ED ED 05); another is at 0100 (move memory routine -
ED ED 03); the last is at 087B (disk I/O routine - ED ED 01). The
code at 0100 can be replaced by 7E 12 13 23 03 79 B0 C2 00 01 C9,
which exactly fits into the available space. The routine at 006A is
likewise could be exactly replaced with F5 C5 D5 E5, which enters
a display routine that appears in other assembly listings but seems
to have no entry point here. Since the ED ED opcode is defined as
for CALLN in the NEC V20/V30's 8080 mode, it might be the case that
these are actually hooks patched into the original code for
emulation purposes. (There is also a slim possibility that this
opcode invokes an undocumented feature of the NEC uPD8080AF, which
at least some models of the Poly-88 are known to have used.)

****************************************************************************/

#include "emu.h"
#include "poly88.h"

#include "bus/s100/ascsasi.h"
#include "bus/s100/poly16k.h"
#include "bus/s100/polyfdc.h"
#include "bus/s100/polyvti.h"
#include "bus/s100/seals8k.h"

#include "cpu/i8085/i8085.h"
#include "imagedev/cassette.h"
#include "speaker.h"


void poly88_state::s100_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(poly88_state::mem_r), FUNC(poly88_state::mem_w));
}

void poly88_state::s100_io(address_map &map)
{
	map(0x00, 0xff).rw(FUNC(poly88_state::in_r), FUNC(poly88_state::out_w));
}

void poly88_state::poly88_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x01).mirror(2).rw(m_usart, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x04, 0x04).mirror(3).w(FUNC(poly88_state::baud_rate_w));
	map(0x08, 0x08).mirror(3).w(FUNC(poly88_state::intr_w));
}

void poly88_state::poly8813_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x01).mirror(2).rw(m_usart, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x04, 0x04).mirror(3).w(FUNC(poly88_state::baud_rate_w));
	map(0x08, 0x0b); //.r(ROM on for CPM).w(RTC reset);
	map(0x0c, 0x0f); //.r(ROM off for CPM).w(Single-step trigger);
}

/* Input ports */
static INPUT_PORTS_START( poly88 )
	PORT_START("CONFIG")
	PORT_CONFNAME(0x80, 0x00, "Tape Mode")
	PORT_CONFSETTING(   0x00, "Byte (300 baud)")
	PORT_CONFSETTING(   0x80, "Polyphase (2400 baud)")

	PORT_START("ONBOARD")
	PORT_CONFNAME(7, 0, "Onboard Addresses")
	PORT_CONFSETTING(0, "0000-0FFF (M), 00-0F (I/O)") // jumper J
	PORT_CONFSETTING(4, "8000-8FFF (M), 80-8F (I/O)") // jumper S
	PORT_CONFSETTING(7, "E000-EFFF (M), E0-EF (I/O)") // jumper T
INPUT_PORTS_END


static void poly88_s100_devices(device_slot_interface &device)
{
	device.option_add("vti", S100_POLY_VTI);
	device.option_add("8ksc", S100_8K_SC);
	device.option_add("8kscbb", S100_8K_SC_BB);
	device.option_add("poly16k", S100_POLY_16K);
	device.option_add("polyfdc", S100_POLY_FDC);
	device.option_add("ascsasi", S100_ASC_SASI);
}

DEVICE_INPUT_DEFAULTS_START(poly88_vti_1800)
	DEVICE_INPUT_DEFAULTS("ADDRESS", 0x3f, 0x06) // 1800-1FFF
DEVICE_INPUT_DEFAULTS_END

DEVICE_INPUT_DEFAULTS_START(poly88_16k_2000)
	DEVICE_INPUT_DEFAULTS("DSW", 0xf, 0xd) // 2000-5FFF
DEVICE_INPUT_DEFAULTS_END

DEVICE_INPUT_DEFAULTS_START(poly88_16k_6000)
	DEVICE_INPUT_DEFAULTS("DSW", 0xf, 0x9) // 6000-9FFF
DEVICE_INPUT_DEFAULTS_END

DEVICE_INPUT_DEFAULTS_START(poly88_16k_a000)
	DEVICE_INPUT_DEFAULTS("DSW", 0xf, 0x5) // A000-DFFF
DEVICE_INPUT_DEFAULTS_END

void poly88_state::poly88(machine_config &config)
{
	/* basic machine hardware */
	I8080A(config, m_maincpu, 16.5888_MHz_XTAL / 9); // uses 8224 clock generator
	m_maincpu->set_addrmap(AS_PROGRAM, &poly88_state::s100_mem);
	m_maincpu->set_addrmap(AS_IO, &poly88_state::s100_io);
	m_maincpu->set_irq_acknowledge_callback(FUNC(poly88_state::poly88_irq_callback));

	ADDRESS_MAP_BANK(config, m_onboard_io);
	m_onboard_io->set_addrmap(0, &poly88_state::poly88_io);
	m_onboard_io->set_data_width(8);
	m_onboard_io->set_addr_width(4);

	TIMER(config, "rtc").configure_periodic(FUNC(poly88_state::rtc_tick), attotime::from_hz(60)); // from AC power

	/* audio hardware */
	SPEAKER(config, "mono").front_center();

	/* cassette */
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	TIMER(config, "kansas_r").configure_periodic(FUNC(poly88_state::kansas_r), attotime::from_hz(38400));

	/* uart */
	I8251(config, m_usart, 16.5888_MHz_XTAL / 9);
	m_usart->rxrdy_handler().set(FUNC(poly88_state::usart_ready_w));
	m_usart->txrdy_handler().set(FUNC(poly88_state::usart_ready_w));
	m_usart->txd_handler().set([this] (bool state) { m_txd = state; });
	m_usart->dtr_handler().set([this] (bool state) { m_dtr = state; });
	m_usart->rts_handler().set([this] (bool state) { m_rts = state; });

	MM5307AA(config, m_brg, 16.5888_MHz_XTAL / 18);
	m_brg->output_cb().set(FUNC(poly88_state::cassette_clock_w));

	/* snapshot */
	SNAPSHOT(config, "snapshot", "img", attotime::from_seconds(2)).set_load_callback(FUNC(poly88_state::snapshot_cb));

	S100_BUS(config, m_s100, 16.5888_MHz_XTAL / 9);
	m_s100->vi2().set(FUNC(poly88_state::vi2_w));
	m_s100->vi5().set(FUNC(poly88_state::vi5_w));

	// Poly-88 backplane has 5 slots, but CPU uses one
	S100_SLOT(config, m_s100_slot[0], poly88_s100_devices, "vti");
	S100_SLOT(config, m_s100_slot[1], poly88_s100_devices, "poly16k");
	S100_SLOT(config, m_s100_slot[2], poly88_s100_devices, nullptr);
	S100_SLOT(config, m_s100_slot[3], poly88_s100_devices, nullptr);

	m_s100_slot[1]->set_option_device_input_defaults("poly16k", DEVICE_INPUT_DEFAULTS_NAME(poly88_16k_2000));
	m_s100_slot[2]->set_option_device_input_defaults("poly16k", DEVICE_INPUT_DEFAULTS_NAME(poly88_16k_6000));
	m_s100_slot[3]->set_option_device_input_defaults("poly16k", DEVICE_INPUT_DEFAULTS_NAME(poly88_16k_a000));
}

void poly88_state::poly8813(machine_config &config)
{
	poly88(config);
	m_onboard_io->set_addrmap(0, &poly88_state::poly8813_io);

	m_s100_slot[0]->set_option_device_input_defaults("vti", DEVICE_INPUT_DEFAULTS_NAME(poly88_vti_1800));
	m_s100_slot[1]->set_default_option("polyfdc");
	m_s100_slot[2]->set_default_option("poly16k");

	// Poly-8813 backplane has 10 slots, but CPU uses one
	S100_SLOT(config, m_s100_slot[4], poly88_s100_devices, nullptr);
	S100_SLOT(config, m_s100_slot[5], poly88_s100_devices, nullptr);
	S100_SLOT(config, m_s100_slot[6], poly88_s100_devices, nullptr);
	S100_SLOT(config, m_s100_slot[7], poly88_s100_devices, nullptr);
	S100_SLOT(config, m_s100_slot[8], poly88_s100_devices, nullptr);

	m_s100_slot[2]->set_option_device_input_defaults("poly16k", DEVICE_INPUT_DEFAULTS_NAME(poly88_16k_2000));
	m_s100_slot[3]->set_option_device_input_defaults("poly16k", DEVICE_INPUT_DEFAULTS_NAME(poly88_16k_6000));
	m_s100_slot[4]->set_option_device_input_defaults("poly16k", DEVICE_INPUT_DEFAULTS_NAME(poly88_16k_a000));
}

/* ROM definition */
ROM_START( poly88 )
	ROM_REGION( 0xc00, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "polymon4.bin", 0x0000, 0x0400, CRC(0baa1a4c) SHA1(c6cf4b89bdde200813d34aab08150d5f3025ce33))
	ROM_LOAD( "tbasic_1.rom", 0x0400, 0x0400, CRC(ec22740e) SHA1(bc606c58ef5f046200bdf402eda66ec070464306))
	ROM_LOAD( "tbasic_2.rom", 0x0800, 0x0400, CRC(f2619232) SHA1(eb6fb0356d2fb153111cfddf39eab10253cb4c53))
ROM_END

ROM_START( poly8813 )
	ROM_REGION( 0xc00, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "poly8813.27",  0x0000, 0x0400, CRC(0baa1a4c) SHA1(c6cf4b89bdde200813d34aab08150d5f3025ce33) )
	ROM_LOAD( "poly8813.26",  0x0400, 0x0400, CRC(7011f3a3) SHA1(228eb54b9f62649b3b674e9f1bf21f2981e12c03) )
	ROM_LOAD( "poly8813.25",  0x0800, 0x0400, CRC(9f7570e2) SHA1(767f2111b4eb856a077b1b4afe9209aca3866e52) )
	//ROM_LOAD( "poly8813-1.bin", 0x0000, 0x0400, CRC(7fd980a0) SHA1(a71d5999deb4323a11db1c0ea0dcb1dacfaf47ef))
	//ROM_LOAD( "poly8813-2.rom", 0x0400, 0x0400, CRC(1ad7c06c) SHA1(c96b8f03c184de58dbdcee18d297dbccf2d77176))
	//ROM_LOAD( "poly8813-3.rom", 0x0800, 0x0400, CRC(3df57e5b) SHA1(5b0c4febfc7515fc07e63dcb21d0ab32bc6a2e46))
ROM_END
/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT   CLASS         INIT         COMPANY                FULLNAME     FLAGS
COMP( 1976, poly88,   0,      0,      poly88,   poly88, poly88_state, empty_init,  "PolyMorphic Systems", "Poly-88",   0 )
COMP( 1977, poly8813, poly88, 0,      poly8813, poly88, poly88_state, empty_init,  "PolyMorphic Systems", "Poly-8813", MACHINE_NOT_WORKING )



poly880.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/***************************************************************************

VEB Polytechnik Poly-Computer 880

http://www.kc85-museum.de/books/poly880/index.html

Initially the screen is blank. The CTC causes a NMI, this autoboots the system,
and then the PIO releases the NMI line.

Pasting:
    0-F : as is
    EXEC : X
    BACK : K
    MEM : M
    GO : G

Test Paste:
    M4000X11X22X33X44X55X66X77X88X99XM4000
    Now press X to confirm the data has been entered.


The SC1 version is a modification that turns it into a chesscomputer.
Not to be confused with the prequel to SC2, but more likely a different
version of SLC1, without the "Lern" part. Just like SLC1, the chess engine
was copied from Fidelity's Sensory Chess Challenger 8.

SC1-SLC1 Keypad Reference:
    1-8 = A1-H8
    C = C (back)
    D = O (option)
    E = St (clear)
    F = Z (enter)


TODO:
- MCYCL (activate single stepping)
- CYCL (single step)
- layout LEDs (address bus, data bus, command bus, MCYCL)
- optional 32KB RAM expansion @ 0x8000
- who made poly880s? slc1 is very similar, it's by the same person?

****************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/z80daisy.h"
#include "imagedev/cassette.h"
#include "machine/z80pio.h"
#include "machine/z80ctc.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "poly880.lh"


namespace {

class poly880_state : public driver_device
{
public:
	poly880_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pio(*this, "pio%u", 0)
		, m_ctc(*this, "ctc")
		, m_display(*this, "display")
		, m_cassette(*this, "cassette")
		, m_inputs(*this, "IN.%u", 0U)
	{ }

	void poly880(machine_config &config);
	void poly880s(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(trigger_reset);
	DECLARE_INPUT_CHANGED_MEMBER(trigger_nmi);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;
	required_device_array<z80pio_device, 2> m_pio;
	required_device<z80ctc_device> m_ctc;
	required_device<pwm_display_device> m_display;
	required_device<cassette_image_device> m_cassette;
	required_ioport_array<3> m_inputs;

	u8 m_matrix = 0;
	bool m_nmi = false;

	void poly880_io(address_map &map) ATTR_COLD;
	void poly880_mem(address_map &map) ATTR_COLD;
	void poly880s_mem(address_map &map) ATTR_COLD;

	void cldig_w(u8 data);
	void ctc_z0_w(int state);
	void ctc_z1_w(int state);
	void pio1_pa_w(u8 data);
	u8 pio1_pb_r();
	void pio1_pb_w(u8 data);
};


// Read/Write Handlers

void poly880_state::cldig_w(u8 data)
{
	m_display->write_my(data);
	m_matrix = data;
}


// Memory Maps

void poly880_state::poly880_mem(address_map &map)
{
	map(0x0000, 0x03ff).mirror(0x0c00).rom();
	map(0x1000, 0x13ff).mirror(0x0c00).rom();
	map(0x2000, 0x23ff).mirror(0x0c00).rom();
	map(0x3000, 0x33ff).mirror(0x0c00).rom();
	map(0x4000, 0x43ff).mirror(0x3c00).ram();
}

void poly880_state::poly880s_mem(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	map(0x4000, 0x43ff).mirror(0x3c00).ram();
}

void poly880_state::poly880_io(address_map &map)
{
	map.global_mask(0xaf);
	map(0x80, 0x83).rw(m_pio[0], FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x84, 0x87).rw(m_pio[1], FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x88, 0x8b).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0xa0, 0xa0).mirror(0x0f).w(FUNC(poly880_state::cldig_w));
}


// Input Ports

INPUT_CHANGED_MEMBER(poly880_state::trigger_reset)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}

INPUT_CHANGED_MEMBER(poly880_state::trigger_nmi)
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, newval ? ASSERT_LINE : CLEAR_LINE);
}

static INPUT_PORTS_START( poly880 )
	PORT_START("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("GO") PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("EXEC") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("BACK") PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("REG") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("FCT") PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("STEP") PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MEM") PORT_CODE(KEYCODE_M) PORT_CHAR('M')

	PORT_START("IN.1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')

	PORT_START("IN.2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')

	PORT_START("SPECIAL")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RES") PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(poly880_state::trigger_reset), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MON") PORT_CODE(KEYCODE_F2) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(poly880_state::trigger_nmi), 0)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MCYCL") PORT_CODE(KEYCODE_F3)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CYCL") PORT_CODE(KEYCODE_F4)
INPUT_PORTS_END


// Z80-CTC Interface

void poly880_state::ctc_z0_w(int state)
{
	// SEND
	if (!m_nmi && state)
	{
		m_nmi = true;
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
	}
}

void poly880_state::ctc_z1_w(int state)
{
}


// Z80-PIO Interface

void poly880_state::pio1_pa_w(u8 data)
{
	/*

	    bit     signal  description

	    PA0     SD0     segment E
	    PA1     SD1     segment D
	    PA2     SD2     segment C
	    PA3     SD3     segment DP
	    PA4     SD4     segment G
	    PA5     SD5     segment A
	    PA6     SD6     segment F
	    PA7     SD7     segment B

	*/

	m_display->write_mx(bitswap<8>(data, 3, 4, 6, 0, 1, 2, 7, 5));
}

u8 poly880_state::pio1_pb_r()
{
	/*

	    bit     signal  description

	    PB0
	    PB1     MIN     tape input
	    PB2
	    PB3     n/c
	    PB4     KI1     key row 1 input
	    PB5     KI2     key row 2 input
	    PB6
	    PB7     KI3     key row 3 input

	*/

	u8 data = 0x4c | ((m_cassette->input() < +0.0) << 1);

	for (int i = 0; i < 8; i++)
	{
		if (BIT(m_matrix, i))
		{
			if (BIT(m_inputs[0]->read(), i)) data |= 0x10;
			if (BIT(m_inputs[1]->read(), i)) data |= 0x20;
			if (BIT(m_inputs[2]->read(), i)) data |= 0x80;
		}
	}

	return data;
}

void poly880_state::pio1_pb_w(u8 data)
{
	/*

	    bit     signal  description

	    PB0     TTY     teletype
	    PB1
	    PB2     MOUT    tape output
	    PB3
	    PB4
	    PB5
	    PB6     SCON    release initial NMI
	    PB7

	*/

	// tape output
	m_cassette->output(BIT(data, 2) ? +1.0 : -1.0);

	if (m_nmi && BIT(data, 6))
	{
		m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
		m_nmi = false;
	}
}


// Z80 Daisy Chain

static const z80_daisy_config poly880_daisy_chain[] =
{
	{ "pio0" },
	{ "pio1" },
	{ "ctc" },
	{ nullptr }
};


// Machine Initialization

void poly880_state::machine_start()
{
	// register for state saving
	save_item(NAME(m_matrix));
	save_item(NAME(m_nmi));
}


// Machine Driver

void poly880_state::poly880(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, XTAL(7'372'800)/8);
	m_maincpu->set_addrmap(AS_PROGRAM, &poly880_state::poly880_mem);
	m_maincpu->set_addrmap(AS_IO, &poly880_state::poly880_io);
	m_maincpu->set_daisy_config(poly880_daisy_chain);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 8);
	m_display->set_segmask(0xff, 0xff);
	config.set_default_layout(layout_poly880);

	// devices
	Z80CTC(config, m_ctc, XTAL(7'372'800)/8);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->zc_callback<0>().set(FUNC(poly880_state::ctc_z0_w));
	m_ctc->zc_callback<1>().set(FUNC(poly880_state::ctc_z1_w));
	m_ctc->zc_callback<2>().set(m_ctc, FUNC(z80ctc_device::trg3));

	Z80PIO(config, m_pio[0], XTAL(7'372'800)/8);
	m_pio[0]->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio[0]->out_pa_callback().set(FUNC(poly880_state::pio1_pa_w));
	m_pio[0]->in_pb_callback().set(FUNC(poly880_state::pio1_pb_r));
	m_pio[0]->out_pb_callback().set(FUNC(poly880_state::pio1_pb_w));

	Z80PIO(config, m_pio[1], XTAL(7'372'800)/8);
	m_pio[1]->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	SPEAKER(config, "cass_output").front_center(); // on data recorder
	m_cassette->add_route(ALL_OUTPUTS, "cass_output", 0.05);
}

void poly880_state::poly880s(machine_config &config)
{
	poly880(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &poly880_state::poly880s_mem);
}


// ROMs

ROM_START( poly880 )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "bm039.i5", 0x0000, 0x0400, CRC(b1c571e8) SHA1(85bfe53d39d6690e79999a1e1240789497e72db0) )
	ROM_LOAD( "bm040.i6", 0x1000, 0x0400, CRC(9efddf5b) SHA1(6ffa2f80b2c6f8ec9e22834f739c82f9754272b8) )
ROM_END

ROM_START( poly880s )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "sc1.rom", 0x0000, 0x1000, CRC(26965b23) SHA1(01568911446eda9f05ec136df53da147b7c6f2bf) )
ROM_END

} // anonymous namespace


// System Drivers

//    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT    CLASS          INIT        COMPANY, FULLNAME, FLAGS
COMP( 1983, poly880,  0,       0,      poly880,  poly880, poly880_state, empty_init, "VEB Polytechnik Karl-Marx-Stadt", "Poly-Computer 880", MACHINE_SUPPORTS_SAVE )
COMP( 1983, poly880s, poly880, 0,      poly880s, poly880, poly880_state, empty_init, "hack", "Poly-Computer 880 (SC1)", MACHINE_SUPPORTS_SAVE )



polysix.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR, O. Galibert
/****************************************************************************

    Korg Polysix (PS-6)

****************************************************************************/

#include "emu.h"
#include "cpu/mcs48/mcs48.h"
#include "imagedev/cassette.h"
#include "machine/nvram.h"
#include "softlist_dev.h"
#include "speaker.h"


class polysix_sound_block : public device_t, public device_sound_interface
{
public:
	polysix_sound_block(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);

	void set_pitch(int channel, u8 pitch);
	void set_gates(u8 gates);
	void set_effect_speed(u8 value);
	void set_cutoff(u8 value);
	void set_eg_intensity(u8 value);
	void set_resonance(u8 value);
	void set_attack(u8 value);
	void set_decay(u8 value);
	void set_suspend(u8 value);
	void set_release(u8 value);
	void set_keyboard_tracking(u8 value);
	void set_pw_pwm(u8 value);
	void set_pwm_speed(u8 value);
	void set_mg_speed(u8 value);
	void set_mg_delay(u8 value);
	void set_mg_level(u8 value);
	void set_control_low(u8 value);
	void set_control_high(u8 value);

	u8 get_control_low();

	virtual void sound_stream_update(sound_stream &stream) override;

	virtual void device_start() override;
	virtual void device_reset() override;

private:
	static const std::array<float, 0x100> phase_step;
	static const std::array<float, 0x100> pwm_phase_step;
	static const std::array<float, 0x100> pw_threshold;

	sound_stream *m_stream;

	std::array<float, 6> m_phase;
	std::array<float, 6> m_organ_eg;

	float m_pwm_phase;

	std::array<u8, 6> m_pitch;
	u8 m_gates;
	u8 m_current_gates;

	u8 m_effect_speed;
	u8 m_cutoff;
	u8 m_eg_intensity;
	u8 m_resonance;
	u8 m_attack;
	u8 m_decay;
	u8 m_suspend;
	u8 m_release;
	u8 m_keyboard_tracking;
	u8 m_pw_pwm;
	u8 m_pwm_speed;
	u8 m_mg_speed;
	u8 m_mg_delay;
	u8 m_mg_level;

	u8 m_control_low;
	u8 m_control_high;
};

DEFINE_DEVICE_TYPE(POLYSIX_SOUND_BLOCK, polysix_sound_block, "polysix_sound_block", "Korg Polysix sound block")

// Phase step at 48KHz for a given pitch for the main oscillator.
// Real range is 0 (C0) to 84 (C7), since it's a 61-keys, 5 octaves
// keyboard with +/- 1 octave transpose capability.

const std::array<float, 0x100> polysix_sound_block::phase_step = []() {
	std::array<float, 0x100> steps;
	// Tune A4 = 440Hz
	for(int i=0; i != 0x100; i++)
		steps[i] = 440.0 * pow(2, (i - 12*4 - 9)/12.0) / 48000;
	return steps;
}();

// Phase step for the pwm.  Actual curve is unclear but looks linear.

const std::array<float, 0x100> polysix_sound_block::pwm_phase_step = []() {
	std::array<float, 0x100> steps;
	// 0 = 0Hz, max = 20Hz, possibly linear?
	for(int i=0; i != 0x100; i++)
		steps[i] = 20 * (i/255.0) / 48000;
	return steps;
}();

// Threshold for the pulse width, linear between 0.5 and 1.1, where 1
// is the actual max value.

const std::array<float, 0x100> polysix_sound_block::pw_threshold = []() {
	std::array<float, 0x100> thr;
	for(int i=0; i != 0x100; i++)
		thr[i] = 0.5 + 0.6 * (i/255.0);
	return thr;
}();

polysix_sound_block::polysix_sound_block(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, POLYSIX_SOUND_BLOCK, tag, owner, clock)
	, device_sound_interface(mconfig, *this)
{
}

void polysix_sound_block::device_start()
{
	m_stream = stream_alloc(0, 1, 48000);

	save_item(NAME(m_phase));
	save_item(NAME(m_organ_eg));
	save_item(NAME(m_pwm_phase));

	save_item(NAME(m_pitch));
	save_item(NAME(m_gates));
	save_item(NAME(m_current_gates));
	save_item(NAME(m_effect_speed));
	save_item(NAME(m_cutoff));
	save_item(NAME(m_eg_intensity));
	save_item(NAME(m_resonance));
	save_item(NAME(m_attack));
	save_item(NAME(m_decay));
	save_item(NAME(m_suspend));
	save_item(NAME(m_release));
	save_item(NAME(m_keyboard_tracking));
	save_item(NAME(m_pw_pwm));
	save_item(NAME(m_pwm_speed));
	save_item(NAME(m_mg_speed));
	save_item(NAME(m_mg_delay));
	save_item(NAME(m_mg_level));
	save_item(NAME(m_control_low));
	save_item(NAME(m_control_high));

	std::fill(m_phase.begin(), m_phase.end(), 0);
	std::fill(m_organ_eg.begin(), m_organ_eg.end(), 0);
	m_pwm_phase = 0;

	std::fill(m_pitch.begin(), m_pitch.end(), 0);

	m_effect_speed = 0;
	m_cutoff = 0;
	m_eg_intensity = 0;
	m_resonance = 0;
	m_attack = 0;
	m_decay = 0;
	m_suspend = 0;
	m_release = 0;
	m_keyboard_tracking = 0;
	m_pw_pwm = 0;
	m_pwm_speed = 0;
	m_mg_speed = 0;
	m_mg_delay = 0;
	m_mg_level = 0;
	m_control_low = 0;
	m_control_high = 0;
	m_gates = 0;
	m_current_gates = 0;
}

void polysix_sound_block::device_reset()
{
}

void polysix_sound_block::set_pitch(int channel, u8 pitch)
{
	if(m_pitch[channel] != pitch) {
		m_stream->update();
		m_pitch[channel] = pitch;

		static const char *const notes[12] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" };
		logerror("channel %d pitch %02x %s%d\n", channel, pitch, notes[pitch % 12], (pitch/12));
	}
}

void polysix_sound_block::set_gates(u8 gates)
{
	if(gates != m_gates) {
		m_stream->update();
		m_gates = gates;
	}
}

void polysix_sound_block::set_effect_speed(u8 value)
{
	if(value != m_effect_speed) {
		m_stream->update();
		m_effect_speed = value;
		logerror("effect_speed = %02x\n", value);
	}
}

void polysix_sound_block::set_cutoff(u8 value)
{
	if(value != m_cutoff) {
		m_stream->update();
		m_cutoff = value;
		logerror("cutoff = %02x\n", value);
	}
}

void polysix_sound_block::set_eg_intensity(u8 value)
{
	if(value != m_eg_intensity) {
		m_stream->update();
		m_eg_intensity = value;
		logerror("eg_intensity = %02x\n", value);
	}
}

void polysix_sound_block::set_resonance(u8 value)
{
	if(value != m_resonance) {
		m_stream->update();
		m_resonance = value;
		logerror("resonance = %02x\n", value);
	}
}

void polysix_sound_block::set_attack(u8 value)
{
	if(value != m_attack) {
		m_stream->update();
		m_attack = value;
		logerror("attack = %02x\n", value);
	}
}

void polysix_sound_block::set_decay(u8 value)
{
	if(value != m_decay) {
		m_stream->update();
		m_decay = value;
		logerror("decay = %02x\n", value);
	}
}

void polysix_sound_block::set_suspend(u8 value)
{
	if(value != m_suspend) {
		m_stream->update();
		m_suspend = value;
		logerror("suspend = %02x\n", value);
	}
}

void polysix_sound_block::set_release(u8 value)
{
	if(value != m_release) {
		m_stream->update();
		m_release = value;
		logerror("release = %02x\n", value);
	}
}

void polysix_sound_block::set_keyboard_tracking(u8 value)
{
	if(value != m_keyboard_tracking) {
		m_stream->update();
		m_keyboard_tracking = value;
		logerror("keyboard_tracking = %02x\n", value);
	}
}

void polysix_sound_block::set_pw_pwm(u8 value)
{
	if(value != m_pw_pwm) {
		m_stream->update();
		m_pw_pwm = value;
		logerror("pw_pwm = %02x\n", value);
	}
}

void polysix_sound_block::set_pwm_speed(u8 value)
{
	if(value != m_pwm_speed) {
		m_stream->update();
		m_pwm_speed = value;
		logerror("pwm_speed = %02x\n", value);
	}
}

void polysix_sound_block::set_mg_speed(u8 value)
{
	if(value != m_mg_speed) {
		m_stream->update();
		m_mg_speed = value;
		logerror("mg_speed = %02x\n", value);
	}
}

void polysix_sound_block::set_mg_delay(u8 value)
{
	if(value != m_mg_delay) {
		m_stream->update();
		m_mg_delay = value;
		logerror("mg_delay = %02x\n", value);
	}
}

void polysix_sound_block::set_mg_level(u8 value)
{
	if(value != m_mg_level) {
		m_stream->update();
		m_mg_level = value;
		logerror("mg_level = %02x\n", value);
	}
}

void polysix_sound_block::set_control_low(u8 value)
{
	if(value != m_control_low) {
		m_stream->update();
		m_control_low = value;
		logerror("control low vco=%s wave=%s sub=%s mod=%s\n",
				 BIT(value, 0, 2) == 0 ? "16'" : BIT(value, 0, 2) == 1 ? "8'" : BIT(value, 0, 2) == 2 ? "4'" : "?",
				 BIT(value, 2, 2) == 1 ? "tri" : BIT(value, 2, 2) == 0 ? "pw" : BIT(value, 2, 2) == 2 ? "pwm" : "?",
				 BIT(value, 4, 2) == 0 ? "off" : BIT(value, 4, 2) == 1 ? "1oct" : BIT(value, 4, 2) == 2 ? "2oct" : "?",
				 BIT(value, 6, 2) == 0 ? "vca" : BIT(value, 6, 2) == 1 ? "vcf" : BIT(value, 6, 2) == 2 ? "vco" : "?"
				 );
	}
}

void polysix_sound_block::set_control_high(u8 value)
{
	if(value != m_control_high) {
		m_stream->update();
		m_control_high = value;
		logerror("control high vca-mode=%s chorus=%s phase=%s ens=%s p.vol=%x\n",
				 BIT(value, 0) ? "eg" : "square",
				 BIT(value, 1) ? "on" : "off",
				 BIT(value, 2) ? "on" : "off",
				 BIT(value, 3) ? "on" : "off",
				 BIT(value, 4, 4)
				 );
	}
}

u8 polysix_sound_block::get_control_low()
{
	return m_control_low;
}

// #*#*#*#*#*#*#*#
void polysix_sound_block::sound_stream_update(sound_stream &stream)
{
	for(int sample=0; sample != stream.samples(); sample++) {
		//      u8 trigger = m_gates & ~m_current_gates;
		float out = 0;

		// Step the pwm phase (common to all channels)
		m_pwm_phase += pwm_phase_step[m_pwm_speed];
		while(m_pwm_phase >= 2)
			m_pwm_phase -= 2;

		//  Wrap into a triangle
		float pwm_phase = m_pwm_phase >= 1 ? 2 - m_pwm_phase : m_pwm_phase;

		//  Compute the threshold
		float pw_thr = pw_threshold[m_pw_pwm];
		if(BIT(m_control_low, 3))
			// PWM mode, the modulation multiplies the threshold part over 0.5 with the phase wrapped between 0.2 and 1
			pw_thr = 0.5 + (pw_thr - 0.5) * (0.2 + pwm_phase * 0.8);

		for(int channel = 0; channel != 6; channel ++) {
			if((m_gates ^ m_current_gates) & (1 << channel))
				logerror("channel %d %s\n", channel, (m_gates & (1<<channel)) ? "keyon" : "keyoff");

			// Step the phase
			m_phase[channel] += phase_step[m_pitch[channel]];
			while(m_phase[channel] >= 4)
				m_phase[channel] -= 4;

			int subosc_step = int(m_phase[channel]);
			float phase = m_phase[channel] - subosc_step;

			// Generate the initial wave in the [-1, 1] range
			float wave;
			if(BIT(m_control_low, 2))
				// triangle
				wave = 2*(phase - 0.5);

			else
				// PW(M)
				wave = phase >= pw_thr ? 1 : -1;

			// Add the sub-oscillator, if active
			if(BIT(m_control_low, 4, 2))
				wave += (subosc_step & (BIT(m_control_low, 5, 1) ? 2 : 1)) ? 1 : 0;

			// Step the organ EG
			if(BIT(m_gates, channel))
				// When gate is on, charge a 0.047uF cap through a 10K resistor
				m_organ_eg[channel] += (1-m_organ_eg[channel])*0.0433581893516088;   // 1-exp(-1/(10e3 * 0.047e-6 * 48000))
			else
				// When gate is off, discharge a 0.047uF cap through a 230K resistor
				m_organ_eg[channel] -= m_organ_eg[channel]*0.00192537196422815;   // 1-exp(-1/(230e3 * 0.047e-6 * 48000))

			out += wave * m_organ_eg[channel];
		}

		m_current_gates = m_gates;
		stream.put(0, sample, out/2);
	}
}




class polysix_state : public driver_device
{
public:
	polysix_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_progmcu(*this, "progmcu")
		, m_keymcu(*this, "keymcu")
		, m_sound(*this, "soundblock")
		, m_keyboard(*this, "K%u", 0U)
		, m_progboard(*this, "P%u", 0U)
		, m_knobs(*this, "A%x", 0U)
		, m_tape_enable(*this, "TAPE")
		, m_tape(*this, "tape")
	{
	}

	virtual void machine_start() override ATTR_COLD;
	void polysix(machine_config &config);

	INPUT_CHANGED_MEMBER(tape_enable_w);

private:
	required_device<mcs48_cpu_device> m_progmcu;
	required_device<mcs48_cpu_device> m_keymcu;
	required_device<polysix_sound_block> m_sound;
	std::unique_ptr<u8[]> m_nvram_ptr;
	required_ioport_array<10> m_keyboard;
	required_ioport_array<8> m_progboard;
	required_ioport_array<16> m_knobs;
	required_ioport m_tape_enable;
	required_device<cassette_image_device> m_tape;

	u16 m_pleds;
	u8 m_kleds;

	u8 m_prog_bus;
	u8 m_prog_p1;
	u8 m_prog_p2;
	u8 m_key_bus;
	u8 m_keyon;
	u8 m_key_p2;
	u8 m_trigger;

	bool m_tape_enabled;

	void key_bus_w(u8 data);
	u8 key_p1_r();
	void key_p2_w(u8 data);
	int key_t0_r();
	int key_t1_r();

	void prog_map(address_map &map);

	u8 prog_r(offs_t offset);
	void prog_w(offs_t offset, u8 data);
	void prog_bus_w(u8 data);
	u8 prog_p1_r();
	void prog_p1_w(u8 data);
	void prog_p2_w(u8 data);
	int prog_t0_r();
	int prog_t1_r();
};

void polysix_state::prog_map(address_map &map)
{
	map(0x00, 0xff).rw(FUNC(polysix_state::prog_r), FUNC(polysix_state::prog_w));
}

void polysix_state::machine_start()
{
	m_nvram_ptr = make_unique_clear<u8[]>(0x400);
	subdevice<nvram_device>("nvram")->set_base(&m_nvram_ptr[0], 0x400);

	save_pointer(NAME(m_nvram_ptr), 0x400);
	save_item(NAME(m_pleds));
	save_item(NAME(m_kleds));
	save_item(NAME(m_key_bus));
	save_item(NAME(m_keyon));
	save_item(NAME(m_trigger));
	save_item(NAME(m_prog_bus));
	save_item(NAME(m_prog_p1));
	save_item(NAME(m_prog_p2));

	save_item(NAME(m_tape_enabled));

	m_pleds = 0;
	m_key_bus = 0;
	m_keyon = 0;
	m_key_p2 = 0;
	m_trigger = 0;

	m_prog_bus = 0;
	m_prog_p1 = 0;
	m_prog_p2 = 0;

	m_tape_enabled = false;
}

u8 polysix_state::prog_r(offs_t offset)
{
	if(BIT(m_prog_p1, 7)) {
		u16 adr = BIT(m_prog_p2, 0) | (offset << 1) | ( BIT(m_prog_p2, 1) << 9);
		return m_nvram_ptr[adr] | 0xf0;
	}

	return 0xff;
}

void polysix_state::prog_w(offs_t offset, u8 data)
{
	if(BIT(m_prog_p1, 7)) {
		u16 adr = BIT(m_prog_p2, 0) | (offset << 1) | ( BIT(m_prog_p2, 1) << 9);
		m_nvram_ptr[adr] = data & 0x0f;
	}
}

u8 polysix_state::prog_p1_r()
{
	u8 res = 0xff;
	for(int i=0; i != 8; i++)
		if(!BIT(m_prog_bus, i))
			res &= m_progboard[i]->read();

	if(!BIT(m_prog_p2, 0))
		machine().debug_break();
	return res;
}

void polysix_state::prog_p1_w(u8 data)
{
	u8 chg = data ^ m_prog_p1;
	m_prog_p1 = data;

	if(BIT(chg, 6) && BIT(m_prog_p1, 6)) {
		switch(m_prog_p2 & 0xf) {
		case 0x0: m_sound->set_effect_speed(m_prog_bus); break;
		case 0x1: m_sound->set_cutoff(m_prog_bus); break;
		case 0x2: m_sound->set_eg_intensity(m_prog_bus); break;
		case 0x3: m_sound->set_resonance(m_prog_bus); break;
		case 0x4: m_sound->set_attack(m_prog_bus); break;
		case 0x5: m_sound->set_decay(m_prog_bus); break;
		case 0x6: m_sound->set_suspend(m_prog_bus); break;
		case 0x7: m_sound->set_release(m_prog_bus); break;
		case 0x8: m_sound->set_keyboard_tracking(m_prog_bus); break;
		case 0x9: m_sound->set_pw_pwm(m_prog_bus); break;
		case 0xa: m_sound->set_pwm_speed(m_prog_bus); break;
		case 0xb: m_sound->set_mg_speed(m_prog_bus); break;
		case 0xc: m_sound->set_mg_delay(m_prog_bus); break;
		case 0xd: m_sound->set_mg_level(m_prog_bus); break;
		}
	}
}

void polysix_state::prog_p2_w(u8 data)
{
	u8 chg = data ^ m_prog_p2;
	m_prog_p2 = data;

	if(BIT(chg, 4) && !BIT(m_prog_p2, 4))
		m_sound->set_control_low(m_prog_bus);

	if(BIT(chg, 5) && !BIT(m_prog_p2, 5))
		m_sound->set_control_high(m_prog_bus);

	u16 old_leds = m_pleds;

	if(BIT(chg, 6) && !BIT(m_prog_p2, 6))
		m_pleds = (m_pleds & 0xff00) | m_prog_bus;

	if(BIT(chg, 7) && !BIT(m_prog_p2, 7))
		m_pleds = (m_pleds & 0x00ff) | (m_prog_bus << 8);

	if(m_pleds != old_leds)
		logerror("pleds %c%c%c%c%c%c%c%c %c%c%c%c%s%s\n",
				 BIT(m_pleds,  0) ? '1' : '.',
				 BIT(m_pleds,  1) ? '2' : '.',
				 BIT(m_pleds,  2) ? '3' : '.',
				 BIT(m_pleds,  3) ? '4' : '.',
				 BIT(m_pleds,  4) ? '5' : '.',
				 BIT(m_pleds,  5) ? '6' : '.',
				 BIT(m_pleds,  6) ? '7' : '.',
				 BIT(m_pleds,  7) ? '8' : '.',
				 BIT(m_pleds,  8) ? 'A' : '.',
				 BIT(m_pleds,  9) ? 'B' : '.',
				 BIT(m_pleds, 10) ? 'C' : '.',
				 BIT(m_pleds, 11) ? 'D' : '.',
				 BIT(m_pleds, 12) ? " manual/found" : "",
				 BIT(m_pleds, 13) ? " write/loading" : "");
}

void polysix_state::prog_bus_w(u8 data)
{
	m_prog_bus = data;
}

int polysix_state::prog_t0_r()
{
	return m_prog_bus <= m_knobs[m_prog_p2 & 0xf]->read();
}

int polysix_state::prog_t1_r()
{
	//  logerror("prog t1 %f\n", m_tape->input());
	return m_tape->input() >= 0;
}

u8 polysix_state::key_p1_r()
{
	u8 res = 0xff;
	for(int i=0; i != 8; i++)
		if(!BIT(m_key_bus, i))
			res &= m_keyboard[i]->read();
	if(!BIT(m_key_p2, 0))
		res &= m_keyboard[8]->read();
	if(!BIT(m_key_p2, 1))
		res &= m_keyboard[9]->read();
	return res;
}

void polysix_state::key_bus_w(u8 data)
{
	m_key_bus = data;
}

// cn12 at klm-371
// cn12-17 -> int, acki, arpeggiator
// cn12-16 = p27, gnd?
// klm-367
// cn05-1 = reset
// cn05-2 = t0 -- 8'
// cn05-3 = t1 -- 4'
// cn05-4 = ic45.7, mg trigger

void polysix_state::key_p2_w(u8 data)
{
	u8 chg = data ^ m_key_p2;
	m_key_p2 = data;

	if(BIT(chg, 3) && !BIT(m_key_p2, 3)) {
		m_sound->set_gates(m_tape_enabled ? 0 : m_key_bus & 0x3f);
		// TODO logerror("ic45 = %02x %s\n", m_key_bus, machine().describe_context());
	}

	if(BIT(chg, 4) && !BIT(m_key_p2, 4)) {
		u8 old_leds = m_kleds;
		m_kleds = m_key_bus;
		if(old_leds != m_kleds)
			logerror("kleds%s%s%s%s%s\n",
					 BIT(m_kleds, 0) ? " key-hold" : "",
					 BIT(m_kleds, 1) ? " chord-memory" : "",
					 BIT(m_kleds, 2) ? " unison" : "",
					 BIT(m_kleds, 3) ? " poly" : "",
					 BIT(m_kleds, 4) ? " arpeggiator" : "");
	}

	if(!BIT(m_key_p2, 5)) {
		int index = BIT(data, 0, 3);
		if(index < 6)
			m_sound->set_pitch(index, m_key_bus);
		else
			; // TODO
	}

	if(BIT(chg, 7))
		logerror("cn12-16=%d\n", BIT(m_key_p2, 7));

	//  logerror("key_p2_w row=%x l1=%d l2=%d inh=%d cn12=%d\n", BIT(data, 0, 3), BIT(data, 3), BIT(data, 4), BIT(data, 5), BIT(data, 7));
}

int polysix_state::key_t0_r()
{
	return !BIT(m_sound->get_control_low(), 0);
}

int polysix_state::key_t1_r()
{
	return !BIT(m_sound->get_control_low(), 01);
}

INPUT_CHANGED_MEMBER(polysix_state::tape_enable_w)
{
	logerror("leds tape enable %s\n", newval ? "on" : "off");
	m_tape_enabled = newval;
	m_progmcu->set_input_line(0, newval);
	if(newval)
		m_keyon = 0;
}

static INPUT_PORTS_START(polysix)
	PORT_START("K0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_C1
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_CS1
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_D1
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_DS1
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_E1
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_F1
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_FS1
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_G1

	PORT_START("K1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_GS1
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_A1
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_AS1
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_B1
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_C2
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_CS2
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_D2
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_DS2

	PORT_START("K2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_E2
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_F2
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_FS2
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_G2
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_GS2
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_A2
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_AS2
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_B2

	PORT_START("K3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_C3
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_CS3
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_D3
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_DS3
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_E3
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_F3
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_FS3
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_G3

	PORT_START("K4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_GS3
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_A3
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_AS3
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_B3
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_C4
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_CS4
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_D4
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_DS4

	PORT_START("K5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_E4
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_F4
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_FS4
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_G4
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_GS4
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_A4
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_AS4
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_B4

	PORT_START("K6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_C5
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_CS5
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_D5
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_DS5
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_E5
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_F5
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_FS5
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_G5

	PORT_START("K7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_GS5
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_A5
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_AS5
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_B5
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_GM_C6
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("K8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("key hold")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("chord memory")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("unison")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("poly")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("arpeggio")
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("K9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_CONFNAME(0x02, 0x02, "Arpeggio latch")
	PORT_CONFSETTING(    0x00, "on")
	PORT_CONFSETTING(    0x02, "off")
	PORT_CONFNAME(0x1c, 0x0c, "Arpeggio mode")
	PORT_CONFSETTING(    0x0c, "up")
	PORT_CONFSETTING(    0x14, "updown")
	PORT_CONFSETTING(    0x18, "down")
	PORT_CONFNAME(0xe0, 0x60, "Arpeggio range")
	PORT_CONFSETTING(    0x60, "full")
	PORT_CONFSETTING(    0xa0, "2 octaves")
	PORT_CONFSETTING(    0xc0, "1 octave")

	PORT_START("P0")
	PORT_CONFNAME(0x03, 0x02, "VCO octave")
	PORT_CONFSETTING(    0x03, "16'")
	PORT_CONFSETTING(    0x02, "8'")
	PORT_CONFSETTING(    0x01, "4'")
	PORT_CONFNAME(0x0c, 0x08, "Waveform")
	PORT_CONFSETTING(    0x08, "Triangle")
	PORT_CONFSETTING(    0x0c, "PW")
	PORT_CONFSETTING(    0x04, "PWM")
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("P1")
	PORT_CONFNAME(0x03, 0x01, "Sub oscillator")
	PORT_CONFSETTING(    0x03, "off")
	PORT_CONFSETTING(    0x02, "1 oct")
	PORT_CONFSETTING(    0x01, "2 oct")
	PORT_CONFNAME(0x0c, 0x0c, "MG modulation")
	PORT_CONFSETTING(    0x04, "VCO")
	PORT_CONFSETTING(    0x08, "VCF")
	PORT_CONFSETTING(    0x0c, "VCA")
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("P2")
	PORT_CONFNAME(0x01, 0x00, "VCA mode")
	PORT_CONFSETTING(    0x00, "EG")
	PORT_CONFSETTING(    0x01, "square")
	PORT_CONFNAME(0x0e, 0x0e, "Effect")
	PORT_CONFSETTING(    0x0e, "off")
	PORT_CONFSETTING(    0x06, "ensemble")
	PORT_CONFSETTING(    0x0a, "phaser")
	PORT_CONFSETTING(    0x0c, "chorus")
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("P3")
	PORT_CONFNAME(0x0f, 0x0a, "VCA dB")
	PORT_CONFSETTING(    0x0f, "-10")
	PORT_CONFSETTING(    0x0e, "-8")
	PORT_CONFSETTING(    0x0d, "-6")
	PORT_CONFSETTING(    0x0c, "-4")
	PORT_CONFSETTING(    0x0b, "-2")
	PORT_CONFSETTING(    0x0a, "0")
	PORT_CONFSETTING(    0x09, "+2")
	PORT_CONFSETTING(    0x08, "+4")
	PORT_CONFSETTING(    0x07, "+6")
	PORT_CONFSETTING(    0x06, "+8")
	PORT_CONFSETTING(    0x05, "+10")
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("P4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("P5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("7") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("8") PORT_CODE(KEYCODE_8)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("P6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("a / to tape") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("b / from tape") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("c / verify") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("d / cancel") PORT_CODE(KEYCODE_D)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("P7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("found / manual")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("write / loading")
	PORT_CONFNAME(0x04, 0x04, "write enable")
	PORT_CONFSETTING(   0x00, "enabled")
	PORT_CONFSETTING(   0x04, "disabled")
	PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("A0")
	PORT_BIT( 0xff, 0x00, IPT_PADDLE ) PORT_SENSITIVITY(100) PORT_KEYDELTA(4) PORT_CENTERDELTA(0) PORT_NAME("Effect speed / intensity")

	PORT_START("A1")
	PORT_BIT( 0xff, 0x00, IPT_PADDLE ) PORT_SENSITIVITY(100) PORT_KEYDELTA(4) PORT_CENTERDELTA(0) PORT_NAME("VCF cutoff")

	PORT_START("A2")
	PORT_BIT( 0xff, 0x00, IPT_PADDLE ) PORT_SENSITIVITY(100) PORT_KEYDELTA(4) PORT_CENTERDELTA(0) PORT_NAME("EG intensity")

	PORT_START("A3")
	PORT_BIT( 0xff, 0x00, IPT_PADDLE ) PORT_SENSITIVITY(100) PORT_KEYDELTA(4) PORT_CENTERDELTA(0) PORT_NAME("Resonance")

	PORT_START("A4")
	PORT_BIT( 0xff, 0x00, IPT_PADDLE ) PORT_SENSITIVITY(100) PORT_KEYDELTA(4) PORT_CENTERDELTA(0) PORT_NAME("Attack")

	PORT_START("A5")
	PORT_BIT( 0xff, 0x00, IPT_PADDLE ) PORT_SENSITIVITY(100) PORT_KEYDELTA(4) PORT_CENTERDELTA(0) PORT_NAME("Decay")

	PORT_START("A6")
	PORT_BIT( 0xff, 0x00, IPT_PADDLE ) PORT_SENSITIVITY(100) PORT_KEYDELTA(4) PORT_CENTERDELTA(0) PORT_NAME("Sustain")

	PORT_START("A7")
	PORT_BIT( 0xff, 0x00, IPT_PADDLE ) PORT_SENSITIVITY(100) PORT_KEYDELTA(4) PORT_CENTERDELTA(0) PORT_NAME("Release")

	PORT_START("A8")
	PORT_BIT( 0xff, 0x00, IPT_PADDLE ) PORT_SENSITIVITY(100) PORT_KEYDELTA(4) PORT_CENTERDELTA(0) PORT_NAME("Keyboard tracking")

	PORT_START("A9")
	PORT_BIT( 0xff, 0x00, IPT_PADDLE ) PORT_SENSITIVITY(100) PORT_KEYDELTA(4) PORT_CENTERDELTA(0) PORT_NAME("PW/PWM")

	PORT_START("Aa")
	PORT_BIT( 0xff, 0x00, IPT_PADDLE ) PORT_SENSITIVITY(100) PORT_KEYDELTA(4) PORT_CENTERDELTA(0) PORT_NAME("PWM speed")

	PORT_START("Ab")
	PORT_BIT( 0xff, 0x00, IPT_PADDLE ) PORT_SENSITIVITY(100) PORT_KEYDELTA(4) PORT_CENTERDELTA(0) PORT_NAME("MG frequency")

	PORT_START("Ac")
	PORT_BIT( 0xff, 0x00, IPT_PADDLE ) PORT_SENSITIVITY(100) PORT_KEYDELTA(4) PORT_CENTERDELTA(0) PORT_NAME("MG delay")

	PORT_START("Ad")
	PORT_BIT( 0xff, 0x00, IPT_PADDLE ) PORT_SENSITIVITY(100) PORT_KEYDELTA(4) PORT_CENTERDELTA(0) PORT_NAME("MG level")

	PORT_START("Ae")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("Af")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("TAPE")
	PORT_CONFNAME(0x01, 0x00, "Tape access") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(polysix_state::tape_enable_w), 0)
	PORT_CONFSETTING(   0x01, "enabled")
	PORT_CONFSETTING(   0x00, "disabled")

INPUT_PORTS_END

void polysix_state::polysix(machine_config &config)
{
	I8048(config, m_progmcu, 6_MHz_XTAL);
	m_progmcu->set_addrmap(AS_IO, &polysix_state::prog_map);
	m_progmcu->p1_in_cb().set(FUNC(polysix_state::prog_p1_r));
	m_progmcu->p1_out_cb().set(FUNC(polysix_state::prog_p1_w));
	m_progmcu->p2_out_cb().set(FUNC(polysix_state::prog_p2_w));
	m_progmcu->bus_out_cb().set(FUNC(polysix_state::prog_bus_w));
	m_progmcu->t0_in_cb().set(FUNC(polysix_state::prog_t0_r));
	m_progmcu->t1_in_cb().set(FUNC(polysix_state::prog_t1_r));

	I8049(config, m_keymcu, 6_MHz_XTAL);
	m_keymcu->p1_in_cb().set(FUNC(polysix_state::key_p1_r));
	m_keymcu->p2_out_cb().set(FUNC(polysix_state::key_p2_w));
	m_keymcu->bus_out_cb().set(FUNC(polysix_state::key_bus_w));
	m_keymcu->t0_in_cb().set(FUNC(polysix_state::key_t0_r));
	m_keymcu->t1_in_cb().set(FUNC(polysix_state::key_t1_r));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // TC5514APL-3 + battery

	CASSETTE(config, m_tape);
	m_tape->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_MUTED);
	m_tape->set_interface("polysix");

	SOFTWARE_LIST(config, "polysix").set_original("polysix");

	POLYSIX_SOUND_BLOCK(config, m_sound);
	m_sound->add_route(0, "speaker", 1.0);

	SPEAKER(config, "speaker").front_center();
}

ROM_START(polysix)
	ROM_REGION(0x400, "progmcu", 0)
	ROM_LOAD("d8048c-345.bin", 0x000, 0x400, CRC(130fb945) SHA1(feaf59d7694de9c3f8009d883a250039f219d046))

	ROM_REGION(0x800, "keymcu", 0)
	ROM_LOAD("d8049c-217.bin", 0x000, 0x800, CRC(246d7767) SHA1(5b608c750e7fe7832070a53a74df416fd132ecb7))

	ROM_REGION(0x400, "nvram", 0)
	ROM_LOAD("nvram.bin", 0x000, 0x400, CRC(1a913972) SHA1(891c7c2aa2f3cd54e3dd6847fad58ab831838c3e))
ROM_END


SYST(1980, polysix, 0, 0, polysix, polysix, polysix_state, empty_init, "Korg", "Polysix Programmable Polyphonic Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



positron.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
// thanks-to:Mike Miller
/**********************************************************************

   SPECIFICATION
       Processor: 6809 (1MHz)
             RAM: upto 256K on board, and 512K with expansion unit.
             ROM: upto 128K
     Text screen: teletext-type 40 x 24, 8 colours.
  Graphic screen: 240 x 240 pixels, 8 colours.
        Keyboard: 88 keys in unit with CPU, numeric/cursor pad. 10 function
                  keys.
      Interfaces: 4 x RS232, IEEE-488, cassette

    TODO:
    - fuse timer for switching tasks needs some attention.
    - nothing is known so emulation is very incomplete.

**********************************************************************/


#include "emu.h"

#include "bus/ieee488/ieee488.h"
#include "bus/rs232/rs232.h"
#include "cpu/m6809/m6809.h"
#include "imagedev/cassette.h"
#include "machine/6840ptm.h"
#include "machine/6850acia.h"
#include "machine/bankdev.h"
#include "machine/clock.h"
#include "machine/ds75160a.h"
#include "machine/input_merger.h"
#include "machine/ram.h"
#include "machine/tms9914.h"
#include "video/saa5050.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "utf8.h"


// Debugging
#define VERBOSE 0
#include "logmacro.h"


namespace {

class positron_state : public driver_device
{
public:
	positron_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mmu_bankdev(*this, "mmu")
		, m_ram(*this, RAM_TAG)
		, m_keyboard(*this, "COL%u", 0)
		, m_hires_ram(*this, "hires_ram")
		, m_lores_ram(*this, "lores_ram")
		, m_cassette(*this, "cassette")
	{ }

	void positron(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

	TIMER_CALLBACK_MEMBER(fuse_update); // TODO: Does nothing

	// disassembly override
	offs_t os9_dasm_override(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const util::disasm_interface::data_buffer &params);

	emu_timer *m_fuse_timer = nullptr;
	bool m_fuse_timer_running = false;

	void positron_map(address_map &map) ATTR_COLD;
	void positron_fetch(address_map &map) ATTR_COLD;
	void mmu_map(address_map &map) ATTR_COLD;

	uint8_t mmu_r(offs_t offset);
	void mmu_w(offs_t offset, uint8_t data);
	uint8_t vector_r(offs_t offset);

	// sockets for upto 8 x SC67476, only 2 actually fitted in this machine
	struct mmu {
		uint16_t access_reg[1024]{};
		uint8_t key_value[8]{};
		uint8_t access_key = 0;
		uint8_t operate_key = 0;
		uint8_t active_key = 0;
		bool sbit;
	} m_mmu;

	uint8_t opcode_r(offs_t offset);
	uint8_t ram_r(offs_t offset);
	void ram_w(offs_t offset, uint8_t data);

	required_device<mc6809_device> m_maincpu;
	required_device<address_map_bank_device> m_mmu_bankdev;
	required_device<ram_device> m_ram;
	required_ioport_array<14> m_keyboard;
	required_shared_ptr<uint8_t> m_hires_ram;
	required_shared_ptr<uint8_t> m_lores_ram;
	required_device<cassette_image_device> m_cassette;

	memory_passthrough_handler m_mmu_shadow_tap;

	uint8_t m_prev_opcode = 0;
};


void positron_state::machine_start()
{
	std::fill(std::begin(m_mmu.access_reg), std::end(m_mmu.access_reg), 0x3ff);
	m_mmu.sbit = true;

	// select task 0
	m_mmu.active_key = 0;

	m_fuse_timer = timer_alloc(FUNC(positron_state::fuse_update), this);
	m_fuse_timer->adjust(attotime::never);
	m_fuse_timer_running = false;

	save_item(STRUCT_MEMBER(m_mmu, access_reg));
	save_item(STRUCT_MEMBER(m_mmu, key_value));
	save_item(STRUCT_MEMBER(m_mmu, access_key));
	save_item(STRUCT_MEMBER(m_mmu, operate_key));
	save_item(STRUCT_MEMBER(m_mmu, active_key));
	save_item(STRUCT_MEMBER(m_mmu, sbit));
}


void positron_state::machine_reset()
{
	std::fill(std::begin(m_mmu.access_reg), std::end(m_mmu.access_reg), 0x3ff);
	std::fill(std::begin(m_mmu.key_value), std::end(m_mmu.key_value), 0x00);
	m_mmu.access_key = 0x00;
	m_mmu.operate_key = 0x00;
	m_mmu.sbit = true;

	// select task 0
	m_mmu.active_key = 0;

	address_space &program = m_maincpu->space(AS_PROGRAM);
	m_mmu_shadow_tap.remove();
	m_mmu_shadow_tap = program.install_read_tap(
			0x0000, 0xffff,
			"mmu_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					if (m_fuse_timer_running && m_prev_opcode == 0x3b) // RTI
					{
						m_mmu.active_key = m_mmu.operate_key;
						logerror("mmu_shadow_r: switched to task %d\n", m_mmu.active_key);
						m_mmu.sbit = false;
						m_fuse_timer_running = false;
					}
				}
			},
			&m_mmu_shadow_tap);
}


TIMER_CALLBACK_MEMBER(positron_state::fuse_update)
{
	//m_mmu.active_key = m_mmu.operate_key;
	//m_mmu.sbit = false;
	m_fuse_timer->adjust(attotime::never);
}

//-------------------------------------------------
//  ADDRESS_MAP( positron_map )
//-------------------------------------------------

void positron_state::positron_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(positron_state::mmu_r), FUNC(positron_state::mmu_w));
}

void positron_state::positron_fetch(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(positron_state::opcode_r));
}

void positron_state::mmu_map(address_map &map)
{
	map.global_mask(0xfffff);
	map(0x00000, 0x3ffff).rw(FUNC(positron_state::ram_r), FUNC(positron_state::ram_w));
	map(0x40000, 0x7ffff).noprw(); // Expansion board RAM
	map(0x80000, 0x81fff).ram().share("lores_ram");
	//map(0x82000, 0x82000)
	map(0x83000, 0x8300d).lr8([this](offs_t offset) { return m_keyboard[offset]->read(); }, "keyboard_r");
	//map(0x84000, 0x84003)
	map(0x90000, 0x97fff).ram().share("hires_ram");
	map(0xa0000, 0xa0007).rw("timer", FUNC(ptm6840_device::read), FUNC(ptm6840_device::write));
	map(0xa2000, 0xa2000).portr("DSW1");
	map(0xa2001, 0xa2001).portr("DSW2");
	map(0xa2002, 0xa2002).portr("DSW3");
	map(0xa2003, 0xa2003).portr("DSW4");
	map(0xa3000, 0xa3001).rw("acia0", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xa4000, 0xa4001).rw("acia1", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xa4002, 0xa4002).lw8([this](uint8_t data) { LOG("acia1_clock_w: %02x\n", data); }, "acia1_clock_w");
	map(0xa5000, 0xa5001).rw("acia2", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xa5002, 0xa5002).lw8([this](uint8_t data) { LOG("acia2_clock_w: %02x\n", data); }, "acia2_clock_w");
	map(0xa6000, 0xa6001).rw("acia3", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xa6002, 0xa6002).lw8([this](uint8_t data) { LOG("acia3_clock_w: %02x\n", data); }, "acia3_clock_w");
	map(0xa7000, 0xa7001).rw("acia4", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xa7002, 0xa7002).lw8([this](uint8_t data) { LOG("acia4_clock_w: %02x\n", data); }, "acia4_clock_w");
	map(0xa8000, 0xa8007).rw("hpib", FUNC(tms9914_device::read), FUNC(tms9914_device::write));
	map(0xe0000, 0xfffff).rom().region("maincpu", 0);
}


uint8_t positron_state::opcode_r(offs_t offset)
{
	uint8_t data = m_maincpu->space(AS_PROGRAM).read_byte(offset);

	if (m_fuse_timer_running && !machine().side_effects_disabled())
	{
		logerror("opcode_r: %04x = %02x\n", offset, data);
	}

	m_prev_opcode = data;

	return data;
}


uint8_t positron_state::mmu_r(offs_t offset)
{
	uint8_t data = 0x00;

	if (offset >= 0xff80 && offset < 0xffcc && m_mmu.active_key == 0x00)
	{
		switch (offset & 0x40)
		{
		case 0x00:
		{
			uint16_t task_reg = (m_mmu.access_key << 5) | ((offset >> 1) & 0x1f);
			if (offset & 1)
				data = m_mmu.access_reg[task_reg] & 0xff;
			else
				data = m_mmu.access_reg[task_reg] >> 8;
			break;
		}
		case 0x40:
			switch (offset & 0x0f)
			{
			case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
				data = m_mmu.key_value[offset & 7];
				if (!machine().side_effects_disabled())
					LOG("(%04X) mmu_r: key %d = %02x\n", m_maincpu->pc(), offset & 7, data);
				break;
			case 0x08:
				data = m_mmu.sbit ? 1 : 0;
				if (!machine().side_effects_disabled())
					LOG("(%04X) mmu_r: sbit = %d\n", m_maincpu->pc(), data);
				break;
			case 0x0a:
				data = m_mmu.access_key;
				if (!machine().side_effects_disabled())
					LOG("(%04X) mmu_r: access_key = %02x\n", m_maincpu->pc(), data);
				break;
			case 0x0b:
				data = m_mmu.operate_key;
				if (!machine().side_effects_disabled())
					LOG("(%04X) mmu_r: operate_key = %02x\n", m_maincpu->pc(), data);
				break;
			}
			break;
		}
	}
	else
	{
		uint16_t task_reg = (m_mmu.active_key << 5) | ((offset >> 11) & 0x1f);
		offs_t mmu_addr = (m_mmu.access_reg[task_reg] << 11) | (offset & 0x7ff);
		data = m_mmu_bankdev->read8(mmu_addr);
	}

	return data;
}

void positron_state::mmu_w(offs_t offset, uint8_t data)
{
	if (offset >= 0xff80 && offset < 0xffcc && m_mmu.active_key == 0x00 && m_mmu.sbit)
	{
		switch (offset & 0x40)
		{
		case 0x00:
		{
			uint16_t task_reg = (m_mmu.access_key << 5) | ((offset >> 1) & 0x1f);
			if (offset & 1)
				m_mmu.access_reg[task_reg] = (m_mmu.access_reg[task_reg] & 0x300) | data;
			else
				m_mmu.access_reg[task_reg] = (m_mmu.access_reg[task_reg] & 0x0ff) | (data << 8);
			break;
		}
		case 0x40:
			switch (offset & 0x0f)
			{
			case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
				m_mmu.key_value[offset & 7] = data & 0x07;
				LOG("(%04X) mmu_w: key %d = %02x\n", m_maincpu->pc(), offset & 7, data);
				break;
			case 0x09:
				LOG("(%04X) mmu_w: fuse = %02x\n", m_maincpu->pc(), data);
				if (data == 0x00)
				{
					m_mmu.active_key = m_mmu.operate_key;
					logerror("mmu_w: switched to task %d\n", m_mmu.active_key);
					m_mmu.sbit = false;
					m_fuse_timer_running = false;
				}
				else
				{
					//m_fuse_timer->adjust(attotime::from_ticks(data & 0x07, 24_MHz_XTAL / 24));
					m_fuse_timer_running = true;
				}
				break;
			case 0x0a:
				m_mmu.access_key = data & 0x1f;
				LOG("(%04X) mmu_w: access_key = %02x\n", m_maincpu->pc(), data);
				break;
			case 0x0b:
				m_mmu.operate_key = data & 0x1f;
				LOG("(%04X) mmu_w: operate_key = %02x\n", m_maincpu->pc(), data);
				break;
			}
			break;
		}
	}
	else
	{
		uint16_t task_reg = (m_mmu.active_key << 5) | ((offset >> 11) & 0x1f);
		offs_t mmu_addr = (m_mmu.access_reg[task_reg] << 11) | (offset & 0x7ff);
		m_mmu_bankdev->write8(mmu_addr, data);
	}
}

uint8_t positron_state::vector_r(offs_t offset)
{
	if (m_mmu.active_key != 0)
		logerror("vector_r: switched to task 0 from task %d (vector = $%04X)\n", m_mmu.active_key, offset);
	m_mmu.active_key = 0;
	m_mmu.sbit = true;
	return m_maincpu->space(AS_PROGRAM).read_byte(offset);
}


uint8_t positron_state::ram_r(offs_t offset)
{
	if (offset < m_ram->size())
		return m_ram->pointer()[offset];
	else
		return 0xff;
}

void positron_state::ram_w(offs_t offset, uint8_t data)
{
	if (offset < m_ram->size())
	{
		m_ram->pointer()[offset] = data;
	}
}


static INPUT_PORTS_START(positron)
	PORT_START("COL0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RETURN")             PORT_CODE(KEYCODE_ENTER)        PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("REPEAT")             PORT_CODE(KEYCODE_INSERT)       PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ESC")                PORT_CODE(KEYCODE_ESC)          PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CONTROL")            PORT_CODE(KEYCODE_LCONTROL)     PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SHIFT")              PORT_CODE(KEYCODE_LSHIFT)       PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SHIFT")              PORT_CODE(KEYCODE_RSHIFT)       PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SHIFT LOCK")         PORT_CODE(KEYCODE_LALT)         PORT_CHAR(UCHAR_MAMEKEY(LALT))      PORT_TOGGLE
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CAPS LOCK")          PORT_CODE(KEYCODE_CAPSLOCK)     PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))  PORT_TOGGLE

	PORT_START("COL1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT)           PORT_CODE(KEYCODE_RIGHT)        PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT)            PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN)            PORT_CODE(KEYCODE_DOWN)         PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_UP)              PORT_CODE(KEYCODE_UP)           PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DEL")                PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_0)            PORT_CHAR('0')      PORT_CHAR('#')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_1)            PORT_CHAR('1')      PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_2)            PORT_CHAR('2')      PORT_CHAR('\"')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_3)            PORT_CHAR('3')      PORT_CHAR(0xA3)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_4)            PORT_CHAR('4')      PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_5)            PORT_CHAR('5')      PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_6)            PORT_CHAR('6')      PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_7)            PORT_CHAR('7')      PORT_CHAR('\'')

	PORT_START("COL3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_8)            PORT_CHAR('8')      PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_9)            PORT_CHAR('9')      PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_QUOTE)        PORT_CHAR(':')      PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_COLON)        PORT_CHAR(';')      PORT_CHAR('+')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_COMMA)        PORT_CHAR(',')      PORT_CHAR('<')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_MINUS)        PORT_CHAR('=')      PORT_CHAR('-')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_STOP)         PORT_CHAR('.')      PORT_CHAR('>')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_SLASH)        PORT_CHAR('/')      PORT_CHAR('?')

	PORT_START("COL4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_BACKSLASH)    PORT_CHAR('@')      PORT_CHAR('_')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_A)            PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_B)            PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_C)            PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_D)            PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_E)            PORT_CHAR('E')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_F)            PORT_CHAR('F')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_G)            PORT_CHAR('G')

	PORT_START("COL5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_H)            PORT_CHAR('H')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_I)            PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_J)            PORT_CHAR('J')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_K)            PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_L)            PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_M)            PORT_CHAR('M')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_N)            PORT_CHAR('N')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_O)            PORT_CHAR('O')

	PORT_START("COL6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_P)            PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_Q)            PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_R)            PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_S)            PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_T)            PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_U)            PORT_CHAR('U')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_V)            PORT_CHAR('V')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_W)            PORT_CHAR('W')

	PORT_START("COL7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_X)            PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_Y)            PORT_CHAR('Y')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_Z)            PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_OPENBRACE)    PORT_CHAR(0x2190)   PORT_CHAR(0xbc)    // ←  ¼
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_BACKSLASH2)   PORT_CHAR(0xbd)     PORT_CHAR(0x2016)  // ½  ‖
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_CHAR(0x2192)   PORT_CHAR(0xbe)    // →  ¾
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                 PORT_CODE(KEYCODE_EQUALS)       PORT_CHAR('^')      PORT_CHAR(0xf7)    // ÷
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SPACE")              PORT_CODE(KEYCODE_SPACE)        PORT_CHAR(' ')

	PORT_START("COL8") // Video Attributes
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Red Alpha Gr")       PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Green Alpha Gr")     PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Yellow Alpha Gr")    PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Blue Alpha Gr")      PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Magenta Alpha Gr")   PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Cyan Alpha Gr")      PORT_CODE(KEYCODE_F6)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("White Alpha Gr")     PORT_CODE(KEYCODE_F7)

	PORT_START("COL9") // Video Attributes
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("FC SC")              PORT_CODE(KEYCODE_F8)  // Flashing/Steady
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DH NH")              PORT_CODE(KEYCODE_F9)  // Double/Normal Height
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SGR CGR")            PORT_CODE(KEYCODE_F10) // Separated/Contiguous Graphics
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("NB BB")              PORT_CODE(KEYCODE_F11) // New/Black Background
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("HGR RGR")            PORT_CODE(KEYCODE_F12) // Hold/Release Graphics
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL10") // Graphics Encoding
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_TOGGLE // unlabelled Viewdata 2x3 matrix toggle
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_TOGGLE // unlabelled Viewdata 2x3 matrix toggle
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_TOGGLE // unlabelled Viewdata 2x3 matrix toggle
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_TOGGLE // unlabelled Viewdata 2x3 matrix toggle
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_TOGGLE // unlabelled Viewdata 2x3 matrix toggle
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_TOGGLE // unlabelled Viewdata 2x3 matrix toggle
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("REP")                PORT_CODE(KEYCODE_PGUP)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENC")                PORT_CODE(KEYCODE_PGDN)

	PORT_START("COL11")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Keypad 0")           PORT_CODE(KEYCODE_0_PAD)        PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Keypad 1")           PORT_CODE(KEYCODE_1_PAD)        PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Keypad 2")           PORT_CODE(KEYCODE_2_PAD)        PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Keypad 3")           PORT_CODE(KEYCODE_3_PAD)        PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Keypad 4")           PORT_CODE(KEYCODE_4_PAD)        PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Keypad 5")           PORT_CODE(KEYCODE_5_PAD)        PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Keypad 6")           PORT_CODE(KEYCODE_6_PAD)        PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Keypad 7")           PORT_CODE(KEYCODE_7_PAD)        PORT_CHAR(UCHAR_MAMEKEY(7_PAD))

	PORT_START("COL12")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Keypad 8")           PORT_CODE(KEYCODE_8_PAD)        PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Keypad 9")           PORT_CODE(KEYCODE_9_PAD)        PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Keypad *")           PORT_CODE(KEYCODE_ASTERISK)     PORT_CHAR(UCHAR_MAMEKEY(ASTERISK))
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Keypad #")           PORT_CODE(KEYCODE_NUMLOCK)      PORT_CHAR(UCHAR_MAMEKEY(NUMLOCK))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL13") // same as COL1?
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED) // right
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED) // left
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED) // down
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED) // up
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED) // del
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("DSW0") //IEEE-488
	PORT_DIPNAME( 0x01, 0x01, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW0:1")
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW0:2")
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )

	PORT_START("DSW1") // RS232 75-4800 baud
	PORT_DIPNAME( 0x01, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW1:1")
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW1:2")
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW1:3")
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW1:4")
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW1:5")
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW1:6")
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW1:7")
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW1:8")
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )

	PORT_START("DSW2") // RS232 75-4800 baud
	PORT_DIPNAME( 0x01, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW2:1")
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW2:2")
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW2:3")
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW2:4")
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW2:5")
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW2:6")
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW2:7")
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW2:8")
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )

	PORT_START("DSW3") // RS232 75-4800 baud
	PORT_DIPNAME( 0x01, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW3:1")
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW3:2")
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW3:3")
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW3:4")
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW3:5")
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW3:6")
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW3:7")
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW3:8")
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )

	PORT_START("DSW4") // RS232 75-4800 baud
	PORT_DIPNAME( 0x01, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW4:1")
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW4:2")
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW4:3")
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW4:4")
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW4:5")
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW4:6")
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW4:7")
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW4:8")
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )

	PORT_START("DSW5") // Video display mode
	PORT_DIPNAME( 0x01, 0x01, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW5:1")
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW5:2")
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW5:3")
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) ) PORT_DIPLOCATION("DSW5:4")
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
INPUT_PORTS_END


/***************************************************************************
  DISASSEMBLY OVERRIDE (OS9 syscalls)
 ***************************************************************************/

static const char *const os9syscalls[] =
{
	"F$Link",          // Link to Module
	"F$Load",          // Load Module from File
	"F$UnLink",        // Unlink Module
	"F$Fork",          // Start New Process
	"F$Wait",          // Wait for Child Process to Die
	"F$Chain",         // Chain Process to New Module
	"F$Exit",          // Terminate Process
	"F$Mem",           // Set Memory Size
	"F$Send",          // Send Signal to Process
	"F$Icpt",          // Set Signal Intercept
	"F$Sleep",         // Suspend Process
	"F$SSpd",          // Suspend Process
	"F$ID",            // Return Process ID
	"F$SPrior",        // Set Process Priority
	"F$SSWI",          // Set Software Interrupt
	"F$PErr",          // Print Error
	"F$PrsNam",        // Parse Pathlist Name
	"F$CmpNam",        // Compare Two Names
	"F$SchBit",        // Search Bit Map
	"F$AllBit",        // Allocate in Bit Map
	"F$DelBit",        // Deallocate in Bit Map
	"F$Time",          // Get Current Time
	"F$STime",         // Set Current Time
	"F$CRC",           // Generate CRC
	"F$GPrDsc",        // get Process Descriptor copy
	"F$GBlkMp",        // get System Block Map copy
	"F$GModDr",        // get Module Directory copy
	"F$CpyMem",        // Copy External Memory
	"F$SUser",         // Set User ID number
	"F$UnLoad",        // Unlink Module by name
	"F$Alarm",         // Color Computer Alarm Call (system wide)
	nullptr,
	nullptr,
	"F$NMLink",        // Color Computer NonMapping Link
	"F$NMLoad",        // Color Computer NonMapping Load
	nullptr,
	nullptr,
	"F$TPS",           // Return System's Ticks Per Second
	"F$TimAlm",        // COCO individual process alarm call
	"F$VIRQ",          // Install/Delete Virtual IRQ
	"F$SRqMem",        // System Memory Request
	"F$SRtMem",        // System Memory Return
	"F$IRQ",           // Enter IRQ Polling Table
	"F$IOQu",          // Enter I/O Queue
	"F$AProc",         // Enter Active Process Queue
	"F$NProc",         // Start Next Process
	"F$VModul",        // Validate Module
	"F$Find64",        // Find Process/Path Descriptor
	"F$All64",         // Allocate Process/Path Descriptor
	"F$Ret64",         // Return Process/Path Descriptor
	"F$SSvc",          // Service Request Table Initialization
	"F$IODel",         // Delete I/O Module
	"F$SLink",         // System Link
	"F$Boot",          // Bootstrap System
	"F$BtMem",         // Bootstrap Memory Request
	"F$GProcP",        // Get Process ptr
	"F$Move",          // Move Data (low bound first)
	"F$AllRAM",        // Allocate RAM blocks
	"F$AllImg",        // Allocate Image RAM blocks
	"F$DelImg",        // Deallocate Image RAM blocks
	"F$SetImg",        // Set Process DAT Image
	"F$FreeLB",        // Get Free Low Block
	"F$FreeHB",        // Get Free High Block
	"F$AllTsk",        // Allocate Process Task number
	"F$DelTsk",        // Deallocate Process Task number
	"F$SetTsk",        // Set Process Task DAT registers
	"F$ResTsk",        // Reserve Task number
	"F$RelTsk",        // Release Task number
	"F$DATLog",        // Convert DAT Block/Offset to Logical
	"F$DATTmp",        // Make temporary DAT image (Obsolete)
	"F$LDAXY",         // Load A [X,[Y]]
	"F$LDAXYP",        // Load A [X+,[Y]]
	"F$LDDDXY",        // Load D [D+X,[Y]]
	"F$LDABX",         // Load A from 0,X in task B
	"F$STABX",         // Store A at 0,X in task B
	"F$AllPrc",        // Allocate Process Descriptor
	"F$DelPrc",        // Deallocate Process Descriptor
	"F$ELink",         // Link using Module Directory Entry
	"F$FModul",        // Find Module Directory Entry
	"F$MapBlk",        // Map Specific Block
	"F$ClrBlk",        // Clear Specific Block
	"F$DelRAM",        // Deallocate RAM blocks
	"F$GCMDir",        // Pack module directory
	"F$AlHRam",        // Allocate HIGH RAM Blocks
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	"F$RegDmp",        // Ron Lammardo's debugging register dump call
	"F$NVRAM",         // Non Volatile RAM (RTC battery backed static) read/write
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	"I$Attach",        // Attach I/O Device
	"I$Detach",        // Detach I/O Device
	"I$Dup",           // Duplicate Path
	"I$Create",        // Create New File
	"I$Open",          // Open Existing File
	"I$MakDir",        // Make Directory File
	"I$ChgDir",        // Change Default Directory
	"I$Delete",        // Delete File
	"I$Seek",          // Change Current Position
	"I$Read",          // Read Data
	"I$Write",         // Write Data
	"I$ReadLn",        // Read Line of ASCII Data
	"I$WritLn",        // Write Line of ASCII Data
	"I$GetStt",        // Get Path Status
	"I$SetStt",        // Set Path Status
	"I$Close",         // Close Path
	"I$DeletX"         // Delete from current exec dir
};


//-------------------------------------------------
//  os9_dasm_override
//-------------------------------------------------

offs_t positron_state::os9_dasm_override(std::ostream &stream, offs_t pc, const util::disasm_interface::data_buffer &opcodes, const util::disasm_interface::data_buffer &params)
{
	unsigned call;
	offs_t result = 0;

	// Microware OS-9 and a number of other 6x09 based systems used the SWI2
	// instruction for syscalls.  This checks for a SWI2 and looks up the syscall as appropriate
	if ((opcodes.r8(pc) == 0x10) && (opcodes.r8(pc+1) == 0x3F))
	{
		call = opcodes.r8(pc+2);
		if ((call < std::size(os9syscalls)) && (os9syscalls[call] != nullptr))
		{
			util::stream_format(stream, "OS9   %s", os9syscalls[call]);
			result = 3;
		}
	}
	return result;
}


void positron_state::positron(machine_config &config)
{
	MC6809(config, m_maincpu, 24_MHz_XTAL / 6);
	m_maincpu->set_addrmap(AS_PROGRAM, &positron_state::positron_map);
	m_maincpu->set_addrmap(AS_OPCODES, &positron_state::positron_fetch);
	m_maincpu->set_dasm_override(FUNC(positron_state::os9_dasm_override));
	m_maincpu->interrupt_vector_read().set(FUNC(positron_state::vector_r));

	ADDRESS_MAP_BANK(config, m_mmu_bankdev); // SC67476
	m_mmu_bankdev->set_addrmap(0, &positron_state::mmu_map);
	m_mmu_bankdev->set_data_width(8);
	m_mmu_bankdev->set_addr_width(20);

	INPUT_MERGER_ANY_HIGH(config, "irqs").output_handler().set_inputline(m_maincpu, M6809_IRQ_LINE);

	RAM(config, m_ram).set_default_size("192K").set_extra_options("64K,128K,256K");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(17.73447_MHz_XTAL*2, 1135, 0, 960, 625, 0, 480); // clk doubled for interlace
	screen.set_screen_update("saa5050", FUNC(saa5050_device::screen_update));

	saa5050_device &saa5050(SAA5050(config, "saa5050", 24_MHz_XTAL / 4));
	saa5050.d_cb().set([this](offs_t offset) { return m_lores_ram[offset & 0x1fff]; });
	saa5050.set_screen_size(80, 24, 256);

	PALETTE(config, "palette", palette_device::RGB_3BIT);

	ptm6840_device &timer(PTM6840(config, "timer", 24_MHz_XTAL / 24));
	//timer.o3_callback().set("acia0", FUNC(acia6850_device::write_txc));
	//timer.o3_callback().append("acia0", FUNC(acia6850_device::write_rxc));
	timer.irq_callback().set("irqs", FUNC(input_merger_device::in_w<6>));

	acia6850_device &acia0(ACIA6850(config, "acia0"));
	//m_acia[0]->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	//m_acia[0]->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	acia0.irq_handler().set("irqs", FUNC(input_merger_device::in_w<0>));

	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	acia6850_device &acia1(ACIA6850(config, "acia1"));
	acia1.txd_handler().set("serial1", FUNC(rs232_port_device::write_txd));
	acia1.rts_handler().set("serial1", FUNC(rs232_port_device::write_rts));
	acia1.irq_handler().set("irqs", FUNC(input_merger_device::in_w<1>));

	clock_device &acia1_clock(CLOCK(config, "acia1_clock", 4800));
	acia1_clock.signal_handler().set("acia1", FUNC(acia6850_device::write_txc));
	acia1_clock.signal_handler().append("acia1", FUNC(acia6850_device::write_rxc));

	rs232_port_device &serial1(RS232_PORT(config, "serial1", default_rs232_devices, nullptr));
	serial1.rxd_handler().set("acia1", FUNC(acia6850_device::write_rxd));
	serial1.cts_handler().set("acia1", FUNC(acia6850_device::write_cts));

	acia6850_device &acia2(ACIA6850(config, "acia2"));
	acia2.txd_handler().set("serial2", FUNC(rs232_port_device::write_txd));
	acia2.rts_handler().set("serial2", FUNC(rs232_port_device::write_rts));
	acia2.irq_handler().set("irqs", FUNC(input_merger_device::in_w<2>));

	clock_device &acia2_clock(CLOCK(config, "acia2_clock", 4800));
	acia2_clock.signal_handler().set("acia2", FUNC(acia6850_device::write_txc));
	acia2_clock.signal_handler().append("acia2", FUNC(acia6850_device::write_rxc));

	rs232_port_device &serial2(RS232_PORT(config, "serial2", default_rs232_devices, nullptr));
	serial2.rxd_handler().set("acia2", FUNC(acia6850_device::write_rxd));
	serial2.cts_handler().set("acia2", FUNC(acia6850_device::write_cts));

	acia6850_device &acia3(ACIA6850(config, "acia3"));
	acia3.txd_handler().set("serial3", FUNC(rs232_port_device::write_txd));
	acia3.rts_handler().set("serial3", FUNC(rs232_port_device::write_rts));
	acia3.irq_handler().set("irqs", FUNC(input_merger_device::in_w<3>));

	clock_device &acia3_clock(CLOCK(config, "acia3_clock", 4800));
	acia3_clock.signal_handler().set("acia3", FUNC(acia6850_device::write_txc));
	acia3_clock.signal_handler().append("acia3", FUNC(acia6850_device::write_rxc));

	rs232_port_device &serial3(RS232_PORT(config, "serial3", default_rs232_devices, nullptr));
	serial3.rxd_handler().set("acia3", FUNC(acia6850_device::write_rxd));
	serial3.cts_handler().set("acia3", FUNC(acia6850_device::write_cts));

	acia6850_device &acia4(ACIA6850(config, "acia4"));
	acia4.txd_handler().set("serial4", FUNC(rs232_port_device::write_txd));
	acia4.rts_handler().set("serial4", FUNC(rs232_port_device::write_rts));
	acia4.irq_handler().set("irqs", FUNC(input_merger_device::in_w<4>));

	clock_device &acia4_clock(CLOCK(config, "acia4_clock", 4800));
	acia4_clock.signal_handler().set("acia4", FUNC(acia6850_device::write_txc));
	acia4_clock.signal_handler().append("acia4", FUNC(acia6850_device::write_rxc));

	rs232_port_device &serial4(RS232_PORT(config, "serial4", default_rs232_devices, nullptr));
	serial4.rxd_handler().set("acia4", FUNC(acia6850_device::write_rxd));
	serial4.cts_handler().set("acia4", FUNC(acia6850_device::write_cts));

	tms9914_device &hpib(TMS9914(config, "hpib", 16_MHz_XTAL / 4));
	hpib.int_write_cb().set("irqs", FUNC(input_merger_device::in_w<5>));
	hpib.dio_read_cb().set(IEEE488_TAG, FUNC(ieee488_device::dio_r));
	hpib.dio_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_dio_w));
	hpib.eoi_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_eoi_w));
	hpib.dav_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_dav_w));
	hpib.nrfd_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_nrfd_w));
	hpib.ndac_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ndac_w));
	hpib.ifc_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ifc_w));
	hpib.srq_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_srq_w));
	hpib.atn_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_atn_w));
	hpib.ren_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ren_w));

	//DS75160A(config, m_ieee1, 0);
	//m_ieee1->read_callback().set(IEEE488_TAG, FUNC(ieee488_device::dio_r));
	//m_ieee1->write_callback().set(IEEE488_TAG, FUNC(ieee488_device::host_dio_w));

	ieee488_device &ieee(IEEE488(config, IEEE488_TAG));
	ieee.eoi_callback().set("hpib", FUNC(tms9914_device::eoi_w));
	ieee.dav_callback().set("hpib", FUNC(tms9914_device::dav_w));
	ieee.nrfd_callback().set("hpib", FUNC(tms9914_device::nrfd_w));
	ieee.ndac_callback().set("hpib", FUNC(tms9914_device::ndac_w));
	ieee.ifc_callback().set("hpib", FUNC(tms9914_device::ifc_w));
	ieee.srq_callback().set("hpib", FUNC(tms9914_device::srq_w));
	ieee.atn_callback().set("hpib", FUNC(tms9914_device::atn_w));
	ieee.ren_callback().set("hpib", FUNC(tms9914_device::ren_w));
	IEEE488_SLOT(config, "ieee_dev", 0, cbm_ieee488_devices, nullptr);
}


ROM_START(positron)
	ROM_REGION(0x20000, "maincpu", ROMREGION_ERASE00) // supports upto 128K ROM
	ROM_LOAD("os9_5_0007.bin", 0x16000, 0x2000, CRC(f6a24c1d) SHA1(443de539deeec5785872b295dab8d93acd60d19c))
	ROM_LOAD("os9_4_0007.bin", 0x18000, 0x2000, CRC(19f73846) SHA1(525aa101b34f03a5c312ec5aad7c1b9062fe9284))
	ROM_LOAD("os9_3_0007.bin", 0x1a000, 0x2000, CRC(62d47630) SHA1(3719f4fb8ebc8cc64c37702f34532f76178a94d7))
	ROM_LOAD("os9_2_0007.bin", 0x1c000, 0x2000, CRC(63380de9) SHA1(53a58f7c60ebbbc294d94c406ec0fc6c5cb0fb7b))
	ROM_LOAD("os9_1_0007.bin", 0x1e000, 0x2000, CRC(63f0fb57) SHA1(7c50c0cbc7c0dbaf8da186ccb474d263f71416ea))
ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT  COMPAT   MACHINE   INPUT     CLASS           INIT        COMPANY                   FULLNAME         FLAGS
COMP( 1982, positron, 0,      0,       positron, positron, positron_state, empty_init, "Positron Computers Ltd", "Positron 9000", MACHINE_NOT_WORKING )



powermacg3.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/****************************************************************************

    powermacg3.cpp
    PowerMac G3 (original beige hardware)
    Preliminary driver by R. Belmont

    The last desktop Old World Mac, with hardware very similar to the first
    New World machines.

    CPU: PowerPC 750 "G3" @ 233 MHz
    Memory controller/PCI bridge: Motorola MPC106 "Grackle"
    Video: ATI Rage II, ATI Rage Pro on rev. B, ATI Rage Pro Turbo on rev. C
    I/O: Heathrow PCI I/O ASIC (see heathrow.cpp for details)

****************************************************************************/

#include "emu.h"
#include "cpu/powerpc/ppc.h"
#include "machine/dimm_spd.h"
#include "machine/input_merger.h"
#include "machine/mpc106.h"
#include "machine/pci.h"
#include "machine/pci-ide.h"
#include "machine/ram.h"
#include "video/atirage.h"
#include "awacs_macrisc.h"
#include "cuda.h"
#include "heathrow.h"
#include "macadb.h"

class pwrmacg3_state : public driver_device
{
public:
	void pwrmacg3(machine_config &config);

	pwrmacg3_state(const machine_config &mconfig, device_type type, const char *tag);

	required_device<ppc_device> m_maincpu;
	required_device<mpc106_host_device> m_mpc106;
	required_device<cuda_device> m_cuda;
	required_device<macadb_device> m_macadb;
	required_device<dimm_spd_device> m_dimm0, m_dimm1, m_dimm2;
	required_device<ram_device> m_ram;
	required_device<atirage_device> m_atirage;

private:
	u16 m_sense;

	void pwrmacg3_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	u16 read_sense();
	void write_sense(u16 data);

	void cuda_reset_w(int state)
	{
		m_maincpu->set_input_line(INPUT_LINE_HALT, state);
		m_maincpu->set_input_line(INPUT_LINE_RESET, state);
	}

	void irq_w(int state)
	{
		m_maincpu->set_input_line(PPC_IRQ, state);
	}
};

pwrmacg3_state::pwrmacg3_state(const machine_config &mconfig, device_type type, const char *tag) :
	driver_device(mconfig, type, tag),
	m_maincpu(*this, "maincpu"),
	m_mpc106(*this, "pci:00.0"),
	m_cuda(*this, "cuda"),
	m_macadb(*this, "macadb"),
	m_dimm0(*this, "dimm0"),
	m_dimm1(*this, "dimm1"),
	m_dimm2(*this, "dimm2"),
	m_ram(*this, RAM_TAG),
	m_atirage(*this, "pci:12.0")
{
}

void pwrmacg3_state::machine_start()
{
	m_sense = 0;

	m_mpc106->set_ram_info((u8 *)m_ram->pointer(), m_ram->size());

	// start off disabling all of the DIMMs
	m_dimm0->set_dimm_size(dimm_spd_device::SIZE_SLOT_EMPTY);
	m_dimm1->set_dimm_size(dimm_spd_device::SIZE_SLOT_EMPTY);
	m_dimm2->set_dimm_size(dimm_spd_device::SIZE_SLOT_EMPTY);

	switch (m_ram->size())
	{
		case 64*1024*1024:
			m_dimm0->set_dimm_size(dimm_spd_device::SIZE_64_MIB);
			break;

		case 96*1024*1024:
			m_dimm0->set_dimm_size(dimm_spd_device::SIZE_64_MIB);
			m_dimm1->set_dimm_size(dimm_spd_device::SIZE_32_MIB);
			break;

		case 128*1024*1024:
			m_dimm0->set_dimm_size(dimm_spd_device::SIZE_128_MIB);
			break;

		case 256*1024*1024:
			m_dimm0->set_dimm_size(dimm_spd_device::SIZE_256_MIB);
			break;
	}

	save_item(NAME(m_sense));
}

void pwrmacg3_state::machine_reset()
{
	// the PPC can't be allowed to run until Cuda's ready
	m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
}

void pwrmacg3_state::pwrmacg3_map(address_map &map)
{
	map.unmap_value_high();
}

u16 pwrmacg3_state::read_sense()
{
	// ID as a 640x480 13" for now
	return (6 << 8);
}

void pwrmacg3_state::write_sense(u16 data)
{
	m_sense = data;
}

void pwrmacg3_state::pwrmacg3(machine_config &config)
{
	PPC750(config, m_maincpu, 66000000);    // actually 233 MHz
	m_maincpu->ppcdrc_set_options(PPCDRC_COMPATIBLE_OPTIONS);
	m_maincpu->set_addrmap(AS_PROGRAM, &pwrmacg3_state::pwrmacg3_map);

	PCI_ROOT(config, "pci", 0);
	MPC106(config, m_mpc106, 0, mpc106_host_device::MAP_TYPE_B, "maincpu", "bootrom");

	heathrow_device &heathrow(HEATHROW(config, "pci:10.0", 0));
	heathrow.set_maincpu_tag("maincpu");

	// Apple's documentation says systems with the 4.0f2 ROM use a Rage II+, but
	// the 4.0f2 ROM won't init the Rage if the PCI ID is 4755 (II+), only 4754 (Rage II).
	atirage_device &ati(ATI_RAGEII(config, m_atirage, 14.318181_MHz_XTAL));
	ati.gpio_get_cb().set(FUNC(pwrmacg3_state::read_sense));
	ati.gpio_set_cb().set(FUNC(pwrmacg3_state::write_sense));

	MACADB(config, m_macadb, 15.6672_MHz_XTAL);

	CUDA_V2XX(config, m_cuda, XTAL(32'768));
	m_cuda->set_default_bios_tag("341s0060");
	m_cuda->reset_callback().set(FUNC(pwrmacg3_state::cuda_reset_w));
	m_cuda->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
	m_cuda->via_clock_callback().set(heathrow, FUNC(heathrow_device::cb1_w));
	m_cuda->via_data_callback().set(heathrow, FUNC(heathrow_device::cb2_w));
	m_macadb->adb_data_callback().set(m_cuda, FUNC(cuda_device::set_adb_line));
	config.set_perfect_quantum(m_maincpu);

	heathrow.irq_callback().set(FUNC(pwrmacg3_state::irq_w));
	heathrow.pb3_callback().set(m_cuda, FUNC(cuda_device::get_treq));
	heathrow.pb4_callback().set(m_cuda, FUNC(cuda_device::set_byteack));
	heathrow.pb5_callback().set(m_cuda, FUNC(cuda_device::set_tip));
	heathrow.cb2_callback().set(m_cuda, FUNC(cuda_device::set_via_data));

	// ALL_HIGH logically ANDs all sources, which is what we want for I2C/SMBus
	input_merger_device &sda_merger(INPUT_MERGER_ALL_HIGH(config, "sda"));
	sda_merger.output_handler().append(m_cuda, FUNC(cuda_device::set_iic_sda));

	m_cuda->iic_sda_callback().set(sda_merger, FUNC(input_merger_device::in_w<0>));
	m_cuda->iic_sda_callback().append(m_dimm0, FUNC(dimm_spd_device::sda_write));
	m_cuda->iic_sda_callback().append(m_dimm1, FUNC(dimm_spd_device::sda_write));
	m_cuda->iic_sda_callback().append(m_dimm2, FUNC(dimm_spd_device::sda_write));

	DIMM_SPD(config, m_dimm0).set_address(0x50);
	m_cuda->iic_scl_callback().set(m_dimm0, FUNC(dimm_spd_device::scl_write));
	m_dimm0->sda_callback().set(sda_merger, FUNC(input_merger_device::in_w<1>));

	DIMM_SPD(config, m_dimm1).set_address(0x51);
	m_cuda->iic_scl_callback().append(m_dimm1, FUNC(dimm_spd_device::scl_write));
	m_dimm1->sda_callback().set(sda_merger, FUNC(input_merger_device::in_w<2>));

	DIMM_SPD(config, m_dimm2).set_address(0x52);
	m_cuda->iic_scl_callback().append(m_dimm2, FUNC(dimm_spd_device::scl_write));
	m_dimm2->sda_callback().set(sda_merger, FUNC(input_merger_device::in_w<3>));

	RAM(config, m_ram);
	m_ram->set_default_size("64M");
	m_ram->set_extra_options("64M,96M,128M,256M");

	screamer_device &screamer(SCREAMER(config, "codec", 45.1584_MHz_XTAL / 2));
	screamer.dma_output().set(heathrow, FUNC(heathrow_device::codec_dma_read));

	heathrow.codec_r_callback().set(screamer, FUNC(screamer_device::read_macrisc));
	heathrow.codec_w_callback().set(screamer, FUNC(screamer_device::write_macrisc));

	SPEAKER(config, "speaker", 2).front();
	screamer.add_route(0, "speaker", 1.0, 0);
	screamer.add_route(1, "speaker", 1.0, 1);
}

/*
    Config register for Gossamer beige G3 and all-in-one
    bit 15: 1 = SWIM3, 0 = PC style FDC
    bit 14: 1 = slow ROM, 0 = burstable ROM
    bit 12 = PCI slot C card present
    bit 10 = PCI slot B card present
    bit 8  = PCI slot A card present
    bits 7-5 = bus to CPU clock ratio (1 for 2:1)
    bit 4:  0 = all-in-one "Molar Mac", 1 = Desktop beige G3
    bits 3-1: bus speed (0=75 MHz, 1=70, 2=78.75, 3=invalid, 4=75, 5=60, 6=66.82, 7=83)
    bit 0:  must be 1 (burn-in diagnostics?)

    desktop = 0b1011111100111101;
    AIO     = 0b1001010100101101;
*/
ROM_START(pwrmacg3)
	ROM_REGION(0x1000000, "bootrom", ROMREGION_64BIT | ROMREGION_BE | ROMREGION_ERASEFF)
	ROM_LOAD( "pmacg3_79d68d63.bin", 0xc00000, 0x400000, CRC(74a3badf) SHA1(e7fc183f62addc6499350c727252d3348184955e) )

	// The Gossamer machine config register is at 0xFF000000, which is in the MPC106's ROM space.
	// So we're hacking it like this.  Hardware is assumed to operate similarly.
	ROM_FILL(0, 1, 0b10111111)  // bf
	ROM_FILL(1, 1, 0b00111101)  // 3d
	ROM_FILL(2, 1, 0b10111111)
	ROM_FILL(3, 1, 0b00111101)
	ROM_FILL(4, 1, 0b10111111)
	ROM_FILL(5, 1, 0b00111101)
	ROM_FILL(6, 1, 0b10111111)
	ROM_FILL(7, 1, 0b00111101)
ROM_END

static INPUT_PORTS_START(pwrmacg3)
INPUT_PORTS_END

COMP(1997, pwrmacg3, 0, 0, pwrmacg3, pwrmacg3, pwrmacg3_state, empty_init, "Apple Computer", "Power Macintosh G3", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



powerpack.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/****************************************************************************

Skeleton driver for Microtek Powerpack In-Circuit Emulator model PP-SW+ PIII

Main POD with two PCBs connected with a small backplane (also connects
with the pass-through CPU adapter using a flat cable):
 - PCB 1:
  * Side A
   · 2 x Xilinx XC3190A.
   · 5 x OKI M5416283-60.
   · 1 x Intel KU80386EX33 i386 CPU 33MHz.
   · 1 x AM29F040B.
   · 1 x Xilinx XC7336.
   · 1 x GAL16V8D.
   · 1 x 93C46 SEEPROM.
   · 1 x MAX786CAI.
  * Side B
   · 2 x Xilinx XC3190A
   · 5 x OKI M5416283-60.
   · 1 x PC-Card slot (with a D-Link JITI DE660 Ethernet PC Card).
   · 1 x RS-232 port.
   · 1 x Reset button.
 - PCB 2:
  * 1 x Xilinx XCV100.
  * 1 x 16 MHz crystal.
  * 4 x LEDs (Power, Selftest, Emulating, Bus Active).

Pass-through CPU adapter for Pentium III:
 - 1 x Xilinx XCV100.

****************************************************************************/

#include "emu.h"
#include "cpu/i386/i386.h"


namespace {

class powerpack_state : public driver_device
{
public:
	powerpack_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }


	void powerpack(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
};


static INPUT_PORTS_START(powerpack)
INPUT_PORTS_END

void powerpack_state::powerpack(machine_config &config)
{
	I386(config, m_maincpu, 33'000'000); // Intel KU80386EX33
}


ROM_START(powerpack)
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_LOAD( "fls-am29f040b.u51",         0x00000, 0x80000, CRC(4bc45cf8) SHA1(429044f0910c92e2afde49639d7caa4c798597e5) )

	ROM_REGION(0x00080, "eeprom", 0)
	ROM_LOAD( "93c46.u52",                 0x00000, 0x00080, CRC(cc9821ae) SHA1(88719489f653fb11b059ec59e7c2dcad27539c3d) )

	ROM_REGION(0x04000, "pld", 0)
	ROM_LOAD( "drm_1.5_xilinx_xc7336.u59", 0x00000, 0x04000, CRC(c6bcbf58) SHA1(d6a2922c3af3bd50bd54c3b2b80e34c96aba8e7a) )
	ROM_LOAD( "vck_1.0-gal16v8d.u27",      0x00000, 0x00117, CRC(dac50f29) SHA1(78e607a580b8c3563b5e3868c5fc77a451cfafb7) )
ROM_END

} // anonymous namespace

SYST( 1999, powerpack, 0, 0, powerpack, powerpack, powerpack_state, empty_init, "Microtek", "Powerpack In-Circuit Emulator PP-SW+ PIII", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



powerstack.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************************

2017-10-29 Skeleton

Motorola Powerstack II. CPU is a PowerPC 604e @ 300MHz.

************************************************************************************************************************************/

#include "emu.h"
#include "cpu/powerpc/ppc.h"


namespace {

class powerstack_state : public driver_device
{
public:
	powerstack_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		//      , m_maincpu(*this, "maincpu")
	{ }

	void powerstack(machine_config &config);
private:
	//  required_device<cpu_device> m_maincpu;
};

static INPUT_PORTS_START( powerstack )
INPUT_PORTS_END

void powerstack_state::powerstack(machine_config &config)
{
//  ppc604_device &maincpu(PPC604(config, "maincpu", 300'000'000)); // PPC604E @ 300MHz
//  maincpu.set_addrmap(AS_PROGRAM, &powerstack_state::mem_map);
}

ROM_START( powerstk )
	ROM_REGION( 0x80000, "roms", 0 )
	ROM_LOAD( "motorola_powerstack2.bin", 0x0000, 0x80000, CRC(948e8fcd) SHA1(9a8c32b621c98bc33ee525f66747c34d39851685) )
ROM_END

} // anonymous namespace


COMP( 1996, powerstk, 0, 0, powerstack, powerstack, powerstack_state, empty_init, "Motorola", "Powerstack II", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



pp.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Preliminary driver for STM (Semi-Tech Microelectronics) Pied Piper.

****************************************************************************/

#include "emu.h"
#include "bus/centronics/ctronics.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/i8279.h"
#include "machine/input_merger.h"
#include "machine/output_latch.h"
#include "machine/wd_fdc.h"
#include "sound/spkrdev.h"
#include "video/scn2674.h"
#include "screen.h"
#include "speaker.h"


namespace {

class pp_state : public driver_device
{
public:
	pp_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_int4(*this, "int4")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0U)
		, m_pvtc(*this, "pvtc")
		, m_speaker(*this, "speaker")
		, m_printer(*this, "printer")
		, m_bios(*this, "bios")
		, m_chargen(*this, "chargen")
		, m_kbd_rows(*this, "ROW%u", 0U)
		, m_modifiers(*this, "MODIFIERS")
		, m_kbd_leds(*this, "led%u", 1U)
		, m_rom_enabled(false)
		, m_int_pending(0)
		, m_int_status(0xf)
		, m_kbd_scan(0)
		, m_kbd_release(false)
		, m_tmi_enable(false)
		, m_printer_busy(false)
		, m_dr(0)
		, m_mode(0)
	{
	}

	void pp(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	SCN2672_DRAW_CHARACTER_MEMBER(display_char);

	template<int Line> void int_w(int state);
	TIMER_CALLBACK_MEMBER(int_update);
	IRQ_CALLBACK_MEMBER(intak_cb);

	void kbd_scan_w(u8 data);
	u8 kbd_cols_r();
	int cntl_r();
	int shift_r();
	void printer_busy_w(int state);

	u8 memory_r(offs_t offset);
	void memory_w(offs_t offset, u8 data);
	u8 stat_r();
	void co_w(u8 data);
	void hld_w(int state);
	void mode_w(u8 data);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void display_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_device<input_merger_device> m_int4;
	required_device<fd1793_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<scn2672_device> m_pvtc;
	required_device<speaker_sound_device> m_speaker;
	required_device<centronics_device> m_printer;
	required_region_ptr<u8> m_bios;
	required_region_ptr<u8> m_chargen;
	required_ioport_array<8> m_kbd_rows;
	required_ioport m_modifiers;
	output_finder<3> m_kbd_leds;

	std::unique_ptr<u8[]> m_ram;
	bool m_rom_enabled;
	u8 m_int_pending;
	u8 m_int_status;
	u8 m_kbd_scan;
	bool m_kbd_release;
	bool m_tmi_enable;
	bool m_printer_busy;
	u8 m_dr;
	u8 m_mode;
};

void pp_state::machine_start()
{
	m_kbd_leds.resolve();
	m_kbd_leds[0] = 1; // power LED (green) is always on

	m_fdc->dden_w(0);

	// 64K of dynamic RAM (8x 4864)
	m_ram = make_unique_clear<u8[]>(0x10000);
	save_pointer(NAME(m_ram), 0x10000);

	save_item(NAME(m_rom_enabled));
	save_item(NAME(m_int_pending));
	save_item(NAME(m_int_status));
	save_item(NAME(m_kbd_scan));
	save_item(NAME(m_kbd_release));
	save_item(NAME(m_tmi_enable));
	save_item(NAME(m_printer_busy));
	save_item(NAME(m_dr));
	save_item(NAME(m_mode));
}

void pp_state::machine_reset()
{
	mode_w(0);
	m_rom_enabled = true;
}


template <int Line>
void pp_state::int_w(int state)
{
	if (BIT(m_int_pending, Line) == state)
		return;

	if (state)
		m_int_pending |= 1 << Line;
	else
		m_int_pending &= ~(1 << Line);

	machine().scheduler().synchronize(timer_expired_delegate(FUNC(pp_state::int_update), this));
}

TIMER_CALLBACK_MEMBER(pp_state::int_update)
{
	if (m_int_pending == 0)
	{
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
		m_int_status = 0xf;
	}
	else
	{
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);

		u8 priority = 7;
		while (priority != 0 && !BIT(m_int_pending, priority))
			priority--;
		m_int_status = (priority ^ 7) << 1;
	}
}

IRQ_CALLBACK_MEMBER(pp_state::intak_cb)
{
	return m_int_status;
}


SCN2672_DRAW_CHARACTER_MEMBER(pp_state::display_char)
{
	u8 dots = m_chargen[(charcode & 0x7f) << 4 | linecount];

	// reverse video logic
	if (cursor ^ (BIT(charcode, 7) && BIT(m_mode, 1)) ^ BIT(m_mode, 5))
		dots = ~dots;

	// highlight logic
	rgb_t fg = cursor || (BIT(charcode, 7) && BIT(m_mode, 2)) ? rgb_t::white() : rgb_t(0xc0, 0xc0, 0xc0);

	for (int i = 0; i < 7; i++)
	{
		bitmap.pix(y, x++) = BIT(dots, 7) ? fg : rgb_t::black();
		dots <<= 1;
	}
	if (!BIT(m_mode, 6))
		bitmap.pix(y, x++) = BIT(dots, 7) ? fg : rgb_t::black();
}


void pp_state::kbd_scan_w(u8 data)
{
	// SL3 = AUTORPT (also clocks timer interrupt)
	if (!BIT(m_kbd_scan, 3) && BIT(data, 3) && m_tmi_enable)
		int_w<3>(1);
	m_kbd_scan = data;
}

u8 pp_state::kbd_cols_r()
{
	if (m_kbd_release)
		return 0xff;
	else
		return m_kbd_rows[m_kbd_scan & 7]->read();
}

int pp_state::cntl_r()
{
	// pin 1 of J13 connector
	return (m_modifiers->read() & 0x5) == 0x5;
}

int pp_state::shift_r()
{
	// pin 3 of J13 connector
	return (m_modifiers->read() & 0x6) == 0x6;
}

void pp_state::printer_busy_w(int state)
{
	m_printer_busy = state;
}


u8 pp_state::memory_r(offs_t offset)
{
	if (m_rom_enabled && !BIT(offset, 15))
	{
		// ROM accesses have 1 wait state
		if (!machine().side_effects_disabled())
			m_maincpu->adjust_icount(-1);

		return m_bios[offset & 0xfff];
	}
	else
		return m_ram[offset];
}

void pp_state::memory_w(offs_t offset, u8 data)
{
	if (m_rom_enabled && !BIT(offset, 15))
	{
		// ROM accesses have 1 wait state
		if (!machine().side_effects_disabled())
			m_maincpu->adjust_icount(-1);

		logerror("%s: Writing %02Xh to ROM at %04Xh\n", machine().describe_context(), data, offset);
	}
	else
		m_ram[offset] = data;
}

u8 pp_state::stat_r()
{
	if (!machine().side_effects_disabled())
		m_rom_enabled = !BIT(m_mode, 4);

	bool index_pulse = (BIT(m_dr, 0) && m_floppy[0]->get_device() != nullptr && m_floppy[0]->get_device()->idx_r())
			|| (BIT(m_dr, 1) && m_floppy[1]->get_device() != nullptr && m_floppy[1]->get_device()->idx_r());

	return m_int_status
			| (BIT(m_modifiers->read(), 3) ? 0x10 : 0x00)
			| (m_printer_busy ? 0x20 : 0x00)
			| (index_pulse ? 0x00 : 0x40)
			| (BIT(m_kbd_scan, 3) ? 0x80 : 0x00);
}

void pp_state::co_w(u8 data)
{
	// D0 = DR1, D1 = DR2, D2 = SS
	m_dr = data & 0x07;
	floppy_image_device *floppy = nullptr;
	for (int i = 0; i < 2 && floppy == nullptr; i++)
		if (BIT(data, i))
			floppy = m_floppy[i]->get_device();
	m_fdc->set_floppy(floppy);
	if (floppy != nullptr)
		floppy->ss_w(BIT(data, 2));

	m_kbd_leds[2] = BIT(data, 0); // pin 21 of J13 connector
	m_kbd_leds[1] = BIT(data, 1); // pin 22 of J13 connector

	m_printer->write_strobe(!BIT(data, 3)); // pin 7 of connector

	// KBIEN
	m_int4->in_w<0>(BIT(data, 4));

	// TMIEN
	if (m_tmi_enable && !BIT(data, 5))
		int_w<3>(0);
	m_tmi_enable = BIT(data, 5);

	// TODO: D6 = HDIEN

	// KBREL
	m_kbd_release = BIT(data, 7);
}

void pp_state::hld_w(int state)
{
	for (int i = 0; i < 2; i++)
		if (m_floppy[i]->get_device() != nullptr)
			m_floppy[i]->get_device()->mon_w(!state);
}

void pp_state::mode_w(u8 data)
{
	// D0 = 40COL, D1 = REV, D2 = HIL, D3 = CHAR (TODO), D4 = ROMSEL, D5 = BOW, D6 = DIV7-8/
	m_pvtc->set_unscaled_clock(13_MHz_XTAL / (BIT(data, 0) ? 2 : 1) / (BIT(data, 6) ? 7 : 8));
	m_pvtc->set_character_width(BIT(data, 6) ? 7 : 8);

	m_speaker->level_w(BIT(data, 7));

	m_mode = data;
}

void pp_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(pp_state::memory_r), FUNC(pp_state::memory_w));
}

void pp_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x07).rw(m_pvtc, FUNC(scn2672_device::read), FUNC(scn2672_device::write));
	map(0x08, 0x09).mirror(2).rw("pkdi", FUNC(i8279_device::read), FUNC(i8279_device::write));
	map(0x0d, 0x0d).mirror(2).r(m_pvtc, FUNC(scn2672_device::buffer_r)).w("cent_data_out", FUNC(output_latch_device::write));
	map(0x10, 0x13).mirror(4).rw("fdc", FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	map(0x18, 0x18).mirror(2).rw(FUNC(pp_state::stat_r), FUNC(pp_state::co_w));
	map(0x1c, 0x1c).mirror(2).w(m_pvtc, FUNC(scn2672_device::buffer_w));
	map(0x1d, 0x1d).mirror(2).w(FUNC(pp_state::mode_w));
	map(0x30, 0x37).unmaprw(); // TODO: hard disk interface
}

void pp_state::display_map(address_map &map)
{
	map.global_mask(0x7ff);
	map(0x000, 0x7ff).ram(); // 4x MM2114L
}


static INPUT_PORTS_START(pp)
	PORT_START("ROW0") // pin 11 of J13 connector
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CHAR(0x1b) PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR('@') PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR('^') PORT_CODE(KEYCODE_6)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('9') PORT_CHAR('(') PORT_CODE(KEYCODE_9)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0') PORT_CHAR(')') PORT_CODE(KEYCODE_0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0xa3) PORT_CHAR('~') PORT_CODE(KEYCODE_TILDE) // 0x60 unshifted internally
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del") PORT_CHAR(UCHAR_MAMEKEY(DEL)) PORT_CODE(KEYCODE_DEL) // 0x7f internally

	PORT_START("ROW1") // pin 13 of J13 connector
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR('$') PORT_CODE(KEYCODE_4)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR('&') PORT_CODE(KEYCODE_7)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('8') PORT_CHAR('*') PORT_CODE(KEYCODE_8)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('-') PORT_CHAR('_') PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('=') PORT_CHAR('+') PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_END)

	PORT_START("ROW2") // pin 14 of J13 connector
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CHAR(0x09) PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(0x05) PORT_CODE(KEYCODE_E)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR(0x12) PORT_CODE(KEYCODE_R)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(0x15) PORT_CODE(KEYCODE_U)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CHAR('i') PORT_CHAR('I') PORT_CODE(KEYCODE_I)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('[') PORT_CHAR('{') PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(']') PORT_CHAR('}') PORT_CHAR(0x1d) PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("ROW3") // pin 15 of J13 connector
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CHAR('q') PORT_CHAR('Q') PORT_CHAR(0x11) PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR(0x17) PORT_CODE(KEYCODE_W)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(0x14) PORT_CODE(KEYCODE_T)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR(0x19) PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(0x0f) PORT_CODE(KEYCODE_O)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR(0x10) PORT_CODE(KEYCODE_P)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\\') PORT_CHAR('|') PORT_CHAR(0x1c) PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Back Space") PORT_CHAR(0x08) PORT_CODE(KEYCODE_BACKSPACE)

	PORT_START("ROW4") // pin 16 of J13 connector
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR(0x01) PORT_CODE(KEYCODE_A)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR(0x06) PORT_CODE(KEYCODE_F)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR(0x07) PORT_CODE(KEYCODE_G)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR(0x0b) PORT_CODE(KEYCODE_K)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR(0x0c) PORT_CODE(KEYCODE_L)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CHAR(0x0d) PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Line Feed") PORT_CHAR(0x0a) PORT_CODE(KEYCODE_RALT)

	PORT_START("ROW5") // pin 17 of J13 connector
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR(0x13) PORT_CODE(KEYCODE_S)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR(0x04) PORT_CODE(KEYCODE_D)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CHAR('h') PORT_CHAR('H') PORT_CODE(KEYCODE_H)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CHAR('j') PORT_CHAR('J') PORT_CODE(KEYCODE_J)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHAR(':') PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\'') PORT_CHAR('"') PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("ROW6") // pin 18 of J13 connector
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(0x18) PORT_CODE(KEYCODE_X)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR(0x03) PORT_CODE(KEYCODE_C)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(0x0e) PORT_CODE(KEYCODE_N)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_M)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('/') PORT_CHAR('?') PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CODE(KEYCODE_LEFT) // 0x02/0x10/0x13 internally
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("ROW7") // pin 19 of J13 connector
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space Bar") PORT_CHAR(' ') PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(0x1a) PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR(0x16) PORT_CODE(KEYCODE_V)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR(0x02) PORT_CODE(KEYCODE_B)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',') PORT_CHAR('<') PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('.') PORT_CHAR('>') PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CODE(KEYCODE_RIGHT) // 0x06/0x0e/0x18 internally
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("MODIFIERS")
	PORT_BIT(0x1, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CHAR(UCHAR_SHIFT_2) PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x2, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CHAR(UCHAR_SHIFT_1) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x4, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Funct") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x8, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Caps Lock") PORT_TOGGLE PORT_CODE(KEYCODE_CAPSLOCK)
INPUT_PORTS_END

static void pp_floppies(device_slot_interface &device)
{
	// Mitsubishi M485X
	device.option_add("525qd", FLOPPY_525_QD);
}

void pp_state::pp(machine_config &config)
{
	Z80(config, m_maincpu, 8_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &pp_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &pp_state::io_map);
	m_maincpu->set_irq_acknowledge_callback(FUNC(pp_state::intak_cb));

	INPUT_MERGER_ALL_HIGH(config, m_int4).output_handler().set(FUNC(pp_state::int_w<4>));
	INPUT_MERGER_ANY_HIGH(config, "int6").output_handler().set(FUNC(pp_state::int_w<6>));
	INPUT_MERGER_ANY_HIGH(config, "int7").output_handler().set(FUNC(pp_state::int_w<7>));

	i8279_device &pkdi(I8279(config, "pkdi", 8_MHz_XTAL / 8));
	pkdi.out_irq_callback().set(m_int4, FUNC(input_merger_device::in_w<1>));
	pkdi.out_sl_callback().set(FUNC(pp_state::kbd_scan_w));
	//pkdi.out_disp_callback().set(FUNC(pp_state::disp_w));
	pkdi.in_rl_callback().set(FUNC(pp_state::kbd_cols_r));
	pkdi.in_shift_callback().set(FUNC(pp_state::shift_r));
	pkdi.in_ctrl_callback().set(FUNC(pp_state::cntl_r));

	FD1793(config, m_fdc, 8_MHz_XTAL / 8); // with FDC9216 data separator
	m_fdc->set_force_ready(true);
	m_fdc->intrq_wr_callback().set("int6", FUNC(input_merger_device::in_w<0>));
	m_fdc->drq_wr_callback().set("int7", FUNC(input_merger_device::in_w<0>));
	m_fdc->hld_wr_callback().set(FUNC(pp_state::hld_w));

	FLOPPY_CONNECTOR(config, m_floppy[0], pp_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], pp_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	SCN2672(config, m_pvtc, 13_MHz_XTAL / 8);
	m_pvtc->set_character_width(8); // 8 or 7 depending on mode
	m_pvtc->set_screen("screen");
	m_pvtc->set_addrmap(0, &pp_state::display_map);
	m_pvtc->intr_callback().set(FUNC(pp_state::int_w<5>));
	m_pvtc->set_display_callback(FUNC(pp_state::display_char));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(13_MHz_XTAL, 630, 0, 560, 240, 0, 216);
	screen.set_screen_update("pvtc", FUNC(scn2672_device::screen_update));

	SPEAKER(config, "mono").front_center(); // audio output on pin 24 of J13 connector
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.5);

	CENTRONICS(config, m_printer, centronics_devices, nullptr);
	m_printer->busy_handler().set(FUNC(pp_state::printer_busy_w));
	output_latch_device &latch(OUTPUT_LATCH(config, "cent_data_out"));
	m_printer->set_output_latch(latch);
}

ROM_START(pp)
	ROM_REGION(0x1000, "bios", 0)
	ROM_LOAD("rom.bin", 0x0000, 0x1000, CRC(0a4c548f) SHA1(ec0c4ae4c17d427046deadb6f7850ddb3ba002c7))

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD("20000111-00.bin", 0x0000, 0x0800, CRC(17480a9e) SHA1(a874a2f5ceb2884cc1af16132bea2c32f26805c4))
	ROM_CONTINUE(0x0000, 0x0800) // first half blank
	ROM_LOAD("chargen.bin", 0x0000, 0x0800, CRC(111e443a) SHA1(455a573addf274ae3fd41307316d87587d8f5550))
	ROM_CONTINUE(0x0000, 0x0800) // first half blank
ROM_END

} // anonymous namespace


COMP(1983, pp, 0, 0, pp, pp, pp_state, empty_init, "STM Electronics", "Pied Piper Communicator 1", MACHINE_NOT_WORKING)



pp01.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/*****************************************************************************************

PP-01 driver by Miodrag Milanovic.
Also for PP-02/03/04/05/06, if the roms ever appear.

2008-09-08 Preliminary driver.

MONIT to enter the Monitor
M - ?
N - ?
P - to return
S - ?
Z - ?

ToDo:
- Interrupt controller
- Add missing keys
- Quickload support
- Software to test with

*****************************************************************************************/

#include "emu.h"
#include "pp01.h"

#include "screen.h"
#include "speaker.h"


/* Address maps */
void pp01_state::mem_map(address_map &map)
{
	for (int i = 0; i < 16; i++)
		map(i << 12, (i << 12) | 0xfff).bankrw(m_bank[i]);
}

void pp01_state::io_map(address_map &map)
{
	map(0xc0, 0xc3).rw(m_ppi[0], FUNC(i8255_device::read), FUNC(i8255_device::write)); // system
	map(0xc4, 0xc7).rw(m_ppi[1], FUNC(i8255_device::read), FUNC(i8255_device::write)); // user
	map(0xc8, 0xc9).mirror(2).rw(m_uart, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xcc, 0xcf).w(FUNC(pp01_state::video_write_mode_w));
	map(0xd0, 0xd3).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	//map(0xd4, 0xd7). MH3214 interrupt controller
	//map(0xd8, 0xdb). MH102 multiply 2 8-bit signed or unsigned numbers, get back 16-bit result.
	map(0xe0, 0xef).mirror(0x10).rw(FUNC(pp01_state::mem_block_r), FUNC(pp01_state::mem_block_w));
}

/* Input ports */
static INPUT_PORTS_START( pp01 )
	PORT_START("LINE0")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Home") PORT_CODE(KEYCODE_HOME)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 0") PORT_CODE(KEYCODE_0_PAD)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)
	PORT_START("LINE1")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 1") PORT_CODE(KEYCODE_1_PAD)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)
	PORT_START("LINE2")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 2") PORT_CODE(KEYCODE_2_PAD)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
	PORT_START("LINE3")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F6") PORT_CODE(KEYCODE_F6)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 3") PORT_CODE(KEYCODE_3_PAD)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
	PORT_START("LINE4")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F7") PORT_CODE(KEYCODE_F7)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F8") PORT_CODE(KEYCODE_F8)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 4") PORT_CODE(KEYCODE_4_PAD)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
	PORT_START("LINE5")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F9") PORT_CODE(KEYCODE_F9)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F10") PORT_CODE(KEYCODE_F10)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 5") PORT_CODE(KEYCODE_5_PAD)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)
	PORT_START("LINE6")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F11") PORT_CODE(KEYCODE_F11)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F12") PORT_CODE(KEYCODE_F12)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 6") PORT_CODE(KEYCODE_6_PAD)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
	PORT_START("LINE7")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F13") PORT_CODE(KEYCODE_PGUP)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F14") PORT_CODE(KEYCODE_PGDN)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 7") PORT_CODE(KEYCODE_7_PAD)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)
	PORT_START("LINE8")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Delete line") PORT_CODE(KEYCODE_TILDE)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 8") PORT_CODE(KEYCODE_8_PAD)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)
	PORT_START("LINE9")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num +") PORT_CODE(KEYCODE_PLUS_PAD)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 9") PORT_CODE(KEYCODE_9_PAD)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)
	PORT_START("LINE10")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_QUOTE)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
	PORT_START("LINE11")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE)
	PORT_START("LINE12")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num *") PORT_CODE(KEYCODE_ASTERISK)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num -") PORT_CODE(KEYCODE_MINUS_PAD)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH)
	PORT_START("LINE13")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_START("LINE14")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num /") PORT_CODE(KEYCODE_SLASH_PAD)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num .") PORT_CODE(KEYCODE_DEL_PAD)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_TILDE)
	PORT_START("LINE15")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("_") PORT_CODE(KEYCODE_HOME)
	PORT_START("LINE16")
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
INPUT_PORTS_END

/* Machine driver */
void pp01_state::pp01(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, 2000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &pp01_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &pp01_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(256, 256);
	screen.set_visarea(0, 256-1, 0, 256-1);
	screen.set_screen_update(FUNC(pp01_state::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(pp01_state::pp01_palette), 8);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	/* Devices */
	I8251(config, m_uart, 2000000);
	m_uart->txd_handler().set([this] (bool state) { m_txd = state; });
	m_uart->txempty_handler().set([this] (bool state) { m_txe = state; });
	m_uart->rts_handler().set([this] (bool state) { m_rts = state; });

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<2>(2000000);
	m_pit->out_handler<2>().set(FUNC(pp01_state::z2_w));

	I8255A(config, m_ppi[0]);
	m_ppi[0]->in_pa_callback().set(FUNC(pp01_state::ppi1_porta_r));
	m_ppi[0]->out_pa_callback().set(FUNC(pp01_state::ppi1_porta_w));
	m_ppi[0]->in_pb_callback().set(FUNC(pp01_state::ppi1_portb_r));
	m_ppi[0]->out_pb_callback().set(FUNC(pp01_state::ppi1_portb_w));
	m_ppi[0]->in_pc_callback().set(FUNC(pp01_state::ppi1_portc_r));
	m_ppi[0]->out_pc_callback().set(FUNC(pp01_state::ppi1_portc_w));

	I8255A(config, m_ppi[1]);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("64K").set_default_value(0x00);

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.15);
	TIMER(config, "kansas_r").configure_periodic(FUNC(pp01_state::kansas_r), attotime::from_hz(19200));
}

/* ROM definition */

ROM_START( pp01 )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "ep01.rom", 0x0000, 0x0400, CRC(e48e440f) SHA1(aaae41f0bf163f254e28f6e4d9f6010b349e0c45))
	ROM_LOAD( "ep02.rom", 0x0400, 0x0400, CRC(b9314867) SHA1(220e2a686c6624574e8ea7f6012030c8378a8be6))
	ROM_LOAD( "ep03.rom", 0x0800, 0x0400, CRC(47b91d49) SHA1(51445e24d1c375e798c5ed7b48cb1c7b5e01603a))
	ROM_LOAD( "ep04.rom", 0x0c00, 0x0400, CRC(cc890123) SHA1(7110f301453d925da0d3f29c2ba4eacd5ff8583d))
	ROM_LOAD( "ep05.rom", 0x1000, 0x0400, CRC(b36e2071) SHA1(7c7b2c410f4039980627196575109426bb7bab35))
	ROM_LOAD( "ep06.rom", 0x1400, 0x0400, CRC(3f74efb3) SHA1(77d055eb53ba9b4b14eb2bb8d71f93095b846bb5))
	ROM_LOAD( "ep07.rom", 0x1800, 0x0400, CRC(1dd0bc29) SHA1(109bc3f24a76884d2581f75edc532917cb7ea155))
	ROM_LOAD( "ep08.rom", 0x1c00, 0x0400, CRC(607b710a) SHA1(c5ac7da31204c54d6892d6b708d0291d71963581))
	ROM_LOAD( "ep09.rom", 0x2000, 0x0400, CRC(07f773d1) SHA1(a6cf454318ef0a564a86e669b751e414b12c12d1))
	ROM_LOAD( "ep10.rom", 0x2400, 0x0400, CRC(7793d84b) SHA1(d16ad3db36c8cec68713082ee60324219d3d5032))
	ROM_LOAD( "ep11.rom", 0x2800, 0x0400, CRC(d1b98b5e) SHA1(5526af6317667d707ae413916242cd1bac821373))
	ROM_LOAD( "ep12.rom", 0x2c00, 0x0400, CRC(03c45326) SHA1(f9157ab86cdf1d111bcf19c24e4d7250705be965))
	ROM_LOAD( "ep13.rom", 0x3000, 0x0400, CRC(51769d6c) SHA1(76d6e91cf32b1eb94b9c0ded02e5057ed7ff7d72))
	ROM_LOAD( "ep14.rom", 0x3400, 0x0400, CRC(5d48cefb) SHA1(9b8599dabaf7297ec800fa9a122ad7e3b7b30e8b))
	ROM_LOAD( "ep15.rom", 0x3800, 0x0400, CRC(f44bfe36) SHA1(0ab3c4f061dff6f779685409b07343d0a839fdae))
	ROM_LOAD( "ep16.rom", 0x3c00, 0x0400, CRC(f39c230d) SHA1(9789cedef5655bcb82561f387cc1a6cf48287d78))
ROM_END


/* Driver */

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY  FULLNAME  FLAGS
COMP( 198?, pp01, 0,      0,      pp01,    pp01,  pp01_state, empty_init, "ZVT",   "PP-01",  MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



prestige.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/*

VTech PreComputer Prestige Elite

PCB Layout
----------

|-------------------------------------------|
|   |-------CN1-------|     CN2             |
|                                           |
|                                       CN3 |
|                                           |
|CN10                                       |
|CN9            RAM                         --|
|CN8                                          |
|                                             |
|                       Z80         ROM       |
|               04010                     CN4 |
|CN7                                          |
|CN6                                          |
|                       |------CN5------|   --|
|-------------------------------------------|

Notes:
    All IC's shown.

    ROM     - VTech LH5S8R14, labeled "1998 27-6020-02" (dumped as 1Mx8)
    Z80     - Z80 family SOC?
    RAM     - LG Semicon GM76C256CLLFW55 32Kx8 Static RAM
    04010   - ?
    CN1     - Centronics connector
    CN2     - mouse connector
    CN3     - LCD ribbon cable
    CN4     - expansion connector
    CN5     - keyboard ribbon cable
    CN6     - speaker wire
    CN7     - volume wire
    CN8     - ? wire
    CN9     - power wire
    CN10    - NVRAM battery wire
*/

/*

 VTech PC Super Color (Spain)
 __________________________________     _______________|||||||||||||____
 |                                 |   |               ||||||||||||| ___|
 |                                 |   |                             |
 |                                 |___|              TI CSM10233AN  |__
 |                                 ____      ____              __\_____ |
 |      _____________              ____     (GLOB)             |_______||
 |      |S2564RL-100 |             ____     _____                 _____ |
 |      |____________|             ____     |    |                |    ||
 |                                 ____     |Z80 |   S2564RL-100->|    ||
 |     ______________              ____     |    |                |    ||
 |     | 27-5560-01  |             ____     |    |<-Z84C0008PEC   |    ||
 |     |_____________|             |   |    |    | __________     |____||
  \                                |   |    |____| |_________|  ________|
   \_____              ____________|   |           SN74HC244N   |
         ||||||||||||||                |__|||||_____         ___|
         ||||||||||||||                            |||||||||||
*/

/*
    Undumped cartridges:

    80-1410   Super Science
    80-1533   Famous Things & Places
    80-0989   Bible Knowledge
    80-1001   Fantasy Trivia
    80-1002   General Knowledge II
    80-1003   Sports History
    80-2314   Familiar Faces
    80-2315   Historical Happenings
    80-2333   Arts, Entertainment & More
    80-2334   Customs & Cultures
    80-1531   32K RAM Memory Expansion Cartridge
    80-12051  Space Scholar
    80-12053  Frenzy of Facts
    80-12052  Spreadsheet Success

*/

/*
    TODO:

    - identify unknown chips (maybe related to the sound??)
    - better IRQ timing
    - the mouse is too slow and sometime freezes
    - finish to decode the memory banking
    - sound
    - cartridges

*/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/ram.h"
#include "machine/timer.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"


namespace {

class prestige_state : public driver_device
{
public:
	prestige_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_cart(*this, "cartslot")
		, m_keyboard(*this, "KEY.%u", 0)
		, m_cart_type(*this, "CART_TYPE")
		, m_bank1(*this, "bank1")
		, m_bank2(*this, "bank2")
		, m_bank3(*this, "bank3")
		, m_bank4(*this, "bank4")
		, m_bank5(*this, "bank5")
	{ }

	void prestige_base(machine_config &config);
	void gl6000sl(machine_config &config);
	void snotec(machine_config &config);
	void glmcolor(machine_config &config);
	void glcolor(machine_config &config);
	void prestige(machine_config &config);
	void gl7007sl(machine_config &config);

protected:
	uint32_t m_extra_program_offset;
	uint16_t m_num_rom_entries;
	uint16_t m_rom_bank_mask;

	virtual void machine_start() override ATTR_COLD;

	virtual void setup_extra_program_offset();

private:
	required_device<cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<generic_slot_device> m_cart;
	required_ioport_array<16> m_keyboard;
	required_ioport m_cart_type;
	required_memory_bank m_bank1;
	required_memory_bank m_bank2;
	required_memory_bank m_bank3;
	required_memory_bank m_bank4;
	required_memory_bank m_bank5;

	uint8_t m_bank[7];
	uint8_t m_kb_matrix;
	uint8_t m_irq_counter;
	uint8_t m_mousex;
	uint8_t m_mousey;
	uint8_t *m_vram;
	struct
	{
		uint16_t  addr1;
		uint16_t  addr2;
		uint8_t   lcd_w;
		uint8_t   lcd_h;
		uint8_t   fb_width;
		uint8_t   split_pos;
	} m_lcdc;

	memory_region *m_cart_rom;

	uint32_t screen_update(int bpp, screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	uint32_t screen_update_1bpp(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	uint32_t screen_update_2bpp(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	uint8_t bankswitch_r(offs_t offset);
	void bankswitch_w(offs_t offset, uint8_t data);
	uint8_t kb_r(offs_t offset);
	void kb_w(uint8_t data);
	uint8_t mouse_r(offs_t offset);
	void mouse_w(offs_t offset, uint8_t data);
	void lcdc_w(offs_t offset, uint8_t data);
	void prestige_palette(palette_device &palette) const;
	void glcolor_palette(palette_device &palette) const;
	TIMER_DEVICE_CALLBACK_MEMBER(irq_timer);
	IRQ_CALLBACK_MEMBER(prestige_int_ack);

	void glcolor_io(address_map &map) ATTR_COLD;
	void prestige_io(address_map &map) ATTR_COLD;
	void prestige_mem(address_map &map) ATTR_COLD;
};


uint8_t prestige_state::bankswitch_r(offs_t offset)
{
	return m_bank[offset];
}

void prestige_state::bankswitch_w(offs_t offset, uint8_t data)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	switch (offset)
	{
	case 0:
		m_bank1->set_entry(data & m_rom_bank_mask);
		break;

	case 1:
		if (!(m_bank[5] & 0x01) && (m_bank[5] & 0x02) && (m_cart_type->read() == 0x02 || m_cart->exists()))
			m_bank2->set_entry(m_num_rom_entries + (data & 0x1f));
		else
			m_bank2->set_entry(data & m_rom_bank_mask);
		break;

	case 2:
		if (!(m_bank[5] & 0x01) && (m_bank[5] & 0x04) && (m_cart_type->read() == 0x02 || m_cart->exists()))
			m_bank3->set_entry(m_num_rom_entries + (data & 0x1f));
		else
			m_bank3->set_entry(data & m_rom_bank_mask);
		break;

	case 3:
		m_bank4->set_entry(data & 0x03);
		break;

	case 4:
		m_bank5->set_entry(data & 0x03);
		break;

	case 5:
		if (m_bank[5] == data)
			break;

		if (data & 0x20)
		{
			program.install_ram(0x8000, 0xbfff, m_vram);
		}
		else if (m_cart_type->read() == 0x01)
		{
			//cartridge memory is writable
			if (data & 0x02)
				program.install_readwrite_bank(0x4000, 0x7fff, m_bank2);
			else
				program.unmap_write(0x4000, 0x7fff);

			if (data & 0x04)
				program.install_readwrite_bank(0x8000, 0xbfff, m_bank3);
			else
				program.unmap_write(0x8000, 0xbfff);
		}
		else
		{
			//cartridge memory is read-only
			program.unmap_write(0x4000, 0xbfff);
			program.install_read_bank(0x8000, 0xbfff, m_bank3);
		}
		break;
	case 6:
		break;
	}

	m_bank[offset] = data;
}

uint8_t prestige_state::kb_r(offs_t offset)
{
	uint8_t data = 0xff;

	for (int line=0; line<8; line++)
		if (!(m_kb_matrix & (1<<line)))
			data &= m_keyboard[offset * 8 + line]->read();

	return data;
}

void prestige_state::kb_w(uint8_t data)
{
	m_kb_matrix = data;
}

uint8_t prestige_state::mouse_r(offs_t offset)
{
	int16_t data = 0;

	switch( offset )
	{
		case 0:     //x-axis
			data = (ioport("MOUSEX")->read() - m_mousex);
			break;
		case 1:     //y-axis
			data = (ioport("MOUSEY")->read() - m_mousey);
			break;
	}

	data = (std::min)(data, int16_t(+127));
	data = (std::max)(data, int16_t(-127));

	return 0x80 + data;
}

void prestige_state::mouse_w(offs_t offset, uint8_t data)
{
	switch( offset )
	{
		case 0:     //x-axis
			m_mousex = ioport("MOUSEX")->read();
			break;
		case 1:     //y-axis
			m_mousey = ioport("MOUSEY")->read();
			break;
	}
}

void prestige_state::lcdc_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
		case 0x02:
			m_lcdc.lcd_w = data;
			break;
		case 0x04:
			m_lcdc.lcd_h = data;
			break;
		case 0x06:
			m_lcdc.addr1 = (m_lcdc.addr1 & 0xff00) | data;
			break;
		case 0x07:
			m_lcdc.addr1 = (m_lcdc.addr1 & 0x00ff) | (data << 8);
			break;
		case 0x08:
			m_lcdc.addr2 = (m_lcdc.addr2 & 0xff00) | data;
			break;
		case 0x09:
			m_lcdc.addr2 = (m_lcdc.addr2 & 0x00ff) | (data << 8);
			break;
		case 0x0a:
			m_lcdc.split_pos = data;
			break;
		case 0x0d:
			m_lcdc.fb_width = data;
			break;
		default:
			logerror("Unknown LCDC reg write %x = %x\n", offset, data);
	}
}


void prestige_state::prestige_mem(address_map &map)
{
	map(0x0000, 0x3fff).bankr("bank1");
	map(0x4000, 0x7fff).bankr("bank2");
	map(0x8000, 0xbfff).bankr("bank3");
	map(0xc000, 0xdfff).bankrw("bank4");
	map(0xe000, 0xffff).bankrw("bank5");
}

void prestige_state::prestige_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x04, 0x05).rw(FUNC(prestige_state::mouse_r), FUNC(prestige_state::mouse_w));
	map(0x30, 0x3f).w(FUNC(prestige_state::lcdc_w));
	map(0x40, 0x40).w(FUNC(prestige_state::kb_w));
	map(0x41, 0x42).r(FUNC(prestige_state::kb_r));
	map(0x50, 0x56).rw(FUNC(prestige_state::bankswitch_r), FUNC(prestige_state::bankswitch_w));
}

void prestige_state::glcolor_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x30, 0x3f).w(FUNC(prestige_state::lcdc_w));
	map(0x40, 0x40).w(FUNC(prestige_state::kb_w));
	map(0x41, 0x42).r(FUNC(prestige_state::kb_r));
	map(0x50, 0x56).rw(FUNC(prestige_state::bankswitch_r), FUNC(prestige_state::bankswitch_w));
}

/* Input ports */
INPUT_PORTS_START( prestige )
	PORT_START("CART_TYPE")
	PORT_CONFNAME( 0x01, 0x00, "Cartridge Type" )
	PORT_CONFSETTING( 0x00, "ROM" )
	PORT_CONFSETTING( 0x01, "RAM" )

	PORT_START("MOUSEX")
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_X ) PORT_SENSITIVITY(20) PORT_KEYDELTA(2)

	PORT_START("MOUSEY")
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y ) PORT_SENSITIVITY(20) PORT_KEYDELTA(2)

	PORT_START("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left mouse button")  PORT_CODE(MOUSECODE_BUTTON1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1")  PORT_CODE(KEYCODE_1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9")  PORT_CODE(KEYCODE_9)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("e")  PORT_CODE(KEYCODE_E)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2018")   PORT_CODE(KEYCODE_OPENBRACE) // U+2018 = ‘
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("g")  PORT_CODE(KEYCODE_G)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",")  PORT_CODE(KEYCODE_COMMA)

	PORT_START("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right mouse button") PORT_CODE(MOUSECODE_BUTTON2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2")  PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0")  PORT_CODE(KEYCODE_0)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("r")  PORT_CODE(KEYCODE_R)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("+")  PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("h")  PORT_CODE(KEYCODE_H)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("z")  PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".")  PORT_CODE(KEYCODE_STOP)

	PORT_START("KEY.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Mouse Up (KB)")  PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3")  PORT_CODE(KEYCODE_3)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"´") PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("t")  PORT_CODE(KEYCODE_T)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ins") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("j")  PORT_CODE(KEYCODE_J)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("x")  PORT_CODE(KEYCODE_X)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-")  PORT_CODE(KEYCODE_MINUS)

	PORT_START("KEY.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Mouse Left (KB)")    PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4")  PORT_CODE(KEYCODE_4)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"¡") PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("y")  PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Caps Lock")  PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("k")  PORT_CODE(KEYCODE_K)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("c")  PORT_CODE(KEYCODE_C)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)

	PORT_START("KEY.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Mouse Right (KB)")   PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5")  PORT_CODE(KEYCODE_5)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Backspace")  PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("u")  PORT_CODE(KEYCODE_U)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("a")  PORT_CODE(KEYCODE_A)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("l")  PORT_CODE(KEYCODE_L)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("v")  PORT_CODE(KEYCODE_V)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right Shift")    PORT_CODE(KEYCODE_RSHIFT)

	PORT_START("KEY.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Mouse Down (KB)")    PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6")  PORT_CODE(KEYCODE_6)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("i")  PORT_CODE(KEYCODE_I)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("s")  PORT_CODE(KEYCODE_S)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"ñ") PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("b")  PORT_CODE(KEYCODE_B)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Help") PORT_CODE(KEYCODE_PGUP)

	PORT_START("KEY.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("??") PORT_CODE(KEYCODE_F10)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7")  PORT_CODE(KEYCODE_7)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("q")  PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("o")  PORT_CODE(KEYCODE_O)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("d")  PORT_CODE(KEYCODE_D)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("??") PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("n")  PORT_CODE(KEYCODE_N)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Symbol") PORT_CODE(KEYCODE_PGDN)

	PORT_START("KEY.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("OFF")    PORT_CODE(KEYCODE_F9)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8")  PORT_CODE(KEYCODE_8)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("w")  PORT_CODE(KEYCODE_W)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("p")  PORT_CODE(KEYCODE_P)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f")  PORT_CODE(KEYCODE_F)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter")  PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("m")  PORT_CODE(KEYCODE_M)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Answer") PORT_CODE(KEYCODE_END)

	PORT_START("KEY.8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space")  PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Word Games") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Player") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Alt")    PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Mathematics")    PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Level")      PORT_CODE(KEYCODE_F4)
	PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Repeat") PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Trivia") PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cartridge")  PORT_CODE(KEYCODE_F6)
	PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.11")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left")   PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Logic Games")    PORT_CODE(KEYCODE_F7)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Business Basics")    PORT_CODE(KEYCODE_F8)
	PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.12")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down")   PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.13")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right")  PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.14")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left mouse button (KB)") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.15")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right mouse button (KB)")    PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNUSED)

INPUT_PORTS_END

INPUT_PORTS_START( glcolor )
	PORT_START("CART_TYPE")
	PORT_CONFNAME( 0x03, 0x02, "Cartridge Type" )
	PORT_CONFSETTING( 0x00, "ROM" )
	PORT_CONFSETTING( 0x01, "RAM" )
	PORT_CONFSETTING( 0x02, "Internal" )

	PORT_START("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('^')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('/')

	PORT_START("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_NAME("Help")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)  PORT_CHAR(0x00df) PORT_CHAR('?')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('=')

	PORT_START("KEY.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("KEY.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')

	PORT_START("KEY.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_NAME("Spieler 1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')

	PORT_START("KEY.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_NAME("Spielers")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_NAME("Stufe")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_NAME("Symbols")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR(';')

	PORT_START("KEY.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL) PORT_NAME("Button 1")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_END) PORT_NAME("Button 2")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_NAME("Button 3")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP) PORT_NAME("Button 4")

	PORT_START("KEY.7")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KEY.8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Backspace") PORT_CHAR(UCHAR_MAMEKEY(BACKSPACE))
	PORT_BIT(0xfc, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KEY.9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(0x00fc) PORT_CHAR(0x00dc)
	PORT_BIT(0xfc, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KEY.10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(0x00e4) PORT_CHAR(0x00c4)
	PORT_BIT(0xfc, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KEY.11")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KEY.12")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(0x00f6) PORT_CHAR(0x00d6)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_NAME("Spieler 2")
	PORT_BIT(0xfc, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KEY.13")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(':')
	PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KEY.14")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KEY.15")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

INPUT_PORTS_END

INPUT_PORTS_START( glmcolor )
	PORT_INCLUDE(glcolor)

	PORT_MODIFY("KEY.14")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(MOUSECODE_BUTTON1) PORT_NAME("Mouse Button 1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(MOUSECODE_BUTTON2) PORT_NAME("Mouse Button 2")

	PORT_START("MOUSEX")
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_X ) PORT_SENSITIVITY(30) PORT_KEYDELTA(2)

	PORT_START("MOUSEY")
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y ) PORT_SENSITIVITY(30) PORT_KEYDELTA(2)
INPUT_PORTS_END


IRQ_CALLBACK_MEMBER(prestige_state::prestige_int_ack)
{
	uint32_t vector;

	m_maincpu->set_input_line(0, CLEAR_LINE);

	if (m_irq_counter == 0x04)
	{
		m_irq_counter = 0;
		vector = 0x0020;
	}
	else
	{
		m_irq_counter++;
		vector = 0x0030;
	}

	return (0xcd<<16) | vector;
}

void prestige_state::machine_start()
{
	setup_extra_program_offset();
	m_num_rom_entries = memregion("maincpu")->bytes() / 0x4000;
	m_rom_bank_mask = m_num_rom_entries - 1;

	std::string region_tag;
	m_cart_rom = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());

	uint8_t *rom = memregion("maincpu")->base();
	uint8_t *cart = nullptr;
	if (m_cart_rom != nullptr)
	{
		cart = m_cart_rom->base();
	}
	else
	{
		/*
		    Each internal ROM also includes an extra program, activated by a
		    blank cartridge that works as a jumper (pins 14 and 18 are shorted):

		    - [snotec] Lucky Check Fortune Telling (ラッキーチェックうらない)
		    - [snotecex] Super Cassette: Guessing Card Game / Jungle Cruise (スーパーカセット あてっこ カードゲーム / ジャングル クルーズ)
		    - [snotecu, snotecug] Super AquaMate (スーパーアクアメイト)
		    - [snotecut] Little Sorcery (リトルソーサリー)
		*/
		cart = rom + m_extra_program_offset;
	}
	uint8_t *ram = m_ram->pointer();
	memset(ram, 0x00, m_ram->size());

	m_bank1->configure_entries(0,                 m_num_rom_entries, rom,  0x4000);
	m_bank1->configure_entries(m_num_rom_entries, 32,                cart, 0x4000);
	m_bank2->configure_entries(0,                 m_num_rom_entries, rom,  0x4000);
	m_bank2->configure_entries(m_num_rom_entries, 32,                cart, 0x4000);
	m_bank3->configure_entries(0,                 m_num_rom_entries, rom,  0x4000);
	m_bank3->configure_entries(m_num_rom_entries, 32,                cart, 0x4000);
	m_bank4->configure_entries(0,                 4,                 ram,  0x2000);
	m_bank5->configure_entries(0,                 4,                 ram,  0x2000);

	m_bank1->set_entry(0);
	m_bank2->set_entry(0);
	m_bank3->set_entry(0);
	m_bank4->set_entry(0);
	m_bank5->set_entry(0);

	m_irq_counter = 0;

	m_lcdc.addr1 = 0;
	m_lcdc.addr2 = 0;
	m_lcdc.lcd_w = 0;
	m_lcdc.lcd_h = 0;
	m_lcdc.fb_width = 0;
	m_lcdc.split_pos = 0;

	//pointer to the videoram
	m_vram = ram;
}

void prestige_state::setup_extra_program_offset()
{
	m_extra_program_offset = 0x40000;
}

void prestige_state::prestige_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(39, 108, 51));
	palette.set_pen_color(1, rgb_t(16,  37, 84));
}

void prestige_state::glcolor_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(0x3f, 0xbf, 0x3f));
	palette.set_pen_color(1, rgb_t(0xff, 0x3f, 0x5f));
	palette.set_pen_color(2, rgb_t(0x1f, 0x1f, 0x3f));
	palette.set_pen_color(3, rgb_t(0xff, 0xdf, 0x1f));
}

uint32_t prestige_state::screen_update(int bpp, screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	int width = m_lcdc.fb_width + m_lcdc.lcd_w + 1;

	for (int y = 0; y <= m_lcdc.lcd_h; y++)
	{
		int line_start;
		if (y <= m_lcdc.split_pos)
			line_start = m_lcdc.addr1 + y * width;
		else
			line_start = m_lcdc.addr2 + (y - m_lcdc.split_pos - 1) * width;

		for (int sx = 0; sx <= m_lcdc.lcd_w; sx++)
		{
			uint8_t data = m_vram[(line_start + sx) & 0x1fff];

			for (int x = 0; x < 8 / bpp; x++)
			{
				int pix = 0;
				for (int b=0; b<bpp; b++)
					pix |= BIT(data, 7 - b) << b;

				if (cliprect.contains(sx * 8 / bpp + x, y))
					bitmap.pix(y, sx * 8 / bpp + x) = pix;

				data <<= bpp;
			}
		}
	}

	return 0;
}

uint32_t prestige_state::screen_update_1bpp(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return screen_update(1, screen, bitmap, cliprect);
}

uint32_t prestige_state::screen_update_2bpp(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return screen_update(2, screen, bitmap, cliprect);
}

TIMER_DEVICE_CALLBACK_MEMBER(prestige_state::irq_timer)
{
	m_maincpu->set_input_line(0, ASSERT_LINE);
}

void prestige_state::prestige_base(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(8'000'000));  // Z84C008
	m_maincpu->set_addrmap(AS_PROGRAM, &prestige_state::prestige_mem);
	m_maincpu->set_addrmap(AS_IO, &prestige_state::prestige_io);
	m_maincpu->set_irq_acknowledge_callback(FUNC(prestige_state::prestige_int_ack));

	TIMER(config, "irq_timer").configure_periodic(FUNC(prestige_state::irq_timer), attotime::from_hz(200));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(prestige_state::screen_update_1bpp));
	screen.set_size(240, 100);
	screen.set_visarea(0, 240-1, 0, 100-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(prestige_state::prestige_palette), 2);

	/* cartridge */
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "genius_cart");

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("32K").set_extra_options("64K");
}

void prestige_state::glcolor(machine_config &config)
{
	prestige_base(config);

	m_maincpu->set_addrmap(AS_IO, &prestige_state::glcolor_io);

	/* video hardware */
	screen_device &screen(*subdevice<screen_device>("screen"));
	screen.set_screen_update(FUNC(prestige_state::screen_update_2bpp));
	screen.set_size(160, 80);
	screen.set_visarea(0, 160-1, 0, 80-1);

	subdevice<palette_device>("palette")->set_entries(4).set_init(FUNC(prestige_state::glcolor_palette));

	SOFTWARE_LIST(config, "cart_list").set_original("glcolor");
	SOFTWARE_LIST(config, "snotec_cart").set_compatible("snotec");
}

void prestige_state::glmcolor(machine_config &config)
{
	glcolor(config);

	m_maincpu->set_addrmap(AS_IO, &prestige_state::prestige_io);
}

void prestige_state::snotec(machine_config &config)
{
	glcolor(config);

	config.device_remove("snotec_cart");
	SOFTWARE_LIST(config.replace(), "cart_list").set_original("snotec");
	SOFTWARE_LIST(config, "glcolor_cart").set_compatible("glcolor");
}

void prestige_state::prestige(machine_config &config)
{
	prestige_base(config);

	SOFTWARE_LIST(config, "gl6000sl_cart").set_compatible("gl6000sl");
	SOFTWARE_LIST(config, "misterx_cart").set_compatible("misterx");
	SOFTWARE_LIST(config, "gl2000_cart").set_compatible("gl2000");
}

void prestige_state::gl6000sl(machine_config &config)
{
	prestige_base(config);

	SOFTWARE_LIST(config, "cart_list").set_original("gl6000sl");
	SOFTWARE_LIST(config, "misterx_cart").set_compatible("misterx");
	SOFTWARE_LIST(config, "gl2000_cart").set_compatible("gl2000");
}

void prestige_state::gl7007sl(machine_config &config)
{
	prestige_base(config);

	SOFTWARE_LIST(config, "gl6000sl_cart").set_compatible("gl6000sl");
	SOFTWARE_LIST(config, "gl2000_cart").set_compatible("gl2000");
	SOFTWARE_LIST(config, "misterx_cart").set_compatible("misterx");
}

class snotecut_state : public prestige_state
{
public:
	snotecut_state(const machine_config &mconfig, device_type type, const char *tag)
		: prestige_state(mconfig, type, tag)
	{ }

protected:
	virtual void setup_extra_program_offset() override;
};

void snotecut_state::setup_extra_program_offset()
{
	m_extra_program_offset = 0x100000;
}

/* ROM definition */
ROM_START( gl6000sl )
	ROM_REGION(0x100000, "maincpu", 0)
	ROM_LOAD( "27-5894-01",   0x000000, 0x080000, CRC(7336231c) SHA1(35a1f739994b5c8fb67a7f76d423e50d8154e9ea) )
ROM_END

ROM_START( gl7007sl )
	ROM_REGION(0x100000, "maincpu", 0)
	ROM_LOAD( "27-6060-00", 0x000000, 0x100000, CRC(06b2a595) SHA1(654d00e55ee43627ff947d72676c8e48e0518123) )
ROM_END

ROM_START( prestige )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "27-6020-02.u2", 0x00000, 0x100000, CRC(6bb6db14) SHA1(5d51fc3fd799e7f01ee99c453f9005fb07747b1e) )
ROM_END

ROM_START( gwnf )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "27-6372-00.bin", 0x00000, 0x100000, CRC(1bb574bd) SHA1(04234a33405782e8641883ebd6dee46a24e014d5) )
ROM_END

ROM_START( glcolor )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "27-5488-00.u5", 0x00000, 0x080000, CRC(e6cf7702) SHA1(ce40418a7777b331bf8c4c881d51732aeb384582) )
ROM_END

ROM_START( glscolor )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "27-5488-00.u5", 0x00000, 0x080000, CRC(e6cf7702) SHA1(ce40418a7777b331bf8c4c881d51732aeb384582) )    // identical to 'Genius Leader Color'
ROM_END

ROM_START( pcscolor )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "27-5560-01.u5", 0x00000, 0x080000, CRC(e21e7ecd) SHA1(f3eeb19a88f1856406b357f2966880113b7340dc) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROM_LOAD( "csm10233an.u1", 0x0000, 0x2000, NO_DUMP ) // TSP50C10 (8K bytes of ROM) labeled "51CTCJT VIDEO TECH CSM10233AN"
ROM_END

ROM_START( snotec )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "27-5616-01.u6", 0x00000, 0x080000, CRC(74093f5b) SHA1(3495b07e297315051888261d608680513a05c08b) )
ROM_END

ROM_START( snotecex )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "27-5758-00.u6", 0x00000, 0x080000, CRC(aac672be) SHA1(6ac09c3ae8c1c987072b2272cfcf34d9083431cb) )
ROM_END

ROM_START( snotecu )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "27-6100-00.u1", 0x00000, 0x100000, CRC(b2f979d5) SHA1(d2a76e99351971d1fb4cf4df9fe5741a606eb844) )
ROM_END

ROM_START( snotecug )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "27-6100-02.u1", 0x00000, 0x100000, CRC(1e14e6ea) SHA1(3e3b8dbea5f559ff98f525e3c7029b9d55e5515b) )
ROM_END

ROM_START( snotecut )
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_LOAD( "27-6429-00.u1", 0x00000, 0x200000, CRC(16b1a0d6) SHA1(72f467e2f3bef4995d0eadb8387a88b0d9fa2893) )
ROM_END

ROM_START( glmcolor )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "27-5673-00.u6", 0x00000, 0x100000, CRC(c4245392) SHA1(bb651aaf11b75f4155c0a0106de9394018110cc7) )
ROM_END

ROM_START( gmmc )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "27-5889-00.bin", 0x080000, 0x080000, CRC(5e2c6359) SHA1(cc01c7bd5c87224b63dd1044db5a36a5cb7824f1) BAD_DUMP ) // very likely underdumped
	ROM_RELOAD( 0x060000, 0x020000 )
	ROM_CONTINUE( 0x040000, 0x020000 )
	ROM_CONTINUE( 0x020000, 0x020000 )
	ROM_CONTINUE( 0x000000, 0x020000 )
ROM_END

} // Anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY   FULLNAME                                   FLAGS
COMP( 1994, glcolor,  0,       0,      glcolor,  glcolor,  prestige_state, empty_init, "VTech",  "Genius Leader Color (Germany)",           MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1994, glscolor, glcolor, 0,      glcolor,  glcolor,  prestige_state, empty_init, "VTech",  "Genius Leader Super Color (Germany)",     MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1994, pcscolor, 0,       0,      glcolor,  glcolor,  prestige_state, empty_init, "VTech",  "PC Super Color (Spain)",                  MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1995, snotec,   0,       0,      snotec,   glcolor,  prestige_state, empty_init, "Bandai", "Super Note Club (Japan)",                 MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1996, snotecex, 0,       0,      snotec,   glcolor,  prestige_state, empty_init, "Bandai", "Super Note Club EX (Japan)",              MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1996, glmcolor, 0,       0,      glmcolor, glmcolor, prestige_state, empty_init, "VTech",  "Genius Leader Magic Color (Germany)",     MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1997, gl6000sl, 0,       0,      gl6000sl, prestige, prestige_state, empty_init, "VTech",  "Genius Leader 6000SL (Germany)",          MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1998, snotecu,  0,       0,      snotec,   glcolor,  prestige_state, empty_init, "Bandai", u8"Super Note Club µ (Japan)",             MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1999, snotecug, snotecu, 0,      snotec,   glcolor,  prestige_state, empty_init, "Bandai", u8"Super Note Club µ girlish (Japan)",     MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1999, snotecut, snotecu, 0,      snotec,   glcolor,  snotecut_state, empty_init, "Bandai", u8"Super Note Club µ teen's time (Japan)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1998, gl7007sl, 0,       0,      gl7007sl, prestige, prestige_state, empty_init, "VTech",  "Genius Leader 7007SL (Germany)",          MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1998, prestige, 0,       0,      prestige, prestige, prestige_state, empty_init, "VTech",  "PreComputer Prestige Elite",              MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1999, gwnf,     0,       0,      prestige, prestige, prestige_state, empty_init, "VTech",  "Genius Winner Notebook Fun (Germany)",    MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 199?, gmmc,     0,       0,      prestige, prestige, prestige_state, empty_init, "VTech",  "Genius Master Mega Color (Germany)",      MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



primo.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger, bataais
/*******************************************************************************

Novag Primo & related chess computers. The chess engine is by David Kittinger.

NOTE: Turn the power switch off before exiting MAME, otherwise NVRAM won't save
properly.

TODO:
- if/when MAME supports an exit callback, hook up power-off switch to that
- Novag Super System peripherals don't work on nsnova due to serial clock drift,
  baud rate differs a bit between host and client, m6801 serial emulation issue
- unmapped reads from 0x33/0x34 (nsnova) or 0x3c/0x3d (others)
- supremo unmapped writes to 0x2000/0x6000, always 0?
- is the 1st version of supremo(black plastic) the same ROM?
- is "Aquamarine / Super Nova" the same ROM as nsnova and just a redesign?

BTANB:
- nprimo/beluga has the same bug as nvip, where if the user presses Go right
  after entering a move during the opening, the CPU opponent will answer by
  playing a move with white

================================================================================

Novag Primo family
------------------

Hardware notes:

Primo (model 871):
- PCB label: 100059/100060
- Hitachi HD6301Y0P (mode 2) @ 8MHz
- 2KB RAM(M5M5118P)
- LCD with 4 7segs and custom segments, no LCD chip
- buzzer, 16 LEDs, 8*8 chessboard buttons

Beluga (model 903):
- PCB label: 100116
- CPU runs at 16MHz, rest is same as Primo

The LCD is the same as the one in VIP / Super VIP. Beluga is an updated version
of the program in Primo, just in a different housing (more similar to nsnova).

================================================================================

Novag Supremo (model 881)
-------------------------

Hardware notes:
- PCB label: 100090
- Hitachi HD63A03YP @ 8MHz
- 32KB ROM(TC57256AD-12), 2KB RAM(TC5516APL)
- rest is same as Primo

Supremo also had a "limited edition" rerelease in 1990, plastic is fake-wood
instead of black and backpanel sticker is gold, otherwise it's the same game.
The model number is still 881, ROM is the same as the standard fake-wood version.

================================================================================

Novag Super Nova (model 904)
----------------------------

Hardware notes:
- Hitachi HD63A03YP (or HD6301Y0P in mode 1) @ 16MHz
- 32KB ROM(TC57256AD-12), 8KB RAM(CXK58648P-10L)
- LCD with 4 7segs and custom segments, no LCD chip
- RJ-12 port for Novag Super System (like the one in nsvip/sexpertc)
- buzzer, 16 LEDs, 8*8 chessboard buttons

Older versions had a bug in the opening moves, always playing B5 after D4.

The program is very similar to Super VIP, it could be said that Super Nova is
the Super VIP combined with the Novag Super System Touch Sensory board.

*******************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/m6800/m6801.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "novag_beluga.lh"
#include "novag_primo.lh"
#include "novag_snova.lh"
#include "novag_supremo.lh"


namespace {

class primo_state : public driver_device
{
public:
	primo_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_lcd_pwm(*this, "lcd_pwm"),
		m_led_pwm(*this, "led_pwm"),
		m_dac(*this, "dac"),
		m_rs232(*this, "rs232"),
		m_inputs(*this, "IN.%u", 0),
		m_out_lcd(*this, "s%u.%u", 0U, 0U)
	{ }

	void primo(machine_config &config);
	void beluga(machine_config &config);
	void supremo(machine_config &config);
	void snova(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(primo_power_off) { if (newval) m_power = false; }
	DECLARE_INPUT_CHANGED_MEMBER(snova_power_off);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD { m_power = true; }

private:
	// devices/pointers
	required_device<hd6301y_cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_lcd_pwm;
	required_device<pwm_display_device> m_led_pwm;
	required_device<dac_1bit_device> m_dac;
	optional_device<rs232_port_device> m_rs232;
	required_ioport_array<2> m_inputs;
	output_finder<4, 10> m_out_lcd;

	bool m_power = false;
	bool m_lcd_strobe = false;
	u8 m_inp_mux = 0;
	u8 m_select = 0;
	u8 m_led_data = 0;

	void primo_map(address_map &map) ATTR_COLD;
	void supremo_map(address_map &map) ATTR_COLD;
	void snova_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void standby(int state);
	void lcd_pwm_w(offs_t offset, u8 data);
	void update_leds();

	u8 p2_r();
	void p2_w(u8 data);
	void p5_w(u8 data);
	void p6_w(u8 data);
};

void primo_state::machine_start()
{
	m_out_lcd.resolve();

	// register for savestates
	save_item(NAME(m_power));
	save_item(NAME(m_lcd_strobe));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_select));
	save_item(NAME(m_led_data));
}



/*******************************************************************************
    I/O
*******************************************************************************/

// power

void primo_state::standby(int state)
{
	// clear display
	if (state)
	{
		m_lcd_pwm->clear();
		m_led_pwm->clear();
	}
}

INPUT_CHANGED_MEMBER(primo_state::snova_power_off)
{
	// nsnova NMI at power-off, which will trigger standby mode
	if (newval && !m_maincpu->standby())
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}


// misc

void primo_state::lcd_pwm_w(offs_t offset, u8 data)
{
	m_out_lcd[offset & 0x3f][offset >> 6] = data;
}

void primo_state::update_leds()
{
	m_led_pwm->matrix(m_select >> 4 & 3, m_led_data);
}


// MCU ports

u8 primo_state::p2_r()
{
	// P20: 4051 Z
	u8 data = 0;

	// read chessboard buttons
	for (int i = 0; i < 8; i++)
		if (BIT(m_led_data, i))
			data |= BIT(m_board->read_file(i), m_inp_mux);

	// read keypad buttons
	for (int i = 0; i < 2; i++)
		if (BIT(m_select, i + 6))
			data |= BIT(m_inputs[i]->read(), m_inp_mux);

	// P23 (nsnova): serial rx
	if (m_rs232)
		data |= m_rs232->rxd_r() << 3;

	// P23 (others): power switch
	if (!m_power)
		data |= 8;

	return data ^ 1;
}

void primo_state::p2_w(u8 data)
{
	// P21: 4066 in/out to LCD
	if (m_lcd_strobe && ~data & 2)
	{
		u16 lcd_data = (m_select << 2 & 0x300) | m_led_data;
		m_lcd_pwm->matrix(m_select & 0xf, lcd_data);
	}
	m_lcd_strobe = bool(data & 2);

	// P22: speaker out
	m_dac->write(BIT(data, 2));

	// P24 (nsnova): serial tx (TTL)
	if (m_rs232)
		m_rs232->write_txd(BIT(data, 4));

	// P25-P27: 4051 S0-S2
	// 4051 Y0-Y7: multiplexed inputs
	m_inp_mux = data >> 5 & 7;
}

void primo_state::p5_w(u8 data)
{
	// P50-P53: 4066 control to LCD
	// P54,P55: led select
	// P56,P57: keypad mux, lcd data
	m_select = data ^ 0xf0;
	update_leds();
}

void primo_state::p6_w(u8 data)
{
	// P60-P67: led data, lcd data, chessboard mux
	m_led_data = ~data;
	update_leds();
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void primo_state::primo_map(address_map &map)
{
	map(0x4000, 0x47ff).mirror(0x3800).ram().share("nvram");
}

void primo_state::supremo_map(address_map &map)
{
	map(0x4000, 0x47ff).mirror(0x1800).ram().share("nvram");
	map(0x8000, 0xffff).rom();
}

void primo_state::snova_map(address_map &map)
{
	map(0x4000, 0x5fff).ram().share("nvram");
	map(0x8000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( primo )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Trace Back / Next Best")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Trace Forward / Auto Play")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Set Level / Pawn")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Info / Knight")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Easy / Bishop")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Solve Mate / Rook")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Sound / Queen")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Referee / King")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Go")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Restore")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Hint")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Clear / Clear Board")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Color")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Verify / Set Up")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Random")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")

	PORT_START("POWER") // needs to be triggered for nvram to work
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(primo_state::primo_power_off), 0)
INPUT_PORTS_END

static INPUT_PORTS_START( supremo )
	PORT_INCLUDE( primo )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Easy / Print Moves / Bishop")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Solve Mate / Print Eval / Rook")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Sound / Print List / Queen")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Referee / Print Board / King")
INPUT_PORTS_END


static INPUT_PORTS_START( beluga )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Trace Back / Next Best")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Trace Forward / Auto Play")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Set Level / Pawn")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Info / Knight")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Easy / Bishop")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Solve Mate / Rook")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Sound / Queen")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Referee / King")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Go")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Restore")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Hint")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Clear Board / Clear")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Color")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Set Up / Verify")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Random")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")

	PORT_START("POWER") // needs to be triggered for nvram to work
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(primo_state::primo_power_off), 0)
INPUT_PORTS_END

static INPUT_PORTS_START( snova )
	PORT_INCLUDE( beluga )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Info / Echo / Knight")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Easy / Moves / Bishop")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Solve Mate / Language / Rook")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Sound / Game / Queen")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Referee / Board / King")

	PORT_MODIFY("IN.1")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Hint / Human")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Color / Video")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Random / Auto Clock")

	PORT_MODIFY("POWER") // needs to be triggered for nvram to work
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(primo_state::snova_power_off), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void primo_state::primo(machine_config &config)
{
	// basic machine hardware
	HD6301Y0(config, m_maincpu, 8_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &primo_state::primo_map);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(hd6301y0_cpu_device::nvram_set_battery));
	m_maincpu->standby_cb().append(FUNC(primo_state::standby));
	m_maincpu->in_p2_cb().set(FUNC(primo_state::p2_r));
	m_maincpu->out_p2_cb().set(FUNC(primo_state::p2_w));
	m_maincpu->out_p5_cb().set(FUNC(primo_state::p5_w));
	m_maincpu->out_p6_cb().set(FUNC(primo_state::p6_w));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(350));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_lcd_pwm).set_size(4, 10);
	m_lcd_pwm->output_x().set(FUNC(primo_state::lcd_pwm_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/5, 606/5);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_led_pwm).set_size(2, 8);
	config.set_default_layout(layout_novag_primo);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void primo_state::beluga(machine_config &config)
{
	primo(config);

	// basic machine hardware
	m_maincpu->set_clock(16_MHz_XTAL);
	m_maincpu->in_p2_cb().set(FUNC(primo_state::p2_r)).mask(0xef);

	// P24 is tied to P52 (freq sel via P50-P53)
	m_maincpu->in_p2_cb().append([this]() { return m_select << 2; }).mask(0x10);

	config.set_default_layout(layout_novag_beluga);
}

void primo_state::supremo(machine_config &config)
{
	primo(config);

	// basic machine hardware
	HD6303Y(config.replace(), m_maincpu, 8_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &primo_state::supremo_map);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(hd6303y_cpu_device::nvram_set_battery));
	m_maincpu->standby_cb().append(FUNC(primo_state::standby));
	m_maincpu->in_p2_cb().set(FUNC(primo_state::p2_r));
	m_maincpu->out_p2_cb().set(FUNC(primo_state::p2_w));
	m_maincpu->out_p5_cb().set(FUNC(primo_state::p5_w));
	m_maincpu->out_p6_cb().set(FUNC(primo_state::p6_w));

	config.set_default_layout(layout_novag_supremo);
}

void primo_state::snova(machine_config &config)
{
	supremo(config);

	// basic machine hardware
	m_maincpu->set_clock(16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &primo_state::snova_map);

	config.set_default_layout(layout_novag_snova);

	// rs232 (configure after video)
	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( nprimo )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("novag_871_31y0c34p.u1", 0x0000, 0x4000, CRC(ad692d2e) SHA1(f41ae4e02a83ba6446a6df59a6ee2c2f87a1f4d8) )

	ROM_REGION( 36256, "screen", 0 )
	ROM_LOAD("nvip.svg", 0, 36256, CRC(3373e0d5) SHA1(25bfbf0405017388c30f4529106baccb4723bc6b) )
ROM_END


ROM_START( beluga )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("novag_903_31y0rm59p.u1", 0x0000, 0x4000, CRC(16fc6bfc) SHA1(5ab4c7e92eb7b6b449c388f50293dfd01aa87c24) )

	ROM_REGION( 36256, "screen", 0 )
	ROM_LOAD("nvip.svg", 0, 36256, CRC(3373e0d5) SHA1(25bfbf0405017388c30f4529106baccb4723bc6b) )
ROM_END


ROM_START( supremo )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("sp_a10.u5", 0x8000, 0x8000, CRC(1db63786) SHA1(4f24452ed8955b31ba88f68cc95c357660930aa4) )

	ROM_REGION( 36256, "screen", 0 )
	ROM_LOAD("nvip.svg", 0, 36256, CRC(3373e0d5) SHA1(25bfbf0405017388c30f4529106baccb4723bc6b) )
ROM_END


ROM_START( nsnova ) // ID = N1.05, serial 326xx/340xx
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("n_530.u5", 0x8000, 0x8000, CRC(727a0ada) SHA1(129c1edc5c1d2e12ce97ebef81c6d5555464a11d) )

	ROM_REGION( 36256, "screen", 0 )
	ROM_LOAD("nvip.svg", 0, 36256, CRC(3373e0d5) SHA1(25bfbf0405017388c30f4529106baccb4723bc6b) )
ROM_END

ROM_START( nsnovaa ) // ID = N1.05, serial 310xx
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("n_319.u5", 0x8000, 0x8000, CRC(7ad4cbde) SHA1(cc92a162d4a63466f2333708a8e07269646188ea) ) // 1 byte different, does not look like bitrot

	ROM_REGION( 36256, "screen", 0 )
	ROM_LOAD("nvip.svg", 0, 36256, CRC(3373e0d5) SHA1(25bfbf0405017388c30f4529106baccb4723bc6b) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS        INIT        COMPANY, FULLNAME, FLAGS
SYST( 1987, nprimo,  0,      0,      primo,   primo,   primo_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Primo (Novag)", MACHINE_SUPPORTS_SAVE )

SYST( 1990, beluga,  0,      0,      beluga,  beluga,  primo_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Beluga", MACHINE_SUPPORTS_SAVE )

SYST( 1988, supremo, 0,      0,      supremo, supremo, primo_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Supremo", MACHINE_SUPPORTS_SAVE )

SYST( 1990, nsnova,  0,      0,      snova,   snova,   primo_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Super Nova (Novag, set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1990, nsnovaa, nsnova, 0,      snova,   snova,   primo_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Super Nova (Novag, set 2)", MACHINE_SUPPORTS_SAVE )



primusex.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Preliminary driver for Yeno Primus Expert mit Stimme.

    This learning computer is likely a German-localized version of some
    VTech product (possibly code-named "Mouse Toy" if the program ROM can
    be trusted), though the keyboard layout suggests that a French version
    might also have been contemplated.

    It includes a mouse port (not yet emulated) and a cartridge slot (no
    carts have been dumped, though at least one titled "SprachenExperte"
    was released; pinout may be identical to Yeno's PC-Logomax 2).

***************************************************************************/

#include "emu.h"

//#include "bus/generic/slot.h"
#include "cpu/z80/z80.h"
#include "machine/i8255.h"
#include "video/hd61202.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"

namespace {

class primusex_state : public driver_device
{
public:
	primusex_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "hd61202")
		, m_rombank(*this, "rombank")
		, m_keys(*this, "KEY%u", 0U)
		, m_cursor(*this, "CURSOR")
	{ }

	void primusex(machine_config &config);

	ioport_value encoder_r();

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void ppi1_pa_w(u8 data);
	void ppi1_pb_w(u8 data);
	u8 ppi1_pc_r();
	void ppi2_pa_w(u8 data);
	u8 ppi2_pb_r();
	void ppi2_pc_w(u8 data);
	void key_scan_w(u8 data);
	u8 keyboard_r();

	HD61202_UPDATE_CB(hd61202_update);

	INTERRUPT_GEN_MEMBER(periodic_int);

	void primusex_mem(address_map &map);
	void primusex_io(address_map &map);

	required_device<cpu_device> m_maincpu;
	required_device<hd61202_device> m_lcdc;
	required_memory_bank m_rombank;
	required_ioport_array<8> m_keys;
	required_ioport m_cursor;

	bool m_nmi_enable = false;
	u8 m_key_scan = 0;
};


HD61202_UPDATE_CB(primusex_state::hd61202_update)
{
	if (lcd_on)
	{
		for (int y = 0; y < 6; y++)
		{
			for (int x = 0; x < 64; x++)
			{
				int addr = y * 64 + x;
				for (int yi = 0; yi < 8; yi++)
				{
					int px = x;
					int py = y * 8 + yi;

					if (cliprect.contains(px, py))
						bitmap.pix(py, px) = BIT(ddr[addr & 0x1ff], yi);
				}
			}

			for (int x = 0; x < 16; x++)
			{
				int px = 64 + x;
				for (int yi = 0; yi < 8; yi++)
				{
					int addr = (6 + (x >> 3)) * 64 + y * 8 + yi;
					int py = y * 8 + yi;

					if (cliprect.contains(px, py))
						bitmap.pix(py, px) = BIT(ddr[addr & 0x1ff], x & 7);
				}
			}
		}
	}
	else
		bitmap.fill(0, cliprect);

	return 0;
}


void primusex_state::ppi1_pa_w(u8 data)
{
	// TODO
	logerror("%s: PPI1 port A = %02Xh\n", machine().describe_context(), data);
}

void primusex_state::ppi1_pb_w(u8 data)
{
	// TODO: bits 5, 7 also used
	m_rombank->set_entry(data & 0x1f);
	m_nmi_enable = BIT(data, 6);
}

void primusex_state::ppi2_pa_w(u8 data)
{
	// TODO
	logerror("%s: PPI2 port A = %02Xh\n", machine().describe_context(), data);
}

u8 primusex_state::ppi2_pb_r()
{
	// TODO
	return 0;
}

void primusex_state::ppi2_pc_w(u8 data)
{
	// TODO
	//logerror("%s: PPI2 port C = %02Xh\n", machine().describe_context(), data);
}

void primusex_state::key_scan_w(u8 data)
{
	m_key_scan = data; // active low
}

u8 primusex_state::keyboard_r()
{
	u8 data = 0xff;
	for (int i = 0; i < 8; i++)
		if (!BIT(m_key_scan, i))
			data &= m_keys[i]->read();
	return data;
}

void primusex_state::primusex_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).bankr("rombank");
	map(0x8000, 0x9fff).ram();
	map(0xc000, 0xc000).rw(m_lcdc, FUNC(hd61202_device::status_r), FUNC(hd61202_device::control_w));
	map(0xc001, 0xc001).rw(m_lcdc, FUNC(hd61202_device::data_r), FUNC(hd61202_device::data_w));
}

void primusex_state::primusex_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x03).rw("ppi1", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x20, 0x20).w(FUNC(primusex_state::key_scan_w));
	map(0x40, 0x40).r(FUNC(primusex_state::keyboard_r));
	map(0x60, 0x63).rw("ppi2", FUNC(i8255_device::read), FUNC(i8255_device::write));
}


ioport_value primusex_state::encoder_r()
{
	ioport_value cursor_keys = m_cursor->read() ^ 0xff;
	if (cursor_keys == 0)
		return 0xf;
	else
		return 31 - count_leading_zeros_32(cursor_keys);
}

INPUT_PORTS_START(primusex)
	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2190  Demo") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(0x08)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') PORT_CHAR(0x00a8) // dead key
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('$') PORT_CHAR('0')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR(';') PORT_CHAR('8')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('\'') PORT_CHAR('6')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR(0x00e7) PORT_CHAR('4')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR(0x00e9) PORT_CHAR('2')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_NAME("Unknown 07")

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ñ")  PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(0x00d1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                   PORT_CODE(KEYCODE_MINUS) PORT_CHAR('<') PORT_CHAR('>')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                   PORT_CODE(KEYCODE_9) PORT_CHAR(',') PORT_CHAR('9')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                   PORT_CODE(KEYCODE_7) PORT_CHAR('"') PORT_CHAR('7')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD)                   PORT_CODE(KEYCODE_5) PORT_CHAR(0x00df) PORT_CHAR('5')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD)                   PORT_CODE(KEYCODE_3) PORT_CHAR(0x00e8) PORT_CHAR('3')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD)                   PORT_CODE(KEYCODE_1) PORT_CHAR(0x00e0) PORT_CHAR('1')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_NAME("Unknown 17")

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Neuer Name  Entfernen") PORT_CODE(KEYCODE_END)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ü")  PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(0x00fc) PORT_CHAR(0x00dc)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O")    PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U")    PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T")    PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E")    PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q")    PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Antwort") PORT_CODE(KEYCODE_CAPSLOCK)

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_NAME("Multiple Choice")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                 PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(0x00b4) PORT_CHAR('`') // dead key
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P")  PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I")  PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z")  PORT_CODE(KEYCODE_Y) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R")  PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W")  PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A")  PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')

	PORT_START("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ä")  PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(0x00e4) PORT_CHAR(0x00c4)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L")    PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J")    PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G")    PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D")    PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X")    PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S")    PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_NAME("Unknown 47")

	PORT_START("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u21b5") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(0x0d)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ö")  PORT_CODE(KEYCODE_COLON) PORT_CHAR(0x00f6) PORT_CHAR(0x00d6)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                   PORT_CODE(KEYCODE_STOP) PORT_CHAR('-') PORT_CHAR('/')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K")    PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H")    PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F")    PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C")    PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_NAME("Unknown 57")

	PORT_START("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_NAME("Unknown 60")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                 PORT_CODE(KEYCODE_SLASH) PORT_CHAR('*') PORT_CHAR('=')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                 PORT_CODE(KEYCODE_COMMA) PORT_CHAR('+') PORT_CHAR('.')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M")  PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N")  PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B")  PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V")  PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y")  PORT_CODE(KEYCODE_Z) PORT_CHAR('y') PORT_CHAR('Y')

	PORT_START("KEY7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_NAME("Unknown 70")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9) PORT_NAME("Unknown 71")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Unknown 72")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Unknown 73")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RCONTROL) PORT_NAME("Hilfe")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL) PORT_NAME("Unknown 75")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space")  PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10) PORT_NAME("Unknown 77")

	PORT_START("SPECIAL")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Unknown 0") PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift")     PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Unknown 2") PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Unknown 3") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0xf0, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CUSTOM_MEMBER(FUNC(primusex_state::encoder_r))

	PORT_START("CURSOR")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u21d2") PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u21d7") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u21d1") PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u21d6") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u21d0") PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u21d9") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u21d3") PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u21d8") PORT_CODE(KEYCODE_3_PAD)
INPUT_PORTS_END


void primusex_state::machine_start()
{
	u8 *mainrom = memregion("program")->base();
	m_rombank->configure_entries(0, 0x10, mainrom, 0x8000);
	m_rombank->configure_entries(0x10, 0x10, mainrom, 0x8000); // or from cartridge?

	m_rombank->set_entry(0);

	save_item(NAME(m_nmi_enable));
	save_item(NAME(m_key_scan));
}

INTERRUPT_GEN_MEMBER(primusex_state::periodic_int)
{
	if (m_nmi_enable)
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}

void primusex_state::primusex(machine_config &config)
{
	Z80(config, m_maincpu, 4'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &primusex_state::primusex_mem);
	m_maincpu->set_addrmap(AS_IO, &primusex_state::primusex_io);
	m_maincpu->set_periodic_int(FUNC(primusex_state::periodic_int), attotime::from_hz(50));

	i8255_device &ppi1(I8255(config, "ppi1"));
	ppi1.out_pa_callback().set(FUNC(primusex_state::ppi1_pa_w));
	ppi1.out_pb_callback().set(FUNC(primusex_state::ppi1_pb_w));
	ppi1.tri_pb_callback().set_constant(0);
	ppi1.in_pc_callback().set_ioport("SPECIAL");

	i8255_device &ppi2(I8255(config, "ppi2"));
	ppi2.out_pa_callback().set(FUNC(primusex_state::ppi2_pa_w));
	ppi2.in_pb_callback().set(FUNC(primusex_state::ppi2_pb_r));
	ppi2.out_pc_callback().set(FUNC(primusex_state::ppi2_pc_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_size(80, 48);
	screen.set_visarea_full();
	screen.set_screen_update(m_lcdc, FUNC(hd61202_device::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);
	HD61202(config, m_lcdc).set_screen_update_cb(FUNC(primusex_state::hd61202_update));

	SOFTWARE_LIST(config, "cart_list").set_original("primusex");
}

ROM_START(primusex) // Z84C0006PEC + 2x CP82C55A + V05040INS (?) with 80x48 pixel LCD
	ROM_REGION(0x80000, "program", 0)
	ROM_LOAD("mtrom.u11", 0x00000, 0x80000, CRC(b35f8e80) SHA1(aa79175f0e590201b62ba1c19492833064e69f71))
ROM_END

} // anonymous namespace

COMP(1994, primusex, 0, 0, primusex, primusex, primusex_state, empty_init, "Yeno", "Primus Expert mit Stimme", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



prisma.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Saitek Kasparov Prisma (model 281)

NOTE: Before exiting MAME, press the STOP button to turn the power off. Otherwise,
NVRAM won't save properly. To force a cold boot, hold the PLAY button and trigger
a power on/reset (F3).

It's the 'sequel' to Simultano, and the first chess computer with a H8 CPU. Even
though H8 is much faster than 6502, it plays weaker than Simultano, probably due to
less RAM. And/or it could also be due to the programmer(s) unfamiliarity with H8.

Hardware notes:
- PCB label: ST9A-PE-001
- Hitachi H8/325 MCU, 20MHz XTAL
- Epson SED1502F, LCD screen (same as Saitek Simultano)
- piezo, 16+3 LEDs, button sensors chessboard

In 1992, it was also sold by Tandy as Chess Champion 2150L, still manufactured
by Saitek. Overall, the hardware is the same, but with a slower CPU (16MHz XTAL).

TODO:
- are older versions of Prisma on PROM H8/325 just like with Blitz?

*******************************************************************************/

#include "emu.h"

#include "cpu/h8/h8325.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"
#include "video/sed1500.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "saitek_prisma.lh"


namespace {

class prisma_state : public driver_device
{
public:
	prisma_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_led_pwm(*this, "led_pwm"),
		m_lcd_pwm(*this, "lcd_pwm"),
		m_lcd(*this, "lcd"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_out_lcd(*this, "s%u.%u", 0U, 0U)
	{ }

	void prisma(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(go_button);
	DECLARE_INPUT_CHANGED_MEMBER(change_cpu_freq);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<h8325_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_led_pwm;
	required_device<pwm_display_device> m_lcd_pwm;
	required_device<sed1502_device> m_lcd;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<4> m_inputs;
	output_finder<16, 34> m_out_lcd;

	u8 m_lcd_data = 0;
	u8 m_lcd_address = 0;
	u8 m_lcd_control = 0;
	u8 m_inp_mux = 0;
	u8 m_led_select = 0;
	u8 m_led_direct = 0;

	// I/O handlers
	void lcd_pwm_w(offs_t offset, u8 data);
	void lcd_output_w(offs_t offset, u64 data);

	void standby(int state);
	void update_leds();

	void p1_w(u8 data);
	void p2_w(u8 data);
	u8 p3_r();
	void p3_w(u8 data);
	void p4_w(u8 data);
	u8 p5_r();
	void p5_w(u8 data);
	void p6_w(u8 data);
	u8 p7_r();
};

void prisma_state::machine_start()
{
	m_out_lcd.resolve();

	// register for savestates
	save_item(NAME(m_lcd_data));
	save_item(NAME(m_lcd_address));
	save_item(NAME(m_lcd_control));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_led_select));
	save_item(NAME(m_led_direct));
}

INPUT_CHANGED_MEMBER(prisma_state::change_cpu_freq)
{
	// 12MHz and 24MHz versions don't exist, but the software supports it
	static const XTAL freq[4] = { 16_MHz_XTAL, 20_MHz_XTAL, 24_MHz_XTAL, 12_MHz_XTAL };
	m_maincpu->set_unscaled_clock(freq[bitswap<2>(newval,7,0)]);
}



/*******************************************************************************
    I/O
*******************************************************************************/

// power

void prisma_state::standby(int state)
{
	if (state)
	{
		// clear display
		for (int i = 0; i < 0x80; i++)
			m_lcd->write(i, 0);

		m_led_pwm->clear();
		m_lcd_pwm->clear();
	}
}

INPUT_CHANGED_MEMBER(prisma_state::go_button)
{
	if (newval && m_maincpu->standby())
		m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
}


// LCD

void prisma_state::lcd_pwm_w(offs_t offset, u8 data)
{
	m_out_lcd[offset & 0x3f][offset >> 6] = data;
}

void prisma_state::lcd_output_w(offs_t offset, u64 data)
{
	if (!m_maincpu->standby())
		m_lcd_pwm->write_row(offset, data);
}


// misc

void prisma_state::update_leds()
{
	m_led_pwm->matrix_partial(0, 2, m_led_select, m_inp_mux);
	m_led_pwm->matrix_partial(2, 1, 1, m_led_direct);
}

void prisma_state::p1_w(u8 data)
{
	// P10-P13: direct leds
	m_led_direct = (data & 0xf) ^ 3;
	update_leds();

	// P14: speaker out
	m_dac->write(BIT(data, 4));

	// P16: ext power (no need to emulate it)
}

void prisma_state::p2_w(u8 data)
{
	// P20-P27: input mux, led data
	m_inp_mux = bitswap<8>(~data,7,6,5,4,0,3,1,2);
	update_leds();
}

u8 prisma_state::p3_r()
{
	// P30-P37: LCD data (never reads here)
	logerror("read from LCD\n");
	return 0xff;
}

void prisma_state::p3_w(u8 data)
{
	// P30-P37: LCD data
	m_lcd_data = bitswap<8>(data,3,4,5,6,7,0,1,2);
}

void prisma_state::p4_w(u8 data)
{
	// P40: LCD CS
	// P41: LCD RD
	// P42: LCD WR
	if (~data & m_lcd_control & 4 && ~data & 1)
		m_lcd->write(m_lcd_address, m_lcd_data);
	m_lcd_control = data;
}

u8 prisma_state::p5_r()
{
	u8 data = 0;

	// P50-P52: read buttons
	for (int i = 0; i < 3; i++)
		if (m_inp_mux & m_inputs[i]->read())
			data |= 1 << i;

	// P53: battery status
	data |= m_inputs[3]->read() << 3;

	return ~data | 0xf0;
}

void prisma_state::p5_w(u8 data)
{
	// P54,P55: led select
	m_led_select = ~data >> 4 & 3;
	update_leds();
}

void prisma_state::p6_w(u8 data)
{
	// P60-P66: LCD address
	m_lcd_address = data & 0x7f;
}

u8 prisma_state::p7_r()
{
	u8 data = 0;

	// P70-P77: read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_file(i);

	return bitswap<8>(~data,6,5,3,2,0,1,7,4);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( prisma )
	PORT_START("IN.0")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Swap Side")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("New Game")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_CONFNAME( 0x81, 0x01, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prisma_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x81, "12MHz (unofficial)" )
	PORT_CONFSETTING(    0x00, "16MHz (Chess Champion 2150L)" )
	PORT_CONFSETTING(    0x01, "20MHz (Prisma)" )
	PORT_CONFSETTING(    0x80, "24MHz (unofficial)" )

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Normal")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Tab / Color")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Stop")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Analysis")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Info")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Function")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Play")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("Sound")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Set Up")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_EQUALS) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")

	PORT_START("IN.3")
	PORT_CONFNAME( 0x01, 0x01, "Battery Status" )
	PORT_CONFSETTING(    0x00, "Low" )
	PORT_CONFSETTING(    0x01, DEF_STR( Normal ) )

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prisma_state::go_button), 0) PORT_NAME("Go")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void prisma_state::prisma(machine_config &config)
{
	// basic machine hardware
	H8325(config, m_maincpu, 20_MHz_XTAL);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->nvram_set_default_value(~0);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(h8325_device::nvram_set_battery));
	m_maincpu->standby_cb().append(FUNC(prisma_state::standby));
	m_maincpu->write_port1().set(FUNC(prisma_state::p1_w));
	m_maincpu->write_port2().set(FUNC(prisma_state::p2_w));
	m_maincpu->read_port3().set(FUNC(prisma_state::p3_r));
	m_maincpu->write_port3().set(FUNC(prisma_state::p3_w));
	m_maincpu->read_port4().set_constant(0xff);
	m_maincpu->write_port4().set(FUNC(prisma_state::p4_w));
	m_maincpu->read_port5().set(FUNC(prisma_state::p5_r));
	m_maincpu->write_port5().set(FUNC(prisma_state::p5_w));
	m_maincpu->write_port6().set(FUNC(prisma_state::p6_w));
	m_maincpu->read_port7().set(FUNC(prisma_state::p7_r));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	SED1502(config, m_lcd, 32768).write_segs().set(FUNC(prisma_state::lcd_output_w));
	PWM_DISPLAY(config, m_lcd_pwm).set_size(16, 34);
	m_lcd_pwm->set_refresh(attotime::from_hz(30));
	m_lcd_pwm->output_x().set(FUNC(prisma_state::lcd_pwm_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(873/2, 1080/2);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_led_pwm).set_size(2+1, 8);
	config.set_default_layout(layout_saitek_prisma);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( prisma )
	ROM_REGION16_BE( 0x8000, "maincpu", 0 )
	ROM_LOAD("90_saitek_86051150st9_3258l02p.u1", 0x0000, 0x8000, CRC(b6f8384f) SHA1(a4e8a4a45009c15bda1778512a87dea756aae6d8) )

	ROM_REGION( 795951, "screen", 0 )
	ROM_LOAD("simultano.svg", 0, 795951, CRC(ac9942bb) SHA1(f9252e5bf7b8af698a403c3f8f5ea9e475e0bf0b) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1990, prisma, 0,      0,      prisma,  prisma, prisma_state, empty_init, "Saitek / Heuristic Software", "Kasparov Prisma", MACHINE_SUPPORTS_SAVE )



pro80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco, Robbbert
/******************************************************************************************************

Protec Pro-80

2009-12-09 Skeleton driver.

TODO:
- Cassette load (last byte is bad)
- Use messram for optional ram
- Fix Step command (doesn't work due to missing interrupt emulation)

The cassette uses 2 bits for input, plus a D flipflop and a 74LS221 oneshot, with the pulse width set
  by 0.02uf and 24k (= 336 usec). The pulse is started by a H->L transistion of the input. At the end
  of the pulse, the current input is passed through the flipflop.

Cassette start address is always 1000. The end address must be entered into 13DC/DD (little-endian).
Then press W to save. To load, press L. If it says r at the end, it indicates a bad checksum.

******************************************************************************************************/


#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/timer.h"
#include "machine/z80pio.h"
#include "imagedev/cassette.h"
#include "speaker.h"
#include "video/pwm.h"

#include "pro80.lh"


namespace {

class pro80_state : public driver_device
{
public:
	pro80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cass(*this, "cassette")
		, m_io_keyboard(*this, "LINE%u", 0U)
		, m_display(*this, "display")
	{ }

	void pro80(machine_config &config);

private:
	void digit_w(u8 data);
	void segment_w(u8 data);
	u8 kp_r();
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	u8 m_digit_sel = 0U;
	u8 m_cass_in = 0U;
	u16 m_cass_data[4]{};
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_device<cassette_image_device> m_cass;
	required_ioport_array<6> m_io_keyboard;
	required_device<pwm_display_device> m_display;
};

TIMER_DEVICE_CALLBACK_MEMBER( pro80_state::kansas_r )
{
	m_cass_data[1]++;
	u8 cass_ws = (m_cass->input() > +0.03) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		m_cass_in = ((m_cass_data[1] < 12) ? 0x10 : 0); // get data bit
		m_cass_data[1] = 0;
		if (!cass_ws) m_cass_in |= 0x20;  // set sync bit
	}
}

void pro80_state::digit_w(u8 data)
{
	// --xx xxxx digit select
	// -x-- ---- cassette out
	// x--- ---- ???
	m_digit_sel = data & 0x3f;
	m_cass->output( BIT(data, 6) ? -1.0 : +1.0);
}

void pro80_state::segment_w(u8 data)
{
	if (m_digit_sel)
	{
		for (u8 i = 0; i < 6; i++)
			if (!BIT(m_digit_sel, i))
				m_display->matrix(1<<i, data);

		m_digit_sel = 0;
	}
}

u8 pro80_state::kp_r()
{
	u8 data = 0x0f;

	for (u8 i = 0; i < 6; i++)
		if (!BIT(m_digit_sel, i))
			data &= m_io_keyboard[i]->read();

	data |= m_cass_in;
	data |= 0xc0;
	m_cass_in = 0;
	return data;
}

void pro80_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x03ff).rom();
	map(0x1000, 0x13ff).ram();
	map(0x1400, 0x17ff).ram(); // 2nd RAM is optional
}

void pro80_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x40, 0x43).rw("pio", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x44, 0x47).r(FUNC(pro80_state::kp_r));
	map(0x48, 0x4b).w(FUNC(pro80_state::digit_w));
	map(0x4c, 0x4f).w(FUNC(pro80_state::segment_w));
}

/* Input ports */
static INPUT_PORTS_START( pro80 )
	PORT_START("LINE0")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("CR") PORT_CODE(KEYCODE_L)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("CW") PORT_CODE(KEYCODE_W)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("SSt") PORT_CODE(KEYCODE_S)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Res") PORT_CODE(KEYCODE_EQUALS)
	PORT_START("LINE1")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("EXE") PORT_CODE(KEYCODE_ENTER)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("NEx") PORT_CODE(KEYCODE_N) PORT_CODE(KEYCODE_UP)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("REx") PORT_CODE(KEYCODE_R)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("MEx") PORT_CODE(KEYCODE_M)
	PORT_START("LINE2")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("7 [IY]") PORT_CODE(KEYCODE_7)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
	PORT_START("LINE3")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("6 [IX]") PORT_CODE(KEYCODE_6)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
	PORT_START("LINE4")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("5 [PC]") PORT_CODE(KEYCODE_5)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("9 [H]") PORT_CODE(KEYCODE_9)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
	PORT_START("LINE5")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("4 [SP]") PORT_CODE(KEYCODE_4)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("8 [L]") PORT_CODE(KEYCODE_8)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
INPUT_PORTS_END

void pro80_state::machine_reset()
{
	m_digit_sel = 0;
	m_cass_in = 0;
}

void pro80_state::machine_start()
{
	save_item(NAME(m_digit_sel));
	save_item(NAME(m_cass_in));
	save_item(NAME(m_cass_data));
}

void pro80_state::pro80(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'000'000) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &pro80_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &pro80_state::io_map);

	/* video hardware */
	config.set_default_layout(layout_pro80);
	PWM_DISPLAY(config, m_display).set_size(6, 8);
	m_display->set_segmask(0x3f, 0xff);

	Z80PIO(config, "pio", XTAL(4'000'000) / 2);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	// Cassette
	CASSETTE(config, m_cass);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	TIMER(config, "kansas_r").configure_periodic(FUNC(pro80_state::kansas_r), attotime::from_hz(40000)); // cass read
}

/* ROM definition */
ROM_START( pro80 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	// This rom dump is taken out of manual for this machine
	ROM_LOAD( "pro80.bin", 0x0000, 0x0400, CRC(1bf6e0a5) SHA1(eb45816337e08ed8c30b589fc24960dc98b94db2))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY   FULLNAME  FLAGS
COMP( 1981, pro80, 0,      0,      pro80,   pro80, pro80_state, empty_init, "Protec", "Pro-80", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



prodigy.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap, Joakim Larsson Edstrom
/*******************************************************************************

Applied Concepts Destiny Prodigy

The chess engine is Morphy, which in turn is based on Sargon 2.5.

Not counting The Mate, a chess game with chessboard peripheral for the Apple II,
this is the only known Destiny series chesscomputer. ACI also announced Destiny
Laser Chess, but it was never released.

********************************************************************************

PCB notes:

  +-----------------------------------------------------------------------------------+
  |LEDS------------------------------------------+        +-----------------+         |
  | | |             o o o o o o o o COLCN        |        |                 |         |
  | V |                                          |        | 4 char BCD LED  |         |
  | O | 8                                        |        +-----------------+         |
  |   |                                    ROWCN |        |||||||||||||||||||  ____   |
  | O | 7                                       o|+----------------------+    /    \  |
  |   |                                         o||  VIA                 |   ( beep ) |
  | O | 6                                       o|| R6522                |    \____/  |
  |   |                                         o|+----------------------+            |
  | O | 5                                       o|+----------------------+            |
  |   |                                         o||  CPU                 |            |
  | O | 4        8 x 8 button matrix            o|| R6502-13             |   +--+  __ |
  |   |                                         o|+----------------------+   |74| PWR||
  | O | 3                                       o|   o    o  +-------------+ |LS| SW >|
  |   |                                          | +=======+ |  ROM        | |14|  __||
  | O | 2                                        | | 2MHz  | | R2912       | +--+     |
  |   |                                          | |  XTAL | +-------------+     +--+ |
  | O | 1                                        |++-------+ +-------------+     |74| |
  |   |                                          || 74145N | |  RAM        |     |LS| |
  |   |   A    B    C    D    E    F    G    H   |+--------+ | M58725P     |     |00| |
  |   +------------------------------------------+           +-------------+     +--+ |
  |LEDS-> O    O    O    O    O    O    O    O                OOOOOOOOOOOO KPDCN      |
  +-----------------------------------------------------------------------------------+

  Tracing the image shows that VIA Port A is used on the ROWCN and Port B on COLCN

  The VIA pins CB1 and CB2 together with PB0 and PB1 via the 74145 drives the BCD display.
  The BCD display is of common anode type and each anode a0-a3 selects which digit that
  will be lit selected by PB0 and PB1. The segments to be lit is selected by the 8 bits
  shifted into the 74164 by clock from VIA CB1 and data from CB2.

Behind the BCD display we find the following supporting circuit:

                                                                 4x7 segment BCD display
                                        +---+       +-----+          +---+---+---+---+
  +-----------------+            CB1    |74 |==/4/=>|2x   |==/8/====>| 0   1   2   3 |
  |                 |       VIA  CB2    |164|==/4/=>|75491| segments |               |
  | 4 char BCD LED  |  ===> 6522        +---+       +-----+          +---+---+---+---+
  +-----------------+            PB1--->|74 |                          |   |   |   |
  |||||||||||||||||||            PB2--->|145|=/4/=/R/=>b(4x    )c=/4/==============>
                                        +---+           (PN2907)e=+     anodes
                                                                  |+5v

The keypad is connected to the 12 pin KPDCN connector left to right KP1:

  Pin #: KP1 KP2 KP3 KP4 KP5 KP6 KP7 KP8 KP9 KP10 KP11 K12
  VIA  :     PB4 PB5 PA0 PA1 PA2 PA3 PA4 PA5 PA6       PA7
  74145:  Q8                                       Q9      - used to decode/ground one half of the KPAD at a time

*******************************************************************************/

#include "emu.h"

#include "cpu/m6502/m6502.h"
#include "machine/6522via.h"
#include "machine/sensorboard.h"
#include "video/pwm.h"
#include "sound/dac.h"

#include "speaker.h"

// internal artwork
#include "aci_prodigy.lh"


namespace {

class prodigy_state : public driver_device
{
public:
	prodigy_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_via(*this, "via"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void prodigy(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<via6522_device> m_via;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_bit_interface> m_dac;
	required_ioport_array<2> m_inputs;

	u8 m_select = 0;
	u8 m_led_data = 0;
	u8 m_shift_data = 0;
	u8 m_shift_clock = 0;

	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_display();
	u8 input1_r();
	u8 input2_r();
	void control_w(u8 data);

	void shift_clock_w(int state);
	void shift_data_w(int state);
};

void prodigy_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_select));
	save_item(NAME(m_led_data));
	save_item(NAME(m_shift_data));
	save_item(NAME(m_shift_clock));
}



/*******************************************************************************
    I/O
*******************************************************************************/

// 6522 ports

void prodigy_state::update_display()
{
	// 4 7seg leds via 74145 Q0-Q3 + leds via Q4,Q5
	m_display->matrix(1 << m_select, m_led_data);
}

void prodigy_state::shift_clock_w(int state)
{
	// shift 8-bit led/digit data on rising edge
	if (state && !m_shift_clock)
	{
		m_led_data = m_led_data << 1 | (m_shift_data & 1);
		update_display();
	}

	m_shift_clock = state;
}

void prodigy_state::shift_data_w(int state)
{
	m_shift_data = state;
}

u8 prodigy_state::input1_r()
{
	u8 data = 0;

	// PA0-PA7: multiplexed inputs
	// read chessboard sensors via 74145 Q0-Q7
	if (m_select < 8)
		data = m_board->read_file(m_select);

	// read keypad(high) via 74145 Q8,Q9
	else if (m_select == 8 || m_select == 9)
		data = m_inputs[m_select - 8]->read() >> 2 & 0xff;

	return ~data;
}

u8 prodigy_state::input2_r()
{
	u8 data = 0;

	// PB4,PB5: keypad(low) via 74145 Q8,Q9
	if (m_select == 8 || m_select == 9)
		data = (m_inputs[m_select - 8]->read() & 3) << 4;

	// PB6: ?

	return ~data;
}

void prodigy_state::control_w(u8 data)
{
	// PB0-PB3: 74145
	m_select = data & 0xf;
	update_display();

	// PB7: speaker out
	m_dac->write(BIT(data, 7));
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void prodigy_state::main_map(address_map &map)
{
	map.global_mask(0x7fff);
	map(0x0000, 0x07ff).ram();
	map(0x2000, 0x200f).m(m_via, FUNC(via6522_device::map));
	map(0x6000, 0x7fff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( prodigy )
	PORT_START("IN.0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Go")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("A 1 / Pawn")
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("D 4 / Rook")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("G 7")
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Restore")
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CE") // clear entry
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Change Board")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("Verify")
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")

	PORT_START("IN.1")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Black")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("B 2 / Knight")
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("E 5 / Queen")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("H 8")
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Halt / Hint")
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Audio")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Time / Number")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("F 6 / King")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("C 3 / Bishop")
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("White")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void prodigy_state::prodigy(machine_config &config)
{
	// basic machine hardware
	M6502(config, m_maincpu, 2_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &prodigy_state::main_map);

	MOS6522(config, m_via, 2_MHz_XTAL); // DDRA = 0x00, DDRB = 0x8f
	m_via->readpa_handler().set(FUNC(prodigy_state::input1_r));
	m_via->readpb_handler().set(FUNC(prodigy_state::input2_r));
	m_via->writepb_handler().set(FUNC(prodigy_state::control_w));
	m_via->cb1_handler().set(FUNC(prodigy_state::shift_clock_w));
	m_via->cb2_handler().set(FUNC(prodigy_state::shift_data_w));
	m_via->irq_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(6, 8);
	m_display->set_segmask(0xf, 0xff);
	config.set_default_layout(layout_aci_prodigy);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( prodigy )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("007-707101", 0x6000, 0x2000, CRC(8d60345a) SHA1(fff18ff12e1b1be91f8eac1178605a682564eff2) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1982, prodigy, 0,      0,      prodigy, prodigy, prodigy_state, empty_init, "Applied Concepts", "Destiny Prodigy", MACHINE_SUPPORTS_SAVE )



prof180x.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/***************************************************************************

    PROF-180X

    07/07/2009 Skeleton driver.

****************************************************************************/

/*

    TODO:

    - memory banking
    - keyboard
    - floppy
    - floppy motor off timer
    - floppy index callback
    - RTC

*/


#include "emu.h"
#include "prof180x.h"

#include "cpu/z180/z180.h"
#include "imagedev/floppy.h"
#include "machine/74259.h"
#include "machine/ram.h"
#include "machine/upd765.h"
#include "screen.h"
#include "softlist_dev.h"

uint32_t prof180x_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}


uint8_t prof180x_state::read(offs_t offset)
{
	uint8_t data = 0;

	if (offset < 0x40000)
	{
	}
	else
	{
	}
/*
    switch ((m_mm1 << 1) | m_mm0)
    {
    case 0:
        // bank0_r = EPROM, bank0_w = RAM, bank1 = RAM
        break;

    case 1:
        // bank0_r = RAM, bank0_w = RAM, bank1 = RAM
        break;

    case 2:
        // bank0_r = UNMAP, bank0_w = UNMAP, bank1 = RAM
        break;

    case 3:
        // bank0_r = RAM, bank0_w = RAM, bank1 = UNMAP
        break;
    }
*/
	return data;
}

void prof180x_state::write(offs_t offset, uint8_t data)
{
	if (offset < 0x40000)
	{
	}
	else
	{
	}
}

void prof180x_state::c0_flag_w(int state)
{
	// C0 (DATA)
	m_c0 = state;
}

void prof180x_state::c1_flag_w(int state)
{
	// C1 (M0)
	m_c1 = state;
}

void prof180x_state::c2_flag_w(int state)
{
	// C2 (M1)
	m_c2 = state;
}

void prof180x_state::mini_flag_w(int state)
{
}

void prof180x_state::mm0_flag_w(int state)
{
	m_mm0 = state;
}

void prof180x_state::rtc_ce_w(int state)
{
}

void prof180x_state::peps_flag_w(int state)
{
}

void prof180x_state::mm1_flag_w(int state)
{
	m_mm1 = state;
}

uint8_t prof180x_state::status0_r()
{
	/*

	    bit     description

	    0       BUSY
	    1
	    2
	    3
	    4       B-E
	    5       IDX
	    6
	    7       MOT

	*/

	return 0;
}

uint8_t prof180x_state::status1_r()
{
	/*

	    bit     description

	    0       FREE
	    1
	    2
	    3
	    4       J18
	    5       J19
	    6
	    7       TDO

	*/

	return 0;
}

uint8_t prof180x_state::status_r(offs_t offset)
{
	return BIT(offset, 8) ? status1_r() : status0_r();
}

/* Address Maps */

void prof180x_state::prof180x_mem(address_map &map)
{
	map(0x00000, 0x0ffff).rom().region(HD64180_TAG, 0);
	map(0x4f000, 0x4ffff).ram();
	//map(0x0000, 0xffff).rw(FUNC(prof180x_state::read), FUNC(prof180x_state::write));
}

void prof180x_state::prof180x_io(address_map &map)
{
	map(0x0000, 0x003f).noprw(); // Z180 internal registers
	map(0x00d8, 0x00d8).mirror(0xff00).w("syslatch", FUNC(ls259_device::write_nibble_d0));
	map(0x00d9, 0x00d9).select(0xff00).r(FUNC(prof180x_state::status_r));
	map(0x00da, 0x00da).mirror(0xff00).rw(FDC9268_TAG, FUNC(upd765a_device::dma_r), FUNC(upd765a_device::dma_w));
	map(0x00db, 0x00db).mirror(0xff00).w("cent_data_out", FUNC(output_latch_device::write));
	map(0x00dc, 0x00dd).mirror(0xff00).m(FDC9268_TAG, FUNC(upd765a_device::map));
}

/* Input ports */

static INPUT_PORTS_START( prof180x )
INPUT_PORTS_END

/* Video */

static void prof180x_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
}

/*
static RTC8583_INTERFACE( rtc_intf )
{
    DEVCB_CPU_INPUT_LINE(HD64180_TAG, INPUT_LINE_INT2)
};
*/

void prof180x_state::machine_start()
{
	// register for state saving
	save_item(NAME(m_c0));
	save_item(NAME(m_c1));
	save_item(NAME(m_c2));
	save_item(NAME(m_mm0));
	save_item(NAME(m_mm1));
}

void prof180x_state::machine_reset()
{
}

void prof180x_state::prof180x(machine_config &config)
{
	/* basic machine hardware */
	z180_device &maincpu(HD64180RP(config, HD64180_TAG, 18.432_MHz_XTAL));
	maincpu.set_addrmap(AS_PROGRAM, &prof180x_state::prof180x_mem);
	maincpu.set_addrmap(AS_IO, &prof180x_state::prof180x_io);

	ls259_device &syslatch(LS259(config, "syslatch")); // Z41
	syslatch.q_out_cb<0>().set(FUNC(prof180x_state::c0_flag_w));
	syslatch.q_out_cb<1>().set(FUNC(prof180x_state::c1_flag_w));
	syslatch.q_out_cb<2>().set(FUNC(prof180x_state::c2_flag_w));
	syslatch.q_out_cb<3>().set(FUNC(prof180x_state::mini_flag_w));
	syslatch.q_out_cb<4>().set(FUNC(prof180x_state::mm0_flag_w));
	syslatch.q_out_cb<5>().set(FUNC(prof180x_state::rtc_ce_w));
	syslatch.q_out_cb<6>().set(FUNC(prof180x_state::peps_flag_w));
	syslatch.q_out_cb<7>().set(FUNC(prof180x_state::mm1_flag_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_screen_update(FUNC(prof180x_state::screen_update));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);

	/* devices */
	UPD765A(config, FDC9268_TAG, 8'000'000, false, true);
	FLOPPY_CONNECTOR(config, FDC9268_TAG ":0", prof180x_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, FDC9268_TAG ":1", prof180x_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, FDC9268_TAG ":2", prof180x_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, FDC9268_TAG ":3", prof180x_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats);

	//RTC8583(config, MK3835_TAG, rtc_intf);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");

	output_latch_device &latch(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(latch);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("128K").set_extra_options("256K,512K");

	/* software lists */
	SOFTWARE_LIST(config, "flop_list").set_original("prof180");
}

/* ROM definition */

ROM_START( prof180x )
	ROM_REGION( 0x10000, HD64180_TAG, 0 )
	ROM_SYSTEM_BIOS( 0, "pmon13", "pmon v1.3" )
	ROMX_LOAD( "pmon1_3.z16", 0x00000, 0x04000, CRC(32986688) SHA1(a6229d7e66ef699722ca3d41179fe3f1b75185d4), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "pmon14", "pmon v1.4" )
	ROMX_LOAD( "pmon1_4.z16", 0x00000, 0x04000, CRC(ed03f49f) SHA1(e016f9e0b89ab64c6203e2e46501d0b09f74ee9b), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "pmon15", "pmon v1.5" )
	ROMX_LOAD( "pmon1_5.z16", 0x00000, 0x04000, CRC(f43d185c) SHA1(a7a219b3d48c74602b3116cfcd34e44d6e7bc423), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "pmon", "pmon" )
	ROMX_LOAD( "pmon.z16",    0x00000, 0x04000, CRC(4f3732d7) SHA1(7dc27262db4e0c8f109470253b9364a216909f2c), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "eboot1", "eboot1" )
	ROMX_LOAD( "eboot1.z16",  0x00000, 0x08000, CRC(7a164b3c) SHA1(69367804b5cbc0633e3d7bbbcc256c2c8c9e7aca), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "eboot2", "eboot2" )
	ROMX_LOAD( "eboot2.z16",  0x00000, 0x08000, CRC(0c2d4301) SHA1(f1a4f457e287b19e14d8ccdbc0383f183d8a3efe), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6, "epmon1", "epmon1" )
	ROMX_LOAD( "epmon1.z16",  0x00000, 0x10000, CRC(27aabfb4) SHA1(41adf038c474596dbf7d387a1a7f33ed86aa7869), ROM_BIOS(6) )
	ROM_SYSTEM_BIOS( 7, "epmon2", "epmon2" )
	ROMX_LOAD( "epmon2.z16",  0x00000, 0x10000, CRC(3b8a7b59) SHA1(33741f0725e3eaa21c6881c712579b3c1fd30607), ROM_BIOS(7) )
	ROM_SYSTEM_BIOS( 8, "epmon3", "epmon3" )
	ROMX_LOAD( "epmon3.z16",  0x00000, 0x10000, CRC(51313af1) SHA1(60c293171a1c7cb9a5ff6d681e61894f44fddbd1), ROM_BIOS(8) )

	ROM_REGION( 0x157, "plds", 0 )
	ROM_LOAD( "pal14l8.z10", 0x000, 0x157, NO_DUMP )
ROM_END

ROM_START( prof181x )
	ROM_REGION( 0x10000, HD64180_TAG, 0 )
	ROM_SYSTEM_BIOS( 0, "pmon13", "pmon v1.3" )
	ROMX_LOAD( "pmon1_3.u13", 0x00000, 0x04000, CRC(32986688) SHA1(a6229d7e66ef699722ca3d41179fe3f1b75185d4), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "pmon14", "pmon v1.4" )
	ROMX_LOAD( "pmon1_4.u13", 0x00000, 0x04000, CRC(ed03f49f) SHA1(e016f9e0b89ab64c6203e2e46501d0b09f74ee9b), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "pmon15", "pmon v1.5" )
	ROMX_LOAD( "pmon1_5.u13", 0x00000, 0x04000, CRC(f43d185c) SHA1(a7a219b3d48c74602b3116cfcd34e44d6e7bc423), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "pmon", "pmon" )
	ROMX_LOAD( "pmon.u13",    0x00000, 0x04000, CRC(4f3732d7) SHA1(7dc27262db4e0c8f109470253b9364a216909f2c), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "eboot1", "eboot1" )
	ROMX_LOAD( "eboot1.u13",  0x00000, 0x08000, CRC(7a164b3c) SHA1(69367804b5cbc0633e3d7bbbcc256c2c8c9e7aca), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "eboot2", "eboot2" )
	ROMX_LOAD( "eboot2.u13",  0x00000, 0x08000, CRC(0c2d4301) SHA1(f1a4f457e287b19e14d8ccdbc0383f183d8a3efe), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6, "epmon1", "epmon1" )
	ROMX_LOAD( "epmon1.u13",  0x00000, 0x10000, CRC(27aabfb4) SHA1(41adf038c474596dbf7d387a1a7f33ed86aa7869), ROM_BIOS(6) )
	ROM_SYSTEM_BIOS( 7, "epmon2", "epmon2" )
	ROMX_LOAD( "epmon2.u13",  0x00000, 0x10000, CRC(3b8a7b59) SHA1(33741f0725e3eaa21c6881c712579b3c1fd30607), ROM_BIOS(7) )
	ROM_SYSTEM_BIOS( 8, "epmon3", "epmon3" )
	ROMX_LOAD( "epmon3.u13",  0x00000, 0x10000, CRC(51313af1) SHA1(60c293171a1c7cb9a5ff6d681e61894f44fddbd1), ROM_BIOS(8) )

	ROM_REGION( 0x157, "plds", 0 ) // converted from JED files
	ROM_LOAD( "pal20v8.u14", 0x000, 0x157, CRC(46da52b0) SHA1(c11362223c0d5c57c6ef970e66d674b89d8e7784) )
	ROM_LOAD( "pal20v8.u15", 0x000, 0x157, CRC(19fef936) SHA1(579ad23ee3c0b1c64c584383f9c8085c6ce3d094) )
	ROM_LOAD( "pal20v8.u19", 0x000, 0x157, CRC(69348c3b) SHA1(6eb8432660eb9b639a95b1973a54dab8b99f10ef) )
	ROM_LOAD( "pal20v8.u21", 0x000, 0x157, CRC(6df4e281) SHA1(602fa4637cd9356acc31b2adfb3a084fd5a0bfcb) )
ROM_END

/* Driver */

/*    YEAR  NAME      PARENT    COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY                 FULLNAME     FLAGS */
COMP( 1986, prof180x, 0,        0,      prof180x, prof180x, prof180x_state, empty_init, "Conitec Datensysteme", "PROF-180X", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1992, prof181x, prof180x, 0,      prof180x, prof180x, prof180x_state, empty_init, "Conitec Datensysteme", "PROF-181X", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



prof80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

    PROF-80 (Prozessor RAM-Floppy Kontroller)
    GRIP-1/2/3/4/5 (Grafik-Interface-Prozessor)
    UNIO-1 (?)

    http://www.prof80.de/
    http://oldcomputers.dyndns.org/public/pub/rechner/conitec/info.html

*/

/*

    TODO:

    - floppy Err on A: Select
    - NE555 timeout is 10x too high
    - grip31 does not work
    - UNIO card (Z80-STI, Z80-SIO, 2x centronics)
    - GRIP-COLOR (192kB color RAM)
    - GRIP-5 (HD6345, 256KB RAM)
    - XR color card

*/

#include "emu.h"
#include "prof80.h"
#include "softlist_dev.h"


//**************************************************************************
//  PERIPHERALS
//**************************************************************************

//-------------------------------------------------
//  motor -
//-------------------------------------------------

void prof80_state::motor(int mon)
{
	if (m_floppy[0]->get_device()) m_floppy[0]->get_device()->mon_w(mon);
	if (m_floppy[1]->get_device()) m_floppy[1]->get_device()->mon_w(mon);

	m_motor = mon;
}


void prof80_state::ready_w(int state)
{
	if (m_ready != state)
	{
		m_fdc->set_ready_line_connected(!state);
		m_fdc->ready_w(!state);
		m_ready = state;
	}
}


void prof80_state::inuse_w(int state)
{
	//m_floppy->inuse_w(state);
}


void prof80_state::motor_w(int state)
{
	if (state)
	{
		// trigger floppy motor off NE555 timer
		int t = 110 * RES_M(10) * CAP_U(6.8); // t = 1.1 * R8 * C6

		m_floppy_motor_off_timer->adjust(attotime::from_msec(t));
	}
	else
	{
		// turn on floppy motor
		motor(0);

		// reset floppy motor off NE555 timer
		m_floppy_motor_off_timer->adjust(attotime::never);
	}
}


void prof80_state::select_w(int state)
{
	if (m_select != state)
	{
		//m_fdc->set_select_lines_connected(state);
		m_select = state;
	}
}


void prof80_state::mini_w(int state)
{
	m_fdc->set_unscaled_clock(16_MHz_XTAL / (state ? 4 : 2));
}


void prof80_state::mstop_w(int state)
{
	if (!state)
	{
		// turn off floppy motor
		motor(1);

		// reset floppy motor off NE555 timer
		m_floppy_motor_off_timer->adjust(attotime::never);
	}
}


//-------------------------------------------------
//  flr_w - flag register
//-------------------------------------------------

void prof80_state::flr_w(uint8_t data)
{
	/*

	    bit     description

	    0       FB
	    1       SB0
	    2       SB1
	    3       SB2
	    4       SA0
	    5       SA1
	    6       SA2
	    7       FA

	*/

	m_flra->write_bit((data >> 4) & 0x07, BIT(data, 7));
	m_flrb->write_bit((data >> 1) & 0x07, BIT(data, 0));
}


//-------------------------------------------------
//  status_r -
//-------------------------------------------------

uint8_t prof80_state::status_r()
{
	/*

	    bit     signal      description

	    0       _RX
	    1
	    2
	    3
	    4       CTS
	    5       _INDEX
	    6
	    7       CTSP

	*/

	uint8_t data = 0;

	// serial receive
	data |= !m_rs232a->rxd_r();

	// clear to send
	data |= m_rs232a->cts_r() << 4;
	data |= m_rs232b->cts_r() << 7;

	// floppy index
	data |= (m_floppy[0]->get_device() ? m_floppy[0]->get_device()->idx_r() : m_floppy[1]->get_device() ? m_floppy[1]->get_device()->idx_r() : 1) << 5;

	return data;
}


//-------------------------------------------------
//  status2_r -
//-------------------------------------------------

uint8_t prof80_state::status2_r()
{
	/*

	    bit     signal      description

	    0       _MOTOR      floppy motor (0=on, 1=off)
	    1
	    2
	    3
	    4       JS4
	    5       JS5
	    6
	    7       _TDO

	*/

	uint8_t data = 0;
	int js4 = 0, js5 = 0;

	// floppy motor
	data |= m_motor;

	// JS4
	switch (m_j4->read())
	{
	case 0: js4 = 0; break;
	case 1: js4 = 1; break;
	case 2: js4 = !m_flra->q0_r(); break;
	case 3: js4 = !m_flra->q1_r(); break;
	case 4: js4 = !m_flra->q2_r(); break;
	}

	data |= js4 << 4;

	// JS5
	switch (m_j5->read())
	{
	case 0: js5 = 0; break;
	case 1: js5 = 1; break;
	case 2: js5 = !m_flra->q0_r(); break;
	case 3: js5 = !m_flra->q1_r(); break;
	case 4: js5 = !m_flra->q2_r(); break;
	}

	data |= js5 << 4;

	// RTC data
	data |= !m_rtc->data_out_r() << 7;

	return data;
}

// UNIO
/*
void prof80_state::unio_ctrl_w(uint8_t data)
{
//  int flag = BIT(data, 0);
    int flad = (data >> 1) & 0x07;

    switch (flad)
    {
    case 0: // CG1
    case 1: // CG2
    case 2: // _STB1
    case 3: // _STB2
    case 4: // _INIT
    case 5: // JSO0
    case 6: // JSO1
    case 7: // JSO2
        break;
    }
}
*/



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( prof80_mem )
//-------------------------------------------------

void prof80_state::prof80_mem(address_map &map)
{
	map(0x0000, 0xffff).m(m_mmu, FUNC(prof80_mmu_device::z80_program_map));
}


//-------------------------------------------------
//  ADDRESS_MAP( prof80_mmu )
//-------------------------------------------------

void prof80_state::prof80_mmu(address_map &map)
{
	map(0x40000, 0x5ffff).ram();
	map(0xc0000, 0xdffff).ram();
	map(0xf0000, 0xf1fff).mirror(0xe000).rom().region(Z80_TAG, 0);
}


//-------------------------------------------------
//  ADDRESS_MAP( prof80_io )
//-------------------------------------------------

void prof80_state::prof80_io(address_map &map)
{
	map(0x00, 0xd7).mirror(0xff00).rw(m_ecb, FUNC(ecbbus_device::io_r), FUNC(ecbbus_device::io_w));
//  map(0x80, 0x8f).mirror(0xff00).rw(UNIO_Z80STI_TAG, FUNC(z80sti_device::read), FUNC(z80sti_device::write));
//  map(0x94, 0x95).mirror(0xff00).rw(UNIO_Z80SIO_TAG, FUNC(z80sio_device::z80sio_d_r), FUNC(z80sio_device::z80sio_d_w)); // TODO: these methods don't exist anymore
//  map(0x96, 0x97).mirror(0xff00).rw(UNIO_Z80SIO_TAG, FUNC(z80sio_device::z80sio_c_r), FUNC(z80sio_device::z80sio_c_w)); // TODO: these methods don't exist anymore
//  map(0x9e, 0x9e).mirror(0xff00).w(FUNC(prof80_state::unio_ctrl_w));
//  map(0x9c, 0x9c).mirror(0xff00).w(UNIO_CENTRONICS1_TAG, FUNC(centronics_device::write));
//  map(0x9d, 0x9d).mirror(0xff00).w(UNIO_CENTRONICS1_TAG, FUNC(centronics_device::write));
//  map(0xc0, 0xc0).mirror(0xff00).r(FUNC(prof80_state::gripc_r));
//  map(0xc1, 0xc1).mirror(0xff00).rw(FUNC(prof80_state::gripd_r), FUNC(prof80_state::gripd_w));
	map(0xd8, 0xd8).mirror(0xff00).w(FUNC(prof80_state::flr_w));
	map(0xda, 0xda).mirror(0xff00).r(FUNC(prof80_state::status_r));
	map(0xdb, 0xdb).mirror(0xff00).r(FUNC(prof80_state::status2_r));
	map(0xdc, 0xdd).mirror(0xff00).m(m_fdc, FUNC(upd765a_device::map));
	map(0xde, 0xde).mirror(0x0001).select(0xff00).w(m_mmu, FUNC(prof80_mmu_device::par_w));
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( prof80 )
//-------------------------------------------------

static INPUT_PORTS_START( prof80 )
	PORT_START("J1")
	PORT_CONFNAME( 0x01, 0x00, "J1 RDY/HDLD")
	PORT_CONFSETTING( 0x00, "HDLD" )
	PORT_CONFSETTING( 0x01, "READY" )

	PORT_START("J2")
	PORT_CONFNAME( 0x01, 0x01, "J2 RDY/DCHG")
	PORT_CONFSETTING( 0x00, "DCHG" )
	PORT_CONFSETTING( 0x01, "READY" )

	PORT_START("J3")
	PORT_CONFNAME( 0x01, 0x00, "J3 Port Address")
	PORT_CONFSETTING( 0x00, "D8-DF" )
	PORT_CONFSETTING( 0x01, "E8-EF" )

	PORT_START("J4")
	PORT_CONFNAME( 0x07, 0x00, "J4 Console")
	PORT_CONFSETTING( 0x00, "GRIP-1" )
	PORT_CONFSETTING( 0x01, "V24 DUPLEX" )
	PORT_CONFSETTING( 0x02, "USER1" )
	PORT_CONFSETTING( 0x03, "USER2" )
	PORT_CONFSETTING( 0x04, "CP/M" )

	PORT_START("J5")
	PORT_CONFNAME( 0x07, 0x00, "J5 Baud")
	PORT_CONFSETTING( 0x00, "9600" )
	PORT_CONFSETTING( 0x01, "4800" )
	PORT_CONFSETTING( 0x02, "2400" )
	PORT_CONFSETTING( 0x03, "1200" )
	PORT_CONFSETTING( 0x04, "300" )

	PORT_START("J6")
	PORT_CONFNAME( 0x01, 0x01, "J6 Interrupt")
	PORT_CONFSETTING( 0x00, "Serial" )
	PORT_CONFSETTING( 0x01, "ECB" )

	PORT_START("J7")
	PORT_CONFNAME( 0x01, 0x01, "J7 DMA MMU")
	PORT_CONFSETTING( 0x00, "PROF" )
	PORT_CONFSETTING( 0x01, "DMA Card" )

	PORT_START("J8")
	PORT_CONFNAME( 0x01, 0x01, "J8 Active Mode")
	PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
	PORT_CONFSETTING( 0x01, DEF_STR( On ) )

	PORT_START("J9")
	PORT_CONFNAME( 0x01, 0x00, "J9 EPROM Type")
	PORT_CONFSETTING( 0x00, "2732/2764" )
	PORT_CONFSETTING( 0x01, "27128" )

	PORT_START("J10")
	PORT_CONFNAME( 0x03, 0x00, "J10 Wait States")
	PORT_CONFSETTING( 0x00, "On all memory accesses" )
	PORT_CONFSETTING( 0x01, "On internal memory accesses" )
	PORT_CONFSETTING( 0x02, DEF_STR( None ) )

	PORT_START("L1")
	PORT_CONFNAME( 0x01, 0x00, "L1 Write Polarity")
	PORT_CONFSETTING( 0x00, "Inverted" )
	PORT_CONFSETTING( 0x01, "Normal" )
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  upd765_interface fdc_intf
//-------------------------------------------------

static void prof80_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  motor_off - disable the floppy motor after
//  a delay
//-------------------------------------------------

TIMER_CALLBACK_MEMBER(prof80_state::motor_off)
{
	motor(1);
}


//-------------------------------------------------
//  machine_start
//-------------------------------------------------

void prof80_state::machine_start()
{
	// initialize RTC
	m_rtc->cs_w(1);
	m_rtc->oe_w(1);

	// create timer
	m_floppy_motor_off_timer = timer_alloc(FUNC(prof80_state::motor_off), this);

	// register for state saving
	save_item(NAME(m_motor));
	save_item(NAME(m_ready));
	save_item(NAME(m_select));
}



//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

//-------------------------------------------------
//  machine_config
//-------------------------------------------------

void prof80_state::prof80(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 6_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &prof80_state::prof80_mem);
	m_maincpu->set_addrmap(AS_IO, &prof80_state::prof80_io);

	// MMU
	PROF80_MMU(config, m_mmu, 0);
	m_mmu->set_addrmap(AS_PROGRAM, &prof80_state::prof80_mmu);

	// RTC
	UPD1990A(config, m_rtc);

	// FDC
	UPD765A(config, m_fdc, 16_MHz_XTAL / 2, true, true); // clocked through FDC9229B
	FLOPPY_CONNECTOR(config, UPD765_TAG ":0", prof80_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, UPD765_TAG ":1", prof80_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, UPD765_TAG ":2", prof80_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, UPD765_TAG ":3", prof80_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);

	// DEMUX latches
	LS259(config, m_flra);
	m_flra->q_out_cb<0>().set(m_rtc, FUNC(upd1990a_device::data_in_w)); // TDI
	m_flra->q_out_cb<0>().append(m_rtc, FUNC(upd1990a_device::c0_w)); // C0
	m_flra->q_out_cb<1>().set(m_rtc, FUNC(upd1990a_device::c1_w)); // C1
	m_flra->q_out_cb<2>().set(m_rtc, FUNC(upd1990a_device::c2_w)); // C2
	m_flra->q_out_cb<3>().set(FUNC(prof80_state::ready_w)); // READY
	m_flra->q_out_cb<4>().set(m_rtc, FUNC(upd1990a_device::clk_w)); // TCK
	m_flra->q_out_cb<5>().set(FUNC(prof80_state::inuse_w)); // IN USE
	m_flra->q_out_cb<6>().set(FUNC(prof80_state::motor_w)); // _MOTOR
	m_flra->q_out_cb<7>().set(FUNC(prof80_state::select_w)); // SELECT
	LS259(config, m_flrb);
	m_flrb->q_out_cb<0>().set(m_fdc, FUNC(upd765a_device::reset_w)); // RESF
	m_flrb->q_out_cb<1>().set(FUNC(prof80_state::mini_w)); // MINI
	m_flrb->q_out_cb<2>().set(m_rs232a, FUNC(rs232_port_device::write_rts)); // _RTS
	m_flrb->q_out_cb<3>().set(m_rs232a, FUNC(rs232_port_device::write_txd)); // TX
	m_flrb->q_out_cb<4>().set(FUNC(prof80_state::mstop_w)); // _MSTOP
	m_flrb->q_out_cb<5>().set(m_rs232b, FUNC(rs232_port_device::write_txd)); // TXP
	m_flrb->q_out_cb<6>().set(m_rtc, FUNC(upd1990a_device::stb_w)); // TSTB
	m_flrb->q_out_cb<7>().set(m_mmu, FUNC(prof80_mmu_device::mme_w)); // MME

	// ECB bus
	ECBBUS(config, m_ecb);
	ECBBUS_SLOT(config, "ecb_1", m_ecb, 1, ecbbus_cards, "grip21");
	ECBBUS_SLOT(config, "ecb_2", m_ecb, 2, ecbbus_cards, nullptr);
	ECBBUS_SLOT(config, "ecb_3", m_ecb, 3, ecbbus_cards, nullptr);
	ECBBUS_SLOT(config, "ecb_4", m_ecb, 4, ecbbus_cards, nullptr);
	ECBBUS_SLOT(config, "ecb_5", m_ecb, 5, ecbbus_cards, nullptr);

	// V24
	RS232_PORT(config, m_rs232a, default_rs232_devices, nullptr);
	RS232_PORT(config, m_rs232b, default_rs232_devices, nullptr);

	// internal ram
	RAM(config, RAM_TAG).set_default_size("128K");

	// software lists
	SOFTWARE_LIST(config, "flop_list").set_original("prof80");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( prof80 )
//-------------------------------------------------

ROM_START( prof80 )
	ROM_REGION( 0x2000, Z80_TAG, 0 )
	ROM_DEFAULT_BIOS( "v17" )
	ROM_SYSTEM_BIOS( 0, "v15", "v1.5" )
	ROMX_LOAD( "prof80v15.z7", 0x0000, 0x2000, CRC(8f74134c) SHA1(83f9dcdbbe1a2f50006b41d406364f4d580daa1f), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v16", "v1.6" )
	ROMX_LOAD( "prof80v16.z7", 0x0000, 0x2000, CRC(7d3927b3) SHA1(bcc15fd04dbf1d6640115be595255c7b9d2a7281), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v17", "v1.7" )
	ROMX_LOAD( "prof80v17.z7", 0x0000, 0x2000, CRC(53305ff4) SHA1(3ea209093ac5ac8a5db618a47d75b705965cdf44), ROM_BIOS(2) )
ROM_END



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   STATE         INIT        COMPANY                 FULLNAME    FLAGS
COMP( 1984, prof80,  0,      0,      prof80,  prof80, prof80_state, empty_init, "Conitec Datensysteme", "PROF-80",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



professor.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

CXG Sphinx Chess Professor (CXG-243)

NOTE: Before exiting MAME, press the OFF button to turn the power off. Otherwise,
NVRAM won't save properly.

The chess engine is by Frans Morsch, similar to the one in Mephisto Europa.
For some reason, they've put the row LEDs on the right instead of on the left.

Hardware notes:
- PCB label: 243 600 001
- Hitachi HD63B01Y0P, 8MHz XTAL
- Sanyo LC7580, LCD with 5 7segs and custom segments
- 8*8 chessboard buttons, 16 LEDs, piezo

*******************************************************************************/

#include "emu.h"

#include "cpu/m6800/m6801.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/lc7580.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "cxg_professor.lh"


namespace {

class professor_state : public driver_device
{
public:
	professor_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_lcd(*this, "lcd"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_out_lcd(*this, "s%u.%u", 0U, 0U)
	{ }

	void professor(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(on_button);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<hd6301y0_cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<lc7580_device> m_lcd;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<3> m_inputs;
	output_finder<2, 52> m_out_lcd;

	u16 m_inp_mux = 0;

	// I/O handlers
	void lcd_output_w(offs_t offset, u64 data);
	template <int N> void leds_w(u8 data);
	void control_w(u8 data);
	u8 input_r();
	void board_w(u8 data);
};

void professor_state::machine_start()
{
	m_out_lcd.resolve();

	// register for savestates
	save_item(NAME(m_inp_mux));
}



/*******************************************************************************
    I/O
*******************************************************************************/

INPUT_CHANGED_MEMBER(professor_state::on_button)
{
	// standby check actually comes from P27 high-impedance state
	if (newval && m_maincpu->standby())
		m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
}

void professor_state::lcd_output_w(offs_t offset, u64 data)
{
	for (int i = 0; i < 52; i++)
		m_out_lcd[offset][i] = BIT(data, i);
}

template <int N>
void professor_state::leds_w(u8 data)
{
	// P10-P17, P40-P47: leds (direct)
	m_display->write_row(N, ~data);
}

void professor_state::control_w(u8 data)
{
	// P20-P22: input mux (buttons)
	m_inp_mux = (m_inp_mux & 0xff) | (~data << 8 & 0x700);

	// P23: speaker out
	m_dac->write(BIT(data, 3));

	// P24: LC7580 DATA
	// P25: LC7580 CLK
	// P26: LC7580 CE
	m_lcd->data_w(BIT(data, 4));
	m_lcd->clk_w(BIT(data, 5));
	m_lcd->ce_w(BIT(data, 6));
}

u8 professor_state::input_r()
{
	// P50-P57: multiplexed inputs
	u8 data = 0;

	// read buttons
	for (int i = 0; i < 3; i++)
		if (BIT(m_inp_mux, i + 8))
			data |= m_inputs[i]->read();

	// read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_file(i);

	return ~data;
}

void professor_state::board_w(u8 data)
{
	// P60-P67: input mux (chessboard)
	m_inp_mux = (m_inp_mux & 0x700) | (data ^ 0xff);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( professor )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("King")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Move")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_N) PORT_NAME("Reset")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("Position")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Color")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Sound")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Test")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Hint")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Monitor")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF)
	PORT_BIT(0xfe, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_ON) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(professor_state::on_button), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void professor_state::professor(machine_config &config)
{
	// basic machine hardware
	HD6301Y0(config, m_maincpu, 8_MHz_XTAL);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(hd6301y_cpu_device::nvram_set_battery));
	m_maincpu->standby_cb().append([this](int state) { if (state) m_display->clear(); });
	m_maincpu->standby_cb().append(m_lcd, FUNC(lc7580_device::inh_w));
	m_maincpu->out_p1_cb().set(FUNC(professor_state::leds_w<0>));
	m_maincpu->out_p4_cb().set(FUNC(professor_state::leds_w<1>));
	m_maincpu->out_p2_cb().set(FUNC(professor_state::control_w));
	m_maincpu->in_p5_cb().set(FUNC(professor_state::input_r));
	m_maincpu->out_p6_cb().set(FUNC(professor_state::board_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	LC7580(config, m_lcd, 0);
	m_lcd->write_segs().set(FUNC(professor_state::lcd_output_w));
	m_lcd->nvram_enable_backup(true);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/5, 921/5);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(2, 8);
	config.set_default_layout(layout_cxg_professor);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( scprof )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("1988_107_newcrest_hd6301y0j76p", 0x0000, 0x4000, CRC(681456c7) SHA1(99f8ab7369dbc2c93335affc38838295a8a2c5f3) )

	ROM_REGION( 61580, "screen", 0 )
	ROM_LOAD("scprof.svg", 0, 61580, CRC(6ebdf57c) SHA1(b0c01f1f251a569a2503a1053d7e676b1e6d9b0a) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY, FULLNAME, FLAGS
SYST( 1989, scprof, 0,      0,      professor, professor, professor_state, empty_init, "CXG Systems / Newcrest Technology", "Sphinx Chess Professor", MACHINE_SUPPORTS_SAVE )



prophet5.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:m1macrophage

/*
The Prophet 5 (aka Model 1000) is a digitally-controlled, 5-voice analog
synthesizer.

The firwmare, running on a Z80, is responsible for:
* Scanning the keyboard and buttons.
* Scanning the value of potentiometers.
* Driving LEDs and digit displays.
* Reacting to inputs (footswitch, external CV and gate) and driving outputs
  (CV, gate).
* Cassette I/O.
* Voice control: setting control voltages (CVs) for voice parameters, asserting
  voice gates, and routing signals via CMOS switches.

The rest of the description is for the Prophet 5 Rev 3.0, which is the revision
being emulated. Most of this info should also apply to Revs 3.1, 3.2 and 3.3.

CVs are generated by DAC71-CSB-I, a 16-bit unipolar current output DAC. Some
units used the DAC71-CSB-V, which outputs a voltage instead. Only the 14 MSbits
of the DAC are utilized. Frequency CVs use the full 14 bits, whereas other CVs
just use the 7 MSbits. See update_vdac() for details.

CVs are routed to 38 sample & hold (S&H) circuits via CD4051 MUXes. There are 3
CVs for each of the 5 voices (3 x 5 = 15 CVs), controlling oscillator and filter
frequencies. The rest (23 CVs) are common to all voices. This setup results in a
1-part multitimbrality. See update_sh() for details.

There is no dedicated ADC chip. The knobs and external CV are scanned by routing
the knob (or input) voltages to a window comparator. Those voltages are compared
with the ADC reference, which is controlled by the firmware. See update_vmux()
and adc_r() for details.

TODO: Outline of voice architecture.

This driver is based on the Prophet 5 Rev 3.0 technical manual, and is intended
as an education tool.

There is no layout and no audio. Running with `-oslog -output console` will
display CVs, and voice and LED control signals.
*/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/7474.h"
#include "machine/pit8253.h"
#include "machine/nvram.h"
#include "machine/output_latch.h"
#include "machine/rescap.h"
#include "machine/timer.h"
#include "video/pwm.h"

#define LOG_SWITCHES (1U << 1)
#define LOG_CV       (1U << 2)
#define LOG_ADC      (1U << 3)

#define VERBOSE (LOG_GENERAL | LOG_CV)
//#define LOG_OUTPUT_FUNC osd_printf_info

#include "logmacro.h"

namespace {

class prophet5_state : public driver_device
{
public:
	static constexpr feature_type unemulated_features() { return feature::TAPE; }

	prophet5_state(const machine_config &mconfig, device_type type, const char *tag) ATTR_COLD;

	void prophet5rev30(machine_config &config) ATTR_COLD;

	DECLARE_INPUT_CHANGED_MEMBER(record_changed);
	DECLARE_INPUT_CHANGED_MEMBER(gate_in_changed);
	DECLARE_INPUT_CHANGED_MEMBER(pot_adjusted);
	DECLARE_INPUT_CHANGED_MEMBER(dac_trimmer_adjusted);
	DECLARE_INPUT_CHANGED_MEMBER(adc_trimmer_adjusted);
	DECLARE_INPUT_CHANGED_MEMBER(cv_in_changed);
	DECLARE_INPUT_CHANGED_MEMBER(seq_trimmer_adjusted);

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

private:
	static double i_bias(const required_ioport &rp, double rp_max, double r, double v);

	void switch_w(u8 data);
	u8 switch_r();
	u8 misc_r();
	u8 adc_r();

	void led_drive_w(u8 data);
	void led_sink_w(u8 data);
	void led_update_w(offs_t offset, u8 data);

	void update_sh();
	void update_vdac();
	void update_vmux();
	void mux_abc_w(u8 data);
	void sh_mux_inh_w(u8 data);
	void dac_w(offs_t offset, u8 data);
	void pot_mux_w(u8 data);

	void update_gate5();
	void latch_gate5_w(int state);
	void clr_int_w(u8 data);
	TIMER_DEVICE_CALLBACK_MEMBER(gate_in_delay_elapsed);

	void update_nvram_record();

	void memory_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;  // U311
	required_device<ttl7474_device> m_tune_ff;  // U322A
	required_device<timer_device> m_gate_in_delay;  // R311, C316, U331A
	required_device<pwm_display_device> m_led_matrix_pwm;
	required_device<pwm_display_device> m_digit_pwm;
	memory_view m_nvram_write_view;
	required_ioport_array<16> m_switches;
	required_ioport m_record;
	required_ioport m_release_footswitch;
	required_ioport m_gate_in;
	required_ioport m_gate_in_connected;
	required_ioport m_test_points;
	required_ioport_array<24> m_pots;
	required_ioport m_dac_gain;
	required_ioport m_adc_gain;
	required_ioport m_seq_cv_in;
	required_ioport m_seq_offset;
	required_ioport m_seq_scale;
	output_finder<> m_tune_mux_select;
	output_finder<> m_tuning;
	output_finder<> m_gate5;
	std::vector<std::vector<output_finder<>>> m_leds;

	u8 m_switch_row = 0;  // U212 input (CD4514 decoder).
	u8 m_mux_abc = 0;  // U338 (CD4174 latch): Q3, Q2, Q5 (MSbit to LSbit).
	u8 m_sh_mux_inh = 0x1f;  // U339 (CD4174): Q4, Q1, Q3, Q2, Q5.
	bool m_seq_cv_enabled = false;  // U339 (CD4174): Q0.
	u16 m_dac_latch = 0;  // 2 x CD4174 (U336, U337) + 2 x CD4013 (U342A, B).
	u8 m_pot_mux_abc = 0;  // U211 (CD4174): Q1, Q5, Q0 (MSbit to LSbit).
	u8 m_pot_mux_inh = 0x07;  // U211 (CD4174): Q3, Q2, Q4.
	double m_vdac = 0;
	double m_adc_vmux = 0;
	double m_adc_vref = 0;
	bool m_tune_counter_out = false;  // 8253 (U315) counter 0 output.
	bool m_latch_gate5 = false;  // U340 (CD4174) output Q4 (pin 12).
	bool m_ext_gate5 = false;  // U330B (CD4013) output Q (pin 13).
	std::array<double, 40> m_cv;

	static inline constexpr double VPLUS = 15.0;
	static inline constexpr double VMINUS = -15.0;
};

prophet5_state::prophet5_state(const machine_config &mconfig, device_type type, const char *tag)
	: driver_device(mconfig, type, tag)
	, m_maincpu(*this, "maincpu")
	, m_tune_ff(*this, "tune_ff")
	, m_gate_in_delay(*this, "gate_in_delay")
	, m_led_matrix_pwm(*this, "led_matrix_pwm")
	, m_digit_pwm(*this, "led_digit_pwm")
	, m_nvram_write_view(*this, "nvram_write_view")
	, m_switches(*this, "switch_row_%u", 0U)
	, m_record(*this, "record")
	, m_release_footswitch(*this, "release_footswitch")
	, m_gate_in(*this, "seq_gate_in")
	, m_gate_in_connected(*this, "gate_in_connected")
	, m_test_points(*this, "test_points")
	, m_pots(*this, "pot_%u", 0U)
	, m_dac_gain(*this, "trimmer_dac_gain")
	, m_adc_gain(*this, "trimmer_adc_gain")
	, m_seq_cv_in(*this, "seq_cv_in")
	, m_seq_offset(*this, "trimmer_seq_offset")
	, m_seq_scale(*this, "trimmer_seq_scale")
	, m_tune_mux_select(*this, "tune_mux_select")
	, m_tuning(*this, "tuning")
	, m_gate5(*this, "gate5")
{
	std::fill(m_cv.begin(), m_cv.end(), 0);

	static constexpr const char *LED_NAMES[8][5] =
	{
		{"osc_a_sqr",  "pmod_freq_a", "wmod_freq_a", "ps1", "record"},
		{"osc_a_saw",  "pmod_pw_a",   "wmod_freq_b", "ps2", "unused_1"},
		{"osc_a_sync", "pmod_filt",   "wmod_pw_a",   "ps3", "a_440"},
		{"osc_b_saw",  "lfo_saw",     "wmod_pw_b",   "ps4", "tune"},
		{"osc_b_tri",  "lfo_tri",     "wmod_filt",   "ps5", "to_cass"},
		{"osc_b_sqr",  "lfo_sqr",     "osc_b_lo",    "ps6", "from_cass"},
		{"osc_b_kbd",  "filt_kbd",    "unused_2",    "ps7", "unused_3"},
		{"unison",     "release",     "unused_4",    "ps8", "preset"},
	};

	for (int y = 0; y < 8; ++y)
	{
		m_leds.push_back(std::vector<output_finder<>>());
		for (int x = 0; x < 5; ++x)
			m_leds[y].push_back(output_finder<>(*this, std::string("led_") + LED_NAMES[y][x]));
	}
}

// Computes the current through resistor R, from the junction of the resistors
// towards V. Rp1 and Rp2 are the two sides of a single potentiometer.
//
//  V+ --- Rp1 --*-- Rp2 --- V-
//               |
//               R
//               |
//               V
double prophet5_state::i_bias(const required_ioport &rp, double rp_max, double r, double v)
{
	const double rp1 = rp_max * rp->read() / 100.0;
	const double rp2 = rp_max - rp1;
	// Compute voltage at the junction of all resistors.
	const double vx = (r * rp1 * VMINUS + r * rp2 * VPLUS + rp1 * rp2 * v) / (r * rp1 + r * rp2 + rp1 * rp2);
	return (vx - v) / r;
}

void prophet5_state::switch_w(u8 data)
{
	m_switch_row = data & 0x0f;
}

u8 prophet5_state::switch_r()
{
	const u8 pressed = m_switches[m_switch_row]->read();
	if (pressed)
		LOGMASKED(LOG_SWITCHES, "Switches - row: %d, pressed: %02x\n", m_switch_row, pressed);
	return pressed;
}

u8 prophet5_state::misc_r()
{
	const u8 d0 = 1;  // Cassette in. Will settle to 1 when there is no cassette input.
	const u8 d1 = BIT(m_release_footswitch->read(), 0);
	const u8 d2 = m_tune_ff->output_comp_r();
	const u8 d3 = BIT(m_gate_in_connected->read(), 0); // External gate enabled (input connected).
	const u8 d4 = BIT(m_record->read(), 0);  // Record enabled (NVRAM write protect disabled).
	const u8 d5 = m_tune_counter_out ? 1 : 0;
	return (d5 << 5) | (d4 << 4) | (d3 << 3) | (d2 << 2) | (d1 << 1) | d0;
}

u8 prophet5_state::adc_r()
{
	// The ADC consists of two comparators (U365C,D, LM339). A network of
	// resistors and diodes create the reference inputs to the two comparators,
	// by adding and subtracting 34mV to the ADC reference. The ADC reference is
	// nominally half the DAC output voltage.
	const u8 d0 = (m_adc_vmux < m_adc_vref - 0.034) ? 1 : 0;  // ADC LO
	const u8 d1 = (m_adc_vmux > m_adc_vref + 0.034) ? 1 : 0;  // ADC HI
	if (d0 || d1)
	{
		LOGMASKED(LOG_ADC, "ADC: Vmux: %f, Vref: %f - lo: %d,  hi: %d\n",
				  m_adc_vmux, m_adc_vref, d0, d1);
	}

	const u8 test_points = m_test_points->read();
	const u8 d2 = BIT(test_points, 1);  // TP301
	const u8 d3 = BIT(test_points, 4);  // TP304
	const u8 d4 = 0;  // Connected to GND.
	const u8 d5 = BIT(test_points, 6);  // TP306

	return (d5 << 5) | (d4 << 4) | (d3 << 3) | (d2 << 2) | (d1 << 1) | d0;
}

void prophet5_state::led_drive_w(u8 data)
{
	m_led_matrix_pwm->write_my(data);
	m_digit_pwm->write_my(data & 0x7f);
}

void prophet5_state::led_sink_w(u8 data)
{
	// The full LED matrix size is 8x7. Columns 0-4 control individual LEDs
	// (m_led_matrix_pwm), and columns 5 and 6 control the "bank" and "program"
	// 7-segment digit displays, respectively (m_digit_pwm). Only the first 7
	// rows are used for the digit displays.
	m_led_matrix_pwm->write_mx(data & 0x1f);
	m_digit_pwm->write_mx((data >> 5) & 0x03);
}

void prophet5_state::led_update_w(offs_t offset, u8 data)
{
	m_leds[offset & 0x3f][offset >> 6] = data;
}

void prophet5_state::update_sh()
{
	constexpr const char *CV_NAMES[40] =
	{
		"FILT ATTACK CV", "FILT DECAY CV", "FILT SUSTAIN CV", "FILT RELEASE CV",
		"AMP ATTACK CV", "AMP DECAY CV", "AMP SUSTAIN CV", "AMP RELEASE CV",
		"FILT CUTOFF CV", "FILT ENV AMT CC", "MIX OSC B CC", "OSC B PW CV",
		"MIX OSC A CC", "OSC A PW CV", "MIX NOISE CC", "FILT RESONANCE CV",
		"GLIDE CV", "LFO FREQ CV", "WMOD SRC MIX CV", "PMOD OSC B CC",
		"PMOD ENV AMT CC", "UNISON CV", "SEQ CV OUT", "NOT CONNECTED 1",
		"OSC 1A S/H", "OSC 1B S/H", "OSC 2A S/H", "OSC 2B S/H",
		"OSC 3A S/H", "OSC 3B S/H", "OSC 4A S/H", "OSC 4B S/H",
		"OSC 5A S/H", "OSC 5B S/H", "FILT 1 S/H", "FILT 2 S/H",
		"FILT 3 S/H", "FILT 4 S/H", "FILT 5 S/H", "NOT CONNECTED 2",
	};

	if ((m_sh_mux_inh & 0x1f) == 0x1f)
		return;  // Exit early if no S&H is selected.

	for (int i = 0; i < 5; ++i)
	{
		if (!BIT(m_sh_mux_inh, i))  // Active low.
		{
			const int cv_index = 8 * i + m_mux_abc;
			if (m_vdac != m_cv[cv_index])
			{
				m_cv[cv_index] = m_vdac;
				LOGMASKED(LOG_CV, "Set CV: %d - %s to %04x (%f V)\n",
						  cv_index, CV_NAMES[cv_index], m_dac_latch, m_vdac);
			}
		}
	}
}

void prophet5_state::update_vdac()
{
	// CVs are produced by DAC71-CSB-I, a 16-bit, unipolar, current output DAC.
	// Some units shipped with the DAC71-CSB-V (voltage output), with
	// corresponding changes to the DAC output buffer.

	constexpr double I_FS_NOMINAL = 1.99997e-3;  // From the datasheet.
	constexpr double DAC_RF = RES_K(5);  // Internal DAC71-CSB feedback resistor.

	// The contents of the DAC latch are inverted by U343, U344 and U345
	// (CD4049). While the DAC71 is a 16-bit DAC, only the 14 MSbits are used.
	// The 2 LSbits are pulled high.
	const u16 dac_input = (~m_dac_latch << 2) | 0x03;

	// Compute the full-scale (max) output current.
	const double i_fs = I_FS_NOMINAL + i_bias(m_dac_gain, RES_K(100), RES_K(100), 0);  // R333, R329
	// Adding i_bias() is a guess. There were no details found on the
	// quantitative effects of DAC Gain. But the implementation is qualitatively
	// correct: according to graphs on the datasheet, gain affects i_out
	// proportionally with scale. Furthermore, the adjustment range that results
	// from adding i_bias() (+/- ~0.78V) seems reasonable.

	// The DAC sinks its max current (i_fs) when the input is 0, and sinks no
	// current when the input is 0xffff.
	const double i_out = i_fs * (0xffff - dac_input) / double(0xffff);

	// The current is converted to a voltage by U347 (LF356 op-amp) and
	// surrounding resistors.
	m_vdac = i_out * (DAC_RF + RES_R(332));  // R330
	update_sh();

	// The DAC voltage is scaled down and used as a reference for the ADC. It
	// will be divided by ~2 if ADC Gain is properly calibrated.
	const double adc_gain = RES_K(5) * m_adc_gain->read() / 100.0;  // R334
	m_adc_vref = m_vdac * RES_VOLTAGE_DIVIDER(adc_gain + RES_K(18.2), RES_K(20.0));  // R335, R336
}

void prophet5_state::update_vmux()
{
	// The Vmux signal is one of the inputs to the ADC comparator. The other
	// one is the ADC reference (m_adc_vref).

	double vmux_sum = 0;
	int n_active_switches = 0;

	constexpr double POT_V_MAX = 5.0;
	for (int mux = 0; mux < 3; ++mux)
	{
		if (!BIT(m_pot_mux_inh, mux))  // Active low.
		{
			const int pot_index = 8 * mux + m_pot_mux_abc;
			vmux_sum += POT_V_MAX * m_pots[pot_index]->read() / 100.0;
			++n_active_switches;
		}
	}

	if (m_seq_cv_enabled)  // U371C (CD4016) control input.
	{
		// CV input is expected to be 0-10V.
		const double cv_in = 10.0 * m_seq_cv_in->read() / 100.0;

		// The external CV is buffered, scaled, and offsetted by U374A (LM348
		// op-amp) and surrounding circuit.

		// Scale resistor network.
		constexpr double R396 = RES_R(470);
		constexpr double R392 = RES_K(24.9);
		constexpr double R391 = RES_K(30.1);
		const double R386 = RES_K(10) * m_seq_scale->read() / 100.0;
		const double scaled_cv = cv_in * RES_VOLTAGE_DIVIDER(R396 + R392 + R386, R391);

		// Offset resistor network.
		constexpr double R389 = RES_K(1);
		constexpr double R390 = RES_M(1);
		constexpr double R385_MAX = RES_K(100);
		const double offset = R389 * i_bias(m_seq_offset, R385_MAX, R390, scaled_cv);

		vmux_sum += scaled_cv + offset;
		++n_active_switches;
	}

	// Each MUX output and the SEQ CV switch output have a 1K resistor to protect
	// from short circuits during startup. Under normal operation, only one
	// switch should be selected. If (buggy) firmware activates more than one,
	// the protection resistors will average out the voltages.

	if (n_active_switches > 0)
		m_adc_vmux = vmux_sum / n_active_switches;
	// Else, Vmux is floating. Happens transiently under normal operation.
	// Leaving m_adc_vmux unchanged when that happens.
}

void prophet5_state::mux_abc_w(u8 data)  // U338, CD4174 latch.
{
	// D0-D2: ABC inputs for all S&H and Tune MUXes.
	m_mux_abc = BIT(data, 0, 3);
	update_sh();

	// D3: -TUNE.
	m_tuning = BIT(data, 3) ? 0 : 1;

	// D4-D5: INH inputs of individual Tune MUXes.
	m_tune_mux_select = ~BIT(data, 4, 2) & 0x03;
}

void prophet5_state::sh_mux_inh_w(u8 data)  // U339, CD4174 latch.
{
	// D0-D4: INH inputs of individual S&H MUXes.
	m_sh_mux_inh = BIT(data, 0, 5);
	update_sh();

	// D5: EN SEQ CV
	m_seq_cv_enabled = BIT(data, 5);
	update_vmux();
}

void prophet5_state::pot_mux_w(u8 data)  // U211, CD4174 latch.
{
	m_pot_mux_abc = BIT(data, 0, 3);
	m_pot_mux_inh = BIT(data, 3, 3);
	update_vmux();
}

void prophet5_state::dac_w(offs_t offset, u8 data)
{
	if (offset == 0)  // Latch low 7 bits.
		m_dac_latch = (m_dac_latch & 0x3f80) | (data & 0x7f);
	else if (offset == 1)  // Latch high 7 bits.
		m_dac_latch = (u16(data & 0x7f) << 7) | (m_dac_latch & 0x007f);
	else
		assert(false);  // Should not happen.
	update_vdac();
}

void prophet5_state::update_gate5()
{
	// U321B (74LS02) -> U331F (CD4049)
	m_gate5 = (m_latch_gate5 || m_ext_gate5) ? 1 : 0;
}

void prophet5_state::latch_gate5_w(int state)
{
	m_latch_gate5 = bool(state);
	update_gate5();
}

void prophet5_state::clr_int_w(u8 data)
{
	// Flipflop U330A (4013) R input asserted, making /Q (-> /INT) = 1.
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);

	// Flipflop U330B (4013) S input asserted, making Q (-> m_ext_gate5) = 1.
	m_ext_gate5 = true;
	update_gate5();

	if (!BIT(m_gate_in->read(), 0))
	{
		// In this case, U330B input R is also asserted. While both R and S are
		// asserted, Q will be 1. But S is only asserted for the duration of the
		// I/O strobe. Once that ends, only R will be asserted, and Q will
		// transition to 0.
		// This situation should not occur, unless the firmware is misbehaving.
		m_ext_gate5 = false;
		update_gate5();
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(prophet5_state::gate_in_delay_elapsed)
{
	// Flipflop U330A (4013) is clocked with D = 1, making /Q (-> /INT) = 0.
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
}

void prophet5_state::update_nvram_record()
{
	m_nvram_write_view.select(BIT(m_record->read(), 0));
}

void prophet5_state::memory_map(address_map &map)
{
	map.global_mask(0x9fff);  // Z80 A13 and A14 are not connected.

	// Memory decoding done by U318 (74LS138).
	map(0x0000, 0x0bff).mirror(0x8000).rom();
	map(0x0c00, 0x0fff).readonly().share("nvram");  // 8 x 1K x 1bit RAMs (6508, U301-U308).
	map(0x1000, 0x13ff).mirror(0x8000).ram();  // 2 x 1K x 4bit RAMs (2114, U316, U317).
	map(0x1800, 0x1803).mirror(0x83fc).rw("tune_pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x8c00, 0x8fff).view(m_nvram_write_view);

	// The "record" switch write protects the NVRAM. When "record" is enabled,
	// U320C (74LS00) will assert RAM /WR when A15=1. When "record" is disabled,
	// /WR will not be asserted, which will result in an NVRAM read.
	m_nvram_write_view[0](0x8c00, 0x8fff).readonly().share("nvram");
	m_nvram_write_view[1](0x8c00, 0x8fff).writeonly().share("nvram");
}

void prophet5_state::io_map(address_map &map)
{
	// The signal names in the comments below (e.g. "CSI0, KBD/SW") match those
	// in the schematics.
	map.global_mask(0x3f);  // Only A0-A5 are used for port decoding.

	// Input port decoding done by U310 A,B,C (74LS00).
	map(0x01, 0x01).mirror(0x3e).r(FUNC(prophet5_state::switch_r));  // CSI0, KBD/SW
	map(0x02, 0x02).mirror(0x3d).r(FUNC(prophet5_state::misc_r));  // CSI1, CASS/MISC
	map(0x04, 0x04).mirror(0x3b).r(FUNC(prophet5_state::adc_r));  // CSI2, ADC

	// Output port decoding for TTL chips done by U319 (74LS138).
	map(0x00, 0x00).mirror(0x07).w(FUNC(prophet5_state::led_drive_w));  // CSOL0, LED DRVR
	map(0x08, 0x08).mirror(0x07).w(FUNC(prophet5_state::led_sink_w));  // CSOL1, LED SINK
	map(0x10, 0x10).mirror(0x07).w(FUNC(prophet5_state::switch_w));  // CSOL2, KBD/SW DRVR
	map(0x18, 0x18).mirror(0x07).w(FUNC(prophet5_state::pot_mux_w));  // CSOL3, POT MUX ADR
	map(0x20, 0x20).mirror(0x07).w("misc_latch", FUNC(output_latch_device::write));  // CSOL4, CASS/TUNE
	map(0x28, 0x28).mirror(0x07).w(FUNC(prophet5_state::clr_int_w));  // CSOL5, CLEAR INT

	// Output port decoding for 15V CMOS chips done by U329 (CD4556).
	map(0x30, 0x30).mirror(0x04).w("program_latch_0", FUNC(output_latch_device::write));  // CSOH0, PROG SW 0
	map(0x31, 0x31).mirror(0x04).w("program_latch_1", FUNC(output_latch_device::write));  // CSOH1, PROG SW 1
	map(0x32, 0x32).mirror(0x04).w("program_latch_2", FUNC(output_latch_device::write));  // CSOH2, PROG SW 2
	map(0x33, 0x33).mirror(0x04).w(FUNC(prophet5_state::mux_abc_w));  // CSOH3, S/H ABC/TUNE
	map(0x38, 0x38).mirror(0x04).w(FUNC(prophet5_state::sh_mux_inh_w));  // CSOH4, S/H
	map(0x39, 0x39).mirror(0x04).w("gate_latch", FUNC(output_latch_device::write));  // CSOH5, GATES
	map(0x3a, 0x3b).mirror(0x04).w(FUNC(prophet5_state::dac_w));  // CSOH6, DAC LSB - CSOH7, DAC MSB
}

void prophet5_state::machine_start()
{
	save_item(NAME(m_switch_row));
	save_item(NAME(m_mux_abc));
	save_item(NAME(m_sh_mux_inh));
	save_item(NAME(m_seq_cv_enabled));
	save_item(NAME(m_dac_latch));
	save_item(NAME(m_pot_mux_abc));
	save_item(NAME(m_pot_mux_inh));
	save_item(NAME(m_vdac));
	save_item(NAME(m_adc_vmux));
	save_item(NAME(m_adc_vref));
	save_item(NAME(m_tune_counter_out));
	save_item(NAME(m_latch_gate5));
	save_item(NAME(m_ext_gate5));
	save_item(NAME(m_cv));

	m_tune_mux_select.resolve();
	m_tuning.resolve();
	m_gate5.resolve();
	for (auto &led_row : m_leds)
		for (auto &led : led_row)
			led.resolve();
}

void prophet5_state::machine_reset()
{
	update_nvram_record();
}

void prophet5_state::prophet5rev30(machine_config &config)
{
	Z80(config, m_maincpu, 5_MHz_XTAL / 2);  // Divided by U325.
	m_maincpu->set_addrmap(AS_PROGRAM, &prophet5_state::memory_map);
	m_maincpu->set_addrmap(AS_IO, &prophet5_state::io_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	auto &pit = PIT8253(config, "tune_pit");  // U315
	pit.out_handler<0>().set("tune_pit", FUNC(pit8253_device::write_gate2)).invert();
	pit.out_handler<0>().append([this] (int state) { m_tune_counter_out = bool(state); });
	pit.set_clk<1>(5_MHz_XTAL / 2);
	pit.set_clk<2>(5_MHz_XTAL / 2);

	TTL7474(config, m_tune_ff, 0).comp_output_cb().set("tune_pit", FUNC(pit8253_device::write_clk0));

	TIMER(config, m_gate_in_delay).configure_generic(FUNC(prophet5_state::gate_in_delay_elapsed));

	PWM_DISPLAY(config, m_led_matrix_pwm).set_size(8, 5);
	m_led_matrix_pwm->output_x().set(FUNC(prophet5_state::led_update_w));

	PWM_DISPLAY(config, m_digit_pwm).set_size(2, 7);
	m_digit_pwm->set_segmask(0x03, 0x7f);

	auto &u332 = OUTPUT_LATCH(config, "misc_latch");
	u332.bit_handler<0>().set(m_tune_ff, FUNC(ttl7474_device::clear_w));
	u332.bit_handler<1>().set(m_tune_ff, FUNC(ttl7474_device::preset_w));
	u332.bit_handler<2>().set_output("cassette_out");
	u332.bit_handler<3>().set(m_tune_ff, FUNC(ttl7474_device::d_w));
	u332.bit_handler<4>().set("tune_pit", FUNC(pit8253_device::write_gate0));
	u332.bit_handler<5>().set("tune_pit", FUNC(pit8253_device::write_gate1));
	u332.bit_handler<5>().append_output("select_440");

	auto &u335 = OUTPUT_LATCH(config, "program_latch_0");
	u335.bit_handler<0>().set_output("osc_a_sqr");
	u335.bit_handler<1>().set_output("osc_a_saw");
	u335.bit_handler<2>().set_output("osc_a_sync");
	u335.bit_handler<3>().set_output("osc_b_saw");
	u335.bit_handler<4>().set_output("osc_b_tri");
	u335.bit_handler<5>().set_output("osc_b_sqr");

	auto &u334 = OUTPUT_LATCH(config, "program_latch_1");
	u334.bit_handler<0>().set_output("pmod_freq_a");
	u334.bit_handler<1>().set_output("pmod_pw_a");
	u334.bit_handler<2>().set_output("pmod_filt");
	u334.bit_handler<3>().set_output("lfo_saw");
	u334.bit_handler<4>().set_output("lfo_tri");
	u334.bit_handler<5>().set_output("lfo_sqr");

	auto &u333 = OUTPUT_LATCH(config, "program_latch_2");
	u333.bit_handler<0>().set_output("wmod_freq_a");
	u333.bit_handler<1>().set_output("wmod_freq_b");
	u333.bit_handler<2>().set_output("wmod_pw_a");
	u333.bit_handler<3>().set_output("wmod_pw_b");
	u333.bit_handler<4>().set_output("wmod_filt");
	u333.bit_handler<5>().set_output("osc_b_lo");

	auto &u340 = OUTPUT_LATCH(config, "gate_latch");
	u340.bit_handler<0>().set_output("gate1");
	u340.bit_handler<1>().set_output("gate2");
	u340.bit_handler<2>().set_output("gate3");
	u340.bit_handler<3>().set_output("gate4");
	u340.bit_handler<4>().set(FUNC(prophet5_state::latch_gate5_w));
	u340.bit_handler<5>().set_output("gate_out");
}

DECLARE_INPUT_CHANGED_MEMBER(prophet5_state::record_changed)
{
	update_nvram_record();
}

DECLARE_INPUT_CHANGED_MEMBER(prophet5_state::gate_in_changed)
{
	if (newval)
	{
		// An RC circuit adds a delay between receiving the gate-in signal and
		// asserting /INT. `DT` is the time it takes for the RC network to
		// discharge from 5V to 2.5V and trip the inverter (U331A, CD4049). This
		// is ~1.4ms nominal. The schematic says "2ms delay". The actual delay
		// is not well-specified, since it depends on the trip point of the
		// inverter.
		const double DT = -RES_K(100) * CAP_U(0.02) * log(2.5 / 5.0);  // R311, C316
		m_gate_in_delay->adjust(attotime::from_double(DT));
	}
	else
	{
		m_gate_in_delay->reset();
		m_ext_gate5 = false;  // Flipflop U330B (4013) R input asserted, making Q (-> m_ext_gate5) = 0.
		update_gate5();
	}
}

DECLARE_INPUT_CHANGED_MEMBER(prophet5_state::pot_adjusted)
{
	update_vmux();
}

DECLARE_INPUT_CHANGED_MEMBER(prophet5_state::dac_trimmer_adjusted)
{
	update_vdac();
}

DECLARE_INPUT_CHANGED_MEMBER(prophet5_state::adc_trimmer_adjusted)
{
	update_vdac();
}

DECLARE_INPUT_CHANGED_MEMBER(prophet5_state::cv_in_changed)
{
	update_vmux();
}

DECLARE_INPUT_CHANGED_MEMBER(prophet5_state::seq_trimmer_adjusted)
{
	update_vmux();
}

INPUT_PORTS_START(prophet5)
	PORT_START("switch_row_0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("OSC A SQR") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("OSC A SAW") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("OSC A SYNC")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("OSC B SAW")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("OSC B TRI")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("OSC B SQR")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("OSC B KBD")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("UNISON")

	PORT_START("switch_row_1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PMOD FREQ A")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PMOD PW A")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PMOD FILT")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("LFO SAW")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("LFO TRI")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("LFO SQR")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("FILT KBD")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("RELEASE")

	PORT_START("switch_row_2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("WMOD FREQ A")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("WMOD FREQ B")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("WMOD PW A")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("WMOD PW B")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("WMOD FILT")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("OSC B LO")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("switch_row_3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PS1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PS2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PS3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PS4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PS5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PS6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PS7") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PS8") PORT_CODE(KEYCODE_8)

	PORT_START("switch_row_4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("RECORD") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("BANK SELECT")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("A-440")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TUNE")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TO CASS")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("FROM CASS")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PRESET")

	PORT_START("switch_row_5")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("switch_row_6")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("switch_row_7")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("switch_row_8")  // C0 - G0 in schematic.
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C2 PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_CS2
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_D2
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_DS2
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_E2
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_F2
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_FS2
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_G2

	PORT_START("switch_row_9")  // G#0 - D#1
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_GS2
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_A2
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_AS2
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_B2
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C3 PORT_CODE(KEYCODE_X)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_CS3
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_D3
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_DS3

	PORT_START("switch_row_10")  // E1 - B1
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_E3
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_F3
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_FS3
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_G3
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_GS3
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_A3
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_AS3
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_B3

	PORT_START("switch_row_11")  // C2 - G2
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C4 PORT_CODE(KEYCODE_C)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_CS4
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_D4
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_DS4
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_E4
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_F4
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_FS4
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_G4

	PORT_START("switch_row_12")  // G#2 - D#3
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_GS4
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_A4
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_AS4
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_B4
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C5 PORT_CODE(KEYCODE_V)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_CS5
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_D4
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_DS4

	PORT_START("switch_row_13")  // E3 - B3
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_E5
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_F5
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_FS4
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_G5
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_GS5
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_A5
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_AS5
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_B5

	PORT_START("switch_row_14")  // C4 - G4
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C6 PORT_CODE(KEYCODE_B)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_CS6
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_D6
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_DS6
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_E6
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_F6
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_FS6
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_G6

	PORT_START("switch_row_15")  // G#4 - C5
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_GS6
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_A6
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_AS6
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_B6
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C7 PORT_CODE(KEYCODE_N)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	// NVRAM (patch memory) write protect switch on back panel. It electrically
	// disables writes to the NVRAM (blocks the /WR signal, look for
	// m_nvram_write_view), and its state can be read by the firmware (see
	// misc_r()).
	PORT_START("record")
	PORT_CONFNAME(0x01, 0x01, "RECORD EN DIS")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::record_changed), 0)
	PORT_CONFSETTING(0x00, "Disable")
	PORT_CONFSETTING(0x01, "Enable")

	PORT_START("gate_in_connected")
	PORT_CONFNAME(0x01, 0x00, "EXT GATE EN")
	PORT_CONFSETTING(0x00, "Not connected")
	PORT_CONFSETTING(0x01, "Connected")

	PORT_START("seq_gate_in")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("SEQ GATE IN") PORT_CODE(KEYCODE_G)
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::gate_in_changed), 0)

	PORT_START("release_footswitch")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("REL FT SW")

	PORT_START("test_points")
	// According to the schematic, TP301 and TP304 have pull-down resistors, and
	// TP306 does not have a resistor.
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TP301") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TP304")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TP306")

	// All knob potentiometers are 10K linear.

	PORT_START("pot_0")  // R217
	PORT_ADJUSTER(50, "GLIDE") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 0)

	PORT_START("pot_1")  // R211
	PORT_ADJUSTER(50, "LFO FREQ") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 1)

	PORT_START("pot_2")  // R216
	PORT_ADJUSTER(50, "WMOD SRC MIX") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 2)

	PORT_START("pot_3")  // R202
	PORT_ADJUSTER(50, "PMOD OSC B") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 3)

	PORT_START("pot_4")  // R201
	PORT_ADJUSTER(50, "PMOD FILT ENV") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 4)

	PORT_START("pot_5")  // R204
	PORT_ADJUSTER(50, "OSC A FREQ") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 5)

	PORT_START("pot_6")  // R213
	PORT_ADJUSTER(50, "OSC B FREQ") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 6)

	PORT_START("pot_7")  // R214
	PORT_ADJUSTER(50, "OSC B FINE") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 7)

	PORT_START("pot_8")  // R101
	PORT_ADJUSTER(50, "FILT CUTOFF") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 8)

	PORT_START("pot_9")  // R103
	PORT_ADJUSTER(50, "FILT ENV AMT") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 9)

	PORT_START("pot_10")  // R208
	PORT_ADJUSTER(50, "MIX OSC B") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 10)

	PORT_START("pot_11")  // R215
	PORT_ADJUSTER(50, "OSC B PW") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 11)

	PORT_START("pot_12")  // R207
	PORT_ADJUSTER(50, "MIX OSC A") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 12)

	PORT_START("pot_13")  // R205
	PORT_ADJUSTER(50, "OSC A PW") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 13)

	PORT_START("pot_14")  // R210
	PORT_ADJUSTER(50, "MIX NOISE") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 14)

	PORT_START("pot_15")  // R102
	PORT_ADJUSTER(50, "FILT RESONANCE") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 15)

	PORT_START("pot_16")  // R105
	PORT_ADJUSTER(50, "FILT ATTACK") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 16)

	PORT_START("pot_17")  // R106
	PORT_ADJUSTER(50, "FILT DECAY") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 17)

	PORT_START("pot_18")  // R107
	PORT_ADJUSTER(50, "FILT SUSTAIN") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 18)

	PORT_START("pot_19")  // R108
	PORT_ADJUSTER(50, "FILT RELEASE") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 19)

	PORT_START("pot_20")  // R109
	PORT_ADJUSTER(50, "AMP ATTACK") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 20)

	PORT_START("pot_21")  // R110
	PORT_ADJUSTER(50, "AMP DECAY") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 21)

	PORT_START("pot_22")  // R111
	PORT_ADJUSTER(50, "AMP SUSTAIN") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 22)

	PORT_START("pot_23")  // R112
	PORT_ADJUSTER(50, "AMP RELEASE") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::pot_adjusted), 23)

	PORT_START("trimmer_dac_gain")  // R333, 100K trimmer.
	// Default value based on calibration instructions, with a small error due
	// to adjuster resolution.
	PORT_ADJUSTER(50, "TRIMMER: DAC GAIN") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::dac_trimmer_adjusted), 0)

	PORT_START("trimmer_adc_gain")  // R334, 5K trimmer.
	// Default value calibrated for the required: Vadcref = Vdac / 2.
	PORT_ADJUSTER(36, "TRIMMER: ADC GAIN") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::adc_trimmer_adjusted), 0)

	PORT_START("seq_cv_in")  // J2, external CV input.
	PORT_ADJUSTER(50, "SEQ CV IN") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::cv_in_changed), 0)

	PORT_START("trimmer_seq_offset")  // R385, 100K trimmer.
	PORT_ADJUSTER(50, "TRIMMER: SEQ OFFSET") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::seq_trimmer_adjusted), 0)

	PORT_START("trimmer_seq_scale")  // R386, 10K trimmer.
	// Default value calibrated for ADC_CV_SEQ_IN = CV_SEQ_IN / 2, with some
	// error due to adjuster resolution. Exact calibration works out to 47.3.
	PORT_ADJUSTER(47, "TRIMMER: SEQ SCALE") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(prophet5_state::seq_trimmer_adjusted), 1)
INPUT_PORTS_END

ROM_START(prophet5rev30)
	ROM_REGION(0xc00, "maincpu", 0)  // 3 x 2708 1Kbyte ROMS.
	ROM_LOAD("0.v8.1.u312", 0x000000, 0x000400, CRC(6337d2ae) SHA1(bad79f6475dc0a8bb139ea0a12258cb3e5bfa0be))
	ROM_LOAD("1.v8.1.u313", 0x000400, 0x000400, CRC(1e334fd3) SHA1(276b7abf4a13fbae0d09e869f786b3073ee82504))
	ROM_LOAD("2.v8.1.u314", 0x000800, 0x000400, CRC(ffafaa95) SHA1(9d119fb22270d45e34c1f16899453ae7469d7d20))
ROM_END

}  // anonymous namespace

// Prophet 5 Rev 3.0, serial numbers 1301-2285.
SYST(1980, prophet5rev30, 0, 0, prophet5rev30, prophet5, prophet5_state, empty_init, "Sequential Circuits", "Prophet 5 (Model 1000) Rev 3.0", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE)



prophet600.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    prophet600.cpp - Sequential Circuits Prophet-600, designed by Dave Smith

    This was the first commercial synthesizer with built-in MIDI.

    Skeleton driver by R. Belmont

    Hardware:
        CPU: Z80 CPU + 8253 PIT + 6850 UART

    Memory map:
    0x0000-0x1fff: ROM
    0x2000-0x27ff: RAM 1
    0x3000-0x37ff: RAM 2
    0x4000-0x4001: DAC for CV/gate drive
    0x6000-0x6001: 6850 writes
    0xe000-0xe001: 6850 reads

    I/O map:
    00-07:  8253 PIT
    08: set row to scan for keyboard/panel input and LED / lamp output
    09: read: analog comparitor, some value vs. the DAC.  write: output for LEDs/lamps
        comparitor bits:
        bit 1: FFFStatus
        bit 2: output of 8253 channel 2
        bit 3: ADCCompare: returns sign of if current value is > or < the DAC value

    0a: read: keyboard/panel input scan bits, write: select a pot on the panel for the comparitor
    0b: write: update all gate signals
    0c: ???
    0d: write: select which CV signal will be updated with the current DAC value
    0e: write: mask, masks
        bit 0: FFFP -FF P
        bit 1: mask gate from 8253
        bit 3: FFFD FF D
        bit 4: FFFCL -FF CL


    Info:
    - Prophet 600 Technical Manual

    - https://github.com/gligli/p600fw - GPLv3 licensed complete replacement firmware
    for the Prophet 600, but written in C and runs on an AVR that replaces the original
    Z80.

***************************************************************************/

#include "emu.h"
#include "machine/clock.h"
#include "cpu/z80/z80.h"
#include "bus/midi/midi.h"
#include "machine/6850acia.h"
#include "machine/pit8253.h"

#include "prophet600.lh"


namespace {

#define MAINCPU_TAG "z80"
#define PIT_TAG     "pit"
#define UART_TAG    "uart"

enum
{
	CV_CV_Osc1A = 0, CV_Osc2A, CV_Osc3A, CV_Osc4A, CV_Osc5A, CV_Osc6A,
	CV_Osc1B, CV_Osc2B, CV_Osc3B, CV_Osc4B, CV_Osc5B, CV_Osc6B,
	CV_Flt1, CV_Flt2, CV_Flt3, CV_Flt4, CV_Flt5, CV_Flt6,
	CV_Amp1, CV_Amp2, CV_Amp3, CV_Amp4, CV_Amp5, CV_Amp6,
	CV_PModOscB, CV_VolA, CV_VolB, CV_MasterVol, CV_APW, CV_ExtFilter, CV_Resonance, CV_BPW,

	CV_MAX
};

class prophet600_state : public driver_device
{
public:
	prophet600_state(const machine_config &mconfig, device_type type, const char *tag)
	: driver_device(mconfig, type, tag),
		m_maincpu(*this, MAINCPU_TAG),
		m_acia(*this, UART_TAG),
		m_digits(*this, "digit%u", 0U),
		m_dac(0),
		m_scanrow(0),
		m_comparitor(0),
		m_nmi_gate(false)
	{ }

	void prophet600(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;

	void pit_ch0_tick_w(int state);
	void pit_ch2_tick_w(int state);
	void acia_irq_w(int state);
	void acia_clock_w(int state);

	void dac_w(offs_t offset, uint8_t data);
	void scanrow_w(uint8_t data);
	void led_w(uint8_t data);
	uint8_t scan_r();
	void potmux_w(uint8_t data);
	uint8_t comparitor_r();
	void mask_w(uint8_t data);
	void cv_w(uint8_t data);
	void gate_w(uint8_t data);

	void cpu_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<acia6850_device> m_acia;

	output_finder<2> m_digits;

	uint16_t m_dac;
	uint8_t m_scanrow;
	uint8_t m_comparitor;

	bool m_nmi_gate;

	// gates
	bool m_ASaw = false, m_ATri = false, m_Sync = false, m_BSaw = false, m_BTri = false, m_PModFA = false, m_PModFil = false;

	// control voltages
	uint16_t m_CVs[CV_MAX]{};
};

void prophet600_state::pit_ch0_tick_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, state);
}

void prophet600_state::pit_ch2_tick_w(int state)
{
	m_comparitor &= ~0x04;
	m_comparitor |= (state == ASSERT_LINE) ? 0x04 : 0x00;
}

void prophet600_state::acia_irq_w(int state)
{
	if (!m_nmi_gate)
	{
		m_maincpu->set_input_line(INPUT_LINE_NMI, state);
	}
}

void prophet600_state::acia_clock_w(int state)
{
	m_acia->write_txc(state);
	m_acia->write_rxc(state);
}

void prophet600_state::dac_w(offs_t offset, uint8_t data)
{
	if (offset)
	{
		m_dac &= 0xff;
		m_dac |= (data<<8);
		return;
	}

	m_dac &= 0xff00;
	m_dac |= data;
}

// also selects LEDs and keyboard keys to check
void prophet600_state::scanrow_w(uint8_t data)
{
	m_scanrow = data;
}

// scanrow 0x10 = LEDs, 0x20 = 7-segment #1, 0x40 = 7-segment #2
void prophet600_state::led_w(uint8_t data)
{
	if (m_scanrow & 0x10)
	{
		// LEDs in row 0x10 are Seq1=0, Seq2=1, ArpUD=2, ArpAssign=3, Preset=4, Record=5, ToTape=6, FromTape=7
	}
	else if (m_scanrow & 0x20)
	{
		m_digits[0] = data;
	}
	else if (m_scanrow & 0x40)
	{
		m_digits[1] = data;
	}
}

uint8_t prophet600_state::scan_r()
{
	return 0;
}

void prophet600_state::mask_w(uint8_t data)
{
	m_nmi_gate = (data & 0x02) ? true : false;
	if (m_nmi_gate) // gate is set, comparitor line is pulled up to Vcc
	{
		m_comparitor |= 0x04;
	}
	else
	{
		m_comparitor &= ~0x04;
	}

//  printf("8253 gate = %x\n", data & 0x02);
}

void prophet600_state::cv_w(uint8_t data)
{
	int cvnum = data & 7;

	if (!(cvnum & 0x08)) m_CVs[cvnum] = m_dac;
	if (!(cvnum & 0x10)) m_CVs[cvnum+8] = m_dac;
	if (!(cvnum & 0x20)) m_CVs[cvnum+16] = m_dac;
	if (!(cvnum & 0x40)) m_CVs[cvnum+24] = m_dac;
}

void prophet600_state::gate_w(uint8_t data)
{
	m_ASaw = (data & 0x01);
	m_ATri = (data & 0x02);
	m_Sync = (data & 0x04);
	m_BSaw = (data & 0x08);
	m_BTri = (data & 0x10);
	m_PModFA = (data & 0x20);
	m_PModFil = (data & 0x40);
}

/* Pots: Mixer=0,Cutoff=1,Resonance=2,FilEnvAmt=3,FilRel=4,FilSus=5,
    FilDec=6,FilAtt=7,AmpRel=8,AmpSus=9,AmpDec=10,AmpAtt=11,
    Glide=12,BPW=13,MVol=14,MTune=15,PitchWheel=16,ModWheel=22,
    Speed,APW,PModFilEnv,LFOFreq,PModOscB,LFOAmt,FreqB,FreqA,FreqBFine
*/
void prophet600_state::potmux_w(uint8_t data)
{
}

uint8_t prophet600_state::comparitor_r()
{
//  m_comparitor ^= 0x04;
	return m_comparitor;
}

void prophet600_state::cpu_map(address_map &map)
{
	map(0x0000, 0x1fff).rom().region(MAINCPU_TAG, 0);
	map(0x2000, 0x27ff).ram();
	map(0x3000, 0x37ff).ram();
	map(0x4000, 0x4001).w(FUNC(prophet600_state::dac_w));
	map(0x6000, 0x6001).w(m_acia, FUNC(acia6850_device::write));
	map(0xe000, 0xe001).r(m_acia, FUNC(acia6850_device::read));
}

void prophet600_state::io_map(address_map &map)
{
	map(0x00, 0x07).mirror(0xff00).rw(PIT_TAG, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x08, 0x08).mirror(0xff00).w(FUNC(prophet600_state::scanrow_w));
	map(0x09, 0x09).mirror(0xff00).rw(FUNC(prophet600_state::comparitor_r), FUNC(prophet600_state::led_w));
	map(0x0a, 0x0a).mirror(0xff00).rw(FUNC(prophet600_state::scan_r), FUNC(prophet600_state::potmux_w));
	map(0x0b, 0x0b).mirror(0xff00).w(FUNC(prophet600_state::gate_w));
	map(0x0d, 0x0d).mirror(0xff00).w(FUNC(prophet600_state::cv_w));
	map(0x0e, 0x0e).mirror(0xff00).w(FUNC(prophet600_state::mask_w));
}

void prophet600_state::machine_start()
{
	m_digits.resolve();
}

// master crystal is 8 MHz, all clocks derived from there
void prophet600_state::prophet600(machine_config &config)
{
	Z80(config, m_maincpu, XTAL(8'000'000)/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &prophet600_state::cpu_map);
	m_maincpu->set_addrmap(AS_IO, &prophet600_state::io_map);

	config.set_default_layout(layout_prophet600);

	pit8253_device &pit8253(PIT8253(config, PIT_TAG, XTAL(8'000'000)/4));
	pit8253.set_clk<0>(XTAL(8'000'000)/4);
	pit8253.set_clk<1>(XTAL(8'000'000)/4);
	pit8253.set_clk<2>(XTAL(8'000'000)/4);
	pit8253.out_handler<0>().set(FUNC(prophet600_state::pit_ch0_tick_w));
	pit8253.out_handler<2>().set(FUNC(prophet600_state::pit_ch2_tick_w));

	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set("mdout", FUNC(midi_port_device::write_txd));
	m_acia->irq_handler().set(FUNC(prophet600_state::acia_irq_w));

	MIDI_PORT(config, "mdin", midiin_slot, "midiin").rxd_handler().set(m_acia, FUNC(acia6850_device::write_rxd));

	MIDI_PORT(config, "mdout", midiout_slot, "midiout");

	clock_device &acia_clock(CLOCK(config, "acia_clock", XTAL(8'000'000)/16));  // 500kHz = 16 times the MIDI rate
	acia_clock.signal_handler().set(FUNC(prophet600_state::acia_clock_w));
}

static INPUT_PORTS_START( prophet600 )
INPUT_PORTS_END

ROM_START( prpht600 )
	ROM_REGION(0x2000, MAINCPU_TAG, 0)
	ROM_LOAD( "p600.bin",     0x000000, 0x002000, CRC(78e3f048) SHA1(61548b6de3d9b5c0ae76f8e751ece0b57de17118) )
ROM_END

} // anonymous namespace


CONS( 1983, prpht600, 0, 0, prophet600, prophet600, prophet600_state, empty_init, "Sequential Circuits", "Prophet-600", MACHINE_NOT_WORKING|MACHINE_NO_SOUND )



proteus.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
// thanks-to:Andrew Trotman
/**********************************************************************

    Poly Proteus Computer

    http://www.cs.otago.ac.nz/homepages/andrew/poly/Poly.htm

    Specifications:

      Processors     6809,Z80

      RAM            64 Kbytes

      ROM            4 Kbytes

      Clock          Z80 4 MHz (1 wait state per machine cycle)
                     6809 4 MHz clock, 1 MHz cycle

      Disk Drives    One or two 8" half height drives (Shugart SA860)
                     Double sided single density
                     Capacity 630 Kbytes per disk (CP/M)
                              590 Kbytes (Flex,Polysys)
                     Single sided can be used for compatibility

      Ports          Standard One RS232
                              One Poly network
                     Optional Total 3 RS232
                              One Poly network
                              One parallel
                              One disk drive extension

      Operating systems
                     CP/M. The standard operating system for 8080/Z80 micros.
                     FLEX. The standard operating system for 6809 micros.
                     POLYSYS. The operating system for the POLY network.

**********************************************************************/


#include "emu.h"
#include "cpu/m6809/m6809.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/6821pia.h"
#include "machine/6840ptm.h"
#include "machine/6850acia.h"
#include "machine/mc6854.h"
#include "machine/ram.h"
#include "machine/input_merger.h"
#include "machine/clock.h"
#include "machine/wd_fdc.h"
#include "bus/rs232/rs232.h"
#include "bus/centronics/ctronics.h"
#include "formats/flex_dsk.h"
#include "formats/poly_dsk.h"
#include "softlist_dev.h"


namespace {

class proteus_state : public driver_device
{
public:
	proteus_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_z80(*this, "z80")
		, m_adlc(*this, "mc6854")
		, m_ptm(*this, "ptm")
		, m_irqs(*this, "irqs")
		, m_pia(*this, "pia")
		, m_acia(*this, "acia%u", 0)
		, m_fdc(*this, "fdc")
		, m_floppy0(*this, "fdc:0")
		, m_floppy1(*this, "fdc:1")
		, m_floppy(nullptr)
	{ }

	void proteus(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

	void ptm_o2_callback(int state);
	void ptm_o3_callback(int state);
	uint8_t network_r(offs_t offset);
	void network_w(offs_t offset, uint8_t data);

	uint8_t drive_register_r();
	void drive_register_w(uint8_t data);
	void motor_w(int state);
	uint8_t fdc_inv_r(offs_t offset);
	void fdc_inv_w(offs_t offset, uint8_t data);
	static void floppy_formats(format_registration &fr);

	void enable_z80_w(uint8_t data);
	void enable_6809_w(uint8_t data);

	void proteus_6809_mem(address_map &map) ATTR_COLD;
	void proteus_z80_mem(address_map &map) ATTR_COLD;
	void proteus_z80_io(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<cpu_device> m_z80;
	required_device<mc6854_device> m_adlc;
	required_device<ptm6840_device> m_ptm;
	required_device<input_merger_device> m_irqs;
	required_device<pia6821_device> m_pia;
	required_device_array<acia6850_device, 3> m_acia;
	required_device<fd1771_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	required_device<floppy_connector> m_floppy1;
	floppy_image_device *m_floppy = nullptr;
};


void proteus_state::machine_start()
{
	uint8_t bitswapped[0x1000];
	uint8_t *rom;

	/* bios rom region */
	rom = memregion("bios")->base();

	/* decrypt rom data lines */
	for (int i = 0x0000; i<0x1000; i++)
		bitswapped[i] = bitswap<8>(rom[i], 3, 4, 2, 5, 1, 6, 0, 7);

	/* decrypt rom address lines */
	for (int i = 0x0000; i<0x1000; i++)
		rom[i] = bitswapped[bitswap<16>(i, 15, 14, 13, 12, 10, 8, 4, 2, 0, 1, 3, 5, 6, 7, 9, 11)];

	/*
	    The Proteus BIOS contains a serial ID for copy protection. Disks also contain the
	    OS serial ID which must match the BIOS serial ID. This can be negated by patching the
	    serial in the BIOS to 0x0000.
	*/
	rom[0x02] = 0x00;
	rom[0x03] = 0x00;
}


void proteus_state::machine_reset()
{
	/* system starts with 6809 running and the Z80 remains reset */
	m_z80->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
}


void proteus_state::ptm_o2_callback(int state)
{
	m_ptm->set_c1(state);
}

void proteus_state::ptm_o3_callback(int state)
{
	m_adlc->txc_w(state);
	m_adlc->rxc_w(!state);
}


uint8_t proteus_state::network_r(offs_t offset)
{
	return m_adlc->read(offset >> 1);
}

void proteus_state::network_w(offs_t offset, uint8_t data)
{
	m_adlc->write(offset >> 1, data);
}


void proteus_state::drive_register_w(uint8_t data)
{
	/* drive select */
	switch (data & 0x03)
	{
	case 0:
		m_floppy = m_floppy0->get_device();
		break;

	case 1:
		m_floppy = m_floppy1->get_device();
		break;

	default:
		m_floppy = nullptr;
		break;
	}
	m_fdc->set_floppy(m_floppy);

	if (m_floppy)
	{
		/* side select */
		m_floppy->ss_w(BIT(data, 6));
	}
}

uint8_t proteus_state::drive_register_r()
{
	/* disk change */
	return (m_floppy ? m_floppy->dskchg_r() : 1) << 1;
}

void proteus_state::motor_w(int state)
{
	if (m_floppy) m_floppy->mon_w(!state);
}

uint8_t proteus_state::fdc_inv_r(offs_t offset)
{
	return m_fdc->read(offset) ^ 0xff;
}

void proteus_state::fdc_inv_w(offs_t offset, uint8_t data)
{
	m_fdc->write(offset, data ^ 0xff);
}


void proteus_state::enable_z80_w(uint8_t data)
{
	logerror("%04X enable z80\n", m_maincpu->pc());
	// Enable Z80 (store an address at E060 and when the 6809 comes back it'll load PC with that address)
	m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
	m_z80->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
}

void proteus_state::enable_6809_w(uint8_t data)
{
	logerror("%04X enable 6809\n", m_z80->pc());
	// TODO: this is untested, not aware of any software that uses it
	m_z80->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
	m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
}


void proteus_state::proteus_6809_mem(address_map &map)
{
	map(0x0000, 0xffff).ram().share(RAM_TAG);
	map(0xe000, 0xe003).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));       // Parallel port
	map(0xe004, 0xe005).rw(m_acia[0], FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // Terminal
	map(0xe008, 0xe009).rw(m_acia[1], FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // Printer
	map(0xe00c, 0xe00d).rw(m_acia[2], FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // Modem
	map(0xe014, 0xe014).rw(FUNC(proteus_state::drive_register_r), FUNC(proteus_state::drive_register_w));
	map(0xe018, 0xe01b).rw(FUNC(proteus_state::fdc_inv_r), FUNC(proteus_state::fdc_inv_w)); // Floppy control
	map(0xe020, 0xe027).rw(m_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write));       // Timer
	map(0xe030, 0xe036).rw(FUNC(proteus_state::network_r), FUNC(proteus_state::network_w)); // Poly network
	map(0xe060, 0xe060).w(FUNC(proteus_state::enable_z80_w));
	map(0xf000, 0xffff).rom().region("bios", 0);
}


void proteus_state::proteus_z80_mem(address_map &map)
{
	map(0x0000, 0xffff).ram().share(RAM_TAG);
}


void proteus_state::proteus_z80_io(address_map &map)
{
	map(0x00, 0x03).mirror(0xff00).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));       // Parallel port
	map(0x04, 0x05).mirror(0xff00).rw(m_acia[0], FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // Terminal
	map(0x08, 0x09).mirror(0xff00).rw(m_acia[1], FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // Printer
	map(0x0c, 0x0d).mirror(0xff00).rw(m_acia[2], FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // Modem
	map(0x14, 0x14).mirror(0xff00).rw(FUNC(proteus_state::drive_register_r), FUNC(proteus_state::drive_register_w));
	map(0x18, 0x1b).mirror(0xff00).rw(FUNC(proteus_state::fdc_inv_r), FUNC(proteus_state::fdc_inv_w)); // Floppy control
	map(0x20, 0x27).mirror(0xff00).rw(m_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write));       // Timer
	map(0x30, 0x36).mirror(0xff00).rw(FUNC(proteus_state::network_r), FUNC(proteus_state::network_w)); // Poly network
	map(0x50, 0x50).mirror(0xff00).w(FUNC(proteus_state::enable_6809_w));
}


//Parameter 1: Baud Rate from BIOS
//  00 = 9600
//  02 = 4800
//  04 = 2400
//  06 = 1200
//  08 = 600
//  0A = 300
static INPUT_PORTS_START(proteus)
	PORT_START("TERM_BAUD")
	PORT_CONFNAME(0x07, 0x00, "Terminal Baud Rate")
	PORT_CONFSETTING(0x00, "9600")
	PORT_CONFSETTING(0x01, "4800")
	PORT_CONFSETTING(0x02, "2400")
	PORT_CONFSETTING(0x03, "1200")
	PORT_CONFSETTING(0x04, "600")
	PORT_CONFSETTING(0x05, "300")
	PORT_CONFSETTING(0x06, "150")

	PORT_START("J1")
	PORT_CONFNAME(0x07, 0x00, "J1 Modem Baud Rate")
	PORT_CONFSETTING(0x00, "9600")
	PORT_CONFSETTING(0x01, "4800")
	PORT_CONFSETTING(0x02, "2400")
	PORT_CONFSETTING(0x03, "1200")
	PORT_CONFSETTING(0x04, "600")
	PORT_CONFSETTING(0x05, "300")

	PORT_START("J2")
	PORT_CONFNAME(0x07, 0x00, "J2 Printer Baud Rate")
	PORT_CONFSETTING(0x00, "9600")
	PORT_CONFSETTING(0x01, "4800")
	PORT_CONFSETTING(0x02, "2400")
	PORT_CONFSETTING(0x03, "1200")
	PORT_CONFSETTING(0x04, "600")
	PORT_CONFSETTING(0x05, "300")
INPUT_PORTS_END


void proteus_state::floppy_formats(format_registration &fr)
{
	fr.add_fm_containers();
	fr.add(FLOPPY_FLEX_FORMAT);
	fr.add(FLOPPY_POLY_CPM_FORMAT);
}

static void proteus_floppies(device_slot_interface &device)
{
	device.option_add("8dssd", FLOPPY_8_DSSD); // Shugart SA-860
}


void proteus_state::proteus(machine_config &config)
{
	/* basic machine hardware */
	MC6809(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &proteus_state::proteus_6809_mem);

	Z80(config, m_z80, 4_MHz_XTAL);
	m_z80->set_addrmap(AS_PROGRAM, &proteus_state::proteus_z80_mem);
	m_z80->set_addrmap(AS_IO, &proteus_state::proteus_z80_io);

	INPUT_MERGER_ANY_HIGH(config, m_irqs);
	m_irqs->output_handler().set_inputline(m_maincpu, M6809_IRQ_LINE);
	m_irqs->output_handler().append_inputline(m_z80, INPUT_LINE_IRQ0);

	/* fdc */
	FD1771(config, m_fdc, 4_MHz_XTAL / 2);
	m_fdc->hld_wr_callback().set(FUNC(proteus_state::motor_w));
	m_fdc->set_force_ready(true);

	FLOPPY_CONNECTOR(config, "fdc:0", proteus_floppies, "8dssd", proteus_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", proteus_floppies, "8dssd", proteus_state::floppy_formats).enable_sound(true);

	/* ram */
	RAM(config, RAM_TAG).set_default_size("64K");

	/* network */
	MC6854(config, m_adlc);
	m_adlc->out_irq_cb().set("irqs", FUNC(input_merger_device::in_w<0>));

	PTM6840(config, m_ptm, 4_MHz_XTAL / 2);
	m_ptm->set_external_clocks(0, 0, 0);
	m_ptm->o2_callback().set(FUNC(proteus_state::ptm_o2_callback));
	m_ptm->o3_callback().set(FUNC(proteus_state::ptm_o3_callback));
	m_ptm->irq_callback().set("irqs", FUNC(input_merger_device::in_w<1>));

	/* parallel port */
	PIA6821(config, m_pia);
	//m_pia->readpb_handler().set(FUNC(proteus_state::pia_pb_r));
	//m_pia->writepa_handler().set(FUNC(proteus_state::pia_pa_w));
	//m_pia->writepb_handler().set(FUNC(proteus_state::pia_pb_w));
	//m_pia->ca2_handler().set(CENTRONICS_TAG, FUNC(centronics_device::write_strobe));
	m_pia->irqa_handler().set("irqs", FUNC(input_merger_device::in_w<2>));
	m_pia->irqb_handler().set("irqs", FUNC(input_merger_device::in_w<3>));

	centronics_device &parallel(CENTRONICS(config, "parallel", centronics_devices, "printer"));
	parallel.ack_handler().set(m_pia, FUNC(pia6821_device::ca1_w));
	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	parallel.set_output_latch(cent_data_out);

	/* terminal port */
	ACIA6850(config, m_acia[0], 0);
	m_acia[0]->txd_handler().set("terminal", FUNC(rs232_port_device::write_txd));
	m_acia[0]->rts_handler().set("terminal", FUNC(rs232_port_device::write_rts));
	m_acia[0]->irq_handler().set("irqs", FUNC(input_merger_device::in_w<4>));

	rs232_port_device &terminal(RS232_PORT(config, "terminal", default_rs232_devices, "terminal")); // TODO: ADM-31 terminal required
	terminal.rxd_handler().set(m_acia[0], FUNC(acia6850_device::write_rxd));
	terminal.cts_handler().set(m_acia[0], FUNC(acia6850_device::write_cts));

	clock_device &acia0_clock(CLOCK(config, "acia0_clock", 4_MHz_XTAL / 2 / 13)); // TODO: this is set using jumpers
	acia0_clock.signal_handler().set(m_acia[0], FUNC(acia6850_device::write_txc));
	acia0_clock.signal_handler().append(m_acia[0], FUNC(acia6850_device::write_rxc));

	/* printer port */
	ACIA6850(config, m_acia[1], 0);
	m_acia[1]->txd_handler().set("printer", FUNC(rs232_port_device::write_txd));
	m_acia[1]->rts_handler().set("printer", FUNC(rs232_port_device::write_rts));
	m_acia[1]->irq_handler().set("irqs", FUNC(input_merger_device::in_w<5>));

	rs232_port_device &printer(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	printer.rxd_handler().set(m_acia[1], FUNC(acia6850_device::write_rxd));
	printer.cts_handler().set(m_acia[1], FUNC(acia6850_device::write_cts));

	clock_device &acia1_clock(CLOCK(config, "acia1_clock", 4_MHz_XTAL / 2 / 13)); // TODO: this is set using jumpers J2
	acia1_clock.signal_handler().set(m_acia[1], FUNC(acia6850_device::write_txc));
	acia1_clock.signal_handler().append(m_acia[1], FUNC(acia6850_device::write_rxc));

	/* modem port */
	ACIA6850(config, m_acia[2], 0);
	m_acia[2]->txd_handler().set("modem", FUNC(rs232_port_device::write_txd));
	m_acia[2]->rts_handler().set("modem", FUNC(rs232_port_device::write_rts));
	m_acia[2]->irq_handler().set("irqs", FUNC(input_merger_device::in_w<6>));

	rs232_port_device &modem(RS232_PORT(config, "modem", default_rs232_devices, nullptr));
	modem.rxd_handler().set(m_acia[2], FUNC(acia6850_device::write_rxd));
	modem.cts_handler().set(m_acia[2], FUNC(acia6850_device::write_cts));

	clock_device &acia2_clock(CLOCK(config, "acia2_clock", 4_MHz_XTAL / 2 / 13)); // TODO: this is set using jumpers J1
	acia2_clock.signal_handler().set(m_acia[2], FUNC(acia6850_device::write_txc));
	acia2_clock.signal_handler().append(m_acia[2], FUNC(acia6850_device::write_rxc));

	/* software lists */
	SOFTWARE_LIST(config, "flop_list").set_original("poly_flop").set_filter("PROTEUS");
}


ROM_START(proteus)
	ROM_REGION(0x1000, "bios", 0)
	ROM_DEFAULT_BIOS("82")
	ROM_SYSTEM_BIOS(0, "82", "030982")
	ROMX_LOAD("proteusv30.u28", 0x0000, 0x1000, CRC(ad02dc36) SHA1(f73aff8b3d85448f603a2fe807e3a7e02550db27), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "86", "300986")
	ROMX_LOAD("300986.u28", 0x0000, 0x1000, CRC(f607d396) SHA1(0b9d9d3178b5587e60da56885b9e8a83f7d5dd26), ROM_BIOS(1))
ROM_END

} // anonymous namespace


/*    YEAR  NAME     PARENT  COMPAT   MACHINE  INPUT    CLASS          INIT        COMPANY     FULLNAME                     FLAGS */
COMP( 1982, proteus, 0,      0,       proteus, proteus, proteus_state, empty_init, "Polycorp", "Poly Proteus (Standalone)", MACHINE_NOT_WORKING )



proteus3.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/******************************************************************************

    Proteus III computer.

    2015-10-02 Skeleton [Robbbert]

    Chips:
    6800 @ 894kHz
    6850 (TTY keyboard interface)
    6850 (Cassette interface)
    6820 (PIA for Keyboard and video
    6844 DMA
    MC14411 baud rate generator
    CRT96364 CRTC @1008kHz

    There's an undumped 74S287 prom (M24) in the video section.
    It converts ascii control codes into the crtc control codes

    Schematic has lots of errors and omissions.

    Like many systems of the time, the cassette is grossly over-complicated,
    using 12 chips for a standard Kansas-city interface. Speed = 600 baud.

    To use the serial keyboard, type in the command: PORT#1
    and to go back to the parallel keyboard type in: PORT#0

    The Basic seems rather buggy and does odd things from time to time.
    bios 0 doesn't seem to have any way to backspace and type over
    bios 0 is the only one to have the EDIT command, although no idea how
    to use it.
    bios 2 is from a compatible system called "Micro Systemes 1", from the
    same company.

    To Do:
    - Add support for k7 cassette files.
    - Need software
    - Need missing PROM, so that all the CRTC controls can be emulated
    - Keyboard may have its own CPU etc, but no info available.
    - Missing buttons: BYE, PANIC, SPEED, HERE-IS. Could be more.
    - Should be able to type in some low-res graphics from the keyboard, not implemented.

******************************************************************************/

#include "emu.h"

#include "cpu/m6800/m6800.h"
#include "imagedev/cassette.h"
#include "machine/6821pia.h"
#include "machine/6850acia.h"
#include "machine/mc14411.h"
#include "machine/clock.h"
#include "machine/keyboard.h"
#include "machine/timer.h"

#include "bus/rs232/rs232.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class proteus3_state : public driver_device
{
public:
	proteus3_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_p_chargen(*this, "chargen")
		, m_pia(*this, "pia")
		, m_brg(*this, "brg")
		, m_acia1(*this, "acia1")
		, m_acia2(*this, "acia2")
		, m_cass(*this, "cassette")
		, m_serial(*this, "SERIAL")
	{ }

	void proteus3(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	void ca2_w(int state);
	void video_w(u8 data);
	void kbd_put(u8 data);
	void acia1_clock_w(int state);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);
	u32 screen_update_proteus3(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	// Clocks
	void write_acia_clocks(int id, int state);
	void write_f1_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F1, state); }
	void write_f2_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F2, state); }
	void write_f3_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F3, state); }
	void write_f4_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F4, state); }
	void write_f5_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F5, state); }
	void write_f6_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F6, state); }
	void write_f7_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F7, state); }
	void write_f8_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F8, state); }
	void write_f9_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F9, state); }
	void write_f10_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F10, state); }
	void write_f11_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F11, state); }
	void write_f12_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F12, state); }
	void write_f13_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F13, state); }
	void write_f14_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F14, state); }
	void write_f15_clock(int state) { write_acia_clocks(mc14411_device::TIMER_F15, state); }

	void mem_map(address_map &map) ATTR_COLD;

	u8 m_video_data = 0U;
	u8 m_flashcnt = 0U;
	u16 m_curs_pos = 0U;
	u8 m_cass_data[4]{};
	bool m_cassbit = false, m_cassold = false, m_cassinbit = false;
	std::unique_ptr<u8[]> m_vram;
	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_p_chargen;
	required_device<pia6821_device> m_pia;
	required_device<mc14411_device> m_brg;
	required_device<acia6850_device> m_acia1; // cassette uart
	required_device<acia6850_device> m_acia2; // tty keyboard uart
	required_device<cassette_image_device> m_cass;

	// hardware configuration and things that need rewiring
	required_ioport             m_serial;
};




/******************************************************************************
 Address Maps
******************************************************************************/

void proteus3_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).ram();
	map(0x8004, 0x8007).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x8008, 0x8009).rw(m_acia1, FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // cassette
	map(0x8010, 0x8011).rw(m_acia2, FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // serial keyboard 7E2 (never writes data)
	map(0xc000, 0xffff).rom().region("maincpu", 0);
}


/******************************************************************************
 Input Ports
******************************************************************************/

static INPUT_PORTS_START(proteus3)
	PORT_START("SERIAL")
	PORT_CONFNAME(0x0F , 0x00 , "Serial Baud Rate") // F1-F16 pins on MC14411 in X16
	PORT_CONFSETTING(mc14411_device::TIMER_F1,  "9600")
	PORT_CONFSETTING(mc14411_device::TIMER_F2,  "7200")
	PORT_CONFSETTING(mc14411_device::TIMER_F3,  "4800")
	PORT_CONFSETTING(mc14411_device::TIMER_F4,  "3600")
	PORT_CONFSETTING(mc14411_device::TIMER_F5,  "2400")
	PORT_CONFSETTING(mc14411_device::TIMER_F6,  "1800")
	PORT_CONFSETTING(mc14411_device::TIMER_F7,  "1200")
	PORT_CONFSETTING(mc14411_device::TIMER_F8,  "600")
	PORT_CONFSETTING(mc14411_device::TIMER_F9,  "300")
	PORT_CONFSETTING(mc14411_device::TIMER_F10, "200")
	PORT_CONFSETTING(mc14411_device::TIMER_F11, "150")
	PORT_CONFSETTING(mc14411_device::TIMER_F12, "134.5")
	PORT_CONFSETTING(mc14411_device::TIMER_F13, "110")
	PORT_CONFSETTING(mc14411_device::TIMER_F14, "75")
	PORT_CONFSETTING(mc14411_device::TIMER_F15, "57600")
	PORT_CONFSETTING(mc14411_device::TIMER_F16, "115200")
INPUT_PORTS_END

void proteus3_state::kbd_put(u8 data)
{
	if (data == 0x08)
		data = 0x0f; // take care of backspace (bios 1 and 2)
	m_pia->portb_w(data);
	m_pia->cb1_w(1);
	m_pia->cb1_w(0);
}

void proteus3_state::write_acia_clocks(int id, int state)
{
	if (id == m_serial->read()) // Configurable serial port
	{
		m_acia2->write_txc(state);
		m_acia2->write_rxc(state);
	}
	if (id == mc14411_device::TIMER_F8) // Fixed bitrate for the cassette interface
	{
		acia1_clock_w(state);
	}
}

/******************************************************************************
 Cassette
******************************************************************************/

TIMER_DEVICE_CALLBACK_MEMBER( proteus3_state::kansas_r )
{
	// no tape - set uart to idle
	m_cass_data[1]++;
	if (m_cass_data[1] > 32)
	{
		m_cass_data[1] = 32;
		m_cassinbit = 1;
	}

	/* cassette - turn 1200/2400Hz to a bit */
	u8 cass_ws = (m_cass->input() > +0.04) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		m_cassinbit = (m_cass_data[1] < 12) ? 1 : 0;
		m_cass_data[1] = 0;
	}
}

void proteus3_state::acia1_clock_w(int state)
{
	// Save - 8N2 - /16 - 600baud
	// Load - 8N2 - /1
	u8 twobit = m_cass_data[3] & 15;
	// incoming @9600Hz
	if (state)
	{
		if (twobit == 0)
		{
			m_cassold = m_cassbit;
			// synchronous rx
			m_acia1->write_rxc(0);
			m_acia1->write_rxd(m_cassinbit);
			m_acia1->write_rxc(1);
		}

		if (m_cassold)
			m_cass->output(BIT(m_cass_data[3], 1) ? +1.0 : -1.0); // 2400Hz
		else
			m_cass->output(BIT(m_cass_data[3], 2) ? +1.0 : -1.0); // 1200Hz

		m_cass_data[3]++;
	}

	m_acia1->write_txc(state);
}


/******************************************************************************
 Video
******************************************************************************/
void proteus3_state::video_w(u8 data)
{
	m_video_data = data;
}

void proteus3_state::ca2_w(int state)
{
	if (state)
	{
		switch(m_video_data)
		{
			case 0x0a: // Line Feed
				if (m_curs_pos > 959) // on bottom line?
				{
					memmove(m_vram.get(), m_vram.get()+64, 960); // scroll
					memset(m_vram.get()+960, 0x20, 64); // blank bottom line
				}
				else
					m_curs_pos += 64;
				break;
			case 0x0d: // Carriage Return
				m_curs_pos &= 0x3c0;
				break;
			case 0x0c: // CLS
				m_curs_pos = 0; // home cursor
				memset(m_vram.get(), 0x20, 1024); // clear screen
				break;
			case 0x0f: // Cursor Left
				if (m_curs_pos)
					m_curs_pos--;
				break;
			case 0x7f: // Erase character under cursor
				m_vram[m_curs_pos] = 0x20;
				break;
			default: // If a displayable character, show it
				if ((m_video_data > 0x1f) && (m_video_data < 0x7f))
				{
					m_vram[m_curs_pos] = m_video_data;
					m_curs_pos++;
					if (m_curs_pos > 1023) // have we run off the bottom?
					{
						m_curs_pos -= 64;
						memmove(m_vram.get(), m_vram.get()+64, 960); // scroll
						memset(m_vram.get()+960, 0x20, 64); // blank bottom line
					}
				}
		}
	}
}

u32 proteus3_state::screen_update_proteus3(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 sy=0,ma=0;
	m_flashcnt++;

	for (u8 y = 0; y < 16; y++ )
	{
		for (u8 ra = 0; ra < 12; ra++)
		{
			u16 *p = &bitmap.pix(sy++);

			for (u16 x = ma; x < ma + 64; x++)
			{
				u8 gfx = 0;
				if (ra < 8)
				{
					u8 chr = m_vram[x]; // get char in videoram
					gfx = m_p_chargen[(chr<<3) | ra]; // get dot pattern in chargen
				}
				else if ((ra == 9) && (m_curs_pos == x) && BIT(m_flashcnt, 4))
					gfx = 0xff;

				/* Display a scanline of a character */
				*p++ = BIT(gfx, 0);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 7);
			}
		}
		ma+=64;
	}
	return 0;
}


/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 8,                  /* 8 x 8 characters */
	128,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                    /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_proteus3 )
	GFXDECODE_ENTRY( "chargen", 0, charlayout, 0, 1 )
GFXDECODE_END


void proteus3_state::machine_reset()
{
	m_curs_pos = 0;
	m_cass_data[0] = m_cass_data[1] = m_cass_data[2] = m_cass_data[3] = 0;
	m_cassbit = 1;
	m_cassold = 1;
	m_acia1->write_rxd(1);

	// Set up the BRG divider. RSA is a jumper setting and RSB is always set High
	m_brg->rsa_w( CLEAR_LINE );
	m_brg->rsb_w( ASSERT_LINE );

	// Disable all configured timers, only enabling the used ones
	m_brg->timer_disable_all();
	m_brg->timer_enable((mc14411_device::timer_id) m_serial->read(), true); // Serial port
	m_brg->timer_enable( mc14411_device::TIMER_F8, true); // Cassette interface
}

void proteus3_state::machine_start()
{
	m_vram = make_unique_clear<u8[]>(0x0400);
	save_pointer(NAME(m_vram), 0x0400);
	save_item(NAME(m_video_data));
	save_item(NAME(m_flashcnt));
	save_item(NAME(m_curs_pos));
	save_item(NAME(m_cass_data));
	save_item(NAME(m_cassbit));
	save_item(NAME(m_cassold));
	save_item(NAME(m_cassinbit));

	m_flashcnt = 0;
}

/******************************************************************************
 Machine Drivers
******************************************************************************/

void proteus3_state::proteus3(machine_config &config)
{
	/* basic machine hardware */
	M6800(config, m_maincpu, XTAL(3'579'545));  /* Divided by 4 internally */
	m_maincpu->set_addrmap(AS_PROGRAM, &proteus3_state::mem_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(200));
	screen.set_size(64*8, 16*12);
	screen.set_visarea(0, 64*8-1, 0, 16*12-1);
	screen.set_screen_update(FUNC(proteus3_state::screen_update_proteus3));
	screen.set_palette("palette");
	GFXDECODE(config, "gfxdecode", "palette", gfx_proteus3);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* Devices */
	PIA6821(config, m_pia);
	m_pia->writepa_handler().set(FUNC(proteus3_state::video_w));
	m_pia->ca2_handler().set(FUNC(proteus3_state::ca2_w));
	m_pia->irqb_handler().set_inputline("maincpu", M6800_IRQ_LINE);

	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(proteus3_state::kbd_put));

	/* cassette */
	ACIA6850(config, m_acia1, 0);
	m_acia1->txd_handler().set([this] (bool state) { m_cassbit = state; });

	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	TIMER(config, "kansas_r").configure_periodic(FUNC(proteus3_state::kansas_r), attotime::from_hz(40000));

	// optional tty keyboard
	ACIA6850(config, m_acia2, 0);
	m_acia2->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_acia2->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "keyboard"));
	rs232.rxd_handler().set(m_acia2, FUNC(acia6850_device::write_rxd));
	rs232.cts_handler().set(m_acia2, FUNC(acia6850_device::write_cts));

	/* Bit Rate Generator */
	MC14411(config, m_brg, XTAL(1'843'200)); // crystal needs verification but is the likely one
	m_brg->out_f<1>().set(FUNC(proteus3_state::write_f1_clock));
	m_brg->out_f<2>().set(FUNC(proteus3_state::write_f2_clock));
	m_brg->out_f<3>().set(FUNC(proteus3_state::write_f3_clock));
	m_brg->out_f<4>().set(FUNC(proteus3_state::write_f4_clock));
	m_brg->out_f<5>().set(FUNC(proteus3_state::write_f5_clock));
	m_brg->out_f<6>().set(FUNC(proteus3_state::write_f6_clock));
	m_brg->out_f<7>().set(FUNC(proteus3_state::write_f7_clock));
	m_brg->out_f<8>().set(FUNC(proteus3_state::write_f8_clock));
	m_brg->out_f<9>().set(FUNC(proteus3_state::write_f9_clock));
	m_brg->out_f<10>().set(FUNC(proteus3_state::write_f10_clock));
	m_brg->out_f<11>().set(FUNC(proteus3_state::write_f11_clock));
	m_brg->out_f<12>().set(FUNC(proteus3_state::write_f12_clock));
	m_brg->out_f<13>().set(FUNC(proteus3_state::write_f13_clock));
	m_brg->out_f<14>().set(FUNC(proteus3_state::write_f14_clock));
	m_brg->out_f<15>().set(FUNC(proteus3_state::write_f15_clock));
}



/******************************************************************************
 ROM Definitions
******************************************************************************/

ROM_START(proteus3)
	ROM_REGION(0x4000, "maincpu", ROMREGION_ERASE00)  // if c000 isn't 0 it assumes a rom is there and jumps to it
	ROM_SYSTEM_BIOS( 0, "14k", "14k BASIC")
	ROMX_LOAD( "bas1.bin",     0x0800, 0x0800, CRC(016bf2d6) SHA1(89605dbede3b6fd101ee0548e5c545a0824fcfd3), ROM_BIOS(0) )
	ROMX_LOAD( "bas2.bin",     0x1000, 0x0800, CRC(39d3e543) SHA1(dd0fe220e3c2a48ce84936301311cbe9f1597ca7), ROM_BIOS(0) )
	ROMX_LOAD( "bas3.bin",     0x1800, 0x0800, CRC(3a41617d) SHA1(175406f4732389e226bc50d27ada39e6ea48de34), ROM_BIOS(0) )
	ROMX_LOAD( "bas4.bin",     0x2000, 0x0800, CRC(ee9d77ee) SHA1(f7e60a1ab88a3accc8ffdc545657c071934d09d2), ROM_BIOS(0) )
	ROMX_LOAD( "bas5.bin",     0x2800, 0x0800, CRC(bd81bb34) SHA1(6325735e5750a9536e63b67048f74711fae1fa42), ROM_BIOS(0) )
	ROMX_LOAD( "bas6.bin",     0x3000, 0x0800, CRC(60cd006b) SHA1(28354f78490da1eb5116cbbc43eaca0670f7f398), ROM_BIOS(0) )
	ROMX_LOAD( "bas7.bin",     0x3800, 0x0800, CRC(84c3dc22) SHA1(8fddba61b5f0270ca2daef32ab5edfd60300c776), ROM_BIOS(0) )

	ROM_SYSTEM_BIOS( 1, "8k", "8k BASIC")
	ROMX_LOAD( "proteus3_basic8k.m0", 0x2000, 0x2000, CRC(7d9111c2) SHA1(3c032c9c7f87d22a1a9819b3b812be84404d2ad2), ROM_BIOS(1) )
	ROM_RELOAD( 0x0000, 0x2000 )

	ROM_SYSTEM_BIOS( 2, "8kms", "8k Micro-Systemes BASIC")
	ROMX_LOAD( "ms1_basic8k.bin", 0x2000, 0x2000, CRC(b5476e28) SHA1(c8c2366d549b2645c740be4ab4237e05c3cab4a9), ROM_BIOS(2) )
	ROM_RELOAD( 0x0000, 0x2000 )

	ROM_REGION(0x0400, "chargen", 0)
	ROM_LOAD( "proteus3_font.m25",   0x0200, 0x0100, CRC(6a3a30a5) SHA1(ab39bf09722928483e497b87ac2dbd870828893b) )
	ROM_CONTINUE( 0x100, 0x100 )
	ROM_CONTINUE( 0x300, 0x100 )
	ROM_CONTINUE( 0x000, 0x100 )

	ROM_REGION(0x0800, "user1", 0) // roms not used yet
	// Proteus III - pbug F800-FFFF, expects RAM at F000-F7FF
	ROM_LOAD( "proteus3_pbug.bin", 0x0000, 0x0800, CRC(1118694d) SHA1(2dfc08d405e8f2936f5b0bd1c4007995151abbba) )
ROM_END

} // Anonymous namespace


/******************************************************************************
 Drivers
******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY                  FULLNAME       FLAGS
COMP( 1978, proteus3, 0,      0,      proteus3, proteus3, proteus3_state, empty_init, "Proteus International", "Proteus III", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



prschess.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

SciSys President Chess (model 231)
(not to be confused with Saitek Kasparov President)

The ROMs are inside a module, at the top-right. No known upgrades were released.
Apparently the chessboard was not that reliable. The manual even says to flip the
computer and shake it when one of the sensors is malfunctioning.

Hardware notes:
- 6502A @ 2MHz
- 16KB ROM(2*HN482764G), 2KB RAM(HM6116P-4)
- buzzer, 64+12 leds, magnet sensors chessboard

TODO:
- verify CPU speed / XTAL
- measure interrupt frequency
- the manual claims that it does a self-test at boot, but on MAME that only
  happens if you hold down one of the buttons

*******************************************************************************/

#include "emu.h"

#include "cpu/m6502/m6502.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"
#include "speaker.h"

// internal artwork
#include "saitek_prschess.lh"


namespace {

class prschess_state : public driver_device
{
public:
	prschess_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// machine configs
	void prschess(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<3> m_inputs;

	u8 m_inp_mux = 0;
	u8 m_led_data[2] = { };

	// address maps
	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_display();
	void leds_w(offs_t offset, u8 data);
	void control_w(u8 data);
	u8 input_r();
};

void prschess_state::machine_start()
{
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_led_data));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void prschess_state::update_display()
{
	u16 led_data = m_led_data[1] << 8 | m_led_data[0];
	led_data = bitswap<16>(led_data,15,14,5,4,3,2,1,0, 7,6,13,12,11,10,9,8);
	m_display->matrix(1 << m_inp_mux, led_data);
}

void prschess_state::leds_w(offs_t offset, u8 data)
{
	m_led_data[offset >> 8] = ~data;
	update_display();
}

void prschess_state::control_w(u8 data)
{
	// d0-d3: input mux, led select
	m_inp_mux = data & 0xf;
	update_display();

	// d5: speaker out
	m_dac->write(BIT(data, 5));

	// other: ?
}

u8 prschess_state::input_r()
{
	u8 data = 0;

	// read chessboard sensors
	if (m_inp_mux < 8)
		data = m_board->read_file(m_inp_mux);

	// read other buttons
	else if (m_inp_mux < 11)
		data = m_inputs[m_inp_mux - 8]->read();

	return data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void prschess_state::main_map(address_map &map)
{
	map(0x0000, 0x07ff).ram();
	map(0x4000, 0x4000).select(0x0100).w(FUNC(prschess_state::leds_w));
	map(0x4200, 0x4200).w(FUNC(prschess_state::control_w));
	map(0x4300, 0x4300).r(FUNC(prschess_state::input_r));
	map(0xc000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( prschess )
	PORT_START("IN.0")
	PORT_BIT(0x03, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Interrupt")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Move")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Hint")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("Legal")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Reset")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("Player V Computer")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Player V Player")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Change Sides")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Clear Board")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("Verify / Set Up")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("White")

	PORT_START("IN.2")
	PORT_BIT(0x03, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void prschess_state::prschess(machine_config &config)
{
	// basic machine hardware
	M6502(config, m_maincpu, 2'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &prschess_state::main_map);
	m_maincpu->set_periodic_int(FUNC(prschess_state::nmi_line_pulse), attotime::from_hz(100)); // guessed

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8+1, 16);
	config.set_default_layout(layout_saitek_prschess);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( prschess )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("y03_rl", 0xc000, 0x2000, CRC(862c3f42) SHA1(e2d2f1d7a0382b0774e86ca83e270dab1df700c2) ) // HN482764G
	ROM_LOAD("y03_rh", 0xe000, 0x2000, CRC(ef95cb9f) SHA1(02f763cf9cab1b4be8964ddb5d93efb05a898123) ) // "
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1982, prschess, 0,      0,      prschess, prschess, prschess_state, empty_init, "SciSys / Heuristic Software", "President Chess", MACHINE_SUPPORTS_SAVE )



ps2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol, Miodrag Milanovic, Carl

#include "emu.h"
#include "cpu/i86/i286.h"
#include "cpu/i386/i386.h"
#include "machine/at.h"
#include "machine/ram.h"
#include "bus/isa/isa_cards.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "softlist_dev.h"

// According to http://nerdlypleasures.blogspot.com/2014/04/the-original-8-bit-ide-interface.html
// the IBM PS/2 Model 25-286 and Model 30-286 use a customised version of the XTA (8-bit IDE) harddisk interface


namespace {

class ps2_state : public driver_device
{
public:
	ps2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_mb(*this, "mb"),
		m_ram(*this, RAM_TAG)
	{ }
	required_device<cpu_device> m_maincpu;
	required_device<at_mb_device> m_mb;
	required_device<ram_device> m_ram;

	void ps2m30286(machine_config &config);
	void ps2386(machine_config &config);
	void ps2386sx(machine_config &config);
	void at_softlists(machine_config &config);
	void ps2_16_io(address_map &map) ATTR_COLD;
	void ps2_16_map(address_map &map) ATTR_COLD;
	void ps2_32_io(address_map &map) ATTR_COLD;
	void ps2_32_map(address_map &map) ATTR_COLD;
protected:
	void machine_start() override ATTR_COLD;
};

void ps2_state::at_softlists(machine_config &config)
{
	/* software lists */
	SOFTWARE_LIST(config, "pc_disk_list").set_original("ibm5150");
	SOFTWARE_LIST(config, "at_disk_list").set_original("ibm5170");
	SOFTWARE_LIST(config, "at_cdrom_list").set_original("ibm5170_cdrom");
	SOFTWARE_LIST(config, "at_hdd_list").set_original("ibm5170_hdd");
	SOFTWARE_LIST(config, "midi_disk_list").set_compatible("midi_flop");
	// TODO: verify thru Windows 3.1
//  SOFTWARE_LIST(config, "photocd_list").set_compatible("photo_cd");
}

void ps2_state::ps2_16_map(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x09ffff).bankrw("bank10");
	map(0x0e0000, 0x0fffff).rom().region("bios", 0);
	map(0xfe0000, 0xffffff).rom().region("bios", 0);
}

void ps2_state::ps2_32_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000000, 0x0009ffff).bankrw("bank10");
	map(0x000e0000, 0x000fffff).rom().region("bios", 0);
	map(0xfffe0000, 0xffffffff).rom().region("bios", 0);
}

void ps2_state::ps2_16_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m(m_mb, FUNC(at_mb_device::map));
}

void ps2_state::ps2_32_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m(m_mb, FUNC(at_mb_device::map));
}

void ps2_state::machine_start()
{
	address_space& space = m_maincpu->space(AS_PROGRAM);

	/* managed RAM */
	membank("bank10")->set_base(m_ram->pointer());

	if (m_ram->size() > 0xa0000)
	{
		offs_t ram_limit = 0x100000 + m_ram->size() - 0xa0000;
		space.install_ram(0x100000,  ram_limit - 1, m_ram->pointer() + 0xa0000);
	}
}

void ps2_state::ps2m30286(machine_config &config)
{
	/* basic machine hardware */
	i80286_cpu_device &maincpu(I80286(config, m_maincpu, 10000000));
	maincpu.set_addrmap(AS_PROGRAM, &ps2_state::ps2_16_map);
	maincpu.set_addrmap(AS_IO, &ps2_state::ps2_16_io);
	maincpu.set_irq_acknowledge_callback("mb:pic8259_master", FUNC(pic8259_device::inta_cb));
	maincpu.shutdown_callback().set("mb", FUNC(at_mb_device::shutdown));

	AT_MB(config, m_mb);
	m_mb->kbd_clk().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	m_mb->kbd_data().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));

	config.set_maximum_quantum(attotime::from_hz(60));

	at_softlists(config);

	// FIXME: determine ISA bus clock
	ISA16_SLOT(config, "isa1", 0, "mb:isabus", pc_isa16_cards, "vga", true);
	ISA16_SLOT(config, "isa2", 0, "mb:isabus", pc_isa16_cards, "fdc", false);
	ISA16_SLOT(config, "isa3", 0, "mb:isabus", pc_isa16_cards, "ide", false);
	ISA16_SLOT(config, "isa4", 0, "mb:isabus", pc_isa16_cards, "comat", false);

	pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_at_keyboards, STR_KBD_IBM_PC_AT_84));
	kbd.out_clock_cb().set(m_mb, FUNC(at_mb_device::kbd_clk_w));
	kbd.out_data_cb().set(m_mb, FUNC(at_mb_device::kbd_data_w));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("1664K").set_extra_options("2M,4M,8M,15M");
}

void ps2_state::ps2386(machine_config &config)
{
	I386(config, m_maincpu, 12000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &ps2_state::ps2_32_map);
	m_maincpu->set_addrmap(AS_IO, &ps2_state::ps2_32_io);
	m_maincpu->set_irq_acknowledge_callback("mb:pic8259_master", FUNC(pic8259_device::inta_cb));

	AT_MB(config, m_mb);
	m_mb->kbd_clk().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	m_mb->kbd_data().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));

	config.set_maximum_quantum(attotime::from_hz(60));
	at_softlists(config);

	// on board devices
	ISA16_SLOT(config, "board1", 0, "mb:isabus", pc_isa16_cards, "fdc_smc", true); // FIXME: determine ISA bus clock
	ISA16_SLOT(config, "board2", 0, "mb:isabus", pc_isa16_cards, "comat", true);
	ISA16_SLOT(config, "board3", 0, "mb:isabus", pc_isa16_cards, "ide", true);
	ISA16_SLOT(config, "board4", 0, "mb:isabus", pc_isa16_cards, "lpt", true);
	// ISA cards
	ISA16_SLOT(config, "isa1", 0, "mb:isabus", pc_isa16_cards, "svga_et4k", false);
	ISA16_SLOT(config, "isa2", 0, "mb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa3", 0, "mb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa4", 0, "mb:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa5", 0, "mb:isabus", pc_isa16_cards, nullptr, false);

	pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_at_keyboards, STR_KBD_MICROSOFT_NATURAL));
	kbd.out_clock_cb().set(m_mb, FUNC(at_mb_device::kbd_clk_w));
	kbd.out_data_cb().set(m_mb, FUNC(at_mb_device::kbd_data_w));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("1664K").set_extra_options("2M,4M,8M,15M,16M,32M,64M,128M,256M");
}

void ps2_state::ps2386sx(machine_config &config)
{
	ps2386(config);
	I386SX(config.replace(), m_maincpu, 12000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &ps2_state::ps2_16_map);
	m_maincpu->set_addrmap(AS_IO, &ps2_state::ps2_16_io);
	m_maincpu->set_irq_acknowledge_callback("mb:pic8259_master", FUNC(pic8259_device::inta_cb));
}

ROM_START( i8530286 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// saved from running machine
	ROM_LOAD16_BYTE("ps2m30.0", 0x00000, 0x10000, CRC(9965a634) SHA1(c237b1760f8a4561ec47dc70fe2e9df664e56596))
	ROM_LOAD16_BYTE("ps2m30.1", 0x00001, 0x10000, CRC(1448d3cb) SHA1(13fa26d895ce084278cd5ab1208fc16c80115ebe))
ROM_END

/*

8530-H31 (Model 30/286)
======================
  P/N          Date
33F5381A EC C01446 1990

*/
ROM_START( i8530h31 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "33f5381a.bin", 0x00000, 0x20000, CRC(ff57057d) SHA1(d7f1777077a8df43c3c14d175b9709bd3969c4b1))
ROM_END

/*
8535-043 (Model 35SX)
===================
  P/N    Checksum     Date
04G2021    C26C       1991    ODD
04G2022    9B94       1991    EVEN
*/
ROM_START( i8535043 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "04g2021.bin", 0x00001, 0x10000, CRC(4069b2eb) SHA1(9855c84c81d1f07e1da66b1ca45c1c10c0717a90))
	ROM_LOAD16_BYTE( "04g2022.bin", 0x00000, 0x10000, CRC(35c1af65) SHA1(7d2445cc463969c808fdd78e0a27a03db5dfc698))
ROM_END

/*
8550-021 (Model 50)
===================
 Code     Date       Internal
90X7420  4/12/87 --> 90X6815
90X7423  8/12/87 --> 90X6816
90X7426  8/12/87 --> 90X6817
90X7429 18/12/87 --> 90X6818

Same ROMs used by : (According to http://www.ibmmuseum.com/ohlandl/8565/8560.html)

IBM Personal System/2 Model 60 (8560-041 and 8560-071)
IBM Personal System/2 Model 65 SX (8565-061 and 8565-121)

*/
ROM_START( i8550021 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "90x7423.zm14", 0x00000, 0x8000, CRC(2c1633e0) SHA1(1af7faa526585a7cfb69e71d90a75e1f1c541586))
	ROM_LOAD16_BYTE( "90x7426.zm16", 0x00001, 0x8000, CRC(e7c762ce) SHA1(228f67dc915d84519da7fc1a59b7f9254278f3a0))
	ROM_LOAD16_BYTE( "90x7420.zm13", 0x10000, 0x8000, CRC(19a57cc1) SHA1(5b31ba66cd3690e651a450619a32b7210769945d))
	ROM_LOAD16_BYTE( "90x7429.zm18", 0x10001, 0x8000, CRC(6f0120f6) SHA1(e112c291ac3d9f6507c93ac49ad26f9fd2245fd2))

	ROM_REGION( 0x800, "keyboard", 0 )
	ROM_LOAD( "72x8455.zm82", 0x000, 0x800, CRC(7da223d3) SHA1(54c52ff6c6a2310f79b2c7e6d1259be9de868f0e) )
ROM_END

/*
8550-061 (Model 50Z)
===================
                  P/N              Date
AMI 8935MKN     15F8365    S63512  1988
AMI 8948MML     15F8366    S63512  1988

http://ps-2.kev009.com:8081/ohlandl/8550/8550z_Planar.html


*/
ROM_START( i8550061 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "15f8365.zm5", 0x00001, 0x10000, CRC(35aa3ecf) SHA1(a122531092a9cb08600b276da9c9c3ce385aab7b))
	ROM_LOAD16_BYTE( "15f8366.zm6", 0x00000, 0x10000, CRC(11bf564d) SHA1(0dda6a7ca9294cfaab5bdf4c05973be13b2766fc))
ROM_END

/*
8555-X61 (Model 55SX)
===================
         Code     Date       Internal
ODD     33F8145  13/03/90 --> 33F8153
EVEN    33F8146  31/01/90 --> 33F8152

8555-081 (Model 55SX)
===================
                         Code          Date    Internal
ODD     AMI 9205MEN     92F0627 EC32680 88 --> 33F8153
EVEN    AMI 9203MGS     92F0626 EC32680 88 --> 33F8152

*/
ROM_START( i8555081 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE("33f8145.zm40", 0x00001, 0x10000, CRC(0895894c) SHA1(7cee77828867ad1bdbe0ac223bc25d23c65b28a0))
	ROM_LOAD16_BYTE("33f8146.zm41", 0x00000, 0x10000, CRC(c6020680) SHA1(b25a64e4b2dca07c567648401100e04e89bbcddb))
ROM_END

/*
8580-071 (Model 80)
===================
                  Code    Date      Internal
AMI 8924MBW     90X8548   1987  --> 72X7551
AMI 8924MBL     90X8549   1987  --> 72X7554
AMI 8924MBG     90X8550   1987  --> 72X7557
AMI 8921MBK     90X8551   1987  --> 72X7560
*/
ROM_START( i8580071 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD32_BYTE( "90x8548.bin", 0x00000, 0x8000, CRC(1f13eea5) SHA1(0bf53ad86f47db3825a713ea2e4ef23715cc4f79))
	ROM_LOAD32_BYTE( "90x8549.bin", 0x00001, 0x8000, CRC(9e0f4a99) SHA1(b8600f04159ed281a57416274390ba9302be541b))
	ROM_LOAD32_BYTE( "90x8550.bin", 0x00002, 0x8000, CRC(cb21df96) SHA1(0c2765f6becfa3f9171c4f13f7b74d19c4c9acc2))
	ROM_LOAD32_BYTE( "90x8551.bin", 0x00003, 0x8000, CRC(3d7e9868) SHA1(2928fe0e48a573cc2c0c41bd7f7188a54a908229))
ROM_END

/*
8580-111 (Model 80)
===================
                 Code    Date    Internal
AMI 8934MDL     15F6637  1987 --> 15F6597
AMI 8944MDI     15F6639  1987 --> 15F6600
*/
ROM_START( i8580111 )
	ROM_REGION32_LE(0x20000, "bios", 0)
	ROM_LOAD16_BYTE( "15f6637.bin", 0x00000, 0x10000, CRC(76c36d1a) SHA1(c68d52a2e5fbd303225ebb006f91869b29ef700a))
	ROM_LOAD16_BYTE( "15f6639.bin", 0x00001, 0x10000, CRC(82cf0f7d) SHA1(13bb39225757b89749af70e881af0228673dbe0c))
ROM_END

} // anonymous namespace


COMP( 1990, i8530h31, 0,        ibm5170, ps2m30286, 0, ps2_state, empty_init, "International Business Machines", "IBM PS/2 8530-H31 (Model 30/286)", MACHINE_NOT_WORKING )
COMP( 1988, i8530286, i8530h31, 0,       ps2m30286, 0, ps2_state, empty_init, "International Business Machines", "IBM PS/2 Model 30-286", MACHINE_NOT_WORKING )
COMP( 198?, i8535043, 0,        ibm5170, ps2386sx,  0, ps2_state, empty_init, "International Business Machines", "IBM PS/2 8535-043 (Model 35SX)", MACHINE_NOT_WORKING )
COMP( 198?, i8550021, i8550061, 0,       ps2m30286, 0, ps2_state, empty_init, "International Business Machines", "IBM PS/2 8550-021 (Model 50)", MACHINE_NOT_WORKING )
COMP( 198?, i8550061, 0,        ibm5170, ps2m30286, 0, ps2_state, empty_init, "International Business Machines", "IBM PS/2 8550-061 (Model 50Z)", MACHINE_NOT_WORKING )
COMP( 1989, i8555081, 0,        ibm5170, ps2386sx,  0, ps2_state, empty_init, "International Business Machines", "IBM PS/2 8555-081 (Model 55SX)", MACHINE_NOT_WORKING )
COMP( 198?, i8580071, 0,        ibm5170, ps2386,    0, ps2_state, empty_init, "International Business Machines", "IBM PS/2 8580-071 (Model 80)", MACHINE_NOT_WORKING )
COMP( 198?, i8580111, 0,        ibm5170, ps2386,    0, ps2_state, empty_init, "International Business Machines", "IBM PS/2 8580-111 (Model 80)", MACHINE_NOT_WORKING )



ps2sony.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/**********************************************************************************************************************

2017-11-05 Skeleton PLACEHOLDER ONLY.

Sony PlayStation 2.

Related to: Konami Bemani/Python, Namco Systems 147, 246, 256 (info from system16.com). See namecops2.cpp for details.

Technical specs: https://en.wikipedia.org/wiki/PlayStation_2_technical_specifications

If development proceeds, it may be prudent to make this and namcops2.cpp derived classes of a common class.

**********************************************************************************************************************/
// the skeleton memory map and config was copied from namcops2.cpp
/*********************************************************************************************************************
Contents of INF files:

SCPH-75008_BIOS_V14_RUS_220.INF
[EE]
CPUrev=0x2E42
FPUrev=0x2E40
BOARDid=0xFFFF
VU0MEMsize=4KB;4KB (*)
VU1MEMsize=16KB;16KB (*)
GSrev=0x551C
GSMEMsize=4MB (*)
ICache=16KB
Dcache=8KB
MEMsize=32MB

[IOP]
CPUrev=0x0030
ICache=4KB (*)
Dcache=1KB (*)
MEMsize=2MB
BOARDinf=0xFF
SPU2rev=0x08
SPU2MEMsize=2M (*)
CDVDmecha=0x05060600
CDVDmeka=0x00
DEV9rev=0x0031
SPDrev=0x13 (unknown)
SPDcaps=0x03 (eth+ata)
SPDmac=00:15:C1:6B:6D:AB
USBhcrev=0x00000010
iLinkports=2
iLinkspeed=400
iLinkvendor=0x------
iLinkdevice=0x------
iLinkSGUID=0x--------

SCPH-77004_BIOS_V15_EUR_220.INF
[EE]
CPUrev=0x2E42
FPUrev=0x2E40
BOARDid=0xFFFF
VU0MEMsize=4KB;4KB (*)
VU1MEMsize=16KB;16KB (*)
GSrev=0x551D
GSMEMsize=4MB (*)
ICache=16KB
Dcache=8KB
MEMsize=32MB

[IOP]
CPUrev=0x0030
ICache=4KB (*)
Dcache=1KB (*)
MEMsize=2MB
BOARDinf=0xFF
SPU2rev=0x08
SPU2MEMsize=2M (*)
CDVDmecha=0x02060A00
CDVDmeka=0x00
DEV9rev=0x0031
SPDrev=0x13 (unknown)
SPDcaps=0x03 (eth+ata)
SPDmac=00:15:C1:BB:55:F3
USBhcrev=0x00000010
iLinkports=2
iLinkspeed=400
iLinkvendor=0x------
iLinkdevice=0x------
iLinkSGUID=0x--------

SCPH-77008_BIOS_V15_RUS_220.INF
[EE]
CPUrev=0x2E42
FPUrev=0x2E40
BOARDid=0xFFFF
VU0MEMsize=4KB;4KB (*)
VU1MEMsize=16KB;16KB (*)
GSrev=0x551C
GSMEMsize=4MB (*)
ICache=16KB
Dcache=8KB
MEMsize=32MB

[IOP]
CPUrev=0x0030
ICache=4KB (*)
Dcache=1KB (*)
MEMsize=2MB
BOARDinf=0xFF
SPU2rev=0x08
SPU2MEMsize=2M (*)
CDVDmecha=0x05060A00
CDVDmeka=0x00
DEV9rev=0x0031
SPDrev=0x13 (unknown)
SPDcaps=0x03 (eth+ata)
SPDmac=00:1D:0D:4D:EF:AB
USBhcrev=0x00000010
iLinkports=2
iLinkspeed=400
iLinkvendor=0x------
iLinkdevice=0x------
iLinkSGUID=0x--------

SCPH-90001_BIOS_V18_USA_230.INF
[EE]
CPUrev=0x2E43
FPUrev=0x2E40
BOARDid=0xFFFF
VU0MEMsize=4KB;4KB (*)
VU1MEMsize=16KB;16KB (*)
GSrev=0x551F
GSMEMsize=4MB (*)
ICache=16KB
Dcache=8KB
MEMsize=32MB

[IOP]
CPUrev=0x0030
ICache=4KB (*)
Dcache=1KB (*)
MEMsize=2MB
BOARDinf=0xFF
SPU2rev=0x08
SPU2MEMsize=2M (*)
CDVDmecha=0x01060C00
CDVDmeka=0x00
DEV9rev=0x0031
SPDrev=0x13 (unknown)
SPDcaps=0x03 (eth+ata)
SPDmac=00:1F:A7:BF:4D:AB
USBhcrev=0x00000010
iLinkports=2
iLinkspeed=400
iLinkvendor=0x------
iLinkdevice=0x------
iLinkSGUID=0x--------

(*) values are not detected
************************************************************************************************************************/

#include "emu.h"

#include "cpu/mips/mips3.h"
#include "cpu/mips/mips1.h"
#include "cpu/mips/ps2vu.h"
#include "cpu/mips/ps2vif1.h"

#include "machine/iopcdvd.h"
#include "machine/iopdma.h"
#include "machine/iopintc.h"
#include "machine/iopsio2.h"
#include "machine/ioptimer.h"

#include "machine/ps2dma.h"
#include "machine/ps2intc.h"
#include "machine/ps2mc.h"
#include "machine/ps2pad.h"
#include "machine/ps2sif.h"
#include "machine/ps2timer.h"

#include "sound/iopspu.h"

#include "video/ps2gs.h"
#include "video/ps2gif.h"

#include "emupal.h"
#include "screen.h"


namespace {

class ps2sony_state : public driver_device
{
public:
	ps2sony_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_iop(*this, "iop")
		, m_timer(*this, "timer%u", 0U)
		, m_dmac(*this, "dmac")
		, m_intc(*this, "intc")
		, m_sif(*this, "sif")
		, m_iop_timer(*this, "iop_timer")
		, m_iop_dma(*this, "iop_dma")
		, m_iop_intc(*this, "iop_intc")
		, m_iop_spu(*this, "iop_spu")
		, m_iop_cdvd(*this, "iop_cdvd")
		, m_iop_sio2(*this, "iop_sio2")
		, m_gs(*this, "gs")
		, m_vu0(*this, "vu0")
		, m_vu1(*this, "vu1")
		, m_pad(*this, "pad%u", 0U)
		, m_mc(*this, "mc")
		, m_screen(*this, "screen")
		, m_ram(*this, "ram")
		, m_iop_ram(*this, "iop_ram")
		, m_sp_ram(*this, "sp_ram")
		, m_vu0_imem(*this, "vu0imem")
		, m_vu0_dmem(*this, "vu0dmem")
		, m_vu1_imem(*this, "vu1imem")
		, m_vu1_dmem(*this, "vu1dmem")
		, m_bios(*this, "bios")
		, m_vblank_timer(nullptr)
	{ }

	void ps2sony(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	TIMER_CALLBACK_MEMBER(vblank);

	uint32_t ipu_r(offs_t offset, uint32_t mem_mask = ~0);
	void ipu_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint64_t vif0_fifo_r(offs_t offset, uint64_t mem_mask = ~0);
	void vif0_fifo_w(offs_t offset, uint64_t data, uint64_t mem_mask = ~0);
	uint64_t gif_fifo_r(offs_t offset, uint64_t mem_mask = ~0);
	void gif_fifo_w(offs_t offset, uint64_t data, uint64_t mem_mask = ~0);
	uint64_t ipu_fifo_r(offs_t offset, uint64_t mem_mask = ~0);
	void ipu_fifo_w(offs_t offset, uint64_t data, uint64_t mem_mask = ~0);
	void debug_w(uint8_t data);
	uint32_t unk_f430_r();
	void unk_f430_w(uint32_t data);
	uint32_t unk_f440_r();
	void unk_f440_w(uint32_t data);
	uint32_t unk_f520_r();
	uint64_t board_id_r();

	void ee_iop_ram_w(offs_t offset, uint64_t data, uint64_t mem_mask = ~0);
	uint64_t ee_iop_ram_r(offs_t offset);
	void iop_debug_w(uint32_t data);

	void iop_timer_irq(int state);

	void mem_map(address_map &map) ATTR_COLD;
	void iop_map(address_map &map) ATTR_COLD;

	required_device<r5900le_device> m_maincpu;
	required_device<iop_device>     m_iop;
	required_device_array<ps2_timer_device, 4> m_timer;
	required_device<ps2_dmac_device> m_dmac;
	required_device<ps2_intc_device> m_intc;
	required_device<ps2_sif_device> m_sif;
	required_device<iop_timer_device> m_iop_timer;
	required_device<iop_dma_device> m_iop_dma;
	required_device<iop_intc_device> m_iop_intc;
	required_device<iop_spu_device> m_iop_spu;
	required_device<iop_cdvd_device> m_iop_cdvd;
	required_device<iop_sio2_device> m_iop_sio2;
	required_device<ps2_gs_device> m_gs;
	required_device<sonyvu0_device> m_vu0;
	required_device<sonyvu1_device> m_vu1;
	required_device_array<ps2_pad_device, 2> m_pad;
	required_device<ps2_mc_device>  m_mc;
	required_device<screen_device>  m_screen;
	required_shared_ptr<uint64_t>   m_ram;
	required_shared_ptr<uint32_t>   m_iop_ram;
	required_shared_ptr<uint64_t>   m_sp_ram;
	required_shared_ptr<uint64_t>   m_vu0_imem;
	required_shared_ptr<uint64_t>   m_vu0_dmem;
	required_shared_ptr<uint64_t>   m_vu1_imem;
	required_shared_ptr<uint64_t>   m_vu1_dmem;
	required_region_ptr<uint32_t>   m_bios;

	uint32_t m_unk_f430_reg = 0;
	uint32_t m_unk_f440_counter = 0;
	uint32_t m_unk_f440_reg = 0;
	uint32_t m_unk_f440_ret = 0;

	uint32_t m_ipu_ctrl = 0;
	uint64_t m_ipu_in_fifo[0x1000]{};
	uint64_t m_ipu_in_fifo_index = 0;
	uint64_t m_ipu_out_fifo[0x1000]{};
	uint64_t m_ipu_out_fifo_index = 0;

	emu_timer *m_vblank_timer = nullptr;
};

/**************************************
 *
 * Machine Hardware
 *
 */

uint64_t ps2sony_state::vif0_fifo_r(offs_t offset, uint64_t mem_mask)
{
	uint64_t ret = 0ULL;
	if (offset)
	{
		logerror("%s: vif0_fifo_r [127..64]: (%08x%08x & %08x%08x)\n", machine().describe_context(), (uint32_t)(ret >> 32), (uint32_t)ret, (uint32_t)(mem_mask >> 32), (uint32_t)mem_mask);
	}
	else
	{
		logerror("%s: vif0_fifo_r [63..0]: (%08x%08x & %08x%08x)\n", machine().describe_context(), (uint32_t)(ret >> 32), (uint32_t)ret, (uint32_t)(mem_mask >> 32), (uint32_t)mem_mask);
	}
	return ret;
}

void ps2sony_state::vif0_fifo_w(offs_t offset, uint64_t data, uint64_t mem_mask)
{
	if (offset)
	{
		logerror("%s: vif0_fifo_w [127..64]: %08x%08x & %08x%08x\n", machine().describe_context(), (uint32_t)(data >> 32), (uint32_t)data, (uint32_t)(mem_mask >> 32), (uint32_t)mem_mask);
	}
	else
	{
		logerror("%s: vif0_fifo_w [63..0]: %08x%08x & %08x%08x\n", machine().describe_context(), (uint32_t)(data >> 32), (uint32_t)data, (uint32_t)(mem_mask >> 32), (uint32_t)mem_mask);
	}
}

uint64_t ps2sony_state::gif_fifo_r(offs_t offset, uint64_t mem_mask)
{
	uint64_t ret = 0ULL;
	if (offset)
	{
		logerror("%s: gif_fifo_r [127..64]: (%08x%08x & %08x%08x)\n", machine().describe_context(), (uint32_t)(ret >> 32), (uint32_t)ret, (uint32_t)(mem_mask >> 32), (uint32_t)mem_mask);
	}
	else
	{
		logerror("%s: gif_fifo_r [63..0]: (%08x%08x & %08x%08x)\n", machine().describe_context(), (uint32_t)(ret >> 32), (uint32_t)ret, (uint32_t)(mem_mask >> 32), (uint32_t)mem_mask);
	}
	return ret;
}

void ps2sony_state::gif_fifo_w(offs_t offset, uint64_t data, uint64_t mem_mask)
{
	if (offset)
	{
		logerror("%s: gif_fifo_w [127..64]: %08x%08x & %08x%08x\n", machine().describe_context(), (uint32_t)(data >> 32), (uint32_t)data, (uint32_t)(mem_mask >> 32), (uint32_t)mem_mask);
	}
	else
	{
		logerror("%s: gif_fifo_w [63..0]: %08x%08x & %08x%08x\n", machine().describe_context(), (uint32_t)(data >> 32), (uint32_t)data, (uint32_t)(mem_mask >> 32), (uint32_t)mem_mask);
	}
}

uint32_t ps2sony_state::ipu_r(offs_t offset, uint32_t mem_mask)
{
	uint32_t ret = 0;
	switch (offset)
	{
		case 0: /* IPU_CMD */
			logerror("%s: ipu_r: IPU_CMD (%08x & %08x)\n", machine().describe_context(), ret, mem_mask);
			break;
		case 2: /* IPU_CTRL */
			ret = m_ipu_in_fifo_index | (m_ipu_out_fifo_index << 4);
			logerror("%s: ipu_r: IPU_CTRL (%08x & %08x)\n", machine().describe_context(), ret, mem_mask);
			break;
		case 4: /* IPU_BP */
			ret = m_ipu_in_fifo_index << 8;
			logerror("%s: ipu_r: IPU_BP (%08x & %08x)\n", machine().describe_context(), ret, mem_mask);
			break;
		case 6: /* IPU_TOP */
			logerror("%s: ipu_r: IPU_TOP (%08x & %08x)\n", machine().describe_context(), ret, mem_mask);
			break;
		default:
			logerror("%s: ipu_r: Unknown offset %08x & %08x\n", machine().describe_context(), 0x10002000 + (offset << 3), mem_mask);
			break;
	}
	return ret;
}

void ps2sony_state::ipu_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	switch (offset)
	{
		case 0: /* IPU_CMD */
			logerror("%s: ipu_w: IPU_CMD = %08x\n", machine().describe_context(), data, mem_mask);
			switch ((data >> 28) & 0xf)
			{
				case 0x00: /* BCLR */
					m_ipu_in_fifo_index = 0;
					logerror("%s: IPU command: BCLR (%08x)\n", machine().describe_context(), data, mem_mask);
					break;
				case 0x01: /* IDEC */
					logerror("%s: IPU command: IDEC (%08x & %08x)\n", machine().describe_context(), data, mem_mask);
					logerror("%s:              FB:%d QSC:%d DT_DECODE:%d SGN:%d DITHER:%d OFM:%s\n", machine().describe_context(),
						data & 0x3f, (data >> 16) & 0x1f, BIT(data, 24), BIT(data, 25), BIT(data, 26), BIT(data, 27) ? "RGB16" : "RGB32");
					break;
				case 0x02: /* BDEC */
					logerror("%s: IPU command: BDEC (%08x & %08x)\n", machine().describe_context(), data, mem_mask);
					logerror("%s:              FB:%d QSC:%d DT:%d DCR:%d MBI:%d\n", machine().describe_context(),
						data & 0x3f, (data >> 16) & 0x1f, BIT(data, 25), BIT(data, 26), BIT(data, 27));
					break;
				case 0x03: /* VDEC */
				{
					static char const *const vlc[4] =
					{
						"Macroblock Address Increment",
						"Macroblock Type",
						"Motion Code",
						"DMVector"
					};
					logerror("%s: IPU command: VDEC (%08x & %08x)\n", machine().describe_context(), data, mem_mask);
					logerror("%s:              FB:%d TBL:%s\n", machine().describe_context(), data & 0x3f, vlc[(data >> 26) & 3]);
					break;
				}
				case 0x04: /* FDEC */
					logerror("%s: IPU command: FDEC (%08x & %08x)\n", machine().describe_context(), data, mem_mask);
					logerror("%s:              FB:%d\n", machine().describe_context(), data & 0x3f);
					break;
				case 0x05: /* SETIQ */
					logerror("%s: IPU command: SETIQ (%08x & %08x)\n", machine().describe_context(), data, mem_mask);
					logerror("%s:              FB:%d IQM:%s quantization matrix\n", machine().describe_context(), data & 0x3f, BIT(data, 27) ? "Non-intra" : "Intra");
					break;
				case 0x06: /* SETVQ */
					logerror("%s: IPU command: SETVQ (%08x & %08x)\n", machine().describe_context(), data, mem_mask);
					break;
				case 0x07: /* CSC */
					logerror("%s: IPU command: CSC (%08x & %08x)\n", machine().describe_context(), data, mem_mask);
					logerror("%s:              MBC:%d DTE:%d OFM:%s\n", machine().describe_context(), data & 0x3ff, BIT(data, 26), BIT(data, 27) ? "RGB16" : "RGB32");
					break;
				case 0x08: /* PACK */
					logerror("%s: IPU command: PACK (%08x & %08x)\n", machine().describe_context(), data, mem_mask);
					logerror("%s:              DITHER:%d OFM:%s\n", machine().describe_context(), BIT(data, 26), BIT(data, 27) ? "RGB16" : "INDX4");
					break;
				case 0x09: /* SETTH */
					logerror("%s: IPU command: SETTH (%08x & %08x)\n", machine().describe_context(), data, mem_mask);
					logerror("%s:              TH0:%d TH1:%s\n", machine().describe_context(), data & 0x1ff, (data >> 16) & 0x1ff);
					break;
				default:
					logerror("%s: Unknown IPU command: %08x & %08x\n", machine().describe_context(), data, mem_mask);
					break;
			}
			break;
		case 2: /* IPU_CTRL */
			logerror("%s: ipu_w: IPU_CTRL = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			if (BIT(data, 30))
			{
				m_ipu_in_fifo_index = 0;
				m_ipu_out_fifo_index = 0;
				m_ipu_ctrl &= ~(0x8000c000);
			}
			break;
		case 4: /* IPU_BP */
			logerror("%s: ipu_w: IPU_BP = %08x & %08x (Not Valid!)\n", machine().describe_context(), data, mem_mask);
			break;
		case 6: /* IPU_TOP */
			logerror("%s: ipu_w: IPU_TOP & %08x = %08x\n", machine().describe_context(), data, mem_mask);
			break;
		default:
			logerror("%s: ipu_w: Unknown offset %08x = %08x\n", machine().describe_context(), 0x10002000 + (offset << 3), data);
			break;
	}
}

uint64_t ps2sony_state::ipu_fifo_r(offs_t offset, uint64_t mem_mask)
{
	uint64_t ret = 0ULL;
	switch (offset)
	{
		case 0:
			logerror("%s: ipu_fifo_r: IPU_OUT_FIFO[127..64] (%08x%08x & %08x%08x)\n", machine().describe_context(), (uint32_t)(ret >> 32), (uint32_t)ret, (uint32_t)(mem_mask >> 32), (uint32_t)mem_mask);
			break;
		case 1:
			logerror("%s: ipu_fifo_r: IPU_OUT_FIFO[63..0] (%08x%08x & %08x%08x)\n", machine().describe_context(), (uint32_t)(ret >> 32), (uint32_t)ret, (uint32_t)(mem_mask >> 32), (uint32_t)mem_mask);
			break;
		case 2:
			logerror("%s: ipu_fifo_r: IPU_IN_FIFO[127..64] & %08x%08x (Not Valid!)\n", machine().describe_context(), (uint32_t)(mem_mask >> 32), (uint32_t)mem_mask);
			break;
		case 3:
			logerror("%s: ipu_fifo_r: IPU_IN_FIFO[63..0] & %08x%08x (Not Valid!)\n", machine().describe_context(), (uint32_t)(mem_mask >> 32), (uint32_t)mem_mask);
			break;
		default:
			logerror("%s: ipu_fifo_r: Unknown offset %08x\n", machine().describe_context(), 0x10007000 + (offset << 1), (uint32_t)(mem_mask >> 32), (uint32_t)mem_mask);
			break;
	}
	return ret;
}

void ps2sony_state::ipu_fifo_w(offs_t offset, uint64_t data, uint64_t mem_mask)
{
	switch (offset)
	{
		case 0:
			logerror("%s: ipu_fifo_w: IPU_OUT_FIFO[127..64] = %08x%08x & %08x%08x (Not Valid!)\n", machine().describe_context(), (uint32_t)(data >> 32), (uint32_t)data, (uint32_t)(mem_mask >> 32), (uint32_t)mem_mask);
			break;
		case 1:
			logerror("%s: ipu_fifo_w: IPU_OUT_FIFO[63..0] = %08x%08x & %08x%08x (Not Valid!)\n", machine().describe_context(), (uint32_t)(data >> 32), (uint32_t)data, (uint32_t)(mem_mask >> 32), (uint32_t)mem_mask);
			break;
		case 2:
			logerror("%s: ipu_fifo_w: IPU_IN_FIFO[127..64] = %08x%08x & %08x%08x\n", machine().describe_context(), (uint32_t)(data >> 32), (uint32_t)data, (uint32_t)(mem_mask >> 32), (uint32_t)mem_mask);
			m_ipu_in_fifo[m_ipu_in_fifo_index] = data;
			m_ipu_in_fifo_index++;
			m_ipu_in_fifo_index &= 0xf;
			break;
		case 3:
			logerror("%s: ipu_fifo_w: IPU_IN_FIFO[63..0] = %08x%08x & %08x%08x\n", machine().describe_context(), (uint32_t)(data >> 32), (uint32_t)data, (uint32_t)(mem_mask >> 32), (uint32_t)mem_mask);
			break;
		default:
			logerror("%s: ipu_fifo_w: Unknown offset %08x = %08x%08x & %08x%08x\n", machine().describe_context(), 0x10007000 + (offset << 1), (uint32_t)(data >> 32), (uint32_t)data, (uint32_t)(mem_mask >> 32), (uint32_t)mem_mask);
			break;
	}
}

void ps2sony_state::iop_timer_irq(int state)
{
	logerror("%s: iop_timer_irq: %d\n", machine().describe_context(), state);
	if (state)
		m_iop_intc->raise_interrupt(iop_intc_device::INT_TIMER);
}

void ps2sony_state::iop_debug_w(uint32_t data)
{
	//printf("%08x ", data);
}

void ps2sony_state::machine_start()
{
	save_item(NAME(m_unk_f430_reg));
	save_item(NAME(m_unk_f440_counter));
	save_item(NAME(m_unk_f440_reg));
	save_item(NAME(m_unk_f440_ret));

	save_item(NAME(m_ipu_ctrl));
	save_item(NAME(m_ipu_in_fifo));
	save_item(NAME(m_ipu_in_fifo_index));
	save_item(NAME(m_ipu_out_fifo));
	save_item(NAME(m_ipu_out_fifo_index));

	if (!m_vblank_timer)
		m_vblank_timer = timer_alloc(FUNC(ps2sony_state::vblank), this);
}

void ps2sony_state::machine_reset()
{
	m_unk_f430_reg = 0;
	m_unk_f440_reg = 0;
	m_unk_f440_ret = 0;
	m_unk_f440_counter = 0;

	m_ipu_ctrl = 0;
	memset(m_ipu_in_fifo, 0, sizeof(uint64_t)*0x1000);
	m_ipu_in_fifo_index = 0;
	memset(m_ipu_out_fifo, 0, sizeof(uint64_t)*0x1000);
	m_ipu_out_fifo_index = 0;

	m_vblank_timer->adjust(m_screen->time_until_pos(0), 1);
}

TIMER_CALLBACK_MEMBER(ps2sony_state::vblank)
{
	if (param)
	{
		// VBlank enter
		m_iop_intc->raise_interrupt(iop_intc_device::INT_VB_ON);
		m_intc->raise_interrupt(ps2_intc_device::INT_VB_ON);
		m_vblank_timer->adjust(m_screen->time_until_pos(32), 0);
		m_gs->vblank_start();
	}
	else
	{
		// VBlank exit
		m_iop_intc->raise_interrupt(iop_intc_device::INT_VB_OFF);
		m_intc->raise_interrupt(ps2_intc_device::INT_VB_OFF);
		m_vblank_timer->adjust(m_screen->time_until_pos(0), 1);
		m_gs->vblank_end();
	}
}

void ps2sony_state::debug_w(uint8_t data)
{
	printf("%c", (char)data);
}

void ps2sony_state::ee_iop_ram_w(offs_t offset, uint64_t data, uint64_t mem_mask)
{
	const uint32_t offset_hi = (offset << 1);
	const uint32_t offset_lo = (offset << 1) + 1;
	const uint32_t mask_hi = (uint32_t)(mem_mask >> 32);
	const uint32_t mask_lo = (uint32_t)mem_mask;
	m_iop_ram[offset_hi] &= ~mask_hi;
	m_iop_ram[offset_hi] |= (uint32_t)(data >> 32) & mask_hi;
	m_iop_ram[offset_lo] &= ~mask_lo;
	m_iop_ram[offset_lo] |= (uint32_t)data & mask_lo;
}

uint64_t ps2sony_state::ee_iop_ram_r(offs_t offset)
{
	return ((uint64_t)m_iop_ram[offset << 1] << 32) | m_iop_ram[(offset << 1) + 1];
}

uint64_t ps2sony_state::board_id_r()
{
	return 0x1234;
}

uint32_t ps2sony_state::unk_f430_r()
{
	logerror("%s: Unknown 1000f430 read: %08x\n", machine().describe_context(), 0);
	//return m_unk_f430_reg;
	//return ~0;
	return 0; // BIOS seems unhappy if we return anything else
}

void ps2sony_state::unk_f430_w(uint32_t data)
{
	m_unk_f430_reg = data & ~0x80000000;
	const uint16_t cmd = (data >> 16) & 0xfff;
	const uint8_t subcmd = (data >> 6) & 0xf;
	switch (cmd)
	{
		case 0x0021:
			if (subcmd == 1 && !BIT(m_unk_f440_reg, 7))
			{
				m_unk_f440_counter = 0;
			}
			break;
	}
	logerror("%s: Unknown 1000f430 write: %08x (cmd:%02x lsb:%02x)\n", machine().describe_context(), data, (data >> 16) & 0xff, data & 0xff);
}

uint32_t ps2sony_state::unk_f440_r()
{
	uint32_t ret = 0;

	const uint8_t lsb5 = m_unk_f430_reg & 0x1f;
	const uint16_t cmd = (uint16_t)(m_unk_f430_reg >> 16) & 0xfff;
	const uint8_t subcmd = (m_unk_f430_reg >> 6) & 0xf;
	if (subcmd == 0)
	{
		switch (cmd)
		{
			case 0x21:
				if (m_unk_f440_counter < 2)
				{
					ret = 0x1f;
					m_unk_f440_counter++;
				}
				break;

			case 0x40:
				ret = lsb5;
				break;

			default:
				fatalerror("!");
				break;
		}
	}

	logerror("%s: Unknown 1000f440 read: %08x\n", machine().describe_context(), ret);
	return ret;
}

void ps2sony_state::unk_f440_w(uint32_t data)
{
	logerror("%s: Unknown 1000f440 write: %08x\n", machine().describe_context(), data);
	m_unk_f440_reg = data;
}

/**************************************
 *
 * Video Hardware
 *
 */

uint32_t ps2sony_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void ps2sony_state::mem_map(address_map &map)
{
	map(0x00000000, 0x01ffffff).mirror(0xe000000).ram().share(m_ram); // 32 MB RAM
	map(0x10000000, 0x100007ff).rw(m_timer[0], FUNC(ps2_timer_device::read), FUNC(ps2_timer_device::write)).umask64(0x00000000ffffffff);
	map(0x10000800, 0x10000fff).rw(m_timer[1], FUNC(ps2_timer_device::read), FUNC(ps2_timer_device::write)).umask64(0x00000000ffffffff);
	map(0x10001000, 0x100017ff).rw(m_timer[2], FUNC(ps2_timer_device::read), FUNC(ps2_timer_device::write)).umask64(0x00000000ffffffff);
	map(0x10001800, 0x10001fff).rw(m_timer[3], FUNC(ps2_timer_device::read), FUNC(ps2_timer_device::write)).umask64(0x00000000ffffffff);
	map(0x10002000, 0x10002fff).rw(FUNC(ps2sony_state::ipu_r), FUNC(ps2sony_state::ipu_w)).umask64(0x00000000ffffffff);
	map(0x10003000, 0x100030af).rw(m_gs, FUNC(ps2_gs_device::gif_r), FUNC(ps2_gs_device::gif_w));
	map(0x10004000, 0x1000400f).mirror(0xff0).rw(FUNC(ps2sony_state::vif0_fifo_r), FUNC(ps2sony_state::vif0_fifo_w));
	map(0x10005000, 0x1000500f).mirror(0xff0).rw(m_vu1, FUNC(sonyvu1_device::vif_r), FUNC(sonyvu1_device::vif_w));
	map(0x10006000, 0x1000600f).mirror(0xff0).rw(FUNC(ps2sony_state::gif_fifo_r), FUNC(ps2sony_state::gif_fifo_w));
	map(0x10007000, 0x1000701f).mirror(0xfe0).rw(FUNC(ps2sony_state::ipu_fifo_r), FUNC(ps2sony_state::ipu_fifo_w));
	map(0x10008000, 0x1000dfff).rw(m_dmac, FUNC(ps2_dmac_device::channel_r), FUNC(ps2_dmac_device::channel_w)).umask64(0x00000000ffffffff);
	map(0x1000e000, 0x1000efff).rw(m_dmac, FUNC(ps2_dmac_device::read), FUNC(ps2_dmac_device::write)).umask64(0x00000000ffffffff);
	map(0x1000f000, 0x1000f017).rw(m_intc, FUNC(ps2_intc_device::read), FUNC(ps2_intc_device::write)).umask64(0x00000000ffffffff);
	map(0x1000f130, 0x1000f137).nopr();
	map(0x1000f180, 0x1000f187).w(FUNC(ps2sony_state::debug_w)).umask64(0x00000000000000ff);
	map(0x1000f200, 0x1000f24f).rw(m_sif, FUNC(ps2_sif_device::ee_r), FUNC(ps2_sif_device::ee_w)).umask64(0x00000000ffffffff);
	map(0x1000f430, 0x1000f437).rw(FUNC(ps2sony_state::unk_f430_r), FUNC(ps2sony_state::unk_f430_w)).umask64(0x00000000ffffffff); // Unknown
	map(0x1000f440, 0x1000f447).rw(FUNC(ps2sony_state::unk_f440_r), FUNC(ps2sony_state::unk_f440_w)).umask64(0x00000000ffffffff); // Unknown
	map(0x1000f520, 0x1000f523).r(m_dmac, FUNC(ps2_dmac_device::disable_mask_r)).umask64(0x00000000ffffffff);
	map(0x1000f590, 0x1000f593).w(m_dmac, FUNC(ps2_dmac_device::disable_mask_w)).umask64(0x00000000ffffffff);
	map(0x11000000, 0x11000fff).mirror(0x3000).ram().share(m_vu0_imem);
	map(0x11004000, 0x11004fff).mirror(0x3000).ram().share(m_vu0_dmem);
	map(0x11008000, 0x1100bfff).ram().share(m_vu1_imem);
	map(0x1100c000, 0x1100ffff).ram().share(m_vu1_dmem);
	map(0x12000000, 0x120003ff).mirror(0xc00).rw(m_gs, FUNC(ps2_gs_device::priv_regs0_r), FUNC(ps2_gs_device::priv_regs0_w));
	map(0x12001000, 0x120013ff).mirror(0xc00).rw(m_gs, FUNC(ps2_gs_device::priv_regs1_r), FUNC(ps2_gs_device::priv_regs1_w));
	map(0x1c000000, 0x1c1fffff).rw(FUNC(ps2sony_state::ee_iop_ram_r), FUNC(ps2sony_state::ee_iop_ram_w)); // IOP has 2MB EDO RAM per Wikipedia, and writes go up to this point
	map(0x1f803800, 0x1f803807).r(FUNC(ps2sony_state::board_id_r));
	map(0x1fc00000, 0x1fffffff).lr32([this] (offs_t offset) { return m_bios[offset]; }, "bios_r");

	map(0x70000000, 0x70003fff).ram().share(m_sp_ram); // 16KB Scratchpad RAM
}

void ps2sony_state::iop_map(address_map &map)
{
	map(0x00000000, 0x001fffff).ram().share(m_iop_ram);
	map(0x1d000000, 0x1d00004f).rw(m_sif, FUNC(ps2_sif_device::iop_r), FUNC(ps2_sif_device::iop_w));
	map(0x1e000000, 0x1e003fff).nopr();
	map(0x1f402000, 0x1f40201f).rw(m_iop_cdvd, FUNC(iop_cdvd_device::read), FUNC(iop_cdvd_device::write));
	map(0x1f801070, 0x1f80107b).rw(m_iop_intc, FUNC(iop_intc_device::read), FUNC(iop_intc_device::write));
	map(0x1f801080, 0x1f8010f7).rw(m_iop_dma, FUNC(iop_dma_device::bank0_r), FUNC(iop_dma_device::bank0_w));
	map(0x1f801450, 0x1f801453).noprw();
	map(0x1f8014a0, 0x1f8014af).rw(m_iop_timer, FUNC(iop_timer_device::read), FUNC(iop_timer_device::write));
	map(0x1f801500, 0x1f801577).rw(m_iop_dma, FUNC(iop_dma_device::bank1_r), FUNC(iop_dma_device::bank1_w));
	map(0x1f801578, 0x1f80157b).noprw();
	map(0x1f802070, 0x1f802073).w(FUNC(ps2sony_state::iop_debug_w)).nopr();
	map(0x1f808200, 0x1f8082ff).rw(m_iop_sio2, FUNC(iop_sio2_device::read), FUNC(iop_sio2_device::write));
	map(0x1f900000, 0x1f9007ff).rw(m_iop_spu, FUNC(iop_spu_device::read), FUNC(iop_spu_device::write));
	map(0x1fc00000, 0x1fffffff).rom().region("bios", 0);
	map(0x1ffe0130, 0x1ffe0133).nopw();
}

static INPUT_PORTS_START( ps2sony )
INPUT_PORTS_END

void ps2sony_state::ps2sony(machine_config &config)
{
	R5900LE(config, m_maincpu, 294'912'000, m_vu0);
	m_maincpu->set_force_no_drc(true);
	m_maincpu->set_icache_size(16384);
	m_maincpu->set_dcache_size(16384);
	m_maincpu->set_addrmap(AS_PROGRAM, &ps2sony_state::mem_map);

	SONYPS2_VU0(config, m_vu0, 294'912'000, m_vu1);
	SONYPS2_VU1(config, m_vu1, 294'912'000, m_gs);

	SONYPS2_TIMER(config, m_timer[0], 294912000/2, true);
	SONYPS2_TIMER(config, m_timer[1], 294912000/2, true);
	SONYPS2_TIMER(config, m_timer[2], 294912000/2, false);
	SONYPS2_TIMER(config, m_timer[3], 294912000/2, false);

	SONYPS2_INTC(config, m_intc, m_maincpu);
	SONYPS2_GS(config, m_gs, 294912000/2, m_intc, m_vu1);
	SONYPS2_DMAC(config, m_dmac, 294912000/2, m_maincpu, m_ram, m_sif, m_gs, m_vu1);
	SONYPS2_SIF(config, m_sif, m_intc);

	SONYPS2_IOP(config, m_iop, XTAL(67'737'600)/2);
	m_iop->set_addrmap(AS_PROGRAM, &ps2sony_state::iop_map);

	config.set_perfect_quantum(m_iop);

	SONYPS2_PAD(config, m_pad[0]);
	SONYPS2_PAD(config, m_pad[1]);
	SONYPS2_MC(config, m_mc);

	SONYIOP_INTC(config, m_iop_intc, m_iop);
	SONYIOP_SIO2(config, m_iop_sio2, m_iop_intc, m_pad[0], m_pad[1], m_mc);
	SONYIOP_CDVD(config, m_iop_cdvd, m_iop_intc);
	SONYIOP_TIMER(config, m_iop_timer, XTAL(67'737'600)/2);
	m_iop_timer->irq().set(FUNC(ps2sony_state::iop_timer_irq));
	SONYIOP_SPU(config, m_iop_spu, XTAL(67'737'600)/2, m_iop, m_iop_intc);

	SONYIOP_DMA(config, m_iop_dma, XTAL(67'737'600)/2, m_iop_intc, m_iop_ram, m_sif, m_iop_spu, m_iop_sio2);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_screen_update(FUNC(ps2sony_state::screen_update));
	screen.set_size(640, 256);
	screen.set_visarea(0, 639, 0, 223);

	PALETTE(config, "palette").set_entries(65536);
}


ROM_START( ps2 )
	// These came from the redump.org "Sony - PlayStation 2 - BIOS Datfile" (version 2017-10-26)
	ROM_REGION32_LE(0x400000, "bios", 0)
	ROM_DEFAULT_BIOS( "scph10000_t" )
	ROM_SYSTEM_BIOS( 0, "scph10000_t", "SCPH-10000 (Version 5.0 01/17/00 T)" )
	ROMX_LOAD( "ps2-0100j-20000117.bin", 0x0000, 0x400000, CRC(b7ef81a9) SHA1(aea061e6e263fdcc1c4fdbd68553ef78dae74263), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "dtlh10000_t_old", "DTL-H10000 (Version 5.0 02/17/00 T)" )
	ROMX_LOAD( "ps2-0101jd-20000217.bin", 0x0000, 0x400000, CRC(4f8b4205) SHA1(16f4a284d0e760ee13a2aff2f7dda928255e3080), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "scph1000015000_t", "SCPH-10000/SCPH-15000 (Version 5.0 02/17/00 T)" )
	ROMX_LOAD( "ps2-0101j-20000217.bin", 0x0000, 0x400000, CRC(211dfb6a) SHA1(916e02431bcd73140504da3355c9598143b77e11), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "dtlh10000_t", "DTL-H10000 (Version 5.0 02/24/00 T)" )
	ROMX_LOAD( "ps2-0101xd-20000224.bin", 0x0000, 0x400000, CRC(2fef9faf) SHA1(4440b246bfde7bb31002c584a76c6ef384908e84), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "dtlh30001_a", "DTL-H30001 (Version 5.0 07/27/00 A)" )
	ROMX_LOAD( "ps2-0110ad-20000727.bin", 0x0000, 0x400000, CRC(795578c1) SHA1(339c646cf0699268552df5b05f18f0a03a9f55ff), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "scph30001_a_old", "SCPH-30001 (Version 5.0 07/27/00 A)" )
	ROMX_LOAD( "ps2-0110a-20000727.bin", 0x0000, 0x400000, CRC(9678ad6a) SHA1(20f6ce6693cf97e9494f8f0227f2b7988ffaf961), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6, "scph30001_a", "SCPH-30001 (Version 5.0 09/02/00 A)" )
	ROMX_LOAD( "ps2-0120a-20000902.bin", 0x0000, 0x400000, CRC(1ae71e5d) SHA1(dbc2318a1029347b5af3a0c74b0bdf88d19efee6), ROM_BIOS(6) )
	ROM_SYSTEM_BIOS( 7, "dtlh30002_e", "DTL-H30002 (Version 5.0 09/02/00 E)" )
	ROMX_LOAD( "ps2-0120ed-20000902.bin", 0x0000, 0x400000, CRC(25495aa7) SHA1(3bb1eecd618ab5c973c7bc53671a4475a02e1d5b), ROM_BIOS(7) )
	ROM_SYSTEM_BIOS( 8, "scph30002_e_old", "SCPH-30002/SCPH-30003/SCPH-30004 (Version 5.0 09/02/00 E)" )
	ROMX_LOAD( "ps2-0120e-20000902.bin", 0x0000, 0x400000, CRC(7b08c33b) SHA1(274c05fec654913a3f698d4b0d592085866a2cbd), ROM_BIOS(8) )
	ROM_SYSTEM_BIOS( 9, "scph18000_3_j", "SCPH-18000 (GH-003) (Version 5.0 10/27/00 J)" )
	ROMX_LOAD( "ps2-0120j-20001027-185015.bin", 0x0000, 0x400000, CRC(9b096622) SHA1(e481079eca752225555f0c26d14c9d0f94d9a8e9), ROM_BIOS(9) )
	ROM_SYSTEM_BIOS( 10, "scph18000_8_j", "SCPH-18000 (GH-008) (Version 5.0 10/27/00 J)" )
	ROMX_LOAD( "ps2-0120j-20001027-191435.bin", 0x0000, 0x400000, CRC(c1ccf3f6) SHA1(a9f5d8ed56cfff18add1b599010493461fa02448), ROM_BIOS(10) )
	ROM_SYSTEM_BIOS( 11, "dtlh30101_a", "DTL-H30101 (Version 5.0 12/28/00 A)" )
	ROMX_LOAD( "ps2-0150ad-20001228.bin", 0x0000, 0x400000, CRC(0380c2ce) SHA1(7284b9d16df9935afc384318e024c87ef0574fe5), ROM_BIOS(11) )
	ROM_SYSTEM_BIOS( 12, "scph30_a", "SCPH-30001/SCPH-35001 (Version 5.0 12/28/00 A)" )
	ROMX_LOAD( "ps2-0150a-20001228.bin", 0x0000, 0x400000, CRC(bce74746) SHA1(5af5b5077d84a9c037ebe12bfab8a38b31d8a543), ROM_BIOS(12) )
	ROM_SYSTEM_BIOS( 13, "scph30002_e", "SCPH-30002/SCPH-30003/SCPH-30004/SCHP-35002/SCPH-35003/SCPH-35004 (Version 5.0 12/28/00 E)" )
	ROMX_LOAD( "ps2-0150e-20001228.bin", 0x0000, 0x400000, CRC(1559fd43) SHA1(e22ef231faf3661edd92f2ee449a71297c82a092), ROM_BIOS(13) )
	ROM_SYSTEM_BIOS( 14, "dtlh30000_j", "DTL-H30000 (Version 5.0 01/18/01 J)" )
	ROMX_LOAD( "ps2-0150jd-20010118.bin", 0x0000, 0x400000, CRC(0b3ec2bc) SHA1(334e029fc7fd50222a399c50384ff42732652259), ROM_BIOS(14) )
	ROM_SYSTEM_BIOS( 15, "scph30000_j_old", "SCPH-30000 (Version 5.0 01/18/01 J)" )
	ROMX_LOAD( "ps2-0150j-20010118.bin", 0x0000, 0x400000, CRC(4fc3b495) SHA1(d6f365a0f07cd04ed28108e6ec5076e2f81e5f72), ROM_BIOS(15) )
	ROM_SYSTEM_BIOS( 16, "scph30001r_a_b", "SCPH-30001R (Version 5.0 04/27/01 A)" )
	ROMX_LOAD( "ps2-0160a-20010427.bin", 0x0000, 0x400000, CRC(4008ac18) SHA1(7331a40b4b4feb1b3f0f77b013b6d38483577baa), ROM_BIOS(16) )
	ROM_SYSTEM_BIOS( 17, "scph30000_j", "SCPH-30000 (Version 5.0 04/27/01 J)" )
	ROMX_LOAD( "ps2-0160j-20010427.bin", 0x0000, 0x400000, CRC(c268ef47) SHA1(e525a0c900e37acf0ae5a655d82a0abcb07c6f1f), ROM_BIOS(17) )
	ROM_SYSTEM_BIOS( 18, "scph30001r_a_a", "SCPH-30001R (Version 5.0 07/04/01 A)" )
	ROMX_LOAD( "ps2-0160a-20010704.bin", 0x0000, 0x400000, CRC(c506c693) SHA1(ce92e8e8c88665f2f645a9522e337823d47a914a), ROM_BIOS(18) )
	ROM_SYSTEM_BIOS( 19, "scph30002r_e_old", "SCPH-30002R/SCPH-30003R/SCPH-30004R (Version 5.0 07/04/01 E)" )
	ROMX_LOAD( "ps2-0160e-20010704.bin", 0x0000, 0x400000, CRC(f1ac735f) SHA1(3cbd048e437c785b5a05a0feced00117a8a42545), ROM_BIOS(19) )
	ROM_SYSTEM_BIOS( 20, "scph30001r_a", "SCPH-30001R (Version 5.0 10/04/01 A)" )
	ROMX_LOAD( "ps2-0160a-20011004.bin", 0x0000, 0x400000, CRC(a01ec625) SHA1(d257bce6ecaf3bafb704c75a1b4741b910bd2d49), ROM_BIOS(20) )
	ROM_SYSTEM_BIOS( 21, "scph30002r_e", "SCPH-30002R/SCPH-30003R/SCPH-30004R (Version 5.0 10/04/01 E)" )
	ROMX_LOAD( "ps2-0160e-20011004.bin", 0x0000, 0x400000, CRC(82aa5055) SHA1(ee34c3a87c53c75ca2a37d77b0042ca24d07831f), ROM_BIOS(21) )
	ROM_SYSTEM_BIOS( 22, "scph30006r_j", "SCPH-30006R (Version 5.0 07/30/01 J)" )
	ROMX_LOAD( "ps2-0160h-20010730.bin", 0x0000, 0x400000, CRC(75f83c67) SHA1(ba15dcf7aac13864c08222037e9321d7468c87d1), ROM_BIOS(22) )
	ROM_SYSTEM_BIOS( 23, "scph39001_a", "SCPH-39001 (Version 5.0 02/07/02 A)" )
	ROMX_LOAD( "ps2-0160a-20020207.bin", 0x0000, 0x400000, CRC(a19e0bf5) SHA1(f9a5d629a036b99128f7cb530c6e3ca016e9c8b7), ROM_BIOS(23) )
	ROM_SYSTEM_BIOS( 24, "scph39002_e", "SCPH-39002/SCPH-39003/SCPH-39004 (Version 5.0 03/19/02 E)" )
	ROMX_LOAD( "ps2-0160e-20020319.bin", 0x0000, 0x400000, CRC(2fe21e4d) SHA1(bff2902bd0ce9729a060581132541e9fd1a9fab6), ROM_BIOS(24) )
	ROM_SYSTEM_BIOS( 25, "scph39006_j", "SCPH-39006 (Version 5.0 04/26/02 J)" )
	ROMX_LOAD( "ps2-0160h-20020426.bin", 0x0000, 0x400000, CRC(3355623e) SHA1(e3a74125c426bcacabca00b513fab928665c8846), ROM_BIOS(25) )
	ROM_SYSTEM_BIOS( 26, "scph50000_j", "SCPH-50000 (Version 5.0 02/06/03 J)" )
	ROMX_LOAD( "ps2-0170j-20030206.bin", 0x0000, 0x400000, CRC(9457f64e) SHA1(d812ac65c357d392396ca9edee812dc41bed8bde), ROM_BIOS(26) )
	ROM_SYSTEM_BIOS( 27, "dtlh50002_e", "DTL-H50002 (Version 5.0 02/27/03 E)" )
	ROMX_LOAD( "ps2-0170ed-20030227.bin", 0x0000, 0x400000, CRC(970a9c56) SHA1(e220bb282378c1f48ea1b585b3675e51a6dca572), ROM_BIOS(27) )
	ROM_SYSTEM_BIOS( 28, "scph50002_e_old", "SCPH-50002/SCPH-50003/SCPH-50004 (Version 5.0 02/27/03 E)" )
	ROMX_LOAD( "ps2-0170e-20030227.bin", 0x0000, 0x400000, CRC(51b5fb8b) SHA1(ad15bd7eabd5bd81ba011516a5be44947d6641aa), ROM_BIOS(28) )
	ROM_SYSTEM_BIOS( 29, "dtlh50001_a", "DTL-H50001 (Version 5.0 03/25/03 A)" )
	ROMX_LOAD( "ps2-0170ad-20030325.bin", 0x0000, 0x400000, CRC(0e1ece79) SHA1(c5bc6e893b4c43d528142e56c96073024de64157), ROM_BIOS(29) )
	ROM_SYSTEM_BIOS( 30, "scph50001_a_old", "SCPH-50001 (Version 5.0 03/25/03 A)" )
	ROMX_LOAD( "ps2-0170a-20030325.bin", 0x0000, 0x400000, CRC(9a99e3f4) SHA1(d269d1ed513227f3ef7133c76cf1b3a64f97b15d), ROM_BIOS(30) )
	ROM_SYSTEM_BIOS( 31, "dtl50009_j", "DTL-50009 (Version 5.0 02/24/03 J)" )
	ROMX_LOAD( "ps2-0180cd-20030224.bin", 0x0000, 0x400000, CRC(8c1a04cf) SHA1(2de87767008fc4a303af64a46251156e965d9065), ROM_BIOS(31) )
	ROM_SYSTEM_BIOS( 32, "desr5000_j", "DESR-5000/DESR-5100/DESR-7000/DESR-7100 (Version 5.0 10/28/03 J)" )
	ROMX_LOAD( "ps2-0180j-20031028.bin", 0x0000, 0x400000, CRC(585fd27c) SHA1(aa4a35c14ee342cf7a03b1dde294ca10e64889e1), ROM_BIOS(32) )
	ROM_SYSTEM_BIOS( 33, "scph50001_a", "SCPH-50001 (Version 5.0 06/23/03 A)" )
	ROMX_LOAD( "ps2-0190a-20030623.bin", 0x0000, 0x400000, CRC(b3e87709) SHA1(c74d92a2952a2912b6698cbcf7742adac8f784d3), ROM_BIOS(33) )
	ROM_SYSTEM_BIOS( 34, "scph50002_e", "SCPH-50002/SCPH-50003/SCPH-50004 (Version 5.0 06/23/03 E)" )
	ROMX_LOAD( "ps2-0190e-20030623.bin", 0x0000, 0x400000, CRC(1752a52e) SHA1(18b9ba833c469c4683676cc20da5124080d980bb), ROM_BIOS(34) )
	ROM_SYSTEM_BIOS( 35, "scph50006_j", "SCPH-50006 (Version 5.0 06/23/03 J)" )
	ROMX_LOAD( "ps2-0190h-20030623.bin", 0x0000, 0x400000, CRC(36b0580c) SHA1(3e18fefb5b5a59046baf25a3f13da1e3a40b92be), ROM_BIOS(35) )
	ROM_SYSTEM_BIOS( 36, "scph50008_e", "SCPH-50008 (Version 5.0 06/23/03 E)" )
	ROMX_LOAD( "ps2-0190r-20030623.bin", 0x0000, 0x400000, CRC(25f6212a) SHA1(34a81db03ab617fbfdd7f9b861692dd2ecd57b82), ROM_BIOS(36) )
	ROM_SYSTEM_BIOS( 37, "scph50009_j", "SCPH-50009 (Version 5.0 06/23/03 J)" )
	ROMX_LOAD( "ps2-0190c-20030623.bin", 0x0000, 0x400000, CRC(d2347ee7) SHA1(92d9eb4b11cef97bb69a275b2851b72f7b0023d6), ROM_BIOS(37) )
	ROM_SYSTEM_BIOS( 38, "scph70000_j", "SCPH-70000 (Version 5.0 06/14/04 J)" )
	ROMX_LOAD( "ps2-0200j-20040614.bin", 0x0000, 0x400000, CRC(2f314730) SHA1(224ab5704ab719edeb05ca1d835812252c97c1b3), ROM_BIOS(38) )
	ROM_SYSTEM_BIOS( 39, "scph70001_a", "SCPH-70001/SCPH-70011/SCPH-70012 (Version 5.0 06/14/04 A)" )
	ROMX_LOAD( "ps2-0200a-20040614.bin", 0x0000, 0x400000, CRC(7ebd68de) SHA1(7a62e5f48603582707e9898eb055ea3eaee50d4c), ROM_BIOS(39) )
	ROM_SYSTEM_BIOS( 40, "scph70002_e", "SCPH-70002/SCPH-70003/SCPH-70004/SCPH-70008 (Version 5.0 06/14/04 E)" )
	ROMX_LOAD( "ps2-0200e-20040614.bin", 0x0000, 0x400000, CRC(6f8e3c29) SHA1(434bc0b4eb4827da0773ec0795aadc5162569a07), ROM_BIOS(40) )
	ROM_SYSTEM_BIOS( 41, "scph70006_j", "SCPH-70006 (Version 5.0 06/14/04 J)" )
	ROMX_LOAD( "ps2-0200h-20040614.bin", 0x0000, 0x400000, CRC(b57201bf) SHA1(7f8e812cab7c7393c85eac6c42661e1fd0a642df), ROM_BIOS(41) )
	ROM_SYSTEM_BIOS( 42, "desr5500_j", "DESR-5500/DESR-7500 (Version 5.0 09/17/04 J)" )
	ROMX_LOAD( "ps2-0210j-20040917.bin", 0x0000, 0x400000, CRC(55710d11) SHA1(bbb1af3085e77599691ec430d147810157da934f), ROM_BIOS(42) )
	ROM_SYSTEM_BIOS( 43, "scph75001_a", "SCPH-75001 (Version 5.0 06/20/05 A)" )
	ROMX_LOAD( "ps2-0220a-20050620.bin", 0x0000, 0x400000, CRC(d305a97a) SHA1(48d0445dffd1e879c7ae752c5166ec3101921555), ROM_BIOS(43) )
	ROM_SYSTEM_BIOS( 44, "scph75002_e", "SCPH-75002/SCPH-75003/SCPH-75004/SCPH-75008 (Version 5.0 06/20/05 E)" )
	ROMX_LOAD( "ps2-0220e-20050620.bin", 0x0000, 0x400000, CRC(e2862e39) SHA1(929a85e974faf4b40d0a7785023b758402c43bd9), ROM_BIOS(44) )
	ROM_SYSTEM_BIOS( 45, "scph77001_a", "SCPH-77001 (Version 5.0 02/10/06 A)" )
	ROMX_LOAD( "ps2-0220a-20060210.bin", 0x0000, 0x400000, CRC(1279fce9) SHA1(92e488d5b2705e4cca83d4d1efbc421012faf83e), ROM_BIOS(45) )
	ROM_SYSTEM_BIOS( 46, "scph77002_e", "SCPH-77002/SCPH-77003/SCPH-77004/SCPH-77008 (Version 5.0 02/10/06 E)" )
	ROMX_LOAD( "ps2-0220e-20060210.bin", 0x0000, 0x400000, CRC(23fa7baa) SHA1(28ad756d0cfd1e7b2e2de3de5d9e14207ee89761), ROM_BIOS(46) )
	ROM_SYSTEM_BIOS( 47, "scph77006_j", "SCPH-77006 (Version 5.0 02/10/06 J)" )
	ROMX_LOAD( "ps2-0220h-20060210.bin", 0x0000, 0x400000, CRC(23001fff) SHA1(fce2a24e5e0400cc6d98c08f426405d19173813e), ROM_BIOS(47) )
	ROM_SYSTEM_BIOS( 48, "scph79001_a", "SCPH-79001/SCPH-90001 (Version 5.0 09/05/06 A)" )
	ROMX_LOAD( "ps2-0220a-20060905.bin", 0x0000, 0x400000, CRC(1c17eafc) SHA1(8361d615cc895962e0f0838489337574dbdc9173), ROM_BIOS(48) )
	ROM_SYSTEM_BIOS( 49, "scph79002_e", "SCPH-79002/SCPH-79003/SCPH-79004/SCPH-79008/SCPH-90002/SCPH-90003/SCPH-90004 (Version 5.0 09/05/06 E)" )
	ROMX_LOAD( "ps2-0220e-20060905.bin", 0x0000, 0x400000, CRC(2d946dbf) SHA1(da5aacead2fb55807d6d4e70b1f10f4fdcfd3281), ROM_BIOS(49) )
	ROM_SYSTEM_BIOS( 50, "scph79006_j", "SCPH-79006/SCPH-90006 (Version 5.0 09/05/06 J)" )
	ROMX_LOAD( "ps2-0220h-20060905.bin", 0x0000, 0x400000, CRC(2d6e09ea) SHA1(a5a2ee0dd9a86ca35b94e97ca92476a584f755bf), ROM_BIOS(50) )
	ROM_SYSTEM_BIOS( 51, "scph90001_a", "SCPH-90001/SCPH-90010 (Version 5.0 02/20/08 A)" )
	ROMX_LOAD( "ps2-0230a-20080220.bin", 0x0000, 0x400000, CRC(286897c2) SHA1(f9229fe159d0353b9f0632f3fdc66819c9030458), ROM_BIOS(51) )
	ROM_SYSTEM_BIOS( 52, "scph90002_e", "SCPH-90002/SCPH-90003/SCPH-90004 (Version 5.0 02/20/08 E)" )
	ROMX_LOAD( "ps2-0230e-20080220.bin", 0x0000, 0x400000, CRC(19eb1081) SHA1(9915b5ba56798f4027ac1bd8d10abe0c1c9c326a), ROM_BIOS(52) )
	ROM_SYSTEM_BIOS( 53, "kdl22px300_j", "KDL-22PX300 (Version 5.0 04/15/10 J)" )
	ROMX_LOAD( "ps2-0250j-20100415.bin", 0x0000, 0x400000, CRC(4e8c160c) SHA1(4b5ef16b67e3b523d28ed2406106cb80470a06d0), ROM_BIOS(53) )

	// These came from unknown sources and are of unknown quality
	ROM_SYSTEM_BIOS( 54, "unknown0", "Unknown0" )
	ROMX_LOAD( "scph-30004r_bios_v6_eur_160.bin", 0x000000, 0x400000, CRC(9386a740) SHA1(8fa040852d4b8688f0c84bcfffc65eb208f2b432), ROM_BIOS(54) )
	ROM_SYSTEM_BIOS( 55, "unknown1", "Unknown1" )
	ROMX_LOAD( "scph39004.bin", 0x000000, 0x400000, CRC(1f2a283c) SHA1(004cc467439f053d5a1fcf4d1b7c13338ce63403), ROM_BIOS(55) )
	ROM_SYSTEM_BIOS( 56, "unknown2", "Unknown2" )
	ROMX_LOAD( "scph50003.bin", 0x000000, 0x400000, CRC(a5860b09) SHA1(003f9bdae45a04c5eb28689813e818566a8c4610), ROM_BIOS(56) )
	ROM_SYSTEM_BIOS( 57, "unknown3", "Unknown3" )
	ROMX_LOAD( "scph_bios_v9_eur_190.bin", 0x000000, 0x400000, CRC(bfe41270) SHA1(dbd7f4d41d54e4f0bf3c4042bb42a42d5d4ef95e), ROM_BIOS(57) )
	ROM_SYSTEM_BIOS( 58, "unknown4", "Unknown4" )
	ROMX_LOAD( "scph_bios_v10_chn_190.bin", 0x000000, 0x400000, CRC(40d6c676) SHA1(b7548a92bd2caa9d60cbc7f79573a5d510f88012), ROM_BIOS(58) )
	ROM_SYSTEM_BIOS( 59, "unknown5", "Unknown5" )
	ROMX_LOAD( "scph_bios_v12_rus_200.bin", 0x000000, 0x400000, CRC(92aa71a2) SHA1(cce6fac0f7e682ad167e1e828b2d53192c3d5051), ROM_BIOS(59) )
	ROM_SYSTEM_BIOS( 60, "unknown6", "Unknown6" )
	ROMX_LOAD( "scph_bios_v15_jap_220.bin", 0x000000, 0x400000, CRC(493c1e58) SHA1(d9a7537fa463fcdd3e270af14a93731736cafc4a), ROM_BIOS(60) )

	// These came from Guru
	ROM_SYSTEM_BIOS( 61, "scph50000_j2", "SCPH-50000 (Version 5.0 08/22/03 J)" )
	ROMX_LOAD( "ps2-0190j-20030822.bin", 0x000000, 0x400000, CRC(79d60546) SHA1(0ea98a25a32145dda514de2f0d4bfbbd806bd00c), ROM_BIOS(61) )
	ROM_SYSTEM_BIOS( 62, "scph70002_a", "SCPH-70002/SCPH-75002 (Version 5.0 06/14/04 A)" )
	ROMX_LOAD( "ps2-0200a-20040614_alt.bin", 0x000000, 0x400000, CRC(3e0aa788) SHA1(597841bfea6e334f5ec4116299091ae2ed3da479), ROM_BIOS(62) )
ROM_END

} // anonymous namespace


CONS( 2000, ps2, 0, 0, ps2sony, ps2sony, ps2sony_state, empty_init, "Sony", "PlayStation 2", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



psion.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

        Psion Organiser II series

        TODO:
        - dump CGROM of the HD44780
        - emulate other devices in slot C (Comms Link, Printer, etc.)

        Note:
        - 4 lines display has an custom LCD controller derived from an HD66780
        - NVRAM works only if the machine is turned off (with OFF menu) before closing MAME
        - psion1 goes into standby right after a cold boot, so press the ON button

        More info:
            http://archive.psion2.org/org2/techref/index.htm

****************************************************************************/


#include "emu.h"

#include "psion_pack.h"
#include "cpu/m6800/m6801.h"
#include "machine/nvram.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "video/hd44780.h"
#include "sound/beep.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class psion_state : public driver_device
{
public:
	psion_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "hd44780")
		, m_screen(*this, "screen")
		, m_beep(*this, "beeper")
		, m_pack(*this, "pack%u", 1U)
		, m_topslot(*this, "topslot")
		, m_nvram(*this, "nvram")
		, m_ram(*this, "ram")
		, m_kb_lines(*this, "K%u", 1U)
		, m_kb_on(*this, "ON")
		, m_battery(*this, "BATTERY")
	{ }

	void psion_base(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(psion_on);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<hd6301x_cpu_device> m_maincpu;
	required_device<hd44780_device> m_lcdc;
	required_device<screen_device> m_screen;
	required_device<beep_device> m_beep;
	required_device_array<datapack_device, 2> m_pack;
	optional_device<datapack_device> m_topslot;
	required_device<nvram_device> m_nvram;
	required_device<ram_device> m_ram;
	required_ioport_array<7> m_kb_lines;
	required_ioport m_kb_on;
	required_ioport m_battery;

	uint8_t kb_read();
	void port2_w(offs_t offset, uint8_t data, uint8_t ddr);
	uint8_t port2_r();
	uint8_t port5_r();
	void port6_w(uint8_t data);
	uint8_t port6_r();

	void psion_palette(palette_device &palette) const;
	TIMER_DEVICE_CALLBACK_MEMBER(nmi_timer);

	uint16_t m_kb_counter = 0;
	bool m_nmi_enable = false;
	uint8_t m_pulse_enable = 0;
};


class psion1_state : public psion_state
{
public:
	psion1_state(const machine_config &mconfig, device_type type, const char *tag)
		: psion_state(mconfig, type, tag)
	{ }

	void psion1(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	HD44780_PIXEL_UPDATE(psion1_pixel_update);

	void psion1_mem(address_map &map) ATTR_COLD;

	uint8_t reset_kb_counter_r();
	uint8_t inc_kb_counter_r();
	uint8_t switchoff_r();
};


class psion2_state : public psion_state
{
public:
	psion2_state(const machine_config &mconfig, device_type type, const char *tag)
		: psion_state(mconfig, type, tag)
		, m_rambank1(*this, "rambank1")
		, m_rambank2(*this, "rambank2")
		, m_rombank1(*this, "rombank1")
		, m_rombank2(*this, "rombank2")
	{ }

	void psion2(machine_config &config);
	void psioncm(machine_config &config);
	void psionxp(machine_config &config);
	void psionla(machine_config &config);
	void psionlz(machine_config &config);
	void psionlz64(machine_config &config);
	void psionp200(machine_config &config);
	void psionp350(machine_config &config);
	void psionp432(machine_config &config);
	void psionp464(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_memory_bank m_rambank1;
	optional_memory_bank m_rambank2;
	required_memory_bank m_rombank1;
	optional_memory_bank m_rombank2;

	HD44780_PIXEL_UPDATE(lz_pixel_update);

	void io_rw(uint16_t offset);
	void io_w(offs_t offset, uint8_t data);
	uint8_t io_r(offs_t offset);

	void psioncm_mem(address_map &map) ATTR_COLD;
	void psionxp_mem(address_map &map) ATTR_COLD;
	void psionla_mem(address_map &map) ATTR_COLD;

	// RAM/ROM banks
	uint8_t m_rambank_count = 0;
	uint8_t m_rombank_count = 0;
};


TIMER_DEVICE_CALLBACK_MEMBER(psion_state::nmi_timer)
{
	if (m_nmi_enable)
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}

uint8_t psion_state::kb_read()
{
	uint8_t data = 0x7c;

	if (m_kb_counter)
	{
		for (int line = 0; line < 7; line++)
			if (m_kb_counter == (0x7f & ~(1 << line)))
				data = m_kb_lines[line]->read();
	}
	else
	{
		// Read all the input lines
		for (int line = 0; line < 7; line++)
			data &= m_kb_lines[line]->read();
	}

	return data & 0x7c;
}


void psion_state::port2_w(offs_t offset, uint8_t data, uint8_t ddr)
{
	/* datapack i/o data bus */
	m_pack[0]->data_w(data & ddr);
	m_pack[1]->data_w(data & ddr);

	if (m_topslot)
		m_topslot->data_w(data & ddr);
}

uint8_t psion_state::port2_r()
{
	/* datapack i/o data bus */
	uint8_t data = m_pack[0]->data_r() | m_pack[1]->data_r();

	if (m_topslot)
		data |= m_topslot->data_r();

	return data;
}

uint8_t psion_state::port5_r()
{
	/*
	x--- ---- ON key active high
	-xxx xx-- keys matrix active low
	---- --x- pulse
	---- ---x battery status
	*/
	return kb_read() | m_battery->read() | m_kb_on->read() | (m_kb_counter == 0x7ff)<<1 | m_pulse_enable<<1;
}

void psion_state::port6_w(uint8_t data)
{
	/*
	datapack control lines
	x--- ---- slot on/off
	-x-- ---- slot 3
	--x- ---- slot 2
	---x ---- slot 1
	---- x--- output enable
	---- -x-- program line
	---- --x- reset line
	---- ---x clock line
	*/
	m_pack[0]->control_w((data & 0x8f) | (data & 0x10));
	m_pack[1]->control_w((data & 0x8f) | ((data & 0x20) >> 1));

	if (m_topslot)
		m_topslot->control_w((data & 0x8f) | ((data & 0x40) >> 2));
}

uint8_t psion_state::port6_r()
{
	/* datapack control lines */
	uint8_t data = (m_pack[0]->control_r() | (m_pack[1]->control_r() & 0x8f)) | ((m_pack[1]->control_r() & 0x10) << 1);

	if (m_topslot)
		data |= (m_topslot->control_r() & 0x8f) | ((m_topslot->control_r() & 0x10) << 2);

	return data;
}

/* Read/Write common */
void psion2_state::io_rw(uint16_t offset)
{
	if (machine().side_effects_disabled())
		return;

	switch (offset & 0xffc0)
	{
	case 0xc0:
		/* switch off, CPU goes into standby mode */
		m_nmi_enable = false;
		m_maincpu->set_input_line(M6801_STBY_LINE, ASSERT_LINE);
		break;
	case 0x100:
		m_pulse_enable = 1;
		break;
	case 0x140:
		m_pulse_enable = 0;
		break;
	case 0x180:
		m_beep->set_state(1);
		break;
	case 0x1c0:
		m_beep->set_state(0);
		break;
	case 0x200:
		m_kb_counter = 0;
		break;
	case 0x240:
		if (offset == 0x260 && (m_rombank_count || m_rambank_count))
		{
			if (m_rambank_count)
				m_rambank2->set_entry(0);

			if (m_rombank_count)
				m_rombank2->set_entry(0);
		}
		else
		{
			m_kb_counter++;
		}
		break;
	case 0x280:
		if (offset == 0x2a0 && m_rambank_count)
		{
			int const rambank = m_rambank2->entry() + 1;
			if (rambank < m_rambank_count)
				m_rambank2->set_entry(rambank);
		}
		else
		{
			m_nmi_enable = true;
		}
		break;
	case 0x2c0:
		if (offset == 0x2e0 && m_rombank_count)
		{
			int const rombank = m_rombank2->entry() + 1;
			if (rombank < m_rombank_count)
				m_rombank2->set_entry(rombank);
		}
		else
		{
			m_nmi_enable = false;
		}
		break;
	}
}

void psion2_state::io_w(offs_t offset, uint8_t data)
{
	switch (offset & 0xffc0)
	{
	case 0x80:
		m_lcdc->write(offset & 0x01, data);
		break;
	default:
		io_rw(offset);
		break;
	}
}

uint8_t psion2_state::io_r(offs_t offset)
{
	switch (offset & 0xffc0)
	{
	case 0x80:
		return m_lcdc->read(offset & 0x01);
	default:
		io_rw(offset);
		break;
	}

	return 0;
}

INPUT_CHANGED_MEMBER(psion_state::psion_on)
{
	/* reset the CPU for resume from standby */
	if (newval && m_maincpu->standby())
	{
		m_maincpu->set_input_line(M6801_STBY_LINE, CLEAR_LINE);
		m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
	}
}

uint8_t psion1_state::reset_kb_counter_r()
{
	if (!machine().side_effects_disabled())
		m_kb_counter = 0;

	return 0;
}

uint8_t psion1_state::inc_kb_counter_r()
{
	if (!machine().side_effects_disabled())
		m_kb_counter++;

	return 0;
}

uint8_t psion1_state::switchoff_r()
{
	if (!machine().side_effects_disabled())
		m_maincpu->set_input_line(M6801_STBY_LINE, ASSERT_LINE);

	return 0;
}

void psion1_state::psion1_mem(address_map &map)
{
	map(0x2000, 0x2001).mirror(0x07fe).rw(m_lcdc, FUNC(hd44780_device::read), FUNC(hd44780_device::write));
	map(0x2800, 0x2800).r(FUNC(psion1_state::reset_kb_counter_r));
	map(0x2e00, 0x2e00).r(FUNC(psion1_state::switchoff_r));
	map(0x3000, 0x3000).r(FUNC(psion1_state::inc_kb_counter_r));
	map(0x4000, 0x47ff).ram().share("ram");
}

void psion2_state::psioncm_mem(address_map &map)
{
	map.unmap_value_low();
	map(0x0100, 0x03ff).rw(FUNC(psion2_state::io_r), FUNC(psion2_state::io_w));
	map(0x2000, 0x3fff).bankrw("rambank1");
	map(0x8000, 0xbfff).bankr("rombank2");
	map(0xc000, 0xffff).bankr("rombank1");
}

void psion2_state::psionxp_mem(address_map &map)
{
	map.unmap_value_low();
	map(0x0100, 0x03ff).rw(FUNC(psion2_state::io_r), FUNC(psion2_state::io_w));
	map(0x2000, 0x5fff).bankrw("rambank1");
	map(0x8000, 0xbfff).bankr("rombank2");
	map(0xc000, 0xffff).bankr("rombank1");
}

void psion2_state::psionla_mem(address_map &map)
{
	map.unmap_value_low();
	map(0x0100, 0x03ff).rw(FUNC(psion2_state::io_r), FUNC(psion2_state::io_w));
	map(0x0400, 0x3fff).bankrw("rambank1");
	map(0x4000, 0x7fff).bankrw("rambank2");
	map(0x8000, 0xbfff).bankr("rombank2");
	map(0xc000, 0xffff).bankr("rombank1");
}


/* Input ports */
INPUT_PORTS_START( psion )
	PORT_START("BATTERY")
	PORT_CONFNAME( 0x01, 0x00, "Battery Status" )
	PORT_CONFSETTING( 0x00, DEF_STR( Normal ) )
	PORT_CONFSETTING( 0x01, "Low Battery" )

	PORT_START("ON")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ON/CLEAR") PORT_CODE(KEYCODE_ESC)  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion_state::psion_on), 0)
INPUT_PORTS_END

INPUT_PORTS_START( psion1 )
	PORT_INCLUDE(psion)

	PORT_START("K1")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2192 [>>]") PORT_CODE(KEYCODE_RIGHT)  // U+2192 = →
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2190 [<<]") PORT_CODE(KEYCODE_LEFT)   // U+2190 = ←
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2193 [FIND]") PORT_CODE(KEYCODE_DOWN) // U+2193 = ↓
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2191 [SAVE]") PORT_CODE(KEYCODE_UP)   // U+2191 = ↑
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MODE/HOME") PORT_CODE(KEYCODE_EQUALS)

	PORT_START("K2")
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S [,]") PORT_CODE(KEYCODE_S)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M [%]") PORT_CODE(KEYCODE_M)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G [=]") PORT_CODE(KEYCODE_G)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A [<]") PORT_CODE(KEYCODE_A)

	PORT_START("K3")
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y [(]") PORT_CODE(KEYCODE_Y)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T [:]") PORT_CODE(KEYCODE_T)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N [$]") PORT_CODE(KEYCODE_N)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H [\"]") PORT_CODE(KEYCODE_H)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B [>]") PORT_CODE(KEYCODE_B)

	PORT_START("K4")
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z [)]") PORT_CODE(KEYCODE_Z)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U [0]") PORT_CODE(KEYCODE_U)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O [1]") PORT_CODE(KEYCODE_O)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I [4]") PORT_CODE(KEYCODE_I)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C [7]") PORT_CODE(KEYCODE_C)

	PORT_START("K5")
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DELETE") PORT_CODE(KEYCODE_DEL)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W [EE]") PORT_CODE(KEYCODE_W)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q [3]") PORT_CODE(KEYCODE_Q)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K [6]") PORT_CODE(KEYCODE_K)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E [9]") PORT_CODE(KEYCODE_E)

	PORT_START("K6")
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("EXECUTE") PORT_CODE(KEYCODE_ENTER)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X [+]") PORT_CODE(KEYCODE_X)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R [-]") PORT_CODE(KEYCODE_R)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L [*]") PORT_CODE(KEYCODE_L)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F [/]") PORT_CODE(KEYCODE_F)

	PORT_START("K7")
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V [.]") PORT_CODE(KEYCODE_V)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P [2]") PORT_CODE(KEYCODE_P)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J [5]") PORT_CODE(KEYCODE_J)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D [8]") PORT_CODE(KEYCODE_D)
INPUT_PORTS_END

INPUT_PORTS_START( psion2 )
	PORT_INCLUDE(psion)

	// Normal keyboard
	PORT_START("K1")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MODE") PORT_CODE(KEYCODE_EQUALS)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2191 [CAP]") PORT_CODE(KEYCODE_UP)   // U+2191 = ↑
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2193 [NUM]") PORT_CODE(KEYCODE_DOWN) // U+2193 = ↓
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2190") PORT_CODE(KEYCODE_LEFT)       // U+2190 = ←
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2192") PORT_CODE(KEYCODE_RIGHT)      // U+2192 = →

	PORT_START("K2")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S [;]") PORT_CODE(KEYCODE_S)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M [,]") PORT_CODE(KEYCODE_M)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G [=]") PORT_CODE(KEYCODE_G)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A [<]") PORT_CODE(KEYCODE_A)

	PORT_START("K3")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T [:]") PORT_CODE(KEYCODE_T)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N [$]") PORT_CODE(KEYCODE_N)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H [\"]") PORT_CODE(KEYCODE_H)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B [>]") PORT_CODE(KEYCODE_B)

	PORT_START("K4")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y [0]") PORT_CODE(KEYCODE_Y)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U [1]") PORT_CODE(KEYCODE_U)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O [4]") PORT_CODE(KEYCODE_O)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I [7]") PORT_CODE(KEYCODE_I)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C [(]") PORT_CODE(KEYCODE_C)

	PORT_START("K5")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W [3]") PORT_CODE(KEYCODE_W)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q [6]") PORT_CODE(KEYCODE_Q)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K [9]") PORT_CODE(KEYCODE_K)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E [%]") PORT_CODE(KEYCODE_E)

	PORT_START("K6")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("EXE") PORT_CODE(KEYCODE_ENTER)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X [+]") PORT_CODE(KEYCODE_X)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R [-]") PORT_CODE(KEYCODE_R)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L [*]") PORT_CODE(KEYCODE_L)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F [/]") PORT_CODE(KEYCODE_F)

	PORT_START("K7")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z [.]") PORT_CODE(KEYCODE_Z)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V [2]") PORT_CODE(KEYCODE_V)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P [5]") PORT_CODE(KEYCODE_P)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J [8]") PORT_CODE(KEYCODE_J)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D [)]") PORT_CODE(KEYCODE_D)
INPUT_PORTS_END

INPUT_PORTS_START( psion2a )
	PORT_INCLUDE(psion)

	// Alpha keyboard
	PORT_START("K1")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MODE") PORT_CODE(KEYCODE_EQUALS)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2191") PORT_CODE(KEYCODE_UP)    // U+2191 = ↑
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2193") PORT_CODE(KEYCODE_DOWN)  // U+2193 = ↓
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2190") PORT_CODE(KEYCODE_LEFT)  // U+2190 = ←
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u2192") PORT_CODE(KEYCODE_RIGHT) // U+2192 = →

	PORT_START("K2")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)

	PORT_START("K3")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("No [N]") PORT_CODE(KEYCODE_N)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)

	PORT_START("K4")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("YES [Y]") PORT_CODE(KEYCODE_Y)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)

	PORT_START("K5")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(". [W]") PORT_CODE(KEYCODE_W)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 [Q]") PORT_CODE(KEYCODE_Q)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 [K]") PORT_CODE(KEYCODE_K)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 [E]") PORT_CODE(KEYCODE_E)

	PORT_START("K6")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("- [X]") PORT_CODE(KEYCODE_X)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 [R]") PORT_CODE(KEYCODE_R)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 [L]") PORT_CODE(KEYCODE_L)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 [F]") PORT_CODE(KEYCODE_F)

	PORT_START("K7")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0 [V]") PORT_CODE(KEYCODE_V)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 [P]") PORT_CODE(KEYCODE_P)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 [J]") PORT_CODE(KEYCODE_J)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 [D]") PORT_CODE(KEYCODE_D)
INPUT_PORTS_END

INPUT_PORTS_START( psion2n )
	PORT_INCLUDE(psion)

	// Numerical keyboard
	PORT_START("K1")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)

	PORT_START("K2")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("EXE") PORT_CODE(KEYCODE_ENTER)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MODE") PORT_CODE(KEYCODE_EQUALS)

	PORT_START("K3")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("K4")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("K5")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("K6")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("K7")
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END


void psion_state::machine_start()
{
	m_nvram->set_base(m_ram->pointer(), m_ram->size());

	save_item(NAME(m_kb_counter));
	save_item(NAME(m_nmi_enable));
	save_item(NAME(m_pulse_enable));
}

void psion2_state::machine_start()
{
	psion_state::machine_start();

	uint8_t *rom_base = memregion("maincpu")->base();
	uint32_t rom_size = memregion("maincpu")->bytes();

	m_rombank_count = 0;
	switch (rom_size)
	{
	case 0x8000: // 32K
		m_rombank1->set_base(rom_base + 0x4000);
		m_rombank2->set_base(rom_base);
		break;

	default: // >= 64K
		m_rombank_count = (rom_size - 0x4000) / 0x4000;

		m_rombank1->set_base(rom_base + 0x4000);
		m_rombank2->configure_entry(0, rom_base);
		m_rombank2->configure_entries(1, m_rombank_count-1, rom_base + 0x8000, 0x4000);
		m_rombank2->set_entry(0);
		break;
	}

	m_rambank_count = 0;
	switch (m_ram->size())
	{
	case 0x2000: // 8K
	case 0x4000: // 16K
		m_rambank1->set_base(m_ram->pointer());
		break;

	case 0x8000: // 32K
		m_rambank1->set_base(m_ram->pointer() + 0x0400);
		m_rambank2->set_base(m_ram->pointer() + 0x4000);
		break;

	default: // >= 64K
		m_rambank_count = (m_ram->size() - 0x4000) / 0x4000;

		m_rambank1->set_base(m_ram->pointer() + 0x0400);
		m_rambank2->configure_entries(0, m_rambank_count, m_ram->pointer() + 0x4000, 0x4000);
		m_rambank2->set_entry(0);
		break;
	}
}

void psion_state::machine_reset()
{
	m_nmi_enable = false;
	m_kb_counter = 0;
	m_pulse_enable = 0;
}

void psion1_state::machine_reset()
{
	psion_state::machine_reset();

	m_nmi_enable = true;
}

void psion2_state::machine_reset()
{
	psion_state::machine_reset();

	if (m_rambank_count)
		m_rambank2->set_entry(0);

	if (m_rombank_count)
		m_rombank2->set_entry(0);
}


HD44780_PIXEL_UPDATE(psion2_state::lz_pixel_update)
{
	if (pos < 40)
	{
		static const uint8_t psion_display_layout[] =
		{
			0x00, 0x01, 0x02, 0x03, 0x28, 0x29, 0x2a, 0x2b, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x2c, 0x2d, 0x2e, 0x2f,
			0x30, 0x31, 0x32, 0x33, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
			0x14, 0x15, 0x16, 0x17, 0x3c, 0x3d, 0x3e, 0x3f, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x40, 0x41, 0x42, 0x43,
			0x44, 0x45, 0x46, 0x47, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
		};

		uint8_t char_pos = psion_display_layout[line*40 + pos];
		bitmap.pix((char_pos / 20) * 9 + y, (char_pos % 20) * 6 + x) = m_maincpu->standby() ? 0 : state;
	}
}

HD44780_PIXEL_UPDATE(psion1_state::psion1_pixel_update)
{
	if (pos < 8 && line < 2)
		bitmap.pix(y, (line * 8 + pos) * 6 + x) = m_maincpu->standby() ? 0 : state;
}

void psion_state::psion_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}

static const gfx_layout psion_charlayout =
{
	5, 8,                   /* 5 x 8 characters */
	256,                    /* 256 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	{ 3, 4, 5, 6, 7},
	{ 0, 8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8},
	8*8                     /* 8 bytes */
};

static GFXDECODE_START( gfx_psion )
	GFXDECODE_ENTRY( "hd44780:cgrom", 0x0000, psion_charlayout, 0, 1 )
GFXDECODE_END


void psion_state::psion_base(machine_config &config)
{
	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_screen_update("hd44780", FUNC(hd44780_device::screen_update));
	m_screen->set_palette("palette");

	PALETTE(config, "palette", FUNC(psion_state::psion_palette), 2);
	GFXDECODE(config, "gfxdecode", "palette", gfx_psion);

	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 3250).add_route(ALL_OUTPUTS, "mono", 1.00);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // RAM

	/* Datapack */
	PSION_DATAPACK(config, m_pack[0]).set_interface("psion_pack");
	PSION_DATAPACK(config, m_pack[1]).set_interface("psion_pack");
}

void psion1_state::psion1(machine_config &config)
{
	psion_base(config);

	HD6301X0(config, m_maincpu, 3.6864_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &psion1_state::psion1_mem);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->in_p2_cb().set(FUNC(psion1_state::port2_r));
	m_maincpu->out_p2_cb().set(FUNC(psion1_state::port2_w));
	m_maincpu->in_p5_cb().set(FUNC(psion1_state::port5_r));
	m_maincpu->in_p6_cb().set(FUNC(psion1_state::port6_r));
	m_maincpu->out_p6_cb().set(FUNC(psion1_state::port6_w));

	RAM(config, m_ram).set_default_size("2K");

	timer_device &nmi_timer(TIMER(config, "nmi_timer"));
	nmi_timer.configure_periodic(FUNC(psion1_state::nmi_timer), attotime::from_msec(500));
	nmi_timer.set_start_delay(attotime::from_seconds(1));

	m_screen->set_size(6*16, 1*8);
	m_screen->set_visarea_full();

	m_lcdc->set_lcd_size(1, 16);
	m_lcdc->set_pixel_update_cb(FUNC(psion1_state::psion1_pixel_update));

	SOFTWARE_LIST(config, "pack_list").set_original("psion1");
}

void psion2_state::psion2(machine_config &config)
{
	psion_base(config);

	HD6303X(config, m_maincpu, 3.6864_MHz_XTAL); // internal operating frequency is 0.9216 MHz
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->in_p2_cb().set(FUNC(psion2_state::port2_r));
	m_maincpu->out_p2_cb().set(FUNC(psion2_state::port2_w));
	m_maincpu->in_p5_cb().set(FUNC(psion2_state::port5_r));
	m_maincpu->in_p6_cb().set(FUNC(psion2_state::port6_r));
	m_maincpu->out_p6_cb().set(FUNC(psion2_state::port6_w));

	RAM(config, m_ram).set_default_size("32K");

	m_screen->set_size(6*16, 9*2);
	m_screen->set_visarea_full();

	m_lcdc->set_lcd_size(2, 16);

	TIMER(config, "nmi_timer").configure_periodic(FUNC(psion2_state::nmi_timer), attotime::from_seconds(1));

	PSION_DATAPACK(config, m_topslot).set_interface("psion_topslot");

	SOFTWARE_LIST(config, "pack_list").set_original("psion2");
}

void psion2_state::psioncm(machine_config &config)
{
	psion2(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &psion2_state::psioncm_mem);

	m_ram->set_default_size("8K");

	subdevice<software_list_device>("pack_list")->set_filter("CM");
}

void psion2_state::psionxp(machine_config &config)
{
	psion2(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &psion2_state::psionxp_mem);

	m_ram->set_default_size("16K");

	subdevice<software_list_device>("pack_list")->set_filter("XP");
}

void psion2_state::psionla(machine_config &config)
{
	psionxp(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &psion2_state::psionla_mem);

	m_ram->set_default_size("32K");
}

void psion2_state::psionp200(machine_config &config)
{
	psionla(config);

	subdevice<software_list_device>("pack_list")->set_filter("POS");
}

void psion2_state::psionp350(machine_config &config)
{
	psionla(config);

	m_ram->set_default_size("96K");

	subdevice<software_list_device>("pack_list")->set_filter("POS");
}

void psion2_state::psionlz(machine_config &config)
{
	psionla(config);

	/* video hardware */
	m_screen->set_size(6*20, 9*4);
	m_screen->set_visarea_full();

	m_lcdc->set_lcd_size(4, 20);
	m_lcdc->set_pixel_update_cb(FUNC(psion2_state::lz_pixel_update));

	subdevice<software_list_device>("pack_list")->set_filter("LZ");
}

void psion2_state::psionlz64(machine_config &config)
{
	psionlz(config);

	m_ram->set_default_size("64K");

	subdevice<software_list_device>("pack_list")->set_filter("LZ,LZ64");
}

void psion2_state::psionp432(machine_config &config)
{
	psionlz(config);

	subdevice<software_list_device>("pack_list")->set_filter("POS");
}

void psion2_state::psionp464(machine_config &config)
{
	psionlz64(config);

	subdevice<software_list_device>("pack_list")->set_filter("POS");
}

/* ROM definition */

ROM_START( psion1 )
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD("psion1.rom", 0x0000, 0x1000, CRC(7e2609c1) SHA1(a3320ea8ac3ab9e0039ee16f7c571731adde5869))
ROM_END

ROM_START( psioncm )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS( "v33" )
	ROM_SYSTEM_BIOS(0, "v24", "CM v2.4 English")
	ROMX_LOAD("24-cm.rom",    0x0000, 0x8000,  CRC(f6798394) SHA1(736997f0db9a9ee50d6785636bdc3f8ff1c33c66), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v26", "CM v2.6 English")
	ROMX_LOAD("26-cm.rom",    0x0000, 0x8000,  CRC(21b7c94c) SHA1(e0a3168c96a3f0b37b8698e86574e40597fe3c62), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v33", "CM v3.3 English")
	ROMX_LOAD("33-cm.rom",    0x0000, 0x8000,  CRC(5c10b167) SHA1(6deea00fe648bddae1d61a22858023bc80277ea0), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v33f", "CM v3.3 French")
	ROMX_LOAD("33-cmf.rom",   0x0000, 0x8000,  CRC(4d626ce2) SHA1(82b96f11a0abfc1931b6022b84733d975ad7ab2b), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "v36f", "CM v3.6 French")
	ROMX_LOAD("36-cmf.rom",   0x0000, 0x8000,  CRC(beabe0f5) SHA1(a5ef3bb92190a257cb0e94d58b2c23935436edeb), ROM_BIOS(4))
ROM_END

ROM_START( psioncmm )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "v37", "CM v3.7 Multilingual")
	ROMX_LOAD("37-cmm.rom",   0x0000, 0x10000, CRC(5be1aa1d) SHA1(34a6d9e2bb4941740b8bcf6b9b46a34b0ae0be5a), ROM_BIOS(0))
ROM_END

ROM_START( psionxp )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS( "v31" )
	ROM_SYSTEM_BIOS(0, "v24", "XP v2.4 English")
	ROMX_LOAD("24-xp.rom",    0x0000, 0x8000,  CRC(3407062c) SHA1(c3818f3f0865cc235905bd7ef6cba325e44fa076), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v26", "XP v2.6 English")
	ROMX_LOAD("26-xp.rom",    0x0000, 0x8000,  CRC(a81db40f) SHA1(af72d94ccee1fa1dade8776bdbd39920665a68b7), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v31", "XP v3.1 English")
	ROMX_LOAD("31-xp.rom",    0x0000, 0x8000,  CRC(cb10b6bf) SHA1(af5e82f62a149ff0dfa6e60414c928649a2abaaa), ROM_BIOS(2))
ROM_END

ROM_START( psionla )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS( "v36" )
	ROM_SYSTEM_BIOS(0, "v30hp", "LA v3.0 Hand Held Products")
	ROMX_LOAD("30-lahp.rom",  0x0000, 0x8000,  CRC(50192528) SHA1(c556d53f70bf5ecae756b2ebfc6d954912316bbe), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v33", "LA v3.3 English")
	ROMX_LOAD("33-la.rom",    0x0000, 0x8000,  CRC(02668ed4) SHA1(e5d4ee6b1cde310a2970ffcc6f29a0ce09b08c46), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v34g", "LA v3.4 German")
	ROMX_LOAD("34-lag.rom",   0x0000, 0x8000,  CRC(13a92c4b) SHA1(dab8bd6a41a5fd509c5ad4b0b0ab80d14f2c421a), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v36", "LA v3.6 English")
	ROMX_LOAD("36-la.rom",    0x0000, 0x8000,  CRC(7442c7f6) SHA1(94f15bd06bd750be70fa4a4ab588237c5a703f65), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "v36f", "LA v3.6 French")
	ROMX_LOAD("36-laf.rom",   0x0000, 0x8000,  CRC(036ef00e) SHA1(98f303273e570e94a1e25a58cf1ffcec0db32165), ROM_BIOS(4))
	ROM_SYSTEM_BIOS(5, "v36g", "LA v3.6 German")
	ROMX_LOAD("36-lag.rom",   0x0000, 0x8000,  CRC(33c6b4d4) SHA1(81d665ce0fa325996d52004655349162093d60a6), ROM_BIOS(5))
	ROM_END

ROM_START( psionlam )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "v37", "LA v3.7 Multilingual")
	ROMX_LOAD("37-lam.dat",   0x0000, 0x10000, CRC(7ee3a1bc) SHA1(c7fbd6c8e47c9b7d5f636e9f56e911b363d6796b), ROM_BIOS(0))
ROM_END

ROM_START( psionp200 )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "v36", "POS 200 v3.6")
	ROMX_LOAD("36-p200.rom",  0x0000, 0x8000,  CRC(4569ef5b) SHA1(8c275474cc6e3f50156f0b6e32121cadd14ea8be), ROM_BIOS(0))
ROM_END

ROM_START( psionp200a )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS( "v36" )
	ROM_SYSTEM_BIOS(0, "v33", "Alpha POS 200 v3.3")
	ROMX_LOAD("33-p200a.rom", 0x0000, 0x8000,  CRC(91e94998) SHA1(e9e8106eb9283d20452697859894aa407cc07bd1), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v36", "Alpha POS 200 v3.6")
	ROMX_LOAD("36-p200a.rom", 0x0000, 0x8000,  CRC(36cceeb7) SHA1(57069812c5a16babfff91dc7d7e0842e5dc68652), ROM_BIOS(1))
ROM_END

ROM_START( psionp250 )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "v36b", "POS 250 v3.6")
	ROMX_LOAD("36-p250.rom",  0x0000, 0x8000,  CRC(235cc76a) SHA1(3229cdff4b049a1fbf9a758ce3abf3fdc9b547c9), ROM_BIOS(0))
ROM_END

ROM_START( psionp350 )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS( "v36" )
	ROM_SYSTEM_BIOS(0, "v36", "POS 350 v3.6")
	ROMX_LOAD("36-p350.rom",  0x0000, 0x8000,  CRC(3a371a74) SHA1(9167210b2c0c3bd196afc08ca44ab23e4e62635e), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v38", "POS 350 v3.8")
	ROMX_LOAD("38-p350.rom",  0x0000, 0x8000,  CRC(1b8b082f) SHA1(a3e875a59860e344f304a831148a7980f28eaa4a), ROM_BIOS(1))
ROM_END

ROM_START( psionlz )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS( "v46" )
	ROM_SYSTEM_BIOS(0, "v42", "LZ v4.2 English, French, German")
	ROMX_LOAD("42-lz.rom",    0x0000, 0x10000, CRC(f2d6ad47) SHA1(ee8315ae872463068d805c6e0b71f62ae8eb65be), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v44", "LZ v4.4 English, French, German")
	ROMX_LOAD("44-lz.rom",    0x0000, 0x10000, CRC(4a0a990b) SHA1(dde0ba69a4a7f02b610ad6bd69a8b8552b060223), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v45", "LZ v4.5 English, French, German")
	ROMX_LOAD("45-lz.rom",    0x0000, 0x10000, CRC(f95d8f39) SHA1(cb64152c2418bf730c89999d1b13c1d1ada1f082), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v45i", "LZ v4.5 English, Spanish, Italian")
	ROMX_LOAD("45-lzi.rom",   0x0000, 0x10000, CRC(202b556d) SHA1(39f061c4b94a1371bfe62484828a8ce424d7315c), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "v45s", "LZ v4.5 English, Swedish, Danish")
	ROMX_LOAD("45-lzs.rom",   0x0000, 0x10000, CRC(2d082d7f) SHA1(fcd00864a0cc617e61997240945ea70a8e9fa211), ROM_BIOS(4))
	ROM_SYSTEM_BIOS(5, "v46", "LZ v4.6 English, French, German")
	ROMX_LOAD("46-lz.rom",    0x0000, 0x10000, CRC(22715f48) SHA1(cf460c81cadb53eddb7afd8dadecbe8c38ea3fc2), ROM_BIOS(5))
	ROM_SYSTEM_BIOS(6, "v46i", "LZ v4.6 English, Spanish, Italian")
	ROMX_LOAD("46-lzi.rom",   0x0000, 0x10000, CRC(ee50b6e9) SHA1(f1821ad179b018d6b22129ef4e8e8b23f452b2f6), ROM_BIOS(6))

	ROM_REGION( 0x1000, "hd44780", 0 )
	ROM_LOAD("psion_lz_charset.bin", 0x0000, 0x1000, BAD_DUMP CRC(44bff6f6) SHA1(aef544548b783d608a7d55456f6c46f421a11ed7))
ROM_END

ROM_START( psionlz64 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS( "v46a" )
	ROM_SYSTEM_BIOS(0, "v42", "LZ64 v4.2 English, French, German")
	ROMX_LOAD("42-lz64.rom",  0x0000, 0x10000, CRC(2df76f5a) SHA1(4255bc2abfc6f6cb666c0464a86d4ac98050268e), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v43", "LZ64 v4.3 English, French, German")
	ROMX_LOAD("43-lz64.rom",  0x0000, 0x10000, CRC(57e7a372) SHA1(46c2da1cfe991c0c1f2486e4aa28388767937ddd), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v44", "LZ64 v4.4 English, French, German")
	ROMX_LOAD("44-lz64.rom",  0x0000, 0x10000, CRC(aa487913) SHA1(5a44390f63fc8c1bc94299ab2eb291bc3a5b989a), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v45", "LZ64 v4.5 English, French, German")
	ROMX_LOAD("45-lz64.rom",  0x0000, 0x10000, CRC(4fbd5d88) SHA1(43f97549d2060840aa6313d526000530f384a08f), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "v46a", "LZ64 v4.6a English, French, German")
	ROMX_LOAD("46a-lz64.rom", 0x0000, 0x10000, CRC(9b0d5a7a) SHA1(f1cdd6ef43cd65ef18e148deca0500f0c1ad2f80), ROM_BIOS(4))
	ROM_SYSTEM_BIOS(5, "v46b", "LZ64 v4.6b English, French, German")
	ROMX_LOAD("46b-lz64.rom", 0x0000, 0x10000, CRC(8d1101e2) SHA1(eddd0c3a2881667a1485b0d66f82f8c7792995c2), ROM_BIOS(5))
	ROM_SYSTEM_BIOS(6, "v46i", "LZ64 v4.6 English, Spanish, Italian")
	ROMX_LOAD("46-lz64i.rom", 0x0000, 0x10000, CRC(c96c7e65) SHA1(1b4af43657bbd3ecd92f370762bde166047b85e2), ROM_BIOS(6))
	ROM_SYSTEM_BIOS(7, "v46s", "LZ64 v4.6 English, Swedish, Danish")
	ROMX_LOAD("46-lz64s.rom", 0x0000, 0x10000, CRC(328d9772) SHA1(7f9e2d591d59ecfb0822d7067c2fe59542ea16dd), ROM_BIOS(7))

	ROM_REGION( 0x1000, "hd44780", 0 )
	ROM_LOAD("psion_lz_charset.bin", 0x0000, 0x1000, BAD_DUMP CRC(44bff6f6) SHA1(aef544548b783d608a7d55456f6c46f421a11ed7))
ROM_END

ROM_START( psionp432 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "v46", "POS432 v4.6")
	ROMX_LOAD("46-p432n.rom",  0x0000, 0x10000, CRC(6683737a) SHA1(2c6bd9938f1b1d762adc8d45802c24f007e3445a), ROM_BIOS(0))

	ROM_REGION( 0x1000, "hd44780", 0 )
	ROM_LOAD("psion_lz_charset.bin", 0x0000, 0x1000, BAD_DUMP CRC(44bff6f6) SHA1(aef544548b783d608a7d55456f6c46f421a11ed7))
ROM_END

ROM_START( psionp464 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "v46", "POS464 v4.6")
	ROMX_LOAD("46-p464.rom",  0x0000, 0x10000, CRC(672a0945) SHA1(d2a6e3fe1019d1bd7ae4725e33a0b9973f8cd7d8), ROM_BIOS(0))

	ROM_REGION( 0x1000, "hd44780", 0 )
	ROM_LOAD("psion_lz_charset.bin", 0x0000, 0x1000, BAD_DUMP CRC(44bff6f6) SHA1(aef544548b783d608a7d55456f6c46f421a11ed7))
ROM_END

} // anonymous namespace


//    YEAR  NAME        PARENT   COMPAT  MACHINE    INPUT    CLASS         INIT        COMPANY  FULLNAME                         FLAGS
COMP( 1984, psion1,     0,       0,      psion1,    psion1,  psion1_state, empty_init, "Psion", "Organiser I",                   MACHINE_NOT_WORKING )
COMP( 1986, psioncm,    0,       0,      psioncm,   psion2,  psion2_state, empty_init, "Psion", "Organiser II CM",               MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1988, psioncmm,   psioncm, 0,      psioncm,   psion2,  psion2_state, empty_init, "Psion", "Organiser II CM Multilingual",  MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1986, psionxp,    psioncm, 0,      psionxp,   psion2,  psion2_state, empty_init, "Psion", "Organiser II XP",               MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1986, psionla,    psioncm, 0,      psionla,   psion2,  psion2_state, empty_init, "Psion", "Organiser II LA",               MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1988, psionlam,   psioncm, 0,      psionla,   psion2,  psion2_state, empty_init, "Psion", "Organiser II LA Multilingual",  MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1987, psionp200,  psioncm, 0,      psionp200, psion2n, psion2_state, empty_init, "Psion", "Organiser II POS 200",          MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1987, psionp200a, psioncm, 0,      psionp200, psion2a, psion2_state, empty_init, "Psion", "Organiser II Alpha POS 200",    MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1988, psionp250,  psioncm, 0,      psionp200, psion2,  psion2_state, empty_init, "Psion", "Organiser II P 250",            MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1988, psionp350,  psioncm, 0,      psionp350, psion2,  psion2_state, empty_init, "Psion", "Organiser II P 350",            MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1989, psionlz,    0,       0,      psionlz,   psion2,  psion2_state, empty_init, "Psion", "Organiser II LZ",               MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1989, psionlz64,  psionlz, 0,      psionlz64, psion2,  psion2_state, empty_init, "Psion", "Organiser II LZ64",             MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1989, psionp432,  psionlz, 0,      psionp432, psion2n, psion2_state, empty_init, "Psion", "Organiser II P 432",            MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1989, psionp464,  psionlz, 0,      psionp464, psion2,  psion2_state, empty_init, "Psion", "Organiser II P 464",            MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )



psion3.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/******************************************************************************

    Psion Series 3

    TODO:
    - DTMF tone generator

******************************************************************************/

#include "emu.h"
#include "cpu/nec/nec.h"
#include "machine/nvram.h"
#include "machine/psion_asic1.h"
#include "machine/psion_asic2.h"
#include "machine/psion_ssd.h"
#include "machine/ram.h"
#include "sound/pcd3311.h"
#include "sound/spkrdev.h"
#include "bus/psion/sibo/slot.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "utf8.h"


namespace {

class psion3_state : public driver_device
{
public:
	psion3_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_asic1(*this, "asic1")
		, m_asic2(*this, "asic2")
		, m_ram(*this, "ram")
		, m_nvram(*this, "nvram")
		, m_palette(*this, "palette")
		, m_keyboard(*this, "COL%u", 0U)
		, m_dtmf(*this, "dtmf")
		, m_speaker(*this, "speaker")
		, m_ssd(*this, "ssd%u", 1U)
		, m_sibo(*this, "sibo")
	{ }

	void psion3(machine_config &config);
	void psion3s(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(key_on);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<psion_asic1_device> m_asic1;
	required_device<psion_asic2_device> m_asic2;
	required_device<ram_device> m_ram;
	required_device<nvram_device> m_nvram;
	required_device<palette_device> m_palette;
	required_ioport_array<10> m_keyboard;
	required_device<pcd3311_device> m_dtmf;
	required_device<speaker_sound_device> m_speaker;
	required_device_array<psion_ssd_device, 2> m_ssd;
	required_device<psion_sibo_slot_device> m_sibo;

	void palette_init(palette_device &palette);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void asic1_map(address_map &map) ATTR_COLD;

	uint8_t port_data_r();
	void port_data_w(uint8_t data);
};


void psion3_state::machine_start()
{
	m_asic1->space(0).install_ram(0, m_ram->mask(), m_ram->pointer());
	m_nvram->set_base(m_ram->pointer(), m_ram->size());
}

void psion3_state::machine_reset()
{
}


void psion3_state::mem_map(address_map &map)
{
	map(0x00000, 0xfffff).rw(m_asic1, FUNC(psion_asic1_device::mem_r), FUNC(psion_asic1_device::mem_w));
}

void psion3_state::io_map(address_map &map)
{
	map(0x0000, 0x001f).rw(m_asic1, FUNC(psion_asic1_device::io_r), FUNC(psion_asic1_device::io_w));
	map(0x0080, 0x008f).rw(m_asic2, FUNC(psion_asic2_device::io_r), FUNC(psion_asic2_device::io_w)).umask16(0x00ff);
	//map(0x0100, 0x01ff).lr8(NAME([]() { return 0xff; })); // w: enable Vcc lines?
	map(0x0200, 0x02ff).w(m_dtmf, FUNC(pcd3311_device::write));
}

void psion3_state::asic1_map(address_map &map)
{
	map(0x00000, 0x7ffff).noprw();
	map(0x80000, 0xfffff).rom().region("flash", 0);
}


static INPUT_PORTS_START( psion3 )
	PORT_START("COL0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)          PORT_CHAR('1')  PORT_CHAR('!')                  PORT_NAME("1 ! Off")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q')  PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)          PORT_CHAR('f')  PORT_CHAR('F')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)          PORT_CHAR('r')  PORT_CHAR('R')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')  PORT_CHAR('<')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)          PORT_CHAR('k')  PORT_CHAR('K')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/')  PORT_CHAR('?')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))                 PORT_NAME(UTF8_RIGHT" End")

	PORT_START("COL1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)          PORT_CHAR('2')  PORT_CHAR('"')  PORT_CHAR('#')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('a')  PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)          PORT_CHAR('v')  PORT_CHAR('V')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)          PORT_CHAR('4')  PORT_CHAR('$')  PORT_CHAR('~')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10)        PORT_CHAR(UCHAR_MAMEKEY(F10))                   PORT_NAME("Help Dial")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)          PORT_CHAR('i')  PORT_CHAR('I')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-')  PORT_CHAR('_')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)                        PORT_NAME("Shift (R)")

	PORT_START("COL2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_CHAR('6')  PORT_CHAR('^')  PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z')  PORT_CHAR('Z')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)          PORT_CHAR('c')  PORT_CHAR('C')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)          PORT_CHAR('5')  PORT_CHAR('%')  PORT_CHAR('\'')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)          PORT_CHAR('m')  PORT_CHAR('M')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8')  PORT_CHAR('?')  PORT_CHAR('}')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('+')  PORT_CHAR('=')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y')  PORT_CHAR('Y')

	PORT_START("COL3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')  PORT_CHAR('>')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11)        PORT_CHAR(UCHAR_MAMEKEY(F11))                   PORT_NAME("Menu")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)          PORT_CHAR('d')  PORT_CHAR('D')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)          PORT_CHAR('t')  PORT_CHAR('T')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)          PORT_CHAR('j')  PORT_CHAR('J')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_CHAR('9')  PORT_CHAR('(')  PORT_CHAR('[')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_CHAR('0')  PORT_CHAR(')')  PORT_CHAR(']')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))                  PORT_NAME(UTF8_LEFT" Home")

	PORT_START("COL4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))                    PORT_NAME(UTF8_UP" Pg Up")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK)   PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))              PORT_NAME("Caps Lock")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)          PORT_CHAR('e')  PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)          PORT_CHAR('g')  PORT_CHAR('G')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)          PORT_CHAR('u')  PORT_CHAR('U')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)          PORT_CHAR('o')  PORT_CHAR('O')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)          PORT_CHAR('p')  PORT_CHAR('P')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))                  PORT_NAME(UTF8_DOWN" Pg Dn")

	PORT_START("COL5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)          PORT_CHAR('h')  PORT_CHAR('H')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)          PORT_CHAR('x')  PORT_CHAR('X')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)          PORT_CHAR('3')  PORT_CHAR(0xa3) PORT_CHAR('\\')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)          PORT_CHAR('b')  PORT_CHAR('B')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7')  PORT_CHAR('&')  PORT_CHAR('{')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)          PORT_CHAR('l')  PORT_CHAR('L')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR('*')  PORT_CHAR(':')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)          PORT_CHAR('n')  PORT_CHAR('N')

	PORT_START("COL6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)          PORT_CHAR('w')  PORT_CHAR('W')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)          PORT_CHAR('s')  PORT_CHAR('S')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))              PORT_NAME("Control")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)                        PORT_NAME("Shift (L)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT)       PORT_CHAR(UCHAR_SHIFT_2)                        PORT_NAME("Psion")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(0x08)                                 PORT_NAME("Delete")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)                                   PORT_NAME("Enter")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')

	PORT_START("COL7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)         PORT_CHAR(UCHAR_MAMEKEY(F2))                    PORT_NAME("Data")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)         PORT_CHAR(UCHAR_MAMEKEY(F5))                    PORT_NAME("Time")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)         PORT_CHAR(UCHAR_MAMEKEY(F8))                    PORT_NAME("Program")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)        PORT_CHAR(0x09)                                 PORT_NAME("Tab")

	PORT_START("COL8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)         PORT_CHAR(UCHAR_MAMEKEY(F3))                    PORT_NAME("Word")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)         PORT_CHAR(UCHAR_MAMEKEY(F6))                    PORT_NAME("World")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0xf8, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)         PORT_CHAR(UCHAR_MAMEKEY(F1))                    PORT_NAME("System")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)         PORT_CHAR(UCHAR_MAMEKEY(F4))                    PORT_NAME("Agenda")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)         PORT_CHAR(UCHAR_MAMEKEY(F7))                    PORT_NAME("Calc")
	PORT_BIT(0xf8, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("ESC")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC)) PORT_NAME("Esc On") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3_state::key_on), 0)
INPUT_PORTS_END


static INPUT_PORTS_START( psion3s )
	PORT_INCLUDE(psion3)

	PORT_MODIFY("COL7")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)         PORT_CHAR(UCHAR_MAMEKEY(F8))                    PORT_NAME("Sheet")
INPUT_PORTS_END


static INPUT_PORTS_START( pocketbk )
	PORT_INCLUDE(psion3)

	PORT_MODIFY("COL1")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10)        PORT_CHAR(UCHAR_MAMEKEY(F10))                   PORT_NAME("Help")

	PORT_MODIFY("COL6")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT)       PORT_CHAR(UCHAR_SHIFT_2)                        PORT_NAME("Acorn")

	PORT_MODIFY("COL7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)         PORT_CHAR(UCHAR_MAMEKEY(F2))                    PORT_NAME("Cards")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)         PORT_CHAR(UCHAR_MAMEKEY(F5))                    PORT_NAME("Time")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)         PORT_CHAR(UCHAR_MAMEKEY(F8))                    PORT_NAME("Key2")

	PORT_MODIFY("COL8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)         PORT_CHAR(UCHAR_MAMEKEY(F3))                    PORT_NAME("Write")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)         PORT_CHAR(UCHAR_MAMEKEY(F6))                    PORT_NAME("Calc")

	PORT_MODIFY("COL9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)         PORT_CHAR(UCHAR_MAMEKEY(F1))                    PORT_NAME("Desktop")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)         PORT_CHAR(UCHAR_MAMEKEY(F4))                    PORT_NAME("Abacus")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)         PORT_CHAR(UCHAR_MAMEKEY(F7))                    PORT_NAME("Key1")
INPUT_PORTS_END


INPUT_CHANGED_MEMBER(psion3_state::key_on)
{
	if (newval)
	{
		m_asic2->on_clr_w(newval);
	}
}


uint8_t psion3_state::port_data_r()
{
	// b0 MainBattery   - 0 Low, 1 Good
	// b1 BackupBattery - 0 Low, 1 Good
	// b2 ?
	// b3 ExternalPower - 0 Yes, 1 No
	// b7 NC
	return 0x03;
}

void psion3_state::port_data_w(uint8_t data)
{
	// b4 ?
	// b5 VOL0
	// b6 VOL1
	logerror("port_data_w: %02x\n", data);
}


void psion3_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(170, 180, 160));
	palette.set_pen_color(1, rgb_t(80, 75, 50));
}


void psion3_state::psion3(machine_config &config)
{
	V30(config, m_maincpu, 7.68_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &psion3_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &psion3_state::io_map);
	m_maincpu->set_irq_acknowledge_callback(m_asic1, FUNC(psion_asic1_device::inta_cb));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_size(240, 80);
	screen.set_visarea_full();
	screen.set_refresh_hz(66);
	screen.set_screen_update(m_asic1, FUNC(psion_asic1_device::screen_update_single));
	screen.set_palette(m_palette);
	PALETTE(config, "palette", FUNC(psion3_state::palette_init), 2);

	PSION_ASIC1(config, m_asic1, 7.68_MHz_XTAL);
	m_asic1->set_screen("screen");
	m_asic1->set_laptop_mode(false);
	m_asic1->set_addrmap(0, &psion3_state::asic1_map);
	m_asic1->int_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_asic1->nmi_cb().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_asic1->frcovl_cb().set(m_asic2, FUNC(psion_asic2_device::frcovl_w));

	PSION_ASIC2(config, m_asic2, 7.68_MHz_XTAL);
	m_asic2->int_cb().set(m_asic1, FUNC(psion_asic1_device::eint3_w));
	m_asic2->nmi_cb().set(m_asic1, FUNC(psion_asic1_device::enmi_w));
	m_asic2->cbusy_cb().set_inputline(m_maincpu, NEC_INPUT_LINE_POLL);
	m_asic2->buz_cb().set(m_speaker, FUNC(speaker_sound_device::level_w));
	m_asic2->buzvol_cb().set([this](int state) { m_speaker->set_output_gain(ALL_OUTPUTS, state ? 1.0 : 0.25); });
	m_asic2->col_cb().set([this](uint8_t data) { return m_keyboard[data]->read(); });
	m_asic2->read_pd_cb().set(FUNC(psion3_state::port_data_r));
	m_asic2->write_pd_cb().set(FUNC(psion3_state::port_data_w));
	m_asic2->data_r<1>().set(m_ssd[0], FUNC(psion_ssd_device::data_r));      // SSD Pack 1
	m_asic2->data_w<1>().set(m_ssd[0], FUNC(psion_ssd_device::data_w));
	m_asic2->data_r<2>().set(m_ssd[1], FUNC(psion_ssd_device::data_r));      // SSD Pack 2
	m_asic2->data_w<2>().set(m_ssd[1], FUNC(psion_ssd_device::data_w));
	m_asic2->data_r<7>().set(m_sibo, FUNC(psion_sibo_slot_device::data_r));  // SIBO expansion port
	m_asic2->data_w<7>().set(m_sibo, FUNC(psion_sibo_slot_device::data_w));

	RAM(config, m_ram).set_default_size("256K").set_extra_options("128K");
	NVRAM(config, "nvram", nvram_device::DEFAULT_NONE);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00); // Piezo buzzer

	PCD3311(config, m_dtmf, 3'580'000).add_route(ALL_OUTPUTS, "mono", 0.25); // PCD3311CT

	PSION_SSD(config, m_ssd[0]);
	m_ssd[0]->door_cb().set(m_asic2, FUNC(psion_asic2_device::dnmi_w));
	PSION_SSD(config, m_ssd[1]);
	m_ssd[1]->door_cb().set(m_asic2, FUNC(psion_asic2_device::dnmi_w));

	PSION_SIBO_SLOT(config, m_sibo, psion_sibo_devices, nullptr);
	m_sibo->int_cb().set(m_asic2, FUNC(psion_asic2_device::sds_int_w));

	SOFTWARE_LIST(config, "ssd_list").set_original("psion_ssd").set_filter("S3");
}

void psion3_state::psion3s(machine_config &config)
{
	psion3(config);

	m_ram->set_default_size("256K").set_extra_options("");
}


ROM_START(psion3)
	ROM_REGION16_LE(0x80000, "flash", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "177f", "V1.77F/ENG 221091")
	ROMX_LOAD("3504-3002-01_19-11-91v1.77f_eng.bin", 0x00000, 0x20000, CRC(73ba99fa) SHA1(1b3f7b2da9cc2f189e88a9aa01fdb6fad7598925), ROM_BIOS(0))
	ROMX_LOAD("3504-3001-01_19-11-91v1.77f_eng.bin", 0x40000, 0x40000, CRC(e868c250) SHA1(48cce7dd219fb776bffe247c48ba070a89bff121), ROM_BIOS(0))
ROM_END

ROM_START(psion3s)
	ROM_REGION16_LE(0x80000, "flash", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "191f", "V1.91F/ENG 140694")
	ROMX_LOAD("s3_v1.91f_eng.bin", 0x00000, 0x80000, CRC(8b4cfa7a) SHA1(582ffba8ec81960b2db283ef0c280a6d8444414f), ROM_BIOS(0))
ROM_END

ROM_START(pocketbk)
	ROM_REGION16_LE(0x80000, "flash", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "191f", "V1.91F/ACN 270892")
	ROMX_LOAD("pb_v1.91f_acn.bin", 0x00000, 0x80000, CRC(875a804b) SHA1(9db07b3de9bcb9cc0c56c9a6fb35b9653eba68b3), ROM_BIOS(0))

	ROM_REGION(0x80000, "ssd1", 0) // Acorn Spell was only available pre-installed in a Pocket Book
	ROM_LOAD("acspell.bin", 0x00000, 0x80000, CRC(2e55032a) SHA1(560a425a19b3f3d12da9a0e2127f2c67aa829082))
ROM_END

} // anonymous namespace


//    YEAR  NAME       PARENT   COMPAT  MACHINE   INPUT      CLASS          INIT         COMPANY             FULLNAME           FLAGS
COMP( 1991, psion3,    0,       0,      psion3,   psion3,    psion3_state,  empty_init,  "Psion",            "Series 3",        MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )
COMP( 1992, pocketbk,  psion3,  0,      psion3s,  pocketbk,  psion3_state,  empty_init,  "Acorn Computers",  "Pocket Book",     MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )
COMP( 1994, psion3s,   psion3,  0,      psion3s,  psion3s,   psion3_state,  empty_init,  "Psion",            "Series 3s",       MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )



psion3a.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/******************************************************************************

    Psion Series 3a/3c/3mx

    TODO:
    - sound devices, replace fake Psion Codec device with M7542 and M7702-03
    - serial ports
    - fix RAM detection for 3mx
    - 3mx speed switch using CTRL+Diamond
    - 3c/3mx backlight using Psion+Space
    - 3c/3mx IrDA

******************************************************************************/

#include "emu.h"
#include "machine/nvram.h"
#include "machine/psion_asic9.h"
#include "machine/psion_condor.h"
#include "machine/psion_ssd.h"
#include "machine/ram.h"
#include "sound/spkrdev.h"
#include "bus/psion/honda/slot.h"
#include "bus/psion/sibo/slot.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


class psion3a_codec_device : public device_t, public device_sound_interface
{
public:
	psion3a_codec_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);

	void pcm_in(uint8_t data);

protected:
	void device_start() override ATTR_COLD;
	void sound_stream_update(sound_stream &stream) override;

private:
	sound_stream *m_stream;
	int16_t m_audio_out;
};

DEFINE_DEVICE_TYPE(PSION_S3A_CODEC, psion3a_codec_device, "psion3a_codec", "Series 3a A-law Codec")

psion3a_codec_device::psion3a_codec_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, PSION_S3A_CODEC, tag, owner, clock)
	, device_sound_interface(mconfig, *this)
	, m_stream(nullptr)
	, m_audio_out(0)
{
}

void psion3a_codec_device::device_start()
{
	m_stream = stream_alloc(0, 1, 8000);
}

void psion3a_codec_device::sound_stream_update(sound_stream &stream)
{
	stream.fill(0, sound_stream::sample_t(m_audio_out) * (1.0 / 4096.0));
}

void psion3a_codec_device::pcm_in(uint8_t data)
{
	m_stream->update();

	// Expand 8-bit signed compressed number to 16-bit 2's complement integer (13-bit magnitude) using A-law
	data ^= 0x55;

	uint8_t seg = (data & 0x70) >> 4;
	m_audio_out = 0;
	if (seg)
	{
		m_audio_out = 0x10;
		seg--;
	}
	m_audio_out = (((m_audio_out + (data & 0x0f)) << 1) + 1) << seg;

	m_audio_out *= (data & 0x80) ? -1.0 : 1.0;
}


namespace {

class psion3a_base_state : public driver_device
{
public:
	psion3a_base_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_asic9(*this, "asic9")
		, m_ram(*this, "ram")
		, m_nvram(*this, "nvram")
		, m_palette(*this, "palette")
		, m_keyboard(*this, "COL%u", 0U)
		, m_speaker(*this, "speaker")
		, m_codec(*this, "codec")
		, m_ssd(*this, "ssd%u", 1U)
	{ }

	void psion_asic9(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(wakeup);

protected:
	virtual void machine_start() override ATTR_COLD;

	required_device<psion_asic9_device> m_asic9;
	required_device<ram_device> m_ram;
	required_device<nvram_device> m_nvram;
	required_device<palette_device> m_palette;
	required_ioport_array<8> m_keyboard;
	required_device<speaker_sound_device> m_speaker;
	required_device<psion3a_codec_device> m_codec;
	required_device_array<psion_ssd_device, 2> m_ssd;

	void palette_init(palette_device &palette);

	uint16_t kbd_r();

	uint8_t m_key_col = 0;
};

class psion3a_state : public psion3a_base_state
{
public:
	psion3a_state(const machine_config &mconfig, device_type type, const char *tag)
		: psion3a_base_state(mconfig, type, tag)
		, m_sibo(*this, "sibo")
	{ }

	void psion3a(machine_config &config);
	void psion3a2(machine_config &config);
	void pocketbk2(machine_config &config);

private:
	required_device<psion_sibo_slot_device> m_sibo;
};

class psion3c_state : public psion3a_base_state
{
public:
	psion3c_state(const machine_config &mconfig, device_type type, const char *tag)
		: psion3a_base_state(mconfig, type, tag)
		, m_condor(*this, "condor")
		, m_honda(*this, "honda")
	{ }

	void psion3c(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<psion_condor_device> m_condor;
	required_device<psion_honda_slot_device> m_honda;
};

class psion3mx_state : public psion3a_base_state
{
public:
	psion3mx_state(const machine_config &mconfig, device_type type, const char *tag)
		: psion3a_base_state(mconfig, type, tag)
		, m_honda(*this, "honda")
	{ }

	void psion3mx(machine_config &config);

private:
	required_device<psion_honda_slot_device> m_honda;
};


void psion3a_base_state::machine_start()
{
	m_nvram->set_base(m_ram->pointer(), m_ram->size());
}

void psion3c_state::machine_reset()
{
	m_asic9->io_space().install_readwrite_handler(0x0100, 0x011f, read8sm_delegate(*m_condor, FUNC(psion_condor_device::read)), write8sm_delegate(*m_condor, FUNC(psion_condor_device::write)), 0x00ff);
}


static INPUT_PORTS_START( psion3a )
	PORT_START("COL0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)                                   PORT_NAME("Enter")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))                 PORT_NAME(u8"\u2192 End") // U+2192 = →
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)        PORT_CHAR(0x09)                                 PORT_NAME("Tab")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y')  PORT_CHAR('Y')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))                  PORT_NAME(u8"\u2190 Home") // U+2190 = ←
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))                  PORT_NAME(u8"\u2193 Pg Dn") // U+2193 = ↓
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)          PORT_CHAR('n')  PORT_CHAR('N')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT)       PORT_CHAR(UCHAR_SHIFT_2)                        PORT_NAME("Psion")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)         PORT_CHAR(UCHAR_MAMEKEY(F8))                    PORT_NAME("Sheet")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)         PORT_CHAR(UCHAR_MAMEKEY(F5))                    PORT_NAME("Time")            PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)         PORT_CHAR(UCHAR_MAMEKEY(F2))                    PORT_NAME("Data")            PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)

	PORT_START("COL1")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/')  PORT_CHAR(';')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-')  PORT_CHAR('_')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('+')  PORT_CHAR('=')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_CHAR('0')  PORT_CHAR(')')  PORT_CHAR(']')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)          PORT_CHAR('p')  PORT_CHAR('P')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR('*')  PORT_CHAR(':')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)                        PORT_NAME("Shift (L)")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)         PORT_CHAR(UCHAR_MAMEKEY(F7))                    PORT_NAME("Calc")            PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)         PORT_CHAR(UCHAR_MAMEKEY(F4))                    PORT_NAME("Agenda")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)         PORT_CHAR(UCHAR_MAMEKEY(F1))                    PORT_NAME("System")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)

	PORT_START("COL2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(0x08)                                 PORT_NAME("Delete")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)          PORT_CHAR('k')  PORT_CHAR('K')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)          PORT_CHAR('i')  PORT_CHAR('I')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8')  PORT_CHAR('?')  PORT_CHAR('}')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_CHAR('9')  PORT_CHAR('(')  PORT_CHAR('[')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)          PORT_CHAR('o')  PORT_CHAR('O')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)          PORT_CHAR('l')  PORT_CHAR('L')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))              PORT_NAME("Control")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)         PORT_CHAR(UCHAR_MAMEKEY(F6))                    PORT_NAME("World")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)         PORT_CHAR(UCHAR_MAMEKEY(F3))                    PORT_NAME("Word")            PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)

	PORT_START("COL3")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')  PORT_CHAR('<')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10)        PORT_CHAR(UCHAR_MAMEKEY(F10))                   PORT_NAME("Help Dial")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)          PORT_CHAR('m')  PORT_CHAR('M')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)          PORT_CHAR('j')  PORT_CHAR('J')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)          PORT_CHAR('u')  PORT_CHAR('U')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7')  PORT_CHAR('&')  PORT_CHAR('{')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)                        PORT_NAME("Shift (R)")

	PORT_START("COL4")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)          PORT_CHAR('r')  PORT_CHAR('R')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)          PORT_CHAR('4')  PORT_CHAR('$')  PORT_CHAR('~')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)          PORT_CHAR('5')  PORT_CHAR('%')  PORT_CHAR('\'')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)          PORT_CHAR('t')  PORT_CHAR('T')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)          PORT_CHAR('g')  PORT_CHAR('G')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)          PORT_CHAR('b')  PORT_CHAR('B')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK)   PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))              PORT_NAME("\xe2\x97\x86 Caps")

	PORT_START("COL5")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)          PORT_CHAR('f')  PORT_CHAR('F')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)          PORT_CHAR('v')  PORT_CHAR('V')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)          PORT_CHAR('c')  PORT_CHAR('C')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)          PORT_CHAR('d')  PORT_CHAR('D')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)          PORT_CHAR('e')  PORT_CHAR('E')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)          PORT_CHAR('3')  PORT_CHAR(0xa3) PORT_CHAR('\\')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11)        PORT_CHAR(UCHAR_MAMEKEY(F11))                   PORT_NAME("Menu")

	PORT_START("COL6")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q')  PORT_CHAR('Q')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('a')  PORT_CHAR('A')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z')  PORT_CHAR('Z')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)          PORT_CHAR('s')  PORT_CHAR('S')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)          PORT_CHAR('w')  PORT_CHAR('W')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)          PORT_CHAR('x')  PORT_CHAR('X')

	PORT_START("COL7")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)          PORT_CHAR('1')  PORT_CHAR('!')                  PORT_NAME("1 ! Off")
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)          PORT_CHAR('2')  PORT_CHAR('"')  PORT_CHAR('#')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_CHAR('6')  PORT_CHAR('^')  PORT_CHAR('@')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')  PORT_CHAR('>')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))                    PORT_NAME(u8"\u2191 Pg Up") // U+2191 = ↑
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)          PORT_CHAR('h')  PORT_CHAR('H')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC))                   PORT_NAME("Esc On")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
INPUT_PORTS_END


static INPUT_PORTS_START( psion3a_de )
	PORT_INCLUDE(psion3a)

	PORT_MODIFY("COL0")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))                 PORT_NAME(u8"\u2192 Ende")    // U+2192 = →
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('z')  PORT_CHAR('Z')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))                  PORT_NAME(u8"\u2190 Pos1")    // U+2190 = ←
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))                  PORT_NAME(u8"\u2193 Bild Dn") // U+2193 = ↓
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)         PORT_CHAR(UCHAR_MAMEKEY(F8))                    PORT_NAME("Tabelle")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)         PORT_CHAR(UCHAR_MAMEKEY(F5))                    PORT_NAME("Uhr")             PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)         PORT_CHAR(UCHAR_MAMEKEY(F2))                    PORT_NAME("Daten")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)

	PORT_MODIFY("COL1")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/')  PORT_CHAR(0xdf)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('+')  PORT_CHAR('<')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_CHAR('0')  PORT_CHAR('=')  PORT_CHAR('}')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR('*')  PORT_CHAR('>')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)         PORT_CHAR(UCHAR_MAMEKEY(F7))                    PORT_NAME("Rechner")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)

	PORT_MODIFY("COL2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(0x08)                                 PORT_NAME("Entf")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8')  PORT_CHAR('(')  PORT_CHAR('[')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_CHAR('9')  PORT_CHAR(')')  PORT_CHAR(']')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))              PORT_NAME("Strg")
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)         PORT_CHAR(UCHAR_MAMEKEY(F6))                    PORT_NAME("Welt")            PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)

	PORT_MODIFY("COL3")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')  PORT_CHAR(';')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10)        PORT_CHAR(UCHAR_MAMEKEY(F10))                   PORT_NAME("Hilfe Wahl")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7')  PORT_CHAR('?')  PORT_CHAR('{')

	PORT_MODIFY("COL4")
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)          PORT_CHAR('4')  PORT_CHAR('$')  PORT_CHAR('@')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)          PORT_CHAR('5')  PORT_CHAR('%')  PORT_CHAR('\'')

	PORT_MODIFY("COL5")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)          PORT_CHAR('3')  PORT_CHAR(0xa7) PORT_CHAR('\\')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11)        PORT_CHAR(UCHAR_MAMEKEY(F11))                   PORT_NAME(u8"Menü")

	PORT_MODIFY("COL6")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_CHAR('y')  PORT_CHAR('Y')

	PORT_MODIFY("COL7")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)          PORT_CHAR('1')  PORT_CHAR('!')                  PORT_NAME("1 ! Aus")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_CHAR('6')  PORT_CHAR('&')  PORT_CHAR(0xba)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')  PORT_CHAR(':')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))                    PORT_NAME(u8"\u2191 Bild Up") // U+2191 = ↑
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC))                   PORT_NAME("Esc Ein")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
INPUT_PORTS_END


static INPUT_PORTS_START( psion3a_fr )
	PORT_INCLUDE(psion3a)

	PORT_MODIFY("COL0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)                                   PORT_NAME(u8"Entrée")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))                 PORT_NAME(u8"\u2192 Fin")   // U+2192 = →
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))                  PORT_NAME(u8"\u2190 Debut") // U+2190 = ←
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))                  PORT_NAME(u8"\u2193")       // U+2193 = ↓
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)         PORT_CHAR(UCHAR_MAMEKEY(F8))                    PORT_NAME("Tableur")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)         PORT_CHAR(UCHAR_MAMEKEY(F5))                    PORT_NAME("Heure")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)         PORT_CHAR(UCHAR_MAMEKEY(F2))                    PORT_NAME("Fiche")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)

	PORT_MODIFY("COL1")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('+')  PORT_CHAR('<')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_CHAR(0xe0) PORT_CHAR('0')  PORT_CHAR(0xba)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR('*')  PORT_CHAR('>')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)         PORT_CHAR(UCHAR_MAMEKEY(F7))                    PORT_NAME("Calc")            PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)         PORT_CHAR(UCHAR_MAMEKEY(F1))                    PORT_NAME(u8"Système")       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)

	PORT_MODIFY("COL2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(0x08)                                 PORT_NAME("Eff")
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('!')  PORT_CHAR('8')  PORT_CHAR('>')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_CHAR(0xe7) PORT_CHAR('9')  PORT_CHAR(0xf9)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))              PORT_NAME("Ctrl")
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)         PORT_CHAR(UCHAR_MAMEKEY(F6))                    PORT_NAME("Monde")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)

	PORT_MODIFY("COL3")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')  PORT_CHAR(';')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10)        PORT_CHAR(UCHAR_MAMEKEY(F10))                   PORT_NAME("Aide Comp")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR(0xe8) PORT_CHAR('7')  PORT_CHAR('<')

	PORT_MODIFY("COL4")
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)          PORT_CHAR('\'') PORT_CHAR('4')  PORT_CHAR('%')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)          PORT_CHAR('(')  PORT_CHAR('5')  PORT_CHAR('@')

	PORT_MODIFY("COL5")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)          PORT_CHAR('"')  PORT_CHAR('3') PORT_CHAR('\\')

	PORT_MODIFY("COL6")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)          PORT_CHAR('a')  PORT_CHAR('A')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('q')  PORT_CHAR('Q')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_CHAR('w')  PORT_CHAR('W')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)          PORT_CHAR('z')  PORT_CHAR('Z')

	PORT_MODIFY("COL7")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)          PORT_CHAR('&')  PORT_CHAR('1')                  PORT_NAME("& 1 Off")
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)          PORT_CHAR(0xe9) PORT_CHAR('2')  PORT_CHAR('#')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_CHAR(')')  PORT_CHAR('6')  PORT_CHAR('^')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')  PORT_CHAR(':')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))                    PORT_NAME(u8"\u2191") // U+2191 = ↑
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC))                   PORT_NAME("Esc On")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
INPUT_PORTS_END


static INPUT_PORTS_START( psion3a_it )
	PORT_INCLUDE(psion3a)

	PORT_MODIFY("COL0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)                                   PORT_NAME("Invio")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))                 PORT_NAME(u8"\u2192 Fine")   // U+2192 = →
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))                  PORT_NAME(u8"\u2190 Inizio") // U+2190 = ←
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))                  PORT_NAME(u8"\u2193 Pag")    // U+2193 = ↓
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)         PORT_CHAR(UCHAR_MAMEKEY(F8))                    PORT_NAME("Foglio")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)         PORT_CHAR(UCHAR_MAMEKEY(F5))                    PORT_NAME("Ora")             PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)         PORT_CHAR(UCHAR_MAMEKEY(F2))                    PORT_NAME("Archivi")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)

	PORT_MODIFY("COL1")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)         PORT_CHAR(UCHAR_MAMEKEY(F7))                    PORT_NAME("Calc")            PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)         PORT_CHAR(UCHAR_MAMEKEY(F4))                    PORT_NAME("Agenda")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)         PORT_CHAR(UCHAR_MAMEKEY(F1))                    PORT_NAME("Sistema")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)

	PORT_MODIFY("COL2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(0x08)                                 PORT_NAME("Canc.")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))              PORT_NAME("Ctrl")
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)         PORT_CHAR(UCHAR_MAMEKEY(F6))                    PORT_NAME("Mondo")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)         PORT_CHAR(UCHAR_MAMEKEY(F3))                    PORT_NAME("Testi")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)

	PORT_MODIFY("COL3")
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10)        PORT_CHAR(UCHAR_MAMEKEY(F10))                   PORT_NAME("Aiuto Telef.")

	PORT_MODIFY("COL4")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK)   PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))              PORT_NAME("\xe2\x97\x86 Maiusc.")

	PORT_MODIFY("COL7")
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))                    PORT_NAME(u8"\u2191 Pag") // U+2191 = ↑
INPUT_PORTS_END


static INPUT_PORTS_START( psion3c )
	PORT_INCLUDE(psion3a)

	PORT_MODIFY("COL2")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9)         PORT_CHAR(UCHAR_MAMEKEY(F9))                    PORT_NAME("Jotter")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
INPUT_PORTS_END


//static INPUT_PORTS_START( psion3c_de )
//  PORT_INCLUDE(psion3a_de)
//
//  PORT_MODIFY("COL2")
//  PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9)         PORT_CHAR(UCHAR_MAMEKEY(F9))                    PORT_NAME("Notiz")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
//INPUT_PORTS_END


static INPUT_PORTS_START( psion3c_fr )
	PORT_INCLUDE(psion3a_fr)

	PORT_MODIFY("COL2")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9)         PORT_CHAR(UCHAR_MAMEKEY(F9))                    PORT_NAME("Calepin")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
INPUT_PORTS_END


static INPUT_PORTS_START( pocketbk2 )
	PORT_INCLUDE(psion3a)

	PORT_MODIFY("COL0")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT)       PORT_CHAR(UCHAR_SHIFT_2)                        PORT_NAME("Acorn")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)         PORT_CHAR(UCHAR_MAMEKEY(F8))                    PORT_NAME("Abacus")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)         PORT_CHAR(UCHAR_MAMEKEY(F5))                    PORT_NAME("Time")            PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)         PORT_CHAR(UCHAR_MAMEKEY(F2))                    PORT_NAME("Cards")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)

	PORT_MODIFY("COL1")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)         PORT_CHAR(UCHAR_MAMEKEY(F7))                    PORT_NAME("Calc")            PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)         PORT_CHAR(UCHAR_MAMEKEY(F4))                    PORT_NAME("Schedule")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)         PORT_CHAR(UCHAR_MAMEKEY(F1))                    PORT_NAME("Desktop")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)

	PORT_MODIFY("COL2")
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)         PORT_CHAR(UCHAR_MAMEKEY(F6))                    PORT_NAME("World")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)         PORT_CHAR(UCHAR_MAMEKEY(F3))                    PORT_NAME("Write")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion3a_state::wakeup), 0)
INPUT_PORTS_END


INPUT_CHANGED_MEMBER(psion3a_base_state::wakeup)
{
	m_asic9->eint0_w(newval);
}


uint16_t psion3a_base_state::kbd_r()
{
	uint16_t data = 0x00;

	for (int i = 0; i < 8; i++)
	{
		if (BIT(m_key_col, i))
			data |= m_keyboard[i]->read();
	}

	return data;
}


void psion3a_base_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(190, 220, 190));

	for (int i = 1; i < 3; i++)
	{
		const int r = (0x99 * i) / 2;
		const int g = (0xaa * i) / 2;
		const int b = (0x88 * i) / 2;
		m_palette->set_pen_color(i, rgb_t(r, g, b));
	}
}


void psion3a_base_state::psion_asic9(machine_config &config)
{
	PSION_ASIC9(config, m_asic9, 7.68_MHz_XTAL); // V30H
	m_asic9->set_screen("screen");
	m_asic9->set_ram_rom("ram", "rom");
	m_asic9->port_ab_r().set(FUNC(psion3a_base_state::kbd_r));
	m_asic9->buz_cb().set(m_speaker, FUNC(speaker_sound_device::level_w));
	//m_asic9->buzvol_cb().set([this](int state) { m_speaker->set_output_gain(ALL_OUTPUTS, state ? 1.0 : 0.25); });
	m_asic9->col_cb().set([this](uint8_t data) { m_key_col = data; });
	m_asic9->data_r<0>().set(m_ssd[1], FUNC(psion_ssd_device::data_r));      // SSD Pack 2
	m_asic9->data_w<0>().set(m_ssd[1], FUNC(psion_ssd_device::data_w));
	m_asic9->data_r<1>().set(m_ssd[0], FUNC(psion_ssd_device::data_r));      // SSD Pack 1
	m_asic9->data_w<1>().set(m_ssd[0], FUNC(psion_ssd_device::data_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_size(480, 160);
	screen.set_visarea_full();
	screen.set_refresh_hz(66);
	screen.set_screen_update(m_asic9, FUNC(psion_asic9_device::screen_update));
	screen.set_palette(m_palette);
	PALETTE(config, "palette", FUNC(psion3a_base_state::palette_init), 3);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);

	RAM(config, m_ram);
	NVRAM(config, "nvram", nvram_device::DEFAULT_NONE);

	PSION_SSD(config, m_ssd[0]);
	m_ssd[0]->door_cb().set(m_asic9, FUNC(psion_asic9_device::medchng_w));
	PSION_SSD(config, m_ssd[1]);
	m_ssd[1]->door_cb().set(m_asic9, FUNC(psion_asic9_device::medchng_w));

	SOFTWARE_LIST(config, "ssd_list").set_original("psion_ssd").set_filter("S3,S3A");
	//SOFTWARE_LIST(config, "flop_list").set_original("psion_flop").set_filter("S3A");
}

void psion3a_state::psion3a(machine_config &config)
{
	psion_asic9(config);

	m_ram->set_default_size("512K").set_extra_options("256K");

	// SIBO expansion port
	PSION_SIBO_SLOT(config, m_sibo, psion_sibo_devices, nullptr);
	m_sibo->int_cb().set(m_asic9, FUNC(psion_asic9_device::sds_int_w));
	m_asic9->data_r<4>().set(m_sibo, FUNC(psion_sibo_slot_device::data_r));
	m_asic9->data_w<4>().set(m_sibo, FUNC(psion_sibo_slot_device::data_w));

	PSION_S3A_CODEC(config, m_codec).add_route(ALL_OUTPUTS, "mono", 1.00); // TODO: M7542
	m_asic9->pcm_out().set(m_codec, FUNC(psion3a_codec_device::pcm_in));
}

void psion3a_state::psion3a2(machine_config &config)
{
	psion3a(config);

	m_ram->set_default_size("2M").set_extra_options("1M");
}

void psion3a_state::pocketbk2(machine_config &config)
{
	psion3a(config);

	m_ram->set_default_size("1M").set_extra_options("256K,512K");
}

void psion3c_state::psion3c(machine_config &config)
{
	psion_asic9(config);

	m_ram->set_default_size("2M").set_extra_options("1M");

	PSION_S3A_CODEC(config, m_codec).add_route(ALL_OUTPUTS, "mono", 1.00); // TODO: M7702-03
	m_asic9->pcm_out().set(m_codec, FUNC(psion3a_codec_device::pcm_in));

	PSION_CONDOR(config, m_condor, 3'686'400); // FIXME: unknown clock source
	m_condor->txd_handler().set(m_honda, FUNC(psion_honda_slot_device::write_txd));
	m_condor->rts_handler().set(m_honda, FUNC(psion_honda_slot_device::write_rts));
	m_condor->dtr_handler().set(m_honda, FUNC(psion_honda_slot_device::write_dtr));
	m_condor->int_handler().set(m_asic9, FUNC(psion_asic9_device::eint1_w));

	// Honda expansion port
	PSION_HONDA_SLOT(config, m_honda, psion_honda_devices, nullptr);
	m_honda->rxd_handler().set(m_condor, FUNC(psion_condor_device::write_rxd));
	m_honda->dcd_handler().set(m_condor, FUNC(psion_condor_device::write_dcd));
	m_honda->dsr_handler().set(m_condor, FUNC(psion_condor_device::write_dsr));
	m_honda->cts_handler().set(m_condor, FUNC(psion_condor_device::write_cts));
	m_honda->sdoe_handler().set(m_asic9, FUNC(psion_asic9_device::medchng_w)); // TODO: verify input line
	m_asic9->data_r<4>().set(m_honda, FUNC(psion_honda_slot_device::data_r));
	m_asic9->data_w<4>().set(m_honda, FUNC(psion_honda_slot_device::data_w));
}

void psion3mx_state::psion3mx(machine_config &config)
{
	psion_asic9(config);

	PSION_ASIC9MX(config.replace(), m_asic9, 3.6864_MHz_XTAL * 15 / 2); // V30MX
	m_asic9->set_screen("screen");
	m_asic9->set_ram_rom("ram", "rom");
	m_asic9->port_ab_r().set(FUNC(psion3mx_state::kbd_r));
	m_asic9->buz_cb().set(m_speaker, FUNC(speaker_sound_device::level_w));
	//m_asic9->buzvol_cb().set([this](int state) { m_speaker->set_output_gain(ALL_OUTPUTS, state ? 1.0 : 0.25); });
	m_asic9->col_cb().set([this](uint8_t data) { m_key_col = data; });
	m_asic9->data_r<0>().set(m_ssd[1], FUNC(psion_ssd_device::data_r));      // SSD Pack 2
	m_asic9->data_w<0>().set(m_ssd[1], FUNC(psion_ssd_device::data_w));
	m_asic9->data_r<1>().set(m_ssd[0], FUNC(psion_ssd_device::data_r));      // SSD Pack 1
	m_asic9->data_w<1>().set(m_ssd[0], FUNC(psion_ssd_device::data_w));

	m_ram->set_default_size("2M").set_extra_options("");

	PSION_S3A_CODEC(config, m_codec).add_route(ALL_OUTPUTS, "mono", 1.00); // TODO: M7702-03
	m_asic9->pcm_out().set(m_codec, FUNC(psion3a_codec_device::pcm_in));

	// Honda expansion port
	PSION_HONDA_SLOT(config, m_honda, psion_honda_devices, nullptr);
	//m_honda->rxd_handler().set(m_asic9mx, FUNC(psion_condor_device::write_rxd));
	//m_honda->dcd_handler().set(m_asic9mx, FUNC(psion_condor_device::write_dcd));
	//m_honda->dsr_handler().set(m_asic9mx, FUNC(psion_condor_device::write_dsr));
	//m_honda->cts_handler().set(m_asic9mx, FUNC(psion_condor_device::write_cts));
	m_honda->sdoe_handler().set(m_asic9, FUNC(psion_asic9_device::medchng_w)); // TODO: verify input line
	m_asic9->data_r<4>().set(m_honda, FUNC(psion_honda_slot_device::data_r));
	m_asic9->data_w<4>().set(m_honda, FUNC(psion_honda_slot_device::data_w));
}


ROM_START(psion3a)
	// Known versions: English, Belgian, Dutch, German
	ROM_REGION16_LE(0x100000, "rom", 0)
	ROM_SYSTEM_BIOS(0, "322f", "V3.22F/ENG")
	ROMX_LOAD("s3a_v3.22f_eng.bin", 0x000000, 0x100000, CRC(fafa3820) SHA1(c1a320b43280cfdb74fc1cb1363fca88dd187487), ROM_BIOS(0))
ROM_END

ROM_START(psion3a2)
	// Known versions: English, Dutch, French, German, Italian, Russian
	ROM_REGION16_LE(0x200000, "rom", 0)
	ROM_SYSTEM_BIOS(0, "340f", "V3.40F/ENG")
	ROMX_LOAD("s3a_v3.40f_eng.bin", 0x000000, 0x200000, CRC(f0adf12c) SHA1(3eb4e7f1fc5611a4d6e65d27d336969ebae94395), ROM_BIOS(0))
ROM_END

ROM_START(psion3a2_us)
	ROM_REGION16_LE(0x200000, "rom", 0)
	ROM_SYSTEM_BIOS(0, "340f", "V3.40F/USA")
	ROMX_LOAD("s3a_v3.40f_usa.bin", 0x000000, 0x200000, CRC(6028294b) SHA1(9dfcb02af268797a15b070ac62e29689f2d18c86), ROM_BIOS(0))
ROM_END

ROM_START(psion3a2_it)
	ROM_REGION16_LE(0x200000, "rom", 0)
	ROM_SYSTEM_BIOS(0, "340f", "V3.40F/ITA")
	ROMX_LOAD("s3a_v3.40f_ita.bin", 0x000000, 0x200000, CRC(0e54df7b) SHA1(c4ed29db1c799fda53acf909a5ef553b4953dc32), ROM_BIOS(0))
ROM_END

ROM_START(psion3a2_de)
	ROM_REGION16_LE(0x200000, "rom", 0)
	ROM_SYSTEM_BIOS(0, "341f", "V3.41F/DEU")
	ROMX_LOAD("s3a_v3.41f_deu.bin", 0x000000, 0x200000, CRC(1f21cb0a) SHA1(fbb9c3356cf8b1d89b8cf50fc12175568c74ce3e), ROM_BIOS(0))
ROM_END

ROM_START(psion3a2_ru)
	ROM_REGION16_LE(0x200000, "rom", 0)
	ROM_SYSTEM_BIOS(0, "343f", "V3.43F/RUS")
	ROMX_LOAD("s3a_v3.43f_rus.bin", 0x000000, 0x200000, CRC(ada4da36) SHA1(24953197f5175596593e5e4045846812ca9b82e3), ROM_BIOS(0))
ROM_END

ROM_START(psion3c)
	// Known versions: English, French, German, Italian, Flemish and Dutch
	ROM_REGION16_LE(0x200000, "rom", 0)
	ROM_SYSTEM_BIOS(0, "520f", "V5.20F/ENG")
	ROMX_LOAD("oak_v5.20f_eng.bin", 0x000000, 0x200000, CRC(d8e672ca) SHA1(23e7570ddbecbfd50953ce6a6b7ead7128814402), ROM_BIOS(0))
ROM_END

ROM_START(psion3mx)
	// Known versions: English, Dutch, French, German, Italian
	ROM_REGION16_LE(0x200000, "rom", 0)
	ROM_SYSTEM_BIOS(0, "616f", "V6.16F/ENG")
	ROMX_LOAD("maple_v6.16f_uk.bin", 0x000000, 0x200000, CRC(10011d9d) SHA1(8c657414513ed57ccf6beddc65dca1fe5ab600fb), ROM_BIOS(0))
ROM_END

ROM_START(psion3mx_nl)
	ROM_REGION16_LE(0x200000, "rom", 0)
	ROM_SYSTEM_BIOS(0, "617f", "V6.17F/DUT")
	ROMX_LOAD("maple_v6.17f_nl.bin", 0x000000, 0x200000, CRC(900752c8) SHA1(7fee6ac2d386f61c7a01cf91033a79a9c2532c6e), ROM_BIOS(0))
ROM_END

ROM_START(psion3mx_fr)
	ROM_REGION16_LE(0x200000, "rom", 0)
	ROM_SYSTEM_BIOS(0, "620f", "V6.20F/FRE")
	ROMX_LOAD("maple_v6.20f_fre.bin", 0x000000, 0x200000, CRC(b4fc57f4) SHA1(26588937d811adf08b973a0188927707d1f6a6e4), ROM_BIOS(0))
ROM_END

ROM_START(pocketbk2)
	ROM_REGION16_LE(0x200000, "rom", 0)
	ROM_SYSTEM_BIOS(0, "130f", "V1.30F/ACN")
	ROMX_LOAD("pb2_v1.30f_acn.bin", 0x000000, 0x200000, CRC(d7ba3a50) SHA1(2d29a7ac5ec4d6bf63bd7d93cc3763e0f9763136), ROM_BIOS(0))
ROM_END

} // anonymous namespace


//    YEAR  NAME          PARENT    COMPAT  MACHINE    INPUT        CLASS           INIT         COMPANY             FULLNAME                    FLAGS
COMP( 1993, psion3a,      0,        0,      psion3a,   psion3a,     psion3a_state,  empty_init,  "Psion",            "Series 3a",                MACHINE_SUPPORTS_SAVE )
COMP( 1994, pocketbk2,    psion3a,  0,      pocketbk2, pocketbk2,   psion3a_state,  empty_init,  "Acorn Computers",  "Pocket Book II",           MACHINE_SUPPORTS_SAVE )
COMP( 1995, psion3a2,     psion3a,  0,      psion3a2,  psion3a,     psion3a_state,  empty_init,  "Psion",            "Series 3a (2M)",           MACHINE_SUPPORTS_SAVE )
COMP( 1995, psion3a2_us,  psion3a,  0,      psion3a2,  psion3a,     psion3a_state,  empty_init,  "Psion",            "Series 3a (2M) (US)",      MACHINE_SUPPORTS_SAVE )
COMP( 1995, psion3a2_it,  psion3a,  0,      psion3a2,  psion3a_it,  psion3a_state,  empty_init,  "Psion",            "Series 3a (2M) (Italian)", MACHINE_SUPPORTS_SAVE )
COMP( 1995, psion3a2_de,  psion3a,  0,      psion3a2,  psion3a_de,  psion3a_state,  empty_init,  "Psion",            "Series 3a (2M) (German)",  MACHINE_SUPPORTS_SAVE )
COMP( 1997, psion3a2_ru,  psion3a,  0,      psion3a2,  psion3a,     psion3a_state,  empty_init,  "Psion",            "Series 3a (2M) (Russian)", MACHINE_SUPPORTS_SAVE )
COMP( 1996, psion3c,      0,        0,      psion3c,   psion3c,     psion3c_state,  empty_init,  "Psion",            "Series 3c",                MACHINE_SUPPORTS_SAVE )
COMP( 1998, psion3mx,     0,        0,      psion3mx,  psion3c,     psion3mx_state, empty_init,  "Psion",            "Series 3mx",               MACHINE_SUPPORTS_SAVE )
COMP( 1998, psion3mx_nl,  psion3mx, 0,      psion3mx,  psion3c,     psion3mx_state, empty_init,  "Psion",            "Series 3mx (Dutch)",       MACHINE_SUPPORTS_SAVE )
COMP( 1998, psion3mx_fr,  psion3mx, 0,      psion3mx,  psion3c_fr,  psion3mx_state, empty_init,  "Psion",            "Series 3mx (French)",      MACHINE_SUPPORTS_SAVE )



psion5.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, Ash Wolf
/***************************************************************************

        Psion 5mx (EPOC R5) series

        Driver by Ryan Holtz, ported from work by Ash Wolf

        TODO:
        - Audio
        - UART support
        - Probably more

        More info:
            https://github.com/Treeki/WindEmu

****************************************************************************/

#include "psion5.h"

#include "utf8.h"

#define LOG_UNKNOWNS        (1U << 1)
#define LOG_IRQ             (1U << 2)
#define LOG_DISPLAY         (1U << 3)
#define LOG_DRAM_READS      (1U << 4)
#define LOG_LCD_READS       (1U << 5)
#define LOG_POWER_READS     (1U << 6)
#define LOG_EOI_READS       (1U << 7)
#define LOG_INT_READS       (1U << 8)
#define LOG_CODEC_READS     (1U << 9)
#define LOG_SSP_READS       (1U << 10)
#define LOG_TIMER_READS     (1U << 11)
#define LOG_BUZZER_READS    (1U << 12)
#define LOG_RTC_READS       (1U << 13)
#define LOG_GPIO_READS      (1U << 14)
#define LOG_KBD_READS       (1U << 15)
#define LOG_DRAM_WRITES     (1U << 16)
#define LOG_LCD_WRITES      (1U << 17)
#define LOG_POWER_WRITES    (1U << 18)
#define LOG_EOI_WRITES      (1U << 19)
#define LOG_INT_WRITES      (1U << 20)
#define LOG_CODEC_WRITES    (1U << 21)
#define LOG_SSP_WRITES      (1U << 22)
#define LOG_TIMER_WRITES    (1U << 23)
#define LOG_BUZZER_WRITES   (1U << 24)
#define LOG_RTC_WRITES      (1U << 25)
#define LOG_GPIO_WRITES     (1U << 26)
#define LOG_PIN_WRITES      (1U << 27)
#define LOG_KBD_WRITES      (1U << 28)

#define LOG_WRITES          (LOG_DRAM_WRITES | LOG_LCD_WRITES | LOG_POWER_WRITES | LOG_EOI_WRITES | LOG_INT_WRITES | LOG_CODEC_WRITES \
							| LOG_SSP_WRITES | LOG_TIMER_WRITES | LOG_BUZZER_WRITES | LOG_RTC_WRITES | LOG_GPIO_WRITES | LOG_PIN_WRITES | LOG_KBD_WRITES)
#define LOG_READS           (LOG_DRAM_READS | LOG_LCD_READS | LOG_POWER_READS | LOG_EOI_READS | LOG_INT_READS | LOG_CODEC_READS \
							| LOG_SSP_READS | LOG_TIMER_READS | LOG_BUZZER_READS | LOG_RTC_READS | LOG_GPIO_READS | LOG_KBD_READS)
#define LOG_ALL_IRQ         (LOG_IRQ | LOG_EOI_WRITES | LOG_INT_WRITES | LOG_EOI_READS | LOG_INT_READS)
#define LOG_ALL             (LOG_UNKNOWNS | LOG_IRQ | LOG_DISPLAY | LOG_WRITES | LOG_READS)

#define VERBOSE             (LOG_CODEC_READS | LOG_CODEC_WRITES | LOG_BUZZER_WRITES)
#include "logmacro.h"

void psion5mx_state::machine_start()
{
	save_item(NAME(m_memcfg));
	save_item(NAME(m_dramcfg));

	save_item(NAME(m_timer_reload));
	save_item(NAME(m_timer_value));
	save_item(NAME(m_timer_ctrl));

	save_item(NAME(m_pending_ints));
	save_item(NAME(m_int_mask));

	save_item(NAME(m_lcd_display_base_addr));

	save_item(NAME(m_rtc));
	save_item(NAME(m_pwrsr));
	save_item(NAME(m_last_ssi_request));
	save_item(NAME(m_ssi_read_counter));

	save_item(NAME(m_kbd_scan));

	save_item(NAME(m_ports));

	m_timers[0] = timer_alloc(FUNC(psion5mx_state::update_timer1), this);
	m_timers[1] = timer_alloc(FUNC(psion5mx_state::update_timer2), this);
	m_periodic = timer_alloc(FUNC(psion5mx_state::update_periodic_irq), this);
	m_rtc_ticker = timer_alloc(FUNC(psion5mx_state::update_rtc), this);
}

void psion5mx_state::machine_reset()
{
	std::fill(std::begin(m_memcfg), std::end(m_memcfg), 0);
	m_dramcfg = 0;

	m_timers[0]->adjust(attotime::never);
	m_timers[1]->adjust(attotime::never);
	m_timer_reload[0] = m_timer_reload[1] = 0;
	m_timer_ctrl[0] = m_timer_ctrl[1] = 0;

	m_pending_ints = 0;
	m_int_mask = 0;

	m_lcd_display_base_addr = 0;

	m_rtc = time(nullptr) - 946684800;
	m_pwrsr = (1 << 10) | (1 << 13);
	m_last_ssi_request = 0;
	m_ssi_read_counter = 0;
	m_kbd_scan = 0;

	std::fill(std::begin(m_ports), std::end(m_ports), 0);

	m_periodic->adjust(attotime::from_hz(64), 0, attotime::from_hz(64));

	m_rtc_ticker->adjust(attotime::from_hz(64), 0, attotime::from_hz(64));
}

void psion5mx_state::check_interrupts()
{
	LOGMASKED(LOG_IRQ, "Pending FIQs is %08x & %08x & %08x\n", m_pending_ints, m_int_mask, IRQ_FIQ_MASK);
	LOGMASKED(LOG_IRQ, "Pending IRQs is %08x & %08x & %08x\n", m_pending_ints, m_int_mask, IRQ_IRQ_MASK);
	bool any_interrupts = (m_pending_ints & m_int_mask) != 0;
	if (any_interrupts)
	{
		m_maincpu->resume(SUSPEND_REASON_HALT);
	}
	m_maincpu->set_input_line(arm7_cpu_device::ARM7_FIRQ_LINE, m_pending_ints & m_int_mask & IRQ_FIQ_MASK ? ASSERT_LINE : CLEAR_LINE);
	m_maincpu->set_input_line(arm7_cpu_device::ARM7_IRQ_LINE, m_pending_ints & m_int_mask & IRQ_IRQ_MASK ? ASSERT_LINE : CLEAR_LINE);
}

TIMER_CALLBACK_MEMBER(psion5mx_state::update_timer1)
{
	update_timer(0);
	if (BIT(m_buzzer_ctrl, 1))
	{
		m_speaker->level_w(BIT(m_timer_value[0], 15));
	}
}

TIMER_CALLBACK_MEMBER(psion5mx_state::update_timer2)
{
	update_timer(1);
}

TIMER_CALLBACK_MEMBER(psion5mx_state::update_periodic_irq)
{
	LOGMASKED(LOG_IRQ, "Flagging periodic IRQ\n");
	m_pending_ints |= (1 << IRQ_TINT);
	check_interrupts();
}

TIMER_CALLBACK_MEMBER(psion5mx_state::update_rtc)
{
	if ((m_pwrsr & 0x3f) == 0x3f)
	{
		m_rtc++;
		m_pwrsr &= ~0x3f;
	}
	else
	{
		m_pwrsr++;
	}
}

void psion5mx_state::update_timer(int timer)
{
	m_timer_value[timer]--;
	if (m_timer_value[timer] == 0)
	{
		LOGMASKED(LOG_IRQ, "Flagging Timer %d IRQ\n", timer + 1);
		m_pending_ints |= (1 << (IRQ_TC1OI + timer));
		check_interrupts();
		if (BIT(m_timer_ctrl[timer], 6))
		{
			m_timer_value[timer] = m_timer_reload[timer];
		}
	}
}

void psion5mx_state::set_timer_ctrl(int timer, uint32_t value)
{
	const uint32_t old = m_timer_ctrl[timer];
	const uint32_t changed = old ^ value;
	m_timer_ctrl[timer] = value;
	if (changed != 0)
	{
		attotime interval = BIT(m_timer_ctrl[timer], 3) ? attotime::from_ticks(1, 512000) : attotime::from_ticks(1, 2000);
		if (BIT(old, 7) && BIT(value, 7))
		{
			m_timers[timer]->adjust(m_timers[timer]->remaining(), 0, interval);
		}
		else if (BIT(value, 7))
		{
			m_timers[timer]->adjust(interval, 0, interval);
		}
		else
		{
			m_timers[timer]->adjust(attotime::never);
		}
	}
}

uint32_t psion5mx_state::periphs_r(offs_t offset, uint32_t mem_mask)
{
	const uint32_t reg = offset << 2;
	uint32_t data = 0;
	switch (reg)
	{
		case REG_MEMCFG1:
			data = m_memcfg[0];
			LOGMASKED(LOG_DRAM_READS, "%s: peripheral read, MEMCFG1 = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_MEMCFG2:
			data = m_memcfg[1];
			LOGMASKED(LOG_DRAM_READS, "%s: peripheral read, MEMCFG2 = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_DRAMCFG:
			data = m_dramcfg;
			LOGMASKED(LOG_DRAM_READS, "%s: peripheral read, DRAMCFG = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_LCDCTL:
			LOGMASKED(LOG_LCD_READS, "%s: peripheral read, LCDCTL = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_LCDST:
			LOGMASKED(LOG_LCD_READS, "%s: peripheral read, LCDST = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_LCD_DBAR1:
			data = m_lcd_display_base_addr;
			LOGMASKED(LOG_LCD_READS, "%s: peripheral read, LCD_DBAR1 = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_LCDT0:
			LOGMASKED(LOG_LCD_READS, "%s: peripheral read, LCDT0 = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_LCDT1:
			LOGMASKED(LOG_LCD_READS, "%s: peripheral read, LCDT1 = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_LCDT2:
			LOGMASKED(LOG_LCD_READS, "%s: peripheral read, LCDT2 = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_PWRSR:
			data = m_pwrsr;
			LOGMASKED(LOG_POWER_READS, "%s: peripheral read, PWRSR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_PWRCNT:
			LOGMASKED(LOG_POWER_READS, "%s: peripheral read, PWRCNT = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_HALT:
			LOGMASKED(LOG_POWER_READS, "%s: peripheral read, HALT = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_STBY:
			LOGMASKED(LOG_POWER_READS, "%s: peripheral read, STBY = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_BLEOI:
			LOGMASKED(LOG_EOI_READS, "%s: peripheral read, BLEOI = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_MCEOI:
			LOGMASKED(LOG_EOI_READS, "%s: peripheral read, MCEOI = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_TEOI:
			LOGMASKED(LOG_EOI_READS, "%s: peripheral read, TEOI = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_STFCLR:
			LOGMASKED(LOG_POWER_READS, "%s: peripheral read, STFCLR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_E2EOI:
			LOGMASKED(LOG_EOI_READS, "%s: peripheral read, E2EOI = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_INTSR:
			data = m_pending_ints & m_int_mask;
			LOGMASKED(LOG_INT_READS, "%s: peripheral read, INTSR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_INTRSR:
			data = m_pending_ints;
			LOGMASKED(LOG_INT_READS, "%s: peripheral read, INTRSR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_INTENS:
			data = m_int_mask;
			LOGMASKED(LOG_INT_READS, "%s: peripheral read, INTENS = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_INTENC:
			LOGMASKED(LOG_INT_READS, "%s: peripheral read, INTENC = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_INTTEST1:
			LOGMASKED(LOG_INT_READS, "%s: peripheral read, INTTEST1 = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_INTTEST2:
			LOGMASKED(LOG_INT_READS, "%s: peripheral read, INTTEST2 = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_PUMPCON:
			LOGMASKED(LOG_POWER_READS, "%s: peripheral read, PUMPCON = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_CODR:
			LOGMASKED(LOG_CODEC_READS, "%s: peripheral read, CODR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_CONFG:
			LOGMASKED(LOG_CODEC_READS, "%s: peripheral read, CONFG = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_COLFG:
			LOGMASKED(LOG_CODEC_READS, "%s: peripheral read, COLFG = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_COEOI:
			LOGMASKED(LOG_CODEC_READS | LOG_EOI_READS, "%s: peripheral read, COEOI = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_COTEST:
			LOGMASKED(LOG_CODEC_READS, "%s: peripheral read, COTEST = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_SSCR0:
			LOGMASKED(LOG_SSP_READS, "%s: peripheral read, SSCR0 = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_SSCR1:
			LOGMASKED(LOG_SSP_READS, "%s: peripheral read, SSCR1 = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_SSDR:
		{
			// TODO: Proper SSP support
			uint16_t value = 0;
			switch (m_last_ssi_request)
			{
				case 0xd0d3: // Touch X
					value = 50 + (uint16_t)(m_touchx->read() * 5.7);
					break;
				case 0x9093: // Touch Y
					value = 3834 - (uint16_t)(m_touchy->read() * 13.225);
					break;
				case 0xa4a4: // Main Battery
				case 0xe4e4: // Backup Battery
					value = 3100;
					break;
			}

			if (m_ssi_read_counter == 4) data = (value >> 5) & 0x7f;
			if (m_ssi_read_counter == 5) data = (value << 3) & 0xf8;
			m_ssi_read_counter++;
			if (m_ssi_read_counter == 6) m_ssi_read_counter = 0;

			// We should be clearing SSEOTI here, possibly,
			// but for now we just leave it on to simplify things
			LOGMASKED(LOG_SSP_READS, "%s: peripheral read, SSDR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		}
		case REG_SSSR:
			data = 0;
			LOGMASKED(LOG_SSP_READS, "%s: peripheral read, SSSR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_TC1LOAD:
			data = m_timer_reload[0];
			LOGMASKED(LOG_TIMER_READS, "%s: peripheral read, TC1LOAD = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_TC1VAL:
			data = m_timer_value[0];
			LOGMASKED(LOG_TIMER_READS, "%s: peripheral read, TC1VAL = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_TC1CTRL:
			data = m_timer_ctrl[0];
			LOGMASKED(LOG_TIMER_READS, "%s: peripheral read, TC1CTRL = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_TC1EOI:
			LOGMASKED(LOG_TIMER_READS | LOG_EOI_READS, "%s: peripheral read, TC1EOI = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_TC2LOAD:
			data = m_timer_reload[1];
			LOGMASKED(LOG_TIMER_READS, "%s: peripheral read, TC2LOAD = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_TC2VAL:
			data = m_timer_value[1];
			LOGMASKED(LOG_TIMER_READS, "%s: peripheral read, TC2VAL = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_TC2CTRL:
			data = m_timer_ctrl[1];
			LOGMASKED(LOG_TIMER_READS, "%s: peripheral read, TC2CTRL = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_TC2EOI:
			LOGMASKED(LOG_EOI_READS, "%s: peripheral read, TC2EOI = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_BZCONT:
			data = m_buzzer_ctrl;
			LOGMASKED(LOG_BUZZER_READS, "%s: peripheral read, BZCONT = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_RTCDRL:
			data = (uint16_t)m_rtc;
			LOGMASKED(LOG_RTC_READS, "%s: peripheral read, RTCDRL = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_RTCDRU:
			data = m_rtc >> 16;
			LOGMASKED(LOG_RTC_READS, "%s: peripheral read, RTCDRU = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_RTCMRL:
			LOGMASKED(LOG_RTC_READS, "%s: peripheral read, RTCMRL = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_RTCMRU:
			LOGMASKED(LOG_RTC_READS, "%s: peripheral read, RTCMRU = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_RTCEOI:
			LOGMASKED(LOG_RTC_READS | LOG_EOI_READS, "%s: peripheral read, RTCEOI = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_PADR:
			data = read_keyboard();
			LOGMASKED(LOG_GPIO_READS, "%s: peripheral read, PADR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_PBDR:
			data = m_ports[PORTB];
			LOGMASKED(LOG_GPIO_READS, "%s: peripheral read, PBDR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_PCDR:
			data = m_ports[PORTC];
			LOGMASKED(LOG_GPIO_READS, "%s: peripheral read, PCDR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_PDDR:
			data = m_ports[PORTD];
			LOGMASKED(LOG_GPIO_READS, "%s: peripheral read, PDDR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_PADDR:
			LOGMASKED(LOG_GPIO_READS, "%s: peripheral read, PADDR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_PBDDR:
			LOGMASKED(LOG_GPIO_READS, "%s: peripheral read, PBDDR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_PCDDR:
			LOGMASKED(LOG_GPIO_READS, "%s: peripheral read, PCDDR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_PDDDR:
			LOGMASKED(LOG_GPIO_READS, "%s: peripheral read, PDDDR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_PEDR:
			LOGMASKED(LOG_GPIO_READS, "%s: peripheral read, PEDR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_PEDDR:
			LOGMASKED(LOG_GPIO_READS, "%s: peripheral read, PEDDR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_KSCAN:
			data = m_kbd_scan;
			LOGMASKED(LOG_KBD_READS, "%s: peripheral read, KSCAN = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_LCDMUX:
			LOGMASKED(LOG_LCD_READS, "%s: peripheral read, LCDMUX = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		default:
			LOGMASKED(LOG_UNKNOWNS, "%s: peripheral read, Unknown = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
	}
	return data;
}

void psion5mx_state::periphs_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	const uint32_t reg = offset << 2;
	switch (reg)
	{
		case REG_MEMCFG1:
			m_memcfg[0] = data;
			LOGMASKED(LOG_DRAM_WRITES, "%s: peripheral write, MEMCFG1 = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_MEMCFG2:
			m_memcfg[1] = data;
			LOGMASKED(LOG_DRAM_WRITES, "%s: peripheral write, MEMCFG2 = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_DRAMCFG:
			m_dramcfg = data;
			LOGMASKED(LOG_DRAM_WRITES, "%s: peripheral write, DRAMCFG = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_LCDCTL:
			LOGMASKED(LOG_LCD_WRITES, "%s: peripheral write, LCDCTL = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_LCDST:
			LOGMASKED(LOG_LCD_WRITES, "%s: peripheral write, LCDST = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_LCD_DBAR1:
			LOGMASKED(LOG_LCD_WRITES, "%s: peripheral write, LCD_DBAR1 = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			COMBINE_DATA(&m_lcd_display_base_addr);
			break;
		case REG_LCDT0:
			LOGMASKED(LOG_LCD_WRITES, "%s: peripheral write, LCDT0 = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_LCDT1:
			LOGMASKED(LOG_LCD_WRITES, "%s: peripheral write, LCDT1 = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_LCDT2:
			LOGMASKED(LOG_LCD_WRITES, "%s: peripheral write, LCDT2 = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_PWRSR:
			COMBINE_DATA(&m_pwrsr);
			LOGMASKED(LOG_POWER_WRITES, "%s: peripheral write, PWRSR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_PWRCNT:
			LOGMASKED(LOG_POWER_WRITES, "%s: peripheral write, PWRCNT = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_HALT:
			m_maincpu->suspend(SUSPEND_REASON_HALT, 1);
			LOGMASKED(LOG_POWER_WRITES, "%s: peripheral write, HALT = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_STBY:
			LOGMASKED(LOG_POWER_WRITES, "%s: peripheral write, STBY = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_BLEOI:
			LOGMASKED(LOG_EOI_WRITES, "%s: peripheral write, BLEOI = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_MCEOI:
			LOGMASKED(LOG_EOI_WRITES, "%s: peripheral write, MCEOI = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_TEOI:
			LOGMASKED(LOG_EOI_WRITES, "%s: peripheral write, TEOI = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			m_pending_ints &= ~(1 << IRQ_TINT);
			check_interrupts();
			break;
		case REG_STFCLR:
			m_pwrsr &= ~0x00003e00;
			LOGMASKED(LOG_POWER_WRITES, "%s: peripheral write, STFCLR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_E2EOI:
			LOGMASKED(LOG_EOI_WRITES, "%s: peripheral write, E2EOI = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_INTSR:
			LOGMASKED(LOG_INT_WRITES, "%s: peripheral write, INTSR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_INTRSR:
			LOGMASKED(LOG_INT_WRITES, "%s: peripheral write, INTRSR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_INTENS:
			m_int_mask |= data & mem_mask;
			LOGMASKED(LOG_INT_WRITES, "%s: peripheral write, INTENS = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			check_interrupts();
			break;
		case REG_INTENC:
			m_int_mask &= ~(data & mem_mask);
			LOGMASKED(LOG_INT_WRITES, "%s: peripheral write, INTENC = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			check_interrupts();
			break;
		case REG_INTTEST1:
			LOGMASKED(LOG_INT_WRITES, "%s: peripheral write, INTTEST1 = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_INTTEST2:
			LOGMASKED(LOG_INT_WRITES, "%s: peripheral write, INTTEST2 = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_PUMPCON:
			LOGMASKED(LOG_POWER_WRITES, "%s: peripheral write, PUMPCON = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_CODR:
			LOGMASKED(LOG_CODEC_WRITES, "%s: peripheral write, CODR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_CONFG:
			LOGMASKED(LOG_CODEC_WRITES, "%s: peripheral write, CONFG = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_COLFG:
			LOGMASKED(LOG_CODEC_WRITES, "%s: peripheral write, COLFG = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_COEOI:
			LOGMASKED(LOG_CODEC_WRITES | LOG_EOI_WRITES, "%s: peripheral write, COEOI = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_COTEST:
			LOGMASKED(LOG_CODEC_WRITES, "%s: peripheral write, COTEST = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_SSCR0:
			LOGMASKED(LOG_SSP_WRITES, "%s: peripheral write, SSCR0 = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_SSCR1:
			LOGMASKED(LOG_SSP_WRITES, "%s: peripheral write, SSCR1 = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_SSDR:
			LOGMASKED(LOG_SSP_WRITES, "%s: peripheral write, SSDR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			// TODO: Proper SPI support
			if (data != 0)
			{
				m_last_ssi_request = (m_last_ssi_request >> 8) | (data & 0xff00);
			}
			break;
		case REG_SSSR:
			LOGMASKED(LOG_SSP_WRITES, "%s: peripheral write, SSSR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_TC1LOAD:
			LOGMASKED(LOG_TIMER_WRITES, "%s: peripheral write, TC1LOAD = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			m_timer_reload[0] = data;
			m_timer_value[0] = data;
			break;
		case REG_TC1VAL:
			LOGMASKED(LOG_TIMER_WRITES, "%s: peripheral write, TC1VAL = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_TC1CTRL:
			LOGMASKED(LOG_TIMER_WRITES, "%s: peripheral write, TC1CTRL = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			set_timer_ctrl(0, data);
			break;
		case REG_TC1EOI:
			LOGMASKED(LOG_TIMER_WRITES | LOG_EOI_WRITES, "%s: peripheral write, TC1EOI = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			m_pending_ints &= ~(1 << IRQ_TC1OI);
			check_interrupts();
			break;
		case REG_TC2LOAD:
			LOGMASKED(LOG_TIMER_WRITES, "%s: peripheral write, TC2LOAD = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			m_timer_reload[1] = data;
			m_timer_value[1] = data;
			break;
		case REG_TC2VAL:
			LOGMASKED(LOG_TIMER_WRITES, "%s: peripheral write, TC2VAL = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_TC2CTRL:
			LOGMASKED(LOG_TIMER_WRITES, "%s: peripheral write, TC2CTRL = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			set_timer_ctrl(1, data);
			break;
		case REG_TC2EOI:
			LOGMASKED(LOG_TIMER_WRITES | LOG_EOI_WRITES, "%s: peripheral write, TC2EOI = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			m_pending_ints &= ~(1 << IRQ_TC2OI);
			check_interrupts();
			break;

		case REG_BZCONT:
			m_buzzer_ctrl = data;
			if (!BIT(m_buzzer_ctrl, 1))
			{
				m_speaker->level_w(BIT(m_buzzer_ctrl, 0));
			}
			LOGMASKED(LOG_BUZZER_WRITES, "%s: peripheral write, BZCONT = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_RTCDRL:
			LOGMASKED(LOG_RTC_WRITES, "%s: peripheral write, RTCDRL = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			m_rtc &= 0xffff0000;
			m_rtc |= (uint16_t)data;
			break;
		case REG_RTCDRU:
			LOGMASKED(LOG_RTC_WRITES, "%s: peripheral write, RTCDRU = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			m_rtc &= 0x0000ffff;
			m_rtc |= data << 16;
			break;
		case REG_RTCMRL:
			LOGMASKED(LOG_RTC_WRITES, "%s: peripheral write, RTCMRL = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_RTCMRU:
			LOGMASKED(LOG_RTC_WRITES, "%s: peripheral write, RTCMRU = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_RTCEOI:
			LOGMASKED(LOG_RTC_WRITES | LOG_EOI_WRITES, "%s: peripheral write, RTCEOI = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_PADR:
			LOGMASKED(LOG_GPIO_WRITES, "%s: peripheral write, PADR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			m_ports[PORTA] = data;
			break;
		case REG_PBDR:
		{
			LOGMASKED(LOG_GPIO_WRITES, "%s: peripheral write, PBDR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			const uint8_t old = m_ports[PORTB];
			m_ports[PORTB] = data;
			const uint8_t diff = old ^ data;
			if (BIT(diff, 0))
				m_etna->eeprom_cs_in(BIT(data, 0));
			if (BIT(diff, 1))
				m_etna->eeprom_clk_in(BIT(data, 1));
			if (diff & 0x3c)
				LOGMASKED(LOG_PIN_WRITES, "Contrast: %d\n", (data >> 2) & 0x0f);
			if (BIT(diff, 6))
				LOGMASKED(LOG_PIN_WRITES, "Case Open: %d\n", BIT(data, 6));
			if (BIT(diff, 7))
				LOGMASKED(LOG_PIN_WRITES, "ETNA CompactFlash Power: %d\n", BIT(data, 7));
			break;
		}
		case REG_PCDR:
		{
			LOGMASKED(LOG_GPIO_WRITES, "%s: peripheral write, PCDR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			const uint8_t old = m_ports[PORTC];
			m_ports[PORTC] = data;
			const uint8_t diff = old ^ data;
			if (BIT(diff, 0))
				LOGMASKED(LOG_PIN_WRITES, "RS232 RTS: %d\n", BIT(data, 0));
			if (BIT(diff, 1))
				LOGMASKED(LOG_PIN_WRITES, "RS232 DTR Toggle: %d\n", BIT(data, 1));
			if (BIT(diff, 2))
				LOGMASKED(LOG_PIN_WRITES, "Disable Power LED: %d\n", BIT(data, 2));
			if (BIT(diff, 3))
				LOGMASKED(LOG_PIN_WRITES, "Enable UART1: %d\n", BIT(data, 3));
			if (BIT(diff, 4))
				LOGMASKED(LOG_PIN_WRITES, "LCD Backlight: %d\n", BIT(data, 4));
			if (BIT(diff, 5))
				LOGMASKED(LOG_PIN_WRITES, "Enable UART0: %d\n", BIT(data, 5));
			if (BIT(diff, 6))
				LOGMASKED(LOG_PIN_WRITES, "Dictaphone: %d\n", BIT(data, 6));
			break;
		}
		case REG_PDDR:
		{
			LOGMASKED(LOG_GPIO_WRITES, "%s: peripheral write, PDDR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			const uint8_t old = m_ports[PORTD];
			m_ports[PORTD] = data;
			const uint8_t diff = old ^ data;
			if (BIT(diff, 0))
				LOGMASKED(LOG_PIN_WRITES, "Codec Enable: %d\n", BIT(data, 0));
			if (BIT(diff, 1))
				LOGMASKED(LOG_PIN_WRITES, "Audio Amp Enable: %d\n", BIT(data, 1));
			if (BIT(diff, 2))
				LOGMASKED(LOG_PIN_WRITES, "LCD Power: %d\n", BIT(data, 2));
			if (BIT(diff, 3))
				LOGMASKED(LOG_PIN_WRITES, "ETNA Door: %d\n", BIT(data, 3));
			if (BIT(diff, 4))
				LOGMASKED(LOG_PIN_WRITES, "Sled: %d\n", BIT(data, 4));
			if (BIT(diff, 5))
				LOGMASKED(LOG_PIN_WRITES, "Pump Power2: %d\n", BIT(data, 5));
			if (BIT(diff, 6))
				LOGMASKED(LOG_PIN_WRITES, "Pump Power1: %d\n", BIT(data, 6));
			if (BIT(diff, 7))
				LOGMASKED(LOG_PIN_WRITES, "ETNA Error: %d\n", BIT(data, 7));
			break;
		}
		case REG_PADDR:
			LOGMASKED(LOG_GPIO_WRITES, "%s: peripheral write, PADDR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_PBDDR:
			LOGMASKED(LOG_GPIO_WRITES, "%s: peripheral write, PBDDR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_PCDDR:
			LOGMASKED(LOG_GPIO_WRITES, "%s: peripheral write, PCDDR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_PDDDR:
			LOGMASKED(LOG_GPIO_WRITES, "%s: peripheral write, PDDDR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_PEDR:
			LOGMASKED(LOG_GPIO_WRITES, "%s: peripheral write, PEDR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_PEDDR:
			LOGMASKED(LOG_GPIO_WRITES, "%s: peripheral write, PEDDR = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		case REG_KSCAN:
			m_kbd_scan = data;
			LOGMASKED(LOG_KBD_WRITES, "%s: peripheral write, KSCAN = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
		case REG_LCDMUX:
			LOGMASKED(LOG_LCD_WRITES, "%s: peripheral write, LCDMUX = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;

		default:
			LOGMASKED(LOG_UNKNOWNS, "%s: peripheral write, Unknown = %08x & %08x\n", machine().describe_context(), data, mem_mask);
			break;
	}
}

uint8_t psion5mx_state::read_keyboard()
{
	if (BIT(m_kbd_scan, 3))
	{
		return m_kbd_cols[m_kbd_scan & 7]->read();
	}
	else if (m_kbd_scan == 0)
	{
		uint8_t rows = 0;
		for (int i = 0; i < 8; i++)
		{
			rows |= m_kbd_cols[i]->read();
		}
		return rows;
	}
	return 0x00;
}

void psion5mx_state::main_map(address_map &map)
{
	map(0x00000000, 0x009fffff).rom().region("maincpu", 0);
	map(0x20000000, 0x20000fff).rw(m_etna, FUNC(etna_device::regs_r), FUNC(etna_device::regs_w));
	map(0x80000000, 0x80000fff).rw(FUNC(psion5mx_state::periphs_r), FUNC(psion5mx_state::periphs_w));
	map(0xc0000000, 0xc03fffff).ram().mirror(0x1fc00000).share("lcd_ram");
}

void psion5mx_state::palette_init(palette_device &palette)
{
	for (int i = 0; i < 16; i++)
	{
		const int r = (0x99 * i) / 15;
		const int g = (0xaa * i) / 15;
		const int b = (0x88 * i) / 15;
		m_palette->set_pen_color(15 - i, rgb_t(r, g, b));
	}
}

uint32_t psion5mx_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const uint8_t *lcd_buf = (uint8_t*)&m_lcd_ram[(m_lcd_display_base_addr & 0x003fffff) >> 2];
	static const int width = 640;
	static const int height = 240;

	// fetch palette
	LOGMASKED(LOG_DISPLAY, "%02x %02x %02x %02x\n", lcd_buf[0], lcd_buf[1], lcd_buf[2], lcd_buf[3]);

	const int bpp = 1 << (lcd_buf[1] >> 4);
	const int ppb = 8 / bpp;
	LOGMASKED(LOG_DISPLAY, "bpp: %d, ppb: %d\n", bpp, ppb);
	uint16_t palette[16] = {};
	for (int i = 0; i < 16; i++)
	{
		palette[i] = lcd_buf[i*2] | ((lcd_buf[i*2+1] << 8) & 0xf00);
		LOGMASKED(LOG_DISPLAY, "palette[%d]: %04x\n", i, palette[i]);
	}

	const pen_t *pen = m_palette->pens();

	// build our image out
	const int line_width = (width * bpp) / 8;
	for (int y = 0; y < height; y++)
	{
		const int line_offs = 0x20 + (line_width * y);
		uint32_t *line = &bitmap.pix(y);
		for (int x = 0; x < width; x++)
		{
			const uint8_t byte = lcd_buf[line_offs + (x / ppb)];
			const int shift = (x & (ppb - 1)) * bpp;
			const int mask = (1 << bpp) - 1;
			const int pal_idx = (byte >> shift) & mask;

			line[x] = pen[palette[pal_idx]];
		}
	}
	return 0;
}

INPUT_CHANGED_MEMBER(psion5mx_state::touch_down)
{
	if (newval)
	{
		logerror("Flagging EINT3\n");
		m_pending_ints |= (1 << IRQ_EINT3);
	}
	else
	{
		logerror("Unflagging EINT3\n");
		m_pending_ints &= ~(1 << IRQ_EINT3);
	}
	check_interrupts();
}

/* Input ports */
INPUT_PORTS_START( psion5mx )
	PORT_START("TOUCHX")
	PORT_BIT(0x3ff, 362, IPT_LIGHTGUN_X) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_MINMAX(42,681) PORT_SENSITIVITY(25) PORT_KEYDELTA(13)

	PORT_START("TOUCHY")
	PORT_BIT(0x0ff, 125, IPT_LIGHTGUN_Y) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_MINMAX(5,244) PORT_SENSITIVITY(25) PORT_KEYDELTA(13)

	PORT_START("TOUCH")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Touch") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psion5mx_state::touch_down), 0)
	PORT_BIT(0xfffe, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^') PORT_CHAR('>')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%') PORT_CHAR('<')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') PORT_CHAR('@')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR(163) PORT_CHAR('\\')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"') PORT_CHAR('#')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!') PORT_CHAR('_')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Dictaphone Record") PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("'") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('~') PORT_CHAR(':')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Del<-") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')') PORT_CHAR('}')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(') PORT_CHAR('{')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*') PORT_CHAR(']')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&') PORT_CHAR('[')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Dictaphone Play") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(128)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR(';')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR('=')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR('-')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') PORT_CHAR('+')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Menu") PORT_CODE(KEYCODE_END)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('?')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Fn") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('/')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Dictaphone Stop") PORT_CODE(KEYCODE_PGUP)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

/* basic configuration for 2 lines display */
void psion5mx_state::psion5mx(machine_config &config)
{
	/* basic machine hardware */
	ARM710T(config, m_maincpu, 36000000); // 36MHz, per wikipedia
	m_maincpu->set_addrmap(AS_PROGRAM, &psion5mx_state::main_map);

	ETNA(config, m_etna);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(0)); /* not accurate */
	screen.set_screen_update(FUNC(psion5mx_state::screen_update));
	screen.set_size(640, 240);
	screen.set_visarea(0, 640-1, 0, 240-1);

	PALETTE(config, m_palette, FUNC(psion5mx_state::palette_init), 16);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);
}

/* ROM definition */

ROM_START( psion5mx )
	ROM_REGION( 0xa00000, "maincpu", 0 )
	ROM_LOAD( "5mx.rom", 0x000000, 0xa00000, CRC(a1e2d038) SHA1(4c082321264e1ae7fe77699e59b8960460690fa6) )
ROM_END

/* Driver */

//    YEAR  NAME        PARENT   COMPAT  MACHINE    INPUT     CLASS           INIT        COMPANY  FULLNAME  FLAGS
COMP( 1999, psion5mx,   0,       0,      psion5mx,  psion5mx, psion5mx_state, empty_init, "Psion", "Series 5mx",    MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )



psionhc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/******************************************************************************

    Psion HC Series

    TODO:
    - battery backed RAM

******************************************************************************/

#include "emu.h"
#include "cpu/nec/nec.h"
#include "machine/nvram.h"
#include "machine/psion_asic1.h"
#include "machine/psion_asic2.h"
#include "machine/psion_asic3.h"
#include "machine/psion_ssd.h"
#include "machine/ram.h"
#include "sound/spkrdev.h"
#include "bus/psion/module/slot.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "utf8.h"


namespace {

class psionhc_state : public driver_device
{
public:
	psionhc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_asic1(*this, "asic1")
		, m_asic2(*this, "asic2")
		, m_asic3(*this, "asic3")
		, m_ram(*this, "ram")
		, m_nvram(*this, "nvram")
		, m_palette(*this, "palette")
		, m_keyboard(*this, "COL%u", 0U)
		, m_speaker(*this, "speaker")
		, m_ssd(*this, "ssd%u", 1U)
		, m_exp(*this, "exp%u", 0U)
	{ }

	void psionhc100(machine_config &config);
	void psionhc110(machine_config &config);
	void psionhc120(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(key_on);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<psion_asic1_device> m_asic1;
	required_device<psion_asic2_device> m_asic2;
	required_device<psion_asic3_device> m_asic3;
	required_device<ram_device> m_ram;
	required_device<nvram_device> m_nvram;
	required_device<palette_device> m_palette;
	required_ioport_array<8> m_keyboard;
	required_device<speaker_sound_device> m_speaker;
	required_device_array<psion_ssd_device, 2> m_ssd;
	required_device_array<psion_module_slot_device, 2> m_exp;

	void palette_init(palette_device &palette);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void asic1_map(address_map &map) ATTR_COLD;

	uint8_t port_data_r();
	void port_data_w(uint8_t data);

	int m_dr = 0;
};


void psionhc_state::machine_start()
{
	m_asic1->space(0).install_ram(0, m_ram->mask(), m_ram->pointer());
	m_nvram->set_base(m_ram->pointer(), m_ram->size());
}

void psionhc_state::machine_reset()
{
}


void psionhc_state::mem_map(address_map &map)
{
	map(0x00000, 0xfffff).rw(m_asic1, FUNC(psion_asic1_device::mem_r), FUNC(psion_asic1_device::mem_w));
}

void psionhc_state::io_map(address_map &map)
{
	map(0x0000, 0x001f).rw(m_asic1, FUNC(psion_asic1_device::io_r), FUNC(psion_asic1_device::io_w));
	map(0x0080, 0x008f).rw(m_asic2, FUNC(psion_asic2_device::io_r), FUNC(psion_asic2_device::io_w)).umask16(0x00ff);
	map(0x0100, 0x01ff).rw(m_exp[0], FUNC(psion_module_slot_device::io_r), FUNC(psion_module_slot_device::io_w));
	map(0x0200, 0x02ff).rw(m_exp[1], FUNC(psion_module_slot_device::io_r), FUNC(psion_module_slot_device::io_w));
}

void psionhc_state::asic1_map(address_map &map)
{
	map(0x00000, 0x7ffff).noprw();
	map(0x80000, 0xfffff).rom().region("flash", 0);
}


static INPUT_PORTS_START( psionhc_uk )
	PORT_START("COL0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))     PORT_NAME(UTF8_RIGHT" Info")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))      PORT_NAME(UTF8_LEFT" Task")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))      PORT_NAME(UTF8_DOWN" PG Dn")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))        PORT_NAME(UTF8_UP" Pg Up")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                                                   PORT_NAME("Menu")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC))       PORT_NAME("Esc")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('+')  PORT_CHAR('=')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                                                   PORT_NAME("Off")

	PORT_START("COL1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)          PORT_CHAR('f')  PORT_CHAR('F')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)          PORT_CHAR('e')  PORT_CHAR('E')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)          PORT_CHAR('d')  PORT_CHAR('D')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)          PORT_CHAR('c')  PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)          PORT_CHAR('b')  PORT_CHAR('B')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('a')  PORT_CHAR('A')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)          PORT_CHAR('l')  PORT_CHAR('L')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)          PORT_CHAR('k')  PORT_CHAR('K')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)          PORT_CHAR('j')  PORT_CHAR('J')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)          PORT_CHAR('i')  PORT_CHAR('I')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)          PORT_CHAR('h')  PORT_CHAR('H')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)          PORT_CHAR('g')  PORT_CHAR('G')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_CHAR('0')  PORT_CHAR(',')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)          PORT_CHAR('r')  PORT_CHAR('R')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q')  PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)          PORT_CHAR('p')  PORT_CHAR('P')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)          PORT_CHAR('o')  PORT_CHAR('O')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)          PORT_CHAR('n')  PORT_CHAR('N')      PORT_NAME("n N No")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)          PORT_CHAR('m')  PORT_CHAR('M')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')                      PORT_NAME("Space")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)          PORT_CHAR('x')  PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)          PORT_CHAR('w')  PORT_CHAR('W')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)          PORT_CHAR('v')  PORT_CHAR('V')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)          PORT_CHAR('u')  PORT_CHAR('U')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)          PORT_CHAR('t')  PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)          PORT_CHAR('s')  PORT_CHAR('S')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)            PORT_NAME("Shift")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y')  PORT_CHAR('Y')      PORT_NAME("y Y Yes")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/')  PORT_CHAR('%')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_CHAR('9')  PORT_CHAR(')')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8')  PORT_CHAR('(')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7')  PORT_CHAR('&')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)        PORT_CHAR(0x09)                     PORT_NAME("Tab")
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z')  PORT_CHAR('Z')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)                               PORT_CHAR('*')  PORT_CHAR('@')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_CHAR('6')  PORT_CHAR(':')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)          PORT_CHAR('5')  PORT_CHAR('?')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)          PORT_CHAR('4')  PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(0x08)                     PORT_NAME("Del")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                                                   PORT_NAME("Backlight")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)                       PORT_NAME("Enter")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-')  PORT_CHAR('\\')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)          PORT_CHAR('3')  PORT_CHAR(0xa3)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)          PORT_CHAR('2')  PORT_CHAR('\'')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)          PORT_CHAR('1')  PORT_CHAR('!')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT)       PORT_CHAR(UCHAR_MAMEKEY(LALT))      PORT_NAME("Psion Lock")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                                                   PORT_NAME("Contrast")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("ON_OFF")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F12) PORT_CHAR(UCHAR_MAMEKEY(F12)) PORT_NAME("On Off") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psionhc_state::key_on), 0)
INPUT_PORTS_END

//static INPUT_PORTS_START( psionhc_num )
//  PORT_START("COL0")
//  PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)          PORT_CHAR('n')  PORT_CHAR('N')      PORT_NAME("No")
//  PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
//  PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))        PORT_NAME(UTF8_UP)
//  PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))      PORT_NAME(UTF8_DOWN)
//  PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(0x08)                     PORT_NAME("Del")
//  PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC))       PORT_NAME("C")
//  PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('+')
//  PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                                                   PORT_NAME("Off")
//
//  PORT_START("COL1")
//  PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')
//  PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_CHAR('0')
//  PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)                               PORT_CHAR('*')
//  PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD)                               PORT_CHAR('%')
//  PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                                                   PORT_NAME("Info")
//  PORT_BIT(0xe0, IP_ACTIVE_HIGH, IPT_UNUSED)
//
//  PORT_START("COL2")
//  PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)
//
//  PORT_START("COL3")
//  PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)         PORT_CHAR(UCHAR_MAMEKEY(F1))        PORT_NAME("F1")
//  PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)         PORT_CHAR(UCHAR_MAMEKEY(F2))        PORT_NAME("F2")
//  PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)         PORT_CHAR(UCHAR_MAMEKEY(F3))        PORT_NAME("F3")
//  PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)         PORT_CHAR(UCHAR_MAMEKEY(F4))        PORT_NAME("F4")
//  PORT_BIT(0xf0, IP_ACTIVE_HIGH, IPT_UNUSED)
//
//  PORT_START("COL4")
//  PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)            PORT_NAME("Shift")
//  PORT_BIT(0xbf, IP_ACTIVE_HIGH, IPT_UNUSED)
//
//  PORT_START("COL5")
//  PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-')
//  PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))      PORT_NAME(UTF8_LEFT)
//  PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_CHAR('9')
//  PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8')
//  PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7')
//  PORT_BIT(0xe0, IP_ACTIVE_HIGH, IPT_UNUSED)
//
//  PORT_START("COL6")
//  PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)//
//  PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))     PORT_NAME(UTF8_RIGHT)
//  PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_CHAR('6')
//  PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)          PORT_CHAR('5')
//  PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)          PORT_CHAR('4')
//  PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/')
//  PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                                                   PORT_NAME("Backlight")
//  PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
//
//  PORT_START("COL7")
//  PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)                       PORT_NAME("Enter")
//  PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y')  PORT_CHAR('Y')      PORT_NAME("Yes")
//  PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)          PORT_CHAR('3')
//  PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)          PORT_CHAR('2')
//  PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)          PORT_CHAR('1')
//  PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
//  PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                                                   PORT_NAME("Contrast")
//  PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
//
//  PORT_START("ON_OFF")
//  PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F12) PORT_CHAR(UCHAR_MAMEKEY(F12)) PORT_NAME("On Off") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psionhc_state::key_on), 0)
//INPUT_PORTS_END

INPUT_CHANGED_MEMBER(psionhc_state::key_on)
{
	if (newval)
	{
		m_asic2->on_clr_w(newval);
	}
}


uint8_t psionhc_state::port_data_r()
{
	// b0 NC
	// b1 NC
	// b2 LED ?
	// b3 VSLED ?
	// b4 SCK6
	// b5 SD6
	// b6 ?
	return 0x00;
}

void psionhc_state::port_data_w(uint8_t data)
{
	// b7 BackLight ?
}


void psionhc_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(131, 136, 139));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}


void psionhc_state::psionhc100(machine_config &config)
{
	V30(config, m_maincpu, 7.68_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &psionhc_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &psionhc_state::io_map);
	m_maincpu->set_irq_acknowledge_callback(m_asic1, FUNC(psion_asic1_device::inta_cb));

	RAM(config, m_ram).set_default_size("128K");
	NVRAM(config, "nvram", nvram_device::DEFAULT_NONE);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_size(160, 80);
	screen.set_visarea_full();
	screen.set_refresh_hz(66);
	screen.set_screen_update(m_asic1, FUNC(psion_asic1_device::screen_update_single));
	screen.set_palette(m_palette);
	PALETTE(config, "palette", FUNC(psionhc_state::palette_init), 2);

	PSION_ASIC1(config, m_asic1, 7.68_MHz_XTAL);
	m_asic1->set_screen("screen");
	m_asic1->set_laptop_mode(false);
	m_asic1->set_addrmap(0, &psionhc_state::asic1_map);
	m_asic1->int_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_asic1->nmi_cb().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_asic1->frcovl_cb().set(m_asic2, FUNC(psion_asic2_device::frcovl_w));

	PSION_ASIC2(config, m_asic2, 7.68_MHz_XTAL);
	m_asic2->int_cb().set(m_asic1, FUNC(psion_asic1_device::eint3_w));
	m_asic2->nmi_cb().set(m_asic1, FUNC(psion_asic1_device::enmi_w));
	m_asic2->cbusy_cb().set_inputline(m_maincpu, NEC_INPUT_LINE_POLL);
	m_asic2->buz_cb().set(m_speaker, FUNC(speaker_sound_device::level_w));
	m_asic2->buzvol_cb().set([this](int state) { m_speaker->set_output_gain(ALL_OUTPUTS, state ? 1.0 : 0.25); });
	m_asic2->dr_cb().set([this](int state) { m_dr = state; });
	m_asic2->col_cb().set([this](uint8_t data) { return m_keyboard[data & 7]->read(); });
	m_asic2->read_pd_cb().set(FUNC(psionhc_state::port_data_r));
	m_asic2->write_pd_cb().set(FUNC(psionhc_state::port_data_w));
	m_asic2->data_r<0>().set(m_asic3, FUNC(psion_asic3_device::data_r));        // Power supply (ASIC3)
	m_asic2->data_w<0>().set(m_asic3, FUNC(psion_asic3_device::data_w));
	m_asic2->data_r<1>().set(m_ssd[0], FUNC(psion_ssd_device::data_r));         // SSD
	m_asic2->data_w<1>().set(m_ssd[0], FUNC(psion_ssd_device::data_w));
	m_asic2->data_r<2>().set(m_ssd[1], FUNC(psion_ssd_device::data_r));         // SSD
	m_asic2->data_w<2>().set(m_ssd[1], FUNC(psion_ssd_device::data_w));
	m_asic2->data_r<5>().set(m_exp[1], FUNC(psion_module_slot_device::data_r)); // Expansion port B
	m_asic2->data_w<5>().set(m_exp[1], FUNC(psion_module_slot_device::data_w));
	m_asic2->data_r<6>().set(m_exp[0], FUNC(psion_module_slot_device::data_r)); // Expansion port A
	m_asic2->data_w<6>().set(m_exp[0], FUNC(psion_module_slot_device::data_w));
	//m_asic2->data_r<7>().set(m_exp[2], FUNC(psion_module_slot_device::data_r));
	//m_asic2->data_w<7>().set(m_exp[2], FUNC(psion_module_slot_device::data_w));

	PSION_PSU_ASIC3(config, m_asic3);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00); // Piezo buzzer

	PSION_SSD(config, m_ssd[0]);
	m_ssd[0]->door_cb().set(m_asic2, FUNC(psion_asic2_device::dnmi_w));
	PSION_SSD(config, m_ssd[1]);
	m_ssd[1]->door_cb().set(m_asic2, FUNC(psion_asic2_device::dnmi_w));

	PSION_MODULE_SLOT(config, m_exp[0], psion_hcmodule_devices, nullptr); // RS232/Parallel
	m_exp[0]->intr_cb().set(m_asic1, FUNC(psion_asic1_device::eint2_w));
	PSION_MODULE_SLOT(config, m_exp[1], psion_hcmodule_devices, nullptr);
	m_exp[1]->intr_cb().set(m_asic1, FUNC(psion_asic1_device::eint1_w));

	SOFTWARE_LIST(config, "ssd_list").set_original("psion_ssd").set_filter("HC");
}

void psionhc_state::psionhc110(machine_config &config)
{
	psionhc100(config);

	m_ram->set_default_size("256K");
}

void psionhc_state::psionhc120(machine_config &config)
{
	psionhc100(config);

	m_ram->set_default_size("512K");
}


ROM_START( psionhc120 )
	ROM_REGION16_LE(0x80000, "flash", ROMREGION_ERASEFF)
	// 2 x 28F010 128k flash chips, V1.51F 050592, V1.62F, V1.64F, V1.71F also known to exist
	ROM_SYSTEM_BIOS(0, "172f", "V1.72F")
	ROMX_LOAD("v172f_1.bin", 0x00000, 0x20000, CRC(5ba21d09) SHA1(a9348eeb223cdc767e434ec34beae546defab108), ROM_BIOS(0))
	ROM_RELOAD(0x20000, 0x20000)
	ROMX_LOAD("v172f_2.bin", 0x40000, 0x20000, CRC(4436f332) SHA1(c6154cd948260729e079c47dea47def5cdc99a36), ROM_BIOS(0))
	ROM_RELOAD(0x60000, 0x20000)
	ROM_SYSTEM_BIOS(1, "170f", "V1.70F")
	ROMX_LOAD("v170f_1.bin", 0x00000, 0x20000, CRC(f733a7ab) SHA1(e49273a3d97d5ffb9f9b5958ad031aeedb40af01), ROM_BIOS(1))
	ROM_RELOAD(0x20000, 0x20000)
	ROMX_LOAD("v170f_2.bin", 0x40000, 0x20000, CRC(edd6a78d) SHA1(61344cd38928183b0e2c58ebfe92984518d8e731), ROM_BIOS(1))
	ROM_RELOAD(0x60000, 0x20000)
	ROM_SYSTEM_BIOS(2, "164f", "V1.64F")
	ROMX_LOAD("v164f_1.bin", 0x00000, 0x20000, CRC(33d2a85e) SHA1(80d01396a626387e662ffb8da746eb863a040072), ROM_BIOS(2))
	ROM_RELOAD(0x20000, 0x20000)
	ROMX_LOAD("v164f_2.bin", 0x40000, 0x20000, CRC(12da077a) SHA1(ef1a9cb651bf8020a6428f0044c01d8603ac225f), ROM_BIOS(2))
	ROM_RELOAD(0x60000, 0x20000)
ROM_END

#define rom_psionhc100 rom_psionhc120
#define rom_psionhc110 rom_psionhc120

} // anonymous namespace


//    YEAR  NAME         PARENT       COMPAT  MACHINE       INPUT        CLASS           INIT         COMPANY   FULLNAME    FLAGS
COMP( 1991, psionhc100,  psionhc120,  0,      psionhc100,   psionhc_uk,  psionhc_state,  empty_init,  "Psion",  "HC 100",   MACHINE_SUPPORTS_SAVE )
COMP( 1991, psionhc110,  psionhc120,  0,      psionhc110,   psionhc_uk,  psionhc_state,  empty_init,  "Psion",  "HC 110",   MACHINE_SUPPORTS_SAVE )
COMP( 1991, psionhc120,  0,           0,      psionhc120,   psionhc_uk,  psionhc_state,  empty_init,  "Psion",  "HC 120",   MACHINE_SUPPORTS_SAVE )



psx.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:smf, pSXAuthor, R. Belmont
/***************************************************************************

  Sony PlayStation
  ================
  Preliminary driver by smf
  Additional development by pSXAuthor and R. Belmont

***************************************************************************/

#include "emu.h"

#include "bus/psx/ctlrport.h"
#include "bus/psx/parallel.h"
#include "cpu/m6805/hd6305.h"
#include "cpu/psx/psx.h"
#include "imagedev/cdromimg.h"
#include "imagedev/snapquik.h"
#include "psxcd.h"
#include "machine/ram.h"
#include "sound/spu.h"
#include "video/psx.h"

#include "debugger.h"
#include "screen.h"
#include "softlist.h"
#include "speaker.h"

#include "multibyte.h"

#include <zlib.h>


namespace {

class psx1_state : public driver_device
{
public:
	psx1_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ram(*this, "maincpu:ram"),
		m_parallel(*this, "parallel"),
		m_psxcd(*this, "psxcd"),
		m_cd_softlist(*this, "cd_list")
	{
	}

	void psx_base(machine_config &config);
	void pse(machine_config &config);
	void psu(machine_config &config);
	void psj(machine_config &config);

private:
	std::vector<uint8_t> m_exe_buffer;
	void psxexe_conv32(uint32_t *uint32);
	int load_psxexe(std::vector<uint8_t> buffer);
	void cpe_set_register(int r, int v);
	int load_cpe(std::vector<uint8_t> buffer);
	int load_psf(std::vector<uint8_t> buffer);
	uint16_t parallel_r(offs_t offset);
	void parallel_w(offs_t offset, uint16_t data);
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_exe);
	void cd_dma_read( uint32_t *p_n_psxram, uint32_t n_address, int32_t n_size );
	void cd_dma_write( uint32_t *p_n_psxram, uint32_t n_address, int32_t n_size );
	required_device<psxcpu_device> m_maincpu;
	required_device<ram_device> m_ram;

	void psx_map(address_map &map) ATTR_COLD;
	void subcpu_map(address_map &map) ATTR_COLD;

	required_device<psx_parallel_slot_device> m_parallel;
	required_device<psxcd_device> m_psxcd;
	required_device<software_list_device> m_cd_softlist;
};


void psx1_state::psxexe_conv32(uint32_t *uint32)
{
	uint8_t *uint8 = (uint8_t *)uint32;

	*(uint32) = uint8[0] | (uint8[1] << 8) | (uint8[2] << 16) | (uint8[3] << 24);
}

int psx1_state::load_psxexe(std::vector<uint8_t> buffer)
{
	struct PSXEXE_HEADER
	{
		uint8_t id[8];
		uint32_t text;    /* SCE only */
		uint32_t data;    /* SCE only */
		uint32_t pc0;
		uint32_t gp0;     /* SCE only */
		uint32_t t_addr;
		uint32_t t_size;
		uint32_t d_addr;  /* SCE only */
		uint32_t d_size;  /* SCE only */
		uint32_t b_addr;  /* SCE only */
		uint32_t b_size;  /* SCE only */
		uint32_t s_addr;
		uint32_t s_size;
		uint32_t SavedSP;
		uint32_t SavedFP;
		uint32_t SavedGP;
		uint32_t SavedRA;
		uint32_t SavedS0;
		uint8_t dummy[0x800 - 76];
	};

	struct PSXEXE_HEADER *psxexe_header = reinterpret_cast<struct PSXEXE_HEADER *>(&buffer[0]);

	if (buffer.size() >= sizeof(struct PSXEXE_HEADER) &&
		memcmp(psxexe_header->id, "PS-X EXE", 8) == 0)
	{
		psxexe_conv32(&psxexe_header->text);
		psxexe_conv32(&psxexe_header->data);
		psxexe_conv32(&psxexe_header->pc0);
		psxexe_conv32(&psxexe_header->gp0);
		psxexe_conv32(&psxexe_header->t_addr);
		psxexe_conv32(&psxexe_header->t_size);
		psxexe_conv32(&psxexe_header->d_addr);
		psxexe_conv32(&psxexe_header->d_size);
		psxexe_conv32(&psxexe_header->b_addr);
		psxexe_conv32(&psxexe_header->b_size);
		psxexe_conv32(&psxexe_header->s_addr);
		psxexe_conv32(&psxexe_header->s_size);
		psxexe_conv32(&psxexe_header->SavedSP);
		psxexe_conv32(&psxexe_header->SavedFP);
		psxexe_conv32(&psxexe_header->SavedGP);
		psxexe_conv32(&psxexe_header->SavedRA);
		psxexe_conv32(&psxexe_header->SavedS0);

		/* todo: check size.. */

		logerror("psx_exe_load: pc    %08x\n", psxexe_header->pc0);
		logerror("psx_exe_load: org   %08x\n", psxexe_header->t_addr);
		logerror("psx_exe_load: len   %08x\n", psxexe_header->t_size);
		logerror("psx_exe_load: sp    %08x\n", psxexe_header->s_addr);
		logerror("psx_exe_load: len   %08x\n", psxexe_header->s_size);

		uint8_t *ram_pointer = m_ram->pointer();
		uint32_t ram_size = m_ram->size();

		uint8_t *text = reinterpret_cast<uint8_t *>(&buffer[sizeof(struct PSXEXE_HEADER)]);

		uint32_t address = psxexe_header->t_addr;
		uint32_t size = psxexe_header->t_size;
		while (size != 0)
		{
			ram_pointer[BYTE4_XOR_LE(address++) % ram_size] = *(text++);
			size--;
		}

		m_maincpu->set_state_int(PSXCPU_PC, psxexe_header->pc0);
		m_maincpu->set_state_int(PSXCPU_R28, psxexe_header->gp0);
		uint32_t stack = psxexe_header->s_addr + psxexe_header->s_size;
		if (stack != 0)
		{
			m_maincpu->set_state_int(PSXCPU_R29, stack);
			m_maincpu->set_state_int(PSXCPU_R30, stack);
		}

		return 1;
	}
	return 0;
}

void psx1_state::cpe_set_register(int r, int v)
{
	if (r < 0x80 && (r % 4) == 0)
	{
		logerror("psx_exe_load: r%-2d   %08x\n", r / 4, v);
		m_maincpu->set_state_int(PSXCPU_R0 + (r / 4), v);
	}
	else if (r == 0x80)
	{
		logerror("psx_exe_load: lo    %08x\n", v);
		m_maincpu->set_state_int(PSXCPU_LO, v);
	}
	else if (r == 0x84)
	{
		logerror("psx_exe_load: hi    %08x\n", v);
		m_maincpu->set_state_int(PSXCPU_HI, v);
	}
	else if (r == 0x88)
	{
		logerror("psx_exe_load: sr    %08x\n", v);
		m_maincpu->set_state_int(PSXCPU_CP0R12, v);
	}
	else if (r == 0x8c)
	{
		logerror("psx_exe_load: cause %08x\n", v);
		m_maincpu->set_state_int(PSXCPU_CP0R13, v);
	}
	else if (r == 0x90)
	{
		logerror("psx_exe_load: pc    %08x\n", v);
		m_maincpu->set_state_int(PSXCPU_PC, v);
	}
	else if (r == 0x94)
	{
		logerror("psx_exe_load: prid  %08x\n", v);
		m_maincpu->set_state_int(PSXCPU_CP0R15, v);
	}
	else
	{
		logerror("psx_exe_load: invalid register %04x/%08x\n", r, v);
	}
}

int psx1_state::load_cpe(std::vector<uint8_t> buffer)
{
	if (buffer.size() >= 4 &&
		memcmp(reinterpret_cast<void *>(&buffer[0]), "CPE\001", 4) == 0)
	{
		int offset = 4;

		for (;;)
		{
			if (offset >= buffer.size() || buffer[offset] > 8)
			{
				break;
			}

			switch (buffer[offset++])
			{
			case 0:
				/* end of file */
				return 1;

			case 1:
				/* read bytes */
				{
					uint32_t address = get_u32le(&buffer[offset]);
					uint32_t size = get_u32le(&buffer[offset + 4]);

					uint8_t *ram_pointer = m_ram->pointer();
					uint32_t ram_size = m_ram->size();

					offset += 8;

					logerror("psx_exe_load: org   %08x\n", address);
					logerror("psx_exe_load: len   %08x\n", size);

					while (size > 0)
					{
						ram_pointer[BYTE4_XOR_LE(address) % ram_size] = buffer[offset++];
						address++;
						size--;
					}
				}
				break;

			case 2:
				/* run address: not tested */
				{
					uint32_t v = get_u32le(&buffer[offset]);

					offset += 4;

					cpe_set_register(0x90, v);
				}
				break;

			case 3:
				/* set reg to longword */
				{
					uint16_t r = get_u16le(&buffer[offset]);
					uint32_t v = get_u32le(&buffer[offset + 2]);

					offset += 6;

					cpe_set_register(r, v);
				}
				break;

			case 4:
				/* set reg to word: not tested */
				{
					uint16_t r = get_u16le(&buffer[offset]);
					uint16_t v = get_u16le(&buffer[offset + 2]);

					offset += 4;

					cpe_set_register(r, v);
				}
				break;

			case 5:
				/* set reg to byte: not tested */
				{
					uint16_t r = get_u16le(&buffer[offset]);
					uint8_t v = buffer[offset + 2];

					offset += 3;

					cpe_set_register(r, v);
				}
				break;

			case 6:
				/* set reg to 3-byte: not tested */
				{
					uint16_t r = get_u16le(&buffer[offset]);
					uint32_t v = get_u24le(&buffer[offset + 2]);

					offset += 5;

					cpe_set_register(r, v);
				}
				break;

			case 7:
				/* workspace: not tested */
				offset += 4;
				break;

			case 8:
				/* unit */
				{
					int unit = buffer[offset + 0];

					offset++;

					logerror("psx_exe_load: unit  %08x\n", unit);
				}
				break;
			}
		}
	}

	return 0;
}

int psx1_state::load_psf(std::vector<uint8_t> buffer)
{
	unsigned long crc;
	unsigned long compressed_size;
	unsigned char *compressed_buffer;
	unsigned long uncompressed_size;
	std::vector<uint8_t> uncompressed_buffer;

	struct PSF_HEADER
	{
		unsigned char id[4];
		uint32_t reserved_size;
		uint32_t exe_size;
		uint32_t exe_crc;
	};

	struct PSF_HEADER *psf_header = reinterpret_cast<struct PSF_HEADER *>(&buffer[0]);

	if (buffer.size() >= sizeof(struct PSF_HEADER) &&
		memcmp(reinterpret_cast<void *>(&buffer[0]), "PSF", 3) == 0)
	{
		psxexe_conv32(&psf_header->reserved_size);
		psxexe_conv32(&psf_header->exe_size);
		psxexe_conv32(&psf_header->exe_crc);

		logerror("psx_exe_load: reserved_size %08x\n", psf_header->reserved_size);
		logerror("psx_exe_load: exe_size      %08x\n", psf_header->exe_size);
		logerror("psx_exe_load: exe_crc       %08x\n", psf_header->exe_crc);

		compressed_size = psf_header->exe_size;
		compressed_buffer = reinterpret_cast<Bytef *>(&buffer[sizeof(struct PSF_HEADER) + psf_header->reserved_size]);

		crc = crc32(crc32(0L, Z_NULL, 0), compressed_buffer, compressed_size);
		if (crc != psf_header->exe_crc)
		{
			logerror("psx_exe_load: psf invalid crc\n");
			return 0;
		}

		uncompressed_size = 0x200000;
		uncompressed_buffer.resize(uncompressed_size);

		if (uncompress(reinterpret_cast<Bytef *>(&uncompressed_buffer[0]), &uncompressed_size, compressed_buffer, compressed_size) != Z_OK)
		{
			logerror("psx_exe_load: psf uncompress failed\n");
		}
		else
		{
			uncompressed_buffer.resize(uncompressed_size);

			if (!load_psxexe(uncompressed_buffer))
			{
				logerror("psx_exe_load: psf load failed\n");
			}
			else
			{
				return 1;
			}
		}
	}

	return 0;
}

uint16_t psx1_state::parallel_r(offs_t offset)
{
	if (m_parallel->hascard())
	{
		uint16_t dat = m_parallel->exp_r(offset);
		return dat;
	}

	// all this could probably be a fake parallel device instead?
	const uint16_t bootloader[] =
	{
		0x00b0, 0x1f00, 0x694c, 0x6563, 0x736e, 0x6465, 0x6220, 0x2079,
		0x6f53, 0x796e, 0x4320, 0x6d6f, 0x7570, 0x6574, 0x2072, 0x6e45,
		0x6574, 0x7472, 0x6961, 0x6d6e, 0x6e65, 0x2074, 0x6e49, 0x2e63,
		0x1f00, 0x3c08, 0x8000, 0x3c09, 0x000c, 0x3529, 0x00fc, 0x8d0a,
		0xfffc, 0x2529, 0x0044, 0xad2a, 0xfffc, 0x0520, 0xfffc, 0x2508,
		0x8003, 0x3c08, 0x1800, 0x4088, 0xffff, 0x3c08, 0xffff, 0x3508,
		0x5800, 0x4088, 0xa180, 0x3c08, 0x0008, 0x03e0, 0x3800, 0x4088,
		0x1f00, 0x3c1a, 0x0100, 0x275a, 0x0008, 0x0340, 0x0010, 0x4200,
		0x3800, 0x4080, 0x1800, 0x4080, 0x5800, 0x4080, 0x1f00, 0x3c08,
		0x0000, 0xad00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
	};

	if (m_exe_buffer.size() != 0 && offset >= 0x40 && offset <= 0x8f)
	{
		return bootloader[offset - 0x40];
	}

	return 0;
}

void psx1_state::parallel_w(offs_t offset, uint16_t data)
{
	if (m_parallel->hascard())
	{
		m_parallel->exp_w(offset, data);
		return;
	}

	if (m_exe_buffer.size() != 0)
	{
		if (load_psxexe(m_exe_buffer) ||
			load_cpe(m_exe_buffer) ||
			load_psf(m_exe_buffer))
		{
			// stop the cpu from advancing to the next instruction, otherwise it would skip the exe's first instruction
			m_maincpu->set_state_int(PSXCPU_DELAYR, PSXCPU_DELAYR_PC);
			m_maincpu->set_state_int(PSXCPU_DELAYV, m_maincpu->state_int(PSXCPU_PC));

			// workround to fix controller in amidog tests that do not initialise the sio registers
			m_maincpu->space(AS_PROGRAM).write_word(0x1f80104e, 0x0088);
			m_maincpu->space(AS_PROGRAM).write_word(0x1f801048, 0x000d);

			// stop on the first instruction of the exe if the debugger is active
			machine().debug_break();
		}
		else
		{
			logerror("psx_exe_load: invalid exe\n");
			m_maincpu->reset();
		}

		m_exe_buffer.resize(0);
	}
}

QUICKLOAD_LOAD_MEMBER(psx1_state::quickload_exe)
{
	m_exe_buffer.resize(image.length());

	if (image.fread(reinterpret_cast<void *>(&m_exe_buffer[0]), image.length()) != image.length())
	{
		m_exe_buffer.resize(0);
		return std::make_pair(image_error::UNSPECIFIED, std::string());
	}

	return std::make_pair(std::error_condition(), std::string());
}

void psx1_state::cd_dma_read( uint32_t *p_n_psxram, uint32_t n_address, int32_t n_size )
{
	uint8_t *psxram = (uint8_t *) p_n_psxram;
	m_psxcd->start_dma(psxram + n_address, n_size*4);
}

void psx1_state::cd_dma_write( uint32_t *p_n_psxram, uint32_t n_address, int32_t n_size )
{
	printf("cd_dma_write?!: addr %x, size %x\n", n_address, n_size);
}

void psx1_state::psx_map(address_map &map)
{
	map(0x1f000000, 0x1f07ffff).rw(FUNC(psx1_state::parallel_r), FUNC(psx1_state::parallel_w));
}

void psx1_state::subcpu_map(address_map &map)
{
	map(0x0000, 0xffff).rom();
}

void psx1_state::psx_base(machine_config &config)
{
	m_maincpu->set_addrmap(AS_PROGRAM, &psx1_state::psx_map);
	m_maincpu->cd_read().set(m_psxcd, FUNC(psxcd_device::read));
	m_maincpu->cd_write().set(m_psxcd, FUNC(psxcd_device::write));
	m_maincpu->subdevice<ram_device>("ram")->set_default_size("2M");

	psxcontrollerports_device &controllers(PSXCONTROLLERPORTS(config, "controllers", 0));
	controllers.rxd().set("maincpu:sio0", FUNC(psxsio0_device::write_rxd));
	controllers.dsr().set("maincpu:sio0", FUNC(psxsio0_device::write_dsr));
	PSX_CONTROLLER_PORT(config, "port1", psx_controllers, "digital_pad");
	PSX_CONTROLLER_PORT(config, "port2", psx_controllers, "digital_pad");

	auto &sio0(*m_maincpu->subdevice<psxsio0_device>("sio0"));
	sio0.dtr_handler().set("controllers", FUNC(psxcontrollerports_device::write_dtr));
	sio0.sck_handler().set("controllers", FUNC(psxcontrollerports_device::write_sck));
	sio0.txd_handler().set("controllers", FUNC(psxcontrollerports_device::write_txd));

	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	/* sound hardware */
	SPEAKER(config, "speaker", 2).front();
	spu_device &spu(SPU(config, "spu", XTAL(67'737'600)/2, m_maincpu.target()));
	spu.add_route(0, "speaker", 1.00, 0);
	spu.add_route(1, "speaker", 1.00, 1);

	QUICKLOAD(config, "quickload", "cpe,exe,psf,psx").set_load_callback(FUNC(psx1_state::quickload_exe));

	PSX_PARALLEL_SLOT(config, "parallel", psx_parallel_devices, nullptr);

	PSXCD(config, m_psxcd, m_maincpu, "spu");
	m_psxcd->irq_handler().set("maincpu:irq", FUNC(psxirq_device::intin2));
	subdevice<psxdma_device>("maincpu:dma")->install_read_handler(3, psxdma_device::read_delegate(&psx1_state::cd_dma_read, this));
	subdevice<psxdma_device>("maincpu:dma")->install_write_handler(3, psxdma_device::write_delegate(&psx1_state::cd_dma_write, this));

	SOFTWARE_LIST(config, m_cd_softlist).set_original("psx");
}

void psx1_state::psj(machine_config &config)
{
	CXD8530CQ(config, m_maincpu, XTAL(67'737'600));

	/* TODO: visible area and refresh rate */
	CXD8561Q(config, "gpu", XTAL(53'693'175), 0x100000, m_maincpu.target()).set_screen("screen");

	psx_base(config);

	m_cd_softlist->set_filter("NTSC-J");
}

void psx1_state::psu(machine_config &config)
{
	psj(config);

	HD63705Z0(config, "subcpu", 4166667).set_addrmap(AS_PROGRAM, &psx1_state::subcpu_map); // FIXME: actually MC68HC05G6

	m_cd_softlist->set_filter("NTSC-U");
}

void psx1_state::pse(machine_config &config)
{
	CXD8530AQ(config, m_maincpu, XTAL(67'737'600));

	/* TODO: visible area and refresh rate */
	CXD8561Q(config, "gpu", XTAL(53'693'175), 0x100000, m_maincpu.target()).set_screen("screen");

	psx_base(config);

	m_cd_softlist->set_filter("PAL-E");
}

ROM_START( psj )
	ROM_REGION32_LE( 0x080000, "maincpu:rom", 0 )

	ROM_SYSTEM_BIOS( 0, "1.0j", "SCPH-1000/DTL-H1000" ) // 22091994
	ROMX_LOAD( "ps-10j.bin",    0x000000, 0x080000, CRC(3b601fc8) SHA1(343883a7b555646da8cee54aadd2795b6e7dd070), ROM_BIOS(0) )

	ROM_SYSTEM_BIOS( 1, "1.1j", "SCPH-3000/DTL-H1000H (Version 1.1 01/22/95)" ) // 22091994
	ROMX_LOAD( "ps-11j.bin",    0x000000, 0x080000, CRC(3539def6) SHA1(b06f4a861f74270be819aa2a07db8d0563a7cc4e), ROM_BIOS(1) )

	ROM_SYSTEM_BIOS( 2, "2.1j", "SCPH-3500 (Version 2.1 07/17/95 J)" ) // 22091994
	ROMX_LOAD( "ps-21j.bin",    0x000000, 0x080000, CRC(bc190209) SHA1(e38466a4ba8005fba7e9e3c7b9efeba7205bee3f), ROM_BIOS(2) )

	ROM_SYSTEM_BIOS( 3, "2.2j", "SCPH-5000/DTL-H1200/DTL-H3000 (Version 2.2 12/04/95 J)" ) // 04121995
/*  ROMX_LOAD( "ps-22j.bad",    0x000000, 0x080000, BAD_DUMP CRC(8c93a399) SHA1(e340db2696274dda5fdc25e434a914db71e8b02b), ROM_BIOS(3) ) */
	ROMX_LOAD( "ps-22j.bin",    0x000000, 0x080000, CRC(24fc7e17) SHA1(ffa7f9a7fb19d773a0c3985a541c8e5623d2c30d), ROM_BIOS(3) )

	ROM_SYSTEM_BIOS( 4, "2.2d", "DTL-H1100 (Version 2.2 03/06/96 D)" ) // 04121995
	ROMX_LOAD( "ps-22d.bin",    0x000000, 0x080000, CRC(decb22f5) SHA1(73107d468fc7cb1d2c5b18b269715dd889ecef06), ROM_BIOS(4) )

	ROM_SYSTEM_BIOS( 5, "3.0j", "SCPH-5500 (Version 3.0 09/09/96 J)" ) // 04121995
	ROMX_LOAD( "ps-30j.bin",    0x000000, 0x080000, CRC(ff3eeb8c) SHA1(b05def971d8ec59f346f2d9ac21fb742e3eb6917), ROM_BIOS(5) )

	ROM_SYSTEM_BIOS( 6, "4.0j", "SCPH-7000/SCPH-7500/SCPH-9000 (Version 4.0 08/18/97 J)" ) // 29051997
	ROMX_LOAD( "ps-40j.bin",    0x000000, 0x080000, CRC(ec541cd0) SHA1(77b10118d21ac7ffa9b35f9c4fd814da240eb3e9), ROM_BIOS(6) )

	ROM_SYSTEM_BIOS( 7, "4.1a", "SCPH-7000W (Version 4.1 11/14/97 A)" ) // 04121995
	ROMX_LOAD( "ps-41a,w.bin", 0x000000, 0x080000, CRC(b7c43dad) SHA1(1b0dbdb23da9dc0776aac58d0755dc80fea20975), ROM_BIOS(7) )

	ROM_SYSTEM_BIOS( 8, "4.3j", "SCPH-100 (Version 4.3 03/11/00 J)" ) // 04121995
	ROMX_LOAD( "psone-43j.bin", 0x000000, 0x080000, CRC(f2af798b) SHA1(339a48f4fcf63e10b5b867b8c93cfd40945faf6c), ROM_BIOS(8) )
ROM_END

ROM_START( psu )
	ROM_REGION32_LE( 0x080000, "maincpu:rom", 0 )

	ROM_SYSTEM_BIOS( 0, "2.0a", "DTL-H1001 (Version 2.0 05/07/95 A)" ) // 22091994
	ROMX_LOAD( "ps-20a.bin",    0x000000, 0x080000, CRC(55847d8c) SHA1(649895efd79d14790eabb362e94eb0622093dfb9), ROM_BIOS(0) )

	ROM_SYSTEM_BIOS( 1, "2.1a", "DTL-H1101 (Version 2.1 07/17/95 A)" ) // 22091994
	ROMX_LOAD( "ps-21a.bin",    0x000000, 0x080000, CRC(aff00f2f) SHA1(ca7af30b50d9756cbd764640126c454cff658479), ROM_BIOS(1) )

	ROM_SYSTEM_BIOS( 2, "2.2a", "SCPH-1001/DTL-H1201/DTL-H3001 (Version 2.2 12/04/95 A)" ) // 04121995
	ROMX_LOAD( "ps-22a.bin",    0x000000, 0x080000, CRC(37157331) SHA1(10155d8d6e6e832d6ea66db9bc098321fb5e8ebf), ROM_BIOS(2) )

	ROM_SYSTEM_BIOS( 3, "3.0a", "SCPH-5501/SCPH-5503/SCPH-7003 (Version 3.0 11/18/96 A)" ) // 04121995
	ROMX_LOAD( "ps-30a.bin",    0x000000, 0x080000, CRC(8d8cb7e4) SHA1(0555c6fae8906f3f09baf5988f00e55f88e9f30b), ROM_BIOS(3) )

	ROM_SYSTEM_BIOS( 4, "4.1a", "SCPH-7001/SCPH-7501/SCPH-7503/SCPH-9001/SCPH-9003/SCPH-9903 (Version 4.1 12/16/97 A)" ) // 04121995
	ROMX_LOAD( "ps-41a.bin",    0x000000, 0x080000, CRC(502224b6) SHA1(14df4f6c1e367ce097c11deae21566b4fe5647a9), ROM_BIOS(4) )

	ROM_SYSTEM_BIOS( 5, "4.5a", "SCPH-101 (Version 4.5 05/25/00 A)" ) // 04121995
	ROMX_LOAD( "psone-45a.bin", 0x000000, 0x080000, CRC(171bdcec) SHA1(dcffe16bd90a723499ad46c641424981338d8378), ROM_BIOS(5) )

	ROM_REGION( 0x10000, "subcpu", 0 )
	ROM_LOAD( "ram.ic304",    0x000000, 0x000400, CRC(e9fefc8b) SHA1(7ab4a498216fb9a3fca9daf6ad21b4553126e74f) )
	ROM_FILL( 0x0400, 0x0c00, 0xff )
	ROM_LOAD( "scea.ic304",   0x001000, 0x004000, CRC(82729934) SHA1(7d5f52eb9df1243dcdab32cb763a9eb6a22706d7) )
	ROM_FILL( 0x5000, 0xae00, 0xff )
	ROM_LOAD( "test.ic304",   0x00fe00, 0x000200, CRC(3b2f8041) SHA1(d7127cb4a9b5efe9deffab3b72ab4451cb30675b) )
ROM_END

ROM_START( pse )
	ROM_REGION32_LE( 0x080000, "maincpu:rom", 0 )

	ROM_SYSTEM_BIOS( 0, "2.0e", "DTL-H1002/SCPH-1002 (Version 2.0 05/10/95 E)" ) // 22091994
	ROMX_LOAD( "ps-20e.bin",    0x000000, 0x080000, CRC(9bb87c4b) SHA1(20b98f3d80f11cbf5a7bfd0779b0e63760ecc62c), ROM_BIOS(0) )

	ROM_SYSTEM_BIOS( 1, "2.1e", "SCPH-1002/DTL-H1102 (Version 2.1 07/17/95 E)" ) // 22091994
	ROMX_LOAD( "ps-21e.bin",    0x000000, 0x080000, CRC(86c30531) SHA1(76cf6b1b2a7c571a6ad07f2bac0db6cd8f71e2cc), ROM_BIOS(1) )

	ROM_SYSTEM_BIOS( 2, "2.2e", "SCPH-1002/DTL-H1202/DTL-H3002 (Version 2.2 12/04/95 E)" ) // 04121995
	ROMX_LOAD( "ps-22e.bin",    0x000000, 0x080000, CRC(1e26792f) SHA1(b6a11579caef3875504fcf3831b8e3922746df2c), ROM_BIOS(2) )

	ROM_SYSTEM_BIOS( 3, "3.0e", "SCPH-5502/SCPH-5552 (Version 3.0 01/06/97 E)" ) // 04121995
/*  ROMX_LOAD( "ps-30e.bad",    0x000000, 0x080000, BAD_DUMP CRC(4d9e7c86) SHA1(f8de9325fc36fcfa4b29124d291c9251094f2e54), ROM_BIOS(3) ) */
	ROMX_LOAD( "ps-30e.bin",    0x000000, 0x080000, CRC(d786f0b9) SHA1(f6bc2d1f5eb6593de7d089c425ac681d6fffd3f0), ROM_BIOS(3) )

	ROM_SYSTEM_BIOS( 4, "4.1e", "SCPH-7002/SCPH-7502/SCPH-9002 (Version 4.1 12/16/97 E)" ) // 04121995
	ROMX_LOAD( "ps-41e.bin",    0x000000, 0x080000, CRC(318178bf) SHA1(8d5de56a79954f29e9006929ba3fed9b6a418c1d), ROM_BIOS(4) )

	ROM_SYSTEM_BIOS( 5, "4.4e", "SCPH-102 (Version 4.4 03/24/00 E)" ) // 04121995
	ROMX_LOAD( "psone-44e.bin", 0x000000, 0x080000, CRC(0bad7ea9) SHA1(beb0ac693c0dc26daf5665b3314db81480fa5c7c), ROM_BIOS(5) )

	ROM_SYSTEM_BIOS( 6, "4.5e", "SCPH-102 (Version 4.5 05/25/00 E)" ) // 04121995
	ROMX_LOAD( "psone-45e.bin", 0x000000, 0x080000, CRC(76b880e5) SHA1(dbc7339e5d85827c095764fc077b41f78fd2ecae), ROM_BIOS(6) )
ROM_END

ROM_START( psa )
	ROM_REGION32_LE( 0x080000, "maincpu:rom", 0 )

	ROM_SYSTEM_BIOS( 0, "3.0a", "SCPH-5501/SCPH-5503/SCPH-7003 (Version 3.0 11/18/96 A)" ) // 04121995
	ROMX_LOAD( "ps-30a.bin",    0x000000, 0x080000, CRC(8d8cb7e4) SHA1(0555c6fae8906f3f09baf5988f00e55f88e9f30b), ROM_BIOS(0) )

	ROM_SYSTEM_BIOS( 1, "4.1a", "SCPH-7001/SCPH-7501/SCPH-7503/SCPH-9001/SCPH-9003/SCPH-9903 (Version 4.1 12/16/97 A)" ) // 04121995
	ROMX_LOAD( "ps-41a.bin",    0x000000, 0x080000, CRC(502224b6) SHA1(14df4f6c1e367ce097c11deae21566b4fe5647a9), ROM_BIOS(1) )
ROM_END

/*
The version number & release date is stored in ascii text at the end of every bios, except for scph1000.
There is also a BCD encoded date at offset 0x100, but this is set to 22091994 for versions prior to 2.2
and 04121995 for all versions from 2.2 ( except Version 4.0J which is 29051997 ).

Consoles not dumped:

DTL-H1001H
SCPH-5001
SCPH-5002 (this is not mentioned in SCPH-102B.pdf so it's likely it doesn't exist)
SCPH-5003
SCPH-103

Holes in version numbers:

Version 2.0 J
Version 4.1 J (SCPH7000W uses 4.1 A)
Version 4.2 J
Version 4.4 J
Version 4.5 J
Version 4.0 A
Version 4.2 A
Version 4.3 A
Version 4.4 A
Version 4.0 E
Version 4.2 E
Version 4.3 E

*/

} // Anonymous namespace


//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY                            FULLNAME                            FLAGS
CONS( 1994, psj,  0,      0,      psj,     0,     psx1_state, empty_init, "Sony Computer Entertainment Inc", "Sony PlayStation (Japan)",         MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
CONS( 1995, pse,  psj,    0,      pse,     0,     psx1_state, empty_init, "Sony Computer Entertainment Inc", "Sony PlayStation (Europe)",        MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
CONS( 1995, psu,  psj,    0,      psu,     0,     psx1_state, empty_init, "Sony Computer Entertainment Inc", "Sony PlayStation (USA)",           MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
CONS( 1995, psa,  psj,    0,      psj,     0,     psx1_state, empty_init, "Sony Computer Entertainment Inc", "Sony PlayStation (Asia-Pacific)",  MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )



pt68k4.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    Peripheral Technology PT68K2/PT68K4 family

    2011-01-03 Skeleton driver.
    2013-09-30 Connected to a terminal
    2014-01-03 Connect real DUARTs, FDC, and TimeKeeper.  Settings now save properly, floppies can be read.
    2014-01-19 ISA bus and compatible cards, PC keyboard support, speaker support
    2014-09-20 Add PT68K2, add save states, we have a working SK*DOS disk!

This has the appearance of a PC, including pc power supply, slots, etc
on a conventional pc-like motherboard and case.

Some pics: http://www.wormfood.net/old_computers/

Source code and manuals for the HUMBUG BIOS and SK*DOS are at:
http://www.users.cloud9.net/~stark/sources.html

Usage:
    Start up and press Enter as prompted.  Type he to see a command list, or fd to boot from the
    first floppy drive.

    The stock NVRAM configures PT68k2 for 2 DSDD 5.25" drives, and PT68k4 for 2 DSHD 5.25" drives.

Chips:
    68230 Parallel Interface/Timer @ FE0081
    68681 DUART/Timer (x2) @ FE0001 and FE0041
    WD37C65 FDC (PC FDC compatible, even mapped as an ISA device)
    MK48T02 TimeKeeper @ odd bytes from FF0001 to FF0FFF.  even bytes in that range are a standard SRAM chip which is not backed up.
    Keyboard at FE01C1 (status/IRQ clear)/FE01C3 (AT scan codes)
    WD1002 HDD controller @ FE0141-FE014F.  "Monk" BIOS also supports an 8-bit ISA IDE card.

Video: ISA MDA or CGA/EGA/VGA-style boards
    ISA memory is C00001-DFFFFF odd bytes only.  So the MDA B0000 framebuffer becames (B0000*2) + C00001 = D60001.
    ISA I/O is at FA0001-FBFFFF odd bytes only, and the mapping is similar.

    HUMBUG BIOS tests MDA and CGA VRAM to determine existence, falls back to serial console if neither exists.  If both exist, MDA is used.
    VRAM is every other byte for ISA cards.  (Only 8 bit cards are supported).

    OP3 on DUART1 drives a speaker.
    IP2 on DUART1 signals if a new keyboard scan code is available.

IRQs:
    2: 68230 PIT
    4: DUART2
    5: DUART1
    6: PC FDC IRQ

TODO: 68230 device
      This system and SK*DOS don't like our ISA WDXT-GEN emulation so HDD installs are not currently possible.

****************************************************************************/

#include "emu.h"

#include "cpu/m68000/m68000.h"
#include "machine/mc68681.h"
#include "machine/timekpr.h"
#include "machine/wd_fdc.h"
#include "sound/spkrdev.h"

#include "bus/isa/cga.h"
#include "bus/isa/ega.h"
#include "bus/isa/fdc.h"
#include "bus/isa/isa.h"
#include "bus/isa/isa_cards.h"
#include "bus/isa/lpt.h"
#include "bus/isa/mda.h"
#include "bus/isa/vga.h"
#include "bus/isa/wdxt_gen.h"
#include "bus/isa/xtide.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"

#include "softlist.h"
#include "speaker.h"


namespace {

#define M68K_TAG "maincpu"
#define DUART1_TAG  "duart1"
#define DUART2_TAG  "duart2"
#define TIMEKEEPER_TAG  "timekpr"
#define ISABUS_TAG "isa"
#define SPEAKER_TAG "speaker"
#define WDFDC_TAG   "wdfdc"

class pt68k4_state : public driver_device
{
public:
	pt68k4_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_p_base(*this, "rambase")
		, m_maincpu(*this, M68K_TAG)
		, m_duart1(*this, DUART1_TAG)
		, m_duart2(*this, DUART2_TAG)
		, m_isa(*this, ISABUS_TAG)
		, m_speaker(*this, SPEAKER_TAG)
		, m_wdfdc(*this, WDFDC_TAG)
		, m_floppy_connector(*this, WDFDC_TAG":%u", 0U)
	{ }

	void pt68k2(machine_config &config);
	void pt68k4(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	uint8_t hiram_r(offs_t offset);
	void hiram_w(offs_t offset, uint8_t data);
	uint8_t keyboard_r(offs_t offset);
	void keyboard_w(uint8_t data);

	uint8_t pia_stub_r();
	void duart1_out(uint8_t data);

	void fdc_select_w(uint8_t data);

	void duart1_irq(int state);
	[[maybe_unused]] void duart2_irq(int state);

	void irq5_w(int state);

	void keyboard_clock_w(int state);
	void keyboard_data_w(int state);

	void pt68k2_mem(address_map &map) ATTR_COLD;
	void pt68k4_mem(address_map &map) ATTR_COLD;

	required_shared_ptr<uint16_t> m_p_base;
	required_device<cpu_device> m_maincpu;
	required_device<mc68681_device> m_duart1;
	required_device<mc68681_device> m_duart2;
	required_device<isa8_device> m_isa;
	required_device<speaker_sound_device> m_speaker;
	optional_device<wd1772_device> m_wdfdc;
	optional_device_array<floppy_connector, 2> m_floppy_connector;

	void irq5_update();

	uint8_t m_hiram[0x800]{};

	bool m_kclk = false;
	uint8_t m_kdata = 0;
	uint8_t m_scancode = 0;
	uint8_t m_kbdflag = 0;
	int m_kbit = 0;
	int m_lastdrive = 0;
	bool m_irq5_duart1 = false, m_irq5_isa = false;
};

static void pt68k_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

// XT keyboard interface - done in TTL instead of an 804x
void pt68k4_state::keyboard_clock_w(int state)
{
//  printf("KCLK: %d\n", state ? 1 : 0);

	// rising edge?
	if ((state == ASSERT_LINE) && (!m_kclk))
	{
		if (m_kbit >= 1 && m_kbit <= 8)
		{
			m_scancode >>= 1;
			m_scancode |= m_kdata;
		}

		// stop bit?
		if (m_kbit == 9)
		{
//          printf("scancode %02x\n", m_scancode);
			m_kbit = 0;
			m_kbdflag = 0x80;
			m_duart1->ip2_w(CLEAR_LINE);
		}
		else
		{
			m_kbit++;
		}
	}

	m_kclk = (state == ASSERT_LINE) ? true : false;
}

void pt68k4_state::keyboard_data_w(int state)
{
//  printf("KDATA: %d\n", state ? 1 : 0);
	m_kdata = (state == ASSERT_LINE) ? 0x80 : 0x00;
}

void pt68k4_state::duart1_out(uint8_t data)
{
	m_speaker->level_w((data >> 3) & 1);
}

uint8_t pt68k4_state::pia_stub_r()
{
	return 0;
}

void pt68k4_state::fdc_select_w(uint8_t data)
{
	floppy_image_device *floppy = m_floppy_connector[0] ? m_floppy_connector[0]->get_device() : nullptr;
	floppy_image_device *floppy2 = m_floppy_connector[1] ? m_floppy_connector[1]->get_device() : nullptr;
	int drive = data & 3;

	if (drive != m_lastdrive)
	{
		switch (drive)
		{
			case 0:
				m_wdfdc->set_floppy(floppy);
				break;

			case 1:
				m_wdfdc->set_floppy(floppy2);
				break;

			default:
				m_wdfdc->set_floppy(nullptr);
				break;
		}

		m_lastdrive = drive;
	}

	switch (drive)
	{
		case 0:
			if (floppy)
				floppy->ss_w((data & 0x40) ? 1 : 0);
			break;

		case 1:
			if (floppy2)
				floppy2->ss_w((data & 0x40) ? 1 : 0);
			break;

		default:
			break;
	}
}

void pt68k4_state::pt68k2_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x0fffff).ram().share("rambase"); // 1MB RAM
	map(0xf80000, 0xf8ffff).rom().region("roms", 0);
	map(0xc00000, 0xdfffff).rw(m_isa, FUNC(isa8_device::mem_r), FUNC(isa8_device::mem_w)).umask16(0x00ff);
	map(0xfa0000, 0xfbffff).rw(m_isa, FUNC(isa8_device::io_r), FUNC(isa8_device::io_w)).umask16(0x00ff);
	map(0xfe0000, 0xfe001f).rw(m_duart1, FUNC(mc68681_device::read), FUNC(mc68681_device::write)).umask16(0x00ff);
	map(0xfe0040, 0xfe005f).rw(m_duart2, FUNC(mc68681_device::read), FUNC(mc68681_device::write)).umask16(0x00ff);
	map(0xfe0080, 0xfe00bf).r(FUNC(pt68k4_state::pia_stub_r)).umask16(0x00ff);
	map(0xfe00c0, 0xfe00ff).w(FUNC(pt68k4_state::fdc_select_w)).umask16(0x00ff);
	map(0xfe0100, 0xfe013f).rw(m_wdfdc, FUNC(wd1772_device::read), FUNC(wd1772_device::write)).umask16(0x00ff);
	map(0xfe01c0, 0xfe01c3).rw(FUNC(pt68k4_state::keyboard_r), FUNC(pt68k4_state::keyboard_w)).umask16(0x00ff);
	map(0xff0000, 0xff0fff).rw(FUNC(pt68k4_state::hiram_r), FUNC(pt68k4_state::hiram_w)).umask16(0xff00);
	map(0xff0000, 0xff0fff).rw(TIMEKEEPER_TAG, FUNC(timekeeper_device::read), FUNC(timekeeper_device::write)).umask16(0x00ff);
}

void pt68k4_state::pt68k4_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x0fffff).ram().share("rambase"); // 1MB RAM (OS9 needs more)
	map(0xf80000, 0xf8ffff).rom().region("roms", 0);
	map(0xc00000, 0xdfffff).rw(m_isa, FUNC(isa8_device::mem_r), FUNC(isa8_device::mem_w)).umask16(0x00ff);
	map(0xfa0000, 0xfbffff).rw(m_isa, FUNC(isa8_device::io_r), FUNC(isa8_device::io_w)).umask16(0x00ff);
	map(0xfe0000, 0xfe001f).rw(m_duart1, FUNC(mc68681_device::read), FUNC(mc68681_device::write)).umask16(0x00ff);
	map(0xfe0040, 0xfe005f).rw(m_duart2, FUNC(mc68681_device::read), FUNC(mc68681_device::write)).umask16(0x00ff);
	map(0xfe01c0, 0xfe01c3).rw(FUNC(pt68k4_state::keyboard_r), FUNC(pt68k4_state::keyboard_w)).umask16(0x00ff);
	map(0xff0000, 0xff0fff).rw(FUNC(pt68k4_state::hiram_r), FUNC(pt68k4_state::hiram_w)).umask16(0xff00);
	map(0xff0000, 0xff0fff).rw(TIMEKEEPER_TAG, FUNC(timekeeper_device::read), FUNC(timekeeper_device::write)).umask16(0x00ff);
}

/* Input ports */
static INPUT_PORTS_START( pt68k4 )
INPUT_PORTS_END

/* built in keyboard: offset 0 reads 0x80 if key ready, 0 if not.  If key ready, offset 1 reads scancode.  Read or write to offs 0 clears key ready */
uint8_t pt68k4_state::keyboard_r(offs_t offset)
{
	if (offset == 0)
	{
		uint8_t rv = m_kbdflag;

		m_kbdflag = 0;
		m_duart1->ip2_w(ASSERT_LINE);

		return rv;
	}

	return m_scancode;
}

void pt68k4_state::keyboard_w(uint8_t data)
{
	m_kbdflag = 0;
	m_duart1->ip2_w(ASSERT_LINE);
}

uint8_t pt68k4_state::hiram_r(offs_t offset)
{
	return m_hiram[offset];
}

void pt68k4_state::hiram_w(offs_t offset, uint8_t data)
{
	m_hiram[offset] = data;
}

void pt68k4_state::machine_start()
{
	save_item(NAME(m_hiram));
	save_item(NAME(m_kclk));
	save_item(NAME(m_kdata));
	save_item(NAME(m_scancode));
	save_item(NAME(m_kbdflag));
	save_item(NAME(m_kbit));
	save_item(NAME(m_lastdrive));
	save_item(NAME(m_irq5_duart1));
	save_item(NAME(m_irq5_isa));

	m_irq5_isa = false;
}

void pt68k4_state::machine_reset()
{
	uint8_t* user1 = memregion("roms")->base();
	memcpy((uint8_t*)m_p_base.target(), user1, 8);

	m_kclk = true;
	m_kbit = 0;
	m_scancode = 0;
	m_kbdflag = 0;
	m_irq5_duart1 = CLEAR_LINE;
	m_irq5_isa = CLEAR_LINE;

	// set line to asserted (no key code ready)
	m_duart1->ip2_w(ASSERT_LINE);

	if (m_wdfdc)
	{
		floppy_image_device *floppy = m_floppy_connector[0] ? m_floppy_connector[0]->get_device() : nullptr;

		m_wdfdc->set_floppy(floppy);

		if (floppy)
			floppy->ss_w(0);

		m_lastdrive = 0;
	}
}

void pt68k4_state::irq5_update()
{
	if ((m_irq5_duart1) || (m_irq5_isa))
	{
		m_maincpu->set_input_line(M68K_IRQ_5, ASSERT_LINE);
	}
	else
	{
		m_maincpu->set_input_line(M68K_IRQ_5, CLEAR_LINE);
	}
}

void pt68k4_state::duart1_irq(int state)
{
	m_irq5_duart1 = state;
	irq5_update();
}

void pt68k4_state::irq5_w(int state)
{
	m_irq5_isa = state;
	irq5_update();
}

void pt68k4_state::duart2_irq(int state)
{
	m_maincpu->set_input_line(M68K_IRQ_4, state);
}

// these are cards supported by the HUMBUG and Monk BIOSes
void pt68k4_isa8_cards(device_slot_interface &device)
{
	device.option_add("mda", ISA8_MDA);
	device.option_add("cga", ISA8_CGA);
	device.option_add("ega", ISA8_EGA); // Monk only
	device.option_add("vga", ISA8_VGA); // Monk only
	device.option_add("fdc_at", ISA8_FDC_AT);
	device.option_add("wdxt_gen", ISA8_WDXT_GEN);
	device.option_add("lpt", ISA8_LPT);
	device.option_add("xtide", ISA8_XTIDE); // Monk only
}

void pt68k4_state::pt68k2(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, 16_MHz_XTAL / 2);    // 68k2 came in 8, 10, and 12 MHz versions
	m_maincpu->set_addrmap(AS_PROGRAM, &pt68k4_state::pt68k2_mem);

	MC68681(config, m_duart1, 3.6864_MHz_XTAL);
	m_duart1->irq_cb().set(FUNC(pt68k4_state::duart1_irq));
	m_duart1->outport_cb().set(FUNC(pt68k4_state::duart1_out));

	MC68681(config, m_duart2, 3.6864_MHz_XTAL);

	pc_kbdc_device &pc_kbdc(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_IBM_PC_XT_83));
	pc_kbdc.out_clock_cb().set(FUNC(pt68k4_state::keyboard_clock_w));
	pc_kbdc.out_data_cb().set(FUNC(pt68k4_state::keyboard_data_w));

	M48T02(config, TIMEKEEPER_TAG, 0);

	WD1772(config, m_wdfdc, 16_MHz_XTAL / 2);
	FLOPPY_CONNECTOR(config, m_floppy_connector[0], pt68k_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy_connector[1], pt68k_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);

	ISA8(config, m_isa, 0);
	m_isa->set_custom_spaces();
	m_isa->irq5_callback().set(FUNC(pt68k4_state::irq5_w));

	ISA8_SLOT(config, "isa1", 0, m_isa, pt68k4_isa8_cards, "cga", false); // FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa2", 0, m_isa, pt68k4_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa3", 0, m_isa, pt68k4_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa4", 0, m_isa, pt68k4_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa5", 0, m_isa, pt68k4_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa6", 0, m_isa, pt68k4_isa8_cards, nullptr, false),

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);

	SOFTWARE_LIST(config, "flop525_list").set_original("pt68k2");
}

void pt68k4_state::pt68k4(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, XTAL(16'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &pt68k4_state::pt68k4_mem);

	// add the DUARTS.  first one has the console on channel A at 19200.
	MC68681(config, m_duart1, XTAL(16'000'000) / 4);
	m_duart1->irq_cb().set(FUNC(pt68k4_state::duart1_irq));
	m_duart1->outport_cb().set(FUNC(pt68k4_state::duart1_out));

	MC68681(config, m_duart2, XTAL(16'000'000) / 4);

	pc_kbdc_device &pc_kbdc(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_IBM_PC_XT_83));
	pc_kbdc.out_clock_cb().set(FUNC(pt68k4_state::keyboard_clock_w));
	pc_kbdc.out_data_cb().set(FUNC(pt68k4_state::keyboard_data_w));

	M48T02(config, TIMEKEEPER_TAG, 0);

	ISA8(config, m_isa, 0);
	m_isa->set_custom_spaces();

	ISA8_SLOT(config, "isa1", 0, m_isa, pt68k4_isa8_cards, "fdc_at", false); // FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa2", 0, m_isa, pt68k4_isa8_cards, "cga", false);
	ISA8_SLOT(config, "isa3", 0, m_isa, pt68k4_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa4", 0, m_isa, pt68k4_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa5", 0, m_isa, pt68k4_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa6", 0, m_isa, pt68k4_isa8_cards, nullptr, false),
	ISA8_SLOT(config, "isa7", 0, m_isa, pt68k4_isa8_cards, nullptr, false),

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);

	SOFTWARE_LIST(config, "flop525_list").set_original("pt68k2");
}

/* ROM definition */
ROM_START( pt68k2 )
	ROM_REGION16_BE( 0x10000, "roms", 0 )
	ROM_LOAD16_BYTE( "hum_u20.bin",  0x000000, 0x008000, CRC(69db483a) SHA1(9dfea73e4d7deef7c66a27cca92eb7c9ff767215) )
	ROM_LOAD16_BYTE( "hum_u27.bin",  0x000001, 0x008000, CRC(54441b06) SHA1(0e2d63b1cd01f88f37fc4859c11c252c4fea220b) )

	ROM_REGION(0x800, TIMEKEEPER_TAG, 0)
	ROM_LOAD( "u21_ds1220.bin", 0x000000, 0x000800, CRC(7a6b75ce) SHA1(07663860aa6cc21aed04a568ff9c05bc75d62e4f) )
ROM_END

ROM_START( pt68k4 )
	ROM_REGION16_BE( 0x10000, "roms", 0 )
	ROM_SYSTEM_BIOS( 0, "humbug", "Humbug" )
	ROMX_LOAD( "humpta40.bin", 0x0000, 0x8000, CRC(af67ff64) SHA1(da9fa31338c6847bb0e66118679b1ec01f6dc30b), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD( "humpta41.bin", 0x0001, 0x8000, CRC(a8b16e27) SHA1(218802f6e20d14cff736bb7423f06ce2f66e074c), ROM_SKIP(1) | ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "monk", "Monk" )
	ROMX_LOAD( "monk_0.bin", 0x0000, 0x8000, CRC(420d6a4b) SHA1(fca8c53c9c3c8ebd09370499cf34f4cc75ed9463), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD( "monk_1.bin", 0x0001, 0x8000, CRC(fc495e82) SHA1(f7b720d87db4d72a23e6c42d2cdd03216db04b60), ROM_SKIP(1) | ROM_BIOS(1) )

	ROM_REGION(0x800, TIMEKEEPER_TAG, 0)
	ROM_LOAD( "u21_ds1220_k4.bin", 0x000000, 0x000800, CRC(753472e6) SHA1(58dc8bcc86191e4a4429fe6a9b4fdd7788abb0cd) )

	ROM_REGION( 0x0900, "proms", 0 )
	ROM_LOAD_OPTIONAL( "20l8.u71",    0x0000, 0x000149, CRC(77365121) SHA1(5ecf490ead119966a5c097d90740acde60462ab0) )
	ROM_LOAD_OPTIONAL( "16l8.u53",    0x0200, 0x000109, CRC(cb6a9984) SHA1(45b9b14e7b45cda6f0edfcbb9895b6a14eacb852) )
	ROM_LOAD_OPTIONAL( "22v10.u40",   0x0400, 0x0002e1, CRC(24df92e4) SHA1(c183113956bb0db132b6f37b239ca0bb7fac2d82) )
	ROM_LOAD_OPTIONAL( "16l8.u11",    0x0700, 0x000109, CRC(397a1363) SHA1(aca2a02e1bf1f7cdb9b0ca24ebecb0b01ae472e8) )
ROM_END

} // Anonymous namespace


/* Driver */
//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY                  FULLNAME  FLAGS
COMP( 1988, pt68k2, 0,      0,      pt68k2,  pt68k4, pt68k4_state, empty_init, "Peripheral Technology", "PT68K2", MACHINE_SUPPORTS_SAVE )
COMP( 1990, pt68k4, 0,      0,      pt68k4,  pt68k4, pt68k4_state, empty_init, "Peripheral Technology", "PT68K4", MACHINE_SUPPORTS_SAVE )



ptcsol.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

    Processor Technology Corp. SOL-20

    07/2009 Skeleton driver.

    Info from: http://www.sol20.org/

    The Sol Terminal Computer was the first complete S-100 microcomputer
    with a built-in keyboard and video output. However, its internal
    architecture resembles a video display terminal more than anything else.

    Note that the SOLOS dump comes from the Solace emu. Confirmed as ok.

    The roms DPMON and CONSOL are widely available on the net as ENT files,
    which can be loaded into memory with the Paste option, then exported to
    binary files with the debugger. They are working, however the ENT files
    do not indicate the values of unused rom space. Therefore they are marked
    as BAD_DUMP.

    Note that the CONSOL rom is basically a dumb terminal program and doesn't
    do anything useful unless the MODE key (whatever that is) is pressed.

    CUTER is a relocatable cassette-based alternative to SOLOS. According
    to the manual, it should work if the sense switches are set to on. But,
    it continuously reads port 00 and does nothing.

    Need original dumps of all roms.

    The character roms are built by a script from the Solace source, then the
    characters moved back to the original 7x9 matrix positions, as shown in
    the manual. Although they look exactly the same, I've marked them as
    BAD_DUMP until properly verified.

    Other roms needed:
      - Dump of U18 in the keyboard

    Hardware variants (ever shipped?):
      - SOL-10, i.e. a stripped down version without expansion backplane and
        numeric keypad
      - SOL-PC, i.e. the single board version, basically it consisted of the
        SOL-20 board only

    Similarity to Exidy Sorcerer:
      - Some hardware is the same design (parallel ports, serial ports, 2
        tape players, the cassette circuits)
      - The cassette format is totally identical, in fact tapes from one
        machine can be loaded on the other natively. They won't run of course.
      - Sorcerer monitor commands are almost identical to the SOLOS ones.
        example: to change tape speed to 300 baud:
        SE T=1 (sorcerer)  SE TA 1 (SOLOS)
        Most other commands are identical. The EN command (enter bytes into
        memory) is rather primitive in SOLOS, better on Sorcerer.
      - Sol20 has "personality modules" where you could plug a new OS into
        a special socket on the motherboard. Sorcerer improved upon this by
        being the first computer to use cartridges.
      - Some circuits are completely different... the video and keyboard are
        notable examples, while the addresses of ram and bios on the basic
        machines is another major difference.

    ToDo:
      - connect up half/full duplex dipswitch function
      - connect up the remaining functions of port FE
      - keyboard (using terminal keyboard for now)
      - optional floppy disk support and CP/M
      - parallel i/o and handshake lines
      - connect serial i/o to something
      - support for loading the various file formats

    File Formats:
      - Most files are simple ascii which can be loaded via the Paste handler.
        These are ASC, ENT, BAS, ROM, BS5 and ECB. Most files require that the
        correct version of BASIC be loaded first. Paste works, but it is very
        very slow. Perhaps we need something faster such as what Solace has.
      - SVT (Solace Virtual Tape) files are a representation of a cassette,
        usually holding about 4 games, just like a multifile tape. This format
        is partially supported.
      - HEX files appear to be the standard Intel format, and can be loaded
        by Solace.
      - The remaining formats (OPN, PL, PRN, SMU, SOL, ASM and LIB) appear
        at first glance to be more specialised, and probably not worth being
        supported.

    System Setup (to play games etc).
      - In the Dipswitches (not the Configuration), turn cursor flashing OFF.
      - Loading via wav files works, so load a tape image in the file manager
      - In UPPER CASE, enter XE press enter, cassette will load.
      - At the end, the program will start by itself.
      - When it says use 2,4,6,8 keys, you can use the keyboard arrow keys.

    Monitor Commands:
      - TE - return to terminal mode
      - DU - dump memory
      - EN - modify memory
      - EX - Go (execute)
      - CU - Insert or remove a custom command
      - SE - Set parameters (eg tape speed)
      - SA - Save
      - GE - Load
      - XE - Load and run
      - CA - List the files on a tape

    How to use the music system:
    1. Choose the "music" item from the software list
    2. Type XEQ press Enter
    3. When the program starts, press R, you are back in the monitor
    4. Type GET press Enter
    5. When this finishes loading, type EX 0 press Enter, there's no response
    6. Press F, wait for numbers to appear
    7. Press S, wait for numbers to appear
    8. Press P, music will be heard (it isn't very loud)

****************************************************************************/

#include "emu.h"

#include "cpu/i8085/i8085.h"
#include "bus/rs232/rs232.h"
//#include "bus/s100/s100.h"
#include "imagedev/cassette.h"
#include "machine/ay31015.h"
#include "machine/clock.h"
#include "machine/keyboard.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/sol_cas.h"


namespace {

class sol20_state : public driver_device
{
public:
	sol20_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_screen(*this, "screen")
		, m_cass1(*this, "cassette")
		, m_cass2(*this, "cassette2")
		, m_uart(*this, "uart")
		, m_uart_s(*this, "uart_s")
		, m_uart_clock(*this, "uart_clock")
		, m_uart_s_clock(*this, "uart_s_clock")
		, m_rs232(*this, "rs232")
		, m_vram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
		, m_iop_arrows(*this, "ARROWS")
		, m_iop_config(*this, "CONFIG")
		, m_iop_s1(*this, "S1")
		, m_iop_s2(*this, "S2")
		, m_iop_s3(*this, "S3")
		, m_iop_s4(*this, "S4")
	{ }

	void sol20(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	struct cass_data_t
	{
		struct
		{
			int length = 0;     /* time cassette level is at input.level */
			int level = 0;      /* cassette level */
			int bit = 0;        /* bit being read */
		} input;
		struct
		{
			int length = 0;     /* time cassette level is at output.level */
			int level = 0;      /* cassette level */
			int bit = 0;        /* bit to output */
		} output;
	};

	u8 sol20_f8_r();
	u8 sol20_fa_r();
	u8 sol20_fc_r();
	u8 sol20_fd_r();
	void sol20_f8_w(u8 data);
	void sol20_fa_w(u8 data);
	void sol20_fd_w(u8 data);
	void sol20_fe_w(u8 data);
	void kbd_put(u8 data);
	TIMER_CALLBACK_MEMBER(sol20_cassette_tc);
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	u8 m_sol20_fa = 0U;
	u8 m_sol20_fc = 0U;
	u8 m_sol20_fe = 0U;
	u8 m_framecnt = 0U;
	cass_data_t m_cass_data;
	emu_timer *m_cassette_timer = nullptr;
	cassette_image_device *cassette_device_image();
	memory_passthrough_handler m_rom_shadow_tap;
	required_device<i8080a_cpu_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_device<screen_device> m_screen;
	required_device<cassette_image_device> m_cass1;
	required_device<cassette_image_device> m_cass2;
	required_device<ay31015_device> m_uart;
	required_device<ay31015_device> m_uart_s;
	required_device<clock_device> m_uart_clock;
	required_device<clock_device> m_uart_s_clock;
	required_device<rs232_port_device> m_rs232;
	required_shared_ptr<u8> m_vram;
	required_region_ptr<u8> m_p_chargen;
	required_ioport m_iop_arrows;
	required_ioport m_iop_config;
	required_ioport m_iop_s1;
	required_ioport m_iop_s2;
	required_ioport m_iop_s3;
	required_ioport m_iop_s4;
};


/* timer to read cassette waveforms */


cassette_image_device *sol20_state::cassette_device_image()
{
	if (m_sol20_fa & 0x40)
		return m_cass2;
	else
		return m_cass1;
}


// identical to sorcerer
TIMER_CALLBACK_MEMBER(sol20_state::sol20_cassette_tc)
{
	u8 cass_ws = 0;
	switch (m_sol20_fa & 0x20)
	{
		case 0x20:              /* Cassette 300 baud */

			/* loading a tape - this is basically the same as the super80.
			               We convert the 1200/2400 Hz signal to a 0 or 1, and send it to the uart. */

			m_cass_data.input.length++;

			cass_ws = ((cassette_device_image())->input() > +0.02) ? 1 : 0;

			if (cass_ws != m_cass_data.input.level)
			{
				m_cass_data.input.level = cass_ws;
				m_cass_data.input.bit = ((m_cass_data.input.length < 0x6) || (m_cass_data.input.length > 0x20)) ? 1 : 0;
				m_cass_data.input.length = 0;
				m_uart->write_si(m_cass_data.input.bit);
			}

			/* saving a tape - convert the serial stream from the uart, into 1200 and 2400 Hz frequencies.
			               Synchronisation of the frequency pulses to the uart is extremely important. */

			m_cass_data.output.length++;
			if (!(m_cass_data.output.length & 0x1f))
			{
				cass_ws = m_uart->so_r();
				if (cass_ws != m_cass_data.output.bit)
				{
					m_cass_data.output.bit = cass_ws;
					m_cass_data.output.length = 0;
				}
			}

			if (!(m_cass_data.output.length & 3))
			{
				if (!((m_cass_data.output.bit == 0) && (m_cass_data.output.length & 4)))
				{
					m_cass_data.output.level ^= 1;          // toggle output this, except on 2nd half of low bit
					cassette_device_image()->output(m_cass_data.output.level ? -1.0 : +1.0);
				}
			}
			return;

		case 0x00:          /* Cassette 1200 baud */
			/* loading a tape */
			m_cass_data.input.length++;

			cass_ws = ((cassette_device_image())->input() > +0.02) ? 1 : 0;

			if (cass_ws != m_cass_data.input.level || m_cass_data.input.length == 10)
			{
				m_cass_data.input.bit = ((m_cass_data.input.length < 10) || (m_cass_data.input.length > 0x20)) ? 1 : 0;
				if ( cass_ws != m_cass_data.input.level )
				{
					m_cass_data.input.length = 0;
					m_cass_data.input.level = cass_ws;
				}
				m_uart->write_si(m_cass_data.input.bit);
			}

			/* saving a tape - convert the serial stream from the uart, into 600 and 1200 Hz frequencies. */

			m_cass_data.output.length++;
			if (!(m_cass_data.output.length & 7))
			{
				cass_ws = m_uart->so_r();
				if (cass_ws != m_cass_data.output.bit)
				{
					m_cass_data.output.bit = cass_ws;
					m_cass_data.output.length = 0;
				}
			}

			if (!(m_cass_data.output.length & 7))
			{
				if (!((m_cass_data.output.bit == 0) && (m_cass_data.output.length & 8)))
				{
					m_cass_data.output.level ^= 1;          // toggle output this, except on 2nd half of low bit
					cassette_device_image()->output(m_cass_data.output.level ? -1.0 : +1.0);
				}
			}
			return;
	}
}

u8 sol20_state::sol20_f8_r()
{
	// d7 - TBMT; d6 - DAV; d5 - CTS; d4 - OE; d3 - PE; d2 - FE; d1 - DSR; d0 - CD
	u8 data = 0;

	m_uart_s->write_swe(0);
	data |= m_uart_s->tbmt_r() ? 0x80 : 0;
	data |= m_uart_s->dav_r( ) ? 0x40 : 0;
	data |= m_rs232->cts_r(  ) ? 0x20 : 0;
	data |= m_uart_s->or_r(  ) ? 0x10 : 0;
	data |= m_uart_s->pe_r(  ) ? 0x08 : 0;
	data |= m_uart_s->fe_r(  ) ? 0x04 : 0;
	data |= m_rs232->dsr_r(  ) ? 0x02 : 0;
	data |= m_rs232->dcd_r(  ) ? 0x01 : 0;
	m_uart_s->write_swe(1);

	return data;
}

u8 sol20_state::sol20_fa_r()
{
	/* set unused bits high */
	u8 data = 0x26;

	m_uart->write_swe(0);
	data |= m_uart->tbmt_r() ? 0x80 : 0;
	data |= m_uart->dav_r( ) ? 0x40 : 0;
	data |= m_uart->or_r(  ) ? 0x10 : 0;
	data |= m_uart->fe_r(  ) ? 0x08 : 0;
	m_uart->write_swe(1);

	bool arrowkey = m_iop_arrows->read() ? 0 : 1;
	bool keydown = m_sol20_fa & 1;

	return data | (arrowkey & keydown);
}

u8 sol20_state::sol20_fc_r()
{
	u8 data = m_iop_arrows->read();
	if (BIT(data, 0)) return 0x32;
	if (BIT(data, 1)) return 0x34;
	if (BIT(data, 2)) return 0x36;
	if (BIT(data, 3)) return 0x38;

	m_sol20_fa |= 1;
	return m_sol20_fc;
}

u8 sol20_state::sol20_fd_r()
{
// Return a byte from parallel interface
	return 0;
}

void sol20_state::sol20_f8_w(u8 data)
{
// The only function seems to be to send RTS from bit 4
	m_rs232->write_rts(BIT(data, 4));
}

void sol20_state::sol20_fa_w(u8 data)
{
	m_sol20_fa &= 1;
	m_sol20_fa |= (data & 0xf0);

	/* cassette 1 motor */
	m_cass1->change_state(
		(BIT(data,7)) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);

	/* cassette 2 motor */
	m_cass2->change_state(
		(BIT(data,6)) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);

	if (data & 0xc0)
		m_cassette_timer->adjust(attotime::zero, 0, attotime::from_hz(19200));
	else
		m_cassette_timer->adjust(attotime::zero);

	// bit 5 baud rate */
	m_uart_clock->set_unscaled_clock(BIT(data, 5) ? 4800 : 19200);
}

void sol20_state::sol20_fd_w(u8 data)
{
// Output a byte to parallel interface
}

void sol20_state::sol20_fe_w(u8 data)
{
	m_sol20_fe = data;
}

void sol20_state::mem_map(address_map &map)
{
	map(0x0000, 0xbfff).ram().share("mainram"); // optional s100 ram
	map(0xc000, 0xc7ff).rom().region("maincpu", 0);
	map(0xc800, 0xcbff).ram(); // system ram
	map(0xcc00, 0xcfff).ram().share("videoram");
	map(0xd000, 0xffff).ram(); // optional s100 ram
}

void sol20_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0xf8, 0xf8).rw(FUNC(sol20_state::sol20_f8_r), FUNC(sol20_state::sol20_f8_w));
	map(0xf9, 0xf9).rw(m_uart_s, FUNC(ay51013_device::receive), FUNC(ay51013_device::transmit));
	map(0xfa, 0xfa).rw(FUNC(sol20_state::sol20_fa_r), FUNC(sol20_state::sol20_fa_w));
	map(0xfb, 0xfb).rw(m_uart, FUNC(ay51013_device::receive), FUNC(ay51013_device::transmit));
	map(0xfc, 0xfc).r(FUNC(sol20_state::sol20_fc_r));
	map(0xfd, 0xfd).rw(FUNC(sol20_state::sol20_fd_r), FUNC(sol20_state::sol20_fd_w));
	map(0xfe, 0xfe).w(FUNC(sol20_state::sol20_fe_w));
	map(0xff, 0xff).portr("S2");
/*  map(0xf8, 0xf8) serial status in (bit 6=data av, bit 7=tmbe)
    map(0xf9, 0xf9) serial data in, out
    map(0xfa, 0xfa) general status in (bit 0=keyb data av, bit 1=parin data av, bit 2=parout ready)
    map(0xfb, 0xfb) tape
    map(0xfc, 0xfc) keyboard data in
    map(0xfd, 0xfd) parallel data in, out
    map(0xfe, 0xfe) scroll register
    map(0xff, 0xff) sense switches */
}

/* Input ports */
static INPUT_PORTS_START( sol20 )
	PORT_START("ARROWS")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("S1")
	PORT_DIPNAME( 0x04, 0x00, "Ctrl Chars")
	PORT_DIPSETTING(    0x04, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x08, 0x00, "Polarity")
	PORT_DIPSETTING(    0x00, DEF_STR( Normal ))
	PORT_DIPSETTING(    0x08, "Inverse")
	PORT_DIPNAME( 0x30, 0x10, "Cursor Type")
	PORT_DIPSETTING(    0x10, "Blinking")
	PORT_DIPSETTING(    0x20, "Solid")
	PORT_DIPSETTING(    0x30, DEF_STR(None))

	PORT_START("S2") // Sense Switches
	PORT_DIPNAME( 0x01, 0x01, "FF bit 0")
	PORT_DIPSETTING(    0x01, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x02, 0x02, "FF bit 1")
	PORT_DIPSETTING(    0x02, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x04, 0x04, "FF bit 2")
	PORT_DIPSETTING(    0x04, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x08, 0x08, "FF bit 3")
	PORT_DIPSETTING(    0x08, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x10, 0x10, "FF bit 4")
	PORT_DIPSETTING(    0x10, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x20, 0x20, "FF bit 5")
	PORT_DIPSETTING(    0x20, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x40, 0x40, "FF bit 6")
	PORT_DIPSETTING(    0x40, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x80, 0x80, "FF bit 7")
	PORT_DIPSETTING(    0x80, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))

	PORT_START("S3")
	PORT_DIPNAME( 0xff, 0x08, "Baud Rate")
	PORT_DIPSETTING(    0x01, "75")
	PORT_DIPSETTING(    0x02, "110")
	PORT_DIPSETTING(    0x04, "180")
	PORT_DIPSETTING(    0x08, "300")
	PORT_DIPSETTING(    0x10, "600")
	PORT_DIPSETTING(    0x20, "1200")
	PORT_DIPSETTING(    0x40, "2400")
	PORT_DIPSETTING(    0x80, "4800/9600")

	PORT_START("S4")
	PORT_DIPNAME( 0x11, 0x10, "Parity")
	PORT_DIPSETTING(    0x00, "Even")
	PORT_DIPSETTING(    0x01, "Odd")
	PORT_DIPSETTING(    0x10, DEF_STR(None))
	PORT_DIPNAME( 0x06, 0x06, "Data Bits")
	PORT_DIPSETTING(    0x00, "5")
	PORT_DIPSETTING(    0x02, "6")
	PORT_DIPSETTING(    0x04, "7")
	PORT_DIPSETTING(    0x06, "8")
	PORT_DIPNAME( 0x08, 0x08, "Stop Bits")
	PORT_DIPSETTING(    0x00, "1")
	PORT_DIPSETTING(    0x08, "2")
	PORT_DIPNAME( 0x20, 0x00, "Duplex")
	PORT_DIPSETTING(    0x00, "Half")
	PORT_DIPSETTING(    0x20, "Full")

	PORT_START("CONFIG")
	PORT_CONFNAME( 0x01, 0x01, "High Baud Rate") // jumper on the board
	PORT_CONFSETTING(    0x00, "4800")
	PORT_CONFSETTING(    0x01, "9600")
	PORT_CONFNAME( 0x02, 0x00, "Character Rom")
	PORT_CONFSETTING(    0x00, "6574")
	PORT_CONFSETTING(    0x02, "6575")
	PORT_CONFNAME( 0x04, 0x04, "Field Rate") // modification described on page III-40 of Sol Systems Manual
	PORT_CONFSETTING(    0x00, "50 Hz")
	PORT_CONFSETTING(    0x04, "60 Hz")
	PORT_CONFNAME( 0x18, 0x18, "CPU Clock")
	PORT_CONFSETTING(    0x18, "2.04 MHz (8080A)")
	PORT_CONFSETTING(    0x10, "2.36 MHz (8080A-2)")
	PORT_CONFSETTING(    0x08, "2.86 MHz (8080A-1)")
INPUT_PORTS_END


void sol20_state::machine_start()
{
	m_cassette_timer = timer_alloc(FUNC(sol20_state::sol20_cassette_tc), this);
	save_item(NAME(m_sol20_fa));
	save_item(NAME(m_sol20_fc));
	save_item(NAME(m_sol20_fe));
	save_item(NAME(m_framecnt)); // unimportant
}

void sol20_state::machine_reset()
{
	u8 data = 0, s_count = 0;
	int s_clock;
	const u16 s_bauds[8]={ 75, 110, 180, 300, 600, 1200, 2400, 4800 };
	m_sol20_fe=0;
	m_sol20_fa=1;

	// set hard-wired uart pins
	m_uart->write_cs(0);
	m_uart->write_nb1(1);
	m_uart->write_nb2(1);
	m_uart->write_tsb(1);
	m_uart->write_eps(1);
	m_uart->write_np(1);
	m_uart->write_cs(1);

	// set switched uart pins
	data = m_iop_s4->read();
	m_uart_s->write_cs(0);
	m_uart_s->write_nb1(BIT(data, 1));
	m_uart_s->write_nb2(BIT(data, 2));
	m_uart_s->write_tsb(BIT(data, 3));
	m_uart_s->write_eps(BIT(data, 0));
	m_uart_s->write_np(BIT(data, 4));
	m_uart_s->write_cs(1);

	// set rs232 baud rate
	data = m_iop_s3->read();

	if (data > 1)
		do
		{
			s_count++;
			data >>= 1;
		}
		while (!(data & 1) && (s_count < 7)); // find which switch is used

	if ( (s_count == 7) & BIT(m_iop_config->read(), 0) ) // if highest, look at jumper
		s_clock = 9600 << 4;
	else
		s_clock = s_bauds[s_count] << 4;

	m_uart_s_clock->set_unscaled_clock(s_clock);

	m_rs232->write_dtr(0);
	m_rs232->write_rts(1);

	int lines = BIT(m_iop_config->read(), 2) ? 260 : 312;
	m_screen->configure(918, lines, m_screen->visible_area(), attotime::from_ticks(918 * lines, 14.318181_MHz_XTAL).as_attoseconds());

	// set CPU speed (TODO: also present on bus pin 49)
	double freq = (14.318181_MHz_XTAL / (4 + ((m_iop_config->read() >> 3) & 3))).dvalue();
	m_maincpu->set_unscaled_clock(freq);

	// Boot tap
	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x0000, 0x07ff, m_rom);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0xc000, 0xc7ff,
			"rom_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// reinstall RAM over the ROM shadow
					m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x07ff, m_ram);
				}
			},
			&m_rom_shadow_tap);
}


u32 sol20_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
// Visible screen is 64 x 16, with start position controlled by scroll register.
// Each character is 9 pixels wide (blank ones at the right) and 13 lines deep.
// Note on blinking characters:
// any character with bit 7 set will blink. With DPMON, do DA C000 C2FF to see what happens
	u16 which = (m_iop_config->read() & 2) << 10;
	u8 s1 = m_iop_s1->read();
	u16 sy=0;
	u8 polarity = (s1 & 8) ? 0xff : 0;

	bool cursor_inv = false;
	if (((s1 & 0x30) == 0x20) || (((s1 & 0x30) == 0x10) && (m_framecnt & 0x08)))
		cursor_inv = true;

	m_framecnt++;

	u16 ma = m_sol20_fe << 6; // scroll register

	for (u8 y = 0; y < 16; y++)
	{
		for (u8 ra = 0; ra < 13; ra++)
		{
			u16 *p = &bitmap.pix(sy++);

			for (u16 x = ma; x < ma + 64; x++)
			{
				u16 inv = polarity;
				u8 chr = m_vram[x & 0x3ff];

				// cursor
				if (BIT(chr, 7) && cursor_inv)
					inv ^= 0xff;

				chr &= 0x7f;

				u8 gfx;
				if ((ra == 0) || ((s1 & 4) && (chr < 0x20)))
					gfx = inv;
				else if ((chr==0x2C) || (chr==0x3B) || (chr==0x67) || (chr==0x6A) || (chr==0x70) || (chr==0x71) || (chr==0x79))
				{
					if (ra < 4)
						gfx = inv;
					else
						gfx = m_p_chargen[which | (chr<<4) | (ra-4) ] ^ inv;
				}
				else
				{
					if (ra < 10)
						gfx = m_p_chargen[which | (chr<<4) | (ra-1) ] ^ inv;
					else
						gfx = inv;
				}

				/* Display a scanline of a character */
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
				*p++ = BIT(inv, 0);
			}
		}
		ma+=64;
	}
	return 0;
}

/* F4 Character Displayer */
static const gfx_layout sol20_charlayout =
{
	7, 9,                   /* 7 x 9 characters */
	128*2,                  /* 128 characters per rom */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_sol20 )
	GFXDECODE_ENTRY( "chargen", 0x0000, sol20_charlayout, 0, 1 )
GFXDECODE_END

void sol20_state::kbd_put(u8 data)
{
	if (data)
	{
		m_sol20_fa &= 0xfe;
		m_sol20_fc = data;
	}
}

void sol20_state::sol20(machine_config &config)
{
	/* basic machine hardware */
	I8080A(config, m_maincpu, 14.318181_MHz_XTAL / 7); // divider selectable as 5, 6 or 7 through jumpers
	m_maincpu->set_addrmap(AS_PROGRAM, &sol20_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &sol20_state::io_map);
	m_maincpu->out_inte_func().set("speaker", FUNC(speaker_sound_device::level_w));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(14.318181_MHz_XTAL, 918, 0, 576, 260, 0, 208);
	m_screen->set_screen_update(FUNC(sol20_state::screen_update));
	m_screen->set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_sol20);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 2.00); // music board

	// devices
	CASSETTE(config, m_cass1).set_formats(sol20_cassette_formats);
	m_cass1->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass1->add_route(ALL_OUTPUTS, "mono", 0.05); // cass1 speaker
	m_cass1->set_interface("sol20_cass");

	CASSETTE(config, m_cass2).set_formats(sol20_cassette_formats);
	m_cass2->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass2->add_route(ALL_OUTPUTS, "mono", 0.05); // cass2 speaker
	m_cass2->set_interface("sol20_cass");

	AY51013(config, m_uart); // TMS6011NC
	m_uart->set_auto_rdav(true); // ROD (pin 4) tied to RDD (pin 18)

	CLOCK(config, m_uart_clock, 4800);
	m_uart_clock->signal_handler().set(m_uart, FUNC(ay51013_device::write_rcp));
	m_uart_clock->signal_handler().append(m_uart, FUNC(ay51013_device::write_tcp));

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);

	AY51013(config, m_uart_s); // TMS6011NC
	m_uart_s->read_si_callback().set(m_rs232, FUNC(rs232_port_device::rxd_r));
	m_uart_s->write_so_callback().set(m_rs232, FUNC(rs232_port_device::write_txd));
	m_uart_s->set_auto_rdav(true); // ROD (pin 4) tied to RDD (pin 18)

	CLOCK(config, m_uart_s_clock, 4800);
	m_uart_s_clock->signal_handler().set(m_uart_s, FUNC(ay51013_device::write_rcp));
	m_uart_s_clock->signal_handler().append(m_uart_s, FUNC(ay51013_device::write_tcp));

	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(sol20_state::kbd_put));

	SOFTWARE_LIST(config, "cass_list").set_original("sol20_cass");
}

/* ROM definition */
ROM_START( sol20 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_SYSTEM_BIOS(0, "solos", "SOLOS")
	ROMX_LOAD( "solos.bin", 0x0000, 0x0800, CRC(4d0af383) SHA1(ac4510c3380ed4a31ccf4f538af3cb66b76701ef), ROM_BIOS(0) )    // from solace emu
	ROM_SYSTEM_BIOS(1, "dpmon", "DPMON")
	ROMX_LOAD( "dpmon.bin", 0x0000, 0x0800, BAD_DUMP CRC(2a84f099) SHA1(60ff6e38082c50afcf0f40707ef65668a411008b), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS(2, "consol", "CONSOL")
	ROMX_LOAD( "consol.bin", 0x0000, 0x0400, BAD_DUMP CRC(80bf6d85) SHA1(84b81c60bb08a3a5435ec1be56a67aa695bce099), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS(3, "solos2", "Solos Patched")
	ROMX_LOAD( "solos2.bin", 0x0000, 0x0800, CRC(7776cc7d) SHA1(c4739a9ea7e8146ce7ae3305ed526b6045efa9d6), ROM_BIOS(3) ) // from Nama
	ROM_SYSTEM_BIOS(4, "bootload", "BOOTLOAD")
	ROMX_LOAD( "bootload.bin", 0x0000, 0x0800, BAD_DUMP CRC(4261ac71) SHA1(4752408ac85d88857e8e9171c7f42bd623c9271e), ROM_BIOS(4) ) // from Nama
	// This one doesn't work
	ROM_SYSTEM_BIOS(5, "cuter", "CUTER")
	ROMX_LOAD( "cuter.bin", 0x0000, 0x0800, BAD_DUMP CRC(39cca901) SHA1(33725d6da63e295552ee13f0a735d33aee8f0d17), ROM_BIOS(5) ) // from Nama

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "6574.bin", 0x0000, 0x0800, BAD_DUMP CRC(fd75df4f) SHA1(4d09aae2f933478532b7d3d1a2dee7123d9828ca) )
	ROM_LOAD( "6575.bin", 0x0800, 0x0800, BAD_DUMP CRC(cfdb76c2) SHA1(ab00798161d13f07bee3cf0e0070a2f0a805591f) )

	ROM_REGION( 0x100, "keyboard", 0 )
	ROM_LOAD( "8574.u18", 0x000, 0x100, NO_DUMP ) // 256x4 bipolar PROM or mask ROM; second half unused
ROM_END

} // anonymous namespace

/* Driver */
//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY                             FULLNAME                    FLAGS
COMP( 1976, sol20, 0,      0,      sol20,   sol20, sol20_state, empty_init, "Processor Technology Corporation", "Sol-20 Terminal Computer", MACHINE_SUPPORTS_SAVE )



pubint_storyreader.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/******************************************************************************

    Non-video 'electronic book' reader that takes cartridges

    Cartridges are accessed using serial read methods, contain data, not code
    so must be internal ROMs on the systems.


    ---

    V1 - rectangular cart
    glob up, pins toward you, pins 1-10, left to right

    1  tied high
    2  CE1 left glob
    3  CE2 right glob
    4  pin 5
    5  pin 4
    6  data
    7  ground
    8  clock
    9  3.3 volts
    10 tied high

    ----

    V2 - rounded top cart

    1  CE1 left glob
    2  CE2 right glob
    3  tied high
    4  ground
    5  clock
    6  data
    7  ground
    8  ground
    9  3.3 volts
    10 tied high

    V2 PCB silkscreened with GPR23L822A - 8Mbit serial ROM

    ----

    To dump:
    set data and clock high, CE1 low for 1 mS
    set CE1 high for 3 uS
    set data low for 2 uS
    set clock low for 2 uS
    set data high for 2 uS
    set clock high for 2 uS
    set clock low for 2 uS
    set data low for 2 uS
    loop 24 times:
      set clock high for 1 uS
      set clock low for 1 uS
    make data high Z
    loop 8M tims:
      set clock high for 1 uS
      sample data
      set clock low for 1 uS
    set CE1 low

    if 2 globs, repeat with CE2

*******************************************************************************/

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class pi_storyreader_state : public driver_device
{
public:
	pi_storyreader_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_cart(*this, "cartslot"),
		m_cart_region(nullptr)
	{ }

	void pi_storyreader(machine_config &config);
	void pi_storyreader_v2(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	required_device<generic_slot_device> m_cart;
	memory_region *m_cart_region;
};



void pi_storyreader_state::machine_start()
{
	// if there's a cart, override the standard mapping
	if (m_cart && m_cart->exists())
	{
		std::string region_tag;
		m_cart_region = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());
	}
}

void pi_storyreader_state::machine_reset()
{
}

DEVICE_IMAGE_LOAD_MEMBER(pi_storyreader_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

static INPUT_PORTS_START( pi_storyreader )
INPUT_PORTS_END


void pi_storyreader_state::pi_storyreader(machine_config &config)
{
	// unknown CPU / MCU type

	// screenless

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "pi_storyreader_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(pi_storyreader_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("pi_storyreader_cart");
}

void pi_storyreader_state::pi_storyreader_v2(machine_config &config)
{
	// unknown CPU / MCU type

	// screenless

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "pi_storyreader_v2_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(pi_storyreader_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("pi_storyreader_v2_cart");
}


ROM_START( pi_stry )
	ROM_REGION( 0x100000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "internal.mcu.rom", 0x0000, 0x1000, NO_DUMP ) // unknown type / size
ROM_END

ROM_START( pi_stry2 )
	ROM_REGION( 0x100000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "internal.mcu.rom", 0x0000, 0x1000, NO_DUMP ) // unknown type / size
ROM_END

} // anonymous namespace


//    year, name,        parent,    compat, machine,            input,            class,                  init,       company,    fullname,                         flags

// These are said to not be compatible with each other
CONS( 200?, pi_stry,     0,         0,      pi_storyreader,      pi_storyreader, pi_storyreader_state, empty_init, "Publications International Ltd", "Story Reader",                MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 200?, pi_stry2,    0,         0,      pi_storyreader_v2,   pi_storyreader, pi_storyreader_state, empty_init, "Publications International Ltd", "Story Reader 2.0",            MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



pulsar.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Pulsar Little Big Board (6000 series)

2013-12-29 Skeleton driver.

Chips: Z80A @4MHz, Z80DART, FD1797-02, 8255A-5, AY-5-8116, MSM5832.
Crystals: 4 MHz, 5.0688 MHz, 32768.

This is a complete CP/M single-board computer. You needed to supply your own
power supply and serial terminal.

The terminal must be set for 9600 baud, 7 bits, even parity, 1 stop bit.


ToDo:
- Fix floppy. It needs to WAIT the cpu whenever port 0xD3 is read, wait
  for either DRQ or INTRQ to assert, then release the cpu and then do the
  actual port read. Our Z80 cannot do that.
- Fix FDC so MAME doesn't crash when a certain disk is inserted.


Monitor Commands:
B - Boot from disk
D - Dump memory
F - Fill memory
G - Go
I - In port
L - Load bootstrap from drive A to 0x80
M - Modify memory
O - Out port
P - choose which rs232 channel for the console
T - Test memory
V - Move memory
X - Test off-board memory banks

****************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/com8116.h"
#include "machine/i8255.h"
#include "machine/msm5832.h"
#include "machine/wd_fdc.h"
#include "machine/z80daisy.h"
#include "machine/z80sio.h"


namespace {

class pulsar_state : public driver_device
{
public:
	pulsar_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_bank1(*this, "bank1")
		, m_fdc (*this, "fdc")
		, m_floppy0(*this, "fdc:0")
		, m_floppy1(*this, "fdc:1")
		, m_rtc(*this, "rtc")
	{ }

	void pulsar(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

	void ppi_pa_w(u8 data);
	void ppi_pb_w(u8 data);
	void ppi_pc_w(u8 data);
	u8 ppi_pc_r();

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	floppy_image_device *m_floppy;
	memory_passthrough_handler m_rom_shadow_tap;
	required_device<z80_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_memory_bank    m_bank1;
	required_device<fd1797_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	required_device<floppy_connector> m_floppy1;
	required_device<msm5832_device> m_rtc;
};

void pulsar_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).ram().share("mainram");
	map(0xf800, 0xffff).bankr("bank1");
}

void pulsar_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0xc0, 0xc3).mirror(0x0c).rw("dart", FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
	map(0xd0, 0xd3).mirror(0x0c).rw(m_fdc, FUNC(fd1797_device::read), FUNC(fd1797_device::write));
	map(0xe0, 0xe3).mirror(0x0c).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xf0, 0xf0).mirror(0x0f).w("brg", FUNC(com8116_device::stt_str_w));
}


/*
d0..d3 Drive select 0-3 (we only emulate 1 drive)
d4     Side select 0=side0
d5     /DDEN
d6     /DSK_WAITEN (don't know what this is, not emulated)
d7     XMEMEX line (for external memory, not emulated)
*/
void pulsar_state::ppi_pa_w(u8 data)
{
	m_floppy = nullptr;
	if (BIT(data, 0))
		m_floppy = m_floppy0->get_device();
	else
	if (BIT(data, 1))
		m_floppy = m_floppy1->get_device();
	m_fdc->set_floppy(m_floppy);
	m_fdc->dden_w(BIT(data, 5));
	if (m_floppy)
		m_floppy->mon_w(0);
}

/*
d0..d3 RTC address
d4     RTC read line
d5     RTC write line
d6     RTC hold line
d7     Allow 64k of ram
*/
void pulsar_state::ppi_pb_w(u8 data)
{
	m_rtc->address_w(data & 0x0f);
	m_rtc->read_w(BIT(data, 4));
	m_rtc->write_w(BIT(data, 5));
	m_rtc->hold_w(BIT(data, 6));
	m_bank1->set_entry(BIT(data, 7));
}

// d0..d3 Data lines to rtc
void pulsar_state::ppi_pc_w(u8 data)
{
	m_rtc->data_w(data & 15);
}

// d7     /2 SIDES
u8 pulsar_state::ppi_pc_r()
{
	uint8_t data = 0;
	if (m_floppy)
		data = m_floppy->twosid_r() << 7;
	return m_rtc->data_r() | data;
}

static const z80_daisy_config daisy_chain_intf[] =
{
	{ "dart" },
	{ nullptr }
};

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

static void pulsar_floppies(device_slot_interface &device)
{
	device.option_add("flop", FLOPPY_8_DSDD);
}

/* Input ports */
static INPUT_PORTS_START( pulsar )
INPUT_PORTS_END

void pulsar_state::machine_reset()
{

	m_bank1->set_entry(1);
	m_rtc->cs_w(1); // always enabled

	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x0000, 0x07ff, m_rom);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0xf800, 0xffff,
			"rom_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// reinstall ram over the rom shadow
					m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x07ff, m_ram);
				}
			},
			&m_rom_shadow_tap);
}

void pulsar_state::machine_start()
{
	// register for savestates
	m_bank1->configure_entry(0, m_ram+0xf800);
	m_bank1->configure_entry(1, m_rom);
}

void pulsar_state::pulsar(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &pulsar_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &pulsar_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain_intf);


	/* Devices */
	i8255_device &ppi(I8255(config, "ppi"));
	ppi.out_pa_callback().set(FUNC(pulsar_state::ppi_pa_w));
	ppi.out_pb_callback().set(FUNC(pulsar_state::ppi_pb_w));
	ppi.in_pc_callback().set(FUNC(pulsar_state::ppi_pc_r));
	ppi.out_pc_callback().set(FUNC(pulsar_state::ppi_pc_w));

	MSM5832(config, "rtc", 32.768_kHz_XTAL);

	z80dart_device& dart(Z80DART(config, "dart", 4_MHz_XTAL));
	dart.out_txda_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	dart.out_dtra_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	dart.out_rtsa_callback().set("rs232", FUNC(rs232_port_device::write_rts));
	dart.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("dart", FUNC(z80dart_device::rxa_w));
	rs232.cts_handler().set("dart", FUNC(z80dart_device::ctsa_w));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	com8116_device &brg(COM8116(config, "brg", 5.0688_MHz_XTAL));
	// Schematic has the labels for FT and FR the wrong way around, but the pin numbers are correct.
	brg.fr_handler().set("dart", FUNC(z80dart_device::txca_w));
	brg.fr_handler().append("dart", FUNC(z80dart_device::rxca_w));
	brg.ft_handler().set("dart", FUNC(z80dart_device::txcb_w));
	brg.ft_handler().append("dart", FUNC(z80dart_device::rxcb_w));

	FD1797(config, m_fdc, 4_MHz_XTAL / 2);
	FLOPPY_CONNECTOR(config, "fdc:0", pulsar_floppies, "flop", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", pulsar_floppies, "flop", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
}

/* ROM definition */
ROM_START( pulsarlb )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_SYSTEM_BIOS(0, "mon7", "MP7A")
	ROMX_LOAD( "mp7a.u2",   0x0000, 0x0800, CRC(726b8a19) SHA1(43b2af84d5622c1f67584c501b730acf002a6113), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "mon6", "LBOOT6") // Blank screen until floppy boots
	ROMX_LOAD( "lboot6.u2", 0x0000, 0x0800, CRC(3bca9096) SHA1(ff99288e51a9e832785ce8e3ab5a9452b1064231), ROM_BIOS(1))
ROM_END

} // anonymous namespace

/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT         COMPANY   FULLNAME                          FLAGS
COMP( 1981, pulsarlb, 0,      0,      pulsar,  pulsar, pulsar_state, empty_init, "Pulsar", "Little Big Board (6000 series)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )




pv1000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/***************************************************************************

    Driver for Casio PV-1000

***************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


// PV-1000 Sound device

class pv1000_sound_device : public device_t, public device_sound_interface
{
public:
	pv1000_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	void voice_w(offs_t offset, uint8_t data);

protected:
	// device-level overrides
	virtual void device_start() override ATTR_COLD;

	// sound stream update overrides
	virtual void sound_stream_update(sound_stream &stream) override;

private:
	// internal state
	struct
	{
		uint32_t  count  = 0;
		uint16_t  period = 0;
		uint8_t   val    = 1; // boot state of all channels
	} m_voice[3];

	uint8_t m_ctrl = 0;
	sound_stream *m_sh_channel = nullptr;
};

DEFINE_DEVICE_TYPE(PV1000, pv1000_sound_device, "pv1000_sound", "NEC D65010G031")

pv1000_sound_device::pv1000_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	device_t(mconfig, PV1000, tag, owner, clock),
	device_sound_interface(mconfig, *this)
{
}

//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void pv1000_sound_device::device_start()
{
	m_sh_channel = stream_alloc(0, 1, clock() / 1024);

	save_item(STRUCT_MEMBER(m_voice, count));
	save_item(STRUCT_MEMBER(m_voice, period));
	save_item(STRUCT_MEMBER(m_voice, val));

	save_item(NAME(m_ctrl));
}

void pv1000_sound_device::voice_w(offs_t offset, uint8_t data)
{
	offset &= 0x03;
	m_sh_channel->update();

	switch (offset)
	{
	case 0x03:
		m_ctrl = data;
		break;

	default:
	{
		const uint8_t per = ~data & 0x3f;

		if ((per == 0) && (m_voice[offset].period != 0))
		{
			// flip output once and stall there!
			m_voice[offset].val = !m_voice[offset].val;
		}

		m_voice[offset].period = per;
	}
	break;
	}
}


//-------------------------------------------------
//  sound_stream_update - handle a stream update
//-------------------------------------------------

/*
  plgDavid's audio implementation/analysis notes:

  Sound appears to be 3 50/50 pulse voices made by cutting the main clock by 1024,
  then by the value of the 6bit period registers.
  This creates a surprisingly accurate pitch range.
  Note: the register periods are inverted.

  plgDavid 2023 update: lidnariq (NESDEV) took a fondness to the system and gave me a bunch of test roms
  to strenghten the emulation.

  Quite a few things were uncovered overall, but for audio specifically:
  1)Audio mix/mux control ($FB, case 0x03) was ignored
  2)lidnariq's tracing of my PCB scans showed that all three sound outputs are mixed using different volumes:
  square1 via i/o$F8 is -6dB, square2 via i/o$F9 is -3dB, defining square3 via i/o$FA as 0dB
*/

void pv1000_sound_device::sound_stream_update(sound_stream &stream)
{
	// Each channel has a different volume via resistor mixing which correspond to -6dB, -3dB, 0dB drops
	static const int volumes[3] = { 0x1000, 0x1800, 0x2000 };

	for (int index = 0; index < stream.samples(); index++)
	{
		s32 sum = 0;

		// First calculate all vals
		for (int i = 0; i < 3; i++)
		{
			m_voice[i].count++;

			if ((m_voice[i].period > 0) && (m_voice[i].count >= m_voice[i].period))
			{
				m_voice[i].count = 0;
				m_voice[i].val = !m_voice[i].val;
			}
		}

		// Then mix channels according to m_ctrl
		if (BIT(m_ctrl, 1))
		{
			// ch0 and ch1
			if (BIT(m_ctrl, 0))
			{
				const int xor01 = BIT(m_voice[0].val ^ m_voice[1].val, 0);
				const int xor12 = BIT(m_voice[1].val ^ m_voice[2].val, 0);
				sum += xor01 * volumes[0];
				sum += xor12 * volumes[1];
			}
			else
			{
				sum += m_voice[0].val * volumes[0];
				sum += m_voice[1].val * volumes[1];
			}

			// ch3 is unaffected by m_ctrl bit 1
			sum += m_voice[2].val * volumes[2];
		}

		stream.put_int(0, index, sum, 32768);
	}
}


// PV-1000 System

class pv1000_state : public driver_device
{
public:
	pv1000_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_sound(*this, "pv1000_sound"),
		m_cart(*this, "cartslot"),
		m_videoram(*this, "videoram"),
		m_gfxdecode(*this, "gfxdecode"),
		m_screen(*this, "screen"),
		m_palette(*this, "palette"),
		m_joysticks(*this, "IN%u", 0)
	{ }

	void pv1000(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void io_w(offs_t offset, uint8_t data);
	uint8_t io_r(offs_t offset);
	void gfxram_w(offs_t offset, uint8_t data);
	uint8_t joystick_r();
	uint8_t m_io_regs[8]{};

	emu_timer *m_irq_on_timer = nullptr;
	emu_timer *m_busrq_on_timer = nullptr;
	emu_timer *m_busrq_off_timer = nullptr;
	uint8_t m_irq_enabled = 0;
	uint8_t m_irq_active = 0;
	uint8_t m_pcg_bank = 0;
	uint8_t m_force_pattern = 0;
	uint8_t m_border_col = 0;
	uint8_t m_render_disable = 0;

	uint8_t * m_gfxram = nullptr;
	void pv1000_postload();

	required_device<cpu_device> m_maincpu;
	required_device<pv1000_sound_device> m_sound;
	required_device<generic_slot_device> m_cart;
	required_shared_ptr<uint8_t> m_videoram;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_ioport_array<4> m_joysticks;

	uint32_t screen_update_pv1000(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	TIMER_CALLBACK_MEMBER(d65010_irq_on_cb);
	TIMER_CALLBACK_MEMBER(d65010_busrq_on_cb);
	TIMER_CALLBACK_MEMBER(d65010_busrq_off_cb);
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	void pv1000_mem(address_map &map) ATTR_COLD;
	void pv1000_io(address_map &map) ATTR_COLD;
};


void pv1000_state::pv1000_mem(address_map &map)
{
	//map(0x0000, 0x7fff) // mapped by the cartslot
	map(0xb800, 0xbbff).ram().share(m_videoram);
	map(0xbc00, 0xbfff).ram().w(FUNC(pv1000_state::gfxram_w)).region("gfxram", 0);
}


void pv1000_state::pv1000_io(address_map &map)
{
	map.global_mask(0xff);
	map(0xf8, 0xff).rw(FUNC(pv1000_state::io_r), FUNC(pv1000_state::io_w));
}


void pv1000_state::gfxram_w(offs_t offset, uint8_t data)
{
	m_gfxram[offset] = data;
	m_gfxdecode->gfx(1)->mark_dirty(offset / 32);
}


void pv1000_state::io_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 0x00:
	case 0x01:
	case 0x02:
	case 0x03:
		//logerror("io_w offset=%02x, data=%02x (%03d)\n", offset, data , data);
		m_sound->voice_w(offset, data);
		break;

	case 0x04:
		/* Bit 1 = Matrix IRQ enabled    *
		 * Bit 0 = Prerender IRQ enabled */
		m_irq_enabled = data & 3;
		m_irq_active &= m_irq_enabled;
		if (m_irq_active == 0)
			m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);

		break;

	case 0x05:
		// Acknowledge Prerender IRQ
		if (m_irq_active & 1)
		{
			m_irq_active &= ~1;
			if (m_irq_active == 0)
				m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
		}
		break;

	//case 0x06 VRAM + PCG location, always fixed at 0xb8xx

	case 0x07:
		/* ---- -xxx unknown, border color? */
		m_pcg_bank = (data & 0xe0) >> 5;
		m_force_pattern = ((data & 0x10) >> 4); /* Dig Dug relies on this */
		m_render_disable = ((data & 0x08) >> 3);
		if (m_render_disable == 0) // If we're enabling rendering mid-scanline...
		{
			int hpos = m_screen->hpos(); // set_raw configured so that BUSREQ is asserted from 0 to 248
			int vpos = m_screen->vpos();
			if (hpos < 248 && vpos >= 26 && vpos < 192+26)
			{
				m_maincpu->set_input_line(Z80_INPUT_LINE_BUSRQ, ASSERT_LINE);
				// The de-assertion is always automatically scheduled
			}
		}

		m_border_col = data & 7;
		break;
	}

	m_io_regs[offset] = data;
}


uint8_t pv1000_state::io_r(offs_t offset)
{
	/* Real hardware always reads MSbit set from the two readable registers;
	   lidnariq suspects D7 is input-only and the 128s bit is high due to the
	   weak pull-ups on the data bus */
	switch (offset)
	{
		case 0x04: // port $FC returns player 2 joystick and interrupt status
			return 0x80 | (joystick_r() & 0x0c) | (m_irq_active & 3); // Bit 1 = Matrix IRQ, Bit 0 = Prerender IRQ

		case 0x05: // port $FD returns both joysticks and acknowledges matrix scan IRQ
			if (!machine().side_effects_disabled() && (m_irq_active & 2))
			{
				m_irq_active &= ~2;
				if (m_irq_active == 0)
					m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
			}
			return 0x80 | joystick_r();

		default:
			/* Ports $F8-$FB, $FE, and $FF are undriven, and pulled high by the
			   resistors next to the Z80 */
			return 0xff;
	}
}

uint8_t pv1000_state::joystick_r()
{
	uint8_t data = 0;

	for (int i = 0; i < 4; i++)
		if (BIT(m_io_regs[5], i))
			data |= m_joysticks[i]->read();

	return data & 0x0f;
}


static INPUT_PORTS_START( pv1000 )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(2)

	PORT_START("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_8WAY

	PORT_START("IN2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2) PORT_8WAY

	PORT_START("IN3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2)
INPUT_PORTS_END


DEVICE_IMAGE_LOAD_MEMBER(pv1000_state::cart_load)
{
	uint32_t size = m_cart->common_get_size("rom");

	if (size != 0x2000 && size != 0x4000 && size != 0x8000)
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported cartridge size (must be 8K, 16K or 32K)");

	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}


uint32_t pv1000_state::screen_update_pv1000(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	bitmap.fill(m_border_col); // border is on top and bottom

	if (m_render_disable)
		return 0;

	for (int y = 0; y < 24; y++)
	{
		for (int x = 2; x < 30; x++) // left-right most columns never even drawn, black instead
		{
			uint16_t tile = m_videoram[y * 32 + x];

			if (tile < 0xe0 || m_force_pattern)
			{
				tile += (m_pcg_bank << 8);
				//When we adjusted timing in set_raw so that BUSREQ is asserted during a clean rectangle, we need to compensate for that here
				m_gfxdecode->gfx(0)->opaque(bitmap,cliprect, tile, 0, 0, 0, x*8+16, y*8+26);
			}
			else
			{
				tile -= 0xe0;
				m_gfxdecode->gfx(1)->opaque(bitmap,cliprect, tile, 0, 0, 0, x*8+16, y*8+26);
			}
		}
	}

	return 0;
}



/* Interrupt is triggering 16 times during vblank. */
/* They are spaced every 4 scanlines, with equal padding before and after */
TIMER_CALLBACK_MEMBER(pv1000_state::d65010_irq_on_cb)
{
	int vpos = m_screen->vpos();
	int next_vpos = vpos + 4;

	m_irq_active |= 2 & m_irq_enabled;
	if (vpos == 20)
	{
		m_irq_active |= 1 & m_irq_enabled;
	}

	if (m_irq_active)
	{
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
	}

	/* Schedule next IRQ trigger

	   These numbers are an artifact of defining Y=0 as the
	   top scanline of the non-blanking display. */
	if (vpos >= 258)
	{
		next_vpos = 0; // 262=0, 4, 8, 12, 16, 20
	}
	else if (vpos >= 20 && vpos < 222)
	{
		next_vpos = 222; // 226, 230, 234, 238, 242, 246, 250, 254, 258
	}
	m_irq_on_timer->adjust(m_screen->time_until_pos(next_vpos, 224+32));
}

/* Add BUSACK-triggered scanline renderer */
TIMER_CALLBACK_MEMBER(pv1000_state::d65010_busrq_on_cb)
{
	int vpos = m_screen->vpos();
	int next_vpos = vpos + 1;

	if (m_render_disable == 0)
	{
		m_maincpu->set_input_line(Z80_INPUT_LINE_BUSRQ, ASSERT_LINE);
	}

	// schedule the de-assertion of Busreq that corresponds to the current assertion
	m_busrq_off_timer->adjust(m_screen->time_until_pos(vpos, 248));

	if (vpos >= 192 + 26)
	{
		next_vpos = 26;
	}
	m_busrq_on_timer->adjust(m_screen->time_until_pos(next_vpos, 0));
}

TIMER_CALLBACK_MEMBER(pv1000_state::d65010_busrq_off_cb)
{
	m_maincpu->set_input_line(Z80_INPUT_LINE_BUSRQ, CLEAR_LINE);
}

void pv1000_state::pv1000_postload()
{
	// restore GFX ram
	for (int i = 0; i < 0x400; i++)
		gfxram_w(i, m_gfxram[i]);
}

void pv1000_state::machine_start()
{
	m_irq_on_timer = timer_alloc(FUNC(pv1000_state::d65010_irq_on_cb), this);
	m_busrq_on_timer = timer_alloc(FUNC(pv1000_state::d65010_busrq_on_cb), this);
	m_busrq_off_timer = timer_alloc(FUNC(pv1000_state::d65010_busrq_off_cb), this);

	m_gfxram = memregion("gfxram")->base();
	save_pointer(NAME(m_gfxram), 0x400);

	if (m_cart->exists())
	{
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x0000, 0x7fff, read8sm_delegate(*m_cart, FUNC(generic_slot_device::read_rom)));

		// FIXME: this is needed for gfx decoding, but there is probably a cleaner solution!
		std::string region_tag;
		memcpy(memregion("gfxrom")->base(), memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str())->base(), m_cart->get_rom_size());
	}

	save_item(NAME(m_io_regs));
	save_item(NAME(m_irq_enabled));
	save_item(NAME(m_irq_active));
	save_item(NAME(m_pcg_bank));
	save_item(NAME(m_force_pattern));
	save_item(NAME(m_border_col));
	save_item(NAME(m_render_disable));

	machine().save().register_postload(save_prepost_delegate(FUNC(pv1000_state::pv1000_postload), this));
}


void pv1000_state::machine_reset()
{
	m_io_regs[5] = 0;
	m_irq_on_timer->adjust(m_screen->time_until_pos(222, 256));
	m_busrq_on_timer->adjust(m_screen->time_until_pos(26, 0));
	m_busrq_off_timer->adjust(m_screen->time_until_pos(26, 248));
}


static const gfx_layout pv1000_3bpp_gfx =
{
	8, 8,           /* 8x8 characters */
	RGN_FRAC(1,1),
	3,
	{ 16*8, 8*8, 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8*4
};


static GFXDECODE_START( gfx_pv1000 )
	GFXDECODE_ENTRY( "gfxrom", 8, pv1000_3bpp_gfx, 0, 8 )
	GFXDECODE_ENTRY( "gfxram", 8, pv1000_3bpp_gfx, 0, 8 )
GFXDECODE_END


void pv1000_state::pv1000(machine_config &config)
{
	Z80(config, m_maincpu, 17897725/5);
	m_maincpu->set_addrmap(AS_PROGRAM, &pv1000_state::pv1000_mem);
	m_maincpu->set_addrmap(AS_IO, &pv1000_state::pv1000_io);

	/* D65010G031 - Video & sound chip */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(17897725/4, 288, 32, 32+224, 262, 0, 244);
	// Pixel aspect is 48/35.
	// Display aspect is MAME's 4:3 default.

	// Note that this value is overridden by the user's pv1000.cfg, if present.
	// 206px x 48/35(PAR) / 4/3(DAR) = 212sl
	m_screen->set_default_position(
			216/206.0, 0, //216 px in storage aspect; cropped to 206 px
			244/212.0, 0); //244 sl in storage aspect; cropped to 212 sl
	m_screen->set_screen_update(FUNC(pv1000_state::screen_update_pv1000));
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette, palette_device::RGB_3BIT);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_pv1000);

	SPEAKER(config, "mono").front_center();
	PV1000(config, m_sound, 17897725).add_route(ALL_OUTPUTS, "mono", 1.00);

	/* Cartridge slot */
	generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config, "cartslot", generic_linear_slot, "pv1000_cart"));
	cartslot.set_must_be_loaded(true);
	cartslot.set_device_load(FUNC(pv1000_state::cart_load));

	/* Software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("pv1000");
}


ROM_START( pv1000 )
	ROM_REGION( 0x8000, "gfxrom", ROMREGION_ERASE00 )
	ROM_REGION( 0x400, "gfxram", ROMREGION_ERASE00 )
ROM_END


/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY  FULLNAME    FLAGS */
CONS( 1983, pv1000, 0,      0,      pv1000,  pv1000, pv1000_state, empty_init, "Casio", "PV-1000",  MACHINE_SUPPORTS_SAVE )



pv2000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/*

CASIO PV-2000

(preliminary work by anondumper)
Thanks for the loaner (Ianoid)

NOTE:
The PCB has printed names of components, not ICXY, etc
but: "hn613128pc64.bin"

SEE
http://hou4gong1.mo-blog.jp/.shared/image.html?/photos/uncategorized/pv_2000_k1.jpg
http://hou4gong1.mo-blog.jp/.shared/image.html?/photos/uncategorized/pv_2000_14.jpg
http://hou4gong1.mo-blog.jp/.shared/image.html?/photos/uncategorized/pv_2000_15.jpg

Keyboard inputs are partially supported. Keys missing from the input ports:
- GAME - no beep in basic - is this really a key?

Todo:
- Add joystick support
- Cassette support

Also See:
http://www2.odn.ne.jp/~haf09260/Pv2000/EnrPV.htm
For BIOS CRC confirmation
*/


#include "emu.h"
#include "cpu/z80/z80.h"
#include "sound/sn76496.h"
#include "video/tms9928a.h"
#include "imagedev/cassette.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class pv2000_state : public driver_device
{
public:
	pv2000_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_cass(*this, "cassette"),
		m_cart(*this, "cartslot"),
		m_keyboard(*this, "IN%u", 0U)
	{ }

	void pv2000(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<cassette_image_device> m_cass;
	required_device<generic_slot_device> m_cart;
	required_ioport_array<10> m_keyboard;
	void cass_conf_w(uint8_t data);
	void keys_w(uint8_t data);
	uint8_t keys_hi_r();
	uint8_t keys_lo_r();
	uint8_t keys_mod_r();
	void pv2000_vdp_interrupt(int state);
	uint8_t cass_in();
	void cass_out(uint8_t data);
	bool m_last_state = false;
	uint8_t m_key_pressed = 0;
	uint8_t m_keyb_column = 0;
	uint8_t m_cass_conf = 0;
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
	void pv2000_io_map(address_map &map) ATTR_COLD;
	void pv2000_map(address_map &map) ATTR_COLD;
};


void pv2000_state::cass_conf_w(uint8_t data)
{
	logerror("%s: cass_conf_w %02x\n", machine().describe_context(), data);

	m_cass_conf = data & 0x0f;

	if (m_cass_conf & 0x01)
		m_cass->change_state(CASSETTE_MOTOR_ENABLED, CASSETTE_MASK_MOTOR);
	else
		m_cass->change_state(CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
}


void pv2000_state::keys_w(uint8_t data)
{
	logerror("%s: keys_w %02x\n", machine().describe_context(), data);

	m_keyb_column = data & 0x0f;

	m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
}


uint8_t pv2000_state::keys_hi_r()
{
	uint8_t data = 0;

	switch (m_keyb_column)
	{
	case 0:
	case 1:
	case 2:
	case 3:
	case 4:
	case 5:
	case 6:
	case 7:
	case 8:
		data = m_keyboard[m_keyb_column]->read() >> 4;
	}

	return data;
}


uint8_t pv2000_state::keys_lo_r()
{
	uint8_t data = 0;

	logerror("%s: pv2000_keys_r\n", machine().describe_context());

	switch (m_keyb_column)
	{
	case 0:
	case 1:
	case 2:
	case 3:
	case 4:
	case 5:
	case 6:
	case 7:
	case 8:
	case 9:
		data = m_keyboard[m_keyb_column]->read() & 0x0f;
	}

	return 0xf0 | data;
}


uint8_t pv2000_state::keys_mod_r()
{
	return 0xf0 | ioport("MOD")->read();
}

uint8_t pv2000_state::cass_in()
{
	// from what i can tell,
	// 0 = data in
	// 1 = must be high
	// 2 = must be low
	// bits 1 & 2 are checked while reading and writing tapes
	// Press STOP key (F1) to cancel LOAD or SAVE

	return 2 | ((m_cass->input() > +0.03) ? 1 : 0);
}

void pv2000_state::cass_out(uint8_t data)
{
	// it outputs 8-bit values here which are not the bytes in the file
	// result is not readable

	m_cass->output(BIT(data, 0) ? -1.0 : +1.0);
}


/* Memory Maps */

void pv2000_state::pv2000_map(address_map &map)
{
	map(0x0000, 0x3fff).rom();

	map(0x4000, 0x4001).rw("tms9928a", FUNC(tms9928a_device::read), FUNC(tms9928a_device::write));

	map(0x7000, 0x7fff).ram();
	//map(0x8000, 0xbfff) ext ram?
	//map(0xc000, 0xffff)      // mapped by the cartslot
}


void pv2000_state::pv2000_io_map(address_map &map)
{
	map.global_mask(0xff);

	//theres also printer and tape I/O (TODO)
	map(0x00, 0x00).w(FUNC(pv2000_state::cass_conf_w));

	//keyboard/joystick
	map(0x10, 0x10).r(FUNC(pv2000_state::keys_hi_r));
	map(0x20, 0x20).rw(FUNC(pv2000_state::keys_lo_r), FUNC(pv2000_state::keys_w));

	//sn76489a
	map(0x40, 0x40).r(FUNC(pv2000_state::keys_mod_r)).w("sn76489a", FUNC(sn76489a_device::write));

	/* Cassette input. Gets hit a lot after a GLOAD command */
	map(0x60, 0x60).rw(FUNC(pv2000_state::cass_in), FUNC(pv2000_state::cass_out));
}


static INPUT_PORTS_START( pv2000 )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')

	PORT_START("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')

	PORT_START("IN2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')

	PORT_START("IN3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Hiragana")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')

	PORT_START("IN4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Yen")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("IN4_1") /* Game?? DEL / MODE / STOP ??, no beep in basic, START in galaga */
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("IN4_2") /* DEL / MODE / STOP ??, no beep in basic, SELECT in galaga */
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')

	PORT_START("IN5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CRSR Up+Left") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CRSR Down+Left") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CRSR Down+Right") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CRSR Up+Right") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')

	PORT_START("IN6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DOWN) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RIGHT) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("IN6_2") /* Unknown ??, no beep in basic */
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("IN6_3") /* Unknown ??, no beep in basic */
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')

	PORT_START("IN7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_UP) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("IN7_2") /* Unknown ??, no beep in basic */
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("IN7_3") /* Unknown ??, no beep in basic */
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("IN8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LCONTROL) PORT_NAME("Attack 0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LALT) PORT_NAME("Attack 1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("IN8_2") /* Unknown ?, no beep in basic */
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("IN8_3") /* Unknown ?, no beep in basic */
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Mode")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Del")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("IN8_7") /* Unknown ?, no beep in basic */

	PORT_START("IN9")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1) PORT_NAME("Stop")

	PORT_START("MOD")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RALT) PORT_NAME("Color")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TAB) PORT_NAME("Func")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


void pv2000_state::pv2000_vdp_interrupt(int state)
{
	// only if it goes up
	if (state && !m_last_state)
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);

	m_last_state = state;

	/* Check if irq triggering from keyboard presses is enabled */
	if (m_keyb_column == 0x0f)
	{
		/* Check if a key is pressed */
		uint8_t key_pressed = m_keyboard[0]->read()
				| m_keyboard[1]->read()
				| m_keyboard[2]->read()
				| m_keyboard[3]->read()
				| m_keyboard[4]->read()
				| m_keyboard[5]->read()
				| m_keyboard[6]->read()
				| m_keyboard[7]->read()
				| m_keyboard[8]->read();

		if (key_pressed && m_key_pressed != key_pressed)
			m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);

		m_key_pressed = key_pressed;
	}
}



/* Machine Initialization */

void pv2000_state::machine_start()
{
	if (m_cart->exists())
		m_maincpu->space(AS_PROGRAM).install_read_handler(0xc000, 0xffff, read8sm_delegate(*m_cart, FUNC(generic_slot_device::read_rom)));
}

void pv2000_state::machine_reset()
{
	m_last_state = 0;
	m_key_pressed = 0;
	m_keyb_column = 0;

	m_maincpu->set_input_line_vector(INPUT_LINE_IRQ0, 0xff); // Z80
	memset(&memregion("maincpu")->base()[0x7000], 0xff, 0x1000);    // initialize RAM
}

DEVICE_IMAGE_LOAD_MEMBER(pv2000_state::cart_load)
{
	uint32_t size = m_cart->common_get_size("rom");

	if (size != 0x2000 && size != 0x4000)
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported cartridge size (must be 8K or 16K)");

	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

/* Machine Drivers */
void pv2000_state::pv2000(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, XTAL(7'159'090)/2); // 3.579545 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &pv2000_state::pv2000_map);
	m_maincpu->set_addrmap(AS_IO, &pv2000_state::pv2000_io_map);

	// video hardware
	tms9928a_device &vdp(TMS9928A(config, "tms9928a", XTAL(10'738'635)));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	vdp.int_callback().set(FUNC(pv2000_state::pv2000_vdp_interrupt));
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SN76489A(config, "sn76489a", XTAL(7'159'090)/2).add_route(ALL_OUTPUTS, "mono", 1.00); /* 3.579545 MHz */

	/* cassette */
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_DISABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	/* cartridge */
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "pv2000_cart", "bin,rom,col").set_device_load(FUNC(pv2000_state::cart_load));

	/* Software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("pv2000");
}



/* ROMs */
ROM_START (pv2000)
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "hn613128pc64.bin", 0x0000, 0x4000, CRC(8f31f297) SHA1(94b5f54dd7bce321e377fdaaf592acd3870cf621) )
ROM_END

} // anonymous namespace


/* System Drivers */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY   FULLNAME    FLAGS
CONS( 1983, pv2000, 0,      0,      pv2000,  pv2000, pv2000_state, empty_init, "Casio",  "PV-2000",  MACHINE_NOT_WORKING )



pv9234.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
/**************************************************************************************************

PowerVu D9234 STB (c) 1997 Scientific Atlanta

20-mar-2010 skeleton driver

References:
- http://www.vetrun.net/forums/showthread.php?t=395
- https://web.archive.org/web/20080203175218/http://www.growl.de/d9234/

TODO:
- everything, including PCB pictures and user manual;
- Probably shared with other PowerVu DVB-S STB models;
- Flash ROM is (2x? 4x?) AT29C256 according to an evasive pic;
- $3f000 area should be bootstrap code, inits UART from there?

Front Panel:
- On/Standby | Signal/Menu on the left;
- arrow keys with select in the middle, next to DVB logo;
- PowerVu Conditional Access slot, on the right;

Back Panel:
- CH3 / CH4 dip, HF modulator (US standard);
- Ant In and Tv Out VHF/UHF connectors, near composite audio/video jacks;
- Satellite LNB PWR +13/+19V 250mA;
- LNB PWR ON / OFF dip;
- AC IN, 100V-240V, 50/60Hz;
According to the Quick Setup Guide, can have following optional slots:
- S-Video Out;
- DE-9 Wideband Data and/or DA-25 Expansion Port;

===================================================================================================

This is the serial output to a terminal, used for debugging.
The boot process goes something like this:

Start

Config: 0x00001080 (Max Config: 00003C80)
MV 00000004.00000003
DL Avail
IOP Com. O.K. 00000004
Check CRC ...
CRC O.K.
Launch App
**************
* Ver 2.05 *
**************
Compiled by: FURLANO
Date & time: Nov 3 1997, 15:34:29
All printing enabled. Press space bar to toggle on/off.
Time stamping enabled. Press 't' to turn on/off.
Press 'o' to toggle printing of MPEG Xport error messages.

**************************************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/arm7/arm7.h"
#include "machine/ins8250.h"

#include "emupal.h"
#include "screen.h"


namespace {

class pv9234_state : public driver_device
{
public:
	pv9234_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_p_ram(*this, "p_ram")
		, m_maincpu(*this, "maincpu")
	{
	}

	void pv9234(machine_config &config);

private:
	void debug1_w(uint32_t data);

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void main_map(address_map &map) ATTR_COLD;

	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

	required_shared_ptr<uint32_t> m_p_ram;
	required_device<cpu_device> m_maincpu;
};

void pv9234_state::video_start()
{
}

uint32_t pv9234_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}


void pv9234_state::debug1_w(uint32_t data)
{
	uint8_t i,j;
	if (data)
	{
		for (i = 0; i < 4; i++)
		{
			j = (data & 0xff000000) >> 24;
			data <<= 8;
//          printf("%c",j); // this prints 'OFF' to the console.
			logerror("debug1=%02x %c\n",j,j);
		}
//      printf("\n");
	}
}

void pv9234_state::main_map(address_map &map)
{
	//map.unmap_value_high();
	// TODO: flash ROM
	map(0x00000000, 0x0007ffff).rom().region("maincpu", 0);
	// map(0x00000000, 0x00000033).w(FUNC(pv9234_state::)); something
	// map(0x00000044, 0x00000047).w(FUNC(pv9234_state::));
	// map(0x00000060, 0x0000006b).w(FUNC(pv9234_state::));
	// map(0x00007000, 0x00007003).w(FUNC(pv9234_state::));
	// map(0x00008000, 0x00008003).w(FUNC(pv9234_state::));
	map(0x00008000, 0x000080ff).unmaprw();
	map(0x00008014, 0x00008017).w(FUNC(pv9234_state::debug1_w));
	// map(0x00008020, 0x00008027).w(FUNC(pv9234_state::));
	map(0x000080c0, 0x000080df).rw("uart", FUNC(ns16550_device::ins8250_r), FUNC(ns16550_device::ins8250_w)).umask32(0x000000ff);
	map(0x000080cc, 0x000080cc).lw8(
		NAME([this] (offs_t offset, u8 data) {
			if (data)
			{
				//printf("%02x %c\n",data,data); // this prints 'Start' to the console.
				logerror("debug=%02x %c\n",data,data);
			}
		})
	);

	map(0x0003e000, 0x0003efff).ram().share("p_ram");
	map(0x00080000, 0x00087fff).mirror(0x78000).ram().share("share1");//mirror is a guess, writes a prg at 0xc0200 then it jumps at b0200 (!)
	map(0xe0000000, 0xe0007fff).mirror(0x0fff8000).ram().share("share1");
	map(0xffffff00, 0xffffffff).ram(); //i/o? stack ram?
}

/* Input ports */
static INPUT_PORTS_START( pv9234 )
INPUT_PORTS_END


void pv9234_state::machine_reset()
{
	int i;

	for(i=0;i<0x1000/4;i++)
		m_p_ram[i] = 0;
}

void pv9234_state::pv9234(machine_config &config)
{
	ARM7(config, m_maincpu, 4915000); // TODO: unknown type, VLSI branded?
	m_maincpu->set_addrmap(AS_PROGRAM, &pv9234_state::main_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	screen.set_screen_update(FUNC(pv9234_state::screen_update));
	screen.set_palette("palette");

	// TODO: wideband DE-9 port

	// TODO: actually NS16550A, uses DA-25 port for a non-standard 8P1 125k baud rate
	NS16550(config, "uart", 8_MHz_XTAL);
//  uart.out_tx_callback().set("serial", FUNC(rs232_port_device::write_txd));
//  uart.out_dtr_callback().set("serial", FUNC(rs232_port_device::write_dtr));
//  uart.out_rts_callback().set("serial", FUNC(rs232_port_device::write_rts));
//
//  rs232_port_device &serial(RS232_PORT(config, "serial", default_rs232_devices, "terminal"));
//  serial.rxd_handler().set("uart", FUNC(ns16550_device::rx_w));
//  serial.dcd_handler().set("uart", FUNC(ns16550_device::dcd_w));
//  serial.dsr_handler().set("uart", FUNC(ns16550_device::dsr_w));
//  serial.cts_handler().set("uart", FUNC(ns16550_device::cts_w));

	// TODO: has a Philips SAA-branded chip on the evasive PCB picture

	PALETTE(config, "palette", palette_device::MONOCHROME);
}

ROM_START( pv9234 )
	ROM_REGION32_LE( 0x80000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_BYTE( "u19.bin", 0x00000, 0x20000, CRC(1e06b0c8) SHA1(f8047f7127919e73675375578bb9fcc0eed2178e))
	ROM_LOAD16_BYTE( "u18.bin", 0x00001, 0x20000, CRC(924487dd) SHA1(fb1d7c9a813ded8c820589fa85ae72265a0427c7))
	ROM_LOAD16_BYTE( "u17.bin", 0x40000, 0x20000, CRC(cac03650) SHA1(edd8aec6fed886d47de39ed4e127de0a93250a45))
	ROM_LOAD16_BYTE( "u16.bin", 0x40001, 0x20000, CRC(bd07d545) SHA1(90a63af4ee82b0f7d0ed5f0e09569377f22dd98c))
ROM_END

} // anonymous namespace

// PowerVu D9223 Commercial Satellite Receiver
// PowerVu D9225 Headend Satellite Receiver
SYST( 1997, pv9234, 0,      0,      pv9234,  pv9234, pv9234_state, empty_init, "Scientific Atlanta", "PowerVu D9234 Business Satellite Receiver", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



pve500.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders: Felipe Sanches
/***************************************************************************

  Sony PVE-500 Editing Control Unit
  "A/B roll edit controller for professional video editing applications"

  Driver by Felipe Correa da Silva Sanches <juca@members.fsf.org>
  Technical info at https://www.garoa.net.br/wiki/PVE-500

  Notes:
  One can induce the self-diagnose by booting the device holding LEARN and P2-RESET buttons togheter
  With the default keyboard map, this can be done by holding keys L and S while pressing F3.
    (Don't forget to unlock the keyboard by using the UI TOGGLE key)

    This self-diagnose routine displays the value C817, which is the checksum value of the subcpu ROM
  and afterwards it displays the following message:

  SELFdIAG Error___ _F3 F3_CtC3c

  which means it detected an error in the CTC circuitry (it means we're emulating it wrong!)
  F3 is the coordinate of the subcpu EPROM chip in the PCB.

    According to the service manual, this error code means: "ICF3 CTC CH-3 counter operation failure (No interruption)"

  Known issues:
  There's still an annoying blinking in the 7-seg display.

  Changelog:

     2014 SEP 01 [Felipe Sanches]:
   * hooked-up MB8421 device (dual-port SRAM)

     2014 JUN 24 [Felipe Sanches]:
   * figured out the multiplexing signals for the 7-seg display

     2014 JUN 23 [Felipe Sanches]:
   * hooked-up the RS422 ports

   2014 JAN 14 [Felipe Sanches]:
   * Initial driver skeleton
*/

#include "emu.h"
#include "bus/rs232/rs232.h" /* actually meant to be RS422 ports */
#include "cpu/mb88xx/mb88xx.h"
#include "cpu/z80/tmpz84c015.h"
#include "machine/clock.h"
#include "machine/cxd1095.h"
#include "machine/eepromser.h"
#include "machine/mb8421.h"
#include "machine/z80sio.h"
#include "sound/beep.h"
#include "speaker.h"

#include "pve500.lh"

#define LOG_7SEG_DISPLAY_SIGNALS (1U << 1)

#define VERBOSE (0)
#include "logmacro.h"


namespace {

#define DEBUGGING_INDUCE_SELFDIAGNOSE 0

#define IO_EXPANDER_PORTA 0
#define IO_EXPANDER_PORTB 1
#define IO_EXPANDER_PORTC 2
#define IO_EXPANDER_PORTD 3
#define IO_EXPANDER_PORTE 4

class pve500_state : public driver_device
{
public:
	pve500_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_subcpu(*this, "subcpu")
		, m_cxdio(*this, "cxdio")
		, m_eeprom(*this, "eeprom")
		, m_buzzer(*this, "buzzer")
		, m_digits(*this, "digit%u", 0U)
	{ }

	void pve500(machine_config &config);

	void init_pve500();

private:
	void mb8421_intl(int state);
	void mb8421_intr(int state);
	void GPI_w(int state);
	void cxdio_reset_w(int state);
	void external_monitor_w(int state);

	uint8_t io_ky_r();
	void io_sc_w(uint8_t data);
	void io_le_w(uint8_t data);
	void io_ld_w(uint8_t data);
	void io_sel_w(uint8_t data);
	void eeprom_w(uint8_t data);
	uint8_t eeprom_r();
	void maincpu_io(address_map &map) ATTR_COLD;
	void maincpu_prg(address_map &map) ATTR_COLD;
	void subcpu_io(address_map &map) ATTR_COLD;
	void subcpu_prg(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	required_device<tmpz84c015_device> m_maincpu;
	required_device<tmpz84c015_device> m_subcpu;
	required_device<cxd1095_device> m_cxdio;
	required_device<eeprom_serial_er5911_device> m_eeprom;
	required_device<beep_device> m_buzzer;
	output_finder<27> m_digits;

	uint8_t io_SEL = 0, io_LD = 0, io_LE = 0, io_SC = 0, io_KY = 0;
	int LD_data[4]{};
};

void pve500_state::GPI_w(int state)
{
	/* TODO: Implement-me */
}

void pve500_state::cxdio_reset_w(int state)
{
	if (!state)
		m_cxdio->reset();
}

void pve500_state::external_monitor_w(int state)
{
	/* TODO: Implement-me */
}

static const z80_daisy_config maincpu_daisy_chain[] =
{
	{ "external_ctc" },
	{ "external_sio" },
	{ nullptr }
};


void pve500_state::maincpu_io(address_map &map)
{
	map(0x00, 0x03).mirror(0xff00).rw("external_sio", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
	map(0x08, 0x0B).mirror(0xff00).rw("external_ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
}

void pve500_state::maincpu_prg(address_map &map)
{
	map(0x0000, 0xbfff).rom(); // ICB7: 48kbytes EPROM
	map(0xc000, 0xdfff).ram(); // ICD6: 8kbytes of RAM
	map(0xe000, 0xe7ff).mirror(0x1800).rw("mb8421", FUNC(mb8421_device::left_r), FUNC(mb8421_device::left_w));
}

void pve500_state::subcpu_io(address_map &map)
{
}

void pve500_state::subcpu_prg(address_map &map)
{
	map(0x0000, 0x7fff).rom(); // ICG5: 32kbytes EPROM
	map(0x8000, 0x8007).mirror(0x3ff8).rw(m_cxdio, FUNC(cxd1095_device::read), FUNC(cxd1095_device::write));
	map(0xc000, 0xc7ff).mirror(0x3800).rw("mb8421", FUNC(mb8421_device::right_r), FUNC(mb8421_device::right_w));
}

void pve500_state::init_pve500()
{
}

static INPUT_PORTS_START( pve500 )
	PORT_START("SCAN0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TRANS")       PORT_CODE(KEYCODE_5)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A/B")         PORT_CODE(KEYCODE_4)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("FROM TO")     PORT_CODE(KEYCODE_3)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P2")          PORT_CODE(KEYCODE_2)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P1")          PORT_CODE(KEYCODE_1)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENTRY")       PORT_CODE(KEYCODE_SPACE)

	PORT_START("SCAN1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ALL STOP")    PORT_CODE(KEYCODE_M)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LAST EDIT")   PORT_CODE(KEYCODE_I)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("AUDIO SPLIT") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A2")          PORT_CODE(KEYCODE_9)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ASMBL")       PORT_CODE(KEYCODE_6)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V")           PORT_CODE(KEYCODE_7)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A1")          PORT_CODE(KEYCODE_8)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENTRY")       PORT_CODE(KEYCODE_SPACE)

	PORT_START("SCAN2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RVW/JUMP")    PORT_CODE(KEYCODE_N)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("AUTO EDIT")   PORT_CODE(KEYCODE_B)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("PREVIEW")     PORT_CODE(KEYCODE_V)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P-FF")        PORT_CODE(KEYCODE_R)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P-REW")       PORT_CODE(KEYCODE_E)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P-STILL")     PORT_CODE(KEYCODE_W)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P-PLAY")      PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENTRY")       PORT_CODE(KEYCODE_SPACE)

	PORT_START("SCAN3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R-OUT")       PORT_CODE(KEYCODE_K)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R-IN")        PORT_CODE(KEYCODE_J)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("GO TO")       PORT_CODE(KEYCODE_H)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P-OUT")       PORT_CODE(KEYCODE_G)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P-IN")        PORT_CODE(KEYCODE_F)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TRIM+")       PORT_CODE(KEYCODE_U)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TRIM-")       PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENTRY")       PORT_CODE(KEYCODE_SPACE)

	PORT_START("SCAN4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R-FF")        PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R-REW")       PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R-STILL")     PORT_CODE(KEYCODE_P)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R-PLAY")      PORT_CODE(KEYCODE_O)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("EDIT")        PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("REC")         PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENTRY")       PORT_CODE(KEYCODE_SPACE)

	PORT_START("SCAN5")
		PORT_DIPNAME( 0x03, 0x02, "R-EDIT REF" )
		PORT_DIPSETTING(    0x02, "TC" )
		PORT_DIPSETTING(    0x00, "RTC" )
		PORT_DIPSETTING(    0x01, "CTL" )

		PORT_DIPNAME( 0x0C, 0x08, "P2-EDIT REF" )
		PORT_DIPSETTING(    0x08, "TC" )
		PORT_DIPSETTING(    0x00, "RTC" )
		PORT_DIPSETTING(    0x04, "CTL" )

		PORT_DIPNAME( 0x30, 0x20, "P1-EDIT REF" )
		PORT_DIPSETTING(    0x20, "TC" )
		PORT_DIPSETTING(    0x00, "RTC" )
		PORT_DIPSETTING(    0x10, "CTL" )

	PORT_START("SCAN6")
		PORT_DIPNAME( 0x03, 0x02, "SYNCHRO" )
		PORT_DIPSETTING(    0x02, "ON/CF" )
		PORT_DIPSETTING(    0x00, "ON" )
		PORT_DIPSETTING(    0x01, "OFF" )

		PORT_DIPNAME( 0x0C, 0x08, "PREROLL" )
		PORT_DIPSETTING(    0x08, "7" )
		PORT_DIPSETTING(    0x00, "5" )
		PORT_DIPSETTING(    0x04, "3" )

	PORT_START("SCAN7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TOTAL")       PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LEARN")       PORT_CODE(KEYCODE_L)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TRANS-1F")    PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TRANS-10F")   PORT_CODE(KEYCODE_X)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TRANS-100F")  PORT_CODE(KEYCODE_C)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R-RESET")     PORT_CODE(KEYCODE_A)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P2-RESET")    PORT_CODE(KEYCODE_S)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P1-RESET")    PORT_CODE(KEYCODE_D)
INPUT_PORTS_END

void pve500_state::machine_start()
{
	io_LD = 0;
	io_SC = 0;
	io_LE = 0;
	io_SEL = 0;
	io_KY = 0;
	m_digits.resolve();
}

void pve500_state::machine_reset()
{
	/* Setup beep */
	m_buzzer->set_state(0);
}

void pve500_state::mb8421_intl(int state)
{
	// shared ram interrupt request from subcpu side
	m_maincpu->trg1(state);
}

void pve500_state::mb8421_intr(int state)
{
	// shared ram interrupt request from maincpu side
	m_subcpu->trg1(state);
}

uint8_t pve500_state::eeprom_r()
{
	return (m_eeprom->ready_read() << 1) | m_eeprom->do_read();
}

void pve500_state::eeprom_w(uint8_t data)
{
	m_eeprom->di_write( (data & (1 << 2)) ? ASSERT_LINE : CLEAR_LINE);
	m_eeprom->clk_write( (data & (1 << 3)) ? ASSERT_LINE : CLEAR_LINE);
	m_eeprom->cs_write( (data & (1 << 4)) ? ASSERT_LINE : CLEAR_LINE);
}

uint8_t pve500_state::io_ky_r()
{
	io_KY = 0x00;
	if (!BIT(io_SC, 0)) io_KY |= ioport("SCAN0")->read();
	if (!BIT(io_SC, 1)) io_KY |= ioport("SCAN1")->read();
	if (!BIT(io_SC, 2)) io_KY |= ioport("SCAN2")->read();
	if (!BIT(io_SC, 3)) io_KY |= ioport("SCAN3")->read();
	if (!BIT(io_SC, 4)) io_KY |= ioport("SCAN4")->read();
	if (!BIT(io_SC, 5)) io_KY |= ioport("SCAN5")->read();
	if (!BIT(io_SC, 6)) io_KY |= ioport("SCAN6")->read();
	if (!BIT(io_SC, 7)) io_KY |= ioport("SCAN7")->read();
#if DEBUGGING_INDUCE_SELFDIAGNOSE
	io_KY = 0x42; //according to procedure described in the service manual
#endif
	return io_KY;
}

void pve500_state::io_sc_w(uint8_t data)
{
	const int swap[4] = {2,1,0,3};

	LOGMASKED(LOG_7SEG_DISPLAY_SIGNALS, "CXD1095 PORTA (io_SC=%02X)\n", data);
	io_SC = data;

	for (int j=0; j<8; j++){
		if (!BIT(io_SC,j)){
			int digits = (j < 3) ? 4 : 3;
			for (int i = 0; i < digits; i++)
			{
				assert(8*swap[i] + j < 27);
				m_digits[8*swap[i] + j] = LD_data[i];
			}
		}
	}
}

void pve500_state::io_le_w(uint8_t data)
{
	LOGMASKED(LOG_7SEG_DISPLAY_SIGNALS, "CXD1095 PORTB (io_LE=%02X)\n", data);
	io_LE = data;
}

void pve500_state::io_ld_w(uint8_t data)
{
	LOGMASKED(LOG_7SEG_DISPLAY_SIGNALS, "CXD1095 PORTD (io_LD=%02X)\n", data);
	io_LD = data;
}

void pve500_state::io_sel_w(uint8_t data)
{
	LOGMASKED(LOG_7SEG_DISPLAY_SIGNALS, "CXD1095 PORTE (io_SEL=%02X)\n", data);
	io_SEL = data;
	for (int i=0; i<4; i++){
		if (BIT(io_SEL, i)){
			LD_data[i] = 0x7F & bitswap<8>(io_LD ^ 0xFF, 7, 0, 1, 2, 3, 4, 5, 6);
		}
	}
}

void pve500_state::pve500(machine_config &config)
{
	/* Main CPU */
	TMPZ84C015(config, m_maincpu, 12_MHz_XTAL / 2); // TMPZ84C015BF-6
	m_maincpu->set_addrmap(AS_PROGRAM, &pve500_state::maincpu_prg);
	m_maincpu->set_addrmap(AS_IO, &pve500_state::maincpu_io);
	m_maincpu->set_daisy_config(maincpu_daisy_chain);
	m_maincpu->out_dtra_callback().set(FUNC(pve500_state::GPI_w));
	m_maincpu->out_dtrb_callback().set(m_buzzer, FUNC(beep_device::set_state)).invert();
	m_maincpu->out_txda_callback().set("recorder", FUNC(rs232_port_device::write_txd));
	m_maincpu->out_txdb_callback().set("player1", FUNC(rs232_port_device::write_txd));

	z80ctc_device& ctc(Z80CTC(config, "external_ctc", 12_MHz_XTAL / 2));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80sio_device& sio(Z80SIO(config, "external_sio", 12_MHz_XTAL / 2)); // TMPZ84C40AP-8
	sio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	sio.out_txda_callback().set("player2", FUNC(rs232_port_device::write_txd));
	sio.out_txdb_callback().set("edl_inout", FUNC(rs232_port_device::write_txd));

	/* Secondary CPU */
	TMPZ84C015(config, m_subcpu, 12_MHz_XTAL / 2); /* TMPZ84C015BF-6 */
	m_subcpu->set_addrmap(AS_PROGRAM, &pve500_state::subcpu_prg);
	m_subcpu->set_addrmap(AS_IO, &pve500_state::subcpu_io);
	m_subcpu->out_dtra_callback().set(FUNC(pve500_state::cxdio_reset_w));
	m_subcpu->out_dtrb_callback().set(FUNC(pve500_state::external_monitor_w));
	m_subcpu->out_txda_callback().set("switcher", FUNC(rs232_port_device::write_txd));
	m_subcpu->out_txdb_callback().set("serial_mixer", FUNC(rs232_port_device::write_txd));

	// PIO callbacks
	m_subcpu->in_pa_callback().set(FUNC(pve500_state::eeprom_r));
	m_subcpu->out_pa_callback().set(FUNC(pve500_state::eeprom_w));

	// ICG3: I/O Expander
	CXD1095(config, m_cxdio);
	m_cxdio->out_porta_cb().set(FUNC(pve500_state::io_sc_w));
	m_cxdio->out_portb_cb().set(FUNC(pve500_state::io_le_w));
	m_cxdio->in_portc_cb().set(FUNC(pve500_state::io_ky_r));
	m_cxdio->out_portd_cb().set(FUNC(pve500_state::io_ld_w));
	m_cxdio->out_porte_cb().set(FUNC(pve500_state::io_sel_w));

	/* Search Dial MCUs */
	MB88201(config, "dial_mcu_left", 4_MHz_XTAL).set_disable(); /* PLAYER DIAL MCU */
	MB88201(config, "dial_mcu_right", 4_MHz_XTAL).set_disable(); /* RECORDER DIAL MCU */

	/* Serial EEPROM (128 bytes, 8-bit data organization) */
	/* The EEPROM stores the setup data */
	EEPROM_MSM16911_8BIT(config, "eeprom");

	/* FIX-ME: These are actually RS422 ports (except EDL IN/OUT which is indeed an RS232 port)*/
	rs232_port_device &recorder(RS232_PORT(config, "recorder", default_rs232_devices, nullptr));
	recorder.rxd_handler().set(m_maincpu, FUNC(tmpz84c015_device::rxa_w));

	rs232_port_device &player1(RS232_PORT(config, "player1", default_rs232_devices, nullptr));
	player1.rxd_handler().set(m_maincpu, FUNC(tmpz84c015_device::rxb_w));

	rs232_port_device &player2(RS232_PORT(config, "player2", default_rs232_devices, nullptr));
	player2.rxd_handler().set("external_sio", FUNC(z80sio_device::rxa_w));

	rs232_port_device &edl_inout(RS232_PORT(config, "edl_inout", default_rs232_devices, nullptr));
	edl_inout.rxd_handler().set("external_sio", FUNC(z80sio_device::rxb_w));

	rs232_port_device &switcher(RS232_PORT(config, "switcher", default_rs232_devices, nullptr));
	switcher.rxd_handler().set(m_subcpu, FUNC(tmpz84c015_device::rxa_w));

	rs232_port_device &serial_mixer(RS232_PORT(config, "serial_mixer", default_rs232_devices, nullptr));
	serial_mixer.rxd_handler().set(m_subcpu, FUNC(tmpz84c015_device::rxb_w));

	clock_device &clk1(CLOCK(config, "clk1", 12_MHz_XTAL / 20));
	clk1.signal_handler().set(m_maincpu, FUNC(tmpz84c015_device::rxca_w));
	clk1.signal_handler().append(m_maincpu, FUNC(tmpz84c015_device::txca_w));
	clk1.signal_handler().append(m_maincpu, FUNC(tmpz84c015_device::rxcb_w));
	clk1.signal_handler().append(m_maincpu, FUNC(tmpz84c015_device::txcb_w));
	clk1.signal_handler().append(m_subcpu, FUNC(tmpz84c015_device::rxca_w));
	clk1.signal_handler().append(m_subcpu, FUNC(tmpz84c015_device::txca_w));
	clk1.signal_handler().append(m_subcpu, FUNC(tmpz84c015_device::rxcb_w));
	clk1.signal_handler().append(m_subcpu, FUNC(tmpz84c015_device::txcb_w));

	/* ICF5: 2kbytes of RAM shared between the two CPUs (dual-port RAM)*/
	mb8421_device &mb8421(MB8421(config, "mb8421"));
	mb8421.intl_callback().set(FUNC(pve500_state::mb8421_intl));
	mb8421.intr_callback().set(FUNC(pve500_state::mb8421_intr));

	/* video hardware */
	config.set_default_layout(layout_pve500);

	/* audio hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, "buzzer", 12_MHz_XTAL / 3200).add_route(ALL_OUTPUTS, "mono", 0.05); // 3.75 kHz CLK2 coming out of IC D4 (frequency divider circuitry)
}

ROM_START( pve500 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("pve500.icb7",  0x00000, 0x10000, CRC(1036709c) SHA1(207d6fcad5c2f081a138184060ce7bd02736965b) ) //48kbyte main-cpu program + 16kbyte of unreachable memory

	ROM_REGION( 0x8000, "subcpu", 0 )
	ROM_LOAD("pve500.icg5",  0x00000, 0x8000, CRC(28cca60a) SHA1(308d70062653769250327ede7a4e1a8a76fc9ab9) ) //32kbyte sub-cpu program

	ROM_REGION( 0x200, "dial_mcu_left", 0 ) /* PLAYER DIAL MCU */
	ROM_LOAD( "pve500.icd3", 0x0000, 0x0200, NO_DUMP )

	ROM_REGION( 0x200, "dial_mcu_right", 0 ) /* RECORDER DIAL MCU */
	ROM_LOAD( "pve500.icc3", 0x0000, 0x0200, NO_DUMP )

	ROM_REGION( 0x80, "eeprom", 0 ) /* The EEPROM stores the setup data */
	ROM_LOAD( "pve500.ice3", 0x0000, 0x080, NO_DUMP )
ROM_END

} // anonymous namespace


//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT         COMPANY  FULLNAME   FLAGS
COMP( 1995, pve500, 0,      0,      pve500,  pve500, pve500_state, init_pve500, "Sony",  "PVE-500", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS)



pwp14.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
// thanks-to:FozzTexx
/***************************************************************************

    Smith Corona PWP System 14

    08/08/2021 Skeleton driver.

    https://twitter.com/FozzTexx/status/1424043157264617478

    XTAL :  7.938 MHz
           14.364 MHz
            6.000 MHz

    FDC - WD2793A
    2 * AM9128 (2048x8 SRAM)
    NEC D4364 (8192x8 SRAM)
    2 * MT4067-12 (64Kx4 DRAM)
    CRT5037 - SMC 8621

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/ram.h"
#include "video/tms9927.h"
#include "machine/wd_fdc.h"
#include "imagedev/floppy.h"

#include "emupal.h"
#include "screen.h"


namespace {

class pwp14_state : public driver_device
{
public:
	pwp14_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_bank1(*this, "bank1")
		, m_bank2(*this, "bank2")
		, m_bank3(*this, "bank3")
		, m_bank4(*this, "bank4")
		, m_ram(*this, RAM_TAG)
		, m_rom(*this, "maincpu")
		, m_crtc(*this, "crtc")
		, m_palette(*this, "palette")
		, m_chargen(*this, "chargen")
		, m_fdc(*this, "fdc")
		, m_bank_reg(0)
	{ }

	void pwp14(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	uint32_t screen_update_pwp14(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void pwp14_io(address_map &map) ATTR_COLD;
	void pwp14_mem(address_map &map) ATTR_COLD;
	void bankswitch_w(offs_t offset, uint8_t data);
	void set_bank();
	u8 typewriter_r(offs_t offset);
	void ram_memory_w(offs_t offset, uint8_t data);

	required_device<cpu_device> m_maincpu;
	required_memory_bank m_bank1;
	required_memory_bank m_bank2;
	required_memory_bank m_bank3;
	required_memory_bank m_bank4;
	required_device<ram_device> m_ram;
	required_region_ptr<u8> m_rom;
	required_device<crt5037_device> m_crtc;
	required_device<palette_device> m_palette;
	required_region_ptr<uint8_t> m_chargen;
	required_device<wd2793_device> m_fdc;
	uint8_t m_bank_reg;
};


void pwp14_state::pwp14_mem(address_map &map)
{
	map(0x0000, 0x3fff).bankrw("bank1");
	map(0x4000, 0x7fff).bankrw("bank2");
	map(0x8000, 0xbfff).bankrw("bank3");
	map(0xc000, 0xffff).bankrw("bank4");
}

void pwp14_state::set_bank()
{
	uint8_t *ram = m_ram->pointer();
	address_space &space = m_maincpu->space(AS_PROGRAM);

	space.install_write_bank(0x0000, 0x3fff, m_bank1);
	space.install_write_bank(0x4000, 0x7fff, m_bank2);
	space.install_write_bank(0x8000, 0xbfff, m_bank3);
	space.install_write_bank(0xc000, 0xffff, m_bank4);
	if (m_bank_reg & 1) {
		m_bank1->set_base(ram + 0x0000);
	} else {
		space.install_write_handler(0x0000, 0x3fff, write8sm_delegate(*this, FUNC(pwp14_state::ram_memory_w)));
		m_bank1->set_base(m_rom + 0x0000);
	}
	if (m_bank_reg & 2) {
		m_bank2->set_base(ram + 0x4000);
	} else {
		space.install_write_handler(0x4000, 0x7fff, write8sm_delegate(*this, FUNC(pwp14_state::ram_memory_w)));
		m_bank2->set_base(m_rom + 0x4000);
	}
	if (m_bank_reg & 3) {
		m_bank3->set_base(ram + 0x8000);
	} else {
		space.unmap_write(0x8000, 0xbfff);
		m_bank3->set_base(m_rom + 0x8000);
	}
	if (m_bank_reg & 4) {
		m_bank4->set_base(ram + 0xc000);
	} else {
		space.unmap_write(0xc000, 0xffff);
		m_bank4->set_base(m_rom + 0xc000);
	}
}

void pwp14_state::ram_memory_w(offs_t offset, uint8_t data)
{
	uint8_t *ram = m_ram->pointer();
	ram[offset] = data;
}

void pwp14_state::bankswitch_w(offs_t offset, uint8_t data)
{
	m_bank_reg = data;
	logerror("bankswitch_w %02x at %04X\n", data, m_maincpu->pc());
	set_bank();
}

u8 pwp14_state::typewriter_r(offs_t offset)
{
	return 0x00;//0xff;
}

void pwp14_state::pwp14_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x0f).rw("crtc", FUNC(crt5037_device::read), FUNC(crt5037_device::write));
	map(0x20, 0x23).rw(m_fdc, FUNC(wd2793_device::read), FUNC(wd2793_device::write));  // FDC ???
	//map(0x40, 0x40) // ???
	map(0x60, 0x60).rw(FUNC(pwp14_state::typewriter_r), FUNC(pwp14_state::bankswitch_w));
}

/* Input ports */
static INPUT_PORTS_START( pwp14 )
INPUT_PORTS_END


void pwp14_state::machine_reset()
{
	m_bank_reg = 0;
	set_bank();
}

void pwp14_state::machine_start()
{
}

uint32_t pwp14_state::screen_update_pwp14(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	rgb_t const *const pen = m_palette->palette()->entry_list_raw();
	uint8_t *vram = m_ram->pointer() + 0x10000;
	rectangle cursor;
	m_crtc->cursor_bounds(cursor);

	for (int y = 0; y < 24; y++)
	{
		for (int ra = 0; ra < 8; ra++)
		{
			for (int x = 0; x < 80; x++)
			{
				uint8_t chr = vram[(y * 80) + x  + 0];
				uint8_t data = m_chargen[(chr << 3) | ra];

				//if (cursor.contains(x * 8, y * 8))
				//  data ^= 0xff;

				// draw 8 pixels of the char
				for (int i = 0; i < 8; i++)
					bitmap.pix(y * 8 + ra, x * 8 + i) = pen[BIT(data, 7 - i)];
			}
		}
	}

	return 0;
}

/* F4 Character Displayer */
static const gfx_layout pwp14_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	1024,                   /* 1024 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 2 x 8 bytes */
};

static GFXDECODE_START( gfx_pwp14 )
	GFXDECODE_ENTRY( "chargen", 0x0000, pwp14_charlayout, 0, 1 )
GFXDECODE_END

void pwp14_state::pwp14(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(6'000'000)); // Z8400BPS
	m_maincpu->set_addrmap(AS_PROGRAM, &pwp14_state::pwp14_mem);
	m_maincpu->set_addrmap(AS_IO, &pwp14_state::pwp14_io);


	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 480);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(pwp14_state::screen_update_pwp14));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_pwp14);

	PALETTE(config, "palette", palette_device::MONOCHROME);
	/* internal ram */
	RAM(config, m_ram).set_default_size("76K").set_default_value(0x00); // 64K DRAM + 2*2K SRAM (VRAM) + 8K SRAM

	CRT5037(config, m_crtc, 14'364'000 / 8); // unknown clock
	m_crtc->set_char_width(8);
	m_crtc->set_screen("screen");

	WD2793(config, m_fdc, XTAL(2'000'000)); // Need proper xtal used
}

/* ROM definition */
ROM_START( pwp14 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "750524.bin", 0x0000, 0x8000, CRC(775b0e1a) SHA1(d546ef2bd4e09b2f182ffe0e393895ebad613fcc))
	ROM_LOAD( "750525.bin", 0x8000, 0x8000, CRC(673e20b5) SHA1(66e3a98cbc5969e6c64a25e39fb0df7f741e6345))

	ROM_REGION(0x2000, "chargen",0)
	ROM_LOAD( "750504.bin", 0x0000, 0x2000, CRC(b9062df6) SHA1(e79044765093b1d1954254d4a839a9e443d624d6))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY         FULLNAME  FLAGS
COMP( 1986, pwp14, 0,      0,      pwp14,   pwp14, pwp14_state, empty_init, "Smith Corona", "PWP System 14",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



pwrview.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Carl, Al Kossow
/***************************************************************************

    Compugraphic MCS Powerview 10

***************************************************************************/

#include "emu.h"
#include "cpu/i86/i186.h"
#include "imagedev/floppy.h"
#include "machine/upd765.h"
#include "machine/i8251.h"
#include "machine/z80sio.h"
#include "machine/pit8253.h"
#include "machine/bankdev.h"
#include "screen.h"
#include "video/mc6845.h"
#include "bus/rs232/rs232.h"


namespace {

class pwrview_state : public driver_device
{
public:
	pwrview_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_pit(*this, "pit"),
		m_uart(*this, "uart"),
		m_sio(*this, "sio"),
		m_fdc(*this, "fdc"),
		m_bios(*this, "bios"),
		m_ram(*this, "ram"),
		m_biosbank(*this, "bios_bank"),
		m_vram(64*1024)
	{ }

	void pwrview(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	TIMER_CALLBACK_MEMBER(update_tmr0);
	TIMER_CALLBACK_MEMBER(update_kbd);

	u16 bank0_r(offs_t offset);
	void bank0_w(offs_t offset, u16 data, u16 mem_mask = ~0);
	u8 unk1_r();
	void unk1_w(u8 data);
	u8 unk2_r();
	void unk2_w(u8 data);
	u8 unk3_r(offs_t offset);
	void unk3_w(offs_t offset, u8 data);
	u8 unk4_r(offs_t offset);
	void unk4_w(offs_t offset, u8 data);
	u8 led_r(offs_t offset);
	void led_w(offs_t offset, u8 data);
	u8 pitclock_r();
	u16 nmiio_r(offs_t offset);
	void nmiio_w(offs_t offset, u16 data);
	void nmimem_w(offs_t offset, u16 data);
	u16 vram1_r();
	void vram1_w(offs_t offset, u16 data, u16 mem_mask);
	u16 vram2_r();
	void vram2_w(offs_t offset, u16 data, u16 mem_mask);
	u16 fbios_r(offs_t offset);
	u8 rotary_r();
	u8 err_r();
	MC6845_UPDATE_ROW(update_row);

	void bios_bank(address_map &map) ATTR_COLD;
	void pwrview_fetch_map(address_map &map) ATTR_COLD;
	void pwrview_io(address_map &map) ATTR_COLD;
	void pwrview_map(address_map &map) ATTR_COLD;

	required_device<i80186_cpu_device> m_maincpu;
	required_device<pit8253_device> m_pit;
	required_device<i8251_device> m_uart;
	required_device<z80sio_device> m_sio;
	required_device<upd765a_device> m_fdc;
	required_memory_region m_bios;
	required_shared_ptr<u16> m_ram;
	required_device<address_map_bank_device> m_biosbank;
	std::vector<u16> m_vram;
	u8 m_leds[2];
	u8 m_switch;
	u8 m_c001;
	u8 m_c009;
	u8 m_c280;
	u8 m_c080;
	u8 m_errcode;
	u16 m_vramwin[2];
	bool m_dtr, m_rtsa, m_rtsb;
	bool m_rts;
	bool m_enable_fdc;
	emu_timer *m_tmr0ext;
	emu_timer *m_tmrkbd;
};

void pwrview_state::machine_start()
{
	save_item(NAME(m_vram));
	m_tmr0ext = timer_alloc(FUNC(pwrview_state::update_tmr0), this);
	m_tmrkbd = timer_alloc(FUNC(pwrview_state::update_kbd), this);
	membank("vram1")->configure_entries(0, 0x400, &m_vram[0], 0x80);
	membank("vram2")->configure_entries(0, 0x400, &m_vram[0], 0x80);
}

void pwrview_state::machine_reset()
{
	m_leds[0] = m_leds[1] = 0;
	m_switch = 0xe0;
	m_c001 = m_c009 = m_c080 = m_c280 = 0;
	m_errcode = 0x31;
	membank("vram1")->set_entry(0);
	membank("vram2")->set_entry(0);
	m_vramwin[0] = m_vramwin[1] = 0;
	m_biosbank->set_bank(0);
	m_uart->write_cts(0);
	m_tmrkbd->adjust(attotime::from_hz(9600*16), 0, attotime::from_hz(9600*16)); // kbd baud is guess
	m_enable_fdc = false;
	m_fdc->set_floppy(m_fdc->subdevice<floppy_connector>("0")->get_device());
}

TIMER_CALLBACK_MEMBER(pwrview_state::update_tmr0)
{
	m_maincpu->tmrin0_w(ASSERT_LINE);
	m_maincpu->tmrin0_w(CLEAR_LINE);
}

TIMER_CALLBACK_MEMBER(pwrview_state::update_kbd)
{
	m_uart->write_rxc(ASSERT_LINE);
	m_uart->write_txc(ASSERT_LINE);
	m_uart->write_rxc(CLEAR_LINE);
	m_uart->write_txc(CLEAR_LINE);
}

MC6845_UPDATE_ROW(pwrview_state::update_row)
{
	for(int c = 0; c < x_count; c++)
	{
		for(int p = 0; p < 62; p++)
		{
			int x = c * 62 + p;
			rgb_t pix = BIT(m_vram[(y * 64) + (x / 16)], x & 15) ? rgb_t::white() : rgb_t::black();
			bitmap.pix(y, x) = pix;
		}
	}
}

u8 pwrview_state::rotary_r()
{
	return ~m_switch;
}

u8 pwrview_state::err_r()
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
	return m_errcode;
}

u16 pwrview_state::bank0_r(offs_t offset)
{
	if(m_c001 & 2)
		return m_ram[offset];
	else
		return m_bios->as_u16(offset);
}

void pwrview_state::bank0_w(offs_t offset, u16 data, u16 mem_mask)
{
	if(m_c001 & 2)
		COMBINE_DATA(&m_ram[offset]);
}

u16 pwrview_state::nmiio_r(offs_t offset)
{
	logerror("%s: io nmi at %04x\n",machine().describe_context(), offset*2);
	m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
	return 0xff;
}

void pwrview_state::nmiio_w(offs_t offset, u16 data)
{
	logerror("%s: io nmi at %04x\n",machine().describe_context(), offset*2);
	m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
	switch(offset) // TODO: some connection with faulting address?
	{
		case 0:
			m_errcode = 0xaa;
			break;
		case 0xc00b / 2:
			m_errcode = 0xb2;
			break;
	}
}

void pwrview_state::nmimem_w(offs_t offset, u16 data)
{
	logerror("%s: mem nmi at %05x\n",machine().describe_context(), ((offset & 0x7fff) * 2) + 0xf8000);
	m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
	switch(((offset & 0x7fff) * 2) + 0x8000) // TODO: some connection with faulting address?
	{
		case 0x82e4:
			m_errcode = 0xae;
			break;
		case 0xbe80:
			m_errcode = 0xa6;
			break;
		case 0xbefc:
			m_errcode = 0xb6;
			break;
	}
}

u16 pwrview_state::fbios_r(offs_t offset)
{
	switch(m_c009 & 0xc)
	{
		case 0x0:
		case 0x4:
			return m_bios->as_u16(offset);
		case 0x8:
			return m_ram[offset + 0xf8000/2];
		case 0xc:
			return 0;
	}
	return 0;
}

u16 pwrview_state::vram1_r()
{
	return m_vramwin[0];
}

void pwrview_state::vram1_w(offs_t offset, u16 data, u16 mem_mask)
{
	data &= 0x3ff;
	COMBINE_DATA(&m_vramwin[0]);
	membank("vram1")->set_entry(m_vramwin[0]);
}

u16 pwrview_state::vram2_r()
{
	return m_vramwin[1];
}

void pwrview_state::vram2_w(offs_t offset, u16 data, u16 mem_mask)
{
	data &= 0x3ff;
	COMBINE_DATA(&m_vramwin[1]);
	membank("vram2")->set_entry(m_vramwin[1]);
}

u8 pwrview_state::unk1_r()
{
	return m_c001;
}

void pwrview_state::unk1_w(u8 data)
{
	m_c001 = data;
}

u8 pwrview_state::unk2_r()
{
	return m_c009;
}

void pwrview_state::unk2_w(u8 data)
{
	if(BIT(data, 6))
	{
		m_maincpu->drq0_w(1);
		m_maincpu->drq1_w(1);
	}
	else
	{
		m_maincpu->drq0_w(0);
		m_maincpu->drq1_w(0);
	}
	if(!BIT(m_c080, 7))
	{
		if(BIT(data, 4))
			m_tmr0ext->adjust(attotime::from_hz(33500), 0, attotime::from_hz(33500)); //refresh?
		else
			m_tmr0ext->adjust(attotime::never);
	}

	m_biosbank->set_bank((data >> 2) & 3);
	m_c009 = data;
}

u8 pwrview_state::unk3_r(offs_t offset)
{
	u8 ret = 0;
	switch(offset)
	{
		case 0:
			ret = m_c280;
			if(BIT(m_c280, 4))
				m_c280 &= ~0x10;
			break;
		case 2:
			ret = (m_rts ? 0 : 0x40) | (m_dtr ? 0 : 0x80) | 0x20;
			break;
		case 3:
			ret = m_sio->m1_r();
			break;
	}
	return ret;
}

void pwrview_state::unk3_w(offs_t offset, u8 data)
{
	switch(offset)
	{
		case 0:
			m_c280 = data;
			m_pit->set_clockin(0, BIT(data, 7) ? 1000000 : 0);
			m_pit->set_clockin(1, BIT(data, 6) ? 1000000 : 0);
			m_pit->set_clockin(2, BIT(data, 5) ? 1000000 : 0);
			if(BIT(data, 2))
			{
				if(!BIT(data, 6))
					m_pit->set_clockin(1, 2000000);
				if(!BIT(data, 7))
					m_pit->set_clockin(2, 2000000);
			}
			else
			{
				if(!BIT(data, 6))
					m_pit->set_clockin(1, 0);
				if(!BIT(data, 7))
					m_pit->set_clockin(2, 0);
			}
			break;
		case 1:
			if(BIT(data, 4))
			{
				m_enable_fdc = true;
				m_fdc->soft_reset();
			}
			else
				m_enable_fdc = false;
			break;
	}
}

u8 pwrview_state::unk4_r(offs_t offset)
{
	switch(offset)
	{
		case 0:
			return m_c080;
	}
	return 0;
}

void pwrview_state::unk4_w(offs_t offset, u8 data)
{
	switch(offset)
	{
		case 0:
			m_c080 = data;
			if(!BIT(data, 7))
			{
				if(BIT(m_c009, 4))
					m_tmr0ext->adjust(attotime::from_hz(33500), 0, attotime::from_hz(33500));
				else
					m_tmr0ext->adjust(attotime::never);
				return;
			}
			switch(data & 7) // this is all hand tuned to match the expected ratio with the pit clock
			{
				case 2:
					m_tmr0ext->adjust(attotime::from_hz(31500), 0, attotime::from_hz(31500)); // hfreq?
					break;
				case 3:
					m_tmr0ext->adjust(attotime::from_hz(60), 0, attotime::from_hz(60)); // vfreq?
					break;
				case 4:
					m_tmr0ext->adjust(attotime::from_hz(500000), 0, attotime::from_hz(500000)); // pixelclock?
					break;
				case 0:
					if(m_maincpu->space(AS_PROGRAM).read_byte(0xfbe00) == 0xff) // HACK: this appears to be the vram bank, are the outputed pixels clocking the timer?
						m_tmr0ext->adjust(attotime::from_hz(31500), 0, attotime::from_hz(31500));
					else
						m_tmr0ext->adjust(attotime::never);
					break;
			}
	}
}

u8 pwrview_state::led_r(offs_t offset)
{
	return m_leds[offset];
}

void pwrview_state::led_w(offs_t offset, u8 data)
{
	std::function<char (u8)> xlate = [](u8 val) -> char {
		const u8 segxlat[] = { 0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x98, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e };
		const char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
		val |= 0x80;
		if(val == 0xff)
			return ' ';
		for(int i = 0; i < 16; i++)
		{
			if(val == segxlat[i])
				return hex[i];
		}
		return '?';
	};
	m_leds[offset] = data;
	if(offset == 1)
	{
		logerror("%c%c%c%c\n", m_leds[1] & 0x80 ? ' ' : '.', xlate(m_leds[1]), m_leds[0] & 0x80 ? ' ' : '.', xlate(m_leds[0]));
		m_c009 &= ~2;
		m_c009 |= (data & 0x80) ? 0 : 2; // TODO: what this means
		m_c009 &= (data & 0x80) ? ~0x10 : ~0; // TODO: what this means
	}
}

u8 pwrview_state::pitclock_r()
{
	m_pit->write_clk0(ASSERT_LINE);
	m_pit->write_clk0(CLEAR_LINE);
	return 0;
}

void pwrview_state::bios_bank(address_map &map)
{
	map(0x00000, 0x07fff).rom().region("bios", 0);
	map(0x00000, 0x07fff).w(FUNC(pwrview_state::nmimem_w));

	map(0x08000, 0x0ffff).w(FUNC(pwrview_state::nmimem_w));
	map(0x0be00, 0x0be7f).bankrw("vram1");
	map(0x0befe, 0x0beff).rw(FUNC(pwrview_state::vram1_r), FUNC(pwrview_state::vram1_w));
	map(0x0bf00, 0x0bf7f).bankrw("vram2");
	map(0x0bffe, 0x0bfff).rw(FUNC(pwrview_state::vram2_r), FUNC(pwrview_state::vram2_w));
	map(0x0c000, 0x0ffff).rom().region("bios", 0x4000);

	map(0x10000, 0x17fff).ram();

	map(0x18000, 0x1ffff).w(FUNC(pwrview_state::nmimem_w));

	map(0x1be00, 0x1be7f).bankrw("vram1");
	map(0x1befe, 0x1beff).rw(FUNC(pwrview_state::vram1_r), FUNC(pwrview_state::vram1_w));
	map(0x1bf00, 0x1bf7f).bankrw("vram2");
	map(0x1bffe, 0x1bfff).rw(FUNC(pwrview_state::vram2_r), FUNC(pwrview_state::vram2_w));
	map(0x1c000, 0x1ffff).rom().region("bios", 0x4000);
}

void pwrview_state::pwrview_map(address_map &map)
{
	map(0x00000, 0xf7fff).ram().share("ram");
	map(0x00000, 0x003ff).rw(FUNC(pwrview_state::bank0_r), FUNC(pwrview_state::bank0_w));
	map(0xf8000, 0xfffff).m(m_biosbank, FUNC(address_map_bank_device::amap16));
}

void pwrview_state::pwrview_fetch_map(address_map &map)
{
	map(0x00000, 0xf7fff).ram().share("ram");
	map(0x00000, 0x003ff).r(FUNC(pwrview_state::bank0_r));
	map(0xf8000, 0xfffff).r(FUNC(pwrview_state::fbios_r));
}

void pwrview_state::pwrview_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(pwrview_state::nmiio_r), FUNC(pwrview_state::nmiio_w));
	map(0xc001, 0xc001).rw(FUNC(pwrview_state::unk1_r), FUNC(pwrview_state::unk1_w));
	map(0xc002, 0xc005).rw(FUNC(pwrview_state::led_r), FUNC(pwrview_state::led_w)).umask16(0xff00);
	map(0xc007, 0xc007).r(FUNC(pwrview_state::rotary_r));
	map(0xc009, 0xc009).rw(FUNC(pwrview_state::unk2_r), FUNC(pwrview_state::unk2_w));
	map(0xc00b, 0xc00b).r(FUNC(pwrview_state::err_r));
	map(0xc00c, 0xc00d).ram();
	map(0xc080, 0xc087).rw(FUNC(pwrview_state::unk4_r), FUNC(pwrview_state::unk4_w));
	map(0xc088, 0xc088).w("crtc", FUNC(hd6845s_device::address_w));
	map(0xc08a, 0xc08a).rw("crtc", FUNC(hd6845s_device::register_r), FUNC(hd6845s_device::register_w));
	map(0xc280, 0xc287).rw(FUNC(pwrview_state::unk3_r), FUNC(pwrview_state::unk3_w)).umask16(0x00ff);
	map(0xc288, 0xc28f).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0xc2a0, 0xc2a7).rw(m_sio, FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w)).umask16(0x00ff);
	map(0xc2c0, 0xc2c3).rw(m_uart, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0xc2e0, 0xc2e3).m(m_fdc, FUNC(upd765a_device::map)).umask16(0x00ff);
	map(0xc2e4, 0xc2e5).ram();
	map(0xc2e6, 0xc2e6).r(FUNC(pwrview_state::pitclock_r));
}

static void pwrview_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

void pwrview_state::pwrview(machine_config &config)
{
	I80186(config, m_maincpu, XTAL(16'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &pwrview_state::pwrview_map);
	m_maincpu->set_addrmap(AS_OPCODES, &pwrview_state::pwrview_fetch_map);
	m_maincpu->set_addrmap(AS_IO, &pwrview_state::pwrview_io);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_physical_aspect(3, 4); // Portrait CRT
	screen.set_raw(XTAL(64'000'000)/8, 992, 0, 744, 1040, 0, 960);  // clock unknown
	screen.set_screen_update("crtc", FUNC(hd6845s_device::screen_update));

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(XTAL(16'000'000)/16); // clocks unknown, fix above when found
	m_pit->set_clk<1>(XTAL(16'000'000)/16);
	m_pit->set_clk<2>(XTAL(16'000'000)/16);
	m_pit->out_handler<1>().set([this](int state){ if (!m_rtsa) m_sio->rxca_w(state); }); //HACK: prevent sdlc_receive from finding sync immediately because wr7 is 0 when rx is enabled
	m_pit->out_handler<1>().append([this](int state){ if (!m_rtsb) m_sio->rxcb_w(state); });
	m_pit->out_handler<1>().append([this](int state){ if (!m_rtsa) m_sio->txca_w(state); });
	m_pit->out_handler<1>().append([this](int state){ if (!m_rtsb) m_sio->txcb_w(state); });

	// floppy disk controller
	UPD765A(config, m_fdc, 8'000'000, false, false); // Rockwell R6765P
	m_fdc->intrq_wr_callback().set([this](int state){ if(m_enable_fdc) m_maincpu->int3_w(state); });
	m_fdc->drq_wr_callback().set(m_maincpu, FUNC(i80186_cpu_device::drq0_w));
	FLOPPY_CONNECTOR(config, "fdc:0", pwrview_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", pwrview_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);

	I8251(config, m_uart, 0);
	m_uart->rxrdy_handler().set(m_maincpu, FUNC(i80186_cpu_device::int3_w));
	m_uart->txd_handler().set([this](bool state){ if(BIT(m_c280, 4) && m_dtr) m_uart->write_rxd(state); }); // m_dtr here appears unlikely but the post seems to expect it
	m_uart->dtr_handler().set([this](bool state){ m_dtr = state; });
	m_uart->rts_handler().set([this](bool state){ m_rts = state; });


	Z80SIO(config, m_sio, 4000000); // Z8442BPS (SIO/2)
	m_sio->out_int_callback().set(m_maincpu, FUNC(i80186_cpu_device::int2_w));
	m_sio->out_txda_callback().set([this](int state){ m_sio->rxa_w(state); }); // TODO: find loopback control reg
	m_sio->out_rtsa_callback().set([this](int state){ m_rtsa = state; });
	m_sio->out_txdb_callback().set([this](int state){ m_sio->rxb_w(state); });
	m_sio->out_rtsb_callback().set([this](int state){ m_rtsb = state; });
	m_sio->out_wrdya_callback().set(m_maincpu, FUNC(i80186_cpu_device::drq1_w)).invert();
	m_sio->out_wrdyb_callback().set(m_maincpu, FUNC(i80186_cpu_device::drq1_w)).invert();

	hd6845s_device &crtc(HD6845S(config, "crtc", XTAL(64'000'000)/64)); // clock unknown
	crtc.set_char_width(62);
	crtc.set_update_row_callback(FUNC(pwrview_state::update_row));
	crtc.set_show_border_area(false);

	ADDRESS_MAP_BANK(config, "bios_bank").set_map(&pwrview_state::bios_bank).set_options(ENDIANNESS_LITTLE, 16, 17, 0x8000);
}

ROM_START(pwrview)
	ROM_REGION16_LE(0x8000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "bios", "bios")
	ROMX_LOAD("215856-003.bin", 0x0000, 0x4000, CRC(1fa2cd11) SHA1(b4755c7d5200a423a750ecf71c0aed33e364138b), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("215856-004.bin", 0x0001, 0x4000, CRC(4fd01e0a) SHA1(c4d1d40d4e8e529c03857f4a3c8428ccf6b8ff99), ROM_SKIP(1) | ROM_BIOS(0))
ROM_END

} // anonymous namespace


COMP(1984, pwrview, 0, 0, pwrview, 0, pwrview_state, empty_init, "Compugraphic", "MCS PowerView 10", MACHINE_NOT_WORKING)



px4.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Dirk Best
/***************************************************************************

    Epson PX-4

    Note: We are missing a dump of the slave 7508 CPU that controls
    the keyboard and some other things.

***************************************************************************/

#include "emu.h"

#include "bus/centronics/ctronics.h"
#include "bus/epson_sio/epson_sio.h"
#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "machine/nvram.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "sound/spkrdev.h"

#include "diserial.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "coreutil.h"

#include "px4.lh"


namespace {

//**************************************************************************
//  CONSTANTS
//**************************************************************************

#define VERBOSE 1


//**************************************************************************
//  MACROS
//**************************************************************************

#define ART_TX_ENABLED  (BIT(m_artcr, 0))
#define ART_RX_ENABLED  (BIT(m_artcr, 2))
#define ART_BREAK       (BIT(m_artcr, 3))


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class px4_state : public driver_device, public device_serial_interface
{
public:
	px4_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		device_serial_interface(mconfig, *this),
		m_z80(*this, "maincpu"),
		m_ram(*this, RAM_TAG),
		m_nvram(*this, "nvram"),
		m_centronics(*this, "centronics"),
		m_ext_cas(*this, "extcas"),
		m_ext_cas_timer(*this, "extcas_timer"),
		m_speaker(*this, "speaker"),
		m_sio(*this, "sio"),
		m_rs232(*this, "rs232"),
		m_caps(*this, "capsule%u", 0U),
		m_dips(*this, "dips"),
		m_leds(*this, "led_%u", 0U),
		m_caps_rom{nullptr, nullptr},
		m_ctrl1(0), m_icrb(0), m_bankr(0),
		m_isr(0), m_ier(0), m_sior(0xbf),
		m_frc_value(0), m_frc_latch(0),
		m_vadr(0), m_yoff(0),
		m_artdir(0xff), m_artdor(0xff), m_artsr(0), m_artcr(0),
		m_one_sec_int_enabled(true), m_key_int_enabled(true),
		m_key_status(0), m_interrupt_status(0),
		m_time(), m_clock_state(0),
		m_ear_last_state(0),
		m_sio_pin(0), m_serial_rx(0), m_rs232_dcd(0), m_rs232_cts(0),
		m_centronics_busy(0), m_centronics_perror(0)
	{ }

	void px4(machine_config &config);

	void init_px4();

	DECLARE_INPUT_CHANGED_MEMBER( key_callback );

protected:
	void px4_palette(palette_device &palette) const;
	uint32_t screen_update_px4(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	uint8_t icrlc_r();
	void ctrl1_w(uint8_t data);
	uint8_t icrhc_r();
	void cmdr_w(uint8_t data);
	uint8_t icrlb_r();
	void ctrl2_w(uint8_t data);
	uint8_t icrhb_r();
	uint8_t isr_r();
	void ier_w(uint8_t data);
	uint8_t str_r();
	void bankr_w(uint8_t data);
	uint8_t sior_r();
	void sior_w(uint8_t data);
	void vadr_w(uint8_t data);
	void yoff_w(uint8_t data);
	void fr_w(uint8_t data);
	void spur_w(uint8_t data);
	uint8_t ctgif_r(offs_t offset);
	void ctgif_w(offs_t offset, uint8_t data);
	uint8_t artdir_r();
	void artdor_w(uint8_t data);
	uint8_t artsr_r();
	void artmr_w(uint8_t data);
	uint8_t iostr_r();
	void artcr_w(uint8_t data);
	void swr_w(uint8_t data);
	void ioctlr_w(uint8_t data);


	TIMER_DEVICE_CALLBACK_MEMBER( ext_cassette_read );
	TIMER_DEVICE_CALLBACK_MEMBER( frc_tick );
	TIMER_DEVICE_CALLBACK_MEMBER( upd7508_1sec_callback );

	// serial
	void sio_rx_w(int state);
	void sio_pin_w(int state);
	void rs232_rx_w(int state);
	void rs232_dcd_w(int state);
	void rs232_dsr_w(int state);
	void rs232_cts_w(int state);

	// centronics
	void centronics_busy_w(int state) { m_centronics_busy = state; }
	void centronics_perror_w(int state) { m_centronics_perror = state; }

	void px4_io(address_map &map) ATTR_COLD;
	void px4_mem(address_map &map) ATTR_COLD;

	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// device_serial_interface overrides
	virtual void tra_callback() override;
	virtual void tra_complete() override;
	virtual void rcv_callback() override;
	virtual void rcv_complete() override;

	// z80 interrupt sources
	enum
	{
		INT0_7508 = 0x01,
		INT1_ART  = 0x02,
		INT2_ICF  = 0x04,
		INT3_OVF  = 0x08,
		INT4_EXT  = 0x10
	};

	// 7508 interrupt sources
	enum
	{
		UPD7508_INT_ALARM      = 0x02,
		UPD7508_INT_POWER_FAIL = 0x04,
		UPD7508_INT_7508_RESET = 0x08,
		UPD7508_INT_Z80_RESET  = 0x10,
		UPD7508_INT_ONE_SECOND = 0x20
	};

	// art (asynchronous receiver transmitter)
	enum
	{
		ART_TXRDY   = 0x01, // output buffer empty
		ART_RXRDY   = 0x02, // data byte received
		ART_TXEMPTY = 0x04, // transmit buffer empty
		ART_PE      = 0x08, // parity error
		ART_OE      = 0x10, // overrun error
		ART_FE      = 0x20  // framing error
	};

	void gapnit_interrupt();

	void serial_rx_w(int state);
	void txd_w(int data);

	void install_rom_capsule(address_space &space, int size, uint8_t *mem);

	// internal devices
	required_device<cpu_device> m_z80;
	required_device<ram_device> m_ram;
	required_device<nvram_device> m_nvram;
	required_device<centronics_device> m_centronics;
	required_device<cassette_image_device> m_ext_cas;
	required_device<timer_device> m_ext_cas_timer;
	required_device<speaker_sound_device> m_speaker;
	required_device<epson_sio_device> m_sio;
	required_device<rs232_port_device> m_rs232;
	required_device_array<generic_slot_device, 2> m_caps;
	required_ioport m_dips;
	output_finder<3> m_leds;

	uint8_t *m_caps_rom[2];

	// gapnit register
	uint8_t m_ctrl1;
	uint16_t m_icrb;
	uint8_t m_bankr;
	uint8_t m_isr;
	uint8_t m_ier;
	uint8_t m_sior;

	// gapnit internal
	uint16_t m_frc_value;
	uint16_t m_frc_latch;

	// gapndi register
	uint8_t m_vadr;
	uint8_t m_yoff;

	// gapnio
	uint8_t m_artdir;
	uint8_t m_artdor;
	uint8_t m_artsr;
	uint8_t m_artcr;
	uint8_t m_swr;

	// 7508 internal
	bool m_one_sec_int_enabled;
	bool m_key_int_enabled;

	uint8_t m_key_status;
	uint8_t m_interrupt_status;

	system_time m_time;
	int m_clock_state;

	// external cassette/barcode reader
	int m_ear_last_state;

	// serial
	int m_sio_pin;
	int m_serial_rx;
	int m_rs232_dcd;
	int m_rs232_cts;

	// centronics
	int m_centronics_busy;
	int m_centronics_perror;
};

class px4p_state : public px4_state
{
public:
	px4p_state(const machine_config &mconfig, device_type type, const char *tag) :
		px4_state(mconfig, type, tag),
		m_rdnvram(*this, "rdnvram"),
		m_rdsocket(*this, "ramdisk_socket"),
		m_ramdisk_address(0),
		m_ramdisk(nullptr)
	{ }

	void px4p(machine_config &config);

	void init_px4p();

private:
	void px4p_palette(palette_device &palette) const;

	void ramdisk_address_w(offs_t offset, uint8_t data);
	uint8_t ramdisk_data_r();
	void ramdisk_data_w(uint8_t data);
	uint8_t ramdisk_control_r();

	void px4p_io(address_map &map) ATTR_COLD;

	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;

	required_device<nvram_device> m_rdnvram;
	required_device<generic_slot_device> m_rdsocket;

	offs_t m_ramdisk_address;
	std::unique_ptr<uint8_t[]> m_ramdisk;
};


//**************************************************************************
//  GAPNIT
//**************************************************************************

// process interrupts
void px4_state::gapnit_interrupt()
{
	// any interrupts enabled and pending?
	if (m_ier & m_isr & INT0_7508)
	{
		m_isr &= ~INT0_7508;
		m_z80->set_input_line_and_vector(0, ASSERT_LINE, 0xf0); // Z80
	}
	else if (m_ier & m_isr & INT1_ART)
		m_z80->set_input_line_and_vector(0, ASSERT_LINE, 0xf2); // Z80
	else if (m_ier & m_isr & INT2_ICF)
		m_z80->set_input_line_and_vector(0, ASSERT_LINE, 0xf4); // Z80
	else if (m_ier & m_isr & INT3_OVF)
		m_z80->set_input_line_and_vector(0, ASSERT_LINE, 0xf6); // Z80
	else if (m_ier & m_isr & INT4_EXT)
		m_z80->set_input_line_and_vector(0, ASSERT_LINE, 0xf8); // Z80
	else
		m_z80->set_input_line(0, CLEAR_LINE);
}

// external cassette or barcode reader input
TIMER_DEVICE_CALLBACK_MEMBER( px4_state::ext_cassette_read )
{
	uint8_t result;
	int trigger = 0;

	// sample input state
	result = (m_ext_cas->input() > 0) ? 1 : 0;

	// detect transition
	switch ((m_ctrl1 >> 1) & 0x03)
	{
	case 0: // trigger inhibit
		trigger = 0;
		break;
	case 1: // falling edge trigger
		trigger = m_ear_last_state == 1 && result == 0;
		break;
	case 2: // rising edge trigger
		trigger = m_ear_last_state == 0 && result == 1;
		break;
	case 3: // rising/falling edge trigger
		trigger = m_ear_last_state != result;
		break;
	}

	// generate an interrupt if we need to trigger
	if (trigger)
	{
		m_icrb = m_frc_value;
		m_isr |= INT2_ICF;
		gapnit_interrupt();
	}

	// save last state
	m_ear_last_state = result;
}

// free running counter
TIMER_DEVICE_CALLBACK_MEMBER( px4_state::frc_tick )
{
	m_frc_value++;

	if (m_frc_value == 0)
	{
		m_isr |= INT3_OVF;
		gapnit_interrupt();
	}
}

// input capture register low command trigger
uint8_t px4_state::icrlc_r()
{
	if (VERBOSE)
		logerror("%s: icrlc_r\n", machine().describe_context());

	// latch value
	m_frc_latch = m_frc_value;

	return m_frc_latch & 0xff;
}

// control register 1
void px4_state::ctrl1_w(uint8_t data)
{
	const int rcv_rates[] = { 110, 150, 300, 600, 1200, 2400, 4800, 9600, 75, 1200, 19200, 38400, 200 };
	const int tra_rates[] = { 110, 150, 300, 600, 1200, 2400, 4800, 9600, 1200, 75, 19200, 38400, 200 };

	if (VERBOSE)
		logerror("%s: ctrl1_w (0x%02x)\n", machine().describe_context(), data);

	// baudrate generator
	int baud = data >> 4;

	if (baud <= 12)
	{
		if (VERBOSE)
			logerror("rcv baud = %d, tra baud = %d\n", rcv_rates[baud], tra_rates[baud]);

		set_rcv_rate(rcv_rates[baud]);
		set_tra_rate(tra_rates[baud]);
	}

	m_ctrl1 = data;
}

// input capture register high command trigger
uint8_t px4_state::icrhc_r()
{
	if (VERBOSE)
		logerror("%s: icrhc_r\n", machine().describe_context());

	return (m_frc_latch >> 8) & 0xff;
}

// command register
void px4_state::cmdr_w(uint8_t data)
{
	if (0)
		logerror("%s: cmdr_w (0x%02x)\n", machine().describe_context(), data);

	// clear overflow interrupt?
	if (BIT(data, 2))
	{
		m_isr &= ~INT3_OVF;
		gapnit_interrupt();
	}
}

// input capture register low barcode trigger
uint8_t px4_state::icrlb_r()
{
	if (VERBOSE)
		logerror("%s: icrlb_r\n", machine().describe_context());

	return m_icrb & 0xff;
}

// control register 2
void px4_state::ctrl2_w(uint8_t data)
{
	if (VERBOSE)
		logerror("%s: ctrl2_w (0x%02x)\n", machine().describe_context(), data);

	// bit 0, MIC, cassette output
	m_ext_cas->output( BIT(data, 0) ? -1.0 : +1.0);

	// bit 1, RMT, cassette motor
	if (BIT(data, 1))
	{
		m_ext_cas->change_state(CASSETTE_MOTOR_ENABLED, CASSETTE_MASK_MOTOR);
		m_ext_cas_timer->adjust(attotime::zero, 0, attotime::from_hz(44100));
	}
	else
	{
		m_ext_cas->change_state(CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
		m_ext_cas_timer->adjust(attotime::zero);
	}
}

// input capture register high barcode trigger
uint8_t px4_state::icrhb_r()
{
	if (VERBOSE)
		logerror("%s: icrhb_r\n", machine().describe_context());

	// clear icf interrupt
	m_isr &= ~INT2_ICF;
	gapnit_interrupt();

	return (m_icrb >> 8) & 0xff;
}

// interrupt status register
uint8_t px4_state::isr_r()
{
	if (VERBOSE)
		logerror("%s: isr_r\n", machine().describe_context());

	return m_isr;
}

// interrupt enable register
void px4_state::ier_w(uint8_t data)
{
	if (0)
		logerror("%s: ier_w (0x%02x)\n", machine().describe_context(), data);

	m_ier = data;
	gapnit_interrupt();
}

// status register
uint8_t px4_state::str_r()
{
	uint8_t data = 0;

	if (0)
		logerror("%s: str_r\n", machine().describe_context());

	data |= (m_ext_cas)->input() > 0 ? 1 : 0;
	data |= 1 << 1;   // BCRD, barcode reader input
	data |= 1 << 2;   // RDY signal from 7805
	data |= 1 << 3;   // RDYSIO, enable access to the 7805
	data |= m_bankr & 0xf0;   // bit 4-7, BANK - memory bank

	return data;
}

// helper function to map rom capsules
void px4_state::install_rom_capsule(address_space &space, int size, uint8_t *mem)
{
	// ram, part 1
	space.install_ram(0x0000, 0xdfff - size, m_ram->pointer());

	// actual rom data, part 1
	if (mem)
		space.install_rom(0xe000 - size, 0xffff, mem + (size - 0x2000));

	// rom data, part 2
	if (mem && size != 0x2000)
		space.install_rom(0x10000 - size, 0xdfff, mem);

	// ram, continued
	space.install_ram(0xe000, 0xffff, m_ram->pointer() + 0xe000);
}

// bank register
void px4_state::bankr_w(uint8_t data)
{
	address_space &space_program = m_z80->space(AS_PROGRAM);

	if (0)
		logerror("%s: bankr_w (0x%02x)\n", machine().describe_context(), data);

	m_bankr = data;

	// bank switch
	switch (data >> 4)
	{
	case 0x00:
		// system bank
		space_program.install_rom(0x0000, 0x7fff, memregion("os")->base());
		space_program.install_ram(0x8000, 0xffff, m_ram->pointer() + 0x8000);
		break;

	case 0x04:
		// memory
		space_program.install_ram(0x0000, 0xffff, m_ram->pointer());
		break;

	case 0x08: install_rom_capsule(space_program, 0x2000, m_caps_rom[0]); break;
	case 0x09: install_rom_capsule(space_program, 0x4000, m_caps_rom[0]); break;
	case 0x0a: install_rom_capsule(space_program, 0x8000, m_caps_rom[0]); break;
	case 0x0c: install_rom_capsule(space_program, 0x2000, m_caps_rom[1]); break;
	case 0x0d: install_rom_capsule(space_program, 0x4000, m_caps_rom[1]); break;
	case 0x0e: install_rom_capsule(space_program, 0x8000, m_caps_rom[1]); break;

	default:
		if (VERBOSE)
			logerror("invalid bank switch value: 0x%02x\n", data >> 4);
		break;
	}
}

// serial io register
uint8_t px4_state::sior_r()
{
	if (0)
		logerror("%s: sior_r 0x%02x\n", machine().describe_context(), m_sior);

	// reading clock?
	if (m_clock_state > 0)
	{
		switch (m_clock_state++)
		{
		case 1: m_sior = (dec_2_bcd(m_time.local_time.year) >> 4) & 0xf; break;
		case 2: m_sior = dec_2_bcd(m_time.local_time.year) & 0xf; break;
		case 3: m_sior = dec_2_bcd(m_time.local_time.month + 1); break;
		case 4: m_sior = dec_2_bcd(m_time.local_time.mday); break;
		case 5: m_sior = dec_2_bcd(m_time.local_time.hour); break;
		case 6: m_sior = dec_2_bcd(m_time.local_time.minute); break;
		case 7: m_sior = dec_2_bcd(m_time.local_time.second); break;
		case 8: m_sior = dec_2_bcd(m_time.local_time.weekday); break;
		}

		// done?
		if (m_clock_state == 9)
			m_clock_state = 0;
	}

	return m_sior;
}

// serial io register
void px4_state::sior_w(uint8_t data)
{
	if (0)
		logerror("%s: sior_w (0x%02x)\n", machine().describe_context(), data);

	// writing clock?
	if (m_clock_state > 0)
	{
		time_t time = m_time.time;
		struct tm *t = localtime(&time);

		switch (m_clock_state++)
		{
		case 1:
			{
				if (data < 10)
				{
					int year = dec_2_bcd(m_time.local_time.year);
					year = (year & 0xff0f) | ((data & 0xf) << 4);
					t->tm_year = bcd_2_dec(year) - 1900;
				}
			}
			break;
		case 2:
			{
				if (data < 10)
				{
					int year = dec_2_bcd(m_time.local_time.year);
					year = (year & 0xfff0) | (data & 0xf);
					t->tm_year = bcd_2_dec(year) - 1900;
				}
			}
			break;
		case 3: t->tm_mon = bcd_2_dec(data & 0x7f) - 1; break;
		case 4: t->tm_mday = bcd_2_dec(data & 0x7f); break;
		case 5: t->tm_hour = bcd_2_dec(data & 0x7f); break;
		case 6: t->tm_min = bcd_2_dec(data & 0x7f); break;
		case 7: t->tm_sec = bcd_2_dec(data & 0x7f); break;
		case 8: t->tm_wday = bcd_2_dec(data & 0x7f); break;
		}

		// update
		m_time.set(mktime(t));

		// done?
		if (m_clock_state == 9)
			m_clock_state = 0;
	}
	else
	{
		m_sior = data;

		switch (data)
		{
		case 0x01:
			if (VERBOSE)
				logerror("7508 cmd: Power OFF\n");

			break;

		case 0x02:
			if (VERBOSE)
				logerror("7508 cmd: Read Status\n");

			if (m_interrupt_status != 0)
			{
				if (VERBOSE)
					logerror("> 7508 has interrupts pending: 0x%02x\n", m_interrupt_status);

				// signal the interrupt(s)
				m_sior = 0xc1 | m_interrupt_status;
				m_interrupt_status = 0x00;
			}
			else if (m_key_status != 0xff)
			{
				m_sior = m_key_status;
				m_key_status = 0xff;
			}
			else
			{
				// nothing happened
				m_sior = 0xbf;
			}

			break;

		case 0x03: if (VERBOSE) logerror("7508 cmd: KB Reset\n"); break;
		case 0x04: if (VERBOSE) logerror("7508 cmd: KB Repeat Timer 1 Set\n"); break;
		case 0x14: if (VERBOSE) logerror("7508 cmd: KB Repeat Timer 2 Set\n"); break;
		case 0x24: if (VERBOSE) logerror("7508 cmd: KB Repeat Timer 1 Read\n"); break;
		case 0x34: if (VERBOSE) logerror("7508 cmd: KB Repeat Timer 2 Read\n"); break;
		case 0x05: if (VERBOSE) logerror("7508 cmd: KB Repeat OFF\n"); break;
		case 0x15: if (VERBOSE) logerror("7508 cmd: KB Repeat ON\n"); break;

		case 0x06:
			if (VERBOSE)
				logerror("7508 cmd: KB Interrupt OFF\n");

			m_key_int_enabled = false;
			break;

		case 0x16:
			if (VERBOSE)
				logerror("7508 cmd: KB Interrupt ON\n");

			m_key_int_enabled = true;
			break;

		case 0x07:
			if (VERBOSE)
				logerror("7508 cmd: Clock Read\n");

			m_clock_state = 1;
			break;

		case 0x17:
			if (VERBOSE)
				logerror("7508 cmd: Clock Write\n");

			m_clock_state = 1;
			break;

		case 0x08:
			if (VERBOSE)
				logerror("7508 cmd: Power Switch Read\n");

			// indicate that the power switch is in the "ON" position
			m_sior = 0x01;
			break;

		case 0x09: if (VERBOSE) logerror("7508 cmd: Alarm Read\n"); break;
		case 0x19: if (VERBOSE) logerror("7508 cmd: Alarm Set\n"); break;
		case 0x29: if (VERBOSE) logerror("7508 cmd: Alarm OFF\n"); break;
		case 0x39: if (VERBOSE) logerror("7508 cmd: Alarm ON\n"); break;

		case 0x0a:
			if (VERBOSE)
				logerror("7508 cmd: DIP Switch Read\n");
			m_sior = m_dips->read();
			break;

		case 0x0b: if (VERBOSE) logerror("7508 cmd: Stop Key Interrupt disable\n"); break;
		case 0x1b: if (VERBOSE) logerror("7508 cmd: Stop Key Interrupt enable\n"); break;
		case 0x0c: if (VERBOSE) logerror("7508 cmd: 7 chr. Buffer\n"); break;
		case 0x1c: if (VERBOSE) logerror("7508 cmd: 1 chr. Buffer\n"); break;

		case 0x0d:
			if (VERBOSE)
				logerror("7508 cmd: 1 sec. Interrupt OFF\n");

			m_one_sec_int_enabled = false;
			break;

		case 0x1d:
			if (VERBOSE)
				logerror("7508 cmd: 1 sec. Interrupt ON\n");

			m_one_sec_int_enabled = true;
			break;

		case 0x0e:
			if (VERBOSE)
				logerror("7508 cmd: KB Clear\n");

			m_sior = 0xbf;
			break;

		case 0x0f: if (VERBOSE) logerror("7508 cmd: System Reset\n"); break;
		}
	}
}


//**************************************************************************
//  GAPNDL
//**************************************************************************

// vram start address register
void px4_state::vadr_w(uint8_t data)
{
	if (VERBOSE)
		logerror("%s: vadr_w (0x%02x)\n", machine().describe_context(), data);

	m_vadr = data;
}

// y offset register
void px4_state::yoff_w(uint8_t data)
{
	if (VERBOSE)
		logerror("%s: yoff_w (0x%02x)\n", machine().describe_context(), data);

	m_yoff = data;
}

// frame register
void px4_state::fr_w(uint8_t data)
{
	if (VERBOSE)
		logerror("%s: fr_w (0x%02x)\n", machine().describe_context(), data);
}

// speed-up register
void px4_state::spur_w(uint8_t data)
{
	if (VERBOSE)
		logerror("%s: spur_w (0x%02x)\n", machine().describe_context(), data);
}


//**************************************************************************
//  GAPNIO
//**************************************************************************

void px4_state::serial_rx_w(int state)
{
	m_serial_rx = state;
	device_serial_interface::rx_w(state);
}

void px4_state::sio_rx_w(int state)
{
	if (!BIT(m_swr, 3) && BIT(m_swr, 2))
		serial_rx_w(state);
}

void px4_state::sio_pin_w(int state)
{
	m_sio_pin = state;
}

void px4_state::rs232_rx_w(int state)
{
	if (BIT(m_swr, 3))
		serial_rx_w(state);
}

void px4_state::rs232_dcd_w(int state)
{
	m_rs232_dcd = state;
}

void px4_state::rs232_dsr_w(int state)
{
	m_artsr |= !state << 7;
}

void px4_state::rs232_cts_w(int state)
{
	m_rs232_cts = state;
}

void px4_state::tra_callback()
{
	if (ART_TX_ENABLED)
	{
		if (ART_BREAK)
			txd_w(0); // transmit break
		else
			txd_w(transmit_register_get_data_bit()); // transmit data
	}
	else
		txd_w(1); // transmit mark
}

void px4_state::tra_complete()
{
	if (m_artsr & ART_TXRDY)
	{
		// no more bytes, buffer now empty
		m_artsr |= ART_TXEMPTY;
	}
	else
	{
		// transfer next byte
		transmit_register_setup(m_artdor);
		m_artsr |= ART_TXRDY;
	}
}

void px4_state::rcv_callback()
{
	if (ART_RX_ENABLED)
		receive_register_update_bit(m_serial_rx); // receive data
}

void px4_state::rcv_complete()
{
	receive_register_extract();
	m_artdir = get_received_char();

	// TODO: verify parity and framing

	// overrun?
	if (m_artsr & ART_RXRDY)
		m_artsr |= ART_OE;

	// set ready and signal interrupt
	m_artsr |= ART_RXRDY;
	m_isr |= INT1_ART;
	gapnit_interrupt();
}

// helper function to write to selected serial port
void px4_state::txd_w(int data)
{
	if (BIT(m_swr, 2))
		// to sio
		m_sio->tx_w(data);
	else
		if (BIT(m_swr, 3))
			// to rs232
			m_rs232->write_txd(data);
		// else to cartridge
}

// cartridge interface
uint8_t px4_state::ctgif_r(offs_t offset)
{
	if (VERBOSE)
		logerror("%s: ctgif_r @ 0x%02x\n", machine().describe_context(), offset);

	return 0x00;
}

// cartridge interface
void px4_state::ctgif_w(offs_t offset, uint8_t data)
{
	if (VERBOSE)
		logerror("%s: ctgif_w (0x%02x @ 0x%02x)\n", machine().describe_context(), data, offset);
}

// art data input register
uint8_t px4_state::artdir_r()
{
	if (VERBOSE)
		logerror("%s: artdir_r (%02x)\n", machine().describe_context(), m_artdir);

	// clear ready
	m_artsr &= ~ART_RXRDY;

	// clear interrupt
	m_isr &= ~INT1_ART;
	gapnit_interrupt();

	return m_artdir;
}

// art data output register
void px4_state::artdor_w(uint8_t data)
{
	if (VERBOSE)
		logerror("%s: artdor_w (0x%02x)\n", machine().describe_context(), data);

	m_artdor = data;

	// new data to transmit?
	if (ART_TX_ENABLED && is_transmit_register_empty())
	{
		transmit_register_setup(m_artdor);
		m_artsr &= ~ART_TXEMPTY;
	}
	else
	{
		// clear ready
		m_artsr &= ~ART_TXRDY;
	}
}

// art status register
uint8_t px4_state::artsr_r()
{
	if (0)
		logerror("%s: artsr_r (%02x)\n", machine().describe_context(), m_artsr);

	return m_artsr;
}

// art mode register
void px4_state::artmr_w(uint8_t data)
{
	int data_bit_count = BIT(data, 2) ? 8 : 7;
	parity_t parity = BIT(data, 4) ? (BIT(data, 5) ? PARITY_EVEN : PARITY_ODD) : PARITY_NONE;
	stop_bits_t stop_bits = BIT(data, 7) ? STOP_BITS_2 : STOP_BITS_1;

	if (VERBOSE)
		logerror("%s: serial frame setup: %d-%s-%d\n", tag(), data_bit_count, device_serial_interface::parity_tostring(parity), stop_bits);

	set_data_frame(1, data_bit_count, parity, stop_bits);
}

// io status register
uint8_t px4_state::iostr_r()
{
	uint8_t data = 0;

	// centronics status
	data |= m_centronics_busy << 0;
	data |= m_centronics_perror << 1;

	// sio status
	data |= !m_sio_pin << 2;

	// serial data
	data |= m_serial_rx << 3;

	// rs232 status
	data |= !m_rs232_dcd << 4;
	data |= !m_rs232_cts << 5;

	data |= 1 << 6;   // bit 6, csel, cartridge option select signal
	data |= 0 << 7;   // bit 7, caud - audio input from cartridge

	if (0)
		logerror("%s: iostr_r: rx = %d, dcd = %d, cts = %d\n", machine().describe_context(), BIT(data, 3), BIT(data, 4), BIT(data, 5));

	return data;
}

// art command register
void px4_state::artcr_w(uint8_t data)
{
	if (VERBOSE)
		logerror("%s: artcr_w (0x%02x)\n", machine().describe_context(), data);

	m_artcr = data;

	// error reset
	if (BIT(data, 4))
		m_artsr &= ~(ART_PE | ART_OE | ART_FE);

	// rs232
	m_rs232->write_dtr(!BIT(data, 1));
	m_rs232->write_rts(!BIT(data, 5));
}

// switch register
void px4_state::swr_w(uint8_t data)
{
	if (VERBOSE)
	{
		const char *cart_mode[] = { "hs", "io", "db", "ot" };
		const char *ser_mode[] = { "cart sio / cart sio", "sio / sio", "rs232 / rs232", "rs232 / sio" };
		logerror("%s: px4_swr_w: cartridge mode: %s, serial mode: %s, audio: %s\n", machine().describe_context(),
			cart_mode[data & 0x03], ser_mode[(data >> 2) & 0x03], BIT(data, 4) ? "on" : "off");
	}

	m_swr = data;
}

// io control register
void px4_state::ioctlr_w(uint8_t data)
{
	if (VERBOSE)
		logerror("%s: ioctlr_w (0x%02x)\n", machine().describe_context(), data);

	m_centronics->write_strobe(!BIT(data, 0));
	m_centronics->write_init(BIT(data, 1));

	m_sio->pout_w(BIT(data, 2));

	// bit 3, cartridge reset

	m_leds[0] = BIT(data, 4); // caps lock
	m_leds[1] = BIT(data, 5); // num lock
	m_leds[2] = BIT(data, 6); // "led 2"

	m_speaker->level_w(BIT(data, 7));
}


//**************************************************************************
//  7508 RELATED
//**************************************************************************

TIMER_DEVICE_CALLBACK_MEMBER( px4_state::upd7508_1sec_callback )
{
	// adjust interrupt status
	m_interrupt_status |= UPD7508_INT_ONE_SECOND;

	// are interrupts enabled?
	if (m_one_sec_int_enabled)
	{
		m_isr |= INT0_7508;
		gapnit_interrupt();
	}

	// update clock
	m_time.set(m_time.time + 1);
}

INPUT_CHANGED_MEMBER( px4_state::key_callback )
{
	uint32_t oldvalue = oldval * field.mask(), newvalue = newval * field.mask();
	uint32_t delta = oldvalue ^ newvalue;
	int i, scancode = 0xff, down = 0;

	for (i = 0; i < 32; i++)
	{
		if (delta & (1 << i))
		{
			down = (newvalue & (1 << i)) ? 0x10 : 0x00;
			scancode = param * 32 + i;

			// control keys
			if ((scancode & 0xa0) == 0xa0)
				scancode |= down;

			if (VERBOSE)
				logerror("upd7508: key callback, key=0x%02x\n", scancode);

			break;
		}
	}

	if (down || (scancode & 0xa0) == 0xa0)
	{
		m_key_status = scancode;

		if (m_key_int_enabled)
		{
			if (VERBOSE)
				logerror("upd7508: key interrupt\n");

			m_isr |= INT0_7508;
			gapnit_interrupt();
		}
	}
}


//**************************************************************************
//  EXTERNAL RAM-DISK
//**************************************************************************

void px4p_state::ramdisk_address_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 0x00: m_ramdisk_address = (m_ramdisk_address & 0xffff00) | ((data & 0xff) <<  0); break;
	case 0x01: m_ramdisk_address = (m_ramdisk_address & 0xff00ff) | ((data & 0xff) <<  8); break;
	case 0x02: m_ramdisk_address = (m_ramdisk_address & 0x00ffff) | ((data & 0x07) << 16); break;
	}
}

uint8_t px4p_state::ramdisk_data_r()
{
	uint8_t ret = 0xff;

	if (m_ramdisk_address < 0x20000)
	{
		// read from ram
		ret = m_ramdisk[m_ramdisk_address];
	}
	else if (m_ramdisk_address < 0x40000)
	{
		// read from rom
		ret = m_rdsocket->read_rom(m_ramdisk_address);
	}

	m_ramdisk_address = (m_ramdisk_address & 0xffff00) | ((m_ramdisk_address & 0xff) + 1);

	return ret;
}

void px4p_state::ramdisk_data_w(uint8_t data)
{
	if (m_ramdisk_address < 0x20000)
		m_ramdisk[m_ramdisk_address] = data;

	m_ramdisk_address = (m_ramdisk_address & 0xffff00) | ((m_ramdisk_address & 0xff) + 1);
}

uint8_t px4p_state::ramdisk_control_r()
{
	// bit 7 determines the presence of a ram-disk
	return 0x7f;
}

//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

uint32_t px4_state::screen_update_px4(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	// display enabled?
	if (BIT(m_yoff, 7))
	{
		// get vram start address
		uint8_t const *vram = &m_ram->pointer()[(m_vadr & 0xf8) << 8];

		for (int y = 0; y < 64; y++)
		{
			// adjust against y-offset
			uint8_t row = (y - (m_yoff & 0x3f)) & 0x3f;

			for (int x = 0; x < 240/8; x++)
			{
				bitmap.pix(row, x * 8 + 0) = BIT(*vram, 7);
				bitmap.pix(row, x * 8 + 1) = BIT(*vram, 6);
				bitmap.pix(row, x * 8 + 2) = BIT(*vram, 5);
				bitmap.pix(row, x * 8 + 3) = BIT(*vram, 4);
				bitmap.pix(row, x * 8 + 4) = BIT(*vram, 3);
				bitmap.pix(row, x * 8 + 5) = BIT(*vram, 2);
				bitmap.pix(row, x * 8 + 6) = BIT(*vram, 1);
				bitmap.pix(row, x * 8 + 7) = BIT(*vram, 0);

				vram++;
			}

			// skip the last 2 unused bytes
			vram += 2;
		}
	}
	else
	{
		// display is disabled, draw an empty screen
		bitmap.fill(0, cliprect);
	}

	return 0;
}


//**************************************************************************
//  DRIVER INIT
//**************************************************************************

void px4_state::init_px4()
{
	// map os rom and last half of memory
	membank("bank1")->set_base(memregion("os")->base());
	membank("bank2")->set_base(m_ram->pointer() + 0x8000);
}

void px4p_state::init_px4p()
{
	init_px4();

	// reserve memory for external ram-disk
	m_ramdisk = std::make_unique<uint8_t[]>(0x20000);
}

void px4_state::machine_start()
{
	m_leds.resolve();

	for (int i = 0; i < 2; i++)
		m_caps_rom[i] = m_caps[i]->get_rom_base();

	m_nvram->set_base(m_ram->pointer(), 0x10000);

	// initialize clock
	machine().base_datetime(m_time);
}

void px4_state::machine_reset()
{
	m_artsr = ART_TXRDY | ART_TXEMPTY | (!m_rs232->dsr_r() << 7);
	receive_register_reset();
	transmit_register_reset();
}

void px4p_state::machine_start()
{
	px4_state::machine_start();
	m_rdnvram->set_base(m_ramdisk.get(), 0x20000);
}


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void px4_state::px4_mem(address_map &map)
{
	map(0x0000, 0x7fff).bankr("bank1");
	map(0x8000, 0xffff).bankrw("bank2");
}

void px4_state::px4_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	// gapnit, 0x00-0x07
	map(0x00, 0x00).rw(FUNC(px4_state::icrlc_r), FUNC(px4_state::ctrl1_w));
	map(0x01, 0x01).rw(FUNC(px4_state::icrhc_r), FUNC(px4_state::cmdr_w));
	map(0x02, 0x02).rw(FUNC(px4_state::icrlb_r), FUNC(px4_state::ctrl2_w));
	map(0x03, 0x03).r(FUNC(px4_state::icrhb_r));
	map(0x04, 0x04).rw(FUNC(px4_state::isr_r), FUNC(px4_state::ier_w));
	map(0x05, 0x05).rw(FUNC(px4_state::str_r), FUNC(px4_state::bankr_w));
	map(0x06, 0x06).rw(FUNC(px4_state::sior_r), FUNC(px4_state::sior_w));
	map(0x07, 0x07).noprw();
	// gapndl, 0x08-0x0f
	map(0x08, 0x08).w(FUNC(px4_state::vadr_w));
	map(0x09, 0x09).w(FUNC(px4_state::yoff_w));
	map(0x0a, 0x0a).w(FUNC(px4_state::fr_w));
	map(0x0b, 0x0b).w(FUNC(px4_state::spur_w));
	map(0x0c, 0x0f).noprw();
	// gapnio, 0x10-0x1f
	map(0x10, 0x13).rw(FUNC(px4_state::ctgif_r), FUNC(px4_state::ctgif_w));
	map(0x14, 0x14).rw(FUNC(px4_state::artdir_r), FUNC(px4_state::artdor_w));
	map(0x15, 0x15).rw(FUNC(px4_state::artsr_r), FUNC(px4_state::artmr_w));
	map(0x16, 0x16).rw(FUNC(px4_state::iostr_r), FUNC(px4_state::artcr_w));
	map(0x17, 0x17).w("cent_data_out", FUNC(output_latch_device::write));
	map(0x18, 0x18).w(FUNC(px4_state::swr_w));
	map(0x19, 0x19).w(FUNC(px4_state::ioctlr_w));
	map(0x1a, 0x1f).noprw();
}

void px4p_state::px4p_io(address_map &map)
{
	px4_io(map);
	map(0x90, 0x92).w(FUNC(px4p_state::ramdisk_address_w));
	map(0x93, 0x93).rw(FUNC(px4p_state::ramdisk_data_r), FUNC(px4p_state::ramdisk_data_w));
	map(0x94, 0x94).r(FUNC(px4p_state::ramdisk_control_r));
}


//**************************************************************************
//  INPUT PORTS
//**************************************************************************

/* The PX-4 has an exchangeable keyboard. Available is a standard ASCII
 * keyboard and an "item" keyboard, as well as regional variants for
 * UK, France, Germany, Denmark, Sweden, Norway, Italy and Spain.
 */

// configuration dip switch found on the rom capsule board
static INPUT_PORTS_START( px4_dips )
	PORT_START("dips")

	PORT_DIPNAME(0x0f, 0x0f, "Character set")
	PORT_DIPLOCATION("PX-4:8,7,6,5")
	PORT_DIPSETTING(0x0f, "ASCII")
	PORT_DIPSETTING(0x0e, "France")
	PORT_DIPSETTING(0x0d, "Germany")
	PORT_DIPSETTING(0x0c, "England")
	PORT_DIPSETTING(0x0b, "Denmark")
	PORT_DIPSETTING(0x0a, "Sweden")
	PORT_DIPSETTING(0x09, "Italy")
	PORT_DIPSETTING(0x08, "Spain")
	PORT_DIPSETTING(0x07, DEF_STR(Japan))
	PORT_DIPSETTING(0x06, "Norway")

	PORT_DIPNAME(0x30, 0x30, "LST device")
	PORT_DIPLOCATION("PX-4:4,3")
	PORT_DIPSETTING(0x00, "SIO")
	PORT_DIPSETTING(0x10, "Cartridge printer")
	PORT_DIPSETTING(0x20, "RS-232C")
	PORT_DIPSETTING(0x30, "Centronics printer")

	// available for user applications
	PORT_DIPNAME(0x40, 0x40, "Not used")
	PORT_DIPLOCATION("PX-4:2")
	PORT_DIPSETTING(0x40, "Enable")
	PORT_DIPSETTING(0x00, "Disable")

	// this is automatically selected by the os, the switch has no effect
	PORT_DIPNAME(0x80, 0x00, "Keyboard type")
	PORT_DIPLOCATION("PX-4:1")
	PORT_DIPSETTING(0x80, "Item keyboard")
	PORT_DIPSETTING(0x00, "Standard keyboard")
INPUT_PORTS_END

// US ASCII keyboard
static INPUT_PORTS_START( px4_h450a )
	PORT_INCLUDE(px4_dips)

	PORT_START("keyboard_0")
	PORT_BIT(0x00000001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 0) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(ESC)) // 00
	PORT_BIT(0x00000002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 0) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(PAUSE))   // 01
	PORT_BIT(0x00000004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 0) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F6))    PORT_NAME("Help") // 02
	PORT_BIT(0x00000008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 0) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F1))    PORT_NAME("PF1")  // 03
	PORT_BIT(0x00000010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 0) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F2))    PORT_NAME("PF2")  // 04
	PORT_BIT(0x00000020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 0) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F3))    PORT_NAME("PF3")  // 05
	PORT_BIT(0x00000040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 0) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F4))    PORT_NAME("PF4")  // 06
	PORT_BIT(0x00000080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 0) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F5))    PORT_NAME("PF5")  // 07
	PORT_BIT(0x0000ff00, IP_ACTIVE_HIGH, IPT_UNUSED)    // 08-0f
	PORT_BIT(0x00010000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 0) PORT_CODE(KEYCODE_ESC)  PORT_CHAR(UCHAR_MAMEKEY(CANCEL)) PORT_NAME("Stop")  // 10
	PORT_BIT(0x00020000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 0) PORT_CODE(KEYCODE_1)   PORT_CHAR('1') PORT_CHAR('!')    // 11
	PORT_BIT(0x00040000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 0) PORT_CODE(KEYCODE_2)   PORT_CHAR('2') PORT_CHAR('"')    // 12
	PORT_BIT(0x00080000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 0) PORT_CODE(KEYCODE_3)   PORT_CHAR('3') PORT_CHAR('#')    // 13
	PORT_BIT(0x00100000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 0) PORT_CODE(KEYCODE_4)   PORT_CHAR('4') PORT_CHAR('$')    // 14
	PORT_BIT(0x00200000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 0) PORT_CODE(KEYCODE_5)   PORT_CHAR('5') PORT_CHAR('%')    // 15
	PORT_BIT(0x00400000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 0) PORT_CODE(KEYCODE_6)   PORT_CHAR('6') PORT_CHAR('&')    // 16
	PORT_BIT(0x00800000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 0) PORT_CODE(KEYCODE_7)   PORT_CHAR('7') PORT_CHAR('\'')   // 17
	PORT_BIT(0xff000000, IP_ACTIVE_HIGH, IPT_UNUSED)    // 18-1f

	PORT_START("keyboard_1")
	PORT_BIT(0x00000001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 1) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')  // 20
	PORT_BIT(0x00000002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 1) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')  // 21
	PORT_BIT(0x00000004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 1) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')  // 22
	PORT_BIT(0x00000008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 1) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')  // 23
	PORT_BIT(0x00000010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 1) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')  // 24
	PORT_BIT(0x00000020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 1) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')  // 25
	PORT_BIT(0x00000040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 1) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')  // 26
	PORT_BIT(0x00000080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 1) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')  // 27
	PORT_BIT(0x0000ff00, IP_ACTIVE_HIGH, IPT_UNUSED)    // 28-2f
	PORT_BIT(0x00010000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 1) PORT_CODE(KEYCODE_D)     PORT_CHAR('d') PORT_CHAR('D')  // 30
	PORT_BIT(0x00020000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 1) PORT_CODE(KEYCODE_F)     PORT_CHAR('f') PORT_CHAR('F')  // 31
	PORT_BIT(0x00040000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 1) PORT_CODE(KEYCODE_G)     PORT_CHAR('g') PORT_CHAR('G')  // 32
	PORT_BIT(0x00080000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 1) PORT_CODE(KEYCODE_H)     PORT_CHAR('h') PORT_CHAR('H')  // 33
	PORT_BIT(0x00100000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 1) PORT_CODE(KEYCODE_J)     PORT_CHAR('j') PORT_CHAR('J')  // 34
	PORT_BIT(0x00200000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 1) PORT_CODE(KEYCODE_K)     PORT_CHAR('k') PORT_CHAR('K')  // 35
	PORT_BIT(0x00400000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 1) PORT_CODE(KEYCODE_L)     PORT_CHAR('l') PORT_CHAR('L')  // 36
	PORT_BIT(0x00800000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 1) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')  // 37
	PORT_BIT(0xff000000, IP_ACTIVE_HIGH, IPT_UNUSED)    // 38-3f

	PORT_START("keyboard_2")
	PORT_BIT(0x00000001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 2) PORT_CODE(KEYCODE_B)     PORT_CHAR('b') PORT_CHAR('B')  // 40
	PORT_BIT(0x00000002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 2) PORT_CODE(KEYCODE_N)     PORT_CHAR('n') PORT_CHAR('N')  // 41
	PORT_BIT(0x00000004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 2) PORT_CODE(KEYCODE_M)     PORT_CHAR('m') PORT_CHAR('M')  // 42
	PORT_BIT(0x00000008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 2) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')  // 43
	PORT_BIT(0x00000010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 2) PORT_CODE(KEYCODE_STOP)  PORT_CHAR('.') PORT_CHAR('>')  // 44
	PORT_BIT(0x00000020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 2) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')  // 45
	PORT_BIT(0x00000040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 2) PORT_CODE(KEYCODE_F9)    PORT_CHAR('[') PORT_CHAR('{')  // 46
	PORT_BIT(0x00000080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 2) PORT_CODE(KEYCODE_F10)   PORT_CHAR(']') PORT_CHAR('}')  // 47
	PORT_BIT(0x0000ff00, IP_ACTIVE_HIGH, IPT_UNUSED)    // 48-4f
	PORT_BIT(0x00010000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 2) PORT_CODE(KEYCODE_8)         PORT_CHAR('8') PORT_CHAR('(')  // 50
	PORT_BIT(0x00020000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 2) PORT_CODE(KEYCODE_9)         PORT_CHAR('9') PORT_CHAR(')')  // 51
	PORT_BIT(0x00040000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 2) PORT_CODE(KEYCODE_0)         PORT_CHAR('0') PORT_CHAR('_')  // 52
	PORT_BIT(0x00080000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 2) PORT_CODE(KEYCODE_MINUS)     PORT_CHAR('-') PORT_CHAR('=')  // 53
	PORT_BIT(0x00100000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 2) PORT_CODE(KEYCODE_EQUALS)    PORT_CHAR('^') PORT_CHAR('~')  // 54
	PORT_BIT(0x00200000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 2) PORT_CODE(KEYCODE_UP)        PORT_CHAR(UCHAR_MAMEKEY(UP))   // 55
	PORT_BIT(0x00400000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 2) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)   PORT_CHAR(UCHAR_MAMEKEY(HOME))  // 56
	PORT_BIT(0x00800000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 2) PORT_CODE(KEYCODE_TAB)       PORT_CHAR('\t')    // 57
	PORT_BIT(0xff000000, IP_ACTIVE_HIGH, IPT_UNUSED)    // 58-5f

	PORT_START("keyboard_3")
	PORT_BIT(0x00000001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 3) PORT_CODE(KEYCODE_O)         PORT_CHAR('o') PORT_CHAR('O')  // 60
	PORT_BIT(0x00000002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 3) PORT_CODE(KEYCODE_P)         PORT_CHAR('p') PORT_CHAR('P')  // 61
	PORT_BIT(0x00000004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 3) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@') PORT_CHAR(96)   // 62
	PORT_BIT(0x00000008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 3) PORT_CODE(KEYCODE_LEFT)      PORT_CHAR(UCHAR_MAMEKEY(LEFT)) // 63
	PORT_BIT(0x00000010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 3) PORT_CODE(KEYCODE_DOWN)      PORT_CHAR(UCHAR_MAMEKEY(DOWN)) // 64
	PORT_BIT(0x00000020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 3) PORT_CODE(KEYCODE_RIGHT)     PORT_CHAR(UCHAR_MAMEKEY(RIGHT))    // 65
	PORT_BIT(0x00000040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 3) PORT_CODE(KEYCODE_A)         PORT_CHAR('a') PORT_CHAR('A')  // 66
	PORT_BIT(0x00000080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 3) PORT_CODE(KEYCODE_S)         PORT_CHAR('s') PORT_CHAR('S')  // 67
	PORT_BIT(0x0000ff00, IP_ACTIVE_HIGH, IPT_UNUSED)    // 48-4f
	PORT_BIT(0x00010000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 3) PORT_CODE(KEYCODE_QUOTE)     PORT_CHAR(':')  PORT_CHAR('*') // 70
	PORT_BIT(0x00020000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 3) PORT_CODE(KEYCODE_ENTER)     PORT_CHAR(13)  // 71
	PORT_BIT(0x00040000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 3) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|') // 72
	PORT_BIT(0x00080000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 3) PORT_CODE(KEYCODE_SPACE)     PORT_CHAR(' ') // 73
	PORT_BIT(0x00100000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 3) PORT_CODE(KEYCODE_Z)         PORT_CHAR('z') PORT_CHAR('Z')  // 74
	PORT_BIT(0x00200000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 3) PORT_CODE(KEYCODE_X)         PORT_CHAR('x') PORT_CHAR('X')  // 75
	PORT_BIT(0x00400000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 3) PORT_CODE(KEYCODE_C)         PORT_CHAR('c') PORT_CHAR('C')  // 76
	PORT_BIT(0x00800000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 3) PORT_CODE(KEYCODE_V)         PORT_CHAR('v') PORT_CHAR('V')  // 77
	PORT_BIT(0xff000000, IP_ACTIVE_HIGH, IPT_UNUSED)    // 58-5f

	PORT_START("keyboard_4")
	PORT_BIT(0x00000001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 4) PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT)) PORT_CHAR(UCHAR_MAMEKEY(PRTSCR)) // 80
	PORT_BIT(0x00000002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 4) PORT_CODE(KEYCODE_DEL)    PORT_CHAR(UCHAR_MAMEKEY(DEL))    PORT_CHAR(12)   // 81
	PORT_BIT(0xfffffffc, IP_ACTIVE_HIGH, IPT_UNUSED)    // 82-9f

	PORT_START("keyboard_5")
	PORT_BIT(0x00000003, IP_ACTIVE_HIGH, IPT_UNUSED)    // a0-a1
	PORT_BIT(0x00000004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 5) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)    // a2
	PORT_BIT(0x00000008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 5) PORT_CODE(KEYCODE_LSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)    // a3
	PORT_BIT(0x00000010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 5) PORT_CODE(KEYCODE_LALT)     PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))  // a4
	PORT_BIT(0x00000020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 5) PORT_CODE(KEYCODE_RALT)     PORT_NAME("Graph")  // a5
	PORT_BIT(0x00000040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 5) PORT_CODE(KEYCODE_RSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)    // a6
	PORT_BIT(0x00000080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(px4_state::key_callback), 5) PORT_CODE(KEYCODE_NUMLOCK)  PORT_CHAR(UCHAR_MAMEKEY(NUMLOCK))   // a7
	PORT_BIT(0xffffff00, IP_ACTIVE_HIGH, IPT_UNUSED)    // a8-bf /* b2-b7 are the 'make' codes for the above keys */
INPUT_PORTS_END

#if 0

/* item keyboard */
static INPUT_PORTS_START( px4_h421a )
	PORT_INCLUDE(px4_dips)
INPUT_PORTS_END

#endif

//**************************************************************************
//  PALETTE
//**************************************************************************

void px4_state::px4_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}

void px4p_state::px4p_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(149, 157, 130));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}


//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

void px4_state::px4(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_z80, XTAL(7'372'800) / 2);    // uPD70008
	m_z80->set_addrmap(AS_PROGRAM, &px4_state::px4_mem);
	m_z80->set_addrmap(AS_IO, &px4_state::px4_io);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(72);
	screen.set_size(240, 64);
	screen.set_visarea(0, 239, 0, 63);
	screen.set_screen_update(FUNC(px4_state::screen_update_px4));
	screen.set_palette("palette");

	config.set_default_layout(layout_px4);

	PALETTE(config, "palette", FUNC(px4_state::px4_palette), 2);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.0);

	TIMER(config, "one_sec").configure_periodic(FUNC(px4_state::upd7508_1sec_callback), attotime::from_seconds(1));
	TIMER(config, "frc").configure_periodic(FUNC(px4_state::frc_tick), attotime::from_hz(XTAL(7'372'800) / 2 / 6));

	// internal ram
	RAM(config, RAM_TAG).set_default_size("64K");
	NVRAM(config, "nvram", nvram_device::DEFAULT_NONE);

	// centronics printer
	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(px4_state::centronics_busy_w));
	m_centronics->perror_handler().set(FUNC(px4_state::centronics_perror_w));

	output_latch_device &latch(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(latch);

	// external cassette
	CASSETTE(config, m_ext_cas);
	m_ext_cas->set_default_state(CASSETTE_PLAY | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_DISABLED);
	m_ext_cas->add_route(ALL_OUTPUTS, "mono", 0.05);

	TIMER(config, m_ext_cas_timer).configure_generic(FUNC(px4_state::ext_cassette_read));

	// sio port
	EPSON_SIO(config, m_sio, nullptr);
	m_sio->rx_callback().set(FUNC(px4_state::sio_rx_w));
	m_sio->pin_callback().set(FUNC(px4_state::sio_pin_w));

	// rs232 port
	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set(FUNC(px4_state::rs232_rx_w));
	m_rs232->dcd_handler().set(FUNC(px4_state::rs232_dcd_w));
	m_rs232->dsr_handler().set(FUNC(px4_state::rs232_dsr_w));
	m_rs232->cts_handler().set(FUNC(px4_state::rs232_cts_w));

	// rom capsules
	GENERIC_CARTSLOT(config, m_caps[0], generic_plain_slot, "px4_cart");
	GENERIC_CARTSLOT(config, m_caps[1], generic_plain_slot, "px4_cart");

	// software list
	SOFTWARE_LIST(config, "cart_list").set_original("px4_cart");
	SOFTWARE_LIST(config, "epson_cpm_list").set_original("epson_cpm");
}

void px4p_state::px4p(machine_config &config)
{
	px4(config);
	m_z80->set_addrmap(AS_IO, &px4p_state::px4p_io);

	NVRAM(config, "rdnvram", nvram_device::DEFAULT_ALL_0);

	subdevice<palette_device>("palette")->set_init(FUNC(px4p_state::px4p_palette));

	GENERIC_CARTSLOT(config, m_rdsocket, generic_plain_slot, "px4_cart");
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

// Note: We are missing "Kana OS V1.0" and "Kana OS V2.0" (Japanese version)

ROM_START( px4 )
	ROM_REGION(0x8000, "os", 0)
	ROM_SYSTEM_BIOS(0, "default",  "PX-4 OS ROM")
	ROMX_LOAD("m25122aa_po_px4.10c", 0x0000, 0x8000, CRC(62d60dc6) SHA1(3d32ec79a317de7c84c378302e95f48d56505502), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "ramtest",  "PX-4/PX-8 DRAM Test Ver. 1.0")
	ROMX_LOAD("ramtest.10c", 0x0000, 0x8000, CRC(f8aced5f) SHA1(a5a2f398e602aa349c3636d6659dd0c7eaba07fb), ROM_BIOS(1))

	ROM_REGION(0x1000, "slave", 0)
	ROM_LOAD("upd7508.bin", 0x0000, 0x1000, NO_DUMP)
ROM_END

ROM_START( px4p )
	ROM_REGION(0x8000, "os", 0)
	ROM_SYSTEM_BIOS(0, "default",  "PX-4+ OS ROM")
	ROMX_LOAD("b0_pxa.10c", 0x0000, 0x8000, CRC(d74b9ef5) SHA1(baceee076c12f5a16f7a26000e9bc395d021c455), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "ramtest",  "PX-4/PX-8 DRAM Test Ver. 1.0")
	ROMX_LOAD("ramtest.10c", 0x0000, 0x8000, CRC(f8aced5f) SHA1(a5a2f398e602aa349c3636d6659dd0c7eaba07fb), ROM_BIOS(1))

	ROM_REGION(0x1000, "slave", 0)
	ROM_LOAD("upd7508.bin", 0x0000, 0x1000, NO_DUMP)
ROM_END

} // anonymous namespace


//**************************************************************************
//  GAME DRIVERS
//**************************************************************************

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT      CLASS       INIT       COMPANY  FULLNAME  FLAGS
COMP( 1985, px4,  0,      0,      px4,     px4_h450a, px4_state,  init_px4,  "Epson", "PX-4",   0 )
COMP( 1985, px4p, px4,    0,      px4p,    px4_h450a, px4p_state, init_px4p, "Epson", "PX-4+",  0 )



px8.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Curt Coder,Dirk Best
/***************************************************************************

    Epson PX-8

    12/05/2009 Skeleton driver.

    Seems to be a CP/M computer.

***************************************************************************/

/*

    TODO:

    - dumps of the internal ROMs
    - uPD7508 CPU core
    - keyboard
    - cassette
    - display
    - jumpers
    - ROM capsule
    - uPD7001 (controlled by uPD7508)
    - RAM disk (64K/128K RAM, Z80, 4K ROM)
    - modem (82C55)
    - Multi-Unit (60K RAM, ROM capsule, modem, 82C55)
    - attach PF-10s

*/

#include "emu.h"
#include "px8.h"

#include "machine/rescap.h"
#include "machine/upd7001.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "px8.lh"

#include "utf8.h"


/***************************************************************************
    CONSTANTS
***************************************************************************/

#define XTAL_CR1        XTAL(9'830'400)
#define XTAL_CR2        XTAL(32'768)

/* interrupt sources */
#define INT0_7508       0x01
#define INT1_SERIAL     0x02
#define INT2_RS232      0x04
#define INT3_BARCODE    0x08
#define INT4_FRC        0x10
#define INT5_OPTION     0x20

enum
{
	GAH40M_CTLR1 = 0,
	GAH40M_CMDR,
	GAH40M_CTLR2,
	GAH40M_IER = 4
};

enum
{
	GAH40M_ICRL_C = 0,
	GAH40M_ICRH_C,
	GAH40M_ICRL_B,
	GAH40M_ICRH_B,
	GAH40M_ISR,
	GAH40M_STR,
	GAH40M_SIOR,
	GAH40M_IVR
};

/***************************************************************************
    READ/WRITE HANDLERS
***************************************************************************/

/*-------------------------------------------------
    bankswitch - memory bankswitching
-------------------------------------------------*/

void px8_state::bankswitch()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	uint8_t *ram = m_ram->pointer();
	uint8_t *ipl_rom = memregion(UPD70008_TAG)->base();

	if (!m_bank0)
	{
		/* IPL ROM */
		program.install_rom(0x0000, 0x7fff, ipl_rom);
	}
	else
	{
		if (m_bk2)
		{
			/* D-RAM (L) */
			program.install_ram(0x0000, 0x7fff, ram);
		}
		else
		{
			/* OPTION ROM (L) */
			program.unmap_readwrite(0x0000, 0x7fff);
		}
	}

	if (m_bk2)
	{
		/* D-RAM (H) */
		program.install_ram(0x8000, 0xffff, ram + 0x8000);
	}
	else
	{
		/* OPTION ROM (H) */
		program.unmap_readwrite(0x8000, 0xffff);
	}
}

/*-------------------------------------------------
    gah40m_r - GAH40M read
-------------------------------------------------*/

uint8_t px8_state::gah40m_r(offs_t offset)
{
	switch (offset)
	{
	case GAH40M_ICRL_C:
		/*

		    bit     signal      description

		    0       ICR0
		    1       ICR1
		    2       ICR2
		    3       ICR3
		    4       ICR4
		    5       ICR5
		    6       ICR6
		    7       ICR7

		*/
		break;

	case GAH40M_ICRH_C:
		/*

		    bit     signal      description

		    0       ICR8
		    1       ICR9
		    2       ICR10
		    3       ICR11
		    4       ICR12
		    5       ICR13
		    6       ICR14
		    7       ICR15

		*/
		break;

	case GAH40M_ICRL_B:
		/*

		    bit     signal      description

		    0       ICR0
		    1       ICR1
		    2       ICR2
		    3       ICR3
		    4       ICR4
		    5       ICR5
		    6       ICR6
		    7       ICR7

		*/
		break;

	case GAH40M_ICRH_B:
		/*

		    bit     signal      description

		    0       ICR8
		    1       ICR9
		    2       ICR10
		    3       ICR11
		    4       ICR12
		    5       ICR13
		    6       ICR14
		    7       ICR15

		*/
		break;

	case GAH40M_ISR:
		/*

		    bit     signal      description

		    0       INT0        7508 interrupt (INT 7508)
		    1       INT1        82C51 interrupt (INT 82C51)
		    2       INT2        6303 interrupt (INT 6303)
		    3       INT3        input capture flag timer (CF)
		    4       INT4        overflow flag timer (OVF)
		    5       INT5        external interrupt (INTEXT)
		    6
		    7

		*/
		break;

	case GAH40M_STR:
		/*

		    bit     signal      description

		    0       _BANK0
		    1       BRDT        barcode reader data timer
		    2       RDY         ready (SIO)
		    3       RDYSIO      SIO ready (SIO)
		    4
		    5
		    6
		    7

		*/
		break;

	case GAH40M_SIOR:
		/*

		    bit     signal      description

		    0       SIO0
		    1       SIO1
		    2       SIO2
		    3       SIO3
		    4       SIO4
		    5       SIO5
		    6       SIO6
		    7       SIO7

		*/
		break;

	case GAH40M_IVR:
		/*

		    bit     description

		    0       0
		    1       vect 1
		    2       vect 2
		    3       vect 3
		    4       1
		    5       1
		    6       1
		    7       1

		*/
		break;
	}

	return 0xff;
}

/*-------------------------------------------------
    gah40m_w - GAH40M write
-------------------------------------------------*/

void px8_state::gah40m_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case GAH40M_CTLR1:
		/*

		    bit     signal      description

		    0       _BANK0      bank switching
		    1       BCK0        barcode mode select 0 timer (down)
		    2       BCK1        barcode mode select 1 timer (up)
		    3       SWBCD       barcode reader switch timer
		    4       BRG0        bad rate generator select 0 timer
		    5       BRG1        bad rate generator select 1 timer
		    6       BRG2        bad rate generator select 2 timer
		    7       BRG3        bad rate generator select 3 timer

		*/

		m_bank0 = BIT(data, 0);
		bankswitch();
		break;

	case GAH40M_CMDR:
		/*

		    bit     description

		    0       set RDYSIOFF (pulse)
		    1       reset RDYSIOFF (pulse)
		    2       reset OVF (pulse)
		    3
		    4
		    5
		    6
		    7

		*/
		break;

	case GAH40M_CTLR2:
		/*

		    bit     signal      description

		    0       LED0        LED
		    1       LED1        LED
		    2       LED2        LED
		    3       SWRS        RS-232C switch
		    4       INHRS       inhibit RS-232C
		    5       AUX         external auxiliary output
		    6
		    7

		*/

		for (int i = 0; i < 3; i++)
			m_leds[i] = BIT(data, i);
		break;

	case GAH40M_IER:
		/*

		    bit     signal      description

		    0       IER0        INT 7508 enable
		    1       IER1        INT 82C51 enable
		    2       IER2        INT 6303 enable
		    3       IER3        INTICF enable
		    4       IER4        INTOVF enable
		    5       IER5        INTEXT enable
		    6
		    7

		*/

		m_ier = data;
		break;

	case GAH40M_SIOR:
		/*

		    bit     signal      description

		    0       SIO0
		    1       SIO1
		    2       SIO2
		    3       SIO3
		    4       SIO4
		    5       SIO5
		    6       SIO6
		    7       SIO7

		*/

		m_sio = data;
		break;
	}
}

/*-------------------------------------------------
    gah40s_r - GAH40S read
-------------------------------------------------*/

uint8_t px8_state::gah40s_r(offs_t offset)
{
	uint8_t data = 0xff;

	switch (offset)
	{
	case 0: /* counter (upper byte) input */
		data = (m_cnt >> 8) & 0x1f;
		break;

	case 1: /* counter (lower byte) */
		data = m_cnt & 0xff;
		break;

	case 3: /* P-ROM read data */
		data = m_prd;
		break;
	}

	return data;
}

/*-------------------------------------------------
    gah40s_w - GAH40S write
-------------------------------------------------*/

void px8_state::gah40s_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 0: /* counter reset */
		m_cnt = 0;
		break;

	case 1: /* command register */
		/*

		    bit     signal      description

		    0       SW PR       PROM power switch
		    1       SW MCT      microcassette power switch
		    2       MTA         microcassette drive motor control signal A
		    3       MTB         microcassette drive motor control signal B
		    4       MTC         microcassette drive motor control signal C
		    5
		    6       _FAST
		    7       _STOP CNT

		*/

		m_swpr = BIT(data, 0);
		break;

	case 2: /* P-ROM address (upper 8 byte) */
		m_pra = (data << 8) | (m_pra & 0xff);
		m_prd = 0; // TODO read prom!
		break;

	case 3: /* P-ROM address (lower 8 byte) */
		m_pra = (m_pra & 0xff00) | data;
		m_prd = 0; // TODO read prom!
		break;
	}
}

/*-------------------------------------------------
    gah40s_ier_w - interrupt enable register write
-------------------------------------------------*/

void px8_state::gah40s_ier_w(uint8_t data)
{
	m_ier = data;
}

/*-------------------------------------------------
   krtn_read - read keyboard return
-------------------------------------------------*/

uint8_t px8_state::krtn_read()
{
	uint8_t data = 0xff;

	switch (m_ksc)
	{
	case 0: data = ioport("KSC0")->read(); break;
	case 1: data = ioport("KSC1")->read(); break;
	case 2: data = ioport("KSC2")->read(); break;
	case 3: data = ioport("KSC3")->read(); break;
	case 4: data = ioport("KSC4")->read(); break;
	case 5: data = ioport("KSC5")->read(); break;
	case 6: data = ioport("KSC6")->read(); break;
	case 7: data = ioport("KSC7")->read(); break;
	case 8: data = ioport("KSC8")->read(); break;
	case 9: data = ioport("SW4")->read();  break;
	}

	return data;
}

/*-------------------------------------------------
   krtn_0_3_r - keyboard return 0..3 read
-------------------------------------------------*/

uint8_t px8_state::krtn_0_3_r()
{
	return krtn_read() & 0x0f;
}

/*-------------------------------------------------
   krtn_4_7_r - keyboard return 4..7 read
-------------------------------------------------*/

uint8_t px8_state::krtn_4_7_r()
{
	return krtn_read() >> 4;
}

/*-------------------------------------------------
   ksc_w - keyboard scan write
-------------------------------------------------*/

void px8_state::ksc_w(uint8_t data)
{
	m_ksc = data;
}

/***************************************************************************
    MEMORY MAPS
***************************************************************************/

/*-------------------------------------------------
    ADDRESS_MAP( px8_mem )
-------------------------------------------------*/

void px8_state::px8_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).bankrw("bank0");
	map(0x8000, 0xffff).bankrw("bank1");
}

/*-------------------------------------------------
    ADDRESS_MAP( px8_io )
-------------------------------------------------*/

void px8_state::px8_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x0f);
	map(0x00, 0x07).rw(FUNC(px8_state::gah40m_r), FUNC(px8_state::gah40m_w));
	map(0x0c, 0x0d).rw(I8251_TAG, FUNC(i8251_device::read), FUNC(i8251_device::write));
//  map(0x0e, 0x0e).rw(SED1320_TAG, FUNC(sed1330_device::status_r), FUNC(sed1330_device::data_w));
//  map(0x0f, 0x0f).rw(SED1320_TAG, FUNC(sed1330_device::data_r), FUNC(sed1330_device::command_w));
}

/*-------------------------------------------------
    ADDRESS_MAP( px8_slave_mem )
-------------------------------------------------*/

void px8_state::px8_slave_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0020, 0x0023).rw(FUNC(px8_state::gah40s_r), FUNC(px8_state::gah40s_w));
//  map(0x0024, 0x0027).rw(SED1320_TAG, FUNC(sed1330_device::), FUNC(sed1330_device::));
	map(0x0028, 0x0028).w(FUNC(px8_state::gah40s_ier_w));
	map(0x8000, 0x97ff).ram().share("video_ram");
	map(0x9800, 0xefff).noprw();
	map(0xf000, 0xffff).rom().region(HD6303_TAG, 0); /* internal mask rom */
}

/***************************************************************************
    INPUT PORTS
***************************************************************************/

static INPUT_PORTS_START( px8 )
	PORT_START("KSC0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SCRN INS") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Left CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Left SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CAPS LOCK") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Right CTRL") PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Right SHIFT") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("NUM GRPH") PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(RALT))

	PORT_START("KSC1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ESC") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PAUSE") PORT_CODE(KEYCODE_F2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("HELP") PORT_CODE(KEYCODE_F3)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PF1") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PF2") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PF3") PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PF4") PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PF5") PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F5))

	PORT_START("KSC2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("STOP") PORT_CODE(KEYCODE_ESC)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("KSC3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')

	PORT_START("KSC4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')

	PORT_START("KSC5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F9) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F10) PORT_CHAR(']') PORT_CHAR('}')

	PORT_START("KSC6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('_')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("HOME BS") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("TAB") PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')

	PORT_START("KSC7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@') PORT_CHAR(96)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')

	PORT_START("KSC8")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')

	PORT_START("SW4")
	PORT_DIPNAME( 0xef, 0x2f, "Character Set" ) PORT_DIPLOCATION("SW4:1,2,3,4,6,7,8")
	PORT_DIPSETTING(    0x2f, "ASCII" )
	PORT_DIPSETTING(    0x2e, DEF_STR( French ) )
	PORT_DIPSETTING(    0x2d, DEF_STR( German ) )
	PORT_DIPSETTING(    0x2c, DEF_STR( English ) )
	PORT_DIPSETTING(    0x2b, "Danish" )
	PORT_DIPSETTING(    0x2a, "Swedish" )
	PORT_DIPSETTING(    0x26, "Norwegian" )
	PORT_DIPSETTING(    0x29, "Italy" )
	PORT_DIPSETTING(    0x28, "Spain" )
	PORT_DIPSETTING(    0x60, "HASCI" )
	PORT_DIPSETTING(    0x21, "Japanese (Japanese)" )
	PORT_DIPSETTING(    0x00, "Japanese (JIS)" )
	PORT_DIPSETTING(    0x01, "Japanese (touch 16)" )
	PORT_DIPNAME( 0x10, 0x00, "RAM disk check" ) PORT_DIPLOCATION("SW4:5")
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
INPUT_PORTS_END

/***************************************************************************
    VIDEO
***************************************************************************/

void px8_state::px8_palette(palette_device &palette) const
{
	palette.set_pen_color(0, 0xa5, 0xad, 0xa5);
	palette.set_pen_color(1, 0x31, 0x39, 0x10);
}

uint32_t px8_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}

/*-------------------------------------------------
    gfx_layout px8_charlayout
-------------------------------------------------*/

static const gfx_layout px8_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

/*-------------------------------------------------
    GFXDECODE( px8 )
-------------------------------------------------*/

static GFXDECODE_START( gfx_px8 )
	GFXDECODE_ENTRY( SED1320_TAG, 0x0000, px8_charlayout, 0, 1 )
GFXDECODE_END

/***************************************************************************
    MACHINE INITIALIZATION
***************************************************************************/

/*-------------------------------------------------
    MACHINE_START( px8 )
-------------------------------------------------*/

void px8_state::machine_start()
{
	m_leds.resolve();

	/* register for state saving */
	save_item(NAME(m_ier));
	save_item(NAME(m_isr));
	save_item(NAME(m_bank0));
	save_item(NAME(m_bk2));
	save_item(NAME(m_sio));
	save_item(NAME(m_ksc));

	// not used yet
	(void)m_icr;
	(void)m_frc;
}

void px8_state::machine_reset()
{
	m_bank0 = 0;
	m_bk2 = 1;

	bankswitch();
}

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

void px8_state::px8(machine_config &config)
{
	/* main cpu (uPD70008) */
	Z80(config, m_maincpu, XTAL_CR1 / 4); /* 2.45 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &px8_state::px8_mem);
	m_maincpu->set_addrmap(AS_IO, &px8_state::px8_io);

	/* slave cpu (HD6303CA) */
	hd6301_cpu_device &slave(HD6301V1(config, HD6303_TAG, XTAL_CR1 / 4)); /* 614 kHz */
	slave.set_addrmap(AS_PROGRAM, &px8_state::px8_slave_mem);
	slave.set_disable();

	/* sub CPU (uPD7508) */
//  upd7508_device &sub(UPD7508(config, UPD7508_TAG, 200000)); /* 200 kHz */
//  sub.set_addrmap(AS_IO, &px8_state::px8_sub_io);
//  sub.set_disable();

	/* video hardware */
	config.set_default_layout(layout_px8);

	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_LCD));
	screen.set_refresh_hz(72);
	screen.set_screen_update(FUNC(px8_state::screen_update));
	screen.set_size(480, 64);
	screen.set_visarea(0, 479, 0, 63);
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_px8);
	PALETTE(config, "palette", FUNC(px8_state::px8_palette), 2);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	/* cartridge */
	GENERIC_CARTSLOT(config, "capsule1", generic_plain_slot, "px8_cart", "bin,rom");

	GENERIC_CARTSLOT(config, "capsule2", generic_plain_slot, "px8_cart", "bin,rom");

	/* devices */
	I8251(config, I8251_TAG, XTAL_CR1 / 4);

	UPD7001(config, UPD7001_TAG, RES_K(27), CAP_P(47));

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(0, "mono", 0.05);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("64K");

	// software
	SOFTWARE_LIST(config, "cart_list").set_original("px8_cart");
	SOFTWARE_LIST(config, "epson_cpm_list").set_original("epson_cpm");
}

/***************************************************************************
    ROMS
***************************************************************************/

ROM_START( px8 )
	ROM_REGION( 0x10000, UPD70008_TAG, 0 )
	ROM_DEFAULT_BIOS("052884")
	ROM_SYSTEM_BIOS( 0, "091383", "9/13/83" )
	ROMX_LOAD( "m25030aa.2a", 0x0000, 0x8000, CRC(bd3e4938) SHA1(5bd48abd2a563a1ae31ff137280f40c8f756e969), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "052884", "5/28/84" )
	ROMX_LOAD( "px060688.2a", 0x0000, 0x8000, CRC(44308bdf) SHA1(5c4545fcf1af9931b4699436294d9b6298052a7b), ROM_BIOS(1) )

	ROM_REGION( 0x0800, SED1320_TAG, 0 )
	ROM_LOAD( "font.rom", 0x0000, 0x0800, CRC(5b52edbd) SHA1(38197edf301bb2843bea040536af545f76b3d44f) )

	ROM_REGION( 0x1000, HD6303_TAG, 0 )
	ROM_LOAD( "hd6303 slave cpu internal rom.13d", 0x0000, 0x1000, NO_DUMP )

	ROM_REGION( 0x1000, UPD7508_TAG, 0 )
	ROM_LOAD( "upd7508 sub cpu internal rom.2e", 0x0000, 0x1000, NO_DUMP )

	// Possibly cartridges
	ROM_REGION( 0x8000, "carts", 0 )
	ROM_LOAD( "px8-util.rom",           0x00000, 0x8000, CRC(4430a271) SHA1(58c23a5f25ad9cdb70ada44dc773e6899e9bd8bf) ) // various utilities
ROM_END

/***************************************************************************
    SYSTEM DRIVERS
***************************************************************************/

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY  FULLNAME  FLAGS */
COMP( 1984, px8,  0,      0,      px8,     px8,   px8_state, empty_init, "Epson", "PX-8",   MACHINE_NOT_WORKING )



pyl601.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************

Pyldin-601

2009-05-12 Skeleton driver.
2012-04-22 Added sound, fixed keyboard, marked as working [Robbbert]

ToDo?
- PYL601 - command 'MODE80' does nothing

- PYL601a - most software looks odd (unplayable) because of the
            different design of the screen.
- PYL601A - command 'MODE40' doesn't go to 40-columns, instead
            there is a space between each letter.


The BASIC
- to get back to dos, enter SYSTEM
- It has its own internal monitor: MON to enter, Q to exit.



':maincpu' (F013): unmapped program memory write to E628 = 00 & FF
':maincpu' (FCBC): unmapped program memory write to E636 = 00 & FF
':maincpu' (FCBC): unmapped program memory write to E637 = 00 & FF
':maincpu' (FCC2): unmapped program memory write to E634 = 07 & FF
':maincpu' (FCC2): unmapped program memory write to E635 = FF & FF
':maincpu' (FCC7): unmapped program memory write to E637 = 34 & FF
':maincpu' (FCCC): unmapped program memory write to E637 = 3C & FF
':maincpu' (FCCF): unmapped program memory write to E636 = 3C & FF
':maincpu' (FCD3): unmapped program memory read from E634 & FF

****************************************************************************/

#include "emu.h"

#include "cpu/m6800/m6800.h"
#include "imagedev/floppy.h"
#include "machine/ram.h"
#include "machine/upd765.h"
#include "sound/spkrdev.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "softlist_dev.h"
#include "screen.h"
#include "speaker.h"

#include "formats/pyldin_dsk.h"

#include "utf8.h"


namespace {

class pyl601_state : public driver_device
{
public:
	pyl601_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_speaker(*this, "speaker")
		, m_fdc(*this, "upd765")
		, m_floppy(*this, "upd765:%u", 0U)
		, m_ram(*this, RAM_TAG)
		, m_maincpu(*this, "maincpu")
		, m_palette(*this, "palette")
	{ }

	void pyl601(machine_config &config);
	void pyl601a(machine_config &config);
	void init_pyl601();

private:
	uint8_t rom_page_r();
	void rom_page_w(uint8_t data);
	void vdisk_page_w(uint8_t data);
	void vdisk_h_w(uint8_t data);
	void vdisk_l_w(uint8_t data);
	void vdisk_data_w(uint8_t data);
	uint8_t vdisk_data_r();
	uint8_t keyboard_r();
	uint8_t keycheck_r();
	void video_mode_w(uint8_t data);
	uint8_t video_mode_r();
	uint8_t timer_r();
	void speaker_w(uint8_t data);
	void led_w(uint8_t data);
	void floppy_w(uint8_t data);
	uint8_t floppy_r();
	MC6845_UPDATE_ROW(pyl601_update_row);
	MC6845_UPDATE_ROW(pyl601a_update_row);
	uint8_t selectedline(uint16_t data);

	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	INTERRUPT_GEN_MEMBER(pyl601_interrupt);
	static void floppy_formats(format_registration &fr);
	void mem_map(address_map &map) ATTR_COLD;

	uint8_t m_rom_page = 0U;
	uint32_t m_vdisk_addr = 0U;
	uint8_t m_key_code = 0U;
	uint8_t m_keyboard_clk = 0U;
	uint8_t m_video_mode = 0U;
	uint8_t m_tick50_mark = 0U;
	uint8_t m_floppy_ctrl = 0U;

	required_device<speaker_sound_device> m_speaker;
	required_device<upd765a_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<ram_device> m_ram;
	required_device<cpu_device> m_maincpu;
	required_device<palette_device> m_palette;
};



uint8_t pyl601_state::rom_page_r()
{
	return m_rom_page;
}

void pyl601_state::rom_page_w(uint8_t data)
{
	m_rom_page = data;
	if (data & 8)
	{
		int chip = (data >> 4) % 5;
		int page = data & 7;
		membank("bank2")->set_base(memregion("romdisk")->base() + chip*0x10000 + page * 0x2000);
	}
	else
	{
		membank("bank2")->set_base(m_ram->pointer() + 0xc000);
	}
}


void pyl601_state::vdisk_page_w(uint8_t data)
{
	m_vdisk_addr = (m_vdisk_addr & 0x0ffff) | ((data & 0x0f)<<16);
}

void pyl601_state::vdisk_h_w(uint8_t data)
{
	m_vdisk_addr = (m_vdisk_addr & 0xf00ff) | (data<<8);
}

void pyl601_state::vdisk_l_w(uint8_t data)
{
	m_vdisk_addr = (m_vdisk_addr & 0xfff00) | data;
}

void pyl601_state::vdisk_data_w(uint8_t data)
{
	m_ram->pointer()[0x10000 + (m_vdisk_addr & 0x7ffff)] = data;
	m_vdisk_addr++;
	m_vdisk_addr&=0x7ffff;
}

uint8_t pyl601_state::vdisk_data_r()
{
	uint8_t retVal = m_ram->pointer()[0x10000 + (m_vdisk_addr & 0x7ffff)];
	m_vdisk_addr++;
	m_vdisk_addr &= 0x7ffff;
	return retVal;
}

uint8_t pyl601_state::selectedline(uint16_t data)
{
	uint8_t i;
	for(i = 0; i < 16; i++)
		if (BIT(data, i))
			return i;

	return 0;
}

uint8_t pyl601_state::keyboard_r()
{
	uint8_t ret = m_key_code;
	m_key_code = 0xff;
	return ret;
}

uint8_t pyl601_state::keycheck_r()
{
	uint8_t retVal = 0x3f;
	uint8_t *keyboard = memregion("keyboard")->base();
	uint16_t row1 = ioport("ROW1")->read();
	uint16_t row2 = ioport("ROW2")->read();
	uint16_t row3 = ioport("ROW3")->read();
	uint16_t row4 = ioport("ROW4")->read();
	uint16_t row5 = ioport("ROW5")->read();
	uint16_t all = row1 | row2 | row3 | row4 | row5;
	uint16_t addr = ioport("MODIFIERS")->read();
	if (all)
	{
		addr |= selectedline(all) << 2;

		addr |=  ((row5 == 0x00) ? 1 : 0) << 6;
		addr |=  ((row4 == 0x00) ? 1 : 0) << 7;
		addr |=  ((row3 == 0x00) ? 1 : 0) << 8;
		addr |=  ((row2 == 0x00) ? 1 : 0) << 9;
		addr |=  ((row1 == 0x00) ? 1 : 0) << 10;

		m_key_code = keyboard[addr];
		m_keyboard_clk = ~m_keyboard_clk;

		if (m_keyboard_clk)
			retVal |= 0x80;
	}
	return retVal;
}


void pyl601_state::video_mode_w(uint8_t data)
{
	m_video_mode = data;
}

uint8_t pyl601_state::video_mode_r()
{
	return m_video_mode;
}

uint8_t pyl601_state::timer_r()
{
	uint8_t retVal= m_tick50_mark | 0x37;
	m_tick50_mark = 0;
	return retVal;
}

void pyl601_state::speaker_w(uint8_t data)
{
	m_speaker->level_w(BIT(data, 3));
}

void pyl601_state::led_w(uint8_t data)
{
//  uint8_t caps_led = BIT(data,4);
}

void pyl601_state::floppy_w(uint8_t data)
{
	// bit 0 is reset (if zero)
	// bit 1 is TC state
	// bit 2 is drive selected
	// bit 3 is motor state

	if (BIT(data,0)==0)
		//reset
		m_fdc->reset();

	floppy_image_device *floppy = m_floppy[BIT(data, 2)]->get_device();
	if(floppy)
		floppy->mon_w(!BIT(data, 3));

	m_fdc->tc_w(BIT(data, 1));

	m_floppy_ctrl = data;
}

uint8_t pyl601_state::floppy_r()
{
	return m_floppy_ctrl;
}

void pyl601_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xbfff).bankrw("bank1");
	map(0xc000, 0xdfff).bankrw("bank2");
	map(0xe000, 0xe5ff).bankrw("bank3");
	map(0xe600, 0xe600).mirror(4).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0xe601, 0xe601).mirror(4).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0xe628, 0xe628).r(FUNC(pyl601_state::keyboard_r));
	map(0xe629, 0xe629).rw(FUNC(pyl601_state::video_mode_r), FUNC(pyl601_state::video_mode_w));
	map(0xe62a, 0xe62a).rw(FUNC(pyl601_state::keycheck_r), FUNC(pyl601_state::led_w));
	map(0xe62b, 0xe62b).rw(FUNC(pyl601_state::timer_r), FUNC(pyl601_state::speaker_w));
	map(0xe62d, 0xe62d).r(FUNC(pyl601_state::video_mode_r));
	map(0xe62e, 0xe62e).rw(FUNC(pyl601_state::keycheck_r), FUNC(pyl601_state::led_w));
	map(0xe680, 0xe680).w(FUNC(pyl601_state::vdisk_page_w));
	map(0xe681, 0xe681).w(FUNC(pyl601_state::vdisk_h_w));
	map(0xe682, 0xe682).w(FUNC(pyl601_state::vdisk_l_w));
	map(0xe683, 0xe683).rw(FUNC(pyl601_state::vdisk_data_r), FUNC(pyl601_state::vdisk_data_w));
	map(0xe6c0, 0xe6c0).rw(FUNC(pyl601_state::floppy_r), FUNC(pyl601_state::floppy_w));
	map(0xe6d0, 0xe6d1).m(m_fdc, FUNC(upd765a_device::map));
	map(0xe6f0, 0xe6f0).rw(FUNC(pyl601_state::rom_page_r), FUNC(pyl601_state::rom_page_w));
	map(0xe700, 0xefff).bankrw("bank4");
	map(0xf000, 0xffff).rom().region("maincpu",0).bankw("bank6");
}

/* Input ports */
/* A small note about natural keyboard mode: Ctrl is mapped to PGUP and the 'Lat/Cyr' key is mapped to PGDOWN */
static INPUT_PORTS_START( pyl601 )
	PORT_START("ROW1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Lat/Cyr") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(PGDN))
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Del") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')

	PORT_START("ROW2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')    // it should be the 4th key at right of 'L'
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ROW3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('`') PORT_CHAR('~')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')

	PORT_START("ROW4")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Caps Lock") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("ROW5")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PGDN) PORT_CHAR(UCHAR_MAMEKEY(F15))
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PGUP) PORT_CHAR(UCHAR_MAMEKEY(F14))
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(F13))
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F12) PORT_CHAR(UCHAR_MAMEKEY(F12))
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11))
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))

	PORT_START("MODIFIERS")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(PGUP))
INPUT_PORTS_END

void pyl601_state::machine_reset()
{
	uint8_t *ram = m_ram->pointer();
	m_key_code = 0xff;
	membank("bank1")->set_base(ram + 0x0000);
	membank("bank2")->set_base(ram + 0xc000);
	membank("bank3")->set_base(ram + 0xe000);
	membank("bank4")->set_base(ram + 0xe700);
	membank("bank6")->set_base(ram + 0xf000);

	m_maincpu->reset();
}

void pyl601_state::machine_start()
{
	save_item(NAME(m_rom_page));
	save_item(NAME(m_vdisk_addr));
	save_item(NAME(m_key_code));
	save_item(NAME(m_keyboard_clk));
	save_item(NAME(m_video_mode));
	save_item(NAME(m_tick50_mark));
	save_item(NAME(m_floppy_ctrl));
}

MC6845_UPDATE_ROW( pyl601_state::pyl601_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	uint8_t const *const charrom = memregion("chargen")->base();

	if (BIT(m_video_mode, 5) == 0)
	{
		for (int column = 0; column < x_count; column++)
		{
			uint8_t code = m_ram->pointer()[(((ma + column) & 0x0fff) + 0xf000)];
			code = ((code << 1) | (code >> 7)) & 0xff;
			uint8_t data;
			if (column == cursor_x-2)
				data = 0xff;
			else
				data = charrom[((code << 3) | (ra & 0x07)) & 0x7ff];

			for (int bit = 0; bit < 8; bit++)
			{
				int x = (column * 8) + bit;

				bitmap.pix(y, x) = palette[BIT(data, 7)];

				data <<= 1;
			}
		}
	}
	else
	{
		for (int i = 0; i < x_count; i++)
		{
			uint8_t data = m_ram->pointer()[(((ma + i) << 3) | (ra & 0x07)) & 0xffff];
			for (int bit = 0; bit < 8; bit++)
			{
				bitmap.pix(y, (i * 8) + bit) = palette[BIT(data, 7)];
				data <<= 1;
			}
		}
	}
}

MC6845_UPDATE_ROW( pyl601_state::pyl601a_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	uint8_t const *const charrom = memregion("chargen")->base();

	if (BIT(m_video_mode, 5) == 0)
	{
		for (int column = 0; column < x_count; column++)
		{
			uint8_t const code = m_ram->pointer()[(((ma + column) & 0x0fff) + 0xf000)];
			uint8_t data = charrom[((code << 4) | (ra & 0x07)) & 0xfff];
			if (column == cursor_x)
				data = 0xff;

			for (int bit = 0; bit < 8; bit++)
			{
				int const x = (column * 8) + bit;

				bitmap.pix(y, x) = palette[BIT(data, 7)];

				data <<= 1;
			}
		}
	}
	else
	{
		for (int i = 0; i < x_count; i++)
		{
			uint8_t data = m_ram->pointer()[(((ma + i) << 3) | (ra & 0x07)) & 0xffff];
			for (int bit = 0; bit < 8; bit++)
			{
				bitmap.pix(y, (i * 8) + bit) = palette[BIT(data, 7)];
				data <<= 1;
			}
		}
	}
}



void pyl601_state::init_pyl601()
{
	memset(m_ram->pointer(), 0, 64 * 1024);
}

INTERRUPT_GEN_MEMBER(pyl601_state::pyl601_interrupt)
{
	m_tick50_mark = 0x80;
	device.execute().set_input_line(0, HOLD_LINE);
}

void pyl601_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_PYLDIN_FORMAT);
}

static void pyl601_floppies(device_slot_interface &device)
{
	device.option_add("525hd", FLOPPY_525_HD);
}

/* F4 Character Displayer */
static const gfx_layout pyl601_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static const gfx_layout pyl601a_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_pyl601 )
	GFXDECODE_ENTRY( "chargen", 0x0000, pyl601_charlayout, 0, 1 )
GFXDECODE_END

static GFXDECODE_START( gfx_pyl601a )
	GFXDECODE_ENTRY( "chargen", 0x0000, pyl601a_charlayout, 0, 1 )
GFXDECODE_END

void pyl601_state::pyl601(machine_config &config)
{
	/* basic machine hardware */
	M6800(config, m_maincpu, 1_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &pyl601_state::mem_map);
	m_maincpu->set_vblank_int("screen", FUNC(pyl601_state::pyl601_interrupt));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 200);
	screen.set_visarea(0, 640 - 1, 0, 200 - 1);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	GFXDECODE(config, "gfxdecode", m_palette, gfx_pyl601);
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* Devices */
	mc6845_device &crtc(MC6845(config, "crtc", 2_MHz_XTAL));
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(8);   /* ? */
	crtc.set_update_row_callback(FUNC(pyl601_state::pyl601_update_row));

	UPD765A(config, m_fdc, 8'000'000, true, true);
	FLOPPY_CONNECTOR(config, "upd765:0", pyl601_floppies, "525hd", pyl601_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "upd765:1", pyl601_floppies, "525hd", pyl601_state::floppy_formats);
	SOFTWARE_LIST(config, "flop_list").set_original("pyl601");

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("576K"); // 64 + 512
}

void pyl601_state::pyl601a(machine_config &config)
{
	pyl601(config);

	m_maincpu->set_clock(2_MHz_XTAL);

	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_pyl601a);

	subdevice<mc6845_device>("crtc")->set_update_row_callback(FUNC(pyl601_state::pyl601a_update_row));
}

/* ROM definition */
ROM_START( pyl601 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "bios.rom",   0x0000, 0x1000, CRC(41fe4c4b) SHA1(d8ca92aea0eb283e8d7779cb976bcdfa03e81aea))

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD( "video.rom",  0x0000, 0x0800, CRC(1c23ba43) SHA1(eb1cfc139858abd0aedbbf3d523f8ba55d27a11d))

	ROM_REGION(0x50000, "romdisk", 0 )
	ROM_LOAD( "rom0.rom", 0x00000, 0x10000, CRC(60103920) SHA1(ee5b4ee5b513c4a0204da751e53d63b8c6c0aab9))
	ROM_LOAD( "rom1.rom", 0x10000, 0x10000, CRC(cb4a9b22) SHA1(dd09e4ba35b8d1a6f60e6e262aaf2f156367e385))
	ROM_LOAD( "rom2.rom", 0x20000, 0x08000, CRC(0b7684bf) SHA1(c02ad1f2a6f484cd9d178d8b060c21c0d4e53442))
	ROM_COPY( "romdisk",  0x20000, 0x28000, 0x08000)
	ROM_LOAD( "rom3.rom", 0x30000, 0x08000, CRC(e4a86dfa) SHA1(96e6bb9ffd66f81fca63bf7491fbba81c4ff1fd2))
	ROM_COPY( "romdisk",  0x30000, 0x38000, 0x08000)
	ROM_LOAD( "rom4.rom", 0x40000, 0x08000, CRC(d88ac21d) SHA1(022db11fdcf8db81ce9efd9cd9fa50ebca88e79e))
	ROM_COPY( "romdisk",  0x40000, 0x48000, 0x08000)

	ROM_REGION(0x0800, "keyboard", 0)
	ROM_LOAD( "keyboard.rom", 0x0000, 0x0800, CRC(41fbe5ca) SHA1(875adaef53bc37e92ad0b6b6ee3d8fd28344d358))
ROM_END

ROM_START( pyl601a )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "bios_a.rom", 0x0000, 0x1000, CRC(e018b11e) SHA1(884d59abd5fa5af1295d1b5a53693facc7945b63))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "video_a.rom", 0x0000,0x1000, CRC(00fa4077) SHA1(d39d15969a08bdb768d08bea4ec9a9cb498232fd))

	ROM_REGION(0x50000, "romdisk", 0 )
	ROM_LOAD( "rom0.rom", 0x00000, 0x10000, CRC(60103920) SHA1(ee5b4ee5b513c4a0204da751e53d63b8c6c0aab9))
	ROM_LOAD( "rom1.rom", 0x10000, 0x10000, CRC(cb4a9b22) SHA1(dd09e4ba35b8d1a6f60e6e262aaf2f156367e385))
	ROM_LOAD( "rom2.rom", 0x20000, 0x08000, CRC(0b7684bf) SHA1(c02ad1f2a6f484cd9d178d8b060c21c0d4e53442))
	ROM_COPY( "romdisk",  0x20000, 0x28000, 0x08000)
	ROM_LOAD( "rom3.rom", 0x30000, 0x08000, CRC(e4a86dfa) SHA1(96e6bb9ffd66f81fca63bf7491fbba81c4ff1fd2))
	ROM_COPY( "romdisk",  0x30000, 0x38000, 0x08000)
	ROM_LOAD( "rom4.rom", 0x40000, 0x08000, CRC(d88ac21d) SHA1(022db11fdcf8db81ce9efd9cd9fa50ebca88e79e))
	ROM_COPY( "romdisk",  0x40000, 0x48000, 0x08000)

	ROM_REGION(0x0800, "keyboard", 0)
	ROM_LOAD( "keyboard.rom", 0x0000, 0x0800, CRC(41fbe5ca) SHA1(875adaef53bc37e92ad0b6b6ee3d8fd28344d358))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT         COMPANY             FULLNAME       FLAGS */
COMP( 1989, pyl601,  0,      0,      pyl601,  pyl601, pyl601_state, init_pyl601, "Mikroelektronika", "Pyldin-601",  MACHINE_SUPPORTS_SAVE )
COMP( 1989, pyl601a, pyl601, 0,      pyl601a, pyl601, pyl601_state, init_pyl601, "Mikroelektronika", "Pyldin-601A", MACHINE_SUPPORTS_SAVE )



pylo.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle, Frank Palazzolo
/*******************************************************************************

Tiger Punch Your Lights Out (model 7-571)

Hardware notes:
- PCB label: 7-571A
- TI TMS70C40 @ 4MHz, custom label but decap shows it's a TMS70C40F with
  ROM serial C61013, 4KB internal ROM is half empty
- 4 7segs, 4*6 other leds, piezo

There's also a version with a Toshiba TMP47C200.

*******************************************************************************/

#include "emu.h"

#include "cpu/tms7000/tms7000.h"
#include "sound/spkrdev.h"
#include "video/pwm.h"

#include "speaker.h"

#include "pylo.lh"


namespace {

class pylo_state : public driver_device
{
public:
	pylo_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_speaker(*this, "speaker"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void pylo(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<tms70c40_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<speaker_sound_device> m_speaker;
	required_ioport_array<6> m_inputs;

	u8 m_inp_mux = 0;
	u8 m_digit_data = 0;
	u8 m_led_data = 0;

	void update_display();
	u8 input_r();
	void input_w(u8 data);
	void digit_w(u8 data);
	void led_w(u8 data);
};

void pylo_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_digit_data));
	save_item(NAME(m_led_data));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void pylo_state::update_display()
{
	m_display->matrix(m_inp_mux >> 2, m_led_data << 7 | m_digit_data);
}

u8 pylo_state::input_r()
{
	u8 data = 0;

	// A0-A7: multiplexed inputs
	for (int i = 0; i < 6; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read();

	return data;
}

void pylo_state::input_w(u8 data)
{
	// B0-B5: input mux
	// B2-B5: digit/led select
	// B6,B7: N/C
	m_inp_mux = data & 0x3f;
	update_display();
}

void pylo_state::digit_w(u8 data)
{
	// C0-C6: digit segments
	// C7: N/C
	m_digit_data = ~data & 0x7f;
	update_display();
}

void pylo_state::led_w(u8 data)
{
	// D0-D5: led data
	m_led_data = ~data & 0x3f;
	update_display();

	// D6: N/C
	// D7: speaker out
	m_speaker->level_w(BIT(~data, 7));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( pylo )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_CONFNAME( 0x0e, 0x02, "Game" )
	PORT_CONFSETTING(    0x02, "1" )
	PORT_CONFSETTING(    0x04, "2" )
	PORT_CONFSETTING(    0x08, "3" )
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON1)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_BUTTON2)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_BUTTON3)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_BUTTON4)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_START1)
	PORT_BIT(0x0e, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON5)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_BUTTON6)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_BUTTON7)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_BUTTON8)

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_START2)
	PORT_BIT(0x0e, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON9)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_BUTTON10)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_BUTTON11)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_BUTTON12)

	PORT_START("IN.3")
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_COCKTAIL
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_COCKTAIL
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_COCKTAIL
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_BUTTON4) PORT_COCKTAIL

	PORT_START("IN.4")
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON5) PORT_COCKTAIL
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_BUTTON6) PORT_COCKTAIL
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_BUTTON7) PORT_COCKTAIL
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_BUTTON8) PORT_COCKTAIL

	PORT_START("IN.5")
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON9) PORT_COCKTAIL
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_BUTTON10) PORT_COCKTAIL
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_BUTTON11) PORT_COCKTAIL
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_BUTTON12) PORT_COCKTAIL
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void pylo_state::pylo(machine_config &config)
{
	// basic machine hardware
	TMS70C40(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->in_porta().set(FUNC(pylo_state::input_r));
	m_maincpu->out_portb().set(FUNC(pylo_state::input_w));
	m_maincpu->out_portc().set(FUNC(pylo_state::digit_w));
	m_maincpu->out_portd().set(FUNC(pylo_state::led_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 13);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_pylo);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( pylo )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("1-7571a1-2.u1", 0x0000, 0x1000, CRC(0cb01059) SHA1(68a422cef6513e8eed2d83d492e2be11d049d183) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY, FULLNAME, FLAGS
SYST( 1988, pylo, 0,      0,      pylo,    pylo,  pylo_state, empty_init, "Tiger", "Punch Your Lights Out", MACHINE_SUPPORTS_SAVE )



ql.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder, Phill Harvey-Smith
/*

    Sinclair QL

*/

/*

    TODO:

    - microdrive
    - ZX8301 memory access slowdown
    - use resnet.h to create palette
    - Tyche bios is broken
    - several disk interfaces (720K/1.44MB/3.2MB)
    - Gold Card (68000 @ 16MHz, 2MB RAM)
    - Super Gold Card (68020 @ 24MHz, 4MB RAM)
    - QLToolkit II ROM
    - GST 68kOS ROM
    - natural keyboard SHIFT does not work, it causes characters to be skipped altogether
    - get modified Danish version to boot (e.g. MD_DESEL was patched to jump to 0x1cd5e)
    - ICL One Per Desk / British Telecom Merlin Tonto / Telecom Australia Computerphone / Mega OPD (US)
    - OPD: 68008P8 @ 7.5MHz, ZX8301 @ 12MHz, CDP65C51E1 @ 1.8432MHz, 128K RAM, 2K NVRAM, 128K ROM (4x32), 16K speech ROM, ADM7910 modem, TMS5220 speech, 2x100K microdrives, RTC 1/1/1970, 1xRS-423
    - Sandy Q-XT 640 (original motherboard, Super QBoard 512KB, Centronics, Super Toolkit II, FDC, 1 or 2 3.5" 1MB drives, 3 expansion slots)
    - CST Thor PC 1F (720K 3.5")
    - CST Thor PC 2F (2x 720k 3.5")
    - CST Thor PC 2FW (20MB Winchester + 720K 3.5")
    - CST Thor 20 (68020)
    - CST Thor 21 (68020+68881)
    - CST Thor XVI CF    (Workstation, 68000 @ 8 MHz, M6802, 68682 DUART)
    - CST Thor XVI IF    (Single Floppy)
    - CST Thor XVI FF    (Dual Floppy)
    - CST Thor XVI W20F  (20MB Winchester, 1 Floppy)
    - CST Thor XVI W20FF (20MB Winchester, 2 Floppies)
    - CST Thor XVI W40F  (40MB Winchester, 1 Floppy)
    - CST Thor XVI W40FF (40MB Winchester, 2 Floppies)


    2011-02-22, P.Harvey-Smith,
        Implemented Miracle Trump Card disk and ram interface.

        The trump card has some interesting memory mapping and re-mapping during initialisation which goes like this

        On reset the card ram is mapped into the entire $40000-$fffff area, this is the official 512K expansion ram area
        plus the area set aside for addon card roms. The trump card onboard rom is also mapped in at $10000-$17fff.

        The main bios then performs a series of loops that initialise and size the ram, this will find either
        0K, 256K, 512K or 768K of expansion ram depending on how many banks of ram are populated.

        After the ram test, the main bios looks for the signature longword of a cartridge rom at $0c000, this read
        triggers the Trump Card to also map it's rom in at $c0000-$c7fff, the first addon rom area. If a cartridge
        rom is found it is first initialised.

        The main bios goes on to search the addon card rom area from $c0000 by searching for the signature word for
        an addon rom at $c0000 and upwards in 16K steps (there is a bug in JM and before that means they only find
        the first addon, but this does not matter).

        The main bios finds the trump card addon rom at $c0000, and calls it's initialisation routine at $c011e, this
        contains a jump instruction to jump to $10124 (in the lower mapped copy of the trumpcard rom).

        The first memory access in the $10000-$17fff range triggers the trumcard to page out the rom at $c0000, and
        page the ram back in. Once it arrives at this state the ram will remain paged in until the next reset.

        I have implemented this with a pair of read handlers at $0c000-$0cfff and $10000-$17fff which do the apropreate
        re-map of the upper area and then remove themselves from the address map and return it to simple rom handlers.

        On the Trump card the above is implemented with a pair of 16L8 PAL chips, the logic of these was decoded with
        the assistance of Malcolm Lear, without who's help working all this out would have been a much harder process.

*/

#include "emu.h"

#include "bus/ql/exp.h"
#include "bus/ql/rom.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68008.h"
#include "cpu/mcs48/mcs48.h"
#include "imagedev/microdrv.h"
#include "qimi.h"
#include "machine/ram.h"
#include "zx8302.h"
#include "sound/spkrdev.h"
#include "zx8301.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "utf8.h"


namespace {

#define SCREEN_TAG  "screen"

#define M68008_TAG  "ic18"
#define I8749_TAG   "ic24"
#define I8051_TAG   "i8051"
#define ZX8301_TAG  "ic22"
#define ZX8302_TAG  "ic23"
#define RS232_A_TAG "ser1"
#define RS232_B_TAG "ser2"
#define QIMI_TAG    "qimi"

#define X1 XTAL(15'000'000)
#define X2 XTAL(32'768)
#define X3 XTAL_4_436MHz
#define X4 XTAL(11'000'000)

class ql_state : public driver_device
{
public:
	ql_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, M68008_TAG),
		m_ipc(*this, I8749_TAG),
		m_zx8301(*this, ZX8301_TAG),
		m_zx8302(*this, ZX8302_TAG),
		m_speaker(*this, "speaker"),
		m_mdv1(*this, MDV_1),
		m_mdv2(*this, MDV_2),
		m_ser1(*this, RS232_A_TAG),
		m_ser2(*this, RS232_B_TAG),
		m_ram(*this, RAM_TAG),
		m_exp(*this, "exp"),
		m_cart(*this, "rom"),
		m_qimi(*this, QIMI_TAG),
		m_rom(*this, M68008_TAG),
		m_y(*this, "Y%u", 0),
		m_joy(*this, "JOY%u", 0),
		m_config(*this, "config"),
		m_extintl(CLEAR_LINE),
		m_keylatch(0),
		m_ipl(0),
		m_comdata_to_ipc(0),
		m_baudx4(0),
		m_qimi_enabled(false),
		m_qimi_extint(CLEAR_LINE)
	{ }

	void ql_ntsc(machine_config &config);
	void opd(machine_config &config);
	void ql(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	required_device<i8749_device> m_ipc;
	required_device<zx8301_device> m_zx8301;
	required_device<zx8302_device> m_zx8302;
	required_device<speaker_sound_device> m_speaker;
	required_device<microdrive_image_device> m_mdv1;
	required_device<microdrive_image_device> m_mdv2;
	required_device<rs232_port_device> m_ser1;
	required_device<rs232_port_device> m_ser2;
	required_device<ram_device> m_ram;
	required_device<ql_expansion_slot_device> m_exp;
	required_device<ql_rom_cartridge_slot_device> m_cart;
	optional_device<qimi_device> m_qimi;
	required_memory_region m_rom;
	required_ioport_array<8> m_y;
	required_ioport_array<2> m_joy;
	required_ioport m_config;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint8_t read(offs_t offset);
	void write(offs_t offset, uint8_t data);

	void ipc_w(uint8_t data);
	void ipc_port1_w(uint8_t data);
	void ipc_port2_w(uint8_t data);
	uint8_t ipc_port2_r();
	int ipc_t1_r();
	uint8_t ipc_bus_r();
	void ql_baudx4_w(int state);
	void ql_comdata_w(int state);
	void zx8302_mdselck_w(int state);
	void zx8302_mdrdw_w(int state);
	void zx8302_erase_w(int state);
	void zx8302_raw1_w(int state);
	int zx8302_raw1_r();
	void zx8302_raw2_w(int state);
	int zx8302_raw2_r();
	void exp_extintl_w(int state);
	void qimi_extintl_w(int state);

	void update_interrupt();

	int m_extintl;

	// IPC
	uint8_t m_keylatch;
	int m_ipl;
	int m_comdata_to_ipc;
	int m_baudx4;

	// QIMI
	bool m_qimi_enabled;
	int m_qimi_extint;

	void ipc_io(address_map &map) ATTR_COLD;
	void ql_mem(address_map &map) ATTR_COLD;
};



//**************************************************************************
//  ADDRESS DECODING
//**************************************************************************

//-------------------------------------------------
//  read -
//-------------------------------------------------

uint8_t ql_state::read(offs_t offset)
{
	uint8_t data = 0;
	int cart_romoeh = 0;
	int exp_romoeh = 0;

	if (offset < 0xc000)
	{
		data = m_rom->base()[offset];
	}
	if (offset >= 0xc000 && offset < 0x10000)
	{
		cart_romoeh = 1;
	}
	if (offset >= 0x18000 && offset <= 0x18003)
	{
		data = m_zx8302->rtc_r(offset & 0x03);
	}
	if (offset == 0x18020)
	{
		data = m_zx8302->status_r();
	}
	if (offset == 0x18021)
	{
		data = m_zx8302->irq_status_r();
	}
	if (offset >= 0x18022 && offset <= 0x18023)
	{
		data = m_zx8302->mdv_track_r();
	}
	if (offset >= 0x20000 && offset < 0x40000)
	{
		data = m_zx8301->data_r(offset & 0x1ffff);
	}
	if (offset >= 0xc0000)
	{
		exp_romoeh = 1;
	}
	if (m_qimi_enabled)
	{
		data = m_qimi->read(offset, data);
	}

	m_cart->romoeh_w(cart_romoeh);
	data = m_cart->read(offset & 0x7fff, data);
	m_cart->romoeh_w(0);

	m_exp->romoeh_w(exp_romoeh);
	data = m_exp->read(offset, data);
	m_exp->romoeh_w(0);

	return data;
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

void ql_state::write(offs_t offset, uint8_t data)
{
	if (offset >= 0x18000 && offset <= 0x18001)
	{
		m_zx8302->rtc_w(data);
	}
	if (offset == 0x18002)
	{
		m_zx8302->control_w(data);
	}
	if (offset == 0x18003)
	{
		m_zx8302->ipc_command_w(data);
	}
	if (offset == 0x18020)
	{
		m_zx8302->mdv_control_w(data);
	}
	if (offset == 0x18021)
	{
		m_zx8302->irq_acknowledge_w(data);
	}
	if (offset == 0x18022)
	{
		m_zx8302->data_w(data);
	}
	if (offset == 0x18063)
	{
		m_zx8301->control_w(data);
	}
	if (offset >= 0x20000 && offset < 0x40000)
	{
			m_zx8301->data_w(offset & 0x1ffff, data);
	}
	if (m_qimi_enabled)
	{
		m_qimi->write(offset, data);
	}

	m_cart->romoeh_w(0);
	m_cart->write(offset & 0x7fff, data);

	m_exp->romoeh_w(0);
	m_exp->write(offset, data);
}



//**************************************************************************
//  INTELLIGENT PERIPHERAL CONTROLLER
//**************************************************************************

//-------------------------------------------------
//  ipc_w -
//-------------------------------------------------

void ql_state::ipc_w(uint8_t data)
{
	m_zx8302->comctl_w(0);
	m_zx8302->comctl_w(1);
}


//-------------------------------------------------
//  ipc_port1_w -
//-------------------------------------------------

void ql_state::ipc_port1_w(uint8_t data)
{
	/*

	    bit     description

	    0       Keyboard column output (KBO0)
	    1       Keyboard column output (KBO1)
	    2       Keyboard column output (KBO2)
	    3       Keyboard column output (KBO3)
	    4       Keyboard column output (KBO4)
	    5       Keyboard column output (KBO5)
	    6       Keyboard column output (KBO6)
	    7       Keyboard column output (KBO7)

	*/

	m_keylatch = data;
}


//-------------------------------------------------
//  ipc_port2_r -
//-------------------------------------------------

uint8_t ql_state::ipc_port2_r()
{
	/*

	    bit     description

	    0       Serial data input (SER2 RxD, SER1 TxD)
	    1
	    2
	    3
	    4
	    5
	    6
	    7       ZX8302 serial link input/output (COMDATA)

	*/

	uint8_t data = 0;

	// SER2 serial data input
	data |= m_ser2->rxd_r();

	// COMDATA
	data |= m_comdata_to_ipc << 7;

	return data;
}


//-------------------------------------------------
//  ipc_port2_w -
//-------------------------------------------------

void ql_state::ipc_port2_w(uint8_t data)
{
	/*

	    bit     description

	    0
	    1       Speaker output
	    2       Interrupt output (IPL0-2)
	    3       Interrupt output (IPL1)
	    4       Serial Clear-to-Send output (SER1 CTS)
	    5       Serial Data Terminal Ready output (SER2 DTR)
	    6
	    7       ZX8302 serial link input/output (COMDATA)

	*/

	// speaker
	m_speaker->level_w(BIT(data, 1));

	// interrupts
	int ipl = (BIT(data, 2) << 1) | BIT(data, 3);

	if (ipl != m_ipl)
	{
		switch (ipl)
		{
		case 0: m_maincpu->set_input_line(M68K_IRQ_7, ASSERT_LINE); break;
		case 1: m_maincpu->set_input_line(M68K_IRQ_5, ASSERT_LINE); break; // CTRL-ALT-7 pressed
		case 2: m_maincpu->set_input_line(M68K_IRQ_2, ASSERT_LINE); break;
		case 3: m_maincpu->set_input_line(M68K_IRQ_7, CLEAR_LINE);  break;
		}

		m_ipl = ipl;
	}

	// TODO SER1 clear to send

	// SER2 data terminal ready
	m_ser2->write_dtr(!BIT(data, 5));

	// COMDATA
	m_zx8302->comdata_w(BIT(data, 7));
}


//-------------------------------------------------
//  ipc_t1_r -
//-------------------------------------------------

int ql_state::ipc_t1_r()
{
	return m_baudx4;
}


//-------------------------------------------------
//  ipc_bus_r -
//-------------------------------------------------

uint8_t ql_state::ipc_bus_r()
{
	/*

	    bit     description

	    0       Keyboard row input (KBI0)
	    1       Keyboard row input (KBI1)
	    2       Keyboard row input (KBI2)
	    3       Keyboard row input (KBI3)
	    4       Keyboard row input (KBI4)
	    5       Keyboard row input (KBI5)
	    6       Keyboard row input (KBI6)
	    7       Keyboard row input (KBI7)

	*/

	uint8_t data = 0;

	if (BIT(m_keylatch, 0)) data |= m_y[0]->read() | m_joy[0]->read();
	if (BIT(m_keylatch, 1)) data |= m_y[1]->read() | m_joy[1]->read();
	if (BIT(m_keylatch, 2)) data |= m_y[2]->read();
	if (BIT(m_keylatch, 3)) data |= m_y[3]->read();
	if (BIT(m_keylatch, 4)) data |= m_y[4]->read();
	if (BIT(m_keylatch, 5)) data |= m_y[5]->read();
	if (BIT(m_keylatch, 6)) data |= m_y[6]->read();
	if (BIT(m_keylatch, 7)) data |= m_y[7]->read();

	return data;
}



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( ql_mem )
//-------------------------------------------------

void ql_state::ql_mem(address_map &map)
{
	map(0x000000, 0x0fffff).rw(FUNC(ql_state::read), FUNC(ql_state::write));
}


//-------------------------------------------------
//  ADDRESS_MAP( ipc_io )
//-------------------------------------------------

void ql_state::ipc_io(address_map &map)
{
	map(0x00, 0x7f).w(FUNC(ql_state::ipc_w));
	map(0x27, 0x28).nopr(); // IPC reads these to set P0 (bus) to Hi-Z mode
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( ql )
//-------------------------------------------------

static INPUT_PORTS_START( ql )
	PORT_START("Y0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_NAME("F4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_NAME("F1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_NAME("F2")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_NAME("F3")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_NAME("F5")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')

	PORT_START("Y1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("ESC  @") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC)) PORT_CHAR(0x1B) PORT_CHAR('@')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))

	PORT_START("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_CHAR(U'£') PORT_CHAR('~')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('\"')

	PORT_START("Y3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CAPS LOCK") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')

	PORT_START("Y4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')

	PORT_START("Y5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("TABULATE") PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("Y6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')

	PORT_START("Y7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("ALT") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT)) PORT_CODE(KEYCODE_RALT)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("JOY0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )   PORT_PLAYER(1) PORT_8WAY PORT_CODE(KEYCODE_F4)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_8WAY PORT_CODE(KEYCODE_F1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_8WAY PORT_CODE(KEYCODE_F2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )PORT_PLAYER(1) PORT_8WAY PORT_CODE(KEYCODE_F3)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON1 )       PORT_PLAYER(1) PORT_CODE(KEYCODE_F5)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("JOY1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_8WAY PORT_CODE(KEYCODE_LEFT)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )   PORT_PLAYER(2) PORT_8WAY PORT_CODE(KEYCODE_UP)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )PORT_PLAYER(2) PORT_8WAY PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON1 )       PORT_PLAYER(2) PORT_CODE(KEYCODE_SPACE)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_8WAY PORT_CODE(KEYCODE_DOWN)

	PORT_START("config")
	PORT_CONFNAME( 0x01, 0x00, "QL Internal Mouse Interface (QIMI)")
	PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
	PORT_CONFSETTING( 0x01, DEF_STR( On ) )
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( ql_es )
//-------------------------------------------------

static INPUT_PORTS_START( ql_es )
	PORT_INCLUDE(ql)

	PORT_MODIFY("Y1")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(']') PORT_CHAR(U'Ü',U'ü')

	PORT_MODIFY("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('`') PORT_CHAR('^')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('!')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('[') PORT_CHAR(U'Ç',U'ç')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(';') PORT_CHAR(':')

	PORT_MODIFY("Y3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(U'Ñ',U'ñ')

	PORT_MODIFY("Y4")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR(U'¡')

	PORT_MODIFY("Y6")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR(U'¿')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('/')

	PORT_MODIFY("Y7")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('<') PORT_CHAR('>')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('?')
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( ql_de )
//-------------------------------------------------

static INPUT_PORTS_START( ql_de )
	PORT_INCLUDE(ql)

	PORT_MODIFY("Y0")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('/')

	PORT_MODIFY("Y1")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('<') PORT_CHAR('>')

	PORT_MODIFY("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('+') PORT_CHAR('*')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(':')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('\\') PORT_CHAR('^')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(U'Ä',U'ä')

	PORT_MODIFY("Y3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(U'Ü',U'ü')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('#') PORT_CHAR('\'')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(U'Ö',U'ö')

	PORT_MODIFY("Y4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR(U'£')

	PORT_MODIFY("Y5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR(U'ß') PORT_CHAR('?')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')

	PORT_MODIFY("Y6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('=')

	PORT_MODIFY("Y7")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR(';')
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( ql_it )
//-------------------------------------------------

static INPUT_PORTS_START( ql_it )
	PORT_INCLUDE(ql)

	PORT_MODIFY("Y0")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('(') PORT_CHAR('5')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('\'') PORT_CHAR('4')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR(U'è') PORT_CHAR('7')

	PORT_MODIFY("Y1")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('<') PORT_CHAR('>')

	PORT_MODIFY("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('$') PORT_CHAR('&')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR(':') PORT_CHAR('/')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('*') PORT_CHAR(U'§')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('?')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(U'ù') PORT_CHAR('%')

	PORT_MODIFY("Y3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(U'ì') PORT_CHAR('=')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('-') PORT_CHAR('+')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')

	PORT_MODIFY("Y4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('"') PORT_CHAR('3')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('#') PORT_CHAR('1')

	PORT_MODIFY("Y5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR(U'ç') PORT_CHAR('9')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR(')') PORT_CHAR('\\')

	PORT_MODIFY("Y6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('^') PORT_CHAR('8')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR(U'é') PORT_CHAR('2')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('_') PORT_CHAR('6')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR(U'à') PORT_CHAR('0')

	PORT_MODIFY("Y7")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR(U'ò') PORT_CHAR('!')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('.')
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( ql_fr )
//-------------------------------------------------

static INPUT_PORTS_START( ql_fr )
	PORT_INCLUDE(ql)
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( ql_se )
//-------------------------------------------------

static INPUT_PORTS_START( ql_se )
	PORT_INCLUDE(ql)
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( ql_dk )
//-------------------------------------------------

static INPUT_PORTS_START( ql_dk )
	PORT_INCLUDE(ql)
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  ZX8302_INTERFACE( ql_zx8302_intf )
//-------------------------------------------------

void ql_state::ql_baudx4_w(int state)
{
	m_baudx4 = state;
}

// CPU to IPC
void ql_state::ql_comdata_w(int state)
{
	m_comdata_to_ipc = state;
}

void ql_state::zx8302_mdselck_w(int state)
{
	m_mdv2->clk_w(state);
	m_mdv1->clk_w(state);
}

void ql_state::zx8302_mdrdw_w(int state)
{
	m_mdv1->read_write_w(state);
	m_mdv2->read_write_w(state);
}

void ql_state::zx8302_erase_w(int state)
{
	m_mdv1->erase_w(state);
	m_mdv2->erase_w(state);
}

void ql_state::zx8302_raw1_w(int state)
{
	m_mdv1->data1_w(state);
	m_mdv2->data1_w(state);
}

int ql_state::zx8302_raw1_r()
{
	return m_mdv1->data1_r() | m_mdv2->data1_r();
}

void ql_state::zx8302_raw2_w(int state)
{
	m_mdv1->data2_w(state);
	m_mdv2->data2_w(state);
}

int ql_state::zx8302_raw2_r()
{
	return m_mdv1->data2_r() | m_mdv2->data2_r();
}

void ql_state::update_interrupt()
{
	m_zx8302->extint_w(m_extintl || m_qimi_extint);
}

void ql_state::exp_extintl_w(int state)
{
	m_extintl = state;
	update_interrupt();
}

void ql_state::qimi_extintl_w(int state)
{
	if (m_qimi_enabled)
	{
		m_qimi_extint = state;
		update_interrupt();
	}
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

void ql_state::machine_start()
{
	// register for state saving
	save_item(NAME(m_keylatch));
	save_item(NAME(m_ipl));
	save_item(NAME(m_comdata_to_ipc));
	save_item(NAME(m_baudx4));
}

void ql_state::machine_reset()
{
	// QIMI
	m_qimi_enabled = (m_config->read() & 0x01) ? true : false;
}



//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  machine_config( ql )
//-------------------------------------------------

void ql_state::ql(machine_config &config)
{
	// basic machine hardware
	M68008(config, m_maincpu, X1/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &ql_state::ql_mem);

	I8749(config, m_ipc, X4);
	m_ipc->set_addrmap(AS_IO, &ql_state::ipc_io);
	m_ipc->p1_out_cb().set(FUNC(ql_state::ipc_port1_w));
	m_ipc->p2_in_cb().set(FUNC(ql_state::ipc_port2_r));
	m_ipc->p2_out_cb().set(FUNC(ql_state::ipc_port2_w));
	m_ipc->t1_in_cb().set(FUNC(ql_state::ipc_t1_r));
	m_ipc->bus_in_cb().set(FUNC(ql_state::ipc_bus_r));

	// video hardware
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50.08);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // not accurate
	screen.set_screen_update(ZX8301_TAG, FUNC(zx8301_device::screen_update));
	screen.set_size(960, 312);
	screen.set_visarea(0, 512-1, 0, 256-1);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	// devices
	ZX8301(config, m_zx8301, X1, m_maincpu);
	m_zx8301->vsync_wr_callback().set(m_zx8302, FUNC(zx8302_device::vsync_w));
	m_zx8301->set_screen(SCREEN_TAG);

	ZX8302(config, m_zx8302, X1);
	m_zx8302->set_rtc_clock(X2);
	m_zx8302->out_ipl1l_callback().set_inputline(m_maincpu, M68K_IRQ_2);
	m_zx8302->out_baudx4_callback().set(FUNC(ql_state::ql_baudx4_w));
	m_zx8302->out_comdata_callback().set(FUNC(ql_state::ql_comdata_w));
	// TXD1
	m_zx8302->out_txd2_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_txd));
	// NETOUT
	m_zx8302->out_mdselck_callback().set(FUNC(ql_state::zx8302_mdselck_w));
	m_zx8302->out_mdseld_callback().set(m_mdv1, FUNC(microdrive_image_device::comms_in_w));
	m_zx8302->out_mdrdw_callback().set(FUNC(ql_state::zx8302_mdrdw_w));
	m_zx8302->out_erase_callback().set(FUNC(ql_state::zx8302_erase_w));
	m_zx8302->out_raw1_callback().set(FUNC(ql_state::zx8302_raw1_w));
	m_zx8302->in_raw1_callback().set(FUNC(ql_state::zx8302_raw1_r));
	m_zx8302->out_raw2_callback().set(FUNC(ql_state::zx8302_raw2_w));
	m_zx8302->in_raw2_callback().set(FUNC(ql_state::zx8302_raw2_r));

	MICRODRIVE(config, m_mdv1);
	m_mdv1->comms_out_wr_callback().set(m_mdv2, FUNC(microdrive_image_device::comms_in_w));
	MICRODRIVE(config, m_mdv2);

	RS232_PORT(config, m_ser1, default_rs232_devices, nullptr); // wired as DCE
	RS232_PORT(config, m_ser2, default_rs232_devices, nullptr); // wired as DTE
	m_ser2->cts_handler().set(m_zx8302, FUNC(zx8302_device::write_cts2));

	QL_EXPANSION_SLOT(config, m_exp, 0, ql_expansion_cards, nullptr); // FIXME: what's the clock on the slot?
	//m_exp->ipl0l_wr_callback().set();
	//m_exp->ipl1l_wr_callback().set();(
	//m_exp->berrl_wr_callback().set();
	m_exp->extintl_wr_callback().set(FUNC(ql_state::exp_extintl_w));

	QL_ROM_CARTRIDGE_SLOT(config, m_cart, ql_rom_cartridge_cards, nullptr);

	QIMI(config, m_qimi, 0);
	m_qimi->extint_wr_callback().set(FUNC(ql_state::qimi_extintl_w));

	// software lists
	SOFTWARE_LIST(config, "cart_list").set_original("ql_cart");
	SOFTWARE_LIST(config, "cass_list").set_original("ql_cass");
	SOFTWARE_LIST(config, "flop_list").set_original("ql_flop");

	// internal ram
	RAM(config, m_ram).set_default_size("128K");
}


//-------------------------------------------------
//  machine_config( ql_ntsc )
//-------------------------------------------------

void ql_state::ql_ntsc(machine_config &config)
{
	ql(config);
	// video hardware
	screen_device &screen(*subdevice<screen_device>(SCREEN_TAG));
	screen.set_refresh_hz(60);
	screen.set_size(960, 262);
	screen.set_visarea(0, 512-1, 0, 256-1);
	}


//-------------------------------------------------
//  machine_config( opd )
//-------------------------------------------------

void ql_state::opd(machine_config &config)
{
	ql(config);
	// internal ram
	m_ram->set_default_size("128K").set_extra_options("256K");
}


/*
//-------------------------------------------------
//  machine_config( megaopd )
//-------------------------------------------------

void ql_state::megaopd(machine_config &config)
{
    ql(config);
    // internal ram
    m_ram->set_default_size("256K");
}
*/


//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( ql )
//-------------------------------------------------

ROM_START( ql )
	ROM_REGION( 0x10000, M68008_TAG, 0 )
	ROM_DEFAULT_BIOS("js")
	ROM_SYSTEM_BIOS( 0, "fb", "v1.00 (FB)" )
	ROMX_LOAD( "fb.ic33", 0x0000, 0x8000, NO_DUMP, ROM_BIOS(0) )
	ROMX_LOAD( "fb.ic34", 0x8000, 0x4000, NO_DUMP, ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "pm", "v1.01 (PM)" )
	ROMX_LOAD( "pm.ic33", 0x0000, 0x8000, NO_DUMP, ROM_BIOS(1) )
	ROMX_LOAD( "pm.ic34", 0x8000, 0x4000, NO_DUMP, ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "ah", "v1.02 (AH)" )
	ROMX_LOAD( "ah.ic33.1", 0x0000, 0x4000, BAD_DUMP CRC(a9b4d2df) SHA1(142d6f01a9621aff5e0ad678bd3cbf5cde0db801), ROM_BIOS(2) )
	ROMX_LOAD( "ah.ic33.2", 0x4000, 0x4000, BAD_DUMP CRC(36488e4e) SHA1(ff6f597b30ea03ce480a3d6728fd1d858da34d6a), ROM_BIOS(2) )
	ROMX_LOAD( "ah.ic34",   0x8000, 0x4000, BAD_DUMP CRC(61259d4c) SHA1(bdd10d111e7ba488551a27c8d3b2743917ff1307), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "jm", "v1.03 (JM)" )
	ROMX_LOAD( "ql.jm 0000.ic33", 0x0000, 0x8000, CRC(1f8e840a) SHA1(7929e716dfe88318bbe99e34f47d039957fe3cc0), ROM_BIOS(3) )
	ROMX_LOAD( "ql.jm 8000.ic34", 0x8000, 0x4000, CRC(9168a2e9) SHA1(1e7c47a59fc40bd96dfefc2f4d86827c15f0199e), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "tb", "v1.0? (TB)" )
	ROMX_LOAD( "tb.ic33", 0x0000, 0x8000, BAD_DUMP CRC(1c86d688) SHA1(7df8028e6671afc4ebd5f65bf6c2d6019181f239), ROM_BIOS(4) )
	ROMX_LOAD( "tb.ic34", 0x8000, 0x4000, BAD_DUMP CRC(de7f9669) SHA1(9d6bc0b794541a4cec2203256ae92c7e68d1011d), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "js", "v1.10 (JS)" )
	ROMX_LOAD( "ql.js 0000.ic33", 0x0000, 0x8000, CRC(1bbad3b8) SHA1(59fd4372771a630967ee102760f4652904d7d5fa), ROM_BIOS(5) )
	ROMX_LOAD( "ql.js 8000.ic34", 0x8000, 0x4000, CRC(c970800e) SHA1(b8c9203026a7de6a44bd0942ec9343e8b222cb41), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6, "tyche", "v2.05 (Tyche)" )
	ROMX_LOAD( "tyche.rom", 0x0000, 0x010000, CRC(8724b495) SHA1(5f33a1bc3f23fd09c31844b65bc3aca7616f180a), ROM_BIOS(6) )
	ROM_SYSTEM_BIOS( 7, "min189", "Minerva v1.89" )
	ROMX_LOAD( "minerva.rom", 0x0000, 0x00c000, BAD_DUMP CRC(930befe3) SHA1(84a99c4df13b97f90baf1ec8cb6c2e52e3e1bb4d), ROM_BIOS(7) )

	ROM_REGION( 0x800, I8749_TAG, 0 )
	ROM_LOAD( "ipc8049.ic24", 0x000, 0x800, CRC(6a0d1f20) SHA1(fcb1c97ee7c66e5b6d8fbb57c06fd2f6509f2e1b) )

	ROM_REGION( 0x400, "plds", 0 )
	ROM_LOAD( "hal16l8.ic38", 0x0000, 0x0400, NO_DUMP )

	ROM_REGION( 0x4000, "printer", 0 ) // original Sinclair QL printer (based on Seikosha SP-1000 / SP-800QL, with custom QL font)
	ROM_LOAD( "bql010-sqpp", 0x0000, 0x4000, CRC(07834797) SHA1(ba94bdad2303a263008b6ea744669a19938d9998) )
ROM_END


//-------------------------------------------------
//  ROM( ql_us )
//-------------------------------------------------

ROM_START( ql_us )
	ROM_REGION( 0xc000, M68008_TAG, 0 )
	ROM_LOAD( "jsu.ic33", 0x0000, 0x8000, BAD_DUMP CRC(e397f49f) SHA1(c06f92eabaf3e6dd298c51cb7f7535d8ef0ef9c5) )
	ROM_LOAD( "jsu.ic34", 0x8000, 0x4000, BAD_DUMP CRC(3debbacc) SHA1(9fbc3e42ec463fa42f9c535d63780ff53a9313ec) )

	ROM_REGION( 0x800, I8749_TAG, 0 )
	ROM_LOAD( "ipc8049.ic24", 0x000, 0x800, CRC(6a0d1f20) SHA1(fcb1c97ee7c66e5b6d8fbb57c06fd2f6509f2e1b) )

	ROM_REGION( 0x400, "plds", 0 )
	ROM_LOAD( "hal16l8.ic38", 0x0000, 0x0400, NO_DUMP )
ROM_END


//-------------------------------------------------
//  ROM( ql_es )
//-------------------------------------------------

ROM_START( ql_es )
	ROM_REGION( 0xc000, M68008_TAG, 0 )
	ROM_LOAD( "mge.ic33", 0x0000, 0x8000, BAD_DUMP CRC(d5293bde) SHA1(bf5af7e53a472d4e9871f182210787d601db0634) )
	ROM_LOAD( "mge.ic34", 0x8000, 0x4000, BAD_DUMP CRC(a694f8d7) SHA1(bd2868656008de85d7c191598588017ae8aa3339) )

	ROM_REGION( 0x800, I8749_TAG, 0 )
	ROM_LOAD( "ipc8049.ic24", 0x000, 0x800, CRC(6a0d1f20) SHA1(fcb1c97ee7c66e5b6d8fbb57c06fd2f6509f2e1b) )

	ROM_REGION( 0x400, "plds", 0 )
	ROM_LOAD( "hal16l8.ic38", 0x0000, 0x0400, NO_DUMP )
ROM_END


//-------------------------------------------------
//  ROM( ql_fr )
//-------------------------------------------------

ROM_START( ql_fr )
	ROM_REGION( 0xc000, M68008_TAG, 0 )
	ROM_LOAD( "mgf.ic33", 0x0000, 0x8000, NO_DUMP )
	ROM_LOAD( "mgf.ic34", 0x8000, 0x4000, NO_DUMP )

	ROM_REGION( 0x800, I8749_TAG, 0 )
	ROM_LOAD( "ipc8049.ic24", 0x000, 0x800, CRC(6a0d1f20) SHA1(fcb1c97ee7c66e5b6d8fbb57c06fd2f6509f2e1b) )

	ROM_REGION( 0x400, "plds", 0 )
	ROM_LOAD( "hal16l8.ic38", 0x0000, 0x0400, NO_DUMP )
ROM_END


//-------------------------------------------------
//  ROM( ql_de )
//-------------------------------------------------

ROM_START( ql_de )
	ROM_REGION( 0xc000, M68008_TAG, 0 )
	ROM_SYSTEM_BIOS( 0, "mg", "v1.10 (MG)" )
	ROMX_LOAD( "mgg.ic33", 0x0000, 0x8000, BAD_DUMP CRC(b4e468fd) SHA1(cd02a3cd79af90d48b65077d0571efc2f12f146e), ROM_BIOS(0) )
	ROMX_LOAD( "mgg.ic34", 0x8000, 0x4000, BAD_DUMP CRC(54959d40) SHA1(ffc0be9649f26019d7be82925c18dc699259877f), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "mf", "v1.14 (MF)" )
	ROMX_LOAD( "mf.ic33", 0x0000, 0x8000, BAD_DUMP CRC(49c40563) SHA1(d3bcd0614cf9b52e9d7fc2832e11463e5030476b), ROM_BIOS(1) )
	ROMX_LOAD( "mf.ic34", 0x8000, 0x4000, BAD_DUMP CRC(5974616b) SHA1(c3603768c08535c25f077eed02fb80128aff13d9), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "ultramg", "Ultrasoft" )
	ROMX_LOAD( "ultramg.rom", 0x0000, 0x00c000, BAD_DUMP CRC(ad12463b) SHA1(0561b3bc7ce090f3101b2142ee957c18c250eefa), ROM_BIOS(2) )

	ROM_REGION( 0x800, I8749_TAG, 0 )
	ROM_LOAD( "ipc8049.ic24", 0x000, 0x800, CRC(6a0d1f20) SHA1(fcb1c97ee7c66e5b6d8fbb57c06fd2f6509f2e1b) )

	ROM_REGION( 0x400, "plds", 0 )
	ROM_LOAD( "hal16l8.ic38", 0x0000, 0x0400, NO_DUMP )
ROM_END


//-------------------------------------------------
//  ROM( ql_it )
//-------------------------------------------------

ROM_START( ql_it )
	ROM_REGION( 0xc000, M68008_TAG, 0 )
	ROM_LOAD( "mgi.ic33", 0x0000, 0x8000, BAD_DUMP CRC(d5293bde) SHA1(bf5af7e53a472d4e9871f182210787d601db0634) )
	ROM_LOAD( "mgi.ic34", 0x8000, 0x4000, BAD_DUMP CRC(a2fdfb83) SHA1(162b1052737500f3c13497cdf0f813ba006bdae9) )

	ROM_REGION( 0x800, I8749_TAG, 0 )
	ROM_LOAD( "ipc8049.ic24", 0x000, 0x800, CRC(6a0d1f20) SHA1(fcb1c97ee7c66e5b6d8fbb57c06fd2f6509f2e1b) )

	ROM_REGION( 0x400, "plds", 0 )
	ROM_LOAD( "hal16l8.ic38", 0x0000, 0x0400, NO_DUMP )
ROM_END


//-------------------------------------------------
//  ROM( ql_se )
//-------------------------------------------------

ROM_START( ql_se )
	ROM_REGION( 0xc000, M68008_TAG, 0 )
	ROM_LOAD( "mgs.ic33", 0x0000, 0x8000, NO_DUMP )
	ROM_LOAD( "mgs.ic34", 0x8000, 0x4000, NO_DUMP )

	ROM_REGION( 0x800, I8749_TAG, 0 )
	ROM_LOAD( "ipc8049.ic24", 0x000, 0x800, CRC(6a0d1f20) SHA1(fcb1c97ee7c66e5b6d8fbb57c06fd2f6509f2e1b) )

	ROM_REGION( 0x400, "plds", 0 )
	ROM_LOAD( "hal16l8.ic38", 0x0000, 0x0400, NO_DUMP )
ROM_END


//-------------------------------------------------
//  ROM( ql_gr )
//-------------------------------------------------

ROM_START( ql_gr )
	ROM_REGION( 0xc000, M68008_TAG, 0 )
	ROM_LOAD( "efp.ic33", 0x0000, 0x8000, BAD_DUMP CRC(eb181641) SHA1(43c1e0215cf540cbbda240b1048910ff55681059) )
	ROM_LOAD( "efp.ic34", 0x8000, 0x4000, BAD_DUMP CRC(4c3b34b7) SHA1(f9dc571d2d4f68520b306ecc7516acaeea69ec0d) )

	ROM_REGION( 0x800, I8749_TAG, 0 )
	ROM_LOAD( "ipc8049.ic24", 0x000, 0x800, CRC(6a0d1f20) SHA1(fcb1c97ee7c66e5b6d8fbb57c06fd2f6509f2e1b) )

	ROM_REGION( 0x400, "plds", 0 )
	ROM_LOAD( "hal16l8.ic38", 0x0000, 0x0400, NO_DUMP )
ROM_END


//-------------------------------------------------
//  ROM( ql_dk )
//-------------------------------------------------

ROM_START( ql_dk )
	ROM_REGION( 0xc000, M68008_TAG, 0 )
	ROM_LOAD( "mgd.ic33",  0x0000, 0x8000, BAD_DUMP CRC(f57755eb) SHA1(dc57939ffb8741e17967a1d2479c339750ec7ff6) )
	ROM_LOAD( "mgd.ic34",  0x8000, 0x4000, BAD_DUMP CRC(1892465a) SHA1(0ff3046b5276da6639d3fe79b22ae25cc265d540) )

	ROM_REGION( 0x4000, "extra", 0 )
	ROM_LOAD( "extra.rom", 0x0000, 0x4000, NO_DUMP ) // located at 0x1c000 in M68008 memory map

	ROM_REGION( 0x800, I8749_TAG, 0 )
	ROM_LOAD( "ipc8049.ic24", 0x000, 0x800, CRC(6a0d1f20) SHA1(fcb1c97ee7c66e5b6d8fbb57c06fd2f6509f2e1b) )

	ROM_REGION( 0x400, "plds", 0 )
	ROM_LOAD( "hal16l8.ic38", 0x0000, 0x0400, NO_DUMP )
ROM_END


//-------------------------------------------------
//  ROM( tonto )
//-------------------------------------------------

ROM_START( tonto )
	ROM_REGION( 0x20000, M68008_TAG, 0 )
	ROM_LOAD( "xbaa02.ic4", 0x000000, 0x008000, CRC(86e7915b) SHA1(a4d8369052eaea93d2174cfd3b14e6cf777f54b4) )
	ROM_LOAD( "xbab03.ic5", 0x008000, 0x008000, CRC(97ef393c) SHA1(450c708e8dfbd42d939a9af6a72ef2a33a3dd3b5) )
	ROM_LOAD( "xbac02.ic6", 0x010000, 0x008000, CRC(a7950897) SHA1(7cd4d6e33a350420a9ebd5c1b32708c29cb20799) )
	ROM_LOAD( "xbad02.ic7", 0x018000, 0x008000, CRC(69d59443) SHA1(cdf28b1b4fca00e8cb79930a1963955af2925618) )

	ROM_REGION( 0x800, I8749_TAG, 0 )
	ROM_LOAD( "ipc8049.ic24", 0x000, 0x800, CRC(6a0d1f20) SHA1(fcb1c97ee7c66e5b6d8fbb57c06fd2f6509f2e1b) )

	ROM_REGION( 0x10000, I8051_TAG, 0 )
	ROM_LOAD( "8051-1.rom", 0x000000, 0x010000, NO_DUMP )

	ROM_REGION( 0x10000, "tms5220", 0 )
	ROM_LOAD( "tms5220.rom", 0x000000, 0x004000, NO_DUMP )

	ROM_REGION( 0x400000, "rompack", 0 )
	ROM_LOAD( "xbak02.ic6", 0x000000, 0x008000, CRC(3d61e84e) SHA1(7d0a473b0ed3e3ae3cd6d3fb43cb7a70a550d1d9) )
	ROM_LOAD( "xbaf01.ic7", 0x008000, 0x008000, CRC(c5724357) SHA1(6e765f8e85d2312c31e4b09c9a6645ab3d166e65) )
	ROM_LOAD( "xbag01.ic8", 0x010000, 0x008000, CRC(9af3bcbf) SHA1(9a848926f2bf6f632ab198a8bb03a8ee83ea0e5d) )
	ROM_LOAD( "qcaw02.ic9", 0x018000, 0x008000, CRC(1091a789) SHA1(33881b2a818cbaf610a0746fde4acfef5c91338f) )
	ROM_LOAD( "qcax02.ic10", 0x020000, 0x008000, CRC(396f13d9) SHA1(b2deb5db4565ff7224730c2f3245fbf9df84d728) )
ROM_END


#if 0
//-------------------------------------------------
//  ROM( megaopd )
//-------------------------------------------------

ROM_START( megaopd )
	ROM_REGION( 0x20000, M68008_TAG, 0 )
	ROM_LOAD( "bios-1.rom", 0x000000, 0x008000, NO_DUMP )
	ROM_LOAD( "bios-2.rom", 0x008000, 0x008000, NO_DUMP )
	ROM_LOAD( "bios-3.rom", 0x010000, 0x008000, NO_DUMP )
	ROM_LOAD( "bios-4.rom", 0x018000, 0x008000, NO_DUMP )

	ROM_REGION( 0x10000, I8051_TAG, 0 )
	ROM_LOAD( "8051-1.rom", 0x000000, 0x010000, NO_DUMP )

	ROM_REGION( 0x10000, "tms5220", 0 )
	ROM_LOAD( "tms5220.rom", 0x000000, 0x004000, NO_DUMP )

	ROM_REGION( 0x400000, "rompack", 0 )
	ROM_LOAD( "rompack-1.rom", 0x000000, 0x008000, NO_DUMP )
	ROM_LOAD( "rompack-2.rom", 0x008000, 0x008000, NO_DUMP )
	ROM_LOAD( "rompack-3.rom", 0x010000, 0x008000, NO_DUMP )
	ROM_LOAD( "rompack-4.rom", 0x018000, 0x008000, NO_DUMP )
	ROM_LOAD( "rompack-5.rom", 0x020000, 0x008000, NO_DUMP )
ROM_END
#endif

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME     PARENT  COMPAT  MACHINE     INPUT   CLASS     INIT        COMPANY                             FULLNAME              FLAGS
COMP( 1984, ql,      0,      0,      ql,         ql,     ql_state, empty_init, "Sinclair Research Ltd",            "QL (UK)",            MACHINE_SUPPORTS_SAVE )
COMP( 1985, ql_us,   ql,     0,      ql_ntsc,    ql,     ql_state, empty_init, "Sinclair Research Ltd",            "QL (USA)",           MACHINE_SUPPORTS_SAVE )
COMP( 1985, ql_es,   ql,     0,      ql,         ql_es,  ql_state, empty_init, "Sinclair Research Ltd",            "QL (Spain)",         MACHINE_SUPPORTS_SAVE )
COMP( 1985, ql_fr,   ql,     0,      ql,         ql_fr,  ql_state, empty_init, "Sinclair Research Ltd",            "QL (France)",        MACHINE_NOT_WORKING )
COMP( 1985, ql_de,   ql,     0,      ql,         ql_de,  ql_state, empty_init, "Sinclair Research Ltd",            "QL (Germany)",       MACHINE_SUPPORTS_SAVE )
COMP( 1985, ql_it,   ql,     0,      ql,         ql_it,  ql_state, empty_init, "Sinclair Research Ltd",            "QL (Italy)",         MACHINE_SUPPORTS_SAVE )
COMP( 1985, ql_se,   ql,     0,      ql,         ql_se,  ql_state, empty_init, "Sinclair Research Ltd",            "QL (Sweden)",        MACHINE_NOT_WORKING )
COMP( 1985, ql_dk,   ql,     0,      ql,         ql_dk,  ql_state, empty_init, "Sinclair Research Ltd",            "QL (Denmark)",       MACHINE_NOT_WORKING )
COMP( 1985, ql_gr,   ql,     0,      ql,         ql,     ql_state, empty_init, "Sinclair Research Ltd",            "QL (Greece)",        MACHINE_SUPPORTS_SAVE )
COMP( 1984, tonto,   0,      0,      opd,        ql,     ql_state, empty_init, "British Telecom Business Systems", "Merlin M1800 Tonto", MACHINE_NOT_WORKING )
//COMP( 1986, megaopd, tonto,  0,      megaopd,    ql,     ql_state, empty_init, "International Computer Limited",   "MegaOPD (USA)",      MACHINE_NOT_WORKING )



qmquasar.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for Quasimidi Quasar & TechnoX synthesizer modules.

***************************************************************************/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/6850acia.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"

namespace {

class qmquasar_state : public driver_device
{
public:
	qmquasar_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void quasar(machine_config &config);
	void technox(machine_config &config);

protected:
	virtual void driver_start() override;

private:
	HD44780_PIXEL_UPDATE(quasar_pixel_update);
	HD44780_PIXEL_UPDATE(technox_pixel_update);

	void prog_map(address_map &map) ATTR_COLD;
	void ext_map(address_map &map) ATTR_COLD;

	required_device<mcs51_cpu_device> m_maincpu;
};

HD44780_PIXEL_UPDATE(qmquasar_state::quasar_pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 40)
		bitmap.pix(line * 8 + y, pos * 6 + x) = state;
}

HD44780_PIXEL_UPDATE(qmquasar_state::technox_pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 16)
		bitmap.pix(line * 8 + y, pos * 6 + x) = state;
}


void qmquasar_state::prog_map(address_map &map)
{
	map(0x0000, 0xffff).rom().region("program", 0); // TODO: banking?
}

void qmquasar_state::ext_map(address_map &map)
{
	map(0x0000, 0x38ff).ram();
	map(0x3900, 0x3903).nopr();
	map(0x390b, 0x390b).nopr();
	map(0x7c00, 0x7fff).noprw();
	map(0x8000, 0x8003).noprw();
	map(0xa000, 0xa001).w("acia", FUNC(acia6850_device::write));
	map(0xa002, 0xa003).r("acia", FUNC(acia6850_device::read));
	map(0xb000, 0xb001).w("lcdc", FUNC(hd44780_device::write));
	map(0xb002, 0xb003).r("lcdc", FUNC(hd44780_device::read));
	map(0xc000, 0xc0ff).nopw();
	map(0xff10, 0xff10).noprw();
}


static INPUT_PORTS_START(qmquasar)
INPUT_PORTS_END

void qmquasar_state::quasar(machine_config &config)
{
	I8032(config, m_maincpu, 12'000'000); // exact type and clock unknown
	m_maincpu->set_addrmap(AS_PROGRAM, &qmquasar_state::prog_map);
	m_maincpu->set_addrmap(AS_IO, &qmquasar_state::ext_map);

	acia6850_device &acia(ACIA6850(config, "acia"));
	acia.irq_handler().set_inputline(m_maincpu, MCS51_INT1_LINE);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6*40, 8*2);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	hd44780_device &lcdc(HD44780(config, "lcdc", 270'000)); // TODO: clock not measured, datasheet typical clock used
	lcdc.set_lcd_size(2, 40);
	lcdc.set_pixel_update_cb(FUNC(qmquasar_state::quasar_pixel_update));
}

void qmquasar_state::technox(machine_config &config)
{
	quasar(config);

	subdevice<screen_device>("screen")->set_size(6*16, 8*2);
	subdevice<screen_device>("screen")->set_visarea_full();

	subdevice<hd44780_device>("lcdc")->set_lcd_size(2, 16);
	subdevice<hd44780_device>("lcdc")->set_pixel_update_cb(FUNC(qmquasar_state::technox_pixel_update));
}


ROM_START(qmquasar)
	ROM_REGION(0x20000, "program", 0)
	ROM_LOAD("27c1000_v200.ic4", 0x00000, 0x20000, CRC(951363bd) SHA1(6dfb2fa362afd8da0fb764108b6263d9da45c676)) // "Version No: 2.00"

	ROM_REGION(0x40000, "samples", 0)
	ROM_LOAD("27c020_v200d.ic51", 0x00000, 0x40000, CRC(ef318407) SHA1(e147413d5df2f9489c2886587ae60f6807b7dbb9))
ROM_END

ROM_START(technox)
	ROM_REGION(0x20000, "program", 0)
	ROM_LOAD("mx27c1000_v105.ic4", 0x00000, 0x20000, CRC(4d393cd9) SHA1(9ee853f1f2e88fbfc86589c4d0f91003d0ff146f)) // "Version No 1.05b"

	ROM_REGION(0x20000, "samples", 0)
	ROM_LOAD("nm27c010_v105.ic51", 0x00000, 0x20000, CRC(d669570e) SHA1(0645a1b022ed25a836152933e6df32da4448f49a))
ROM_END

void qmquasar_state::driver_start()
{
	memory_region *rgn = memregion("program");
	u8 *rom = rgn->base();

	for (offs_t base = 0x00000; base < rgn->bytes(); base += 0x100)
	{
		// Copy 256-byte block before descrambling addresses
		std::vector<uint8_t> orig(&rom[base], &rom[base + 0x100]);

		for (offs_t offset = 0; offset < 0x100; offset++)
			rom[base + offset] = orig[bitswap<8>(offset, 1, 2, 3, 6, 5, 7, 1, 0) ^ 0x40 ^ ((offset & 0x11) << 3)];
	}
}

} // anonymous namespace

SYST(1993, qmquasar, 0, 0, quasar,  qmquasar, qmquasar_state, empty_init, "Quasimidi Musikelektronik GmbH", "Quasimidi Quasar", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1995, technox,  0, 0, technox, qmquasar, qmquasar_state, empty_init, "Quasimidi Musikelektronik GmbH", "TechnoX",          MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



qmsirius.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for Quasimidi Sirius keyboard and Rave-O-Lution module.

***************************************************************************/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"

namespace {

class qmsirius_state : public driver_device
{
public:
	qmsirius_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void qmsirius(machine_config &config);

private:
	HD44780_PIXEL_UPDATE(lcd_pixel_update);

	void prog_map(address_map &map) ATTR_COLD;
	void ext_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};

HD44780_PIXEL_UPDATE(qmsirius_state::lcd_pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 16)
		bitmap.pix(line * 8 + y, pos * 6 + x) = state;
}

void qmsirius_state::prog_map(address_map &map)
{
	map(0x0000, 0xffff).rom().region("program", 0);
}

void qmsirius_state::ext_map(address_map &map)
{
	map(0x8000, 0x8001).w("lcdc", FUNC(hd44780_device::write));
	map(0x8002, 0x8003).r("lcdc", FUNC(hd44780_device::read));
}

static INPUT_PORTS_START(qmsirius)
INPUT_PORTS_END

void qmsirius_state::qmsirius(machine_config &config)
{
	P80C552(config, m_maincpu, 12_MHz_XTAL); // PCB80C552-5 16WP
	m_maincpu->set_addrmap(AS_PROGRAM, &qmsirius_state::prog_map);
	m_maincpu->set_addrmap(AS_IO, &qmsirius_state::ext_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6*16, 8*2);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	hd44780_device &lcdc(HD44780(config, "lcdc", 270'000)); // HC16202NY-LY; TODO: clock not measured, datasheet typical clock used
	lcdc.set_lcd_size(2, 16);
	lcdc.set_pixel_update_cb(FUNC(qmsirius_state::lcd_pixel_update));
}

ROM_START(qmsirius)
	ROM_REGION(0x80000, "program", 0)
	ROM_DEFAULT_BIOS("v205a")
	ROM_SYSTEM_BIOS(0, "v205a", "Version No. 2.05a")
	ROMX_LOAD("siriusv205a.bin", 0x00000, 0x80000, CRC(3e974cec) SHA1(d44dca58717f89c6eabcf329a39014df1dea215c), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v203", "Version No. 2.03")
	ROMX_LOAD("siriusv203.bin", 0x00000, 0x80000, CRC(34e08d49) SHA1(fc50b9f89d66eddd0aac4ffc2447ee4be7cc41f6), ROM_BIOS(1)) // TMS27C040-12
ROM_END

ROM_START(qmrave)
	ROM_REGION(0x80000, "program", 0)
	ROM_LOAD("quasimidi raveolution 309 os v2.0e.bin", 0x00000, 0x80000, CRC(b0872f0b) SHA1(db71b3654981ef82eeaa2c999453824ea7d2676e))
ROM_END

} // anonymous namespace

SYST(1998, qmsirius, 0, 0, qmsirius, qmsirius, qmsirius_state, empty_init, "Quasimidi Musikelektronik GmbH", "Quasimidi Sirius",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1996, qmrave,   0, 0, qmsirius, qmsirius, qmsirius_state, empty_init, "Quasimidi Musikelektronik GmbH", "Rave-O-Lution 309", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



qtsbc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

QT Computer Systems SBC +2/4

This looks like the same system as the Compu/Time SBC-880 Processor Board.

Currently it crashes. There's a memory move routine at 50A4, and after
a few turns it is told to move E603 bytes which corrupts everything.

Chips: P8251, D8253C, MK3880N-4 (Z80). 3x 6-sw dips. Unmarked crystal.
       EPROM: D2716-2; RAM: 2x MM2114N-3L.

A blue jumper marked 4M and 2M (between U11 and U12) selects the CPU clock.

The RS232 port uses a 26-pin right-angle header (J1) rather than the
conventional DB25 connector. The second 26-pin header (J2) mostly carries
data and handshake signals for two unidirectional parallel ports, but also
includes a few timer outputs.

Feature list from QT ad:
- 1K RAM (which can be located at any 1K boundary) plus one each
  Parallel and Serial I/O ports on board
- Power on jump to onboard EPROM (2708 or 2716)
- EPROM addressable on any 1K or 2K boundary
- Full 64K use of RAM allowed in shadow mode
- Programmable Baud rate selection, 110-9600
- 2 or 4MHz switch selectable
- DMA capability allows MWRT signal generation on CPU board or elsewhere
  in system under DMA logic or front panel control
- Two programmable timers available for use by programs run with the
  SBC+2/4 (timer output and controls available at parallel I/O connector;
  parallel input and output ports available for use on CPU board).

List of signals on pin headers (from CompuTime manual):

        J1                                  J2

     2  RS232 Transmit Data              1  Output Port Data Bit 0
     3  RS232 Receive Data               2  Output Port Data Bit 1
     4  Request to Send                  3  Output Port Data Bit 2
     5  Clear to Send                    4  Output Port Data Bit 3
     6  Data Set Ready                   5  Output Port Data Bit 4
     7  Signal Ground                    6  Output Port Data Bit 5
     8  Carrier Detect                   7  Output Port Data Bit 6
    11  Reverse Channel Transmit         8  Output Port Data Bit 7
    20  Data Terminal Ready              9  Signal Ground
                                        10  Output Port Clock
                                        11  Counter 1 Gate Input
                                        12  Counter 2 Gate Input
                                        14  Input Port Data Bit 0
                                        15  Input Port Data Bit 1
                                        16  Input Port Data Bit 2
                                        17  Input Port Data Bit 3
                                        18  Input Port Data Bit 4
                                        19  Input Port Data Bit 5
                                        20  Input Port Data Bit 6
                                        21  Input Port Data Bit 7
                                        22  Signal Ground
                                        23  Input Port Strobe
                                        24  Counter 1 Output
                                        25  Counter 2 Output

****************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
//#include "bus/s100/s100.h"
#include "cpu/z80/z80.h"
#include "machine/i8251.h"
#include "machine/pit8253.h"


namespace {

class qtsbc_state : public driver_device
{
public:
	qtsbc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pit(*this, "pit")
		, m_usart(*this, "usart")
		, m_rs232(*this, "rs232")
		, m_dsw(*this, "DSW%u", 1)
		, m_jumpers(*this, "JUMPERS")
		, m_cpu_speed(*this, "SPEED")
		, m_eprom(*this, "maincpu")
		, m_p_ram(*this, "ram")
	{ }

	void qtsbc(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u8 memory_r(offs_t offset);
	void memory_w(offs_t offset, u8 data);
	u8 io_r(offs_t offset);
	void io_w(offs_t offset, u8 data);
	void rts_loopback_w(int state);
	void dtr_loopback_w(int state);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<pit8253_device> m_pit;
	required_device<i8251_device> m_usart;
	required_device<rs232_port_device> m_rs232;
	required_ioport_array<3> m_dsw;
	required_ioport m_jumpers;
	required_ioport m_cpu_speed;
	required_region_ptr<u8> m_eprom;
	required_shared_ptr<u8> m_p_ram;
	bool m_power_on = false;
	s32 m_rts = 1;
	s32 m_dtr = 1;
};


u8 qtsbc_state::memory_r(offs_t offset)
{
	ioport_value jumpers = m_jumpers->read();
	ioport_value dsw3 = m_dsw[2]->read();

	bool eprom_addressed = (offset & (BIT(jumpers, 2) ? 0xf800 : 0xfc00)) >> 10 == dsw3;
	if (eprom_addressed && !machine().side_effects_disabled())
		m_power_on = false;

	if ((m_power_on && !BIT(jumpers, 1)) || (eprom_addressed && !BIT(jumpers, 0)))
	{
		offset &= BIT(jumpers, 2) ? 0x07ff : 0x03ff;
		return m_eprom[offset];
	}
#ifdef NOT_IMPLEMENTED_CURRENTLY
	else if ((offset & 0xfc00) >> 10 == m_dsw[1]->read())
	{
		offset &= 0x03ff;
		return m_ram[offset];
	}
#endif
	else
	{
		if (offset == 0xe377)
			return 0x80;

		// TODO: S-100 bus
		return m_p_ram[offset];
	}
}

void qtsbc_state::memory_w(offs_t offset, u8 data)
{
#ifdef NOT_IMPLEMENTED_CURRENTLY
	if ((offset & 0xfc00) >> 10 == m_dsw[1]->read())
	{
		offset &= 0x03ff;
		m_ram[offset] = data;
	}
	else
#endif
	{
		// TODO: S-100 bus
		m_p_ram[offset] = data;
	}
}

u8 qtsbc_state::io_r(offs_t offset)
{
	if ((offset & 0xf8) >> 3 == (m_dsw[0]->read() & 0x1f))
	{
		switch (offset & 7)
		{
		case 0:
		case 1:
		case 2:
		case 3:
		default:
			return m_pit->read(offset & 3);

		case 4:
		case 5:
			// TODO: 8-bit parallel input port
			return 0xff;

		case 6:
		case 7:
			return m_usart->read(offset & 1);
		}
	}
	else
	{
		// TODO: S-100 bus (no address mirroring)
		logerror("Input from %04X\n", offset);

		// Ports 40 and 43 are for some sort of off-board RAM
		// Bit 0 of 43 is some sort of busy signal; bits 1-3 select some memory size factor
		if ((offset & 0xff) == 0x43)
			return 0x00;

		// Ports 08, 10 and 80 also used for read access
		return 0xff;
	}
}

void qtsbc_state::io_w(offs_t offset, u8 data)
{
	if ((offset & 0x00f8) >> 3 == (m_dsw[0]->read() & 0x1f))
	{
		switch (offset & 7)
		{
		case 0:
		case 1:
		case 2:
		case 3:
		default:
			m_pit->write(offset & 3, data);
			break;

		case 4:
		case 5:
			// TODO: 8-bit parallel output port
			break;

		case 6:
		case 7:
			m_usart->write(offset & 1, data);
			break;
		}
	}
	else
	{
		// TODO: S-100 bus (no address mirroring)
		logerror("Output %02X to %04X\n", data, offset);
	}
}

void qtsbc_state::rts_loopback_w(int state)
{
	// Filtered through this routine to avoid infinite loops
	if (state != m_rts)
	{
		m_rts = state;
		m_rs232->write_rts(m_rts);
	}
}

void qtsbc_state::dtr_loopback_w(int state)
{
	// Filtered through this routine to avoid infinite loops
	if (state != m_dtr)
	{
		m_dtr = state;
		m_rs232->write_dtr(m_dtr);
	}
}

void qtsbc_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).ram().share("ram");
	map(0x0000, 0xffff).rw(FUNC(qtsbc_state::memory_r), FUNC(qtsbc_state::memory_w));
}

void qtsbc_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(qtsbc_state::io_r), FUNC(qtsbc_state::io_w));
}

/* Input ports */
static INPUT_PORTS_START( qtsbc )
	PORT_START("DSW1")
	PORT_DIPNAME(0x1f, 0x00, "On-Board I/O Ports") PORT_DIPLOCATION("SW1:1,2,3,4,5")
	PORT_DIPSETTING(0x00, "00-07")
	PORT_DIPSETTING(0x01, "08-0F")
	PORT_DIPSETTING(0x02, "10-17")
	PORT_DIPSETTING(0x03, "18-1F")
	PORT_DIPSETTING(0x04, "20-27")
	PORT_DIPSETTING(0x05, "28-2F")
	PORT_DIPSETTING(0x06, "30-37")
	PORT_DIPSETTING(0x07, "38-3F")
	PORT_DIPSETTING(0x08, "40-47")
	PORT_DIPSETTING(0x09, "48-4F")
	PORT_DIPSETTING(0x0a, "50-57")
	PORT_DIPSETTING(0x0b, "58-5F")
	PORT_DIPSETTING(0x0c, "60-67")
	PORT_DIPSETTING(0x0d, "68-6F")
	PORT_DIPSETTING(0x0e, "70-77")
	PORT_DIPSETTING(0x0f, "78-7F")
	PORT_DIPSETTING(0x10, "80-87")
	PORT_DIPSETTING(0x11, "88-8F")
	PORT_DIPSETTING(0x12, "90-87")
	PORT_DIPSETTING(0x13, "98-9F")
	PORT_DIPSETTING(0x14, "A0-A7")
	PORT_DIPSETTING(0x15, "A8-AF")
	PORT_DIPSETTING(0x16, "B0-B7")
	PORT_DIPSETTING(0x17, "B8-BF")
	PORT_DIPSETTING(0x18, "C0-C7")
	PORT_DIPSETTING(0x19, "C8-CF")
	PORT_DIPSETTING(0x1a, "D0-D7")
	PORT_DIPSETTING(0x1b, "D8-DF")
	PORT_DIPSETTING(0x1c, "E0-E7")
	PORT_DIPSETTING(0x1d, "E8-EF")
	PORT_DIPSETTING(0x1e, "F0-F7")
	PORT_DIPSETTING(0x1f, "F8-FF")
	PORT_DIPUNUSED(0x20, 0x20) PORT_DIPLOCATION("SW1:6")

	PORT_START("DSW2")
	PORT_DIPNAME(0x3f, 0x37, "On-Board RAM Address") PORT_DIPLOCATION("SW2:6,5,4,3,2,1")
	PORT_DIPSETTING(0x00, "0000-03FF")
	PORT_DIPSETTING(0x01, "0400-07FF")
	PORT_DIPSETTING(0x02, "0800-0BFF")
	PORT_DIPSETTING(0x03, "0C00-0FFF")
	PORT_DIPSETTING(0x04, "1000-13FF")
	PORT_DIPSETTING(0x05, "1400-17FF")
	PORT_DIPSETTING(0x06, "1800-1BFF")
	PORT_DIPSETTING(0x07, "1C00-1FFF")
	PORT_DIPSETTING(0x08, "2000-23FF")
	PORT_DIPSETTING(0x09, "2400-27FF")
	PORT_DIPSETTING(0x0a, "2800-2BFF")
	PORT_DIPSETTING(0x0b, "2C00-2FFF")
	PORT_DIPSETTING(0x0c, "3000-33FF")
	PORT_DIPSETTING(0x0d, "3400-37FF")
	PORT_DIPSETTING(0x0e, "3800-3BFF")
	PORT_DIPSETTING(0x0f, "3C00-3FFF")
	PORT_DIPSETTING(0x10, "4000-43FF")
	PORT_DIPSETTING(0x11, "4400-47FF")
	PORT_DIPSETTING(0x12, "4800-4BFF")
	PORT_DIPSETTING(0x13, "4C00-4FFF")
	PORT_DIPSETTING(0x14, "5000-53FF")
	PORT_DIPSETTING(0x15, "5400-57FF")
	PORT_DIPSETTING(0x16, "5800-5BFF")
	PORT_DIPSETTING(0x17, "5C00-5FFF")
	PORT_DIPSETTING(0x18, "6000-63FF")
	PORT_DIPSETTING(0x19, "6400-67FF")
	PORT_DIPSETTING(0x1a, "6800-6BFF")
	PORT_DIPSETTING(0x1b, "6C00-6FFF")
	PORT_DIPSETTING(0x1c, "7000-73FF")
	PORT_DIPSETTING(0x1d, "7400-77FF")
	PORT_DIPSETTING(0x1e, "7800-7BFF")
	PORT_DIPSETTING(0x1f, "7C00-7FFF")
	PORT_DIPSETTING(0x20, "8000-83FF")
	PORT_DIPSETTING(0x21, "8400-87FF")
	PORT_DIPSETTING(0x22, "8800-8BFF")
	PORT_DIPSETTING(0x23, "8C00-8FFF")
	PORT_DIPSETTING(0x24, "9000-93FF")
	PORT_DIPSETTING(0x25, "9400-97FF")
	PORT_DIPSETTING(0x26, "9800-9BFF")
	PORT_DIPSETTING(0x27, "9C00-9FFF")
	PORT_DIPSETTING(0x28, "A000-A3FF")
	PORT_DIPSETTING(0x29, "A400-A7FF")
	PORT_DIPSETTING(0x2a, "A800-ABFF")
	PORT_DIPSETTING(0x2b, "AC00-AFFF")
	PORT_DIPSETTING(0x2c, "B000-B3FF")
	PORT_DIPSETTING(0x2d, "B400-B7FF")
	PORT_DIPSETTING(0x2e, "B800-BBFF")
	PORT_DIPSETTING(0x2f, "BC00-BFFF")
	PORT_DIPSETTING(0x30, "C000-C3FF")
	PORT_DIPSETTING(0x31, "C400-C7FF")
	PORT_DIPSETTING(0x32, "C800-CBFF")
	PORT_DIPSETTING(0x33, "CC00-CFFF")
	PORT_DIPSETTING(0x34, "D000-D3FF")
	PORT_DIPSETTING(0x35, "D400-D7FF")
	PORT_DIPSETTING(0x36, "D800-DBFF")
	PORT_DIPSETTING(0x37, "DC00-DFFF")
	PORT_DIPSETTING(0x38, "E000-E3FF")
	PORT_DIPSETTING(0x39, "E400-E7FF")
	PORT_DIPSETTING(0x3a, "E800-EBFF")
	PORT_DIPSETTING(0x3b, "EC00-EFFF")
	PORT_DIPSETTING(0x3c, "F000-F3FF")
	PORT_DIPSETTING(0x3d, "F400-F7FF")
	PORT_DIPSETTING(0x3e, "F800-FBFF")
	PORT_DIPSETTING(0x3f, "FC00-FFFF")

	PORT_START("DSW3")
	PORT_DIPNAME(0x3f, 0x14, "On-Board EPROM Address") PORT_DIPLOCATION("SW3:6,5,4,3,2,1")
	PORT_DIPSETTING(0x00, "0000-03FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x01, "0400-07FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x02, "0800-0BFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x03, "0C00-0FFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x04, "1000-13FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x05, "1400-17FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x06, "1800-1BFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x07, "1C00-1FFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x08, "2000-23FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x09, "2400-27FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x0a, "2800-2BFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x0b, "2C00-2FFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x0c, "3000-33FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x0d, "3400-37FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x0e, "3800-3BFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x0f, "3C00-3FFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x10, "4000-43FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x11, "4400-47FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x12, "4800-4BFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x13, "4C00-4FFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x14, "5000-53FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x15, "5400-57FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x16, "5800-5BFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x17, "5C00-5FFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x18, "6000-63FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x19, "6400-67FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x1a, "6800-6BFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x1b, "6C00-6FFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x1c, "7000-73FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x1d, "7400-77FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x1e, "7800-7BFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x1f, "7C00-7FFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x20, "8000-83FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x21, "8400-87FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x22, "8800-8BFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x23, "8C00-8FFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x24, "9000-93FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x25, "9400-97FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x26, "9800-9BFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x27, "9C00-9FFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x28, "A000-A3FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x29, "A400-A7FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x2a, "A800-ABFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x2b, "AC00-AFFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x2c, "B000-B3FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x2d, "B400-B7FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x2e, "B800-BBFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x2f, "BC00-BFFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x30, "C000-C3FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x31, "C400-C7FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x32, "C800-CBFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x33, "CC00-CFFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x34, "D000-D3FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x35, "D400-D7FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x36, "D800-DBFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x37, "DC00-DFFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x38, "E000-E3FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x39, "E400-E7FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x3a, "E800-EBFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x3b, "EC00-EFFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x3c, "F000-F3FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x3d, "F400-F7FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x3e, "F800-FBFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x3f, "FC00-FFFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x00)
	PORT_DIPSETTING(0x00, "0000-07FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x02, "0800-0FFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x04, "1000-17FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x06, "1800-1FFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x08, "2000-27FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x0a, "2800-2FFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x0c, "3000-37FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x0e, "3800-3FFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x10, "4000-47FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x12, "4800-4FFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x14, "5000-57FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x16, "5800-5FFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x18, "6000-67FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x1a, "6800-6FFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x1c, "7000-77FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x1e, "7800-7FFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x20, "8000-87FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x22, "8800-8FFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x24, "9000-97FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x26, "9800-9FFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x28, "A000-A7FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x2a, "A800-AFFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x2c, "B000-B7FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x2e, "B800-BFFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x30, "C000-C7FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x32, "C800-CFFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x34, "D000-D7FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x36, "D800-DFFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x38, "E000-E7FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x3a, "E800-EFFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x3c, "F000-F7FF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)
	PORT_DIPSETTING(0x3e, "F800-FFFF") PORT_CONDITION("JUMPERS", 0x04, EQUALS, 0x04)

	PORT_START("JUMPERS")
	PORT_DIPNAME(0x02, 0x00, "Power-On Jump") PORT_DIPLOCATION("T to U:1")
	PORT_DIPSETTING(0x02, DEF_STR(Off)) // T to U open
	PORT_DIPSETTING(0x00, DEF_STR(On)) // T to U shorted
	PORT_DIPNAME(0x01, 0x01, "EPROM Phantom Mode") PORT_DIPLOCATION("Q to R:1") PORT_CONDITION("JUMPERS", 0x02, EQUALS, 0x00)
	PORT_DIPSETTING(0x00, DEF_STR(Off)) // Q to R shorted
	PORT_DIPSETTING(0x01, DEF_STR(On)) // Q to R open
	PORT_DIPNAME(0x01, 0x00, "On-Board EPROM") PORT_DIPLOCATION("Q to R:1") PORT_CONDITION("JUMPERS", 0x02, EQUALS, 0x02)
	PORT_DIPSETTING(0x01, "Disabled") // Q to R open
	PORT_DIPSETTING(0x00, "Enabled") // Q to R shorted
	PORT_DIPNAME(0x04, 0x04, "On-Board EPROM Size") PORT_DIPLOCATION("P to O:1")
	PORT_DIPSETTING(0x00, "1K x 8 (2708)") // P to O shorted
	PORT_DIPSETTING(0x04, "2K x 8 (2716)") // P to O open

	PORT_START("SPEED")
	PORT_CONFNAME(0x01, 0x01, "CPU Speed")
	PORT_CONFSETTING(0x00, "2 MHz")
	PORT_CONFSETTING(0x01, "4 MHz")
INPUT_PORTS_END


void qtsbc_state::machine_start()
{
	m_usart->write_cts(0);

	save_item(NAME(m_power_on));
	save_item(NAME(m_rts));
	save_item(NAME(m_dtr));
}

void qtsbc_state::machine_reset()
{
	m_power_on = true;
	m_maincpu->set_unscaled_clock(BIT(m_cpu_speed->read(), 0) ? 4_MHz_XTAL : 4_MHz_XTAL / 2);
}

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void qtsbc_state::qtsbc(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 4_MHz_XTAL); // Mostek MK3880
	m_maincpu->set_addrmap(AS_PROGRAM, &qtsbc_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &qtsbc_state::io_map);

	/* video hardware */
	PIT8253(config, m_pit); // U9
	m_pit->set_clk<0>(4_MHz_XTAL / 2); /* Timer 0: baud rate gen for 8251 */
	m_pit->out_handler<0>().set(m_usart, FUNC(i8251_device::write_txc));
	m_pit->out_handler<0>().append(m_usart, FUNC(i8251_device::write_rxc));
	m_pit->set_clk<1>(4_MHz_XTAL / 2);
	m_pit->out_handler<1>().set(m_pit, FUNC(pit8253_device::write_clk2));

	I8251(config, m_usart, 0); // U8
	m_usart->txd_handler().set(m_rs232, FUNC(rs232_port_device::write_txd));

	RS232_PORT(config, m_rs232, default_rs232_devices, "terminal");
	m_rs232->rxd_handler().set(m_usart, FUNC(i8251_device::write_rxd));
	m_rs232->dsr_handler().set(m_usart, FUNC(i8251_device::write_dsr)); // actually from pin 11, "Reverse Channel Transmit"
	m_rs232->cts_handler().set(FUNC(qtsbc_state::rts_loopback_w));
	m_rs232->dcd_handler().set(FUNC(qtsbc_state::dtr_loopback_w));
	m_rs232->set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));
}

/* ROM definition */
ROM_START( qtsbc )
	ROM_REGION( 0x0800, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "qtsbc.u23", 0x0000, 0x0800, CRC(823fd942) SHA1(64c4f74dd069ae4d43d301f5e279185f32a1efa0))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY                     FULLNAME     FLAGS
COMP( 198?, qtsbc, 0,      0,      qtsbc,   qtsbc, qtsbc_state, empty_init, "QT Computer Systems Inc.", "SBC + 2/4", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



quakeat.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
/*

 Quake Arcade Tournament

 Only the HDD is dumped.  The HDD is stickered 'Release Beta 2'

 We've also seen CDs of this for sale, so maybe there should be a CD too, for the music?

TODO:
- In pcipc throws "E0409 -- Security key not found." in glquake.exe when it starts running.

===================================================================================================
 -- set info

Quake Arcade Tournament by Lazer-Tron

PC running Windows 95 with a Dongle on the parallel port

Created .chd with version 0.125

It found the following disk paramaters...

Input offset    511
Cylinders   263
Heads       255
Sectors     63
Byte/Sector 512
Sectors/Hunk    8
Logical size    2,163,248,864

The "backup" directory on hard disk was created by the dumper.

 -- Hardware info found on the following web pages:
http://web.archive.org/web/20070810060806/http://www.wave-report.com/archives/1998/98170702.htm
http://www.thedodgegarage.com/3dfx/q3d_quicksilver.htm
http://quakearcadetournament.blogspot.com/
http://web.archive.org/web/20001001045148/http://www.quantum3d.com:80/press%20releases/4-20-98.html
https://www.quaddicted.com/webarchive/www.quaddicted.com/quake-nostalgia/quake-arcade-tournament-edition/

Quantum3D Heavy Metal HM233G (part of Quantum3D's Quicksilver family)
- NLX form factor system that is based on the Intel 440LX chipset
- Intel NX440LX motherboard
    - Intel 82440LX AGPset (82443LX Northbridge / 82371AB PIIX4 PCI-ISA Southbridge)
    - SMC FDC37C677 I/O
    - Yamaha OPL3-SA3 (YMF715) Audio codec (16-bit per sample 3D audio)
    - Intel Pro 10/100 PCI Ethernet NIC
    - (Optional) Cirrus CL-GD5465 AGP Graphics Controller
    - Intel/Phoenix BIOS
- Intel Pentium II 233 233MHz CPU processor with 512KB of L2 cache
- (1) 32MB PC66 66 MHz SDRAM 168-pin DIMM
- Microsoft Windows 95 OSR2.5
- shock-mounted 3.1GB Ultra DMA-33 EIDE hard drive
- 12-24x CD-ROM drive
- 1.44 MB floppy drive
- Quantum3D Obsidian2 90-4440 AGP AGPTV Voodoo2-based realtime 3D graphics accelerator
      (a professional version of the Quantum3D Obsidian2 S-12 AGPTV)
- Companion PCI 2D/VGA: Quantum3D Ventana MGV-PCI (Alliance Semiconductor ProMotion aT25)
      or Quantum3D Ventana "MGV Rush" (custom Quantum3D Ventana 50 Voodoo Rush with 3D-disabled
      2D-only BIOS and no TV-out, only using the Alliance Semiconductor ProMotion aT25)
- Quantum3D GCI-2 (Game Control Interface II) I/O board - designed to interface coin-op and
      industrial input/output control devices to a PC. Fits in either PCI or ISA bus slot for
      mechanical attachment only. Communications between the GCI and the PC are via a standard
      RS-232 serial interface, using a 14-byte packet protocol. Power is provided by a 4-pin
      Molex style disk driver power connector.

Note: Quantum3D Quicksilver QS233G configuration seem very similar to the HM233G, with the only
    exceptions being that the Quantum3D Obsidian2 90-4440 is replaced with the earlier Quantum3D
    Obsidian 100SB-4440V Voodoo Graphics realtime 3D graphics accelerator with 2D Alliance
    Semiconductor ProMotion aT25 MGV 2000 daughter card, and the Quantum3D GCI-2 might be replaced
    with an earlier Quantum3D GCI.

Dongle: Rainbow Technologies parallel-port security dongle (at least 1024 bytes)

HDD image contains remnants of an Actua Soccer Arcade installation.

*/

#include "emu.h"
#include "cpu/i386/i386.h"
#include "machine/pci.h"
#include "machine/pci-ide.h"
#include "machine/i82443bx_host.h"
#include "machine/i82371eb_isa.h"
#include "machine/i82371eb_ide.h"
#include "machine/i82371eb_acpi.h"
#include "machine/i82371eb_usb.h"
#include "machine/w83977tf.h"
#include "bus/isa/isa_cards.h"
//#include "bus/rs232/hlemouse.h"
//#include "bus/rs232/null_modem.h"
//#include "bus/rs232/rs232.h"
//#include "bus/rs232/sun_kbd.h"
//#include "bus/rs232/terminal.h"
#include "video/voodoo_pci.h"

// TODO: change me up to agp_slot
#define PCI_AGP_ID "pci:01.0:00.0"

namespace {

class quakeat_state : public driver_device
{
public:
	quakeat_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_voodoo(*this, PCI_AGP_ID)
	{ }

	void ga6la7(machine_config &config);
	void quake(machine_config &config);

private:
	required_device<pentium2_device> m_maincpu;
	required_device<voodoo_banshee_pci_device> m_voodoo;

	void ga6la7_map(address_map &map) ATTR_COLD;
	void ga6la7_io(address_map &map) ATTR_COLD;
	void quake_map(address_map &map) ATTR_COLD;

	static void winbond_superio_config(device_t *device);
};

void quakeat_state::ga6la7_map(address_map &map)
{
	map.unmap_value_high();
}

void quakeat_state::ga6la7_io(address_map &map)
{
	map.unmap_value_high();
}


static INPUT_PORTS_START( quake )
INPUT_PORTS_END

static void isa_internal_devices(device_slot_interface &device)
{
	device.option_add("w83977tf", W83977TF);
}

void quakeat_state::winbond_superio_config(device_t *device)
{
	// TODO: Winbond w83977ef
	w83977tf_device &fdc = *downcast<w83977tf_device *>(device);
//  fdc.set_sysopt_pin(1);
	fdc.gp20_reset().set_inputline(":maincpu", INPUT_LINE_RESET);
	fdc.gp25_gatea20().set_inputline(":maincpu", INPUT_LINE_A20);
	fdc.irq1().set(":pci:07.0", FUNC(i82371eb_isa_device::pc_irq1_w));
	fdc.irq8().set(":pci:07.0", FUNC(i82371eb_isa_device::pc_irq8n_w));
//  fdc.txd1().set(":serport0", FUNC(rs232_port_device::write_txd));
//  fdc.ndtr1().set(":serport0", FUNC(rs232_port_device::write_dtr));
//  fdc.nrts1().set(":serport0", FUNC(rs232_port_device::write_rts));
//  fdc.txd2().set(":serport1", FUNC(rs232_port_device::write_txd));
//  fdc.ndtr2().set(":serport1", FUNC(rs232_port_device::write_dtr));
//  fdc.nrts2().set(":serport1", FUNC(rs232_port_device::write_rts));
}

void quakeat_state::ga6la7(machine_config &config)
{
	// TODO: Socket 370 Celeron with 366-566 MHz
	PENTIUM2(config, m_maincpu, 90'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &quakeat_state::ga6la7_map);
	m_maincpu->set_addrmap(AS_IO, &quakeat_state::ga6la7_io);
	m_maincpu->set_irq_acknowledge_callback("pci:07.0:pic8259_master", FUNC(pic8259_device::inta_cb));
	m_maincpu->smiact().set("pci:00.0", FUNC(i82443bx_host_device::smi_act_w));

	PCI_ROOT(config, "pci", 0);
	// 16MB - 384MB of supported EDO RAM
	I82443LX_HOST(config, "pci:00.0", 0, "maincpu", 256*1024*1024);
	I82443LX_BRIDGE(config, "pci:01.0", 0 ); //"pci:01.0:00.0");
	//I82443LX_AGP   (config, "pci:01.0:00.0");

	i82371eb_isa_device &isa(I82371EB_ISA(config, "pci:07.0", 0, "maincpu"));
	isa.boot_state_hook().set([](u8 data) { /* printf("%02x\n", data); */ });
	isa.smi().set_inputline("maincpu", INPUT_LINE_SMI);

	i82371eb_ide_device &ide(I82371EB_IDE(config, "pci:07.1", 0, "maincpu"));
	ide.irq_pri().set("pci:07.0", FUNC(i82371eb_isa_device::pc_irq14_w));
	ide.irq_sec().set("pci:07.0", FUNC(i82371eb_isa_device::pc_mirq0_w));

	I82371EB_USB (config, "pci:07.2", 0);
	I82371EB_ACPI(config, "pci:07.3", 0);
	LPC_ACPI     (config, "pci:07.3:acpi", 0);
	SMBUS        (config, "pci:07.3:smbus", 0);

	ISA16_SLOT(config, "board4", 0, "pci:07.0:isabus", isa_internal_devices, "w83977tf", true).set_option_machine_config("w83977tf", winbond_superio_config);
	ISA16_SLOT(config, "isa1", 0, "pci:07.0:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa2", 0, "pci:07.0:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa3", 0, "pci:07.0:isabus", pc_isa16_cards, nullptr, false);

	VOODOO_BANSHEE_X86_PCI(config, m_voodoo, 0, m_maincpu, "screen"); // "pci:0d.0" J4D2
	m_voodoo->set_fbmem(8);
	m_voodoo->set_status_cycles(1000);
//  subdevice<generic_voodoo_device>(PCI_AGP_ID":voodoo")->vblank_callback().set("pci:07.0", FUNC(i82371eb_isa_device::pc_irq5_w));

	// TODO: fix legacy raw setup here
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(57);
	screen.set_size(640, 480);
	screen.set_visarea(0, 640 - 1, 0, 480 - 1);
	screen.set_screen_update(PCI_AGP_ID, FUNC(voodoo_banshee_pci_device::screen_update));
}

void quakeat_state::quake(machine_config &config)
{
	ga6la7(config);
	// ...
}

ROM_START( ga6la7 )
	ROM_REGION32_LE(0x40000, "pci:07.0", 0)
	ROM_LOAD("6la7a.14", 0x00000, 0x40000, CRC(4fb23f37) SHA1(b8c6a647c6f3e5000b8d4d99c42eb1dc66be0cd8) )
ROM_END

ROM_START(quake)
	// 4N4XL0X0.86A.0011.P05
	ROM_REGION32_LE(0x20000, "pci:07.0", 0)  /* motherboard bios */
	// TODO: compressed
//  ROM_LOAD("p05-0011.bio", 0x000000, 0x10000, NO_DUMP )
//  ROM_CONTINUE( 0x1ffff-0xa0, 0xa0 )
	ROM_LOAD("quakearcadetournament.pcbios", 0x000000, 0x20000, NO_DUMP )

	// Hitachi DK237A-21 A/A0A0, IDE/ATA 2.5" 2.1GB 4000 RPM
	// WS03131880
	DISK_REGION( "disks" )
	// wrong chs 263,255,63
//  DISK_IMAGE( "quakeat", 0, BAD_DUMP SHA1(c44695b9d521273c9d3c0e18c88f0dca0185bd7b) )
	// regenerated from above, with -chs 4200,16,63 as per reported HDD label
	DISK_IMAGE( "quakeat", 0, BAD_DUMP SHA1(9a422ad342aeddd447514d0287efde49e3de5fa8) )
ROM_END

} // anonymous namespace


COMP( 1999, ga6la7,  0,  0, ga6la7, 0, quakeat_state, empty_init, "Gigabyte", "GA-6LA7", MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // errors out with ISA state 0x05 (keyboard), then wants flash ROM i/f to work properly

GAME( 1998, quake,  0,      quake,  quake, quakeat_state, empty_init, ROT0, "Lazer-Tron / iD Software", "Quake Arcade Tournament (Release Beta 2)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
// Actua Soccer Arcade



qvt102.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Dirk Best
/****************************************************************************

    Qume QVT-102/QVT-102A video terminal

    Hardware:
    - Motorola 6800 CPU
    - Hitachi HD46505SP (Motorola 6845-compatible) CRTC
    - Hitachi HD46850 (Motorola 6850-compatible) ACIA
    - M58725P-15 (6116-compatible) (2k x 8bit RAM)
    - Zilog Z8430 CTC
    - 16.6698MHz Crystal
    - 2x TC5514-APL + 3V battery, functioning as NVRAM

    Keyboard: D8748D, 6.000MHz Crystal, Beeper

    For the QVT102, the 'Setup' function is entered by typing F11. The 'left'
    and 'right' arrow keys then move between entries on a line, and the 'up'
    and 'down' arrow keys move between lines. The space bar cycles through
    options for an entry. Shift-S saves the values to NVRAM; F11 exits without
    saving; Shift-D resets to the default values; and Shift-R restores from
    the saved values.

    TODO:
    - Support QVT-102A differences (bidirectional aux, different keyboard)
    - Key click sounds weird

****************************************************************************/

#include "emu.h"
#include "cpu/m6800/m6800.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/input_merger.h"
#include "machine/6850acia.h"
#include "machine/nvram.h"
#include "machine/z80ctc.h"
#include "video/mc6845.h"
#include "sound/spkrdev.h"
#include "bus/rs232/rs232.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class qvt102_state : public driver_device
{
public:
	qvt102_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_irqs(*this, "irqs")
		, m_kbdmcu(*this, "kbdmcu")
		, m_keys_p1(*this, "P1_%u", 0U)
		, m_keys_p2(*this, "P2_%u", 0U)
		, m_keys_special(*this, "SPECIAL")
		, m_jumper(*this, "JUMPER")
		, m_acia(*this, "acia")
		, m_host(*this, "host")
		, m_aux(*this, "aux")
		, m_ctc(*this, "ctc")
		, m_crtc(*this, "crtc")
		, m_screen(*this, "screen")
		, m_palette(*this, "palette")
		, m_speaker(*this, "speaker")
		, m_vram(*this, "videoram")
		, m_char_rom(*this, "chargen")
		, m_latch(0)
		, m_kbd_data(0)
		, m_kbd_bus(0xff), m_kbd_p1(0xff), m_kbd_p2(0xff)
	{ }

	void qvt102(machine_config &config);

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

private:
	required_device<m6800_cpu_device> m_maincpu;
	required_device<input_merger_device> m_irqs;
	required_device<i8748_device> m_kbdmcu;
	required_ioport_array<8> m_keys_p1;
	required_ioport_array<8> m_keys_p2;
	required_ioport m_keys_special;
	required_ioport m_jumper;
	required_device<acia6850_device> m_acia;
	required_device<rs232_port_device> m_host;
	required_device<rs232_port_device> m_aux;
	required_device<z80ctc_device> m_ctc;
	required_device<hd6845s_device> m_crtc;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<speaker_sound_device> m_speaker;
	required_shared_ptr<uint8_t> m_vram;
	required_region_ptr<u8> m_char_rom;

	void mem_map(address_map &map) ATTR_COLD;

	MC6845_UPDATE_ROW(crtc_update_row);

	uint8_t vsync_ack_r();
	void vsync_w(int state);
	uint8_t kbd_r();
	void latch_w(uint8_t data);

	uint8_t ctc_r();
	void ctc_w(uint8_t data);

	void acia_txd_w(int state);
	void acia_rts_w(int state);
	void dsr_w(int state);
	void host_rxd_w(int state);
	void host_dcd_w(int state);

	uint8_t m_latch;
	int m_kbd_data;

	// keyboard mcu
	uint8_t mcu_bus_r();
	void mcu_bus_w(uint8_t data);
	int mcu_t0_r();
	int mcu_t1_r();
	void mcu_p1_w(uint8_t data);
	void mcu_p2_w(uint8_t data);

	uint8_t m_kbd_bus, m_kbd_p1, m_kbd_p2;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void qvt102_state::mem_map(address_map &map)
{
	map(0x0000, 0x03ff).ram().share("nvram");
	map(0x2800, 0x2800).rw(FUNC(qvt102_state::ctc_r), FUNC(qvt102_state::ctc_w));
	map(0x3000, 0x3000).r(FUNC(qvt102_state::vsync_ack_r));
	map(0x3800, 0x3800).w(FUNC(qvt102_state::latch_w));
	map(0x4000, 0x47ff).ram().share("videoram").mirror(0x3800);
	map(0x8000, 0x8000).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x8001, 0x8001).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x9800, 0x9801).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xa000, 0xa000).r(FUNC(qvt102_state::kbd_r));
	map(0xe000, 0xffff).rom().region("maincpu", 0);
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( qvt102 )
	PORT_START("P1_0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';')  PORT_CHAR(':')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)          PORT_CHAR('j')  PORT_CHAR('J')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)          PORT_CHAR('h')  PORT_CHAR('H')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)          PORT_CHAR('d')  PORT_CHAR('D')

	PORT_START("P1_1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)          PORT_CHAR('l')  PORT_CHAR('L')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)          PORT_CHAR('k')  PORT_CHAR('K')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)          PORT_CHAR('g')  PORT_CHAR('G')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)          PORT_CHAR('f')  PORT_CHAR('F')

	PORT_START("P1_2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)          PORT_CHAR('p')  PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)          PORT_CHAR('o')  PORT_CHAR('O')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y')  PORT_CHAR('Y')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)          PORT_CHAR('t')  PORT_CHAR('T')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)          PORT_CHAR('w')  PORT_CHAR('W')

	PORT_START("P1_3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/')  PORT_CHAR('?')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)          PORT_CHAR('m')  PORT_CHAR('M')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)          PORT_CHAR('v')  PORT_CHAR('V')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)          PORT_CHAR('c')  PORT_CHAR('C')

	PORT_START("P1_4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-')  PORT_CHAR('_')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9)         PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_CHAR('9')  PORT_CHAR('(')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)         PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7')  PORT_CHAR('&')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)          PORT_CHAR('4')  PORT_CHAR('$')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)         PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)          PORT_CHAR('3')  PORT_CHAR('#')

	PORT_START("P1_5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')  PORT_CHAR('>')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')  PORT_CHAR('<')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)          PORT_CHAR('n')  PORT_CHAR('N')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)          PORT_CHAR('b')  PORT_CHAR('B')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)          PORT_CHAR('x')  PORT_CHAR('X')

	PORT_START("P1_6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_CHAR('0')  PORT_CHAR(')')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)         PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8')  PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)         PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_CHAR('6')  PORT_CHAR('^')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)          PORT_CHAR('5')  PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)         PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)          PORT_CHAR('2')  PORT_CHAR('@')

	PORT_START("P1_7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[')  PORT_CHAR('{')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)          PORT_CHAR('i')  PORT_CHAR('I')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)          PORT_CHAR('u')  PORT_CHAR('U')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)          PORT_CHAR('r')  PORT_CHAR('R')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)          PORT_CHAR('e')  PORT_CHAR('E')

	PORT_START("P2_0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)      PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD)  PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)      PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)      PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)          PORT_CHAR('s')  PORT_CHAR('S')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("P2_1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)      PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                                                  PORT_NAME("PF4")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                                                  PORT_NAME("PF2")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)      PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT)     PORT_CHAR(10)                      PORT_NAME("Line Feed")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('a')  PORT_CHAR('A')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("P2_2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)         PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11)        PORT_CHAR(UCHAR_MAMEKEY(F11))       PORT_NAME("Setup")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q')  PORT_CHAR('Q')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("P2_3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD)  PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)      PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)      PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)      PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z')  PORT_CHAR('Z')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("P2_4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PAUSE)      PORT_CHAR(UCHAR_MAMEKEY(CANCEL))    PORT_NAME("Break")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PRTSCR)     PORT_CHAR(UCHAR_MAMEKEY(PRTSCR))    PORT_NAME("Print")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))      PORT_NAME("\xe2\x86\x93") // ↓
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))      PORT_NAME("\xe2\x86\x90") // ←
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('~')  PORT_CHAR('`')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)         PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("P2_5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)      PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD)   PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))   PORT_NAME("Keypad ,")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)      PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD)    PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))  PORT_NAME("Keypad .")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)                       PORT_NAME("Return") // ↵
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SCRLOCK)    PORT_CHAR(UCHAR_MAMEKEY(SCRLOCK))   PORT_NAME("No Scroll")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("P2_6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(8)                        PORT_NAME("Back Space")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD)                                                                   PORT_NAME("PF3")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD)                                                                   PORT_NAME("PF1")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))        PORT_NAME("\xe2\x86\x91") // ↑
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('=')  PORT_CHAR('+')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)         PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)        PORT_CHAR(9)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("P2_7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)        PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME)       PORT_CHAR(UCHAR_MAMEKEY(HOME))      PORT_NAME("Clear / Home")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))     PORT_NAME("\xe2\x86\x92") // →
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("SPECIAL")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)   PORT_CODE(KEYCODE_RSHIFT)   PORT_CHAR(UCHAR_SHIFT_1) // ⇧
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_NAME("Ctrl")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK)                             PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))

	PORT_START("JUMPER")
	PORT_DIPNAME(0x01, 0x00, "EIA DSR (W1)")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x01, DEF_STR( On ))
	PORT_DIPNAME(0x02, 0x02, "EIA DCD (W2)")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x02, DEF_STR( On ))
	PORT_DIPNAME(0x04, 0x04, "EIA DTR (W3)")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x04, DEF_STR( On ))
	PORT_DIPNAME(0x08, 0x00, "EIA RTS (W4)")
	PORT_DIPSETTING(   0x00, DEF_STR( Off ))
	PORT_DIPSETTING(   0x08, DEF_STR( On ))
	PORT_DIPNAME(0x30, 0x00, "Character Set")
	PORT_DIPSETTING(   0x00, "US (W17)")
	PORT_DIPSETTING(   0x10, "GM (W18)")
	PORT_DIPSETTING(   0x20, "UK (W19)")
	PORT_DIPNAME(0x40, 0x00, "Attribute Code Intensity")
	PORT_DIPSETTING(   0x00, "Half (W21)")
	PORT_DIPSETTING(   0x40, "Full (W22)")
INPUT_PORTS_END


//**************************************************************************
//  KEYBOARD
//**************************************************************************

// 7------- unused
// -654---- row select/special keys
// ----3--- unknown
// -----21- unused
// -------0 speaker

uint8_t qvt102_state::mcu_bus_r()
{
	return m_kbd_bus;
}

void qvt102_state::mcu_bus_w(uint8_t data)
{
	m_speaker->level_w(BIT(data, 0));
	m_kbd_bus = data;
}

int qvt102_state::mcu_t0_r()
{
	int state = 1;

	// The keyboard firmware also scans for a key at bit 3, and it appears
	// to be a modifier key and is passed to the host in bit 3 of the
	// second code, but the terminal firmware appears to ignore it. The
	// schematics show no sign of a connection.

	if (BIT(m_kbd_bus, 4) == 0) state &= BIT(m_keys_special->read(), 0);
	if (BIT(m_kbd_bus, 5) == 0) state &= BIT(m_keys_special->read(), 1);
	if (BIT(m_kbd_bus, 6) == 0) state &= BIT(m_keys_special->read(), 2);

	return state;
}

int qvt102_state::mcu_t1_r()
{
	int state = 1;

	for (int i = 0; i < 8; i++)
	{
		if (BIT(m_kbd_p1, i) == 0)
			state &= BIT(m_keys_p1[(m_kbd_bus >> 4) & 7]->read(), i);
		if (BIT(m_kbd_p2, i) == 0)
			state &= BIT(m_keys_p2[(m_kbd_bus >> 4) & 7]->read(), i);
	}

	return state;
}

void qvt102_state::mcu_p1_w(uint8_t data)
{
	m_kbd_p1 = data;
}

void qvt102_state::mcu_p2_w(uint8_t data)
{
	m_kbd_p2 = data;

	m_kbd_data = !BIT(data, 7);

	// The keyboard serial data is read by the host using a bit banger in
	// the IRQ handler and it is a tight loop requiring good
	// synchronization between the host CPU and keyboard controller.  A
	// word starts with a raising of the line which (inverted) triggers
	// the IRQ and the handler then waits and synchronizes to a falling
	// edge. There is a delay of about 189us before this falling edge. The
	// IRQ handler then reads the bits in a tight loop, reading the last
	// after around 332 usec.
	//
	// The strategy here is to boost the interleave when the line is
	// written high, and to hold this boost for 350us. This ensures that
	// the boost lasts for the critical section of the IRQ handler.
	//
	if (m_kbd_data)
		machine().scheduler().perfect_quantum(attotime::from_usec(350));

	m_irqs->in_w<2>(m_kbd_data);
}


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

uint8_t qvt102_state::vsync_ack_r()
{
	m_irqs->in_w<0>(CLEAR_LINE);
	return 0;
}

void qvt102_state::vsync_w(int state)
{
	if (state)
		m_irqs->in_w<0>(ASSERT_LINE);
}

MC6845_UPDATE_ROW( qvt102_state::crtc_update_row )
{
	const rgb_t *palette = m_palette->palette()->entry_list_raw();

	// line attribute (active for the rest of the line)
	uint8_t attr = 0;

	for (int x = 0; x < x_count; x++)
	{
		uint16_t mem = (ma + x) & 0x7ff;
		uint8_t chr = m_vram[mem];

		// address lines 10 and 11 for the character rom get a special handling
		int a10 = (chr & 0x40) || ((chr & 0xe0) == 0);
		int a11 = 0;

		switch (m_jumper->read() & 0x30)
		{
		case 0x00: a11 = 0; break;    // US
		case 0x10: a11 = a10; break;  // German
		case 0x20: a11 = !a10; break; // UK
		}

		uint16_t gfx = m_char_rom[(a11 << 11) | (a10 << 10) | ((chr << 4) & 0x3f0) | ra] << 1;

		// d0 for drawing codes (avoids gap)
		if (chr >= 0x80 && chr <= 0x9f)
			gfx |= BIT(gfx, 1);

		// half intensity is defined per character
		int half = BIT(chr, 7);

		// check for new attribute
		if (chr >= 0x90 && chr <= 0x9f)
		{
			attr = chr;
			half ^= BIT(m_jumper->read(), 6);
		}

		// draw 9 pixels of the character
		for (int i = 0; i < 9; i++)
		{
			// from schematics
			bool p = !(BIT(m_latch, 3) & BIT(attr, 1)); // blink
			p = !(p & BIT(gfx, i) & (!BIT(attr, 0))); // rom/blank
			p = !(p & !((BIT(attr, 3) & (ra == 11)))); // underline
			p = p ^ (BIT(attr, 2) ^ ((x == cursor_x) | BIT(m_latch, 2))); // reverse

			bitmap.pix(y, x*9 + (8-i)) = palette[p ? 2 - half : 0];
		}

	}
}

static const gfx_layout char_layout =
{
	8,12,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8*16
};

static GFXDECODE_START(chars)
	GFXDECODE_ENTRY("chargen", 0, char_layout, 0, 1)
GFXDECODE_END


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void qvt102_state::latch_w(uint8_t data)
{
	// 7------- host to keyboard
	// -6------ aux print
	// --5----- ctc cs0
	// ---4---- dtri
	// ----3--- blink
	// -----2-- rv
	// ------1- eia/cl
	// -------0 aux en

	m_latch = data;

	// jumper w3 enables this connection
	if (machine().phase() != machine_phase::INIT)
		if (BIT(m_jumper->read(), 2))
			m_host->write_dtr(BIT(data, 4));

	m_kbdmcu->set_input_line(MCS48_INPUT_IRQ, BIT(data, 7) ? ASSERT_LINE : CLEAR_LINE);
}

uint8_t qvt102_state::kbd_r()
{
	// 7------- keyboard to host
	// -6------ dtr2

	uint8_t data = 0;

	data |= !(m_aux->dsr_r() || m_aux->dcd_r()) << 6;
	data |= !m_kbd_data << 7;

	return data;
}

uint8_t qvt102_state::ctc_r()
{
	return m_ctc->read(BIT(m_latch, 5));
}

void qvt102_state::ctc_w(uint8_t data)
{
	m_ctc->write(BIT(m_latch, 5), data);
}

void qvt102_state::acia_txd_w(int state)
{
	if (BIT(m_latch, 6) == 0)
	{
		// print
		m_aux->write_txd(state);
	}
	else
	{
		// aux enable
		if (BIT(m_latch, 0) == 0)
			m_aux->write_txd(state);

		m_host->write_txd(state);
	}
}

void qvt102_state::acia_rts_w(int state)
{
	m_host->write_rts(state);

	// jumper w4 enables rts output to dtr
	if (machine().phase() != machine_phase::INIT)
		if (BIT(m_jumper->read(), 3))
			m_host->write_dtr(state);
}

void qvt102_state::dsr_w(int state)
{
	if (machine().phase() != machine_phase::INIT)
		if (BIT(m_jumper->read(), 0))
			m_acia->write_dcd(state);
}

void qvt102_state::host_rxd_w(int state)
{
	m_acia->write_rxd(state);

	// aux enable
	if (BIT(m_latch, 0) == 0)
		m_aux->write_txd(state);
}

void qvt102_state::host_dcd_w(int state)
{
	if (machine().phase() != machine_phase::INIT)
		if (BIT(m_jumper->read(), 1))
			m_acia->write_dcd(state);
}

void qvt102_state::machine_start()
{
	m_kbd_data = 0;
	m_kbd_bus = m_kbd_p1 = m_kbd_p2 = 0xff;

	// register for save states
	save_item(NAME(m_latch));
	save_item(NAME(m_kbd_data));
	save_item(NAME(m_kbd_bus));
	save_item(NAME(m_kbd_p1));
	save_item(NAME(m_kbd_p2));
}

void qvt102_state::machine_reset()
{
	m_latch = 0;
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void qvt102_state::qvt102(machine_config &config)
{
	M6800(config, m_maincpu, 16.6698_MHz_XTAL / 18);
	m_maincpu->set_addrmap(AS_PROGRAM, &qvt102_state::mem_map);

	INPUT_MERGER_ANY_HIGH(config, m_irqs).output_handler().set_inputline(m_maincpu, M6800_IRQ_LINE);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // 2x TC5514-APL + 3V battery

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_color(rgb_t::green());
	m_screen->set_raw(16.6698_MHz_XTAL, 882, 9, 729, 315, 0, 300); // 80x24+1
	m_screen->set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	GFXDECODE(config, "gfxdecode", m_palette, chars);

	HD6845S(config, m_crtc, 16.6698_MHz_XTAL / 9);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(9);
	m_crtc->set_update_row_callback(FUNC(qvt102_state::crtc_update_row));
	m_crtc->out_vsync_callback().set(FUNC(qvt102_state::vsync_w));

	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set(FUNC(qvt102_state::acia_txd_w));
	m_acia->rts_handler().set(FUNC(qvt102_state::acia_rts_w));
	m_acia->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<1>));

	RS232_PORT(config, m_host, default_rs232_devices, nullptr);
	m_host->rxd_handler().set(FUNC(qvt102_state::host_rxd_w));
	m_host->cts_handler().set(m_acia, FUNC(acia6850_device::write_cts));
	m_host->dsr_handler().set(FUNC(qvt102_state::dsr_w));
	m_host->dcd_handler().set(FUNC(qvt102_state::host_dcd_w));

	RS232_PORT(config, m_aux, default_rs232_devices, nullptr);
	m_aux->dsr_handler().set(FUNC(qvt102_state::dsr_w));

	Z80CTC(config, m_ctc, 16.6698_MHz_XTAL / 9);
	m_ctc->set_clk<0>(16.6698_MHz_XTAL / 18);
	m_ctc->set_clk<1>(16.6698_MHz_XTAL / 18);
	m_ctc->zc_callback<0>().set(m_acia, FUNC(acia6850_device::write_txc));
	m_ctc->zc_callback<1>().set(m_acia, FUNC(acia6850_device::write_rxc));

	I8748(config, m_kbdmcu, 6_MHz_XTAL);
	m_kbdmcu->bus_in_cb().set(FUNC(qvt102_state::mcu_bus_r));
	m_kbdmcu->bus_out_cb().set(FUNC(qvt102_state::mcu_bus_w));
	m_kbdmcu->t0_in_cb().set(FUNC(qvt102_state::mcu_t0_r));
	m_kbdmcu->t1_in_cb().set(FUNC(qvt102_state::mcu_t1_r));
	m_kbdmcu->p1_out_cb().set(FUNC(qvt102_state::mcu_p1_w));
	m_kbdmcu->p2_out_cb().set(FUNC(qvt102_state::mcu_p2_w));

	// sound hardware (on keyboard)
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.0);
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( qvt102 )
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("t205m.u8", 0x0000, 0x2000, CRC(59cc04f6) SHA1(ee2e3a3ea7b57a231483fcc74266f0f3f51204af))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("c3205m.u32", 0x0000, 0x1000, CRC(f6d86e87) SHA1(c0885e4a35095a730d760bf91a1cf4e8edd6a2bb))

	ROM_REGION(0x400, "kbdmcu", 0)
	ROM_LOAD("k301.u302",  0x000, 0x400, CRC(67564b20) SHA1(5897ff920f8fae4aa498d3a4dfd45b58183c041d))
ROM_END

ROM_START( qvt102a )
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("t205p.u8", 0x0000, 0x2000, CRC(2e375abc) SHA1(12ad1e49c5773c36c3a8d65845c9a50f9dec141f))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("c3205m.u32", 0x0000, 0x1000, CRC(f6d86e87) SHA1(c0885e4a35095a730d760bf91a1cf4e8edd6a2bb))

	ROM_REGION(0x400, "kbdmcu", 0)
	ROM_LOAD("k301.u302", 0x000, 0x400, CRC(67564b20) SHA1(5897ff920f8fae4aa498d3a4dfd45b58183c041d))
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS         INIT        COMPANY  FULLNAME    FLAGS
COMP( 1983, qvt102,  0,      0,      qvt102,  qvt102,  qvt102_state, empty_init, "Qume",  "QVT-102",  MACHINE_SUPPORTS_SAVE )
COMP( 1983, qvt102a, qvt102, 0,      qvt102,  qvt102,  qvt102_state, empty_init, "Qume",  "QVT-102A", MACHINE_SUPPORTS_SAVE )



qvt103.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************************

Skeleton driver for Qume QVT-103 video display terminal.

************************************************************************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/nvram.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "video/crt9007.h"
#include "emupal.h"
#include "screen.h"


namespace {

class qvt103_state : public driver_device
{
public:
	qvt103_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_p_chargen(*this, "chargen")
		, m_vram(*this, "vram")
	{ }

	void qvt103(machine_config &config);

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_region_ptr<u8> m_p_chargen;
	required_shared_ptr<u8> m_vram;
};

u32 qvt103_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	for (int col = 0; col < 25; col++)
	{
		for (int row = 0; row < 80; row++)
		{
			uint8_t code = m_vram[col * 80 + row] & 0x7f;

			for (int y = 0; y < 12; y++)
			{
				uint16_t gfx = m_p_chargen[code << 4 | y];

				for (int x = 0; x < 8; x++)
					bitmap.pix(col*12 + y, row*8 + (7 - x)) = BIT(gfx, x) ? rgb_t::white() : rgb_t::black();
			}
		}
	}

	return 0;
}

void qvt103_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x5fff).rom().region("maincpu", 0);
	map(0x6000, 0x6001).rw("kbdmcu", FUNC(i8741a_device::upi41_master_r), FUNC(i8741a_device::upi41_master_w));
//  map(0x6000, 0x6001).lr8("test", [this]() -> u8 { return machine().rand(); }); // uncomment to pass kbd test
	map(0x8000, 0x87ff).ram().share("nvram");
	map(0xa000, 0xa03f).rw("vpac", FUNC(crt9007_device::read), FUNC(crt9007_device::write));
	map(0xc000, 0xdfff).ram(); // not entirely contiguous?
	map(0xe000, 0xffff).ram().share("vram");
}

void qvt103_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x14, 0x17).rw("dart", FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
	map(0x18, 0x1b).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
}

static INPUT_PORTS_START( qvt103 )
INPUT_PORTS_END

static const gfx_layout char_layout =
{
	8,12,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 7, 0, 1, 2, 3, 4, 5, 6 }, // drawing chars look better with 0, 1, 2, 3, 4, 5, 6, 7
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8*16
};

static GFXDECODE_START(chars)
	GFXDECODE_ENTRY("chargen", 0, char_layout, 0, 1)
GFXDECODE_END

static const z80_daisy_config daisy_chain[] =
{
	{ "dart" },
	{ "ctc" },
	{ nullptr }
};

void qvt103_state::qvt103(machine_config &config)
{
	Z80(config, m_maincpu, 29.376_MHz_XTAL / 9); // divider guessed
	m_maincpu->set_addrmap(AS_PROGRAM, &qvt103_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &qvt103_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // TC5516APL + battery

	z80ctc_device &ctc(Z80CTC(config, "ctc", 29.376_MHz_XTAL / 9));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80dart_device &dart(Z80DART(config, "dart", 29.376_MHz_XTAL / 9));
	dart.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(29.376_MHz_XTAL * 2 / 3, 102 * 10, 0, 80 * 10, 320, 0, 300);
	//screen.set_raw(29.376_MHz_XTAL, 170 * 9, 0, 132 * 9, 320, 0, 300);
	screen.set_screen_update(FUNC(qvt103_state::screen_update));

	PALETTE(config, "palette", palette_device::MONOCHROME_HIGHLIGHT);

	GFXDECODE(config, "gfxdecode", "palette", chars);

	crt9007_device &vpac(CRT9007(config, "vpac", 29.376_MHz_XTAL / 15));
	vpac.set_character_width(10);
	vpac.int_callback().set("ctc", FUNC(z80ctc_device::trg3));

	I8741A(config, "kbdmcu", 6_MHz_XTAL);
}

/**************************************************************************************************************

Qume QVT-103.
Chips: Z80A, Z80A DART, Z80A CTC, 2x CRT9212, 5x HM6116P-2, TC5516APL, D8741AD, CRT9007, 1x 10-sw dip, Button battery.
Crystals: (all hard to read) 29.376, 6.000
Keyboard CPU, Crystal, ROM are on the main board.

***************************************************************************************************************/

ROM_START( qvt103 )
	ROM_REGION(0x6000, "maincpu", 0)
	ROM_LOAD( "t103e1.u28", 0x0000, 0x2000, CRC(eace3cbe) SHA1(1e7f395c5233d8656df5305163d050275f0a8033) )
	ROM_LOAD( "t103e2.u27", 0x2000, 0x4000, CRC(100cf542) SHA1(4b2569d509790a0f94b4447fb9d3d42582fcaf66) )

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "c103b.u40",  0x0000, 0x1000, CRC(3419760d) SHA1(3455c70ed48c7f7769d73a84f152beddf508094f) )

	ROM_REGION(0x0400, "kbdmcu", 0)
	ROM_LOAD( "k304a.u24",  0x0000, 0x0400, CRC(e4b1f0da) SHA1(e9f8c48c34105464b3db206b34f67e7603484fea) )
ROM_END

} // anonymous namespace


COMP( 1983, qvt103, 0, 0, qvt103, qvt103, qvt103_state, empty_init, "Qume", "QVT-103", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



qvt190.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/****************************************************************************

    Qume QVT-190

    Hardware:
    - MC68B00P
    - 2x MC68B50P
    - MC68B45P
    - V61C16P55L
    - M5M5165P-70L
    - ABHGA101006
    - button battery, 7-DIL-jumper

    Crystal: unreadable (but likely to be 16.6698)

****************************************************************************/

#include "emu.h"
#include "cpu/m6800/m6800.h"
#include "machine/6850acia.h"
#include "machine/nvram.h"
#include "machine/z80ctc.h"
#include "video/mc6845.h"
#include "emupal.h"
#include "screen.h"


namespace {

class qvt190_state : public driver_device
{
public:
	qvt190_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_palette(*this, "palette")
		, m_p_chargen(*this, "chargen")
		, m_videoram(*this, "videoram")
	{ }

	void qvt190(machine_config &config);

private:
	MC6845_UPDATE_ROW(update_row);

	void qvt190_mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<palette_device> m_palette;
	required_region_ptr<u8> m_p_chargen;
	required_shared_ptr<u8> m_videoram;
};

MC6845_UPDATE_ROW(qvt190_state::update_row)
{
	const rgb_t *palette = m_palette->palette()->entry_list_raw();

	for (int x = 0; x < x_count; x++)
	{
		uint16_t mem = (ma + x) & 0x7ff;
		uint8_t chr = m_videoram[mem];
		uint16_t gfx = m_p_chargen[(chr << 4) | ra];

		if (x == cursor_x)
			gfx = ~gfx;

		for (int i = 0; i < 9; i++)
			bitmap.pix(y, x*9 + (8-i)) = palette[BIT(gfx, i) ? 2 : 0];
	}
}

void qvt190_state::qvt190_mem_map(address_map &map)
{
	map(0x0000, 0x07ff).ram().share("nvram");
	map(0x2500, 0x2501).rw("acia1", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0x2600, 0x2601).rw("acia2", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0x2800, 0x2800).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x2801, 0x2801).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x4000, 0x47ff).ram().share("videoram");
	map(0x8000, 0xffff).rom().region("maincpu", 0);
}

static INPUT_PORTS_START( qvt190 )
INPUT_PORTS_END

static const gfx_layout char_layout =
{
	8,12,
	RGN_FRAC(1,1), // 512
	1,
	{ 0 },
	{ STEP8(0,1) },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8*16
};

static const gfx_layout drawing_char_layout =
{
	8,12,
	16,
	1,
	{ 0 },
	{ STEP8(0,1) },
	{
		0x000*8+12*8, 0x000*8+13*8, 0x000*8+14*8, 0x000*8+15*8,
		0x200*8+12*8, 0x200*8+13*8, 0x200*8+14*8, 0x200*8+15*8,
		0x400*8+12*8, 0x400*8+13*8, 0x400*8+14*8, 0x400*8+15*8
	},
	8*16
};

static GFXDECODE_START(chars)
	GFXDECODE_ENTRY("chargen", 0, char_layout, 0, 1)
	GFXDECODE_ENTRY("chargen", 0, drawing_char_layout, 0, 1)
GFXDECODE_END

void qvt190_state::qvt190(machine_config &config)
{
	M6800(config, m_maincpu, 16.6698_MHz_XTAL / 9);
	m_maincpu->set_addrmap(AS_PROGRAM, &qvt190_state::qvt190_mem_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // V61C16P55L + battery

	ACIA6850(config, "acia1", 0);

	ACIA6850(config, "acia2", 0);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(16.6698_MHz_XTAL, 882, 18, 738, 315, 0, 300);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	GFXDECODE(config, "gfxdecode", m_palette, chars);

	mc6845_device &crtc(MC6845(config, "crtc", 16.6698_MHz_XTAL / 9));
	crtc.set_screen("screen");
	crtc.set_char_width(9);
	crtc.set_update_row_callback(FUNC(qvt190_state::update_row));
}

ROM_START( qvt190 )
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD( "95987-267.u19", 0x0000, 0x8000, CRC(78894d8e) SHA1(0a0f6883dd18872bddeb3ed18ebe496080e6591b) )

	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD( "95864-304.u17", 0x0000, 0x2000, CRC(2792e99b) SHA1(4a84d029d0e63975fc95dc7056d2523193dff986) )
ROM_END

} // anonymous namespace


COMP( 1987, qvt190, 0, 0, qvt190, qvt190, qvt190_state, empty_init, "Qume", "QVT-190", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



qvt201.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************************

Skeleton driver for Qume QVT-201 & QVT-202 display terminals.

************************************************************************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "bus/rs232/rs232.h"
#include "machine/input_merger.h"
#include "machine/mc68681.h"
#include "machine/nvram.h"
#include "video/scn2674.h"
#include "emupal.h"
#include "screen.h"


namespace {

class qvt201_state : public driver_device
{
public:
	qvt201_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mainnmi(*this, "mainnmi")
		, m_eia(*this, "eia")
		, m_screen(*this, "screen")
		, m_p_chargen(*this, "chargen")
		, m_dataram(*this, "dataram")
		, m_attram(*this, "attram")
	{ }

	void qvt201(machine_config &config);

private:
	[[maybe_unused]] SCN2672_DRAW_CHARACTER_MEMBER(draw_character);

	void offset_w(uint8_t data);
	void keyboard_w(uint8_t data);
	uint8_t keyboard_r();
	void duart_out_w(uint8_t data);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<input_merger_device> m_mainnmi;
	required_device<rs232_port_device> m_eia;
	required_device<screen_device> m_screen;
	required_region_ptr<u8> m_p_chargen;
	required_shared_ptr<u8> m_dataram;
	required_shared_ptr<u8> m_attram;
};


SCN2672_DRAW_CHARACTER_MEMBER(qvt201_state::draw_character)
{
}

void qvt201_state::offset_w(uint8_t data)
{
}

void qvt201_state::keyboard_w(uint8_t data)
{
}

uint8_t qvt201_state::keyboard_r()
{
	return 1;
}

void qvt201_state::duart_out_w(uint8_t data)
{
	// OP0 = RTS (EIA pin 4)
	// OP1 = DTR (EIA pin 20)
	// OP2 not used?
	// OP3 = 132/_80
	// OP4 = SRV
	// OP5 = BLOCK/_UL
	// OP6 = preset MBC NMI flipflop
	// OP7 = _DATA/TALK (EIA pin 14)

	m_eia->write_rts(BIT(data, 0));
	m_eia->write_dtr(BIT(data, 1));
	m_mainnmi->in_w<1>(!BIT(data, 6));
}

void qvt201_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("maincpu", 0);
	map(0x8800, 0x8fff).ram().share("nvram");
	map(0x9000, 0x9007).rw("crtc", FUNC(scn2672_device::read), FUNC(scn2672_device::write));
	map(0x9800, 0x980f).rw("duart", FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0xa000, 0xa000).w(FUNC(qvt201_state::offset_w));
	map(0xa800, 0xa800).w(FUNC(qvt201_state::keyboard_w));
	map(0xb000, 0xb000).r(FUNC(qvt201_state::keyboard_r));
	map(0xc000, 0xdfff).ram().share("dataram");
	map(0xe000, 0xffff).ram().share("attram");
}

static INPUT_PORTS_START( qvt201 )
INPUT_PORTS_END

static const gfx_layout char_layout =
{
	8,10,
	RGN_FRAC(1,1), // 256
	1,
	{ 0 },
	{ STEP8(0,1) },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8 },
	8*16
};

// ascii control code chars
// those are also at 0x80 to 0x9f in the normal char decode
// don't know why they are duplicated here
static const gfx_layout ctrl_char_layout =
{
	8,10,
	RGN_FRAC(1,4), // 32
	1,
	{ 0 },
	{ STEP8(0,1) },
	{ 10*8, 11*8, 12*8, 13*8, 14*8, 16*8+10*8, 16*8+11*8, 16*8+12*8, 16*8+13*8, 16*8+14*8 },
	8*32
};

// 64 bytes of data remain undecoded
// byte 10 and 11 from 0x400 to 0x7ff in the rom
// (0x000 to 0x3ff are the control chars above, 0x800 to 0xfff is 0xff)

static GFXDECODE_START(chars)
	GFXDECODE_ENTRY("chargen", 0, char_layout, 0, 1)
	GFXDECODE_ENTRY("chargen", 0, ctrl_char_layout, 0, 1)
GFXDECODE_END

void qvt201_state::qvt201(machine_config &config)
{
	Z80(config, m_maincpu, 3.6864_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &qvt201_state::mem_map); // IORQ is not used at all

	input_merger_device &mainint(INPUT_MERGER_ANY_HIGH(config, "mainint")); // open collector
	mainint.output_handler().set_inputline("maincpu", INPUT_LINE_IRQ0);

	input_merger_device &mainnmi(INPUT_MERGER_ALL_HIGH(config, "mainnmi"));
	mainnmi.output_handler().set_inputline("maincpu", INPUT_LINE_NMI);

	scn2681_device &duart(SCN2681(config, "duart", 3.6864_MHz_XTAL)); // XTAL not directly connected
	duart.irq_cb().set("mainint", FUNC(input_merger_device::in_w<1>));
	duart.a_tx_cb().set(m_eia, FUNC(rs232_port_device::write_txd));
	duart.b_tx_cb().set("aux", FUNC(rs232_port_device::write_txd));
	duart.outport_cb().set(FUNC(qvt201_state::duart_out_w));

	RS232_PORT(config, m_eia, default_rs232_devices, nullptr);
	m_eia->rxd_handler().set("duart", FUNC(scn2681_device::rx_a_w));

	rs232_port_device &aux(RS232_PORT(config, "aux", default_rs232_devices, nullptr));
	aux.rxd_handler().set("duart", FUNC(scn2681_device::rx_b_w));
	aux.dsr_handler().set("duart", FUNC(scn2681_device::ip4_w));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // TC5516APL-2 or uPD446C-2 + battery

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(48.654_MHz_XTAL / 3, 102 * 10, 0, 80 * 10, 265, 0, 250);
	//screen.set_raw(48.654_MHz_XTAL / 2, 170 * 9, 0, 132 * 9, 265, 0, 250);
	screen.set_screen_update("crtc", FUNC(scn2672_device::screen_update));

	PALETTE(config, "palette", palette_device::MONOCHROME_HIGHLIGHT);

	GFXDECODE(config, "gfxdecode", "palette", chars);

	scn2672_device &crtc(SCN2672(config, "crtc", 48.654_MHz_XTAL / 30));
	crtc.set_character_width(10); // 9 in 132-column mode
	crtc.intr_callback().set("mainint", FUNC(input_merger_device::in_w<0>));
	crtc.breq_callback().set("mainnmi", FUNC(input_merger_device::in_w<0>));
	crtc.set_screen("screen");
}


/**************************************************************************************************************

Qume QVT-201.
Chips: Z80A, SCN2681A, SCN2672B, 4x HM6116P-2, D446C-2, button battery
Crystals: (from schematics, unreadable on photo) 48.654 MHz (Y1), 3.6864 MHz (Y2)
Board is marked QVT-202 LB10 REV2 74 6 26.
Printed label on PCB: 301488-02 REV.2
                      MFG:607   QC:PASS

***************************************************************************************************************/

ROM_START( qvt201 )
	ROM_REGION(0x8000, "maincpu", 0) // "Program Contents ©1986 Qume Corp."
	ROM_LOAD( "390410-002.u11", 0x0000, 0x4000, CRC(69337561) SHA1(022e49bf5e8d76a3c2cc5af65630d3f77cc32bc1) )
	ROM_LOAD( "390410-001.u10", 0x4000, 0x4000, CRC(977cc138) SHA1(a019980ea6da2dce53617bced420014ab4e03ec8) )

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "301847-01.u42",  0x0000, 0x1000, CRC(546ed236) SHA1(312d57a7012f50327310bd11bda000149f13342e) )
ROM_END

} // anonymous namespace


COMP( 1986, qvt201, 0, 0, qvt201, qvt201, qvt201_state, empty_init, "Qume", "QVT-201 (Rev. T201VE)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



qvt70.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Dirk Best
/****************************************************************************

    Qume QVT-70/QVT-82 terminal

    QVT-70:
    - Z80 (Z8040008VSC)
    - Z80 DART (Z0847006PSC)
    - QUME 303489-01 QFP144
    - DTC 801000-02 QFP100
    - ROM 128k + 64k
    - CXK5864CM-70LL (8k, next to ROMs)
    - W242575-70LL (32k) + 5x CXK5864CM-70LL (8k)
    - DS1231
    - Beeper + Battery
    - XTAL unreadable

    Features:
    - 65 hz with 16x16 characters
    - 78 hz with 16x13 characters
    - 64 background/foreground colors
    - 80/132 columns

    QVT-82:
    - Z80 (Z0840008PSC)
    - Z80 DART (Z0847006PSC)
    - QUME 303489-01 QFP144
    - ROM 64k * 2
    - RAM 8k UM6264AK-10L (above Z80) + 8k UM6264K-70L * 3 (below Z80)
    - DS1231
    - 54.2857MHz XTAL
    - Battery

****************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/z80sio.h"
#include "bus/centronics/ctronics.h"
#include "bus/rs232/rs232.h"

#include "emupal.h"
#include "screen.h"


namespace {

class qvt70_state : public driver_device
{
public:
	qvt70_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_rombank(*this, "rom"),
		m_rambank(*this, "ram%d", 0U),
		m_gfxdecode(*this, "gfxdecode")
	{ }

	void qvt70(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_memory_bank m_rombank;
	required_memory_bank_array<2> m_rambank;
	required_device<gfxdecode_device> m_gfxdecode;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void vblank_w(int state);

	void rombank_w(uint8_t data);

	// gate array
	void voffset_lsb_w(uint8_t data);
	void voffset_msb_w(uint8_t data);
	uint8_t unk_1d_r();
	void unk_1d_w(uint8_t data);
	uint8_t unk_1e_r();
	void unk_1e_w(uint8_t data);
	void unk_1f_w(uint8_t data);
	uint8_t unk_32_r();
	void unk_42_w(uint8_t data);
	void unk_60_w(uint8_t data);

	std::unique_ptr<uint8_t[]> m_ram;

	uint8_t m_1d, m_1e, m_1f;
	bool m_nmi_enable;

	uint16_t m_voffset;
};

void qvt70_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).bankr(m_rombank);
	map(0x8000, 0x8000).w(FUNC(qvt70_state::rombank_w));
	map(0xa000, 0xbfff).ram();
	map(0xc000, 0xdfff).bankrw(m_rambank[0]);
	map(0xe000, 0xffff).bankrw(m_rambank[1]);
}

void qvt70_state::io_map(address_map &map)
{
	map.global_mask(0xff);
//  map(0x07, 0x07) // ? (set to 0x5d)
//  map(0x08, 0x08) // ? (set to 0x00)
//  map(0x09, 0x09) // ? (set to 0x00)
	map(0x0a, 0x0a).w(FUNC(qvt70_state::voffset_lsb_w));
	map(0x0b, 0x0b).w(FUNC(qvt70_state::voffset_msb_w));
//  map(0x0c, 0x0c) // ? (set to 0x5e then 0x67)
//  map(0x0d, 0x0d) // ? (set to 0x0c then 0x04)
//  map(0x0e, 0x0e) // ? (set to 0x0f then 0x07)
//  map(0x0f, 0x0f) // ? (set to 0x06 then 0x07)
//  map(0x10, 0x10) // columns? (set to 0x50 = 80)
//  map(0x11, 0x11) // columns? (set to 0x84 = 132)
//  map(0x12, 0x12) // ? (set to 0x31 = 49)
//  map(0x13, 0x13) // rows? (set to 0x19 = 25)
//  map(0x14, 0x14) // ? (set to 0x39 = 57)
//  map(0x15, 0x15) // ? (set to 0x60 = 96)
//  map(0x16, 0x16) // ? (set to 0x39 = 57)
//  map(0x17, 0x17) // debug output? (used during memtest)
//  map(0x18, 0x18) // ? (set to 0x20 = 32)
//  map(0x19, 0x19) // ? (set to 0x0f = 15)
//  map(0x1a, 0x1a) // ? (set to 0x00 then 0xff)
//  map(0x1b, 0x1b) // ? (set to 0x20 = 32)
//  map(0x1c, 0x1c) // ? (set to 0x50 then 0xff)
	map(0x1d, 0x1d).rw(FUNC(qvt70_state::unk_1d_r), FUNC(qvt70_state::unk_1d_w)); // ram banking? status?
	map(0x1e, 0x1e).rw(FUNC(qvt70_state::unk_1e_r), FUNC(qvt70_state::unk_1e_w)); // ram banking?
	map(0x1f, 0x1f).w(FUNC(qvt70_state::unk_1f_w));
//  map(0x20, 0x20) // ? (set to 0x00)
//  map(0x21, 0x21) // ? (set to 0x00)
//  map(0x22, 0x22) // ? (set to 0x00)
//  map(0x23, 0x23) // ? (set to 0x00)
//  map(0x24, 0x24) // ? (set to 0x00)
//  map(0x25, 0x25) // ? (set to 0xa0)
//  map(0x26, 0x26) // ? (set to 0xff)
//  map(0x27, 0x27) // ? (set to 0x80)
//  map(0x28, 0x28) // ? (set to 0x9f)
	// 29-31
	map(0x32, 0x32).r(FUNC(qvt70_state::unk_32_r)); // keyboard data?
	// 33-41
	map(0x42, 0x42).w(FUNC(qvt70_state::unk_42_w)); // nmi enable?
	// 43-54
	map(0x60, 0x60).w(FUNC(qvt70_state::unk_60_w)); // nmi enable?
	// 61-6f
	map(0x80, 0x83).rw("dart", FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
}

static INPUT_PORTS_START( qvt70 )
	PORT_START("1d")
	PORT_DIPUNKNOWN_DIPLOC(0x01, 0x00, "1d:1")
	PORT_DIPUNKNOWN_DIPLOC(0x02, 0x02, "1d:2")
	PORT_DIPUNKNOWN_DIPLOC(0x04, 0x00, "1d:3")
	PORT_DIPUNKNOWN_DIPLOC(0x08, 0x00, "1d:4")
	PORT_DIPUNKNOWN_DIPLOC(0x10, 0x00, "1d:5")
	PORT_DIPUNKNOWN_DIPLOC(0x20, 0x00, "1d:6")
	PORT_DIPUNKNOWN_DIPLOC(0x40, 0x00, "1d:7")
	PORT_DIPUNKNOWN_DIPLOC(0x80, 0x00, "1d:8")

	PORT_START("1e")
	PORT_DIPUNKNOWN_DIPLOC(0x01, 0x00, "1e:1")
	PORT_DIPUNKNOWN_DIPLOC(0x02, 0x00, "1e:2")
	PORT_DIPUNKNOWN_DIPLOC(0x04, 0x00, "1e:3")
	PORT_DIPUNKNOWN_DIPLOC(0x08, 0x00, "1e:4")
	PORT_DIPUNKNOWN_DIPLOC(0x10, 0x00, "1e:5")
	PORT_DIPUNKNOWN_DIPLOC(0x20, 0x00, "1e:6")
	PORT_DIPUNKNOWN_DIPLOC(0x40, 0x00, "1e:7")
	PORT_DIPUNKNOWN_DIPLOC(0x80, 0x00, "1e:8")

	PORT_START("32")
	PORT_DIPUNKNOWN_DIPLOC(0x01, 0x00, "32:1")
	PORT_DIPUNKNOWN_DIPLOC(0x02, 0x00, "32:2")
	PORT_DIPUNKNOWN_DIPLOC(0x04, 0x00, "32:3")
	PORT_DIPUNKNOWN_DIPLOC(0x08, 0x00, "32:4")
	PORT_DIPUNKNOWN_DIPLOC(0x10, 0x00, "32:5")
	PORT_DIPUNKNOWN_DIPLOC(0x20, 0x00, "32:6")
	PORT_DIPUNKNOWN_DIPLOC(0x40, 0x00, "32:7")
	PORT_DIPUNKNOWN_DIPLOC(0x80, 0x00, "32:8")
INPUT_PORTS_END

static const gfx_layout char_layout_8x16 =
{
	8, 16,
	256,
	1,
	{ 0 },
	{ STEP8(0, 1) },
	{ STEP16(0, 8) },
	8*16
};

static const gfx_layout char_layout_8x9 =
{
	8, 9,
	256,
	1,
	{ 0 },
	{ STEP8(0, 1) },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8 },
	8*16
};

static GFXDECODE_START(chars)
	GFXDECODE_RAM(nullptr, 0, char_layout_8x16, 0, 1)
	GFXDECODE_RAM(nullptr, 0, char_layout_8x9, 0, 1)
GFXDECODE_END

uint32_t qvt70_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if (m_voffset == 0)
		return 0;

	for (int y = 0; y < 27; y++)
	{
		for (int x = 0; x < 132; x++)
		{
			uint8_t code = m_maincpu->space(AS_PROGRAM).read_byte(m_voffset + (y * 132) + x);

			// draw 16 lines
			for (int i = 0; i < 16; i++)
			{
				uint8_t data = m_ram[0x2000 * 7 | ((code << 4) + i)];

				// 8 pixels of the character
				for (int p = 0; p < 8; p++)
					bitmap.pix(y * 16 + i, x * 8 + p) = BIT(data, 7 - p) ? rgb_t::white() : rgb_t::black();
			}
		}
	}

	return 0;
}

void qvt70_state::vblank_w(int state)
{
	if (state == 1 && m_nmi_enable)
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}

void qvt70_state::voffset_lsb_w(uint8_t data)
{
	m_voffset = (m_voffset & 0xff00) | data;
}

void qvt70_state::voffset_msb_w(uint8_t data)
{
	m_voffset = (data << 8) | (m_voffset & 0x00ff);
	logerror("voffset = %04x\n", m_voffset);
}

uint8_t qvt70_state::unk_1d_r()
{
	// 7654---- unknown
	// ----3--- data available on io port 32
	// -----210 unknown

	uint8_t val = 0;
	val = ioport("1d")->read();
//  val = machine().rand();
	logerror("1d read: %02x\n", val);

	return val;
}

void qvt70_state::unk_1d_w(uint8_t data)
{
	logerror("1d = %02x\n", data);

	m_1d = data;

	// banking is likely wrong

	// the vram test sets 1d = 04 and wants c000-dfff and e000-ffff to
	// point to the same area

	// c000-dfff
	switch (data & 0x0c)
	{
	case 0x00: m_rambank[0]->set_entry(0); break;
	case 0x04: m_rambank[0]->set_entry(1); break;
	case 0x08: m_rambank[0]->set_entry(2); break;
	case 0x0c: m_rambank[0]->set_entry(3); break;
	}

	// e000-ffff
	switch (data & 0xc0)
	{
	case 0x00: m_rambank[1]->set_entry(1); break;
	case 0x40: m_rambank[1]->set_entry(4 + BIT(m_1e, 3)); break;
	case 0x80: m_rambank[1]->set_entry(6 + BIT(m_1e, 2)); break;
	case 0xc0: m_rambank[1]->set_entry(8); break;
	}
}

uint8_t qvt70_state::unk_1e_r()
{
	uint8_t val = 0;
	val = ioport("1e")->read();
//  val = machine().rand();
	logerror("1e read: %02x\n", val);

	return val;
}

void qvt70_state::unk_1e_w(uint8_t data)
{
	logerror("1e = %02x\n", data);
	m_1e = data;
}

void qvt70_state::unk_1f_w(uint8_t data)
{
	logerror("1f = %02x\n", data);
	m_1f = data;
}

uint8_t qvt70_state::unk_32_r()
{
	uint8_t val = 0;
	val = ioport("32")->read();
//  val = machine().rand();
	logerror("32 read: %02x\n", val);

	return val;
}

void qvt70_state::unk_42_w(uint8_t data)
{
	logerror("42 = %02x\n", data);
	m_nmi_enable = bool(BIT(data, 0));
}

void qvt70_state::unk_60_w(uint8_t data)
{
	logerror("60 = %02x\n", data);
//  m_nmi_enable = bool(BIT(data, 7));
}

void qvt70_state::rombank_w(uint8_t data)
{
	if (data & ~0x19)
		logerror("rombank_w: %02x\n", data);

	// 765-----  unknown
	// ---43---  bankswitching
	// -----21-  unknown
	// -------0  bankswitching

	switch (data & 0x19)
	{
		case 0x00: m_rombank->set_entry(0); break;
		case 0x08: m_rombank->set_entry(1); break;
		case 0x10: m_rombank->set_entry(2); break;
		case 0x18: m_rombank->set_entry(3); break;
		case 0x01: m_rombank->set_entry(4); break;
		case 0x11: m_rombank->set_entry(5); break;

		default:
			logerror("Unknown ROM bank: %02x\n", data);
	}
}

void qvt70_state::machine_start()
{
	m_rombank->configure_entries(0, 6, memregion("maincpu")->base(), 0x8000);

	m_ram = std::make_unique<uint8_t[]>(0x12000);
	m_rambank[0]->configure_entries(0, 9, &m_ram[0], 0x2000);
	m_rambank[1]->configure_entries(0, 9, &m_ram[0], 0x2000);

	m_gfxdecode->gfx(0)->set_source(&m_ram[0x2000*7 + 0x0000]);
	m_gfxdecode->gfx(1)->set_source(&m_ram[0x2000*7 + 0x1000]);

	save_pointer(NAME(m_ram), 0x12000);
}

void qvt70_state::machine_reset()
{
	m_nmi_enable = false;
}

void qvt70_state::qvt70(machine_config &config)
{
	Z80(config, m_maincpu, 8'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &qvt70_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &qvt70_state::io_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(70);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // not accurate
	screen.set_screen_update(FUNC(qvt70_state::screen_update));
	screen.set_size(1056, 432);
	screen.set_visarea(0, 1056-1, 0, 432-1);
	screen.screen_vblank().set(FUNC(qvt70_state::vblank_w));

	PALETTE(config, "palette").set_entries(64);

	GFXDECODE(config, m_gfxdecode, "palette", chars);

	Z80DART(config, "dart", 4'000'000);
	// dart irq connected to maincpu int

	// 25-pin (dcd, rxd, txd, dtr, dsr, rts, cts and current loop)
	RS232_PORT(config, "serial1", default_rs232_devices, nullptr);

	// 9-pin (dcd, rxd, txd, dtr, dsr, rts, cts)
	RS232_PORT(config, "serial2", default_rs232_devices, nullptr);

	CENTRONICS(config, "parallel", centronics_devices, nullptr);
}

ROM_START( qvt70 )
	ROM_REGION(0x30000, "maincpu", 0)
	ROM_LOAD( "251513-03_revj.u11", 0x00000, 0x20000, CRC(c56796fe) SHA1(afe024ff93d5e75dc18041219d61e1a22fc6d883) ) // 251513-03  C/S:D1DA  95' REV.J (checksum matches)
	ROM_LOAD( "251513-04_revj.u12", 0x20000, 0x10000, CRC(3960bbd5) SHA1(9db306cef09be21ff43c081ebe11e9b46f617861) ) // 251513-04  C/S:18D0  95' REV.J (checksum matches)
ROM_END

ROM_START( qvt82 )
	ROM_REGION(0x30000, "maincpu", 0)
	ROM_LOAD( "304229-02d_revd.u6", 0x00000, 0x10000, CRC(597431df) SHA1(10c4669b759dd7cfd6746e54dc12807197cf841a) ) // 304229-02D  QVT-82 REV. D  U6 (BF7F) (checksum matches)
	ROM_LOAD( "304229-01d_revd.u5", 0x20000, 0x10000, CRC(9ebd09b6) SHA1(ef9f002016d05b770e7b66d15f05fc286bd022d9) ) // 304229-01D  QVT-82 REV. D  U5 (462B) (checksum matches)
ROM_END

} // anonymous namespace


COMP( 1992, qvt70, 0, 0, qvt70, qvt70, qvt70_state, empty_init, "Qume", "QVT-70", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1993, qvt82, 0, 0, qvt70, qvt70, qvt70_state, empty_init, "Qume", "QVT-82", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



qx10.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Mariusz Wojcieszek, Angelo Salese, Brian Johnson
/***************************************************************************

    QX-10

    Preliminary driver by Mariusz Wojcieszek

    Status:
    Driver boots and load CP/M from floppy image. Needs upd7220 for gfx

    Done:
    - preliminary memory map
    - sound
    - floppy (upd765)
    - DMA
    - Interrupts (pic8295)

    banking:
    - 0x1c = 0
    map(0x0000,0x1fff).rom()
    map(0x2000,0xdfff).noprw()
    map(0xe000,0xffff).ram()
    - 0x1c = 1 0x20 = 1
    map(0x0000,0x7fff).ram() (0x18 selects bank)
    map(0x8000,0x87ff) CMOS
    map(0x8800,0xdfff).noprw() or previous bank?
    map(0xe000,0xffff).ram()
    - 0x1c = 1 0x20 = 0
    map(0x0000,0xdfff).ram() (0x18 selects bank)
    map(0xe000,0xffff).ram()
****************************************************************************/


#include "emu.h"

#include "bus/centronics/ctronics.h"
#include "bus/epson_qx/keyboard/keyboard.h"
#include "bus/epson_qx/option.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "imagedev/snapquik.h"
#include "machine/am9517a.h"
#include "machine/i8255.h"
#include "machine/mc146818.h"
#include "machine/output_latch.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/ram.h"
#include "machine/upd765.h"
#include "machine/z80sio.h"
#include "sound/spkrdev.h"
#include "video/upd7220.h"

#include "emupal.h"
#include "speaker.h"
#include "screen.h"
#include "softlist_dev.h"


namespace {

#define MAIN_CLK    15974400

#define RS232_TAG   "rs232"

/*
    Driver data
*/

class qx10_state : public driver_device
{
public:
	qx10_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_pit_1(*this, "pit8253_1"),
		m_pit_2(*this, "pit8253_2"),
		m_pic_m(*this, "pic8259_master"),
		m_pic_s(*this, "pic8259_slave"),
		m_scc(*this, "upd7201"),
		m_ppi(*this, "i8255"),
		m_dma_1(*this, "8237dma_1"),
		m_dma_2(*this, "8237dma_2"),
		m_fdc(*this, "upd765"),
		m_floppy(*this, "upd765:%u", 0U),
		m_hgdc(*this, "upd7220"),
		m_rtc(*this, "rtc"),
		m_kbd(*this, "kbd"),
		m_centronics(*this, "centronics"),
		m_bus(*this, "bus"),
		m_speaker(*this, "speaker"),
		m_vram_bank(0),
		m_char_rom(*this, "chargen"),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen"),
		m_ram(*this, RAM_TAG),
		m_palette(*this, "palette"),
		m_ram_view(*this, "ramview")
	{
	}

	void qx10(machine_config &config);

private:
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual void video_start() override ATTR_COLD;

	void update_memory_mapping();

	void update_speaker();

	void qx10_18_w(uint8_t data);
	void prom_sel_w(uint8_t data);
	void cmos_sel_w(uint8_t data);
	void qx10_upd765_interrupt(int state);
	void update_fdd_motor(uint8_t state);
	void fdd_motor_w(uint8_t data);
	uint8_t qx10_30_r();
	void zoom_w(uint8_t data);
	void tc_w(int state);
	void sqw_out(uint8_t state);
	IRQ_CALLBACK_MEMBER( inta_call );
	uint8_t get_slave_ack(offs_t offset);
	uint8_t vram_bank_r();
	void vram_bank_w(uint8_t data);
	uint16_t vram_r(offs_t offset);
	void vram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint8_t memory_read_byte(offs_t offset);
	void memory_write_byte(offs_t offset, uint8_t data);

	uint8_t portb_r();
	void portc_w(uint8_t data);

	void centronics_busy_handler(uint8_t state);
	void centronics_fault_handler(uint8_t state);
	void centronics_perror_handler(uint8_t state);
	void centronics_select_handler(uint8_t state);
	void centronics_sense_handler(uint8_t state);

	void keyboard_clk(int state);
	void keyboard_irq(int state);
	void speaker_freq(int state);
	void speaker_duration(int state);

	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);

	void qx10_palette(palette_device &palette) const;
	void dma_hrq_changed(int state);

	UPD7220_DISPLAY_PIXELS_MEMBER( hgdc_display_pixels );
	UPD7220_DRAW_TEXT_LINE_MEMBER( hgdc_draw_text );

	void qx10_io(address_map &map) ATTR_COLD;
	void qx10_mem(address_map &map) ATTR_COLD;
	void upd7220_map(address_map &map) ATTR_COLD;

	required_device<pit8253_device> m_pit_1;
	required_device<pit8253_device> m_pit_2;
	required_device<pic8259_device> m_pic_m;
	required_device<pic8259_device> m_pic_s;
	required_device<upd7201_device> m_scc;
	required_device<i8255_device> m_ppi;
	required_device<am9517a_device> m_dma_1;
	required_device<am9517a_device> m_dma_2;
	required_device<upd765a_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<upd7220_device> m_hgdc;
	required_device<mc146818_device> m_rtc;
	required_device<bus::epson_qx::keyboard::keyboard_port_device> m_kbd;
	required_device<centronics_device> m_centronics;
	required_device<bus::epson_qx::option_bus_device> m_bus;
	required_device<speaker_sound_device>   m_speaker;
	uint8_t m_vram_bank;
	//required_shared_ptr<uint8_t> m_video_ram;
	std::unique_ptr<uint16_t[]> m_video_ram;
	required_region_ptr<uint8_t> m_char_rom;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<ram_device> m_ram;
	required_device<palette_device> m_palette;

	/* FDD */
	int     m_fdcint = 0;
	uint8_t  m_motor_clk = 0;
	uint16_t m_counter = 0;
	//int     m_fdcready = 0;

	int m_spkr_enable = 0;
	int m_spkr_freq = 0;
	int m_pit1_out0 = 0;

	/* centronics */
	int m_centronics_error = 0;
	int m_centronics_busy = 0;
	int m_centronics_paper = 0;
	int m_centronics_select = 0;
	int m_centronics_sense = 0;


	/* memory */
	memory_view m_ram_view;
	int     m_external_bank = 0;
	int     m_membank = 0;
	int     m_memprom = 0;
	int     m_memcmos = 0;
	uint8_t   m_cmosram[0x800]{};

	uint8_t m_color_mode = 0;
	uint8_t m_zoom = 0;
};

UPD7220_DISPLAY_PIXELS_MEMBER( qx10_state::hgdc_display_pixels )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	int gfx[3];
	address &= 0xffff;
	if(m_color_mode)
	{
		gfx[0] = m_video_ram[address + 0x00000];
		gfx[1] = m_video_ram[address + 0x10000];
		gfx[2] = m_video_ram[address + 0x20000];
	}
	else
	{
		gfx[0] = m_video_ram[address];
		gfx[1] = 0;
		gfx[2] = 0;
	}

	for(int xi=0;xi<16;xi++)
	{
		uint8_t pen;
		pen = ((gfx[0] >> xi) & 1) ? 1 : 0;
		pen|= ((gfx[1] >> xi) & 1) ? 2 : 0;
		pen|= ((gfx[2] >> xi) & 1) ? 4 : 0;
		for (int z = 0; z <= m_zoom; ++z)
		{
			int xval = ((x+xi)*(m_zoom+1))+z;
			if (xval >= bitmap.cliprect().width() * 2)
				continue;
			bitmap.pix(y, xval) = palette[pen];
		}
	}
}

UPD7220_DRAW_TEXT_LINE_MEMBER( qx10_state::hgdc_draw_text )
{
	const rgb_t *palette = m_palette->palette()->entry_list_raw();
	addr &= 0xffff;

	for (int x = 0; x < pitch; x++)
	{
		int tile = m_video_ram[((addr+x)*2) >> 1] & 0xff;
		int attr = m_video_ram[((addr+x)*2) >> 1] >> 8;

		// TODO: color mode support
		uint8_t color = (m_color_mode) ? 1 : (attr & 4) ? 2 : 1;

		for (int yi = 0; yi < lr; yi++)
		{
			uint8_t tile_data = (m_char_rom[tile*16+yi]);

			if(attr & 8)
				tile_data^=0xff;

			if(cursor_on && cursor_addr == addr+x)
				tile_data^=0xff;

			 //TODO: check for blinking interval
			if(attr & 0x80 && m_screen->frame_number() & 0x10)
				tile_data=0;

			for (int xi = 0; xi < 8; xi++)
			{
				int res_x = ((x * 8) + xi) * (m_zoom + 1);
				int res_y = y + (yi * (m_zoom + 1));

				// TODO: cpm22mf:flop2 display random character test will go out of bounds here
				if(!m_screen->visible_area().contains(res_x, res_y))
					continue;

				uint8_t pen;
				if(yi >= 16)
					pen = 0;
				else
					pen = ((tile_data >> xi) & 1) ? color : 0;

				for (int zx = 0; zx <= m_zoom; ++zx)
				{
					for (int zy = 0; zy <= m_zoom; ++zy)
					{
						if(pen)
							bitmap.pix(res_y+zy, res_x+zx) = palette[pen];
					}
				}
			}
		}
	}
}

uint32_t qx10_state::screen_update( screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect )
{
	bitmap.fill(m_palette->black_pen(), cliprect);

	m_hgdc->screen_update(screen, bitmap, cliprect);

	return 0;
}

/*
    Sound
*/
void qx10_state::update_speaker()
{

	/*
	 *                 freq -----
	 * pit1_out0 -----            NAND ---- level
	 *                 NAND -----
	 * !enable   -----
	 */

	uint8_t level = ((!m_spkr_enable && m_pit1_out0) || !m_spkr_freq) ? 1 : 0;
	m_speaker->level_w(level);
}

/*
    Memory
*/
void qx10_state::update_memory_mapping()
{
	int drambank = -1;

	if (m_membank & 1)
	{
		drambank = 0;
	}
	else if (m_membank & 2)
	{
		drambank = 1;
	}
	else if (m_membank & 4)
	{
		drambank = 2;
	}
	else if (m_membank & 8)
	{
		drambank = 3;
	}

	if (drambank >= 0 || !m_memprom || m_memcmos)
	{
		m_ram_view.select(0);
		if (!m_memprom)
		{
			membank("bank1")->set_base(memregion("maincpu")->base());
		}
		else
		{
			membank("bank1")->set_base(m_ram->pointer() + drambank*64*1024);
		}
		if (m_memcmos)
		{
			membank("bank2")->set_base(m_cmosram);
		}
		else
		{
			membank("bank2")->set_base(m_ram->pointer() + drambank*64*1024 + 32*1024);
		}
	}
	else
	{
		if (m_external_bank)
		{
			m_ram_view.select(1);
		}
		else
		{
			m_ram_view.disable();
		}
	}

}

void qx10_state::qx10_18_w(uint8_t data)
{
	m_membank = (data >> 4) & 0x0f;
	m_spkr_enable = (data >> 2) & 0x01;
	m_external_bank = (data >> 3) & 0x01;
	m_pit_1->write_gate2(BIT(data, 1));
	m_pit_1->write_gate0(data & 1);
	update_speaker();
	update_memory_mapping();
}

void qx10_state::prom_sel_w(uint8_t data)
{
	m_memprom = data & 1;
	update_memory_mapping();
}

void qx10_state::cmos_sel_w(uint8_t data)
{
	m_memcmos = data & 1;
	update_memory_mapping();
}

void qx10_state::zoom_w(uint8_t data)
{
	m_zoom = data & 0x0f;
}

/***********************************************************

    Quickload

    This loads a .COM file to address 0x100 then jumps
    there. Sometimes .COM has been renamed to .CPM to
    prevent windows going ballistic. These can be loaded
    as well.

************************************************************/

QUICKLOAD_LOAD_MEMBER(qx10_state::quickload_cb)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);

	if (image.length() >= 0xfd00)
		return std::make_pair(image_error::INVALIDLENGTH, std::string());

	/* The right RAM bank must be active */
	m_membank = 0;
	update_memory_mapping();

	/* Avoid loading a program if CP/M-80 is not in memory */
	if ((prog_space.read_byte(0) != 0xc3) || (prog_space.read_byte(5) != 0xc3))
	{
		machine_reset();
		return std::make_pair(image_error::UNSUPPORTED, std::string());
	}

	/* Load image to the TPA (Transient Program Area) */
	uint16_t quickload_size = image.length();
	for (uint16_t i = 0; i < quickload_size; i++)
	{
		uint8_t data;
		if (image.fread( &data, 1) != 1)
			return std::make_pair(image_error::UNSPECIFIED, std::string());
		prog_space.write_byte(i+0x100, data);
	}

	/* clear out command tail */
	prog_space.write_byte(0x80, 0);   prog_space.write_byte(0x81, 0);

	/* Roughly set SP basing on the BDOS position */
	m_maincpu->set_state_int(Z80_SP, 256 * prog_space.read_byte(7) - 300);
	m_maincpu->set_pc(0x100);       // start program

	return std::make_pair(std::error_condition(), std::string());
}

/*
    FDD
*/

static void qx10_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

void qx10_state::qx10_upd765_interrupt(int state)
{
	m_fdcint = state;

	//logerror("Interrupt from upd765: %d\n", state);
	// signal interrupt
	m_pic_m->ir6_w(state);
}

void qx10_state::update_fdd_motor(uint8_t state)
{
	for (auto& fdd : m_floppy)
	{
		floppy_image_device *floppy = fdd->get_device();
		if (floppy)
		{
			floppy->mon_w(state);
		}
	}
}

void qx10_state::fdd_motor_w(uint8_t data)
{
	m_counter = 0;
	update_fdd_motor(0);
	// motor off controlled by clock
}

uint8_t qx10_state::qx10_30_r()
{
	floppy_image_device *floppy1,*floppy2;

	floppy1 = m_floppy[0]->get_device();
	floppy2 = m_floppy[1]->get_device();

	return m_fdcint |
			BIT(m_counter, 11) << 1 |
			((floppy1 != nullptr) || (floppy2 != nullptr) ? 1 : 0) << 3 |
			m_membank << 4;
}

void qx10_state::centronics_busy_handler(uint8_t state)
{
	m_centronics_busy = state;
}

void qx10_state::centronics_perror_handler(uint8_t state)
{
	m_centronics_paper = state;
}

void qx10_state::centronics_fault_handler(uint8_t state)
{
	m_centronics_error = state;
}

void qx10_state::centronics_select_handler(uint8_t state)
{
	m_centronics_select = state;
}

void qx10_state::centronics_sense_handler(uint8_t state)
{
	m_centronics_sense = state;
}

uint8_t qx10_state::portb_r()
{
	uint8_t status = 0;

	status |= m_centronics_error  << 3;
	status |= m_centronics_paper  << 4;
	status |= m_centronics_busy   << 5;
	status |= m_centronics_sense  << 6;
	status |= m_centronics_select << 7;

	return status;
}

void qx10_state::portc_w(uint8_t data)
{
	m_centronics->write_strobe(BIT(data, 0));
	m_centronics->write_autofd(BIT(data, 4));
	m_centronics->write_init(!BIT(data, 5));
	m_pic_s->ir0_w(BIT(data, 3));
}

/*
    DMA8237
*/
void qx10_state::dma_hrq_changed(int state)
{
	/* Assert HLDA */
	m_dma_1->hack_w(state);
}

void qx10_state::tc_w(int state)
{
	/* floppy terminal count */
	m_fdc->tc_w(!state);
	m_bus->slots_w<&bus::epson_qx::option_slot_device::eopf>(state);
}

/*
    8237 DMA (Master)
    Channel 1: Floppy disk
    Channel 2: GDC
    Channel 3: Option slots
*/
uint8_t qx10_state::memory_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	return prog_space.read_byte(offset);
}

void qx10_state::memory_write_byte(offs_t offset, uint8_t data)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	return prog_space.write_byte(offset, data);
}

/*
    8237 DMA (Slave)
    Channel 1: Option slots #1
    Channel 2: Option slots #2
    Channel 3: Option slots #3
    Channel 4: Option slots #4
*/

/*
    MC146818
*/
void qx10_state::sqw_out(uint8_t state)
{
	uint8_t clk = !(state || BIT(m_counter, 11));
	uint16_t cnt = m_counter;

	if (!clk && m_motor_clk)
	{
		cnt = (cnt + 1) & 0xfff;
	}
	if (BIT(cnt, 11) && !BIT(m_counter, 11))
	{
		update_fdd_motor(1);
	}

	m_motor_clk = clk;
	m_counter = cnt;
}

void qx10_state::keyboard_irq(int state)
{
	m_scc->m1_r(); // always set
	m_pic_m->ir4_w(state);
}

void qx10_state::keyboard_clk(int state)
{
	// clock keyboard too
	m_kbd->clk_w(state);
	m_scc->rxca_w(state);
	m_scc->txca_w(state);
}

void qx10_state::speaker_duration(int state)
{
	m_pit1_out0 = state;
	update_speaker();
}

void qx10_state::speaker_freq(int state)
{
	m_spkr_freq = state;
	update_speaker();
}

/*
    Master PIC8259
    IR0     Power down detection interrupt
    IR1     Software timer #1 interrupt
    IR2     External interrupt INTF1
    IR3     External interrupt INTF2
    IR4     Keyboard/RS232 interrupt
    IR5     CRT/lightpen interrupt
    IR6     Floppy controller interrupt
    IR7     Slave cascade
*/

IRQ_CALLBACK_MEMBER(qx10_state::inta_call)
{
	uint32_t vector = m_pic_m->acknowledge() << 16;
	vector |= m_pic_m->acknowledge();
	vector |= m_pic_m->acknowledge() << 8;
	return vector;
}

uint8_t qx10_state::get_slave_ack(offs_t offset)
{
	if (offset==7) { // IRQ = 7
		return m_pic_s->acknowledge();
	}
	return 0x00;
}


/*
    Slave PIC8259
    IR0     Printer interrupt
    IR1     External interrupt #1
    IR2     Calendar clock interrupt
    IR3     External interrupt #2
    IR4     External interrupt #3
    IR5     Software timer #2 interrupt
    IR6     External interrupt #4
    IR7     External interrupt #5

*/

uint8_t qx10_state::vram_bank_r()
{
	return m_vram_bank;
}

void qx10_state::vram_bank_w(uint8_t data)
{
	if(m_color_mode)
	{
		m_vram_bank = data & 7;
		if(data != 1 && data != 2 && data != 4)
			printf("%02x\n",data);
	}
}

void qx10_state::qx10_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xdfff).view(m_ram_view);
	m_ram_view[0](0x0000, 0x7fff).bankrw("bank1");
	m_ram_view[0](0x8000, 0xdfff).bankrw("bank2");
	m_ram_view[1](0x0000, 0xdfff).unmaprw();
	map(0xe000, 0xffff).ram();
}

void qx10_state::qx10_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).rw(m_pit_1, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x04, 0x07).rw(m_pit_2, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x08, 0x09).rw(m_pic_m, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x0c, 0x0d).rw(m_pic_s, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x10, 0x13).rw(m_scc, FUNC(upd7201_device::cd_ba_r), FUNC(upd7201_device::cd_ba_w));
	map(0x14, 0x17).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x18, 0x1b).portr("DSW").w(FUNC(qx10_state::qx10_18_w));
	map(0x1c, 0x1f).w(FUNC(qx10_state::prom_sel_w));
	map(0x20, 0x23).w(FUNC(qx10_state::cmos_sel_w));
	map(0x2c, 0x2c).portr("CONFIG");
	map(0x2d, 0x2d).rw(FUNC(qx10_state::vram_bank_r), FUNC(qx10_state::vram_bank_w));
	map(0x30, 0x33).rw(FUNC(qx10_state::qx10_30_r), FUNC(qx10_state::fdd_motor_w));
	map(0x34, 0x35).m(m_fdc, FUNC(upd765a_device::map));
	map(0x38, 0x39).rw(m_hgdc, FUNC(upd7220_device::read), FUNC(upd7220_device::write));
	map(0x3a, 0x3a).w(FUNC(qx10_state::zoom_w));
//  map(0x3b, 0x3b) GDC light pen req
	map(0x3c, 0x3c).rw(m_rtc, FUNC(mc146818_device::data_r), FUNC(mc146818_device::data_w));
	map(0x3d, 0x3d).w(m_rtc, FUNC(mc146818_device::address_w));
	map(0x40, 0x4f).rw(m_dma_1, FUNC(am9517a_device::read), FUNC(am9517a_device::write));
	map(0x50, 0x5f).rw(m_dma_2, FUNC(am9517a_device::read), FUNC(am9517a_device::write));
}

/* Input ports */
/* TODO: shift break */
/*INPUT_CHANGED_MEMBER(qx10_state::key_stroke)
{
    if(newval && !oldval)
    {
        m_keyb.rx = uint8_t(param & 0x7f);
        m_pic_m->ir4_w(1);
    }

    if(oldval && !newval)
        m_keyb.rx = 0;
}*/

static INPUT_PORTS_START( qx10 )
	/* TODO: All of those have unknown meaning */
	PORT_START("DSW")
	PORT_DIPNAME( 0x01, 0x00, "DSW" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) ) //CMOS related
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("CONFIG")
	PORT_CONFNAME( 0x03, 0x02, "Video Board" )
	PORT_CONFSETTING( 0x02, "Monochrome" )
	PORT_CONFSETTING( 0x01, "Color" )
	PORT_BIT(0xfc, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END


void qx10_state::machine_start()
{
	m_bus->set_memview(m_ram_view[1]);
}

void qx10_state::machine_reset()
{
	m_dma_1->dreq0_w(1);
	m_dma_1->dreq1_w(1);

	m_spkr_enable = 0;
	m_pit1_out0 = 1;

	m_zoom = 0;

	m_external_bank = 0;
	m_memprom = 0;
	m_memcmos = 0;
	m_membank = 0;
	update_memory_mapping();

	{
		int i;

		/* TODO: is there a bit that sets this up? */
		m_color_mode = ioport("CONFIG")->read() & 1;

		if(m_color_mode) //color
		{
			for ( i = 0; i < 8; i++ )
				m_palette->set_pen_color(i, pal1bit((i >> 2) & 1), pal1bit((i >> 1) & 1), pal1bit((i >> 0) & 1));
		}
		else //monochrome
		{
			for ( i = 0; i < 8; i++ )
				m_palette->set_pen_color(i, pal1bit(0), pal1bit(0), pal1bit(0));

			m_palette->set_pen_color(1, 0x00, 0x9f, 0x00);
			m_palette->set_pen_color(2, 0x00, 0xff, 0x00);
			m_vram_bank = 0;
		}
	}
}

/* F4 Character Displayer */
static const gfx_layout qx10_charlayout =
{
	8, 16,                  /* 8 x 16 characters */
	RGN_FRAC(1,1),          /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	/* y offsets */
	{  0*8,  1*8,  2*8,  3*8,  4*8,  5*8,  6*8,  7*8, 8*8,  9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_qx10 )
	GFXDECODE_ENTRY( "chargen", 0x0000, qx10_charlayout, 1, 1 )
GFXDECODE_END

void qx10_state::video_start()
{
	// allocate memory
	m_video_ram = make_unique_clear<uint16_t[]>(0x30000);
}

void qx10_state::qx10_palette(palette_device &palette) const
{
	// ...
}

uint16_t qx10_state::vram_r(offs_t offset)
{
	int bank = 0;

	if (m_vram_bank & 1)     { bank = 0; } // B
	else if(m_vram_bank & 2) { bank = 1; } // G
	else if(m_vram_bank & 4) { bank = 2; } // R

	return m_video_ram[offset + (0x10000 * bank)];
}

void qx10_state::vram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	int bank = 0;

	if (m_vram_bank & 1)     { bank = 0; } // B
	else if(m_vram_bank & 2) { bank = 1; } // G
	else if(m_vram_bank & 4) { bank = 2; } // R

	COMBINE_DATA(&m_video_ram[offset + (0x10000 * bank)]);
}

void qx10_state::upd7220_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(qx10_state::vram_r), FUNC(qx10_state::vram_w)).mirror(0x30000);
}

void qx10_state::qx10(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, MAIN_CLK / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &qx10_state::qx10_mem);
	m_maincpu->set_addrmap(AS_IO, &qx10_state::qx10_io);
	m_maincpu->set_irq_acknowledge_callback(FUNC(qx10_state::inta_call));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_screen_update(FUNC(qx10_state::screen_update));
	m_screen->set_raw(16.67_MHz_XTAL, 872, 152, 792, 421, 4, 404);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_qx10);
	PALETTE(config, m_palette, FUNC(qx10_state::qx10_palette), 8);

	/* Devices */

/*
    Timer 0
    Counter CLK                         Gate                    OUT             Operation
    0       Keyboard clock (1200bps)    Memory register D0      Speaker timer   Speaker timer (100ms)
    1       Keyboard clock (1200bps)    +5V                     8259A (10E) IR5 Software timer
    2       Clock 1,9668MHz             Memory register D7      8259 (12E) IR1  Software timer
*/
	PIT8253(config, m_pit_1, 0);
	m_pit_1->set_clk<0>(1200);
	m_pit_1->out_handler<0>().set(FUNC(qx10_state::speaker_duration));
	m_pit_1->set_clk<1>(1200);
	m_pit_1->out_handler<1>().set(m_pic_s, FUNC(pic8259_device::ir5_w));
	m_pit_1->set_clk<2>(MAIN_CLK / 8);
	m_pit_1->out_handler<2>().set(m_pic_m, FUNC(pic8259_device::ir1_w));

/*
    Timer 1
    Counter CLK                 Gate        OUT                 Operation
    0       Clock 1,9668MHz     +5V         Speaker frequency   1kHz
    1       Clock 1,9668MHz     +5V         Keyboard clock      1200bps (Clock / 1664)
    2       Clock 1,9668MHz     +5V         RS-232C baud rate   9600bps (Clock / 208)
*/
	PIT8253(config, m_pit_2, 0);
	m_pit_2->set_clk<0>(MAIN_CLK / 8);
	m_pit_2->out_handler<0>().set(FUNC(qx10_state::speaker_freq));
	m_pit_2->set_clk<1>(MAIN_CLK / 8);
	m_pit_2->out_handler<1>().set(FUNC(qx10_state::keyboard_clk));
	m_pit_2->set_clk<2>(MAIN_CLK / 8);
	m_pit_2->out_handler<2>().set(m_scc, FUNC(upd7201_device::rxtxcb_w));

	PIC8259(config, m_pic_m, 0);
	m_pic_m->out_int_callback().set_inputline(m_maincpu, 0);
	m_pic_m->in_sp_callback().set_constant(1);
	m_pic_m->read_slave_ack_callback().set(FUNC(qx10_state::get_slave_ack));

	PIC8259(config, m_pic_s, 0);
	m_pic_s->out_int_callback().set(m_pic_m, FUNC(pic8259_device::ir7_w));
	m_pic_s->in_sp_callback().set_constant(0);

	UPD7201(config, m_scc, MAIN_CLK/4); // channel b clock set by pit2 channel 2
	// Channel A: Keyboard
	m_scc->out_txda_callback().set(m_kbd, FUNC(bus::epson_qx::keyboard::keyboard_port_device::rxd_w));
	// Channel B: RS232
	m_scc->out_txdb_callback().set(RS232_TAG, FUNC(rs232_port_device::write_txd));
	m_scc->out_dtrb_callback().set(RS232_TAG, FUNC(rs232_port_device::write_dtr));
	m_scc->out_rtsb_callback().set(RS232_TAG, FUNC(rs232_port_device::write_rts));
	m_scc->out_int_callback().set(FUNC(qx10_state::keyboard_irq));

	AM9517A(config, m_dma_1, MAIN_CLK/4);
	m_dma_1->dreq_active_low();
	m_dma_1->out_hreq_callback().set(FUNC(qx10_state::dma_hrq_changed));
	m_dma_1->out_eop_callback().set(FUNC(qx10_state::tc_w));
	m_dma_1->in_memr_callback().set(FUNC(qx10_state::memory_read_byte));
	m_dma_1->out_memw_callback().set(FUNC(qx10_state::memory_write_byte));
	m_dma_1->in_ior_callback<0>().set(m_fdc, FUNC(upd765a_device::dma_r));
	m_dma_1->in_ior_callback<1>().set(m_hgdc, FUNC(upd7220_device::dack_r));
	m_dma_1->out_iow_callback<0>().set(m_fdc, FUNC(upd765a_device::dma_w));
	m_dma_1->out_iow_callback<1>().set(m_hgdc, FUNC(upd7220_device::dack_w));

	AM9517A(config, m_dma_2, MAIN_CLK/4);
	m_dma_2->dreq_active_low();

	I8255(config, m_ppi, 0);
	m_ppi->out_pa_callback().set("prndata", FUNC(output_latch_device::write));
	m_ppi->in_pb_callback().set(FUNC(qx10_state::portb_r));
	m_ppi->out_pc_callback().set(FUNC(qx10_state::portc_w));

	UPD7220(config, m_hgdc, 16.67_MHz_XTAL/4/2);
	m_hgdc->set_addrmap(0, &qx10_state::upd7220_map);
	m_hgdc->set_display_pixels(FUNC(qx10_state::hgdc_display_pixels));
	m_hgdc->set_draw_text(FUNC(qx10_state::hgdc_draw_text));
	m_hgdc->drq_wr_callback().set(m_dma_1, FUNC(am9517a_device::dreq1_w)).invert();
	m_hgdc->set_screen("screen");

	MC146818(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->irq().set(m_pic_s, FUNC(pic8259_device::ir2_w));
	m_rtc->sqw().set(FUNC(qx10_state::sqw_out));

	UPD765A(config, m_fdc, 8'000'000, true, true);
	m_fdc->intrq_wr_callback().set(FUNC(qx10_state::qx10_upd765_interrupt));
	m_fdc->drq_wr_callback().set(m_dma_1, FUNC(am9517a_device::dreq0_w)).invert();
	FLOPPY_CONNECTOR(config, m_floppy[0], qx10_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[1], qx10_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);

	rs232_port_device &rs232(RS232_PORT(config, RS232_TAG, default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_scc, FUNC(upd7201_device::rxb_w));

	EPSON_QX_KEYBOARD_PORT(config, m_kbd, bus::epson_qx::keyboard::keyboard_devices, "qx10_hasci");
	m_kbd->txd_handler().set(m_scc, FUNC(upd7201_device::rxa_w));

	output_latch_device &prndata(OUTPUT_LATCH(config, "prndata"));
	CENTRONICS(config, m_centronics, centronics_devices, nullptr);
	m_centronics->set_output_latch(prndata);
	m_centronics->ack_handler().set(m_ppi, FUNC(i8255_device::pc6_w));
	m_centronics->busy_handler().set(FUNC(qx10_state::centronics_busy_handler));
	m_centronics->perror_handler().set(FUNC(qx10_state::centronics_perror_handler));
	m_centronics->fault_handler().set(FUNC(qx10_state::centronics_fault_handler));
	m_centronics->select_handler().set(FUNC(qx10_state::centronics_select_handler));
	m_centronics->sense_handler().set(FUNC(qx10_state::centronics_sense_handler));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("256K");

	EPSON_QX_OPTION_BUS(config, m_bus, MAIN_CLK / 4);
	m_dma_1->out_iow_callback<2>().set(m_bus, FUNC(bus::epson_qx::option_bus_device::dackf_w));
	m_dma_1->in_ior_callback<2>().set(m_bus, FUNC(bus::epson_qx::option_bus_device::dackf_r));
	m_dma_2->out_iow_callback<0>().set(m_bus, FUNC(bus::epson_qx::option_bus_device::dacks_w<0>));
	m_dma_2->in_ior_callback<0>().set(m_bus, FUNC(bus::epson_qx::option_bus_device::dacks_r<0>));
	m_dma_2->out_iow_callback<1>().set(m_bus, FUNC(bus::epson_qx::option_bus_device::dacks_w<1>));
	m_dma_2->in_ior_callback<1>().set(m_bus, FUNC(bus::epson_qx::option_bus_device::dacks_r<1>));
	m_dma_2->out_iow_callback<2>().set(m_bus, FUNC(bus::epson_qx::option_bus_device::dacks_w<2>));
	m_dma_2->in_ior_callback<2>().set(m_bus, FUNC(bus::epson_qx::option_bus_device::dacks_r<2>));
	m_dma_2->out_iow_callback<3>().set(m_bus, FUNC(bus::epson_qx::option_bus_device::dacks_w<3>));
	m_dma_2->in_ior_callback<3>().set(m_bus, FUNC(bus::epson_qx::option_bus_device::dacks_r<3>));
	m_dma_2->out_eop_callback().set(m_bus, FUNC(bus::epson_qx::option_bus_device::slots_w<&bus::epson_qx::option_slot_device::eops>));
	m_bus->out_drqf_callback().set(m_dma_1, FUNC(am9517a_device::dreq2_w));
	m_bus->out_rdyf_callback().set(m_dma_1, FUNC(am9517a_device::ready_w));
	m_bus->out_drqs_callback<0>().set(m_dma_2, FUNC(am9517a_device::dreq0_w));
	m_bus->out_drqs_callback<1>().set(m_dma_2, FUNC(am9517a_device::dreq1_w));
	m_bus->out_drqs_callback<2>().set(m_dma_2, FUNC(am9517a_device::dreq2_w));
	m_bus->out_drqs_callback<3>().set(m_dma_2, FUNC(am9517a_device::dreq3_w));
	m_bus->out_rdys_callback().set(m_dma_2, FUNC(am9517a_device::ready_w));
	m_bus->out_inth1_callback().set(m_pic_m, FUNC(pic8259_device::ir2_w));
	m_bus->out_inth2_callback().set(m_pic_m, FUNC(pic8259_device::ir3_w));
	m_bus->out_intl_callback<0>().set(m_pic_s, FUNC(pic8259_device::ir1_w));
	m_bus->out_intl_callback<1>().set(m_pic_s, FUNC(pic8259_device::ir3_w));
	m_bus->out_intl_callback<2>().set(m_pic_s, FUNC(pic8259_device::ir4_w));
	m_bus->out_intl_callback<3>().set(m_pic_s, FUNC(pic8259_device::ir6_w));
	m_bus->out_intl_callback<4>().set(m_pic_s, FUNC(pic8259_device::ir7_w));
	m_bus->set_iospace(m_maincpu, AS_IO);
	EPSON_QX_OPTION_BUS_SLOT(config, "option1", m_bus, 0, bus::epson_qx::option_bus_devices, nullptr);
	EPSON_QX_OPTION_BUS_SLOT(config, "option2", m_bus, 1, bus::epson_qx::option_bus_devices, nullptr);
	EPSON_QX_OPTION_BUS_SLOT(config, "option3", m_bus, 2, bus::epson_qx::option_bus_devices, nullptr);
	EPSON_QX_OPTION_BUS_SLOT(config, "option4", m_bus, 3, bus::epson_qx::option_bus_devices, nullptr);
	EPSON_QX_OPTION_BUS_SLOT(config, "option5", m_bus, 4, bus::epson_qx::option_bus_devices, nullptr);

	// software lists
	SOFTWARE_LIST(config, "flop_list").set_original("qx10_flop");

	QUICKLOAD(config, "quickload", "com,cpm", attotime::from_seconds(3)).set_load_callback(FUNC(qx10_state::quickload_cb));
}

/* ROM definition */
ROM_START( qx10 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "v006", "v0.06")
	ROMX_LOAD( "ipl006.bin", 0x0000, 0x0800, CRC(3155056a) SHA1(67cc0ae5055d472aa42eb40cddff6da69ffc6553), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v003", "v0.03")
	ROMX_LOAD( "ipl003.bin", 0x0000, 0x0800, CRC(3cbc4008) SHA1(cc8c7d1aa0cca8f9753d40698b2dc6802fd5f890), ROM_BIOS(1))

	ROM_REGION( 0x1000, "chargen", 0 )
//  ROM_LOAD( "qge.2e",   0x0000, 0x0800, BAD_DUMP CRC(ed93cb81) SHA1(579e68bde3f4184ded7d89b72c6936824f48d10b))  //this one contains special characters only
//  ROM_LOAD( "qge.2e",   0x0000, 0x1000, BAD_DUMP CRC(eb31a2d5) SHA1(6dc581bf2854a07ae93b23b6dfc9c7abd3c0569e))
	ROM_LOAD( "qga.2e",   0x0000, 0x1000, CRC(4120b128) SHA1(9b96f6d78cfd402f8aec7c063ffb70a21b78eff0))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY  FULLNAME  FLAGS */
COMP( 1983, qx10, 0,      0,      qx10,    qx10,  qx10_state, empty_init, "Epson", "QX-10",  MACHINE_NOT_WORKING )



r100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for Kawai R-100 drum machine.

***************************************************************************/

#include "emu.h"
#include "mb63h158.h"
#include "cpu/m6502/m50734.h"
#include "machine/nvram.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"


namespace {

class kawai_r100_state : public driver_device
{
public:
	kawai_r100_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "lcdc")
	{
	}

	void r100(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	HD44780_PIXEL_UPDATE(pixel_update);

	void p0_w(u8 data);
	void p1_w(u8 data);
	void p2_w(u8 data);
	void p3_w(u8 data);
	void buffer_w(u8 data);

	void main_map(address_map &map) ATTR_COLD;
	void data_map(address_map &map) ATTR_COLD;

	required_device<m50734_device> m_maincpu;
	required_device<hd44780_device> m_lcdc;
};


void kawai_r100_state::machine_start()
{
	m_lcdc->rw_w(0); // write only
}

HD44780_PIXEL_UPDATE(kawai_r100_state::pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 16)
		bitmap.pix(line * 8 + y, pos * 6 + x) = state;
}

void kawai_r100_state::p0_w(u8 data)
{
	m_lcdc->rs_w(BIT(data, 3));
	m_lcdc->e_w(BIT(data, 4));
}

void kawai_r100_state::p1_w(u8 data)
{
}

void kawai_r100_state::p2_w(u8 data)
{
}

void kawai_r100_state::p3_w(u8 data)
{
}

void kawai_r100_state::buffer_w(u8 data)
{
}

void kawai_r100_state::main_map(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("nvram1");
	map(0x2000, 0x3fff).ram().share("nvram2");
	map(0x4100, 0x41ff).r("sensor", FUNC(mb63h158_device::read));
	map(0x4200, 0x4200).mirror(0xff).w(m_lcdc, FUNC(hd44780_device::db_w));
	map(0x4300, 0x4300).mirror(0xff).w(FUNC(kawai_r100_state::buffer_w));
	map(0x4400, 0xffff).rom().region("program", 0x4400);
}

void kawai_r100_state::data_map(address_map &map)
{
	// TODO: 0x0000-0x1fff and 0x2000-0x3fff mapped to cartridge slot
}


static INPUT_PORTS_START(r100)
INPUT_PORTS_END

void kawai_r100_state::r100(machine_config &config)
{
	M50734(config, m_maincpu, 16_MHz_XTAL / 2); // M50734SP
	m_maincpu->set_addrmap(AS_PROGRAM, &kawai_r100_state::main_map);
	m_maincpu->set_addrmap(AS_DATA, &kawai_r100_state::data_map);
	m_maincpu->set_p0_3state(0x20);
	m_maincpu->set_p3_3state(0x02);
	m_maincpu->p0_out_cb().set(FUNC(kawai_r100_state::p0_w));
	m_maincpu->p1_out_cb().set(FUNC(kawai_r100_state::p1_w));
	m_maincpu->p2_out_cb().set(FUNC(kawai_r100_state::p2_w));
	m_maincpu->p3_out_cb().set(FUNC(kawai_r100_state::p3_w));
	m_maincpu->p4_in_cb().set_constant(0x0f);

	NVRAM(config, "nvram1", nvram_device::DEFAULT_ALL_0); // MB8464-15LL-SK + battery
	NVRAM(config, "nvram2", nvram_device::DEFAULT_ALL_0); // MB8464-15LL-SK + battery

	MB63H158(config, "sensor", 16_MHz_XTAL / 4);

	//M60009_AGU_DGU(config, "pcm", 5_MHz_XTAL);

	// LCD unit
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6*16, 8*2);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 16);
	m_lcdc->set_pixel_update_cb(FUNC(kawai_r100_state::pixel_update));
}

ROM_START(r100)
	ROM_REGION(0x10000, "program", 0)
	ROM_SYSTEM_BIOS(0, "c", "Revision C")
	ROMX_LOAD("kawai_6p13c.u18", 0x00000, 0x08000, CRC(3d7ed644) SHA1(d47d6c866d19390d781287ae7a69962c4503ce8e), ROM_BIOS(0))
	ROMX_LOAD("kawai_6p14c.u16", 0x08000, 0x08000, CRC(61757eca) SHA1(24331ed8d5842d65ad1b2a472fb708e8c5a140c7), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "b", "Revision B")
	ROMX_LOAD("kawai_6p13b.u18", 0x00000, 0x08000, CRC(53d288a0) SHA1(25d5bcaf4b5146d13c9d75313d3110776dd4e237), ROM_BIOS(1)) // D27C256AD-20
	ROMX_LOAD("kawai_6p14b.u16", 0x08000, 0x08000, CRC(777889bb) SHA1(30fb93afcc2e8738bcc6f991081f269953e49b15), ROM_BIOS(1)) // D27C256AD-20

	ROM_REGION(0x80000, "pcm", 0)
	ROM_LOAD("kawai_mn234001kaa.u20", 0x00000, 0x80000, CRC(aaf1805e) SHA1(5894b4cb03e17a5aa8c2b0c9b1b3d9285009a1c3))
ROM_END

} // anonymous namespace


SYST(1987, r100, 0, 0, r100, r100, kawai_r100_state, empty_init, "Kawai Musical Instrument Manufacturing", "R-100 Digital Drum Machine", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



r9751.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Brandon Munger, Stephen Stair
/******************************************************************************
*
* Rolm CBX 9751 Driver
*
* This driver attempts to emulate the following models:
* * Model 10
* * Model 20
* * Model 40
* * Model 50
* * Model 70
*
* The following software releases are known:
* * 9004
* * 9005
*
* The basis of this driver was influenced by the zexall.c driver by
* Jonathan Gevaryahu.
*
*
* Special Thanks to:
* * Stephen Stair (sgstair)   - for help with reverse engineering,
*               programming, and emulation
* * Felipe Sanches (FSanches) - for help with MESS and emulation
* * Michael Zapf (mizapf)     - for building the HDC9234/HDC9224
*               driver that makes this driver possible
*
* Memory map:
* * 0x00000000 - 0x00ffffff : RAM 12MB to 16MB known, up to 128MB?
* * 0x08000000 - 0x0800ffff : PROM Region
* * 0x5ff00000 - 0x5fffffff : System boards
* * 0xff010000 - 0xfff8ffff : CPU board registers
*
* Working:
* * Floppy Disk IO (PDC device)
* * SMIOC terminal (preliminary)
*
* TODO:
* * Identify registers required for OS booting
* * Hard disk support
* * SMIOC ports (1-8)
* * Identify various LED registers for system boards
* * ROLMLink (RLI) board support
* * Analog Telephone Interface (ATI) board support
* * T1 (T1DN) board support
*
******************************************************************************/

/* Core includes */
#include "emu.h"
#include "cpu/m68000/m68030.h"
#include "machine/terminal.h"

#include "machine/nscsi_bus.h"
#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"

#include "machine/wd33c9x.h"

#include "machine/pdc.h"
#include "machine/smioc.h"
#include "softlist.h"


namespace {

/* Log defines */
#define TRACE_FDC 0
#define TRACE_HDC 0
#define TRACE_LED 0
#define TRACE_DMA 0

/* Trace accesses to other boards in the system */
#define ENABLE_TRACE_ALL_DEVICES 0

/* Trace accesses to 68k system board registers */
#define ENABLE_TRACE_SYSTEM 0
#define SYSTEM_TRACE_LEVEL 5





class r9751_state : public driver_device
{
public:
	r9751_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_pdc(*this, "pdc"),
		m_smioc(*this, "smioc"),
		m_wd33c93(*this, "scsi:7:wd33c93"),
		m_main_ram(*this, "main_ram")
	{
		device_trace_init();
		system_trace_init();
	}
	~r9751_state()
	{
		device_trace_teardown();
		system_trace_teardown();
	}

	void r9751(machine_config &config);

	void init_r9751();

private:

	uint32_t r9751_mmio_5ff_r(offs_t offset);
	void r9751_mmio_5ff_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t r9751_mmio_ff01_r(offs_t offset);
	void r9751_mmio_ff01_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t r9751_mmio_ff05_r(offs_t offset);
	void r9751_mmio_ff05_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t r9751_mmio_fff8_r(offs_t offset);
	void r9751_mmio_fff8_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);

	uint8_t pdc_dma_r(offs_t offset);
	void pdc_dma_w(uint8_t data);
	uint8_t smioc_dma_r(offs_t offset);
	void smioc_dma_w(offs_t offset, uint8_t data);

	void r9751_mem(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<pdc_device> m_pdc;
	required_device<smioc_device> m_smioc;
	required_device<wd33c93_device> m_wd33c93;
	required_shared_ptr<uint32_t> m_main_ram;

	m68000_base_device* ptr_m68000 = nullptr;

	// Begin registers
	uint32_t reg_ff050004 = 0;
	uint32_t reg_fff80040 = 0;
	uint32_t fdd_dest_address = 0; // 5FF080B0
	uint32_t fdd_cmd_complete = 0;
	uint32_t smioc_dma_bank = 0;
	uint32_t fdd_dma_bank = 0;
	attotime timer_32khz_last;
	// End registers

	address_space *m_mem = nullptr;

	// functions
	uint32_t swap_uint32( uint32_t val );
	uint32_t debug_a6();
	uint32_t debug_a5();
	uint32_t debug_a5_20();
	[[maybe_unused]] void UnifiedTrace(u32 address, u32 data, const char* operation="Read", const char* Device="SMIOC", const char* RegisterName=nullptr, const char* extraText=nullptr);

	virtual void machine_reset() override ATTR_COLD;
	void trace_device(int address, int data, const char* direction);

	void system_trace_init();
	void system_trace_teardown();
	void trace_system(int table, int offset, int data, const char* direction);

	uint32_t m_device_trace_enable[2];
	//int m_trace_last_address[64];
	//int m_trace_last_data[64];
	//int m_trace_repeat_count[64];
	//char const * m_trace_last_direction[64];
	//char const * m_generic_command_names[256];
	//char const ** m_specific_command_tables[64];
	void device_trace_init();
	void device_trace_teardown();
	void device_trace_enable_all();
	void device_trace_disable(int device);

	void* system_trace_context;

	static void scsi_devices(device_slot_interface &device);
	void wd33c93(device_t *device);
};

#if ENABLE_TRACE_ALL_DEVICES
// Device tracing
static char const *const DeviceNames[] = {
	nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,"??", // 0x00-0x07
	"??","HDD",nullptr,nullptr,nullptr,nullptr,nullptr,nullptr, // 0x08-0x0F
	nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr, // 0x10-0x17
	nullptr,nullptr,nullptr,nullptr,"SMIOCb","SMIOCb?","SMIOCb?","SMIOCb?", // 0x18-0x1F - Unclear how wide the range is for the second SMIOC range.
	nullptr,nullptr,nullptr,nullptr,"SMIOCa","SMIOCa?","SMIOCa","SMIOCa", // 0x20-0x27 - 0x25 SMIOC we have not observed being used yet.
	"??",nullptr,nullptr,nullptr,"FDC",nullptr,nullptr,nullptr, // 0x28-0x2F
	"??",nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr, // 0x30-0x37
	"SMIOCc?",nullptr,"SMIOCd?",nullptr,nullptr,nullptr,nullptr,"??" // 0x38-0x3F
};

struct GenericCommandRecord {
	u32 AddressPattern;
	const char * Name;
};

static const GenericCommandRecord GenericCommands[] = {
	{ 0x200, "Clear Status" },
	{ 0x800, "Status" },
	{ 0x1000, "Word Count / Result" },
	{ 0x2800, "DMA Busy" },
	{ 0x3000, "Command Result" },
	{ 0x4000, "Command" },  // Also a write length.
	//{ 0x8000, "various" },  // Read address,
	//{ 0xC000, "various" },  // Write address, Read length
};

static const GenericCommandRecord SpecificCommands[] = {
	{ 0x5FF00198, "Special/reinit?" },
	{ 0x5FF08098, "Command Parameter" },
	{ 0x5FF0C098, "DMA Transmit Address" },
	{ 0x5FF0409C, "DMA Transmit Length" },
	{ 0x5FF0809C, "DMA Receive Address" },
	{ 0x5FF0C09C, "DMA Receive Length" },
	{ 0x5FF004B0, "Reset PDC" },
	{ 0x5FF010B0, "Clear result?" },
	{ 0x5FF041B0, "Old style Command" },
	{ 0x5FF080B0, "SCSI Command Address" },
	{ 0x5FF0C1B0, "DMA Receive Address" },
};


void r9751_state::device_trace_init()
{
	for (int i = 0; i < 64; i++)
	{
		m_trace_last_address[i] = -1;
		m_trace_last_data[i] = -1;
		m_trace_repeat_count[i] = 0;
		m_trace_last_direction[i] = nullptr;
		m_specific_command_tables[i] = nullptr;
	}
	for (int i = 0; i < 256; i++)
	{
		m_generic_command_names[i] = "?";
	}

	for (int i = 0; i < sizeof(GenericCommands) / sizeof(GenericCommands[0]); i++)
	{
		int index = (GenericCommands[i].AddressPattern & 0xFF00) >> 8;
		m_generic_command_names[index] = GenericCommands[i].Name;
	}

	for (int i = 0; i < sizeof(SpecificCommands) / sizeof(SpecificCommands[0]); i++)
	{
		int deviceIndex = (SpecificCommands[i].AddressPattern & 0xFC) >> 2;
		char const ** deviceTable = m_specific_command_tables[deviceIndex];
		if (deviceTable == nullptr)
		{
			deviceTable = new char const *[256];
			memset(deviceTable, 0, sizeof(*deviceTable) * 256);
			m_specific_command_tables[deviceIndex] = deviceTable;
		}

		int index = (SpecificCommands[i].AddressPattern & 0xFF00) >> 8;

		deviceTable[index] = SpecificCommands[i].Name;
	}
}
void r9751_state::device_trace_teardown()
{
	for (int i = 0; i < 64; i++)
	{
		delete[] m_specific_command_tables[i];
	}
}

void r9751_state::trace_device(int address, int data, const char* direction)
{
	int dev = (address & 0xFF) / 4;
	char const * devName = "?";
	char const * regName = "?";

	if (!(m_device_trace_enable[(dev >> 5)] & (1 << (dev & 31))))
	{
		// Device is not enabled for tracing.
		return;
	}

	if ((address == m_trace_last_address[dev]) && (data == m_trace_last_data[dev]) && (direction == m_trace_last_direction[dev]))
	{
		m_trace_repeat_count[dev]++;
		return;
	}
	// Compute Device and Register name
	if (DeviceNames[dev] != nullptr)
	{
		devName = DeviceNames[dev];
	}

	if (m_trace_repeat_count[dev] > 0)
	{
		regName = m_generic_command_names[(m_trace_last_address[dev] & 0xFF00) >> 8];
		logerror("Previous access on Device 0x%x (%s) to [%08x] (%s) repeated %d times\n", dev, devName, m_trace_last_address[dev], regName, m_trace_repeat_count[dev]);
	}
	m_trace_repeat_count[dev] = 0;
	m_trace_last_address[dev] = address;
	m_trace_last_data[dev] = data;
	m_trace_last_direction[dev] = direction;

	regName = m_generic_command_names[(address & 0xFF00) >> 8];
	if (m_specific_command_tables[dev] != nullptr)
	{
		int index = (address & 0xFF00) >> 8;
		if (m_specific_command_tables[dev][index] != nullptr)
		{
			regName = m_specific_command_tables[dev][index];
		}
	}


	u32 stacktrace[4];
	u32 basepointer[2];
	u32 reg_a6 = ptr_m68000->state_int(M68K_A6);

	for (int i = 0; i<4; i++)
		stacktrace[i] = 0;
	for (int i = 0; i<2; i++)
		basepointer[i] = 0;

	stacktrace[0] = m_maincpu->pc();
	if (reg_a6 + 4 < 0xFFFFFF) stacktrace[1] = m_maincpu->space(AS_PROGRAM).read_dword(reg_a6 + 4);
	if (reg_a6 < 0xFFFFFF && reg_a6 != 0) basepointer[0] = m_maincpu->space(AS_PROGRAM).read_dword(reg_a6);
	if (basepointer[0] + 4 < 0xFFFFFF && basepointer[0] != 0) stacktrace[2] = m_maincpu->space(AS_PROGRAM).read_dword(basepointer[0] + 4);
	if (basepointer[0] < 0xFFFFFF && basepointer[0] != 0) basepointer[1] = m_maincpu->space(AS_PROGRAM).read_dword(basepointer[0]);
	if (basepointer[1] + 4 < 0xFFFFFF && basepointer[1] != 0) stacktrace[3] = m_maincpu->space(AS_PROGRAM).read_dword(basepointer[1] + 4);

	logerror("%s Device 0x%x (%s) Register [%08X] (%s) %s %08X (%08X, %08X, %08X, %08X)\n", machine().time().as_string(), dev, devName, address, regName, direction, data, stacktrace[0], stacktrace[1], stacktrace[2], stacktrace[3]);

}

#else
void r9751_state::device_trace_init() {}
void r9751_state::device_trace_teardown() {}
void r9751_state::trace_device(int address, int data, const char* direction) {}
#endif

void r9751_state::device_trace_enable_all()
{
	for (int i = 0; i < sizeof(m_device_trace_enable) / sizeof(m_device_trace_enable[0]); i++)
	{
		m_device_trace_enable[i] = 0xFFFFFFFF;
	}
}
void r9751_state::device_trace_disable(int device)
{
	device = device & 0x3F;
	m_device_trace_enable[device >> 5] &= ~(1 << (device & 0x1F));
}


// System register tracing
struct SystemRegisterInfo {
	int VerboseLevel;
	u32 RegisterAddress;
	const char * RegisterName;
	const char * Description;
};

static const SystemRegisterInfo SystemRegisters[] = {
	{ 5, 0xff010000, "DB00" },{ 5, 0xff010004, "DB01" },{ 5, 0xff010008, "DB02" },{ 5, 0xff01000c, "DB03" },
	{ 5, 0xff010010, "DB04" },{ 5, 0xff010014, "DB05" },{ 5, 0xff010018, "DB06" },{ 5, 0xff01001c, "DB07" },
	{ 5, 0xff010020, "DB08" },{ 5, 0xff010024, "DB09" },{ 5, 0xff010028, "DB10" },{ 5, 0xff01002c, "DB11" },
	{ 5, 0xff010040, "DBVR" },{ 5, 0xff010044, "DTAR" },{ 5, 0xff010048, "DESR" },{ 5, 0xff01004c, "DVR" },
	{ 5, 0xff010050, "DRR" },{ 5, 0xff010054, "DDR" },{ 5, 0xff010060, "DMR" },{ 5, 0xff010064, "RCR" },
	{ 5, 0xff010068, "RESR" },{ 5, 0xff01006c, "RBSR" },{ 5, 0xff010070, "RBDR" },{ 5, 0xff010074, "RBAR" },
	{ 5, 0xff010078, "RIOB" },{ 5, 0xff01007c, "RPSR" },{ 5, 0xff010080, "PDR" },

	{ 5, 0xff020004, "ARSP" },{ 5, 0xff020008, "MEXC" },{ 5, 0xff02000c, "MADD" },{ 5, 0xff020010, "MDAT" },
	{ 5, 0xff020014, "ADIA" },{ 5, 0xff020018, "ECCD" },{ 5, 0xff02001c, "VPDS" },{ 5, 0xff020020, "HBRK" },
	{ 5, 0xff020024, "LBRK" },{ 5, 0xff020028, "REFS" },{ 5, 0xff02002c, "RCNT" },{ 5, 0xff020030, "RTMR" },

	{ 5, 0xff050004, "ROSR" },{ 5, 0xff050008, "BIVR" },{ 5, 0xff05000c, "RDDR", "LED - RAS Digit Display Register" },
	{ 5, 0xff050104, "AMR" },
	{ 5, 0xff050108, "MIVR" },{ 5, 0xff050200, "APCR" },{ 5, 0xff050204, "PESR" },{ 5, 0xff050208, "PEAR" },
	{ 5, 0xff05020c, "PEDR" },{ 5, 0xff050210, "PEST" },{ 5, 0xff050300, "RCID" },
	{ 2, 0xff050310, "APLT" }, // APLT is accessed heavily in background/timing processes.
	{ 2, 0xff050320, "WDT" }, // WDT use is particularly noisy
	{ 5, 0xff050400, "APT" },{ 5, 0xff050404, "AIVR" },{ 5, 0xff050408, "ASSR" },
	{ 5, 0xff050410, "STRU" },{ 5, 0xff050414, "STRL" },
	{ 5, 0xff050500, "EV00" },{ 5, 0xff050504, "EV01" },{ 5, 0xff050508, "EV02" },{ 5, 0xff05050c, "EV03" },
	{ 5, 0xff050510, "EV04" },{ 5, 0xff050514, "EV05" },{ 5, 0xff050518, "EV06" },{ 5, 0xff05051c, "EV07" },
	{ 5, 0xff050520, "EV08" },{ 5, 0xff050524, "EV09" },{ 5, 0xff050528, "EV10" },{ 5, 0xff05052c, "EV11" },
	{ 5, 0xff050530, "EV12" },{ 5, 0xff050534, "EV13" },{ 5, 0xff050538, "EV14" },{ 5, 0xff05053c, "EV15" },
	{ 5, 0xff050540, "EV16" },{ 5, 0xff050544, "EV17" },{ 5, 0xff050548, "EV18" },{ 5, 0xff05054c, "EV19" },
	{ 5, 0xff050550, "EV20" },{ 5, 0xff050554, "EV21" },{ 5, 0xff050558, "EV22" },{ 5, 0xff05055c, "EV23" },
	{ 5, 0xff050560, "EV24" },{ 5, 0xff050564, "EV25" },
	{ 5, 0xff050580, "PISR" },
	{ 2, 0xff050584, "PIMR" }, // PIMR use in the bios is noisy
	{ 5, 0xff050600, "CDMR" },{ 5, 0xff050604, "CDER" },
	{ 5, 0xff050610, "CIV0" },{ 5, 0xff050614, "CIV1" },{ 5, 0xff050618, "CIV2" },{ 5, 0xff05061c, "CIV3" },
	{ 5, 0xff050620, "CIV4" },{ 5, 0xff050624, "CIV5" },{ 5, 0xff050628, "CIV6" },{ 5, 0xff05062c, "CIV7" },
	{ 5, 0xff050630, "CCV0" },{ 5, 0xff050634, "CCV1" },{ 5, 0xff050638, "CCV2" },{ 5, 0xff05063c, "CCV3" },
	{ 5, 0xff050640, "CCV4" },{ 5, 0xff050644, "CCV5" },{ 5, 0xff050648, "CCV6" },{ 5, 0xff05064c, "CCV7" },

	{ 5, 0xff060014, "SCAS" },{ 5, 0xff060018, "SCRG" },{ 5, 0xff060020, "SDER" },{ 5, 0xff060024, "SDCR" },
	{ 5, 0xff060028, "SESR" },{ 5, 0xff06002c, "SBSR" },{ 5, 0xff060030, "SBDR" },{ 5, 0xff060034, "SBAR" },
	{ 5, 0xff060038, "SDAR" },{ 5, 0xff06003c, "SINT" },{ 5, 0xff060040, "SDXC" },{ 5, 0xff060044, "SOSR" },
	{ 5, 0xff060048, "SISR" },{ 5, 0xff06004c, "SIDR" },{ 5, 0xff060050, "SDSD" },

};


static constexpr int MaxSystemTables = 256; // FF00xxxx through FFFFxxxx
static constexpr int MaxSystemRegisters = 0x400; // FF0x0000 through FF0x0FFC
void r9751_state::system_trace_init()
{
	const SystemRegisterInfo*** trace_context;

	// Allocate tables for FF00 through FFFF
	trace_context = new const SystemRegisterInfo**[MaxSystemTables];
	memset(trace_context, 0, sizeof(void*)*MaxSystemTables);

	// Iterate over the system register info
	for (int i = 0; i < (sizeof(SystemRegisters) / sizeof(*SystemRegisters)); i++)
	{
		const SystemRegisterInfo* current = SystemRegisters + i;

		int table = (current->RegisterAddress & 0x00FF0000) >> 16;
		int regindex = (current->RegisterAddress & 0xFFFC) >> 2;

		if (table >= MaxSystemTables || regindex >= MaxSystemRegisters)
		{
			logerror("system_trace_init: Register out of range and cannot be traced: 0x%x %s\n", current->RegisterAddress, current->RegisterName);
			continue;
		}

		// Lookup the first tier table and create it if it doesn't exist
		const SystemRegisterInfo** firstTier = trace_context[table];
		if (firstTier == nullptr)
		{
			firstTier = new const SystemRegisterInfo*[MaxSystemRegisters];
			memset(firstTier, 0, sizeof(void*) * MaxSystemRegisters);
			trace_context[table] = firstTier;
		}

		// Set the content in the table
		firstTier[regindex] = current;

	}

	system_trace_context = trace_context;
}

void r9751_state::system_trace_teardown()
{
	for (int i = 0; i < MaxSystemTables; i++)
	{
		delete[] ((const SystemRegisterInfo***)system_trace_context)[i];
	}
	delete[] (const SystemRegisterInfo***)system_trace_context;
}

#if ENABLE_TRACE_SYSTEM
void r9751_state::trace_system(int table, int offset, int data, const char* direction)
{
	const SystemRegisterInfo*** trace_context = (const SystemRegisterInfo***)system_trace_context;

	const char* regName = "?";
	const char* regDescription = "?";
	int level = 10; // Undefined registers are level 10

	if (table < MaxSystemTables && offset < MaxSystemRegisters)
	{
		const SystemRegisterInfo** firstTier = trace_context[table];
		if (firstTier != nullptr)
		{
			const SystemRegisterInfo* current = firstTier[offset];
			if (current != nullptr)
			{
				regName = current->RegisterName;
				if (current->Description != nullptr) regDescription = current->Description;
				level = current->VerboseLevel;
			}
		}
	}

	if (level >= SYSTEM_TRACE_LEVEL)
	{
		u32 address = 0xFF000000 | (table << 16) | (offset << 2);
		logerror("%s System Register [%08X] (%s %s) %s %08X (PC=%08X)\n", machine().time().as_string(), address, regName, regDescription, direction, data, m_maincpu->pc());
	}

}
#else
void r9751_state::trace_system(int table, int offset, int data, const char* direction)
{
}
#endif


uint32_t r9751_state::swap_uint32( uint32_t val )
{
	val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF );
	return (val << 16) | (val >> 16);
}

uint32_t r9751_state::debug_a6()
{
	return m_maincpu->space(AS_PROGRAM).read_dword(ptr_m68000->state_int(M68K_A6) + 4);
}

uint32_t r9751_state::debug_a5()
{
	return m_maincpu->space(AS_PROGRAM).read_dword(ptr_m68000->state_int(M68K_A5));
}

uint32_t r9751_state::debug_a5_20()
{
	return m_maincpu->space(AS_PROGRAM).read_dword(ptr_m68000->state_int(M68K_A5) + 0x20);
}

void r9751_state::UnifiedTrace(u32 address, u32 data, const char* operation, const char* Device, const char* RegisterName, const char* extraText)
{
	u32 stacktrace[4];
	u32 basepointer[2];
	u32 reg_a6 = ptr_m68000->state_int(M68K_A6);

	for(int i=0; i<4; i++)
		stacktrace[i] = 0;
	for(int i=0; i<2; i++)
		basepointer[i] = 0;

	stacktrace[0] = m_maincpu->pc();
	if(reg_a6 + 4 < 0xFFFFFF) stacktrace[1] = m_maincpu->space(AS_PROGRAM).read_dword(reg_a6 + 4);
	if(reg_a6 < 0xFFFFFF && reg_a6 != 0) basepointer[0] = m_maincpu->space(AS_PROGRAM).read_dword(reg_a6);
	if(basepointer[0] + 4 < 0xFFFFFF && basepointer[0] != 0) stacktrace[2] = m_maincpu->space(AS_PROGRAM).read_dword(basepointer[0] + 4);
	if(basepointer[0] < 0xFFFFFF && basepointer[0] != 0) basepointer[1] = m_maincpu->space(AS_PROGRAM).read_dword(basepointer[0]);
	if(basepointer[1] + 4 < 0xFFFFFF && basepointer[1] != 0) stacktrace[3] = m_maincpu->space(AS_PROGRAM).read_dword(basepointer[1] + 4);

	logerror("%s[%08X] => %08X (%s:%s) %s (%08X, %08X, %08X, %08X)\n", operation, address, data, Device, RegisterName==nullptr?"":RegisterName, extraText==nullptr?"":extraText, stacktrace[0], stacktrace[1], stacktrace[2], stacktrace[3]);
}

uint8_t r9751_state::pdc_dma_r(offs_t offset)
{
	/* This callback function takes the value written to 0xFF01000C as the bank offset */
	uint32_t address = (fdd_dma_bank & 0x7FFFF800) + (offset & 0x3FFFF);
	if (TRACE_DMA) logerror("DMA READ: %08X DATA: %08X\n", address, m_maincpu->space(AS_PROGRAM).read_byte(address));
	return m_maincpu->space(AS_PROGRAM).read_byte(address);
}

void r9751_state::pdc_dma_w(uint8_t data)
{
	/* This callback function takes the value written to 0xFF01000C as the bank offset */
	uint32_t address = (fdd_dma_bank & 0x7FFFF800) + (m_pdc->fdd_68k_dma_address & 0x3FFFF);
	m_maincpu->space(AS_PROGRAM).write_byte(address, data);
	if (TRACE_DMA) logerror("DMA WRITE: %08X DATA: %08X\n", address, data);
}

uint8_t r9751_state::smioc_dma_r(offs_t offset)
{
	/* This callback function takes the value written to 0xFF01000C as the bank offset */
	uint32_t address = (smioc_dma_bank & 0x7FFFF800) + (offset*2 & 0x3FFFF);
	u16 word = m_maincpu->space(AS_PROGRAM).read_word(address);
	char c = word & 0xFF;
	if (c < 32 || c > 127) c = ' ';
	if (TRACE_DMA) logerror("%s SMIOC DMA READ: %08X DATA: %04X (%c)\n", machine().time().as_string(), address, word, c);
	return m_maincpu->space(AS_PROGRAM).read_word(address) & 0x00FF;
}

void r9751_state::smioc_dma_w(offs_t offset, uint8_t data)
{
	/* This callback function takes the value written to 0xFF01000C as the bank offset */
	uint32_t address = (smioc_dma_bank & 0x7FFFF800) + (offset*2 & 0x3FFFF);
	m_maincpu->space(AS_PROGRAM).write_word(address, data);
	char c = data & 0xFF;
	if (c < 32 || c > 127) c = ' ';
	if (TRACE_DMA) logerror("%s SMIOC DMA WRITE: %08X DATA: %08X (%c)\n", machine().time().as_string(), address, data, c);
}

void r9751_state::init_r9751()
{
	reg_ff050004 = 0;
	reg_fff80040 = 0;
	fdd_dest_address = 0;
	fdd_cmd_complete = 0;
	fdd_dma_bank = 0;
	smioc_dma_bank = 0;

	m_mem = &m_maincpu->space(AS_PROGRAM);

	m_maincpu->interface<m68000_base_device>(ptr_m68000);

	device_trace_enable_all();
	device_trace_disable(0x07);
	device_trace_disable(0x09);

	/* Save states */
	save_item(NAME(reg_ff050004));
	save_item(NAME(reg_fff80040));
	save_item(NAME(fdd_dest_address));
	save_item(NAME(fdd_cmd_complete));
	save_item(NAME(smioc_dma_bank));
	save_item(NAME(fdd_dma_bank));
	save_item(NAME(timer_32khz_last));

}

void r9751_state::machine_reset()
{
	uint8_t *rom = memregion("prom")->base();
	uint32_t *ram = m_main_ram;

	memcpy(ram, rom, 8);
}

/******************************************************************************
 External board communication registers [0x5FF00000 - 0x5FFFFFFF]
******************************************************************************/
uint32_t r9751_state::r9751_mmio_5ff_r(offs_t offset)
{
	uint32_t data;
	switch(offset << 2)
	{
		/* PDC HDD region (0x24, device 9) */
		case 0x0824: /* HDD Command result code */
			data = 0x10;
			break;

		case 0x3024: /* HDD SCSI command completed successfully */
			data = 0x1;
			if(TRACE_HDC) logerror("SCSI HDD command completion status - Read: %08X, From: %08X, Register: %08X\n", data, m_maincpu->pc(), offset << 2 | 0x5FF00000);
			break;

		/* SMIOC region (0x98, device 0x26) */
		case 0x0898: /* Serial status or DMA status */
			data = m_smioc->GetStatus();
			break;

		case 0x0870: /* Serial status or DMA status 2 (0x70, device 0x24) */
			data = m_smioc->GetStatus2();
			break;
		/* SMIOC region (0x9C, device 0x27) */

		case 0x1098: /* Serial word count register */
			data = m_smioc->m_wordcount;
			m_smioc->ClearParameter();
			break;

		case 0x1070: /* Serial word count register (alternate) */
			data = m_smioc->m_wordcount2;
			m_smioc->ClearParameter2();
			break;

		case 0x2898: /* SMIOC DMA Busy register - nonzero = busy */
			data = m_smioc->m_deviceBusy;
			break;

		/* PDC FDD region (0xB0, device 44 */
		case 0x08B0: /* FDD Command result code */
			data = 0x10;
			break;

		case 0x10B0: /* Clear 5FF030B0 ?? */
			if(TRACE_FDC) logerror("--- FDD 0x5FF010B0 READ (0)\n");
			data = 0;
			break;

		case 0x1890: /* Device offset 0x90 (Bit 7 needs to be set) */
			data = 0x80;
			break;

		case 0x30B0: /* FDD command completion status */
			data = (m_pdc->reg_p5 << 8) + m_pdc->reg_p4;
			if(TRACE_FDC && data != 0) logerror("--- SCSI FDD command completion status - Read: %08X, From: %08X, Register: %08X\n", data, m_maincpu->pc(), offset << 2 | 0x5FF00000);
			break;

		default:
			data = 0;
			break;
	}
	trace_device(offset << 2 | 0x5FF00000, data, ">>");
	return data;
}

void r9751_state::r9751_mmio_5ff_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	uint8_t data_b0, data_b1;

	trace_device(offset << 2 | 0x5FF00000, data, "<<");

	/* Unknown mask */
	if (mem_mask != 0xFFFFFFFF)
		logerror("Mask found: %08X Register: %08X PC: %08X\n", mem_mask, offset << 2 | 0x5FF00000, m_maincpu->pc());

	switch(offset << 2)
	{
		/* PDC HDD region (0x24, device 9) */

		/* SMIOC region (0x98, device 26) - Output */
		case 0x0298:
			m_smioc->ClearStatus();
			break;
		case 0x0270:
			m_smioc->ClearStatus2();
			break;
		case 0x4070: // SMIOC upper port command (ports 4-7)
			m_smioc->SendCommand2(data);
			break;
		case 0x8070: // SMIOC upper port parameter (ports 4-7)
			m_smioc->SetCommandParameter2(data);
			break;

		case 0x0198: // SMIOC soft reset?
			// It's not clear what exactly this register write does.
			//   It isn't a soft reset of the SMIOC because the 68k does not wait long enough for the SMIOC to finish rebooting.
			//   Also the 68k does this twice in succession, with a status clear in between.
			// The theory now is that a write to this register causes the status registers on the SMIOC to be
			//   set to the magic value 0x0100 (which is set after initialization is complete) - which serves as a trigger
			//   for the disktool software to reinitialize the SMIOC and proceed into a working state.
			// This probably isn't correct, but hopefully we will determine the correct approach in the future.

			m_smioc->m_status = 0x0140;
			m_smioc->m_status2 = 0x0140;
			break;

		case 0x4098: /* Serial DMA Command */
			m_smioc->SendCommand(data);
			break;
		case 0x8098: /* Command Parameter */
			m_smioc->SetCommandParameter(data);
			break;
		case 0xC098: /* Serial DMA Transmit data address */
			m_smioc->SetDmaParameter(smiocdma_sendaddress, data);
			break;

		/* SMIOC region (0x9C, device 27) - Input */
		case 0x409C: /* Serial DMA write length */
			m_smioc->SetDmaParameter(smiocdma_sendlength, data);
			break;
		case 0x809C: /* Serial DMA Receive data address */
			m_smioc->SetDmaParameter(smiocdma_recvaddress, data);
			break;
		case 0xC09C: /* Serial DMA read length */
			m_smioc->SetDmaParameter(smiocdma_recvlength, data);
			break;

		/* PDC FDD region (0xB0, device 44) */
		case 0x04B0: /* FDD RESET PDC */
			if(TRACE_FDC) logerror("PDC RESET, PC: %08X DATA: %08X\n", m_maincpu->pc(), data);
			m_pdc->reset();
			break;
		case 0x41B0: /* Unknown - Probably old style commands */
			if(TRACE_FDC) logerror("--- FDD Command: %08X, From: %08X, Register: %08X\n", data, m_maincpu->pc(), offset << 2 | 0x5FF00000);

			/* Clear FDD Command completion status 0x5FF030B0 (PDC 0x4, 0x5) */
			m_pdc->reg_p4 = 0;
			m_pdc->reg_p5 = 0;

			data_b0 = data & 0xFF;
			data_b1 = (data & 0xFF00) >> 8;
			m_pdc->reg_p0 = data_b0;
			m_pdc->reg_p1 = data_b1;
			m_pdc->reg_p38 |= 0x2; /* Set bit 1 on port 38 register, PDC polls this port looking for a command */
			if(TRACE_FDC) logerror("--- FDD Old Command: %02X and %02X\n", data_b0, data_b1);
			break;
		case 0xC0B0:
		case 0xC1B0: /* fdd_dest_address register */
			fdd_dest_address = data << 1;
			if(TRACE_FDC) logerror("--- FDD destination address: %08X PC: %08X Register: %08X (A6+4): %08X\n", (fdd_dma_bank & 0x7FFFF800) + (fdd_dest_address&0x3FFFF), m_maincpu->pc(), offset << 2 | 0x5FF00000, debug_a6());
			data_b0 = data & 0xFF;
			data_b1 = (data & 0xFF00) >> 8;
			m_pdc->reg_p6 = data_b0;
			m_pdc->reg_p7 = data_b1;
			m_pdc->reg_p38 |= 0x2; // Set bit 1 on port 38 register, PDC polls this port looking for a command
			if(TRACE_FDC)logerror("--- FDD SET PDC Port 38: %X\n",m_pdc->reg_p38);
			break;
		case 0x80B0: /* FDD command address register */
			uint32_t fdd_scsi_command;
			uint32_t fdd_scsi_command2;
			unsigned char c_fdd_scsi_command[8]; // Array for SCSI command
			int scsi_lba; // FDD LBA location here, extracted from command

			/* Clear FDD Command completion status 0x5FF030B0 (PDC 0x4, 0x5) */
			m_pdc->reg_p4 = 0;
			m_pdc->reg_p5 = 0;

			/* Send FDD SCSI command location address to PDC 0x2, 0x3 */
			if(TRACE_FDC) logerror("--- FDD command address: %08X PC: %08X Register: %08X (A6+4): %08X A4: %08X (A5): %08X (A5+20): %08X\n", (fdd_dma_bank & 0x7FFFF800) + ((data << 1)&0x3FFFF), m_maincpu->pc(), offset << 2 | 0x5FF00000, debug_a6(), ptr_m68000->state_int(M68K_A4), debug_a5(), debug_a5_20());
			data_b0 = data & 0xFF;
			data_b1 = (data & 0xFF00) >> 8;
			m_pdc->reg_p2 = data_b0;
			m_pdc->reg_p3 = data_b1;

			fdd_scsi_command = swap_uint32(m_mem->read_dword((fdd_dma_bank & 0x7FFFF800) + ((data << 1)&0x3FFFF)));
			fdd_scsi_command2 = swap_uint32(m_mem->read_dword(((fdd_dma_bank & 0x7FFFF800) + ((data << 1)&0x3FFFF))+4));

			memcpy(c_fdd_scsi_command,&fdd_scsi_command,4);
			memcpy(c_fdd_scsi_command+4,&fdd_scsi_command2,4);

			if(TRACE_FDC)
			{
				logerror("--- FDD SCSI Command: ");
				for(int i = 0; i < 8; i++)
					logerror("%02X ", c_fdd_scsi_command[i]);
				logerror("\n");
			}

			scsi_lba = c_fdd_scsi_command[3] | (c_fdd_scsi_command[2]<<8) | ((c_fdd_scsi_command[1]&0x1F)<<16);
			if(TRACE_FDC) logerror("--- FDD SCSI LBA: %i\n", scsi_lba);

			break;

		default:
			break;
	}
}

/******************************************************************************
 CPU board registers [0xFF010000 - 0xFF06FFFF]
******************************************************************************/
uint32_t r9751_state::r9751_mmio_ff01_r(offs_t offset)
{
	u32 data;
	switch(offset << 2)
	{
		default:
			data = 0;
	}
	trace_system(1, offset, data, ">>");
	return data;
}

void  r9751_state::r9751_mmio_ff01_w (offs_t offset, uint32_t data, uint32_t mem_mask)
{
	/* Unknown mask */
	if (mem_mask != 0xFFFFFFFF)
		logerror("Mask found: %08X Register: %08X PC: %08X\n", mem_mask, offset << 2 | 0xFF010000, m_maincpu->pc());

	trace_system(1, offset, data, "<<");

	switch(offset << 2)
	{
		case 0x000C: /* FDD DMA Offset */
			fdd_dma_bank = data;
			if(TRACE_DMA) logerror("Banking register(FDD): %08X PC: %08X Data: %08X\n", offset << 2 | 0xFF010000, m_maincpu->pc(), data);
			return;
		case 0x0010: /* SMIOC DMA Offset */
			smioc_dma_bank = data;
			if(TRACE_DMA) logerror("Banking register(SMIOC): %08X PC: %08X Data: %08X\n", offset << 2 | 0xFF010000, m_maincpu->pc(), data);
			return;
		default:
			if(TRACE_DMA) logerror("Banking register(Unknown): %08X PC: %08X Data: %08X\n", offset << 2 | 0xFF010000, m_maincpu->pc(), data);
			return;
	}
}

uint32_t r9751_state::r9751_mmio_ff05_r(offs_t offset)
{
	uint32_t data;

	switch(offset << 2)
	{
		case 0x0004:
			data = reg_ff050004;
			break;
		case 0x0300:
			data = 0x1B | (1<<0x14);
			break;
		case 0x0320: /* Some type of counter */
			data = (machine().time() - timer_32khz_last).as_ticks(32768) & 0xFFFF;
			break;
		case 0x0584:
			data = 0;
			break;
		case 0x0610:
			data = 0xabacabac;
			break;
		case 0x0014:
			data = 0x80;
			break;
		default:
			data = 0;
			break;
	}
	trace_system(5, offset, data, "<<");
	return data;
}

void r9751_state::r9751_mmio_ff05_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	/* Unknown mask */
	if (mem_mask != 0xFFFFFFFF)
		logerror("Mask found: %08X Register: %08X PC: %08X\n", mem_mask, offset << 2 | 0xFF050000, m_maincpu->pc());

	trace_system(5, offset, data, ">>");

	switch(offset << 2)
	{
		case 0x0004:
			reg_ff050004 = data;
			return;
		case 0x000C: /* CPU LED hex display indicator */
			if(TRACE_LED) logerror("\n*** LED: %02x, Instruction: %08x ***\n\n", data, m_maincpu->pc());
			return;
		case 0x0320:
			timer_32khz_last = machine().time();
			return;
		default:
			return;
	}
}

uint32_t r9751_state::r9751_mmio_fff8_r(offs_t offset)
{
	uint32_t data;

	switch(offset << 2)
	{
		case 0x0040:
			data = reg_fff80040;
			break;
		default:
			data = 0;
			break;
	}
	trace_system(5, offset, data, "<<");
	return data;
}

void r9751_state::r9751_mmio_fff8_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	/* Unknown mask */
	if (mem_mask != 0xFFFFFFFF)
		logerror("Mask found: %08X Register: %08X PC: %08X\n", mem_mask, offset << 2 | 0xFFF80000, m_maincpu->pc());

	trace_system(5, offset, data, ">>");

	switch(offset << 2)
	{
		case 0x0040:
			reg_fff80040 = data;
			return;
		default:
			return;
	}
}

/******************************************************************************
 Address Maps
******************************************************************************/

void r9751_state::r9751_mem(address_map &map)
{
	//map.unmap_value_high();
	map(0x00000000, 0x00ffffff).ram().share("main_ram"); // 16MB
	map(0x08000000, 0x0800ffff).rom().region("prom", 0);
	map(0x5FF00000, 0x5FFFFFFF).rw(FUNC(r9751_state::r9751_mmio_5ff_r), FUNC(r9751_state::r9751_mmio_5ff_w));
	map(0xFF010000, 0xFF01FFFF).rw(FUNC(r9751_state::r9751_mmio_ff01_r), FUNC(r9751_state::r9751_mmio_ff01_w));
	map(0xFF050000, 0xFF06FFFF).rw(FUNC(r9751_state::r9751_mmio_ff05_r), FUNC(r9751_state::r9751_mmio_ff05_w));
	map(0xFFF80000, 0xFFF8FFFF).rw(FUNC(r9751_state::r9751_mmio_fff8_r), FUNC(r9751_state::r9751_mmio_fff8_w));
	//map(0xffffff00,0xffffffff).ram(); // Unknown area
}

/******************************************************************************
 Input Ports
******************************************************************************/
static INPUT_PORTS_START( r9751 )
INPUT_PORTS_END

/******************************************************************************
 Machine Drivers
******************************************************************************/

void r9751_state::scsi_devices(device_slot_interface &device)
{
	device.option_add("cdrom", NSCSI_CDROM);
	device.option_add("harddisk", NSCSI_HARDDISK);
}

void r9751_state::wd33c93(device_t *device)
{
	device->set_clock(10000000);
	//  downcast<wd33c93a_device *>(device)->irq_cb().set(*this, FUNC(r9751_state::scsi_irq_w));
	//  downcast<wd33c93a_device *>(device)->drq_cb().set(*this, FUNC(r9751_state::scsi_drq_w));
}

void r9751_state::r9751(machine_config &config)
{
	/* basic machine hardware */
	M68030(config, m_maincpu, 20000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &r9751_state::r9751_mem);
	config.set_maximum_quantum(attotime::from_hz(1000));

	/* i/o hardware */
	SMIOC(config, m_smioc, 0);
	m_smioc->m68k_r_callback().set(FUNC(r9751_state::smioc_dma_r));
	m_smioc->m68k_w_callback().set(FUNC(r9751_state::smioc_dma_w));

	/* disk hardware */
	PDC(config, m_pdc, 0);
	m_pdc->m68k_r_callback().set(FUNC(r9751_state::pdc_dma_r));
	m_pdc->m68k_w_callback().set(FUNC(r9751_state::pdc_dma_w));

	NSCSI_BUS(config, "scsi", 0);
	NSCSI_CONNECTOR(config, "scsi:0", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:1", scsi_devices, "harddisk", false);
	NSCSI_CONNECTOR(config, "scsi:3", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:4", scsi_devices, "cdrom", false);
	NSCSI_CONNECTOR(config, "scsi:5", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:6", scsi_devices, nullptr, false);
	NSCSI_CONNECTOR(config, "scsi:7").option_set("wd33c93", WD33C93)
		.machine_config([this](device_t *device) { wd33c93(device); });

	/* software list */
	SOFTWARE_LIST(config, "flop_list").set_original("r9751");
}



/******************************************************************************
 ROM Definitions
******************************************************************************/

ROM_START(r9751)
	ROM_REGION32_BE(0x00010000, "prom", 0)
	ROM_SYSTEM_BIOS(0, "prom34",  "PROM Version 3.4")
	ROMX_LOAD( "p-n_98d4643__abaco_v3.4__=49fe7a=__j221.27512.bin", 0x0000, 0x10000, CRC(9fb19a85) SHA1(c861e15a2fc9a4ef689c2034c53fbb36f17f7da6), ROM_GROUPWORD | ROM_BIOS(0) ) // Label: "P/N 98D4643 // ABACO V3.4 // (49FE7A) // J221" 27512 @Unknown

	ROM_SYSTEM_BIOS(1, "prom42", "PROM Version 4.2")
	ROMX_LOAD( "98d5731__zebra_v4.2__4cd79d.u5", 0x0000, 0x10000, CRC(e640f8df) SHA1(a9e4fa271d7f2f3a134e2120932ec088d5b8b007), ROM_GROUPWORD | ROM_BIOS(1) ) // Label: 98D5731 // ZEBRA V4.2 // 4CD79D 27512 @Unknown
ROM_END

} // anonymous namespace


/******************************************************************************
 Drivers
******************************************************************************/

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY               FULLNAME              FLAGS
COMP( 1988, r9751, 0,      0,      r9751,   r9751, r9751_state, init_r9751, "ROLM Systems, Inc.", "ROLM 9751 Model 10", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



radio86.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

        Radio-86RK driver by Miodrag Milanovic

        15/03/2008 Preliminary driver.

****************************************************************************/


#include "emu.h"
#include "radio86.h"

#include "cpu/i8085/i8085.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/rk_cas.h"


/* Address maps */
void radio86_state::radio86_mem(address_map &map)
{
	map(0x0000, 0x7fff).ram().share("mainram");
	map(0x8000, 0x8003).rw(m_ppi1, FUNC(i8255_device::read), FUNC(i8255_device::write)).mirror(0x1ffc);
	//map(0xa000, 0xa003).rw(m_ppi2, FUNC(i8255_device::read), FUNC(i8255_device::write)).mirror(0x1ffc);
	map(0xc000, 0xc001).rw("crtc", FUNC(i8275_device::read), FUNC(i8275_device::write)).mirror(0x1ffe); // video
	map(0xe000, 0xffff).w(m_dma, FUNC(i8257_device::write));    // DMA
	map(0xf000, 0xffff).rom().region("maincpu",0);  // System ROM
}

void radio86_state::radio86_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0xff).rw(FUNC(radio86_state::radio_io_r), FUNC(radio86_state::radio_io_w));
}

void radio86_state::rk7007_io(address_map &map)
{
	map.unmap_value_high();
	map(0x80, 0x83).rw("ms7007", FUNC(i8255_device::read), FUNC(i8255_device::write));
}

void radio86_state::radio86rom_mem(address_map &map)
{
	map(0x0000, 0x7fff).ram().share("mainram");
	map(0x8000, 0x8003).rw(m_ppi1, FUNC(i8255_device::read), FUNC(i8255_device::write)).mirror(0x1ffc);
	map(0xa000, 0xa003).rw(m_ppi2, FUNC(i8255_device::read), FUNC(i8255_device::write)).mirror(0x1ffc);
	map(0xc000, 0xc001).rw("crtc", FUNC(i8275_device::read), FUNC(i8275_device::write)).mirror(0x1ffe); // video
	map(0xe000, 0xffff).w(m_dma, FUNC(i8257_device::write));    // DMA
	map(0xf000, 0xffff).rom().region("maincpu",0);  // System ROM
}

void radio86_state::radio86ram_mem(address_map &map)
{
	map(0x0000, 0xdfff).ram().share("mainram");
	map(0xe000, 0xe7ff).rom();  // System ROM page 2
	map(0xe800, 0xf5ff).ram();  // RAM
	map(0xf700, 0xf703).rw(m_ppi1, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xf780, 0xf7bf).rw("crtc", FUNC(i8275_device::read), FUNC(i8275_device::write)); // video
	map(0xf684, 0xf687).rw(m_ppi2, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xf688, 0xf688).w(FUNC(radio86_state::radio86_pagesel));
	map(0xf800, 0xffff).w(m_dma, FUNC(i8257_device::write));    // DMA
	map(0xf800, 0xffff).rom().region("maincpu",0);  // System ROM page 1
}

void radio86_state::radio86_16_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).ram().share("mainram");
	map(0x4000, 0x7fff).r(FUNC(radio86_state::radio_cpu_state_r));
	map(0x8000, 0x8003).rw(m_ppi1, FUNC(i8255_device::read), FUNC(i8255_device::write)).mirror(0x1ffc);
	//map(0xa000, 0xa003).rw(m_ppi2, FUNC(i8255_device::read), FUNC(i8255_device::write)).mirror(0x1ffc);
	map(0xc000, 0xc001).rw("crtc", FUNC(i8275_device::read), FUNC(i8275_device::write)).mirror(0x1ffe); // video
	map(0xe000, 0xffff).w(m_dma, FUNC(i8257_device::write));    // DMA
	map(0xf000, 0xffff).rom().region("maincpu",0);  // System ROM
}


void radio86_state::mikron2_mem(address_map &map)
{
	map(0x0000, 0x7fff).ram().share("mainram");
	map(0xc000, 0xc003).rw(m_ppi1, FUNC(i8255_device::read), FUNC(i8255_device::write)).mirror(0x00fc);
	//map(0xc100, 0xc103).rw(m_ppi2, FUNC(i8255_device::read), FUNC(i8255_device::write)).mirror(0x00fc);
	map(0xc200, 0xc201).rw("crtc", FUNC(i8275_device::read), FUNC(i8275_device::write)).mirror(0x00fe); // video
	map(0xc300, 0xc3ff).w(m_dma, FUNC(i8257_device::write));    // DMA
	map(0xf000, 0xffff).rom().region("maincpu",0);  // System ROM
}

void radio86_state::impuls03_mem(address_map &map)
{
	map(0x0000, 0x7fff).ram().share("mainram");
	map(0x8000, 0x8003).rw(m_ppi1, FUNC(i8255_device::read), FUNC(i8255_device::write)).mirror(0x1ffc);
	map(0xa000, 0xbfff).rom().region("maincpu",0x1000);  // Basic ROM
	map(0xc000, 0xc001).rw("crtc", FUNC(i8275_device::read), FUNC(i8275_device::write)).mirror(0x1ffe); // video
	map(0xe000, 0xffff).w(m_dma, FUNC(i8257_device::write));    // DMA
	map(0xf000, 0xffff).rom().region("maincpu",0);  // System ROM
}

/* Input ports */
INPUT_PORTS_START( radio86 )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ins") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Back") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR(0xA4)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')

	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')

	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')

	PORT_START("LINE7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('^')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("LINE8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Rus/Lat") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
INPUT_PORTS_END

// layout from http://retropc.org/Elektronika_KR-02_s_67.html

INPUT_PORTS_START( kr03 )
	PORT_START("LINE0")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC),27)
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR(0xA4)
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')

	PORT_START("LINE1")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) // xxx
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')

	PORT_START("LINE2")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')

	PORT_START("LINE3")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Rus") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@')

	PORT_START("LINE4")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Lat") PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Back") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("LINE5")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('>') PORT_CHAR('.')
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\')
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')

	PORT_START("LINE6")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD)) // xxx
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED) // _
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')

	PORT_START("LINE7")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Str")
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PS (LF)")
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=') // xxx
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')

	PORT_START("LINE8")
	PORT_BIT(0x00, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

INPUT_PORTS_START( ms7007 )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR(' ') PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR(')') PORT_CHAR('9')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ins") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('=') PORT_CHAR('-')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(':') PORT_CHAR('*')

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('\'') PORT_CHAR('7')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('(') PORT_CHAR('8')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR(0xA4) PORT_CHAR('4')

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('!') PORT_CHAR('1')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('"') PORT_CHAR('2')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('#') PORT_CHAR('3')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('%') PORT_CHAR('5')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('&') PORT_CHAR('6')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')

	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')

	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR('<') PORT_CHAR(',')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('~')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Back") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('>') PORT_CHAR('.')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

	PORT_START("LINE7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)

	PORT_START("LINE8")
	PORT_BIT(0xFF, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("CLINE0")
	PORT_BIT(0x1F, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))

	PORT_START("CLINE1")
	PORT_BIT(0x1F, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(ASTERISK))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR(';') PORT_CHAR('+')

	PORT_START("CLINE2")
	PORT_BIT(0x1F, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')

	PORT_START("CLINE3")
	PORT_BIT(0x7F, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("CLINE4")
	PORT_BIT(0x7F, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')

	PORT_START("CLINE5")
	PORT_BIT(0x1F, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Rus/Lat") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)

	PORT_START("CLINE6")
	PORT_BIT(0x1F, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))

	PORT_START("CLINE7")
	PORT_BIT(0x1F, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
INPUT_PORTS_END

/* F4 Character Displayer */
static const gfx_layout radio86_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	128,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_radio86 )
	GFXDECODE_ENTRY( "chargen", 0x0000, radio86_charlayout, 0, 1 )
GFXDECODE_END


/* Machine driver */
void radio86_state::radio86(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, XTAL(16'000'000) / 9);
	m_maincpu->set_addrmap(AS_PROGRAM, &radio86_state::radio86_mem);
	m_maincpu->set_addrmap(AS_IO, &radio86_state::radio86_io);

	I8255(config, m_ppi1);
	m_ppi1->out_pa_callback().set(FUNC(radio86_state::radio86_8255_porta_w2));
	m_ppi1->in_pb_callback().set(FUNC(radio86_state::radio86_8255_portb_r2));
	m_ppi1->in_pc_callback().set(FUNC(radio86_state::radio86_8255_portc_r2));
	m_ppi1->out_pc_callback().set(FUNC(radio86_state::radio86_8255_portc_w2));

	i8275_device &crtc(I8275(config, "crtc", XTAL(16'000'000) / 12));
	crtc.set_character_width(6);
	crtc.set_display_callback(FUNC(radio86_state::display_pixels));
	crtc.drq_wr_callback().set(m_dma, FUNC(i8257_device::dreq2_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_screen_update("crtc", FUNC(i8275_device::screen_update));
	screen.set_raw(XTAL(16'000'000) / 2, 516, 0, 78*6, 310, 0, 30*10);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_radio86);
	PALETTE(config, m_palette, FUNC(radio86_state::radio86_palette), 3);

	SPEAKER(config, "mono").front_center();

	I8257(config, m_dma, XTAL(16'000'000) / 9);
	m_dma->out_hrq_cb().set(FUNC(radio86_state::hrq_w));
	m_dma->in_memr_cb().set(FUNC(radio86_state::memory_read_byte));
	m_dma->out_memw_cb().set(FUNC(radio86_state::memory_write_byte));
	m_dma->out_iow_cb<2>().set("crtc", FUNC(i8275_device::dack_w));
	m_dma->set_reverse_rw_mode(1);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(rkr_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("radio86_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("radio86_cass");
}


void radio86_state::kr03(machine_config &config)
{
	radio86(config);
	/* basic machine hardware */

	m_ppi1->out_pa_callback().set(FUNC(radio86_state::radio86_8255_porta_w2));
	m_ppi1->in_pb_callback().set(FUNC(radio86_state::kr03_8255_portb_r2));
	m_ppi1->in_pc_callback().set(FUNC(radio86_state::radio86_8255_portc_r2));
	m_ppi1->out_pc_callback().set(FUNC(radio86_state::radio86_8255_portc_w2));
}

void radio86_state::radio16(machine_config &config)
{
	radio86(config);
	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &radio86_state::radio86_16_mem);
}

void radio86_state::radiorom(machine_config &config)
{
	radio86(config);
	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &radio86_state::radio86rom_mem);

	I8255(config, m_ppi2);
	m_ppi2->in_pa_callback().set(FUNC(radio86_state::radio86rom_romdisk_porta_r));
	m_ppi2->out_pb_callback().set(FUNC(radio86_state::radio86_romdisk_portb_w));
	m_ppi2->out_pc_callback().set(FUNC(radio86_state::radio86_romdisk_portc_w));

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "radio86_cart", "bin,rom");

	SOFTWARE_LIST(config, "cart_list").set_original("radio86_cart");
}

void radio86_state::radioram(machine_config &config)
{
	radio86(config);
	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &radio86_state::radio86ram_mem);

	I8255(config, m_ppi2);
	m_ppi2->in_pa_callback().set(FUNC(radio86_state::radio86ram_romdisk_porta_r));
	m_ppi2->out_pb_callback().set(FUNC(radio86_state::radio86_romdisk_portb_w));
	m_ppi2->out_pc_callback().set(FUNC(radio86_state::radio86_romdisk_portc_w));
}

void radio86_state::rk7007(machine_config &config)
{
	radio86(config);
	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_IO, &radio86_state::rk7007_io);

	i8255_device &ms7007(I8255(config, "ms7007"));
	ms7007.out_pa_callback().set(FUNC(radio86_state::radio86_8255_porta_w2));
	ms7007.in_pb_callback().set(FUNC(radio86_state::radio86_8255_portb_r2));
	ms7007.in_pc_callback().set(FUNC(radio86_state::rk7007_8255_portc_r));
	ms7007.out_pc_callback().set(FUNC(radio86_state::radio86_8255_portc_w2));
}

void radio86_state::rk700716(machine_config &config)
{
	radio16(config);
	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_IO, &radio86_state::rk7007_io);

	i8255_device &ms7007(I8255(config, "ms7007"));
	ms7007.out_pa_callback().set(FUNC(radio86_state::radio86_8255_porta_w2));
	ms7007.in_pb_callback().set(FUNC(radio86_state::radio86_8255_portb_r2));
	ms7007.in_pc_callback().set(FUNC(radio86_state::rk7007_8255_portc_r));
	ms7007.out_pc_callback().set(FUNC(radio86_state::radio86_8255_portc_w2));
}

void radio86_state::mikron2(machine_config &config)
{
	radio86(config);
	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &radio86_state::mikron2_mem);
}

void radio86_state::impuls03(machine_config &config)
{
	radio86(config);
	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &radio86_state::impuls03_mem);
}

/* ROM definition */
ROM_START( radio86 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "bios.rom", 0x0800, 0x0800, CRC(bf1ceea5) SHA1(8f3d472203e550e9854dd79e1f44628635581ed0))
	ROM_RELOAD( 0x0000, 0x0800 )

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD ("radio86.fnt", 0x0000, 0x0400, CRC(7666bd5e) SHA1(8652787603bee9b4da204745e3b2aa07a4783dfc))
ROM_END

ROM_START( radio4k )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "bios4k.rom", 0x0000, 0x1000, CRC(2ac9d864) SHA1(296716c6cddc9dd31d500ba421aa807c45757cfd))

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD ("radio86.fnt", 0x0000, 0x0400, CRC(7666bd5e) SHA1(8652787603bee9b4da204745e3b2aa07a4783dfc))
ROM_END

ROM_START( spektr01 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "spektr001.rom", 0x0800, 0x0800, CRC(5a38e6d5) SHA1(799c3bbe2a9f08f3aba55379cc093329048350ff))
	ROM_RELOAD( 0x0000, 0x0800 )

	ROM_REGION(0x0800, "chargen",0 )
	ROM_LOAD ("radio86.fnt", 0x0000, 0x0400, CRC(7666bd5e) SHA1(8652787603bee9b4da204745e3b2aa07a4783dfc))
ROM_END

ROM_START( radio16 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "rk86.16k", 0x0800, 0x0800, CRC(fd8a4caf) SHA1(90d6af571049a7c8748eac03541e921eac3f70c5))
	ROM_RELOAD( 0x0000, 0x0800 )

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD ("radio86.fnt", 0x0000, 0x0400, CRC(7666bd5e) SHA1(8652787603bee9b4da204745e3b2aa07a4783dfc))
ROM_END

ROM_START( radiorom )
	ROM_REGION( 0x20000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "32k", "32 KB rom disk")
	ROMX_LOAD( "radiorom.rom", 0x0800, 0x0800, CRC(b5cdeab7) SHA1(1c80d72082f2fb2190b575726cb82d86ae0ee7d8), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "64k", "64 KB rom disk")
	ROMX_LOAD( "radiorom.64",  0x0800, 0x0800, CRC(5250b927) SHA1(e885e0f5b2325190b38a4c92b20a8b4fa78fbd8f), ROM_BIOS(1))
	ROM_COPY( "maincpu", 0x0800, 0x0000, 0x0800 )

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD ("radio86.fnt", 0x0000, 0x0400, CRC(7666bd5e) SHA1(8652787603bee9b4da204745e3b2aa07a4783dfc))
ROM_END

ROM_START( radioram )
	ROM_REGION( 0x20000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "r86-1.bin", 0x0800, 0x0800, CRC(7e7ab7cb) SHA1(fedb00b6b8fbe1167faba3e4611b483f800e6934))
	ROM_RELOAD( 0x0000, 0x0800 )
	ROM_LOAD( "r86-2.bin", 0xe000, 0x0800, CRC(955f0616) SHA1(d2b9f960558bdcb60074091fc79d1ad56c313586))
	ROM_LOAD( "romdisk.bin", 0x10000, 0x10000, CRC(43c0279b) SHA1(bc1dfd9bdbce39460616e2158f5d96279d0af3cf))

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD ("radio86.fnt", 0x0000, 0x0400, CRC(7666bd5e) SHA1(8652787603bee9b4da204745e3b2aa07a4783dfc))
ROM_END

ROM_START( rk7007 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "ms7007.rom", 0x0800, 0x0800, CRC(002811dc) SHA1(4529eb72198c49af77fbcd7833bcd06a1cf9b1ac))
	ROM_RELOAD( 0x0000, 0x0800 )

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD ("radio86.fnt", 0x0000, 0x0400, CRC(7666bd5e) SHA1(8652787603bee9b4da204745e3b2aa07a4783dfc))
ROM_END

ROM_START( rk700716 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "ms7007.16k", 0x0800, 0x0800, CRC(5268d7b6) SHA1(efd69d8456b8cf8b37f33237153c659725608528))
	ROM_RELOAD( 0x0000, 0x0800 )

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD ("radio86.fnt", 0x0000, 0x0400, CRC(7666bd5e) SHA1(8652787603bee9b4da204745e3b2aa07a4783dfc))
ROM_END

ROM_START( mikron2 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mikron2.bin", 0x0800, 0x0800, CRC(2cd79bb4) SHA1(501df47e65aaa8f4ce27751bc2a7e7089e2e888c))
	ROM_RELOAD( 0x0000, 0x0800 )

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD ("radio86.fnt", 0x0000, 0x0400, CRC(7666bd5e) SHA1(8652787603bee9b4da204745e3b2aa07a4783dfc))
ROM_END

ROM_START( kr03 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "kr03-dd17.rf2", 0x0800, 0x0800, CRC(ac2e24d5) SHA1(a1317a261bfd55b3b37109b14d1391308dee04de))
	ROM_RELOAD( 0x0000, 0x0800 )

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD ("kr03-dd12.rf2", 0x0000, 0x0800, CRC(085f4259) SHA1(11c5829b072a00961ad936c26559fb63bf2dc896))
ROM_END

ROM_START( impuls03 )
	ROM_REGION( 0x3000, "maincpu", 0 )
	ROM_LOAD( "imp03bas.rom", 0x1000, 0x2000, CRC(b13b2de4) SHA1(9af8c49d72ca257bc34cad3c62e530730929702e))
	ROM_LOAD( "imp03mon.rom", 0x0800, 0x0800, CRC(8c591ce4) SHA1(8e8e9cba6b3123d74218b92f4b4210606ba53376))
	ROM_RELOAD( 0x0000, 0x0800 )

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD ("radio86.fnt", 0x0000, 0x0400, CRC(7666bd5e) SHA1(8652787603bee9b4da204745e3b2aa07a4783dfc))
ROM_END
/* Driver */

//    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT    CLASS          INIT           COMPANY        FULLNAME                       FLAGS
COMP( 1986, radio86,  0,       0,      radio86,  radio86, radio86_state, init_radio86,  "<unknown>",   "Radio-86RK",                  MACHINE_SUPPORTS_SAVE )
COMP( 1986, radio16,  radio86, 0,      radio16,  radio86, radio86_state, init_radio86,  "<unknown>",   "Radio-86RK (16K RAM)",        MACHINE_SUPPORTS_SAVE )
COMP( 1986, kr03,     radio86, 0,      kr03,     kr03,    radio86_state, init_radio86,  "Elektronika", "KR-03",                       MACHINE_SUPPORTS_SAVE )
COMP( 1986, radio4k,  radio86, 0,      radio86,  radio86, radio86_state, init_radio86,  "<unknown>",   "Radio-86RK (4K ROM)",         MACHINE_SUPPORTS_SAVE )
COMP( 1986, radiorom, radio86, 0,      radiorom, radio86, radio86_state, init_radio86,  "<unknown>",   "Radio-86RK (ROM-Disk)",       MACHINE_SUPPORTS_SAVE )
COMP( 1986, radioram, radio86, 0,      radioram, radio86, radio86_state, init_radioram, "<unknown>",   "Radio-86RK (ROM/RAM Disk)",   MACHINE_SUPPORTS_SAVE )
COMP( 1986, spektr01, radio86, 0,      radio86,  radio86, radio86_state, init_radio86,  "<unknown>",   "Spektr-001",                  MACHINE_SUPPORTS_SAVE )
COMP( 1986, rk7007,   radio86, 0,      rk7007,   ms7007,  radio86_state, init_radio86,  "<unknown>",   "Radio-86RK (MS7007)",         MACHINE_SUPPORTS_SAVE )
COMP( 1986, rk700716, radio86, 0,      rk700716, ms7007,  radio86_state, init_radio86,  "<unknown>",   "Radio-86RK (MS7007 16K RAM)", MACHINE_SUPPORTS_SAVE )
COMP( 1986, mikron2,  radio86, 0,      mikron2,  radio86, radio86_state, init_radio86,  "<unknown>",   "Mikron-2",                    MACHINE_SUPPORTS_SAVE )
COMP( 1986, impuls03, radio86, 0,      impuls03, radio86, radio86_state, init_radio86,  "<unknown>",   "Impuls-03",                   MACHINE_SUPPORTS_SAVE )



radionic.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************
Komtek 1 (Radionic R1001) memory map

0000-37ff ROM                            R   D0-D7
37de      UART status                    R/W D0-D7
37df      UART data                      R/W D0-D7
37e0      interrupt latch address
37e1      select disk drive 0            W
37e2      cassette drive latch address   W
37e3      select disk drive 1            W
37e4      select which cassette unit     W   D0-D1 (D0 selects unit 1, D1 selects unit 2)
37e5      select disk drive 2            W
37e7      select disk drive 3            W
37e0-37e3 floppy motor                   W   D0-D3
          or floppy head select          W   D3
37e8      send a byte to printer         W   D0-D7
37e8      read printer status            R   D7
37ec-37ef FDC FD1771                     R/W D0-D7
37ec      command                        W   D0-D7
37ec      status                         R   D0-D7
37ed      track                          R/W D0-D7
37ee      sector                         R/W D0-D7
37ef      data                           R/W D0-D7
3800-38ff keyboard matrix                R   D0-D7
3900-3bff unused - kbd mirrored
3c00-3fff static video RAM and colour ram, banked
4000-ffff dynamic main RAM

Printer: Usually 37e8, but you can use the PPI instead.

Cassette baud rate: 500 baud

I/O ports
FF:
- bits 0 and 1 are for writing a cassette
- bit 2 must be high to turn the cassette motor on, enables cassette data paths on a system-80
- bit 3 switches the display between 64 or 32 characters per line
- bit 6 remembers the 32/64 screen mode (inverted)
- bit 7 is for reading from a cassette

Shift and Right-arrow will enable 32 cpl.

SYSTEM commands:
    - Press Break (End key) to quit
    - Press Enter to exit with error
    - xxxx to load program xxxx from tape.
    - / to execute last program loaded
    - /nnnnn to execute program at nnnnn (decimal)

Inbuilt Monitor - its existence is not revealed to the user.
    - SYSTEM then /12710 = enter machine-language monitor
    Monitor commands:
    - B : return to Basic
    - Dnnnn : Dump hex to screen. Press down-arrow for more. Press enter to quit.
    - Mnnnn : Modify memory. Enter new byte and it increments to next address. X to quit.
    - Gnnnn : Execute program at nnnn
    - Gnnnn,tttt : as above, breakpoint at tttt
    - R : modify registers
    The monitor appears to end at 33D3, however the rom contains more code through to 35EB.
    But, the 34xx area is overwitten by the optional RS-232 expansion box.

About the RTC - The time is incremented while it is enabled via the Machine Configuration. The time
    is stored in a series of bytes in the computer's work area. The bytes are in a certain order,
    this being: seconds, minutes, hours, days. The seconds are stored at 0x4041. A reboot always
    sets the time to zero.

About colours - The computer has 16 colours with a byte at 350B controlling the operation.
    POKE 13579,2  : monochrome
    POKE 13579,4  : programmable colour
    POKE 13579,12 : automatic colour
    More information was discovered, this being
    - It doesn't have to be 350B (13579), anything in the 35xx range will do.
    - d0 : write of vram goes to vram (0 = yes, 1 = no)
    - d1 : write of vram goes to colour ram (0 = yes, 1 = no)
    - d2 : colour enable (0 = monochrome, 1 = colour)
    - d3 : programmable or automatic (0 = programmable, 1 = automatic)
    The colour codes and names are listed in the palette below. The descriptions are quite vague,
    and appear to be background only. The foreground is assumed to always be white.
    Automatic is a preset colour set by internal jumpers.
    - No schematic or technical info for the colour board
    - No information on the settings of the Automatic mode
    - The "User Friendly Manual" has a bunch of mistakes (page 15).
    - It's not known if colour ram can be read. But LDOS won't scroll if it can't always read vram.
    - No colour programs exist in the wild, so nothing can be verified.
    So, some guesswork has been done.

About the PPI - A selling point of this computer was the ability to sense and control external gadgets.
    For example, it could join to a temperature sensor, and when the temperature reached a certain value
    the computer could instruct a device to turn on or off. There's 4 input jacks and 6 output jacks.
    The PPI can also control a parallel printer. To enable this, enter SYSTEM then /12367 .

About the RS-232 unit - This is an external box that plugs into the expansion port. It takes over memory
    region 3400-34FF, although it only uses 3400 and 3401. It has a baud generator consisting of 2x 74LS163
    and a dipswitch block to choose one of 5 possible rates. The UART and RS-232 parts are conventional.
    There's no programming of the unit from the inbuilt roms; you need to write your own.

********************************************************************************************************

To Do / Status:
--------------

- Difficulty loading real tapes.
- Writing to floppy is problematic; freezing/crashing are common issues.
- Add fdc doubler (only some info available)

*******************************************************************************************************/

#include "emu.h"
#include "trs80.h"
#include "trs80_quik.h"

#include "bus/rs232/rs232.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/i8255.h"

#include "softlist_dev.h"
#include "utf8.h"


namespace {

#define MASTER_XTAL 12164800


class radionic_state : public trs80_state
{
public:
	radionic_state(const machine_config &mconfig, device_type type, const char *tag)
		: trs80_state(mconfig, type, tag)
		, m_ppi(*this, "ppi")
		, m_uart2(*this, "uart2")
		, m_clock(*this, "uclock")
	{ }

	void radionic(machine_config &config);

private:
	INTERRUPT_GEN_MEMBER(rtc_via_nmi);
	void radionic_palette(palette_device &palette) const;
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	std::unique_ptr<u8[]> m_vram;  // video ram
	std::unique_ptr<u8[]> m_cram;  // colour ram
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;
	void cctrl_w(offs_t offset, u8 data);
	void video_w(offs_t offset, u8 data);
	u8 video_r(offs_t offset);
	void ppi_pa_w(offs_t offset, u8 data);
	void ppi_pb_w(offs_t offset, u8 data);
	void ppi_pc_w(offs_t offset, u8 data);
	u8 ppi_pc_r(offs_t offset);
	void mem_map(address_map &map) ATTR_COLD;
	static void floppy_formats(format_registration &fr);
	u8 m_cctrl = 2;
	required_device<i8255_device> m_ppi;
	required_device<i8251_device> m_uart2;
	required_device<clock_device> m_clock;
};

void radionic_state::mem_map(address_map &map)
{
	map(0x0000, 0x37ff).rom();
	map(0x3400, 0x3401).mirror(0xfe).rw("uart2", FUNC(i8251_device::read), FUNC(i8251_device::write));  // optional RS-232
	map(0x3500, 0x35ff).w(FUNC(radionic_state::cctrl_w));
	map(0x3600, 0x3603).mirror(0xfc).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));  // interface to external sensors
	map(0x37e0, 0x37e3).rw(FUNC(radionic_state::irq_status_r), FUNC(radionic_state::motor_w));
	map(0x37e8, 0x37eb).rw(FUNC(radionic_state::printer_r), FUNC(radionic_state::printer_w));
	map(0x37ec, 0x37ef).rw(FUNC(radionic_state::fdc_r), FUNC(radionic_state::fdc_w));
	map(0x3800, 0x3bff).r(FUNC(radionic_state::keyboard_r));
	map(0x3c00, 0x3fff).rw(FUNC(radionic_state::video_r), FUNC(radionic_state::video_w));
	map(0x4000, 0xffff).ram();
}

/**************************************************************************
   w/o SHIFT                             with SHIFT
   +-------------------------------+     +-------------------------------+
   | 0   1   2   3   4   5   6   7 |     | 0   1   2   3   4   5   6   7 |
+--+---+---+---+---+---+---+---+---+  +--+---+---+---+---+---+---+---+---+
|0 | @ | A | B | C | D | E | F | G |  |0 | ` | a | b | c | d | e | f | g |
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|1 | H | I | J | K | L | M | N | O |  |1 | h | i | j | k | l | m | n | o |
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|2 | P | Q | R | S | T | U | V | W |  |2 | p | q | r | s | t | u | v | w |
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|3 | X | Y | Z | [ | \ | ] | ^ | _ |  |3 | x | y | z | { | | | } | ~ |   |
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|4 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |  |4 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|5 | 8 | 9 | : | ; | , | - | . | / |  |5 | 8 | 9 | * | + | < | = | > | ? |
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|6 |ENT|CLR|BRK|UP |DN |LFT|RGT|SPC|  |6 |ENT|CLR|BRK|UP |DN |LFT|RGT|SPC|
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|7 |SHF|   |   |   |   |   |   |   |  |7 |SHF|   |   |   |   |   |   |   |
+--+---+---+---+---+---+---+---+---+  +--+---+---+---+---+---+---+---+---+

***************************************************************************/

static INPUT_PORTS_START( radionic )
	PORT_START("LINE0")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('@')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)          PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)          PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)          PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)          PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)          PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)          PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)          PORT_CHAR('G') PORT_CHAR('g')

	PORT_START("LINE1")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)          PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)          PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)          PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)          PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)          PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)          PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)          PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)          PORT_CHAR('O') PORT_CHAR('o')

	PORT_START("LINE2")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)          PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)          PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)          PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)          PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)          PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)          PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)          PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)          PORT_CHAR('W') PORT_CHAR('w')

	PORT_START("LINE3")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)          PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)          PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)          PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_F1)  PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("\\") PORT_CODE(KEYCODE_F2) PORT_CHAR('\\') PORT_CHAR('}')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_F3)  PORT_CHAR(']') PORT_CHAR('|')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_F4)  PORT_CHAR('^')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("_") PORT_CODE(KEYCODE_F5)  PORT_CHAR('_') // radionic: LF

	PORT_START("LINE4") // Number pad: System 80 Mk II only
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD)          PORT_CHAR('0')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)          PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)          PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("LINE5")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME(": *") PORT_CODE(KEYCODE_MINUS)    PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("; +") PORT_CODE(KEYCODE_COLON)    PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA)    PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("- =") PORT_CODE(KEYCODE_EQUALS)   PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP)     PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH)    PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("LINE6")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)      PORT_CHAR(13)
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("Clear") PORT_CODE(KEYCODE_HOME)       PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_END)        PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)        PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	/* backspace do the same as cursor left */
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_BACKSPACE)   PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')

	PORT_START("LINE7")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0xfe, 0x00, IPT_UNUSED)

	PORT_START("CONFIG")
	PORT_CONFNAME(  0xc0, 0x00, "Floppy and RTC") // Floppy doesn't work if RTC is on, so interlink them.
	PORT_CONFSETTING(   0x00, "Both Off" )
	PORT_CONFSETTING(   0x80, "RTC off, Floppy On" )
	PORT_CONFSETTING(   0x40, "RTC on, Floppy Off" )
	PORT_CONFNAME(  0x30, 0x00, "Colour")  // 2 switches on the side
	PORT_CONFSETTING(   0x00, "Monochrome" )
	PORT_CONFSETTING(   0x10, "Auto Colour" )
	PORT_CONFSETTING(   0x30, "Programmable Colour" )
	PORT_CONFNAME(  0x07, 0x00, "Serial Port" ) // a jumper on the board? (not documented)
	PORT_CONFSETTING(   0x00, "300 baud" )
	PORT_CONFSETTING(   0x01, "600 baud" )
	PORT_CONFSETTING(   0x02, "1200 baud" )
	PORT_CONFSETTING(   0x03, "2400 baud" )
	PORT_CONFSETTING(   0x04, "4800 baud" )
INPUT_PORTS_END


/* Levels are unknown - guessing */
static constexpr rgb_t radionic_pens[] =
{
	// colour
	{ 200, 200, 200 }, // 0 off white
	{   0, 255,   0 }, // 1 light green
	{ 255,   0,   0 }, // 2 red
	{   0, 128,   0 }, // 3 dark green
	{   0,   0, 255 }, // 4 blue
	{   0, 255, 255 }, // 5 greenish blue
	{ 255,   3,  62 }, // 6 rose red
	{ 136, 155, 174 }, // 7 dusty blue
	{ 200, 200,   0 }, // 8 greenish yellow
	{ 173, 255,  47 }, // 9 light yellow (greenish)
	{ 207,  33,  33 }, // 10 golden red
	{ 128, 128, 128 }, // 11 grey
	{ 220, 255,   0 }, // 12 reddish green
	{ 255, 255, 191 }, // 13 pale yellow
	{ 247, 170,   0 }, // 14 orange
	{  90, 156,  57 }, // 15 ogre green
	// monochrome
	{ 250, 250, 250 }, // 16 white
	{   0,   0,   0 }  // 17 black
};

void radionic_state::radionic_palette(palette_device &palette) const
{
	palette.set_pen_colors(0, radionic_pens);
}

/**************************** F4 CHARACTER DISPLAYER ***********************************************************/
static const gfx_layout radionic_charlayout =
{
	8, 16,          /* 8 x 16 characters */
	256,            /* 256 characters */
	1,          /* 1 bits per pixel */
	{ 0 },          /* no bitplanes */
	/* x offsets */
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	/* y offsets */
	{  0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 2048*8, 2049*8, 2050*8, 2051*8, 2052*8, 2053*8, 2054*8, 2055*8 },
	8*8        /* every char takes 16 bytes */
};

static GFXDECODE_START(gfx_radionic)
	GFXDECODE_ENTRY( "chargen", 0, radionic_charlayout, 0, 1 )
GFXDECODE_END

/* lores characters are in the character generator. Each character is 8x16. */
u32 radionic_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 sy=0,ma=0;
	u8 cols = m_cpl ? 32 : 64;
	u8 skip = m_cpl ? 2 : 1;
	// colours have to be enabled both by a poke and by switches on the side of the unit
	bool col_en = BIT(m_cctrl, 2) && BIT(m_io_config->read(), 4);
	bool auto_en = BIT(m_cctrl, 3) || !BIT(m_io_config->read(), 5);
	u8 fg = 16, bg = 17;   // monochrome
	if (col_en && auto_en)
		bg = 4;    // automatic colour

	if (cols != m_cols)
	{
		m_cols = cols;
		screen.set_visible_area(0, cols*8-1, 0, 16*16-1);
	}

	for (u8 y = 0; y < 16; y++)
	{
		for (u8 ra = 0; ra < 16; ra++)
		{
			u16 *p = &bitmap.pix(sy++);

			for (u16 x = ma; x < ma + 64; x+=skip)
			{
				u8 chr = m_vram[x];
				if (col_en && !auto_en)
					bg = m_cram[x] & 15;

				/* get pattern of pixels for that character scanline */
				u8 gfx = m_p_chargen[(chr<<3) | (ra & 7) | (ra & 8) << 8];

				/* Display a scanline of a character (8 pixels) */
				*p++ = BIT(gfx, 0) ? fg : bg;
				*p++ = BIT(gfx, 1) ? fg : bg;
				*p++ = BIT(gfx, 2) ? fg : bg;
				*p++ = BIT(gfx, 3) ? fg : bg;
				*p++ = BIT(gfx, 4) ? fg : bg;
				*p++ = BIT(gfx, 5) ? fg : bg;
				*p++ = BIT(gfx, 6) ? fg : bg;
				*p++ = BIT(gfx, 7) ? fg : bg;
			}
		}
		ma+=64;
	}
	return 0;
}

void radionic_state::machine_start()
{
	trs80_state::machine_start();
	save_item(NAME(m_cctrl));

	// video ram
	m_vram = make_unique_clear<u8[]>(0x0800);
	save_pointer(NAME(m_vram), 0x0800);

	// colour
	m_cram = make_unique_clear<u8[]>(0x0800);
	save_pointer(NAME(m_cram), 0x0800);
}

void radionic_state::machine_reset()
{
	trs80_state::machine_reset();
	m_cctrl = 2;

	u8 sw = m_io_config->read() & 7;
	u16 baud = 300;
	for (u8 i = 0; i < sw; i++)
		baud <<= 1;
	m_clock->set_unscaled_clock(baud*16); // It's designed on the assumption that the uart will divide by 16
	//printf("%d\n",baud);
}

INTERRUPT_GEN_MEMBER(radionic_state::rtc_via_nmi)
{
	if (BIT(m_io_config->read(), 6))
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::from_usec(100));
}

u8 radionic_state::video_r(offs_t offset)
{
	return m_vram[offset];
}

void radionic_state::video_w(offs_t offset, u8 data)
{
	if (!BIT(m_cctrl, 0))
		m_vram[offset] = data;
	if (!BIT(m_cctrl, 1))
		m_cram[offset] = data;
}

void radionic_state::cctrl_w(offs_t offset, u8 data)
{
	m_cctrl = data & 15;
}

void radionic_state::ppi_pa_w(offs_t offset, u8 data)
{
	// d0-7: Data to extra printer
}

void radionic_state::ppi_pb_w(offs_t offset, u8 data)
{
	// d0-7: Outputs to control jacks (only 6 connected up by default)
}

void radionic_state::ppi_pc_w(offs_t offset, u8 data)
{
	// Printer control
	// d0: Strobe
}

u8 radionic_state::ppi_pc_r(offs_t offset)
{
	// Printer Status
	// d1: Busy
	// d2: out of paper
	// d3: Unit select

	// Sensor Status
	// d4-7: sensing inputs
	return 0xFF;
}

void radionic_state::floppy_formats(format_registration &fr)
{
	fr.add(FLOPPY_JV1_FORMAT);
}

// Most images are single-sided, 40 tracks or less.
// However, the default is QD to prevent MAME from
// crashing if a disk with more than 40 tracks is used.
static void radionic_floppies(device_slot_interface &device)
{
	device.option_add("35t_sd", FLOPPY_525_SSSD_35T);
	device.option_add("40t_sd", FLOPPY_525_SSSD);
	device.option_add("40t_dd", FLOPPY_525_DD);
	device.option_add("80t_qd", FLOPPY_525_QD);
}

void radionic_state::radionic(machine_config &config)
{
	// Photos from Incog show 12.1648, and 3.579545 xtals. The schematic seems to just approximate these values.
	Z80(config, m_maincpu, 3.579545_MHz_XTAL / 2);
	//m_maincpu->set_clock(MASTER_XTAL / 6); // early machines only, before the floppy interface was added
	m_maincpu->set_addrmap(AS_PROGRAM, &radionic_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &radionic_state::trs80_io);
	m_maincpu->set_periodic_int(FUNC(radionic_state::rtc_via_nmi), attotime::from_hz(MASTER_XTAL / 12 / 16384));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(MASTER_XTAL, 768, 0, 512, 312, 0, 256);
	screen.set_screen_update(FUNC(radionic_state::screen_update));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_radionic);
	PALETTE(config, "palette", FUNC(radionic_state::radionic_palette), 18);   // 16 colours + monochrome

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	/* devices */
	CASSETTE(config, m_cassette);
	m_cassette->set_formats(trs80l2_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("trs80_cass");

	TRS80_QUICKLOAD(config, "quickload", m_maincpu, attotime::from_seconds(1));

	FD1771(config, m_fdc, 16_MHz_XTAL / 16);
	m_fdc->intrq_wr_callback().set(FUNC(radionic_state::intrq_w));

	FLOPPY_CONNECTOR(config, m_floppy[0], radionic_floppies, "80t_qd", radionic_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], radionic_floppies, "80t_qd", radionic_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[2], radionic_floppies, nullptr, radionic_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[3], radionic_floppies, nullptr, radionic_state::floppy_formats).enable_sound(true);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit7));
	m_centronics->perror_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit6));
	m_centronics->select_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit5));
	m_centronics->fault_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit4));

	INPUT_BUFFER(config, m_cent_status_in);

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	CLOCK(config, m_clock, 4'800);
	m_clock->signal_handler().set(m_uart2, FUNC(i8251_device::write_txc));
	m_clock->signal_handler().set(m_uart2, FUNC(i8251_device::write_rxc));

	// RS232 port: the schematic is missing most of the info, so guessing
	I8251(config, m_uart2, 3.579545_MHz_XTAL / 2 );
	m_uart2->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_uart2->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	m_uart2->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_uart2, FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set(m_uart2, FUNC(i8251_device::write_dsr));

	// Interface to external circuits
	I8255(config, m_ppi);
	m_ppi->in_pc_callback().set(FUNC(radionic_state::ppi_pc_r));      // Sensing from external and printer status
	m_ppi->out_pa_callback().set(FUNC(radionic_state::ppi_pa_w));    // Data for external plugin printer module
	m_ppi->out_pb_callback().set(FUNC(radionic_state::ppi_pb_w));    // Control data to external
	m_ppi->out_pc_callback().set(FUNC(radionic_state::ppi_pc_w));    // Printer strobe

	SOFTWARE_LIST(config, "cass_list").set_original("trs80_cass").set_filter("1"); // R
	SOFTWARE_LIST(config, "quik_list").set_original("trs80_quik").set_filter("1"); // R
	SOFTWARE_LIST(config, "flop_list").set_original("trs80_flop").set_filter("1"); // R
}


/***************************************************************************

  Game driver(s)

***************************************************************************/

ROM_START(radionic)
	ROM_REGION(0x3800, "maincpu", 0)
	ROM_LOAD("ep1.z37",        0x0000, 0x1000, CRC(e8908f44) SHA1(7a5a60c3afbeb6b8434737dd302332179a7fca59) )
	ROM_LOAD("ep2.z36",        0x1000, 0x1000, CRC(46e88fbf) SHA1(a3ca32757f269e09316e1e91ba1502774e2f5155) )
	ROM_LOAD("ep3.z35",        0x2000, 0x1000, CRC(306e5d66) SHA1(1e1abcfb5b02d4567cf6a81ffc35318723442369) )
	ROM_LOAD("ep4.z34",        0x3000, 0x0800, CRC(70f90f26) SHA1(cbee70da04a3efac08e50b8e3a270262c2440120) )
	ROM_CONTINUE(              0x3000, 0x0800)

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("trschar.z58",    0x0000, 0x1000, CRC(02e767b6) SHA1(c431fcc6bd04ce2800ca8c36f6f8aeb2f91ce9f7) )
ROM_END

} // anonymous namespace


//    YEAR  NAME         PARENT   COMPAT   MACHINE   INPUT     CLASS           INIT           COMPANY     FULLNAME                    FLAGS
COMP( 1983, radionic,    0,       trs80l2, radionic, radionic, radionic_state, empty_init,    "Komtek",   "Radionic R1001/Komtek 1",  MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



rainbow.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Miodrag Milanovic,Karl-Ludwig Deisenhofer
/***************************************************************************************************
DEC Rainbow 100

Driver-in-progress by R. Belmont and Miodrag Milanovic.
Portions (2013 - 2018) by Karl-Ludwig Deisenhofer (Floppy, ClikClok RTC, NVRAM, DIPs, hard disk, Color Graphics).
Baud rate generator by AJR (2018) and Shattered (2016), keyboard & GDC fixes by Cracyc (June - Nov. 2016).

Model B implementation has a workaround prevents a side effect of ERROR 13 to unlock floppy drives A-D.

Native single sided 5.25" images with 80 tracks, 10 sectors are well tested (*.IMD / *.TD0=TeleDisk / *.IMG with 400 K).
VT180 images (184.320 Bytes) are very unreliable in CP/M - though a real machine can read them.
5.25 MFM PC style drives and 720 K (3.5 " DS-DD MFM PC formatted disks) (on slots 3 + 4) show regressions / bugs
  as of Dec.2018 (file content bad while dir is OK, seek errors, write fault errors when copying _to_ hard disk).

ALWAYS USE THE RIGHT SLOT AND SAVE YOUR DATA BEFORE MOUNTING FOREIGN DISK FORMATS!

You * should * also reassign SETUP (away from F3, where it sits on a LK201).
DATA LOSS POSSIBLE: when in partial emulation mode, F3 performs a hard reset!

STATE AS OF DECEMBER 2018
-------------------------
Driver is based entirely on the DEC-100 'B' variant (DEC-190 and DEC-100 A models are treated as clones).
While this is OK for the compatible -190, it doesn't do justice to ancient '100 A' hardware.
The public domain file RBCONVERT.ZIP documents how model 'A' differs from version B.
NVRAM files from -A and -B machines are not interchangeable. If problems arise, delete the NVRAM file.

Venix 86-R (BSW) is working, just follow https://github.com/bsdimp/venix/blob/master/doc/MESS-RB-INSTALL.md

CPM 2.1 / DOS2.11 / DOS 3.x / diag disks boot. UCSD systems (fort_sys, pas_sys) boot, but expect 4 QD drives
  loaded with disks (reassign slots, reset and mount three empty 400 K images before startup at #2, #3, #4).

It is possible to boot DOS 3.10 from floppy A: and later use a hard disk attached to E:.

NB.: a single hard disk (5 - 67 MB, 512 byte sectors) may be attached before startup. It should remain there
until shutdown. "Hot swapping" wasn't possible on the original system (our GUI just doesn't forbid it).

To create a DEC RD50/ST506 compatible image (153 cylinders, 4 heads, 16 sectors, standard 512 byte sectors) enter
>chdman createhd -c none -chs 153,4,16 -ss 512 -o RD50_ST506.chd
NOTE: use -c none parameter for no compression. No more than 8 heads or 1024 cylinders.

Some BUGS remain: BIOS autoboot doesnt work at all. It is not possible to boot from a properly formatted
 winchester with "W" (CPU crash). So there's an issue with the secondary boot loader (for hard disks)...

CTRL-SETUP (soft reboot) always triggers ERROR 19 (64 K RAM err.). One explanation is that ZFLIP/ZRESET is
handled wrongly, so shared mem. just below $8000 is tainted by Z80 stack data. A reentrance problem?

Occassionally, ERROR 13 -keyboard stuck- appears (for reasons yet unknown).


CORVUS HARD DISK
----------------
Up to 4 Corvus Disks with up to 20 MB each can be emulated (to be mounted as hard disks 2 - 5).
MS DOS 2.x and CP/M v2.x were once supported, but are untested (in part because no binary drivers have survived).

To get a Corvus 11 drive up and running under CP/M 1.x, you'll need drcdutil.td0 from Donald Maslin's Archive.

First, create a 11 MB hard disk:
>Chdman createhd -c none -chs 306,4,20 -ss 512 -o CORVUS11.chd
[ -chs 306,2,20 for the 6 MB model and  -chs 306,6,20 for the 20 MB type ]

Then make a copy of your CP/M 86-80 V1.x boot disk. This copy must be patched to make the Corvus hard drive usable!
With 'drcdutil.td0' mounted in A: and a write enabled (non TeleDisk) image of CPM 1.x in B: type:
b:>SUBMIT A:hinstall

This replaces the following CP/M files on B:
   B:Z80CCP.SYS  <- A:HZ80CCP.SYS
   B:Z80.SYS     <- A:HZ80.SYS
   B:PRMTVPV.SYS <- A:HPRMTVPV.SYS

Due to a missing drive specification in HINSTALL.SUB, the last PIP must be invoked manually:
b:>PIP B:PRMTVPVT.SYS=A:HPRMTVPV.SYS[V]

Finally, boot from the newly patched CP/M disk and type CLINK2TN (a step necessary after each cold boot).
CLINK2TN can only be used together with a Corvus 11 MB hard disk. It needs a patched CP/M 1.x disk and won't run on CP/M 2.x.
[ use CLINK2FV for the 6 MB model and CLINK2TW for the 20 MB type ]

Two steps are needed to initialize the new disk:
Step 1: invoke PUTGET, then press "f". Enter "Drive no: 1", "HEX BYTE? e5", "Starting disc address?  2320", "Number of Sectors? 64"
Step 2: invoke PUTGET, then press "f". Enter "Drive no: 1", "HEX BYTE? e5", "Starting disc address?  48592", "Number of Sectors? 64"
Done.

Required steps vary with 5 and 20 MB models (look into the *.DOC files in DRCDUTIL.TD0 / CLINK86.A86 / DRIVEL.COM).
Parameters for initialization can be taken from Chapter 2 of the Disk System Installion Guide for TRS-80 II (same type H drives).


COLOR EMULATION (NEC 7220 + extra hardware)
-------------------------------------------

-------------------- Differences to VT240: ---------------------------------------------------
   - Registers of graphics option not directly mapped (indirect access via mode register)
   - write mask is 16 bits wide (not only 8)
   - scroll register is 8 bits wide - not 16.
   - no "LINE ERASE MODE", 7220 DMA lines are unused. No ZOOM hardware (factor must always be 1)

   Two modes: highres and medres mode (different bank length..?)
   - MEDRES: palette of 16 colors out of 4096.   384 x 240
   - HIGRES: palette of  4 colors out of 4096.   800 x 240
   Palette takes 2 byte per palette entry. CLUT ("color map") is 32 byte long.
------------------------------------------------------------------------------------------------

DEC 'R-M-B' COLOR CABLE VS. THE UNOFFICIAL 'R-G-B' MODE (a bit of history)
   (1) the standard DEC "color cable" connected the green gun of a VR241 to the mono output of the Rainbow
   (2) an unofficial DIY cable enabled R-G-B graphics + separate text

EMULATION SPECIFIC
   (1) COLOR_MONITOR reflects DEC's recommendation (R-M-B with VR241 above)
   (2) DUAL MONITOR enables both screens, even if onboard graphics has been accidently shut off
       (also helps debugging semi broken programs, for example Doodle).
   (3) AUTODETECT (DIP setting) snoops the color palette and chooses the correct 'wiring'

SCREEN 1 vs. SCREEN 2 IN EMULATION
   All GDC 7220 output is displayed on the right. Be it color or monochrome, Option Graphics output is on screen 2.
   If you select MONO_MONITOR via DIP, output from GDC will appear on screen 2 in 16 shades of grey.
   The type of monochrome monitor (VR-210 A, B or C) is selectable via another DIP (coarsly simulates a phosphor color).

BUGS
- GDC diagnostic disk fails on 9 of 13 tests (tests 4 and 6 - 13).

Details
a. (Rainbow driver) : interaction between DEC's external hardware and the NEC 7220 isn't fully understood (see page 173 of AA-AE36A)
   It is also unclear what port $50 actually does when it 'synchronizes R-M-W cycles'.
   For now, we provide sane defaults for both vector and bitmap units without disturbing display mode(s) or the NEC 7220.
b. the Hblank / Vblank ratio is plainly wrong (quick test / subtest #6),
c. IRQs are flagged as 'erratic' (quick test / subtest #12).
d. (7220) : incorrect fifo stati are handed out (GDC reports FIFO_EMPTY instead of _FULL when quick test #4 floods the queue)
e. (7220) : RDAT with MOD 2 used extensively here, but unimplemented (modes other than 0 undocumented by NEC / Intel)

UNIMPLEMENTED:
- Rainbow 100 A palette quirks (2 bit palette... applies to certain modes only)

UNKNOWN IMPLEMENTATION DETAILS:
1. READBACK (hard copy programs like JOBSDUMP definitely use it. See also GDC diagnostics).  VRAM_R...?

2. UNVERIFIED DIVIDERS (31.188 Mhz / 32) is at least close to 1 Mhz (as on the VT240, which uses a very similar design)

3. UPD7220 / CORE oddities

To obtain pixel exact graphics use 'Graphics Only' in Video Options and cmd.line switches -nowindow -aspect1 auto -nokeepaspect
(Over-Under or Side-by-Side modes always distorted on my 1600 x 900 laptop)


CURRENTY UNEMULATED
-------------------
(a) the serial printer on port B prints garbage. It is worth to mention that port B relies on XON/XOFF,
    while DTR_L (CTS B) means 'printer ready'. There is also a ROM patch in place...

(b1) LOOPBACK circuit not emulated (used in startup tests).

(b2) system interaction tests HALT Z80 CPU at location $0211 (forever). Boot the RX50 diag.disk
 to see what happens (key 3 - individual tests, then 12 - system interaction). Uses LOOPBACK too?

(c) arbitration chip (E11; in 100-A schematics or E13 in -B) is dumped, but yet unemulated.
It is a 6308 OTP ROM (2048 bit, 256 x 8) used as a lookup table (LUT) with the address pins (A)
used as inputs and the data pins (D) as output.

Plays a role in DMA access to lower memory (limited to 64 K; Extended communication option only).
Arbiter is also involved in refresh and shared memory contention (affects Z80/8088 CPU cycles).

=> INPUTS on E13 (PC-100 B):

SH5 RF SH REQ H   -> Pin 19 (A7) shared memory request / refresh ?
     1K -> +5 V   -> Pin 18 (A6) < UNUSED >
SH 2 BDL ACK (L)  -> Pin 17 (A5) BUNDLE OPTION: IRQ acknowledged
SH 2 NONSHRCYC H  -> Pin 5 (A4) unshared memory cycle is in progress
SH 2 PRECHARGE H  -> Pin 4 (A3)
SH 2 SHMUX 88 ENB -> Pin 3 (A2) shared memory
SH2 DO REFRESH H  -> Pin 2 (A1) indicates that extended memory must be refreshed -> on J6 as (L)
SH10 BDL REQ (L)  -> Pin 1 (A0) BUNDLE OPTION wishes to use shared memory

HARDWARE UPGRADES WORTH EMULATING (should be implemented as SLOT DEVICES):
* Extended communication option (occupies BUNDLE_OPTION 1 + 2)  REFERENCE: AA-V172A-TV + Addendum AV-Y890A-TV.
Two ports, a high-speed RS-422 half-duplex interface (port A) + lower-speed RS-423 full/half-duplex interface
with modem control (port B). A 5 Mhz. 8237 DMA controller transfers data into and out of shared memory (not: optional RAM).

Uses SHRAM, SHMA, BDL SH WR L, NONSHARED CYCLE. Implementation requires DMA and arbitration logic (using dump of E11/E13 ?).
Can't be added if RD51 hard disk controller present (J4 + J5). For programming info see NEWCOM1.DOC (-> RBETECDOC.ZIP).

* ( NO DUMP YET ) PC CHARACTER SET (Suitable Solutions?). Supported by IBM PC software emulator named CodeBlue (see 3.1 patch)

* ( NO DUMP YET ) TECHNICAL CHARACTER SET (TCS; available for Rainbow 100, 100B, 100+; $95 from DEC)
Source: price list of a DEC reseller.
Contains 94 graphic characters from $A1 - $FE, including symbols and characters used in technical applications,
 see http://support.attachmate.com/techdocs/1184.html and http://vt100.net/charsets/technical.html

* 8087  Numerical Data Coprocessor daughterboard.       REFERENCE: EK-PCNDP-IN-PRE
Daughterboard, to be plugged into the expansion port where the memory expansion card usually sits (J6).
If a memory adapter board is present, it has to be plugged into a connector atop the 8087 copro board.
The 8088 is put into the CPU socket on the coprocessor board.
SOFTWARE: MATH test on 'Design Maturity Diagnostics'; AutoCad, TurboPascal and Fortran.

* Suitable Solutions TURBOW286: 12 Mhz, 68-pin, low power AMD N80L286-12 and WAYLAND/EDSUN EL286-88-10-B ( 80286 to 8088 Processor Signal Converter )
plus DC 7174 or DT 7174 (barely readable). Add-on card, replaces main 8088 cpu (via ribbon cable). Patched V5.03 BOOT ROM labeled 'TBSS1.3 - 3ED4'.

* NEC_V20 (requires modded BOOT ROM because of - at least 2 - hard coded timing loops):
100A:         100B/100+:                       100B+ ALTERNATE RECOMMENDATION (fixes RAM size auto-detection problems when V20 is in place.
Tested on a 30+ year old live machine. Your mileage may vary)

Location Data  Location Data                   Loc.|Data
....     ..    ....     ..  ------------------ 00C6 46 [ increases 'wait for Z80' from approx. 27,5 ms (old value 40) to 30,5 ms ]
....     ..    ....     ..  ------------------ 0303 00 [ disable CHECKSUM ]
043F     64    072F     64 <-----------------> 072F 73 [ increases minimum cycle time from 2600 (64) to 3000 ms (73) ]
067D     20    0B36     20 <-----------------> 0B36 20 [ USE A VALUE OF 20 FOR THE NEC - as in the initial patch! CHANGES CAUSE VFR-ERROR 10 ]
1FFE     2B    3FFE     1B  (BIOS CHECKSUM)
1FFF     70    3FFF     88  (BIOS CHECKSUM)

--------------------------------------------------------------
Meaning of Diagnostics LEDs (from PC100ESV1.PDF found, e.g.,
on ftp://ftp.update.uu.se/pub/rainbow/doc/rainbow-docs/

Internal Diagnostic Messages                               F
Msg Message                               Lights Display   A
No.                                       * = on o = off   T
..........................................- = on or off    A
..........................................1 2 3 4 5 6 7    L
--------------------------------------------------------------
.1  Main Board (Video)                    o * * o * o *   Yes
.2  Main Board* (unsolicited interrupt)   * * * * o * o   Yes
.3  Drive A or B (index)                  o o * o o * *
.4  Drive A or B (motor)                  * * o o o * *
.5  Drive A or B (seek)                   o * o o o * *
.6  Drive A or B (read)                   * o o o o * *
.7  Drive A or B (restore)                o * * o o * *
.8  Drive A or B (step)                   * o * o o * *
.9  System Load incomplete+ (System Load) o o o o o o o
10  Main Board (video, vfr)               * * * o * o *   Yes
11  System Load incomplete+ (Boot Load)   o o o o o o o
12  Drive A or B (not ready)              o o o o o * *
13  Keyboard                              * * o * o * o   Yes
14  Main Board (nvm data)                 * * * * o * *
15  (no msg. 15 in that table)
16  Interrupts off*                       * * * o o o o   Cond.
17  Main Board (video RAM)                * * * o * * o   Yes
18  Main Board (Z80 crc)                  * * * * o o *   Yes
19  Main Board RAM (0-64K)                - - - * * o *   Yes
20  Main Board (unsolicited int., Z80)    * * * o o o *   Yes
21  Drive Not Ready+                      o o o o o o o
22  Remove Card or Diskette               o * * o o o *
23  Non-System Diskette+                  o o o o o o o
24  new memory size = nnnK                o o o o o o o
25  Set Up Defaults stored                o o o o o o o
26  Main Board (RAM arbitration)          * * * o * o o   Yes
27  Main Board (RAM option)               - - - * * o o
28  RX50 controller board                 * * * o o * *
29  Main Board* (Z80 response)            * * * * o o o
30  Main Board (ROM crc, ROM 0)           * * * * * * *   Yes
31  Main Board (ROM crc, ROM 1)           * * * * * * o   Yes
-   Main Board (ROM crc, ROM 2)           * * * o * * *   Yes
33  Main Board (contention)               o o o o o * o   Yes
40  Main Board (printer port)             * o * * o * o
50  Main Board (keyboard port)            o o * * o * o   Yes
60  Main Board (comm port)                o * * * o * o
--------------------------------------------------------------
*   These errors can occur at any time because the circuits
are monitored constantly
+   These messages may occur during power-up if auto boot is
selected

PCB layout
==========

DEC-100 model B
= part no.70-19974-02 according to document EK-RB100-TM_001

PCB # 5416206 / 5016205-01C1:

7-6-5-4 |3-2-1
DIAGNOSTIC-LEDs |J3   | |J2     | |J1    |
|------|----8088|Z80-|--|VIDEO|-|PRINTER|-|SERIAL|----|
|  2 x 64 K             |/KBD.|                  !!!!!|
|  R  A  M              NEC D7201C            |P|!W90!|
|                                             |O|!!!!!|
|   [W6]    ROM 1       INTEL 8088            |W|     |
|           (23-020e5-00)                     |E|     |
|                                             |R|     |
| ...J5..   BOOT ROM 0      ...J4...          =J8     |
|           (23-022e5-00)                             |
| ...J6...                                            |
| [W5]                                                |
|                                                     |
|     INTEL 8251A   ZILOG Z 80A                       |
|                [W18]                                |
| A  4x                74 LS 244                      |
| M  S           [W15]                                |
| 9  -   DEC-DC011     74 LS 245                      |
| 1  R           [W14]                                |
| 2  A                  [W13]                         |
| 8  M   CHARGEN.-                                    |
|        ROM (4K)            ...J7...  | ...J9 = RX50 |
|                                                     |
|-------------PCB# 5416206 / 5016205-01C1-------------|

CONNECTORS ("J"):
    ...J5... ...J4... both: RD51 controller (hard disk)
    ...J5... ...J4... both: EXTENDED COMM. controller

    ...J6... is the MEMORY OPTION connector (52 pin)
    ...J7... is the GRAPHICS OPTION connector (40 pin)
    ...J9... RX50 FLOPPY CONTROLLER (40 pin; REQUIRED)

JUMPERS (labeled "W"):
  W5 + W6 are out when 16K x 8 EPROMS are used
/ W5 + W6 installed => 32 K x 8 EPROMs (pin 27 = A14)

W13, W14, W15, W18 = for manufacturing tests.
=> W13 - W15 affect diagnostic read register (port $0a)
=> W18 pulls DSR to ground and affects 8251A - port $11 (bit 7)

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! DO NOT SHORT JUMPER / CONNECTOR [W90] ON LIVE HARDWARE  !!
!!                                                         !!
!! WARNING:  CIRCUIT DAMAGE could occur if this jumper is  !!
!! set by end users.        See PDF document AA-V523A-TV.  !!
!!                                                         !!
!! W90 connects to pin 2 (Voltage Bias on PWR connector J8)!!
!! and is designed FOR ===> FACTORY TESTS OF THE PSU <===  !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

WIRE CONNECTORS - SEEN ON SCHEMATICS - NOT PRESENT ON DEC-100 B (-A only?):
W16 pulls J2 printer port pin 1 to GND when set (chassis to logical GND).
W17 pulls J1 serial  port pin 1 to GND when set (chassis to logical GND).
****************************************************************************/
#include "emu.h"

#include "cpu/i86/i86.h"
#include "cpu/z80/z80.h"
#include "vtvideo.h"
#include "video/upd7220.h"

#include "machine/wd_fdc.h"
#include "formats/rx50_dsk.h"
#include "imagedev/floppy.h"

#include "imagedev/harddriv.h"
#include "machine/wd2010.h"
#include "machine/corvushd.h"

#include "machine/z80sio.h"
#include "bus/rs232/rs232.h"
#include "imagedev/bitbngr.h"
#include "machine/com8116.h"
#include "bus/rs232/hlemouse.h"
#include "bus/rs232/terminal.h"

#include "machine/i8251.h"
#include "lk201.h"
#include "machine/nvram.h"
#include "machine/ripple_counter.h"
#include "machine/timer.h"
#include "machine/ram.h"

#include "machine/ds1215.h"
#include "emupal.h"
#include "softlist.h"
#include "screen.h"

#include "rainbow.lh" // BEZEL - LAYOUT with LEDs for diag 1-7, keyboard 8-11 and floppy 20-23


namespace {

#define RD51_MAX_HEAD 8
#define RD51_MAX_CYLINDER 1024
#define RD51_SECTORS_PER_TRACK 17

#define RTC_ENABLED
// Tested drivers (from Suitable Solutions distribution disk and Latrobe archive), preferred first -
// File.........Version / author ------------------- YY/YYYY ----- Read only RTC_BASE ---- Platform
// RBCLIK21.COM Author: Vincent Esser. With source.. 4 digits (Y2K)..Y.......$fc000/fe000..100-B (default cfg.)
// CLIKA.COM .. V1.03A (C) 1987 Suitable Solutions.. 2 digits........N (*)...$ed000........100-A
// CLIKCLOK.COM V1.01 (C) 1986,87 Suitable Solutions 2 digits........N (*)...$fc000/fe000..100-B (default   " )
// CLIKF4.COM . V1.0  (C) 1986 Suitable Solutions... 2 digits........N (*)...$f4000........100-B (alternate " )
// (*)   Time or date changes are not persistent in emulation. To prove the setter works, changes are logged.

// (Y2K) DS1315 unit only holds 2 digits, so Vincent Esser's freeware employs a windowing technique.
//       While Suitable's DOS 3.10 accepts dates > 2000, don't take that for granted with software from the 80s.
//  Model B can have an RTC mapped at 0xF4000 instead - ClikClok V1.0 / CLIKF4.COM

// ----------------------------------------------------------------------------------------------
// * MHFU disabled by writing a _sensible_ value to port 0x10C (instead of port 0x0c)
// Note: documentation incorrectly claims that zero must be written to 0x10C.

// * MHFU re-enabled by writing to 0x0c.
// DEC says that MHFU is also re-enabled 'automatically after STI' (when under BIOS control?)

// Schematics show "VERT FREQ INT" (= DC012 output, pin 2) and MHFU ENBL L are evaluated,
//  as well as the power good signal from the PSU (AC_OK). MS_TO_POWER_GOOD is a guess:
#define MS_TO_POWER_GOOD 350
// Reset duration of 108 ms from documentation -
#define RESET_DURATION_MS 108

// Driver uses an IRQ callback from the 8088 -and a counter- to determine if the CPU is alive.
// Counter is reset by writing to 0x10c, or by acknowledging (!) a VBL IRQ within 108 ms.
#define MHFU_IS_ENABLED 1
#define MHFU_COUNT -1
#define MHFU_VALUE -2
#define MHFU_RESET_and_ENABLE   -100
#define MHFU_RESET_and_DISABLE  -200
#define MHFU_RESET              -250

// ----------------------------------------------------------------------------------------------
// NEC 7220 GDC     *************************************

// Indirect Register, port $53, see page 181 of AA-AE36A (PDF):
// (actual values : see comments)
#define GDC_SELECT_WRITE_BUFFER  0x01 // 0xFE
#define GDC_SELECT_PATTERN_MULTIPLIER 0x02 // 0xFD
#define GDC_SELECT_PATTERN       0x04 // 0xFB
#define GDC_SELECT_FG_BG         0x08 // 0xF7
#define GDC_SELECT_ALU_PS        0x10 // 0xEF
#define GDC_SELECT_COLOR_MAP     0x20 // 0xDF
#define GDC_SELECT_MODE_REGISTER 0x40 // 0xBF
#define GDC_SELECT_SCROLL_MAP    0x80 // 0x7F

// MODE REGISTER
#define GDC_MODE_HIGHRES        0x01
#define GDC_MODE_VECTOR         0x02

// ( " ) READBACK OPERATION  (if ENABLE_WRITES = 0):
#define GDC_MODE_ENABLE_WRITES       0x10
#define GDC_MODE_READONLY_SCROLL_MAP 0x20

// ( " )  READBACK OPERATION  (plane select = bit mask in bits 2 + 3 of MODE register):
#define GDC_MODE_READBACK_PLANE_MASK 12
#define GDC_MODE_READBACK_PLANE_00  0x00
#define GDC_MODE_READBACK_PLANE_01  0x04
#define GDC_MODE_READBACK_PLANE_02  0x08
#define GDC_MODE_READBACK_PLANE_03  0x0c

#define GDC_MODE_ENABLE_VSYNC_IRQ 0x40
#define GDC_MODE_ENABLE_VIDEO   0x80

// ALU_PS REGISTER (bits 5 + 4):
#define ALU_PS_MODE_MASK 48
#define REPLACE_MODE    00
#define COMPLEMENT_MODE 16
#define OVERLAY_MODE    32

// ----------------------------------------------------------------------------------------------
#define LK201_TAG   "lk201"
#define FD1793_TAG  "fd1793x"

#define INVALID_DRIVE 255
#define MAX_FLOPPIES 4

// Monitor configurations -> see DIP switches. New: auto-detect color palette (last option).
static constexpr int MONO_MONITOR = 0x01;  // Tetris-M and Pacman-M need this setting (no auto-detection)
static constexpr int COLOR_MONITOR = 0x02; // DEC recommendation. GWBASIC and most old libraries. Superseded by later development
static constexpr int DUAL_MONITOR = 0x03;  // Debugging, AutoCad, 'newer' freeware. Green is missing with unpatched software (for technical reasons)
static constexpr int AUTODETECT_MONITOR = 0x04;  // Snoop palette, then choose best output.

class rainbow_base_state : public driver_device
{
public:
	rainbow_base_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),

		m_inp1(*this, "W13"),
		m_inp2(*this, "W14"),
		m_inp3(*this, "W15"),
		m_inp4(*this, "W18"),
		m_inp5(*this, "DEC_HARD_DISK"), // DO NOT CHANGE ORDER
		m_inp6(*this, "CORVUS_HARD_DISKS"), // DO NOT CHANGE ORDER
		m_inp7(*this, "GRAPHICS_OPTION"),   // DO NOT CHANGE ORDER
		m_inp9(*this, "MONO_MONITOR_TYPE"),
		m_inp10(*this, "J17"),
		m_inp11(*this, "CLIKCLOK"),
		m_inp12(*this, "WATCHDOG"),
		m_inp13(*this, "MONITOR_CONFIGURATION"),

		m_crtc(*this, "vt100_video"),

		m_i8088(*this, "maincpu"),
		m_z80(*this, "subcpu"),
		m_ram(*this, "ram"),

		m_fdc(*this, FD1793_TAG),
		m_floppies(*this, FD1793_TAG ":%u", 0U),
		m_hdc(*this, "hdc"),
		m_corvus_hdc(*this, "corvus"),

		m_mpsc(*this, "mpsc"),
		m_dbrg(*this, "dbrg"),
		m_comm_port(*this, "comm"),

		m_kbd8251(*this, "kbdser"),
		m_lk201(*this, LK201_TAG),

		m_p_ram(*this, "p_ram"),

		m_p_vol_ram(*this, "vol_ram"),
		m_p_nvram(*this, "nvram"),

		m_hgdc(*this, "upd7220"), // GDC

		m_screen2(*this, "screen2"),
		m_palette2(*this, "palette2"), // GDC
		m_video_ram(*this, "vram"),

		m_digits(*this, "digit%u", 0U),
		m_leds(*this, "led%u", 1U),
		m_driveleds(*this, "driveled%u", 0U)
	{
	}

	void rainbow_base(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	TIMER_CALLBACK_MEMBER(command_tick);
	TIMER_CALLBACK_MEMBER(switch_off_tick);

	void rainbow8088_base_map(address_map &map) ATTR_COLD;
	void rainbow8088_base_io(address_map &map) ATTR_COLD;

	uint8_t ext_ram_r(offs_t offset);

	uint8_t read_video_ram_r(offs_t offset);
	void video_interrupt(int state);

	uint8_t diagnostic_r();
	void diagnostic_w(uint8_t data);

	uint8_t comm_control_r();
	void comm_control_w(uint8_t data);

	uint8_t share_z80_r(offs_t offset);
	void share_z80_w(offs_t offset, uint8_t data);

	// 'RD51' MFM CONTROLLER (WD1010) *************************************
	uint8_t hd_status_60_r(); // TRI STATE DATA PORT (R/W)
	void hd_status_60_w(uint8_t data);

	uint8_t hd_status_68_r(); // EXTRA REGISTER 0x68 (R/W 8088)
	void hd_status_68_w(uint8_t data);

	uint8_t hd_status_69_r(); // EXTRA REGISTER 0x69 (R/- 8088)

	void bundle_irq(int state);
	void hdc_bdrq(int state);  // BUFFER DATA REQUEST (FROM WD)
	void hdc_bcr(int state);   // BUFFER COUNTER RESET (FROM WD)

	void hdc_step(int state);
	void hdc_direction(int state);

	void hdc_read_sector(int state);
	void hdc_write_sector(int state);

	int hdc_drive_ready();
	int hdc_write_fault();

	uint8_t corvus_status_r();

	uint8_t i8088_latch_r();
	void i8088_latch_w(uint8_t data);
	uint8_t z80_latch_r();
	void z80_latch_w(uint8_t data);

	void z80_diskdiag_read_w(uint8_t data);
	void z80_diskdiag_write_w(uint8_t data);

	uint8_t z80_generalstat_r();

	uint8_t z80_diskstatus_r();
	void z80_diskcontrol_w(uint8_t data);

	void kbd_tx(int state);
	void kbd_rxready_w(int state);
	void kbd_txready_w(int state);

	uint8_t rtc_reset();
	uint8_t rtc_enable();

	void mpsc_irq(int state);
	void comm_bitrate_w(uint8_t data);
	void printer_bitrate_w(uint8_t data);
	void bitrate_counter_w(uint8_t data);
	void dbrg_fr_w(int state);
	void dbrg_ft_w(int state);

	void GDC_EXTRA_REGISTER_w(offs_t offset, uint8_t data);
	uint8_t GDC_EXTRA_REGISTER_r(offs_t offset);

	uint32_t screen_update_rainbow(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	IRQ_CALLBACK_MEMBER(irq_callback);

	TIMER_DEVICE_CALLBACK_MEMBER(hd_motor_tick);

	static void floppy_formats(format_registration &fr);

	UPD7220_DISPLAY_PIXELS_MEMBER( hgdc_display_pixels );
	uint16_t vram_r(offs_t offset);
	void vram_w(offs_t offset, uint16_t data);
	void GDC_vblank_irq(int state);

	void rainbowz80_io(address_map &map) ATTR_COLD;
	void rainbowz80_mem(address_map &map) ATTR_COLD;
	void upd7220_map(address_map &map) ATTR_COLD;
	enum
	{   // LOWEST PRIORITY
		// Mnemonic - - - - - -  TYPE  ADDRESS - Source
		//                      [1][0]  [1][0] <= Depends on DTR(L) output of keyboard PUSART (on Rainbow-100 B)
		IRQ_8088_MAILBOX = 0, // 27/A7  9C/29C  - [built-in] Interrupt from Z80A
		IRQ_8088_KBD,         // 26/A6  98/298  - [built-in] KEYBOARD Interrupt - 8251A
		IRQ_BDL_INTR_L,       // 25/A5  94/294  - [ext. BUNDLE OPTION] Hard disk or Extended communication IRQ (no DMA)
		IRQ_COMM_PTR_INTR_L,  // 24/A4  90/290  - [built-in 7201] Communication/Printer interrupt
		IRQ_DMAC_INTR_L,      // 23/A3  8C/28C  - [ext. COMM.BOARD only] - external DMA Controller (8237) interrupt
		IRQ_GRF_INTR_L,       // 22/A2  88/288  - [ext. COLOR GRAPHICS]
		IRQ_BDL_INTR_1L,      // 21/A1  84/284  - [ext. COMM.BOARD only]
		IRQ_8088_VBL,         // 20/A0  80/280  - [built-in DC012] - VERT INTR L (= schematics)
		IRQ_8088_NMI          // 02/02  08/08   - [external MEMORY EXTENSION] - PARITY ERROR L
	};  // HIGHEST PRIORITY

	required_ioport m_inp1;
	required_ioport m_inp2;
	required_ioport m_inp3;
	required_ioport m_inp4;
	required_ioport m_inp5;
	required_ioport m_inp6;
	required_ioport m_inp7;
	required_ioport m_inp9;
	required_ioport m_inp10;
	required_ioport m_inp11;
	required_ioport m_inp12;
	required_ioport m_inp13;
	required_device<rainbow_video_device> m_crtc;
	required_device<cpu_device> m_i8088;
	required_device<cpu_device> m_z80;
	required_device<ram_device> m_ram;

	required_device<fd1793_device> m_fdc;
	required_device_array<floppy_connector, 4> m_floppies;
	optional_device<wd2010_device> m_hdc;

	required_device<corvus_hdc_device> m_corvus_hdc;

	required_device<upd7201_device> m_mpsc;
	required_device<com8116_003_device> m_dbrg;
	required_device<rs232_port_device> m_comm_port;

	required_device<i8251_device> m_kbd8251;
	required_device<lk201_device> m_lk201;
	required_shared_ptr<uint8_t> m_p_ram;
	required_shared_ptr<uint8_t> m_p_vol_ram;
	required_shared_ptr<uint8_t> m_p_nvram;

	required_device<upd7220_device> m_hgdc;  // GDC
	required_device<screen_device> m_screen2;
	required_device<palette_device> m_palette2;
	required_shared_ptr<uint16_t> m_video_ram;

	output_finder<2> m_digits;
	output_finder<7> m_leds;
	output_finder<4> m_driveleds;

	void raise_8088_irq(int ref);
	void lower_8088_irq(int ref);

	void update_mpsc_irq();
	int m_mpsc_irq;
	void update_8088_irqs();

	void update_bundle_irq(); // RD51 or COMM.OPTION!
	int do_write_sector();
	void hdc_buffer_counter_reset();
	void hdc_reset();

	harddisk_image_device *rainbow_hdc_file(int ref);

	uint8_t m_gdc_write_buffer[16]; // 16 x 8 bits for CPU, 8 x 16 for GDC
	uint8_t m_gdc_color_map[32];
	uint8_t m_gdc_scroll_buffer[256];

	uint8_t  m_gdc_indirect_register;
	uint8_t  m_gdc_mode_register;
	uint8_t  m_gdc_scroll_index;
	uint8_t  m_gdc_color_map_index;
	uint8_t  m_gdc_write_buffer_index;
	uint8_t  m_gdc_alu_ps_register;
	uint8_t  m_gdc_fg_bg;
	uint8_t  m_vpat, m_patmult, m_patcnt, m_patidx;

	uint16_t m_gdc_write_mask;

	bool m_onboard_video_selected;   // (internal switch, on board video to mono out)
	bool m_screen_blank;

	uint8_t m_monitor_suggested;

	int m_int88;
	int m_intz80;

	bool m_zflip;                   // Z80 alternate memory map with A15 inverted
	bool m_z80_halted;
	int  m_z80_diskcontrol;         // retains values needed for status register

	uint8_t m_printer_bitrate;

	bool m_kbd_tx_ready, m_kbd_rx_ready;
	int m_KBD;

	uint8_t m_diagnostic;

	uint8_t m_z80_private[0x800];     // Z80 private 2K
	uint8_t m_z80_mailbox, m_8088_mailbox;

	void update_kbd_irq();

	int m_present_drive;
	floppy_image_device *m_floppy;

	int m_irq_high;
	uint32_t m_irq_mask;

	int m_bdl_irq;
	int m_hdc_buf_offset;

	bool m_hdc_index_latch;
	bool m_hdc_step_latch;
	int m_hdc_direction;
	bool m_hdc_write_gate;

	bool m_hdc_drive_ready;
	bool m_hdc_write_fault;

	uint8_t m_hdc_buffer[2048];

	bool m_power_good;
	emu_timer   *cmd_timer;
	emu_timer   *switch_off_timer;

	const int vectors[9] = { 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x02 };

	// VIDEO LEVELS:  0 is 100 % output; F is 0 % output. Range of 0...255.
	// LIMITED RANGE levels for 100-A model (valid only for all mono + green out on COLOR MONITOR):
	//const uint8_t A_MONO_GREEN_video_levels[16] = { 255 , 185,  166, 21, 255 , 185,  166, 21, 255 , 185,  166, 21, 255 , 185,  166, 21};

	// FULL RANGE video levels for 100-B model, taken from page 46 of PDF
	const uint8_t video_levels[16] = { 255, 217,  201,186, 171, 156, 140, 125, 110, 97, 79, 66, 54, 31, 18, 0 };

	const int comm_rates[16] = { 50,75,110,134,150,200,300,600,1200,1800,2000,2400,3600,4800,9600,19200 };
};

class rainbow_modela_state : public rainbow_base_state
{
public:
	rainbow_modela_state(const machine_config &mconfig, device_type type, const char *tag) :
		rainbow_base_state(mconfig, type, tag),
		m_rtc(*this, "rtc")
	{
	}

	void rainbow_modela(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;

	void rainbow8088_map(address_map &map) ATTR_COLD;
	void rainbow8088_io(address_map &map) ATTR_COLD;

	void ext_ram_w(offs_t offset, uint8_t data);
	uint8_t rtc_r(offs_t offset);
	void rtc_w(offs_t offset, uint8_t data);
	void irq_hi_w(int state);
	uint8_t system_parameter_r();

	required_device<ds1215_device> m_rtc;
};

class rainbow_modelb_state : public rainbow_base_state
{
public:
	rainbow_modelb_state(const machine_config &mconfig, device_type type, const char *tag) :
		rainbow_base_state(mconfig, type, tag),
		m_rtc(*this, "rtc")
	{
	}

	void rainbow_modelb(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;

	void rainbow8088_map(address_map &map) ATTR_COLD;
	void rainbow8088_io(address_map &map) ATTR_COLD;

	void ext_ram_w(offs_t offset, uint8_t data);
	uint8_t rtc_r(offs_t offset);
	void irq_hi_w(int state);
	uint8_t system_parameter_r();

	required_device<ds1216e_device> m_rtc;
};

// It * should be * OK to RESET the SCROLL_BUFFER and the COLOR_MAP (at least with WELL WRITTEN programs)

// Situation less clear for vector mode (some programs work extensively * before * OPTION_GRFX_RESET

// THIS MACRO * RESETS *  the PATTERN TO DEFAULT.
// NOTE 2: m_patmult  MUST BE LOADED BEFORE !!
#define OPTION_RESET_PATTERNS \
	m_vpat = 0xff;                              \
	if (m_patmult == 0)  m_patmult = 0x01;      \
	if (m_patcnt == 0)   m_patcnt = m_patmult;  \
	if (m_patidx == 0)   m_patidx = 7;


// GDC RESET MACRO - used in  "machine_reset"  & GDC_EXTRA_REGISTER_w   !
#define OPTION_GRFX_RESET                                   \
	lower_8088_irq(IRQ_GRF_INTR_L);                         \
	m_monitor_suggested = m_inp13->read();                  \
	m_gdc_indirect_register = 0;                            \
	m_gdc_color_map_index = 0;                              \
	for (int i = 0; i < 256; i++)                           \
		m_gdc_scroll_buffer[i] = i;                         \
	m_gdc_scroll_index = 0;                                 \
	m_gdc_write_buffer_index = 0;                           \
	m_gdc_write_mask = 0x00;                                \
	m_gdc_alu_ps_register = 0x0F;                           \
	m_gdc_fg_bg = 0xF0;                                     \
	m_gdc_mode_register &= GDC_MODE_VECTOR | GDC_MODE_HIGHRES | GDC_MODE_ENABLE_WRITES | GDC_MODE_READONLY_SCROLL_MAP;\
	m_gdc_mode_register |= GDC_MODE_ENABLE_VIDEO;           \
	logerror("\n** OPTION GRFX. RESET **\n");

UPD7220_DISPLAY_PIXELS_MEMBER( rainbow_base_state::hgdc_display_pixels )
{
	if(m_inp7->read() == 0)
		return;

	const rgb_t *paletteX = m_palette2->palette()->entry_list_raw();

	uint16_t plane0, plane1, plane2, plane3;
	uint8_t pen;

	if (m_onboard_video_selected && (m_inp13->read() != DUAL_MONITOR))
	{
		for (int xi = 0; xi < 16; xi++) // blank screen when VT102 output active (..)
		{
			if (bitmap.cliprect().contains(x + xi, y))
				bitmap.pix(y, x + xi) = 0;
		}
		return; // no output from graphics option
	}

	// ********************* GET BITMAP DATA FOR 4 PLANES ***************************************
	// _READ_ BIT MAP  from 2 or 4 planes (plane 0 is least, plane 3 most significant). See page 42 / 43
	if (m_gdc_mode_register & GDC_MODE_HIGHRES)
	{
		address = ( m_gdc_scroll_buffer[ ((address & 0x7FC0) >> 7) & 0xff ] << 7) |  (address & 0x7F);
		plane0 = m_video_ram[((address & 0x3fff) + 0x0000)];
		plane1 = m_video_ram[((address & 0x3fff) + 0x8000)];
		plane2 = plane3 = 0;
	}
	else
	{
		address = ( m_gdc_scroll_buffer[ ((address & 0x3FC0) >> 7) & 0xff ] << 7) |  (address & 0x7F);
		// MED.RESOLUTION (4 planes, 4 color bits, 16 color map entries / 16 -or 4- MONOCHROME SHADES)
		plane0 = m_video_ram[((address & 0x1fff) + 0x00000)];
		plane1 = m_video_ram[((address & 0x1fff) + 0x08000)];
		plane2 = m_video_ram[((address & 0x1fff) + 0x10000)];
		plane3 = m_video_ram[((address & 0x1fff) + 0x18000)];
	}

	bool mono = (m_monitor_suggested == MONO_MONITOR) ? true : false; // 1 = MONO, 2 = COLOR, 3 = DUAL MONITOR; 4 = AUTO

	for (int xi = 0; xi < 16; xi++)
	{
		pen = BIT(plane0 ,xi) |
			 (BIT(plane1 ,xi) << 1) |
			 (BIT(plane2 ,xi) << 2) |
			 (BIT(plane3 ,xi) << 3);

		if (bitmap.cliprect().contains(x + xi, y))
			bitmap.pix(y, x + xi) = paletteX[mono ? (pen + 16) : pen];
	}
}

void rainbow_base_state::floppy_formats(format_registration &fr)
{
	// this order is important as there is a DS 40track 400KB pc format that is the same size as the SS 80track rx50 format
	fr.add(FLOPPY_RX50IMG_FORMAT);
	fr.add_pc_formats();
}

static void rainbow_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD); // QD means 80 tracks with DD data rate (single or double sided).
	device.option_add("525dd", FLOPPY_525_DD); // mimic a 5.25" PC (40 track) drive. Requires IDrive5.SYS.
	device.option_add("35dd", FLOPPY_35_DD); // mimic 3.5" PC drive (720K, double density). Use Impdrv3.SYS.
	device.option_add("525ssdd", FLOPPY_525_SSDD); // to read a single sided, (160K) PC-DOS 1 disk with MediaMaster
}

void rainbow_base_state::machine_start()
{
	m_power_good = false; // Simulate AC_OK signal from power supply.
	cmd_timer = timer_alloc(FUNC(rainbow_base_state::command_tick), this);
	cmd_timer->adjust(attotime::from_msec(MS_TO_POWER_GOOD));

	switch_off_timer = timer_alloc(FUNC(rainbow_base_state::switch_off_tick), this);
	switch_off_timer->adjust(attotime::from_msec(10));

	m_digits.resolve();
	m_leds.resolve();
	m_driveleds.resolve();

	m_screen_blank = false;

	auto *printer_port = subdevice<rs232_port_device>("printer");
	printer_port->write_dtr(0);
	printer_port->write_rts(0);

	save_item(NAME(m_z80_private));
	save_item(NAME(m_z80_mailbox));
	save_item(NAME(m_8088_mailbox));
	save_item(NAME(m_zflip));
	save_item(NAME(m_printer_bitrate));
	save_item(NAME(m_kbd_tx_ready));
	save_item(NAME(m_kbd_rx_ready));
	save_item(NAME(m_irq_high));
	save_item(NAME(m_irq_mask));
}



void rainbow_base_state::rainbow8088_base_map(address_map &map)
{
	map.unmap_value_high();

	// There is a 2212 (256 x 4 bit) NVRAM from 0xed000 to 0xed0ff (*)
	// shadowed at $ec000 - $ecfff and from $ed100 - $edfff.

	// (*) ED000 - ED0FF is the area the DEC-100-B BIOS accesses and checks

	//  - Specs say that the CPU has direct access to volatile RAM only.
	//    So NVRAM is hidden and loads & saves are triggered within the
	//    'diagnostic_w' handler (similar to real hardware).

	//  - Address bits 8-12 are ignored (-> mirror()).
	map(0xed000, 0xed0ff).ram().share("vol_ram"); //.mirror(0x1f00);
	map(0xed100, 0xed1ff).ram().share("nvram");

	map(0xee000, 0xeffff).ram().share("p_ram");
	map(0xf0000, 0xfffff).rom();
}

void rainbow_modela_state::rainbow8088_map(address_map &map)
{
	rainbow8088_base_map(map);
	map(0x00000, 0xcffff).rw(FUNC(rainbow_modela_state::ext_ram_r), FUNC(rainbow_modela_state::ext_ram_w));

#ifdef RTC_ENABLED
	// *********************************** / DS1315 'PHANTOM CLOCK' IMPLEMENTATION FOR 'DEC-100-A' ***************************************
	map(0xed000, 0xed000).r(FUNC(rainbow_modela_state::rtc_r));
	map(0xed0fe, 0xed0ff).w(FUNC(rainbow_modela_state::rtc_w));
	// *********************************** / DS1315 'PHANTOM CLOCK' IMPLEMENTATION FOR 'DEC-100-A' ***************************************
#endif
}

// DEC-100-B probes until a 'flaky' area is found (BOOT ROM around F400:0E04).
// It is no longer possible to key in the RAM size from within the 100-B BIOS.
void rainbow_modelb_state::rainbow8088_map(address_map &map)
{
	rainbow8088_base_map(map);
	map(0x00000, 0xdffff).rw(FUNC(rainbow_modelb_state::ext_ram_r), FUNC(rainbow_modelb_state::ext_ram_w));

#ifdef RTC_ENABLED
	// *********************************** / DS1315 'PHANTOM CLOCK' IMPLEMENTATION FOR 'DEC-100-B' ***************************************
	// No address space needed ( -> IRQs must be disabled to block ROM accesses during reads ).
	map(0xfc000, 0xfe104).r(FUNC(rainbow_modelb_state::rtc_r));
	// *********************************** / DS1315 'PHANTOM CLOCK' IMPLEMENTATION FOR 'DEC-100-B' ***************************************
#endif
}

void rainbow_base_state::rainbow8088_base_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x1ff);
	map(0x00, 0x00).rw(FUNC(rainbow_base_state::i8088_latch_r), FUNC(rainbow_base_state::i8088_latch_w));
	map(0x02, 0x02).rw(FUNC(rainbow_base_state::comm_control_r), FUNC(rainbow_base_state::comm_control_w)); // Communication status / control register (8088)
	map(0x04, 0x04).w(m_crtc, FUNC(rainbow_video_device::dc011_w));

	map(0x06, 0x06).w(FUNC(rainbow_base_state::comm_bitrate_w));

	map(0x0a, 0x0a).rw(FUNC(rainbow_base_state::diagnostic_r), FUNC(rainbow_base_state::diagnostic_w));
	map(0x0c, 0x0c).select(0x100).w(m_crtc, FUNC(rainbow_video_device::dc012_w));

	map(0x0e, 0x0e).w(FUNC(rainbow_base_state::printer_bitrate_w));

	map(0x10, 0x11).rw(m_kbd8251, FUNC(i8251_device::read), FUNC(i8251_device::write));

	// ===========================================================
	// There are 4 select lines for Option Select 1 to 4
	// Option Select ------------------- Bundle Option Present
	// 1 2 3 4:                          BDL PRES (L):
	// X X o o Communication Option----- X
	// o X o o RD51 hard disk controller X --------- (X = SELECT)
	// ===========================================================
	// 0x20 -> 0x2f ***** EXTENDED COMM. OPTION / Option Select 1.
	// See boot rom @1EA6: 0x27 (<- RESET EXTENDED COMM OPTION  )

	// Corvus B/H harddisk controller (incompatible with EXT.COMM OPTION):
	map(0x20, 0x20).rw(m_corvus_hdc, FUNC(corvus_hdc_device::read), FUNC(corvus_hdc_device::write));
	map(0x21, 0x21).r(FUNC(rainbow_base_state::corvus_status_r));

	// ===========================================================
	// 0x30 -> 0x3f ***** Option Select 3
	// ===========================================================
	// 0x40  COMMUNICATIONS DATA REGISTER (MPSC)
	// 0x41  PRINTER DATA REGISTER (MPSC)
	// 0x42  COMMUNICATIONS CONTROL / STATUS REGISTER (MPSC)
	// 0x43  PRINTER CONTROL / STATUS REGISTER (MPSC)
	// ===========================================================
	// 0x50 - 0x57 ***** COLOR GRAPHICS OPTION:

	// * Color graphics option (NEC upd7220 GDC plus external hw.). See Programmer's Reference AA-AE36A-TV.
	// Either 384 x 240 x 16 or 800 x 240 x 4 colors (out of 4096). 8 x 64 K video RAM.
	// (Write Buffer, Pattern Register/Multiplier, ALU/PS, Color Map, readback and offset/scroll hardware):
	map(0x50, 0x57).rw(FUNC(rainbow_base_state::GDC_EXTRA_REGISTER_r), FUNC(rainbow_base_state::GDC_EXTRA_REGISTER_w));

	// ===========================================================
	// 0x60 -> 0x6f ***** EXTENDED COMM. OPTION / Option Select 2.
	// ===========================================================
	// 0x60 -> 0x6f ***** RD51 HD. CONTROLLER   / Option Select 2.
	map(0x60, 0x67).rw(m_hdc, FUNC(wd2010_device::read), FUNC(wd2010_device::write)).mirror(0x100);
	map(0x68, 0x68).rw(FUNC(rainbow_base_state::hd_status_68_r), FUNC(rainbow_base_state::hd_status_68_w));
	map(0x69, 0x69).r(FUNC(rainbow_base_state::hd_status_69_r));
	// ===========================================================
	// THE RD51 CONTROLLER: WD1010AL - 00 (WDC '83)
	// + 2 K x 8 SRAM (SY2128-4 or Japan 8328) 21-17872-01
	// + 74(L)Sxxx glue logic (drive/head select, buffers etc.)
	// + 10 Mhz Quartz (/2)
	// SERVICE JUMPERS (not to be removed for normal operation):
	//   JUMPER "W1" : bridge between 10 Mhz master clock and board
	//   JUMPER "W2" : bridges SYNC within Read Data Circuit
	//   JUMPER "W3" : bridges 'drive read data' (from hard disk)
	// Later RD51 boards (> '83 week 28 ?) have no jumpers at all.
	// ===========================================================
	// DEC RD TYPE (MByte) CYL ---- HEADS ---- MODEL (typical)
	// DEC RD50 (5 Mbyte): 153 cyl. 4 heads -- ST506
	// DEC RD51(10 Mbyte); 306 cyl. 4 heads -- ST412
	// DEC RD31(20 Mbyte); 615 cyl. 4 heads -- ST225
	// DEC RD52(32 Mbyte); 512 cyl. 8 heads -- Q540  [!]
	// DEC RD32(40 Mbyte); 820 cyl. 6 heads -- ST251 [!]
	// DEC RD53(67 Mbyte); 1024 cyl.8 heads -- 1325  [!]
	// [!] More than 4 heads. Prepare with WUTIL and / or DSKPREP.

	// SIZE RESTRICTIONS
	// * HARDWARE:
	//      WD1010 controller has a built-in limit of 8 heads / 1024 cylinders.
	// * BOOT LOADERS:
	//   - the DEC boot loader (and FDISK from DOS 3.10) initially allowed a maximum hard disc size of 20 MB.
	//   - the custom boot loader that comes with 'WUTIL 3.2' allows 117 MB and 8 surfaces.
	// * SOFTWARE:
	//   - MS-DOS 2 allows a maximum partition size of 16 MB (sizes > 15 MB are incompatible to DOS 3)
	//     [ no more than 4 partitions of 8 MB size on one hard disk possible ]
	//   - MS-DOS 3 - and Concurrent CPM - have a global 32 MB (1024 cylinder) limit
	//   - a CP/M-86-80 partition can have up to 8 MB (all CP/M partitions together must not exceed 10 MB)
	// ===========================================================
	// 0x70 -> 0x7f ***** Option Select 4
	// ===========================================================
	// 0x10c -> (MHFU disable register handled by 0x0c + select())
}

void rainbow_modela_state::rainbow8088_io(address_map &map)
{
	rainbow_base_state::rainbow8088_base_io(map);
	map(0x08, 0x08).r(FUNC(rainbow_modela_state::system_parameter_r));
}

void rainbow_modelb_state::rainbow8088_io(address_map &map)
{
	rainbow_base_state::rainbow8088_base_io(map);
	map(0x08, 0x08).r(FUNC(rainbow_modelb_state::system_parameter_r));
}

void rainbow_base_state::rainbowz80_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(rainbow_base_state::share_z80_r), FUNC(rainbow_base_state::share_z80_w));
}

void rainbow_base_state::rainbowz80_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x00).rw(FUNC(rainbow_base_state::z80_latch_r), FUNC(rainbow_base_state::z80_latch_w));
	map(0x20, 0x20).rw(FUNC(rainbow_base_state::z80_generalstat_r), FUNC(rainbow_base_state::z80_diskdiag_read_w)); // read to port 0x20 used by MS-DOS 2.x diskette loader.
	map(0x21, 0x21).rw(FUNC(rainbow_base_state::z80_generalstat_r), FUNC(rainbow_base_state::z80_diskdiag_write_w));
	map(0x40, 0x40).rw(FUNC(rainbow_base_state::z80_diskstatus_r), FUNC(rainbow_base_state::z80_diskcontrol_w));
	map(0x60, 0x63).rw(m_fdc, FUNC(fd1793_device::read), FUNC(fd1793_device::write));

	// Z80 I/O shadow area > $80
	map(0x80, 0x80).rw(FUNC(rainbow_base_state::z80_latch_r), FUNC(rainbow_base_state::z80_latch_w));
	map(0xA0, 0xA0).rw(FUNC(rainbow_base_state::z80_generalstat_r), FUNC(rainbow_base_state::z80_diskdiag_read_w)); // read to port 0x20 used by MS-DOS 2.x diskette loader.
	map(0xA1, 0xA1).rw(FUNC(rainbow_base_state::z80_generalstat_r), FUNC(rainbow_base_state::z80_diskdiag_write_w));
	map(0xC0, 0xC0).rw(FUNC(rainbow_base_state::z80_diskstatus_r), FUNC(rainbow_base_state::z80_diskcontrol_w));
	map(0xE0, 0xE3).rw(m_fdc, FUNC(fd1793_device::read), FUNC(fd1793_device::write));
}

/* Input ports */

/* DIP switches */
static INPUT_PORTS_START(rainbow100b_in)

	PORT_START("MONO_MONITOR_TYPE")
	PORT_DIPNAME(0x03, 0x03, "MONO MONITOR TYPE")
	PORT_DIPSETTING(0x01, "WHITE (VR201-A)")
	PORT_DIPSETTING(0x02, "GREEN (VR201-B)")
	PORT_DIPSETTING(0x03, "AMBER (VR201-C)")

	// FLOPPY, BUNDLE, GRAPHICS affect 'system_parameter_r':

	// EXT.COMM.card -or- RD51 HD. controller (marketed later).
	PORT_START("DEC_HARD_DISK") // BUNDLE_OPTION
	PORT_DIPNAME(0x01, 0x00, "DEC HARD DISK (#1)") PORT_TOGGLE
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x01, DEF_STR(On))

	PORT_START("CORVUS_HARD_DISKS")
	PORT_DIPNAME(0x01, 0x00, "CORVUS HARD DISKS (#2 to #5)") PORT_TOGGLE
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x01, DEF_STR(On))

	PORT_START("CLIKCLOK") // DS1315 RTC
	PORT_DIPNAME(0x01, 0x00, "REAL TIME CLOCK (CLIKCLOK)") PORT_TOGGLE
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x01, DEF_STR(On))

	PORT_START("GRAPHICS_OPTION") // GDC
	PORT_DIPNAME(0x01, 0x00, "GRAPHICS OPTION") PORT_TOGGLE
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x01, DEF_STR(On))

	// W13 - W18 are used for factory tests and affect the boot process -
	PORT_START("W13")
	PORT_DIPNAME(0x02, 0x02, "W13 (FACTORY TEST A, LEAVE OFF)") PORT_TOGGLE
	PORT_DIPSETTING(0x02, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))

	PORT_START("W14")
	PORT_DIPNAME(0x04, 0x04, "W14 (FACTORY TEST B, LEAVE OFF)") PORT_TOGGLE
	PORT_DIPSETTING(0x04, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_START("W15")
	PORT_DIPNAME(0x08, 0x08, "W15 (FACTORY TEST C, LEAVE OFF)") PORT_TOGGLE
	PORT_DIPSETTING(0x08, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))

	PORT_START("W18") // DSR = 1 when switch is OFF - see i8251.c
	PORT_DIPNAME(0x01, 0x00, "W18 (FACTORY TEST D, LEAVE OFF) (8251A: DSR)") PORT_TOGGLE
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x01, DEF_STR(On))
	PORT_WRITE_LINE_DEVICE_MEMBER("kbdser", FUNC(i8251_device::write_dsr))

	// J17 jumper on FDC controller board shifts drive select (experimental) -
	PORT_START("J17")
	PORT_DIPNAME(0x02, 0x00, "J17 DRIVE SELECT (A => C and B => D)") PORT_TOGGLE
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x02, DEF_STR(On))

	PORT_START("WATCHDOG")
	PORT_DIPNAME(0x01, 0x00, "WATCHDOG ENABLED (MHFU)") PORT_TOGGLE
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x01, DEF_STR(On))

	PORT_START("MONITOR_CONFIGURATION") // GDC
	PORT_DIPNAME(0x0F, 0x04, "MONITOR CONFIGURATION")
	PORT_DIPSETTING(0x04, "AUTODETECT")
	PORT_DIPSETTING(0x01, "MONO ONLY / 4 to 16 monochrome shades (single VR-201)")
	PORT_DIPSETTING(0x02, "COLOR ONLY (single VR-241 with BCC-17 cable)")
	PORT_DIPSETTING(0x03, "DUAL MONITOR (SCREEN 1: TEXT;  SCREEN 2: R-G-B)")
INPUT_PORTS_END

void rainbow_base_state::machine_reset()
{
	// 'F3' (in partial emulation) here replaces 'CTRL-SETUP' (soft reboot on an original Rainbow)
	// FIXME: BIOS reports error 19 when CTRL-SETUP is pressed (Z80 or flags aren't fully reset then?)
	popmessage("Reset");

	m_crtc->MHFU(MHFU_RESET_and_DISABLE);

	//  *********** HARD DISK CONTROLLERS...
	address_space &io = m_i8088->space(AS_IO);
	if (m_inp5->read() == 0x01) // ...PRESENT?
	{
		// Install 8088 read / write handler
		io.unmap_readwrite(0x60, 0x60);
		io.install_read_handler(0x60, 0x60, read8smo_delegate(*this, FUNC(rainbow_base_state::hd_status_60_r)));
		io.install_write_handler(0x60, 0x60, write8smo_delegate(*this, FUNC(rainbow_base_state::hd_status_60_w)));

		hdc_reset();
		m_hdc_drive_ready = true;
		m_hdc_write_fault = false;

		harddisk_image_device *local_hard_disk;
		local_hard_disk = rainbow_hdc_file(0); // one hard disk for now.

		m_leds[0] = 0;
		switch_off_timer->adjust(attotime::from_msec(500));

		if (local_hard_disk)
		{
			const auto &info = local_hard_disk->get_info();
			m_leds[0] = 1;

			uint32_t max_sector = (info.cylinders) * (info.heads) * (info.sectors);
			popmessage("DEC %u (%3.2f) MB HARD DISK MOUNTED.\nGEOMETRY: %d HEADS (1..%d ARE OK).\n%d CYLINDERS (151 to %d ARE OK).\n%d SECTORS / TRACK (up to %d ARE OK). \n%d BYTES / SECTOR (128 to 1024 ARE OK).\n",
					   max_sector * info.sectorbytes / 1000000,
					   (float)max_sector * (float)info.sectorbytes / 1048576.0f,
					   info.heads, RD51_MAX_HEAD,
					   info.cylinders, RD51_MAX_CYLINDER,
					   info.sectors, RD51_SECTORS_PER_TRACK,
					   info.sectorbytes);
		}
	}

	if (m_inp6->read() == 0x00) // Unmap port if Corvus not present
			io.unmap_readwrite(0x20, 0x20);

	// *********** FLOPPY DISK CONTROLLER [ NOT OPTIONAL ]
	m_present_drive = INVALID_DRIVE;
	m_fdc->reset();
	m_fdc->set_floppy(nullptr);
	m_fdc->dden_w(0);

	// *********** NEC 7220 DISPLAY CONTROLLER [ OPTIONAL ]
	OPTION_GRFX_RESET
	OPTION_RESET_PATTERNS

	for (int i = 0; i < 32; i++)
		m_gdc_color_map[i] = 0x00;
	m_gdc_color_map_index = 0;
	// *********** Z80

	m_z80->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
	m_z80_halted = true;

	m_zflip = true; // ZRESET high on startup
	m_diagnostic = 0;   // DIAGNOSTIC_R/W registers (shouldn't it be 1?)

	m_intz80 = false;
	m_int88 = false;

	// *********** SERIAL COMM. (7201)
	m_mpsc->reset();
	m_mpsc_irq = 0;
	m_printer_bitrate = 0;

	// *********** KEYBOARD + IRQ
	m_kbd_tx_ready = m_kbd_rx_ready = false;
	m_kbd8251->write_cts(0);
	m_KBD = 0;

	m_irq_high = 0;
	m_irq_mask = 0;

	// RESET RED LEDs
	m_leds[0] = 1;
	m_leds[1] = 1;
	m_leds[2] = 1;
	m_leds[3] = 1;
	m_leds[4] = 1;
	m_leds[5] = 1;
	m_leds[6] = 1;

	// GREEN KEYBOARD LEDs (see dec/lk201.cpp)
}

void rainbow_modela_state::machine_reset()
{
	rainbow_base_state::machine_reset();
	logerror("*** RAINBOW A MODEL ASSUMED (64 - 832 K RAM).\n");
}

void rainbow_modelb_state::machine_reset()
{
	rainbow_base_state::machine_reset();
	logerror("*** RAINBOW B MODEL ASSUMED (128 - 896 K RAM)\n");
}

// Simulate AC_OK signal (power good) and RESET after ~ 108 ms.
TIMER_CALLBACK_MEMBER(rainbow_base_state::command_tick)
{
	if (m_power_good == false)
	{
		m_power_good = true;
		logerror("**** POWER GOOD ****\n");
	}
	else
	{
		logerror("**** WATCHDOG: CPU RESET ****\n");
		m_i8088->reset(); // gives 'ERROR_16 - INTERRUPTS OFF' (indicates hardware failure or software bug).
	}
}

TIMER_CALLBACK_MEMBER(rainbow_base_state::switch_off_tick)
{
	m_driveleds[0] = 0; // DRIVE 0 (A)
	m_driveleds[1] = 0; // DRIVE 1 (B)
	m_driveleds[2] = 0; // DRIVE 2 (C)
	m_driveleds[3] = 0; // DRIVE 3 (D)

	m_leds[0] = 1;  // 1 = OFF (One of the CPU LEDs as drive LED for DEC hard disk)
	m_leds[1] = 1;  // 1 = OFF (One of the CPU LEDs as drive LED for Corvus HD)
}

uint32_t rainbow_base_state::screen_update_rainbow(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	static int old_monitor;

	if ((m_monitor_suggested < 1) || (m_monitor_suggested > 3))
			m_monitor_suggested = COLOR_MONITOR;

	if (m_monitor_suggested != old_monitor)
	{
		old_monitor = m_monitor_suggested;
		if (m_monitor_suggested == 1)
			popmessage("MONOCHROME MONITOR");
		if (m_monitor_suggested == 2)
			popmessage("COLOR MONITOR");
		if (m_monitor_suggested == 3)
			popmessage("DUAL MONITOR");
	}

	int palette_selected;
	if (m_onboard_video_selected && (m_monitor_suggested == COLOR_MONITOR))
		 palette_selected = 2; // Color monitor; green text
	else
		 palette_selected = m_inp9->read();

	m_crtc->palette_select(palette_selected);
	m_crtc->video_update(bitmap, cliprect);

	if (m_screen_blank || ((!m_onboard_video_selected) && (m_inp13->read() != DUAL_MONITOR))) // dual monitor: never blank all
		m_crtc->video_blanking(bitmap, cliprect);
	else
		m_crtc->video_update(bitmap, cliprect);
	return 0;
}

// Interrupt handling and arbitration.  See 3.1.3.8 OF PC-100 spec.
void rainbow_base_state::update_8088_irqs()
{
	if (m_irq_mask != 0)
	{
		for (int i = IRQ_8088_VBL; i >= 0; i--)
		{
			if (m_irq_mask & (1 << i))
			{
				m_i8088->set_input_line_and_vector(INPUT_LINE_INT0, ASSERT_LINE, vectors[i] | m_irq_high); // I8088
				break;
			}
		}
	}
	else
	{
		m_i8088->set_input_line(INPUT_LINE_INT0, CLEAR_LINE);
	}
}


void rainbow_base_state::raise_8088_irq(int ref)
{
	m_irq_mask |= (1 << ref);
	update_8088_irqs();
}

void rainbow_base_state::lower_8088_irq(int ref)
{
	m_irq_mask &= ~(1 << ref);
	update_8088_irqs();
}


// IRQ service for 7201 (commm / printer)
void rainbow_base_state::update_mpsc_irq()
{
	if (m_mpsc_irq == 0)
		lower_8088_irq(IRQ_COMM_PTR_INTR_L);
	else
		raise_8088_irq(IRQ_COMM_PTR_INTR_L);
}

void rainbow_base_state::mpsc_irq(int state)
{
	m_mpsc_irq = state;
	update_mpsc_irq();
}

// PORT 0x06 : Communication bit rates (see page 21 of PC 100 SPEC)
void rainbow_base_state::comm_bitrate_w(uint8_t data)
{
	m_dbrg->str_w(data & 0x0f);  // PDF is wrong, low nibble is RECEIVE clock (verified in SETUP).
	logerror("\n(COMM.) receive bitrate = %d ($%02x)\n", comm_rates[data & 0x0f] , data & 0x0f);

	m_dbrg->stt_w( ((data & 0xf0) >> 4) );
	logerror("(COMM.) transmit bitrate = %d ($%02x)\n", comm_rates[((data & 0xf0) >> 4)] ,(data & 0xf0) >> 4);
}

// PORT 0x0e : Printer bit rates
void rainbow_base_state::printer_bitrate_w(uint8_t data)
{
	m_printer_bitrate = data & 7;
	// bits 0 - 2 = 0: nominally 75 bps, actually 75.35 bps
	// bits 0 - 2 = 1: nominally 150 bps, actually 150.7 bps
	// bits 0 - 2 = 2: nominally 300 bps, actually 301.4 bps
	// bits 0 - 2 = 3: nominally 600 bps, actually 602.8 bps
	// bits 0 - 2 = 4: nominally 1200 bps, actually 1205.6 bps
	// bits 0 - 2 = 5: nominally 2400 bps, actually 2411.2 bps
	// bits 0 - 2 = 6: nominally 4800 bps, actually 4822.4 bps (keyboard is tied to this rate)
	// bits 0 - 2 = 7: nominally 9600 bps, actually 9644.8 bps
	// TX and RX rate cannot be programmed independently.
	logerror("\n(PRINTER) receive = transmit bitrate: %d ($%02x)", 9600 / ( 1 << (7 - (data & 7))) , data & 7);

	// "bit 3 controls the communications port clock (RxC,TxC). External clock when 1, internal when 0"
	logerror(" - CLOCK (0 = internal): %02x", data & 8);
}

void rainbow_base_state::dbrg_fr_w(int state)
{
	m_mpsc->rxca_w(state);
}

void rainbow_base_state::dbrg_ft_w(int state)
{
	m_mpsc->txca_w(state);
}

void rainbow_base_state::bitrate_counter_w(uint8_t data)
{
	bool prt_rxtxc = BIT(data, 7 - m_printer_bitrate);
	bool kbd_rxtxc = BIT(data, 1);

	m_mpsc->rxcb_w(prt_rxtxc);
	m_mpsc->txcb_w(prt_rxtxc);

	m_kbd8251->write_rxc(kbd_rxtxc);
	m_kbd8251->write_txc(kbd_rxtxc);
}

// Only Z80 * private SRAM * is wait state free
// (= fast enough to allow proper I/O to the floppy)

// Shared memory is contended by refresh, concurrent
//    8088 accesses and arbitration logic (DMA).
uint8_t rainbow_base_state::share_z80_r(offs_t offset)
{
	if (m_zflip)
	{
		if (offset < 0x8000)
		{
			return m_ram->read(offset + 0x8000);
		}
		else if (offset < 0x8800)
		{
			return m_z80_private[offset & 0x7ff]; // SRAM
		}

		return m_ram->read(offset ^ 0x8000);
	}
	else
	{
		if (offset < 0x800)
		{
			return m_z80_private[offset]; // SRAM
		}

		return m_ram->read(offset);
	}
}

void rainbow_base_state::share_z80_w(offs_t offset, uint8_t data)
{
	if (m_zflip)
	{
		if (offset < 0x8000)
		{
			m_ram->write(offset + 0x8000, data);
			return; // [!]
		}
		else if (offset < 0x8800)
		{
			m_z80_private[offset & 0x7ff] = data; // SRAM
			return; // [!]
		}

		m_ram->write(offset ^ 0x8000, data);
	}
	else
	{
		if (offset < 0x800)
			m_z80_private[offset] = data; // SRAM
		else
			m_ram->write(offset, data);
	}
	return;
}

uint8_t rainbow_base_state::ext_ram_r(offs_t offset)
{
	if (offset < m_ram->size())
		return m_ram->read(offset);
	return 0;
}

void rainbow_modela_state::ext_ram_w(offs_t offset, uint8_t data)
{
	if (offset < m_ram->size())
		m_ram->write(offset, data);
}

// NMI logic (parity test)
void rainbow_modelb_state::ext_ram_w(offs_t offset, uint8_t data)
{
	if (offset < m_ram->size())
		m_ram->write(offset, data);

	if (m_diagnostic & 0x08)
		if (offset >= 0x10000)
			m_i8088->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}

// ------------------------ClikClok (for 100-A; DS1315) ------------------------------------------
// Version for 100-A plugs into NVRAM chip socket. There is a socket on the ClikClok for the NVRAM

// Requires a short program from the Suitable Solutions ClikClok distribution disk (CLIKA.COM)
// - also needed to set time/date (*).                   Reads $ed000, writes ed0fe/ed0ff.
void rainbow_modela_state::rtc_w(offs_t offset, uint8_t data)
{
	if (m_inp11->read() == 0x01) // if enabled...
	{
		if (m_rtc->ceo_r())
		{
			m_rtc->write(offset & 0x01);

			return;
		}

		m_rtc->write(offset & 0x01);
	}

	m_p_vol_ram[offset] = data;  // Poke value into VOL_RAM.
}

// ------------------------ClikClok (for 100-B; DS1315)  ------------------------------------------------
// Add-on hardware, occupies one of the EPROM sockets of the 100-B. TODO: check address decoders on board
// Requires CLIKCLOK.COM or RBCLIK21.COM (freeware from Latrobe).                       Uses FC000/FE000.
uint8_t rainbow_modela_state::rtc_r(offs_t offset)
{
	if (m_inp11->read() == 0x01) // if enabled...
	{
		if (offset == 0x00) // read time/date from 0xED000 (ClikClok for 100-A)
		{
			if (m_rtc->ceo_r())
				return m_rtc->read();
			 else
				m_rtc->read();
		}
	}

	return m_p_vol_ram[offset];  // return volatile RAM
}

uint8_t rainbow_modelb_state::rtc_r(offs_t offset)
{
	if (m_inp11->read() == 0x01) // if enabled...
	{
		if (m_rtc->ceo_r())
			return m_rtc->read(offset);
		else
			m_rtc->read(offset);
	}

	uint8_t *rom = memregion("maincpu")->base();
	return rom[0xfc000 + offset];  // return ROM
}
// ------------------------/ ClikClok (for model B; DS1315)  -------------------------------


// --------------------------------- Corvus (B/H)  -----------------------------------------
// PORT 0x21 : Corvus status register (ready / direction)
uint8_t rainbow_base_state::corvus_status_r()
{
	if (m_inp6->read() == 0) // Corvus controller
	{
		popmessage("Corvus controller invoked - but switched OFF.\nCheck DIP and perform a reset.\n\nIncompatible software also triggers this warning (illegal access to port $21)");
		return 0;
	}
	else
	{
		m_leds[1] = 0;
		switch_off_timer->adjust(attotime::from_msec(500));

		uint8_t status = m_corvus_hdc->status_r();
		uint8_t data = BIT(status, 7); // 0x80 BUSY (Set = Busy, Clear = Ready)
		data |= BIT(status, 6) << 1; // 0x40 DIR. (Controller -> Host, or Host->Controller)
		return data;
	}
}
// ---------------------------------/ Corvus (B/H)  ----------------------------------------


// ---------------------------- RD51 HARD DISK CONTROLLER ----------------------------------
static const int SECTOR_SIZES[4] = { 256, 512, 1024, 128 };

void rainbow_base_state::hdc_reset()
{
//  logerror(">> HARD DISC CONTROLLER RESET <<\n");
	m_hdc->reset();

	m_bdl_irq = 0;
	update_bundle_irq(); // reset INTRQ

	m_hdc_buf_offset = 0;
	m_hdc_direction = 0;

	m_hdc->buffer_ready(false);
	m_hdc_write_gate = false;

	m_hdc_step_latch = false;
	m_hdc_index_latch = false;
}

// Return 'hard_disk_file' object for harddisk 1 (fixed).
// < nullptr if geometry is insane or other errors occured >
harddisk_image_device *rainbow_base_state::rainbow_hdc_file(int drv)
{
	m_hdc_drive_ready = false;

	if (m_inp5->read() != 0x01) // ...PRESENT?
		return nullptr;

	if (drv != 0)
		return nullptr;

	harddisk_image_device *img = dynamic_cast<harddisk_image_device *>(subdevice("decharddisk1"));

	if (!img)
		return nullptr;

	if (!img->exists())
		return nullptr;

	const auto &info = img->get_info();

	// MFM ALLOWS UP TO 17 SECTORS / TRACK.
	// CYLINDERS: 151 (~ 5 MB) to 1024 (max. cylinders on WD1010 controller)
	if (((info.sectors <= RD51_SECTORS_PER_TRACK)) &&
		((info.heads >= 1) && (info.heads <= RD51_MAX_HEAD)) &&            // HEADS WITHIN 1...8
		((info.cylinders > 150) && (info.cylinders <= RD51_MAX_CYLINDER)))
	{
		m_hdc_drive_ready = true;
		return img;  // HAS SANE GEOMETRY
	}
	else
	{
		uint32_t max_sector = info.cylinders * info.heads * info.sectors;
		popmessage("DEC %u (%3.2f) MB HARD DISK REJECTED.\nGEOMETRY: %d HEADS (1..%d ARE OK).\n%d CYLINDERS (151 to %d ARE OK).\n%d SECTORS / TRACK (up to %d ARE OK). \n%d BYTES / SECTOR (128 to 1024 ARE OK).\n",
					max_sector * info.sectorbytes / 1000000,
					(float)max_sector * (float)info.sectorbytes / 1048576.0f,
					info.heads, RD51_MAX_HEAD,
					info.cylinders, RD51_MAX_CYLINDER,
					info.sectors, RD51_SECTORS_PER_TRACK,
					info.sectorbytes);
		logerror("<<< === HARD DISK IMAGE REJECTED = (invalid geometry) === >>>\n");
		return nullptr;
	}
}

// LBA sector from CHS
static uint32_t get_and_print_lbasector(device_t *device, const hard_disk_file::info &info, uint16_t cylinder, uint8_t head, uint8_t sector_number)
{
	// LBA_ADDRESS = (C * HEADS + H) * NUMBER_SECTORS + (S - 1)
	uint32_t lbasector = (double)cylinder * info.heads; // LBA : ( x 4 )
	lbasector += head;
	lbasector *= info.sectors;   // LBA : ( x 16 )
	lbasector += (sector_number - 1); // + (sector number - 1)

//  device->logerror(" CYLINDER %u - HEAD %u - SECTOR NUMBER %u (LBA-SECTOR %u) ", cylinder, head, sector_number, lbasector);
	return lbasector;
}

// READ SECTOR (on BCS 1 -> 0 transition)
void rainbow_base_state::hdc_read_sector(int state)
{
	static int last_state;
	int read_status = 1;

	if (!m_hdc_write_gate) // do not read when WRITE GATE is on
	{
		uint8_t sdh = (m_hdc->read(0x06));
		int drv = (sdh & (8 + 16)) >> 3; // get DRIVE from SDH register

		if ((state == 0) && (last_state == 1) && (drv == 0))
		{
			read_status = 2; //          logerror("\nTRYING TO READ");
			m_leds[0] = 0;
			switch_off_timer->adjust(attotime::from_msec(500));

			int hi = (m_hdc->read(0x05)) & 0x07;
			uint16_t cylinder = (m_hdc->read(0x04)) | (hi << 8);
			uint8_t sector_number = m_hdc->read(0x03);

			harddisk_image_device *local_hard_disk = rainbow_hdc_file(0); // one hard disk for now.

			if (local_hard_disk)
			{
				read_status = 3;

				const auto &info = local_hard_disk->get_info();
				read_status = 4;
				m_leds[0] = 1;

				// Pointer to info + C + H + S
				uint32_t lbasector = get_and_print_lbasector(this, info, cylinder, sdh & 0x07, sector_number);

				if ((cylinder <= info.cylinders) &&                          // filter invalid ranges
					(SECTOR_SIZES[(sdh >> 5) & 0x03] == info.sectorbytes)    // may not vary in image!
					)
				{
					read_status = 5;
					if (local_hard_disk->read(lbasector, m_hdc_buffer)) // accepts LBA sector (uint32_t) !
						read_status = 0; //  logerror("...success!\n");
				}
				m_hdc_buf_offset = 0;
				m_hdc->buffer_ready(true);
			} // if valid  (..)

			if (read_status != 0)
			{
				logerror("...** READ FAILED WITH STATUS %u ** (CYLINDER %u - HEAD %u - SECTOR # %u - SECTOR_SIZE %u ) ***\n",
					read_status, cylinder, sdh & 0x07, sector_number, SECTOR_SIZES[(sdh >> 5) & 0x03]
				);
			}

		} //   (on BCS 1 -> 0)

	} // do not read when WRITE GATE is on

	last_state = state;
}


// WRITE SECTOR
// ...IF WRITE_GATE (WG) TRANSITS FROM 1 -> 0

// NO PROVISIONS for  sector sizes != 512 or MULTIPLE DRIVES (> 0) !!!
void rainbow_base_state::hdc_write_sector(int state)
{
	int success = 0;
	static int wg_last;

	if (state == 0)
		m_hdc_write_gate = false;
	else
		m_hdc_write_gate = true;

	int drv = ((m_hdc->read(0x06)) & (8 + 16)) >> 3; // get DRIVE from SDH register

	if (state == 0 && wg_last == 1 && drv == 0)  // Check correct state transition and DRIVE 0 ....
	{
		m_leds[0] = 0;  // (1 = OFF ) =HARD DISK ACTIVITY =
		switch_off_timer->adjust(attotime::from_msec(500));

		if (rainbow_hdc_file(0) != nullptr)
		{
			success = do_write_sector();
			if (success < 88)
				logerror("! SECTOR WRITE (or FORMAT) FAULT !  ERROR CODE %i.\n", success);

			m_hdc_buf_offset = 0;
			m_hdc->buffer_ready(false);
		}

		// CHD WRITE FAILURES  or  UNMOUNTED HARDDSIK TRIGGER A PERMANENT ERROR.
		if (success < 50)
			m_hdc_write_fault = true; // reset only by HDC RESET!
	}

	wg_last = state;  // remember state
}


// Initiated by 'hdc_write_sector' (below)
// - in turn invoked by a WG: 1 -> 0 transit.
// STATUS CODES:
//   0 = DEFAULT ERROR (no HARD DISK FILE ?)
//   10 = CHD WRITE FAILURE (?)

//  50 = SANITY CHECK FAILED (cylinder limit / <> 512 sectors?)

//  88 = (LOW LEVEL) WRITE/FORMAT (sector_count != 1 IGNORED)
//  99 = SUCCESS : SECTOR WRITTEN

// * RELIES * ON THE FACT THAT THERE WILL BE NO MULTI SECTOR TRANSFERS (!)
int rainbow_base_state::do_write_sector()
{
	int feedback = 0; // no error
	m_leds[0] = 0; // ON
	switch_off_timer->adjust(attotime::from_msec(500));

	harddisk_image_device *local_hard_disk = rainbow_hdc_file(0); // one hard disk for now.

	if (local_hard_disk)
	{
		const auto &info = local_hard_disk->get_info();
		feedback = 10;
		m_leds[0] = 1; // OFF

		uint8_t sdh = (m_hdc->read(0x06));

		int hi = (m_hdc->read(0x05)) & 0x07;
		uint16_t cylinder = (m_hdc->read(0x04)) | (hi << 8);

		int sector_number = m_hdc->read(0x03);
		int sector_count = m_hdc->read(0x02); // (1 = single sector)

		if (!(cylinder <= info.cylinders &&                     // filter invalid cylinders
			  SECTOR_SIZES[(sdh >> 5) & 0x03] == info.sectorbytes // 512, may not vary
			  ))
		{
			logerror("...*** SANITY CHECK FAILED (CYLINDER %u vs. info.cylinders %u - - SECTOR_SIZE %u vs. info.sectorbytes %u) ***\n",
					 cylinder, info.cylinders, SECTOR_SIZES[(sdh >> 5) & 0x03], info.sectorbytes);
			return 50;
		}
		// Pointer to info + C + H + S
		uint32_t lbasector = get_and_print_lbasector(this, info, cylinder, sdh & 0x07, sector_number);

		if (sector_count != 1) // ignore all SECTOR_COUNTS != 1
			return 88; // logerror(" - ** IGNORED (SECTOR_COUNT !=1) **\n");

		if (local_hard_disk->write(lbasector, m_hdc_buffer))  // accepts LBA sector (uint32_t) !
			feedback = 99; // success
		else
			logerror("...FAILURE **** \n");

	} // IF hard disk present
	return feedback;
}


uint8_t rainbow_base_state::hd_status_60_r()
{
	int data = m_hdc_buffer[m_hdc_buf_offset];
	//logerror("HARD DISK DISK BUFFER: READ offset %04x | data = %02x\n", m_hdc_buf_offset, data); // ! DO NOT CHANGE ORDER !

	m_hdc_buf_offset += 1;
	if (m_hdc_buf_offset >= 1024) // 1 K enforced by controller
	{
		m_hdc_buf_offset = 0;
		m_hdc->buffer_ready(true);
	}
	return data;
}

void rainbow_base_state::hd_status_60_w(uint8_t data)
{
	//logerror("HARD DISK BUFFER: WRITE offset %04x | data = %02x\n", m_hdc_buf_offset, data);

	m_hdc_buffer[m_hdc_buf_offset] = data;
	m_hdc_buf_offset += 1;

	if (m_hdc_buf_offset >= 1024) // 1 K enforced by controller
	{
		m_hdc_buf_offset = 0;
		m_hdc->buffer_ready(true);
	}
}


// Secondary Command / Status Registers(68H) is...
//   (A) a write - only register for commands
//   (B) a read - only register for status signals
// Holds the status of the following signals:
// - 3 hard-wired controller module identification bits.
// - signals from the WD1010 chip,
// - disk drive(latched status signals)
uint8_t rainbow_base_state::hd_status_68_r()
{
	// (*) Bits 5-7 : HARD WIRED IDENTIFICATION BITS, bits 5+7 = 1 and bit 6 = 0  (= 101 f?r RD51 module)
	int data = 0xe0; // 111 gives DRIVE NOT READY (when W is pressed on boot screen)
	if (m_inp5->read() == 0x01 && rainbow_hdc_file(0) != nullptr)
		data = 0xa0; // A0 : OK, DRIVE IS READY (!)

	int my_offset = 0x07;
	int stat = m_hdc->read(my_offset);
//  logerror("(x68) WD1010 register %04x (STATUS) read, result : %04x\n", my_offset, stat);

	// NOTE: SEEK COMPLETE IS CURRENTLY HARD WIRED / NOT FULLY EMULATED -
	// Bit 4 : SEEK COMPLETE: This status bit indicates that the disk drive positioned the R/W heads over the desired track on the disk surface.

	// (ALT.TEXT): "Seek Complete - When this signal from the disk drive goes low(0), it indicates that the R /W heads settled on the correct track.
	// Writing is inhibited until this signal goes low(0).  Seek complete is high(1) during normal seek operation.
	if (stat & 16) // SEEK COMPLETE (bit 4)?
		data |= 16;

	// Bit 3 : DIRECTION : This bit indicates the direction the read/write heads in the disk
	//                     drive will move when the WD1010 chip issues step pulse(s). When high(1), the R / W heads will move toward the spindle.
	//                     When low (0), the heads will move away from the spindle, towards track O.
	if (m_hdc_direction)
		data |= 8;

	// Bit 2 :  LATCHED STEP PULSE: This status bit from the step pulse latch indicates if the WD1010
	//              chip issued a step pulse since the last time the 8088 processor cleared the step pulse latch.
	if (m_hdc_step_latch)
		data |= 4;

	// Bit 1 :  LATCHED INDEX : This status bit from the index latch indicates if the disk drive
	//                  encountered an index mark since the last time the 8088 processor cleared the index latch.
	if (m_hdc_index_latch)
		data |= 2;

	// Bit 0 :  CTRL BUSY : indicates that the WD 1010 chip is accessing the sector buffer. When this bit is set,
	//          the 8088 cannot access the WD 1010 registers.
	if (stat & 128) // BUSY (bit 7)?
		data |= 1;

	return data;
}


// 68 (WRITE): Secondary Command Registers (68H) - -  "write-only register for commands"
// - see TABLE 4.8 (4-24)
void rainbow_base_state::hd_status_68_w(uint8_t data)
{
	// Bit 4-7 : --- not used / reserved

	// Bit 3 :  CLEAR STEP LATCH : This bit BAD<3>H clears out the step pulse latch. The step pulse
	//latch is set every time the WD1010 chip issues a step pulse.The output of the step pulse latch is sent to the secondary status register.
	if (data & 0x08)
		m_hdc_step_latch = false;

	// Bit 2 :  CLEAR INDEX LATCH : This bit BAD<2>H clears out the index latch. The index latch is
	//set when the disk drive senses the index position on the disk.The index latch output is sent to the secondary status register.
	if (data & 0x04)
		m_hdc_index_latch = false;

	// * Bit 1 :  SOFTWARE INITIALIZE: The BAD<I>H bit sets this bit. This bit, when set, initializes the
	// controller. The controller cannot be accessed for 7 microseconds(us) after the 8088 issues the software initialize.
	if (data & 0x02)
		hdc_reset();

	// * Bit 0 :  SET BUFFER READY : READ SECTOR command: this bit, when set, tells the WDI010 chip that the sector buffer was emptied which would then end the
	//          command. WRITE SECTOR / FORMAT CMD: bit tells the WD1010 that the sector buffer now contains valid data for transfer to the disk drive.

	// * SET BY BIOS:  2 : (WD1010 IRQ based transfer operation?)  @ 0810
	//                 1 : see  @ 088D after 'READ_SECTOR_OK'
	if (data & 0x01)
	{
		m_leds[0] = 0;  // 1 = OFF (One of the CPU LEDs as DRIVE LED) = HARD DISK ACTIVITY =
		switch_off_timer->adjust(attotime::from_msec(500));

		m_hdc->buffer_ready(true);
	}
}


/*
/ READ ONLY REGISTER (HEX 69)

The drive status register at I/O address 69H is a read-only register
that monitors the status of control and error signals to/from the disk drive.

0 Drive Select - high (1) indicates that the controller module is selecting the drive.

1-3 Head Select - These 3 bits are the binary head address of the R/W head
selected for the current read/write operation. The RD51 drive has 4 heads.

4 Write Gate - The WDlOI0 chip asserts this bit high (1) to inform the 8088 of
data being written on the disk. Signal also enables write current in disk drive.

5 Drive Write Fault - The disk drive asserts this bit high (1) to indicate that a condition
exists at the drive that may cause improper writing on the disk.
Inhibits further writing until the error is corrected (.. until RESET?) [Bavarese]

6 Drive Ready - When the disk drive together with SEEK COMPLETE asserts this
bit high (1), it indicates that the drive is ready to read, write, or
seek. When this bit is low (0), all reading, writing, and seeking are
inhibited.

7 Track 0 - The disk drive sets this bit high (1) when the R/W heads are
positioned over cylinder 0 (the data track furthest away from the spindle).
*/
uint8_t rainbow_base_state::hd_status_69_r()
{
	int hs = m_hdc->read(0x06) & (1 + 2 + 4); // SDH bits 0-2 = HEAD #
//  logerror("(x69 READ) %i = HEAD SELECT WD1010\n", hs);

	uint8_t data = (hs << 1);

	// DRIVE SELECT: 2 bits in SDH register of WDx010 could address 4 drives.
	// External circuit supports 1 drive here (DRIVE 0 selected or deselected)
	int drv = ((m_hdc->read(0x06) >> 3) & 0x01);  // 0x03 gives error R6 with DIAG.DISK
	if (drv == 0)
		data |= 1; //      logerror("(x69 READ) %i = _DRIVE # 0_ SELECT! \n", drv);

	if (m_hdc_write_gate) // WRITE GATE (cached here)
		data |= 16;

	if (m_hdc_write_fault)
		data |= 32;

	if (m_hdc_drive_ready)
		data |= 64;

	// Fake TRACK 0 signal  (normally FROM DRIVE)
	if ((m_hdc->read(0x04) == 0) && (m_hdc->read(0x05) == 0)) // CYL.LO - CYL.HI
		data |= 128; //      logerror("(x69 READ) TRACK 00 detected\n");

	return data;
}

// TREAT SIGNALS FROM / TO CONTROLLER
void rainbow_base_state::hdc_step(int state)
{
	m_hdc_step_latch = true;

	m_leds[0] = 0;  // 1 = OFF (One of the CPU LEDs as DRIVE LED)  = HARD DISK ACTIVITY =
	switch_off_timer->adjust(attotime::from_msec(500));
}

void rainbow_base_state::hdc_direction(int state)
{
	m_hdc_direction = state; // (0 = OUT)
}

int rainbow_base_state::hdc_drive_ready()
{
	return m_hdc_drive_ready;
}

int rainbow_base_state::hdc_write_fault()
{
	return m_hdc_write_fault;
}

// Buffer counter reset when BCR goes from 0 -> 1
void rainbow_base_state::hdc_bcr(int state)
{
	static int bcr_state;
	if (bcr_state == 0 && state == 1)
		hdc_buffer_counter_reset();
	bcr_state = state;
}

void rainbow_base_state::hdc_buffer_counter_reset()
{
	m_hdc->buffer_ready(false);
	m_hdc_buf_offset = 0;
}

// DATA REQUEST - When high (..) initiates data transfers
// to or from the sector buffer. On a READ, this signal
// goes high AFTER the sector buffer is filled.

// On a WRITE / FORMAT command, signal goes high when the WD1010
// chip is READY TO ACCESS the information in the sector buffer.
void rainbow_base_state::hdc_bdrq(int state)
{
	static int old_state;
//  logerror("BDRQ - BUFFER DATA REQUEST OBTAINED: %u\n", state);

	if (state == 1 && old_state == 0)
	{
		hdc_buffer_counter_reset();

		m_bdl_irq = state;
		update_bundle_irq(); // TRIGGER AN INTERRUPT
	}
	old_state = state;
}
// ---------------------------- / RD51 HARD DISK CONTROLLER ----------------------------------


// IRQ service for both RD51 and COMM. OPTION
void rainbow_base_state::update_bundle_irq()
{
	if (m_bdl_irq == 0)
	{
		lower_8088_irq(IRQ_BDL_INTR_L);

		if (m_inp5->read() == 0x01)
			hdc_buffer_counter_reset();
	}
	else
	{
		raise_8088_irq(IRQ_BDL_INTR_L);
	}
}

void rainbow_base_state::bundle_irq(int state)
{
	m_bdl_irq = state;
	update_bundle_irq();
}


uint8_t rainbow_modela_state::system_parameter_r()
{
	/*  Info about option boards is in bits 0 - 3:
	SYSTEM PARAMETER INFORMATION: see AA-P308A-TV page 92 section 14.0
	Bundle card (1) | Floppy (2) | Graphics (4) | Memory option (8)
	0 1 2 3 4 5 6 7
	B F G M
	(bit SET means NOT present; 4-7 reserved )

	B : no separation between the 2 available 'bundle cards' (HD controller / COMM.OPTION) ?

	M : old RAM extension (128 / 192 K ?) detected with OPTION_PRESENT bit, newer models 'by presence'.
	BIOS uses a separate IRQ vector for RAM board detection (at least on a 100-B).
	*/
	return ((m_inp5->read() == 1 ? 0 : 1) |
			(m_inp7->read() == 1 ? 0 : 4) | // Floppy is always present (bit 1 zero)
			(m_ram->size() > 0x10000 ? 0 : 8) |
			0xf0); // unverified
}

uint8_t rainbow_modelb_state::system_parameter_r()
{
	/*  Info about option boards is in bits 0 - 3:
	SYSTEM PARAMETER INFORMATION: see AA-P308A-TV page 92 section 14.0
	Bundle card (1) | Floppy (2) | Graphics (4) | Memory option (8)
	0 1 2 3 4 5 6 7
	B F G M
	(bit SET means NOT present; 4-7 reserved )

	B : no separation between the 2 available 'bundle cards' (HD controller / COMM.OPTION) ?

	M : old RAM extension (128 / 192 K ?) detected with OPTION_PRESENT bit, newer models 'by presence'.
	BIOS uses a separate IRQ vector for RAM board detection (at least on a 100-B).
	*/
	return ((m_inp5->read() == 1 ? 0 : 1) |
			(m_inp7->read() == 1 ? 0 : 4) | // Floppy is always present (bit 1 zero)
			0xf8); // unverified
}

//  [02] COMMUNICATIONS STATUS REGISTER - PAGE 154 (**** READ **** )
//  Used to read status of SERIAL port, IRQ line of each CPU, and MHFU logic enable signal.

// 0 COMM RI   (reflects status of RI line at COMM port)
// 1 COMM SI / SCF(reflects status of speed indicator line or
//                 the secondary receive line signal detect at COMM port)
// 2 COMM DSR  (reflects status of DSR at COMM)
// 3 COMM CTS  (reflects status of CTS at COMM)
// 4 COMM RLSD (receive line signal detect at COMM; also connected to DCDA on MPSC)
uint8_t rainbow_base_state::comm_control_r()
{
	bool is_mhfu_enabled = false;
	if (m_power_good)
		is_mhfu_enabled = m_crtc->MHFU(MHFU_IS_ENABLED);

	return (m_comm_port->ri_r() ? 0x01 : 0x00) |
		   (m_comm_port->si_r() ? 0x02 : 0x00) |
		   (m_comm_port->dsr_r() ? 0x04 : 0x00) |
		   (m_comm_port->cts_r() ? 0x08 : 0x00) |
		   (m_comm_port->dcd_r() ? 0x10 : 0x00) |
		   (is_mhfu_enabled ? 0x00 : 0x20) |   // (L) status of MHFU flag => bit pos.5
		   (m_int88 ? 0x00 : 0x40) |           // (L)
		   (m_intz80 ? 0x00 : 0x80);           // (L)

}

//  Communication control register of -COMM- port (when written):
// (these 4 bits talk DIRECTLY to the COMM port according to schematics):
// 0 COMM SPD SEL H (controls speed select line of COMM port)
// 1 COMM SRTS H     (controls secondary request to send line of COMM)
// 2 COMM DTR L      (controls terminal ready line of COMM)
// 3 COMM RTS        (controls request to send line of COMM)
void rainbow_base_state::comm_control_w(uint8_t data)
{
	logerror("%02x to COMM.CONTROL REGISTER ", data);

	m_comm_port->write_spds(BIT(data, 0));
	// SRTS not currently emulated
	m_comm_port->write_dtr(BIT(data, 2));
	m_comm_port->write_rts(BIT(data, 3));

	/* 8088 LEDs:
	5  7  6  4    <- BIT POSITION
	D6 -D5-D4-D3  <- INTERNAL LED NUMBER (DEC PDF)
	-4--5--6--7-  <- NUMBERS EMBOSSED ON BACK OF PLASTIC HOUSING (see error chart)
	*/
	m_leds[3] = BIT(data, 5); // LED "D6"
	m_leds[4] = BIT(data, 7); // LED "D5"
	m_leds[5] = BIT(data, 6); // LED "D4"
	m_leds[6] = BIT(data, 4); // LED "D3"
}

// 8088 writes to port 0x00 (interrupts Z80)
// See page 133 (4-34)
void rainbow_base_state::i8088_latch_w(uint8_t data)
{
	// logerror("%02x to Z80 mailbox\n", data);

	// The interrupt vector address(F7H) placed on the bus is hardwired into the Z80A interrupt vector encoder.
	// The F7H interrupt vector address causes the Z80A processor to perform an RST 30 instruction in
	// interrupt mode 0
	m_z80->set_input_line_and_vector(0, ASSERT_LINE, 0xf7); // Z80
	m_z80_mailbox = data;

	m_intz80 = true;
}

// Z80 reads port 0x00
// See page 134 (4-35)
uint8_t rainbow_base_state::z80_latch_r()
{
	// logerror("Read %02x from Z80 mailbox\n", m_z80_mailbox);
	m_z80->set_input_line(0, CLEAR_LINE);

	m_intz80 = false;
	return m_z80_mailbox;
}

// Z80 writes to port 0x00 (interrupts 8088)
// See page 134 (4-35)
void rainbow_base_state::z80_latch_w(uint8_t data)
{
	// logerror("%02x to 8088 mailbox\n", data);
	raise_8088_irq(IRQ_8088_MAILBOX);
	m_8088_mailbox = data;

	m_int88 = true;
}

// 8088 reads port 0x00. See page 133 (4-34)
uint8_t rainbow_base_state::i8088_latch_r()
{
	// logerror("Read %02x from 8088 mailbox\n", m_8088_mailbox);
	lower_8088_irq(IRQ_8088_MAILBOX);

	m_int88 = false;
	return m_8088_mailbox;
}

// (Z80) : WRITE to 0x20
void rainbow_base_state::z80_diskdiag_read_w(uint8_t data)
{
	m_zflip = true; //  "a write to 20H will _SET_ ZFLIP"
}

// (Z80) : PORT 21H * WRITE *
void rainbow_base_state::z80_diskdiag_write_w(uint8_t data)
{
	/*   Z80 LEDs:
	4   5   6  <- bit #
	D11 D10 -D9 <- INTERNAL LED NUMBER (see PDF)
	-1 --2-- 3  <- NUMBERS EMBOSSED ON BACK OF PLASTIC HOUSING (see error chart)
	*/
	m_leds[0] = BIT(data, 4); // LED "D11"
	m_leds[1] = BIT(data, 5); // LED "D10"
	m_leds[2] = BIT(data, 6); // LED "D9"

	m_zflip = false; // "a write to 21H will reset ZFLIP"
}

// (Z80) : PORT 20H / 21H  _READ_
uint8_t rainbow_base_state::z80_generalstat_r()
{
	/*
	General / diag.status register Z80 / see page 157 (table 4-18).
	---- BITS FROM RX50 CONTROLLER CARD:
	D7 : STEP L : reflects status of STEP signal _FROM FDC_
	(when this 2us output pulse is low, the stepper will move into DIR)
	D6 : WRITE GATE L :reflects status of WRITE GATE signal _FROM FDC_
	(asserted low before data can be written on the diskette)
	D5 : TR00: reflects status of TRACK 0 signal (= 1) * from the disk drive *
	D4 : DIR L: reflects status of DIRECTION signal * FROM FDC * to disk
	(when low, the head will step towards the center)
	D3 : READY L: reflects status of READY L signal * from the disk drive *
	(low active, asserts when disk is inserted and door is closed)
	---- BITS BELOW FROM MAINBOARD:
	D2 : INT88 L: (bit reads the INT88 bit sent by Z80 to interrupt 8088)
	D1 : INTZ80 L: (bit reads the INTZ80 bit sent by 8088 to interrupt Z80)
	D0 : ZFLIP L: (read from the diagnostic control register of Z80A)
	*/
	static int last_track;
	int track = 0;

	int fdc_step = 0;
	int fdc_ready = 0;
	int tk00 = 0;
	int fdc_write_gate = 0;
	int last_dir = 0;

	uint8_t fdc_status;

	if (m_fdc)
	{
		track = m_fdc->track_r();
		if (track == 0)
			tk00 = 1;

		if (track != last_track)
			fdc_step = 1;  // calculate STEP (sic)

		last_dir = track > last_track ? 0 : 1; // see WD_FDC
		last_track = track;

		fdc_status = m_fdc->status_r();

		if ((fdc_status & 0x80) == 0) // (see WD_FDC: S_WP = 0x40, S_NRDY = 0x80, S_TR00 = 0x04)
			fdc_ready = 1;

		if (fdc_ready && ((fdc_status & 0x40) == 0) && m_power_good)
			fdc_write_gate = 1; // "valid only when drive is selected" !
	}
	// logerror(" RDY:%x  WG:%d ",fdc_ready,fdc_write_gate);
	int data = (fdc_step ? 0x00 : 0x80) |
			   (fdc_write_gate ? 0x00 : 0x40) |
			   (tk00 ? 0x20 : 0x00) |  // ***** ALL LOW ACTIVE - EXCEPT tk00 :
			   (last_dir ? 0x00 : 0x10) |
			   (fdc_ready ? 0x00 : 0x08) |
			   (m_int88 ? 0x00 : 0x04) |
			   (m_intz80 ? 0x00 : 0x02) |
			   (m_zflip ? 0x00 : 0x01);

	return data;
}


// (Z80) : PORT 40H _READ_
// 40H diskette status Register **** READ ONLY *** ( 4-60 of TM100.pdf )
uint8_t rainbow_base_state::z80_diskstatus_r()
{
	int track = 0xEE;
	int data = m_z80_diskcontrol & (255 - 0x80 - 0x40 - 0x20 - 0x04); // 00011011

	// D7: DRQ: reflects status of DATA REQUEST signal from FDC.
	// '1' indicates that FDC has read data OR requires new write data.

	// D6: IRQ: indicates INTERRUPT REQUEST signal from FDC. Indicates that a
	//          status bit has changed. Set to 1 at the completion of any
	//          command (.. see page 207 or 5-25).
	if (m_fdc)
	{
		data |= m_fdc->drq_r()   ? 0x80 : 0x00;
		data |= m_fdc->intrq_r() ? 0x40 : 0x00;
		track = m_fdc->track_r();

		// D2: TG43 * LOW ACTIVE * :  0 = INDICATES TRACK > 43 SIGNAL FROM FDC TO DISK DRIVE.
		// (asserted when writing data to tracks 44 through 79)
		data |= (track > 43) ? 0x00 : 0x04;  // ! LOW ACTIVE !
	}

	// D5: SIDE 0 * HIGH ACTIVE *: status of side select signal at J2 + J3 of RX50 controller.
	//              For 1 sided drives, this bit will always read low (0).
	if (m_floppy != nullptr)
		data |= m_floppy->ss_r() ? 0x20 : 0x00;

	// *LOW ACTIVE *
	// D4: MOTOR 1 ON L: 0 = indicates MOTOR 1 ON bit is set in drive control reg.
	// D3: MOTOR 0 ON L: 0 = indicates MOTOR 0 ON bit is set in drive  "

	// Print HEX track number
	static uint8_t bcd2hex[] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71 };
	// 0...9 ,A (0x77), b (0x7c), C (0x39) , d (0x5e), E (0x79), F (0x71)
	m_digits[0] = bcd2hex[(track >> 4) & 0x0f];
	m_digits[1] = bcd2hex[track & 0x0f];

	// D1: DS1 H: reflect status of bits 0 and 1 from disk.control reg.
	// D0: DS0 H: "
	return data;
}


// (Z80) : PORT 40H  * WRITE *
// NOTE: routine will accept invalid drive letters...

// ALL SIGNALS ARE HIGH ACTIVE (H), EXCEPT:
// BIT 5 : SIDE 0 L : For single sided drives, this bit is always set to 0 for side O.
void rainbow_base_state::z80_diskcontrol_w(uint8_t data)
{
	int enable_start;
	int disable_start; // set defaults

	int selected_drive = INVALID_DRIVE;

	int drive = 0;
	if (m_inp10->read() && ((data & 3) < 2))
		drive = (data & 1) + 2;
	else
		drive = data & 3;

	if (m_floppies[drive])
	{
		m_floppy = m_floppies[drive]->get_device();
		if (m_floppy)
			selected_drive = drive;
	}

	if (selected_drive == INVALID_DRIVE)
	{
		logerror("(m_present_drive = %i)   ** SELECTED DRIVE ** INVALID. (selected drive = %i)\n", m_present_drive, selected_drive);

		m_present_drive = INVALID_DRIVE;
		m_floppy = nullptr;
	}

	for (int i = 0; i < 4; i++)
		m_driveleds[i] = (selected_drive == i) ? 1 : 0;
	switch_off_timer->adjust(attotime::from_msec(500));

	if (m_floppy != nullptr)
	{
		m_fdc->set_floppy(m_floppy);  // Sets new  _image device_
		m_fdc->dden_w(0); // 0 = MFM
		m_floppy->ss_w((data & 0x20) ? 1 : 0); // RX50 board in Rainbow has 'side select'
		m_floppy->set_rpm(300.);

		if ( !m_floppy->exists() && (selected_drive > 1) )
			popmessage("NO IMAGE ATTACHED TO %c\n", 65 + selected_drive );
	}

	if (selected_drive < MAX_FLOPPIES)
	{
		m_present_drive = selected_drive;

		bool force_ready = ((data & 4) == 0) ? true : false;
		m_fdc->set_force_ready(force_ready); // 1 : assert DRIVE READY on FDC (diagnostic override)

		if (selected_drive < 2)
		{
			data |= 8;
			enable_start = 0;
			disable_start = 2;
		}
		else
		{
			data |= 16;
			enable_start = 2;
			disable_start = 4;
		}

		// RX-50 has head A and head B (1 for each of the 2 disk slots in a RX-50).
		// Assume the other one is switched off -
		for (int f_num = 0; f_num < MAX_FLOPPIES; f_num++)
		{
		floppy_image_device *tmp_floppy = m_floppies[f_num]->get_device();

		if (!tmp_floppy)
			continue;
		tmp_floppy->mon_w(ASSERT_LINE);
		if ((f_num >= enable_start) && (f_num < disable_start))
			tmp_floppy->mon_w(CLEAR_LINE); // enable
		}
	}

	data = (data & (255 - 3)); // invalid drive = DRIVE 0 ?!

	if (m_present_drive == INVALID_DRIVE)
		logerror("**** INVALID DRIVE ****\n");
	else
		data = data | m_present_drive;

	m_z80_diskcontrol = data;
}
// --------- END OF Z80 --------------------

uint8_t rainbow_base_state::read_video_ram_r(offs_t offset)
{
	return m_p_ram[offset];
}




// **************************************************
// VIDEO INTERRUPT HANDLING
// **************************************************

// CPU acknowledge of VBL IRQ resets counter
IRQ_CALLBACK_MEMBER(rainbow_base_state::irq_callback)
{
	int intnum = -1;
	for (int i = IRQ_8088_VBL; i >= 0; i--)
	{
			if (m_irq_mask & (1 << i))
			{
				if (i == IRQ_8088_VBL)  // If VBL IRQ acknowledged...
					m_crtc->MHFU(MHFU_RESET); // ...reset counter (also: DC012_W)

				intnum = vectors[i] | m_irq_high;
				break;
			}
	}
	return intnum;
}

// NEC7220 Vsync IRQ ***************************************** GDC

// VERIFY: SCROLL_MAP & COLOR_MAP are updated at the next VSYNC (not immediately)... Are there more registers?
void rainbow_base_state::GDC_vblank_irq(int state)
{
	// VERIFICATION NEEDED: IRQ raised before or after new palette loaded...?
	if (m_gdc_mode_register & GDC_MODE_ENABLE_VSYNC_IRQ) // 0x40
		raise_8088_irq(IRQ_GRF_INTR_L);
	else
		lower_8088_irq(IRQ_GRF_INTR_L);

	m_monitor_suggested = m_inp13->read();
	if (m_monitor_suggested < 1 || m_monitor_suggested > 3)
		m_monitor_suggested = COLOR_MONITOR;

	int mono_sum = 0;
	int green_sum = 0;
	for (uint8_t xi = 0; xi < 16; xi++) // DELAYED LOAD OF PALETTE ...
	{
		uint8_t colordata1  = m_gdc_color_map[xi];
		uint8_t colordata2 = m_gdc_color_map[xi + 16];      // Does it matter if the palette is incomplete...?

		//              Color map:  32 x 8
		//              2nd 16 Byte     1st 16 Bytes (colordata1)
		//              -----------     ------------
		//              7..4  3..0      7..4  3..0
		//              Mono  Blue      Red   Green
		// NOTE: 2nd 16 BYTES ARE MONO PALETTE, 1st 16 ARE COLOR PALETTE * HERE * (on the VT240 driver, it is the other way round)

		uint8_t mono = (colordata2 & 0xF0) >> 4;  // FIXME: limit palette in appropriate modes on 100-A
		mono_sum += mono;

		uint8_t blue = (colordata2 & 0x0F);

		uint8_t red  = (colordata1 & 0xF0) >> 4;
		uint8_t green =(colordata1 & 0x0F);
		green_sum += green;

		switch (m_monitor_suggested)
		{
		case MONO_MONITOR:
			switch (m_inp9->read()) //  - monochrome monitor (phosphor) type  (1,2,3)
			{
			case 1: // BLACK & WHITE
				m_palette2->set_pen_color(xi + 16, pal4bit(mono), pal4bit(mono), pal4bit(mono) );
				break;

			case 2: // GREEN SHADES. Hand picked value from vtvideo coarsly transformed into a RGB value:
				red   = uint8_t( ( 35.0f / 100.0f) *  ( video_levels[ mono ] / 2.55f) );
				green = uint8_t( (145.0f / 100.0f) *  ( video_levels[ mono ] / 2.55f) );
				blue  = uint8_t( ( 75.0f / 100.0f) *  ( video_levels[ mono ] / 2.55f) );
				m_palette2->set_pen_color(xi + 16, rgb_t( red, green, blue) );
				break;

			case 3: // AMBER. Assumption: "normal" value at 80 % is 213, 146, 82 (decimal)
				red   = uint8_t( (213.0f / 100.0f) *  ( video_levels[ mono ] / 2.55f) );
				green = uint8_t( (146.0f / 100.0f) *  ( video_levels[ mono ] / 2.55f) );
				blue  = uint8_t( ( 82.0f / 100.0f) *  ( video_levels[ mono ] / 2.55f) );
				m_palette2->set_pen_color(xi + 16, rgb_t( red, green, blue) );
				break;
			}
			break;

		case COLOR_MONITOR:
			if (!(m_gdc_mode_register & GDC_MODE_ENABLE_VIDEO))
				red = blue = 0; // Page 21 of AA-AE36A (PDF) explains why

			m_palette2->set_pen_color(xi, pal4bit(red) , pal4bit(mono) , pal4bit(blue));
			break;

		case DUAL_MONITOR:
			m_palette2->set_pen_color(xi, pal4bit(red), pal4bit(green), pal4bit(blue));
			break;
		}
	} // palette (loop)

	if (green_sum > 0 && green_sum == mono_sum) // (R-G-B + M) palette (split cable). Examples: PACMAN, AutoCad
	{
		if (m_monitor_suggested == MONO_MONITOR)
			logerror("\n[HINT: COLOR PALETTE DETECTED - SUITABLE FOR DUAL MONITOR] ");
		if (m_inp13->read() == AUTODETECT_MONITOR)
			m_monitor_suggested = DUAL_MONITOR;
	}

	if (green_sum == 0 && mono_sum > 0)  // No green = original DEC spec. Example: NLANDER. All older libaries use R-M-B.
	{
		if (m_inp13->read() == AUTODETECT_MONITOR)
			m_monitor_suggested = COLOR_MONITOR;
		if (m_monitor_suggested == DUAL_MONITOR)
			logerror("\n[HINT: SINGLE COLOR MONITOR ONLY!  GREEN MISSING => NO SPLIT CABLE]");
	}

} // 7220 vblank IRQ


void rainbow_base_state::video_interrupt(int state)
{
	if (state == ASSERT_LINE)
		raise_8088_irq(IRQ_8088_VBL);
	else
		lower_8088_irq(IRQ_8088_VBL);

	if (state == ASSERT_LINE && m_power_good && m_crtc->MHFU(MHFU_IS_ENABLED)) // If enabled...
	{
		if (m_crtc->MHFU(MHFU_VALUE) > 10) // + more than (10 * 16.666) msecs gone (108 ms would be by the book)
		{
			m_crtc->MHFU(MHFU_RESET_and_DISABLE);
			popmessage("**** WATCHDOG TRIPPED:nVBL IRQ not acknowledged within (at least) 108 milliseconds. ****");

			if (m_inp12->read() == 0x01) // (DIP) for watchdog active?
				cmd_timer->adjust(attotime::from_msec(RESET_DURATION_MS));
		}
	}
}

// Reflects bits from 'diagnostic_w' (1:1), except test jumpers
uint8_t rainbow_base_state::diagnostic_r() // 8088 (port 0A READ). Fig.4-29 + table 4-15
{
	return ((m_diagnostic & 0xf1) | m_inp1->read() | m_inp2->read() | m_inp3->read());
}

void rainbow_base_state::diagnostic_w(uint8_t data) // 8088 (port 0A WRITTEN). Fig.4-28 + table 4-15
{
	//    logerror("%02x to diag port (PC=%x)\n", data, m_i8088->pc());

	// ZRESET from 8088 to Z80 - - HIGH at powerup!
	if (!(data & 1))
	{
		m_z80->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
		m_z80_halted = true;
	}

	if ((data & 1) && (m_z80_halted))
	{
		m_zflip = true;
		m_z80_halted = false;

		m_z80->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
		m_z80->reset();
	}

	if ((m_diagnostic & 1) && !(data & 1)) // ZRESET goes LOW...
	{
		logerror("FDC ** RESET **\n");
		m_fdc->reset();
	}

	if (!(m_diagnostic & 1) && (data & 1)) // ZRESET goes HIGH...
	{
		logerror("FDC RESTORE\n");
		m_fdc->reset(); // See formatter description p.197 or 5-13
	}

	m_screen_blank = BIT(data, 1)? false : true; // inverse logic

	// Switch determines how the monochrome output pin is taken from:
	//    0  = M(ono) out from system module (DC011/DC012). Default, also used to setup dual monitors.
	//    1  = M(ono) output from GRAPHICS OPTION. (G)reen remains unused with a single COLOR monitor.
	m_onboard_video_selected = (data & 0x04) ? false : true;
	if (!m_onboard_video_selected)
	{
		if (m_inp7->read() == 1)
		{
			logerror("HINT: GRAPHICS OPTION ON. TEXT ONLY (DC011/DC012) OUTPUT NOW DISABLED.\n");
		}
		else
		{       logerror("ALARM: GRAPHICS OPTION * SWITCHED OFF * VIA DIP. TEXT OUTPUT STILL ENABLED!\n");
			m_onboard_video_selected = true;
		}
		logerror("DATA: %x (PC=%x)\n", data, m_i8088->pc());
	}

	// BIT 3: PARITY (1 enables parity test on memory board. Usually 64K per bank). -> ext_ram_w.
	if (data & 0x08)
		logerror("*** PARITY TEST [on RAM EXTENSION] - (bit 3 - diagnostic_w)\n");

	// MISSING BITS (* not vital for normal operation, see diag.disk) -
	// * BIT 4: DIAG LOOPBACK (0 at power-up; 1 directs RX50 and DC12 output to printer port)
	// * BIT 5: PORT LOOPBACK (1 enables loopback for COMM, PRINTER, KEYBOARD ports)

	/* 2.1.7.3 DIAGNOSTIC LOOPBACK Maintenance Bit - The DIAGNOSTIC LOOPBACK bit is a
	maintenance bit that is cleared on power - up.This bit, when set to 1,
	allows the floppy data separator and the serial video output to be tested
	through the use of the printer port. The following table shows how signals are routed.

	DIAGNOSTIC LOOPBACK = 0     DIAGNOSTIC LOOPBACK = 1     SIGNAL INPUT
	SIGNAL SOURCE               SIGNAL SOURCE               TO
	FROM                        FROM
	PRT RDATA(J2)               VIDEO OUT                   PRT RXD(7201)
	PRT RXTXC                   500 KHZ                     PRT RXTXC(7201)
	MASTER CLK                  250 KHZ                     VIDEO CLK(DCO11)
	FLOPPY RAW DATA             PRT TXD(7201)               FLOPPY DATA SEPARATOR

	During Diagnostic Loopback, the - TEST input of the 8088 is connected to the
	interrupt output of the MPSC.Thus, using the 8088's WAIT instruction in a
	polled I / O loop, the diagnostic firmware will be able to keep up with the
	500 Kb data rate on the MPSC.
	*/
	if (data & 16)
	{
		logerror("WARNING: UNEMULATED DIAG LOOPBACK (directs RX50 and DC12 output to printer port) ****\n");
	}

	address_space &io = m_i8088->space(AS_IO);
	if (data & 32)
	{
		/* BIT 5: PORT LOOPBACK (1 enables loopback for COMM, PRINTER, KEYBOARD ports)
		2.1.7.2. of AA-V523A-TV (PDF Mar83) says how the signals are routed:
		port_loopback_0  |  port_loopback_1   SIGNAL INPUT TO
		COMM RCV DATA.......COMM TXD..........COMM_RXD
		PRT  RCV DATA.......KBD TXD...........PRT RDATA
		KBD  RCV DATA.......PRT TXD...........KBD RXD
		*/
		logerror("WARNING: UNEMULATED PORT LOOPBACK (COMM, PRINTER, KEYBOARD ports) ****\n");

		io.unmap_readwrite(0x40, 0x43);  // unmap MPSC handlers to prevent CPU crashes ("INTERRUPTS OFF")
	}

	// Install 8088 read / write handler once loopback test is over
	if ( !(data & 32) && (m_diagnostic & 32) )
	{
			io.install_readwrite_handler(0x40, 0x43, read8sm_delegate(*m_mpsc, FUNC(upd7201_device::cd_ba_r)), write8sm_delegate(*m_mpsc, FUNC(upd7201_device::cd_ba_w)));
			logerror("\n **** COMM HANDLER INSTALLED **** ");
			//popmessage("Autoboot from drive %c", m_p_nvram[0xab] ? (64 + m_p_nvram[0xab]) : 0x3F );
	}

	// BIT 6: Transfer data from volatile memory to NVM  (PROGRAM: 1 => 0   BIT 6)
	if (!(data & 0x40) && (m_diagnostic & 0x40))
		memcpy(m_p_nvram, m_p_vol_ram, 256);

	// BIT 7: Transfer data from NVM to volatile memory (RECALL 0 => 1     BIT 7)
	if ((data & 0x80) && !(m_diagnostic & 0x80))
		memcpy(m_p_vol_ram, m_p_nvram, 256);

	m_diagnostic = data;
}

// KEYBOARD
void rainbow_base_state::update_kbd_irq()
{
	if ((m_kbd_rx_ready) || (m_kbd_tx_ready))
		raise_8088_irq(IRQ_8088_KBD);
	else
		lower_8088_irq(IRQ_8088_KBD);
}

void rainbow_base_state::kbd_tx(int state)
{
	m_lk201->rx_w(state);
}

void rainbow_base_state::kbd_rxready_w(int state)
{
	m_kbd_rx_ready = (state == 1) ? true : false;
	update_kbd_irq();
}

void rainbow_base_state::kbd_txready_w(int state)
{
	m_kbd_tx_ready = (state == 1) ? true : false;
	update_kbd_irq();
}

TIMER_DEVICE_CALLBACK_MEMBER(rainbow_base_state::hd_motor_tick)
{
	if (m_power_good)
		m_crtc->MHFU(MHFU_COUNT); // // Increment IF ENABLED and POWER_GOOD, return count

	m_hdc_index_latch = true; // HDC drive index signal (not working ?)
}

void rainbow_modela_state::irq_hi_w(int state)
{
	m_irq_high = 0;
}

// on 100-B, DTR from the keyboard 8051 controls bit 7 of IRQ vectors
void rainbow_modelb_state::irq_hi_w(int state)
{
	m_irq_high = (state == ASSERT_LINE) ? 0x80 : 0;
}


// ********************************* NEC UPD7220 ***********************************************
// Readback mode: correct place?  Not for vector mode (really)...?

// NOTE: "More than one plane at a time can be enabled for a write operation; however,
//        only one plane can be enabled for a read operation at anyone time."

uint16_t rainbow_base_state::vram_r(offs_t offset)
{
	if ((!(m_gdc_mode_register & GDC_MODE_VECTOR)) || machine().side_effects_disabled())  // (NOT VECTOR MODE)
	{
		// SCROLL_MAP IN BITMAP MODE ONLY...?
		if (m_gdc_mode_register & GDC_MODE_HIGHRES)
			offset = (m_gdc_scroll_buffer[(offset & 0x3FC0) >> 6] << 6) | (offset & 0x3F);
		else
			offset = (m_gdc_scroll_buffer[(offset & 0x1FC0) >> 6] << 6) | (offset & 0x3F);

		int readback_plane = 0;

		if (!(m_gdc_mode_register & GDC_MODE_ENABLE_WRITES)) // 0x10           // READBACK OPERATION - if ENABLE_WRITES NOT SET
		   readback_plane = (m_gdc_mode_register & GDC_MODE_READBACK_PLANE_MASK) >> 2; // READBACK PLANE 00..02, mask in bits 2+3

		return m_video_ram[ (offset & 0x7fff)  + (0x8000 * readback_plane)];
	}
	return 0xffff;
}

// NOTE: Rainbow has separate registers for fore and background.
void rainbow_base_state::vram_w(offs_t offset, uint16_t data)
{
	if (m_gdc_mode_register & GDC_MODE_HIGHRES)
		offset = (m_gdc_scroll_buffer[(offset & 0x3FC0) >> 6] << 6) | (offset & 0x3F);
	else
		offset = (m_gdc_scroll_buffer[(offset & 0x1FC0) >> 6] << 6) | (offset & 0x3F);

	offset &= 0xffff; // same as in VT240?
	uint16_t chr = data; // VT240 : uint8_t

	if (m_gdc_mode_register & GDC_MODE_VECTOR) // VT240 : if(SELECT_VECTOR_PATTERN_REGISTER)
	{
		chr = bitswap<8>(m_vpat, m_patidx, m_patidx, m_patidx, m_patidx, m_patidx, m_patidx, m_patidx, m_patidx);
		chr |= (chr << 8);
		if (m_patcnt-- == 0)
		{
			m_patcnt = m_patmult;
			if (m_patidx-- == 0)
				m_patidx = 7;
		}
	}
	else
	{
		chr = m_gdc_write_buffer[ m_gdc_write_buffer_index++ ];
		m_gdc_write_buffer_index &= 0xf;

		chr |= (m_gdc_write_buffer[m_gdc_write_buffer_index++] << 8);
		m_gdc_write_buffer_index &= 0xf;
	}

	if (m_gdc_mode_register & GDC_MODE_ENABLE_WRITES) // 0x10
	{
		// ALU_PS register: controls logic used in writing to the bitmap / inhibiting of writing to specified planes.
		//     plane select and logic operations on write buffer... (and more)  **** SEE  PAGE 36 ****
		int ps = m_gdc_alu_ps_register & 0x0F; // PLANE SELECT 0..3    // VT 240 : ~m_gdc_alu_ps_register & 3;
		uint8_t fore = ((m_gdc_fg_bg & 0xf0)) >> 4;
		uint8_t back =  (m_gdc_fg_bg & 0x0f);      // background : 0..3 confirmed, see p.39 AA-AE36A (PDF)

		for (int i = 0; i <= 3; i++)
		{
			if (BIT(ps, i)) // 1 means don't touch (bits already inverted)
			{
				uint16_t mem = m_video_ram[(offset & 0xffff) + (0x8000 * i)];

				uint16_t out = 0; // VT240 : uint8_t
				for (int j = 0; j <= 15; j++)  // REPLACE MODE : one replaced by FG, zero by BG ( 16 instead of 8 bit on VT240 )
					out |= BIT(chr, j) ? ((fore & 1) << j) : ((back & 1) << j);

				switch ((m_gdc_alu_ps_register) & ALU_PS_MODE_MASK)
				{
				case OVERLAY_MODE: // (OR)
					out |= mem;
					break;

				case COMPLEMENT_MODE: // (XOR)
					out ^= ~mem;
					break;

				default: // ALL ELSE
					break;
				}

				if (!(m_gdc_mode_register & GDC_MODE_VECTOR)) // 0 : Text Mode and Write Mask Batch
					out = (out & ~m_gdc_write_mask) | (mem & m_gdc_write_mask);
				else
					out = (out & ~data) | (mem & data);

				if (m_gdc_mode_register & GDC_MODE_ENABLE_WRITES) // 0x10
					m_video_ram[(offset & 0xffff) + (0x8000 * i)] = out;
		   } // if plane selected

			fore >>= 1;
			back >>= 1;

		} // plane select (LOOP)
		return;
	} // if enable_writes
}

// (READ)
// Read  scroll buffer (see GDC Diagnostic Disk, SCROLL BUFFER test)
uint8_t rainbow_base_state::GDC_EXTRA_REGISTER_r(offs_t offset)
{
	uint8_t data = 0;
	switch (offset)
	{
	case 0:
		data = m_gdc_mode_register; // ?
		break;

	case 1:
		if (m_gdc_indirect_register & GDC_SELECT_SCROLL_MAP ) // 0x80
		{
			// Documentation says it is always incremented (read and write):
			data = m_gdc_scroll_buffer[m_gdc_scroll_index++]; // // * READ * SCROLL_MAP ( 256 x 8 )
			m_gdc_scroll_index &= 0xFF; // 0...255  (CPU accesses 256 bytes)
			break;
		}
		else
			logerror("\n * UNEXPECTED CASE: READ REGISTER 50..55 with INDIRECT_REGISTER $%02x and OFFSET $%02x *", m_gdc_indirect_register, offset);
		break;

	case 6:
	case 7:
		data = m_hgdc->read(offset & 0x01);
		break;

	default:
		logerror("\n * UNHANDLED CASE: READ REGISTER 50..55 with INDIRECT_REGISTER $%02x and OFFSET $%02x *", m_gdc_indirect_register, offset);
		break;
	} // switch
	return data;
}

void rainbow_base_state::GDC_EXTRA_REGISTER_w(offs_t offset, uint8_t data)
{
	static int last_message, last_mode, last_readback, last_scroll_index;

	if (offset > 0) // Port $50 reset done @ boot ROM 1EB4/8 regardless if option present.
	{
		if (m_inp7->read() != 1)
		{
			if (last_message != 1)
			{
				popmessage("\nCOLOR GRAPHICS ADAPTER INVOKED.  PLEASE TURN ON THE APPROPRIATE DIP SWITCH, THEN REBOOT.\n");
				logerror("OFFSET: %x (PC=%x)\n", 0x50 +offset , m_i8088->pc());
				last_message = 1;
			}
			return;
		}
	}

	switch (offset)
	{
	case 0: // Mode register must be reloaded following any write to port 50 (software reset).
		// FIXME: "Any write to this port also resynchronizes the
		//        read/modify/write memory cycles of the Graphics Option to those of the GDC." (?)

		if (data & 1) // PDF QV069 suggests 1 -> 0 -> 1. Most programs just set bit 0 (PACMAN).
		{
			// Graphics option software reset (separate from GDC reset...)
			OPTION_GRFX_RESET
			OPTION_RESET_PATTERNS
		}
		break;

	case 1: //  51h = DATA loaded into (a register previously addressed by a write to 53h)
		if (m_gdc_indirect_register & GDC_SELECT_WRITE_BUFFER) // 0x01
		{
			m_gdc_write_buffer_index = 0;                   // (writing to 51h  CLEARS  the index counter)
			break;
		}

		if (m_gdc_indirect_register & GDC_SELECT_COLOR_MAP) // 0x20
		{
			m_gdc_color_map[m_gdc_color_map_index++] = ~data; // tilde data verified by DIAGNOSTIC!
			if (m_gdc_color_map_index == 32)
			{
				m_gdc_color_map_index = 0; // 0...31  (CPU accesses 32 bytes

				logerror("* COLOR MAP FULLY LOADED *\n");
				for (int zi = 0; zi < 16; zi++)
				{
					int g =  m_gdc_color_map[zi] & 0x0F;
					int r = (m_gdc_color_map[zi] & 0xF0) >> 4;

					int b =  m_gdc_color_map[zi + 16] & 0x0F;
					int m =  (m_gdc_color_map[zi + 16] & 0xF0) >> 4;
					logerror("[%d] %1x %1x %1x  %1x (1:1)\n", zi, r, g, b, m);
				}
				logerror("------------------------------\n");
			} // if all colors present
			break;
		}

		if (m_gdc_indirect_register & GDC_SELECT_SCROLL_MAP) // 0x80
		{
			if (!(m_gdc_mode_register & GDC_MODE_READONLY_SCROLL_MAP)) // ? READONLY / WRITE logic  correct...?
			{
				m_gdc_scroll_buffer[m_gdc_scroll_index] = data; // // WRITE TO SCROLL_MAP ( 256 x 8 )

				if (m_gdc_scroll_index == 255)
					logerror("---- SCROLL MAP FULLY LOADED ---*\n");
				m_gdc_scroll_index++;
				m_gdc_scroll_index &= 0xFF; // 0...255  (CPU accesses 256 bytes)
			}
			break;
		}

		// -----------------PATTERN + MULTIPLIER USED IN VECTOR MODE ONLY!
		// SEE PAGE 37 OF AA-AE36A (PDF).
		if (m_gdc_indirect_register & GDC_SELECT_PATTERN_MULTIPLIER) // 0x02
		{
			// On a Rainbow, 12 indicates a multiplier of 16-12 = 4 (example)
			m_patmult = 16 - (data & 15); // 4 bit register  // VT240: "patmult_w"
			break;
		}

		if (m_gdc_indirect_register & GDC_SELECT_PATTERN) // 0x04
		{
			// NOTE : Pattern Multiplier MUST BE LOADED before (!)
			m_vpat = data;
			break;
		}

		if (m_gdc_indirect_register & GDC_SELECT_FG_BG) // 0x08
		{
			m_gdc_fg_bg = data;  // Neither bitswap nor negated (and also not both)...
			break; //  Next: prepare FG / BG (4 bits each) + plane  in  ALU - PLANE_SELECT register.
		}

		if (m_gdc_indirect_register & GDC_SELECT_ALU_PS) // 0x10
		{
			m_gdc_alu_ps_register = ~data;  // Negated...
			break;
		}

		if (m_gdc_indirect_register & GDC_SELECT_MODE_REGISTER) // 0x40
		{
			m_gdc_mode_register =  data; // Neither bitswap nor negated (and also not both)...

			if (data & GDC_MODE_HIGHRES) // 0x01
			{
				if (last_message != 2)
				{
					last_message = 2;
					logerror("* HIGH RESOLUTION *\n");
				}
			}
			else
			{
				if (last_message != 3)
				{
					last_message = 3;
					logerror("MEDIUM RESOLUTION\n");
				}
			}

			if (last_mode != (data & GDC_MODE_VECTOR)) // 0x02
			{
				last_mode = data & GDC_MODE_VECTOR;
				if (data & GDC_MODE_VECTOR)
					logerror(" VECTOR MODE ");
				else
					logerror(" WORD MODE ");
			}

			if (last_readback != (data & GDC_MODE_ENABLE_WRITES)) // 0x10
			{
				last_readback = data & GDC_MODE_ENABLE_WRITES;
				if (data & GDC_MODE_ENABLE_WRITES) // 0x10
					logerror(" READBACK: OFF - ENABLE_WRITES ");
				else    // READBACK PLANE 00..02 - mask in bits 2+3:
					logerror(" READBACK MODE; plane = %02x ", m_gdc_mode_register & GDC_MODE_READBACK_PLANE_MASK); // unsure if PLANE is set... already?!
			}

			if (last_scroll_index != m_gdc_scroll_index)
			{
				last_scroll_index = m_gdc_scroll_index;
				if (data & GDC_MODE_READONLY_SCROLL_MAP) // 0x20
				   logerror(" SCROLL MAP READ_ONLY. Index : %02x ", m_gdc_scroll_index);
				 else
				   logerror(" SCROLL MAP IS WRITABLE. Index : %02x ", m_gdc_scroll_index);
			}

			if (!(data & GDC_MODE_ENABLE_VSYNC_IRQ)) // 0x40
				lower_8088_irq(IRQ_GRF_INTR_L); // also clears the interrupt

			// case 0x80 :  If this bit is a 1 red and blue outputs are enabled. If this bit is a 0 red and blue outputs are disabled (page 20 of AA-AE36A)
			break;
		} // GDC_SELECT_MODE_REGISTER

		logerror("\n* UNIMPLEMENTED CASE. MODE = %02x / m_gdc_indirect_register = %02x\n",m_gdc_mode_register, m_gdc_indirect_register);
		break;

	case 2:
		//  52h   Data written to this port is loaded into the Write Buffer
		//        While the CPU accesses the Write Buffer as sixteen 8-bit bytes,
		//        the GDC accesses the buffer as eight 16-bit words.
		//        A 16-bit Write Mask gives the GDC control over individual bits of a word.
		// --------------------  WRITE BUFFER USED IN WORD MODE ONLY !
		// "OUTPUT WRITE BUFFER IS THE INVERSE OF THE INPUT" (quote from 4-3 of the PDF)
		//  BITSWAP SEEMS NECESSARY (see digits in DOODLE)... !
		m_gdc_write_buffer[m_gdc_write_buffer_index++] = ~bitswap<8>(data, 0, 1, 2, 3, 4, 5, 6, 7);
		m_gdc_write_buffer_index &= 0xf; // write up to 16 bytes to port 52h.
		break;

	case 3: //  53h   Indirect Register; address selection for indirect addressing. See 51h.
		m_gdc_indirect_register = data ^ 0xff;

		// Index to WRITE_BUFFER is reset via dummy write to port 51h (not here!).

		if (m_gdc_indirect_register & GDC_SELECT_COLOR_MAP) // 0x20
			m_gdc_color_map_index = 0;                      // (also clears the index counter)
		// NEXT: 32 BYTE COLOR MAP, LOADED TO $51

		//if (m_gdc_indirect_register & GDC_SELECT_MODE_REGISTER) // 0x40
		//      logerror(" *** SELECT MODE REGISTER");

		if (m_gdc_indirect_register & GDC_SELECT_SCROLL_MAP) // 0x80
		{
			if (last_scroll_index != m_gdc_scroll_index)
			{
				last_scroll_index =  m_gdc_scroll_index;
				logerror(" *** SCROLL INDEX COUNTER RESET, old value = %d", m_gdc_scroll_index);
			}
			m_gdc_scroll_index = 0;                         // (also clears the index counter)
		}  // NEXT: LOAD 256 BYTE SCROLL MAP INTO $51
		break;

	// --------- WRITE MASK (2 x 8 = 16 bits) USED IN WORD MODE ONLY !
	// There is no specific order for the WRITE_MASK (according to txt/code samples in DEC's PDF).
	// NOTE: LOW <-> HI JUXTAPOSITION!
	case 4: // 54h   Write Mask LOW
		m_gdc_write_mask = ( bitswap<8>(data, 0, 1, 2, 3, 4, 5, 6, 7) << 8 )  | ( m_gdc_write_mask & 0x00FF );
		break;
	case 5: // 55h   Write Mask HIGH
		m_gdc_write_mask = ( m_gdc_write_mask & 0xFF00 ) | bitswap<8>(data, 0, 1, 2, 3, 4, 5, 6, 7);
		break;

	case 6:
	case 7:
		m_hgdc->write(offset & 0x01, data);
		break;
	} // switch

}


/* F4 Character Displayer */
static const gfx_layout rainbow_charlayout =
{
	8, 10,          /* 8 x 16 characters */
	256,            /* 256 characters */
	1,              /* 1 bits per pixel */
	{ 0 },          /* no bitplanes */
			/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 15 * 8, 0 * 8, 1 * 8, 2 * 8, 3 * 8, 4 * 8, 5 * 8, 6 * 8, 7 * 8, 8 * 8 },
	8 * 16      /* every char takes 16 bytes */
};

static GFXDECODE_START(gfx_rainbow)
	GFXDECODE_ENTRY("chargen", 0x0000, rainbow_charlayout, 0, 1)
GFXDECODE_END

// Allocate 512 K (4 x 64 K x 16 bit) of memory (GDC):
void rainbow_base_state::upd7220_map(address_map &map)
{
	map(0x00000, 0x3ffff).rw(FUNC(rainbow_base_state::vram_r), FUNC(rainbow_base_state::vram_w)).share("vram");
}

void rainbow_base_state::rainbow_base(machine_config &config)
{
	config.set_default_layout(layout_rainbow);

	/* basic machine hardware */
	I8088(config, m_i8088, 24.0734_MHz_XTAL / 5); // approximately 4.815 MHz
	m_i8088->set_irq_acknowledge_callback(FUNC(rainbow_base_state::irq_callback));

	Z80(config, m_z80, 24.0734_MHz_XTAL / 6);
	m_z80->set_addrmap(AS_PROGRAM, &rainbow_base_state::rainbowz80_mem);
	m_z80->set_addrmap(AS_IO, &rainbow_base_state::rainbowz80_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(24.0734_MHz_XTAL / 6, 442, 0, 400, 264, 0, 240); // ~NTSC compatible video timing (?)
	screen.set_screen_update(FUNC(rainbow_base_state::screen_update_rainbow));
	screen.set_palette("vt100_video:palette");
	GFXDECODE(config, "gfxdecode", "vt100_video:palette", gfx_rainbow);

	RAINBOW_VIDEO(config, m_crtc, 24.0734_MHz_XTAL);
	m_crtc->set_screen("screen");
	m_crtc->set_chargen("chargen");
	m_crtc->ram_rd_callback().set(FUNC(rainbow_base_state::read_video_ram_r));
	m_crtc->vert_freq_intr_wr_callback().set(FUNC(rainbow_base_state::video_interrupt));

	// *************************** COLOR GRAPHICS (OPTION) **************************************
	// While the OSC frequency is confirmed, the divider is not (refresh rate is ~60 Hz with 32).
	UPD7220(config, m_hgdc, 31188000 / 32); // Duell schematics shows a 31.188 Mhz oscillator (confirmed by RFKA).
	m_hgdc->vsync_wr_callback().set(FUNC(rainbow_base_state::GDC_vblank_irq)); // "The vsync callback line needs to be below the 7220 DEVICE_ADD line."

	m_hgdc->set_addrmap(0, &rainbow_base_state::upd7220_map);
	m_hgdc->set_display_pixels(FUNC(rainbow_base_state::hgdc_display_pixels));
	m_hgdc->set_screen(m_screen2); // set_screen needs to be added after 7720 device in the machine config, not after the screen.

	PALETTE(config, m_palette2).set_entries(32);

	SCREEN(config, m_screen2, SCREEN_TYPE_RASTER);
	m_screen2->set_video_attributes(VIDEO_UPDATE_AFTER_VBLANK | VIDEO_ALWAYS_UPDATE);

	// VR241 color monitor is specified for 20 MHz bandwidth ( 60 Hz / 15.72 kHz horizontal rate )
	// - sufficient for 800 x 240 non-interlaced at 60 Hz (non interlaced).
	//m_screen2->set_raw(31188000 / 2 , 992, 0, 800, 262, 0, 240);

	// Alternate configuration:
	m_screen2->set_raw(31188000 / 4 , 496, 0, 400, 262, 0, 240);

	m_screen2->set_screen_update("upd7220", FUNC(upd7220_device::screen_update));

	FD1793(config, m_fdc, 24.0734_MHz_XTAL / 24); // no separate 1 Mhz quartz
	FLOPPY_CONNECTOR(config, FD1793_TAG ":0", rainbow_floppies, "525qd", rainbow_base_state::floppy_formats);
	FLOPPY_CONNECTOR(config, FD1793_TAG ":1", rainbow_floppies, "525qd", rainbow_base_state::floppy_formats);
	FLOPPY_CONNECTOR(config, FD1793_TAG ":2", rainbow_floppies, "525qd", rainbow_base_state::floppy_formats);
	FLOPPY_CONNECTOR(config, FD1793_TAG ":3", rainbow_floppies, "525qd", rainbow_base_state::floppy_formats);
	//FLOPPY_CONNECTOR(config, FD1793_TAG ":2", rainbow_floppies, "525dd", rainbow_base_state::floppy_formats);
	//FLOPPY_CONNECTOR(config, FD1793_TAG ":3", rainbow_floppies, "35dd", rainbow_base_state::floppy_formats);
	SOFTWARE_LIST(config, "flop_list").set_original("rainbow");

	/// ********************************* HARD DISK CONTROLLER *****************************************
	WD2010(config, m_hdc, 5000000); // 10 Mhz quartz on controller (divided by 2 for WCLK)
	m_hdc->out_intrq_callback().set(FUNC(rainbow_base_state::bundle_irq)); // FIRST IRQ SOURCE (OR'ed with DRQ)
	m_hdc->out_bdrq_callback().set(FUNC(rainbow_base_state::hdc_bdrq));  // BUFFER DATA REQUEST

	// SIGNALS -FROM- WD CONTROLLER:
	m_hdc->out_bcs_callback().set(FUNC(rainbow_base_state::hdc_read_sector)); // Problem: OUT_BCS_CB = WRITE8 ... (!)
	m_hdc->out_bcr_callback().set(FUNC(rainbow_base_state::hdc_bcr));         // BUFFER COUNTER RESET (pulses)

	m_hdc->out_wg_callback().set(FUNC(rainbow_base_state::hdc_write_sector));   // WRITE GATE
	m_hdc->out_step_callback().set(FUNC(rainbow_base_state::hdc_step));         // STEP PULSE
	m_hdc->out_dirin_callback().set(FUNC(rainbow_base_state::hdc_direction));

	// WF + DRDY are actually wired to a routine here:
	m_hdc->in_wf_callback().set(FUNC(rainbow_base_state::hdc_write_fault));   // WRITE FAULT (fatal until next reset)
	m_hdc->in_drdy_callback().set(FUNC(rainbow_base_state::hdc_drive_ready)); // DRIVE_READY (VCC = ready)

	// Always set seek complete and track 00 signal (not super clean, but does not affect operation):
	m_hdc->in_sc_callback().set_constant(1);                             // SEEK COMPLETE (VCC = complete)
	m_hdc->in_tk000_callback().set_constant(1);                  // TRACK 00 signal (= from drive)

	HARDDISK(config, "decharddisk1");
	/// ******************************** / HARD DISK CONTROLLER ****************************************

	CORVUS_HDC(config, m_corvus_hdc, 0);
	HARDDISK(config, "harddisk1", "corvus_hdd");
	HARDDISK(config, "harddisk2", "corvus_hdd");
	HARDDISK(config, "harddisk3", "corvus_hdd");
	HARDDISK(config, "harddisk4", "corvus_hdd");

	COM8116_003(config, m_dbrg, 24.0734_MHz_XTAL / 4); // 6.01835 MHz (nominally 6 MHz)
	m_dbrg->fr_handler().set(FUNC(rainbow_base_state::dbrg_fr_w));
	m_dbrg->ft_handler().set(FUNC(rainbow_base_state::dbrg_ft_w));

	UPD7201(config, m_mpsc, 24.0734_MHz_XTAL / 5 / 2); // 2.4073 MHz (nominally 2.5 MHz)
	m_mpsc->out_int_callback().set(FUNC(rainbow_base_state::mpsc_irq));
	m_mpsc->out_txda_callback().set(m_comm_port, FUNC(rs232_port_device::write_txd));
	m_mpsc->out_txdb_callback().set("printer", FUNC(rs232_port_device::write_txd));
	// RTS and DTR outputs are not connected

	RS232_PORT(config, m_comm_port, default_rs232_devices, nullptr);
	m_comm_port->rxd_handler().set(m_mpsc, FUNC(upd7201_device::rxa_w));
	m_comm_port->cts_handler().set(m_mpsc, FUNC(upd7201_device::ctsa_w));
	m_comm_port->dcd_handler().set(m_mpsc, FUNC(upd7201_device::dcda_w));

	rs232_port_device &printer(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	printer.rxd_handler().set(m_mpsc, FUNC(upd7201_device::rxb_w));
	printer.dcd_handler().set(m_mpsc, FUNC(upd7201_device::ctsb_w)); // actually DTR

	m_comm_port->option_add("microsoft_mouse", MSFT_HLE_SERIAL_MOUSE);
	m_comm_port->option_add("logitech_mouse", LOGITECH_HLE_SERIAL_MOUSE);
	m_comm_port->set_default_option("logitech_mouse");

	printer.set_default_option("printer");

	I8251(config, m_kbd8251, 24.0734_MHz_XTAL / 5 / 2);
	m_kbd8251->txd_handler().set(FUNC(rainbow_base_state::kbd_tx));
	m_kbd8251->rxrdy_handler().set(FUNC(rainbow_base_state::kbd_rxready_w));
	m_kbd8251->txrdy_handler().set(FUNC(rainbow_base_state::kbd_txready_w));

	LK201(config, m_lk201, 0);
	m_lk201->tx_handler().set(m_kbd8251, FUNC(i8251_device::write_rxd));

	ripple_counter_device &prtbrg(RIPPLE_COUNTER(config, "prtbrg", 24.0734_MHz_XTAL / 6 / 13)); // 74LS393 at E17 (both halves)
	// divided clock should ideally be 307.2 kHz, but is actually approximately 308.6333 kHz
	prtbrg.set_stages(8);
	prtbrg.count_out_cb().set(FUNC(rainbow_base_state::bitrate_counter_w));

	TIMER(config, "motor").configure_periodic(FUNC(rainbow_base_state::hd_motor_tick), attotime::from_hz(60));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);
}

void rainbow_modela_state::rainbow_modela(machine_config &config)
{
	rainbow_base(config);
	m_i8088->set_addrmap(AS_PROGRAM, &rainbow_modela_state::rainbow8088_map);
	m_i8088->set_addrmap(AS_IO, &rainbow_modela_state::rainbow8088_io);
	RAM(config, m_ram).set_default_size("64K").set_extra_options("64K,128K,192K,256K,320K,384K,448K,512K,576K,640K,704K,768K");
	m_kbd8251->dtr_handler().set(FUNC(rainbow_modela_state::irq_hi_w));

	DS1215(config, m_rtc); // DS1315 (ClikClok for DEC-100 B)   * OPTIONAL *
}

void rainbow_modelb_state::rainbow_modelb(machine_config &config)
{
	rainbow_base(config);
	m_i8088->set_addrmap(AS_PROGRAM, &rainbow_modelb_state::rainbow8088_map);
	m_i8088->set_addrmap(AS_IO, &rainbow_modelb_state::rainbow8088_io);
	RAM(config, m_ram).set_default_size("128K").set_extra_options("128K,192K,256K,320K,384K,448K,512K,576K,640K,704K,768K,832K,896K");
	m_kbd8251->dtr_handler().set(FUNC(rainbow_modelb_state::irq_hi_w));

	DS1216E(config, m_rtc); // DS1315 (ClikClok for DEC-100 B)   * OPTIONAL *
}

//----------------------------------------------------------------------------------------
// 'Rainbow 100-A' (system module 70-19974-00, PSU H7842-A)
// - first generation hardware (introduced May '82) with ROM 04.03.11
// - inability to boot from hard disc (mind the inadequate PSU)
//----------------------------------------------------------------------------------------
// AVAILABLE RAM: 64 K on board (versus 128 K on model 'B').

// Two compatible memory expansions were sold by DEC:
// (PCIXX-AA) : 64 K (usable on either Rainbow 100-A or 100-B) *
// (PCIXX-AB) : 192 K ( " )  *
// Totals to 256 K on a 100-A, while the RAM limit appears to be 832 K.

// * DEC changed the way signals are handled on J6 (memory connector) later:
//  "Whether a PC100-A or PC100-B memory module is installed on the PC100-B system module
//   affects the functions the signals on 5 pins (29, 30, 32, 43, and 47) of the J6 connector
//   will perform." (from 'EK-RB100_TM_001 Addendum for PC100-A_PC100-B Dec.84' page 120).
//----------------------------------------------------------------------------------------
// KNOWN DIFFERENCES TO 100-B:
// - cannot control bit 7 of IRQ vector (prevents DOS > 2.01 from booting on unmodified hardware)
// - 4 color palette with graphics option (instead of 16 colors on later models)
// - smaller ROMs (3 x 2764) with fewer routines (no documented way to beep...)
// - socketed NVRAM chip: X2212D 8238AES
ROM_START(rainbow100a)
	ROM_REGION(0x100000, "maincpu", 0)

	ROM_DEFAULT_BIOS("040311a")
	// printer and comm. port error messages, keyboard works, machine boots
	ROM_SYSTEM_BIOS(0, "040311a", "Version 04.03.11A")
	ROMX_LOAD("23-176e4-00.e89", 0xfa000, 0x2000, CRC(405e9619) SHA1(86604dccea84b46e05d705abeda25b12f7cc8c59), ROM_BIOS(0)) // ROM (FA000-FBFFF) (E89) 8 K
	ROMX_LOAD("23-177e4-00.e90", 0xfc000, 0x2000, CRC(1ec72a66) SHA1(ed19944ae711e97d6bec34c885be04c4c3c95852), ROM_BIOS(0)) // ROM (FC000-FDFFF) (E90) 8 K
	ROM_FILL(0xfa26d, 1, 0x00) // [0xFA000 + 0x026d] disable CRC check   [100-A ROM]
	ROM_FILL(0xfadea, 1, 0x00) // [0xFA000 + 0x0dea] Floppy workaround: in case of Z80 RESPONSE FAILURE ($80 bit set in AL), don't block floppy access

	// printer, comm. and a third error message, no keyboard, doesn't boot
	ROM_SYSTEM_BIOS(1, "010111a", "Version 01.01.11A")
	ROMX_LOAD("23-090e4-0_11-24.bin", 0xfa000, 0x2000, CRC(08d83c93) SHA1(ee26c3162071ccc3f7dce1c2afb9933329a287de), ROM_BIOS(1)) // ROM (FA000-FBFFF) (E89) 8 K
	ROMX_LOAD("23-091e4-0_11-24.bin", 0xfc000, 0x2000, CRC(387c50f2) SHA1(598f04ccbdf0e34826785992a56891d6674ac47e), ROM_BIOS(1)) // ROM (FC000-FDFFF) (E90) 8 K


	// SOCKETED LANGUAGE ROM (E91) with 1 single localization per ROM -
	ROM_LOAD("23-092e4-00.e91", 0xfe000, 0x2000, CRC(c269175a) SHA1(e82cf69b811f1e376621277f81db28e299fe06f0))  // ROM (FE000-FFFFF) (E91) 8 K - English (?)
	// See also MP-01491-00 - PC100A FIELD MAINTENANCE SET. Appendix A of EK-RB100 Rainbow
	// Technical Manual Addendum f.100A and 100B (Dec.84) lists 15 localizations / part numbers

	ROM_REGION(0x1000, "chargen", 0) // [E98] 2732 (4 K) EPROM
	ROM_LOAD("23-020e3-00.e98", 0x0000, 0x1000, CRC(b5ee2824) SHA1(8e940e32f39ec5c51cae0351ddd59ab06416d5c6))

	// Z80 ARBITRATION PROM
	ROM_REGION(0x100, "prom", 0)
	ROM_LOAD("23-090b1.mmi6308-ij.e11", 0x0000, 0x0100, CRC(cac3a7e3) SHA1(2d0468cda36fa287f705364c56dbf62f548d2e4c) ) // MMI 6308-IJ; Silkscreen stamp: "LM8413 // 090B1"; 256x8 Open Collector prom @E11, same prom is @E13 on 100-B
ROM_END


//----------------------------------------------------------------------------------------
// ROM definition for 100-B (system module 70-19974-02, PSU H7842-D)
// Built until ~ May 1986 (from MP-01491-00)
// - 32 K ROM (version 5.03)
// - 128 K base and 896 K max. mem.
ROM_START(rainbow)
	ROM_REGION(0x100000, "maincpu", 0)

	// Note that the 'Field Maintenance Print Set 1984' also lists alternate revision 'A1' with
	//              23-063e3-00 (for chargen) and '23-074e5-00' / '23-073e5-00' for E5-01 / E5-02.

	// Part numbers 22E5, 20E5 and 37E3 verified to match revision "B" (FCC ID : A0994Q - PC100 - B).

	// BOOT ROM
	ROM_LOAD("23-022e5-00.bin", 0xf0000, 0x4000, CRC(9d1332b4) SHA1(736306d2a36bd44f95a39b36ebbab211cc8fea6e))
	ROM_RELOAD(0xf4000, 0x4000)
	ROM_FILL(0xf4303, 1, 0x00) // [0xf4000 + 0x0303] disable CRC check   [100-B ROM]
	ROM_FILL(0xf535e, 1, 0x00) // [0xf4000 + 0x135e] Floppy workaround: in case of Z80 RESPONSE FAILURE ($80 bit set in AL), don't block floppy access


	// LANGUAGE ROM
	ROM_LOAD("23-020e5-00.bin", 0xf8000, 0x4000, CRC(8638712f) SHA1(8269b0d95dc6efbe67d500dac3999df4838625d8)) // German, French, English
	//ROM_LOAD( "23-015e5-00.bin", 0xf8000, 0x4000, NO_DUMP) // Dutch, French, English
	//ROM_LOAD( "23-016e5-00.bin", 0xf8000, 0x4000, NO_DUMP) // Finish, Swedish, English
	//ROM_LOAD( "23-017e5-00.bin", 0xf8000, 0x4000, NO_DUMP) // Danish, Norwegian, English
	//ROM_LOAD( "23-018e5-00.bin", 0xf8000, 0x4000, NO_DUMP) // Spanish, Italian, English
	ROM_RELOAD(0xfc000, 0x4000)

	// CHARACTER GENERATOR (E3-03)
	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("23-037e3.bin", 0x0000, 0x1000, CRC(1685e452) SHA1(bc299ff1cb74afcededf1a7beb9001188fdcf02f)) // the 'invalid character' symbol and the yen symbol were changed vs 23-020e3 from 100a

	// Z80 ARBITRATION PROM
	ROM_REGION(0x100, "prom", 0)
	ROM_LOAD("23-090b1.mmi6308-ij.e13", 0x0000, 0x0100, CRC(cac3a7e3) SHA1(2d0468cda36fa287f705364c56dbf62f548d2e4c) ) // MMI 6308-IJ; Silkscreen stamp: "LM8413 // 090B1"; 256x8 Open Collector prom @E13, same prom is @E11 on 100-A
ROM_END

//----------------------------------------------------------------------------------------
// 'Rainbow 190 B' (announced March 1985) is identical to 100-B, with alternate ROM v5.05.
// According to an article in Wall Street Journal it came with a 10 MB HD and 640 K RAM.

// All programs not dependent on specific ROM addresses should work. A first glance:
// - jump tables (F4000-F40083 and FC000-FC004D) were not extended
// - absolute addresses of some internal routines have changed (affects BOOT 2.x / 3.x dual boot)

// A Readme from January 1985 mentions 'recent ROM changes for MASS 11' (a VAX word processor).
// It is *likely* that the sole differences between 5.05 and 5.03 affect terminal emulation.

ROM_START(rainbow190)
	ROM_REGION(0x100000, "maincpu", 0)
	ROM_LOAD("dec190rom0.bin", 0xf0000, 0x4000, CRC(fac191d2) SHA1(4aff5b1e031d3b5eafc568b23e68235270bb34de)) //FIXME: need correct rom name
	ROM_RELOAD(0xf4000, 0x4000)
	ROM_LOAD("dec190rom1.bin", 0xf8000, 0x4000, CRC(5ce59632) SHA1(d29793f7014c57a4e7cb77bbf6e84f9113635ed2)) //FIXME: need correct rom name

	ROM_RELOAD(0xfc000, 0x4000)
	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("23-037e3.bin", 0x0000, 0x1000, CRC(1685e452) SHA1(bc299ff1cb74afcededf1a7beb9001188fdcf02f))

	// Z80 ARBITRATION PROM
	ROM_REGION(0x100, "prom", 0)
	ROM_LOAD("23-090b1.mmi6308-ij.e13", 0x0000, 0x0100, CRC(cac3a7e3) SHA1(2d0468cda36fa287f705364c56dbf62f548d2e4c) ) // MMI 6308-IJ; Silkscreen stamp: "LM8413 // 090B1"; 256x8 Open Collector prom @E13, same prom is @E11 on 100-A
ROM_END
//----------------------------------------------------------------------------------------

} // anonymous namespace


/* Driver */

/*   YEAR  NAME         PARENT   COMPAT  MACHINE         INPUT           STATE                 INIT        COMPANY                          FULLNAME         FLAGS */
COMP(1982, rainbow100a, rainbow, 0,      rainbow_modela, rainbow100b_in, rainbow_modela_state, empty_init, "Digital Equipment Corporation", "Rainbow 100-A", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP(1983, rainbow,     0,       0,      rainbow_modelb, rainbow100b_in, rainbow_modelb_state, empty_init, "Digital Equipment Corporation", "Rainbow 100-B", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_COLORS)
COMP(1985, rainbow190,  rainbow, 0,      rainbow_modelb, rainbow100b_in, rainbow_modelb_state, empty_init, "Digital Equipment Corporation", "Rainbow 190-B", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_COLORS)



rambo.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Felipe Sanches
/*
    RAMBo (RepRap Arduino-compatible Mother Board) by UltiMachine
    for controlling desktop 3d printers
    http://reprap.org/wiki/Rambo

    driver by Felipe Correa da Silva Sanches <fsanches@metamaquina.com.br>

    This driver is based on the schematics of the version 1.1b:
    http://reprap.org/mediawiki/images/7/75/Rambo1-1-schematic.png

    3d printers currently supported by this driver:
    * Metam??quina 2

    3d printers known to use this board:
    * TODO: list them all here
*/

#include "emu.h"
#include "cpu/avr8/avr8.h"


namespace {

#define MASTER_CLOCK    16000000

/****************************************************\
* I/O devices                                        *
\****************************************************/

class rambo_state : public driver_device
{
public:
	rambo_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu")
	{
	}

	void rambo(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void rambo_prg_map(address_map &map) ATTR_COLD;
	void rambo_data_map(address_map &map) ATTR_COLD;

	uint8_t m_port_a = 0;
	required_device<atmega2560_device> m_maincpu;
};

/****************************************************\
* Address maps                                       *
\****************************************************/

void rambo_state::rambo_prg_map(address_map &map)
{
	map(0x00000, 0x3ffff).rom();
}

void rambo_state::rambo_data_map(address_map &map)
{
	map(0x0200, 0x21FF).ram();  /* ATMEGA2560 Internal SRAM */
}

/****************************************************\
* Machine definition                                 *
\****************************************************/

void rambo_state::machine_start()
{
	save_item(NAME(m_port_a));
}

void rambo_state::machine_reset()
{
	m_port_a = 0;
}

void rambo_state::rambo(machine_config &config)
{
	ATMEGA2560(config, m_maincpu, MASTER_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &rambo_state::rambo_prg_map);
	m_maincpu->set_addrmap(AS_DATA, &rambo_state::rambo_data_map);
	m_maincpu->set_eeprom_tag("eeprom");
	m_maincpu->set_low_fuses(0xff);
	m_maincpu->set_high_fuses(0xda);
	m_maincpu->set_extended_fuses(0xf4);
	m_maincpu->set_lock_bits(0x0f);
	m_maincpu->gpio_in<atmega2560_device::GPIOA>().set([this]() { return m_port_a; });
	m_maincpu->gpio_out<atmega2560_device::GPIOA>().set([this](uint8_t data) { m_port_a = data; });

	/*TODO: Add an ATMEGA32U2 for USB-Serial communications */
	/*TODO: Emulate the AD5206 digipot */
	/*TODO: Emulate the A4982 stepper motor drivers and instantiate 5 of these here
	        for controlling the X, Y, Z, E1 (and optionally E2) motors */
	/*TODO: Simulate the heating elements */
	/*TODO: Implement the thermistor measurements */
}

ROM_START( metamaq2 )
	ROM_REGION( 0x40000, "maincpu", 0 )
	ROM_DEFAULT_BIOS("20131015")

	ROM_SYSTEM_BIOS( 0, "20130619", "June 19th, 2013" )
	/* SOURCE(https://github.com/Metamaquina/Repetier-Firmware/tree/MM2_2013_06_19) */
	ROMX_LOAD("repetier-fw-metamaquina2-2013-06-19.bin", 0x0000, 0x1000e, CRC(4279b178) SHA1(e4d3c9d6421287c980639c2df32d07b754adc8fc), ROM_BIOS(0))

	ROM_SYSTEM_BIOS( 1, "20130624", "June 24th, 2013" )
	/* SOURCE(https://github.com/Metamaquina/Repetier-Firmware/tree/MM2_RC2_RAMBo_rev10e_2013_06_24) */
	ROMX_LOAD("repetier-fw-metamaquina2-2013-06-24_mm2rc2_rambo_rev10e.bin", 0x0000, 0xcebc, CRC(82400a3c) SHA1(0781ce29406ce69b63edb93d776b9c081bed841e), ROM_BIOS(1))

	ROM_SYSTEM_BIOS( 2, "20130625", "June 25th, 2013" )
	/* SOURCE(https://github.com/Metamaquina/Repetier-Firmware/tree/MM2_2013_06_25) */
	ROMX_LOAD("repetier-fw-metamaquina2-2013-06-25.bin", 0x0000, 0x10076, CRC(e7e4db38) SHA1(0c307bb0a0ee4e9d38253936e7030d0efb3c1845), ROM_BIOS(2))

	ROM_SYSTEM_BIOS( 3, "20130709", "July 9th, 2013" )
	/* SOURCE(https://github.com/Metamaquina/Repetier-Firmware/tree/MM2_2013_07_09) */
	ROMX_LOAD("repetier-fw-metamaquina2-2013-07-09.bin", 0x0000, 0x10078, CRC(9a45509f) SHA1(3a2e6516b45cc0ea1aef039335b02208847aaebf), ROM_BIOS(3))

	ROM_SYSTEM_BIOS( 4, "20130712", "July 12th, 2013" )
	/* SOURCE(https://github.com/Metamaquina/Repetier-Firmware/tree/MM2_2013_07_12) */
	ROMX_LOAD("repetier-fw-metamaquina2-2013-07-12.bin", 0x0000, 0x10184, CRC(9aeac87c) SHA1(c1441096553c214c12a34da87fa42cc3f0eaf74d), ROM_BIOS(4))

	ROM_SYSTEM_BIOS( 5, "20130717", "July 17th, 2013" )
	/* SOURCE(https://github.com/Metamaquina/Repetier-Firmware/tree/MM2_2013_07_17) */
	ROMX_LOAD("repetier-fw-metamaquina2-2013-07-17.bin", 0x0000, 0x10180, CRC(7c053ed0) SHA1(7abeabcbfdb411b6e681e2d0c9398c40b142f76b), ROM_BIOS(5))

	ROM_SYSTEM_BIOS( 6, "20130806", "August 6th, 2013" )
	/* SOURCE(https://github.com/Metamaquina/Repetier-Firmware/tree/MM2_2013_08_06) */
	ROMX_LOAD("repetier-fw-metamaquina2-2013-08-06.bin", 0x0000, 0x1017e, CRC(6aaf5a14) SHA1(93cebee8ab9eda9d81e70504b407268a198577f0), ROM_BIOS(6))

	ROM_SYSTEM_BIOS( 7, "20130809", "August 9th, 2013" )
	/* SOURCE(https://github.com/Metamaquina/Repetier-Firmware/tree/MM2_2013_08_09) */
	ROMX_LOAD("repetier-fw-metamaquina2-2013-08-09.bin", 0x0000, 0x1018a, CRC(ee53a011) SHA1(666d09fe69220a172528fe8d1c358e3ddaaa743a), ROM_BIOS(7))

	ROM_SYSTEM_BIOS( 8, "20130822", "August 22nd, 2013" )
	/* SOURCE(https://github.com/Metamaquina/Repetier-Firmware/tree/MM2_2013_08_22) */
	ROMX_LOAD("repetier-fw-metamaquina2-2013-08-22.bin", 0x0000, 0x1018a, CRC(70a5a3c9) SHA1(20e52ea7bf40e71020b815b9fb6385d880677927), ROM_BIOS(8))

	ROM_SYSTEM_BIOS( 9, "20130913", "September 13th, 2013" )
	/* source code for this one is unavailable as it was an unreleased internal development build */
	ROMX_LOAD("repetier-fw-metamaquina2-2013-09-13-devel.bin", 0x0000, 0x101bc, CRC(5e7c7933) SHA1(5b9bfe919daf705ad7a9a2de3cf4c51e3338ec47), ROM_BIOS(9))

	ROM_SYSTEM_BIOS( 10, "20130920", "September 20th, 2013" )
	/* SOURCE(https://github.com/Metamaquina/Repetier-Firmware/tree/MM2_2013_09_20) */
	ROMX_LOAD("repetier-fw-metamaquina2-2013-09-20.bin", 0x0000, 0x10384, CRC(48378e58) SHA1(513f0a0c65219875cc467420cc091e3489b58919), ROM_BIOS(10))

	ROM_SYSTEM_BIOS( 11, "20131015", "October 15th, 2013" )
	/* SOURCE(https://github.com/Metamaquina/Repetier-Firmware/tree/MM2_2013_10_15) */
	ROMX_LOAD("repetier-fw-metamaquina2-2013-10-15.bin", 0x0000, 0x102c8, CRC(520134bd) SHA1(dfe2251aad06972f237eb4920ce14ccb32da5af0), ROM_BIOS(11))

	/*Arduino MEGA bootloader */
	/* This is marked as a BAD_DUMP because we're not sure this is the bootloader we're actually using.
	   This is inherited from the Replicator 1 driver.
	   A proper dump would be good.
	   Also, it is not clear whether there's any difference in the bootloader
	   between the ATMEGA1280 and the ATMEGA2560 MCUs */
	ROM_LOAD( "atmegaboot_168_atmega1280.bin", 0x3f000, 0x0f16, BAD_DUMP CRC(c041f8db) SHA1(d995ebf360a264cccacec65f6dc0c2257a3a9224) )

	/* on-die 4kbyte eeprom */
	ROM_REGION( 0x1000, "eeprom", ROMREGION_ERASEFF )
ROM_END

} // anonymous namespace


//   YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY        FULLNAME                            FLAGS
COMP(2012, metamaq2, 0,      0,      rambo,   0,     rambo_state, empty_init, "Metamaquina", "Metamaquina 2 desktop 3d printer", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



ravens.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Ravensburger Selbstbaucomputer

This is a project described in "Ravensburger" magazine. You had to make
the entire thing (including the circuit boards) yourself.


https://web.archive.org/web/20160321001634/http://petersieg.bplaced.com/?2650_Computer:2650_Selbstbaucomputer

2013-04-23 Skeleton driver.

No instructions, no schematics - it's all guesswork.

The cassette saves a noise but it returns a bad load. This is why MNW is set.


Version 0.9
-----------
Hardware:
        0000-07FF ROM "MON1"
        0800-1FFF RAM (3x HM6116)
        24 pushbuttons and 6-digit LED display on front panel.
        Other buttons and switches on the main board.

The few photos show the CPU and a number of ordinary 74LSxxx chips.
There is a XTAL of unknown frequency.

The buttons are labelled CMD, RUN, GOTO, RST, F, MON, PC, NXT but at
this time not all buttons are identified.

What is known:
- Press NXT to read memory. Press NXT again to read the next address.
- Press PC and it says PCxxxx
- Press CMD, it says CND=, you can choose one of these:
-- A displays value of a register. Press A again to see more registers.
-- B sets a breakpoint
-- C clears a breakpoint
-- D dumps blocks to tape
-- E examine tape file
-- F fetch (load) from tape

Quickload: Load the program then press Y. There are 6 that work and
           6 that do nothing.

ToDo:
- Cassette

Version V2.0
------------
This used a terminal interface with a few non-standard control codes.
The pushbuttons and LEDs appear to have been done away with.

Commands (must be in uppercase):
A    Examine memory; press C to alter memory
B    Set breakpoint?
C    View breakpoint?
D    Dump to screen and tape (at the same time) D 00 04 dumps pages 0 to 4
E    Execute
I    Registers? (Esc to quit)
L    Load
R    ? (Esc to quit)
V    Verify

ToDo:
- Cassette

****************************************************************************/

#include "emu.h"

#include "cpu/s2650/s2650.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "machine/terminal.h"
#include "speaker.h"

#include "ravens.lh"


namespace {

class ravens_base : public driver_device
{
public:
	ravens_base(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cass(*this, "cassette")
	{ }

protected:
	int cass_r();
	void cass_w(int state);
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);
	void mem_map(address_map &map) ATTR_COLD;
	required_device<s2650_device> m_maincpu;
	required_device<cassette_image_device> m_cass;
};

class ravens_state : public ravens_base
{
public:
	ravens_state(const machine_config &mconfig, device_type type, const char *tag)
		: ravens_base(mconfig, type, tag)
		, m_io_keyboard(*this, "X%u", 0U)
		, m_digits(*this, "digit%u", 0U)
		, m_leds(*this, "led%u", 0U)
	{ }

	void ravens(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void io_map(address_map &map) ATTR_COLD;
	u8 port17_r();
	void display_w(offs_t offset, u8 data);
	void leds_w(u8 data);

	required_ioport_array<3> m_io_keyboard;
	output_finder<7> m_digits;
	output_finder<8> m_leds;
};

class ravens2_state : public ravens_base
{
public:
	ravens2_state(const machine_config &mconfig, device_type type, const char *tag)
		: ravens_base(mconfig, type, tag)
		, m_terminal(*this, "terminal")
	{ }

	void ravens2(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	void io_map(address_map &map) ATTR_COLD;
	void kbd_put(u8 data);
	u8 port07_r();
	void port1b_w(u8 data);
	void port1c_w(u8 data);
	u8 m_term_out = 0U;
	u8 m_term_in = 0U;
	required_device<generic_terminal_device> m_terminal;
};

void ravens_base::cass_w(int state)
{
	m_cass->output(state ? -1.0 : +1.0);
}

int ravens_base::cass_r()
{
	return (m_cass->input() > 0.03) ? 1 : 0;
}

void ravens_state::machine_start()
{
	m_digits.resolve();
	m_leds.resolve();
}

void ravens_state::display_w(offs_t offset, u8 data)
{
	m_digits[offset] = data;
}

void ravens_state::leds_w(u8 data)
{
	for (int i = 0; i < 8; i++)
		m_leds[i] = !BIT(data, i);
}

u8 ravens2_state::port07_r()
{
	u8 ret = m_term_in;
	if (!machine().side_effects_disabled())
		m_term_in = 0x80;
	return ret;
}

u8 ravens_state::port17_r()
{
	u8 keyin, i;

	keyin = m_io_keyboard[0]->read();
	if (keyin != 0xff)
		for (i = 0; i < 8; i++)
			if (BIT(~keyin, i))
				return i | 0x80;

	keyin = m_io_keyboard[1]->read();
	if (keyin != 0xff)
		for (i = 0; i < 8; i++)
			if (BIT(~keyin, i))
				return i | 0x88;

	keyin = m_io_keyboard[2]->read();
	if (!BIT(keyin, 0))
		m_maincpu->reset();
	if (keyin != 0xff)
		for (i = 0; i < 8; i++)
			if (BIT(~keyin, i))
				return (i<<4) | 0x80;

	return 0;
}

void ravens2_state::port1b_w(u8 data)
{
	if (BIT(data, 7))
		return;
	if (data == 1) // don't send PSU register to terminal
		return;
	else
	if ((data == 0x08 && m_term_out == 0x20))
		data = 0x0c; // FormFeed
	else
	if ((data == 0x0a && m_term_out == 0x20))
		data = 0x0a; // LineFeed
	else
		data = m_term_out;

	m_terminal->write(data);
}

void ravens2_state::port1c_w(u8 data)
{
	m_term_out = data;
}

void ravens2_state::machine_reset()
{
	m_term_in = 0x80;
}

void ravens2_state::machine_start()
{
	save_item(NAME(m_term_out));
	save_item(NAME(m_term_in));
}

void ravens_base::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).rom();
	map(0x0800, 0x1fff).ram();
	map(0x2000, 0x7FFF).ram(); // for quickload, optional
}

void ravens_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x09, 0x09).w(FUNC(ravens_state::leds_w)); // LED output port
	map(0x10, 0x15).w(FUNC(ravens_state::display_w)); // 6-led display
	map(0x17, 0x17).r(FUNC(ravens_state::port17_r)); // pushbuttons
}

void ravens2_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x07, 0x07).r(FUNC(ravens2_state::port07_r));
	map(0x1b, 0x1b).w(FUNC(ravens2_state::port1b_w));
	map(0x1c, 0x1c).w(FUNC(ravens2_state::port1c_w));
}

/* Input ports */
static INPUT_PORTS_START( ravens )
	PORT_START("X0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')

	PORT_START("X1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("X2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RST") PORT_CODE(KEYCODE_F3)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("NXT") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RUN") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PC")  PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("???") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("???") PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CMD") PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("???") PORT_CODE(KEYCODE_O) PORT_CHAR('O')
INPUT_PORTS_END

void ravens2_state::kbd_put(u8 data)
{
	if (data > 0x60) data -= 0x20; // fold to uppercase
	m_term_in = data;
}

QUICKLOAD_LOAD_MEMBER(ravens_base::quickload_cb)
{
	int const quick_length = image.length();
	if (quick_length < 0x0900)
		return std::make_pair(image_error::INVALIDLENGTH, "File too short");
	else if (quick_length > 0x8000)
		return std::make_pair(image_error::INVALIDLENGTH, "File too long (must be no more than 32K)");

	std::vector<u8> quick_data;
	quick_data.resize(quick_length);
	int const read_ = image.fread( &quick_data[0], quick_length);
	if (read_ != quick_length)
		return std::make_pair(image_error::UNSPECIFIED, "Cannot read the file");
	else if (quick_data[0] != 0xc6)
		return std::make_pair(image_error::INVALIDIMAGE, "Invalid header");

	int const exec_addr = quick_data[2] * 256 + quick_data[3];
	if (exec_addr >= quick_length)
	{
		return std::make_pair(
				image_error::INVALIDIMAGE,
				util::string_format("Exec address %04X beyond end of file %04X", exec_addr, quick_length));
	}

	constexpr int QUICK_ADDR = 0x900;
	address_space &space = m_maincpu->space(AS_PROGRAM);
	for (int i = QUICK_ADDR; i < read_; i++)
		space.write_byte(i, quick_data[i]);

	// display a message about the loaded quickload
	image.message(" Quickload: size=%04X : exec=%04X",quick_length,exec_addr);

	// Start the quickload
	m_maincpu->set_state_int(S2650_PC, exec_addr);

	return std::pair(std::error_condition(), std::string());
}

void ravens_state::ravens(machine_config &config)
{
	/* basic machine hardware */
	S2650(config, m_maincpu, XTAL(1'000'000)); // frequency is unknown
	m_maincpu->set_addrmap(AS_PROGRAM, &ravens_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &ravens_state::io_map);
	m_maincpu->sense_handler().set(FUNC(ravens_state::cass_r));
	m_maincpu->flag_handler().set(FUNC(ravens_state::cass_w));

	/* video hardware */
	config.set_default_layout(layout_ravens);

	/* quickload */
	QUICKLOAD(config, "quickload", "pgm", attotime::from_seconds(1)).set_load_callback(FUNC(ravens_state::quickload_cb));

	SPEAKER(config, "mono").front_center();

	/* cassette */
	CASSETTE(config, m_cass);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
}

void ravens2_state::ravens2(machine_config &config)
{
	/* basic machine hardware */
	S2650(config, m_maincpu, XTAL(1'000'000)); // frequency is unknown
	m_maincpu->set_addrmap(AS_PROGRAM, &ravens2_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &ravens2_state::io_map);
	m_maincpu->sense_handler().set(FUNC(ravens2_state::cass_r));
	m_maincpu->flag_handler().set(FUNC(ravens2_state::cass_w));

	/* video hardware */
	GENERIC_TERMINAL(config, m_terminal, 0);
	m_terminal->set_keyboard_callback(FUNC(ravens2_state::kbd_put));

	/* quickload */
	QUICKLOAD(config, "quickload", "pgm", attotime::from_seconds(1)).set_load_callback(FUNC(ravens2_state::quickload_cb));

	SPEAKER(config, "mono").front_center();

	/* cassette */
	CASSETTE(config, m_cass);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
}

/* ROM definition */
ROM_START( ravens )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "v1.0", "V1.0" )
	ROMX_LOAD( "mon_v1.0.bin", 0x0000, 0x0800, CRC(785eb1ad) SHA1(c316b8ac32ab6aa37746af37b9f81a23367fedd8), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v0.9", "V0.9" )
	ROMX_LOAD( "mon_v0_9.bin", 0x0000, 0x07b5, CRC(2f9b9178) SHA1(ec2ebbc80ee9ff2502c1409ab4f99127032ed724), ROM_BIOS(1))
ROM_END

ROM_START( ravens2 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mon_v2.0.bin", 0x0000, 0x0800, CRC(bcd47c58) SHA1(f261a3f128fbedbf59a8b5480758fff4d7f76de1))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS          INIT        COMPANY                            FULLNAME                               FLAGS */
COMP( 1984, ravens,  0,      0,      ravens,  ravens, ravens_state,  empty_init, "Joseph Glagla and Dieter Feiler", "Ravensburger Selbstbaucomputer V0.9", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1985, ravens2, ravens, 0,      ravens2, ravens, ravens2_state, empty_init, "Joseph Glagla and Dieter Feiler", "Ravensburger Selbstbaucomputer V2.0", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



rc2014.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/**************************************************************************************

    RC2014 Modular Computer

**************************************************************************************/

#include "emu.h"
#include "bus/rc2014/rc2014.h"
#include "bus/rc2014/modules.h"

namespace {

class rc2014_state : public driver_device
{
public:
	rc2014_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_bus(*this, "bus")
	{ }

	//
	// RC2014 Classic
	//
	// Z80 CPU module
	// Clock/Reset
	// 32K RAM module
	// Switchable ROM module
	// Serial I/O
	//
	// Backplane-5 - 5 x 40pin slots
	//
	void rc2014(machine_config &config)
	{
		RC2014_BUS(config, m_bus, 0);
		RC2014_SLOT(config, "bus:1", m_bus, rc2014_bus_modules, "z80");
		RC2014_SLOT(config, "bus:2", m_bus, rc2014_bus_modules, "clock");
		RC2014_SLOT(config, "bus:3", m_bus, rc2014_bus_modules, "ram32k");
		RC2014_SLOT(config, "bus:4", m_bus, rc2014_bus_modules, "sw_rom");
		RC2014_SLOT(config, "bus:5", m_bus, rc2014_bus_modules, "serial");
	}

	//
	// Backplane-5 - 5 x 40pin slots
	//
	void rc2014bp5(machine_config &config)
	{
		RC2014_BUS(config, m_bus, 0);
		RC2014_SLOT(config, "bus:1", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:2", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:3", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:4", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:5", m_bus, rc2014_bus_modules, nullptr);
	}

	//
	// RC2014 Classic II
	//
	// Clock/Reset
	// Z80 CPU module
	// 32K RAM module
	// Switchable ROM module
	// Serial I/O
	//
	// Backplane-8 - 8 x 40pin slots
	//
	// all modules are in slightly different form factor
	//
	void rc2014cl2(machine_config &config)
	{
		RC2014_BUS(config, m_bus, 0);
		RC2014_SLOT(config, "bus:1", m_bus, rc2014_bus_modules, "z80");
		RC2014_SLOT(config, "bus:2", m_bus, rc2014_bus_modules, "clock");
		RC2014_SLOT(config, "bus:3", m_bus, rc2014_bus_modules, "ram32k");
		RC2014_SLOT(config, "bus:4", m_bus, rc2014_bus_modules, "sw_rom");
		RC2014_SLOT(config, "bus:5", m_bus, rc2014_bus_modules, "serial");
		RC2014_SLOT(config, "bus:6", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:7", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:8", m_bus, rc2014_bus_modules, nullptr);
	}


	//
	// RC2014 Zed
	//
	// Z80 2.1 CPU Module
	// Dual Clock Module
	// 512k ROM 512k RAM Module
	// Dual Serial SIO/2 Module
	//
	// Backplane 8 - 8 x 40pin slots
	//
	// Some modules are extended bus modules in standard slots
	//
	void rc2014zed(machine_config &config)
	{
		RC2014_BUS(config, m_bus, 0);
		RC2014_SLOT(config, "bus:1", m_bus, rc2014_bus_modules, "z80_21_40p");
		RC2014_SLOT(config, "bus:2", m_bus, rc2014_bus_modules, "dual_clk_40p");
		RC2014_SLOT(config, "bus:3", m_bus, rc2014_bus_modules, "rom_ram");
		RC2014_SLOT(config, "bus:4", m_bus, rc2014_bus_modules, "sio_40p");
		RC2014_SLOT(config, "bus:5", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:6", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:7", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:8", m_bus, rc2014_bus_modules, nullptr);
	}

	//
	// Backplane 8 - 8 x 40pin slots
	//
	void rc2014bp8(machine_config &config)
	{
		RC2014_BUS(config, m_bus, 0);
		RC2014_SLOT(config, "bus:1", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:2", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:3", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:4", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:5", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:6", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:7", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:8", m_bus, rc2014_bus_modules, nullptr);
	}

	//
	// SC133 – MODULAR BACKPLANE (RC2014)
	//
	void sc133(machine_config &config)
	{
		RC2014_BUS(config, m_bus, 0);
		RC2014_SLOT(config, "bus:1", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:2", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:3", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:4", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:5", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:6", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:7", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:8", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:9", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:10", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:11", m_bus, rc2014_bus_modules, nullptr);
		RC2014_SLOT(config, "bus:e",  m_bus, rc2014_bus_edge_modules, nullptr);
	}

private:
	required_device<rc2014_bus_device> m_bus;
};

ROM_START(rc2014)
ROM_END

ROM_START(rc2014bp5)
ROM_END

ROM_START(rc2014cl2)
ROM_END

ROM_START(rc2014zed)
ROM_END

ROM_START(rc2014bp8)
ROM_END

ROM_START(sc133)
ROM_END

class rc2014pro_state : public driver_device
{
public:
	rc2014pro_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_bus(*this, "bus")
	{ }

	//
	// RC2014 PRo
	//
	// Z80 2.1 CPU Module
	// Dual Clock Module
	// 64k RAM
	// Pageable ROM
	// Dual Serial SIO/2 Module
	// Compact Flash storage
	//
	// Backplane Pro - 12 x extended slots
	//
	void rc2014pro(machine_config &config)
	{
		RC2014_EXT_BUS(config, m_bus, 0);
		RC2014_EXT_SLOT(config, "bus:1", m_bus, rc2014_ext_bus_modules, "z80_21");
		RC2014_EXT_SLOT(config, "bus:2", m_bus, rc2014_ext_bus_modules, "dual_clk");
		RC2014_EXT_SLOT(config, "bus:3", m_bus, rc2014_ext_bus_modules, "ram64k");
		RC2014_EXT_SLOT(config, "bus:4", m_bus, rc2014_ext_bus_modules, "page_rom");
		RC2014_EXT_SLOT(config, "bus:5", m_bus, rc2014_ext_bus_modules, "sio");
		RC2014_EXT_SLOT(config, "bus:6", m_bus, rc2014_ext_bus_modules, "cf");
		RC2014_EXT_SLOT(config, "bus:7", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:8", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:9", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:10", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:11", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:12", m_bus, rc2014_ext_bus_modules, nullptr);
	}

	//
	// RC2014 Zed
	//
	// Z80 2.1 CPU Module
	// Dual Clock Module
	// 512k ROM 512k RAM Module
	// Dual Serial SIO/2 Module
	//
	// Backplane Pro - 12 x extended slots
	//
	void rc2014zedp(machine_config &config)
	{
		RC2014_EXT_BUS(config, m_bus, 0);
		RC2014_EXT_SLOT(config, "bus:1", m_bus, rc2014_ext_bus_modules, "z80_21");
		RC2014_EXT_SLOT(config, "bus:2", m_bus, rc2014_ext_bus_modules, "dual_clk");
		RC2014_EXT_SLOT(config, "bus:3", m_bus, rc2014_ext_bus_modules, "rom_ram");
		RC2014_EXT_SLOT(config, "bus:4", m_bus, rc2014_ext_bus_modules, "sio");
		RC2014_EXT_SLOT(config, "bus:5", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:6", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:7", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:8", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:9", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:10", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:11", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:12", m_bus, rc2014_ext_bus_modules, nullptr);
	}

	//
	// Backplane Pro - 12 x extended slots
	//
	void rc2014bppro(machine_config &config)
	{
		RC2014_EXT_BUS(config, m_bus, 0);
		RC2014_EXT_SLOT(config, "bus:1", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:2", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:3", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:4", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:5", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:6", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:7", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:8", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:9", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:10", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:11", m_bus, rc2014_ext_bus_modules, nullptr);
		RC2014_EXT_SLOT(config, "bus:12", m_bus, rc2014_ext_bus_modules, nullptr);
	}
private:
	required_device<rc2014_ext_bus_device> m_bus;
};

ROM_START(rc2014pro)
ROM_END

ROM_START(rc2014zedp)
ROM_END

ROM_START(rc2014bppro)
ROM_END

static DEVICE_INPUT_DEFAULTS_START(mini_cpm)
	DEVICE_INPUT_DEFAULTS("ROM", 0x1, 0x0)
DEVICE_INPUT_DEFAULTS_END

class rc2014mini_state : public driver_device
{
public:
	rc2014mini_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_bus(*this, "bus")
	{ }

	//
	// RC2014 Mini
	//
	// Added by Chris Swan
	// https://rc2014.co.uk/full-kits/rc2014-mini/
	//
	void rc2014mini(machine_config &config)
	{
		RC2014_BUS(config, m_bus, 0);
		RC2014_SLOT(config, "board", m_bus, rc2014_mini_bus_modules, "mini", true);
		RC2014_SLOT(config, "bus:1", m_bus, rc2014_mini_bus_modules, nullptr);
	}

	//
	// RC2014 Mini with CP/M Upgrade
	//
	void rc2014minicpm(machine_config &config)
	{
		RC2014_BUS(config, m_bus, 0);
		RC2014_SLOT(config, "board", m_bus, rc2014_mini_bus_modules, "mini", true).set_option_device_input_defaults("mini", DEVICE_INPUT_DEFAULTS_NAME(mini_cpm));
		RC2014_SLOT(config, "bus:1", m_bus, rc2014_mini_bus_modules, "mini_cpm", true);
	}

	//
	// RC2014 Micro
	//
	// This is card that can be used directly without any backplane board
	// Power is coming from usb-to-serial adapter
	//
	void rc2014micro(machine_config &config)
	{
		RC2014_BUS(config, m_bus, 0);
		RC2014_SLOT(config, "board", m_bus, rc2014_bus_modules, "micro", true);
	}
private:
	required_device<rc2014_bus_device> m_bus;
};

ROM_START(rc2014mini)
ROM_END

ROM_START(rc2014minicpm)
ROM_END

ROM_START(rc2014micro)
ROM_END

class scc_state : public driver_device
{
public:
	scc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_bus(*this, "bus")
	{ }

	//
	// SC105 – MODULAR BACKPLANE (RC2014)
	//
	// Input: Power supply
	// Cards: 6 x RC2014/80 pin sockets
	// Output: Edge mounted BP80 socket
	//
	void sc105(machine_config &config)
	{
		RC2014_RC80_BUS(config, m_bus, 0);
		RC2014_RC80_SLOT(config, "bus:1", m_bus, rc2014_rc80_bus_modules, nullptr);
		RC2014_RC80_SLOT(config, "bus:2", m_bus, rc2014_rc80_bus_modules, nullptr);
		RC2014_RC80_SLOT(config, "bus:3", m_bus, rc2014_rc80_bus_modules, nullptr);
		RC2014_RC80_SLOT(config, "bus:4", m_bus, rc2014_rc80_bus_modules, nullptr);
		RC2014_RC80_SLOT(config, "bus:5", m_bus, rc2014_rc80_bus_modules, nullptr);
		RC2014_RC80_SLOT(config, "bus:6", m_bus, rc2014_rc80_bus_modules, nullptr);
		RC2014_RC80_SLOT(config, "bus:e", m_bus, rc2014_rc80_bus_edge_modules, nullptr);
	}

	//
	// SC112 – MODULAR BACKPLANE (RC2014)
	//
	// Input: Power supply
	// Cards: 6 x RC2014/80 pin sockets
	// Output: Through-hole BP80 socket
	//
	void sc112(machine_config &config)
	{
		RC2014_RC80_BUS(config, m_bus, 0);
		RC2014_RC80_SLOT(config, "bus:1", m_bus, rc2014_rc80_bus_modules, nullptr);
		RC2014_RC80_SLOT(config, "bus:2", m_bus, rc2014_rc80_bus_modules, nullptr);
		RC2014_RC80_SLOT(config, "bus:3", m_bus, rc2014_rc80_bus_modules, nullptr);
		RC2014_RC80_SLOT(config, "bus:4", m_bus, rc2014_rc80_bus_modules, nullptr);
		RC2014_RC80_SLOT(config, "bus:5", m_bus, rc2014_rc80_bus_modules, nullptr);
		RC2014_RC80_SLOT(config, "bus:6", m_bus, rc2014_rc80_bus_modules, nullptr);
		RC2014_RC80_SLOT(config, "bus:e", m_bus, rc2014_rc80_bus_edge_modules, nullptr);
	}

	//
	// SC116 – 3-SLOT BACKPLANE (RC2014)
	//
	// Input: Power supply
	// Cards: 3 x RC2014/80 pin sockets
	//
	void sc116(machine_config &config)
	{
		RC2014_RC80_BUS(config, m_bus, 0);
		RC2014_RC80_SLOT(config, "bus:1", m_bus, rc2014_rc80_bus_modules, nullptr);
		RC2014_RC80_SLOT(config, "bus:2", m_bus, rc2014_rc80_bus_modules, nullptr);
		RC2014_RC80_SLOT(config, "bus:3", m_bus, rc2014_rc80_bus_modules, nullptr);
	}

	//
	// SC203 – Modular Z180 Computer
	//
	// SC111 - Z180 CPU module
	// SC119 - Z180 memory module
	// SC112 - 6-slot backplane with power input, or
	// SC116 - 3-slot backplane with power input
	//
	void sc203(machine_config &config)
	{
		RC2014_RC80_BUS(config, m_bus, 0);
		RC2014_RC80_SLOT(config, "bus:1", m_bus, rc2014_rc80_bus_modules, "sc111");
		RC2014_RC80_SLOT(config, "bus:2", m_bus, rc2014_rc80_bus_modules, "sc119");
		RC2014_RC80_SLOT(config, "bus:3", m_bus, rc2014_rc80_bus_modules, nullptr);
	}

private:
	required_device<rc2014_rc80_bus_device> m_bus;
};

ROM_START(sc105)
ROM_END

ROM_START(sc112)
ROM_END

ROM_START(sc116)
ROM_END

ROM_START(sc203)
ROM_END

} // anonymous namespace

// This ties everything together
//    YEAR  NAME          PARENT    COMPAT    MACHINE         INPUT    CLASS             INIT           COMPANY              FULLNAME                         FLAGS
COMP( 2016, rc2014,       0,        0,        rc2014,         0,       rc2014_state,     empty_init,    "RFC2795 Ltd",       "RC2014 Classic",                MACHINE_SUPPORTS_SAVE )
COMP( 2017, rc2014pro,    rc2014,   0,        rc2014pro,      0,       rc2014pro_state,  empty_init,    "RFC2795 Ltd",       "RC2014 Pro",                    MACHINE_SUPPORTS_SAVE )
COMP( 2020, rc2014cl2,    rc2014,   0,        rc2014cl2,      0,       rc2014_state,     empty_init,    "RFC2795 Ltd",       "RC2014 Classic II",             MACHINE_SUPPORTS_SAVE )
COMP( 2018, rc2014zed,    rc2014,   0,        rc2014zed,      0,       rc2014_state,     empty_init,    "RFC2795 Ltd",       "RC2014 Zed",                    MACHINE_SUPPORTS_SAVE )
COMP( 2018, rc2014zedp,   rc2014,   0,        rc2014zedp,     0,       rc2014pro_state,  empty_init,    "RFC2795 Ltd",       "RC2014 Zed Pro",                MACHINE_SUPPORTS_SAVE )
COMP( 2016, rc2014mini,   rc2014,   0,        rc2014mini,     0,       rc2014mini_state, empty_init,    "RFC2795 Ltd",       "RC2014 Mini",                   MACHINE_SUPPORTS_SAVE )
COMP( 2016, rc2014minicpm,rc2014,   0,        rc2014minicpm,  0,       rc2014mini_state, empty_init,    "RFC2795 Ltd",       "RC2014 Mini with CP/M Upgrade", MACHINE_SUPPORTS_SAVE )
COMP( 2019, rc2014micro,  rc2014,   0,        rc2014micro,    0,       rc2014mini_state, empty_init,    "RFC2795 Ltd",       "RC2014 Micro",                  MACHINE_SUPPORTS_SAVE )
COMP( 2018, sc203,        rc2014,   0,        sc203,          0,       scc_state,        empty_init,    "Stephen C Cousins", "SC203 - Modular Z180 Computer", MACHINE_SUPPORTS_SAVE )
// Backplanes
COMP( 2016, rc2014bp5,    rc2014,   0,        rc2014bp5,      0,       rc2014_state,     empty_init,    "RFC2795 Ltd",       "RC2014 Backplane-5",                 MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 2016, rc2014bp8,    rc2014,   0,        rc2014bp8,      0,       rc2014_state,     empty_init,    "RFC2795 Ltd",       "RC2014 Backplane-8",                 MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 2017, rc2014bppro,  rc2014,   0,        rc2014bppro,    0,       rc2014pro_state,  empty_init,    "RFC2795 Ltd",       "RC2014 Backplane Pro",               MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 2018, sc105,        rc2014,   0,        sc105,          0,       scc_state,        empty_init,    "Stephen C Cousins", "SC105 - Modular Backplane (RC2014)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 2018, sc112,        rc2014,   0,        sc112,          0,       scc_state,        empty_init,    "Stephen C Cousins", "SC112 - Modular Backplane (RC2014)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 2018, sc116,        rc2014,   0,        sc116,          0,       scc_state,        empty_init,    "Stephen C Cousins", "SC116 - Modular Backplane (RC2014)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 2018, sc133,        rc2014,   0,        sc133,          0,       rc2014_state,     empty_init,    "Stephen C Cousins", "SC133 - Modular Backplane (RC2014)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )



rc702.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/******************************************************************************************************************

Regnecentralen Piccolo RC702

2016-09-10 Skeleton driver

Undumped prom at IC55 type 74S287
Keyboard has 8048 and 2758, both undumped.

ToDo:
- Printer
- Hard drive, ports 0x60-0x67. Extra CTC on HD board, ports 0x44-0x47
- Other things

Issues:
- Floppy disc error. It reads 0x780 bytes from the wrong sector then gives diskette error (use bios 0)


****************************************************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/z80daisy.h"
#include "machine/7474.h"
#include "machine/am9517a.h"
#include "machine/clock.h"
#include "machine/keyboard.h"
#include "machine/upd765.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"
#include "sound/beep.h"
#include "video/i8275.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class rc702_state : public driver_device
{
public:
	rc702_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_palette(*this, "palette")
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_bank1(*this, "bank1")
		, m_p_chargen(*this, "chargen")
		, m_ctc1(*this, "ctc1")
		, m_pio(*this, "pio")
		, m_dma(*this, "dma")
		, m_beep(*this, "beeper")
		, m_7474(*this, "7474")
		, m_fdc(*this, "fdc")
		, m_floppy0(*this, "fdc:0")
	{ }

	void rc702(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	uint8_t memory_read_byte(offs_t offset);
	void memory_write_byte(offs_t offset, uint8_t data);
	void port14_w(uint8_t data);
	void port1c_w(uint8_t data);
	void crtc_drq_w(int state);
	void hreq_w(int state);
	void clock_w(int state);
	void eop_w(int state);
	void q_w(int state);
	void qbar_w(int state);
	void dack1_w(int state);
	I8275_DRAW_CHARACTER_MEMBER(display_pixels);
	void rc702_palette(palette_device &palette) const;
	void kbd_put(u8 data);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	bool m_q_state = false;
	bool m_qbar_state = false;
	bool m_drq_state = false;
	uint16_t m_beepcnt = 0U;
	bool m_eop = false;
	bool m_dack1 = false;
	required_device<palette_device> m_palette;
	required_device<z80_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_memory_bank    m_bank1;
	required_region_ptr<u8> m_p_chargen;
	required_device<z80ctc_device> m_ctc1;
	required_device<z80pio_device> m_pio;
	required_device<am9517a_device> m_dma;
	required_device<beep_device> m_beep;
	required_device<ttl7474_device> m_7474;
	required_device<upd765a_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
};


void rc702_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).ram().share("mainram");
	map(0x0000, 0x07ff).bankr("bank1");
}

void rc702_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x01).rw("crtc", FUNC(i8275_device::read), FUNC(i8275_device::write));
	map(0x04, 0x05).m(m_fdc, FUNC(upd765a_device::map));
	map(0x08, 0x0b).rw("sio1", FUNC(z80dart_device::cd_ba_r), FUNC(z80dart_device::cd_ba_w)); // boot sequence doesn't program this
	map(0x0c, 0x0f).rw(m_ctc1, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x10, 0x13).rw(m_pio, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x14, 0x17).portr("DSW").w(FUNC(rc702_state::port14_w)); // motors
	map(0x18, 0x1b).lw8(NAME([this] (u8 data) { m_bank1->set_entry(0); })); // replace roms with ram
	map(0x1c, 0x1f).w(FUNC(rc702_state::port1c_w)); // sound
	map(0xf0, 0xff).rw(m_dma, FUNC(am9517a_device::read), FUNC(am9517a_device::write));
}

/* Input ports */
static INPUT_PORTS_START( rc702 )
	PORT_START("DSW")
	PORT_DIPNAME( 0x01, 0x00, "S01")
	PORT_DIPSETTING(    0x01, DEF_STR( Off ))
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPNAME( 0x02, 0x00, "S02")
	PORT_DIPSETTING(    0x02, DEF_STR( Off ))
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPNAME( 0x04, 0x00, "S03")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ))
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPNAME( 0x08, 0x00, "S04")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ))
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPNAME( 0x10, 0x00, "S05")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ))
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPNAME( 0x20, 0x00, "S06")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ))
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPNAME( 0x40, 0x00, "S07")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ))
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPNAME( 0x80, 0x00, "Minifloppy") // also need to switch frequencies to fdc
	PORT_DIPSETTING(    0x80, DEF_STR( Off ))
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
INPUT_PORTS_END

void rc702_state::machine_reset()
{
	m_bank1->set_entry(1);
	m_beepcnt = 0xffff;
	m_dack1 = 0;
	m_eop = 0;
	m_7474->preset_w(1);
	m_fdc->set_ready_line_connected(1); // always ready for minifloppy; controlled by fdc for 20cm
	m_fdc->set_unscaled_clock(4000000); // 4MHz for minifloppy; 8MHz for 20cm
	m_maincpu->reset();
}

void rc702_state::machine_start()
{
	m_bank1->configure_entry(0, m_ram);
	m_bank1->configure_entry(1, m_rom);
	save_item(NAME(m_q_state));
	save_item(NAME(m_qbar_state));
	save_item(NAME(m_drq_state));
	save_item(NAME(m_beepcnt));
	save_item(NAME(m_eop));
	save_item(NAME(m_dack1));
}

void rc702_state::q_w(int state)
{
	m_q_state = state;

	if (m_q_state && m_drq_state)
		m_dma->dreq3_w(1);
	else
		m_dma->dreq3_w(0);
}

void rc702_state::qbar_w(int state)
{
	m_qbar_state = state;

	if (m_qbar_state && m_drq_state)
		m_dma->dreq2_w(1);
	else
		m_dma->dreq2_w(0);
}

void rc702_state::crtc_drq_w(int state)
{
	m_drq_state = state;

	if (m_q_state && m_drq_state)
		m_dma->dreq3_w(1);
	else
		m_dma->dreq3_w(0);

	if (m_qbar_state && m_drq_state)
		m_dma->dreq2_w(1);
	else
		m_dma->dreq2_w(0);
}

void rc702_state::eop_w(int state)
{
	if (state == m_eop)
		return;

	m_eop = state;

	if (!m_eop && !m_dack1)
		m_fdc->tc_w(1);
	else
		m_fdc->tc_w(0);
}

void rc702_state::dack1_w(int state)
{
	if (state == m_dack1)
		return;

	m_dack1 = state;

	if (!m_eop && !m_dack1)
		m_fdc->tc_w(1);
	else
		m_fdc->tc_w(0);

	//m_fdc->dack_w = state;  // pin not emulated
}

void rc702_state::port14_w(uint8_t data)
{
	floppy_image_device *floppy = m_floppy0->get_device();
	m_fdc->set_floppy(floppy);
	floppy->mon_w(!BIT(data, 0));
}

void rc702_state::port1c_w(uint8_t data)
{
	m_beep->set_state(1);
	m_beepcnt = 0x3000;
}

// monitor is orange even when powered off
void rc702_state::rc702_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(0xc0, 0x60, 0x00));
	palette.set_pen_color(1, rgb_t(0xff, 0xb4, 0x00));
}

I8275_DRAW_CHARACTER_MEMBER( rc702_state::display_pixels )
{
	const rgb_t *palette = m_palette->palette()->entry_list_raw();
	uint8_t gfx = 0;

	using namespace i8275_attributes;

	if (!BIT(attrcode, VSP))
		gfx = m_p_chargen[(linecount & 15) | (charcode << 4)];

	if (BIT(attrcode, LTEN))
		gfx = 0xff;

	if (BIT(attrcode, RVV))
		gfx ^= 0xff;

	// Highlight not used
	bitmap.pix(y, x++) = palette[BIT(gfx, 1) ? 1 : 0];
	bitmap.pix(y, x++) = palette[BIT(gfx, 2) ? 1 : 0];
	bitmap.pix(y, x++) = palette[BIT(gfx, 3) ? 1 : 0];
	bitmap.pix(y, x++) = palette[BIT(gfx, 4) ? 1 : 0];
	bitmap.pix(y, x++) = palette[BIT(gfx, 5) ? 1 : 0];
	bitmap.pix(y, x++) = palette[BIT(gfx, 6) ? 1 : 0];
	bitmap.pix(y, x++) = palette[BIT(gfx, 7) ? 1 : 0];
}

// Baud rate generator. All inputs are 0.614MHz.
void rc702_state::clock_w(int state)
{
	m_ctc1->trg0(state);
	m_ctc1->trg1(state);
	if (m_beepcnt == 0)
		m_beep->set_state(0);
	if (m_beepcnt < 0xfe00)
		m_beepcnt--;
}

void rc702_state::hreq_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);
	m_dma->hack_w(state); // tell dma that bus has been granted
}

uint8_t rc702_state::memory_read_byte(offs_t offset)
{
	return m_maincpu->space(AS_PROGRAM).read_byte(offset);
}

void rc702_state::memory_write_byte(offs_t offset, uint8_t data)
{
	m_maincpu->space(AS_PROGRAM).write_byte(offset,data);
}

static const z80_daisy_config daisy_chain_intf[] =
{
	{ "ctc1" },
	{ "sio1" },
	{ "pio" },
	{ nullptr }
};

void rc702_state::kbd_put(u8 data)
{
	m_pio->port_a_write(data);
	m_pio->strobe_a(0);
	m_pio->strobe_a(1);
}

static void floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

void rc702_state::rc702(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(8'000'000) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &rc702_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &rc702_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain_intf);

	CLOCK(config, "ctc_clock", 614000).signal_handler().set(FUNC(rc702_state::clock_w));

	Z80CTC(config, m_ctc1, 8_MHz_XTAL / 2);
	m_ctc1->zc_callback<0>().set("sio1", FUNC(z80dart_device::txca_w));
	m_ctc1->zc_callback<0>().append("sio1", FUNC(z80dart_device::rxca_w));
	m_ctc1->zc_callback<1>().set("sio1", FUNC(z80dart_device::rxtxcb_w));
	m_ctc1->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80dart_device& dart(Z80DART(config, "sio1", XTAL(8'000'000) / 2));
	dart.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80PIO(config, m_pio, 8_MHz_XTAL / 2);
	m_pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
//  m_pio->out_pb_callback().set(FUNC(rc702_state::portxx_w)); // parallel port

	AM9517A(config, m_dma, 8_MHz_XTAL / 2);
	m_dma->out_hreq_callback().set(FUNC(rc702_state::hreq_w));
	m_dma->out_eop_callback().set(FUNC(rc702_state::eop_w)).invert();   // real line is active low, mame has it backwards
	m_dma->in_memr_callback().set(FUNC(rc702_state::memory_read_byte));
	m_dma->out_memw_callback().set(FUNC(rc702_state::memory_write_byte));
	m_dma->in_ior_callback<1>().set(m_fdc, FUNC(upd765a_device::dma_r));
	m_dma->out_iow_callback<1>().set(m_fdc, FUNC(upd765a_device::dma_w));
	m_dma->out_iow_callback<2>().set("crtc", FUNC(i8275_device::dack_w));
	m_dma->out_iow_callback<3>().set("crtc", FUNC(i8275_device::dack_w));
	m_dma->out_dack_callback<1>().set(FUNC(rc702_state::dack1_w));

	UPD765A(config, m_fdc, 8_MHz_XTAL, true, true);
	m_fdc->intrq_wr_callback().set(m_ctc1, FUNC(z80ctc_device::trg3));
	m_fdc->drq_wr_callback().set(m_dma, FUNC(am9517a_device::dreq1_w));
	FLOPPY_CONNECTOR(config, "fdc:0", floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	/* Keyboard */
	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(rc702_state::kbd_put));

	TTL7474(config, m_7474, 0);
	m_7474->output_cb().set(FUNC(rc702_state::q_w));
	m_7474->comp_output_cb().set(FUNC(rc702_state::qbar_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_size(272*2, 200+4*8);
	screen.set_visarea(0, 272*2-1, 0, 200-1);
	screen.set_screen_update("crtc", FUNC(i8275_device::screen_update));

	i8275_device &crtc(I8275(config, "crtc", 11640000/7));
	crtc.set_character_width(7);
	crtc.set_display_callback(FUNC(rc702_state::display_pixels));
	crtc.irq_wr_callback().set(m_7474, FUNC(ttl7474_device::clear_w)).invert();
	crtc.irq_wr_callback().append(m_ctc1, FUNC(z80ctc_device::trg2));
	crtc.drq_wr_callback().set(FUNC(rc702_state::crtc_drq_w));

	PALETTE(config, m_palette, FUNC(rc702_state::rc702_palette), 2);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 1000).add_route(ALL_OUTPUTS, "mono", 0.50);
}


/* ROM definition */
ROM_START( rc702 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_SYSTEM_BIOS(0, "rc702", "RC702")
	ROMX_LOAD( "roa375.ic66", 0x0000, 0x0800, CRC(034cf9ea) SHA1(306af9fc779e3d4f51645ba04f8a99b11b5e6084), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "rc703", "RC703")
	ROMX_LOAD( "rob357.rom", 0x0000, 0x0800,  CRC(dcf84a48) SHA1(7190d3a898bcbfa212178a4d36afc32bbbc166ef), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "rc700", "RC700")
	ROMX_LOAD( "rob358.rom", 0x0000, 0x0800,  CRC(254aa89e) SHA1(5fb1eb8df1b853b931e670a2ff8d062c1bd8d6bc), ROM_BIOS(2))

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "roa296.rom", 0x0000, 0x0800, CRC(7d7e4548) SHA1(efb8b1ece5f9eeca948202a6396865f26134ff2f) ) // char
	ROM_LOAD( "roa327.rom", 0x0800, 0x0800, CRC(bed7ddb0) SHA1(201ae9e4ac3812577244b9c9044fadd04fb2b82f) ) // semi_gfx
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY           FULLNAME         FLAGS
COMP( 1979, rc702, 0,      0,      rc702,   rc702, rc702_state, empty_init, "Regnecentralen", "RC702 Piccolo", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



rc759.cpp
<---------------------------------------------------------------------->
// license: GPL-2.0+
// copyright-holders: Dirk Best
/***************************************************************************

    Regnecentralen RC759 Piccoline

    TODO:
    - Needs better I82730 emulation
    - Floppy I/O errors
    - Many more things

    Notes:
    - Press SPACE during self-test for an extended menu

***************************************************************************/

#include "emu.h"
#include "cpu/i86/i186.h"
#include "machine/i8255.h"
#include "machine/mm58167.h"
#include "machine/nvram.h"
#include "machine/pic8259.h"
#include "machine/ram.h"
#include "machine/wd_fdc.h"
#include "rc759_kbd.h"
#include "sound/sn76496.h"
#include "sound/spkrdev.h"
#include "video/i82730.h"
#include "bus/centronics/ctronics.h"
#include "bus/isbx/isbx.h"
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "formats/rc759_dsk.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class rc759_state : public driver_device
{
public:
	rc759_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_pic(*this, "pic"),
		m_nvram(*this, "nvram"),
		m_ppi(*this, "ppi"),
		m_txt(*this, "txt"),
		m_palette(*this, "palette"),
		m_cas(*this, "cas"),
		m_isbx(*this, "isbx"),
		m_speaker(*this, "speaker"),
		m_snd(*this, "snd"),
		m_rtc(*this, "rtc"),
		m_centronics(*this, "centronics"),
		m_fdc(*this, "fdc"),
		m_floppy(*this, "fdc:%u", 0),
		m_vram(*this, "vram"),
		m_config(*this, "config"),
		m_kbd(*this, "kbd"),
		m_cas_enabled(0), m_cas_data(0),
		m_drq_source(0),
		m_nvram_bank(0),
		m_gfx_mode(0),
		m_centronics_strobe(0), m_centronics_init(0), m_centronics_select_in(0), m_centronics_busy(0),
		m_centronics_ack(0), m_centronics_fault(0), m_centronics_perror(0), m_centronics_select(0),
		m_centronics_data(0xff)
	{ }

	void rc759(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<i80186_cpu_device> m_maincpu;
	required_device<pic8259_device> m_pic;
	required_device<nvram_device> m_nvram;
	required_device<i8255_device> m_ppi;
	required_device<i82730_device> m_txt;
	required_device<palette_device> m_palette;
	required_device<cassette_image_device> m_cas;
	required_device<isbx_slot_device> m_isbx;
	required_device<speaker_sound_device> m_speaker;
	required_device<sn76489a_device> m_snd;
	required_device<mm58167_device> m_rtc;
	required_device<centronics_device> m_centronics;
	required_device<wd2797_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_shared_ptr<uint16_t> m_vram;
	required_ioport m_config;
	required_device<rc759_kbd_hle_device> m_kbd;

	static void floppy_formats(format_registration &fr);
	void floppy_control_w(uint8_t data);
	uint8_t floppy_ack_r();
	void floppy_reserve_w(uint8_t data);
	void floppy_release_w(uint8_t data);

	uint8_t ppi_porta_r();
	uint8_t ppi_portb_r();
	void ppi_portc_w(uint8_t data);

	void centronics_busy_w(int state);
	void centronics_ack_w(int state);
	void centronics_fault_w(int state);
	void centronics_perror_w(int state);
	void centronics_select_w(int state);

	uint8_t centronics_data_r();
	void centronics_data_w(uint8_t data);
	uint8_t centronics_control_r();
	void centronics_control_w(uint8_t data);

	I82730_UPDATE_ROW(txt_update_row);
	void txt_ca_w(uint16_t data);
	void txt_irst_w(uint16_t data);
	uint8_t palette_r(offs_t offset);
	void palette_w(offs_t offset, uint8_t data);

	void i186_timer0_w(int state);
	void i186_timer1_w(int state);

	void nvram_init(nvram_device &nvram, void *data, size_t size);
	uint8_t nvram_r(offs_t offset);
	void nvram_w(offs_t offset, uint8_t data);

	void rtc_data_w(uint8_t data);
	uint8_t rtc_data_r();
	void rtc_addr_w(uint8_t data);

	uint8_t irq_callback();

	void rc759_io(address_map &map) ATTR_COLD;
	void rc759_map(address_map &map) ATTR_COLD;

	std::vector<uint8_t> m_nvram_mem;

	int m_cas_enabled;
	int m_cas_data;
	int m_drq_source;
	int m_nvram_bank;
	int m_gfx_mode;

	bool m_floppy_reserved;

	uint8_t m_rtc_read_addr;
	uint8_t m_rtc_read_data;
	uint8_t m_rtc_write_addr;
	uint8_t m_rtc_write_data;
	uint8_t m_rtc_strobe;

	int m_centronics_strobe;
	int m_centronics_init;
	int m_centronics_select_in;
	int m_centronics_busy;
	int m_centronics_ack;
	int m_centronics_fault;
	int m_centronics_perror;
	int m_centronics_select;
	uint8_t m_centronics_data;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void rc759_state::rc759_map(address_map &map)
{
	map(0x00000, 0x3ffff).ram();
	map(0x40000, 0x7ffff).ram();
	map(0xd0000, 0xd7fff).mirror(0x08000).ram().share("vram");
	map(0xe8000, 0xeffff).mirror(0x10000).rom().region("bios", 0);
}

void rc759_state::rc759_io(address_map &map)
{
	map.unmap_value_high();
	map(0x000, 0x003).mirror(0x0c).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x020, 0x020).r(m_kbd, FUNC(rc759_kbd_hle_device::read));
	map(0x056, 0x056).w(m_snd, FUNC(sn76494_device::write));
	map(0x056, 0x057).nopr();
	map(0x05a, 0x05a).w(FUNC(rc759_state::rtc_data_w));
	map(0x05c, 0x05c).rw(FUNC(rc759_state::rtc_data_r), FUNC(rc759_state::rtc_addr_w));
//  map(0x060, 0x06f).w(FUNC(rc759_state::crt_control_w)).umask16(0x00ff);
	map(0x070, 0x077).mirror(0x08).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
	map(0x080, 0x0ff).rw(FUNC(rc759_state::nvram_r), FUNC(rc759_state::nvram_w)).umask16(0x00ff);
//  map(0x100, 0x101) net
	map(0x180, 0x1bf).rw(FUNC(rc759_state::palette_r), FUNC(rc759_state::palette_w)).umask16(0x00ff);
	map(0x230, 0x231).w(FUNC(rc759_state::txt_irst_w));
	map(0x240, 0x241).w(FUNC(rc759_state::txt_ca_w));
	map(0x250, 0x250).rw(FUNC(rc759_state::centronics_data_r), FUNC(rc759_state::centronics_data_w));
	map(0x260, 0x260).rw(FUNC(rc759_state::centronics_control_r), FUNC(rc759_state::centronics_control_w));
	map(0x280, 0x287).rw(m_fdc, FUNC(wd2797_device::read), FUNC(wd2797_device::write)).umask16(0x00ff);
	map(0x288, 0x288).w(FUNC(rc759_state::floppy_control_w));
//  map(0x28a, 0x28b) external printer data
//  map(0x28d, 0x28d) external printer control
	map(0x28e, 0x28e).rw(FUNC(rc759_state::floppy_ack_r), FUNC(rc759_state::floppy_reserve_w));
	map(0x290, 0x290).w(FUNC(rc759_state::floppy_release_w));
//  map(0x292, 0x293).rw(FUNC(rc759_state::printer_ack_r), FUNC(rc759_state::printer_reserve_w)).umask16(0x00ff);
//  map(0x294, 0x295).w(FUNC(rc759_state::printer_release_w)).umask16(0x00ff);
	map(0x300, 0x30f).rw(m_isbx, FUNC(isbx_slot_device::mcs0_r), FUNC(isbx_slot_device::mcs0_w)).umask16(0x00ff);
	map(0x310, 0x31f).rw(m_isbx, FUNC(isbx_slot_device::mcs1_r), FUNC(isbx_slot_device::mcs1_w)).umask16(0x00ff);
//  map(0x320, 0x321) isbx dma ack
//  map(0x330, 0x331) isbx tc
}


//**************************************************************************
//  INPUT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( rc759 )
	PORT_START("config")
	PORT_CONFNAME(0x20, 0x00, "Monitor Type")
	PORT_CONFSETTING(0x00, "Color")
	PORT_CONFSETTING(0x20, "Monochrome")
	PORT_CONFNAME(0x40, 0x00, "Monitor Frequency")
	PORT_CONFSETTING(0x00, "15 kHz")
	PORT_CONFSETTING(0x40, "22 kHz")
INPUT_PORTS_END


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

I82730_UPDATE_ROW( rc759_state::txt_update_row )
{
	for (int i = 0; i < x_count; i++)
	{
		uint16_t gfx = m_vram[(data[i] & 0x3ff) << 4 | lc];

		// pretty crude detection if char sizes have been initialized, need something better
		if ((gfx & 0xff) == 0)
			continue;

		// figure out char width
		int width;
		for (width = 0; width < 16; width++)
			if (BIT(gfx, width) == 0)
				break;

		width = 15 - width;

		for (int p = 0; p < width; p++)
			bitmap.pix(y, i * width + p) = BIT(gfx, 15 - p) ? rgb_t::white() : rgb_t::black();
	}
}

void rc759_state::txt_ca_w(uint16_t data)
{
	m_txt->ca_w(1);
	m_txt->ca_w(0);
}

void rc759_state::txt_irst_w(uint16_t data)
{
	m_txt->irst_w(1);
	m_txt->irst_w(0);
}

uint8_t rc759_state::palette_r(offs_t offset)
{
	// not sure if it's possible to read back
	logerror("palette_r(%02x)\n", offset);
	return 0xff;
}

void rc759_state::palette_w(offs_t offset, uint8_t data)
{
	logerror("palette_w(%02x): %02x\n", offset, data);

	// two colors/byte. format: IRGBIRGB
	static constexpr uint8_t val[4] = { 0x00, 0x55, 0xaa, 0xff };
	int r, g, b;

	r = (BIT(data, 2) << 1) | BIT(data, 3);
	g = (BIT(data, 1) << 1) | BIT(data, 3);
	b = (BIT(data, 0) << 1) | BIT(data, 3);

	m_palette->set_pen_color(offset * 2 + 0, rgb_t(val[r], val[g], val[b]));

	r = (BIT(data, 6) << 1) | BIT(data, 7);
	g = (BIT(data, 5) << 1) | BIT(data, 7);
	b = (BIT(data, 4) << 1) | BIT(data, 7);

	m_palette->set_pen_color(offset * 2 + 1, rgb_t(val[r], val[g], val[b]));
}


//**************************************************************************
//  FLOPPY
//**************************************************************************

void rc759_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_RC759_FORMAT);
}

void rc759_state::floppy_control_w(uint8_t data)
{
	// 7-------  ready control
	// -6------  fdc clock?
	// --5-----  dden?
	// ---4----  precomp 125/250 nsec?
	// ----3---  write precomp
	// -----2--  motor 1
	// ------1-  motor 0
	// -------0  drive select

	logerror("floppy_control_w: %02x\n", data);

	m_fdc->set_floppy(m_floppy[BIT(data, 0)]->get_device());

	if (m_floppy[0]->get_device()) m_floppy[0]->get_device()->mon_w(!BIT(data, 1));
	if (m_floppy[1]->get_device()) m_floppy[1]->get_device()->mon_w(!BIT(data, 2));

	m_fdc->dden_w(BIT(data, 5));
	m_fdc->set_unscaled_clock(BIT(data, 6) ? 2000000 : 1000000);
	m_fdc->set_force_ready(BIT(data, 7));
}

uint8_t rc759_state::floppy_ack_r()
{
	// 7-------  floppy ack
	// -6543210  unused?

	return m_floppy_reserved ? 0x00 : 0x80;
}

void rc759_state::floppy_reserve_w(uint8_t data)
{
	m_floppy_reserved = true;
}

void rc759_state::floppy_release_w(uint8_t data)
{
	m_floppy_reserved = false;
}


//**************************************************************************
//  I/O
//**************************************************************************

uint8_t rc759_state::ppi_porta_r()
{
	uint8_t data = 0;

	data |= m_cas_enabled ? m_cas_data : (m_cas->input() > 0 ? 1 : 0);
	data |= m_isbx->mpst_r() << 1;
	data |= m_isbx->opt0_r() << 2;
	data |= m_isbx->opt1_r() << 3;
	data |= 1 << 4; // mem ident0
	data |= 1 << 5; // mem ident1 (both 1 = 256k installed)
	data |= 0 << 6; // dpc connect (0 = external floppy/printer installed)
	data |= 1 << 7; // not used

	return data;
}

uint8_t rc759_state::ppi_portb_r()
{
	uint8_t data = 0;

	data |= 1 << 0; // 0 = micronet controller installed
	data |= 1 << 1; // rtc type, mm58167/cdp1879
	data |= m_snd->ready_r() << 2;
	data |= 1 << 3; // not used
	data |= 1 << 4; // not used
	data |= m_config->read(); // monitor type and frequency
	data |= 1 << 7; // 0 = enable remote hardware debug (using an isbx351 module)

	return data;
}

void rc759_state::ppi_portc_w(uint8_t data)
{
	// 7-------  keyboard enable
	// -6------  gfx mode
	// --54----  nvram bank
	// ----32--  drq source
	// ------1-  cassette motor
	// -------0  cassette enable

	m_kbd->enable_w(BIT(data, 7));
	m_gfx_mode = BIT(data, 6);
	m_nvram_bank = (data >> 4) & 0x03;
	m_drq_source = (data >> 2) & 0x03;
	m_cas->change_state(BIT(data, 1) ? CASSETTE_MOTOR_DISABLED : CASSETTE_MOTOR_ENABLED, CASSETTE_MASK_MOTOR);
	m_cas_enabled = BIT(data, 0);
}

void rc759_state::centronics_busy_w(int state)
{
	m_centronics_busy = state;
	m_pic->ir6_w(state);
}

void rc759_state::centronics_ack_w(int state)
{
	m_centronics_ack = state;
}

void rc759_state::centronics_fault_w(int state)
{
	m_centronics_fault = state;
}

void rc759_state::centronics_perror_w(int state)
{
	m_centronics_perror = state;
}

void rc759_state::centronics_select_w(int state)
{
	m_centronics_select = state;
}

uint8_t rc759_state::centronics_data_r()
{
	return m_centronics_data;
}

void rc759_state::centronics_data_w(uint8_t data)
{
	m_centronics_data = data;

	m_centronics->write_data0(BIT(data, 0));
	m_centronics->write_data1(BIT(data, 1));
	m_centronics->write_data2(BIT(data, 2));
	m_centronics->write_data3(BIT(data, 3));
	m_centronics->write_data4(BIT(data, 4));
	m_centronics->write_data5(BIT(data, 5));
	m_centronics->write_data6(BIT(data, 6));
	m_centronics->write_data7(BIT(data, 7));
}

uint8_t rc759_state::centronics_control_r()
{
	uint8_t data = 0;

	data |= m_centronics_busy << 0;
	data |= m_centronics_ack << 1;
	data |= m_centronics_fault << 2;
	data |= m_centronics_perror << 3;
	data |= m_centronics_select << 4;
	data |= !m_centronics_strobe << 5;
	data |= !m_centronics_init << 6;
	data |= !m_centronics_select_in << 7;

	return data;
}

void rc759_state::centronics_control_w(uint8_t data)
{
	logerror("centronics_control_w: %02x\n", data);

	m_centronics_strobe = BIT(data, 0);
	m_centronics_init = BIT(data, 2);
	m_centronics_select_in = BIT(data, 4);

	m_centronics->write_strobe(m_centronics_strobe);
	m_centronics->write_autofd(BIT(data, 1));
	m_centronics->write_init(m_centronics_init);
	m_centronics->write_select_in(m_centronics_select_in);
}


//**************************************************************************
//  SOUND/RTC
//**************************************************************************

void rc759_state::rtc_data_w(uint8_t data)
{
	m_rtc_write_data = data;
}

uint8_t rc759_state::rtc_data_r()
{
	return m_rtc_read_data;
}

void rc759_state::rtc_addr_w(uint8_t data)
{
	if (BIT(data, 7))
		m_rtc_read_addr = data & 0x1f;
	else
		m_rtc_write_addr = data & 0x1f;

	if (BIT(data, 6) && BIT(m_rtc_strobe, 6) == 0)
		m_rtc->write(m_rtc_write_addr, m_rtc_write_data);

	if (BIT(data, 5) && BIT(m_rtc_strobe, 5) == 0)
		m_rtc_read_data = m_rtc->read(m_rtc_read_addr);

	m_rtc_strobe = data;
}


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void rc759_state::i186_timer0_w(int state)
{
	m_cas_data = 1;

	if (m_cas_enabled)
		m_cas_data = state ? 0 : 1;

	m_cas->output(m_cas_data ? -1.0 : 1.0);
}

void rc759_state::i186_timer1_w(int state)
{
	m_speaker->level_w(state);
}

// 256x4 nvram is bank-switched using ppi port c, bit 4 and 5
void rc759_state::nvram_init(nvram_device &nvram, void *data, size_t size)
{
	memset(data, 0x00, size);
	memset(data, 0xaa, 1);
}

uint8_t rc759_state::nvram_r(offs_t offset)
{
	offs_t addr = (m_nvram_bank << 6) | offset;

	logerror("nvram_r(%02x)\n", addr);

	if (addr & 1)
		return (m_nvram_mem[addr >> 1] & 0xf0) >> 4;
	else
		return (m_nvram_mem[addr >> 1] & 0x0f) >> 0;
}

void rc759_state::nvram_w(offs_t offset, uint8_t data)
{
	offs_t addr = (m_nvram_bank << 6) | offset;

	logerror("nvram_w(%02x): %02x\n", addr, data);

	if (addr & 1)
		m_nvram_mem[addr >> 1] = ((data << 4) & 0xf0) | (m_nvram_mem[addr >> 1] & 0x0f);
	else
		m_nvram_mem[addr >> 1] = (m_nvram_mem[addr >> 1] & 0xf0) | (data & 0x0f);
}

uint8_t rc759_state::irq_callback()
{
	return m_pic->acknowledge();
}

void rc759_state::machine_start()
{
	m_nvram_mem.resize(256 / 2);
	m_nvram->set_base(&m_nvram_mem[0], 256 / 2);
}

void rc759_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

static void rc759_floppies(device_slot_interface &device)
{
	device.option_add("hd", FLOPPY_525_HD);
}

void rc759_state::rc759(machine_config &config)
{
	I80186(config, m_maincpu, 6000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &rc759_state::rc759_map);
	m_maincpu->set_addrmap(AS_IO, &rc759_state::rc759_io);
	m_maincpu->read_slave_ack_callback().set(FUNC(rc759_state::irq_callback));
	m_maincpu->tmrout0_handler().set(FUNC(rc759_state::i186_timer0_w));
	m_maincpu->tmrout1_handler().set(FUNC(rc759_state::i186_timer1_w));

	PIC8259(config, m_pic);
	m_pic->out_int_callback().set(m_maincpu, FUNC(i80186_cpu_device::int0_w));

	NVRAM(config, "nvram").set_custom_handler(FUNC(rc759_state::nvram_init));

	I8255(config, m_ppi);
	m_ppi->in_pa_callback().set(FUNC(rc759_state::ppi_porta_r));
	m_ppi->in_pb_callback().set(FUNC(rc759_state::ppi_portb_r));
	m_ppi->out_pc_callback().set(FUNC(rc759_state::ppi_portc_w));

	MM58167(config, "rtc", 32.768_kHz_XTAL).irq().set(m_pic, FUNC(pic8259_device::ir3_w));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(rc759_state::centronics_busy_w));
	m_centronics->ack_handler().set(FUNC(rc759_state::centronics_ack_w));
	m_centronics->fault_handler().set(FUNC(rc759_state::centronics_fault_w));
	m_centronics->perror_handler().set(FUNC(rc759_state::centronics_perror_w));
	m_centronics->select_handler().set(FUNC(rc759_state::centronics_select_w));

	// video
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(1250000 * 16, 896, 96, 816, 377, 4, 364); // 22 kHz setting
	screen.set_screen_update("txt", FUNC(i82730_device::screen_update));
	screen.screen_vblank().set(m_maincpu, FUNC(i80186_cpu_device::tmrin0_w)); // TMRIN0 source not documented, but self-test needs something like this

	I82730(config, m_txt, 1250000, m_maincpu);
	m_txt->set_screen("screen");
	m_txt->set_update_row_callback(FUNC(rc759_state::txt_update_row));
	m_txt->sint().set(m_pic, FUNC(pic8259_device::ir4_w));

	PALETTE(config, m_palette).set_entries(64);

	// sound
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);
	SN76489A(config, m_snd, 20_MHz_XTAL / 10).add_route(ALL_OUTPUTS, "mono", 1.0);

	CASSETTE(config, m_cas);
	m_cas->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cas->add_route(ALL_OUTPUTS, "mono", 0.05);

	// expansion slot
	ISBX_SLOT(config, m_isbx, 0, isbx_cards, nullptr);
	m_isbx->mintr0().set("maincpu", FUNC(i80186_cpu_device::int1_w));
	m_isbx->mintr1().set("maincpu", FUNC(i80186_cpu_device::int3_w));
	m_isbx->mdrqt().set("maincpu", FUNC(i80186_cpu_device::drq0_w));

	// floppy
	WD2797(config, m_fdc, 2000000);
	m_fdc->intrq_wr_callback().set(m_pic, FUNC(pic8259_device::ir0_w));
	m_fdc->drq_wr_callback().set(m_maincpu, FUNC(i80186_cpu_device::drq0_w));

	FLOPPY_CONNECTOR(config, "fdc:0", rc759_floppies, "hd", rc759_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", rc759_floppies, "hd", rc759_state::floppy_formats);

	// keyboard
	RC759_KBD_HLE(config, m_kbd);
	m_kbd->int_handler().set(m_pic, FUNC(pic8259_device::ir1_w));
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( rc759 )
	ROM_REGION16_LE(0x8000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "1-21", "1? Version 2.1")
	ROMX_LOAD("rc759-1-2.1.rom", 0x0000, 0x8000, CRC(3a777d56) SHA1(a8592d61d5e1f92651a6f5e41c4ba14c9b6cc39b), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "1-51", "1? Version 5.1")
	ROMX_LOAD("rc759-1-5.1.rom", 0x0000, 0x8000, CRC(e1d53845) SHA1(902dc5ce28efd26b4f9c631933e197c2c187a7f1), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "2-40", "2? Version 4.0")
	ROMX_LOAD("rc759-2-4.0.rom", 0x0000, 0x8000, CRC(d3cb752a) SHA1(f50afe5dfa1b33a36a665d32d57c8c41d6685005), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "2-51", "2? Version 5.1")
	ROMX_LOAD("rc759-2-5.1.rom", 0x0000, 0x8000, CRC(00a31948) SHA1(23c4473c641606a56473791773270411d1019248), ROM_BIOS(3))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY           FULLNAME           FLAGS
COMP( 1984, rc759, 0,      0,      rc759,   rc759, rc759_state, empty_init, "Regnecentralen", "RC759 Piccoline", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



rd100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/********************************************************************************

    Data RD100

    Little is known about this system except for a few PCB pictures. No
    manuals, schematic or circuit description have been found.

    The RD100 was apparently sold in France under the "Superkit" brand. There
    appear to have been several versions. Earlier models had 7-segment LEDs
    and rudimentary keyboards. The model dumped here is apparently the K32K,
    which had a 16x2 character LCD display, a QWERTY keyboard and non-numeric
    keypad, Centronics and RS-232 ports, and an extension board for prototyping.

*********************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/m6809/m6809.h"
#include "machine/6821pia.h"
#include "machine/6840ptm.h"
#include "machine/6850acia.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"


namespace {

class rd100_state : public driver_device
{
public:
	rd100_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_keys(*this, "KEY%u", 0U)
		, m_pia1(*this, "pia1")
	{ }

	void rd100(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	HD44780_PIXEL_UPDATE(pixel_update);

	uint8_t keys_r();
	void key_scan_w(uint8_t data);
	int shift_r();
	int ctrl_r();

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_ioport_array<9> m_keys;
	required_device<pia6821_device> m_pia1;

	uint8_t m_key_scan = 0;
	bool m_shift = false;
	bool m_ctrl = false;
};


void rd100_state::machine_start()
{
	save_item(NAME(m_key_scan));
}

void rd100_state::machine_reset()
{
	m_key_scan = 0;
	m_shift = 0;
	m_ctrl = 0;
}

HD44780_PIXEL_UPDATE(rd100_state::pixel_update)
{
	if (pos < 16)
		bitmap.pix(line * 8 + y, pos * 6 + x) = state;
}

uint8_t rd100_state::keys_r()
{
	uint8_t result = 0xff;
	for (int i = 0; i < 8; i++)
		if (!BIT(m_key_scan, i))
			result &= m_keys[i]->read();

	return result;
}

void rd100_state::key_scan_w(uint8_t data)
{
	m_key_scan = data;
}

int rd100_state::shift_r()
{
	if (m_shift)
	{
		m_shift = 0;
		m_pia1->ca1_w(1);
	}
	bool ky = BIT(m_keys[8]->read(), 0);
	if (!ky)
		m_shift = 1;
	return ky;
}

int rd100_state::ctrl_r()
{
	if (m_ctrl)
	{
		m_ctrl = 0;
		m_pia1->cb1_w(1);
	}
	bool ky = BIT(m_keys[8]->read(), 1);
	if (!ky)
		m_ctrl = 1;
	return ky;
}

void rd100_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).ram();
	map(0x8404, 0x8407).rw("piax", FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x8408, 0x840b).rw("piay", FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x8608, 0x860f).rw("timer", FUNC(ptm6840_device::read), FUNC(ptm6840_device::write));
	map(0x8610, 0x8611).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0x8640, 0x8643).rw(m_pia1, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x8680, 0x8683).rw("pia2", FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x8700, 0x8701).rw("hd44780", FUNC(hd44780_device::read), FUNC(hd44780_device::write));
	map(0x8800, 0xffff).rom().region("roms", 0x800);
}

/* Input ports */
static INPUT_PORTS_START( rd100 )
	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('8')   PORT_CHAR('(')                 PORT_CODE(KEYCODE_8)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ESC") PORT_CHAR(0x1b)                PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('I')   PORT_CHAR('i') PORT_CHAR(0x09) PORT_CODE(KEYCODE_I)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(' ')                                  PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('K')   PORT_CHAR('k') PORT_CHAR(0x0b) PORT_CODE(KEYCODE_K)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('@')   PORT_CHAR('`')                 PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',')   PORT_CHAR('<')                 PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\\')  PORT_CHAR('|') PORT_CHAR(0x1c) PORT_CODE(KEYCODE_LSHIFT)

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('9')   PORT_CHAR(')')                 PORT_CODE(KEYCODE_9)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1')   PORT_CHAR('!')                 PORT_CODE(KEYCODE_1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('O')   PORT_CHAR('o') PORT_CHAR(0x0f) PORT_CODE(KEYCODE_O)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('Q')   PORT_CHAR('q') PORT_CHAR(0x11) PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('L')   PORT_CHAR('l') PORT_CHAR(0x0c) PORT_CODE(KEYCODE_L)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('A')   PORT_CHAR('a') PORT_CHAR(0x01) PORT_CODE(KEYCODE_A)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('.')   PORT_CHAR('>')                 PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('Z')   PORT_CHAR('z') PORT_CHAR(0x1a) PORT_CODE(KEYCODE_Z)

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0')   PORT_CHAR('_') PORT_CHAR(0x1f) PORT_CODE(KEYCODE_0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2')   PORT_CHAR('"')                 PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('P')   PORT_CHAR('p') PORT_CHAR(0x10) PORT_CODE(KEYCODE_P)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('W')   PORT_CHAR('w') PORT_CHAR(0x17) PORT_CODE(KEYCODE_W)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(';')   PORT_CHAR('+')                 PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('S')   PORT_CHAR('s') PORT_CHAR(0x13) PORT_CODE(KEYCODE_S)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('[')   PORT_CHAR('{')                 PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('X')   PORT_CHAR('x') PORT_CHAR(0x18) PORT_CODE(KEYCODE_X)

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(':')   PORT_CHAR('*')                 PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3')   PORT_CHAR('#')                 PORT_CODE(KEYCODE_3)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('^')   PORT_CHAR('~') PORT_CHAR(0x1e) PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('E')   PORT_CHAR('e') PORT_CHAR(0x05) PORT_CODE(KEYCODE_E)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('/')   PORT_CHAR('?')                 PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('D')   PORT_CHAR('d') PORT_CHAR(0x04) PORT_CODE(KEYCODE_D)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(']')   PORT_CHAR('}') PORT_CHAR(0x1d) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('C')   PORT_CHAR('c') PORT_CHAR(0x03) PORT_CODE(KEYCODE_C)

	PORT_START("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('-')   PORT_CHAR('=')                 PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4')   PORT_CHAR('$')                 PORT_CODE(KEYCODE_4)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BS")  PORT_CHAR(0x08)                PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('R')   PORT_CHAR('r') PORT_CHAR(0x12) PORT_CODE(KEYCODE_R)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CR")  PORT_CHAR(0x0d)                PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('F')   PORT_CHAR('f') PORT_CHAR(0x06) PORT_CODE(KEYCODE_F)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('V')   PORT_CHAR('v') PORT_CHAR(0x16) PORT_CODE(KEYCODE_V)

	PORT_START("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5')   PORT_CHAR('%')                 PORT_CODE(KEYCODE_5)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('T')   PORT_CHAR('t') PORT_CHAR(0x14) PORT_CODE(KEYCODE_T)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('G')   PORT_CHAR('g') PORT_CHAR(0x07) PORT_CODE(KEYCODE_G)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('B')   PORT_CHAR('b') PORT_CHAR(0x02) PORT_CODE(KEYCODE_B)

	PORT_START("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6')   PORT_CHAR('&')                 PORT_CODE(KEYCODE_6)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('Y')   PORT_CHAR('y') PORT_CHAR(0x19) PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('H')   PORT_CHAR('h')                 PORT_CODE(KEYCODE_H)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('N')   PORT_CHAR('n') PORT_CHAR(0x0e) PORT_CODE(KEYCODE_N)

	PORT_START("KEY7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7')   PORT_CHAR('\'')                PORT_CODE(KEYCODE_7)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('U')   PORT_CHAR('u') PORT_CHAR(0x15) PORT_CODE(KEYCODE_U)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('J')   PORT_CHAR('j') PORT_CHAR(0x0a) PORT_CODE(KEYCODE_J)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('M')   PORT_CHAR('m')                 PORT_CODE(KEYCODE_M)

	PORT_START("KEY8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CHAR(UCHAR_SHIFT_1)     PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL")  PORT_CHAR(UCHAR_SHIFT_2)     PORT_CODE(KEYCODE_TAB)
INPUT_PORTS_END

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_2400 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_2400 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void rd100_state::rd100(machine_config &config)
{
	// basic machine hardware
	MC6809(config, m_maincpu, 4_MHz_XTAL); // MC6809P???
	m_maincpu->set_addrmap(AS_PROGRAM, &rd100_state::mem_map);

	PIA6821(config, m_pia1);
	m_pia1->readpa_handler().set(FUNC(rd100_state::keys_r));
	m_pia1->writepb_handler().set(FUNC(rd100_state::key_scan_w));
	m_pia1->readca1_handler().set(FUNC(rd100_state::shift_r));
	m_pia1->readcb1_handler().set(FUNC(rd100_state::ctrl_r));

	PIA6821(config, "pia2");
	PIA6821(config, "piax");
	PIA6821(config, "piay");

	ptm6840_device &timer(PTM6840(config, "timer", 4_MHz_XTAL / 4));
	timer.o3_callback().set("acia", FUNC(acia6850_device::write_txc));
	timer.o3_callback().append("acia", FUNC(acia6850_device::write_rxc));

	acia6850_device &acia(ACIA6850(config, "acia"));
	acia.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	acia.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_screen_update("hd44780", FUNC(hd44780_device::screen_update));
	screen.set_size(16*6, 16);
	screen.set_visarea(0, 16*6-1, 0, 16-1);
	screen.set_palette("palette");

	hd44780_device &hd44780(HD44780(config, "hd44780", 270'000)); // TODO: clock not measured, datasheet typical clock used
	hd44780.set_lcd_size(2, 16);
	hd44780.set_pixel_update_cb(FUNC(rd100_state::pixel_update));

	PALETTE(config, "palette").set_entries(2);

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set("acia", FUNC(acia6850_device::write_rxd));
	rs232.cts_handler().set("acia", FUNC(acia6850_device::write_cts));
	rs232.dsr_handler().set("acia", FUNC(acia6850_device::write_dcd));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));
}

ROM_START( rd100 )
	ROM_REGION( 0x8000, "roms", 0 )
	ROM_LOAD( "pak3-01.bin",  0x0000, 0x8000, CRC(cf5bbf01) SHA1(0673f4048d700b84c30781af23fbeabe0b994306) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY      FULLNAME  FLAGS
COMP( 1989, rd100, 0,      0,      rd100,   rd100, rd100_state, empty_init, "Data R.D.", "RD100",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



regence.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
/*******************************************************************************

La Régence

NOTE: The hardware triggers an NMI on power-off (or power-failure). If this isn't
done, NVRAM fails at next power-on.

French chess computer by "France Double R". German distribution by Sandy Electronic,
who sub-titled it TSB 4 (Turniersensorbrett), the EPROM contents is the same.
There is no English version.

the chess engine is Richard Lang's Cyrus. This was from when he was working for
Intelligent Software, before he got hired by Hegener + Glaser.

Hardware notes:
- PCB label: FRANCE DOUBLE R, MADE IN FRANCE
- Sharp LH0080A Z80A @ 4 MHz (8MHz XTAL)
- 3*4KB ROM, sockets support up to 48KB ROM
- 2KB battery-backed RAM (MSM5128-15RS), 3 sockets, only middle one used
- TTL, piezo, 8*8+4 LEDs, magnetic sensors

TODO:
- if/when MAME supports an exit callback, hook up power-off switch to that

*******************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "regence.lh"


namespace {

class regence_state : public driver_device
{
public:
	regence_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_board(*this, "board"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(power_off);

	void regence(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD { m_power = true; }

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<sensorboard_device> m_board;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<2> m_inputs;

	bool m_power = false;
	u8 m_inp_mux = 0;
	u8 m_led_data = 0;

	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_display();
	void control_w(u8 data);
	void leds_w(u8 data);
	u8 input_r();
};

void regence_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_power));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_led_data));
}

INPUT_CHANGED_MEMBER(regence_state::power_off)
{
	// NMI at power-off (it prepares nvram for next power-on)
	if (newval && m_power)
	{
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
		m_power = false;
	}
}



/*******************************************************************************
    I/O
*******************************************************************************/

void regence_state::update_display()
{
	m_display->matrix(1 << m_inp_mux, m_led_data);
}

void regence_state::control_w(u8 data)
{
	// d0-d3: input mux/led select
	m_inp_mux = data & 0xf;
	update_display();

	// d7: speaker out
	m_dac->write(BIT(data, 7));

	// other: ?
}

void regence_state::leds_w(u8 data)
{
	// d0-d7: led data
	m_led_data = data;
	update_display();
}

u8 regence_state::input_r()
{
	u8 data = 0;

	// d0-d7: multiplexed inputs
	// read chessboard sensors
	if (m_inp_mux < 8)
		data = m_board->read_file(m_inp_mux, true);

	// read other buttons
	else if (m_inp_mux < 10)
		data = m_inputs[m_inp_mux - 8]->read();

	return data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void regence_state::main_map(address_map &map)
{
	map(0x0000, 0x0fff).rom();
	map(0x4000, 0x4fff).rom();
	map(0x8000, 0x8fff).rom();
	map(0xd000, 0xd7ff).ram().share("nvram");
	map(0xf000, 0xf000).rw(FUNC(regence_state::input_r), FUNC(regence_state::control_w));
	map(0xf800, 0xf800).w(FUNC(regence_state::leds_w));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( regence ) // see comments for German version labels
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Changement de Position (Set Up)") // Veränderung
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME(u8"Retour en Arrière (Take Back)") // Zug Zurück
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("Nouvelle Partie (New Game)")      // Neues Spiel (press after setup)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("King")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Rook")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Son (Sound)")    // Ton
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Niveau (Level)") // Stufe
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_CODE(KEYCODE_H) PORT_NAME("Marche/Arret (Move/Halt)") // Zug-Halt
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("Noir (Black)")   // Schwarz
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Blanc (White)")  // Weiss
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Knight")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("POWER") // needs to be triggered for nvram to work
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(regence_state::power_off), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void regence_state::regence(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 8_MHz_XTAL/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &regence_state::main_map);

	m_maincpu->set_periodic_int(FUNC(regence_state::irq0_line_hold), attotime::from_hz(448)); // from 555, measured

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 8);
	config.set_default_layout(layout_regence);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( regence )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("arc0.ic13", 0x0000, 0x1000, CRC(ac6a0a67) SHA1(52b115c7cd372dfbad14b00854aa4f6f75a937d3) ) // M5L2732K
	ROM_LOAD("arc1.ic12", 0x4000, 0x1000, CRC(5c2fb0c7) SHA1(811ab3d7cefcf872741eb2265115080aaf913f0f) ) // "
	ROM_LOAD("arc2.ic11", 0x8000, 0x1000, CRC(e4c39dbd) SHA1(b6a6d1d39f73a2ff1ade6205bdf180be13e84df3) ) // "
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1982, regence, 0,      0,      regence, regence, regence_state, empty_init, "France Double R / Intelligent Software", u8"La Régence", MACHINE_SUPPORTS_SAVE )



renaissance.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Saitek Kasparov Renaissance

Saitek's 2nd version modular chesscomputer. It accepts the same modules as
Leonardo/Galileo. "OSA" version for Renaissance is 1.5.

NOTE: In order for NVRAM to work properly, press the STOP button to turn off
the chesscomputer before exiting MAME. Unlike Leonardo/Galileo, it looks like
it will always do a cold boot if you reset without having pressed STOP.

Hardware notes:
- HD6301Y0P (mode 1) or HD6303YP MCU @ 10MHz
- 8KB RAM, 32KB ROM
- "HELIOS" I/O (NEC gate array)
- Epson SED1502F, LCD screen
- magnet sensors chessboard with 81 leds

The 6301Y0 seen on one of them, was a SX8A 6301Y0G84P, this is in fact the
MCU(+internal maskrom, disabled here) used in Saitek Conquistador.

The LCD screen is fairly large, it's the same one as in Saitek Simultano,
so a chessboard display + 7seg info. It's on a small drawer that can be
pushed in to hide the chessboard display.

TODO:
- fart noise at boot if maestroa module is inserted
- weird beep at boot if sparc module is inserted (related to above?)
- make it a subdriver of leonardo.cpp? or too many differences

*******************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "bus/saitek_osa/expansion.h"
#include "cpu/m6800/m6801.h"
#include "machine/input_merger.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"
#include "video/sed1500.h"

#include "render.h"
#include "screen.h"
#include "speaker.h"

// internal artwork
#include "saitek_renaissance.lh"


namespace {

class ren_state : public driver_device
{
public:
	ren_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_expansion(*this, "exp"),
		m_stb(*this, "stb"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_lcd_pwm(*this, "lcd_pwm"),
		m_lcd(*this, "lcd"),
		m_dac(*this, "dac"),
		m_rs232(*this, "rs232"),
		m_inputs(*this, "IN.%u", 0),
		m_out_lcd(*this, "s%u.%u", 0U, 0U)
	{ }

	template <int N> DECLARE_INPUT_CHANGED_MEMBER(change_view);
	DECLARE_INPUT_CHANGED_MEMBER(go_button);

	void ren(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<hd6303y_cpu_device> m_maincpu;
	required_device<saitekosa_expansion_device> m_expansion;
	required_device<input_merger_device> m_stb;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<pwm_display_device> m_lcd_pwm;
	required_device<sed1502_device> m_lcd;
	required_device<dac_1bit_device> m_dac;
	required_device<rs232_port_device> m_rs232;
	required_ioport_array<8+1> m_inputs;
	output_finder<16, 34> m_out_lcd;

	int m_ack_state = 0;
	int m_rts_state = 0;
	u8 m_inp_mux = 0;
	u8 m_led_data[2] = { };

	void main_map(address_map &map) ATTR_COLD;

	void lcd_pwm_w(offs_t offset, u8 data);
	void lcd_output_w(offs_t offset, u64 data);

	void standby(int state);
	void update_display();
	void mux_w(u8 data);
	void leds_w(u8 data);
	void control_w(u8 data);
	u8 control_r();
	void exp_rts_w(int state);

	u8 p2_r();
	void p2_w(u8 data);
	u8 p5_r();
	void p5_w(u8 data);
	u8 p6_r();
	void p6_w(u8 data);
};

void ren_state::machine_start()
{
	m_out_lcd.resolve();

	save_item(NAME(m_ack_state));
	save_item(NAME(m_rts_state));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_led_data));
}

template <int N> INPUT_CHANGED_MEMBER(ren_state::change_view)
{
	if (oldval && !newval)
	{
		// meant for changing lcd drawer view
		render_target *target = machine().render().first_target();
		target->set_view(target->view() + N);
	}
}



/*******************************************************************************
    I/O
*******************************************************************************/

// power

void ren_state::machine_reset()
{
	m_stb->in_clear<0>();
}

void ren_state::standby(int state)
{
	if (state)
	{
		m_display->clear();
		m_lcd_pwm->clear();
	}
}

INPUT_CHANGED_MEMBER(ren_state::go_button)
{
	if (newval && m_maincpu->standby())
	{
		m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
		machine_reset();
	}
}


// LCD

void ren_state::lcd_pwm_w(offs_t offset, u8 data)
{
	m_out_lcd[offset & 0x3f][offset >> 6] = data;
}

void ren_state::lcd_output_w(offs_t offset, u64 data)
{
	m_lcd_pwm->write_row(offset, m_maincpu->standby() ? 0 : data);
}


// misc

void ren_state::update_display()
{
	m_display->matrix_partial(0, 9, 1 << (m_inp_mux & 0xf), (m_inp_mux << 4 & 0x100) | m_led_data[0]);
	m_display->matrix_partial(9, 1, 1, (m_inp_mux >> 2 & 0x38) | m_led_data[1]);
}

void ren_state::mux_w(u8 data)
{
	// d0-d3 input/chessboard led mux
	// d4: chessboard led data
	// d5: module led
	// d6,d7: mode led
	m_inp_mux = data ^ 0x20;
	update_display();
}

void ren_state::leds_w(u8 data)
{
	// chessboard led data
	m_led_data[0] = data;
	update_display();
}

void ren_state::control_w(u8 data)
{
	// d1: speaker out
	m_dac->write(BIT(data, 1));

	// d2: comm led
	m_led_data[1] = (m_led_data[1] & ~0x4) | (~data & 0x4);
	update_display();

	// other: ?
}

u8 ren_state::control_r()
{
	// d5,d6: freq sel?
	// d7: ?
	return 0;
}

void ren_state::exp_rts_w(int state)
{
	// recursive NAND with ACK-P
	if (state && m_ack_state)
		m_expansion->ack_w(m_ack_state);
	m_rts_state = state;
}


// MCU ports

u8 ren_state::p2_r()
{
	u8 data = 0;

	// P20-P22: multiplexed inputs
	if (~m_inp_mux & 8)
		data = m_inputs[m_inp_mux & 7]->read();

	// P23: serial rx
	data |= m_rs232->rxd_r() << 3;

	return ~data ^ 8;
}

void ren_state::p2_w(u8 data)
{
	// P24: serial tx (TTL)
	m_rs232->write_txd(BIT(data, 4));

	// P25,P26: b/w leds
	m_led_data[1] = (m_led_data[1] & ~3) | (~data >> 5 & 3);
	update_display();
}

u8 ren_state::p5_r()
{
	// P56: battery status
	u8 b = m_inputs[8]->read() & 0x40;

	// P54: IS strobe (handled with inputline)
	// other: ?
	return b | (0xff ^ 0x50);
}

void ren_state::p5_w(u8 data)
{
	// P51: expansion NMI-P
	m_expansion->nmi_w(BIT(data, 1));

	// P53: NAND with STB-P
	m_stb->in_w<1>(BIT(data, 3));

	// P55: expansion ACK-P (recursive NAND with RTS-P)
	int ack_state = BIT(data, 5);
	if (m_rts_state || !ack_state)
		m_expansion->ack_w(ack_state);
	m_ack_state = ack_state;

	// P50: power-off on falling edge
	m_expansion->pw_w(data & 1);

	// other: ?
}

u8 ren_state::p6_r()
{
	// P60-P67: read chessboard sensors and module data
	return ~m_board->read_file(m_inp_mux & 0xf) & m_expansion->data_r();
}

void ren_state::p6_w(u8 data)
{
	// P60-P67: module data
	m_expansion->data_w(data);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void ren_state::main_map(address_map &map)
{
	map(0x2000, 0x2000).w(FUNC(ren_state::mux_w));
	map(0x2400, 0x2400).w(FUNC(ren_state::leds_w));
	map(0x2600, 0x2600).rw(FUNC(ren_state::control_r), FUNC(ren_state::control_w));
	map(0x4000, 0x5fff).ram().share("nvram");
	map(0x6000, 0x607f).w("lcd", FUNC(sed1502_device::write));
	map(0x8000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( ren )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Scroll")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Tab / Color")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_EQUALS) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Function")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Sound")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("Stop")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Library")

	PORT_START("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Info")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Play")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_L) PORT_NAME("Level")

	PORT_START("IN.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Normal")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Analysis")

	PORT_START("IN.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("Set Up")

	PORT_START("IN.8")
	PORT_CONFNAME( 0x40, 0x00, "Battery Status" )
	PORT_CONFSETTING(    0x40, "Low" )
	PORT_CONFSETTING(    0x00, DEF_STR( Normal ) )

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ren_state::go_button), 0) PORT_NAME("Go")

	PORT_START("VIEW")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ren_state::change_view<+1>), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ren_state::change_view<-1>), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void ren_state::ren(machine_config &config)
{
	// basic machine hardware
	HD6303Y(config, m_maincpu, 10_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &ren_state::main_map);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(hd6303y_cpu_device::nvram_set_battery));
	m_maincpu->standby_cb().append(FUNC(ren_state::standby));
	m_maincpu->in_p2_cb().set(FUNC(ren_state::p2_r));
	m_maincpu->out_p2_cb().set(FUNC(ren_state::p2_w));
	m_maincpu->in_p5_cb().set(FUNC(ren_state::p5_r));
	m_maincpu->out_p5_cb().set(FUNC(ren_state::p5_w));
	m_maincpu->in_p6_cb().set(FUNC(ren_state::p6_r));
	m_maincpu->out_p6_cb().set(FUNC(ren_state::p6_w));

	INPUT_MERGER_ANY_LOW(config, m_stb);
	m_stb->output_handler().set_inputline(m_maincpu, M6801_IS3_LINE);

	config.set_maximum_quantum(attotime::from_hz(6000));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	SED1502(config, m_lcd, 32768).write_segs().set(FUNC(ren_state::lcd_output_w));
	PWM_DISPLAY(config, m_lcd_pwm).set_size(16, 34);
	m_lcd_pwm->set_refresh(attotime::from_hz(30));
	m_lcd_pwm->output_x().set(FUNC(ren_state::lcd_pwm_w));

	auto &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(873/2, 1080/2);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(9+1, 9);
	config.set_default_layout(layout_saitek_renaissance);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);

	// expansion module (configure after video)
	SAITEKOSA_EXPANSION(config, m_expansion, saitekosa_expansion_modules);
	m_expansion->stb_handler().set(m_stb, FUNC(input_merger_device::in_w<0>));
	m_expansion->rts_handler().set(FUNC(ren_state::exp_rts_w));

	// rs232 (configure after expansion module)
	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( renaissa )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("sw7_518d_u3.u3", 0x8000, 0x8000, CRC(21d2405f) SHA1(6ddcf9bdd30aa446fcaeab919a8f950dc3428365) ) // HN27C256AG-10

	ROM_REGION( 795951, "screen", 0 )
	ROM_LOAD("simultano.svg", 0, 795951, CRC(ac9942bb) SHA1(f9252e5bf7b8af698a403c3f8f5ea9e475e0bf0b) )
ROM_END

ROM_START( renaissaa )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("sx7_518c.u3", 0x8000, 0x8000, CRC(c909ff4d) SHA1(d6509f5a267d98287197195bd2949a0a190758f1) ) // MBM27C256H-10

	ROM_REGION( 795951, "screen", 0 )
	ROM_LOAD("simultano.svg", 0, 795951, CRC(ac9942bb) SHA1(f9252e5bf7b8af698a403c3f8f5ea9e475e0bf0b) )
ROM_END

ROM_START( renaissab )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("sx7_518b.u3", 0x8000, 0x8000, CRC(a0c3ffe8) SHA1(fa170a6d4d54d41de77e0bb72f969219e6f376af) ) // MBM27C256H-10

	ROM_REGION( 795951, "screen", 0 )
	ROM_LOAD("simultano.svg", 0, 795951, CRC(ac9942bb) SHA1(f9252e5bf7b8af698a403c3f8f5ea9e475e0bf0b) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT    COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY, FULLNAME, FLAGS
SYST( 1989, renaissa,  0,        0,      ren,     ren,   ren_state, empty_init, "Saitek / Heuristic Software", "Kasparov Renaissance (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, renaissaa, renaissa, 0,      ren,     ren,   ren_state, empty_init, "Saitek / Heuristic Software", "Kasparov Renaissance (set 2)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, renaissab, renaissa, 0,      ren,     ren,   ren_state, empty_init, "Saitek / Heuristic Software", "Kasparov Renaissance (set 3)", MACHINE_SUPPORTS_SAVE )



replicator.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Felipe Sanches
/*
    Replicator 1 desktop 3d printer

    driver by Felipe Correa da Silva Sanches <fsanches@metamaquina.com.br>

    Changelog:

    2013 DEC 28 [Felipe Sanches]:
        * LCD now works. We can see the firmware boot screen :-)

    2013 DEC 24 [Felipe Sanches]:
        * declaration of internal EEPROM

    2013 DEC 18 [Felipe Sanches]:
        * Initial driver skeleton
*/

// TODO:
// * figure out what's wrong with the keypad inputs (interface seems to be blocked in the first screen)
// * fix avr8 timer/counter #0 (toggle OC0B) and #5 (overflow interrupt "Microsecond timer") so that we get the buzzer to work
// * figure-out correct size of internal EEPROM
// * emulate an SD Card
// * implement avr8 WDR (watchdog reset) opcode

#include "emu.h"
#include "cpu/avr8/avr8.h"
#include "sound/dac.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#define LOG_PORT_A      (1U << 1)
#define LOG_PORT_B      (1U << 2)
#define LOG_PORT_C      (1U << 3)
#define LOG_PORT_D      (1U << 4)
#define LOG_PORT_E      (1U << 5)
#define LOG_PORT_F      (1U << 6)
#define LOG_PORT_G      (1U << 7)
#define LOG_PORT_H      (1U << 8)
#define LOG_PORT_J      (1U << 9)
#define LOG_PORT_K      (1U << 10)
#define LOG_PORT_L      (1U << 11)
#define LOG_LCD_CLK     (1U << 12)
#define LOG_LCD_SHIFT   (1U << 13)

#define VERBOSE         (0)
#include "logmacro.h"


namespace {

#define MASTER_CLOCK    16000000

//Port A bits:
//Bit 0 unused
//Bit 1 unused
#define A_AXIS_DIR (1 << 2)
#define A_AXIS_STEP (1 << 3)
#define A_AXIS_EN (1 << 4)
#define A_AXIS_POT (1 << 5)
#define B_AXIS_DIR (1 << 6)
#define B_AXIS_STEP (1 << 7)

//Port B bits:
#define SD_CS (1 << 0)
#define SCK_1280 (1 << 1)
#define MOSI_1280 (1 << 2)
#define MISO_1280 (1 << 3)
#define EX2_PWR_CHECK (1 << 4)
#define EX2_HEAT (1 << 5)
#define EX2_FAN (1 << 6)
#define BLINK (1 << 7)

//Port C bits:
#define EX2_1280 (1 << 0)
#define EX1_1280 (1 << 1)
#define LCD_CLK (1 << 2)
#define LCD_DATA (1 << 3)
#define LCD_STROBE (1 << 4)
#define RLED (1 << 5)
#define GLED (1 << 6)
#define DETECT (1 << 7)

//Port D bits:
#define PORTD_SCL (1 << 0)
#define PORTD_SDA (1 << 1)
#define EX_RX_1280 (1 << 2)
#define EX_TX_1280 (1 << 3)
//Bit 4 unused
//Bit 5 unused
//Bit 6 unused
//Bit 7 unused

//Port E bits:
#define RX_1280 (1 << 0)
#define TX_1280 (1 << 1)
#define THERMO_SCK (1 << 2)
#define THERMO_CS1 (1 << 3)
#define THERMO_CS2 (1 << 4)
#define THERMO_DO (1 << 5)
//Bit 6 unused
//Bit 7 unused

//Port F bits:
#define X_AXIS_DIR (1 << 0)
#define X_AXIS_STEP (1 << 1)
#define X_AXIS_EN (1 << 2)
#define X_AXIS_POT (1 << 3)
#define Y_AXIS_DIR (1 << 4)
#define Y_AXIS_STEP (1 << 5)
#define Y_AXIS_EN (1 << 6)
#define Y_AXIS_POT (1 << 7)

//Port G bits:
#define EX4_1280 (1 << 0)
#define EX3_1280 (1 << 1)
#define B_AXIS_EN (1 << 2)
//Bit 3 unused
#define CUTOFF_SR_CHECK (1 << 4)
#define BUZZ (1 << 5)
//Bit 6 unused
//Bit 7 unused

//Port H bits:
#define CUTOFF_TEST (1 << 0)
#define CUTOFF_RESET (1 << 1)
#define EX1_PWR_CHECK (1 << 2)
#define EX1_HEAT (1 << 3)
#define EX1_FAN (1 << 4)
#define SD_WP (1 << 5)
#define SD_CD (1 << 6)
//Bit 7 unused

//Port J bits:
#define BUTTON_CENTER (1 << 0)
#define BUTTON_RIGHT (1 << 1)
#define BUTTON_LEFT (1 << 2)
#define BUTTON_DOWN (1 << 3)
#define BUTTON_UP (1 << 4)
#define POTS_SCL (1 << 5)
#define B_AXIS_POT (1 << 6)
//Bit 7 unused

//Port K bits:
#define Z_AXIS_DIR (1 << 0)
#define Z_AXIS_STEP (1 << 1)
#define Z_AXIS_EN (1 << 2)
#define Z_AXIS_POT (1 << 3)
#define EX7_1280 (1 << 4)
#define EX6_1280 (1 << 5)
#define EX5_1280 (1 << 6)
#define HBP_THERM (1 << 7)

//Port L bits:
#define X_MIN (1 << 0)
#define X_MAX (1 << 1)
#define Y_MIN (1 << 2)
#define Y_MAX (1 << 3)
#define HBP (1 << 4)
#define EXTRA_FET (1 << 5)
#define Z_MIN (1 << 6)
#define Z_MAX (1 << 7)

/****************************************************\
* I/O devices                                        *
\****************************************************/

class replicator_state : public driver_device
{
public:
	replicator_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_lcdc(*this, "hd44780"),
		m_dac(*this, "dac"),
		m_io_keypad(*this, "keypad")
	{
	}

	void replicator(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void palette_init(palette_device &palette) const;

	void prg_map(address_map &map) ATTR_COLD;
	void data_map(address_map &map) ATTR_COLD;

	uint8_t port_a_r();
	uint8_t port_b_r();
	uint8_t port_c_r();
	uint8_t port_d_r();
	uint8_t port_e_r();
	uint8_t port_f_r();
	uint8_t port_g_r();
	uint8_t port_h_r();
	uint8_t port_j_r();
	uint8_t port_k_r();
	uint8_t port_l_r();

	void port_a_w(uint8_t data);
	void port_b_w(uint8_t data);
	void port_c_w(uint8_t data);
	void port_d_w(uint8_t data);
	void port_e_w(uint8_t data);
	void port_f_w(uint8_t data);
	void port_g_w(uint8_t data);
	void port_h_w(uint8_t data);
	void port_j_w(uint8_t data);
	void port_k_w(uint8_t data);
	void port_l_w(uint8_t data);

	uint8_t m_port_a;
	uint8_t m_port_b;
	uint8_t m_port_c;
	uint8_t m_port_d;
	uint8_t m_port_e;
	uint8_t m_port_f;
	uint8_t m_port_g;
	uint8_t m_port_h;
	uint8_t m_port_j;
	uint8_t m_port_k;
	uint8_t m_port_l;

	uint8_t m_shift_register_value;

	required_device<atmega1280_device> m_maincpu;
	required_device<hd44780_device> m_lcdc;
	required_device<dac_bit_interface> m_dac;
	required_ioport m_io_keypad;
};

uint8_t replicator_state::port_a_r()
{
	LOGMASKED(LOG_PORT_A, "%s: Port A READ (A-axis signals + B-axis STEP&DIR)\n", machine().describe_context());
	return 0;
}

uint8_t replicator_state::port_b_r()
{
	LOGMASKED(LOG_PORT_B, "%s: Port B READ (SD-CS; 1280-MISO/MOSI/SCK; EX2-FAN/HEAT/PWR-CHECK; BLINK)\n", machine().describe_context());
	return 0;
}

uint8_t replicator_state::port_c_r()
{
	LOGMASKED(LOG_PORT_C, "%s: Port C READ (1280-EX1/EX2; LCD-signals; R&G-LED; DETECT)\n", machine().describe_context());
	return DETECT; //indicated that the Interface board is present.
}

uint8_t replicator_state::port_d_r()
{
	LOGMASKED(LOG_PORT_D, "%s: Port D READ (SDA/SCL; 1280-EX-TX/RX)\n", machine().describe_context());
	return 0;
}

uint8_t replicator_state::port_e_r()
{
	LOGMASKED(LOG_PORT_E, "%s: Port E READ (1280-TX/RX; THERMO-signals)\n", machine().describe_context());
	return 0;
}

uint8_t replicator_state::port_f_r()
{
	LOGMASKED(LOG_PORT_F, "%s: Port F READ (X-axis & Y-axis signals)\n", machine().describe_context());
	return 0;
}

uint8_t replicator_state::port_g_r()
{
	LOGMASKED(LOG_PORT_G, "%s: Port G READ (BUZZ; Cutoff-sr-check; B-axis EN; 1280-EX3/EX4)\n", machine().describe_context());
	return 0;
}

uint8_t replicator_state::port_h_r()
{
	LOGMASKED(LOG_PORT_H, "%s: Port H READ (cuttoff-text/reset; EX1-FAN/HEAT/PWR-CHECK; SD-CD/SD-WP)\n", machine().describe_context());
	return 0;
}

uint8_t replicator_state::port_j_r()
{
	LOGMASKED(LOG_PORT_J, "%s: Port J READ (Interface buttons; POTS-SCL; B-axis-POT)\n", machine().describe_context());
	return m_io_keypad->read();
}

uint8_t replicator_state::port_k_r()
{
	LOGMASKED(LOG_PORT_K, "%s: Port K READ (Z-axis signals; HBP-THERM; 1280-EX5/6/7)\n", machine().describe_context());
	return 0;
}

uint8_t replicator_state::port_l_r()
{
	LOGMASKED(LOG_PORT_L, "%s: Port L READ (HBP; EXTRA-FET; X-MIN/MAX; Y-MIN/MAX; Z-MIN/MAX)\n", machine().describe_context());
	return 0;
}

void replicator_state::port_a_w(uint8_t data)
{
	if (data == m_port_a) return;

	const uint8_t old = m_port_a;
	const uint8_t changed = data ^ old;

	if (changed & A_AXIS_DIR)
		LOGMASKED(LOG_PORT_A, "%s: [A] A_AXIS_DIR: %d\n", machine().describe_context(), data & A_AXIS_DIR ? 1 : 0);
	if (changed & A_AXIS_STEP)
		LOGMASKED(LOG_PORT_A, "%s: [A] A_AXIS_STEP: %d\n", machine().describe_context(), data & A_AXIS_STEP ? 1 : 0);
	if (changed & A_AXIS_EN)
		LOGMASKED(LOG_PORT_A, "%s: [A] A_AXIS_EN: %d\n", machine().describe_context(), data & A_AXIS_EN ? 1 : 0);
	if (changed & A_AXIS_POT)
		LOGMASKED(LOG_PORT_A, "%s: [A] A_AXIS_POT: %d\n", machine().describe_context(), data & A_AXIS_POT ? 1 : 0);
	if (changed & B_AXIS_DIR)
		LOGMASKED(LOG_PORT_A, "%s: [A] B_AXIS_DIR: %d\n", machine().describe_context(), data & B_AXIS_DIR ? 1 : 0);
	if (changed & B_AXIS_STEP)
		LOGMASKED(LOG_PORT_A, "%s: [A] B_AXIS_STEP: %d\n", machine().describe_context(), data & B_AXIS_STEP ? 1 : 0);

	m_port_a = data;
}

void replicator_state::port_b_w(uint8_t data)
{
	if (data == m_port_b) return;

	const uint8_t old = m_port_b;
	const uint8_t changed = data ^ old;

	if (changed & SD_CS)
		LOGMASKED(LOG_PORT_B, "%s: [B] SD Card Chip Select: %d\n", machine().describe_context(), data & SD_CS ? 1 : 0);
	if (changed & SCK_1280)
		LOGMASKED(LOG_PORT_B, "%s: [B] 1280-SCK: %d\n", machine().describe_context(), data & SCK_1280 ? 1 : 0);
	if (changed & MOSI_1280)
		LOGMASKED(LOG_PORT_B, "%s: [B] 1280-MOSI: %d\n", machine().describe_context(), data & MOSI_1280 ? 1 : 0);
	if (changed & MISO_1280)
		LOGMASKED(LOG_PORT_B, "%s: [B] 1280-MISO: %d\n", machine().describe_context(), data & MISO_1280 ? 1 : 0);
	if (changed & EX2_PWR_CHECK)
		LOGMASKED(LOG_PORT_B, "%s: [B] EX2-PWR-CHECK: %d\n", machine().describe_context(), data & EX2_PWR_CHECK ? 1 : 0);
	if (changed & EX2_HEAT)
		LOGMASKED(LOG_PORT_B, "%s: [B] EX2_HEAT: %d\n", machine().describe_context(), data & EX2_HEAT ? 1 : 0);
	if (changed & EX2_FAN)
		LOGMASKED(LOG_PORT_B, "%s: [B] EX2_FAN: %d\n", machine().describe_context(), data & EX2_FAN ? 1 : 0);
	if (changed & BLINK)
		LOGMASKED(LOG_PORT_B, "%s: [B] BLINK: %d\n", machine().describe_context(), data & BLINK ? 1 : 0);

	m_port_b = data;
}

void replicator_state::port_c_w(uint8_t data)
{
	if (data == m_port_c) return;

	const uint8_t old_port_c = m_port_c;
	const uint8_t changed = data ^ old_port_c;

	if(changed & EX2_1280)
		LOGMASKED(LOG_PORT_C, "%s: [C] EX2_1280: %d\n", machine().describe_context(), data & EX2_1280 ? 1 : 0);
	if(changed & EX1_1280)
		LOGMASKED(LOG_PORT_C, "%s: [C] EX1_1280: %d\n", machine().describe_context(), data & EX1_1280 ? 1 : 0);
	if(changed & LCD_CLK)
		LOGMASKED(LOG_PORT_C, "%s: [C] LCD_CLK: %d\n", machine().describe_context(), data & LCD_CLK ? 1 : 0);
	if(changed & LCD_DATA)
		LOGMASKED(LOG_PORT_C, "%s: [C] LCD_DATA: %d\n", machine().describe_context(), data & LCD_DATA ? 1 : 0);
	if(changed & LCD_STROBE)
		LOGMASKED(LOG_PORT_C, "%s: [C] LCD_STROBE: %d\n", machine().describe_context(), data & LCD_STROBE ? 1 : 0);
	if(changed & RLED)
		LOGMASKED(LOG_PORT_C, "%s: [C] RLED: %d\n", machine().describe_context(), data & RLED ? 1 : 0);
	if(changed & GLED)
		LOGMASKED(LOG_PORT_C, "%s: [C] GLED: %d\n", machine().describe_context(), data & GLED ? 1 : 0);
	if(changed & DETECT)
		LOGMASKED(LOG_PORT_C, "%s: [C] DETECT: %d\n", machine().describe_context(), data & DETECT ? 1 : 0);

	if (changed & LCD_CLK)
	{
		/* The LCD is interfaced by an 8-bit shift register (74HC4094). */
		if (data & LCD_CLK) // CLK positive edge
		{
			m_shift_register_value = (m_shift_register_value << 1) | ((data & LCD_DATA) >> 3);
			LOGMASKED(LOG_LCD_CLK, "%s: [C] LCD CLK positive edge. shift_register=0x%02X\n", machine().describe_context(), m_shift_register_value);
		}
	}

	if (changed & LCD_STROBE)
	{
		if (data & LCD_STROBE) // STROBE positive edge
		{
			LOGMASKED(LOG_LCD_SHIFT, "%s: LCD shift register = %02X\n", machine().describe_context(), m_shift_register_value);
			m_lcdc->rs_w(BIT(m_shift_register_value, 1));
			m_lcdc->rw_w(BIT(m_shift_register_value, 2));
			m_lcdc->e_w(BIT(m_shift_register_value, 3));
			m_lcdc->db_w(m_shift_register_value & 0xF0);
		}
	}

	m_port_c = data;
}

void replicator_state::port_d_w(uint8_t data)
{
	if (data == m_port_d) return;

	const uint8_t old = m_port_d;
	const uint8_t changed = data ^ old;

	if (changed & PORTD_SCL)
		LOGMASKED(LOG_PORT_D, "%s: [D] PORTD_SCL: %d\n", machine().describe_context(), data & PORTD_SCL ? 1 : 0);
	if (changed & PORTD_SDA)
		LOGMASKED(LOG_PORT_D, "%s: [D] PORTD_SDA: %d\n", machine().describe_context(), data & PORTD_SDA ? 1 : 0);
	if (changed & EX_RX_1280)
		LOGMASKED(LOG_PORT_D, "%s: [D] EX_RX_1280: %d\n", machine().describe_context(), data & EX_RX_1280 ? 1 : 0);
	if (changed & EX_TX_1280)
		LOGMASKED(LOG_PORT_D, "%s: [D] EX_TX_1280: %d\n", machine().describe_context(), data & EX_TX_1280 ? 1 : 0);

	m_port_d = data;
}

void replicator_state::port_e_w(uint8_t data)
{
	if (data == m_port_e) return;

	const uint8_t old = m_port_e;
	const uint8_t changed = data ^ old;

	if (changed & RX_1280)
		LOGMASKED(LOG_PORT_E, "%s: [E] 1280-RX: %d\n", machine().describe_context(), data & RX_1280 ? 1 : 0);
	if (changed & TX_1280)
		LOGMASKED(LOG_PORT_E, "%s: [E] 1280-TX: %d\n", machine().describe_context(), data & TX_1280 ? 1 : 0);
	if (changed & THERMO_SCK)
		LOGMASKED(LOG_PORT_E, "%s: [E] THERMO-SCK: %d\n", machine().describe_context(), data & THERMO_SCK ? 1 : 0);
	if (changed & THERMO_CS1)
		LOGMASKED(LOG_PORT_E, "%s: [E] THERMO-CS1: %d\n", machine().describe_context(), data & THERMO_CS1 ? 1 : 0);
	if (changed & THERMO_CS2)
		LOGMASKED(LOG_PORT_E, "%s: [E] THERMO-CS2: %d\n", machine().describe_context(), data & THERMO_CS2 ? 1 : 0);
	if (changed & THERMO_DO)
		LOGMASKED(LOG_PORT_E, "%s: [E] THERMO-DO: %d\n", machine().describe_context(), data & THERMO_DO ? 1 : 0);

	m_port_e = data;
}

void replicator_state::port_f_w(uint8_t data)
{
	if (data == m_port_f) return;

	const uint8_t old = m_port_f;
	const uint8_t changed = data ^ old;

	if (changed & X_AXIS_DIR)
		LOGMASKED(LOG_PORT_F, "%s: [F] X_AXIS_DIR: %d\n", machine().describe_context(), data & X_AXIS_DIR ? 1 : 0);
	if (changed & X_AXIS_STEP)
		LOGMASKED(LOG_PORT_F, "%s: [F] X_AXIS_STEP: %d\n", machine().describe_context(), data & X_AXIS_STEP ? 1 : 0);
	if (changed & X_AXIS_EN)
		LOGMASKED(LOG_PORT_F, "%s: [F] X_AXIS_EN: %d\n", machine().describe_context(), data & X_AXIS_EN ? 1 : 0);
	if (changed & X_AXIS_POT)
		LOGMASKED(LOG_PORT_F, "%s: [F] X_AXIS_POT: %d\n", machine().describe_context(), data & X_AXIS_POT ? 1 : 0);
	if (changed & Y_AXIS_DIR)
		LOGMASKED(LOG_PORT_F, "%s: [F] Y_AXIS_DIR: %d\n", machine().describe_context(), data & Y_AXIS_DIR ? 1 : 0);
	if (changed & Y_AXIS_STEP)
		LOGMASKED(LOG_PORT_F, "%s: [F] Y_AXIS_STEP: %d\n", machine().describe_context(), data & Y_AXIS_STEP ? 1 : 0);
	if (changed & Y_AXIS_EN)
		LOGMASKED(LOG_PORT_F, "%s: [F] Y_AXIS_EN: %d\n", machine().describe_context(), data & Y_AXIS_EN ? 1 : 0);
	if (changed & Y_AXIS_POT)
		LOGMASKED(LOG_PORT_F, "%s: [F] Y_AXIS_POT: %d\n", machine().describe_context(), data & Y_AXIS_POT ? 1 : 0);

	m_port_f = data;
}

void replicator_state::port_g_w(uint8_t data)
{
	if (data == m_port_g) return;

	const uint8_t old = m_port_g;
	const uint8_t changed = data ^ old;

	if (changed & EX4_1280)
		LOGMASKED(LOG_PORT_G, "%s: [G] EX4_1280: %d\n", machine().describe_context(), data & EX4_1280 ? 1 : 0);
	if (changed & EX3_1280)
		LOGMASKED(LOG_PORT_G, "%s: [G] EX3_1280: %d\n", machine().describe_context(), data & EX3_1280 ? 1 : 0);
	if (changed & B_AXIS_EN)
		LOGMASKED(LOG_PORT_G, "%s: [G] B_AXIS_EN: %d\n", machine().describe_context(), data & B_AXIS_EN ? 1 : 0);
	if (changed & CUTOFF_SR_CHECK)
		LOGMASKED(LOG_PORT_G, "%s: [G] CUTOFF_SR_CHECK: %d\n", machine().describe_context(), data & CUTOFF_SR_CHECK ? 1 : 0);
	if (changed & BUZZ)
		LOGMASKED(LOG_PORT_G, "%s: [G] BUZZ: %d\n", machine().describe_context(), data & BUZZ ? 1 : 0);

	if (changed & BUZZ)
	{
		m_dac->write(BIT(data, 5));
	}

	m_port_g = data;
}

void replicator_state::port_h_w(uint8_t data)
{
	if (data == m_port_h) return;

	const uint8_t old = m_port_h;
	const uint8_t changed = data ^ old;

	if (changed & CUTOFF_TEST)
		LOGMASKED(LOG_PORT_H, "%s: [H] CUTOFF_TEST: %d\n", machine().describe_context(), data & CUTOFF_TEST ? 1 : 0);
	if (changed & CUTOFF_RESET)
		LOGMASKED(LOG_PORT_H, "%s: [H] CUTOFF_RESET: %d\n", machine().describe_context(), data & CUTOFF_RESET ? 1 : 0);
	if (changed & EX1_PWR_CHECK)
		LOGMASKED(LOG_PORT_H, "%s: [H] EX1_PWR_CHECK: %d\n", machine().describe_context(), data & EX1_PWR_CHECK ? 1 : 0);
	if (changed & EX1_HEAT)
		LOGMASKED(LOG_PORT_H, "%s: [H] EX1_HEAT: %d\n", machine().describe_context(), data & EX1_HEAT ? 1 : 0);
	if (changed & EX1_FAN)
		LOGMASKED(LOG_PORT_H, "%s: [H] EX1_FAN: %d\n", machine().describe_context(), data & EX1_FAN ? 1 : 0);
	if (changed & SD_WP)
		LOGMASKED(LOG_PORT_H, "%s: [H] SD_WP: %d\n", machine().describe_context(), data & SD_WP ? 1 : 0);
	if (changed & SD_CD)
		LOGMASKED(LOG_PORT_H, "%s: [H] SD_CD: %d\n", machine().describe_context(), data & SD_CD ? 1 : 0);

	m_port_h = data;
}

void replicator_state::port_j_w(uint8_t data)
{
	if (data == m_port_j) return;

	const uint8_t old = m_port_j;
	const uint8_t changed = data ^ old;

	if (changed & BUTTON_CENTER)
		LOGMASKED(LOG_PORT_J, "%s: [J] BUTTON_CENTER: %d\n", machine().describe_context(), data & BUTTON_CENTER ? 1 : 0);
	if (changed & BUTTON_RIGHT)
		LOGMASKED(LOG_PORT_J, "%s: [J] BUTTON_RIGHT: %d\n", machine().describe_context(), data & BUTTON_RIGHT ? 1 : 0);
	if (changed & BUTTON_LEFT)
		LOGMASKED(LOG_PORT_J, "%s: [J] BUTTON_LEFT: %d\n", machine().describe_context(), data & BUTTON_LEFT ? 1 : 0);
	if (changed & BUTTON_DOWN)
		LOGMASKED(LOG_PORT_J, "%s: [J] BUTTON_DOWN: %d\n", machine().describe_context(), data & BUTTON_DOWN ? 1 : 0);
	if (changed & BUTTON_UP)
		LOGMASKED(LOG_PORT_J, "%s: [J] BUTTON_UP: %d\n", machine().describe_context(), data & BUTTON_UP ? 1 : 0);
	if (changed & POTS_SCL)
		LOGMASKED(LOG_PORT_J, "%s: [J] POTS_SCL: %d\n", machine().describe_context(), data & POTS_SCL ? 1 : 0);
	if (changed & B_AXIS_POT)
		LOGMASKED(LOG_PORT_J, "%s: [J] B_AXIS_POT: %d\n", machine().describe_context(), data & B_AXIS_POT ? 1 : 0);

	m_port_j = data;
}

void replicator_state::port_k_w(uint8_t data)
{
	if (data == m_port_k) return;

	const uint8_t old = m_port_k;
	const uint8_t changed = data ^ old;

	if (changed & Z_AXIS_DIR)
		LOGMASKED(LOG_PORT_K, "%s: [K] Z_AXIS_DIR: %d\n", machine().describe_context(), data & Z_AXIS_DIR ? 1 : 0);
	if (changed & Z_AXIS_STEP)
		LOGMASKED(LOG_PORT_K, "%s: [K] Z_AXIS_STEP: %d\n", machine().describe_context(), data & Z_AXIS_STEP ? 1 : 0);
	if (changed & Z_AXIS_EN)
		LOGMASKED(LOG_PORT_K, "%s: [K] Z_AXIS_EN: %d\n", machine().describe_context(), data & Z_AXIS_EN ? 1 : 0);
	if (changed & Z_AXIS_POT)
		LOGMASKED(LOG_PORT_K, "%s: [K] Z_AXIS_POT: %d\n", machine().describe_context(), data & Z_AXIS_POT ? 1 : 0);
	if (changed & EX7_1280)
		LOGMASKED(LOG_PORT_K, "%s: [K] EX7_1280: %d\n", machine().describe_context(), data & EX7_1280 ? 1 : 0);
	if (changed & EX6_1280)
		LOGMASKED(LOG_PORT_K, "%s: [K] EX6_1280: %d\n", machine().describe_context(), data & EX6_1280 ? 1 : 0);
	if (changed & EX5_1280)
		LOGMASKED(LOG_PORT_K, "%s: [K] EX5_1280: %d\n", machine().describe_context(), data & EX5_1280 ? 1 : 0);
	if (changed & HBP_THERM)
		LOGMASKED(LOG_PORT_K, "%s: [K] HBP_THERM: %d\n", machine().describe_context(), data & HBP_THERM ? 1 : 0);

	m_port_k = data;
}

void replicator_state::port_l_w(uint8_t data)
{
	if (data == m_port_l) return;

	const uint8_t old_port_l = m_port_l;
	const uint8_t changed = data ^ old_port_l;

	if (changed & X_MIN)
		LOGMASKED(LOG_PORT_L, "%s: [L] X_MIN: %d\n", machine().describe_context(), data & X_MIN ? 1 : 0);
	if (changed & X_MAX)
		LOGMASKED(LOG_PORT_L, "%s: [L] X_MAX: %d\n", machine().describe_context(), data & X_MAX ? 1 : 0);
	if (changed & Y_MIN)
		LOGMASKED(LOG_PORT_L, "%s: [L] Y_MIN: %d\n", machine().describe_context(), data & Y_MIN ? 1 : 0);
	if (changed & Y_MAX)
		LOGMASKED(LOG_PORT_L, "%s: [L] Y_MAX: %d\n", machine().describe_context(), data & Y_MAX ? 1 : 0);
	if (changed & HBP)
		LOGMASKED(LOG_PORT_L, "%s: [L] HBP: %d\n", machine().describe_context(), data & HBP ? 1 : 0);
	if (changed & EXTRA_FET)
		LOGMASKED(LOG_PORT_L, "%s: [L] EXTRA_FET: %d\n", data & EXTRA_FET ? 1 : 0);
	if (changed & Z_MIN)
		LOGMASKED(LOG_PORT_L, "%s: [L] Z_MIN: %d\n", machine().describe_context(), data & Z_MIN ? 1 : 0);
	if (changed & Z_MAX)
		LOGMASKED(LOG_PORT_L, "%s: [L] Z_MAX: %d\n", machine().describe_context(), data & Z_MAX ? 1 : 0);

	m_port_l = data;
}

/****************************************************\
* Address maps                                       *
\****************************************************/

void replicator_state::prg_map(address_map &map)
{
	map(0x00000, 0x1ffff).rom();
}

void replicator_state::data_map(address_map &map)
{
	map(0x0200, 0x21ff).ram();  /* ATMEGA1280 Internal SRAM */
}

/****************************************************\
* Input ports                                        *
\****************************************************/

static INPUT_PORTS_START( replicator )
	PORT_START("keypad")
	PORT_BIT(0x00000001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CENTER") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x00000002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RIGHT") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x00000004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LEFT") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x00000008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DOWN") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x00000010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("UP") PORT_CODE(KEYCODE_W)
INPUT_PORTS_END

/****************************************************\
* Machine definition                                 *
\****************************************************/

void replicator_state::machine_start()
{
	save_item(NAME(m_shift_register_value));
	save_item(NAME(m_port_a));
	save_item(NAME(m_port_b));
	save_item(NAME(m_port_c));
	save_item(NAME(m_port_d));
	save_item(NAME(m_port_e));
	save_item(NAME(m_port_f));
	save_item(NAME(m_port_g));
	save_item(NAME(m_port_h));
	save_item(NAME(m_port_j));
	save_item(NAME(m_port_k));
	save_item(NAME(m_port_l));
}

void replicator_state::machine_reset()
{
	m_shift_register_value = 0;
	m_port_a = 0;
	m_port_b = 0;
	m_port_c = 0;
	m_port_d = 0;
	m_port_e = 0;
	m_port_f = 0;
	m_port_g = 0;
	m_port_h = 0;
	m_port_j = 0;
	m_port_k = 0;
	m_port_l = 0;
}

void replicator_state::palette_init(palette_device &palette) const
{
	// These colors were picked with the color picker in Inkscape, based on a photo of the LCD used in the Replicator 1 3d printer:
	palette.set_pen_color(0, rgb_t(0xca, 0xe7, 0xeb));
	palette.set_pen_color(1, rgb_t(0x78, 0xab, 0xa8));
}

static const gfx_layout hd44780_charlayout =
{
	5, 8,                   /* 5 x 8 characters */
	256,                    /* 256 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	{ 3, 4, 5, 6, 7},
	{ 0, 8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8},
	8*8                     /* 8 bytes */
};

static GFXDECODE_START( gfx_replicator )
	GFXDECODE_ENTRY( "hd44780:cgrom", 0x0000, hd44780_charlayout, 0, 1 )
GFXDECODE_END

void replicator_state::replicator(machine_config &config)
{
	ATMEGA1280(config, m_maincpu, MASTER_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &replicator_state::prg_map);
	m_maincpu->set_addrmap(AS_DATA, &replicator_state::data_map);

	m_maincpu->set_eeprom_tag("eeprom");
	m_maincpu->set_low_fuses(0xff);
	m_maincpu->set_high_fuses(0xda);
	m_maincpu->set_extended_fuses(0xf4);
	m_maincpu->set_lock_bits(0x0f);

	m_maincpu->gpio_in<atmega1280_device::GPIOA>().set(FUNC(replicator_state::port_a_r));
	m_maincpu->gpio_in<atmega1280_device::GPIOB>().set(FUNC(replicator_state::port_b_r));
	m_maincpu->gpio_in<atmega1280_device::GPIOC>().set(FUNC(replicator_state::port_c_r));
	m_maincpu->gpio_in<atmega1280_device::GPIOD>().set(FUNC(replicator_state::port_d_r));
	m_maincpu->gpio_in<atmega1280_device::GPIOE>().set(FUNC(replicator_state::port_e_r));
	m_maincpu->gpio_in<atmega1280_device::GPIOF>().set(FUNC(replicator_state::port_f_r));
	m_maincpu->gpio_in<atmega1280_device::GPIOG>().set(FUNC(replicator_state::port_g_r));
	m_maincpu->gpio_in<atmega1280_device::GPIOH>().set(FUNC(replicator_state::port_h_r));
	m_maincpu->gpio_in<atmega1280_device::GPIOJ>().set(FUNC(replicator_state::port_j_r));
	m_maincpu->gpio_in<atmega1280_device::GPIOK>().set(FUNC(replicator_state::port_k_r));
	m_maincpu->gpio_in<atmega1280_device::GPIOL>().set(FUNC(replicator_state::port_l_r));

	m_maincpu->gpio_out<atmega1280_device::GPIOA>().set(FUNC(replicator_state::port_a_w));
	m_maincpu->gpio_out<atmega1280_device::GPIOB>().set(FUNC(replicator_state::port_b_w));
	m_maincpu->gpio_out<atmega1280_device::GPIOC>().set(FUNC(replicator_state::port_c_w));
	m_maincpu->gpio_out<atmega1280_device::GPIOD>().set(FUNC(replicator_state::port_d_w));
	m_maincpu->gpio_out<atmega1280_device::GPIOE>().set(FUNC(replicator_state::port_e_w));
	m_maincpu->gpio_out<atmega1280_device::GPIOF>().set(FUNC(replicator_state::port_f_w));
	m_maincpu->gpio_out<atmega1280_device::GPIOG>().set(FUNC(replicator_state::port_g_w));
	m_maincpu->gpio_out<atmega1280_device::GPIOH>().set(FUNC(replicator_state::port_h_w));
	m_maincpu->gpio_out<atmega1280_device::GPIOJ>().set(FUNC(replicator_state::port_j_w));
	m_maincpu->gpio_out<atmega1280_device::GPIOK>().set(FUNC(replicator_state::port_k_w));
	m_maincpu->gpio_out<atmega1280_device::GPIOL>().set(FUNC(replicator_state::port_l_w));

	/*TODO: Add an ATMEGA8U2 for USB-Serial communications */

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("hd44780", FUNC(hd44780_device::screen_update));
	screen.set_size(120, 18*2); //4x20 chars
	screen.set_visarea(0, 120-1, 0, 18*2-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(replicator_state::palette_init), 2);
	GFXDECODE(config, "gfxdecode", "palette", gfx_replicator);

	HD44780(config, "hd44780", 270'000).set_lcd_size(4, 20); // TODO: clock not measured, datasheet typical clock used

	/* sound hardware */
	/* A piezo is connected to the PORT G bit 5 (OC0B pin driven by Timer/Counter #4) */
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac, 0).add_route(0, "speaker", 0.5);
}

ROM_START( replica1 )
	ROM_REGION( 0x20000, "maincpu", 0 )
	ROM_DEFAULT_BIOS("v750")

	/* Version 5.1 release:
	- Initial firmware release
	*/
	ROM_SYSTEM_BIOS( 0, "v51", "V 5.1" )
	ROMX_LOAD("mighty-mb40-v5.1.bin", 0x0000, 0x10b90, CRC(20d65cd1) SHA1(da18c3eb5a29a6bc1eecd92eaae6063fe29d0305), ROM_BIOS(0))

	/* Version 5.2 release:
	- Nozzle Tolerance added to EEPROM
	- Updated onboard menus
	- X,Y calibration tool added
	*/
	ROM_SYSTEM_BIOS( 1, "v52", "V 5.2" )
	ROMX_LOAD("mighty-mb40-v5.2.bin", 0x0000, 0x126c4, CRC(555e47cf) SHA1(9d24a3dbeddce16669bb4d29c3366220ddf15d2a), ROM_BIOS(1))

	/* Version 5.5 release:
	- Acceleration added to motor motion
	- Digipot updates
	*/
	ROM_SYSTEM_BIOS( 2, "v55", "V 5.5" )
	ROMX_LOAD("mighty-mb40-v5.5.bin", 0x0000, 0x1a420, CRC(9327d7e4) SHA1(d734ba2bda12f50ec3ac0035ab11591909d9edde), ROM_BIOS(2))

	/* Version 6.2.0 release:
	- Bug fix release to firmware 6.0
	- Addresses wavy print issue above 1cm
	- Left extruder prints with makerware.
	*/
	ROM_SYSTEM_BIOS( 3, "v620", "V 6.2.0" )
	ROMX_LOAD("mighty_one_v6.2.0.bin", 0x0000, 0x1cf54, CRC(00df6f48) SHA1(db05afc2e1ebc104fb04753634a911187e396556), ROM_BIOS(3))

	/* Version 7.0.0 release:
	- Major upgrade to Stepper Motor Smoothness (via Sailfish team)
	- X3G format introduced
	- Heaters default to leaving 'preheat' on more of the time
	*/
	ROM_SYSTEM_BIOS( 4, "v700", "V 7.0.0" )
	ROMX_LOAD("mighty_one_v7.0.0.bin", 0x0000, 0x1cb52, CRC(aa2a5fcf) SHA1(934e642b0b2d007689249680bad03c9255ae016a), ROM_BIOS(4))

	/* Version 7.2.0 release:
	- Removes support for S3G files
	- X3G is the recognized format
	- Minor bug fixes
	*/
	ROM_SYSTEM_BIOS( 5, "v720", "V 7.2.0" )
	ROMX_LOAD("mighty_one_v7.2.0.bin", 0x0000, 0x1cb80, CRC(5e546706) SHA1(ed4aaf7522d5a5beea7eb69bf2c85d7a89f8f188), ROM_BIOS(5))

	/* Version 7.3.0 release:
	- Pause at Z Height
	- Elapsed time displays during prints
	- Minor bug fixes
	*/
	ROM_SYSTEM_BIOS( 6, "v730", "V 7.3.0" )
	ROMX_LOAD("mighty_one_v7.3.0.bin", 0x0000, 0x1d738, CRC(71811ff5) SHA1(6728ea600ab3ff4b589adca90b0d700d9b70bd18), ROM_BIOS(6))

	/* Version 7.4.0 (bugfix) release:
	- Fixes issues with Z Pause and elapsed print time
	*/
	ROM_SYSTEM_BIOS( 7, "v740", "V 7.4.0" )
	ROMX_LOAD("mighty_one_v7.4.0.bin", 0x0000, 0x1b9e2, CRC(97b05a27) SHA1(76ca2c9c1db2e006e501c3177a8a1aa693dda0f9), ROM_BIOS(7))

	/* Version 7.5.0 (bugfix) release:
	- Fixes issue with Heat Hold
	*/
	ROM_SYSTEM_BIOS( 8, "v750", "V 7.5.0" )
	ROMX_LOAD("mighty_one_v7.5.0.bin", 0x0000, 0x1b9c4, CRC(169d6709) SHA1(62b5aacd1bc46969042aea7a50531ec467a4ff1f), ROM_BIOS(8))

	/* Sailfish firmware image - Metam??quina experimental build v7.5.0 */
	ROM_SYSTEM_BIOS( 9, "v750mm", "V 7.5.0 - Metam??quina" )
	ROMX_LOAD("mighty_one_v7.5.0.mm.bin", 0x0000, 0x1ef9a, CRC(0d36d9e7) SHA1(a53899775b4c4eea87b6903758ebb75f06710a69), ROM_BIOS(9))


	/*Arduino MEGA bootloader */
	ROM_LOAD( "atmegaboot_168_atmega1280.bin", 0x1f000, 0x0f16, CRC(c041f8db) SHA1(d995ebf360a264cccacec65f6dc0c2257a3a9224) )

	/* on-die 4kbyte eeprom */
	ROM_REGION( 0x1000, "eeprom", ROMREGION_ERASEFF )
ROM_END

} // anonymous namespace


/*   YEAR  NAME      PARENT  COMPAT  MACHINE     INPUT       CLASS             INIT        COMPANY     FULLNAME */
COMP(2012, replica1, 0,      0,      replicator, replicator, replicator_state, empty_init, "Makerbot", "Replicator 1 desktop 3d printer", MACHINE_NOT_WORKING)



reutapm.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/******************************************************************************

    Reuters APM Board (Application Processor Module)

    https://chrisacorns.computinghistory.org.uk/Computers/Reuters.html

******************************************************************************/

#include "emu.h"
#include "bbc.h"
#include "acorn_serproc.h"

#include "machine/keyboard.h"


namespace {

class reuters_state : public bbc_state
{
public:
	reuters_state(const machine_config &mconfig, device_type type, const char *tag)
		: bbc_state(mconfig, type, tag)
		, m_view_shadow(*this, "view_shadow")
		, m_adlc(*this, "mc6854")
		, m_statid(*this, "STATID")
	{ }

	void reutapm(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	memory_view m_view_shadow;
	optional_device<mc6854_device> m_adlc;
	required_ioport m_statid;

	void reutapm_fetch(address_map &map) ATTR_COLD;
	void reutapm_mem(address_map &map) ATTR_COLD;

	uint8_t fetch_r(offs_t offset);
	void romsel_w(offs_t offset, uint8_t data);
	uint8_t paged_r(offs_t offset);
	void paged_w(offs_t offset, uint8_t data);

	void kbd_put(uint8_t data);

	uint8_t m_keydata;
};


void reuters_state::machine_start()
{
	bbc_state::machine_start();
}


void reuters_state::reutapm_fetch(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(reuters_state::fetch_r));
}


void reuters_state::reutapm_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).rw(m_ram, FUNC(ram_device::read), FUNC(ram_device::write));                                   //    0000-7FFF                 Regular RAM
	map(0x3000, 0x7fff).view(m_view_shadow);                                                                          //    3000-7FFF                 20K Shadow RAM
	m_view_shadow[0](0x3000, 0x7fff).lr8(NAME([this] (offs_t offset) { return m_ram->pointer()[offset + 0xb000]; }));
	m_view_shadow[0](0x3000, 0x7fff).lw8(NAME([this] (offs_t offset, uint8_t data) { m_ram->pointer()[offset + 0xb000] = data; }));
	map(0x8000, 0xbfff).rw(FUNC(reuters_state::paged_r), FUNC(reuters_state::paged_w));                               //    8000-BFFF                 Paged ROM/RAM
	map(0xc000, 0xffff).rw(FUNC(reuters_state::mos_r), FUNC(reuters_state::mos_w));                                   //    C000-FBFF                 OS ROM
	map(0xfc00, 0xfcff).rw("1mhzbus", FUNC(bbc_1mhzbus_slot_device::fred_r), FUNC(bbc_1mhzbus_slot_device::fred_w));  //    FC00-FCFF                 FRED Address Page
	map(0xfd00, 0xfdff).rw("1mhzbus", FUNC(bbc_1mhzbus_slot_device::jim_r), FUNC(bbc_1mhzbus_slot_device::jim_w));    //    FD00-FDFF                 JIM Address Page
	map(0xfe00, 0xfeff).lr8(NAME([]() { return 0xfe; })).nopw();                                                      //    FE00-FEFF                 SHEILA Address Page
	map(0xfe00, 0xfe00).mirror(0x06).rw(m_crtc, FUNC(hd6845s_device::status_r), FUNC(hd6845s_device::address_w));     //    FE00-FE07  6845 CRTC      Video controller
	map(0xfe01, 0xfe01).mirror(0x06).rw(m_crtc, FUNC(hd6845s_device::register_r), FUNC(hd6845s_device::register_w));
	map(0xfe08, 0xfe09).rw("acia1", FUNC(acia6850_device::read), FUNC(acia6850_device::write));                       //    FE08-FE09  6850 ACIA      Serial controller
	map(0xfe0a, 0xfe0b).rw("acia2", FUNC(acia6850_device::read), FUNC(acia6850_device::write));                       //    FE0A-FE0B  6850 ACIA      Serial controller
	map(0xfe10, 0xfe11).rw("serproc1", FUNC(acorn_serproc_device::read), FUNC(acorn_serproc_device::write));          //    FE10-FE11  Serial ULA     Serial system chip
	map(0xfe12, 0xfe13).rw("serproc2", FUNC(acorn_serproc_device::read), FUNC(acorn_serproc_device::write));          //    FE12-FE13  Serial ULA     Serial system chip
	map(0xfe18, 0xfe1f).lr8(NAME([this]() { econet_int_enable(0); return m_statid->read(); }));                       // R: FE18-FE1F  INTOFF/STATID  ECONET Interrupt Off / ID No.
	map(0xfe18, 0xfe1f).lw8(NAME([this](uint8_t data) { econet_int_enable(0); }));                                    // W: FE18-FE1F  INTOFF         ECONET Interrupt Off
	map(0xfe20, 0xfe2f).lr8(NAME([this]() { econet_int_enable(1); return 0xfe; }));                                   // R: FE20-FE2F  INTON          ECONET Interrupt On
	map(0xfe20, 0xfe2f).w(FUNC(reuters_state::video_ula_w));                                                          // W: FE20-FE2F  Video ULA      Video system chip
	map(0xfe30, 0xfe3f).w(FUNC(reuters_state::romsel_w));                                                             // W: FE30-FE3F  84LS161        Paged ROM selector
	map(0xfe30, 0xfe30).portr("SW2");
	map(0xfe32, 0xfe32).portr("SW1");
	map(0xfe40, 0xfe4f).mirror(0x10).m(m_sysvia, FUNC(via6522_device::map));                                          //    FE40-FE5F  6522 VIA       SYSTEM VIA
	map(0xfe60, 0xfe6f).mirror(0x10).m(m_uservia, FUNC(via6522_device::map));                                         //    FE60-FE7F  6522 VIA       USER VIA
	map(0xfea0, 0xfea3).mirror(0x1c).rw(m_adlc, FUNC(mc6854_device::read), FUNC(mc6854_device::write));               //    FEA0-FEBF  68B54 ADLC     ECONET controller
	map(0xfee0, 0xfeff).rw("tube", FUNC(bbc_tube_slot_device::host_r), FUNC(bbc_tube_slot_device::host_w));           //    FEE0-FEFF  Tube ULA       Tube system interface
}


uint8_t reuters_state::fetch_r(offs_t offset)
{
	switch (offset & 0xf000)
	{
	case 0xa000:
		// Code executing from sideways RAM between 0xa000-0xafff will access the shadow RAM (if selected)
		if (m_vdusel && m_paged_ram)
			m_view_shadow.select(0);
		else
			m_view_shadow.disable();
		break;

	case 0xc000:
	case 0xd000:
		// Access shadow RAM if VDU drivers and shadow RAM selected
		if (m_vdusel)
			m_view_shadow.select(0);
		else
			m_view_shadow.disable();
		break;

	default:
		m_view_shadow.disable();
		break;
	}
	return m_maincpu->space(AS_PROGRAM).read_byte(offset);
}


void reuters_state::romsel_w(offs_t offset, uint8_t data)
{
	// the B+ addresses all 16 ROM sockets and extra 12K of RAM at 0x8000 and 20K of shadow RAM at 0x3000
	switch (offset & 0x07)
	{
	case 0x00:
		m_paged_ram = BIT(data, 7);

		m_romsel = data & 0x0f;
		break;

	case 0x04:
		// the video display should now use this flag to display the shadow RAM memory
		m_vdusel = BIT(data, 7);
		setvideoshadow(m_vdusel);
		break;
	}
}


uint8_t reuters_state::paged_r(offs_t offset)
{
	uint8_t data;

	if (m_paged_ram && ((m_romsel & 0x0e) == 0x02) && offset < 0x3000)
		data = m_ram->pointer()[offset + 0x8000];
	else
		data = m_region_rom->base()[offset + (m_romsel << 14)];

	return data;
}

void reuters_state::paged_w(offs_t offset, uint8_t data)
{
	if (m_paged_ram && ((m_romsel & 0x0e) == 0x02) && offset < 0x3000)
		m_ram->pointer()[offset + 0x8000] = data;
}


static INPUT_PORTS_START(reutapm)
	PORT_START("SW2")
	//PORT_DIPNAME(0x01, 0x00, "Unknown") PORT_DIPLOCATION("SW2:1")
	//PORT_DIPNAME(0x02, 0x00, "Unknown") PORT_DIPLOCATION("SW2:2")
	//PORT_DIPNAME(0x04, 0x00, "Unknown") PORT_DIPLOCATION("SW2:3")
	PORT_DIPNAME(0x08, 0x00, "Video Out") PORT_DIPLOCATION("SW2:4")
	PORT_DIPSETTING(   0x00, "Colour")
	PORT_DIPSETTING(   0x08, "Mono")
	PORT_DIPNAME(0x30, 0x00, "S2 Input") PORT_DIPLOCATION("SW2:5,6")
	PORT_DIPSETTING(   0x00, "9600/3600 Hz")
	PORT_DIPSETTING(   0x10, "4800/1800 Hz")
	PORT_DIPSETTING(   0x20, "1200/450 Hz")
	PORT_DIPSETTING(   0x30, "300/112 Hz")
	PORT_DIPNAME(0x40, 0x00, "S1 Input") PORT_DIPLOCATION("SW2:7")
	PORT_DIPSETTING(   0x00, "1200 Hz")
	PORT_DIPSETTING(   0x40, "300 Hz")
	PORT_DIPNAME(0x80, 0x00, "Printer") PORT_DIPLOCATION("SW2:8")
	PORT_DIPSETTING(   0x00, "Parallel")
	PORT_DIPSETTING(   0x80, "S1 Serial")

	PORT_START("SW1")
	PORT_DIPNAME(0x03, 0x01, "Selection Char") PORT_DIPLOCATION("SW1:1,2")
	PORT_DIPSETTING(   0x00, "00 hex")
	PORT_DIPSETTING(   0x01, "E5 hex")
	PORT_DIPSETTING(   0x02, "E3 hex")
	PORT_DIPSETTING(   0x03, "8E hex")
	PORT_DIPNAME(0x04, 0x04, "Video Switching") PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(   0x00, "Enabled")
	PORT_DIPSETTING(   0x04, "Disabled")
	PORT_DIPNAME(0x08, 0x00, "Switch Chars") PORT_DIPLOCATION("SW1:4")
	PORT_DIPSETTING(   0x00, "Output")
	PORT_DIPSETTING(   0x10, "Suppressed")
	PORT_DIPNAME(0x10, 0x10, "KBD In Strobe") PORT_DIPLOCATION("SW1:5")
	PORT_DIPSETTING(   0x00, "+ve")
	PORT_DIPSETTING(   0x10, "-ve")
	PORT_DIPNAME(0x20, 0x00, "KBD Out Strobe") PORT_DIPLOCATION("SW1:6")
	PORT_DIPSETTING(   0x00, "+ve")
	PORT_DIPSETTING(   0x20, "-ve")
	PORT_DIPNAME(0x40, 0x00, "Video In") PORT_DIPLOCATION("SW1:7")
	PORT_DIPSETTING(   0x00, "Colour")
	PORT_DIPSETTING(   0x40, "Mono")
	PORT_DIPNAME(0x80, 0x80, "Video Frequency") PORT_DIPLOCATION("SW1:8")
	PORT_DIPSETTING(   0x00, "60 Hz")
	PORT_DIPSETTING(   0x80, "50 Hz")

	PORT_START("STATID")
	PORT_CONFNAME(0xff, 0xfe, "Econet Station ID")
	PORT_CONFSETTING( 0x00,   "0" )    PORT_CONFSETTING( 0x01,   "1" )    PORT_CONFSETTING( 0x02,   "2" )    PORT_CONFSETTING( 0x03,   "3" )    PORT_CONFSETTING( 0x04,   "4" )
	PORT_CONFSETTING( 0x05,   "5" )    PORT_CONFSETTING( 0x06,   "6" )    PORT_CONFSETTING( 0x07,   "7" )    PORT_CONFSETTING( 0x08,   "8" )    PORT_CONFSETTING( 0x09,   "9" )
	PORT_CONFSETTING( 0x0a,  "10" )    PORT_CONFSETTING( 0x0b,  "11" )    PORT_CONFSETTING( 0x0c,  "12" )    PORT_CONFSETTING( 0x0d,  "13" )    PORT_CONFSETTING( 0x0e,  "14" )
	PORT_CONFSETTING( 0x0f,  "15" )    PORT_CONFSETTING( 0x10,  "16" )    PORT_CONFSETTING( 0x11,  "17" )    PORT_CONFSETTING( 0x12,  "18" )    PORT_CONFSETTING( 0x13,  "19" )
	PORT_CONFSETTING( 0x14,  "20" )    PORT_CONFSETTING( 0x15,  "21" )    PORT_CONFSETTING( 0x16,  "22" )    PORT_CONFSETTING( 0x17,  "23" )    PORT_CONFSETTING( 0x18,  "24" )
	PORT_CONFSETTING( 0x19,  "25" )    PORT_CONFSETTING( 0x1a,  "26" )    PORT_CONFSETTING( 0x1b,  "27" )    PORT_CONFSETTING( 0x1c,  "28" )    PORT_CONFSETTING( 0x1d,  "29" )
	PORT_CONFSETTING( 0x1e,  "30" )    PORT_CONFSETTING( 0x1f,  "31" )    PORT_CONFSETTING( 0x20,  "32" )    PORT_CONFSETTING( 0x21,  "33" )    PORT_CONFSETTING( 0x22,  "34" )
	PORT_CONFSETTING( 0x23,  "35" )    PORT_CONFSETTING( 0x24,  "36" )    PORT_CONFSETTING( 0x25,  "37" )    PORT_CONFSETTING( 0x26,  "38" )    PORT_CONFSETTING( 0x27,  "39" )
	PORT_CONFSETTING( 0x28,  "40" )    PORT_CONFSETTING( 0x29,  "41" )    PORT_CONFSETTING( 0x2a,  "42" )    PORT_CONFSETTING( 0x2b,  "43" )    PORT_CONFSETTING( 0x2c,  "44" )
	PORT_CONFSETTING( 0x2d,  "45" )    PORT_CONFSETTING( 0x2e,  "46" )    PORT_CONFSETTING( 0x2f,  "47" )    PORT_CONFSETTING( 0x30,  "48" )    PORT_CONFSETTING( 0x31,  "49" )
	PORT_CONFSETTING( 0x32,  "50" )    PORT_CONFSETTING( 0x33,  "51" )    PORT_CONFSETTING( 0x34,  "52" )    PORT_CONFSETTING( 0x35,  "53" )    PORT_CONFSETTING( 0x36,  "54" )
	PORT_CONFSETTING( 0x37,  "55" )    PORT_CONFSETTING( 0x38,  "56" )    PORT_CONFSETTING( 0x39,  "57" )    PORT_CONFSETTING( 0x3a,  "58" )    PORT_CONFSETTING( 0x3b,  "59" )
	PORT_CONFSETTING( 0x3c,  "60" )    PORT_CONFSETTING( 0x3d,  "61" )    PORT_CONFSETTING( 0x3e,  "62" )    PORT_CONFSETTING( 0x3f,  "63" )    PORT_CONFSETTING( 0x40,  "64" )
	PORT_CONFSETTING( 0x41,  "65" )    PORT_CONFSETTING( 0x42,  "66" )    PORT_CONFSETTING( 0x43,  "67" )    PORT_CONFSETTING( 0x44,  "68" )    PORT_CONFSETTING( 0x45,  "69" )
	PORT_CONFSETTING( 0x46,  "70" )    PORT_CONFSETTING( 0x47,  "71" )    PORT_CONFSETTING( 0x48,  "72" )    PORT_CONFSETTING( 0x49,  "73" )    PORT_CONFSETTING( 0x4a,  "74" )
	PORT_CONFSETTING( 0x4b,  "75" )    PORT_CONFSETTING( 0x4c,  "76" )    PORT_CONFSETTING( 0x4d,  "77" )    PORT_CONFSETTING( 0x4e,  "78" )    PORT_CONFSETTING( 0x4f,  "79" )
	PORT_CONFSETTING( 0x50,  "80" )    PORT_CONFSETTING( 0x51,  "81" )    PORT_CONFSETTING( 0x52,  "82" )    PORT_CONFSETTING( 0x53,  "83" )    PORT_CONFSETTING( 0x54,  "84" )
	PORT_CONFSETTING( 0x55,  "85" )    PORT_CONFSETTING( 0x56,  "86" )    PORT_CONFSETTING( 0x57,  "87" )    PORT_CONFSETTING( 0x58,  "88" )    PORT_CONFSETTING( 0x59,  "89" )
	PORT_CONFSETTING( 0x5a,  "90" )    PORT_CONFSETTING( 0x5b,  "91" )    PORT_CONFSETTING( 0x5c,  "92" )    PORT_CONFSETTING( 0x5d,  "93" )    PORT_CONFSETTING( 0x5e,  "94" )
	PORT_CONFSETTING( 0x5f,  "95" )    PORT_CONFSETTING( 0x60,  "96" )    PORT_CONFSETTING( 0x61,  "97" )    PORT_CONFSETTING( 0x62,  "98" )    PORT_CONFSETTING( 0x63,  "99" )
	PORT_CONFSETTING( 0x64, "100" )    PORT_CONFSETTING( 0x65, "101" )    PORT_CONFSETTING( 0x66, "102" )    PORT_CONFSETTING( 0x67, "103" )    PORT_CONFSETTING( 0x68, "104" )
	PORT_CONFSETTING( 0x69, "105" )    PORT_CONFSETTING( 0x6a, "106" )    PORT_CONFSETTING( 0x6b, "107" )    PORT_CONFSETTING( 0x6c, "108" )    PORT_CONFSETTING( 0x6d, "109" )
	PORT_CONFSETTING( 0x6e, "110" )    PORT_CONFSETTING( 0x6f, "111" )    PORT_CONFSETTING( 0x70, "112" )    PORT_CONFSETTING( 0x71, "113" )    PORT_CONFSETTING( 0x72, "114" )
	PORT_CONFSETTING( 0x73, "115" )    PORT_CONFSETTING( 0x74, "116" )    PORT_CONFSETTING( 0x75, "117" )    PORT_CONFSETTING( 0x76, "118" )    PORT_CONFSETTING( 0x77, "119" )
	PORT_CONFSETTING( 0x78, "120" )    PORT_CONFSETTING( 0x79, "121" )    PORT_CONFSETTING( 0x7a, "122" )    PORT_CONFSETTING( 0x7b, "123" )    PORT_CONFSETTING( 0x7c, "124" )
	PORT_CONFSETTING( 0x7d, "125" )    PORT_CONFSETTING( 0x7e, "126" )    PORT_CONFSETTING( 0x7f, "127" )    PORT_CONFSETTING( 0x80, "128" )    PORT_CONFSETTING( 0x81, "129" )
	PORT_CONFSETTING( 0x82, "130" )    PORT_CONFSETTING( 0x83, "131" )    PORT_CONFSETTING( 0x84, "132" )    PORT_CONFSETTING( 0x85, "133" )    PORT_CONFSETTING( 0x86, "134" )
	PORT_CONFSETTING( 0x87, "135" )    PORT_CONFSETTING( 0x88, "136" )    PORT_CONFSETTING( 0x89, "137" )    PORT_CONFSETTING( 0x8a, "138" )    PORT_CONFSETTING( 0x8b, "139" )
	PORT_CONFSETTING( 0x8c, "140" )    PORT_CONFSETTING( 0x8d, "141" )    PORT_CONFSETTING( 0x8e, "142" )    PORT_CONFSETTING( 0x8f, "143" )    PORT_CONFSETTING( 0x90, "144" )
	PORT_CONFSETTING( 0x91, "145" )    PORT_CONFSETTING( 0x92, "146" )    PORT_CONFSETTING( 0x93, "147" )    PORT_CONFSETTING( 0x94, "148" )    PORT_CONFSETTING( 0x95, "149" )
	PORT_CONFSETTING( 0x96, "150" )    PORT_CONFSETTING( 0x97, "151" )    PORT_CONFSETTING( 0x98, "152" )    PORT_CONFSETTING( 0x99, "153" )    PORT_CONFSETTING( 0x9a, "154" )
	PORT_CONFSETTING( 0x9b, "155" )    PORT_CONFSETTING( 0x9c, "156" )    PORT_CONFSETTING( 0x9d, "157" )    PORT_CONFSETTING( 0x9e, "158" )    PORT_CONFSETTING( 0x9f, "159" )
	PORT_CONFSETTING( 0xa0, "160" )    PORT_CONFSETTING( 0xa1, "161" )    PORT_CONFSETTING( 0xa2, "162" )    PORT_CONFSETTING( 0xa3, "163" )    PORT_CONFSETTING( 0xa4, "164" )
	PORT_CONFSETTING( 0xa5, "165" )    PORT_CONFSETTING( 0xa6, "166" )    PORT_CONFSETTING( 0xa7, "167" )    PORT_CONFSETTING( 0xa8, "168" )    PORT_CONFSETTING( 0xa9, "169" )
	PORT_CONFSETTING( 0xaa, "170" )    PORT_CONFSETTING( 0xab, "171" )    PORT_CONFSETTING( 0xac, "172" )    PORT_CONFSETTING( 0xad, "173" )    PORT_CONFSETTING( 0xae, "174" )
	PORT_CONFSETTING( 0xaf, "175" )    PORT_CONFSETTING( 0xb0, "176" )    PORT_CONFSETTING( 0xb1, "177" )    PORT_CONFSETTING( 0xb2, "178" )    PORT_CONFSETTING( 0xb3, "179" )
	PORT_CONFSETTING( 0xb4, "180" )    PORT_CONFSETTING( 0xb5, "181" )    PORT_CONFSETTING( 0xb6, "182" )    PORT_CONFSETTING( 0xb7, "183" )    PORT_CONFSETTING( 0xb8, "184" )
	PORT_CONFSETTING( 0xb9, "185" )    PORT_CONFSETTING( 0xba, "186" )    PORT_CONFSETTING( 0xbb, "187" )    PORT_CONFSETTING( 0xbc, "188" )    PORT_CONFSETTING( 0xbd, "189" )
	PORT_CONFSETTING( 0xbe, "190" )    PORT_CONFSETTING( 0xbf, "191" )    PORT_CONFSETTING( 0xc0, "192" )    PORT_CONFSETTING( 0xc1, "193" )    PORT_CONFSETTING( 0xc2, "194" )
	PORT_CONFSETTING( 0xc3, "195" )    PORT_CONFSETTING( 0xc4, "196" )    PORT_CONFSETTING( 0xc5, "197" )    PORT_CONFSETTING( 0xc6, "198" )    PORT_CONFSETTING( 0xc7, "199" )
	PORT_CONFSETTING( 0xc8, "200" )    PORT_CONFSETTING( 0xc9, "201" )    PORT_CONFSETTING( 0xca, "202" )    PORT_CONFSETTING( 0xcb, "203" )    PORT_CONFSETTING( 0xcc, "204" )
	PORT_CONFSETTING( 0xcd, "205" )    PORT_CONFSETTING( 0xce, "206" )    PORT_CONFSETTING( 0xcf, "207" )    PORT_CONFSETTING( 0xd0, "208" )    PORT_CONFSETTING( 0xd1, "209" )
	PORT_CONFSETTING( 0xd2, "210" )    PORT_CONFSETTING( 0xd3, "211" )    PORT_CONFSETTING( 0xd4, "212" )    PORT_CONFSETTING( 0xd5, "213" )    PORT_CONFSETTING( 0xd6, "214" )
	PORT_CONFSETTING( 0xd7, "215" )    PORT_CONFSETTING( 0xd8, "216" )    PORT_CONFSETTING( 0xd9, "217" )    PORT_CONFSETTING( 0xda, "218" )    PORT_CONFSETTING( 0xdb, "219" )
	PORT_CONFSETTING( 0xdc, "220" )    PORT_CONFSETTING( 0xdd, "221" )    PORT_CONFSETTING( 0xde, "222" )    PORT_CONFSETTING( 0xdf, "223" )    PORT_CONFSETTING( 0xe0, "224" )
	PORT_CONFSETTING( 0xe1, "225" )    PORT_CONFSETTING( 0xe2, "226" )    PORT_CONFSETTING( 0xe3, "227" )    PORT_CONFSETTING( 0xe4, "228" )    PORT_CONFSETTING( 0xe5, "229" )
	PORT_CONFSETTING( 0xe6, "230" )    PORT_CONFSETTING( 0xe7, "231" )    PORT_CONFSETTING( 0xe8, "232" )    PORT_CONFSETTING( 0xe9, "233" )    PORT_CONFSETTING( 0xea, "234" )
	PORT_CONFSETTING( 0xeb, "235" )    PORT_CONFSETTING( 0xec, "236" )    PORT_CONFSETTING( 0xed, "237" )    PORT_CONFSETTING( 0xee, "238" )    PORT_CONFSETTING( 0xef, "239" )
	PORT_CONFSETTING( 0xf0, "240" )    PORT_CONFSETTING( 0xf1, "241" )    PORT_CONFSETTING( 0xf2, "242" )    PORT_CONFSETTING( 0xf3, "243" )    PORT_CONFSETTING( 0xf4, "244" )
	PORT_CONFSETTING( 0xf5, "245" )    PORT_CONFSETTING( 0xf6, "246" )    PORT_CONFSETTING( 0xf7, "247" )    PORT_CONFSETTING( 0xf8, "248" )    PORT_CONFSETTING( 0xf9, "249" )
	PORT_CONFSETTING( 0xfa, "250" )    PORT_CONFSETTING( 0xfb, "251" )    PORT_CONFSETTING( 0xfc, "252" )    PORT_CONFSETTING( 0xfd, "253" )    PORT_CONFSETTING( 0xfe, "254" )
	PORT_CONFSETTING( 0xff, "255" )
INPUT_PORTS_END


void reuters_state::kbd_put(uint8_t data)
{
	m_keydata = data;

	m_uservia->write_cb1(0);
	m_uservia->write_cb1(1);
}


void reuters_state::reutapm(machine_config &config)
{
	M6512(config, m_maincpu, 16_MHz_XTAL / 8);
	m_maincpu->set_addrmap(AS_PROGRAM, &reuters_state::reutapm_mem);
	m_maincpu->set_addrmap(AS_OPCODES, &reuters_state::reutapm_fetch);

	INPUT_MERGER_ANY_HIGH(config, m_irqs).output_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	RAM(config, m_ram).set_default_size("64K");

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(16_MHz_XTAL, 1024, 0, 640, 312, 0, 256);
	m_screen->set_screen_update("crtc", FUNC(hd6845s_device::screen_update));

	PALETTE(config, m_palette).set_entries(16);

	SAA5050(config, m_trom, 12_MHz_XTAL / 2);

	HD6845S(config, m_crtc, 16_MHz_XTAL / 8);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_reconfigure_callback(FUNC(reuters_state::crtc_reconfigure));
	m_crtc->set_update_row_callback(FUNC(reuters_state::crtc_update_row));
	m_crtc->out_de_callback().set(FUNC(reuters_state::de_changed));
	m_crtc->out_hsync_callback().set(FUNC(reuters_state::hsync_changed));
	m_crtc->out_vsync_callback().set(FUNC(reuters_state::vsync_changed));

	LS259(config, m_latch);
	m_latch->q_out_cb<6>().set_output("capslock_led");
	m_latch->q_out_cb<7>().set_output("shiftlock_led");

	MOS6522(config, m_sysvia, 16_MHz_XTAL / 16);
	//m_sysvia->writepa_handler().set(FUNC(reuters_state::sysvia_pa_w)); // keyboard out
	m_sysvia->writepb_handler().set(m_latch, FUNC(ls259_device::write_nibble_d3));
	//m_sysvia->writepb_handler().set(FUNC(reuters_state::sysvia_pb_w)); // alarm AL1/AL2 on PB5/PB4
	//m_sysvia->ca2_handler().set(FUNC(reuters_state::kbd_strobe)); // keyboard strobe out
	m_sysvia->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<0>));

	MOS6522(config, m_uservia, 16_MHz_XTAL / 16);
	m_uservia->writepa_handler().set("cent_data_out", FUNC(output_latch_device::write));
	m_uservia->readpb_handler().set([this]() { return m_keydata; });
	m_uservia->ca2_handler().set("printer", FUNC(centronics_device::write_strobe));
	m_uservia->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<1>));

	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(reuters_state::kbd_put));

	centronics_device &centronics(CENTRONICS(config, "printer", centronics_devices, "printer"));
	centronics.ack_handler().set(m_uservia, FUNC(via6522_device::write_ca1));
	output_latch_device &latch(OUTPUT_LATCH(config, "cent_data_out"));
	centronics.set_output_latch(latch);

	acia6850_device &acia1(ACIA6850(config, "acia1", 0));
	acia1.txd_handler().set("rs423", FUNC(rs232_port_device::write_txd));
	acia1.rts_handler().set("rs423", FUNC(rs232_port_device::write_rts));
	acia1.rts_handler().append("serproc1", FUNC(acorn_serproc_device::write_rtsi));
	acia1.irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<2>));

	acorn_serproc_device &serproc1(ACORN_SERPROC(config, "serproc1", 16_MHz_XTAL / 13));
	serproc1.rxc_handler().set("acia1", FUNC(acia6850_device::write_rxc));
	serproc1.txc_handler().set("acia1", FUNC(acia6850_device::write_txc));
	serproc1.rxd_handler().set("acia1", FUNC(acia6850_device::write_rxd));
	serproc1.dcd_handler().set("acia1", FUNC(acia6850_device::write_dcd));
	serproc1.ctso_handler().set("acia1", FUNC(acia6850_device::write_cts));

	rs232_port_device &rs423(RS232_PORT(config, "rs423", default_rs232_devices, nullptr));
	rs423.rxd_handler().set("serproc1", FUNC(acorn_serproc_device::write_din));
	rs423.cts_handler().set("serproc1", FUNC(acorn_serproc_device::write_ctsi));

	acia6850_device &acia2(ACIA6850(config, "acia2"));
	acia2.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	acia2.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	acia2.rts_handler().append("serproc2", FUNC(acorn_serproc_device::write_rtsi));
	acia2.irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<3>));

	acorn_serproc_device &serproc2(ACORN_SERPROC(config, "serproc2", 16_MHz_XTAL / 13));
	serproc2.rxc_handler().set("acia2", FUNC(acia6850_device::write_rxc));
	serproc2.txc_handler().set("acia2", FUNC(acia6850_device::write_txc));
	serproc2.rxd_handler().set("acia2", FUNC(acia6850_device::write_rxd));
	serproc2.ctso_handler().set("acia2", FUNC(acia6850_device::write_cts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set("serproc2", FUNC(acorn_serproc_device::write_din));
	rs232.cts_handler().set("serproc2", FUNC(acorn_serproc_device::write_ctsi));
	rs232.dcd_handler().set("acia2", FUNC(acia6850_device::write_dcd));
	rs232.dsr_handler().set("sysvia", FUNC(via6522_device::write_cb2));
	rs232.dsr_handler().append("sysvia", FUNC(via6522_device::write_pb6));

	MC6854(config, m_adlc);
	m_adlc->out_txd_cb().set("network", FUNC(econet_device::host_data_w));
	m_adlc->out_irq_cb().set(FUNC(reuters_state::adlc_irq_w));
	//m_adlc->out_rts_cb().

	econet_device &econet(ECONET(config, "network", 0));
	econet.clk_wr_callback().set(m_adlc, FUNC(mc6854_device::txc_w));
	econet.clk_wr_callback().append(m_adlc, FUNC(mc6854_device::rxc_w));
	econet.data_wr_callback().set(m_adlc, FUNC(mc6854_device::set_rx));
	ECONET_SLOT(config, "econet", "network", econet_devices);

	bbc_1mhzbus_slot_device &bus(BBC_1MHZBUS_SLOT(config, m_1mhzbus, 16_MHz_XTAL / 16, bbc_1mhzbus_devices, nullptr)); // TODO: Reuters Solid State Store
	bus.irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<4>));
	bus.nmi_handler().set(FUNC(reuters_state::bus_nmi_w));

	bbc_tube_slot_device &tube(BBC_TUBE_SLOT(config, m_tube, bbc_tube_devices, nullptr));
	tube.irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<5>));
}


ROM_START(reutapm)
	ROM_REGION(0x44000, "rom", ROMREGION_ERASEFF)
	// page 0  00000  IC71 DNFS                      // page 8  20000  IC33
	// page 1  04000  IC71                           // page 9  24000  IC33
	// page 2  08000                                 // page 10 28000  IC44
	// page 3  0c000                                 // page 11 2c000  IC44
	// page 4  10000  IC22                           // page 12 30000  IC50
	// page 5  14000  IC22                           // page 13 34000  IC50
	// page 6  18000  IC29                           // page 14 38000  IC57
	// page 7  1c000  IC29                           // page 15 3C000  IC57
	ROM_SYSTEM_BIOS(0, "hwtest", "HWTEST")
	ROMX_LOAD("ros_dnfs_0251720-02_v2.0.rom",      0x00000, 0x4000, CRC(1c4145d5) SHA1(9a8c1977b08399455a55f4b83f4b8f637cf2bc04), ROM_BIOS(0))
	ROM_CONTINUE(0x40000, 0x4000)
	ROMX_LOAD("environment_0251.721-01_v0.11.rom", 0x38000, 0x8000, CRC(5615f7e4) SHA1(d7ab8b67e01a05bae098941f877bfc604292395d), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "topic", "TOPIC")
	ROMX_LOAD("ros_dnfs_251720_v3.65.rom", 0x00000, 0x4000, CRC(1c4145d5) SHA1(9a8c1977b08399455a55f4b83f4b8f637cf2bc04), ROM_BIOS(1))
	ROM_CONTINUE(0x40000, 0x4000)
	ROMX_LOAD("topic_v1.1.rom",            0x30000, 0x4000, CRC(a3951386) SHA1(91eee39009e9148d1970b4707bb291d344f2a2b1), ROM_BIOS(1))
	ROMX_LOAD("environment_251721_v7.rom", 0x38000, 0x4000, CRC(0bfa2aa0) SHA1(9b5e9fea700715d03bd00c2362f57294161a7441), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "apm", "APM")
	ROMX_LOAD("r0.3_nfs3.65.rom",   0x00000, 0x4000, CRC(1c4145d5) SHA1(9a8c1977b08399455a55f4b83f4b8f637cf2bc04), ROM_BIOS(2))
	ROM_CONTINUE(0x40000, 0x4000)
	ROMX_LOAD("apm-f814-24oct.rom", 0x30000, 0x4000, CRC(5a59b894) SHA1(ae50aa7f13d63a6b582baf3a967257fb8c315684), ROM_BIOS(2))
	ROMX_LOAD("sysrom-v6-4bc8.rom", 0x38000, 0x4000, CRC(249ff54c) SHA1(e5d178d19b2c22852f576a90fae4109e22d324ac), ROM_BIOS(2))

	ROM_SYSTEM_BIOS(3, "lib", "Library")
	ROMX_LOAD("reuterb.rom",    0x30000, 0x4000, BAD_DUMP CRC(9e02f59b) SHA1(1e63aa3bf4b37bf9ba41e454f95db05c3d15bfbf), ROM_BIOS(3))
	ROMX_LOAD("reutera100.rom", 0x38000, 0x4000, CRC(98ebabfb) SHA1(a7887e1e5c206203491e1e06682b9508b0fef49d), ROM_BIOS(3))
	ROMX_LOAD("mos_r030.rom",   0x40000, 0x4000, CRC(8b652337) SHA1(6a5c7ace255c8ac96c983d5ba67084fbd71ff61e), ROM_BIOS(3))

	ROM_REGION(0x4000, "mos", 0)
	ROM_COPY("rom", 0x40000, 0, 0x4000)
ROM_END

} // anonymous namespace


//    YEAR  NAME        PARENT  COMPAT MACHINE     INPUT      CLASS           INIT       COMPANY               FULLNAME          FLAGS
COMP( 1985, reutapm,    0,      0,     reutapm,    reutapm,   reuters_state,  init_bbc,  "Acorn Computers",    "Reuters APM",    MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )



rex6000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

        Xircom / Intel REX 6000

        Driver by Sandro Ronco

        Known REX 6000 banks:
        0x0000 - 0x00ff     first flash chip
        0x0600 - 0x06ff     second flash chip
        0x1000 - 0x1003     32KB RAM

        Known DataSlim 2 banks:
        0x0000 - 0x00ff     first flash chip
        0x0b00 - 0x0bff     second flash chip
        0x1000 - 0x1003     32KB RAM

        TODO:
        - deleting all Memos causes an error
        - alarm doesn't work
        - alarm sound and keyclick
        - NVRAM and warm start (without the pen calibration)
        - serial I/O

****************************************************************************/


#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/ins8250.h"
#include "machine/rp5c01.h"
#include "machine/bankdev.h"
#include "machine/ram.h"
#include "machine/intelfsh.h"
#include "machine/timer.h"
#include "imagedev/snapquik.h"
#include "sound/beep.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

#define MAKE_BANK(lo, hi)       ((lo) | ((hi)<<8))

//irq flag/mask bits
#define IRQ_FLAG_KEYCHANGE  0x01
#define IRQ_FLAG_ALARM      0x02
#define IRQ_FLAG_SERIAL     0x04
#define IRQ_FLAG_1HZ        0x10
#define IRQ_FLAG_IRQ2       0x20
#define IRQ_FLAG_IRQ1       0x40
#define IRQ_FLAG_EVENT      0x80

#define TC8521_TAG  "rtc"

class rex6000_state : public driver_device
{
public:
	rex6000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_beep(*this, "beeper")
		, m_uart(*this, "ns16550")
		, m_bankdev0(*this, "bank0")
		, m_bankdev1(*this, "bank1")
		, m_flash0a(*this, "flash0a")
		, m_flash0b(*this, "flash0b")
		, m_flash1a(*this, "flash1a")
		, m_flash1b(*this, "flash1b")
		, m_nvram(*this, "nvram")
		, m_battery(*this, "BATTERY")
		, m_pen_x(*this, "PENX")
		, m_pen_y(*this, "PENY")
	{ }

	void rex6000(machine_config &config);

	void rex6000_palettte(palette_device &palette) const;
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_rex6000);
	DECLARE_INPUT_CHANGED_MEMBER(trigger_irq);
	void serial_irq(int state);
	void alarm_irq(int state);

	TIMER_DEVICE_CALLBACK_MEMBER(irq_timer1);
	TIMER_DEVICE_CALLBACK_MEMBER(irq_timer2);
	TIMER_DEVICE_CALLBACK_MEMBER(sec_timer);

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);

	uint8_t bankswitch_r(offs_t offset);
	void bankswitch_w(offs_t offset, uint8_t data);
	uint8_t lcd_base_r(offs_t offset);
	void lcd_base_w(offs_t offset, uint8_t data);
	uint8_t beep_r(offs_t offset);
	void beep_w(offs_t offset, uint8_t data);
	uint8_t lcd_io_r(offs_t offset);
	void lcd_io_w(offs_t offset, uint8_t data);
	uint8_t irq_r(offs_t offset);
	void irq_w(offs_t offset, uint8_t data);
	uint8_t touchscreen_r(offs_t offset);
	void touchscreen_w(offs_t offset, uint8_t data);

	void rex6000_banked_map(address_map &map) ATTR_COLD;
	void rex6000_io(address_map &map) ATTR_COLD;
	void rex6000_mem(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<beep_device> m_beep;
	required_device<ns16550_device> m_uart;
	required_device<address_map_bank_device> m_bankdev0;
	required_device<address_map_bank_device> m_bankdev1;
	optional_device<intelfsh8_device> m_flash0a;
	optional_device<intelfsh8_device> m_flash0b;
	optional_device<intelfsh8_device> m_flash1a;
	optional_device<intelfsh8_device> m_flash1b;
	required_shared_ptr<uint8_t> m_nvram;
	required_ioport m_battery;
	optional_ioport m_pen_x;
	optional_ioport m_pen_y;

	uint8_t m_bank[4];
	uint8_t m_beep_io[5];
	uint8_t m_lcd_base[2];
	uint8_t m_touchscreen[0x10];
	uint8_t m_lcd_enabled;
	uint8_t m_lcd_cmd;

	uint8_t m_irq_mask;
	uint8_t m_irq_flag;
	uint8_t m_port6;
	uint8_t m_beep_mode;
	uint8_t m_power_on;

};


class oz750_state : public rex6000_state
{
public:
	oz750_state(const machine_config &mconfig, device_type type, const char *tag)
		: rex6000_state(mconfig, type, tag)
		, m_keyboard(*this, "COL.%u", 0)
	{
	}

	optional_ioport_array<10> m_keyboard;

	uint8_t kb_status_r();
	uint8_t kb_data_r();
	void kb_mask_w(offs_t offset, uint8_t data);
	DECLARE_INPUT_CHANGED_MEMBER(trigger_on_irq);
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_oz750);

	virtual void machine_reset() override ATTR_COLD;
	uint32_t screen_update_oz(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void oz750(machine_config &config);
	void oz750_banked_map(address_map &map) ATTR_COLD;
	void oz750_io(address_map &map) ATTR_COLD;
private:
	int oz_wzd_extract_tag(const std::vector<uint8_t> &data, const char *tag, char *dest_buf);

	uint16_t m_kb_mask = 0;
};


uint8_t rex6000_state::bankswitch_r(offs_t offset)
{
	return m_bank[offset];
}

void rex6000_state::bankswitch_w(offs_t offset, uint8_t data)
{
	m_bank[offset&3] = data;

	switch (offset)
	{
		case 0:     //bank 1 low
		case 1:     //bank 1 high
		{
			//bank 1 start at 0x8000
			m_bankdev0->set_bank(MAKE_BANK(m_bank[0], m_bank[1]) + 4);
			break;
		}
		case 2:     //bank 2 low
		case 3:     //bank 2 high
		{
			m_bankdev1->set_bank(MAKE_BANK(m_bank[2], m_bank[3]));
			break;
		}
	}
}

uint8_t rex6000_state::beep_r(offs_t offset)
{
	return m_beep_io[offset];
}

void rex6000_state::beep_w(offs_t offset, uint8_t data)
{
	m_beep_io[offset] = data;

	switch (offset)
	{
		case 0: // alarm mode control
			/*
			    ---- ---x   beep off
			    ---- --x-   continuous beep
			    ---- -x--   continuous alarm 1
			    ---- x---   continuous alarm 2
			    ---x ----   continuous alarm 3
			    --x- ----   single very short beep (keyclick)
			    -x-- ----   single alarm
			    x--- ----   single short beep
			*/
			// TODO: the beeper frequency and length in alarm mode need to be measured
			break;
		case 1: // tone mode control
			if (m_beep_mode)
			{
				m_beep->set_state(BIT(data, 0));

				// the beeper frequency is update only if the bit 1 is set
				if (BIT(data, 1))
				{
					uint16_t div = ((m_beep_io[2] | m_beep_io[3]<<8) & 0x0fff) + 2;
					m_beep->set_clock(16384 / div);
				}
			}
			break;
		case 4: // select alarm/tone mode
			if (m_beep_mode != BIT(data, 0))
				m_beep->set_state(0); // turned off when mode changes

			m_beep_mode = BIT(data, 0);
			break;
	}
}

uint8_t rex6000_state::lcd_base_r(offs_t offset)
{
	return m_lcd_base[offset];
}

void rex6000_state::lcd_base_w(offs_t offset, uint8_t data)
{
	m_lcd_base[offset&1] = data;
}

uint8_t rex6000_state::lcd_io_r(offs_t offset)
{
	return (offset == 0) ? m_lcd_enabled : m_lcd_cmd;
}

void rex6000_state::lcd_io_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
		case 0:
			m_lcd_enabled = data;
			break;
		case 1:
			m_lcd_cmd = data;
			break;
	}
}

uint8_t rex6000_state::irq_r(offs_t offset)
{
	switch (offset)
	{
		case 0: //irq flag
			/*
			    ---- ---x   input port
			    ---- --x-   alarm
			    ---- -x--   serial
			    ---- x---   ??
			    ---x ----   1Hz timer
			    --x- ----   32Hz timer
			    -x-- ----   4096Hz timer
			    x--- ----   special events
			*/
			return m_irq_flag;

		case 1:
			return m_port6;

		case 2: //irq mask
			return m_irq_mask;

		default:
			return 0;
	}
}

void rex6000_state::irq_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
		case 0: //irq flag
			m_irq_flag = data;
			break;
		case 1: //clear irq flag
			m_port6 = data;
			m_irq_flag &= (~data);
			break;
		case 2: //irq mask
			m_irq_mask = data;
			break;
		case 3: //power control
			m_power_on = data & 1;
			break;
	}
}

uint8_t rex6000_state::touchscreen_r(offs_t offset)
{
	uint16_t x = m_pen_x->read();
	uint16_t y = m_pen_y->read();
	uint16_t battery = m_battery->read();

	switch (offset)
	{
		case 0x08:
			return ((ioport("INPUT")->read() & 0x40) ? 0x20 : 0x00) | 0x10;
		case 0x09:
			if (m_touchscreen[4] & 0x80)
				return (battery>>0) & 0xff;
			else
				return (y>>0) & 0xff;
		case 0x0a:
			if (m_touchscreen[4] & 0x80)
				return (battery>>8) & 0xff;
			else
				return (y>>8) & 0xff;
		case 0x0b:
			return (x>>0) & 0xff;
		case 0x0c:
			return (x>>8) & 0xff;
	}

	return m_touchscreen[offset&0x0f];
}

void rex6000_state::touchscreen_w(offs_t offset, uint8_t data)
{
	m_touchscreen[offset&0x0f] = data;
}

uint8_t oz750_state::kb_status_r()
{
	uint8_t data = 0x6b;
	if (m_battery->read() & 0x01)   data |= 0x80;

	return data;
}

uint8_t oz750_state::kb_data_r()
{
	uint8_t data = 0;
	for(int i=0; i<10; i++)
	{
		if (m_kb_mask & (1<<i))
			data |= m_keyboard[i]->read();
	}

	return data;
}
void oz750_state::kb_mask_w(offs_t offset, uint8_t data)
{
	if (offset)
		m_kb_mask = (m_kb_mask & 0x00ff) | (data << 8);
	else
		m_kb_mask = (m_kb_mask & 0xff00) | data;
}

void rex6000_state::rex6000_banked_map(address_map &map)
{
	map(0x0000000, 0x00fffff).rw(m_flash0a, FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0x0100000, 0x01fffff).rw(m_flash0b, FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0x0c00000, 0x0cfffff).rw(m_flash1a, FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0x0d00000, 0x0dfffff).rw(m_flash1b, FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0x1600000, 0x16fffff).rw(m_flash1a, FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0x1700000, 0x17fffff).rw(m_flash1b, FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0x2000000, 0x2007fff).ram().share("nvram");
}

void oz750_state::oz750_banked_map(address_map &map)
{
	map(0x0000000, 0x01fffff).rw(m_flash0a, FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0x0200000, 0x02fffff).mirror(0x100000).rw(m_flash1a, FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0x0600000, 0x07fffff).rw(FUNC(oz750_state::lcd_io_r), FUNC(oz750_state::lcd_io_w));
	map(0x0800000, 0x083ffff).mirror(0x1c0000).ram().share("nvram");
	map(0x0a00000, 0x0a3ffff).mirror(0x1c0000).ram();
}


void rex6000_state::rex6000_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).rw(m_flash0a, FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0x8000, 0x9fff).rw(m_bankdev0, FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0xa000, 0xbfff).rw(m_bankdev1, FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0xc000, 0xffff).bankrw("ram");            //system RAM
}

void rex6000_state::rex6000_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x01, 0x04).rw(FUNC(rex6000_state::bankswitch_r), FUNC(rex6000_state::bankswitch_w));
	map(0x05, 0x07).rw(FUNC(rex6000_state::irq_r), FUNC(rex6000_state::irq_w));
	map(0x10, 0x10).portr("INPUT");
	map(0x15, 0x19).rw(FUNC(rex6000_state::beep_r), FUNC(rex6000_state::beep_w));
	map(0x22, 0x23).rw(FUNC(rex6000_state::lcd_base_r), FUNC(rex6000_state::lcd_base_w));
	map(0x30, 0x3f).rw(TC8521_TAG, FUNC(tc8521_device::read), FUNC(tc8521_device::write));
	map(0x40, 0x47).mirror(0x08).rw(m_uart, FUNC(ns16550_device::ins8250_r), FUNC(ns16550_device::ins8250_w));
	map(0x50, 0x51).rw(FUNC(rex6000_state::lcd_io_r), FUNC(rex6000_state::lcd_io_w));
	map(0x60, 0x6f).rw(FUNC(rex6000_state::touchscreen_r), FUNC(rex6000_state::touchscreen_w));
}

void oz750_state::oz750_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x01, 0x04).rw(FUNC(oz750_state::bankswitch_r), FUNC(oz750_state::bankswitch_w));
	map(0x05, 0x08).rw(FUNC(oz750_state::irq_r), FUNC(oz750_state::irq_w));
	map(0x10, 0x10).r(FUNC(oz750_state::kb_data_r));
	map(0x11, 0x12).rw(FUNC(oz750_state::kb_status_r), FUNC(oz750_state::kb_mask_w));
	map(0x15, 0x19).rw(FUNC(oz750_state::beep_r), FUNC(oz750_state::beep_w));
	map(0x22, 0x23).rw(FUNC(oz750_state::lcd_base_r), FUNC(oz750_state::lcd_base_w));
	map(0x30, 0x3f).rw(TC8521_TAG, FUNC(tc8521_device::read), FUNC(tc8521_device::write));
	map(0x40, 0x47).mirror(0x08).rw(m_uart, FUNC(ns16550_device::ins8250_r), FUNC(ns16550_device::ins8250_w));
}

INPUT_CHANGED_MEMBER(rex6000_state::trigger_irq)
{
	if (!(m_irq_mask & IRQ_FLAG_KEYCHANGE))
	{
		m_irq_flag |= IRQ_FLAG_KEYCHANGE;

		m_maincpu->set_input_line(0, HOLD_LINE);
	}
}

INPUT_CHANGED_MEMBER(oz750_state::trigger_on_irq)
{
	m_uart->cts_w(!newval);

	if (!(m_irq_mask & IRQ_FLAG_EVENT))
	{
		m_irq_flag |= IRQ_FLAG_EVENT;

		m_maincpu->set_input_line(0, HOLD_LINE);
	}
}

/* Input ports */
INPUT_PORTS_START( rex6000 )
	PORT_START("BATTERY")
	PORT_CONFNAME( 0x03ff, 0x03ff, "Battery Status" )
	PORT_CONFSETTING( 0x03ff, "Good" )
	PORT_CONFSETTING( 0x0000, "Poor" )

	PORT_START("INPUT")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD)  PORT_NAME("Home")   PORT_CODE(KEYCODE_HOME)         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(rex6000_state::trigger_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD)  PORT_NAME("Back")   PORT_CODE(KEYCODE_BACKSPACE)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(rex6000_state::trigger_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)  PORT_NAME("Select")     PORT_CODE(KEYCODE_SPACE)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(rex6000_state::trigger_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)  PORT_NAME("Up")     PORT_CODE(KEYCODE_PGUP)         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(rex6000_state::trigger_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD)  PORT_NAME("Down")   PORT_CODE(KEYCODE_PGDN)         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(rex6000_state::trigger_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Pen")    PORT_CODE(KEYCODE_ENTER) PORT_CODE(MOUSECODE_BUTTON1)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(rex6000_state::trigger_irq), 0)

	PORT_START("PENX")
	PORT_BIT(0x3ff, 0x00, IPT_LIGHTGUN_X) PORT_NAME("Pen X") PORT_CROSSHAIR(X, 1, 0, 0) PORT_SENSITIVITY(50) PORT_KEYDELTA(10) PORT_INVERT

	PORT_START("PENY")
	PORT_BIT(0x3ff, 0x00, IPT_LIGHTGUN_Y) PORT_NAME("Pen Y") PORT_CROSSHAIR(Y, 1, 0, 0) PORT_SENSITIVITY(50) PORT_KEYDELTA(10) PORT_INVERT
INPUT_PORTS_END

INPUT_PORTS_START( oz750 )
	PORT_START("BATTERY")
	PORT_CONFNAME( 0x01, 0x01, "Battery Status" )
	PORT_CONFSETTING( 0x01, "Normal operating mode" )
	PORT_CONFSETTING( 0x00, "Replace battery mode" )

	PORT_START("ON")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)  PORT_CODE(KEYCODE_0_PAD)  PORT_NAME("ON")                         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_on_irq), 0)

	PORT_START("COL.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)         PORT_CHAR(UCHAR_MAMEKEY(ESC))       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)           PORT_CHAR('Q')                      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)           PORT_CHAR('G')                      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)           PORT_CHAR('A')                      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)      PORT_CHAR(UCHAR_MAMEKEY(LSHIFT))    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)    PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)      PORT_CHAR('1')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)      PORT_CHAR('V')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)      PORT_CHAR('Y')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)      PORT_CHAR('H')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)      PORT_CHAR('S')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)      PORT_CHAR('Z')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)     PORT_NAME("MENU")                        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)      PORT_CHAR('2')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)      PORT_CHAR('E')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)      PORT_CHAR('U')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)      PORT_CHAR('J')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)      PORT_CHAR('D')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)      PORT_CHAR('X')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)     PORT_NAME("NEW")                         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)      PORT_CHAR('3')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)      PORT_CHAR('R')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)      PORT_CHAR('I')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)      PORT_CHAR('K')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)      PORT_CHAR('F')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)      PORT_CHAR('C')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)     PORT_CHAR(UCHAR_MAMEKEY(F3))             PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)      PORT_CHAR('4')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)      PORT_CHAR('T')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)      PORT_CHAR('O')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)      PORT_CHAR('L')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)   PORT_CHAR(UCHAR_MAMEKEY(LEFT))           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)      PORT_CHAR('V')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)  PORT_CHAR(' ')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)      PORT_CHAR('5')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)      PORT_CHAR('8')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)      PORT_CHAR('P')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD)   PORT_NAME("RETURN")                 PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)   PORT_CHAR(UCHAR_MAMEKEY(DOWN))           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)      PORT_CHAR('B')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)  PORT_CHAR('-')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)      PORT_CHAR('5')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)      PORT_CHAR('8')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)    PORT_CHAR(UCHAR_MAMEKEY(DEL))            PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_MAMEKEY(RSHIFT))         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)  PORT_CHAR(UCHAR_MAMEKEY(RIGHT))          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)      PORT_CHAR('N')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)  PORT_CHAR(13)                            PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)      PORT_CHAR('7')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)      PORT_CHAR('0')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)   PORT_CHAR('.')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)  PORT_CHAR(',')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)     PORT_CHAR(UCHAR_MAMEKEY(UP))             PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)      PORT_CHAR('M')                           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL.8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)     PORT_NAME("MENU Screen")                 PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)     PORT_NAME("ESC Screen")                  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)     PORT_NAME("ENTER Screen")                PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP)   PORT_CHAR(UCHAR_MAMEKEY(PGUP))           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN)   PORT_CHAR(UCHAR_MAMEKEY(PGDN))           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL.9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)     PORT_NAME("MAIN")                        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)     PORT_NAME("TEL")                         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9)     PORT_NAME("SCHEDULE")                    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10)    PORT_NAME("MEMO")                        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11)    PORT_NAME("My Programs")                 PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F12)    PORT_NAME("Backlight")                   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(oz750_state::trigger_irq), 0)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

void rex6000_state::machine_start()
{
	membank("ram")->set_base((uint8_t*)m_nvram + 0x4000);
}

void rex6000_state::machine_reset()
{
	memset(m_bank, 0, sizeof(m_bank));
	memset(m_beep_io, 0, sizeof(m_beep_io));
	memset(m_lcd_base, 0, sizeof(m_lcd_base));
	memset(m_touchscreen, 0, sizeof(m_touchscreen));
	m_lcd_enabled = 0;
	m_lcd_cmd = 0;
	m_irq_mask = 0;
	m_irq_flag = 0;
	m_port6 = 0;
	m_beep_mode = 0;
	m_power_on = 0;
}

void oz750_state::machine_reset()
{
	rex6000_state::machine_reset();
	m_kb_mask = 0;
}

uint32_t rex6000_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint16_t const lcd_bank = MAKE_BANK(m_lcd_base[0], m_lcd_base[1]);

	if (m_lcd_enabled)
	{
		for (int y=0; y<120; y++)
			for (int x=0; x<30; x++)
			{
				uint8_t data = m_bankdev0->space(AS_PROGRAM).read_byte((lcd_bank << 13) + y*30 + x);

				for (int b=0; b<8; b++)
				{
					bitmap.pix(y, (x * 8) + b) = BIT(data, 7);
					data <<= 1;
				}
			}
	}
	else
	{
		bitmap.fill(0, cliprect);
	}

	return 0;
}

uint32_t oz750_state::screen_update_oz(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint16_t const lcd_bank = MAKE_BANK(m_lcd_base[0], m_lcd_base[1]);

	if (m_lcd_enabled && m_power_on)
	{
		for (int y=0; y<=cliprect.max_y; y++)
			for (int x=0; x<30; x++)
			{
				uint8_t data = m_bankdev0->space(AS_PROGRAM).read_byte((lcd_bank << 13) + y*30 + x);

				for (int b=0; b<8; b++)
				{
					bitmap.pix(y, (x * 8) + b) = BIT(data, 0);
					data >>= 1;
				}
			}
	}
	else
	{
		bitmap.fill(0, cliprect);
	}

	return 0;
}

TIMER_DEVICE_CALLBACK_MEMBER(rex6000_state::irq_timer1)
{
	if (!(m_irq_mask & IRQ_FLAG_IRQ2))
	{
		m_irq_flag |= IRQ_FLAG_IRQ2;

		m_maincpu->set_input_line(0, HOLD_LINE);
	}

}

TIMER_DEVICE_CALLBACK_MEMBER(rex6000_state::irq_timer2)
{
	if (!(m_irq_mask & IRQ_FLAG_IRQ1))
	{
		m_irq_flag |= IRQ_FLAG_IRQ1;

		m_maincpu->set_input_line(0, HOLD_LINE);
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(rex6000_state::sec_timer)
{
	if (!(m_irq_mask & IRQ_FLAG_1HZ))
	{
		m_irq_flag |= IRQ_FLAG_1HZ;

		m_maincpu->set_input_line(0, HOLD_LINE);
	}
}

void rex6000_state::alarm_irq(int state)
{
	if (!(m_irq_mask & IRQ_FLAG_ALARM) && state)
	{
		m_irq_flag |= IRQ_FLAG_ALARM;
		m_maincpu->set_input_line(0, HOLD_LINE);
	}
}

void rex6000_state::serial_irq(int state)
{
	if (!(m_irq_mask & IRQ_FLAG_SERIAL))
	{
		m_irq_flag |= IRQ_FLAG_SERIAL;
		m_maincpu->set_input_line(0, HOLD_LINE);
	}
}

void rex6000_state::rex6000_palettte(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}

QUICKLOAD_LOAD_MEMBER(rex6000_state::quickload_rex6000)
{
	// FIXME: this suffers buffer overruns on excessively short images and various kinds of invalid images
	static const char magic[] = "ApplicationName:Addin";
	uint32_t img_start = 0;

	std::vector<uint8_t> data(image.length());
	image.fread(&data[0], image.length());

	if(strncmp((const char*)&data[0], magic, 21))
		return std::make_pair(image_error::INVALIDIMAGE, std::string());

	img_start = strlen((const char*)&data[0]) + 5;
	img_start += 0xa0;  //skip the icon (40x32 pixel)

	for (uint32_t i=0; i<image.length() - img_start ;i++)
		m_flash0b->write_raw(i, data[img_start + i]);

	return std::make_pair(std::error_condition(), std::string());
}

int oz750_state::oz_wzd_extract_tag(const std::vector<uint8_t> &data, const char *tag, char *dest_buf)
{
	int tag_len = strlen(tag);
	uint32_t img_start = 0;
	for (img_start=0; img_start < data.size() - tag_len; img_start++)
		if (data[img_start] && !memcmp(&data[img_start], tag, tag_len))
			break;

	if (img_start >= data.size() - tag_len)
	{
		if (dest_buf)
			strcpy(dest_buf, "NONE");
		return 0;
	}

	img_start += tag_len;

	while(data[img_start] == '\r' || data[img_start] == '\n')
		img_start++;

	if (dest_buf)
	{
		uint32_t i;
		for (i=0; data[img_start + i] != 0 && data[img_start + i] != '\n' && data[img_start + i] != '\r'; i++)
			dest_buf[i] = data[img_start + i];

		dest_buf[i] = '\0';
	}

	return img_start;
}

QUICKLOAD_LOAD_MEMBER(oz750_state::quickload_oz750)
{
	address_space* flash = &m_flash0a->memory().space(0);
	std::vector<uint8_t> data(image.length());
	image.fread(&data[0], image.length());

	const char *fs_type = "BSIC";
	char data_type[0x100];
	char app_name[0x100];
	char file_name[0x100];

	oz_wzd_extract_tag(data, "<DATA TYPE>", data_type);
	if (strcmp(data_type, "MY PROGRAMS"))
		return std::make_pair(image_error::INVALIDIMAGE, std::string());

	oz_wzd_extract_tag(data, "<TITLE>", app_name);
	oz_wzd_extract_tag(data, "<DATA>", file_name);
	if (!strncmp(file_name, "PFILE:", 6))
		memmove(file_name, file_name + 6, strlen(file_name + 6) + 1);

	uint32_t img_start = oz_wzd_extract_tag(data, "<BIN>", nullptr);

	if (img_start == 0)
		return std::make_pair(image_error::INVALIDIMAGE, std::string());

	uint16_t icon_size = data[img_start++];

	uint32_t pos = 0xc0000;
	flash->write_byte(pos++, 0x4f);

	for (int i=0; fs_type[i]; i++)
		flash->write_byte(pos++, fs_type[i]);                   // file type

	flash->write_byte(pos++, 0xff);
	flash->write_byte(pos++, 0xff);
	flash->write_byte(pos++, 0xff);
	flash->write_byte(pos++, 0x10 + icon_size);                                         // filename offset LSB
	flash->write_byte(pos++, 0x00);                                                     // filename offset MSB
	flash->write_byte(pos++, 0x11 + icon_size + strlen(file_name));                     // title offset LSB
	flash->write_byte(pos++, 0x00);                                                     // title offset MSB
	flash->write_byte(pos++, 0x12 + icon_size + strlen(file_name) + strlen(app_name));  // data offset LSB
	flash->write_byte(pos++, 0x00);                                                     // data offset MSB
	flash->write_byte(pos++, 1);
	flash->write_byte(pos++, 1);

	flash->write_byte(pos++, icon_size);                        // icon size

	for (int i=0; i<icon_size; i++)
		flash->write_byte(pos++, data[img_start++]);            // icon data

	for (int i=0, slen = strlen(file_name); i <= slen; i++)
		flash->write_byte(pos++, file_name[i]);                 // filename

	for (int i=0, slen = strlen(app_name); i <= slen; i++)
		flash->write_byte(pos++, app_name[i]);                  // title

	uint16_t size = (uint16_t)image.length() - img_start;
	flash->write_byte(pos++, size);                             // data size LSB
	flash->write_byte(pos++, size >> 8);                        // data size MSB

	for (int i=img_start; i<image.length(); i++)
		flash->write_byte(pos++, data[i]);                      // data

	return std::make_pair(std::error_condition(), std::string());
}


/* F4 Character Displayer */
static const gfx_layout rex6000_bold_charlayout =
{
	16, 11,                 /* 16 x 11 characters */
	256,                    /* 256 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 },
	/* y offsets */
	{ 0*16, 1*16, 2*16, 3*16, 4*16, 5*16, 6*16, 7*16, 8*16, 9*16, 10*16 },
	8*24            /* every char takes 24 bytes, first 2 bytes are used for the char size */
};

static const gfx_layout rex6000_tiny_charlayout =
{
	16, 9,                  /* 16 x 9 characters */
	256,                    /* 256 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 },
	/* y offsets */
	{ 0*16, 1*16, 2*16, 3*16, 4*16, 5*16, 6*16, 7*16, 8*16 },
	8*20            /* every char takes 20 bytes, first 2 bytes ared use for the char size */
};

static const gfx_layout rex6000_graph_charlayout =
{
	16, 13,                 /* 16 x 13 characters */
	48,                     /* 48 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 },
	/* y offsets */
	{ 0*16, 1*16, 2*16, 3*16, 4*16, 5*16, 6*16, 7*16, 8*16, 9*16, 10*16 , 11*16 , 12*16 },
	8*28            /* every char takes 28 bytes, first 2 bytes are used for the char size */
};

static GFXDECODE_START( gfx_rex6000 )
	GFXDECODE_ENTRY( "flash0a", 0x0f0000, rex6000_bold_charlayout,  0, 0 )  //normal
	GFXDECODE_ENTRY( "flash0a", 0x0f2000, rex6000_bold_charlayout,  0, 0 )  //bold
	GFXDECODE_ENTRY( "flash0a", 0x0f4000, rex6000_tiny_charlayout,  0, 0 )  //tiny
	GFXDECODE_ENTRY( "flash0a", 0x0f6000, rex6000_graph_charlayout, 0, 0 )  //graphic
GFXDECODE_END


void rex6000_state::rex6000(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'000'000)); //Toshiba microprocessor Z80 compatible at 4.3MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &rex6000_state::rex6000_mem);
	m_maincpu->set_addrmap(AS_IO, &rex6000_state::rex6000_io);

	TIMER(config, "sec_timer").configure_periodic(FUNC(rex6000_state::sec_timer), attotime::from_hz(1));
	TIMER(config, "irq_timer1").configure_periodic(FUNC(rex6000_state::irq_timer1), attotime::from_hz(32));
	TIMER(config, "irq_timer2").configure_periodic(FUNC(rex6000_state::irq_timer2), attotime::from_hz(4096));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(rex6000_state::screen_update));
	screen.set_size(240, 120);
	screen.set_visarea(0, 240-1, 0, 120-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(rex6000_state::rex6000_palettte), 2);
	GFXDECODE(config, "gfxdecode", "palette", gfx_rex6000);

	ADDRESS_MAP_BANK(config, "bank0").set_map(&rex6000_state::rex6000_banked_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x2000);
	ADDRESS_MAP_BANK(config, "bank1").set_map(&rex6000_state::rex6000_banked_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x2000);

	NS16550(config, m_uart, XTAL(1'843'200));
	m_uart->out_tx_callback().set("serport", FUNC(rs232_port_device::write_txd));
	m_uart->out_dtr_callback().set("serport", FUNC(rs232_port_device::write_dtr));
	m_uart->out_rts_callback().set("serport", FUNC(rs232_port_device::write_rts));
	m_uart->out_int_callback().set(FUNC(rex6000_state::serial_irq));

	rs232_port_device &serport(RS232_PORT(config, "serport", default_rs232_devices, nullptr));
	serport.rxd_handler().set(m_uart, FUNC(ins8250_uart_device::rx_w));
	serport.dcd_handler().set(m_uart, FUNC(ins8250_uart_device::dcd_w));
	serport.dsr_handler().set(m_uart, FUNC(ins8250_uart_device::dsr_w));
	serport.ri_handler().set(m_uart, FUNC(ins8250_uart_device::ri_w));
	serport.cts_handler().set(m_uart, FUNC(ins8250_uart_device::cts_w));

	/* quickload */
	QUICKLOAD(config, "quickload", "rex,ds2").set_load_callback(FUNC(rex6000_state::quickload_rex6000));

	tc8521_device &rtc(TC8521(config, TC8521_TAG, XTAL(32'768)));
	rtc.out_alarm_callback().set(FUNC(rex6000_state::alarm_irq));

	/*
	Fujitsu 29DL16X has a feature which is capable of reading data from one
	bank of memory while a program or erase operation is in progress in the
	other bank of memory (simultaneous operation). This is not supported yet
	by the flash emulation and I have split every bank into a separate
	device in order to have similar behavior.
	*/
	FUJITSU_29DL164BD(config, m_flash0a); //bank 0 of first flash
	FUJITSU_29DL164BD(config, m_flash0b); //bank 1 of first flash
	FUJITSU_29DL164BD(config, m_flash1a); //bank 0 of second flash
	FUJITSU_29DL164BD(config, m_flash1b); //bank 1 of second flash

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("32K");

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 0).add_route(ALL_OUTPUTS, "mono", 1.00);
}

void oz750_state::oz750(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(9'830'400)); //Toshiba microprocessor Z80 compatible at 9.8MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &oz750_state::rex6000_mem);
	m_maincpu->set_addrmap(AS_IO, &oz750_state::oz750_io);

	TIMER(config, "sec_timer").configure_periodic(FUNC(rex6000_state::sec_timer), attotime::from_hz(1));
	TIMER(config, "irq_timer1").configure_periodic(FUNC(rex6000_state::irq_timer1), attotime::from_hz(64));
	TIMER(config, "irq_timer2").configure_periodic(FUNC(rex6000_state::irq_timer2), attotime::from_hz(8192));

	NS16550(config, m_uart, XTAL(9'830'400) / 4);
	m_uart->out_tx_callback().set("serport", FUNC(rs232_port_device::write_txd));
	m_uart->out_dtr_callback().set("serport", FUNC(rs232_port_device::write_dtr));
	m_uart->out_rts_callback().set("serport", FUNC(rs232_port_device::write_rts));
	m_uart->out_int_callback().set(FUNC(rex6000_state::serial_irq));

	rs232_port_device &serport(RS232_PORT(config, "serport", default_rs232_devices, nullptr));
	serport.rxd_handler().set(m_uart, FUNC(ins8250_uart_device::rx_w));
	serport.dcd_handler().set(m_uart, FUNC(ins8250_uart_device::dcd_w));
	serport.dsr_handler().set(m_uart, FUNC(ins8250_uart_device::dsr_w));
	serport.ri_handler().set(m_uart, FUNC(ins8250_uart_device::ri_w));
	//serport.cts_handler().set(m_uart, FUNC(ins8250_uart_device::cts_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(oz750_state::screen_update_oz));
	screen.set_size(240, 80);
	screen.set_visarea(0, 240-1, 0, 80-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(rex6000_state::rex6000_palettte), 2);

	ADDRESS_MAP_BANK(config, "bank0").set_map(&oz750_state::oz750_banked_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x2000);
	ADDRESS_MAP_BANK(config, "bank1").set_map(&oz750_state::oz750_banked_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x2000);

	/* quickload */
	QUICKLOAD(config, "quickload", "wzd").set_load_callback(FUNC(oz750_state::quickload_oz750));

	tc8521_device &rtc(TC8521(config, TC8521_TAG, XTAL(32'768)));
	rtc.out_alarm_callback().set(FUNC(rex6000_state::alarm_irq));

	SHARP_LH28F016S(config, m_flash0a);
	SHARP_LH28F016S(config, m_flash1a);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("512K");

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 0).add_route(ALL_OUTPUTS, "mono", 1.00);
}

/* ROM definition */
ROM_START( rex6000 )
	ROM_REGION( 0x200000, "flash0a", ROMREGION_ERASE )
	ROM_LOAD( "rex6000_0.dat", 0x0000, 0x100000, BAD_DUMP CRC(e7a7324f) SHA1(01adcd469c93984438cbd4bf6e5a46e74da500d0))

	ROM_REGION( 0x200000, "flash0b", ROMREGION_ERASE )
	ROM_LOAD( "rex6000_1.dat", 0x0000, 0x100000, BAD_DUMP CRC(cc4b51db) SHA1(73b655e21ece30e8dc60f2f09719bc446492cc0f))

	ROM_REGION( 0x200000, "flash1a", ROMREGION_ERASE )
	ROM_LOAD( "rex6000_2.dat", 0x0000, 0x100000, NO_DUMP)

	ROM_REGION( 0x200000, "flash1b", ROMREGION_ERASE )
	ROM_LOAD( "rex6000_3.dat", 0x0000, 0x100000, NO_DUMP)
ROM_END

ROM_START( ds2 )
	ROM_REGION( 0x200000, "flash0a", ROMREGION_ERASE )
	ROM_LOAD( "ds2_0.dat", 0x0000, 0x100000, BAD_DUMP CRC(3197bed3) SHA1(9f826fd6d23ced797daae666d88ced14d24b64c3))

	ROM_REGION( 0x200000, "flash0b", ROMREGION_ERASE )
	ROM_LOAD( "ds2_1.dat", 0x0000, 0x100000, BAD_DUMP CRC(a909b755) SHA1(4db8a44539b9f94d418c33bbd794afb9bacbbadd))

	ROM_REGION( 0x200000, "flash1a", ROMREGION_ERASE )
	ROM_LOAD( "ds2_2.dat", 0x0000, 0x100000, BAD_DUMP CRC(a738ea1c) SHA1(3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3))

	ROM_REGION( 0x200000, "flash1b", ROMREGION_ERASE )
	ROM_LOAD( "ds2_3.dat", 0x0000, 0x100000, BAD_DUMP CRC(64f7c189) SHA1(a47d3413ddb879083c3aec03a42fe357d3a3c10a))
ROM_END

ROM_START( oz750 )
	ROM_REGION( 0x200000, "flash0a", ROMREGION_ERASEFF )
	ROM_LOAD( "oz750_0.bin", 0x0000, 0x200000, CRC(bf321bab) SHA1(2fb44a870b26aebf4b949b5de84fb39cd9205191))

	ROM_REGION( 0x200000, "flash1a", ROMREGION_ERASEFF )
	ROM_LOAD( "oz750_1.bin", 0x0000, 0x100000, CRC(d74e97d7) SHA1(19d17393a9af85e07773feaf1aed5e2cfa80f7cc))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY           FULLNAME         FLAGS */
COMP( 199?, oz750,   0,       0,      oz750,   oz750,   oz750_state,   empty_init, "Sharp",          "Wizard OZ-750", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
COMP( 2000, rex6000, 0,       0,      rex6000, rex6000, rex6000_state, empty_init, "Xircom / Intel", "REX 6000",      MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
COMP( 2000, ds2,     rex6000, 0,      rex6000, rex6000, rex6000_state, empty_init, "Citizen",        "DataSlim 2",    MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



risc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco, hap
/*******************************************************************************

Mephisto Risc 1MB/II (stylized "risc")

The chess engine in Mephisto Risc is also compatible with Tasc's The ChessMachine,
it is more or less equivalent to Gideon 3.0 (Risc 1MB) and Gideon 3.1 (Risc II),
see ROM defs for details. "Main" CPU is slow, but all the chess calculations are
done with the ARM.

Hardware notes:
- G65SC02P-4 @ 2.5MHz
- 128KB ROM
- Tasc ChessMachine EC PCB
- Mephisto modular display module
- Mephisto modular chessboard

*******************************************************************************/

#include "emu.h"

#include "mmboard.h"
#include "mmdisplay2.h"

#include "cpu/m6502/g65sc02.h"
#include "machine/74259.h"
#include "machine/chessmachine.h"
#include "machine/nvram.h"

// internal artwork
#include "mephisto_risc.lh"


namespace {

class risc_state : public driver_device
{
public:
	risc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_chessm(*this, "chessm"),
		m_rombank(*this, "rombank"),
		m_keys(*this, "KEY")
	{ }

	void mrisc(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<chessmachine_device> m_chessm;
	required_memory_bank m_rombank;
	required_ioport m_keys;

	void mrisc_mem(address_map &map) ATTR_COLD;

	u8 keys_r(offs_t offset);
	u8 chessm_r();
	void chessm_w(u8 data);
};

void risc_state::machine_start()
{
	m_rombank->configure_entries(0, 4, memregion("maincpu")->base(), 0x8000);
}



/*******************************************************************************
    I/O
*******************************************************************************/

u8 risc_state::keys_r(offs_t offset)
{
	return ~(BIT(m_keys->read(), offset) << 7);
}

u8 risc_state::chessm_r()
{
	// d0: chessmachine data
	return m_chessm->data_r();
}

void risc_state::chessm_w(u8 data)
{
	// d0,d7: chessmachine data
	m_chessm->data0_w(BIT(data, 0));
	m_chessm->data1_w(BIT(data, 7));

	// d1: chessmachine reset
	m_chessm->reset_w(BIT(data, 1));
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void risc_state::mrisc_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).ram().share("nvram");
	map(0x2000, 0x2000).w("display", FUNC(mephisto_display2_device::latch_w));
	map(0x2004, 0x2004).w("display", FUNC(mephisto_display2_device::io_w));
	map(0x2c00, 0x2c07).r(FUNC(risc_state::keys_r));
	map(0x2400, 0x2400).w("board", FUNC(mephisto_board_device::led_w));
	map(0x2800, 0x2800).w("board", FUNC(mephisto_board_device::mux_w));
	map(0x3000, 0x3000).r("board", FUNC(mephisto_board_device::input_r));
	map(0x3400, 0x3407).w("outlatch", FUNC(hc259_device::write_d7)).nopr();
	map(0x3800, 0x3800).w(FUNC(risc_state::chessm_w));
	map(0x3c00, 0x3c00).r(FUNC(risc_state::chessm_r));
	map(0x4000, 0x7fff).rom();
	map(0x8000, 0xffff).bankr("rombank");
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( mrisc )
	PORT_START("KEY")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("TRN / Pawn")      PORT_CODE(KEYCODE_T)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("INFO / Knight")   PORT_CODE(KEYCODE_I)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("MEM / Bishop")    PORT_CODE(KEYCODE_M)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("POS / Rook")      PORT_CODE(KEYCODE_P)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("LEV / Queen")     PORT_CODE(KEYCODE_L)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("FCT / King")      PORT_CODE(KEYCODE_F)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("ENT / New Game")  PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CODE(KEYCODE_F1) // combine for NEW GAME
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("CL / New Game")   PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_F1) // "
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void risc_state::mrisc(machine_config &config)
{
	// basic machine hardware
	G65SC02(config, m_maincpu, 10_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &risc_state::mrisc_mem);

	const attotime irq_period = attotime::from_hz(10_MHz_XTAL / 0x4000);
	m_maincpu->set_periodic_int(FUNC(risc_state::irq0_line_hold), irq_period);

	CHESSMACHINE(config, m_chessm, 14'000'000); // Mephisto manual says 14MHz (no XTAL)

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	hc259_device &outlatch(HC259(config, "outlatch"));
	outlatch.q_out_cb<0>().set_output("led100");
	outlatch.q_out_cb<1>().set_output("led101");
	outlatch.q_out_cb<2>().set_output("led102");
	outlatch.q_out_cb<3>().set_output("led103");
	outlatch.q_out_cb<4>().set_output("led104");
	outlatch.q_out_cb<5>().set_output("led105");
	outlatch.parallel_out_cb().set_membank("rombank").rshift(6).mask(0x03).exor(0x01);

	MEPHISTO_SENSORS_BOARD(config, "board");
	MEPHISTO_DISPLAY_MODULE2(config, "display");
	config.set_default_layout(layout_mephisto_risc);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( mrisc )
	ROM_REGION( 0x20000, "maincpu", 0 )
	// contains ChessMachine engine at 0x0-0x03fff + 0x10000-0x1c74f, concatenate those sections and make a .bin file,
	// then it will work on ChessMachine software. It identifies as R E B E L version HG-021, 03-04-92
	ROM_LOAD("meph-risci-v1-2", 0x00000, 0x20000, CRC(19c6ab83) SHA1(0baab84e5aa6999c24250938d207145144945fd5) )
ROM_END

ROM_START( mrisca )
	ROM_REGION( 0x20000, "maincpu", 0 )
	// contains ChessMachine engine at 0x0-0x03fff + 0x10000-0x1c6db, concatenate those sections and make a .bin file,
	// then it will work on ChessMachine software. It identifies as R E B E L version HG-020, 14-03-92
	ROM_LOAD("risc_1mb_rebel", 0x00000, 0x20000, CRC(f00b43ab) SHA1(8e9f3c99331b104af2008db1f0538ebaa97bc1e9) )
ROM_END

ROM_START( mrisc2 )
	ROM_REGION( 0x20000, "maincpu", 0 )
	// contains ChessMachine engine at 0x0-0x03fff + 0x10000-0x1cb7f, concatenate those sections and make a .bin file,
	// then it will work on ChessMachine software. It identifies as R E B E L version 2.31, 22-07-93, world champion Madrid 1992
	ROM_LOAD("risc_2.31", 0x00000, 0x20000, CRC(9ecf9cd3) SHA1(7bfc628183037a172242c9589f15aca218d8fb12) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT   COMPAT  MACHINE   INPUT  CLASS       INIT        COMPANY, FULLNAME, FLAGS
SYST( 1992, mrisc,   0,       0,      mrisc,    mrisc, risc_state, empty_init, "Hegener + Glaser / Tasc", "Mephisto Risc 1MB (v1.2)", MACHINE_SUPPORTS_SAVE )
SYST( 1992, mrisca,  mrisc,   0,      mrisc,    mrisc, risc_state, empty_init, "Hegener + Glaser / Tasc", "Mephisto Risc 1MB (v1.0)", MACHINE_SUPPORTS_SAVE )

SYST( 1994, mrisc2,  0,       0,      mrisc,    mrisc, risc_state, empty_init, "Hegener + Glaser / Tasc", "Mephisto Risc II", MACHINE_SUPPORTS_SAVE )



risc2500.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco, hap
/*******************************************************************************

Saitek RISC 2500, Mephisto Montreux

The chess engine (The King) is also compatible with Tasc's The ChessMachine
software. The hardware+software appears to have been subcontracted to Tasc.
It has similarities with Tasc R30.

To make sure it continues the game at next power-on, press the OFF button before
exiting MAME. If nvram is broken somehow, boot with the BACK button held down.

Hardware notes:
- PCB label: TASC23C
- ARM2 CPU(VY86C010) @ 14.16MHz
- 128KB ROM, 128KB RAM*
- SED1520, custom LCD screen
- 8*8 chessboard buttons, 16 leds, piezo

*: Sold with 128KB RAM by default. This can be easily increased up to 2MB
by the user(chesscomputer owner, but also the MAME user in this case).
The manual also says that RAM is expandable.

According to Saitek's repair manual, there is a GAL and a clock frequency
divider chip, ROM access goes through it. This allows reading from slow EPROM
while the chess engine resides in faster RAM. The piezo output routine is
also in ROM, it would be way too high-pitched at full speed.

Undocumented buttons:
- hold LEFT+RIGHT on boot to start the QC TestMode
- hold UP+DOWN on boot to start the TestMode

TODO:
- bootrom disable timer shouldn't be needed, real ARM has already fetched the next opcode
- More accurate dynamic cpu clock divider, without the cost of emulation speed.
  The current implementation catches almost everything, luckily ARM opcodes have a
  fixed length. It only fails to detect ALU opcodes that directly modify pc(R15).
  It also possibly has problems with very short subroutine calls from ROM to RAM,
  but I tested for those and the shortest one is more than 50 cycles.

*******************************************************************************/

#include "emu.h"

#include "cpu/arm/arm.h"
#include "machine/nvram.h"
#include "machine/ram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"
#include "video/sed1520.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

// internal artwork
#include "mephisto_montreux.lh"
#include "saitek_risc2500.lh"


namespace {

class risc2500_state : public driver_device
{
public:
	risc2500_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_boot_view(*this, "boot_view"),
		m_rom(*this, "maincpu"),
		m_ram(*this, "ram"),
		m_nvram(*this, "nvram"),
		m_board(*this, "board"),
		m_led_pwm(*this, "led_pwm"),
		m_lcdc(*this, "lcdc"),
		m_screen(*this, "screen"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_lcd_dmz(*this, "dmz%u.%u.%u", 0U, 0U, 0U),
		m_lcd_digit(*this, "digit%u", 0U),
		m_lcd_seg(*this, "seg%u.%u", 0U, 0U),
		m_lcd_sym(*this, "sym%u", 0U)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(on_button);

	void risc2500(machine_config &config);
	void montreux(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<arm_cpu_device> m_maincpu;
	memory_view m_boot_view;
	required_region_ptr<u32> m_rom;
	required_device<ram_device> m_ram;
	required_device<nvram_device> m_nvram;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_led_pwm;
	required_device<sed1520_device> m_lcdc;
	required_device<screen_device> m_screen;
	required_device<dac_2bit_ones_complement_device> m_dac;
	required_ioport_array<8> m_inputs;
	output_finder<12, 7, 6> m_lcd_dmz;
	output_finder<12> m_lcd_digit;
	output_finder<12, 8> m_lcd_seg;
	output_finder<14> m_lcd_sym;

	emu_timer *m_boot_timer;

	bool m_power = false;
	u32 m_control = 0;
	u32 m_prev_pc = 0;
	u64 m_prev_cycle = 0;

	void risc2500_mem(address_map &map) ATTR_COLD;

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	SED1520_UPDATE_CB(sed1520_update);

	u32 input_r();
	void control_w(offs_t offset, u32 data, u32 mem_mask = ~0);
	u32 rom_r(offs_t offset);
	void power_off();

	u32 disable_bootrom_r();
	TIMER_CALLBACK_MEMBER(disable_bootrom) { m_boot_view.select(1); }
};



/*******************************************************************************
    Initialization
*******************************************************************************/

void risc2500_state::machine_start()
{
	m_lcd_dmz.resolve();
	m_lcd_digit.resolve();
	m_lcd_seg.resolve();
	m_lcd_sym.resolve();

	m_boot_timer = timer_alloc(FUNC(risc2500_state::disable_bootrom), this);
	m_boot_view[1].install_ram(0, m_ram->size() - 1, m_ram->pointer());
	m_nvram->set_base(m_ram->pointer(), m_ram->size());

	// register for savestates
	save_item(NAME(m_power));
	save_item(NAME(m_control));
	save_item(NAME(m_prev_pc));
	save_item(NAME(m_prev_cycle));
}

void risc2500_state::machine_reset()
{
	m_boot_view.select(0);
	m_boot_timer->adjust(attotime::never);

	m_power = true;
	m_control = 0;
	m_prev_pc = m_maincpu->pc();
	m_prev_cycle = m_maincpu->total_cycles();
}



/*******************************************************************************
    Video
*******************************************************************************/

u32 risc2500_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	// forward to SED1520
	return m_lcdc->screen_update(screen, m_screen->curbitmap().as_ind16(), cliprect);
}

SED1520_UPDATE_CB(risc2500_state::sed1520_update)
{
	for (int c = 0; c < 12; c++)
	{
		u8 data = 0;

		// 12 characters 5 x 7
		for (int x = 0; x < 5; x++)
		{
			if (lcd_on)
				data = bitswap<8>(dram[c * 5 + x], 6,5,0,1,2,3,4,7);

			for (int y = 1; y < 8; y++)
				m_lcd_dmz[11 - c][y - 1][4 - x] = BIT(data, y);
		}

		// LCD digits and symbols
		int data_addr = 80 + c * 5;
		if (lcd_on)
		{
			data = ((dram[data_addr + 1] & 0x3) << 5) | ((dram[data_addr + 2] & 0x7) << 2) | (dram[data_addr + 4] & 0x3);
			data = bitswap<8>(data, 7,3,0,1,4,6,5,2) | ((dram[data_addr - 1] & 0x04) ? 0x80 : 0);
		}

		for (int s = 0; s < 8; s++)
			m_lcd_seg[11 - c][s] = BIT(data, s);

		m_lcd_digit[11 - c] = data;
		m_lcd_sym[c] = lcd_on ? BIT(dram[data_addr + 1], 2) : 0;
	}

	m_lcd_sym[12] = lcd_on ? BIT(dram[0x73], 0) : 0;
	m_lcd_sym[13] = lcd_on ? BIT(dram[0x5a], 0) : 0;

	return 0;
}



/*******************************************************************************
    I/O
*******************************************************************************/

// soft power on/off

INPUT_CHANGED_MEMBER(risc2500_state::on_button)
{
	if (newval && !m_power)
	{
		m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
		machine_reset();
	}
}

void risc2500_state::power_off()
{
	m_power = false;
	m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);

	// clear display
	m_lcdc->reset();
	m_led_pwm->clear();
}


// main I/O

u32 risc2500_state::input_r()
{
	u32 data = 0;

	for (int i = 0; i < 8; i++)
	{
		if (BIT(m_control, i))
		{
			data |= m_inputs[i]->read() << 24;
			data |= m_board->read_rank(i, true);
		}
	}

	if (!machine().side_effects_disabled())
		m_maincpu->set_input_line(ARM_FIRQ_LINE, CLEAR_LINE);

	return data;
}

void risc2500_state::control_w(offs_t offset, u32 data, u32 mem_mask)
{
	if (mem_mask != 0xffffffff)
		logerror("control_w unexpected mem_mask %08X\n", mem_mask);

	// lcd
	if (BIT(m_control & ~data, 27))
	{
		if (BIT(data, 26))
			m_lcdc->data_write(data);
		else
			m_lcdc->control_write(data);
	}

	// leds
	m_led_pwm->matrix(data >> 30, ~data & 0xff);

	// speaker
	m_dac->write(data >> 28 & 3);

	// power-off
	if (BIT(m_control & ~data, 24))
		power_off();

	m_control = data;
}

u32 risc2500_state::disable_bootrom_r()
{
	// disconnect bootrom from the bus after next opcode
	if (!machine().side_effects_disabled() && m_boot_timer->remaining().is_never())
		m_boot_timer->adjust(m_maincpu->cycles_to_attotime(10));

	return 0;
}

u32 risc2500_state::rom_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
	{
		// handle dynamic cpu clock divider when accessing rom
		s64 diff = m_maincpu->total_cycles() - m_prev_cycle;
		u32 pc = m_maincpu->pc();

		if (diff > 0)
		{
			static constexpr int arm_branch_cycles = 3;
			static constexpr int arm_max_cycles = 17; // datablock transfer
			static constexpr int divider = -8 + 1;

			// this takes care of almost all cases, otherwise, total cycles taken can't be determined
			if (diff <= arm_branch_cycles || (diff <= arm_max_cycles && (pc - m_prev_pc) == 4 && (pc & ~0x02000000) == (offset * 4)))
				m_maincpu->adjust_icount(divider * (int)diff);
			else
				m_maincpu->adjust_icount(divider);
		}

		m_prev_cycle = m_maincpu->total_cycles();
		m_prev_pc = pc;
	}

	return m_rom[offset];
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void risc2500_state::risc2500_mem(address_map &map)
{
	map(0x00000000, 0x001fffff).view(m_boot_view);
	m_boot_view[0](0x00000000, 0x0003ffff).r(FUNC(risc2500_state::rom_r));

	map(0x01000000, 0x01000003).rw(FUNC(risc2500_state::input_r), FUNC(risc2500_state::control_w));
	map(0x01800000, 0x01800003).r(FUNC(risc2500_state::disable_bootrom_r));
	map(0x02000000, 0x0203ffff).r(FUNC(risc2500_state::rom_r));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( risc2500 )
	PORT_START("IN.0")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Pawn")     PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Back")     PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_BACKSPACE)

	PORT_START("IN.1")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Knight")   PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Enter")    PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)

	PORT_START("IN.2")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Bishop")   PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Down")     PORT_CODE(KEYCODE_DOWN)

	PORT_START("IN.3")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Rook")     PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Up")       PORT_CODE(KEYCODE_UP)

	PORT_START("IN.4")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Queen")    PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Menu")     PORT_CODE(KEYCODE_M)

	PORT_START("IN.5")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("King")     PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Play")     PORT_CODE(KEYCODE_P)

	PORT_START("IN.6")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Right")    PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("New Game") PORT_CODE(KEYCODE_N)

	PORT_START("IN.7")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Left")     PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Off")      PORT_CODE(KEYCODE_F)

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("On")       PORT_CODE(KEYCODE_O) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(risc2500_state::on_button), 0)
INPUT_PORTS_END

static INPUT_PORTS_START( montreux ) // on/off buttons have different labels
	PORT_INCLUDE( risc2500 )

	PORT_MODIFY("IN.7")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Stop")     PORT_CODE(KEYCODE_S)

	PORT_MODIFY("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Go")       PORT_CODE(KEYCODE_G) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(risc2500_state::on_button), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void risc2500_state::risc2500(machine_config &config)
{
	// basic machine hardware
	ARM(config, m_maincpu, 28.322_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &risc2500_state::risc2500_mem);
	m_maincpu->set_copro_type(arm_cpu_device::copro_type::VL86C020);

	const attotime irq_period = attotime::from_hz(32.768_kHz_XTAL / 128); // 256Hz
	m_maincpu->set_periodic_int(FUNC(risc2500_state::irq1_line_assert), irq_period);

	RAM(config, m_ram).set_extra_options("128K, 256K, 512K, 1M, 2M");
	m_ram->set_default_size("128K");
	m_ram->set_default_value(0);

	NVRAM(config, "nvram", nvram_device::DEFAULT_NONE);

	SENSORBOARD(config, m_board);
	m_board->set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(100));
	m_board->set_nvram_enable(true);

	// video hardware
	SED1520(config, m_lcdc);
	m_lcdc->set_screen_update_cb(FUNC(risc2500_state::sed1520_update));

	SCREEN(config, m_screen, SCREEN_TYPE_SVG);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(1920/5, 768/5);
	m_screen->set_visarea_full();
	m_screen->set_screen_update(FUNC(risc2500_state::screen_update));

	PWM_DISPLAY(config, m_led_pwm).set_size(2, 8);
	config.set_default_layout(layout_saitek_risc2500);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_2BIT_ONES_COMPLEMENT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.125);
}

void risc2500_state::montreux(machine_config &config)
{
	risc2500(config);
	config.set_default_layout(layout_mephisto_montreux);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( risc2500 ) // v1.04 21-Oct-92
	ROM_REGION( 0x40000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD("st17_a22_u_7.u7", 0x000000, 0x020000, CRC(84a06178) SHA1(66f4d9f53de6da865a3ebb4af1d6a3e245c59a3c) ) // 27C010A-12

	ROM_REGION( 221222, "screen", 0 )
	ROM_LOAD("risc2500.svg", 0, 221222, CRC(73076886) SHA1(d278f071f8a92201a5aba59ad1c99234a389118f) )
ROM_END

ROM_START( risc2500a ) // v1.03 14-Oct-92
	ROM_REGION( 0x40000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD("st17_a15.u7", 0x000000, 0x020000, CRC(7a707e82) SHA1(87187fa58117a442f3abd30092cfcc2a4d7c7efc) ) // 27C010A-15

	ROM_REGION( 221222, "screen", 0 )
	ROM_LOAD("risc2500.svg", 0, 221222, CRC(73076886) SHA1(d278f071f8a92201a5aba59ad1c99234a389118f) )
ROM_END

ROM_START( montreux ) // v1.00 10-Dec-94
	ROM_REGION( 0x40000, "maincpu", 0 )
	ROM_LOAD("rt17b_103_u_7.u7", 0x000000, 0x040000, CRC(db374cf3) SHA1(44dd60d56779084326c3dfb41d2137ebf0b4e0ac) ) // 27C020-15

	ROM_REGION( 221222, "screen", 0 )
	ROM_LOAD("risc2500.svg", 0, 221222, CRC(73076886) SHA1(d278f071f8a92201a5aba59ad1c99234a389118f) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT    COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1992, risc2500,  0,        0,      risc2500, risc2500, risc2500_state, empty_init, "Saitek / Tasc", "Kasparov RISC 2500 (v1.04)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_TIMING )
SYST( 1992, risc2500a, risc2500, 0,      risc2500, risc2500, risc2500_state, empty_init, "Saitek / Tasc", "Kasparov RISC 2500 (v1.03)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_TIMING )

SYST( 1995, montreux,  0,        0,      montreux, montreux, risc2500_state, empty_init, "Saitek / Tasc", "Mephisto Montreux", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_TIMING ) // after Saitek bought Hegener + Glaser



riscpc.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Angelo Salese
// thanks-to: Tomasz Slanina, Sarah Walker
/***************************************************************************

    Acorn RiscPC line of computers

    TODO:
    - IOMD currently hardwired with ARM7500FE flavour for all machines, needs information about
      which uses what;
    - PS/2 keyboard doesn't work properly;
    - Fix pendingUnd fatalerror from ARM7 core;
    - Fix pendingAbtD fatalerror for RiscOS 4.xx;

****************************************************************************/

#include "emu.h"
#include "cpu/arm7/arm7.h"
#include "machine/acorn_vidc.h"
#include "machine/arm_iomd.h"
#include "machine/i2cmem.h"
#include "machine/at_keybc.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "bus/pc_kbd/keyboards.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"
#include "debugger.h"


namespace {

class riscpc_state : public driver_device
{
public:
	riscpc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_vidc(*this, "vidc")
		, m_iomd(*this, "iomd")
		, m_screen(*this, "screen")
		, m_i2cmem(*this, "i2cmem")
		, m_kbdc(*this, "kbdc")
		, m_mouse(*this, "MOUSE")
	{ }

	void rpc700(machine_config &config);
	void rpc600(machine_config &config);
	void sarpc(machine_config &config);
	void sarpc_j233(machine_config &config);
	void a7000(machine_config &config);
	void a7000p(machine_config &config);

private:
	void base_config(machine_config &config);

	required_device<cpu_device> m_maincpu;
	required_device<arm_vidc20_device> m_vidc;
	required_device<arm7500fe_iomd_device> m_iomd;
	required_device<screen_device> m_screen;
	required_device<i2cmem_device> m_i2cmem;
	required_device<ps2_keyboard_controller_device> m_kbdc;
	required_ioport m_mouse;

	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

	void a7000_map(address_map &map) ATTR_COLD;
	void riscpc_map(address_map &map) ATTR_COLD;

	bool m_i2cmem_clock = false;
	int iocr_od0_r();
	int iocr_od1_r();
	void iocr_od0_w(int state);
	void iocr_od1_w(int state);
};

int riscpc_state::iocr_od1_r()
{
	// TODO: presuming same as Acorn Archimedes, where i2c clock can be readback
	return (m_i2cmem_clock == true) ? 1 : 0;
}

int riscpc_state::iocr_od0_r()
{
	return (m_i2cmem->read_sda() ? 1 : 0); //eeprom read
}

void riscpc_state::iocr_od0_w(int state)
{
	m_i2cmem->write_sda(state == true ? 1 : 0);
}

void riscpc_state::iocr_od1_w(int state)
{
	m_i2cmem_clock = state;
	m_i2cmem->write_scl(state == true ? 1 : 0);
}

void riscpc_state::a7000_map(address_map &map)
{
	map(0x00000000, 0x003fffff).mirror(0x00800000).rom().region("user1", 0);
//  map(0x01000000, 0x01ffffff).noprw(); //expansion ROM
	//
//  map(0x02000000, 0x027fffff).mirror(0x00800000).ram(); // VRAM, not installed on A7000 models
//  I/O 03000000 - 033fffff
//  AM_RANGE(0x03010000, 0x03011fff) //Super IO
//  AM_RANGE(0x03012000, 0x03029fff) //FDC
//  AM_RANGE(0x0302b000, 0x0302bfff) //Network podule
//  AM_RANGE(0x03040000, 0x0304ffff) //podule space 0,1,2,3
//  AM_RANGE(0x03070000, 0x0307ffff) //podule space 4,5,6,7
	map(0x03200000, 0x032001ff).m(m_iomd, FUNC(arm7500fe_iomd_device::map));
	map(0x03310000, 0x03310003).portr(m_mouse);

	map(0x03400000, 0x037fffff).w(m_vidc, FUNC(arm_vidc20_device::write));
//  AM_RANGE(0x08000000, 0x08ffffff) AM_MIRROR(0x07000000) //EASI space

	map(0x10000000, 0x13ffffff).ram(); //SIMM 0 bank 0
	map(0x14000000, 0x17ffffff).ram(); //SIMM 0 bank 1
//  map(0x18000000, 0x18ffffff).mirror(0x03000000).ram(); //SIMM 1 bank 0
//  map(0x1c000000, 0x1cffffff).mirror(0x03000000).ram(); //SIMM 1 bank 1
}

void riscpc_state::riscpc_map(address_map &map)
{
	a7000_map(map);
	map(0x02000000, 0x027fffff).mirror(0x00800000).ram(); // VRAM
}


/* Input ports */
static INPUT_PORTS_START( a7000 )
//  PORT_INCLUDE( at_keyboard )

	PORT_START("MOUSE")
	// for debugging we leave video and sound HWs as options, eventually slotify them
	PORT_CONFNAME( 0x01, 0x00, "Monitor Type" )
	PORT_CONFSETTING(    0x00, "VGA" )
	PORT_CONFSETTING(    0x01, "TV Screen" )
	PORT_BIT( 0x0e, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Mouse Right")   PORT_CODE(MOUSECODE_BUTTON3)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Mouse Center")  PORT_CODE(MOUSECODE_BUTTON2)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Mouse Left")    PORT_CODE(MOUSECODE_BUTTON1)
	// TODO: understand condition where this occurs
	PORT_CONFNAME( 0x80, 0x00, "CMOS Reset bit" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x80, DEF_STR( On ) )
	PORT_CONFNAME( 0x100, 0x000, "Sound HW" )
	PORT_CONFSETTING(    0x000, "16-bit" )
	PORT_CONFSETTING(    0x100, "8-bit" )
	PORT_BIT(0xfffffe00, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

void riscpc_state::machine_start()
{
	// ...
}

void riscpc_state::machine_reset()
{

}

void riscpc_state::base_config(machine_config &config)
{
	I2C_24C02(config, m_i2cmem);

	// TODO: verify type
	pc_kbdc_device &kbd_con(PC_KBDC(config, "kbd", pc_at_keyboards, STR_KBD_IBM_PC_AT_101));
	kbd_con.out_clock_cb().set(m_kbdc, FUNC(ps2_keyboard_controller_device::kbd_clk_w));
	kbd_con.out_data_cb().set(m_kbdc, FUNC(ps2_keyboard_controller_device::kbd_data_w));

	// auxiliary connector
//  pc_kbdc_device &aux_con(PC_KBDC(config, "aux", ps2_mice, STR_HLE_PS2_MOUSE));
//  aux_con.out_clock_cb().set(m_kbdc, FUNC(ps2_keyboard_controller_device::aux_clk_w));
//  aux_con.out_data_cb().set(m_kbdc, FUNC(ps2_keyboard_controller_device::aux_data_w));

	PS2_KEYBOARD_CONTROLLER(config, m_kbdc, 12_MHz_XTAL);
	m_kbdc->hot_res().set(m_iomd, FUNC(arm_iomd_device::keyboard_reset));
	m_kbdc->kbd_clk().set(kbd_con, FUNC(pc_kbdc_device::clock_write_from_mb));
	m_kbdc->kbd_data().set(kbd_con, FUNC(pc_kbdc_device::data_write_from_mb));
	m_kbdc->kbd_irq().set(m_iomd, FUNC(arm_iomd_device::keyboard_irq));
//  m_kbdc->aux_clk().set(aux_con, FUNC(pc_kbdc_device::clock_write_from_mb));
//  m_kbdc->aux_data().set(aux_con, FUNC(pc_kbdc_device::data_write_from_mb));
//  m_kbdc->aux_irq().set(FUNC(riscpc_state::keyboard_interrupt));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);

	ARM_VIDC20(config, m_vidc, 24_MHz_XTAL);
	m_vidc->set_screen("screen");
	m_vidc->vblank().set(m_iomd, FUNC(arm_iomd_device::vblank_irq));
	m_vidc->sound_drq().set(m_iomd, FUNC(arm_iomd_device::sound_drq));

	m_iomd->set_host_cpu_tag(m_maincpu);
	m_iomd->set_vidc_tag(m_vidc);
	m_iomd->set_kbdc_tag(m_kbdc);
	m_iomd->iocr_read_od<0>().set(FUNC(riscpc_state::iocr_od0_r));
	m_iomd->iocr_read_od<1>().set(FUNC(riscpc_state::iocr_od1_r));
	m_iomd->iocr_write_od<0>().set(FUNC(riscpc_state::iocr_od0_w));
	m_iomd->iocr_write_od<1>().set(FUNC(riscpc_state::iocr_od1_w));
}

void riscpc_state::rpc600(machine_config &config)
{
	constexpr XTAL cpuxtal(60_MHz_XTAL/2);

	ARM7(config, m_maincpu, cpuxtal); // really ARM610
	m_maincpu->set_addrmap(AS_PROGRAM, &riscpc_state::riscpc_map);

	ARM7500FE_IOMD(config, m_iomd, cpuxtal);
	base_config(config);
}

void riscpc_state::rpc700(machine_config &config)
{
	constexpr XTAL cpuxtal(80_MHz_XTAL/2);
	ARM710A(config, m_maincpu, cpuxtal);
	m_maincpu->set_addrmap(AS_PROGRAM, &riscpc_state::riscpc_map);

	ARM7500FE_IOMD(config, m_iomd, cpuxtal);
	base_config(config);
}

void riscpc_state::a7000(machine_config &config)
{
	constexpr XTAL cpuxtal(32'000'000);

	ARM7500(config, m_maincpu, cpuxtal);
	m_maincpu->set_addrmap(AS_PROGRAM, &riscpc_state::a7000_map);

	ARM7500FE_IOMD(config, m_iomd, cpuxtal);
	base_config(config);
}

void riscpc_state::a7000p(machine_config &config)
{
	constexpr XTAL cpuxtal(48'000'000);

	ARM7500(config, m_maincpu, cpuxtal); // really ARM7500FE
	m_maincpu->set_addrmap(AS_PROGRAM, &riscpc_state::a7000_map);

	ARM7500FE_IOMD(config, m_iomd, cpuxtal);
	base_config(config);
}

void riscpc_state::sarpc(machine_config &config)
{
	// TODO: ranges from 160 to 233 MHz
	constexpr XTAL cpuxtal(200'000'000);

	SA1110(config, m_maincpu, cpuxtal); // StrongARM
	m_maincpu->set_addrmap(AS_PROGRAM, &riscpc_state::riscpc_map);

	ARM7500FE_IOMD(config, m_iomd, cpuxtal);
	base_config(config);
}

void riscpc_state::sarpc_j233(machine_config &config)
{
	// TODO: 233 MHz, unsupported by xtal module
	constexpr XTAL cpuxtal(200'000'000);

	SA1110(config, m_maincpu, cpuxtal); // StrongARM
	m_maincpu->set_addrmap(AS_PROGRAM, &riscpc_state::riscpc_map);

	ARM7500FE_IOMD(config, m_iomd, cpuxtal);
	base_config(config);
}

ROM_START(rpc600)
	ROM_REGION32_LE( 0x800000, "user1", ROMREGION_ERASEFF )
	// Version 3.50
	ROM_SYSTEM_BIOS( 0, "350", "RiscOS 3.50" )
	ROMX_LOAD("0277,521-01.bin", 0x000000, 0x100000, CRC(8ba4444e) SHA1(1b31d7a6e924bef0e0056c3a00a3fed95e55b175), ROM_BIOS(0))
	ROMX_LOAD("0277,522-01.bin", 0x100000, 0x100000, CRC(2bc95c9f) SHA1(f8c6e2a1deb4fda48aac2e9fa21b9e01955331cf), ROM_BIOS(0))
ROM_END

ROM_START(rpc700)
	ROM_REGION32_LE( 0x800000, "user1", ROMREGION_ERASEFF )
	// Version 3.60
	ROM_SYSTEM_BIOS( 0, "360", "RiscOS 3.60" )
	ROMX_LOAD("1203,101-01.bin", 0x000000, 0x200000, CRC(2eeded56) SHA1(7217f942cdac55033b9a8eec4a89faa2dd63cd68), ROM_GROUPWORD | ROM_SKIP(2) | ROM_BIOS(0))
	ROMX_LOAD("1203,102-01.bin", 0x000002, 0x200000, CRC(6db87d21) SHA1(428403ed31682041f1e3d114ea02a688d24b7d94), ROM_GROUPWORD | ROM_SKIP(2) | ROM_BIOS(0))
ROM_END

ROM_START(a7000)
	ROM_REGION32_LE( 0x800000, "user1", ROMREGION_ERASEFF )
	// Version 3.60
	ROM_SYSTEM_BIOS( 0, "360", "RiscOS 3.60" )
	ROMX_LOAD("1203,101-01.bin", 0x000000, 0x200000, CRC(2eeded56) SHA1(7217f942cdac55033b9a8eec4a89faa2dd63cd68), ROM_GROUPWORD | ROM_SKIP(2) | ROM_BIOS(0))
	ROMX_LOAD("1203,102-01.bin", 0x000002, 0x200000, CRC(6db87d21) SHA1(428403ed31682041f1e3d114ea02a688d24b7d94), ROM_GROUPWORD | ROM_SKIP(2) | ROM_BIOS(0))
ROM_END

ROM_START(a7000p)
	ROM_REGION32_LE( 0x800000, "user1", ROMREGION_ERASEFF )
	// Version 3.71
	ROM_SYSTEM_BIOS( 0, "371", "RiscOS 3.71" )
	ROMX_LOAD("1203,261-01.bin", 0x000000, 0x200000, CRC(8e3c570a) SHA1(ffccb52fa8e165d3f64545caae1c349c604386e9), ROM_GROUPWORD | ROM_SKIP(2) | ROM_BIOS(0))
	ROMX_LOAD("1203,262-01.bin", 0x000002, 0x200000, CRC(cf4615b4) SHA1(c340f29aeda3557ebd34419fcb28559fc9b620f8), ROM_GROUPWORD | ROM_SKIP(2) | ROM_BIOS(0))
	// Version 4.02
	ROM_SYSTEM_BIOS( 1, "402", "RiscOS 4.02" )
	ROMX_LOAD("riscos402_1.bin", 0x000000, 0x200000, CRC(4c32f7e2) SHA1(d290e29a4de7be9eb36cbafbb2dc99b1c4ce7f72), ROM_GROUPWORD | ROM_SKIP(2) | ROM_BIOS(1))
	ROMX_LOAD("riscos402_2.bin", 0x000002, 0x200000, CRC(7292b790) SHA1(67f999c1ccf5419e0a142b7e07f809e13dfed425), ROM_GROUPWORD | ROM_SKIP(2) | ROM_BIOS(1))
	// Version 4.39
	ROM_SYSTEM_BIOS( 2, "439", "RiscOS 4.39" )
	ROMX_LOAD("riscos439_1.bin", 0x000000, 0x200000, CRC(dab94cb8) SHA1(a81fb7f1a8117f85e82764675445092d769aa9af), ROM_GROUPWORD | ROM_SKIP(2) | ROM_BIOS(2))
	ROMX_LOAD("riscos439_2.bin", 0x000002, 0x200000, CRC(22e6a5d4) SHA1(b73b73c87824045130840a19ce16fa12e388c039), ROM_GROUPWORD | ROM_SKIP(2) | ROM_BIOS(2))
ROM_END

ROM_START(sarpc)
	ROM_REGION32_LE( 0x800000, "user1", ROMREGION_ERASEFF )
	// Version 3.70
	ROM_SYSTEM_BIOS( 0, "370", "RiscOS 3.70" )
	ROMX_LOAD("1203,191-01.bin", 0x000000, 0x200000, NO_DUMP, ROM_GROUPWORD | ROM_SKIP(2) | ROM_BIOS(0))
	ROMX_LOAD("1203,192-01.bin", 0x000002, 0x200000, NO_DUMP, ROM_GROUPWORD | ROM_SKIP(2) | ROM_BIOS(0))
ROM_END

ROM_START(sarpc_j233)
	ROM_REGION32_LE( 0x800000, "user1", ROMREGION_ERASEFF )
	// Version 3.71
	ROM_SYSTEM_BIOS( 0, "371", "RiscOS 3.71" )
	ROMX_LOAD("1203,261-01.bin", 0x000000, 0x200000, CRC(8e3c570a) SHA1(ffccb52fa8e165d3f64545caae1c349c604386e9), ROM_GROUPWORD | ROM_SKIP(2) | ROM_BIOS(0))
	ROMX_LOAD("1203,262-01.bin", 0x000002, 0x200000, CRC(cf4615b4) SHA1(c340f29aeda3557ebd34419fcb28559fc9b620f8), ROM_GROUPWORD | ROM_SKIP(2) | ROM_BIOS(0))
ROM_END

} // anonymous namespace


/***************************************************************************

  Game driver(s)

***************************************************************************/

/*    YEAR  NAME        PARENT  COMPAT  MACHINE     INPUT  CLASS         INIT        COMPANY  FULLNAME                  FLAGS */
COMP( 1994, rpc600,     0,      0,      rpc600,     a7000, riscpc_state, empty_init, "Acorn", "Risc PC 600",            MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1994, rpc700,     rpc600, 0,      rpc700,     a7000, riscpc_state, empty_init, "Acorn", "Risc PC 700",            MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1995, a7000,      rpc600, 0,      a7000,      a7000, riscpc_state, empty_init, "Acorn", "Archimedes A7000",       MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1997, a7000p,     rpc600, 0,      a7000p,     a7000, riscpc_state, empty_init, "Acorn", "Archimedes A7000+",      MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1997, sarpc,      rpc600, 0,      sarpc,      a7000, riscpc_state, empty_init, "Acorn", "StrongARM Risc PC",      MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1997, sarpc_j233, rpc600, 0,      sarpc_j233, a7000, riscpc_state, empty_init, "Acorn", "J233 StrongARM Risc PC", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



rm380z.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol,Gabriele D'Antona
/*

Research Machines 380Z (aka "RML 380Z" or "RM 380Z")
Microcomputer produced by Research Machines Limited, Oxford, UK
1978-1985
MESS driver by Wilbert Pol and friol (dantonag (at) gmail.com)
Driver started on 22/12/2011

Stefano Bodrato, 09/12/2016 - skeleton for cassette support
True tape samples are needed to continue !

Robin Sergeant, 2024 - h/w scrolling, HRG, sound, 8" disk support
and various bug fixes.

===

From the Firmware Manual:

List of known firmware revisions by sign-on message:
*** RM380Z ***
COS 3.0/C
COS 3.0/F
COS 3.0/M
COS 3.4C/C
COS 3.4C/F
COS 3.4C/M
COS 3.4D/C
COS 3.4D/M
COS 3.4E/F
COS 4.0/F
COS 4.0A/F
COS 4.0A/M
COS 4.0B/M
COS 4.2 A

/C = Cassette
/F = 8 inch single density floppy
/M = 5.25 inch single density floppy
Ver 4.2 can have either floppy type

Monitor commands:
B - Boot CP/M  (COS /F, COS /M)
X - Boot CP/M from another drive (as above)
L - Load program from cassette (COS /C)
D - Dump memory to cassette (as above)
C - Continue program at restart address (as above)
J - Go to address
O - Select printer option (and cassette speed for COS /C)
M - Enable HRG board as memory (COS 3.4, COS 4.0)
W - Select 40 or 80 characters per line (All 80-column machines)
Ctrl+F - Enter Front Panel (=the debugger)
Ctrl+T - Enter Typewriter mode
Ctrl+S - Autopaging on
Ctrl+Q - Autopaging off
Ctrl+A - Toggle autopaging

Graphics characters: These are low-res (2x3 TRS80-style) from 80-BF, repeated at C0-FF.
80-BF will be low-intensity.
The characters 00-1F have one set for COS 3.4 and COS 4.0, and a different set for the others.
COS 4.0 and 4.2 allow one to redefine the 80-FF character range, and to have attributes.
ROS 2.2 allows an alternate character set.

Sound:
RM380Z has a connector for a speaker

===

Memory map from service manual:

PAGE SEL bit in PORT0 set to 0:

  0000-3BFF - CPU RAM row 1 (15KB!)
  3C00-7BFF - CPU RAM row 2 (16KB)
  7C00-BBFF - Add-on RAM row 1 or HRG RAM (16KB)
  BC00-DFFF - Add-on RAM row 2 (9KB!)
  E000-EFFF - ROM (COS)
  F000-F5FF - VDU and HRG Video RAM
  F600-F9FF - ROM (monitor extension)
  FA00-FAFF - Reserved, RAM?
  FB00-FBFF - Memory-mapped ports (FBFC-FBFF)
  FC00-FFFF - RAM

PAGE SEL bit in PORT0 set to 1:
  0000-0FFF - ROM (COS mirror from E000)
  1B00-1BFF - Memory-mapped ports (1BFC-1BFF)
  1C00-1DFF - ROM
  4000-7FFF - CPU RAM row 1 (16KB!, this RAM normally appears at 0000)
  8000-BFFF - ???
  C000-DFFF - ???
  E000-EFFF - ROM (COS)
  F000-F5FF - VDU and HRG Video RAM
  F600-F9FF - ROM (monitor extension)
  FA00-FAFF - Reserved, RAM?
  FB00-FBFF - Memory-mapped ports (FBFC-FBFF)
  FC00-FFFF - RAM

Video resolution:
80x24 - 8 pixels wide, 10 pixels high = 640x240
Video input clock is 16MHz

According to the manuals, VDU-1 chargen is Texas 74LS262.

===

Notes on COS 4.0 disassembly:

- routine at 0xe438 is called at startup in COS 4.0 and it sets the RST vectors in RAM
- routine at 0xe487 finds "top" of system RAM and stores it in 0x0006 and 0x000E
- 0xeca0 - outputs a string (null terminated) to screen (?)
- 0xff18 - does char output to screen (char in A?)

===

TODO:

- Make cassette interface work.


Attempt to register save state entry after state registration is closed!
Module timer tag static_vblank_timer name m_param
Attempt to register save state entry after state registration is closed!
Module timer tag static_vblank_timer name m_enabled
Attempt to register save state entry after state registration is closed!
Module timer tag static_vblank_timer name m_period.attoseconds
Attempt to register save state entry after state registration is closed!
Module timer tag static_vblank_timer name m_period.seconds
Attempt to register save state entry after state registration is closed!
Module timer tag static_vblank_timer name m_start.attoseconds
Attempt to register save state entry after state registration is closed!
Module timer tag static_vblank_timer name m_start.seconds
Attempt to register save state entry after state registration is closed!
Module timer tag static_vblank_timer name m_expire.attoseconds
Attempt to register save state entry after state registration is closed!
Module timer tag static_vblank_timer name m_expire.seconds
':maincpu' (E48B): unmapped program memory write to E000 = C1 & FF
':maincpu' (E48E): unmapped program memory write to E000 = 3E & FF

*/


#include "emu.h"
#include "rm380z.h"
#include "speaker.h"

#include "screen.h"


void rm380z_state::rm380z_mem(address_map &map)
{
	map(0xe000, 0xefff).rom().region(RM380Z_MAINCPU_TAG, 0);
	map(0xf000, 0xf5ff).rw(FUNC(rm380z_state::videoram_read), FUNC(rm380z_state::videoram_write));
	map(0xf600, 0xf9ff).rom().region(RM380Z_MAINCPU_TAG, 0x1000);     /* Extra ROM space for COS4.0 */
	map(0xfa00, 0xfaff).ram();
	map(0xfb00, 0xfbff).rw(FUNC(rm380z_state::port_read), FUNC(rm380z_state::port_write));
	map(0xfc00, 0xffff).ram().share("hiram");
}

void rm380z_state::rm380z_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0xbf).rw(FUNC(rm380z_state::rm380z_portlow_r), FUNC(rm380z_state::rm380z_portlow_w));
	map(0xc0, 0xc3).mirror(0x20).rw(m_fdc, FUNC(fd1771_device::read), FUNC(fd1771_device::write));
	map(0xc4, 0xc7).mirror(0x20).w(FUNC(rm380z_state::disk_0_control));
	map(0xe8, 0xff).rw(FUNC(rm380z_state::rm380z_porthi_r), FUNC(rm380z_state::rm380z_porthi_w));
}

INPUT_PORTS_START( rm380z )

//  PORT_START("additional_chars")
//  PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Escape") PORT_CODE(KEYCODE_ESC) PORT_CODE(KEYCODE_ESC)

INPUT_PORTS_END

INPUT_PORTS_START( rm380zhrg )

	PORT_START("display_type")
	PORT_CONFNAME( 0x01, 0x00, "Monitor" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(rm380z_state_cos40_hrg::monitor_changed), 0)
	PORT_CONFSETTING( 0x00, "Colour Monitor" )
	PORT_CONFSETTING( 0x01, "Monochrome b/w Monitor" )

INPUT_PORTS_END

//
//
//

static void rm380z_floppies(device_slot_interface &device)
{
	device.option_add("mds", FLOPPY_525_SD);
	device.option_add("fds", FLOPPY_8_DSSD);
}

uint32_t rm380z_state::screen_update_rm380z(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	// blank screen
	bitmap.fill(0);

	update_screen(bitmap);

	return 0;
}

void rm380z_state::base_configure(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &rm380z_state::rm380z_mem);
	m_maincpu->set_addrmap(AS_IO, &rm380z_state::rm380z_io);

	/* video hardware */
	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_screen_update(FUNC(rm380z_state::screen_update_rm380z));
	m_screen->set_palette(m_palette);

	SPEAKER(config, "mono").front_center();

	/* RAM configurations */
	RAM(config, RAM_TAG).set_default_size("56K");

	/* floppy disk */
	FD1771(config, m_fdc, 16_MHz_XTAL / 16);
	FLOPPY_CONNECTOR(config, m_floppy0, rm380z_floppies, "mds", floppy_image_device::default_mfm_floppy_formats).set_fixed(true);
	FLOPPY_CONNECTOR(config, m_floppy1, rm380z_floppies, "mds", floppy_image_device::default_mfm_floppy_formats).set_fixed(true);

	/* keyboard */
	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(rm380z_state::keyboard_put));
}

void rm380z_state::fds_configure()
{
	// FDS drives require a 2 MHz square wave clock frequency
	m_fdc->set_unscaled_clock(16_MHz_XTAL / 8);
	// change media type for floppy connectors
	m_floppy0->set_default_option("fds");
	m_floppy1->set_default_option("fds");
}

void rm380z_state_cos34::rm380z34e(machine_config &config)
{
	base_configure(config);

	/* cassette */
	CASSETTE(config, m_cassette);
//  m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	m_screen->set_raw(8_MHz_XTAL, 512, 0, 320, 312, 0, 240);

	SN74S262(config, m_rocg, 0);
	m_rocg->set_palette(m_palette);
}

void rm380z_state_cos40::rm380z(machine_config &config)
{
	base_configure(config);

	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.80);

	m_screen->set_raw(16_MHz_XTAL, 1024, 0, 640, 312, 0, 240);
}

void rm380z_state_cos40_hrg::rm380zhrg(machine_config &config)
{
	rm380z(config);

	m_palette->set_init(FUNC(rm380z_state_cos40_hrg::palette_init)).set_entries(19);
}

/* ROM definitions */

ROM_START( rm380z34d ) // COS 3.4D/F
	ROM_REGION( 0x10000, RM380Z_MAINCPU_TAG, ROMREGION_ERASEFF )
	ROM_LOAD( "cos34d-f.bin", 0x0000, 0x1000, CRC(eb128b40) SHA1(c46f358fb76459987e41750d052995563f2f7d53))
ROM_END

ROM_START( rm380z34e ) // COS 3.4E/M
	ROM_REGION( 0x10000, RM380Z_MAINCPU_TAG, ROMREGION_ERASEFF )
	ROM_LOAD( "cos34e-m.bin", 0x0000, 0x1000, CRC(20e2ddf4) SHA1(3177b28793d5a348c94fd0ae6393d74e2e9a8662))
ROM_END

ROM_START( rm380z ) // COS 4.0B/M
	ROM_REGION( 0x10000, RM380Z_MAINCPU_TAG, 0 )
	ROM_LOAD( "cos40b-m.bin",           0x0000, 0x1000, CRC(1f0b3a5c) SHA1(0b29cb2a3b7eaa3770b34f08c4fd42844f42700f) )
	ROM_LOAD( "cos40b-m_f600-f9ff.bin", 0x1000, 0x0400, CRC(e3397d9d) SHA1(490a0c834b0da392daf782edc7d51ca8f0668b1a) )
	ROM_LOAD( "cos40b-m_1c00-1dff.bin", 0x1400, 0x0200, CRC(0f759f44) SHA1(9689c1c1faa62c56def999cbedbbb0c8d928dcff) )
	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "c-gen-22.bin",           0x0000, 0x0800, CRC(1b67127f) SHA1(289a919871d30c5e832d22244bcac1dcfd544baa) )
ROM_END

ROM_START( rm380zhrg ) // COS 4.0B/M
	ROM_REGION( 0x10000, RM380Z_MAINCPU_TAG, 0 )
	ROM_LOAD( "cos40b-m.bin",           0x0000, 0x1000, CRC(1f0b3a5c) SHA1(0b29cb2a3b7eaa3770b34f08c4fd42844f42700f) )
	ROM_LOAD( "cos40b-m_f600-f9ff.bin", 0x1000, 0x0400, CRC(e3397d9d) SHA1(490a0c834b0da392daf782edc7d51ca8f0668b1a) )
	ROM_LOAD( "cos40b-m_1c00-1dff.bin", 0x1400, 0x0200, CRC(0f759f44) SHA1(9689c1c1faa62c56def999cbedbbb0c8d928dcff) )
	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "c-gen-22.bin",           0x0000, 0x0800, CRC(1b67127f) SHA1(289a919871d30c5e832d22244bcac1dcfd544baa) )
ROM_END

ROM_START( rm380zf ) // COS 4.0B/F
	ROM_REGION( 0x10000, RM380Z_MAINCPU_TAG, 0 )
	ROM_LOAD( "cos40b-f.bin",           0x0000, 0x1000, CRC(c4110957) SHA1(08d924c7a152ca102585520a987051bcad3fca3f) )
	ROM_LOAD( "cos40b-f_f600-f9ff.bin", 0x1000, 0x0400, CRC(3b983326) SHA1(4a5273ca196cb98f9bb262f3e8f13bf22c9ca11c) )
	ROM_LOAD( "cos40b-f_1c00-1dff.bin", 0x1400, 0x0200, CRC(0f759f44) SHA1(9689c1c1faa62c56def999cbedbbb0c8d928dcff) )
	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "c-gen-22.bin",           0x0000, 0x0800, CRC(1b67127f) SHA1(289a919871d30c5e832d22244bcac1dcfd544baa) )
ROM_END

ROM_START( rm380zfhrg ) // COS 4.0B/F
	ROM_REGION( 0x10000, RM380Z_MAINCPU_TAG, 0 )
	ROM_LOAD( "cos40b-f.bin",           0x0000, 0x1000, CRC(c4110957) SHA1(08d924c7a152ca102585520a987051bcad3fca3f) )
	ROM_LOAD( "cos40b-f_f600-f9ff.bin", 0x1000, 0x0400, CRC(3b983326) SHA1(4a5273ca196cb98f9bb262f3e8f13bf22c9ca11c) )
	ROM_LOAD( "cos40b-f_1c00-1dff.bin", 0x1400, 0x0200, CRC(0f759f44) SHA1(9689c1c1faa62c56def999cbedbbb0c8d928dcff) )
	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "c-gen-22.bin",           0x0000, 0x0800, CRC(1b67127f) SHA1(289a919871d30c5e832d22244bcac1dcfd544baa) )
ROM_END


/* Driver */
//   YEAR  NAME        PARENT   COMPAT  MACHINE     INPUT      CLASS                   INIT                       COMPANY              FULLNAME                        FLAGS
COMP(1978, rm380z,     0,       0,      rm380z,     rm380z,    rm380z_state_cos40,     driver_device::empty_init, "Research Machines", "RM-380Z, COS 4.0B/M",          0)
COMP(1978, rm380zhrg,  rm380z,  0,      rm380zhrg,  rm380zhrg, rm380z_state_cos40_hrg, driver_device::empty_init, "Research Machines", "RM-380Z, COS 4.0B/M with HRG", 0)
COMP(1978, rm380zf,    0,       0,      rm380zf,    rm380z,    rm380z_state_cos40,     driver_device::empty_init, "Research Machines", "RM-380Z, COS 4.0B/F",          0)
COMP(1978, rm380zfhrg, rm380zf, 0,      rm380zfhrg, rm380zhrg, rm380z_state_cos40_hrg, driver_device::empty_init, "Research Machines", "RM-380Z, COS 4.0B/F with HRG", 0)
COMP(1978, rm380z34d,  rm380z,  0,      rm380z34d,  rm380z,    rm380z_state_cos34,     driver_device::empty_init, "Research Machines", "RM-380Z, COS 3.4D/F",          MACHINE_NO_SOUND_HW)
COMP(1978, rm380z34e,  rm380z,  0,      rm380z34e,  rm380z,    rm380z_state_cos34,     driver_device::empty_init, "Research Machines", "RM-380Z, COS 3.4E/M",          MACHINE_NO_SOUND_HW)



rm480z.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robin Sergeant
/*

Research Machines 480Z (aka "RML 480Z" or "Link 480Z")
Microcomputer produced by Research Machines Limited, Oxford, UK
1982-1985

===

From the Firmware Manual:

List of known firmware revisions by sign-on message:

RML 40-Character LINK 480Z V1.0
RML 80-Character LINK 480Z V1.0
RML 40-Character LINK 480Z V1.1 A
RML 40-Character LINK 480Z V1.1 B
RML 80-Character LINK 480Z V1.1 A
RML 80-Character LINK 480Z V1.1 B
RML 80-Character LINK 480Z V1.2 A
RML 80-Character LINK 480Z V1.2 B
RML 80-Character LINK 480Z V1.2 C
RML 80-Character LINK 480Z V1.2 D
RML 80-Character LINK 480Z V2.2 B

V1.0 refers to ROS 1.0 (Mk1 harwdare)
V1.1 refers to ROS 1.1
V1.2 refers to ROS 1.2
V2.2 refers to ROS 2.2 (Mk2 hardware)

The Mk1 hardware uses 4 x 8K EEPROMs, whereas Mk2 has 2 x 16K
(memory maps differ slighly due to these differences).

Monitor commands:
B - Boot CP/M  (ROS 1.2, ROS 2.2)
X - Boot CP/M from another drive (as above)
N - Boot network
T - Enter terminal mode
L - Load program from cassette
D - Dump memory to cassette
C - Continue program at restart address
J - Go to address
O - Select printer option and cassette speed
W - Select 40 or 80 characters per line (All 80-column machines)
R - Start ROM BASIC (ROS 1.1, ROS 1.2, ROS 2.2)
Ctrl+Shift+8 - Break and return to current OS
Ctrl+Shift+9 - Break and return to front panel
Ctrl+F - Enter Front Panel (=the debugger)
Ctrl+T - Enter Typewriter mode
Ctrl+S - Autopaging on
Ctrl+Q - Autopaging off
Ctrl+A - Toggle autopaging

Graphics characters: These are low-res (2x3 TRS80-style) from 80-BF, repeated at C0-FF.
80-BF will be low-intensity, except if using a colour monitor.
The characters 00-1F have one set for COS 3.4 and COS 4.0, and a different set for the others.
ROS 2.2 allows an alternate character set.

Sound:
RM480Z has the speaker fitted

===

4 different memory maps are used (see memory map functions for layout):

Page 0 is used for start-up and some ROS functions
Page 1 is designed for running CP/M
Page 2 is used to run BIR (Basic In Rom)
Page 3 contains only RAM (no known uses)

Up to 256K of RAM can be used with bank switching
The 64K address space is divided into 4 banks, each of which can be configured to
address a particular 16K chunk of physical RAM.

Page selection and bank switching is performed by writes to control ports.

Video resolution:
80x24 - 8 pixels wide, 10 pixels high = 640x240
Video input clock is 16MHz

Additional HRG video modes:

640 x 192 (1 bit per pixel, monochrome)
320 x 192 (2 bits per pixel, colour)
160 x 96 (4 bits per pixel, colour)

HRG occupies the top portion of the screen, with 4 lines of video (text) output below.
The video (text) display is drawn over any HRG output (text can overlay graphics).

===

Interrupt driven keyboard, with repeat key (non ASCII scan codes).

No built in FDC, but external floppy drives can be connected via RS232 port.

Could also network boot if connected to a CHAIN LAN, where a 380Z would act as a file server.

Programs could also be loaded from Cassette and rompacks.

===

TODO:

- Add cassette support
- Add rompack support (parallel port EEPROM cartridges)
- Add support for option hardware (if software supporting it is available).
- Save states

*/

#include "emu.h"
#include "rm480z.h"
#include "rm_mq2.h"
#include "machine/clock.h"
#include "speaker.h"
#include "screen.h"
#include "utf8.h"

void rm480z_state::rm480z_MK1_mem(address_map &map)
{
	map(0x0000, 0x3fff).bankrw(m_bank[0]);
	map(0x4000, 0x7fff).bankrw(m_bank[1]);
	map(0x8000, 0xbfff).bankrw(m_bank[2]);
	map(0xc000, 0xffff).bankrw(m_bank[3]);

	map(0x0000, 0xf7ff).view(m_view);
	// page 0 (for start-up)
	m_view[0](0x0000, 0x07ff).rom().region("ros", 0x0000);
	m_view[0](0x0800, 0x17ff).rom().region("bir0", 0x0800);
	m_view[0](0x1800, 0x1fff).rom().region("bir1", 0x1800);
	m_view[0](0x3800, 0x3fff).rom().region("ros", 0x1800);
	m_view[0](0xe800, 0xf7ff).rom().region("ros", 0x0800);
	// page 1 (for running CP/M)
	m_view[1](0xe800, 0xf7ff).rom().region("ros", 0x0800);
	//page 2 (for running BIR)
	m_view[2](0x9800, 0x9fff).rom().region("bir1", 0x1800);
	m_view[2](0xa000, 0xbfff).rom().region("bir2", 0x0000);
	m_view[2](0xc000, 0xd7ff).rom().region("bir1", 0x0000);
	m_view[2](0xd800, 0xdfff).rom().region("bir0", 0x1800);
	m_view[2](0xe000, 0xe7ff).rom().region("bir0", 0x0000);
	m_view[2](0xe800, 0xf7ff).rom().region("ros", 0x0800);
}

void rm480z_state::rm480z_MK2_mem(address_map &map)
{
	map(0x0000, 0x3fff).bankrw(m_bank[0]);
	map(0x4000, 0x7fff).bankrw(m_bank[1]);
	map(0x8000, 0xbfff).bankrw(m_bank[2]);
	map(0xc000, 0xffff).bankrw(m_bank[3]);

	map(0x0000, 0xf7ff).view(m_view);
	// page 0 (for start-up)
	m_view[0](0x0000, 0x07ff).rom().region("rom0", 0x0000);
	m_view[0](0x0800, 0x1fff).rom().region("rom1", 0x0800);
	m_view[0](0x7800, 0x7fff).rom().region("rom0", 0x3800);
	m_view[0](0xe800, 0xf7ff).rom().region("rom0", 0x2800);
	// page 1 (for running CP/M)
	m_view[1](0xe800, 0xf7ff).rom().region("rom0", 0x2800);
	//page 2 (for running BIR)
	m_view[2](0x9800, 0xbfff).rom().region("rom1", 0x1800);
	m_view[2](0xc000, 0xc7ff).rom().region("rom1", 0x0000);
	m_view[2](0xc800, 0xe7ff).rom().region("rom0", 0x0800);
	m_view[2](0xe800, 0xf7ff).rom().region("rom0", 0x2800);
}

void rm480z_state::rm480z_io(address_map &map)
{
	map(0x00, 0x17).select(0x7f00).rw(FUNC(rm480z_state::videoram_read), FUNC(rm480z_state::videoram_write));
	map(0x18, 0x1d).select(0xff00).rw(FUNC(rm480z_state::status_port_read), FUNC(rm480z_state::control_port_write));
	map(0x20, 0x23).mirror(0xff00).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x24, 0x27).mirror(0xff00).rw(m_sio, FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
	map(0x38, 0x3b).mirror(0xff00).rw(FUNC(rm480z_state::hrg_port_read), FUNC(rm480z_state::hrg_port_write));
	//map(0x28, 0x29).mirror(0xff02); // am9511/am9512 maths chip // option
	//map(0x2c, 0x2f).mirror(0xff00); // z80ctc IEEE int, Maths int, RTC, RTC // option
	//map(0x30, 0x37).mirror(0xff00); // IEEE chip // option
}

INPUT_PORTS_START( rm480z )
	PORT_START("kbrow.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("m M")             PORT_CODE(KEYCODE_M)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(", <")             PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("i I")             PORT_CODE(KEYCODE_I)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9 )")             PORT_CODE(KEYCODE_9)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("k K")             PORT_CODE(KEYCODE_K)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("l L")             PORT_CODE(KEYCODE_L)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0 _")             PORT_CODE(KEYCODE_0)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("o O")             PORT_CODE(KEYCODE_O)

	PORT_START("kbrow.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("x X")             PORT_CODE(KEYCODE_X)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("c C")             PORT_CODE(KEYCODE_C)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("w W")             PORT_CODE(KEYCODE_W)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3 #")             PORT_CODE(KEYCODE_3)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("s S")             PORT_CODE(KEYCODE_S)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("d D")             PORT_CODE(KEYCODE_D)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4 $")             PORT_CODE(KEYCODE_4)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("e E")             PORT_CODE(KEYCODE_E)

	PORT_START("kbrow.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("v V")             PORT_CODE(KEYCODE_V)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("b B")             PORT_CODE(KEYCODE_B)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("r R")             PORT_CODE(KEYCODE_R)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5 %")             PORT_CODE(KEYCODE_5)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("f F")             PORT_CODE(KEYCODE_F)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("g G")             PORT_CODE(KEYCODE_G)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6 &")             PORT_CODE(KEYCODE_6)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("t T")             PORT_CODE(KEYCODE_T)

	PORT_START("kbrow.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift")           PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Repeat")          PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("[ {")             PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("\\ |")            PORT_CODE(KEYCODE_BACKSLASH) PORT_CODE(KEYCODE_BACKSLASH2)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("] }")             PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Line Feed")       PORT_CODE(KEYCODE_MENU)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Delete")          PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Carriage Return") PORT_CODE(KEYCODE_ENTER)

	PORT_START("kbrow.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Space")           PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("n N")             PORT_CODE(KEYCODE_N)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("y Y")             PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7 '")             PORT_CODE(KEYCODE_7)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("h H")             PORT_CODE(KEYCODE_H)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("j J")             PORT_CODE(KEYCODE_J)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8 (")             PORT_CODE(KEYCODE_8)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("u U")             PORT_CODE(KEYCODE_U)

	PORT_START("kbrow.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Control")         PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("z Z")             PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("q Q")             PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1 !")             PORT_CODE(KEYCODE_1)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Caps Lock")       PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("a A")             PORT_CODE(KEYCODE_A)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2 \"")            PORT_CODE(KEYCODE_2)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Escape")          PORT_CODE(KEYCODE_ESC)

	PORT_START("kbrow.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F3")              PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN)         PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT)        PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_UP)           PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F4")              PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT)         PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F2")              PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F1")              PORT_CODE(KEYCODE_F1)

	PORT_START("kbrow.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(". >")             PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/ ?")             PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("p P")             PORT_CODE(KEYCODE_P)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("- =")             PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("; +")             PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(": *")             PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("^ ~")             PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("@ `")             PORT_CODE(KEYCODE_ASTERISK)

	PORT_START("display_type")
	PORT_CONFNAME( 0x01, 0x00, "Monitor" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(rm480z_state::monitor_changed), 0)
	PORT_CONFSETTING( 0x00, "TTL RGB Colour Monitor" )
	PORT_CONFSETTING( 0x01, "Monochrome b/w Monitor" )
INPUT_PORTS_END

uint32_t rm480z_state::screen_update_rm480z(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	// blank screen
	bitmap.fill(0);

	update_screen(bitmap);

	return 0;
}

static void rm480z_default_rs232_devices(device_slot_interface &device)
{
	device.option_add("rm_mq2", RM_MQ2);
}

static const z80_daisy_config daisy_chain[] =
{
	{ "sio" },
	{ "ctc" },
	{ nullptr }
};

void rm480z_state::rm480z(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &rm480z_state::rm480z_MK2_mem);
	m_maincpu->set_addrmap(AS_IO, &rm480z_state::rm480z_io);
	m_maincpu->set_daisy_config(daisy_chain);

	Z80SIO(config, m_sio, 16_MHz_XTAL / 4);
	m_sio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_sio->out_txdb_callback().set(m_rs232, FUNC(rs232_port_device::write_txd));
	m_sio->out_dtrb_callback().set(m_rs232, FUNC(rs232_port_device::write_dtr));
	m_sio->out_rtsb_callback().set(m_rs232, FUNC(rs232_port_device::write_rts));

	// rs232 port for floppy drive
	RS232_PORT(config, m_rs232, rm480z_default_rs232_devices, "rm_mq2");
	m_rs232->rxd_handler().set(m_sio, FUNC(z80sio_device::rxb_w));
	m_rs232->dcd_handler().set(m_sio, FUNC(z80sio_device::dcdb_w));
	m_rs232->dsr_handler().set(m_sio, FUNC(z80sio_device::syncb_w));
	m_rs232->cts_handler().set(m_sio, FUNC(z80sio_device::ctsb_w));

	Z80CTC(config, m_ctc, 16_MHz_XTAL / 4);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->zc_callback<0>().set(m_sio, FUNC(z80sio_device::rxtxcb_w));

	CLOCK(config, "ctc_clock", 16_MHz_XTAL / 8).signal_handler().set(m_ctc, FUNC(z80ctc_device::trg0));

	/* video hardware */
	PALETTE(config, m_palette).set_init(FUNC(rm480z_state::palette_init)).set_entries(19);
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_screen_update(FUNC(rm480z_state::screen_update_rm480z));
	m_screen->set_palette(m_palette);
	m_screen->set_raw(16_MHz_XTAL, 1024, 0, 640, 312, 0, 240);
	m_screen->register_vblank_callback(vblank_state_delegate(&rm480z_state::vblank_callback, this));

	// keyboard is clocked by 500 kHz oscillator divided to give a 15625 Hz scan rate
	TIMER(config, "kbd_scan").configure_periodic(FUNC(rm480z_state::kbd_scan), attotime::from_hz(500_kHz_XTAL / 32));

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.80);

	/* RAM configurations */
	RAM(config, RAM_TAG).set_default_size("256K");
}

void rm480z_state::rm480za(machine_config &config)
{
	rm480z(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &rm480z_state::rm480z_MK1_mem);
}

/* ROM definitions */

ROM_START( rm480z )
	ROM_REGION( 0x4000, "rom0", 0 )
	ROM_LOAD( "fv2.0_0_12099_19.2.86.ic83", 0x0000, 0x4000, CRC(a0f02d8a) SHA1(1c063b842699dc0ad85a5a5f337f2864497f9c0f) )
	ROM_REGION( 0x4000, "rom1", 0 )
	ROM_LOAD( "fv2.0_1_12100_27.2.86.ic93", 0x0000, 0x4000, CRC(2a93ca6e) SHA1(7fdd772d4251dbf951a687d184ed787cfe21212b) )
	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "cg06_12098_28.2.86.ic98",    0x0000, 0x2000, CRC(15d40f7e) SHA1(a7266357eb9be849f77a97ff3013b236c0af8289) )
ROM_END

ROM_START( rm480za )
	ROM_REGION( 0x2000, "ros", 0 )
	ROM_DEFAULT_BIOS("1.2d")
	ROM_SYSTEM_BIOS(0, "1.2b", "ROS 1.2B")
	ROMX_LOAD( "ros_1.2b.ls",   0x0000, 0x2000, CRC(37e93287) SHA1(c96d4b7eedadb0fb8e3732b6ba3e898e123c393f), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "1.2d", "ROS 1.2D")
	ROMX_LOAD( "ros-1-2d.bin",  0x0000, 0x2000, CRC(3fe61618) SHA1(ee4d70694489ab7f123e59d73b304d6d5fcd8a81), ROM_BIOS(1) )

	ROM_REGION( 0x2000, "bir0", 0 )
	ROM_LOAD( "bir5-4-0.bin",  0x0000, 0x2000, CRC(51875c95) SHA1(96bb058512a0f21634e629229effc6b36d0f0a7a) )
	ROM_REGION( 0x2000, "bir1", 0 )
	ROM_LOAD( "bir5-4-1.bin",  0x0000, 0x2000, CRC(63959245) SHA1(2e42453ce281fd6cc2de176ff98f0a326d3ae8a8) )
	ROM_REGION( 0x2000, "bir2", 0 )
	ROM_LOAD( "bir5-4-2.bin",  0x0000, 0x2000, CRC(d3eb07cf) SHA1(9e576e8d2ae571319dc6c1cb035f13cf56abf690) )

	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "cg06.lq",        0x0000, 0x2000, BAD_DUMP CRC(15d40f7e) SHA1(a7266357eb9be849f77a97ff3013b236c0af8289) ) // chip is marked CG05, might not be the same, so marked as bad
ROM_END

/* Driver */
//   YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT                       COMPANY              FULLNAME             FLAGS
COMP(1981, rm480z,  0,      0,      rm480z,  rm480z, rm480z_state, driver_device::empty_init, "Research Machines", "LINK 480Z (set 1)", 0)
COMP(1981, rm480za, rm480z, 0,      rm480za, rm480z, rm480z_state, driver_device::empty_init, "Research Machines", "LINK 480Z (set 2)", 0)



rmnimbus.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Phill Harvey-Smith, Carl
/*
    drivers/rmnimbus.c

    Research machines Nimbus.

    2009-11-14, P.Harvey-Smith.

*/

#include "emu.h"
#include "rmnimbus.h"
#include "rmnkbd.h"

#include "cpu/mcs51/mcs51.h"
#include "imagedev/floppy.h"

#include "bus/isa/fdc.h"
#include "bus/rs232/rs232.h"
#include "bus/scsi/acb4070.h"
#include "bus/scsi/s1410.h"
#include "bus/scsi/scsihd.h"

#include "softlist.h"
#include "speaker.h"


static void rmnimbus_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
}

static void keyboard(device_slot_interface &device)
{
	device.option_add("rmnkbd", RMNIMBUS_KEYBOARD);
}

void rmnimbus_state::nimbus_mem(address_map &map)
{
	map(0x00000, 0x1FFFF).bankrw(RAM_BANK00_TAG);
	map(0x20000, 0x3FFFF).bankrw(RAM_BANK01_TAG);
	map(0x40000, 0x5FFFF).bankrw(RAM_BANK02_TAG);
	map(0x60000, 0x7FFFF).bankrw(RAM_BANK03_TAG);
	map(0x80000, 0x9FFFF).bankrw(RAM_BANK04_TAG);
	map(0xA0000, 0xBFFFF).bankrw(RAM_BANK05_TAG);
	map(0xC0000, 0xDFFFF).bankrw(RAM_BANK06_TAG);
	map(0xE0000, 0xEFFFF).bankrw(RAM_BANK07_TAG);
	map(0xF0000, 0xFFFFF).rom().region(MAINCPU_TAG, 0x0f0000);
}

void rmnimbus_state::nimbus_io(address_map &map)
{
	map(0x0000, 0x0031).rw(FUNC(rmnimbus_state::nimbus_video_io_r), FUNC(rmnimbus_state::nimbus_video_io_w));
	map(0x0080, 0x0080).rw(FUNC(rmnimbus_state::nimbus_mcu_r), FUNC(rmnimbus_state::nimbus_mcu_w));
	map(0x0092, 0x0092).rw(FUNC(rmnimbus_state::nimbus_iou_r), FUNC(rmnimbus_state::nimbus_iou_w));
	map(0x00a0, 0x00a0).r(FUNC(rmnimbus_state::nimbus_joystick_r));
	map(0x00a0, 0x00a3).w(FUNC(rmnimbus_state::nimbus_joystick_select));
	map(0x00a4, 0x00a4).rw(FUNC(rmnimbus_state::nimbus_mouse_js_r), FUNC(rmnimbus_state::nimbus_mouse_js_w));
	map(0x00c0, 0x00cf).rw(FUNC(rmnimbus_state::nimbus_pc8031_r), FUNC(rmnimbus_state::nimbus_pc8031_w)).umask16(0x00ff);
	map(0x00e0, 0x00ef).rw(AY8910_TAG, FUNC(ay8910_device::data_r), FUNC(ay8910_device::address_data_w)).umask16(0x00ff);
	map(0x00f0, 0x00f7).rw(m_z80sio, FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w)).umask16(0x00ff);
	map(0x0400, 0x0400).w(FUNC(rmnimbus_state::fdc_ctl_w));
	map(0x0408, 0x040f).rw(m_fdc, FUNC(wd2793_device::read), FUNC(wd2793_device::write)).umask16(0x00ff);
	map(0x0410, 0x041f).rw(FUNC(rmnimbus_state::scsi_r), FUNC(rmnimbus_state::scsi_w)).umask16(0x00ff);
	map(0x0480, 0x049f).m(m_via, FUNC(via6522_device::map)).umask16(0x00ff);
}


static INPUT_PORTS_START( nimbus )
	PORT_START("config")
	PORT_CONFNAME( 0x01, 0x01, "Mouse mode" )
	PORT_CONFSETTING( 0x00, "Emulate incremental encoders" )
	PORT_CONFSETTING( 0x01, "Simulate BIOS handler" )

	PORT_START(JOYSTICK0_TAG)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1)

	PORT_START(JOYSTICK1_TAG)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(2) PORT_8WAY
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2)

	PORT_START(MOUSE_BUTTON_TAG)  /* Mouse buttons */
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Mouse Button 2") PORT_CODE(MOUSECODE_BUTTON2)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Mouse Button 1") PORT_CODE(MOUSECODE_BUTTON1)

	PORT_START(MOUSEX_TAG) /* Mouse - X AXIS */
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1)

	PORT_START(MOUSEY_TAG) /* Mouse - Y AXIS */
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1)

INPUT_PORTS_END

void rmnimbus_state::nimbus_iocpu_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom();
}

void rmnimbus_state::nimbus_iocpu_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x000FF).rw(FUNC(rmnimbus_state::nimbus_pc8031_iou_r), FUNC(rmnimbus_state::nimbus_pc8031_iou_w));
}

void rmnimbus_state::nimbus(machine_config &config)
{
	/* basic machine hardware */
	I80186(config, m_maincpu, 16000000); // the cpu is a 10Mhz part but the serial clocks are wrong unless it runs at 8Mhz
	m_maincpu->set_addrmap(AS_PROGRAM, &rmnimbus_state::nimbus_mem);
	m_maincpu->set_addrmap(AS_IO, &rmnimbus_state::nimbus_io);
	m_maincpu->read_slave_ack_callback().set(FUNC(rmnimbus_state::cascade_callback));
	m_maincpu->tmrout0_handler().set(m_z80sio, FUNC(z80sio_device::rxca_w));
	m_maincpu->tmrout1_handler().set(m_z80sio, FUNC(z80sio_device::rxtxcb_w));

	I8031(config, m_iocpu, 11059200);
	m_iocpu->set_addrmap(AS_PROGRAM, &rmnimbus_state::nimbus_iocpu_mem);
	m_iocpu->set_addrmap(AS_IO, &rmnimbus_state::nimbus_iocpu_io);
	m_iocpu->port_in_cb<1>().set(FUNC(rmnimbus_state::nimbus_pc8031_port1_r));
	m_iocpu->port_out_cb<1>().set(FUNC(rmnimbus_state::nimbus_pc8031_port1_w));
	m_iocpu->port_in_cb<3>().set(FUNC(rmnimbus_state::nimbus_pc8031_port3_r));
	m_iocpu->port_out_cb<3>().set(FUNC(rmnimbus_state::nimbus_pc8031_port3_w));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(4.433619_MHz_XTAL * 2, 650, 0, 640, 260, 0, 250);
	m_screen->set_screen_update(FUNC(rmnimbus_state::screen_update_nimbus));
	//m_screen->set_video_attributes(VIDEO_UPDATE_SCANLINE);
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette).set_entries(16);

	/* Backing storage */
	WD2793(config, m_fdc, 2000000);
	m_fdc->set_force_ready(true);
	m_fdc->intrq_wr_callback().set(FUNC(rmnimbus_state::nimbus_fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(rmnimbus_state::nimbus_fdc_drq_w));
	m_fdc->enmf_rd_callback().set(FUNC(rmnimbus_state::nimbus_fdc_enmf_r));
	FLOPPY_CONNECTOR(config, FDC_TAG":0", rmnimbus_floppies, "35dd", isa8_fdc_device::floppy_formats);
	FLOPPY_CONNECTOR(config, FDC_TAG":1", rmnimbus_floppies, "35dd", isa8_fdc_device::floppy_formats);

	SCSI_PORT(config, m_scsibus);
	m_scsibus->set_data_input_buffer("scsi_data_in");
	m_scsibus->msg_handler().set(FUNC(rmnimbus_state::write_scsi_msg));
	m_scsibus->bsy_handler().set(FUNC(rmnimbus_state::write_scsi_bsy));
	m_scsibus->io_handler().set(FUNC(rmnimbus_state::write_scsi_io));
	m_scsibus->cd_handler().set(FUNC(rmnimbus_state::write_scsi_cd));
	m_scsibus->req_handler().set(FUNC(rmnimbus_state::write_scsi_req));

	m_scsibus->set_slot_device(1, "harddisk", SCSIHD, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_0));
	m_scsibus->set_slot_device(2, "harddisk", SCSIHD, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_1));
	m_scsibus->set_slot_device(3, "harddisk", ACB4070, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_2));
	m_scsibus->set_slot_device(4, "harddisk", S1410, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_3));

	OUTPUT_LATCH(config, m_scsi_data_out);
	m_scsibus->set_output_latch(*m_scsi_data_out);

	INPUT_BUFFER(config, m_scsi_data_in);

	OUTPUT_LATCH(config, m_scsi_ctrl_out);
	m_scsi_ctrl_out->bit_handler<0>().set(m_scsibus, FUNC(scsi_port_device::write_rst));
	m_scsi_ctrl_out->bit_handler<1>().set(m_scsibus, FUNC(scsi_port_device::write_sel));
	m_scsi_ctrl_out->bit_handler<2>().set(FUNC(rmnimbus_state::write_scsi_iena));

	RAM(config, m_ram);
	m_ram->set_default_size("1536K");
	m_ram->set_extra_options("128K,256K,384K,512K,640K,1024K");

	/* Peripheral chips */
	Z80SIO(config, m_z80sio, 4000000); // Z0844006PSC (SIO/0)
	m_z80sio->out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
	m_z80sio->out_dtrb_callback().set("rs232b", FUNC(rs232_port_device::write_dtr));
	m_z80sio->out_rtsb_callback().set("rs232b", FUNC(rs232_port_device::write_rts));
	m_z80sio->out_int_callback().set(FUNC(rmnimbus_state::sio_interrupt));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", keyboard, "rmnkbd"));
	rs232a.rxd_handler().set(m_z80sio, FUNC(z80sio_device::rxa_w));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_z80sio, FUNC(z80sio_device::rxb_w));
	rs232b.dcd_handler().set(m_z80sio, FUNC(z80sio_device::dcdb_w));
	rs232b.ri_handler().set(m_z80sio, FUNC(z80sio_device::syncb_w));
	rs232b.cts_handler().set(m_z80sio, FUNC(z80sio_device::ctsb_w));

	EEPROM_93C06_16BIT(config, m_eeprom);

	MOS6522(config, m_via, 1000000);
	m_via->writepa_handler().set("cent_data_out", FUNC(output_latch_device::write));
	m_via->writepb_handler().set(FUNC(rmnimbus_state::nimbus_via_write_portb));
	m_via->ca2_handler().set(m_centronics, FUNC(centronics_device::write_strobe));
	m_via->irq_handler().set(m_maincpu, FUNC(i80186_cpu_device::int3_w));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set(m_via, FUNC(via6522_device::write_ca1)).invert();

	output_latch_device &cent_data(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data);

	/* sound hardware */
	SPEAKER(config, MONO_TAG).front_center();
	ay8910_device &ay8910(AY8910(config, AY8910_TAG, 2000000));
	ay8910.port_a_write_callback().set(FUNC(rmnimbus_state::nimbus_sound_ay8910_porta_w));
	ay8910.port_b_write_callback().set(FUNC(rmnimbus_state::nimbus_sound_ay8910_portb_w));
	ay8910.add_route(ALL_OUTPUTS, MONO_TAG, 0.75);

	msm5205_device &msm5205(MSM5205(config, MSM5205_TAG, 384000));
	msm5205.vck_callback().set(FUNC(rmnimbus_state::nimbus_msm5205_vck)); /* VCK function */
	msm5205.set_prescaler_selector(msm5205_device::S48_4B);      /* 8 kHz */
	msm5205.add_route(ALL_OUTPUTS, MONO_TAG, 0.75);

	SOFTWARE_LIST(config, "disk_list").set_original("nimbus");

	m_maincpu->set_dasm_override(FUNC(rmnimbus_state::dasm_override));
}

/*
Known unavailable BIOS set:
    Another version of v1.31a labelled: SYS1 16128 31/10/86, SYS2 16129 28/10/86
    Another version of v1.32c labelled: SYS1 17130 10.03.87, SYS 2. 17131 09.03.87
    Another version of v1.32c labelled: SYS 1 21323 2/12/88, SYS 2 21324 5/1/89
    v1.33c labelled "RESEARCH MACHINE P.N. 32857(32858) PC186 SYS1(SYS2) V1.33C", no date labelled
    Another version of v1.40d labelled: 24693 IC30 02/7/91(AA), 24694 IC27 28/6/91(AA)
*/
ROM_START( nimbus )
	ROM_REGION( 0x100000, MAINCPU_TAG, 0 )

	ROM_SYSTEM_BIOS(0, "v125a", "Nimbus BIOS v1.25a (1985-12-02)")
	ROMX_LOAD("sys1-1.25a_13484_6-11-85_m5m27256p.rom", 0xf0001, 0x8000, CRC(5870df28) SHA1(12e1a7d22439d512b221c355d641d113f0e6568e), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("sys2-1.25a_13485_2-12-85_m5m27256p.rom", 0xf0000, 0x8000, CRC(15888320) SHA1(32cc2485468c6a9944e505162e319a283eef8a84), ROM_SKIP(1) | ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "v131a", "Nimbus BIOS v1.31a (1986-06-18)")
	ROMX_LOAD("sys1-1.31a-16128-1986-06-18.rom", 0xf0001, 0x8000, CRC(6416eb05) SHA1(1b640163a7efbc24381c7b24976a8609c066959b), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("sys2-1.31a-16129-1986-06-18.rom", 0xf0000, 0x8000, CRC(b224359d) SHA1(456bbe37afcd4429cca76ba2d6bd534dfda3fc9c), ROM_SKIP(1) | ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "v132c", "Nimbus BIOS v1.32c (1987-03-21)")
	ROMX_LOAD("sys1-1.32c_17130_21-3-87_m5m27256p_63210c.rom", 0xf0001, 0x8000, CRC(91b88b29) SHA1(1db5e3ff8bb237f006b53d5f91b080f99e95a58e), ROM_SKIP(1) | ROM_BIOS(2))
	ROMX_LOAD("sys2-1.32c_17131_21-3-87_m5m27256p_63210c.rom", 0xf0000, 0x8000, CRC(4876cc4f) SHA1(4348b293f5c443453c08663809bfa5c462222137), ROM_SKIP(1) | ROM_BIOS(2))

	ROM_SYSTEM_BIOS(3, "v132d", "Nimbus BIOS v1.32d (1989-01-20)")
	ROMX_LOAD("sys1-1.32d_21323_17-1-89_m5m27256p_63210c.rom", 0xf0001, 0x8000, CRC(e0ecbc02) SHA1(b5cb16df23bd30af5556660364e4733790f99164), ROM_SKIP(1) | ROM_BIOS(3))
	ROMX_LOAD("sys2-1.32d_21324_20-1-89_m5m27256p_63210c.rom", 0xf0000, 0x8000, CRC(8ef4a357) SHA1(29309cb8bfe9256d4684f3e6575e3720b0dcacd4), ROM_SKIP(1) | ROM_BIOS(3))

	ROM_SYSTEM_BIOS(4, "v132f", "Nimbus BIOS v1.32f (1989-11-29)")
	ROMX_LOAD("sys-1-1.32f-22779-1989-11-29.rom", 0xf0001, 0x8000, CRC(786c31e8) SHA1(da7f828f7f96087518bea1a3d89fee59b283b4ba), ROM_SKIP(1) | ROM_BIOS(4))
	ROMX_LOAD("sys-2-1.32f-22778-1989-11-29.rom", 0xf0000, 0x8000, CRC(0be3db64) SHA1(af806405ec6fbc20385705f90d5059a47de17b08), ROM_SKIP(1) | ROM_BIOS(4))

	ROM_SYSTEM_BIOS(5, "v140d", "Nimbus BIOS v1.40d (1990-02-19)")
	ROMX_LOAD("rt-1.40d-24694-ic27-1990-02-19.rom", 0xf0001, 0x8000, CRC(b8d3dc0b) SHA1(82e0dcdc6c7a83339af68d6cb61211fcb14bed88), ROM_SKIP(1) | ROM_BIOS(5))
	ROMX_LOAD("left-1.40d-24693-ic30-1990-02-15.rom", 0xf0000, 0x8000, CRC(b0826b0b) SHA1(3baa369a0e7ef138ca29aae0ee8a89ab670a02b9), ROM_SKIP(1) | ROM_BIOS(5))

	ROM_REGION( 0x4000, IOCPU_TAG, 0 )
	ROM_LOAD("hexec-v1.02u-13488-1985-10-29.rom", 0x0000, 0x1000, CRC(75c6adfd) SHA1(0f11e0b7386c6368d20e1fc7a6196d670f924825))

	ROM_REGION16_LE( 0x20, ER59256_TAG, 0 ) // default eeprom data
	ROM_LOAD("er59256", 0x00, 0x20, CRC(1a39de76) SHA1(0b6607f008dd92d6ab9af62b0b042fc3f5f4461c))
ROM_END

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS           INIT        COMPANY              FULLNAME  FLAGS
COMP( 1986, nimbus, 0,      0,      nimbus,  nimbus, rmnimbus_state, empty_init, "Research Machines", "Nimbus", 0)



robotadv.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
/*******************************************************************************

Novag Chess Robot Adversary, chess computer with robotic arm. The chess engine
is MyChess by David Kittinger, just like the one in Novag Savant.

Hardware notes:
- PCB label: GOODNIGHT DESIGN, PACIFIC MICROELECTRONICS GROUP, 743-279A/280A/281A
- Zilog Z8400B PS, 6 MHz XTAL
- 40KB ROM (4*2764 or equivalent, 4*MSM2716AS) + 1 socket for expansion
- 5KB RAM (8*TMM314APL-1, 2*TC5514AP-8 battery-backed)
- SN76489AN sound
- robot arm with 4 DC motors
- 12+12 leds, 8*8 magnet sensors, printer port

See patent US4398720 for a more detailed description of the hardware.

Newer versions sold in West Germany were marketed as 7.5MHz, but it's not known
if it's really an overclock, or maybe they just removed waitstates.

On the left and right of the chessboard are designated spots for captured pieces,
no magnet sensors underneath. The user is not required to place pieces there, but
the chesscomputer will give an error when it tries to take a piece from there
(eg. with trace back, review, or when it cleans up after the game has finished).

In MAME, the claw position is shown with a small dot, opaque means it's open.
After the CPU's move, wait until the claw is closed before inputting new move.

TODO:
- it becomes unresponsive when pressing certain keys right after the user lost,
  like Trace Back, possibly BTANB?
- Z80 waitstates according to patent

*******************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/clock.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/sn76496.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "novag_robotadv.lh"


namespace {

class robotadv_state : public driver_device
{
public:
	robotadv_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_board(*this, "board"),
		m_sn(*this, "sn"),
		m_inputs(*this, "IN.%u", 0),
		m_piece_hand(*this, "cpu_hand"),
		m_out_motor(*this, "motor%u", 0U),
		m_out_pos(*this, "pos_%c", unsigned('x'))
	{ }

	void robotadv(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD { refresh(); }

private:
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<sensorboard_device> m_board;
	required_device<sn76489a_device> m_sn;
	required_ioport_array<3> m_inputs;
	output_finder<> m_piece_hand;
	output_finder<6> m_out_motor;
	output_finder<2> m_out_pos;

	u8 m_control1 = 0;
	u8 m_control2 = 0;
	u8 m_latch = 0;
	u8 m_motor_on = 0;
	u8 m_motor_dir = 0;
	u8 m_limits = 0;
	s32 m_counter[4] = { };
	attotime m_pwm_accum[4];
	attotime m_pwm_last;
	emu_timer *m_refresh_timer;

	void main_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	void control1_w(u8 data);
	void control2_w(u8 data);
	void latch_w(u8 data);
	u8 limits_r();
	u8 input_r();
	u8 counters_r();

	void init_board(u8 data);
	void clear_board(u8 data);
	void refresh(s32 param = 0);
	void update_counters();
	void update_limits();
	void update_clawpos(double *x, double *y);
	void update_piece(double x, double y);
};



/*******************************************************************************
    Initialization
*******************************************************************************/

void robotadv_state::machine_start()
{
	m_refresh_timer = timer_alloc(FUNC(robotadv_state::refresh), this);

	// resolve outputs
	m_piece_hand.resolve();
	m_out_motor.resolve();
	m_out_pos.resolve();

	// register for savestates
	save_item(NAME(m_control1));
	save_item(NAME(m_control2));
	save_item(NAME(m_latch));
	save_item(NAME(m_motor_on));
	save_item(NAME(m_motor_dir));
	save_item(NAME(m_limits));
	save_item(NAME(m_counter));
	save_item(NAME(m_pwm_accum));
	save_item(NAME(m_pwm_last));
}

void robotadv_state::init_board(u8 data)
{
	m_board->preset_chess(data);

	// reposition pieces if board will be rotated
	if (data & 2)
	{
		for (int y = 0; y < 8; y++)
			for (int x = 7; x >= 0; x--)
			{
				m_board->write_piece(x + 4, y, m_board->read_piece(x, y));
				m_board->write_piece(x, y, 0);
			}
	}
}

void robotadv_state::clear_board(u8 data)
{
	m_piece_hand = 0;
	m_board->clear_board(data);
}



/*******************************************************************************
    Motor Sim
*******************************************************************************/

void robotadv_state::update_counters()
{
	attotime now = machine().time();
	attotime delta = now - m_pwm_last;
	m_pwm_last = now;

	for (int i = 0; i < 4; i++)
	{
		if (BIT(m_motor_on, i))
		{
			m_pwm_accum[i] += delta;

			// increment counters per 1us
			const u32 freq = 1'000'000;
			u64 ticks = m_pwm_accum[i].as_ticks(freq);
			m_pwm_accum[i] -= attotime::from_ticks(ticks, freq);

			if (BIT(m_motor_dir, i))
				m_counter[i] -= ticks;
			else
				m_counter[i] += ticks;
		}
	}
}

void robotadv_state::update_limits()
{
	m_limits = 0;

	// claw and forearm lever
	const u32 range[2] = { 300'000, 2'000'000 };
	for (int i = 0; i < 2; i++)
	{
		m_counter[i] %= range[i];
		if (m_counter[i] > range[i] / 2)
			m_limits |= 1 << i;
	}

	// forearm rotation
	if (m_counter[2] <= 0)
	{
		m_counter[2] = 0;
		m_limits |= 4;
	}

	// upper arm rotation
	if (m_counter[3] >= 0)
		m_limits |= 8;
}

void robotadv_state::update_clawpos(double *x, double *y)
{
	double r, d, a;

	// upper arm
	r = 5.43;
	d = m_counter[3] / 12670.0;
	a = d * (M_PI / 180.0) + M_PI;
	*x = r * cos(a);
	*y = r * sin(a);

	// elbow (home position is at a slight angle)
	r = 1.07;
	d = m_counter[2] / 14730.0 + 8.8;
	a += d * (M_PI / 180.0) + (1.5 * M_PI);
	*x += r * cos(a);
	*y += r * sin(a);

	// forearm is perpendicular to elbow
	r = 5.62;
	a += 1.5 * M_PI;
	*x += r * cos(a);
	*y += r * sin(a);
}

void robotadv_state::update_piece(double x, double y)
{
	// convert claw x/y to sensorboard x/y (1.0 = 1 square)
	int bx = -1, by = 0;

	// chessboard
	x += 4.0;
	y -= 2.1;
	if (x >= 0.0 && x < 8.0 && y >= 0.0 && y < 8.0)
	{
		bx = x;
		by = y;
	}

	// left edge
	x += 2.4;
	y += 1.0;
	if (x >= 0.0 && x < 2.0 && y >= 0.0 && y < 8.0)
	{
		bx = x + 8;
		by = y;
	}

	// right edge
	x -= 10.8;
	if (x >= 0.0 && x < 2.0 && y >= 0.0 && y < 8.0)
	{
		bx = x + 10;
		by = y;
	}

	by = 7 - by;

	if (m_limits & 1)
	{
		// open empty claw
		if (m_piece_hand == 0)
			return;

		// drop piece to invalid position (shouldn't happen)
		else if (bx == -1)
			popmessage("Invalid piece drop!");

		// collision with piece on board (user interference)
		else if (m_board->read_piece(bx, by) != 0)
			popmessage("Collision at %c%d!", bx + 'A', by + 1);
		else
		{
			m_board->write_piece(bx, by, m_piece_hand);
			m_board->refresh();
		}

		m_piece_hand = 0;
	}
	else
	{
		// close claw while forearm is raised, or at invalid position
		if (~m_limits & 2 || bx == -1)
			return;

		// pick up piece, unless it was picked up by the user
		const int pos = (by << 4 & 0xf0) | (bx & 0x0f);
		if (pos != m_board->get_handpos())
		{
			m_piece_hand = m_board->read_piece(bx, by);

			if (m_piece_hand != 0)
			{
				m_board->write_piece(bx, by, 0);
				m_board->refresh();
			}
		}
	}
}

void robotadv_state::refresh(s32 param)
{
	if (machine().side_effects_disabled())
		return;

	update_counters();

	u8 limits_prev = m_limits;
	update_limits();

	double x, y;
	update_clawpos(&x, &y);

	// claw opened or closed
	if ((m_limits ^ limits_prev) & 1)
		update_piece(x, y);

	// output claw position
	const int open = (m_limits & 1) ? 0x800 : 0; // put open state on x bit 11
	m_out_pos[0] = int((x + 15.0) * 50.0 + 0.5) | open;
	m_out_pos[1] = int((y + 15.0) * 50.0 + 0.5);

	m_refresh_timer->adjust(attotime::from_hz(60));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void robotadv_state::control1_w(u8 data)
{
	// d0 falling edge: write sound
	if (~data & m_control1 & 1)
		m_sn->write(m_latch);

	// d1: ?
	// d2,d3: chess clock peripheral?
	// d5,d7: arm motor on
	refresh();
	m_motor_on = (m_motor_on & ~0xc) | (bitswap<2>(data,7,5) << 2);

	// d4,d6: arm motor direction
	m_motor_dir = (m_motor_dir & ~0xc) | (bitswap<2>(data,6,4) << 2);

	// reverse accum if direction changed
	if ((data ^ m_control1) & 0x10)
		m_pwm_accum[2] = attotime::from_usec(1) - m_pwm_accum[2];
	if ((data ^ m_control1) & 0x40)
		m_pwm_accum[3] = attotime::from_usec(1) - m_pwm_accum[3];

	for (int i = 0; i < 4; i++)
		m_out_motor[i + 2] = BIT(data, i + 4);

	m_control1 = data;
}

void robotadv_state::control2_w(u8 data)
{
	// d0,d1: claw/lever motor on
	refresh();
	m_motor_on = (m_motor_on & ~3) | (data & 3);

	for (int i = 0; i < 2; i++)
		m_out_motor[i] = BIT(data, i);

	// d2-d4: keypad mux
	// d5-d7: led select
	m_display->write_my(data >> 5);
	m_control2 = data;
}

void robotadv_state::latch_w(u8 data)
{
	// led data, sound data, chessboard mux
	m_display->write_mx(data);
	m_latch = data;
}

u8 robotadv_state::limits_r()
{
	// d0: ?
	// d1-d4: limit switches
	// d5-d7: printer
	refresh();
	return m_limits << 1;
}

u8 robotadv_state::input_r()
{
	u8 data = 0;

	// read chessboard
	if (m_latch)
	{
		refresh();
		for (int i = 0; i < 8; i++)
		{
			if (BIT(m_latch, i))
				data |= m_board->read_file(i, true);
		}
	}

	// read keypad
	for (int i = 0; i < 3; i++)
		if (BIT(m_control2 >> 2, i))
			data |= m_inputs[i]->read();

	return ~data;
}

u8 robotadv_state::counters_r()
{
	// arm motors optical sensors to cd4029 counters
	refresh();
	const int ratio = 300; // around 1 count per 300us
	u8 c2 = (m_counter[2] / ratio) & 0xf;
	u8 c3 = (m_counter[3] / ratio) & 0xf;

	return c2 << 4 | c3;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void robotadv_state::main_map(address_map &map)
{
	map(0x0000, 0x5fff).rom();
	map(0x8000, 0xbfff).rom();
	map(0xc000, 0xcfff).ram();
	map(0xd400, 0xd7ff).ram().share("nvram");
}

void robotadv_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0xc0, 0xc0).w(FUNC(robotadv_state::control1_w));
	map(0xc1, 0xc1).w(FUNC(robotadv_state::control2_w));
	map(0xc2, 0xc2).nopw(); // printer
	map(0xc3, 0xc3).r(FUNC(robotadv_state::limits_r));
	map(0xc4, 0xc4).w(FUNC(robotadv_state::latch_w));
	map(0xc5, 0xc5).nopw(); // printer
	map(0xc6, 0xc6).r(FUNC(robotadv_state::input_r));
	map(0xc7, 0xc7).r(FUNC(robotadv_state::counters_r));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( robotadv )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Trace Forward")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Verify")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Change Color")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("Review")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("Return")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Hint")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Level")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("Go")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("Print List")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Sound")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Solve Mate")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Print Board")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("Best Move")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Promote")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Set Up")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Trace Back")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Demo Program")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("New Game")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Auto Play")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Test")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Form Size")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Classic Game")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Emotions")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Print Moves")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void robotadv_state::robotadv(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 6_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &robotadv_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &robotadv_state::io_map);

	auto &irq_clock(CLOCK(config, "irq_clock", 1200)); // approximation, from 555 timer with VR
	irq_clock.set_pulse_width(attotime::from_usec(10)); // guessed
	irq_clock.signal_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->set_size(8+4, 8);
	m_board->clear_cb().set(FUNC(robotadv_state::clear_board));
	m_board->init_cb().set(FUNC(robotadv_state::init_board));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(3, 8);
	config.set_default_layout(layout_novag_robotadv);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	SN76489A(config, m_sn, 6_MHz_XTAL / 2).add_route(ALL_OUTPUTS, "speaker", 1.0);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( robotadv )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("mk37029n-5_brown.u5", 0x0000, 0x2000, CRC(04f8524c) SHA1(644a35ab53aaf641af799fab2ebc870a7f4d4535) ) // MK37000-5
	ROM_LOAD("mk37030n-5_white.u6", 0x2000, 0x2000, CRC(d6db4cfb) SHA1(54ab3eee2cbc9604e793116144c129f372e4144e) ) // "
	ROM_LOAD("mk37031n-5_blue.u8",  0x4000, 0x2000, CRC(ae1ead57) SHA1(13fcbd751efb478f0c4f08611388eaae60bba8bf) ) // "
	ROM_LOAD("orange.u10",          0xa000, 0x2000, CRC(a90a2576) SHA1(9f91ca21477de3ebc668d3ec3a08842c5d19c5ec) ) // HN482764G

	ROM_LOAD("u1",         0x8000, 0x0800, CRC(db6b2598) SHA1(1315c831d3a745a171fddc7ecb7a2d23d9acf4e8) ) // MSM2716AS (u1 has no label, confirmed with several pcbs)
	ROM_LOAD("robep_2.u2", 0x8800, 0x0800, CRC(100d8a59) SHA1(b60656eee18f861b0bafedea8242afd319a8e9e3) ) // "
	ROM_LOAD("robep_3.u3", 0x9000, 0x0800, CRC(1a0067db) SHA1(73527246847e14527b7fba0ef19aaa46650d15da) ) // "
	ROM_LOAD("robep_4.u4", 0x9800, 0x0800, CRC(30dba023) SHA1(03a133ea454ee2f60890a51a77be57eadd9af9dd) ) // "
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1982, robotadv, 0,      0,      robotadv, robotadv, robotadv_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Chess Robot Adversary", MACHINE_SUPPORTS_SAVE | MACHINE_MECHANICAL | MACHINE_IMPERFECT_CONTROLS )



roland_cm32p.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Valley Bell
/*************************************************************************************************

    Roland CM-32P driver

    Driver by Valley Bell

    Thanks to KitsuWhooa for the PCB layout diagram and part list.


Notes:
- When booting, it does a basic check by writing values 0..0xFF to 0x108A.
  It then expects (value & 0x03) to be read back from 0x1080 and (value) to be read back from 0x1081/0x1082.
  The routine for doing the check begins at 0x4686.
  Succeeding means it ends up at 0x4801.
- When in test mode, the firmware gets stuck in the loop at 0xBB06, waiting for Interrupt #5 (calls 0x4014) to fire.
  It gets there by calling function 0xBBBB, which writes text (address w@0x40, num characters b@0x43) to the LCD screen.
  This can be worked around by setting b@BB2D = 03.
- The firmware gets also stuck in the loop at 0x7D70, waiting for Interrupt #8 (calls 0x4020).
  This might be related to finding the best free voice?
  You can exit the loop by setting b@7D80 = 00.
- Test Mode shows the results of 4 checks:
    1. SRAM check
    2. MIDI IN/OUT check
    3. PCM ROM and card check
    4. RCC-CPU check
  Errors in check 2 and 3 are "not abnormal".
  In order to make check 2 pass successfully, you need to connect MIDI Out with MIDI In, creating a loopback.
  Check 3 requires the SN-U110-04 PCM card ("Electric Grand & Clavi") to be inserted in order to succeed.
- In order to access the built-in PCM ROM (IC18), the CPU asks the sound chip to read offsets 0x000000 .. 0x07FFFF.
  The PCM card is accessed via offsets 0x080000 .. 0x0FFFFF.
  The additional PCM ROMs are mapped to 0x100000 .. 0x17FFFF (IC19) and 0x200000 .. 0x27FFFF according to the sample table in IC18.
- The sound chip has 32 voices. Voice 0 is reserved by the firmware for reading data from the PCM ROM.
  The firmware allocates voices from back to front, i.e. voice 31 first.
- The "PCM sound chip" itself doesn't seem to support panning.
  Maybe it just has 6 outputs (one for each part, like the U-110) and an additional DSP does the rest.


TODO:
- figure out how "freeing a voice" works - right now the firmware gets stuck when playing the 32th note.


PCB Layout
----------

PCM BOARD CM-32P ASSY 79554310 00

|   OUTPUT  | PHONES | INPUT (MT-32) | MIDI IN | MIDI THROUGH |  DC 9V |
|----------------------------------------------------------------------|
|  JK2   JK4   JK3   JKLI    JKRI CN4 JK1I         JK1T          JK5   |
|                                                                      |
|                                                                  SW1 |
|                   ||                  IC2    IC1                     |
|    IC35 IC33 IC34 ||  IC31 IC30                              CN0     |
| CN3               ||<--IC32                                          |
|                                       |----|  |----|    |----|       |
|                                       |    |  |    |    |    |       |
|    IC22  ||  IC24                     |IC10|  |IC9 |    |    |       |
|          ||       IC25      IC4       |    |  |    |    |    |       |
|          ||<--IC23                    |SRAM|  |ROM |    |    |       |
|                                       |----|  |----|    |IC3 |   X1  |
| |----|                                                  |    |       |
| | IC |         |------|                                 |    |       |
| | 17 |         | IC16 |                     IC12        |    |       |
| |----|         |------|                 X2              |----|       |
|                                |------|     IC13                     |
|    IC6  |----||----||----|     | IC15 |                              |
| |-||-|  |    ||    ||    |     |------|     IC14      |------|       |
| | || |  |IC20||IC19||IC18|                            | IC8  |       |
| |-||-|  |    ||    ||    |                            |------|       |
| IC7     |SMPL||SMPL||SMPL|   |------------------|                    |
|         |ROM ||ROM ||ROM |   |        CN5       |                    |
|         |----||----||----|   |                  |       CN1          |
|------------------------------|------------------|--------------------|
                               |                  |

Parts:

(All parts THT unless otherwise noted.)
|-----------------------------------------------------------------------|
| JK1I, JK1T                   | DIN 5                                  |
| JK2, JK4, JKRI, JKLI         | 1/4 Mono Jack                          |
| JK3                          | 1/4 Stereo Jack                        |
| JK5                          | DC Barrel Jack (Centre Negative)       |
| SW1                          | SW PCB DPDT                            |
| IC1                          | 74HC04AP                               |
| IC2                          | TLP552                                 |
| IC3                          | P8098 (CPU)                            |
| IC4                          | M51953AL                               |
| IC6, IC7                     | M5M4464AP-12                           |
| IC8                          | Roland R15239111 / M60012-0141FP (QFP) |
| IC9                          | AM27C512-150DC                         |
| IC10                         | TC5564APL-15                           |
| IC12                         | 74HC00AP                               |
| IC13                         | 74HC02AP                               |
| IC14                         | 74HC32AP                               |
| IC15                         | Roland R15229894 / MB87419 (QFP)       |
| IC16                         | Roland R15229895 / MB87420 (QFP)       |
| IC17                         | Roland P15239126 / 23140-007 (QFP)     |
| IC18                         | Roland R15179970 / MB834000A-20 3B1    |
| IC19                         | Roland R15179971 / MB834000A-20 3B2    |
| IC20                         | Roland R15179972 / HN62304BPE98        |
| IC22                         | PCM56P                                 |
| IC23                         | M5238L                                 |
| IC24                         | HD14052BP                              |
| IC25                         | NJM2082D                               |
| IC30, IC31, IC33, IC34, IC35 | NJM4565DD                              |
| IC32                         | M5207L01                               |
| CN0                          | 7805A                                  |
| CN1                          | LED PCB                                |
| CN3                          | Volume Control PCB                     |
| CN4                          | unpopulated                            |
| CN5                          | PCM Card Slot                          |
| X1                           | Crystal 12MHz                          |
| X2                           | Crystal 32.768MHz                      |
|-----------------------------------------------------------------------|


PCM ROM address/data line scrambling:

     address              data
external internal   external internal
   A0 - - -  A0        D0 - - - D2
   A1 - - -  A5        D1 - - - D7
   A2 - - -  A4        D2 - - - D6
   A3 - - -  A6        D3 - - - D4
   A4 - - -  A1        D4 - - - D1
   A5 - - -  A2        D5 - - - D3
   A6 - - -  A3        D6 - - - D0
   A7 - - -  A8        D7 - - - D5
   A8 - - - A10
   A9 - - - A13
  A10 - - -  A9
  A11 - - -  A7
  A12 - - - A11
  A13 - - - A12
  A14 - - - A16
  A15 - - - A14
  A16 - - - A15
  A17 - - - A17
  A18 - - - A18

Line scramling of A0..A6 and D0..D7 matches the SN-U110 cards.
For A7..A16 they changed the scrambling.


PCM ROM Tables
--------------
Sample Table (address: 0x00100)
------------
Pos Len Description
00  02  start offset, bits 0-15 (Little Endian)
02  01  start offset, bits 16-18 (bits 0-2)
        PCM card select (bit 3): set for sounds on PCM cards
        ROM bank (bits 4-5):
            0 = IC18
            1 = IC19
            2 = IC20
        loop mode (bits 6-7):
            0 = normal loop
            1 = no loop
            2 = ping-pong (forwards, backwards, forwards, backwards, ...)
03  02  last sample (sample length = last sample + 1)
05  02  loop length (loop start = sample length - loop length)
07  01  ??
08  01  reference note (when played back at 32000 Hz)
09  01  ??
-> 0Ah bytes per entry

Tone List (address: 0x01000)
---------
Pos Len Description
00  0A  sample name
0A  01  tone type
            00 - single
            01 - dual
            02 - detune
            03 - velocity mix
            04 - velocity switch
            05..07 - invalid
            08..0F/10..17/.../78..7F - same as 00..07
            80..FF - rhythm?
0B  01  ??
0C  02  ??
0E  01  ??
0F  01  ??
10  0B  some note IDs (padded with FF)
1B  0C  sample IDs (always one more than number of note IDs, padded with FF)
27  09  ??
30  0B  some note IDs (padded with FF)
3B  0C  sample IDs (always one more than number of note IDs, padded with FF)
47  09  ??
-> 50h bytes per entry

Note: Section 30..4F is only used with tone types 01, 03, 04


CM-32P Firmware Work RAM Layout
-------------------------------
2100..22FF - MIDI data receive buffer

2344..23C1 - Part 1..6 "Patch temporary area" (see manual page 21, 0x15 bytes per partial)
23C4..23D4 - "System area" settings (see manual page 22, master volume, reverb setting, channel assignments)
  23CE-23D3 - Part 1..6 MIDI channel
23D6..25B5 - Part 1..6 instrument data (0x50 bytes per partial, from PCM ROM at 0x1000/0x1050/0x10A0/...)
25B8..2B57 - Part 1..6 sample table data (0xF0 bytes per partial, from PCM ROM at 0x0100/0x010A/0x0114/...)

34DC..34E1 - Part 1..6 Modulation (CC #1, initialized with 0)
34E2..34E7 - Part 1..6 Pitch Bend LSB (initialized with 0)
34E8..34ED - Part 1..6 Pitch Bend MSB (initialized with 64)
34EE..34F3 - Part 1..6 Expression setting (CC #11, initialized with 100)
34F4..34F9 - Part 1..6 Sustain setting (CC #64, initialized with 0)
34FA..34FF - Part 1..6 unused (initialized with 0)
3500..3505 - Part 1..6 RPN LSB (CC #98, initialized with 0xFF)
3506..350B - Part 1..6 RPN MSB (CC #99, initialized with 0xFF)
350C..3511 - Part 1..6 NRPN received (initialized with 0xFF, set to 0 when RPN LSB/MSB is received, set to 0xFF when NRPN is received)
3512..3517 - Part 1..6 ?? (initialized with 0)
3518..351D - Part 1..6 Instrument setting
351E - Reverb Mode
351F - Reverb Time
3521 - ?? (initialized with 1)
352A..3889 - voice memory (32 bytes per block)
 35AA..35C9 - some jump table index for interrupt #8
38DE..394D - more voice memory (32 bytes per block)

397E..3??? - state of playing notes
  3986 - current panning volume, left speaker (00..1F)
  39A6 - current panning volume, right speaker (00..1F)
  39C6 - target panning volume, left speaker (00..1F)
  39E6 - target panning volume, right speaker (00..1F)

3D7C..3E84 - SysEx receive data buffer

Some routine locations
----------------------
0x4014  LCD related interrupt handler
0x401C  serial input (MIDI data) interrupt handler
0x4020  some interrupt handler required while playing notes
0x45CB  Initialization (memory clear + checks), external memory is checked from 0x4679 on
0x5024  decide whether or not Test Mode is entered (normal boot == jump to 0x502A)
0x50F5  MIDI handling code
0x65E8  PCM ROM instrument check
0x6650  PCM card instrument check (Note: assumes that the SN-U110-04 PCM card is inserted)
0x6EA4  play a note (parameters: 0040 - part, 0041 = note pitch, 0042 - velocity)
0xB027  load instrument data from PCM ROM (writes to 23D6 + 50*i)
0xB12B  load instrument sample data from PCM ROM (reads sample IDs from 23D6+1B + 50*i, writes to 25B8 + F0*i)
0xB1A0  load secondary instrument sample data from PCM ROM (reads sample IDs from 23D6+3B + 50h*i, writes to 25B8+50 + F0*i)
0xB1E8  load 1 sample table entry from PCM ROM
0xB316  PCM ROM signature check
0xBBBB  write text to LCD

*/

#include "emu.h"
#include "cpu/mcs96/i8x9x.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "sound/roland_lp.h"
#include "video/msm6222b.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

// unscramble address: ROM dump offset -> proper (descrambled) offset
#define UNSCRAMBLE_ADDR_INT(_offset) \
	bitswap<19>(_offset,18,17,15,14,16,12,11, 7, 9,13,10, 8, 3, 2, 1, 6, 4, 5, 0)
// scramble address: proper offset -> ROM dump offset
#define SCRAMBLE_ADDR_INT(_offset) \
	bitswap<19>(_offset,18,17,14,16,15, 9,13,12, 8,10, 7,11, 3, 1, 2, 6, 5, 4, 0)

// PCM cards use a different address line scrambling
#define UNSCRAMBLE_ADDR_EXT(_offset) \
	bitswap<19>(_offset,18,17, 8, 9,16,11,12, 7,14,10,13,15, 3, 2, 1, 6, 4, 5, 0)

#define UNSCRAMBLE_DATA(_data) \
	bitswap<8>(_data,1,2,7,3,5,0,4,6)


static INPUT_PORTS_START( cm32p )
	PORT_START("A7")
	PORT_BIT(0x03ff, 0x0000, IPT_DIAL) PORT_NAME("Knob") PORT_SENSITIVITY(50) PORT_KEYDELTA(8) PORT_CODE_DEC(KEYCODE_DOWN) PORT_CODE_INC(KEYCODE_UP)

	PORT_START("SERVICE")   // connected to Port 0 of the P8098 CPU.
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test Switch") PORT_TOGGLE PORT_CODE(KEYCODE_F2) // SW A (checked during boot)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test: Check/Tune") PORT_CODE(KEYCODE_B) // SW B
	//PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PCM card inserted") PORT_TOGGLE PORT_CODE(KEYCODE_C)

	PORT_START("SW")    // test switches, accessed by reading from address 0x1300
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test: MSB Adj.") PORT_CODE(KEYCODE_1)   // SW 1
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test: THD Check") PORT_CODE(KEYCODE_2)  // SW 2
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test: PCM Out: String 1") PORT_CODE(KEYCODE_3)  // SW 3
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test: PCM Out: Sax 1") PORT_CODE(KEYCODE_4) // SW 4
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test: PCM + Long Reverb") PORT_CODE(KEYCODE_5)  // SW 5
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test: PCM + Short Reverb") PORT_CODE(KEYCODE_6) // SW 6
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test: VCA Down Check") PORT_CODE(KEYCODE_7) // SW 7
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test: VCA Up Check") PORT_CODE(KEYCODE_8)   // SW 8
INPUT_PORTS_END

class cm32p_state : public driver_device
{
public:
	cm32p_state(const machine_config &mconfig, device_type type, const char *tag);

	void cm32p(machine_config &config);

	void init_cm32p();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<i8x9x_device> cpu;
	required_device<mb87419_mb87420_device> pcm;
	required_device<generic_slot_device> pcmcard;
	required_device<msm6222b_device> lcd;
	required_device<timer_device> midi_timer;
	required_device<ram_device> some_ram;
	required_ioport test_sw;
	required_ioport service_port;

	void mt32_palette(palette_device &palette) const;

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void midi_w(u16 data);

	u8 lcd_ctrl_r();
	void lcd_ctrl_w(u8 data);
	void lcd_data_w(u8 data);
	u16 port0_r();
	u8 pcmrom_r(offs_t offset);
	u8 dsp_io_r(offs_t offset);
	void dsp_io_w(offs_t offset, u8 data);
	u8 snd_io_r(offs_t offset);
	void snd_io_w(offs_t offset, u8 data);
	u8 test_sw_r();

	TIMER_DEVICE_CALLBACK_MEMBER(midi_timer_cb);
	TIMER_DEVICE_CALLBACK_MEMBER(samples_timer_cb);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(card_load);
	DECLARE_DEVICE_IMAGE_UNLOAD_MEMBER(card_unload);

	void cm32p_map(address_map &map) ATTR_COLD;

	void descramble_rom_internal(u8* dst, const u8* src);
	void descramble_rom_external(u8* dst, const u8* src);

	bool pcmard_loaded;
	u8 midi;
	int midi_pos;
	u8 sound_io_buffer[0x100];
	u8 dsp_io_buffer[0x80];
};

cm32p_state::cm32p_state(const machine_config &mconfig, device_type type, const char *tag)
	: driver_device(mconfig, type, tag)
	, cpu(*this, "maincpu")
	, pcm(*this, "pcm")
	, pcmcard(*this, "cardslot")
	, lcd(*this, "lcd")
	, midi_timer(*this, "midi_timer")
	, some_ram(*this, "some_ram")
	, test_sw(*this, "SW")
	, service_port(*this, "SERVICE")
{
}


// screen update function from Roland D-110
uint32_t cm32p_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 sy=0;
	u8 const *const data = lcd->render();
	bitmap.fill(0);

	for (u8 y = 0; y < 2; y++)
	{
		for (u8 ra = 0; ra < 9; ra++)
		{
			u16 *p = &bitmap.pix(sy++);

			for (u16 x = 0; x < 16; x++)
			{
				u8 gfx = 0;
				if (ra < 8)
					gfx = data[x*16 + y*640 + ra];

				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
				*p++ = 0;
			}
		}
	}
	return 0;
}

void cm32p_state::machine_start()
{
	u8 *rom = memregion("maincpu")->base();

	// TODO: The IC8 gate array has an "LCD INT" line that needs to be emulated. Then, the hack can be removed.
	// Note: The hack is not necessary when *not* using test mode.
	rom[0xBB2D] = 0x03; // hack to make test mode not freeze when displaying the LCD text

	// TODO: remove this hack
	rom[0x7D80] = 0x00; // hack to exit some loop waiting for interrupt #8
}

void cm32p_state::machine_reset()
{
	midi_timer->adjust(attotime::from_hz(1));
	midi_pos = 0;
}

DEVICE_IMAGE_LOAD_MEMBER(cm32p_state::card_load)
{
	uint32_t const size = pcmcard->common_get_size("rom");
	if (size > 0x080000)
		return std::make_pair(image_error::INVALIDLENGTH, "Invalid size (maximum supported is 512K)");

	pcmcard->rom_alloc(0x080000, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);    // cards are up to 512K
	pcmcard->common_load_rom(pcmcard->get_rom_base(), size, "rom");
	u8 *base = pcmcard->get_rom_base();
	if (size < 0x080000)
	{
		uint32_t mirror = (1 << (31 - count_leading_zeros_32(size)));
		if (mirror < 0x020000)  // due to how address descrambling works, we can currently only do mirroring for 128K pages
			mirror = 0x020000;
		for (uint32_t ofs = mirror; ofs < 0x080000; ofs += mirror)
			memcpy(base + ofs, base, mirror);
	}

	u8 *src = reinterpret_cast<u8 *>(memregion("pcmorg")->base());
	u8 *dst = reinterpret_cast<u8 *>(memregion("pcm")->base());
	memcpy(&src[0x080000], base, 0x080000);
	// descramble PCM card ROM
	descramble_rom_external(&dst[0x080000], &src[0x080000]);
	pcmard_loaded = true;

	return std::make_pair(std::error_condition(), std::string());
}

DEVICE_IMAGE_UNLOAD_MEMBER(cm32p_state::card_unload)
{
	u8 *src = reinterpret_cast<u8*>(memregion("pcmorg")->base());
	u8 *dst = reinterpret_cast<u8*>(memregion("pcm")->base());
	memset(&src[0x080000], 0xff, 0x080000);
	memset(&dst[0x080000], 0xff, 0x080000);
	pcmard_loaded = false;
}

void cm32p_state::lcd_ctrl_w(u8 data)
{
	lcd->control_w(data);
}

u8 cm32p_state::lcd_ctrl_r()
{
	// The CM-64 service manual lists "D-110 LCD UNIT" for using PCM test mode, so I assume it works like that.
	// However, the CM-32P firmware doesn't seem to ever read the status.
	return lcd->control_r() >> 7;
}

void cm32p_state::lcd_data_w(u8 data)
{
	lcd->data_w(data);
}

void cm32p_state::midi_w(u16 data)
{
	logerror("midi_out %02x\n", data);
	midi = data;
}

TIMER_DEVICE_CALLBACK_MEMBER(cm32p_state::midi_timer_cb)
{
	const static u8 midi_data[3] = { 0x9a, 0x40, 0x7f };
	midi = midi_data[midi_pos++];
	logerror("midi_in %02x\n", midi);
	cpu->serial_w(midi);
	if(midi_pos < sizeof(midi_data))
		midi_timer->adjust(attotime::from_hz(1250));
}

u16 cm32p_state::port0_r()
{
	return service_port->read() | (pcmard_loaded ? 0x10 : 0x00);
}

u8 cm32p_state::pcmrom_r(offs_t offset)
{
	const u8* pcm_rom = memregion("pcm")->base();
	return pcm_rom[offset];
}

u8 cm32p_state::dsp_io_r(offs_t offset)
{
	return dsp_io_buffer[offset];
}

void cm32p_state::dsp_io_w(offs_t offset, u8 data)
{
	dsp_io_buffer[offset] = data;
	// do read/write to some external memory, makes the RCC-CPU check pass. (routine at 0x4679)
	switch(offset)
	{
	case 0x04:
		// write to partials?? (written in loop at 0x4375)
		break;
	case 0x06:
		{
			u8* ram = some_ram->pointer();
			offs_t ofs = data;
			ram[0x000 | ofs] = dsp_io_buffer[0x00] & 0x03;
			ram[0x100 | ofs] = dsp_io_buffer[0x01];
			ram[0x200 | ofs] = dsp_io_buffer[0x02];
		}
		break;
	case 0x0A:
		{
			const u8* ram = some_ram->pointer();
			offs_t ofs = data;
			dsp_io_buffer[0x00] = ram[0x000 | ofs];
			dsp_io_buffer[0x01] = ram[0x100 | ofs];
			dsp_io_buffer[0x02] = ram[0x200 | ofs];
		}
		break;
	}
}

u8 cm32p_state::snd_io_r(offs_t offset)
{
	// lots of offset modification magic to achieve the following:
	//  - offsets 00..1F are "sound chip read"
	//  - offsets 20..3F are a readback of what was written to registers 00..1F
	//  - This behaviour is reversed for offset 01/21, which is used for reading the PCM sample tables.
	// All this is just for making debugging easier, as it allows one to check the
	// register state using the Memory Viewer.
	if (offset == 0x01 || offset == 0x21)
		offset ^= 0x20; // remove when PCM data readback via sound chip is confirmed to work
	if (offset < 0x20)
		return pcm->read(offset);
	if (offset < 0x40)
		offset -= 0x20;

	if (offset == 0x01)
	{
		// code for reading from the PCM sample table is at 0xB027
		// The code at 0xB0AC writes to 1411/1F (??), then 1403/02 (bank), then 1409/08/0B/0A (address).
		// It waits a few cycles and at 0xB0F7 it reads the resulting data from 1401.
		offs_t bank = sound_io_buffer[0x03];
		offs_t addr = (sound_io_buffer[0x09] << 0) | (sound_io_buffer[0x0A] << 8) | (sound_io_buffer[0x0B] << 16);
		addr = ((addr >> 6) + 2) & 0x3FFFF;
		addr |= (bank << 16);
		// write actual ROM address to 1440..1443 for debugging
		sound_io_buffer[0x43] = (addr >>  0) & 0xFF;
		sound_io_buffer[0x42] = (addr >>  8) & 0xFF;
		sound_io_buffer[0x41] = (addr >> 16) & 0xFF;
		sound_io_buffer[0x40] = (addr >> 24) & 0xFF;
		return pcmrom_r(addr);
	}
	return sound_io_buffer[offset];
}

void cm32p_state::snd_io_w(offs_t offset, u8 data)
{
	// register map
	// ------------
	// Note: 16-bit words are Little Endian, the firmware writes the odd byte is first
	//  00/01 - ??
	//  02/03 - ROM bank (only bits 11-13 are used, bit 11 = PCM card, bits 12-13 select between IC18/19/20)
	//  04/05 - frequency (2.14 fixed point, 0x4000 = 32000 Hz)
	//  06/07 - volume
	//  08/09 - sample start address, fraction (2.14 fixed point, i.e. 1 byte = 0x4000)
	//  0A/0B - sample start address (high word, i.e. address bits 2..17)
	//  0C/0D - sample end address (high word)
	//  0E/0F - sample loop address (high word)
	//  11/13/15/17 - voice enable mask (11 = least significant 8 bits, 17 = most significant 8 bits)
	//  1A - ??
	//  1F - voice select
	if (offset < 0x20)
		pcm->write(offset, data);
	sound_io_buffer[offset] = data;
}

u8 cm32p_state::test_sw_r()
{
	return test_sw->read();
}

TIMER_DEVICE_CALLBACK_MEMBER(cm32p_state::samples_timer_cb)
{
	// TODO: does this trigger something?
}

void cm32p_state::mt32_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(0, 0, 0));
	palette.set_pen_color(1, rgb_t(0, 255, 0));
}

void cm32p_state::cm32p_map(address_map &map)
{
	map(0x1080, 0x10ff).rw(FUNC(cm32p_state::dsp_io_r), FUNC(cm32p_state::dsp_io_w));   // DSP area (writes to 1080..82/86/8C/8D)
	map(0x1100, 0x1100).rw(FUNC(cm32p_state::lcd_ctrl_r), FUNC(cm32p_state::lcd_ctrl_w));
	map(0x1102, 0x1102).w(FUNC(cm32p_state::lcd_data_w));
	map(0x1300, 0x1300).r(FUNC(cm32p_state::test_sw_r));    // test switch state
	map(0x1400, 0x14ff).rw(FUNC(cm32p_state::snd_io_r), FUNC(cm32p_state::snd_io_w));   // sound chip area
	map(0x2000, 0x20ff).rom().region("maincpu", 0x2000);    // init vector @ 2080
	map(0x2100, 0x3fff).ram();  // main RAM
	map(0x4000, 0xffff).rom().region("maincpu", 0x4000);
}

void cm32p_state::cm32p(machine_config &config)
{
	i8x9x_device &maincpu(P8098(config, cpu, 12_MHz_XTAL));
	maincpu.set_addrmap(AS_PROGRAM, &cm32p_state::cm32p_map);
	maincpu.ach7_cb().set_ioport("A7");
	maincpu.serial_tx_cb().set(FUNC(cm32p_state::midi_w));
	maincpu.in_p0_cb().set(FUNC(cm32p_state::port0_r));

	SPEAKER(config, "speaker", 2).front();

	MB87419_MB87420(config, pcm, 32.768_MHz_XTAL);
	pcm->int_callback().set_inputline(cpu, i8x9x_device::EXTINT_LINE);
	pcm->add_route(0, "speaker", 1.0, 0);
	pcm->add_route(1, "speaker", 1.0, 1);

	RAM(config, some_ram).set_default_size("8K");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_screen_update(FUNC(cm32p_state::screen_update));
	screen.set_size(16*6-1, (16*6-1)*3/4);
	screen.set_visarea(0, 16*6-2, 0, (16*6-1)*3/4-1);
	screen.set_palette("palette");
	PALETTE(config, "palette", FUNC(cm32p_state::mt32_palette), 2);
	MSM6222B_01(config, lcd, 0);

	generic_cartslot_device &cardslot(GENERIC_CARTSLOT(config, "cardslot", generic_romram_plain_slot, "u110_card", "bin"));
	cardslot.set_device_load(FUNC(cm32p_state::card_load));
	cardslot.set_device_unload(FUNC(cm32p_state::card_unload));
	SOFTWARE_LIST(config, "card_list").set_original("u110_card");

	TIMER(config, midi_timer).configure_generic(FUNC(cm32p_state::midi_timer_cb));

	TIMER(config, "samples_timer").configure_periodic(FUNC(cm32p_state::samples_timer_cb), attotime::from_hz(32000*2));
}

void cm32p_state::init_cm32p()
{
	// Roland did a fair amount of scrambling on the address and data lines.
	// Only the first 0x80 bytes of the ROMs are readable text in a raw dump.
	// The CM-32P actually checks some of these header bytes, but it uses post-scrambling variants of offsets/values.
	u8* src = static_cast<u8*>(memregion("pcmorg")->base());
	u8* dst = static_cast<u8*>(memregion("pcm")->base());
	// descramble internal ROMs
	descramble_rom_internal(&dst[0x000000], &src[0x000000]);
	descramble_rom_internal(&dst[0x100000], &src[0x100000]);
	descramble_rom_internal(&dst[0x200000], &src[0x200000]);
	// descramble PCM card ROM
	descramble_rom_external(&dst[0x080000], &src[0x080000]);
}

void cm32p_state::descramble_rom_internal(u8* dst, const u8* src)
{
	for (offs_t srcpos = 0x00; srcpos < 0x80000; srcpos ++)
	{
		offs_t dstpos = UNSCRAMBLE_ADDR_INT(srcpos);
		dst[dstpos] = UNSCRAMBLE_DATA(src[srcpos]);
	}
}

void cm32p_state::descramble_rom_external(u8* dst, const u8* src)
{
	for (offs_t srcpos = 0x00; srcpos < 0x80000; srcpos ++)
	{
		offs_t dstpos = UNSCRAMBLE_ADDR_EXT(srcpos);
		dst[dstpos] = UNSCRAMBLE_DATA(src[srcpos]);
	}
}


ROM_START( cm32p )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "cm-32_p__1.0.0.am27c512.7d.ic9",  0x000000, 0x10000, CRC(6f2f6dfd) SHA1(689f77c1d56f923ef1dab7d993a124c47736bc56) ) // "CM-32 P // 1 0 0 <filled bubbles in red marker>" sticker on an AM27C512-150DC eprom @ IC9

	ROM_REGION( 0x400000, "pcmorg", 0 ) // ROMs before descrambling
	ROM_LOAD( "roland__r15179970__mb834000a-20__3b1_aa__8917_r00.45f.ic18", 0x000000, 0x80000, CRC(8e53b2a3) SHA1(4872530870d5079776e80e477febe425dc0ec1df) ) // markings under chip footprint are "MB834000A-20P-G-3B1"
	// 0x080000 .. 0x0FFFFF is reserved for the PCM card
	ROM_LOAD( "roland__r15179971__mb834000a-20__3b2_aa__8919_r02.34f.ic19", 0x100000, 0x80000, CRC(c8220761) SHA1(49e55fa672020f95fd9c858ceaae94d6db93df7d) ) // markings under chip footprint are "MB834000A20P-G-3B2" (including the missing dash, which is a typo on the board silkscreen)
	ROM_LOAD( "roland__r15179972__hn62304bpe98__9d1_japan.3f.ic20", 0x200000, 0x80000, CRC(733c4054) SHA1(9b6b59ab74e5bf838702abb087c408aaa85b7b1f) ) // markings under chip footprint are "HN62304BPE98"
	ROM_REGION( 0x400000, "pcm", ROMREGION_ERASEFF )    // ROMs after descrambling
ROM_END

} // anonymous namespace


SYST( 1989, cm32p, 0, 0, cm32p, cm32p, cm32p_state, init_cm32p, "Roland", "CM-32P", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )



roland_d10.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert, Jonathan Gevaryahu
/*************************************************************************************************

    Roland D-10/D-110 driver

    Driver by Olivier Galibert and Jonathan Gevaryahu

    The Roland D-110 is an expander (synthesizer without the keyboard)
    from 1988.  Internally it's very similar to a mt32, with a better
    LCD screen (16x2) and more control buttons.  More importantly, it
    has more sound rom, a battery-backed ram and a port for memory
    cards allowing to load and save new sounds.

    After the first boot, the ram needs to be reinitialized to factory
    default values.  Press Write/Copy (I) while resetting then
    validate with Enter (K).
*/

#include "emu.h"
#include "cpu/mcs96/i8x9x.h"
#include "machine/bankdev.h"
#include "mb63h149.h"
#include "machine/nvram.h"
#include "machine/timer.h"
#include "video/msm6222b.h"
#include "emupal.h"
#include "screen.h"


namespace {

static INPUT_PORTS_START( d10 )
INPUT_PORTS_END

static INPUT_PORTS_START( d110 )
	PORT_START("SC0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Write/Copy") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Number +") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Bank +") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Group +") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part +") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Timbre") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Patch") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Exit") PORT_CODE(KEYCODE_Q)

	PORT_START("SC1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Enter") PORT_CODE(KEYCODE_K)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Number -") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Bank -") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Group -") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part -") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("System") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Edit") PORT_CODE(KEYCODE_A)
INPUT_PORTS_END


class roland_d10_state : public driver_device
{
public:
	roland_d10_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_bank(*this, "bank")
		, m_rams(*this, "rams")
		, m_memcs(*this, "memcs")
		, m_lcd(*this, "lcd")
		, m_midi_timer(*this, "midi_timer")
		, m_maincpu(*this, "maincpu")
	{ }

	void d10(machine_config &config);
	void d110(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void bank_w(uint8_t data);
	uint8_t fixed_r(offs_t offset);
	void fixed_w(offs_t offset, uint8_t data);
	void so_w(uint8_t data);
	void midi_w(uint8_t data);
	uint8_t lcd_ctrl_r();
	void lcd_ctrl_w(uint8_t data);
	void lcd_data_w(uint8_t data);
	uint8_t port0_r();
	TIMER_DEVICE_CALLBACK_MEMBER(midi_timer_cb);
	TIMER_DEVICE_CALLBACK_MEMBER(samples_timer_cb);
	void d10_palette(palette_device &palette) const;
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void d10_map(address_map &map) ATTR_COLD;
	void d10_bank_map(address_map &map) ATTR_COLD;
	void d110_map(address_map &map) ATTR_COLD;
	void d110_bank_map(address_map &map) ATTR_COLD;

	uint8_t  m_lcd_data_buffer[256]{};
	int      m_lcd_data_buffer_pos = 0;
	uint8_t  m_midi = 0;
	int      m_midi_pos = 0;
	uint8_t  m_port0 = 0;
	required_device<address_map_bank_device> m_bank;
	required_device<nvram_device> m_rams;
	required_device<nvram_device> m_memcs;
	required_device<msm6222b_device> m_lcd;
	required_device<timer_device> m_midi_timer;
	required_device<i8x9x_device> m_maincpu;
};


uint32_t roland_d10_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint16_t sy=0;
	uint8_t const *const data = m_lcd->render();
	bitmap.fill(0);

	for (uint8_t y = 0; y < 2; y++)
	{
		for (uint8_t ra = 0; ra < 9; ra++)
		{
			uint16_t *p = &bitmap.pix(sy++);

			for (uint16_t x = 0; x < 16; x++)
			{
				uint8_t gfx = 0;
				if (ra < 8)
					gfx = data[x*16 + y*640 + ra];

				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
				*p++ = 0;
			}
		}
	}
	return 0;
}

void roland_d10_state::machine_start()
{
	m_lcd_data_buffer_pos = 0;
}

void roland_d10_state::machine_reset()
{
	//  midi_timer->adjust(attotime::from_hz(1));
	m_midi_pos = 0;
	m_port0 = 0x80; // battery ok
}

void roland_d10_state::lcd_ctrl_w(uint8_t data)
{
	m_lcd->control_w(data);
	for(int i=0; i != m_lcd_data_buffer_pos; i++)
		m_lcd->data_w(m_lcd_data_buffer[i]);
	m_lcd_data_buffer_pos = 0;
}

uint8_t roland_d10_state::lcd_ctrl_r()
{
	// Busy flag in the msm622b is bit 7, while the software expects it in bit 0...
	return m_lcd->control_r() >> 7;
}

void roland_d10_state::lcd_data_w(uint8_t data)
{
	if(m_lcd_data_buffer_pos == sizeof(m_lcd_data_buffer)) {
		logerror("Warning: lcd data buffer overflow (%04x)\n", m_maincpu->pc());
		return;
	}
	m_lcd_data_buffer[m_lcd_data_buffer_pos++] = data;
}

void roland_d10_state::bank_w(uint8_t data)
{
	m_bank->set_bank(data);
}

uint8_t roland_d10_state::fixed_r(offs_t offset)
{
	return m_bank->space(0).read_byte(0x40000 + offset);
}

void roland_d10_state::fixed_w(offs_t offset, uint8_t data)
{
	m_bank->space(0).write_byte(0x40000 + offset, data);
}

void roland_d10_state::midi_w(uint8_t data)
{
	logerror("midi_out %02x\n", data);
	m_midi = data;
}

TIMER_DEVICE_CALLBACK_MEMBER(roland_d10_state::midi_timer_cb)
{
	const static uint8_t midi_data[3] = { 0x91, 0x40, 0x7f };
	m_midi = midi_data[m_midi_pos++];
	logerror("midi_in %02x\n", m_midi);
	m_maincpu->serial_w(m_midi);
	if(m_midi_pos < sizeof(midi_data))
		m_midi_timer->adjust(attotime::from_hz(1250));
}

uint8_t roland_d10_state::port0_r()
{
	return m_port0;
}

TIMER_DEVICE_CALLBACK_MEMBER(roland_d10_state::samples_timer_cb)
{
	m_port0 ^= 0x10;
}

void roland_d10_state::so_w(uint8_t data)
{
	// bit 0   = led
	// bit 1-2 = reverb program a13/a14
	// bit 3   = R. SW. to ananlog board
	// bit 5   = boss 8Mhz clock, handled internally
	//  logerror("so: rw=%d bank=%d led=%d\n", (data >> 3) & 1, (data >> 1) & 3, data & 1);
}

void roland_d10_state::d10_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(0, 255, 0));
	palette.set_pen_color(1, rgb_t(0, 0, 0));
}

void roland_d10_state::d10_map(address_map &map)
{
	map(0x0100, 0x0100).w(FUNC(roland_d10_state::bank_w));
	map(0x0200, 0x0200).w(FUNC(roland_d10_state::so_w));
	map(0x0300, 0x0300).w(FUNC(roland_d10_state::lcd_data_w));
	map(0x0380, 0x0380).rw(FUNC(roland_d10_state::lcd_ctrl_r), FUNC(roland_d10_state::lcd_ctrl_w));
	map(0x0c00, 0x0dff).rw("keyscan", FUNC(mb63h149_device::read), FUNC(mb63h149_device::write));
	map(0x1000, 0x7fff).rom().region("firmware", 0x1000);
	map(0x8000, 0xbfff).m(m_bank, FUNC(address_map_bank_device::amap16));
	map(0xc000, 0xffff).rw(FUNC(roland_d10_state::fixed_r), FUNC(roland_d10_state::fixed_w));
}

void roland_d10_state::d10_bank_map(address_map &map)
{
	map(0x00000, 0x0ffff).rom().region("firmware", 0);
	map(0x40000, 0x47fff).ram();
	map(0x80000, 0x87fff).ram().share("rams");
	map(0xa0000, 0xbffff).rom().region("presets", 0);
	map(0xc0000, 0xc7fff).ram().share("memcs");
}

void roland_d10_state::d110_map(address_map &map)
{
	map(0x0100, 0x0100).w(FUNC(roland_d10_state::bank_w));
	map(0x0200, 0x0200).w(FUNC(roland_d10_state::so_w));
	map(0x021a, 0x021b).portr("SC0").nopw();
	map(0x021c, 0x021d).portr("SC1");
	map(0x0300, 0x0300).w(FUNC(roland_d10_state::lcd_data_w));
	map(0x0380, 0x0380).rw(FUNC(roland_d10_state::lcd_ctrl_r), FUNC(roland_d10_state::lcd_ctrl_w));
	//map(0x1000, 0x7fff).rom().region("firmware", 0x1000);
	map(0x1000, 0x7fff).lr8([this](offs_t offset) { return m_bank->space(0).read_byte(offset + 0x01000); }, "firmware_r");
	map(0x8000, 0xbfff).m(m_bank, FUNC(address_map_bank_device::amap8));
	map(0xc000, 0xffff).rw(FUNC(roland_d10_state::fixed_r), FUNC(roland_d10_state::fixed_w));
}

void roland_d10_state::d110_bank_map(address_map &map)
{
	map(0x00000, 0x0ffff).rom().region("firmware", 0);
	map(0x40000, 0x47fff).ram().share("rams");
	map(0x80000, 0x9ffff).rom().region("presets", 0);
	map(0xc0000, 0xc7fff).ram().share("memcs");
}

void roland_d10_state::d10(machine_config &config)
{
	N8097BH(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_d10_state::d10_map);
	m_maincpu->serial_tx_cb().set(FUNC(roland_d10_state::midi_w));
	m_maincpu->in_p0_cb().set(FUNC(roland_d10_state::port0_r));

	ADDRESS_MAP_BANK(config, m_bank);
	m_bank->set_endianness(ENDIANNESS_LITTLE);
	m_bank->set_data_width(16);
	m_bank->set_addr_width(20);
	m_bank->set_stride(0x4000);
	m_bank->set_addrmap(0, &roland_d10_state::d10_bank_map);

// Battery-backed main ram
	NVRAM(config, m_rams, nvram_device::DEFAULT_ALL_0);

// Shall become a proper memcard device someday
	NVRAM( config, m_memcs, nvram_device::DEFAULT_ALL_0 );

	mb63h149_device &keyscan(MB63H149(config, "keyscan", 16.384_MHz_XTAL));
	keyscan.int_callback().set_inputline(m_maincpu, i8x9x_device::HSI0_LINE);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_screen_update(FUNC(roland_d10_state::screen_update));
//  screen.set_size(20*6-1, 2*9-1);
	screen.set_size(16*6-1, (16*6-1)*3/4);
	screen.set_visarea(0, 16*6-2, 0, (16*6-1)*3/4-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(roland_d10_state::d10_palette), 2);

	MSM6222B_01(config, m_lcd, 0);

	TIMER(config, m_midi_timer).configure_generic(FUNC(roland_d10_state::midi_timer_cb));

	TIMER(config,  "samples_timer").configure_periodic(FUNC(roland_d10_state::samples_timer_cb), attotime::from_hz(32000*2) );
}

void roland_d10_state::d110(machine_config &config)
{
	d10(config);

	// D-110 ties BUSWIDTH to GND to make all external accesses 8 bits wide
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_d10_state::d110_map);
	m_bank->set_data_width(8);
	m_bank->set_addrmap(0, &roland_d10_state::d110_bank_map);

	config.device_remove("keyscan");
}

ROM_START( d10 )
	ROM_REGION16_LE( 0x10000, "firmware", 0 )
	ROM_DEFAULT_BIOS( "106" )

	ROM_SYSTEM_BIOS( 0, "102", "Firmware 1.02" )
	ROMX_LOAD( "d-10_v1.02_a.ic14",            1,   0x8000, CRC(71162a61) SHA1(a39f6ea90682346bc259615198eac18c1f0ad0bd), ROM_BIOS(0) | ROM_SKIP(1) )
	ROMX_LOAD( "d-10_v1.02_b.ic13",            0,   0x8000, CRC(99def0ed) SHA1(ff6d3f88a3cdfa901bd0e9a83350ea2842fd16bc), ROM_BIOS(0) | ROM_SKIP(1) )

	ROM_SYSTEM_BIOS( 1, "106", "Firmware 1.06" )
	ROMX_LOAD( "d-10a_1.06.ic14",              1,   0x8000, CRC(ea90848a) SHA1(1689b47210e4033b922816a1817c430358d96641), ROM_BIOS(1) | ROM_SKIP(1) ) // AM27C256-200DC
	ROMX_LOAD( "d-10b_1.06.ic13",              0,   0x8000, CRC(26dbbf48) SHA1(f6063c2e3000e08a0dc3e45c8ee9400916f5493c), ROM_BIOS(1) | ROM_SKIP(1) ) // AM27C256-155DC

	ROM_REGION16_LE( 0x20000, "presets", 0 )
	ROM_LOAD(  "r15179873-lh5310-97.ic12",     0,  0x20000, CRC(580a8f9e) SHA1(05587a0542b01625dcde37de5bb339880e47eb93) )

	ROM_REGION( 0x100000, "la32", 0 )
	ROM_LOAD(  "r15179878-hn62304bpc99.ic27",  0,       0x80000, CRC(e117e6ab) SHA1(6760d14900161b8715c2bfd4ebe997877087c90c) )
	ROM_LOAD(  "r15179880-hn62304bpd10.ic28",  0x80000, 0x80000, CRC(b329f945) SHA1(9c59f50518a070461b2ec6cb4e43ee7cc1e905b6) )

	ROM_REGION( 0x8000, "boss", 0 )
	ROM_LOAD(  "r15179879-hn623257pz20.ic33",  0,   0x8000, CRC(5d34174e) SHA1(17bd2887711c5c5458aba6d3be5972b2096eb450) )
ROM_END

ROM_START( d110 )
	ROM_REGION( 0x10000, "firmware", 0 )
	ROM_DEFAULT_BIOS( "110" )

	ROM_SYSTEM_BIOS( 0, "106", "Firmware 1.06" )
	ROMX_LOAD( "d-110.v1.06.ic19.bin",         0,   0x8000, CRC(3dd5b6e9) SHA1(73b155fb0a8adc2362e73cb0803dafba9ccfb508), ROM_BIOS(0) )

	ROM_SYSTEM_BIOS( 1, "110", "Firmware 1.10" )
	ROMX_LOAD( "d-110.v1.10.ic19.bin",         0,   0x8000, CRC(3ae68187) SHA1(28635510f30d6c1fb88e00da03e5b4e045c380cb), ROM_BIOS(1) )

	ROM_REGION( 0x20000, "presets", 0 )
	ROM_LOAD(  "r15179873-lh5310-97.ic12.bin", 0,  0x20000, CRC(580a8f9e) SHA1(05587a0542b01625dcde37de5bb339880e47eb93) )

	ROM_REGION( 0x100000, "la32", 0 )
	ROM_LOAD(  "r15179878.ic7.bin",            0,  0x80000, CRC(e117e6ab) SHA1(6760d14900161b8715c2bfd4ebe997877087c90c) )
	ROM_LOAD(  "r15179880.ic8.bin",      0x80000,  0x80000, CRC(b329f945) SHA1(9c59f50518a070461b2ec6cb4e43ee7cc1e905b6) )

	ROM_REGION( 0x8000, "boss", 0 )
	ROM_LOAD(  "r15179879.ic6.bin",            0,   0x8000, CRC(5d34174e) SHA1(17bd2887711c5c5458aba6d3be5972b2096eb450) )
ROM_END

} // anonymous namespace


SYST( 1988, d10,  0,   0, d10,  d10,  roland_d10_state, empty_init, "Roland", "D-10 Multi Timbral Linear Synthesizer", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
SYST( 1988, d110, d10, 0, d110, d110, roland_d10_state, empty_init, "Roland", "D-110 Multi Timbral Sound Module",      MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



roland_d50.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Roland D-50/D-550.

****************************************************************************/

#include "emu.h"
#include "cpu/upd78k/upd78k3.h"
#include "machine/bankdev.h"
#include "mb63h149.h"
#include "machine/nvram.h"
#include "pg1000.h"


namespace {

class roland_d50_state : public driver_device
{
public:
	roland_d50_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_eram(*this, "eram")
	{
	}

	void d50(machine_config &config);
	void d550(machine_config &config);

private:
	void d50_mem_map(address_map &map) ATTR_COLD;
	void d550_mem_map(address_map &map) ATTR_COLD;
	void eram_map(address_map &map) ATTR_COLD;

	required_device<upd78312_device> m_maincpu;
	required_device<address_map_bank_device> m_eram;
};


void roland_d50_state::d50_mem_map(address_map &map)
{
	// Internal ROM is enabled at 0000–1FFF (+5V pullup on EA pin)
	map(0x2000, 0x7fff).rom().region("progrom", 0x2000);
	map(0x8000, 0xbfff).m(m_eram, FUNC(address_map_bank_device::amap8));
	map(0xc000, 0xdfff).ram();
	map(0xf400, 0xf7ff).rw("keyscan", FUNC(mb63h149_device::read), FUNC(mb63h149_device::write));
}

void roland_d50_state::d550_mem_map(address_map &map)
{
	// Internal ROM is enabled at 0000–1FFF (+5V pullup on EA pin)
	map(0x2000, 0x7fff).rom().region("progrom", 0x2000);
	map(0x8000, 0xbfff).m(m_eram, FUNC(address_map_bank_device::amap8));
	map(0xc000, 0xdfff).ram();
}

void roland_d50_state::eram_map(address_map &map)
{
	map(0x00000, 0x07fff).ram().share("toneram");
	//map(0x08000, 0x0ffff).rw(FUNC(roland_d50_state::memcard_r), FUNC(roland_d50_state::memcard_w));
	map(0x10000, 0x13fff).mirror(0x4000).rom().region("progrom", 0x8000);
	map(0x18000, 0x1bfff).mirror(0x4000).rom().region("progrom", 0xc000);
}


static INPUT_PORTS_START(d50)
	PORT_START("ROW6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TUNE/FUNC")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("MIDI")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_9) PORT_NAME("INTERNAL")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_O) PORT_NAME("CARD")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("LOCAL")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("VALUE")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("LOWER")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("UPPER")

	PORT_START("ROW5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_NAME("SHIFT")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1/ABC")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4/JKL")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7/STU")
	PORT_BIT(0xf0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("ROW4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0/SPACE")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2/DEF")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5/MNO")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8/VWX")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_ENTER) PORT_NAME("ENTER")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3/GHI")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6/PQR")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9/YZ-")

	PORT_START("ROW3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("DECREMENT")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("INCREMENT")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("WRITE")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("DATA TRANS")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("EXIT")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("UNDO")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("COPY")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("COMPARE")

	PORT_START("ROW2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TUNE/DETUNE")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("KEY MODE")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("SPLIT POINT")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TONE BALANCE")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("L-TONE EDIT")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("U-TONE EDIT")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("PATCH EDIT")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("ROW1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_Q) PORT_NAME("Patch number 1")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_W) PORT_NAME("Patch number 2")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_E) PORT_NAME("Patch number 3")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_R) PORT_NAME("Patch number 4")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_T) PORT_NAME("Patch number 5")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_Y) PORT_NAME("Patch number 6")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_U) PORT_NAME("Patch number 7")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_I) PORT_NAME("Patch number 8")

	PORT_START("ROW0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_1) PORT_NAME("Patch Bank 1")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_2) PORT_NAME("Patch Bank 2")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_3) PORT_NAME("Patch Bank 3")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_4) PORT_NAME("Patch Bank 4")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_5) PORT_NAME("Patch Bank 5")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_6) PORT_NAME("Patch Bank 6")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_7) PORT_NAME("Patch Bank 7")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_8) PORT_NAME("Patch Bank 8")
INPUT_PORTS_END

static INPUT_PORTS_START(d550)
	PORT_START("ROW4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_G) PORT_NAME("EXIT")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_H) PORT_NAME("EDIT")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_J) PORT_NAME("WRITE")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_ENTER) PORT_NAME("ENTER")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_N) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_NAME("SHIFT")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_M) PORT_NAME("CHASE")
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("ROW3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_A) PORT_NAME("TUNE")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_S) PORT_NAME("MIDI")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_D) PORT_NAME("DATA TRANS")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_Z) PORT_NAME("COPY")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_X) PORT_NAME("UNDO")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_C) PORT_NAME("COMPARE")
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("ROW2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_F) PORT_NAME("INT")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_O) PORT_CODE(KEYCODE_V) PORT_NAME("CARD")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_L) PORT_NAME(">>")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_K) PORT_NAME("<<")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_RIGHT) PORT_NAME(">")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_LEFT) PORT_NAME("<")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_UP) PORT_NAME("VALUE UP")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_DOWN) PORT_NAME("VALUE DOWN")

	PORT_START("ROW1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_1) PORT_NAME("Patch Bank 1")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_2) PORT_NAME("Patch Bank 2")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_3) PORT_NAME("Patch Bank 3")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_4) PORT_NAME("Patch Bank 4")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_5) PORT_NAME("Patch Bank 5")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_6) PORT_NAME("Patch Bank 6")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_7) PORT_NAME("Patch Bank 7")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_8) PORT_NAME("Patch Bank 8")

	PORT_START("ROW0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_Q) PORT_NAME("Patch number 1")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_W) PORT_NAME("Patch number 2")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_E) PORT_NAME("Patch number 3")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_R) PORT_NAME("Patch number 4")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_T) PORT_NAME("Patch number 5")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_Y) PORT_NAME("Patch number 6")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_U) PORT_NAME("Patch number 7")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_I) PORT_NAME("Patch number 8")
INPUT_PORTS_END


void roland_d50_state::d50(machine_config &config)
{
	UPD78312(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_d50_state::d50_mem_map);

	ADDRESS_MAP_BANK(config, m_eram);
	m_eram->set_addrmap(0, &roland_d50_state::eram_map);
	m_eram->set_data_width(8);
	m_eram->set_endianness(ENDIANNESS_LITTLE);
	m_eram->set_addr_width(17);
	m_eram->set_stride(0x4000);

	NVRAM(config, "toneram", nvram_device::DEFAULT_ALL_0); // HM62256LP-12 + battery

	// LCD unit is LM402802 (D-50) or LM402551 (D-550)

	MB63H149(config, "keyscan", 32.768_MHz_XTAL / 2); // on Dyna Scan Board
	//keyscan.int_callback().set_inputline(m_maincpu, upd78312_device::INT2_LINE);

	//MB87136(config, "synthe", 32.768_MHz_XTAL);

	PG1000(config, "programmer");
}

void roland_d50_state::d550(machine_config &config)
{
	d50(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_d50_state::d550_mem_map);

	config.device_remove("keyscan");
}

// the internal date format for the external program roms is as such:
// DDMY where DD is the day within the month, M is a month where
// January == A,February == B, etc, and Y is the last digit of the year
// (the first 3 digits are '198'); note that the date was not updated for
// some minor revisions.

// the portion after the data is in an unknown format, but is presumably
// version information for certain subprograms on the rom itself.

ROM_START(d50) // Newer PCB with silkscreen "Roland || D-50, D-550 || MAIN BOARD" and a checkbox for which board it is intended for "[O]D-50 | ASSY 76180090 || [ ]D-550 | ASSY 79379050" and trace layer ID "22925445 05" and a QFP LA32 "R15229896 || LA32 || 8816Q26" at IC31; the board supports either the split or the combined PCM roms.
	ROM_REGION(0x10000, "progrom", 0)
	ROM_SYSTEM_BIOS(0, "v2.22", "ROM Version 2.22") // "        D-50    Ver 2.22                Thanks to Eric & Adrian.  14J7 D.420/7.9"
	ROMX_LOAD("d-50_222.ic22", 0x00000, 0x10000, CRC(e92c69f9) SHA1(fd783abc7fbd4abe2dedfe89d59e396b5e687a27), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v2.21", "ROM Version 2.21") // "        D-50    Ver 2.21                Thanks to Eric & Adrian.  14J7 D.420/7.6"
	ROMX_LOAD("roland_d-50__r15179866-01__lh531452.v2.21.27c512.ic22", 0x0000, 0x10000, CRC(3e72bdf0) SHA1(6e7f8cd3b3ce385350ca2eda6aee73f5bbc433ef), ROM_BIOS(1)) // "Roland D-50 // R15179866-01 // LH531452" Mask ROM (23c512 equivalent)
	ROM_SYSTEM_BIOS(2, "v2.20cfw", "ROM Version 2.20 (CFW, patched)") // "        D-50    Ver 2.21                Thanks to Ricard W.       14J7 D.420/7.5"
	ROMX_LOAD("roland_d50_220rw_cfw_patched.ic22", 0x00000, 0x10000, CRC(8901640e) SHA1(abbc0026c64c55a6cbaca5e3e5dcb1a7536e91b3), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v2.20", "ROM Version 2.20") // "        D-50    Ver 2.20                Thanks to Eric & Adrian.  14J7 D.420/7.5"
	ROMX_LOAD("roland_d-50__r15179866__lh531306__8803_d.v2.20.27c512.ic22", 0x0000, 0x10000, CRC(5db7c340) SHA1(ada0e65bf04ec24188c01b03ad4e0087d88439a8), ROM_BIOS(3)) // "Roland D-50 // R15179866 // LH531306 // 8803 D" Mask ROM (23c512 equivalent)
	ROM_SYSTEM_BIOS(4, "v2.10", "ROM Version 2.10") // "        D-50    Ver 2.10                Thanks to Eric & Adrian.  16G7 D.400/7.4"
	ROMX_LOAD("d50__v.2.10.27c512.ic22", 0x00000, 0x10000, CRC(d1387c54) SHA1(904cf9daf296a66d3c4969266541983081e19471), ROM_BIOS(4))
	// missing 2.00

	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("d78312g-022_15179266.ic25", 0x0000, 0x2000, CRC(9564903f) SHA1(f68ed97a06764ee000fe6e9d7b39017165f0efc4)) // 8-digit Roland part number not printed on IC
	ROM_COPY("progrom", 0x0000, 0x0000, 0x2000)

	ROM_REGION(0x80000, "pcm", 0)
	ROM_LOAD("roland__r15179858_8801ebi__tc534000p-7477.ic30", 0x00000, 0x80000, CRC(e2aed2d9) SHA1(e9f5b38b9b5fce04beb4cf871401e821a42edacb)) // A+B "Roland || R15179858 8801EBI || TC534000P-7477" 512KiB Mask ROM @ ic30
	// ic29 is empty on boards with tc534000-sized Mask ROMs
ROM_END

ROM_START(d50o) // Older PCB with silkscreen "Roland || D-50 MAIN BOARD || ASSY 76180090" and trace layer ID "22925445 00" and a PGA LA32 "R15229851 || MB87136A || 8706 E02" at IC31, though the oldest service manual supposedly claims it could have an "MB87136" with no A as well. This board only supports the split PCM roms unless hand-modified. This board has a factory greenwire fix for a missing trace from the IC8 Chorus ASIC MB87137-001 pin 59 to the IC1 DRAM D41416 pin 17.
	ROM_REGION(0x10000, "progrom", 0)
	ROM_SYSTEM_BIOS(0, "v1.10cfw", "ROM Version 1.10 maybe user patched") // "        D-50    Ver 1.10                Thanks to Eric & Adrian.  30G7 C.410/7.4"
	ROMX_LOAD("roland__1-11__2-1-96_greg.v1.10.ic22", 0x00000, 0x10000, CRC(7fc199c5) SHA1(3fc889d6c44f8b40de6619a3427d0c89730dd9b3), ROM_BIOS(0)) // this particular dump came from a handwritten label eprom chip labeled "Roland || 1-11 2/1/96 Greg" so it could be original code, but it was a hand-made update chip by some operator in the past. It is possible this code has a user-bugfix applied to it, though, given the 1-11 in the label.
	// missing 1.0.7
	ROM_SYSTEM_BIOS(1, "v1.06", "ROM Version 1.06") // "Thanks to Eric & Adrian.  01E7 C.36a/5.4        D-50    Ver 1.06                "
	ROMX_LOAD("d50-v1.06.ic22", 0x00000, 0x10000, CRC(ccba4e46) SHA1(ce56321226dbbf7dbfac2ad344da447ad6448ee8), ROM_BIOS(1))
	// missing 1.0.5
	ROM_SYSTEM_BIOS(2, "v1.04", "ROM Version 1.04") // "Thanks to Eric & Adrian.  08D7 C.35r/5.3        D-50    Ver 1.04                "
	ROMX_LOAD("d-50__1.0.4.mbm27c512.ic22", 0x00000, 0x10000, CRC(d871451e) SHA1(e692f3553ce6d61633c7551e98ad86f5c40e6449), ROM_BIOS(2)) // "D-50 // 1.0.4" on an MBM27c512 @ ic22 (original roland sticker? not sure, no pic)

	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("d78312g-017_15179261.ic25", 0x0000, 0x2000, NO_DUMP) // not compatible with newer firmware
	ROM_COPY("progrom", 0x0000, 0x0000, 0x2000)

	ROM_REGION(0x80000, "pcm", 0)
	ROM_LOAD("roland_a__r15179835_8710eai__tc532000p-7469.ic30", 0x00000, 0x40000, CRC(1461c0fb) SHA1(55257e70bcf003439b76f96b7ae7e45a3cf24276)) // A "Roland A || R15179835 8710EAI || TC532000P-7469" 256KiB Mask ROM @ ic30; this is identical to the first half of the 512KiB ROM; verified from original ic; also seen with 8736 datecode
	ROM_LOAD("roland_b__r15179836_8710eai__tc532000p-7470.ic29", 0x40000, 0x40000, CRC(e50599bf) SHA1(487c62f2ef7baccf2421059a940fa707e27aefb2)) // B "Roland B || R15179836 8710EAI || TC532000P-7470" 256KiB Mask ROM @ ic29; this is identical to the second half of the 512KiB ROM; verified from original ic; also seen with 8736 datecode
ROM_END

ROM_START(d550) // Newer PCB with silkscreen "Roland || D-50, D-550 || MAIN BOARD" and a checkbox for which board it is intended for "[ ]D-50 | ASSY 76180090 || [O]D-550 | ASSY 79379050" and trace layer ID "22925445 05" and a QFP LA32 "R15229896 || LA32 || 8816Q26" at IC31, This board supports either the split or the combined PCM roms.
	ROM_REGION(0x10000, "progrom", 0)
	ROM_SYSTEM_BIOS(0, "v1.02", "ROM Version 1.02") // "        D-550   Ver 1.02                Thanks to Eric & Adrian.  20G7 D.000/1.0"
	ROMX_LOAD("roland_d-550_v1.02_eprom_firmware.ic22", 0x00000, 0x10000, CRC(11b54d24) SHA1(d11bdb50c49edababec73a936346a6f918dd0949), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v1.01", "ROM Version 1.01") // "        D-550   Ver 1.01                Thanks to Eric & Adrian.  20G7 D.000/0.9"
	ROMX_LOAD("roland_d-550_ver_1.01.ic22", 0x00000, 0x10000, CRC(c267019f) SHA1(314616d14b91e8c8733aee5dd64736fda8ff6904), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v1.0.0", "ROM Version 1.0.0") // "        D-550   Ver 1.00                Thanks to Eric & Adrian.  20G7 D.000/0.5"
	ROMX_LOAD("d-550__1.0.0.mbm27c512.ic22", 0x00000, 0x10000, CRC(f8ec84c3) SHA1(00b2b74009bdc0747e11bf57d4dc6c39424c7669), ROM_BIOS(2))

	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("d78312g-022_15179266.ic25", 0x0000, 0x2000, NO_DUMP)
	ROM_COPY("progrom", 0x0000, 0x0000, 0x2000)

	ROM_REGION(0x80000, "pcm", 0)
	ROM_LOAD("roland__r15179858_8801ebi__tc534000p-7477.ic30", 0x00000, 0x80000, CRC(e2aed2d9) SHA1(e9f5b38b9b5fce04beb4cf871401e821a42edacb)) // A+B "Roland || R15179858 8801EBI || TC534000P-7477" 512KiB Mask ROM @ ic30
	// ic29 is empty on boards with tc534000-sized Mask ROMs
ROM_END

} // anonymous namespace


SYST(1987, d50,  0,   0, d50,  d50, roland_d50_state, empty_init, "Roland", "D-50 Linear Synthesizer (Ver. 2.xx)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
SYST(1987, d50o, d50, 0, d50,  d50, roland_d50_state, empty_init, "Roland", "D-50 Linear Synthesizer (Ver. 1.xx)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
SYST(1987, d550, d50, 0, d550, d550, roland_d50_state, empty_init, "Roland", "D-550 Linear Synthesizer", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



roland_d70.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:giulioz, ValleyBell
/****************************************************************************

    Driver for Roland D-70 synthesizer.
    Derived by the CM32P driver by ValleyBell

****************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/mcs96/i8x9x.h"
#include "cpu/mcs96/i8xc196.h"
#include "machine/timer.h"
#include "sound/roland_lp.h"
#include "video/t6963c.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "multibyte.h"

#include <queue>

#include "roland_d70.lh"


namespace {

// unscramble address: ROM dump offset -> proper (descrambled) offset
template <typename T> constexpr auto UNSCRAMBLE_ADDR_INT(T offset) {
	return bitswap<19>(offset, 18, 17, 15, 14, 16, 12, 11, 7, 9, 13, 10, 8, 3, 2, 1, 6, 4, 5, 0);
}
// scramble address: proper offset -> ROM dump offset
template <typename T> constexpr auto SCRAMBLE_ADDR_INT(T offset) {
	return bitswap<19>(offset, 18, 17, 14, 16, 15, 9, 13, 12, 8, 10, 7, 11, 3, 1, 2, 6, 5, 4, 0);
}

constexpr u8 UNSCRAMBLE_DATA(u8 data) { return bitswap<8>(data, 1, 2, 7, 3, 5, 0, 4, 6); }

// Bitmasks for the display board interface via PORT1
static constexpr u8 CONT_MASK = 0b10000000;
static constexpr u8 RW_MASK   = 0b01000000;
static constexpr u8 AD_MASK   = 0b00100000;
static constexpr u8 SCK_MASK  = 0b00010000;
static constexpr u8 SI_MASK   = 0b00001000;
static constexpr u8 SO_MASK   = 0b00000100;
static constexpr u8 ACK_MASK  = 0b00000010;
static constexpr u8 ENCO_MASK = 0b00000001;

static INPUT_PORTS_START(d70)
	PORT_START("KEY0")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Performance") PORT_CODE(KEYCODE_K)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Patch") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Tone") PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A/B") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Int/Card") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Command") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Write") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Enter") PORT_CODE(KEYCODE_T)

	PORT_START("KEY1")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Bank 1")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Bank 2")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Bank 3")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Bank 4")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Bank 5")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Bank 6")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Bank 7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Bank 8")

	PORT_START("KEY2")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Number 1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Number 2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Number 3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Number 4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Number 5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Number 6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Number 7") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Number 8") PORT_CODE(KEYCODE_8)

	PORT_START("KEY3")
	// NOTE: Signals for DEC and INC buttons seem to be incorrectly described
	//       (swapped) on the schematics available in the service manual.
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Dec/Del") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Inc/Ins") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Midi Out") PORT_CODE(KEYCODE_N)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Tone Display") PORT_CODE(KEYCODE_B)

	PORT_START("KEY4")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Exit") PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F5") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F4") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F3") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F2") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F1") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("User") PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part") PORT_CODE(KEYCODE_COMMA)

	PORT_START("KEY5")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Edit") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Portamento")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Resonance")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Pan")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Tuning") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Attack")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Release")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PCM Card") PORT_CODE(KEYCODE_C)

	PORT_START("KEY6")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Play") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Solo")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Cutoff")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Level")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Upper 4")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Upper 3")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Lower 2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Lower 1")

	PORT_START("KEY7")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Effect/Ctrl") PORT_CODE(KEYCODE_M)

	PORT_START("PROTECT_SW")
	PORT_DIPNAME(0x01, 0x01, "Memory Protect Switch")
	PORT_DIPSETTING(   0x00, DEF_STR(On))
	PORT_DIPSETTING(   0x01, DEF_STR(Off))

	PORT_START("SLIDER0")
	PORT_ADJUSTER(100, "MODU")

	PORT_START("SLIDER1")
	PORT_ADJUSTER(100, "AFTER")

	PORT_START("SLIDER2")
	PORT_ADJUSTER(100, "C2")

	PORT_START("SLIDER3")
	PORT_ADJUSTER(100, "C1")

	PORT_START("SLIDER4")
	PORT_ADJUSTER(100, "LOWER (1)")

	PORT_START("SLIDER5")
	PORT_ADJUSTER(100, "LOWER (2)")

	PORT_START("SLIDER6")
	PORT_ADJUSTER(100, "UPPER (3)")

	PORT_START("SLIDER7")
	PORT_ADJUSTER(100, "UPPER (4)")
INPUT_PORTS_END


class roland_d70_state : public driver_device
{
public:
	roland_d70_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_bank_view(*this, "bank"),
		m_ram(*this, "ram", 128 * 1024, ENDIANNESS_LITTLE),
		m_dsp_ram(*this, "dsp_ram", 8 * 1024, ENDIANNESS_LITTLE),
		m_rom_bank(*this, "rom_bank"),
		m_ram_bank(*this, "ram_bank"),
		m_pcm_rom(*this, "pcm"),
		m_cpu(*this, "maincpu"),
		m_pcm(*this, "pcm"),
		m_lcd(*this, "lcd"),
		m_midi_timer(*this, "midi_timer"),
		m_keys(*this, "KEY%u", 0),
		m_sliders(*this, "SLIDER%u", 0),
		m_protect_sw(*this, "PROTECT_SW"),
		m_selected_slider(0),
		m_sw_scan_index(0),
		m_sw_scan_bank(-1),
		m_sw_scan_state(0xff),
		m_sw_scan_mode(0),
		m_sw_scan_current_out(0xff),
		m_midi_rx(0),
		m_midi_pos(0)
	{
	}

	void d70(machine_config &config) ATTR_COLD;
	void init_d70() ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void lcd_map(address_map &map) ATTR_COLD;
	void lcd_palette(palette_device &palette) const ATTR_COLD;

	void bank_w(u8 data);
	u8 ksga_io_r(offs_t offset);
	void ksga_io_w(offs_t offset, u8 data);
	u16 port0_r();
	u8 port1_r();
	u8 port2_r();
	void port1_w(u8 data);
	void port2_w(u8 data);
	u8 dsp_io_r(offs_t offset);
	void dsp_io_w(offs_t offset, u8 data);
	u8 tvf_io_r(offs_t offset);
	void tvf_io_w(offs_t offset, u8 data);
	u8 snd_io_r(offs_t offset);
	void snd_io_w(offs_t offset, u8 data);

	u8 ach0_r();
	u8 ach1_r();
	u8 ach2_r();
	u8 ach3_r();
	u8 ach4_r();

	TIMER_DEVICE_CALLBACK_MEMBER(midi_timer_cb);
	TIMER_DEVICE_CALLBACK_MEMBER(samples_timer_cb);

	void midi_in_w(int state);

	void d70_map(address_map &map) ATTR_COLD;

	void descramble_rom_internal(u8 *dst, const u8 *src) ATTR_COLD;
	void descramble_rom_external(u8 *dst, const u8 *src) ATTR_COLD;

	memory_view m_bank_view;
	memory_share_creator<u16> m_ram;
	memory_share_creator<u8> m_dsp_ram;
	required_memory_bank m_rom_bank;
	required_memory_bank m_ram_bank;
	required_region_ptr<u8> m_pcm_rom;
	required_device<i8x9x_device> m_cpu;
	required_device<mb87419_mb87420_device> m_pcm;
	required_device<t6963c_device> m_lcd;
	required_device<timer_device> m_midi_timer;
	required_ioport_array<8> m_keys;
	required_ioport_array<8> m_sliders;
	required_ioport m_protect_sw;

	u8 m_sound_io_buffer[0x100];
	u8 m_dsp_io_buffer[0x80];
	u8 m_selected_slider;
	int m_sw_scan_index;
	int m_sw_scan_bank;
	u8 m_sw_scan_state;
	u8 m_sw_scan_mode;
	u8 m_sw_scan_current_out;
	u8 m_midi_rx;
	int m_midi_pos;
	std::queue<u8> midi_queue;
};

void roland_d70_state::machine_start() {
	m_bank_view.select(0);
	m_rom_bank->configure_entries(0, 8, memregion("maincpu")->base(), 0x4000);
	m_ram_bank->configure_entries(0, 2, &m_ram[0], 0x4000);
	m_cpu->space(AS_PROGRAM).install_ram(0xc000, 0xffff, &m_ram[0]);
}

void roland_d70_state::bank_w(u8 data) {
	m_bank_view.select(BIT(data, 5));
	m_rom_bank->set_entry(data & 0x07);
	m_ram_bank->set_entry(data & 0x01);
}

u8 roland_d70_state::ksga_io_r(offs_t offset) {
	return 0;
}

void roland_d70_state::ksga_io_w(offs_t offset, u8 data) {
}

void roland_d70_state::lcd_map(address_map &map) {
	map(0x0000, 0x7fff).ram();
}

void roland_d70_state::midi_in_w(int state) {
	if (m_midi_pos == 0 || m_midi_pos == 9) {
		m_midi_pos += 1;
	} else if (m_midi_pos == 10) {
		midi_queue.push(m_midi_rx);
		// logerror("midi enqueued %x\n", m_midi_rx);
		m_midi_rx = 0;
		m_midi_pos = 0;
	} else {
		m_midi_rx |= state << (m_midi_pos - 1);
		m_midi_pos += 1;
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(roland_d70_state::midi_timer_cb) {
	// CPU doesn't have a proper serial interface so we are forced
	// to simulate it this way for now
	if (midi_queue.empty())
		return;

	u8 midi = midi_queue.front();
	if (midi == 0x90)
		midi = 0x9a;
	if (midi == 0x80)
		midi = 0x8a;
	midi_queue.pop();
	logerror("midi_in %02x\n", midi);
	m_cpu->serial_w(midi);
}

u16 roland_d70_state::port0_r() {
	return 0x00;
}

u8 roland_d70_state::port1_r() {
	m_sw_scan_current_out = m_ram[0x0f / 2] >> 8;
	// logerror("p1r %02x\n", m_sw_scan_current_out);
	return m_sw_scan_current_out;
}

void roland_d70_state::port1_w(u8 data) {
	// logerror("p1w %02x\n", data);
	// m_sw_scan_current_out = data & ~(SO_MASK);

	if (data & RW_MASK) {
		if ((data & CONT_MASK) && !(m_sw_scan_state & CONT_MASK)) {
			m_sw_scan_index = 0;
			m_sw_scan_bank += 1;
		}

		// Clock cycle
		if ((data & SCK_MASK) && !(m_sw_scan_state & SCK_MASK)) {
			if ((data & AD_MASK)) {
				if (m_sw_scan_mode == 0) {
					m_sw_scan_current_out &= ~(SO_MASK);
				} else if (m_sw_scan_mode == 1) {
					u8 buttonState = m_sw_scan_bank != -1 ? BIT(m_keys[m_sw_scan_bank]->read(), m_sw_scan_index) : 1;

					if (buttonState) {
						m_sw_scan_current_out |= SO_MASK;
					} else {
						m_sw_scan_current_out &= ~(SO_MASK);
					}
					m_sw_scan_index += 1;
				}
			} else {
				m_sw_scan_mode += 1;
			}
		}
	} else {
		m_sw_scan_mode = 0;
		m_sw_scan_index = 0;
		m_sw_scan_bank = -1;
	}

	m_sw_scan_state = data;

	m_ram[0x0f / 2] = (m_ram[0x0f / 2] & 0x00ff) | (u16(m_sw_scan_current_out) << 8);
}

u8 roland_d70_state::port2_r() {
	u8 value = m_selected_slider << 5;
	if (m_protect_sw->read())
		value |= (1 << 4);
	return value;
}

void roland_d70_state::port2_w(u8 data) {
	m_selected_slider = data >> 5;
}

u8 roland_d70_state::dsp_io_r(offs_t offset) {
	return m_dsp_io_buffer[offset];
}

void roland_d70_state::dsp_io_w(offs_t offset, u8 data) {
	m_dsp_io_buffer[offset] = data;
	// do read/write to some external memory, makes the RCC-CPU check pass.
	// (routine at 0x4679)
	switch (offset) {
	case 0x04:
		// write to partials?? (written in loop at 0x4375)
		break;

	case 0x06:
		m_dsp_ram[0x000 | data] = m_dsp_io_buffer[0x00] & 0x03;
		m_dsp_ram[0x100 | data] = m_dsp_io_buffer[0x01];
		m_dsp_ram[0x200 | data] = m_dsp_io_buffer[0x02];
		break;

	case 0x0a:
		m_dsp_io_buffer[0x00] = m_dsp_ram[0x000 | data];
		m_dsp_io_buffer[0x01] = m_dsp_ram[0x100 | data];
		m_dsp_io_buffer[0x02] = m_dsp_ram[0x200 | data];
		break;
	}
}

u8 roland_d70_state::tvf_io_r(offs_t offset) {
	logerror("tvf read %04x\n", offset);
	return 0;
}

void roland_d70_state::tvf_io_w(offs_t offset, u8 data) {
	logerror("tvf write %04x= %02x\n", offset, data);
}

u8 roland_d70_state::snd_io_r(offs_t offset) {
	// logerror("lp read %x\n", offset);
	// lots of offset modification magic to achieve the following:
	//  - offsets 00..1F are "sound chip read"
	//  - offsets 20..3F are a readback of what was written to registers 00..1F
	//  - This behaviour is reversed for offset 01/21, which is used for reading
	//  the PCM sample tables.
	// All this is just for making debugging easier, as it allows one to check the
	// register state using the Memory Viewer.
	if (offset == 0x01 || offset == 0x21)
		offset ^= 0x20; // remove when PCM data readback via sound chip is confirmed to work
	if (offset < 0x20)
		return m_pcm->read(offset);
	if (offset < 0x40)
		offset -= 0x20;

	if (offset == 0x01) {
		// code for reading from the PCM sample table is at 0xb027
		// The code at 0xb0ac writes to 1411/1F (??), then 1403/02 (bank), then
		// 1409/08/0b/0a (address). It waits a few cycles and at 0xb0f7 it reads the
		// resulting data from 1401.
		offs_t bank = m_sound_io_buffer[0x03];
		offs_t addr = get_u24le(&m_sound_io_buffer[0x09]);
		addr = ((addr >> 6) + 2) & 0x3ffff;
		addr |= (bank << 16);
		// write actual ROM address to 1440..1443 for debugging
		put_u32be(&m_sound_io_buffer[40], addr);
		return m_pcm_rom[addr];
	}
	return m_sound_io_buffer[offset];
}

void roland_d70_state::snd_io_w(offs_t offset, u8 data) {
	// register map
	// ------------
	// Note: 16-bit words are Little Endian, the firmware writes the odd byte is
	// first
	//  00/01 - ??
	//  02/03 - ROM bank (only bits 11-13 are used, bit 11 = PCM card, bits 12-13
	//  select between IC18/19/20) 04/05 - frequency (2.14 fixed point, 0x4000 =
	//  32000 Hz) 06/07 - volume 08/09 - sample start address, fraction (2.14
	//  fixed point, i.e. 1 byte = 0x4000) 0A/0B - sample start address (high
	//  word, i.e. address bits 2..17) 0C/0D - sample end address (high word)
	//  0E/0F - sample loop address (high word)
	//  11/13/15/17 - voice enable mask (11 = least significant 8 bits, 17 = most
	//  significant 8 bits) 1A - ?? 1F - voice select
	if (offset < 0x20) {
		m_pcm->write(offset, data);
	}
	m_sound_io_buffer[offset] = data;
}

u8 roland_d70_state::ach0_r() {
	return m_sliders[m_selected_slider & 7]->read();
}

u8 roland_d70_state::ach1_r() { return 128; } // TODO: EXT PEDAL
u8 roland_d70_state::ach2_r() { return 128; } // TODO: BENDER
u8 roland_d70_state::ach3_r() { return 128; } // TODO: BATTERY
u8 roland_d70_state::ach4_r() { return 128; } // TODO: RAM CARD (VBB)

TIMER_DEVICE_CALLBACK_MEMBER(roland_d70_state::samples_timer_cb) {
}

void roland_d70_state::lcd_palette(palette_device &palette) const {
	palette.set_pen_color(0, rgb_t(0x9f, 0xb4, 0x86));
	palette.set_pen_color(1, rgb_t(0x5e, 0x5f, 0x71));
}

void roland_d70_state::d70_map(address_map &map) {
	map(0x0100, 0x0100).w(FUNC(roland_d70_state::bank_w));
	map(0x0400, 0x07ff).rw(FUNC(roland_d70_state::ksga_io_r), FUNC(roland_d70_state::ksga_io_w));
	map(0x0800, 0x0802).rw(m_lcd, FUNC(t6963c_device::read), FUNC(t6963c_device::write)).umask16(0x00ff);
	map(0x0900, 0x09ff).rw(FUNC(roland_d70_state::snd_io_r), FUNC(roland_d70_state::snd_io_w));
	map(0x0a00, 0x0aff).rw(FUNC(roland_d70_state::dsp_io_r), FUNC(roland_d70_state::dsp_io_w));
	map(0x0c00, 0x0cff).rw(FUNC(roland_d70_state::tvf_io_r), FUNC(roland_d70_state::tvf_io_w));
	map(0x1000, 0x7fff).rom().region("maincpu", 0x1000);
	map(0x8000, 0xbfff).view(m_bank_view);
	m_bank_view[0](0x8000, 0xbfff).bankr(m_rom_bank);
	m_bank_view[1](0x8000, 0xbfff).bankrw(m_ram_bank);
	//map(0xc000, 0xffff) fixed RAM - will install on start
}

void roland_d70_state::d70(machine_config &config) {
	i8x9x_device &maincpu(N8097BH(config, m_cpu, 12_MHz_XTAL));
	maincpu.set_addrmap(AS_PROGRAM, &roland_d70_state::d70_map);
	maincpu.serial_tx_cb().set("mdout", FUNC(midi_port_device::write_txd));
	maincpu.in_p0_cb().set(FUNC(roland_d70_state::port0_r));
	maincpu.in_p1_cb().set(FUNC(roland_d70_state::port1_r));
	maincpu.out_p1_cb().set(FUNC(roland_d70_state::port1_w));
	maincpu.in_p2_cb().set(FUNC(roland_d70_state::port2_r));
	maincpu.out_p2_cb().set(FUNC(roland_d70_state::port2_w));
	maincpu.ach0_cb().set(FUNC(roland_d70_state::ach0_r));
	maincpu.ach1_cb().set(FUNC(roland_d70_state::ach1_r));
	maincpu.ach2_cb().set(FUNC(roland_d70_state::ach2_r));
	maincpu.ach3_cb().set(FUNC(roland_d70_state::ach3_r));
	maincpu.ach4_cb().set(FUNC(roland_d70_state::ach4_r));

	SPEAKER(config, "speaker", 2).front();

	MB87419_MB87420(config, m_pcm, 32.768_MHz_XTAL);
	m_pcm->int_callback().set_inputline(m_cpu, i8x9x_device::EXTINT_LINE);
	m_pcm->add_route(0, "speaker", 1.0, 0);
	m_pcm->add_route(1, "speaker", 1.0, 1);

	T6963C(config, m_lcd, 0);
	m_lcd->set_addrmap(0, &roland_d70_state::lcd_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_size(240, 64);
	screen.set_visarea_full();
	screen.set_screen_update("lcd", FUNC(t6963c_device::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(roland_d70_state::lcd_palette), 2);

	TIMER(config, m_midi_timer).configure_periodic(FUNC(roland_d70_state::midi_timer_cb), attotime::from_hz(1250));

	TIMER(config, "samples_timer").configure_periodic(FUNC(roland_d70_state::samples_timer_cb), attotime::from_hz(32000 * 2));

	midi_port_device &mdin(MIDI_PORT(config, "mdin", midiin_slot, "midiin"));
	mdin.rxd_handler().set(FUNC(roland_d70_state::midi_in_w));
	mdin.rxd_handler().append("mdthru", FUNC(midi_port_device::write_txd));

	MIDI_PORT(config, "mdout", midiout_slot, "midiout");
	MIDI_PORT(config, "mdthru", midiout_slot, "midiout");

	config.set_default_layout(layout_roland_d70);
}

void roland_d70_state::init_d70() {
	// Roland did a fair amount of scrambling on the address and data lines.
	// Only the first 0x80 bytes of the ROMs are readable text in a raw dump.
	// The U-110 actually checks some of these header bytes, but it uses
	// post-scrambling variants of offsets/values.
	u8 *src = reinterpret_cast<u8 *>(memregion("pcmorg")->base());
	u8 *dst = reinterpret_cast<u8 *>(memregion("pcm")->base());
	// descramble internal ROMs
	descramble_rom_internal(&dst[0x000000], &src[0x000000]);
	descramble_rom_internal(&dst[0x080000], &src[0x080000]);
	descramble_rom_internal(&dst[0x100000], &src[0x100000]);
	descramble_rom_internal(&dst[0x180000], &src[0x180000]);
	descramble_rom_internal(&dst[0x200000], &src[0x200000]);
	descramble_rom_internal(&dst[0x300000], &src[0x300000]);
}

void roland_d70_state::descramble_rom_internal(u8 *dst, const u8 *src) {
	for (offs_t srcpos = 0x00; srcpos < 0x80000; srcpos++) {
		const offs_t dstpos = UNSCRAMBLE_ADDR_INT(srcpos);
		dst[dstpos] = UNSCRAMBLE_DATA(src[srcpos]);
	}
}

ROM_START(d70)
	ROM_REGION(0x20000, "maincpu", 0)
	ROM_DEFAULT_BIOS("v119")
	ROM_SYSTEM_BIOS( 0, "v119", "Version 1.19 - March 9, 1993" )
	ROM_SYSTEM_BIOS( 1, "v116", "Version 1.16 - January 28, 1991" )
	ROM_SYSTEM_BIOS( 2, "v114", "Version 1.14 - September 20, 1990" )
	ROM_SYSTEM_BIOS( 3, "v112", "Version 1.12 - August 8, 1990" )
	ROM_SYSTEM_BIOS( 4, "v110", "Version 1.10 - April 19, 1990" )
	ROM_SYSTEM_BIOS( 5, "v100", "Version 1.00 - March 10, 1990" )

	ROMX_LOAD("roland_d70_v1.19_a_even.ic4", 0, 0x10000, CRC(95fcf250) SHA1(174962eb42f56aaf936aeca4db77228bf19ec97a), ROM_BIOS(0) | ROM_SKIP(1) )
	ROMX_LOAD("roland_d70_v1.19_b_odd.ic9",  1, 0x10000, CRC(a14f0ce1) SHA1(9ef2d62b1be5c38b9fa0072a5889b8ab0e47623e), ROM_BIOS(0) | ROM_SKIP(1) )

	ROMX_LOAD("roland_d70_v1.16_a_even.ic4", 0, 0x10000, CRC(761f6eac) SHA1(18bd2c8390d67f1000fae652f51a03322bb088cd), ROM_BIOS(1) | ROM_SKIP(1) )
	ROMX_LOAD("roland_d70_v1.16_b_odd.ic9",  1, 0x10000, CRC(103a1f07) SHA1(530408a8ae8d74786cb8179892b7e3a24db93000), ROM_BIOS(1) | ROM_SKIP(1) )

	ROMX_LOAD("roland_d70_v1.14_a_even.ic4", 0, 0x10000, CRC(651e7cd5) SHA1(73ad2d897f51449111064e92b515a4118fee4dad), ROM_BIOS(2) | ROM_SKIP(1) )
	ROMX_LOAD("roland_d70_v1.14_b_odd.ic9",  1, 0x10000, CRC(b3533278) SHA1(8c2019c20b60d4fbb5f5956bce672a84f515215b), ROM_BIOS(2) | ROM_SKIP(1) )

	ROMX_LOAD("roland_d70_v1.12_a_even.ic4", 0, 0x10000, CRC(86032879) SHA1(07e7545c2dae93311f7b5d77b65c548e56391748), ROM_BIOS(3) | ROM_SKIP(1) )
	ROMX_LOAD("roland_d70_v1.12_b_odd.ic9",  1, 0x10000, CRC(910cf30e) SHA1(15cb5ecb956205dc9d6254822178139a228790d8), ROM_BIOS(3) | ROM_SKIP(1) )

	ROMX_LOAD("roland_d70_v1.10_a_even.ic4", 0, 0x10000, CRC(5f7374c5) SHA1(15d9249c35c3db6a7d1df6a4f4e42f0ce99f11a5), ROM_BIOS(4) | ROM_SKIP(1) )
	ROMX_LOAD("roland_d70_v1.10_b_odd.ic9",  1, 0x10000, CRC(f024220e) SHA1(cde358c1ec205f446114f478b43269b01b8be048), ROM_BIOS(4) | ROM_SKIP(1) )

	// Seen at https://www.youtube.com/watch?v=9zGcHzpz7zo
	ROMX_LOAD("roland_d70_v1.00_a_even.ic4", 0, 0x10000, NO_DUMP, ROM_BIOS(5) | ROM_SKIP(1) )
	ROMX_LOAD("roland_d70_v1.00_b_odd.ic9",  1, 0x10000, NO_DUMP, ROM_BIOS(5) | ROM_SKIP(1) )


	ROM_REGION(0x600000, "pcmorg", 0) // ROMs before descrambling
	ROM_LOAD("roland_d70_waverom-a.bin", 0x000000, 0x80000, CRC(8e53b2a3) SHA1(4872530870d5079776e80e477febe425dc0ec1df))
	ROM_LOAD("roland_d70_waverom-e.bin", 0x080000, 0x80000, CRC(d46cc7a4) SHA1(d378ac89a5963e37f7c157b3c8e71892c334fd7b))
	ROM_LOAD("roland_d70_waverom-b.bin", 0x100000, 0x80000, CRC(c8220761) SHA1(49e55fa672020f95fd9c858ceaae94d6db93df7d))
	ROM_LOAD("roland_d70_waverom-f.bin", 0x180000, 0x80000, CRC(d4b01f5e) SHA1(acd867d68e49e5f59f1006ed14a7ca197b6dc4af))
	ROM_LOAD("roland_d70_waverom-c.bin", 0x200000, 0x80000, CRC(733c4054) SHA1(9b6b59ab74e5bf838702abb087c408aaa85b7b1f))
	ROM_LOAD("roland_d70_waverom-d.bin", 0x300000, 0x80000, CRC(b6c662d2) SHA1(3fcbcfd0d8d0fa419c710304c12482e2f79a907f))
	ROM_REGION(0x600000, "pcm", ROMREGION_ERASEFF) // ROMs after descrambling

	ROM_REGION(0x400, "lcd:cgrom", 0)
	ROM_LOAD("t6963c_0101.bin", 0x000, 0x400, CRC(547d118b) SHA1(0dd3e3acd3d47e6ece644c98c390fc86587373e9))
	// This t6963c_0101 internal CG ROM is similar to lm24014w_0101.bin which may be
	// used as a replacement
ROM_END

} // anonymous namespace

SYST(1991, d70, 0, 0, d70, d70, roland_d70_state, init_d70, "Roland", "D-70 Super LA Synthesizer", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)



roland_jd800.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Roland JD-800 synthesizer.

****************************************************************************/

#include "emu.h"
//#include "bus/midi/midi.h"
//#include "cpu/h8/h8330.h"
#include "cpu/h8500/h8532.h"
//#include "machine/nvram.h"
//#include "machine/ssc1000.h"


namespace {

class roland_jd800_state : public driver_device
{
public:
	roland_jd800_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void jd800(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<h8532_device> m_maincpu;
};


void roland_jd800_state::mem_map(address_map &map)
{
	map(0xc0000, 0xfffff).rom().region("progrom", 0);
}


static INPUT_PORTS_START(jd800)
INPUT_PORTS_END

void roland_jd800_state::jd800(machine_config &config)
{
	HD6435328(config, m_maincpu, 20_MHz_XTAL); // MD2 = VCC, MD1 = MD0 = GND
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_jd800_state::mem_map);

	//SSC1000(config, "keybc", 20_MHz_XTAL / 2);

	//NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // HM62256LFP-12SLT (@ IC29) + CR2032 battery

	//H8330(config, "iomcu", 20_MHz_XTAL / 2).set_disable(); // MD0 = MD1 = +5V (single-chip mode)

	//MB83371A(config, "pcm", 26.195_MHz_XTAL);
	//TC24SC220AF_001(config, "mixer", 28.224_MHz_XTAL);
}

ROM_START(jd800)
	ROM_REGION(0x8000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD("r15199765.ic36", 0x0000, 0x8000, NO_DUMP)

	ROM_REGION(0x40000, "progrom", 0)
	ROM_LOAD("roland_r15209315_9119-d.ic31", 0x00000, 0x40000, CRC(04565c05) SHA1(08c0fdc79e6d8d1b5cd3261c977da0244428057c)) // version 1.01

	ROM_REGION(0x4000, "iomcu", ROMREGION_ERASE00) // H8/330
	ROM_LOAD("r15199742.ic5", 0x0000, 0x4000, NO_DUMP)

	ROM_REGION(0x300000, "pcm", 0)
	ROM_LOAD("roland_r15209290_mb838000-20_2a6-aa_9114-r11.ic25", 0x000000, 0x100000, CRC(d743cec8) SHA1(7ac9943f1769913d7bbf95c5cca76ca711179641))
	ROM_LOAD("roland_r15209291_mb838000-20_2a7-aa_9119-r15.ic26", 0x100000, 0x100000, CRC(06098658) SHA1(5c364e3b9c1b206fe94fbaa190caa17b01189937))
	ROM_LOAD("roland_r15209305_mb838000-20_2a8-aa_9102-r01.ic1",  0x200000, 0x100000, CRC(5c83e539) SHA1(2861baa9a6f42a1257c241927815ed366becd7dc)) // on card board
ROM_END

} // anonymous namespace


SYST(1991, jd800, 0, 0, jd800, jd800, roland_jd800_state, empty_init, "Roland", "JD-800 Programmable Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



roland_jv80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Roland JV-80, JV-880 and related synthesizers.

****************************************************************************/

#include "emu.h"
#include "cpu/h8500/h8510.h"
#include "cpu/h8500/h8532.h"
#include "machine/nvram.h"
#include "sound/roland_gp.h"


namespace {

class roland_jv80_state : public driver_device
{
public:
	roland_jv80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pcm(*this, "pcm")
	{
	}

	void jv880(machine_config &config);

private:
	void jv880_mem_map(address_map &map) ATTR_COLD;

	required_device<h8532_device> m_maincpu;
	required_device<tc6116_device> m_pcm;
};


void roland_jv80_state::jv880_mem_map(address_map &map)
{
	map(0x00000, 0x07fff).rom().region("maincpu", 0);
	map(0x08000, 0x0dfff).ram().mirror(0xa0000);
	map(0x0f000, 0x0f3ff).rw(m_pcm, FUNC(tc6116_device::read), FUNC(tc6116_device::write));
	map(0x10000, 0x3ffff).rom().region("progrom", 0x10000);
	map(0x40000, 0x4ffff).rom().region("progrom", 0);
	map(0xa0000, 0xbffff).ram();
	map(0xc0000, 0xd7fff).ram().share("nvram");
	// map(0xe0000, 0xf7fff).ram().share("cardram");
}

static INPUT_PORTS_START(jv880)
INPUT_PORTS_END

void roland_jv80_state::jv880(machine_config &config)
{
	HD6435328(config, m_maincpu, 20_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_jv80_state::jv880_mem_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // LC36256AML-10 (IC18) + CR2032 battery

	TC6116(config, "pcm", 23.2_MHz_XTAL);
}

class roland_rd500_state : public driver_device
{
public:
	roland_rd500_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pcm(*this, "pcm")
	{
	}

	void rd500(machine_config &config);

private:
	void rd500_mem_map(address_map &map) ATTR_COLD;

	u8 keyscan_r(offs_t offset);
	void keyscan_w(offs_t offset, u8 data);

	required_device<h8510_device> m_maincpu;
	required_device<tc6116_device> m_pcm;
};

void roland_rd500_state::rd500_mem_map(address_map &map)
{
	map(0x000000, 0x07ffff).rom().region("progrom", 0);
	map(0x900000, 0x90ffff).ram();
	map(0xa00000, 0xa0ffff).rw(m_pcm, FUNC(tc6116_device::read), FUNC(tc6116_device::write));
	map(0xc00000, 0xc0ffff).rw(FUNC(roland_rd500_state::keyscan_r), FUNC(roland_rd500_state::keyscan_w));
}

u8 roland_rd500_state::keyscan_r(offs_t offset)
{
	return 0;
}

void roland_rd500_state::keyscan_w(offs_t offset, u8 data)
{
}

static INPUT_PORTS_START(rd500)
INPUT_PORTS_END

void roland_rd500_state::rd500(machine_config &config)
{
	HD6415108(config, m_maincpu, 20_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_rd500_state::rd500_mem_map);

	TC6116(config, "pcm", 23.2_MHz_XTAL);
}

ROM_START(jv880)
	ROM_DEFAULT_BIOS("v1.0.1")
	ROM_SYSTEM_BIOS(0, "v1.0.0", "ROM Version 1.0.0")
	ROM_SYSTEM_BIOS(1, "v1.0.1", "ROM Version 1.0.1")

	ROM_REGION(0x8000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD("roland_r15199810_6435328b86f.ic16", 0x0000, 0x8000, CRC(b3d611b1) SHA1(6c1b59905b361ac3c4803d39589406bc3e1d0647))

	ROM_REGION(0x40000, "progrom", 0)
	ROMX_LOAD("roland_jv-880_v1.00.ic17", 0x00000, 0x40000, CRC(b266efe9) SHA1(282116e8e8471053cf159d22675931592b7f7c8f), ROM_BIOS(0))
	ROMX_LOAD("roland_jv-880_v1.01.ic17", 0x00000, 0x40000, CRC(5f19c95f) SHA1(38ec496f16dfa02d35f934cf32d8302aaf5f236e), ROM_BIOS(1))

	ROM_REGION(0x400000, "waverom", 0)
	ROM_LOAD("roland-a_r15209312_lh5375n2.ic27", 0x000000, 0x200000, CRC(1348c0dc) SHA1(37e28498351fb502f6d43398d288a026c02b446d))
	ROM_LOAD("roland-b_r15209313_lh5375n3.ic25", 0x200000, 0x200000, CRC(d55fcf90) SHA1(963ce75b6668dab377d3a2fd895630a745491be5))
ROM_END

ROM_START(rd500)
	ROM_REGION16_BE(0x80000, "progrom", 0)
	ROM_LOAD16_WORD_SWAP("rd500_rom.bin", 0x00000, 0x80000, CRC(668fc7e9) SHA1(59e28d3e2190902dd6fd02a9820f96e383781178))

	ROM_REGION(0x800000, "waverom", 0)
	ROM_LOAD("roland-a_r00342978.ic4", 0x000000, 0x200000, CRC(c885bf4f) SHA1(e14f0f4a8181e09fae7db10130e4ed3cd6bf5a34))
	ROM_LOAD("roland-b_r00343012.ic5", 0x200000, 0x200000, CRC(ceb02d33) SHA1(9f6969d94598c68902188085d0c91fb8b300d762))
	ROM_LOAD("roland-c_r00343023.ic6", 0x400000, 0x200000, CRC(f627cdb7) SHA1(7b834fee5db5a7377ec7f66172d0fa3096cefbc9))
	ROM_LOAD("roland-d_r00343034.ic7", 0x600000, 0x200000, CRC(c06be973) SHA1(2e7ce8a91a6f92648f73d7ff8c1d608f62df9aab))
ROM_END

} // anonymous namespace


//SYST(1992, jv80, 0, 0, jv80, jv80, roland_jv80_state, empty_init, "Roland", "JV-80 Multi Timbral Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1992, jv880, 0, 0, jv880, jv880, roland_jv80_state, empty_init, "Roland", "JV-880 Multi Timbral Synthesizer Module", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1994, rd500, 0, 0, rd500, rd500, roland_rd500_state, empty_init, "Roland", "RD-500 Digital Piano", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



roland_jx3p.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Roland JX-3P synthesizer and similar modules.

****************************************************************************/

#include "emu.h"
#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
//#include "bus/midi/midi.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/adc0804.h"
#include "machine/nvram.h"
#include "pg200.h"
#include "machine/pit8253.h"
#include "machine/rescap.h"


namespace {

class roland_jx3p_state : public driver_device
{
public:
	roland_jx3p_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ifcpu(*this, "ifcpu")
		, m_cartslot(*this, "cartslot")
		, m_ifpit(*this, "ifpit%u", 0U)
	{
	}

	void jx3p(machine_config &config);
	void mks30(machine_config &config);
	void gr700(machine_config &config);

private:
	void prescale_w(u8 data);
	void dac_w(offs_t offset, u8 data);
	void sw_interface_w(u8 data);
	void led_display_w(offs_t offset, u8 data);
	void analog_select_w(u8 data);

	void if_anlg_mux_w(u8 data);

	void prog_map(address_map &map) ATTR_COLD;
	void common_ext_map(address_map &map) ATTR_COLD;
	void jk3p_ext_map(address_map &map) ATTR_COLD;
	void mks30_ext_map(address_map &map) ATTR_COLD;
	void if_prog_map(address_map &map) ATTR_COLD;
	void if_ext_map(address_map &map) ATTR_COLD;

	required_device<mcs51_cpu_device> m_maincpu;
	optional_device<mcs51_cpu_device> m_ifcpu;
	optional_device<generic_cartslot_device> m_cartslot;
	optional_device_array<pit8253_device, 4> m_ifpit;
};


void roland_jx3p_state::prescale_w(u8 data)
{
}

void roland_jx3p_state::dac_w(offs_t offset, u8 data)
{
}

void roland_jx3p_state::sw_interface_w(u8 data)
{
}

void roland_jx3p_state::led_display_w(offs_t offset, u8 data)
{
}

void roland_jx3p_state::analog_select_w(u8 data)
{
}

void roland_jx3p_state::if_anlg_mux_w(u8 data)
{
}

void roland_jx3p_state::prog_map(address_map &map)
{
	map.global_mask(0x1fff);
	map(0x0000, 0x1fff).rom().region("program", 0);
}

void roland_jx3p_state::common_ext_map(address_map &map)
{
	map(0x0000, 0x0000).mirror(0x1ff).w(FUNC(roland_jx3p_state::prescale_w));
	map(0x0200, 0x0203).mirror(0x1fc).w("counter1", FUNC(pit8253_device::write));
	map(0x0400, 0x0403).mirror(0x1fc).w("counter2", FUNC(pit8253_device::write));
	map(0x0600, 0x0603).mirror(0x1fc).w("counter3", FUNC(pit8253_device::write));
	map(0x0800, 0x0803).mirror(0x1fc).w("counter4", FUNC(pit8253_device::write));
	map(0x0a00, 0x0a00).mirror(0x10f).select(0xf0).w(FUNC(roland_jx3p_state::dac_w));
	map(0x0c08, 0x0c08).mirror(0x1f0).portr("SWSCN0");
	map(0x0c09, 0x0c09).mirror(0x1f0).portr("SWSCN1");
	map(0x0c0a, 0x0c0a).mirror(0x1f0).portr("SWSCN2");
	map(0x0c0b, 0x0c0b).mirror(0x1f0).portr("SWSCN3");
	map(0x0e00, 0x0e00).mirror(0x10f).select(0xf0).w(FUNC(roland_jx3p_state::led_display_w));
}

void roland_jx3p_state::jk3p_ext_map(address_map &map)
{
	map.global_mask(0x1fff);
	common_ext_map(map);
	map(0x0c00, 0x0c00).mirror(0x1f0).portr("KYSCN0");
	map(0x0c01, 0x0c01).mirror(0x1f0).portr("KYSCN1");
	map(0x0c02, 0x0c02).mirror(0x1f0).portr("KYSCN2");
	map(0x0c03, 0x0c03).mirror(0x1f0).portr("KYSCN3");
	map(0x0c04, 0x0c04).mirror(0x1f0).portr("KYSCN4");
	map(0x0c05, 0x0c05).mirror(0x1f0).portr("KYSCN5");
	map(0x0c06, 0x0c06).mirror(0x1f0).portr("KYSCN6");
	map(0x0c07, 0x0c07).mirror(0x1f0).portr("KYSCN7");
	map(0x0c0c, 0x0c0c).mirror(0x1f0).portr("SWSCN4");
	map(0x1000, 0x1000).mirror(0x1ff).w(FUNC(roland_jx3p_state::sw_interface_w));
	map(0x1800, 0x1fff).ram().share("nvram");
}

void roland_jx3p_state::mks30_ext_map(address_map &map)
{
	map.global_mask(0x3fff);
	common_ext_map(map);
	map(0x0c00, 0x0c00).mirror(0x1ff).w(FUNC(roland_jx3p_state::sw_interface_w));
	map(0x1000, 0x17ff).mirror(0x800).ram().share("nvram");
	map(0x2000, 0x3fff).rw(m_cartslot, FUNC(generic_cartslot_device::read_ram), FUNC(generic_cartslot_device::write_ram));
}

void roland_jx3p_state::if_prog_map(address_map &map)
{
	map.global_mask(0x1fff);
	map(0x0000, 0x1fff).rom().region("interface", 0);
}

void roland_jx3p_state::if_ext_map(address_map &map)
{
	map.global_mask(0xfff);
	map(0x000, 0x7ff).ram();
	map(0x800, 0x803).mirror(0x788).rw(m_ifpit[0], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x810, 0x813).mirror(0x788).rw(m_ifpit[1], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x820, 0x823).mirror(0x788).rw(m_ifpit[2], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x830, 0x833).mirror(0x788).rw(m_ifpit[3], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x840, 0x840).mirror(0x78f).rw("adc", FUNC(adc0803_device::read), FUNC(adc0803_device::write));
}


static INPUT_PORTS_START(jx3p)
	PORT_START("KYSCN0")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KYSCN1")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KYSCN2")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KYSCN3")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KYSCN4")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KYSCN5")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KYSCN6")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KYSCN7")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SWSCN0")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SWSCN1")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SWSCN2")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SWSCN3")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SWSCN4")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)
INPUT_PORTS_END

static INPUT_PORTS_START(mks30)
	PORT_START("SWSCN0")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SWSCN1")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SWSCN2")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SWSCN3")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)
INPUT_PORTS_END


void roland_jx3p_state::jx3p(machine_config &config)
{
	I8031(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_jx3p_state::prog_map);
	m_maincpu->set_addrmap(AS_IO, &roland_jx3p_state::jk3p_ext_map);
	m_maincpu->port_out_cb<1>().set(FUNC(roland_jx3p_state::analog_select_w));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // TC5517APL + battery

	PIT8253(config, "counter1");
	PIT8253(config, "counter2");
	PIT8253(config, "counter3");
	PIT8253(config, "counter4");

	PG200(config, "programmer");
}

void roland_jx3p_state::mks30(machine_config &config)
{
	jx3p(config);
	m_maincpu->set_addrmap(AS_IO, &roland_jx3p_state::mks30_ext_map);

	GENERIC_CARTSLOT(config, m_cartslot, generic_plain_slot, nullptr, "mks30_cart");
}

void roland_jx3p_state::gr700(machine_config &config)
{
	mks30(config);

	I8031(config, m_ifcpu, 12_MHz_XTAL);
	m_ifcpu->set_addrmap(AS_PROGRAM, &roland_jx3p_state::if_prog_map);
	m_ifcpu->set_addrmap(AS_IO, &roland_jx3p_state::if_ext_map);
	m_ifcpu->port_out_cb<1>().set(FUNC(roland_jx3p_state::if_anlg_mux_w));
	m_ifcpu->port_in_cb<3>().set("adc", FUNC(adc0803_device::intr_r)).lshift(4);

	for (auto &ifpit : m_ifpit)
	{
		PIT8253(config, ifpit);
		ifpit->set_clk<0>(12_MHz_XTAL / 6);
		ifpit->set_clk<1>(12_MHz_XTAL / 6);
		ifpit->set_clk<2>(12_MHz_XTAL / 6);
	}

	ADC0803(config, "adc", RES_K(10), CAP_P(150));
}

ROM_START(jx3p)
	ROM_REGION(0x2000, "program", 0)
	ROM_LOAD("v4_8c93.ic52", 0x0000, 0x2000, CRC(1c6790a2) SHA1(498c1f64d049ecba561149ae9e57f7a13972129f))
ROM_END

ROM_START(mks30)
	ROM_REGION(0x2000, "program", 0)
	ROM_LOAD("mks-30_2.0.ic46", 0x0000, 0x2000, CRC(09d6032f) SHA1(1bd8dac850e10ad53889ab2bf2ef913e0f932d96))
ROM_END

ROM_START(gr700)
	ROM_REGION(0x2000, "program", 0)
	ROM_LOAD("sh_v1.4.ic46", 0x0000, 0x2000, CRC(3c7eac76) SHA1(a8038a174d1a0dccb7197f9727b468ec9960f15a))

	ROM_REGION(0x2000, "interface", 0)
	ROM_LOAD("if_v1.4.ic17", 0x0000, 0x2000, CRC(56579d31) SHA1(b6d5b02e8952d52eff5a0bf77f7922e6d135454a))
ROM_END

} // anonymous namespace


SYST(1983, jx3p,  0, 0, jx3p,  jx3p,  roland_jx3p_state, empty_init, "Roland", "JX-3P Programmable Preset Polyphonic Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1984, mks30, 0, 0, mks30, mks30, roland_jx3p_state, empty_init, "Roland", "MKS-30 Planet-S MIDI Sound Module",                MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1984, gr700, 0, 0, gr700, mks30, roland_jx3p_state, empty_init, "Roland", "GR-700 Guitar Synthesizer",                        MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



roland_jx8p.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Roland JX-8P and related synthesizers.

****************************************************************************/

#include "emu.h"
#include "jx8p_synth.h"
#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
//#include "bus/midi/midi.h"
#include "cpu/m6800/m6801.h"
//#include "cpu/upd7500/upd7500.h"
#include "mb63h149.h"
#include "machine/nvram.h"
//#include "machine/pg800.h"
#include "machine/rescap.h"
#include "machine/upd7001.h"


namespace {

class roland_jx8p_state : public driver_device
{
public:
	roland_jx8p_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_assignercpu(*this, "assignercpu")
		, m_sw_scan(*this, "SCAN%u", 0U)
	{
	}

	void jx8p(machine_config &config);
	void jx8po(machine_config &config);
	void jx10(machine_config &config);
	void mks70(machine_config &config);

private:
	u8 switches_r(offs_t offset);
	void leds_w(u8 data);

	void jx8p_assigner_map(address_map &map) ATTR_COLD;
	void superjx_assigner_map(address_map &map) ATTR_COLD;

	required_device<hd6303r_cpu_device> m_assignercpu;
	required_ioport_array<8> m_sw_scan;
};


u8 roland_jx8p_state::switches_r(offs_t offset)
{
	return m_sw_scan[offset]->read();
}

void roland_jx8p_state::leds_w(u8 data)
{
}

void roland_jx8p_state::jx8p_assigner_map(address_map &map)
{
	map(0x2000, 0x3fff).rw("cartslot", FUNC(generic_slot_device::read_ram), FUNC(generic_slot_device::write_ram));
	map(0x4000, 0x4007).mirror(0x1ff8).r(FUNC(roland_jx8p_state::switches_r));
	map(0x6000, 0x6000).mirror(0x1fff).w(FUNC(roland_jx8p_state::leds_w));
	map(0x8000, 0x87ff).mirror(0x1800).rw("keyscan", FUNC(mb63h149_device::read), FUNC(mb63h149_device::write));
	map(0xa000, 0xa7ff).mirror(0x1800).ram().share("nvram");
	map(0xc000, 0xffff).rom().region("assigner", 0);
}

void roland_jx8p_state::superjx_assigner_map(address_map &map)
{
	map(0x1000, 0x17ff).mirror(0x800).rw("keyscan", FUNC(mb63h149_device::read), FUNC(mb63h149_device::write));
	map(0x2000, 0x3fff).rw("cartslot", FUNC(generic_slot_device::read_ram), FUNC(generic_slot_device::write_ram));
	map(0x4000, 0x4007).mirror(0xff8).r(FUNC(roland_jx8p_state::switches_r));
	map(0x5000, 0x5000).mirror(0xfff).w(FUNC(roland_jx8p_state::leds_w));
	map(0x6000, 0x7fff).ram().share("nvram");
	map(0x8000, 0xffff).rom().region("assigner", 0);
}


static INPUT_PORTS_START(jx8p)
	PORT_START("SCAN0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("3")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("4")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("5")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("6")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("7")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("8")

	PORT_START("SCAN1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("9")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("10")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("11")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("12")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("13")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("14")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("15")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("16")

	PORT_START("SCAN2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("17")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("18")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("19")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("20")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("21")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("22")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("23")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("24")

	PORT_START("SCAN3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("25")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("26")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("27")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("28")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("29")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("30")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("31")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("32")

	PORT_START("SCAN4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Key Poly")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Key Unison")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Key Solo")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Bank Cartridge")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Bank Memory")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Bank Preset")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("SCAN5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("After Vibrato")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("After Brilliance")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("After Volume")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P.Chain Enter")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P.Chain Left")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P.Chain Right")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("SCAN6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Write")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Copy Cartridge")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Copy Memory")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Edit M.Tune")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Edit MIDI")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Edit Name")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Edit Param")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("SCAN7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("BRNG 0")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("BRNG 1")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("BRNG 2")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("BRNG 3")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PORTA SW")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("BEND POL")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("LFO SW")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Hold Pedal")
INPUT_PORTS_END

static INPUT_PORTS_START(jx10)
	PORT_START("SCAN0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("B")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("E")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("H")

	PORT_START("SCAN1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P3")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P4")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P5")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P6")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P7")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P8")

	PORT_START("SCAN2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("T0")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("T1")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("T2")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("T3")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("T4")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("T5")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("T6")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("T7")

	PORT_START("SCAN3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("CH FC")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("CH TM")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("CH SW")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SQ FC")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SQ RE")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW SW")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("T8")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("T9")

	PORT_START("SCAN4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN)
	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("SCAN5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN)
	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("SCAN6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("T ENT")
	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("SCAN7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN)
	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)
INPUT_PORTS_END

static INPUT_PORTS_START(mks70)
	PORT_START("SCAN0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Write")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Param")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Value")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Name")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Chase")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Cart Sel")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("M. Tune")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Patch")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Tone")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("MIDI")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Back")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Forward")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("8")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("7")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G9")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("H0")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Enter")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Tenkey")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("B")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("E")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("3")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("4")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("5")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("6")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN5")
	PORT_BIT(0x3f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN6")
	PORT_BIT(0x3f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("SCAN7")
	PORT_BIT(0x3f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN)
INPUT_PORTS_END

void roland_jx8p_state::jx8p(machine_config &config)
{
	HD6303R(config, m_assignercpu, 16_MHz_XTAL / 2); // HD63B03RP
	m_assignercpu->set_addrmap(AS_PROGRAM, &roland_jx8p_state::jx8p_assigner_map);
	m_assignercpu->in_p1_cb().set("adc", FUNC(upd7001_device::eoc_so_r)).bit(0);
	m_assignercpu->out_p1_cb().set("adc", FUNC(upd7001_device::sck_w)).bit(1);
	m_assignercpu->out_p1_cb().append("adc", FUNC(upd7001_device::si_w)).bit(2);
	m_assignercpu->out_p1_cb().append("adc", FUNC(upd7001_device::cs_w)).bit(3);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // TC5517APL + battery

	mb63h149_device &keyscan(MB63H149(config, "keyscan", 16_MHz_XTAL));
	keyscan.int_callback().set_inputline(m_assignercpu, HD6301_IRQ1_LINE);

	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, nullptr, "jx8p_cart");

	upd7001_device &adc(UPD7001(config, "adc", RES_K(27), CAP_P(47)));
	adc.dl_w(1);

	//UPD7537(config, "displaycpu", 400_kHz_XTAL);

	JX8P_SYNTH(config, "synth", 16_MHz_XTAL / 2);
}

void roland_jx8p_state::jx8po(machine_config &config)
{
	jx8p(config);

	MB63H130(config.replace(), "keyscan", 16_MHz_XTAL); // no INT
}

void roland_jx8p_state::jx10(machine_config &config)
{
	HD6303R(config, m_assignercpu, 16_MHz_XTAL / 2); // HD63B03RP
	m_assignercpu->set_addrmap(AS_PROGRAM, &roland_jx8p_state::superjx_assigner_map);
	m_assignercpu->in_p1_cb().set("adc", FUNC(upd7001_device::eoc_so_r)).bit(0);
	m_assignercpu->out_p1_cb().set("adc", FUNC(upd7001_device::sck_w)).bit(1);
	m_assignercpu->out_p1_cb().append("adc", FUNC(upd7001_device::si_w)).bit(2);
	m_assignercpu->out_p1_cb().append("adc", FUNC(upd7001_device::cs_w)).bit(3);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // TC5564PL-20 + battery

	mb63h149_device &keyscan(MB63H149(config, "keyscan", 16_MHz_XTAL));
	keyscan.int_callback().set_inputline(m_assignercpu, HD6301_IRQ1_LINE);

	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, nullptr, "jx8p_cart");

	upd7001_device &adc(UPD7001(config, "adc", RES_K(27), CAP_P(47)));
	adc.dl_w(1);

	//UPD7538A(config, "displaycpu", 600_kHz_XTAL);

	SUPERJX_SYNTH(config, "lower", 16_MHz_XTAL / 2);
	SUPERJX_SYNTH(config, "upper", 16_MHz_XTAL / 2);
}

void roland_jx8p_state::mks70(machine_config &config)
{
	jx10(config);

	m_assignercpu->in_p1_cb().set_constant(0x01);
	m_assignercpu->out_p1_cb().set_nop();

	config.device_remove("adc");
}

ROM_START(jx8p)
	ROM_REGION(0x4000, "assigner", 0)
	ROM_LOAD("jx8p_a3-1.ic6", 0x0000, 0x4000, CRC(fc566635) SHA1(aa7ce16107553f337eb87bbe6171062950389ea1))

	ROM_REGION(0x800, "displaycpu", 0)
	ROM_LOAD("upd7537-014_15179201.ic1", 0x000, 0x800, NO_DUMP)

	ROM_REGION(0x4000, "synth:program", 0)
	ROM_LOAD("jx8p-b-v21.ic22", 0x0000, 0x2000, CRC(4f51c873) SHA1(c3217596000329ac2ff03f1b8b2a5e0f2f3d7783))
	ROM_RELOAD(0x2000, 0x2000)
ROM_END

// Older assigner boards with MB63H130 instead of MB63H149 are incompatible with Ver. 3.0 and later ROMs
ROM_START(jx8po)
	ROM_REGION(0x4000, "assigner", 0)
	ROM_SYSTEM_BIOS(0, "v21", "Ver. 2.1 (English)")
	ROMX_LOAD("jx8p-a-v21.ic6", 0x0000, 0x4000, CRC(4c357353) SHA1(287aad14dc22b1f9d814d0fa9ef3088dd8a33dc3), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v21d", "Ver. 2.1 (Danish?)") // only differences are "KLAVER" replaces "PIANO" and "SIUUUU" replaces "JX-JET"
	ROMX_LOAD("jx8p21a.ic6", 0x0000, 0x4000, CRC(f2d1ac36) SHA1(8df4d0647abbf70e4eea2971ac9c629c6f6ef4a3), ROM_BIOS(1))

	ROM_REGION(0x800, "displaycpu", 0)
	ROM_LOAD("upd7537-014_15179201.ic1", 0x000, 0x800, NO_DUMP)

	ROM_REGION(0x4000, "synth:program", 0)
	ROM_LOAD("jx8p-b-v21.ic22", 0x0000, 0x2000, CRC(4f51c873) SHA1(c3217596000329ac2ff03f1b8b2a5e0f2f3d7783))
	ROM_RELOAD(0x2000, 0x2000)
ROM_END

ROM_START(jx10)
	ROM_REGION(0x8000, "assigner", 0)
	ROM_SYSTEM_BIOS(0, "v230", "Ver. 2.30")
	ROMX_LOAD("jx-10_a2_3.ic6", 0x0000, 0x8000, CRC(3cfc8b94) SHA1(add36ff741f25ddb5324ec921fe06e7f19a61efe), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v230se", "Ver. 2.30 (tone edit hack)") // http://www.colinfraser.com/jx10/jx.htm
	ROMX_LOAD("jx10-2.3-se-00.ic6", 0x0000, 0x8000, CRC(60773d32) SHA1(74980fb5864b9ffeca2b1efce7512c0dbef96a67), ROM_BIOS(1))

	ROM_REGION(0x1000, "displaycpu", 0)
	ROM_LOAD("upd7538a-013_15179240.ic3", 0x0000, 0x1000, NO_DUMP)

	ROM_REGION(0x4000, "lower:program", 0)
	ROM_LOAD("jx-10_b2_1.ic1", 0x0000, 0x2000, CRC(db2c8a7a) SHA1(c99253149a9f23e6944e319f12594e8a02c3976f)) // M5L27128K-2
	ROM_RELOAD(0x2000, 0x2000)

	ROM_REGION(0x4000, "upper:program", 0)
	ROM_LOAD("jx-10_c2_1.ic1", 0x0000, 0x2000, CRC(4c701d9b) SHA1(76bb3e89d6dd348eda5c353fd822e95b3927e679)) // M5L27128K-2
	ROM_RELOAD(0x2000, 0x2000)
ROM_END

ROM_START(mks70)
	ROM_REGION(0x8000, "assigner", 0)
	ROM_SYSTEM_BIOS(0, "v108", "Ver. 1.08")
	ROMX_LOAD("mks70_v108-a.ic6", 0x0000, 0x8000, CRC(d0f2ed4e) SHA1(3f052684fbccf30cef391dc0420673002279d8e0), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v103", "Ver. 1.03")
	ROMX_LOAD("a-v103.ic6", 0x0000, 0x8000, CRC(9d43f917) SHA1(fa9a1c8d5dd2c19bee2650aa3f7080773d90f8ef), ROM_BIOS(1))

	ROM_REGION(0x1000, "displaycpu", 0)
	ROM_LOAD("upd7538a-013_15179240.ic3", 0x0000, 0x1000, NO_DUMP)

	ROM_REGION(0x4000, "lower:program", 0) // no change between 1.03 and 1.06
	ROM_LOAD("b-v103.ic1", 0x0000, 0x4000, CRC(d568f6d3) SHA1(78b723ad1e606f0fefc0ee17297172c1842e6bb6))

	ROM_REGION(0x4000, "upper:program", 0) // no change between 1.03 and 1.06
	ROM_LOAD("c-v103.ic1", 0x0000, 0x4000, CRC(4808729c) SHA1(0adcfa405d6f5be7c4c32ffa5b2e224c66e72f74))
ROM_END

} // anonymous namespace


SYST(1985, jx8p,  0,    0, jx8p,  jx8p,  roland_jx8p_state, empty_init, "Roland", "JX-8P Polyphonic Synthesizer (Ver. 3.x)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1985, jx8po, jx8p, 0, jx8po, jx8p,  roland_jx8p_state, empty_init, "Roland", "JX-8P Polyphonic Synthesizer (Ver. 2.x)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1986, jx10,  0,    0, jx10,  jx10,  roland_jx8p_state, empty_init, "Roland", "JX-10 Super JX Polyphonic Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1987, mks70, jx10, 0, mks70, mks70, roland_jx8p_state, empty_init, "Roland", "MKS-70 Super JX Polyphonic Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



roland_mc50.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Roland MC-50 & similar MIDI sequencers.

****************************************************************************/

#include "emu.h"
//#include "bus/midi/midi.h"
#include "cpu/z180/z180.h"
#include "imagedev/floppy.h"
#include "machine/wd_fdc.h"


namespace {

class roland_mc50_state : public driver_device
{
public:
	roland_mc50_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_mpu(*this, "mpu")
		, m_wdfdc(*this, "wdfdc")
		, m_wdfdd(*this, "wdfdc:0")
	{
	}

	void mc300(machine_config &config);
	void mc50(machine_config &config);
	void mc50mk2(machine_config &config);

private:
	void mem_map_mc300(address_map &map) ATTR_COLD;
	void mem_map_mc50(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<z180_device> m_mpu;
	optional_device<wd1772_device> m_wdfdc;
	//optional_device<hd63266_device> m_hdfdc;
	optional_device<floppy_connector> m_wdfdd;
};

void roland_mc50_state::mem_map_mc300(address_map &map)
{
	map(0x00000, 0x01fff).rom().region("mpurom", 0);
	map(0x40000, 0xfffff).ram();
}

void roland_mc50_state::mem_map_mc50(address_map &map)
{
	map(0x00000, 0x3ffff).rom().region("mpurom", 0);
	map(0x40000, 0x7ffff).ram();
	map(0x80000, 0xfffff).rom().region("mpurom", 0);
}

void roland_mc50_state::io_map(address_map &map)
{
	map(0x0000, 0x003f).noprw(); // internal registers
	map(0x00c0, 0x00c3).mirror(0xff00).rw(m_wdfdc, FUNC(wd1772_device::read), FUNC(wd1772_device::write));
}

static INPUT_PORTS_START(mc50)
INPUT_PORTS_END

static void mc50_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
}

void roland_mc50_state::mc300(machine_config &config)
{
	Z80180(config, m_mpu, 10_MHz_XTAL);
	m_mpu->set_addrmap(AS_PROGRAM, &roland_mc50_state::mem_map_mc300);
	m_mpu->set_addrmap(AS_IO, &roland_mc50_state::io_map);

	//bu3904s_device &fsk(BU3904S(config, "fsk", 10_MHz_XTAL / 2));
	//fsk.xint_callback().set_inputline(m_mpu, Z180_INPUT_LINE_IRQ0);

	WD1772(config, m_wdfdc, 8_MHz_XTAL);
	m_wdfdc->intrq_wr_callback().set_inputline(m_mpu, Z180_INPUT_LINE_IRQ1);
	m_wdfdc->drq_wr_callback().set_inputline(m_mpu, Z180_INPUT_LINE_DREQ0);

	FLOPPY_CONNECTOR(config, m_wdfdd, mc50_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats);
}

void roland_mc50_state::mc50(machine_config &config)
{
	Z80180(config, m_mpu, 10_MHz_XTAL); // HD64180R1F6
	m_mpu->set_addrmap(AS_PROGRAM, &roland_mc50_state::mem_map_mc50);
	m_mpu->set_addrmap(AS_IO, &roland_mc50_state::io_map);

	//mn53015_device &fsk(MN53015(config, "fsk", 10_MHz_XTAL / 2));
	//fsk.int_callback().set_inputline(m_mpu, Z180_INPUT_LINE_IRQ0);

	WD1772(config, m_wdfdc, 8_MHz_XTAL); // WD1772-02
	m_wdfdc->intrq_wr_callback().set_inputline(m_mpu, Z180_INPUT_LINE_IRQ1);
	m_wdfdc->drq_wr_callback().set_inputline(m_mpu, Z180_INPUT_LINE_DREQ0);
	m_wdfdc->dden_w(0);

	FLOPPY_CONNECTOR(config, m_wdfdd, mc50_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats);

	// TODO: LCD unit (EA-D20225PX-1)
}

void roland_mc50_state::mc50mk2(machine_config &config)
{
	mc50(config);

	//config.device_remove("wdfdc");
	//HD63266(config, m_hdfdc, 16_MHz_XTAL); // HD63266F
}

ROM_START(mc300)
	ROM_REGION(0x2000, "mpurom", 0)
	ROM_SYSTEM_BIOS(0, "v102", "Version 1.02")
	ROMX_LOAD("osv1.02-27c64.bin", 0x0000, 0x2000, CRC(a509d5b2) SHA1(d5b23aa2514610d3a5b3f33bdf4479fdb8dc91bd), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v101", "Version 1.01")
	ROMX_LOAD("osv1.01-27c64.bin", 0x0000, 0x2000, CRC(459fd2a1) SHA1(5cea936807c80945ac39d0e7ecc63d3a2b968664), ROM_BIOS(1))
ROM_END

ROM_START(mc50)
	ROM_REGION(0x80000, "mpurom", 0)
	ROM_LOAD("mc50_mk1.ic9", 0x00000, 0x80000, CRC(322b5f73) SHA1(f5cb5738f03c51d7019a661ac660ecea56b44350))
ROM_END

ROM_START(mc50mk2)
	ROM_REGION(0x80000, "mpurom", 0)
	ROM_LOAD("mc50_mkii-firmware-v.0060.ic7", 0x00000, 0x80000, CRC(a428e378) SHA1(ac411c33ef79cf9ae51c5d5dbb1ed9ea5839db3e))
ROM_END

} // anonymous namespace


SYST(1988, mc300,   0, 0, mc300,   mc50, roland_mc50_state, empty_init, "Roland", "MC-300 Micro Composer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1990, mc50,    0, 0, mc50,    mc50, roland_mc50_state, empty_init, "Roland", "MC-50 Micro Composer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1992, mc50mk2, 0, 0, mc50mk2, mc50, roland_mc50_state, empty_init, "Roland", "MC-50mkII Micro Composer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



roland_mt32.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert,Jonathan Gevaryahu
/*************************************************************************************************

    Roland MT-32/CM32L driver

    Driver by Olivier Galibert and Jonathan Gevaryahu

    The Roland MT32 is an expander (synthesizer without the keyboard)
    from 1987.  It had a small 20-char LCD display and 10 control
    buttons (part 1-5, rhythm, sound/sound group, volume/master
    volume), a rotating knob and a midi message led.  It uses a weird
    kind of synthesis called "Linear Additive".

    The CM32L versions is a simpler/smaller version without the lcd,
    buttons or knob but keeping the led.  It also has a different
    firmware and more partials rom, adding "game sounds".  "CM" means
    "Computer Music".

    The LAPC-I (I as in Intel) is a ISA board variant of the CM32L
    with a MPU-401 frontend added to communicate with the synth.  All
    the roms are identical with the CM32L.

    MT-100 is a desktop digital synthesizer which shares functionality
    and main board with the MT-32, but extends it with a sequencer
    and a Quick Disk drive.

    Some special tricks:
    - pressing 3+V (Part 3 + Volume) at boot time starts the test mode

    - predding 4+R+M (Part 4 + Rhythm + Master Volume) at boot time
      shows the firmware version

    - the CM32L drives the lcd as if it was there and looks at the
      buttons at boot time for test mode and firmware version



Roland MT-32 rev00 pcb (early 1.0.7 and all earlier firmware, with ic26 and ic27 socketed):

PCB Layout
----------

MT-32 MAIN BOARD ASSY 79377310 00
|---------------------------------|------------------------|-----------|
|  JK1   JK2            IR3M03    |          JK3           |JK4   SW1  |
|                                 |                        |           |
|5218  072D 5218  072D            |------------------------|           |
|                          IR3M03     PC910       74HC04               |
|                                                                      |
|072D       4051                                                       |
|                            ROM.IC21                     |----|       |
|   M5238                                                 |    |       |
|                                     ROM.IC26    6264    |    |       |
| |---------| 74LS05 74HC27  ROM.IC22                     |    |  12MHz|
| | PCM54HP |        74HC00                       6264    |    |       |
| |---------|                                             |8095|       |
|                            |------|                     |    |       |
|                            |      | ROM.IC27    6264    |    |       |
| 4416  4416                 |  *2  |                     |    |       |
|                            |      |             6264    |----|       |
| 4416  4416                 |      |                                  |
|                            |------|                    |-----|       |
|             |-------|                                  |     |       |
| ROM.IC13    |       |       32.768kHz                  |  *1 |       |
|             |  *3   |                         74HC04   |     |       |
| CN1  CN2    |-------|      CN3  CN4  74HC02   74HC00   |-----|    CN5|
|----------------------------------------------------------------------|
Notes: (All IC's listed for completeness)
      8095   - Intel C8095-90 16-Bit Microcontroller, Clock Input 12.000MHz (DIP48)
      6264   - Hitachi HM6264ALSP-15 8K x8 SRAM (DIP28)
      4416   - Mitsubishi M5M4416P-12 16K x4 DRAM (DIP18)
      PC910  - Sharp PC910 Opto-Isolator (DIP8)
      IR3M03 - Sharp IR3M03A (DIP8)
      072D   - New Japan Radio Co., Ltd JRC072D Dual J-FET Input Operational Amplifier (DIP8)
      5218   - Mitsubishi 5218 Operational Amplifier (DIP8)
      M5238  - Mitsubishi M5238 LCD Display Controller (SIP8)
      CN1    - 6 Pin Connector
      CN2    - 6 Pin Connector
      CN3    - 2 Pin Connector
      CN4    - 3 Pin Connector
      CN5    - 10 Pin Connector
      SW1    - Power On/Off Switch
      JK1    - 1/4" Stereo Jack
      JK2    - 1/4" Stereo Jack
      JK3    - MIDI In, MIDI Out and Midi Thru DIN5 Jacks
      JK4    - Mini 1/8th" Power Input Jack
      *1     - R15229865 HG61H15B59F (QFP80, stamped 'Roland')
      *2     - R15229851 MB87136A LA32 (PGA144?, stamped 'Roland')
      *3     - R15229863 HG61H20R36F (QFP100, stamped 'BOSS')
      PCM54HP- Burr-Brown PCM54HP 16-Bit Monolithic Digital-to-Analog Converter (DIP28)
      & Various logic ICs - 4051, 74HC27, 74HC00, 74HC02, 74HC04, 74LS05

      ROMs -  Filename          Device Type
              ------------------------------------------------------------------------
              ROM.IC26.106      Mitsubishi M5M27C256 (labeled 1.0.6)   read as 27C256
              ROM.IC27.106      Mitsubishi M5M27C256 (labeled 1.0.6)   read as 27C256
              ROM.IC13.200      Mitsubishi M5M27C128 (labeled 2.0.0)   read as 27C128
              ROM.IC21          Toshiba TC532000P (-7471)              read as TC572000
              ROM.IC22          Toshiba TC532000P (-7472)              read as TC572000



Newer version

PCB Layout
----------

MT-32 MAIN BOARD ASSY 79377310 01
|---------------------------------|------------------------|-----------|
|  JK1   JK2            IR3M03    |          JK3           |JK4   SW1  |
|                                 |                        |           |
|5218  072D 5218  072D            |------------------------|           |
|                          IR3M03     PC910       74HC04               |
|                                                                      |
|072D       4051                                                       |
|                            ROM.IC37                     |----|       |
|   M5238                                                 |    |       |
|                                     ROM.IC26    4364    |    |       |
| |---------| 74LS05 74HC27                               |    |  12MHz|
| | PCM54HP |        74HC00                       4364    |    |       |
| |---------|                                             |8095|       |
|                                                         |    |       |
|                              |-----|ROM.IC27    4364    |    |       |
| 81416  81416                 |     |                    |    |       |
|                              |  *2 |            4364    |----|       |
| 81416  81416                 |     |                                 |
|                              |-----|                   |-----|       |
|             |-------|                                  |     |       |
| ROM.IC13    |       |       32.768kHz                  |  *1 |       |
|             |  *3   |                         74HC04   |     |       |
| CN1  CN2    |-------|      CN3  CN4  74HC02   74HC00   |-----|    CN5|
|----------------------------------------------------------------------|
Notes: (All IC's listed for completeness)
      8095   - Intel C8095-90 16-Bit Microcontroller, Clock Input 12.000MHz (DIP48)
      4364   - NEC D4364CX-15L 8K x8 SRAM (DIP28)
      81416  - Fujitsu MB81416 16K x4 DRAM (DIP18)
      PC910  - Sharp PC910 Opto-Isolator (DIP8)
      IR3M03 - Sharp IR3M03A (DIP8)
      072D   - New Japan Radio Co., Ltd JRC072D Dual J-FET Input Operational Amplifier (DIP8)
      5218   - Mitsubishi 5218 Operational Amplifier (DIP8)
      M5238  - Mitsubishi M5238 LCD Display Controller (SIP8)
      CN1    - 6 Pin Connector
      CN2    - 6 Pin Connector
      CN3    - 2 Pin Connector
      CN4    - 3 Pin Connector
      CN5    - 10 Pin Connector
      SW1    - Power On/Off Switch
      JK1    - 1/4" Stereo Jack
      JK2    - 1/4" Stereo Jack
      JK3    - MIDI In, MIDI Out and Midi Thru DIN5 Jacks
      JK4    - Mini 1/8th" Power Input Jack
      *1     - R15229865 HG61H15B59F (QFP80, stamped 'Roland')
      *2     - R15229896 LA32 (QFP100, stamped 'Roland')
      *3     - R15229863 HG61H20R36F (QFP100, stamped 'BOSS')
      PCM54HP- Burr-Brown PCM54HP 16-Bit Monolithic Digital-to-Analog Converter (DIP28)
      & Various logic ICs - 4051, 74HC27, 74HC00, 74HC02, 74HC04, 74LS05

      ROMs -  Filename     Device Type
              ----------------------------------------------------
              ROM.IC26     Hitachi HN623258PH26   read as 27C256
              ROM.IC27     Hitachi HN623258PH26   read as 27C256
              ROM.IC13     Hitachi HN623257PC21   read as 27C256
              ROM.IC37     Toshiba TC534000P      read as TC574000
*/

#include "emu.h"
#include "cpu/mcs96/i8x9x.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "video/sed1200.h"
#include "emupal.h"
#include "screen.h"


namespace {

static INPUT_PORTS_START( mt32 )
	PORT_START("SC0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Sound group") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Volume") PORT_CODE(KEYCODE_V)
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("SC1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Rhythm") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Sound") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Master volume") PORT_CODE(KEYCODE_M)
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("A7")
	PORT_BIT(0x03ff, 0x0000, IPT_DIAL) PORT_NAME("Knob") PORT_SENSITIVITY(50) PORT_KEYDELTA(8) PORT_CODE_DEC(KEYCODE_DOWN) PORT_CODE_INC(KEYCODE_UP)
INPUT_PORTS_END

class mt32_state : public driver_device
{
public:
	mt32_state(const machine_config &mconfig, device_type type, const char *tag);

	void mt32(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<i8x9x_device> cpu;
	required_device<ram_device> ram;
	optional_device<sed1200d0a_device> lcd;
	required_device<timer_device> midi_timer;

	void mt32_palette(palette_device &palette) const;

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void bank_w(uint8_t data);
	void so_w(uint8_t data);
	void midi_w(uint16_t data);

	uint8_t lcd_ctrl_r();
	void lcd_ctrl_w(uint8_t data);
	void lcd_data_w(uint8_t data);
	uint16_t port0_r();

	TIMER_DEVICE_CALLBACK_MEMBER(midi_timer_cb);
	TIMER_DEVICE_CALLBACK_MEMBER(samples_timer_cb);

	void mt32_map(address_map &map) ATTR_COLD;

	uint8_t lcd_data_buffer[256]{};
	int lcd_data_buffer_pos = 0;
	uint8_t midi = 0;
	int midi_pos = 0;
	uint8_t port0 = 0;
};

mt32_state::mt32_state(const machine_config &mconfig, device_type type, const char *tag) :
	driver_device(mconfig, type, tag),
	cpu(*this, "maincpu"),
	ram(*this, "ram"),
	lcd(*this, "lcd"),
	midi_timer(*this, "midi_timer")
{
}


uint32_t mt32_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	bitmap.fill(0);
	const uint8_t *data = lcd->render();
	for(int c=0; c<20; c++)
		for(int y=0; y<8; y++) {
			uint8_t v = data[c*8+y];
			for(int x=0; x<5; x++)
				bitmap.pix(y == 7 ? 8 : y, c*6+x) = v & (0x10 >> x) ? 1 : 0;
		}
	return 0;
}

void mt32_state::machine_start()
{
	membank("bank")->configure_entries(0, 4, memregion("maincpu")->base(), 0x4000);
	membank("bank")->configure_entries(0x10, 2, ram->pointer(), 0x4000); // 0x10 doesn't seem used though, but that ram is accessible at c000+
	membank("fixed")->set_base(ram->pointer());

	lcd_data_buffer_pos = 0;
}

void mt32_state::machine_reset()
{
	midi_timer->adjust(attotime::from_hz(1));
	midi_pos = 0;
	port0 = 0;
}

void mt32_state::lcd_ctrl_w(uint8_t data)
{
	lcd->cs_w(0);
	lcd->control_w(data >> 4);
	lcd->control_w(data);
	for(int i=0; i != lcd_data_buffer_pos; i++) {
		lcd->data_w(lcd_data_buffer[i] >> 4);
		lcd->data_w(lcd_data_buffer[i]);
	}
	lcd->cs_w(1);
	lcd_data_buffer_pos = 0;
}

uint8_t mt32_state::lcd_ctrl_r()
{
	// Note that this does not read from the actual LCD unit (whose /RD line is pulled high)
	return 0;
}

void mt32_state::lcd_data_w(uint8_t data)
{
	lcd_data_buffer[lcd_data_buffer_pos++] = data;
}

void mt32_state::bank_w(uint8_t data)
{
	membank("bank")->set_entry(data);
}

void mt32_state::midi_w(uint16_t data)
{
	logerror("midi_out %02x\n", data);
	midi = data;
}

TIMER_DEVICE_CALLBACK_MEMBER(mt32_state::midi_timer_cb)
{
	const static uint8_t midi_data[3] = { 0x91, 0x40, 0x7f };
	midi = midi_data[midi_pos++];
	logerror("midi_in %02x\n", midi);
	cpu->serial_w(midi);
	if(midi_pos < sizeof(midi_data))
		midi_timer->adjust(attotime::from_hz(1250));
}

uint16_t mt32_state::port0_r()
{
	return port0;
}

TIMER_DEVICE_CALLBACK_MEMBER(mt32_state::samples_timer_cb)
{
	port0 ^= 0x10;
}

void mt32_state::so_w(uint8_t data)
{
	// bit 0   = led
	// bit 1-2 = reverb program a13/a14
	// bit 3-4 = nc
	// bit 5   = boss x1
	// bit 6   = lcd cs, also handled internally by the gate array
	// bit 7   = lcd clk, also handled internally by the gate array
	//  logerror("so: x1=%d bank=%d led=%d\n", (data >> 5) & 1, (data >> 1) & 3, data & 1);
}

void mt32_state::mt32_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(0, 0, 0));
	palette.set_pen_color(1, rgb_t(0, 255, 0));
}

void mt32_state::mt32_map(address_map &map)
{
	map(0x0100, 0x0100).w(FUNC(mt32_state::bank_w));
	map(0x0200, 0x0200).w(FUNC(mt32_state::so_w));
	map(0x021a, 0x021a).portr("SC0");
	map(0x021c, 0x021c).portr("SC1");
	map(0x0300, 0x0300).w(FUNC(mt32_state::lcd_data_w));
	map(0x0380, 0x0380).rw(FUNC(mt32_state::lcd_ctrl_r), FUNC(mt32_state::lcd_ctrl_w));
	map(0x1000, 0x7fff).rom().region("maincpu", 0x1000);
	map(0x8000, 0xbfff).bankrw("bank");
	map(0xc000, 0xffff).bankrw("fixed");
}

void mt32_state::mt32(machine_config &config)
{
	i8x9x_device &maincpu(P8098(config, cpu, 12_MHz_XTAL));
	maincpu.set_addrmap(AS_PROGRAM, &mt32_state::mt32_map);
	maincpu.ach7_cb().set_ioport("A7");
	maincpu.serial_tx_cb().set(FUNC(mt32_state::midi_w));
	maincpu.in_p0_cb().set(FUNC(mt32_state::port0_r));

	RAM(config, ram).set_default_size("32K");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_screen_update(FUNC(mt32_state::screen_update));
//  screen.set_size(20*6-1, 9);
	screen.set_size(20*6-1, (20*6-1)*3/4);
	screen.set_visarea(0, 20*6-2, 0, (20*6-1)*3/4-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(mt32_state::mt32_palette), 2);

	SED1200D0A(config, lcd, 0);

	TIMER(config, midi_timer).configure_generic(FUNC(mt32_state::midi_timer_cb));

	TIMER(config, "samples_timer").configure_periodic(FUNC(mt32_state::samples_timer_cb), attotime::from_hz(32000*2));
}

ROM_START( mt32 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_DEFAULT_BIOS( "107" )

	ROM_SYSTEM_BIOS( 0, "104", "Firmware 1.0.4" )
	ROMX_LOAD(       "mt32_1.0.4.ic27.bin",          0,   0x8000, CRC(a93b65f2) SHA1(9cd4858014c4e8a9dff96053f784bfaac1092a2e), ROM_BIOS(0) | ROM_SKIP(1) )
	ROMX_LOAD(       "mt32_1.0.4.ic26.bin",          1,   0x8000, CRC(b5ee2192) SHA1(fe8db469b5bfeb37edb269fd47e3ce6d91014652), ROM_BIOS(0) | ROM_SKIP(1) )

	ROM_SYSTEM_BIOS( 1, "105", "Firmware 1.0.5" )
	ROMX_LOAD(       "mt32_1.0.5.ic27.bin",          0,   0x8000, CRC(3281216c) SHA1(57a09d80d2f7ca5b9734edbe9645e6e700f83701), ROM_BIOS(1) | ROM_SKIP(1) )
	ROMX_LOAD(       "mt32_1.0.5.ic26.bin",          1,   0x8000, CRC(e06d8020) SHA1(52e3c6666db9ef962591a8ee99be0cde17f3a6b6), ROM_BIOS(1) | ROM_SKIP(1) )

	ROM_SYSTEM_BIOS( 2, "106", "Firmware 1.0.6" )
	ROMX_LOAD(       "mt32_1.0.6.ic27.bin",          0,   0x8000, CRC(29369ae1) SHA1(cc83bf23cee533097fb4c7e2c116e43b50ebacc8), ROM_BIOS(2) | ROM_SKIP(1) )
	ROMX_LOAD(       "mt32_1.0.6.ic26.bin",          1,   0x8000, CRC(4d495d98) SHA1(bf4f15666bc46679579498386704893b630c1171), ROM_BIOS(2) | ROM_SKIP(1) )

	ROM_SYSTEM_BIOS( 3, "107", "Firmware 1.0.7" )
	ROMX_LOAD(       "mt32_1.0.7.ic27.bin",          0,   0x8000, CRC(67fd8968) SHA1(13f06b38f0d9e0fc050b6503ab777bb938603260), ROM_BIOS(3) | ROM_SKIP(1) ) // also exists as a Mask ROM "R15449122 // HN623258PH26" @IC27
	ROMX_LOAD(       "mt32_1.0.7.ic26.bin",          1,   0x8000, CRC(60f45882) SHA1(c55e165487d71fa88bd8c5e9c083bc456c1a89aa), ROM_BIOS(3) | ROM_SKIP(1) ) // also exists as a Mask ROM "R15449123 // HN623258PH27" @IC26

	ROM_SYSTEM_BIOS( 4, "br", "Blue Ridge enhanced firmware" )
	ROMX_LOAD(       "blue_ridge__mt32b.bin",        1,   0x8000, CRC(5816476f) SHA1(e0934320d7cbb5edfaa29e0d01ae835ef620085b), ROM_BIOS(4) | ROM_SKIP(1) )
	ROMX_LOAD(       "blue_ridge__mt32a.bin",        0,   0x8000, CRC(d75fc3d9) SHA1(11a6ae5d8b6ee328b371af7f1e40b82125aa6b4d), ROM_BIOS(4) | ROM_SKIP(1) )

	ROM_SYSTEM_BIOS( 5, "m9", "M9 enhanced firmware" )
	ROMX_LOAD(       "a__m-9.27c256.ic27.bin",       0,   0x8000, CRC(c078ab00) SHA1(381e4208c0211a9a24a3a1b06a36760a1940ea6b), ROM_BIOS(5) | ROM_SKIP(1) )
	ROMX_LOAD(       "b__m-9.27c256.ic26.bin",       1,   0x8000, CRC(e9c439c4) SHA1(36fece02eddd84230a7cf32f931c94dd14adbf2c), ROM_BIOS(5) | ROM_SKIP(1) )

	// Dumped from "new" board revision single 128K x 8 ROM
	ROM_SYSTEM_BIOS( 6, "204", "Firmware 2.0.4" )
	ROMX_LOAD(       "mt32_2.0.4.ic28",              0,   0x10000, CRC(59a49d5c) SHA1(2c16432b6c73dd2a3947cba950a0f4c19d6180eb), ROM_BIOS(6) )
	ROM_IGNORE(0x10000)  // banking needs to be implemented

	ROM_SYSTEM_BIOS( 7, "207", "Firmware 2.0.7" )
	ROMX_LOAD(       "mt32_2.0.7.ic28",              0,   0x10000, CRC(a016b607) SHA1(47b52adefedaec475c925e54340e37673c11707c), ROM_BIOS(7) )
	ROM_IGNORE(0x10000)  // banking needs to be implemented

// We need a bios-like selection for these too
	ROM_REGION( 0x80000, "la32", 0 )
	// separate PCM Mask ROMs from the MT-32 1.x older PCB
	ROM_LOAD(        "r15179844.ic21.bin",           0,  0x40000, CRC(dd9deac3) SHA1(3a1e19b0cd4036623fd1d1d11f5f25995585962b) )
	ROM_LOAD(        "r15179845.ic22.bin",     0x40000,  0x40000, CRC(4ee6506c) SHA1(2cadb99d21a6a4a6f5b61b6218d16e9b43f61d01) )

	// combined PCM Mask ROM from the MT-32 1.x newer PCB; same contents as the two ROMs above, concatenated.
	ROM_LOAD(        "r15449121.ic37.bin",           0,  0x80000, CRC(573e31cc) SHA1(f6b1eebc4b2d200ec6d3d21d51325d5b48c60252) )


	ROM_REGION( 0x8000, "boss", 0 )
	ROM_LOAD(        "r15179857.ic13.bin",           0,   0x8000, CRC(cb219d85) SHA1(c2933cb7ad86e51904aa1c3bc12fa234e73a337f) )
ROM_END

ROM_START( mt100 )
	ROM_REGION( 0x20000, "maincpu", 0 )
	ROM_DEFAULT_BIOS( "203" )

	ROM_SYSTEM_BIOS( 0, "203", "Firmware 2.0.3" )
	ROMX_LOAD(       "mt32_2.0.3.ic28",              0,  0x20000, CRC(6af6b774) SHA1(5837064c9df4741a55f7c4d8787ac158dff2d3ce), ROM_BIOS(0) )

	ROM_REGION( 0x80000, "la32", 0 )
	ROM_LOAD(        "r15449121.ic37.bin",           0,  0x80000, CRC(573e31cc) SHA1(f6b1eebc4b2d200ec6d3d21d51325d5b48c60252) )  // Same as c32l

	ROM_REGION( 0x8000, "boss", 0 )
	ROM_LOAD(        "r15179917.ic13.bin",           0,   0x8000, CRC(236c87a6) SHA1(e1c03905c46e962d1deb15eeed92eb61b42bba4a) )  // Same as c32l

	ROM_REGION( 0x10000, "prboard", 0 )
	ROM_LOAD(        "mt100_4.0.0.bin",              0,  0x10000, CRC(0e3bffbb) SHA1(aeef44d56f0026243b4a6415ee5c60a8de9cea0f) )  // Roland MC-03 PR-100 Ver 4.00
ROM_END

ROM_START( cm32l )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_DEFAULT_BIOS( "102" )

	ROM_SYSTEM_BIOS( 0, "100", "Firmware 1.00" )
	ROMX_LOAD(       "lapc-i.v1.0.0.ic3.bin",        0,  0x10000, CRC(ee62022f) SHA1(73683d585cd6948cc19547942ca0e14a0319456d), ROM_BIOS(0) )

	ROM_SYSTEM_BIOS( 1, "102", "Firmware 1.02" )
	ROMX_LOAD(       "cm32l_control.rom",            0,  0x10000, CRC(b998047e) SHA1(a439fbb390da38cada95a7cbb1d6ca199cd66ef8), ROM_BIOS(1) )

	ROM_REGION( 0x100000, "la32", 0 )
// We need a bios-like selection for these too
	// separate PCM Mask ROMS from the LAPC-I and CM-32L
	ROM_LOAD(        "r15449121.ic9.bin",            0,  0x80000, CRC(573e31cc) SHA1(f6b1eebc4b2d200ec6d3d21d51325d5b48c60252) ) // shared with MT-32 1.x newer pcb, RA-50 and others
	ROM_LOAD(        "r15179945.ic8.bin",      0x80000,  0x80000, CRC(8e9ea06e) SHA1(3ad889fde5db5b6437cbc2eb6e305312fec3df93) ) // extended CM-32L specific sounds

	// combined PCM Mask ROM; same contents as the two roms above, concatenated; this may match the contents of the LAPC-N rom "R15209324" but needs verification
	ROM_LOAD(        "cm32l_pcm.rom",                0, 0x100000, CRC(04204baa) SHA1(f2a10225b0c191a10fbf068f1320c91b35c1c3f2) )

	ROM_REGION( 0x8000, "boss", 0 )
	ROM_LOAD(        "r15179917.ic19.bin",           0,   0x8000, CRC(236c87a6) SHA1(e1c03905c46e962d1deb15eeed92eb61b42bba4a) ) // shared with RA-50 and others
ROM_END

} // anonymous namespace


CONS( 1987, mt32,  0, 0, mt32, mt32, mt32_state, empty_init, "Roland", "MT-32",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
CONS( 1988, mt100, 0, 0, mt32, mt32, mt32_state, empty_init, "Roland", "MT-100", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
CONS( 1989, cm32l, 0, 0, mt32, mt32, mt32_state, empty_init, "Roland", "CM-32L", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



roland_mt80s.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/****************************************************************************

    Skeleton driver for Roland MT-80s MIDI player.

****************************************************************************/

#include "emu.h"

//#include "bus/midi/midi.h"
#include "cpu/f2mc16/f2mc16.h"
#include "imagedev/floppy.h"


namespace {

class roland_mt80s_state : public driver_device
{
public:
	roland_mt80s_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void mt80s(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	//required_device<hd63266_device> m_hdfdc;
};

void roland_mt80s_state::mem_map(address_map &map)
{
	map(0x000100, 0x0008ff).ram(); // TODO: should be mapped as internal memory by device
	map(0x100000, 0x13ffff).ram();
	map(0xf80000, 0xffffff).rom().region("maincpu", 0);
}

static INPUT_PORTS_START(mt80s)
INPUT_PORTS_END

void roland_mt80s_state::mt80s(machine_config &config)
{
	F2MC16(config, m_maincpu, 24_MHz_XTAL); // actually an MB90705H
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_mt80s_state::mem_map);

	// HD63266(config, m_hdfdc, 16_MHz_XTAL); // HD63266F

	// TC6116(config, "pcm", 16_MHz_XTAL); // TC6116AF
}

ROM_START(mt80s)
	ROM_REGION(0x80000, "maincpu", 0)
	ROM_LOAD("mt80s_101.ic4", 0x00000, 0x80000, CRC(c23f5cc4) SHA1(8583aaa146ac51ab0b8d62cec0403a89130f1389))

	ROM_REGION(0x100000, "wave", 0)
	ROM_LOAD("mb838000.ic7", 0x000000, 0x100000, NO_DUMP)
ROM_END

} // anonymous namespace


SYST(1995, mt80s,    0, 0, mt80s,    mt80s, roland_mt80s_state, empty_init, "Roland", "MT 80s Music Player", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



roland_pr100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*************************************************************************************************

    Skeleton driver for Roland PR-100 MIDI sequencer.

    There are two custom gate arrays: M60012-0105SP on the main board and M6003A-0117SP on the
    panel board. The main board gate array multiplexes addresses for the four MB81464 DRAMs and
    decodes all CPU I/O space accesses. It also generates a "metronome" signal which is this
    device's only audio output. The panel board's gate array is almost entirely controlled by
    the main gate array, with only the reset and power signals generated independently.

    The LCD unit is a DM0815.

*************************************************************************************************/

#include "emu.h"
//#include "bus/midi/midi.h"
#include "cpu/z180/z180.h"
#include "machine/eepromser.h"
#include "machine/input_merger.h"
#include "machine/i8251.h"
#include "mb87013.h"


namespace {

class roland_pr100_state : public driver_device
{
public:
	roland_pr100_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_eeprom(*this, "eeprom")
		, m_port9x(0)
	{
	}

	void pr100(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	u8 port9x_r(offs_t offset);
	void port9x_w(offs_t offset, u8 data);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<z180_device> m_maincpu;
	required_device<eeprom_serial_93cxx_device> m_eeprom;

	u8 m_port9x;
};

void roland_pr100_state::machine_start()
{
	save_item(NAME(m_port9x));
}

u8 roland_pr100_state::port9x_r(offs_t offset)
{
	return m_port9x;
}

void roland_pr100_state::port9x_w(offs_t offset, u8 data)
{
	logerror("port%02X = %02X\n", offset + 0x90, data);
	m_port9x = data;
}


void roland_pr100_state::mem_map(address_map &map)
{
	map.global_mask(0x3ffff); // A18 not connected
	map(0x00000, 0x0ffff).mirror(0x10000).rom().region("program", 0); // CE = A17
	map(0x20000, 0x3ffff).ram();
}

void roland_pr100_state::io_map(address_map &map)
{
	map(0x0000, 0x003f).noprw(); // internal
	map(0x0090, 0x0093).mirror(0xff00).rw(FUNC(roland_pr100_state::port9x_r), FUNC(roland_pr100_state::port9x_w)); // EEPROM I/O?
	map(0x00a0, 0x00a3).rw("qdc", FUNC(mb87013_device::read), FUNC(mb87013_device::write));
}


static INPUT_PORTS_START(pr100)
INPUT_PORTS_END

void roland_pr100_state::pr100(machine_config &config)
{
	HD64180RP(config, m_maincpu, 10_MHz_XTAL); // HD64B180R0P
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_pr100_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &roland_pr100_state::io_map);

	mb87013_device &qdc(MB87013(config, "qdc", 6.5_MHz_XTAL));
	qdc.sio_rd_callback().set("sio", FUNC(i8251_device::read));
	qdc.sio_wr_callback().set("sio", FUNC(i8251_device::write));
	qdc.txc_callback().set("sio", FUNC(i8251_device::write_txc));
	qdc.rxc_callback().set("sio", FUNC(i8251_device::write_rxc));
	qdc.rxd_callback().set("sio", FUNC(i8251_device::write_rxd));
	qdc.dsr_callback().set("sio", FUNC(i8251_device::write_dsr));
	qdc.op4_callback().set("qdc", FUNC(mb87013_device::rts_w));

	i8251_device &sio(I8251(config, "sio", 6.5_MHz_XTAL)); // MB89251
	sio.dtr_handler().set("qdc", FUNC(mb87013_device::dtr_w));
	sio.txd_handler().set("qdc", FUNC(mb87013_device::txd_w));
	sio.rxrdy_handler().set("siodrq", FUNC(input_merger_device::in_w<0>));
	sio.txrdy_handler().set("siodrq", FUNC(input_merger_device::in_w<0>));
	sio.write_cts(0);

	INPUT_MERGER_ANY_HIGH(config, "siodrq").output_handler().set_inputline(m_maincpu, Z180_INPUT_LINE_DREQ1); // 74ALS08

	EEPROM_93C46_16BIT(config, m_eeprom); // HY93C46
}

ROM_START(pr100)
	ROM_REGION(0x10000, "program", 0)
	ROM_LOAD("roland_mbm27c512-20.ic10", 0x00000, 0x10000, CRC(41160b69) SHA1(11e5fb001dd004a5625d9a75fb1acac4ade614c8))
ROM_END

ROM_START(pr100_201)
	ROM_REGION(0x10000, "program", 0)
	ROM_LOAD("pr100-201_mbm27c512-20.ic10", 0x00000, 0x10000, CRC(2f3bc01c) SHA1(114ff687ffac4d517edba77d427422c4d2a6d369))
ROM_END

} // anonymous namespace


SYST(1987, pr100,     0,     0, pr100, pr100, roland_pr100_state, empty_init, "Roland", "PR-100 Digital Sequencer (v2.02)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
SYST(1987, pr100_201, pr100, 0, pr100, pr100, roland_pr100_state, empty_init, "Roland", "PR-100 Digital Sequencer (v2.01)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



roland_r8.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR,Valley Bell
/****************************************************************************

    Skeleton driver for Roland R-8 drum machine.

****************************************************************************/

/*
PCM Card Sample Table Format
----------------------------
Note: 2-byte values are stored in Little Endian.

General Layout
--------------
Pos   Len   Description
   80    01 card ID (00 for SN-R8-01, 01 for SN-R8-02, etc.)
   81    01 number of tones
   82    0E padding (byte 0xFF)
   90    70 tone offsets, 2 bytes per offset, relative to 0x100, each value points to the start of a "tone" definition
  100   500 tone list
  600   200 demo song header?
  800   800 ??
 1000  2000 demo song data?
 3000 7D000 sample data

Tone List (address: 0x100)
---------
Pos Len Description
00  08  tone name
08  01  ?? (always 0x8F)
09  01  ??
0A  01  frequency (0xE0 == 44100 Hz)
0B  02  ??
0C  02  sample A bank/flags
        sample cards:
            Bit 10 (0x0400) = bank (0 = card offsets 0x00000..0x3FFFF, 1 = card offsets 0x40000..0x7FFFF)
        R8 internal ROM:
            Bit 10 (0x0400) = bank bit 0 (0 = offsets 0x00000..0x3FFFF, 1 = offsets 0x40000..0x7FFFF)
            Bit 12 (0x1000) = bank bit 1 (0 = offsets 0x00000..0x7FFFF, 1 = offsets 0x80000..0xFFFFF)
        Bits 14-15 (0xC000) - play/loop mode
            0 = normal loop
            1 = no loop? (not used)
            2 = ping-pong 1? (forwards, backwards, forwards, backwards, ...)
            3 = ping-pong 2? (backwards, forwards, backwards, forwards, ...)
                -> when this is used, then the start address is usually set to (end address) or (end address+1)
0E  02  sample A start address (high word, i.e. address bits 2..17)
10  02  sample A start address, fraction (2.14 fixed point, i.e. 1 byte = 0x4000)
12  02  sample A loop address (high word)
14  02  sample A end address (high word)
16  02  ??
18  02  ??
1A  02  ??
1C  02  ??
1E  02  sample B bank and flags?
20  02  sample B start address (high word)
22  02  sample B start address, fraction
24  02  sample B loop address (high word)
26  02  sample B end address (high word)
28  02  ??
2A  02  ??
2C  02  ??
2E  02  ??
-> 30h bytes per entry

"sample A" is what is heard. The use for "sample B" is unknown.
The 8 unknwon consecutive bytes (0x16..0x1D and 0x28..0x2F) are probably related to the envelope generation.


R8/R8M internal tone list offsets: 0x018C10
R8/R8M internal tone list data: 0x018CA0
R8 mkII doesn't seem to store the tone list in the program ROM.

*/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/upd78k/upd78k2.h"
#include "machine/nvram.h"
#include "sound/roland_lp.h"

#include "softlist_dev.h"
#include "speaker.h"

#include <array>


namespace {

// PCM card address line scrambling
#define UNSCRAMBLE_ADDR_EXT(_offset) \
	bitswap<19>(_offset,18,17, 8, 9,16,11,12, 7,14,10,13,15, 3, 2, 1, 6, 4, 5, 0)

#define UNSCRAMBLE_DATA(_data) \
	bitswap<8>(_data,1,2,7,3,5,0,4,6)

// PCM card offsets in PCM chip memory space
// Right now this is an assumption based on the existing sample tables and wasn't confirmed with the firmware yet.
static const offs_t PCMCARD_SIZE = 0x080000;    // 512 KB
static const std::array<offs_t, 3> PCMCARD_OFFSETS = {0x080000, 0x180000, 0x280000};

class roland_r8_base_state : public driver_device
{
public:
	roland_r8_base_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pcm(*this, "pcm")
	{
	}

	void r8_common(machine_config &config);
	void init_r8();

protected:
	void mk1_map(address_map &map) ATTR_COLD;
	void mk2_map(address_map &map) ATTR_COLD;

	std::pair<std::error_condition, std::string> pcmrom_load(generic_slot_device* pcmcard, int card_id, device_image_interface &image);
	void pcmrom_unload(int card_id);
	void descramble_rom_external(u8* dst, const u8* src);

	required_device<upd78k2_device> m_maincpu;
	required_device<mb87419_mb87420_device> m_pcm;
	//required_device<generic_slot_device> m_ramcard;   // TODO
};

class roland_r8_state : public roland_r8_base_state
{
public:
	roland_r8_state(const machine_config &mconfig, device_type type, const char *tag)
		: roland_r8_base_state(mconfig, type, tag)
		, m_pcmcard(*this, "pcmcard")
	{
	}

	void r8(machine_config &config);

private:
	DEVICE_IMAGE_LOAD_MEMBER(pcmcard_load) { return pcmrom_load(m_pcmcard, 0, image); }
	DEVICE_IMAGE_UNLOAD_MEMBER(pcmcard_unload) { pcmrom_unload(0); }

	required_device<generic_slot_device> m_pcmcard;
};

class roland_r8m_state : public roland_r8_base_state
{
public:
	roland_r8m_state(const machine_config &mconfig, device_type type, const char *tag)
		: roland_r8_base_state(mconfig, type, tag)
		, m_pcmcards(*this, "pcmcard%u", 0U)
	{
	}

	void r8m(machine_config &config);

private:
	DEVICE_IMAGE_LOAD_MEMBER(pcmcard0_load) { return pcmrom_load(m_pcmcards[0], 0, image); }
	DEVICE_IMAGE_UNLOAD_MEMBER(pcmcard0_unload) { pcmrom_unload(0); }
	DEVICE_IMAGE_LOAD_MEMBER(pcmcard1_load) { return pcmrom_load(m_pcmcards[1], 1, image); }
	DEVICE_IMAGE_UNLOAD_MEMBER(pcmcard1_unload) { pcmrom_unload(1); }
	DEVICE_IMAGE_LOAD_MEMBER(pcmcard2_load) { return pcmrom_load(m_pcmcards[2], 2, image); }
	DEVICE_IMAGE_UNLOAD_MEMBER(pcmcard2_unload) { pcmrom_unload(2); }

	required_device_array<generic_slot_device, 3> m_pcmcards;
};

class roland_r8mk2_state : public roland_r8_base_state
{
public:
	roland_r8mk2_state(const machine_config &mconfig, device_type type, const char *tag)
		: roland_r8_base_state(mconfig, type, tag)
		, m_pcmcard(*this, "pcmcard")
	{
	}

	void r8mk2(machine_config &config);

private:
	DEVICE_IMAGE_LOAD_MEMBER(pcmcard_load) { return pcmrom_load(m_pcmcard, 0, image); }
	DEVICE_IMAGE_UNLOAD_MEMBER(pcmcard_unload) { pcmrom_unload(0); }

	required_device<generic_slot_device> m_pcmcard;
};


std::pair<std::error_condition, std::string> roland_r8_base_state::pcmrom_load(generic_slot_device *pcmcard, int card_id, device_image_interface &image)
{
	uint32_t const size = pcmcard->common_get_size("rom");
	if (size > PCMCARD_SIZE)
		return std::make_pair(image_error::INVALIDLENGTH, "Invalid size (maximum supported is 512K)");

	pcmcard->rom_alloc(PCMCARD_SIZE, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	pcmcard->common_load_rom(pcmcard->get_rom_base(), size, "rom");
	u8 *base = pcmcard->get_rom_base();
	if (size < PCMCARD_SIZE)
	{
		uint32_t mirror = (1 << (31 - count_leading_zeros_32(size)));
		if (mirror < 0x020000)  // due to how address descrambling works, we can currently only do mirroring for 128K pages
			mirror = 0x020000;
		for (uint32_t ofs = mirror; ofs < PCMCARD_SIZE; ofs += mirror)
			memcpy(base + ofs, base, mirror);
	}

	offs_t pcm_addr = PCMCARD_OFFSETS[card_id];
	u8 *src = reinterpret_cast<u8 *>(memregion("pcmorg")->base());
	u8 *dst = reinterpret_cast<u8 *>(memregion("pcm")->base());
	memcpy(&src[pcm_addr], base, PCMCARD_SIZE);
	// descramble PCM card ROM
	descramble_rom_external(&dst[pcm_addr], &src[pcm_addr]);
	//pcmard_loaded[card_id] = true;

	return std::make_pair(std::error_condition(), std::string());
}

void roland_r8_base_state::pcmrom_unload(int card_id)
{
	u8 *src = reinterpret_cast<u8 *>(memregion("pcmorg")->base());
	u8 *dst = reinterpret_cast<u8 *>(memregion("pcm")->base());
	offs_t pcm_addr = PCMCARD_OFFSETS[card_id];
	memset(&src[pcm_addr], 0xff, PCMCARD_SIZE);
	memset(&dst[pcm_addr], 0xff, PCMCARD_SIZE);
	//pcmard_loaded[card_id] = false;
}


void roland_r8_base_state::mk1_map(address_map &map)
{
	map(0x00000, 0x1ffff).rom().region("maincpu", 0);
	map(0x20000, 0x27fff).ram().share("nvram");
	map(0x70000, 0x7001f).rw(m_pcm, FUNC(mb87419_mb87420_device::read), FUNC(mb87419_mb87420_device::write));
}

void roland_r8_base_state::mk2_map(address_map &map)
{
	map(0x00000, 0x1ffff).rom().region("maincpu", 0);
	map(0x20000, 0x27fff).ram().share("nvram1");
	map(0x28000, 0x2ffff).ram().share("nvram2");
	map(0x70000, 0x7001f).rw(m_pcm, FUNC(mb87419_mb87420_device::read), FUNC(mb87419_mb87420_device::write));
}


static INPUT_PORTS_START(r8)
INPUT_PORTS_END


void roland_r8_base_state::r8_common(machine_config &config)
{
	UPD78210(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_r8_state::mk1_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // SRM20256LC-12 + battery

	//bu3904s_device &fsk(BU3904S(config, "fsk", 12_MHz_XTAL));
	//fsk.xint_callback().set_inputline(m_maincpu, upd78k2_device::INTP0_LINE);

	SPEAKER(config, "speaker", 2).front();

	MB87419_MB87420(config, m_pcm, 33.8688_MHz_XTAL);
	//m_pcm->int_callback().set_inputline(m_maincpu, upd78k2_device::INTP1_LINE);
	m_pcm->set_device_rom_tag("pcm");
	m_pcm->add_route(0, "speaker", 1.0, 0);
	m_pcm->add_route(1, "speaker", 1.0, 1);
}

void roland_r8_state::r8(machine_config &config)
{
	r8_common(config);

	GENERIC_CARTSLOT(config, m_pcmcard, generic_romram_plain_slot, "r8_card", "bin");
	m_pcmcard->set_device_load(FUNC(roland_r8_state::pcmcard_load));
	m_pcmcard->set_device_unload(FUNC(roland_r8_state::pcmcard_unload));

	SOFTWARE_LIST(config, "card_list").set_original("r8_card");
}

void roland_r8m_state::r8m(machine_config &config)
{
	r8_common(config);

	UPD78213(config.replace(), m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_r8m_state::mk1_map);

	for (auto& pcmcard : m_pcmcards)
		GENERIC_CARTSLOT(config, pcmcard, generic_romram_plain_slot, "r8_card", "bin");
	m_pcmcards[0]->set_device_load(FUNC(roland_r8m_state::pcmcard0_load));
	m_pcmcards[0]->set_device_unload(FUNC(roland_r8m_state::pcmcard0_unload));
	m_pcmcards[1]->set_device_load(FUNC(roland_r8m_state::pcmcard1_load));
	m_pcmcards[1]->set_device_unload(FUNC(roland_r8m_state::pcmcard1_unload));
	m_pcmcards[2]->set_device_load(FUNC(roland_r8m_state::pcmcard2_load));
	m_pcmcards[2]->set_device_unload(FUNC(roland_r8m_state::pcmcard2_unload));
	SOFTWARE_LIST(config, "card_list").set_original("r8_card");
}

void roland_r8mk2_state::r8mk2(machine_config &config)
{
	UPD78213(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_r8mk2_state::mk2_map);

	NVRAM(config, "nvram1", nvram_device::DEFAULT_ALL_0); // SRM20256LC-10 + battery
	NVRAM(config, "nvram2", nvram_device::DEFAULT_ALL_0); // SRM20256LC-10 + battery

	//bu3904s_device &fsk(BU3904S(config, "fsk", 12_MHz_XTAL));
	//fsk.xint_callback().set_inputline(m_maincpu, upd78k2_device::INTP0_LINE);

	SPEAKER(config, "speaker", 2).front();

	MB87419_MB87420(config, m_pcm, 33.8688_MHz_XTAL);
	//m_pcm->int_callback().set_inputline(m_maincpu, upd78k2_device::INTP1_LINE);
	m_pcm->set_device_rom_tag("pcm");
	m_pcm->add_route(0, "speaker", 1.0, 0);
	m_pcm->add_route(1, "speaker", 1.0, 1);

	GENERIC_CARTSLOT(config, m_pcmcard, generic_romram_plain_slot, "r8_card", "bin");
	m_pcmcard->set_device_load(FUNC(roland_r8mk2_state::pcmcard_load));
	m_pcmcard->set_device_unload(FUNC(roland_r8mk2_state::pcmcard_unload));
	SOFTWARE_LIST(config, "card_list").set_original("r8_card");
}

void roland_r8_base_state::init_r8()
{
	u8 *src = static_cast<u8*>(memregion("pcmorg")->base());
	u8 *dst = static_cast<u8*>(memregion("pcm")->base());

	for (offs_t addr = 0; addr < memregion("pcmorg")->bytes(); addr += 0x100000)
	{
		// TODO: descramble internal ROMs
		memcpy(&dst[addr + 0x00000], &src[addr + 0x00000], 0x80000);
		// descramble PCM card ROMs
		descramble_rom_external(&dst[addr + 0x80000], &src[addr + 0x80000]);
	}
}

void roland_r8_base_state::descramble_rom_external(u8* dst, const u8* src)
{
	for (offs_t srcpos = 0x00; srcpos < PCMCARD_SIZE; srcpos ++)
	{
		offs_t dstpos = UNSCRAMBLE_ADDR_EXT(srcpos);
		dst[dstpos] = UNSCRAMBLE_DATA(src[srcpos]);
	}
}


ROM_START(r8)
	ROM_REGION(0x20000, "maincpu", 0)
	ROM_LOAD("roland r-8_2.02_27c010.bin", 0x00000, 0x20000, CRC(45d0f64f) SHA1(55f0831db74cbdeae20cd7f1ff28af27dafba9b9))

	ROM_REGION(0x200000, "pcmorg", ROMREGION_ERASE00) // ROMs before descrambling
	ROM_LOAD("r15179929-mn234000rle.ic30", 0x000000, 0x080000, NO_DUMP)
	// 0x80000..0xFFFFF is reserved for PCM cards, according to the sample list
	ROM_LOAD("r15179930-mn234000rlf.ic31", 0x100000, 0x080000, NO_DUMP)
	ROM_REGION(0x200000, "pcm", ROMREGION_ERASE00) // ROMs after descrambling
ROM_END

ROM_START(r8m)
	ROM_REGION(0x20000, "maincpu", 0)
	ROM_LOAD("rolandr8mv104.bin", 0x00000, 0x20000, CRC(5e95e2f6) SHA1(b4e1a8f15f72a9db9aa8fd41ee3c3ebd10460587))

	ROM_REGION(0x400000, "pcmorg", ROMREGION_ERASE00) // ROMs before descrambling (must be large enough to allow for 3 PCM cards at offsets 0.5M, 1.5M and 2.5M)
	// same ROMs as R-8 assumed
	ROM_LOAD("r15179929-mn234000rle.bin", 0x000000, 0x080000, NO_DUMP)
	// 0x80000..0xFFFFF is reserved for PCM cards, according to the sample list
	ROM_LOAD("r15179930-mn234000rlf.bin", 0x100000, 0x080000, NO_DUMP)
	ROM_REGION(0x400000, "pcm", ROMREGION_ERASE00) // ROMs after descrambling
ROM_END

ROM_START(r8mk2)
	ROM_REGION(0x20000, "maincpu", 0)
	ROM_LOAD("roland r8 mkii eprom v1.0.3.bin", 0x00000, 0x20000, CRC(128a9a0c) SHA1(94bd8c76efe270754219f2899f31b62fc4f9060d))

	ROM_REGION(0x400000, "pcmorg", ROMREGION_ERASE00) // ROMs before descrambling
	ROM_LOAD("r15209440-upd27c8001eacz-025.ic30", 0x000000, 0x080000, NO_DUMP)
	ROM_LOAD("r15209441-upd27c8001eacz-026.ic31", 0x100000, 0x080000, NO_DUMP)
	ROM_LOAD("r15209442-upd27c8001eacz-027.ic82", 0x200000, 0x080000, NO_DUMP)
	ROM_REGION(0x400000, "pcm", ROMREGION_ERASE00) // ROMs after descrambling
ROM_END

} // anonymous namespace


SYST(1989, r8,    0,  0, r8,    r8, roland_r8_state, init_r8, "Roland", "R-8 Human Rhythm Composer (v2.02)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1990, r8m,   r8, 0, r8m,   r8, roland_r8m_state, init_r8, "Roland", "R-8M Total Percussion Sound Module (v1.04)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1992, r8mk2, 0,  0, r8mk2, r8, roland_r8mk2_state, init_r8, "Roland", "R-8 Mk II Human Rhythm Composer (v1.0.3)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



roland_ra30.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Roland RA-30 Realtime Arranger.

****************************************************************************/

#include "emu.h"
#include "cpu/h8500/h8510.h"


namespace {

class roland_ra30_state : public driver_device
{
public:
	roland_ra30_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void ra30(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<h8510_device> m_maincpu;
};

void roland_ra30_state::mem_map(address_map &map)
{
	map(0x000000, 0x07ffff).mirror(0x100000).rom().region("progrom", 0);
}


static INPUT_PORTS_START(ra30)
INPUT_PORTS_END

void roland_ra30_state::ra30(machine_config &config)
{
	HD6415108(config, m_maincpu, 20'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_ra30_state::mem_map);
}

ROM_START(ra30)
	ROM_REGION16_BE(0x80000, "progrom", 0)
	ROM_LOAD("roland_r00892534_hn62444bp_e85.ic22", 0x00000, 0x80000, CRC(c8b75d6f) SHA1(68472f2fa6ee2cab77b153c0131dcf2d76e0dce2))

	ROM_REGION(0x80000, "stylerom", 0)
	ROM_LOAD("roland_r00679623_hn62444bp_e76.ic18", 0x00000, 0x80000, CRC(3c1670a6) SHA1(d0ef77143f64a20c6a151746eb0bfad15a98b2e5))
ROM_END

} // anonymous namespace


SYST(1995, ra30, 0, 0, ra30, ra30, roland_ra30_state, empty_init, "Roland", "RA-30 Realtime Arranger", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



roland_s10.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Roland S-10 and related samplers.

    MKS-100 is S-10 without the keyboard; the two use the same main board.
    S-220 also lacks a keyboard interface but implements a four-channel
    VCA.

****************************************************************************/

#include "emu.h"
#include "bu3905.h"
#include "sa16.h"
//#include "bus/midi/midi.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/i8251.h"
#include "mb62h195.h"
#include "mb63h149.h"
#include "mb87013.h"
#include "machine/nvram.h"
#include "machine/rescap.h"
#include "machine/upd7001.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"


namespace {

class roland_s10_state : public driver_device
{
public:
	roland_s10_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_io(*this, "io")
		, m_lcdc(*this, "lcdc")
		, m_sampler(*this, "sampler")
	{
	}

	void s10(machine_config &config);
	void mks100(machine_config &config);

protected:
	HD44780_PIXEL_UPDATE(lcd_pixel_update);

	void lcd_data_w(offs_t offset, u8 data);
	void led_data_w(offs_t offset, u8 data);
	u8 sw_scan_r(offs_t offset);
	void sw_scan_w(offs_t offset, u8 data);
	void led_latch_w(u8 data);

	void prog_map(address_map &map) ATTR_COLD;
	void s10_ext_map(address_map &map) ATTR_COLD;
	void mks100_ext_map(address_map &map) ATTR_COLD;

	void palette_init(palette_device &palette);

	required_device<mcs51_cpu_device> m_maincpu;
	required_device<mb62h195_device> m_io;
	required_device<hd44780_device> m_lcdc;
	required_device<sa16_base_device> m_sampler;
};

class roland_s220_state : public roland_s10_state
{
public:
	roland_s220_state(const machine_config &mconfig, device_type type, const char *tag)
		: roland_s10_state(mconfig, type, tag)
		, m_outctrl(*this, "outctrl")
	{
	}

	void s220(machine_config &config);

private:
	HD44780_PIXEL_UPDATE(lcd_pixel_update);

	void output_control_w(offs_t offset, u8 data);
	void vca_cv_w(offs_t offset, u8 data);
	void led_latch1_w(u8 data);
	void led_latch2_w(u8 data);

	void s220_ext_map(address_map &map) ATTR_COLD;

	required_device<bu3905_device> m_outctrl;
};


HD44780_PIXEL_UPDATE(roland_s10_state::lcd_pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 8)
		bitmap.pix(y, (line * 8 + pos) * 6 + x) = state;
}

HD44780_PIXEL_UPDATE(roland_s220_state::lcd_pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 16)
		bitmap.pix(line * 8 + y, pos * 6 + x) = state;
}

void roland_s10_state::lcd_data_w(offs_t offset, u8 data)
{
	if (offset == 0)
		m_lcdc->control_w(data);
	else
		m_lcdc->data_w(data);
}

void roland_s10_state::led_data_w(offs_t offset, u8 data)
{
}

u8 roland_s10_state::sw_scan_r(offs_t offset)
{
	return 0;
}

void roland_s10_state::sw_scan_w(offs_t offset, u8 data)
{
}

void roland_s10_state::led_latch_w(u8 data)
{
}

void roland_s220_state::output_control_w(offs_t offset, u8 data)
{
	m_outctrl->write(offset, data & 0x0f);
}

void roland_s220_state::vca_cv_w(offs_t offset, u8 data)
{
}

void roland_s220_state::led_latch1_w(u8 data)
{
}

void roland_s220_state::led_latch2_w(u8 data)
{
}

void roland_s10_state::prog_map(address_map &map)
{
	map(0x0000, 0xffff).rom().region("program", 0);
}

void roland_s10_state::mks100_ext_map(address_map &map)
{
	map(0x4000, 0x4003).mirror(0xffc).rw("qddia", FUNC(mb87013_device::read), FUNC(mb87013_device::write));
	map(0x6000, 0x7fff).ram().share("nvram");
	map(0x8000, 0x80ff).mirror(0xf00).w(FUNC(roland_s10_state::lcd_data_w));
	map(0x9000, 0x90ff).mirror(0xf00).w(FUNC(roland_s10_state::led_data_w));
	map(0xa000, 0xa0ff).mirror(0xf00).rw(FUNC(roland_s10_state::sw_scan_r), FUNC(roland_s10_state::sw_scan_w));
	map(0xc000, 0xc000).mirror(0xfff).w(FUNC(roland_s10_state::led_latch_w));
	map(0xe000, 0xffff).rw(m_sampler, FUNC(rf5c36_device::read), FUNC(rf5c36_device::write));
}

void roland_s10_state::s10_ext_map(address_map &map)
{
	mks100_ext_map(map);
	map(0x5000, 0x57ff).mirror(0x800).rw("keyscan", FUNC(mb63h149_device::read), FUNC(mb63h149_device::write));
	//map(0xb000, 0xb000).mirror(0xfff).rw(FUNC(roland_s10_state::upd7001_r), FUNC(mb63h149_device::upd7001_w));
}

void roland_s220_state::s220_ext_map(address_map &map)
{
	map(0x0000, 0x000f).mirror(0x3ff0).w(FUNC(roland_s220_state::output_control_w));
	map(0x4000, 0x4003).mirror(0xffc).rw("qddia", FUNC(mb87013_device::read), FUNC(mb87013_device::write));
	map(0x5000, 0x5000).mirror(0xfff).w(FUNC(roland_s220_state::led_latch1_w));
	map(0x6000, 0x7fff).ram().share("nvram");
	map(0x8000, 0x80ff).mirror(0xf00).w(FUNC(roland_s220_state::lcd_data_w));
	map(0x9000, 0x90ff).mirror(0xf00).w(FUNC(roland_s220_state::vca_cv_w));
	map(0xa000, 0xa0ff).mirror(0xf00).rw(FUNC(roland_s220_state::sw_scan_r), FUNC(roland_s220_state::sw_scan_w));
	map(0xc000, 0xc000).mirror(0xfff).w(FUNC(roland_s220_state::led_latch2_w));
	map(0xe000, 0xffff).rw(m_sampler, FUNC(rf5c36_device::read), FUNC(rf5c36_device::write));
}


static INPUT_PORTS_START(s10)
INPUT_PORTS_END

static INPUT_PORTS_START(mks100)
INPUT_PORTS_END

static INPUT_PORTS_START(s220)
INPUT_PORTS_END

void roland_s10_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(131, 136, 139));
	palette.set_pen_color(1, rgb_t( 92,  83,  88));
}

void roland_s10_state::s10(machine_config &config)
{
	I8032(config, m_maincpu, 12_MHz_XTAL); // SAB8032A
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_s10_state::prog_map);
	m_maincpu->set_addrmap(AS_IO, &roland_s10_state::s10_ext_map);

	MB62H195(config, m_io);
	m_io->lc_callback().set(m_lcdc, FUNC(hd44780_device::write));
	m_io->sout_callback().set("adc", FUNC(upd7001_device::si_w));
	m_io->sck_callback().set("adc", FUNC(upd7001_device::sck_w));
	m_io->sin_callback().set("adc", FUNC(upd7001_device::so_r));
	m_io->adc_callback().set("adc", FUNC(upd7001_device::cs_w));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // TC5564PL-20 + battery

	i8251_device &usart(I8251(config, "usart", 6.5_MHz_XTAL)); // MB89251A
	usart.dtr_handler().set("qddia", FUNC(mb87013_device::dtr_w));
	usart.txd_handler().set("qddia", FUNC(mb87013_device::txd_w));

	mb87013_device &qddia(MB87013(config, "qddia", 6.5_MHz_XTAL));
	qddia.sio_rd_callback().set("usart", FUNC(i8251_device::read));
	qddia.sio_wr_callback().set("usart", FUNC(i8251_device::write));
	qddia.txc_callback().set("usart", FUNC(i8251_device::write_txc));
	qddia.rxc_callback().set("usart", FUNC(i8251_device::write_rxc));
	qddia.rxd_callback().set("usart", FUNC(i8251_device::write_rxd));
	qddia.dsr_callback().set("usart", FUNC(i8251_device::write_dsr));
	qddia.op4_callback().set("qddia", FUNC(mb87013_device::rts_w));

	mb63h149_device &keyscan(MB63H149(config, "keyscan", 12_MHz_XTAL));
	keyscan.int_callback().set_inputline(m_maincpu, MCS51_T1_LINE);

	// LCD unit: LM16155C
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6*16, 8*1);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(roland_s10_state::palette_init), 2);

	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 8);
	m_lcdc->set_pixel_update_cb(FUNC(roland_s10_state::lcd_pixel_update));
	m_lcdc->set_busy_factor(0.005f);

	UPD7001(config, "adc", RES_K(27), CAP_P(47));

	RF5C36(config, m_sampler, 26.88_MHz_XTAL);
	m_sampler->int_callback().set_inputline(m_maincpu, MCS51_INT1_LINE);
}

void roland_s10_state::mks100(machine_config &config)
{
	s10(config);
	m_maincpu->set_addrmap(AS_IO, &roland_s10_state::mks100_ext_map);

	m_io->sout_callback().set_nop();
	m_io->sck_callback().set_nop();
	m_io->sin_callback().set_constant(1);
	m_io->adc_callback().set_nop();

	config.device_remove("keyscan");
	config.device_remove("adc");
}

void roland_s220_state::s220(machine_config &config)
{
	s10(config);
	m_maincpu->set_addrmap(AS_IO, &roland_s220_state::s220_ext_map);

	m_io->sout_callback().set_nop();
	m_io->sck_callback().set_nop();
	m_io->sin_callback().set_constant(1);
	m_io->adc_callback().set_nop();

	config.device_remove("keyscan");
	config.device_remove("adc");

	// LCD unit: LDS7A1681A
	subdevice<screen_device>("screen")->set_size(6*16, 8*2);
	subdevice<screen_device>("screen")->set_visarea_full();
	m_lcdc->set_pixel_update_cb(FUNC(roland_s220_state::lcd_pixel_update));

	BU3905(config, m_outctrl);

	m_sampler->sh_callback().set(m_outctrl, FUNC(bu3905_device::axi_w));
}


ROM_START(s10)
	ROM_REGION(0x10000, "program", 0)
	ROM_LOAD("s-10_roland_2-0-7.ic26", 0x00000, 0x10000, CRC(5e588042) SHA1(a41e626bce036bcc9699bede3af137c2888ac704))
ROM_END

ROM_START(mks100)
	ROM_REGION(0x10000, "program", 0)
	ROM_LOAD("roland_mks-100_v1.04_ic26.bin", 0x00000, 0x10000, CRC(39a94481) SHA1(8c6e84d3298f44512d36fe57b80c8f6ea050197c))
ROM_END

ROM_START(s220)
	ROM_REGION(0x10000, "program", 0)
	ROM_LOAD("roland_s-220_v1.04_ic25.bin", 0x00000, 0x10000, CRC(1b74b694) SHA1(11ce4b47abe48116eb34d575e3da46387240c2b1))
ROM_END

} // anonymous namespace


SYST(1986, s10,    0,   0, s10,    s10,    roland_s10_state,  empty_init, "Roland", "S-10 Digital Sampling Keyboard", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1987, mks100, s10, 0, mks100, mks100, roland_s10_state,  empty_init, "Roland", "MKS-100 Digital Sampler",        MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1987, s220,   0,   0, s220,   s220,   roland_s220_state, empty_init, "Roland", "S-220 Digital Sampler",          MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



roland_s50.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Roland S-50 and related samplers.

****************************************************************************/

#include "emu.h"
#include "bu3905.h"
#include "sa16.h"
#include "mb63h149.h"

#include "formats/roland_dsk.h"

//#include "bus/midi/midi.h"
#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"
#include "cpu/mcs96/i8x9x.h"
#include "imagedev/floppy.h"
#include "machine/mb87030.h"
#include "machine/nscsi_bus.h"
#include "machine/timer.h"
#include "machine/wd_fdc.h"
#include "video/tms3556.h"
#include "video/t6963c.h"
#include "emupal.h"
#include "screen.h"


namespace {

class roland_s50_base_state : public driver_device
{
public:
	roland_s50_base_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_common_ram(*this, "common")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0U)
		, m_vdp(*this, "vdp")
		, m_wave(*this, "wave")
		, m_keyscan(*this, "keyscan")
		, m_floppy_select(0)
	{
	}

protected:
	u8 floppy_status_r();
	u8 floppy_unknown_r();
	u16 key_r(offs_t offset);
	void key_w(offs_t offset, u16 data);

	TIMER_DEVICE_CALLBACK_MEMBER(vdp_timer);

	void vram_map(address_map &map) ATTR_COLD;

protected:
	required_device<i8x9x_device> m_maincpu;
	required_memory_bank m_common_ram;
	required_device<wd_fdc_digital_device_base> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	optional_device<tms3556_device> m_vdp;
	required_device<sa16_base_device> m_wave;
	optional_device<mb63h149_device> m_keyscan;

	u8 m_floppy_select;
};

class roland_s50_state : public roland_s50_base_state
{
public:
	roland_s50_state(const machine_config &mconfig, device_type type, const char *tag)
		: roland_s50_base_state(mconfig, type, tag)
		, m_sram_bank(*this, "sram")
		, m_sram(*this, "sram", 0x10000U, ENDIANNESS_LITTLE)
		, m_io_view(*this, "io")
	{
	}

	void s50(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void p2_w(u8 data);
	void floppy_select_w(u8 data);

private:
	void ioga_out_w(u8 data);

	void mem_map(address_map &map) ATTR_COLD;

protected:
	required_memory_bank m_sram_bank;
	memory_share_creator<u16> m_sram;
	memory_view m_io_view;
};

class roland_s550_state : public roland_s50_state
{
public:
	roland_s550_state(const machine_config &mconfig, device_type type, const char *tag)
		: roland_s50_state(mconfig, type, tag)
		, m_lowram_bank(*this, "lowram")
		, m_lowmem_view(*this, "lowmem")
		, m_lowram(*this, "lowram", 0x10000U, ENDIANNESS_LITTLE)
	{
	}

	void s550(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void sram_bank_w(u8 data);

	void mem_map(address_map &map) ATTR_COLD;

	required_memory_bank m_lowram_bank;
	memory_view m_lowmem_view;
	memory_share_creator<u16> m_lowram;
};

class roland_w30_state : public roland_s50_base_state
{
public:
	roland_w30_state(const machine_config &mconfig, device_type type, const char *tag)
		: roland_s50_base_state(mconfig, type, tag)
		, m_bank1_view(*this, "bank1")
		, m_bank2_view(*this, "bank2")
		, m_psram1_bank(*this, "psram1")
		, m_psram2_bank(*this, "psram2")
		, m_psram(*this, "psram", 0x20000U, ENDIANNESS_LITTLE)
		, m_psram_bank(0)
	{
	}

	void w30(machine_config &config);
	[[maybe_unused]] void s330(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u8 psram_bank_r();
	void psram_bank_w(u8 data);
	void floppy_select_w(u8 data);
	u8 unknown_status_r();

	void w30_mem_map(address_map &map) ATTR_COLD;
	[[maybe_unused]] void s330_mem_map(address_map &map) ATTR_COLD;
	void psram1_map(address_map &map) ATTR_COLD;
	void psram2_map(address_map &map) ATTR_COLD;

	memory_view m_bank1_view;
	memory_view m_bank2_view;
	required_memory_bank m_psram1_bank;
	required_memory_bank m_psram2_bank;
	memory_share_creator<u16> m_psram;

	u8 m_psram_bank;
};

void roland_s50_state::machine_start()
{
	floppy_select_w(0);
	m_fdc->dden_w(0);

	m_sram_bank->configure_entries(0, 4, &m_sram[0], 0x4000);
	m_sram_bank->set_entry(0);
	m_common_ram->set_base(&m_sram[0]);

	save_item(NAME(m_floppy_select));
}

void roland_s550_state::machine_start()
{
	roland_s50_state::machine_start();

	m_lowram_bank->configure_entries(0, 8, &m_lowram[0], 0x2000);
	m_lowram_bank->set_entry(0);
}

void roland_s50_state::machine_reset()
{
	floppy_select_w(0);
}

void roland_w30_state::machine_start()
{
	m_fdc->dden_w(0);

	m_psram1_bank->configure_entries(0, 8, &m_psram[0x10000 / 2], 0x2000);
	m_psram2_bank->configure_entries(0, 4, &m_psram[0], 0x4000);
	m_common_ram->set_base(&m_psram[0]);

	save_item(NAME(m_psram_bank));
	save_item(NAME(m_floppy_select));
}

void roland_w30_state::machine_reset()
{
	floppy_select_w(0);
	psram_bank_w(0);
}

TIMER_DEVICE_CALLBACK_MEMBER(roland_s50_base_state::vdp_timer)
{
	// FIXME: internalize this ridiculousness
	m_vdp->interrupt();
}


void roland_s50_state::p2_w(u8 data)
{
	m_io_view.select(BIT(data, 5));
}

void roland_s50_state::ioga_out_w(u8 data)
{
	m_sram_bank->set_entry(BIT(data, 6, 2));
}

void roland_s550_state::sram_bank_w(u8 data)
{
	m_sram_bank->set_entry(BIT(data, 0, 2));
	m_lowram_bank->set_entry(BIT(data, 2, 3));
	m_lowmem_view.select(BIT(data, 2, 3) == 0 ? 0 : 1);
}

u8 roland_w30_state::psram_bank_r()
{
	return m_psram_bank;
}

void roland_w30_state::psram_bank_w(u8 data)
{
	m_psram_bank = data;
	m_bank1_view.select(BIT(data, 3, 4) == 0 ? 0 : 1);
	m_bank2_view.select(BIT(data, 0, 3) == 0 ? 0 : 1);
	m_psram1_bank->set_entry(BIT(data, 3, 3));
	m_psram2_bank->set_entry(BIT(data, 0, 2));
}

u8 roland_s50_base_state::floppy_status_r()
{
	floppy_image_device *floppy = nullptr;
	if (BIT(m_floppy_select, 2))
		floppy = m_floppy[0]->get_device();
	else if (BIT(m_floppy_select, 3))
		floppy = m_floppy[1]->get_device();

	u8 status = m_fdc->intrq_r() << 2 | m_fdc->drq_r() << 3;
	if (floppy)
	{
		if (!floppy->ready_r())
			status |= 1;
		if (!floppy->dskchg_r())
			status |= 2;
	}
	return status;
}

void roland_s50_state::floppy_select_w(u8 data)
{
	floppy_image_device *floppy = nullptr;
	if (BIT(data, 6))
		floppy = m_floppy[0]->get_device();
	else if (BIT(data, 7))
		floppy = m_floppy[1]->get_device();

	m_fdc->set_floppy(floppy);
	if (floppy)
		floppy->ss_w(BIT(data, 4));

	m_floppy_select = data >> 4;
}

void roland_w30_state::floppy_select_w(u8 data)
{
	floppy_image_device *floppy = nullptr;
	if (BIT(data, 2))
		floppy = m_floppy[0]->get_device();
	else if (BIT(data, 3))
		floppy = m_floppy[1]->get_device();

	m_fdc->set_floppy(floppy);
	if (floppy)
		floppy->ss_w(BIT(data, 0));

	m_floppy_select = data;
}

u8 roland_s50_base_state::floppy_unknown_r()
{
	return 1;
}

u16 roland_s50_base_state::key_r(offs_t offset)
{
	return m_keyscan->read(offset) << 1;
}

void roland_s50_base_state::key_w(offs_t offset, u16 data)
{
	m_keyscan->write(offset, data >> 1);
}

u8 roland_w30_state::unknown_status_r()
{
	return 0x1c;
}


void roland_s50_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).rom().region("program", 0);
	map(0x4000, 0x7fff).bankrw(m_common_ram);
	map(0x8000, 0xbfff).bankrw(m_sram_bank);
	map(0xc000, 0xffff).view(m_io_view);
	m_io_view[0](0xc000, 0xc000).w(FUNC(roland_s50_state::ioga_out_w));
	m_io_view[0](0xc200, 0xc200).rw(FUNC(roland_s50_state::floppy_status_r), FUNC(roland_s50_state::floppy_select_w));
	m_io_view[0](0xc300, 0xc300).r(FUNC(roland_s50_state::floppy_unknown_r));
	m_io_view[0](0xc800, 0xc807).rw(m_fdc, FUNC(wd1772_device::read), FUNC(wd1772_device::write)).umask16(0x00ff);
	m_io_view[0](0xd200, 0xd200).r(m_vdp, FUNC(tms3556_device::vram_r));
	m_io_view[0](0xd202, 0xd202).rw(m_vdp, FUNC(tms3556_device::initptr_r), FUNC(tms3556_device::vram_w));
	m_io_view[0](0xd204, 0xd204).rw(m_vdp, FUNC(tms3556_device::reg_r), FUNC(tms3556_device::reg_w));
	m_io_view[0](0xc000, 0xffff).rw(m_wave, FUNC(rf5c36_device::read), FUNC(rf5c36_device::write)).umask16(0xff00);
	m_io_view[1](0xc000, 0xcfff).mirror(0x3000).rw(FUNC(roland_s50_state::key_r), FUNC(roland_s50_state::key_w));
}

void roland_s550_state::mem_map(address_map &map)
{
	map(0x0000, 0x1fff).view(m_lowmem_view);
	m_lowmem_view[0](0x0000, 0x1fff).rom().region("program", 0);
	m_lowmem_view[1](0x0000, 0x1fff).bankrw(m_lowram_bank);
	map(0x2000, 0x3fff).rom().region("program", 0x2000);
	map(0x4000, 0x7fff).bankrw(m_common_ram);
	map(0x8000, 0xbfff).bankrw(m_sram_bank);
	map(0xc000, 0xffff).view(m_io_view);
	m_io_view[0](0xc200, 0xc200).rw(FUNC(roland_s550_state::floppy_status_r), FUNC(roland_s550_state::floppy_select_w));
	m_io_view[0](0xc300, 0xc300).r(FUNC(roland_s550_state::floppy_unknown_r));
	m_io_view[0](0xc800, 0xc807).rw(m_fdc, FUNC(wd1772_device::read), FUNC(wd1772_device::write)).umask16(0x00ff);
	m_io_view[0](0xd000, 0xd000).r(m_vdp, FUNC(tms3556_device::vram_r));
	m_io_view[0](0xd002, 0xd002).rw(m_vdp, FUNC(tms3556_device::initptr_r), FUNC(tms3556_device::vram_w));
	m_io_view[0](0xd004, 0xd004).rw(m_vdp, FUNC(tms3556_device::reg_r), FUNC(tms3556_device::reg_w));
	//m_io_view[0](0xd800, 0xd81f).rw(m_tvf, FUNC(mb654419u_device::read), FUNC(mb654419u_device::write)).umask16(0x00ff);
	m_io_view[0](0xe000, 0xe000).w(FUNC(roland_s550_state::sram_bank_w));
	m_io_view[0](0xe800, 0xe81f).w("outas", FUNC(bu3905_device::write)).umask16(0x00ff);
	m_io_view[0](0xf800, 0xf81f).m("scsi:7:scsic", FUNC(mb89352_device::map)).umask16(0x00ff);
	m_io_view[0](0xc000, 0xffff).rw(m_wave, FUNC(rf5c36_device::read), FUNC(rf5c36_device::write)).umask16(0xff00);
	m_io_view[1](0xc000, 0xffff).unmaprw();
}

void roland_w30_state::w30_mem_map(address_map &map)
{
	map(0x0000, 0x1fff).view(m_bank1_view);
	m_bank1_view[0](0x0000, 0x1fff).rom().region("program", 0);
	m_bank1_view[1](0x0000, 0x1fff).bankrw(m_psram1_bank);
	map(0x2000, 0x3fff).rom().region("program", 0x2000);
	map(0x4000, 0x7fff).bankrw(m_common_ram);
	map(0x8000, 0xbfff).view(m_bank2_view);
	m_bank2_view[0](0x8000, 0xbfff).rom().region("program", 0);
	m_bank2_view[1](0x8000, 0xbfff).bankrw(m_psram2_bank);
	map(0xc200, 0xc200).rw(FUNC(roland_w30_state::floppy_status_r), FUNC(roland_w30_state::floppy_select_w));
	map(0xc600, 0xc600).rw(FUNC(roland_w30_state::psram_bank_r), FUNC(roland_w30_state::psram_bank_w));
	map(0xc800, 0xc807).rw(m_fdc, FUNC(wd1772_device::read), FUNC(wd1772_device::write)).umask16(0x00ff);
	map(0xd806, 0xd806).r(FUNC(roland_w30_state::unknown_status_r));
	map(0xe000, 0xe01f).m("scsi:7:scsic", FUNC(mb89352_device::map)).umask16(0x00ff);
	map(0xe400, 0xe403).rw("lcd", FUNC(lm24014h_device::read), FUNC(lm24014h_device::write)).umask16(0x00ff);
	//map(0xe800, 0xe83f).w("output", FUNC(upd65006gf_376_3b8_device::write)).umask16(0x00ff);
	//map(0xf000, 0xf01f).rw(m_tvf, FUNC(mb654419u_device::read), FUNC(mb654419u_device::write)).umask16(0x00ff);
	map(0xc000, 0xf7ff).rw(m_wave, FUNC(sa16_device::read), FUNC(sa16_device::write)).umask16(0xff00);
	map(0xf800, 0xffff).rw(FUNC(roland_w30_state::key_r), FUNC(roland_w30_state::key_w));
}

[[maybe_unused]] void roland_w30_state::s330_mem_map(address_map &map)
{
	map(0x0000, 0x1fff).view(m_bank1_view);
	m_bank1_view[0](0x0000, 0x1fff).rom().region("program", 0);
	m_bank1_view[1](0x0000, 0x1fff).bankrw(m_psram1_bank);
	map(0x2000, 0x3fff).rom().region("program", 0x2000);
	map(0x4000, 0x7fff).bankrw(m_common_ram);
	map(0x8000, 0xbfff).view(m_bank2_view);
	m_bank2_view[0](0x8000, 0xbfff).rom().region("program", 0);
	m_bank2_view[1](0x8000, 0xbfff).bankrw(m_psram2_bank);
	map(0xc000, 0xffff).rw(m_wave, FUNC(sa16_device::read), FUNC(sa16_device::write)).umask16(0xff00);
}

void roland_s50_base_state::vram_map(address_map &map)
{
	map(0x0000, 0xffff).ram();
}

static INPUT_PORTS_START(s50)
INPUT_PORTS_END

static INPUT_PORTS_START(s550)
INPUT_PORTS_END

static INPUT_PORTS_START(w30)
INPUT_PORTS_END

[[maybe_unused]] static INPUT_PORTS_START(s330)
INPUT_PORTS_END

static void s50_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
}

static void floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_ROLAND_SDISK_FORMAT);
}

static void scsi_devices(device_slot_interface &device)
{
	device.option_add("cdrom", NSCSI_CDROM);
	device.option_add("harddisk", NSCSI_HARDDISK);
}

void roland_s50_state::s50(machine_config &config)
{
	C8095_90(config, m_maincpu, 24_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_s50_state::mem_map);
	m_maincpu->out_p2_cb().set(FUNC(roland_s50_state::p2_w));

	MB63H149(config, m_keyscan, 24_MHz_XTAL / 2);
	m_keyscan->int_callback().set_inputline(m_maincpu, i8x9x_device::EXTINT_LINE);

	WD1772(config, m_fdc, 8_MHz_XTAL); // WD1770-00 or WD1772-02
	m_fdc->drq_wr_callback().set_inputline(m_maincpu, i8x9x_device::HSI2_LINE);
	m_fdc->intrq_wr_callback().set_inputline(m_maincpu, i8x9x_device::HSI3_LINE);

	// Floppy unit: FDD4261A0K or FDD4251G0K
	FLOPPY_CONNECTOR(config, m_floppy[0], s50_floppies, "35dd", &floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], s50_floppies, nullptr, &floppy_formats).enable_sound(true);

	//UPD7538A(config, "fipcpu", 600_kHz_XTAL);

	TMS3556(config, m_vdp, 14.3496_MHz_XTAL); // TMS3556NL
	m_vdp->set_addrmap(0, &roland_s50_state::vram_map);
	//m_vdp->vsync_callback().set_inputline(m_maincpu, i8x9x_device::HSI1_LINE).invert();
	m_vdp->set_screen("screen");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_video_attributes(VIDEO_UPDATE_BEFORE_VBLANK);
	screen.set_screen_update(m_vdp, FUNC(tms3556_device::screen_update));
	screen.set_size(tms3556_device::TOTAL_WIDTH, tms3556_device::TOTAL_HEIGHT*2);
	screen.set_visarea(0, tms3556_device::TOTAL_WIDTH-1, 0, tms3556_device::TOTAL_HEIGHT-1);
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::RGB_3BIT);

	TIMER(config, "vdp_timer").configure_scanline(FUNC(roland_s50_state::vdp_timer), "screen", 0, 1);

	RF5C36(config, m_wave, 26.88_MHz_XTAL);
	m_wave->int_callback().set_inputline(m_maincpu, i8x9x_device::HSI0_LINE);
}

void roland_s550_state::s550(machine_config &config)
{
	s50(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &roland_s550_state::mem_map);
	m_fdc->intrq_wr_callback().set_nop();

	//UPD7537(config.device_replace(), "fipcpu", 400_kHz_XTAL);

	config.device_remove("keyscan");

	// SCSI controller on Option Board
	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:1", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:7").option_set("scsic", MB89352).machine_config([this](device_t *device) {
		device->set_clock(8_MHz_XTAL);
		downcast<mb89352_device &>(*device).out_irq_callback().set_inputline(m_maincpu, i8x9x_device::EXTINT_LINE);
	});

	BU3905(config, "outas");

	//MB654419U(config, m_tvf, 20_MHz_XTAL);

	m_wave->sh_callback().set("outas", FUNC(bu3905_device::axi_w));
}

void roland_w30_state::w30(machine_config &config)
{
	N8097BH(config, m_maincpu, 24_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_w30_state::w30_mem_map);

	MB63H149(config, m_keyscan, 24_MHz_XTAL / 2);
	m_keyscan->int_callback().set_inputline(m_maincpu, i8x9x_device::EXTINT_LINE);

	WD1772(config, m_fdc, 8_MHz_XTAL); // WD1772-02

	// Floppy unit: FX-354 (307F1JC)
	FLOPPY_CONNECTOR(config, m_floppy[0], s50_floppies, "35dd", &floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], s50_floppies, nullptr, &floppy_formats).enable_sound(true);

	// SCSI controller on main board, by option (KW-30)
	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:1", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:7").option_set("scsic", MB89352).clock(8_MHz_XTAL); // INTR & DREQ not connected

	LM24014H(config, "lcd"); // LCD unit: LM240142

	SA16(config, m_wave, 26.88_MHz_XTAL);
	m_wave->int_callback().set_inputline(m_maincpu, i8x9x_device::HSI0_LINE);

	//UPD65006GF_376_3B8(config, "output", 26.88_MHz_XTAL);

	//MB654419U(config, m_tvf, 20_MHz_XTAL);
}

void roland_w30_state::s330(machine_config &config)
{
	C8095_90(config, m_maincpu, 24_MHz_XTAL / 2); // P8097-90
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_w30_state::s330_mem_map);

	WD1772(config, m_fdc, 8_MHz_XTAL); // WD1772-02

	// Floppy unit: ND-362S-A
	FLOPPY_CONNECTOR(config, m_floppy[0], s50_floppies, "35dd", &floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], s50_floppies, nullptr, &floppy_formats).enable_sound(true);

	// LCD unit: DM1620-5BL7 (MW-5F)

	TMS3556(config, m_vdp, 14.3496_MHz_XTAL); // TMS3556NL
	m_vdp->set_addrmap(0, &roland_w30_state::vram_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_video_attributes(VIDEO_UPDATE_BEFORE_VBLANK);
	screen.set_screen_update(m_vdp, FUNC(tms3556_device::screen_update));
	screen.set_size(tms3556_device::TOTAL_WIDTH, tms3556_device::TOTAL_HEIGHT*2);
	screen.set_visarea(0, tms3556_device::TOTAL_WIDTH-1, 0, tms3556_device::TOTAL_HEIGHT-1);
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::RGB_3BIT);

	TIMER(config, "vdp_timer").configure_scanline(FUNC(roland_w30_state::vdp_timer), "screen", 0, 1);

	SA16(config, m_wave, 26.88_MHz_XTAL);
	m_wave->int_callback().set_inputline(m_maincpu, i8x9x_device::HSI0_LINE);
	m_wave->sh_callback().set("outas", FUNC(bu3905_device::axi_w));

	BU3905(config, "outas");

	//MB654419U(config, m_tvf, 20_MHz_XTAL);
}

ROM_START(s50)
	ROM_REGION16_LE(0x4000, "program", 0)
	// PROMs contain identical data but are mapped with even bytes loaded from IC64 and odd bytes from IC65
	ROM_LOAD("s-50.ic64", 0x0000, 0x4000, CRC(9a911016) SHA1(00a829d7921556c41d872c10b7bbb82b62b6c5cf))
	ROM_LOAD("s-50.ic65", 0x0000, 0x4000, CRC(9a911016) SHA1(00a829d7921556c41d872c10b7bbb82b62b6c5cf))

	ROM_REGION(0x1000, "fipcpu", 0) // on panel board
	ROM_LOAD("upd7538a-013_15179240.ic1", 0x0000, 0x1000, NO_DUMP)
ROM_END

ROM_START(s550)
	ROM_REGION16_LE(0x4000, "program", 0)
	// PROMs contain identical data but are mapped with even bytes loaded from IC6 and odd bytes from IC3
	ROM_LOAD("s-550_2-0-0.ic6", 0x0000, 0x4000, CRC(9dbc93b7) SHA1(bd9219772773f51e5ad7872daa1eaf03ec23f2c5))
	ROM_LOAD("s-550_2-0-0.ic3", 0x0000, 0x4000, CRC(9dbc93b7) SHA1(bd9219772773f51e5ad7872daa1eaf03ec23f2c5))

	ROM_REGION(0x800, "fipcpu", 0)
	ROM_LOAD("upd7537c-014_15179201.ic35", 0x000, 0x800, NO_DUMP)
ROM_END

ROM_START(w30)
	ROM_REGION16_LE(0x4000, "program", 0)
	ROM_LOAD16_BYTE("w-30_1-0-3.ic19", 0x0000, 0x2000, CRC(4aa83074) SHA1(6d6f3f9dc58a4aed7cbc5d8cfce4a8b3bc2a276a))
	ROM_LOAD16_BYTE("w-30_1-0-3.ic20", 0x0001, 0x2000, CRC(9c5e3c7f) SHA1(42a0463322be5f965967d531d3636376785c9820))

	ROM_REGION16_LE(0x100000, "wave", 0)
	ROM_LOAD16_BYTE("lh534146_15179935.ic30", 0x00000, 0x80000, NO_DUMP) // D0-D3 not connected
	ROM_LOAD16_BYTE("lh534145_15179936.ic29", 0x00001, 0x80000, NO_DUMP)
ROM_END

[[maybe_unused]] ROM_START(s330)
	ROM_REGION16_LE(0x4000, "program", 0)
	ROM_LOAD16_BYTE("s-330.ic15", 0x0000, 0x2000, NO_DUMP)
	ROM_LOAD16_BYTE("s-330.ic14", 0x0001, 0x2000, NO_DUMP)
ROM_END

} // anonymous namespace


SYST(1987, s50,  0,   0, s50,  s50,  roland_s50_state,  empty_init, "Roland", "S-50 Digital Sampling Keyboard", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1987, s550, s50, 0, s550, s550, roland_s550_state, empty_init, "Roland", "S-550 Digital Sampler", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1988, w30,  0,   0, w30,  w30,  roland_w30_state,  empty_init, "Roland", "W-30 Music Workstation", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
//SYST(1988, s330, w30, 0, s330, s330, roland_w30_state, empty_init, "Roland", "S-330 Digital Sampler", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



roland_sc55.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/*************************************************************************************************

    Roland Sound Canvas SC-55

    Skeleton by R. Belmont

    Reference and LCD photo: http://www.synthman.com/roland/Roland_SC-55.html
                             http://en.wikipedia.org/wiki/Roland_SCC-1

    The Roland SC55 is an expander (synthesizer without the keyboard)
    from 1991.  It has 24 voice polyphony, is 16 part multitimbral, and
    outputs 16-bit stereo samples at 32 kHz.  The synthesis engine uses a
    combination of Roland's LA and straight PCM playback.

    The front panel includes the power switch, a headphone jack with volume knob,
    a second MIDI IN port, a large LCD, ALL and MUTE buttons, and a group of up/down
    buttons for Part, Level, Reverb, Key Shift, Instrument, Pan, Chorus, and MIDI Channel.

    The CM-300 is a cut down version only one midi in and no display.

    The SCC-1 is an ISA board variant of the CM-300 with a MPU-401 frontend added to
    communicate with the synth.


    Main PCB:

    20.0 MHz crystal
    Roland R15239147  HG62E11B23FS  1L1 Japan
    Roland R15199778  6435328A97F   1M1 R Japan - Hitachi H8/532 MCU with internal ROM (Hitachi p/n HD6435328A97F)
    Roland R15239148  24201F002  9148EAI Japan
    Roland R15209363  LH532H6D   9152 D
    R15239176  BU3910F
    HM62256ALFP-12T   32K by 8-bit RAM
    65256BLFP-12T     32K by 8-bit high-speed pseudo-static RAM
    MB89251A - Serial data transceiver

    LCD controller (on front panel board) is a Toshiba T7934.
*/

#include "emu.h"
#include "machine/ram.h"
#include "cpu/h8500/h8532.h"


namespace {

static INPUT_PORTS_START( sc55 )
INPUT_PORTS_END

class sc55_state : public driver_device
{
public:
	sc55_state(const machine_config &mconfig, device_type type, const char *tag);

	void sc55(machine_config &config);

private:
	required_device<h8532_device> m_maincpu;

	void sc55_map(address_map &map) ATTR_COLD;
};

sc55_state::sc55_state(const machine_config &mconfig, device_type type, const char *tag) :
	driver_device(mconfig, type, tag),
	m_maincpu(*this, "maincpu")
{
}

void sc55_state::sc55_map(address_map &map)
{
	map(0x00000, 0x07fff).rom().region("maincpu", 0);
	map(0x40000, 0x7ffff).rom().region("progrom", 0);
}

void sc55_state::sc55(machine_config &config)
{
	HD6435328(config, m_maincpu, 20_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &sc55_state::sc55_map);
}

ROM_START( sc55 )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "roland_r15199778_6435328a97f.ic30", 0x0000, 0x8000, CRC(4ed0d171) SHA1(dd01ec54027751c2f2f2e47bbb7a0bf3d1ca8ae2) )

	ROM_REGION( 0x40000, "progrom", 0 )
	ROM_LOAD( "roland_r15209363.ic23", 0x000000, 0x040000, CRC(2dc58549) SHA1(9c17f85e784dc1549ac1f98d457b353393331f6b) )

	ROM_REGION( 0x300000, "waverom", 0 )
	ROM_LOAD( "roland-gss.a_r15209276.ic28", 0x000000, 0x100000, CRC(1ac774d3) SHA1(8cc3c0d7ec0993df81d4ca1970e01a4b0d8d3775) )
	ROM_LOAD( "roland-gss.b_r15209277.ic27", 0x100000, 0x100000, CRC(8dcc592a) SHA1(80e6eb130c18c09955551563f78906163c55cc11) )
	ROM_LOAD( "roland-gss.c_r15209281.ic26", 0x200000, 0x100000, CRC(e21ebc04) SHA1(7454b817778179806f3f9d1985b3a2ef67ace76f) )
ROM_END

ROM_START( sc155 )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "roland_r15199799.ic30", 0x0000, 0x8000, CRC(a160da90) SHA1(76f646bc03f66dbee7606f2181d4ea76f05ece7d) )

	ROM_REGION( 0x40000, "progrom", 0 )
	ROM_LOAD( "roland_r15209361.ic15", 0x000000, 0x040000, CRC(e19d4a52) SHA1(e9e1bb1bc2691145ffe17f01a48d6614c9f22225) )

	ROM_REGION( 0x300000, "waverom", 0 )
	ROM_LOAD( "roland-gss.a_r15209276.ic28", 0x000000, 0x100000, CRC(1ac774d3) SHA1(8cc3c0d7ec0993df81d4ca1970e01a4b0d8d3775) )
	ROM_LOAD( "roland-gss.b_r15209277.ic27", 0x100000, 0x100000, CRC(8dcc592a) SHA1(80e6eb130c18c09955551563f78906163c55cc11) )
	ROM_LOAD( "roland-gss.c_r15209281.ic26", 0x200000, 0x100000, CRC(e21ebc04) SHA1(7454b817778179806f3f9d1985b3a2ef67ace76f) )
ROM_END

ROM_START( cm300 )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "roland_r15199774.ic6", 0x0000, 0x8000, NO_DUMP ) // scc-1: ic10

	ROM_REGION( 0x40000, "progrom", 0 )
	// scc-1: ic14
	ROM_DEFAULT_BIOS("120")
	ROM_SYSTEM_BIOS(0, "120", "GS Standard  VER=1.20")
	ROMX_LOAD( "roland_r15279812.ic8", 0x000000, 0x040000, CRC(546542ab) SHA1(b288cef2aa2df60cbaa2c084a77a68b298de9567), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "110", "GS Standard  VER=1.10")
	ROMX_LOAD( "roland_r15279809.ic8", 0x000000, 0x040000, CRC(94d96954) SHA1(32e76286a626cf960b6665792e53dce3d51170d1), ROM_BIOS(1) )

	ROM_REGION( 0x300000, "waverom", 0 )
	ROM_LOAD( "roland_r15279806.ic2", 0x000000, 0x100000, CRC(b1b31a41) SHA1(891cf2ac5ca64f453b370b9076f9fb2b4ebc5dcf) ) // scc-1: ic17
	ROM_LOAD( "roland_r15279807.ic3", 0x100000, 0x100000, CRC(359edfb2) SHA1(49f38f181b444fc39ad86c4ddab3b25bf839d0b4) ) // scc-1: ic18
	ROM_LOAD( "roland_r15279808.ic4", 0x200000, 0x100000, CRC(0f826c7f) SHA1(4d91cdeaed048d653dbf846a221003c3a3f08279) ) // scc-1: ic19

	// Only in the scc-1, should be moved to ISA
	// 4Mhz 6801
	ROM_REGION( 0x1000, "mpu401", 0)
	ROM_LOAD( "roland_r15239182.ic2", 0x0000, 0x1000, CRC(8aea085f) SHA1(3cce3fb328ec4055a53ae976d790ced257ae1f67) )
ROM_END

} // anonymous namespace


SYST( 1991, sc55, 0, 0, sc55, sc55, sc55_state, empty_init, "Roland", "Sound Canvas SC-55", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
SYST( 1992, sc155, sc55, 0, sc55, sc55, sc55_state, empty_init, "Roland", "Sound Canvas SC-155", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
SYST( 1991, cm300, sc55, 0, sc55, sc55, sc55_state, empty_init, "Roland", "Sound Canvas CM-300", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



roland_sc55mk2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/*************************************************************************************************

    Roland Sound Canvas SC-55mkII

    Skeleton by R. Belmont

    The Roland SC-55mkII is an expander (synthesizer without the keyboard)
    from 1994.  It has 28 voice polyphony, is 16 part multitimbral, and
    outputs 18-bit stereo samples at 32 kHz.  The synthesis engine uses a
    combination of Roland's LA and straight PCM playback.

    The front panel includes the power switch, a headphone jack with volume knob,
    a second MIDI IN port, a large LCD, ALL and MUTE buttons, and a group of up/down
    buttons for Part, Level, Reverb, Key Shift, Instrument, Pan, Chorus, and MIDI Channel.

    Main PCB:

    20.0 MHz crystal
    Roland R15199848  HD6475328F    Hitachi H8/532 MCU with internal ROM (main CPU)
    Roland R15199849  M37409M2-FP   Mitsubishi M740 series microcontroller (sub CPU)
    Roland R15209463  4M MASK ROM   Main program, version 1.00
    Roland R15209359  16M WAVE ROM
    Roland R15279813  8M WAVE ROM
    Roland R15279543  SRM20256SLM10 SRAM (32K x 8-bit non-volatile work RAM)
    Roland R15179463  TC51832FL-85  PSRAM (32Kword x 8-bit PCM custom chip's work RAM)
    Roland R15219714  uPD63200GS-E2 D/A converter
    PCM custom is IC26, part number is not currently known

    TODO:
    - H8/500 series CPU core
    - M37409M2 MCU support
    - LCD
    - Sound synthesis
*/

#include "emu.h"

#include "cpu/h8500/h8532.h"


namespace {

static INPUT_PORTS_START( sc55mk2 )
INPUT_PORTS_END

class sc55mk2_state : public driver_device
{
public:
	sc55mk2_state(const machine_config &mconfig, device_type type, const char *tag);

	void sc55mk2(machine_config &config);

private:
	required_device<h8532_device> m_maincpu;

	void sc55mk2_map(address_map &map) ATTR_COLD;
};

sc55mk2_state::sc55mk2_state(const machine_config &mconfig, device_type type, const char *tag) :
	driver_device(mconfig, type, tag),
	m_maincpu(*this, "maincpu")
{
}

void sc55mk2_state::sc55mk2_map(address_map &map)
{
	map(0x40000, 0x7ffff).rom().region("progrom", 0);
}

void sc55mk2_state::sc55mk2(machine_config &config)
{
	HD6435328(config, m_maincpu, 20_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &sc55mk2_state::sc55mk2_map);
}

ROM_START( sc55mk2 )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASE00 )  // H8/532 main code
	ROM_LOAD("r15199858_main_mcu.bin", 0x000000, 0x008000, CRC(9b66631f) SHA1(b91bb1d9dccffe831b7cfde7800a3fe32b2fbda6))

	ROM_REGION( 0x1000, "subcpu", 0 )   // M37409M2 sub-CPU with 3 UARTs, clocked at 10 MHz
	ROM_LOAD("r15199880_secondary_mcu.bin", 0x000000, 0x001000, CRC(702c0a82) SHA1(4d48578d811a762a8e7bfaf18989bcac70ae1ba4))

	ROM_REGION( 0x80000, "progrom", 0 ) // additional H8/532 code and patch data
	ROM_LOAD("r00233567_control.bin", 0x000000, 0x080000, CRC(fcee1e8e) SHA1(078cb5feea05e80bb9a1bb857a2163ee434fd053))

	ROM_REGION( 0x300000, "waverom", 0 )
	ROM_LOAD("r15209359_pcm_1.bin", 0x000000, 0x200000, CRC(1519d3b3) SHA1(96708cb21381c2fd03de4babbf7aea301c7594a6))
	ROM_LOAD("r15279813_pcm_2.bin", 0x200000, 0x100000, CRC(0f826c7f) SHA1(4d91cdeaed048d653dbf846a221003c3a3f08279))
ROM_END

} // anonymous namespace


SYST( 1994, sc55mk2, 0, 0, sc55mk2, sc55mk2, sc55mk2_state, empty_init, "Roland", "Sound Canvas SC-55mkii", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



roland_sc88.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Roland SC-88 MIDI sound generator.

****************************************************************************/

#include "emu.h"
#include "cpu/h8500/h8510.h"
//#include "cpu/m6502/m38881.h"
#include "machine/nvram.h"


namespace {

class roland_sc88_state : public driver_device
{
public:
	roland_sc88_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void sc88vl(machine_config &config);

private:
	void main_map(address_map &map) ATTR_COLD;

	required_device<h8510_device> m_maincpu;
};


void roland_sc88_state::main_map(address_map &map)
{
	map(0x000000, 0x07ffff).rom().region("progrom", 0);
	map(0x080000, 0x08ffff).ram().share("nvram");
}

static INPUT_PORTS_START(sc88vl)
INPUT_PORTS_END

void roland_sc88_state::sc88vl(machine_config &config)
{
	HD6415108(config, m_maincpu, 20_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_sc88_state::main_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // SRM2A256SLM-70 x2 + battery

	//M38881M2(config, "subcpu", 20_MHz_XTAL / 2);
}

ROM_START(sc88vl)
	ROM_REGION16_BE(0x80000, "progrom", 0)
	ROM_LOAD16_WORD_SWAP("roland_sc88_vl-1.04.ic29", 0x00000, 0x80000, CRC(66aa5762) SHA1(3a20f8f8cefd0d5e1edb103046f6fe94bb73ac7a))

	ROM_REGION(0x2000, "subcpu", 0)
	ROM_LOAD("roland-r00232667-m38881m2-150gp.ic23", 0x0000, 0x2000, NO_DUMP)

	ROM_REGION16_LE(0x800000, "waverom", 0)
	ROM_LOAD("roland-r00785356-hn624316fbc25.ic10", 0x000000, 0x200000, NO_DUMP)
	ROM_LOAD("roland-r00785367-hn624316fbc26.ic7",  0x200000, 0x200000, NO_DUMP)
	ROM_LOAD("roland-r00788489-hn624316fbc27.ic4",  0x400000, 0x200000, NO_DUMP)
	ROM_LOAD("roland-r00788490-hn624316fbc28.ic2",  0x600000, 0x200000, NO_DUMP)
ROM_END

} // anonymous namespace


SYST(1995, sc88vl, 0, 0, sc88vl, sc88vl, roland_sc88_state, empty_init, "Roland", "SoundCanvas SC-88VL", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



roland_tb303.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
/***************************************************************************

Roland TB-303 Bass Line, 1982, designed by Tadao Kikumoto

Hardware notes:
- NEC uCOM-43 MCU, labeled D650C 133
- 3*uPD444C 1024x4 Static CMOS SRAM
- board is packed with discrete components

TODO:
- still too much to list here

***************************************************************************/

#include "emu.h"

#include "cpu/ucom4/ucom4.h"
#include "machine/clock.h"
#include "video/pwm.h"

#include "tb303.lh"


namespace {

class tb303_state : public driver_device
{
public:
	tb303_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void tb303(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<ucom4_cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_ioport_array<5> m_inputs;

	u8 m_ram[0xc00];
	u8 m_ram_addrset[3];
	u16 m_ram_address = 0;
	u8 m_ram_data = 0;
	bool m_ram_ce = false;
	bool m_ram_we = false;
	u8 m_led_data = 0;
	u8 m_inp_mux = 0;

	void refresh_ram();
	template<int N> void ram_address_w(u8 data);
	void ram_data_w(u8 data);
	u8 ram_data_r();
	void strobe_w(u8 data);

	void update_leds();
	void led_w(u8 data);
	void input_w(u8 data);
	u8 input_r(offs_t offset);
};

void tb303_state::machine_start()
{
	// zerofill
	memset(m_ram, 0, sizeof(m_ram));
	memset(m_ram_addrset, 0, sizeof(m_ram_addrset));

	// register for savestates
	save_item(NAME(m_ram));
	save_item(NAME(m_ram_addrset));
	save_item(NAME(m_ram_address));
	save_item(NAME(m_ram_data));
	save_item(NAME(m_ram_ce));
	save_item(NAME(m_ram_we));
	save_item(NAME(m_led_data));
	save_item(NAME(m_inp_mux));
}



/***************************************************************************
    I/O
***************************************************************************/

// external ram

void tb303_state::refresh_ram()
{
	// MCU E2,E3 goes through a 4556 IC(pin 14,13) to one of uPD444 _CE:
	// _Q0: N/C, _Q1: IC-5, _Q2: IC-3, _Q3: IC-4
	m_ram_ce = true;
	u8 hi = 0;
	switch (m_ram_addrset[2] >> 2 & 3)
	{
		case 0: m_ram_ce = false; break;
		case 1: hi = 0; break;
		case 2: hi = 1; break;
		case 3: hi = 2; break;
	}

	if (m_ram_ce)
	{
		// _WE must be high(read mode) for address transitions
		if (!m_ram_we)
			m_ram_address = hi << 10 | (m_ram_addrset[2] << 8 & 0x300) | m_ram_addrset[1] << 4 | m_ram_addrset[0];
		else
			m_ram[m_ram_address] = m_ram_data;
	}
}

template<int N>
void tb303_state::ram_address_w(u8 data)
{
	// MCU D,F,E: RAM address
	m_ram_addrset[N] = data;
	refresh_ram();

	// MCU D,F01: pitch data
	//..
}

void tb303_state::ram_data_w(u8 data)
{
	// MCU C: RAM data
	m_ram_data = data;
	refresh_ram();
}

u8 tb303_state::ram_data_r()
{
	// MCU C: RAM data
	if (m_ram_ce && !m_ram_we)
		return m_ram[m_ram_address];
	else
		return 0;
}

void tb303_state::strobe_w(u8 data)
{
	// MCU I0: RAM _WE
	m_ram_we = (data & 1) ? false : true;
	refresh_ram();

	// MCU I1: pitch data latch strobe
	// MCU I2: gate signal
}


// switch board

void tb303_state::update_leds()
{
	// 4*4 LED matrix from port G/H:
	/*
	    0.0 D204    1.0 D211    2.0 D217    3.0 D205
	    0.1 D206    1.1 D213    2.1 D218    3.1 D207
	    0.2 D208    1.2 D215    2.2 D220    3.2 D210
	    0.3 D209    1.3 D216    2.3 D221    3.3 D212
	*/
	m_display->matrix(m_inp_mux, m_led_data);

	// todo: battery led
	// todo: 4 more leds(see top-left part)
}

void tb303_state::led_w(u8 data)
{
	// MCU G: leds state
	m_led_data = data;
	update_leds();
}

void tb303_state::input_w(u8 data)
{
	// MCU H: input/led mux
	m_inp_mux = data ^ 0xf;
	update_leds();
}

u8 tb303_state::input_r(offs_t offset)
{
	u8 data = 0;

	// MCU A,B: multiplexed inputs
	// if input mux(port H) is 0, port A status buffer & gate is selected (via Q5 NAND)
	if (offset == 0 && m_inp_mux == 0)
	{
		// todo..
		data = m_inputs[4]->read();
	}
	else
	{
		for (int i = 0; i < 4; i++)
			if (BIT(m_inp_mux, i))
				data |=  m_inputs[i]->read();

		data >>= (offset*4) & 0xf;
	}

	return data;
}



/***************************************************************************
    Inputs
***************************************************************************/

static INPUT_PORTS_START( tb303 )
	PORT_START("IN.0") // H0 port A/B
	PORT_CONFNAME( 0x03, 0x03, "Mode" )
	PORT_CONFSETTING(    0x03, "Track Write" )
	PORT_CONFSETTING(    0x02, "Track Play" )
	PORT_CONFSETTING(    0x00, "Pattern Play" )
	PORT_CONFSETTING(    0x01, "Pattern Write" )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("DEL  C#")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("INS  D#")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("1  C")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("2  D")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("3  E")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_NAME("4  F")

	PORT_START("IN.1") // H1 port A/B
	PORT_CONFNAME( 0x07, 0x00, "Track / Patt.Group" )
	PORT_CONFSETTING(    0x00, "1 / I" )
	PORT_CONFSETTING(    0x01, "2 / I" )
	PORT_CONFSETTING(    0x02, "3 / II" )
	PORT_CONFSETTING(    0x03, "4 / II" )
	PORT_CONFSETTING(    0x04, "5 / III" )
	PORT_CONFSETTING(    0x05, "6 / III" )
	PORT_CONFSETTING(    0x06, "7 / IV" )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_NAME("5  G")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_NAME("6  A")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_NAME("7  B")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_NAME("8  C")

	PORT_START("IN.2") // H2 port A/B
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("Pattern Clear")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("Function")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Pitch Mode")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Time Mode")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_NAME("9  Step")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_NAME("0  3n")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("100  A")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("200  B")

	PORT_START("IN.3") // H3 port B
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("F#")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("G#")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Y) PORT_NAME("A#")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Back")

	PORT_START("IN.4") // H=0 port A
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_NAME("Run/Stop")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SPACE) PORT_NAME("Tap")
	PORT_BIT( 0xfc, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END



/***************************************************************************
    Machine Configs
***************************************************************************/

void tb303_state::tb303(machine_config &config)
{
	// basic machine hardware
	NEC_D650(config, m_maincpu, 454545); // LC circuit(TI S74230), 2.2us
	m_maincpu->read_a().set(FUNC(tb303_state::input_r));
	m_maincpu->read_b().set(FUNC(tb303_state::input_r));
	m_maincpu->read_c().set(FUNC(tb303_state::ram_data_r));
	m_maincpu->write_c().set(FUNC(tb303_state::ram_data_w));
	m_maincpu->write_d().set(FUNC(tb303_state::ram_address_w<0>));
	m_maincpu->write_e().set(FUNC(tb303_state::ram_address_w<2>));
	m_maincpu->write_f().set(FUNC(tb303_state::ram_address_w<1>));
	m_maincpu->write_g().set(FUNC(tb303_state::led_w));
	m_maincpu->write_h().set(FUNC(tb303_state::input_w));
	m_maincpu->write_i().set(FUNC(tb303_state::strobe_w));

	auto &irq_clock(CLOCK(config, "irq_clock"));
	irq_clock.set_period(attotime::from_usec(1800)); // clock rate 1.8ms
	irq_clock.set_duty_cycle(0.1); // short duty cycle
	irq_clock.signal_handler().set_inputline(m_maincpu, 0);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 4);
	config.set_default_layout(layout_tb303);

	// sound hardware
	// discrete...
}



/***************************************************************************
    ROM Definitions
***************************************************************************/

ROM_START( tb303 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d650c-133.ic8", 0x0000, 0x0800, CRC(268a8d8b) SHA1(7a4236b2bc9a5cd4c80c63ca1a193e03becfcb4c) )
ROM_END

} // anonymous namespace



/***************************************************************************
    Drivers
***************************************************************************/

SYST( 1982, tb303, 0, 0, tb303, tb303, tb303_state, empty_init, "Roland", "TB-303 Bass Line", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



roland_tnsc1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:

/*
    This is just a holder for the Roland "Style Data ROM" Software List to ensure they aren't orphaned
    These "Style Data ROM" cards are used by various devices, but none of the devices have been dumped

    Once a supported system is dumped this can be removed and the list can be hooked up to that

    Possible systems:
      Roland E-5
      Roland E-20
      Roland E-30
      Roland E-35
      Roland E-70
      Roland Pro-E
      Roland E/RA-50
      Roland RA-90
      Roland CA-30
      Roland KR-500
      Roland KR-3000
*/

#include "emu.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "softlist_dev.h"


namespace {

class rlndtnsc1_state : public driver_device
{
public:
	rlndtnsc1_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_cart(*this, "cartslot")
	{ }

	void rlndtnsc1(machine_config &config);
protected:
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
	optional_device<generic_slot_device> m_cart;
};


static INPUT_PORTS_START( rlndtnsc1 )
INPUT_PORTS_END

DEVICE_IMAGE_LOAD_MEMBER(rlndtnsc1_state::cart_load)
{
	uint32_t size = m_cart->common_get_size("rom");
	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");
	return std::make_pair(std::error_condition(), std::string());
}

void rlndtnsc1_state::rlndtnsc1(machine_config &config)
{
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "roland_tnsc1");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(rlndtnsc1_state::cart_load));
	m_cart->set_must_be_loaded(true);

	SOFTWARE_LIST(config, "cart_list").set_original("roland_tnsc1");
}

ROM_START( rlndtnsc1 )
ROM_END

} // anonymous namespace


CONS( 198?, rlndtnsc1, 0, 0, rlndtnsc1, rlndtnsc1, rlndtnsc1_state, empty_init, "Roland", "Roland Music Style Card TN-SC1 Software List holder", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



roland_tnsc2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:

/*
    This is just a holder for the Roland "Style Data ROM" Software List to ensure they aren't orphaned
    These "Style Data ROM" cards are used by various devices, but none of the devices have been dumped

    Once a supported system is dumped this can be removed and the list can be hooked up to that

    Possible systems:
      Roland E-35
      Roland E-36
      Roland E-56
      Roland E-70
      Roland RA-90
      Roland KR-650
      Roland KR-3500
      Roland KR-4500
      Roland KR-5500

    The ROM dump of TN-SC2-04 begins with "Roland E-70A", so this was probably the first system the cards were intended for.
*/

#include "emu.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "softlist_dev.h"


namespace {

class rlndtnsc2_state : public driver_device
{
public:
	rlndtnsc2_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_cart(*this, "cartslot")
	{ }

	void rlndtnsc2(machine_config &config);
protected:
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
	optional_device<generic_slot_device> m_cart;
};


static INPUT_PORTS_START( rlndtnsc2 )
INPUT_PORTS_END

DEVICE_IMAGE_LOAD_MEMBER(rlndtnsc2_state::cart_load)
{
	uint32_t size = m_cart->common_get_size("rom");
	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");
	return std::make_pair(std::error_condition(), std::string());
}

void rlndtnsc2_state::rlndtnsc2(machine_config &config)
{
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "roland_tnsc2");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(rlndtnsc2_state::cart_load));
	m_cart->set_must_be_loaded(true);

	SOFTWARE_LIST(config, "cart_list").set_original("roland_tnsc2");
}

ROM_START( rlndtnsc2 )
ROM_END

} // anonymous namespace


CONS( 198?, rlndtnsc2, 0, 0, rlndtnsc2, rlndtnsc2, rlndtnsc2_state, empty_init, "Roland", "Roland Music Style Card TN-SC2 Software List holder", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



roland_tr505.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/**********************************************************************

    Skeleton driver for Roland TR-505 drum machine.

    At present only the sound ROM is dumped. The firmware is entirely
    an internal mask program.

**********************************************************************/

#include "emu.h"
#include "mb63h114.h"
//#include "bus/midi/midi.h"
#include "cpu/m6800/m6801.h"
//#include "machine/nvram.h"
//#include "video/upd7227.h"


namespace {

class roland_tr505_state : public driver_device
{
public:
	roland_tr505_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mac(*this, "mac")
	{
	}

	void tr505(machine_config &config);

private:
	required_device<hd6301y0_cpu_device> m_maincpu;
	required_device<mb63h114_device> m_mac;
};


static INPUT_PORTS_START(tr505)
INPUT_PORTS_END

void roland_tr505_state::tr505(machine_config &config)
{
	HD6301Y0(config, m_maincpu, 4_MHz_XTAL); // HD6301Y0A in single chip mode (MP0 = MP1 = +5V)
	m_maincpu->set_disable();

	//NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // TC5517APL + battery

	//UPD7225(config, "lcdd");

	MB63H114(config, m_mac, 1.6_MHz_XTAL);
}

ROM_START(tr505)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_LOAD("hd6301y0a99p.ic3", 0x0000, 0x4000, NO_DUMP) // Version 1.1 (690700 & up)
	//ROM_LOAD("hd6301y0a51p.ic3", 0x0000, 0x4000, NO_DUMP) // Version 1.0 (630100–690699)

	ROM_REGION(0x20000, "mac", 0)
	ROM_LOAD("tr-505_rawromdump.bin", 0x00000, 0x20000, CRC(2234c834) SHA1(6441d3e7b53aff4511b23021dc854b7a5cc57689)) // TC531000P
ROM_END

} // anonymous namespace


SYST(1986, tr505, 0, 0, tr505, tr505, roland_tr505_state, empty_init, "Roland", "TR-505 Rhythm Composer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



roland_tr606.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
/***************************************************************************

Roland TR-606 Drumatix, early 1982

Hardware notes:
- NEC uCOM-43 MCU, labeled D650C 128
- 2*uPD444C 1024x4 Static CMOS SRAM
- board is packed with discrete components

TODO:
- everything

***************************************************************************/

#include "emu.h"

#include "cpu/ucom4/ucom4.h"
#include "machine/clock.h"


namespace {

class tr606_state : public driver_device
{
public:
	tr606_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu")
	{ }

	void tr606(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<ucom4_cpu_device> m_maincpu;
};

void tr606_state::machine_start()
{
}



/***************************************************************************
    Inputs
***************************************************************************/

static INPUT_PORTS_START( tr606 )
INPUT_PORTS_END



/***************************************************************************
    Machine Configs
***************************************************************************/

void tr606_state::tr606(machine_config &config)
{
	// basic machine hardware
	NEC_D650(config, m_maincpu, 454545); // LC circuit(TI S74230), 2.2us

	auto &irq_clock(CLOCK(config, "irq_clock"));
	irq_clock.set_period(attotime::from_usec(1800)); // clock rate 1.8ms (same as tb303)
	irq_clock.set_duty_cycle(0.1); // short duty cycle
	irq_clock.signal_handler().set_inputline(m_maincpu, 0);

	// sound hardware
	// discrete...
}



/***************************************************************************
    ROM Definitions
***************************************************************************/

ROM_START( tr606 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d650c-128.ic4", 0x0000, 0x0800, CRC(eee88f80) SHA1(ae605ce2b95adc2e0bacde3cd7ed0f39ac88b981) )
ROM_END

} // anonymous namespace



/***************************************************************************
    Drivers
***************************************************************************/

SYST( 1982, tr606, 0, 0, tr606, tr606, tr606_state, empty_init, "Roland", "TR-606 Drumatix", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



roland_tr707.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR,m1macrophage
/****************************************************************************

    Driver for Roland TR-707/727 drum machines.

    From the Service Notes: “The differences between two models [TR-707 and
    TR-727] are sound data, component values in several audio stages and a
    couple of pin connections at IC30 of Voice board. Both models derive all
    rhythm sounds from PCM-encoded samples of real sounds stored in ROM.”

    The TR-707 and TR-727 have 10 voices, each of which can be played
    simultaneously. Some of the voices have two sample variations, and the
    hi-hat voice has two decay variations (open and closed). Only one variation
    per voice can play at a time. This brings the total number of sounds to 15.
    While there are 16 drum pads, the two "closed hi hat" pads ("short whistle"
    for the TR-727) produce the same sound.

    There are 2 voice architectures. The "multiplex sound" section uses a single
    8-bit DAC with time-multiplexing for 8 voices. The "single sound" section
    consists of two independent ROMs and 6-bit DACs. More information in
    tr707_audio_device::device_add_mconfig().

    The TR-707/727 come with the following sounds:
     Pad       TR-707             TR-727
    * 1       Bass Drum 1        Hi Bongo
    * 2       Bass Drum 2        Low Bongo
    * 3       Snare Drum 1       Mute Hi Conga
    * 4       Snare Drum 2       Open Hi Conga
    * 5       Low Tom            Low Conga
    * 6       Mid Tom            Hi Timbale
    * 7       Hi Tom             Low Timbale
    * 8       Rimshot            Hi Agogo
    * 9       Cowbell            Low Agogo
    * 10      Handclap           Cabasa
    * 11      Tambourine         Maracas
    * 12, 13  Closed Hi Hat      Short Whistle
    * 14      Open Hi Hat        Long Whistle
    * 15      Crash Cymbal       Quijada
    * 16      Ride Cymbal        Star Chime

    Documentation and code use the TR-707 sound names.

    Reasons for MACHINE_IMPERFECT_SOUND, and other subtle imperfections:
    * The single-transistor VCAs for the cymbals and hi-hat are modeled as
      linear, which they aren't.
    * No time-multiplexing of the DAC across 8 voices. Using 8 DACs for now.
    * BPFs for bass, snare and toms change their response curve based on
      signal amplitude. The way the circuit is designed might also add distortion.
      This effect is not modeled.
    * Diodes in some of the EGs might shape the decay curve. These are not modeled.
    * Some uncertainty about the hat EG circuit. See update_hat_eg().

    This driver is based on the TR-707 service notes, and is intended as an
    educational tool.
****************************************************************************/

#include "emu.h"
#include "mb63h114.h"
#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/m6800/m6801.h"
#include "machine/7474.h"
#include "machine/nvram.h"
#include "machine/rescap.h"
#include "machine/timer.h"
#include "sound/dac.h"
#include "sound/flt_biquad.h"
#include "sound/flt_rc.h"
#include "sound/flt_vol.h"
#include "sound/mixer.h"
#include "sound/va_eg.h"
#include "sound/va_vca.h"
#include "video/hd61602.h"
#include "video/pwm.h"
#include "speaker.h"

#include "roland_tr707.lh"

#define LOG_TRIGGER (1U << 1)
#define LOG_SYNC    (1U << 2)
#define LOG_TEMPO   (1U << 3)
#define LOG_ACCENT  (1U << 4)
#define LOG_CART    (1U << 5)
#define LOG_MIX     (1U << 6)
#define LOG_KEYS    (1U << 7)

#define VERBOSE (LOG_GENERAL)
//#define LOG_OUTPUT_FUNC osd_printf_info

#include "logmacro.h"

namespace {

enum mux_voice
{
	MV_BASS = 0,
	MV_SNARE,
	MV_LOW_TOM,
	MV_MID_TOM,
	MV_HI_TOM,
	MV_HI_HAT,
	MV_RIMSHOT,
	MV_HANDCLAP,
	MV_COUNT
};

constexpr const double MUX_EG_C[MV_COUNT] =
{
	CAP_U(0.047),  // C53
	CAP_U(0.047),  // C48
	CAP_U(0.047),  // C54
	CAP_U(0.047),  // C52
	CAP_U(0.047),  // C58
	CAP_U(1),      // C47
	CAP_U(0.047),  // C57
	CAP_U(0.047),  // C55
};

enum cymbal_voice
{
	CV_CRASH = 0,
	CV_RIDE,
	CV_COUNT
};

constexpr const double CYMBAL_EG_C[CV_COUNT] =
{
	CAP_U(1),  // C50
	CAP_U(1),  // C49
};

enum mix_channel
{
	MC_BASS = 0,
	MC_SNARE,
	MC_LOW_TOM,
	MC_MID_TOM,
	MC_HI_TOM,
	MC_RIMSHOT,
	MC_HANDCLAP,
	MC_HI_HAT,
	MC_CRASH,
	MC_RIDE,
	MC_COUNT
};

// Values for components that differ between the TR707 and TR727.
struct component_config
{
	// IC30 configuration.
	const u8 xck2_input;  // 0: A, 1: B, 2: C, 3: D.

	// Envelope generator discharge resistors.
	const double R95;
	const double R102;
	const double R82;
	const double R91;
	const double R73;

	// Mixer channel pan resistors.
	const double R203;
	const double R205;
	const double R206;
	const double R207;
	const double R208;
	const double R211;
	const double R214;

	// Pre-fader BPF resistors and capacitors.
	const double R255;
	const double C213;
	const double R256;
	const double R233;
	const double C216;
	const double R237;
	const double C220;
	const double R242;
	const double C221;
	const double C222;
	const double R246;
	const double C226;
	const double C230;
	const double R249;
	const double C231;
};

constexpr const component_config TR707_COMPONENTS =
{
	.xck2_input = 3,

	.R95  = RES_M(4.7),
	.R102 = RES_M(2.2),
	.R82  = RES_M(2.2),
	.R91  = RES_M(4.7),
	.R73  = RES_M(2.2),

	.R203 = RES_K(22),
	.R205 = RES_K(33),
	.R206 = RES_K(33),
	.R207 = RES_K(47),
	.R208 = RES_K(22),
	.R211 = RES_K(33),
	.R214 = RES_K(47),

	.R255 = RES_K(10),
	.C213 = CAP_U(0.01),
	.R256 = RES_K(2.2),
	.R233 = RES_K(33),
	.C216 = CAP_P(470),
	.R237 = RES_K(47),
	.C220 = CAP_U(0.01),
	.R242 = RES_K(47),
	.C221 = CAP_U(0.01),
	.C222 = CAP_U(1),
	.R246 = RES_K(33),
	.C226 = CAP_U(0.01),
	.C230 = CAP_U(0.01),
	.R249 = RES_K(22),
	.C231 = CAP_U(0.01),
};

constexpr const component_config TR727_COMPONENTS =
{
	.xck2_input = 2,

	.R95  = RES_M(1),
	.R102 = RES_M(1),  // Not sure. Barely legible.
	.R82  = RES_M(4.7),
	.R91  = RES_M(2.2),
	.R73  = RES_M(4.7),

	.R203 = RES_K(47),
	.R205 = RES_K(47),
	.R206 = RES_K(22),
	.R207 = RES_K(22),
	.R208 = RES_K(47),
	.R211 = RES_K(22),  // Not sure. Barely legible.
	.R214 = RES_K(33),

	.R255 = RES_K(22),
	.C213 = CAP_U(0.0047),
	.R256 = RES_K(47),
	.R233 = RES_K(47),
	.C216 = CAP_U(0.0047),
	.R237 = RES_K(22),
	.C220 = CAP_U(0.012),
	.R242 = RES_K(33),
	.C221 = CAP_U(0.012),
	.C222 = CAP_U(10),
	.R246 = RES_K(47),
	.C226 = CAP_U(0.0022),
	.C230 = CAP_U(0.01),
	.R249 = RES_K(10),
	.C231 = CAP_U(0.0022),
};


class tr707_audio_device : public device_t
{
public:
	tr707_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, const component_config &components) ATTR_COLD;
	tr707_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) ATTR_COLD;

	void accent_level_w(u8 data);
	void voice_select_w(u8 data);
	void voice_trigger_w(u16 data);

	DECLARE_INPUT_CHANGED_MEMBER(mix_adjusted);
	DECLARE_INPUT_CHANGED_MEMBER(master_volume_adjusted);

protected:
	virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
	virtual void device_start() override ATTR_COLD;
	virtual void device_reset() override ATTR_COLD;

private:
	static inline constexpr double VCC = 5;  // Volts.
	static inline constexpr double VBE = 0.6;  // BJT base-emitter voltage drop.
	static inline constexpr u16 MAX_CYMBAL_COUNTER = 0x8000;

	static double mux_dac_v(double v_eg, u8 data);

	void advance_sample_w(offs_t offset, u16 data);

	void update_hat_eg();
	void update_master_volume();
	void update_mix(int channel);


	const component_config m_comps;
	const std::array<double, MV_COUNT> m_mux_eg_r;
	const std::array<double, CV_COUNT> m_cymbal_eg_r;

	required_memory_region m_mux_samples;  // IC34, IC35
	required_device<mb63h114_device> m_mac;  // IC30
	required_device_array<dac08_device, MV_COUNT> m_mux_dac;  // uPC624C (IC37)
	required_device_array<va_rc_eg_device, MV_COUNT> m_mux_eg;
	required_device_array<va_vca_device, MV_COUNT> m_mux_vca;
	required_device<va_rc_eg_device> m_hat_eg;

	required_memory_region_array<CV_COUNT> m_cymbal_samples;  // IC19, IC22
	required_device_array<dac_6bit_r2r_device, CV_COUNT> m_cymbal_dac;  // RA3, RA4 (RKM7LW502)
	required_device_array<filter_rc_device, CV_COUNT> m_cymbal_hpf;
	required_device_array<va_rc_eg_device, CV_COUNT> m_cymbal_eg;
	required_device_array<va_vca_device, CV_COUNT> m_cymbal_vca;  // Q14, Q15 (2SD1469R)

	required_ioport_array<MC_COUNT> m_level_sliders;
	required_device_array<filter_volume_device, MC_COUNT> m_level;
	// BPFs before the voice volume faders.
	required_device_array<filter_biquad_device, MC_COUNT> m_voice_bpf;
	// LPFs after the voice volume faders.
	required_device_array<filter_rc_device, MC_COUNT> m_voice_lpf;

	required_device<filter_volume_device> m_left_level;  // IC206a opamp + VR212a potentiometer.
	required_device<filter_volume_device> m_right_level;  // IC206b opamp + VR212b potentiometer.
	required_ioport m_master_volume;  // VR212

	u16 m_triggers = 0x3ff;
	double m_accent_level = 0;
	u8 m_bass_variation = 0;
	u8 m_snare_variation = 0;
	u8 m_rimshot_cowbell = 0;  // 0: rimshot, 1: cowbell.
	u8 m_handclap_tambourine = 0;  // 0: handclap, 1: tambourine.
	bool m_hat_is_closed = false;
	bool m_hat_triggering = false;
	u8 m_mux_sample = 0;
	u8 m_mux_voice = 0;
	u8 m_mac_c = 0;  // Last value of IC30, output C (pin 7).
	std::array<u16, CV_COUNT> m_cymbal_counter = {MAX_CYMBAL_COUNTER, MAX_CYMBAL_COUNTER};  // TC404 (IC18, IC23), TC4520 (IC20a, IC20b).
};

}  // anonymous namespace


DEFINE_DEVICE_TYPE(TR707_AUDIO, tr707_audio_device, "tr707_audio", "TR-707 audio circuits");

tr707_audio_device::tr707_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, const component_config &components)
	: device_t(mconfig, TR707_AUDIO, tag, owner, 0)
	, m_comps(components)
	, m_mux_eg_r
	{
		m_comps.R95,
		m_comps.R102,
		RES_M(4.7),  // R92
		RES_M(4.7),  // R93
		RES_M(4.7),  // R85
		RES_M(4.7),  // R104
		m_comps.R82,
		m_comps.R91,
	}
	, m_cymbal_eg_r
	{
		// R58 is 47K in the schematic, but that causes a very quick decay.
		// Using 470K, which matches R61 used in the ride cymbal voice below.
		RES_K(470),  // R58
		RES_2_PARALLEL(/*R61*/RES_K(470), m_comps.R73),
	}
	, m_mux_samples(*this, ":voices")
	, m_mac(*this, "mac")
	, m_mux_dac(*this, "mux_dac_%u", 1)
	, m_mux_eg(*this, "mux_eg_%u", 1)
	, m_mux_vca(*this, "mux_vca_%u", 1)
	, m_hat_eg(*this, "hat_eg")
	, m_cymbal_samples(*this, ":cymbal%u", 1)
	, m_cymbal_dac(*this, "cymbal_dac_%u", 1)
	, m_cymbal_hpf(*this, "cymbal_hpf_%u", 1)
	, m_cymbal_eg(*this, "cymbal_eg_%u", 1)
	, m_cymbal_vca(*this, "cymbal_vca_%u", 1)
	, m_level_sliders(*this, ":SLIDER_%u", 2)  // Level sliders start at 2.
	, m_level(*this, "level_%u", 1)
	, m_voice_bpf(*this, "voice_bpf_%u", 1)
	, m_voice_lpf(*this, "voice_lpf_%u", 1)
	, m_left_level(*this, "left_master_level")
	, m_right_level(*this, "right_master_level")
	, m_master_volume(*this, ":VOLUME")
{
}

tr707_audio_device::tr707_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: tr707_audio_device(mconfig, tag, owner, TR707_COMPONENTS)
{
}

void tr707_audio_device::accent_level_w(u8 data)
{
	// D0-D5 are converted to a voltage by a 6-bit resistor array DAC (RA5,
	// RKM7LW502), and buferred by emitter follower Q19, R77, R78. Could not
	// find any information about that DAC. Assuming it is linear.
	m_accent_level = VCC * (data & 0x3f) / double(0x3f) - VBE;
	LOGMASKED(LOG_TRIGGER, "Accent level: %02x - %f\n", data, m_accent_level);
}

void tr707_audio_device::voice_select_w(u8 data)
{
	m_bass_variation = BIT(data, 0);
	m_snare_variation = BIT(data, 1);
	m_rimshot_cowbell = BIT(data, 6);
	m_handclap_tambourine = BIT(data, 7);

	m_hat_is_closed = BIT(data, 5);
	update_hat_eg();

	LOGMASKED(LOG_TRIGGER, "Voice selected: %02x. Bass: %d, snare: %d, rim/cow: %d, hcp/tamb: %d, hat closed: %d\n",
			  data, m_bass_variation, m_snare_variation, m_rimshot_cowbell, m_handclap_tambourine, m_hat_is_closed);
}

void tr707_audio_device::voice_trigger_w(u16 data)
{
	constexpr double R79 = RES_R(100);

	if ((data & 0x03ff) == m_triggers)
		return;

	const u16 old_triggers = m_triggers;
	m_triggers = data & 0x03ff;

	// Reset sample ROM address counters for triggered MUX voices.
	m_mac->xst_w(m_triggers & 0xff);

	// The EG capacitors for all triggered voices will charge through a
	// single 100 Ohm resistor (R79). If more than one voice is triggered,
	// there will be multiple capacitors charging in parallel. Treat that
	// parallel capacitance as the effective capacitance of each triggered EG.
	double effective_charge_c = 0;
	for (int i = 0; i < MV_COUNT; ++i)
		if (!BIT(m_triggers, i))
			effective_charge_c += MUX_EG_C[i];
	for (int i = 0; i < CV_COUNT; ++i)
		if (!BIT(m_triggers, 8 + i))
			effective_charge_c += CYMBAL_EG_C[i];

	// Trigger amplitude EGs for MUX voices.
	for (int i = 0; i < MV_COUNT; ++i)
	{
		va_rc_eg_device *eg = m_mux_eg[i];
		if (!BIT(m_triggers, i))  // EG attack
		{
			// When the trigger is active, the EG capacitor will be connected
			// to both: the discharge resistor to ground (m_mux_eg_r[i]) and the
			// resistor to the accent voltage (R79) via a transistor. This setup
			// affects the effective resistance and target voltage of the RC
			// circuit as per the equations below. The effect of the transistor
			// in the charge path is not modelled.
			eg->set_r(RES_2_PARALLEL(R79, m_mux_eg_r[i]));
			eg->set_c(effective_charge_c);
			eg->set_target_v(m_accent_level * RES_VOLTAGE_DIVIDER(R79, m_mux_eg_r[i]));
		}
		else  // EG release
		{
			eg->set_r(m_mux_eg_r[i]);
			eg->set_c(MUX_EG_C[i]);
			eg->set_target_v(0);
		}
	}

	// In addition to the EG for the DAC reference current (handled above), the
	// hi-hat voice has an additional EG and VCA that postprocesses the DAC
	// output. This is used to create the open and closed hat variations.
	m_hat_triggering = !BIT(m_triggers, MV_HI_HAT);  // Active low.
	update_hat_eg();

	// Trigger amplitude EGs for cymbal voices.
	for (int i = 0; i < CV_COUNT; ++i)
	{
		va_rc_eg_device *eg = m_cymbal_eg[i];
		const int trigger_index = 8 + i;

		if (!BIT(m_triggers, trigger_index))  // EG attack
		{
			// See comments for MUX voice triggering above.
			eg->set_r(RES_2_PARALLEL(R79, m_cymbal_eg_r[i]));
			eg->set_c(effective_charge_c);
			eg->set_target_v(m_accent_level * RES_VOLTAGE_DIVIDER(R79, m_cymbal_eg_r[i]));
		}
		else  // EG release
		{
			eg->set_r(m_cymbal_eg_r[i]);
			eg->set_c(CYMBAL_EG_C[i]);
			eg->set_target_v(0);
		}

		// The trigger signal is connected to the ROM address counter's reset
		// inputs (active high) via a 100 pF capacitor. This setup will cause a
		// reset on the positive edge of the trigger.
		if (!BIT(old_triggers, trigger_index) && BIT(m_triggers, trigger_index))
			m_cymbal_counter[i] = 0;
	}

	if (m_triggers != 0x3ff)
		LOGMASKED(LOG_TRIGGER, "Trigger: %03x\n", m_triggers);
}

DECLARE_INPUT_CHANGED_MEMBER(tr707_audio_device::mix_adjusted)
{
	update_mix(param);
}

DECLARE_INPUT_CHANGED_MEMBER(tr707_audio_device::master_volume_adjusted)
{
	update_master_volume();
}

void tr707_audio_device::device_add_mconfig(machine_config &config)
{
	// *** Mulitpex sound section ***

	// Encompasses all voices other than crash and ride cymbal. The 8 voices
	// time-multiplex a single 8-bit DAC. A MUX (IC40, 4051) selects the
	// amplitude envelope generator to act as the DAC reference, and a DMUX
	// (IC41, 4051) routes the output of the DAC to the appropriate sample &
	// hold circuit. The MB63H114 generates sample ROM addresses for the 8
	// voices, and the addressing and timing signals for the MUX, DMUX and DAC
	// input.

	MB63H114(config, m_mac, 1.6_MHz_XTAL);
	m_mac->counter_cb().set(FUNC(tr707_audio_device::advance_sample_w));

	// Larger DAC data values result in more negative voltages. So the maximum
	// voltage is produced when data = 0, and the minimum one when data = 0xff.
	constexpr double MAX_MUX_EG_V = VCC;
	const double mux_dac_vpp = mux_dac_v(MAX_MUX_EG_V, 0) - mux_dac_v(MAX_MUX_EG_V, 0xff);
	const double mux_dac_scale = -(mux_dac_vpp / 2.0) / MAX_MUX_EG_V;

	// Time multiplexing is not emulated at the moment. Using 8 "virtual" DACs
	// and corresponding VCAs instead. The DAC circuit is somewhat elaborate.
	// See mux_dac_v() for its interpretation.
	for (int i = 0; i < MV_COUNT; ++i)
	{
		VA_RC_EG(config, m_mux_eg[i]).set_r(m_mux_eg_r[i]).set_c(MUX_EG_C[i]);
		DAC08(config, m_mux_dac[i]);
		VA_VCA(config, m_mux_vca[i]);
		m_mux_dac[i]->add_route(0, m_mux_vca[i], 1.0, 0);
		m_mux_eg[i]->add_route(0, m_mux_vca[i], mux_dac_scale, 1);
	}


	// *** Hi-hat VCA section ***

	// The hi-hat is one of multiplex voices, but it is post-processed by a
	// high-pass filter (HPF) and a single-transistor VCA with its own envelope
	// generator (EG). The EG has a slow and fast decay mode, which are used for
	// the open and closed hi-hat sounds, respectively. For more info on the hat
	// EG, see update_hat_eg().

	constexpr double HAT_HPF_SCALE = RES_VOLTAGE_DIVIDER(RES_K(10), RES_R(220));  // R125, R121
	constexpr double HAT_VCA_V2I_SCALE = 0.0084;  // Converts from input voltage to output current.
	constexpr double HAT_VCA_SCALE = -HAT_VCA_V2I_SCALE * RES_K(4.7);  // R120, inverting op-amp.

	auto &hat_hpf = FILTER_RC(config, "hat_hpf");
	hat_hpf.set_rc(filter_rc_device::HIGHPASS, RES_R(220), 0, 0, CAP_U(1));  // ~723 Hz, R121, C69
	m_mux_vca[MV_HI_HAT]->add_route(0, hat_hpf, HAT_HPF_SCALE, 0);

	VA_RC_EG(config, m_hat_eg).set_c(CAP_U(1));  // C71
	auto &hat_vca = VA_VCA(config, "hat_vca");  // 2SD1469R, Q32
	hat_hpf.add_route(0, hat_vca, HAT_VCA_SCALE, 0);
	m_hat_eg->add_route(0, hat_vca, 1.0 / VCC, 1);


	// *** Single sound section ***

	// Two voices, each with their own ROM, address counter, 6-bit DAC, HPF,
	// single-transistor VCA, and amplitude EG.

	constexpr double CYMBAL_HPF_SCALE = RES_VOLTAGE_DIVIDER(RES_K(22), RES_R(470));  // [R59, R60], [R63, R64]
	constexpr double CYMBAL_VCA_V2I_SCALE = 0.0043;  // Converts from input voltage to output current.
	constexpr double CYMBAL_VCA_SCALE = -CYMBAL_VCA_V2I_SCALE * RES_K(10);  // [R62, R65], inverting op-amp.

	for (int i = 0; i < CV_COUNT; ++i)
	{
		FILTER_RC(config, m_cymbal_hpf[i]);
		m_cymbal_hpf[i]->set_rc(filter_rc_device::HIGHPASS, RES_R(470), 0, 0, CAP_U(1));  // ~339 Hz, [R63, R64], [C35, C34]

		DAC_6BIT_R2R(config, m_cymbal_dac[i]).set_output_range(0, VCC);
		m_cymbal_dac[i]->add_route(0, m_cymbal_hpf[i], CYMBAL_HPF_SCALE);

		VA_RC_EG(config, m_cymbal_eg[i]).set_r(m_cymbal_eg_r[i]).set_c(CYMBAL_EG_C[i]);
		VA_VCA(config, m_cymbal_vca[i]);  // 2SD1469R [Q14, Q15]
		m_cymbal_hpf[i]->add_route(0, m_cymbal_vca[i], CYMBAL_VCA_SCALE, 0);
		m_cymbal_eg[i]->add_route(0, m_cymbal_vca[i], 1.0 / VCC, 1);
	}


	/*** Mixer section ***/

	// Each voice is processed by a band-pass filter (BPF) with a custom tuning
	// per voice, an inverting op-amp with the volume slider as the feedback
	// resistor, and a ~15.9 KHz RC low-pass filter (LPF). Following the LPF,
	// each voice is mixed by dedicated resistors into the left and right
	// channels, for a fixed pan setting per voice.
	// Each voice has its own output, which also follows the LPF. If the voice
	// output is connected, the voice won't be mixed into the left and right
	// channels.

	constexpr double R_MAX_MASTER_VOLUME = RES_K(50);  // VR212a, VR212b
	constexpr double R_MAX_CHANNEL_VOLUME = RES_K(50);  // VR202-VR211

	const std::array<double, MC_COUNT> r_mix_left =
	{
		RES_K(22),  // R202
		m_comps.R205,
		m_comps.R208,
		m_comps.R211,
		m_comps.R214,
		RES_K(33),  // R217
		RES_K(33),  // R221
		RES_K(47),  // R224
		RES_K(33),  // R227
		RES_K(22),  // R229
	};

	const std::array<double, MC_COUNT> r_mix_right =
	{
		m_comps.R203,
		m_comps.R206,
		m_comps.R207,
		RES_K(33),  // R212
		RES_K(22),  // R215
		RES_K(33),  // R218
		RES_K(33),  // R220
		RES_K(22),  // R223
		RES_K(22),  // R226
		RES_K(47),  // R230
	};

	// Band-pass filter (BPF) components for each voice.
	// The BPFs for the bass, snare and toms have a parallel path to R1, with
	// a separate resistor and diodes in both directions. This is probably meant
	// to change the filter's response depending on the signal's amplitude.
	// This change in response is not emulated at the moment. The filter is
	// modeled for a high amplitude signal.
	// The rest of the BPFs are typical RC-based BPFs.
	const std::array<std::array<double, 4>, MC_COUNT> bpf_comps =
	{{
		// {R1, R2, C1, C2}

		{RES_2_PARALLEL(m_comps.R255,       /*R231*/RES_K(22)), /*R234*/RES_K(33), m_comps.C213,        /*C214*/CAP_U(1)},
		{RES_2_PARALLEL(m_comps.R256,       /*R232*/RES_K(22)), m_comps.R233,      m_comps.C216,        /*C215*/CAP_U(1)},
		{RES_2_PARALLEL(/*R257*/RES_K(4.7), /*R235*/RES_K(22)), /*R238*/RES_K(47), /*C217*/CAP_U(0.01), /*C218*/CAP_U(1)},
		{RES_2_PARALLEL(/*R258*/RES_K(4.7), /*R236*/RES_K(22)), m_comps.R237,      m_comps.C220,        /*c219*/CAP_U(1)},
		{RES_2_PARALLEL(/*R259*/RES_K(4.7), /*R239*/RES_K(22)), m_comps.R242,      m_comps.C221,        m_comps.C222},

		{/*R240*/RES_K(22), /*R241*/RES_K(47), /*C224*/CAP_P(470), /*C223*/CAP_U(0.01)},
		{/*R243*/RES_K(22), m_comps.R246,      /*C225*/CAP_P(470), m_comps.C226},
		{/*R224*/RES_K(22), /*R245*/RES_K(22), /*C228*/CAP_P(470), /*C227*/CAP_U(0.01)},
		{/*R247*/RES_K(10), /*R250*/RES_K(22), /*C229*/CAP_P(470), m_comps.C230},
		{/*R248*/RES_K(10), m_comps.R249,      /*C232*/CAP_P(470), m_comps.C231},
	}};

	const std::array<device_sound_interface *, MC_COUNT> voices =
	{
		m_mux_vca[MV_BASS],
		m_mux_vca[MV_SNARE],
		m_mux_vca[MV_LOW_TOM],
		m_mux_vca[MV_MID_TOM],
		m_mux_vca[MV_HI_TOM],
		m_mux_vca[MV_RIMSHOT],
		m_mux_vca[MV_HANDCLAP],
		&hat_vca,
		m_cymbal_vca[CV_CRASH],
		m_cymbal_vca[CV_RIDE],
	};

	auto &left_mixer = MIXER(config, "left_mixer");
	auto &right_mixer = MIXER(config, "right_mixer");
	for (int i = 0; i < MC_COUNT; ++i)
	{
		FILTER_BIQUAD(config, m_voice_bpf[i]);
		m_voice_bpf[i]->rc_cc_bandpass_setup(bpf_comps[i][0], bpf_comps[i][1], bpf_comps[i][2], bpf_comps[i][3]);
		voices[i]->add_route(0, m_voice_bpf[i], 1.0);

		FILTER_VOLUME(config, m_level[i]);
		m_voice_bpf[i]->add_route(0, m_level[i], -R_MAX_CHANNEL_VOLUME / bpf_comps[i][1]);  // Inverting op-amp.

		FILTER_RC(config, m_voice_lpf[i]).set_lowpass(RES_K(1), CAP_U(0.01));  // ~15.9 KHz.
		m_level[i]->add_route(0, m_voice_lpf[i], 1.0);

		m_voice_lpf[i]->add_route(0, left_mixer, -R_MAX_MASTER_VOLUME / r_mix_left[i]);  // Inverting op-amp.
		m_voice_lpf[i]->add_route(0, right_mixer, -R_MAX_MASTER_VOLUME / r_mix_right[i]);  // Same.
	}

	// Model DC-blocking capacitors (C211, C212) on the opamp (IC206) inverting
	// inputs. Those do not have corresponding resistors.
	const filter_biquad_device::biquad_params dcblock
	{
		// HIGHPASS1P is a stable highpass filter with inaccurate frequency
		// response. Good for DC-blocking (very low cutoff frequency), where
		// frequency response does not matter.
		.type = filter_biquad_device::biquad_type::HIGHPASS1P,
		.fc = 0.05,
		.q = 0,  // N/A
		.gain = 1,
	};
	auto &left_dcblock = FILTER_BIQUAD(config, "left_dcblock").setup(dcblock);  // C211
	auto &right_dcblock = FILTER_BIQUAD(config, "right_dcblock").setup(dcblock);  // C212
	left_mixer.add_route(0, left_dcblock, 1.0);
	right_mixer.add_route(0, right_dcblock, 1.0);

	FILTER_VOLUME(config, m_left_level);  // IC206a opamp + VR212a potentiometer.
	FILTER_VOLUME(config, m_right_level);  // IC206b opamp + VR212b potentiometer.
	left_dcblock.add_route(0, m_left_level, 1.0);
	right_dcblock.add_route(0, m_right_level, 1.0);


	/*** Output section ***/

	// The outputs of the left and right summing op-amps are processed by BPFs
	// with a flat response, and -3dB points at ~0.35 Hz and ~12.4 KHz, before
	// making it to the left and right output sockets. Note that the exact
	// frequency response of these BPFs will be affected by the input impedance
	// of the connected device (not emulated).
	auto &left_bpf = FILTER_BIQUAD(config, "left_out_bpf");
	auto &right_bpf = FILTER_BIQUAD(config, "right_out_bpf");
	left_bpf.rc_rr_bandpass_setup(RES_K(1), RES_K(47), CAP_U(10), CAP_U(0.01));  // R114, R112, C79, C76
	right_bpf.rc_rr_bandpass_setup(RES_K(1), RES_K(47), CAP_U(10), CAP_U(0.01));  // R113, R111, C80, C77
	m_left_level->add_route(0, left_bpf, 1.0);
	m_right_level->add_route(0, right_bpf, 1.0);

	constexpr double VOLTAGE_TO_AUDIO_SCALE = 0.2;
	SPEAKER(config, "speaker", 2).front();
	left_bpf.add_route(0, "speaker", VOLTAGE_TO_AUDIO_SCALE, 0);
	right_bpf.add_route(0, "speaker", VOLTAGE_TO_AUDIO_SCALE, 1);
}

void tr707_audio_device::device_start()
{
	save_item(NAME(m_triggers));
	save_item(NAME(m_accent_level));
	save_item(NAME(m_bass_variation));
	save_item(NAME(m_snare_variation));
	save_item(NAME(m_rimshot_cowbell));
	save_item(NAME(m_handclap_tambourine));
	save_item(NAME(m_hat_is_closed));
	save_item(NAME(m_hat_triggering));
	save_item(NAME(m_mux_sample));
	save_item(NAME(m_mux_voice));
	save_item(NAME(m_mac_c));
	save_item(NAME(m_cymbal_counter));
}

void tr707_audio_device::device_reset()
{
	update_hat_eg();
	update_master_volume();
	for (int channel = 0; channel < MC_COUNT; ++channel)
		update_mix(channel);
}

// Computes the output voltage of the mux DAC circuit, given the voltage at the
// output of the envelope generator and the DAC data value.
double tr707_audio_device::mux_dac_v(double v_eg, u8 data)
{
	// These equations use a simplified model of BJTs: constant Vbe, no current
	// flowing into the base. These simplifications do not make e meaningful
	// difference in this particular case.
	constexpr double R156 = RES_K(12);
	constexpr double R153 = RES_K(2.2);
	constexpr double R147 = RES_K(2.2);
	constexpr double R148 = RES_K(2.2);

	// Compute reference current into the DAC.
	const double v_in = v_eg - VBE;  // VBE of Q44.
	const double i_ref = VCC / R156 + v_in / R153;

	// Compute output current, using the formula in the DAC08 datasheet. The
	// formula specifies "/ 256", rather than the more intuitive "/ 255".
	const double i_out = double(data) / 256.0 * i_ref;

	// Compute voltage at the DAC output. There is a connection from v_in to the
	// DAC's i_out, via 2 resistors.
	const double v_dac_out = v_in - (R147 + R148) * i_out;

	// Compute the final output of the circuit.
	return v_dac_out - /*Q43*/VBE - /*Q41*/VBE;
}

void tr707_audio_device::advance_sample_w(offs_t offset, u16 data)
{
	// Playback of multiplex voice samples lags one step behind the sample
	// selection by the MAC. The DAC reference MUX (IC40) and sound output MUX
	// (IC41) CBA inputs are configured to accommodate that. For example, the
	// DAC reference for voice 0 (bass drum) is selected when IC40 CBA = 1.

	// Latch the sample addressed in the previous step, into the multiplex
	// voice DAC.
	m_mux_dac[m_mux_voice]->data_w(m_mux_sample);

	// Update MB63H114 clock inputs.
	const u8 b = BIT(offset, 1);
	const u8 c = BIT(offset, 2);
	const u8 d = BIT(offset, 3);
	const u8 xck2 = BIT(offset, m_comps.xck2_input);
	m_mac->xck_w((b << 7) | (b << 6) | (c << 5) | (d << 4) | (d << 3) | (xck2 << 2) | (b << 1) | (b << 0));

	// Compute next multiplex voice sample. This will be latched into the DAC
	// in the next advance_sample_w invocation.
	u16 counter = 0;
	m_mux_voice = offset & 0x07;
	switch (m_mux_voice)
	{
		case 0: counter = (data & 0x1ffe) | m_bass_variation; break;
		case 1: counter = (data & 0x1ffe) | m_snare_variation; break;
		case 6: counter = (data & 0x1ffe) | m_rimshot_cowbell; break;
		case 7: counter = (data & 0x1ffe) | m_handclap_tambourine; break;
		default: counter = data; break;
	}
	m_mux_sample = m_mux_samples->as_u8((m_mux_voice << 13) | counter);

	// Update single sound (cymbal) DACs.
	for (int i = 0; i < CV_COUNT; ++i)
	{
		// Once bit 15 is set, the ROM address counter stops incrementing, and
		// no new data is latched to the DAC.
		if (m_cymbal_counter[i] >= MAX_CYMBAL_COUNTER)
			continue;

		// Cymbal timing is actually controlled by output B, which is divided by
		// 2 by a flipflop. But C is also B divided by 2, so using C here for
		// convenience.
		if (m_mac_c && !c)  // Negative edge increments ROM address.
		{
			++m_cymbal_counter[i];
		}
		else if (!m_mac_c && c)  // Positive edge latches ROM contents to DAC.
		{
			const u8 cymbal_sample = m_cymbal_samples[i]->as_u8(m_cymbal_counter[i] & 0x7fff);
			m_cymbal_dac[i]->data_w(cymbal_sample >> 2);  // Bits D2-D7.
		}
	}
	m_mac_c = c;
}

void tr707_audio_device::update_hat_eg()
{
	// Departures from the hat EG schematic:
	//
	// 1) The schematic shows Q20 connected to the accent level. But this looks
	// wrong. It would keep the EG constantly triggered. This emulation assumes
	// it is connected to the hat trigger signal.
	//
	// 2) The schematic shows two capacitors (C70 and C71) separated by a
	// resistor (R127). This setup would result in a slower attack, the EG would
	// only reach a fraction of its max value, and the hat volume would be very
	// low. Furthermore, C70 isn't really functional in this setup.
	// This emulation ignores C71, which leaves C70 as the only capacitor, and
	// makes the EG behave similar to the other ones in this drum machine.
	//
	// 3) Diode D12 (engaged for the closed hat sound) will probably slow down
	// the EG decay as it reaches lower levels. This effect is not emulated.

	double r_discharge = RES_2_PARALLEL(RES_K(220), RES_M(1));  // R124, R126
	if (m_hat_is_closed)
		r_discharge = RES_2_PARALLEL(r_discharge, RES_K(10));  // R123
	r_discharge += RES_K(4.7);  // R127

	if (m_hat_triggering)
	{
		const double r_charge = RES_R(100);  // R128
		m_hat_eg->set_r(RES_2_PARALLEL(r_charge, r_discharge));
		m_hat_eg->set_target_v(VCC * RES_VOLTAGE_DIVIDER(r_charge, r_discharge));
	}
	else
	{
		m_hat_eg->set_r(r_discharge);
		m_hat_eg->set_target_v(0);
	}

}

void tr707_audio_device::update_master_volume()
{
	const double gain = m_master_volume->read() / 100.0;
	m_left_level->set_gain(gain);
	m_right_level->set_gain(gain);
	LOGMASKED(LOG_MIX, "Master volume adjusted %d %f\n", m_master_volume->read(), gain);
}

void tr707_audio_device::update_mix(int channel)
{
	assert(channel >= 0 && channel < MC_COUNT);
	const double gain = m_level_sliders[channel]->read() / 100.0;
	m_level[channel]->set_gain(gain);
	LOGMASKED(LOG_MIX, "Mix slider adjusted %d: %f\n", channel, gain);
}


namespace {

constexpr const char AUDIO_TAG[] = "tr707_audio";

class roland_tr707_state : public driver_device
{
public:
	static constexpr feature_type unemulated_features() { return feature::TAPE; }

	roland_tr707_state(const machine_config &mconfig, device_type type, const char *tag) ATTR_COLD;

	void tr707(machine_config &config);
	void tr727(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(sync_input_changed);
	DECLARE_INPUT_CHANGED_MEMBER(tempo_pots_adjusted);
	DECLARE_INPUT_CHANGED_MEMBER(accent_pots_adjusted);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	enum dinsync_index
	{
		DINSYNC_STARTSTOP = 0,
		DINSYNC_TEMPO,
		DINSYNC_CONTINUE
	};

	enum accent_adc_timer_param
	{
		ACCENT_FLIPFLOP_CLR_CLEAR = 0,
		ACCENT_FLIPFLOP_CLR_ASSERT,
	};

	struct seg_output  // Reference to an LCD segment output.
	{
		enum seg_type
		{
			NONE = 0,
			TRIANGLE,
			TRACK,
			TEXT,
			DOT,
			DIGIT,
		};

		seg_output() : seg_output(NONE, 0, 0) {}
		seg_output(enum seg_type _type, offs_t _x, offs_t _y = 0) : type(_type), x(_x), y(_y) {}

		enum seg_type type;
		offs_t x;
		offs_t y;
	};

	static double discharge_t(double r, double c, double v);

	u8 key_scan_r();
	void key_led_row_w(u8 data);
	void leds_w(u8 data);
	void led_outputs_w(offs_t offset, u8 data);
	u8 trigger_r(offs_t offset);
	void trigger_w(offs_t offset, u8 data);

	u8 lcd_reset_r();
	void lcd_seg_w(offs_t offset, u64 data);
	void lcd_seg_outputs_w(offs_t offset, u8 data);

	int cart_connected_r() const;
	u8 cart_r(offs_t offset);
	void cart_w(offs_t offset, u8 data);

	int midi_rxd_r() const;
	void midi_rxd_w(int state);

	template<enum dinsync_index Which> int dinsync_r() const;
	template<enum dinsync_index Which> void dinsync_w(int state);

	void tempo_source_w(u8 data);
	void update_tempo_line();
	void internal_tempo_clock_cb(int state);
	void update_internal_tempo_timer(bool cap_reset);
	TIMER_DEVICE_CALLBACK_MEMBER(tempo_timer_tick);
	TIMER_DEVICE_CALLBACK_MEMBER(tempo_restart_timer_tick);

	void update_accent_adc();
	void accent_adc_flipflop_cb(int state);
	TIMER_DEVICE_CALLBACK_MEMBER(accent_adc_timer_tick);

	void mem_map(address_map &map) ATTR_COLD;
	void tr_707_727_common(machine_config &config) ATTR_COLD;

	required_device<tr707_audio_device> m_audio;
	required_device<hd6303x_cpu_device> m_maincpu;
	required_device<generic_slot_device> m_cartslot;
	required_device<pwm_display_device> m_led_matrix;
	required_ioport_array<4> m_key_switches;
	output_finder<> m_cart_led;  // D325 (GL9NP2) dual LED (red & green).
	std::vector<std::vector<output_finder<>>> m_leds;

	required_device<hd61602_device> m_lcdc;
	required_device<pwm_display_device> m_lcd_pwm;
	output_finder<10> m_seg_triangle;
	output_finder<4> m_seg_track;
	output_finder<7> m_seg_text;
	output_finder<16, 10> m_seg_dot;
	output_finder<3> m_seg_digit;

	required_ioport m_tapesync_in;
	required_ioport m_dinsync_in;
	required_ioport m_dinsync_config;
	output_finder<3> m_dinsync_out;

	required_device<timer_device> m_tempo_timer;
	required_device<timer_device> m_tempo_restart_timer;
	required_device<ttl7474_device> m_tempo_ff;  // Actually a 4013, IC4a.
	required_ioport m_tempo_trimmer;  // TM-1, 200K(B).
	required_ioport m_tempo_knob;  // VR301, 1M(B).

	required_device<va_rc_eg_device> m_accent_adc_rc;
	required_device<timer_device> m_accent_adc_timer;
	required_device<ttl7474_device> m_accent_adc_ff;  // 4013, IC4b.
	required_ioport m_accent_trimmer_series;  // TM-2, 50K(B).
	required_ioport m_accent_trimmer_parallel;  // TM-3, 200K(B).
	required_ioport m_accent_level;  // VR201, 50K(B).

	output_finder<> m_layout_727;
	output_finder<> m_layout_cart;
	bool m_is_727;  // Configuration. Not needed in save state.
	std::vector<std::vector<seg_output>> m_seg_map;  // Configuration.

	u8 m_cart_bank;  // IC27 (40H174), Q5.
	u8 m_key_led_row;  // P60-P63.
	u8 m_tempo_source;  // P64-P66.
	bool m_midi_rxd_bit;

	static constexpr const double VCC = 5.0;  // Volts.
	// Typical negative- and positive-going thresholds for a 4584 Schmitt
	// trigger with a 5V supply.
	static constexpr const double NEG_THRESH_4584 = 2.1;
	static constexpr const double POS_THRESH_4584 = 2.7;
};

roland_tr707_state::roland_tr707_state(const machine_config &mconfig, device_type type, const char *tag)
	: driver_device(mconfig, type, tag)
	, m_audio(*this, AUDIO_TAG)
	, m_maincpu(*this, "maincpu")
	, m_cartslot(*this, "cartslot")
	, m_led_matrix(*this, "led_matrix")
	, m_key_switches(*this, "KEY%u", 0U)
	, m_cart_led(*this, "led_cart")
	, m_leds(4)
	, m_lcdc(*this, "lcdc")
	, m_lcd_pwm(*this, "lcd_pwm")
	, m_seg_triangle(*this, "seg_triangle_%u", 1U)
	, m_seg_track(*this, "seg_track_%u", 1U)
	, m_seg_text(*this, "seg_text_%u", 1U)
	, m_seg_dot(*this, "seg_dot_%u_%u", 1U, 1U)
	, m_seg_digit(*this, "seg_digit_%u", 1U)
	, m_tapesync_in(*this, "TAPESYNC")
	, m_dinsync_in(*this, "DINSYNC")
	, m_dinsync_config(*this, "DINSYNC_CONFIG")
	, m_dinsync_out(*this, "DINSYNC_OUT_%u", 0U)
	, m_tempo_timer(*this, "tempo_clock")
	, m_tempo_restart_timer(*this, "tempo_restart_timer")
	, m_tempo_ff(*this, "tempo_flipflop")
	, m_tempo_trimmer(*this, "TM1")
	, m_tempo_knob(*this, "TEMPO")
	, m_accent_adc_rc(*this, "accent_adc_rc_network")
	, m_accent_adc_timer(*this, "accent_adc_timer")
	, m_accent_adc_ff(*this, "accent_adc_flipflop")
	, m_accent_trimmer_series(*this, "TM2")
	, m_accent_trimmer_parallel(*this, "TM3")
	, m_accent_level(*this, "SLIDER_1")
	, m_layout_727(*this, "is727")
	, m_layout_cart(*this, "has_cartridge")
	, m_is_727(false)
	, m_seg_map(hd61602_device::NCOM, std::vector<seg_output>(hd61602_device::NSEG))  // 4x51 LCD segments.
	, m_cart_bank(0)
	, m_key_led_row(0xff)
	, m_tempo_source(0xff)
	, m_midi_rxd_bit(true)  // Initial value is high, for serial "idle".
{
	// Initalize LED outputs.

	constexpr const char *LED_NAME_SUFFIXES[4][6] =
	{
		{"1", "2", "3", "4", "scale_1", "scale_2"},
		{"5", "6", "7", "8", "scale_3", "scale_4"},
		{"9", "10", "11", "12", "group_1", "group_2"},
		{"13", "14", "15", "16", "group_3", "group_4"},
	};
	for (int i = 0; i < 4; ++i)
		for (int j = 0; j < 6; ++j)
			m_leds[i].push_back(output_finder<>(*this, std::string("led_") + LED_NAME_SUFFIXES[i][j]));


	// Build a mapping (m_seg_map) from the LCD controller's rows and columns (
	// "com" and "seg" in hd61602 parlance) to the corresponding outputs.

	for (int i = 0; i < 4; ++i)
		m_seg_map[3 - i][50] = seg_output(seg_output::TRACK, i);

	constexpr const std::tuple<int, int> SEG_TRIANGLES[10] =
	{
		{0, 48},  // cymbal
		{1, 48},  // hi hat
		{2, 48},  // hcp / tamb
		{3, 48},  // rim / cowbell
		{3, 47},  // hi tom
		{2, 47},  // mid tom
		{1, 47},  // low tom
		{0, 47},  // snare drum
		{0, 46},  // bass drum
		{1, 46},  // accent
	};
	for (int i = 0; i < 10; ++i)
	{
		const std::tuple<int, int> &seg = SEG_TRIANGLES[i];
		m_seg_map[std::get<0>(seg)][std::get<1>(seg)] = seg_output(seg_output::TRIANGLE, i);
	}

	constexpr const std::tuple<int, int> SEG_TEXT[7] =
	{
		{0, 44},  // tempo
		{0, 40},  // measure
		{3, 46},  // track play
		{2, 46},  // track write
		{3, 49},  // pattern play
		{2, 49},  // step write
		{1, 49},  // tap write
	};
	for (int i = 0; i < 7; ++i)
	{
		const std::tuple<int, int> &seg = SEG_TEXT[i];
		m_seg_map[std::get<0>(seg)][std::get<1>(seg)] = seg_output(seg_output::TEXT, i);
	}

	constexpr const std::tuple<int, int> SEG_DIGIT[3][7] =
	{
		{ {0, 45}, {1, 44}, {2, 44}, {3, 44}, {3, 45}, {1, 45}, {2, 45} },
		{ {0, 43}, {1, 42}, {2, 42}, {3, 42}, {3, 43}, {1, 43}, {2, 43} },
		{ {0, 41}, {1, 40}, {2, 40}, {3, 40}, {3, 41}, {1, 41}, {2, 41} },
	};
	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < 7; ++j)
		{
			const std::tuple<int, int> &seg = SEG_DIGIT[i][j];
			m_seg_map[std::get<0>(seg)][std::get<1>(seg)] = seg_output(seg_output::DIGIT, i, j);
		}
	}

	// Segment addresses (row.col aka com.seg) for the 10x16 matrix of dots.
	// "0.0 .. 3.0" means "0.0 1.0 2.0 3.0" and similar for the decreasing
	// cases susch as "3.1 .. 0.1".

	//  cymbal   0.0  .. 3.0  3.1  .. 0.1  0.20 .. 3.20 3.21 .. 0.21
	//  hat:     0.2  .. 3.2  3.3  .. 0.3  0.22 .. 3.22 3.23 .. 0.23
	//  hcp:     0.4  .. 3.4  3.5  .. 0.5  0.24 .. 3.24 3.25 .. 0.25
	//  rim:     0.6  .. 3.6  3.7  .. 0.7  0.26 .. 3.26 3.27 .. 0.27
	//  hi tom:  0.8  .. 3.8  3.9  .. 0.9  0.28 .. 3.28 3.29 .. 0.29
	//  mid tom: 0.10 .. 3.10 3.11 .. 0.11 0.30 .. 3.30 3.31 .. 0.31
	//  low tom: 0.12 .. 3.12 3.13 .. 0.13 0.32 .. 3.32 3.33 .. 0.33
	//  snare:   0.14 .. 3.14 3.15 .. 0.15 0.34 .. 3.34 3.35 .. 0.35
	//  bass:    0.16 .. 3.16 3.17 .. 0.17 0.36 .. 3.36 3.37 .. 0.37
	//  accent:  0.18 .. 3.18 3.19 .. 0.19 0.38 .. 3.38 3.39 .. 0.39

	// Map each row.col to an x.y output position, based on the table above.
	for (int i = 0; i < 4; ++i)
	{
		for (int j = 0; j < 10; ++j)
		{
			m_seg_map[i][2 * j] = seg_output(seg_output::DOT, i, j);
			m_seg_map[3 - i][2 * j + 1] = seg_output(seg_output::DOT, i + 4, j);
			m_seg_map[i][2 * j + 20] = seg_output(seg_output::DOT, i + 8, j);
			m_seg_map[3 - i][2 * j + 21] = seg_output(seg_output::DOT, i + 12, j);
		}
	}
}

void roland_tr707_state::machine_start()
{
	save_item(NAME(m_cart_bank));
	save_item(NAME(m_key_led_row));
	save_item(NAME(m_tempo_source));
	save_item(NAME(m_midi_rxd_bit));

	m_layout_727.resolve();
	m_layout_cart.resolve();
	m_dinsync_out.resolve();
	m_cart_led.resolve();
	for (std::vector<output_finder<>> &led_row : m_leds)
		for (output_finder<> &led_output : led_row)
			led_output.resolve();

	m_seg_triangle.resolve();
	m_seg_track.resolve();
	m_seg_text.resolve();
	m_seg_dot.resolve();
	m_seg_digit.resolve();
}

void roland_tr707_state::machine_reset()
{
	update_internal_tempo_timer(true);
	update_accent_adc();
	m_layout_727 = m_is_727;
	m_layout_cart = m_cartslot->exists() ? 1 : 0;
}


double roland_tr707_state::discharge_t(double r, double c, double v)
{
	// RC (dis)charge time to reach V:
	//   dt = -R * C * log( (Vend - V) / (Vend - Vstart) )
	// In this case, Vstart = 5V, Vend = 0V.
	return -r * c * log(v / VCC);
}

u8 roland_tr707_state::key_scan_r()
{
	bool row_active[4] = {false, false, false, false};
	u8 row_keys[4] = {0, 0, 0, 0};
	for (int n = 0; n < 4; n++)
	{
		row_active[n] = !BIT(m_key_led_row, n);
		row_keys[n] = m_key_switches[n]->read();
	}

	// In contrast to rows 1 and 2 (drum pads), rows 2 and 3 (other buttons)
	// lack a protection diode for each key. The wiring is such that, if one
	// of rows 2 or 3 is activated, and two keys in the same column are pressed,
	// then the other row will also get activated.
	if ((row_active[2] || row_active[3]) && (row_keys[2] & row_keys[3]))
	{
		row_active[2] = true;
		row_active[3] = true;
		LOGMASKED(LOG_KEYS, "Row 2/3 conflict: %02x, %02x\n", row_keys[2], row_keys[3]);
	}

	u8 data = 0x00;
	for (int n = 0; n < 4; n++)
		if (row_active[n])
			data |= row_keys[n];

	if (data)
		LOGMASKED(LOG_KEYS, "Keys pressed: %02x\n", data);
	return data;
}

void roland_tr707_state::key_led_row_w(u8 data)
{
	m_key_led_row = data;

	// key/led selection will enable positive supply to LED anodes (via
	// Q301-304) when low.
	m_led_matrix->write_my(~m_key_led_row & 0x0f);
}

void roland_tr707_state::leds_w(u8 data)
{
	// Data bits D0-D5 (IC301, 40H174) are inverted by IC302 (M54517 transistor
	// array) and connected to LED cathodes (low = on). So D0-D5 are inverted
	// twice.
	m_led_matrix->write_mx(data & 0x3f);
}

void roland_tr707_state::led_outputs_w(offs_t offset, u8 data)
{
	m_leds[offset & 0x3f][offset >> 6] = data;
}

u8 roland_tr707_state::trigger_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		trigger_w(offset, 0);
	return 0x00;  // Data bus pulled low.
}

void roland_tr707_state::trigger_w(offs_t offset, u8 data)
{
	m_cart_bank = BIT(offset, 10);
	m_cart_led = (offset >> 10) & 0x03; // Bit 11: green, bit 10: red.
	m_audio->voice_trigger_w(offset & 0x3ff);
}

u8 roland_tr707_state::lcd_reset_r()
{
	if (!machine().side_effects_disabled())
		m_lcdc->reset_counter_strobe();
	return 0x00;  // Data bus pulled low.
}

void roland_tr707_state::lcd_seg_w(offs_t offset, u64 data)
{
	m_lcd_pwm->matrix(1 << offset, data);
}

void roland_tr707_state::lcd_seg_outputs_w(offs_t offset, u8 data)
{
	const seg_output &seg = m_seg_map[offset & 0x3f][offset >> 6];
	switch (seg.type)
	{
		case seg_output::TRIANGLE: m_seg_triangle[seg.x] = data;   break;
		case seg_output::TRACK:    m_seg_track[seg.x] = data;      break;
		case seg_output::TEXT:     m_seg_text[seg.x] = data;       break;
		case seg_output::DOT:      m_seg_dot[seg.x][seg.y] = data; break;
		case seg_output::DIGIT:
		{
			const u8 bit = BIT(data, 0) << seg.y;
			const u8 mask = 0x7f ^ (1 << seg.y);
			m_seg_digit[seg.x] = (m_seg_digit[seg.x] & mask) | bit;
			break;
		}
		default: break;
	}
}

int roland_tr707_state::cart_connected_r() const
{
	// P54 is pulled up by R24 and connected to pin 1 ("sens") of the cartridge,
	// connection. When the cartridge is connected, P54 will be grounded.
	return m_cartslot->exists() ? 0 : 1;

	// That same signal is also connected to the /G1 and /G2 inputs of IC7 (
	// 40H367 buffer) that routes control signals to the cartridge.
	// 1A <- /RD
	// 2A <- /WR
	// 3A <- Cartslot select, from IC11:Y3 (memory address decoder).
	// 4A <- Cartridge LED, red (see roland_tr707_state::trigger_w()).
	// 5A <- A11
	// 6A <- A10
}

u8 roland_tr707_state::cart_r(offs_t offset)
{
	return m_cartslot->read_ram((m_cart_bank << 12) | offset);
}

void roland_tr707_state::cart_w(offs_t offset, u8 data)
{
	m_cartslot->write_ram((m_cart_bank << 12) | offset, data);
	LOGMASKED(LOG_CART, "Cart write: %d - %04x - %02x\n", m_cart_bank, offset, data);
}

int roland_tr707_state::midi_rxd_r() const
{
	return m_midi_rxd_bit ? 1 : 0;
}

void roland_tr707_state::midi_rxd_w(int state)
{
	m_midi_rxd_bit = bool(state);
}

template<enum roland_tr707_state::dinsync_index Which> int roland_tr707_state::dinsync_r() const
{
	// There is a single DIN-sync socket that can act as either an input or an
	// output. MCU outputs P27, P26 and P21 (start/stop, continue, tempo) are
	// inverted by Q9, Q11 and Q10 respectively (output stage). The output stage
	// is connected to the socket pins. Those pins are also connected to (and
	// inverted by) Q7, Q12 and Q9 (inpug stage). The wiring is such that the
	// input stage will either sense the DIN-sync pins if they are being driven,
	// or the output stage if not.

	if (BIT(m_dinsync_config->read(), 0))  // DIN-sync cable connected and serving as input.
		return BIT(m_dinsync_in->read(), Which) ? 0 : 1;
	else
		return m_dinsync_out[Which] ? 0 : 1;
}

template<enum roland_tr707_state::dinsync_index Which> void roland_tr707_state::dinsync_w(int state)
{
	// See comments in dinsync_r().
	const int new_value = state ? 0 : 1;
	if (new_value == m_dinsync_out[Which])
		return;
	m_dinsync_out[Which] = new_value;
	LOGMASKED(LOG_SYNC, "Set dinsync out %d: %d\n", Which, new_value);

	if (Which == DINSYNC_STARTSTOP && !state)  // Resets tempo on a "start".
	{
		// 1->0 transition on the input of IC3d. This resets the tempo timing
		// capacitor, does an immediate flipflop clear, and does a flipflop
		// preset ~9ms later. That timing matches the DIN-sync requirement to
		// start sending clock pulses 9ms after the start signal.
		m_tempo_timer->reset();
		m_tempo_ff->clear_w(0);  // See comment regarding polarity in:
		m_tempo_ff->clear_w(1);  // tempo_restart_timer_tick().

		const double dt = discharge_t(RES_M(1), CAP_U(0.01), NEG_THRESH_4584);  // R5, C11.
		m_tempo_restart_timer->adjust(attotime::from_double(dt));
		LOGMASKED(LOG_TEMPO, "Reset tempo - dt: %f\n", dt);
	}
}

void roland_tr707_state::tempo_source_w(u8 data)
{
	if (m_tempo_source == data)
		return;
	m_tempo_source = data;
	update_tempo_line();
	LOGMASKED(LOG_TEMPO, "Selected tempo source: %02x\n", data);
}

void roland_tr707_state::update_tempo_line()
{
	// A set of NAND gates (IC2) and a discrete AND circuit (D4, D5, R9)
	// determine which of the tempo clock sources makes it to P20 (TIN).
	const bool tempo = !(BIT(m_tempo_source, 0) && m_tempo_ff->output_comp_r());  // IC2b
	const bool din_sync = !(BIT(m_tempo_source, 1) && dinsync_r<DINSYNC_TEMPO>());  // IC2c
	const bool tape_sync = !(BIT(m_tempo_source, 2) && BIT(m_tapesync_in->read(), 0));  // IC2a
	const bool selected_clock = !(tempo && din_sync && tape_sync);  // IC2d w/ discrente AND.
	m_maincpu->set_input_line(M6801_TIN_LINE, selected_clock ? ASSERT_LINE : CLEAR_LINE);
}

DECLARE_INPUT_CHANGED_MEMBER(roland_tr707_state::sync_input_changed)
{
	update_tempo_line();
}

void roland_tr707_state::update_internal_tempo_timer(bool cap_reset)
{
	// The tempo clock circuit's timing capacitor (C10) starts at 5V and
	// discharges through the tempo trimmer, resistor R318, and tempo knob. When
	// it reaches a Schmitt trigger's (IC3b) negative-going threshold, it will
	// reset the timing capacitor to 5V (via Q3), and clock a flipflop (IC4a)
	// wired as a divide-by-two circuit. The inverted output of IC4a is the
	// tempo clock (24 cycles per quarter note). The firmware can also reset the
	// tempo clock.

	constexpr const double C10 = CAP_U(0.047);
	constexpr const double R318 = RES_K(47);
	constexpr const double TRIMMER_MAX = RES_K(200);  // TM-1.
	constexpr const double KNOB_MAX = RES_M(1);  // VR301.

	// Using 100.0 - x, so that larger values result in higher tempo.
	// The tempo potentiometer has a "C" (inverse audio) taper, wired such that
	// turning it clockwise reduces the resistance. Treating the input as a
	// position (a value increase corresponds to a resistance decrease),
	// results in an "A" (audio) taper wrt the position.
	const double knob_r = KNOB_MAX * RES_AUDIO_POT_LAW((100.0 - m_tempo_knob->read()) / 100.0);
	const double trimmer_r = TRIMMER_MAX * (100.0 - m_tempo_trimmer->read()) / 100.0;
	const double r = R318 + trimmer_r + knob_r;
	const double period = discharge_t(r, C10, NEG_THRESH_4584);

	// If not invoked after a capacitor reset, continue from the current
	// position in the cycle.
	double remaining = period;
	if (!cap_reset)
		remaining *= m_tempo_timer->remaining().as_double() / m_tempo_timer->period().as_double();

	m_tempo_timer->adjust(attotime::from_double(remaining), 0, attotime::from_double(period));
	LOGMASKED(LOG_TEMPO, "Update tempo timer. R: %f, T: %f, Rem: %f, F: %f, BPM: %f\n",
			  r, period, remaining, 1.0 / period, 60.0 / period / 24 / 2);
}

void roland_tr707_state::internal_tempo_clock_cb(int state)
{
	// Divide-by-two configuration: output /Q connected to input D.
	m_tempo_ff->d_w(state);
	// /Q connected to IC2b (4011), part of the tempo source select circuit.
	update_tempo_line();
}

TIMER_DEVICE_CALLBACK_MEMBER(roland_tr707_state::tempo_timer_tick)
{
	m_tempo_ff->clock_w(1);
	m_tempo_ff->clock_w(0);
}

TIMER_DEVICE_CALLBACK_MEMBER(roland_tr707_state::tempo_restart_timer_tick)
{
	// If comparing to the actual circuit, note the inverted writes. The
	// 4013 used in the circuit has active-high 'preset' and 'clear' inputs,
	// in contrast to the 7474 used here to emulate it.
	LOGMASKED(LOG_TEMPO, "Tempo reset timer invoked\n");
	m_tempo_ff->preset_w(0);
	m_tempo_ff->preset_w(1);
	update_internal_tempo_timer(true);
}

DECLARE_INPUT_CHANGED_MEMBER(roland_tr707_state::tempo_pots_adjusted)
{
	update_internal_tempo_timer(false);
}

void roland_tr707_state::update_accent_adc()
{
	// The position of the Accent slider is determined by an ADC circuit
	// consisting of a 4013 flipflop (IC4b), a 4584 Schmitt trigger inverter
	// (IC3e), and passive components. It works by measuring the time it takes
	// for a capacitor (C15) to discharge via the Accent slider, two trimmers
	// and a resistor. The firmware initiates the discharge. Once the voltage
	// reaches the negative-going threshold of IC3e, IRQ2 will be asserted and
	// the firmware will restart the charge-discharge cycle.

	constexpr const double ACCENT_MAX = RES_K(50);
	constexpr const double TM2_MAX = RES_K(50);
	constexpr const double TM3_MAX = RES_K(200);
	constexpr const double R159 = RES_K(1);

	const double accent = ACCENT_MAX * m_accent_level->read() / 100.0;
	const double tm2 = TM2_MAX * m_accent_trimmer_series->read() / 100.0;
	const double tm3 = TM3_MAX * m_accent_trimmer_parallel->read() / 100.0;

	const double parallel_r = (tm3 > 0 && accent > 0) ? RES_2_PARALLEL(tm3, accent) : 0;
	const double r = R159 + tm2 + parallel_r;
	m_accent_adc_rc->set_r(r);

	const bool charging = m_accent_adc_ff->output_comp_r();
	const double target_v = charging ? VCC : 0.0;
	m_accent_adc_rc->set_target_v(target_v);

	attotime dt = attotime::never;
	const double current_v = m_accent_adc_rc->get_v();
	if (charging && current_v < POS_THRESH_4584)
	{
		dt = m_accent_adc_rc->get_dt(POS_THRESH_4584);
		assert(dt != attotime::never);
		m_accent_adc_timer->adjust(dt, ACCENT_FLIPFLOP_CLR_CLEAR);
	}
	else if (!charging && current_v > NEG_THRESH_4584)
	{
		dt = m_accent_adc_rc->get_dt(NEG_THRESH_4584);
		assert(dt != attotime::never);
		m_accent_adc_timer->adjust(dt, ACCENT_FLIPFLOP_CLR_ASSERT);
	}
	else
	{
		m_accent_adc_timer->reset();
	}

	// flipflop Q connected to P51 (IRQ2).
	const enum line_state irq2 = m_accent_adc_ff->output_r() ? CLEAR_LINE : ASSERT_LINE;
	m_maincpu->set_input_line(HD6301_IRQ2_LINE, irq2);

	LOGMASKED(LOG_ACCENT, "Update accent ADC - R: %f, V: %f, dt: %f, irq2: %d\n",
			  r, target_v, dt.as_double(), irq2);
}

TIMER_DEVICE_CALLBACK_MEMBER(roland_tr707_state::accent_adc_timer_tick)
{
	// If comparing to the actual circuit, note the inverted 'clear' signal. The
	// 4013 used in the circuit has an active-high 'clear' input, in contrast to
	// the 7474 used here to emulate it.
	LOGMASKED(LOG_ACCENT, "Accent ADC timer elapsed: %d\n", param);
	switch (param)
	{
		case ACCENT_FLIPFLOP_CLR_ASSERT:
			m_accent_adc_ff->clear_w(0);  // Active low.
			break;
		case ACCENT_FLIPFLOP_CLR_CLEAR:
			m_accent_adc_ff->clear_w(1);
			break;
	}
}

void roland_tr707_state::accent_adc_flipflop_cb(int state)
{
	LOGMASKED(LOG_ACCENT, "Accent flipflop - charging: %d\n", state);
	update_accent_adc();
}

DECLARE_INPUT_CHANGED_MEMBER(roland_tr707_state::accent_pots_adjusted)
{
	LOGMASKED(LOG_ACCENT, "Accent slider or trimmers changed\n");
	update_accent_adc();
}

void roland_tr707_state::mem_map(address_map &map)
{
	// Address bus A0-A11 pulled high by RA1 and RA2.
	map.unmap_value_low();  // Data bus pulled low by RA301.
	map(0x0000, 0x0000).mirror(0x7ff).unmaprw();
	map(0x0800, 0x0800).mirror(0x7ff).r(FUNC(roland_tr707_state::key_scan_r));
	map(0x1000, 0x1000).mirror(0xfff).r(FUNC(roland_tr707_state::lcd_reset_r)).w(m_lcdc, FUNC(hd61602_device::data_w));
	map(0x2000, 0x27ff).ram().share("nvram1");
	map(0x2800, 0x2fff).ram().share("nvram2");
	map(0x3000, 0x3fff).rw(FUNC(roland_tr707_state::cart_r), FUNC(roland_tr707_state::cart_w));
	map(0x4000, 0x4000).mirror(0xfff).w(FUNC(roland_tr707_state::leds_w));
	map(0x5000, 0x5000).mirror(0xfff).w(m_audio, FUNC(tr707_audio_device::accent_level_w));
	map(0x6000, 0x6fff).rw(FUNC(roland_tr707_state::trigger_r), FUNC(roland_tr707_state::trigger_w));
	map(0x7000, 0x7000).mirror(0xfff).w(m_audio, FUNC(tr707_audio_device::voice_select_w));
	map(0x8000, 0xbfff).mirror(0x4000).rom().region("program", 0);
}


static INPUT_PORTS_START(tr707)
	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("8") PORT_CODE(KEYCODE_8)

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("9") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("10") PORT_CODE(KEYCODE_0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("11") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("12") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("13") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("14") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("15") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("16") PORT_CODE(KEYCODE_Y)

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Start") PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Stop/Cont") PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Pattern Clear") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Scale") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Last Step") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Inst Select") PORT_CODE(KEYCODE_F)

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tempo") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Track") PORT_CODE(KEYCODE_K)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Pattern") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Shuffle") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Group A") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Group B") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Group C") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Group D") PORT_CODE(KEYCODE_V)

	PORT_START("TEMPO")  // Tempo knob, VR301, 1M(C) (EWH-LNAF20C16, inverse audio taper).
	PORT_ADJUSTER(50, "Tempo") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(roland_tr707_state::tempo_pots_adjusted), 0)

	PORT_START("VOLUME")  // Master volume slider, VR212, 50K(B) (S3028, dual-gang).
	PORT_ADJUSTER(50, "Volume Level") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(tr707_audio_device::master_volume_adjusted), 0)

	// SLIDER_* are all 50K B-curve (linear) potentiomenters (part# S2018).
	PORT_START("SLIDER_1")  // VR201
	PORT_ADJUSTER(50, "Accent") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(roland_tr707_state::accent_pots_adjusted), 0)
	PORT_START("SLIDER_2")  // VR202
	PORT_ADJUSTER(100, "Bass Drum Level") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(tr707_audio_device::mix_adjusted), MC_BASS)
	PORT_START("SLIDER_3")  // VR203
	PORT_ADJUSTER(100, "Snare Drum Level") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(tr707_audio_device::mix_adjusted), MC_SNARE)
	PORT_START("SLIDER_4")  // VR204
	PORT_ADJUSTER(100, "Low Tom Level") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(tr707_audio_device::mix_adjusted), MC_LOW_TOM)
	PORT_START("SLIDER_5")  // VR205
	PORT_ADJUSTER(100, "Mid Tom Level") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(tr707_audio_device::mix_adjusted), MC_MID_TOM)
	PORT_START("SLIDER_6")  // VR206
	PORT_ADJUSTER(100, "Hi Tom Level") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(tr707_audio_device::mix_adjusted), MC_HI_TOM)
	PORT_START("SLIDER_7")  // VR207
	PORT_ADJUSTER(100, "Rimshot / Cowbell Level") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(tr707_audio_device::mix_adjusted), MC_RIMSHOT)
	PORT_START("SLIDER_8")  // VR208
	PORT_ADJUSTER(100, "Handclap / Tambourine Level") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(tr707_audio_device::mix_adjusted), MC_HANDCLAP)
	PORT_START("SLIDER_9")  // VR209
	PORT_ADJUSTER(100, "Hi Hat Level") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(tr707_audio_device::mix_adjusted), MC_HI_HAT)
	PORT_START("SLIDER_10")  // VR210
	PORT_ADJUSTER(100, "Crash Level") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(tr707_audio_device::mix_adjusted), MC_CRASH)
	PORT_START("SLIDER_11")  // VR211
	PORT_ADJUSTER(100, "Ride Level") PORT_CHANGED_MEMBER(AUDIO_TAG, FUNC(tr707_audio_device::mix_adjusted), MC_RIDE)

	// Trimmer defaults based on calibration instructions in the service notes.
	PORT_START("TM1")  // Tempo trimmer, TM-1, 200K(B) (RVF8P01-204).
	PORT_ADJUSTER(62, "TRIMMER: tempo (TM1)")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(roland_tr707_state::tempo_pots_adjusted), 0)
	PORT_START("TM2")  // Accent series trimmer, TM-2, 50K(B) (RVF8P01-503).
	PORT_ADJUSTER(45, "TRIMMER: accent, series (TM2)")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(roland_tr707_state::accent_pots_adjusted), 0)
	PORT_START("TM3")  // Accent parallel trimmer, TM-3, 200K(B) (RVF8P01-204).
	PORT_ADJUSTER(15, "TRIMMER: accent, parallel (TM3)")
		 PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(roland_tr707_state::accent_pots_adjusted), 0)

	PORT_START("DINSYNC_CONFIG")
	PORT_CONFNAME(0x01, 0x01, "DIN sync input cable connected")
	PORT_CONFSETTING(0x00, DEF_STR(No))
	PORT_CONFSETTING(0x01, DEF_STR(Yes))

	PORT_START("DINSYNC")  // SYNC socket (J4). Bit numbers map to `enum dinsync_index`.
	// SYNC start needs to be active for SYNC tempo to have an effect.
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("SYNC start/stop") PORT_CODE(KEYCODE_B)  // Pin 1.
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("SYNC tempo") PORT_CODE(KEYCODE_M)  // Pin 3. 24 cloks per quarter note.
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(roland_tr707_state::sync_input_changed), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("SYNC Continue") PORT_CODE(KEYCODE_N)  // Pin 5.

	PORT_START("TAPESYNC")  // TAPE LOAD / SYNC IN socket (J5).
	// Input connected to a filter (C30, R43, C31, R47), followed by a
	// comparator (IC17b) referenced to 3V.
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("TAPE LOAD / SYNC IN")
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(roland_tr707_state::sync_input_changed), 0)

	PORT_START("START_STOP_IN")  // START/STOP socket (J8).
	// This physical input is active low (pulled high by R66), but gets inverted
	// by Q16, which is what MCU reads. So treat as active high.
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("START / STOP IN") PORT_CODE(KEYCODE_BACKSLASH)
INPUT_PORTS_END

void roland_tr707_state::tr_707_727_common(machine_config &config)
{
	HD6303X(config, m_maincpu, 4_MHz_XTAL); // HD6303XF
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_tr707_state::mem_map);

	// P20 (TIN) connected to IC2d (4011), which outputs the selected tempo
	// source. Not configured here, look for M6801_TIN_LINE.
	m_maincpu->in_p2_cb().set(FUNC(roland_tr707_state::midi_rxd_r)).mask(0x01).lshift(3);

	m_maincpu->out_p2_cb().set(FUNC(roland_tr707_state::dinsync_w<DINSYNC_TEMPO>)).bit(1);
	m_maincpu->out_p2_cb().append(m_accent_adc_ff, FUNC(ttl7474_device::clock_w)).bit(2);
	m_maincpu->out_p2_cb().append("mdout", FUNC(midi_port_device::write_txd)).bit(4);
	m_maincpu->out_p2_cb().append_output("TAPESYNC_OUT").bit(5);  // TAPE SAVE / SYNC OUT socket (J6).
	m_maincpu->out_p2_cb().append(FUNC(roland_tr707_state::dinsync_w<DINSYNC_CONTINUE>)).bit(6);
	m_maincpu->out_p2_cb().append(FUNC(roland_tr707_state::dinsync_w<DINSYNC_STARTSTOP>)).bit(7);

	m_maincpu->in_p5_cb().set_constant(0).mask(0x01);  // Connected to gnd.
	// P51 (IRQ2) connected to IC4b (m_accent_adc_ff), pin Q. Not configured
	// here, look for HD6301_IRQ2_LINE.
	// A table in the service notes describes P52 and P53 as "unused, pulled up".
	m_maincpu->in_p5_cb().append_constant(0x0c).mask(0x0c);
	m_maincpu->in_p5_cb().append(FUNC(roland_tr707_state::cart_connected_r)).mask(0x01).lshift(4);
	m_maincpu->in_p5_cb().append(FUNC(roland_tr707_state::dinsync_r<DINSYNC_CONTINUE>)).mask(0x01).lshift(5);
	m_maincpu->in_p5_cb().append(FUNC(roland_tr707_state::dinsync_r<DINSYNC_STARTSTOP>)).mask(0x01).lshift(6);
	m_maincpu->in_p5_cb().append_ioport("START_STOP_IN").mask(0x01).lshift(7);

	m_maincpu->out_p6_cb().set(FUNC(roland_tr707_state::key_led_row_w)).mask(0x0f);
	m_maincpu->out_p6_cb().append(FUNC(roland_tr707_state::tempo_source_w)).rshift(4).mask(0x07);
	m_maincpu->out_p6_cb().append_output("RIMTRIG_OUT").bit(7).invert();  // Inverted by Q13.

	NVRAM(config, "nvram1", nvram_device::DEFAULT_ALL_0); // HM6116LP-4 + battery
	NVRAM(config, "nvram2", nvram_device::DEFAULT_ALL_0); // HM6116LP-4 + battery

	TIMER(config, m_tempo_timer).configure_generic(FUNC(roland_tr707_state::tempo_timer_tick));
	TIMER(config, m_tempo_restart_timer).configure_generic(FUNC(roland_tr707_state::tempo_restart_timer_tick));
	TTL7474(config, m_tempo_ff, 0);  // 4013, IC4a.
	m_tempo_ff->comp_output_cb().set(FUNC(roland_tr707_state::internal_tempo_clock_cb));

	VA_RC_EG(config, m_accent_adc_rc).set_c(CAP_U(0.27));  // C15.
	TIMER(config, m_accent_adc_timer).configure_generic(FUNC(roland_tr707_state::accent_adc_timer_tick));
	TTL7474(config, m_accent_adc_ff, 0);  // 4013, IC4b.
	m_accent_adc_ff->d_w(1);  // D tied to VCC.
	m_accent_adc_ff->comp_output_cb().set(FUNC(roland_tr707_state::accent_adc_flipflop_cb));

	MIDI_PORT(config, "mdin", midiin_slot, "midiin").rxd_handler().set(FUNC(roland_tr707_state::midi_rxd_w));
	MIDI_PORT(config, "mdout", midiout_slot, "midiout");

	GENERIC_CARTSLOT(config, m_cartslot, generic_plain_slot, nullptr, "m64c_cart");

	HD61602(config, m_lcdc);
	m_lcdc->write_segs().set(FUNC(roland_tr707_state::lcd_seg_w));
	PWM_DISPLAY(config, m_lcd_pwm).set_size(hd61602_device::NCOM, hd61602_device::NSEG);
	m_lcd_pwm->output_x().set(FUNC(roland_tr707_state::lcd_seg_outputs_w));

	PWM_DISPLAY(config, m_led_matrix).set_size(4, 6);
	m_led_matrix->output_x().set(FUNC(roland_tr707_state::led_outputs_w));

	config.set_default_layout(layout_roland_tr707);
}

void roland_tr707_state::tr707(machine_config &config)
{
	tr_707_727_common(config);
	TR707_AUDIO(config, m_audio, TR707_COMPONENTS);
	m_is_727 = false;
}

void roland_tr707_state::tr727(machine_config &config)
{
	tr_707_727_common(config);
	TR707_AUDIO(config, m_audio, TR727_COMPONENTS);
	m_is_727 = true;
}

ROM_START(tr707)
	ROM_REGION(0x4000, "program", 0)
	ROM_LOAD("os_rom_firmware.ic13", 0x0000, 0x4000, CRC(3517ea00) SHA1(f5d57a79abf49131bd9832ae4e2dbced914ea523)) // 27128

	ROM_REGION(0x10000, "voices", 0) // "BD-MT" (IC34) and "HT-TAMB" (IC35)
	ROM_LOAD("hn61256p_c71_15179694.ic34", 0x0000, 0x8000, CRC(a196489b) SHA1(fd2bfe67d4d03d2b2134aa7feebe9167c44b1f8d))
	ROM_LOAD("hn61256p_c72_15179695.ic35", 0x8000, 0x8000, CRC(b05302e5) SHA1(5cc866f345906d817147ae2a61bc36d7be926511))

	ROM_REGION(0x8000, "cymbal1", 0) // "Crash Cymbal"
	ROM_LOAD("hn61256p_c73_15179696.ic19", 0x0000, 0x8000, CRC(b0bea07f) SHA1(965e23ad71e1f95d56307fa67272725dff46ba67))

	ROM_REGION(0x8000, "cymbal2", 0) // "Ride Cymbal"
	ROM_LOAD("hn61256p_c74_15179697.ic22", 0x0000, 0x8000, CRC(9411943a) SHA1(6c7c0f002ed66e4ccf182a4538d9bb239623ac43))
ROM_END

ROM_START(tr727)
	ROM_REGION(0x4000, "program", 0)
	ROM_LOAD("osv_1.0_hd4827128.ic13", 0x0000, 0x4000, CRC(49954161) SHA1(8eb033d9729aa84cc3c33b8ce30925ff3c35e70a))

	ROM_REGION(0x10000, "voices", 0) // "BNG-HTB" (IC34) and "LTB-MC" (IC35)
	ROM_LOAD("hn61256p_15179694.ic34", 0x0000, 0x8000, NO_DUMP)
	ROM_LOAD("hn61256p_15179695.ic35", 0x8000, 0x8000, NO_DUMP)

	ROM_REGION(0x8000, "cymbal1", 0) // "Quijada"
	ROM_LOAD("hn61256p_15179696.ic19", 0x0000, 0x8000, NO_DUMP)

	ROM_REGION(0x8000, "cymbal2", 0) // "Star Chime"
	ROM_LOAD("hn61256p_15179697.ic22", 0x0000, 0x8000, NO_DUMP)
ROM_END

} // anonymous namespace


SYST(1985, tr707, 0, 0, tr707, tr707, roland_tr707_state, empty_init, "Roland", "TR-707 Rhythm Composer", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE)
SYST(1985, tr727, 0, 0, tr727, tr707, roland_tr707_state, empty_init, "Roland", "TR-727 Rhythm Composer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)



roland_tr808.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
/***************************************************************************

Roland TR-808 Rhythm Composer, drum machine from 1980

Hardware notes:
- NEC uCOM-43 MCU, labeled D650C 085
- 4*uPD444C 1024x4 Static CMOS SRAM
- board is packed with discrete components

TODO:
- everything

***************************************************************************/

#include "emu.h"

#include "cpu/ucom4/ucom4.h"
#include "machine/clock.h"


namespace {

class tr808_state : public driver_device
{
public:
	tr808_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu")
	{ }

	void tr808(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<ucom4_cpu_device> m_maincpu;
};

void tr808_state::machine_start()
{
}



/***************************************************************************
    Inputs
***************************************************************************/

static INPUT_PORTS_START( tr808 )
INPUT_PORTS_END



/***************************************************************************
    Machine Configs
***************************************************************************/

void tr808_state::tr808(machine_config &config)
{
	// basic machine hardware
	NEC_D650(config, m_maincpu, 500000); // 2us according to schematics

	auto &irq_clock(CLOCK(config, "irq_clock"));
	irq_clock.set_period(attotime::from_usec(1900)); // clock rate 1.9ms
	irq_clock.set_duty_cycle(0.1); // short duty cycle
	irq_clock.signal_handler().set_inputline(m_maincpu, 0);

	// sound hardware
	// discrete...
}



/***************************************************************************
    ROM Definitions
***************************************************************************/

ROM_START( tr808 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "d650c-085.ic4", 0x0000, 0x0800, CRC(06f0c405) SHA1(eddac7af6396c3f220914d9cad2c9e398872b034) )
ROM_END

} // anonymous namespace



/***************************************************************************
    Drivers
***************************************************************************/

SYST( 1980, tr808, 0, 0, tr808, tr808, tr808_state, empty_init, "Roland", "TR-808 Rhythm Composer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



roland_tr909.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for Roland TR-909 drum machine.

***************************************************************************/

#include "emu.h"
#include "bus/generic/slot.h"
#include "cpu/upd7810/upd7810.h"
#include "machine/nvram.h"


namespace {

class roland_tr909_state : public driver_device
{
public:
	roland_tr909_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_cartslot(*this, "cartslot")
		, m_inputs(*this, "IN%u", 1U)
		, m_porta(0)
		, m_portb(0)
	{
	}

	void tr909(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void dac1_w(u8 data);
	void dac2_w(u8 data);
	void dac3_w(u8 data);
	void dac4_w(u8 data);
	void dac5_w(u8 data);
	void dac6_w(u8 data);
	void dac7_w(u8 data);
	void dac8_w(u8 data);
	void trig1_w(u8 data);
	void trig2_w(u8 data);

	void pa_w(u8 data);
	void pb_w(u8 data);
	u8 switches_r();
	u8 cart_sense_r();

	void tr909_mem(address_map &map) ATTR_COLD;

	required_device<generic_cartslot_device> m_cartslot;
	required_ioport_array<5> m_inputs;

	u8 m_porta;
	u8 m_portb;
};


void roland_tr909_state::machine_start()
{
	save_item(NAME(m_porta));
	save_item(NAME(m_portb));
}

void roland_tr909_state::machine_reset()
{
	dac1_w(0);
	dac2_w(0);
	dac3_w(0);
	dac4_w(0);
	dac5_w(0);
	dac6_w(0);
	dac7_w(0);
	dac8_w(0);
	trig1_w(0);
	trig2_w(0);
}


void roland_tr909_state::dac1_w(u8 data)
{
	logerror("%s: DAC 1 = %02X\n", machine().describe_context(), data & 0x7e);
}

void roland_tr909_state::dac2_w(u8 data)
{
	logerror("%s: DAC 2 = %02X\n", machine().describe_context(), data & 0x7e);
}

void roland_tr909_state::dac3_w(u8 data)
{
	logerror("%s: DAC 3 = %02X\n", machine().describe_context(), data & 0x7e);
}

void roland_tr909_state::dac4_w(u8 data)
{
	logerror("%s: DAC 4 = %02X\n", machine().describe_context(), data & 0x7e);
}

void roland_tr909_state::dac5_w(u8 data)
{
	logerror("%s: DAC 5 = %02X\n", machine().describe_context(), data & 0x7e);
}

void roland_tr909_state::dac6_w(u8 data)
{
	logerror("%s: DAC 6 = %02X\n", machine().describe_context(), data & 0x7e);
}

void roland_tr909_state::dac7_w(u8 data)
{
	logerror("%s: DAC 7 = %02X\n", machine().describe_context(), data & 0x7e);
}

void roland_tr909_state::dac8_w(u8 data)
{
	logerror("%s: DAC 8 = %02X\n", machine().describe_context(), data & 0x7e);
}

void roland_tr909_state::trig1_w(u8 data)
{
	// D6 is unused
	logerror("%s: Trigger 1 = %02X\n", machine().describe_context(), data & 0x3e);
}

void roland_tr909_state::trig2_w(u8 data)
{
	logerror("%s: Trigger 2 = %02X\n", machine().describe_context(), data & 0x7e);
}


void roland_tr909_state::pa_w(u8 data)
{
	// TODO: LED matrix and 4511 digits
	m_porta = data;
}

void roland_tr909_state::pb_w(u8 data)
{
	// TODO: LED matrix, 4511 LE and tape outputs
	m_portb = data;
}

u8 roland_tr909_state::switches_r()
{
	u8 data = 0xff;
	for (int i = 0; i < 5; i++)
		if (!BIT(m_porta, i))
			data &= m_inputs[i]->read();

	return data;
}

u8 roland_tr909_state::cart_sense_r()
{
	// TODO
	return 0;
}


void roland_tr909_state::tr909_mem(address_map &map)
{
	map(0x0000, 0x1fff).rom().region("firmware", 0);
	map(0x2000, 0x3fff).ram().share("nvram");
	map(0x4000, 0x4000).mirror(0x1fff).r(FUNC(roland_tr909_state::switches_r));
	map(0x4001, 0x4001).mirror(0x1ff0).w(FUNC(roland_tr909_state::dac1_w));
	map(0x4002, 0x4002).mirror(0x1ff0).w(FUNC(roland_tr909_state::dac2_w));
	map(0x4003, 0x4003).mirror(0x1ff0).w(FUNC(roland_tr909_state::dac3_w));
	map(0x4004, 0x4004).mirror(0x1ff0).w(FUNC(roland_tr909_state::dac4_w));
	map(0x4005, 0x4005).mirror(0x1ff0).w(FUNC(roland_tr909_state::dac5_w));
	map(0x4006, 0x4006).mirror(0x1ff0).w(FUNC(roland_tr909_state::dac6_w));
	map(0x4007, 0x4007).mirror(0x1ff0).w(FUNC(roland_tr909_state::dac7_w));
	map(0x4008, 0x4008).mirror(0x1ff0).w(FUNC(roland_tr909_state::dac8_w));
	map(0x4009, 0x4009).mirror(0x1ff0).w(FUNC(roland_tr909_state::trig1_w));
	map(0x400a, 0x400a).mirror(0x1ff0).w(FUNC(roland_tr909_state::trig2_w));
	map(0x6000, 0x7fff).unmaprw(); // cartridge
}


static INPUT_PORTS_START(tr909)
	PORT_START("IN1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW3")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW4")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW5")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW6")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW7")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW8")

	PORT_START("IN2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW9")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW10")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW11")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW12")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW13")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW14")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW15")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SW16")

	PORT_START("IN3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Shift")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Scale")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Inst Select")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Shuffle/Flam")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Last Step")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Stop/Cont")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Start")

	PORT_START("IN4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Clear")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Track 1")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Track 2")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Track 3")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Track 4")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Pattern 1")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Pattern 2")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Pattern 3")

	PORT_START("IN5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Enter/Accent")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Tape Sync")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Cycle")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Available Meas")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Fwd")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Back")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Tempo")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Ext Inst")

	PORT_START("TEMPO")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNKNOWN) // TODO: analog

	PORT_START("TOTAL")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNKNOWN) // TODO: analog
INPUT_PORTS_END


static void ram_carts(device_slot_interface &device)
{
}

void roland_tr909_state::tr909(machine_config &config)
{
	upd7810_device &maincpu(UPD7810(config, "maincpu", 12_MHz_XTAL));
	maincpu.set_addrmap(AS_PROGRAM, &roland_tr909_state::tr909_mem);
	maincpu.pa_out_cb().set(FUNC(roland_tr909_state::pa_w));
	maincpu.pb_out_cb().set(FUNC(roland_tr909_state::pb_w));
	//maincpu.pc_in_cb().set(FUNC(roland_tr909_state::tr909_portc_r));
	//maincpu.pc_out_cb().set(FUNC(roland_tr909_state::tr909_portc_w));
	maincpu.an0_func().set_ioport("TEMPO");
	maincpu.an1_func().set(FUNC(roland_tr909_state::cart_sense_r));
	maincpu.an2_func().set_constant(0);
	maincpu.an3_func().set_ioport("TOTAL");
	maincpu.an4_func().set_constant(0);
	maincpu.an5_func().set_constant(0);
	maincpu.an6_func().set_constant(0);
	maincpu.an7_func().set_constant(0);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // TC5565PL-15 + battery

	// no screen

	GENERIC_CARTSLOT(config, m_cartslot, ram_carts, "tr909_cart", "bin");

	//SOFTWARE_LIST(config, "cart_list", "tr909");
}


ROM_START(tr909)
	ROM_REGION(0x2000, "firmware", 0)
	//cpu is a D7811G 037 with 4096w of ROM
	//mode pins are both pulled to 5v

	//version 0 and 1 firmware also exist
	ROM_SYSTEM_BIOS(0, "v4", "v4.0")
	ROMX_LOAD("tr909_v4.ic609", 0, 0x2000, CRC(a9b45f30) SHA1(b94374e2cd6b5a6c405da4fa7793c98c0a6015c4), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v1", "v1.0")
	ROMX_LOAD("tr909_v1.ic609", 0, 0x2000, CRC(bfb9682b) SHA1(bcd754900574d1861034af8056574ffa77db3067), ROM_BIOS(1))

	//samples
	ROM_REGION(0x8000, "hihat", 0)
	ROM_LOAD("hn61256p__c43.ic69", 0, 0x8000, CRC(2aaae11b) SHA1(22a34d603e78673bb096f5e56a8971afe14d8fee))

	ROM_REGION(0x8000, "crash", 0)
	ROM_LOAD("hn61256p__c42.ic62", 0, 0x8000, CRC(dc61cf1a) SHA1(bc730107b0c391f4879c9777a3e85f3e57f3aad6))

	ROM_REGION(0x8000, "ride", 0)
	ROM_LOAD("hn61256p__c44.ic54", 0, 0x8000, CRC(01a9b435) SHA1(daa54c58c7e3ae3398f125568537ec82d5bd1dfd))
ROM_END

} // anonymous namespace


SYST(1984, tr909, 0, 0, tr909, tr909, roland_tr909_state, empty_init, "Roland", "TR-909 Rhythm Composer", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



roland_u20.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Roland U-20 & related synthesizers.

****************************************************************************/

#include "emu.h"
#include "cpu/mcs96/i8x9x.h"
#include "sound/roland_lp.h"
#include "speaker.h"


namespace {

class roland_u20_state : public driver_device
{
public:
	roland_u20_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pcm(*this, "pcm")
	{
	}

	void u20(machine_config &config);
	void u220(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<i8x9x_device> m_maincpu;
	required_device<mb87419_mb87420_device> m_pcm;
};


void roland_u20_state::mem_map(address_map &map)
{
	map(0x1800, 0x1fff).ram(); // TODO: more RAM than this is present
	map(0x2000, 0xffff).rom().region("progrom", 0x2000); // TODO: banking
}

static INPUT_PORTS_START(u20)
INPUT_PORTS_END

void roland_u20_state::u20(machine_config &config)
{
	P8098(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &roland_u20_state::mem_map);

	//R15239124(config, "keyscan", 12_MHz_XTAL);

	SPEAKER(config, "speaker", 2).front();

	MB87419_MB87420(config, m_pcm, 32.768_MHz_XTAL);
	m_pcm->int_callback().set_inputline(m_maincpu, i8x9x_device::EXTINT_LINE);
	m_pcm->set_device_rom_tag("waverom");
	m_pcm->add_route(0, "speaker", 1.0, 0);
	m_pcm->add_route(1, "speaker", 1.0, 1);
}

void roland_u20_state::u220(machine_config &config)
{
	u20(config);

	//config.device_remove("keyscan");
	m_pcm->int_callback().set_inputline(m_maincpu, i8x9x_device::HSI0_LINE);
}

ROM_START(u20)
	ROM_REGION(0x20000, "progrom", 0)
	ROM_SYSTEM_BIOS(0, "v303", "Version 3.03")
	ROMX_LOAD("u-20-v303.bin", 0x00000, 0x20000, CRC(28ce7fca) SHA1(d4186a7034a0646e1c76a3a7f5c3cf5f165ace80), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v103", "Version 1.03")
	ROMX_LOAD("u-20-v103.bin", 0x00000, 0x20000, CRC(eb94054f) SHA1(1127e21ba94cc629eb00355c0be6ace6ca7759d6), ROM_BIOS(1)) // M5M27C100P

	ROM_REGION(0x400000, "waverom", ROMREGION_ERASE00)
	ROM_LOAD("roland-a_r15179892f_mb834000a-20_226-aa.ic27", 0x000000, 0x080000, NO_DUMP)
	ROM_LOAD("roland-b_r15179893f_mb834000a-20_227-aa.ic28", 0x080000, 0x080000, NO_DUMP)
	ROM_LOAD("roland-c_r15179894f_mb834000a-20_228-aa.ic29", 0x100000, 0x080000, NO_DUMP)
	ROM_LOAD("roland-d_r15179895f_mb834000a-20_229-aa.ic30", 0x180000, 0x080000, NO_DUMP)
	ROM_LOAD("roland-e_r15179947_mb834000a-20_3a1-aa.ic31",  0x200000, 0x080000, NO_DUMP)
	ROM_LOAD("roland-f_r15179948_mb834000a-20_3a2-aa.ic32",  0x280000, 0x080000, NO_DUMP)
ROM_END

ROM_START(u220)
	ROM_REGION(0x20000, "progrom", 0)
	ROM_SYSTEM_BIOS(0, "v102", "Version 1.02")
	ROMX_LOAD("u-220_roland_1-0-2.ic8", 0x00000, 0x20000, CRC(d5492e9b) SHA1(9d72b1688a173505b5e1c5ddac01efa78ee76c7f), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v101", "Version 1.01")
	ROMX_LOAD("u-220_roland_1-0-1.ic8", 0x00000, 0x20000, CRC(893afa9c) SHA1(d0b66c0ea0e3af284a1806226aed79d8da2f3dd4), ROM_BIOS(1)) // HN27C101G-20

	ROM_REGION(0x400000, "waverom", ROMREGION_ERASE00)
	ROM_LOAD("roland-a_r15179892f_mb834000a-20_226-aa.ic19", 0x000000, 0x080000, NO_DUMP)
	ROM_LOAD("roland-b_r15179893f_mb834000a-20_227-aa.ic20", 0x080000, 0x080000, NO_DUMP)
	ROM_LOAD("roland-c_r15179894f_mb834000a-20_228-aa.ic21", 0x100000, 0x080000, NO_DUMP)
	ROM_LOAD("roland-d_r15179895f_mb834000a-20_229-aa.ic22", 0x180000, 0x080000, NO_DUMP)
	ROM_LOAD("roland-e_r15179947_mb834000a-20_3a1-aa.ic23",  0x200000, 0x080000, NO_DUMP)
	ROM_LOAD("roland-f_r15179948_mb834000a-20_3a2-aa.ic24",  0x280000, 0x080000, NO_DUMP)
ROM_END

} // anonymous namespace


SYST(1989, u20,  0, 0, u20,  u20, roland_u20_state, empty_init, "Roland", "U-20 RS-PCM Keyboard", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1989, u220, 0, 0, u220, u20, roland_u20_state, empty_init, "Roland", "U-220 RS-PCM Sound Module", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



roma2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
/*******************************************************************************

Mephisto Roma II

It's a cost-reduced version of Roma 68000, not really a 'sequel'.

Hardware notes:
- MC68HC000FN10 @ 9.83MHz
- 64KB ROM (2*27C256), 16KB RAM (2*HY6264LP-10), piezo
- compatible with magnets chessboard or Mephisto Mobil, and 7seg LCD module

Mephisto Montreal 68000 from 1993 is on similar hardware, but it is a standalone
chess computer (Roma II is a module). The chess engine is still the old 1987 Roma.

TODO:
- verify montreal XTAL (currently guessed from beeper pitch on video)
- verify irq source and level
- does it have DTACK waitstates?

*******************************************************************************/

#include "emu.h"

#include "mmboard.h"
#include "mmdisplay1.h"

#include "cpu/m68000/m68000.h"
#include "machine/74259.h"
#include "sound/dac.h"

#include "speaker.h"

// internal artwork
#include "mephisto_montreal.lh"
#include "mephisto_roma2.lh"


namespace {

class roma2_state : public driver_device
{
public:
	roma2_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_outlatch(*this, "outlatch"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_reset(*this, "RESET")
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(reset_button);

	void roma2(machine_config &config);
	void montreal(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	required_device<hc259_device> m_outlatch;
	required_device<mephisto_board_device> m_board;
	required_device<mephisto_display1_device> m_display;
	required_device<dac_2bit_ones_complement_device> m_dac;
	required_ioport_array<4> m_inputs;
	optional_ioport m_reset;

	void main_map(address_map &map) ATTR_COLD;

	u8 input_r();
};



/*******************************************************************************
    I/O
*******************************************************************************/

INPUT_CHANGED_MEMBER(roma2_state::reset_button)
{
	// RES buttons in serial tied to CPU RESET
	if (m_reset->read() == 3)
		m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
}

u8 roma2_state::input_r()
{
	u8 data = 0;

	// read keypad
	for (int i = 0; i < 4; i++)
		if (!BIT(m_outlatch->output_state(), i))
			data |= m_inputs[i]->read();

	return data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void roma2_state::main_map(address_map &map)
{
	map(0x000000, 0x00ffff).rom();
	map(0x800000, 0x803fff).ram();
	map(0x900000, 0x90000f).w(m_outlatch, FUNC(hc259_device::write_d0)).umask16(0xff00);
	map(0xb00000, 0xb00000).w(m_board, FUNC(mephisto_board_device::led_w));
	map(0xc00000, 0xc00000).w(m_board, FUNC(mephisto_board_device::mux_w));
	map(0xd00000, 0xd00000).r(m_board, FUNC(mephisto_board_device::input_r));
	map(0xd00001, 0xd00001).r(FUNC(roma2_state::input_r));
	map(0xe00000, 0xe00000).w(m_display, FUNC(mephisto_display1_device::data_w));

	map(0x900000, 0x900009).nopr(); // st
	map(0xc00000, 0xc00001).nopr(); // st
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( montreal )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("INFO") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("POS") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("LEV") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("MEM") PORT_CODE(KEYCODE_M)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("CL") PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_F1) // combine CL/ENT for NEW GAME
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("ENT") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CODE(KEYCODE_F1) // "
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("A / 1") PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("B / 2 / Pawn") PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("C / 3 / Knight") PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("D / 4 / Bishop") PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("E / 5 / Rook") PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("F / 6 / Queen") PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("G / 7 / King") PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("H / 8") PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Left / Black / 9") PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Right / White / 0") PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_RIGHT)
INPUT_PORTS_END

static INPUT_PORTS_START( roma2 )
	PORT_INCLUDE( montreal )

	PORT_MODIFY("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("CL") PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("ENT") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("RES 1") PORT_CODE(KEYCODE_Z) PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(roma2_state::reset_button), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("RES 2") PORT_CODE(KEYCODE_X) PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(roma2_state::reset_button), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void roma2_state::roma2(machine_config &config)
{
	// basic machine hardware
	M68000(config, m_maincpu, 9.8304_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &roma2_state::main_map);

	const attotime irq_period = attotime::from_hz(9.8304_MHz_XTAL / 0x14000); // 120Hz
	m_maincpu->set_periodic_int(FUNC(roma2_state::irq5_line_hold), irq_period);

	HC259(config, m_outlatch);
	// Q0-Q3: input mux; Q4: lcd common; Q6-Q7: DAC
	m_outlatch->q_out_cb<4>().set(m_display, FUNC(mephisto_display1_device::common_w));
	m_outlatch->parallel_out_cb().set(m_dac, FUNC(dac_2bit_ones_complement_device::write)).rshift(6).mask(3);

	MEPHISTO_SENSORS_BOARD(config, m_board);
	m_board->set_delay(attotime::from_msec(200));

	// video hardware
	MEPHISTO_DISPLAY_MODULE1(config, m_display);
	config.set_default_layout(layout_mephisto_roma2);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_2BIT_ONES_COMPLEMENT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.125);
}

void roma2_state::montreal(machine_config &config)
{
	roma2(config);

	// basic machine hardware
	m_maincpu->set_clock(12.288_MHz_XTAL);

	const attotime irq_period = attotime::from_hz(12.288_MHz_XTAL / 0x14000); // 150Hz
	m_maincpu->set_periodic_int(FUNC(roma2_state::irq5_line_hold), irq_period);

	config.set_default_layout(layout_mephisto_montreal);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( roma2 )
	ROM_REGION16_BE( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("roma_ii_u_ver.4.02", 0x00000, 0x08000, CRC(89e95f5f) SHA1(6c3992f35fba2c1cc08a93f50aa991d5ffda5bc3) )
	ROM_LOAD16_BYTE("roma_ii_l_ver.4.02", 0x00001, 0x08000, CRC(74b03889) SHA1(9d2a09b93f3b2dc483b4f30db134f83deb3aa951) )
ROM_END

ROM_START( montreal )
	ROM_REGION16_BE( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("montreal_even_5.01.u3", 0x00000, 0x08000, CRC(b32a5db4) SHA1(02a41732d80d91a984814acfae7fec0a8522c13d) )
	ROM_LOAD16_BYTE("montreal_odd_5.01.u2",  0x00001, 0x08000, CRC(a60e2808) SHA1(8a2d404548a934573be125b0cdb91285569b8f72) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT  COMPAT  MACHINE   INPUT     CLASS        INIT        COMPANY, FULLNAME, FLAGS
SYST( 1989, roma2,     0,      0,      roma2,    roma2,    roma2_state, empty_init, "Hegener + Glaser", "Mephisto Roma II", MACHINE_SUPPORTS_SAVE )
SYST( 1993, montreal,  0,      0,      montreal, montreal, roma2_state, empty_init, "Hegener + Glaser", "Mephisto Montreal 68000", MACHINE_SUPPORTS_SAVE )



royal.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

CXG Sphinx Royal family

NOTE: Turn the power switch (or button in supra's case) off before exiting MAME,
otherwise NVRAM won't save properly. And only turn the power off when it's the
user's turn to move, this is warned about in the manual.

TODO:
- if/when MAME supports an exit callback, hook up power-off switch/button to that

Hardware notes:
- Hitachi HD614042S, 4MHz XTAL
- optional LCD panel(s), see below
- chessboard buttons, 16+4 LEDs, piezo

Royal has 2 LCD panels, Supra has 1 (D12 pin is low), Granada and others have 0.
The LCD panel has 4 7segs (no DP) and 2 unused segments: an x in the middle, and
a white square under the first digit.

The 1992 versions by National Telecommunications System Ltd (Granada CXG-347,
Sierra, Seville) have a lower-speed 3.58MHz XTAL, but since none of them have
LCD panels, users won't notice the slower chess clocks.

Excluding other brands with the same product name, HD614042SJ02 MCU was used in:
- CXG Sphinx Royal (model 240)
- CXG Sphinx Supra (model 048)
- CXG Sphinx Granada (model 247/347)
- CXG Sphinx Seville (model 807)
- CXG Sphinx Sierra (model 647W)
- CXG Sphinx Alicante (model 328, suspected)
- Excalibur Chess Wizard (model 807E), Excalibur brand Sphinx Seville
- Excalibur Stiletto (model 328E/638E), Excalibur brand Sphinx Alicante (suspected)

*******************************************************************************/

#include "emu.h"

#include "cpu/hmcs400/hmcs400.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "cxg_granada.lh"
#include "cxg_royal.lh"
#include "cxg_supra.lh"


namespace {

class royal_state : public driver_device
{
public:
	royal_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_led_pwm(*this, "led_pwm"),
		m_lcd_pwm(*this, "lcd_pwm"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_out_digit(*this, "digit%u", 0U)
	{ }

	void royal(machine_config &config);
	void granada(machine_config &config);
	void supra(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(granada_change_cpu_freq);
	DECLARE_INPUT_CHANGED_MEMBER(supra_on_button);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<hmcs400_cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_led_pwm;
	required_device<pwm_display_device> m_lcd_pwm;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<2> m_inputs;
	output_finder<8> m_out_digit;

	u8 m_inp_mux = 0;
	u8 m_lcd_com = 0;
	u16 m_lcd_segs = 0;
	u8 m_lcd_select = 0;

	// I/O handlers
	void stop_mode(int state);

	void update_lcd();
	template<int N> void lcd_segs_w(u8 data);
	void lcd_com_w(u8 data);

	template<int N> u8 board_r();
	template<int N> void input_w(u8 data);
	void control_w(u16 data);
	u16 input_r();
};

void royal_state::machine_start()
{
	m_out_digit.resolve();

	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_lcd_com));
	save_item(NAME(m_lcd_segs));
	save_item(NAME(m_lcd_select));
}

INPUT_CHANGED_MEMBER(royal_state::granada_change_cpu_freq)
{
	// 1992 models run at lower speed
	m_maincpu->set_unscaled_clock((newval & 1) ? 4_MHz_XTAL : 3.58_MHz_XTAL);
}



/*******************************************************************************
    I/O
*******************************************************************************/

// power

void royal_state::stop_mode(int state)
{
	// clear display
	if (state)
	{
		m_lcd_pwm->clear();
		m_led_pwm->clear();
	}
}

INPUT_CHANGED_MEMBER(royal_state::supra_on_button)
{
	// stop mode check actually comes from D3 high-impedance state
	if (newval && m_maincpu->stop_mode())
		m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
}


// LCD

void royal_state::update_lcd()
{
	for (int i = 0; i < 2; i++)
	{
		// LCD common is analog (voltage level)
		const u8 com = population_count_32(m_lcd_com >> (i * 2) & 3);
		const u16 data = (com == 0) ? m_lcd_segs : (com == 2) ? ~m_lcd_segs : 0;

		// 2 digits per common
		for (int j = 0; j < 2; j++)
		{
			u8 digit = bitswap<8>(data >> (j * 8), 0,1,2,3,4,5,6,7);
			m_lcd_pwm->write_row(m_lcd_select * 4 + i * 2 + j, digit);
		}
	}
}

template<int N>
void royal_state::lcd_segs_w(u8 data)
{
	// R0x,R6x-R8x: LCD segment data
	const u8 shift = N * 4;
	m_lcd_segs = (m_lcd_segs & ~(0xf << shift)) | (data << shift);
	update_lcd();
}

void royal_state::lcd_com_w(u8 data)
{
	// R50-R53: LCD common
	m_lcd_com = data;
	update_lcd();
}


// misc

template<int N>
u8 royal_state::board_r()
{
	// R1x,R2x: read chessboard
	u8 data = 0;

	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_rank(i);

	return data >> (N * 4) & 0xf;
}

template<int N>
void royal_state::input_w(u8 data)
{
	// R3x,R4x: input mux, LED data
	const u8 shift = 4 * N;
	m_inp_mux = (m_inp_mux & ~(0xf << shift)) | (data << shift);
	m_led_pwm->write_mx(~m_inp_mux);
}

void royal_state::control_w(u16 data)
{
	// D4,D5: LED select
	// D6-D9: status LEDs (direct)
	m_led_pwm->write_my(data >> 4 & 0x3f);

	// D13: LCD panel select
	m_lcd_select = BIT(data, 13);
	update_lcd();

	// D14: speaker out
	m_dac->write(BIT(data, 14));
}

u16 royal_state::input_r()
{
	u16 data = 0;

	// D0,D1: read buttons
	for (int i = 0; i < 2; i++)
		if (m_inp_mux & m_inputs[i]->read())
			data |= 1 << i;

	// D10,D11: freq sel
	// D12: 1/2 LCD panels
	return data | 0x140c;
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( royal )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_POWER_OFF)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Move")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Set-Up")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Sound")
INPUT_PORTS_END

static INPUT_PORTS_START( granada )
	PORT_INCLUDE( royal )

	PORT_START("CPU")
	PORT_CONFNAME( 0x01, 0x01, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(royal_state::granada_change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "3.58MHz (1992 version)" )
	PORT_CONFSETTING(    0x01, "4MHz (1988 version)" )
INPUT_PORTS_END

static INPUT_PORTS_START( supra )
	PORT_INCLUDE( royal )

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_ON) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(royal_state::supra_on_button), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void royal_state::royal(machine_config &config)
{
	// basic machine hardware
	HD614042(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->stop_cb().set(m_maincpu, FUNC(hmcs400_cpu_device::nvram_set_battery));
	m_maincpu->stop_cb().append(FUNC(royal_state::stop_mode));
	m_maincpu->write_r<0x0>().set(FUNC(royal_state::lcd_segs_w<0>));
	m_maincpu->read_r<0x1>().set(FUNC(royal_state::board_r<0>));
	m_maincpu->read_r<0x2>().set(FUNC(royal_state::board_r<1>));
	m_maincpu->write_r<0x3>().set(FUNC(royal_state::input_w<0>));
	m_maincpu->write_r<0x4>().set(FUNC(royal_state::input_w<1>));
	m_maincpu->write_r<0x5>().set(FUNC(royal_state::lcd_com_w));
	m_maincpu->write_r<0x6>().set(FUNC(royal_state::lcd_segs_w<3>));
	m_maincpu->write_r<0x7>().set(FUNC(royal_state::lcd_segs_w<2>));
	m_maincpu->write_r<0x8>().set(FUNC(royal_state::lcd_segs_w<1>));
	m_maincpu->write_d().set(FUNC(royal_state::control_w));
	m_maincpu->read_d().set(FUNC(royal_state::input_r));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_lcd_pwm).set_size(8, 8);
	m_lcd_pwm->set_bri_levels(0.05);
	m_lcd_pwm->set_segmask(0xff, 0x7f);
	m_lcd_pwm->output_digit().set([this](offs_t offset, u64 data) { m_out_digit[offset] = data; });

	PWM_DISPLAY(config, m_led_pwm).set_size(6, 8);
	config.set_default_layout(layout_cxg_royal);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void royal_state::supra(machine_config &config)
{
	royal(config);
	m_maincpu->read_d().set(FUNC(royal_state::input_r)).exor(0x1000);
	config.set_default_layout(layout_cxg_supra);
}

void royal_state::granada(machine_config &config)
{
	supra(config);
	config.set_default_layout(layout_cxg_granada);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( sroyal )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD("1988_105_newcrest_hd614042sj02", 0x0000, 0x2000, CRC(47334ac9) SHA1(b4dc930e34926f1b33f6d247af45627c891202ff) )
ROM_END

#define rom_granada rom_sroyal
#define rom_supra rom_sroyal

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS        INIT        COMPANY, FULLNAME, FLAGS
SYST( 1988, sroyal,  0,      0,      royal,   royal,   royal_state, empty_init, "CXG Systems / Newcrest Technology / Intelligent Chess Software", "Sphinx Royal", MACHINE_SUPPORTS_SAVE )
SYST( 1988, granada, sroyal, 0,      granada, granada, royal_state, empty_init, "CXG Systems / Newcrest Technology / Intelligent Chess Software", "Sphinx Granada", MACHINE_SUPPORTS_SAVE )
SYST( 1988, supra,   sroyal, 0,      supra,   supra,   royal_state, empty_init, "CXG Systems / Newcrest Technology / Intelligent Chess Software", "Sphinx Supra", MACHINE_SUPPORTS_SAVE )



rs6000_type7xxx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    rs6000_type7xxx.cpp - IBM RS/6000 PowerPC based MicroChannel systems
    Skeleton by R. Belmont

    Type 7009: PPC 601 @ 80 MHz, serial console, SCSI, 3-digit 7-segment display,
    4 MCA slots, Ethernet, and a 2.88MB floppy with an unknown controller

    The NetBSD boot log for a Type 7011 indicates the integrated SCSI is
    an NCR 53C720 appearing as an MCA board with the ID word 0x8fba.
    The "I/O planar" appears as an MCA board with the ID word 0x8f98 and
    it contains two NS16550A UARTs and an unknown Ethernet chip.

    References:
    https://www.ibm.com/common/ssi/ShowDoc.wss?docURL=/common/ssi/rep_sm/0/897/ENUS7009-C10/index.html&lang=en_US
    http://mail-index.netbsd.org/port-prep/2008/01/15/msg000001.html

************************************************************************/

#include "emu.h"

#include "cpu/powerpc/ppc.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "speaker.h"

namespace {

class type7xxx_state : public driver_device
{
public:
	type7xxx_state(const machine_config &mconfig, device_type type, const char *tag);

	void type7009(machine_config &config);

private:
	required_device<ppc_device> m_maincpu;

	void type7009_map(address_map &map) ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};

type7xxx_state::type7xxx_state(const machine_config &mconfig, device_type type, const char *tag) :
	driver_device(mconfig, type, tag),
	m_maincpu(*this, "maincpu")
{
}

void type7xxx_state::type7009_map(address_map &map)
{
	map(0xfff00000, 0xfff7ffff).rom().region("bootrom", 0);
}

void type7xxx_state::type7009(machine_config &config)
{
	PPC601(config, m_maincpu, 80000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &type7xxx_state::type7009_map);
}

static INPUT_PORTS_START( type7009 )
INPUT_PORTS_END

ROM_START( rs6k7009 )
	ROM_REGION64_BE(0x80000, "bootrom", 0)
	ROM_LOAD( "am27c040@dip32_ibm_7009_c10.bin", 0x000000, 0x080000, CRC(ff889bba) SHA1(b633584a0707913ac42ec127fcc567aa27d8af06) )
ROM_END

}   // anonymous namespace

COMP( 1994, rs6k7009,  0, 0, type7009, type7009, type7xxx_state, empty_init, "International Business Machines", "RS/6000 Type 7009 Model C10 Server",  MACHINE_NOT_WORKING|MACHINE_NO_SOUND_HW )



rt1715.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert, Sergey Svishchev
/***************************************************************************

        Robotron PC-1715

        10/06/2008 Preliminary driver.

    Notes:
    - keyboard connected to sio channel a
    - sio channel a clock output connected to ctc trigger 0

    Docs:
    - http://www.robotrontechnik.de/html/computer/pc1715w.htm
    - https://www.tiffe.de/Robotron/PC1715/ -- scanned PDFs
      https://www.tiffe.de/Robotron/PC1715/MANUAL_PC_1715.pdf
    - http://www.sax.de/~zander/pc1715/pc_bw.html -- typeset PDFs
      http://www.sax.de/~zander/pc1715/doku/pc_manu.pdf
    - http://xepb.org/robotron/

****************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/keyboard.h"
#include "machine/ram.h"
#include "machine/upd765.h"
#include "machine/z80ctc.h"
#include "machine/z80dma.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"
#include "video/i8275.h"

#include "emupal.h"
#include "screen.h"


#define LOG_BANK    (1U << 1)

#define VERBOSE (LOG_GENERAL)
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"

#define LOGBANK(format, ...)    LOGMASKED(LOG_BANK,   "%11.6f at %s: " format, machine().time().as_double(), machine().describe_context(), __VA_ARGS__)


namespace {

class rt1715_state : public driver_device
{
public:
	rt1715_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_sio0(*this, "sio0")
		, m_ctc0(*this, "ctc0")
		, m_printer(*this, "printer")
		, m_rs232(*this, "rs232")
		, m_fdc(*this, "i8272")
		, m_floppy(*this, "i8272:%u", 0U)
		, m_dma(*this, "z80dma")
		, m_ctc2(*this, "ctc2")
		, m_screen(*this, "screen")
		, m_palette(*this, "palette")
		, m_crtc(*this, "i8275")
		, m_p_chargen(*this, "gfx")
		, m_videoram(*this, "videoram")
		, m_p_cas(*this, "prom")
	{ }

	void rt1715(machine_config &config);
	void rt1715w(machine_config &config);

private:
	uint8_t memory_read_byte(offs_t offset);
	void memory_write_byte(offs_t offset, uint8_t data);
	uint8_t io_read_byte(offs_t offset);
	void io_write_byte(offs_t offset, uint8_t data);
	void tc_w(int state);
	void rt1715_floppy_enable(uint8_t data);
	uint8_t k7658_led1_r();
	uint8_t k7658_led2_r();
	uint8_t k7658_data_r(offs_t offset);
	void k7658_data_w(uint8_t data);
	void rt1715_rom_disable(uint8_t data);
	void rt1715w_set_bank(uint8_t data);
	void rt1715w_floppy_motor(uint8_t data);
	void rt1715w_krfd_w(uint8_t data);
	void rt1715_palette(palette_device &palette) const;
	I8275_DRAW_CHARACTER_MEMBER(crtc_display_pixels);
	void crtc_drq_w(int state);

	void k7658_io(address_map &map) ATTR_COLD;
	void k7658_mem(address_map &map) ATTR_COLD;
	void rt1715_base_io(address_map &map) ATTR_COLD;
	void rt1715_io(address_map &map) ATTR_COLD;
	void rt1715w_io(address_map &map) ATTR_COLD;
	void rt1715_mem(address_map &map) ATTR_COLD;
	void rt1715w_mem(address_map &map) ATTR_COLD;

	DECLARE_MACHINE_START(rt1715);
	DECLARE_MACHINE_RESET(rt1715);
	DECLARE_MACHINE_START(rt1715w);
	DECLARE_MACHINE_RESET(rt1715w);

	required_device<z80_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<z80sio_device> m_sio0;
	required_device<z80ctc_device> m_ctc0;
	required_device<rs232_port_device> m_printer;
	required_device<rs232_port_device> m_rs232;
	optional_device<i8272a_device> m_fdc;
	optional_device_array<floppy_connector, 2> m_floppy;
	optional_device<z80dma_device> m_dma;
	optional_device<z80ctc_device> m_ctc2;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<i8275_device> m_crtc;
	required_region_ptr<uint8_t> m_p_chargen;
	optional_device<ram_device> m_videoram;
	optional_region_ptr<uint8_t> m_p_cas;

	int m_led1_val = 0;
	int m_led2_val = 0;
	u8 m_krfd = 0U;
	uint16_t m_dma_adr = 0U;
	int m_r = 0, m_w = 0;
};


/***************************************************************************
    FLOPPY
***************************************************************************/

void rt1715_state::rt1715_floppy_enable(uint8_t data)
{
	LOG("%s: rt1715_floppy_enable %02x\n", machine().describe_context(), data);
}

void rt1715_state::rt1715w_floppy_motor(uint8_t data)
{
	LOG("%s: rt1715w_floppy_motor %02x\n", machine().describe_context(), data);

	if (m_floppy[0]->get_device()) m_floppy[0]->get_device()->mon_w(data & 0x80 ? 1 : 0);
	if (m_floppy[1]->get_device()) m_floppy[1]->get_device()->mon_w(data & 0x08 ? 1 : 0);
}

void rt1715_state::rt1715w_krfd_w(uint8_t data)
{
	LOG("%s: rt1715w_krfd_w %02x\n", machine().describe_context(), data);
	m_krfd = data;
}

void rt1715_state::tc_w(int state)
{
	m_fdc->tc_w(state & BIT(m_krfd, 7));
}

/***************************************************************************
    KEYBOARD
***************************************************************************/

/* si/so led */
uint8_t rt1715_state::k7658_led1_r()
{
	m_led1_val ^= 1;
	LOG("%s: k7658_led1_r %02x\n", machine().describe_context(), m_led1_val);
	return 0xff;
}

/* caps led */
uint8_t rt1715_state::k7658_led2_r()
{
	m_led2_val ^= 1;
	LOG("%s: k7658_led2_r %02x\n", machine().describe_context(), m_led2_val);
	return 0xff;
}

/* read key state */
uint8_t rt1715_state::k7658_data_r(offs_t offset)
{
	uint8_t result = 0xff;

	if (BIT(offset,  0)) result &= ioport("row_00")->read();
	if (BIT(offset,  1)) result &= ioport("row_10")->read();
	if (BIT(offset,  2)) result &= ioport("row_20")->read();
	if (BIT(offset,  3)) result &= ioport("row_30")->read();
	if (BIT(offset,  4)) result &= ioport("row_40")->read();
	if (BIT(offset,  5)) result &= ioport("row_50")->read();
	if (BIT(offset,  6)) result &= ioport("row_60")->read();
	if (BIT(offset,  7)) result &= ioport("row_70")->read();
	if (BIT(offset,  8)) result &= ioport("row_08")->read();
	if (BIT(offset,  9)) result &= ioport("row_18")->read();
	if (BIT(offset, 10)) result &= ioport("row_28")->read();
	if (BIT(offset, 11)) result &= ioport("row_38")->read();
	if (BIT(offset, 12)) result &= ioport("row_48")->read();

	return result;
}

/* serial output on D0 */
void rt1715_state::k7658_data_w(uint8_t data)
{
	LOG("%s: k7658_data_w %d\n", machine().describe_context(), BIT(data, 0));
	m_sio0->rxa_w(BIT(data, 0));
	m_sio0->rxca_w(0);
	m_sio0->rxca_w(1);
}


/***************************************************************************
    MEMORY HANDLING
***************************************************************************/

MACHINE_START_MEMBER(rt1715_state, rt1715)
{
	membank("bank2")->set_base(m_ram->pointer() + 0x0800);
	membank("bank3")->set_base(m_ram->pointer());
}

MACHINE_RESET_MEMBER(rt1715_state, rt1715)
{
	/* on reset, enable ROM */
	membank("bank1")->set_base(memregion("ipl")->base());
}

void rt1715_state::rt1715_rom_disable(uint8_t data)
{
	LOGBANK("%s: rt1715_set_bank %02x\n", machine().describe_context(), data);

	/* disable ROM, enable RAM */
	membank("bank1")->set_base(m_ram->pointer());
}

MACHINE_START_MEMBER(rt1715_state, rt1715w)
{
}

MACHINE_RESET_MEMBER(rt1715_state, rt1715w)
{
	m_dma->rdy_w(1);
	m_krfd = 0;
	m_dma_adr = 0;
	m_r = 0;
	m_w = 0;
}

/*
   BR (A62, A63)

   b2..0 = AB18..16

   0 - Hintergrundbank (Bildschirm, Zeichengeneratoren)
   1 - Systembank (gebanktes BIOS, BDOS)
   2 - Anwenderbank (TPA)
   3 - RAM-Disk
   4 - RAM-Disk
   5 - RAM-Disk
*/
void rt1715_state::rt1715w_set_bank(uint8_t data)
{
	int w = (data >> 4) & 7;
	int r = data & 7;

	LOGBANK("%s: rt1715w_set_bank target %x source %x%s\n", machine().describe_context(), w, r, r == w ? "" : " DIFF");

	m_r = r;
	m_w = w;
}

uint8_t rt1715_state::memory_read_byte(offs_t offset)
{
	uint8_t data = 0;

	switch (m_r)
	{
	case 0:
		switch (offset >> 12)
		{
		case 0:
			data = memregion("ipl")->base()[offset & 0x7ff];
			break;

		case 1:
			break;

		case 2:
			data = m_p_chargen[offset & 0xfff];
			break;

		case 3:
			data = m_videoram->pointer()[offset & 0xfff];
			break;

		default:
			data = m_ram->pointer()[offset];
			break;
		}
		LOGBANK("mem r %04x bank %d == %02x\n", offset, m_r, data);
		break;

	default:
		uint8_t cas_addr = 128 + (m_r << 4) + ((offset >> 12) & 15);
		uint8_t cas_data = m_p_cas[cas_addr] ^ 15;
		switch (cas_data)
		{
		case 1:
			data = m_ram->pointer()[offset];
			break;

		case 2:
			data = m_ram->pointer()[offset + 0x10000];
			break;

		case 4:
			data = m_ram->pointer()[offset + 0x20000];
			break;

		case 8:
			data = m_ram->pointer()[offset + 0x30000];
			break;

		default:
			break;
		}
		LOGBANK("mem r %04x bank %d cas %d(%02x) == %02x\n", offset, m_r, cas_data, cas_addr, data);
		break;
	}
	return data;
}

void rt1715_state::memory_write_byte(offs_t offset, uint8_t data)
{
	switch (m_w)
	{
	case 0:
		switch (offset >> 12)
		{
		case 0:
		case 1:
			break;

		case 2:
			m_p_chargen[offset & 0xfff] = data;
			break;

		case 3:
			m_videoram->pointer()[offset & 0xfff] = data;
			break;

		default:
			m_ram->pointer()[offset] = data;
			break;
		}
		LOGBANK("mem w %04x bank %d <- %02x\n", offset, m_w, data);
		break;

	default:
		uint8_t cas_addr = 128 + (m_w << 4) + ((offset >> 12) & 15);
		uint8_t cas_data = m_p_cas[cas_addr] ^ 15;
		switch (cas_data)
		{
		case 1:
			m_ram->pointer()[offset] = data;
			break;

		case 2:
			m_ram->pointer()[offset + 0x10000] = data;
			break;

		case 4:
			m_ram->pointer()[offset + 0x20000] = data;
			break;

		case 8:
			m_ram->pointer()[offset + 0x30000] = data;
			break;

		default:
			break;
		}
		LOGBANK("mem w %04x bank %d cas %d(%02x) <- %02x\n", offset, m_w, cas_data, cas_addr, data);
		break;
	}
}

uint8_t rt1715_state::io_read_byte(offs_t offset)
{
	address_space &prog_space = m_maincpu->space(AS_IO);
	return prog_space.read_byte(offset);
}

void rt1715_state::io_write_byte(offs_t offset, uint8_t data)
{
	address_space &prog_space = m_maincpu->space(AS_IO);
	prog_space.write_byte(offset, data);
}

/***************************************************************************
    VIDEO EMULATION
***************************************************************************/

void rt1715_state::crtc_drq_w(int state)
{
	if (state)
	{
		m_crtc->dack_w(m_videoram->pointer()[m_dma_adr++]);
		m_dma_adr %= (80 * 24);
	}
}

I8275_DRAW_CHARACTER_MEMBER(rt1715_state::crtc_display_pixels)
{
	using namespace i8275_attributes;

	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u8 gfx = BIT(attrcode, LTEN) ? 0xff : 0;

	if (!BIT(attrcode, VSP))
		gfx = m_p_chargen[(BIT(attrcode, GPA0) ? 0x800 : 0) | (linecount << 7) | charcode];

	if (BIT(attrcode, RVV))
		gfx ^= 0xff;

	bool hlgt = BIT(attrcode, HLGT);
	for (u8 i=0; i<8; i++)
		bitmap.pix(y, x + i) = palette[BIT(gfx, 7-i) ? (hlgt ? 2 : 1) : 0];
}

/* F4 Character Displayer */
static const gfx_layout rt1715_charlayout =
{
	8, 16,                  /* 8 x 16 characters */
	128,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*128, 1*128*8, 2*128*8, 3*128*8, 4*128*8, 5*128*8, 6*128*8, 7*128*8, 8*128*8, 9*128*8, 10*128*8, 11*128*8, 12*128*8, 13*128*8, 14*128*8, 15*128*8 },
	8                   /* every char takes 1 x 16 bytes */
};

static GFXDECODE_START( gfx_rt1715 )
	GFXDECODE_ENTRY("gfx", 0x0000, rt1715_charlayout, 0, 1)
	GFXDECODE_ENTRY("gfx", 0x0800, rt1715_charlayout, 0, 1)
GFXDECODE_END


/***************************************************************************
    PALETTE
***************************************************************************/

void rt1715_state::rt1715_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(0x00, 0x00, 0x00)); // black
	palette.set_pen_color(1, rgb_t(0x00, 0x7f, 0x00)); // low intensity
	palette.set_pen_color(2, rgb_t(0x00, 0xff, 0x00)); // high intensity
}


/***************************************************************************
    ADDRESS MAPS
***************************************************************************/

void rt1715_state::rt1715_mem(address_map &map)
{
	map(0x0000, 0x07ff).bankr("bank1").bankw("bank3");
	map(0x0800, 0xffff).bankrw("bank2");
}

void rt1715_state::rt1715_base_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x08, 0x0b).rw(m_ctc0, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x0c, 0x0f).rw(m_sio0, FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
	map(0x10, 0x17).noprw();
//  map(0x10, 0x13).rw(m_ctc1, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
//  map(0x14, 0x17).rw(m_sio1, FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
	map(0x18, 0x19).rw(m_crtc, FUNC(i8275_device::read), FUNC(i8275_device::write));
//  map(0x2c, 0x2f) // LT107CS -- serial DSR?
//  map(0x30, 0x33) // LT111CS -- serial SEL? (data rate selector)
}

void rt1715_state::rt1715_io(address_map &map)
{
	rt1715_base_io(map);

	map(0x00, 0x03).rw("a71", FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt)); // floppy data
	map(0x04, 0x07).rw("a72", FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt)); // floppy control/status
	map(0x20, 0x20).w(FUNC(rt1715_state::rt1715_floppy_enable));
//  map(0x24, 0x27).w(FUNC(rt1715_state::rt1715_rom_enable)); // MEMCS0
	map(0x28, 0x2b).w(FUNC(rt1715_state::rt1715_rom_disable)); // MEMCS1
//  map(0x34, 0x37) // BWSCS (read: memory start address, write: switch chargen)
}

void rt1715_state::rt1715w_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(rt1715_state::memory_read_byte), FUNC(rt1715_state::memory_write_byte));
}

// rt1715w -- decoders A13, A14, page C
void rt1715_state::rt1715w_io(address_map &map)
{
	rt1715_base_io(map);

	map(0x00, 0x00).rw(m_dma, FUNC(z80dma_device::read), FUNC(z80dma_device::write)); // A2
	map(0x04, 0x07).rw(m_ctc2, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write)); // A4
//  map(0x1a, 0x1b) // chargen write protection
	map(0x1c, 0x1d).m(m_fdc, FUNC(i8272a_device::map));
	map(0x20, 0x23).w(FUNC(rt1715_state::rt1715w_krfd_w)); // KRFD -- FD-Steuerregister (A45)
	map(0x24, 0x27).w(FUNC(rt1715_state::rt1715w_set_bank)); // BR (A62, A63)
	map(0x28, 0x2b).w(FUNC(rt1715_state::rt1715w_floppy_motor)); // MOS
	map(0x34, 0x37).portr("S8"); // KON -- Konfigurations-schalter FD (config switch -- A114, DIP S8)
//  map(0x38, 0x3b) // SR (RST1) -- Ru:cksetzen von Flip-Flops im FD
//  map(0x3c, 0x3f) // RST (RST2) -- Ru:cksetzen von Flip-Flops in V.24 (Pru:ftechnik)
	// these two ports are accessed only via DMA
	map(0x40, 0x40).r(m_fdc, FUNC(i8272a_device::msr_r));
	map(0x41, 0x41).rw(m_fdc, FUNC(i8272a_device::dma_r), FUNC(i8272a_device::dma_w));
}

void rt1715_state::k7658_mem(address_map &map)
{
	map(0x0000, 0x07ff).mirror(0xf800).rom();
}

void rt1715_state::k7658_io(address_map &map)
{
	map(0x0000, 0x1fff).w(FUNC(rt1715_state::k7658_data_w)).nopr();
	map(0x2000, 0x2000).mirror(0x8000).r(FUNC(rt1715_state::k7658_led1_r));
	map(0x4000, 0x4000).mirror(0x8000).r(FUNC(rt1715_state::k7658_led2_r));
	map(0x8000, 0x9fff).r(FUNC(rt1715_state::k7658_data_r));
}


/***************************************************************************
    INPUT PORTS
***************************************************************************/

static INPUT_PORTS_START( k7658 )
	PORT_START("row_00")
	// D04 A54 E04 B54 B04 C04 D54 C54
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad S *1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad S *3")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad CE") PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad -") PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))

	PORT_START("row_10")
	// D03 A53 E03 B53 B03 C03 D53 C53
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 3") PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 9") PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 6") PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD))

	PORT_START("row_20")
	// D02 A52 E02 B52 B02 C02 D52 C52
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 00") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 2") PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 8") PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 5") PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))

	PORT_START("row_30")
	// D11 --- E11 A15 A10 C11 --- A16
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad New-Line") PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR('\r')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))

	PORT_START("row_40")
	// D10 --- E10 --- B10 C10 E52 E51
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_START("row_50")
	// D12 --- E12 B16 B01 C12 D16 C16
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')

	PORT_START("row_60")
	// D07 A17 E07 B17 B07 C07 D17 C17
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("A17 8E")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("rechter Rand") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END))

	PORT_START("row_70")
	// D01 A51 E01 B51 B00 C01 D51 C51
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 0") PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 1") PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 7") PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad 4") PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))

	PORT_START("row_08")
	// D00 B99 E00 --- B11 C00 E15 E16
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SI/SO") PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(RALT))

	PORT_START("row_18")
	// D08 A56 E08 B56 B08 C08 D56 C56
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad SQ (F14)")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Keypad PS (F13)")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F12) PORT_CHAR(UCHAR_MAMEKEY(F12))

	PORT_START("row_28")
	// D09 E53 E09 E54 B09 C09 E55 E56
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10))

	PORT_START("row_38")
	// D05 A55 E05 B55 B05 C05 D55 C55
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))

	PORT_START("row_48")
	// D06 A05 E06 B15 B06 C06 D15 C15
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("linker Rand")
INPUT_PORTS_END

static INPUT_PORTS_START( rt1715w )
	PORT_INCLUDE(k7658)
	PORT_START("S8")
	PORT_DIPNAME( 0x01, 0x01, "Floppy drive type" )
	PORT_DIPSETTING(    0x01, "5.25\"-FD" )
	PORT_DIPSETTING(    0x00, "8\"-FD" )
	PORT_BIT( 0x7e, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

/* verify priority -- p. 14 of PC-1715-Servicemanual.pdf */
static const z80_daisy_config rt1715_daisy_chain[] =
{
	{ "a71" },
	{ "a72" },
	{ "ctc0" },
	{ "sio0" },
	{ nullptr }
};

static const z80_daisy_config rt1715w_daisy_chain[] =
{
	{ "sio0" },
	{ nullptr }
};

static void rt1715w_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

void rt1715_state::rt1715(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 9.832_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &rt1715_state::rt1715_mem);
	m_maincpu->set_addrmap(AS_IO, &rt1715_state::rt1715_io);
	m_maincpu->set_daisy_config(rt1715_daisy_chain);

	MCFG_MACHINE_START_OVERRIDE(rt1715_state, rt1715)
	MCFG_MACHINE_RESET_OVERRIDE(rt1715_state, rt1715)

	/* keyboard */
	z80_device &keyboard(Z80(config, "keyboard", 683000));
	keyboard.set_addrmap(AS_PROGRAM, &rt1715_state::k7658_mem);
	keyboard.set_addrmap(AS_IO, &rt1715_state::k7658_io);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_screen_update("i8275", FUNC(i8275_device::screen_update));
	m_screen->set_raw(13.824_MHz_XTAL, 864, 0, 624, 320, 0, 300); // ?

	GFXDECODE(config, "gfxdecode", "palette", gfx_rt1715);
	PALETTE(config, "palette", FUNC(rt1715_state::rt1715_palette), 3);

	I8275(config, m_crtc, 13.824_MHz_XTAL / 8);
	m_crtc->set_character_width(8);
	m_crtc->set_display_callback(FUNC(rt1715_state::crtc_display_pixels));
	m_crtc->set_screen(m_screen);

	Z80SIO(config, m_sio0, 9.832_MHz_XTAL / 4);
	m_sio0->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_sio0->out_txda_callback().set(m_printer, FUNC(rs232_port_device::write_txd));
	m_sio0->out_txdb_callback().set(m_rs232, FUNC(rs232_port_device::write_txd));
	m_sio0->out_dtrb_callback().set(m_rs232, FUNC(rs232_port_device::write_dtr));
	m_sio0->out_rtsb_callback().set(m_rs232, FUNC(rs232_port_device::write_rts));

	Z80CTC(config, m_ctc0, 15.9744_MHz_XTAL / 4);
	m_ctc0->zc_callback<0>().set(m_sio0, FUNC(z80sio_device::txca_w));
	m_ctc0->zc_callback<2>().set(m_sio0, FUNC(z80sio_device::rxtxcb_w));

	// X4 connector -- printer
	RS232_PORT(config, m_printer, default_rs232_devices, "printer");
	m_printer->cts_handler().set(m_sio0, FUNC(z80sio_device::ctsa_w));

	// X5 connector -- V24 port
	RS232_PORT(config, m_rs232, default_rs232_devices, "null_modem");
	m_rs232->rxd_handler().set(m_sio0, FUNC(z80sio_device::rxb_w));
	m_rs232->cts_handler().set(m_sio0, FUNC(z80sio_device::ctsb_w));
	m_rs232->dsr_handler().set(m_sio0, FUNC(z80sio_device::syncb_w));

	/* floppy */
	Z80PIO(config, "a71", 9.832_MHz_XTAL / 4);
	Z80PIO(config, "a72", 9.832_MHz_XTAL / 4);

	/* internal ram */
	RAM(config, m_ram).set_default_size("64K").set_default_value(0x00);
}

void rt1715_state::rt1715w(machine_config &config)
{
	rt1715(config);

	m_maincpu->set_clock(15.9744_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &rt1715_state::rt1715w_mem);
	m_maincpu->set_addrmap(AS_IO, &rt1715_state::rt1715w_io);
	m_maincpu->set_daisy_config(rt1715w_daisy_chain);
	m_maincpu->busack_cb().set(m_dma, FUNC(z80dma_device::bai_w));

	MCFG_MACHINE_START_OVERRIDE(rt1715_state, rt1715w)
	MCFG_MACHINE_RESET_OVERRIDE(rt1715_state, rt1715w)

	config.device_remove("a71");
	config.device_remove("a72");

	m_crtc->drq_wr_callback().set(FUNC(rt1715_state::crtc_drq_w));

	// operates in polled mode
	I8272A(config, m_fdc, 8'000'000 / 4, false);
	m_fdc->drq_wr_callback().set(m_dma, FUNC(z80dma_device::rdy_w)).invert();
	FLOPPY_CONNECTOR(config, "i8272:0", rt1715w_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "i8272:1", rt1715w_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);

	Z80DMA(config, m_dma, 15.9744_MHz_XTAL / 4);
	m_dma->out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	m_dma->out_int_callback().set(FUNC(rt1715_state::tc_w));
	m_dma->in_mreq_callback().set(FUNC(rt1715_state::memory_read_byte));
	m_dma->out_mreq_callback().set(FUNC(rt1715_state::memory_write_byte));
	m_dma->in_iorq_callback().set(FUNC(rt1715_state::io_read_byte));
	m_dma->out_iorq_callback().set(FUNC(rt1715_state::io_write_byte));

	Z80CTC(config, m_ctc2, 15.9744_MHz_XTAL / 4);

	m_ram->set_default_size("256K");
	RAM(config, m_videoram).set_default_size("4K").set_default_value(0x00);
}


/***************************************************************************
    ROM DEFINITIONS
***************************************************************************/

ROM_START( rt1715 )
	ROM_REGION(0x0800, "ipl", 0)
	ROM_LOAD("s500.a25.3", 0x0000, 0x0800, NO_DUMP) // CCITT 90e7
	ROM_LOAD("s501.a25.3", 0x0000, 0x0800, NO_DUMP) // CCITT 68da
	ROM_LOAD("s502.a25.3", 0x0000, 0x0800, CRC(7b6302e1) SHA1(e8f61763ff8841078a1939aa5e85a17f2af42163))

	ROM_REGION(0x1000, "gfx", 0)
	ROM_LOAD("s619.a25.2", 0x0000, 0x0800, CRC(98647763) SHA1(93fba51ed26392ec3eff1037886576fa12443fe5))
	ROM_LOAD("s602.a25.1", 0x0800, 0x0800, NO_DUMP) // CCITT fd67

	ROM_REGION(0x0800, "keyboard", 0)
	ROM_LOAD("s600.ic8", 0x0000, 0x0800, CRC(b7070122) SHA1(687056b822086ef0eee1e9b27e5b031bdbcade61))

	ROM_REGION(0x0800, "floppy", 0)
	ROM_LOAD("068.a8.2", 0x0000, 0x0400, CRC(5306d57b) SHA1(a12d025717b039a8a760eb9961365402f1f501f5)) // "read rom"
	ROM_LOAD("069.a8.1", 0x0400, 0x0400, CRC(319fa72c) SHA1(5f26af1e36339a934760a63e5975e9db09abeaaf)) // "write rom"
ROM_END

ROM_START( rt1715lc )
	ROM_REGION(0x0800, "ipl", 0)
	ROM_LOAD("s500.a25.3", 0x0000, 0x0800, NO_DUMP) // CCITT 90e7
	ROM_LOAD("s501.a25.3", 0x0000, 0x0800, NO_DUMP) // CCITT 68da
	ROM_LOAD("s502.a25.3", 0x0000, 0x0800, CRC(7b6302e1) SHA1(e8f61763ff8841078a1939aa5e85a17f2af42163))

	ROM_REGION(0x1000, "gfx", 0)
	ROM_LOAD("s643.a25.2", 0x0000, 0x0800, CRC(ea37f0e6) SHA1(357760974d944b9782734504b9820771e7e37645))
	ROM_LOAD("s605.a25.1", 0x0800, 0x0800, CRC(38062024) SHA1(798f62d4adeb7098b7dcbfe6caf28302853ee97d))

	ROM_REGION(0x0800, "keyboard", 0)
	ROM_LOAD("s642.ic8", 0x0000, 0x0800, NO_DUMP) // CCITT 962e

	ROM_REGION(0x0800, "floppy", 0)
	ROM_LOAD("068.a8.2", 0x0000, 0x0400, CRC(5306d57b) SHA1(a12d025717b039a8a760eb9961365402f1f501f5)) // "read rom"
	ROM_LOAD("069.a8.1", 0x0400, 0x0400, CRC(319fa72c) SHA1(5f26af1e36339a934760a63e5975e9db09abeaaf)) // "write rom"
ROM_END

ROM_START( rt1715w )
	ROM_REGION(0x0800, "ipl", 0)
	ROM_LOAD("s550.bin", 0x0000, 0x0800, CRC(0a96c754) SHA1(4d9ad5b877353d91ba355044d2847e1d621e2b01))

	// loaded from floppy on startup
	ROM_REGION(0x1000, "gfx", ROMREGION_ERASEFF)

	ROM_REGION(0x0800, "keyboard", 0)
	ROM_LOAD("s600.ic8", 0x0000, 0x0800, CRC(b7070122) SHA1(687056b822086ef0eee1e9b27e5b031bdbcade61))

	ROM_REGION(0x0100, "prom", 0)
	ROM_LOAD("287.bin", 0x0000, 0x0100, CRC(8508360c) SHA1(d262a8c3cf2d284c67f23b853e0d59ae5cc1d4c8)) // /CAS decoder prom, 74S287
ROM_END

} // anonymous namespace


/***************************************************************************
    GAME DRIVERS
***************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT  CLASS         INIT        COMPANY     FULLNAME                             FLAGS
COMP( 1986, rt1715,   0,      0,      rt1715,  k7658,   rt1715_state, empty_init, "Robotron", "Robotron PC-1715",                  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )
COMP( 1986, rt1715lc, rt1715, 0,      rt1715,  k7658,   rt1715_state, empty_init, "Robotron", "Robotron PC-1715 (latin/cyrillic)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )
COMP( 1986, rt1715w,  rt1715, 0,      rt1715w, rt1715w, rt1715_state, empty_init, "Robotron", "Robotron PC-1715W",                 MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



rtpc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * IBM RT PC.
 *
 * Sources:
 *   - IBM RT PC Hardware Technical Reference Volume I, 75X0232, March 1987
 *   - http://www.cs.cmu.edu/afs/andrew.cmu.edu/usr/shadow/www/ibmrt.html
 *   - http://ps-2.kev009.com/ohlandl/6152/rt_index.html
 *
 * TODO:
 *   - finish refactoring iocc
 *   - additional machine variants
 *   - configurable RAM size
 *   - shared interrupts
 */
/*
 * https://www-01.ibm.com/common/ssi/ShowDoc.wss?docURL=/common/ssi/rep_ca/6/897/ENUS186-006/index.html
 * https://www-01.ibm.com/common/ssi/ShowDoc.wss?docURL=/common/ssi/rep_ca/1/897/ENUS187-021/index.html
 * https://www-01.ibm.com/common/ssi/ShowDoc.wss?docURL=/common/ssi/rep_ca/0/897/ENUS188-120/index.html
 *
 *   Model  Chassis  CPU  RAM      HDD  Release   Price    Notes
 *    010    6151    032  1M/4M    40M  Jan 1986  $11,700
 *    015    6151    032  2M/4M    70M  Nov 1986  $10,050
 *    020    6150    032  1M/4M    40M  Jan 1986  $14,945
 *    025    6150    032  2M/4M    70M  Jan 1986  $17,940
 *    A25    6150    032  2M/4M    70M  Jan 1986  $19,510  5080 attachment/no keyboard
 *    115    6151    Adv  4M/16M   70M  Feb 1987  $10,600  AFPA
 *    125    6150    Adv  4M/16M   70M  Feb 1987  $16,100  AFPA
 *    B25    6150    Adv  4M/16M   70M  Feb 1987  $17,670  AFPA, 5080 attachment/no keyboard
 *    130    6151    Enh  16M     114M  Jul 1988  $23,220  EAFPA
 *    135    6150    Enh  16M     114M  Jul 1988  $30,595  EAFPA
 *    B35    6150    Enh  16M     114M  Jul 1988  $32,165  EAFPA, 5080 attachment/no keyboard
 *
 * 032 (aka SGP), 170ns (23.5294 MHz crystal / 4 == 5.882350 MHz == 170ns), 1MB/2MB/4MB memory boards
 * Advanced, 100ns 4MB (6151) external (6150) (presume ~40MHz crystal/4), 4MB/8MB memory boards
 * Enhanced, 80ns, 16MB soldered, EAFPA standard, CMOS (49.400 MHz crystal/4 == 12.350MHz == 80.971ns)
 *
 * FPA is NS32081
 * AFPA is M68881 @ 20MHz
 * EAFPA is AD90221-2 ADSP-3210 (multiplier) + AD90222-2 ADSP-3221 (fp alu) + AD90220-2 ADSP-1401 (program sequencer)
 *
 * system processor real memory address map
 *   0000'0000-00ff'ffff 16MB memory management unit
 *   0100'0000-efff'ffff -- not defined
 *
 * system board I/O addresses (all in segment F)
 *   f000'0000-f0ff'ffff 16MB i/o channel i/o map
 *   f100'0000-f3ff'ffff 48MB reserved
 *   f400'0000-f4ff'ffff 16MB i/o channel memory map
 *   f500'0000-f7ff'ffff reserved?
 *   f800'0000-fbff'ffff reserved
 *   fc00'0000-fdff'ffff mc68881
 *   fe00'0000-feff'ffff fpa2
 *   ff00'0000-ffff'ffff 16MB floating point accelerator
 *
 * system processor i/o address map
 *   00'0000-7f'ffff 8MB not defined
 *   80'0000-80'ffff 64KB processor channel device initialization
 *   81'0000-81'ffff 64KB memory management unit
 *   82'0000-ff'ffff 8064KB not defined
 *
 * advanced processor
 *   80'0400 i/o interface 1 i/o base
 *   80'0100 i/o interface 2 i/o base
 *
 */
 /*
  * https://ardent-tool.com/615x/rt_loadable_post.html
  *
  * WIP
  *  - boots to vrm install disk menu
  *  - requires improved hard disk controller emulation
  */

#include "emu.h"

#include "rosetta.h"
#include "rtpc_iocc.h"
#include "rtpc_kls.h"

// cpu and memory
#include "cpu/romp/romp.h"

// various hardware
#include "imagedev/floppy.h"
#include "machine/am9517a.h"
#include "machine/input_merger.h"
#include "machine/mc146818.h"
#include "machine/pic8259.h"
#include "machine/z80scc.h"

// busses and connectors
#include "bus/isa/isa.h"
#include "bus/isa/isa_cards.h"
#include "bus/isa/5080pa.h"
#include "bus/isa/amgda.h"
#include "bus/isa/ega.h"
#include "bus/isa/fdc.h"
#include "bus/isa/ide.h"
#include "bus/isa/mda.h"
#include "bus/isa/ubpnic.h"

#include "bus/rs232/rs232.h"

#include "rtpc.lh"

#define VERBOSE (LOG_GENERAL)
#include "logmacro.h"

namespace {

class rtpc_state : public driver_device
{
public:
	rtpc_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_mmu(*this, "mmu")
		, m_iocc(*this, "iocc")
		, m_dma(*this, "dma%u", 0U)
		, m_pic(*this, "pic%u", 0U)
		, m_kls(*this, "kls")
		, m_rtc(*this, "rtc")
		, m_scc(*this, "scc")
		, m_isa(*this, "isa")
		, m_slot(*this, "isa%u", 1U)
		, m_ipl(*this, "ipl")
	{
	}

	void ibm6150(machine_config &config);
	void ibm6151(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void device_reset() override ATTR_COLD;

	void iocc_mem_map(address_map &map);
	template <bool SCC> void iocc_pio_map(address_map &map) ATTR_COLD;

	void common(machine_config &config);

	void crra_w(u8 data);
	void crrb_w(u8 data);
	void dia_w(u8 data);

	// devices
	required_device<romp_device> m_cpu;
	required_device<rosetta_device> m_mmu;
	required_device<rtpc_iocc_device> m_iocc;

	required_device_array<am9517a_device, 2> m_dma;
	required_device_array<pic8259_device, 2> m_pic;
	required_device<rtpc_kls_device> m_kls;
	required_device<mc146818_device> m_rtc;
	optional_device<z80scc_device> m_scc;

	required_device<isa16_device> m_isa;
	optional_device_array<device_t, 8> m_slot;

	required_region_ptr<u32> m_ipl;

	u8 m_ch8er;  // dma channel 8 enable register
	u8 m_crra;   // component reset register a
	u8 m_crrb;   // component reset register b
	u8 m_ext[2]; // external serial registers
};

void rtpc_state::machine_start()
{
	m_ch8er = 0;
	m_crra = 0xff;
	m_crrb = 0xff;

	m_ext[0] = 0;
	m_ext[1] = 0;
}

void rtpc_state::device_reset()
{
	offs_t const mask = m_ipl.bytes() - 1;

	m_cpu->space(AS_PROGRAM).install_rom(0, mask, 0x00ff'ffff & ~mask, m_ipl);
}

void rtpc_state::iocc_mem_map(address_map &map)
{
	map.unmap_value_high();

	// HACK: temporarily silence noisy probing for absent hardware
	map(0x0b'0000, 0x0b'0fff).noprw();
	map(0x0b'8000, 0x0b'8fff).noprw();
	map(0xd2'0000, 0xd3'ffff).noprw();
	map(0xe0'0000, 0xe1'ffff).noprw();
	map(0xe8'0000, 0xe8'ffff).noprw();
	map(0xea'0000, 0xea'ffff).noprw();
}

template <bool SCC> void rtpc_state::iocc_pio_map(address_map &map)
{
	// HACK: temporarily silence noisy probing for absent hardware
	map(0x00'0110, 0x00'0113).noprw();
	map(0x00'0374, 0x00'0375).noprw();
	map(0x00'01e8, 0x00'01ef).noprw();

	// interrupt level enables (shared interrupts)
	map(0x00'02f0, 0x00'02f7).nopw(); // irq 0..7
	map(0x00'06f0, 0x00'06f7).nopw(); // irq 8..15 (excluding 13)

	if (SCC)
	{
		map(0x00'8000, 0x00'8003).rw(m_scc, FUNC(z80scc_device::dc_ab_r), FUNC(z80scc_device::dc_ab_w));
		map(0x00'8020, 0x00'8020).lrw8([this]() { return m_ext[0]; }, "ext_a_r", [this](u8 data) { m_ext[0] = data; }, "ext_a_w");
		map(0x00'8040, 0x00'8040).lrw8([this]() { return m_ext[1]; }, "ext_b_r", [this](u8 data) { m_ext[1] = data; }, "ext_b_w");
		map(0x00'8060, 0x00'8060).lr8([this]() { return u8(m_scc->m1_r()); }, "intack");
	}

	// delay 1µs per byte written
	map(0x00'80e0, 0x00'80e3).nopr(); // TODO: does read also generate a delay?
	map(0x00'80e0, 0x00'80e3).lw8([this](u8 data) { m_cpu->eat_cycles(m_cpu->clock() / 1000000 + 1); }, "io_delay");

	map(0x00'8400, 0x00'8407).m(m_kls, FUNC(rtpc_kls_device::map));

	map(0x00'8800, 0x00'883f).rw(m_rtc, FUNC(mc146818_device::read_direct), FUNC(mc146818_device::write_direct));
	map(0x00'8840, 0x00'884f).mirror(0x10).rw(m_dma[0], FUNC(am9517a_device::read), FUNC(am9517a_device::write));
	map(0x00'8860, 0x00'887f).umask16(0x00ff).rw(m_dma[1], FUNC(am9517a_device::read), FUNC(am9517a_device::write));
	map(0x00'8880, 0x00'8881).mirror(0x1e).rw(m_pic[0], FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x00'88a0, 0x00'88a1).mirror(0x1e).rw(m_pic[1], FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x00'88c0, 0x00'88c0).mirror(0x1f).rw(m_iocc, FUNC(rtpc_iocc_device::dbr_r), FUNC(rtpc_iocc_device::dbr_w));
	map(0x00'88e0, 0x00'88e0).mirror(0x1f).rw(m_iocc, FUNC(rtpc_iocc_device::dmr_r), FUNC(rtpc_iocc_device::dmr_w));

	map(0x00'8c00, 0x00'8c00).mirror(0x03).lrw8([this]() { return m_ch8er; }, "ch8er_r", [this](u8 data) { m_ch8er = data; }, "ch8er_w");
	map(0x00'8c20, 0x00'8c20).mirror(0x03).rw(m_iocc, FUNC(rtpc_iocc_device::ccr_r), FUNC(rtpc_iocc_device::ccr_w));
	map(0x00'8c40, 0x00'8c40).mirror(0x03).lr8([this]() { return m_crra; }, "crra_r");
	map(0x00'8c40, 0x00'8c40).mirror(0x03).w(FUNC(rtpc_state::crra_w));
	map(0x00'8c60, 0x00'8c60).mirror(0x03).lr8([this]() { return m_crrb; }, "crrb_r");
	map(0x00'8c60, 0x00'8c60).mirror(0x03).w(FUNC(rtpc_state::crrb_w));

	// memory config reg (cc=2x8M, dd=2x2M, 88=2x4M)
	map(0x00'8c80, 0x00'8c80).mirror(0x03).lr8([]() { return 0xf8; }, "mcr");
	map(0x00'8ca0, 0x00'8ca0).mirror(0x03).w(FUNC(rtpc_state::dia_w));

	// 8c82 diag dma mode?
	// 8c84 diag dma exit?
	map(0x00'8ce0, 0x00'8ce1).nopw(); // FIXME: hex display register?

	map(0x01'1000, 0x01'20ff).noprw(); // silence something
}

void rtpc_state::crra_w(u8 data)
{
	LOG("crra_w 0x%02x (%s)\n", data, machine().describe_context());

	// selectively reset i/o slots 1..8
	for (unsigned i = 0; i < 8; i++)
	{
		if (BIT(data, i) && !BIT(m_crra, i) && m_slot[i].found())
		{
			LOG("reset slot %u\n", i);
			m_slot[i]->reset();
		}
	}

	m_crra = data;
}

void rtpc_state::crrb_w(u8 data)
{
	LOG("crrb_w 0x%02x (%s)\n", data, machine().describe_context());

	// bit  function
	//  0   8530
	//  1   rs232 interface
	//  2   8051
	//  3   dmac1
	//  4   dmac2
	//  5   arbitor
	//  6   reserved
	//  7   reserved

	if (m_scc && BIT(data, 0))
		m_scc->reset();

	// TODO: rs232 if

	m_kls->mcu_reset_w(!BIT(data, 2));

	// TODO: dmac !ready
	// TODO: arbitor

	m_crrb = data;
}

void rtpc_state::dia_w(u8 data)
{
	bool const state = BIT(data, 0);

	LOG("dia_w 0x%02x (%s)\n", data, machine().describe_context());

	m_pic[0]->ir0_w(state);
	m_pic[0]->ir1_w(state);
	m_pic[0]->ir2_w(state);
	m_pic[0]->ir3_w(state);
	m_pic[0]->ir4_w(state);
	m_pic[0]->ir5_w(state);
	m_pic[0]->ir6_w(state);
	m_pic[0]->ir7_w(state);

	m_pic[1]->ir0_w(state);
	m_pic[1]->ir1_w(state);
	m_pic[1]->ir2_w(state);
	m_pic[1]->ir3_w(state);
	m_pic[1]->ir4_w(state);
	m_pic[1]->ir5_w(state);
	m_pic[1]->ir6_w(state);
	m_pic[1]->ir7_w(state);
}

void rtpc_state::common(machine_config &config)
{
	/*
	 * irq  source
	 *  0   mcu system attention
	 *  1   rtc interrupt
	 *  2   mmu program check, iocc error
	 *  3   pic 0 interrupt
	 *  4   pic 1 interrupt
	 *  5   (not connected, software interrupt?)
	 *  6   (not connected, software interrupt?)
	 * nmi  mmu machine check, early power off
	 */
	ROMP(config, m_cpu, 23'529'400 / 4);
	m_cpu->set_mmu(m_mmu);
	m_cpu->set_iou(m_iocc);

	input_merger_device &reqi2(INPUT_MERGER_ANY_LOW(config, "reqi2"));
	reqi2.output_handler().set_inputline(m_cpu, INPUT_LINE_IRQ2).invert();

	// TODO: hole for 1M/4M memory boards
	// 2/2 rams 4M
	// 2/1 rams 4M
	// 1/1 rams 4M hole 1M
	// 4/0 rams 4M
	// 4/4 rams 16M hole 4M
	ROSETTA(config, m_mmu, m_cpu->clock(), rosetta_device::RAM_4M);
	m_mmu->set_mem(m_cpu, AS_PROGRAM);
	m_mmu->set_rom("ipl");
	m_mmu->out_pchk().set(reqi2, FUNC(input_merger_device::in_w<0>));
	m_mmu->out_mchk().set_inputline(m_cpu, INPUT_LINE_NMI);

	RTPC_IOCC(config, m_iocc, 0);
	m_iocc->set_addrmap(0, &rtpc_state::iocc_mem_map);
	m_iocc->out_int().set(reqi2, FUNC(input_merger_device::in_w<1>));
	m_iocc->out_rst().set_inputline(m_dma[0], INPUT_LINE_RESET);
	m_iocc->out_rst().append_inputline(m_dma[1], INPUT_LINE_RESET);

	// ISA bus
	ISA16(config, m_isa, 14'318'180 / 3);
	m_isa->set_memspace(m_iocc, AS_PROGRAM);
	m_isa->set_iospace(m_iocc, AS_IO);
	//m_isa->iochck_callback().set(FUNC(at_mb_device::iochck_w));

	// NEC
	// D8237AC-5
	// 8903HV101
	AM9517A(config, m_dma[0], m_isa->clock());

	// dma0 channel 0 == isa channel 2 (diskette drive, serial port A)
	m_isa->drq2_callback().set(m_dma[0], FUNC(am9517a_device::dreq_w<0>));
	m_dma[0]->in_ior_callback<0>().set([this]() { return m_isa->dack_r(2); });
	m_dma[0]->out_iow_callback<0>().set([this](u8 data) { m_isa->dack_w(2, data); });
	m_dma[0]->out_dack_callback<0>().set(m_iocc, FUNC(rtpc_iocc_device::dack_w<2>));

	// dma0 channel 1 == isa channel 1 (serial port B)
	m_isa->drq1_callback().set(m_dma[0], FUNC(am9517a_device::dreq_w<1>));
	m_dma[0]->in_ior_callback<1>().set([this]() { return m_isa->dack_r(1); });
	m_dma[0]->out_iow_callback<1>().set([this](u8 data) { m_isa->dack_w(1, data); });
	m_dma[0]->out_dack_callback<1>().set(m_iocc, FUNC(rtpc_iocc_device::dack_w<1>));

	// dma0 channel 2 == isa channel 0 (serial port A)
	m_isa->drq0_callback().set(m_dma[0], FUNC(am9517a_device::dreq_w<2>));
	m_dma[0]->in_ior_callback<2>().set([this]() { return m_isa->dack_r(0); });
	m_dma[0]->out_iow_callback<2>().set([this](u8 data) { m_isa->dack_w(0, data); });
	m_dma[0]->out_dack_callback<2>().set(m_iocc, FUNC(rtpc_iocc_device::dack_w<0>));

	// dma0 channel 3 == isa channel 3 (pc network, serial port B)
	m_isa->drq3_callback().set(m_dma[0], FUNC(am9517a_device::dreq_w<3>));
	m_dma[0]->in_ior_callback<3>().set([this]() { return m_isa->dack_r(3); });
	m_dma[0]->out_iow_callback<3>().set([this](u8 data) { m_isa->dack_w(3, data); });
	m_dma[0]->out_dack_callback<3>().set(m_iocc, FUNC(rtpc_iocc_device::dack_w<3>));

	m_dma[0]->out_hreq_callback().set(m_dma[0], FUNC(am9517a_device::hack_w));
	m_dma[0]->in_memr_callback().set(m_iocc, FUNC(rtpc_iocc_device::dma_b_r));
	m_dma[0]->out_memw_callback().set(m_iocc, FUNC(rtpc_iocc_device::dma_b_w));
	m_dma[0]->out_eop_callback().set(m_pic[0], FUNC(pic8259_device::ir0_w));

	// FIXME: eop should be asserted on the bus and tested by the device
	// when transferring data, not routed to a specific card like this
	m_dma[0]->out_eop_callback().append([this](int state) { m_isa->eop_w(m_iocc->adc_r(), state); });

	// NEC
	// D8237AC-5
	// 8903HV101
	AM9517A(config, m_dma[1], m_isa->clock());

	// dma1 channel 0 == coprocessor channel 8?
	m_dma[1]->out_dack_callback<0>().set(m_iocc, FUNC(rtpc_iocc_device::dack_w<4>));

	// dma1 channel 1 == isa channel 5
	m_isa->drq5_callback().set(m_dma[1], FUNC(am9517a_device::dreq_w<1>));
	m_dma[1]->out_dack_callback<1>().set(m_iocc, FUNC(rtpc_iocc_device::dack_w<5>));

	// dma1 channel 2 == isa channel 6
	m_isa->drq6_callback().set(m_dma[1], FUNC(am9517a_device::dreq_w<2>));
	m_dma[1]->out_dack_callback<2>().set(m_iocc, FUNC(rtpc_iocc_device::dack_w<6>));

	// dma1 channel 3 == isa channel 7
	m_isa->drq7_callback().set(m_dma[1], FUNC(am9517a_device::dreq_w<3>));
	m_dma[1]->out_dack_callback<3>().set(m_iocc, FUNC(rtpc_iocc_device::dack_w<7>));

	m_dma[1]->out_hreq_callback().set(m_dma[1], FUNC(am9517a_device::hack_w));
	m_dma[1]->in_memr_callback().set(m_iocc, FUNC(rtpc_iocc_device::dma_w_r));
	m_dma[1]->out_memw_callback().set(m_iocc, FUNC(rtpc_iocc_device::dma_w_w));


	// TODO: system board irq 8, 13

	// NEC
	// D8259AC
	PIC8259(config, m_pic[0]);
	m_pic[0]->out_int_callback().set_inputline(m_cpu, INPUT_LINE_IRQ3).invert();
	// irq  source
	//  0   iocc irq 0: dma tc
	//  1   iocc irq 10: (multiport async)
	//  2   iocc irq 9: pc network, (multiport async), enhanced color graphics adapter, 3278/79 emulation adapter
	//  3   iocc irq 3: serial port 2, pc network
	//  4   iocc irq 4: serial port 1
	//  5   iocc irq 1: kbd
	//  6   iocc irq 2: 8530
	//  7   iocc irq 7: parallel port 1, monochrome/printer
	m_isa->irq10_callback().set(m_pic[0], FUNC(pic8259_device::ir1_w));
	m_isa->irq2_callback().set(m_pic[0], FUNC(pic8259_device::ir2_w));
	m_isa->irq3_callback().set(m_pic[0], FUNC(pic8259_device::ir3_w));
	m_isa->irq4_callback().set(m_pic[0], FUNC(pic8259_device::ir4_w));
	m_isa->irq7_callback().set(m_pic[0], FUNC(pic8259_device::ir7_w));

	// NEC
	// D8259AC
	PIC8259(config, m_pic[1]);
	m_pic[1]->out_int_callback().set_inputline(m_cpu, INPUT_LINE_IRQ4).invert();
	// irq  source
	//  0   iocc irq 8: reserved
	//  1   iocc irq 11: (advanced monochrome graphics display) (multiport async)
	//  2   iocc irq 14: fixed disk
	//  3   iocc irq 12: (ibm rt pc streaming tape drive adapter)
	//  4   iocc irq 6: diskette drive
	//  5   iocc irq 5: parallel port 2
	//  6   iocc irq 15: (286 coprocessor)
	//  7   iocc irq 13: serial port ex ct1 irpt
	m_isa->irq11_callback().set(m_pic[1], FUNC(pic8259_device::ir1_w));
	m_isa->irq14_callback().set(m_pic[1], FUNC(pic8259_device::ir2_w));
	m_isa->irq12_callback().set(m_pic[1], FUNC(pic8259_device::ir3_w));
	m_isa->irq6_callback().set(m_pic[1], FUNC(pic8259_device::ir4_w));
	m_isa->irq5_callback().set(m_pic[1], FUNC(pic8259_device::ir5_w));
	m_isa->irq15_callback().set(m_pic[1], FUNC(pic8259_device::ir6_w));

	RTPC_KLS(config, m_kls);
	m_kls->atn().set_inputline(m_cpu, INPUT_LINE_IRQ0);
	m_kls->irq().set(m_pic[0], FUNC(pic8259_device::ir5_w));

	// MC146818AP
	// IL 0A46D8729
	MC146818(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->sqw().set(m_cpu, FUNC(romp_device::clk_w));
	m_rtc->sqw().append(m_kls, FUNC(rtpc_kls_device::rtc_sqw_w));
	m_rtc->irq().set_inputline(m_cpu, INPUT_LINE_IRQ1).invert();
	m_rtc->irq().append(m_kls, FUNC(rtpc_kls_device::rtc_irq_w)).invert();

	config.set_default_layout(layout_rtpc);
}

void rtpc_isa8_cards(device_slot_interface &device)
{
	device.option_add("baseband", ISA8_UBPNIC);
	device.option_add("ega", ISA8_EGA);
	device.option_add("fdc", ISA8_FDC_AT);
	device.option_add("mda", ISA8_MDA);
}

void rtpc_isa16_cards(device_slot_interface &device)
{
	device.option_add("5080pa", ISA16_5080PA);
	device.option_add("amgda", ISA16_AMGDA);
	device.option_add("ide", ISA16_IDE);

	rtpc_isa8_cards(device);
}

void rtpc_state::ibm6150(machine_config &config)
{
	common(config);
	m_iocc->set_addrmap(2, &rtpc_state::iocc_pio_map<true>);

	SCC8530(config, m_scc, 3'580'000);
	m_scc->configure_channels(3'072'000, 3'072'000, 3'072'000, 3'072'000);
	m_scc->out_int_callback().set(m_pic[0], FUNC(pic8259_device::ir6_w));

	// port: TXD, DTR, RTS, RI, RXD, DSR, CTS, DCD
	// external registers: DTR, DSR, RI
	// scc: TXD, RTS, RXD, CTS, DCD
	// scc: DTR/REQ, SYNC not connected

	rs232_port_device &port0(RS232_PORT(config, "serial0", default_rs232_devices, nullptr));
	port0.cts_handler().set(m_scc, FUNC(z80scc_device::ctsa_w));
	port0.dcd_handler().set(m_scc, FUNC(z80scc_device::dcda_w));
	port0.rxd_handler().set(m_scc, FUNC(z80scc_device::rxa_w));
	m_scc->out_rtsa_callback().set(port0, FUNC(rs232_port_device::write_rts));
	m_scc->out_txda_callback().set(port0, FUNC(rs232_port_device::write_txd));

	rs232_port_device &port1(RS232_PORT(config, "serial1", default_rs232_devices, nullptr));
	// TXD, DTR, RTS, RI, RXD, DSR, CTS, DCD
	port1.cts_handler().set(m_scc, FUNC(z80scc_device::ctsb_w));
	port1.dcd_handler().set(m_scc, FUNC(z80scc_device::dcdb_w));
	port1.rxd_handler().set(m_scc, FUNC(z80scc_device::rxb_w));
	m_scc->out_rtsb_callback().set(port1, FUNC(rs232_port_device::write_rts));
	m_scc->out_txdb_callback().set(port1, FUNC(rs232_port_device::write_txd));

	// ISA slots
	ISA16_SLOT(config, m_slot[0], 0, m_isa, rtpc_isa16_cards, "fdc",      false); // slot 1: disk/diskette adapter
	ISA16_SLOT(config, m_slot[1], 0, m_isa, rtpc_isa16_cards, nullptr,    false); // slot 2: option
	ISA8_SLOT(config,  m_slot[2], 0, m_isa, rtpc_isa8_cards,  "mda",      false); // slot 3: option
	ISA16_SLOT(config, m_slot[3], 0, m_isa, rtpc_isa16_cards, nullptr,    false); // slot 4: option
	ISA16_SLOT(config, m_slot[4], 0, m_isa, rtpc_isa16_cards, nullptr,    false); // slot 5: option
	ISA8_SLOT(config,  m_slot[5], 0, m_isa, rtpc_isa8_cards,  "baseband", false); // slot 6: option
	ISA16_SLOT(config, m_slot[6], 0, m_isa, rtpc_isa16_cards, nullptr,    false); // slot 7: option
	ISA16_SLOT(config, m_slot[7], 0, m_isa, rtpc_isa16_cards, nullptr,    false); // slot 8: coprocessor/option
}

void rtpc_state::ibm6151(machine_config &config)
{
	common(config);
	m_iocc->set_addrmap(2, &rtpc_state::iocc_pio_map<false>);

	// ISA slots
	ISA8_SLOT(config,  m_slot[0], 0, m_isa, rtpc_isa8_cards,  "mda",      false); // slot 1: option
	ISA16_SLOT(config, m_slot[1], 0, m_isa, rtpc_isa16_cards, "baseband", false); // slot 2: option
	ISA16_SLOT(config, m_slot[2], 0, m_isa, rtpc_isa16_cards, nullptr,    false); // slot 2: option
	ISA16_SLOT(config, m_slot[3], 0, m_isa, rtpc_isa16_cards, nullptr,    false); // slot 4: option
	ISA16_SLOT(config, m_slot[4], 0, m_isa, rtpc_isa16_cards, nullptr,    false); // slot 5: coprocessor/option
	ISA16_SLOT(config, m_slot[5], 0, m_isa, rtpc_isa16_cards, "fdc",      false); // slot 6: disk/diskette adapter
}

ROM_START(ibm6150)
	ROM_REGION32_BE(0x10000, "ipl", 0)
	ROM_SYSTEM_BIOS(0, "ipl", "IPL")
	ROMX_LOAD("79x3456.bin", 0x00000, 0x4000, CRC(0a45a9ba) SHA1(02ca637c6a871c180dbfebf2ec68d8ec5a998c76), ROM_BIOS(0) | ROM_SKIP(3))
	ROMX_LOAD("79x3458.bin", 0x00001, 0x4000, CRC(7bd08ab6) SHA1(aabcfbb8fa1a5f8a08fb5cfd90ca6fe05258fde9), ROM_BIOS(0) | ROM_SKIP(3))
	ROMX_LOAD("79x3460.bin", 0x00002, 0x4000, CRC(897586e0) SHA1(528772635903f27235ebba2622b03386b84e4e17), ROM_BIOS(0) | ROM_SKIP(3))
	ROMX_LOAD("79x3462.bin", 0x00003, 0x4000, CRC(12aca906) SHA1(58f95b95768ef131d8d9d552506a9fe9c9c6077d), ROM_BIOS(0) | ROM_SKIP(3))
ROM_END

ROM_START(ibm6151)
	ROM_REGION32_BE(0x10000, "ipl", 0)
	ROM_SYSTEM_BIOS(0, "ipl", "IPL")
	ROMX_LOAD("79x3456.bin", 0x00000, 0x4000, CRC(0a45a9ba) SHA1(02ca637c6a871c180dbfebf2ec68d8ec5a998c76), ROM_BIOS(0) | ROM_SKIP(3))
	ROMX_LOAD("79x3458.bin", 0x00001, 0x4000, CRC(7bd08ab6) SHA1(aabcfbb8fa1a5f8a08fb5cfd90ca6fe05258fde9), ROM_BIOS(0) | ROM_SKIP(3))
	ROMX_LOAD("79x3460.bin", 0x00002, 0x4000, CRC(897586e0) SHA1(528772635903f27235ebba2622b03386b84e4e17), ROM_BIOS(0) | ROM_SKIP(3))
	ROMX_LOAD("79x3462.bin", 0x00003, 0x4000, CRC(12aca906) SHA1(58f95b95768ef131d8d9d552506a9fe9c9c6077d), ROM_BIOS(0) | ROM_SKIP(3))
ROM_END

#define rom_rtpc010 rom_ibm6151
#define rom_rtpc015 rom_ibm6151
#define rom_rtpc020 rom_ibm6150
#define rom_rtpc025 rom_ibm6150
#define rom_rtpca25 rom_ibm6150

} // anonymous namespace

/*   YEAR   NAME      PARENT  COMPAT  MACHINE   INPUT  CLASS        INIT         COMPANY                             FULLNAME               FLAGS */
COMP(1986,  rtpc010,  0,      0,      ibm6151,  0,     rtpc_state,  empty_init,  "International Business Machines",  "IBM RT PC Model 010", MACHINE_NOT_WORKING)
COMP(1986,  rtpc015,  0,      0,      ibm6151,  0,     rtpc_state,  empty_init,  "International Business Machines",  "IBM RT PC Model 015", MACHINE_NOT_WORKING)
COMP(1986,  rtpc020,  0,      0,      ibm6150,  0,     rtpc_state,  empty_init,  "International Business Machines",  "IBM RT PC Model 020", MACHINE_NOT_WORKING)
COMP(1986,  rtpc025,  0,      0,      ibm6150,  0,     rtpc_state,  empty_init,  "International Business Machines",  "IBM RT PC Model 025", MACHINE_NOT_WORKING)
COMP(1986,  rtpca25,  0,      0,      ibm6150,  0,     rtpc_state,  empty_init,  "International Business Machines",  "IBM RT PC Model A25", MACHINE_NOT_WORKING)



rvoice.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Jonathan Gevaryahu, Kevin Horton
/******************************************************************************
*
*  Bare bones Realvoice PC driver
*  By Jonathan Gevaryahu AKA Lord Nightmare
*  Binary supplied by Kevin 'kevtris' Horton
*

******************************************************************************/

/* Core includes */
#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/m6800/m6801.h"
#include "machine/clock.h"
#include "machine/mos6551.h"

/* Components */


namespace {

class rvoice_state : public driver_device
{
public:
	rvoice_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void rvoicepc(machine_config &config);

	void init_rvoicepc();

private:
	required_device<hd6301y_cpu_device> m_maincpu;
	void hd63701_main_mem(address_map &map) ATTR_COLD;
};


/* Devices */

void rvoice_state::init_rvoicepc()
{
}


/******************************************************************************
 Address Maps
******************************************************************************/

void rvoice_state::hd63701_main_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x001e, 0x001e).nopr(); // FIXME: TRCSR2 needs implementation
	map(0x0034, 0x0037).rw("acia65c51", FUNC(mos6551_device::read), FUNC(mos6551_device::write)); // ACIA 65C51
	map(0x2000, 0x7fff).ram(); // EXTERNAL SRAM
	map(0x8000, 0xffff).rom(); // 27512 EPROM
}


/******************************************************************************
 Input Ports
******************************************************************************/
static INPUT_PORTS_START( rvoicepc )
	PORT_START("BAUD")
	PORT_DIPNAME(0x06, 0x06, "Baud Rate")
	PORT_DIPSETTING(0x00, "1200")
	PORT_DIPSETTING(0x02, "2400")
	PORT_DIPSETTING(0x04, "4800")
	PORT_DIPSETTING(0x06, "9600")
	PORT_DIPNAME(0x20, 0x00, "Data Bits")
	PORT_DIPSETTING(0x20, "7")
	PORT_DIPSETTING(0x00, "8")
	PORT_DIPNAME(0x80, 0x00, "Stop Bits")
	PORT_DIPSETTING(0x00, "1")
	PORT_DIPSETTING(0x80, "2")
	PORT_BIT(0x59, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("PARITY")
	PORT_DIPNAME(0x60, 0x00, "Parity")
	PORT_DIPSETTING(0x00, "None")
	PORT_DIPSETTING(0x20, "Odd")
	PORT_DIPSETTING(0x60, "Even")
	PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x90, IP_ACTIVE_HIGH, IPT_UNUSED) // output pins
INPUT_PORTS_END

/******************************************************************************
 Machine Drivers
******************************************************************************/

void rvoice_state::rvoicepc(machine_config &config)
{
	/* basic machine hardware */
	HD6303Y(config, m_maincpu, XTAL(7'372'800));
	m_maincpu->set_addrmap(AS_PROGRAM, &rvoice_state::hd63701_main_mem);
	m_maincpu->in_p5_cb().set_ioport("PARITY");
	m_maincpu->in_p6_cb().set_ioport("BAUD");

	//hd63701_cpu_device &playercpu(HD63701(config "playercpu", XTAL(7'372'800))); // not dumped yet
	//playercpu.set_addrmap(AS_PROGRAM, &rvoice_state::hd63701_slave_mem);
	//playercpu.set_addrmap(AS_PROGRAM, &rvoice_state::hd63701_slave_io);
	config.set_maximum_quantum(attotime::from_hz(60));

	mos6551_device &acia(MOS6551(config, "acia65c51", 0));
	acia.set_xtal(1.8432_MHz_XTAL);
	acia.irq_handler().set_inputline(m_maincpu, HD6301_IRQ1_LINE);
	acia.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	acia.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	// HACK: some signal needs to be connected to Tin
	CLOCK(config, "testclock", 60).signal_handler().set_inputline(m_maincpu, M6801_TIN_LINE);

	/* video hardware */

	/* sound hardware */

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("acia65c51", FUNC(mos6551_device::write_rxd));
	rs232.dcd_handler().set("acia65c51", FUNC(mos6551_device::write_dcd));
	rs232.dsr_handler().set("acia65c51", FUNC(mos6551_device::write_dsr));
	rs232.cts_handler().set("acia65c51", FUNC(mos6551_device::write_cts));
}



/******************************************************************************
 ROM Definitions
******************************************************************************/

ROM_START(rvoicepc)

	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD("rv_pc.bin", 0x08000, 0x08000, CRC(4001cd5f) SHA1(d973c6e19e493eedd4f7216bc530ddb0b6c4921e))
	ROM_CONTINUE(0x8000, 0x8000) // first half of 27c512 rom is blank due to stupid address decoder circuit

ROM_END

} // anonymous namespace


/******************************************************************************
 Drivers
******************************************************************************/

//    YEAR  NAME       PARENT  COMPAT  MACHINE   INPUT     CLASS         INIT           COMPANY                           FULLNAME        FLAGS
COMP( 1988?, rvoicepc, 0,      0,      rvoicepc, rvoicepc, rvoice_state, init_rvoicepc, "Adaptive Communication Systems", "Realvoice PC", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



rx78.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese, Robbbert
/************************************************************************************************************

Gundam RX-78 (c) 1983 Bandai

TODO:
- implement printer;
- Implement 2nd cart slot
- Keyboard works in all scenarios, but it is a guess.
- RAM handling can't be right: PCB has 30KB shared RAM (manual also says this in the technical specs),
  but MAME allocates much more
- Find out what port F3 does - read and write - used by many games
- Find out what ports 23 and EF do - used by rengo

Notes:
- BS-BASIC v1.0 notes:
  -- COLOR x doesn't do anything. It sets a memory location with x, but does nothing with it.
  -- COLOR x,y where y sets the background colour. This part works.
  -- When BASIC is first started, it sets the colours but doesn't save the information. So when
     COLOR x is entered, although x has no effect, it also sets the background colour, which not
     having been set, sets the background black.
  -- At the first scroll, the display memory is disrupted in the logo area, probably another
     btanb. After that, scrolling works correctly.
  -- Need a real machine to confirm these problems, but if true, one can only wonder how such
     obvious issues made it out the door.
- To stop a cmt load, press STOP + SHIFT keys (this combination is the BREAK key).

For a list of all known software and devices for the system, please see hash/rx78.xml.

==============================================================================================================
Summary of Monitor commands.
- The monitor is entered at bootup. The prompt is the * character. This is followed by a command
  letter (upper case). Some commands require hex parameters. You must enter all 4 characters of
  these. No spaces allowed except where shown.
- While in BASIC, you may enter the monitor by using the MON command. After you have finished,
  you can return to BASIC by entering the command *J2005.

- Tape commands:
*L Load a tape
*V Verify a tape
*S Save a block of memory to tape. You are asked for a filename (blank is allowed), the start address,
   the end address, and the Jump address (where it should begin execution)

- Memory commands:
*Dnnnn nnnn Displays a hex dump in the address range entered
*Mnnnn      Allows you to examine and modify memory. Enter to skip to next, period (.) to quit.
*Jnnnn      Transfer execution (Jump) to a program in memory at the specified address

- Other:
*R          This is a block transfer load from a mystery parallel device, using ports E0 and E1,
            using handshaking similar to a centronics printer. The incoming file is loaded into
            memory and it appears that the operator is not provided any information of what happened.

==============================================================================================================

Known issues:
- Sekigahara: Possible joystick problem (need to be checked again)
- Need more software to test with.
BTANB:
- ProWrestling: When player 1 jumps at player 2 and misses, he always lands behind player 2.

*************************************************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "sound/sn76496.h"
#include "imagedev/cassette.h"
#include "machine/ram.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class rx78_state : public driver_device
{
public:
	rx78_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cass(*this, "cassette")
		, m_cart(*this, "cartslot")
		, m_ram(*this, RAM_TAG)
		, m_palette(*this, "palette")
		, m_io_keyboard(*this, "X%u", 0U)
	{ }

	void init_rx78();
	void rx78(machine_config &config);

private:
	u8 key_r();
	u8 cass_r();
	u8 vram_r(offs_t offset);
	void cass_w(u8 data);
	void vram_w(offs_t offset, u8 data);
	void vram_read_bank_w(u8 data);
	void vram_write_bank_w(u8 data);
	void key_w(u8 data);
	void vdp_reg_w(offs_t offset, u8 data);
	void vdp_bg_reg_w(u8 data);
	void vdp_pri_mask_w(u8 data);
	void portf3_w(u8 data);
	void create_palette(palette_device &palette);
	INTERRUPT_GEN_MEMBER(interrupt);
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER( cart_load );
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	void rx78_io(address_map &map) ATTR_COLD;
	void rx78_mem(address_map &map) ATTR_COLD;

	u8 m_vram_read_bank = 0U;
	u8 m_vram_write_bank = 0U;
	u8 m_pal_reg[7]{};
	u8 m_pri_mask = 0U;
	u8 m_key_mux = 0U;
	u8 m_background = 0U;
	bool m_irq_en = true;
	u8 m_irq_slow = 0U;
	u8 m_irq_count = 0U;
	std::unique_ptr<u8[]> m_vram;
	required_device<cpu_device> m_maincpu;
	required_device<cassette_image_device> m_cass;
	required_device<generic_slot_device> m_cart;
	required_device<ram_device> m_ram;
	required_device<palette_device> m_palette;
	required_ioport_array<16> m_io_keyboard;
};


#define MASTER_CLOCK XTAL(28'636'363)


void rx78_state::cass_w(u8 data)
{
	m_cass->output(BIT(data, 0) ? -1.0 : +1.0);
}

u8 rx78_state::cass_r()
{
	return (m_cass->input() > 0.03) ? 0 : 1;
}


uint32_t rx78_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u8 layers;
	u8 laycol[2];
	const u8 borderx = 32, bordery = 20;

	bitmap.fill(64, cliprect); // set the border

	u16 count = 0x2c0; //first 0x2bf bytes aren't used for bitmap drawing apparently

	for(u8 y=0; y<184; y++)
	{
		for(u8 x=0; x<192; x+=8)
		{
			for (u8 i = 0; i < 8; i++)
			{
				layers = 0;
				for (u8 j = 0; j < 6; j++)
					if (BIT(m_pri_mask, j))
						layers |= (BIT(m_vram[count + j*0x2000], i))<<j;

				laycol[0] = 0;
				laycol[1] = 0;
				for (u8 j = 0; j < 6; j++)
					if (BIT(layers, j))
						laycol[BIT(m_pal_reg[6], j)] |= m_pal_reg[j];

				// This fixes text in Space Enemy
				if (m_pal_reg[6])
					for (u8 j = 0; j < 6; j++)
						if (BIT(layers, j))
							if (!m_pal_reg[j])
								laycol[0] = 0;

				u8 color = laycol[1] ? laycol[1] : (laycol[0] ? laycol[0] : m_background);
				bitmap.pix(y+bordery, x+i+borderx) = color;
			}
			count++;
		}
	}

	return 0;
}


u8 rx78_state::key_r()
{
	if((m_key_mux >= 1) && (m_key_mux <= 15))
		return m_io_keyboard[m_key_mux]->read();

	u8 res = 0;
	for(u8 i=1; i<10; i++)
		res |= m_io_keyboard[i]->read();

	return res;
}

void rx78_state::key_w(u8 data)
{
	// special codes: 10 (disable irq?) used by gundam and exbaseb; 30 (no idea) used by basic
	m_key_mux = data & 15;
	m_irq_en = !BIT(data, 4);
	m_maincpu->set_input_line(0, CLEAR_LINE);    // fixes exbaseb opening screen
}

// guess
void rx78_state::portf3_w(u8 data)
{
	data &= 3;
	if (data == 3)
		m_irq_slow = 64;    // fixes cracer traffic light
	else
	if (data == 2)
		m_irq_slow = 8;     // fixes seki flash rate of sight
	else
		m_irq_slow = 0;
}

u8 rx78_state::vram_r(offs_t offset)
{
	if(m_vram_read_bank == 0 || m_vram_read_bank > 6)
		return 0xff;

	return m_vram[offset + ((m_vram_read_bank - 1) * 0x2000)];
}

void rx78_state::vram_w(offs_t offset, u8 data)
{
	for (u8 i = 0; i < 6; i++)
		if (BIT(m_vram_write_bank, i))
			m_vram[offset + i * 0x2000] = data;
}

void rx78_state::vram_read_bank_w(u8 data)
{
	m_vram_read_bank = data;
}

void rx78_state::vram_write_bank_w(u8 data)
{
	m_vram_write_bank = data;
}

void rx78_state::vdp_reg_w(offs_t offset, u8 data)
{
	if (offset < 6)
		m_pal_reg[offset] = bitswap<8>(data, 7, 3, 6, 2, 5, 1, 4, 0) & 0x3f;
	else
		m_pal_reg[offset] = data & 0x3f;
}

void rx78_state::vdp_bg_reg_w(u8 data)
{
	u8 r = (data & 0x11) == 0x11 ? 0xff : ((data & 0x11) == 0x01 ? 0x7f : 0);
	u8 g = (data & 0x22) == 0x22 ? 0xff : ((data & 0x22) == 0x02 ? 0x7f : 0);
	u8 b = (data & 0x44) == 0x44 ? 0xff : ((data & 0x44) == 0x04 ? 0x7f : 0);
	m_palette->set_pen_color(64, rgb_t(r,g,b));   // use this as the border colour
	m_background = bitswap<8>(data, 7, 3, 6, 2, 5, 1, 4, 0) & 0x3f;
}

void rx78_state::vdp_pri_mask_w(u8 data)
{
	m_pri_mask = data;
}

void rx78_state::create_palette(palette_device &palette)
{
	constexpr u8 level[] = { 0, 0x7f, 0, 0xff };
	for (u8 i = 0; i < 64; i++)
	{
		u8 r = level[BIT(i, 0, 2)];
		u8 g = level[BIT(i, 2, 2)];
		u8 b = level[BIT(i, 4, 2)];
		palette.set_pen_color(i, rgb_t(r, g, b));
	}
	vdp_bg_reg_w(0);
}


void rx78_state::rx78_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom().region("roms", 0);
	//map(0x2000, 0x5fff)      // mapped by the cartslot
	map(0x6000, 0xafff).ram(); //ext RAM
	map(0xb000, 0xebff).ram();
	map(0xec00, 0xffff).rw(FUNC(rx78_state::vram_r), FUNC(rx78_state::vram_w));
}

void rx78_state::rx78_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
//  map(0x23, 0x23).nopw(); //used by rengo
//  map(0xe2, 0xe2).noprw(); //printer
//  map(0xe3, 0xe3).nopw(); //printer
//  map(0xef, 0xef).nopw(); //used by rengo
	map(0xf0, 0xf0).rw(FUNC(rx78_state::cass_r), FUNC(rx78_state::cass_w)); //cmt
	map(0xf1, 0xf1).w(FUNC(rx78_state::vram_read_bank_w));
	map(0xf2, 0xf2).w(FUNC(rx78_state::vram_write_bank_w));
	map(0xf3, 0xf3).w(FUNC(rx78_state::portf3_w));
	map(0xf4, 0xf4).rw(FUNC(rx78_state::key_r), FUNC(rx78_state::key_w)); //keyboard
	map(0xf5, 0xfb).w(FUNC(rx78_state::vdp_reg_w)); //vdp
	map(0xfc, 0xfc).w(FUNC(rx78_state::vdp_bg_reg_w)); //vdp
	map(0xfe, 0xfe).w(FUNC(rx78_state::vdp_pri_mask_w));
	map(0xff, 0xff).w("sn1", FUNC(sn76489a_device::write)); //psg
}

/* Input ports */
static INPUT_PORTS_START( rx78 )
	PORT_START("X0")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("X1")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("X2")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("X3")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')

	PORT_START("X4")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')

	PORT_START("X5")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')

	PORT_START("X6")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Up Down Arrow") PORT_CODE(KEYCODE_PGUP) PORT_CHAR('^')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Right Left Arrow") PORT_CODE(KEYCODE_PGDN)

	PORT_START("X7")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("CLR / HOME") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_UNUSED )
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("INST / DEL") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)

	PORT_START("X8")
	PORT_BIT(0x07,IP_ACTIVE_HIGH,IPT_UNUSED )
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("STOP") PORT_CODE(KEYCODE_END) PORT_CHAR(0xff) PORT_CHAR(3)
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_UNUSED )
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_UNUSED )
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("SFT LOCK") PORT_CODE(KEYCODE_CAPSLOCK)

	PORT_START("X9")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) //kana shift?
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_UNUSED )
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0xf8,IP_ACTIVE_HIGH,IPT_UNUSED )

	PORT_START("X10")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("P1 Up Left") PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1)

	PORT_START("X11")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("P1 Down Left")  PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("P1 Up Right") PORT_PLAYER(1)

	PORT_START("X12")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_8WAY  PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("P1 Down Right") PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_8WAY  PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1)

	PORT_START("X13")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("P2 Up Left") PORT_PLAYER(2)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2)

	PORT_START("X14")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("P2 Down Left")  PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("P2 Up Right") PORT_PLAYER(2)

	PORT_START("X15")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_8WAY  PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("P2 Down Right") PORT_PLAYER(2)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_8WAY  PORT_PLAYER(2)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2)
INPUT_PORTS_END


void rx78_state::machine_reset()
{
	address_space &prg = m_maincpu->space(AS_PROGRAM);
	if (m_cart->exists())
	{
		u32 size = m_cart->common_get_size("rom");
		if (size > 0x9000)
			size = 0x9000;
		if (size)
			prg.install_read_handler(0x2000, size+0x1FFF, read8sm_delegate(*m_cart, FUNC(generic_slot_device::read_rom)));
	}
}

void rx78_state::machine_start()
{
	m_vram = make_unique_clear<u8[]>(0xc000);
	save_pointer(NAME(m_vram), 0xc000);
	save_item(NAME(m_vram_read_bank));
	save_item(NAME(m_vram_write_bank));
	save_item(NAME(m_pal_reg));
	save_item(NAME(m_pri_mask));
	save_item(NAME(m_key_mux));
	save_item(NAME(m_background));
}

INTERRUPT_GEN_MEMBER(rx78_state::interrupt)
{
	if (m_irq_en)
	{
		m_irq_count++;
		if (m_irq_count > m_irq_slow)
		{
			m_irq_count = 0;
			irq0_line_hold(device);
		}
	}
	else
	// wait for a keypress
	if (key_r())
	{
		irq0_line_hold(device);
	}
}

DEVICE_IMAGE_LOAD_MEMBER( rx78_state::cart_load )
{
	u32 size = m_cart->common_get_size("rom");

	if (size != 0x2000 && size != 0x4000 && size != 0x8000)
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported cartridge size (must be 8K, 16K or 32K)");

	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

/* F4 Character Displayer */
static const gfx_layout rx78_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	187,                    /* 187 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_rx78 )
	GFXDECODE_ENTRY( "roms", 0x1a27, rx78_charlayout, 0, 8 )
GFXDECODE_END

void rx78_state::rx78(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, MASTER_CLOCK/7); // unknown divider
	m_maincpu->set_addrmap(AS_PROGRAM, &rx78_state::rx78_mem);
	m_maincpu->set_addrmap(AS_IO, &rx78_state::rx78_io);
	m_maincpu->set_vblank_int("screen", FUNC(rx78_state::interrupt));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
//  screen.set_refresh_hz(60);
//  screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
//  screen.set_size(192, 184);
//  screen.set_visarea(0, 192-1, 0, 184-1);
	/* guess: generic NTSC video timing at 256x224, system runs at 192x184, suppose with some border area to compensate */
	screen.set_raw(MASTER_CLOCK/4, 442, 0, 256, 263, 0, 224);
	screen.set_screen_update(FUNC(rx78_state::screen_update));
	screen.set_palette("palette");

	PALETTE(config, m_palette, FUNC(rx78_state::create_palette), 64+1);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_rx78);

	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "rx78_cart", "bin,rom").set_device_load(FUNC(rx78_state::cart_load));

	RAM(config, RAM_TAG).set_default_size("32K").set_extra_options("16K");

	SPEAKER(config, "mono").front_center();
	SN76489A(config, "sn1", XTAL(28'636'363)/8).add_route(ALL_OUTPUTS, "mono", 0.50); // unknown divider

	CASSETTE(config, m_cass);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cass->set_interface("rx78_cass");

	/* Software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("rx78_cart");
	SOFTWARE_LIST(config, "cass_list").set_original("rx78_cass");
}

/* ROM definition */
ROM_START( rx78 )
	ROM_REGION( 0x2000, "roms", 0 )
	ROM_LOAD( "ipl.rom", 0x0000, 0x2000, CRC(a194ea53) SHA1(ba39e73e6eb7cbb8906fff1f81a98964cd62af0d))
ROM_END

void rx78_state::init_rx78()
{
	u32 ram_size = m_ram->size();
	address_space &prg = m_maincpu->space(AS_PROGRAM);

	if (ram_size == 0x4000)
		prg.unmap_readwrite(0x6000, 0xafff);
}

} // anonymous namespace


/* Driver */

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT       COMPANY   FULLNAME     FLAGS */
COMP( 1983, rx78, 0,      0,      rx78,    rx78,  rx78_state, init_rx78, "Bandai", "Gundam RX-78", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



rz1.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    Casio RZ-1

    Sampling drum machine

    Hardware:
    - uPD7811G-120
    - HN4872128G25 (program rom)
    - uPD4364C (data ram, battery backed)
    - 2x uPD934G (percussion generator)
    - 2x HN613256P (sample rom)
    - 2x uPD4364C-15L (sample ram, battery backed)
    - EXK-F19Z2064 (10-bit DAC)

    Notes:
    - Each sample ROM holds 1.49s of sounds in the following format:
      PCM data, signed 8-bit, mono, 20,000 Hz
    - Holding EDIT/RECORD, DELETE, INSERT/AUTO-COMPENSATE and
      CHAIN/BEAT at startup causes the system to go into a RAM test.

    TODO:
    - Metronome
    - Make audio input generic (core support needed)

***************************************************************************/

#include "emu.h"
#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/upd7810/upd7810.h"
#include "formats/trs_cas.h"
#include "imagedev/cassette.h"
#include "machine/nvram.h"
#include "sound/upd934g.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "rz1.lh"


namespace {


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class rz1_state : public driver_device
{
public:
	rz1_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_hd44780(*this, "hd44780"),
		m_pg(*this, "pg%u", 0U),
		m_toms(*this, "tom%u", 1U),
		m_bd(*this, "bd"),
		m_cassette(*this, "cassette"),
		m_linein(*this, "linein"),
		m_keys(*this, "kc%u", 0U),
		m_foot(*this, "foot"),
		m_led_sampling(*this, "led_sampling"),
		m_led_song(*this, "led_song"),
		m_led_pattern(*this, "led_pattern"),
		m_led_startstop(*this, "led_startstop"),
		m_port_a(0),
		m_port_b(0xff),
		m_midi_rx(1)
	{ }

	void rz1(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<upd7811_device> m_maincpu;
	required_device<hd44780_device> m_hd44780;
	required_device_array<upd934g_device, 2> m_pg;
	required_device_array<speaker_device, 3> m_toms;
	required_device<speaker_device> m_bd;
	required_device<cassette_image_device> m_cassette;
	required_device<cassette_image_device> m_linein;
	required_ioport_array<8> m_keys;
	required_ioport m_foot;

	output_finder<> m_led_sampling;
	output_finder<> m_led_song;
	output_finder<> m_led_pattern;
	output_finder<> m_led_startstop;

	void map(address_map &map) ATTR_COLD;
	void pg0_map(address_map &map) ATTR_COLD;
	void pg1_map(address_map &map) ATTR_COLD;

	uint8_t key_r();

	void rz1_palette(palette_device &palette) const;
	HD44780_PIXEL_UPDATE(lcd_pixel_update);
	void leds_w(uint8_t data);

	void upd934g_c_w(offs_t offset, uint8_t data);
	void upd934g_b_w(offs_t offset, uint8_t data);
	uint8_t analog_r();

	uint8_t port_a_r();
	void port_a_w(uint8_t data);
	void port_b_w(uint8_t data);
	uint8_t port_c_r();
	void port_c_w(uint8_t data);

	uint8_t m_port_a;
	uint8_t m_port_b;
	int m_midi_rx;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void rz1_state::map(address_map &map)
{
//  map(0x0000, 0x0fff).rom().region("maincpu", 0);
	map(0x2000, 0x3fff).ram().share("dataram");
	map(0x4000, 0x7fff).rom().region("program", 0);
	map(0x8000, 0x8fff).w(FUNC(rz1_state::upd934g_c_w));
	map(0x9000, 0x9fff).rw(FUNC(rz1_state::key_r), FUNC(rz1_state::upd934g_b_w));
	map(0xa000, 0xbfff).ram().share("sample1");
	map(0xc000, 0xdfff).ram().share("sample2");
	map(0xe000, 0xe001).w(FUNC(rz1_state::leds_w));
}

void rz1_state::pg0_map(address_map &map)
{
	map(0x0000, 0x7fff).rom();
	map(0x8000, 0x9fff).ram().share("sample1");
	map(0xa000, 0xbfff).ram().share("sample2");
}

void rz1_state::pg1_map(address_map &map)
{
	map(0x0000, 0x7fff).rom();
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( rz1 )
	PORT_START("kc0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TOM1")               PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TOM3")               PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RIM")                PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("OPEN HH")            PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CLAPS")              PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("COWBELL")            PORT_CODE(KEYCODE_F6)
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("kc1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TOM2")               PORT_CODE(KEYCODE_F7)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B D")                PORT_CODE(KEYCODE_F8)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S D")                PORT_CODE(KEYCODE_F9)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CLOSED HH")          PORT_CODE(KEYCODE_F10)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RIDE")               PORT_CODE(KEYCODE_F11)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CRASH")              PORT_CODE(KEYCODE_F12)
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("kc2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SAMPLE 1")           PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SAMPLE 2")           PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SAMPLE 3")           PORT_CODE(KEYCODE_3)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SAMPLE 4")           PORT_CODE(KEYCODE_4)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MUTE")               PORT_CODE(KEYCODE_M)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ACCENT")             PORT_CODE(KEYCODE_A)
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("kc3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("START/STOP")         PORT_CODE(KEYCODE_S)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CONTINUE START")     PORT_CODE(KEYCODE_C)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SAMPLING")           PORT_CODE(KEYCODE_L)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TEMPO \xe2\x96\xb3") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TEMPO \xe2\x96\xbd") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RESET/COPY")         PORT_CODE(KEYCODE_R)
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("kc4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("PATTERN")                PORT_CODE(KEYCODE_P)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SONG")                   PORT_CODE(KEYCODE_O)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("EDIT/RECORD")            PORT_CODE(KEYCODE_E)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DELETE")                 PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("INSERT/AUTO-COMPENSATE") PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CHAIN/BEAT")             PORT_CODE(KEYCODE_B)
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("kc5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MT SAVE")            PORT_CODE(KEYCODE_5)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MT LOAD")            PORT_CODE(KEYCODE_6)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MIDI CH")            PORT_CODE(KEYCODE_7)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MIDI CLOCK")         PORT_CODE(KEYCODE_8)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("kc6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0 (1/2)")            PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1 (1/4)")            PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2 (1/6)")            PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3 (1/8)")            PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4 (1/12)")           PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5 (1/16)")           PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("kc7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6 (1/24)")           PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7 (1/32)")           PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8 (1/48)")           PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9 (1/96)")           PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("\xe2\x96\xb3 (YES)") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("\xe2\x96\xbd (NO)")  PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("foot")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Foot Switch")        PORT_CODE(KEYCODE_SPACE)
INPUT_PORTS_END


//**************************************************************************
//  KEYBOARD
//**************************************************************************

uint8_t rz1_state::key_r()
{
	uint8_t data = 0;

	for (int i = 0; i < 8; i++)
		if (BIT(m_port_a, i) == 0)
			data |= m_keys[i]->read();

	return data;
}


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

void rz1_state::rz1_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148)); // background
	palette.set_pen_color(1, rgb_t(92, 83, 88));    // lcd pixel on
	palette.set_pen_color(2, rgb_t(131, 136, 139)); // lcd pixel off
}

HD44780_PIXEL_UPDATE( rz1_state::lcd_pixel_update )
{
	// char size is 5x8
	if (x > 4 || y > 7)
		return;

	if (line < 1 && pos < 16)
		bitmap.pix(1 + y, 1 + line*8*6 + pos*6 + x) = state ? 1 : 2;
}

void rz1_state::leds_w(uint8_t data)
{
	// 76------  unknown
	// --5-----  sampling led
	// ---4----  start/stop led
	// ----3---  pattern led red
	// -----2--  pattern led green
	// ------1-  song led red
	// -------0  song led green

	m_led_song = BIT(~data, 0, 2);
	m_led_pattern = BIT(~data, 2, 2);
	m_led_startstop = BIT(~data, 4);
	m_led_sampling = BIT(~data, 5);
}


//**************************************************************************
//  AUDIO EMULATION
//**************************************************************************

void rz1_state::upd934g_c_w(offs_t offset, uint8_t data)
{
	m_pg[0]->write(offset >> 8, data);
}

void rz1_state::upd934g_b_w(offs_t offset, uint8_t data)
{
	m_pg[1]->write(offset >> 8, data);
}

uint8_t rz1_state::analog_r()
{
	return uint8_t(int8_t(m_linein->input() * 127.0) + 127);
}


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

uint8_t rz1_state::port_a_r()
{
	if ((BIT(m_port_b, 7) == 0) && (BIT(m_port_b, 6) == 1))
	{
		// Not clear why, but code expects to read busy flag from PA5 rather than PA7
		return bitswap<8>(m_hd44780->read(BIT(m_port_b, 5)), 5, 6, 7, 4, 3, 2, 1, 0);
	}

	logerror("port_a_r (PB = %02x)\n", m_port_b);
	return 0;
}

void rz1_state::port_a_w(uint8_t data)
{
	m_port_a = data;

	if ((BIT(m_port_b, 7) == 0) && (BIT(m_port_b, 6) == 0))
		m_hd44780->write(BIT(m_port_b, 5), data);
}

void rz1_state::port_b_w(uint8_t data)
{
	// 7-------  lcd e
	// -6------  lcd rw
	// --5-----  lcd rs
	// ---4----  percussion generator reset
	// ----3---  metronome trigger
	// -----2--  power-on mute for line-out
	// ------1-  change-over signal tom3/bd
	// -------0  change-over signal tom1/tom2

	if (0)
		logerror("port_b_w: %02x\n", data);

	m_port_b = data;

	m_toms[0]->set_input_gain(0, BIT(data, 0) ? 1.0 : 0.0);
	m_toms[1]->set_input_gain(0, BIT(data, 0) ? 0.0 : 1.0);
	m_toms[2]->set_input_gain(0, BIT(data, 1) ? 1.0 : 0.0);
	m_bd->set_input_gain(0, BIT(data, 1) ? 0.0 : 1.0);
}

uint8_t rz1_state::port_c_r()
{
	// 7-------  foot-sustain input
	// -6------  cassette data out
	// --5-----  cassette remote control
	// ---4----  change-over signal for sampling ram
	// ----3---  cassette data in
	// -----2--  control signal for percussion generator c
	// ------1-  midi in (handled elsewhere: cpu rxd)
	// -------0  midi out (handled elsewhere: cpu txd)

	uint8_t data = 0;

	data |= (m_cassette->input() > 0 ? 0 : 1) << 3;
	data |= m_foot->read() << 7;

	return data;
}

void rz1_state::port_c_w(uint8_t data)
{
	m_cassette->change_state(BIT(data, 5) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
	m_cassette->output(BIT(data, 6) ? -1.0 : 1.0);

	logerror("port_c_w: %02x\n", data);
}

void rz1_state::machine_start()
{
	// resolve output finders
	m_led_sampling.resolve();
	m_led_song.resolve();
	m_led_pattern.resolve();
	m_led_startstop.resolve();

	// register for save states
	save_item(NAME(m_port_a));
	save_item(NAME(m_port_b));
	save_item(NAME(m_midi_rx));
}

void rz1_state::machine_reset()
{
	m_midi_rx = 1;
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void rz1_state::rz1(machine_config &config)
{
	UPD7811(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &rz1_state::map);
	m_maincpu->pa_in_cb().set(FUNC(rz1_state::port_a_r));
	m_maincpu->pa_out_cb().set(FUNC(rz1_state::port_a_w));
	m_maincpu->pb_out_cb().set(FUNC(rz1_state::port_b_w));
	m_maincpu->pc_in_cb().set(FUNC(rz1_state::port_c_r));
	m_maincpu->pc_out_cb().set(FUNC(rz1_state::port_c_w));
	m_maincpu->rxd_func().set([this]() { return m_midi_rx; });
	m_maincpu->txd_func().set("mdout", FUNC(midi_port_device::write_txd));
	m_maincpu->an0_func().set(FUNC(rz1_state::analog_r));
	m_maincpu->an1_func().set(FUNC(rz1_state::analog_r));
	m_maincpu->an2_func().set(FUNC(rz1_state::analog_r));
	m_maincpu->an3_func().set(FUNC(rz1_state::analog_r));
	m_maincpu->an4_func().set(FUNC(rz1_state::analog_r));
	m_maincpu->an5_func().set(FUNC(rz1_state::analog_r));
	m_maincpu->an6_func().set(FUNC(rz1_state::analog_r));
	m_maincpu->an7_func().set(FUNC(rz1_state::analog_r));

	NVRAM(config, "dataram", nvram_device::DEFAULT_NONE);
	NVRAM(config, "sample1", nvram_device::DEFAULT_NONE);
	NVRAM(config, "sample2", nvram_device::DEFAULT_NONE);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // not accurate
	screen.set_size(6*16+1, 10);
	screen.set_visarea(0, 6*16, 0, 10-1);
	screen.set_screen_update("hd44780", FUNC(hd44780_device::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(rz1_state::rz1_palette), 3);

	HD44780(config, m_hd44780, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_hd44780->set_lcd_size(1, 16);
	m_hd44780->set_pixel_update_cb(FUNC(rz1_state::lcd_pixel_update));

	config.set_default_layout(layout_rz1);

	// audio hardware

	// individual drum outputs
	SPEAKER(config, "tom1").front_center();
	SPEAKER(config, "tom2").front_center();
	SPEAKER(config, "tom3").front_center();
	SPEAKER(config, "bd").front_center();
	SPEAKER(config, "rim_and_sd").front_center();
	SPEAKER(config, "hihat").front_center();
	SPEAKER(config, "claps_and_ride").front_center();
	SPEAKER(config, "cowbell_and_crash").front_center();
	SPEAKER(config, "sample_1_and_2").front_center();
	SPEAKER(config, "sample_3_and_4").front_center();
	// for tape / line in
	SPEAKER(config, "speaker").front_center();

	UPD934G(config, m_pg[0], 1333000);
	m_pg[0]->set_addrmap(0, &rz1_state::pg0_map);
	m_pg[0]->add_route(0, "claps_and_ride", 1.0);
	m_pg[0]->add_route(1, "cowbell_and_crash", 1.0);
	m_pg[0]->add_route(2, "sample_1_and_2", 1.0);
	m_pg[0]->add_route(3, "sample_3_and_4", 1.0);

	UPD934G(config, m_pg[1], 1280000);
	m_pg[1]->set_addrmap(0, &rz1_state::pg1_map);
	// tom1/tom2 and tom3/bd are multiplexed together (see port_b_w)
	m_pg[1]->add_route(0, "tom1", 1.0);
	m_pg[1]->add_route(0, "tom2", 1.0);
	m_pg[1]->add_route(1, "tom3", 1.0);
	m_pg[1]->add_route(1, "bd", 1.0);
	m_pg[1]->add_route(2, "rim_and_sd", 1.0);
	m_pg[1]->add_route(3, "hihat", 1.0);

	// midi
	midi_port_device &mdin(MIDI_PORT(config, "mdin", midiin_slot, "midiin"));
	mdin.rxd_handler().set([this](int state) { m_midi_rx = state; });
	mdin.rxd_handler().append("mdthru", FUNC(midi_port_device::write_txd));

	MIDI_PORT(config, "mdout", midiout_slot, "midiout");

	MIDI_PORT(config, "mdthru", midiout_slot, "midiout");

	// mt (magnetic tape)
	CASSETTE(config, m_cassette);
	m_cassette->set_formats(trs80l2_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->set_interface("rz1_cass");
	m_cassette->add_route(ALL_OUTPUTS, "speaker", 0.05);

	// should be a generic audio input port, using cassette for now
	CASSETTE(config, m_linein);
	m_linein->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_linein->set_interface("audio_cass");
	m_linein->add_route(ALL_OUTPUTS, "speaker", 0.05);

	SOFTWARE_LIST(config, "cass_list").set_original("rz1_cass");
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( rz1 )
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("upd7811g-120.bin", 0x0000, 0x1000, CRC(597ac04a) SHA1(96451a764296eaa22aaad3cba121226dcba865f4))

	ROM_REGION(0x4000, "program", 0)
	ROM_LOAD("program.bin", 0x0000, 0x4000, CRC(b44b2652) SHA1(b77f8daece9adb177b6ce1ef518fc3238b8c0a9c))

	// Clap, Ride, Cowbell and Crash
	ROM_REGION(0x8000, "pg0", 0)
	ROM_LOAD("sound_b.cm6", 0x0000, 0x8000, CRC(ee5b703e) SHA1(cbf2e92c68901f236678d704e9e695a5c84ff49e))

	// Toms 1~3, Kick, Snare, Rimshot, Closed Hi-Hat, Open Hi-Hat and Metronome Click
	ROM_REGION(0x8000, "pg1", 0)
	ROM_LOAD("sound_a.cm5", 0x0000, 0x8000, CRC(c643ff24) SHA1(e886314d22a9a5473bfa2cb237ecafcf0daedfc1))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY  FULLNAME  FLAGS
CONS( 1986, rz1,  0,      0,      rz1,     rz1,   rz1_state, empty_init, "Casio", "RZ-1",   MACHINE_SUPPORTS_SAVE )



rzone.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

** subclass of hh_sm510_state (hh_sm510.h, hh_sm510.cpp) **

Tiger R-Zone driver

This is a backwards console, the heart of the machine is the cartridge. The
console houses the controller, speaker, power, backlight, and a polarizer filter
for the screen. The cartridge has the MCU, optional sound ROM, and the LCD screen
in a translucent window.

Console family:

1995: R-Zone HeadGear: Wearable headset, controller is separate, red-on-black
      screen is reflected in front of the right eye.
1996: R-Zone SuperScreen: Handheld console, inverted filter (aka black LCD
      segments), optional background sheet as with standalone handhelds.
1997: R-Zone X.P.G - Xtreme Pocket Game: Handheld version of HeadGear.
1997: R-Zone DataZone: PDA with a built-in SuperScreen.

TODO:
- softwarelist? it's impossible right now due to SVG initialization
- support for SuperScreen. SVG colors will need to be inverted, or maybe with
  artwork or HLSL?
- add DataZone, will get its own driver

*******************************************************************************/

#include "emu.h"
#include "hh_sm510.h"

#include "machine/timer.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "rzone.lh"


namespace {

class rzone_state : public hh_sm510_state
{
public:
	rzone_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_sm510_state(mconfig, type, tag),
		m_led_out(*this, "led"),
		m_led_off(*this, "led_off")
	{ }

	void rzbatfor(machine_config &config);
	void rztoshden(machine_config &config);
	void rzindy500(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	output_finder<> m_led_out;
	required_device<timer_device> m_led_off;

	int m_led_pin = 0;
	int m_sctrl = 0;
	int m_sclock = 0;

	TIMER_DEVICE_CALLBACK_MEMBER(led_off_callback) { m_led_out = m_led_pin ? 1 : 0; }
	void led_w(int state);
	void audio_w(int state);
	void sctrl_w(int state);
	void sclock_w(int state);
	int sdata_r();

	void t1_write_r(u8 data);
	void t1_write_s(u8 data);
	virtual u8 input_r() override;

	void t2_update_audio();
	void t2_write_r(u8 data);
	void t2_write_s(u8 data);
};


// machine start

void rzone_state::machine_start()
{
	hh_sm510_state::machine_start();

	// resolve outputs
	m_led_out.resolve();

	// register for savestates
	save_item(NAME(m_led_pin));
	save_item(NAME(m_sctrl));
	save_item(NAME(m_sclock));
}



/*******************************************************************************
    I/O
*******************************************************************************/

// console

void rzone_state::led_w(int state)
{
	// LED: enable backlight
	if (state)
		m_led_out = 1;

	// delay led off to prevent flickering
	if (!state && m_led_pin)
		m_led_off->adjust(attotime::from_msec(30));

	m_led_pin = state;
}

void rzone_state::audio_w(int state)
{
	// Audio: speaker out
	m_speaker->level_w(state ? 1 : 0);
}

void rzone_state::sctrl_w(int state)
{
	// SCTRL: 74165 SH/LD: reload inputs while low
	if (!state || !m_sctrl)
		m_inp_mux = m_inputs[0]->read();

	m_sctrl = state;
}

void rzone_state::sclock_w(int state)
{
	// SCLOCK: 74165 CLK: shift inputs on rising edge when 74165 SH/LD is high
	if (m_sctrl && !m_sclock && state)
		m_inp_mux >>= 1;

	m_sclock = state;
}

int rzone_state::sdata_r()
{
	// SDATA: 74165 Q
	sctrl_w(m_sctrl); // reload inputs if needed
	return m_inp_mux & 1;
}


// cartridge type 1: simple SM510

void rzone_state::t1_write_r(u8 data)
{
	// R1: Audio
	audio_w(data & 1);

	// R2: SCTRL
	sctrl_w(data >> 1 & 1);
}

void rzone_state::t1_write_s(u8 data)
{
	// S1: LED
	led_w(data & 1);

	// S2: SCLOCK
	sclock_w(data >> 1 & 1);
}

u8 rzone_state::input_r()
{
	// K1: SDATA
	return sdata_r();
}


// cartridge type 2: simple SM512, 2 audio lines

void rzone_state::t2_update_audio()
{
	audio_w((m_s >> 2 & 1) | (m_r & 1));
}

void rzone_state::t2_write_r(u8 data)
{
	// R: Audio
	m_r = data;
	t2_update_audio();
}

void rzone_state::t2_write_s(u8 data)
{
	// S1: SCTRL
	sctrl_w(data & 1);

	// S2: SCLOCK
	sclock_w(data >> 1 & 1);

	// S3: Audio
	m_s = data;
	t2_update_audio();

	// S4: LED
	led_w(data >> 3 & 1);
}



/*******************************************************************************
    Inputs
*******************************************************************************/

static INPUT_PORTS_START( rzone )
	PORT_START("IN.0")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_POWER_ON ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // A
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_CB(input_changed) // B
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_CB(input_changed) // C
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_CHANGED_CB(input_changed) // D
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_VOLUME_DOWN ) PORT_CHANGED_CB(input_changed) PORT_NAME("Sound")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_CHANGED_CB(input_changed)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Pause")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_CB(input_changed)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void rzone_state::rzbatfor(machine_config &config)
{
	// basic machine hardware
	SM512(config, m_maincpu); // no external XTAL
	m_maincpu->write_segs().set(FUNC(rzone_state::sm510_lcd_segment_w));
	m_maincpu->read_k().set(FUNC(rzone_state::input_r));
	m_maincpu->write_s().set(FUNC(rzone_state::t2_write_s));
	m_maincpu->write_r().set(FUNC(rzone_state::t2_write_r));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1368, 1080);
	screen.set_visarea_full();

	TIMER(config, m_led_off).configure_generic(FUNC(rzone_state::led_off_callback));
	config.set_default_layout(layout_rzone);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

void rzone_state::rztoshden(machine_config &config)
{
	// basic machine hardware
	SM510(config, m_maincpu);
	m_maincpu->set_r_mask_option(sm510_base_device::RMASK_DIRECT);
	m_maincpu->write_segs().set(FUNC(rzone_state::sm510_lcd_segment_w));
	m_maincpu->read_k().set(FUNC(rzone_state::input_r));
	m_maincpu->write_s().set(FUNC(rzone_state::t1_write_s));
	m_maincpu->write_r().set(FUNC(rzone_state::t1_write_r));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1392, 1080);
	screen.set_visarea_full();

	TIMER(config, m_led_off).configure_generic(FUNC(rzone_state::led_off_callback));
	config.set_default_layout(layout_rzone);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

void rzone_state::rzindy500(machine_config &config)
{
	// basic machine hardware
	SM510(config, m_maincpu); // no external XTAL
	m_maincpu->set_r_mask_option(sm510_base_device::RMASK_DIRECT); // confirmed
	m_maincpu->write_segs().set(FUNC(rzone_state::sm510_lcd_segment_w));
	m_maincpu->read_k().set(FUNC(rzone_state::input_r));
	m_maincpu->write_s().set(FUNC(rzone_state::t1_write_s));
	m_maincpu->write_r().set(FUNC(rzone_state::t1_write_r));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1425, 1080);
	screen.set_visarea_full();

	TIMER(config, m_led_off).configure_generic(FUNC(rzone_state::led_off_callback));
	config.set_default_layout(layout_rzone);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( rzbatfor )
	ROM_REGION( 0x1000, "maincpu", 0 ) // model 71-231, SM512 under epoxy (die label KMN1202) (not the same game as the standalone Tiger handheld)
	ROM_LOAD( "12_02.program", 0x0000, 0x1000, CRC(27abdb52) SHA1(b356ff80b628244da588b4748404b78d7a57eccd) )

	ROM_REGION( 0x100, "maincpu:melody", 0 )
	ROM_LOAD( "12_02.melody", 0x000, 0x100, CRC(d794746c) SHA1(f0706c5100c090c65fcb2d768b5a5b4a55b29e04) )

	ROM_REGION( 652556, "screen", 0)
	ROM_LOAD( "rzbatfor.svg", 0, 652556, CRC(4d850489) SHA1(31a2a1e9209c0f77dbc268cddbfa4a67478734a7) )
ROM_END

ROM_START( rztoshden )
	ROM_REGION( 0x1000, "maincpu", 0 ) // model 71-241, SM510 under epoxy (die label ML4)
	ROM_LOAD( "ml4", 0x0000, 0x1000, CRC(282c641f) SHA1(f94e4a17ffe90adcc6046070034be9b777f72288) )

	ROM_REGION( 857474, "screen", 0)
	ROM_LOAD( "rztoshden.svg", 0, 857474, CRC(e4340f84) SHA1(4f040d3c7dc06d66b4f06942e610a64c11e5cd4d) )
ROM_END

ROM_START( rzindy500 )
	ROM_REGION( 0x1000, "maincpu", 0 ) // model 71-312, SM510 under epoxy (die label KMS10 22)
	ROM_LOAD( "10_22", 0x0000, 0x1000, CRC(99a746d0) SHA1(64264499d45a566fa9a0801c20e7fa27eac18da6) )

	ROM_REGION( 533411, "screen", 0)
	ROM_LOAD( "rzindy500.svg", 0, 533411, CRC(cfc85677) SHA1(014b9123d81fba1488b4a22a6b6fd0c09e22c1ea) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT  CLASS        INIT        COMPANY, FULLNAME, FLAGS
SYST( 1995, rzbatfor,  0,      0,      rzbatfor,  rzone, rzone_state, empty_init, "Tiger Electronics", "R-Zone: Batman Forever", MACHINE_SUPPORTS_SAVE ) // licensed from DC Comics
SYST( 1996, rztoshden, 0,      0,      rztoshden, rzone, rzone_state, empty_init, "Tiger Electronics", "R-Zone: Battle Arena Toshinden", MACHINE_SUPPORTS_SAVE ) // licensed from Takara
SYST( 1996, rzindy500, 0,      0,      rzindy500, rzone, rzone_state, empty_init, "Tiger Electronics", "R-Zone: Indy 500", MACHINE_SUPPORTS_SAVE ) // licensed from Sega



sacstate.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

SacState 8008

2009-02-23 Skeleton driver.

http://www.digibarn.com/stories/bill-pentz-story/index.html

The year is listed as '1972/73'.

All input to be in lowercase.
The weird characters that show on screen are various escape sequences.
These are different depending on the terminal type chosen. The codes
need to be understood and emulated before this system makes sense.

Known Monitor commands: (from the disassembly)
!   write buffer to AD000
*   set RAM036 flag
+   increment AD000 by 1
+n  increment AD000 by n
-   decrement AD000 by 1
-n  decrement AD000 by n
:   clear RAM036 flag
=   display AD000
=nnn    set AD000 to nnn
@   fill buffer with 026

unknown commands: / & d e l r s t u z \ ^ | ~

Other input will either result in '!' message, or halt.


****************************************************************************/

#include "emu.h"
#include "cpu/i8008/i8008.h"
#include "machine/terminal.h"


namespace {

class sacstate_state : public driver_device
{
public:
	sacstate_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_terminal(*this, "terminal")
	{ }

	void sacstate(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	u8 port00_r();
	u8 port01_r();
	u8 port04_r();
	void port08_w(u8 data);
	void kbd_put(u8 data);
	void sacstate_io(address_map &map) ATTR_COLD;
	void sacstate_mem(address_map &map) ATTR_COLD;

	u8 m_term_data = 0U;
	u8 m_val = 0U;
	required_device<cpu_device> m_maincpu;
	required_device<generic_terminal_device> m_terminal;
};

u8 sacstate_state::port01_r()
{
	u8 ret = m_val;
	if (m_term_data)
		ret |= 0x04; // data in
	return ret;
}

u8 sacstate_state::port00_r()
{
	u8 ret = m_term_data;
	m_term_data = 0;
	return ret;
}

u8 sacstate_state::port04_r()
{
	logerror("unknown_r\n");
	return 0;
}

void sacstate_state::port08_w(u8 data)
{
	if (data == 0x40)
		m_val = 0x40;
	else
	if (data == 0x04)
		m_val = 0;
}

void sacstate_state::sacstate_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000, 0x7ff).rom();
	map(0x800, 0xfff).ram();
}

void sacstate_state::sacstate_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x1f);
	map(0x00, 0x00).r(FUNC(sacstate_state::port00_r));
	map(0x01, 0x01).r(FUNC(sacstate_state::port01_r));
	map(0x04, 0x04).r(FUNC(sacstate_state::port04_r));
	map(0x08, 0x08).w(FUNC(sacstate_state::port08_w));
	map(0x16, 0x16).w(m_terminal, FUNC(generic_terminal_device::write));
	map(0x17, 0x1f).nopw();
}

/* Input ports */
static INPUT_PORTS_START( sacstate )
	PORT_START("CONFIG")
	PORT_CONFNAME( 0x08, 0x08, "Terminal Type") // no idea of actual terminal types
	PORT_CONFSETTING(    0x00, "A")
	PORT_CONFSETTING(    0x08, "B")
INPUT_PORTS_END

void sacstate_state::kbd_put(u8 data)
{
	m_term_data = data;
}

void sacstate_state::machine_reset()
{
	m_term_data = 0;
	m_val = ioport("CONFIG")->read();
}

void sacstate_state::machine_start()
{
	save_item(NAME(m_term_data));
	save_item(NAME(m_val));
}

void sacstate_state::sacstate(machine_config &config)
{
	/* basic machine hardware */
	I8008(config, m_maincpu, 800000);
	m_maincpu->set_addrmap(AS_PROGRAM, &sacstate_state::sacstate_mem);
	m_maincpu->set_addrmap(AS_IO, &sacstate_state::sacstate_io);

	/* video hardware */
	GENERIC_TERMINAL(config, m_terminal, 0);
	m_terminal->set_keyboard_callback(FUNC(sacstate_state::kbd_put));
}

/* ROM definition */
ROM_START( sacstate )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "sacst1.bin", 0x0700, 0x0100, CRC(ba020160) SHA1(6337cdf65583808768664653c937e50040aec6d4))
	ROM_LOAD( "sacst2.bin", 0x0600, 0x0100, CRC(26f3e505) SHA1(3526060dbd1bf885c2e686bc9a6082387630952a))
	ROM_LOAD( "sacst3.bin", 0x0500, 0x0100, CRC(965b3474) SHA1(6d9142e68d375fb000fd6ea48369d0801274ded6))
	ROM_LOAD( "sacst4.bin", 0x0400, 0x0100, CRC(3cd3e169) SHA1(75a99e8e4dbd6e054209a4979bb498f37e962697))
	ROM_LOAD( "sacst5.bin", 0x0300, 0x0100, CRC(30619454) SHA1(cb498880bec27c9adc44dc1267858555000452c6))
	ROM_LOAD( "sacst6.bin", 0x0200, 0x0100, CRC(a4cd2ff6) SHA1(3f4da5510c0778eb770c96c01f91f5cb7f5285fa))
	ROM_LOAD( "sacst7.bin", 0x0100, 0x0100, CRC(33971d8b) SHA1(9e0bbeef6a6a15107f270e8b285300284ee7f63f))
	ROM_LOAD( "sacst8.bin", 0x0000, 0x0100, CRC(931252ef) SHA1(e06ea6947f432f0a4ce944de74978d929920fb53))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY     FULLNAME         FLAGS
COMP( 1973, sacstate, 0,      0,      sacstate, sacstate, sacstate_state, empty_init, "SacState", "SacState 8008", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



sag.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Kevin Horton, Sean Riddle
/*******************************************************************************

Entex Select-A-Game Machine, handheld game console.
Technically, the main unit is the peripheral(buttons, display, speaker, power),
and the cartridge holds the MCU(processor, ROM, RAM).

Hardware notes:
- cyan/red VFD Futaba DM-16Z + cyan VFD 9-digit panel Futaba 9-ST-11A 1F
- 1-bit sound, two 7-button control panels attached to each side
- edge connector to cartridge, MCU on cartridge (HD38800 or TMS1670)

A 2nd version of the console was announced, called Table Top Game Machine,
supposed to be backward-compatible, but Entex didn't release it. Their next
console was the Adventure Vision.

MAME external artwork is recommended for the per-game VFD overlays. The artwork
orientation can be rotated in the video options. By default, the "visitor" side
is at the bottom. This is how most of the games are played, Space Invader 2 is
an exception.

*******************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/hmcs40/hmcs40.h"
#include "cpu/tms1000/tms1400.h"
#include "video/pwm.h"
#include "sound/spkrdev.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "sag.lh"


namespace {

class sag_state : public driver_device
{
public:
	sag_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_hmcs40_cpu(*this, "hmcs40_cpu"),
		m_tms1k_cpu(*this, "tms1k_cpu"),
		m_display(*this, "display"),
		m_speaker(*this, "speaker"),
		m_cart(*this, "cartslot"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void sag(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	optional_device<hmcs40_cpu_device> m_hmcs40_cpu;
	optional_device<tms1k_base_device> m_tms1k_cpu;
	required_device<pwm_display_device> m_display;
	required_device<speaker_sound_device> m_speaker;
	required_device<generic_slot_device> m_cart;
	required_ioport_array<6> m_inputs;

	u16 m_grid = 0;
	u16 m_plate = 0;

	void update_display();
	u8 input_r();
	void speaker_w(int state);
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	void hmcs40_write_r(offs_t offset, u8 data);
	void hmcs40_write_d(u16 data);
	u16 hmcs40_read_d();

	void tms1k_write_r(u32 data);
	void tms1k_write_o(u16 data);
	u8 tms1k_read_k();
};

void sag_state::machine_start()
{
	save_item(NAME(m_grid));
	save_item(NAME(m_plate));
}



/*******************************************************************************
    Cartridge Init
*******************************************************************************/

DEVICE_IMAGE_LOAD_MEMBER(sag_state::cart_load)
{
	u32 size = m_cart->common_get_size("rom");

	if (size != 0x1000 && size != 0x1100 && size != 0x2000)
		return std::make_pair(image_error::INVALIDLENGTH, "Invalid ROM file size (must be 4096, 4352 or 8192 bytes)");

	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	// detect MCU on file size
	if (size == 0x1000)
	{
		// TMS1670 MCU
		if (!image.loaded_through_softlist())
			return std::make_pair(image_error::UNSUPPORTED, "Can only load TMS1670 type through software list");

		memcpy(memregion("tms1k_cpu")->base(), m_cart->get_rom_base(), size);
		m_tms1k_cpu->set_clock(375000); // approximation - RC osc. R=47K, C=47pF

		// init PLAs
		size = image.get_software_region_length("rom:mpla");
		if (size != 867)
			return std::make_pair(image_error::BADSOFTWARE, "Invalid MPLA data area size (must be 867 bytes)");

		memcpy(memregion("tms1k_cpu:mpla")->base(), image.get_software_region("rom:mpla"), size);

		size = image.get_software_region_length("rom:opla");
		if (size != 557)
			return std::make_pair(image_error::BADSOFTWARE, "Invalid OPLA data area size (must be 557 bytes)");

		memcpy(memregion("tms1k_cpu:opla")->base(), image.get_software_region("rom:opla"), size);

		subdevice<pla_device>("tms1k_cpu:mpla")->reinit();
		subdevice<pla_device>("tms1k_cpu:opla")->reinit();
	}
	else
	{
		// HD38800 MCU
		u8 *dest = memregion("hmcs40_cpu")->base();
		memcpy(dest, m_cart->get_rom_base(), size);

		// copy patterns
		if (size == 0x1100)
			memmove(dest + 0x1e80, dest + 0x1000, 0x100);

		m_hmcs40_cpu->set_clock(450000); // from main PCB
	}

	return std::make_pair(std::error_condition(), std::string());
}



/*******************************************************************************
    I/O
*******************************************************************************/

// main unit

void sag_state::update_display()
{
	// grid 0-7 are the 'pixels'
	m_display->matrix_partial(0, 8, m_grid, m_plate);

	// grid 8-13 are 7segs
	u8 seg = bitswap<7>(m_plate,4,5,6,7,8,9,10);
	m_display->matrix_partial(8, 6, m_grid >> 8, seg);
}

u8 sag_state::input_r()
{
	u8 data = 0;

	// grid 1-6 double as input mux
	for (int i = 0; i < 6; i++)
		if (BIT(m_grid, i + 1))
			data |= m_inputs[i]->read();

	return data;
}

void sag_state::speaker_w(int state)
{
	m_speaker->level_w(state);
}


// cartridge type 1: HD38800

void sag_state::hmcs40_write_r(offs_t offset, u8 data)
{
	// R0x-R3x: vfd plate
	int shift = offset * 4;
	m_plate = (m_plate & ~(0xf << shift)) | (data << shift);
	update_display();
}

void sag_state::hmcs40_write_d(u16 data)
{
	// D0: speaker out
	speaker_w(data & 1);

	// D1-D12: vfd grid (10 and 11 unused)
	m_grid = bitswap<14>(data,9,10,0,0,11,12,1,2,3,4,5,6,7,8) & 0x33ff;
	update_display();
}

u16 sag_state::hmcs40_read_d()
{
	// D13-D15: multiplexed inputs
	return input_r() << 13;
}


// cartridge type 2: TMS1670

void sag_state::tms1k_write_r(u32 data)
{
	// R0: speaker out
	speaker_w(data & 1);

	// R1-R12: vfd grid (0 and 7 unused)
	// R13,R14: vfd plate 3,2
	m_grid = bitswap<14>(data,7,8,9,10,11,12,0,1,2,3,4,5,6,0) & 0x3f7e;
	m_plate = (m_plate & 0xff0) | bitswap<2>(data,13,14) << 2;
	update_display();
}

void sag_state::tms1k_write_o(u16 data)
{
	// O0-O7: vfd plate 4-11
	m_plate = (m_plate & 0xf) | data << 4;
	update_display();
}

u8 sag_state::tms1k_read_k()
{
	// K1-K4: multiplexed inputs
	return input_r();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( sag ) // P1 = Visitor (left side), P2 = Home (right side)
	PORT_START("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_COCKTAIL PORT_NAME("P2 Button 6")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Button 6")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Button 7")

	PORT_START("IN.1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_COCKTAIL PORT_NAME("P2 Button 5")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P1 Button 5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Button 7")

	PORT_START("IN.2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL PORT_16WAY PORT_NAME("P2 Button 4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY PORT_NAME("P1 Button 2")
	PORT_BIT( 0x04, 0x04, IPT_CUSTOM ) PORT_CONDITION("FAKE", 0x03, EQUALS, 0x00) // demo

	PORT_START("IN.3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL PORT_16WAY PORT_NAME("P2 Button 3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY PORT_NAME("P1 Button 1")
	PORT_CONFNAME( 0x04, 0x00, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x00, "1" )
	PORT_CONFSETTING(    0x04, "2" )

	PORT_START("IN.4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL PORT_16WAY PORT_NAME("P2 Button 2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY PORT_NAME("P1 Button 4")
	PORT_BIT( 0x04, 0x04, IPT_CUSTOM ) PORT_CONDITION("FAKE", 0x03, EQUALS, 0x01) // 1 player

	PORT_START("IN.5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_COCKTAIL PORT_16WAY PORT_NAME("P2 Button 1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY PORT_NAME("P1 Button 3")
	PORT_CONFNAME( 0x04, 0x04, "Game" )
	PORT_CONFSETTING(    0x04, "1" )
	PORT_CONFSETTING(    0x00, "2" )

	PORT_START("FAKE") // shared IN.2/IN.4
	PORT_CONFNAME( 0x03, 0x01, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x00, "Demo" )
	PORT_CONFSETTING(    0x01, "1" )
	PORT_CONFSETTING(    0x02, "2" )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void sag_state::sag(machine_config &config)
{
	// basic machine hardware
	HD38800(config, m_hmcs40_cpu, 0);
	m_hmcs40_cpu->write_r<0>().set(FUNC(sag_state::hmcs40_write_r));
	m_hmcs40_cpu->write_r<1>().set(FUNC(sag_state::hmcs40_write_r));
	m_hmcs40_cpu->write_r<2>().set(FUNC(sag_state::hmcs40_write_r));
	m_hmcs40_cpu->write_r<3>().set(FUNC(sag_state::hmcs40_write_r));
	m_hmcs40_cpu->write_d().set(FUNC(sag_state::hmcs40_write_d));
	m_hmcs40_cpu->read_d().set(FUNC(sag_state::hmcs40_read_d));

	TMS1670(config, m_tms1k_cpu, 0);
	m_tms1k_cpu->read_k().set(FUNC(sag_state::tms1k_read_k));
	m_tms1k_cpu->write_r().set(FUNC(sag_state::tms1k_write_r));
	m_tms1k_cpu->write_o().set(FUNC(sag_state::tms1k_write_o));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8+6, 14);
	m_display->set_segmask(0x3f00, 0x7f);
	config.set_default_layout(layout_sag);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	// cartridge
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "sag_cart");
	m_cart->set_must_be_loaded(true);
	m_cart->set_device_load(FUNC(sag_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("entex_sag");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( sag )
	// nothing here yet, ROM is on the cartridge
	ROM_REGION( 0x2000, "hmcs40_cpu", ROMREGION_ERASE00 )
	ROM_REGION( 0x1000, "tms1k_cpu", ROMREGION_ERASE00 )
	ROM_REGION( 867, "tms1k_cpu:mpla", ROMREGION_ERASE00 )
	ROM_REGION( 557, "tms1k_cpu:opla", ROMREGION_ERASE00 )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY, FULLNAME, FLAGS
SYST( 1981, sag,  0,      0,      sag,     sag,   sag_state, empty_init, "Entex", "Select-A-Game Machine", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )



sage2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/***************************************************************************

        Sage II

        For memory map look at :
            http://www.thebattles.net/sage/img/SDT.pdf  (pages 14-)


        06/12/2009 Skeleton driver.

****************************************************************************/

/*

    TODO:

    - floppy loading
    - TMS9914 IEEE-488 controller
    - board 2 (4x 2651 USART)
    - Winchester controller

*/

#include "emu.h"
#include "sage2.h"
#include "bus/rs232/rs232.h"
#include "softlist_dev.h"

//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( sage2_mem )
//-------------------------------------------------

void sage2_state::sage2_mem(address_map &map)
{
	map.unmap_value_high();
	map(0xffc000, 0xffc007).rw(I8253_1_TAG, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0xffc010, 0xffc01f).noprw(); //rw(TMS9914_TAG, FUNC(tms9914_device::read), FUNC(tms9914_device::write)).umask16(0x00ff);
	map(0xffc020, 0xffc027).rw(I8255A_0_TAG, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff); // i8255, DIPs + Floppy ctrl port
	map(0xffc030, 0xffc033).rw(m_usart1, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0xffc040, 0xffc043).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0xffc050, 0xffc053).m(m_fdc, FUNC(upd765a_device::map)).umask16(0x00ff);
	map(0xffc060, 0xffc067).rw(I8255A_1_TAG, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff); // i8255, Printer
	map(0xffc070, 0xffc073).rw(m_usart0, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0xffc080, 0xffc087).mirror(0x78).rw(I8253_0_TAG, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
//  map(0xffc400, 0xffc407).rw(S2651_0_TAG, FUNC(s2651_device::read), FUNC(s2651_device::write)).umask16(0x00ff);
//  map(0xffc440, 0xffc447).rw(S2651_1_TAG, FUNC(s2651_device::read), FUNC(s2651_device::write)).umask16(0x00ff);
//  map(0xffc480, 0xffc487).rw(S2651_2_TAG, FUNC(s2651_device::read), FUNC(s2651_device::write)).umask16(0x00ff);
//  map(0xffc4c0, 0xffc4c7).rw(S2651_3_TAG, FUNC(s2651_device::read), FUNC(s2651_device::write)).umask16(0x00ff);
//  map(0xffc500, 0xffc7ff) // Winchester drive ports
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( sage2 )
//-------------------------------------------------

static INPUT_PORTS_START( sage2 )
	PORT_START("J7")
	PORT_DIPNAME( 0x07, 0x07, "Terminal Baud Rate" ) PORT_DIPLOCATION("J7:1,2,3")
	PORT_DIPSETTING(    0x07, "19200" )
	PORT_DIPSETTING(    0x06, "9600" )
	PORT_DIPSETTING(    0x05, "4800" )
	PORT_DIPSETTING(    0x04, "2400" )
	PORT_DIPSETTING(    0x03, "1200" )
	PORT_DIPSETTING(    0x02, "600" )
	PORT_DIPSETTING(    0x01, "300" )
	PORT_DIPSETTING(    0x00, "Reserved (19200)" )
	PORT_DIPNAME( 0x08, 0x08, "Parity Control" ) PORT_DIPLOCATION("J7:4")
	PORT_DIPSETTING(    0x08, "Even Parity" )
	PORT_DIPSETTING(    0x00, "Disabled" )
	PORT_DIPNAME( 0x30, 0x20, "Boot Device" ) PORT_DIPLOCATION("J7:5,6")
	PORT_DIPSETTING(    0x30, "Debugger" )
	PORT_DIPSETTING(    0x20, "Floppy Drive 0" )
	PORT_DIPSETTING(    0x10, "Winchester" )
	PORT_DIPSETTING(    0x00, "Reserved (Debugger)" )
	PORT_DIPNAME( 0x40, 0x40, "Floppy Configuration" ) PORT_DIPLOCATION("J7:7")
	PORT_DIPSETTING(    0x40, "96 TPI" )
	PORT_DIPSETTING(    0x00, "48 TPI" )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Service_Mode ) ) PORT_DIPLOCATION("J7:8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("J6")
	PORT_DIPNAME( 0x1f, 0x07, "IEEE-488 Bus Address" ) PORT_DIPLOCATION("J6:1,2,3,4,5")
	PORT_DIPSETTING(    0x00, "0" )
	PORT_DIPSETTING(    0x01, "1" )
	PORT_DIPSETTING(    0x02, "2" )
	PORT_DIPSETTING(    0x03, "3" )
	PORT_DIPSETTING(    0x04, "4" )
	PORT_DIPSETTING(    0x05, "5" )
	PORT_DIPSETTING(    0x06, "6" )
	PORT_DIPSETTING(    0x07, "7" )
	PORT_DIPSETTING(    0x08, "8" )
	PORT_DIPSETTING(    0x09, "9" )
	PORT_DIPSETTING(    0x0a, "10" )
	PORT_DIPSETTING(    0x0b, "11" )
	PORT_DIPSETTING(    0x0c, "12" )
	PORT_DIPSETTING(    0x0d, "13" )
	PORT_DIPSETTING(    0x0e, "14" )
	PORT_DIPSETTING(    0x0f, "15" )
	PORT_DIPSETTING(    0x10, "16" )
	PORT_DIPSETTING(    0x11, "17" )
	PORT_DIPSETTING(    0x12, "18" )
	PORT_DIPSETTING(    0x13, "19" )
	PORT_DIPSETTING(    0x14, "20" )
	PORT_DIPSETTING(    0x15, "21" )
	PORT_DIPSETTING(    0x16, "22" )
	PORT_DIPSETTING(    0x17, "23" )
	PORT_DIPSETTING(    0x18, "24" )
	PORT_DIPSETTING(    0x19, "25" )
	PORT_DIPSETTING(    0x1a, "26" )
	PORT_DIPSETTING(    0x1b, "27" )
	PORT_DIPSETTING(    0x1c, "28" )
	PORT_DIPSETTING(    0x1d, "29" )
	PORT_DIPSETTING(    0x1e, "30" )
	PORT_DIPSETTING(    0x1f, "31" )
	PORT_DIPNAME( 0x20, 0x00, "IEEE-488 TALK" ) PORT_DIPLOCATION("J6:6")
	PORT_DIPSETTING(    0x00, "Disabled" )
	PORT_DIPSETTING(    0x20, "Enabled" )
	PORT_DIPNAME( 0x40, 0x00, "IEEE-488 LISTEN" ) PORT_DIPLOCATION("J6:7")
	PORT_DIPSETTING(    0x00, "Disabled" )
	PORT_DIPSETTING(    0x40, "Enabled" )
	PORT_DIPNAME( 0x80, 0x00, "IEEE-488 Consecutive Addresses" ) PORT_DIPLOCATION("J6:8")
	PORT_DIPSETTING(    0x00, "1" )
	PORT_DIPSETTING(    0x80, "2" )
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  I8255A INTERFACE( ppi0_intf )
//-------------------------------------------------

/*

    IR0     U74 OUT2
    IR1     RX2I+
    IR2     TX1I+
    IR3     TX2I+
    IR4     MI-
    IR5     CNI+
    IR6     U74 OUT0
    IR7     SI+

*/


//-------------------------------------------------
//  I8255A INTERFACE( ppi0_intf )
//-------------------------------------------------

void sage2_state::ppi0_pc_w(uint8_t data)
{
	/*

	    bit     signal

	    PC0     TC+
	    PC1     RDY+
	    PC2     FDIE+
	    PC3     SL0-
	    PC4     SL1-
	    PC5     MOT-
	    PC6     PCRMP-
	    PC7     FRES+

	*/

	// floppy terminal count
	m_fdc->tc_w(BIT(data, 0));

	// floppy ready
	m_fdc->ready_w(BIT(data, 1));

	// floppy interrupt enable
	m_fdie = BIT(data, 2);
	update_fdc_int();

	// drive select
	m_floppy = nullptr;

	if (!BIT(data, 3)) m_floppy = m_floppy0->get_device();
	if (!BIT(data, 4)) m_floppy = m_floppy1->get_device();

	m_fdc->set_floppy(m_floppy);

	// floppy motor
	if (m_floppy) m_floppy->mon_w(BIT(data, 5));

	// FDC reset
	if(BIT(data, 7)) m_fdc->reset();
}


//-------------------------------------------------
//  I8255A INTERFACE( ppi1_intf )
//-------------------------------------------------

void sage2_state::write_centronics_ack(int state)
{
	if (!state)
	{
		m_pic->ir5_w(ASSERT_LINE);
	}
}

void sage2_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

void sage2_state::write_centronics_perror(int state)
{
	m_centronics_perror = state;
}

void sage2_state::write_centronics_select(int state)
{
	m_centronics_select = state;
}

void sage2_state::write_centronics_fault(int state)
{
	m_centronics_fault = state;
}

uint8_t sage2_state::ppi1_pb_r()
{
	/*

	    bit     signal

	    PB0     FDI+
	    PB1     WP+
	    PB2     RG-
	    PB3     CD-
	    PB4     BUSY
	    PB5     PAPER
	    PB6     SEL
	    PB7     FAULT-

	*/

	uint8_t data = 0;

	// floppy interrupt
	data = m_fdc->get_irq();

	// floppy write protected
	data = (m_floppy ? m_floppy->wpt_r() : 1) << 1;

	// RS-232 ring indicator

	// RS-232 carrier detect

	// centronics
	data |= m_centronics_busy << 4;
	data |= m_centronics_perror << 5;
	data |= m_centronics_select << 6;
	data |= m_centronics_fault << 7;

	return data;
}

void sage2_state::ppi1_pc_w(uint8_t data)
{
	/*

	    bit     signal

	    PC0     PRES-
	    PC1     U8 SC+
	    PC2     SI+
	    PC3     LEDR+
	    PC4     STROBE-
	    PC5     PRIME-
	    PC6     U38 CL-
	    PC7     RMI-

	*/

	if (!BIT(data, 0))
	{
		// clear parity error interrupt
		m_maincpu->set_input_line(M68K_IRQ_7, CLEAR_LINE);
	}

	// s? interrupt
	m_pic->ir7_w(BIT(data, 2));

	// processor LED
	m_led = BIT(data, 3);

	// centronics
	m_centronics->write_strobe(BIT(data, 4));
	m_centronics->write_init(BIT(data, 5));

	if (!BIT(data, 6))
	{
		// clear ACK interrupt
		m_pic->ir5_w(CLEAR_LINE);
	}

	if (!BIT(data, 7))
	{
		// clear modem interrupt
		m_pic->ir4_w(CLEAR_LINE);
	}
}

void sage2_state::br1_w(int state)
{
	m_usart0->write_txc(state);
	m_usart0->write_rxc(state);
}

void sage2_state::br2_w(int state)
{
	m_usart1->write_txc(state);
	m_usart1->write_rxc(state);
}

//-------------------------------------------------
//  upd765_interface fdc_intf
//-------------------------------------------------

static void sage2_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD); // Mitsubishi M4859
}

void sage2_state::update_fdc_int()
{
	m_maincpu->set_input_line(M68K_IRQ_6, m_fdie && m_fdc_int);
}

void sage2_state::fdc_irq(int state)
{
	m_fdc_int = state;
	update_fdc_int();
}


static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_19200 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_19200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( sage2 )
//-------------------------------------------------

void sage2_state::machine_start()
{
	m_led.resolve();
	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x000000, 0x001fff, 0x07e000, m_rom->base()); // Avoid the 68000 reading from lalaland in its reset handler
}

void sage2_state::machine_reset()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.unmap_readwrite(0x000000, 0x07ffff);
	program.install_rom(0x000000, 0x001fff, 0x07e000, m_rom->base());
	program.install_read_handler(0xfe0000, 0xfe3fff, read16sm_delegate(*this, FUNC(sage2_state::rom_r)));
}

uint16_t sage2_state::rom_r(offs_t offset)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.unmap_readwrite(0x000000, 0x07ffff);
	program.install_ram(0, m_ram->size()-1, m_ram->pointer());
	program.install_rom(0xfe0000, 0xfe1fff, 0x002000, m_rom->base());
	return program.read_word(0xfe0000 | (offset*2));
}

//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

//-------------------------------------------------
//  machine_config( sage2 )
//-------------------------------------------------

void sage2_state::sage2(machine_config &config)
{
	// basic machine hardware
	M68000(config, m_maincpu, XTAL(16'000'000)/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &sage2_state::sage2_mem);

	// devices
	PIC8259(config, m_pic, 0);
	m_pic->out_int_callback().set_inputline(m_maincpu, M68K_IRQ_1);

	i8255_device &ppi0(I8255A(config, I8255A_0_TAG));
	ppi0.in_pa_callback().set_ioport("J7");
	ppi0.in_pb_callback().set_ioport("J6");
	ppi0.out_pc_callback().set(FUNC(sage2_state::ppi0_pc_w));

	i8255_device &ppi1(I8255A(config, I8255A_1_TAG));
	ppi1.out_pa_callback().set("cent_data_out", FUNC(output_latch_device::write));
	ppi1.in_pb_callback().set(FUNC(sage2_state::ppi1_pb_r));
	ppi1.out_pc_callback().set(FUNC(sage2_state::ppi1_pc_w));

	pit8253_device &i8253_0(PIT8253(config, I8253_0_TAG, 0));
	i8253_0.set_clk<0>(0); // from U75 OUT0
	i8253_0.out_handler<0>().set(m_pic, FUNC(pic8259_device::ir6_w));
	i8253_0.set_clk<1>(XTAL(16'000'000)/2/125);
	i8253_0.out_handler<1>().set(I8253_0_TAG, FUNC(pit8253_device::write_clk2));
	i8253_0.set_clk<2>(0); // from OUT2
	i8253_0.out_handler<2>().set(m_pic, FUNC(pic8259_device::ir0_w));

	pit8253_device &i8253_1(PIT8253(config, I8253_1_TAG, 0));
	i8253_1.set_clk<0>(XTAL(16'000'000)/2/125);
	i8253_1.out_handler<0>().set(I8253_0_TAG, FUNC(pit8253_device::write_clk0));
	i8253_1.set_clk<1>(XTAL(16'000'000)/2/13);
	i8253_1.out_handler<1>().set(FUNC(sage2_state::br1_w));
	i8253_1.set_clk<2>(XTAL(16'000'000)/2/13);
	i8253_1.out_handler<2>().set(FUNC(sage2_state::br2_w));

	I8251(config, m_usart0, 0);
	m_usart0->txd_handler().set(RS232_A_TAG, FUNC(rs232_port_device::write_txd));
	m_usart0->dtr_handler().set(RS232_A_TAG, FUNC(rs232_port_device::write_dtr));
	m_usart0->rts_handler().set(RS232_A_TAG, FUNC(rs232_port_device::write_rts));
	m_usart0->rxrdy_handler().set_inputline(M68000_TAG, M68K_IRQ_5);
	m_usart0->txrdy_handler().set(m_pic, FUNC(pic8259_device::ir2_w));

	rs232_port_device &rs232a(RS232_PORT(config, RS232_A_TAG, default_rs232_devices, "terminal"));
	rs232a.rxd_handler().set(m_usart0, FUNC(i8251_device::write_rxd));
	rs232a.dsr_handler().set(m_usart0, FUNC(i8251_device::write_dsr));
	rs232a.cts_handler().set(m_usart0, FUNC(i8251_device::write_cts));
	rs232a.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	I8251(config, m_usart1, 0);
	m_usart1->txd_handler().set(RS232_B_TAG, FUNC(rs232_port_device::write_txd));
	m_usart1->dtr_handler().set(RS232_B_TAG, FUNC(rs232_port_device::write_dtr));
	m_usart1->rts_handler().set(RS232_B_TAG, FUNC(rs232_port_device::write_rts));
	m_usart1->rxrdy_handler().set(m_pic, FUNC(pic8259_device::ir1_w));
	m_usart1->txrdy_handler().set(m_pic, FUNC(pic8259_device::ir3_w));

	rs232_port_device &rs232b(RS232_PORT(config, RS232_B_TAG, default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_usart1, FUNC(i8251_device::write_rxd));
	rs232b.dsr_handler().set(m_usart1, FUNC(i8251_device::write_dsr));

	UPD765A(config, m_fdc, 8'000'000, false, false);
	m_fdc->intrq_wr_callback().set(FUNC(sage2_state::fdc_irq));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set(FUNC(sage2_state::write_centronics_ack));
	m_centronics->busy_handler().set(FUNC(sage2_state::write_centronics_busy));
	m_centronics->perror_handler().set(FUNC(sage2_state::write_centronics_perror));
	m_centronics->select_handler().set(FUNC(sage2_state::write_centronics_select));
	m_centronics->fault_handler().set(FUNC(sage2_state::write_centronics_fault));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	FLOPPY_CONNECTOR(config, UPD765_TAG ":0", sage2_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, UPD765_TAG ":1", sage2_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);

	IEEE488(config, m_ieee488);

	// internal ram
	RAM(config, RAM_TAG).set_default_size("512K");

	// software list
	SOFTWARE_LIST(config, "flop_list").set_original("sage2");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( sage2 )
//-------------------------------------------------

ROM_START( sage2 )
	ROM_REGION16_BE( 0x2000, M68000_TAG, 0 )
	ROM_LOAD16_BYTE( "sage2.u18", 0x0001, 0x1000, CRC(ca9b312d) SHA1(99436a6d166aa5280c3b2d28355c4d20528fe48c) )
	ROM_LOAD16_BYTE( "sage2.u17", 0x0000, 0x1000, CRC(27e25045) SHA1(041cd9d4617473d089f31f18cbb375046c3b61bb) )
ROM_END



//**************************************************************************
//  DRIVER INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  DRIVER_INIT( sage2 )
//-------------------------------------------------

void sage2_state::init_sage2()
{
}



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  STATE        INIT        COMPANY            FULLNAME   FLAGS
COMP( 1982, sage2,  0,      0,      sage2,   sage2, sage2_state, init_sage2, "Sage Technology", "Sage II", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



samcoupe.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Lee Hammerton, Dirk Best
/***************************************************************************

    Miles Gordon Technology SAM Coupe

    Note:
    - Early ROMs are buggy. Version 1.0 requires the command CALL 229385
      (256k machine) or CALL 491529 (512k machine) to execute the bootstrap.

    TODO:
    - Better timing, emulate memory contention

    Hardware:
    - Z80 at 6 MHz
    - Custom ASIC handling the system and video
    - SAA1099 sound
    - XTAL X1: 24 MHz (system clock)
    - XTAL X2: 4.433619 MHz (RGB encoder)

***************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/bankdev.h"
#include "machine/ram.h"
#include "sound/saa1099.h"
#include "sound/spkrdev.h"
#include "imagedev/cassette.h"
#include "bus/samcoupe/drive/drive.h"
#include "bus/samcoupe/expansion/expansion.h"
#include "bus/samcoupe/mouse/mouseport.h"
#include "formats/tzx_cas.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

//**************************************************************************
//  CONSTANTS
//**************************************************************************

#define SAM_ENABLE_JOYSTICK (0)

#define SAM_BLOCK           8

#define SAM_TOTAL_WIDTH     SAM_BLOCK*96
#define SAM_TOTAL_HEIGHT    312
#define SAM_SCREEN_WIDTH    SAM_BLOCK*64
#define SAM_SCREEN_HEIGHT   192
#define SAM_BORDER_LEFT     SAM_BLOCK*4
#define SAM_BORDER_RIGHT    SAM_BLOCK*4
#define SAM_BORDER_TOP      37
#define SAM_BORDER_BOTTOM   46


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class samcoupe_state :  public driver_device
{
public:
	samcoupe_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_mem(*this, "mem"),
		m_io(*this, "io"),
		m_ram(*this, RAM_TAG),
		m_bios(*this, "maincpu"),
		m_screen(*this, "screen"),
		m_speaker(*this, "speaker"),
		m_cassette(*this, "cassette"),
		m_mouse(*this, "mouseport"),
		m_keyboard(*this, "kbd_%u", 0U),
#if SAM_ENABLE_JOYSTICK
		m_joystick(*this, "joy_%u", 0U)
#endif
		m_drive(*this, "drive%u", 1),
		m_expansion(*this, "exp")
	{
	}

	void samcoupe(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<address_map_bank_device> m_mem;
	required_device<address_map_bank_device> m_io;
	required_device<ram_device> m_ram;
	required_memory_region m_bios;
	required_device<screen_device> m_screen;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cassette;
	required_device<samcoupe_mouse_port_device> m_mouse;
	required_ioport_array<9> m_keyboard;
#if SAM_ENABLE_JOYSTICK
	required_ioport_array<2> m_joystick;
#endif
	required_device_array<samcoupe_drive_port_device, 2> m_drive;
	required_device<samcoupe_expansion_device> m_expansion;

	enum
	{
		INT_LINE    = 0x01,
		INT_MOUSE   = 0x02,
		INT_MIDIIN  = 0x04,
		INT_FRAME   = 0x08,
		INT_MIDIOUT = 0x10
	};

	void postload();

	// video
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	bitmap_ind16 m_bitmap;

	emu_timer *m_video_update_timer;
	emu_timer *m_irq_off_timer;

	uint8_t m_lmpr, m_hmpr, m_vmpr; /* memory pages */
	uint8_t m_border;           /* border */
	uint8_t m_clut[16];         /* color lookup table, 16 entries */
	uint8_t m_line_int;         /* line interrupt */
	uint8_t m_status;           /* status register */

	/* attribute */
	uint8_t m_attribute;

	uint8_t *m_videoram;

	uint8_t pen_r(offs_t offset);
	void clut_w(offs_t offset, uint8_t data);
	uint8_t status_r(offs_t offset);
	void line_int_w(uint8_t data);
	uint8_t lmpr_r();
	void lmpr_w(uint8_t data);
	uint8_t hmpr_r();
	void hmpr_w(uint8_t data);
	uint8_t vmpr_r();
	void vmpr_w(uint8_t data);
	uint8_t midi_r();
	void midi_w(uint8_t data);
	uint8_t keyboard_r(offs_t offset);
	void border_w(uint8_t data);
	uint8_t attributes_r();

	void samcoupe_palette(palette_device &palette) const;
	INTERRUPT_GEN_MEMBER(samcoupe_frame_interrupt);
	TIMER_CALLBACK_MEMBER(irq_off);
	TIMER_CALLBACK_MEMBER(sam_video_update_callback);

	void draw_mode4_line(int y, int hpos);
	void draw_mode3_line(int y, int hpos);
	void draw_mode12_block(bitmap_ind16 &bitmap, int vpos, int hpos, uint8_t mask);
	void draw_mode2_line(int y, int hpos);
	void draw_mode1_line(int y, int hpos);

	void samcoupe_irq(uint8_t src);

	// memory
	uint8_t mem_r(offs_t offset);
	void mem_w(offs_t offset, uint8_t data);
	uint8_t io_r(offs_t offset);
	void io_w(offs_t offset, uint8_t data);

	uint8_t mem_page_r(int page, offs_t offset);
	void mem_page_w(int page, offs_t offset, uint8_t data);

	uint8_t mem_block_a_r(offs_t offset);
	void mem_block_a_w(offs_t offset, uint8_t data);
	uint8_t mem_block_b_r(offs_t offset);
	void mem_block_b_w(offs_t offset, uint8_t data);
	uint8_t mem_block_c_r(offs_t offset);
	void mem_block_c_w(offs_t offset, uint8_t data);
	uint8_t mem_block_d_r(offs_t offset);
	void mem_block_d_w(offs_t offset, uint8_t data);

	void mem_map_base(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	void io_map_base(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	int m_pages; // total internal memory pages (16 or 32)
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void samcoupe_state::mem_map_base(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(samcoupe_state::mem_r), FUNC(samcoupe_state::mem_w));
}

void samcoupe_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).ram().rw(FUNC(samcoupe_state::mem_block_a_r), FUNC(samcoupe_state::mem_block_a_w));
	map(0x4000, 0x7fff).ram().rw(FUNC(samcoupe_state::mem_block_b_r), FUNC(samcoupe_state::mem_block_b_w));
	map(0x8000, 0xbfff).ram().rw(FUNC(samcoupe_state::mem_block_c_r), FUNC(samcoupe_state::mem_block_c_w));
	map(0xc000, 0xffff).ram().rw(FUNC(samcoupe_state::mem_block_d_r), FUNC(samcoupe_state::mem_block_d_w));
}

void samcoupe_state::io_map_base(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(samcoupe_state::io_r), FUNC(samcoupe_state::io_w));
}

void samcoupe_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00e0, 0x00e7).mirror(0xff00).rw(m_drive[0], FUNC(samcoupe_drive_port_device::read), FUNC(samcoupe_drive_port_device::write));
	map(0x00e8, 0x00ef).mirror(0xff00).noprw(); // "print" - handled by expansion devices
	map(0x00f0, 0x00f7).mirror(0xff00).rw(m_drive[1], FUNC(samcoupe_drive_port_device::read), FUNC(samcoupe_drive_port_device::write));
	map(0x00f8, 0x00f8).select(0xff00).rw(FUNC(samcoupe_state::pen_r), FUNC(samcoupe_state::clut_w));
	map(0x00f9, 0x00f9).select(0xff00).rw(FUNC(samcoupe_state::status_r), FUNC(samcoupe_state::line_int_w));
	map(0x00fa, 0x00fa).select(0xff00).rw(FUNC(samcoupe_state::lmpr_r), FUNC(samcoupe_state::lmpr_w));
	map(0x00fb, 0x00fb).select(0xff00).rw(FUNC(samcoupe_state::hmpr_r), FUNC(samcoupe_state::hmpr_w));
	map(0x00fc, 0x00fc).select(0xff00).rw(FUNC(samcoupe_state::vmpr_r), FUNC(samcoupe_state::vmpr_w));
	map(0x00fd, 0x00fd).select(0xff00).rw(FUNC(samcoupe_state::midi_r), FUNC(samcoupe_state::midi_w));
	map(0x00fe, 0x00fe).select(0xff00).rw(FUNC(samcoupe_state::keyboard_r), FUNC(samcoupe_state::border_w));
	map(0x00ff, 0x00ff).select(0xff00).r(FUNC(samcoupe_state::attributes_r));
	map(0x00ff, 0x00ff).mirror(0xfe00).w("saa1099", FUNC(saa1099_device::data_w));
	map(0x01ff, 0x01ff).mirror(0xfe00).w("saa1099", FUNC(saa1099_device::control_w));
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( samcoupe )
	PORT_START("kbd_0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)     PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)     PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR('?')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)     PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)     PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(F3))

	PORT_START("kbd_1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)     PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)     PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)     PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)     PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR('{')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)     PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR('}')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(F6))

	PORT_START("kbd_2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)     PORT_CHAR('q') PORT_CHAR('Q') PORT_CHAR('<')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)     PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR('>')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)     PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)     PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR('[')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)     PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(']')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(F9))

	PORT_START("kbd_3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)        PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)        PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)        PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)        PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)        PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)      PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)      PORT_CHAR('\t')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))

	PORT_START("kbd_4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)         PORT_CHAR('0') PORT_CHAR('~')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)         PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)         PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)         PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)         PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)     PORT_CHAR('-') PORT_CHAR('/')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)    PORT_CHAR('+') PORT_CHAR('*')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DELETE") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)

	PORT_START("kbd_5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)          PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('=') PORT_CHAR('_')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('"') PORT_CHAR(0xA9)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F0") PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(F10))

	PORT_START("kbd_6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)     PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR(0xA3)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)     PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)     PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)     PORT_CHAR('h') PORT_CHAR('H') PORT_CHAR('^')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("EDIT") PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START("kbd_7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SYMBOL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)     PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)     PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)     PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)  PORT_CHAR('.')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("INV") PORT_CODE(KEYCODE_SLASH) PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CHAR('\\')

	PORT_START("kbd_8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)     PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)   PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)   PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)  PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

#if SAM_ENABLE_JOYSTICK
	/* Sam Coupe has single 9-pin ATARI-compatible connector but supports 2 joysticks via a splitter,
	   this works by using a different ground for each stick (pin 8: stick 1 gnd, pin 9: stick 2 gnd.)
	   Joysticks overlay number keys 6-0 for the stick 1 and 1-5 for stick 2 (same scheme as ZX Spectrum) */

	PORT_START("joy_0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON1)        PORT_PLAYER(1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)    PORT_8WAY PORT_PLAYER(1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)  PORT_8WAY PORT_PLAYER(1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)  PORT_8WAY PORT_PLAYER(1)

	PORT_START("joy_1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)  PORT_8WAY PORT_PLAYER(2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) 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_BUTTON1)        PORT_PLAYER(2)
#endif
INPUT_PORTS_END


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

void samcoupe_state::samcoupe_palette(palette_device &palette) const
{
	for (int i = 0; i < 128; i++)
	{
		uint8_t const b = bitswap<3>(i, 4, 0, 3); //  blue: bits 403
		uint8_t const r = bitswap<3>(i, 5, 1, 3); //   red: bits 513
		uint8_t const g = bitswap<3>(i, 6, 2, 3); // green: bits 623

		palette.set_pen_color(i, pal3bit(r), pal3bit(g), pal3bit(b));
	}
}

void samcoupe_state::video_start()
{
	m_screen->register_screen_bitmap(m_bitmap);
}

uint32_t samcoupe_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
	return 0;
}

void samcoupe_state::draw_mode4_line(int y, int hpos)
{
	/* get start address */
	uint8_t const *vram = m_videoram + ((y - SAM_BORDER_TOP) * 128) + ((hpos - SAM_BORDER_LEFT) / 4);

	for (int i = 0; i < (SAM_BLOCK*2)/4; i++)
	{
		/* draw 2 pixels (doublewidth) */
		m_bitmap.pix(y, hpos + i * 4 + 0) = m_clut[(*vram >> 4) & 0x0f];
		m_bitmap.pix(y, hpos + i * 4 + 1) = m_clut[(*vram >> 4) & 0x0f];
		m_bitmap.pix(y, hpos + i * 4 + 2) = m_clut[(*vram >> 0) & 0x0f];
		m_bitmap.pix(y, hpos + i * 4 + 3) = m_clut[(*vram >> 0) & 0x0f];

		/* move to next address */
		vram++;

		/* attribute register contains the third displayed byte */
		if (i == 2)
			m_attribute = *vram;
	}
}

void samcoupe_state::draw_mode3_line(int y, int hpos)
{
	/* get start address */
	uint8_t const *vram = m_videoram + ((y - SAM_BORDER_TOP) * 128) + ((hpos - SAM_BORDER_LEFT) / 4);

	for (int i = 0; i < (SAM_BLOCK*2)/4; i++)
	{
		/* draw 4 pixels */
		m_bitmap.pix(y, hpos + i * 4 + 0) = m_clut[(*vram >> 6) & 0x03];
		m_bitmap.pix(y, hpos + i * 4 + 1) = m_clut[(*vram >> 4) & 0x03];
		m_bitmap.pix(y, hpos + i * 4 + 2) = m_clut[(*vram >> 2) & 0x03];
		m_bitmap.pix(y, hpos + i * 4 + 3) = m_clut[(*vram >> 0) & 0x03];

		/* move to next address */
		vram++;

		/* attribute register contains the third displayed byte */
		if (i == 2)
			m_attribute = *vram;
	}
}

void samcoupe_state::draw_mode12_block(bitmap_ind16 &bitmap, int vpos, int hpos, uint8_t mask)
{
	/* extract colors from attribute */
	uint8_t ink = m_clut[((m_attribute >> 3) & 0x08) | (m_attribute & 0x07)];
	uint8_t pap = m_clut[(m_attribute >> 3) & 0x0f];

	/* draw block of 8 pixels (doubled to 16) */
	for (int i = 0; i < SAM_BLOCK; i++)
	{
		bitmap.pix(vpos, hpos + i*2 + 0) = BIT(mask, 7 - i) ? ink : pap;
		bitmap.pix(vpos, hpos + i*2 + 1) = BIT(mask, 7 - i) ? ink : pap;
	}
}

void samcoupe_state::draw_mode2_line(int y, int hpos)
{
	int cell = (y - SAM_BORDER_TOP) * 32 + (hpos - SAM_BORDER_LEFT) / SAM_BLOCK / 2;

	uint8_t mask = m_videoram[cell];
	m_attribute = m_videoram[cell + 0x2000];

	draw_mode12_block(m_bitmap, y, hpos, mask);
}

void samcoupe_state::draw_mode1_line(int y, int hpos)
{
	uint8_t mask = m_videoram[((((y - SAM_BORDER_TOP) & 0xc0) << 5) | (((y - SAM_BORDER_TOP) & 0x07) << 8) | (((y - SAM_BORDER_TOP) & 0x38) << 2)) + (hpos - SAM_BORDER_LEFT) / SAM_BLOCK / 2];
	m_attribute = m_videoram[32*192 + (((y - SAM_BORDER_TOP) & 0xf8) << 2) + (hpos - SAM_BORDER_LEFT) / SAM_BLOCK / 2];

	draw_mode12_block(m_bitmap, y, hpos, mask);
}

TIMER_CALLBACK_MEMBER(samcoupe_state::sam_video_update_callback)
{
	int vpos = m_screen->vpos();
	int hpos = m_screen->hpos();

	int next_vpos = vpos;
	int next_hpos = hpos + SAM_BLOCK*2;

	/* next scanline? */
	if (next_hpos >= SAM_BORDER_LEFT + SAM_SCREEN_WIDTH + SAM_BORDER_RIGHT)
	{
		next_vpos = (vpos + 1) % (SAM_BORDER_TOP + SAM_SCREEN_HEIGHT + SAM_BORDER_BOTTOM);
		next_hpos = 0;
	}

	/* display disabled? (only in mode 3 or 4) */
	if (BIT(m_vmpr, 6) && BIT(m_border, 7))
	{
		m_bitmap.plot_box(hpos, vpos, SAM_BLOCK*2, 1, 0);
	}
	else
	{
		/* border area? */
		if (vpos < SAM_BORDER_TOP || vpos >= SAM_BORDER_TOP + SAM_SCREEN_HEIGHT || hpos < SAM_BORDER_LEFT || hpos >= SAM_BORDER_LEFT + SAM_SCREEN_WIDTH)
		{
			m_attribute = 0xff;
			m_bitmap.plot_box(hpos, vpos, SAM_BLOCK*2, 1, m_clut[(m_border & 0x20) >> 2 | (m_border & 0x07)]);
		}
		else
		{
			/* main screen area */
			switch ((m_vmpr & 0x60) >> 5)
			{
			case 0: draw_mode1_line(vpos, hpos); break;
			case 1: draw_mode2_line(vpos, hpos); break;
			case 2: draw_mode3_line(vpos, hpos); break;
			case 3: draw_mode4_line(vpos, hpos); break;
			}
		}
	}

	/* do we need to trigger the scanline interrupt (interrupt happens at the start of the right border before the specified line)? */
	if (m_line_int < SAM_SCREEN_HEIGHT && hpos == SAM_BORDER_LEFT + SAM_SCREEN_WIDTH && vpos == (m_line_int + SAM_BORDER_TOP - 1))
		samcoupe_irq(INT_LINE);

	/* schedule next update */
	m_video_update_timer->adjust(m_screen->time_until_pos(next_vpos, next_hpos));
}


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

TIMER_CALLBACK_MEMBER(samcoupe_state::irq_off)
{
	/* adjust STATUS register */
	m_status |= param;

	/* clear interrupt */
	if ((m_status & 0x1f) == 0x1f)
		m_maincpu->set_input_line(0, CLEAR_LINE);
}

void samcoupe_state::samcoupe_irq(uint8_t src)
{
	/* assert irq and a timer to set it off again */
	m_maincpu->set_input_line(0, ASSERT_LINE);
	m_irq_off_timer->adjust(attotime::from_usec(20), src);

	/* adjust STATUS register */
	m_status &= ~src;
}

INTERRUPT_GEN_MEMBER(samcoupe_state::samcoupe_frame_interrupt)
{
	/* signal frame interrupt */
	samcoupe_irq(INT_FRAME);
}

void samcoupe_state::postload()
{
	// restore videoram pointer
	vmpr_w(m_vmpr);
}

void samcoupe_state::machine_start()
{
	// make sure the ram device is already running
	if (!m_ram->started())
		throw device_missing_dependencies();

	m_pages = m_ram->size() / 0x4000; // 16 or 32

	/* schedule our video updates */
	m_video_update_timer = timer_alloc(FUNC(samcoupe_state::sam_video_update_callback), this);
	m_video_update_timer->adjust(m_screen->time_until_pos(0, 0));

	// register for save states
	save_item(NAME(m_lmpr));
	save_item(NAME(m_hmpr));
	save_item(NAME(m_vmpr));
	save_item(NAME(m_border));
	save_item(NAME(m_clut), 16);
	save_item(NAME(m_line_int));
	save_item(NAME(m_status));
	save_item(NAME(m_attribute));

	machine().save().register_postload(save_prepost_delegate(FUNC(samcoupe_state::postload), this));

	// allocate machine timers
	m_irq_off_timer = timer_alloc(FUNC(samcoupe_state::irq_off), this);
	m_irq_off_timer->adjust(attotime::never);
}

void samcoupe_state::machine_reset()
{
	m_lmpr = 0x0f;      // ROM0 paged in, ROM1 paged out, RAM Banks
	m_hmpr = 0x01;
	vmpr_w(0x81);
	m_line_int = 0xff;  // line interrupts disabled
	m_status = 0x1f;    // no interrupts active
}


//**************************************************************************
//  ASIC
//**************************************************************************

uint8_t samcoupe_state::pen_r(offs_t offset)
{
	uint8_t data;

	if (offset & 0x100)
	{
		// HPEN
		int vpos = m_screen->vpos();

		// return the current screen line or 192 for the border area
		if (vpos < SAM_BORDER_TOP || vpos >= SAM_BORDER_TOP + SAM_SCREEN_HEIGHT)
			data = 192;
		else
			data = vpos - SAM_BORDER_TOP;
	}
	else
	{
		// LPEN

		// 765432--  screen horizontal position
		// ------1-  midi status (currently transferring)
		// -------0  border clut bit 0

		data = (m_screen->hpos() & 0xfc) | (0 << 1) | (m_border & 1);
	}

	return data;
}

void samcoupe_state::clut_w(offs_t offset, uint8_t data)
{
	m_clut[(offset >> 8) & 0x0f] = data & 0x7f;
}

uint8_t samcoupe_state::status_r(offs_t offset)
{
	// 765-----  keyboard matrix k8 to k6
	// ---4----  midi out interrupt
	// ----3---  frame interrupt
	// -----2--  midi in interrupt
	// ------1-  mouse interrupt
	// -------0  line interrupt

	uint8_t data = 0xe0;

	for (int i = 0; i < 8; i++)
		if (BIT(offset, i + 8) == 0)
			data &= m_keyboard[i]->read() & 0xe0;

	data |= m_status;

	return data;
}

void samcoupe_state::line_int_w(uint8_t data)
{
	m_line_int = data;
}

uint8_t samcoupe_state::lmpr_r()
{
	return m_lmpr;
}

void samcoupe_state::lmpr_w(uint8_t data)
{
	m_lmpr = data;
}

uint8_t samcoupe_state::hmpr_r()
{
	return m_hmpr;
}

void samcoupe_state::hmpr_w(uint8_t data)
{
	m_hmpr = data;
}

uint8_t samcoupe_state::vmpr_r()
{
	return m_vmpr;
}

void samcoupe_state::vmpr_w(uint8_t data)
{
	m_vmpr = data;

	if (BIT(m_vmpr, 6))   /* if bit set in 2 bank screen mode */
		m_videoram = &m_ram->pointer()[(m_vmpr & 0x1e) << 14];
	else
		m_videoram = &m_ram->pointer()[(m_vmpr & 0x1f) << 14];
}

uint8_t samcoupe_state::midi_r()
{
	logerror("Read from MIDI port\n");
	return 0xff;
}

void samcoupe_state::midi_w(uint8_t data)
{
	logerror("Write to MIDI port: 0x%02x\n", data);
}

uint8_t samcoupe_state::keyboard_r(offs_t offset)
{
	// 7-------  status bit external memory
	// -6------  cassette input
	// --5-----  light pen input
	// ---43210  keyboard matrix k5 to k1, mouse

	uint8_t data = 0x1f;

	for (int i = 0; i < 8; i++)
		if (BIT(offset, i + 8) == 0)
			data &= m_keyboard[i]->read() & 0x1f;

#if SAM_ENABLE_JOYSTICK
	if (BIT(offset, 12) == 0) data &= m_joystick[0]->read() & 0x1f;
	if (BIT(offset, 11) == 0) data &= m_joystick[1]->read() & 0x1f;
#endif

	if (offset == 0xff00)
	{
		data &= m_keyboard[8]->read() & 0x1f;
		data &= m_mouse->read() & 0x1f;
	}

	data |= 1 << 5;
	data |= (m_cassette->input() > 0 ? 1 : 0) << 6;
	data |= 1 << 7;

	return data;
}

void samcoupe_state::border_w(uint8_t data)
{
	// 7-------  screen off in mode 3/4
	// -6------  midi through
	// --5--210  clut address for border color
	// ---4----  beeper
	// ----3---  cassette output

	m_border = data;

	m_cassette->output(BIT(data, 3) ? -1.0 : +1.0);
	m_speaker->level_w(BIT(data, 4));
}

uint8_t samcoupe_state::attributes_r()
{
	return m_attribute;
}


//**************************************************************************
//  MEMORY
//**************************************************************************

//-------------------------------------------------
//  mem_r/w - program space accesses
//-------------------------------------------------

uint8_t samcoupe_state::mem_r(offs_t offset)
{
	uint8_t data = 0xff;

	int xmem = BIT(m_hmpr, 7) && BIT(offset, 15) ? 1 : 0;

	m_expansion->xmem_w(xmem);

	data &= m_expansion->mreq_r(offset);

	if (xmem == 0)
		data &= m_mem->read8(offset);

	return data;
}

void samcoupe_state::mem_w(offs_t offset, uint8_t data)
{
	int xmem = BIT(m_hmpr, 7) && BIT(offset, 15) ? 1 : 0;

	m_expansion->xmem_w(xmem);

	m_expansion->mreq_w(offset, data);

	if (xmem == 0)
		m_mem->write8(offset, data);
}

//-------------------------------------------------
//  io_r/w - i/o space accesses
//-------------------------------------------------

uint8_t samcoupe_state::io_r(offs_t offset)
{
	uint8_t data = 0xff;

	m_expansion->print_w((offset & 0xf8) == 0xe8); // 0xe8 to 0xef

	data &= m_expansion->iorq_r(offset);
	data &= m_io->read8(offset);

	return data;
}

void samcoupe_state::io_w(offs_t offset, uint8_t data)
{
	m_expansion->print_w((offset & 0xf8) == 0xe8);

	m_expansion->iorq_w(offset, data);
	m_io->write8(offset, data);
}

//-------------------------------------------------
//  mem_page_r/w - read/write memory pages
//-------------------------------------------------

uint8_t samcoupe_state::mem_page_r(int page, offs_t offset)
{
	if (page < m_pages)
		return m_ram->pointer()[(page << 14) | offset];
	else
		return 0xff;
}

void samcoupe_state::mem_page_w(int page, offs_t offset, uint8_t data)
{
	if (page < m_pages)
		m_ram->pointer()[(page << 14) | offset] = data;
}

//-------------------------------------------------
//  mem_block_a/b/c/d_r/w - read/write memory blocks
//-------------------------------------------------

uint8_t samcoupe_state::mem_block_a_r(offs_t offset)
{
	int page = m_lmpr & 0x1f;

	// ram enabled?
	if (BIT(m_lmpr, 5))
		return mem_page_r(page, offset);
	else
		return m_bios->base()[offset];
}

void samcoupe_state::mem_block_a_w(offs_t offset, uint8_t data)
{
	int page = m_lmpr & 0x1f;

	// not write protected?
	if (BIT(m_lmpr, 7) == 0)
		mem_page_w(page, offset, data);
}

uint8_t samcoupe_state::mem_block_b_r(offs_t offset)
{
	int page = ((m_lmpr & 0x1f) + 1) & 0x1f;
	return mem_page_r(page, offset);
}

void samcoupe_state::mem_block_b_w(offs_t offset, uint8_t data)
{
	int page = ((m_lmpr & 0x1f) + 1) & 0x1f;
	mem_page_w(page, offset, data);
}

uint8_t samcoupe_state::mem_block_c_r(offs_t offset)
{
	int page = m_hmpr & 0x1f;
	return mem_page_r(page, offset);
}

void samcoupe_state::mem_block_c_w(offs_t offset, uint8_t data)
{
	int page = m_hmpr & 0x1f;
	mem_page_w(page, offset, data);
}

uint8_t samcoupe_state::mem_block_d_r(offs_t offset)
{
	int page = ((m_hmpr & 0x1f) + 1) & 0x1f;

	// ram enabled?
	if (BIT(m_lmpr, 6) == 0)
		return mem_page_r(page, offset);
	else
		return m_bios->base()[0x4000 + offset];
}

void samcoupe_state::mem_block_d_w(offs_t offset, uint8_t data)
{
	int page = ((m_hmpr & 0x1f) + 1) & 0x1f;
	mem_page_w(page, offset, data);
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void samcoupe_state::samcoupe(machine_config &config)
{
	Z80(config, m_maincpu, 24_MHz_XTAL / 4); // 6 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &samcoupe_state::mem_map_base);
	m_maincpu->set_addrmap(AS_IO, &samcoupe_state::io_map_base);
	m_maincpu->set_vblank_int("screen", FUNC(samcoupe_state::samcoupe_frame_interrupt));

	ADDRESS_MAP_BANK(config, m_mem);
	m_mem->set_addrmap(AS_PROGRAM, &samcoupe_state::mem_map);
	m_mem->set_data_width(8);
	m_mem->set_addr_width(16);

	ADDRESS_MAP_BANK(config, m_io);
	m_io->set_addrmap(AS_PROGRAM, &samcoupe_state::io_map);
	m_io->set_data_width(8);
	m_io->set_addr_width(16);

	RAM(config, RAM_TAG).set_default_size("512K").set_extra_options("256K");

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(24_MHz_XTAL / 2, SAM_TOTAL_WIDTH,  0, SAM_BORDER_LEFT + SAM_SCREEN_WIDTH + SAM_BORDER_RIGHT,
									   SAM_TOTAL_HEIGHT, 0, SAM_BORDER_TOP + SAM_SCREEN_HEIGHT + SAM_BORDER_BOTTOM);
	m_screen->set_screen_update(FUNC(samcoupe_state::screen_update));
	m_screen->set_palette("palette");

	PALETTE(config, "palette", FUNC(samcoupe_state::samcoupe_palette), 128);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);
	SAA1099(config, "saa1099", 24_MHz_XTAL / 3).add_route(ALL_OUTPUTS, "mono", 0.50); // 8 MHz

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(tzx_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("samcoupe_cass");
	SOFTWARE_LIST(config, "cass_list").set_original("samcoupe_cass");

	// expansion ports
	SAMCOUPE_MOUSE_PORT(config, m_mouse, samcoupe_mouse_modules);

	SAMCOUPE_DRIVE_PORT(config, m_drive[0], samcoupe_drive_modules, "floppy");
	SAMCOUPE_DRIVE_PORT(config, m_drive[1], samcoupe_drive_modules, "floppy");

	SOFTWARE_LIST(config, "flop_list").set_original("samcoupe_flop");

	SAMCOUPE_EXPANSION(config, m_expansion, samcoupe_expansion_modules);
}


//**************************************************************************
//  ROM DEFINTIONS
//**************************************************************************

/*
    The bios is actually 32K. This is the combined version of the two old 16K MESS roms.
    It does match the 3.0 one the most, but the first half differs in one byte
    and in the second half, the case of the "plc" in the company string differs.
*/
ROM_START( samcoupe )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0,  "31",  "v3.1" )
	ROMX_LOAD( "rom31.z5",  0x0000, 0x8000, CRC(0b7e3585) SHA1(c86601633fb61a8c517f7657aad9af4e6870f2ee), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1,  "30",  "v3.0" )
	ROMX_LOAD( "rom30.z5",  0x0000, 0x8000, CRC(e535c25d) SHA1(d390f0be420dfb12b1e54a4f528b5055d7d97e2a), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2,  "25",  "v2.5" )
	ROMX_LOAD( "rom25.z5",  0x0000, 0x8000, CRC(ddadd358) SHA1(a25ed85a0f1134ac3a481a3225f24a8bd3a790cf), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3,  "24",  "v2.4" )
	ROMX_LOAD( "rom24.z5",  0x0000, 0x8000, CRC(bb23fee4) SHA1(10cd911ba237dd2cf0c2637be1ad6745b7cc89b9), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4,  "21",  "v2.1" )
	ROMX_LOAD( "rom21.z5",  0x0000, 0x8000, CRC(f6804b46) SHA1(11dcac5fdea782cdac03b4d0d7ac25d88547eefe), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5,  "20",  "v2.0" )
	ROMX_LOAD( "rom20.z5",  0x0000, 0x8000, CRC(eaf32054) SHA1(41736323f0236649f2d5fe111f900def8db93a13), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6,  "181", "v1.81" )
	ROMX_LOAD( "rom181.z5", 0x0000, 0x8000, CRC(d25e1de1) SHA1(cb0fa79e4d5f7df0b57ede08ea7ecc9ae152f534), ROM_BIOS(6) )
	ROM_SYSTEM_BIOS( 7,  "18",  "v1.8" )
	ROMX_LOAD( "rom18.z5",  0x0000, 0x8000, CRC(f626063f) SHA1(485e7d9e9a4f8a70c0f93cd6e69ff12269438829), ROM_BIOS(7) )
	ROM_SYSTEM_BIOS( 8,  "14",  "v1.4" )
	ROMX_LOAD( "rom14.z5",  0x0000, 0x8000, CRC(08799596) SHA1(b4e596051f2748dee9481ea4af7d15ccddc1e1b5), ROM_BIOS(8) )
	ROM_SYSTEM_BIOS( 9,  "13",  "v1.3" )
	ROMX_LOAD( "rom13.z5",  0x0000, 0x8000, CRC(2093768c) SHA1(af8d348fd080b18a4cbe9ed69d254be7be330146), ROM_BIOS(9) )
	ROM_SYSTEM_BIOS( 10, "12",  "v1.2" )
	ROMX_LOAD( "rom12.z5",  0x0000, 0x8000, CRC(7fe37dd8) SHA1(9339a0c1f72e8512c6f32dec15ab7d6c3bb04151), ROM_BIOS(10) )
	ROM_SYSTEM_BIOS( 11, "10",  "v1.0" )
	ROMX_LOAD( "rom10.z5",  0x0000, 0x8000, CRC(3659d31f) SHA1(d3de7bb74e04d5b4dc7477f70de54d540b1bcc07), ROM_BIOS(11) )
	ROM_SYSTEM_BIOS( 12, "04",  "v0.4" )
	ROMX_LOAD( "rom04.z5",  0x0000, 0x8000, CRC(f439e84e) SHA1(8bc457a5c764b0bb0aa7008c57f28c30248fc6a4), ROM_BIOS(12) )
	ROM_SYSTEM_BIOS( 13, "01",  "v0.1" )
	ROMX_LOAD( "rom01.z5",  0x0000, 0x8000, CRC(c04acfdf) SHA1(8976ed005c14905eec1215f0a5c28aa686a7dda2), ROM_BIOS(13) )
	ROM_SYSTEM_BIOS( 14, "atom",  "ATOM HDD Interface Auto Boot" )
	ROMX_LOAD( "atom.z5",   0x0000, 0x8000, CRC(dec75f58) SHA1(cd342579f066c0863e4f769c2e6757085e21b0a1), ROM_BIOS(14) )
ROM_END

} // anonymous namespace


/***************************************************************************
    GAME DRIVERS
***************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY                        FULLNAME     FLAGS
COMP( 1989, samcoupe, 0,      0,      samcoupe, samcoupe, samcoupe_state, empty_init, "Miles Gordon Technology plc", u8"SAM Coupé", MACHINE_SUPPORTS_SAVE )



sansa_fuze.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

/* Sansa Fuze */

// info can be found at
// http://www.rockbox.org/wiki/SansaFuze / http://www.rockbox.org/wiki/SansaAMSFirmware
// http://forums.rockbox.org/index.php?topic=14064
// http://daniel.haxx.se/sansa/ams.html

#include "emu.h"
#include "cpu/arm7/arm7.h"


namespace {

class sansa_fuze_state : public driver_device
{
public:
	sansa_fuze_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void sansa_fuze(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	void sansa_fuze_map(address_map &map) ATTR_COLD;
};



void sansa_fuze_state::sansa_fuze_map(address_map &map)
{
	map(0x00000000, 0x0001ffff).rom();

	map(0x80000000, 0x8001ffff).rom().region("maincpu", 0x00000);
	map(0x81000000, 0x81ffffff).ram();
}


static INPUT_PORTS_START( sansa_fuze )
INPUT_PORTS_END


void sansa_fuze_state::sansa_fuze(machine_config &config)
{
	/* basic machine hardware */
	ARM7(config, m_maincpu, 50000000); // arm based, speed unknown
	m_maincpu->set_addrmap(AS_PROGRAM, &sansa_fuze_state::sansa_fuze_map);
}

ROM_START( sanfuze2 )
	ROM_REGION(0x20000, "maincpu", 0 )
	// this rom was dumped using the RockBox (custom firmware) debugging features, it's presumably the internal ROM
	// I don't know if it's specific to the Fuze 2, or shared.
	ROM_LOAD( "sanza.rom",   0x00000, 0x20000, CRC(a93674b0) SHA1(3a17dfc9ad31f07fdd66e3dac94c9494c59f3203) )

	// these are firmware update files, we probably need to extract the data from them in order to use them with the ROM
	// above and get things booting.
	// http://forums.sandisk.com/t5/Sansa-Fuze/Sansa-Fuze-Firmware-Update-01-02-31-amp-02-03-33/td-p/139175

	ROM_REGION(0xf00000, "updates2", 0 )
	/// 02.03.33  (for Fuze 2?)
	ROM_LOAD( "020333_fuzpa.bin",   0x00000, 0xf00000, CRC(045ec5be) SHA1(d951d93ff1c0a50343e0cf8e6997930b7c94e5ad) ) // original filename fuzpa.bin
	ROM_LOAD( "020333_clpp_data.dat",   0x00000, 0x566a0d, CRC(2093569c) SHA1(7882abcf000860a3071f5afe91719530bc54c68a) ) // original filename clpp_data.dat, actually a 6 minute MP3 advertisement for slotRadio

	ROM_REGION(0xf00000, "updates1", 0 )
	// 01.02.31 (for Fuze 1?)
	ROM_LOAD( "010231_fuzea.bin",   0x00000, 0xf00000, CRC(48b264cb) SHA1(387d5270fdd2fb7ba3901be59651a55167700768) ) // original filename fuzea.bin

ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE     INPUT       CLASS             INIT        COMPANY    FULLNAME        FLAGS
CONS( 200?, sanfuze2, 0,      0,      sansa_fuze, sansa_fuze, sansa_fuze_state, empty_init, "Sandisk", "Sansa Fuze 2", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



sapi1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************

SAPI-1 driver by Miodrag Milanovic

2008-09-09 Preliminary driver.

2010-12-07 Added some code to allow sapizps3 to read its rom.
With no available docs, the i/o ports are a guess. The ram
allocation is based on the actions of the various bios roms.
Port 25 is used as a jump vector. in a,(25); ld l,a; jp(hl).

2012-04-19 Connected sapizps3 to a terminal. It is trying to
load a 128-byte boot sector from a floppy disk.
Modernised driver.
Connected sapizps2 to ascii keyboard. System is now usable.
According to wikipedia, sapi1 & 2 have cassette facility,
while sapi3 uses 8 inch floppy disk.

Cassette:
2019-07-26 Cassette implemented @ 1200 baud.
- sapi1 -bios 1 and sapizps2 -bios 2 have a common system allowing
  files to be shared among them. (working)
- sapi1 -bios 0 uses its own system on ports 40-43, not compatible
  with the above. (working)
- sapizps2 -bios 0 and -bios 1: unknown how to save a file. They cannot
  load files from any of the above working systems. (not working)


ToDo:
- sapi3 is trying to read a disk, so there is no response after showing the logo

Unable to proceed due to no info available (& in English).

ZPS stands for "Základní Počítačová Sestava" (basic computer system).

****************************************************************************/


#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "bus/rs232/rs232.h"
#include "machine/ay31015.h"
#include "machine/clock.h"
#include "imagedev/cassette.h"
#include "machine/keyboard.h"
#include "machine/ram.h"
#include "video/mc6845.h"
#include "emupal.h"
#include "screen.h"
#include "machine/timer.h"
#include "speaker.h"


namespace {

class sapi_state : public driver_device
{
public:
	sapi_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_p_videoram(*this, "videoram")
		, m_bank1(*this, "bank1")
		, m_io_keyboard(*this, "LINE%u", 0U)
		, m_maincpu(*this, "maincpu")
		, m_cass(*this, "cassette")
		, m_uart(*this, "uart")
		, m_uart_clock(*this, "uart_clock")
		, m_v24(*this, "v24")
		, m_palette(*this, "palette")
	{ }

	void sapi3(machine_config &config);
	void sapi1(machine_config &config);
	void sapi2(machine_config &config);
	void sapi3a(machine_config &config);
	void sapi3b(machine_config &config);

	void init_sapizps3();
	void init_sapizps3a();
	void init_sapizps3b();

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	optional_shared_ptr<uint8_t> m_p_videoram;
	void kbd_put(u8 data);
	uint8_t sapi1_keyboard_r();
	void sapi1_keyboard_w(uint8_t data);
	uint8_t sapi2_keyboard_status_r();
	uint8_t sapi2_keyboard_data_r();
	uint8_t sapi3_0c_r();
	void sapi3_00_w(uint8_t data);
	uint8_t sapi3_25_r();
	void sapi3_25_w(uint8_t data);
	void port10_w(uint8_t data);
	void port11_w(uint8_t data);
	void port13_w(uint8_t data);
	void port43_w(uint8_t data);
	uint8_t port10_r();
	uint8_t port11_r();
	uint8_t port40_r();
	uint8_t port41_r();
	MC6845_UPDATE_ROW(crtc_update_row);
	int si();
	void so(int state);

	uint32_t screen_update_sapi1(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	uint32_t screen_update_sapi3(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void sapi1_mem(address_map &map) ATTR_COLD;
	void sapi1_io(address_map &map) ATTR_COLD;
	void sapi2_mem(address_map &map) ATTR_COLD;
	void sapi2_io(address_map &map) ATTR_COLD;
	void sapi3_io(address_map &map) ATTR_COLD;
	void sapi3_mem(address_map &map) ATTR_COLD;
	void sapi3a_io(address_map &map) ATTR_COLD;
	void sapi3a_mem(address_map &map) ATTR_COLD;
	void sapi3b_io(address_map &map) ATTR_COLD;
	void sapi3b_mem(address_map &map) ATTR_COLD;

	uint8_t m_term_data = 0U;
	uint8_t m_keyboard_mask = 0U;
	uint8_t m_refresh_counter = 0U;
	uint8_t m_zps3_25 = 0U;
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);
	void kansas_w(int state);
	u8 m_cass_data[4]{};
	bool m_cassinbit = 0, m_cassoutbit = 0, m_cassold = 0;
	bool m_ier = 0, m_iet = 0;
	optional_memory_bank m_bank1;   // Only for sapi3
	required_ioport_array<5> m_io_keyboard;
	required_device<cpu_device> m_maincpu;
	optional_device<cassette_image_device> m_cass;
	optional_device<ay31015_device> m_uart;
	optional_device<clock_device> m_uart_clock;
	optional_device<rs232_port_device> m_v24;
	optional_device<palette_device> m_palette;
};

static const uint8_t MHB2501[] = {
	0x0c,0x11,0x13,0x15,0x17,0x10,0x0e,0x00, // @
	0x04,0x0a,0x11,0x11,0x1f,0x11,0x11,0x00, // A
	0x1e,0x11,0x11,0x1e,0x11,0x11,0x1e,0x00, // B
	0x0e,0x11,0x10,0x10,0x10,0x11,0x0e,0x00, // C
	0x1e,0x09,0x09,0x09,0x09,0x09,0x1e,0x00, // D
	0x1f,0x10,0x10,0x1e,0x10,0x10,0x1f,0x00, // E
	0x1f,0x10,0x10,0x1e,0x10,0x10,0x10,0x00, // F
	0x0e,0x11,0x10,0x10,0x13,0x11,0x0f,0x00, // G

	0x11,0x11,0x11,0x1f,0x11,0x11,0x11,0x00, // H
	0x0e,0x04,0x04,0x04,0x04,0x04,0x0e,0x00, // I
	0x01,0x01,0x01,0x01,0x11,0x11,0x0e,0x00, // J
	0x11,0x12,0x14,0x18,0x14,0x12,0x11,0x00, // K
	0x10,0x10,0x10,0x10,0x10,0x10,0x1f,0x00, // L
	0x11,0x1b,0x15,0x15,0x11,0x11,0x11,0x00, // M
	0x11,0x11,0x19,0x15,0x13,0x11,0x11,0x00, // N
	0x0e,0x11,0x11,0x11,0x11,0x11,0x0e,0x00, // O

	0x1e,0x11,0x11,0x1e,0x10,0x10,0x10,0x00, // P
	0x0e,0x11,0x11,0x11,0x15,0x12,0x0d,0x00, // Q
	0x1e,0x11,0x11,0x1e,0x14,0x12,0x11,0x00, // R
	0x0e,0x11,0x10,0x0e,0x01,0x11,0x0e,0x00, // S
	0x1f,0x04,0x04,0x04,0x04,0x04,0x04,0x00, // T
	0x11,0x11,0x11,0x11,0x11,0x11,0x0e,0x00, // U
	0x11,0x11,0x11,0x0a,0x0a,0x04,0x04,0x00, // V
	0x11,0x11,0x11,0x15,0x15,0x15,0x0a,0x00, // W

	0x11,0x11,0x0a,0x04,0x0a,0x11,0x11,0x00, // X
	0x11,0x11,0x0a,0x04,0x04,0x04,0x04,0x00, // Y
	0x1f,0x01,0x02,0x04,0x08,0x10,0x1f,0x00, // Z
	0x1c,0x10,0x10,0x10,0x10,0x10,0x1c,0x00, // [
	0x00,0x10,0x08,0x04,0x02,0x01,0x00,0x00, // backslash
	0x07,0x01,0x01,0x01,0x01,0x01,0x07,0x00, // ]
	0x0e,0x11,0x00,0x00,0x00,0x00,0x00,0x00, // ^
	0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x00, // _

	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //
	0x04,0x04,0x04,0x04,0x04,0x00,0x04,0x00, // !
	0x0a,0x0a,0x0a,0x00,0x00,0x00,0x00,0x00, // "
	0x0a,0x0a,0x1f,0x0a,0x1f,0x0a,0x0a,0x00, // #
	0x00,0x11,0x0e,0x0a,0x0e,0x11,0x00,0x00, //
	0x18,0x19,0x02,0x04,0x08,0x13,0x03,0x00, // %
	0x04,0x0a,0x0a,0x0c,0x15,0x12,0x0d,0x00, // &
	0x04,0x04,0x08,0x00,0x00,0x00,0x00,0x00, // '

	0x02,0x04,0x08,0x08,0x08,0x04,0x02,0x00, // (
	0x08,0x04,0x02,0x02,0x02,0x04,0x08,0x00, // )
	0x00,0x04,0x15,0x0e,0x15,0x04,0x00,0x00, // *
	0x00,0x04,0x04,0x1f,0x04,0x04,0x00,0x00, // +
	0x00,0x00,0x00,0x00,0x08,0x08,0x10,0x00, // ,
	0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x00, // -
	0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00, // .
	0x00,0x01,0x02,0x04,0x08,0x10,0x00,0x00, // /

	0x0e,0x11,0x13,0x15,0x19,0x11,0x0e,0x00, // 0
	0x04,0x0c,0x04,0x04,0x04,0x04,0x0e,0x00, // 1
	0x0e,0x11,0x01,0x06,0x08,0x10,0x1f,0x00, // 2
	0x1f,0x01,0x02,0x06,0x01,0x11,0x0e,0x00, // 3
	0x02,0x06,0x0a,0x12,0x1f,0x02,0x02,0x00, // 4
	0x1f,0x10,0x1e,0x01,0x01,0x11,0x0e,0x00, // 5
	0x07,0x08,0x10,0x1e,0x11,0x11,0x0e,0x00, // 6
	0x1f,0x01,0x02,0x04,0x08,0x08,0x08,0x00, // 7

	0x0e,0x11,0x11,0x0e,0x11,0x11,0x0e,0x00, // 8
	0x0e,0x11,0x11,0x0f,0x01,0x02,0x1c,0x00, // 9
	0x00,0x00,0x00,0x00,0x08,0x00,0x08,0x00, // :
	0x00,0x00,0x04,0x00,0x04,0x04,0x08,0x00, // ;
	0x02,0x04,0x08,0x10,0x08,0x04,0x02,0x00, // <
	0x00,0x00,0x1f,0x00,0x1f,0x00,0x00,0x00, // =
	0x08,0x04,0x02,0x01,0x02,0x04,0x08,0x00, // >
	0x0e,0x11,0x01,0x02,0x04,0x00,0x04,0x00  // ?
};


/* Address maps */
void sapi_state::sapi1_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).rom();
	map(0x1000, 0x1fff).rom(); // Extension ROM
	map(0x2000, 0x23ff).ram();
	map(0x2400, 0x27ff).rw(FUNC(sapi_state::sapi1_keyboard_r), FUNC(sapi_state::sapi1_keyboard_w)); // PORT 0 - keyboard
	//map(0x2800, 0x2bff).noprw(); // PORT 1
	//map(0x2c00, 0x2fff).noprw(); // PORT 2
	//map(0x3000, 0x33ff).noprw(); // 3214
	map(0x3800, 0x3fff).ram().share("videoram"); // AND-1 (video RAM)
	map(0x4000, 0x7fff).ram(); // REM-1
}

void sapi_state::sapi1_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	// -bios 1
	map(0x10, 0x10).rw(FUNC(sapi_state::port10_r),FUNC(sapi_state::port10_w));
	map(0x11, 0x11).rw(FUNC(sapi_state::port11_r),FUNC(sapi_state::port11_w));
	map(0x12, 0x12).rw(m_uart, FUNC(ay51013_device::receive), FUNC(ay51013_device::transmit));
	map(0x13, 0x13).w(FUNC(sapi_state::port13_w));
	// -bios 0
	map(0x40, 0x40).r(FUNC(sapi_state::port40_r)); // get inverted byte
	map(0x41, 0x41).r(FUNC(sapi_state::port41_r)); // get status
	map(0x42, 0x42).nopw();   // strobe DS
	map(0x43, 0x43).w(FUNC(sapi_state::port43_w)); // send inverted byte
}

void sapi_state::sapi2_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).rom();
	map(0x1000, 0x1fff).rom(); // Extension ROM
	map(0x2000, 0x23ff).ram();
	map(0x2400, 0x27ff).r(FUNC(sapi_state::sapi2_keyboard_status_r));
	map(0x2800, 0x28ff).r(FUNC(sapi_state::sapi2_keyboard_data_r));
	map(0x3800, 0x3fff).ram().share("videoram"); // AND-1 (video RAM)
	map(0x4000, 0x7fff).ram(); // REM-1
}

void sapi_state::sapi2_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x10, 0x10).rw(FUNC(sapi_state::port10_r),FUNC(sapi_state::port10_w));
	map(0x11, 0x11).rw(FUNC(sapi_state::port11_r),FUNC(sapi_state::port11_w));
	map(0x12, 0x12).rw(m_uart, FUNC(ay51013_device::receive), FUNC(ay51013_device::transmit));
	map(0x13, 0x13).w(FUNC(sapi_state::port13_w));
}

void sapi_state::sapi3_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).ram().bankrw("bank1");
	map(0x0800, 0xf7ff).ram();
	map(0xf800, 0xffff).ram().share("videoram");
}

void sapi_state::sapi3a_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).ram().bankrw("bank1");
	map(0x0800, 0xf7ff).ram();
	map(0xf800, 0xfdff).rom();
	map(0xfe00, 0xffff).ram();
}

void sapi_state::sapi3b_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).ram().bankrw("bank1");
	map(0x0800, 0xafff).ram();
	map(0xb000, 0xbfff).ram().share("videoram");
	map(0xc000, 0xffff).ram();
}

void sapi_state::sapi3_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x00).w(FUNC(sapi_state::sapi3_00_w));
	map(0x25, 0x25).rw(FUNC(sapi_state::sapi3_25_r), FUNC(sapi_state::sapi3_25_w));
}

void sapi_state::sapi3a_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x00).w(FUNC(sapi_state::sapi3_00_w));
	map(0x10, 0x10).rw(FUNC(sapi_state::port10_r), FUNC(sapi_state::port10_w));
	map(0x11, 0x11).rw(FUNC(sapi_state::port11_r), FUNC(sapi_state::port11_w));
	map(0x12, 0x12).rw(m_uart, FUNC(ay51013_device::receive), FUNC(ay51013_device::transmit));
	map(0x13, 0x13).w(FUNC(sapi_state::port13_w));
	map(0x25, 0x25).rw(FUNC(sapi_state::sapi3_25_r), FUNC(sapi_state::sapi3_25_w));
}

void sapi_state::sapi3b_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x00).w(FUNC(sapi_state::sapi3_00_w));
	map(0x0c, 0x0c).r(FUNC(sapi_state::sapi3_0c_r));
	map(0x25, 0x25).rw(FUNC(sapi_state::sapi3_25_r), FUNC(sapi_state::sapi3_25_w));
	map(0xe0, 0xe0).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0xe1, 0xe1).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
}

/* Input ports */
static INPUT_PORTS_START( sapi1 )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('~')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR(';')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('+')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('/')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('\'')

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR(0xA4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR(',')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('-')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR(':')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('<')

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('"')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('.')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('*')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('@')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('>')

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('?')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('=')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('#')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')

	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
INPUT_PORTS_END



/**************************************

    Video

**************************************/

uint32_t sapi_state::screen_update_sapi1(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for(uint8_t y = 0; y < 24; y++ )
	{
		uint16_t addr = y*64;
		uint16_t xpos = 0;
		for(uint8_t x = 0; x < 40; x++ )
		{
			uint8_t chr = m_p_videoram[addr + x];
			uint8_t attr = (chr >> 6) & 3;
			chr &= 0x3f;
			for(uint8_t ra = 0; ra < 9; ra++ )
			{
				for(uint8_t b = 0; b < 6; b++ )
				{
					bool val = 0;

					if (ra==8)
					{
						if (attr==2)
							val = BIT(m_refresh_counter, 5);
					}
					else
					{
						val = BIT(MHB2501[(chr<<3) | ra], 5-b);
						if (attr==1)
							val = BIT(m_refresh_counter, 5) ? val : 0;
					}

					if(attr==3)
					{
						bitmap.pix(y*9+ra, xpos+2*b   ) = val;
						bitmap.pix(y*9+ra, xpos+2*b+1 ) = val;
					}
					else
					{
						bitmap.pix(y*9+ra, xpos+b ) = val;
					}
				}
			}
			xpos+= (attr==3) ? 12 : 6;
			if (xpos>=6*40) break;
		}
	}
	m_refresh_counter++;
	return 0;
}

// The attributes seem to be different on this one, they need to be understood, so disabled for now
uint32_t sapi_state::screen_update_sapi3(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for(uint8_t y = 0; y < 20; y++ )
	{
		uint16_t addr = y*64;
		uint16_t xpos = 0;
		for(uint8_t x = 0; x < 40; x++ )
		{
			uint8_t chr = m_p_videoram[addr + x];
			uint8_t attr = 0;//(chr >> 6) & 3;
			if (chr > 0x3f)
				chr &= 0x1f;

			for(uint8_t ra = 0; ra < 9; ra++ )
			{
				for(uint8_t b = 0; b < 6; b++ )
				{
					bool val = 0;

					if (ra==8)
					{
						if (attr==2)
							val = BIT(m_refresh_counter, 5);
					}
					else
					{
						val = BIT(MHB2501[(chr<<3) | ra], 5-b);
						if (attr==1)
							val = BIT(m_refresh_counter, 5) ? val : 0;
					}

					if(attr==3)
					{
						bitmap.pix(y*9+ra, xpos+2*b   ) = val;
						bitmap.pix(y*9+ra, xpos+2*b+1 ) = val;
					}
					else
					{
						bitmap.pix(y*9+ra, xpos+b ) = val;
					}
				}
			}
			xpos+= (attr==3) ? 12 : 6;
			if (xpos>=6*40) break;
		}
	}
	m_refresh_counter++;
	return 0;
}

MC6845_UPDATE_ROW( sapi_state::crtc_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	uint32_t *p = &bitmap.pix(y);

	for (uint16_t x = 0; x < x_count; x++)
	{
		uint8_t inv = 0, gfx = 0;
		if (x == cursor_x) inv ^= 0xff;
		uint16_t mem = (2*(ma + x)) & 0xfff;
		uint8_t chr = m_p_videoram[mem] & 0x3f;

		if (ra < 8)
			gfx = MHB2501[(chr<<3) | ra] ^ inv;

		/* Display a scanline of a character */
		*p++ = palette[BIT(gfx, 5)];
		*p++ = palette[BIT(gfx, 4)];
		*p++ = palette[BIT(gfx, 3)];
		*p++ = palette[BIT(gfx, 2)];
		*p++ = palette[BIT(gfx, 1)];
		*p++ = palette[BIT(gfx, 0)];
	}
}

/**************************************

    Keyboard

**************************************/

uint8_t sapi_state::sapi1_keyboard_r()
{
	uint8_t key = 0xff;
	if (BIT(m_keyboard_mask, 0)) key &= m_io_keyboard[0]->read();
	if (BIT(m_keyboard_mask, 1)) key &= m_io_keyboard[1]->read();
	if (BIT(m_keyboard_mask, 2)) key &= m_io_keyboard[2]->read();
	if (BIT(m_keyboard_mask, 3)) key &= m_io_keyboard[3]->read();
	if (BIT(m_keyboard_mask, 4)) key &= m_io_keyboard[4]->read();
	return key;
}

void sapi_state::sapi1_keyboard_w(uint8_t data)
{
	m_keyboard_mask = (data ^ 0xff ) & 0x1f;
}

uint8_t sapi_state::sapi2_keyboard_status_r()
{
	return (m_term_data) ? 0 : 1;
}

uint8_t sapi_state::sapi2_keyboard_data_r()
{
	uint8_t ret = ~m_term_data;
	m_term_data = 0;
	return ret;
}

uint8_t sapi_state::port10_r()
{
	uint8_t result = 0;
	result |= m_uart->tbmt_r() || m_uart->dav_r();
	result |= m_uart->or_r() << 1;
	result |= m_uart->fe_r() << 2;
	result |= m_uart->pe_r() << 3;
	result |= m_cassinbit    << 4;
	if (m_v24)
	{
		result |= m_v24->dcd_r() << 5;
		result |= m_v24->dsr_r() << 6;
		result |= m_v24->cts_r() << 7;
	}
	return result;
}

void sapi_state::port10_w(uint8_t data)
{
	if (m_v24)
	{
		m_v24->write_rts(BIT(data, 0));
		m_v24->write_dtr(BIT(data, 1));
	}
	// WD2 = BRK
	// WD3 = S1 - allow cassin?
	// WD4 = KAZ - cass or rs232
	// WD5 = START - motor
	// WD6 = IET - allow tmbt to cause irq
	// WD7 = IER - allow dav to cause irq
	m_iet = BIT(data, 6);
	m_ier = BIT(data, 7);
}

uint8_t sapi_state::port11_r()
{
	u8 data = 0x3f;
	data |= m_uart->dav_r() ? 0x80 : 0;
	data |= m_uart->tbmt_r() ? 0x40 : 0;
	return data;
}

void sapi_state::port11_w(uint8_t data)
{
	m_uart->write_np(BIT(data, 0));
	m_uart->write_tsb(BIT(data, 1));
	m_uart->write_nb1(BIT(data, 3));
	m_uart->write_nb2(BIT(data, 2));
	m_uart->write_eps(BIT(data, 4));
	m_uart->write_cs(1);
	m_uart->write_cs(0);
}

void sapi_state::port13_w(uint8_t data)
{
	// really pulsed by K155AG3 (=74123N): R29=22k, C16=220 (output combined with master reset)
	m_uart->write_xr(0);
	m_uart->write_xr(1);
}

uint8_t sapi_state::port40_r()
{
	return ~m_uart->receive();
}

uint8_t sapi_state::port41_r()
{
	u8 data = 0x7e;
	data |= m_uart->dav_r() ? 0 : 1;
	data |= m_uart->tbmt_r() ? 0x80 : 0;
	return data;
}

void sapi_state::port43_w(uint8_t data)
{
	m_uart->transmit(~data);
}

int sapi_state::si()
{
	return m_cassinbit;
}

void sapi_state::so(int state)
{
	m_cassoutbit = state;
}

void sapi_state::kansas_w(int state)
{
	if ((m_cass->get_state() & CASSETTE_MASK_UISTATE) == CASSETTE_RECORD)
	{
		// incoming @19200Hz
		u8 twobit = m_cass_data[3] & 15;

		if (state)
		{
			if (twobit == 0)
				m_cassold = m_cassoutbit;

			if (m_cassold)
				m_cass->output(BIT(m_cass_data[3], 2) ? -1.0 : +1.0); // 2400Hz
			else
				m_cass->output(BIT(m_cass_data[3], 3) ? -1.0 : +1.0); // 1200Hz

			m_cass_data[3]++;
		}
	}

	m_uart->write_tcp(state);
	m_uart->write_rcp(state);
}

TIMER_DEVICE_CALLBACK_MEMBER( sapi_state::kansas_r )
{
	// no tape - set to idle
	m_cass_data[1]++;
	if (m_cass_data[1] > 48)
	{
		m_cass_data[1] = 48;
		m_cassinbit = 1;
	}

	// check for interrupts  (not needed)
//  if ((m_uart->tbmt_r() && m_iet) || (m_uart->dav_r() && m_ier))
//      m_maincpu->set_input_line(I8085_INTR_LINE, HOLD_LINE);

	if ((m_cass->get_state() & CASSETTE_MASK_UISTATE) != CASSETTE_PLAY)
		return;

	/* cassette - turn 1200/2400Hz to a bit */
	m_cass_data[1]++;
	uint8_t cass_ws = (m_cass->input() > +0.04) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		m_cassinbit = (m_cass_data[1] < 24) ? 1 : 0;
		m_cass_data[1] = 0;
	}
}

/**************************************

    Machine

**************************************/

void sapi_state::kbd_put(u8 data)
{
	m_term_data = data;
}

uint8_t sapi_state::sapi3_0c_r()
{
	return 0xc0;
}

/* switch out the rom shadow */
void sapi_state::sapi3_00_w(uint8_t data)
{
	m_bank1->set_entry(0);
}

/* to stop execution in random ram */
uint8_t sapi_state::sapi3_25_r()
{
	return m_zps3_25;
}

void sapi_state::sapi3_25_w(uint8_t data)
{
	m_zps3_25 = data & 0xfc; //??
}

void sapi_state::machine_reset()
{
	m_keyboard_mask = 0;
	m_refresh_counter = 0x20;

	if (m_uart)
	{
		// setup uart to 8N2 for sapi1 -bios 0
		m_uart->write_np(1);
		m_uart->write_tsb(1);
		m_uart->write_nb1(1);
		m_uart->write_nb2(1);
		m_uart->write_eps(1);
		m_uart->write_cs(1);
		m_uart->write_cs(0);
	}

	if (m_bank1)
		m_bank1->set_entry(1);
}

void sapi_state::machine_start()
{
	save_item(NAME(m_term_data));
	save_item(NAME(m_keyboard_mask));
	save_item(NAME(m_refresh_counter));
	save_item(NAME(m_zps3_25));
	save_item(NAME(m_cass_data));
	save_item(NAME(m_cassinbit));
	save_item(NAME(m_cassoutbit));
	save_item(NAME(m_cassold));
	save_item(NAME(m_ier));
	save_item(NAME(m_iet));

	m_term_data = 0;
}

void sapi_state::init_sapizps3()
{
	uint8_t *RAM = memregion("maincpu")->base();
	m_bank1->configure_entries(0, 2, &RAM[0x0000], 0x10000);
}

void sapi_state::init_sapizps3a()
{
	uint8_t *RAM = memregion("maincpu")->base();
	m_bank1->configure_entries(0, 2, &RAM[0x0000], 0xf800);
	m_uart->write_swe(0);
}

void sapi_state::init_sapizps3b()
{
	uint8_t *RAM = memregion("maincpu")->base();
	m_bank1->configure_entries(0, 2, &RAM[0x0000], 0x10000);
}


/* Machine driver */
void sapi_state::sapi1(machine_config &config)
{
	/* basic machine hardware */
	I8080A(config, m_maincpu, 18_MHz_XTAL / 9); // Tesla MHB8080A + MHB8224 + MHB8228
	m_maincpu->set_addrmap(AS_PROGRAM, &sapi_state::sapi1_mem);
	m_maincpu->set_addrmap(AS_IO, &sapi_state::sapi1_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(40*6, 24*9);
	screen.set_visarea(0, 40*6-1, 0, 24*9-1);
	screen.set_screen_update(FUNC(sapi_state::screen_update_sapi1));
	screen.set_palette(m_palette);

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("64K");

	// cassette is connected to the uart
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	SPEAKER(config, "mono").front_center();
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	// uart
	AY31015(config, m_uart);   // MHB1012
	m_uart->read_si_callback().set(FUNC(sapi_state::si));
	m_uart->write_so_callback().set(FUNC(sapi_state::so));
	m_uart->set_auto_rdav(true);

	CLOCK(config, m_uart_clock, 12.288_MHz_XTAL / 640);   // 19200
	m_uart_clock->signal_handler().set(FUNC(sapi_state::kansas_w));
	TIMER(config, "kansas_r").configure_periodic(FUNC(sapi_state::kansas_r), attotime::from_hz(40000));
}

void sapi_state::sapi2(machine_config &config)
{
	sapi1(config);
	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &sapi_state::sapi2_mem);
	m_maincpu->set_addrmap(AS_IO, &sapi_state::sapi2_io);

	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(sapi_state::kbd_put));
}

void sapi_state::sapi3(machine_config &config)
{
	sapi2(config);
	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &sapi_state::sapi3_mem);
	m_maincpu->set_addrmap(AS_IO, &sapi_state::sapi3_io);

	screen_device &screen(*subdevice<screen_device>("screen"));
	screen.set_size(40*6, 20*9);
	screen.set_visarea(0, 40*6-1, 0, 20*9-1);
	screen.set_screen_update(FUNC(sapi_state::screen_update_sapi3));

	config.device_remove("cassette");
	config.device_remove("uart_clock");
	config.device_remove("kansas_r");
	config.device_remove("mono");
	config.device_remove("uart");
}

void sapi_state::sapi3b(machine_config &config)
{
	sapi3(config);
	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &sapi_state::sapi3b_mem);
	m_maincpu->set_addrmap(AS_IO, &sapi_state::sapi3b_io);

	mc6845_device &crtc(MC6845(config, "crtc", 1008000)); // guess
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(6);
	crtc.set_update_row_callback(FUNC(sapi_state::crtc_update_row));

	subdevice<screen_device>("screen")->set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	subdevice<screen_device>("screen")->set_no_palette();
}

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 ) // high bit stripped off in software
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void sapi_state::sapi3a(machine_config &config)
{
	/* basic machine hardware */
	I8080A(config, m_maincpu, 18_MHz_XTAL / 9); // Tesla MHB8080A + MHB8224 + MHB8228
	m_maincpu->set_addrmap(AS_PROGRAM, &sapi_state::sapi3a_mem);
	m_maincpu->set_addrmap(AS_IO, &sapi_state::sapi3a_io);

	/* video hardware */
	AY51013(config, m_uart); // Tesla MHB1012
	m_uart->read_si_callback().set(m_v24, FUNC(rs232_port_device::rxd_r));
	m_uart->write_so_callback().set(m_v24, FUNC(rs232_port_device::write_txd));
	m_uart->set_auto_rdav(true); // RDAV not actually tied to RDE, but pulsed by K155AG3 (=74123N): R25=22k, C14=220

	CLOCK(config, m_uart_clock, 12.288_MHz_XTAL / 80); // TODO: divider selectable by jumpers
	m_uart_clock->signal_handler().set(m_uart, FUNC(ay51013_device::write_tcp));
	m_uart_clock->signal_handler().append(m_uart, FUNC(ay51013_device::write_rcp));

	RS232_PORT(config, m_v24, default_rs232_devices, "terminal").set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("64K");
}


/**************************************

    Roms

**************************************/

ROM_START( sapi1 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "mb1", "MB1" )
	ROMX_LOAD("sapi1.rom", 0x0000, 0x1000, CRC(c6e85b01) SHA1(2a26668249c6161aef7215a1e2b92bfdf6fe3671), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "mb2", "MB2 (ANK-1)" )
	ROMX_LOAD("mb2_4.bin", 0x0000, 0x1000, CRC(a040b3e0) SHA1(586990a07a96323741679a11ff54ad0023da87bc), ROM_BIOS(1))

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD("sapi1.chr",  0x0000, 0x1000, CRC(9edafa2c) SHA1(a903db0e8923cca91646274d010dc19b6b377e3e))
ROM_END

ROM_START( sapizps2 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "v4", "MIKOS 4" )
	ROMX_LOAD("36.bin", 0x0000, 0x0800, CRC(a27f340a) SHA1(d07d208fcbe428897336c17197d3e8fb52181f38), ROM_BIOS(0))
	ROMX_LOAD("37.bin", 0x0800, 0x0800, CRC(30daa708) SHA1(66e990c40788ee25cf6cabd4842a78daf4fcdddd), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v5", "MIKOS 5" )
	ROMX_LOAD("mikos5_1.bin", 0x0000, 0x0800, CRC(c2a83ca3) SHA1(a3678253d7690c89945e791ea0f8e15b081c9126), ROM_BIOS(1))
	ROMX_LOAD("mikos5_2.bin", 0x0800, 0x0800, CRC(c4458a04) SHA1(0cc909323f0e6507d95e57ea39e1deb8bd57bf89), ROM_BIOS(1))
	ROMX_LOAD("mikos5_3.bin", 0x1000, 0x0800, CRC(efb499f3) SHA1(78f0ca3ff10d7af4ae94ab820723296beb035f8f), ROM_BIOS(1))
	ROMX_LOAD("mikos5_4.bin", 0x1800, 0x0800, CRC(4d90e9be) SHA1(8ec554198697550a49432e8210d43700ef1d6a32), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "mb3", "MB3 (Consul)" )
	ROMX_LOAD("mb3_1.bin", 0x0000, 0x1000, CRC(be895f88) SHA1(7fc2a92f41d978a9f0ccd0e235ea3c6146adfb6f), ROM_BIOS(2))
ROM_END

ROM_START( sapizps3 )
	ROM_REGION( 0x10800, "maincpu", 0 )
	// These 2 bioses use videoram at F800
	ROM_SYSTEM_BIOS( 0, "per", "Perina" )
	ROMX_LOAD("perina_1988.bin",0x10000, 0x0800, CRC(d71e8d3a) SHA1(9b3a26ea7c2f2c8a1fb10b51c1c880acc9fd806d), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "1zmod", "JPR-1Zmod" )
	ROMX_LOAD("jpr1zmod.bin",   0x10000, 0x0800, CRC(69a29b07) SHA1(1cd31032954fcd7d10b1586be62db6f7597eb4f2), ROM_BIOS(1))
ROM_END

ROM_START( sapizps3a )
	ROM_REGION( 0x10000, "maincpu", 0 )
	// This bios uses a terminal
	ROM_LOAD("jpr1a.bin",      0xf800, 0x0800, CRC(3ed89786) SHA1(dcc8657b4884bfe58d114c539b733b73d038ee30))
ROM_END

ROM_START( sapizps3b )
	ROM_REGION( 0x10800, "maincpu", 0 )
	// This BIOS uses a 6845
	ROM_LOAD("pkt1.bin",       0x10000, 0x0800, CRC(ed5a2725) SHA1(3383c15f87f976400b8d0f31829e2a95236c4b6c))
ROM_END

} // Anonymous namespace


/* Driver */

//    YEAR  NAME       PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT            COMPANY  FULLNAME                   FLAGS
COMP( 1985, sapi1,     0,      0,      sapi1,   sapi1, sapi_state, empty_init,     "Tesla", "SAPI-1 ZPS 1",            MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1985, sapizps2,  sapi1,  0,      sapi2,   sapi1, sapi_state, empty_init,     "Tesla", "SAPI-1 ZPS 2",            MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1985, sapizps3,  sapi1,  0,      sapi3,   sapi1, sapi_state, init_sapizps3,  "Tesla", "SAPI-1 ZPS 3",            MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1985, sapizps3a, sapi1,  0,      sapi3a,  sapi1, sapi_state, init_sapizps3a, "Tesla", "SAPI-1 ZPS 3 (terminal)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1985, sapizps3b, sapi1,  0,      sapi3b,  sapi1, sapi_state, init_sapizps3b, "Tesla", "SAPI-1 ZPS 3 (6845)",     MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )




sapphire.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Novag Sapphire (model 9304) / Sapphire II (model 38601)

Handheld chess computer. It's the successor to Novag Super VIP, whereas Ruby is
the successor to Novag VIP. The chess engine is by David Kittinger again.

Hardware notes:

Sapphire:
- PCB label: 100168 REV A
- Hitachi H8/325 MCU (mode 2), 26.601712MHz XTAL
- 32KB EPROM (M27C256B-12F1), 128KB SRAM (KM681000ALG-10)
- LCD with 4 7segs and custom segments, same as Novag VIP
- RJ-12 port for Novag Super System (always 9600 baud)
- 24 buttons, piezo

Sapphire II:
- PCB label: 100209 REV A
- Hitachi H8/325 MCU (mode 2), 32MHz XTAL
- 128KB EPROM (27C010), 128KB SRAM (KM681000CLG-7)
- LCD has more segments, the rest is the same as Sapphire

Sapphire II MCU and EPROM are the same as Diamond II. MCU pin P62 determines
which hardware it runs on, see diamond.cpp for Diamond II.

TODO:
- Novag Super System peripherals don't work due to serial clock drift, baud rate
  differs a bit between host and client, m6801 serial emulation issue (to work
  around it, underclock sapphire to exactly 26.4192MHz, sapphire2 to 31.9488MHz)
- it does a cold boot at every reset, so nvram won't work properly unless MAME
  adds some kind of auxillary autosave state feature at power-off

BTANB:
- Average Time level (AT) does not work properly after a few moves on sapphire,
  this is mentioned in the manual and it suggests to set user programmable time
  control

*******************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/h8/h8325.h"
#include "machine/nvram.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "novag_sapphire.lh"
#include "novag_sapphire2.lh"


namespace {

class sapphire_state : public driver_device
{
public:
	sapphire_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_memory(*this, "memory"),
		m_nvram(*this, "nvram", 0x20000, ENDIANNESS_BIG),
		m_rambank(*this, "rambank"),
		m_rombank(*this, "rombank"),
		m_lcd_pwm(*this, "lcd_pwm"),
		m_dac(*this, "dac"),
		m_rs232(*this, "rs232"),
		m_inputs(*this, "IN.%u", 0),
		m_out_lcd(*this, "s%u.%u", 0U, 0U)
	{ }

	void sapphire(machine_config &config);
	void sapphire2(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(power_switch);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD { set_power(true); }

private:
	// devices/pointers
	required_device<h8325_device> m_maincpu;
	memory_view m_memory;
	memory_share_creator<u8> m_nvram;
	required_memory_bank m_rambank;
	optional_memory_bank m_rombank;
	required_device<pwm_display_device> m_lcd_pwm;
	required_device<dac_1bit_device> m_dac;
	required_device<rs232_port_device> m_rs232;
	required_ioport_array<3> m_inputs;
	output_finder<4, 10+6> m_out_lcd;

	bool m_power = false;
	u8 m_inp_mux = 0;
	u8 m_lcd_sclk = 0;
	u32 m_lcd_data = 0;
	u8 m_lcd_segs2 = 0;

	void sapphire_map(address_map &map) ATTR_COLD;
	void sapphire2_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void set_power(bool power);
	u8 power_r();

	void lcd_pwm_w(offs_t offset, u8 data);
	void update_lcd();
	void s1_lcd_data_w(u8 data);
	void s2_lcd_data_w(u8 data);

	u8 read_buttons();
	void bank_w(u8 data);
	void p4_w(u8 data);
	u8 p5_r();
	void p6_w(u8 data);
	u8 p7_r();
};

void sapphire_state::machine_start()
{
	m_out_lcd.resolve();

	if (m_rombank)
		m_rombank->configure_entries(0, 4, memregion("eprom")->base(), 0x8000);

	m_rambank->configure_entries(0, 4, m_nvram, 0x8000);
	m_memory.select(0);

	// register for savestates
	save_item(NAME(m_power));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_lcd_sclk));
	save_item(NAME(m_lcd_data));
	save_item(NAME(m_lcd_segs2));
}



/*******************************************************************************
    I/O
*******************************************************************************/

// power

void sapphire_state::set_power(bool power)
{
	// power switch is tied to IRQ2
	m_maincpu->set_input_line(INPUT_LINE_IRQ2, power ? ASSERT_LINE : CLEAR_LINE);
	m_power = power;
}

INPUT_CHANGED_MEMBER(sapphire_state::power_switch)
{
	if (newval)
		set_power(bool(param));
}

u8 sapphire_state::power_r()
{
	// P66: power switch (IRQ2)
	return m_power ? 0xbf : 0xff;
}


// LCD

void sapphire_state::lcd_pwm_w(offs_t offset, u8 data)
{
	m_out_lcd[offset & 0x3f][offset >> 6] = data;
}

void sapphire_state::update_lcd()
{
	for (int i = 0; i < 4; i++)
	{
		const u8 shift = m_lcd_pwm->width() & 0x18;

		// LCD common is analog (voltage level)
		const u8 com = population_count_32(m_lcd_data >> (shift + (i * 2)) & 3);
		u16 segs = m_lcd_data & ((1 << shift) - 1);
		segs |= m_lcd_segs2 << shift; // sapphire

		m_lcd_pwm->write_row(i, (com == 0) ? segs : (com == 2) ? ~segs : 0);
	}
}

void sapphire_state::s1_lcd_data_w(u8 data)
{
	// P60,P61: same as sapphire2
	s2_lcd_data_w(data);

	// P62: 2*14015B R
	if (data & 4)
		m_lcd_data = 0;

	// P64,P65: 2 more LCD segments
	m_lcd_segs2 = data >> 4 & 3;
	update_lcd();
}

void sapphire_state::s2_lcd_data_w(u8 data)
{
	// P60: 2/3*14015B C (chained)
	if (data & 1 && !m_lcd_sclk)
	{
		// P61: 14015B D, outputs to LCD
		m_lcd_data = m_lcd_data << 1 | BIT(data, 1);
		update_lcd();
	}
	m_lcd_sclk = data & 1;
}


// misc

u8 sapphire_state::read_buttons()
{
	u8 data = 0;

	for (int i = 0; i < 3; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read();

	return ~data;
}

void sapphire_state::bank_w(u8 data)
{
	// ROM/RAM bankswitch
	const u8 bank = data & 3;

	// only sapphire2 has ROM banks
	if (m_rombank)
		m_rombank->set_entry(bank);

	m_rambank->set_entry(bank);
}

void sapphire_state::p4_w(u8 data)
{
	// P40: speaker out
	m_dac->write(data & 1);

	// P43-P45: input mux
	m_inp_mux = ~data >> 3 & 7;
}

u8 sapphire_state::p5_r()
{
	// P52-P55: read buttons (low)
	return read_buttons() << 2 | 0xc3;
}

void sapphire_state::p6_w(u8 data)
{
	// P63: ROM/RAM CS
	m_memory.select(BIT(data, 3));

	// other: LCD (see above)
}

u8 sapphire_state::p7_r()
{
	// P70-P73: read buttons (high)
	return read_buttons() >> 4 | 0xf0;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void sapphire_state::sapphire_map(address_map &map)
{
	map(0x8000, 0xffff).view(m_memory);
	m_memory[0](0x8000, 0xffff).rom().region("eprom", 0);
	m_memory[1](0x8000, 0xffff).bankrw(m_rambank);

	map(0xff90, 0xff9f).unmaprw(); // reserved for H8 registers
	map(0xffb0, 0xffff).unmaprw(); // "
}

void sapphire_state::sapphire2_map(address_map &map)
{
	sapphire_map(map);
	m_memory[0](0x8000, 0xffff).bankr(m_rombank);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( sapphire )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("NG")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_NAME("C/CB")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("Option 1/2 / Random")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_COLON) PORT_NAME("Level")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_OPENBRACE) PORT_NAME("Hint / Analyze")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_MINUS) PORT_NAME("Ver/Set")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_STOP) PORT_NAME("Right / Easy")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_SLASH) PORT_NAME("GO")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Pawn / Load Game / ProDelete")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Knight / Save Game / ProSave")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Bishop / Training / ProPrior")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_J) PORT_NAME("Rook / Referee / ProPrint")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_K) PORT_NAME("Queen / Sound / BkSelect")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("King / Info / Restore")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_COMMA) PORT_NAME("Left / Next Best")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("Color")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("H 8")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("G 7")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("F 6")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("E 5")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("D 4")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("C 3")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("B 2")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("A 1")

	PORT_START("BATT")
	PORT_CONFNAME( 0x80, 0x80, "Battery Status" )
	PORT_CONFSETTING(    0x00, "Low" )
	PORT_CONFSETTING(    0x80, DEF_STR( Normal ) )
	PORT_BIT(0x7f, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("LOCK")
	PORT_CONFNAME( 0x80, 0x00, "Keyboard Lock")
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x80, DEF_STR( On ) )
	PORT_BIT(0x7f, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("POWER")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_ON) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sapphire_state::power_switch), 1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sapphire_state::power_switch), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void sapphire_state::sapphire(machine_config &config)
{
	// basic machine hardware
	H8325(config, m_maincpu, 26.601712_MHz_XTAL);
	m_maincpu->set_mode(2);
	m_maincpu->set_addrmap(AS_PROGRAM, &sapphire_state::sapphire_map);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(h8325_device::nvram_set_battery));
	m_maincpu->standby_cb().append([this](int state) { if (state) m_lcd_pwm->clear(); });
	m_maincpu->write_sci_tx<0>().set(m_rs232, FUNC(rs232_port_device::write_txd));
	m_maincpu->read_port2().set_ioport("BATT").invert();
	m_maincpu->read_port4().set_ioport("LOCK").invert();
	m_maincpu->write_port4().set(FUNC(sapphire_state::p4_w));
	m_maincpu->write_port4().append(FUNC(sapphire_state::bank_w)).rshift(1);
	m_maincpu->read_port5().set(FUNC(sapphire_state::p5_r));
	m_maincpu->read_port6().set(FUNC(sapphire_state::power_r));
	m_maincpu->write_port6().set(FUNC(sapphire_state::p6_w));
	m_maincpu->write_port6().append(FUNC(sapphire_state::s1_lcd_data_w));
	m_maincpu->read_port7().set(FUNC(sapphire_state::p7_r));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	// video hardware
	PWM_DISPLAY(config, m_lcd_pwm).set_size(4, 10);
	m_lcd_pwm->output_x().set(FUNC(sapphire_state::lcd_pwm_w));
	m_lcd_pwm->set_bri_levels(0.05);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/3, 606/3);
	screen.set_visarea_full();

	config.set_default_layout(layout_novag_sapphire);

	// rs232 (configure after video)
	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set(m_maincpu, FUNC(h8325_device::sci_rx_w<0>));

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void sapphire_state::sapphire2(machine_config &config)
{
	sapphire(config);

	// basic machine hardware
	m_maincpu->set_clock(32_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &sapphire_state::sapphire2_map);
	m_maincpu->write_port4().set(FUNC(sapphire_state::p4_w)); // bankswitch is on port 6
	m_maincpu->write_port6().set(FUNC(sapphire_state::p6_w));
	m_maincpu->write_port6().append(FUNC(sapphire_state::bank_w)).rshift(4);
	m_maincpu->write_port6().append(FUNC(sapphire_state::s2_lcd_data_w));

	// video hardware
	m_lcd_pwm->set_width(16);

	screen_device &screen(*subdevice<screen_device>("screen"));
	screen.set_size(1920/3, 671/3);
	screen.set_visarea_full();

	config.set_default_layout(layout_novag_sapphire2);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( sapphire ) // ID = SAPPHIRE 1.01
	ROM_REGION16_BE( 0x8000, "maincpu", 0 )
	ROM_LOAD("novag_9304-010053_6433258b46f.u1", 0x0000, 0x8000, CRC(bfc39f4b) SHA1(dc96440c070e903772f4485757443dd690e92120) )

	ROM_REGION16_BE( 0x8000, "eprom", 0 )
	ROM_LOAD("bk301_26601.u2", 0x0000, 0x8000, CRC(648ebe8f) SHA1(2883f962a0bf17426fd809b9f2c01ce3dec0df1b) )

	ROM_REGION( 36256, "screen", 0 )
	ROM_LOAD("nvip.svg", 0, 36256, CRC(3373e0d5) SHA1(25bfbf0405017388c30f4529106baccb4723bc6b) )
ROM_END

ROM_START( sapphire2 ) // ID = SAPPHIRE II 1.02
	ROM_REGION16_BE( 0x8000, "maincpu", 0 )
	ROM_LOAD("ihp_7600109_hd6433258c67f.u1", 0x0000, 0x8000, CRC(10970123) SHA1(8f72e756915de6569c3936140c775d24730e9065) )

	ROM_REGION16_BE( 0x20000, "eprom", 0 )
	ROM_LOAD("32dsii_060597.u3", 0x00000, 0x20000, CRC(c1be39c6) SHA1(e33ee655bd342fed736c2b2093a89752e695aff3) )

	ROM_REGION( 72533, "screen", 0 )
	ROM_LOAD("sapphire2.svg", 0, 72533, CRC(34944b61) SHA1(4a0536ac07790cced9f9bf15522b17ebc375ff8a) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1994, sapphire,  0,      0,      sapphire,  sapphire, sapphire_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Sapphire", MACHINE_SUPPORTS_SAVE )

SYST( 1997, sapphire2, 0,      0,      sapphire2, sapphire, sapphire_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Sapphire II", MACHINE_SUPPORTS_SAVE )



sartorius.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Vas Crabb
/*
 Sartorius 3733 digital scale

 Mainboard:
 * 4194.304 kHz crystal
 * Intel D4040 4-bit CPU
 * Intel D4289 8-bit memory controller
 * 2 * Intel P4002-1 (64+16)*4 RAM (sockets for 2 * Intel P4002-2 unpopulated)
 * 2 * 2708 1k*8 ROM labelled "129/0" and "129/1"
 * National Semiconductor MM74C907N hex open-drain P-channel buffer
 * Intersil IM6561AIJN 256*4 CMOS RAM
 * RCA CD4007AE dual complementary pair plus inverter
 * National Semiconductor CD4049CN hex inverting buffer
 * Intel P4201A clock generator
 * 4 * Motorola MC14099B 8-bit addressable latch
 * 2 * RCA CD4052BE 2-channel 4:1 analog switch

 Display board (connects to mainboard via 28-pin edge connector):
 * MM74C42 1-of-10 decoder
 * RCA CD4071BE Quad 2-input OR gate
 * RCA CD4011BE Quad 2-input NAND gate
 * RCA CD4055BE BCD to 7-segment decoder with high-voltage outputs
 * 12 TO-92 transistors near the MM74C42 and CD4071BE
 * 7 TO-92 transistors near the CD4011BE
 * 7 TO-92 transistors near the CD4055BE

 Front panel has a digital display, an "R" button for switching between
 300g and 3kg ranges, and a T button for zeroing.  Rear panel has a
 three-position rotary switch to select between standalone operation and
 data acquisition modes, and a 50-pin micro ribbon connector for data
 communication.

 The vacuum fluorescent display has seven 7-segment digits, each of
 which can be followed by a . (stop) or , (comma) for digit grouping or
 decimal point.  The last digit can also show the vulgar fraction ½
 using the G segment as the fraction bar and additional 1 and 2 segments
 (these could potentially be used independently as well).  To the right
 of the least significant digit, the display can show % (percent) or g
 (lowecase G).  To the legt of the leftmost digit, the display has,
 from top to bottom, + (plus), - (minus), and a small circle.

 Strangely, the P4201A clock generator IC is a long way from the
 crystal.  The mode pin is tied to Vss, putting it in divide-by-seven
 mode.

 No dates or other ASCII strings in program ROM.  IC date codes up to
 1978.
 */

#include "emu.h"
#include "cpu/mcs40/mcs40.h"

namespace {

class sart3733_state : public driver_device
{
public:
	sart3733_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag)
	{
	}

	void sart3733(machine_config &config);

private:
	void sart3733_memory(address_map &map) ATTR_COLD;
	void sart3733_rom(address_map &map) ATTR_COLD;
	void sart3733_status(address_map &map) ATTR_COLD;
};


	void sart3733_state::sart3733_rom(address_map &map)
{
	map(0x0000, 0x07ff).rom().region("maincpu", 0x0000);
}

	void sart3733_state::sart3733_memory(address_map &map)
{
	map(0x0000, 0x007f).ram(); // 2 * 4002
}

	void sart3733_state::sart3733_status(address_map &map)
{
	map(0x0000, 0x001f).ram(); // 2 * 4002
}


void sart3733_state::sart3733(machine_config &config)
{
	i4040_cpu_device &cpu(I4040(config, "maincpu", 750'000)); // clock speed is a complete guess - can't see crystal or multi-phase clock generator
	cpu.set_rom_map(&sart3733_state::sart3733_rom);
	cpu.set_ram_memory_map(&sart3733_state::sart3733_memory);
	cpu.set_ram_status_map(&sart3733_state::sart3733_status);
}


INPUT_PORTS_START(sart3733)
INPUT_PORTS_END


ROM_START (sart3733)
	ROM_REGION(0x0800, "maincpu", 0)
	ROM_LOAD("129-0", 0x0000, 0x0400, CRC(7ba30b9a) SHA1(40f828145542c2032d30b9921c829406ba9a3679))
	ROM_LOAD("129-1", 0x0400, 0x0400, CRC(a1e2ac6e) SHA1(aaa504274bc077a0de2ba78675d1eb3ad3157b6a))
ROM_END

} // anonymous namespace

SYST( 1978?, sart3733, 0, 0, sart3733, sart3733, sart3733_state, empty_init, "Sartorius-Werke GmbH", "3733 (digital scale)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



saturn.cpp
<---------------------------------------------------------------------->
// license:LGPL-2.1+
// copyright-holders:David Haywood, Angelo Salese, Olivier Galibert, Mariusz Wojcieszek, R. Belmont
/**************************************************************************************************

    Driver by David Haywood, Angelo Salese, Olivier Galibert & Mariusz Wojcieszek
    SCSP driver provided by R. Belmont, based on ElSemi's SCSP sound chip emulator
    Many thanks to Guru, Fabien, Runik and Charles MacDonald for the help given.

***************************************************************************************************

Sega Saturn
(C) Sega 1994/1995/1996/1997

The Sega Saturn is a 32-bit 5th-generation home video game console that was developed by Sega and released
on November 22nd 1994 in Japan, May 11th 1995 in North America, and July 8th 1995 in Europe as the successor
to the Sega Genesis. The Saturn has a dual-CPU architecture and a total of eight processors.
The games are on CD-ROM.

Basic Hardware:

Processors (8)
----------
Two Hitachi SH2 32-bit RISC @ 28.63636MHz
One Hitachi SH1 32-bit RISC @ 20.0MHz
Two 32-bit video display processors (VDP 1 / VDP 2)
One Saturn Control Unit processor (SCU) @ 14.31818MHz
One Motorola 68EC000 sound processor @ 11.2896MHz
One Yamaha YMF292-F DSP sound processor @ 28.63636MHz

Memory
------
16 Megabits DRAM
12 Megabits VRAM (Video RAM)
4 Megabits Audio DRAM
4 Megabits CD-ROM Cache DRAM
256 Kbits battery-backed SRAM

Video
-----
VDP 1
- 32-bit video display processor
- Sprite, polygon, and geometry engine
- Dual 256Kb frame buffers for rotation and scaling effects
- Texture Mapping
- Goraud Shading
- 512Kb cache for textures
VDP 2
- 32-bit background and scroll plane video display processor
- Background engine
- 5 simulataneous scrolling backgrounds
- 2 simultaneous rotating playfields
- 200,000 Texture Mapped Polygons/Second
- 500,000 Flat Shaded Polygons/Second
- Up to 60 frames per second animation
- 24-bit True Color Graphics
- 16 Million Available Colors
- 320x224, 640x224, and 720x576 Resolution

Audio
-----
Motorola 68EC000 sound processor @ 11.2896MHz
Yamaha 24-bit Digital Signal Processor @ 28.63636MHz
- 32 PCM (Pulse Code Modulation) Channels
- 8 FM (Frequency Modulation) Channels
- 44.1 kHz Sampling Rate

Storage
-------
CD-ROM (2X)
- 320 Kb/Second transfer speed
- Compatible with Audio CD, CD+G, CD+EG, CD Single (8cm)
  and with optional hardware; Video CD, Photo CD & Digital Karaoke
- Optional additional memory cartridge

Input/Output
------------
High speed serial communications port
Internal 32-bit expansion port
Internal Multi AV port for optional Video CD (MPEG) decoder board
Composite video & stereo audio (Standard)
Optional RF(TV), S-Video & RGB outputs
2 ports for analog control pad, light gun or other controllers


PCB Layouts
-----------
There were *many* main board revisions. The two general 'sizes' are documented here.

Small board (VA revision documented)
-----------
Main board with a separate small sub-board for the controller ports, power LED and reset button.
The power supply is slightly longer than the main board, has a 5 pin connector and outputs 9VDC, 5VDC and 3.3VDC

837-12126 IC BD SATURN MAIN VA SG
171-7128B (C) SEGA 1995 PC BD SATURN MAIN VA SG
(This PCB was found in a USA Saturn, Model MK-80000, also known as Saturn model 1 with BIOS MPR-17941)

837-12135 IC BD SATURN MAIN VA PAL SD
171-7131A (C) SEGA 1995 PC BD SATURN MAIN VA PAL SD
(This PCB was found in a PAL Saturn, Model MK-80200-50, also known as Saturn model 1 PAL with BIOS MPR-17942)

(note both of these PCBs listed above are almost identical)

|VIDEO--COMM-----------------------------------------------|
|                        CART_SLOT            SW1 BATTERY  |
|ADAC         CXA1645M   |--------| |-------|    20MHz     |
|     SW2                |315-5688| |YGR019B|   TC514260   |
| ^4502161               |        | |       |   |-------|  |
| ^4502161    |--------| |--------| |-------|   |HD6437097 |
| |--------|  |315-5890|   *D489020             |       |  |
| |315-5883|  |        |    81141625 81141625   |-------|  |
| |        |  |--------|                        CARD_SLOT  |
| |--------|  ^4502161                    HC08             |
| ^5241605    ^4502161          |----| |----|   |--------| |
|  514270 |--------|HC04        |SH-2| |SH-2|   |315-5914| |
| |-----| |315-5687|HC04        |----| |----|   |--------| |
| |68000| |        |      |------|32.768kHz                |
| |-----| |--------|      315-5744       LS245  TC514260   |
|       ^74HC157 315-5746 |------|        ^62257  ^ROM  CN4|
|  CN3      CN7  &14.31818MHz  CN2       LS245  TC514260   |
|----------------------------------------------------------|
Notes: (all IC's shown. ^ denotes these parts are on the other side of the PCB)
             ROM - SOP40 mask ROM for BIOS.
                   Chip is pin-compatible with Toshiba TC574200 or MX27C4100 and can be read with a simple 1:1 DIP40 to SOP40 adapter
                   JAPAN BIOS marked 'MPR-17940-MX' or 'MPR-17940-T'
                   USA BIOS   marked 'MPR-17941-MX' or 'MPR-17941-T'
                   PAL BIOS   marked 'MPR-17942-MX' or 'MPR-17942-T'
                   T = Toshiba, MX = Macronix. Both contain identical data
                   Other BIOSes known to exist include:
                   (These are mainly on the very       MPR-16605-T
                    early version main boards VA0/VA1  MPR-16606-T
                    and are all DIP chips)             MPR-16606A-T
                                                       MPR-17577-T
                                                       EPR-17578 HI-SATURN BOOT ROM VER 1.01 SUM AA44 '95 1/27 (EPROM)
        81141625 - Fujitsu 81141625-017 128k x16-bit x 2 banks (4Mbit) SDRAM                     \ compatible
         5241605 - Hitachi HM5241605TT17S or HM524165CTT17S 128k x16-bit x 2 banks (4Mbit) SDRAM /
                   The two 81141625 are the WORK RAM HIGH and the two TC514260 (near the ROM) make up the WORK RAM LOW
                   The 5241605 is the VDP1 Sprite RAM
         D489020 - NEC D489020GF-A15 SGRAM (probably 8Mbit). *- This single chip is replaced by two 81141625 IC's on some boards
         4502161 - NEC D4502161G5-A12 or Sanyo LC382161T-17 64k x16-bit x 2 banks (2Mbit) SDRAM
                   Two chips are used for the VDP1 Frame RAM, the other two are for the VDP2 Video RAM
        TC514260 - 256k x16-bit (4Mbit) DRAM. Any of the following compatible chips are used....
                   Hitachi HM514260AJ7 / HM514260CJ7
                   Toshiba TC514260BJ-70
                   Fujitsu 814260-70
                   Mitsubishi M5M44260CJ
                   Samsung KM416C256BJ-7
                   Hyundai HY514260B JC-70
                   Vanguard VG264260AJ
                   Panasonic MN414260CSJ-07
        HM514270 - Hitachi HM514270 256k x16-bit (4Mbit) DRAM, used for the sound WORK RAM
           62257 - Epson SRM20257LLM10 32k x8-bit (256kbit) battery-backed SRAM (also used SONY CXK58257AM-10L, NEC UPD43257B-10LL, UM62257AM-70LL)
            ADAC - Dual CMOS Audio DAC. Either Burr-Brown BBPCM1710U or Philips TDA1386T
        CXA1645M - Sony CXA1645M RGB to Composite Video Encoder
            SH-2 - Hitachi HD6417095 SH-2 CPU. Clock input 28.63636MHz (14.31818*2)
           68000 - Motorola MC68EC000FN12 CPU. Clock input 11.2896MHz
               & - Master Clock. 14.31818MHz for USA revision or 17.7344MHz for PAL revision
        315-5744 - Sega 315-5744 Hitachi HD404920 microcontroller used as the System Manager and Peripheral Controller (SMPC)
        315-5746 - Sega 315-5746 Phase-locked Loop (PLL) clock generator IC
        315-5883 - Sega 315-5883 Hitachi HD64440 Video Display Processor 1 (VDP1). Earliest revision is 315-5689
        315-5687 - Sega 315-5687 Yamaha YMF292-F Saturn Custom Sound Processor (SCSP). Clock input 28.63636MHz (14.31818*2)
        315-5688 - Sega 315-5688 System Control Unit (SCU). Clock input 14.31818MHz
        315-5890 - Sega 315-5890 Video Display Processor 2 (VDP2)
        315-5914 - Sega 315-5914 DRAM controller. Earliest revision is 315-5778. Later revision is 315-5963
       HD6437097 - Hitachi HD6437097F20 SH1 (SH7034 family) microcontroller with 64k internal ROM. Clock input 20.000MHz
         YGR019B - Hitachi YGR019B CD-Subsystem LSI. Earlier revision is YGR019A. Later revision combines this IC and the SH1 together
                   into one IC (YGR022 315-5962). The SH1 and the YGR019B make up the 'CD Block' CD Authentication and CD I/O data controller.
                   Another of it's functions is to prevent copied CDs from being played
           VIDEO - 10-pin Mini-DIN video output port
            COMM - Communication port
       CARD_SLOT - Expansion slot for MPEG decoder card and other optional expansions
       CART_SLOT - Expansion slot for plug-in RAM or ROM carts
             SW1 - Master reset switch accessible behind the card slot/battery cover. Pressing this clears the battery-backed SRAM, resets the system
                   and the user has to set the language, date and time
         BATTERY - CR2032 3V lithium coin battery. When the system is off the battery provides power to the backup SRAM and SMPC which contains an RTC
             SW2 - CDROM cover open/close detection switch
             CN2 - 24-pin flat cable connector for control port board
             CN3 - 5-pin power connector
             CN4 - Flat cable connector for CDROM data cable. On some main board revisions the connector is reversed and the cable is folded so it
                   is also reversed/flipped 180 degrees at the other end
             CN7 - 5-pin connector for CDROM power


Control Port board
------------------
839-0820 EXPORT
839-0821 PAL
PC BD SATURN PIO VA EXPORT 171-7112A (C) SEGA 1995 CSPD CAD-TEAM
|---------------------------------|
|               FLAT-CABLE        |
|                                 |
| GREEN-LED         RED-LED  RESET|
|--PORT1-----PORT2----------------|
Notes:
      PORT1/2 - Controller ports for controller/joystick/lightgun etc
    GREEN-LED - Power LED
      RED-LED - CDROM drive access LED
        RESET - Push-button reset switch
   FLAT-CABLE - 24-pin flat cable joined to main board CN2


Large board (VA7 revision documented)
-----------
This is a single main board containing everything.

837-12643 IC BD SATURN MAIN VA7 USA SD
171-7208C (C) SEGA 1996 PC BD SATURN MAIN VA7 USA SD
(This PCB was found in a USA Saturn, Model MK-80000A, also known as Saturn model 2 with BIOS MPR-17941)

837-12992 IC BD SATURN MAIN VA7 PAL
171-7424A (C) SEGA 1996 PC BD SATURN MAIN VA7 PAL
(This PCB was found in a PAL Saturn, Model MK-80200A-50, also known as Saturn model 2 PAL with BIOS MPR-17942)

(note both of these PCBs listed above are almost identical)

|VIDEO--COMM-----------------------------------------------|
|                        CART_SLOT                BATTERY  |
|     SW2                |--------|                        |
|ADAC       RGB          |315-5966|   |--------| ^TC514260 |
|                        |        |   |YGR022  |           |
|                        |--------|   |315-5962|           |
|                                     |--------|           |
| |--------|  |--------|                   20MHz           |
| |315-5687|  |315-5964|                        CARD_SLOT  |
| |        |  |        |                                   |
| |--------|  |--------|           |----|   LS245 HC08     |
| ^HM514270  ^524165     HC04      |SH-2|                  |
| ^5221605   ^5221605              |----|         TC514260 |
| ^5221605   ^5221605                                      |
|           |--------|                            TC514260 |
|  |-----|  |315-5883|   HC04                              |
|  |68000|  |        |&14.31818MHz |----|  |--------|      |
|  |-----|  |--------|  %CY2292    |SH-2|  |315-5977-01    |
|CN3             |------|          |----|  |--------|      |
|                315-5744                         62257 CN4|
|            CN7 |------|                                  |
|--|             32.768kHz LS245 81141625 81141625  ROM |--|
   |            GREEN-LED                  RESET        |
   |-------------------PORT1-----PORT2------------------|
Notes: (all IC's shown. ^ denotes these parts are on the other side of the PCB)
             ROM - SOP40 mask ROM for BIOS.
                   Chip is pin-compatible with Toshiba TC574200 or MX27C4100 and can be read with a simple 1:1 DIP40 to SOP40 adapter
                   JAPAN BIOS marked 'MPR-17940-MX' or 'MPR-17940-T'
                   USA BIOS   marked 'MPR-17941-MX' or 'MPR-17941-T'
                   PAL BIOS   marked 'MPR-17942-MX' or 'MPR-17942-T'
                   T = Toshiba, MX = Macronix. Both contain identical data
        81141625 - Fujitsu 81141625-017 128k x16-bit x 2 banks (4Mbit) SDRAM
                   The two 81141625 are the WORK RAM HIGH and two TC514260 (near the SH-2) make up the WORK RAM LOW
          524165 - Hitachi HM524165CTT17S 128k x16-bit x 2 banks (4Mbit) SDRAM. This is the VDP1 Sprite RAM
         5221605 - Hitachi HM5221605TT17S 64k x16-bit x 2 banks (2Mbit) SDRAM
                   Two chips are used for the VDP1 Frame RAM, the other two are for the VDP2 Video RAM
        TC514260 - 256k x16-bit (4Mbit) DRAM. Any of the following compatible chips are used....
                   Hitachi HM514260AJ7 / HM514260CJ7
                   Toshiba TC514260BJ-70
                   Fujitsu 814260-70
                   Mitsubishi M5M44260CJ
                   Samsung KM416C256BJ-7
                   Hyundai HY514260B JC-70
                   Vanguard VG264260AJ
                   Panasonic MN414260CSJ-07
        HM514270 - Hitachi HM514270 256k x16-bit (4Mbit) DRAM, used for the sound WORK RAM
           62257 - Epson SRM20257LLM10 32k x8-bit (256kbit) battery-backed SRAM (also used SONY CXK58257AM-10L, NEC UPD43257B-10LL, UM62257AM-70LL)
            ADAC - Dual CMOS Audio DAC. Either Burr-Brown BBPCM1710U or Philips TDA1386T
             RGB - RGB to Composite Video Encoder with PAL & NTSC output capability. IC is either Fujitsu MB3516A or ROHM BH7236AF
            SH-2 - Hitachi HD6417095 SH-2 CPU. Clock input 28.63636MHz (14.31818*2)
           68000 - Motorola MC68EC000FN12 CPU. Clock input 11.2896MHz
          CY2292 - Cypress CY2292SC-04 PLL clock generator IC. % = On the VA7 PAL version this chip is replaced with the older Sega PLL (315-5746)
               & - Master Clock. 14.31818MHz for USA revision or 17.7344MHz for PAL revision
        315-5744 - Sega 315-5744 Hitachi HD404920 microcontroller used as the System Manager and Peripheral Controller (SMPC)
        315-5883 - Sega 315-5883 Hitachi HD64440 Video Display Processor 1 (VDP1).
        315-5687 - Sega 315-5687 Yamaha YMF292-F Saturn Custom Sound Processor (SCSP). Clock input 28.63636MHz (14.31818*2)
        315-5964 - Sega 315-5964 Video Display Processor 2 (VDP2)
        315-5966 - Sega 315-5966 System Control Unit (SCU). Clock input 14.31818MHz
     315-5977-01 - Sega 315-5977-01 DRAM controller
          YGR022 - Hitachi YGR022 Sega 315-5962 single IC containing CD-Subsystem LSI and Hitachi SH-1 microcontroller with 64k internal ROM. Clock input 20.000MHz
           VIDEO - 10-pin Mini-DIN video output port
            COMM - Communication port
       CARD_SLOT - Expansion slot for MPEG decoder card and other optional expansions
       CART_SLOT - Expansion slot for plug-in RAM or ROM carts
         BATTERY - CR2032 3V lithium coin battery. When the system is off the battery provides power to the backup SRAM and SMPC which contains an RTC
             SW2 - CDROM cover open/close detection switch
             CN3 - 4-pin or 5-pin power connector
             CN4 - Flat cable connector for CDROM data cable
             CN7 - 5-pin connector for CDROM power
         PORT1/2 - Controller ports for controller/joystick/lightgun etc
       GREEN-LED - Power LED
           RESET - Push-button reset switch


Motherboard List
----------------
Board types used in Model 1: VA0 to VA3
Board types used in Model 2: VA2 to VA15
If the VA-number is an even number the board uses a single 8Mbit SGRAM for some of the work RAM, if an odd number it uses two 4Mbit SDRAMs.
Note there are MANY missing. Please help to update this list if you have info for others not listed here.

837-11076    IC BD SATURN MAIN VA0.5        171-6874E PC BD SATURN MAIN VA0.5      (C) SEGA 1994
837-11076-01 IC BD SATURN MAIN VA0 CCI      171-6874D PC BD SATURN MAIN VA0.5      (C) SEGA 1994
837-11491    IC BD SATURN MAIN VA0          171-6962A PC BD SATURN MAIN VA0 USA    (C) SEGA 1995
837-11493    IC BD SATURN MAIN VA0 PAL      171-6963B PC BD SATURN MAIN VA0 PAL    (C) SEGA 1995
837-11613-01 IC BD SATURN MAIN VA1          171-7006C PC BD SATURN MAIN VA1        (C) SEGA 1995
837-11892-01 PAL                            171-7069B MAIN                         (C) SEGA 1995
837-12126    IC BD SATURN MAIN VA SG        171-7128B PC BD SATURN MAIN VA SG      (C) SEGA 1995
837-12126    IC BD SATURN MAIN VA SG        171-7128C PC BD SATURN MAIN VA SG      (C) SEGA 1995
837-12133    IC BD SATURN MAIN VA SD        171-7130C PC BD SATURN MAIN VA SD      (C) SEGA 1995
837-12134    IC BD SATURN MAIN VA USA SD    171-7130C PC BD SATURN MAIN VA USA SD  (C) SEGA 1995
837-12135    IC BD SATURN MAIN VA PAL SD    171-7131A PC BD SATURN MAIN VA PAL SD  (C) SEGA 1995
837-12459    IC BD SATURN MAIN VA6 JPN SG   171-7207A PC BD SATURN MAIN VA6 SG     (C) SEGA 1996
837-12468    IC BD SATURN MAIN VA8 JPN OCU  171-7209C PC BD SATURN MAIN VA8 OCU    (C) SEGA 1996
837-12643    IC BD SATURN MAIN VA7 USA SD   171-7208C PC BD SATURN MAIN VA7 USA SD (C) SEGA 1996
(none)                                      171-7291B PC BD SATURN MAIN VA9 PAL    (C) SEGA 1996
(none)                                      171-7291C PC BD SATURN MAIN VA9        (C) SEGA 1996
837-12650    IC BD SATURN MAIN VA13 JPN     171-7???? PC BD SATURN MAIN VA13       (C) SEGA 1996
837-12845    IC BD SATURN MAIN VA13 USA     171-7???? PC BD SATURN MAIN VA13       (C) SEGA 1996
837-12992    IC BD SATURN MAIN VA7 PAL      171-7424A PC BD SATURN MAIN VA7 PAL    (C) SEGA 1996
837-13100    IC BD SATURN MAIN VA13 PAL     171-7455D PC BD SATURN MAIN VA13 PAL   (C) SEGA 1997
837-13137    IC BD SATURN MAIN VA15 JPN     171-7462B PC BD SATURN MAIN VA15       (C) SEGA 1997


Motherboard Variations Summary
------------------------------

NTSC
----
- VA0: First revision. CD Block (YGR019A & HD6437097) is on a daughterboard. Power supply mounted on top casing.
  Has 40 pin DIP EPROM or mask ROM for BIOS. Larger board with control ports on the main board. Uses JVC CD drive units ENR-007B/ENR-007D.
- VA1: marked as 'VA' on the main board. Power supply is now bottom mounted and plugs in on top into 5 pins on the main board. Most sub boards
  integrated into the main board except the controller ports which are on a small sub board. BIOS is SOP40 mask ROM located on the bottom side of
  the main board. Battery-backup RAM and the VRAM are also on the bottom side of the main board. Uses ENR-007D CD drive units.
- VA2 & VA3: Mostly same as VA1. VA2 is marked as 'VA SG' (uses SGRAM), and VA3 is marked as VA SD (uses SDRAM). Uses ENR-011A CD drive units.
- VA4 & VA5: Same as VA2 and VA3 but in a cheaper model 2 case. Uses ENR-011A CD drive units.
- VA6: One single PCB for everything. Uses an off-the-shelf PLL chip (CY2292). Some custom chips have been revised and have different 315-xxxx numbers.
  BIOS and battery-backed RAM moved to the top side of the main board. Power supply has 4 pins and generates only +5VDC. Uses ENR-013A CD drive unit
  or Sanyo 610-6185-30 CD drive unit.
- VA6 & VA7 has the CD Block reduced to a single IC (YGR022). Some custom chips have been revised and have different 315-xxxx numbers.
- VA8 & VA9 still has the CD Block ICs separated. VA9 uses the old type PLL chip (315-5746).
- VA10 to VA15: uses HQA-001A CD drive unit or Sanyo 610-6294-30 / 610-6473-30 CD drive unit. 68000 & YMF292 integrated into a single IC (315-5965).
  The integrated sound IC has a bug with certain 68000 commands.
- VA11 has a small daughter board mounted on the main board to fix a design fault (possibly to fix the above sound IC problem?)
- VA11+ boards use a smaller TSSOP20 audio DAC, VA10 uses the old one.
- VA12, VA14 and VA16 might not exist.
- VA13 fixes the design fault on VA11 so the patch board is no longer needed.
- VA15 integrates the two SH-2 main CPUs into a larger single IC (HD6417098 / 315-6018).

PAL (only the main differences to the above are listed)
---
- No PAL board ever used SGRAM.
- All PAL boards have an odd VA-number.
- All PAL boards have a 5 pin power connector and use a 5 pin power supply.
- All PAL boards use different region & video output jumpers when compared to NTSC machines.
- All PAL boards use a 17.7344MHz master clock (NTSC units use 14.31818MHz).
- All PAL boards replace the composite sync output on the A/V OUT connector with 9VDC which is used for SCART auto switching. The 9VDC power comes from
  pin 5 of the power supplies with a 5 pin connector. Composite sync is still there at TP4 on the bottom of the board but not wired into the A/V OUT port.
- VA0 PAL - has extra jumpers to set the master clock divider (JP18 & 19), functional but unpopulated 50/60Hz switch on the back (SW4).
- VA1 PAL - unpopulated 50/60Hz switch on the back (SW4). There is a design fault as it is still connected to the master clock divider select pin.
  Therefore the switch does not work on its own, you have to cut or raise & ground the PLL pin 1 for the switch to work.
- VA3 PAL - has extra jumpers to set the master clock divider (JP20 & 21), functional but unpopulated 50/60Hz switch on the back (SW4).
- VA5 PAL - same as VA3 PAL.
- VA7 PAL - Unlike NTSC boards, this still uses the old PLL (315-5746) and pin 1 is connected to the PAL/NTSC and 50/60Hz selection pins on the video
            encoder and the VDP2.
- VA9 - same as VA7 PAL.
- VA13 PAL - Other than a 5 pin power connector it's identical to the NTSC VA13 board.
- VA17 PAL - probably the final revision specifically for EU/PAL regions. Differences are unknown.

Power supplies
--------------
Type A is used on VA0 main boards and is mounted to the top casing. Pinout is GND, GND, 3.3V, 5V, (empty pin), 9V. (5 pins total)
Type B is used on VA1 to VA5 main boards and is bottom mounted. Pinout is GND, GND, 3.3V, 5V, 9V. (5 pins total)
Type C is used on VA6+ main boards and is bottom mounted. Pinout is GND, GND, 5V, 5V (4 pins total).
PAL units use either Type B or a 5-pin version of Type C power supplies. On earlier boards such as 'VA' the 9V pin is connected to pin 1 of the CD ROM
unit power supply cable connector on the main board. On later boards it's also connected to the A/V OUT port for SCART auto switching.

CD Drives
---------
With 20 pin flat cable connector, VA0-VA1:
- JVC ENR-007B EMW10447-003E
- JVC ENR-007B EMW10447-004E
- JVC ENR-007D EMW10447-005E
- JVC ENR-007D EMW10447-006E
- Hitachi JA00292

With 21 pin flat cable connector, VA2-VA5:
- JVC ENR-011A EMW10589-002
- JVC ENR-011A EMW10589-003

With 21 pin flat cable connector, VA6-VA9:
- JVC ENR-013A EMW20035-002 610-6185-20
- Sanyo 610-6185-30 (sometimes with an extra protection board where the flat cable plugs in)

With 21 pin flat cable connector, VA10-VA15:
- JVC HQA-001A HQ100002-002 610-6294-20
- Sanyo 610-6294-30 \
- Sanyo 610-6473-30 / (sometimes with an extra protection board where the flat cable plugs in)
These are the same as the VA6-VA9 units but lack an oscillator and have a white border on the edges of the PCB.

Optical pickups used - JVC drive: Optima-6, Hitachi drive: HOP-6, Sanyo drive: SF-P101 is used in the 610-6185-30

****************************************************************************************************

Emulation Notes:
-To enter into an Advanced Test Mode,keep pressed the Test Button (F2) on the start-up.
-Memo: Some tests done on the original & working PCB,to be implemented:
 -The AD-Stick returns 0x00 or a similar value.
 -The Ports E,F & G must return 0xff

TODO:
(Main issues)
- decap the SH-1, used for CD block (needed especially for Sega Saturn)
- IRQs: some games have some issues with timing accurate IRQs, check/fix all of them.
- The Cart-Dev mode hangs even with the -dev bios,I would like to see what it does on the real HW.
- IC13 games on the dev bios doesn't even load the cartridge / crashes the emulation at start-up,
  rom rearrange needed?
- SCU DSP still has its fair share of issues, it also needs to be converted to CPU structure;
- Add the RS232c interface (serial port), needed by fhboxers (accesses some ports in the a-bus dummy range).
- Video emulation is nowhere near perfection.
- Reimplement the idle skip if possible.
- Move SCU device into its respective file;

test1f diagnostic hacks:
"chash parge error" test 0x6035d04 <- 0x0009 (nop the button check)
"chase line pearg" test 0x6036964 <- 0x0009 (nop the button check again)

****************************************************************************************************/

#include "emu.h"
#include "saturn.h"

#include "saturn_cdb.h"
#include "stvcd.h"

#include "cpu/m68000/m68000.h"
#include "cpu/scudsp/scudsp.h"
#include "machine/nvram.h"
#include "machine/smpc.h"

#include "bus/saturn/bram.h"
#include "bus/saturn/dram.h"
#include "bus/saturn/rom.h"
#include "bus/saturn/sat_slot.h"

#include "bus/sat_ctrl/ctrl.h"

#include "softlist.h"
#include "speaker.h"



class sat_console_state : public saturn_state
{
public:
	sat_console_state(const machine_config &mconfig, device_type type, const char *tag)
		: saturn_state(mconfig, type, tag)
		, m_exp(*this, "exp")
		, m_nvram(*this, "nvram")
		, m_stvcd(*this, "stvcd")
		, m_ctrl1(*this, "ctrl1")
		, m_ctrl2(*this, "ctrl2")
	{ }

	void saturn(machine_config &config);
	void saturnjp(machine_config &config);
	void saturneu(machine_config &config);
	void saturnus(machine_config &config);
	void saturnkr(machine_config &config);

	template <bool is_pal> void init_saturn();

	DECLARE_INPUT_CHANGED_MEMBER(tray_open);
	DECLARE_INPUT_CHANGED_MEMBER(tray_close);

private:
	DECLARE_MACHINE_START(saturn);
	DECLARE_MACHINE_RESET(saturn);

	// SMPC region codes, hardwired via jumper setting.
	// - Given the scheme bit 3 should determine if the region is PAL or NTSC.
	// - 0 and F are "prohibited", others are "Sega reserved".
	// - Documentation states that 2 is "TAIWAN" and 6 is "KOREA",
	//   but games on latter definitely wants 2 rather than 6.
	//   We currently swap, former actual slot needs to be confirmed.
	enum {
		REGION_NTSC_0 = 0,
		REGION_NTSC_JAPAN,
//      REGION_NTSC_TAIWAN,
		REGION_NTSC_KOREA,
		REGION_NTSC_3,
		REGION_NTSC_USA, // & Canada, Mexico
		REGION_NTSC_BRAZIL,
//      REGION_NTSC_KOREA,
		REGION_NTSC_TAIWAN, // & Philippines
		REGION_NTSC_7,
		REGION_PAL_8,
		REGION_PAL_9,
		REGION_PAL_ASIA, // China, Middle East, East Asia not covered above
		REGION_PAL_B,
		REGION_PAL_EUROPE, // Australia, South Africa
		REGION_PAL_AMERICA, // Non-NTSC Central/South America
		REGION_PAL_E,
		REGION_PAL_F
	};

	uint8_t saturn_cart_type_r();
	uint32_t abus_dummy_r(offs_t offset);

	uint32_t saturn_null_ram_r();
	void saturn_null_ram_w(uint32_t data);

	uint8_t saturn_pdr1_direct_r();
	uint8_t saturn_pdr2_direct_r();
	void saturn_pdr1_direct_w(uint8_t data);
	void saturn_pdr2_direct_w(uint8_t data);
	uint8_t m_direct_mux[2];
	uint8_t saturn_direct_port_read(bool which);
	uint8_t smpc_direct_mode(uint16_t in_value, bool which);
	uint8_t smpc_th_control_mode(uint16_t in_value, bool which);

	void nvram_init(nvram_device &nvram, void *data, size_t size);

	required_device<sat_cart_slot_device> m_exp;
	required_device<nvram_device> m_nvram;
	required_device<stvcd_device> m_stvcd;

	required_device<saturn_control_port_device> m_ctrl1;
	required_device<saturn_control_port_device> m_ctrl2;

	void saturn_mem(address_map &map) ATTR_COLD;
	void sound_mem(address_map &map) ATTR_COLD;
	void scsp_mem(address_map &map) ATTR_COLD;
};


uint8_t sat_console_state::saturn_cart_type_r()
{
	if (m_exp)
		return m_exp->get_cart_type();
	else
		return 0xff;
}

/* TODO: Bug! accesses this one, if returning 0 the SH-2 hard-crashes. Might be an actual bug with the CD block. */
uint32_t sat_console_state::abus_dummy_r(offs_t offset)
{
	logerror("A-Bus Dummy access %08x\n",offset*4);
	return -1;
}

void sat_console_state::saturn_mem(address_map &map)
{
	map(0x00000000, 0x0007ffff).rom().mirror(0x20000000).region("bios", 0).nopw(); // bios
	map(0x00100000, 0x0010007f).rw(m_smpc_hle, FUNC(smpc_hle_device::read), FUNC(smpc_hle_device::write));
	map(0x00180000, 0x0018ffff).rw(FUNC(sat_console_state::saturn_backupram_r), FUNC(sat_console_state::saturn_backupram_w)).share("share1");
	map(0x00200000, 0x002fffff).ram().mirror(0x20100000).share("workram_l");
	map(0x01000000, 0x017fffff).w(FUNC(sat_console_state::saturn_minit_w));
	map(0x01800000, 0x01ffffff).w(FUNC(sat_console_state::saturn_sinit_w));
//  map(0x02000000, 0x023fffff).rom().mirror(0x20000000); // Cartridge area
//  map(0x02400000, 0x027fffff).ram(); // External Data RAM area
//  map(0x04000000, 0x047fffff).ram(); // External Battery RAM area
	map(0x04ffffff, 0x04ffffff).r(FUNC(sat_console_state::saturn_cart_type_r));
	map(0x05000000, 0x057fffff).r(FUNC(sat_console_state::abus_dummy_r));
	map(0x05800000, 0x0589ffff).rw(m_stvcd, FUNC(stvcd_device::stvcd_r), FUNC(stvcd_device::stvcd_w));
	/* Sound */
	map(0x05a00000, 0x05a7ffff).rw(FUNC(sat_console_state::saturn_soundram_r), FUNC(sat_console_state::saturn_soundram_w));
	map(0x05b00000, 0x05b00fff).rw(m_scsp, FUNC(scsp_device::read), FUNC(scsp_device::write));
	/* VDP1 */
	map(0x05c00000, 0x05c7ffff).rw(FUNC(sat_console_state::saturn_vdp1_vram_r), FUNC(sat_console_state::saturn_vdp1_vram_w));
	map(0x05c80000, 0x05cbffff).rw(FUNC(sat_console_state::saturn_vdp1_framebuffer0_r), FUNC(sat_console_state::saturn_vdp1_framebuffer0_w));
	map(0x05d00000, 0x05d0001f).rw(FUNC(sat_console_state::saturn_vdp1_regs_r), FUNC(sat_console_state::saturn_vdp1_regs_w));
	map(0x05e00000, 0x05e7ffff).mirror(0x80000).rw(FUNC(sat_console_state::saturn_vdp2_vram_r), FUNC(sat_console_state::saturn_vdp2_vram_w));
	map(0x05f00000, 0x05f7ffff).rw(FUNC(sat_console_state::saturn_vdp2_cram_r), FUNC(sat_console_state::saturn_vdp2_cram_w));
	map(0x05f80000, 0x05fbffff).rw(FUNC(sat_console_state::saturn_vdp2_regs_r), FUNC(sat_console_state::saturn_vdp2_regs_w));
	map(0x05fe0000, 0x05fe00cf).m(m_scu, FUNC(sega_scu_device::regs_map)); //rw(FUNC(sat_console_state::saturn_scu_r), FUNC(sat_console_state::saturn_scu_w));
	map(0x06000000, 0x060fffff).ram().mirror(0x21f00000).share("workram_h");
	map(0x40000000, 0x46ffffff).nopw(); // associative purge page
	map(0x60000000, 0x600003ff).nopw(); // cache address array
	map(0xc0000000, 0xc0000fff).ram(); // cache data array, Dragon Ball Z sprites relies on this
}

void sat_console_state::sound_mem(address_map &map)
{
	map(0x000000, 0x0fffff).ram().share("sound_ram");
	map(0x100000, 0x100fff).rw(m_scsp, FUNC(scsp_device::read), FUNC(scsp_device::write));
}

void sat_console_state::scsp_mem(address_map &map)
{
	map(0x000000, 0x0fffff).ram().share("sound_ram");
}


INPUT_CHANGED_MEMBER(sat_console_state::tray_open)
{
	if(newval)
		m_stvcd->set_tray_open();
}

INPUT_CHANGED_MEMBER(sat_console_state::tray_close)
{
	if(newval)
		m_stvcd->set_tray_close();
}

static INPUT_PORTS_START( saturn )
	PORT_START("RESET") /* hardwired buttons */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CHANGED_MEMBER("smpc", FUNC(smpc_hle_device::trigger_nmi_r), 0) PORT_NAME("Reset Button")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sat_console_state::tray_open), 0) PORT_NAME("Tray Open Button")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sat_console_state::tray_close), 0) PORT_NAME("Tray Close")

	PORT_START("fake")
	PORT_CONFNAME(0x01,0x00,"Master-Slave Comms")
	PORT_CONFSETTING(0x00,"Normal (400 cycles)")
	PORT_CONFSETTING(0x01,"One Shot (Hack)")
INPUT_PORTS_END


/* TODO: if you change the driver configuration then NVRAM contents gets screwed, needs mods in MAME framework */
void sat_console_state::nvram_init(nvram_device &nvram, void *data, size_t size)
{
	static const uint8_t init[64] = {
	'B', 'a', 'c', 'k', 'U', 'p', 'R', 'a', 'm', ' ', 'F', 'o', 'r', 'm', 'a', 't',
	'B', 'a', 'c', 'k', 'U', 'p', 'R', 'a', 'm', ' ', 'F', 'o', 'r', 'm', 'a', 't',
	'B', 'a', 'c', 'k', 'U', 'p', 'R', 'a', 'm', ' ', 'F', 'o', 'r', 'm', 'a', 't',
	'B', 'a', 'c', 'k', 'U', 'p', 'R', 'a', 'm', ' ', 'F', 'o', 'r', 'm', 'a', 't', };

	memset(data, 0x00, size);
	memcpy(data, init, sizeof(init));
}


MACHINE_START_MEMBER(sat_console_state, saturn)
{
	m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x02400000, 0x027fffff, read32smo_delegate(*this, FUNC(sat_console_state::saturn_null_ram_r)), write32smo_delegate(*this, FUNC(sat_console_state::saturn_null_ram_w)));
	m_slave->space(AS_PROGRAM).install_readwrite_handler(0x02400000, 0x027fffff, read32smo_delegate(*this, FUNC(sat_console_state::saturn_null_ram_r)), write32smo_delegate(*this, FUNC(sat_console_state::saturn_null_ram_w)));

	m_maincpu->space(AS_PROGRAM).nop_readwrite(0x04000000, 0x047fffff);
	m_slave->space(AS_PROGRAM).nop_readwrite(0x04000000, 0x047fffff);

	m_nvram->set_base(m_backupram.get(), 0x8000);

	if (m_exp)
	{
		switch (m_exp->get_cart_type())
		{
			case 0x21:  // Battery RAM cart
			case 0x22:
			case 0x23:
			case 0x24:
				m_maincpu->space(AS_PROGRAM).install_read_handler(0x04000000, 0x047fffff, read32sm_delegate(*m_exp, FUNC(sat_cart_slot_device::read_ext_bram)));
				m_maincpu->space(AS_PROGRAM).install_write_handler(0x04000000, 0x047fffff, write32s_delegate(*m_exp, FUNC(sat_cart_slot_device::write_ext_bram)));
				m_slave->space(AS_PROGRAM).install_read_handler(0x04000000, 0x047fffff, read32sm_delegate(*m_exp, FUNC(sat_cart_slot_device::read_ext_bram)));
				m_slave->space(AS_PROGRAM).install_write_handler(0x04000000, 0x047fffff, write32s_delegate(*m_exp, FUNC(sat_cart_slot_device::write_ext_bram)));
				m_maincpu->space(AS_PROGRAM).install_read_handler(0x24000000, 0x247fffff, read32sm_delegate(*m_exp, FUNC(sat_cart_slot_device::read_ext_bram)));
				m_maincpu->space(AS_PROGRAM).install_write_handler(0x24000000, 0x247fffff, write32s_delegate(*m_exp, FUNC(sat_cart_slot_device::write_ext_bram)));
				m_slave->space(AS_PROGRAM).install_read_handler(0x24000000, 0x247fffff, read32sm_delegate(*m_exp, FUNC(sat_cart_slot_device::read_ext_bram)));
				m_slave->space(AS_PROGRAM).install_write_handler(0x24000000, 0x247fffff, write32s_delegate(*m_exp, FUNC(sat_cart_slot_device::write_ext_bram)));
				break;
			case 0x5a:  // Data RAM cart
			case 0x5c:
				m_maincpu->space(AS_PROGRAM).install_read_handler(0x02400000, 0x025fffff, read32sm_delegate(*m_exp, FUNC(sat_cart_slot_device::read_ext_dram0)));
				m_maincpu->space(AS_PROGRAM).install_write_handler(0x02400000, 0x025fffff, write32s_delegate(*m_exp, FUNC(sat_cart_slot_device::write_ext_dram0)));
				m_maincpu->space(AS_PROGRAM).install_read_handler(0x02600000, 0x027fffff, read32sm_delegate(*m_exp, FUNC(sat_cart_slot_device::read_ext_dram1)));
				m_maincpu->space(AS_PROGRAM).install_write_handler(0x02600000, 0x027fffff, write32s_delegate(*m_exp, FUNC(sat_cart_slot_device::write_ext_dram1)));
				m_slave->space(AS_PROGRAM).install_read_handler(0x02400000, 0x025fffff, read32sm_delegate(*m_exp, FUNC(sat_cart_slot_device::read_ext_dram0)));
				m_slave->space(AS_PROGRAM).install_write_handler(0x02400000, 0x025fffff, write32s_delegate(*m_exp, FUNC(sat_cart_slot_device::write_ext_dram0)));
				m_slave->space(AS_PROGRAM).install_read_handler(0x02600000, 0x027fffff, read32sm_delegate(*m_exp, FUNC(sat_cart_slot_device::read_ext_dram1)));
				m_slave->space(AS_PROGRAM).install_write_handler(0x02600000, 0x027fffff, write32s_delegate(*m_exp, FUNC(sat_cart_slot_device::write_ext_dram1)));
				m_maincpu->space(AS_PROGRAM).install_read_handler(0x22400000, 0x225fffff, read32sm_delegate(*m_exp, FUNC(sat_cart_slot_device::read_ext_dram0)));
				m_maincpu->space(AS_PROGRAM).install_write_handler(0x22400000, 0x225fffff, write32s_delegate(*m_exp, FUNC(sat_cart_slot_device::write_ext_dram0)));
				m_maincpu->space(AS_PROGRAM).install_read_handler(0x22600000, 0x227fffff, read32sm_delegate(*m_exp, FUNC(sat_cart_slot_device::read_ext_dram1)));
				m_maincpu->space(AS_PROGRAM).install_write_handler(0x22600000, 0x227fffff, write32s_delegate(*m_exp, FUNC(sat_cart_slot_device::write_ext_dram1)));
				m_slave->space(AS_PROGRAM).install_read_handler(0x22400000, 0x225fffff, read32sm_delegate(*m_exp, FUNC(sat_cart_slot_device::read_ext_dram0)));
				m_slave->space(AS_PROGRAM).install_write_handler(0x22400000, 0x225fffff, write32s_delegate(*m_exp, FUNC(sat_cart_slot_device::write_ext_dram0)));
				m_slave->space(AS_PROGRAM).install_read_handler(0x22600000, 0x227fffff, read32sm_delegate(*m_exp, FUNC(sat_cart_slot_device::read_ext_dram1)));
				m_slave->space(AS_PROGRAM).install_write_handler(0x22600000, 0x227fffff, write32s_delegate(*m_exp, FUNC(sat_cart_slot_device::write_ext_dram1)));
				break;
			case 0xff: // ROM cart + mirror
				m_maincpu->space(AS_PROGRAM).install_read_handler(0x02000000, 0x023fffff, read32sm_delegate(*m_exp, FUNC(sat_cart_slot_device::read_rom)));
				m_maincpu->space(AS_PROGRAM).install_read_handler(0x22000000, 0x223fffff, read32sm_delegate(*m_exp, FUNC(sat_cart_slot_device::read_rom)));
				m_slave->space(AS_PROGRAM).install_read_handler(0x02000000, 0x023fffff, read32sm_delegate(*m_exp, FUNC(sat_cart_slot_device::read_rom)));
				m_slave->space(AS_PROGRAM).install_read_handler(0x22000000, 0x223fffff, read32sm_delegate(*m_exp, FUNC(sat_cart_slot_device::read_rom)));
				break;
		}
	}

	// save states
//  save_pointer(NAME(m_scu_regs), 0x100/4);
	save_item(NAME(m_en_68k));
	save_item(NAME(m_scsp_last_line));
	save_item(NAME(m_vdp2.odd));
}

/* Die Hard Trilogy tests RAM address 0x25e7ffe bit 2 with Slave during FRT minit irq, in-development tool for breaking execution of it? */
uint32_t sat_console_state::saturn_null_ram_r()
{
	return 0xffffffff;
}

void sat_console_state::saturn_null_ram_w(uint32_t data)
{
}

MACHINE_RESET_MEMBER(sat_console_state,saturn)
{
	m_scsp_last_line = 0;

	// don't let the slave cpu and the 68k go anywhere
	m_slave->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
	m_audiocpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);

	m_en_68k = 0;

	//memset(stv_m_workram_l, 0, 0x100000);
	//memset(stv_m_workram_h, 0, 0x100000);

	m_maincpu->set_unscaled_clock(MASTER_CLOCK_320/2);
	m_slave->set_unscaled_clock(MASTER_CLOCK_320/2);

	m_vdp2.old_crmd = -1;
	m_vdp2.old_tvmd = -1;
}

uint8_t sat_console_state::saturn_pdr1_direct_r()
{
	return saturn_direct_port_read(false);
}

uint8_t sat_console_state::saturn_pdr2_direct_r()
{
	return saturn_direct_port_read(true);
}

void sat_console_state::saturn_pdr1_direct_w(uint8_t data)
{
	m_direct_mux[0] = data;
}

void sat_console_state::saturn_pdr2_direct_w(uint8_t data)
{
	m_direct_mux[1] = data;
}

inline uint8_t sat_console_state::saturn_direct_port_read(bool which)
{
	// bail out if direct mode is disabled
	if(m_smpc_hle->get_iosel(which) == false)
		return 0xff;

	saturn_control_port_device *port = which == true ? m_ctrl2 : m_ctrl1;
	uint8_t cur_mode = m_smpc_hle->get_ddr(which);
	uint8_t res = 0;
	uint16_t ctrl_read = port->read_direct();

//  check for control method
	switch(cur_mode & 0x60)
	{
		case 0: break;
		case 0x40: res = smpc_th_control_mode(ctrl_read,which); break;
		case 0x60: res = smpc_direct_mode(ctrl_read,which); break;
		default:
			popmessage("SMPC: unemulated control method %02x, contact MAMEdev",cur_mode & 0x60);
			break;
	}

	return res;
}

uint8_t sat_console_state::smpc_th_control_mode(uint16_t in_value, bool which)
{
	uint8_t res = 0;
	uint8_t th = (m_direct_mux[which] >> 5) & 3;

	switch (th)
	{
		/* TODO: 3D Lemmings bogusly enables TH Control mode, wants this to return the ID, needs HW tests.  */
		case 3:
			res = th << 6;
			res |= 0x14;
			res |= (in_value & 8); // L
			break;
		case 2:
			res = th << 6;
			//  1 C B Right Left Down Up
			//  WHP actually has a very specific code at 0x6015f30, doesn't like bits 0-1 active here ...
			res|= ((in_value >>  4) & 0x30); // C & B
			res|= ((in_value >> 12) & 0xc);
			break;
		case 1:
			res = th << 6;
			res |= 0x10;
			res |= ((in_value >> 4) & 0xf); // R, X, Y, Z
			break;
		case 0:
			res = th << 6;
			//  0 Start A 0 0    Down Up
			res |= ((in_value >>  6) & 0x30); // Start & A
			res |= ((in_value >> 12) & 0x03);
			//  ... and it actually wants bits 2 - 3 active here.
			res |= 0xc;
			break;
	}

	return res;
}

uint8_t sat_console_state::smpc_direct_mode(uint16_t in_value,bool which)
{
	uint8_t hshake = (m_direct_mux[which] >> 5) & 3;
	const int shift_bit[4] = { 4, 12, 8, 0 };

	return 0x80 | 0x10 | ((in_value >> shift_bit[hshake]) & 0xf);
}

void sat_console_state::saturn(machine_config &config)
{
	/* basic machine hardware */
	SH7604(config, m_maincpu, MASTER_CLOCK_352/2); // 28.6364 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &sat_console_state::saturn_mem);
	m_maincpu->set_is_slave(0);
	TIMER(config, "scantimer").configure_scanline(FUNC(sat_console_state::saturn_scanline), "screen", 0, 1);

	SH7604(config, m_slave, MASTER_CLOCK_352/2); // 28.6364 MHz
	m_slave->set_addrmap(AS_PROGRAM, &sat_console_state::saturn_mem);
	m_slave->set_is_slave(1);
	TIMER(config, "slave_scantimer").configure_scanline(FUNC(sat_console_state::saturn_slave_scanline), "screen", 0, 1);

	M68000(config, m_audiocpu, 11289600); //256 x 44100 Hz = 11.2896 MHz
	m_audiocpu->set_addrmap(AS_PROGRAM, &sat_console_state::sound_mem);
	m_audiocpu->reset_cb().set(FUNC(sat_console_state::m68k_reset_callback));

	SEGA_SCU(config, m_scu, 0);
	m_scu->set_hostcpu(m_maincpu);

//  SH-1

//  SMPC MCU, running at 4 MHz (+ custom RTC device that runs at 32.768 KHz)
	SMPC_HLE(config, m_smpc_hle, XTAL(4'000'000));
	m_smpc_hle->set_screen_tag("screen");
	m_smpc_hle->set_control_port_tags("ctrl1", "ctrl2");
	m_smpc_hle->pdr1_in_handler().set(FUNC(sat_console_state::saturn_pdr1_direct_r));
	m_smpc_hle->pdr2_in_handler().set(FUNC(sat_console_state::saturn_pdr2_direct_r));
	m_smpc_hle->pdr1_out_handler().set(FUNC(sat_console_state::saturn_pdr1_direct_w));
	m_smpc_hle->pdr2_out_handler().set(FUNC(sat_console_state::saturn_pdr2_direct_w));
	m_smpc_hle->master_reset_handler().set(FUNC(saturn_state::master_sh2_reset_w));
	m_smpc_hle->master_nmi_handler().set(FUNC(saturn_state::master_sh2_nmi_w));
	m_smpc_hle->slave_reset_handler().set(FUNC(saturn_state::slave_sh2_reset_w));
	m_smpc_hle->sound_reset_handler().set(FUNC(saturn_state::sound_68k_reset_w));
	m_smpc_hle->system_reset_handler().set(FUNC(saturn_state::system_reset_w));
	m_smpc_hle->system_halt_handler().set(FUNC(saturn_state::system_halt_w));
	m_smpc_hle->dot_select_handler().set(FUNC(saturn_state::dot_select_w));
	m_smpc_hle->interrupt_handler().set(m_scu, FUNC(sega_scu_device::smpc_irq_w));

	MCFG_MACHINE_START_OVERRIDE(sat_console_state,saturn)
	MCFG_MACHINE_RESET_OVERRIDE(sat_console_state,saturn)

	NVRAM(config, "nvram").set_custom_handler(FUNC(sat_console_state::nvram_init));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(MASTER_CLOCK_320/8, 427, 0, 320, 263, 0, 224);
	m_screen->set_screen_update(FUNC(sat_console_state::screen_update_stv_vdp2));

	PALETTE(config, m_palette).set_entries(2048+(2048*2)); //standard palette + extra memory for rgb brightness.

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_stv);

	MCFG_VIDEO_START_OVERRIDE(sat_console_state,stv_vdp2)

	SPEAKER(config, "speaker", 2).front();

	SCSP(config, m_scsp, 8467200*8/3); // 8.4672 MHz EXTCLK * 8 / 3 = 22.5792 MHz
	m_scsp->set_addrmap(0, &sat_console_state::scsp_mem);
	m_scsp->irq_cb().set(FUNC(saturn_state::scsp_irq));
	m_scsp->main_irq_cb().set(m_scu, FUNC(sega_scu_device::sound_req_w));
	m_scsp->add_route(0, "speaker", 1.0, 0);
	m_scsp->add_route(1, "speaker", 1.0, 1);

	stvcd_device &stvcd(STVCD(config, "stvcd", 0));
	stvcd.add_route(0, "scsp", 1.0, 0);
	stvcd.add_route(1, "scsp", 1.0, 1);

	SATURN_CONTROL_PORT(config, "ctrl1", saturn_controls, "joypad");
	SATURN_CONTROL_PORT(config, "ctrl2", saturn_controls, "joypad");
}

static void saturn_cart(device_slot_interface &device)
{
	device.option_add_internal("rom",    SATURN_ROM);
	device.option_add_internal("ram8",   SATURN_DRAM_8MB);
	device.option_add_internal("ram32",  SATURN_DRAM_32MB);
	device.option_add_internal("bram4",  SATURN_BRAM_4MB);
	device.option_add_internal("bram8",  SATURN_BRAM_8MB);
	device.option_add_internal("bram16", SATURN_BRAM_16MB);
	device.option_add_internal("bram32", SATURN_BRAM_32MB);
}


void sat_console_state::saturnus(machine_config &config)
{
	saturn(config);
	SATURN_CDB(config, "saturn_cdb", 16000000);

	SOFTWARE_LIST(config, "cd_list").set_original("saturn").set_filter("NTSC-U");
	SOFTWARE_LIST(config, "photocd_list").set_compatible("photo_cd");

	SATURN_CART_SLOT(config, "exp", saturn_cart, nullptr);
	SOFTWARE_LIST(config, "cart_list").set_original("sat_cart");

	m_smpc_hle->set_region_code(REGION_NTSC_USA);
}

void sat_console_state::saturneu(machine_config &config)
{
	saturn(config);
	SATURN_CDB(config, "saturn_cdb", 16000000);

	SOFTWARE_LIST(config, "cd_list").set_original("saturn").set_filter("PAL");
	SOFTWARE_LIST(config, "photocd_list").set_compatible("photo_cd");

	SATURN_CART_SLOT(config, "exp", saturn_cart, nullptr);
	SOFTWARE_LIST(config, "cart_list").set_original("sat_cart");

	m_smpc_hle->set_region_code(REGION_PAL_EUROPE);
}

void sat_console_state::saturnjp(machine_config &config)
{
	saturn(config);
	SATURN_CDB(config, "saturn_cdb", 16000000);

	SOFTWARE_LIST(config, "cd_list").set_original("saturn").set_filter("NTSC-J");
	SOFTWARE_LIST(config, "photocd_list").set_compatible("photo_cd");

	SATURN_CART_SLOT(config, "exp", saturn_cart, nullptr);
	SOFTWARE_LIST(config, "cart_list").set_original("sat_cart");

	m_smpc_hle->set_region_code(REGION_NTSC_JAPAN);
}

void sat_console_state::saturnkr(machine_config &config)
{
	saturn(config);
	SATURN_CDB(config, "saturn_cdb", 16000000);

	SOFTWARE_LIST(config, "cd_list").set_original("saturn").set_filter("NTSC-K");

	SATURN_CART_SLOT(config, "exp", saturn_cart, nullptr);
	SOFTWARE_LIST(config, "cart_list").set_original("sat_cart");

	m_smpc_hle->set_region_code(REGION_NTSC_KOREA);
}


template <bool is_pal> void sat_console_state::init_saturn()
{
	// TODO: setter for (missing) VDP2 device
	m_vdp2.pal = is_pal;

	// set compatible options
	m_maincpu->sh2drc_set_options(SH2DRC_STRICT_VERIFY|SH2DRC_STRICT_PCREL);
	m_slave->sh2drc_set_options(SH2DRC_STRICT_VERIFY|SH2DRC_STRICT_PCREL);

	m_maincpu->sh2drc_add_fastram(0x00000000, 0x0007ffff, 1, &m_rom[0]);
	m_maincpu->sh2drc_add_fastram(0x00200000, 0x002fffff, 0, &m_workram_l[0]);
	m_maincpu->sh2drc_add_fastram(0x06000000, 0x060fffff, 0, &m_workram_h[0]);
	m_slave->sh2drc_add_fastram(0x00000000, 0x0007ffff, 1, &m_rom[0]);
	m_slave->sh2drc_add_fastram(0x00200000, 0x002fffff, 0, &m_workram_l[0]);
	m_slave->sh2drc_add_fastram(0x06000000, 0x060fffff, 0, &m_workram_h[0]);

	/* amount of time to boost interleave for on MINIT / SINIT, needed for communication to work */
	m_minit_boost = 400;
	m_sinit_boost = 400;
	m_minit_boost_timeslice = attotime::zero;
	m_sinit_boost_timeslice = attotime::zero;

//  m_scu_regs = make_unique_clear<uint32_t[]>(0x100/4);
	m_backupram = make_unique_clear<uint8_t[]>(0x8000);
}

ROM_START( saturnjp )
	ROM_REGION32_BE( 0x80000, "bios", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "101", "Japan v1.01 (941228)")
	ROMX_LOAD("sega_101.bin", 0x00000000, 0x00080000, CRC(224b752c) SHA1(df94c5b4d47eb3cc404d88b33a8fda237eaf4720), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "1003", "Japan v1.003 (941012)")
	ROMX_LOAD("sega1003.bin", 0x00000000, 0x00080000, CRC(b3c63c25) SHA1(7b23b53d62de0f29a23e423d0fe751dfb469c2fa), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "100", "Japan v1.00 (940921)")
	ROMX_LOAD("sega_100.bin", 0x00000000, 0x00080000, CRC(2aba43c2) SHA1(2b8cb4f87580683eb4d760e4ed210813d667f0a2), ROM_BIOS(2))
ROM_END

ROM_START( saturn )
	ROM_REGION32_BE( 0x80000, "bios", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "101a", "Overseas v1.01a (941115)")
	/* Confirmed by ElBarto */
	ROMX_LOAD("mpr-17933.bin", 0x00000000, 0x00080000, CRC(4afcf0fa) SHA1(faa8ea183a6d7bbe5d4e03bb1332519800d3fbc3), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "100a", "Overseas v1.00a (941115)")
	ROMX_LOAD("sega_100a.bin", 0x00000000, 0x00080000, CRC(f90f0089) SHA1(3bb41feb82838ab9a35601ac666de5aacfd17a58), ROM_BIOS(1))
ROM_END

ROM_START( saturneu )
	ROM_REGION32_BE( 0x80000, "bios", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "101a", "Overseas v1.01a (941115)")
	/* Confirmed by ElBarto */
	ROMX_LOAD("mpr-17933.bin", 0x00000000, 0x00080000, CRC(4afcf0fa) SHA1(faa8ea183a6d7bbe5d4e03bb1332519800d3fbc3), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "100a", "Overseas v1.00a (941115)")
	ROMX_LOAD("sega_100a.bin", 0x00000000, 0x00080000, CRC(f90f0089) SHA1(3bb41feb82838ab9a35601ac666de5aacfd17a58), ROM_BIOS(1))
ROM_END

ROM_START( saturnkr )
	ROM_REGION32_BE( 0x80000, "bios", ROMREGION_ERASEFF )
	// undumped, uses Japanese VA1 motherboard with v1.02a BIOS rev,
	// with extra checks for region jumpers that disables Japanese language if setting matches '2' (no Korea option tho)
	ROM_LOAD("sega_101.bin", 0x00000000, 0x00080000, BAD_DUMP CRC(224b752c) SHA1(df94c5b4d47eb3cc404d88b33a8fda237eaf4720) )
ROM_END

ROM_START( vsaturn )
	ROM_REGION32_BE( 0x80000, "bios", ROMREGION_ERASEFF )
	ROM_LOAD("vsaturn.bin", 0x00000000, 0x00080000, CRC(e4d61811) SHA1(4154e11959f3d5639b11d7902b3a393a99fb5776))
ROM_END

ROM_START( hisaturn )
	ROM_REGION32_BE( 0x80000, "bios", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "102", "v1.02 (950519)")
	ROMX_LOAD("mpr-18100.bin", 0x000000, 0x080000, CRC(3408dbf4) SHA1(8a22710e09ce75f39625894366cafe503ed1942d), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "101", "v1.01 (950130)")
	ROMX_LOAD("hisaturn.bin", 0x00000000, 0x00080000, CRC(721e1b60) SHA1(49d8493008fa715ca0c94d99817a5439d6f2c796), ROM_BIOS(1))
ROM_END

/*    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT   CLASS              INIT           COMPANY    FULLNAME            FLAGS */
CONS( 1994, saturn,   0,      0,      saturnus, saturn, sat_console_state, init_saturn<false>, "Sega",    "Saturn (USA)",     MACHINE_NOT_WORKING )
CONS( 1994, saturnjp, saturn, 0,      saturnjp, saturn, sat_console_state, init_saturn<false>, "Sega",    "Saturn (Japan)",   MACHINE_NOT_WORKING )
CONS( 1994, saturneu, saturn, 0,      saturneu, saturn, sat_console_state, init_saturn<true>,  "Sega",    "Saturn (PAL)",     MACHINE_NOT_WORKING )
CONS( 1995, saturnkr, saturn, 0,      saturnkr, saturn, sat_console_state, init_saturn<false>, "Samsung", "Saturn (Korea)",   MACHINE_NOT_WORKING )
CONS( 1995, vsaturn,  saturn, 0,      saturnjp, saturn, sat_console_state, init_saturn<false>, "JVC",     "V-Saturn",         MACHINE_NOT_WORKING )
CONS( 1995, hisaturn, saturn, 0,      saturnjp, saturn, sat_console_state, init_saturn<false>, "Hitachi", "HiSaturn",         MACHINE_NOT_WORKING )



savant.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Novag Savant, chess computer with touchscreen. It was followed by Savant II and
Savant Royale on the same hardware, the program is the same and they just added
a bigger opening book. The chess engine is MyChess by David Kittinger.

Like the 1984 version Chess Robot Adversary, Savant Royale was marketed as 7.5MHz,
but it's not known how they sped it up,

Hardware overview:
- Zilog Z80B @ 6MHz
- 24KB ROM(3*TMM2364) + sockets for expansion (populated in Savant II)
- 4KB RAM(8*MM2114N-2L), 256x4 battery-backed RAM(MWS5101)
- Fairchild 3870 @ 4MHz (2KB internal ROM)
- HLCD0538, HLCD0539, LCD screen with 8*8 touch-sensitive overlay
- external ports for optional chess clock and printer

The display (both the LCD and the sensors) didn't last long, probably none exist
anymore in original working order.

*******************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "cpu/f8/f8.h"
#include "machine/f3853.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/hlcd0538.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "novag_savant.lh"


namespace {

class savant_state : public driver_device
{
public:
	savant_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_mcu(*this, "mcu"),
		m_psu(*this, "psu"),
		m_lcd1(*this, "lcd1"),
		m_lcd2(*this, "lcd2"),
		m_display(*this, "display"),
		m_board(*this, "board"),
		m_dac(*this, "dac"),
		m_nvram(*this, "nvram"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// machine configs
	void savant(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<cpu_device> m_mcu;
	required_device<f38t56_device> m_psu;
	required_device<hlcd0538_device> m_lcd1;
	required_device<hlcd0539_device> m_lcd2;
	required_device<pwm_display_device> m_display;
	required_device<sensorboard_device> m_board;
	required_device<dac_1bit_device> m_dac;
	required_shared_ptr<u8> m_nvram;
	required_ioport_array<3> m_inputs;

	bool m_z80_wait = false;
	u8 m_inp_mux = 0;
	u8 m_databus = 0;
	u8 m_control = 0;
	u64 m_lcd_data = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;
	void mcu_map(address_map &map) ATTR_COLD;
	void mcu_io(address_map &map) ATTR_COLD;

	// I/O handlers
	void nvram_w(offs_t offset, u8 data);
	u8 nvram_r(offs_t offset);
	u8 stall_r(offs_t offset);
	void stall_w(offs_t offset, u8 data = 0);
	u8 mcustatus_r();

	void lcd1_output_w(u64 data);
	void lcd2_output_w(u64 data);

	u8 databus_r();
	void databus_w(u8 data);
	u8 control_r();
	void control_w(u8 data);
	void lcd_w(u8 data);
	u8 input_r();
};

void savant_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_z80_wait));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_databus));
	save_item(NAME(m_control));
	save_item(NAME(m_lcd_data));
}

void savant_state::machine_reset()
{
	m_z80_wait = false;
}



/*******************************************************************************
    I/O
*******************************************************************************/

// Z80 side

void savant_state::nvram_w(offs_t offset, u8 data)
{
	// nvram is only d0-d3
	m_nvram[offset] = data & 0xf;
}

u8 savant_state::nvram_r(offs_t offset)
{
	return m_nvram[offset] & 0xf;
}

void savant_state::stall_w(offs_t offset, u8 data)
{
	// any access to port C0 puts the Z80 into WAIT, sets BUSRQ, and sets MCU EXT INT
	if (!m_z80_wait)
	{
		m_databus = offset >> 8;
		m_psu->ext_int_w(1);
		m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE);
		m_maincpu->set_input_line(Z80_INPUT_LINE_BUSRQ, ASSERT_LINE);
		m_maincpu->retry_access();
	}

	m_z80_wait = !m_z80_wait;
}

u8 savant_state::stall_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		stall_w(offset);

	return m_databus;
}

u8 savant_state::mcustatus_r()
{
	// d0: MCU P1.2
	return BIT(~m_control, 2);
}


// 3870 side

void savant_state::lcd1_output_w(u64 data)
{
	// uses C1-C24
	m_lcd_data = m_lcd_data << 24 | (data >> 8 & 0xffffff);
	m_display->matrix(data & 0xff, m_lcd_data);
}

void savant_state::lcd2_output_w(u64 data)
{
	// uses C6-C32
	m_lcd_data = data >> 5 & 0x7ffffff;
}

u8 savant_state::databus_r()
{
	return ~m_databus;
}

void savant_state::databus_w(u8 data)
{
	m_databus = ~data;
}

u8 savant_state::control_r()
{
	return m_control;
}

void savant_state::control_w(u8 data)
{
	// d0: clear EXT INT, clear Z80 WAIT
	if (data & ~m_control & 1)
	{
		m_psu->ext_int_w(0);
		m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, CLEAR_LINE);
	}

	// d1: clear Z80 BUSRQ
	if (data & ~m_control & 2)
		m_maincpu->set_input_line(Z80_INPUT_LINE_BUSRQ, CLEAR_LINE);

	// d2: return data for Z80

	// d3: speaker out
	m_dac->write(BIT(data, 3));

	// d4: LCD pins
	m_lcd2->lcd_w(BIT(~data, 4));
	m_lcd1->lcd_w(BIT(~data, 4));

	// d5-d7: keypad mux
	m_control = data;
}

void savant_state::lcd_w(u8 data)
{
	// d0: HLCD0538 data
	// d4: HLCD0539 data
	m_lcd1->data_w(BIT(~data, 0));
	m_lcd2->data_w(BIT(~data, 4));

	// STROBE pin to LCD chips CLK
	m_lcd1->clk_w(1); m_lcd1->clk_w(0);
	m_lcd2->clk_w(1); m_lcd2->clk_w(0);

	// also touchscreen input mux
	m_inp_mux = bitswap<8>(data,7,3,6,2,5,1,4,0);
}

u8 savant_state::input_r()
{
	u8 data = 0;

	// read touchscreen
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_rank(i);

	// read keypad
	for (int i = 0; i < 3; i++)
		if (BIT(m_control >> 5, i))
			data |= m_inputs[i]->read();

	return bitswap<8>(data,3,2,1,0,4,5,6,7);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void savant_state::main_map(address_map &map)
{
	map(0x0000, 0xbfff).rom();
	map(0xc000, 0xcfff).ram();
	map(0xd000, 0xd0ff).mirror(0x0300).ram().rw(FUNC(savant_state::nvram_r), FUNC(savant_state::nvram_w)).share("nvram");
}

void savant_state::main_io(address_map &map)
{
	map(0xc0, 0xc0).mirror(0x0038).select(0xff00).rw(FUNC(savant_state::stall_r), FUNC(savant_state::stall_w));
	map(0xc1, 0xc1).mirror(0xff38).nopw(); // clock
	map(0xc2, 0xc2).mirror(0xff38).nopw(); // printer
	map(0xc3, 0xc3).mirror(0xff38).nopr(); // printer
	map(0xc4, 0xc4).mirror(0xff38).r(FUNC(savant_state::mcustatus_r));
	map(0xc5, 0xc5).mirror(0xff38).nopw(); // printer
}

void savant_state::mcu_map(address_map &map)
{
	map.global_mask(0x7ff);
	map(0x0000, 0x07ff).rom();
}

void savant_state::mcu_io(address_map &map)
{
	map(0x00, 0x00).rw(FUNC(savant_state::databus_r), FUNC(savant_state::databus_w));
	map(0x01, 0x01).rw(FUNC(savant_state::control_r), FUNC(savant_state::control_w));
	map(0x04, 0x07).rw(m_psu, FUNC(f38t56_device::read), FUNC(f38t56_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( savant )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Review")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Print Board")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Sound on/off / Knight")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Solve Mate")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("Show Moves")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Form Size")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Best Move / Bishop")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Clear Board")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Trace Forward")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Print List")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Set Rate / Rook")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Classic Game")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("Return")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("Auto Play")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Mate Alert / Queen")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Change Color")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("Trace Back")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Print Moves")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Set Level / Pawn")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("New Game")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("Go")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Hint")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Promote")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Set Up")

	PORT_START("LIGHT") // user-controlled light switch (9 light bulbs behind LCD panel)
	PORT_CONFNAME( 0x01, 0x01, "LCD Light" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void savant_state::savant(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 6_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &savant_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &savant_state::main_io);

	F8(config, m_mcu, 4_MHz_XTAL/2);
	m_mcu->set_addrmap(AS_PROGRAM, &savant_state::mcu_map);
	m_mcu->set_addrmap(AS_IO, &savant_state::mcu_io);
	m_mcu->set_irq_acknowledge_callback("psu", FUNC(f38t56_device::int_acknowledge));

	F38T56(config, m_psu, 4_MHz_XTAL/2);
	m_psu->set_int_vector(0x0020);
	m_psu->int_req_callback().set_inputline(m_mcu, F8_INPUT_LINE_INT_REQ);
	m_psu->write_a().set(FUNC(savant_state::lcd_w));
	m_psu->read_b().set(FUNC(savant_state::input_r));

	config.set_perfect_quantum(m_mcu);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->set_delay(attotime::from_msec(200));
	m_board->set_ui_enable(false); // no chesspieces
	m_board->set_mod_enable(true);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	// video hardware
	HLCD0538(config, m_lcd1).write_cols().set(FUNC(savant_state::lcd1_output_w));
	HLCD0539(config, m_lcd2).write_cols().set(FUNC(savant_state::lcd2_output_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(958, 1080);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(8, 24+27);
	config.set_default_layout(layout_novag_savant);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( savant )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD("5605_1f_orange.u13", 0x0000, 0x2000, CRC(0f24fd37) SHA1(b9426b53623d2a98aa2b3099010a7579b0f51db5) ) // TMM2364
	ROM_LOAD("5606_1g_white.u14",  0x2000, 0x2000, CRC(e8b2eddd) SHA1(5f148a3c1c2cd099bd19a48d972a01e5e26ef2ff) ) // "
	ROM_LOAD("5607_1e_blue.u15",   0x4000, 0x2000, CRC(a07f845a) SHA1(e45218fdf955777e571a71ae9d501567b760a3c0) ) // "
	// 3 more ROM sockets not populated(yellow, red, gold), manual mentions possible expansion

	ROM_REGION( 0x0800, "mcu", 0 )
	ROM_LOAD("sl90547.u29", 0x0000, 0x0800, CRC(6fbf2aa0) SHA1(18e673ba5b806b397dd3d350525b5467c25a0d94) )

	ROM_REGION( 764183, "screen", 0)
	ROM_LOAD("savant.svg", 0, 764183, CRC(c3adac84) SHA1(e534aa0a3d339b5351a44aa0507c1ae6af8e1d75) )
ROM_END

ROM_START( savant2 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD("5605_1f_orange.u13",    0x0000, 0x2000, CRC(0f24fd37) SHA1(b9426b53623d2a98aa2b3099010a7579b0f51db5) ) // TMM2364
	ROM_LOAD("5606_1g_white.u14",     0x2000, 0x2000, CRC(e8b2eddd) SHA1(5f148a3c1c2cd099bd19a48d972a01e5e26ef2ff) ) // "
	ROM_LOAD("5607_1e_blue.u15",      0x4000, 0x2000, CRC(a07f845a) SHA1(e45218fdf955777e571a71ae9d501567b760a3c0) ) // "
	ROM_LOAD("c2m040_savant_red.u17", 0x8000, 0x2000, CRC(0025afb4) SHA1(4f1b63754ed1cc6d765165ec217556b5e7705df6) ) // "

	ROM_REGION( 0x0800, "mcu", 0 )
	ROM_LOAD("sl90547.u29", 0x0000, 0x0800, CRC(6fbf2aa0) SHA1(18e673ba5b806b397dd3d350525b5467c25a0d94) )

	ROM_REGION( 764183, "screen", 0)
	ROM_LOAD("savant.svg", 0, 764183, CRC(c3adac84) SHA1(e534aa0a3d339b5351a44aa0507c1ae6af8e1d75) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1981, savant,  0,      0,      savant,  savant, savant_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Savant", MACHINE_SUPPORTS_SAVE )
SYST( 1982, savant2, savant, 0,      savant,  savant, savant_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Savant II", MACHINE_SUPPORTS_SAVE )



savia84.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Savia 84

More data at :
        http://www.nostalcomp.cz/pdfka/savia84.pdf
        http://www.nostalcomp.cz/savia.php
        (use archive.org)

2011-02-05 Skeleton driver.
2011-10-11 Found a new rom. Working [Robbbert]

I assume all the LEDs are red ones. The LEDs down the
 left side I assume to be bit 0 through 7 in that order.

Pasting:
        0-F : as is
        DA : ^
        AD : -
        GO : X

Here is a test program. Copy the text and Paste into the emulator.
    -1800^3E^55^D3^F9^76^XX1800^

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/i8255.h"
#include "video/pwm.h"
#include "savia84.lh"


namespace {

class savia84_state : public driver_device
{
public:
	savia84_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ppi8255(*this, "ppi8255")
		, m_display(*this, "display")
		, m_io_keyboard(*this, "X%u", 0U)
		, m_leds(*this, "led%u", 0U)
		{ }

	void savia84(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	uint8_t ppi_portc_r();
	void ppi_porta_w(uint8_t data);
	void ppi_portb_w(uint8_t data);
	void ppi_portc_w(uint8_t data);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	uint8_t m_digit = 0U;
	uint8_t m_seg = 0U;

	required_device<cpu_device> m_maincpu;
	required_device<i8255_device> m_ppi8255;
	required_device<pwm_display_device> m_display;
	required_ioport_array<9> m_io_keyboard;
	output_finder<8> m_leds;
};

void savia84_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x7fff); // A15 not connected at the CPU
	map(0x0000, 0x07ff).rom();
	map(0x1800, 0x1fff).ram();
}

void savia84_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x07);
	map(0x00, 0x03).rw(m_ppi8255, FUNC(i8255_device::read), FUNC(i8255_device::write)); // ports F8-FB
}

/* Input ports */
static INPUT_PORTS_START( savia84 )
	PORT_START("X0")
	PORT_BIT( 0x9F, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')

	PORT_START("X1")
	PORT_BIT( 0x8F, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')

	PORT_START("X2")
	PORT_BIT( 0x8F, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')

	PORT_START("X3")
	PORT_BIT( 0x9F, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Ex") PORT_CODE(KEYCODE_X) PORT_CHAR('X')

	PORT_START("X4")
	PORT_BIT( 0x8F, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BR") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')

	PORT_START("X5")
	PORT_BIT( 0x8F, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("AD") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')

	PORT_START("X6")
	PORT_BIT( 0x9F, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')

	PORT_START("X7")
	PORT_BIT( 0x9F, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')

	PORT_START("X8")
	PORT_BIT( 0x8F, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DA") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
INPUT_PORTS_END


void savia84_state::ppi_porta_w(uint8_t data) // OUT F8 - output segments on the selected digit
{
	m_seg = ~data;
	m_display->matrix(1 << m_digit, m_seg);
}

void savia84_state::ppi_portb_w(uint8_t data) // OUT F9 - light the 8 leds down the left side
{
	for (int i = 0; i < 8; i++)
		m_leds[i] = !BIT(data, i);
}

void savia84_state::ppi_portc_w(uint8_t data) // OUT FA - set keyboard scanning row; set digit to display
{
	m_digit = data & 15;
	m_display->matrix(1 << m_digit, m_seg);
}

uint8_t savia84_state::ppi_portc_r() // IN FA - read keyboard
{
	if (m_digit < 9)
		return m_io_keyboard[m_digit]->read();
	else
		return 0xff;
}

void savia84_state::machine_start()
{
	m_leds.resolve();

	save_item(NAME(m_digit));
	save_item(NAME(m_seg));
}

void savia84_state::savia84(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'000'000) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &savia84_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &savia84_state::io_map);

	/* video hardware */
	config.set_default_layout(layout_savia84);
	PWM_DISPLAY(config, m_display).set_size(9, 7);
	m_display->set_segmask(0x1fd, 0x7f);

	/* Devices */
	I8255(config, m_ppi8255);
	m_ppi8255->out_pa_callback().set(FUNC(savia84_state::ppi_porta_w));
	m_ppi8255->out_pb_callback().set(FUNC(savia84_state::ppi_portb_w));
	m_ppi8255->in_pc_callback().set(FUNC(savia84_state::ppi_portc_r));
	m_ppi8255->out_pc_callback().set(FUNC(savia84_state::ppi_portc_w));
}

/* ROM definition */
ROM_START( savia84 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD("savia84.bin", 0x0000, 0x0800, CRC(fa8f1fcf) SHA1(b08404469ed988d96c0413416b6a66f3e5b997a3))

	// Note - the below is a bad dump and does not work
	//ROM_LOAD("savia84_1kb.bin", 0x0000, 0x0400, CRC(23a5c15e) SHA1(7e769ed8960d8c591a25cfe4effffcca3077c94b))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY      FULLNAME    FLAGS
COMP( 1984, savia84, 0,      0,      savia84, savia84, savia84_state, empty_init, "J.T. Hyan", "Savia 84", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



sb180.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

    Micromint SB180

****************************************************************************/

#include "emu.h"
#include "cpu/z180/z180.h"
#include "imagedev/floppy.h"
#include "machine/upd765.h"
#include "bus/rs232/rs232.h"


namespace {

#define FDC9266_TAG "u24"

class sb180_state : public driver_device
{
public:
	sb180_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_fdc(*this, FDC9266_TAG)
		, m_floppy(*this, FDC9266_TAG ":%u", 0)
	{ }

	void sb180(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;

	void sb180_io(address_map &map) ATTR_COLD;
	void sb180_mem(address_map &map) ATTR_COLD;

	required_device<z180_device> m_maincpu;
	required_device<upd765a_device> m_fdc;
	required_device_array<floppy_connector, 4> m_floppy;
};

void sb180_state::sb180_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x01fff).rom().mirror(0x3e000);
	map(0x40000, 0x7ffff).ram(); // 256KB RAM  8 * 41256 DRAM
}

void sb180_state::sb180_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x7f).ram(); /* Z180 internal registers */
	map(0x80, 0x81).m(m_fdc, FUNC(upd765a_device::map));
	map(0xa0, 0xa0).rw(m_fdc, FUNC(upd765a_device::dma_r), FUNC(upd765a_device::dma_w));
}

/* Input ports */
static INPUT_PORTS_START( sb180 )
INPUT_PORTS_END

void sb180_state::machine_reset()
{
	// motor is actually connected on TXS pin of CPU
	for (auto &drive : m_floppy)
	{
		if (drive->get_device())
			drive->get_device()->mon_w(0);
	}
}

static void sb180_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
}

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void sb180_state::sb180(machine_config &config)
{
	/* basic machine hardware */
	HD64180RP(config, m_maincpu, XTAL(12'288'000)); // location U17 HD64180
	m_maincpu->set_addrmap(AS_PROGRAM, &sb180_state::sb180_mem);
	m_maincpu->set_addrmap(AS_IO, &sb180_state::sb180_io);
	m_maincpu->tend1_wr_callback().set(m_fdc, FUNC(upd765a_device::tc_line_w));
	m_maincpu->txa1_wr_callback().set("rs232", FUNC(rs232_port_device::write_txd));

	// FDC9266 location U24
	UPD765A(config, m_fdc, XTAL(8'000'000));
	m_fdc->intrq_wr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ2);
	m_fdc->drq_wr_callback().set_inputline(m_maincpu, Z180_INPUT_LINE_DREQ1);

	/* floppy drives */
	FLOPPY_CONNECTOR(config, m_floppy[0], sb180_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[1], sb180_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[2], sb180_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[3], sb180_floppies, "35dd", floppy_image_device::default_mfm_floppy_formats);

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal)); // must be below the DEVICE_INPUT_DEFAULTS_START block
	rs232.rxd_handler().set(m_maincpu, FUNC(z180_device::rxa1_w));
}

/* ROM definition */
ROM_START( sb180 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "monitor.bin", 0x0000, 0x2000, CRC(49640012) SHA1(ea571dc7476430e31b74bd1ab7a577e9013ad0bd))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY      FULLNAME   FLAGS */
COMP( 1985, sb180,  0,      0,      sb180,   sb180,  sb180_state,  empty_init, "Micromint", "SB180",   MACHINE_NO_SOUND)



sb8085.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Preliminary driver for Space Byte 8085 CPU.

    This is an early Intel 8085-based single-board computer whose onboard
    I/O consists of RS232 ports for a CRT and serial printer and a parallel
    interface intended for use with ICOM's FD 3700 or Frugal Floppy disk
    controller. The resident monitor program also supports the Tarbell
    cassette tape interface and the PolyMorphic Systems Video Terminal
    Interface.

    The bus connector is only somewhat S-100 compatible. Anticipating the
    draft IEEE standard, no clock signal is placed on pin 25 or 49 (though
    documentation recommends jumpering pin 49 to the 3.072 MHz clock on pin
    24 for the VTI). The conventional INT and VI0-7 pins are disregarded in
    favor of collecting the four 8085-specific interrupts on pins 12-15 in
    ascending order of priority. SDSB/CDSB/ASDB/DODSB are also disregarded,
    and there is an alternate PHOLD input on pin 16.

    Space Byte also advertised a 16K static RAM board and a 2708/2716
    EPROM programmer for use in this system.

    Getting it to work with the terminal:
    1. Slot CRT: choose Terminal. (The VTI should be removed from S100 slot
       1 or else its keyboard must be disabled later.)
    2. Reboot
    3. Press A (certain other keys may work here). It will autodetect the
       baud rate and start up. (Only 110, 150, 300, 1200, 2400, 4800 and
       9600 baud are detected properly.)
    4. Now you can enter commands (such as D000,FFF)
    5. If it says KEYBOARD ERROR it doesn't mean a problem with the
       keyboard, it means you made a mistake.

    To use the monitor with the VTI, start by typing A (in uppercase).

***************************************************************************/

#include "emu.h"

#include "cpu/i8085/i8085.h"
#include "bus/rs232/rs232.h"
#include "bus/s100/s100.h"
#include "bus/s100/polyvti.h"
#include "bus/s100/seals8k.h"
#include "machine/i8155.h"
#include "machine/i8251.h"


namespace {

class sb8085_state : public driver_device
{
public:
	sb8085_state(const machine_config &config, device_type type, const char *tag)
		: driver_device(config, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_i8155(*this, "i8155")
		, m_usart(*this, "usart")
		, m_crt(*this, "crt")
		, m_printer(*this, "printer")
		, m_s100(*this, "s100")
		, m_program(*this, "program")
		, m_config_select(*this, "SELECT")
		, m_portd(*this, "PORTD")
	{
	}

	static constexpr feature_type unemulated_features() { return feature::DISK | feature::TAPE; }

	void sb8085(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	bool board_selected(offs_t offset);
	u8 mem_r(offs_t offset);
	void mem_w(offs_t offset, u8 data);
	u8 in_r(offs_t offset);
	void out_w(offs_t offset, u8 data);
	void phantom_disable_w(int state);

	void printer_select_w(int state);
	void usart_txd_w(int state);
	void crt_rts_w(int state);
	void printer_rts_w(int state);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<i8085a_cpu_device> m_maincpu;
	required_device<i8155_device> m_i8155;
	required_device<i8251_device> m_usart;
	required_device<rs232_port_device> m_crt;
	required_device<rs232_port_device> m_printer;
	required_device<s100_bus_device> m_s100;
	required_region_ptr<u8> m_program;
	required_ioport m_config_select;
	required_ioport m_portd;

	bool m_phantom;
	bool m_printer_select;
	bool m_usart_txd;
	bool m_crt_rts;
	bool m_printer_rts;
};

void sb8085_state::machine_start()
{
	m_printer_select = false;
	m_usart_txd = true;
	m_crt_rts = true;
	m_printer_rts = true;

	// Pins 6, 8, 5 (printer) are tied to +12
	m_crt->write_dtr(0);
	m_printer->write_rts(0);
	m_printer->write_dtr(0);

	save_item(NAME(m_phantom));
	save_item(NAME(m_printer_select));
	save_item(NAME(m_usart_txd));
	save_item(NAME(m_crt_rts));
	save_item(NAME(m_printer_rts));
}

void sb8085_state::machine_reset()
{
	m_phantom = false;
}

bool sb8085_state::board_selected(offs_t offset)
{
	if (!m_phantom)
		return true;

	ioport_value config = m_config_select->read();
	return !BIT(config, 1) || (offset & 0xe000) == (BIT(config, 0) ? 0xe000 : 0xc000);
}

u8 sb8085_state::mem_r(offs_t offset)
{
	if (board_selected(offset))
	{
		if ((offset & 0x1c00) < 0x0c00)
			return m_program[offset & 0xfff];
		else if ((offset & 0x1c00) >= 0x1800)
			return m_i8155->memory_r(offset & 0xff);
		else
			return 0xff;
	}
	else
		return m_s100->smemr_r(offset);
}

void sb8085_state::mem_w(offs_t offset, u8 data)
{
	if (board_selected(offset))
	{
		if ((offset & 0x1c00) >= 0x1800)
			m_i8155->memory_w(offset & 0xff, data);
	}
	else
		m_s100->mwrt_w(offset, data);
}

u8 sb8085_state::in_r(offs_t offset)
{
	offset |= offset << 8;
	if (board_selected(offset))
	{
		if ((offset & 0x1c00) == 0x0000)
			return m_usart->read(BIT(offset, 8));
		else if ((offset & 0x1c00) == 0x0c00)
			return m_portd->read() ^ 0xff;
		else if ((offset & 0x1c00) >= 0x1800)
			return m_i8155->io_r(offset & 7);
		else
			return 0xff;
	}
	else
		return m_s100->sinp_r(offset);
}

void sb8085_state::out_w(offs_t offset, u8 data)
{
	offset |= offset << 8;
	if (board_selected(offset))
	{
		if ((offset & 0x1c00) == 0x0000)
			m_usart->write(BIT(offset, 8), data);
		else if ((offset & 0x1c00) >= 0x1800)
			m_i8155->io_w(offset & 7, data);
	}
	else
		m_s100->sout_w(offset, data);
}

void sb8085_state::phantom_disable_w(int state)
{
	if (!state)
		m_phantom = true;
}

void sb8085_state::printer_select_w(int state)
{
	if (state && m_printer_select)
	{
		m_printer_select = false;
		m_crt->write_txd(m_usart_txd);
		m_printer->write_txd(1);
		m_usart->write_cts(m_crt_rts);
	}
	else if (!state && !m_printer_select)
	{
		m_printer_select = true;
		m_crt->write_txd(1);
		m_printer->write_txd(m_usart_txd);
		m_usart->write_cts(m_printer_rts);
	}
}

void sb8085_state::usart_txd_w(int state)
{
	m_usart_txd = state;
	if (m_printer_select)
		m_printer->write_txd(state);
	else
		m_crt->write_txd(state);
}

void sb8085_state::crt_rts_w(int state)
{
	m_crt_rts = state;
	if (!m_printer_select)
		m_usart->write_cts(state);
}

void sb8085_state::printer_rts_w(int state)
{
	m_printer_rts = state;
	if (m_printer_select)
		m_usart->write_cts(state);
}

void sb8085_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(sb8085_state::mem_r), FUNC(sb8085_state::mem_w));
}

void sb8085_state::io_map(address_map &map)
{
	map(0x00, 0xff).rw(FUNC(sb8085_state::in_r), FUNC(sb8085_state::out_w));
}

INPUT_PORTS_START(sb8085)
	PORT_START("SELECT")
	PORT_CONFNAME(3, 2, "Board select")
	PORT_CONFSETTING(0, "Single board")
	PORT_CONFSETTING(2, "C000-DFFFH")
	PORT_CONFSETTING(3, "E000-FFFFH")

	PORT_START("PORTD")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN) // from disk connector
INPUT_PORTS_END


static void sb8085_s100_devices(device_slot_interface &device)
{
	device.option_add("vti", S100_POLY_VTI);
	device.option_add("8ksc", S100_8K_SC);
	device.option_add("8kscbb", S100_8K_SC_BB);
}

static DEVICE_INPUT_DEFAULTS_START(terminal)
	DEVICE_INPUT_DEFAULTS("RS232_RXBAUD", 0xff, RS232_BAUD_9600)
	DEVICE_INPUT_DEFAULTS("RS232_TXBAUD", 0xff, RS232_BAUD_9600)
	DEVICE_INPUT_DEFAULTS("RS232_DATABITS", 0xff, RS232_DATABITS_7)
	DEVICE_INPUT_DEFAULTS("RS232_PARITY", 0xff, RS232_PARITY_EVEN)
	DEVICE_INPUT_DEFAULTS("RS232_STOPBITS", 0xff, RS232_STOPBITS_2)
DEVICE_INPUT_DEFAULTS_END

void sb8085_state::sb8085(machine_config &config)
{
	I8085A(config, m_maincpu, 6.144_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &sb8085_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &sb8085_state::io_map);
	m_maincpu->in_sid_func().set(m_crt, FUNC(rs232_port_device::rxd_r));

	I8155(config, m_i8155, 6.144_MHz_XTAL / 2);
	m_i8155->out_pc_callback().set(FUNC(sb8085_state::phantom_disable_w)).bit(5);
	m_i8155->out_to_callback().set(m_usart, FUNC(i8251_device::write_txc));
	m_i8155->out_to_callback().append(m_usart, FUNC(i8251_device::write_rxc));

	I8251(config, m_usart, 6.144_MHz_XTAL / 2);
	m_usart->rts_handler().set(m_crt, FUNC(rs232_port_device::write_rts));
	m_usart->txd_handler().set(FUNC(sb8085_state::usart_txd_w));
	m_usart->dtr_handler().set(FUNC(sb8085_state::printer_select_w));

	RS232_PORT(config, m_crt, default_rs232_devices, nullptr);
	m_crt->rxd_handler().set(m_usart, FUNC(i8251_device::write_rxd));
	m_crt->cts_handler().set(FUNC(sb8085_state::crt_rts_w));
	m_crt->set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	RS232_PORT(config, m_printer, default_rs232_devices, nullptr);
	m_printer->cts_handler().set(FUNC(sb8085_state::printer_rts_w));

	S100_BUS(config, m_s100, 6.144_MHz_XTAL / 2);
	S100_SLOT(config, "s100:1", sb8085_s100_devices, "vti");
	S100_SLOT(config, "s100:2", sb8085_s100_devices, nullptr);
	S100_SLOT(config, "s100:3", sb8085_s100_devices, nullptr);
	S100_SLOT(config, "s100:4", sb8085_s100_devices, nullptr);
}


ROM_START(sb8085)
	ROM_REGION(0xc00, "program", 0)
	// From manual listing titled "SPACE BYTE 8085 PROM MONITOR 11-14-77" to be programmed on 3 TMS2708JL
	ROM_LOAD("1.u19", 0x000, 0x400, CRC(9e1f11dd) SHA1(4c4482baa133dd6d437aaf749e31b72c9718eb97))
	ROM_LOAD("2.u20", 0x400, 0x400, CRC(2426af98) SHA1(b6e37041f997aeea13be79df10dc410f4b0c51a6))
	ROM_LOAD("3.u21", 0x800, 0x400, CRC(088ad01b) SHA1(6832e63dc1769db09107bc09f4c2cfb158dd8d33))
ROM_END

} // anonymous namespace


COMP(1977, sb8085, 0, 0, sb8085, sb8085, sb8085_state, empty_init, "Space Byte", "Space Byte 8085", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)



sbackgc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Saitek Sensory Backgammon Computer / Electronic Champion Backgammon

NOTE: Before exiting MAME, change the power switch from GO to STOP. Otherwise,
NVRAM won't save properly. For sbackgc, only turn it off when it's the user's
turn to move.

These were supposedly programmed by Eric van Riet Paap (not sure about Sensory,
but pretty certain about Champion). The Champion program got 3rd place in the
first Computer Olympiad in 1989 (backgammon category). It also includes several
game variations like Jacquet or Trictrac.

MAME's sensorboard interface is a bit different compared to chess. Pieces can be
stacked up to 3. Pressing the Bear Off button (Del / Backspace) will optionally
remove the currently held piece. Capturing pieces is disabled, except when hitting
a blot, in which case the piece in hand is swapped with the one on the board.

Hardware notes:

Sensory Backgammon Computer (model 680):
- PCB label: GT2-PE-018
- Hitachi HD614085S @ ~5MHz (LC osc, no XTAL)
- LCD with custom segments
- 24 LEDs, 13*2 buttons sensor board, piezo

Electronic Champion Backgammon (model 681):
- PCB label: GT4-PE-009
- Hitachi HD6301Y0P @ ~4MHz (LC osc, no XTAL)
- rest is same as Sensory Backgammon Computer

There's also model 082, it looks nearly identical to model 680, but hardware is
unknown. Handheld Champion Backgammon (model 682) has a HD6301Y0F.

TODO:
- is Sensory Backgammon Computer model 082 on a different MCU? maybe NEC uCOM-75?
- if/when MAME supports an exit callback, hook up power-off switch to that

*******************************************************************************/

#include "emu.h"

#include "cpu/hmcs400/hmcs400.h"
#include "cpu/m6800/m6801.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "saitek_ecbackg.lh"
#include "saitek_sbackgc.lh"


namespace {

// shared base class

class sbackgc_base_state : public driver_device
{
public:
	sbackgc_base_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_led_pwm(*this, "led_pwm"),
		m_lcd_pwm(*this, "lcd_pwm"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_out_lcd(*this, "s%u.%u", 0U, 0U)
	{ }

	void shared(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(init_board);
	DECLARE_INPUT_CHANGED_MEMBER(bear_off);
	DECLARE_INPUT_CHANGED_MEMBER(power_off) { if (newval) m_power = false; }

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD { m_power = true; }

	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_led_pwm;
	required_device<pwm_display_device> m_lcd_pwm;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<6> m_inputs;
	output_finder<2, 24> m_out_lcd;

	bool m_power = false;
	u8 m_inp_mux = 0;
	u8 m_led_data = 0;
	u32 m_lcd_segs = 0;
	u8 m_lcd_com = 0;

	// sensorboard handlers
	void init_backgammon();
	void init_jacquet();
	void init_plakoto();
	void board_init_cb(u8 data);
	u8 board_spawn_cb(offs_t offset);
	u8 board_remove_cb();
	u8 board_sensor_cb(offs_t offset);
	u8 board_r(u8 row);

	// shared I/O handlers (just the LCD)
	void clear_display();
	void lcd_pwm_w(offs_t offset, u8 data);
	void update_lcd();
	template <int N> void lcd_segs_w(offs_t offset, u8 data, u8 mem_mask);
	void lcd_com_w(u8 data);
};

void sbackgc_base_state::machine_start()
{
	m_out_lcd.resolve();

	// register for savestates
	save_item(NAME(m_power));
	save_item(NAME(m_led_data));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_lcd_segs));
	save_item(NAME(m_lcd_com));
}


// Sensory Backgammon Computer

class sbackgc_state : public sbackgc_base_state
{
public:
	sbackgc_state(const machine_config &mconfig, device_type type, const char *tag) :
		sbackgc_base_state(mconfig, type, tag)
	{ }

	void sbackgc(machine_config &config);

private:
	// I/O handlers
	template <int N> void leds_w(u8 data);
	u8 buttons_r();
	u8 input1_r();
	u16 input2_r();
	void control_w(u16 data);
};


// Electronic Champion Backgammon

class ecbackg_state : public sbackgc_base_state
{
public:
	ecbackg_state(const machine_config &mconfig, device_type type, const char *tag) :
		sbackgc_base_state(mconfig, type, tag)
	{ }

	void ecbackg(machine_config &config);

private:
	// I/O handlers
	u8 p2_r();
	void p2_w(u8 data);
	u8 p5_r();
	void p6_w(u8 data);
};



/*******************************************************************************
    Sensorboard
*******************************************************************************/

void sbackgc_base_state::init_backgammon()
{
	// initial position for Backgammon (default)
	for (int i = 0; i < 5; i++)
	{
		m_board->write_piece(6, i, 1);
		m_board->write_piece(0, i, 4);
		m_board->write_piece(0, 9 - i, 1);
		m_board->write_piece(6, 9 - i, 4);

		if (i < 3)
		{
			m_board->write_piece(4, i, 1);
			m_board->write_piece(4, 9 - i, 4);
		}

		if (i < 2)
		{
			m_board->write_piece(11, 9 - i, 1);
			m_board->write_piece(11, i, 4);
		}
	}
}

void sbackgc_base_state::init_jacquet()
{
	// initial position for Jacquet / Trictrac / Moultezim
	for (int i = 0; i < 5; i++)
	{
		m_board->write_piece(11, 9 - i, 3);
		m_board->write_piece(0, i, 6);
	}
}

void sbackgc_base_state::init_plakoto()
{
	// initial position for Plakoto
	for (int i = 0; i < 5; i++)
	{
		m_board->write_piece(11, 9 - i, 3);
		m_board->write_piece(11, i, 6);
	}
}

INPUT_CHANGED_MEMBER(sbackgc_base_state::init_board)
{
	if (!newval)
		return;

	m_board->cancel_sensor();
	m_board->cancel_hand();
	m_board->clear_board();

	// 3 possible initial board positions
	switch (param)
	{
		case 0:
			init_backgammon();
			break;

		case 1:
			init_jacquet();
			break;

		case 2:
			init_plakoto();
			break;

		default:
			break;
	}

	m_board->refresh();
}

void sbackgc_base_state::board_init_cb(u8 data)
{
	if (~data & 1)
		init_backgammon();
}

u8 sbackgc_base_state::board_spawn_cb(offs_t offset)
{
	return (offset == 1) ? 1 : 4;
}

INPUT_CHANGED_MEMBER(sbackgc_base_state::bear_off)
{
	if (newval && m_inputs[5]->read() & 1)
	{
		// remove piece when Bear Off button is pressed
		board_remove_cb();
		m_board->refresh();
	}
}

u8 sbackgc_base_state::board_remove_cb()
{
	int handpos = m_board->get_handpos();

	if (handpos != -1)
	{
		u8 x = handpos & 0xf;
		u8 y = handpos >> 4 & 0xf;
		u8 piece = m_board->read_piece(x, y);

		m_board->remove_hand();

		// decrement piece
		if (piece != 1 && piece != 4)
			m_board->write_piece(x, y, piece - 1);
	}
	else
		m_board->cancel_hand();

	// block default sensorboard handling
	return 1;
}

u8 sbackgc_base_state::board_sensor_cb(offs_t offset)
{
	u8 x = offset & 0xf;
	u8 y = offset >> 4 & 0xf;
	u8 piece = m_board->read_piece(x, y);

	u8 hand = m_board->get_hand();

	// drop piece
	if (hand != 0)
	{
		// drop piece on same spot
		if (offset == m_board->get_handpos())
		{
			m_board->cancel_hand();
			return 1;
		}

		if (piece != 0)
		{
			u8 piece_color = (piece < 4) ? 1 : 4;
			u8 piece_index = piece - piece_color;

			// invalid drop (overflow, or onto a stack of the opposite color)
			if (piece_index == 2 || ((hand != piece_color) && piece_index == 1))
				return 1;

			board_remove_cb();

			// swap piece (blot)
			if ((hand != piece_color) && piece_index == 0)
			{
				m_board->write_piece(x, y, hand);
				m_board->set_hand(piece_color);
			}

			// increment piece
			else
				m_board->write_piece(x, y, piece + 1);
		}
		else
		{
			// drop piece on empty spot
			board_remove_cb();
			m_board->write_piece(x, y, hand);
		}
	}

	// pick up piece
	else if (piece != 0)
	{
		// white or black piece
		piece = (piece < 4) ? 1 : 4;

		m_board->set_hand(piece);
		m_board->set_handpos(offset);
	}

	// block default sensorboard handling
	return 3;
}

u8 sbackgc_base_state::board_r(u8 row)
{
	u8 data = 0;

	// inputs to sensorboard translation table (0xff is invalid)
	static const u8 lut_board[4*7] =
	{
		0x0c, 0x1a, 0x18, 0x16, 0x06, 0x08, 0x0a,
		0x1c, 0x15, 0x13, 0x11, 0x01, 0x03, 0x05,
		0xff, 0x14, 0x12, 0x10, 0x00, 0x02, 0x04,
		0xff, 0x1b, 0x19, 0x17, 0x07, 0x09, 0x0b
	};

	for (int i = 0; i < 7; i++)
	{
		const u8 pos = lut_board[row * 7 + i];
		const u8 x = pos & 0xf;
		const u8 y = pos >> 4;

		// 5 rows per button
		int state = 0;
		for (int j = 0; j < 5; j++)
			state |= m_board->read_sensor(x, y * 5 + j);

		data = data << 1 | state;
	}

	return data;
}



/*******************************************************************************
    I/O
*******************************************************************************/

// LCD

void sbackgc_base_state::clear_display()
{
	m_lcd_pwm->clear();
	m_led_pwm->clear();
}

void sbackgc_base_state::lcd_pwm_w(offs_t offset, u8 data)
{
	m_out_lcd[offset & 0x3f][offset >> 6] = data;
}

void sbackgc_base_state::update_lcd()
{
	for (int i = 0; i < 2; i++)
	{
		// LCD common is analog (voltage level)
		const u8 com = population_count_32(m_lcd_com >> (i * 2) & 3);
		const u32 data = (com == 0) ? m_lcd_segs : (com == 2) ? ~m_lcd_segs : 0;
		m_lcd_pwm->write_row(i, data);
	}
}

template <int N>
void sbackgc_base_state::lcd_segs_w(offs_t offset, u8 data, u8 mem_mask)
{
	if (mem_mask == 0)
		return;

	// R3x-R8x: LCD segments (P1x,P3x,P4x on ecbackg)
	const u8 width = 32 - count_leading_zeros_32(mem_mask);
	const u8 mask = (1 << width) - 1;
	const u8 shift = width * N;

	m_lcd_segs = (m_lcd_segs & ~(mask << shift)) | ((data & mask) << shift);
	update_lcd();
}

void sbackgc_base_state::lcd_com_w(u8 data)
{
	// R00-R03: LCD common (P70-P73 on ecbackg)
	m_lcd_com = data & 0xf;
	update_lcd();
}


// sbackgc-specific

template <int N>
void sbackgc_state::leds_w(u8 data)
{
	// R1x,R2x: LED data
	const u8 shift = 4 * N;
	m_led_data = (m_led_data & ~(0xf << shift)) | (data << shift);
	m_led_pwm->write_mx(m_led_data);
}

u8 sbackgc_state::buttons_r()
{
	u8 data = 0xf;

	for (int i = 0; i < 4; i++)
		if (m_inp_mux & m_inputs[i]->read())
			data ^= 1 << i;

	return data;
}

u8 sbackgc_state::input1_r()
{
	// R90-R93: multiplexed inputs
	u8 data = 0xf;

	// read board
	for (int i = 0; i < 4; i++)
		if (m_inp_mux & board_r(i))
			data ^= 1 << i;

	// read buttons (high)
	return data & (buttons_r() | 3);
}

u16 sbackgc_state::input2_r()
{
	// D11,D12: read buttons (low)
	u16 data = buttons_r() << 11 & 0x1800;

	// D13: power switch state
	data |= m_power ? 0x2000 : 0;

	// D15: freq sel (3MHz or 5MHz)
	return data | 0x800f;
}

void sbackgc_state::control_w(u16 data)
{
	// D0-D2: LED select
	m_led_pwm->write_my(~data & 7);

	// D4-D10: input mux
	m_inp_mux = ~data >> 4 & 0x7f;

	// D14: speaker out
	m_dac->write(BIT(data, 14));
}


// ecbackg-specific

u8 ecbackg_state::p2_r()
{
	// P27: power switch state
	u8 data = m_power ? 0 : 0x80;

	// P24: P57 (unused)
	data |= ~p5_r() >> 3 & 0x10;
	return ~data;
}

void ecbackg_state::p2_w(u8 data)
{
	// P20-P22: LED select
	m_led_pwm->write_my(data & 7);

	// P23: speaker out
	m_dac->write(BIT(data, 3));
}

u8 ecbackg_state::p5_r()
{
	// P50-P56: multiplexed inputs
	u8 data = 0;

	// read buttons
	for (int i = 0; i < 5; i++)
		if (BIT(bitswap<5>(m_inp_mux,6,0,3,5,4), i))
			data |= m_inputs[i]->read();

	// read board
	for (int i = 0; i < 4; i++)
		if (BIT(bitswap<4>(m_inp_mux,0,3,2,1), i))
			data |= board_r(i);

	return ~data;
}

void ecbackg_state::p6_w(u8 data)
{
	// P60-P67: input mux, LED data
	m_inp_mux = ~data;
	m_led_pwm->write_mx(~data);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( sbackgc )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_A) PORT_NAME("Double / Accept")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_J) PORT_NAME("Reject")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("Play")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Game Option")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("Verify")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Roll Dice")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Dice 1")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Dice 2")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Dice 3")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Dice 4")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Dice 5")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Dice 6")

	PORT_START("IN.2")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sbackgc_base_state::bear_off), 0) PORT_NAME("Bear Off")

	PORT_START("IN.3")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Set Up")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")

	PORT_START("IN.5")
	PORT_CONFNAME( 0x01, 0x01, "Remove Piece on Bear Off" )
	PORT_CONFSETTING(    0x00, DEF_STR( No ) )
	PORT_CONFSETTING(    0x01, DEF_STR( Yes ) )

	PORT_START("POWER") // needs to be triggered for nvram to work
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sbackgc_base_state::power_off), 0) PORT_NAME("Stop")

	PORT_START("BOARD")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sbackgc_base_state::init_board), 0) PORT_NAME("Board Reset Backgammon")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sbackgc_base_state::init_board), 1) PORT_NAME("Board Reset Jacquet")
INPUT_PORTS_END

static INPUT_PORTS_START( ecbackg )
	PORT_INCLUDE( sbackgc )

	PORT_MODIFY("IN.3")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Statistics")

	PORT_MODIFY("BOARD")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sbackgc_base_state::init_board), 2) PORT_NAME("Board Reset Plakoto")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void sbackgc_base_state::shared(machine_config &config)
{
	// basic machine hardware
	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(FUNC(sbackgc_base_state::board_init_cb));
	m_board->spawn_cb().set(FUNC(sbackgc_base_state::board_spawn_cb));
	m_board->remove_cb().set(FUNC(sbackgc_base_state::board_remove_cb));
	m_board->sensor_cb().set(FUNC(sbackgc_base_state::board_sensor_cb));
	m_board->set_size(13, 10);
	m_board->set_spawnpoints(2);
	m_board->set_max_id(6);
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_lcd_pwm).set_size(2, 24);
	m_lcd_pwm->output_x().set(FUNC(sbackgc_base_state::lcd_pwm_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/5, 518/5);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_led_pwm).set_size(3, 8);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void sbackgc_state::sbackgc(machine_config &config)
{
	// basic machine hardware
	hmcs400_cpu_device &maincpu(HD614085(config, m_maincpu, 5'000'000)); // approximation, no XTAL
	maincpu.nvram_enable_backup(true);
	maincpu.stop_cb().set(maincpu, FUNC(hmcs400_cpu_device::nvram_set_battery));
	maincpu.stop_cb().append([this](int state) { if (state) clear_display(); });
	maincpu.write_r<0x0>().set(FUNC(sbackgc_state::lcd_com_w));
	maincpu.write_r<0x1>().set(FUNC(sbackgc_state::leds_w<0>));
	maincpu.write_r<0x2>().set(FUNC(sbackgc_state::leds_w<1>));
	maincpu.write_r<0x3>().set(FUNC(sbackgc_state::lcd_segs_w<0>));
	maincpu.write_r<0x4>().set(FUNC(sbackgc_state::lcd_segs_w<1>));
	maincpu.write_r<0x5>().set(FUNC(sbackgc_state::lcd_segs_w<2>));
	maincpu.write_r<0x6>().set(FUNC(sbackgc_state::lcd_segs_w<3>));
	maincpu.write_r<0x7>().set(FUNC(sbackgc_state::lcd_segs_w<4>));
	maincpu.write_r<0x8>().set(FUNC(sbackgc_state::lcd_segs_w<5>));
	maincpu.read_r<0x9>().set(FUNC(sbackgc_state::input1_r));
	maincpu.read_r<0xa>().set_ioport("IN.4").exor(1);
	maincpu.write_d().set(FUNC(sbackgc_state::control_w));
	maincpu.read_d().set(FUNC(sbackgc_state::input2_r));

	shared(config);

	m_lcd_pwm->set_bri_levels(0.05);
	config.set_default_layout(layout_saitek_sbackgc);
}

void ecbackg_state::ecbackg(machine_config &config)
{
	// basic machine hardware
	hd6301y0_cpu_device &maincpu(HD6301Y0(config, m_maincpu, 4'000'000)); // approximation, no XTAL
	maincpu.nvram_enable_backup(true);
	maincpu.standby_cb().set(maincpu, FUNC(hd6301y0_cpu_device::nvram_set_battery));
	maincpu.standby_cb().append([this](int state) { if (state) clear_display(); });
	maincpu.out_p1_cb().set(FUNC(ecbackg_state::lcd_segs_w<0>));
	maincpu.in_p2_cb().set(FUNC(ecbackg_state::p2_r));
	maincpu.out_p2_cb().set(FUNC(ecbackg_state::p2_w));
	maincpu.out_p3_cb().set(FUNC(ecbackg_state::lcd_segs_w<1>));
	maincpu.out_p4_cb().set(FUNC(ecbackg_state::lcd_segs_w<2>));
	maincpu.in_p5_cb().set(FUNC(ecbackg_state::p5_r));
	maincpu.out_p6_cb().set(FUNC(ecbackg_state::p6_w));
	maincpu.out_p7_cb().set(FUNC(ecbackg_state::lcd_com_w));

	shared(config);

	config.set_default_layout(layout_saitek_ecbackg);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( sbackgc )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("1988_gx2_saitek_614085sc72.u1", 0x0000, 0x4000, CRC(0e91d7a1) SHA1(fa147105fa99d01210b53389b8b70031cff6ad66) )

	ROM_REGION( 109526, "screen", 0 )
	ROM_LOAD("sbackgc.svg", 0, 109526, CRC(183b12fb) SHA1(5a3682d6ff28086c2ab890eee1403c8c3d7d8527) )
ROM_END

ROM_START( ecbackg )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("saitek_89_gx4b_c12_31y0rm79p.u1", 0x0000, 0x4000, CRC(d7278545) SHA1(9ece31cdb237067aeec480c066e0917752697a4d) )

	ROM_REGION( 109526, "screen", 0 )
	ROM_LOAD("ecbackg.svg", 0, 109526, CRC(b8149d74) SHA1(0cc6f1a2c50f53f8d2be73379019d275799d0546) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1988, sbackgc, 0,      0,      sbackgc, sbackgc, sbackgc_state, empty_init, "Saitek", "Sensory Backgammon Computer", MACHINE_SUPPORTS_SAVE )

SYST( 1989, ecbackg, 0,      0,      ecbackg, ecbackg, ecbackg_state, empty_init, "Saitek", "Electronic Champion Backgammon", MACHINE_SUPPORTS_SAVE )



sbc6510.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

SBC6510 from Josip Perusanec

2009-12-18 Skeleton driver.
2012-08-12 Working [Robbbert]

CPU MOS 6510 (1MHz)
ROM 4KB
RAM 128KB
CIA 6526 - for interrupt gen and scan of keyboard
YM2149/AY-3-8910 - sound + HDD/CF IDE
GAL16V8 - address decoder
ATMEGA8 - gen. of PAL video signal (modified TellyMate)
keyboard of C64 computer used

Commands:
A - (unknown)
C - (unknown)
E - (unknown)
F - (unknown)
G - (unknown)
L - (unknown)
R - (unknown)
S - (unknown)

Some commands expect a filename enclosed in double quotes. If the quotes
are not there, it loops forever looking for them. Good example of bad
programming. There is no help and no error messages.

ToDo:

- The ATMEGA8 is a CPU with Timer, 4 IO ports, UART, ADC, Watchdog all
  built in. This enormously complex device needs to be emulated. It also
  contains some (4k?) RAM, of which certain addresses have special meaning.
  For example bytes 0 and 1 control the serial video stream of bits.

- When the system boots, there is a Y in the top corner. This is actually
  Esc-Y, which our terminal does not understand.

- IDE interface.

- Find out the proper way to use the monitor, there are no instructions
  and the slightest mistake can freeze the system.

- No software to test with.

- Natural keyboard & Paste do not work when shift is involved.


****************************************************************************/

#include "emu.h"
#include "cpu/avr8/avr8.h"
#include "cpu/m6502/m6510.h"
#include "machine/mos6526.h"
#include "machine/terminal.h"
#include "sound/ay8910.h"
#include "emupal.h"
#include "speaker.h"


namespace {

class sbc6510_state : public driver_device
{
public:
	sbc6510_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_videocpu(*this, "videocpu")
		, m_terminal(*this, "terminal")
		, m_keyboard(*this, "X%u", 0U)
	{ }

	void sbc6510(machine_config &config);

private:
	u8 a2_r();
	void a2_w(u8 data);
	u8 psg_a_r();
	u8 psg_b_r();
	void key_w(u8 data);
	u8 key_r();

	void main_mem_map(address_map &map) ATTR_COLD;
	void video_data_map(address_map &map) ATTR_COLD;
	void video_mem_map(address_map &map) ATTR_COLD;

	u8 m_key_row = 0U;
	u8 m_2 = 0U;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_device<atmega88_device> m_videocpu;
	required_device<generic_terminal_device> m_terminal;
	required_ioport_array<9> m_keyboard;
};


void sbc6510_state::main_mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0001).ram();
	map(0x0002, 0x0002).rw(FUNC(sbc6510_state::a2_r), FUNC(sbc6510_state::a2_w));
	map(0x0003, 0xdfff).ram();
	map(0xe000, 0xe00f).mirror(0x1f0).rw("cia6526", FUNC(mos6526_device::read), FUNC(mos6526_device::write));
	map(0xe800, 0xe800).mirror(0x1ff).w("ay8910", FUNC(ay8910_device::address_w));
	map(0xea00, 0xea00).mirror(0x1ff).rw("ay8910", FUNC(ay8910_device::data_r), FUNC(ay8910_device::data_w));
	map(0xf000, 0xffff).rom().region("maincpu",0);
}

void sbc6510_state::video_mem_map(address_map &map)
{
	map(0x0000, 0x1fff).rom();
}

void sbc6510_state::video_data_map(address_map &map)
{
	map(0x0100, 0x04ff).ram();
}

/* Input ports */
static INPUT_PORTS_START( sbc6510 ) // cbm keyboard
	PORT_START( "X0" )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Crsr Down Up")    PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3)        PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2)        PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1)        PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F4)        PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Crsr Right Left") PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Return")          PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Del  Inst")       PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START( "X1" )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Shift (Left)")    PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)         PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)         PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)         PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)         PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)         PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)         PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)         PORT_CHAR('3') PORT_CHAR('#')

	PORT_START( "X2" )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)         PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)         PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)         PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)         PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6)         PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)         PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)         PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5)         PORT_CHAR('5') PORT_CHAR('%')

	PORT_START( "X3" )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)         PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)         PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)         PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)         PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8)         PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)         PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)         PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7)         PORT_CHAR('7')

	PORT_START( "X4" )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)         PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)         PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)         PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)         PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0)         PORT_CHAR('0')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)         PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)         PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9)         PORT_CHAR('9') PORT_CHAR(')')

	PORT_START( "X5" )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA)     PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)     PORT_CHAR(':') PORT_CHAR('[')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)      PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS)    PORT_CHAR('-')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)         PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)         PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)     PORT_CHAR('+')

	PORT_START( "X6" )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH)     PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("\xE2\x86\x91  Pi") PORT_CODE(KEYCODE_DEL) PORT_CHAR(0x2191) PORT_CHAR(0x03C0)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('=')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Shift (Right)")   PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Home  Clr")       PORT_CODE(KEYCODE_INSERT)     PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE)     PORT_CHAR(';') PORT_CHAR(']')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE)    PORT_CHAR('*')
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH2)    PORT_CHAR(0xA3)

	PORT_START( "X7" )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Stop Run")        PORT_CODE(KEYCODE_HOME)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)         PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CBM")             PORT_CODE(KEYCODE_LALT)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE)     PORT_CHAR(' ')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2)         PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TAB)       PORT_CHAR(9)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("\xE2\x86\x90")    PORT_CODE(KEYCODE_TILDE)   PORT_CHAR(0x2190) // newline
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1)         PORT_CHAR('1') PORT_CHAR('!')

	PORT_START( "X8" )  /* unused */
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Restore")         PORT_CODE(KEYCODE_PRTSCR)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Shift Lock (switch)") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


u8 sbc6510_state::a2_r()
{
	return m_2;
}

void sbc6510_state::a2_w(u8 data)
{
	m_2 = data;
	m_terminal->write(data);
}

void sbc6510_state::machine_start()
{
	save_item(NAME(m_key_row));
	save_item(NAME(m_2));
}


void sbc6510_state::machine_reset()
{
}

u8 sbc6510_state::psg_a_r()
{
	return 0xff;
}

u8 sbc6510_state::psg_b_r()
{
	return 0x7f;
}

u8 sbc6510_state::key_r()
{
	u8 i, data=0;

	for (i = 0; i < 8; i++)
		if (!BIT(m_key_row, i))
			data |= m_keyboard[i]->read();

	return ~data;
}

void sbc6510_state::key_w(u8 data)
{
	m_key_row = data;
}

static const gfx_layout charset_8x16 =
{
	8, 9,
	256,
	1,
	{ 0 },
	{ STEP8(0,1) },
	{ 0*1024*2, 1*1024*2, 2*1024*2, 3*1024*2, 4*1024*2, 5*1024*2, 6*1024*2, 7*1024*2, 8*1024*2 },
	8
};


static GFXDECODE_START( gfx_sbc6510 )
	GFXDECODE_ENTRY( "videocpu", 0x1500, charset_8x16, 0, 128 )
GFXDECODE_END


void sbc6510_state::sbc6510(machine_config &config)
{
	/* basic machine hardware */
	M6510(config, m_maincpu, XTAL(1'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &sbc6510_state::main_mem_map);

	ATMEGA88(config, m_videocpu, XTAL(16'000'000)); // Video CPU trips SLEEP opcode, needs to be emulated
	m_videocpu->set_addrmap(AS_PROGRAM, &sbc6510_state::video_mem_map);
	m_videocpu->set_addrmap(AS_DATA, &sbc6510_state::video_data_map);
	m_videocpu->set_eeprom_tag("eeprom");

	PALETTE(config, "palette", palette_device::MONOCHROME); // for F4 displayer only
	GFXDECODE(config, "gfxdecode", "palette", gfx_sbc6510);

	/* video hardware */
	GENERIC_TERMINAL(config, m_terminal, 0);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	ay8910_device &ay8910(AY8910(config, "ay8910", XTAL(1'000'000)));
	// Ports A and B connect to the IDE socket
	ay8910.port_a_read_callback().set(FUNC(sbc6510_state::psg_a_r));
	ay8910.port_b_read_callback().set(FUNC(sbc6510_state::psg_b_r));
	ay8910.add_route(ALL_OUTPUTS, "mono", 1.00);

	mos6526_device &cia(MOS6526(config, "cia6526", XTAL(1'000'000)));
	cia.set_tod_clock(50);
	cia.irq_wr_callback().set_inputline("maincpu", M6510_IRQ_LINE);
	cia.pa_wr_callback().set(FUNC(sbc6510_state::key_w));
	cia.pb_rd_callback().set(FUNC(sbc6510_state::key_r));
}

/* ROM definition */
ROM_START( sbc6510 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "sbc6510.rom", 0x0000, 0x1000, CRC(e13a5e62) SHA1(1e7482e9b98b39d0cc456254fbe8fd0981e9377e))

	ROM_REGION( 0x2000, "videocpu", 0 ) // ATMEGA8 at 16MHz
	ROM_LOAD( "video.bin",   0x0000, 0x2000, CRC(809f31ce) SHA1(4639de5f7b8f6c036d74f217ba85e7e897039094))

	ROM_REGION( 0x0200, "gal", ROMREGION_ERASEFF )
	ROM_LOAD( "sbc6510.gal", 0x0000, 0x0117, CRC(f78f9927) SHA1(b951163958f5722032826d0d17a07c81dbd5f68e))

	ROM_REGION( 0x2000, "eeprom", ROMREGION_ERASEFF )
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY            FULLNAME   FLAGS */
COMP( 2009, sbc6510, 0,      0,      sbc6510, sbc6510, sbc6510_state, empty_init, "Josip Perusanec", "SBC6510", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



sbrain.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/************************************************************************************************************

Intertec SuperBrain

2013-08-19 Skeleton

Intertec Compustar terminal appears to have identical hardware. Need roms for it.

Chips: 2x Z80; FD1791; 2x 8251; 8255; BR1941; CRT8002-003; KR3600-056; DP8350; MM5035
Xtals: 16.0, 10.92, 5.0688
RAM: 32K or 64K dynamic (TMS4116-20NL); 1K static disk buffer (MM2114)
Disk parameters: 512 bytes x 10 sectors x 35 tracks. 1 and 2-sided disks supported.
Sound: Beeper
Expansion bus: 40 pins, nearly identical to TRS-80

The boot prom is shared between both cpus. This feat is accomplished by holding the sub cpu
 in reset, until the main cpu has prepared a few memory locations. The first thing in the rom
 is to check these locations, and then program flow splits into 2 sections, one for each cpu.
The main cpu does a busreq to gain access to the sub cpu's 1k static ram. When the sub cpu
 responds with busack, then the main cpu switches bank2. In emulation, it isn't actually
 necessary to stop the sub cpu because of other handshaking. Our Z80 emulation doesn't
 support the busack signal anyway, so we just assume it is granted immediately.
When booted, the time (corrupted) is displayed at top right. You need to run TIME hh:mm:ss
 to set the time (TIME.COM must be on the disk).

The schematic in parts is difficult to read. Some assumptions have been made.

To Do:
- Improve keyboard.
- Row buffering DMA (DP8350, MM5035) and line-by-line rendering.
- Proper character generator emulation (CRT8002).
- Add expansion bus slot.
- Probably lots of other stuff.

*************************************************************************************************************/


#include "emu.h"
#include "cpu/z80/z80.h"
#include "bus/rs232/rs232.h"
#include "imagedev/floppy.h"
#include "machine/com8116.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/wd_fdc.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "sound/beep.h"
#include "video/dp8350.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include <algorithm>


namespace {

class sbrain_state : public driver_device
{
public:
	sbrain_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_subcpu(*this, "subcpu")
		, m_p_chargen(*this, "chargen")
		, m_beep(*this, "beeper")
		, m_crtc(*this, "crtc")
		, m_usart(*this, "usart%u", 0U)
		, m_mainport(*this, "mainport")
		, m_ppi(*this, "ppi")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0U)
		, m_ram(*this, RAM_TAG)
		, m_boot_prom(*this, "subcpu")
		, m_disk_buffer(*this, "buffer")
		, m_keyboard(*this, "X%u", 0)
		, m_modifiers(*this, "MODIFIERS")
		, m_serial_sw(*this, "BAUDCLOCK")
	{
	}

	void sbrain(machine_config &config);
	void sagafox(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u8 mem_r(offs_t offset);
	void mem_w(offs_t offset, u8 data);

	u8 ppi_pa_r();
	void ppi_pa_w(u8 data);
	u8 ppi_pb_r();
	void ppi_pb_w(u8 data);
	u8 ppi_pc_r();
	void ppi_pc_w(u8 data);
	u8 char_int_ack_r();
	void char_int_ack_w(u8 data);
	u8 keyboard_r();

	void disk_select_w(u8 data);

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	TIMER_DEVICE_CALLBACK_MEMBER(kbd_scan);

	void crtc_lrc_w(int state);
	void crtc_vblank_w(int state);
	void crtc_vsync_w(int state);

	void external_txc_w(int state);
	void external_rxc_w(int state);
	void internal_txc_rxc_w(int state);

	void main_io_map(address_map &map) ATTR_COLD;
	void main_mem_map(address_map &map) ATTR_COLD;
	void sub_io_map(address_map &map) ATTR_COLD;
	void sub_mem_map(address_map &map) ATTR_COLD;

	u8 m_keydown = 0U;
	u8 m_porta = 0U;
	u8 m_portb = 0U;
	u8 m_portc = 0U;
	u8 m_port10 = 0U;
	u8 m_key_data = 0U;
	u8 m_framecnt = 0U;

	required_device<cpu_device> m_maincpu;
	required_device<z80_device> m_subcpu;
	required_region_ptr<u8> m_p_chargen;
	required_device<beep_device> m_beep;
	required_device<dp8350_device> m_crtc;
	required_device_array<i8251_device, 2> m_usart;
	required_device<rs232_port_device> m_mainport;
	required_device<i8255_device> m_ppi;
	required_device<fd1791_device> m_fdc;
	required_device_array<floppy_connector, 4> m_floppy;
	required_device<ram_device> m_ram;
	required_region_ptr<u8> m_boot_prom;
	required_shared_ptr<u8> m_disk_buffer;

	required_ioport_array<10> m_keyboard;
	required_ioport m_modifiers;
	required_ioport m_serial_sw;
};

void sbrain_state::main_mem_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(sbrain_state::mem_r), FUNC(sbrain_state::mem_w));
}

void sbrain_state::main_io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x40, 0x41).mirror(6).rw(m_usart[0], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x48, 0x48).mirror(7).rw(FUNC(sbrain_state::char_int_ack_r), FUNC(sbrain_state::char_int_ack_w)); //chr_int_latch
	map(0x50, 0x50).mirror(7).r(FUNC(sbrain_state::keyboard_r));
	map(0x58, 0x59).mirror(6).rw(m_usart[1], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x60, 0x60).mirror(7).w("brg", FUNC(com8116_device::stt_str_w));
	map(0x68, 0x6b).mirror(4).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
}

void sbrain_state::sub_mem_map(address_map &map)
{
	map(0x0000, 0x07ff).mirror(0xf000).rom().region("subcpu", 0);
	map(0x8800, 0x8bff).mirror(0x7400).ram().share("buffer");
}

void sbrain_state::sub_io_map(address_map &map)
{
	map.global_mask(0x1f);
	map(0x08, 0x0b).mirror(4).rw(m_fdc, FUNC(fd1791_device::read), FUNC(fd1791_device::write));
	map(0x10, 0x10).mirror(7).w(FUNC(sbrain_state::disk_select_w));
}


u8 sbrain_state::mem_r(offs_t offset)
{
	switch (offset & 0xc000)
	{
	case 0x0000:
		// PPIC-2 set selects boot PROM
		if (BIT(m_portc, 2))
			return m_boot_prom[offset & 0x7ff];

		// lowest 16K of RAM is disabled when PPIC-0 is set
		if (BIT(m_portc, 0))
			return 0xff;
		break;

	case 0x8000:
		// resetting PPIC-4 selects disk buffer RAM
		if (!BIT(m_portc, 4))
			return m_disk_buffer[offset & 0x3ff];

		break;
	}

	return m_ram->read(offset);
}

void sbrain_state::mem_w(offs_t offset, u8 data)
{
	// any memory write affects CRTC when PPIC-0 is set
	if (BIT(m_portc, 0))
		m_crtc->register_load(bitswap<2>(data, 0, 1), offset & 0xfff);

	switch (offset & 0xc000)
	{
	case 0x0000:
		// can't write to boot PROM
		if (BIT(m_portc, 2))
			return;

		// RAS is disabled for bank 0 when PPIC-0 is set
		if (BIT(m_portc, 0))
			return;

		break;

	case 0x8000:
		// resetting PPIC-4 selects disk buffer RAM
		if (!BIT(m_portc, 4))
		{
			m_disk_buffer[offset & 0x3ff] = data;
			break;
		}

		break;
	}

	m_ram->write(offset, data);
}

u8 sbrain_state::char_int_ack_r()
{
	if (!machine().side_effects_disabled())
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
	return 0xff;
}

void sbrain_state::char_int_ack_w(u8 data)
{
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
}

u8 sbrain_state::keyboard_r()
{
	return m_key_data;
}

/* Misc disk functions
d0 : busy signal to master CPU
d1 : SEL A (drive 0?)
d2 : SEL B (drive 1?)
d3 : SEL C
d4 : SEL D
d5 : side select
d6,7 : not used
*/
void sbrain_state::disk_select_w(u8 data)
{
	m_port10 = data | 0xc0;

	floppy_image_device *floppy = nullptr;
	for (int d = 0; d < 4; d++)
		if (BIT(m_port10, d + 1))
		{
			floppy = m_floppy[d]->get_device();
			if (floppy)
				break;
		}

	m_fdc->set_floppy(floppy);

	if (floppy)
		floppy->ss_w(BIT(m_port10, 5));
}

u8 sbrain_state::ppi_pa_r()
{
	return m_porta;
}

/* Video functions:
d0,1 : 11 = alphanumeric; 10 = external ;other = graphics
d2 : Underline
d3,4 : not used
d5 : strike through
d6 : 1=60hz 0=50hz
d7 : reverse video
*/
void sbrain_state::ppi_pa_w(u8 data)
{
	m_crtc->refresh_control(BIT(data, 6));

	m_porta = data;
}

/* Inputs
d0 : data ready from keyboard
d1 : key held down
d2 : Vert Blank
d3 : not used
d4 : /capslock
d5 : disk status: 1 = busy, 0 = ready
d6 : Ring Indicator line from main rs232 port, 1=normal, 0=set
d7 : cpu2 /busak line
*/
u8 sbrain_state::ppi_pb_r()
{
	u8 vertsync = m_crtc->vblank_r() << 2;
	u8 capslock = BIT(m_modifiers->read(), 0) << 4; // bit 4, capslock
	u8 p10d0 = BIT(m_port10, 0) << 5; // bit 5
	u8 ri = m_mainport->ri_r() << 6;
	u8 busak = m_subcpu->busack_r() ? 0 : 128; // bit 7 (negative true)
	return busak | ri | p10d0 | capslock | vertsync | m_keydown;
}

void sbrain_state::ppi_pb_w(u8 data)
{
	m_portb = data & 8;
}

u8 sbrain_state::ppi_pc_r()
{
	return m_portc;
}

/* System
d0 : 1 = bank 0 disabled
d1 : character blanking
d2 : 1=enable rom, 0=enable ram bank 0
d3 : cpu2 reset line
d4 : 1=enable ram bank 2, 0=bank 2 uses disk buffer
d5 : cpu2 /busreq line
d6 : beeper
d7 : keyboard, 1=enable comms, 0=reset
*/
void sbrain_state::ppi_pc_w(u8 data)
{
	m_portc = data;
	m_beep->set_state(BIT(data, 6));
	if (!BIT(data, 7))
		m_keydown &= 2; // ack DR

	m_subcpu->set_input_line(INPUT_LINE_RESET, BIT(data, 3) ? ASSERT_LINE : CLEAR_LINE);
	m_fdc->mr_w(!BIT(data, 3));
	if (BIT(data, 3))
		disk_select_w(0);
	m_subcpu->set_input_line(Z80_INPUT_LINE_BUSRQ, BIT(data, 5) ? CLEAR_LINE : ASSERT_LINE);
}

void sbrain_state::external_txc_w(int state)
{
	if (!BIT(m_serial_sw->read(), 0))
		m_usart[1]->write_txc(state);
}

void sbrain_state::external_rxc_w(int state)
{
	if (!BIT(m_serial_sw->read(), 2))
		m_usart[1]->write_rxc(state);
}

void sbrain_state::internal_txc_rxc_w(int state)
{
	if (!BIT(m_serial_sw->read(), 1))
		m_usart[1]->write_txc(state);
	if (!BIT(m_serial_sw->read(), 3))
		m_usart[1]->write_rxc(state);
	if (!BIT(m_serial_sw->read(), 4))
		m_mainport->write_etc(state);
}

u8 translate_table[3][10][8] = {
	// unshifted
	{
		{ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37 },
		{ 0x38, 0x39, 0x2e, 0x2c, 0x2d, 0x00, 0x00, 0x00 },
		{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
		{ 0x09, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67 },
		{ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f },
		{ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77 },
		{ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x7b, 0x03, 0x7f },
		{ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37 },
		{ 0x38, 0x39, 0x2d, 0x3d, 0x5c, 0x08, 0x0a, 0x0d },
		{ 0x1b, 0x00, 0x20, 0x3b, 0x27, 0x2c, 0x2e, 0x2f }
	},
	// shift
	{
		{ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37 },
		{ 0x38, 0x39, 0x2e, 0x2c, 0x2d, 0x00, 0x00, 0x00 },
		{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
		{ 0x09, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47 },
		{ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f },
		{ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57 },
		{ 0x58, 0x59, 0x5a, 0x5d, 0x7c, 0x7d, 0x03, 0x7f },
		{ 0x29, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5e, 0x26 },
		{ 0x2a, 0x28, 0x5f, 0x2b, 0x7e, 0x08, 0x0a, 0x0d },
		{ 0x1b, 0x00, 0x20, 0x3a, 0x22, 0x3c, 0x3e, 0x3f }
	},
	// ctrl
	{
		{ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37 },
		{ 0x38, 0x39, 0x2e, 0x2c, 0x2d, 0x00, 0x00, 0x00 },
		{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
		{ 0x09, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 },
		{ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
		{ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 },
		{ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x03, 0x1f },
		{ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37 },
		{ 0x38, 0x39, 0x2d, 0x3d, 0x1e, 0x08, 0x0a, 0x0d },
		{ 0x1b, 0x00, 0x20, 0x3b, 0x27, 0x2c, 0x2e, 0x2f }
	}
};

static INPUT_PORTS_START( sbrain )
	PORT_START("X0")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad 0") PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad 1") PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad 2") PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad 3") PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad 4") PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad 5") PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad 6") PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad 7") PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))

	PORT_START("X1")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad 8") PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad 9") PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad .") PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad ,") PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD))
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Keypad -") PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Insert") PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START("X2")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))

	PORT_START("X3")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')

	PORT_START("X4")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("X5")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')

	PORT_START("X6")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("[ ]") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR(']')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\\ |") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("{ }") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('{') PORT_CHAR('}')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_END) PORT_CHAR(3)
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Delete") PORT_CODE(KEYCODE_DEL) PORT_CHAR(127)

	PORT_START("X7")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')

	PORT_START("X8")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("- _") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("= +") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\\ ~") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('\\') PORT_CHAR('~')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Line Feed") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(10)
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)

	PORT_START("X9")
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Here Is") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(32)
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("; :") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("' \"") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('\"')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("MODIFIERS")
	PORT_BIT(0x01,IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CapsLock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)

	PORT_START("BAUDCLOCK")
	PORT_DIPNAME(0x03, 0x01, "Main TX Clock") PORT_DIPLOCATION("SW:1,3")
	PORT_DIPSETTING(0x01, "Internal")
	PORT_DIPSETTING(0x02, "External")
	PORT_DIPNAME(0x0c, 0x04, "Main RX Clock") PORT_DIPLOCATION("SW:2,4")
	PORT_DIPSETTING(0x04, "Internal")
	PORT_DIPSETTING(0x08, "External")
	PORT_DIPNAME(0x10, 0x10, "Internal Baud Clock to Main Port") PORT_DIPLOCATION("SW:5")
	PORT_DIPSETTING(0x10, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))
INPUT_PORTS_END

TIMER_DEVICE_CALLBACK_MEMBER(sbrain_state::kbd_scan)
{
	// m_keydown: d0 = 1 after key pressed, and is reset by pc7; d1 = 1 while a key is down.
	m_keydown &= 1;
	u8 i, j, keyin, mods = m_modifiers->read() & 6;
	u8 translate_set = 0;
	if (BIT(mods, 1))
		translate_set = 1;
	else
	if (BIT(mods, 2))
		translate_set = 2;

	for (i = 0; i < 10; i++)
	{
		keyin = m_keyboard[i]->read();
		if (keyin)
		{
			for (j = 0; j < 8; j++)
			{
				if (BIT(keyin, j))
				{
					u8 pressed = translate_table[translate_set][i][j];
					m_keydown = (m_key_data == pressed) ? (m_keydown | 2) : 3;
					m_key_data = pressed;
					return;
				}
			}
		}
	}
	m_key_data = 0xff;
}

void sbrain_state::crtc_lrc_w(int state)
{
	// TODO: actually triggered by BUSACK and taken after DMA burst finishes
	if (state && !m_crtc->lbre_r() && !m_crtc->vblank_r())
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
}

void sbrain_state::crtc_vblank_w(int state)
{
	if (state)
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
}

void sbrain_state::crtc_vsync_w(int state)
{
	// TODO: internal to the CRT8002
	if (!state)
		m_framecnt++;
}

void sbrain_state::machine_start()
{
	std::fill_n(m_ram->pointer(), m_ram->size(), 0x00);

	m_usart[0]->write_cts(0);

	save_item(NAME(m_keydown));
	save_item(NAME(m_porta));
	save_item(NAME(m_portb));
	save_item(NAME(m_portc));
	save_item(NAME(m_port10));
	save_item(NAME(m_key_data));
	save_item(NAME(m_framecnt));
}

static void sbrain_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

void sbrain_state::machine_reset()
{
	m_keydown = 0;

	for (auto &floppy : m_floppy)
	{
		floppy_image_device *device = floppy->get_device();
		if (device != nullptr)
			device->mon_w(0); // motors run all the time
	}

	// PPI resets to input mode, which causes PPIC-3 to be pulled up to +5V, resetting disk CPU
}

u32 sbrain_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	// PPIC-3 blanks entire display
	if (BIT(m_portc, 3))
	{
		bitmap.fill(rgb_t::black(), cliprect);
		return 0;
	}

	uint16_t sy=0;

	// Where attributes come from:
	// - Most systems use ram for character-based attributes, but this one uses strictly hardware which would seem cumbersome
	// - d0,1 graphics from porta d0,d1
	// - d2 strike-through from porta d5
	// - d3 underline from porta d2
	// - d4 reverse-video from porta d7
	// - d5 blank from PC1 (scan-line based)
	// - d6 flash from bit 7 of each character

	uint16_t ma = m_crtc->top_of_page();
	uint16_t cr = m_crtc->cursor_address();
	uint8_t *videoram = &m_ram->pointer()[m_ram->size() - 0x800];
	for (uint8_t y = 0; y < 24; y++)
	{
		for (uint8_t ra = 0; ra < 10; ra++)
		{
			uint32_t *p = &bitmap.pix(sy++);

			for (uint16_t x = 0; x < 80; x++)
			{
				uint8_t gfx = 0;
				if (ra > 0)
				{
					uint8_t chr = videoram[(x + ma) & 0x7ff];

					if (!BIT(chr, 7) || BIT(m_framecnt, 5))
					{
						chr &= 0x7f;

						gfx = m_p_chargen[(chr<<4) | ra];
					}
				}

				if (((x + ma) & 0xfff) == cr)
					gfx ^= 0xfe;

				/* Display a scanline of a character */
				*p++ = BIT(gfx, 7) ? rgb_t::white() : rgb_t::black();
				*p++ = BIT(gfx, 6) ? rgb_t::white() : rgb_t::black();
				*p++ = BIT(gfx, 5) ? rgb_t::white() : rgb_t::black();
				*p++ = BIT(gfx, 4) ? rgb_t::white() : rgb_t::black();
				*p++ = BIT(gfx, 3) ? rgb_t::white() : rgb_t::black();
				*p++ = BIT(gfx, 2) ? rgb_t::white() : rgb_t::black();
				*p++ = BIT(gfx, 1) ? rgb_t::white() : rgb_t::black();
			}
		}
		ma = (ma + 80) & 0xfff;
	}
	return 0;
}

void sbrain_state::sbrain(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &sbrain_state::main_mem_map);
	m_maincpu->set_addrmap(AS_IO, &sbrain_state::main_io_map);

	Z80(config, m_subcpu, 16_MHz_XTAL / 4);
	m_subcpu->set_addrmap(AS_PROGRAM, &sbrain_state::sub_mem_map);
	m_subcpu->set_addrmap(AS_IO, &sbrain_state::sub_io_map);

	RAM(config, m_ram).set_default_size("64K").set_extra_options("32K");

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::amber());
	screen.set_screen_update(FUNC(sbrain_state::screen_update));

	DP8350(config, m_crtc, 10.92_MHz_XTAL).set_screen("screen"); // XTAL not directly connected
	m_crtc->character_generator_program(1);
	m_crtc->lrc_callback().set(FUNC(sbrain_state::crtc_lrc_w));
	m_crtc->vblank_callback().set(FUNC(sbrain_state::crtc_vblank_w));
	m_crtc->vsync_callback().set(FUNC(sbrain_state::crtc_vsync_w));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 800).add_route(ALL_OUTPUTS, "mono", 1.00);

	/* Devices */
	I8255(config, m_ppi);
	m_ppi->in_pa_callback().set(FUNC(sbrain_state::ppi_pa_r));
	m_ppi->out_pa_callback().set(FUNC(sbrain_state::ppi_pa_w));
	m_ppi->in_pb_callback().set(FUNC(sbrain_state::ppi_pb_r));
	m_ppi->out_pb_callback().set(FUNC(sbrain_state::ppi_pb_w));
	m_ppi->in_pc_callback().set(FUNC(sbrain_state::ppi_pc_r));
	m_ppi->out_pc_callback().set(FUNC(sbrain_state::ppi_pc_w));
	m_ppi->tri_pc_callback().set_constant(0x7f);

	I8251(config, m_usart[0], 16_MHz_XTAL / 8);
	m_usart[0]->txd_handler().set("auxport", FUNC(rs232_port_device::write_txd));

	I8251(config, m_usart[1], 16_MHz_XTAL / 8);
	m_usart[1]->txd_handler().set(m_mainport, FUNC(rs232_port_device::write_txd));
	m_usart[1]->rts_handler().set(m_mainport, FUNC(rs232_port_device::write_rts));
	m_usart[1]->dtr_handler().set(m_mainport, FUNC(rs232_port_device::write_dtr));

	RS232_PORT(config, m_mainport, default_rs232_devices, nullptr);
	m_mainport->rxd_handler().set(m_usart[1], FUNC(i8251_device::write_rxd));
	m_mainport->cts_handler().set(m_usart[1], FUNC(i8251_device::write_cts));
	m_mainport->dsr_handler().set(m_usart[1], FUNC(i8251_device::write_dsr));
	m_mainport->txc_handler().set(FUNC(sbrain_state::external_txc_w));
	m_mainport->rxc_handler().set(FUNC(sbrain_state::external_rxc_w));

	rs232_port_device &auxport(RS232_PORT(config, "auxport", default_rs232_devices, nullptr));
	auxport.rxd_handler().set(m_usart[0], FUNC(i8251_device::write_rxd));
	auxport.dsr_handler().set(m_usart[0], FUNC(i8251_device::write_dsr));

	com8116_device &brg(COM8116(config, "brg", 5.0688_MHz_XTAL)); // BR1941L
	brg.fr_handler().set(m_usart[0], FUNC(i8251_device::write_txc));
	brg.fr_handler().append(m_usart[0], FUNC(i8251_device::write_rxc));
	brg.ft_handler().set(FUNC(sbrain_state::internal_txc_rxc_w));

	FD1791(config, m_fdc, 16_MHz_XTAL / 16);
	m_fdc->set_force_ready(true);

	FLOPPY_CONNECTOR(config, "fdc:0", sbrain_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", sbrain_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:2", sbrain_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:3", sbrain_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	TIMER(config, "timer_a", 0).configure_periodic(FUNC(sbrain_state::kbd_scan), attotime::from_hz(15));

	SOFTWARE_LIST(config, "flop_list").set_original("sbrain");
}

void sbrain_state::sagafox(machine_config& config)
{
	sbrain(config);

	subdevice<software_list_device>("flop_list")->set_original("sagafox");
}

ROM_START( sbrain )
	ROM_REGION( 0x0800, "subcpu", ROMREGION_ERASEFF ) // only the second CPU has its own ROM
	ROM_SYSTEM_BIOS( 0, "4_2", "4.2")
	ROMX_LOAD("sbii_sb4_2.z69", 0x0000, 0x0800, CRC(89313e26) SHA1(755d494934099a4488abc44a8566c18d7d4fdea3), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "4_003", "4.003" )
	ROMX_LOAD("4_003_vc8001.z69", 0x0000, 0x0800, CRC(3ce3cd53) SHA1(fb6ade6bd67de3d9f911a1a48481ca619bda65ae), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "3_1", "3.1" )
	ROMX_LOAD("3_1.z69", 0x0000, 0x0800, CRC(b6a2e6a5) SHA1(a646faaecb9ac45ee1a42764628e8971524d5c13), ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "3_05", "3.05" )
	ROMX_LOAD("qd_3_05.z69", 0x0000, 0x0800, CRC(aedbe777) SHA1(9ee9ca3f05e11ceb80896f06c3a3ae352db214dc), ROM_BIOS(3))
	ROM_SYSTEM_BIOS( 4, "4_2_50", "4.2 (50Hz hack)")
	ROMX_LOAD("sbii_sb4_2_50hz.z69", 0x0000, 0x0800, CRC(285a894b) SHA1(694fef446fe19c0962f79951aa4d464489a9d161), ROM_BIOS(4))
	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD("crt8002-003.bin", 0x0000, 0x0800, BAD_DUMP CRC(5181d324) SHA1(7aa2d084947bcc0e3d31568f4de84c23b84abfff)) // hand-crafted
ROM_END

ROM_START( sagafox )
	ROM_REGION( 0x0800, "subcpu", 0 )
	ROM_LOAD("1b02.bin", 0x0000, 0x0800, CRC(1cd6e4d2) SHA1(585d2a52840804a3771077bcc77e249b28ea1602))

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD("1cg0.bin", 0x0000, 0x0800, CRC(508c56ef) SHA1(9ba2cceeb3b9f72503693372ae76f54d5919f024))

	ROM_REGION( 0x0400, "keyboard", 0 ) // use with KR3600-PRO
	ROM_LOAD("1kb1.bin", 0x0000, 0x0400, CRC(92b00014) SHA1(0669ecfc24bf010735a9c14791409e80390a01f7))
ROM_END

ROM_START( sagafoxf80 )
	ROM_REGION( 0x0800, "subcpu", 0 )
	ROM_LOAD("f80_6ms.bin", 0x0000, 0x0800, CRC(81ed0c24) SHA1(411fd7bbb85dfd8c793f0871041f64febf5571e0))

	ROM_REGION( 0x0800, "chargen", 0 ) // minor differences to sagafox, most notable on '6', suffering from bitrot?
	ROM_LOAD("1cg0.bin", 0x0000, 0x0800, CRC(fba860fc) SHA1(7cd467fe2c11861daf1ed5f9f002874eabe2a47a))

	ROM_REGION( 0x0400, "keyboard", 0 ) // use with KR3600-PRO
	ROM_LOAD("1kb1.bin", 0x0000, 0x0400, CRC(92b00014) SHA1(0669ecfc24bf010735a9c14791409e80390a01f7))

	ROM_REGION( 0x0800, "oam", 0 ) // software protection module?
	ROM_LOAD("oam120.bin", 0x0000, 0x0800, CRC(880a8e36) SHA1(c6bee88a294090f039161fe20ce36a4ada3b10d3))
ROM_END

} // anonymous namespace


//    YEAR  NAME        PARENT  COMPAT MACHINE   INPUT   CLASS         INIT        COMPANY                                FULLNAME                            FLAGS
COMP( 1981, sbrain,     0,      0,     sbrain,   sbrain, sbrain_state, empty_init, "Intertec Data Systems",               "SuperBrain Video Computer System", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
COMP( 1980, sagafox,    sbrain, 0,     sagafox,  sbrain, sbrain_state, empty_init, "Sistemi Avanzati Gestione Aziendale", "Saga Fox",                         MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
COMP( 1980, sagafoxf80, sbrain, 0,     sagafox,  sbrain, sbrain_state, empty_init, "Sistemi Avanzati Gestione Aziendale", "Saga Fox/F80",                     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )



sc12.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger, yoyo_chessboard
/*******************************************************************************

Fidelity Sensory 12 Chess Challenger (SC12, SE12, 6086)
------------------------------------
RE information from netlist by Berger

8*(8+1) buttons, 8+8+2 red LEDs
DIN 41524C printer port (600 baud, 7 data bits, 1 stop bit, no parity)
36-pin edge connector
CPU is a R65C02P4, running at 4MHz*

Model SC12 is 3MHz, model SE12 3.57MHz, model 6086 4MHz.

*By default, the CPU frequency is lowered on A13/A14 access, with a factory-set jumper:
/2 on model SC12(1.5MHz), /4 on model 6086(1MHz)

NE556 dual-timer IC:
- timer#1, one-shot at power-on, to CPU _RESET
- timer#2: R1=82K+50K pot at 26K, R2=1K, C=22nF, to CPU _IRQ: ~596Hz, active low=15.25us

Memory map:
-----------
0000-0FFF: 4K RAM (2016 * 2)
2000-5FFF: cartridge
6000-7FFF: control(W)
8000-9FFF: 8K ROM SSS SCM23C65E4
A000-BFFF: keypad(R)
C000-DFFF: 4K ROM TI TMS2732AJL-45
E000-FFFF: 8K ROM Toshiba TMM2764D-2

control: (74LS377)
--------
Q0-Q3: 7442 A0-A3
Q4: enable printer port pin 1 input
Q5: printer port pin 5 output
Q6,Q7: LEDs common anode

7442 0-8: input mux and LEDs cathode
7442 9: buzzer

The keypad is read through a 74HC251, where S0,1,2 is from CPU A0,1,2, Y is connected to CPU D7.
If control Q4 is set, printer data can be read from I0.

Similar to EAS, the new game command for SC12 is: RE -> A6 (or A8) -> CL.
The newer model 6086 does not have this issue.

Fidelity Electronics, Ltd. went bankrupt in early 1984. They were briefly Fidelity
Computer Products, Inc. (FCP) afterwards, and then Fidelity International, Inc.

TODO:
- is SE12 program the same as SC12? just a faster CPU, and probably /4 divider?

*******************************************************************************/

#include "emu.h"
#include "clockdiv.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "bus/rs232/rs232.h"
#include "cpu/m6502/r65c02.h"
#include "machine/clock.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "fidel_sc12.lh"


namespace {

// note: sub-class of fidel_clockdiv_state (see clockdiv.*)

class sc12_state : public fidel_clockdiv_state
{
public:
	sc12_state(const machine_config &mconfig, device_type type, const char *tag) :
		fidel_clockdiv_state(mconfig, type, tag),
		m_rs232(*this, "rs232"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.0")
	{ }

	// machine configs
	void sc12(machine_config &config);
	void sc12b(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(change_cpu_freq);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<rs232_port_device> m_rs232;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport m_inputs;

	u8 m_inp_mux = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void control_w(u8 data);
	u8 input_r(offs_t offset);
};

void sc12_state::machine_start()
{
	fidel_clockdiv_state::machine_start();

	// register for savestates
	save_item(NAME(m_inp_mux));
}

INPUT_CHANGED_MEMBER(sc12_state::change_cpu_freq)
{
	// known official CPU speeds: 3MHz, 3.57MHz, 4MHz
	static const XTAL xtal[3] = { 3_MHz_XTAL, 3.579545_MHz_XTAL, 4_MHz_XTAL };
	m_maincpu->set_unscaled_clock(xtal[newval % 3]);
}



/*******************************************************************************
    I/O
*******************************************************************************/

void sc12_state::control_w(u8 data)
{
	// d0-d3: 7442 a0-a3
	// 7442 0-8: led data, input mux
	m_inp_mux = data & 0xf;
	u16 sel = 1 << m_inp_mux & 0x3ff;

	// 7442 9: speaker out
	m_dac->write(BIT(sel, 9));

	// d6,d7: led select (active low)
	m_display->matrix(~data >> 6 & 3, sel & 0x1ff);

	// d4: set when reading printer busy flag from 0xa000
	// d5: printer port data
	m_rs232->write_txd(BIT(~data, 5));
}

u8 sc12_state::input_r(offs_t offset)
{
	u8 data = 0;

	// a0-a2,d7: multiplexed inputs (active low)
	// read chessboard sensors
	if (m_inp_mux < 8)
		data = m_board->read_file(m_inp_mux);

	// read button panel
	else if (m_inp_mux == 8)
		data = m_inputs->read();

	return (data >> offset & 1) ? 0 : 0x80;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void sc12_state::main_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).ram();
	map(0x2000, 0x5fff).r("cartslot", FUNC(generic_slot_device::read_rom));
	map(0x6000, 0x6000).mirror(0x1fff).w(FUNC(sc12_state::control_w));
	map(0x8000, 0x9fff).rom();
	map(0xa000, 0xa007).mirror(0x1ff8).r(FUNC(sc12_state::input_r));
	map(0xc000, 0xcfff).mirror(0x1000).rom();
	map(0xe000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( sc12_base )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("RV / Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("DM / Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("TB / Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("LV / Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("PV / Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("PB / King")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CL")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_N) PORT_NAME("RE")
INPUT_PORTS_END

static INPUT_PORTS_START( sc12 )
	PORT_INCLUDE( fidel_clockdiv_2 ) // default for 3MHz
	PORT_INCLUDE( sc12_base )

	PORT_START("CPU")
	PORT_CONFNAME( 0x03, 0x00, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sc12_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "3MHz (SC12)" )
	PORT_CONFSETTING(    0x01, "3.57MHz (SE12)" )
	PORT_CONFSETTING(    0x02, "4MHz (6086)" )
INPUT_PORTS_END

static INPUT_PORTS_START( sc12b )
	PORT_INCLUDE( fidel_clockdiv_4 ) // default for >3MHz
	PORT_INCLUDE( sc12_base )

	PORT_START("CPU")
	PORT_CONFNAME( 0x03, 0x02, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sc12_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "3MHz (SC12)" )
	PORT_CONFSETTING(    0x01, "3.57MHz (SE12)" )
	PORT_CONFSETTING(    0x02, "4MHz (6086)" )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void sc12_state::sc12(machine_config &config)
{
	// basic machine hardware
	R65C02(config, m_maincpu, 3_MHz_XTAL); // R65C02P3
	m_maincpu->set_addrmap(AS_PROGRAM, &sc12_state::main_map);

	auto &irq_clock(CLOCK(config, "irq_clock", 600)); // from 556 timer (22nF, 102K, 1K), ideal frequency is 600Hz
	irq_clock.set_pulse_width(attotime::from_nsec(15250)); // active for 15.25us
	irq_clock.signal_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(200));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(2, 9);
	config.set_default_layout(layout_fidel_sc12);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);

	// cartridge
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "fidel_scc");
	SOFTWARE_LIST(config, "cart_list").set_original("fidel_scc");
}

void sc12_state::sc12b(machine_config &config)
{
	sc12(config);
	m_maincpu->set_clock(4_MHz_XTAL); // R65C02P4
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( fscc12 ) // model 6086, PCB label 510-1084B01
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101-1068a01.ic15", 0x8000, 0x2000, CRC(63c76cdd) SHA1(e0771c98d4483a6b1620791cb99a7e46b0db95c4) ) // SSS SCM23C65E4
	ROM_LOAD("orange.ic13",      0xc000, 0x1000, CRC(45070a71) SHA1(8aeecff828f26fb7081902c757559903be272649) ) // TI TMS2732AJL-45, no label, orange sticker
	ROM_LOAD("red.ic14",         0xe000, 0x2000, CRC(183d3edc) SHA1(3296a4c3bce5209587d4a1694fce153558544e63) ) // Toshiba TMM2764D-2, no label, red sticker
ROM_END

ROM_START( fscc12a ) // model SC12, PCB label 510-1084B01
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101-1068a01.ic15", 0x8000, 0x2000, CRC(63c76cdd) SHA1(e0771c98d4483a6b1620791cb99a7e46b0db95c4) ) // SSS SCM23C65E4
	ROM_LOAD("orange.ic13",      0xc000, 0x1000, CRC(ed5289b2) SHA1(9b0c7f9ae4102d4a66eb8c91d4e84b9eec2ffb3d) ) // TI TMS2732AJL-45, no label, orange sticker
	ROM_LOAD("red.ic14",         0xe000, 0x2000, CRC(0c4968c4) SHA1(965a66870b0f8ce9549418cbda09d2ff262a1504) ) // TI TMS2764JL-25, no label, red sticker
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY, FULLNAME, FLAGS
SYST( 1986, fscc12,  0,      0,      sc12b,   sc12b, sc12_state, empty_init, "Fidelity International", "Sensory Chess Challenger \"12 B\" (model 6086)", MACHINE_SUPPORTS_SAVE )
SYST( 1984, fscc12a, fscc12, 0,      sc12,    sc12,  sc12_state, empty_init, "Fidelity Computer Products", "Sensory Chess Challenger \"12\" (model SC12)", MACHINE_SUPPORTS_SAVE )



sc2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco, hap
/*******************************************************************************

Schachcomputer SC 2 (G-5002.500)

2nd chess computer by VEB(Volkseigener Betrieb) Funkwerk Erfurt. The company
was renamed to VEB Mikroelektronik "Karl Marx" Erfurt in 1983, and formed into
X-FAB Semiconductor Foundries AG after the German unification. SC 2 chess
program is an unlicensed copy of Fidelity Chess Challenger 10 C, with some
patches and an extra 1KB ROM to deal with the different I/O.

3 versions known: initial version, revision E, revision EP.

Schachcomputer SC 1 was canceled before wide release, it's assumed to
be on similar hardware, but PCB photos show 10 ROM chips instead of 9.

keypad legend:

R - Rückstellen (reset)
K - Programmstufen (level)
W - Figurenwahl (white/black)
P - Problemeingabe (problem mode)
T - Tonabschaltung (sound on/off)
L - Löschen (clear)
Q - Quittierung (enter)

Fidelity CC10 synonyms: RE, LV, RV, PB, ♪, CL, EN

*******************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/z80pio.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "sc2.lh"


namespace {

class sc2_state : public driver_device
{
public:
	sc2_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_pio(*this, "z80pio"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void sc2(machine_config &config);

	// Rückstellen is also tied to CPU RESET
	DECLARE_INPUT_CHANGED_MEMBER(reset_button) { m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE); }

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<z80pio_device> m_pio;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<4> m_inputs;

	u8 m_inp_mux = 0;
	u8 m_digit_data = 0;

	void main_io(address_map &map) ATTR_COLD;
	void main_map(address_map &map) ATTR_COLD;

	void update_display();
	u8 pio_port_b_r();
	void pio_port_a_w(u8 data);
	void pio_port_b_w(u8 data);
	u8 speaker_r(offs_t offset);
};

void sc2_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_digit_data));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void sc2_state::update_display()
{
	m_display->matrix(~m_inp_mux, m_digit_data);
}

u8 sc2_state::pio_port_b_r()
{
	u8 data = 0;

	// read keypad
	for (int i = 0; i < 4; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read();

	return data << 4 | 0xf;
}

void sc2_state::pio_port_a_w(u8 data)
{
	// digit segment data
	m_digit_data = bitswap<8>(data,7,0,1,2,3,4,5,6);
	update_display();
}

void sc2_state::pio_port_b_w(u8 data)
{
	// d0-d3: keypad mux(active high), led mux(active low)
	m_inp_mux = data;
	update_display();
}

u8 sc2_state::speaker_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		m_dac->write(BIT(~offset, 11));

	return 0xff;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void sc2_state::main_map(address_map &map)
{
	map(0x0000, 0x0fff).rom();
	map(0x1000, 0x13ff).ram();
	map(0x2000, 0x33ff).rom();
	map(0x3400, 0x3400).select(0x800).r(FUNC(sc2_state::speaker_r));
}

void sc2_state::main_io(address_map &map)
{
	map.global_mask(0x03);
	map(0x00, 0x03).rw(m_pio, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( sc2 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("A 1") PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_A)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("B 2") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_B)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("C 3") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_C)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("D 4") PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_D)

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("E 5") PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_E)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("F 6") PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_F)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("G 7") PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_G)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("H 8") PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_H)

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("K") PORT_CODE(KEYCODE_K)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("W") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sc2_state::reset_button), 0)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("P") PORT_CODE(KEYCODE_P)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void sc2_state::sc2(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 9.8304_MHz_XTAL/4); // U880 Z80 clone
	m_maincpu->set_addrmap(AS_PROGRAM, &sc2_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &sc2_state::main_io);

	Z80PIO(config, m_pio, 9.8304_MHz_XTAL/4);
	m_pio->out_pa_callback().set(FUNC(sc2_state::pio_port_a_w));
	m_pio->in_pb_callback().set(FUNC(sc2_state::pio_port_b_r));
	m_pio->out_pb_callback().set(FUNC(sc2_state::pio_port_b_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 8);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_sc2);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( sc2 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "bm008.bin", 0x0000, 0x0400, CRC(3023ea82) SHA1(07020d153d802c672c39e1af3c716dbe35e23f08) )
	ROM_LOAD( "bm009.bin", 0x0400, 0x0400, CRC(6a34814e) SHA1(e58ae6615297b028db135a48a8f9e186a4220f4f) )
	ROM_LOAD( "bm010.bin", 0x0800, 0x0400, CRC(deab0373) SHA1(81c9a7197eef8d9131e47ecd2ec35b943caee54e) )
	ROM_LOAD( "bm011.bin", 0x0c00, 0x0400, CRC(c8282339) SHA1(8d6b8861281e967a77609b6d77e80afd47d28ed2) )
	ROM_LOAD( "bm012.bin", 0x2000, 0x0400, CRC(2e6a4294) SHA1(7b9bd191c9ec73139a65c3a339ab88e1f3eb5ed2) )
	ROM_LOAD( "bm013.bin", 0x2400, 0x0400, CRC(3e02eb42) SHA1(2e4a9a8fd04c202c9518550d7e8cf9bfea394153) )
	ROM_LOAD( "bm014.bin", 0x2800, 0x0400, CRC(538d449e) SHA1(c4186995b69e97740e01eaff84a20d49d03d180f) )
	ROM_LOAD( "bm015.bin", 0x2c00, 0x0400, CRC(b4991dca) SHA1(6a6cdddf5c4afa24773acf693f58c34b99c8d328) )
	ROM_LOAD( "bm037.bin", 0x3000, 0x0400, CRC(2b67faf1) SHA1(5c65734acaeb766240dbd492a774c56fcfc382f7) )
ROM_END

ROM_START( sc2a )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "bm008.bin", 0x0000, 0x0400, CRC(3023ea82) SHA1(07020d153d802c672c39e1af3c716dbe35e23f08) )
	ROM_LOAD( "bm009.bin", 0x0400, 0x0400, CRC(6a34814e) SHA1(e58ae6615297b028db135a48a8f9e186a4220f4f) )
	ROM_LOAD( "bm010.bin", 0x0800, 0x0400, CRC(deab0373) SHA1(81c9a7197eef8d9131e47ecd2ec35b943caee54e) )
	ROM_LOAD( "bm011.bin", 0x0c00, 0x0400, CRC(c8282339) SHA1(8d6b8861281e967a77609b6d77e80afd47d28ed2) )
	ROM_LOAD( "bm012.bin", 0x2000, 0x0400, CRC(2e6a4294) SHA1(7b9bd191c9ec73139a65c3a339ab88e1f3eb5ed2) )
	ROM_LOAD( "bm013.bin", 0x2400, 0x0400, CRC(3e02eb42) SHA1(2e4a9a8fd04c202c9518550d7e8cf9bfea394153) )
	ROM_LOAD( "bm014.bin", 0x2800, 0x0400, CRC(538d449e) SHA1(c4186995b69e97740e01eaff84a20d49d03d180f) )
	ROM_LOAD( "bm015.bin", 0x2c00, 0x0400, CRC(b4991dca) SHA1(6a6cdddf5c4afa24773acf693f58c34b99c8d328) )
	ROM_LOAD( "bm016.bin", 0x3000, 0x0400, CRC(4fe0853a) SHA1(c2253e320778b0ea468fb54f26ae83d07f9700e6) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY, FULLNAME, FLAGS
SYST( 1981, sc2,  0,      0,      sc2,     sc2,   sc2_state, empty_init, "VEB Funkwerk Erfurt", "Schachcomputer SC 2 (rev. E)", MACHINE_SUPPORTS_SAVE )
SYST( 1981, sc2a, sc2,    0,      sc2,     sc2,   sc2_state, empty_init, "VEB Funkwerk Erfurt", "Schachcomputer SC 2", MACHINE_SUPPORTS_SAVE )



sc6.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:yoyo_chessboard, Berger, Sean Riddle
/*******************************************************************************

Fidelity Sensory Chess Challenger 6 (model SC6)
Fidelity Mini Sensory Chess Challenger (model MSC, 1982 version)
Fidelity The Classic (model CC8/6079)
Fidelity Gambit Voice (model 6085)

The chess engine is by Ron Nelson. These are all on similar hardware. Several
models are equal to eachother from an emulator's perspective, hence some aren't
included in MAME, see list below.

Known MCU ROM serials:
- 7203 100-1012B01 (1982), Mini Sensory Chess Challenger
- 9517 [no label]  (1985), The Classic (model CC8)
- 9311 100-1020B02 (1986), The Classic (model 6079), The Gambit, Silver Bullet
- 9311 100-1020B01 (1989), The Gambit, Designer 1500, Peri Beta
- 9337 100-1020C01 (1987), Gambit Voice

100-1020B01 ROM contents is confirmed to be identical to 100-1020B02.

TODO:
- is The Classic model 6079D a different ROM?

================================================================================

SC6 hardware notes:
- PCB label: 510-1045B01
- INS8040N-11 MCU, 11MHz XTAL
- external 4KB ROM 2332 101-1035A01, in module slot
- buzzer, 2 7seg LEDs, 8*8 chessboard buttons

SC6 released modules, * denotes not dumped yet:
- *BO6: Book Openings I
- *CG6: Greatest Chess Games 1
- SC6: pack-in, original program

SC6 program is contained in BO6 and CG6.

================================================================================

MSC hardware notes:
- PCB label: 510-1044B01
- P8049H MCU, 2KB internal ROM, 11MHz XTAL
- buzzer, 18 leds, 8*8 chessboard buttons, module slot

MCU ports I/O is identical to SC6.

It accepts the same modules as the 1st MSC version. See msc.cpp for known modules.
The module overrides the internal ROM, by asserting the EA pin.

Silver Bullet hardware notes:
- PCB from MSC, but lose/check leds unpopulated
- TMP80C50AP-6-9311 MCU, 4KB internal ROM, 6MHz XTAL
- buzzer, 16 leds, 8*8 chessboard buttons, module slot (takes MSC modules)

================================================================================

The Classic (model CC8) hardware notes:
- PCB label: 510-1095A01
- TMP80C50AP-6-9517 MCU, 4KB internal ROM, 6MHz XTAL
- buzzer, 16 leds, 8*8 chessboard buttons

The Classic (model 6079) hardware notes:
- TMP80C50AP-6-9311 MCU, rest is same as model CC8

The Gambit hardware notes:
- PCB label: 510-1115A01
- rest is same as The Classic (model 6079)

The Gambit either has a black/white button panel color theme, or black/red/white.
The latter was more commonly seen on newer versions and Gambit Voice.

Designer 1500 hardware notes:
- PCB label: 510.1131A01
- rest is same as The Classic (model 6079)

Peri Beta has the same PCB/ROM as Designer 1500, only the housing differs.

================================================================================

Gambit Voice hardware notes:
- TMP80C50AP-6-9337 MCU, 4KB internal ROM, 6MHz XTAL
- 510.1117A01 sound PCB (TSI S14001A, 4KB or 8KB ROM)
- speaker, 16 leds, 8*8 chessboard buttons

The sound PCB is the same as the one from Excel Voice (see excel.cpp), except
with a smaller ROM, and no language selection DIP switches. It only supports
the English or French speech ROM.

As noted above, the non-voice Gambit is the same ROM as The Classic (model 6079).

*******************************************************************************/

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "sound/s14001a.h"
#include "video/pwm.h"

#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "fidel_classic.lh"
#include "fidel_gambitv.lh"
#include "fidel_msc_v2.lh"
#include "fidel_sc6.lh"


namespace {

class sc6_state : public driver_device
{
public:
	sc6_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_speech(*this, "speech"),
		m_language(*this, "language"),
		m_cart(*this, "cartslot"),
		m_inputs(*this, "IN.0")
	{ }

	// machine configs
	void shared(machine_config &config);
	void sc6(machine_config &config);
	void msc(machine_config &config);
	void classic(machine_config &config);
	void gambitv(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<mcs48_cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	optional_device<dac_1bit_device> m_dac;
	optional_device<s14001a_device> m_speech;
	optional_region_ptr<u8> m_language;
	optional_device<generic_slot_device> m_cart;
	required_ioport m_inputs;

	u8 m_inp_mux = 0;
	u8 m_speech_data = 0;

	// address maps
	void msc_map(address_map &map) ATTR_COLD;
	void sc6_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void mux_w(u8 data);
	void select_w(u8 data);
	u8 input_r();

	u8 msc_rom_r(offs_t offset);

	void speech_w(offs_t offset, u8 data, u8 mem_mask);
	u8 speech_r();
};

void sc6_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_speech_data));
}

void sc6_state::machine_reset()
{
	// EA pin is tied to VCC when a cartridge is inserted
	m_maincpu->set_input_line(MCS48_INPUT_EA, (m_cart && m_cart->exists()) ? ASSERT_LINE : CLEAR_LINE);
}



/*******************************************************************************
    I/O
*******************************************************************************/

// common

void sc6_state::mux_w(u8 data)
{
	// P24-P27: 7442 A-D (or 74145)
	// 7442 0-8: input mux, led data
	m_inp_mux = data >> 4 & 0xf;
	u16 sel = 1 << m_inp_mux;
	m_display->write_mx(sel);

	// 7442 9: speaker out
	if (m_dac != nullptr)
		m_dac->write(BIT(sel, 9));
}

void sc6_state::select_w(u8 data)
{
	// P16,P17: digit/led select
	m_display->write_my(~data >> 6 & 3);
}

u8 sc6_state::input_r()
{
	// P10-P15,T0,T1: multiplexed inputs
	u8 data = 0;

	// read chessboard sensors
	if (m_inp_mux < 8)
		data = m_board->read_file(m_inp_mux);

	// read button panel
	else if (m_inp_mux == 8)
		data = m_inputs->read();

	return ~data;
}


// speech (gambitv)

u8 sc6_state::speech_r()
{
	// P20: S14001A busy pin
	u8 data = m_speech->busy_r();

	// P21: language jumper
	// P22,P23: unused?
	return data | (*m_language << 1 & 2) | 0xfc;
}

void sc6_state::speech_w(offs_t offset, u8 data, u8 mem_mask)
{
	data &= mem_mask;
	m_speech_data = data;

	// DB6: speech ROM A12
	m_speech->set_rom_bank(BIT(data, 6));

	// DB0-DB5: S14001A C0-C5
	// DB7: S14001A start pin
	m_speech->data_w(data & 0x3f);
	m_speech->start_w(BIT(data, 7));
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

u8 sc6_state::msc_rom_r(offs_t offset)
{
	// MSC module ROM A12 is forced high (lower half has a Z8 program)
	return m_cart->read_rom(offset | 0x1000);
}

void sc6_state::msc_map(address_map &map)
{
	map(0x0000, 0x0fff).r(FUNC(sc6_state::msc_rom_r));
}

void sc6_state::sc6_map(address_map &map)
{
	map(0x0000, 0x0fff).r(m_cart, FUNC(generic_slot_device::read_rom));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( sc6 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("RV / Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("DM / Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("TB / Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("LV / Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("PV / Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("PB / King")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CL")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_N) PORT_NAME("RE")
INPUT_PORTS_END

static INPUT_PORTS_START( msc )
	PORT_INCLUDE( sc6 )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Speaker / Bishop")
INPUT_PORTS_END

static INPUT_PORTS_START( gambitv )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Move / Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Hint / Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Take Back / Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Level / Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Verify / Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Problem / King")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Clear")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void sc6_state::shared(machine_config &config)
{
	// basic machine hardware
	m_maincpu->p1_in_cb().set(FUNC(sc6_state::input_r)).mask(0x3f);
	m_maincpu->p1_in_cb().append_constant(0xc0).mask(0xc0);
	m_maincpu->p1_out_cb().set(FUNC(sc6_state::select_w));
	m_maincpu->p2_out_cb().set(FUNC(sc6_state::mux_w));
	m_maincpu->t0_in_cb().set(FUNC(sc6_state::input_r)).bit(6);
	m_maincpu->t1_in_cb().set(FUNC(sc6_state::input_r)).bit(7);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(2, 9);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void sc6_state::sc6(machine_config &config)
{
	I8040(config, m_maincpu, 11_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &sc6_state::sc6_map);
	shared(config);

	// video hardware
	m_display->set_segmask(0x3, 0x7f);
	config.set_default_layout(layout_fidel_sc6);

	// cartridge
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "fidel_sc6").set_must_be_loaded(true);
	SOFTWARE_LIST(config, "cart_list").set_original("fidel_sc6");
}

void sc6_state::msc(machine_config &config)
{
	I8049(config, m_maincpu, 11_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &sc6_state::msc_map);
	shared(config);

	config.set_default_layout(layout_fidel_msc_v2);

	// cartridge
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "fidel_msc");
	SOFTWARE_LIST(config, "cart_list").set_original("fidel_msc");
}

void sc6_state::classic(machine_config &config)
{
	I8050(config, m_maincpu, 6_MHz_XTAL);
	shared(config);

	config.set_default_layout(layout_fidel_classic);
}

void sc6_state::gambitv(machine_config &config)
{
	classic(config);

	// basic machine hardware
	m_maincpu->p2_in_cb().set(FUNC(sc6_state::speech_r));
	m_maincpu->bus_in_cb().set([this](){ return m_speech_data; });
	m_maincpu->bus_out_cb().set(FUNC(sc6_state::speech_w));

	config.set_default_layout(layout_fidel_gambitv);

	// sound hardware
	config.device_remove("dac");
	S14001A(config, m_speech, 25000); // R/C circuit, around 25khz
	m_speech->add_route(ALL_OUTPUTS, "speaker", 0.75);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( fscc6 )
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASE00 )
	// none here, it's in the module slot
ROM_END

ROM_START( miniscc )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD("p8049h_7203_100-1012b01.ic1", 0x0000, 0x0800, CRC(ea3261f7) SHA1(1601358fdf0eee0b973c0f4c78bf679b8dada72a) )
ROM_END

ROM_START( classic )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("tmp80c50ap-6-9311_100-1020b02.ic1", 0x0000, 0x1000, CRC(ba41b5ba) SHA1(1a5c5b2e990a07b9ff51eecfa952a4b890107797) )
ROM_END

ROM_START( classica )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("tmp80c50ap-6_9517.ic1", 0x0000, 0x1000, CRC(dfd30755) SHA1(92075c7df9205b9647801487b9bbddcf230dfc91) )
ROM_END

ROM_START( gambitv )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("tmp80c50ap-6-9337_100-1020c01.ic1", 0x0000, 0x1000, CRC(dafee386) SHA1(d67914fb2abd73c0068b7e61fc23d211c52d65d9) )

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "fr", "French")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 1, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 0, ROM_BIOS(1) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(1) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT   COMPAT  MACHINE  INPUT    CLASS      INIT        COMPANY, FULLNAME, FLAGS
SYST( 1982, fscc6,    0,       0,      sc6,     sc6,     sc6_state, empty_init, "Fidelity Electronics", "Sensory Chess Challenger \"6\"", MACHINE_SUPPORTS_SAVE )
SYST( 1982, miniscc,  0,       0,      msc,     msc,     sc6_state, empty_init, "Fidelity Electronics", "Mini Sensory Chess Challenger (MCS-48 version)", MACHINE_SUPPORTS_SAVE ) // aka "Mini Sensory II"

SYST( 1986, classic,  0,       0,      classic, sc6,     sc6_state, empty_init, "Fidelity International", "The Classic (model 6079)", MACHINE_SUPPORTS_SAVE )
SYST( 1985, classica, classic, 0,      classic, sc6,     sc6_state, empty_init, "Fidelity International", "The Classic (model CC8)", MACHINE_SUPPORTS_SAVE )

SYST( 1987, gambitv,  0,       0,      gambitv, gambitv, sc6_state, empty_init, "Fidelity International", "Gambit Voice", MACHINE_SUPPORTS_SAVE )



sc8.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:yoyo_chessboard
/*******************************************************************************

Fidelity Sensory Chess Challenger "8" (SCC)

It is the first chesscomputer with sensors under the chessboard.

The box names it Sensory Chess Challenger, Sensory Chess Challenger "8" title
is on the 1st page of the manual. Like other Fidelity chesscomputers from
around this time, the number indicates maximum difficulty level.

It was also rereleased in 1983 as "Poppy", marketed for children, the housing
was in bright red color.

Hardware notes:
- PCB label: 510-1011 REV.2
- Z80A CPU @ 3.9MHz
- 4KB ROM(MOS 2732), 256 bytes RAM(35391CP)
- chessboard buttons, 8*8+1 leds

*******************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "fidel_sc8.lh"


namespace {

class scc_state : public driver_device
{
public:
	scc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.0")
	{ }

	// machine configs
	void scc(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport m_inputs;

	u8 m_inp_mux = 0;
	u8 m_led_data = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	// I/O handlers
	u8 input_r();
	void control_w(offs_t offset, u8 data);
};

void scc_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_led_data));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void scc_state::control_w(offs_t offset, u8 data)
{
	// a0-a2,d7: led data
	u8 mask = 1 << (offset & 7);
	m_led_data = (m_led_data & ~mask) | ((data & 0x80) ? mask : 0);

	// d0-d3: 7442 to led select, input mux
	// d4: corner led(direct)
	m_inp_mux = data & 0xf;
	u16 sel = 1 << m_inp_mux;
	m_display->matrix((sel & 0xff) | (data << 4 & 0x100), m_led_data);

	// 7442 9: speaker out
	m_dac->write(BIT(sel, 9));
}

u8 scc_state::input_r()
{
	// d0-d7: multiplexed inputs (active low)
	u8 data = 0;

	// read chessboard sensors
	if (m_inp_mux < 8)
		data = m_board->read_file(m_inp_mux);

	// read button panel
	else if (m_inp_mux == 8)
		data = m_inputs->read();

	return ~data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void scc_state::main_map(address_map &map)
{
	map(0x0000, 0x0fff).rom();
	map(0x5000, 0x50ff).ram();
}

void scc_state::main_io(address_map &map)
{
	map.global_mask(0x07);
	map(0x00, 0x07).rw(FUNC(scc_state::input_r), FUNC(scc_state::control_w));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( scc )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Rook")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Knight")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("King")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CL")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_N) PORT_NAME("RE")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void scc_state::scc(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 3.9_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &scc_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &scc_state::main_io);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	config.set_default_layout(layout_fidel_sc8);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( fscc8 ) // model SCC
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "101-32017", 0x0000, 0x1000, CRC(5340820d) SHA1(e3494c7624b3cacbbb9a0a8cc9e1ed3e00326dfd) ) // 2732
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY, FULLNAME, FLAGS
SYST( 1980, fscc8, 0,      0,      scc,     scc,   scc_state, empty_init, "Fidelity Electronics", "Sensory Chess Challenger \"8\"", MACHINE_SUPPORTS_SAVE )



sc9.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger, yoyo_chessboard
/*******************************************************************************

Fidelity SC9, Fidelity Playmatic "S"

TODO:
- fscc9ps module switch and led
- verify fscc9ps XTAL (checked against sound recording, 99.97% similarity)

Hardware notes:

Fidelity Sensory Chess Challenger "9" (SC9) overview:
- PCB label: 510-1046C01 2-1-82
- R6502-13, 1.4MHz from resonator, another pcb with the same resonator was measured 1.49MHz*
- 2KB RAM(TMM2016P), 2*8KB ROM(HN48364P)
- 36-pin edge connector, assume same as SC12
- 8*(8+1) buttons, 8*8+1 LEDs

*: 2 other boards were measured 1.60MHz and 1.88MHz(newest serial). Online references
suggest 3 versions of SC9(C01) total: 1.5MHz, 1.6MHz, and 1.9MHz.

I/O is via TTL, not further documented here

3 versions were available, the newest "B" version was 2MHz and included the Budapest
program. The Playmatic S was only released in Germany, it's basically a 'deluxe'
version of SC9 with magnet sensors and came with CB9 and CB16.

================================================================================

Starting with SC9, Fidelity added a cartridge slot to their chess computers, meant
for extra book opening databases and recorded games.

Known modules (*denotes undumped):
- CB9: Book Openings 1
- CB16: Book Openings 2
- *CG64: 64 Greatest Games
- DVC: Sicilan Varation (prototype)
- EOA-EOE: Chess Encyclopedia Volume A-E (5 modules, needs 7seg display)
- *TDF: Tarrasch Defence to the Queen's Gambit

The edge connector has D0-D7, A0-A13, 2 chip select lines, read/write lines, IRQ
line. IRQ and write strobe are unused. Maximum known size is 16KB.

*******************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/m6502/m6502.h"
#include "machine/clock.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "fidel_playmatic.lh"
#include "fidel_sc9.lh"


namespace {

class sc9_state : public driver_device
{
public:
	sc9_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.0")
	{ }

	// machine configs
	void sc9b(machine_config &config);
	void sc9c(machine_config &config);
	void sc9d(machine_config &config);
	void playmatic(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(sc9c_change_cpu_freq);

protected:
	virtual void machine_start() override ATTR_COLD;

	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport m_inputs;

	u8 m_inp_mux = 0;
	u8 m_led_data = 0;

	// address maps
	void sc9_map(address_map &map) ATTR_COLD;
	void sc9d_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void control_w(u8 data);
	void led_w(offs_t offset, u8 data);
	u8 input_r();
	u8 input_d7_r(offs_t offset);
};

void sc9_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_led_data));
}

INPUT_CHANGED_MEMBER(sc9_state::sc9c_change_cpu_freq)
{
	// SC9(C01) was released with 1.5MHz, 1.6MHz, or 1.9MHz CPU
	static const u32 freq[3] = { 1'500'000, 1'600'000, 1'900'000 };
	m_maincpu->set_unscaled_clock(freq[newval % 3]);
}



/*******************************************************************************
    I/O
*******************************************************************************/

void sc9_state::control_w(u8 data)
{
	// d0-d3: 74245 P0-P3
	// 74245 Q0-Q8: input mux, led select
	m_inp_mux = data & 0xf;
	u16 sel = 1 << m_inp_mux;
	m_display->write_my(sel);

	// 74245 Q9: speaker out
	m_dac->write(BIT(sel, 9));

	// d4,d5: ?
	// d6,d7: N/C
}

void sc9_state::led_w(offs_t offset, u8 data)
{
	// a0-a2,d0: led data via NE591N
	m_led_data = (m_led_data & ~(1 << offset)) | ((data & 1) << offset);
	m_display->write_mx(m_led_data);
}

u8 sc9_state::input_r()
{
	// d0-d7: multiplexed inputs (active low)
	u8 data = 0;

	// read chessboard sensors
	if (m_inp_mux < 8)
		data = m_board->read_file(m_inp_mux);

	// read button panel
	else if (m_inp_mux == 8)
		data = m_inputs->read();

	return ~data;
}

u8 sc9_state::input_d7_r(offs_t offset)
{
	// a0-a2,d7: multiplexed inputs
	return (input_r() >> offset & 1) ? 0x80 : 0;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void sc9_state::sc9_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).mirror(0x1800).ram();
	map(0x2000, 0x5fff).r("cartslot", FUNC(generic_slot_device::read_rom));
	map(0x6000, 0x6000).mirror(0x1fff).w(FUNC(sc9_state::control_w));
	map(0x8000, 0x8007).mirror(0x1ff8).w(FUNC(sc9_state::led_w)).nopr();
	map(0xa000, 0xa000).mirror(0x1fff).r(FUNC(sc9_state::input_r));
	map(0xc000, 0xffff).rom();
}

void sc9_state::sc9d_map(address_map &map)
{
	sc9_map(map);
	map(0xa000, 0xa007).mirror(0x1ff8).r(FUNC(sc9_state::input_d7_r));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( sc9 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("RV / Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("DM / Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("TB / Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("LV / Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("PV / Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("PB / King")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CL")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_N) PORT_NAME("RE")
INPUT_PORTS_END

static INPUT_PORTS_START( sc9c )
	PORT_INCLUDE( sc9 )

	PORT_START("CPU")
	PORT_CONFNAME( 0x03, 0x00, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sc9_state::sc9c_change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "1.5MHz" )
	PORT_CONFSETTING(    0x01, "1.6MHz" )
	PORT_CONFSETTING(    0x02, "1.9MHz" )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void sc9_state::sc9d(machine_config &config)
{
	// basic machine hardware
	M6502(config, m_maincpu, 3.9_MHz_XTAL / 2); // R6502AP, 3.9MHz resonator
	m_maincpu->set_addrmap(AS_PROGRAM, &sc9_state::sc9d_map);

	auto &irq_clock(CLOCK(config, "irq_clock", 600)); // from 555 timer (22nF, 102K, 2.7K), ideal frequency is 600Hz
	irq_clock.set_pulse_width(attotime::from_usec(41)); // active for 41us
	irq_clock.signal_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(200));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(9, 8);
	config.set_default_layout(layout_fidel_sc9);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);

	// cartridge
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "fidel_scc");
	SOFTWARE_LIST(config, "cart_list").set_original("fidel_scc");
}

void sc9_state::sc9b(machine_config &config)
{
	sc9d(config);

	// basic machine hardware
	m_maincpu->set_clock(1'500'000); // from ceramic resonator "681 JSA", measured
	m_maincpu->set_addrmap(AS_PROGRAM, &sc9_state::sc9_map);
}

void sc9_state::playmatic(machine_config &config)
{
	sc9b(config);

	// basic machine hardware
	m_maincpu->set_clock(5.626_MHz_XTAL / 2); // advertised as double the speed of SC9
	m_board->set_type(sensorboard_device::MAGNETS);

	config.set_default_layout(layout_fidel_playmatic);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( fscc9 ) // PCB label 510-1046D01
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101-1034b01", 0xc000, 0x2000, CRC(65288753) SHA1(651f5ca5969ddd72a20cbebdec2de83c4bf10650) )
	ROM_LOAD("101-1034c02", 0xe000, 0x2000, CRC(238b092f) SHA1(7ddffc6dba822aee9d8ad6815b23024ed5cdfd26) )
ROM_END

ROM_START( fscc9b ) // PCB label 510-1046B01
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101-1034a01", 0xc000, 0x2000, CRC(b845c458) SHA1(d3fda65dbd9fae44fa4b93f8207839d8fa0c367a) )
	ROM_LOAD("101-1034a02", 0xe000, 0x2000, CRC(ecfa0a4c) SHA1(738df99a250fad0b1da5ebeb8c92a9ad1461417b) )
ROM_END

ROM_START( fscc9c ) // PCB label 510-1046C01
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101-1034a01", 0xc000, 0x2000, CRC(b845c458) SHA1(d3fda65dbd9fae44fa4b93f8207839d8fa0c367a) ) // HN48364P
	ROM_LOAD("101-1034b02", 0xe000, 0x2000, CRC(cbaf97d7) SHA1(7ed8e68bb74713d9e2ff1d9c037012320b7bfcbf) ) // "
ROM_END

ROM_START( fscc9ps )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("play64c1.bin", 0xc000, 0x2000, CRC(e96aa95d) SHA1(16d90cf0ef166aef579d442671290a2c43e24dfe) )
	ROM_LOAD("play64en.bin", 0xe000, 0x2000, CRC(6fa188d2) SHA1(1b9b0209c496c89ecb7f9ec07bfd9429ff9b275e) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE    INPUT  CLASS      INIT        COMPANY, FULLNAME, FLAGS
SYST( 1982, fscc9,   0,      0,      sc9d,      sc9,   sc9_state, empty_init, "Fidelity Electronics", "Sensory Chess Challenger \"9\" (rev. D)", MACHINE_SUPPORTS_SAVE ) // aka version "B"
SYST( 1982, fscc9b,  fscc9,  0,      sc9b,      sc9,   sc9_state, empty_init, "Fidelity Electronics", "Sensory Chess Challenger \"9\" (rev. B)", MACHINE_SUPPORTS_SAVE )
SYST( 1982, fscc9c,  fscc9,  0,      sc9b,      sc9c,  sc9_state, empty_init, "Fidelity Electronics", "Sensory Chess Challenger \"9\" (rev. C)", MACHINE_SUPPORTS_SAVE )
SYST( 1983, fscc9ps, fscc9,  0,      playmatic, sc9,   sc9_state, empty_init, "Fidelity Deutschland", "Sensory 9 Playmatic S", MACHINE_SUPPORTS_SAVE ) // 9 is not between quotation marks here



schess.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:bataais
/*******************************************************************************

SciSys Sensor Chess (model 221)
(not to be confused with Saitek Kasparov Sensor Chess)

Two versions were released. The 1st version is fake-wood brown plastic, and 2nd
version is grey plastic. The brown version only has 256 bytes RAM. The grey version
has 1KB but does not use the extra available RAM. The ROM(software) is assumed to
be the same for each version.

Buttons are unresponsive, this also applies to the chessboard buttons. The program
is not interrupt-driven and only checks the buttons around twice per second. The user
needs to hold a button for up to half a second to get a response. In MAME, a delay is
applied to the sensorboard interface to make sure that all clicks work there. But that
also means it's not possible to play blitz style (which wouldn't be possible on the
real device anyway, since it reacts so slowly).

Hardware notes:
- Synertek 6502A @ ~2MHz
- 4KB ROM(AMI 2332)
- 1KB RAM(2*2114), or 256 bytes RAM(GTE 3539)
- buzzer, 64+12 leds, button chessboard
- expansion slot at top-right (dummy empty cartridge by default)

Expansion modules: (* denotes not dumped)
- Strong Play Module
- Classical Style Super Strong
- *Hyper Modern Super Strong

*******************************************************************************/

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/m6502/m6502.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "saitek_schess.lh"


namespace {

class schess_state : public driver_device
{
public:
	schess_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// machine configs
	void schess(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<3> m_inputs;

	u8 m_inp_mux = 0;
	u8 m_led_data = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_display();
	void leds1_w(u8 data);
	void leds2_w(offs_t offset, u8 data);
	void control_w(u8 data);
	u8 input_r();
};

void schess_state::machine_start()
{
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_led_data));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void schess_state::update_display()
{
	u8 led_data = bitswap<8>(m_led_data,4,5,6,7,3,2,1,0);
	m_display->matrix_partial(0, 8, 1 << m_inp_mux, led_data);
}

void schess_state::leds1_w(u8 data)
{
	// chessboard leds (muxed)
	m_led_data = ~data;
	update_display();
}

void schess_state::leds2_w(offs_t offset, u8 data)
{
	// button panel leds (direct)
	m_display->write_row(8 + (offset ? 1 : 0), ~data);
}

void schess_state::control_w(u8 data)
{
	// d0-d3: input mux, led select
	m_inp_mux = data & 0xf;
	update_display();

	// d5: speaker out
	m_dac->write(BIT(data, 5));

	// other: ?
}

u8 schess_state::input_r()
{
	u8 data = 0;

	// read chessboard sensors
	if (m_inp_mux < 8)
		data = m_board->read_file(m_inp_mux);

	// read other buttons
	else if (m_inp_mux < 10)
		data = m_inputs[m_inp_mux & 1]->read();
	else
		data = m_inputs[2]->read();

	return bitswap<8>(data,4,5,6,7,3,2,1,0);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void schess_state::main_map(address_map &map)
{
	map(0x0000, 0x03ff).ram();
	map(0x4000, 0x4000).w(FUNC(schess_state::leds1_w));
	map(0x4800, 0x4800).w(FUNC(schess_state::control_w));
	map(0x5000, 0x5000).select(0x0800).w(FUNC(schess_state::leds2_w));
	map(0x6000, 0x6000).r(FUNC(schess_state::input_r));
	map(0x8000, 0x8fff).r("cartslot", FUNC(generic_slot_device::read_rom));
	map(0xf000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( schess )
	PORT_START("IN.0")
	PORT_BIT(0x03, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("Player V Computer")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("White")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Knight")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Reset")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Change Sides")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Hint")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Clear Board")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("Legal")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")

	PORT_START("IN.2")
	PORT_BIT(0x03, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Player V Player")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Interrupt")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Move")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("Verify / Set Up")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Bishop")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void schess_state::schess(machine_config &config)
{
	// basic machine hardware
	M6502(config, m_maincpu, 2'000'000); // approximation, no XTAL
	m_maincpu->set_addrmap(AS_PROGRAM, &schess_state::main_map);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_ticks(1'115'000, 2'000'000)); // see driver notes

	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "schess_cart");
	SOFTWARE_LIST(config, "cart_list").set_original("saitek_schess");

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8+2, 8);
	config.set_default_layout(layout_saitek_schess);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( schess )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("y02-rom", 0xf000, 0x1000, CRC(25181e03) SHA1(4d3a606b196b9019c00795b2cd65ce10fbef932c) ) // AMI 2332
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1981, schess, 0,      0,      schess,  schess, schess_state, empty_init, "SciSys / Heuristic Software", "Sensor Chess", MACHINE_SUPPORTS_SAVE )



scopus.cpp
<---------------------------------------------------------------------->
// license:GPL2+
// copyright-holders:Felipe Sanches
/********************************************************************

   Scopus Sagitta 150 / 180
   Serial terminal

   driver by: Felipe Correa da Silva Sanches <juca@members.fsf.org>

   Thanks to Werner Moecke for the help in inspecting the circuits and
     debugging this driver.

   TODO:
   - figure out how the additional ROMs are mapped into memory
   - figure out how/if the ROMs of the two models (150 and 180) can
     be switched (is that via a dipswitch setting?) The model 180
     contains a Model 150 ROM as well.
   - emulate the printer port
   - document the keyboard layout and emulate it

*********************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8251.h"
#include "machine/i8257.h"
#include "machine/i8212.h"
#include "video/i8275.h"
#include "emupal.h"
#include "screen.h"
#include "bus/rs232/rs232.h"
#include "machine/clock.h"


namespace {

class sagitta180_state : public driver_device
{
public:
	sagitta180_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_palette(*this, "palette"),
		m_crtc(*this, "crtc"),
		m_dma8257(*this, "dma"),
		m_maincpu(*this, "maincpu")
	{ }

	void sagitta180(machine_config &config);

	void init_sagitta180();

private:
	void hrq_w(int state);
	uint8_t memory_read_byte(offs_t offset);
	I8275_DRAW_CHARACTER_MEMBER(crtc_display_pixels);

	void maincpu_io_map(address_map &map) ATTR_COLD;
	void maincpu_map(address_map &map) ATTR_COLD;

	/* devices */
	required_device<palette_device> m_palette;
	required_device<i8275_device> m_crtc;
	required_device<i8257_device> m_dma8257;
	required_device<cpu_device> m_maincpu;

	// Character generator
	const uint8_t *m_chargen = nullptr;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
};

void sagitta180_state::machine_start()
{
	m_palette->set_pen_color(0, rgb_t(0x00,0x00,0x00)); // black
	m_palette->set_pen_color(1, rgb_t(0x00,0xa0,0x00)); // normal
	m_palette->set_pen_color(2, rgb_t(0x00,0xff,0x00)); // highlight

	m_chargen = memregion("chargen")->base();
}


const gfx_layout sagitta180_charlayout =
{
	8, 8,             /* 8x16 characters - the last 8 lines are always blank */
	128,                /* 128 characters */
	1,              /* 1 bits per pixel */
	{0},                /* no bitplanes; 1 bit per pixel */
	{0,1,2,3,4,5,6,7},
	{0, 8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*16                /* space between characters */
};

static GFXDECODE_START( gfx_sagitta180 )
	GFXDECODE_ENTRY( "chargen", 0x0000, sagitta180_charlayout, 0, 1 )
GFXDECODE_END

I8275_DRAW_CHARACTER_MEMBER(sagitta180_state::crtc_display_pixels)
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	uint8_t const chargen_byte = m_chargen[ (linecount & 7) | ((unsigned)charcode << 3) ];

	uint8_t pixels;
	using namespace i8275_attributes;
	if (BIT(attrcode, LTEN)) {
		pixels = ~0;
	} else if (BIT(attrcode, VSP) || (linecount & 8) != 0) {
		pixels = 0;
	} else {
		pixels = chargen_byte;
	}

	if (BIT(attrcode, RVV)) {
		pixels = ~pixels;
	}

	for (unsigned i = 0; i < 7; i++) {
		bitmap.pix(y, x + i) = palette[ BIT(pixels, 7 - i) ];
	}
}


void sagitta180_state::machine_reset()
{
}

void sagitta180_state::maincpu_map(address_map &map)
{
	map.global_mask(0xffff);
	map(0x0000, 0x07ff).rom();
//  map(0x0800, 0x17ff).rom();
	map(0x1800, 0xffff).ram();
}


void sagitta180_state::maincpu_io_map(address_map &map)
{
	map(0x00, 0x00).portr("DSW");
	map(0x20, 0x21).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x30, 0x31).rw(m_crtc, FUNC(i8275_device::read), FUNC(i8275_device::write));
	map(0x40, 0x48).rw(m_dma8257, FUNC(i8257_device::read), FUNC(i8257_device::write));
}


static INPUT_PORTS_START( sagitta180 )
	/*
	  Serial settings:
	   * async
	   * 1 stop-bit
	   * baud-rate factor = 16x
	   * byte width and parity are specified by dipswitch seetings below.
	*/
	PORT_START("DSW")
	PORT_DIPNAME( 0x03, 0x01, "Serial settings" ) PORT_DIPLOCATION("SW1:8,7")
	PORT_DIPSETTING(    0x00, "7 bits, even parity" )
	PORT_DIPSETTING(    0x01, "8 bits, parity disabled" )
	PORT_DIPSETTING(    0x02, "7 bits, odd parity" )
	//PORT_DIPSETTING(  0x03, "8 bits, parity disabled" )
	PORT_DIPUNKNOWN_DIPLOC( 0x04, 0x00, "SW1:6" )
	PORT_DIPUNKNOWN_DIPLOC( 0x08, 0x00, "SW1:5" )
	PORT_DIPUNKNOWN_DIPLOC( 0x10, 0x00, "SW1:4" )
	PORT_DIPUNKNOWN_DIPLOC( 0x20, 0x00, "SW1:3" )
	PORT_DIPUNKNOWN_DIPLOC( 0x40, 0x00, "SW1:2" )
	PORT_DIPUNKNOWN_DIPLOC( 0x80, 0x00, "SW1:1" )
INPUT_PORTS_END

void sagitta180_state::hrq_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state);
	m_dma8257->hlda_w(state);
}

uint8_t sagitta180_state::memory_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	return prog_space.read_byte(offset);
}

void sagitta180_state::sagitta180(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, XTAL(10'000'000)); /* guessed ! */
	m_maincpu->set_addrmap(AS_PROGRAM, &sagitta180_state::maincpu_map);
	m_maincpu->set_addrmap(AS_IO, &sagitta180_state::maincpu_io_map);
//  m_maincpu->set_irq_acknowledge_callback("intlatch", FUNC(i8212_device::inta_cb));

	I8257(config, m_dma8257, XTAL(14'745'600)); /* guessed xtal */
	m_dma8257->out_iow_cb<2>().set("crtc", FUNC(i8275_device::dack_w));
	m_dma8257->out_hrq_cb().set(FUNC(sagitta180_state::hrq_w));
	m_dma8257->in_memr_cb().set(FUNC(sagitta180_state::memory_read_byte));

	i8251_device &uart(I8251(config, "uart", 0));
	uart.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	uart.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	uart.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "keyboard"));
	rs232.rxd_handler().set("uart", FUNC(i8251_device::write_rxd));
	rs232.cts_handler().set("uart", FUNC(i8251_device::write_cts));
	rs232.dsr_handler().set("uart", FUNC(i8251_device::write_dsr));

	clock_device &uart_clock(CLOCK(config, "uart_clock", 153600));
	uart_clock.signal_handler().set("uart", FUNC(i8251_device::write_txc));
	uart_clock.signal_handler().append("uart", FUNC(i8251_device::write_rxc));

//  i8212_device &intlatch(I8212(config, "intlatch", 0));
//  intlatch.md_rd_callback().set_constant(GND); // guessed !
//  intlatch.di_rd_callback().set("picu", FUNC(i8214_device::vector_r));
//  intlatch.int_wr_callback().set_inputline("maincpu", I8085_INTR_LINE); // guessed !

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_screen_update("crtc", FUNC(i8275_device::screen_update));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(80*5, 25*8);
	screen.set_visarea(0, 80*5-1, 0, 25*8-1);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_sagitta180);

	I8275(config, m_crtc, 12480000 / 8); /* guessed xtal */
	m_crtc->set_character_width(8);
	m_crtc->set_display_callback(FUNC(sagitta180_state::crtc_display_pixels));
	m_crtc->drq_wr_callback().set(m_dma8257, FUNC(i8257_device::dreq2_w));
	m_crtc->irq_wr_callback().set_inputline(m_maincpu, I8085_INTR_LINE);
	m_crtc->set_screen("screen");

	PALETTE(config, m_palette).set_entries(3);
}


ROM_START( sagitta180 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "sagitta180.u25",   0x0000, 0x0800, CRC(be21a153) SHA1(c1351cda839e0873e8d969e45502530bc26105fd) )
	ROM_LOAD( "180_2763_1.u40",   0x1000, 0x1000, CRC(45839a05) SHA1(875ec4db37bcacdc9c1d62ed7c0757b6e9c45b34) )
	ROM_LOAD( "180_2763_2.u46",   0x2000, 0x1000, CRC(10093151) SHA1(a474207c8f8505e41e4a3c0429fc4a308a282b26) )
	ROM_LOAD( "180_2763_0.u60",   0x3000, 0x1000, CRC(6be85799) SHA1(b1b76740b418d7bd1efd4d405b6fe770797b072a) )

	ROM_REGION( 0x1000, "chargen", 0 ) /* data copied from ibm-pc-jr driver */
	ROM_LOAD("cga.chr",  0x00000, 0x01000, BAD_DUMP CRC(42009069) SHA1(ed08559ce2d7f97f68b9f540bddad5b6295294dd)) // from an unknown clone cga card (Actual IC is a 2708 that I was not able to dump yet)
ROM_END

} // anonymous namespace


//    YEAR    NAME       PARENT  COMPAT  MACHINE     INPUT       CLASS             INIT        COMPANY   FULLNAME       FLAGS */
COMP( 1979?, sagitta180, 0,      0,      sagitta180, sagitta180, sagitta180_state, empty_init, "Scopus", "Sagitta 180", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



scorpion.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic

#include "emu.h"
#include "beta_m.h"
#include "spec128.h"

#include "bus/spectrum/ay/slot.h"
#include "bus/spectrum/zxbus/bus.h"
#include "machine/timer.h"
#include "speaker.h"

#define LOG_IO   (1U << 1)
#define LOG_MEM  (1U << 2)
#define LOG_WARN (1U << 3)

#define VERBOSE ( /*LOG_GENERAL | LOG_IO | LOG_MEM |*/ LOG_WARN )
#include "logmacro.h"

#define LOGIO(...)   LOGMASKED(LOG_IO,   __VA_ARGS__)
#define LOGMEM(...)  LOGMASKED(LOG_MEM,  __VA_ARGS__)
#define LOGWARN(...) LOGMASKED(LOG_WARN, __VA_ARGS__)

namespace {

class scorpion_state : public spectrum_128_state
{
public:
	scorpion_state(const machine_config &mconfig, device_type type, const char *tag)
		: spectrum_128_state(mconfig, type, tag)
		, m_bank0_rom(*this, "bank0_rom")
		, m_io_shadow_view(*this, "io_shadow_view")
		, m_beta(*this, BETA_DISK_TAG)
		, m_ay_slot(*this, "ay_slot")
		, m_io_mouse(*this, "mouse_input%u", 1U)
	{ }

	void scorpion(machine_config &config);
	void profi(machine_config &config);
	void quorum(machine_config &config);

	INPUT_CHANGED_MEMBER(on_nmi);

protected:
	static constexpr u8 ROM_PAGE_SYS = 2;
	static constexpr u8 ROM_PAGE_DOS = 3;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

	bool dos()    const { return m_beta->is_active(); }
	bool romram() const { return BIT(m_port_1ffd_data, 0); }
	bool rom1()   const { return BIT(m_port_7ffd_data, 4); }

	virtual void scorpion_io(address_map &map) ATTR_COLD;
	void scorpion_mem(address_map &map) ATTR_COLD;
	void scorpion_switch(address_map &map) ATTR_COLD;
	u8 port_ff_r();
	void port_7ffd_w(u8 data);
	void port_1ffd_w(u8 data);

	virtual rectangle get_screen_area() override;
	virtual void scorpion_update_memory();
	virtual void do_nmi();
	void update_io(bool dos_enable);

	memory_view m_bank0_rom;
	memory_view m_io_shadow_view;
	required_device<beta_disk_device> m_beta;
	required_device<ay_slot_device> m_ay_slot;

	u8 m_is_m1_even;
	u8 m_nmi_pending;
	u8 m_magic_lock;
	u8 m_ram_banks;

private:
	u8 beta_neutral_r(offs_t offset);
	u8 beta_enable_r(offs_t offset);
	u8 beta_disable_r(offs_t offset);
	INTERRUPT_GEN_MEMBER(scorpion_interrupt);

	required_ioport_array<3> m_io_mouse;
};

class scorpiontb_state : public scorpion_state
{
public:
	scorpiontb_state(const machine_config &mconfig, device_type type, const char *tag)
		: scorpion_state(mconfig, type, tag)
	{ }

	void scorpiontb(machine_config &config);

	INPUT_CHANGED_MEMBER(turbo_changed);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

	virtual void scorpion_io(address_map &map) override ATTR_COLD;
	virtual void ay_address_w(u8 data);

	virtual void scorpion_update_memory() override;

	u8 m_turbo;
	u8 m_prof_plane;

private:
	u8 ay_data_r();

	u8 m_ay_reg;

};

class scorpiongmx_state : public scorpiontb_state
{
public:
	scorpiongmx_state(const machine_config &mconfig, device_type type, const char *tag)
		: scorpiontb_state(mconfig, type, tag)
		, m_io_gmx(*this, "io_gmx")
	{ }

	void scorpiongmx(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

	virtual void scorpion_io(address_map &map) override ATTR_COLD;
	void global_cfg_w(u8 data);
	u8 port_78fd_r();
	u8 port_7afd_r();
	u8 port_7efd_r();
	void port_7efd_w(u8 data);

	virtual void scorpion_update_memory() override;
	virtual void spectrum_update_screen(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) override;
	virtual rectangle get_screen_area() override;
	virtual void do_nmi() override;

	memory_view m_io_gmx;

private:
	bool fixrom() const { return BIT(m_port_00_data, 4); }

	u8 m_magic_shift;
	u8 m_gfx_ext = 0;
	u8 m_magic_disabled;
	u8 m_port_00_data;
	u8 m_port_78fd_data;
	u8 m_port_7afd_lock_data;
	u8 m_scroll_lo;
	u8 m_scroll_hi;
	u8 m_port_7efd_lock_data;
	u8 m_port_dffd_data;

};


/****************************************************************************************************/
/* Zs Scorpion 256 */

/*
port 7ffd. full compatibility with Zx spectrum 128. digits are:

D0-D2 - number of RAM page to put in C000-FFFF
D3    - switch of address for RAM of screen. 0 - 4000, 1 - c000
D4    - switch of ROM : 0-zx128, 1-zx48
D5    - 1 in this bit will block further output in port 7FFD, until reset.
*/

/*
port 1ffd - additional port for resources of computer.

D0    - block of ROM in 0-3fff. when set to 1 - allows read/write page 0 of RAM
D1    - selects ROM expansion. this rom contains main part of service monitor.
D2    - not used
D3    - used for output in RS-232C
D4    - extended RAM. set to 1 - connects RAM page with number 8-15 in
    C000-FFFF. number of page is given in gidits D0-D2 of port 7FFD
D5    - signal of strobe for interface centronics. to form the strobe has to be
    set to 1.
D6-D7 - not used. ( yet ? )
*/

/* rom 0=zx128, 1=zx48, 2 = service monitor, 3=tr-dos */

void scorpion_state::scorpion_update_memory()
{
	if (romram() && !m_nmi_pending)
	{
		m_bank_ram[0]->set_entry(0);
		m_bank0_rom.select(0);
		LOGMEM("mem: RAM0(0)");
	}
	else
	{
		const u8 rom_page = BIT(m_port_1ffd_data, 1)
			? ROM_PAGE_SYS
			: (((m_nmi_pending || dos()) << 1) | rom1());

		m_bank_rom[0]->set_entry(rom_page);
		m_bank0_rom.disable();
		LOGMEM("mem: ROM0(%x)", m_bank_rom[0]->entry());
	}

	m_bank_ram[3]->set_entry(((((m_port_1ffd_data & 0xc0) >> 2) | (m_port_1ffd_data & 0x10) >> 1) | (m_port_7ffd_data & 0x07)) % m_ram_banks);
	LOGMEM(", RAM3(%x)\n", m_bank_ram[3]->entry());
}

INTERRUPT_GEN_MEMBER(scorpion_state::scorpion_interrupt)
{
	m_irq_on_timer->adjust(m_screen->time_until_pos(get_screen_area().top(), get_screen_area().left()) - m_screen->clocks_to_attotime(14344 * 2));
}

INPUT_CHANGED_MEMBER(scorpion_state::on_nmi)
{
	m_nmi_pending = newval;
}

void scorpion_state::do_nmi()
{
	update_io(true);
	m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
	m_nmi_pending = 0;
}

void scorpion_state::update_io(bool dos_enable)
{
	if (dos_enable)
		m_beta->enable();
	else
		m_beta->disable();

	scorpion_update_memory();

	m_io_shadow_view.select(dos() ?  1 : 0);
}

u8 scorpion_state::port_ff_r()
{
	if (m_magic_lock && !machine().side_effects_disabled())
	{
		LOGIO("Registers lock released\n");
		m_magic_lock = 0;
	}
	return scorpion_state::floating_bus_r();
}

void scorpion_state::port_7ffd_w(u8 data)
{
	/* disable paging */
	if (BIT(m_port_7ffd_data, 5))
		return;

	m_port_7ffd_data = data;
	scorpion_update_memory();

	m_screen->update_now();
	m_screen_location = m_ram->pointer() + ((BIT(m_port_7ffd_data, 3) ? 7 : 5) << 14);
}

void scorpion_state::port_1ffd_w(u8 data)
{
	m_port_1ffd_data = data;
	scorpion_update_memory();
}

u8 scorpion_state::beta_neutral_r(offs_t offset)
{
	if (m_is_m1_even && (m_maincpu->total_cycles() & 1)) m_maincpu->eat_cycles(1);

	return m_program.read_byte(offset);
}

u8 scorpion_state::beta_enable_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
	{
		if (m_is_m1_even && (m_maincpu->total_cycles() & 1)) m_maincpu->eat_cycles(1);
		if (!dos() && rom1())
		{
			update_io(true);
		}
	}
	return m_program.read_byte(offset + 0x3d00);
}

u8 scorpion_state::beta_disable_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
	{
		if (m_is_m1_even && (m_maincpu->total_cycles() & 1)) m_maincpu->eat_cycles(1);
		if (m_nmi_pending)
		{
			do_nmi();
		}
		else if (dos())
		{
			update_io(false);
		}
	}
	return m_program.read_byte(offset + 0x4000);
}

void scorpion_state::scorpion_mem(address_map &map)
{
	map(0x0000, 0x3fff).bankr(m_bank_rom[0]);
	map(0x0000, 0x3fff).view(m_bank0_rom);
	m_bank0_rom[0](0x0000, 0x3fff).bankrw(m_bank_ram[0]);

	map(0x4000, 0x7fff).bankr(m_bank_ram[1]).w(FUNC(scorpion_state::spectrum_128_ram_w<1>));
	map(0x8000, 0xbfff).bankr(m_bank_ram[2]).w(FUNC(scorpion_state::spectrum_128_ram_w<2>));
	map(0xc000, 0xffff).bankr(m_bank_ram[3]).w(FUNC(scorpion_state::spectrum_128_ram_w<3>));
}

void scorpion_state::scorpion_io(address_map &map)
{
	map.unmap_value_high();

	map(0x0021, 0x0021).mirror(0x3fdc) // 1FFD | 00xxxxxxxx1xxx01
		.w(FUNC(scorpion_state::port_1ffd_w));
	map(0x4021, 0x4021).mirror(0x3fdc) // 7FFD | 01xxxxxxxx1xxx01
		.w(FUNC(scorpion_state::port_7ffd_w));

	map(0xa021, 0xa021).mirror(0x1fdc) // BFFD | 101xxxxxxx1xxx01
		.w(m_ay_slot, FUNC(ay_slot_device::data_w));
	map(0xe021, 0xe021).mirror(0x1fdc) // FFFD | 111xxxxxxx1xxx01
		.rw(m_ay_slot, FUNC(ay_slot_device::data_r), FUNC(ay_slot_device::address_w));

	map(0x0000, 0xffff).view(m_io_shadow_view);

	// !Shadow
	m_io_shadow_view[0](0x0022, 0x0022).select(0xffdc) // FE | xxxxxxxxxx1xxx10
		.rw(FUNC(scorpion_state::spectrum_ula_r), FUNC(scorpion_state::spectrum_ula_w));
	m_io_shadow_view[0](0x0023, 0x0023).mirror(0xffdc) // FF | xxxxxxxxxx1xxx11
		.r(FUNC(scorpion_state::port_ff_r));
	m_io_shadow_view[0](0xfadf, 0xfadf).lr8(NAME([this]() -> u8 { return 0x80 | (m_io_mouse[2]->read() & 0x07); }));
	m_io_shadow_view[0](0xfbdf, 0xfbdf).lr8(NAME([this]() -> u8 { return m_io_mouse[0]->read(); }));
	m_io_shadow_view[0](0xffdf, 0xffdf).lr8(NAME([this]() -> u8 { return ~m_io_mouse[1]->read(); }));
	m_io_shadow_view[0](0x0003, 0x0003) // 1F | xxxxxxxx0x0xxx11
		.select(0xff5c).lr8(NAME([this]() -> u8 { return (m_beta->state_r() & 0xc0) | 0x00; })); // TODO Kepmston Joystick

	// Shadow
	// DOS + xxxxxxxx0nnxxx11
	m_io_shadow_view[1](0x0003, 0x0003).mirror(0xff1c).rw(m_beta, FUNC(beta_disk_device::status_r), FUNC(beta_disk_device::command_w));
	m_io_shadow_view[1](0x0023, 0x0023).mirror(0xff1c).rw(m_beta, FUNC(beta_disk_device::track_r), FUNC(beta_disk_device::track_w));
	m_io_shadow_view[1](0x0043, 0x0043).mirror(0xff1c).rw(m_beta, FUNC(beta_disk_device::sector_r), FUNC(beta_disk_device::sector_w));
	m_io_shadow_view[1](0x0063, 0x0063).mirror(0xff1c).rw(m_beta, FUNC(beta_disk_device::data_r), FUNC(beta_disk_device::data_w));
	m_io_shadow_view[1](0x00e3, 0x00e3).mirror(0xff1c).rw(m_beta, FUNC(beta_disk_device::state_r), FUNC(beta_disk_device::param_w));

	subdevice<zxbus_device>("zxbus")->set_io_space(m_io_shadow_view[0], m_io_shadow_view[1]);
}

void scorpion_state::scorpion_switch(address_map &map)
{
	map(0x0000, 0x3fff).r(FUNC(scorpion_state::beta_neutral_r)); // Overlap with previous because we want real addresses on the 3e00-3fff range
	map(0x3d00, 0x3dff).r(FUNC(scorpion_state::beta_enable_r));
	map(0x4000, 0xffff).r(FUNC(scorpion_state::beta_disable_r));
}

void scorpion_state::machine_start()
{
	spectrum_128_state::machine_start();

	save_item(NAME(m_port_1ffd_data));
	save_item(NAME(m_nmi_pending));
	save_item(NAME(m_magic_lock));
	save_item(NAME(m_ram_banks));

	// reconfigure ROMs
	memory_region *rom = memregion("maincpu");
	m_bank_rom[0]->configure_entries(0, rom->bytes() / 0x4000, rom->base() + 0x10000, 0x4000);
	m_ram_banks = m_ram->size() / 0x4000;
	m_bank_ram[0]->configure_entries(0, m_ram_banks, m_ram->pointer(), 0x4000);
	m_bank_ram[2]->configure_entries(0, m_ram_banks, m_ram->pointer(), 0x4000);
}

void scorpion_state::machine_reset()
{
	m_is_m1_even = 1;
	m_nmi_pending = 0;
	m_magic_lock = 0;

	m_port_fe_data = 255;
	m_port_7ffd_data = 0;
	m_port_1ffd_data = 0;

	m_bank_ram[2]->set_entry(2);

	update_io(false);
}

void scorpion_state::video_start()
{
	spectrum_state::video_start();
	m_screen_location = m_ram->pointer() + (5 << 14);
}

/* F4 Character Displayer */
static const gfx_layout spectrum_charlayout =
{
	8, 8,          /* 8 x 8 characters */
	96,            /* 96 characters */
	1,             /* 1 bits per pixel */
	{ 0 },         /* no bitplanes */
	{STEP8(0, 1)}, /* x offsets */
	{STEP8(0, 8)}, /* y offsets */
	8*8            /* every char takes 8 bytes */
};

static const gfx_layout quorum_charlayout =
{
	8, 8,          /* 8 x 8 characters */
	160,           /* 160 characters */
	1,             /* 1 bits per pixel */
	{ 0 },         /* no bitplanes */
	{STEP8(0, 1)}, /* x offsets */
	{STEP8(0, 8)}, /* y offsets */
	8*8            /* every char takes 8 bytes */
};

static const gfx_layout profi_8_charlayout =
{
	8, 8,          /* 8 x 8 characters */
	224,           /* 224 characters */
	1,             /* 1 bits per pixel */
	{ 0 },         /* no bitplanes */
	{STEP8(0, 1)}, /* x offsets */
	{STEP8(0, 8)}, /* y offsets */
	8*8            /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_scorpion )
	GFXDECODE_ENTRY( "maincpu", 0x17d00, spectrum_charlayout, 7, 8 )
GFXDECODE_END

static GFXDECODE_START( gfx_profi )
	GFXDECODE_ENTRY( "maincpu", 0x17d00, spectrum_charlayout, 7, 8 )
	GFXDECODE_ENTRY( "maincpu", 0x1abfc, profi_8_charlayout, 7, 8 )
	/* There are more characters after this, that haven't been decoded */
GFXDECODE_END

static GFXDECODE_START( gfx_quorum )
	GFXDECODE_ENTRY( "maincpu", 0x1fb00, quorum_charlayout, 7, 8 )
GFXDECODE_END

rectangle scorpion_state::get_screen_area()
{
	return spectrum_state::get_screen_area();
}

INPUT_PORTS_START( scorpion )
	PORT_INCLUDE( spec_plus )

	PORT_MODIFY("NMI")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("NMI") PORT_CODE(KEYCODE_F12) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(scorpion_state::on_nmi), 0)


	PORT_START("mouse_input1")
	PORT_BIT(0xff, 0, IPT_MOUSE_X) PORT_SENSITIVITY(30)

	PORT_START("mouse_input2")
	PORT_BIT(0xff, 0, IPT_MOUSE_Y) PORT_SENSITIVITY(30)

	PORT_START("mouse_input3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON4) PORT_NAME("Left mouse button") PORT_CODE(MOUSECODE_BUTTON1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON5) PORT_NAME("Right mouse button") PORT_CODE(MOUSECODE_BUTTON2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON6) PORT_NAME("Middle mouse button") PORT_CODE(MOUSECODE_BUTTON3)
INPUT_PORTS_END

void scorpion_state::scorpion(machine_config &config)
{
	// Yellow PCB
	spectrum(config);
	m_ram->set_default_size("256K");

	m_maincpu->set_memory_map(&scorpion_state::scorpion_mem);
	m_maincpu->set_m1_map(&scorpion_state::scorpion_switch);
	m_maincpu->set_io_map(&scorpion_state::scorpion_io);
	m_maincpu->set_vblank_int("screen", FUNC(scorpion_state::scorpion_interrupt));
	m_maincpu->refresh_cb().remove();
	m_maincpu->nomreq_cb().remove();

	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_scorpion);
	SPECTRUM_ULA_UNCONTENDED(config.replace(), m_ula);

	SPEAKER(config.replace(), "speakers", 2).front();

	AY_SLOT(config, "ay_slot", 14_MHz_XTAL / 8, default_ay_slot_devices, "ay_ay8912") // BAC
		.add_route(1, "speakers", 0.50, 0)
		.add_route(0, "speakers", 0.25, 0)
		.add_route(0, "speakers", 0.25, 1)
		.add_route(2, "speakers", 0.50, 1);

	BETA_DISK(config, m_beta, 0);

	config.device_remove("exp");

	zxbus_device &zxbus(ZXBUS(config, "zxbus", 0));
	ZXBUS_SLOT(config, "zxbus:1", 0, zxbus, zxbus_gmx_cards, nullptr);
	ZXBUS_SLOT(config, "zxbus:2", 0, zxbus, zxbus_gmx_cards, nullptr);
}

void scorpion_state::profi(machine_config &config)
{
	scorpion(config);
	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_profi);
}

void scorpion_state::quorum(machine_config &config)
{
	scorpion(config);
	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_quorum);
}


/******************************************************************************
 * Scorpion ZS-256 Turbo+
 * ***************************************************************************/
void scorpiontb_state::ay_address_w(u8 data)
{
	m_ay_reg = data;
	m_ay_slot->address_w(data);
}

u8 scorpiontb_state::ay_data_r()
{
	return (m_ay_reg == 0x0e)
		? (((m_port_7ffd_data & 0x10) << 1) | (m_port_1ffd_data & 0x10) | (m_port_7ffd_data & 0x0f))
		: m_ay_slot->data_r();
}

INPUT_CHANGED_MEMBER(scorpiontb_state::turbo_changed)
{
	if (newval == 1)
	{
		m_turbo = !m_turbo;
		m_maincpu->set_clock_scale(1 << m_turbo);
		//popmessage("Turbo %s\n", m_turbo ? "ON" : "OFF");
	}
}

INPUT_PORTS_START( scorpiontb )
	PORT_INCLUDE( scorpion )

	PORT_START("TURBO")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TURBO") PORT_CODE(KEYCODE_F11) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(scorpiontb_state::turbo_changed), 0)
INPUT_PORTS_END

void scorpiontb_state::scorpiontb(machine_config &config)
{
	scorpion(config);

	rectangle visarea = { get_screen_area().left() - SPEC_LEFT_BORDER, get_screen_area().right() + SPEC_RIGHT_BORDER,
		get_screen_area().top() - SPEC_TOP_BORDER, get_screen_area().bottom() + SPEC_BOTTOM_BORDER };
	m_screen->set_raw(X1 / 2, SPEC_CYCLES_PER_LINE * 2, SPEC_UNSEEN_LINES + SPEC_SCREEN_HEIGHT + 4, visarea);

	m_ram->set_default_size("256K").set_extra_options("1M");
}

void scorpiontb_state::scorpion_update_memory()
{
	LOGMEM("romplane %x, ", m_prof_plane);
	scorpion_state::scorpion_update_memory();
	m_bank_rom[0]->set_entry((m_prof_plane << 2) | m_bank_rom[0]->entry());
}

void scorpiontb_state::machine_start()
{
	scorpion_state::machine_start();

	save_item(NAME(m_prof_plane));
	save_item(NAME(m_ay_reg));
	save_item(NAME(m_turbo));
}

void scorpiontb_state::machine_reset()
{
	m_prof_plane = 0;
	m_ay_reg = -1;

	scorpion_state::machine_reset();
	m_is_m1_even = 0;
	m_turbo = 0;
	m_maincpu->set_clock_scale(1);
}

static const u8 prof_plane_map[] =
{
	0, 1, 2, 3,
	3, 3, 3, 2,
	2, 2, 0, 1,
	1, 0, 1, 0,
};

void scorpiontb_state::video_start()
{
	scorpion_state::video_start();

	address_space &prg = m_maincpu->space(AS_PROGRAM);
	prg.install_read_tap(0x0100, 0x010c, "plane_switch", [this](offs_t offset, u8 &data, u8 mem_mask)
	{
		if (!machine().side_effects_disabled() && !romram() && ((m_bank_rom[0]->entry() & 3) == ROM_PAGE_SYS))
		{
			const u8 offs = offset & 0x000f;
			if ((offs == 0) || (offs == 4) || (offs == 8) || (offs == 0x0c))
			{
				u8 plane = prof_plane_map[(offset & 0x0c) | (m_prof_plane & 0x03)];
				if (m_prof_plane != plane)
				{
					m_prof_plane = plane;
					scorpion_update_memory();
				}
			}
		}
	});
}

void scorpiontb_state::scorpion_io(address_map &map)
{
	scorpion_state::scorpion_io(map);
	map(0x0021, 0x0021).mirror(0x3fdc) // 1FFD | 00xxxxxxxx1xxx01
		.lr8(NAME([this](offs_t offset) -> u8 { m_turbo = 0; m_maincpu->set_clock_scale(1); return 0xff; }));
	map(0x4021, 0x4021).mirror(0x3fdc) // 7FFD | 01xxxxxxxx1xxx01
		.lr8(NAME([this](offs_t offset) -> u8 { m_turbo = 1; m_maincpu->set_clock_scale(2); return 0xff; }));
	map(0xe021, 0xe021).mirror(0x1fdc) // FFFD | 111xxxxxxx1xxx01
		.rw(FUNC(scorpiontb_state::ay_data_r),  FUNC(scorpiontb_state::ay_address_w));

	// Centronics
	map(0x0001, 0x0001).mirror(0xffdc).nopw();
}


/******************************************************************************
 * Scorpion GMX
 * ***************************************************************************/
void scorpiongmx_state::scorpion_update_memory()
{
	scorpiontb_state::scorpion_update_memory();
	if (BIT(m_port_1ffd_data, 2))
	{
		m_bank_rom[0]->set_entry((m_prof_plane << 2) | ROM_PAGE_DOS);
		m_bank0_rom.disable();
		m_beta->enable();
		LOGMEM("... ROM0(%x)", m_bank_rom[0]->entry());
	}
	m_bank_ram[2]->set_entry((m_port_78fd_data ^ 2) % m_ram_banks);
	LOGMEM("... RAM2(%x)", m_bank_ram[2]->entry());
	m_bank_ram[3]->set_entry((((m_port_dffd_data & 7) << 4) | ((m_port_1ffd_data & 0x10) >> 1) | (m_port_7ffd_data & 0x07)) % m_ram_banks);
	LOGMEM(", RAM3(%x)\n", m_bank_ram[3]->entry());
}

void scorpiongmx_state::spectrum_update_screen(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	if (!m_gfx_ext)
		return scorpiontb_state::spectrum_update_screen(screen, bitmap, cliprect);

	// 640x200 ...
	spectrum_update_border(screen, bitmap, cliprect);

	const bool invert_attrs = u64(screen.frame_number() / m_frame_invert_count) & 1;
	const u8 *screen_location = m_ram->pointer() + ((BIT(m_port_7ffd_data, 3) ? 0x3b : 0x39) << 14);
	for (u16 vpos = cliprect.top(); vpos <= cliprect.bottom(); vpos++)
	{
		const u16 y = (vpos - get_screen_area().top() + (((m_scroll_hi << 8) | m_scroll_lo) / 80)) % (25 * 8);
		for (u16 hpos = cliprect.left() & 0xfff8; hpos <= cliprect.right();)
		{
			const u16 x = hpos - get_screen_area().left();
			const u8 *scr = screen_location + (y * 80) + (x >> 3);

			const u8 attr = *(scr + (0x40 << 14));
			const u16 fg = ((attr >> 3) & 0x08) | (attr & 0x07);
			const u16 bg = (attr >> 3) & 0x0f;
			const bool invert = (attr & 0x80) && invert_attrs;

			const u8 chunk = *scr;
			for (u8 i = 0x80; i; i >>= 1)
				bitmap.pix(vpos, hpos++) = (chunk & i)
					? (invert ? bg : fg)
					: (invert ? fg : bg);
		}
	}
}

rectangle scorpiongmx_state::get_screen_area()
{
	return m_gfx_ext
		? rectangle { 80, 80 + 639, 80, 80 + 199 }
		: scorpiontb_state::get_screen_area();
}

void scorpiongmx_state::do_nmi()
{
	if (m_magic_disabled)
	{
		LOGIO("NMI ignored as Magic disabled");
		m_nmi_pending = 0;
	}
	else
	{
		m_port_7afd_lock_data = port_7afd_r() & 0x7f;
		m_port_7efd_lock_data = port_7efd_r() & 0x4f;
		LOGIO("NMI: registers locked 7afd=%02x, 7efd=%02x\n", m_port_7afd_lock_data, m_port_7efd_lock_data);
		scorpion_state::do_nmi();
		m_magic_lock = 1;
	}
}

void scorpiongmx_state::global_cfg_w(u8 data)
{
	m_port_00_data = data;
	if (BIT(data, 5)) // BLKEXT
	{
		LOGIO("GMX ports disabled\n");
		m_io_gmx.disable();
	}
	else
		m_io_gmx.select(0);

	if (BIT(data, 3))
	{
		m_magic_shift = 0x88 | (data & 7);
		if (!fixrom())
			m_maincpu->reset();
	}
}

u8 scorpiongmx_state::port_78fd_r()
{
	u8 tmp = (BIT(m_port_fe_data, 1) << 7) // BRD1
		| (m_port_78fd_data & 0x7f);
	tmp |= (m_magic_shift & 1);

	if(!machine().side_effects_disabled())
		m_magic_shift >>= 1;

	return tmp;
}

u8 scorpiongmx_state::port_7afd_r()
{
	u8 data;
	if (m_magic_lock)
		data = m_port_7afd_lock_data;
	else
		data = (BIT(m_port_dffd_data, 0, 3) << 4) | (BIT(m_port_1ffd_data, 4) << 3) | BIT(m_port_7ffd_data, 0, 3);

	data |= BIT(m_port_fe_data, 0) << 7; // BRD0

	return data;
}

u8 scorpiongmx_state::port_7efd_r()
{
	u8 data;
	if (m_magic_lock)
		data = m_port_7efd_lock_data;
	else
		data = (BIT(m_port_1ffd_data, 0) << 6)
			| (m_gfx_ext << 3)
			| (m_turbo << 2)
			| (BIT(m_port_7ffd_data, 3) << 1)
			| BIT(m_port_7ffd_data, 5);

	data |= BIT(m_port_fe_data, 2) << 7 // BRD2
		| (BIT(m_port_00_data, 5) << 5)
		| (BIT(m_port_00_data, 7) << 4);

	return data;
}

void scorpiongmx_state::port_7efd_w(u8 data)
{
	m_turbo = BIT(data, 7);
	m_maincpu->set_clock_scale(1 << m_turbo);

	if (!fixrom())
	{
		m_prof_plane = BIT(data, 4, 3); // D4..6 - A16..18: 28F400
		scorpion_update_memory();
	}

	m_gfx_ext = BIT(data, 3);
	const rectangle scr = get_screen_area();
	const rectangle broder = m_gfx_ext
		? rectangle {24, 24, 40, 40}
		: rectangle {SPEC_LEFT_BORDER, SPEC_RIGHT_BORDER, SPEC_TOP_BORDER, SPEC_BOTTOM_BORDER};
	m_screen->configure((SPEC_CYCLES_PER_LINE * 2) << m_gfx_ext, m_screen->height()
		, {scr.left() - broder.left(), scr.right() + broder.right()
		, scr.top() - broder.top(), scr.bottom() + broder.bottom()}
		, m_screen->frame_period().as_attoseconds());

	m_magic_disabled = BIT(data, 2);

	// D1 - Vpp: 28F400 PowerOn
	// D0 - EWR: 28F400 W
}

void scorpiongmx_state::scorpiongmx(machine_config &config)
{
	scorpiontb(config);
	m_ram->set_default_size("2M");
}

void scorpiongmx_state::machine_start()
{
	scorpiontb_state::machine_start();

	save_item(NAME(m_gfx_ext));
	save_item(NAME(m_magic_disabled));
	save_item(NAME(m_port_00_data));
	save_item(NAME(m_port_78fd_data));
	save_item(NAME(m_port_7afd_lock_data));
	save_item(NAME(m_scroll_lo));
	save_item(NAME(m_scroll_hi));
	save_item(NAME(m_port_7efd_lock_data));
	save_item(NAME(m_port_dffd_data));

	m_port_00_data = 0;
}

void scorpiongmx_state::machine_reset()
{
	m_io_gmx.select(0);

	m_magic_shift = 0;
	m_gfx_ext = 0;
	m_magic_disabled = 0;
	m_port_78fd_data = 0;
	m_port_7afd_lock_data = 0;
	m_scroll_lo = 0;
	m_scroll_hi = 0;
	m_port_7efd_lock_data = 0;
	m_port_dffd_data = 0;

	scorpiontb_state::machine_reset();
}

void scorpiongmx_state::video_start()
{
	scorpion_state::video_start();
}

void scorpiongmx_state::scorpion_io(address_map &map)
{
	scorpiontb_state::scorpion_io(map);
	map(0x0000, 0x0000).mirror(0xff00).w(FUNC(scorpiongmx_state::global_cfg_w));

	map(0x0000, 0xffff).view(m_io_gmx);
	m_io_gmx[0](0x78fd, 0x78fd).mirror(0x0000).r(FUNC(scorpiongmx_state::port_78fd_r))
		.lw8(NAME([this](u8 data) { m_port_78fd_data = data & 0x7f; scorpion_update_memory(); }));
	m_io_gmx[0](0x7afd, 0x7afd).mirror(0x0000).r(FUNC(scorpiongmx_state::port_7afd_r))
		.lw8(NAME([this](u8 data) { m_scroll_lo = data & 0xf0; }));
	m_io_gmx[0](0x7cfd, 0x7cfd).mirror(0x0000)
		.lw8(NAME([this](u8 data) { m_scroll_hi = data & 0x3f; }));
	m_io_gmx[0](0x7efd, 0x7efd).mirror(0x0000).rw(FUNC(scorpiongmx_state::port_7efd_r), FUNC(scorpiongmx_state::port_7efd_w));
	m_io_gmx[0](0xdffd, 0xdffd).mirror(0x0000)
		.lw8(NAME([this](u8 data) { m_port_dffd_data = data & 0x07; scorpion_update_memory(); }));
}


/***************************************************************************

  Game driver(s)

***************************************************************************/

ROM_START(scorpio)
	ROM_REGION(0x90000, "maincpu", 0)
	ROM_DEFAULT_BIOS("v3")

	ROM_SYSTEM_BIOS(0, "v1", "V.2.92")
	ROMX_LOAD("scorp0.rom", 0x010000, 0x4000, CRC(0eb40a09) SHA1(477114ff0fe1388e0979df1423602b21248164e5), ROM_BIOS(0))
	ROMX_LOAD("scorp1.rom", 0x014000, 0x4000, CRC(9d513013) SHA1(367b5a102fb663beee8e7930b8c4acc219c1f7b3), ROM_BIOS(0))
	ROMX_LOAD("scorp2.rom", 0x018000, 0x4000, CRC(fd0d3ce1) SHA1(07783ee295274d8ff15d935bfd787c8ac1d54900), ROM_BIOS(0))
	ROMX_LOAD("scorp3.rom", 0x01c000, 0x4000, CRC(1fe1d003) SHA1(33703e97cc93b7edfcc0334b64233cf81b7930db), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v2", "V.2.92 joined")
	ROMX_LOAD( "scorpion.rom", 0x010000, 0x10000, CRC(fef73c28) SHA1(66cecdadf992d8adb9c66deee929eb56600dc9bc), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v3", "V.2.94")
	ROMX_LOAD( "scorp294.rom", 0x010000, 0x10000, CRC(99f57ce1) SHA1(083bb57ad52cc871b92d3e1794fd9790872c3584), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v4", "NeOS 256")
	ROMX_LOAD( "neos_256.rom", 0x010000, 0x4000, CRC(364ae09a) SHA1(bb6db1947415503a6bc48f33c603fb3a0dbb3690), ROM_BIOS(3))
	ROMX_LOAD( "scorp1.rom",   0x014000, 0x4000, CRC(9d513013) SHA1(367b5a102fb663beee8e7930b8c4acc219c1f7b3), ROM_BIOS(3))
	ROMX_LOAD( "scorp2.rom",   0x018000, 0x4000, CRC(fd0d3ce1) SHA1(07783ee295274d8ff15d935bfd787c8ac1d54900), ROM_BIOS(3))
	ROMX_LOAD( "scorp3.rom",   0x01c000, 0x4000, CRC(1fe1d003) SHA1(33703e97cc93b7edfcc0334b64233cf81b7930db), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "scorp_test", "Scorpion Test")
	ROMX_LOAD( "scorp_test.rom", 0x010000, 0x10000, CRC(e0230ca7) SHA1(f38e4d23cb29b4ae3fe8b00a52d7b0f9bb845407), ROM_BIOS(4))

	// This is a ROM for Scorpion emulator which modified in order to hide real hardware details
	//ROMX_LOAD( "scorp402.rom", 0x010000, 0x20000, CRC(9fcf893d) SHA1(0cc7ba60f5cfc36e75bd3a5c90e26b2a1905a970))

	ROM_REGION(0x01000, "keyboard", 0)
	ROM_LOAD( "scrpkey.rom", 0x0000, 0x1000, CRC(e938a510) SHA1(2753993c97ff0fc6cff26ed792929abc1288dc6f))

	ROM_REGION(0x010000, "gsound", 0) // TODO GS connected to ZXBUS
	ROM_LOAD( "gs104.rom", 0x0000, 0x8000, CRC(7a365ba6) SHA1(c865121306eb3a7d811d82fbcc653b4dc1d6fa3d))
	ROM_LOAD( "gs105a.rom", 0x8000, 0x8000, CRC(1cd490c6) SHA1(1ba78923dc7d803e7995c165992e14e4608c2153))
ROM_END

ROM_START(scorpiontb)
	ROM_REGION(0x90000, "maincpu", 0)
	ROM_DEFAULT_BIOS("v4.43su")

	ROM_SYSTEM_BIOS(0, "v3.9f", "ProfROM V.3.9f")
	ROMX_LOAD( "prof_39f.rom", 0x010000, 0x20000, CRC(c55e64da) SHA1(cec7770fe26350f57f6c325a29db78787dc4521e), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v4.01_01", "ProfROM V.4.01 (No ROM disk)")
	ROMX_LOAD( "scorp401_0358765b.rom", 0x010000, 0x40000, CRC(0358765b) SHA1(414c21a4d01fafb6ee1752a51b79bb2f82091625), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v4.01_02", "ProfROM V.4.01 (ZxUnZip, ZxZip, TASM 4.0, SM 2.01, HD Copy 1.7, Test FDD)")
	ROMX_LOAD( "scorp401_520f4c15.rom", 0x010000, 0x40000, CRC(520f4c15) SHA1(8a877b06e69e829b99e06be0ba48bc0fe6458672), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v4.01_03", "ProfROM V.4.01 (Fatall 0.25, SCBoot 1.5, MagOS 6.3)")
	ROMX_LOAD( "scorp401_749516c5.rom", 0x010000, 0x40000, CRC(749516c5) SHA1(712535c4eec26224f39650e4740db131b2d0ed14), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "v4.01_04", "ProfROM V.4.01 (Fatall 0.2)")
	ROMX_LOAD( "scorp401_809c249d.rom", 0x010000, 0x40000, CRC(809c249d) SHA1(1e7ea10aeadb33a7d363809ccf31d43a0a8670ac), ROM_BIOS(4))
	ROM_SYSTEM_BIOS(5, "v4.01_05", "ProfROM V.4.01 (ZxUnZip, ZxZip, TASM 4.0, SM 2.01, HD Copy 1.7, TestFDD)")
	ROMX_LOAD( "scorp401_847a66e4.rom", 0x010000, 0x40000, CRC(847a66e4) SHA1(a01522cadf18c087f32c1997c5c2e889290921fa), ROM_BIOS(5))
	ROM_SYSTEM_BIOS(6, "v4.01_06", "ProfROM V.4.01 (MagOS 6.3, Real Comm 2.6F, TRDNavig 0.75b, ZXunzip 1.2x, Cat HDD, Test ...)")
	ROMX_LOAD( "scorp401_91f513ab.rom", 0x010000, 0x40000, CRC(91f513ab) SHA1(e2b8941591e77c1d35b811a6724b31ff3486d42d), ROM_BIOS(6))
	ROM_SYSTEM_BIOS(7, "v4.01_07", "ProfROM V.4.01 (Commander, ZWord, MAGOS 6.3, STS 5.1, ADS 2.01, FPM 3.05, Ztool)")
	ROMX_LOAD( "scorp401_98275049.rom", 0x010000, 0x40000, CRC(98275049) SHA1(a7518915293a82b70b2fd5261ef886c4dd33fa9b), ROM_BIOS(7))
	ROM_SYSTEM_BIOS(8, "v4.01_08", "ProfROM V.4.01 (Fatall 0.25, Test Scorpion, TASM 4.0, SCBoot, Chess Psion, Tetris, Chuckie Egg, Bomber)")
	ROMX_LOAD( "scorp401_a3b10c26.rom", 0x010000, 0x40000, CRC(a3b10c26) SHA1(945a671183402eee99f852eab93e28a93a81b575), ROM_BIOS(8))
	ROM_SYSTEM_BIOS(9, "v4.01_09", "ProfROM V.4.01 (Fatall 0.25, SCBoot 1.5, MagOS 6.3, Real Command 2.6)")
	ROMX_LOAD( "scorp401_e5476776.rom", 0x010000, 0x40000, CRC(e5476776) SHA1(3dcff254f5bbc1e6c6a5319d21ebbd5c7d65077c), ROM_BIOS(9))
	ROM_SYSTEM_BIOS(10, "v4.01_10", "ProfROM V.4.01 (Fatall 0.25, SCBoot 1.5, Wild Player 3.3, Best View 2.19)")
	ROMX_LOAD( "scorp401_e8900ba7.rom", 0x010000, 0x40000, CRC(e8900ba7) SHA1(5caa403036cc2268db2c35077902e3f432f3c779), ROM_BIOS(10))
	ROM_SYSTEM_BIOS(11, "v4.15", "ProfROM V.4.xx.015")
	ROMX_LOAD( "prof4xx015.rom", 0x010000, 0x40000, CRC(5d4ba991) SHA1(2d1f0bd95909cdff32a96ab55b4bcae547e20bd1), ROM_BIOS(11))
	ROM_SYSTEM_BIOS(12, "v4.15lg", "ProfROM V.4.xx.015 (Basic Looking Glass)")
	ROMX_LOAD( "prof4xx015lg.rom", 0x010000, 0x40000, CRC(3b450ceb) SHA1(e1af7110ad89764ac020c636ab835c39ada061c0), ROM_BIOS(12))
	ROM_SYSTEM_BIOS(13, "v4.27", "ProfROM V.4.xx.027")
	ROMX_LOAD( "prof4xx027.rom", 0x010000, 0x40000, CRC(8da39bed) SHA1(39a601d8af62df95efd5a514b53a4a07571befab), ROM_BIOS(13))
	ROM_SYSTEM_BIOS(14, "v4.27lg", "ProfROM V.4.xx.027 (Basic Looking Glass)")
	ROMX_LOAD( "prof4xx027lg.rom", 0x010000, 0x40000, CRC(50be8b57) SHA1(fa930ec7efe8eee641c889a0ce2ae16f03ebc1f4), ROM_BIOS(14))
	ROM_SYSTEM_BIOS(15, "v4.29", "ProfROM V.4.xx.029")
	ROMX_LOAD( "prof4xx029.rom", 0x010000, 0x40000, CRC(aebe12a3) SHA1(31c5481587554a8173016ccda46e1490cbd9fa5c), ROM_BIOS(15))
	ROM_SYSTEM_BIOS(16, "v4.29lg", "ProfROM V.4.xx.029 (Basic Looking Glass)")
	ROMX_LOAD( "prof4xx029lg.rom", 0x010000, 0x40000, CRC(1ae71a4c) SHA1(c7d8f20134f5623f2498feea5c9efbcb2fd686a3), ROM_BIOS(16))
	ROM_SYSTEM_BIOS(17, "v4.31", "ProfROM V.4.xx.031")
	ROMX_LOAD( "prof4xx031.rom", 0x010000, 0x40000, CRC(43ab9b83) SHA1(cc95edf10ee6bfd16cf738ea648cf4ce35b3b232), ROM_BIOS(17))
	ROM_SYSTEM_BIOS(18, "v4.31lg", "ProfROM V.4.xx.031 (Basic Looking Glass)")
	ROMX_LOAD( "prof4xx031lg.rom", 0x010000, 0x40000, CRC(f7f2936c) SHA1(61f8ca193624c7ed23d54f35cbebb76bc94e96e2), ROM_BIOS(18))
	ROM_SYSTEM_BIOS(19, "v4.31_3d2f", "ProfROM V.4.xx.031(3D2F)")
	ROMX_LOAD( "prof4xx031_3d2f.rom", 0x010000, 0x40000, CRC(e850e0d1) SHA1(48d41013130a5d92c2b2fd04142d5ba6ce5a324a), ROM_BIOS(19))
	ROM_SYSTEM_BIOS(20, "v4.31lg_3d2f", "ProfROM V.4.xx.031 (Basic Looking Glass) (3D2F)")
	ROMX_LOAD( "prof4xx031lg_3d2f.rom", 0x010000, 0x40000, CRC(cc525181) SHA1(985b437e8b35c0c3493a347e76f683e498bd40e4), ROM_BIOS(20))
	ROM_SYSTEM_BIOS(21, "v4.41", "ProfROM V.4.xx.041.8689")
	ROMX_LOAD( "prof4xx041.8689.rom", 0x010000, 0x40000, CRC(35f11072) SHA1(25f8dcd04619e44829e3ba4cb360f2ae9bf8bf01), ROM_BIOS(21))
	ROM_SYSTEM_BIOS(22, "v4.41_3d2f", "ProfROM V.4.xx.041.8689 (3D2F)")
	ROMX_LOAD( "prof4xx041.8689_3d2f.rom", 0x010000, 0x40000, CRC(25c5885a) SHA1(972871f1b19bc2980ec22220d81994223bf6e884), ROM_BIOS(22))
	ROM_SYSTEM_BIOS(23, "v4.41_uni", "ProfROM V.4.xx.041.8689 (UNI)")
	ROMX_LOAD( "prof4xx041.8689_uni.rom", 0x010000, 0x40000, CRC(3914e0aa) SHA1(2a951aa49ccdecf38adc8c901296b352264d621f), ROM_BIOS(23))
	ROM_SYSTEM_BIOS(24, "v4.41lg", "ProfROM V.4.xx.041.8689 (Basic Looking Glass)")
	ROMX_LOAD( "prof4xx041.8689lg.rom", 0x010000, 0x40000, CRC(81a8189d) SHA1(3991c4bf9ab7d184a86f413753f954e43f7337fa), ROM_BIOS(24))
	ROM_SYSTEM_BIOS(25, "v4.41lg_3d2f", "ProfROM V.4.xx.041.8689 (Basic Looking Glass) (3D2F)")
	ROMX_LOAD( "prof4xx041.8689lg_3d2f.rom", 0x010000, 0x40000, CRC(01c7390a) SHA1(84d4518d751979722fde834c568fa652058e55fa), ROM_BIOS(25))
	ROM_SYSTEM_BIOS(26, "v4.41lg_uni", "ProfROM V.4.xx.041.8689 (Basic Looking Glass) (UNI)")
	ROMX_LOAD( "prof4xx041.8689lg_uni.rom", 0x010000, 0x40000, CRC(62df1e38) SHA1(6df006eb8429464c54097f10b6ef2731c52f91b9), ROM_BIOS(26))
	ROM_SYSTEM_BIOS(27, "v4.43su", "ProfROM V.4.xx.043.9226su (1024) (1FFD)")
	ROMX_LOAD( "prof4xx043.9226su_1024_1ffd.rom", 0x010000, 0x40000, CRC(2d13296d) SHA1(02b94e78f0c058ca5762d67401946892df43b49c), ROM_BIOS(27))
	ROM_SYSTEM_BIOS(28, "scorp_test", "Scorpion Test")
	ROMX_LOAD( "scorp_test.rom", 0x010000, 0x10000, CRC(e0230ca7) SHA1(f38e4d23cb29b4ae3fe8b00a52d7b0f9bb845407), ROM_BIOS(28))
ROM_END

ROM_START(scorpiongmx)
	ROM_REGION(0x90000, "maincpu", 0)
	ROM_DEFAULT_BIOS("v6.43su")

	ROM_SYSTEM_BIOS(0, "v12500", "GMX Boot Rom 1.2 V. 5.00")
	ROMX_LOAD( "gmx12500.rom", 0x010000, 0x80000, CRC(00df8568) SHA1(dd303d298f96ec4f9fe736eefd3c36fff1dfcdf5), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v12501", "GMX Boot Rom 1.2 V. 5.01")
	ROMX_LOAD( "gmx12501.rom", 0x010000, 0x80000, CRC(34c8d210) SHA1(433237c8b5cd9de3bff3b074b6c7065788306995), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v13500", "GMX Boot Rom 1.3 V. 5.00")
	ROMX_LOAD( "gmx13500.rom", 0x010000, 0x80000, CRC(47c9df88) SHA1(e26823989ce111a086a9e44bfa07fa631019c19d), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v5.27", "ProfROM +GMX V.5.xx.027")
	ROMX_LOAD( "profgmx5xx027.rom", 0x010000, 0x80000, CRC(05dc16f5) SHA1(b786c78e9f332010e3996f3281adf3f75924bede), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "v5.29", "ProfROM +GMX V.5.xx.029")
	ROMX_LOAD( "profgmx5xx029.rom", 0x010000, 0x80000, CRC(41016b99) SHA1(86f324d45f4f181c9231a7001b30d5ce3d1512c6), ROM_BIOS(4))
	ROM_SYSTEM_BIOS(5, "v5.30", "ProfROM +GMX V.5.xx.030")
	ROMX_LOAD( "profgmx5xx030.rom", 0x010000, 0x80000, CRC(21edd18e) SHA1(90fc58600e2da85fea7dc033a22dcc88633530bc), ROM_BIOS(5))
	ROM_SYSTEM_BIOS(6, "v5.30_3d2f", "ProfROM +GMX V.5.xx.030 (3D2F)")
	ROMX_LOAD( "profgmx5xx030_3d2f.rom", 0x010000, 0x80000, CRC(ab6e2380) SHA1(6bd2904364badb00700a7d4576d8396c62916d67), ROM_BIOS(6))
	ROM_SYSTEM_BIOS(7, "v5.31", "ProfROM +GMX V.5.xx.031")
	ROMX_LOAD( "profgmx5xx031.rom", 0x010000, 0x80000, CRC(a2ec83d5) SHA1(e2a022c1bdea516b43828752fc52b112c0bad59c), ROM_BIOS(7))
	ROM_SYSTEM_BIOS(8, "v5.31_3d2f", "ProfROM +GMX V.5.xx.031 (3D2F)")
	ROMX_LOAD( "profgmx5xx031_3d2f.rom", 0x010000, 0x80000, CRC(bb900382) SHA1(1e01444e3eb65dffcb9c41b2b8e034a020198a1e), ROM_BIOS(8))
	ROM_SYSTEM_BIOS(9, "v5.41", "ProfROM +GMX V.5.xx.041.8689")
	ROMX_LOAD( "profgmx5xx041.8689.rom", 0x010000, 0x80000, CRC(fefc285e) SHA1(63ed6e18f8d9cb203c7279f3ecb431950c5b83e6), ROM_BIOS(9))
	ROM_SYSTEM_BIOS(10, "v5.41_3d2f", "ProfROM +GMX V.5.xx.041.8689 (3D2F)")
	ROMX_LOAD( "profgmx5xx041.8689_3d2f.rom", 0x010000, 0x80000, CRC(a2923935) SHA1(1b8cfe2b8e9dab07dc1852c84f36049d6c27cd93), ROM_BIOS(10))
	ROM_SYSTEM_BIOS(11, "v5.41_uni", "ProfROM +GMX V.5.xx.041.8689 (UNI)")
	ROMX_LOAD( "profgmx5xx041.8689_uni.rom", 0x010000, 0x80000, CRC(9b5f6b2b) SHA1(3c2628b5acdbb4c954b8607c2971b66d9f3ccc2a), ROM_BIOS(11))
	ROM_SYSTEM_BIOS(12, "v6.41", "ProfROM +GMX V.6.xx.041.8689")
	ROMX_LOAD( "profgmx6xx041.8689.rom", 0x010000, 0x80000, CRC(c2c6b073) SHA1(bff736c5d0389e171986f0f1c853319bbf704737), ROM_BIOS(12))
	ROM_SYSTEM_BIOS(13, "v6.41_3d2f", "ProfROM +GMX V.6.xx.041.8689 (3D2F)")
	ROMX_LOAD( "profgmx6xx041.8689_3d2f.rom", 0x010000, 0x80000, CRC(37afe94d) SHA1(904d85addf8e38206223f049a4fbeb707fb0dfc5), ROM_BIOS(13))
	ROM_SYSTEM_BIOS(14, "v6.41_uni", "ProfROM +GMX V.6.xx.041.8689 (UNI)")
	ROMX_LOAD( "profgmx6xx041.8689_uni.rom", 0x010000, 0x80000, CRC(04d9aa7c) SHA1(a22d89337874a9b01ed80b8a1c09cc86c56dff69), ROM_BIOS(14))
	ROM_SYSTEM_BIOS(15, "v6.43s", "ProfROM +GMX V.6.xx.043.9226s")
	ROMX_LOAD( "profgmx6xx043.9226s.rom", 0x010000, 0x80000, CRC(a0551240) SHA1(446fcdb3de225f06e7ac9ee8e03812a4c7900a4a), ROM_BIOS(15))
	ROM_SYSTEM_BIOS(16, "v6.43se", "ProfROM +GMX V.6.xx.043.9226se (VG93 emulation enabled)")
	ROMX_LOAD( "profgmx6xx043.9226se.rom", 0x010000, 0x80000, CRC(16452fc8) SHA1(c5a69762e291b6dbe656f8f3d1661a738f332108), ROM_BIOS(16))
	ROM_SYSTEM_BIOS(17, "v6.43su", "ProfROM +GMX V.6.xx.043.9226su (VG93 emulation disabled)")
	ROMX_LOAD( "profgmx6xx043.9226su.rom", 0x010000, 0x80000, CRC(3060e891) SHA1(416164b288d61f2c9438e1242df6a3e892fdac94), ROM_BIOS(17))
ROM_END

ROM_START(profi)
	ROM_REGION(0x020000, "maincpu", 0)
	ROM_SYSTEM_BIOS(0, "v1", "ver 1")
	ROMX_LOAD( "profi.rom", 0x010000, 0x10000, CRC(285a0985) SHA1(2b33ab3561e7bc5997da7f0d3a2a906fe7ea960f), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v2", "ver 2")
	ROMX_LOAD( "profi_my.rom", 0x010000, 0x10000, CRC(2ffd6cd9) SHA1(1b74a3251358c5f102bb87654f47b02281e15e9c), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v3", "ver 3")
	ROMX_LOAD( "profi-p1.rom", 0x010000, 0x10000, CRC(537ddb81) SHA1(00a23e8dc722b248d4f98cb14a600ce7487f2b9c), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v4", "ver 4")
	ROMX_LOAD( "profi_1.rom", 0x010000, 0x10000, CRC(f07fbee8) SHA1(b29c81a94658a4d50274ba953775a49e855534de), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "v5", "T-Rex")
	ROMX_LOAD( "profi-p.rom", 0x010000, 0x10000, CRC(314f6b57) SHA1(1507f53ec64dcf5154b5cfce6922f69f70296a53), ROM_BIOS(4))
	ROM_SYSTEM_BIOS(5, "v6", "JV Kramis V0.2")
	ROMX_LOAD( "profi32.rom", 0x010000, 0x10000, CRC(77327f52) SHA1(019bd00cc7939741d99b99beac6ae1298652e652), ROM_BIOS(5))
	ROM_SYSTEM_BIOS(6, "v7", "Power Of Sound Group")
	ROMX_LOAD( "profi1k.rom", 0x010000, 0x10000, CRC(a932676f) SHA1(907ac56219f325949a7c2fe8168799d9cdd5ba6c), ROM_BIOS(6))
ROM_END

ROM_START(quorum)
	ROM_REGION(0x020000, "maincpu", 0)
	ROM_LOAD("qu7v42.rom",   0x010000, 0x10000, CRC(e950eee5) SHA1(f8e22672722b0038689c6c8bc4acf5392acc9d8c))
ROM_END

ROM_START(bestzx)
	ROM_REGION(0x020000, "maincpu", 0)
	ROM_LOAD( "bestzx.rom", 0x010000, 0x10000, CRC(fc7936e8) SHA1(0d6378c51b2f08a3e2b4c75e64c76c15ae5dc76d))
ROM_END

ROM_START( kay1024 )
	ROM_REGION(0x020000, "maincpu", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "v1", "ver 1")
	ROMX_LOAD( "kay98.rom",    0x010000, 0x08000, CRC(7fbf2d43) SHA1(e555f2ed01ecf2231d493bd70a4d79b436e9f10e), ROM_BIOS(0))
	ROMX_LOAD( "trd503.rom",   0x01c000, 0x04000, CRC(10751aba) SHA1(21695e3f2a8f796386ce66eea8a246b0ac44810c), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v2", "Kramis V0.2")
	ROMX_LOAD( "kay1024b.rom", 0x010000, 0x10000, CRC(ab99c31e) SHA1(cfa9e6553aea72956fce4f0130c007981d684734), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v3", "Kramis V0.3")
	ROMX_LOAD( "kay1024s.rom", 0x010000, 0x10000, CRC(67351caa) SHA1(1d9c0606b380c000ca1dfa33f90a122ecf9df1f1), ROM_BIOS(2))
ROM_END

} // Anonymous namespace


//    YEAR  NAME         PARENT   COMPAT  MACHINE      INPUT       CLASS               INIT        COMPANY              FULLNAME                        FLAGS
COMP( 1992, scorpio,     spec128, 0,      scorpion,    scorpion,   scorpion_state,     empty_init, "Scorpion, Ltd.",    "Scorpion ZS-256 (Yellow PCB)", 0 )
COMP( 1996, scorpiontb,  spec128, 0,      scorpiontb,  scorpiontb, scorpiontb_state,   empty_init, "Scorpion, Ltd.",    "Scorpion ZS-256 TURBO+",       0 )
COMP( 1998, scorpiongmx, spec128, 0,      scorpiongmx, scorpiontb, scorpiongmx_state,  empty_init, "Scorpion, Ltd.",    "Scorpion GMX",                 0 )
COMP( 1991, profi,       spec128, 0,      profi,       scorpion,   scorpion_state,     empty_init, "Kondor and Kramis", "Profi",                        MACHINE_NOT_WORKING )
COMP( 1998, kay1024,     spec128, 0,      scorpion,    scorpion,   scorpion_state,     empty_init, "NEMO",              "Kay 1024",                     MACHINE_NOT_WORKING )
COMP( 19??, quorum,      spec128, 0,      quorum,      scorpion,   scorpion_state,     empty_init, "<unknown>",         "Quorum",                       MACHINE_NOT_WORKING )
COMP( 19??, bestzx,      spec128, 0,      scorpion,    scorpion,   scorpion_state,     empty_init, "<unknown>",         "BestZX",                       MACHINE_NOT_WORKING )



scrablex.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Selchow & Righter Scrabble Lexor
It's a tabletop scrabble game/scorekeeper.

Hardware notes:
- PCB label: PB-074
- Fujitsu MB8841 MCU
- 8-digit 14-seg LEDs, 2-bit sound

There's also a version of this game on a Matsushita MN1405 MCU (see hh_mn1400.cpp),
even though it's a different MCU, the I/O is very similar.

*******************************************************************************/

#include "emu.h"

#include "cpu/mb88xx/mb88xx.h"
#include "sound/spkrdev.h"
#include "video/pwm.h"

#include "speaker.h"

#include "scrablex.lh"


namespace {

class scrablex_state : public driver_device
{
public:
	scrablex_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_speaker(*this, "speaker"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void scrablex(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<mb8841_cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<speaker_sound_device> m_speaker;
	required_ioport_array<5> m_inputs;

	u8 m_inp_mux = 0;
	u16 m_r = 0;

	void write_o(u8 data);
	void write_p(u8 data);
	template<int N> u8 read_r();
	template<int N> void write_r(u8 data);
};

void scrablex_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_r));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void scrablex_state::write_o(u8 data)
{
	// O0-O7: digit select (uses index 8-15)
	m_display->write_my((1 << (data & 0xf)) >> 8);
}

void scrablex_state::write_p(u8 data)
{
	// P0-P3: input mux part
	m_inp_mux = (m_inp_mux & ~0xf) | (~data & 0xf);
}

template<int N>
u8 scrablex_state::read_r()
{
	u16 data = 0;

	// R0-R8: multiplexed inputs
	for (int i = 0; i < 5; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read();

	return ~data >> (N * 4) & 0xf;
}

template<int N>
void scrablex_state::write_r(u8 data)
{
	// R0-R13: digit segments
	m_r = (m_r & ~(0xf << (N * 4))) | ((~data & 0xf) << (N * 4));
	m_display->write_mx(bitswap<14>(m_r,10,8,6,12,11,7,9,13,5,4,3,2,1,0));

	// R14,R15: speaker out
	m_speaker->level_w(m_r >> 14 & 3);

	// R15: input mux part
	m_inp_mux = (m_inp_mux & 0xf) | (m_r >> 11 & 0x10);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( scrablex )
	PORT_START("IN.0")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')

	PORT_START("IN.1")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')

	PORT_START("IN.2")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR('*')

	PORT_START("IN.3")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_NAME("Double")
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_NAME("Triple")
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("Word")
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("Bonus")
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_NAME("Minus")
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_NAME("Clear")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("Enter")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1) PORT_NAME("Flash")
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_NAME("Solo")
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3) PORT_NAME("Score")
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_NAME("Timer")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_NAME("Review")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void scrablex_state::scrablex(machine_config &config)
{
	// basic machine hardware
	MB8841(config, m_maincpu, 500000); // approximation - RC osc. R=15K, C=100pF
	m_maincpu->set_pla_bits(4);
	m_maincpu->write_o().set(FUNC(scrablex_state::write_o));
	m_maincpu->write_p().set(FUNC(scrablex_state::write_p));
	m_maincpu->read_r<0>().set(FUNC(scrablex_state::read_r<0>));
	m_maincpu->read_r<1>().set(FUNC(scrablex_state::read_r<1>));
	m_maincpu->read_r<2>().set(FUNC(scrablex_state::read_r<2>));
	m_maincpu->read_r<3>().set(FUNC(scrablex_state::read_r<3>));
	m_maincpu->write_r<0>().set(FUNC(scrablex_state::write_r<0>));
	m_maincpu->write_r<1>().set(FUNC(scrablex_state::write_r<1>));
	m_maincpu->write_r<2>().set(FUNC(scrablex_state::write_r<2>));
	m_maincpu->write_r<3>().set(FUNC(scrablex_state::write_r<3>));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 14);
	m_display->set_segmask(0xff, 0x3fff);
	config.set_default_layout(layout_scrablex);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[4] = { 0.0, 1.0, -1.0, 0.0 };
	m_speaker->set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.125);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( scrablex )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "mb8841_320m", 0x0000, 0x0800, CRC(bef4ab5a) SHA1(44e385225c5e8818ed93c3654a294aa412042e34) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1980, scrablex, 0,      0,      scrablex, scrablex, scrablex_state, empty_init, "Selchow & Righter", "Scrabble Lexor: Computer Word Game (MB8841 version)", MACHINE_SUPPORTS_SAVE )



scv.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/***************************************************************************

    Driver for Epoch Super Cassette Vision

TODO:
- verify video clock in European units.
- verify raw video parameters.

***************************************************************************/

#include "emu.h"
#include "cpu/upd177x/upd177x.h"
#include "cpu/upd7810/upd7810.h"
#include "bus/scv/slot.h"
#include "bus/scv/rom.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class scv_state : public driver_device
{
public:
	scv_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_videoram(*this,"videoram"),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen"),
		m_upd1771c(*this, "upd1771c"),
		m_cart(*this, "cartslot"),
		m_pa(*this, "PA.%u", 0),
		m_pc0(*this, "PC0"),
		m_charrom(*this, "charrom")
	{ }

	void scv(machine_config &config);
	void scv_pal(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	TIMER_CALLBACK_MEMBER(vblank_update);
	TIMER_CALLBACK_MEMBER(pa_w_update);

private:
	void scv_palette(palette_device &palette) const;
	u32 screen_update_scv(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void draw_sprite_part(bitmap_ind16 &bitmap, u8 x, u8 y, u8 pat, u8 col);
	void draw_sprite(bitmap_ind16 &bitmap, u8 x, u8 y, u8 tile_idx, u8 col, bool left, bool right, bool top, bool bottom, u8 clip_y);
	void draw_sprites(bitmap_ind16 &bitmap);
	void draw_text(bitmap_ind16 &bitmap, u8 x, u8 y, u8 *char_data, u8 fg, uint8_t bg);
	void draw_semi_graph(bitmap_ind16 &bitmap, u8 x, u8 y, u8 data, u8 fg);
	void draw_block_graph(bitmap_ind16 &bitmap, u8 x, u8 y, u8 col);

	void scv_mem(address_map &map) ATTR_COLD;

	static const u8 s_spr_2col_lut0[16];
	static const u8 s_spr_2col_lut1[16];

	u8 m_porta;
	u8 m_portc;
	emu_timer *m_vb_timer;

	required_shared_ptr<u8> m_videoram;
	required_device<upd7801_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<upd1771c_cpu_device> m_upd1771c;
	required_device<scv_cart_slot_device> m_cart;
	required_ioport_array<8> m_pa;
	required_ioport m_pc0;
	required_memory_region m_charrom;
	emu_timer *m_pa_w_timer;
};


const u8 scv_state::s_spr_2col_lut0[16] = {0, 15, 12, 13, 10, 11,  8, 9, 6, 7,  4,  5, 2, 3,  1,  1};
const u8 scv_state::s_spr_2col_lut1[16] = {0,  1,  8, 11,  2,  3, 10, 9, 4, 5, 12, 13, 6, 7, 14, 15};


void scv_state::scv_mem(address_map &map)
{
	map(0x0000, 0x0fff).rom();   // BIOS

	map(0x2000, 0x3403).ram().share(m_videoram);  // VRAM + 4 registers
	map(0x3600, 0x3600).lw8(NAME([this] (u8 data) {
		// With the current cores there are sync issues between writing and clearing PA in scv kungfurd when playing back adpcm.
		// During a reset an OUT PA gets executed just around the same time when the main cpu is writing to PA.
		// Since the OUT PA completes while the external write to PA is still in progress the external write to PA wins.
		m_pa_w_timer->adjust(m_upd1771c->cycles_to_attotime(8), data);
	}));

	// 8000 - ff7f - Cartridge
	// ff80 - ffff - CPU internal RAM
}


static INPUT_PORTS_START(scv)
	PORT_START("PA.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_UP) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_PLAYER(1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_PLAYER(2)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("PA.1" )
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_PLAYER(1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(2) PORT_8WAY
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_PLAYER(2)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("PA.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("1") PORT_CODE(KEYCODE_1_PAD)

	PORT_START("PA.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("3") PORT_CODE(KEYCODE_3_PAD)

	PORT_START("PA.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("5") PORT_CODE(KEYCODE_5_PAD)

	PORT_START("PA.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("7") PORT_CODE(KEYCODE_7_PAD)

	PORT_START("PA.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("9") PORT_CODE(KEYCODE_9_PAD)

	PORT_START("PA.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Cl") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("En") PORT_CODE(KEYCODE_PLUS_PAD)

	PORT_START("PC0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME(DEF_STR(Pause)) PORT_CODE(KEYCODE_O)
INPUT_PORTS_END


void scv_state::scv_palette(palette_device &palette) const
{
	/*
	  SCV Epoch-1A chip RGB voltage readouts from paused BIOS color test:

	  (values in millivolts)

	        R   G   B
	  0    29  29 325
	  1    29  27  22
	  2    25  24 510
	  3   337  28 508
	  4    29 515  22
	  5   336 512 329
	  6    26 515 511
	  7    29 337  25
	  8   520  24  22
	  9   517 338  21
	  10  520  25 512
	  11  521 336 333
	  12  518 515  21
	  13  342 336  22
	  14  337 336 330
	  15  516 511 508

	  Only tree 'bins' of values are obviously captured
	   25 ish
	  330 ish
	  520 ish.

	  Quantizing/scaling/rounding between 0 and 255 we thus get:

	*/
	palette.set_pen_color( 0,   0,   0, 155);
	palette.set_pen_color( 1,   0,   0,   0);
	palette.set_pen_color( 2,   0,   0, 255);
	palette.set_pen_color( 3, 161,   0, 255);
	palette.set_pen_color( 4,   0, 255,   0);
	palette.set_pen_color( 5, 160, 255, 157);
	palette.set_pen_color( 6,   0, 255, 255);
	palette.set_pen_color( 7,   0, 161,   0);
	palette.set_pen_color( 8, 255,   0,   0);
	palette.set_pen_color( 9, 255, 161,   0);
	palette.set_pen_color(10, 255,   0, 255);
	palette.set_pen_color(11, 255, 160, 159);
	palette.set_pen_color(12, 255, 255,   0);
	palette.set_pen_color(13, 163, 160,   0);
	palette.set_pen_color(14, 161, 160, 157);
	palette.set_pen_color(15, 255, 255, 255);
}


TIMER_CALLBACK_MEMBER(scv_state::vblank_update)
{
	int vpos = m_screen->vpos();

	switch (vpos)
	{
	case 240:
		m_maincpu->set_input_line(UPD7810_INTF2, ASSERT_LINE);
		break;
	case 0:
		m_maincpu->set_input_line(UPD7810_INTF2, CLEAR_LINE);
		break;
	}

	m_vb_timer->adjust(m_screen->time_until_pos((vpos + 1) % 262, 0));
}


void scv_state::draw_sprite_part(bitmap_ind16 &bitmap, u8 x, u8 y, u8 pat, u8 col)
{
	if (x >= 4)
	{
		x -= 4;

		if (BIT(pat, 3))
			bitmap.pix(y + 2, x) = col;

		if (BIT(pat, 2) && x < 255)
			bitmap.pix(y + 2, x + 1) = col;

		if (BIT(pat, 1) && x < 254)
			bitmap.pix(y + 2, x + 2) = col;

		if (BIT(pat, 0) && x < 253)
			bitmap.pix(y + 2, x + 3) = col;
	}
}


void scv_state::draw_sprite(bitmap_ind16 &bitmap, u8 x, u8 y, u8 tile_idx, u8 col, bool left, bool right, bool top, bool bottom, u8 clip_y)
{
	y += clip_y * 2;
	for (int j = clip_y * 4; j < 32; j += 4, y += 2)
	{
		if ((top && (j < 16)) || (bottom && (j >= 16)))
		{
			if (left)
			{
				const u8 pat0 = m_videoram[tile_idx * 32 + j + 0];
				const u8 pat1 = m_videoram[tile_idx * 32 + j + 1];

				draw_sprite_part(bitmap, x     , y,     pat0 >> 4,   col);
				draw_sprite_part(bitmap, x +  4, y,     pat1 >> 4,   col);
				draw_sprite_part(bitmap, x     , y + 1, pat0 & 0x0f, col);
				draw_sprite_part(bitmap, x +  4, y + 1, pat1 & 0x0f, col);
			}
			if (right)
			{
				const u8 pat2 = m_videoram[tile_idx * 32 + j + 2];
				const u8 pat3 = m_videoram[tile_idx * 32 + j + 3];

				draw_sprite_part(bitmap, x +  8, y,     pat2 >> 4,   col);
				draw_sprite_part(bitmap, x + 12, y,     pat3 >> 4,   col);
				draw_sprite_part(bitmap, x +  8, y + 1, pat2 & 0x0f, col);
				draw_sprite_part(bitmap, x + 12, y + 1, pat3 & 0x0f, col);
			}
		}
	}
}


void scv_state::draw_sprites(bitmap_ind16 &bitmap)
{
	const u8 num_sprites = BIT(m_videoram[0x1400], 2) ? 64 : 128;

	for (int i = 0; i < num_sprites; i++)
	{
		u8 spr_y = m_videoram[0x1200 + i * 4] & 0xfe;
		u8 y_32 = BIT(m_videoram[0x1200 + i * 4], 0);       // Xx32 sprite
		u8 clip = m_videoram[0x1201 + i * 4] >> 4;
		u8 col = m_videoram[0x1201 + i * 4] & 0x0f;
		u8 spr_x = m_videoram[0x1202 + i * 4] & 0xfe;
		u8 x_32 = BIT(m_videoram[0x1202 + i * 4], 0);       // 32xX sprite
		u8 tile_idx = m_videoram[0x1203 + i * 4] & 0x7f;
		const bool half = BIT(m_videoram[0x1203 + i * 4], 7);
		bool left = true;
		bool right = true;
		bool top = true;
		bool bottom = true;

		if (!col || !spr_y)
			continue;

		if (half)
		{
			if (BIT(tile_idx, 6))
			{
				if (y_32)
				{
					spr_y -= 8;
					top = false;
					y_32 = 0;
				}
				else
					bottom = false;
			}
			if (x_32)
			{
				spr_x -= 8;
				left = false;
				x_32 = 0;
			}
			else
				right = false;
		}

		if (BIT(m_videoram[0x1400], 5) && BIT(i, 5))
		{
			// 2 color sprite handling
			draw_sprite(bitmap, spr_x, spr_y, tile_idx, col, left, right, top, bottom, clip);
			if (x_32 || y_32)
			{
				col = BIT(i, 4) ? s_spr_2col_lut1[col] : s_spr_2col_lut0[col];
				tile_idx ^= (8 * x_32 + y_32);

				draw_sprite(bitmap, spr_x, spr_y, tile_idx, col, left, right, top, bottom, clip);
			}
		}
		else
		{
			// regular sprite handling
			draw_sprite(bitmap, spr_x, spr_y, tile_idx, col, left, right, top, bottom, clip);
			if (x_32)
				draw_sprite(bitmap, spr_x + 16, spr_y, tile_idx | 8, col, 1, 1, top, bottom, clip);

			if (y_32)
			{
				clip = BIT(clip, 3) ? (clip & 0x07) : 0;
				draw_sprite(bitmap, spr_x, spr_y + 16, tile_idx | 1, col, left, right, 1, 1, clip);
				if (x_32)
					draw_sprite(bitmap, spr_x + 16, spr_y + 16, tile_idx | 9, col, 1, 1, 1, 1, clip);
			}
		}
	}
}


void scv_state::draw_text(bitmap_ind16 &bitmap, u8 x, u8 y, u8 *char_data, u8 fg, u8 bg)
{
	for (int i = 0; i < 8; i++)
	{
		const u8 d = char_data[i];

		for (int j = 0; j < 8; j++)
			bitmap.pix(y + i, x + j) = (BIT(d, 7 - j)) ? fg : bg;
	}

	for (int i = 8; i < 16; i++)
		for (int j = 0; j < 8; j++)
			bitmap.pix(y + i, x + j) = bg;
}


void scv_state::draw_semi_graph(bitmap_ind16 &bitmap, u8 x, u8 y, u8 data, u8 fg)
{
	if (!data)
		return;

	for (int i = 0; i < 4; i++)
		for (int j = 0; j < 4; j++)
			bitmap.pix(y + i, x + j) = fg;
}


void scv_state::draw_block_graph(bitmap_ind16 &bitmap, u8 x, u8 y, u8 col)
{
	if (!col)
		return;

	for (int i = 0; i < 8; i++)
		for (int j = 0; j < 8; j++)
			bitmap.pix(y + i, x + j) = col;
}


u32 scv_state::screen_update_scv(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	const u8 fg = m_videoram[0x1403] >> 4;
	const u8 bg = m_videoram[0x1403] & 0x0f;
	const u8 gr_fg = m_videoram[0x1401] >> 4;
	const u8 gr_bg = m_videoram[0x1401] & 0x0f;
	const int clip_x = (m_videoram[0x1402] & 0x0f) * 2;
	const int clip_y = m_videoram[0x1402] >> 4;

	// Clear the screen
	bitmap.fill(gr_bg, cliprect);

	// Draw background
	for (int y = 0; y < 16; y++)
	{
		const bool text_y = (y < clip_y) ? !BIT(m_videoram[0x1400], 7) : BIT(m_videoram[0x1400], 7);

		for (int x = 0; x < 32; x++)
		{
			const bool text_x = (x < clip_x) ? !BIT(m_videoram[0x1400], 6) : BIT(m_videoram[0x1400], 6);
			const u8 d = m_videoram[0x1000 + y * 32 + x];

			if (text_x && text_y)
			{
				// Text mode
				uint8_t *char_data = m_charrom->base() + (d & 0x7f) * 8;
				draw_text(bitmap, x * 8, y * 16, char_data, fg, bg);
			}
			else
			{
				switch (m_videoram[0x1400] & 0x03)
				{
				case 0x01:      // Semi graphics mode
					draw_semi_graph(bitmap, x * 8    , y * 16     , d & 0x80, gr_fg);
					draw_semi_graph(bitmap, x * 8 + 4, y * 16     , d & 0x40, gr_fg);
					draw_semi_graph(bitmap, x * 8    , y * 16 +  4, d & 0x20, gr_fg);
					draw_semi_graph(bitmap, x * 8 + 4, y * 16 +  4, d & 0x10, gr_fg);
					draw_semi_graph(bitmap, x * 8    , y * 16 +  8, d & 0x08, gr_fg);
					draw_semi_graph(bitmap, x * 8 + 4, y * 16 +  8, d & 0x04, gr_fg);
					draw_semi_graph(bitmap, x * 8    , y * 16 + 12, d & 0x02, gr_fg);
					draw_semi_graph(bitmap, x * 8 + 4, y * 16 + 12, d & 0x01, gr_fg);
					break;

				case 0x03:      // Block graphics mode
					draw_block_graph(bitmap, x * 8, y * 16    , d >> 4);
					draw_block_graph(bitmap, x * 8, y * 16 + 8, d & 0x0f);
					break;

				default:        // Otherwise draw nothing?
					break;
				}
			}
		}
	}

	if (BIT(m_videoram[0x1400], 4))
		draw_sprites(bitmap);

	return 0;
}


TIMER_CALLBACK_MEMBER(scv_state::pa_w_update)
{
	m_upd1771c->pa_w(param);
}


void scv_state::machine_start()
{
	m_vb_timer = timer_alloc(FUNC(scv_state::vblank_update), this);
	m_pa_w_timer = timer_alloc(FUNC(scv_state::pa_w_update), this);

	save_item(NAME(m_porta));
	save_item(NAME(m_portc));
}


void scv_state::machine_reset()
{
	m_vb_timer->adjust(m_screen->time_until_pos(0, 0));
}


// F4 Character Displayer
static const gfx_layout scv_charlayout =
{
	8, 8,               // 8 x 8 characters
	128,                // 128 characters
	1,                  // 1 bits per pixel
	{0},                // no bitplanes
	// x offsets
	{0, 1, 2, 3, 4, 5, 6, 7},
	// y offsets
	{0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8},
	8*8                 // every char takes 8 bytes
};

static GFXDECODE_START(gfx_scv)
	GFXDECODE_ENTRY("charrom", 0x0000, scv_charlayout, 0, 8)
GFXDECODE_END


static void scv_cart(device_slot_interface &device)
{
	device.option_add_internal("rom8k",       SCV_ROM8K);
	device.option_add_internal("rom16k",      SCV_ROM16K);
	device.option_add_internal("rom32k",      SCV_ROM32K);
	device.option_add_internal("rom32k_ram",  SCV_ROM32K_RAM8K);
	device.option_add_internal("rom64k",      SCV_ROM64K);
	device.option_add_internal("rom128k",     SCV_ROM128K);
	device.option_add_internal("rom128k_ram", SCV_ROM128K_RAM4K);
}

void scv_state::scv(machine_config &config)
{
	UPD7801(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &scv_state::scv_mem);
	m_maincpu->pa_out_cb().set([this] (u8 data) { m_porta = data; });
	m_maincpu->pb_in_cb().set([this] () {
		u8 data = 0xff;

		for (int i = 0; i < 8; i++)
			if (!BIT(m_porta, i))
				data &= m_pa[i]->read();

		return data;
	});
	m_maincpu->pc_in_cb().set([this] () {
		return BIT(m_pc0->read(), 0);
	});
	m_maincpu->pc_out_cb().set([this] (u8 data) {
		m_portc = data;
		m_cart->write_bank(m_portc); // Only bits 5 & 6 are exposed to the cartridge?
		m_upd1771c->set_input_line(INPUT_LINE_RESET, BIT(m_portc, 3) ? CLEAR_LINE : ASSERT_LINE);
	});
	config.set_perfect_quantum(m_maincpu);

	// Video chip is EPOCH TV-1
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(XTAL(14'318'181)/2, 456, 24, 24+192, 262, 23, 23+222);  // Clock verified. TODO: Verify rest of the parameters
	m_screen->set_screen_update(FUNC(scv_state::screen_update_scv));
	m_screen->set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_scv);
	PALETTE(config, "palette", FUNC(scv_state::scv_palette), 16);

	// Sound is generated by UPD1771C clocked at XTAL(6'000'000)
	SPEAKER(config, "mono").front_center();
	UPD1771C(config, m_upd1771c, 6_MHz_XTAL);
	m_upd1771c->pb_out_cb().set([this] (u8 data) { m_maincpu->set_input_line(UPD7810_INTF1, BIT(data, 0) ? ASSERT_LINE : CLEAR_LINE); });
	m_upd1771c->add_route(ALL_OUTPUTS, "mono", 0.70);

	SCV_CART_SLOT(config, m_cart, scv_cart, nullptr);
	m_cart->set_address_space(m_maincpu, AS_PROGRAM);

	// Software lists
	SOFTWARE_LIST(config, "cart_list").set_original("scv");
}


void scv_state::scv_pal(machine_config &config)
{
	scv(config);

	// Video chip is EPOCH TV-1A
	m_screen->set_raw(13.4_MHz_XTAL/2, 456, 24, 24+192, 294, 23, 23+222);     // TODO: Verify clock and video parameters
}


// The same bios is used in both the NTSC and PAL versions of the console
ROM_START(scv)
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("upd7801g.s01", 0, 0x1000, CRC(7ac06182) SHA1(6e89d1227581c76441a53d605f9e324185f1da33))

	ROM_REGION(0x400, "charrom", 0)
	ROM_LOAD("epochtv.chr.s02", 0, 0x400, BAD_DUMP CRC(db521533) SHA1(40b4e44838c35191f115437a14f200f052e71509))

	ROM_REGION(0x400, "upd1771c", 0)
	ROM_LOAD16_WORD("upd1771c-017.s03", 0, 0x400, CRC(975bd68d) SHA1(e777217c622331677ac1d6520a741d48ad9133c0))
ROM_END


ROM_START(scv_pal)
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("upd7801g.s01", 0, 0x1000, CRC(7ac06182) SHA1(6e89d1227581c76441a53d605f9e324185f1da33))

	ROM_REGION(0x400, "charrom", 0)
	ROM_LOAD("epochtv.chr.s02", 0, 0x400, BAD_DUMP CRC(db521533) SHA1(40b4e44838c35191f115437a14f200f052e71509))

	ROM_REGION(0x400, "upd1771c", 0)
	ROM_LOAD16_WORD("upd1771c-017.s03", 0, 0x400, CRC(975bd68d) SHA1(e777217c622331677ac1d6520a741d48ad9133c0))
ROM_END

} // anonymous namespace


/*   YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY  FULLNAME                       FLAGS */
CONS(1984, scv,     0,      0,      scv,     scv,   scv_state, empty_init, "Epoch", "Super Cassette Vision",       MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE)
CONS(198?, scv_pal, scv,    0,      scv_pal, scv,   scv_state, empty_init, "Yeno",  "Super Cassette Vision (PAL)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE)



sdiamond.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Novag (Perfect Technology*) Star Diamond (model 1004)

*: Novag Industries dissolved in 2000. The Novag brand continued for a few years
under Perfect Technology, Ltd., established by the daughter of Novag's founder.
The main programmer (David Kittinger) also moved to the new company.

Although there may be newer Novag products with (old) software by David Kittinger,
Star Diamond was the last chess computer that he personally worked on.

Hardware notes:
- PCB label: TF-05 94V0Δ
- Hitachi H8S/2312 12312VTE25V, 25MHz XTAL
- 512KB Flash ROM (SST 39VF400A), only 192KB used
- 256KB RAM (2*Hynix HY62V8100B)
- LCD with 6 7segs and custom segments (same as Sapphire II)
- RJ-12 port for Novag Super System (always 57600 baud)
- piezo, 16 LEDs, button sensors chessboard

Other than 2 checksum bytes, the opening book data (0x10000-0x2ffff) is identical
to the Diamond II / Sapphire II 060597 EPROM.

TODO:
- it does a cold boot at every reset, so nvram won't work properly unless MAME
  adds some kind of auxillary autosave state feature at power-off

*******************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/h8/h8s2319.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "novag_sdiamond.lh"


namespace {

class sdiamond_state : public driver_device
{
public:
	sdiamond_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_led_pwm(*this, "led_pwm"),
		m_lcd_pwm(*this, "lcd_pwm"),
		m_dac(*this, "dac"),
		m_rs232(*this, "rs232"),
		m_inputs(*this, "IN.%u", 0),
		m_out_lcd(*this, "s%u.%u", 0U, 0U)
	{ }

	void sdiamond(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(power_switch);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD { set_power(true); }

private:
	// devices/pointers
	required_device<h8s2312_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_led_pwm;
	required_device<pwm_display_device> m_lcd_pwm;
	required_device<dac_1bit_device> m_dac;
	required_device<rs232_port_device> m_rs232;
	required_ioport_array<4> m_inputs;
	output_finder<4, 16> m_out_lcd;

	bool m_power = false;
	u8 m_inp_mux = 0;
	u8 m_lcd_sclk = 0;
	u16 m_lcd_segs = 0;
	u8 m_lcd_com = 0;

	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void standby(int state);
	void set_power(bool power);

	void lcd_pwm_w(offs_t offset, u8 data);
	void update_lcd();
	void lcd_segs_w(u8 data);
	void lcd_com_w(offs_t offset, u8 data, u8 mem_mask);

	void p1_w(u8 data);
	u8 p2_r();
	u8 p4_r();
	u8 pf_r();
	void pg_w(u8 data);
};

void sdiamond_state::machine_start()
{
	m_out_lcd.resolve();

	// register for savestates
	save_item(NAME(m_power));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_lcd_sclk));
	save_item(NAME(m_lcd_segs));
	save_item(NAME(m_lcd_com));
}



/*******************************************************************************
    I/O
*******************************************************************************/

// power

void sdiamond_state::standby(int state)
{
	// clear display
	if (state)
	{
		m_lcd_pwm->clear();
		m_led_pwm->clear();
	}
}

void sdiamond_state::set_power(bool power)
{
	// power switch is tied to NMI
	m_maincpu->set_input_line(INPUT_LINE_NMI, power ? ASSERT_LINE : CLEAR_LINE);
	m_power = power;
}

INPUT_CHANGED_MEMBER(sdiamond_state::power_switch)
{
	if (newval)
		set_power(bool(param));
}


// LCD

void sdiamond_state::lcd_pwm_w(offs_t offset, u8 data)
{
	m_out_lcd[offset & 0x3f][offset >> 6] = data;
}

void sdiamond_state::update_lcd()
{
	for (int i = 0; i < 4; i++)
	{
		// LCD common is 0/1/Hi-Z
		const u16 data = BIT(m_lcd_com, i + 4) ? (BIT(m_lcd_com, i) ? ~m_lcd_segs : m_lcd_segs) : 0;
		m_lcd_pwm->write_row(i, data);
	}
}

void sdiamond_state::lcd_com_w(offs_t offset, u8 data, u8 mem_mask)
{
	// P20-P23: LCD common
	m_lcd_com = mem_mask << 4 | (data & 0xf);
	update_lcd();
}

void sdiamond_state::lcd_segs_w(u8 data)
{
	// P35: 2*14015B C (chained)
	if (data & 0x20 && !m_lcd_sclk)
	{
		// P34: 14015B D, outputs to LCD segments
		m_lcd_segs = m_lcd_segs << 1 | BIT(data, 4);
		update_lcd();
	}
	m_lcd_sclk = BIT(data, 5);
}


// misc

void sdiamond_state::p1_w(u8 data)
{
	// P10-P17: input mux, led data
	m_inp_mux = ~data;
	m_led_pwm->write_mx(~data);
}

u8 sdiamond_state::p2_r()
{
	u8 data = 0;

	// P26: power switch
	if (!m_power)
		data |= 0x40;

	// P27: battery status
	data |= m_inputs[3]->read() << 7;
	return data | 0xf;
}

u8 sdiamond_state::p4_r()
{
	u8 data = 0;

	// P40-P47: read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_rank(i ^ 7);

	return ~data;
}

u8 sdiamond_state::pf_r()
{
	u8 data = 0;

	// PF0-PF2: read buttons
	for (int i = 0; i < 3; i++)
		if (m_inp_mux & m_inputs[i]->read())
			data |= 1 << i;

	return ~data;
}

void sdiamond_state::pg_w(u8 data)
{
	// PG0,PG1: led select
	m_led_pwm->write_my(~data & 3);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void sdiamond_state::main_map(address_map &map)
{
	map(0x000000, 0x07ffff).rom();
	map(0x400000, 0x43ffff).ram().share("nvram");
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( sdiamond )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Next Best / Take Back / Print Board")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Trace Forward / Print Game")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Easy / Replay")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Random / Video")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Restore / Human")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Book Select / Auto/Demo")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Sound / Auto Clock")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Referee / Print Moves")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Color")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Verify / Setup / Rating")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Pro-op Print / King")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Pro-op Priority / Queen")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Pro-op Delete / Rook")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Pro-op Save / Bishop")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Load Game / Knight")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Save Game / Pawn")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Go")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Info")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Hint")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Training")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("Option 1/2")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Set Level")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_J) PORT_NAME("Clear / Clear Board")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_K) PORT_NAME("New Game")

	PORT_START("IN.3")
	PORT_CONFNAME( 0x01, 0x00, "Battery Status" )
	PORT_CONFSETTING(    0x01, "Low" )
	PORT_CONFSETTING(    0x00, DEF_STR( Normal ) )

	PORT_START("POWER")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_ON) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sdiamond_state::power_switch), 1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sdiamond_state::power_switch), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void sdiamond_state::sdiamond(machine_config &config)
{
	// basic machine hardware
	H8S2312(config, m_maincpu, 25_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &sdiamond_state::main_map);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(h8s2312_device::nvram_set_battery));
	m_maincpu->standby_cb().append(FUNC(sdiamond_state::standby));
	m_maincpu->write_sci_tx<0>().set(m_rs232, FUNC(rs232_port_device::write_txd));
	m_maincpu->write_port1().set(FUNC(sdiamond_state::p1_w));
	m_maincpu->read_port2().set(FUNC(sdiamond_state::p2_r));
	m_maincpu->write_port2().set(FUNC(sdiamond_state::lcd_com_w));
	m_maincpu->write_port3().set(FUNC(sdiamond_state::lcd_segs_w));
	m_maincpu->read_port4().set(FUNC(sdiamond_state::p4_r));
	m_maincpu->read_portf().set(FUNC(sdiamond_state::pf_r));
	m_maincpu->write_portf().set(m_dac, FUNC(dac_1bit_device::write)).bit(6);
	m_maincpu->write_portg().set(FUNC(sdiamond_state::pg_w));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	//m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_lcd_pwm).set_size(4, 16);
	m_lcd_pwm->output_x().set(FUNC(sdiamond_state::lcd_pwm_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/5, 671/5);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_led_pwm).set_size(2, 8);
	config.set_default_layout(layout_novag_sdiamond);

	// rs232 (configure after video)
	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set(m_maincpu, FUNC(h8s2312_device::sci_rx_w<0>));

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( sdiamond ) // ID = H8S/SD V1.04
	ROM_REGION16_BE( 0x80000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP("39vf400a.ic3", 0x00000, 0x80000, CRC(ee9a4fee) SHA1(b86e5efa5b7b9ddbe9fe1dabfe8cbc2bc40809b8) )

	ROM_REGION( 72533, "screen", 0 )
	ROM_LOAD("sapphire2.svg", 0, 72533, CRC(34944b61) SHA1(4a0536ac07790cced9f9bf15522b17ebc375ff8a) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 2003, sdiamond, 0,      0,      sdiamond, sdiamond, sdiamond_state, empty_init, "Perfect Technology / Intelligent Heuristic Programming", "Star Diamond", MACHINE_SUPPORTS_SAVE )



sdk51.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Skeleton driver for Intel MCS-51 System Design Kit (SDK-51).

*******************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/mcs51/mcs51.h"
#include "cpu/mcs48/mcs48.h"
//#include "imagedev/cassette.h"
#include "machine/bankdev.h"
#include "machine/i8155.h"
#include "machine/i8243.h"
#include "machine/i8251.h"


namespace {

class sdk51_state : public driver_device
{
public:
	sdk51_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_progmem(*this, "progmem")
		, m_datamem(*this, "datamem")
		, m_mem0(*this, "mem0")
		, m_upi(*this, "upi")
		, m_usart(*this, "usart")
		, m_cycles(*this, "cycles")
		, m_kb(*this, "KB%u", 0U)
	{
	}

	void sdk51(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void psen_map(address_map &map) ATTR_COLD;
	void movx_map(address_map &map) ATTR_COLD;
	void progmem_map(address_map &map) ATTR_COLD;
	void datamem_map(address_map &map) ATTR_COLD;
	void mem0_map(address_map &map) ATTR_COLD;

	u8 psen_r(offs_t offset);
	u8 datamem_r(offs_t offset);
	void datamem_w(offs_t offset, u8 data);

	u8 brkmem_r(offs_t offset);
	void brkmem_w(offs_t offset, u8 data);

	u8 upibus_r();
	void upibus_w(u8 data);
	void display_clock_w(int state);
	void upiobf_w(int state);
	void serial_control_w(u8 data);

	required_device<mcs51_cpu_device> m_maincpu;
	required_device<address_map_bank_device> m_progmem;
	required_device<address_map_bank_device> m_datamem;
	required_device<address_map_bank_device> m_mem0;
	required_device<upi41_cpu_device> m_upi;
	required_device<i8251_device> m_usart;
	required_region_ptr<u8> m_cycles;
	required_ioport_array<7> m_kb;

	bool m_upiobf = false;
	u8 m_serial_control = 0;
	u32 m_kdtime = 0;
	bool m_display_clock = false;
};

u8 sdk51_state::psen_r(offs_t offset)
{
	return m_progmem->read8(offset);
}

u8 sdk51_state::datamem_r(offs_t offset)
{
	return m_datamem->read8(offset);
}

void sdk51_state::datamem_w(offs_t offset, u8 data)
{
	m_datamem->write8(offset, data);
}

u8 sdk51_state::brkmem_r(offs_t offset)
{
	return 1 | (m_upiobf ? 2 : 0);
}

void sdk51_state::brkmem_w(offs_t offset, u8 data)
{
	m_mem0->set_bank(0);
}

u8 sdk51_state::upibus_r()
{
	u8 result = 0xff;

	if (!BIT(m_serial_control, 0))
		result &= m_usart->read(BIT(m_serial_control, 2));

	if (!BIT(m_upi->p2_r(), 6))
		for (int n = 0; n < 7; n++)
			if (BIT(m_kdtime, n))
				result &= m_kb[n]->read();

	return result;
}

void sdk51_state::upibus_w(u8 data)
{
	if (!BIT(m_serial_control, 1))
		m_usart->write(BIT(m_serial_control, 2), data);
}

void sdk51_state::serial_control_w(u8 data)
{
	m_serial_control = data;
}

void sdk51_state::display_clock_w(int state)
{
	if (!m_display_clock && state)
		m_kdtime = ((m_kdtime << 1) & 0xfffffe) | BIT(m_upi->p1_r(), 6);

	m_display_clock = state;
}

void sdk51_state::upiobf_w(int state)
{
	if (m_upiobf != bool(state))
	{
		m_upiobf = state;
		m_maincpu->set_input_line(MCS51_INT0_LINE, state ? ASSERT_LINE : CLEAR_LINE);
	}
}

void sdk51_state::psen_map(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(sdk51_state::psen_r));
}

void sdk51_state::movx_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(sdk51_state::datamem_r), FUNC(sdk51_state::datamem_w));
}

void sdk51_state::progmem_map(address_map &map)
{
	map(0x0000, 0x1fff).m(m_mem0, FUNC(address_map_bank_device::amap8));
	map(0x2000, 0x3fff).ram();
	map(0xe000, 0xffff).rom().region("monitor", 0);
}

void sdk51_state::datamem_map(address_map &map)
{
	progmem_map(map);
	map(0xa000, 0xa001).mirror(0xffe).rw(m_upi, FUNC(upi41_cpu_device::upi41_master_r), FUNC(upi41_cpu_device::upi41_master_w));
	map(0xb000, 0xb0ff).mirror(0x700).rw("io", FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w));
	map(0xb800, 0xb807).mirror(0x7f8).rw("io", FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
	map(0xc000, 0xdfff).rw(FUNC(sdk51_state::brkmem_r), FUNC(sdk51_state::brkmem_w));
}

void sdk51_state::mem0_map(address_map &map)
{
	map(0x0000, 0x1fff).ram();
	map(0x2000, 0x2fff).mirror(0x1000).rom().region("monitor", 0);
}

static INPUT_PORTS_START(sdk51)
	PORT_START("KB0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CHAR(UCHAR_SHIFT_1) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Control") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x09) PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x1b) PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Rubout") PORT_CHAR(0x7f) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(' ') PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('@') PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CHAR(0x0d) PORT_CODE(KEYCODE_ENTER)

	PORT_START("KB1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0') PORT_CHAR('\\') PORT_CODE(KEYCODE_0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR('"') PORT_CODE(KEYCODE_2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR('$') PORT_CODE(KEYCODE_4)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR('&') PORT_CODE(KEYCODE_6)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR('\'') PORT_CODE(KEYCODE_7)

	PORT_START("KB2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('8') PORT_CHAR('(') PORT_CODE(KEYCODE_8)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('9') PORT_CHAR(')') PORT_CODE(KEYCODE_9)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('-') PORT_CHAR('=') PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHAR('+') PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(':') PORT_CHAR('*') PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('/') PORT_CHAR('?') PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('.') PORT_CHAR(']') PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',') PORT_CHAR('[') PORT_CODE(KEYCODE_COMMA)

	PORT_START("KB3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('A') PORT_CODE(KEYCODE_A)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('B') PORT_CODE(KEYCODE_B)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('C') PORT_CODE(KEYCODE_C)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('D') PORT_CODE(KEYCODE_D)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('E') PORT_CODE(KEYCODE_E)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('F') PORT_CODE(KEYCODE_F)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('G') PORT_CODE(KEYCODE_G)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('H') PORT_CODE(KEYCODE_H)

	PORT_START("KB4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('I') PORT_CODE(KEYCODE_I)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('J') PORT_CODE(KEYCODE_J)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('K') PORT_CODE(KEYCODE_K)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('L') PORT_CODE(KEYCODE_L)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('M') PORT_CODE(KEYCODE_M)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('N') PORT_CODE(KEYCODE_N)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('O') PORT_CODE(KEYCODE_O)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('P') PORT_CODE(KEYCODE_P)

	PORT_START("KB5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('Q') PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('R') PORT_CODE(KEYCODE_R)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('S') PORT_CODE(KEYCODE_S)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('T') PORT_CODE(KEYCODE_T)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('U') PORT_CODE(KEYCODE_U)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('V') PORT_CODE(KEYCODE_V)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('W') PORT_CODE(KEYCODE_W)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('X') PORT_CODE(KEYCODE_X)

	PORT_START("KB6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('Y') PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('Z') PORT_CODE(KEYCODE_Z)
	PORT_BIT(0xfc, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

void sdk51_state::machine_start()
{
	m_upiobf = false;
	m_serial_control = 0x0f;
	m_display_clock = true;

	save_item(NAME(m_upiobf));
	save_item(NAME(m_serial_control));
	save_item(NAME(m_kdtime));
	save_item(NAME(m_display_clock));
}

void sdk51_state::machine_reset()
{
	m_mem0->set_bank(1);
	m_kdtime = 0;
}

void sdk51_state::sdk51(machine_config &config)
{
	I8031(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &sdk51_state::psen_map);
	m_maincpu->set_addrmap(AS_IO, &sdk51_state::movx_map);

	ADDRESS_MAP_BANK(config, m_progmem);
	m_progmem->set_addrmap(0, &sdk51_state::progmem_map);
	m_progmem->set_data_width(8);
	m_progmem->set_addr_width(16);

	ADDRESS_MAP_BANK(config, m_datamem);
	m_datamem->set_addrmap(0, &sdk51_state::datamem_map);
	m_datamem->set_data_width(8);
	m_datamem->set_addr_width(16);

	ADDRESS_MAP_BANK(config, m_mem0);
	m_mem0->set_addrmap(0, &sdk51_state::mem0_map);
	m_mem0->set_data_width(8);
	m_mem0->set_addr_width(14);
	m_mem0->set_stride(0x2000);

	I8041A(config, m_upi, 6_MHz_XTAL);
	m_upi->p1_in_cb().set(FUNC(sdk51_state::upibus_r));
	m_upi->p1_out_cb().set(FUNC(sdk51_state::upibus_w));
	m_upi->p2_in_cb().set("upiexp", FUNC(i8243_device::p2_r));
	m_upi->p2_out_cb().set(FUNC(sdk51_state::display_clock_w)).bit(7);
	m_upi->p2_out_cb().append(FUNC(sdk51_state::upiobf_w)).bit(4);
	m_upi->p2_out_cb().append("upiexp", FUNC(i8243_device::p2_w)).mask(0x0f);
	m_upi->prog_out_cb().set("upiexp", FUNC(i8243_device::prog_w));
	m_upi->t1_in_cb().set(m_usart, FUNC(i8251_device::txrdy_r));

	i8243_device &upiexp(I8243(config, "upiexp"));
	upiexp.p4_out_cb().set(FUNC(sdk51_state::serial_control_w));

	i8155_device &io(I8155(config, "io", 6_MHz_XTAL / 3));
	io.out_to_callback().set(m_usart, FUNC(i8251_device::write_txc));
	io.out_to_callback().append(m_usart, FUNC(i8251_device::write_txc));

	I8251(config, m_usart, 6_MHz_XTAL / 3);
	m_usart->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_usart->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_usart, FUNC(i8251_device::write_rxd));
	rs232.cts_handler().set(m_usart, FUNC(i8251_device::write_cts));
}

ROM_START(sdk51)
	ROM_REGION(0x2000, "monitor", 0) // "SDK-51 MONITOR VER. 1.03"
	ROM_LOAD("u59-e000.bin", 0x0000, 0x1000, CRC(cc6c7b05) SHA1(ead75920347ff19487e730e90e1e1f7207d44601))
	ROM_LOAD("u60-f000.bin", 0x1000, 0x1000, CRC(da6e664d) SHA1(18416106307f37dba6dbb789f3d39fe6d5294755))

	ROM_REGION(0x0400, "upi", 0)
	ROM_LOAD("u41-8041a.bin", 0x000, 0x400, CRC(02f38b69) SHA1(ab2ac73b69b3297572583242ed5bd717eb116c37))

	ROM_REGION(0x0200, "cycles", 0)
	ROM_LOAD("u63-3622a.bin", 0x000, 0x200, CRC(85cbd498) SHA1(f0214b6d02d6d153b5fafd9adf5a23013373c9c4)) // pin 9 output stuck high but not used
ROM_END

} // anonymous namespace


COMP(1981, sdk51, 0, 0, sdk51, sdk51, sdk51_state, empty_init, "Intel", "MCS-51 System Design Kit", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



sdk80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes,Ryan Holtz
/***************************************************************************

        Intel SDK-80

The 8080 System Design Kit (SDK-80) is a complete microcomputer system in
kit form, and provides a excellent prototype vehicle for evaluation of the
8080 microcomputer system (MCS-80).

An extensive system monitor is included in a pre-programmed ROM for general
software utilities and system diagnostics.

Download the User Manual to get the operating procedures.

Monitor Commands:
D<low address>,<high address>                Display memory contents
G<entry point>                               Go to address (execute program at address)
I<address>                                   Insert instructions into RAM
M<low address>,<high address>,<destination>  Move blocks of memory
S<address>                                   Substitute memory locations
X<register identifier>                       Examine and modify registers

****************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8255.h"
#include "machine/i8251.h"
#include "machine/clock.h"
#include "bus/rs232/rs232.h"


namespace {

#define I8255A_0_TAG    "ppi8255_0"
#define I8255A_1_TAG    "ppi8255_1"
#define I8251A_TAG      "usart"
#define I8251A_BAUD_TAG "usart_baud"
#define RS232_TAG       "rs232"

class sdk80_state : public driver_device
{
public:
	sdk80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_usart(*this, I8251A_TAG)
		, m_ppi_0(*this, I8255A_0_TAG)
		, m_ppi_1(*this, I8255A_1_TAG)
		, m_rs232(*this, RS232_TAG)
		, m_usart_baud_rate(*this, I8251A_BAUD_TAG)
		, m_usart_divide_counter(0)
		, m_usart_clock_state(0)
	{ }

	void sdk80(machine_config &config);

private:
	void usart_clock_tick(int state);

	void sdk80_io(address_map &map) ATTR_COLD;
	void sdk80_mem(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<i8251_device> m_usart;
	required_device<i8255_device> m_ppi_0;
	required_device<i8255_device> m_ppi_1;
	required_device<rs232_port_device> m_rs232;
	required_ioport m_usart_baud_rate;

	uint8_t m_usart_divide_counter;
	uint8_t m_usart_clock_state;
};

void sdk80_state::sdk80_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).rom();
	map(0x1000, 0x13ff).ram();
}

void sdk80_state::sdk80_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0xec, 0xef).rw(m_ppi_1, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xf4, 0xf7).rw(m_ppi_0, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xfa, 0xfb).rw(m_usart, FUNC(i8251_device::read), FUNC(i8251_device::write));
}

static INPUT_PORTS_START( sdk80 )
	PORT_START(I8251A_BAUD_TAG)
	PORT_DIPNAME( 0x3f, 0x01, "i8251 Baud Rate" )
	PORT_DIPSETTING(    0x01, "4800")
	PORT_DIPSETTING(    0x02, "2400")
	PORT_DIPSETTING(    0x04, "1200")
	PORT_DIPSETTING(    0x08, "600")
	PORT_DIPSETTING(    0x10, "300")
	PORT_DIPSETTING(    0x20, "150")
	PORT_DIPSETTING(    0x40, "75")
INPUT_PORTS_END

void sdk80_state::usart_clock_tick(int state)
{
	uint8_t old_counter = m_usart_divide_counter;
	m_usart_divide_counter++;

	uint8_t transition = (old_counter ^ m_usart_divide_counter) & m_usart_baud_rate->read();
	if (transition)
	{
		m_usart->write_txc(m_usart_clock_state);
		m_usart->write_rxc(m_usart_clock_state);
		m_usart_clock_state ^= 1;
	}
}

static DEVICE_INPUT_DEFAULTS_START( terminal ) // set up terminal to default to 4800
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_4800 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_4800 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void sdk80_state::sdk80(machine_config &config)
{
	/* basic machine hardware */
	I8080A(config, m_maincpu, XTAL(18'432'000)/9);
	m_maincpu->set_addrmap(AS_PROGRAM, &sdk80_state::sdk80_mem);
	m_maincpu->set_addrmap(AS_IO, &sdk80_state::sdk80_io);

	I8251(config, m_usart, 0);
	m_usart->txd_handler().set(RS232_TAG, FUNC(rs232_port_device::write_txd));
	m_usart->dtr_handler().set(RS232_TAG, FUNC(rs232_port_device::write_dtr));
	m_usart->rts_handler().set(RS232_TAG, FUNC(rs232_port_device::write_rts));

	I8255A(config, m_ppi_0);
	I8255A(config, m_ppi_1);

	RS232_PORT(config, m_rs232, default_rs232_devices, "terminal");
	m_rs232->rxd_handler().set(m_usart, FUNC(i8251_device::write_rxd));
	m_rs232->dsr_handler().set(m_usart, FUNC(i8251_device::write_dsr));
	m_rs232->cts_handler().set(m_usart, FUNC(i8251_device::write_cts));
	m_rs232->set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	clock_device &clock(CLOCK(config, "usart_clock", XTAL(18'432'000)/60));
	clock.signal_handler().set(FUNC(sdk80_state::usart_clock_tick));
}

/* ROM definition */
ROM_START( sdk80 )
	ROM_REGION( 0x01400, "maincpu", ROMREGION_ERASEFF )
	//ROM_LOAD( "mcs80.a14", 0x0000, 0x0400, BAD_DUMP CRC(3ce7bd37) SHA1(04cc67875b53d4cdfefce07041af12be3acedf4f)) // Compiled from manual listing
	ROM_LOAD( "mcs80.a14", 0x0000, 0x0400, BAD_DUMP CRC(9bb1c268) SHA1(e84e358f81f181f40f4f8d4c4f76370b7d82e615) ) // Compiled from corrected listing - see issue #6324
ROM_END

} // anonymous namespace


/*    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT   CLASS        INIT        COMPANY   FULLNAME     FLAGS */
COMP( 1975, sdk80, 0,      0,      sdk80,   sdk80,  sdk80_state, empty_init, "Intel",  "SDK-80",    MACHINE_NO_SOUND_HW )



sdk85.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/**************************************************************************************************************************************

        Intel MCS-85 System Design Kit (SDK-85)

        09/12/2009 Skeleton driver.

        22/06/2011 Working [Robbbert]

This is an evaluation kit for the 8085 cpu.

All onboard RAM and (P)ROM is contained within address-latched Intel memories with built-in I/O
(8155 and 8355/8755/8755A).

There is no speaker or storage facility in the standard kit.

Download the User Manual to get the operating procedures.

An example is Press SUBST key, enter an address, press NEXT key, enter data,
then press NEXT to increment the address.

Warning: the default cold start routine fails to initialize SP properly, which will cause the GO
command to fail. SP can be set to point to the end of onboard RAM by the following sequence of
button presses: EXAM REG, 4, 2, 0, EXEC, EXAM REG, 5, F, F, EXEC. In TTY mode, use the "XS"
command to change SP. Another option is to push the RESET button again, which will leave SP
wherever it was in the monitor's scratchpad area.

ToDo:
- Artwork


Notes on Mastermind bios.
Original author: Paolo Forlani
Ported to SDK85 by: Stefano Bodrato

Game instructions (from Stefano):
Start up, press 0 to begin. Game shows ----. You need to guess a number between 0000 and 9999.
Enter your guess, "computer" will answer showing on left with the number of found digits.
On the right you'll get a "clue", slightly different than on the standard game (making it a bit more tricky and more intriguing).
Once you find the number, you'll see it flashing.   Press the 2 key and you'll get your score (number of attempts before guessing).
Press 0 to restart.

*************************************************************************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "bus/sdk85/memexp.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8155.h"
#include "machine/i8355.h"
#include "machine/i8279.h"
#include "softlist_dev.h"
#include "sdk85.lh"


namespace {

class sdk85_state : public driver_device
{
public:
	sdk85_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_kdc(*this, "kdc")
		, m_romio(*this, "romio")
		, m_expromio(*this, "expromio")
		, m_ramio(*this, "ramio")
		, m_expramio(*this, "expramio")
		, m_tty(*this, "tty")
		, m_keyboard(*this, "X%u", 0U)
		, m_digits(*this, "digit%u", 0U)
	{ }

	void sdk85(machine_config &config);

	void reset_w(int state);
	void vect_intr_w(int state);

private:
	int sid_r();

	void scanlines_w(u8 data);
	void digit_w(u8 data);
	u8 kbd_r();

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	u8 m_digit = 0U;
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

	required_device<i8085a_cpu_device> m_maincpu;
	required_device<i8279_device> m_kdc;
	required_device<i8355_device> m_romio;
	required_device<sdk85_romexp_device> m_expromio;
	required_device<i8155_device> m_ramio;
	required_device<i8155_device> m_expramio;
	required_device<rs232_port_device> m_tty;
	required_ioport_array<3> m_keyboard;
	output_finder<6> m_digits;
};

void sdk85_state::machine_reset()
{
	// Prevent spurious TRAP when system is reset
	m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
}

void sdk85_state::machine_start()
{
	m_digits.resolve();
	save_item(NAME(m_digit));
}

void sdk85_state::reset_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? CLEAR_LINE : ASSERT_LINE);
	if (!state)
	{
		m_kdc->reset();
		m_romio->reset();
		m_expromio->reset();
		m_ramio->reset();
		m_expramio->reset();
	}
}

void sdk85_state::vect_intr_w(int state)
{
	m_maincpu->set_input_line(I8085_RST75_LINE, state ? ASSERT_LINE : CLEAR_LINE);
}

int sdk85_state::sid_r()
{
	// Actual HW has S25 switch to ground SID when using keyboard input instead of TTY RX
	if (m_tty->get_card_device() == nullptr)
		return 0;
	else
		return m_tty->rxd_r();
}

void sdk85_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).r(m_romio, FUNC(i8355_device::memory_r));
	map(0x0800, 0x0fff).rw(m_expromio, FUNC(sdk85_romexp_device::memory_r), FUNC(sdk85_romexp_device::memory_w));
	map(0x1800, 0x1800).mirror(0x06ff).rw(m_kdc, FUNC(i8279_device::data_r), FUNC(i8279_device::data_w));
	map(0x1900, 0x1900).mirror(0x06ff).rw(m_kdc, FUNC(i8279_device::status_r), FUNC(i8279_device::cmd_w));
	map(0x2000, 0x20ff).mirror(0x0700).rw(m_ramio, FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w));
	map(0x2800, 0x28ff).mirror(0x0700).rw(m_expramio, FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w));
}

void sdk85_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x03).mirror(0x04).rw(m_romio, FUNC(i8355_device::io_r), FUNC(i8355_device::io_w));
	map(0x08, 0x0b).mirror(0x04).rw(m_expromio, FUNC(sdk85_romexp_device::io_r), FUNC(sdk85_romexp_device::io_w));
	map(0x20, 0x27).rw(m_ramio, FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
	map(0x28, 0x2f).rw(m_expramio, FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
}

/* Input ports */
static INPUT_PORTS_START( sdk85 )
	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("0")      PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("1")      PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("2")      PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("3  I")   PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("4  SPH") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("5  SPL") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("6  PCH") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("7  PCL") PORT_CODE(KEYCODE_7) PORT_CHAR('7')

	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("8  H") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("9  L") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("A")    PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("B")    PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("C")    PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("D")    PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("E")    PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("F")    PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("EXEC  .")     PORT_CODE(KEYCODE_STOP)  PORT_CHAR('.')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("NEXT  ,")     PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("GO")          PORT_CODE(KEYCODE_G)     PORT_CHAR('G')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("SUBST MEM")   PORT_CODE(KEYCODE_S)     PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("EXAM REG")    PORT_CODE(KEYCODE_X)     PORT_CHAR('X')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("SINGLE STEP") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/')
	PORT_BIT(0xC0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("INTR") // buttons hardwired to 8085 inputs
	PORT_BIT(1, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("VECT INTR") PORT_WRITE_LINE_MEMBER(FUNC(sdk85_state::vect_intr_w)) PORT_CODE(KEYCODE_F2)
	PORT_BIT(2, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("RESET")     PORT_WRITE_LINE_MEMBER(FUNC(sdk85_state::reset_w))     PORT_CODE(KEYCODE_F1)
INPUT_PORTS_END


void sdk85_state::scanlines_w(u8 data)
{
	m_digit = data & 7;
}

void sdk85_state::digit_w(u8 data)
{
	if (m_digit < 6)
		m_digits[m_digit] = bitswap<8>(~data, 3, 2, 1, 0, 7, 6, 5, 4);
}

u8 sdk85_state::kbd_r()
{
	u8 data = (m_digit < 3) ? m_keyboard[m_digit]->read() : 0xff;
	return data;
}

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_110 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_110 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END

void sdk85_state::sdk85(machine_config &config)
{
	/* basic machine hardware */
	I8085A(config, m_maincpu, 6.144_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &sdk85_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &sdk85_state::io_map);
	m_maincpu->in_sid_func().set(FUNC(sdk85_state::sid_r));
	m_maincpu->out_sod_func().set(m_tty, FUNC(rs232_port_device::write_txd)).invert();

	I8355(config, m_romio, 6.144_MHz_XTAL / 2); // Monitor ROM (A14)

	SDK85_ROMEXP(config, m_expromio, 6.144_MHz_XTAL / 2, sdk85_romexp_device::rom_options, nullptr); // Expansion ROM (A15)

	I8155(config, m_ramio, 6.144_MHz_XTAL / 2); // Basic RAM (A16)
	m_ramio->out_to_callback().set_inputline(m_maincpu, I8085_TRAP_LINE);

	I8155(config, m_expramio, 6.144_MHz_XTAL / 2); // Expansion RAM (A17)

	/* video hardware */
	config.set_default_layout(layout_sdk85);

	/* Devices */
	I8279(config, m_kdc, 6.144_MHz_XTAL / 2);                               // Keyboard/Display Controller (A13)
	m_kdc->out_irq_callback().set_inputline(m_maincpu, I8085_RST55_LINE);   // irq
	m_kdc->out_sl_callback().set(FUNC(sdk85_state::scanlines_w));           // scan SL lines
	m_kdc->out_disp_callback().set(FUNC(sdk85_state::digit_w));             // display A&B
	m_kdc->in_rl_callback().set(FUNC(sdk85_state::kbd_r));                  // kbd RL lines
	m_kdc->in_shift_callback().set_constant(1);                             // Shift key
	m_kdc->in_ctrl_callback().set_constant(1);

	RS232_PORT(config, m_tty, default_rs232_devices, nullptr); // actually a 20 mA current loop
	m_tty->set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	SOFTWARE_LIST(config, "rom_list").set_original("sdk85");
}

/* ROM definition */
ROM_START( sdk85 )
	ROM_REGION( 0x800, "romio", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "default", "Default")
	ROMX_LOAD( "sdk85.a14", 0x0000, 0x0800, CRC(9d5a983f) SHA1(54e218560fbec009ac3de5cfb64b920241ef2eeb), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "mastermind", "Mastermind")
	ROMX_LOAD( "mastermind.a14", 0x0000, 0x0800, CRC(36b694ae) SHA1(4d8a5ae5d10e8f72a6e349c7eeaf1aa00c4e45e1), ROM_BIOS(1) )
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME  FLAGS */
COMP( 1977, sdk85, 0,      0,      sdk85,   sdk85, sdk85_state, empty_init, "Intel", "MCS-85 System Design Kit", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



sdk86.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Jonathan Gevaryahu
/***************************************************************************

Intel MCS-86 System Design Kit (SDK-86)
This is an evaluation kit for the 8086 cpu.

There is no speaker or storage facility in the standard kit.

Download the User Manual to get the operating procedures.
The user manual is available from: http://www.bitsavers.org/pdf/intel/8086/9800698A_SDK-86_Users_Man_Apr79.pdf

2009-05-12 Skeleton driver by Micko
2009-11-29 Some fleshing out by Lord Nightmare
2011-06-22 Working [Robbbert]


Paste Test:
       N0100^11^22^33^44^55^66^77^88^99^X0100^
       Press UP to verify the data.

ToDo:
- Add optional 2x 8255A port read/write logging

****************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/i86/i86.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/i8279.h"
#include "sdk86.lh"


namespace {

class sdk86_state : public driver_device
{
public:
	sdk86_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_uart(*this, "uart")
		, m_kdc(*this, "i8279")
		, m_ppi1(*this, "ppi1")
		, m_ppi2(*this, "ppi2")
		, m_io_keyboard(*this, "X%u", 0U)
		, m_digits(*this, "digit%u", 0U)
	{ }

	void sdk86(machine_config &config);
	DECLARE_INPUT_CHANGED_MEMBER(nmi_button);
	DECLARE_INPUT_CHANGED_MEMBER(reset_button);

private:
	void scanlines_w(u8 data);
	void digit_w(u8 data);
	u8 kbd_r();

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	u8 m_digit = 0U;
	void machine_start() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_device<i8251_device> m_uart;
	required_device<i8279_device> m_kdc;
	required_device<i8255_device> m_ppi1;
	required_device<i8255_device> m_ppi2;
	required_ioport_array<3> m_io_keyboard;
	output_finder<8> m_digits;
};

void sdk86_state::mem_map(address_map &map)
{
	map(0x00000, 0x00fff).ram(); //2K standard, or 4k (board fully populated)
	map(0xfe000, 0xfffff).rom().region("maincpu", 0);
}

void sdk86_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0xfff0, 0xfff3).mirror(4).rw(m_uart, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0xffe8, 0xffeb).mirror(4).rw(m_kdc, FUNC(i8279_device::read), FUNC(i8279_device::write)).umask16(0x00ff);
	map(0xfff8, 0xffff).rw(m_ppi1, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0xff00);
	map(0xfff8, 0xffff).rw(m_ppi2, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
}

/* Input ports */
static INPUT_PORTS_START( sdk86 )
	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0 EB/AX") PORT_CODE(KEYCODE_0) PORT_CHAR('0')  // examine/modify bytes
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 ER/BX") PORT_CODE(KEYCODE_1) PORT_CHAR('1')  // examine/modify register
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 GO/CX") PORT_CODE(KEYCODE_2) PORT_CHAR('2')  // go
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 ST/DX") PORT_CODE(KEYCODE_3) PORT_CHAR('3')  // step
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 IB/SP") PORT_CODE(KEYCODE_4) PORT_CHAR('4')  // read a byte from port
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 OB/BP") PORT_CODE(KEYCODE_5) PORT_CHAR('5')  // output byte to port
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 MV/SI") PORT_CODE(KEYCODE_6) PORT_CHAR('6')  // copy memory block
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 EW/DI") PORT_CODE(KEYCODE_7) PORT_CHAR('7')  // examine/modify words

	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 IW/CS") PORT_CODE(KEYCODE_8) PORT_CHAR('8')  // read a word from port
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 OW/DS") PORT_CODE(KEYCODE_9) PORT_CHAR('9')  // output a word to port
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A SS") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B ES") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C IP") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D FL") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_ENTER) PORT_CHAR('X')  // end current command
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA) PORT_CODE(KEYCODE_UP) PORT_CHAR('^')  // enter data and increment address
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('M')   // subtract one number from another
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("+") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('P')   // add two numbers
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_COLON) PORT_CHAR('S')   // separator between segment and offset
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("REG") PORT_CODE(KEYCODE_R) PORT_CHAR('R')     // registers
	PORT_BIT(0xC0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("INTR") PORT_CODE(KEYCODE_ESC) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sdk86_state::nmi_button), 0) PORT_CHAR('I')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Systm Reset") PORT_CODE(KEYCODE_F3) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sdk86_state::reset_button), 0) PORT_CHAR('N')
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(sdk86_state::nmi_button)
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, newval ? ASSERT_LINE : CLEAR_LINE);
}

// Reset button connects to i8284, output of this resets all major chips
INPUT_CHANGED_MEMBER(sdk86_state::reset_button)
{
	if (newval)
	{
		m_ppi1->reset();
		m_ppi2->reset();
		m_uart->reset();
		m_kdc->reset();
		m_maincpu->reset();
	}
}

void sdk86_state::scanlines_w(u8 data)
{
	m_digit = data;
}

void sdk86_state::digit_w(u8 data)
{
	if (m_digit < 8)
		m_digits[m_digit] = data;
}

u8 sdk86_state::kbd_r()
{
	u8 data = 0xff;

	if ((m_digit & 7) < 3)
		data = m_io_keyboard[m_digit & 7]->read();

	return data;
}

void sdk86_state::machine_start()
{
	m_digits.resolve();
	save_item(NAME(m_digit));
}

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_4800 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_4800 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END

void sdk86_state::sdk86(machine_config &config)
{
	/* basic machine hardware */
	I8086(config, m_maincpu, XTAL(14'745'600)/3); /* divided down by i8284 clock generator; jumper selection allows it to be slowed to 2.5MHz, hence changing divider from 3 to 6 */
	m_maincpu->set_addrmap(AS_PROGRAM, &sdk86_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &sdk86_state::io_map);

	/* video hardware */
	config.set_default_layout(layout_sdk86);

	/* Devices */
	I8251(config, m_uart, 0);
	m_uart->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_uart->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	m_uart->rts_handler().set(m_uart, FUNC(i8251_device::write_cts));

	// it's meant to interface with an intellec unit, and you need floppy disks & drives for that
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_uart, FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set(m_uart, FUNC(i8251_device::write_dsr));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	clock_device &usart_clock(CLOCK(config, "usart_clock", XTAL(14'745'600)/3/16));
	usart_clock.signal_handler().set(m_uart, FUNC(i8251_device::write_txc));
	usart_clock.signal_handler().append(m_uart, FUNC(i8251_device::write_rxc));

	I8279(config, m_kdc, 2500000);        // based on divider
	m_kdc->out_sl_callback().set(FUNC(sdk86_state::scanlines_w)); // scan SL lines
	m_kdc->out_disp_callback().set(FUNC(sdk86_state::digit_w));   // display A&B
	m_kdc->in_rl_callback().set(FUNC(sdk86_state::kbd_r));        // kbd RL lines
	m_kdc->in_shift_callback().set_constant(0);                   // Shift key
	m_kdc->in_ctrl_callback().set_constant(0);

	I8255A(config, m_ppi1);
	I8255A(config, m_ppi2);
}

/* ROM definition */
ROM_START( sdk86 )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF ) // all are Intel D2616 ?eproms with the windows painted over? (factory programmed eproms? this would match the 'i8642' marking on the factory programmed eprom version of the AT keyboard mcu...)
	/* Note that the rom pairs at FE000-FEFFF and FF000-FFFFF are
	   interchangeable; the ones at FF000-FFFFF are the ones which start on
	   bootup, and the other ones live at FE000-FEFFF and can be switched in by
	   the user. One pair is the Serial RS232 Monitor and the other is the
	   Keypad/front panel monitor. On the SDK-86 I (LN) dumped, the Keypad
	   monitor was primary, but the other SDK-86 I know of has the roms in
	   the opposite arrangement (Serial primary). */
	// Keypad Monitor Version 1.1 (says "- 86   1.1" on LED display at startup)
	ROM_SYSTEM_BIOS( 0, "keypad", "Keypad Monitor" )
	ROMX_LOAD( "0456_104531-001.a36", 0x0000, 0x0800, CRC(f9c4a809) SHA1(aea324c3f52dd393f1eed2b856ba11f050a35b93), ROM_SKIP(1) | ROM_BIOS(0) ) /* Label: "iD2616 // T142099WS // (C)INTEL '77 // 0456 // 104531-001" */
	ROMX_LOAD( "0457_104532-001.a37", 0x0001, 0x0800, CRC(a245ba5c) SHA1(7f67277f866fca5377cb123e9cc405b5fdfe61d3), ROM_SKIP(1) | ROM_BIOS(0) ) /* Label: "iD2616 // T145054WS // (C)INTEL '77 // 0457 // 104532-001" */
	ROMX_LOAD( "0169_102042-001.a27", 0x1000, 0x0800, CRC(3f46311a) SHA1(a97e6861b736f26230b9adbf5cd2576a9f60d626), ROM_SKIP(1) | ROM_BIOS(0) ) /* Label: "iD2616 // T142094WS // (C)INTEL '77 // 0169 // 102042-001" */
	ROMX_LOAD( "0170_102043-001.a30", 0x1001, 0x0800, CRC(65924471) SHA1(5d258695bf585f89179dfa0a113a0eeeabd5ee2b), ROM_SKIP(1) | ROM_BIOS(0) ) /* Label: "iD2616 // T145056WS // (C)INTEL '77 // 0170 // 102043-001" */
	// Serial Monitor Version 1.2 (says "  86   1.2" on LED display at startup, and sends a data prompt over serial)
	ROM_SYSTEM_BIOS( 1, "serial", "Serial Monitor" )
	ROMX_LOAD( "0169_102042-001.a36", 0x0000, 0x0800, CRC(3f46311a) SHA1(a97e6861b736f26230b9adbf5cd2576a9f60d626), ROM_SKIP(1) | ROM_BIOS(1) ) /* Label: "iD2616 // T142094WS // (C)INTEL '77 // 0169 // 102042-001" */
	ROMX_LOAD( "0170_102043-001.a37", 0x0001, 0x0800, CRC(65924471) SHA1(5d258695bf585f89179dfa0a113a0eeeabd5ee2b), ROM_SKIP(1) | ROM_BIOS(1) ) /* Label: "iD2616 // T145056WS // (C)INTEL '77 // 0170 // 102043-001" */
	ROMX_LOAD( "0456_104531-001.a27", 0x1000, 0x0800, CRC(f9c4a809) SHA1(aea324c3f52dd393f1eed2b856ba11f050a35b93), ROM_SKIP(1) | ROM_BIOS(1) ) /* Label: "iD2616 // T142099WS // (C)INTEL '77 // 0456 // 104531-001" */
	ROMX_LOAD( "0457_104532-001.a30", 0x1001, 0x0800, CRC(a245ba5c) SHA1(7f67277f866fca5377cb123e9cc405b5fdfe61d3), ROM_SKIP(1) | ROM_BIOS(1) ) /* Label: "iD2616 // T145054WS // (C)INTEL '77 // 0457 // 104532-001" */

	/* proms:
	 * dumped 11/21/09 through 11/29/09 by LN
	 * purposes: (according to sdk-86 user manual from http://www.bitsavers.org/pdf/intel/8086/9800698A_SDK-86_Users_Man_Apr79.pdf)
	 * A12: main address decoding (selects ram or rom or open bus/offboard, see page 2-7)
	 * A22: I/O decoding for 8251, 8279 and optional pair of 8255 chips (in the FFE8-FFFF I/O area; see page 2-6)
	 * A26: ROM address decoding for selecting which of the 4 pairs of roms is active (note that to use the FCxxx and FDxxx pairs requires wiring them into the prototype area, they are not standard; see page 2-5)
	 * A29: RAM address decoding (see page 2-4)
	 */
	ROM_REGION(0x1000, "proms", 0 ) // all are Intel D3625A 1kx4 (82s137A equivalent)
	ROM_LOAD( "0036_101993-001.a12", 0x0000, 0x0400, CRC(bb7edbfd) SHA1(8847f9815c7cb8695986743199673920a7d4390d)) /* Label: "iD3625A 0036 // 8142 // 101993-001" */
	ROM_LOAD( "0035_101992-001.a22", 0x0400, 0x0400, CRC(76aced0c) SHA1(89fa39473e19d8cb6b65d6430d3d683ae2398fb3)) /* Label: "iD3625A 0035 // 8142 // 101992-001" */
	ROM_LOAD( "0037_101994-001.a26", 0x0800, 0x0400, CRC(d6f33d30) SHA1(41e794bf202266fa57516403e6a80ebbf6c95fdc)) /* Label: "iD3625A 0037 // 8142 // 101994-001" */
	ROM_LOAD( "0038_101995-001.a29", 0x0C00, 0x0400, CRC(3d2c18bc) SHA1(5e1935cd07fef26b2cf3d8fa7612fe0d8e678c06)) /* Label: "iD3625A 0038 // 8142 // 101995-001" */
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY   FULLNAME  FLAGS */
COMP( 1979, sdk86, 0,      0,      sdk86,   sdk86, sdk86_state, empty_init, "Intel",  "MCS-86 System Design Kit", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



seattlecmp.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Seattle Computer SCP-300F S100 card. It has sockets on the card for
one serial and 2 parallel connections.

2013-08-14 Skeleton driver.

When started you must press Enter twice before anything happens.

All commands must be in UPPER case.

Known Commands:
B : Boot from disk?
D : Dump memory
E : Edit memory
F : Find
G : Go?
I : Input port
M : Move
O : Output port
R : Display / Modify Registers
S : Search
T : Trace

Chips on the board: 8259 x2; AM9513; 8251; 2716 ROM (MON-86 V1.5TDD)
There is a 4MHz crystal connected to the 9513.

****************************************************************************/

#include "emu.h"
#include "cpu/i86/i86.h"
#include "machine/am9513.h"
#include "machine/i8251.h"
#include "machine/pic8259.h"
#include "bus/rs232/rs232.h"
//#include "bus/s100/s100.h"


namespace {

class seattle_comp_state : public driver_device
{
public:
	seattle_comp_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pic(*this, "pic%u", 1U)
		, m_monitor(*this, "monitor")
	{ }

	void seattle(machine_config &config);

private:
	u8 pic_slave_ack(offs_t offset);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device_array<pic8259_device, 2> m_pic;
	required_region_ptr<u8> m_monitor;
};


u8 seattle_comp_state::pic_slave_ack(offs_t offset)
{
	if (offset == 1)
		return m_pic[1]->acknowledge();

	return 0;
}


void seattle_comp_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0xff7ff).ram();
	map(0xff800, 0xfffff).lr8([this](offs_t offset) { return m_monitor[offset]; }, "monitor_r");
}

void seattle_comp_state::io_map(address_map &map)
{
	//map.unmap_value_high();
	map.global_mask(0xff);
	map(0xf0, 0xf1).rw("pic1", FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0xf2, 0xf3).rw("pic2", FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0xf4, 0xf5).rw("stc", FUNC(am9513_device::read8), FUNC(am9513_device::write8));
	map(0xf6, 0xf7).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	//map(0xfc, 0xfd) Parallel data, status, serial DCD
	//map(0xfe, 0xff) Eprom disable bit, read sense switches (bank of 8 dipswitches)
}

/* Input ports */
static INPUT_PORTS_START( seattle )
INPUT_PORTS_END


// bit 7 needs to be stripped off, we do this by choosing 7 bits and even parity
static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_19200 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_19200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END

void seattle_comp_state::seattle(machine_config &config)
{
	/* basic machine hardware */
	I8086(config, m_maincpu, 24_MHz_XTAL / 3); // 8 MHz or 4 MHz selectable
	m_maincpu->set_addrmap(AS_PROGRAM, &seattle_comp_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &seattle_comp_state::io_map);
	m_maincpu->set_irq_acknowledge_callback("pic1", FUNC(pic8259_device::inta_cb));

	PIC8259(config, m_pic[0]);
	m_pic[0]->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_INT0);
	m_pic[0]->read_slave_ack_callback().set(FUNC(seattle_comp_state::pic_slave_ack));

	PIC8259(config, m_pic[1]);
	m_pic[1]->out_int_callback().set(m_pic[0], FUNC(pic8259_device::ir1_w));

	am9513_device &stc(AM9513(config, "stc", 4_MHz_XTAL)); // dedicated XTAL
	stc.out2_cb().set(m_pic[1], FUNC(pic8259_device::ir0_w));
	stc.out3_cb().set(m_pic[1], FUNC(pic8259_device::ir4_w));
	stc.out4_cb().set(m_pic[1], FUNC(pic8259_device::ir7_w));
	stc.out5_cb().set("uart", FUNC(i8251_device::write_txc));
	stc.out5_cb().append("uart", FUNC(i8251_device::write_rxc));
	stc.fout_cb().set("stc", FUNC(am9513_device::source1_w));
	// FOUT not shown on schematics, which inexplicably have Source 1 tied to Gate 5

	i8251_device &uart(I8251(config, "uart", 24_MHz_XTAL / 12)); // CLOCK on line 49
	uart.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	uart.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	uart.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	uart.rxrdy_handler().set("pic2", FUNC(pic8259_device::ir1_w));
	uart.txrdy_handler().set("pic2", FUNC(pic8259_device::ir5_w));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("uart", FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set("uart", FUNC(i8251_device::write_dsr));
	rs232.cts_handler().set("uart", FUNC(i8251_device::write_cts));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));
}

/* ROM definition */
ROM_START( scp300f )
	ROM_REGION( 0x800, "monitor", 0 )
	ROM_LOAD( "mon86 v1.5tdd", 0x0000, 0x0800, CRC(7db23169) SHA1(c791b02ca33a4e1f8e95eb541624a59738f378c4))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS               INIT        COMPANY            FULLNAME    FLAGS
COMP( 1986, scp300f, 0,      0,      seattle, seattle, seattle_comp_state, empty_init, "Seattle Computer", "SCP-300F", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



secoinsa20.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:

/***********************************************************************************************************************************

Skeleton driver for Secoinsa Serie 20 minicomputer.

Secoinsa (Sociedad Española de Comunicaciones e Informática, S.A.) was a company founded in 1975 by Fujitsu, the Spanish
National Institute for Industry (Instituto Nacional de Industria, INI), Telefónica, and other investors (mainly banks).
It primarily manufactured and sold FACOM clones for the Spanish market.

In 1986, the company was split and sold. Telefónica acquired the professional services division, while Fujitsu España
took the hardware and software areas.

Key features for Secoinsa Serie 20:
    -CPU: Z80B.
    -RAM: 80 Kbytes, expandable to 272 Kbytes.
    -Peripherals: Terminal interface, RS-232 serial port for printer, two sync/async communication channels.
    -Screen: Green phosphor with 24 lines of 80 characters, with an option for NSP terminal with 12/25 lines of 69/80/132 characters.
    -Keyboard: QWERTY, 102 keys.
    -Mass storage: One or two 8" floppy disk drives of 640 Kbytes or 1.2 Mbytes. Winchester hard drives of 10 or 20 Mbytes.
    -Operating System: OASIS.
    -Programming languages: BASIC (interpreter and compiler), Spanish EXEC.

The dumped Secoinsa Serie 20 was found with the following PCBs:

PCB SECOINSA SM16B-0200-0170
     _______________________________________________________________
    |    __________    __________    __________       __________   |
    |   |SN74LS30N|   |SN74LS112AN  |_PAL_3U__|  ::  |SN74LS125AN  |
    |    __________    __________    __________  ::   __________   |
    |   |SN74LS240N   |SN74LS74AN   |SN74LS125AN ::  |MC 1489AL|  _|_
    |                                __________                  |   |
    |    __________    __________   |_SN7417N_|  ::   __________ |   |
  __|   |SN74LS374N   |SN74LS112AN   __________  ::  |MC 1489AL| |   |
 |__                                |_SN7416N_|  ::              |   |
 |__     __________    __________    __________       __________ |   |
 |__ ·· |SN74LS374N   |_74F138__|   |SN74LS74AN  ::  |SN75188J_| |   |
 |__ ··  __________    __________    __________  ::   __________ |   |
 |__ ·· |SN74LS374N   |_74F138__|   |SN74LS08N|  ::  |_DS3486N_| |   |
 |__ ··  __________    __________    __________       __________ |   |
 |__ ·· |SN74LS374N   |_PAL_2P__|   |SN74LS74AN ·· : |SN75188J_| |   |
 |__                                ··········     :             |   |
 |__     __________      ____________________         __________ |   |
 |__    |SN74LS240N     | AMD Z8530PC       |        |MC_1489AL| |___|
 |__                    |___________________|         __________   |
 |__     __________         ··  ::::::::::           |_MC3487L_|   |
 |__    |SN74LS244N      ____________________         __________   |
 |__                    | AMD P8237A-5      |        |SN74LS112AN  |
 |__     __________     |___________________|         __________   |
 |__    |SN74LS244N             ::::::::::           |SN74LS112AN  |
 |__                     ____________________         __________   |
 |__     __________     | AMD P8237A-5      |        |_DS3486N_|   |
 |__    |SN74LS245N     |___________________|         __________   |
 |__                            :::..:::::           |MC_1489AL|   |
 |__     __________      ____________________         __________  _|_
 |__    |SN74LS373N     | AMD Z8530PC       |        |_MC3487L_| |   |
 |__     __________     |___________________|         __________ |   |
 |__    |SN74LS245N                               :  |MC 1489AL| |   |
 |__     __________    __________    __________   :   __________ |   |
 |__    |SN74LS244N   SM25LS2521PC  |SN74LS74AN   :  |SN75188J_| |   |
 |__     __________    __________    __________  ::   __________ |   |
 |__    |SN74LS74AN   |SN74LS112AN  |SN74LS112AN  :  |_MC3486P_| |   |
    |    __________    __________    __________   :   __________ |   |
    |   |_PAL_1D__| ..|SN74LS74AN   |SN74LS09N|      |SN75188J_| |   |
    |    __________ .. __________                ..              |   |
    |   |_PAL_1C__| ..|SN74LS74AN :  __________  ..   __________ |   |
    |    __________ .. __________   |_SN7416N_|  ..  |MC_1489AL| |   |
    |   |_PAL_1B__| ..|SN74LS08N|                                |___|
    |    __________    __________    __________       __________   |
    |   |_SN7417N_|   |SN74LS125AN  |_SN7417N_|      |MC_1489AL|   |
    |    __________    __________    __________       __________   |
    |   |SN74LS165N   |SN74LS165N   |SN74LS74AN      |MC14040BCP   |
    |______________________________________________________________|



PCB SECOINSA SM16B-0200-0140:
 -Floppy controller with FDC9229BT, NEC D765AC.
 -Serial communications with Z8530DC, Signetics 2651.
 -RTC with MM58167AN.
     _______________________________________________________________
    |    __________     __________    __________       __________  |
    |__ |SN74LS165AN   |MC14538BCP   |SN74LS74AN      |_PAL_1A__|  |
    |__| __________     __________    __________       __________  |
    |__|| DIPS x 8| .. |SN74LS139AN  |SN74LS74AN      |_DM7416N_|  |
    |    __________ ··  __________    __________       __________  |
    |__ |_DM7416N_| ·· |_SN7417N_|   |SN74LS74AN  :   AM25LS252IPC |
    |__| ________________________                                  |
    |__|| NEC D765AC            |     __________       __________  |___
    |__||                       |    |SN74LS04N|      |DM74LS244N   __|
    |__||_______________________|                                   __|
    |__| __________     __________    __________       __________   __|
    |__||DM74LS244N    |SN74LS139AN  |SN74LS240N      |SN74LS245N   __|
    |__|                    .. :                                    __|
    |__| __________                   __________       __________   __|
    |__||SN74LS240N     ::::::       |SN74LS245N      |SN74LS46INS  __|
    |__| __________     __________    __________       __________   __|
    |   |_SN7417N_|    |FDC9229BT|   |_PAL_2G__|      |SN74LS46INS  __|
    |__  ________________             __________       __________   __|
    |__||S 8212 2651 N  |            |74SC374P_|      |_PAL_1H__|   __|
    |__||_______________|             __________       __________   __|
    |__| ________________________    |_SN7417N_|      |DM74LS244N   __|
    |__||AMD Z8530DC            |     __________       __________   __|
    |__||                       |    |SN74LS240N      |_PAL_1K__|   __|
    |__||_______________________|                      __________   __|
    |__| __________     __________    __________      |SN74LS46INS  __|
    |__||SN75189AJ|    | DIPS x 8|   |SN74LS148N        :::::::::   __|
    |__| __________ ..  __________    __________      ___________   __|
    |__||_SN7417N_| ·· |_DM7416N_|   |_PAL_2M__|     | P8254    |   __|
    |    __________ ..  __________    __________     |__________|   __|
    |__ |_DM7416N_| .. |SN74LS74AN   |SN74LS125AN      __________   __|
    |__|            ..    Xtal          Xtal          |_PAL_1N__|  |
    |__|                 16 MHz       4.9152 MHz      ___________  |
    |    __________     __________    __________     |MM58167AN |  |
    |   |SN74LS74AN    |SN74LS74AN   |SN74LS374N     |__________|  |
    |    __________     __________    __________                   |
    |   |SN74S74N_|    |SN74LS165AN  |SN75188J_|                   |
    |______________________________________________________________|



CPU PCB
     _______________________________________________________________
    |   ___                          __________       _________    |
    |  |  |                         |SN74LS74AN      |74LS08N_|    |
    |  |EMPTY                        __________       __________   |
    |  |__|                         |_74LS04N_|      |SN74LS373N   |
    |                                                              |
    |   __________       ______________________       __________   |
    |  |SN74LS21N|      | ZILOG Z8400B PS     |      |SN74LS373N   |
    |                   | Z80B CPU            |                    |
    |   __________      |_____________________|       __________   |
    |  |DIPS x8  |                                   |DM81LS95N|   |___
    |   __________     __________    __________       __________    __|
    |  |SN74LS00N|    |SN74LS373N   |_74LS04N_|      |SN74LS244N    __|
    |   __________   _____________   __________       __________    __|
    |  |SL74LS11N|  | EPROM      |  |DIPS x 8 |      |SN74LS244N    __|
    |   __________  |____________|   __________       __________    __|
    |  |SN74LS74AN   _____________  |DM74LS266N      |SN74LS244N    __|
    |   __________  | EMPTY      |   __________       __________    __|
    |  |SN74LS165AN |____________|  |_PAL_2H__|      |SN74244N_|    __|
    |   __________     __________    __________       __________    __|
    |  |SN74LS00N|    |_74LS04N_|   |_74LS02N_| Xtal |_74LS08N_|    __|
    |   __________     __________    __________ 4MHz  __________    __|
   _|_ |SN74LS10N|    |_74LS02N_|   |_74LS04N_|      |SN74LS367AN   __|
  |   | __________     __________    __________       __________    __|
  |   ||DM74LS161AN   |_74LS04N_|   |SN74LS125AN     |SN74LS367AN   __|
  |   | __________     __________    __________       __________    __|
  |   ||74LS157N_|    |_74LS33N_|   |SN74LS273N      |SN74LS244N    __|
  |   | __________     __________    __________       __________    __|
  |   ||74LS74AN_|    |SN74LS10N|   |SN74LS273N      |_74LS32N_|    __|
  |   | __________     __________    __________       __________    __|
  |   ||_74LS04N_|    |DM74LS245N   |SN74LS148N      |_74LS32N_|    __|
  |___|                __________                                  |
    |     Xtal        |SN74LS244N                                  |
    |    20 MHz                                                    |
    |______________________________________________________________|



PCB SECOINSA SM16B-0200-0010
     _______________________________________________________________
    |                                               __________     |
    |                                __________    SN74LS165AN     |
    |                               |SN74LS157N     __________     |
    |                                __________    |SN74LS04N|     |
    |                            ·· |SN74LS393N     __________     |
   _|_                           ··  __________    |SN74LS32N|     |
  |   |                             |SN74LS393N     __________     |
  |   |                              __________    |SN74LS74N|     |
  |   |                             |74LS138N_|     __________     |
  |   |                                            |LS74LS74AN     |___
  |   |                              __________     __________      __|
  |   |                             |DM74LS374N    |SN74LS240N      __|
  |   |                              __________     __________      __|
  |___|                             |SN74LS245N    |SN74LS240N      __|
    |                                               __________      __|
    |                                              AM25LS2521PC     __|
    |                                __________     __________      __|
    |                               |SN74LS74AN    |SN74LS273N      __|
    |                                __________     __________      __|
    |                               |SN74LS155N    |SN74LS244N      __|
    |                         ··     __________     __________      __|
    |         _____________  ···    |PAL16L8 2K    AM25LS2521PC     __|
    |        |AM9128-15PC |    .     __________     __________  ..  __|
   _|_       |____________|    :    |PAL16L8 2L    |_DIPSx8__|  ..  __|
  |   |       _____________          __________     __________  ..  __|
  |   |      |AM9128-15PC |         |PAL16L8 2M    AM25LS2521PC ..  __|
  |   |      |____________|          __________     __________  ..  __|
  |   |       __________            |SN74LS273N    |_DIPSx8__|  ..  __|
  |   |      |NS74LS374N             __________     __________      __|
  |   |       __________            |SN74LS280N    |SN74LS139AN     __|
  |   |      |SN74LS245N             __________     __________      __|
  |___|       __________            |SN74LS273N    |SN74LS74AN     |
    |        |SN74LS240N                        ··                 |
    |         __________             __________ ··  __________     |
    |        |SN74LS393N            |SN74LS374N ·· |SN74LS74AN     |
    |                                           ··                 |
    |                                           ··                 |
    |______________________________________________________________|


Six RAM PCBs:

RAM PCB 1 (8 x HM6116P-3)
     _______________________________________________________________
    |  _______   _______   _______   _______   _______             |
    | |      |  |      |  |      |  |      |  |      |  ___   ___  |
    | |      |  |      |  |      |  |      |  |      | |  |  |  |  |
    | |HM    |  |HM    |  |HM    |  |EMPTY |  |EMPTY | | <-74LS138N|
    | |6116P |  |6116P |  |6116P |  |      |  |      | |  |  | <-74LS138N
    | |3     |  |3     |  |3     |  |      |  |      | |  |  |  |  |
    | |______|  |______|  |______|  |______|  |______| |__|  |__|  |
    |  _______   _______   _______   _______   _______             |
    | |      |  |      |  |      |  |      |  |      |             |
    | |      |  |      |  |      |  |      |  |      |  _________  |___
    | |HM    |  |HM    |  |HM    |  |EMPTY |  |EMPTY | |SN74LS10N   __|
    | |6116P |  |6116P |  |6116P |  |      |  |      |              __|
    | |3     |  |3     |  |3     |  |      |  |      |  __________  __|
    | |______|  |______|  |______|  |______|  |______| |SN74LS244N  __|
    |  _______   _______   _______   _______   _______              __|
    | |      |  |      |  |      |  |      |  |      |  __________  __|
    | |      |  |      |  |      |  |      |  |      | |SN74LS244N  __|
    | |HM    |  |HM    |  |EMPTY |  |EMPTY |  |EMPTY |              __|
    | |6116P |  |6116P |  |      |  |      |  |      |  __________  __|
    | |3     |  |3     |  |      |  |      |  |      | |SN74LS244N  __|
    | |______|  |______|  |______|  |______|  |______|              __|
    |  _______   _______   _______   _______   _______  __________  __|
    | |      |  |      |  |      |  |      |  |      | |SN74LS244N  __|
    | |      |  |      |  |      |  |      |  |      |              __|
    | |EMPTY |  |EMPTY |  |EMPTY |  |EMPTY |  |EMPTY |  _________   __|
    | |      |  |      |  |      |  |      |  |      | |74LS138N|   __|
    | |      |  |      |  |      |  |      |  |      |              __|
    | |______|  |______|  |______|  |______|  |______|  _________   __|
    |  _______   _______   _______   _______   _______ |SN74LS08N   __|
    | |      |  |      |  |      |  |      |  |      |         :::  __|
    | |      |  |      |  |      |  |      |  |      |  __________  __|
    | |EMPTY |  |EMPTY |  |EMPTY |  |EMPTY |  |EMPTY | AM25LS252IPC __|
    | |      |  |      |  |      |  |      |  |      |              __|
    | |      |  |      |  |      |  |      |  |      |  _________   __|
    | |______|  |______|  |______|  |______|  |______| SN74ALS133N  __|
    |  _______   _______   _______   _______   _______              __|
    | |      |  |      |  |      |  |      |  |      |              __|
    | |      |  |      |  |      |  |      |  |      |  __________  __|
    | |EMPTY |  |EMPTY |  |EMPTY |  |EMPTY |  |EMPTY | |DIPS x 8 |  __|
    | |      |  |      |  |      |  |      |  |      |              __|
    | |      |  |      |  |      |  |      |  |      |             |
    | |______|  |______|  |______|  |______|  |______|  ___   ___  |
    |                                _______   _______ |  |  |  |  |
    |                               |      |  |      | | <-74LS138N|
    |                               |      |  |      | |  |  | <-74LS138N
    |                               |EMPTY |  |EMPTY | |  |  |  |  |
    |                               |      |  |      | |__|  |__|  |
    |                               |      |  |      |             |
    |                               |______|  |______|             |
    |______________________________________________________________|

RAM PCB 2 (24 x HM6116P-3)
     _______________________________________________________________
    |  _______   _______   _______   _______   _______             |
    | |      |  |      |  |      |  |      |  |      |  ___   ___  |
    | |      |  |      |  |      |  |      |  |      | |  |  |  |  |
    | |HM    |  |HM    |  |HM    |  |EMPTY |  |EMPTY | | <-74LS138N|
    | |6116P |  |6116P |  |6116P |  |      |  |      | |  |  | <-74LS138N
    | |3     |  |3     |  |3     |  |      |  |      | |  |  |  |  |
    | |______|  |______|  |______|  |______|  |______| |__|  |__|  |
    |  _______   _______   _______   _______   _______             |
    | |      |  |      |  |      |  |      |  |      |             |
    | |      |  |      |  |      |  |      |  |      |  _________  |___
    | |HM    |  |HM    |  |HM    |  |EMPTY |  |EMPTY | |SN74LS10N   __|
    | |6116P |  |6116P |  |6116P |  |      |  |      |              __|
    | |3     |  |3     |  |3     |  |      |  |      |  __________  __|
    | |______|  |______|  |______|  |______|  |______| |SN74LS244N  __|
    |  _______   _______   _______   _______   _______              __|
    | |      |  |      |  |      |  |      |  |      |  __________  __|
    | |      |  |      |  |      |  |      |  |      | |SN74LS244N  __|
    | |HM    |  |HM    |  |EMPTY |  |EMPTY |  |EMPTY |              __|
    | |6116P |  |6116P |  |      |  |      |  |      |  __________  __|
    | |3     |  |3     |  |      |  |      |  |      | |SN74LS244N  __|
    | |______|  |______|  |______|  |______|  |______|              __|
    |  _______   _______   _______   _______   _______  __________  __|
    | |      |  |      |  |      |  |      |  |      | |SN74LS244N  __|
    | |      |  |      |  |      |  |      |  |      |              __|
    | |HM    |  |HM    |  |HM    |  |HM    |  |EMPTY |  _________   __|
    | |6116P |  |6116P |  |6116P |  |6116P |  |      | |74LS138N|   __|
    | |3     |  |3     |  |3     |  |3     |  |      |              __|
    | |______|  |______|  |______|  |______|  |______|  _________   __|
    |  _______   _______   _______   _______   _______ |SN74LS08N   __|
    | |      |  |      |  |      |  |      |  |      |         :::  __|
    | |      |  |      |  |      |  |      |  |      |  __________  __|
    | |HM    |  |HM    |  |HM    |  |HM    |  |HM    | AM25LS252IPC __|
    | |6116P |  |6116P |  |6116P |  |6116P |  |6116P |              __|
    | |3     |  |3     |  |3     |  |3     |  |3     |  _________   __|
    | |______|  |______|  |______|  |______|  |______| SN74ALS133N  __|
    |  _______   _______   _______   _______   _______              __|
    | |      |  |      |  |      |  |      |  |      |              __|
    | |      |  |      |  |      |  |      |  |      |  __________  __|
    | |HM    |  |HM    |  |HM    |  |HM    |  |HM    | |DIPS x 8 |  __|
    | |6116P |  |6116P |  |6116P |  |6116P |  |6116P |              __|
    | |3     |  |3     |  |3     |  |3     |  |3     |             |
    | |______|  |______|  |______|  |______|  |______|  ___   ___  |
    |                                _______   _______ |  |  |  |  |
    |                               |      |  |      | | <-74LS138N|
    |                               |      |  |      | |  |  | <-74LS138N
    |                               |HM    |  |HM    | |  |  |  |  |
    |                               |6116P |  |6116P | |__|  |__|  |
    |                               |3     |  |3     |             |
    |                               |______|  |______|             |
    |______________________________________________________________|

RAM PCB 3 (24 x HM6116P-3)
     _______________________________________________________________
    |  _______   _______   _______   _______   _______             |
    | |      |  |      |  |      |  |      |  |      |  ___   ___  |
    | |      |  |      |  |      |  |      |  |      | |  |  |  |  |
    | |HM    |  |HM    |  |HM    |  |EMPTY |  |EMPTY | | <-74LS138N|
    | |6116P |  |6116P |  |6116P |  |      |  |      | |  |  | <-74LS138N
    | |3     |  |3     |  |3     |  |      |  |      | |  |  |  |  |
    | |______|  |______|  |______|  |______|  |______| |__|  |__|  |
    |  _______   _______   _______   _______   _______             |
    | |      |  |      |  |      |  |      |  |      |             |
    | |      |  |      |  |      |  |      |  |      |  _________  |___
    | |HM    |  |HM    |  |HM    |  |EMPTY |  |EMPTY | |SN74LS10N   __|
    | |6116P |  |6116P |  |6116P |  |      |  |      |              __|
    | |3     |  |3     |  |3     |  |      |  |      |  __________  __|
    | |______|  |______|  |______|  |______|  |______| |SN74LS244N  __|
    |  _______   _______   _______   _______   _______              __|
    | |      |  |      |  |      |  |      |  |      |  __________  __|
    | |      |  |      |  |      |  |      |  |      | |SN74LS244N  __|
    | |HM    |  |HM    |  |EMPTY |  |EMPTY |  |EMPTY |              __|
    | |6116P |  |6116P |  |      |  |      |  |      |  __________  __|
    | |3     |  |3     |  |      |  |      |  |      | |SN74LS244N  __|
    | |______|  |______|  |______|  |______|  |______|              __|
    |  _______   _______   _______   _______   _______  __________  __|
    | |      |  |      |  |      |  |      |  |      | |SN74LS244N  __|
    | |      |  |      |  |      |  |      |  |      |              __|
    | |HM    |  |HM    |  |HM    |  |HM    |  |EMPTY |  _________   __|
    | |6116P |  |6116P |  |6116P |  |6116P |  |      | |74LS138N|   __|
    | |3     |  |3     |  |3     |  |3     |  |      |              __|
    | |______|  |______|  |______|  |______|  |______|  _________   __|
    |  _______   _______   _______   _______   _______ |SN74LS08N   __|
    | |      |  |      |  |      |  |      |  |      |         :::  __|
    | |      |  |      |  |      |  |      |  |      |  __________  __|
    | |HM    |  |HM    |  |HM    |  |HM    |  |HM    | AM25LS252IPC __|
    | |6116P |  |6116P |  |6116P |  |6116P |  |6116P |              __|
    | |3     |  |3     |  |3     |  |3     |  |3     |  _________   __|
    | |______|  |______|  |______|  |______|  |______| SN74ALS133N  __|
    |  _______   _______   _______   _______   _______              __|
    | |      |  |      |  |      |  |      |  |      |              __|
    | |      |  |      |  |      |  |      |  |      |  __________  __|
    | |HM    |  |HM    |  |HM    |  |HM    |  |HM    | |DIPS x 8 |  __|
    | |6116P |  |6116P |  |6116P |  |6116P |  |6116P |              __|
    | |3     |  |3     |  |3     |  |3     |  |3     |             |
    | |______|  |______|  |______|  |______|  |______|  ___   ___  |
    |                                _______   _______ |  |  |  |  |
    |                               |      |  |      | | <-74LS138N|
    |                               |      |  |      | |  |  | <-74LS138N
    |                               |HM    |  |HM    | |  |  |  |  |
    |                               |6116P |  |6116P | |__|  |__|  |
    |                               |3     |  |3     |             |
    |                               |______|  |______|             |
    |______________________________________________________________|

RAM PCB 4 (24 x HM6116P-3)
     _______________________________________________________________
    |  _______   _______   _______   _______   _______             |
    | |      |  |      |  |      |  |      |  |      |  ___   ___  |
    | |      |  |      |  |      |  |      |  |      | |  |  |  |  |
    | |HM    |  |HM    |  |HM    |  |EMPTY |  |EMPTY | | <-74LS138N|
    | |6116P |  |6116P |  |6116P |  |      |  |      | |  |  | <-74LS138N
    | |3     |  |3     |  |3     |  |      |  |      | |  |  |  |  |
    | |______|  |______|  |______|  |______|  |______| |__|  |__|  |
    |  _______   _______   _______   _______   _______             |
    | |      |  |      |  |      |  |      |  |      |             |
    | |      |  |      |  |      |  |      |  |      |  _________  |___
    | |HM    |  |HM    |  |HM    |  |EMPTY |  |EMPTY | |SN74LS10N   __|
    | |6116P |  |6116P |  |6116P |  |      |  |      |              __|
    | |3     |  |3     |  |3     |  |      |  |      |  __________  __|
    | |______|  |______|  |______|  |______|  |______| |SN74LS244N  __|
    |  _______   _______   _______   _______   _______              __|
    | |      |  |      |  |      |  |      |  |      |  __________  __|
    | |      |  |      |  |      |  |      |  |      | |SN74LS244N  __|
    | |HM    |  |HM    |  |EMPTY |  |EMPTY |  |EMPTY |              __|
    | |6116P |  |6116P |  |      |  |      |  |      |  __________  __|
    | |3     |  |3     |  |      |  |      |  |      | |SN74LS244N  __|
    | |______|  |______|  |______|  |______|  |______|              __|
    |  _______   _______   _______   _______   _______  __________  __|
    | |      |  |      |  |      |  |      |  |      | |SN74LS244N  __|
    | |      |  |      |  |      |  |      |  |      |              __|
    | |HM    |  |HM    |  |HM    |  |HM    |  |EMPTY |  _________   __|
    | |6116P |  |6116P |  |6116P |  |6116P |  |      | |74LS138N|   __|
    | |3     |  |3     |  |3     |  |3     |  |      |              __|
    | |______|  |______|  |______|  |______|  |______|  _________   __|
    |  _______   _______   _______   _______   _______ |SN74LS08N   __|
    | |      |  |      |  |      |  |      |  |      |         :::  __|
    | |      |  |      |  |      |  |      |  |      |  __________  __|
    | |HM    |  |HM    |  |HM    |  |HM    |  |HM    | AM25LS252IPC __|
    | |6116P |  |6116P |  |6116P |  |6116P |  |6116P |              __|
    | |3     |  |3     |  |3     |  |3     |  |3     |  _________   __|
    | |______|  |______|  |______|  |______|  |______| SN74ALS133N  __|
    |  _______   _______   _______   _______   _______              __|
    | |      |  |      |  |      |  |      |  |      |              __|
    | |      |  |      |  |      |  |      |  |      |  __________  __|
    | |HM    |  |HM    |  |HM    |  |HM    |  |HM    | |DIPS x 8 |  __|
    | |6116P |  |6116P |  |6116P |  |6116P |  |6116P |              __|
    | |3     |  |3     |  |3     |  |3     |  |3     |             |
    | |______|  |______|  |______|  |______|  |______|  ___   ___  |
    |                                _______   _______ |  |  |  |  |
    |                               |      |  |      | | <-74LS138N|
    |                               |      |  |      | |  |  | <-74LS138N
    |                               |HM    |  |HM    | |  |  |  |  |
    |                               |6116P |  |6116P | |__|  |__|  |
    |                               |3     |  |3     |             |
    |                               |______|  |______|             |
    |______________________________________________________________|

RAM PCB 5 (32 x HM6116P-3)
     _______________________________________________________________
    |  _______   _______   _______   _______   _______             |
    | |      |  |      |  |      |  |      |  |      |  ___   ___  |
    | |      |  |      |  |      |  |      |  |      | |  |  |  |  |
    | |HM    |  |HM    |  |HM    |  |HM    |  |HM    | | <-74LS138N|
    | |6116P |  |6116P |  |6116P |  |6116P |  |6116P | |  |  | <-74LS138N
    | |3     |  |3     |  |3     |  |3     |  |3     | |  |  |  |  |
    | |______|  |______|  |______|  |______|  |______| |__|  |__|  |
    |  _______   _______   _______   _______   _______             |
    | |      |  |      |  |      |  |      |  |      |             |
    | |      |  |      |  |      |  |      |  |      |  _________  |___
    | |HM    |  |HM    |  |HM    |  |HM    |  |HM    | |SN74LS10N   __|
    | |6116P |  |6116P |  |6116P |  |6116P |  |6116P |              __|
    | |3     |  |3     |  |3     |  |3     |  |3     |  __________  __|
    | |______|  |______|  |______|  |______|  |______| |SN74LS244N  __|
    |  _______   _______   _______   _______   _______              __|
    | |      |  |      |  |      |  |      |  |      |  __________  __|
    | |      |  |      |  |      |  |      |  |      | |SN74LS244N  __|
    | |HM    |  |HM    |  |EMPTY |  |EMPTY |  |EMPTY |              __|
    | |6116P |  |6116P |  |6116P |  |6116P |  |6116P |  __________  __|
    | |3     |  |3     |  |3     |  |3     |  |3     | |SN74LS244N  __|
    | |______|  |______|  |______|  |______|  |______|              __|
    |  _______   _______   _______   _______   _______  __________  __|
    | |      |  |      |  |      |  |      |  |      | |SN74LS244N  __|
    | |      |  |      |  |      |  |      |  |      |              __|
    | |HM    |  |HM    |  |HM    |  |HM    |  |EMPTY |  _________   __|
    | |6116P |  |6116P |  |6116P |  |6116P |  |6116P | |74LS138N|   __|
    | |3     |  |3     |  |3     |  |3     |  |3     |              __|
    | |______|  |______|  |______|  |______|  |______|  _________   __|
    |  _______   _______   _______   _______   _______ |SN74LS08N   __|
    | |      |  |      |  |      |  |      |  |      |         :::  __|
    | |      |  |      |  |      |  |      |  |      |  __________  __|
    | |HM    |  |HM    |  |HM    |  |HM    |  |HM    | AM25LS252IPC __|
    | |6116P |  |6116P |  |6116P |  |6116P |  |6116P |              __|
    | |3     |  |3     |  |3     |  |3     |  |3     |  _________   __|
    | |______|  |______|  |______|  |______|  |______| SN74ALS133N  __|
    |  _______   _______   _______   _______   _______              __|
    | |      |  |      |  |      |  |      |  |      |              __|
    | |      |  |      |  |      |  |      |  |      |  __________  __|
    | |HM    |  |HM    |  |HM    |  |HM    |  |HM    | |DIPS x 8 |  __|
    | |6116P |  |6116P |  |6116P |  |6116P |  |6116P |              __|
    | |3     |  |3     |  |3     |  |3     |  |3     |             |
    | |______|  |______|  |______|  |______|  |______|  ___   ___  |
    |                                _______   _______ |  |  |  |  |
    |                               |      |  |      | | <-74LS138N|
    |                               |      |  |      | |  |  | <-74LS138N
    |                               |HM    |  |HM    | |  |  |  |  |
    |                               |6116P |  |6116P | |__|  |__|  |
    |                               |3     |  |3     |             |
    |                               |______|  |______|             |
    |______________________________________________________________|

RAM PCB 5 (24 x HM6116P-3)
     _______________________________________________________________
    |  _______   _______   _______   _______   _______             |
    | |      |  |      |  |      |  |      |  |      |  ___   ___  |
    | |      |  |      |  |      |  |      |  |      | |  |  |  |  |
    | |HM    |  |HM    |  |HM    |  |EMPTY |  |EMPTY | | <-74LS138N|
    | |6116P |  |6116P |  |6116P |  |      |  |      | |  |  | <-74LS138N
    | |3     |  |3     |  |3     |  |      |  |      | |  |  |  |  |
    | |______|  |______|  |______|  |______|  |______| |__|  |__|  |
    |  _______   _______   _______   _______   _______             |
    | |      |  |      |  |      |  |      |  |      |             |
    | |      |  |      |  |      |  |      |  |      |  _________  |___
    | |HM    |  |HM    |  |HM    |  |EMPTY |  |EMPTY | |SN74LS10N   __|
    | |6116P |  |6116P |  |6116P |  |      |  |      |              __|
    | |3     |  |3     |  |3     |  |      |  |      |  __________  __|
    | |______|  |______|  |______|  |______|  |______| |SN74LS244N  __|
    |  _______   _______   _______   _______   _______              __|
    | |      |  |      |  |      |  |      |  |      |  __________  __|
    | |      |  |      |  |      |  |      |  |      | |SN74LS244N  __|
    | |HM    |  |HM    |  |EMPTY |  |EMPTY |  |EMPTY |              __|
    | |6116P |  |6116P |  |      |  |      |  |      |  __________  __|
    | |3     |  |3     |  |      |  |      |  |      | |SN74LS244N  __|
    | |______|  |______|  |______|  |______|  |______|              __|
    |  _______   _______   _______   _______   _______  __________  __|
    | |      |  |      |  |      |  |      |  |      | |SN74LS244N  __|
    | |      |  |      |  |      |  |      |  |      |              __|
    | |HM    |  |HM    |  |HM    |  |HM    |  |EMPTY |  _________   __|
    | |6116P |  |6116P |  |6116P |  |6116P |  |      | |74LS138N|   __|
    | |3     |  |3     |  |3     |  |3     |  |      |              __|
    | |______|  |______|  |______|  |______|  |______|  _________   __|
    |  _______   _______   _______   _______   _______ |SN74LS08N   __|
    | |      |  |      |  |      |  |      |  |      |         :::  __|
    | |      |  |      |  |      |  |      |  |      |  __________  __|
    | |HM    |  |HM    |  |HM    |  |HM    |  |HM    | AM25LS252IPC __|
    | |6116P |  |6116P |  |6116P |  |6116P |  |6116P |              __|
    | |3     |  |3     |  |3     |  |3     |  |3     |  _________   __|
    | |______|  |______|  |______|  |______|  |______| SN74ALS133N  __|
    |  _______   _______   _______   _______   _______              __|
    | |      |  |      |  |      |  |      |  |      |              __|
    | |      |  |      |  |      |  |      |  |      |  __________  __|
    | |HM    |  |HM    |  |HM    |  |HM    |  |HM    | |DIPS x 8 |  __|
    | |6116P |  |6116P |  |6116P |  |6116P |  |6116P |              __|
    | |3     |  |3     |  |3     |  |3     |  |3     |             |
    | |______|  |______|  |______|  |______|  |______|  ___   ___  |
    |                                _______   _______ |  |  |  |  |
    |                               |      |  |      | | <-74LS138N|
    |                               |      |  |      | |  |  | <-74LS138N
    |                               |HM    |  |HM    | |  |  |  |  |
    |                               |6116P |  |6116P | |__|  |__|  |
    |                               |3     |  |3     |             |
    |                               |______|  |______|             |
    |______________________________________________________________|


WESTERN DIGITAL MFM HDD CONTROLLER WD 100,1-05

    _____________________________________   __________________________   ___________________
   |       _____________                |__|                         |__|                  |
   |   ·· |N8T31N 8324 |   __________   __________   __________    __________________      |
   |   ·· |____________|  |_74LS08N_|  |74LS191N_|  |TMS2149-5NL   | N8X305N  8328   |     |
   |   ··    __________   __________   __________   __________     |                 |     |
   |·· ··   |SN74LS244N  |74LS191N_|  |74LS191N_|  |TMS2149-5NL    |_________________|     |
   |·· ··    __________   __________   __________   __________     _____   _____   _____   |
   |·· ··   |_74LS32N_|  |_74S00PC_|  |SN74LS244N  |WD1100PE-01   |    |  |    |  | <-N82S181N
   |·· ··    __________   __________   __________   __________    | <-N82S181N |  |    |   |
   |·· ··   |SN74LS14N|  |74LS374N_|  |WD1100PE-05 |_74S138N_|    |____|  |__<-N82S181N|   |
   |·· ··    __________   __________   __________   __________   __________                |
   |··      |_74S06PC_|  |74LS273PC|  |WD11C0PE-12 |_74S138N_|  |74LS175PC|  Xtal 8 MHz    |
   |··   __________  _________  __________  __________  __________  __________  __________ |
   |··  |_74S06PC_| |74LS259PC |WD1100V-07 |DM74S04N_| |SN74LS137N WD1100PE-03 |SN74S74N_| |
   | o               _________  __________  __________  __________  __________  __________ |
   | o  .. .. .. .. |DM74S64N| WD1100PE-09 |DTZM1-60_| |WD11CE-08| |DM74S74N_| |SN74S74N_| |
   | o  .. .. .. ..  _________  __________  __________  __________  __________             |
   | o  .. .. .. .. |AM26LS32P |AM26S02PC| |_T74LS54_| |SN74S74N_| |SN74S124N|             |
   |    ·· ·· ·· ··  _________                                                             |
   |    ·· ·· ·· ·· |AM26LS31CN         Xtal 20 MHz                                        |
   |                                     ___                          ___                  |
   |____________________________________|  |_________________________|  |__________________|

***********************************************************************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"

namespace {

class secoinsa20_state : public driver_device
{
public:
	secoinsa20_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void secoinsa20(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};


// Input ports
static INPUT_PORTS_START( secoinsa20 )
INPUT_PORTS_END


void secoinsa20_state::machine_reset()
{
}

void secoinsa20_state::machine_start()
{
}

void secoinsa20_state::secoinsa20(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, XTAL(4'000'000));
}



ROM_START( secoinsa20 )
	ROM_REGION(0x20000, "maincpu", 0)
	ROM_LOAD( "secoinsa_iras_3f_vo2.2_2732a.bin", 0x0000, 0x1000, CRC(50b09416) SHA1(9a799e2e6ebad484226778f41b292a66ebe45849) )

	ROM_REGION(0x10000, "wd_mfm_controller", 0)
	ROM_LOAD( "62-000019-00-a0_n82s181n.u16",     0x0000, 0x0400, CRC(d7819b5d) SHA1(c74dc7cbe05796ec787db482f4ccc92d93df2fa4) )
	ROM_LOAD( "62-000019-01-a0_n82s181n.u15",     0x0400, 0x0400, CRC(bb775759) SHA1(dae3fc65e072c5ebec6c545cef4e58ba0dc44ae7) )
	ROM_LOAD( "62-000019-02-a0_n82s181n.u14",     0x0800, 0x0400, CRC(94f9ed35) SHA1(2a14134849213a733b05b9e216b45909c36bd859) )

	ROM_REGION(0x00117, "plds", 0)
	ROM_LOAD( "asp1_2m_v00.1_pal16l8.2m",         0x0000, 0x0117, BAD_DUMP CRC(c20eae34) SHA1(9a879350be345ea757ba7b6636a9c789932b75b4) ) // On PCB SM16B-0200-0010, bruteforced
	ROM_LOAD( "asp2_2l_v00.1_pal16l8.2l",         0x0000, 0x0117, NO_DUMP ) // On PCB SM16B-0200-0010
	ROM_LOAD( "asp_p3_2k_v00.1_pal16l8.2k",       0x0000, 0x0117, BAD_DUMP CRC(4838b218) SHA1(312a2cca2a403776f6d861763258edee17a040b8) ) // On PCB SM16B-0200-0010, bruteforced
	ROM_LOAD( "palp1_1d_v00.1_pal16l8.1d",        0x0000, 0x0117, NO_DUMP ) // On PCB SM16B-0200-0170
	ROM_LOAD( "palp2_1c_v00.1_pal16r4.1c",        0x0000, 0x0117, NO_DUMP ) // On PCB SM16B-0200-0170
	ROM_LOAD( "palp3_3u_v00.1_pal16l8.3u",        0x0000, 0x0117, NO_DUMP ) // On PCB SM16B-0200-0170
	ROM_LOAD( "palp4_1b_v00.1_pal16r8.1b",        0x0000, 0x0117, NO_DUMP ) // On PCB SM16B-0200-0170
	ROM_LOAD( "palp5_2p_v00.2_pal16l8.2p",        0x0000, 0x0117, BAD_DUMP CRC(0a8d42ef) SHA1(5f3af633742fd4859458237bc576671be8f5a898) ) // On PCB SM16B-0200-0170, bruteforced
	ROM_LOAD( "pcpu_2h_v00.1_pal16l8.2h",         0x0000, 0x0117, NO_DUMP ) // On CPU PCB
	ROM_LOAD( "pss1_1h_v00.1_pal16l8.1h",         0x0000, 0x0117, NO_DUMP ) // On PCB SM16B-0200-0140
	ROM_LOAD( "pss2_1k_v00.1_pal16l8.1k",         0x0000, 0x0117, BAD_DUMP CRC(069664b1) SHA1(c5d32b20a3e9985dd1cb23acfe73f1f8be2e270d) ) // On PCB SM16B-0200-0140, bruteforced
	ROM_LOAD( "pss3_1a_v00.2_pal16r4.1a",         0x0000, 0x0117, NO_DUMP ) // On PCB SM16B-0200-0140
	ROM_LOAD( "pss4_2g_v00.1_pal16l8.2g",         0x0000, 0x0117, BAD_DUMP CRC(34f23cf6) SHA1(c67f0c1878e1a93787bcd09647162e174218fd6f) ) // On PCB SM16B-0200-0140, bruteforced
	ROM_LOAD( "pss5_1n_v00.2_pal16r4.1n",         0x0000, 0x0117, NO_DUMP ) // On PCB SM16B-0200-0140
	ROM_LOAD( "pss6_2m_v00.1_pal16l8.2m",         0x0000, 0x0117, NO_DUMP ) // On PCB SM16B-0200-0140
ROM_END

} // Anonymous namespace

//    YEAR  NAME        PARENT  COMPAT  MACHINE     INPUT       CLASS             INIT        COMPANY     FULLNAME    FLAGS
COMP( 1981, secoinsa20, 0,      0,      secoinsa20, secoinsa20, secoinsa20_state, empty_init, "Secoinsa", "Serie 20", MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )



sega_beena.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont, QUFB
/******************************************************************************

    sega_beena.cpp

    Driver for the Sega Toys' Advanced Pico BEENA

    TODO:

        MIDI audio
        Peripherals (including the SD-Card adapter)
        Component list / PCB diagram
        Fix graphical glitches (e.g. 'Car Beena' scrolling background)
        Fix test mode for 'Car Beena' (fails on 'Test EEP')

    Hardware
    --------

    The PCB includes a custom SoC marked with 'Sega Toys 9H0-0008' but manufactured by Applause Technologies.

    There are 2 known PCB revisions, which only have minor differences unrelated to gameplay,
    yielding identical BIOS dumps:

        9B0-0007G / 702810006.03GA SW1529 (Rev. 1)
        9B0-0007F / 702810004.02A  SW1404 (Rev. 4)

    A headerless but fully functional JTAG port is present, following the ARM standard pinout:

         20  19
       -----------
        GND  0
          0  0
        GND  nSRST
        GND  TDO
        GND  RTCK
        GND  TCLK
        GND  TMS
        GND  TDI
        GND  nTRST
        3.3  0
       -----------
          2  1

    Both BIOS and Cartridge ROMs have 'edinburgh' in their headers, which is a codename that also appears
    marked on the SoC's development board as 'EDINBURGH_EVAL V1.0'. A non Sega Toys version of the SoC was sold,
    marked as 'Applause Technologies AP2010' with codename 'LANCELOT'. They appear to have identical features,
    with the only difference presumably being the contents of the internal ROM.

    Cartridge ROMs are also full of .ogg files containing the string 'Encoded with Speex speex-1.0.4'
    as well as .au files for sounds and .mid files for music.

    Cartridges pinout:

        Glob down, PCB cut corner at upper right.

        Top row of pins A25 on left to A1 on right.
        Bottom row of pins B25 on left to B1 on right.

        A1  /CE (tied high with resistor)
        A2  D11
        A3  D3
        A4  D10
        A5  D2
        A6  D9
        A7  D1
        A8  D8
        A9  D0
        A10 /OE
        A11 N/C
        A12 A0
        A13 A1
        A14 A2
        A15 A3
        A16 A4
        A17 A5
        A18 A6
        A19 A7
        A20 A17
        A21 A18
        A22 N/C
        A23 VCC
        A24 N/C
        A25 GND

        B1  CE (for Flashrom)
        B2  D4
        B3  D12
        B4  D5
        B5  D13
        B6  D6
        B7  D14
        B8  D7
        B9  D15
        B10 A16
        B11 A15
        B12 A14
        B13 A13
        B14 A12
        B15 A11
        B16 A10
        B17 A9
        B18 A8
        B19 A19
        B20 A20
        B21 WE (for Flashrom)
        B22 A21
        B23 VCC
        B24 N/C
        B25 GND

    Storyware
    ---------

    Toggling 'Pen Target' input switches between mapping pen coordinates to the tablet or the book.

    Test Mode
    ---------

    Most games contain a hidden test mode that can be activated by the same inputs:

        Pages 1, 3, and 5 covered, the others exposed;
        Left red button held down;

    If the machine configuration 'Test Mode Pages' is enabled, the driver forces this page setup,
    so that the player only needs to hold the left red button at reset to activate this mode.

    Toggling 'Memory Cache' allows the player to observe differences between test failure and success.

    For 'TV Ocha-Ken', hold down A B C, then power on the system, release all buttons, and press B 3 times.

    For 'Car Beena', hold down all 3 buttons, turn the handle left to full lock, then power on the system.

*******************************************************************************/

#include "emu.h"

#include "9h0-0008_card.h"
#include "9h0-0008_card_reader.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/arm7/ap2010cpu.h"
#include "machine/clock.h"
#include "machine/timer.h"
#include "sound/ap2010pcm.h"

#include "crsshair.h"
#include "emupal.h"
#include "render.h"
#include "softlist_dev.h"
#include "schedule.h"
#include "speaker.h"
#include "screen.h"

#include "beena.lh"
#include "tvochken.lh"

#define VERBOSE (0)
#include "logmacro.h"


namespace {

class sega_9h0_0008_state : public driver_device
{
public:
	sega_9h0_0008_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_workram(*this, "workram")
		, m_pcm(*this, "pcm")
		, m_screen_main(*this, "screen")
		, m_gfxdecode(*this, "gfxdecode")
		, m_palette(*this, "palette")
		, m_paletteram(*this, "paletteram")
		, m_tilemap_bg(*this, "tilemap_bg")
		, m_tilemap_fg(*this, "tilemap_fg")
		, m_tilemap_scroll_x(*this, "tilemap_scroll_x")
		, m_tilemap_scroll_y(*this, "tilemap_scroll_y")
		, m_tilemap_sprites(*this, "tilemap_sprites")
		, m_bitmap(*this, "bitmap")
		, m_io_sensor_regs(*this, "io_sensor_regs")
		, m_io_auxiliary_regs(*this, "io_auxiliary_regs")
		, m_io_cpu_config(*this, "CPU_CONFIG")
		, m_io_video_config(*this, "VIDEO_CONFIG")
	{ }

protected:
	static inline constexpr uint32_t ROM_MASK_BASE = 0x80000000;
	static inline constexpr uint32_t ROM_FLASH_BASE = 0xa0000000;

	void sega_9h0_0008(machine_config &config);

	virtual void device_post_load() override;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

	virtual void install_game_rom();
	virtual void update_sensors(offs_t offset);

	void beena_arm7_map(address_map &map) ATTR_COLD;

	void request_irq();
	void request_fiq();

	TIMER_DEVICE_CALLBACK_MEMBER(scanline);
	virtual void update_crosshair(screen_device &screen);
	void irq_wait_speedup();
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	uint32_t video_reg_r(offs_t offset);
	void video_reg_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	void pal_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint8_t tiles_layers_r(offs_t offset);
	uint8_t tiles_sprites_r(offs_t offset);
	void tiles_layers_w(offs_t offset, uint8_t data);
	void tiles_sprites_w(offs_t offset, uint8_t data);
	int32_t scroll_x(int32_t x, uint16_t i);
	int32_t scroll_y(int32_t y, uint16_t i);
	void draw_layer(bitmap_rgb32 &bitmap, const rectangle &cliprect, const uint16_t *tilemap, const uint8_t scroll_idx, const bool is_active, const bool is_overlay_rendered);
	void draw_layer_tiles(bitmap_rgb32 &bitmap, const rectangle &cliprect, const uint16_t *tilemap, const uint8_t scroll_idx, const bool is_overlay_rendered);
	void draw_layer_scanlines(bitmap_rgb32 &bitmap, const rectangle &cliprect, const uint16_t *tilemap, const uint8_t scroll_idx, const bool is_overlay_rendered);
	void draw_sprites(bitmap_rgb32 &bitmap, const rectangle &cliprect, const bool is_overlay_rendered);
	void draw_bitmap(bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void screen_blend(bitmap_rgb32 &bitmap, const rectangle &cliprect);
	int32_t rescale_alpha_step(uint8_t step);
	template <typename BitmapType, typename FunctionClass>
	void drawgfxzoom_with_pixel_op(gfx_element *gfx, BitmapType &dest, const rectangle &cliprect, uint32_t code, int flipx, int flipy, int32_t destx, int32_t desty, uint32_t scalex, uint32_t scaley, FunctionClass &&pixel_op);

	uint32_t io_sensors_r(offs_t offset);

	uint32_t io_memcache_r();
	virtual uint32_t io_expansion_r();
	void memcache_advance(uint32_t &status);
	void memcache_parse_data_bit(uint32_t &status);

	uint32_t rtc_r(offs_t offset);
	void rtc_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	void update_rtc();

	uint32_t midi_reg_r(offs_t offset);
	void midi_reg_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);

	static constexpr int16_t signed10(uint32_t number)
	{
		return util::sext(number, 10);
	}
	static constexpr int32_t rescale(int32_t x, int32_t min_x, int32_t max_x, int32_t a, int32_t b)
	{
		// Rescaling (min-max normalization) from [min_x..max_x] to [a..b].
		return a + (((x - min_x) * (b - a)) / (max_x - min_x));
	}
	static constexpr uint32_t alpha_blend_rgb_levels(uint32_t dst, uint32_t src, uint8_t level_b, uint8_t level_g, uint8_t level_r)
	{
		// Similar to drawgfx::alpha_blend_r32(), but distinct levels are applied to each channel.
		return ((((src & 0x0000ff) * level_r + (dst & 0x0000ff) * int(256 - level_r)) >> 8)) |
				((((src & 0x00ff00) * level_g + (dst & 0x00ff00) * int(256 - level_g)) >> 8) & 0x00ff00) |
				((((src & 0xff0000) * level_b + (dst & 0xff0000) * int(256 - level_b)) >> 8) & 0xff0000);
	}

	required_device<ap2010cpu_device> m_maincpu;
	required_shared_ptr<uint32_t> m_workram;
	required_device<ap2010pcm_device> m_pcm;
	bool m_requested_fiq;
	uint32_t m_irq_wait_start_addr;

	required_device<screen_device> m_screen_main;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<palette_device> m_palette;
	std::unique_ptr<rgb_t[]> m_cache_palette;
	required_shared_ptr<uint32_t> m_paletteram;
	required_shared_ptr<uint32_t> m_tilemap_bg;
	required_shared_ptr<uint32_t> m_tilemap_fg;
	required_shared_ptr<uint32_t> m_tilemap_scroll_x;
	required_shared_ptr<uint32_t> m_tilemap_scroll_y;
	required_shared_ptr<uint32_t> m_tilemap_sprites;
	required_shared_ptr<uint32_t> m_bitmap;
	std::unique_ptr<uint8_t[]> m_tiles_layers;
	std::unique_ptr<uint8_t[]> m_tiles_sprites;
	std::unique_ptr<uint32_t[]> m_video_regs;
	bitmap_rgb32 m_cache_layer; // Used for x-scrolling scanlines, same size as bitplane
	uint8_t m_scale;
	uint16_t m_scanline;

	required_shared_ptr<uint32_t> m_io_sensor_regs;
	required_shared_ptr<uint32_t> m_io_auxiliary_regs;
	uint32_t m_pen_target;
	uint32_t m_effective_page;

	uint16_t m_rtc[0x100/2]{};

	std::unique_ptr<uint32_t[]> m_midi_regs;
	uint32_t m_midi_busy_count;

	required_ioport m_io_cpu_config;
	required_ioport m_io_video_config;

private:
	static inline constexpr uint16_t SCREEN_W = 704;
	static inline constexpr uint16_t SCREEN_H = 480;

	static inline constexpr uint32_t UNKNOWN_ADDR = 0xffffffff;

	static inline constexpr uint16_t MEMCACHE_FIFO_MAX_SIZE = 0x100;

	enum memcache_seq : uint32_t
	{
		BITS_EMPTY = 0,
		BITS_0     = 0x0000ff00,
		BITS_0_1   = 0x00ff0001, // Command 0xa0 store bit 0
								 // Command 0xa1 move to next bit?
		BITS_0_1_7 = 0xff000107, // Reset
		BITS_6     = 0x0000ff06, // Query (1 if ready for next events)
		BITS_6_7   = 0x00ff0607, // Query (0 if events parsed successfully)
								 // Command 0xa0 store bit 1
								 // Command 0xa1 load bit
		BITS_7     = 0x0000ff07,
		BITS_7_1   = 0x00ff0701, // Start command
		BITS_ERR   = 0xffffffff,
	};

	enum memcache_state : uint8_t
	{
		IDLE = 0,
		READ_COMMAND,
		READ_ADDRESS_BYTE,
		READ_ACK,
		READ_RAM_BYTE,
		WRITE_RAM_BYTE,
		CONSUME_UNTIL_IDLE,
	};

	uint8_t fifo_state_after_pop();
	uint8_t fifo_events_pop();
	void fifo_state_after_push(uint8_t state);
	void fifo_events_push(uint8_t event);

	uint8_t m_memcache[0x800]{};

	uint32_t m_memcache_seq;

	uint32_t m_memcache_addr;
	uint8_t m_memcache_data;
	uint8_t m_memcache_i;
	uint8_t m_memcache_state;

	uint8_t m_fifo_state_after_data[MEMCACHE_FIFO_MAX_SIZE]{};
	uint16_t m_fifo_state_after_size = 0;
	uint16_t m_fifo_state_after_head = 0;
	uint16_t m_fifo_state_after_tail = 0;

	uint8_t m_fifo_events_data[MEMCACHE_FIFO_MAX_SIZE]{};
	uint16_t m_fifo_events_size = 0;
	uint16_t m_fifo_events_head = 0;
	uint16_t m_fifo_events_tail = 0;
};

/*
    FIXME: All of these have guessed timing.

    Animation durations in hardware captures suggest that an IRQ is raised every VBLANK.
    Palette changes can occur for each scanline via FIQ callback set by games.

    These variables in BIOS RAM may be worth following:
    - 0x20003ee0: FIQ enabled;
    - 0x20003ee1: IRQ enabled;
    Along with pairs used by interrupt callbacks:
    - 0x20003ed1..0x20003ed4: status;
    - 0x20003ee2..0x20003ee5: 1 if busy?
*/
TIMER_DEVICE_CALLBACK_MEMBER(sega_9h0_0008_state::scanline)
{
	irq_wait_speedup();

	// Pixel clock approximation derived from palette lookup tables applied per scanline,
	// used for gradient backgrounds in "Partner In TV".
	//
	// FIXME:
	// - Rainbow arc in "Cars 2" before bridge section is not rendered;
	// - Foreground drop animation in "Cooking Beena" title screen is skipped;
	m_scanline = param / m_scale;
	m_video_regs[0x4/4] = 0x10000 * (20 + m_scanline);

	uint8_t irq_frequency = m_io_cpu_config->read() & 0xf;
	if ((m_scanline % (SCREEN_H / irq_frequency)) == 0) {
		uint8_t video_status = (m_video_regs[0] & 0x10) ^ 0x10;
		m_video_regs[0] = (m_video_regs[0] & 0xffffffef) | video_status;

		update_rtc();

		request_irq();

		// FIXME: Needed for .au data processing in "Kazoku Minna no Nouryoku Trainer"
		m_requested_fiq = true;
	}

	request_fiq();
}

void sega_9h0_0008_state::irq_wait_speedup()
{
	if ((m_io_cpu_config->read() & 0x10) == 0x10) {
		return;
	}

	/*
	    All games execute a busy wait until the next IRQ request is served.
	    This can lead to significant downgrade of emulation speed.

	    The busy wait subroutine is copied to a dynamic location in work RAM,
	    somewhere after 0xc00cc000, but before the stack pointer. r0 stores
	    an address to a variable that is updated by the game's IRQ callback
	    when video data has been processed:

	        e3 a0 30 01   mov     r3,#0x1
	        e5 c0 30 00   strb    r3,[r0,#0x0]
	        e5 d0 30 00   ldrb    r3,[r0,#0x0]
	        e3 53 00 00   cmp     r3,#0x0
	        1a ff ff fc   bne     LAB_c00ce8bc

	    Epilogue is the following for most games:

	        e5 9f 30 00   ldr     r3,[DAT_c00ce8d0] = 80000000h
	        e5 93 f0 08   ldr     pc=>LAB_c00fff80,[r3,#offset ->SP]

	    But slightly different in early games:

	        e1 2f ff 1e   bx      lr

	    Since this code has a predictable byte signature, we can search
	    in memory to find its exact start address, then consume enough cycles to
	    reduce the number of instructions executed until the next IRQ is asserted.
	*/
	if (m_irq_wait_start_addr == UNKNOWN_ADDR) {
		if (m_maincpu->pc() > 0xc00cc000 && m_maincpu->pc() < 0xc00fff80) {
			const uint32_t IRQ_WAIT_SIGNATURE[] = {
				0xe3a03001,
				0xe5c03000,
				0xe5d03000,
				0xe3530000,
				0x1afffffc
			};
			int8_t addr_delta = 8;
			uint32_t *shared32 = reinterpret_cast<uint32_t *>(m_workram.target());
			uint32_t candidate_start_addr = m_maincpu->pc() - addr_delta;
			uint32_t candidate_offset = (candidate_start_addr - 0xc00cc000) / 4;
			for (size_t i = 0; i < addr_delta; i++) {
				bool matched = true;
				for (size_t sig_i = 0; sig_i < 5; sig_i++) {
					if (IRQ_WAIT_SIGNATURE[sig_i] != shared32[candidate_offset + i + sig_i]) {
						matched = false;
						break;
					}
				}
				if (matched) {
					m_irq_wait_start_addr = candidate_start_addr + i;

					for (size_t sig_i = 0; sig_i < 5; sig_i++) {
						m_maincpu->add_hotspot(candidate_start_addr + i + sig_i * 4);
					}
				}
			}
		}
	}
}

static const gfx_layout sega_beena_8bpp_layout =
{
	16,16,
	0x800, // 0x800 * 0x10 * 0x10 = 0x80000 (tile data mapping size)
	8,
	{ STEP8(0, 1) },
	{ STEP16(0, 8) },
	{ STEP16(0, 8*16) },
	8*16*16
};

void sega_9h0_0008_state::beena_arm7_map(address_map &map)
{
	// BIOS internal ROM
	map(0x00000000, 0x0001ffff).rom();
	// BIOS internal RAM
	map(0x20000000, 0x20003fff).ram();

	// FIXME: Need to confirm upper bounds / mirrored ranges on hardware, some return inconsistent reads
	// Video (registers, palette, sprite data...)
	map(0x40000000, 0x400000ff).mirror(0xff00).rw(FUNC(sega_9h0_0008_state::video_reg_r), FUNC(sega_9h0_0008_state::video_reg_w));
	map(0x40010000, 0x400103ff).ram().share("tilemap_sprites");
	map(0x40010400, 0x4001ffff).ram();
	map(0x40020000, 0x400201ff).w(FUNC(sega_9h0_0008_state::pal_w)).share("paletteram");
	map(0x40020200, 0x4002ffff).ram();
	map(0x40030000, 0x400302ff).ram().share("tilemap_scroll_y");
	map(0x40030300, 0x4003ffff).ram();

	// I/O (registers, pages, pads, pen...)
	map(0x50000000, 0x5000000f).mirror(0xfff0).ram(); // Unknown
	map(0x50010000, 0x5001003f).mirror(0xffc0).lrw32(
		NAME([this](offs_t offset) {
			LOG("audio_pcm_reg_r @ %08x: addr=%08x data=%08x\n", m_maincpu->pc(), offset * 4, m_pcm->reg_r(offset));

			return m_pcm->reg_r(offset);
		}),
		NAME([this](offs_t offset, uint32_t data, uint32_t mem_mask) {
			LOG("audio_pcm_reg_w @ %08x: addr=%08x data=%08x mask=%08x\n", m_maincpu->pc(), offset * 4, data, mem_mask);

			// FIXME: Games hang during .au data processing unless FIQ requests are served,
			// review this after playback is implemented
			if (offset == 0x10/4) {
				if (data != 0) {
					m_requested_fiq = true;
				}
			}

			m_pcm->reg_w(offset, data, mem_mask);
		}));
	map(0x50020000, 0x5002007f).mirror(0xff80).r(FUNC(sega_9h0_0008_state::io_sensors_r)).share("io_sensor_regs");
	map(0x50030000, 0x500300ff).mirror(0xff00).lrw32(
		NAME([this](offs_t offset) {
			LOG("m_io_auxiliary_regs_r @ %08x: addr=%08x\n", m_maincpu->pc(), offset * 4);

			if (offset == 0x0c/4) {
				return io_expansion_r();
			}
			return offset == 0xbc/4 ? io_memcache_r() : m_io_auxiliary_regs[offset];
		}),
		NAME([this](offs_t offset, uint32_t data, uint32_t mem_mask) {
			LOG("m_io_auxiliary_regs_w @ %08x: addr=%08x data=%08x mask=%08x\n", m_maincpu->pc(), offset * 4, data, mem_mask);

			COMBINE_DATA(&m_io_auxiliary_regs[offset]);
			if (offset == 0xc0/4) {
				fifo_events_push(data);
			}
		})).share("io_auxiliary_regs");

	// Realtime clock
	map(0x60000000, 0x600000ff).mirror(0xff00).rw(FUNC(sega_9h0_0008_state::rtc_r), FUNC(sega_9h0_0008_state::rtc_w));
	map(0x60010000, 0x6003ffff).ram(); // Unknown

	// MIDI
	map(0x70000000, 0x7000001f).mirror(0xffe0).rw(FUNC(sega_9h0_0008_state::midi_reg_r), FUNC(sega_9h0_0008_state::midi_reg_w));
	map(0x70010000, 0x7003ffff).ram(); // Unknown

	// Game Mask ROM
	map(ROM_MASK_BASE, ROM_MASK_BASE + 0x7fffff).mirror(0x800000).lr8(NAME([]() { return 0xff; }));

	// Game NOR Flash ROM
	// Some games have an unreferenced handler that jumps to 0xa0ffc000;
	map(ROM_FLASH_BASE, ROM_FLASH_BASE + 0x7fffff).mirror(0x800000).lr8(NAME([]() { return 0xff; }));

	// Direct bitmap with 2-byte BGR555 colors per pixel, same as tile palettes. Scaling also applied.
	// The bitmap size is checked by games to be `w * h <= 0x63000`, so we allocate `0x63000 * 2 = 0xc6000`.
	map(0xc0000000, 0xc00c5fff).ram().share("bitmap");
	// Tilemap data for 2 layers (background and foreground).
	map(0xc00c6000, 0xc00c68ff).ram().share("tilemap_scroll_x");
	// Work RAM for tilemap data
	map(0xc00c6900, 0xc00c7fff).ram();
	map(0xc00c8000, 0xc00c9fff).ram().share("tilemap_fg");
	map(0xc00ca000, 0xc00cbfff).ram().share("tilemap_bg");
	// Work RAM for general use + stack (usually starts at 0xc00fff80)
	map(0xc00cc000, 0xc00fffff).ram().share("workram");
	// Tile data for 2 layers (background and foreground) + sprites
	map(0xc0100000, 0xc017ffff).ram().rw(FUNC(sega_9h0_0008_state::tiles_layers_r), FUNC(sega_9h0_0008_state::tiles_layers_w));
	map(0xc0180000, 0xc01fffff).ram().rw(FUNC(sega_9h0_0008_state::tiles_sprites_r), FUNC(sega_9h0_0008_state::tiles_sprites_w));
}

void sega_9h0_0008_state::update_sensors(offs_t offset)
{
	// Power status
	// - bit 1 = 0: a "low battery" screen shows up;
	// - bit 2 = 0: game code goes into infinite loop, maybe power button pressed?
	if (offset == 0x38/4) {
		m_io_sensor_regs[offset] = 3;
	}
}

uint32_t sega_9h0_0008_state::io_sensors_r(offs_t offset)
{
	if (!machine().side_effects_disabled()) {
		update_sensors(offset);
	}

	return m_io_sensor_regs[offset];
}

uint32_t sega_9h0_0008_state::video_reg_r(offs_t offset)
{
	return m_video_regs[offset];
}

void sega_9h0_0008_state::video_reg_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("video_reg_w @ %08x: addr=%08x data=%08x mask=%08x\n", m_maincpu->pc(), offset * 4, data, mem_mask);

	/*
	   Video control
	   - bit 1..4: rendering toggles
	     - 0x1: enable rendering
	     - 0x2: enable fg layer
	     - 0x4: enable bg layer
	   - bit 5..8: scroll data entries
	     - 0x10: 0x900 for x-axis scanlines
	     - 0x20:  0x90 for x-axis tiles
	     - 0x40:  0xb4 for y-axis tiles
	   - bit 9..12: video format
	     - 0x100: vertical frequency: 60Hz (NTSC) if 0, 50Hz (PAL) if 1
	     - 0x200: mode: 352x240 (scales 2x from top-left corner) if 0, 704x480 if 1
	   - bit 13..16: direct bitmap render priority
	     - 0x0000: before overlay sprites
	     - 0x1000: before all sprites
	     - 0x2000: before fg layer
	     - 0x3000: before bg and fg layers
	*/
	if (offset == 0x10/4) {
		m_scale = ((data & 0x200) == 0) ? 2 : 1;
	}

	COMBINE_DATA(&m_video_regs[offset]);
}

uint32_t sega_9h0_0008_state::midi_reg_r(offs_t offset)
{
	if (offset == 0 && m_midi_busy_count > 0) {
		if (!machine().side_effects_disabled()) {
			m_midi_busy_count--;
		}
		return 0x2;
	}

	return m_midi_regs[offset];
}

void sega_9h0_0008_state::midi_reg_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	LOG("midi_reg_w @ %08x: addr=%08x data=%08x mask=%08x\n", m_maincpu->pc(), offset * 4, data, mem_mask);

	switch (offset) {
		case 0x4/4:
			if ((data & 5) == 5) {
				// TODO: MIDI all notes off
			}
			break;
		case 0x8/4:
			// TODO: MIDI buffered event
			// Games wait before sending additional notes,
			// checking if audio status is busy then ready.
			m_midi_busy_count = 1;
			break;
		case 0xc/4:
			// TODO: MIDI passthrough event
			break;
		case 0x18/4:
			// TODO: MIDI update_divisor
			break;
	}

	COMBINE_DATA(&m_midi_regs[offset]);
}

void sega_9h0_0008_state::install_game_rom()
{
	// Nothing (overriden by each system)
}

void sega_9h0_0008_state::machine_start()
{
	install_game_rom();

	save_item(NAME(m_requested_fiq));
	save_item(NAME(m_irq_wait_start_addr));

	save_item(NAME(m_fifo_state_after_data));
	save_item(NAME(m_fifo_state_after_size));
	save_item(NAME(m_fifo_state_after_head));
	save_item(NAME(m_fifo_state_after_tail));

	save_item(NAME(m_fifo_events_data));
	save_item(NAME(m_fifo_events_size));
	save_item(NAME(m_fifo_events_head));
	save_item(NAME(m_fifo_events_tail));

	m_video_regs = make_unique_clear<uint32_t[]>(0x100/4);
	save_pointer(NAME(m_video_regs), 0x100/4);
	save_item(NAME(m_scale));

	m_midi_regs = make_unique_clear<uint32_t[]>(0x20/4);
	save_pointer(NAME(m_midi_regs), 0x20/4);
	save_item(NAME(m_midi_busy_count));

	save_item(NAME(m_pen_target));
	save_item(NAME(m_effective_page));

	m_cache_layer.allocate(1024, 512);
}

void sega_9h0_0008_state::video_start()
{
	m_cache_palette = std::make_unique<rgb_t[]>(0x100*SCREEN_H);
	for (int i = 0; i < 0x100 * SCREEN_H; i++) {
		m_cache_palette[i] = 0;
	}
	save_pointer(NAME(m_cache_palette.get()), 0x100*SCREEN_H);

	m_tiles_layers = make_unique_clear<uint8_t[]>(0x80000);
	m_tiles_sprites = make_unique_clear<uint8_t[]>(0x80000);
	save_pointer(NAME(m_tiles_layers.get()), 0x80000);
	save_pointer(NAME(m_tiles_sprites.get()), 0x80000);

	m_gfxdecode->set_gfx(0, std::make_unique<gfx_element>(
		m_palette, sega_beena_8bpp_layout, m_tiles_layers.get(), 0, 1, 0));
	m_gfxdecode->set_gfx(1, std::make_unique<gfx_element>(
		m_palette, sega_beena_8bpp_layout, m_tiles_sprites.get(), 0, 1, 0));
}

void sega_9h0_0008_state::machine_reset()
{
	m_requested_fiq = false;
	m_irq_wait_start_addr = UNKNOWN_ADDR;

	memset(m_video_regs.get(), 0, 0x100);
	m_video_regs[0x10/4] = 0x200; // Tiles are 1x scaled by default.
	m_scale = 1;
	m_scanline = 0;

	bool is_video_connected = m_io_video_config->read() == 0;
	if (!is_video_connected) {
		m_video_regs[0] |= 0x00000200;
	}

	memset(m_midi_regs.get(), 0, 0x20);
	m_midi_busy_count = 0;

	m_io_sensor_regs[0] = 7; // Pen is making contact with the target surface.
	m_io_sensor_regs[0x2c/4] = 0xffffffff; // Pages 1 to 4 are closed.
	m_io_sensor_regs[0x30/4] = 0x00ffffff; // Pages 5 to 6 are closed.
	m_pen_target = 0;
	m_effective_page = 0;

	m_memcache_addr = 0;
	m_memcache_data = 0;
	m_memcache_i = 0;
	m_memcache_seq = BITS_EMPTY;
	m_memcache_state = IDLE;

	m_fifo_state_after_size = 0;
	m_fifo_state_after_head = 0;
	m_fifo_state_after_tail = 0;

	update_rtc();
}

void sega_9h0_0008_state::device_post_load()
{
	m_gfxdecode->gfx(0)->mark_all_dirty();
	m_gfxdecode->gfx(1)->mark_all_dirty();
}

uint8_t sega_9h0_0008_state::fifo_state_after_pop()
{
	uint8_t state = m_fifo_state_after_data[m_fifo_state_after_head];
	m_fifo_state_after_head = (m_fifo_state_after_head + 1) & (MEMCACHE_FIFO_MAX_SIZE - 1);
	m_fifo_state_after_size--;
	return state;
}

uint8_t sega_9h0_0008_state::fifo_events_pop()
{
	uint8_t event = m_fifo_events_data[m_fifo_events_head];
	m_fifo_events_head = (m_fifo_events_head + 1) & (MEMCACHE_FIFO_MAX_SIZE - 1);
	m_fifo_events_size--;
	return event;
}

void sega_9h0_0008_state::fifo_state_after_push(uint8_t state)
{
	// trash old data
	if (m_fifo_state_after_size > MEMCACHE_FIFO_MAX_SIZE - 1) {
		fifo_state_after_pop();
	}

	m_fifo_state_after_data[m_fifo_state_after_tail] = state;
	m_fifo_state_after_tail = (m_fifo_state_after_tail + 1) & (MEMCACHE_FIFO_MAX_SIZE - 1);
	m_fifo_state_after_size++;
}

void sega_9h0_0008_state::fifo_events_push(uint8_t event)
{
	// trash old data
	if (m_fifo_events_size > MEMCACHE_FIFO_MAX_SIZE - 1) {
		fifo_events_pop();
	}

	m_fifo_events_data[m_fifo_events_tail] = event;
	m_fifo_events_tail = (m_fifo_events_tail + 1) & (MEMCACHE_FIFO_MAX_SIZE - 1);
	m_fifo_events_size++;
}

void sega_9h0_0008_state::memcache_advance(uint32_t &status)
{
	uint8_t event = fifo_events_pop();

	status = event == 6 ? 1 : 0;

	switch (m_memcache_seq) {
		case BITS_0:
			m_memcache_seq = event == 1 ? BITS_0_1 : BITS_ERR;
			break;
		case BITS_0_1:
			m_memcache_seq = event == 7 ? BITS_0_1_7 : (event == 6 ? BITS_6 : BITS_ERR);
			break;
		case BITS_6:
			m_memcache_seq = event == 7 ? BITS_6_7 : (event == 0 ? BITS_0 : BITS_ERR);
			break;
		case BITS_7:
			m_memcache_seq = event == 1 ? BITS_7_1 : BITS_ERR;
			break;
		case BITS_EMPTY:
			m_memcache_seq = event == 7 ? BITS_7 : (event == 6 ? BITS_6 : (event == 0 ? BITS_0 : BITS_ERR));
			break;
		default:
			m_memcache_seq = BITS_ERR;
	}

	LOG("memcache seq = %08x\n", m_memcache_seq);
}

void sega_9h0_0008_state::memcache_parse_data_bit(uint32_t &status)
{
	if (m_memcache_seq == BITS_0_1) {
		// After a command has been ack'd, we might reset instead of parse the command's data bits.
		// Due to both sequences starting with the same prefix, we have to bailout on the shorter
		// sequence if the lookahead matches the longer sequence.
		if (m_fifo_events_size && m_fifo_events_data[m_fifo_events_head] == 7) {
			LOG("memcache READ_COMMAND 0 1 skipped\n");
			return;
		}
		m_memcache_seq = BITS_EMPTY;
		m_memcache_data = (m_memcache_data << 1) | 0;
		m_memcache_i++;
	} else if (m_memcache_seq == BITS_6_7) {
		m_memcache_seq = BITS_EMPTY;
		m_memcache_data = (m_memcache_data << 1) | 1;
		m_memcache_i++;
	} else if (m_memcache_seq == BITS_0_1_7) {
		status = 1;
		m_memcache_seq = BITS_EMPTY;
		m_memcache_state = IDLE;
		m_fifo_state_after_size = 0;
		m_fifo_state_after_head = 0;
		m_fifo_state_after_tail = 0;
		LOG("memcache READ_COMMAND 0 1 7 -> IDLE\n");
	}
}

uint32_t sega_9h0_0008_state::io_expansion_r()
{
	return 0; // FIXME: Confirm with hardware tests.
}

uint32_t sega_9h0_0008_state::io_memcache_r()
{
	/*
	    A non-memory mapped cache can be interfaced with data address 0x500300c0,
	    while the status after parsing this data (events) can be read at 0x500300bc.

	    The cache stores RAM values in range 0xc00c6400..0xc00c6fff.

	    During I/O init, this cache is cleared.

	    During Test Mode, some memory integrity checks are carried out,
	    consisting of reading RAM to the cache, then writing those cached bytes
	    at the same RAM address, which is then compared with the expected values.

	    The communication protocol consists of sending sequences of events,
	    contextual to the command that is currently being parsed (which in turn
	    is initiated with a sequence of events).

	    Simplified flow:

	        IDLE
	        READ_COMMAND (includes RAM address bits 8..12)
	            if 0xa0:
	                READ_ADDRESS_BYTE (RAM address bits 0..7)
	                READ_RAM_BYTE (RAM value bits 0..7)
	            if 0xa1:
	                WRITE_RAM_BYTE (assumed to be at previous read RAM address)

	    Some of these sequences can be followed by acknowledgements (READ_ACK), to
	    ensure sent events were parsed as expected, otherwise games bailout from the
	    session. Always ends with the same sequence (BITS_0_1_7).
	*/
	uint32_t status = 0;

	if (machine().side_effects_disabled()) {
		return status;
	}

	while (m_fifo_events_size) {
		memcache_advance(status);

		bool is_memcache_disabled = (m_io_cpu_config->read() & 0x40) == 0x40;
		if (is_memcache_disabled) {
			continue;
		}

		switch (m_memcache_state) {
			case IDLE:
				if (m_memcache_seq == BITS_6) {
					m_memcache_seq = BITS_EMPTY;
					m_memcache_state = CONSUME_UNTIL_IDLE;
					LOG("memcache IDLE stat\n");
				} else if (m_memcache_seq == BITS_7_1) {
					m_memcache_seq = BITS_EMPTY;
					m_memcache_state = READ_COMMAND;
					LOG("memcache IDLE -> READ_COMMAND\n");
				}
				break;
			case READ_COMMAND:
				memcache_parse_data_bit(status);
				if (m_memcache_i == 8) {
					LOG("memcache READ_COMMAND byte data = %08x\n", m_memcache_data);
					m_memcache_state = READ_ACK;
					m_memcache_addr = (((m_memcache_data & 0xe) >> 1) << 8) | (m_memcache_addr & 0xff);
					LOG("memcache READ_COMMAND addr & 0xf00 = %08x\n", m_memcache_addr);
					if ((m_memcache_data & 0xa1) == 0xa0) {
						fifo_state_after_push(READ_ADDRESS_BYTE);
						fifo_state_after_push(READ_ACK);
						fifo_state_after_push(READ_RAM_BYTE);
						LOG("memcache READ_COMMAND 0xa0 -> READ_ACK\n");
					} else if ((m_memcache_data & 0xa1) == 0xa1) {
						fifo_state_after_push(WRITE_RAM_BYTE);
						LOG("memcache READ_COMMAND 0xa1 -> READ_ACK\n");
					} else {
						m_memcache_state = CONSUME_UNTIL_IDLE;
						m_fifo_state_after_size = 0;
						m_fifo_state_after_head = 0;
						m_fifo_state_after_tail = 0;
						LOG("memcache READ_COMMAND -> CONSUME_UNTIL_IDLE (unknown command?)\n");
					}
					m_memcache_data = 0;
					m_memcache_i = 0;
				}
				break;
			case READ_ACK:
				if (m_memcache_seq == BITS_6_7) {
					m_memcache_seq = BITS_EMPTY;
					m_memcache_state = fifo_state_after_pop();
					LOG("memcache READ_ACK\n");
				}
				break;
			case READ_ADDRESS_BYTE:
				memcache_parse_data_bit(status);
				if (m_memcache_i == 8) {
					m_memcache_addr |= m_memcache_data;
					m_memcache_data = 0;
					m_memcache_i = 0;
					m_memcache_state = fifo_state_after_pop();
					LOG("memcache READ_ADDRESS_BYTE addr = %08x\n", m_memcache_addr);
				}
				break;
			case READ_RAM_BYTE:
				memcache_parse_data_bit(status);
				if (m_memcache_seq == BITS_7_1) {
					m_memcache_seq = BITS_EMPTY;
					m_memcache_state = READ_COMMAND;
					m_memcache_data = 0;
					m_memcache_i = 0;
					LOG("memcache READ_RAM_BYTE -> READ_COMMAND (interrupted?)\n");
				}
				if (m_memcache_i == 8) {
					LOG("memcache READ_RAM_BYTE [%08x]=>%02x\n", m_memcache_addr, m_memcache_data);
					LOG("memcache READ_RAM_BYTE effective addr = %08x\n", 0xc00c6400 + 0x100 + m_memcache_addr);
					m_memcache[m_memcache_addr] = m_memcache_data;
					m_memcache_data = 0;
					m_memcache_i = 0;

					// Later games (e.g. "Pocket Monsters Best Wishes") optimize reading the next RAM bytes
					// and just store one after the other, skipping READ_COMMAND + READ_ADDRESS_BYTE sequences.
					m_memcache_state = READ_ACK;
					m_fifo_state_after_size = 0;
					m_fifo_state_after_head = 0;
					m_fifo_state_after_tail = 0;
					fifo_state_after_push(READ_RAM_BYTE);
					m_memcache_addr++;
				}
				break;
			case WRITE_RAM_BYTE:
				if (m_memcache_i == 8) {
					if (m_memcache_seq == BITS_6_7) {
						m_memcache_seq = BITS_EMPTY;
						m_memcache_i = 0;
						LOG("memcache WRITE_RAM_BYTE ACK\n");
					}
				}
				if (m_memcache_seq == BITS_0_1) {
					if (m_fifo_events_size && m_fifo_events_data[m_fifo_events_head] == 7) {
						LOG("memcache WRITE_RAM_BYTE 0 1 skipped\n");
						continue;
					}
					m_memcache_seq = BITS_EMPTY;
					m_memcache_addr++;
					m_memcache_i = 0;
					LOG("memcache WRITE_RAM_BYTE next addr %08x\n", m_memcache_addr);
				} else if (m_memcache_seq == BITS_6_7) {
					status = (m_memcache[m_memcache_addr] >> (7 - m_memcache_i)) & 1;
					LOG("memcache WRITE_RAM_BYTE read addr [%08x]=>(%08x >> %02x) & 1 = %02x\n",
							m_memcache_addr,
							m_memcache[m_memcache_addr],
							m_memcache_i,
							status);
					m_memcache_seq = BITS_EMPTY;
					m_memcache_i++;
				} else if (m_memcache_seq == BITS_0_1_7) {
					status = 1;
					m_memcache_addr = 0;
					m_memcache_data = 0;
					m_memcache_i = 0;
					m_memcache_seq = BITS_EMPTY;
					m_memcache_state = IDLE;
					m_fifo_state_after_size = 0;
					m_fifo_state_after_head = 0;
					m_fifo_state_after_tail = 0;
					LOG("memcache WRITE_RAM_BYTE 0 1 7 -> IDLE\n");
				}
				break;
			case CONSUME_UNTIL_IDLE:
				if (m_memcache_seq == BITS_0_1_7) {
					status = 1;
					m_memcache_addr = 0;
					m_memcache_data = 0;
					m_memcache_i = 0;
					m_memcache_seq = BITS_EMPTY;
					m_memcache_state = IDLE;
					LOG("memcache CONSUME_UNTIL_IDLE 0 1 7 -> IDLE\n");
				}
				break;
		}

		if (m_memcache_seq == BITS_ERR) {
			// Caught unexpected events, so clear all state, and try to resync at next reset bits.
			m_memcache_addr = 0;
			m_memcache_data = 0;
			m_memcache_i = 0;
			m_memcache_seq = BITS_EMPTY;
			m_memcache_state = CONSUME_UNTIL_IDLE;
			m_fifo_state_after_size = 0;
			m_fifo_state_after_head = 0;
			m_fifo_state_after_tail = 0;
			LOG("memcache BITS_ERR -> CONSUME_UNTIL_IDLE\n");
		}
	}

	return status;
}

uint32_t sega_9h0_0008_state::rtc_r(offs_t offset)
{
	return m_rtc[offset];
}

void sega_9h0_0008_state::rtc_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	COMBINE_DATA(&m_rtc[offset]);
	LOG("rtc_w @ %08x [%02x]=%08x\n", m_maincpu->pc(), offset, data);
}

void sega_9h0_0008_state::pal_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	uint16_t *shared16 = reinterpret_cast<uint16_t *>(m_paletteram.target());
	COMBINE_DATA(&shared16[BYTE_XOR_BE(offset)]);

	uint16_t pal_entry = shared16[BYTE_XOR_BE(offset)];
	uint8_t r, g, b;
	r = (pal_entry & 0x001f) >> 0;
	g = (pal_entry & 0x03e0) >> 5;
	b = (pal_entry & 0x7c00) >> 10;
	rgb_t color = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
	m_cache_palette[m_scanline * 0x100 + offset] = color;
	m_palette->set_pen_color(offset, color);
	LOG("m_cache_palette[%d / m_scale * 0x100 + %04x] = %04x (%04x)\n", m_scanline, offset, color, pal_entry);

	m_requested_fiq = true;
}

uint8_t sega_9h0_0008_state::tiles_layers_r(offs_t offset)
{
	return m_tiles_layers[offset];
}

void sega_9h0_0008_state::tiles_layers_w(offs_t offset, uint8_t data)
{
	if (m_tiles_layers[offset] != data) {
		m_tiles_layers[offset] = data;
		m_gfxdecode->gfx(0)->mark_dirty(offset/(16*16));
	}
}

uint8_t sega_9h0_0008_state::tiles_sprites_r(offs_t offset)
{
	return m_tiles_sprites[offset];
}

void sega_9h0_0008_state::tiles_sprites_w(offs_t offset, uint8_t data)
{
	if (m_tiles_sprites[offset] != data) {
		m_tiles_sprites[offset] = data;
		m_gfxdecode->gfx(1)->mark_dirty(offset/(16*16));
	}
}

void sega_9h0_0008_state::draw_layer(
		bitmap_rgb32 &bitmap,
		const rectangle &cliprect,
		const uint16_t *tilemap,
		const uint8_t scroll_idx,
		const bool is_active,
		const bool is_overlay_rendered)
{
	if (!is_active) {
		return; // Disabled
	}

	if ((m_video_regs[0x10/4] & 0x30) == 0x30) {
		draw_layer_scanlines(bitmap, cliprect, tilemap, scroll_idx, is_overlay_rendered);
	} else {
		draw_layer_tiles(bitmap, cliprect, tilemap, scroll_idx, is_overlay_rendered);
	}
}

void sega_9h0_0008_state::draw_layer_tiles(
		bitmap_rgb32 &bitmap,
		const rectangle &cliprect,
		const uint16_t *tilemap,
		const uint8_t scroll_idx,
		const bool is_overlay_rendered)
{
	// Bitplane area =  1024x512 (64x32 tiles), matches tile data mapping entries (64 * 32 = 0x800).
	//  Visible area =   704x480 (44x30 tiles), offset by +10 tiles in x-axis and +2 tiles in y-axis.
	//  Tilemap area = 1024x1024 (64x64 tiles), all these off-screen tiles must be considered for scrolling.
	for (size_t offset = 0; offset < 0x2000/2; offset++) {
		int32_t y = ((offset / 64) - 2) * 16;
		int32_t x = ((offset % 64) - 10) * 16;

		uint16_t scroll_x_idx = scroll_idx;
		uint16_t scroll_y_idx = scroll_idx;
		uint8_t entry_size = 4; // 2 int16_t values, 1 for each tile layer
		if (y >= 0 && y < (0x90 / entry_size) * 16) {
			if ((m_video_regs[0x10/4] & 0x20) == 0x20) {
				scroll_x_idx += y/16 * 2;
			}
		}
		if (x >= 0 && x < (0xb4 / entry_size) * 16) {
			if ((m_video_regs[0x10/4] & 0x40) == 0x40) {
				scroll_y_idx += x/16 * 2;
			}
		}

		x = scroll_x(x, scroll_x_idx);
		y = scroll_y(y, scroll_y_idx);

		uint16_t tile = tilemap[BYTE_XOR_BE(offset)];
		uint16_t tile_transform = (tile & 0xf000) >> 12;
		uint16_t flip_x = (tile_transform & 1) != 0;
		uint16_t flip_y = (tile_transform & 2) != 0;
		bool is_overlay = (tile_transform & 4) != 0;
		if (is_overlay != is_overlay_rendered) {
			continue;
		}

		uint16_t tile_idx = tile & 0x7ff;
		bool is_sprite_tile_data_used = tile & 0x800;
		gfx_element *gfx = m_gfxdecode->gfx(is_sprite_tile_data_used ? 1 : 0);
		const pen_t *paldata = m_palette->pens() + gfx->colorbase() + gfx->granularity() * (7 % gfx->colors());
		const uint8_t scale = m_scale;
		drawgfxzoom_with_pixel_op(gfx, bitmap, cliprect,
				tile_idx,
				flip_x,
				flip_y,
				x * m_scale,
				y * m_scale,
				0x10000 * m_scale,
				0x10000 * m_scale,
				[scale, paldata] (int32_t y, const std::unique_ptr<rgb_t[]> &palcache, uint32_t &destp, const uint8_t &srcp)
				{
					if (srcp != 0) {
						rgb_t color = palcache[y / scale * 0x100 + srcp];
						if (color == 0) {
							color = paldata[srcp];
						}
						destp = color;
					}
				});
	}
}

/**
 * Similar to gfx_element::drawgfxzoom_core(), but uses a pixel_op that depends on the current y-position to render,
 * in order to apply dedicated palettes for each scanline. These palettes are cached and passed directly
 * as a parameter instead of being captured in the pixel_op lambda, to avoid degraded performance from large copies.
 */
template <typename BitmapType, typename FunctionClass>
inline void sega_9h0_0008_state::drawgfxzoom_with_pixel_op(
		gfx_element *gfx,
		BitmapType &dest,
		const rectangle &cliprect,
		uint32_t code,
		int flipx,
		int flipy,
		int32_t
		destx,
		int32_t desty,
		uint32_t scalex,
		uint32_t scaley,
		FunctionClass &&pixel_op)
{
	do {
		assert(dest.valid());
		assert(dest.cliprect().contains(cliprect));

		// ignore empty/invalid cliprects
		if (cliprect.empty())
			break;

		// compute scaled size
		uint32_t dstwidth = (scalex * gfx->width() + 0x8000) >> 16;
		uint32_t dstheight = (scaley * gfx->height() + 0x8000) >> 16;
		if (dstwidth < 1 || dstheight < 1)
			break;

		// compute 16.16 source steps in dx and dy
		int32_t dx = (gfx->width() << 16) / dstwidth;
		int32_t dy = (gfx->height() << 16) / dstheight;

		// compute final pixel in X and exit if we are entirely clipped
		int32_t destendx = destx + dstwidth - 1;
		if (destx > cliprect.right() || destendx < cliprect.left())
			break;

		// apply left clip
		int32_t srcx = 0;
		if (destx < cliprect.left()) {
			srcx = (cliprect.left() - destx) * dx;
			destx = cliprect.left();
		}

		// apply right clip
		if (destendx > cliprect.right())
			destendx = cliprect.right();

		// compute final pixel in Y and exit if we are entirely clipped
		int32_t destendy = desty + dstheight - 1;
		if (desty > cliprect.bottom() || destendy < cliprect.top())
			break;

		// apply top clip
		int32_t srcy = 0;
		if (desty < cliprect.top()) {
			srcy = (cliprect.top() - desty) * dy;
			desty = cliprect.top();
		}

		// apply bottom clip
		if (destendy > cliprect.bottom())
			destendy = cliprect.bottom();

		// apply X flipping
		if (flipx) {
			srcx = (dstwidth - 1) * dx - srcx;
			dx = -dx;
		}

		// apply Y flipping
		if (flipy) {
			srcy = (dstheight - 1) * dy - srcy;
			dy = -dy;
		}

		// fetch the source data
		const uint8_t *srcdata = gfx->get_data(code);

		// compute how many blocks of 4 pixels we have
		uint32_t numblocks = (destendx + 1 - destx) / 4;
		uint32_t leftovers = (destendx + 1 - destx) - 4 * numblocks;

		// iterate over pixels in Y
		for (int32_t cury = desty; cury <= destendy; cury++) {
			auto *destptr = &dest.pix(cury, destx);
			const uint8_t *srcptr = srcdata + (srcy >> 16) * gfx->rowbytes();
			int32_t cursrcx = srcx;
			srcy += dy;

			// iterate over unrolled blocks of 4
			for (int32_t curx = 0; curx < numblocks; curx++) {
				pixel_op(cury, m_cache_palette, destptr[0], srcptr[cursrcx >> 16]);
				cursrcx += dx;
				pixel_op(cury, m_cache_palette, destptr[1], srcptr[cursrcx >> 16]);
				cursrcx += dx;
				pixel_op(cury, m_cache_palette, destptr[2], srcptr[cursrcx >> 16]);
				cursrcx += dx;
				pixel_op(cury, m_cache_palette, destptr[3], srcptr[cursrcx >> 16]);
				cursrcx += dx;

				destptr += 4;
			}

			// iterate over leftover pixels
			for (int32_t curx = 0; curx < leftovers; curx++) {
				pixel_op(cury, m_cache_palette, destptr[0], srcptr[cursrcx >> 16]);
				cursrcx += dx;
				destptr++;
			}
		}
	} while (0);
}

int32_t sega_9h0_0008_state::scroll_x(int32_t x, uint16_t i)
{
	uint16_t *scroll_data = reinterpret_cast<uint16_t *>(m_tilemap_scroll_x.target());
	x += -signed10(scroll_data[BYTE_XOR_BE(i)]);

	// Wrap-around at tilemap borders
	if (x > 0x2c0 + 0xa0) {
		x -= (0x2c0 + 0xa0 * 2);
	}
	if (x < -0xa0) {
		x += (0x2c0 + 0xa0 * 2);
	}

	return x;
}

int32_t sega_9h0_0008_state::scroll_y(int32_t y, uint16_t i)
{
	uint16_t *scroll_data = reinterpret_cast<uint16_t *>(m_tilemap_scroll_y.target());
	uint32_t scroll_entry = scroll_data[BYTE_XOR_BE(i)];
	if (((scroll_entry & 0xfc00) == 0xfc00) && (y <= 224)) {
		// Skip scroll on first 14 (7 if 2x scaled) tile rows.
		// Test case is title screen of "Cooking Beena", where both title and table
		// are rendered in the foreground layer, but only the table should be scrolled.
		return y;
	}

	y += signed10(scroll_entry);

	// Wrap-around at tilemap borders
	if (y > (0x400 - 0x20) - 0x10) {
		y -= 0x400;
	}
	if (y < -0x20) {
		y += 0x400;
	}

	return y;
}

void sega_9h0_0008_state::draw_layer_scanlines(
		bitmap_rgb32 &bitmap,
		const rectangle &cliprect,
		const uint16_t *tilemap,
		const uint8_t scroll_idx,
		const bool is_overlay_rendered)
{
	const rectangle cache_bitmap_bounds(0, m_cache_layer.width()-1, 0, m_cache_layer.height()-1);
	m_cache_layer.fill(0, cache_bitmap_bounds);

	// Apply y-scrolling + wrap-around on each tile row and draw to cached bitmap
	const int8_t tile_factor = 16;
	for (size_t offset = 0; offset < 0x2000/2; offset++) {
		int32_t y = ((offset / 64) - 2) * tile_factor;
		int32_t x = ((offset % 64) - 10) * tile_factor;

		uint16_t tile = tilemap[BYTE_XOR_BE(offset)];
		uint16_t tile_transform = (tile & 0xf000) >> 12;
		uint16_t flip_x = (tile_transform & 1) != 0;
		uint16_t flip_y = (tile_transform & 2) != 0;
		bool is_overlay = (tile_transform & 4) != 0;
		if (is_overlay != is_overlay_rendered) {
			continue;
		}

		uint16_t scroll_y_idx = scroll_idx;
		if (x >= 0 && x < (0xb4 / 4) * tile_factor) {
			if ((m_video_regs[0x10/4] & 0x40) == 0x40) {
				scroll_y_idx += x /tile_factor * 2;
			}
		}

		y = scroll_y(y, scroll_y_idx);

		// Undo the x-axis visible area adjustment, otherwise we clip out tile data
		// that is later used for x-scrolling.
		x += 10 * tile_factor;

		uint16_t tile_idx = tile & 0x7ff;
		bool is_sprite_tile_data_used = tile & 0x800;
		gfx_element *gfx = m_gfxdecode->gfx(is_sprite_tile_data_used ? 1 : 0);
		gfx->zoom_alpha(m_cache_layer, cache_bitmap_bounds,
				tile_idx,
				7,
				flip_x,
				flip_y,
				x,
				y,
				0x10000,
				0x10000,
				0,
				0xff);
	}

	// Apply x-scrolling + wrap-around on each cached scanline and draw to screen bitmap
	for (int8_t yi = 0; yi < 32 / m_scale; yi++) {
		for (int8_t xi = 0; xi < 64; xi++) {
			int32_t y = (yi - 0) * tile_factor;
			int32_t x = (xi - 0) * tile_factor;

			for (int8_t line_yi = 0; line_yi < tile_factor; line_yi++) {
				int32_t dst_y = y + line_yi;
				int32_t src_y = dst_y;

				uint16_t scroll_x_idx = scroll_idx;
				if (y >= 0 && y < (0x90 / 4) * tile_factor) {
					scroll_x_idx += (y + line_yi) * 2;
				}

				for (int8_t line_xi = 0; line_xi < tile_factor; line_xi++) {
					int32_t dst_x = x + line_xi;
					int32_t src_x = dst_x;

					dst_x = scroll_x(dst_x, scroll_x_idx);

					// Redo the x-axis visible area adjustment
					dst_x -= 10 * tile_factor;

					if (dst_x * m_scale < cliprect.min_x
						|| dst_x * m_scale > cliprect.max_x
						|| dst_y * m_scale < cliprect.min_y
						|| dst_y * m_scale > cliprect.max_y) {
						continue; // Skip beyond screen size
					}
					if (src_x < m_cache_layer.cliprect().min_x
						|| src_x > m_cache_layer.cliprect().max_x
						|| src_y < m_cache_layer.cliprect().min_y
						|| src_y > m_cache_layer.cliprect().max_y) {
						continue; // Skip beyond cache size
					}

					uint32_t *src = &m_cache_layer.pix(src_y, src_x);
					if (*src != 0) {
						for (size_t dyi = 0; dyi < m_scale; dyi++) {
							for (size_t dxi = 0; dxi < m_scale; dxi++) {
								bitmap.pix(dyi + dst_y * m_scale, dxi + dst_x * m_scale) = *src;
							}
						}
					}
				}
			}
		}
	}
}

void sega_9h0_0008_state::draw_bitmap(bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if ((m_video_regs[0x20/4] & 1) == 0) {
		return; // Disabled
	}

	uint16_t *bitmap_data = reinterpret_cast<uint16_t *>(m_bitmap.target());

	uint16_t bitmap_full_w   = ((m_video_regs[0x20/4] >> 8)  & 0xff) << 4;
	uint16_t bitmap_full_h   = ((m_video_regs[0x20/4] >> 16) & 0xff) << 4;
	uint16_t bitmap_offset_x = ((m_video_regs[0x24/4] >> 0)  & 0x3ff);
	uint16_t bitmap_offset_y = ((m_video_regs[0x24/4] >> 16) & 0x3ff);
	uint16_t bitmap_scroll_x = ((m_video_regs[0x28/4] >> 0)  & 0x3ff);
	uint16_t bitmap_scroll_y = ((m_video_regs[0x28/4] >> 16) & 0x3ff);
	uint16_t bitmap_clip_w   = ((m_video_regs[0x2c/4] >> 0)  & 0x3ff);
	uint16_t bitmap_clip_h   = ((m_video_regs[0x2c/4] >> 16) & 0x3ff);
	uint32_t i = bitmap_offset_y * bitmap_full_w + bitmap_offset_x;
	for (size_t y = 0; y < bitmap_full_h * m_scale; y += m_scale) {
		for (size_t x = 0; x < bitmap_full_w * m_scale; x += m_scale) {
			if (i >= 0xc6000/2) {
				return;
			}

			uint16_t color = bitmap_data[BYTE_XOR_BE(i)];
			i++;

			if (color == 0
					|| y >= bitmap_clip_h * m_scale
					|| x >= bitmap_clip_w * m_scale
					|| y + bitmap_scroll_y * m_scale > cliprect.max_y
					|| x + bitmap_scroll_x * m_scale > cliprect.max_x) {
				continue;
			}

			uint8_t r = pal5bit((color & 0x001f) >> 0);
			uint8_t g = pal5bit((color & 0x03e0) >> 5);
			uint8_t b = pal5bit((color & 0x7c00) >> 10);
			uint32_t rgb = (b) | (g << 8) | (r << 16);
			for (size_t yi = 0; yi < m_scale; yi++) {
				for (size_t xi = 0; xi < m_scale; xi++) {
					bitmap.pix(y + yi + bitmap_scroll_y * m_scale, x + xi + bitmap_scroll_x * m_scale) = rgb;
				}
			}
		}
	}
}

void sega_9h0_0008_state::draw_sprites(bitmap_rgb32 &bitmap, const rectangle &cliprect, const bool is_overlay_rendered)
{
	gfx_element *gfx = m_gfxdecode->gfx(1);

	uint16_t *tilemap_data = reinterpret_cast<uint16_t *>(m_tilemap_sprites.target());

	// For sprites to overlap as expected, they must be drawn in reverse order
	for (int16_t offset = 0x400/2 - 4; offset >= 0; offset -= 4) {
		uint16_t priority =  tilemap_data[BYTE_XOR_BE(offset + 1)] & 0xff;
		if ((priority > 0 && !is_overlay_rendered) || (priority == 0 && is_overlay_rendered)) {
			continue;
		}

		uint32_t tile_pos = (tilemap_data[BYTE_XOR_BE(offset)] << 16) | tilemap_data[BYTE_XOR_BE(offset + 1)];
		uint16_t y = (tile_pos >> 20) & 0x7ff;
		uint16_t x = (tile_pos >> 8)  & 0x7ff;

		uint16_t flip_y = ((tile_pos >> 20) & 0x800) != 0;
		uint16_t flip_x = ((tile_pos >> 8)  & 0x800) != 0;

		int16_t delta_factor = m_video_regs[0x38/4] & 0xff;
		int16_t delta_x = 0;
		int16_t delta_y = 0;
		if (delta_factor == 0x2c) {
			delta_x = 14 * 16;
			delta_y =  7 * 16;
		} else if (delta_factor == 0x20) {
			delta_x =  6 * 16;
			delta_y = -1 * 16;
		}

		uint16_t tile_idx = tilemap_data[BYTE_XOR_BE(offset + 2)] & 0x7ff;
		uint16_t tile_pattern_len = (tilemap_data[BYTE_XOR_BE(offset + 2)] & 0xf000) >> 12;

		if (tile_idx == 0 && x == 0 && y == 0)
			continue;

		uint8_t pattern_x_len = ((tile_pattern_len % 4) + 1);
		uint8_t pattern_y_len = ((tile_pattern_len / 4) + 1);
		uint8_t pattern_i = 0;

		std::vector<uint8_t> pattern_x_v(pattern_x_len);
		std::iota(pattern_x_v.begin(), pattern_x_v.end(), 0);
		if (flip_x) {
			std::reverse(pattern_x_v.begin(), pattern_x_v.end());
		}
		std::vector<uint8_t> pattern_y_v(pattern_y_len);
		std::iota(pattern_y_v.begin(), pattern_y_v.end(), 0);
		if (flip_y) {
			std::reverse(pattern_y_v.begin(), pattern_y_v.end());
		}
		for (size_t pattern_y : pattern_y_v) {
			for (size_t pattern_x : pattern_x_v) {
				// FIXME: Can be transparent (e.g. pen cursor fade-in seen in "Kazoku Minna no Nouryoku Trainer")
				gfx->zoom_alpha(bitmap, cliprect,
						tile_idx + pattern_i++,
						7,
						flip_x,
						flip_y,
						(x + pattern_x * 16) * m_scale - (0x2c0 / 2) + delta_x,
						(y + pattern_y * 16) * m_scale - (0x1e0 / 2) + delta_y,
						0x10000 * m_scale,
						0x10000 * m_scale,
						0,
						0xff);
			}
		}
	}
}

void sega_9h0_0008_state::update_crosshair(screen_device &screen)
{
	// Nothing (only the console uses a crosshair)
}

uint32_t sega_9h0_0008_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	update_crosshair(screen);

	bitmap.fill(m_palette->pen_color(0), cliprect);

	if ((m_video_regs[0x10/4] & 1) == 0) {
		screen_blend(bitmap, cliprect);

		return 0; // Disabled
	}

	uint16_t *shared16_tilemap_bg = reinterpret_cast<uint16_t *>(m_tilemap_bg.target());
	uint16_t *shared16_tilemap_fg = reinterpret_cast<uint16_t *>(m_tilemap_fg.target());

	if (((m_video_regs[0x10/4] & 0x3000) == 0x3000)) {
		draw_bitmap(bitmap, cliprect);
	}

	draw_layer(bitmap, cliprect, shared16_tilemap_bg, 1, (m_video_regs[0x10/4] & 4) != 0, false);

	if (((m_video_regs[0x10/4] & 0x3000) == 0x2000)) {
		draw_bitmap(bitmap, cliprect);
	}

	draw_layer(bitmap, cliprect, shared16_tilemap_fg, 0, (m_video_regs[0x10/4] & 2) != 0, false);

	bool is_bitmap_after_sprites = (m_video_regs[0x20/4] & 0x10) != 0;
	if (((m_video_regs[0x10/4] & 0x3000) == 0x1000) && !is_bitmap_after_sprites) {
		draw_bitmap(bitmap, cliprect);
	}

	draw_sprites(bitmap, cliprect, false);

	draw_layer(bitmap, cliprect, shared16_tilemap_bg, 1, (m_video_regs[0x10/4] & 4) != 0, true);

	if (((m_video_regs[0x10/4] & 0x3000) == 0) && !is_bitmap_after_sprites) {
		draw_bitmap(bitmap, cliprect);
	}

	// TV bottom frame in front of running dog in "Partner in TV"
	draw_layer(bitmap, cliprect, shared16_tilemap_fg, 0, (m_video_regs[0x10/4] & 2) != 0, true);

	if (((m_video_regs[0x10/4] & 0x3000) == 0x1000) && is_bitmap_after_sprites) {
		// Minimap after lamp posts in "Cars 2" bridge section
		draw_bitmap(bitmap, cliprect);
	}

	draw_sprites(bitmap, cliprect, true);

	if (((m_video_regs[0x10/4] & 0x3000) == 0) && is_bitmap_after_sprites) {
		draw_bitmap(bitmap, cliprect);
	}

	screen_blend(bitmap, cliprect);

	// Invalidate on VBLANK: the latest entries are kept by m_palette.
	for (int i = 0; i < SCREEN_H; i++)
		for (int offset = 0; offset < 0x100; offset++)
			m_cache_palette[i * 0x100 + offset] = 0;

	return 0;
}

void sega_9h0_0008_state::screen_blend(bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	uint8_t alpha_r_step = (m_video_regs[0x30/4] >>  0) & 0x3f;
	uint8_t alpha_g_step = (m_video_regs[0x30/4] >>  8) & 0x3f;
	uint8_t alpha_b_step = (m_video_regs[0x30/4] >> 16) & 0x3f;

	uint8_t alpha_r = rescale_alpha_step(alpha_r_step);
	uint8_t alpha_g = rescale_alpha_step(alpha_g_step);
	uint8_t alpha_b = rescale_alpha_step(alpha_b_step);

	uint8_t blend_r = alpha_r_step < 0x20 ? 0xff : 0;
	uint8_t blend_g = alpha_g_step < 0x20 ? 0xff : 0;
	uint8_t blend_b = alpha_b_step < 0x20 ? 0xff : 0;
	uint32_t blend_rgb = blend_b | (blend_g << 8) | (blend_r << 16);

	for (size_t y = cliprect.min_y; y <= cliprect.max_y; y++) {
		for (size_t x = cliprect.min_x; x <= cliprect.max_x; x++) {
			uint32_t *dst = &bitmap.pix(y, x);
			*dst = alpha_blend_rgb_levels(blend_rgb, *dst, alpha_b, alpha_r, alpha_g);
		}
	}
}

/**
 * Similar to sega_9h0_0008_state::rescale(),
 * but the step can either lighten (e.g. video debug flag `0xc00c7cc7 = 1`
 * in "Soreike! Anpanman Hajimete Kaketa yo! Oboeta yo! Hiragana Katakana"
 * or darken (e.g. screen fadeout).
 */
int32_t sega_9h0_0008_state::rescale_alpha_step(uint8_t step)
{
	return step < 0x20 ? rescale(step, 0, 0x1f, 0xff, 0) : rescale(step, 0x20, 0x3f, 0, 0xff);
}

void sega_9h0_0008_state::request_irq()
{
	m_maincpu->set_input_line(arm7_cpu_device::ARM7_IRQ_LINE, ASSERT_LINE);
	m_maincpu->set_input_line(arm7_cpu_device::ARM7_IRQ_LINE, CLEAR_LINE);
}

void sega_9h0_0008_state::request_fiq()
{
	if (m_requested_fiq) {
		m_maincpu->set_input_line(arm7_cpu_device::ARM7_FIRQ_LINE, ASSERT_LINE);
		m_maincpu->set_input_line(arm7_cpu_device::ARM7_FIRQ_LINE, CLEAR_LINE);

		m_requested_fiq = false;
	}
}

void sega_9h0_0008_state::update_rtc()
{
	system_time systime;
	machine().current_datetime(systime);
	m_rtc[0] = systime.local_time.second;
	m_rtc[1] = systime.local_time.minute;
	m_rtc[2] = systime.local_time.hour;
	m_rtc[3] = systime.local_time.mday;
	m_rtc[4] = systime.local_time.weekday;
	m_rtc[5] = systime.local_time.month;
	m_rtc[6] = systime.local_time.year - 2000;
}

void sega_9h0_0008_state::sega_9h0_0008(machine_config &config)
{
	AP2010CPU(config, m_maincpu, 81'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &sega_9h0_0008_state::beena_arm7_map);

	SCREEN(config, m_screen_main, SCREEN_TYPE_RASTER);
	m_screen_main->set_refresh_hz(60);
	m_screen_main->set_vblank_time(ATTOSECONDS_IN_USEC(0));
	m_screen_main->set_size(SCREEN_W, SCREEN_H);
	m_screen_main->set_visarea(0, SCREEN_W-1, 0, SCREEN_H-1);
	m_screen_main->set_screen_update(FUNC(sega_9h0_0008_state::screen_update));

	TIMER(config, "scantimer").configure_scanline(FUNC(sega_9h0_0008_state::scanline), m_screen_main, 0, 1);

	PALETTE(config, m_palette).set_entries(0x100);

	GFXDECODE(config, m_gfxdecode, m_palette, gfxdecode_device::empty);

	SPEAKER(config, "speaker").front_center();
	AP2010PCM(config, m_pcm); // Unknown clock
	m_pcm->add_route(ALL_OUTPUTS, "speaker", 1.0);
}


class sega_9h0_0008_cart_state : public sega_9h0_0008_state
{
public:
	sega_9h0_0008_cart_state(const machine_config &mconfig, device_type type, const char *tag)
		: sega_9h0_0008_state(mconfig, type, tag)
		, m_cart(*this, "cartslot")
	{ }

protected:
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	required_device<generic_slot_device> m_cart;
};

DEVICE_IMAGE_LOAD_MEMBER(sega_9h0_0008_cart_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM32_WIDTH, ENDIANNESS_BIG);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	if (!image.loaded_through_softlist()) {
		// loadflags weren't parsed, start by manually applying `endianness="big"`,
		// taking into consideration the host's endianness.
		uint32_t *rom32 = reinterpret_cast<uint32_t *>(m_cart->get_rom_base());
		for (size_t i = 0; i < size / 4; i++) {
			rom32[i] = big_endianize_int32(rom32[i]);
		}
		// Afterwards apply `load16_word_swap`, regardless of host's endianness,
		// since this reflects how ROM data was stored, not the CPU's architecture.
		uint16_t *rom16 = reinterpret_cast<uint16_t *>(m_cart->get_rom_base());
		for (size_t i = 0; i < size / 2; i++) {
			rom16[i] = swapendian_int16(rom16[i]);
		}
	}

	return std::make_pair(std::error_condition(), std::string());
}


class sega_beena_state : public sega_9h0_0008_cart_state
{
public:
	sega_beena_state(const machine_config &mconfig, device_type type, const char *tag)
		: sega_9h0_0008_cart_state(mconfig, type, tag)
		, m_card(*this, "card")
		, m_io_page_config(*this, "PAGE_CONFIG")
		, m_io_page(*this, "PAGE")
		, m_io_pad_left(*this, "PAD_LEFT")
		, m_io_pad_right(*this, "PAD_RIGHT")
		, m_io_pen_left(*this, "PEN_LEFT")
		, m_io_pen_right(*this, "PEN_RIGHT")
		, m_io_pen_x(*this, "PENX")
		, m_io_pen_y(*this, "PENY")
	{ }

	void sega_beena(machine_config &config);

	virtual DECLARE_CROSSHAIR_MAPPER_MEMBER(pen_y_mapper);

protected:
	virtual void video_start() override ATTR_COLD;

private:
	virtual void install_game_rom() override;
	virtual uint32_t io_expansion_r() override;
	virtual void update_crosshair(screen_device &screen) override;
	virtual void update_sensors(offs_t offset) override;

	required_device<sega_9h0_0008_card_device> m_card;
	required_ioport m_io_page_config;
	required_ioport m_io_page;
	required_ioport m_io_pad_left;
	required_ioport m_io_pad_right;
	required_ioport m_io_pen_left;
	required_ioport m_io_pen_right;
	required_ioport m_io_pen_x;
	required_ioport m_io_pen_y;
};

static void beena_iox_devices(device_slot_interface &device)
{
	device.option_add("rd", BEENA_CARD_READER);
	device.option_add("rd2061", BEENA_CARD_READER_2061);
}

void sega_beena_state::sega_beena(machine_config &config)
{
	sega_9h0_0008(config);

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "sega_beena_cart");
	m_cart->set_endian(ENDIANNESS_BIG);
	m_cart->set_width(GENERIC_ROM32_WIDTH);
	m_cart->set_device_load(FUNC(sega_beena_state::cart_load));
	m_cart->set_must_be_loaded(false);

	SEGA_9H0_0008_CARD(config, m_card, beena_iox_devices, nullptr, false);

	SOFTWARE_LIST(config, "cart_list").set_original("sega_beena_cart");

	config.set_default_layout(layout_beena);
}

void sega_beena_state::video_start()
{
	const int view = m_card->get_card_device() ? 1 : 0;
	machine().render().first_target()->set_view(view);

	sega_9h0_0008_state::video_start();
}

void sega_beena_state::install_game_rom()
{
	if (m_cart->exists()) {
		std::string region_tag;
		memory_region *cart_rom = m_cart->memregion("cart:rom");
		uint32_t *rom32 = reinterpret_cast<uint32_t *>(cart_rom->base());

		/*
		    ROM header contains a field for the base address which we can use to map
		    the corresponding address range. On hardware, this mapping is done by the BIOS when
		    writing to the following registers:

		        0x60020004=0x1ff and 0x60020010=0xa5: ROM_MASK_BASE mapped when the cart has a mask ROM;
		        0x60020004=0x3fc and 0x60020010=0x90: ROM_FLASH_BASE mapped when the cart has a flash ROM;

		    Signatures on each address map are then checked to choose which one will run game code.

		    It's possible to force some invalid mappings which result in the console hanging during reads.
		    For example, setting 0x60020004=0x1ff and 0x60020010=0x90 will map both address ranges
		    when the cart has a mask ROM.
		*/
		uint32_t base_address = rom32[0xdc/4];
		if (base_address != ROM_MASK_BASE && base_address != ROM_FLASH_BASE) {
			osd_printf_warning("Unknown base address '%08x' parsed from ROM, defaulting to '%08x'...\n", base_address, ROM_MASK_BASE);
			base_address = ROM_MASK_BASE;
		}
		m_maincpu->space(AS_PROGRAM).install_rom(base_address, base_address + 0x7fffff, 0x800000, cart_rom->base());
	}
}

/**
 * Combines:
 * - Scanned card barcode
 *   - bit 5: current data bit to read from 16-bit value;
 *   - bit 6: status for advancing data bit position;
 * - Peripheral status
 *   - bit 7 == 0: peripheral is ready;
 */
uint32_t sega_beena_state::io_expansion_r()
{
	if (m_card->get_card_device()) {
		return m_card->get_card_device()->read(true);
	}

	return 0; // FIXME: Confirm with hardware tests.
}

void sega_beena_state::update_crosshair(screen_device &screen)
{
	// Show crosshair on single screen with storyware pen target
	machine().crosshair().get_crosshair(0).set_screen(m_pen_target ? CROSSHAIR_SCREEN_NONE : &screen);
}

void sega_beena_state::update_sensors(offs_t offset)
{
	sega_9h0_0008_state::update_sensors(offset);

	uint16_t io_page = 0;
	int16_t io_pen_x = 0;
	int16_t io_pen_y = 0;
	int16_t io_pen_x_min = m_io_pen_x->field(0xff)->minval();
	int16_t io_pen_x_max = m_io_pen_x->field(0xff)->maxval();
	int16_t io_pen_y_min = m_io_pen_y->field(0xff)->minval();
	int16_t io_pen_y_max = m_io_pen_y->field(0xff)->maxval();
	int16_t pen_y_min = io_pen_y_min;
	int16_t pen_y_max = io_pen_y_max;
	uint32_t pen_pressed = 0;
	uint32_t pen_data = 0;

	bool is_tablet = true;
	bool is_test_mode = m_io_page_config->read() == 0;

	switch (offset) {
		// Pen position + button press state.
		//
		// A minimum of 3 readings must be parsed, otherwise effective pen position is not computed.
		// Later games seem to only require 2 readings.
		//
		// Each reading encodes bits `pyyy yyyy yyyx xxxx xxxx`, where `p` is 1 on pen button pressed.
		//
		// Offsets of readings for each pen are given by 3 bits set at 0x50020000:
		// - e.g. for 1st pen, 0x50020000=0x05 will parse 0x50020008, and 0x50020010, ignoring 0x5002000c;
		// - e.g. for 2nd pen, 0x50020000=0x70 will parse 0x50020018, 0x5002001c, and 0x50020020;
		//
		// TODO: Check how 2 connected pens are reported. Regardless of where a single pen is connected
		// (ports marked on PCB as CN12 or CN13), the first set of offsets is updated.
		//
		// 1st pen's 1st reading.
		case 0x8/4:
		// 1st pen's 2nd reading.
		case 0xc/4:
		// 1st pen's 3rd reading.
		case 0x10/4:
			io_pen_x = m_io_pen_x->read();
			io_pen_y = m_io_pen_y->read();

			m_pen_target = BIT(m_io_pen_left->read(), 1);

			// The y-position is used to distinguish between pen targets:
			// - Tablet:    0x338..0x248
			// - Storyware: 0x3b8..0x342
			//
			// The storyware's coordinates don't reach maximum screen positions (games don't compute
			// effective pen positions if such values are forced), possibly to account for page tab gaps.
			// Applying the same displacements to rescaled positions also results in more accurate mappings.
			//
			// FIXME: Confirm displacements with hardware tests.
			is_tablet = m_pen_target == 0;
			if (is_tablet) {
				// screen x-position [0..0x2c0]
				io_pen_x = rescale(io_pen_x, io_pen_x_min, io_pen_x_max, 0x1b8, 0x58);
				// screen y-position [0..0x1e0]
				io_pen_y = rescale(io_pen_y, pen_y_min, pen_y_max, 0x338, 0x248);
			}
			else {
				// screen x-position [8..0x2bc], off-by-4 upper bound?
				io_pen_x = rescale(io_pen_x, io_pen_x_min+8, io_pen_x_max-8, 0x1b6, 0x109);
				// screen y-position [0..0x1d8]
				io_pen_y = rescale(io_pen_y, pen_y_min+8, pen_y_max, 0x3b8, 0x342);
			}
			pen_pressed = (m_io_pen_left->read() & 1) ? 0x80000 : 0;
			pen_data = (io_pen_y << 9) | io_pen_x | pen_pressed;
			LOG("Pen on %s(%d): x=%04x,y=%04x => %08x",
				is_tablet ? "tablet" : "storyware",
				m_pen_target,
				io_pen_x,
				io_pen_y,
				pen_data);

			m_io_sensor_regs[offset] = pen_data;
			break;
		// Pages 1 to 4, one for each byte value.
		// We'll just threshold them to 0x00 (open) and 0xff (closed).
		case 0x2c/4:
			io_page = m_io_page->read();
			if (is_test_mode) {
				m_io_sensor_regs[offset] = 0xff00ff00;
				m_effective_page = 6;
			} else if ((m_effective_page ^ io_page) != 0) {
				m_effective_page = io_page;
				m_io_sensor_regs[offset] = (0xffffffffULL >> (m_effective_page * 8));
				LOG("Selected page: %d\n", m_effective_page);
			}

			break;
		// Pages 5 to 6, encodes bytes `XX AA BB YY`
		// - XX: unused? always 0x00 on hardware;
		// - YY: needs to be parsed as open for effective page computation (TODO: is this a sensor?);
		// - AA: page 5;
		// - BB: page 6;
		case 0x30/4:
			if (is_test_mode) {
				m_io_sensor_regs[offset] = 0xffff00ff;
			} else {
				switch (m_effective_page) {
					case 5:
						m_io_sensor_regs[offset] = 0x0000ff00;
						break;
					case 6:
						m_io_sensor_regs[offset] = 0x00000000;
						break;
					default:
						m_io_sensor_regs[offset] = 0x00ffff00;
				}
			}

			break;
		// Left and right pads, encodes bits `XXXX XXAB CDEF GHIJ`
		// - X: unused?
		// - ABCDE: red + directional buttons of left pad;
		// - FGHIJ: red + directional buttons of right pad;
		case 0x34/4:
			m_io_sensor_regs[offset] = ((m_io_pad_left->read() << 5) | m_io_pad_right->read()) & 0x3ff;
			break;
	}
}

CROSSHAIR_MAPPER_MEMBER(sega_beena_state::pen_y_mapper)
{
	// TODO: Either remove or adapt for Storyware layout
	return linear_value;
}


class tvochken_state : public sega_9h0_0008_state
{
public:
	tvochken_state(const machine_config &mconfig, device_type type, const char *tag)
		: sega_9h0_0008_state(mconfig, type, tag)
		, m_card(*this, "card")
		, m_io_buttons(*this, "BUTTONS")
	{ }

	void tvochken(machine_config &config);

	virtual uint32_t io_expansion_r() override;

private:
	virtual void install_game_rom() override;

	required_device<sega_9h0_0008_card_device> m_card;
	required_ioport m_io_buttons;
};

static void tvochken_iox_devices(device_slot_interface &device)
{
	device.option_add("rd1831", TVOCHKEN_CARD_READER);
	device.set_default_option("rd1831");
	device.set_fixed(true);
}

void tvochken_state::tvochken(machine_config &config)
{
	sega_9h0_0008(config);

	SEGA_9H0_0008_CARD(config, m_card, tvochken_iox_devices, nullptr, false);

	SOFTWARE_LIST(config, "card_list").set_original("tvochken");

	config.set_default_layout(layout_tvochken);
}

/**
 * Combines all inputs:
 * - Pressed buttons
 *   - bit 1: A;
 *   - bit 2: B;
 *   - bit 3: C;
 * - Scanned card barcode
 *   - bit 5: current data bit to read from 16-bit value;
 *   - bit 6: status for advancing data bit position;
 *
 * Parsing protocol for tvochken at function 0xa0001240 doesn't seem to use any
 * control commands to start parsing either input. It expects each parsed barcode
 * to take several reads before advancing to the next data bit. We handle this
 * by holding the value for a few reads, which also covers reads unrelated
 * to barcodes.
 */
uint32_t tvochken_state::io_expansion_r()
{
	const uint32_t card_data = m_card->get_card_device()
		? m_card->get_card_device()->read((m_io_auxiliary_regs[0x8/4] & 0xff) == 0)
		: 0;
	return card_data | m_io_buttons->read();
}

void tvochken_state::install_game_rom()
{
	memory_region *rom = memregion("flash_rom");
	m_maincpu->space(AS_PROGRAM).install_rom(ROM_FLASH_BASE, ROM_FLASH_BASE + 0x7fffff, 0x800000, rom->base());
}


class carbeena_state : public sega_9h0_0008_cart_state
{
public:
	carbeena_state(const machine_config &mconfig, device_type type, const char *tag)
		: sega_9h0_0008_cart_state(mconfig, type, tag)
		, m_io_buttons(*this, "BUTTONS")
	{ }

	void carbeena(machine_config &config);

	virtual uint32_t io_expansion_r() override;

private:
	virtual void install_game_rom() override;

	required_ioport m_io_buttons;
};

void carbeena_state::carbeena(machine_config &config)
{
	sega_9h0_0008(config);

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "carbeena_cart");
	m_cart->set_endian(ENDIANNESS_BIG);
	m_cart->set_width(GENERIC_ROM32_WIDTH);
	m_cart->set_device_load(FUNC(carbeena_state::cart_load));
	m_cart->set_must_be_loaded(false);

	SOFTWARE_LIST(config, "cart_list").set_original("carbeena_cart");
}

uint32_t carbeena_state::io_expansion_r()
{
	return m_io_buttons->read() & 0x3f;
}

void carbeena_state::install_game_rom()
{
	// TODO: These mappings are controlled by program code via writes to 0x60020004 and 0x60020010.
	// Likely only one of them should be mapped at a time (also suggested by test mode, which only
	// outputs 1 size out of 2 installed ROMs).
	if (m_cart->exists()) {
		std::string region_tag;
		memory_region *cart_rom = m_cart->memregion("cart:rom");
		m_maincpu->space(AS_PROGRAM).install_rom(ROM_FLASH_BASE, ROM_FLASH_BASE + 0x7fffff, 0x800000, cart_rom->base());
	}

	// Despite being a flash ROM, it gets mapped on the base address usually assigned to mask ROMs.
	memory_region *rom = memregion("mainboard_rom");
	m_maincpu->space(AS_PROGRAM).install_rom(ROM_MASK_BASE, ROM_MASK_BASE + 0x7fffff, 0x800000, rom->base());
}


static INPUT_PORTS_START( sega_9h0_0008 )
	PORT_START("VIDEO_CONFIG")
	PORT_CONFNAME( 0x80, 0x00, "Video Output" )
	PORT_CONFSETTING( 0x00, DEF_STR( On ) )
	PORT_CONFSETTING( 0x80, DEF_STR( Off ) )

	PORT_START("CPU_CONFIG")
	PORT_CONFNAME( 0x0f, 0x01, "IRQ Frequency" )
	PORT_CONFSETTING( 0x01, "1 (Once every VBLANK)" )
	PORT_CONFSETTING( 0x02, "2" )
	PORT_CONFSETTING( 0x03, "3" )
	PORT_CONFSETTING( 0x04, "4" )
	PORT_CONFSETTING( 0x05, "5" )
	PORT_CONFNAME( 0x10, 0x10, "IRQ Wait Speedup" )
	PORT_CONFSETTING( 0x00, DEF_STR( On ) )
	PORT_CONFSETTING( 0x10, DEF_STR( Off ) )
	PORT_CONFNAME( 0x60, 0x20, "Memory Cache" )
	PORT_CONFSETTING( 0x20, DEF_STR( On ) )
	PORT_CONFSETTING( 0x40, DEF_STR( Off ) )
INPUT_PORTS_END

static INPUT_PORTS_START( sega_beena )
	PORT_INCLUDE( sega_9h0_0008 )

	PORT_START("PAGE_CONFIG")
	PORT_CONFNAME( 0x80, 0x80, "Test Mode Pages" )
	PORT_CONFSETTING( 0x00, DEF_STR( On ) )
	PORT_CONFSETTING( 0x80, DEF_STR( Off ) )

	PORT_START("PAD_LEFT")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICKLEFT_UP ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICKLEFT_DOWN ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICKLEFT_LEFT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICKLEFT_RIGHT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("Left Red Button")

	PORT_START("PAD_RIGHT")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICKRIGHT_UP ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICKRIGHT_DOWN ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICKRIGHT_LEFT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICKRIGHT_RIGHT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("Right Red Button")

	PORT_START("PEN_LEFT")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("Left Pen Button")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_TOGGLE PORT_PLAYER(1) PORT_NAME("Left Pen Target")

	PORT_START("PEN_RIGHT")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_PLAYER(1) PORT_NAME("Right Pen Button")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_TOGGLE PORT_PLAYER(1) PORT_NAME("Right Pen Target")

	PORT_START("PAGE")
	PORT_BIT( 0x0f, 0x00, IPT_POSITIONAL ) PORT_POSITIONS(7) PORT_WRAPS PORT_SENSITIVITY(10) PORT_KEYDELTA(1) PORT_CODE(JOYCODE_X) PORT_CODE_DEC(KEYCODE_HOME) PORT_CODE_INC(KEYCODE_END) PORT_FULL_TURN_COUNT(7) PORT_NAME("Selected Page")

	PORT_START("PENX")
	PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(30) PORT_KEYDELTA(10) PORT_MINMAX(0, 255) PORT_PLAYER(1) PORT_NAME("Pen X")

	PORT_START("PENY")
	PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_SENSITIVITY(30) PORT_KEYDELTA(10) PORT_MINMAX(0, 255) PORT_PLAYER(1) PORT_NAME("Pen Y") PORT_CROSSHAIR_MAPPER_MEMBER(FUNC(sega_beena_state::pen_y_mapper))
INPUT_PORTS_END

static INPUT_PORTS_START( tvochken )
	PORT_INCLUDE( sega_9h0_0008 )

	PORT_START("BUTTONS")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("A")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("B")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("C")
INPUT_PORTS_END

static INPUT_PORTS_START( carbeena )
	PORT_INCLUDE( sega_9h0_0008 )

	PORT_START("BUTTONS")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_NAME("Handle Left")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_NAME("Handle Limit") // TODO: Likely set when turning the handle to full lock
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("Handle Right")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("Blue Button") // At the right of the center button, despite being mapped to a lower bit
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("Center Button")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("Yellow Button")
INPUT_PORTS_END

#define ROM_9H0_0008 \
	/* SoC internal BIOS dumped with a JTAG debugger. */ \
	ROM_REGION32_BE( 0x20000, "maincpu", 0 ) \
	ROM_LOAD( "9h0-0008.bios.ic1", 0x00000000, 0x20000, CRC(5471aaf8) SHA1(33d756b6c64afb0fa377b3f85ab74505e9ae2f9c) ) \
	/* SoC MIDI synthesizer parameters and PCM data. */ \
	/* Control ROM doesn't appear to be memory-mapped, so this data will only */ \
	/* be usable when the synthesizer gets reverse engineered. */ \
	ROM_REGION32_BE( 0x8000, "midipcm", 0 ) \
	ROM_LOAD( "9h0-0008.midipcm.ic1", 0x00000000, 0x8000, CRC(ed336d29) SHA1(4af7b7cf7fc4fe8b7a514cde91f930a582742509) )

ROM_START( beena )
	ROM_9H0_0008
ROM_END

ROM_START( tvochken )
	ROM_9H0_0008

	ROM_REGION32_BE( 0x400000, "flash_rom", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "m5m29gt320vp-80.u3", 0x00000000, 0x400000, CRC(75c1fbc1) SHA1(b07adcabaadb8b684335f52dd953f4696585c819) )
ROM_END

ROM_START( carbeena )
	ROM_9H0_0008

	ROM_REGION32_BE( 0x800000, "mainboard_rom", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "en29lv640b.ic9", 0x00000000, 0x800000, CRC(ce6649df) SHA1(e09568b93c9ab0901c6eb32d5e0408e484d4c564) )
ROM_END

} // anonymous namespace

//    year, name,     parent, compat, machine,    input,      class,            init,       company,                                fullname,              flags
CONS( 2005, beena,    0,      0,      sega_beena, sega_beena, sega_beena_state, empty_init, "Sega Toys",                            "Advanced Pico BEENA", MACHINE_IMPERFECT_GRAPHICS|MACHINE_IMPERFECT_TIMING|MACHINE_IMPERFECT_SOUND )
CONS( 2005, tvochken, 0,      0,      tvochken,   tvochken,   tvochken_state,   empty_init, "Sega Toys",                            "TV Ocha-Ken",         MACHINE_IMPERFECT_GRAPHICS|MACHINE_IMPERFECT_TIMING|MACHINE_IMPERFECT_SOUND )
// Release date 2009-12: http://products.alpine.co.jp/om/owner/product?P1=1632
CONS( 2009, carbeena, 0,      0,      carbeena,   carbeena,   carbeena_state,   empty_init, "Sega Toys / Alpine Electronics, Inc.", "Car Beena",           MACHINE_IMPERFECT_GRAPHICS|MACHINE_IMPERFECT_TIMING|MACHINE_IMPERFECT_SOUND )



sega_ferie.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:QUFB
/******************************************************************************

    Sega Ferie (c) 1994 Sega

    TODO:

    - Fix input press state, e.g. World Travel test program tablet check not passed, unresponsive menu lists
    - LCD effects, in particular when to clear/show dots, e.g. Kitten test program credits are jumbled
    - Most tablet commands aren't parsed, e.g. pen calibration positions
    - Disk beeper (used in Test Program)

    Hardware
    --------

    Ferie Kitten (first model, just named "Ferie"):

    - PCB Revision: SB1P01B, 171-6896, 837-11123
    - IC1 (CPU): Toshiba T6A84
    - IC2 (Mask ROM): Sega MPR-17062-T
    - IC3 (Static RAM): Sony CXK58257AM-10L
    - IC4 (Touchpad Driver): Sega 315-5832

    Ferie Puppy:

    - PCB Revision: MB-TPD338-1.0
    - U1 (CPU): Chip-on-board
    - U2 (ROM): Chip-on-board
    - U3 (RAM): Chip-on-board
    - U4 (Touchpad Driver): Sega 315-5832
    - LCD Driver: Toshiba T6A04

    Ferie World Travel:

    - PCB Revision: SB1P02A
    - U1 (CPU): Toshiba T6A84
    - U2 (Mask ROM): Sega MPR-18080A
    - U3 (Static RAM): Sony CXK58257AM-10L
    - U4 (Touchpad Driver): Sega 315-5889

    Mask ROM pinouts are identical to MPR-18201-S: https://www.smspower.org/Development/MaskROMs

    Test Program
    ------------

    Ferie Kitten stores it in page 7 (ROM offset 0x70000). Currently unknown how to
    normally load this program. It requires writing "TEST" at RAM address 0x70.
    To force loading after reset, run "do pc = 0x1ea9" in the debugger.

*******************************************************************************/

#include "emu.h"

#include "cpu/z80/t6a84.h"
#include "machine/ram.h"
#include "machine/timer.h"

#include "crsshair.h"
#include "emupal.h"
#include "screen.h"

#include "ferie.lh"

#define LOG_MEM    (1U << 1)
#define LOG_LCD    (1U << 2)
#define LOG_TABLET (1U << 3)

//#define VERBOSE (LOG_MEM | LOG_LCD | LOG_TABLET)
#include "logmacro.h"

namespace {

class sega_ferie_state : public driver_device
{
public:
	sega_ferie_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_palette(*this, "palette")
		, m_screen(*this, "screen")
		, m_ram(*this, RAM_TAG)
		, m_io_buttons(*this, "BUTTONS")
		, m_io_pen_x(*this, "PEN_X")
		, m_io_pen_y(*this, "PEN_Y")
		, m_io_pen_y_rescale(*this, "PEN_Y_RESCALE")
	{ }

	void sega_ferie(machine_config &config);

	DECLARE_CROSSHAIR_MAPPER_MEMBER(pen_y_mapper);
	ioport_value pen_y_rescale_r();
	ioport_value pen_target_r();

private:
	static inline constexpr uint16_t LCD_W = 120;
	static inline constexpr uint16_t LCD_H = 64;

	static inline constexpr uint8_t RST1 = 0x08;
	static inline constexpr uint8_t RST3 = 0x18;

	static inline constexpr uint8_t HIGH_BATTERY = 0x40;

	// Data is sent in sequences of writes to I/O port 0xe5,
	// filling lines of dots in a configurable direction. An internal index
	// is increment after each line, and gets reset when it reaches a given threshold.
	enum lcd_fill_pattern : uint8_t
	{
		FILL_8_DOTS_Y, // Column-wise
		FILL_8_DOTS_X  // Row-wise
	};

	/*
	    Commands for parsing and configuring tablet variables. Both
	    data for requests and replies is shifted one bit at a time:

	    ROM_00::086a 4f              LD         C,data
	    ROM_00::086b 06 08           LD         i,0x8
	    ROM_00::086d cd 91 11        CALL       set_data_page_8
	    ROM_00::0870 36 00           LD         (reply),0x0
	                            LAB_ROM_00__0872
	    ROM_00::0872 00              NOP
	    ROM_00::0873 00              NOP
	    ROM_00::0874 00              NOP
	    ROM_00::0875 00              NOP
	    ROM_00::0876 79              LD         data,C
	    ROM_00::0877 e6 01           AND        0x1
	    ROM_00::0879 d3 f6           OUT        (TABLET_CTRL),data
	    ROM_00::087b 00              NOP
	    ; ...
	    ROM_00::0887 00              NOP
	    ROM_00::0888 79              LD         data,C
	    ROM_00::0889 e6 01           AND        0x1
	    ROM_00::088b f6 02           OR         0x2
	    ROM_00::088d d3 f6           OUT        (TABLET_CTRL),data
	    ROM_00::088f cb 39           SRL        C
	    ROM_00::0891 db f7           IN         data,(TABLET_DATA)
	    ROM_00::0893 07              RLCA
	    ROM_00::0894 cb 1e           RR         (reply)
	    ROM_00::0896 10 da           DJNZ       LAB_ROM_00__0872
	*/
	enum tablet_command_request : uint8_t
	{
		READ = 0,
		EFFECTIVE_PEN_X_Y = 0x0a,
		RAW_PEN_X_Y = 0x15, // Only used for callibration?
	};

	// Replies are read via one or more sequences of READ commands.
	enum tablet_command_reply_state : uint16_t
	{
		REPLY_0x06 = 0,
		EFFECTIVE_PEN_STATUS = 0x0a00,
		EFFECTIVE_PEN_X = 0x0a01,
		EFFECTIVE_PEN_Y = 0x0a02,
		RAW_PEN_STATUS = 0x1500,
		RAW_PEN_X = 0x1501,
		RAW_PEN_Y = 0x1502,
	};

	enum input_state : uint8_t
	{
		INPUT_RELEASE = 0,
		INPUT_PRESS = 0x10,
		INPUT_HOLD = 0x11,
	};

	enum pen_target : uint8_t
	{
		PEN_TARGET_LCD = 0,
		PEN_TARGET_PANEL = 1,
	};

	virtual void machine_start() override;
	virtual void machine_reset() override;

	void ferie_io_map(address_map &map);

	TIMER_DEVICE_CALLBACK_MEMBER(irq);

	void update_rtc();

	void update_crosshair(screen_device &screen);
	void palette(palette_device &palette) const;
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void lcd_ctrl_w(uint8_t data);
	void lcd_data_w(uint8_t data);
	uint8_t get_lcd_dots(uint16_t x, uint16_t y);

	uint8_t code_r(offs_t offset);
	uint8_t data_r(offs_t offset);
	void data_w(offs_t offset, uint8_t data);
	uint8_t stack_r(offs_t offset);
	void stack_w(offs_t offset, uint8_t data);
	uint8_t get_page_data(uint32_t address);

	void tablet_ctrl_w(uint8_t data);
	uint8_t tablet_data_r();

	static constexpr float rescale(float x, float min_x, float max_x, float a, float b)
	{
		// Rescaling (min-max normalization) from [min_x..max_x] to [a..b].
		return a + (((x - min_x) * (b - a)) / (max_x - min_x));
	}

	required_device<t6a84_device> m_maincpu;
	optional_device<palette_device> m_palette;
	required_device<screen_device> m_screen;
	required_device<ram_device> m_ram;

	required_ioport m_io_buttons;
	required_ioport m_io_pen_x;
	required_ioport m_io_pen_y;
	required_ioport m_io_pen_y_rescale;

	uint8_t *m_rom;

	uint8_t m_irq_ctrl;
	uint8_t m_irq_status;
	uint8_t m_irq_vector;

	uint8_t m_rtc;

	std::unique_ptr<uint8_t[]> m_lcd_dots;
	uint8_t m_lcd_x;
	uint8_t m_lcd_y;
	uint8_t m_lcd_i;
	uint8_t m_lcd_mode;

	uint8_t m_tablet_cmd;
	uint8_t m_tablet_cmd_prev;
	uint8_t m_tablet_data_i;
	uint16_t m_tablet_reply_state;
	bool m_is_tablet_cmd_bit_set;

	uint8_t m_button_state;
	uint8_t m_pen_state;
	uint8_t m_pen_target;
};

void sega_ferie_state::ferie_io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0xe4, 0xe4).lr8(NAME([]() { return 0; })).w(FUNC(sega_ferie_state::lcd_ctrl_w));
	map(0xe5, 0xe5).w(FUNC(sega_ferie_state::lcd_data_w));
	map(0xeb, 0xeb).lrw8(NAME([] () { return 0; /* TODO: On/Off button state */ }), NAME([this] (uint8_t data) { m_irq_ctrl = data; }));
	map(0xf4, 0xf4).lr8(NAME([this] () { return m_rtc; }));
	map(0xf5, 0xf5).lr8(NAME([this] () { return m_io_buttons->read() & 0x3f; }));
	map(0xf6, 0xf6).w(FUNC(sega_ferie_state::tablet_ctrl_w));
	map(0xf7, 0xf7).r(FUNC(sega_ferie_state::tablet_data_r));
}

void sega_ferie_state::machine_start()
{
	uint8_t *rom = memregion("mask_rom")->base();
	m_rom = reinterpret_cast<uint8_t *>(rom);
	uint8_t *ram = m_ram->pointer();

	m_maincpu->space(AS_PROGRAM).install_rom(0x00000, 0x7ffff, rom);
	m_maincpu->space(AS_PROGRAM).install_ram(0x80000, 0x87fff, ram);
	m_maincpu->space(AS_DATA).install_ram(0x00000, 0x7ffff, rom);
	m_maincpu->space(AS_DATA).install_ram(0x80000, 0x87fff, ram);
	m_maincpu->space(t6a84_device::AS_STACK).install_ram(0x00000, 0x7ffff, rom);
	m_maincpu->space(t6a84_device::AS_STACK).install_ram(0x80000, 0x87fff, ram);

	m_maincpu->space(AS_PROGRAM).install_read_handler(0x0000, 0xffff,
			read8sm_delegate(*this, FUNC(sega_ferie_state::code_r)));
	m_maincpu->space(AS_DATA).install_readwrite_handler(0x0000, 0xffff,
			read8sm_delegate(*this, FUNC(sega_ferie_state::data_r)),
			write8sm_delegate(*this, FUNC(sega_ferie_state::data_w)));
	m_maincpu->space(t6a84_device::AS_STACK).install_readwrite_handler(0x0000, 0xffff,
			read8sm_delegate(*this, FUNC(sega_ferie_state::stack_r)),
			write8sm_delegate(*this, FUNC(sega_ferie_state::stack_w)));

	save_item(NAME(m_irq_ctrl));
	save_item(NAME(m_irq_status));
	save_item(NAME(m_irq_vector));

	save_item(NAME(m_rtc));

	m_lcd_dots = make_unique_clear<uint8_t[]>(LCD_W * LCD_H);
	save_pointer(NAME(m_lcd_dots), LCD_W * LCD_H);
	save_item(NAME(m_lcd_mode));
	save_item(NAME(m_lcd_x));
	save_item(NAME(m_lcd_y));
	save_item(NAME(m_lcd_i));

	save_item(NAME(m_tablet_cmd));
	save_item(NAME(m_tablet_cmd_prev));
	save_item(NAME(m_tablet_data_i));
	save_item(NAME(m_tablet_reply_state));
	save_item(NAME(m_is_tablet_cmd_bit_set));

	save_item(NAME(m_button_state));
	save_item(NAME(m_pen_state));
	save_item(NAME(m_pen_target));
}

void sega_ferie_state::machine_reset()
{
	m_irq_ctrl = 0;
	m_irq_status = 0;
	m_irq_vector = RST3;

	m_rtc = 0;

	memset(m_lcd_dots.get(), 0, LCD_W * LCD_H);
	m_lcd_mode = FILL_8_DOTS_Y;
	m_lcd_x = 0;
	m_lcd_y = 0;
	m_lcd_i = 0;

	m_tablet_cmd = READ;
	m_tablet_cmd_prev = READ;
	m_tablet_data_i = 0;
	m_tablet_reply_state = REPLY_0x06;
	m_is_tablet_cmd_bit_set = false;

	m_button_state = INPUT_RELEASE;
	m_pen_state = INPUT_RELEASE;
	m_pen_target = PEN_TARGET_LCD;
}

CROSSHAIR_MAPPER_MEMBER(sega_ferie_state::pen_y_mapper)
{
	// Parameter `linear_value` is ignored, since we will read the input port directly
	// for adjustments, just need to return that value in the expected range [0.0f..1.0f].
	return (float) pen_y_rescale_r() / 0xff;
}

ioport_value sega_ferie_state::pen_y_rescale_r()
{
	/*
	    There are two distinct areas that can be interacted with the pen:

	    - LCD screen visible area: 8 tiles = 64 pixels;
	    - Bottom panel: 3 tiles = 24 pixels;

	    In order to transparently map coordinates between each area, we split
	    the value across these areas, but rescaled to the input port's full range.
	*/
	const int16_t io_pen_y_min = m_io_pen_y->field(0xff)->minval();
	const int16_t io_pen_y_max = m_io_pen_y->field(0xff)->maxval();
	const int16_t screen_y_max = io_pen_y_max * 0.75f;
	int16_t adjusted_value = m_io_pen_y->read();
	if (adjusted_value > screen_y_max) {
		adjusted_value = rescale(adjusted_value, screen_y_max, io_pen_y_max, io_pen_y_min, io_pen_y_max);
		m_pen_target = PEN_TARGET_PANEL;
	} else {
		adjusted_value = rescale(adjusted_value, io_pen_y_min, screen_y_max, io_pen_y_min, io_pen_y_max);
		m_pen_target = PEN_TARGET_LCD;
	}

	return adjusted_value;
}

ioport_value sega_ferie_state::pen_target_r()
{
	return m_pen_target;
}

TIMER_DEVICE_CALLBACK_MEMBER(sega_ferie_state::irq)
{
	bool is_button_pressed = (m_io_buttons->read() & 0x7f) != 0;
	if (is_button_pressed && m_button_state == INPUT_RELEASE) {
		m_button_state = INPUT_PRESS;
	}
	if (m_button_state == INPUT_PRESS) {
		m_irq_vector = (m_irq_ctrl == 0xf7) ? RST1 : RST3;
		m_button_state = INPUT_HOLD;
	}
	if (!is_button_pressed && m_button_state == INPUT_HOLD) {
		m_irq_vector = RST3;
		m_button_state = INPUT_RELEASE;
	}

	m_maincpu->set_input_line_vector(0, 0xc7 | m_irq_vector);
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, m_irq_status ? ASSERT_LINE : CLEAR_LINE);
	m_irq_status ^= 1;
}

uint8_t sega_ferie_state::code_r(offs_t offset)
{
	return get_page_data(m_maincpu->code_address(offset));
}

uint8_t sega_ferie_state::data_r(offs_t offset)
{
	return get_page_data(m_maincpu->data_address(offset));
}

void sega_ferie_state::data_w(offs_t offset, uint8_t data)
{
	LOGMASKED(LOG_MEM, "DATA w [%06x] = %02x\n", offset, data);
	if (offset > 0x7fff) {
		osd_printf_warning("DATA w OOB @ %06x\n", m_maincpu->pc());
	} else {
		m_ram->pointer()[offset] = data;
	}
}

uint8_t sega_ferie_state::stack_r(offs_t offset)
{
	return get_page_data(m_maincpu->stack_address(offset));
}

void sega_ferie_state::stack_w(offs_t offset, uint8_t data)
{
	LOGMASKED(LOG_MEM, "STACK w [%06x] = %02x\n", offset, data);
	if (offset > 0x7fff) {
		osd_printf_warning("STACK w OOB @ %06x\n", m_maincpu->pc());
	} else {
		m_ram->pointer()[offset] = data;
	}
}

uint8_t sega_ferie_state::get_page_data(uint32_t address)
{
	uint8_t data = 0;
	if (((int32_t) address) - 0x80000 > 0x7fff) {
		osd_printf_warning("get_page_data OOB @ %06x\n", m_maincpu->pc());
	} else {
		data = (address < 0x80000) ? m_rom[address] : m_ram->pointer()[address - 0x80000];
	}
	LOGMASKED(LOG_MEM, "get_page_data [%06x] = %02x\n", address, data);

	return data;
}

uint8_t sega_ferie_state::tablet_data_r()
{
	if (machine().side_effects_disabled() || !m_is_tablet_cmd_bit_set) {
		return HIGH_BATTERY;
	}

	if (m_tablet_data_i == 0) {
		if (m_tablet_reply_state == REPLY_0x06) {
			switch (m_tablet_cmd_prev) {
				case EFFECTIVE_PEN_X_Y:
					m_tablet_reply_state = EFFECTIVE_PEN_STATUS;
					LOGMASKED(LOG_TABLET, "tablet_data_r EFFECTIVE_PEN_X_Y -> EFFECTIVE_PEN_STATUS\n");
					break;
				case RAW_PEN_X_Y:
					m_tablet_reply_state = RAW_PEN_STATUS;
					LOGMASKED(LOG_TABLET, "tablet_data_r RAW_PEN_X_Y -> RAW_PEN_STATUS\n");
					break;
			}
		}

		if (BIT(m_io_buttons->read(), 6)) {
			if (m_pen_state == INPUT_RELEASE) {
				m_pen_state = INPUT_PRESS;
				LOGMASKED(LOG_TABLET, "tablet_data_r INPUT_RELEASE -> INPUT_PRESS\n");
			} else if (m_pen_state == INPUT_PRESS) {
				m_pen_state = INPUT_HOLD;
				LOGMASKED(LOG_TABLET, "tablet_data_r INPUT_PRESS -> INPUT_HOLD\n");
			}
		} else if (m_pen_state != INPUT_RELEASE) {
			m_pen_state = INPUT_RELEASE;
			LOGMASKED(LOG_TABLET, "tablet_data_r INPUT_RELEASE\n");
		}
	}

	int16_t io_pen_x_min = m_io_pen_x->field(0xff)->minval();
	int16_t io_pen_x_max = m_io_pen_x->field(0xff)->maxval();
	int16_t io_pen_y_min = m_io_pen_y->field(0xff)->minval();
	int16_t io_pen_y_max = m_io_pen_y->field(0xff)->maxval();
	int16_t io_pen_x_pos = m_io_pen_x->read();
	int16_t io_pen_y_pos = pen_y_rescale_r();
	uint8_t reply;
	switch (m_tablet_reply_state) {
		case EFFECTIVE_PEN_STATUS:
			// Fallthrough
		case RAW_PEN_STATUS:
			reply = m_pen_state;
			break;
		case EFFECTIVE_PEN_X:
			reply = rescale(io_pen_x_pos, io_pen_x_min, io_pen_x_max, 0x00, 0x80);
			break;
		case EFFECTIVE_PEN_Y:
			reply = (m_pen_target == PEN_TARGET_LCD)
					? rescale(io_pen_y_pos, io_pen_y_min, io_pen_y_max, 0x00, 0x40)
					: rescale(io_pen_y_pos, io_pen_y_min, io_pen_y_max, 0x40, 0x40 + 3 * 8);
			break;
		case RAW_PEN_X:
			reply = io_pen_x_pos;
			break;
		case RAW_PEN_Y:
			reply = io_pen_y_pos;
			break;
		default:
			reply = 0x06;
	}

	uint8_t data = BIT(reply, m_tablet_data_i) << 7;

	LOGMASKED(LOG_TABLET, "tablet_data_r i = %02x\n", m_tablet_data_i);
	m_tablet_data_i++;
	if (m_tablet_data_i == 8) {
		LOGMASKED(LOG_TABLET, "tablet_data_r cmd = %02x, rpy = %02x @ %06x\n", m_tablet_cmd, reply, m_maincpu->pc());
		m_tablet_data_i = 0;
		if (m_tablet_cmd != READ) {
			m_tablet_cmd_prev = m_tablet_cmd;
			m_tablet_reply_state = REPLY_0x06;
		}
		m_tablet_cmd = READ;
		switch (m_tablet_reply_state) {
			case EFFECTIVE_PEN_STATUS:
				if (m_pen_state == INPUT_HOLD) {
					m_tablet_reply_state = EFFECTIVE_PEN_X;
					LOGMASKED(LOG_TABLET, "tablet_data_r EFFECTIVE_PEN_STATUS -> EFFECTIVE_PEN_X\n");
				}
				break;
			case EFFECTIVE_PEN_X:
				m_tablet_reply_state = EFFECTIVE_PEN_Y;
				LOGMASKED(LOG_TABLET, "tablet_data_r EFFECTIVE_PEN_X -> EFFECTIVE_PEN_Y\n");
				break;
			case RAW_PEN_STATUS:
				if (m_pen_state == INPUT_HOLD) {
					m_tablet_reply_state = RAW_PEN_X;
					LOGMASKED(LOG_TABLET, "tablet_data_r RAW_PEN_STATUS -> RAW_PEN_X\n");
				}
				break;
			case RAW_PEN_X:
				m_tablet_reply_state = RAW_PEN_Y;
				LOGMASKED(LOG_TABLET, "tablet_data_r RAW_PEN_X -> RAW_PEN_Y\n");
				break;
			default:
				m_tablet_reply_state = REPLY_0x06;
		}
	}

	m_is_tablet_cmd_bit_set = false;

	return data | HIGH_BATTERY;
}

void sega_ferie_state::tablet_ctrl_w(uint8_t data)
{
	if ((data & 2) != 0) {
		LOGMASKED(LOG_TABLET, "tablet_ctrl_w i = %02x\n", m_tablet_data_i);
		m_tablet_cmd |= ((data & 1) << m_tablet_data_i);
		m_is_tablet_cmd_bit_set = true;
	}
}

void sega_ferie_state::update_rtc()
{
	system_time systime;
	machine().current_datetime(systime);
	m_rtc = systime.time;
}

void sega_ferie_state::update_crosshair(screen_device &screen)
{
	// Either screen crosshair or layout view's cursor should be visible at a time.
	machine().crosshair().get_crosshair(0).set_screen(m_pen_target ? CROSSHAIR_SCREEN_NONE : &screen);
}

void sega_ferie_state::palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(188, 190, 144));  // Background
	palette.set_pen_color(1, rgb_t(176, 180, 134));  // LCD pixel off
	palette.set_pen_color(2, rgb_t( 38,  60,  80));  // LCD pixel on
}

uint32_t sega_ferie_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	update_rtc();
	update_crosshair(screen);

	// Render LCD
	for (size_t y = cliprect.min_y; y <= cliprect.max_y; y++) {
		for (size_t x = cliprect.min_x; x <= cliprect.max_x / 8; x++) {
			uint8_t data = get_lcd_dots(x, y);
			for (size_t bit_i = 0; bit_i < 8; bit_i++) {
				// TODO: Adjust based on configured contrast
				bitmap.pix(y, (8 * x) + bit_i) = BIT(data, 7 - bit_i) + 1;
			}
		}
	}

	return 0;
}

uint8_t sega_ferie_state::get_lcd_dots(uint16_t x, uint16_t y)
{
	return m_lcd_dots[y * LCD_W + x];
}

void sega_ferie_state::lcd_ctrl_w(uint8_t data)
{
	LOGMASKED(LOG_LCD, "lcd_ctrl_w @ %06x = %02x\n", m_maincpu->pc(), data);
	if ((data & 0x20) != 0) {
		m_lcd_x = data & 0x1f;
	} else if ((data & 0x80) != 0) {
		m_lcd_y = data & 0x7f;
		m_lcd_i = 0;
	} else if (data == 0x7) {
		LOGMASKED(LOG_LCD, "lcd_ctrl_w FILL_8_DOTS_X\n");
		m_lcd_mode = FILL_8_DOTS_X;
	} else if (data == 0x3) {
		LOGMASKED(LOG_LCD, "lcd_ctrl_w FILL_8_DOTS_Y\n");
		m_lcd_mode = FILL_8_DOTS_Y;
	}
}

void sega_ferie_state::lcd_data_w(uint8_t data)
{
	const uint16_t offset = (m_lcd_mode == FILL_8_DOTS_Y)
			? (m_lcd_y + m_lcd_i) * LCD_W + m_lcd_x
			: m_lcd_y * LCD_W + (m_lcd_x + m_lcd_i);

	LOGMASKED(LOG_LCD, "lcd_data_w[y=%02x,x=%02x,i=%02x => %08x] = %02x\n", m_lcd_y, m_lcd_x, m_lcd_i, offset, data);
	if (offset > LCD_W * LCD_H - 1) {
		osd_printf_warning("lcd_data_w OOB @ %06x\n", m_maincpu->pc());
	} else {
		m_lcd_dots[offset] = data;
	}

	m_lcd_i += 1;
	if (m_lcd_mode == FILL_8_DOTS_Y) {
		if (m_lcd_i == 0x8) {
			m_lcd_i = 0;
		}
	} else if (m_lcd_mode == FILL_8_DOTS_X) {
		if (m_lcd_i == 0xf) {
			m_lcd_i = 0;
			m_lcd_y += 1;
		}
	}
}

static INPUT_PORTS_START( sega_ferie )
	PORT_START("BUTTONS")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_RIGHT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_LEFT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("Return")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("On/Off")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("Pen Down")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(sega_ferie_state::pen_target_r))

	PORT_START("PEN_X")
	PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(30) PORT_KEYDELTA(10) PORT_MINMAX(0, 255) PORT_PLAYER(1) PORT_NAME("Pen X")

	PORT_START("PEN_Y")
	PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_SENSITIVITY(30) PORT_KEYDELTA(10) PORT_MINMAX(0, 255) PORT_PLAYER(1) PORT_NAME("Pen Y") PORT_CROSSHAIR_MAPPER_MEMBER(FUNC(sega_ferie_state::pen_y_mapper))

	PORT_START("PEN_Y_RESCALE")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(sega_ferie_state::pen_y_rescale_r))
INPUT_PORTS_END

void sega_ferie_state::sega_ferie(machine_config &config)
{
	T6A84(config, m_maincpu, XTAL(4'000'000)); // High frequency external crystal, connected to HXIN/HXOUT pins
	m_maincpu->set_addrmap(AS_IO, &sega_ferie_state::ferie_io_map);

	RAM(config, RAM_TAG).set_default_size("32K").set_default_value(0x00);

	// FIXME: Guessed timings
	TIMER(config, "irq").configure_periodic(FUNC(sega_ferie_state::irq), attotime::from_hz(200));

	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(0));
	m_screen->set_size(LCD_W, LCD_H);
	m_screen->set_visarea(0, LCD_W - 1, 0, LCD_H - 1);
	m_screen->set_screen_update(FUNC(sega_ferie_state::screen_update));
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(sega_ferie_state::palette), 3);

	config.set_default_layout(layout_ferie);
}

ROM_START( ferieki )
	ROM_REGION16_LE( 0x80000, "mask_rom", 0 )
	ROM_LOAD( "mpr-17062-t.ic2", 0x00000, 0x80000, CRC(c693c9f6) SHA1(491e23902f5ef0dea9156f244f5aa2a21ab68505) )
ROM_END

ROM_START( feriepu )
	ROM_REGION16_LE( 0x80000, "mask_rom", 0 )
	ROM_LOAD( "rom.u2", 0x00000, 0x80000, CRC(895bfe47) SHA1(28ca98471a4a4b5084884b39eccbc74bff1cf4c6) )
ROM_END

ROM_START( feriewt )
	ROM_REGION16_LE( 0x80000, "mask_rom", 0 )
	ROM_LOAD( "mpr-18080a.u2", 0x00000, 0x80000, CRC(117aea09) SHA1(73ff933ba2bacd485cbc0580b023341fffac692f) )
ROM_END

} // anonymous namespace


//    year, name,     parent,  compat, machine,    input,      class,            init,       company, fullname,             flags
CONS( 1994, ferieki,  0,       0,      sega_ferie, sega_ferie, sega_ferie_state, empty_init, "Sega",  "Ferie Kitten",       MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
CONS( 1995, feriepu,  0,       0,      sega_ferie, sega_ferie, sega_ferie_state, empty_init, "Sega",  "Ferie Puppy",        MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
CONS( 1995, feriewt,  0,       0,      sega_ferie, sega_ferie, sega_ferie_state, empty_init, "Sega",  "Ferie World Travel", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



sega_sawatte.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR

/* Sega Sawatte / S-Pico

a sound-only Pico type system (one of the boards even says S-PICO)

CPU is unknown (MCU with internal ROM?) cartridge dumps contain 6502 code
and have been tested as working using a flash cart.


images supplied by Team Europe

http://mamedev.emulab.it/haze/reference/sawatte/cartridge_pcb_front.jpg
http://mamedev.emulab.it/haze/reference/sawatte/cartridge_pcb_back.jpg

http://mamedev.emulab.it/haze/reference/sawatte/PCB_Front.jpg
http://mamedev.emulab.it/haze/reference/sawatte/PCB_Back.jpg

http://mamedev.emulab.it/haze/reference/sawatte/Console_Front.JPG
http://mamedev.emulab.it/haze/reference/sawatte/Console_Back.JPG

http://mamedev.emulab.it/haze/reference/sawatte/cartridge_example.jpg

*/

#include "emu.h"
#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/m6502/m6502.h"
#include "softlist_dev.h"


namespace {

class sawatte_state : public driver_device
{
public:
	sawatte_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cartslot(*this, "cartslot")
		, m_soundram(*this, "soundram")
		, m_irq3_timer(nullptr)
		, m_irq4_timer(nullptr)
		, m_data_bank(0)
		, m_prog_bank(0)
		, m_irq_status(0)
		, m_irq_mask(0)
	{ }

	void sawatte(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	TIMER_CALLBACK_MEMBER(irq3_timer);
	TIMER_CALLBACK_MEMBER(irq4_timer);

	void bank08_w(u8 data);
	void bank09_w(u8 data);
	void bank0a_w(u8 data);
	u8 irq_status_r();
	void irq_mask_w(u8 data);
	void irq3_timer_w(u8 data);
	void irq4_timer_w(u8 data);
	u8 fixed_r(offs_t offset);
	u8 data_bank_r(offs_t offset);
	u8 prog_bank_r(offs_t offset);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<generic_slot_device> m_cartslot;
	required_shared_ptr<u8> m_soundram;

	emu_timer *m_irq3_timer;
	emu_timer *m_irq4_timer;

	u16 m_data_bank;
	u8 m_prog_bank;
	u8 m_irq_status;
	u8 m_irq_mask;
};

void sawatte_state::machine_start()
{
	m_irq3_timer = timer_alloc(FUNC(sawatte_state::irq3_timer), this);
	m_irq4_timer = timer_alloc(FUNC(sawatte_state::irq4_timer), this);

	save_item(NAME(m_data_bank));
	save_item(NAME(m_prog_bank));
	save_item(NAME(m_irq_status));
	save_item(NAME(m_irq_mask));
}

void sawatte_state::machine_reset()
{
	m_data_bank = 0;
	m_prog_bank = 0;

	m_irq_status = 0;
	m_irq_mask = 0;
	m_maincpu->set_input_line(m6502_device::IRQ_LINE, CLEAR_LINE);

	m_irq3_timer->adjust(attotime::never);
	m_irq4_timer->adjust(attotime::never);
}

TIMER_CALLBACK_MEMBER(sawatte_state::irq3_timer)
{
	if (BIT(m_irq_mask, 3))
	{
		m_irq_status |= 0x08;
		m_maincpu->set_input_line(m6502_device::IRQ_LINE, ASSERT_LINE);
	}
}

TIMER_CALLBACK_MEMBER(sawatte_state::irq4_timer)
{
	if (BIT(m_irq_mask, 4))
	{
		m_irq_status |= 0x10;
		m_maincpu->set_input_line(m6502_device::IRQ_LINE, ASSERT_LINE);
	}
}


void sawatte_state::bank08_w(u8 data)
{
	m_data_bank = (m_data_bank & 0xff00) | data;
}

void sawatte_state::bank09_w(u8 data)
{
	m_data_bank = u16(data) << 8 | (m_data_bank & 0x00ff);
}

void sawatte_state::bank0a_w(u8 data)
{
	m_prog_bank = data;
}

u8 sawatte_state::irq_status_r()
{
	return m_irq_status;
}

void sawatte_state::irq_mask_w(u8 data)
{
	m_irq_mask = data;
	if (m_irq_status != 0)
	{
		m_irq_status &= data;
		if (m_irq_status == 0)
			m_maincpu->set_input_line(m6502_device::IRQ_LINE, CLEAR_LINE);
	}
}

void sawatte_state::irq3_timer_w(u8 data)
{
	if (data == 0)
		m_irq3_timer->adjust(attotime::never);
	else
	{
		attotime period = attotime::from_hz(10'000); // probably incorrect
		m_irq3_timer->adjust(period, 0, period);
	}
}

void sawatte_state::irq4_timer_w(u8 data)
{
	if (!BIT(data, 0))
		m_irq4_timer->adjust(attotime::never);
	else
	{
		attotime period = attotime::from_hz(8'000); // probably incorrect
		m_irq4_timer->adjust(period, 0, period);
	}
}

u8 sawatte_state::fixed_r(offs_t offset)
{
	return m_cartslot->read_rom(offset + 0x200);
}

u8 sawatte_state::data_bank_r(offs_t offset)
{
	return m_cartslot->read_rom(offset | offs_t(m_data_bank) << 11);
}

u8 sawatte_state::prog_bank_r(offs_t offset)
{
	return m_cartslot->read_rom(offset | offs_t(m_prog_bank) << 12);
}


void sawatte_state::mem_map(address_map &map)
{
	map.global_mask(0x1fff);
	map(0x0000, 0x0000).nopr();
	map(0x0008, 0x0008).w(FUNC(sawatte_state::bank08_w));
	map(0x0009, 0x0009).w(FUNC(sawatte_state::bank09_w));
	map(0x000a, 0x000a).w(FUNC(sawatte_state::bank0a_w));
	map(0x0011, 0x0011).portr("IN1");
	map(0x0012, 0x0012).portr("IN2");
	map(0x0016, 0x0016).nopr();
	map(0x0017, 0x0017).r(FUNC(sawatte_state::irq_status_r));
	map(0x0020, 0x0020).nopw(); // matrix scanning?
	map(0x0022, 0x0022).nopw(); // matrix scanning?
	map(0x0026, 0x0026).w(FUNC(sawatte_state::irq3_timer_w));
	map(0x0027, 0x0027).w(FUNC(sawatte_state::irq_mask_w));
	map(0x002f, 0x002f).w(FUNC(sawatte_state::irq4_timer_w));
	map(0x0060, 0x007f).ram();
	map(0x0080, 0x009f).ram().share("soundram");
	map(0x00a0, 0x01ff).ram(); // might not all exist
	map(0x0200, 0x07ff).r(FUNC(sawatte_state::fixed_r));
	map(0x0800, 0x0fff).r(FUNC(sawatte_state::data_bank_r));
	map(0x1000, 0x1fff).r(FUNC(sawatte_state::prog_bank_r));
}


static INPUT_PORTS_START( sawatte )
	PORT_START("IN1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_BUTTON4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_BUTTON5)

	PORT_START("IN2")
	PORT_BIT(0x1f, IP_ACTIVE_HIGH, IPT_UNKNOWN) // matrix inputs?
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_BUTTON6)
INPUT_PORTS_END


void sawatte_state::sawatte(machine_config &config)
{
	M6502(config, m_maincpu, 4'000'000); // could be some stock SoC, type and clock unknown
	m_maincpu->set_addrmap(AS_PROGRAM, &sawatte_state::mem_map);

	GENERIC_CARTSLOT(config, m_cartslot, generic_plain_slot, "sawatte_cart");
	SOFTWARE_LIST(config, "cart_list").set_original("sawatte");
}

ROM_START( sawatte )
ROM_END

} // anonymous namespace


CONS( 1996?, sawatte, 0, 0, sawatte,  sawatte, sawatte_state, empty_init, "Sega", "Sawatte", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



segaai.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol, Fabio Priuli
// thanks-to:Chris Covell
/*

 Sega AI driver


 Not much is known at this stage, except that the system was intended to be
 used for educational purposes in schools. Yet the audio chips seem much more
 powerful than what an educational computer requires...

 CPU : 16bit V20 @ 5MHz
 ROM : 128KB OS.with SEGA PROLOG
 RAM : 128KB
 VRAM : 64KB
 Video : V9938 Resolution 256x212
 Sound : SN76489
 Cassette Drive : 9600bps
 TV Output : RGB, Video, RF
 Keyboard : new JIS arrangement (Japanese input mapping)


TODO:
- Cassette, playback is controlled by the computer. Games with cassette
  spin up the cassette for about 2 seconds
  - The tape sometimes seeks back and forth for 5-15 seconds for the right
    voice clip, while the game and player (kids) wait
  "In these tapes, by the way, the left audio channel is the narration,
  and the right channel the data bursts right before each one."
- Keyboard?
- SEGA Prolog? How to enter?

- Sound box:
  Note from ccovell:
  "With the Sound Box attached, I connected the output line of the Sound
  Box's keyboard pins to one or more input pins, and it suddenly played an
  FM instrument and printed "piano" on the screen! From this, pressing U/D
  on the pad cycled through the various instruments, and the PL/PR buttons
  lowered and raised the volume."

===========================================================================

 Sega AI Computer quick PCB overview by Chris Covell

 Major ICs

 IC 1    D701080-5     (86/09?)  NEC V20 CPU       DIP40
 IC 2    315-5200      (86/25)   SEGA          QFP100
 IC 3    27C512-25     (86/15)   64K EPROM "E000  8/24"
 IC 4    27C512-25     (86/06)   64K EPROM "F000  7/21"
 IC 5    MPR-7689      (86/22)   SEGA "264 AA E79" (ROM) DIP28
 IC 10   V9938                   Yamaha MSX2 VDP
 IC 13   D7759C        (86/12)   NEC Speech Synthesizer   DIP40
 IC 14   MPR-7619      (86/23)   SEGA (ROM)      DIP28
 IC 15   MPR-7620      (86/23)   SEGA (ROM)      DIP28
 IC 16   SN76489AN               TI PSG         DIP16
 IC 17   D8251AFC      (86/09)   NEC Communications Interface DIP28
 IC 18   315-5201      (86/25)   SEGA (bodge wire on pins 9,10) DIP20
 IC 19   M5204A        (87?/01)  OKI
 IC 20   D8255AC-2     (86/08)   NEC Peripheral Interface DIP40

 IC 6,7,8,9,11,12   D41464C-12   NEC 32K DRAMs - 128K RAM, 64K VRAM

 Crystals, etc

 X1   20.000           "KDS 6D"
 X2   21.47727         "KDS"
 X3   640kHz           "CSB 640 P"

 Connectors

 CN1   6-pin DIN Power connector
 CN2   8-pin DIN "AUX" connector
 CN3   Video phono jack
 CN4   Audio phono jack
 CN5   35-pin Sega MyCard connector
 CN6   60-pin expansion connector A1..A30 Bottom, B1..B30 Top
 CN7   9-pin header connector to "Joy, Button, LED" unit
 CN8   13(?) pin flat flex connector to pressure pad
 CN9   9-pin header connector to tape drive motor, etc.
 CN10   13-pin header connector to tape heads
 JP2   2-wire header to SW2 button board
 PJ1   7-wire header to Keyboard / Mic connector board
 MIC   2-wire header to mic on KB/Mic board
 SW1   Reset Switch

 Power switch is on the AC Adaptor

 Joypad unit (by Mitsumi) has U/D/L/R, "PL" and "PR" buttons, and a power LED.

Power Connector Pinout (Seen from AC Adaptor plug):
   1     5        1  12V COM    5   5V COM
      6           2  12V OUT    6   5V OUT
   2     4        3   5V COM
      3           4   5V OUT

AUX Connector Pinout:
   7   6          1 +5V(?)      5 csync
  3  8  1         2 GND         6 green
   5   4          3 blue        7 Audio out
     2            4 +5V(?)      8 red

New JIS Keyboard Connector Pinout:
    1 2           1,2,3 data lines
  3 4   5         4 ??          5,8 data lines
   6 7 8          6 GND         7 +5V


*/

#include "emu.h"

#include "bus/segaai/segaai_exp.h"
#include "bus/segaai/segaai_slot.h"
#include "cpu/nec/nec.h"
#include "imagedev/cassette.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "sound/sn76496.h"
#include "sound/upd7759.h"
#include "video/v9938.h"

#include "crsshair.h"
#include "softlist.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "segaai.lh"

//#define VERBOSE (LOG_GENERAL)
#include "logmacro.h"


namespace {

class segaai_state : public driver_device
{
public:
	segaai_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_sound(*this, "sn76489a")
		, m_v9938(*this, "v9938")
		, m_upd7759(*this, "upd7759")
		, m_i8251(*this, "i8251")
		, m_i8255(*this, "i8255")
		, m_cassette(*this, "cassette")
		, m_cardslot(*this, "cardslot")
		, m_expslot(*this, "exp")
		, m_port4(*this, "PORT4")
		, m_port5(*this, "PORT5")
		, m_touch(*this, "TOUCH")
		, m_touchpadx(*this, "TOUCHPADX")
		, m_touchpady(*this, "TOUCHPADY")
		, m_vector(0)
	{ }

	void segaai(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	static constexpr u8 VECTOR_V9938 = 0xf8;
	static constexpr u8 VECTOR_I8251_SEND = 0xf9;
	static constexpr u8 VECTOR_I8251_RECEIVE = 0xfa;
	static constexpr u8 VECTOR_UPD7759 = 0xfb;

	static constexpr u8 IRQ_V9938 = 0x01;
	static constexpr u8 IRQ_UPD7759 = 0x08;

	// 8255 Port B bits
	static constexpr u8 TOUCH_PAD_PRESSED = 0x02;
	static constexpr u8 TOUCH_PAD_DATA_AVAILABLE = 0x04;

	static constexpr u8 UPD7759_MODE = 0x01;
	static constexpr u8 UPD7759_BANK = 0x02;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void update_irq_state();
	u32 get_vector() { return m_vector; }
	void vdp_interrupt(int state);
	void upd7759_drq_w(int state);
	IRQ_CALLBACK_MEMBER(irq_callback);
	u8 i8255_portb_r();
	u8 i8255_portc_r();
	void i8255_portc_w(u8 data);
	void upd7759_ctrl_w(u8 data);
	void upd7759_data_w(u8 data);
	void port1c_w(u8 data);
	void port1d_w(u8 data);
	void port1e_w(u8 data);
	u8 port1e_r();
	u8 irq_enable_r();
	void irq_enable_w(u8 data);
	void irq_select_w(u8 data);

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<sn76489a_device> m_sound;
	required_device<v9938_device> m_v9938;
	required_device<upd7759_device> m_upd7759;
	required_device<i8251_device> m_i8251;
	required_device<i8255_device> m_i8255;
	required_device<cassette_image_device> m_cassette;
	required_device<segaai_card_slot_device> m_cardslot;
	required_device<segaai_exp_slot_device> m_expslot;
	required_ioport m_port4;
	required_ioport m_port5;
	required_ioport m_touch;
	required_ioport m_touchpadx;
	required_ioport m_touchpady;

	u8 m_i8255_portb;
	u8 m_upd7759_ctrl;
	u8 m_upd7759_bank_ff;
	u8 m_port_1c;
	u8 m_port_1d;
	u8 m_port_1e;
	u32 m_prev_v9938_irq;
	u32 m_prev_upd7759_irq;
	u8 m_touchpad_x;
	u8 m_touchpad_y;
	u8 m_irq_active;
	u8 m_irq_enabled;
	u32 m_vector;
};


void segaai_state::mem_map(address_map &map)
{
	map(0x00000, 0x1ffff).ram();
	// 0x20000-0x3ffff - Dynamically mapped by expansion slot
	// 0x80000-0x8ffff - Dynamically mapped by expansion slot
	// 0xa0000-0xbffff - Dynamically mapped by cardslot
	map(0xc0000, 0xdffff).rom();
	map(0xe0000, 0xeffff).rom();
	map(0xf0000, 0xfffff).rom();
}


/*
Interesting combination of I/O actions from the BIOS:

EC267: B0 03                mov     al,3h
EC269: E6 17                out     17h,al
EC26B: B0 FC                mov     al,0FCh     ; 11111100
EC26D: E6 0F                out     0Fh,al
EC26F: B0 FF                mov     al,0FFh
EC271: E6 08                out     8h,al

same code at ECDBE, ED2FC
EC2D6: B0 05                mov     al,5h
EC2D8: E6 17                out     17h,al
EC2DA: B0 FA                mov     al,0FAh     ; 11111010
EC2DC: E6 0F                out     0Fh,al
EC2DE: B0 00                mov     al,0h
EC2E0: E4 08                in      al,8h

same code at ECE08, ECE1D, ED282, EDBA8, EDD78
EC319: B0 04                mov     al,4h
EC31B: E6 17                out     17h,al
EC31D: B0 FE                mov     al,0FEh     ; 11111110
EC31F: E6 0F                out     0Fh,al

ECB45: 80 FA 03             cmp     dl,3h
ECB48: 74 05                be      0ECB4Fh
ECB4A: B0 09                mov     al,9h
ECB4C: E9 02 00             br      0ECB51h
ECB4F: B0 08                mov     al,8h
ECB51: E6 17                out     17h,al

same code at ED02A, ED17E, ED1DC
ECEE5: B0 03                mov     al,3h
ECEE7: E6 17                out     17h,al
ECEE9: B0 FC                mov     al,0FCh     ; 11111100
ECEEB: E6 0F                out     0Fh,al
ECEED: B0 00                mov     al,0h
ECEEF: E6 08                out     8h,al

same code at ED0D9, ED120, EDB04, EDC8F
ECF0D: B0 02                mov     al,2h
ECF0F: E6 17                out     17h,al
ECF11: B0 FE                mov     al,0FEh     ; 11111110
ECF13: E6 0F                out     0Fh,al

*/

void segaai_state::io_map(address_map &map)
{
	map(0x00, 0x03).rw(m_v9938, FUNC(v9938_device::read), FUNC(v9938_device::write));
	map(0x04, 0x07).rw(m_i8255, FUNC(i8255_device::read), FUNC(i8255_device::write));
	// port 7, bit 7, engages tape?

	map(0x08, 0x08).rw(m_i8251, FUNC(i8251_device::data_r), FUNC(i8251_device::data_w));
	map(0x09, 0x09).rw(m_i8251, FUNC(i8251_device::status_r), FUNC(i8251_device::control_w));

	// 0x0a (w) - ??
	// 0a: 00 written during boot
	map(0x0b, 0x0b).w(FUNC(segaai_state::upd7759_ctrl_w));

	map(0x0c, 0x0c).w(m_sound, FUNC(sn76489a_device::write));

	// 0x0e (w) - ??
	// 0x0f (w) - ??
	// during boot:
	// 0e <- 13
	// 0f <- ff
	// 0f <- 07
	// 0e <- 07
	// 0e <- 08
	// 0f <- fe

	map(0x14, 0x14).mirror(0x01).w(FUNC(segaai_state::upd7759_data_w));

	// IRQ Enable
	map(0x16, 0x16).rw(FUNC(segaai_state::irq_enable_r), FUNC(segaai_state::irq_enable_w));
	// IRQ Enable (per IRQ source selection) Why 2 registers for IRQ enable?
	map(0x17, 0x17).w(FUNC(segaai_state::irq_select_w));

	// Touchpad
	map(0x1c, 0x1c).w(FUNC(segaai_state::port1c_w));
	map(0x1d, 0x1d).w(FUNC(segaai_state::port1d_w));
	map(0x1e, 0x1e).rw(FUNC(segaai_state::port1e_r), FUNC(segaai_state::port1e_w));

	// 0x1f (w) - ??

	// Expansion I/O
	// 0x20-0x3f - Dynamically mapped by expansion slot
}


static INPUT_PORTS_START(ai_kbd)
	PORT_START("PORT4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP) PORT_8WAY
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN) PORT_8WAY
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT) PORT_8WAY
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_8WAY
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("PL")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("RL")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("upd7759", FUNC(upd7759_device::busy_r))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED) // Microphone sensor

	PORT_START("PORT5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON3) PORT_NAME("Grey Button")
	PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("TOUCH")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON4) PORT_NAME("Press touchpad")

	PORT_START("TOUCHPADX")
	PORT_BIT(0xff, 0x80, IPT_LIGHTGUN_X) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(30) PORT_KEYDELTA(10) PORT_MINMAX(0, 255) PORT_PLAYER(1) PORT_NAME("Touchpad X")

	PORT_START("TOUCHPADY")
	PORT_BIT(0xff, 0x80, IPT_LIGHTGUN_Y) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_SENSITIVITY(30) PORT_KEYDELTA(10) PORT_MINMAX(0, 255) PORT_PLAYER(1) PORT_NAME("Touchpad Y")
INPUT_PORTS_END


void segaai_state::update_irq_state()
{
	int state = CLEAR_LINE;

	if (m_irq_active & m_irq_enabled)
	{
		state = ASSERT_LINE;
	}

	m_maincpu->set_input_line(0, state);
}


// Based on edge triggers level triggers are created
void segaai_state::vdp_interrupt(int state)
{
	if (state != CLEAR_LINE)
	{
		if (m_prev_v9938_irq == CLEAR_LINE)
		{
			m_irq_active |= IRQ_V9938;
		}
	}
	m_prev_v9938_irq = state;

	update_irq_state();
}


// Based on edge triggers level triggers are created
void segaai_state::upd7759_drq_w(int state)
{
	int upd7759_irq = state ? CLEAR_LINE : ASSERT_LINE;
	if (upd7759_irq != CLEAR_LINE)
	{
		if (m_prev_upd7759_irq == CLEAR_LINE)
		{
			m_irq_active |= IRQ_UPD7759;
		}
	}
	m_prev_upd7759_irq = upd7759_irq;

	update_irq_state();
}


IRQ_CALLBACK_MEMBER(segaai_state::irq_callback)
{
	if (m_irq_active & m_irq_enabled & IRQ_V9938)
	{
		m_vector = VECTOR_V9938;
		m_irq_active &= ~IRQ_V9938;
	}
	else if (m_irq_active & m_irq_enabled & IRQ_UPD7759)
	{
		m_vector = VECTOR_UPD7759;
		m_irq_active &= ~IRQ_UPD7759;
	}
	else
	{
		if (m_irq_active & m_irq_enabled)
		{
			fatalerror("Unknown irq triggered: $%02X active, $%02X enabled\n", m_irq_active, m_irq_enabled);
		}
		fatalerror("irq_callback called but no irq active and enabled: $%02X active, $%02X enabled\n", m_irq_active, m_irq_enabled);
	}

	update_irq_state();
	return m_vector;
}


/*
Mainboard 8255 port B

 76543210
 +-------- Tape input (right channel?), unknown if input is signal level or bit
  +------- Tape head engaged
   +------ Tape insertion sensor (0 - tape is inserted, 1 - no tape inserted)
    +----- Tape write enable sensor
     +---- keyboard connector pin 3
      +--- 0 = Touch pad data available
       +-- 0 = Touch pad pressed
        +- Trigger button near touch panel (active low)
*/
u8 segaai_state::i8255_portb_r()
{
	m_i8255_portb = (m_i8255_portb & 0xf8) | (m_port5->read() & 0x01);

	if (BIT(m_port_1d, 0))
	{
		if (!BIT(m_touch->read(), 0))
		{
			m_i8255_portb |= TOUCH_PAD_PRESSED;
		}

		m_i8255_portb |= TOUCH_PAD_DATA_AVAILABLE;
	}
	else
	{
		m_i8255_portb |= TOUCH_PAD_PRESSED;
	}

	if (m_cassette->get_image() != nullptr)
	{
		m_i8255_portb &= ~0x20;
	}
	else
	{
		m_i8255_portb |= 0x20;
	}

	// when checking whether the tape is running Popoland wants to see bit7 set and bit5 reset
	// toggling this stops eigogam2 from booting normally into a game.
	// For tape reading eigogam2 routines at A11EA and A120C
	// There is a whistle tone on the cassette before normal speech starts, the code there likely
	// checks for this whistle tone.
	//m_i8255_portb ^= 0x80;

	return m_i8255_portb;
}


/*
Mainboard 8255 port C

 76543210
 +-------- keyboard connector pin 5
  +------- keyboard connector pin 8
   +------ keyboard connector pin 2
    +----- keyboard connector pin 1
     +---- Output
      +--- Output
       +-- Output
        +- Output
*/
u8 segaai_state::i8255_portc_r()
{
	u8 data = 0xf0;

	return data;
}


/*
 pin 10-13 - PC0-PC3 -> RA41
 PC0-PC3 continue on underside of pcb
 pin 14-17 - PC4-PC7 -> RA42
 PC3 continues on underside of pcb
 PC2 - pin 6 upa2003c, drives upa2003c pin 11
 PC1 - pin 4 upa2003c, drives upa2003c pin 13
 PC0 - pin 2 upd2003c, drives upa2003c pin 15
 these go to 3 dots to the left of resistors below upa2003c?
 which end up on the 9 pin flat connector on the lower left side of the pcb
 PC4 - to pin 1 of 7 pin connector bottom right side of pcb -> jis keyboard connector ?
 CN9 - 9 pin connector goes to cassette interface
 pin 1 - red
 pin 2 - black
 pin 3 - blue
 pin 4 - yellow
 pin 5 - white
 pin 6 - black/grey
 pin 7 - blue
 pin 8 - grey
 pin 9 - brown
*/
void segaai_state::i8255_portc_w(u8 data)
{
	// Writes to bits 6,5,4, unknown what they mean
	// Bit 0 written by cosmictr
	LOG("i8255 port c write: %02x\n", data);
}


void segaai_state::upd7759_data_w(u8 data)
{
	m_upd7759->start_w(ASSERT_LINE);
	m_upd7759->port_w(data);
	m_upd7759->start_w(CLEAR_LINE);
}


void segaai_state::upd7759_ctrl_w(u8 data)
{
	LOG("I/O Port $0b write: $%02x\n", data);

	u8 prev_upd7759_ctrl = m_upd7759_ctrl;
	m_upd7759_ctrl = data;

	// bit0 is connected to /md line of the uPD7759
	m_upd7759->md_w((m_upd7759_ctrl & UPD7759_MODE) ? 0 : 1);

	if (BIT(prev_upd7759_ctrl, 0))
	{
		if (!BIT(m_upd7759_ctrl, 0))
		{
			m_upd7759_bank_ff = 0;
			m_upd7759->set_rom_bank(m_upd7759_bank_ff);
		}
	}
	else
	{
		m_upd7759_bank_ff ^= 0x01;
		m_upd7759->set_rom_bank(m_upd7759_bank_ff);
	}
}


// I/O Port 16 - IRQ Enable
u8 segaai_state::irq_enable_r()
{
	return m_irq_enabled;
}


// IRQ Enable
// 76543210
// +-------- ???
//  +------- ???
//   +------ ???
//    +----- ???
//     +---- D7759 IRQ enable
//      +--- ???
//       +-- ??? 8251 receive?
//        +- V9938 IRQ enable
void segaai_state::irq_enable_w(u8 data)
{
	m_irq_enabled = data;
	m_irq_active &= data;
	update_irq_state();
}

// I/O Port 17 - IRQ Enable selection
// This port seems to be used to set or reset specific bits in the IRQ enable register.
// Why 2 ways of setting/clearing irq enable bits?
void segaai_state::irq_select_w(u8 data)
{
	int pin = (data >> 1) & 0x07;
	if (BIT(data, 0))
	{
		m_irq_enabled |= (1 << pin);
	}
	else
	{
		m_irq_enabled &= ~(1 << pin);
		m_irq_active &= m_irq_enabled;
	}
	update_irq_state();
}


void segaai_state::port1c_w(u8 data)
{
	m_port_1c = data;
}


void segaai_state::port1d_w(u8 data)
{
	m_port_1d = data;
}


void segaai_state::port1e_w(u8 data)
{
	m_port_1e = data;
}


u8 segaai_state::port1e_r()
{
	if (BIT(m_port_1c, 0))
	{
		return m_touchpady->read();
	}
	else
	{
		return m_touchpadx->read();
	}
}


void segaai_state::machine_start()
{
	m_i8255_portb = 0x7f;
	m_upd7759_ctrl = 0;
	m_upd7759_bank_ff = 0;
	m_port_1c = 0;
	m_port_1d = 0;
	m_port_1e = 0;
	m_prev_v9938_irq = CLEAR_LINE;
	m_prev_upd7759_irq = CLEAR_LINE;
	m_touchpad_x = 0;
	m_touchpad_y = 0;
	m_vector = 0;
	m_irq_enabled = 0;
	m_irq_active = 0;

	save_item(NAME(m_i8255_portb));
	save_item(NAME(m_upd7759_ctrl));
	save_item(NAME(m_upd7759_bank_ff));
	save_item(NAME(m_port_1c));
	save_item(NAME(m_port_1d));
	save_item(NAME(m_port_1e));
	save_item(NAME(m_prev_v9938_irq));
	save_item(NAME(m_prev_upd7759_irq));
	save_item(NAME(m_touchpad_x));
	save_item(NAME(m_touchpad_y));
	save_item(NAME(m_irq_active));
	save_item(NAME(m_irq_enabled));
	save_item(NAME(m_vector));

	machine().crosshair().get_crosshair(0).set_screen(CROSSHAIR_SCREEN_NONE);
}


void segaai_state::segaai(machine_config &config)
{
	V20(config, m_maincpu, 20_MHz_XTAL/4);
	m_maincpu->set_addrmap(AS_PROGRAM, &segaai_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &segaai_state::io_map);
	m_maincpu->set_irq_acknowledge_callback(FUNC(segaai_state::irq_callback));

	V9938(config, m_v9938, 21.477272_MHz_XTAL);
	m_v9938->set_screen_ntsc(m_screen);
	m_v9938->set_vram_size(0x10000);
	m_v9938->int_cb().set(FUNC(segaai_state::vdp_interrupt));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);

	I8255(config, m_i8255);
	m_i8255->in_pa_callback().set_ioport(m_port4);
	m_i8255->in_pb_callback().set(FUNC(segaai_state::i8255_portb_r));
	m_i8255->in_pc_callback().set(FUNC(segaai_state::i8255_portc_r));
	m_i8255->out_pc_callback().set(FUNC(segaai_state::i8255_portc_w));

	I8251(config, m_i8251, 0);

	SPEAKER(config, "mono").front_center();

	SN76489A(config, m_sound, 21.477272_MHz_XTAL/6); // not verified, but sounds close to real hw recordings
	m_sound->add_route(ALL_OUTPUTS, "mono", 1.00);

	UPD7759(config, m_upd7759);
	m_upd7759->add_route(ALL_OUTPUTS, "mono", 0.70);
	m_upd7759->drq().set(FUNC(segaai_state::upd7759_drq_w));

	// Card slot
	SEGAAI_CARD_SLOT(config, m_cardslot, segaai_cards, nullptr);
	m_cardslot->set_address_space(m_maincpu, AS_PROGRAM);
	SOFTWARE_LIST(config, "software").set_original("segaai");

	// Expansion slot
	SEGAAI_EXP_SLOT(config, m_expslot, 21'477'272/6, segaai_exp, nullptr);  // not verified, assuming 3.58MHz
	m_expslot->set_mem_space(m_maincpu, AS_PROGRAM);
	m_expslot->set_io_space(m_maincpu, AS_IO);

	// Built-in cassette
	CASSETTE(config, m_cassette).set_stereo();
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->set_interface("cass");
	m_cassette->add_route(0, "mono", 0.70); // Channel 0 contains regular recorded sound
	m_cassette->set_channel(1); // Channel 1 contains data

	config.set_default_layout(layout_segaai);
}


ROM_START(segaai)
	ROM_REGION(0x100000, "maincpu", 0)
	ROM_LOAD("mpr-7689.ic5",  0xc0000, 0x20000, CRC(62402ac9) SHA1(bf52d22b119d54410dad4949b0687bb0edf3e143))
	ROM_LOAD("e000 8_24.ic3", 0xe0000, 0x10000, CRC(c8b6a539) SHA1(cbf8473d1e3d8037ea98e9ca8b9aafdc8d16ff23))   // actual label is "e000 8/24"
	ROM_LOAD("f000 7_21.ic4", 0xf0000, 0x10000, CRC(64d6cd8c) SHA1(68c130048f16d6a0abe1978e84440931470222d9))   // actual label is "f000 7/21"

	ROM_REGION(0x40000, "upd7759", 0)
	ROM_LOAD("mpr-7619.ic14", 0x00000, 0x20000, CRC(d1aea002) SHA1(c8d5408bba65b17301f19cf9ebd2b635d642525a))
	ROM_LOAD("mpr-7620.ic15", 0x20000, 0x20000, CRC(e042754b) SHA1(02aede7a3e2fda9cbca621b530afa4520cf16610))
ROM_END

} // anonymous namespace

COMP(1986, segaai,     0,         0,      segaai,   ai_kbd, segaai_state,   empty_init,    "Sega",   "AI", MACHINE_NOT_WORKING)



segapico.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Emmanuel Vadot

/****************************************** PICO emulation ****************************************/

/* todo, make this more independent of the Genesis emulation, it's really only the same CPU + VDP
   and doesn't need to be connected to the Genesis at all.

   sound is the 315-5641 / D77591, should be compatible with the 7759? but probably wants us to maintain
   an external buffer of at least 0x40 bytes and feed it on a timer in sync with the timer in the chip?

   currently no way to select (or display) the story book area of the games? (will require layout and
   external artwork)

*/


/*

Pico mainboard (PAL version)

+---+                              +-------------------------------------------------------------------------+
|   |                              |                                    +-+                                  |
|   |                              |                 +--+               |B|        +-+                       |
|   |                              |                 |A3|               +-+        | |                       |
|   +------------------------------+                 +--+                          |C|                     +-|
|                                                                                  | |                     | |
|    GCMK-C2X                                                                      +-+                     | --> PEN
|                                 +-----+     +--+                                                         +-|
|                                 |     |     |A3|            +--------+                                     |
|                                 | A1  |     +--+            |        |              +---------+            |
|          SEGA                   |     |                     |    A2  |              |HM53861J |            |
|     1994 837-10846              +-----+                     |        |              |         |            |
|      IAC MAIN PAL                                           +--------+              +---------+            |
|     MADE IN JAPAN                                                        +----+                            |
|           VA0          +----------+                                      |XTAL|                         +--+
|                        |   SEGA   |         +---------+                  |    |                         |  <-- VCC IN
|                        | 315-5640 |         |MC68HC000|                  |53.2|                         +--+
|                        | 9434 W51 |         |FN8-A    |  +----+          |00  |    +----------+            |
|                        |          |         |         |  |    |          +----+    |   SEGA   |            |
|                        |          |         |  2B89N  |  |    |                    | 315-5313A|            |
|                        |          |         |S0AH9425A|  | A4 |                    |   F1001  |         +--|
|                        +----------+         +---------+  |    |                    |          |         |  |
|                                                          |    |                    |9428 LAGG |         |  --> VIDEO OUT
|                                                          |    |                    |          |         |  |
|                                                          +----+                    +----------+         +--|
|                                                                                                            |
|                                                                     +----------------------------+         |
|                                                                     ||||||CARTRIDGE CONNECTOR|||||         |
|                                                                     +----------------------------+         |
|                                                                                                            |
+------------------------------------------------------------------------------------------------------------+

A1 = SEGA / 315-5641 / D77591 / 9442CA010
A2 = SEGA / 315-5769 U13 / 9451MD020
A3 = BA10324AF
A4 = MALAYSIA 9336 / 651632DFP-15 / 0000988S
B = 4K16 / HC00
C = MB3514 / 9325 M36


315-5640  - touchpad controller?
315-5313A - VDP
315-5641  - PCM chip


*/

/*
   Pico Implementation By ElBarto (Emmanuel Vadot, elbarto@megadrive.org)
   Still missing the PCM custom chip
   Some game will not boot due to this

 Pico Info from Notaz (http://notaz.gp2x.de/docs/picodoc.txt)

 addr   acc   description
-------+-----+------------
800001  byte  Version register.
              ?vv? ????, where v can be:
                00 - hardware is for Japan
                01 - European version
                10 - USA version
                11 - ?
800003  byte  Buttons, 0 for pressed, 1 for released:
                bit 0: UP (white)
                bit 1: DOWN (orange)
                bit 2: LEFT (blue)
                bit 3: RIGHT (green)
                bit 4: red button
                bit 5: unused?
                bit 6: unused?
                bit 7: pen button
800005  byte  Most significant byte of pen x coordinate.
800007  byte  Least significant byte of pen x coordinate.
800009  byte  Most significant byte of pen y coordinate.
80000b  byte  Least significant byte of pen y coordinate.
80000d  byte  Page register. One bit means one uncovered page sensor.
                00 - storyware closed
                01, 03, 07, 0f, 1f, 3f - pages 1-6
                either page 5 or page 6 is often unused.
800010  word  PCM data register.
        r/w   read returns free bytes left in PCM FIFO buffer
              writes write data to buffer.
800012  word  PCM control register.
        r/w   For writes, it has following possible meanings:
              ?p?? ???? ???? ?rrr
                p - set to enable playback?
                r - sample rate / PCM data type?
                  0: 8kHz 4bit ADPCM?
                  1-7: 16kHz variants?
              For reads, if bit 15 is cleared, it means PCM is 'busy' or
              something like that, as games sometimes wait for it to become 1.
800019  byte  Games write 'S'
80001b  byte  Games write 'E'
80001d  byte  Games write 'G'
80001f  byte  Games write 'A'

*/

#include "emu.h"
#include "megadriv.h"

#include "bus/megadrive/rom.h"
#include "sound/315-5641.h"

#include "softlist_dev.h"
#include "speaker.h"

#define VERBOSE (0)
#include "logmacro.h"

namespace {

#define PICO_PENX   1
#define PICO_PENY   2

class pico_base_state : public md_core_state
{
public:
	pico_base_state(const machine_config &mconfig, device_type type, const char *tag) :
		md_core_state(mconfig, type, tag),
		m_sega_315_5641_pcm(*this, "315_5641"),
		m_io_page(*this, "PAGE"),
		m_io_pad(*this, "PAD"),
		m_io_penx(*this, "PENX"),
		m_io_peny(*this, "PENY")
	{ }

	void init_pico();
	void init_picou();
	void init_picoj();

protected:
	optional_device<sega_315_5641_pcm_device> m_sega_315_5641_pcm;

	required_ioport m_io_page;
	required_ioport m_io_pad;
	required_ioport m_io_penx;
	required_ioport m_io_peny;

	uint8_t m_version_hi_nibble;

	uint8_t m_page_register;

	uint16_t pico_read_penpos(int pen);
	uint16_t pico_68k_io_read(offs_t offset);
	void pico_68k_io_write(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	void sound_cause_irq(int state);

	void pico_mem(address_map &map) ATTR_COLD;
};

class pico_state : public pico_base_state
{
public:
	pico_state(const machine_config &mconfig, device_type type, const char *tag) :
		pico_base_state(mconfig, type, tag),
		m_picocart(*this, "picoslot")
	{ }

	void pico_ntsc(machine_config &config);
	void pico_pal(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<pico_cart_slot_device> m_picocart;
};



uint16_t pico_base_state::pico_read_penpos(int pen)
{
	uint16_t penpos = 0;

	switch (pen)
	{
		case PICO_PENX:
			penpos = m_io_penx->read();
			penpos |= 0x6;
			penpos = penpos * 320 / 255;
			penpos += 0x3d;
			break;
		case PICO_PENY:
			penpos = m_io_peny->read();
			penpos |= 0x6;
			penpos = penpos * 251 / 255;
			penpos += 0x1fc;
			break;
	}

	return penpos;
}

uint16_t pico_base_state::pico_68k_io_read(offs_t offset)
{
	uint8_t retdata = 0;

	switch (offset)
	{
		case 0: /* Version register ?XX?????? where XX is 00 for japan, 01 for europe and 10 for USA*/
			retdata = m_version_hi_nibble;
			break;
		case 1:
			retdata = m_io_pad->read();
			break;

			/*
			Still notes from notaz for the pen :

			The pen can be used to 'draw' either on the drawing pad or on the storyware
			itself. Both storyware and drawing pad are mapped on single virtual plane, where
			coordinates range:

			x: 0x03c - 0x17c
			y: 0x1fc - 0x2f7 (drawing pad)
			  0x2f8 - 0x3f3 (storyware)
			*/
		case 2:
			retdata = pico_read_penpos(PICO_PENX) >> 8;
			break;
		case 3:
			retdata = pico_read_penpos(PICO_PENX) & 0x00ff;
			break;
		case 4:
			retdata = pico_read_penpos(PICO_PENY) >> 8;
			break;
		case 5:
			retdata = pico_read_penpos(PICO_PENY) & 0x00ff;
			break;
		case 6:
		/* Page register :
		   00 - storyware closed
		   01, 03, 07, 0f, 1f, 3f - pages 1-6
		   either page 5 or page 6 is often unused.
		*/
			{
				uint8_t tmp = m_io_page->read();
				if (tmp == 2 && m_page_register != 0x3f)
				{
					m_page_register <<= 1;
					m_page_register |= 1;
				}
				if (tmp == 1 && m_page_register != 0x00)
					m_page_register >>= 1;
				retdata = m_page_register;
				break;
			}



		case 8: // toy story 2 checks this for 0x3f (is that 'empty'?)
			/* Returns free bytes left in the PCM FIFO buffer */
			retdata = m_sega_315_5641_pcm->get_fifo_space();
			break;
		case 9:
		/*
		   For reads, if bit 15 is cleared, it means PCM is 'busy' or
		   something like that, as games sometimes wait for it to become 1.
		*/
			//  return (m_upd7759->busy_r()^1) << 15;
			// The BUSY bit stays 1 as long as some PCM sound is playing.
			// SMPS drivers check 800012 [byte] and clear the "prevent music PCM" byte when the READY bit gets set.
			// If this is done incorrectly, the voices in Sonic Gameworld (J) are muted by the music's PCM drums.
			return m_sega_315_5641_pcm->busy_r() << 15;


		case 7:
		case 10:
		case 11:
		case 12:
		case 13:
		case 14:
		case 15:
			logerror("pico_68k_io_read %d\n", offset);

	}

	return retdata | retdata << 8;
}


void pico_base_state::sound_cause_irq(int state)
{
//  printf("sound irq\n");
	/* sega_315_5641_pcm callback */
	m_maincpu->set_input_line(3, HOLD_LINE);
}

void pico_base_state::pico_68k_io_write(offs_t offset, uint16_t data, uint16_t mem_mask)
{
//  printf("pico_68k_io_write %04x %04x %04x\n", offset*2, data, mem_mask);

	switch (offset)
	{
		case 0x10/2:
			if (mem_mask & 0xff00)
				m_sega_315_5641_pcm->port_w((data >> 8) & 0xff);
			if (mem_mask & 0x00ff)
				m_sega_315_5641_pcm->port_w((data >> 0) & 0xff);
			break;
		case 0x12/2: // guess
			// Note about uPD7759 lines:
			//  reset line: 1 - normal, 1->0 - reset chip, 0 - playback disabled
			//  start line: 0->1 - start playback
			if (mem_mask & 0xff00)
			{
				// I assume that:
				// value 8000 resets the FIFO? (always used with low reset line)
				// value 0800 maps to the uPD7759's reset line (0 = reset, 1 = normal)
				// value 4000 maps to the uPD7759's start line (0->1 = start)
				m_sega_315_5641_pcm->fifo_reset_w(BIT(data, 15));
				m_sega_315_5641_pcm->reset_w(BIT(data, 11));
				m_sega_315_5641_pcm->start_w(BIT(data, 14));
			}


			/*m_sega_315_5641_pcm->reset_w(0);
			m_sega_315_5641_pcm->start_w(0);
			m_sega_315_5641_pcm->reset_w(1);
			m_sega_315_5641_pcm->start_w(1);

			if (ACCESSING_BITS_0_7) m_sega_315_5641_pcm->port_w(space,0,data&0xff);
			if (ACCESSING_BITS_8_15) m_sega_315_5641_pcm->port_w(space,0,(data>>8)&0xff);*/

			break;
	}
}

void pico_base_state::pico_mem(address_map &map)
{
	map(0x000000, 0x3fffff).rom();
	map(0x800000, 0x80001f).rw(FUNC(pico_base_state::pico_68k_io_read), FUNC(pico_base_state::pico_68k_io_write));
	map(0xc00000, 0xc0001f).rw("gen_vdp", FUNC(sega315_5313_device::vdp_r), FUNC(sega315_5313_device::vdp_w));
	map(0xe00000, 0xe0ffff).ram().mirror(0x1f0000);
}


static INPUT_PORTS_START( pico )
	PORT_START("PAD")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("Red Button")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("Pen Button")

	PORT_START("PAGE")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("Increment Page")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("Decrement Page")

	PORT_START("PENX")
	PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(30) PORT_KEYDELTA(10) PORT_MINMAX(0, 255) PORT_PLAYER(1) PORT_NAME("PEN X")

	PORT_START("PENY")
	PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_SENSITIVITY(30) PORT_KEYDELTA(10) PORT_MINMAX(0,255 ) PORT_PLAYER(1) PORT_NAME("PEN Y")
INPUT_PORTS_END


static void pico_cart(device_slot_interface &device)
{
	device.option_add_internal("rom",  MD_STD_ROM);
	device.option_add_internal("rom_sram",  MD_ROM_SRAM);   // not sure these are needed...
	device.option_add_internal("rom_sramsafe",  MD_ROM_SRAM);   // not sure these are needed...
}

void pico_state::machine_start()
{
	pico_base_state::machine_start();

	m_maincpu->space(AS_PROGRAM).install_read_handler(0x000000, 0x7fffff, read16sm_delegate(*m_picocart, FUNC(base_md_cart_slot_device::read)));
	m_maincpu->space(AS_PROGRAM).install_write_handler(0x000000, 0x7fffff, write16s_delegate(*m_picocart, FUNC(base_md_cart_slot_device::write)));
	m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0xa13000, 0xa130ff, read16sm_delegate(*m_picocart, FUNC(base_md_cart_slot_device::read_a13)), write16sm_delegate(*m_picocart, FUNC(base_md_cart_slot_device::write_a13)));
	m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0xa15000, 0xa150ff, read16sm_delegate(*m_picocart, FUNC(base_md_cart_slot_device::read_a15)), write16sm_delegate(*m_picocart, FUNC(base_md_cart_slot_device::write_a15)));
	m_maincpu->space(AS_PROGRAM).install_write_handler(0xa14000, 0xa14003, write16sm_delegate(*m_picocart, FUNC(base_md_cart_slot_device::write_tmss_bank)));

	m_vdp->stop_timers();
}

void pico_state::pico_ntsc(machine_config &config)
{
	md_core_ntsc(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &pico_state::pico_mem);

	m_vdp->add_route(ALL_OUTPUTS, "speaker", 0.50, 0);
	m_vdp->add_route(ALL_OUTPUTS, "speaker", 0.50, 1);

	PICO_CART_SLOT(config, m_picocart, pico_cart, nullptr).set_must_be_loaded(true);
	SOFTWARE_LIST(config, "cart_list").set_original("pico");

	SPEAKER(config, "speaker", 2).front();

	SEGA_315_5641_PCM(config, m_sega_315_5641_pcm, upd7759_device::STANDARD_CLOCK*2);
	m_sega_315_5641_pcm->fifo_cb().set(FUNC(pico_state::sound_cause_irq));
	m_sega_315_5641_pcm->add_route(ALL_OUTPUTS, "speaker", 0.16, 0);
	m_sega_315_5641_pcm->add_route(ALL_OUTPUTS, "speaker", 0.16, 1);
}

void pico_state::pico_pal(machine_config &config)
{
	md_core_pal(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &pico_state::pico_mem);

	m_vdp->add_route(ALL_OUTPUTS, "speaker", 0.50, 0);
	m_vdp->add_route(ALL_OUTPUTS, "speaker", 0.50, 1);

	PICO_CART_SLOT(config, m_picocart, pico_cart, nullptr).set_must_be_loaded(true);
	SOFTWARE_LIST(config, "cart_list").set_original("pico");

	SPEAKER(config, "speaker", 2).front();

	SEGA_315_5641_PCM(config, m_sega_315_5641_pcm, upd7759_device::STANDARD_CLOCK*2);
	m_sega_315_5641_pcm->fifo_cb().set(FUNC(pico_state::sound_cause_irq));
	m_sega_315_5641_pcm->add_route(ALL_OUTPUTS, "speaker", 0.16, 0);
	m_sega_315_5641_pcm->add_route(ALL_OUTPUTS, "speaker", 0.16, 1);
}



ROM_START( pico )
	ROM_REGION(MD_CPU_REGION_SIZE, "maincpu", ROMREGION_ERASEFF)
	ROM_REGION( 0x10000, "soundcpu", ROMREGION_ERASEFF)
ROM_END

ROM_START( picou )
	ROM_REGION(MD_CPU_REGION_SIZE, "maincpu", ROMREGION_ERASEFF)
	ROM_REGION( 0x10000, "soundcpu", ROMREGION_ERASEFF)
ROM_END

ROM_START( picoj )
	ROM_REGION(MD_CPU_REGION_SIZE, "maincpu", ROMREGION_ERASEFF)
	ROM_REGION( 0x10000, "soundcpu", ROMREGION_ERASEFF)
ROM_END


void pico_base_state::init_pico()
{
	m_maincpu->set_tas_write_callback(*this, FUNC(pico_base_state::megadriv_tas_callback));

	// TODO: move this to the device interface?
	m_vdp->set_use_cram(1);
	m_vdp->set_vdp_pal(true);
	m_vdp->set_framerate(50);
	m_vdp->set_total_scanlines(313);

	m_version_hi_nibble = 0x60; // Export PAL
}

void pico_base_state::init_picou()
{
	m_maincpu->set_tas_write_callback(*this, FUNC(pico_base_state::megadriv_tas_callback));

	// TODO: move this to the device interface?
	m_vdp->set_use_cram(1);
	m_vdp->set_vdp_pal(false);
	m_vdp->set_framerate(60);
	m_vdp->set_total_scanlines(262);

	m_version_hi_nibble = 0x40; // Export NTSC
}

void pico_base_state::init_picoj()
{
	m_maincpu->set_tas_write_callback(*this, FUNC(pico_base_state::megadriv_tas_callback));

	// TODO: move this to the device interface?
	m_vdp->set_use_cram(1);
	m_vdp->set_vdp_pal(false);
	m_vdp->set_framerate(60);
	m_vdp->set_total_scanlines(262);

	m_version_hi_nibble = 0x00; // JPN NTSC
}


/*

This looks a lot like a Pico with extra sound hardware...
 YMZ263B is the basis of a Sound Blaster Clone
 YMF262-M is the OPL3

YAMAHA - MIXT BOOK PLAYER COPERA
MMG-1


            +-------------+
            |             |
            |------+      |
            |      |      |
 MIDI OUT <--      |      |
            |      |      |
            |------+      |
            |             |              +------------------------------------------------------------------------------------------------------+
            |------+      |              |                                    +--+                                                              |
            |      |      |              |                                    |YM|                                                              |
  MIDI IN <--      |      |              |                                    |7 |                                                              |
            |      |      |              |                                    |12|                             +---------+                      |
            |------+ +--+ |       +------+                                    |8B|                             | YAMAHA  |                      |
            |        |||| |       |                                           +--+                             | YMZ263B |                      |
            +---------||--+       |                              +--------+                                    |         |                      |
                      ||          |                              |YM7 128B|                                    +---------+    +---+             |
                      ||          |                              +--------+                                                   |YMF|             |
            +---------||----------+                                                                                           |262|             |
            |         ||                                                                                                      |-M |             |
            |         ||                                                                                                      +---+             |
            |         ||                                                                                                                        |
            |         ||                                                           SEGA                                                         |
            |------+  ||                                                 1993 837-9845 COPERA                                                   |
            |      |  ||                                                      MADE IN JAPAN                                                  +--|
            |      |  ||                                                           VA0                                                       |  --> PEN
  CONTROL <--      |  ||                                                                                                                     |  |
            |      |  ||                                                                                                                     +--|
            |      |  ||                                                                                                                        |
            |      |  ||                                                                                                                        |
            |------+  ||                                                                                         +----+                         |
            |         ||                                                                                         |XTAL|  +------------+         |
            |         ||                                                                                         |    |  | OKI JAPAN  |      +----+
            |         ||                                          +----------+                                   |53.6|  | M54C864-80 |      |    |
            |        ||||                                         |   SEGA   |                                   |93  |  +------------+      |    |
            |        +--+                                         | 315-5639 |                                   | Mhz|                      |    |
            |                                                     |       U11|                                   +----+  +-------------+     |  E |
            |                                                     |9341PD025 |                                           |             |     |  X |
            |--+                                                  +----------+                                           |     SEGA    |     |  T |
      MIC <--  |                                                                     +----+  +----+                      |  315-5313A  |     |  E |
            |  |                                                                     |TC51|  |TC51|                      |   FC1001    |     |  N |
            |--+                                                                     |832A|  |832A|                      |  9331 AASG  |     |  D |
            |                                                                        |FL-1|  |FL-1|                      |             |     |  E |
            |                                                                        |0   |  |0   |                      |             |     |  D |
            |                                                                        |    |  |    |                      |             |     |    |
            |--+                                                                     +----+  +----+                      +-------------+     |    |
          +--  |                                                                                                                             |  C |
          | |--+                                                                                                                             |  O |
S-AUDIO <-| |                              +-----------+           +-------+                                                                 |  N |
          | |--+                           |   SEGA    |           | SEGA  |          +-----------+                                          |  N |
          +--  |                           | 315-5640  |           |315-564|          |   3D4 UA  |                                          |  E |
            |--+                           | 9333 W26  |           |1      |          |HD68HC000CP|                                          |  C |
            |                              |           |           |D77591 |          |8          |                                          |  T |
            |--+                           |           |           +-------+          |           |                                          |  O |
  S-VIDEO <--  |                           |           |                              |           |                                          |  R |
            |--+                           |           |                              |           |                                          |    |
            |                              +-----------+                              |      JAPAN|                                          |    |
            |                                                                         +-----------+                                          |    |
            |--+                                                                                                                             |    |
       VCC --> |                                                                      +----------------------------------------------+       |    |
            |--+                                                                      |                  CARTRIDGE                   |       +----+
            |                                                                         |                  CONNECTOR                   |          |
            |                                                                         +----------------------------------------------+          |
            +-----------------------------------------------------------------------------------------------------------------------------------+

*/



class copera_state : public pico_base_state
{
public:
	copera_state(const machine_config &mconfig, device_type type, const char *tag) :
		pico_base_state(mconfig, type, tag),
		m_picocart(*this, "coperaslot")
	{ }

	void copera(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

	void copera_pcm_cb(int state);
	uint16_t copera_io_read(offs_t offset);
	void copera_io_write(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

	TIMER_CALLBACK_MEMBER(process_ext_timer);

private:
	void copera_mem(address_map &map) ATTR_COLD;

	required_device<copera_cart_slot_device> m_picocart;

	std::unique_ptr<uint16_t[]> m_ext_regs;
	bool m_is_ext_requested;
	emu_timer *m_ext_timer;
};

TIMER_CALLBACK_MEMBER(copera_state::process_ext_timer)
{
	// TODO: When is it enabled? Expected even if games don't set bit 3 of VDP mode register 3...
	if (m_is_ext_requested)
	{
		m_maincpu->set_input_line(2, HOLD_LINE);
		m_is_ext_requested = false;
	}
	else
	{
		m_maincpu->set_input_line(2, CLEAR_LINE);
		m_is_ext_requested = true;
	}
}

void copera_state::copera_mem(address_map &map)
{
	map(0x000000, 0x3fffff).rom();
	map(0x800000, 0x80001f).rw(FUNC(copera_state::pico_68k_io_read), FUNC(copera_state::pico_68k_io_write));
	map(0xbff800, 0xbff87f).rw(FUNC(copera_state::copera_io_read), FUNC(copera_state::copera_io_write)); // FIXME: Guessed range.
	map(0xc00000, 0xc0001f).rw(m_vdp, FUNC(sega315_5313_device::vdp_r), FUNC(sega315_5313_device::vdp_w));
	map(0xe00000, 0xe0ffff).ram().mirror(0x1f0000);
}

static void copera_cart(device_slot_interface &device)
{
	device.option_add_internal("rom",  MD_STD_ROM);
	device.option_add_internal("rom_sram",  MD_ROM_SRAM);   // not sure these are needed...
	device.option_add_internal("rom_sramsafe",  MD_ROM_SRAM);   // not sure these are needed...
}

void copera_state::machine_start()
{
	pico_base_state::machine_start();

	m_maincpu->space(AS_PROGRAM).install_read_handler(0x000000, 0x7fffff, read16sm_delegate(*m_picocart, FUNC(base_md_cart_slot_device::read)));
	m_maincpu->space(AS_PROGRAM).install_write_handler(0x000000, 0x7fffff, write16s_delegate(*m_picocart, FUNC(base_md_cart_slot_device::write)));
	m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0xa13000, 0xa130ff, read16sm_delegate(*m_picocart, FUNC(base_md_cart_slot_device::read_a13)), write16sm_delegate(*m_picocart, FUNC(base_md_cart_slot_device::write_a13)));
	m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0xa15000, 0xa150ff, read16sm_delegate(*m_picocart, FUNC(base_md_cart_slot_device::read_a15)), write16sm_delegate(*m_picocart, FUNC(base_md_cart_slot_device::write_a15)));
	m_maincpu->space(AS_PROGRAM).install_write_handler(0xa14000, 0xa14003, write16sm_delegate(*m_picocart, FUNC(base_md_cart_slot_device::write_tmss_bank)));

	m_sega_315_5641_pcm->reset_w(0);
	m_sega_315_5641_pcm->start_w(0);
	m_sega_315_5641_pcm->reset_w(1);
	m_sega_315_5641_pcm->start_w(1);

	m_vdp->stop_timers();

	m_ext_regs = make_unique_clear<uint16_t[]>(0x80/2);

	// FIXME: Guessed timing.
	//
	// It must be less than HBLANK. Games have a busy loop where they read
	// VDP status register and check if bit 7 (vertical interrupt pending) is
	// set and then cleared (e.g. Copera no Chikyuu Daisuki @ 0xfb3c4).
	// Too frequent EXT interrupts result in that subroutine only executing after
	// scanline 224 and will never catch bit 7 set.
	m_ext_timer = timer_alloc(FUNC(copera_state::process_ext_timer), this);
	m_ext_timer->adjust(attotime::zero, 0, m_vdp->screen().scan_period() * 20);
}

void copera_state::machine_reset()
{
	pico_base_state::machine_reset();

	m_is_ext_requested = true;
	m_ext_regs[0] = 0;
	m_ext_regs[0x2/2] = 0xffff;
	m_ext_regs[0x4/2] = 0xffff;
}

void copera_state::copera(machine_config &config)
{
	md_core_ntsc(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &copera_state::copera_mem);

	m_vdp->add_route(ALL_OUTPUTS, "speaker", 0.50, 0);
	m_vdp->add_route(ALL_OUTPUTS, "speaker", 0.50, 1);

	COPERA_CART_SLOT(config, m_picocart, copera_cart, nullptr).set_must_be_loaded(true);
	SOFTWARE_LIST(config, "cart_list").set_original("copera");

	SPEAKER(config, "speaker", 2).front();

	SEGA_315_5641_PCM(config, m_sega_315_5641_pcm, upd7759_device::STANDARD_CLOCK);
	m_sega_315_5641_pcm->fifo_cb().set(FUNC(copera_state::copera_pcm_cb));
	m_sega_315_5641_pcm->add_route(ALL_OUTPUTS, "speaker", 0.16, 0);
	m_sega_315_5641_pcm->add_route(ALL_OUTPUTS, "speaker", 0.16, 1);
}

void copera_state::copera_pcm_cb(int state)
{
	// TODO: Not IRQ3 (games assign an infinite loop handler), likely handled by an EXT callback.
}

uint16_t copera_state::copera_io_read(offs_t offset)
{
	LOG("COPERA IO r @ %08x: %08x = %04x\n", m_maincpu->pc(), 0xbff800 + offset * 2, m_ext_regs[offset]);
	return m_ext_regs[offset];
}

void copera_state::copera_io_write(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (ACCESSING_BITS_8_15)
	{
		m_ext_regs[offset] = (data & mem_mask) | (m_ext_regs[offset] & 0x00ff);
	}
	if (ACCESSING_BITS_0_7)
	{
		m_ext_regs[offset] = (data & mem_mask) | (m_ext_regs[offset] & 0xff00);
	}

	// TODO: We only enable EXT handler callback 3.
	if (((m_ext_regs[0x4/2] & 0xff) == 0xd) && ((m_ext_regs[0x2/2] & 0xff) == 0x3f))
	{
		m_ext_regs[0] |= 1 << 3;
	}

	LOG("COPERA IO w @ %08x: %08x = %04x (mask %08x)\n", m_maincpu->pc(), 0xbff800 + offset * 2, data, mem_mask);
}


ROM_START( copera )
	ROM_REGION(MD_CPU_REGION_SIZE, "maincpu", ROMREGION_ERASEFF)
	ROM_REGION( 0x10000, "soundcpu", ROMREGION_ERASEFF)
ROM_END

} // anonymous namespace


CONS( 1994, pico,  0,    0, pico_pal,  pico, pico_state,   init_pico,  "Sega", "Pico (Europe, PAL)", MACHINE_NOT_WORKING)
CONS( 1994, picou, pico, 0, pico_ntsc, pico, pico_state,   init_picou, "Sega", "Pico (USA, NTSC)",   MACHINE_NOT_WORKING)
CONS( 1993, picoj, pico, 0, pico_ntsc, pico, pico_state,   init_picoj, "Sega", "Pico (Japan, NTSC)", MACHINE_NOT_WORKING)

CONS( 1993, copera, 0,   0, copera,    pico, copera_state, init_picoj, "Yamaha / Sega", "Yamaha Mixt Book Player Copera", MACHINE_NOT_WORKING)



segapm.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/* Sega Picture Magic (codename JANUS) */
// http://segaretro.org/Sega_Picture_Magic

// this uses a Sega 32X PCB (not in a 32X case) attached to a stripped down 68k based board rather than a full Genesis / Megadrive
// it is likely the internal SH2 bios roms differ


#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "emupal.h"
#include "screen.h"


namespace {

class segapm_state : public driver_device
{
public:
	segapm_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu")
	{ }

	void segapm(machine_config &config);

private:
	virtual void video_start() override ATTR_COLD;
	uint32_t screen_update_segapm(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	required_device<cpu_device> m_maincpu;
	void segapm_map(address_map &map) ATTR_COLD;
};


void segapm_state::video_start()
{
}

uint32_t segapm_state::screen_update_segapm(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}



void segapm_state::segapm_map(address_map &map)
{
	map(0x000000, 0x07ffff).rom();

	// A15100

	map(0xe00000, 0xe7ffff).ram();

}

static INPUT_PORTS_START( segapm )
INPUT_PORTS_END




void segapm_state::segapm(machine_config &config)
{
	M68000(config, m_maincpu, 8000000); // ??
	m_maincpu->set_addrmap(AS_PROGRAM, &segapm_state::segapm_map);

	// + 2 sh2s on 32x board

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(0));
	screen.set_size(32*8, 32*8);
	screen.set_visarea(0*8, 32*8-1, 2*8, 30*8-1);
	screen.set_screen_update(FUNC(segapm_state::screen_update_segapm));
	screen.set_palette("palette");

	PALETTE(config, "palette").set_format(palette_device::xRGB_555, 0x200);
}



ROM_START( segapm ) // was more than one cartridge available? if so softlist them?
	ROM_REGION( 0x80000, "maincpu", 0 ) /* 68000 Code */
	ROM_LOAD( "picture magic boot cart.bin", 0x00000, 0x80000, CRC(c9ab4e60) SHA1(9c4d4ab3e59c8acde86049a1ba3787aa03b549a3) ) // internal header is GOUSEI HENSYUU

	// todo, sh2 bios roms etc.
ROM_END

} // anonymous namespace


//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY  FULLNAME         FLAGS
CONS( 1996, segapm, 0,      0,      segapm,  segapm, segapm_state, empty_init, "Sega",  "Picture Magic", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



selz80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

SEL Z80 Trainer (LEHRSYSTEME)

2010-08-31 Skeleton driver.
2011-06-23 Working [Robbbert]

No diagram has been found. The following is guesswork.

Test sequence: Press -, enter an address, press = to show contents, press
               up/down-arrow to cycle through addresses.

Paste test: -=11H22H33H44H55H66H77H88H99HK-1000=
Now press UP to see that the data has been entered.

ToDo:
- Keys are a guess, need to be confirmed.
- Needs to be tested by a subject-matter expert.
- i8255 to be added (address is unknown)
- "Tape-Interface" to be added (has its own LED)
- "Binary" area to be added - has 8 slide switches and a LED for each
- Halt LED
- "User Display" to be added - has 6 digits and a 74C917 chip
- 3 large sockets labelled "E C B - BUS"

- Unknown I/O:
'maincpu' (00F2): unmapped i/o memory write to 000C = 00 & FF
'maincpu' (00F8): unmapped i/o memory write to 0010 = FF & FF

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/i8251.h"
#include "machine/i8279.h"
#include "bus/rs232/rs232.h"
#include "machine/clock.h"
#include "video/pwm.h"
#include "selz80.lh"


namespace {

class selz80_state : public driver_device
{
public:
	selz80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_p_ram(*this, "ram")
		, m_io_keyboard(*this, "X%u", 0U)
		, m_clock(*this, "uart_clock")
		, m_display(*this, "display")
	{ }

	void selz80(machine_config &config);

protected:
	void scanlines_w(uint8_t data);
	void digit_w(uint8_t data);
	uint8_t kbd_r();

	void selz80_io(address_map &map) ATTR_COLD;

	u8 m_digit = 0U;
	u8 m_seg = 0U;
	void setup_baud();
	void machine_start() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	optional_shared_ptr<u8> m_p_ram;
	required_ioport_array<4> m_io_keyboard;
	required_device<clock_device> m_clock;
	required_device<pwm_display_device> m_display;

private:
	void selz80_mem(address_map &map) ATTR_COLD;
	void machine_reset() override ATTR_COLD;

};

class dagz80_state : public selz80_state
{
public:
	using selz80_state::selz80_state;
	void dagz80(machine_config &config);

private:
	void dagz80_mem(address_map &map) ATTR_COLD;
	void machine_reset() override ATTR_COLD;
};

void dagz80_state::dagz80_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).ram().share("ram");
	map(0xe000, 0xffff).ram();
}

void selz80_state::selz80_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).rom();
	map(0x1000, 0x27ff).ram(); // all 3 RAM sockets filled
	// map(0x3000, 0x37ff).rom();  // empty socket for ROM
	map(0xa000, 0xffff).rom();
}

void selz80_state::selz80_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x01).rw("i8279", FUNC(i8279_device::read), FUNC(i8279_device::write));
	map(0x18, 0x19).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));
}

/* Input ports */
static INPUT_PORTS_START( selz80 )
/* 2 x 16-key pads
RS SS RN MM     C(IS)  D(FL)  E(FL') F
EX BP TW TR     8(IX)  9(IY)  A(PC)  B(SP)
RL IN -  +      4(AF') 5(BC') 6(DE') 7(HL')
RG EN SA SD     0(AF)  1(BC)  2(DE)  3(HL)
  */
	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E FL'") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D FL") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C IS") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MM") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RN") PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RS") PORT_CODE(KEYCODE_T) PORT_CHAR('T')

	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B SP") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A PC") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 IY") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 IX") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TR (tape read)") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TW (tape write)") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BP") PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("EX") PORT_CODE(KEYCODE_O) PORT_CHAR('O')

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 HL'") PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 DE'") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 BC'") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 AF'") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("+") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_DOWN) PORT_CHAR('V')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("IN") PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RL") PORT_CODE(KEYCODE_G) PORT_CHAR('G')

	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 HL") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 DE") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 BC") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0 AF") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SD") PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SA") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("EN") PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RG") PORT_CODE(KEYCODE_L) PORT_CHAR('L')

	PORT_START("BJ") // baud jumper
	/* not connected to cpu, each pair of pins is connected directly to the output
	   of a 4020 counter dividing the ???? clock to feed the 8251. You use a jumper
	   (like the kind on the back of a IDE hard drive) to choose the speed. */
	PORT_DIPNAME( 0x0F, 0x00, "Baud Rate" )
	PORT_DIPSETTING(    0x00, "9600" )
	PORT_DIPSETTING(    0x01, "4800" )
	PORT_DIPSETTING(    0x02, "2400" )
	PORT_DIPSETTING(    0x03, "1200" )
	PORT_DIPSETTING(    0x04, "600" )
	PORT_DIPSETTING(    0x05, "300" )
	PORT_DIPSETTING(    0x06, "150" )
	PORT_DIPSETTING(    0x07, "75" )
	PORT_DIPSETTING(    0x08, "38" ) // 37.5
	PORT_DIPSETTING(    0x09, "18" ) // 17.75
	PORT_DIPSETTING(    0x0A, "9" )  // 8.875
INPUT_PORTS_END


void selz80_state::setup_baud()
{
	// setup baud rate for uart
	u8 baudsw = ioport("BJ")->read() & 15;
	if (baudsw)
	{
		u32 speed = (9600*16) >> baudsw;
		m_clock->set_unscaled_clock(speed);
	}
}

void selz80_state::machine_start()
{
	save_item(NAME(m_digit));
	save_item(NAME(m_seg));
}

void selz80_state::machine_reset()
{
	setup_baud();
}

void dagz80_state::machine_reset()
{
	setup_baud();
	uint8_t* rom = memregion("user1")->base();
	uint16_t size = memregion("user1")->bytes();
	memcpy(m_p_ram, rom, size);
	m_maincpu->reset();
}

void selz80_state::scanlines_w(uint8_t data)
{
	m_digit = data;
	m_display->matrix(1 << m_digit, m_seg);
}

void selz80_state::digit_w(uint8_t data)
{
	m_seg = bitswap<8>(data, 3, 2, 1, 0, 7, 6, 5, 4);
	m_display->matrix(1 << m_digit, m_seg);
}

uint8_t selz80_state::kbd_r()
{
	uint8_t data = 0xff;

	if ((m_digit & 7) < 4)
		data = m_io_keyboard[m_digit & 3]->read();

	return data;
}

void selz80_state::selz80(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 4.9152_MHz_XTAL / 2); // NEC uPD780C-1 cpu
	m_maincpu->set_addrmap(AS_PROGRAM, &selz80_state::selz80_mem);
	m_maincpu->set_addrmap(AS_IO, &selz80_state::selz80_io);

	/* video hardware */
	config.set_default_layout(layout_selz80);
	PWM_DISPLAY(config, m_display).set_size(8, 8);
	m_display->set_segmask(0xff, 0xff);

	/* Devices */
	CLOCK(config, m_clock, 153'600);
	m_clock->signal_handler().set("uart", FUNC(i8251_device::write_txc));
	m_clock->signal_handler().append("uart", FUNC(i8251_device::write_rxc));

	i8251_device &uart(I8251(config, "uart", 0));
	uart.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	uart.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	uart.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set("uart", FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set("uart", FUNC(i8251_device::write_dsr));
	rs232.cts_handler().set("uart", FUNC(i8251_device::write_cts));

	i8279_device &kbdc(I8279(config, "i8279", 4.9152_MHz_XTAL / 2)); // based on divider
	kbdc.out_sl_callback().set(FUNC(selz80_state::scanlines_w));    // scan SL lines
	kbdc.out_disp_callback().set(FUNC(selz80_state::digit_w));      // display A&B
	kbdc.in_rl_callback().set(FUNC(selz80_state::kbd_r));           // kbd RL lines
	kbdc.in_shift_callback().set_constant(1);                       // Shift key
	kbdc.in_ctrl_callback().set_constant(1);
}

void dagz80_state::dagz80(machine_config &config)
{
	selz80(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &dagz80_state::dagz80_mem);
}


/* ROM definition */
ROM_START( selz80 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "v3.3", "V3.3")
	ROMX_LOAD( "z80-trainer.rom", 0x0000, 0x1000, CRC(eed1755f) SHA1(72e6ebfccb0e50034660bc36db1a741932311ce1), ROM_BIOS(0)) // (c)TEL86/V3.3
	ROM_SYSTEM_BIOS(1, "v3.2", "V3.2")
	ROMX_LOAD( "moniz80_3.2_04.12.1985.bin", 0x0000, 0x1000, CRC(3a3cf574) SHA1(ba6cd2276ce66f3a4545baf4d396f6c06d51dc38), ROM_BIOS(1)) // (c)SEL85/V3.2
	ROM_LOAD( "term80-a000.bin", 0xa000, 0x2000, CRC(0a58c0a7) SHA1(d1b4b3b2ad0d084175b1ff6966653d8b20025252))
	ROM_LOAD( "term80-e000.bin", 0xe000, 0x2000, CRC(158e08e6) SHA1(f1add43bcf8744a01238fb893ee284872d434db5))
ROM_END

ROM_START( dagz80 )
	ROM_REGION( 0x2000, "user1", ROMREGION_ERASEFF )
	ROM_LOAD( "moni_1.5_15.08.1988.bin", 0x0000, 0x2000, CRC(318aee6e) SHA1(c698fdee401b88e673791aabcba6a9628938a075) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY  FULLNAME           FLAGS
COMP( 1985, selz80, 0,      0,      selz80,  selz80, selz80_state, empty_init, "SEL",   "SEL Z80 Trainer", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1988, dagz80, selz80, 0,      dagz80,  selz80, dagz80_state, empty_init, "DAG",   "DAG Z80 Trainer", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



semcrossw.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:

/***************************************************************************

 ETRA (https://www.grupoetra.com/) semaphore controller for a crosswalk
 (unknown model, unknown year).

Main PCB
  __________________________________________________
 |     _______    _______    _______                |
 |    |      |   |      |   |      |                |
 |    |______|   |______|   |______|                |
 |                           _________    _________ |
 |                          |_74LS03_|   DM74LS122N |
 |              _________                           |
 |             |_UM6114_|    _________    _________ |
 |                          |T74LS04B1   |_74LS90_| |__
 |                                                   __|
 |              _________    _________               __|
 |             |_UM6114_|   DM74LS155N    _________  __|
 |       ________________                DM74LS155N  __|
  \     | X2816CP-12    |  Xtal                      __|
  _\    |_______________|  4.000 MHz                 __|
 |__     ________________    ________________        __|
 |__    | AT2716 EPROM  |   | MC6802P       |        __|
 |__    |_______________|   |_______________|        __|
 |__                                                 __|
 |__     _________           ________________        __|
 |__    |_74LS156|          | MC6821P       |       |
 |__                        |_______________|       |
 |__                                                |
 |__     _________    _________    _________        |
 |__    |________|   |_74LS132|   |_74LS90_|        |
   |                                                |
   |________________________________________________|

Relays PCB
           _________________________________________
          |                                         |
          |                                         |
          |                        _________        |
          |                       |74LS122N|        |
          |                 ________________        |
         /                 | MC6821P       |        |
        /                  |_______________|        |
       /                                            |__
  ____/                   _______ _______            __|
 |       _______          MOC3020 MOC3020            __|
 |      TXAL2215B         _______ _______   _______  __|
 |       _______          MOC3020 MOC3020  |_7404N|  __|
  \     TXAL2215B         _______ _______   _______  __|
   \     _______          MOC3020 MOC3020  |_7404N|  __|
   |    TXAL2215B         _______ _______   _______  __|
   |     _______          MOC3020 MOC3020  |_7404N|  __|
   |    TXAL2215B         _______ _______            __|
   |     _______          MOC3020 MOC3020            __|
   |    TXAL2215B         _______ _______            __|
   |     _______          MOC3020 MOC3020           |
   |    TXAL2215B                                   |
   |                                                |
   |                                                |
   |                                                |
   |                                                |
   |________________________________________________|

Programmer PCB (keyboard)
    _______________________________________________________
   |  ______ ______ ______ ______ ______ ______           |
   | | ___ || ___ || ___ || ___ || ___ || ___ |           |
   | ||__| |||__| |||__| |||__| |||__| |||__| |  ___      |
   | ||__| |||__| |||__| |||__| |||__| |||__| | |  |      |
   | |_____||_____||_____||_____||_____||_____| |  |<-7407N
   |                                            |__|      |
   |       __________________________________             |
   |      | ____   ____   ____   ____   _   |             |
   |      || R |  | M |  | N |  | K |  (_)  |             |
   |      ||___|  |___|  |___|  |___|       |             |
   | ___  | ____   ____   ____   ____       |      SWITCH |
   ||  |  || 0 |  | 1 |  | 2 |  | 3 |   __  |             |
   ||  |  ||___|  |___|  |___|  |___|  (||) |             |
   ||__|  | ____   ____   ____   ____       |             |
 74LS155N || 4 |  | 5 |  | 6 |  | 7 |   __  |             |
   |      ||___|  |___|  |___|  |___|  (||) |             |
   | ___  | ____   ____   ____   ____       |    ___      |
   ||  |  || 8 |  | 9 |  | A |  | B |   __  |   |  |      |
   ||  |  ||___|  |___|  |___|  |___|  (||) |   |  |<-SN74LS03N
   ||__|  | ____   ____   ____   ____       |   |__|      |
SCL4052BE || C |  | D |  | E |  | F |       |    ___  ___ |
   |      ||___|  |___|  |___|  |___|       |   |  |<-TC4093BP
   | ___  |_________________________________|   |  | |  | |
   ||  |                                        |__| |__|<-7407N
   ||  |<-CD4093BE                                        |
   ||__|   ____  ____  ____  ____  ____  ____  ____  ____ |
   |       4N32  B250  4N32  B250  4N32  B250  4N32  B250 |
   |            C1000       C1000       C1000       C1000 |
   |                               __________             |
   |                              |  CONN   |             |
   |______________________________________________________|

Notes from one operator that used to work with this controller model:
 For programming the semaphore controller, you just put the memory values with the keyboard.
 From 100 to 200 you'll find the first program, from 200 to 300 the second, and so on up to
 seven programs, with 100 for green, 101 for yellow, 102 for clear, and then repeat it again.

***************************************************************************/

#include "emu.h"

#include "cpu/m6800/m6800.h"
#include "machine/6821pia.h"


namespace {

class semcrossw_state : public driver_device
{
public:
	semcrossw_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pia(*this, "pia%u", 1U)
	{
	}

	void semcrossw(machine_config &config);

private:
	// devices/pointers
	required_device<m6802_cpu_device> m_maincpu;
	required_device_array<pia6821_device, 2> m_pia;
};

static INPUT_PORTS_START(semcrossw)
INPUT_PORTS_END

void semcrossw_state::semcrossw(machine_config &config)
{
	M6802(config, m_maincpu, XTAL(4'000'000));

	PIA6821(config, m_pia[0]);

	PIA6821(config, m_pia[1]);
}

ROM_START(semcrossw)
	ROM_REGION(0x800, "maincpu", 0)
	ROM_LOAD("at27c16.bin",    0x000, 0x800, CRC(2e7b10b1) SHA1(fba6465db1baa38ab79ed24a85de460f8be488b9))

	ROM_REGION(0x800, "eeprom", 0)
	ROM_LOAD("x2816cp-12.bin", 0x000, 0x800, BAD_DUMP CRC(c2ef2e80) SHA1(6c3c4215169c2941a37053888174fe0499301bac)) // BAD_DUMP because dumped from an already configured machine
ROM_END

} // anonymous namespace


//   YEAR  NAME       PARENT COMPAT MACHINE    INPUT      CLASS            INIT        COMPANY  FULLNAME                                              FLAGS
SYST(19??, semcrossw, 0,     0,     semcrossw, semcrossw, semcrossw_state, empty_init, "Etra",  "Crosswalk traffic light controller (unknown model)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



senterprise.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle, Berger
/*******************************************************************************

CXG Super Enterprise

The chess engine is Kaare Danielsen's Enterprise program. It's the 16KB 'sequel'
to LogiChess used in Enterprise "S" (emulated in saitek/companion2.cpp).

NOTE: It triggers an NMI when the power switch is changed from ON to SAVE.
If this is not done, NVRAM won't save properly.

TODO:
- if/when MAME supports an exit callback, hook up power-off switch to that

Hardware notes:

Super Crown:
- PCB label: CXG 218-600-001
- Hitachi HD6301Y0P (mode 2), 8MHz XTAL
- 2KB battery-backed RAM (HM6116LP-3)
- chessboard buttons, 24 LEDs, piezo

Super Enterprise (model 210.C):
- PCB label: 210C 600-002
- Sanyo LC7580, same LCDs as Sphinx Galaxy
- rest is same as above

210 MCU is used in:
- CXG Super Enterprise (model 210, black/brown/blue)
- CXG Advanced Star Chess (model 211)
- CXG Super Crown (model 218, black/brown)
- Mephisto Merlin 16K (H+G brand Super Crown)

210C MCU is used in:
- CXG Super Enterprise (model 210.C)
- CXG Sphinx Titan (model 270, suspected)

*******************************************************************************/

#include "emu.h"

#include "cpu/m6800/m6801.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/lc7580.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "cxg_senterprise.lh"
#include "cxg_senterprisec.lh"


namespace {

// model 210 / shared

class senterp_state : public driver_device
{
public:
	senterp_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(power_off);

	void senterp(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// devices/pointers
	required_device<hd6301y0_cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<3> m_inputs;

	emu_timer *m_standbytimer;
	u8 m_inp_mux = 0;

	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	u8 input1_r();
	u8 input2_r();
	void leds_w(u8 data);
	void mux_w(u8 data);

	TIMER_CALLBACK_MEMBER(set_standby);
};

void senterp_state::machine_start()
{
	m_standbytimer = timer_alloc(FUNC(senterp_state::set_standby), this);

	// register for savestates
	save_item(NAME(m_inp_mux));
}


// model 210.C

class senterpc_state : public senterp_state
{
public:
	senterpc_state(const machine_config &mconfig, device_type type, const char *tag) :
		senterp_state(mconfig, type, tag),
		m_lcd(*this, "lcd"),
		m_out_digit(*this, "digit%u", 0U),
		m_out_lcd(*this, "s%u.%u", 0U, 0U)
	{ }

	void senterpc(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<lc7580_device> m_lcd;
	output_finder<8> m_out_digit;
	output_finder<2, 52> m_out_lcd;

	void lcd_output_w(offs_t offset, u64 data);
	void lcd_w(u8 data);
};

void senterpc_state::machine_start()
{
	senterp_state::machine_start();

	// resolve outputs
	m_out_digit.resolve();
	m_out_lcd.resolve();
}



/*******************************************************************************
    Power
*******************************************************************************/

void senterp_state::machine_reset()
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
	m_maincpu->set_input_line(M6801_STBY_LINE, CLEAR_LINE);
}

TIMER_CALLBACK_MEMBER(senterp_state::set_standby)
{
	m_maincpu->set_input_line(M6801_STBY_LINE, ASSERT_LINE);
}

INPUT_CHANGED_MEMBER(senterp_state::power_off)
{
	if (newval && !m_maincpu->standby())
	{
		// NMI when power switch is set to SAVE, followed by STBY (internal or STBY pin)
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
		m_standbytimer->adjust(attotime::from_msec(10));
	}
}



/*******************************************************************************
    I/O
*******************************************************************************/

// common

u8 senterp_state::input1_r()
{
	u8 data = 0;

	// P20,P21: read buttons
	for (int i = 0; i < 2; i++)
		if (m_inp_mux & m_inputs[i]->read())
			data |= 1 << i;

	// P26,P27: freq sel (senterp)
	data |= m_inputs[2]->read();
	return ~data;
}

u8 senterp_state::input2_r()
{
	u8 data = 0;

	// P50-P57: read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_rank(i);

	return ~data;
}

void senterp_state::leds_w(u8 data)
{
	// P23-P25: led select
	m_display->write_my(~data >> 3 & 7);
}

void senterp_state::mux_w(u8 data)
{
	// P60-P67: input mux, led data
	m_inp_mux = ~data;
	m_display->write_mx(m_inp_mux);
}


// LCD (senterpc)

void senterpc_state::lcd_output_w(offs_t offset, u64 data)
{
	// output individual segments
	for (int i = 0; i < 52; i++)
		m_out_lcd[offset][i] = BIT(data, i);

	// unscramble digit 7segs
	static const u8 seg2digit[4*7] =
	{
		0x03, 0x04, 0x00, 0x40, 0x41, 0x02, 0x42,
		0x05, 0x06, 0x07, 0x48, 0x44, 0x45, 0x46,
		0x0c, 0x0d, 0x0b, 0x0a, 0x4a, 0x4c, 0x4b,
		0x0e, 0x0f, 0x10, 0x50, 0x4d, 0x4e, 0x4f
	};

	for (int i = 0; i < 8; i++)
	{
		u8 digit = 0;
		for (int seg = 0; seg < 7; seg++)
		{
			u8 bit = seg2digit[7 * (i & 3) + seg] + 26 * (i >> 2);
			digit |= m_out_lcd[BIT(bit, 6)][bit & 0x3f] << seg;
		}
		m_out_digit[i] = digit;
	}
}

void senterpc_state::lcd_w(u8 data)
{
	// P22: LC7580 DATA
	// P26: LC7580 CLK
	// P27: LC7580 CE
	m_lcd->data_w(BIT(data, 2));
	m_lcd->clk_w(BIT(data, 6));
	m_lcd->ce_w(BIT(data, 7));

	// P22+P27: piezo
	m_dac->write(BIT(data, 2) & BIT(~data, 7));
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void senterp_state::main_map(address_map &map)
{
	map(0x4000, 0x47ff).mirror(0x3800).ram().share("nvram");
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( senterp )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Move")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Multi Move")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Hint")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Time")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Library/Clearboard")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_C) PORT_NAME("Sound/Color")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Enter Position")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")

	PORT_START("IN.2")
	PORT_BIT(0x3f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_CUSTOM) // freq sel
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_CUSTOM) // "

	PORT_START("POWER") // needs to be triggered for nvram to work
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(senterp_state::power_off), 0)
INPUT_PORTS_END

static INPUT_PORTS_START( senterpc )
	PORT_INCLUDE( senterp )

	PORT_MODIFY("IN.1")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Replay")

	PORT_MODIFY("IN.2")
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void senterp_state::senterp(machine_config &config)
{
	// basic machine hardware
	HD6301Y0(config, m_maincpu, 8_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &senterp_state::main_map);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(hd6301y0_cpu_device::nvram_set_battery));
	m_maincpu->standby_cb().append([this](int state) { if (state) m_display->clear(); });
	m_maincpu->in_p2_cb().set(FUNC(senterp_state::input1_r));
	m_maincpu->out_p2_cb().set(FUNC(senterp_state::leds_w));
	m_maincpu->out_p2_cb().append(m_dac, FUNC(dac_1bit_device::write)).bit(2);
	m_maincpu->in_p5_cb().set(FUNC(senterp_state::input2_r));
	m_maincpu->out_p6_cb().set(FUNC(senterp_state::mux_w));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(3, 8);
	config.set_default_layout(layout_cxg_senterprise);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void senterpc_state::senterpc(machine_config &config)
{
	senterp(config);

	// basic machine hardware
	m_maincpu->standby_cb().append(m_lcd, FUNC(lc7580_device::inh_w));
	m_maincpu->out_p2_cb().set(FUNC(senterpc_state::leds_w));
	m_maincpu->out_p2_cb().append(FUNC(senterpc_state::lcd_w));

	// video hardware
	LC7580(config, m_lcd, 0);
	m_lcd->write_segs().set(FUNC(senterpc_state::lcd_output_w));

	config.set_default_layout(layout_cxg_senterprisec);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( senterp )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("1985_210_newcrest_hd6301y0a14p", 0x0000, 0x4000, CRC(871719c8) SHA1(8c0f5bef2573b9cbebe87be3a899fec6308603be) )
ROM_END

ROM_START( senterpc )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("1986_210c_cxg_systems_hd6301y0b27p", 0x0000, 0x4000, CRC(5bb67dd6) SHA1(753c33643a5c45e899d0f4743d3ccf7a0728bd48) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1986, senterp,  0,       0,      senterp,  senterp,  senterp_state,  empty_init, "CXG Systems / Newcrest Technology / LogiSoft", "Super Enterprise (model 210)", MACHINE_SUPPORTS_SAVE )
SYST( 1986, senterpc, senterp, 0,      senterpc, senterpc, senterpc_state, empty_init, "CXG Systems / Newcrest Technology / LogiSoft", "Super Enterprise (model 210.C)", MACHINE_SUPPORTS_SAVE )



sexpert.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Novag Super Expert (model 878/886/887/902) / Novag Super Forte (model 879/?/901)

Hardware notes (Super Expert)
- R65C02P4 or W65C802P-6 @ 5MHz/6MHz (10MHz/12MHz XTAL)
- 8KB RAM battery-backed, 3*32KB ROM
- HD44780A00 LCD controller (16x1)
- beeper(32KHz/32), IRQ(32KHz/128) via MC14060
- optional R65C51P2 ACIA @ 1.8432MHz, for Super System (only works in version C)
- printer port, magnetic sensors, 8*8 chessboard leds

I/O via TTL, hardware design was very awkward.

Super Forte is very similar, just a cheaper plastic case and chessboard buttons
instead of magnet sensors.

To distinguish between versions, press the Set Level button.
- version A: no selectivity setting (but can turn on/off with level H8)
- version B: default selectivity 3
- version C: default selectivity 5

Note that the H8 option doesn't appear to work with sexperta2, but when doing a
hex compare with sexperta, the program differences are minor.

To identify C program version, start MAME with -rs232 terminal, and afterwards,
input I followed by Enter.

TODO:
- use W65C802 device for version B/C? it works ok but this cpu core emulation is
  not as accurate, and the program doesn't enable extended mode (in other words,
  it always runs in W65C02 emulation mode)

*******************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/m6502/w65c02.h"
#include "machine/clock.h"
#include "machine/mos6551.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/beep.h"
#include "video/hd44780.h"
#include "video/pwm.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

// internal artwork
#include "novag_sexpert.lh"
#include "novag_sforte.lh"


namespace {

// Super Expert / shared

class sexpert_state : public driver_device
{
public:
	sexpert_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_rombank(*this, "rombank"),
		m_screen(*this, "screen"),
		m_display(*this, "display"),
		m_lcd(*this, "hd44780"),
		m_board(*this, "board"),
		m_acia(*this, "acia"),
		m_rs232(*this, "rs232"),
		m_beeper(*this, "beeper"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// machine configs
	void sexpert(machine_config &config);
	void sexpertb(machine_config &config);

	void init_sexpert();

	DECLARE_INPUT_CHANGED_MEMBER(change_cpu_freq);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_memory_bank m_rombank;
	required_device<screen_device> m_screen;
	required_device<pwm_display_device> m_display;
	required_device<hd44780_device> m_lcd;
	required_device<sensorboard_device> m_board;
	required_device<mos6551_device> m_acia;
	required_device<rs232_port_device> m_rs232;
	required_device<beep_device> m_beeper;
	required_ioport_array<8> m_inputs;

	u8 m_inp_mux = 0;
	u8 m_lcd_control = 0;
	u8 m_lcd_data = 0;

	// address maps
	void sexpert_map(address_map &map) ATTR_COLD;

	// I/O handlers
	virtual void lcd_control_w(u8 data);
	virtual void lcd_data_w(u8 data);
	void leds_w(u8 data);
	void mux_w(u8 data);
	u8 input1_r();
	u8 input2_r();

	HD44780_PIXEL_UPDATE(lcd_pixel_update);
	void lcd_palette(palette_device &palette) const;
};

void sexpert_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_lcd_control));
	save_item(NAME(m_lcd_data));
}

INPUT_CHANGED_MEMBER(sexpert_state::change_cpu_freq)
{
	// machines were released with either 5MHz or 6MHz CPU
	m_maincpu->set_unscaled_clock((newval & 1) ? (12_MHz_XTAL/2) : (10_MHz_XTAL/2));
}

void sexpert_state::machine_reset()
{
	m_rombank->set_entry(0);
}

void sexpert_state::init_sexpert()
{
	m_rombank->configure_entries(0, 2, memregion("maincpu")->base() + 0x8000, 0x8000);
}


// Super Forte

class sforte_state : public sexpert_state
{
public:
	sforte_state(const machine_config &mconfig, device_type type, const char *tag) :
		sexpert_state(mconfig, type, tag)
	{ }

	// machine configs
	void sforte(machine_config &config);
	void sforteb(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	emu_timer *m_beeptimer = nullptr;

	// address maps
	void sforte_map(address_map &map) ATTR_COLD;

	// I/O handlers
	virtual void lcd_control_w(u8 data) override;
	virtual void lcd_data_w(u8 data) override;

	TIMER_CALLBACK_MEMBER(beep) { m_beeper->set_state(param); }
};

void sforte_state::machine_start()
{
	sexpert_state::machine_start();
	m_beeptimer = timer_alloc(FUNC(sforte_state::beep), this);
}



/*******************************************************************************
    I/O
*******************************************************************************/

// LCD

void sexpert_state::lcd_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(0xff, 0xff, 0xff)); // background
	palette.set_pen_color(1, rgb_t(0x00, 0x00, 0x00)); // lcd pixel on
	palette.set_pen_color(2, rgb_t(0xe8, 0xe8, 0xe8)); // lcd pixel off
}

HD44780_PIXEL_UPDATE(sexpert_state::lcd_pixel_update)
{
	// char size is 5x8
	if (x > 4 || y > 7)
		return;

	if (line < 2 && pos < 8)
	{
		// internal: (8+8)*1, external: 1*16
		bitmap.pix(1 + y, 1 + line*8*6 + pos*6 + x) = state ? 1 : 2;
	}
}


// common

void sexpert_state::lcd_control_w(u8 data)
{
	// d0: HD44780 RS
	// d1: HD44780 R/W
	// d2: HD44780 E
	if (m_lcd_control & ~data & 4 && ~data & 2)
		m_lcd->write(m_lcd_control & 1, m_lcd_data);
	m_lcd_control = data & 7;
}

void sexpert_state::lcd_data_w(u8 data)
{
	// d0-d7: HD44780 data
	m_lcd_data = data;
}

void sexpert_state::leds_w(u8 data)
{
	// d0-d7: chessboard leds
	m_display->write_mx(data);
}

void sexpert_state::mux_w(u8 data)
{
	// d0: rom bankswitch
	m_rombank->set_entry(data & 1);

	// d3: enable beeper
	m_beeper->set_state(BIT(data, 3));

	// d4-d7: 74145 to input mux/led select
	m_inp_mux = 1 << (data >> 4 & 0xf) & 0xff;
	m_display->write_my(m_inp_mux);
}

u8 sexpert_state::input1_r()
{
	u8 data = 0;

	// d0-d7: multiplexed inputs (chessboard squares)
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_rank(i, true);

	return ~data;
}

u8 sexpert_state::input2_r()
{
	u8 data = 0;

	// d0-d2: printer port

	// d5-d7: multiplexed inputs (side panel)
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read() << 5;

	return ~data & 0xe0;
}


// sforte-specific

void sforte_state::lcd_control_w(u8 data)
{
	// d3: rom bankswitch
	m_rombank->set_entry(data >> 3 & 1);

	// LCD pins: same as sexpert
	sexpert_state::lcd_control_w(data);
	lcd_data_w(m_lcd_data); // refresh inp mux
}

void sforte_state::lcd_data_w(u8 data)
{
	// d0-d2: 74145 to input mux/led select
	// 74145 D from lcd control d2 (HD44780 E)
	m_inp_mux = 1 << ((m_lcd_control << 1 & 8) | (data & 7));
	m_display->write_my(m_inp_mux);

	// d5,d6: led data
	m_display->write_mx(~data >> 5 & 3);

	// d7: enable beeper
	// capacitor for noise filter (sound glitches otherwise)
	if (m_beeptimer->param() != BIT(data, 7))
		m_beeptimer->adjust(attotime::from_msec(1), BIT(data, 7));

	// LCD pins: same as sexpert
	sexpert_state::lcd_data_w(data);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void sexpert_state::sexpert_map(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("nvram");
	map(0x1ff0, 0x1fff).unmaprw(); // 8KB RAM, but RAM CE pin is deactivated on $1ff0-$1fff
	map(0x1ff0, 0x1ff0).r(FUNC(sexpert_state::input1_r));
	map(0x1ff1, 0x1ff1).r(FUNC(sexpert_state::input2_r));
	map(0x1ff2, 0x1ff2).nopw(); // printer
	map(0x1ff3, 0x1ff3).nopw(); // printer
	map(0x1ff4, 0x1ff4).w(FUNC(sexpert_state::leds_w));
	map(0x1ff5, 0x1ff5).w(FUNC(sexpert_state::mux_w));
	map(0x1ff6, 0x1ff6).w(FUNC(sexpert_state::lcd_control_w));
	map(0x1ff7, 0x1ff7).w(FUNC(sexpert_state::lcd_data_w));
	map(0x1ffc, 0x1fff).rw("acia", FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x2000, 0x7fff).rom();
	map(0x8000, 0xffff).bankr("rombank");
}

void sforte_state::sforte_map(address_map &map)
{
	sexpert_map(map);
	map(0x1ff4, 0x1ff5).nopw();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( sexpert )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Go")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Take Back / Analyze Games")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Right")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Set Level")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Flip Display / Time Control")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Left")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Hint / Next Best")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Priority / Tournament Book / Pawn")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Yes/Start / Start of Game")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Trace Forward / AutoPlay")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Pro-Op / Restore Game / Rook")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("No/End / End of Game")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("Clear Board / Delete Pro-Op")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Best Move/Random / Review / Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Print Book / Store Game")

	PORT_START("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Change Color")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Sound / Info / Bishop")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Print Moves / Print Evaluations")

	PORT_START("IN.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_J) PORT_NAME("Verify/Set Up / Pro-Op Book/Both Books")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Solve Mate / Infinite / Queen")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Print List / Acc. Time")

	PORT_START("IN.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_K) PORT_NAME("New Game")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Player/Player / Gambit Book / King")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Print Board / Interface")

	PORT_START("CPU")
	PORT_CONFNAME( 0x01, 0x00, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sexpert_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "5MHz" )
	PORT_CONFSETTING(    0x01, "6MHz" )
INPUT_PORTS_END

static INPUT_PORTS_START( sexpertb )
	PORT_INCLUDE( sexpert )

	PORT_MODIFY("CPU") // default CPU for B/C is W65C802P-6 @ 6MHz
	PORT_CONFNAME( 0x01, 0x01, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sexpert_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "5MHz" )
	PORT_CONFSETTING(    0x01, "6MHz" )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void sexpert_state::sexpert(machine_config &config)
{
	// basic machine hardware
	W65C02(config, m_maincpu, 10_MHz_XTAL/2); // or 12_MHz_XTAL/2, also seen with R65C02
	m_maincpu->set_addrmap(AS_PROGRAM, &sexpert_state::sexpert_map);

	auto &irq_clock(CLOCK(config, "irq_clock", 32.768_kHz_XTAL/128)); // 256Hz
	irq_clock.set_pulse_width(attotime::from_nsec(21500)); // active for 21.5us
	irq_clock.signal_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(200));
	m_board->set_nvram_enable(true);

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60); // arbitrary
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	m_screen->set_size(6*16+1, 10);
	m_screen->set_visarea_full();
	m_screen->set_screen_update("hd44780", FUNC(hd44780_device::screen_update));
	m_screen->set_palette("palette");

	PALETTE(config, "palette", FUNC(sexpert_state::lcd_palette), 3);

	HD44780(config, m_lcd, 270'000); // OSC = 91K resistor
	m_lcd->set_lcd_size(2, 8);
	m_lcd->set_pixel_update_cb(FUNC(sexpert_state::lcd_pixel_update));

	PWM_DISPLAY(config, m_display).set_size(8, 8);
	config.set_default_layout(layout_novag_sexpert);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 32.768_kHz_XTAL/32); // 1024Hz
	m_beeper->add_route(ALL_OUTPUTS, "mono", 0.25);

	// uart (configure after video)
	MOS6551(config, m_acia).set_xtal(1.8432_MHz_XTAL); // R65C51P2 - RTS to CTS, DCD to GND
	m_acia->irq_handler().set_inputline("maincpu", w65c02_device::NMI_LINE);
	m_acia->rts_handler().set("acia", FUNC(mos6551_device::write_cts));
	m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_acia->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set("acia", FUNC(mos6551_device::write_rxd));
	m_rs232->dsr_handler().set("acia", FUNC(mos6551_device::write_dsr));
}

void sexpert_state::sexpertb(machine_config &config)
{
	sexpert(config);
	m_maincpu->set_clock(12_MHz_XTAL/2);
}

void sforte_state::sforte(machine_config &config)
{
	sexpert(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &sforte_state::sforte_map);
	subdevice<clock_device>("irq_clock")->set_pulse_width(attotime::from_usec(10)); // measured between 8us and 12us (unstable)

	m_board->set_type(sensorboard_device::BUTTONS);

	m_display->set_width(2);
	config.set_default_layout(layout_novag_sforte);
}

void sforte_state::sforteb(machine_config &config)
{
	sforte(config);
	m_maincpu->set_clock(12_MHz_XTAL/2);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( sexperta )
	ROM_REGION( 0x18000, "maincpu", 0 )
	ROM_LOAD("se_lo_608.u3",     0x00000, 0x8000, CRC(5c98264c) SHA1(fbbe0d0cf64944fd3a90a7e9711b1deef8b9b51d) )
	ROM_LOAD("se_hi1_608.u1",    0x08000, 0x8000, CRC(68009cb4) SHA1(ae8d1b5058eff72d3fcfd6a011608ae7b3de5060) )
	ROM_LOAD("se_sf_hi0_c22.u2", 0x10000, 0x8000, CRC(3e42cf7c) SHA1(b2faa36a127e08e5755167a25ed4a07f12d62957) )
ROM_END

ROM_START( sexperta1 )
	ROM_REGION( 0x18000, "maincpu", 0 )
	ROM_LOAD("se_202_l.u3",  0x00000, 0x8000, CRC(d51fed16) SHA1(01aaddab36a721a4b9efde94979256d397cd1953) ) // NEC D27C256AD-12
	ROM_LOAD("se_202_h1.u1", 0x08000, 0x8000, CRC(933eafa8) SHA1(293f63a5bc7d760ad675522c98f9f0a49e61aef5) ) // "
	ROM_LOAD("se_c22_h0.u2", 0x10000, 0x8000, CRC(3e42cf7c) SHA1(b2faa36a127e08e5755167a25ed4a07f12d62957) ) // "
ROM_END

ROM_START( sexperta2 )
	ROM_REGION( 0x18000, "maincpu", 0 )
	ROM_LOAD("se_lo_b15.u3",     0x00000, 0x8000, CRC(6cc9527c) SHA1(29bab809399f2863a88a9c41535ecec0a4fd65ea) )
	ROM_LOAD("se_hi1_b15.u1",    0x08000, 0x8000, CRC(6e57f0c0) SHA1(ea44769a6f54721fd4543366bda932e86e497d43) )
	ROM_LOAD("se_sf_hi0_a23.u2", 0x10000, 0x8000, CRC(7d4e1528) SHA1(53c7d458a5571afae402f00ae3d0f5066634b068) )
ROM_END

ROM_START( sexpertb )
	ROM_REGION( 0x18000, "maincpu", 0 )
	ROM_LOAD("se_lo_619.u3",    0x00000, 0x8000, CRC(92002eb6) SHA1(ed8ca16701e00b48fa55c856fa4a8c6613079c02) )
	ROM_LOAD("se_hi1_619.u1",   0x08000, 0x8000, CRC(814b4420) SHA1(c553e6a8c048dcc1cf48d410111a86e06b99d356) )
	ROM_LOAD("se_f_hi0_605.u2", 0x10000, 0x8000, CRC(bb07ad52) SHA1(30cf9005021ab2d7b03facdf2d3588bc94dc68a6) )
ROM_END

ROM_START( sexpertc ) // ID = E3.6
	ROM_REGION( 0x18000, "maincpu", 0 )
	ROM_LOAD("se_lo_v3.6.u3", 0x00000, 0x8000, CRC(5a29105e) SHA1(be37bb29b530dbba847a5e8d27d81b36525e47f7) )
	ROM_LOAD("se_hi1.u1",     0x08000, 0x8000, CRC(0085c2c4) SHA1(d84bf4afb022575db09dd9dc12e9b330acce35fa) )
	ROM_LOAD("se_hi0.u2",     0x10000, 0x8000, CRC(2d085064) SHA1(76162322aa7d23a5c07e8356d0bbbb33816419af) )
ROM_END

ROM_START( sexpertc1 ) // ID = E3.0
	ROM_REGION( 0x18000, "maincpu", 0 )
	ROM_LOAD("se_c24_l.u3",  0x00000, 0x8000, CRC(43ed7a9e) SHA1(273c485e5be6b107b6c5c448003ba7686d4a6d06) )
	ROM_LOAD("se_c23_h1.u1", 0x08000, 0x8000, CRC(0085c2c4) SHA1(d84bf4afb022575db09dd9dc12e9b330acce35fa) )
	ROM_LOAD("se_c22_h0.u2", 0x10000, 0x8000, CRC(2d085064) SHA1(76162322aa7d23a5c07e8356d0bbbb33816419af) )
ROM_END

ROM_START( sexpertc2 ) // ID = E1.2
	ROM_REGION( 0x18000, "maincpu", 0 )
	ROM_LOAD("e_111_black.u3", 0x00000, 0x8000, CRC(d0f65341) SHA1(e8ebbfdbcf8ad613cc68acdb0db011eed855cb9f) ) // Toshiba TC57256AD-12
	ROM_LOAD("e_111_red.u1",   0x08000, 0x8000, CRC(59dc112b) SHA1(e1031648da8fc9479d1134d3fd205af254610c1d) ) // "
	ROM_LOAD("c26_green.u2",   0x10000, 0x8000, CRC(c6a1419a) SHA1(017a0ffa9aa59438c879624a7ddea2071d1524b8) ) // "
ROM_END


ROM_START( sfortea )
	ROM_REGION( 0x18000, "maincpu", 0 )
	ROM_LOAD("sf_lo_609.u13",    0x00000, 0x8000, CRC(88138075) SHA1(bc1f3a2829f7299c81b48202c651cde9b8831157) )
	ROM_LOAD("sf_hi1_609.u11",   0x08000, 0x8000, CRC(ccd35d09) SHA1(9101cbdecdec00aa4de6d72c96ecdffbcf3359f6) )
	ROM_LOAD("se_f_hi0_c22.u12", 0x10000, 0x8000, CRC(3e42cf7c) SHA1(b2faa36a127e08e5755167a25ed4a07f12d62957) )
ROM_END

ROM_START( sfortea1 )
	ROM_REGION( 0x18000, "maincpu", 0 )
	ROM_LOAD("sfa_lo_204.u13",  0x00000, 0x8000, CRC(78734bfd) SHA1(b6d8e9efccee6f6d0b0cd257a82162bf8ccec719) )
	ROM_LOAD("sfa_hi1_204.u11", 0x08000, 0x8000, CRC(e5e84580) SHA1(bae55c3da7b720bf6ccfb450e383c53cebd5e9ef) )
	ROM_LOAD("sfa_hi0_c22.u12", 0x10000, 0x8000, CRC(3e42cf7c) SHA1(b2faa36a127e08e5755167a25ed4a07f12d62957) )
ROM_END

ROM_START( sfortea2 )
	ROM_REGION( 0x18000, "maincpu", 0 )
	ROM_LOAD("sfalo.u13",   0x00000, 0x8000, CRC(86e0230a) SHA1(0d6e18a17e636b8c7292c8f331349d361892d1a8) )
	ROM_LOAD("sfahi.u11",   0x08000, 0x8000, CRC(81c02746) SHA1(0bf68b68ade5a3263bead88da0a8965fc71483c1) )
	ROM_LOAD("sfabook.u12", 0x10000, 0x8000, CRC(3e42cf7c) SHA1(b2faa36a127e08e5755167a25ed4a07f12d62957) )
ROM_END

ROM_START( sforteb )
	ROM_REGION( 0x18000, "maincpu", 0 )
	ROM_LOAD("forte_b_lo.u13",  0x00000, 0x8000, CRC(48bfe5d6) SHA1(323642686b6d2fb8db2b7d50c6cd431058078ce1) )
	ROM_LOAD("forte_b_hi1.u11", 0x08000, 0x8000, CRC(9778ca2c) SHA1(d8b88b9768a1a9171c68cbb0892b817d68d78351) )
	ROM_LOAD("forte_b_hi0.u12", 0x10000, 0x8000, CRC(bb07ad52) SHA1(30cf9005021ab2d7b03facdf2d3588bc94dc68a6) )
ROM_END

ROM_START( sfortec ) // ID = F3.6
	ROM_REGION( 0x18000, "maincpu", 0 )
	ROM_LOAD("sfl_124.u13", 0x00000, 0x8000, CRC(c0cba797) SHA1(c630c3552178854a46275c18b0741b0ad1ae3c75) ) // Toshiba TC57256AD-12
	ROM_LOAD("sfh_b15.u11", 0x08000, 0x8000, CRC(e129ec69) SHA1(76feb0d3110a1c7746233cd89c0b2aaae9d0e427) ) // "
	ROM_LOAD("sef_b07.u12", 0x10000, 0x8000, CRC(2d085064) SHA1(76162322aa7d23a5c07e8356d0bbbb33816419af) ) // NEC D27C256AD-12
ROM_END

ROM_START( sfortec1 ) // ID = F1.2
	ROM_REGION( 0x18000, "maincpu", 0 )
	ROM_LOAD("sfl_c_111.u13", 0x00000, 0x8000, CRC(f040cf30) SHA1(1fc1220b8ed67cdffa3866d230ce001721cf684f) ) // Toshiba TC57256AD-12
	ROM_LOAD("sfh_c_111.u11", 0x08000, 0x8000, CRC(0f926b32) SHA1(9c7270ecb3f41dd9172a9a7928e6e04e64b2a340) ) // NEC D27C256AD-12
	ROM_LOAD("h0_c_c26.u12",  0x10000, 0x8000, CRC(c6a1419a) SHA1(017a0ffa9aa59438c879624a7ddea2071d1524b8) ) // Toshiba TC57256AD-12
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT    COMPAT  MACHINE   INPUT     CLASS          INIT          COMPANY, FULLNAME, FLAGS
SYST( 1988, sexperta,  0,        0,      sexpert,  sexpert,  sexpert_state, init_sexpert, "Novag Industries / Intelligent Heuristic Programming", "Super Expert (version A, set 1)", MACHINE_SUPPORTS_SAVE ) // 886
SYST( 1987, sexperta1, sexperta, 0,      sexpert,  sexpert,  sexpert_state, init_sexpert, "Novag Industries / Intelligent Heuristic Programming", "Super Expert (version A, set 2)", MACHINE_SUPPORTS_SAVE ) // 878
SYST( 1987, sexperta2, sexperta, 0,      sexpert,  sexpert,  sexpert_state, init_sexpert, "Novag Industries / Intelligent Heuristic Programming", "Super Expert (version A, set 3)", MACHINE_SUPPORTS_SAVE ) // 878
SYST( 1988, sexpertb,  sexperta, 0,      sexpertb, sexpertb, sexpert_state, init_sexpert, "Novag Industries / Intelligent Heuristic Programming", "Super Expert (version B)", MACHINE_SUPPORTS_SAVE ) // 887
SYST( 1990, sexpertc,  sexperta, 0,      sexpertb, sexpertb, sexpert_state, init_sexpert, "Novag Industries / Intelligent Heuristic Programming", "Super Expert (version C, v3.6)", MACHINE_SUPPORTS_SAVE ) // 902
SYST( 1990, sexpertc1, sexperta, 0,      sexpertb, sexpertb, sexpert_state, init_sexpert, "Novag Industries / Intelligent Heuristic Programming", "Super Expert (version C, v3.0)", MACHINE_SUPPORTS_SAVE ) // 902
SYST( 1990, sexpertc2, sexperta, 0,      sexpertb, sexpertb, sexpert_state, init_sexpert, "Novag Industries / Intelligent Heuristic Programming", "Super Expert (version C, v1.2)", MACHINE_SUPPORTS_SAVE ) // 902

SYST( 1987, sfortea,   0,        0,      sforte,   sexpert,  sforte_state,  init_sexpert, "Novag Industries / Intelligent Heuristic Programming", "Super Forte (version A, set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1987, sfortea1,  sfortea,  0,      sforte,   sexpert,  sforte_state,  init_sexpert, "Novag Industries / Intelligent Heuristic Programming", "Super Forte (version A, set 2)", MACHINE_SUPPORTS_SAVE )
SYST( 1987, sfortea2,  sfortea,  0,      sforte,   sexpert,  sforte_state,  init_sexpert, "Novag Industries / Intelligent Heuristic Programming", "Super Forte (version A, set 3)", MACHINE_SUPPORTS_SAVE )
SYST( 1988, sforteb,   sfortea,  0,      sforteb,  sexpertb, sforte_state,  init_sexpert, "Novag Industries / Intelligent Heuristic Programming", "Super Forte (version B)", MACHINE_SUPPORTS_SAVE )
SYST( 1990, sfortec,   sfortea,  0,      sforteb,  sexpertb, sforte_state,  init_sexpert, "Novag Industries / Intelligent Heuristic Programming", "Super Forte (version C, v3.6)", MACHINE_SUPPORTS_SAVE )
SYST( 1990, sfortec1,  sfortea,  0,      sforteb,  sexpertb, sforte_state,  init_sexpert, "Novag Industries / Intelligent Heuristic Programming", "Super Forte (version C, v1.2)", MACHINE_SUPPORTS_SAVE )



sg1000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

Sega SG-1000

PCB Layout
----------

171-5078 (C) SEGA 1983
171-5046 REV. A (C) SEGA 1983

|---------------------------|                              |----------------------------|
|   SW1     CN2             |   |------|---------------|   |    SW2     CN4             |
|                           |---|         CN3          |---|                            |
|  CN1                                                                              CN5 |
|                                                                                       |
|   10.738635MHz            |------------------------------|                7805        |
|   |---|                   |------------------------------|                            |
|   |   |                                 CN6                                           |
|   | 9 |                                                                               |
|   | 9 |                                                                       LS32    |
|   | 1 |       |---------|                                                             |
|   | 8 |       | TMM2009 |                                                     LS139   |
|   | A |       |---------|             |------------------|                            |
|   |   |                               |       Z80        |                            |
|   |---|                               |------------------|                            |
|                                                                                       |
|                                                                                       |
|       MB8118  MB8118  MB8118  MB8118              SN76489A            SW3             |
|           MB8118  MB8118  MB8118  MB8118                          LS257   LS257       |
|---------------------------------------------------------------------------------------|

Notes:
    All IC's shown.

    Z80     - NEC D780C-1 / Zilog Z8400A (REV.A) Z80A CPU @ 3.579545
    TMS9918A- Texas Instruments TMS9918ANL Video Display Processor @ 10.738635MHz
    MB8118  - Fujitsu MB8118-12 16K x 1 Dynamic RAM
    TMM2009 - Toshiba TMM2009P-A / TMM2009P-B (REV.A)
    SN76489A- Texas Instruments SN76489AN Digital Complex Sound Generator @ 3.579545
    CN1     - player 1 joystick connector
    CN2     - RF video connector
    CN3     - keyboard connector
    CN4     - power connector (+9VDC)
    CN5     - player 2 joystick connector
    CN6     - cartridge connector
    SW1     - TV channel select switch
    SW2     - power switch
    SW3     - hold switch

*/

/*

    TODO:

    - SC-3000 return instruction referenced by R when reading ports 60-7f,e0-ff
    - connect PSG /READY signal to Z80 WAIT
    - accurate video timing
    - SH-400 racing controller
    - SF-7000 serial comms

*/


#include "emu.h"

#include "bus/centronics/ctronics.h"
#include "bus/rs232/rs232.h"
#include "bus/sega8/sega8_slot.h"
#include "bus/sg1000_exp/sg1000exp.h"
#include "bus/sms_ctrl/controllers.h"
#include "bus/sms_ctrl/smsctrl.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/upd765.h"
#include "sound/sn76496.h"
#include "video/tms9928a.h"

#include "crsshair.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/sf7000_dsk.h"


namespace {

#define Z80_TAG         "z80"
#define SN76489AN_TAG   "sn76489an"
#define UPD765_TAG      "upd765"
#define UPD8251_TAG     "upd8251"
#define UPD9255_TAG     "upd9255"
#define UPD9255_1_TAG   "upd9255_1" // "upd9255_0" is being used by sk1100 device
#define TMS9918A_TAG    "tms9918a"
#define RS232_TAG       "rs232"

class sg1000_state_base : public driver_device
{
public:
	sg1000_state_base(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, Z80_TAG),
		m_rom(*this, Z80_TAG),
		m_cart(*this, "slot"),
		m_sgexpslot(*this, "sgexp")
	{ }

protected:
	required_device<cpu_device> m_maincpu;
	required_memory_region m_rom;
	optional_device<sega8_cart_slot_device> m_cart;
	required_device<sg1000_expansion_slot_device> m_sgexpslot;

	void sg1000_base(machine_config &config);
};

class sg1000_state : public sg1000_state_base
{
public:
	sg1000_state(const machine_config &mconfig, device_type type, const char *tag) :
		sg1000_state_base(mconfig, type, tag),
		m_ctrlports(*this, "ctrl%u", 1U)
	{ }

	void sg1000(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER( trigger_nmi );

protected:
	required_device_array<sms_control_port_device, 2> m_ctrlports;

	virtual void machine_start() override ATTR_COLD;

	uint8_t peripheral_r(offs_t offset);
	void peripheral_w(offs_t offset, uint8_t data);

	void sg1000_map(address_map &map) ATTR_COLD;
	void sg1000_io_map(address_map &map) ATTR_COLD;
	void sc3000_map(address_map &map) ATTR_COLD;
	void sc3000_io_map(address_map &map) ATTR_COLD;
};

class omv_state : public sg1000_state_base
{
public:
	omv_state(const machine_config &mconfig, device_type type, const char *tag) :
		sg1000_state_base(mconfig, type, tag)
	{ }

	void omv1000(machine_config &config);

private:
	uint8_t omv_r(offs_t offset);
	void omv_w(offs_t offset, uint8_t data);

	void omv_map(address_map &map) ATTR_COLD;
	void omv_io_map(address_map &map) ATTR_COLD;
};

class omv2000_state : public omv_state
{
public:
	omv2000_state(const machine_config &mconfig, device_type type, const char *tag) :
		omv_state(mconfig, type, tag),
		m_ctrl2(*this, "ctrl2")
	{ }

	void omv2000(machine_config &config);

	template <unsigned Shift> ioport_value ctrl2_r();

private:
	required_device<sms_control_port_device> m_ctrl2;

	virtual void machine_start() override ATTR_COLD;
};

class sc3000_state : public sg1000_state
{
public:
	sc3000_state(const machine_config &mconfig, device_type type, const char *tag) :
		sg1000_state(mconfig, type, tag)
	{ }

	void sc3000(machine_config &config);
	void sc3000pal(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

	void sc3000_base(machine_config &config);
};

class sf7000_state : public sc3000_state
{
public:
	sf7000_state(const machine_config &mconfig, device_type type, const char *tag) :
		sc3000_state(mconfig, type, tag),
		m_fdc(*this, UPD765_TAG),
		m_centronics(*this, "centronics"),
		m_floppy0(*this, UPD765_TAG ":0:3ssdd"),
		m_romview(*this, "romview")
	{ }

	void sf7000(machine_config &config);

private:
	required_device<upd765a_device> m_fdc;
	required_device<centronics_device> m_centronics;
	required_device<floppy_image_device> m_floppy0;

	memory_view m_romview;

	int m_centronics_busy = 0;

	virtual void machine_start() override ATTR_COLD;

	void write_centronics_busy(int state);
	uint8_t ppi_pa_r();
	void ppi_pc_w(uint8_t data);

	void sf7000_io_map(address_map &map) ATTR_COLD;
	void sf7000_map(address_map &map) ATTR_COLD;

	static void floppy_formats(format_registration &fr);
};

/***************************************************************************
    READ/WRITE HANDLERS
***************************************************************************/

// TODO: not sure if the OMV bios actually detects the presence of a cart,
// or if the cart data simply overwrites the internal BIOS...
// for the moment let assume the latter!
uint8_t omv_state::omv_r(offs_t offset)
{
	if (m_cart && m_cart->exists())
		return m_cart->read_cart(offset);
	else
		return m_rom->base()[offset];
}

void omv_state::omv_w(offs_t offset, uint8_t data)
{
	if (m_cart && m_cart->exists())
		m_cart->write_cart(offset, data);
}

uint8_t sg1000_state::peripheral_r(offs_t offset)
{
	bool joy_ports_disabled = m_sgexpslot->is_readable(offset);

	if (joy_ports_disabled)
		return m_sgexpslot->read(offset);
	else if (offset & 0x01)
		return BIT(m_ctrlports[1]->in_r(), 2, 4) | (0xf0 & m_sgexpslot->read(offset));
	else
		return BIT(m_ctrlports[0]->in_r(), 0, 6) | (BIT(m_ctrlports[1]->in_r(), 0, 2) << 6);
}

void sg1000_state::peripheral_w(offs_t offset, uint8_t data)
{
	bool joy_ports_disabled = m_sgexpslot->is_writeable(offset);

	if (joy_ports_disabled)
	{
		m_sgexpslot->write(offset, data);
	}
}

/*-------------------------------------------------
    ADDRESS_MAP( sg1000_map )
-------------------------------------------------*/

void sg1000_state::sg1000_map(address_map &map)
{
	map(0x0000, 0xbfff).rw(m_cart, FUNC(sega8_cart_slot_device::read_cart), FUNC(sega8_cart_slot_device::write_cart));
	map(0xc000, 0xc3ff).mirror(0x3c00).ram();
}

/*-------------------------------------------------
    ADDRESS_MAP( sg1000_io_map )
-------------------------------------------------*/

void sg1000_state::sg1000_io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x40, 0x40).mirror(0x3f).w(SN76489AN_TAG, FUNC(sn76489a_device::write));
	map(0x80, 0x81).mirror(0x3e).rw(TMS9918A_TAG, FUNC(tms9918a_device::read), FUNC(tms9918a_device::write));
	map(0xdc, 0xdf).rw(FUNC(sg1000_state::peripheral_r), FUNC(sg1000_state::peripheral_w));
}

/*-------------------------------------------------
    ADDRESS_MAP( omv_map )
-------------------------------------------------*/

void omv_state::omv_map(address_map &map)
{
	map(0x0000, 0xbfff).rw(FUNC(omv_state::omv_r), FUNC(omv_state::omv_w));
	map(0xc000, 0xc7ff).mirror(0x3800).ram();
}

/*-------------------------------------------------
    ADDRESS_MAP( omv_io_map )
-------------------------------------------------*/

void omv_state::omv_io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x40, 0x40).mirror(0x3f).w(SN76489AN_TAG, FUNC(sn76489a_device::write));
	map(0x80, 0x81).mirror(0x3e).rw(TMS9918A_TAG, FUNC(tms9918a_device::read), FUNC(tms9918a_device::write));
	map(0xc0, 0xc0).mirror(0x38).portr("C0");
	map(0xc1, 0xc1).mirror(0x38).portr("C1");
	map(0xc2, 0xc2).mirror(0x38).portr("C2");
	map(0xc3, 0xc3).mirror(0x38).portr("C3");
	map(0xc4, 0xc4).mirror(0x3a).portr("C4");
	map(0xc5, 0xc5).mirror(0x3a).portr("C5");
}

/*-------------------------------------------------
    ADDRESS_MAP( sc3000_map )
-------------------------------------------------*/

void sg1000_state::sc3000_map(address_map &map)
{
	map(0x0000, 0xbfff).rw(m_cart, FUNC(sega8_cart_slot_device::read_cart), FUNC(sega8_cart_slot_device::write_cart));
	map(0xc000, 0xc7ff).mirror(0x3800).ram();
}

/*-------------------------------------------------
    ADDRESS_MAP( sc3000_io_map )
-------------------------------------------------*/

void sg1000_state::sc3000_io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0xff).rw(m_cart, FUNC(sega8_cart_slot_device::read_io), FUNC(sega8_cart_slot_device::write_io));
	map(0x7f, 0x7f).w(SN76489AN_TAG, FUNC(sn76489a_device::write));
	map(0xbe, 0xbf).rw(TMS9918A_TAG, FUNC(tms9918a_device::read), FUNC(tms9918a_device::write));
	map(0xdc, 0xdf).rw(FUNC(sg1000_state::peripheral_r), FUNC(sg1000_state::peripheral_w));
}

/* This is how the I/O ports are really mapped, but MAME does not support overlapping ranges
void sg1000_state::sc3000_io_map(address_map &map)
{
    map.global_mask(0xff);
    map(0x00, 0x00).mirror(0xdf).rw(UPD9255_TAG, FUNC(i8255_device::read), FUNC(i8255_device::write));
    map(0x00, 0x00).mirror(0x7f).w(SN76489AN_TAG, FUNC(sn76489a_device::write));
    map(0x00, 0x01).mirror(0xae).rw(TMS9918A_TAG, FUNC(tms9918a_device::read), FUNC(tms9918a_device::write));
    map(0x60, 0x60).mirror(0x9f).r(FUNC(sg1000_state::sc3000_r_r));
}
*/

/*-------------------------------------------------
    ADDRESS_MAP( sf7000_map )
-------------------------------------------------*/

void sf7000_state::sf7000_map(address_map &map)
{
	map(0x0000, 0xffff).ram();
	map(0x0000, 0x3fff).view(m_romview);
	m_romview[0](0x0000, 0x3fff).rom().region(Z80_TAG, 0);
}

/*-------------------------------------------------
    ADDRESS_MAP( sf7000_io_map )
-------------------------------------------------*/

void sf7000_state::sf7000_io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x7f, 0x7f).w(SN76489AN_TAG, FUNC(sn76489a_device::write));
	map(0xbe, 0xbf).rw(TMS9918A_TAG, FUNC(tms9918a_device::read), FUNC(tms9918a_device::write));
	map(0xdc, 0xdf).rw(FUNC(sf7000_state::peripheral_r), FUNC(sf7000_state::peripheral_w));
	map(0xe0, 0xe1).m(m_fdc, FUNC(upd765a_device::map));
	map(0xe4, 0xe7).rw(UPD9255_1_TAG, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xe8, 0xe9).rw(UPD8251_TAG, FUNC(i8251_device::read), FUNC(i8251_device::write));
}

/***************************************************************************
    INPUT PORTS
***************************************************************************/

/*-------------------------------------------------
    INPUT_CHANGED_MEMBER( trigger_nmi )
-------------------------------------------------*/

INPUT_CHANGED_MEMBER( sg1000_state::trigger_nmi )
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, newval ? CLEAR_LINE : ASSERT_LINE);
}

/*-------------------------------------------------
    ioport_value ctrl2_r()
-------------------------------------------------*/

template <unsigned Shift>
ioport_value omv2000_state::ctrl2_r()
{
	return m_ctrl2->in_r() >> Shift;
}

/*-------------------------------------------------
    INPUT_PORTS( sg1000 )
-------------------------------------------------*/

static INPUT_PORTS_START( sg1000 )
	PORT_START("NMI")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME(DEF_STR(Pause)) PORT_CODE(KEYCODE_1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sg1000_state::trigger_nmi), 0)
INPUT_PORTS_END

/*-------------------------------------------------
    INPUT_PORTS( omv )
-------------------------------------------------*/

static INPUT_PORTS_START( omv1000 )
	PORT_START("C0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("C1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("9 #") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("0 *") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("C2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("C3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("C4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("S-1")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("S-2")
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("C5")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( omv2000 )
	PORT_INCLUDE( omv1000 )

	PORT_MODIFY("C4")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(omv2000_state::ctrl2_r<0>));

	PORT_MODIFY("C5")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(omv2000_state::ctrl2_r<2>));
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

/*-------------------------------------------------
    INPUT_PORTS( sc3000 )
-------------------------------------------------*/

static INPUT_PORTS_START( sc3000 )
	// keyboard keys are added by the embedded sk1100 device

	PORT_START("NMI")
	// This is actually part of the main keyboard, though emulated separately here
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Reset") PORT_CODE(KEYCODE_F10) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sg1000_state::trigger_nmi), 0)
INPUT_PORTS_END

/*-------------------------------------------------
    INPUT_PORTS( sf7000 )
-------------------------------------------------*/

static INPUT_PORTS_START( sf7000 )
	PORT_INCLUDE( sc3000 )

	PORT_START("BAUD")
	PORT_CONFNAME( 0x07, 0x05, "Baud rate")
	PORT_CONFSETTING( 0x00, "9600 baud" )
	PORT_CONFSETTING( 0x01, "4800 baud" )
	PORT_CONFSETTING( 0x02, "2400 baud" )
	PORT_CONFSETTING( 0x03, "1200 baud" )
	PORT_CONFSETTING( 0x04, "600 baud" )
	PORT_CONFSETTING( 0x05, "300 baud" )
INPUT_PORTS_END

/***************************************************************************
    DEVICE CONFIGURATION
***************************************************************************/

/*-------------------------------------------------
    I8255 INTERFACE
-------------------------------------------------*/

void sf7000_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

uint8_t sf7000_state::ppi_pa_r()
{
	/*
	    Signal  Description

	    PA0     INT from FDC
	    PA1     BUSY from Centronics printer
	    PA2     INDEX from FDD
	    PA3
	    PA4
	    PA5
	    PA6
	    PA7
	*/

	uint8_t data = 0;

	data |= m_fdc->get_irq() ? 0x01 : 0x00;
	data |= m_centronics_busy << 1;
	data |= m_floppy0->idx_r() << 2;

	return data;
}

void sf7000_state::ppi_pc_w(uint8_t data)
{
	/*
	    Signal  Description

	    PC0     /INUSE signal to FDD (drives LED?)
	    PC1     /MOTOR ON signal to FDD
	    PC2     TC signal to FDC
	    PC3     RESET signal to FDC
	    PC4     not connected
	    PC5     not connected
	    PC6     /ROM SEL (switch between IPL ROM and RAM)
	    PC7     /STROBE to Centronics printer
	*/

	if (!BIT(data, 0))
		m_fdc->set_floppy(m_floppy0);
	else
		m_fdc->set_floppy(nullptr);

	/* floppy motor */
	m_floppy0->mon_w(BIT(data, 1));

	/* FDC terminal count */
	m_fdc->tc_w(BIT(data, 2));

	/* FDC reset */
	m_fdc->reset_w(BIT(data, 3));

	/* ROM selection */
	if (BIT(data, 6))
		m_romview.disable();
	else
		m_romview.select(0);

	/* printer strobe */
	m_centronics->write_strobe(BIT(data, 7));
}

/*-------------------------------------------------
    upd765_interface sf7000_upd765_interface
-------------------------------------------------*/

void sf7000_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_SF7000_FORMAT);
}

/*-------------------------------------------------
    floppy_interface sf7000_floppy_interface
-------------------------------------------------*/

static void sf7000_floppies(device_slot_interface &device)
{
	device.option_add("3ssdd", FLOPPY_3_SSDD);
}

/*-------------------------------------------------
    MACHINE_START( sg1000 )
-------------------------------------------------*/

void sg1000_state::machine_start()
{
	sg1000_state_base::machine_start();

	if (m_cart->get_type() == SEGA8_DAHJEE_TYPEA || m_cart->get_type() == SEGA8_DAHJEE_TYPEB)
	{
		m_maincpu->space(AS_PROGRAM).install_read_handler(0xc000, 0xffff, read8sm_delegate(*m_cart, FUNC(sega8_cart_slot_device::read_ram)));
		m_maincpu->space(AS_PROGRAM).install_write_handler(0xc000, 0xffff, write8sm_delegate(*m_cart, FUNC(sega8_cart_slot_device::write_ram)));
	}

	if (m_cart)
		m_cart->save_ram();

	m_ctrlports[0]->out_w(0x3f, 0x40);
	m_ctrlports[1]->out_w(0x3f, 0x40);
}

/*-------------------------------------------------
    MACHINE_START( omv2000 )
-------------------------------------------------*/

void omv2000_state::machine_start()
{
	omv_state::machine_start();

	// TODO: confirm wiring of pin 7
	m_ctrl2->out_w(0x3f, 0x40);
}

/*-------------------------------------------------
    MACHINE_START( sc3000 )
-------------------------------------------------*/

void sc3000_state::machine_start()
{
	sg1000_state_base::machine_start();

	if (m_cart && m_cart->exists() && (m_cart->get_type() == SEGA8_BASIC_L3 || m_cart->get_type() == SEGA8_MUSIC_EDITOR
								|| m_cart->get_type() == SEGA8_DAHJEE_TYPEA || m_cart->get_type() == SEGA8_DAHJEE_TYPEB
								|| m_cart->get_type() == SEGA8_MULTICART || m_cart->get_type() == SEGA8_MEGACART))
	{
		m_maincpu->space(AS_PROGRAM).install_read_handler(0xc000, 0xffff, read8sm_delegate(*m_cart, FUNC(sega8_cart_slot_device::read_ram)));
		m_maincpu->space(AS_PROGRAM).install_write_handler(0xc000, 0xffff, write8sm_delegate(*m_cart, FUNC(sega8_cart_slot_device::write_ram)));
	}

	if (m_cart)
		m_cart->save_ram();

	m_ctrlports[0]->out_w(0x7f, 0x00);
	m_ctrlports[1]->out_w(0x7f, 0x00);
}


/*-------------------------------------------------
    MACHINE_START( sf7000 )
-------------------------------------------------*/

void sf7000_state::machine_start()
{
	sc3000_state::machine_start();

	save_item(NAME(m_centronics_busy));
}

/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

void sg1000_state_base::sg1000_base(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 10.738635_MHz_XTAL / 3); // LH0080A

	/* video hardware */
	tms9918a_device &vdp(TMS9918A(config, TMS9918A_TAG, 10.738635_MHz_XTAL));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	vdp.int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	SN76489A(config, SN76489AN_TAG, 10.738635_MHz_XTAL / 3).add_route(ALL_OUTPUTS, "mono", 1.00);

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("sg1000");
}

void sc3000_state::sc3000_base(machine_config &config)
{
	sg1000_base(config);

	/* controller ports */
	SMS_CONTROL_PORT(config, m_ctrlports[0], sms_control_port_passive_devices, SMS_CTRL_OPTION_JOYPAD);
	SMS_CONTROL_PORT(config, m_ctrlports[1], sms_control_port_passive_devices, SMS_CTRL_OPTION_JOYPAD);

	/* sc3000 has all sk1100 features built-in, so add it as a fixed slot */
	SG1000_EXPANSION_SLOT(config, m_sgexpslot, sg1000_expansion_devices, "sk1100", true);

	/* the sk1100 device will add sc3000 cart and cass lists */
}

/*-------------------------------------------------
    machine_config( sg1000 )
-------------------------------------------------*/

void sg1000_state::sg1000(machine_config &config)
{
	sg1000_base(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &sg1000_state::sg1000_map);
	m_maincpu->set_addrmap(AS_IO, &sg1000_state::sg1000_io_map);

	/* controller ports */
	SMS_CONTROL_PORT(config, m_ctrlports[0], sms_control_port_passive_devices, SMS_CTRL_OPTION_JOYPAD).set_fixed(true);
	SMS_CONTROL_PORT(config, m_ctrlports[1], sms_control_port_passive_devices, SMS_CTRL_OPTION_JOYPAD);

	/* expansion slot */
	SG1000_EXPANSION_SLOT(config, m_sgexpslot, sg1000_expansion_devices, nullptr, false);

	/* cartridge */
	SG1000_CART_SLOT(config, m_cart, sg1000_cart, nullptr).set_must_be_loaded(true);
}

/*-------------------------------------------------
    machine_config( omv1000 )
-------------------------------------------------*/

void omv_state::omv1000(machine_config &config)
{
	sg1000_base(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &omv_state::omv_map);
	m_maincpu->set_addrmap(AS_IO, &omv_state::omv_io_map);

	/* expansion slot */
	SG1000_EXPANSION_SLOT(config, m_sgexpslot, sg1000_expansion_devices, nullptr, false);

	/* cartridge */
	OMV_CART_SLOT(config, m_cart, sg1000_cart, nullptr);
}

/*-------------------------------------------------
    machine_config( omv2000 )
-------------------------------------------------*/

void omv2000_state::omv2000(machine_config &config)
{
	omv1000(config);

	/* controller ports */
	SMS_CONTROL_PORT(config, m_ctrl2, sms_control_port_passive_devices, SMS_CTRL_OPTION_JOYPAD);
}

/*-------------------------------------------------
    machine_config( sc3000 )
-------------------------------------------------*/

void sc3000_state::sc3000(machine_config &config)
{
	sc3000_base(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &sc3000_state::sc3000_map);
	m_maincpu->set_addrmap(AS_IO, &sc3000_state::sc3000_io_map);

	/* cartridge */
	SC3000_CART_SLOT(config, m_cart, sg1000_cart, nullptr).set_must_be_loaded(true);
}

void sc3000_state::sc3000pal(machine_config &config)
{
	sc3000(config);

	tms9929a_device &vdp(TMS9929A(config.replace(), TMS9918A_TAG, 10.738635_MHz_XTAL));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	vdp.int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	// Z80A and SN76489AN are clocked by a separate ceramic oscillator since TMS9929A has no CPUCLK output
	m_maincpu->set_clock(3.58_MHz_XTAL);
	subdevice(SN76489AN_TAG)->set_clock(3.58_MHz_XTAL);

	m_sgexpslot->set_default_option("sk1100e");
}

/*-------------------------------------------------
    machine_config( sf7000 )
-------------------------------------------------*/

void sf7000_state::sf7000(machine_config &config)
{
	// FIXME: this thing is actually a cartridge slot peripheral for the SC-3000 and shouldn't be a separate machine

	sc3000_base(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &sf7000_state::sf7000_map);
	m_maincpu->set_addrmap(AS_IO, &sf7000_state::sf7000_io_map);

	/* devices */
	i8255_device &ppi(I8255(config, UPD9255_1_TAG));
	ppi.in_pa_callback().set(FUNC(sf7000_state::ppi_pa_r));
	ppi.out_pb_callback().set("cent_data_out", FUNC(output_latch_device::write));
	ppi.out_pc_callback().set(FUNC(sf7000_state::ppi_pc_w));
	ppi.tri_pc_callback().set_constant(0x8f); // /ROM SEL must be active after reset

	i8251_device &upd8251(I8251(config, UPD8251_TAG, 4.9152_MHz_XTAL / 2));
	upd8251.txd_handler().set(RS232_TAG, FUNC(rs232_port_device::write_txd));
	upd8251.dtr_handler().set(RS232_TAG, FUNC(rs232_port_device::write_dtr));
	upd8251.rts_handler().set(RS232_TAG, FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, RS232_TAG, default_rs232_devices, nullptr));
	rs232.rxd_handler().set(UPD8251_TAG, FUNC(i8251_device::write_rxd));
	rs232.cts_handler().set(UPD8251_TAG, FUNC(i8251_device::write_cts));
	rs232.dsr_handler().set(UPD8251_TAG, FUNC(i8251_device::write_dsr));

	UPD765A(config, m_fdc, 16_MHz_XTAL / 4); // clocked through SED9420C
	m_fdc->set_ready_line_connected(true); // or disconnected by split pad?
	m_fdc->set_select_lines_connected(false); // FIXME: actually connected according to schematics
	FLOPPY_CONNECTOR(config, UPD765_TAG ":0", sf7000_floppies, "3ssdd", sf7000_state::floppy_formats);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(sf7000_state::write_centronics_busy));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	/* software lists */
	SOFTWARE_LIST(config, "flop_list").set_original("sf7000");
}

/***************************************************************************
    ROMS
***************************************************************************/

ROM_START( sg1000 )
	ROM_REGION( 0x10000, Z80_TAG, ROMREGION_ERASE00 )
ROM_END

#define rom_sg1000m2 rom_sg1000

#define rom_sc3000 rom_sg1000

#define rom_sc3000pal rom_sg1000

ROM_START( sf7000 )
	ROM_REGION( 0x10000, Z80_TAG, ROMREGION_ERASE00 )
	ROM_LOAD( "ipl.rom", 0x0000, 0x2000, CRC(d76810b8) SHA1(77339a6db2593aadc638bed77b8e9bed5d9d87e3) )
ROM_END

ROM_START( omv1000 )
	ROM_REGION( 0x10000, Z80_TAG, ROMREGION_ERASE00 )
	ROM_LOAD( "omvbios.bin", 0x0000, 0x4000, BAD_DUMP CRC(c5a67b95) SHA1(6d7c64dd60dee4a33061d3d3a7c2ed190d895cdb) )    // The BIOS comes from a Multivision FG-2000. It is still unknown if the FG-1000 BIOS differs
ROM_END

ROM_START( omv2000 )
	ROM_REGION( 0x10000, Z80_TAG, ROMREGION_ERASE00 )
	ROM_LOAD( "omvbios.bin", 0x0000, 0x4000, CRC(c5a67b95) SHA1(6d7c64dd60dee4a33061d3d3a7c2ed190d895cdb) )
ROM_END

} // anonymous namespace


/***************************************************************************
    SYSTEM DRIVERS
***************************************************************************/

//    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT    CLASS          INIT        COMPANY             FULLNAME                                    FLAGS
CONS( 1983, sg1000,    0,      0,      sg1000,    sg1000,  sg1000_state,  empty_init, "Sega",             "SG-1000",                                  MACHINE_SUPPORTS_SAVE )
CONS( 1984, sg1000m2,  sg1000, 0,      sc3000,    sc3000,  sc3000_state,  empty_init, "Sega",             "SG-1000 II",                               MACHINE_SUPPORTS_SAVE )
CONS( 1984, omv1000,   sg1000, 0,      omv1000,   omv1000, omv_state,     empty_init, "Tsukuda Original", "Othello Multivision FG-1000",              MACHINE_SUPPORTS_SAVE )
CONS( 1984, omv2000,   sg1000, 0,      omv2000,   omv2000, omv2000_state, empty_init, "Tsukuda Original", "Othello Multivision FG-2000",              MACHINE_SUPPORTS_SAVE )

// SC-3000 and SC-3000H are identical except for key mechanism.
// PAL/SECAM versions of SC-3000 were distributed by Yeno in France, by John Sands Electronics in Australia and by Grandstand Leisure in New Zealand.
COMP( 1983, sc3000,    0,      sg1000, sc3000,    sc3000,  sc3000_state,  empty_init, "Sega",             "SC-3000 (NTSC)",                           MACHINE_SUPPORTS_SAVE )
COMP( 1983, sc3000pal, sc3000, 0,      sc3000pal, sc3000,  sc3000_state,  empty_init, "Sega",             "SC-3000 (PAL)",                            MACHINE_SUPPORTS_SAVE )
COMP( 1983, sf7000,    sc3000, 0,      sf7000,    sf7000,  sf7000_state,  empty_init, "Sega",             "SC-3000/Super Control Station SF-7000",    MACHINE_SUPPORTS_SAVE )



sh4robot.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

SH4 Robot

http://web.archive.org/web/20131127151413/perso.telecom-paristech.fr/~polti/robot/

Original site died. None of the downloads in the above wayback page work, so fairly useless.


2013-11-27 Skeleton driver.


      0x0000 0000 - 0x7FFF FFFF     : P0 area, cachable
      0x8000 0000 - 0x9FFF FFFF     : P1 area, cachable
      0xA000 0000 - 0xBFFF FFFF     : P2 area, non-cachable
      0xC000 0000 - 0xDFFF FFFF     : P3 area, cachable
      0xE000 0000 - 0xFFFF FFFF     : P4 area, non-cachable


      0x0000 0000 - 0x03FF FFFF     : Area 0 (boot, ROM)
      0x0400 0000 - 0x07FF FFFF     : Area 1 (FPGA)
      0x0800 0000 - 0x08FF FFFF     : Area 2 (SDRAM 1, 16M)
      0x0C00 0000 - 0x0CFF FFFF     : Area 3 (SDRAM 2, 16M)
      0x1000 0000 - 0x13FF FFFF     : Area 4 (FPGA)
      0x1400 0000 - 0x17FF FFFF     : Area 5 (FPGA)
      0x1800 0000 - 0x1BFF FFFF     : Area 6 (FPGA)
      0x1C00 0000 - 0x1FFF FFFF     : Area 7 (reserved)

****************************************************************************/

#include "emu.h"
#include "cpu/sh/sh4.h"


namespace {

class sh4robot_state : public driver_device
{
public:
	sh4robot_state(const machine_config &mconfig, device_type type, const char *tag)
	: driver_device(mconfig, type, tag)
	, m_maincpu(*this, "maincpu")
	{ }

	void sh4robot(machine_config &config);

private:
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<sh7750_device> m_maincpu;
};


void sh4robot_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000000, 0x00000fff).rom();
	map(0x08000000, 0x08ffffff).ram(); // SDRAM 1
	map(0x0c000000, 0x0cffffff).ram(); // SDRAM 2
}

void sh4robot_state::io_map(address_map &map)
{
	map.unmap_value_high();
}

static INPUT_PORTS_START( sh4robot )
INPUT_PORTS_END

void sh4robot_state::sh4robot(machine_config &config)
{
	/* basic machine hardware */
	SH7750(config, m_maincpu, 200000000);
	m_maincpu->set_md(0, 1);
	m_maincpu->set_md(1, 0);
	m_maincpu->set_md(2, 1);
	m_maincpu->set_md(3, 0);
	m_maincpu->set_md(4, 0);
	m_maincpu->set_md(5, 1);
	m_maincpu->set_md(6, 0);
	m_maincpu->set_md(7, 1);
	m_maincpu->set_md(8, 0);
	m_maincpu->set_sh4_clock(200000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &sh4robot_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &sh4robot_state::io_map);
	m_maincpu->set_force_no_drc(true);
}

/* ROM definition */
ROM_START( sh4robot )
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "bootloader.bin", 0x0000, 0x0882, CRC(d2ea0b7d) SHA1(7dd566c5e325d1ce1156a0bcbd7e10d011e9d35f))

	// FLASH TC58128AFT
	// flash blocks 1 till 199 (1*32*512 till 199*32*512)
	//ROM_LOAD( "vmlinux-nand_img_with_oob-2.6.10-v1.0", 0x0000, 0x149be0, CRC(eec69ef5) SHA1(524e26d2c2c28061911f4726646b18596d134736))
	// Root FS at flash blocks from 201 till end (201*32*512)
	//ROM_LOAD( "shix-linux-v1.0.yaffs", 0x0000, 0x7e9e40, CRC(7a7fdb04) SHA1(0b761e2d179335398399cb046de4e591157cb72f))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY      FULLNAME  FLAGS
COMP( 20??, sh4robot, 0,      0,      sh4robot, sh4robot, sh4robot_state, empty_init, "<unknown>", "Robot",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_MECHANICAL | MACHINE_REQUIRES_ARTWORK )



shine.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/***********************************************************************************

Shine/1

TODO: everything to be verified.

The system rom has an inbuilt monitor program - unknown how to access it.

The floppy disk is accessed via an "expansion" socket on the back. Inbuilt commands
such as DIR [1|2], RENAME, ERASE, PROT, XFER look interesting. The DIR command does
multiple reads of 0x9E00.

There's a DIN socket for cassette. Commands BAUD, SAVE, LOAD appear to be the ones
but the syntax has yet to be worked out. BAUD [0-9] is allowed but what is it doing?


************************************************************************************/

#include "emu.h"

#include "cpu/m6502/m6502.h"
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "machine/ram.h"
#include "machine/6522via.h"
#include "machine/wd_fdc.h"
#include "machine/input_merger.h"
#include "sound/spkrdev.h"
#include "video/mc6847.h"
#include "bus/centronics/ctronics.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "utf8.h"


namespace {

class shine_state : public driver_device
{
public:
	shine_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_vdg(*this, "vdg")
		, m_ram(*this, "ram")
		, m_via(*this, "via%u", 0)
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0)
		, m_centronics(*this, "centronics")
		, m_speaker(*this, "speaker")
		, m_cass(*this, "cassette")
		, m_irqs(*this, "irqs")
		, m_y(*this, "Y%u", 0U)
		, m_video_ram(*this, "video_ram")
	{ }

	void shine(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void shine_mem(address_map &map) ATTR_COLD;
	uint8_t via0_pa_r();
	void via0_pb_w(uint8_t data);
	void floppy_w(uint8_t data);
	uint8_t vdg_videoram_r(offs_t offset);

	required_device<cpu_device> m_maincpu;
	required_device<mc6847_base_device> m_vdg;
	required_device<ram_device> m_ram;
	required_device_array<via6522_device, 2> m_via;
	required_device<fd1771_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<centronics_device> m_centronics;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cass;
	required_device<input_merger_device> m_irqs;
	required_ioport_array<8> m_y;
	required_shared_ptr<uint8_t> m_video_ram;

	/* keyboard state */
	u8 m_keylatch = 0U;
};



void shine_state::shine_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x67ff).ram();
	map(0x6800, 0x7fff).ram().share(m_video_ram);
	map(0x9400, 0x940f).m(m_via[0], FUNC(via6522_device::map));
	map(0x9800, 0x980f).m(m_via[1], FUNC(via6522_device::map));
	map(0x9c00, 0x9c03).rw(m_fdc, FUNC(fd1771_device::read), FUNC(fd1771_device::write));
	map(0x9d00, 0x9d00).w(FUNC(shine_state::floppy_w));
	map(0xb000, 0xffff).rom().region("maincpu",0);
}


static INPUT_PORTS_START( shine )
	PORT_START("Y0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL")               PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT")              PORT_CODE(KEYCODE_LSHIFT)     PORT_CODE(KEYCODE_RSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_0)          PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_5)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("Y3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('@')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_A)          PORT_CHAR('A')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_B)          PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_C)          PORT_CHAR('C')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_D)          PORT_CHAR('D')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_E)          PORT_CHAR('E')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_F)          PORT_CHAR('F')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_G)          PORT_CHAR('G')

	PORT_START("Y4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_H)          PORT_CHAR('H')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_I)          PORT_CHAR('I')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_J)          PORT_CHAR('J')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_K)          PORT_CHAR('K')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_L)          PORT_CHAR('L')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_M)          PORT_CHAR('M')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_N)          PORT_CHAR('N')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_O)          PORT_CHAR('O')

	PORT_START("Y5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_P)          PORT_CHAR('P')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_Q)          PORT_CHAR('Q')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_R)          PORT_CHAR('R')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_S)          PORT_CHAR('S')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_T)          PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_U)          PORT_CHAR('U')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_V)          PORT_CHAR('V')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_W)          PORT_CHAR('W')

	PORT_START("Y6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_X)          PORT_CHAR('X')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_Y)          PORT_CHAR('Y')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_Z)          PORT_CHAR('Z')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR('[')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('\\')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )                                 PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR(']')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_UP)              PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('^')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ESC")                PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC),27)

	PORT_START("Y7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("COPY")               PORT_CODE(KEYCODE_TAB)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RET")                PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DEL")                PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(8)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPACE")              PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("REPT")               PORT_CODE(KEYCODE_RCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_UP UTF8_DOWN)    PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))    PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LOCK")               PORT_CODE(KEYCODE_CAPSLOCK)   PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
INPUT_PORTS_END

/***************************************************************************
    DEVICE CONFIGURATION
***************************************************************************/

uint8_t shine_state::via0_pa_r()
{
	uint8_t data;

	data = m_y[m_keylatch]->read();

	return data;
}

void shine_state::via0_pb_w(uint8_t data)
{
	/* keyboard column */
	m_keylatch = data & 0x07;

	/* MC6847 - all to be verified */
	m_vdg->ag_w(BIT(data, 4));
	m_vdg->gm0_w(BIT(data, 5));
	m_vdg->gm1_w(BIT(data, 6));
	m_vdg->gm2_w(BIT(data, 7));
}


void shine_state::floppy_w(uint8_t data)
{
	floppy_image_device *floppy = nullptr;

	if (!BIT(data, 0)) floppy = m_floppy[0]->get_device();
	if (!BIT(data, 1)) floppy = m_floppy[1]->get_device();

	m_fdc->set_floppy(floppy);

	if (floppy)
		floppy->mon_w(0);
}


uint8_t shine_state::vdg_videoram_r(offs_t offset)
{
	if (offset == ~0) return 0xff;

	offset ^= 0x17ff;

	m_vdg->as_w(BIT(m_video_ram[offset], 6));
	m_vdg->inv_w(BIT(m_video_ram[offset], 7));

	return m_video_ram[offset];
}


void shine_state::machine_start()
{
	/* register for state saving */
	save_item(NAME(m_keylatch));
}


void shine_state::shine(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, 2000000); // 2MHz ??
	m_maincpu->set_addrmap(AS_PROGRAM, &shine_state::shine_mem);

	INPUT_MERGER_ANY_HIGH(config, "irqs").output_handler().set_inputline("maincpu", M6502_IRQ_LINE);

	/* video hardware */
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	MC6847(config, m_vdg, 3.579545_MHz_XTAL);
	m_vdg->set_screen("screen");
	m_vdg->input_callback().set(FUNC(shine_state::vdg_videoram_r));
	m_vdg->set_black_and_white(true);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 1.00);

	RAM(config, m_ram);
	m_ram->set_default_size("32K");
	m_ram->set_extra_options("16K");

	MOS6522(config, m_via[0], 1000000);
	m_via[0]->readpa_handler().set(FUNC(shine_state::via0_pa_r));
	m_via[0]->writepb_handler().set(FUNC(shine_state::via0_pb_w));
	m_via[0]->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<0>));
	m_via[0]->cb2_handler().set(m_speaker, FUNC(speaker_sound_device::level_w));

	MOS6522(config, m_via[1], 1000000);
	m_via[1]->irq_handler().set(m_irqs, FUNC(input_merger_device::in_w<1>));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");

	FD1771(config, m_fdc, 1000000);

	FLOPPY_CONNECTOR(config, m_floppy[0], "525qd", FLOPPY_525_QD, true, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], "525qd", FLOPPY_525_QD, false, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
}


ROM_START( shine )
	ROM_REGION( 0x5000, "maincpu", 0 )
	ROM_LOAD("basic_plus.ic58", 0x0000, 0x1000, CRC(c75d9675) SHA1(eecc12533aa33c7744e1ea1fde56883739ac7436))
	ROM_LOAD("disco-dk2.ic59",  0x1000, 0x1000, CRC(b9230d50) SHA1(3975e5850356c035cd1963e0fbca1e980463ee01))
	ROM_LOAD("d3.ic62",         0x2000, 0x1000, CRC(7c88e219) SHA1(93003715bb82d63b6d4f673f89eae59c73f2945e))
	ROM_LOAD("e3.ic61",         0x3000, 0x1000, CRC(5a1624e9) SHA1(b4fbc983c646d4e70dda878d5dd75e45408522a9))
	ROM_LOAD("f3.ic60",         0x4000, 0x1000, CRC(1549ca2f) SHA1(5b011cdca0121a550af956b6d4580544942459ce))
ROM_END

} // anonymous namespace


/*    YEAR  NAME    PARENT  COMPAT  MACHINE   INPUT   CLASS         INIT         COMPANY                  FULLNAME    FLAGS */
COMP( 1983, shine,  0,      0,      shine,    shine,  shine_state,  empty_init,  "Lorenzon Elettronica",  "Shine/1", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



si5500.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*****************************************************************************************

    Skeleton driver for Scientific Instruments Model 5500 temperature controller.

*****************************************************************************************/

#include "emu.h"
#include "cpu/tms9900/tms9980a.h"
#include "machine/74259.h"
//#include "machine/icl7104.h"
#include "machine/tms9901.h"
#include "machine/tms9902.h"
#include "machine/tms9914.h"
#include "machine/x2201.h"


namespace {

class si5500_state : public driver_device
{
public:
	si5500_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mainpsi(*this, "mainpsi")
		, m_gpibpsi(*this, "gpibpsi")
		, m_gpibc(*this, "gpibc")
		, m_keyplatch(*this, "keyplatch")
		, m_keypad(*this, "KEYPAD%u", 0U)
	{
	}

	void si5500(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void mainic_w(offs_t offset, u8 data);
	void gpib_int_w(int state);
	void acc_int_w(int state);
	u8 gpibpsi_input_r(offs_t offset);
	void gpibc_we_w(int state);
	void gpibc_dbin_w(int state);
	u8 keypad_r(offs_t offset);

	void mem_map(address_map &map) ATTR_COLD;
	void cru_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<tms9901_device> m_mainpsi;
	required_device<tms9901_device> m_gpibpsi;
	required_device<tms9914_device> m_gpibc;
	required_device<ls259_device> m_keyplatch;
	required_ioport_array<4> m_keypad;

	u8 m_gpib_data = 0;
};

void si5500_state::machine_start()
{
	m_gpib_data = 0;
	save_item(NAME(m_gpib_data));
}

void si5500_state::mainic_w(offs_t offset, u8 data)
{
	if (data)
		m_maincpu->set_input_line(offset & 7, ASSERT_LINE);
	else
		m_maincpu->set_input_line(INT_9980A_CLEAR, CLEAR_LINE);
}

void si5500_state::gpib_int_w(int state)
{
	m_mainpsi->set_int_line(4, state);
}

void si5500_state::acc_int_w(int state)
{
	m_mainpsi->set_int_line(5, state);
}

u8 si5500_state::gpibpsi_input_r(offs_t offset)
{
	switch (offset)
	{
	case tms9901_device::P0:
	case tms9901_device::P1:
	case tms9901_device::P2:
	case tms9901_device::P3:
	case tms9901_device::P4:
	case tms9901_device::P5:
	case tms9901_device::P6:
		return BIT(m_gpib_data, offset-tms9901_device::P0);
	case tms9901_device::INT15_P7:
		return BIT(m_gpib_data, 7);
	default:
		return 1;
	}
}

void si5500_state::gpibc_we_w(int state)
{
	if (!state)
	{
		u16 pio = m_gpibpsi->pio_outputs();
		m_gpibc->write((pio >> 8) & 7, pio & 0xff);
	}
}

void si5500_state::gpibc_dbin_w(int state)
{
	if (state)
	{
		u16 pio = m_gpibpsi->pio_outputs();
		m_gpib_data = m_gpibc->read((pio >> 8) & 7);
	}
}

u8 si5500_state::keypad_r(offs_t offset)
{
	for (int n = 0; n < 4; n++)
		if (!BIT(m_keyplatch->output_state(), n))
			if (!BIT(m_keypad[n]->read(), offset))
				return 0;
	return 1;
}

void si5500_state::mem_map(address_map &map)
{
	map(0x0000, 0x2fff).rom().region("program", 0);
	map(0x3800, 0x3fff).ram();
}

void si5500_state::cru_map(address_map &map)
{
	map(0x0000, 0x003f).rw(m_mainpsi, FUNC(tms9901_device::read), FUNC(tms9901_device::write));
	map(0x0080, 0x00bf).rw("acc", FUNC(tms9902_device::cruread), FUNC(tms9902_device::cruwrite));
	map(0x00c0, 0x00ff).rw("adpsi", FUNC(tms9901_device::read), FUNC(tms9901_device::write));
	map(0x0100, 0x010f).w("outlatch1", FUNC(ls259_device::write_d0));
	map(0x0110, 0x011f).w("outlatch2", FUNC(ls259_device::write_d0));
	map(0x0120, 0x012f).w("outlatch3", FUNC(ls259_device::write_d0));
	map(0x0140, 0x014f).w("outlatch4", FUNC(ls259_device::write_d0));
	map(0x0150, 0x015f).w("outlatch5", FUNC(ls259_device::write_d0));
	map(0x0160, 0x016f).w(m_keyplatch, FUNC(ls259_device::write_d0)).nopr();
	map(0x0170, 0x017f).r(FUNC(si5500_state::keypad_r));
	map(0x0400, 0x043f).rw("nvrpsi", FUNC(tms9901_device::read), FUNC(tms9901_device::write));
	map(0x0440, 0x047f).rw(m_gpibpsi, FUNC(tms9901_device::read), FUNC(tms9901_device::write));
	map(0x0800, 0x0fff).rw("novram", FUNC(x2201_device::read), FUNC(x2201_device::write));
}

static INPUT_PORTS_START(si5500)
	PORT_START("KEYPAD0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("7  Edit  Prop") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("4  Select  Heater") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("1  Diag  Sensor") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEYPAD1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("8  Keep  Int") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("5  Auto  Time") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("2  Cal  Sensor") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEYPAD2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("9  Load  Der") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("6  Print  Rate") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("3  I/O  Set Pt") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("#  -") PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEYPAD3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Stop") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Man") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Run") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("*") PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0xf0, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

void si5500_state::si5500(machine_config &config)
{
	TMS9981(config, m_maincpu, 10_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &si5500_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &si5500_state::cru_map);

	TMS9901(config, m_mainpsi, 10_MHz_XTAL / 4);
	m_mainpsi->intreq_cb().set(FUNC(si5500_state::mainic_w));

	tms9902_device &acc(TMS9902(config, "acc", 10_MHz_XTAL / 4));
	acc.int_cb().set(FUNC(si5500_state::acc_int_w));

	TMS9901(config, "adpsi", 10_MHz_XTAL / 4);

	X2201(config, "novram");

	tms9901_device &nvrpsi(TMS9901(config, "nvrpsi", 10_MHz_XTAL / 4));
	// P0-11 = inputs for DAC?
	nvrpsi.p_out_cb(13).set("novram", FUNC(x2201_device::array_recall_w));
	nvrpsi.p_out_cb(14).set("novram", FUNC(x2201_device::store_w));
	nvrpsi.p_out_cb(15).set("novram", FUNC(x2201_device::cs_w));

	TMS9901(config, m_gpibpsi, 10_MHz_XTAL / 4);
	m_gpibpsi->p_out_cb(11).set(FUNC(si5500_state::gpibc_we_w));
	m_gpibpsi->p_out_cb(12).set(FUNC(si5500_state::gpibc_dbin_w));
	m_gpibpsi->read_cb().set(FUNC(si5500_state::gpibpsi_input_r));

	TMS9914(config, m_gpibc, 10_MHz_XTAL / 4);
	m_gpibc->int_write_cb().set(FUNC(si5500_state::gpib_int_w));

	LS259(config, "outlatch1");
	LS259(config, "outlatch2");
	LS259(config, "outlatch3");
	LS259(config, "outlatch4");
	LS259(config, "outlatch5");
	LS259(config, m_keyplatch);
}

// Board #1 (power): no digital ICs
// Board #2 (analog): TMS9901NL, ICL7104-16CPL, ICL8052ACPD, AD524AD, LF13509D
// Board #3 (output): X2201AD, 2x TMS9901NL, DAC1222LCN, TMS9914ANL, SN75162BN, DS75160AN
// Board #4: HM6116P-4, 3x TMS2532A-25JL, XTAL (10 MHz), TMS9981JDL, TMS9902ANL, TMS9901NL
// Back of front panel: 6x HD74LS259, HD74LS251
ROM_START(si5500)
	ROM_REGION(0x3000, "program", 0)
	ROM_LOAD("m5500dl_7-9-86.u4", 0x0000, 0x1000, CRC(cfcff0fe) SHA1(94173b3b7513954221ce3402ea5b5c36dfa5a8da))
	ROM_LOAD("m5500dl_7-9-86.u5", 0x1000, 0x1000, CRC(a932e85a) SHA1(f7152ae78bad79b457bb739e7ecc4556ca33cbbc))
	ROM_LOAD("m5500dl_7-9-86.u6", 0x2000, 0x1000, CRC(3161347d) SHA1(fab6c228a21ef3ecce255079a48ef1697f2c7ccb))
ROM_END

} // anonymous namespace


COMP(1986, si5500, 0, 0, si5500, si5500, si5500_state, empty_init, "Scientific Instruments", "Model 5500 Temperature Controller", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



siena.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/******************************************************************************

    Psion Siena

******************************************************************************/

#include "emu.h"

#include "bus/psion/honda/slot.h"
#include "machine/nvram.h"
#include "machine/psion_asic9.h"
#include "machine/psion_condor.h"
#include "machine/ram.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class siena_state : public driver_device
{
public:
	siena_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_asic9(*this, "asic9")
		, m_ram(*this, "ram")
		, m_nvram(*this, "nvram")
		, m_palette(*this, "palette")
		, m_keyboard(*this, "COL%u", 0U)
		, m_speaker(*this, "speaker")
		, m_condor(*this, "condor")
		, m_honda(*this, "honda")
	{ }

	void siena(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(wakeup);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<psion_asic9_device> m_asic9;
	required_device<ram_device> m_ram;
	required_device<nvram_device> m_nvram;
	required_device<palette_device> m_palette;
	required_ioport_array<8> m_keyboard;
	required_device<speaker_sound_device> m_speaker;
	required_device<psion_condor_device> m_condor;
	required_device<psion_honda_slot_device> m_honda;

	void palette_init(palette_device &palette);

	uint16_t kbd_r();

	uint8_t m_key_col = 0;
};


void siena_state::machine_start()
{
	m_nvram->set_base(m_ram->pointer(), m_ram->size());
}

void siena_state::machine_reset()
{
	m_asic9->io_space().install_readwrite_handler(0x0100, 0x011f, read8sm_delegate(*m_condor, FUNC(psion_condor_device::read)), write8sm_delegate(*m_condor, FUNC(psion_condor_device::write)), 0x00ff);
}


static INPUT_PORTS_START( siena )
	PORT_START("COL0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3')                  PORT_NAME("3 MC")
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2')                  PORT_NAME("2 MR")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1')                  PORT_NAME("1 Min")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME)                                                       PORT_NAME("On/CE")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(siena_state::wakeup), 0)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNKNOWN)

	PORT_START("COL1")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)          PORT_CHAR('w')  PORT_CHAR('W') PORT_CHAR('"')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)          PORT_CHAR('d')  PORT_CHAR('D') PORT_CHAR('/')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)          PORT_CHAR('c')  PORT_CHAR('C')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';')  PORT_CHAR(':')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)          PORT_CHAR('o')  PORT_CHAR('O') PORT_CHAR('(')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RCONTROL)   PORT_CHAR(UCHAR_SHIFT_2)                        PORT_NAME("Fn")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP)                                                       PORT_NAME("IR Send")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(siena_state::wakeup), 0)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)         PORT_CHAR(UCHAR_MAMEKEY(F3))                    PORT_NAME("Word")            PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(siena_state::wakeup), 0)

	PORT_START("COL2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)          PORT_CHAR('e')  PORT_CHAR('E') PORT_CHAR(0xa3)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)          PORT_CHAR('f')  PORT_CHAR('F') PORT_CHAR('~')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)          PORT_CHAR('v')  PORT_CHAR('V')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10)        PORT_CHAR(UCHAR_MAMEKEY(F10))                   PORT_NAME("Help")
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')  PORT_CHAR('<')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)          PORT_CHAR('p')  PORT_CHAR('P') PORT_CHAR(')')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT)       PORT_CHAR(UCHAR_MAMEKEY(LALT))                  PORT_NAME("Psion")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN)                                                       PORT_NAME("IR Receive")
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)         PORT_CHAR(UCHAR_MAMEKEY(F4))                    PORT_NAME("Agenda")

	PORT_START("COL3")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)          PORT_CHAR('r')  PORT_CHAR('R') PORT_CHAR('$')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)          PORT_CHAR('g')  PORT_CHAR('G') PORT_CHAR('@')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)          PORT_CHAR('b')  PORT_CHAR('B')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))                  PORT_NAME(u8"\u2190 Home") // U+2190 = ←
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')  PORT_CHAR('>')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-')  PORT_CHAR('_')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('=')                                  PORT_NAME("= %")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)                        PORT_NAME("Shift (L)")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)         PORT_CHAR(UCHAR_MAMEKEY(F5))                    PORT_NAME("Time")            PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(siena_state::wakeup), 0)

	PORT_START("COL4")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)          PORT_CHAR('t')  PORT_CHAR('T') PORT_CHAR('%')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)          PORT_CHAR('h')  PORT_CHAR('H') PORT_CHAR('{')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)          PORT_CHAR('n')  PORT_CHAR('N')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))                  PORT_NAME(u8"\u2193 Pg Dn") // U+2193 = ↓
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))                    PORT_NAME(u8"\u2191 Pg Up") // U+2191 = ↑
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)                                   PORT_NAME("Enter")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD)   PORT_CHAR('+')                                  PORT_NAME("+ M+")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)                        PORT_NAME("Shift (R)")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)         PORT_CHAR(UCHAR_MAMEKEY(F6))                    PORT_NAME("World")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(siena_state::wakeup), 0)

	PORT_START("COL5")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y')  PORT_CHAR('Y') PORT_CHAR('^')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)          PORT_CHAR('j')  PORT_CHAR('J') PORT_CHAR('}')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)          PORT_CHAR('m')  PORT_CHAR('M')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))                 PORT_NAME(u8"\u2192 End") // U+2192 = →
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/')  PORT_CHAR('?')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(0x08)                                 PORT_NAME("Del")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK)   PORT_CHAR('*')                                  PORT_NAME(u8"×")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_END)                                                        PORT_NAME("Off")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)         PORT_CHAR(UCHAR_MAMEKEY(F8))                    PORT_NAME("Sheet")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(siena_state::wakeup), 0)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)         PORT_CHAR(UCHAR_MAMEKEY(F7))                    PORT_NAME("Calc")            PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(siena_state::wakeup), 0)

	PORT_START("COL6")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q')  PORT_CHAR('Q') PORT_CHAR('!')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)          PORT_CHAR('s')  PORT_CHAR('S') PORT_CHAR('\\')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)          PORT_CHAR('x')  PORT_CHAR('X')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK)   PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))              PORT_NAME("\xe2\x97\x86 Caps")
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)          PORT_CHAR('l')  PORT_CHAR('L') PORT_CHAR(']')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)          PORT_CHAR('i')  PORT_CHAR('I') PORT_CHAR('*')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD)  PORT_CHAR('/')                                  PORT_NAME(u8"÷")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD)  PORT_CHAR('-')                                  PORT_NAME("- M-")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC))                   PORT_NAME("Esc")             PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(siena_state::wakeup), 0)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)         PORT_CHAR(UCHAR_MAMEKEY(F2))                    PORT_NAME("Data")            PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(siena_state::wakeup), 0)

	PORT_START("COL7")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)        PORT_CHAR(0x09)                                 PORT_NAME("Tab")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('a')  PORT_CHAR('A') PORT_CHAR('#')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z')  PORT_CHAR('Z')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11)        PORT_CHAR(UCHAR_MAMEKEY(F11))                   PORT_NAME("Menu Info")
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)          PORT_CHAR('k')  PORT_CHAR('K') PORT_CHAR('[')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)          PORT_CHAR('u')  PORT_CHAR('U') PORT_CHAR('&')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD)    PORT_CHAR('.')                                  PORT_NAME(". +/-")
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))              PORT_NAME("Control")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)         PORT_CHAR(UCHAR_MAMEKEY(F1))                    PORT_NAME("System")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(siena_state::wakeup), 0)
INPUT_PORTS_END


static INPUT_PORTS_START( siena_fr )
	PORT_INCLUDE(siena)

	PORT_MODIFY("COL1")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)          PORT_CHAR('z')  PORT_CHAR('Z') PORT_CHAR(U'é')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)          PORT_CHAR('d')  PORT_CHAR('D') PORT_CHAR('%')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)          PORT_CHAR('c')  PORT_CHAR('C')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(':')  PORT_CHAR('/')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)          PORT_CHAR('o')  PORT_CHAR('O') PORT_CHAR(U'ç')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)         PORT_CHAR(UCHAR_MAMEKEY(F3))                    PORT_NAME("Texte")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(siena_state::wakeup), 0)

	PORT_MODIFY("COL2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)          PORT_CHAR('e')  PORT_CHAR('E') PORT_CHAR('"')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)          PORT_CHAR('f')  PORT_CHAR('F') PORT_CHAR('@')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)          PORT_CHAR('v')  PORT_CHAR('V') PORT_CHAR('~')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10)        PORT_CHAR(UCHAR_MAMEKEY(F10))                   PORT_NAME("Aide")
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')  PORT_CHAR('?') PORT_CHAR(']')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)          PORT_CHAR('p')  PORT_CHAR('P') PORT_CHAR(U'à')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)         PORT_CHAR(UCHAR_MAMEKEY(F4))                    PORT_NAME("Agenda")

	PORT_MODIFY("COL3")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)          PORT_CHAR('r')  PORT_CHAR('R') PORT_CHAR('\'')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)          PORT_CHAR('g')  PORT_CHAR('G')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)          PORT_CHAR('b')  PORT_CHAR('B') PORT_CHAR('^')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))                  PORT_NAME(u8"\u2190 Début") // U+2190 = ←
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR(';')  PORT_CHAR('.')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-')  PORT_CHAR('_') PORT_CHAR(U'°')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)         PORT_CHAR(UCHAR_MAMEKEY(F5))                    PORT_NAME("Heure")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(siena_state::wakeup), 0)

	PORT_MODIFY("COL4")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)          PORT_CHAR('t')  PORT_CHAR('T') PORT_CHAR('(')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)          PORT_CHAR('h')  PORT_CHAR('H') PORT_CHAR('<')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)          PORT_CHAR('n')  PORT_CHAR('N') PORT_CHAR('[')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)                                   PORT_NAME(u8"Entrée")
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)         PORT_CHAR(UCHAR_MAMEKEY(F6))                    PORT_NAME("Monde")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(siena_state::wakeup), 0)

	PORT_MODIFY("COL5")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y')  PORT_CHAR('Y') PORT_CHAR(')')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)          PORT_CHAR('j')  PORT_CHAR('J') PORT_CHAR('>')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)          PORT_CHAR('m')  PORT_CHAR('M') PORT_CHAR(U'µ')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))                 PORT_NAME(u8"\u2192 Fin") // U+2192 = →
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('$')  PORT_CHAR('*') PORT_CHAR(U'£')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(0x08)                                 PORT_NAME("Eff")
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)         PORT_CHAR(UCHAR_MAMEKEY(F8))                    PORT_NAME("Tableur")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(siena_state::wakeup), 0)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)         PORT_CHAR(UCHAR_MAMEKEY(F7))                    PORT_NAME("Calc")            PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(siena_state::wakeup), 0)

	PORT_MODIFY("COL6")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)          PORT_CHAR('a')  PORT_CHAR('A') PORT_CHAR('&')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)          PORT_CHAR('s')  PORT_CHAR('S') PORT_CHAR('\\')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)          PORT_CHAR('x')  PORT_CHAR('X') PORT_CHAR('`')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)          PORT_CHAR('l')  PORT_CHAR('L') PORT_CHAR(U'ù')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)          PORT_CHAR('i')  PORT_CHAR('I') PORT_CHAR('!')
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)         PORT_CHAR(UCHAR_MAMEKEY(F2))                    PORT_NAME("Fiche")           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(siena_state::wakeup), 0)

	PORT_MODIFY("COL7")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('q')  PORT_CHAR('Q') PORT_CHAR('#')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_CHAR('w')  PORT_CHAR('W')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)          PORT_CHAR('k')  PORT_CHAR('K') PORT_CHAR(U'§')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)          PORT_CHAR('u')  PORT_CHAR('U') PORT_CHAR(U'è')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))              PORT_NAME("Ctrl")
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)         PORT_CHAR(UCHAR_MAMEKEY(F1))                    PORT_NAME(u8"Système")       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(siena_state::wakeup), 0)
INPUT_PORTS_END


INPUT_CHANGED_MEMBER(siena_state::wakeup)
{
	m_asic9->eint0_w(newval);
}


uint16_t siena_state::kbd_r()
{
	uint16_t data = 0x00;

	for (int i = 0; i < 8; i++)
	{
		if (BIT(m_key_col, i))
			data |= m_keyboard[i]->read();
	}

	return data;
}


void siena_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(190, 220, 190));

	for (int i = 1; i < 3; i++)
	{
		const int r = (0x99 * i) / 2;
		const int g = (0xaa * i) / 2;
		const int b = (0x88 * i) / 2;
		m_palette->set_pen_color(i, rgb_t(r, g, b));
	}
}


void siena_state::siena(machine_config &config)
{
	PSION_ASIC9(config, m_asic9, 7.68_MHz_XTAL);
	m_asic9->set_screen("screen");
	m_asic9->set_ram_rom("ram", "rom");
	m_asic9->port_ab_r().set(FUNC(siena_state::kbd_r));
	m_asic9->buz_cb().set(m_speaker, FUNC(speaker_sound_device::level_w));
	m_asic9->col_cb().set([this](uint8_t data) { m_key_col = data; });

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_size(240, 160);
	screen.set_visarea_full();
	screen.set_refresh_hz(66);
	screen.set_screen_update(m_asic9, FUNC(psion_asic9_device::screen_update));
	screen.set_palette(m_palette);
	PALETTE(config, "palette", FUNC(siena_state::palette_init), 3);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00); // Piezo buzzer

	RAM(config, m_ram).set_default_size("512K").set_extra_options("1M");
	NVRAM(config, "nvram", nvram_device::DEFAULT_NONE);

	PSION_CONDOR(config, m_condor, 3'686'400); // FIXME: unknown clock source
	m_condor->txd_handler().set(m_honda, FUNC(psion_honda_slot_device::write_txd));
	m_condor->rts_handler().set(m_honda, FUNC(psion_honda_slot_device::write_rts));
	m_condor->dtr_handler().set(m_honda, FUNC(psion_honda_slot_device::write_dtr));
	m_condor->int_handler().set(m_asic9, FUNC(psion_asic9_device::eint1_w));

	// Honda expansion port
	PSION_HONDA_SLOT(config, m_honda, psion_honda_devices, "ssd");
	m_honda->rxd_handler().set(m_condor, FUNC(psion_condor_device::write_rxd));
	m_honda->dcd_handler().set(m_condor, FUNC(psion_condor_device::write_dcd));
	m_honda->dsr_handler().set(m_condor, FUNC(psion_condor_device::write_dsr));
	m_honda->cts_handler().set(m_condor, FUNC(psion_condor_device::write_cts));
	m_honda->sdoe_handler().set(m_asic9, FUNC(psion_asic9_device::medchng_w));
	m_asic9->data_r<4>().set(m_honda, FUNC(psion_honda_slot_device::data_r));
	m_asic9->data_w<4>().set(m_honda, FUNC(psion_honda_slot_device::data_w));

	SOFTWARE_LIST(config, "ssd_list").set_original("psion_ssd").set_filter("SIENA");
	//SOFTWARE_LIST(config, "flop_list").set_original("psion_flop").set_filter("SIENA");
}


ROM_START(siena)
	ROM_REGION16_LE(0x100000, "rom", 0)
	ROM_SYSTEM_BIOS(0, "420f", "V4.20F/ENG")
	ROMX_LOAD("vine_v4.20f_eng.bin", 0x00000, 0x100000, CRC(641f8e7c) SHA1(fe0e46540e0aac5aabb2dd1b96689da41e8f55fb), ROM_BIOS(0))
ROM_END

ROM_START(siena_fr)
	ROM_REGION16_LE(0x100000, "rom", 0)
	ROM_SYSTEM_BIOS(0, "421f", "V4.21F/FRN")
	ROMX_LOAD("vine_v4.21f_frn.bin", 0x00000, 0x100000, CRC(104691d6) SHA1(d1e12b305cd2de7dbf6b1a342adb7bf196d7abcb), ROM_BIOS(0))
ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT      CLASS          INIT         COMPANY   FULLNAME           FLAGS
COMP( 1996, siena,    0,      0,      siena,    siena,     siena_state,   empty_init,  "Psion",  "Siena",           MACHINE_SUPPORTS_SAVE )
COMP( 1996, siena_fr, siena,  0,      siena,    siena_fr,  siena_state,   empty_init,  "Psion",  "Siena (French)",  MACHINE_SUPPORTS_SAVE )



simultano.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Achim, bataais, Berger
/*******************************************************************************

Saitek Simultano, it is related to Saitek Stratos, see stratos.cpp
But it's not similar enough to be a subdriver of it.

Two versions are known: "B" from 1988/1989, and "C" from 1989.

Hardware notes:
- WDC W65C02P @ 5MHz
- 64KB ROM (2*AMI 27256), socket for 32KB Endgame ROM (ver. 2)
- 8KB RAM (SRM2264LC) battery-backed
- "HELIOS" NEC gate array
- Epson SED1502F, LCD screen
- piezo, 16+3 leds, button sensors chessboard

It also appeared in Tandy's Chess Champion 2150, still manufactured and
programmed by Saitek. Not as a simple rebrand, but with hardware differences:
3MHz R65C02, 1 64KB ROM and no EGR socket.

TODO:
- IRQ is from HELIOS pin 2, how is timing determined? same problem as with stratos
- verify that egr(1) does not work on real chesscomputer
- is cc2150 the same rom contents as the first simultano?

*******************************************************************************/

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/m6502/r65c02.h"
#include "cpu/m6502/w65c02.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"
#include "video/sed1500.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "saitek_simultano.lh"


namespace {

class simultano_state : public driver_device
{
public:
	simultano_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_rombank(*this, "rombank"),
		m_board(*this, "board"),
		m_led_pwm(*this, "led_pwm"),
		m_lcd_pwm(*this, "lcd_pwm"),
		m_lcd(*this, "lcd"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_out_lcd(*this, "s%u.%u", 0U, 0U)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(go_button);

	void simultano(machine_config &config);
	void cc2150(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	memory_view m_rombank;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_led_pwm;
	required_device<pwm_display_device> m_lcd_pwm;
	required_device<sed1502_device> m_lcd;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<8+1> m_inputs;
	output_finder<16, 34> m_out_lcd;

	bool m_power = false;
	u8 m_select = 0;
	u8 m_control = 0;

	void simultano_map(address_map &map) ATTR_COLD;
	void cc2150_map(address_map &map) ATTR_COLD;

	void power_off();
	void lcd_pwm_w(offs_t offset, u8 data);
	void lcd_output_w(offs_t offset, u64 data);

	void select_w(u8 data);
	u8 chessboard_r();
	void sound_w(u8 data);
	u8 control_r();
	void control_w(u8 data);
};

void simultano_state::machine_start()
{
	m_out_lcd.resolve();

	// register for savestates
	save_item(NAME(m_power));
	save_item(NAME(m_select));
	save_item(NAME(m_control));
}

void simultano_state::machine_reset()
{
	m_power = true;
	m_control = 0;
	m_rombank.select(0);
}



/*******************************************************************************
    I/O
*******************************************************************************/

// soft power on/off

INPUT_CHANGED_MEMBER(simultano_state::go_button)
{
	if (newval && !m_power)
	{
		m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
		machine_reset();
	}
}

void simultano_state::power_off()
{
	m_power = false;
	m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);

	// clear display
	for (int i = 0; i < 0x80; i++)
		m_lcd->write(i, 0);

	m_led_pwm->clear();
	m_lcd_pwm->clear();
}


// LCD

void simultano_state::lcd_pwm_w(offs_t offset, u8 data)
{
	m_out_lcd[offset & 0x3f][offset >> 6] = data;
}

void simultano_state::lcd_output_w(offs_t offset, u64 data)
{
	if (m_power)
		m_lcd_pwm->write_row(offset, data);
}


// HELIOS

void simultano_state::sound_w(u8 data)
{
	m_dac->write(1);
}

void simultano_state::select_w(u8 data)
{
	m_dac->write(0); // guessed

	// d0-d3: input/chessboard mux
	// d6,d7: side panel led mux
	// d4,d5: led data
	m_led_pwm->matrix_partial(0, 2, data >> 4 & 3, 1 << (data & 0xf));
	m_led_pwm->matrix_partial(2, 2, data >> 6 & 3, ~data >> 4 & 3);
	m_select = data;
}

u8 simultano_state::chessboard_r()
{
	// d0-d7: chessboard sensors
	return ~m_board->read_file(m_select & 0xf);
}

u8 simultano_state::control_r()
{
	u8 data = 0;
	u8 sel = m_select & 0xf;

	// read button panel
	if (sel < 9)
		data |= m_inputs[sel]->read() << 5;

	return data;
}

void simultano_state::control_w(u8 data)
{
	u8 prev = m_control;
	m_control = data;

	// d0,d1: rombank
	m_rombank.select(bitswap<2>(data,0,1));

	// d6 falling edge: power-off request
	if (~data & prev & 0x40)
		power_off();
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void simultano_state::cc2150_map(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("nvram");
	map(0x2000, 0x2000).w(FUNC(simultano_state::select_w));
	map(0x2200, 0x2200).w(FUNC(simultano_state::sound_w));
	map(0x2400, 0x2400).r(FUNC(simultano_state::chessboard_r));
	map(0x2600, 0x2600).rw(FUNC(simultano_state::control_r), FUNC(simultano_state::control_w));
	//map(0x4000, 0x5fff).noprw(); // tries to access RAM, unpopulated on PCB
	map(0x6000, 0x607f).rw("lcd", FUNC(sed1502_device::read), FUNC(sed1502_device::write));

	map(0x8000, 0xffff).view(m_rombank);
	m_rombank[0](0x8000, 0xffff).rom().region("maincpu", 0x0000);
	m_rombank[1](0x8000, 0xffff).rom().region("maincpu", 0x8000);
	m_rombank[2](0x8000, 0xffff).lr8(NAME([]() { return 0xff; }));
	m_rombank[3](0x8000, 0xffff).lr8(NAME([]() { return 0xff; }));
}

void simultano_state::simultano_map(address_map &map)
{
	cc2150_map(map);
	m_rombank[2](0x8000, 0xffff).r("extrom", FUNC(generic_slot_device::read_rom));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( simultano )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Set Up")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_CUSTOM) // freq sel

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("Sound")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Stop")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("New Game")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Play")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Tab / Color")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")

	PORT_START("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_EQUALS) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Function")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_CUSTOM) // freq sel

	PORT_START("IN.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Library")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Info")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_CUSTOM) // freq sel

	PORT_START("IN.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Analysis")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Normal")

	PORT_START("IN.8")
	PORT_CONFNAME( 0x01, 0x01, "Battery Status" )
	PORT_CONFSETTING(    0x00, "Low" )
	PORT_CONFSETTING(    0x01, DEF_STR( Normal ) )

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(simultano_state::go_button), 0) PORT_NAME("Go")
INPUT_PORTS_END

static INPUT_PORTS_START( cc2150 )
	PORT_INCLUDE( simultano )

	PORT_MODIFY("IN.5")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_CUSTOM)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void simultano_state::cc2150(machine_config &config)
{
	// basic machine hardware
	R65C02(config, m_maincpu, 3_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &simultano_state::cc2150_map);
	m_maincpu->set_periodic_int(FUNC(simultano_state::irq0_line_hold), attotime::from_hz(91.6)); // measured

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(350));
	m_board->set_nvram_enable(true);

	// video hardware
	SED1502(config, m_lcd, 32768).write_segs().set(FUNC(simultano_state::lcd_output_w));
	PWM_DISPLAY(config, m_lcd_pwm).set_size(16, 34);
	m_lcd_pwm->set_refresh(attotime::from_hz(30));
	m_lcd_pwm->output_x().set(FUNC(simultano_state::lcd_pwm_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(873/2, 1080/2);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_led_pwm).set_size(2+2, 8);
	config.set_default_layout(layout_saitek_simultano);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void simultano_state::simultano(machine_config &config)
{
	cc2150(config);

	// basic machine hardware
	W65C02(config.replace(), m_maincpu, 5_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &simultano_state::simultano_map);
	m_maincpu->set_periodic_int(FUNC(simultano_state::irq0_line_hold), attotime::from_hz(76)); // approximation

	// extension rom
	GENERIC_SOCKET(config, "extrom", generic_plain_slot, "saitek_egr");
	SOFTWARE_LIST(config, "cart_list").set_original("saitek_egr").set_filter("egr2");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( simultano )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("yo1h_c12_u3.u3",  0x0000, 0x8000, CRC(e7f8bae4) SHA1(82d8c6879e031b9909dd63bff692055f32236f9c) )
	ROM_LOAD("byo1h_c13_u4.u4", 0x8000, 0x8000, CRC(4f5557bc) SHA1(2fd4b1791cec4e6e33b1da644edb603ed8c9cd2e) )

	ROM_REGION( 795951, "screen", 0 )
	ROM_LOAD("simultano.svg", 0, 795951, CRC(ac9942bb) SHA1(f9252e5bf7b8af698a403c3f8f5ea9e475e0bf0b) )
ROM_END

ROM_START( simultanoa )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("yo1h_c12e_u3.u3",  0x0000, 0x8000, CRC(d583fdb4) SHA1(4be242691215ab1635a5d672441d339596f719c6) ) // AMI 27256
	ROM_LOAD("byo1h_c13b_u4.u4", 0x8000, 0x8000, CRC(c607b421) SHA1(b0c784b570dfd1fcbe3da68bcfbae2dae2957a74) ) // "

	ROM_REGION( 795951, "screen", 0 )
	ROM_LOAD("simultano.svg", 0, 795951, CRC(ac9942bb) SHA1(f9252e5bf7b8af698a403c3f8f5ea9e475e0bf0b) )
ROM_END

ROM_START( cc2150 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("yo1g_418_u3.u3",  0x8000, 0x8000, CRC(612dac24) SHA1(ba318f2ba34f9eb3df76a30c455bded76617bb11) ) // AMI 27512
	ROM_CONTINUE(               0x0000, 0x8000 )

	ROM_REGION( 795951, "screen", 0 )
	ROM_LOAD("simultano.svg", 0, 795951, CRC(ac9942bb) SHA1(f9252e5bf7b8af698a403c3f8f5ea9e475e0bf0b) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME        PARENT     COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY, FULLNAME, FLAGS
SYST( 1989, simultano,  0,         0,      simultano, simultano, simultano_state, empty_init, "Saitek / Heuristic Software", "Kasparov Simultano (ver. C)", MACHINE_SUPPORTS_SAVE )
SYST( 1989, simultanoa, simultano, 0,      simultano, simultano, simultano_state, empty_init, "Saitek / Heuristic Software", "Kasparov Simultano (ver. B)", MACHINE_SUPPORTS_SAVE )

SYST( 1988, cc2150,     simultano, 0,      cc2150,    cc2150,    simultano_state, empty_init, "Tandy Corporation / Saitek / Heuristic Software", "Chess Champion 2150", MACHINE_SUPPORTS_SAVE )



sis630.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Angelo Salese, R. Belmont
// thanks-to: Diego Nappino
/**************************************************************************************************

SiS 630 chipset based PC

TODO (main):
- Finalize Super I/O handling of ITE 8705F (I/O $2e / $2f)
\- fan monitor, cfr. I/O $294 reads in shutms11 BIOS fan tests;
\- FDC doesn't work, moans on first boot;
- '900 Ethernet PXE (missing ROM dump);
- USB controllers (OpenHCI compliant);
- ACPI is not fully lpc-acpi compliant;
- EISA slots;
- SMBus;
- PS/2 mouse is unstable, worked around by disabling and using a serial mouse instead.

TODO (usability, to be moved in a SW list):
- windows xp sp3: tests HW then does an ACPI devtrap write ($48), will eventually BSoD with
  ACPI STOP #a5 error with param $11
\- To bypass hold F7 while the "to install SCSI drivers [...] press F6" appears.
   And by F7 I really mean it :shrug:

- windows xp sp3: BSoD during install with a STOP #0a IRQL_NOT_LESS_OR_EQUAL;

- windows neptune: BSoD during ethernet check (after time clock setup)
  with a STOP #a0 INTERNAL_POWER_ERROR with param1 0x5 ("reserved"!?)

- gamecstl Kontron BIOS:
\- hangs at PC=0xf3cf2, again wanting a SMI# from devtrap_en_w;
\- No PS/2 inputs;

- gamecstl dump (tested from shutms11, also see notes below):
\- Currently black screens before booting normal Windows, reading $5004 from the LPC ACPI
   (flip EAX to non-zero to bypass).
   NB: it also writes to $5048 once (devtrap_en_w), which should generate a SMI# event;
\- Doesn't accept any PS/2 input, tries to install a "PCI standard CPU Host Bridge" (?),
   hangs there;
\- GUI is never recognized no matter what, punts with DirectX not installed;

- xubuntu 6.10: throws several SCSIDEV unhandled $46 & $51 commands
  (get configuration/read disc information),
  eventually punts to prompt with a "can't access tty: job control turned off" (on live CD) or
  hangs at "Configuring network interfaces" (on actual install);

- xubuntu 10.10: stalls after '900 ethernet check;

- Haiku 0.1: hangs throwing an "unhandled READ TOC format 2",
  serial COM1 prints a "vm_mark_page_range_inuse: page 0x9f in non-free state 7!"

- Red Hat 6.2: Triple Faults on x87 exception check
\- prints the type of mounted floating point exception if bypassed.

Notes on possible shutms11 BIOS bugs:
- BeOS 5 hardwires serial mouse checks at $2e8, ignoring BIOS PnP.
- FreeDOS 1.3 ctmouse often fails serial detection (will work if you load it at prompt)
- BIOS in general often throws serial conflicts when changing port setups,
  even on perfectly valid combinations (i.e. COM1 assigned while COM2 is *disabled*)
- PCI device listing often forgets to list print ACPI irq.

===================================================================================================

    Cristaltec "Game Cristal" (MAME bootleg)

    Skeleton driver by R. Belmont, based on taitowlf.cpp by Ville Linde

    Notes:
    - Specs: P3-866, SiS 630 motherboard + integrated graphics card,
      SiS 7018 sound, DirectX 8.1.
    - Image is just a more or less stock Windows 98SE with a customized shell
      "Ialoader.exe" (sic) to boot the frontend, located in C:\WINDOWS
      It will eventually hang after throwing a "DirectX missing" error.
    - In order to bypass the shell launcher, you should:
      1. edit C:\WINDOWS\system.ini and change shell property to explorer.exe
      2. remove the autoexec.bat contents, it will otherwise copy a bunch of .ini
         files from C:\dat to C:\WINDOWS, and replacing the system.ini shell launcher.
      Alternatively you can also execute "open.exe" from MS-DOS, that removes above customization
      too.
    - (gamecstl) Device Manager installed devices:
      - two Samsung SyncMaster 900SL monitors ('630 + '301?);
      - SiS 630 display adapter;
      - Samsung CD-ROM SC-152L;
      - SiS 5513 Dual PCI IDE Controller;
      - COM1, COM2, LPT1 enabled;
      - a QDI USBDisk USB Mass Storage SCSI driver;
      - a SiS 7001 PCI to USB Open Host Controller + USB root hub x 2;
    - C:\drvs contains a collection of drivers, mostly the ones described above.
    - C:\WINDOWS\temp has a couple footprints:
      1. has a Portuguese version of the Intel Processor Identification Utility,
         most likely used for binding the emulator to the CPU serial via CPUID;
      2. has u3spwd.exe (USB Flash Disk), likely used to copy the necessary files for
         making the frontend to work;
    - Input is via a custom COM1 port JAMMA adaptor.
    - The custom emulator is a heavily modified version of MAME32. If you extract the
      disk image, it's in C:\GH4\GH4.EXE. It's UPX compressed, so unpack it before doing
      any forensics. The emulator does run on Windows as new as XP Pro SP2 but you can't
      control it due to the lack of the custom input.
    - C:\GH4\mvs contains movie clips of the emulated games.
      These are MS-CRAM encoded, 288x208 at 20 fps, stereo MS ADPCM with 11025 Hz sample rate,
      36 seconds length.
      Mentioning this because SiS 630 has several HW registers dedicated to video playback,
      which will be most likely used once we get there.
    - C:\GH4\rdir contains filled NVRAM directory of the supported games.
      These are probably copied from a factory default (like skipping NVRAM errors in spang & mk),
      needs to be extensively checked if they can be flushed and given a working state with no
      arbitrary user data (i.e. the mk games sports about 3 hours of playtime each)

    Updates 27/11/2007 (Diego Nappino):
    The COM1 port is opened at 19200 bps, No parity, 8 bit data, 1 stop bit.
    The protocol is based on a 6 bytes frame with a leading byte valued 0x05 and a trailing one at 0x02
    The four middle bytes are used, in negative logic (0xFF = No button pressed), to implement the inputs.
    Each bit meaning as follows:

             Byte 1         Byte 2          Byte 3        Byte 4
    Bit 0    P1-Credit      P1-Button C     P2-Left        UNUSED
    Bit 1    P1-Start       P1-Button D     P2-Right       UNUSED
    Bit 2    P1-Down        P1-Button E     P2-Button A    SERVICE
    Bit 3    P1-Up          TEST            P2-Button B    UNUSED
    Bit 4    P1-Left        P2-Credit       P2-Button C    UNUSED
    Bit 5    P1-Right       P2-Start        P2-Button D    UNUSED
    Bit 6    P1-Button A    P2-Down         P2-Button E    UNUSED
    Bit 7    P1-Button B    P2-Up           VIDEO-MODE     UNUSED

    The JAMMA adaptor sends a byte frame each time an input changes.
    So for example, if the P1-Button A and P1-Button B are both pressed, it will send:

    0x05 0xFC 0xFF 0xFF 0xFF 0x02

    And when the buttons are both released

    0x05 0xFF 0xFF 0xFF 0xFF 0x02

    CPUID info:

    Original set:
    CPUID Level:       EAX:           EBX:           ECX:           EDX:
    00000000       00000003       756E6547       6C65746E       49656E69
    00000001       0000068A       00000002       00000000       0387F9FF
    00000002       03020101       00000000       00000000       0C040882
    00000003       00000000       00000000       CA976D2E       000082F6
    80000000       00000000       00000000       CA976D2E       000082F6
    C0000000       00000000       00000000       CA976D2E       000082F6

    Version 2:
    CPUID Level:       EAX:           EBX:           ECX:           EDX:
    00000000       00000003       756E6547       6C65746E       49656E69
    00000001       0000068A       00000002       00000000       0387F9FF
    00000002       03020101       00000000       00000000       0C040882
    00000003       00000000       00000000       B8BA1941       00038881
    80000000       00000000       00000000       B8BA1941       00038881
    C0000000       00000000       00000000       B8BA1941       00038881


    The JAMMA adaptor is a small external PCB from Azkoyen with an MCU (unknown type):
                                _______________         _______
     ____--__-- _______________|   DB-25      |________| DB-9 |_____
    |   |__||__| <-Jacks       |______________|        |______|    |
    | _________                                               ___  |
    ||o o o o |<-Power                     ________________  |  |  |
    |                                     | MCU           |  | <-MAX232N
    |                                     |_______________|  |__|  |
    |                                      _________   Xtal        |
    |                                     |DIPS x 8|   6 MHz       |
    |                                                              |
    |    Test sw->(o)         _________   _________   _________    |
    | Service sw->(o)         SN74HC245N  SN74HC245N  SN74HC245N   |
    |_________                                          ___________|
             |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
                               JAMMA

**************************************************************************************************/

#include "emu.h"
#include "cpu/i386/i386.h"
#include "bus/isa/isa_cards.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/rs232/hlemouse.h"
#include "bus/rs232/null_modem.h"
#include "bus/rs232/rs232.h"
#include "bus/rs232/sun_kbd.h"
#include "bus/rs232/terminal.h"
#include "machine/intelfsh.h"
#include "machine/it8705f.h"
#include "machine/pci.h"
#include "machine/sis5513_ide.h"
#include "machine/sis630_host.h"
#include "machine/sis630_gui.h"
#include "machine/sis7001_usb.h"
#include "machine/sis7018_audio.h"
#include "machine/sis900_eth.h"
#include "machine/sis950_lpc.h"
#include "machine/sis950_smbus.h"


namespace {

class sis630_state : public driver_device
{
public:
	sis630_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ide_00_1(*this, "pci:00.1")
		, m_lpc_01_0(*this, "pci:01.0")
	{ }

	void sis630(machine_config &config);

	void asuspolo(machine_config &config);
	void asuscusc(machine_config &config);
	void gamecstl(machine_config &config);
	void zidav630e(machine_config &config);

private:

	required_device<pentium3_device> m_maincpu;
	required_device<sis5513_ide_device> m_ide_00_1;
	required_device<sis950_lpc_device> m_lpc_01_0;

//  void main_io(address_map &map) ATTR_COLD;
//  void main_map(address_map &map) ATTR_COLD;
	static void ite_superio_config(device_t *device);
};


static INPUT_PORTS_START(sis630)
INPUT_PORTS_END

static void isa_com(device_slot_interface &device)
{
	device.option_add("microsoft_mouse", MSFT_HLE_SERIAL_MOUSE);
	device.option_add("logitech_mouse", LOGITECH_HLE_SERIAL_MOUSE);
	device.option_add("wheel_mouse", WHEEL_HLE_SERIAL_MOUSE);
	device.option_add("msystems_mouse", MSYSTEMS_HLE_SERIAL_MOUSE);
	device.option_add("rotatable_mouse", ROTATABLE_HLE_SERIAL_MOUSE);
	device.option_add("terminal", SERIAL_TERMINAL);
	device.option_add("null_modem", NULL_MODEM);
	device.option_add("sun_kbd", SUN_KBD_ADAPTOR);
}

static void isa_internal_devices(device_slot_interface &device)
{
	device.option_add("it8705f", IT8705F);
}

void sis630_state::ite_superio_config(device_t *device)
{
	it8705f_device &fdc = *downcast<it8705f_device *>(device);
//  fdc.set_sysopt_pin(1);
	fdc.irq1().set(":pci:01.0", FUNC(sis950_lpc_device::pc_irq1_w));
	fdc.irq8().set(":pci:01.0", FUNC(sis950_lpc_device::pc_irq8n_w));
	fdc.txd1().set(":serport0", FUNC(rs232_port_device::write_txd));
	fdc.ndtr1().set(":serport0", FUNC(rs232_port_device::write_dtr));
	fdc.nrts1().set(":serport0", FUNC(rs232_port_device::write_rts));
	fdc.txd2().set(":serport1", FUNC(rs232_port_device::write_txd));
	fdc.ndtr2().set(":serport1", FUNC(rs232_port_device::write_dtr));
	fdc.nrts2().set(":serport1", FUNC(rs232_port_device::write_rts));
}

void sis630_state::sis630(machine_config &config)
{
	// Slot 1/Socket 370, Coppermine FC-PGA @ 500~850+/100 MHz or Celeron PPGA 300~600+ MHz
	// TODO: lowered rate for debugging aid, needs a slot option anyway
	PENTIUM3(config, m_maincpu, 100'000'000);
//  m_maincpu->set_addrmap(AS_PROGRAM, &sis630_state::main_map);
//  m_maincpu->set_addrmap(AS_IO, &sis630_state::main_io);
	m_maincpu->set_irq_acknowledge_callback("pci:01.0:pic_master", FUNC(pic8259_device::inta_cb));
//  m_maincpu->smiact().set("pci:00.0", FUNC(sis950_lpc_device::smi_act_w));

	// TODO: unknown flash ROM types
	// Needs a $80000 sized ROM
	AMD_29F400T(config, "flash");

	PCI_ROOT(config, "pci", 0);
	// up to 512MB, 2 x DIMM sockets
	SIS630_HOST(config, "pci:00.0", 0, "maincpu", 256*1024*1024);
	SIS5513_IDE(config, m_ide_00_1, 0, "maincpu");
	// TODO: both on same line as default, should also trigger towards LPC
	m_ide_00_1->irq_pri().set("pci:01.0:pic_slave", FUNC(pic8259_device::ir6_w));
		//FUNC(sis950_lpc_device::pc_irq14_w));
	m_ide_00_1->irq_sec().set("pci:01.0:pic_slave", FUNC(pic8259_device::ir7_w));
		//FUNC(sis950_lpc_device::pc_mirq0_w));

	SIS950_LPC(config, m_lpc_01_0, XTAL(33'000'000), "maincpu", "flash");
	m_lpc_01_0->fast_reset_cb().set([this] (int state) {
		if (state)
			machine().schedule_soft_reset();
	});
	LPC_ACPI(config, "pci:01.0:acpi", 0);
	SIS950_SMBUS(config, "pci:01.0:smbus", 0);

	SIS900_ETH(config, "pci:01.1", 0);
	// USB config: 2 on back, 3 on front. Front is fn 2
	SIS7001_USB(config, "pci:01.2", 0, 3);
	SIS7001_USB(config, "pci:01.3", 0, 2);
	SIS7018_AUDIO(config, "pci:01.4", 0);
	// documentation doesn't mention modem part #, derived from Shuttle MS11 MB manual
//  SIS7013_MODEM_AC97(config, "pci:01.6"

	// "Virtual PCI-to-PCI Bridge"
	SIS630_BRIDGE(config, "pci:02.0", 0, "pci:02.0:00.0");
	// GUI must go under the virtual bridge
	// This will be correctly identified as bus #1-dev #0-func #0 by the Award BIOS
	SIS630_GUI(config, "pci:02.0:00.0", 0);

	// optional stuff (according to Kontron 786LCD manual)
//  "pci:08.0" SCSI controller (vendor=1000 NCR / LSI Logic / Symbios Logic device=0012 53C895A)
//  "pci:09.0" IEEE1394 controller (vendor=1033 NEC device=00ce uPD72872 / μPD72872)

	// TODO: 3 expansion PCI slots (PC104+)
	// "pci:09.x" to "pci:12.x"?
	// (PIC-MG)
	// "pci:20.x" to "pci:17.x"?

	// TODO: 1 parallel + 2 serial ports
	// TODO: 1 game port ('7018?)

	// TODO: move in MB implementations
	// (some unsupported variants uses W83697HF, namely Gigabyte GA-6SMZ7)
	ISA16_SLOT(config, "superio", 0, "pci:01.0:isabus", isa_internal_devices, "it8705f", true).set_option_machine_config("it8705f", ite_superio_config);

	rs232_port_device& serport0(RS232_PORT(config, "serport0", isa_com, "microsoft_mouse"));
	serport0.rxd_handler().set("superio:it8705f", FUNC(it8705f_device::rxd1_w));
	serport0.dcd_handler().set("superio:it8705f", FUNC(it8705f_device::ndcd1_w));
	serport0.dsr_handler().set("superio:it8705f", FUNC(it8705f_device::ndsr1_w));
	serport0.ri_handler().set("superio:it8705f", FUNC(it8705f_device::nri1_w));
	serport0.cts_handler().set("superio:it8705f", FUNC(it8705f_device::ncts1_w));

	rs232_port_device &serport1(RS232_PORT(config, "serport1", isa_com, nullptr));
	serport1.rxd_handler().set("superio:it8705f", FUNC(it8705f_device::rxd2_w));
	serport1.dcd_handler().set("superio:it8705f", FUNC(it8705f_device::ndcd2_w));
	serport1.dsr_handler().set("superio:it8705f", FUNC(it8705f_device::ndsr2_w));
	serport1.ri_handler().set("superio:it8705f", FUNC(it8705f_device::nri2_w));
	serport1.cts_handler().set("superio:it8705f", FUNC(it8705f_device::ncts2_w));

	// TODO: AMR (Audio/modem riser) + UPT (Panel Link-TV out), assume [E]ISA compliant, needs specific slot options
//  ISA16_SLOT(config, "isa1", 0, "pci:01.0:isabus", pc_isa16_cards, nullptr, false);
//  ISA16_SLOT(config, "isa2", 0, "pci:01.0:isabus", pc_isa16_cards, nullptr, false);
}

void sis630_state::asuspolo(machine_config &config)
{
	sis630_state::sis630(config);

	// one expansion PCI only
	// SiS950 rebadged as ITE chip (unreadable on available photo)
}

void sis630_state::asuscusc(machine_config &config)
{
	sis630_state::sis630(config);

	// 2x expansion PCIs
	// "ASUS Mozart"
}

void sis630_state::zidav630e(machine_config &config)
{
	sis630_state::sis630(config);

	// SiS630E
	// 3x expansion PCIs
	// ITE 8705F (Super I/O)
	// Winbond chip nearby with unreadable marking

	// Max allowed CPU 333 MHz (according to AIDA16)
}

// Kontron 786LCD/3.5 based
void sis630_state::gamecstl(machine_config &config)
{
	sis630_state::sis630(config);
	// TODO: Actually Celeron, as also stated by the BIOS
	PENTIUM3(config.replace(), m_maincpu, 100'000'000);

	// tries to install '900 on Windows boot, which implies it doesn't have it
	// (leave it on for now since it has specific option in Setup BIOS)
	//config.device_remove("pci:01.1");

	// TODO: mapped RAM config
	// TODO: add custom inputs
	// TODO: eventually remove PS/2 connector defaults
//  subdevice<pc_kbdc_device>("pci:01.0:ps2_con")->set_default_option(nullptr);
//  subdevice<pc_kbdc_device>("pci:01.0:aux_con")->set_default_option(nullptr);
}

ROM_START(shutms11)
	ROM_REGION32_LE(0x80000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "ms11s11d", "ms11s11d")
	ROMX_LOAD( "ms11s11d.bin",     0x040000, 0x040000, CRC(27077a58) SHA1(32327ebf328cb0c2dec819c3710acc83527803c5), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "ms11s134", "ms11s134")
	ROMX_LOAD( "ms11s134.bin",     0x040000, 0x040000, CRC(d739c4f3) SHA1(2301e57163ac4d9b7eddcabce52fa7d01b22330e), ROM_BIOS(1) )
ROM_END

ROM_START(asuspolo)
	ROM_REGION32_LE(0x80000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "polotv", "Polo-TV 1009")
	ROMX_LOAD( "potv1009.awd",     0x040000, 0x040000, CRC(981e1c75) SHA1(0e1cd42ad62fca63e4919c708348ce18947faaa4), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "polo",   "Polo 1011.001 (beta)")
	ROMX_LOAD( "1011efx.001",      0x040000, 0x040000, CRC(00d73848) SHA1(b2b4ed8e9ec10b853dfdabe1af580b01983864fc), ROM_BIOS(1) )
ROM_END

ROM_START(asuscusc)
	ROM_REGION32_LE(0x80000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "cusc",        "Cusc 1009")
	ROMX_LOAD( "cusc1009.awd",     0x040000, 0x040000, CRC(f7d8cab9) SHA1(47e7728d487a8105de1bc0eeb58a603e334304c0), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "cusc_beta",   "Cusc 1011.001 (beta)")
	ROMX_LOAD( "cusc1011.001",     0x040000, 0x040000, CRC(c2935b70) SHA1(8dedfc7423ebbee5dbe3af3ad92cd0f9866ca876), ROM_BIOS(1) )
ROM_END

ROM_START(zidav630e)
	ROM_REGION32_LE(0x80000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "award_v108",  "Award BIOS v1.08")
	ROMX_LOAD( "v630108e.bin",     0x040000, 0x040000, CRC(25c91274) SHA1(95ff37ad0cfb39bb4ceff2db1cd47f13849ea53a), ROM_BIOS(0) )
	// Corrupt file
//  ROM_SYSTEM_BIOS(1, "award_v104",  "Award BIOS v1.04")
//  ROMX_LOAD( "V630e104.bin",     0x040000, 0x040000, CRC(?) SHA1(?), ROM_BIOS(1) )
ROM_END


// GameCristal - PC-based multigame arcade (with an unknown emulator).

ROM_START(gamecstl)
	ROM_REGION32_LE(0x80000, "flash", ROMREGION_ERASEFF )
	// from gamecstl HDD dump, under "C:\drvs\bios\bios1_9"
	ROM_LOAD( "prod19.rom",     0x040000, 0x040000, BAD_DUMP CRC(9262306c) SHA1(5cd805622ecb4d326591b5f2cf918fe5cb1bce8e) )
	ROM_CONTINUE(               0x000000, 0x040000 )

	DISK_REGION( "pci:00.1:ide1:0:hdd" )
	DISK_IMAGE( "gamecstl", 0, SHA1(b431af3c42c48ba07972d77a3d24e60ee1e4359e) )

	ROM_REGION( 0x2000, "mcu", 0 )
	ROM_LOAD( "gamecristal_datasat.bin", 0x0000, 0x2000, NO_DUMP ) // MCU on the JAMMA interface PCB, unknown type and ROM size
ROM_END

ROM_START(gamecst2)
	ROM_REGION32_LE(0x80000, "pci:01.0:flash", ROMREGION_ERASEFF )
	ROM_LOAD( "prod19.rom",     0x040000, 0x040000, BAD_DUMP CRC(9262306c) SHA1(5cd805622ecb4d326591b5f2cf918fe5cb1bce8e) )
	ROM_CONTINUE(               0x000000, 0x040000 )

	DISK_REGION( "pci:00.1:ide1:0:hdd" )
	DISK_IMAGE( "gamecst2", 0, SHA1(14e1b311cb474801c7bdda3164a0c220fb102159) )

	ROM_REGION( 0x2000, "mcu", 0 )
	ROM_LOAD( "gamecristal_datasat.bin", 0x0000, 0x2000, NO_DUMP ) // MCU on the JAMMA interface PCB, unknown type and ROM size
ROM_END

} // anonymous namespace


COMP( 2000, shutms11,  0, 0, sis630,   sis630, sis630_state, empty_init, "Shuttle", "MS11 PC (SiS630 chipset)",                 MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
COMP( 2001, asuspolo,  0, 0, asuspolo, sis630, sis630_state, empty_init, "Asus",    "Polo \"Genie\" (SiS630 chipset)",          MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // hangs at CMOS check first time, corrupts flash ROM on successive boots
COMP( 2001, asuscusc,  0, 0, asuscusc, sis630, sis630_state, empty_init, "Asus",    "Terminator P-3 \"Cusc\" (SiS630 chipset)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // fails CMOS test, does crc with I/O accesses at $c00
COMP( 2001, zidav630e, 0, 0, zidav630e,sis630, sis630_state, empty_init, "Zida",    "V630E Baby AT (SiS630 chipset)",           MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // Flash ROM corrupts often, otherwise same-y as shutms11


// Arcade based games
GAME( 2002, gamecstl,  0,        gamecstl, sis630, sis630_state, empty_init, ROT0, "Cristaltec", "GameCristal",                 MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
GAME( 2002, gamecst2,  gamecstl, gamecstl, sis630, sis630_state, empty_init, ROT0, "Cristaltec", "GameCristal (version 2.613)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )



sitcom.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Vas Crabb
/***************************************************************************

    SITCOM (known as Sitcom, Sitcom85, Sitcom8085)

    25/09/2011 Driver [Robbbert]

    http://www.sitcom.tk/
    http://www.sbprojects.net/projects/izabella/html/sitcom_.html

    The display consists of a LED connected to SOD, and a pair of DL1414
    intelligent alphanumeric displays.

    The idea of this device is that you write a 8085 program with an
    assembler on your PC.  You then compile it, and then send it to the
    SITCOM via a serial cable. The program then (hopefully) runs on the
    SITCOM.  With the 8255 expansion, you could wire up input devices or
    other hardware for your program to use.

    The SOD LED blinks slowly while waiting; stays on while downloading;
    and blinks quickly if an error occurs.

    After a successful upload, hit the Reset button to mirror RAM into
    the low 32kB of the address space in place of ROM and run the
    program.

    The null modem bitbanger should be configured for 9600 8N1 to upload
    a program.  The data should be in Intel HEX format.

****************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/i8085/i8085.h"
#include "imagedev/bitbngr.h"
#include "machine/clock.h"
#include "machine/i8255.h"
#include "video/dl1416.h"

#include "softlist_dev.h"

#include "sitcom.lh"
#include "sitcomtmr.lh"

#include <cmath>


namespace {

class sitcom_state : public driver_device
{
public:
	sitcom_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_buttons(*this, "BUTTONS")
		, m_maincpu(*this, "maincpu")
		, m_digits(*this, "digit%u", 0U)
		, m_leds(*this, "p%c%u", unsigned('a'), 0U)
		, m_bootstrap(*this, "bootstrap")
		, m_rxd(true)
	{
	}

	void sitcom(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(update_buttons);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual void update_ppi_pa(uint8_t data);
	virtual void update_ppi_pb(uint8_t data);

	void sitcom_mem(address_map &map) ATTR_COLD;
	void sitcom_io(address_map &map) ATTR_COLD;

	template <unsigned D> void update_ds(offs_t offset, uint16_t data) { m_digits[(D << 2) | offset] = data; }
	void update_rxd(int state)          { m_rxd = bool(state); }
	int sid_line()                      { return m_rxd ? 1 : 0; }

	static void sitcom_null_modem(device_t *device)
	{
		device->subdevice<bitbanger_device>("stream")->set_interface("rs232");
	}

	required_ioport                          m_buttons;
	required_device<i8085a_cpu_device>       m_maincpu;
	output_finder<15>                        m_digits;
	output_finder<2, 8>                      m_leds;
	memory_view                              m_bootstrap;

	bool m_rxd;
};


class sitcom_timer_state : public sitcom_state
{
public:
	sitcom_timer_state(const machine_config &mconfig, device_type type, const char *tag)
		: sitcom_state(mconfig, type, tag)
		, m_speed(*this, "SPEED")
		, m_ppi(*this, "ppi")
		, m_ds2(*this, "ds2")
		, m_test_led(*this, "test_led")
		, m_dac(*this, "dac")
		, m_shutter_timer(nullptr)
		, m_shutter(false)
		, m_dac_cs(true)
		, m_dac_wr(true)
	{
	}

	int shutter_r();
	DECLARE_INPUT_CHANGED_MEMBER(update_shutter);
	DECLARE_INPUT_CHANGED_MEMBER(update_speed);

	void sitcomtmr(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual void update_ppi_pa(uint8_t data) override;
	virtual void update_ppi_pb(uint8_t data) override;

	TIMER_CALLBACK_MEMBER(shutter_tick);

	void update_dac(uint8_t value);

	required_ioport                 m_speed;
	required_device<i8255_device>   m_ppi;
	required_device<dl1414_device>  m_ds2;
	output_finder<>                 m_test_led;
	output_finder<>                 m_dac;
	emu_timer                       *m_shutter_timer;

	bool                            m_shutter;
	bool                            m_dac_cs, m_dac_wr;
};


void sitcom_state::sitcom_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).mirror(0x8000).ram();
	map(0x0000, 0x7fff).view(m_bootstrap);
	m_bootstrap[0](0x0000, 0x7fff).unmapw().rom().region("bootstrap", 0);
}

void sitcom_state::sitcom_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x03).mirror(0x1c).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xc0, 0xc3).mirror(0x1c).w("ds0", FUNC(dl1414_device::bus_w));
	map(0xe0, 0xe3).mirror(0x1c).w("ds1", FUNC(dl1414_device::bus_w));
}


INPUT_PORTS_START( sitcom )
	PORT_START("BUTTONS")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Boot")  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sitcom_state::update_buttons), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Reset") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sitcom_state::update_buttons), 0)

	PORT_START("PORTC")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("0") PORT_CODE(KEYCODE_0)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("2") PORT_CODE(KEYCODE_2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("3") PORT_CODE(KEYCODE_3)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("6") PORT_CODE(KEYCODE_6)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("7") PORT_CODE(KEYCODE_7)
INPUT_PORTS_END

INPUT_PORTS_START( sitcomtmr )
	PORT_INCLUDE(sitcom)

	PORT_MODIFY("BUTTONS")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Shutter") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sitcom_timer_state::update_shutter), 0)

	PORT_MODIFY("PORTC")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Grey")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Blue")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(sitcom_timer_state::shutter_r))
	PORT_BIT( 0xf8, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("SPEED")
	PORT_CONFNAME(0xff, 0x1e, "Shutter Speed") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sitcom_timer_state::update_speed), 0)
	PORT_CONFSETTING(0x00, "B")
	PORT_CONFSETTING(0x01, "1")
	PORT_CONFSETTING(0x02, "1/2")
	PORT_CONFSETTING(0x04, "1/4")
	PORT_CONFSETTING(0x08, "1/8")
	PORT_CONFSETTING(0x0f, "1/15")
	PORT_CONFSETTING(0x1e, "1/30")
	PORT_CONFSETTING(0x3c, "1/60")
	PORT_CONFSETTING(0x7d, "1/125")
INPUT_PORTS_END


void sitcom_state::machine_start()
{
	m_digits.resolve();
	m_leds.resolve();

	save_item(NAME(m_rxd));

	m_rxd = true;
}

void sitcom_state::machine_reset()
{
	m_bootstrap.select(0);
}

void sitcom_state::update_ppi_pa(uint8_t data)
{
	for (int i = 0; 8 > i; ++i)
		m_leds[0][i] = BIT(data, i);
}

void sitcom_state::update_ppi_pb(uint8_t data)
{
	for (int i = 0; 8 > i; ++i)
		m_leds[1][i] = BIT(data, i);
}

INPUT_CHANGED_MEMBER( sitcom_state::update_buttons )
{
	bool const boot(BIT(m_buttons->read(), 0));
	bool const reset(BIT(m_buttons->read(), 1));

	m_maincpu->set_input_line(INPUT_LINE_RESET, (boot || reset) ? ASSERT_LINE : CLEAR_LINE);

	if (boot)
		m_bootstrap.select(0);
	else if (reset)
		m_bootstrap.disable();
}


void sitcom_timer_state::update_ppi_pa(uint8_t data)
{
	if (!m_dac_cs && !m_dac_wr)
		update_dac(data);

	m_ds2->data_w(data & 0x7f);
}

void sitcom_timer_state::update_ppi_pb(uint8_t data)
{
	if (!m_dac_cs && !BIT(data, 0))
		update_dac(m_ppi->pa_r());
	m_dac_wr = BIT(data, 0);
	m_dac_cs = BIT(data, 1);

	m_ds2->wr_w(BIT(data, 2));
	m_ds2->addr_w(bitswap<2>(data, 3, 4));
	m_test_led = BIT(data, 5);
}

int sitcom_timer_state::shutter_r()
{
	return m_shutter ? 0 : 1;
}

INPUT_CHANGED_MEMBER( sitcom_timer_state::update_shutter )
{
	ioport_value const speed(m_speed->read());
	if (!speed)
	{
		m_shutter = bool(newval);
	}
	else if (!m_shutter && newval)
	{
		m_shutter = true;
		m_shutter_timer->adjust(attotime::from_hz(speed));
	}
}

INPUT_CHANGED_MEMBER( sitcom_timer_state::update_speed )
{
	if (!newval)
	{
		m_shutter = bool(BIT(m_buttons->read(), 2));
	}
	else if (!oldval)
	{
		m_shutter = false;
	}
}

TIMER_CALLBACK_MEMBER(sitcom_timer_state::shutter_tick)
{
	m_shutter = false;
}

void sitcom_timer_state::machine_start()
{
	sitcom_state::machine_start();

	m_test_led.resolve();
	m_dac.resolve();

	m_shutter_timer = timer_alloc(FUNC(sitcom_timer_state::shutter_tick), this);

	save_item(NAME(m_shutter));
	save_item(NAME(m_dac_cs));
	save_item(NAME(m_dac_wr));

	m_shutter = false;
	m_dac_cs = true;
	m_dac_wr = true;
}

void sitcom_timer_state::machine_reset()
{
	sitcom_state::machine_reset();
}

void sitcom_timer_state::update_dac(uint8_t value)
{
	// supposed to be a DAC and an analog meter, but that's hard to do with internal layouts
	m_dac = value;
	constexpr u8 s_7seg[10] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f };
	m_digits[12] = s_7seg[value % 10];
	value /= 10;
	m_digits[13] = s_7seg[value % 10];
	value /= 10;
	m_digits[14] = s_7seg[value % 10] | 0x80;
}


void sitcom_state::sitcom(machine_config &config)
{
	// basic machine hardware
	I8085A(config, m_maincpu, 6.144_MHz_XTAL); // 3.072MHz can be used for an old slow 8085
	m_maincpu->set_addrmap(AS_PROGRAM, &sitcom_state::sitcom_mem);
	m_maincpu->set_addrmap(AS_IO, &sitcom_state::sitcom_io);
	m_maincpu->in_sid_func().set(FUNC(sitcom_state::sid_line));
	m_maincpu->out_sod_func().set_output("sod_led");

	CLOCK(config, "100hz", 100).signal_handler().set_inputline("maincpu", I8085_RST75_LINE);

	i8255_device &ppi(I8255(config, "ppi"));
	ppi.out_pa_callback().set(FUNC(sitcom_state::update_ppi_pa));
	ppi.out_pb_callback().set(FUNC(sitcom_state::update_ppi_pb));
	ppi.in_pc_callback().set_ioport("PORTC");

	// video hardware
	DL1414T(config, "ds0", u32(0)).update().set(FUNC(sitcom_state::update_ds<0>)); // left display
	DL1414T(config, "ds1", u32(0)).update().set(FUNC(sitcom_state::update_ds<1>)); // right display

	// host interface
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "null_modem"));
	rs232.rxd_handler().set(FUNC(sitcom_state::update_rxd));
	rs232.set_option_machine_config("null_modem", sitcom_null_modem);

	SOFTWARE_LIST(config, "bitb_list").set_original("sitcom");
	config.set_default_layout(layout_sitcom);
}

void sitcom_timer_state::sitcomtmr(machine_config &config)
{
	sitcom(config);
	DL1414T(config, m_ds2, u32(0)).update().set(FUNC(sitcom_timer_state::update_ds<2>)); // remote display
	config.set_default_layout(layout_sitcomtmr);
}


ROM_START( sitcom )
	ROM_REGION( 0x8000, "bootstrap", ROMREGION_ERASEFF )
	ROM_LOAD( "boot8085.bin", 0x0000, 0x06b8, CRC(1b5e3310) SHA1(3323b65f0c10b7ab6bb75ec824e6d5fb643693a8) )
ROM_END

ROM_START( sitcomtmr )
	ROM_REGION( 0x8000, "bootstrap", ROMREGION_ERASEFF )
	ROM_LOAD( "boot8085.bin", 0x0000, 0x06b8, CRC(1b5e3310) SHA1(3323b65f0c10b7ab6bb75ec824e6d5fb643693a8) )
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT      CLASS               INIT        COMPANY                            FULLNAME        FLAGS */
COMP( 2002, sitcom,    0,      0,      sitcom,    sitcom,    sitcom_state,       empty_init, "San Bergmans & Izabella Malcolm", "SITCOM",       MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW)
COMP( 2002, sitcomtmr, sitcom, 0,      sitcomtmr, sitcomtmr, sitcom_timer_state, empty_init, "San Bergmans & Izabella Malcolm", "SITCOM Timer", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW)



sixtrak.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:m1macrophage

/*
The Sequential Circuits Six-Trak (aka Model 610) is a 6-voice, MIDI-enabled,
digitally-controlled analog synthesizer.

Each voice is built around a CEM3394, a single-oscillator "synth-on-a-chip".
Control voltages (CVs) for each CEM3394 are generated independently, for a
6-part multitimbrality. A single noise source can be mixed in every voice.
Modulation sources (e.g. envelope generators) for the VCA and VCF are computed
by the firmware and incorporated into the CVs. Because of this, the sample &
hold (S&H) circuits for the VCA amplitude and VCF frequency include an
additional RC circuit to smoothen the CV changes.

The synth's computer is built around a Z80. It implements the user interface
(scans for key presses and controls LEDs), implements a sequencer, responds to
and controls MIDI, periodically refreshes CVs for each of the 6 voices, and
takes care of tuning.

CVs are generated by a 12-bit DAC (0V - -4V). A MUX selects between the DAC
output and its inverted output (0V - 4V), resulting in a 13-bit resolution.
However, the full 13-bit resolution is only used for a few CVs. Most CVs just
use the 6 MSbits of the DAC.

A set of MUXes route the generated CV to the appropriate voice and parameter.
Each of the 6 voice chips (CEM3394) has 8 CV-controlled parameters:
- VCO pitch.
- VCA amplitude.
- VCF resonance.
- VCF cutoff frequency.
- Noise mix.
- VCF cutoff frequency modulation, by the triangle output of the VCO.
- VCO pulse width.
- VCO shape.

Known hardware revisions:
- Model 610 Rev A: serial numbers 1-900.
- Model 610 Rev B: serial numbers 901-?.
    Changes to autotune circuit: disconnect U146-2 from whatever it was
    connected to (not sure what), and connect it to U146-6.
    Unsure if there are other changes.

This driver is based on the service manual for the Six-Trak (newer than Rev A,
probably for Rev B), and is intended as an educational tool.
*/

#include "emu.h"
#include "bus/midi/midi.h"
#include "cpu/z80/z80.h"
#include "machine/6850acia.h"
#include "machine/7474.h"
#include "machine/clock.h"
#include "machine/nvram.h"
#include "machine/output_latch.h"
#include "machine/pit8253.h"
#include "machine/rescap.h"

#define LOG_CV              (1U << 1)
#define LOG_KEYS            (1U << 2)
#define LOG_ADC_VALUE_KNOB  (1U << 3)
#define LOG_ADC_PITCH_WHEEL (1U << 4)

#define VERBOSE (LOG_GENERAL | LOG_CV)
//#define LOG_OUTPUT_FUNC osd_printf_info

#include "logmacro.h"

namespace {

class sixtrak_state : public driver_device
{
public:
	sixtrak_state(const machine_config &mconfig, device_type type, const char *tag) ATTR_COLD;

	void sixtrak(machine_config &config) ATTR_COLD;

protected:
	void machine_start() override ATTR_COLD;

private:
	double get_dac_v(bool inverted) const;
	double get_voltage_mux_out() const;
	void update_cvs();

	void dac_low_w(u8 data);
	void dac_high_w(u8 data);
	void voice_select_w(u8 data);
	void param_select_w(u8 data);
	void digit_w(u8 data);
	u8 misc_r();
	u8 keys_r();

	u64 iorq_wait_state(offs_t offset, u64 current_time);
	void memory_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_ioport_array<16> m_keys;
	required_ioport m_footswitch;
	required_ioport m_pitch_wheel;
	required_ioport m_mod_wheel;
	required_ioport m_track_vol_knob;
	required_ioport m_speed_knob;
	required_ioport m_value_knob;
	required_ioport m_tune_knob;
	output_finder<> m_left_digit;
	output_finder<> m_right_digit;

	u8 m_key_row = 0;
	u16 m_dac_value = 0;
	u8 m_sh_voices = 0x3f;
	u8 m_sh_param = 0;
	u8 m_voltage_mux_input = 0;
	std::array<std::array<double, 8>, 6> m_cvs;  // 6 voices x 8 parameters.
};

sixtrak_state::sixtrak_state(const machine_config &mconfig, device_type type, const char *tag)
	: driver_device(mconfig, type, tag)
	, m_maincpu(*this, "maincpu")
	, m_keys(*this, "key_row_%u", 0U)
	, m_footswitch(*this, "footswitch")
	, m_pitch_wheel(*this, "pitch_wheel")
	, m_mod_wheel(*this, "mod_wheel")
	, m_track_vol_knob(*this, "track_volume_knob")
	, m_speed_knob(*this, "speed_knob")
	, m_value_knob(*this, "value_knob")
	, m_tune_knob(*this, "tune_knob")
	, m_left_digit(*this, "digit_left")
	, m_right_digit(*this, "digit_right")
{
	for (auto &voice_cvs : m_cvs)
		std::fill(voice_cvs.begin(), voice_cvs.end(), 0.0);
}

double sixtrak_state::get_dac_v(bool inverted) const
{
	// The 12-bit DAC (U110, 7541) and corresponding I2V converter (U156, LF411A)
	// produce a voltage in the range 0V (all data bits 0) to -4V (all data
	// bits 1). That voltage is inverted by U111A (5532) and surrounding
	// resistors.

	// A MUX (U108, 4051) selects between the non-inverted and inverted voltages
	// when updating CVs. This essentially adds 1 bit of precision to the
	// 12-bit DAC, for the CVs that need it. But most CVs just use the 6 most
	// significant bits of the DAC. The ADC comparator always compares against
	// the inverted (0V - 4V) voltage.

	constexpr double DAC_MAX_VOLTAGE = -4.0;
	constexpr double DAC_MAX_VALUE = double(make_bitmask<u16>(12));
	const double v = m_dac_value * DAC_MAX_VOLTAGE / DAC_MAX_VALUE;
	return inverted ? -v : v;
}

double sixtrak_state::get_voltage_mux_out() const
{
	// The pitch and mod wheel potentiometers (100K, linear) are attached to
	// ground on one side and a shared 27K resistor (R1208) to 5V on the other.
	// The pot wipers are attached to corresponding mux inputs.
	constexpr double WHEEL_MAX_V = 5.0 * RES_VOLTAGE_DIVIDER(RES_K(27), RES_2_PARALLEL(RES_K(100), RES_K(100)));

	// All knobs are 10K linear potentiometers, and each one is attached to
	// ground on one side and to a separate 2K resistor to 5V on the other. The
	// pot wipers are attached to corresponding mux inputs.
	constexpr double KNOB_MAX_V = 5.0 * RES_VOLTAGE_DIVIDER(RES_K(2), RES_K(10));

	// The voltage MUX (U108, CD4051) routes potentiometer voltages to the ADC
	// comparator and routes the appropriate DAC output to the CV S&H circuits.
	// The MUX's output is always enabled. The firmware controls whether the ADC
	// or an S&H circuit is activated.
	switch (m_voltage_mux_input)
	{
		case 0: return get_dac_v(false);
		case 1: return get_dac_v(true);
		case 2: return m_value_knob->read() * KNOB_MAX_V / 100.0;
		case 3: return m_tune_knob->read() * KNOB_MAX_V / 100.0;
		case 4: return m_mod_wheel->read() * WHEEL_MAX_V / 100.0;
		case 5: return m_track_vol_knob->read() * KNOB_MAX_V / 100.0;
		case 6: return m_pitch_wheel->read() * WHEEL_MAX_V / 100.0;
		case 7: return m_speed_knob->read() * KNOB_MAX_V / 100.0;
	}

	assert(false);  // Execution should not reach here.
	return 0;
}

void sixtrak_state::update_cvs()
{
	if ((m_sh_voices & 0x3f) == 0x3f)
		return;  // Exit early if no voice S&Hs are activated.

	const double cv = get_voltage_mux_out();
	for (int voice = 0; voice < 6; ++voice)
	{
		if (!BIT(m_sh_voices, voice))  // Active low.
		{
			assert(m_sh_param < 8);
			if (m_cvs[voice][m_sh_param] != cv)
			{
				m_cvs[voice][m_sh_param] = cv;
				LOGMASKED(LOG_CV, "CV - voice: %u, param: %u, cv: %f - %03x - %x\n",
						  voice, m_sh_param, cv, m_dac_value, m_voltage_mux_input);
			}
		}
	}
}

void sixtrak_state::dac_low_w(u8 data)
{
	// D2-D7 -> Latch U105 -> low 6 bits of 12-bit DAC (U110, 7541).
	m_dac_value = (m_dac_value & 0x0fc0) | ((data >> 2) & 0x3f);
	update_cvs();
}

void sixtrak_state::dac_high_w(u8 data)
{
	// D0-D5 -> Latch U104 -> high 6 bits of 12-bit DAC (U110, 7541).
	m_dac_value = (u16(data & 0x3f) << 6) | (m_dac_value & 0x003f);
	update_cvs();
}

void sixtrak_state::voice_select_w(u8 data)
{
	// D0-D5 -> Latch U120 -> INH input of 6 x 4051.
	// Selects which of the voice MUXes to activate.
	m_sh_voices = data & 0x3f;
	update_cvs();
}

void sixtrak_state::param_select_w(u8 data)
{
	// D0-D2 -> Latch U118 D0-D2 -> A, B, C inputs of all 6 S&H 4051.
	// Selects which voice parameter to activate.
	m_sh_param = data & 0x07;

	// D7-D5 -> Latch U118 D3-D5 -> A, B, C inputs of Voltage MUX 4051 (U110).
	m_voltage_mux_input = bitswap<3>(data, 5, 6, 7);

	// D4-D7 -> Decoder U155 (CD4514) -> key row select.
	m_key_row = (data >> 4) & 0x0f;

	update_cvs();
}

void sixtrak_state::digit_w(u8 data)
{
	static constexpr u8 PATTERNS_CD4511[0x10] =
	{
		0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7c, 0x07, 0x7f, 0x67, 0, 0, 0, 0, 0, 0
	};
	m_left_digit = PATTERNS_CD4511[BIT(data, 0, 4)];
	m_right_digit = PATTERNS_CD4511[BIT(data, 4, 4)];
}

u8 sixtrak_state::misc_r()
{
	// D0, D1 - autotune-related.
	// TODO: emulate autotune.
	const u8 d0 = 1;
	const u8 d1 = 1;

	// D2: ADC comparator.
	// If either of the MUX inputs B or C are high, they will activate the
	// ADC comparator's (U109, LM311) output pullup resistor (R168) via diodes
	// D128 and D129.
	const bool comp_enabled = BIT(m_voltage_mux_input, 1, 2);
	const u8 d2 = (comp_enabled && get_voltage_mux_out() >= get_dac_v(true)) ? 1 : 0;
	if (comp_enabled)
	{
		if (m_voltage_mux_input == 2)
		{
			LOGMASKED(LOG_ADC_VALUE_KNOB, "ADC value - pot v: %f, dac v: %f, comp: %d\n",
					  get_voltage_mux_out(), get_dac_v(true), d2);
		}
		else if (m_voltage_mux_input == 6)
		{
			LOGMASKED(LOG_ADC_PITCH_WHEEL, "ADC pitch - input: %d, pot v: %f, dac v: %f, comp: %d\n",
					  m_pitch_wheel->read(), get_voltage_mux_out(), get_dac_v(true), d2);
		}
	}

	// D3: Control footswitch.
	const u8 d3 = BIT(m_footswitch->read(), 0);

	return (d3 << 3) | (d2 << 2) | (d1 << 1) | d0;
}

u8 sixtrak_state::keys_r()
{
	u8 data = m_keys[m_key_row]->read();
	if (m_key_row < 4)
	{
		// The first 4 rows of the key matrix do not have protection diodes for
		// each key. If a key is pressed in the row being scanned, and one or
		// more keys in the same column (within rows 0-3) are pressed, there will
		// be a short between a high and low output of the CD4514 (assuming
		// the schematic is not missing diodes on those outputs), and the press
		// will likely not register. The first 4 rows are used for the synth's
		// buttons. The piano roll keys have the typical diodes and don't have
		// this issue. Furthermore, there are diodes protecting the rest of the
		// rows from rows 0-3.
		u8 inactive_row_presses = 0;
		for (int row = 0; row < 4; ++row)
			if (row != m_key_row)
				inactive_row_presses |= m_keys[row]->read();
		if (data & inactive_row_presses)
			LOGMASKED(LOG_KEYS, "Conflicting key presses: %02x %02x\n", data, inactive_row_presses);
		data = data & ~inactive_row_presses;
	}
	if (data)
		LOGMASKED(LOG_KEYS, "Key row: %d, value: %02x\n", m_key_row, data);
	return data;
}

u64 sixtrak_state::iorq_wait_state(offs_t offset, u64 current_time)
{
	// U123C and U123D (74LS02) will set /WAIT low when both /IORQ and the 2MHz
	// clock are low. So the 2MHz clock needs to be high for an IORQ to proceed.
	// The 2MHz clock is the CPU's clock (4MHz) divided by 2.
	return current_time | 1;
}

void sixtrak_state::memory_map(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	map(0x4000, 0x47ff).ram().share("nvram1");
	map(0x4800, 0x4fff).ram().share("nvram2");
	map(0x5000, 0x57ff).mirror(0x0800).ram().share("nvram3");
	map(0x6000, 0x6001).mirror(0x1ffe).w("midiacia", FUNC(acia6850_device::write));
	map(0xe000, 0xe001).mirror(0x1ffe).r("midiacia", FUNC(acia6850_device::read));
}

void sixtrak_state::io_map(address_map &map)
{
	map.global_mask(0xff);

	map(0x00, 0x03).mirror(0xf4).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write));

	map(0x09, 0x09).mirror(0xf6).r(FUNC(sixtrak_state::misc_r));
	map(0x0a, 0x0a).mirror(0xf5).r(FUNC(sixtrak_state::keys_r));

	map(0x08, 0x08).mirror(0xe0).w("led_latch_2", FUNC(output_latch_device::write));
	map(0x09, 0x09).mirror(0xf0).w(FUNC(sixtrak_state::dac_low_w));
	map(0x0a, 0x0a).mirror(0xf0).w(FUNC(sixtrak_state::dac_high_w));
	map(0x0b, 0x0b).mirror(0xf0).w("led_latch_0", FUNC(output_latch_device::write));
	map(0x0c, 0x0c).mirror(0xf0).w("led_latch_1", FUNC(output_latch_device::write));
	map(0x0d, 0x0d).mirror(0xf0).w("tune_latch", FUNC(output_latch_device::write));
	map(0x0e, 0x0e).mirror(0xf0).w(FUNC(sixtrak_state::voice_select_w));
	map(0x0f, 0x0f).mirror(0xf0).w(FUNC(sixtrak_state::param_select_w));
	map(0x18, 0x18).mirror(0xe0).w(FUNC(sixtrak_state::digit_w));
}

void sixtrak_state::machine_start()
{
	save_item(NAME(m_key_row));
	save_item(NAME(m_dac_value));
	save_item(NAME(m_sh_voices));
	save_item(NAME(m_sh_param));
	save_item(NAME(m_voltage_mux_input));
	save_item(NAME(m_cvs));

	m_left_digit.resolve();
	m_right_digit.resolve();

	m_maincpu->space(AS_IO).install_readwrite_before_time(
		0x00, 0xff, ws_time_delegate(*this, FUNC(sixtrak_state::iorq_wait_state)));
}

void sixtrak_state::sixtrak(machine_config &config)
{
	Z80(config, m_maincpu, 8_MHz_XTAL / 2);  // U134 (74LS93), QA.
	m_maincpu->set_addrmap(AS_PROGRAM, &sixtrak_state::memory_map);
	m_maincpu->set_addrmap(AS_IO, &sixtrak_state::io_map);

	NVRAM(config, "nvram1", nvram_device::DEFAULT_ALL_0);  // U119 (6116)
	NVRAM(config, "nvram2", nvram_device::DEFAULT_ALL_0);  // U117 (6116)
	NVRAM(config, "nvram3", nvram_device::DEFAULT_ALL_0);  // U112 (6116)

	auto &pit = PIT8253(config, "pit");  // U133
	pit.set_clk<2>(8_MHz_XTAL / 4);  // U134 (74LS93), QB.
	pit.out_handler<2>().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	// TODO: also used for autotune.

	auto &aciaclock = CLOCK(config, "aciaclock", 8_MHz_XTAL / 16);  // U134 (74LS93), QD.
	aciaclock.signal_handler().set("midiacia", FUNC(acia6850_device::write_txc));
	aciaclock.signal_handler().append("midiacia", FUNC(acia6850_device::write_rxc));
	aciaclock.signal_handler().append("nmiff", FUNC(ttl7474_device::clock_w));

	auto &acia = ACIA6850(config, "midiacia", 0);  // U137 (or is it U157?)
	acia.txd_handler().set("mdout", FUNC(midi_port_device::write_txd));
	acia.irq_handler().set("nmiff", FUNC(ttl7474_device::d_w)).invert();
	acia.write_dcd(0);
	acia.write_cts(0);

	TTL7474(config, "nmiff", 0).output_cb().set_inputline(m_maincpu, INPUT_LINE_NMI).invert();  // U146B

	MIDI_PORT(config, "mdin", midiin_slot, "midiin").rxd_handler().set("midiacia", FUNC(acia6850_device::write_rxd));
	MIDI_PORT(config, "mdout", midiout_slot, "midiout");

	auto &u148 = OUTPUT_LATCH(config, "tune_latch");  // CD40174
	// Bit 0: sound output on/off.
	// Bit 1, 2, 4: autotune-related.
	// Bit 3: Not connected.
	u148.bit_handler<5>().set("nmiff", FUNC(ttl7474_device::preset_w));

	auto &u102 = OUTPUT_LATCH(config, "led_latch_0");  // 74LS174
	u102.bit_handler<0>().set_output("led_track_1").invert();
	u102.bit_handler<1>().set_output("led_track_2").invert();
	u102.bit_handler<2>().set_output("led_track_3").invert();
	u102.bit_handler<3>().set_output("led_track_4").invert();
	u102.bit_handler<4>().set_output("led_track_5").invert();
	u102.bit_handler<5>().set_output("led_track_6").invert();

	auto &u101 = OUTPUT_LATCH(config, "led_latch_1");  // 74LS174
	u101.bit_handler<0>().set_output("led_seq_a").invert();
	u101.bit_handler<1>().set_output("led_seq_b").invert();
	u101.bit_handler<2>().set_output("led_arp_up_down").invert();
	u101.bit_handler<3>().set_output("led_arp_assign").invert();
	u101.bit_handler<4>().set_output("led_stack_a").invert();
	u101.bit_handler<5>().set_output("led_stack_b").invert();

	auto &u149 = OUTPUT_LATCH(config, "led_latch_2");  // 74LS174
	u149.bit_handler<0>().set_output("led_program").invert();
	u149.bit_handler<1>().set_output("led_param").invert();
	u149.bit_handler<2>().set_output("led_value").invert();
	u149.bit_handler<3>().set_output("led_record").invert();
	u149.bit_handler<4>().set_output("led_record_track").invert();
	u149.bit_handler<5>().set_output("led_legato").invert();
}

INPUT_PORTS_START(sixtrak)
	PORT_START("key_row_0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("SEL 0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("SEL 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("SEL 2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("SEL 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("SEL 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("SEL 5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("SEL 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("SEL 7") PORT_CODE(KEYCODE_7_PAD)

	PORT_START("key_row_1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("SEL 8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("SEL 9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("PROGRAM") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("PARAM") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("VALUE") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("RECORD")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("RECORD TRACK") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("LEGATO")

	PORT_START("key_row_2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("TRACK 1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("TRACK 2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("TRACK 3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("TRACK 4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("TRACK 5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("TRACK 6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("key_row_3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("SEQ A")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("SEQ B")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("UP/DOWN")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("ARP ASSIGN")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("STACK A")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("STACK B")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("key_row_4")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("key_row_5")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("key_row_6")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("key_row_7")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("key_row_8")  // C0 - G0 in schematic.
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C2
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_CS2
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_D2
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_DS2
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_E2
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_F2
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_FS2
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_G2

	PORT_START("key_row_9")  // G#0 - D#1
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_GS2
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_A2
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_AS2
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_B2
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C3
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_CS3
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_D3
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_DS3

	PORT_START("key_row_10")  // E1 - B1
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_E3
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_F3
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_FS3
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_G3
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_GS3
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_A3
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_AS3
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_B3

	PORT_START("key_row_11")  // C2 - G2
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C4
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_CS4
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_D4
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_DS4
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_E4
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_F4
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_FS4
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_G4

	PORT_START("key_row_12")  // G#2 - D#3
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_GS4
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_A4
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_AS4
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_B4
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C5
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_CS5
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_D5
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_DS5

	PORT_START("key_row_13")  // E3 - B3
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_E5
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_F5
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_FS5
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_G5
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_GS5
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_A5
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_AS5
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_B5

	PORT_START("key_row_14")  // C4
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C6
	PORT_BIT(0xfe, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("key_row_15")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("footswitch")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("CONTROL FOOTSWITCH") PORT_CODE(KEYCODE_SPACE)

	PORT_START("pitch_wheel")  // R1, 100K linear.
	PORT_BIT(0x7f, 50, IPT_PADDLE) PORT_NAME("PITCH") PORT_MINMAX(0, 100) PORT_SENSITIVITY(30) PORT_KEYDELTA(5) PORT_CENTERDELTA(10)

	PORT_START("mod_wheel")  // R2, 100K linear.
	PORT_ADJUSTER(0, "MOD")

	PORT_START("track_volume_knob")  // Knob, R119(?), 10K lineaar.
	PORT_ADJUSTER(50, "TRACK VOL")

	PORT_START("speed_knob")  // Knob, R115, 10K linear.
	PORT_ADJUSTER(50, "SPEED")

	PORT_START("value_knob")  // Knob, R163, 10K linear.
	PORT_ADJUSTER(50, "VALUE")

	PORT_START("tune_knob")  // Knob, R138(?), 10K linear.
	PORT_ADJUSTER(50, "TUNE")
INPUT_PORTS_END

// The firmware version can be displayed by pressing RECORD TRACK and SELECT 5.
ROM_START(sixtrak)
	ROM_REGION(0x4000, "maincpu", 0)  // U128 (27128)
	ROM_DEFAULT_BIOS("v11")

	ROM_SYSTEM_BIOS(0, "v11", "Six-Trak V11 (1985)")  // Last official release.
	ROMX_LOAD("trak-11.bin", 0x000000, 0x004000, CRC(4d361d4f) SHA1(f14d4291c1a6a3e9462ae9786641cef11a9aac9a), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "v9", "Six-Trak V9")
	ROMX_LOAD("trak-9.bin", 0x000000, 0x004000, CRC(d1e6261c) SHA1(bdad57290c24a9ce02c3a5161f8d12f8f96fc74a), ROM_BIOS(1))
ROM_END

}  // anonymous namespace

SYST(1984, sixtrak, 0, 0, sixtrak, sixtrak, sixtrak_state, empty_init, "Sequential Circuits", "Six-Trak (Model 610)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE)



sk1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Vas Crabb
/*
 Casio SK-1 Sampling Keyboard
 Also sold as Realistic Concertmate-500

 Low-cost 32-key 4-voice digital sampling keyboard.
 Holds a single 8-bit 9.38kHz PCM in volatile RAM.

 LSI1   SoC     MSM6283-01GS SoC
 LSI2   ROM     µPD23C256EAC-011 ROM
 LSI3   DRAM    µPD4168C
 LSI4   DRAM    µPD4168C

 7.24MHz system clock generated using an L10-495 oscillator coil and capacitors

 SoC has onboard sampling and playback capability
 Analog filtering section is simple and can be netlisted once SoC is producing output

 SoC has onboard keyboard scanning - matrix is 10 rows by 8 columns (KO0-KO9 and KI1-KI8)
 Piano keys have N-key rollover diodes, other buttons/switches don't

 So-called program ROM actually mostly contains samples, which begin at offset 0x0b00;
 a table at 0x085c has their starting addresses. The real microcode is likely internal,
 since MSM6283s with different suffixes show up in other Casio keyboards.
 */

#include "emu.h"
#include "machine/bankdev.h"


namespace {

class sk1_state : public driver_device
{
public:
	sk1_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
	{
	}

	void sk1(machine_config &config);
	void sk5(machine_config &config);

	// make slide switches usable on a keyboard
	template <ioport_value V> DECLARE_INPUT_CHANGED_MEMBER(sw_function);
	template <ioport_value V> DECLARE_INPUT_CHANGED_MEMBER(sw_mode);

	ioport_value function_in() { return m_sw_function; }
	ioport_value mode_in() { return m_sw_mode; }

private:
	void sk1_memory(address_map &map) ATTR_COLD;

	virtual void driver_start() override;

	ioport_value    m_sw_function = 0xfe;
	ioport_value    m_sw_mode = 0xfe;
};


template <ioport_value V> INPUT_CHANGED_MEMBER(sk1_state::sw_function)
{
	if (!newval && oldval)
	{
		if (!(~m_sw_function & 0x03) && (~V & 0x03))
		{
			// TODO: generate CPU reset pulse
		}
		m_sw_function = V;
	}
}


template <ioport_value V> INPUT_CHANGED_MEMBER(sk1_state::sw_mode)
{
	if (!newval && oldval)
		m_sw_mode = V;
}


void sk1_state::driver_start()
{
	save_item(NAME(m_sw_function));
	save_item(NAME(m_sw_mode));
}


void sk1_state::sk1_memory(address_map &map)
{
	// chip selects are driven by decoding A13 and A15 with IC3 quad 2-input NOR gate
	map(0x0000, 0x7fff).rom().region("lsi2", 0x0000);
	map(0x8000, 0x83ff).mirror(0x4000).ram();
}


void sk1_state::sk1(machine_config &config)
{
	//MSM6283(config, "maincpu", 7'277'088);

	// just to attach the memory map to something until I can work out what the CPU core is
	ADDRESS_MAP_BANK(config, "dummy").set_map(&sk1_state::sk1_memory).set_data_width(8).set_addr_width(16);
}

void sk1_state::sk5(machine_config &config)
{
	sk1(config);

	//MSM6294(config, "percussion", 256'000);
}


INPUT_PORTS_START(sk1)
	PORT_START("KO0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("SAMPLING")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("LOOP SET")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("DEMO")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("ONE KEY PLAY R")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("ONE KEY PLAY L")
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KO1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("PORTAMENTO")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("HARMONIC SYNTHESIZER")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("ENVELOPE SELECT")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("VIBRATO")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_F3
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_FS3
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_G3
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_GS3

	PORT_START("KO2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("MEMORY PLAY")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("RESET")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("RHYTHM SELECT")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("FILL-IN")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_A3
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_AS3
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_B3
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_C4

	PORT_START("KO3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("TEMPO+")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("TEMPO-")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("DELETE")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("CLEAR")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_CS4
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_D4
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_DS4
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_E4

	PORT_START("KO4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("SAMPLING SOUND")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("HARMONICS SOUND")
	PORT_BIT(0x0c, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_F4
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_FS4
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_G4
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_GS4

	PORT_START("KO5")
	PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_A4
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_AS4
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_B4
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_C5

	PORT_START("KO6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("JAZZ ORGAN")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("PIPE ORGAN")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("FLUTE")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("HUMAN VOICE")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_CS5
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_D5
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_DS5
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_E5

	PORT_START("KO7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("SYNTH DRUMS")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("TRUMPET")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("BRASS ENSEMBLE")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("PIANO")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_F5
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_FS5
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_G5
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_GS5

	PORT_START("KO8")
	PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_CUSTOM_MEMBER(FUNC(sk1_state::mode_in))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_A5
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_AS5
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_B5
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER)   PORT_GM_C6

	PORT_START("KO9")
	PORT_BIT(0x83, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_CUSTOM_MEMBER(FUNC(sk1_state::function_in))
	PORT_BIT(0x7c, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("TOGGLES")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("PLAY")      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sk1_state::sw_function<0xfe>), 0) // three-position function switch
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("RECORD")    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sk1_state::sw_function<0xfd>), 0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("POWER OFF") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sk1_state::sw_function<0x7f>), 0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("NORMAL")    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sk1_state::sw_mode<0xfe>), 0) // four-position mode switch
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("SOLO 1")    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sk1_state::sw_mode<0xfd>), 0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("SOLO 2")    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sk1_state::sw_mode<0xfb>), 0)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER)   PORT_NAME("CHORD")     PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sk1_state::sw_mode<0xf7>), 0)
INPUT_PORTS_END


ROM_START(sk1)
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("msm6283-01gs.lsi1", 0x0000, 0x1000, NO_DUMP)

	ROM_REGION(0x8000, "lsi2", 0) // µPD23C256EAC-011
	ROM_LOAD("sk1.lsi2", 0x0000, 0x8000, CRC(d615963c) SHA1(0dbf2d1c4c776f1a1c35dd2be4d6ca03882afd4c))
ROM_END

ROM_START(sk5)
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("msm6283-05gs.bin", 0x0000, 0x1000, NO_DUMP)

	ROM_REGION(0x4000, "percussion", 0)
	ROM_LOAD("msm6294-05.bin", 0x0000, 0x4000, NO_DUMP)

	ROM_REGION(0x8000, "lsi2", 0) // µPD23C256EAC-038
	ROM_LOAD("casio_sk5.bin", 0x0000, 0x8000, CRC(1fda590b) SHA1(c77ccb5fa20275478bf271512633fa1561d6f07c))
ROM_END

ROM_START(sk10)
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("msm6283.bin", 0x0000, 0x1000, NO_DUMP)

	ROM_REGION(0x8000, "lsi2", 0) // µPD23C256EAC-070
	ROM_LOAD("casio_sk10.bin", 0x0000, 0x8000, CRC(5945b619) SHA1(929e906bfa0fcd99a8398b37ec62d0512299065c))
ROM_END

ROM_START(sk2)
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("msm6283-07gs.bin", 0x0000, 0x1000, NO_DUMP)

	ROM_REGION(0x8000, "lsi2", 0) // µPD23C256EAC-093
	ROM_LOAD("casio_sk2.bin", 0x0000, 0x8000, CRC(f47e421d) SHA1(50785ffd09bb2effcc9f48de466b624fd59f1317))
ROM_END

} // anonymous namespace

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY  FULLNAME  FLAGS
SYST( 1985, sk1,  0,      0,      sk1,     sk1,   sk1_state, empty_init, "Casio", "SK-1",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
SYST( 1987, sk5,  0,      0,      sk5,     sk1,   sk1_state, empty_init, "Casio", "SK-5",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
SYST( 1987, sk10, 0,      0,      sk1,     sk1,   sk1_state, empty_init, "Casio", "SK-10",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
SYST( 1988, sk2,  0,      0,      sk1,     sk1,   sk1_state, empty_init, "Casio", "SK-2",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



sk101bl.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

Skeleton driver for Reuters Model SK 101 BL trading terminal.

This terminal is a small portable-sized unit with a 115-key keyboard, which is driven largely through LSTTL shift registers. The
keyboard layout is similar to that depicted in patent EP0434224A2.

Attached to the keyboard is a backlit LCD module displaying one row of 16 characters.

The main board incorporates two DSWs and a "BLMHYB01" hybrid module containing unknown interface logic.

TODO: figure out keycodes (are they translated externally?)

***********************************************************************************************************************************/

#include "emu.h"
//#include "bus/rs232/rs232.h"
#include "cpu/mcs51/mcs51.h"
#include "sound/spkrdev.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class sk101bl_state : public driver_device
{
public:
	sk101bl_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "lcdc")
		, m_key_row(*this, "ROW%u", 0U)
	{
	}

	void sk101bl(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	HD44780_PIXEL_UPDATE(pixel_update);

	u8 p1_r();
	void p1_w(u8 data);
	void scan_load_w(u8 data);
	u8 scan_shift_r();
	void lcd_control_w(u8 data);

	void prog_map(address_map &map) ATTR_COLD;
	void ext_map(address_map &map) ATTR_COLD;

	required_device<i80c31_device> m_maincpu;
	required_device<hd44780_device> m_lcdc;
	optional_ioport_array<16> m_key_row;

	u8 m_p1_data = 0;
	u8 m_lcd_control = 0;
	u16 m_key_scan = 0;
	u16 m_shift_output = 0;
	u8 m_row_counter = 0;
};

void sk101bl_state::machine_start()
{
	m_p1_data = 0xff;
	m_shift_output = 0;
	m_key_scan = 0xffff;
	m_row_counter = 0;

	save_item(NAME(m_p1_data));
	save_item(NAME(m_lcd_control));
	save_item(NAME(m_key_scan));
	save_item(NAME(m_shift_output));
	save_item(NAME(m_row_counter));
}

void sk101bl_state::machine_reset()
{
	m_lcd_control = 0;
}

HD44780_PIXEL_UPDATE(sk101bl_state::pixel_update)
{
	if (pos < 16 && line == 0)
		bitmap.pix(line * 10 + y, pos * 6 + x) = state;
}

u8 sk101bl_state::p1_r()
{
	return m_lcdc->db_r();
}

void sk101bl_state::p1_w(u8 data)
{
	if (BIT(m_p1_data, 7) && BIT(m_p1_data, 6) && !BIT(data, 6) && !BIT(m_lcd_control, 2))
		m_row_counter = (m_row_counter - 1) & 0x0f;

	m_p1_data = data;
	m_lcdc->db_w(data);
}

void sk101bl_state::scan_load_w(u8 data)
{
	m_key_scan = m_key_row[m_row_counter & 0x0f].read_safe(0xffff);
	m_maincpu->set_input_line(MCS51_INT0_LINE, BIT(m_key_scan, 15) ? CLEAR_LINE : ASSERT_LINE);
}

u8 sk101bl_state::scan_shift_r()
{
	if (!machine().side_effects_disabled())
	{
		m_key_scan = (m_key_scan << 1) | 1;
		m_maincpu->set_input_line(MCS51_INT0_LINE, BIT(m_key_scan, 15) ? CLEAR_LINE : ASSERT_LINE);
	}
	return 0xff;
}

void sk101bl_state::lcd_control_w(u8 data)
{
	if (BIT(data, 7) && !BIT(m_lcd_control, 7))
		m_shift_output = (m_shift_output << 1) | (BIT(m_p1_data, 7) ? 0 : 1);

	m_lcdc->rw_w(BIT(data, 3));
	m_lcdc->rs_w(BIT(data, 2));
	m_lcdc->e_w(BIT(data, 1));

	if (BIT(data, 0))
		m_row_counter = 0;

	m_lcd_control = data;
}

void sk101bl_state::prog_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("program", 0);
}

void sk101bl_state::ext_map(address_map &map)
{
	map(0x0000, 0x0000).mirror(0x1fff).r(FUNC(sk101bl_state::scan_shift_r));
	map(0x1000, 0x1000).mirror(0x0fff).w(FUNC(sk101bl_state::scan_load_w));
	map(0x2000, 0x7fff).rom().region("program", 0x2000);
	map(0x4000, 0x4000).mirror(0x0fff).w(FUNC(sk101bl_state::lcd_control_w));
	map(0xe000, 0xffff).ram(); // TC5565APL-15L
}

static INPUT_PORTS_START(sk101bl)
INPUT_PORTS_END

void sk101bl_state::sk101bl(machine_config &config)
{
	I80C31(config, m_maincpu, 11.0592_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &sk101bl_state::prog_map);
	m_maincpu->set_addrmap(AS_IO, &sk101bl_state::ext_map);
	m_maincpu->port_in_cb<1>().set(FUNC(sk101bl_state::p1_r));
	m_maincpu->port_out_cb<1>().set(FUNC(sk101bl_state::p1_w));
	m_maincpu->port_out_cb<3>().set("alarm", FUNC(speaker_sound_device::level_w)).bit(3);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(16*6, 10);
	screen.set_visarea(0, 16*6-1, 0, 10-1);
	screen.set_palette("palette");

	HD44780(config, m_lcdc, 270'000).set_lcd_size(1, 16); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_pixel_update_cb(FUNC(sk101bl_state::pixel_update));

	PALETTE(config, "palette").set_entries(2);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "alarm").add_route(ALL_OUTPUTS, "mono", 1.00);
}

ROM_START(sk101bl)
	ROM_REGION(0x8000, "program", 0)
	ROM_LOAD("sk_101_5.0_1ca2.ic7", 0x0000, 0x8000, CRC(f7903ca5) SHA1(66648cc1622c1241cdbd443af706750acbb93502)) // 27256-25
ROM_END

} // anonymous namespace


COMP(1988, sk101bl, 0, 0, sk101bl, sk101bl, sk101bl_state, empty_init, "Reuters", "Model SK 101 BL", MACHINE_NOT_WORKING)



slc1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert, hap
/*******************************************************************************

2011-JUL-16 SLC1 skeleton driver [Robbbert]
2011-DEC-29 Working [Robbbert]
2021-APR-27 And stuff [Stuff doer]

Schach- und Lerncomputer SLC 1 by Dr.Ing. Dieter Scheuschner, published in
Schaltungssammlung für den Amateur, 5. Lieferung 1989 (East Germany).

Reference:
    - https://dr-scheuschner.de/start/schachcomputer-slc1/
    - http://www.jens-mueller.org/jkcemu/slc1.html

This computer is both a Z80 trainer, and a chess computer. The keyboard
    is different between the two. So by default, the chess number buttons
    are on the main keyboard, and the one for the trainer are on the numpad.

    There is no chess board attached. You supply your own and you sync the
    pieces and the computer instructions. The chess engine was copied from
    Fidelity's Sensory Chess Challenger 8. Even the TTL I/O is the same.

    When started, it is in Chess mode. Press 11111 to switch to Trainer mode.

Hardware:
    4 Kbytes ROM in the address range 0000-0FFF
    1 Kbyte RAM in the address range 5000-53FF (user area starts at 5100)
    6-digit 7-segment display
    Busy LED
    Keyboard with 12 keys

Trainer Keys:
    0-7 : hexadecimal numbers
    Shift then 0-7 : Hexadecimal 8-F (decimal points will appear)
    ADR : enter an address to work with. After the 4 digits are entered,
          the data at that address shows, and you can modify the data.
    + (inc) : Enter the data into memory, and increment the address by 1.

Pasting:
    0-7 : as is
    8-F : X, then 0-7
    + : O
    - : XO
    ADR : Z

Test Paste:
    00000Z510011O22O33O44O55O66O77OX8X8OX9X9OZ5100
    Now press O to confirm the data has been entered.

TODO:
    - Pasting doesn't work, probably too slow.
    - The handwritten schematics and prototype by Dr.Scheuschner show 4 extra
      keys (R, MEM, N, Y) and 8 7segs, but they aren't in the final version.

*******************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "slc1.lh"


namespace {

class slc1_state : public driver_device
{
public:
	slc1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_dac(*this, "dac")
		, m_inputs(*this, "IN.%u", 0U)
		, m_display(*this, "display")
		, m_busyled(*this, "busy_led")
	{ }

	void slc1(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(trigger_reset);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<3> m_inputs;
	required_device<pwm_display_device> m_display;
	output_finder<> m_busyled;

	u8 m_select = 0;
	u8 m_segment = 0;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	u8 input_r();
	void control_w(offs_t offset, u8 data);
};

void slc1_state::machine_start()
{
	m_busyled.resolve();

	save_item(NAME(m_select));
	save_item(NAME(m_segment));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void slc1_state::control_w(offs_t offset, u8 data)
{
	// d0-d3: 7442 or equivalent
	m_select = data & 0xf;
	u16 sel = 1 << m_select;

	// 7442 0-1,3-6: digit select
	// 7442 3-5: keypad select
	// 7442 9: speaker out
	m_dac->write(BIT(sel, 9));

	// a0-a2+d7: digit segment data
	u8 mask = 1 << (offset & 7);
	m_segment = (m_segment & ~mask) | ((data & 0x80) ? mask : 0);
	m_display->matrix(sel, m_segment);

	// d4: busy led
	m_busyled = BIT(data, 4);
}

u8 slc1_state::input_r()
{
	u8 data = 0;

	// read keypad
	if (m_select >= 3 && m_select <= 5)
		data |= m_inputs[m_select - 3]->read();

	return data << 4 ^ 0xff;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void slc1_state::mem_map(address_map &map)
{
	map.global_mask(0x4fff);
	map(0x0000, 0x0fff).rom();
	map(0x4000, 0x43ff).ram().mirror(0xc00);
}

void slc1_state::io_map(address_map &map)
{
	map.global_mask(0x07);
	map(0x00, 0x07).rw(FUNC(slc1_state::input_r), FUNC(slc1_state::control_w));
}



/******************************************************************************
    Input Ports
*******************************************************************************/

INPUT_CHANGED_MEMBER(slc1_state::trigger_reset)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}

static INPUT_PORTS_START( slc1 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D 4 / T / 3 B") PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C 3 / L / 2 A") PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B 2 / S / 1 9") PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A 1 / B / 0 8") PORT_CODE(KEYCODE_A) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0')

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E 5 / D / 4 C INS") PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F 6 / K / 5 D DEL") PORT_CODE(KEYCODE_F) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G 7 / 6 E BL") PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H 8 / 7 F GO") PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7')

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C / Seq / BG") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A / +/- 1 / SS") PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("St / Fu / DP") PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_CHAR('S')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z / ADR / BP") PORT_CODE(KEYCODE_Z) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR('Z')

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(slc1_state::trigger_reset), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Config
*******************************************************************************/

void slc1_state::slc1(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 2500000);
	m_maincpu->set_addrmap(AS_PROGRAM, &slc1_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &slc1_state::io_map);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(7, 8);
	m_display->set_segmask(0x7b, 0xff);
	config.set_default_layout(layout_slc1);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START(slc1)
	ROM_REGION(0x1000, "maincpu", 0 )
	ROM_SYSTEM_BIOS(0, "bios0", "SLC-1")
	ROMX_LOAD("slc1_0000.bin",   0x0000, 0x1000, CRC(06d32967) SHA1(f25eac66a4fca9383964d509c671a7ad2e020e7e), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "bios1", "SC-1 v2")
	ROMX_LOAD("sc1-v2.bin",      0x0000, 0x1000, CRC(1f122a85) SHA1(d60f89f8b59d04f4cecd6e3ecfe0a24152462a36), ROM_BIOS(1))
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY, FULLNAME, FLAGS
SYST( 1989, slc1, 0,      0,      slc1,    slc1,  slc1_state, empty_init, "Dieter Scheuschner", "Schach- und Lerncomputer SLC 1", MACHINE_SUPPORTS_SAVE )



slicer.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Carl

// Slicer Computers Slicer 80186 SBC
// The bios makefile refers to a "exe3bin" utility, this can be substituted with FreeDOS exe2bin and the /l=0xf800 option
// which will fixup the relocations

#include "emu.h"

#include "bus/isa/isa.h"
#include "bus/rs232/rs232.h"
#include "bus/scsi/scsi.h"
#include "cpu/i86/i186.h"
#include "imagedev/floppy.h"
#include "machine/74259.h"
#include "machine/mc68681.h"
#include "machine/wd_fdc.h"


namespace {

class slicer_state : public driver_device
{
public:
	slicer_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_fdc(*this, "fdc"),
		m_floppies(*this, "fdc:%u", 0U),
		m_sasi(*this, "sasi")
	{
	}

	void slicer(machine_config &config);

private:
	void sio_out_w(uint8_t data);
	void drive_size_w(int state);
	template <unsigned Drive> void drive_sel_w(int state);

	void slicer_io(address_map &map) ATTR_COLD;
	void slicer_map(address_map &map) ATTR_COLD;

	required_device<fd1797_device> m_fdc;
	required_device_array<floppy_connector, 4> m_floppies;
	required_device<scsi_port_device> m_sasi;
};

void slicer_state::sio_out_w(uint8_t data)
{
	const int state = (data & 0x80) ? 0 : 1;

	for (auto &floppy : m_floppies)
	{
		if (floppy->get_device())
			floppy->get_device()->mon_w(state);
	}
}

template <unsigned Drive>
void slicer_state::drive_sel_w(int state)
{
	if (!state)
		return;

	m_fdc->set_floppy(m_floppies[Drive]->get_device());
}

void slicer_state::drive_size_w(int state)
{
	m_fdc->set_unscaled_clock (state ? 1'000'000 : 2'000'000);
}

void slicer_state::slicer_map(address_map &map)
{
	map(0x00000, 0x3ffff).ram(); // fixed 256k for now
	map(0xf8000, 0xfffff).rom().region("bios", 0);
}

void slicer_state::slicer_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x007f).rw(m_fdc, FUNC(fd1797_device::read), FUNC(fd1797_device::write)).umask16(0x00ff); //PCS0
	map(0x0080, 0x00ff).rw("duart", FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask16(0x00ff); //PCS1
	map(0x0100, 0x010f).mirror(0x0070).w("drivelatch", FUNC(ls259_device::write_d0)).umask16(0x00ff); //PCS2
	// TODO: 0x180 sets ack
	map(0x0180, 0x0180).r("sasi_data_in", FUNC(input_buffer_device::read)).w("sasi_data_out", FUNC(output_latch_device::write)).umask16(0x00ff); //PCS3
	map(0x0181, 0x0181).r("sasi_ctrl_in", FUNC(input_buffer_device::read));
	map(0x0184, 0x0184).r("sasi_data_in", FUNC(input_buffer_device::read)).w("sasi_data_out", FUNC(output_latch_device::write)).umask16(0x00ff);
	map(0x0185, 0x0185).r("sasi_ctrl_in", FUNC(input_buffer_device::read));
}

static void slicer_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
	device.option_add("8dsdd", FLOPPY_8_DSDD);
}

void slicer_state::slicer(machine_config &config)
{
	i80186_cpu_device &maincpu(I80186(config, "maincpu", 16_MHz_XTAL)); // 8 MHz clock output
	maincpu.set_addrmap(AS_PROGRAM, &slicer_state::slicer_map);
	maincpu.set_addrmap(AS_IO, &slicer_state::slicer_io);

	scn2681_device &duart(SCN2681(config, "duart", 3.6864_MHz_XTAL));
	duart.irq_cb().set("maincpu", FUNC(i80186_cpu_device::int0_w));
	duart.a_tx_cb().set("rs232_1", FUNC(rs232_port_device::write_txd));
	duart.b_tx_cb().set("rs232_2", FUNC(rs232_port_device::write_txd));
	duart.outport_cb().set(FUNC(slicer_state::sio_out_w));

	rs232_port_device &rs232_1(RS232_PORT(config, "rs232_1", default_rs232_devices, "terminal"));
	rs232_1.rxd_handler().set("duart", FUNC(scn2681_device::rx_a_w));
	rs232_port_device &rs232_2(RS232_PORT(config, "rs232_2", default_rs232_devices, nullptr));
	rs232_2.rxd_handler().set("duart", FUNC(scn2681_device::rx_b_w));

	FD1797(config, m_fdc, 16_MHz_XTAL / 2 / 8);
	m_fdc->intrq_wr_callback().set("maincpu", FUNC(i80186_cpu_device::int1_w));
	m_fdc->drq_wr_callback().set("maincpu", FUNC(i80186_cpu_device::drq0_w));
	FLOPPY_CONNECTOR(config, m_floppies[0], slicer_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppies[1], slicer_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppies[2], slicer_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppies[3], slicer_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);

	ls259_device &drivelatch(LS259(config, "drivelatch")); // U29
	drivelatch.q_out_cb<0>().set(m_sasi, FUNC(scsi_port_device::write_sel));
	drivelatch.q_out_cb<1>().set(m_sasi, FUNC(scsi_port_device::write_rst));
	drivelatch.q_out_cb<2>().set(FUNC(slicer_state::drive_sel_w<3>));
	drivelatch.q_out_cb<3>().set(FUNC(slicer_state::drive_sel_w<2>));
	drivelatch.q_out_cb<4>().set(FUNC(slicer_state::drive_sel_w<1>));
	drivelatch.q_out_cb<5>().set(FUNC(slicer_state::drive_sel_w<0>));
	drivelatch.q_out_cb<6>().set(FUNC(slicer_state::drive_size_w));
	drivelatch.q_out_cb<7>().set(m_fdc, FUNC(fd1797_device::dden_w));

	SCSI_PORT(config, m_sasi, 0);
	m_sasi->set_data_input_buffer("sasi_data_in");
	m_sasi->bsy_handler().set("sasi_ctrl_in", FUNC(input_buffer_device::write_bit3));
	m_sasi->msg_handler().set("sasi_ctrl_in", FUNC(input_buffer_device::write_bit4));
	m_sasi->cd_handler().set("sasi_ctrl_in", FUNC(input_buffer_device::write_bit5));
	m_sasi->req_handler().set("sasi_ctrl_in", FUNC(input_buffer_device::write_bit6));
	m_sasi->io_handler().set("sasi_ctrl_in", FUNC(input_buffer_device::write_bit7));

	output_latch_device &sasi_data_out(OUTPUT_LATCH(config, "sasi_data_out"));
	m_sasi->set_output_latch(sasi_data_out);
	INPUT_BUFFER(config, "sasi_data_in");
	INPUT_BUFFER(config, "sasi_ctrl_in");
}

ROM_START( slicer )
	ROM_REGION16_LE(0x8001, "bios", 0)
	// built from sources, reset.asm adds an extra byte
	ROM_LOAD("epbios.bin", 0x0000, 0x8001, CRC(96fe9dd4) SHA1(5fc43454fe7d51f2ae97aef822155dcd28eb7f23))

	ROM_REGION(0x10000, "user1", 0)
	//slicer_h.bin : main slicer board, high byte
	//slicer_l.bin : main slicer board, low byte
	//slvid_cg.bin : slicer video/keyboard expansion board, character generator
	//slvid_e.bin : slicer video/keyboard expansion board, even byte
	//slvid_o.bin : slicer video/keyboard expansion board, odd byte
	ROM_LOAD( "slicer_h.bin", 0x000000, 0x004000, CRC(1f9a79b7) SHA1(2070c6818d39fe7ec4370fc2304469793a126731) )
	ROM_LOAD( "slicer_l.bin", 0x000000, 0x004000, CRC(6feef94b) SHA1(174488591b727a4130166bcb2e83c0e74323d43b) )
	ROM_LOAD( "slvid_cg.bin", 0x000000, 0x001000, CRC(d4d9ac2f) SHA1(866c760320b224ba8670501ea905de32193acedc) )
	ROM_LOAD( "slvid_o.bin",  0x000000, 0x001000, CRC(c62dda77) SHA1(1d0b9abc53412b0725072d4c33c478fb5358ab5c) )
	ROM_LOAD( "slvid_e.bin",  0x000000, 0x001000, CRC(8694274f) SHA1(8373baaea8d689bf52699b587942a57f26baf740) )
ROM_END

} // anonymous namespace


COMP( 1983, slicer, 0, 0, slicer, 0, slicer_state, empty_init, "Slicer Computers", "Slicer", MACHINE_NO_SOUND )



slsstars.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for SuperStars CD jukebox by Sound Leisure Ltd.

****************************************************************************/

#include "emu.h"
#include "cpu/mc68hc11/mc68hc11.h"
#include "machine/timekpr.h"


namespace {

class slsstars_state : public driver_device
{
public:
	slsstars_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void slsstars(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<mc68hc11_cpu_device> m_maincpu;
};


void slsstars_state::mem_map(address_map &map)
{
	map(0x10f8, 0x10ff).nopw(); // some kind of serial output?
	map(0x1800, 0x1800).nopw(); // watchdog?
	map(0x4000, 0x5fff).rw("timekpr", FUNC(m48t58_device::read), FUNC(m48t58_device::write));
	map(0x8000, 0xffff).rom().region("program", 0);
}


static INPUT_PORTS_START(slsstars)
	// all unknown
INPUT_PORTS_END


void slsstars_state::slsstars(machine_config &config)
{
	MC68HC11F1(config, m_maincpu, 8'000'000); // unknown clock
	m_maincpu->set_addrmap(AS_PROGRAM, &slsstars_state::mem_map);
	m_maincpu->set_default_config(0x3f);

	M48T58(config, "timekpr"); // U3
}


ROM_START(slsstars)
	ROM_REGION(0x8000, "program", 0)
	ROM_LOAD("superstars-eprom.u2", 0x0000, 0x8000, CRC(59d26dda) SHA1(4ce5fa5b7f317bdada889f66dbf5a106b1907b30))
ROM_END

} // anonymous namespace


SYST(199?, slsstars, 0, 0, slsstars, slsstars, slsstars_state, empty_init, "Sound Leisure", "SuperStars (CD jukebox)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_MECHANICAL | MACHINE_REQUIRES_ARTWORK)



sm1800.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

        SM1800 (original name is CM1800 in cyrilic letters)

        10/12/2010 Skeleton driver.

        On board hardware :
            KR580VM80A central processing unit (i8080)
            KR580VG75  programmable CRT video display controller (i8275)
            KR580VV55  programmable parallel interface (i8255)
            KR580IK51  programmable serial interface/communication controller (i8251)
            KR580VV79  programmable peripheral device, keyboard and display controller (i8279)

****************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8255.h"
#include "machine/i8251.h"
#include "video/i8275.h"
#include "emupal.h"
#include "screen.h"


namespace {

class sm1800_state : public driver_device
{
public:
	sm1800_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_uart(*this, "uart")
		, m_ppi(*this, "ppi")
		, m_crtc(*this, "crtc")
		, m_palette(*this, "palette")
	{ }

	void sm1800(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	required_device<i8251_device> m_uart;
	required_device<i8255_device> m_ppi;
	required_device<i8275_device> m_crtc;
	required_device<palette_device> m_palette;
	void portb_w(uint8_t data);
	void portc_w(uint8_t data);
	uint8_t porta_r();
	uint8_t portc_r();
	uint8_t m_irq_state = 0U;
	void machine_start() override ATTR_COLD;
	void sm1800_palette(palette_device &palette) const;
	INTERRUPT_GEN_MEMBER(vblank_interrupt);
	IRQ_CALLBACK_MEMBER(irq_callback);
	I8275_DRAW_CHARACTER_MEMBER( crtc_display_pixels );
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
};

void sm1800_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).rom();
	//map(0x0fb0, 0x0fff).w("crtc", FUNC(i8275_device::dack_w));
	map(0x1000, 0x17ff).ram(); // videoram looks like 1080-17FF, normal ascii
}

void sm1800_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x3c, 0x3d).rw(m_crtc, FUNC(i8275_device::read), FUNC(i8275_device::write));
	map(0x5c, 0x5d).rw(m_uart, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x6c, 0x6f).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
	//map(0x74, 0x74).rw("i8279", FUNC(i8279_device::status_r), FUNC(i8279_device::cmd_w));
	//map(0x75, 0x75).rw("i8279", FUNC(i8279_device::data_r), FUNC(i8279_device::data_w));
}

/* Input ports */
static INPUT_PORTS_START( sm1800 )
INPUT_PORTS_END

IRQ_CALLBACK_MEMBER(sm1800_state::irq_callback)
{
	return 0xff;
}

void sm1800_state::machine_start()
{
	save_item(NAME(m_irq_state));
}

INTERRUPT_GEN_MEMBER(sm1800_state::vblank_interrupt)
{
	m_maincpu->set_input_line(0, m_irq_state ?  HOLD_LINE : CLEAR_LINE);
	m_irq_state ^= 1;
}

I8275_DRAW_CHARACTER_MEMBER( sm1800_state::crtc_display_pixels )
{
	using namespace i8275_attributes;

	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	uint8_t const *const charmap = memregion("chargen")->base();
	uint8_t pixels = charmap[(linecount & 7) + (charcode << 3)] ^ 0xff;
	if (BIT(attrcode, VSP))
		pixels = 0;

	if (BIT(attrcode, LTEN))
		pixels = 0xff;

	if (BIT(attrcode, RVV))
		pixels ^= 0xff;

	bool hlgt = BIT(attrcode, HLGT);
	for(int i=0;i<8;i++)
		bitmap.pix(y, x + i) = palette[(pixels >> (7-i)) & 1 ? (hlgt ? 2 : 1) : 0];
}

void sm1800_state::portb_w(uint8_t data)
{
}

void sm1800_state::portc_w(uint8_t data)
{
}

uint8_t sm1800_state::porta_r()
{
	return 0xff;
}

uint8_t sm1800_state::portc_r()
{
	return 0;
}

void sm1800_state::sm1800_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t::black()); // black
	palette.set_pen_color(1, 0xa0, 0xa0, 0xa0); // white
	palette.set_pen_color(2, rgb_t::white()); // highlight
}


/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_sm1800 )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END


void sm1800_state::sm1800(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, XTAL(2'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &sm1800_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &sm1800_state::io_map);
	m_maincpu->set_vblank_int("screen", FUNC(sm1800_state::vblank_interrupt));
	m_maincpu->set_irq_acknowledge_callback(FUNC(sm1800_state::irq_callback));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_screen_update("crtc", FUNC(i8275_device::screen_update));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	PALETTE(config, m_palette, FUNC(sm1800_state::sm1800_palette), 3);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_sm1800);

	/* Devices */
	I8255(config, m_ppi);
	m_ppi->in_pa_callback().set(FUNC(sm1800_state::porta_r));
	m_ppi->out_pb_callback().set(FUNC(sm1800_state::portb_w));
	m_ppi->in_pc_callback().set(FUNC(sm1800_state::portc_r));
	m_ppi->out_pc_callback().set(FUNC(sm1800_state::portc_w));

	I8275(config, m_crtc, 2000000);
	m_crtc->set_character_width(8);
	m_crtc->set_display_callback(FUNC(sm1800_state::crtc_display_pixels));

	I8251(config, m_uart, 0);
}

/* ROM definition */
ROM_START( sm1800 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "prog.bin", 0x0000, 0x0800, CRC(55736ad5) SHA1(b77f720f1b64b208dd6a5d4f9c9521d1284028e9))

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "font.bin", 0x0000, 0x0800, CRC(28ed9ebc) SHA1(f561136962a06a5dcb5a0436931d29e940155d24))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   STATE         INIT        COMPANY      FULLNAME   FLAGS */
COMP( ????, sm1800, 0,      0,      sm1800,  sm1800, sm1800_state, empty_init, "<unknown>", "SM1800",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



sm7238.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************

    SM 7238 (aka T3300) color/mono text terminal, compatible with DEC VT 240.
    Graphics options add Tek 401x and DEC ReGIS support

    Technical manual and schematics: http://doc.pdp-11.org.ru/Terminals/CM7238/

    To do:
    - graphics options
    - colors
    - document hardware and ROM variants, verify if pixel stretching is done
    - verify blink frequency

****************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/i8085/i8085.h"
#include "machine/bankdev.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "km035.h"
#include "machine/nvram.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"

#include "emupal.h"
#include "screen.h"


//#define VERBOSE (LOG_DEBUG)
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"


namespace {

static constexpr int KSM_COLUMNS_MAX = 132;

static constexpr int KSM_TOTAL_HORZ = KSM_COLUMNS_MAX * 10;
static constexpr int KSM_DISP_HORZ = KSM_COLUMNS_MAX * 8;

static constexpr int KSM_TOTAL_VERT = 260;
static constexpr int KSM_DISP_VERT = 250;


class sm7238_state : public driver_device
{
public:
	sm7238_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_nvram(*this, "nvram")
		, m_videobank(*this, "videobank")
		, m_p_videoram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
		, m_p_charram(*this, "charram")
		, m_pic8259(*this, "pic8259")
		, m_i8251line(*this, "i8251line")
		, m_rs232(*this, "rs232")
		, m_i8251kbd(*this, "i8251kbd")
		, m_keyboard(*this, "keyboard")
		, m_i8251prn(*this, "i8251prn")
		, m_printer(*this, "prtr")
		, m_t_hblank(*this, "t_hblank")
		, m_t_vblank(*this, "t_vblank")
		, m_t_color(*this, "t_color")
		, m_t_iface(*this, "t_iface")
		, m_screen(*this, "screen")
	{ }

	void sm7238(machine_config &config);

private:
	void write_keyboard_clock(int state);
	void write_printer_clock(int state);

	void control_w(uint8_t data);
	void text_control_w(uint8_t data);
	void vmem_w(offs_t offset, uint8_t data);

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void sm7238_io(address_map &map) ATTR_COLD;
	void sm7238_mem(address_map &map) ATTR_COLD;
	void videobank_map(address_map &map) ATTR_COLD;

	void recompute_parameters();

	struct
	{
		uint8_t control = 0;
		uint16_t ptr = 0;
		int stride = 0;
		bool reverse = false;
	} m_video;

	virtual void machine_reset() override ATTR_COLD;

	required_device<i8080_cpu_device> m_maincpu;
	required_device<nvram_device> m_nvram;
	required_device<address_map_bank_device> m_videobank;
	required_shared_ptr<u8> m_p_videoram;
	required_region_ptr<u8> m_p_chargen;
	required_shared_ptr<u8> m_p_charram;
	required_device<pic8259_device> m_pic8259;
	required_device<i8251_device> m_i8251line;
	required_device<rs232_port_device> m_rs232;
	required_device<i8251_device> m_i8251kbd;
	required_device<km035_device> m_keyboard;
	required_device<i8251_device> m_i8251prn;
	required_device<rs232_port_device> m_printer;
	required_device<pit8253_device> m_t_hblank;
	required_device<pit8253_device> m_t_vblank;
	required_device<pit8253_device> m_t_color;
	required_device<pit8253_device> m_t_iface;
	required_device<screen_device> m_screen;
};

void sm7238_state::sm7238_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x9fff).rom();
	map(0xa000, 0xa7ff).ram();
	map(0xb000, 0xb3ff).ram().share("nvram");
	map(0xb800, 0xb800).w(FUNC(sm7238_state::text_control_w));
	map(0xbc00, 0xbc00).w(FUNC(sm7238_state::control_w));
	map(0xc000, 0xcfff).ram().share("charram");
	map(0xe000, 0xffff).m(m_videobank, FUNC(address_map_bank_device::amap8));
}

void sm7238_state::videobank_map(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("videoram");
	map(0x2000, 0x2fff).mirror(0x1000).w(FUNC(sm7238_state::vmem_w));
}

void sm7238_state::sm7238_io(address_map &map)
{
	map.unmap_value_high();
//  map(0x40, 0x4f).ram() // LUT
	map(0xa0, 0xa1).rw(m_i8251line, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xa4, 0xa5).rw(m_i8251kbd, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xa8, 0xab).rw(m_t_color, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xac, 0xad).rw(m_pic8259, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0xb0, 0xb3).rw(m_t_hblank, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xb4, 0xb7).rw(m_t_vblank, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xb8, 0xb9).rw(m_i8251prn, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xbc, 0xbf).rw(m_t_iface, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
}

void sm7238_state::machine_reset()
{
	m_video = decltype(m_video)();
	m_videobank->set_bank(0);
}

void sm7238_state::control_w(uint8_t data)
{
	LOG("Control Write: %02xh: lut %d nvram %d c2 %d iack %d\n",
		data, BIT(data, 0), BIT(data, 2), BIT(data, 3), BIT(data, 5));
}

void sm7238_state::text_control_w(uint8_t data)
{
	if (data ^ m_video.control)
	{
		LOG("Text Control Write: %02xh: 80/132 %d dma %d clr %d dlt %d inv %d ?? %d\n",
			data, BIT(data, 0), BIT(data, 1), BIT(data, 2), BIT(data, 3), BIT(data, 4), BIT(data, 5));
	}

	if (BIT((data ^ m_video.control), 0))
	{
		m_video.stride = BIT(data, 0) ? 80 : 132;
		recompute_parameters();
	}

	if (BIT((data ^ m_video.control), 2))
	{
		m_videobank->set_bank(1 - BIT(data, 2));
	}

	m_video.reverse = BIT(data, 4);
	m_video.control = data;
}

void sm7238_state::vmem_w(offs_t offset, uint8_t data)
{
	m_p_videoram[offset] = data;
	m_p_videoram[offset + 0x1000] = data;
}

void sm7238_state::write_keyboard_clock(int state)
{
	m_i8251kbd->write_txc(state);
	m_i8251kbd->write_rxc(state);
}

void sm7238_state::write_printer_clock(int state)
{
	m_i8251prn->write_txc(state);
	m_i8251prn->write_rxc(state);
}

void sm7238_state::recompute_parameters()
{
	rectangle visarea;
	attoseconds_t refresh;

	visarea.set(0, m_video.stride * 8 - 1, 0, KSM_DISP_VERT - 1);

	if (m_video.stride == 80)
	{
		refresh = HZ_TO_ATTOSECONDS(12.5_MHz_XTAL) * m_video.stride * 10 * KSM_TOTAL_VERT;
	}
	else
	{
		refresh = HZ_TO_ATTOSECONDS(20.625_MHz_XTAL) * m_video.stride * 10 * KSM_TOTAL_VERT;
	}

	m_screen->configure(m_video.stride * 10, KSM_TOTAL_VERT, visarea, refresh);
}

uint32_t sm7238_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	if (!BIT(m_video.control, 3))
	{
		bitmap.fill(0);
		return 0;
	}

	uint8_t ctl2 = 0;
	uint16_t sy = 0, ma = 0;
	bool double_width = false, double_height = false, bottom_half = false;
	bool blink((m_screen->frame_number() % 30) > 14);

	for (uint8_t y = 0; y < 26; y++)
	{
		for (uint8_t ra = 0; ra < 10; ra++)
		{
			if (y == 1 && ctl2 && ra < ctl2)
				continue;

			uint16_t *p = &bitmap.pix(sy++, 0);

			for (uint16_t x = ma; x < ma + m_video.stride; x++)
			{
				uint16_t chr = m_p_videoram[x] << 4;
				uint8_t const attr = m_p_videoram[x + 0x1000];

				// alternate font 1
				if (BIT(attr, 6))
				{
					chr += 0x1000;
				}

				uint8_t bg = 0;
				uint8_t fg = 1;

				uint16_t chraddr;
				if (double_height)
				{
					chraddr = chr | (bottom_half ? (5 + (ra >> 1)) : (ra >> 1));
				}
				else
				{
					chraddr = chr | ra;
				}

				// alternate font 2 (downloadable font) -- only in models .05 and .06
				uint8_t gfx;
				if (BIT(attr, 7) && m_p_charram[chr + 15])
				{
					gfx = m_p_charram[chraddr] ^ 255;
				}
				else
				{
					gfx = m_p_chargen[chraddr] ^ 255;
				}

				/* Process attributes */
				if ((BIT(attr, 1)) && (ra == 9))
				{
					gfx = 0xff; // underline
				}
				if (BIT(attr, 2) && blink)
				{
					gfx = 0;
				}
				if (BIT(attr, 3))
				{
					gfx ^= 0xff; // reverse video
				}
				if (BIT(attr, 4))
				{
					fg = 2; // highlight
				}
				else
				{
					fg = 1;
				}
				if (m_video.reverse)
				{
					bg = fg;
					fg = 0;
				}

				for (int i = 7; i >= 0; i--)
				{
					*p++ = BIT(gfx, i) ? fg : bg;
					if (double_width)
						*p++ = BIT(gfx, i) ? fg : bg;
				}

				if (double_width) x++;
			}
		}
		uint8_t const ctl1 = m_p_videoram[ma + 0x1000 + m_video.stride];
		double_width = BIT(ctl1, 6);
		double_height = BIT(ctl1, 7);
		bottom_half = BIT(ctl1, 5);

		ctl2 = m_p_videoram[ma + 0x1000 + m_video.stride + 1] >> 4;

		ma = m_p_videoram[ma + m_video.stride + 1] |
			(m_p_videoram[ma + 0x1000 + m_video.stride + 1] << 8);
		ma &= 0x0fff;
	}

	return 0;
}


/* F4 Character Displayer */
static const gfx_layout sm7238_charlayout =
{
	8, 12,                  /* most chars use 8x10 pixels */
	512,                    /* 512 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	16*8                 /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_sm7238 )
	GFXDECODE_ENTRY("chargen", 0x0000, sm7238_charlayout, 0, 1)
GFXDECODE_END

void sm7238_state::sm7238(machine_config &config)
{
	I8080(config, m_maincpu, 16.5888_MHz_XTAL / 9);
	m_maincpu->set_addrmap(AS_PROGRAM, &sm7238_state::sm7238_mem);
	m_maincpu->set_addrmap(AS_IO, &sm7238_state::sm7238_io);
	m_maincpu->in_inta_func().set("pic8259", FUNC(pic8259_device::acknowledge));

	ADDRESS_MAP_BANK(config, "videobank").set_map(&sm7238_state::videobank_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x2000);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER, rgb_t::green());
	m_screen->set_raw(20.625_MHz_XTAL, KSM_TOTAL_HORZ, 0, KSM_DISP_HORZ, KSM_TOTAL_VERT, 0, KSM_DISP_VERT);
	m_screen->set_screen_update(FUNC(sm7238_state::screen_update));
	m_screen->screen_vblank().set(m_pic8259, FUNC(pic8259_device::ir2_w));
	m_screen->set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME_HIGHLIGHT);
	GFXDECODE(config, "gfxdecode", "palette", gfx_sm7238);

	PIC8259(config, m_pic8259, 0);
	m_pic8259->out_int_callback().set_inputline(m_maincpu, 0);

	PIT8253(config, m_t_hblank, 0);
	m_t_hblank->set_clk<1>(16.384_MHz_XTAL / 9); // FIXME -- keyboard is slower and doesn't sync otherwise
	m_t_hblank->out_handler<1>().set(FUNC(sm7238_state::write_keyboard_clock));

	PIT8253(config, m_t_vblank, 0);
	m_t_vblank->set_clk<2>(16.5888_MHz_XTAL / 9);
	m_t_vblank->out_handler<2>().set(FUNC(sm7238_state::write_printer_clock));

	PIT8253(config, m_t_color, 0);

	PIT8253(config, m_t_iface, 0);
	m_t_iface->set_clk<1>(16.5888_MHz_XTAL / 9);
	m_t_iface->out_handler<1>().set(m_i8251line, FUNC(i8251_device::write_txc));
	m_t_iface->set_clk<2>(16.5888_MHz_XTAL / 9);
	m_t_iface->out_handler<2>().set(m_i8251line, FUNC(i8251_device::write_rxc));

	// serial connection to host
	I8251(config, m_i8251line, 0);
	m_i8251line->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_i8251line->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	m_i8251line->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_i8251line->rxrdy_handler().set("pic8259", FUNC(pic8259_device::ir1_w));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "null_modem"));
	rs232.rxd_handler().set(m_i8251line, FUNC(i8251_device::write_rxd));
	rs232.cts_handler().set(m_i8251line, FUNC(i8251_device::write_cts));
	rs232.dsr_handler().set(m_i8251line, FUNC(i8251_device::write_dsr));

	// serial connection to KM-035 keyboard
	I8251(config, m_i8251kbd, 0);
	m_i8251kbd->txd_handler().set(m_keyboard, FUNC(km035_device::write_rxd));
	m_i8251kbd->rxrdy_handler().set(m_pic8259, FUNC(pic8259_device::ir3_w));

	KM035(config, m_keyboard, 0);
	m_keyboard->tx_handler().set(m_i8251kbd, FUNC(i8251_device::write_rxd));
	m_keyboard->rts_handler().set(m_i8251kbd, FUNC(i8251_device::write_cts));

	// serial connection to printer
	I8251(config, m_i8251prn, 0);
	m_i8251prn->rxrdy_handler().set("pic8259", FUNC(pic8259_device::ir3_w));

	rs232_port_device &prtr(RS232_PORT(config, "prtr", default_rs232_devices, nullptr));
	prtr.rxd_handler().set(m_i8251prn, FUNC(i8251_device::write_rxd));
	prtr.cts_handler().set(m_i8251prn, FUNC(i8251_device::write_cts));
	prtr.dsr_handler().set(m_i8251prn, FUNC(i8251_device::write_dsr));
}

ROM_START( sm7238 )
	ROM_REGION(0xa000, "maincpu", ROMREGION_ERASE00)
	// version 1.0
	ROM_LOAD( "01_6.064", 0x0000, 0x2000, CRC(10f98d90) SHA1(cbed0b92d8beb558ce27ccd8256e1b3ab7351a58))
	ROM_LOAD( "02_6.064", 0x2000, 0x2000, CRC(b22c0202) SHA1(68a5c45697c4a541a182f0762904d36f4496e344))
	ROM_LOAD( "03_6.064", 0x4000, 0x2000, CRC(3e9d37ad) SHA1(26eb257733a88bd5665a9601813934da27219bc2))
	ROM_LOAD( "04_6.064", 0x6000, 0x2000, CRC(7b8c9e06) SHA1(537bd35749c15ef66656553d9e7ec6a1f9671f98))
	// version 1.1 undumped

	ROM_REGION(0x2000, "chargen", ROMREGION_ERASE00)
	ROM_LOAD( "bsk1_00_2.064", 0x0000, 0x2000, CRC(1e3d5885) SHA1(5afdc10f775f424473c2a78de62e3bfc82bdddd1))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  STATE         INIT        COMPANY  FULLNAME   FLAGS
COMP( 1989, sm7238, 0,      0,      sm7238,  0,     sm7238_state, empty_init, "USSR",  "SM 7238", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_COLORS )



smc777.cpp
<---------------------------------------------------------------------->
// license:LGPL-2.1+
// copyright-holders:Angelo Salese
/**************************************************************************************************

SMC-777 (c) 1983 Sony

TODO:
- Implement SMC-70 specific features;
- Implement GFX modes other than 160x100x4
- ROM/RAM bankswitch, it apparently happens after one instruction prefetching.
  We currently use an hackish implementation until the MAME/MESS framework can
  support that ...
- keyboard input is very sluggish, convert to device_matrix_interface;
- cursor stuck in Bird Crash;
- add mc6845 features;
- interlace (cfr. cpm22 in setup mode);

**************************************************************************************************/

#include "emu.h"

#include "cpu/mcs48/mcs48.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "imagedev/snapquik.h"
#include "machine/74259.h"
#include "machine/timer.h"
#include "machine/wd_fdc.h"
#include "sound/beep.h"
#include "sound/sn76496.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

#define mc6845_h_char_total     (m_crtc_vreg[0]+1)
#define mc6845_h_display        (m_crtc_vreg[1])
#define mc6845_h_sync_pos       (m_crtc_vreg[2])
#define mc6845_sync_width       (m_crtc_vreg[3])
#define mc6845_v_char_total     (m_crtc_vreg[4]+1)
#define mc6845_v_total_adj      (m_crtc_vreg[5])
#define mc6845_v_display        (m_crtc_vreg[6])
#define mc6845_v_sync_pos       (m_crtc_vreg[7])
#define mc6845_mode_ctrl        (m_crtc_vreg[8])
#define mc6845_tile_height      (m_crtc_vreg[9]+1)
#define mc6845_cursor_y_start   (m_crtc_vreg[0x0a])
#define mc6845_cursor_y_end     (m_crtc_vreg[0x0b])
#define mc6845_start_addr       (((m_crtc_vreg[0x0c]<<8) & 0x3f00) | (m_crtc_vreg[0x0d] & 0xff))
#define mc6845_cursor_addr      (((m_crtc_vreg[0x0e]<<8) & 0x3f00) | (m_crtc_vreg[0x0f] & 0xff))
#define mc6845_light_pen_addr   (((m_crtc_vreg[0x10]<<8) & 0x3f00) | (m_crtc_vreg[0x11] & 0xff))
#define mc6845_update_addr      (((m_crtc_vreg[0x12]<<8) & 0x3f00) | (m_crtc_vreg[0x13] & 0xff))

class smc777_state : public driver_device
{
public:
	smc777_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_crtc(*this, "crtc")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0U)
		, m_ioctrl(*this, "ioctrl")
		, m_beeper(*this, "beeper")
		, m_gfxdecode(*this, "gfxdecode")
		, m_palette(*this, "palette")
	{ }

	void smc777(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

private:
	void mc6845_w(offs_t offset, uint8_t data);
	uint8_t vram_r(offs_t offset);
	uint8_t attr_r(offs_t offset);
	uint8_t pcg_r(offs_t offset);
	void vram_w(offs_t offset, uint8_t data);
	void attr_w(offs_t offset, uint8_t data);
	void pcg_w(offs_t offset, uint8_t data);
	uint8_t fbuf_r(offs_t offset);
	void fbuf_w(offs_t offset, uint8_t data);
	uint8_t key_r(offs_t offset);
	void key_w(offs_t offset, uint8_t data);
	void border_col_w(uint8_t data);
	uint8_t io_status_1c_r();
	uint8_t io_status_1d_r();
	void io_control_w(uint8_t data);
	void raminh_w(int state);
	void vsup_w(int state);
	void screen_lines_w(int state);
	void rgb_select_w(int state);
	void mt_on_w(int state);
	void sound_out_w(int state);
	void printer_strb_w(int state);
	void cas_out_w(int state);
	void color_mode_w(uint8_t data);
	void ramdac_w(offs_t offset, uint8_t data);
	uint8_t gcw_r();
	void gcw_w(uint8_t data);
	uint8_t main_map_r(offs_t offset);
	void main_map_w(offs_t offset, uint8_t data);
	uint8_t vsync_irq_status_r();
	void vsync_irq_enable_w(uint8_t data);
	void palette_init(palette_device &palette) const;
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void vsync_w(int state);
	TIMER_DEVICE_CALLBACK_MEMBER(keyboard_callback);

	uint8_t fdc_r(offs_t offset);
	void fdc_w(offs_t offset, uint8_t data);
	uint8_t fdc1_fast_status_r();
	void fdc1_select_w(uint8_t data);
	void fdc_intrq_w(int state);
	void fdc_drq_w(int state);

	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);

	void main_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<mc6845_device> m_crtc;
	required_device<mb8876_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<ls259_device> m_ioctrl;
	required_device<beep_device> m_beeper;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<palette_device> m_palette;

	uint8_t *m_ipl_rom = nullptr;
	std::unique_ptr<uint8_t[]> m_work_ram;
	std::unique_ptr<uint8_t[]> m_vram;
	std::unique_ptr<uint8_t[]> m_attr;
	std::unique_ptr<uint8_t[]> m_gvram;
	std::unique_ptr<uint8_t[]> m_pcg;

	uint8_t m_keyb_press = 0;
	uint8_t m_keyb_press_flag = 0;
	uint8_t m_shift_press_flag = 0;
	uint8_t m_backdrop_pen = 0;
	uint8_t m_display_reg = 0;
	uint8_t m_fdc_irq_flag = 0;
	uint8_t m_fdc_drq_flag = 0;
	struct { uint8_t r = 0, g = 0, b = 0; } m_pal;
	uint8_t m_raminh = 0, m_raminh_pending_change = 0; //bankswitch
	uint8_t m_raminh_prefetch = 0;
	uint8_t m_pal_mode = 0;
	uint8_t m_keyb_cmd = 0;
	uint8_t m_crtc_vreg[0x20]{};
	uint8_t m_crtc_addr = 0;
	bool m_vsync_idf = false;
	bool m_vsync_ief = false;
};


/* TODO: correct calculation thru mc6845 regs */
#define CRTC_MIN_X 24*8
#define CRTC_MIN_Y 4*8

void smc777_state::video_start()
{
}

uint32_t smc777_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint16_t count;

//  popmessage("%d %d %d %d",mc6845_v_char_total,mc6845_v_total_adj,mc6845_v_display,mc6845_v_sync_pos);

	bitmap.fill(m_palette->pen(m_backdrop_pen), cliprect);

	int x_width = ((m_display_reg & 0x80) >> 7);

	count = 0x0000;

	for(int yi=0;yi<8;yi++)
	{
		for(int y=0;y<200;y+=8)
		{
			for(int x=0;x<160;x++)
			{
				uint16_t color = (m_gvram[count] & 0xf0) >> 4;

				/* todo: clean this up! */
				//if(x_width)
				{
					bitmap.pix(y+yi+CRTC_MIN_Y, x*4+0+CRTC_MIN_X) = m_palette->pen(color);
					bitmap.pix(y+yi+CRTC_MIN_Y, x*4+1+CRTC_MIN_X) = m_palette->pen(color);
				}
				//else
				//{
				//  bitmap.pix(y+yi+CRTC_MIN_Y, x*2+0+CRTC_MIN_X) = m_palette->pen(color);
				//}

				color = (m_gvram[count] & 0x0f) >> 0;
				//if(x_width)
				{
					bitmap.pix(y+yi+CRTC_MIN_Y, x*4+2+CRTC_MIN_X) = m_palette->pen(color);
					bitmap.pix(y+yi+CRTC_MIN_Y, x*4+3+CRTC_MIN_X) = m_palette->pen(color);
				}
				//else
				//{
				//  bitmap.pix(y+yi+CRTC_MIN_Y, x*2+1+CRTC_MIN_X) = m_palette->pen(color);
				//}

				count++;

			}
		}
		count+= 0x60;
	}

	count = 0x0000;

	for(int y=0;y<25;y++)
	{
		for(int x=0;x<80/(x_width+1);x++)
		{
			/*
			-x-- ---- blink
			--xx x--- bg color (00 transparent, 01 white, 10 black, 11 complementary to fg color
			---- -xxx fg color
			*/
			int tile = m_vram[count];
			int color = m_attr[count] & 7;
			int bk_color = (m_attr[count] & 0x18) >> 3;
			int blink = m_attr[count] & 0x40;
			//int bk_struct[4] = { -1, 0x10, 0x11, (color & 7) ^ 8 };

			int bk_pen = -1;
			switch(bk_color & 3)
			{
				case 0: bk_pen = -1; break; //transparent
				case 1: bk_pen = 0x17; break; //white
				case 2: bk_pen = 0x10; break; //black
				case 3: bk_pen = (color ^ 0xf); break; //complementary
			}

			if(blink && m_screen->frame_number() & 0x10) //blinking, used by Dragon's Alphabet
				color = bk_pen;

			for(int yi=0;yi<8;yi++)
			{
				for(int xi=0;xi<8;xi++)
				{
					int pen = ((m_pcg[tile*8+yi]>>(7-xi)) & 1) ? (color+m_pal_mode) : bk_pen;

					if (pen != -1)
					{
						if(x_width)
						{
							bitmap.pix(y*8+CRTC_MIN_Y+yi, (x*8+xi)*2+0+CRTC_MIN_X) = m_palette->pen(pen);
							bitmap.pix(y*8+CRTC_MIN_Y+yi, (x*8+xi)*2+1+CRTC_MIN_X) = m_palette->pen(pen);
						}
						else
							bitmap.pix(y*8+CRTC_MIN_Y+yi, x*8+CRTC_MIN_X+xi) = m_palette->pen(pen);
					}
				}
			}

			// draw cursor
			if(mc6845_cursor_addr == count)
			{
				int cursor_on = 0;
				switch(mc6845_cursor_y_start & 0x60)
				{
					case 0x00: cursor_on = 1; break; //always on
					case 0x20: cursor_on = 0; break; //always off
					case 0x40: if(m_screen->frame_number() & 0x10) { cursor_on = 1; } break; //fast blink
					case 0x60: if(m_screen->frame_number() & 0x20) { cursor_on = 1; } break; //slow blink
				}

				if(cursor_on)
				{
					for(int yc=0;yc<(8-(mc6845_cursor_y_start & 7));yc++)
					{
						for(int xc=0;xc<8;xc++)
						{
							if(x_width)
							{
								bitmap.pix(y*8+CRTC_MIN_Y-yc+7, (x*8+xc)*2+0+CRTC_MIN_X) = m_palette->pen(0x7);
								bitmap.pix(y*8+CRTC_MIN_Y-yc+7, (x*8+xc)*2+1+CRTC_MIN_X) = m_palette->pen(0x7);
							}
							else
								bitmap.pix(y*8+CRTC_MIN_Y-yc+7, x*8+CRTC_MIN_X+xc) = m_palette->pen(0x7);
						}
					}
				}
			}

			(m_display_reg & 0x80) ? count+=2 : count++;
		}
	}

	return 0;
}

void smc777_state::mc6845_w(offs_t offset, uint8_t data)
{
	if(offset == 0)
	{
		m_crtc_addr = data;
		m_crtc->address_w(data);
	}
	else
	{
		m_crtc_vreg[m_crtc_addr] = data;
		m_crtc->register_w(data);
	}
}

uint8_t smc777_state::vram_r(offs_t offset)
{
	uint16_t vram_index;

	vram_index  = ((offset & 0x0007) << 8);
	vram_index |= ((offset & 0xff00) >> 8);

	return m_vram[vram_index];
}

uint8_t smc777_state::attr_r(offs_t offset)
{
	uint16_t vram_index;

	vram_index  = ((offset & 0x0007) << 8);
	vram_index |= ((offset & 0xff00) >> 8);

	return m_attr[vram_index];
}

uint8_t smc777_state::pcg_r(offs_t offset)
{
	uint16_t vram_index;

	vram_index  = ((offset & 0x0007) << 8);
	vram_index |= ((offset & 0xff00) >> 8);

	return m_pcg[vram_index];
}

void smc777_state::vram_w(offs_t offset, uint8_t data)
{
	uint16_t vram_index;

	vram_index  = ((offset & 0x0007) << 8);
	vram_index |= ((offset & 0xff00) >> 8);

	m_vram[vram_index] = data;
}

void smc777_state::attr_w(offs_t offset, uint8_t data)
{
	uint16_t vram_index;

	vram_index  = ((offset & 0x0007) << 8);
	vram_index |= ((offset & 0xff00) >> 8);

	m_attr[vram_index] = data;
}

void smc777_state::pcg_w(offs_t offset, uint8_t data)
{
	uint16_t vram_index;

	vram_index  = ((offset & 0x0007) << 8);
	vram_index |= ((offset & 0xff00) >> 8);

	m_pcg[vram_index] = data;

	m_gfxdecode->gfx(0)->mark_dirty(vram_index >> 3);
}

uint8_t smc777_state::fbuf_r(offs_t offset)
{
	uint16_t vram_index;

	vram_index  = ((offset & 0x007f) << 8);
	vram_index |= ((offset & 0xff00) >> 8);

	return m_gvram[vram_index];
}

void smc777_state::fbuf_w(offs_t offset, uint8_t data)
{
	uint16_t vram_index;

	vram_index  = ((offset & 0x00ff) << 8);
	vram_index |= ((offset & 0xff00) >> 8);

	m_gvram[vram_index] = data;
}


/***********************************************************

    Quickload

    This loads a .COM file to address 0x100 then jumps
    there. Sometimes .COM has been renamed to .CPM to
    prevent windows going ballistic. These can be loaded
    as well.

************************************************************/

QUICKLOAD_LOAD_MEMBER(smc777_state::quickload_cb)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);

	// Avoid loading a program if CP/M-80 is not in memory
	if ((prog_space.read_byte(0) != 0xc3) || (prog_space.read_byte(5) != 0xc3))
	{
		machine_reset();
		return std::make_pair(image_error::UNSUPPORTED, "CP/M must already be running");
	}

	// Check for sufficient RAM based on position of CPM
	const int mem_avail = 256 * prog_space.read_byte(7) + prog_space.read_byte(6) - 512;
	if (mem_avail < image.length())
		return std::make_pair(image_error::UNSPECIFIED, "Insufficient memory available");

	// Load image to the TPA (Transient Program Area)
	uint16_t quickload_size = image.length();
	for (uint16_t i = 0; i < quickload_size; i++)
	{
		uint8_t data;
		if (image.fread(&data, 1) != 1)
			return std::make_pair(image_error::UNSPECIFIED, "Problem reading the image at offset " + std::to_string(i));
		prog_space.write_byte(i + 0x100, data);
	}

	// clear out command tail
	prog_space.write_byte(0x80, 0);
	prog_space.write_byte(0x81, 0);

	// Roughly set SP basing on the BDOS position
	m_maincpu->set_state_int(Z80_SP, mem_avail + 384);
	m_maincpu->set_pc(0x100); // start program

	return std::make_pair(std::error_condition(), std::string());
}

uint8_t smc777_state::fdc_r(offs_t offset)
{
	return m_fdc->read(offset) ^ 0xff;
}

void smc777_state::fdc_w(offs_t offset, uint8_t data)
{
	m_fdc->write(offset, data ^ 0xff);
}

uint8_t smc777_state::fdc1_fast_status_r()
{
	uint8_t data = 0;

	// TODO: inverted wrt documentation?
	data |= !m_fdc_drq_flag << 6;
	data |= m_fdc_irq_flag << 7;

	return data;
}

void smc777_state::fdc1_select_w(uint8_t data)
{
	floppy_image_device *floppy = nullptr;

	// x--- ---- SIDE1: [SMC-70] side select
	// ---- --x- EXDS: [SMC-70] external drive select (0=internal, 1=external)
	// ---- ---x DS01: select floppy drive
	floppy = m_floppy[data & 1]->get_device();

	m_fdc->set_floppy(floppy);

	// no idea where the motor on signal is
	if (floppy)
		floppy->mon_w(0);

	if(data & 0xf0)
		logerror("floppy access %02x\n", data);
}

void smc777_state::fdc_intrq_w(int state)
{
	m_fdc_irq_flag = state;
}

void smc777_state::fdc_drq_w(int state)
{
	m_fdc_drq_flag = state;
}

uint8_t smc777_state::key_r(offs_t offset)
{
	/*
	-x-- ---- shift key
	---- -x-- MCU data ready
	---- ---x handshake bit?
	*/

	switch(m_keyb_cmd)
	{
		case 0x00: //poll keyboard input
		{
			if(offset == 0)
				m_keyb_press_flag = 0;

			return (offset == 0) ? m_keyb_press : ((m_shift_press_flag << 6) | (m_keyb_press_flag << 2) | (m_keyb_press_flag));
		}
		default:
		{
			//if(offset == 1)
			//  logerror("Unknown keyboard command %02x read-back\n",m_keyb_cmd);

			return (offset == 0) ? 0x00 : (machine().rand() & 0x5);
		}
	}

	// never executed
	//return 0x00;
}

// TODO: pinpoint interface type
void smc777_state::key_w(offs_t offset, uint8_t data)
{
	if(offset == 1) //keyboard command
		m_keyb_cmd = data;
	else
	{
		// keyboard command param
	}
}

void smc777_state::border_col_w(uint8_t data)
{
	if(data & 0xf0)
		logerror("Special border color enabled %02x\n",data);

	m_backdrop_pen = data & 0xf;
}


uint8_t smc777_state::io_status_1c_r()
{
	/*
	 * RES     x--- ---- Power On bit (1=reset switch)
	 * HiZ     -x-- ---- [SMC-70] no drive (always '1'?)
	 * LPH     --x- ---- [SMC-70] light pen H position
	 * CP      ---x ---- [SMC-777] color board (active low)
	 * LPV     ---x x--- [SMC-70] light pen V position
	 * ID      ---- -x-- 0=SMC-777 1=SMC-70
	 * MD      ---- --xx [SMC-70] boot mode (00=DISK; 10=ROM; 11=EXT)
	 */
	logerror("System R\n");

	return 0;
}

uint8_t smc777_state::io_status_1d_r()
{
	/*
	 * TCIN    x--- ---- CMT read data
	 * HiZ     -x-- ---- [SMC-70] no drive (always '1'?)
	 * LPIN    --x- ---- [SMC-70] light pen input
	 * PR_BUSY ---x ---- printer busy
	 * PR_ACK  ---- x--- printer ACK
	 * ID      ---- -x-- 0=SMC-777 1=SMC-70
	 * MD      ---- --xx [SMC-70] boot mode (00=DISK; 10=ROM; 11=EXT)
	 */
	return 0;
}


void smc777_state::io_control_w(uint8_t data)
{
	m_ioctrl->write_bit(data & 0x07, BIT(data, 4));
}

void smc777_state::raminh_w(int state)
{
	// "ROM / RAM register change is done at the beginning of the next M1 cycle"
	m_raminh_pending_change = state ^ 1;
	m_raminh_prefetch = (uint8_t)(m_maincpu->state_int(Z80_R)) & 0x7f;
}

void smc777_state::vsup_w(int state)
{
	logerror("%s: Display %sabled\n", machine().describe_context(), state ? "dis" : "en");
}

void smc777_state::screen_lines_w(int state)
{
	logerror("%s: Screen line number %d\n", machine().describe_context(), state ? 625 : 525);
}

void smc777_state::rgb_select_w(int state)
{
	// 0=RGB 1=Component
	logerror("%s: %s video selected\n", machine().describe_context(), state ? "Component" : "RGB");
}

void smc777_state::mt_on_w(int state)
{
	logerror("%s: Cassette monitor %s\n", machine().describe_context(), state ? "on" : "off");
}

void smc777_state::sound_out_w(int state)
{
	m_beeper->set_state(state);
}

void smc777_state::printer_strb_w(int state)
{
	logerror("%s: Printer strobe %d\n", machine().describe_context(), !state);
}

void smc777_state::cas_out_w(int state)
{
	logerror("%s: Cassette write %d\n", machine().describe_context(), state);
}

void smc777_state::color_mode_w(uint8_t data)
{
	/*
	 * ---x -111 gfx palette select
	 * ---x -110 text palette select
	 * ---x -101 joy 2 out
	 * ...
	 * ---x -000 joy 2 out
	 */
	switch(data & 0x07)
	{
		case 0x06: m_pal_mode = (data & 0x10) ^ 0x10; break;
		default: logerror("Color FF %02x\n",data); break;
	}
}

void smc777_state::ramdac_w(offs_t offset, uint8_t data)
{
	uint8_t pal_index;
	pal_index = (offset & 0xf00) >> 8;

	if(data & 0x0f)
		logerror("RAMdac used with data bits 0-3 set (%02x)\n",data);

	switch((offset & 0x3000) >> 12)
	{
		case 0: m_pal.r = (data & 0xf0) >> 4; m_palette->set_pen_color(pal_index, pal4bit(m_pal.r), pal4bit(m_pal.g), pal4bit(m_pal.b)); break;
		case 1: m_pal.g = (data & 0xf0) >> 4; m_palette->set_pen_color(pal_index, pal4bit(m_pal.r), pal4bit(m_pal.g), pal4bit(m_pal.b)); break;
		case 2: m_pal.b = (data & 0xf0) >> 4; m_palette->set_pen_color(pal_index, pal4bit(m_pal.r), pal4bit(m_pal.g), pal4bit(m_pal.b)); break;
		case 3: logerror("RAMdac used with gradient index = 3! pal_index = %02x data = %02x\n",pal_index,data); break;
	}
}

uint8_t smc777_state::gcw_r()
{
	return m_display_reg;
}

/*
 * x--- ---- text mode (0 = 80x25 1 = 40x25)
 * -x-- ---- text page (in 40x25 mode)
 * --x- ---- color mode (1=for 2bpp mode, blue is replaced with white)
 * ---x ---- [SMC-70] interlace
 * ---- xxyy gfx mode (model dependant)
 * [SMC-70]
 * ---- 11-- 640x400x1 bpp
 * ---- 10-- 640x200x2 bpp
 * ---- 01-- 320x200x4 bpp
 * ---- 00yy 160x100x4 bpp, bits 0-1 selects page
 * [SMC-777]
 * ---- 1--- 640x200x2 bpp
 * ---- 0--- 320x200x4 bpp
 */
void smc777_state::gcw_w(uint8_t data)
{
	m_display_reg = data;
}

uint8_t smc777_state::main_map_r(offs_t offset)
{
	uint8_t z80_r;

	// TODO: do the bankswitch AFTER that the prefetch instruction is executed (hackish implementation)
	if(m_raminh_prefetch != 0xff && !machine().side_effects_disabled())
	{
		z80_r = (uint8_t)m_maincpu->state_int(Z80_R);

		if(z80_r == ((m_raminh_prefetch+1) & 0x7f))
		{
			m_raminh = m_raminh_pending_change;
			m_raminh_prefetch = 0xff;
		}
	}

	if(m_raminh == 1 && ((offset & 0xc000) == 0))
		return m_ipl_rom[offset];

	return m_work_ram[offset];
}

void smc777_state::main_map_w(offs_t offset, uint8_t data)
{
	m_work_ram[offset] = data;
}

uint8_t smc777_state::vsync_irq_status_r()
{
	if (m_vsync_idf == true)
	{
		m_vsync_idf = false;
		return 1;
	}

	return 0;
}

void smc777_state::vsync_irq_enable_w(uint8_t data)
{
	if(data & 0xfe)
		logerror("Irq mask = %02x\n",data & 0xfe);

	// IRQ mask
	m_vsync_ief = BIT(data,0);
	// TODO: clear idf on 1->0 irq mask transition?
}

void smc777_state::main_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(smc777_state::main_map_r), FUNC(smc777_state::main_map_w));
}

void smc777_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x07).select(0xff00).rw(FUNC(smc777_state::vram_r), FUNC(smc777_state::vram_w));
	map(0x08, 0x0f).select(0xff00).rw(FUNC(smc777_state::attr_r), FUNC(smc777_state::attr_w));
	map(0x10, 0x17).select(0xff00).rw(FUNC(smc777_state::pcg_r), FUNC(smc777_state::pcg_w));
	map(0x18, 0x19).mirror(0xff00).w(FUNC(smc777_state::mc6845_w));
	map(0x1a, 0x1b).mirror(0xff00).rw(FUNC(smc777_state::key_r), FUNC(smc777_state::key_w));
	map(0x1c, 0x1c).mirror(0xff00).rw(FUNC(smc777_state::io_status_1c_r), FUNC(smc777_state::io_control_w));
	map(0x1d, 0x1d).mirror(0xff00).rw(FUNC(smc777_state::io_status_1d_r), FUNC(smc777_state::io_control_w));
//  map(0x1e, 0x1f) rs232 irq control
	map(0x20, 0x20).mirror(0xff00).rw(FUNC(smc777_state::gcw_r), FUNC(smc777_state::gcw_w));
	map(0x21, 0x21).mirror(0xff00).rw(FUNC(smc777_state::vsync_irq_status_r), FUNC(smc777_state::vsync_irq_enable_w));
//  map(0x22, 0x22) printer output data
	map(0x23, 0x23).mirror(0xff00).w(FUNC(smc777_state::border_col_w));
//  map(0x24, 0x24) rtc write address (M5M58321RS)
//  map(0x25, 0x25) rtc read
//  map(0x26, 0x26) rs232 #1
//  map(0x28, 0x2c) fdc #2 (8")
//  map(0x2d, 0x2f) rs232 #2
	map(0x30, 0x33).mirror(0xff00).rw(FUNC(smc777_state::fdc_r), FUNC(smc777_state::fdc_w));
	map(0x34, 0x34).mirror(0xff00).rw(FUNC(smc777_state::fdc1_fast_status_r), FUNC(smc777_state::fdc1_select_w));
//  map(0x35, 0x37) rs232 #3
//  map(0x38, 0x3b) cache disk unit
	// 0x38 (R) CDSTS status port (W) CDCMD command port
	// 0x39 (W) track register
	// 0x3a (W) sector register
	// 0x3b (RW) data port
//  map(0x3c, 0x3d) rgb superimposer / genlock control
//  map(0x40, 0x47) ieee-488 / TMS9914A I/F
	map(0x44, 0x44).mirror(0xff00).portr("GPDSW"); // normally unmapped in GPIB interface
//  map(0x48, 0x49) hdd (winchester)
	// TODO: address bit 8 selects joy port 2
	map(0x51, 0x51).mirror(0xff00).portr("JOY_1P").w(FUNC(smc777_state::color_mode_w));
	map(0x52, 0x52).select(0xff00).w(FUNC(smc777_state::ramdac_w));
	map(0x53, 0x53).mirror(0xff00).w("sn1", FUNC(sn76489a_device::write));
//  map(0x54, 0x59) vrt controller
//  map(0x5a, 0x5b) ram banking
//  map(0x70, 0x70) auto-start ROM (ext-ROM)
//  map(0x74, 0x74) ieee-488 GPIB ROM port
//  map(0x75, 0x75) vrt controller ROM
//  map(0x7e, 0x7f) kanji ROM
	map(0x80, 0xff).select(0xff00).rw(FUNC(smc777_state::fbuf_r), FUNC(smc777_state::fbuf_w));
}


static INPUT_PORTS_START( smc777 )
	PORT_START("key0") //0x00-0x07
	PORT_BIT (0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)      PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT (0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)      PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT (0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)      PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT (0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)      PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT (0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)      PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT (0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)      PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT (0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)      PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT (0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)      PORT_CHAR(UCHAR_MAMEKEY(7_PAD))

	PORT_START("key1") //0x08-0x0f
	PORT_BIT (0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)      PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT (0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)      PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT (0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/ PAD") PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT (0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("* PAD") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT (0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("- PAD") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT (0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+ PAD") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT (0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENTER PAD") PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13)
	PORT_BIT (0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(". PAD") PORT_CODE(KEYCODE_DEL_PAD)

	PORT_START("key2") //0x10-0x17
	PORT_BIT (0x01, IP_ACTIVE_HIGH,IPT_UNUSED) // PORT_NAME("@") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@')
	PORT_BIT (0x02, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT (0x04, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT (0x08, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT (0x10, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT (0x20, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT (0x40, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT (0x80, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('G')

	PORT_START("key3") //0x18-0x1f
	PORT_BIT (0x01, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT (0x02, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT (0x04, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT (0x08, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT (0x10, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT (0x20, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT (0x40, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT (0x80, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('O')

	PORT_START("key4") //0x20-0x27
	PORT_BIT (0x01, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT (0x02, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT (0x04, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT (0x08, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT (0x10, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT (0x20, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT (0x40, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT (0x80, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('W')

	PORT_START("key5") //0x28-0x2f
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0-1") /* TODO: labels */
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0-2")
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1-1")
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1-2")

	PORT_START("key6") //0x30-0x37
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x80,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')

	PORT_START("key7") //0x38-0x3f
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2-1") /* TODO: labels */
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2-3")
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3-1")
	PORT_BIT(0x20,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3-2")
	PORT_BIT(0x40,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3-3")

	PORT_START("key8") //0x40-0x47
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(27)
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("BACKSPACE") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("TAB") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("ESC") PORT_CHAR(27)
	PORT_BIT(0x20, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("INS") PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x40, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL)
	/* TODO: control inputs */

	PORT_START("key9") //0x40-0x47
	/* TODO: cursor inputs */
	PORT_BIT(0x01, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("CURSOR UP") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x02, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("CURSOR DOWN") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x04, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("CURSOR LEFT") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x08, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("CURSOR RIGHT") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x10, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("HOME") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x20, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("END") PORT_CODE(KEYCODE_END)
	PORT_BIT(0x40, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PGUP") PORT_CODE(KEYCODE_PGUP)
	PORT_BIT(0x80, IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PGDOWN") PORT_CODE(KEYCODE_PGDN)

	PORT_START("keya") //0x48-0x4f
	PORT_BIT(0x01,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x02,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x04,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x08,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x10,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5)

	PORT_START("key_mod")
	PORT_BIT (0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT (0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KANA SHIFT") PORT_CODE(KEYCODE_LALT)


	#if 0
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')

	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':')
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';')
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[')
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(']')
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^')
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("_")
	#endif

	PORT_START("JOY_1P")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH,IPT_UNKNOWN ) //status?

	PORT_START("GPDSW")
	PORT_DIPNAME( 0x01, 0x00, "GPDSW" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )
INPUT_PORTS_END

/*
    Keyboard data:
    kana lock |= 0x80
    numpad 0 to 9 = 0x30 - 0x39
    / pad = 0x2f
    * pad = 0x2a
    - pad = 0x2d
    + pad = 0x2b
    ENTER pad = 0x0d
    . pad = 0x2e
    enter = 0x0d
    space = 0x20
    backspace = 0x08
    CTRL (TAB?) = 0x09
    cursor up = 0x17
    cursor down = 0x1c
    cursor left = 0x16
    cursor right = 0x19
    0 to 9 -> 0x30 to 0x39
    S + 0 = 0x29
    S + 1 = 0x21
    S + 2 = 0x40
    S + 3 = 0x23
    S + 4 = 0x24
    S + 5 = 0x25
    S + 6 = 0x5e
    S + 7 = 0x26
    S + 8 = 0x2a
    S + 9 = 0x28
    F1 = 0x01 / 0x15
    F2 = 0x02 / 0x18
    F3 = 0x04 / 0x12
    F4 = 0x06 / 0x05
    F5 = 0x0b / 0x03
    ESC = 0x1b
    INS = 0x0f
    DEL = 0x11
    PG UP = 0x12
    PG DOWN = 0x03
    HOME = 0x14
    END = 0x0e
    ' = 0x2d / 0x5f
    "P" row 1 ] = 0x5d / 0x7d
    "P" row 2 ' / ~ = 0x60 / 0x7e
    "L" row 1 ; / : = 0x3b / 0x3a
    "L" row 2 = (unused)
    "L" row 3 Yen / | = 0x5c / 0x7c
    "M" row 1 , / < = 0x2c / 0x3c
    "M" row 2 . / > = 0x2e / 0x3e
    "M" row 3 / / ? = 0x2f / 0x3f
*/

static const uint8_t smc777_keytable[2][0xa0] =
{
	/* normal*/
	{
		0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* numpad */
		0x38, 0x39, 0x2f, 0x2a, 0x2d, 0x2b, 0x0d, 0x2e,
		0xff, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* A - G */
		0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* H - O */
		0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* P - W */
		0x78, 0x79, 0x7a, 0x2d, 0x5d, 0x60, 0xff, 0xff, /* X - Z */
		0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0 - 7*/
		0x38, 0x39, 0x3b, 0x5c, 0x2c, 0x2e, 0x2f, 0xff, /* 8 - 9 */
		0x0d, 0x20, 0x08, 0x09, 0x1b, 0x0f, 0x11, 0xff,
		0x17, 0x1c, 0x16, 0x19, 0x14, 0x0e, 0x12, 0x03,
		0x01, 0x02, 0x04, 0x06, 0x0b, 0xff, 0xff, 0xff,

	},
	/* shift */
	{
		0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* numpad */
		0x38, 0x39, 0x2f, 0x2a, 0x2d, 0x2b, 0x0d, 0x2e,
		0xff, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
		0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* H - O */
		0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* P - W */
		0x58, 0x59, 0x5a, 0x5f, 0x7d, 0x7e, 0xff, 0xff, /* X - Z */
		0x29, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5e, 0x26,
		0x2a, 0x28, 0x3a, 0x7c, 0x3c, 0x3e, 0x3f, 0xff,
		0x0d, 0x20, 0x08, 0x09, 0x1b, 0x0f, 0x11, 0xff,
		0x17, 0x1c, 0x16, 0x19, 0x14, 0x0e, 0x12, 0x03,
		0x15, 0x18, 0x12, 0x05, 0x03, 0xff, 0xff, 0xff, /* F1 - F5 */
	}
};

TIMER_DEVICE_CALLBACK_MEMBER(smc777_state::keyboard_callback)
{
	static const char *const portnames[11] = { "key0","key1","key2","key3","key4","key5","key6","key7", "key8", "key9", "keya" };
	int i,port_i,scancode;
	uint8_t shift_mod = ioport("key_mod")->read() & 1;
	uint8_t kana_mod = ioport("key_mod")->read() & 0x10;
	scancode = 0;

	for(port_i=0;port_i<11;port_i++)
	{
		for(i=0;i<8;i++)
		{
			if((ioport(portnames[port_i])->read()>>i) & 1)
			{
				m_keyb_press = smc777_keytable[shift_mod & 1][scancode];
				if(kana_mod) { m_keyb_press|=0x80; }
				m_keyb_press_flag = 1;
				m_shift_press_flag = shift_mod & 1;
				return;
			}
			scancode++;
		}
	}
}

static const gfx_layout smc777_charlayout =
{
	8, 8,
	0x800 / 8,
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};

void smc777_state::machine_start()
{
	m_ipl_rom = memregion("ipl")->base();
	m_work_ram = make_unique_clear<uint8_t[]>(0x10000);
	m_vram = make_unique_clear<uint8_t[]>(0x800);
	m_attr = make_unique_clear<uint8_t[]>(0x800);
	m_gvram = make_unique_clear<uint8_t[]>(0x8000);
	m_pcg = make_unique_clear<uint8_t[]>(0x800);

	save_pointer(NAME(m_work_ram), 0x10000);
	save_pointer(NAME(m_vram), 0x800);
	save_pointer(NAME(m_attr), 0x800);
	save_pointer(NAME(m_gvram), 0x8000);
	save_pointer(NAME(m_pcg), 0x800);

	m_gfxdecode->set_gfx(0, std::make_unique<gfx_element>(m_palette, smc777_charlayout, m_pcg.get(), 0, 8, 0));
}

void smc777_state::machine_reset()
{
	m_raminh = 1;
	m_raminh_pending_change = 1;
	m_raminh_prefetch = 0xff;
	m_pal_mode = 0x10;
	m_vsync_idf = false;

	m_beeper->set_state(0);
}


void smc777_state::palette_init(palette_device &palette) const
{
	// set-up SMC-70 mode colors
	for(int i = 0x10; i < 0x18; i++)
	{
		uint8_t const r = BIT(i, 2);
		uint8_t const g = BIT(i, 1);
		uint8_t const b = BIT(i, 0);

		palette.set_pen_color(i, pal1bit(r), pal1bit(g), pal1bit(b));
		palette.set_pen_color(i+8, rgb_t::black());
	}
}


void smc777_state::vsync_w(int state)
{
	if (state && m_vsync_ief)
	{
		m_maincpu->set_input_line(0,HOLD_LINE);
		m_vsync_idf = true;
	}
}


static void smc777_floppies(device_slot_interface &device)
{
	device.option_add("ssdd", FLOPPY_35_SSDD);
}


void smc777_state::smc777(machine_config &config)
{
	constexpr XTAL MASTER_CLOCK = 32.2238_MHz_XTAL;

	Z80(config, m_maincpu, MASTER_CLOCK / 8); // nominally 4.028 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &smc777_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &smc777_state::io_map);

	I8041A(config, "mcu", 6_MHz_XTAL).set_disable();

	LS259(config, m_ioctrl);
	m_ioctrl->q_out_cb<0>().set(FUNC(smc777_state::raminh_w));
	m_ioctrl->q_out_cb<1>().set(FUNC(smc777_state::vsup_w));
	m_ioctrl->q_out_cb<2>().set(FUNC(smc777_state::screen_lines_w));
	m_ioctrl->q_out_cb<3>().set(FUNC(smc777_state::rgb_select_w));
	m_ioctrl->q_out_cb<4>().set(FUNC(smc777_state::mt_on_w));
	m_ioctrl->q_out_cb<5>().set(FUNC(smc777_state::sound_out_w));
	m_ioctrl->q_out_cb<6>().set(FUNC(smc777_state::printer_strb_w));
	m_ioctrl->q_out_cb<7>().set(FUNC(smc777_state::cas_out_w));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	// TODO: retrieve defaults from 6845
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_size(0x400, 400);
	m_screen->set_visarea(0, 660-1, 0, 220-1); //normal 640 x 200 + 20 pixels for border color
	m_screen->set_screen_update(FUNC(smc777_state::screen_update));
	m_screen->set_palette(m_palette);
	// TODO: 6845 CCLK width is likely supposed to handle this (currently draws 1023x261)
	m_screen->set_default_position(1.500, 0.000, 1.125, 0.000);

	// 16 + 8 colors (SMC-777 + SMC-70) + 8 empty entries (SMC-70)
	PALETTE(config, m_palette, FUNC(smc777_state::palette_init), 0x20);

	GFXDECODE(config, m_gfxdecode, m_palette, gfxdecode_device::empty);

	HD6845S(config, m_crtc, MASTER_CLOCK / 16); // HD68A45SP; unknown clock, hand tuned to get ~60 fps
	m_crtc->set_screen(m_screen);
	m_crtc->set_show_border_area(true);
	m_crtc->set_char_width(8);
	m_crtc->out_vsync_callback().set(FUNC(smc777_state::vsync_w));

	MB8876(config, m_fdc, MASTER_CLOCK / 32); // divider not confirmed
	m_fdc->intrq_wr_callback().set(FUNC(smc777_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(smc777_state::fdc_drq_w));

	// does it really support 16 of them?
	FLOPPY_CONNECTOR(config, m_floppy[0], smc777_floppies, "ssdd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[1], smc777_floppies, "ssdd", floppy_image_device::default_mfm_floppy_formats);

	SOFTWARE_LIST(config, "flop_list").set_original("smc777");
	QUICKLOAD(config, "quickload", "com,cpm", attotime::from_seconds(3)).set_load_callback(FUNC(smc777_state::quickload_cb));

	SPEAKER(config, "mono").front_center();

	SN76489A(config, "sn1", MASTER_CLOCK / 8).add_route(ALL_OUTPUTS, "mono", 0.50); // unknown clock / divider

	BEEP(config, m_beeper, 300); // TODO: correct frequency
	m_beeper->add_route(ALL_OUTPUTS, "mono", 0.50);

	TIMER(config, "keyboard_timer").configure_periodic(FUNC(smc777_state::keyboard_callback), attotime::from_hz(240/32));
}

ROM_START( smc777 )
	/* shadow ROM */
	ROM_REGION( 0x10000, "ipl", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "1st", "1st rev.")
	ROMX_LOAD( "smcrom.dat", 0x0000, 0x4000, CRC(b2520d31) SHA1(3c24b742c38bbaac85c0409652ba36e20f4687a1), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "2nd", "2nd rev.")
	ROMX_LOAD( "smcrom.v2",  0x0000, 0x4000, CRC(c1494b8f) SHA1(a7396f5c292f11639ffbf0b909e8473c5aa63518), ROM_BIOS(1))

	ROM_REGION( 0x400, "mcu", ROMREGION_ERASEFF )
	ROM_LOAD( "m5l8041a-077p.bin", 0x000, 0x400, NO_DUMP ) // 8041 keyboard mcu, needs decapping
ROM_END

} // anonymous namespace


COMP( 1983, smc777, 0,      0,      smc777,  smc777, smc777_state, empty_init, "Sony",  "SMC-777", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND)



smondial.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco, hap
// thanks-to:yoyo_chessboard
/*******************************************************************************

Mephisto Super Mondial
Mephisto Super Mondial II
Mephisto Mega IV

Hardware notes:
- G65SC02P-4 @ 4MHz
- 8KB RAM(battery-backed), 32KB ROM
- expansion slot at underside (only used for smondial2)
- 2*PCF2112, 2 7seg LCD screens
- 8*8 chessboard buttons, 24 tri-color leds, piezo

Undocumented buttons:
- smondialb: holding CL+INFO on boot runs diagnostics

*******************************************************************************/

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/m6502/g65sc02.h"
#include "cpu/m6502/r65c02.h"
#include "machine/74259.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pcf2100.h"
#include "video/pwm.h"

#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "mephisto_mega4.lh"
#include "mephisto_smondial.lh"
#include "mephisto_smondial2.lh"


namespace {

// Super Mondial B / shared

class smondialb_state : public driver_device
{
public:
	smondialb_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_lcd_latch(*this, "lcd_latch"),
		m_led_pwm(*this, "led_pwm"),
		m_lcd(*this, "lcd%u", 0),
		m_dac(*this, "dac"),
		m_keys(*this, "KEY.%u", 0),
		m_digits(*this, "digit%u", 0U)
	{ }

	// machine configs
	void smondial2(machine_config &config);
	void mega4(machine_config &config);
	void smondialb(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<hc259_device> m_lcd_latch;
	required_device<pwm_display_device> m_led_pwm;
	required_device_array<pcf2112_device, 2> m_lcd;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<4> m_keys;
	output_finder<8> m_digits;

	u8 m_led_data = 0;
	u8 m_board_mux = 0;

	// address maps
	void smondialb_mem(address_map &map) ATTR_COLD;
	void smondial2_mem(address_map &map) ATTR_COLD;

	// I/O handlers
	template<int N> void lcd_output_w(u32 data);
	void update_leds();
	u8 input_r(offs_t offset);
	virtual void led_w(u8 data);
	void board_w(u8 data);
	INTERRUPT_GEN_MEMBER(nmi_handler);
};

void smondialb_state::machine_start()
{
	m_digits.resolve();

	save_item(NAME(m_led_data));
	save_item(NAME(m_board_mux));
}


// Super Mondial A

class smondiala_state : public smondialb_state
{
public:
	smondiala_state(const machine_config &mconfig, device_type type, const char *tag) :
		smondialb_state(mconfig, type, tag)
	{ }

	void smondiala(machine_config &config);

private:
	void smondiala_mem(address_map &map) ATTR_COLD;

	// led row/column is switched around, do a trampoline here instead of making a different .lay file
	virtual void led_w(u8 data) override { smondialb_state::led_w(bitswap<8>(data, 7,6,3,2,5,4,1,0)); }

	void irq_ack_w(u8 data);
};



/*******************************************************************************
    I/O
*******************************************************************************/

template<int N>
void smondialb_state::lcd_output_w(u32 data)
{
	// lcd segment outputs
	for (int i = 0; i < 4; i++)
		m_digits[i + N*4] = bitswap<8>(data >> (8 * i), 7,4,5,0,1,2,3,6);
}

void smondialb_state::update_leds()
{
	m_led_pwm->matrix(m_board_mux, m_led_data);
}

void smondialb_state::board_w(u8 data)
{
	m_board_mux = ~data;
	update_leds();
}

void smondialb_state::led_w(u8 data)
{
	// d0-d5: led data
	m_led_data = data;
	update_leds();

	// d6: nmi enable (does not apply to smondiala)
	// d7: speaker out
	m_dac->write(BIT(data, 7));
}

u8 smondialb_state::input_r(offs_t offset)
{
	u8 data = 0;

	// read keypad
	for (int i = 0; i < 4; i++)
		if (!BIT(m_lcd_latch->output_state(), i))
			data |= BIT(m_keys[i]->read(), offset & 3);

	// read chessboard sensors
	for (int i = 0; i < 8; i++)
		if (BIT(m_board_mux, i))
			data |= BIT(m_board->read_rank(i), offset);

	return ~(data << 7);
}

INTERRUPT_GEN_MEMBER(smondialb_state::nmi_handler)
{
	if (m_led_data & 0x40)
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}

void smondiala_state::irq_ack_w(u8 data)
{
	m_maincpu->set_input_line(0, CLEAR_LINE);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void smondialb_state::smondialb_mem(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("nvram");
	map(0x2400, 0x2400).w(FUNC(smondialb_state::led_w));
	map(0x2800, 0x2800).w(FUNC(smondialb_state::board_w));
	map(0x2c00, 0x2c07).w(m_lcd_latch, FUNC(hc259_device::write_d7)).nopr();
	map(0x3000, 0x3007).r(FUNC(smondialb_state::input_r));
	map(0x8000, 0xffff).rom();
}

void smondialb_state::smondial2_mem(address_map &map)
{
	smondialb_mem(map);
	map(0x4000, 0x7fff).r("cartslot", FUNC(generic_slot_device::read_rom));
}

void smondiala_state::smondiala_mem(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("nvram");
	map(0x4000, 0x4007).r(FUNC(smondiala_state::input_r));
	map(0x6000, 0x6000).w(FUNC(smondiala_state::irq_ack_w));
	map(0x6400, 0x6407).w("led_latch", FUNC(hc259_device::write_d7)).nopr();
	map(0x6800, 0x6807).w("board_latch", FUNC(hc259_device::write_d7)).nopr();
	map(0x6c00, 0x6c07).w(m_lcd_latch, FUNC(hc259_device::write_d7)).nopr();
	map(0x8000, 0xffff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( smondial )
	PORT_START("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Pawn / 1")   PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Queen / 5")  PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("BOOK / 9")   PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_B)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("CL")         PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE)

	PORT_START("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Knight / 2") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("King / 6")   PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("POS / 0")    PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_P)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("LEV")        PORT_CODE(KEYCODE_L)

	PORT_START("KEY.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Bishop / 3") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Black / 7")  PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("MEM")        PORT_CODE(KEYCODE_M)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("ENT")        PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)

	PORT_START("KEY.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("Rook / 4")   PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("White / 8")  PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("INFO")       PORT_CODE(KEYCODE_I)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("RES")        PORT_CODE(KEYCODE_F1)
INPUT_PORTS_END

static INPUT_PORTS_START( smondial2 )
	PORT_INCLUDE( smondial )

	PORT_MODIFY("KEY.0")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("HELP / 9")   PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_H)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("LEV")        PORT_CODE(KEYCODE_L)

	PORT_MODIFY("KEY.1")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("INFO / 0")   PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_I)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("CL")         PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE)

	PORT_MODIFY("KEY.2")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("POS")        PORT_CODE(KEYCODE_P)

	PORT_MODIFY("KEY.3")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD)    PORT_NAME("MEM")        PORT_CODE(KEYCODE_M)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void smondialb_state::smondialb(machine_config &config)
{
	// basic machine hardware
	G65SC02(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &smondialb_state::smondialb_mem);

	const attotime nmi_period = attotime::from_hz(4_MHz_XTAL / 0x2000);
	m_maincpu->set_periodic_int(FUNC(smondialb_state::nmi_handler), nmi_period);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	HC259(config, m_lcd_latch);
	m_lcd_latch->q_out_cb<4>().set(m_lcd[0], FUNC(pcf2112_device::data_w));
	m_lcd_latch->q_out_cb<4>().append(m_lcd[1], FUNC(pcf2112_device::data_w));
	m_lcd_latch->q_out_cb<5>().set(m_lcd[1], FUNC(pcf2112_device::dlen_w));
	m_lcd_latch->q_out_cb<6>().set(m_lcd[0], FUNC(pcf2112_device::clb_w));
	m_lcd_latch->q_out_cb<6>().append(m_lcd[1], FUNC(pcf2112_device::clb_w));
	m_lcd_latch->q_out_cb<7>().set(m_lcd[0], FUNC(pcf2112_device::dlen_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(250));

	// video hardware
	PCF2112(config, m_lcd[0], 50); // frequency guessed
	m_lcd[0]->write_segs().set(FUNC(smondialb_state::lcd_output_w<0>));
	PCF2112(config, m_lcd[1], 50); // "
	m_lcd[1]->write_segs().set(FUNC(smondialb_state::lcd_output_w<1>));

	PWM_DISPLAY(config, m_led_pwm).set_size(8, 6);
	config.set_default_layout(layout_mephisto_smondial);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void smondiala_state::smondiala(machine_config &config)
{
	smondialb(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &smondiala_state::smondiala_mem);

	const attotime irq_period = attotime::from_hz(4_MHz_XTAL / 0x2000);
	m_maincpu->set_periodic_int(FUNC(smondiala_state::irq0_line_assert), irq_period);

	HC259(config, "led_latch").parallel_out_cb().set(FUNC(smondiala_state::led_w)).invert();
	HC259(config, "board_latch").parallel_out_cb().set(FUNC(smondiala_state::board_w)).invert();
}

void smondialb_state::smondial2(machine_config &config)
{
	smondialb(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &smondialb_state::smondial2_mem);

	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "smondial2_cart");
	SOFTWARE_LIST(config, "cart_list").set_original("mephisto_smondial2");

	config.set_default_layout(layout_mephisto_smondial2);
}

void smondialb_state::mega4(machine_config &config)
{
	smondialb(config);

	// basic machine hardware
	R65C02(config.replace(), m_maincpu, 4.9152_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &smondialb_state::smondialb_mem);

	const attotime nmi_period = attotime::from_hz(4.9152_MHz_XTAL / 0x2000);
	m_maincpu->set_periodic_int(FUNC(smondialb_state::nmi_handler), nmi_period);

	config.set_default_layout(layout_mephisto_mega4);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( smondial )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("supermondial_a.bin", 0x8000, 0x8000, CRC(c1d7d0a5) SHA1(d7f0da6938458c06925f0936e63915319144d7e0) )
ROM_END

ROM_START( smondialab )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("supermondial_ab.bin", 0x8000, 0x8000, CRC(a8781685) SHA1(fd4c97e13bd398dc4c85e3e1778bf7e59fccd71e) )
ROM_END

ROM_START( smondialb )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("supermondial_b.bin", 0x8000, 0x8000, CRC(6fb89e97) SHA1(b001e657b4fdc097322b28a25c31814f3da7b124) )
ROM_END


ROM_START( smondial2 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("supermondial_ii.bin", 0x8000, 0x8000, CRC(cd73df4a) SHA1(bad786074be613d7f48bf98b6fdf8178a4a85f5b) )
ROM_END


ROM_START( mega4 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("mega_iv.bin", 0x8000, 0x8000, CRC(dee355d2) SHA1(6bc79c0fb169020f017412f5f9696b9ecafbf99f) )
ROM_END

ROM_START( mega4a )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("mega_iv_17.03.88", 0x8000, 0x8000, CRC(85267e82) SHA1(654c9cf84bf2165fc94f8c4cf9c662786ef3283b) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME        PARENT    COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY, FULLNAME, FLAGS
SYST( 1986, smondial,   0,        0,      smondiala, smondial,  smondiala_state, empty_init, "Hegener + Glaser", "Mephisto Super Mondial (ver. A)",  MACHINE_SUPPORTS_SAVE )
SYST( 1986, smondialab, smondial, 0,      smondiala, smondial,  smondiala_state, empty_init, "Hegener + Glaser", "Mephisto Super Mondial (ver. AB)", MACHINE_SUPPORTS_SAVE )
SYST( 1986, smondialb,  smondial, 0,      smondialb, smondial,  smondialb_state, empty_init, "Hegener + Glaser", "Mephisto Super Mondial (ver. B)",  MACHINE_SUPPORTS_SAVE )

SYST( 1988, smondial2,  0,        0,      smondial2, smondial2, smondialb_state, empty_init, "Hegener + Glaser", "Mephisto Super Mondial II", MACHINE_SUPPORTS_SAVE )

SYST( 1988, mega4,      0,        0,      mega4,     smondial,  smondialb_state, empty_init, "Hegener + Glaser", "Mephisto Mega IV (set 1)",  MACHINE_SUPPORTS_SAVE )
SYST( 1988, mega4a,     mega4,    0,      mega4,     smondial,  smondialb_state, empty_init, "Hegener + Glaser", "Mephisto Mega IV (set 2)",  MACHINE_SUPPORTS_SAVE )



sms.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol, Charles MacDonald,Mathis Rosenhauer,Brad Oliver,Michael Luong,Fabio Priuli,Enik Land
/******************************************************************************
 Contributors:

    Marat Fayzullin (MG source)
    Charles MacDonald
    Mathis Rosenhauer
    Brad Oliver
    Michael Luong
    Wilbert Pol
    Fabio Priuli
    Enik Land

 To do:

 - SIO interface for Game Gear (needs netplay, I guess)
 - Support for other DE-9 compatible controllers, like the Mega Drive 6-Button
   that has homebrew software support
 - On sms1kr (without FM), verify if Rapid uses C-Sync and PSG can be muted like on smsj
 - Accurate GG vertical scaling in SMS mode (h-scaling seems right for subpixel rendering)
 - Software compatibility flags, by region and/or BIOS
 - Samsung modem for Gam*Boy Securities Information Service System
 - Sega Demo Unit II (SMS kiosk-like expansion device)
 - SMS 8 slot game changer (kiosk-like expansion device)
 - Sega Floppy Disc Unit (SMS expansion device) - unreleased
 - Emulate SRAM cartridges? (for use with Bock's dump tool)

 The Game Gear SIO hardware is not emulated but has some
 placeholders in 'machine/sms.c'

 Changes:
    Apr 02 - Added raster palette effects for SMS & GG (ML)
                 - Added sprite collision (ML)
                 - Added zoomed sprites (ML)
    May 02 - Fixed paging bug (ML)
                 - Fixed sprite and tile priority bug (ML)
                 - Fixed bug #66 (ML)
                 - Fixed bug #78 (ML)
                 - try to implement LCD persistence emulation for GG (ML)
    Jun 10, 02 - Added bios emulation (ML)
    Jun 12, 02 - Added PAL & NTSC systems (ML)
    Jun 25, 02 - Added border emulation (ML)
    Jun 27, 02 - Version bits for Game Gear (bits 6 of port 00) (ML)
    Nov-Dec, 05 - Numerous cleanups, fixes, updates (WP)
    Mar, 07 - More cleanups, fixes, mapper additions, etc (WP)

--------------------------------------------------------------------------------

General compatibility issues on real hardware (not emulation bugs):

- Some ROMs have issues or don't work when running on a console of different
  region;
- Many Japanese, Korean and homebrew ROMs don't have the signature required by
  BIOSes of consoles sold overseas;
- Few games of the ones with FM support need to detect the system region as
  Japanese to play FM sound;
- The Korean SMS versions have Japanese-format cartridge slot, but only on the
  first (Gam*Boy I) the region is detected as Japanese;
- Some SMS ROMs don't run when are plugged-in to SMS expansion slot, through
  the gender adapter;
- Some SMS ROMs don't run or have issues when are plugged-in to a Game Gear,
  through the Master Gear adapter;

--------------------------------------------------------------------------------

Sega Master System II
Sega 1990

This particular version was manufactured between July 1991 and October 1992
The PCB is stamped '17 AUG 1991'

PCB Layout
----------
171-5922A
(C) SEGA 1990
IC BD M4Jr. PAL
|----------|------|-----------------------|
| POWER_IN |RF_OUT|       IC1  IC2   IC3  |
|          |      |       Z80  BIOS  6264 |
|          |      | IC9                   |
|          |------| CXA1145               |
|                                         |
|                                         |
|IC8                                      |
|7805                                     |
|                   |---------------|     |
|                   |   CART SLOT   |     |
|                   |---------------|     |
|                                         |
|                                         |
| PAUSE_SW                                |
|                                         |
|      IC6       IC5            IC4       |
|      D4168     315-5246       315-5237  |
|                                         |
|      IC7                                |
|      D4168                              |
|                                         |
| POWER_SW           JOY1 JOY2  53.2034MHz|
|-----------------------------------------|
Notes: (All ICs shown)
       IC1 Z80      - Z0840004PSC Z80 CPU (DIP40). Clock input 3.54689MHz (53.2034/15)
       IC2 BIOS     - 1M 28-pin mask ROM marked MPR-12808 (DIP28)
       IC3 6264     - Samsung KM6264 8k x8 SRAM. Some models have NEC D4168 or Sanyo LC3664 which are compatible (DIP28)
       IC4 315-5237 - Custom Sega I/O controller IC (DIP48)
                      Clocks - Pin 43 - master clock input 53.2034MHz from OSC
                               Pin 41 - 10.6406MHz (53.2034/5)
                               Pin 42 - 4.43361MHz (53.2034/12)
       IC5 315-5246 - Custom Sega Video Display Processor (VDP) (64 pin flat pack)
                      The VDP also contains a Texas Instruments SN76489 sound chip
                      Clocks - Pin 33 - 3.54689MHz (53.2034/15)
                               Pin 34 - 10.6406MHz (53.2034/5)
                               Pin 35 - 10.6406MHz (53.2034/5)
                               Pin 39 - 2.66017MHz (53.2034/20)
       IC6/IC7 D4168- NEC D4168 8k x8 SRAM (DIP28)
       IC8 7805     - Motorola MC7805 voltage regulator (7v to 25v input, 5v output)
       IC9 CXA1145  - Sony CXA1145 RGB to composite video encoder IC (DIP24)
       POWER_IN     - Power input from AC/DC power pack. System requires 9VDC at 500mA. Center pin is negative and
                      outer barrel is positive. Note this is opposite to regular DC power packs
       RF_OUT       - RF modulator with RF signal output to TV
       POWER_SW     - Power on/off switch
       PAUSE_SW     - Push button used to pause the game
       JOY1/JOY2    - Joystick connectors (DB9)
       HSync        - 15.5565kHz
       VSync        - 49.7015Hz


Cart PCB Examples
-----------------
Note! There are many more types of PCBs & custom chip matching variations. This document
is not meant to provide all details of every type in existence. Some games have been
found on different types of ROM boards with and without bankswitching hardware.

Type with no bankswitching hardware

171-5519 \ no visible difference?
171-5519D/
|------------------|
||----------|      |
||  DIP28   |      |
||----------|      |
|IC1               |
|------------------|
Notes:
      DIP28 - 1M mask ROM (DIP28) at location IC1. Actual ROM type is Fujitsu MB831001
              This ROM has a built-in mapper so no bankswitching chip is required.
              The CPU uses pin 22 of the ROM to bankswitch it.
              Found in....
                          Game Name         Sega ROM ID
                          -----------------------------
                          World Grand Prix  MPR-11074
                          Black Belt        MPR-10150
                          Ghost House       MPR-12586


Types with bankswitching hardware

171-5713D (uses 315-5235)
171-5838 (uses 315-5365)
171-5893 (uses 315-5365)
|------------------|
|                  |
|  |------------|  |
|  |   SDIP42   |  |
|  |------------|  |
|               IC1|
||----------|      |
||  DIP32   |      |
||----------|      |
|IC2               |
|------------------|
Notes:
     SDIP42 - Custom Sega bankswitch chip at location IC1. There are several different
              types of these chips with different 315-xxxx numbers
              These include 315-5235 (DIP42), 315-5208 (DIP28) and 315-5365 (DIP42) and possibly others.
      DIP32 - 1MBit/2MBit/4MBit mask ROM (DIP32) at location IC2
              Actual ROM type can be 831000, 831001, 832011, 834000, 834011
              Found in....
                          Game Name         Sega ROM ID   Bank Chip
                          -----------------------------------------
                          Spellcaster       MPR-12532-T   315-5365
                          Altered Beast     MPR-12534     315-5235
                          Bubble Bobble     MPR-14177     315-5365

171-5442 (uses 315-5235)
|--------------------------|
|  |----------------|      |
|  |     DIP40      |      |
|  |----------------|IC2   |
|                          |
|      |------------|      |
|      |   SDIP42   |      |
|      |------------|IC1   |
|----|                |----|
     |                |
     |----------------|
Notes:
      SDIP42 - Custom Sega bankswitch chip at location IC1. There are several different
               types of these chips with different 315-xxxx numbers
               These include 315-5235 (DIP42), 315-5208 (DIP28) and 315-5365 (DIP42) and possibly others.
       DIP40 - 2MBit/4Mbit 16-bit mask ROM (DIP40) at location IC2
               Found in....
                           Game Name         Sega ROM ID   Bank Chip
                           -----------------------------------------
                           Space Harrier     MPR-10410     315-5235

Another ROM board 171-5497 used by Monopoly has 315-5235 DIP42 mapper chip, DIP28 mask ROM, a DIP8 chip (unknown),
DIP28 SRAM (likely 8k) and a 3V coin battery.
Yet another type of ROM board with unknown PCB number used by Phantasy Star has 315-5235 DIP42 mapper chip, DIP32 mask
ROM and DIP28 SRAM (likely 8k) and a 3V coin battery.
Unfortunatley the majority of these ROM boards, ROM types and MPR-xxxxx Sega part numbers are undocumented because they
were mostly dumped from the edge connector without being opened.
Some additional info can be found at http://www.smspower.org/Development/Index
Some excellent SMS documentation can be found at http://cgfm2.emuviews.com/sms.php

--------------------------------------------------------------------------------
SMS Store Unit memory map for the second CPU:

0000-3FFF - BIOS
4000-47FF - RAM
8000      - System Control Register (R/W)
            Reading:
            bit7      - ready (0 = ready, 1 = not ready)
            bit6      - active timer bit switch (0 = timer 2, 1 = timer 1)
            bit5      - unknown
            bit4-bit3 - timer 2 length bit switches (10s-25s)
            bit2-bit0 - timer 1 length bit switches (30s-135s)
            Writing:
            bit7-bit4 - led of selected game to set
            bit3      - unknown, 1 seems to be written all the time
            bit2      - unknown, 1 seems to be written all the time
            bit1      - reset signal for sms cpu, 0 = reset low, 1 = reset high
            bit0      - which cpu receives interrupt signals, 0 = sms cpu, 1 = controlling cpu
C000      - Card/Cartridge selction register (W)
            bit7-bit4 - slot to select
            bit3      - slot type (0 = cartridge, 1 = card ?)
            bit2-bit0 - unknown
C400      - ???? (used once)
D800      - Selection buttons #1, 1-8 (R)
DC00      - Selection buttons #2, 9-16 (R)

 ******************************************************************************/

#include "emu.h"
#include "sms.h"

#include "bus/sms_ctrl/controllers.h"
#include "cpu/z80/z80.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "sms1.lh"


#define MASTER_CLOCK_GG     32215905.0
#define MASTER_CLOCK_PALN   10746168.0
// The clocks for PAL and PAL-M used here differ from their nominal values,
// because with the latter errors will occur on Flubba's VDPTest, probably
// due to rounding issues in the core.
// Nominal XTAL value for SMS PAL-M (Brazil) is 10726834.
#define MASTER_CLOCK_PALM   10726833.0
// Nominal XTAL value for SMS PAL is 53203424.
#define MASTER_CLOCK_PAL    53203425.0  /* 12 * subcarrier freq. (4.43361875MHz) */


void sms1_state::sms1_mem(address_map &map)
{
	map(0x0000, 0xbfff).w(FUNC(sms1_state::write_cart));
	map(0x0000, 0x3fff).r(FUNC(sms1_state::read_0000));
	map(0x4000, 0x7fff).r(FUNC(sms1_state::read_4000));
	map(0x8000, 0xbfff).r(FUNC(sms1_state::read_8000));
	map(0xc000, 0xfff7).rw(FUNC(sms1_state::read_ram), FUNC(sms1_state::write_ram));
	map(0xfff8, 0xfffb).rw(FUNC(sms1_state::sscope_r), FUNC(sms1_state::sscope_w));             /* 3-D glasses */
	map(0xfffc, 0xffff).rw(FUNC(sms1_state::sms_mapper_r), FUNC(sms1_state::sms_mapper_w));     /* Bankswitch control */
}

void sms_state::sms_mem(address_map &map)
{
	map(0x0000, 0xbfff).w(FUNC(sms_state::write_cart));
	map(0x0000, 0x3fff).r(FUNC(sms_state::read_0000));
	map(0x4000, 0x7fff).r(FUNC(sms_state::read_4000));
	map(0x8000, 0xbfff).r(FUNC(sms_state::read_8000));
	map(0xc000, 0xfffb).rw(FUNC(sms_state::read_ram), FUNC(sms_state::write_ram));
	map(0xfffc, 0xffff).rw(FUNC(sms_state::sms_mapper_r), FUNC(sms_state::sms_mapper_w));       /* Bankswitch control */
}

void smssdisp_state::sms_store_mem(address_map &map)
{
	map(0x0000, 0x3fff).rom();                     /* BIOS */
	map(0x4000, 0x47ff).ram();                     /* RAM */
	map(0x6000, 0x7fff).r(FUNC(smssdisp_state::store_cart_peek));
	map(0x8000, 0x8000).portr("DSW").w(FUNC(smssdisp_state::sms_store_control_w)); /* Control */
	map(0xc000, 0xc000).rw(FUNC(smssdisp_state::sms_store_cart_select_r), FUNC(smssdisp_state::sms_store_cart_select_w)); /* cartridge/card slot selector */
	map(0xd800, 0xd800).portr("GAMESEL1");         /* Game selector port #1 */
	map(0xdc00, 0xdc00).portr("GAMESEL2");         /* Game selector port #2 */
}

// I/O ports $3E and $3F do not exist on Mark III
void sg1000m3_state::sg1000m3_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();

	map(0x40, 0x7f).r(FUNC(sg1000m3_state::sms_count_r)).w(m_vdp, FUNC(sega315_5124_device::psg_w));
	map(0x80, 0x80).mirror(0x3e).rw(m_vdp, FUNC(sega315_5124_device::data_read), FUNC(sega315_5124_device::data_write));
	map(0x81, 0x81).mirror(0x3e).rw(m_vdp, FUNC(sega315_5124_device::control_read), FUNC(sega315_5124_device::control_write));
	map(0xc0, 0xc7).mirror(0x38).rw(FUNC(sg1000m3_state::sg1000m3_peripheral_r), FUNC(sg1000m3_state::sg1000m3_peripheral_w));
}

void sms_state::sms_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();

	map(0x00, 0x00).mirror(0x3e).w(FUNC(sms_state::sms_mem_control_w));
	map(0x01, 0x01).mirror(0x3e).w(FUNC(sms_state::sms_io_control_w));
	map(0x40, 0x7f).r(FUNC(sms_state::sms_count_r)).w(m_vdp, FUNC(sega315_5124_device::psg_w));
	map(0x80, 0x80).mirror(0x3e).rw(m_vdp, FUNC(sega315_5124_device::data_read), FUNC(sega315_5124_device::data_write));
	map(0x81, 0x81).mirror(0x3e).rw(m_vdp, FUNC(sega315_5124_device::control_read), FUNC(sega315_5124_device::control_write));
	map(0xc0, 0xc0).mirror(0x3e).r(FUNC(sms_state::sms_input_port_dc_r));
	map(0xc1, 0xc1).mirror(0x3e).r(FUNC(sms_state::sms_input_port_dd_r));
}


// It seems the Korean versions do some more strict decoding on the I/O addresses.
// At least the mirrors for I/O ports $3E/$3F don't seem to exist there.
// Leaving the mirrors breaks the Korean cartridge bublboky.
void sms_state::smskr_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();

	map(0x3e, 0x3e).w(FUNC(sms_state::sms_mem_control_w));
	map(0x3f, 0x3f).w(FUNC(sms_state::sms_io_control_w));
	map(0x40, 0x7f).r(FUNC(sms_state::sms_count_r)).w(m_vdp, FUNC(sega315_5124_device::psg_w));
	map(0x80, 0x80).mirror(0x3e).rw(m_vdp, FUNC(sega315_5124_device::data_read), FUNC(sega315_5124_device::data_write));
	map(0x81, 0x81).mirror(0x3e).rw(m_vdp, FUNC(sega315_5124_device::control_read), FUNC(sega315_5124_device::control_write));
	map(0xc0, 0xc0).mirror(0x3e).r(FUNC(sms_state::sms_input_port_dc_r));
	map(0xc1, 0xc1).mirror(0x3e).r(FUNC(sms_state::sms_input_port_dd_r));
}


// Mirrors for I/O ports $3E/$3F don't exist on the Japanese SMS.
// Also, $C0/$C1 are the only mirrors for I/O ports $DC/$DD.
void sms_state::smsj_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();

	map(0x3e, 0x3e).w(FUNC(sms_state::sms_mem_control_w));
	map(0x3f, 0x3f).w(FUNC(sms_state::sms_io_control_w));
	map(0x40, 0x7f).r(FUNC(sms_state::sms_count_r)).w(m_vdp, FUNC(sega315_5124_device::psg_w));
	map(0x80, 0x80).mirror(0x3e).rw(m_vdp, FUNC(sega315_5124_device::data_read), FUNC(sega315_5124_device::data_write));
	map(0x81, 0x81).mirror(0x3e).rw(m_vdp, FUNC(sega315_5124_device::control_read), FUNC(sega315_5124_device::control_write));
	map(0xc0, 0xc0).r(FUNC(sms_state::sms_input_port_dc_r));
	map(0xc1, 0xc1).r(FUNC(sms_state::sms_input_port_dd_r));
	map(0xdc, 0xdc).r(FUNC(sms_state::sms_input_port_dc_r));
	map(0xdd, 0xdd).r(FUNC(sms_state::sms_input_port_dd_r));
	map(0xf0, 0xf0).w(FUNC(sms_state::smsj_ym2413_register_port_w));
	map(0xf1, 0xf1).w(FUNC(sms_state::smsj_ym2413_data_port_w));
	map(0xf2, 0xf2).rw(FUNC(sms_state::smsj_audio_control_r), FUNC(sms_state::smsj_audio_control_w));
}


// It seems the mirrors for I/O ports $3E/$3F also don't seem to exist on the
// Game Gear. Leaving the mirrors breaks 'gloc' (it freezes after 1st stage).
void gamegear_state::gg_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();

	map(0x3e, 0x3e).w(FUNC(gamegear_state::sms_mem_control_w)); // TODO: only really exists in Master System mode
	map(0x40, 0x7f).r(FUNC(gamegear_state::sms_count_r)).w(m_vdp, FUNC(sega315_5124_device::psg_w));
	map(0x80, 0x80).mirror(0x3e).rw(m_vdp, FUNC(sega315_5124_device::data_read), FUNC(sega315_5124_device::data_write));
	map(0x81, 0x81).mirror(0x3e).rw(m_vdp, FUNC(sega315_5124_device::control_read), FUNC(sega315_5124_device::control_write));
	map(0xc0, 0xc0).r(FUNC(gamegear_state::gg_input_port_dc_r));
	map(0xc1, 0xc1).r(FUNC(gamegear_state::gg_input_port_dd_r));
	map(0xdc, 0xdc).r(FUNC(gamegear_state::gg_input_port_dc_r));
	map(0xdd, 0xdd).r(FUNC(gamegear_state::gg_input_port_dd_r));

	map(0x00, 0x3f).view(m_io_view);

	// Game Gear mode
	m_io_view[0](0x00, 0x00).r(FUNC(gamegear_state::gg_input_port_00_r));
	m_io_view[0](0x01, 0x01).rw(m_gg_ioport, FUNC(gamegear_io_port_device::data_r), FUNC(gamegear_io_port_device::data_w));
	m_io_view[0](0x02, 0x02).rw(m_gg_ioport, FUNC(gamegear_io_port_device::ctrl_r), FUNC(gamegear_io_port_device::ctrl_w));
	m_io_view[0](0x03, 0x03).rw(m_gg_ioport, FUNC(gamegear_io_port_device::txdata_r), FUNC(gamegear_io_port_device::txdata_w));
	m_io_view[0](0x04, 0x04).r(m_gg_ioport, FUNC(gamegear_io_port_device::rxdata_r));
	m_io_view[0](0x05, 0x05).rw(m_gg_ioport, FUNC(gamegear_io_port_device::s_ctrl_r), FUNC(gamegear_io_port_device::s_ctrl_w));
	m_io_view[0](0x06, 0x06).w(m_vdp, FUNC(sega315_5124_device::psg_stereo_w));

	// Master System mode
	m_io_view[1](0x3f, 0x3f).w(FUNC(gamegear_state::gg_io_control_w));
}


static INPUT_PORTS_START( sms )
	PORT_START("PAUSE")
	PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME(DEF_STR(Pause)) PORT_CODE(KEYCODE_1) PORT_WRITE_LINE_DEVICE_MEMBER("sms_vdp", FUNC(sega315_5124_device::n_nmi_in_write))
INPUT_PORTS_END

static INPUT_PORTS_START( sg1000m3 )
	PORT_INCLUDE( sms )

	PORT_START("SEGASCOPE")
	PORT_CONFNAME( 0x01, 0x00, "SegaScope (3-D Glasses)" )
	PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
	PORT_CONFSETTING( 0x01, DEF_STR( On ) )

	PORT_START("SSCOPE_BINOCULAR")
	PORT_CONFNAME( 0x03, 0x00, "SegaScope - Binocular Hack" ) PORT_CONDITION("SEGASCOPE", 0x01, EQUALS, 0x01)
	PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
	PORT_CONFSETTING( 0x01, "Left Lens" )
	PORT_CONFSETTING( 0x02, "Right Lens" )
	PORT_CONFSETTING( 0x03, "Both Lens" )
	PORT_BIT( 0x03, 0x00, IPT_UNUSED ) PORT_CONDITION("SEGASCOPE", 0x01, EQUALS, 0x00)
INPUT_PORTS_END

static INPUT_PORTS_START( sms1 )
	PORT_INCLUDE( sg1000m3 )

	PORT_START("RESET")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_SERVICE2 ) PORT_NAME("Reset Button")
INPUT_PORTS_END

static INPUT_PORTS_START( smsj )
	PORT_INCLUDE( sg1000m3 )

	PORT_START("RAPID")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_SERVICE2 ) PORT_NAME("Rapid Button")
INPUT_PORTS_END

static INPUT_PORTS_START( smssdisp )
	// For each peripheral port (for controllers or 3-D glasses), there are sets
	// of two connectors wired in parallel on the real hardware. This allows to
	// have different controllers, like a pad and a Light Phaser, plugged together
	// for a player input, what avoids having to re-plug them every time a game is
	// changed to another that requires a different controller. Also, this allows
	// 3-D games to be properly watched by two persons at same time.
	// For now the driver just uses single input ports.
	PORT_INCLUDE( sms1 )

	PORT_START("DSW")
	PORT_DIPNAME( 0x07, 0x07, "Timer 1 length" )
	PORT_DIPSETTING( 0x00, "135s" )
	PORT_DIPSETTING( 0x01, "120s" )
	PORT_DIPSETTING( 0x02, "105s" )
	PORT_DIPSETTING( 0x03, "90s" )
	PORT_DIPSETTING( 0x04, "75s" )
	PORT_DIPSETTING( 0x05, "60s" )
	PORT_DIPSETTING( 0x06, "45s" )
	PORT_DIPSETTING( 0x07, "30s" )
	PORT_DIPNAME( 0x18, 0x18, "Timer 2 length" )
	PORT_DIPSETTING( 0x00, "25s" )
	PORT_DIPSETTING( 0x08, "20s" )
	PORT_DIPSETTING( 0x10, "15s" )
	PORT_DIPSETTING( 0x18, "10s" )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING( 0x00, DEF_STR( On ) )
	PORT_DIPSETTING( 0x20, DEF_STR( Off ) )
	PORT_DIPNAME( 0x40, 0x40, "Select Timer" )
	PORT_DIPSETTING( 0x00, "Timer 2" )
	PORT_DIPSETTING( 0x40, "Timer 1" )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNKNOWN )   // READY, must be high

	PORT_START("GAMESEL1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Game 03") PORT_CODE(KEYCODE_N)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Game 02") PORT_CODE(KEYCODE_H)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Game 01") PORT_CODE(KEYCODE_Y)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Game 00") PORT_CODE(KEYCODE_6)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Game 07") PORT_CODE(KEYCODE_M)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Game 06") PORT_CODE(KEYCODE_J)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Game 05") PORT_CODE(KEYCODE_U)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Game 04") PORT_CODE(KEYCODE_7)

	PORT_START("GAMESEL2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Game 11") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Game 10") PORT_CODE(KEYCODE_K)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Game 09") PORT_CODE(KEYCODE_I)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Game 08") PORT_CODE(KEYCODE_8)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Game 15") PORT_CODE(KEYCODE_STOP)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Game 14") PORT_CODE(KEYCODE_L)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Game 13") PORT_CODE(KEYCODE_O)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Game 12") PORT_CODE(KEYCODE_9)
INPUT_PORTS_END

static INPUT_PORTS_START( gg )
	PORT_START("GG_PORT_DC")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_8WAY
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("START")
	PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START ) PORT_NAME("Start")

	PORT_START("PERSISTENCE")
	PORT_CONFNAME( 0x01, 0x01, "LCD Persistence Hack" )
	PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
	PORT_CONFSETTING( 0x01, DEF_STR( On ) )
INPUT_PORTS_END


void sms_state::sms_base(machine_config &config)
{
	SPEAKER(config, "mono").front_center();

	SMS_CART_SLOT(config, "slot", sms_cart, nullptr);

	SOFTWARE_LIST(config, "cart_list").set_original("sms");

	SMS_CONTROL_PORT(config, m_port_ctrl1, sms_control_port_devices, SMS_CTRL_OPTION_JOYPAD);
	m_port_ctrl1->set_screen(m_main_scr);
	m_port_ctrl1->th_handler().set(FUNC(sms_state::sms_ctrl1_th_input));

	SMS_CONTROL_PORT(config, m_port_ctrl2, sms_control_port_devices, SMS_CTRL_OPTION_JOYPAD);
	m_port_ctrl2->set_screen(m_main_scr);
	m_port_ctrl2->th_handler().set(FUNC(sms_state::sms_ctrl2_th_input));
}

void sms_state::sms_ntsc_base(machine_config &config)
{
	sms_base(config);

	Z80(config, m_maincpu, XTAL(10'738'635)/3);
	m_maincpu->set_addrmap(AS_PROGRAM, &sms_state::sms_mem);
	m_maincpu->set_addrmap(AS_IO, &sms_state::sms_io);

	config.set_maximum_quantum(attotime::from_hz(60));
}

/*
    For SMS drivers, the ratio between CPU and pixel clocks, set through dividers, is 2/3. The
    division that sets the pixel clock, in screen.set_raw(), results in a remainder
    that is discarded internally. Due to this rounding, the cycle time and the screen pixel
    time, derived from their clocks, do not longer match (inversely) the exact original ratio
    of these clocks. The SMS VDP emulation controls some properties (counters/flags) through
    screen timing, that the core calculates based on the emulation time. The VDP properties
    are read in the CPU timeslice. When a CPU operation that access the VDP is executed, the
    elapsed emulation time is also based on how many CPU cycles have elapsed since start of
    the current timeslice. Depending on this amount of CPU cycles, when the core divides the
    elapsed time by the pixel time, the obtained pixel count may be less than expected. Flubba's
    VDPTest ROM relies on exact results. A workaround is to use an additional macro, for each
    driver, that resets the refresh rate, and by consequence the pixel time, without discarding
    the remainder of the division. If the core is fixed in the future, the screen.set_refresh_hz
    lines after each screen.set_raw call below can be removed.
*/

template <typename X>
void sms_state::screen_sms_pal_raw_params(screen_device &screen, X &&pixelclock)
{
	screen.set_raw(pixelclock,
		sega315_5124_device::WIDTH,
		sega315_5124_device::LBORDER_START + sega315_5124_device::LBORDER_WIDTH - 2,
		sega315_5124_device::LBORDER_START + sega315_5124_device::LBORDER_WIDTH + 256 + 10,
		sega315_5124_device::HEIGHT_PAL,
		sega315_5124_device::TBORDER_START + sega315_5124_device::PAL_240_TBORDER_HEIGHT,
		sega315_5124_device::TBORDER_START + sega315_5124_device::PAL_240_TBORDER_HEIGHT + 240);
	screen.set_refresh_hz(pixelclock / (sega315_5124_device::WIDTH * sega315_5124_device::HEIGHT_PAL));
}

template <typename X>
void sms_state::screen_sms_ntsc_raw_params(screen_device &screen, X &&pixelclock)
{
	screen.set_raw(pixelclock,
		sega315_5124_device::WIDTH,
		sega315_5124_device::LBORDER_START + sega315_5124_device::LBORDER_WIDTH - 2,
		sega315_5124_device::LBORDER_START + sega315_5124_device::LBORDER_WIDTH + 256 + 10,
		sega315_5124_device::HEIGHT_NTSC,
		sega315_5124_device::TBORDER_START + sega315_5124_device::NTSC_224_TBORDER_HEIGHT,
		sega315_5124_device::TBORDER_START + sega315_5124_device::NTSC_224_TBORDER_HEIGHT + 224);
	screen.set_refresh_hz(pixelclock / (sega315_5124_device::WIDTH * sega315_5124_device::HEIGHT_NTSC));
}

template <typename X>
void gamegear_state::screen_gg_raw_params(screen_device &screen, X &&pixelclock)
{
	screen.set_raw(pixelclock,
		sega315_5124_device::WIDTH,
		sega315_5124_device::LBORDER_START + sega315_5124_device::LBORDER_WIDTH + 6*8,
		sega315_5124_device::LBORDER_START + sega315_5124_device::LBORDER_WIDTH + 26*8,
		sega315_5124_device::HEIGHT_NTSC,
		sega315_5124_device::TBORDER_START + sega315_5124_device::NTSC_192_TBORDER_HEIGHT + 3*8,
		sega315_5124_device::TBORDER_START + sega315_5124_device::NTSC_192_TBORDER_HEIGHT + 21*8 );
	screen.set_refresh_hz(pixelclock / (sega315_5124_device::WIDTH * sega315_5124_device::HEIGHT_NTSC));
}


void sms_state::sms2_ntsc(machine_config &config)
{
	sms_ntsc_base(config);
	/* video hardware */
	SCREEN(config, m_main_scr, SCREEN_TYPE_RASTER);
	screen_sms_ntsc_raw_params(*m_main_scr, XTAL(10'738'635)/2);
	m_main_scr->set_screen_update(FUNC(sms_state::screen_update_sms));

	SEGA315_5246(config, m_vdp, XTAL(10'738'635));
	m_vdp->set_screen(m_main_scr);
	m_vdp->set_is_pal(false);
	m_vdp->n_int().set_inputline(m_maincpu, 0);
	m_vdp->n_nmi().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_vdp->add_route(ALL_OUTPUTS, "mono", 1.00);

	m_has_bios_full = true;
}


void sms1_state::sms1_ntsc(machine_config &config)
{
	sms_ntsc_base(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &sms1_state::sms1_mem);  // This adds the SegaScope handlers for 3-D glasses

	/* video hardware */
	SCREEN(config, m_main_scr, SCREEN_TYPE_RASTER);
	screen_sms_ntsc_raw_params(*m_main_scr, XTAL(10'738'635)/2);
	m_main_scr->set_screen_update(FUNC(sms1_state::screen_update_sms));

	SCREEN(config, m_left_lcd, SCREEN_TYPE_LCD);    // This is needed for SegaScope Left LCD
	screen_sms_ntsc_raw_params(*m_left_lcd, XTAL(10'738'635)/2);
	m_left_lcd->set_screen_update(FUNC(sms1_state::screen_update_left));

	SCREEN(config, m_right_lcd, SCREEN_TYPE_LCD);   // This is needed for SegaScope Right LCD
	screen_sms_ntsc_raw_params(*m_right_lcd, XTAL(10'738'635)/2);
	m_right_lcd->set_screen_update(FUNC(sms1_state::screen_update_right));

	m_main_scr->screen_vblank().set(FUNC(sms1_state::sscope_vblank));

	config.set_default_layout(layout_sms1);

	SEGA315_5124(config, m_vdp, XTAL(10'738'635));
	m_vdp->set_screen(m_main_scr);
	m_vdp->set_is_pal(false);
	m_vdp->n_int().set_inputline(m_maincpu, 0);
	m_vdp->n_nmi().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_vdp->add_route(ALL_OUTPUTS, "mono", 1.00);

	// card and expansion slots, not present in Master System II
	SMS_CARD_SLOT(config, "mycard", sms_cart, nullptr);
	SMS_EXPANSION_SLOT(config, "smsexp", sms_expansion_devices, nullptr);

	m_has_bios_full = true;
	m_has_pwr_led = true;
}

void smssdisp_state::sms_sdisp(machine_config &config)
{
	sms1_ntsc(config);

	m_vdp->n_int().set(FUNC(smssdisp_state::sms_store_int_callback));

	Z80(config, m_control_cpu, XTAL(10'738'635)/3);
	m_control_cpu->set_addrmap(AS_PROGRAM, &smssdisp_state::sms_store_mem);
	/* Both CPUs seem to communicate with the VDP etc? */
	m_control_cpu->set_addrmap(AS_IO, &smssdisp_state::sms_io);

	config.device_remove("mycard");
	config.device_remove("smsexp");

	for (int i = 1; i < 16; i++)
		SMS_CART_SLOT(config, m_slots[i], sms_cart, nullptr);
	for (int i = 0; i < 16; i++)
		SMS_CARD_SLOT(config, m_cards[i], sms_cart, nullptr);

	m_has_bios_full = false;
	m_has_pwr_led = false;
}

void sms_state::sms_pal_base(machine_config &config)
{
	sms_base(config);
	/* basic machine hardware */
	Z80(config, m_maincpu, MASTER_CLOCK_PAL/15);
	m_maincpu->set_addrmap(AS_PROGRAM, &sms_state::sms_mem);
	m_maincpu->set_addrmap(AS_IO, &sms_state::sms_io);

	config.set_maximum_quantum(attotime::from_hz(50));
}

void sms_state::sms2_pal(machine_config &config)
{
	sms_pal_base(config);

	/* video hardware */
	SCREEN(config, m_main_scr, SCREEN_TYPE_RASTER);
	screen_sms_pal_raw_params(*m_main_scr, MASTER_CLOCK_PAL/10);
	m_main_scr->set_screen_update(FUNC(sms_state::screen_update_sms));

	SEGA315_5246(config, m_vdp, MASTER_CLOCK_PAL/5);
	m_vdp->set_screen(m_main_scr);
	m_vdp->set_is_pal(true);
	m_vdp->n_int().set_inputline(m_maincpu, 0);
	m_vdp->n_nmi().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_vdp->add_route(ALL_OUTPUTS, "mono", 1.00);

	m_has_bios_full = true;
}

void sms1_state::sms1_pal(machine_config &config)
{
	sms_pal_base(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &sms1_state::sms1_mem);  // This adds the SegaScope handlers for 3-D glasses

	/* video hardware */
	SCREEN(config, m_main_scr, SCREEN_TYPE_RASTER);
	screen_sms_pal_raw_params(*m_main_scr, MASTER_CLOCK_PAL/10);
	m_main_scr->set_screen_update(FUNC(sms1_state::screen_update_sms));

	SCREEN(config, m_left_lcd, SCREEN_TYPE_LCD);    // This is needed for SegaScope Left LCD
	screen_sms_pal_raw_params(*m_left_lcd, MASTER_CLOCK_PAL/10);
	m_left_lcd->set_screen_update(FUNC(sms1_state::screen_update_left));

	SCREEN(config, m_right_lcd, SCREEN_TYPE_LCD);   // This is needed for SegaScope Right LCD
	screen_sms_pal_raw_params(*m_right_lcd, MASTER_CLOCK_PAL/10);
	m_right_lcd->set_screen_update(FUNC(sms1_state::screen_update_right));

	m_main_scr->screen_vblank().set(FUNC(sms1_state::sscope_vblank));

	config.set_default_layout(layout_sms1);

	SEGA315_5124(config, m_vdp, MASTER_CLOCK_PAL/5);
	m_vdp->set_screen(m_main_scr);
	m_vdp->set_is_pal(true);
	m_vdp->n_int().set_inputline(m_maincpu, 0);
	m_vdp->n_nmi().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_vdp->add_route(ALL_OUTPUTS, "mono", 1.00);

	// card and expansion slots, not present in Master System II
	SMS_CARD_SLOT(config, "mycard", sms_cart, nullptr);
	SMS_EXPANSION_SLOT(config, "smsexp", sms_expansion_devices, nullptr);

	m_has_bios_full = true;
	m_has_pwr_led = true;
}


void sms_state::sms_paln_base(machine_config &config)
{
	sms_base(config);
	/* basic machine hardware */
	Z80(config, m_maincpu, MASTER_CLOCK_PALN/3);
	m_maincpu->set_addrmap(AS_PROGRAM, &sms_state::sms_mem);
	m_maincpu->set_addrmap(AS_IO, &sms_state::sms_io);

	config.set_maximum_quantum(attotime::from_hz(50));
}

void sms_state::sms3_paln(machine_config &config)
{
	sms_paln_base(config);

	/* video hardware */
	SCREEN(config, m_main_scr, SCREEN_TYPE_RASTER);
	screen_sms_pal_raw_params(*m_main_scr, MASTER_CLOCK_PALN/2);
	m_main_scr->set_screen_update(FUNC(sms_state::screen_update_sms));

	SEGA315_5246(config, m_vdp, MASTER_CLOCK_PALN);
	m_vdp->set_screen(m_main_scr);
	m_vdp->set_is_pal(true);
	m_vdp->n_int().set_inputline(m_maincpu, 0);
	m_vdp->n_nmi().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_vdp->add_route(ALL_OUTPUTS, "mono", 1.00);

	m_has_bios_full = true;
}

void sms1_state::sms1_paln(machine_config &config)
{
	sms_paln_base(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &sms1_state::sms1_mem);  // This adds the SegaScope handlers for 3-D glasses

	/* video hardware */
	SCREEN(config, m_main_scr, SCREEN_TYPE_RASTER);
	screen_sms_pal_raw_params(*m_main_scr, MASTER_CLOCK_PALN/2);
	m_main_scr->set_screen_update(FUNC(sms1_state::screen_update_sms));

	SCREEN(config, m_left_lcd, SCREEN_TYPE_LCD);    // This is needed for SegaScope Left LCD
	screen_sms_pal_raw_params(*m_left_lcd, MASTER_CLOCK_PALN/2);
	m_left_lcd->set_screen_update(FUNC(sms1_state::screen_update_left));

	SCREEN(config, m_right_lcd, SCREEN_TYPE_LCD);   // This is needed for SegaScope Right LCD
	screen_sms_pal_raw_params(*m_right_lcd, MASTER_CLOCK_PALN/2);
	m_right_lcd->set_screen_update(FUNC(sms1_state::screen_update_right));

	m_main_scr->screen_vblank().set(FUNC(sms1_state::sscope_vblank));

	config.set_default_layout(layout_sms1);

	SEGA315_5124(config, m_vdp, MASTER_CLOCK_PALN);
	m_vdp->set_screen(m_main_scr);
	m_vdp->set_is_pal(true);
	m_vdp->n_int().set_inputline(m_maincpu, 0);
	m_vdp->n_nmi().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_vdp->add_route(ALL_OUTPUTS, "mono", 1.00);

	// card and expansion slots, not present in Tec Toy Master System III
	SMS_CARD_SLOT(config, "mycard", sms_cart, nullptr);
	SMS_EXPANSION_SLOT(config, "smsexp", sms_expansion_devices, nullptr);

	m_has_bios_full = true;
	m_has_pwr_led = true;
}


void sms_state::sms_br_base(machine_config &config)
{
	sms_base(config);
	/* basic machine hardware */
	Z80(config, m_maincpu, MASTER_CLOCK_PALM/3);
	m_maincpu->set_addrmap(AS_PROGRAM, &sms_state::sms_mem);
	m_maincpu->set_addrmap(AS_IO, &sms_state::sms_io);

	// PAL-M has near the same frequency of NTSC
	config.set_maximum_quantum(attotime::from_hz(60));
}

void sms_state::sms3_br(machine_config &config)
{
	sms_br_base(config);
	/* video hardware */
	// PAL-M height/width parameters are the same of NTSC screens.
	SCREEN(config, m_main_scr, SCREEN_TYPE_RASTER);
	screen_sms_ntsc_raw_params(*m_main_scr, MASTER_CLOCK_PALM/2);
	m_main_scr->set_screen_update(FUNC(sms_state::screen_update_sms));

	SEGA315_5246(config, m_vdp, MASTER_CLOCK_PALM);
	m_vdp->set_screen(m_main_scr);
	m_vdp->set_is_pal(false); // PAL-M has same line count of NTSC
	m_vdp->n_int().set_inputline(m_maincpu, 0);
	m_vdp->n_nmi().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_vdp->add_route(ALL_OUTPUTS, "mono", 1.00);

	m_has_bios_full = true;
}

void sms1_state::sms1_br(machine_config &config)
{
	sms_br_base(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &sms1_state::sms1_mem);  // This adds the SegaScope handlers for 3-D glasses

	/* video hardware */
	// PAL-M height/width parameters are the same of NTSC screens.
	SCREEN(config, m_main_scr, SCREEN_TYPE_RASTER);
	screen_sms_ntsc_raw_params(*m_main_scr, MASTER_CLOCK_PALM/2);
	m_main_scr->set_screen_update(FUNC(sms1_state::screen_update_sms));

	SCREEN(config, m_left_lcd, SCREEN_TYPE_LCD);    // This is needed for SegaScope Left LCD
	screen_sms_ntsc_raw_params(*m_left_lcd, MASTER_CLOCK_PALM/2);
	m_left_lcd->set_screen_update(FUNC(sms1_state::screen_update_left));

	SCREEN(config, m_right_lcd, SCREEN_TYPE_LCD);   // This is needed for SegaScope Right LCD
	screen_sms_ntsc_raw_params(*m_right_lcd, MASTER_CLOCK_PALM/2);
	m_right_lcd->set_screen_update(FUNC(sms1_state::screen_update_right));

	m_main_scr->screen_vblank().set(FUNC(sms1_state::sscope_vblank));

	config.set_default_layout(layout_sms1);

	SEGA315_5124(config, m_vdp, MASTER_CLOCK_PALM);
	m_vdp->set_screen(m_main_scr);
	m_vdp->set_is_pal(false); // PAL-M has same line count of NTSC
	m_vdp->n_int().set_inputline(m_maincpu, 0);
	m_vdp->n_nmi().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_vdp->add_route(ALL_OUTPUTS, "mono", 1.00);

	// card and expansion slots, not present in Tec Toy Master System III
	SMS_CARD_SLOT(config, "mycard", sms_cart, nullptr);
	SMS_EXPANSION_SLOT(config, "smsexp", sms_expansion_devices, nullptr);

	m_has_bios_full = true;
	m_has_pwr_led = true;
}


void sms_state::sms2_kr(machine_config &config)
{
	sms2_ntsc(config);

	m_maincpu->set_addrmap(AS_IO, &sms_state::smskr_io);

	config.device_remove("slot");
	SG1000MK3_CART_SLOT(config, "slot", sg1000mk3_cart, nullptr);
	SOFTWARE_LIST(config, "cart_list2").set_original("sg1000");

	// Despite having a Japanese cartridge slot, this version is detected as Export region.
	m_has_jpn_sms_cart_slot = true;
}

void sms1_state::sms1_kr(machine_config &config)
{
	sms1_ntsc(config);

	m_maincpu->set_addrmap(AS_IO, &sms1_state::smskr_io);

	// need to replace the cartridge slot with the Japanese version, so to
	// keep the usual media order, remove and reinsert all of them.
	config.device_remove("slot");
	config.device_remove("mycard");
	config.device_remove("smsexp");
	SG1000MK3_CART_SLOT(config, "slot", sg1000mk3_cart, nullptr);
	SMS_CARD_SLOT(config, "mycard", sms_cart, nullptr);
	SMS_EXPANSION_SLOT(config, "smsexp", sms_expansion_devices, nullptr);

	SOFTWARE_LIST(config, "cart_list2").set_original("sg1000");

	m_vdp->n_csync().set(FUNC(sms1_state::rapid_n_csync_callback));

	m_has_bios_full = false;
	m_has_bios_2000 = true;
	m_ioctrl_region_is_japan = true;
	m_has_jpn_sms_cart_slot = true;
}

void sms1_state::smsj(machine_config &config)
{
	sms1_kr(config);

	m_maincpu->set_addrmap(AS_IO, &sms1_state::smsj_io);

	YM2413(config, m_ym, XTAL(10'738'635)/3);
	// if this output gain is changed, the gain set when unmute the output need
	// to be changed too, probably along the gain set for the Mark III FM Unit.
	m_ym->add_route(ALL_OUTPUTS, "mono", 1.00);

	m_is_smsj = true;
}

void sg1000m3_state::sg1000m3(machine_config &config)
{
	sms1_ntsc(config);

	m_maincpu->set_addrmap(AS_IO, &sg1000m3_state::sg1000m3_io);

	// Remove and reinsert all media slots, as done with the sms1_kr config,
	// and also replace the expansion slot with the SG-1000 version.
	config.device_remove("slot");
	config.device_remove("mycard");
	config.device_remove("smsexp");
	SG1000MK3_CART_SLOT(config, "slot", sg1000mk3_cart, nullptr);
	SMS_CARD_SLOT(config, "mycard", sms_cart, nullptr);
	SG1000_EXPANSION_SLOT(config, m_sgexpslot, sg1000_expansion_devices, nullptr, false);

	SOFTWARE_LIST(config, "cart_list2").set_original("sg1000");

	// Mark III does not have TH connected.
	m_port_ctrl1->th_handler().set_nop();
	m_port_ctrl2->th_handler().set_nop();

	m_has_bios_full = false;
	m_is_mark_iii = true;
	m_has_jpn_sms_cart_slot = true;
}

void gamegear_state::gamegear(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, MASTER_CLOCK_GG/9);
	m_maincpu->set_addrmap(AS_PROGRAM, &gamegear_state::sms_mem);
	m_maincpu->set_addrmap(AS_IO, &gamegear_state::gg_io);

	config.set_maximum_quantum(attotime::from_hz(60));

	/* video hardware */
	SCREEN(config, m_main_scr, SCREEN_TYPE_LCD);
	screen_gg_raw_params(*m_main_scr, MASTER_CLOCK_GG/6);
	m_main_scr->set_screen_update(FUNC(gamegear_state::screen_update_gamegear));

	/* sound hardware */
	SPEAKER(config, "speaker", 2).front();

	/* VDP chip of the Gamegear 2 ASIC version */
	SEGA315_5377(config, m_vdp, MASTER_CLOCK_GG/3);
	m_vdp->set_screen(m_main_scr);
	m_vdp->set_is_pal(false);
	m_vdp->n_int().set_inputline(m_maincpu, 0);
	m_vdp->vblank().set(FUNC(gamegear_state::gg_pause_callback));
	m_vdp->add_route(0, "speaker", 1.00, 0);
	m_vdp->add_route(1, "speaker", 1.00, 1);

	/* cartridge */
	GAMEGEAR_CART_SLOT(config, "slot", gg_cart, nullptr).set_must_be_loaded(true);

	SOFTWARE_LIST(config, "cart_list").set_original("gamegear");

	GAMEGEAR_IO_PORT(config, m_gg_ioport, 0);
	m_gg_ioport->set_in_handler(m_port_gg_ext, FUNC(sms_control_port_device::in_r));
	m_gg_ioport->set_out_handler(m_port_gg_ext, FUNC(sms_control_port_device::out_w));
	m_gg_ioport->hl_handler().set(FUNC(gamegear_state::gg_nmi));

	SMS_CONTROL_PORT(config, m_port_gg_ext, sms_control_port_devices, nullptr);
	m_port_gg_ext->set_screen(m_main_scr);
	m_port_gg_ext->th_handler().set(FUNC(gamegear_state::gg_ext_th_input));

	m_is_gamegear = true;
	m_has_bios_0400 = true;
	m_has_pwr_led = true;
}

void gamegear_state::gamegeaj(machine_config &config)
{
	gamegear(config);
	m_ioctrl_region_is_japan = true;
}


ROM_START(sms1)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_FILL(0x0000, 0x4000, 0xff)

	ROM_REGION(0x20000, "user1", 0)
	ROM_SYSTEM_BIOS( 0, "bios13", "US/European BIOS v1.3 (1986)" )
	ROMX_LOAD("mpr-10052.rom", 0x0000, 0x2000, CRC(0072ed54) SHA1(c315672807d8ddb8d91443729405c766dd95cae7), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "hangonsh", "US/European BIOS v2.4 with Hang On and Safari Hunt (1988)" )
	ROMX_LOAD("mpr-11459a.rom", 0x0000, 0x20000, CRC(91e93385) SHA1(9e179392cd416af14024d8f31c981d9ee9a64517), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "hangon", "US/European BIOS v3.4 with Hang On (1988)" )
	ROMX_LOAD("mpr-11458.rom", 0x0000, 0x20000, CRC(8edf7ac6) SHA1(51fd6d7990f62cd9d18c9ecfc62ed7936169107e), ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "missiled", "US/European BIOS v4.4 with Missile Defense 3D (1988)" )
	ROMX_LOAD("missiled.rom", 0x0000, 0x20000, CRC(e79bb689) SHA1(aa92ae576ca670b00855e278378d89e9f85e0351), ROM_BIOS(3))
	ROM_SYSTEM_BIOS( 4, "v10", "US Master System BIOS v1.0 (prototype)" )
	ROMX_LOAD("v1.0.bin", 0x0000, 0x2000, CRC(72bec693) SHA1(29091ff60ef4c22b1ee17aa21e0e75bac6b36474), ROM_BIOS(4))
	ROM_SYSTEM_BIOS( 5, "proto", "US Master System Prototype BIOS" )
	ROMX_LOAD("m404prot.rom", 0x0000, 0x2000, CRC(1a15dfcc) SHA1(4a06c8e66261611dce0305217c42138b71331701), ROM_BIOS(5))
ROM_END

ROM_START(sms)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_FILL(0x0000, 0x4000, 0xff)

	ROM_REGION(0x20000, "user1", 0)
	ROM_SYSTEM_BIOS( 0, "alexkidd", "US/European BIOS with Alex Kidd in Miracle World (1990)" )
	ROMX_LOAD("mpr-12808.ic2", 0x0000, 0x20000, CRC(cf4a09ea) SHA1(3af7b66248d34eb26da40c92bf2fa4c73a46a051), ROM_BIOS(0)) /* "SEGA // MPR-12808 W63 // 9114E9004" @ IC2 */
ROM_END

ROM_START(smssdisp)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_FILL(0x0000, 0x4000, 0x00)

	ROM_REGION(0x4000, "user1", 0)
	ROM_FILL(0x0000, 0x4000, 0xff)

	ROM_REGION(0x4000, "control", 0)
	ROM_LOAD("smssdisp.rom", 0x0000, 0x4000, CRC(ee2c29ba) SHA1(fc465122134d95363112eb51b9ab71db3576cefd))
ROM_END

ROM_START(sms1pal)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_FILL(0x0000, 0x4000, 0xff)

	ROM_REGION(0x20000, "user1", 0)
	ROM_SYSTEM_BIOS( 0, "bios13", "US/European BIOS v1.3 (1986)" )
	ROMX_LOAD("mpr-10052.rom", 0x0000, 0x2000, CRC(0072ed54) SHA1(c315672807d8ddb8d91443729405c766dd95cae7), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "bios20", "European BIOS v2.0 (1987?)" ) //Chinese (PAL-D)?
	ROMX_LOAD("mpr-10883.rom", 0x0000, 0x2000, CRC(b3d854f8) SHA1(fc7eb9141f38c92bf98d9134816f64b45e811112), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "hangonsh", "US/European BIOS v2.4 with Hang On and Safari Hunt (1988)" )
	ROMX_LOAD("mpr-11459a.rom", 0x0000, 0x20000, CRC(91e93385) SHA1(9e179392cd416af14024d8f31c981d9ee9a64517), ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "hangon", "Sega Master System - US/European BIOS v3.4 with Hang On (1988)" )
	ROMX_LOAD("mpr-11458.rom", 0x0000, 0x20000, CRC(8edf7ac6) SHA1(51fd6d7990f62cd9d18c9ecfc62ed7936169107e), ROM_BIOS(3))
	ROM_SYSTEM_BIOS( 4, "missiled", "US/European BIOS v4.4 with Missile Defense 3D (1988)" )
	ROMX_LOAD("missiled.rom", 0x0000, 0x20000, CRC(e79bb689) SHA1(aa92ae576ca670b00855e278378d89e9f85e0351), ROM_BIOS(4))
ROM_END

ROM_START(smspal)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_FILL(0x0000, 0x4000, 0xff)

	ROM_REGION(0x40000, "user1", 0)
	ROM_SYSTEM_BIOS( 0, "alexkidd", "US/European BIOS with Alex Kidd in Miracle World (1990)" ) /* PCB Label: SEGA // IC BD M4Jr. PAL" Master System II with 314-5246 (ZIP) VDP and 314-5237 (DIP48) IO */
	ROMX_LOAD("mpr-12808.ic2", 0x0000, 0x20000, CRC(cf4a09ea) SHA1(3af7b66248d34eb26da40c92bf2fa4c73a46a051), ROM_BIOS(0)) /* "SEGA // MPR-12808 W63 // 9114E9004" @ IC2 */
	ROM_SYSTEM_BIOS( 1, "sonic", "European/Brazilian BIOS with Sonic the Hedgehog (1991)" )
	ROMX_LOAD("sonbios.rom", 0x0000, 0x40000, CRC(81c3476b) SHA1(6aca0e3dffe461ba1cb11a86cd4caf5b97e1b8df), ROM_BIOS(1))
ROM_END

ROM_START(sg1000m3)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_FILL(0x0000, 0x4000, 0x00)
ROM_END

ROM_START(smsj) /* PCB Label: "SEGA(R) IC BOARD M4J MAIN // 837-6418"; has "YM2413 // 78 04 71 G" at IC10; Back of pcb has traces marked "171-5541 (C)SEGA 1987 MADE IN JAPAN"
    see http://www.smspower.org/Development/JapaneseSMS837-6418 */
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_FILL(0x0000, 0x4000, 0xff)

	ROM_REGION(0x4000, "user1", 0)
	ROM_SYSTEM_BIOS( 0, "jbios21", "Japanese BIOS v2.1 (1987)" )
	ROMX_LOAD("mpr-11124.ic2", 0x0000, 0x2000, CRC(48d44a13) SHA1(a8c1b39a2e41137835eda6a5de6d46dd9fadbaf2), ROM_BIOS(0)) /* "SONY 7J06 // MPR-11124 // JAPAN 021" @ IC2 */
ROM_END

ROM_START(smskr)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_FILL(0x0000, 0x4000, 0xff)

	ROM_REGION(0x20000, "user1", 0)
	ROM_SYSTEM_BIOS( 0, "akbioskr", "Samsung Gam*Boy II with Alex Kidd in Miracle World (1990)" )
	ROMX_LOAD("akbioskr.rom", 0x000, 0x20000, CRC(9c5bad91) SHA1(2feafd8f1c40fdf1bd5668f8c5c02e5560945b17), ROM_BIOS(0))
ROM_END

ROM_START(sms1br)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_FILL(0x0000, 0x4000, 0xff)

	ROM_REGION(0x20000, "user1", 0)
	ROM_SYSTEM_BIOS( 0, "hangonsh", "US/European BIOS v2.4 with Hang On and Safari Hunt (1988)" )
	ROMX_LOAD("mpr-11459a.rom", 0x0000, 0x20000, CRC(91e93385) SHA1(9e179392cd416af14024d8f31c981d9ee9a64517), ROM_BIOS(0))
ROM_END

ROM_START(sms2br)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_FILL(0x0000, 0x4000, 0xff)

	ROM_REGION(0x40000, "user1", 0)
	ROM_SYSTEM_BIOS( 0, "alexkidd", "US/European BIOS with Alex Kidd in Miracle World (1990)" )
	ROMX_LOAD("mpr-12808.ic2", 0x0000, 0x20000, CRC(cf4a09ea) SHA1(3af7b66248d34eb26da40c92bf2fa4c73a46a051), ROM_BIOS(0))
ROM_END

ROM_START(smsbr)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_FILL(0x0000, 0x4000, 0xff)

	ROM_REGION(0x40000, "user1", 0)
	ROM_SYSTEM_BIOS( 0, "alexkidd", "US/European BIOS with Alex Kidd in Miracle World (1990)" )
	ROMX_LOAD("mpr-12808.ic2", 0x0000, 0x20000, CRC(cf4a09ea) SHA1(3af7b66248d34eb26da40c92bf2fa4c73a46a051), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "sonic", "European/Brazilian BIOS with Sonic the Hedgehog (1991)" )
	ROMX_LOAD("sonbios.rom", 0x0000, 0x40000, CRC(81c3476b) SHA1(6aca0e3dffe461ba1cb11a86cd4caf5b97e1b8df), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "hangonsh", "US/European BIOS v2.4 with Hang On and Safari Hunt (1988)" )
	ROMX_LOAD("mpr-11459a.rom", 0x0000, 0x20000, CRC(91e93385) SHA1(9e179392cd416af14024d8f31c981d9ee9a64517), ROM_BIOS(2))
ROM_END

ROM_START(sms2paln)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_FILL(0x0000, 0x4000, 0xff)

	ROM_REGION(0x40000, "user1", 0)
	ROM_SYSTEM_BIOS( 0, "alexkidd", "US/European BIOS with Alex Kidd in Miracle World (1990)" )
	ROMX_LOAD("mpr-12808.ic2", 0x0000, 0x20000, CRC(cf4a09ea) SHA1(3af7b66248d34eb26da40c92bf2fa4c73a46a051), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "missiled", "US/European BIOS v4.4 with Missile Defense 3D (1988)" )
	ROMX_LOAD("missiled.rom", 0x0000, 0x20000, CRC(e79bb689) SHA1(aa92ae576ca670b00855e278378d89e9f85e0351), ROM_BIOS(1))
ROM_END

ROM_START(smspaln)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_FILL(0x0000, 0x4000, 0xff)

	ROM_REGION(0x40000, "user1", 0)
	ROM_SYSTEM_BIOS( 0, "alexkidd", "US/European BIOS with Alex Kidd in Miracle World (1990)" )
	ROMX_LOAD("mpr-12808.ic2", 0x0000, 0x20000, CRC(cf4a09ea) SHA1(3af7b66248d34eb26da40c92bf2fa4c73a46a051), ROM_BIOS(0))
ROM_END

ROM_START(gamegear)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_FILL(0x0000, 0x4000, 0x00)

	ROM_REGION(0x0400, "user1", 0)
	ROM_SYSTEM_BIOS( 0, "none", "No BIOS" )
	ROM_SYSTEM_BIOS( 1, "majesco", "Majesco BIOS" )
	ROMX_LOAD("majbios.rom", 0x0000, 0x0400, CRC(0ebea9d4) SHA1(914aa165e3d879f060be77870d345b60cfeb4ede), ROM_BIOS(1))
ROM_END

#define rom_gamegeaj rom_gamegear
#define rom_sms1krfm rom_smsj
#define rom_sms1kr rom_smsj
#define rom_sms1paln rom_sms1br

/***************************************************************************

  Game driver(s)

  US
   - Sega Master System (I) (sms1)
     - prototype (M404) bios - 1986
     - without built-in game v1.3 - 1986
     - built-in Hang On/Safari Hunt v2.4 - 1988
     - built-in Hang On v3.4 - 1988
     - built-in Missile Defense 3-D v4.4 - 1988
     - built-in Hang On/Astro Warrior - 19??
   - Sega Master System II (sms)
     - built-in Alex Kidd in Miracle World - 1990

  JP
   - Sega SG-1000 Mark III (sg1000m3)
     - no bios - 1985
   - Sega Master System (I) (smsj)
     - without built-in game v2.1 - 1987

  KR
   - Samsung Gam*Boy (I) - with FM Chip (sms1krfm)
     - without built-in game v2.1 - 1989
   - Samsung Gam*Boy (I) (sms1kr)
     - without built-in game v2.1 - 19??
   - Samsung Gam*Boy II / Aladdin Boy (smskr)
     - built-in Alex Kidd in Miracle World (Korean) - 1991 (GB II) / 1992 (AB)
  Note about KR:
     - units of Gam*Boy (I) with plug-in AC adaptor have FM and the ones with
       built-in AC adaptor do not.

  EU
   - Sega Master System (I) (sms1pal)
     - without built-in game v1.3 - 1986
     - built-in Hang On/Safari Hunt v2.4 - 1988
     - built-in Hang On v3.4 - 1988
     - built-in Missile Defense 3-D v4.4 - 1988
     - built-in Hang On/Astro Warrior - 19??
   - Sega Master System II (smspal)
     - built-in Alex Kidd in Miracle World - 1990
     - built-in Sonic the Hedgehog - 1991

  BR
   - Tec Toy Master System (I) (sms1br)
     - built-in Hang On/Safari Hunt v2.4 - 1989
   - Tec Toy Master System II (sms2br)
     - built-in Alex Kidd in Miracle World - 1991
   - Tec Toy Master System III Compact (smsbr)
     - built-in Alex Kidd in Miracle World - 1992
     - built-in Sonic the Hedgehog - 1993
     - built-in World Cup Italia '90 (Super Futebol II) - 1994
     - built-in Hang On/Safari Hunt v2.4 (blue L.Phaser pack) - 1995
   - Tec Toy Master System Super Compact (no driver)
     - built-in Alex Kidd in Miracle World - 1993
     - built-in Sonic the Hedgehog - 1993
     - built-in World Cup Italia '90 (Super Futebol II) - 1994
   - Tec Toy Master System Girl (no driver)
     - built-in Monica no Castelo do Dragao - 1994
     - built-in Sonic the Hedgehog (T. Monica em O Resgate pack) - 199?
  Notes about BR:
   - PAL-M has the same line count and near the same frequency of NTSC
   - Tec Toy later changed its logo twice and its name to Tectoy
   - 20XX models (Handy, Collection, Evolution...) likely have SoC hardware

  PAL-N (Argentina, Paraguay, Uruguay)
   - Tec Toy Master System (I) (sms1paln)
     - built-in Hang On/Safari Hunt v2.4
   - Tec Toy Master System II (sms2paln)
     - built-in Alex Kidd in Miracle World
     - built-in Missile Defense 3-D v4.4
   - Tec Toy Master System III Compact (smspaln)
     - built-in Alex Kidd in Miracle World
  Notes:
   - Distributed by: Gameland (Argentina), Forstar (Uruguay)

  These are coin-operated machines (stuff for MAME):

   - Sega Game Box 9
   - Sega Mark III Soft Desk 5
   - Sega Mark III Soft Desk 10
   - Sega Shooting Zone

   The SMS Store Display Unit is labeled PD-W UNIT. Pictures found on Internet
   show cartridges with a label where a not-for-sale message is written along
   the information that it is for use on the Product Display-Working Unit.

***************************************************************************/

/*    YEAR  NAME      PARENT    COMPAT  MACHINE    INPUT     CLASS           INIT           COMPANY    FULLNAME                              FLAGS */
CONS( 1985, sg1000m3, sms,      0,      sg1000m3,  sg1000m3, sg1000m3_state, empty_init,    "Sega",    "Mark III",                           MACHINE_SUPPORTS_SAVE )
CONS( 1986, sms1,     sms,      0,      sms1_ntsc, sms1,     sms1_state,     empty_init,    "Sega",    "Master System",                      MACHINE_SUPPORTS_SAVE )
CONS( 1986, sms1pal,  sms,      0,      sms1_pal,  sms1,     sms1_state,     empty_init,    "Sega",    "Master System (PAL)" ,               MACHINE_SUPPORTS_SAVE )
CONS( 1986, smssdisp, sms,      0,      sms_sdisp, smssdisp, smssdisp_state, empty_init,    "Sega",    "Master System Store Display Unit",   MACHINE_SUPPORTS_SAVE )
CONS( 1987, smsj,     sms,      0,      smsj,      smsj,     sms1_state,     empty_init,    "Sega",    "Master System (Japan)",              MACHINE_SUPPORTS_SAVE )
CONS( 1990, sms,      0,        0,      sms2_ntsc, sms,      sms_state,      empty_init,    "Sega",    "Master System II",                   MACHINE_SUPPORTS_SAVE )
CONS( 1990, smspal,   sms,      0,      sms2_pal,  sms,      sms_state,      empty_init,    "Sega",    "Master System II (PAL)",             MACHINE_SUPPORTS_SAVE )
CONS( 1989, sms1krfm, sms,      0,      smsj,      smsj,     sms1_state,     empty_init,    "Samsung", "Gam*Boy (Korea) (FM)",               MACHINE_SUPPORTS_SAVE )
CONS( 19??, sms1kr,   sms,      0,      sms1_kr,   smsj,     sms1_state,     empty_init,    "Samsung", "Gam*Boy (Korea)",                    MACHINE_SUPPORTS_SAVE )
CONS( 1991, smskr,    sms,      0,      sms2_kr,   sms,      sms_state,      empty_init,    "Samsung", "Gam*Boy II (Korea)",                 MACHINE_SUPPORTS_SAVE )
CONS( 1989, sms1br,   sms,      0,      sms1_br,   sms1,     sms1_state,     empty_init,    "Tec Toy", "Master System (Brazil)",             MACHINE_SUPPORTS_SAVE )
CONS( 1991, sms2br,   sms,      0,      sms1_br,   sms1,     sms1_state,     empty_init,    "Tec Toy", "Master System II (Brazil)",          MACHINE_SUPPORTS_SAVE )
CONS( 1992, smsbr,    sms,      0,      sms3_br,   sms,      sms_state,      empty_init,    "Tec Toy", "Master System III Compact (Brazil)", MACHINE_SUPPORTS_SAVE )
CONS( 19??, sms1paln, sms,      0,      sms1_paln, sms1,     sms1_state,     empty_init,    "Tec Toy", "Master System (PAL-N)",              MACHINE_SUPPORTS_SAVE )
CONS( 19??, sms2paln, sms,      0,      sms1_paln, sms1,     sms1_state,     empty_init,    "Tec Toy", "Master System II (PAL-N)",           MACHINE_SUPPORTS_SAVE )
CONS( 19??, smspaln,  sms,      0,      sms3_paln, sms,      sms_state,      empty_init,    "Tec Toy", "Master System III Compact (PAL-N)",  MACHINE_SUPPORTS_SAVE )

CONS( 1991, gamegear, 0,        sms,    gamegear,  gg,       gamegear_state, empty_init,    "Sega",    "Game Gear (Europe/America)",         MACHINE_SUPPORTS_SAVE )
CONS( 1990, gamegeaj, gamegear, 0,      gamegeaj,  gg,       gamegear_state, empty_init,    "Sega",    "Game Gear (Japan)",                  MACHINE_SUPPORTS_SAVE )



snes.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese, R. Belmont, Anthony Kruize, Fabio Priuli
/***************************************************************************

  snes.c

  Driver file to handle emulation of the Nintendo Super NES.

  Based on original MESS driver by Lee Hammerton (aka Savoury Snax),
  later contributions by
   R. Belmont
   Anthony Kruize
   Angelo Salese
   Fabio Priuli
   byuu (extensive RE of both SNES and add-on chips)


  Todo (in no particular order):
    - Fix sync between 5A22, SPC700 and PPU
    - Fix remaining sound and video bugs
    - Fix vertical mosaic effects
    - Add support for real CX4 and ST018 CPUs
    - Add support for SA-1 and SuperGB add-ons
    - Fix superscope support
    - Add support for other controllers

***************************************************************************/

#include "emu.h"
#include "snes.h"

#include "snescx4.h"

#include "bus/snes/snes_slot.h"
#include "bus/snes/snes_carts.h"
#include "bus/snes_ctrl/ctrl.h"

#include "softlist_dev.h"
#include "speaker.h"


class snes_console_state : public snes_state
{
public:
	snes_console_state(const machine_config &mconfig, device_type type, const char *tag)
			: snes_state(mconfig, type, tag)
			, m_ctrl1(*this, "ctrl1")
			, m_ctrl2(*this, "ctrl2")
			, m_cartslot(*this, "snsslot")
	{ }

	void snespal(machine_config &config);
	void snes(machine_config &config);

private:
	uint8_t snes20_hi_r(address_space &space, offs_t offset);
	void snes20_hi_w(address_space &space, offs_t offset, uint8_t data);
	uint8_t snes20_lo_r(address_space &space, offs_t offset);
	void snes20_lo_w(address_space &space, offs_t offset, uint8_t data);
	uint8_t snes21_lo_r(address_space &space, offs_t offset);
	void snes21_lo_w(address_space &space, offs_t offset, uint8_t data);
	uint8_t snes21_hi_r(address_space &space, offs_t offset);
	void snes21_hi_w(address_space &space, offs_t offset, uint8_t data);
	uint8_t snessfx_hi_r(address_space &space, offs_t offset);
	uint8_t snessfx_lo_r(address_space &space, offs_t offset);
	void snessfx_hi_w(address_space &space, offs_t offset, uint8_t data);
	void snessfx_lo_w(address_space &space, offs_t offset, uint8_t data);
	uint8_t snessa1_hi_r(address_space &space, offs_t offset);
	uint8_t snessa1_lo_r(address_space &space, offs_t offset);
	void snessa1_hi_w(address_space &space, offs_t offset, uint8_t data);
	void snessa1_lo_w(address_space &space, offs_t offset, uint8_t data);
	uint8_t snes7110_hi_r(address_space &space, offs_t offset);
	uint8_t snes7110_lo_r(address_space &space, offs_t offset);
	void snes7110_hi_w(address_space &space, offs_t offset, uint8_t data);
	void snes7110_lo_w(address_space &space, offs_t offset, uint8_t data);
	uint8_t snessdd1_lo_r(address_space &space, offs_t offset);
	void snessdd1_lo_w(address_space &space, offs_t offset, uint8_t data);
	uint8_t snessdd1_hi_r(address_space &space, offs_t offset);
	void snessdd1_hi_w(address_space &space, offs_t offset, uint8_t data);
	uint8_t snesbsx_hi_r(address_space &space, offs_t offset);
	void snesbsx_hi_w(address_space &space, offs_t offset, uint8_t data);
	uint8_t snesbsx_lo_r(address_space &space, offs_t offset);
	void snesbsx_lo_w(address_space &space, offs_t offset, uint8_t data);
	uint8_t snessgb_hi_r(address_space &space, offs_t offset);
	uint8_t snessgb_lo_r(address_space &space, offs_t offset);
	void snessgb_hi_w(address_space &space, offs_t offset, uint8_t data);
	void snessgb_lo_w(address_space &space, offs_t offset, uint8_t data);
	uint8_t pfest94_hi_r(address_space &space, offs_t offset);
	void pfest94_hi_w(address_space &space, offs_t offset, uint8_t data);
	uint8_t pfest94_lo_r(address_space &space, offs_t offset);
	void pfest94_lo_w(address_space &space, offs_t offset, uint8_t data);

	// input related
	SNESCTRL_ONSCREEN_CB(onscreen_cb);
	SNESCTRL_GUNLATCH_CB(gun_latch_cb);
	virtual void io_read() override;
	virtual uint8_t oldjoy1_read(int latched) override;
	virtual uint8_t oldjoy2_read(int latched) override;
	virtual void write_joy_latch(uint8_t data) override;
	virtual void wrio_write(uint8_t data) override;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	int m_type = 0;
	required_device<snes_control_port_device> m_ctrl1;
	required_device<snes_control_port_device> m_ctrl2;
	optional_device<sns_cart_slot_device> m_cartslot;

	void snes_map(address_map &map) ATTR_COLD;
	void spc_map(address_map &map) ATTR_COLD;
};


/*************************************
 *
 *  Memory handlers
 *
 *************************************/

// Memory access for the various types of carts

//---------------------------------------------------------------------------------
// LoROM & LoROM + BSX slot & LoROM + some add-on chips
//---------------------------------------------------------------------------------

// In general LoROM games have perfect mirror between 0x00-0x7d and 0x80-0xff
// But BSX+LoROM games use different read handlers (to access ROM beyond 2MB)
// so we use two different set of handlers...

// Also we have here LoROM + CX4, until the Hitachi CPU is emulated,
// and the LoROM + Seta DSP, because their chip_read/chip_write need global offset

uint8_t snes_console_state::snes20_hi_r(address_space &space, offs_t offset)
{
	uint16_t address = offset & 0xffff;

	// take care of add-on IO
	if ((m_cartslot->get_type() == SNES_ST010 || m_cartslot->get_type() == SNES_ST011)
		&& (offset >= 0x600000 && offset < 0x680000 && (offset & 0xffff) < 0x4000))
		return m_cartslot->chip_read(offset);
	else if ((m_cartslot->get_type() == SNES_ST010 || m_cartslot->get_type() == SNES_ST011)
				&& (offset >= 0x680000 && offset < 0x700000 && (offset & 0xffff) < 0x8000))
		return m_cartslot->chip_read(offset);
	else if (m_cartslot->get_type() == SNES_CX4
				&& (offset < 0x400000 && (offset & 0xffff) >= 0x6000 && (offset & 0xffff) < 0x8000))    // hack until we emulate the real CPU
		return CX4_read((offset & 0xffff) - 0x6000);

	if (offset < 0x400000)
	{
		if (address < 0x2000)
			return m_wram[address];
		else if (address < 0x6000)
			return snes_r_io(address);
		else if (address < 0x8000)
			return snes_open_bus_r();
		else
			return m_cartslot->read_h(offset);
	}
	else if (offset < 0x700000)
	{
		if (address < 0x8000)
			return snes_open_bus_r();
		else
			return m_cartslot->read_h(offset);
	}
	else
	{
		if (m_type == SNES_SUFAMITURBO && address >= 0x8000 && offset < 0x740000)
			return m_cartslot->read_h(offset);

		// here usually there is SRAM mirrored in the whole range, but if ROM is very large then arrives here too (see tokimeki and wizardg4)
		if (m_cartslot->m_cart->get_rom_size() > 0x200000 && address >= 0x8000)
			return m_cartslot->read_h(offset);
		else
		{
			if (m_cartslot->m_cart->get_nvram_size() > 0x8000)
			{
				// In this case, SRAM is mapped in 0x8000 chunks at diff offsets: 0x700000-0x707fff, 0x710000-0x717fff, etc.
				offset = ((offset - 0x700000) / 0x10000) * 0x8000 + (offset & 0x7fff);
				return m_cartslot->read_ram(offset);
			}
			else if (m_cartslot->m_cart->get_nvram_size() > 0)
				return m_cartslot->read_ram(offset);
			else
				return snes_open_bus_r();
		}
	}
}

void snes_console_state::snes20_hi_w(address_space &space, offs_t offset, uint8_t data)
{
	uint16_t address = offset & 0xffff;

	// take care of add-on IO
	if ((m_cartslot->get_type() == SNES_ST010 || m_cartslot->get_type() == SNES_ST011)
		&& (offset >= 0x600000 && offset < 0x680000 && (offset & 0xffff) < 0x4000))
	{ m_cartslot->chip_write(offset, data); return; }
	else if ((m_cartslot->get_type() == SNES_ST010 || m_cartslot->get_type() == SNES_ST011)
				&& (offset >= 0x680000 && offset < 0x700000 && (offset & 0xffff) < 0x8000))
	{ m_cartslot->chip_write(offset, data); return; }
	else if (m_cartslot->get_type() == SNES_CX4
				&& (offset < 0x400000 && (offset & 0xffff) >= 0x6000 && (offset & 0xffff) < 0x8000))    // hack until we emulate the real CPU
	{ CX4_write(space, (offset & 0xffff) - 0x6000, data); return; }
	else if (m_type == SNES_SUFAMITURBO
				&& address >= 0x8000 && ((offset >= 0x600000 && offset < 0x640000) || (offset >= 0x700000 && offset < 0x740000)))
	{ m_cartslot->write_h(offset, data); return; }

	if (offset < 0x400000)
	{
		if (address < 0x2000)
			m_wram[address] = data;
		else if (address < 0x6000)
			snes_w_io(space, address, data);
	}
	else if (offset >= 0x700000 && (m_cartslot->m_cart->get_rom_size() <= 0x200000 || address < 0x8000))    // NVRAM access
	{
		if (m_cartslot->m_cart->get_nvram_size() > 0x8000)
		{
			// In this case, SRAM is mapped in 0x8000 chunks at diff offsets: 0x700000-0x707fff, 0x710000-0x717fff, etc.
			offset = ((offset - 0x700000) / 0x10000) * 0x8000 + (offset & 0x7fff);
			m_cartslot->write_ram(offset, data);
		}
		else if (m_cartslot->m_cart->get_nvram_size() > 0)
			m_cartslot->write_ram(offset, data);
	}
}

uint8_t snes_console_state::snes20_lo_r(address_space &space, offs_t offset)
{
	uint16_t address = offset & 0xffff;

	// take care of add-on IO
	if ((m_cartslot->get_type() == SNES_ST010 /*|| m_cartslot->get_type() == SNES_ST011*/) // why does this break moritash?
		&& (offset >= 0x600000 && offset < 0x680000 && (offset & 0xffff) < 0x4000))
		return m_cartslot->chip_read(offset);
	else if ((m_cartslot->get_type() == SNES_ST010 || m_cartslot->get_type() == SNES_ST011)
				&& (offset >= 0x680000 && offset < 0x700000 && (offset & 0xffff) < 0x8000))
		return m_cartslot->chip_read(offset);
	else if (m_cartslot->get_type() == SNES_CX4
				&& (offset < 0x400000 && (offset & 0xffff) >= 0x6000 && (offset & 0xffff) < 0x8000))    // hack until we emulate the real CPU
		return CX4_read((offset & 0xffff) - 0x6000);

	if (offset < 0x400000)
	{
		if (address < 0x2000)
			return m_wram[address];
		else if (address < 0x6000)
			return snes_r_io(address);
		else if (address < 0x8000)
			return snes_open_bus_r();
		else
			return m_cartslot->read_l(offset);
	}
	else if (offset < 0x700000)
	{
		if (address < 0x8000)
			return snes_open_bus_r();
		else
			return m_cartslot->read_l(offset);
	}
	else
	{
		if (m_type == SNES_SUFAMITURBO && address >= 0x8000 && offset < 0x740000)
			return m_cartslot->read_l(offset);

		// here usually there is SRAM mirrored in the whole range, but if ROM is very large then arrives here too (see tokimeki and wizardg4)
		if (m_cartslot->m_cart->get_rom_size() > 0x200000 && address >= 0x8000)
			return m_cartslot->read_l(offset);
		else
		{
			if (m_cartslot->m_cart->get_nvram_size() > 0x8000)
			{
				// In this case, SRAM is mapped in 0x8000 chunks at diff offsets: 0x700000-0x707fff, 0x710000-0x717fff, etc.
				offset = ((offset - 0x700000) / 0x10000) * 0x8000 + (offset & 0x7fff);
				return m_cartslot->read_ram(offset);
			}
			else if (m_cartslot->m_cart->get_nvram_size() > 0)
				return m_cartslot->read_ram(offset);
			else
				return snes_open_bus_r();
		}
	}
}

void snes_console_state::snes20_lo_w(address_space &space, offs_t offset, uint8_t data)
{
	if (m_type == SNES_SUFAMITURBO
		&& (offset & 0xffff) >= 0x8000 && ((offset >= 0x600000 && offset < 0x640000) || (offset >= 0x700000 && offset < 0x740000)))
	{ m_cartslot->write_l(offset, data); return; }

	// other add-on writes matches the hi handler
	snes20_hi_w(space, offset, data);
}


//---------------------------------------------------------------------------------
// HiROM & HiROM + BSX slot
//---------------------------------------------------------------------------------

uint8_t snes_console_state::snes21_lo_r(address_space &space, offs_t offset)
{
	uint16_t address = offset & 0xffff;

	if (offset < 0x400000 && address < 0x8000)
	{
		if (address < 0x2000)
			return m_wram[address];
		else if (address < 0x6000)
			return snes_r_io(address);
		else
		{
			if (m_type == SNES_BSXHI && m_cartslot->m_cart->get_nvram_size() && offset >= 0x200000)
			{
				int mask = (m_cartslot->m_cart->get_nvram_size() - 1) & 0x7fff;
				return m_cartslot->read_ram((offset - 0x6000) & mask);
			}

			if (m_cartslot->m_cart->get_nvram_size() && offset >= 0x300000)
			{
				/* Donkey Kong Country checks this and detects a copier if 0x800 is not masked out due to sram size */
				/* OTOH Secret of Mana does not work properly if sram is not mirrored on later banks */
				int mask = (m_cartslot->m_cart->get_nvram_size() - 1) & 0x7fff; /* Limit SRAM size to what's actually present */
				return m_cartslot->read_ram((offset - 0x6000) & mask);
			}
			else
				return snes_open_bus_r();
		}
	}

	// ROM access
	return m_cartslot->read_l(offset);
}

void snes_console_state::snes21_lo_w(address_space &space, offs_t offset, uint8_t data)
{
	uint16_t address = offset & 0xffff;
	if (offset < 0x400000 && address < 0x8000)
	{
		if (address < 0x2000)
			m_wram[address] = data;
		else if (address < 0x6000)
			snes_w_io(space, address, data);
		else
		{
			if (m_type == SNES_BSXHI && m_cartslot->m_cart->get_nvram_size() && offset >= 0x200000)
			{
				int mask = (m_cartslot->m_cart->get_nvram_size() - 1) & 0x7fff;
				m_cartslot->write_ram((offset - 0x6000) & mask, data);
				return;
			}
			if (m_cartslot->m_cart->get_nvram_size() && offset >= 0x300000)
			{
				/* Donkey Kong Country checks this and detects a copier if 0x800 is not masked out due to sram size */
				/* OTOH Secret of Mana does not work properly if sram is not mirrored on later banks */
				int mask = (m_cartslot->m_cart->get_nvram_size() - 1) & 0x7fff; /* Limit SRAM size to what's actually present */
				m_cartslot->write_ram((offset - 0x6000) & mask, data);
			}
		}
	}
}

uint8_t snes_console_state::snes21_hi_r(address_space &space, offs_t offset)
{
	uint16_t address = offset & 0xffff;

	if (offset < 0x400000 && address < 0x8000)
	{
		if (address < 0x2000)
			return m_wram[address];
		else if (address < 0x6000)
			return snes_r_io(address);
		else
		{
			if (m_type == SNES_BSXHI && m_cartslot->m_cart->get_nvram_size() && offset >= 0x200000)
			{
				int mask = (m_cartslot->m_cart->get_nvram_size() - 1) & 0x7fff;
				return m_cartslot->read_ram((offset - 0x6000) & mask);
			}

			if (m_cartslot->m_cart->get_nvram_size() && offset >= 0x300000)
			{
				/* Donkey Kong Country checks this and detects a copier if 0x800 is not masked out due to sram size */
				/* OTOH Secret of Mana does not work properly if sram is not mirrored on later banks */
				int mask = (m_cartslot->m_cart->get_nvram_size() - 1) & 0x7fff; /* Limit SRAM size to what's actually present */
				return m_cartslot->read_ram((offset - 0x6000) & mask);
			}
			else
				return snes_open_bus_r();
		}
	}

	// ROM access
	return m_cartslot->read_h(offset);
}

void snes_console_state::snes21_hi_w(address_space &space, offs_t offset, uint8_t data)
{
	uint16_t address = offset & 0xffff;
	if (offset < 0x400000 && address < 0x8000)
	{
		if (address < 0x2000)
			m_wram[address] = data;
		else if (address < 0x6000)
			snes_w_io(space, address, data);
		else
		{
			if (m_type == SNES_BSXHI && m_cartslot->m_cart->get_nvram_size() && offset >= 0x200000)
			{
				int mask = (m_cartslot->m_cart->get_nvram_size() - 1) & 0x7fff;
				m_cartslot->write_ram((offset - 0x6000) & mask, data);
				return;
			}
			if (m_cartslot->m_cart->get_nvram_size() && offset >= 0x300000)
			{
				/* Donkey Kong Country checks this and detects a copier if 0x800 is not masked out due to sram size */
				/* OTOH Secret of Mana does not work properly if sram is not mirrored on later banks */
				int mask = (m_cartslot->m_cart->get_nvram_size() - 1) & 0x7fff; /* Limit SRAM size to what's actually present */
				m_cartslot->write_ram((offset - 0x6000) & mask, data);
			}
		}
	}
}

//---------------------------------------------------------------------------------
// LoROM + SuperFX / GSU
//---------------------------------------------------------------------------------

uint8_t snes_console_state::snessfx_hi_r(address_space &space, offs_t offset)
{
	uint16_t address = offset & 0xffff;

	if (offset < 0x400000)
	{
		if (address < 0x2000)
			return m_wram[address];
		else if (address < 0x6000)
		{
			if (address >= 0x3000 && address < 0x3300)
				return m_cartslot->chip_read(offset);
			else
				return snes_r_io(address);
		}
		else if (address < 0x8000)
			return m_cartslot->read_ram(offset & 0x1fff);
		else
			return m_cartslot->read_h(offset);
	}
	else if (offset < 0x600000)
		return m_cartslot->read_h(offset);
	else
		return m_cartslot->read_ram(offset);
}

uint8_t snes_console_state::snessfx_lo_r(address_space &space, offs_t offset)
{
	uint16_t address = offset & 0xffff;

	if (offset < 0x400000)
	{
		if (address < 0x2000)
			return m_wram[address];
		else if (address < 0x6000)
		{
			if (address >= 0x3000 && address < 0x3300)
				return m_cartslot->chip_read(offset);
			else
				return snes_r_io(address);
		}
		else if (address < 0x8000)
			return m_cartslot->read_ram(offset & 0x1fff);
		else
			return m_cartslot->read_l(offset);
	}
	else if (offset < 0x600000)
		return m_cartslot->read_l(offset);
	else
		return m_cartslot->read_ram(offset);
}

void snes_console_state::snessfx_hi_w(address_space &space, offs_t offset, uint8_t data)
{
	uint16_t address = offset & 0xffff;
	if (offset < 0x400000)
	{
		if (address < 0x2000)
			m_wram[address] = data;
		else if (address < 0x6000)
		{
			if (address >= 0x3000 && address < 0x3300)
				m_cartslot->chip_write(offset, data);
			else
				snes_w_io(space, address, data);
		}
		else if (address < 0x8000)
			m_cartslot->write_ram(offset & 0x1fff, data);
	}
	else if (offset >= 0x600000)
		m_cartslot->write_ram(offset, data);
}

void snes_console_state::snessfx_lo_w(address_space &space, offs_t offset, uint8_t data)
{
	snessfx_hi_w(space, offset, data);
}

//---------------------------------------------------------------------------------
// LoROM + SA-1
//---------------------------------------------------------------------------------

uint8_t snes_console_state::snessa1_hi_r(address_space &space, offs_t offset)
{
	uint16_t address = offset & 0xffff;

	if (offset < 0x400000)
	{
		if (address < 0x2000)
			return m_wram[address];
		else if (address < 0x6000)
		{
			if (address >= 0x2200 && address < 0x2400)
				return m_cartslot->chip_read(offset);    // SA-1 Regs
			else if (address >= 0x3000 && address < 0x3800)
				return m_cartslot->chip_read(offset);    // Internal SA-1 RAM (2K)
			else
				return snes_r_io(address);
		}
		else if (address < 0x8000)
			return m_cartslot->chip_read(offset);        // SA-1 BWRAM
		else
			return m_cartslot->read_h(offset);
	}
	else
		return m_cartslot->read_h(offset);
}

uint8_t snes_console_state::snessa1_lo_r(address_space &space, offs_t offset)
{
	uint16_t address = offset & 0xffff;

	if (offset < 0x400000)
	{
		if (address < 0x2000)
			return m_wram[address];
		else if (address < 0x6000)
		{
			if (address >= 0x2200 && address < 0x2400)
				return m_cartslot->chip_read(offset);    // SA-1 Regs
			else if (address >= 0x3000 && address < 0x3800)
				return m_cartslot->chip_read(offset);    // Internal SA-1 RAM (2K)
			else
				return snes_r_io(address);
		}
		else if (address < 0x8000)
			return m_cartslot->chip_read(offset);        // SA-1 BWRAM
		else
			return m_cartslot->read_l(offset);
	}
	else if (offset < 0x500000)
		return m_cartslot->chip_read(offset);        // SA-1 BWRAM (not mirrored above!)
	else
		return snes_r_io(address);                   // nothing mapped here!
}

void snes_console_state::snessa1_hi_w(address_space &space, offs_t offset, uint8_t data)
{
	uint16_t address = offset & 0xffff;
	if (offset < 0x400000)
	{
		if (address < 0x2000)
			m_wram[address] = data;
		else if (address < 0x6000)
		{
			if (address >= 0x2200 && address < 0x2400)
				m_cartslot->chip_write(offset, data);    // SA-1 Regs
			else if (address >= 0x3000 && address < 0x3800)
				m_cartslot->chip_write(offset, data);    // Internal SA-1 RAM (2K)
			else
				snes_w_io(space, address, data);
		}
		else if (address < 0x8000)
			m_cartslot->chip_write(offset, data);        // SA-1 BWRAM
	}
}

void snes_console_state::snessa1_lo_w(address_space &space, offs_t offset, uint8_t data)
{
	if (offset >= 0x400000 && offset < 0x500000)
		m_cartslot->chip_write(offset, data);        // SA-1 BWRAM (not mirrored above!)
	else
		snessa1_hi_w(space, offset, data);
}

//---------------------------------------------------------------------------------
// HiROM + SPC-7110
//---------------------------------------------------------------------------------

uint8_t snes_console_state::snes7110_hi_r(address_space &space, offs_t offset)
{
	uint16_t address = offset & 0xffff;

	if (offset < 0x400000)
	{
		if (address < 0x2000)
			return m_wram[address];
		else if (address < 0x6000)
		{
			uint16_t limit = (m_cartslot->get_type() == SNES_SPC7110_RTC) ? 0x4843 : 0x4840;
			if (address >= 0x4800 && address < limit)
				return m_cartslot->chip_read(address);

			return snes_r_io(address);
		}
		else if (address < 0x8000)
		{
			if (offset < 0x10000)
				return m_cartslot->read_ram(offset);
			if (offset >= 0x300000 && offset < 0x310000)
				return m_cartslot->read_ram(offset);
		}
		else
			return m_cartslot->read_h(offset);
	}
	return m_cartslot->read_h(offset);
}

uint8_t snes_console_state::snes7110_lo_r(address_space &space, offs_t offset)
{
	uint16_t address = offset & 0xffff;

	if (offset < 0x400000)
	{
		if (address < 0x2000)
			return m_wram[address];
		else if (address < 0x6000)
		{
			uint16_t limit = (m_cartslot->get_type() == SNES_SPC7110_RTC) ? 0x4843 : 0x4840;
			if (address >= 0x4800 && address < limit)
				return m_cartslot->chip_read(address);

			return snes_r_io(address);
		}
		else if (address < 0x8000)
		{
			if (offset < 0x10000)
				return m_cartslot->read_ram(offset);
			if (offset >= 0x300000 && offset < 0x310000)
				return m_cartslot->read_ram(offset);
		}
		else
			return m_cartslot->read_l(offset);
	}
	if (offset >= 0x500000 && offset < 0x510000)
		return m_cartslot->chip_read(0x4800);

	return snes_open_bus_r();
}

void snes_console_state::snes7110_hi_w(address_space &space, offs_t offset, uint8_t data)
{
	snes7110_lo_w(space, offset, data);
}

void snes_console_state::snes7110_lo_w (address_space &space, offs_t offset, uint8_t data)
{
	uint16_t address = offset & 0xffff;
	if (offset < 0x400000)
	{
		if (address < 0x2000)
			m_wram[address] = data;
		else if (address < 0x6000)
		{
			uint16_t limit = (m_cartslot->get_type() == SNES_SPC7110_RTC) ? 0x4843 : 0x4840;
			if (address >= 0x4800 && address < limit)
			{
				m_cartslot->chip_write(address, data);
				return;
			}
			snes_w_io(space, address, data);
		}
		else if (address < 0x8000)
		{
			if (offset < 0x10000)
				m_cartslot->write_ram(offset, data);
			if (offset >= 0x300000 && offset < 0x310000)
				m_cartslot->write_ram(offset, data);
		}
	}
}


//---------------------------------------------------------------------------------
// LoROM + S-DD1
//---------------------------------------------------------------------------------

uint8_t snes_console_state::snessdd1_lo_r(address_space &space, offs_t offset)
{
	uint16_t address = offset & 0xffff;

	if (offset < 0x400000)
	{
		if (address < 0x2000)
			return m_wram[address];
		else if (address < 0x6000)
		{
			if (address >= 0x4800 && address < 0x4808)
				return m_cartslot->chip_read(address);

			return snes_r_io(address);
		}
		else if (address < 0x8000)
			return snes_open_bus_r();
		else
			return m_cartslot->read_l(offset);
	}
	else if (offset >= 0x700000 && address < 0x8000 && m_cartslot->m_cart->get_nvram_size())    // NVRAM access
		return m_cartslot->read_ram(offset);
	else    // ROM access
		return m_cartslot->read_l(offset);
}

uint8_t snes_console_state::snessdd1_hi_r(address_space &space, offs_t offset)
{
	if (offset >= 0x400000)
		return m_cartslot->read_h(offset);
	else
		return snessdd1_lo_r(space, offset);
}

void snes_console_state::snessdd1_lo_w(address_space &space, offs_t offset, uint8_t data)
{
	snessdd1_hi_w(space, offset, data);
}

void snes_console_state::snessdd1_hi_w(address_space &space, offs_t offset, uint8_t data)
{
	uint16_t address = offset & 0xffff;
	if (offset < 0x400000)
	{
		if (address < 0x2000)
			m_wram[address] = data;
		else if (address < 0x6000)
		{
			if (address >= 0x4300 && address < 0x4380)
			{
				m_cartslot->chip_write(address, data);
				// here we don't return, but we let the w_io happen...
			}
			if (address >= 0x4800 && address < 0x4808)
			{
				m_cartslot->chip_write(address, data);
				return;
			}
			snes_w_io(space, address, data);
		}
	}
	if (offset >= 0x700000 && address < 0x8000 && m_cartslot->m_cart->get_nvram_size())
		return m_cartslot->write_ram(offset, data);
}


//---------------------------------------------------------------------------------
// LoROM + BS-X (Base unit)
//---------------------------------------------------------------------------------

uint8_t snes_console_state::snesbsx_hi_r(address_space &space, offs_t offset)
{
	uint16_t address = offset & 0xffff;

	if (offset < 0x400000)
	{
		if (address < 0x2000)
			return m_wram[address];
		else if (address < 0x6000)
		{
			if (address >= 0x2188 && address < 0x21a0)
				return m_cartslot->chip_read(offset);
			if (address >= 0x5000)
				return m_cartslot->chip_read(offset);
			return snes_r_io(address);
		}
		else if (address < 0x8000)
		{
			if (offset >= 0x200000)
				return m_cartslot->read_h(offset);
			else
				return snes_open_bus_r();
		}
		else
			return m_cartslot->read_h(offset);
	}
	return m_cartslot->read_h(offset);
}

void snes_console_state::snesbsx_hi_w(address_space &space, offs_t offset, uint8_t data)
{
	uint16_t address = offset & 0xffff;
	if (offset < 0x400000)
	{
		if (address < 0x2000)
			m_wram[address] = data;
		else if (address < 0x6000)
		{
			if (address >= 0x2188 && address < 0x21a0)
			{
				m_cartslot->chip_write(offset, data);
				return;
			}
			if (address >= 0x5000)
			{
				m_cartslot->chip_write(offset, data);
				return;
			}
			snes_w_io(space, address, data);
		}
		else if (address < 0x8000)
		{
			if (offset >= 0x200000)
				return m_cartslot->write_l(offset, data);
		}
		else
			return m_cartslot->write_l(offset, data);
	}
	return m_cartslot->write_l(offset, data);
}

uint8_t snes_console_state::snesbsx_lo_r(address_space &space, offs_t offset)
{
	uint16_t address = offset & 0xffff;

	if (offset < 0x400000)
	{
		if (address < 0x2000)
			return m_wram[address];
		else if (address < 0x6000)
		{
			if (address >= 0x2188 && address < 0x21a0)
				return m_cartslot->chip_read(offset);
			if (address >= 0x5000)
				return m_cartslot->chip_read(offset);
			return snes_r_io(address);
		}
		else if (address < 0x8000)
		{
			if (offset >= 0x200000)
				return m_cartslot->read_l(offset);
			else
				return snes_open_bus_r();
		}
		else
			return m_cartslot->read_l(offset);
	}
	return m_cartslot->read_l(offset);
}

void snes_console_state::snesbsx_lo_w(address_space &space, offs_t offset, uint8_t data)
{
	snesbsx_hi_w(space, offset, data);
}


//---------------------------------------------------------------------------------
// LoROM + SuperGB
//---------------------------------------------------------------------------------

uint8_t snes_console_state::snessgb_hi_r(address_space &space, offs_t offset)
{
	uint16_t address = offset & 0xffff;

	if (offset < 0x400000)
	{
		if (address < 0x2000)
			return m_wram[address];
		else if (address < 0x6000)
			return snes_r_io(address);
		else if (address < 0x8000)
			return m_cartslot->chip_read(offset);
		else
			return m_cartslot->read_h(offset);
	}
	else if (address >= 0x8000)
		return m_cartslot->read_h(offset);

	return snes_open_bus_r();
}

uint8_t snes_console_state::snessgb_lo_r(address_space &space, offs_t offset)
{
	return snessgb_hi_r(space, offset);
}

void snes_console_state::snessgb_hi_w(address_space &space, offs_t offset, uint8_t data)
{
	uint16_t address = offset & 0xffff;
	if (offset < 0x400000)
	{
		if (address < 0x2000)
			m_wram[address] = data;
		else if (address < 0x6000)
			snes_w_io(space, address, data);
		else if (address < 0x8000)
			m_cartslot->chip_write(offset, data);
	}
}

void snes_console_state::snessgb_lo_w(address_space &space, offs_t offset, uint8_t data)
{
	snessgb_hi_w(space, offset, data);
}

//---------------------------------------------------------------------------------
// Powerfest '94 event cart
//---------------------------------------------------------------------------------

uint8_t snes_console_state::pfest94_hi_r(address_space &space, offs_t offset)
{
	uint16_t address = offset & 0xffff;

	if (offset < 0x400000)
	{
		if (address < 0x2000)
			return m_wram[address];
		else if (address < 0x6000)
			return snes_r_io(address);
		else if (address < 0x8000)
		{
			if (offset < 0x100000)    // DSP access
				return m_cartslot->chip_read(offset);
			else if (offset == 0x106000)    // menu access
				return m_cartslot->chip_read(offset + 0x8000);
			else if (offset >= 0x300000 && m_cartslot->m_cart->get_nvram_size())    // NVRAM access
				return m_cartslot->read_ram(offset);
			else
				return snes_open_bus_r();
		}
		else
			return m_cartslot->read_h(offset);
	}
	return m_cartslot->read_h(offset);
}

void snes_console_state::pfest94_hi_w(address_space &space, offs_t offset, uint8_t data)
{
	uint16_t address = offset & 0xffff;
	if (offset < 0x400000)
	{
		if (address < 0x2000)
			m_wram[address] = data;
		else if (address < 0x6000)
			snes_w_io(space, address, data);
		else if (address < 0x8000)
		{
			if (offset < 0x100000)    // DSP access
				m_cartslot->chip_write(offset, data);
			else if (offset == 0x206000)    // menu access
				m_cartslot->chip_write(offset + 0x8000, data);
			else if (offset >= 0x300000 && m_cartslot->m_cart->get_nvram_size())    // NVRAM access
				m_cartslot->write_ram(offset, data);
		}
	}
}

uint8_t snes_console_state::pfest94_lo_r(address_space &space, offs_t offset)
{
	uint16_t address = offset & 0xffff;

	if (offset < 0x400000)
	{
		if (address < 0x2000)
			return m_wram[address];
		else if (address < 0x6000)
			return snes_r_io(address);
		else if (address < 0x8000)
		{
			if (offset < 0x100000)    // DSP access
				return m_cartslot->chip_read(offset);
			else if (offset == 0x106000)    // menu access
				return m_cartslot->chip_read(offset + 0x8000);
			else if (offset >= 0x300000 && m_cartslot->m_cart->get_nvram_size())    // NVRAM access
				return m_cartslot->read_ram(offset);
			else
				return snes_open_bus_r();
		}
		else
			return m_cartslot->read_l(offset);
	}
	return 0xff;    // or open_bus?
}

void snes_console_state::pfest94_lo_w(address_space &space, offs_t offset, uint8_t data)
{
	pfest94_hi_w(space, offset, data);
}


/*************************************
 *
 *  Address maps
 *
 *************************************/

void snes_console_state::snes_map(address_map &map)
{
//  map(0x000000, 0x7dffff).rw(FUNC(snes_console_state::snes20_lo_r), FUNC(snes_console_state::snes20_lo_w));
	map(0x7e0000, 0x7fffff).ram().share("wram");                                     /* 8KB Low RAM, 24KB High RAM, 96KB Expanded RAM */
//  map(0x800000, 0xffffff).rw(FUNC(snes_console_state::snes20_hi_r), FUNC(snes_console_state::snes20_hi_w));
}

void snes_console_state::spc_map(address_map &map)
{
	map(0x0000, 0xffff).ram().share("aram");
}


/*************************************
 *
 *  Input ports
 *
 *************************************/

static INPUT_PORTS_START( snes )
	// input devices go through slot options
	PORT_START("OPTIONS")
	PORT_CONFNAME( 0x01, 0x00, "Hi-Res pixels blurring (TV effect)")
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )

#if SNES_LAYER_DEBUG
	PORT_START("DEBUG1")
	PORT_CONFNAME( 0x03, 0x00, "Select BG1 priority" )
	PORT_CONFSETTING(    0x00, "All" )
	PORT_CONFSETTING(    0x01, "BG1B (lower) only" )
	PORT_CONFSETTING(    0x02, "BG1A (higher) only" )
	PORT_CONFNAME( 0x0c, 0x00, "Select BG2 priority" )
	PORT_CONFSETTING(    0x00, "All" )
	PORT_CONFSETTING(    0x04, "BG2B (lower) only" )
	PORT_CONFSETTING(    0x08, "BG2A (higher) only" )
	PORT_CONFNAME( 0x30, 0x00, "Select BG3 priority" )
	PORT_CONFSETTING(    0x00, "All" )
	PORT_CONFSETTING(    0x10, "BG3B (lower) only" )
	PORT_CONFSETTING(    0x20, "BG3A (higher) only" )
	PORT_CONFNAME( 0xc0, 0x00, "Select BG4 priority" )
	PORT_CONFSETTING(    0x00, "All" )
	PORT_CONFSETTING(    0x40, "BG4B (lower) only" )
	PORT_CONFSETTING(    0x80, "BG4A (higher) only" )

	PORT_START("DEBUG2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Toggle BG 1") PORT_CODE(KEYCODE_1_PAD) PORT_TOGGLE
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Toggle BG 2") PORT_CODE(KEYCODE_2_PAD) PORT_TOGGLE
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Toggle BG 3") PORT_CODE(KEYCODE_3_PAD) PORT_TOGGLE
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Toggle BG 4") PORT_CODE(KEYCODE_4_PAD) PORT_TOGGLE
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Toggle Objects") PORT_CODE(KEYCODE_5_PAD) PORT_TOGGLE
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Toggle Main/Sub") PORT_CODE(KEYCODE_6_PAD) PORT_TOGGLE
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Toggle Color Math") PORT_CODE(KEYCODE_7_PAD) PORT_TOGGLE
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Toggle Windows") PORT_CODE(KEYCODE_8_PAD) PORT_TOGGLE

	PORT_START("DEBUG3")
	PORT_BIT( 0x4, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Toggle Mosaic") PORT_CODE(KEYCODE_9_PAD) PORT_TOGGLE
	PORT_CONFNAME( 0x70, 0x00, "Select OAM priority" )
	PORT_CONFSETTING(    0x00, "All" )
	PORT_CONFSETTING(    0x10, "OAM0 only" )
	PORT_CONFSETTING(    0x20, "OAM1 only" )
	PORT_CONFSETTING(    0x30, "OAM2 only" )
	PORT_CONFSETTING(    0x40, "OAM3 only" )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("DEBUG4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Toggle Mode 0 draw") PORT_TOGGLE
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Toggle Mode 1 draw") PORT_TOGGLE
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Toggle Mode 2 draw") PORT_TOGGLE
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Toggle Mode 3 draw") PORT_TOGGLE
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Toggle Mode 4 draw") PORT_TOGGLE
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Toggle Mode 5 draw") PORT_TOGGLE
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Toggle Mode 6 draw") PORT_TOGGLE
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Toggle Mode 7 draw") PORT_TOGGLE
#endif
INPUT_PORTS_END


/*************************************
 *
 *  Input callbacks
 *
 *************************************/

void snes_console_state::io_read()
{
	// is automatic reading on? if so, read 16bits from oldjoy1/2
	if (SNES_CPU_REG(NMITIMEN) & 1)
	{
		uint16_t joy1 = 0, joy2 = 0, joy3 = 0, joy4 = 0;
		m_ctrl1->port_poll();
		m_ctrl2->port_poll();

		for (int i = 0; i < 16; i++)
		{
			joy1 |= ((m_ctrl1->read_pin4() & 1) << (15 - i));
			joy2 |= ((m_ctrl2->read_pin4() & 1) << (15 - i));
			joy3 |= ((m_ctrl1->read_pin5() & 1) << (15 - i));
			joy4 |= ((m_ctrl2->read_pin5() & 1) << (15 - i));
		}

		SNES_CPU_REG(JOY1L) = (joy1 & 0x00ff) >> 0;
		SNES_CPU_REG(JOY1H) = (joy1 & 0xff00) >> 8;
		SNES_CPU_REG(JOY2L) = (joy2 & 0x00ff) >> 0;
		SNES_CPU_REG(JOY2H) = (joy2 & 0xff00) >> 8;
		SNES_CPU_REG(JOY3L) = (joy3 & 0x00ff) >> 0;
		SNES_CPU_REG(JOY3H) = (joy3 & 0xff00) >> 8;
		SNES_CPU_REG(JOY4L) = (joy4 & 0x00ff) >> 0;
		SNES_CPU_REG(JOY4H) = (joy4 & 0xff00) >> 8;
	}
}

uint8_t snes_console_state::oldjoy1_read(int latched)
{
	uint8_t ret = 0;
	ret |= m_ctrl1->read_pin4();
	ret |= (m_ctrl1->read_pin5() << 1);
	return ret;
}

uint8_t snes_console_state::oldjoy2_read(int latched)
{
	uint8_t ret = 0;
	ret |= m_ctrl2->read_pin4();
	ret |= (m_ctrl2->read_pin5() << 1);
	return ret;
}

void snes_console_state::write_joy_latch(uint8_t data)
{
	m_ctrl1->write_strobe(data);
	m_ctrl2->write_strobe(data);
}

void snes_console_state::wrio_write(uint8_t data)
{
	if (!(SNES_CPU_REG(WRIO) & 0x80) && (data & 0x80))
	{
		// external latch
		m_ppu->set_latch_hv(m_ppu->current_x(), m_ppu->current_y());
	}

	m_ctrl1->write_pin6(BIT(data, 6));
	m_ctrl2->write_pin6(BIT(data, 7));

}

SNESCTRL_GUNLATCH_CB(snes_console_state::gun_latch_cb)
{
	// these are the theoretical boundaries
	if (x < 0)
		x = 0;
	if (x > (SNES_SCR_WIDTH - 1))
		x = SNES_SCR_WIDTH - 1;

	if (y < 0)
		y = 0;
	if (y > (m_ppu->last_visible_line() - 1))
		y = m_ppu->last_visible_line() - 1;

//  m_ppu->set_latch_hv(x, y);  // it would be more accurate to write twice to WRIO register, first with bit7 = 0 and then with bit7 = 1
	m_ppu->set_latch_hv(m_ppu->current_x(), m_ppu->current_y());
}

SNESCTRL_ONSCREEN_CB(snes_console_state::onscreen_cb)
{
	// these are the theoretical boundaries, but we currently are always onscreen due to the
	// way IPT_LIGHTGUNs work... investigate more on this!
	if (x < 0 || x >= SNES_SCR_WIDTH || y < 0 || y >= m_ppu->last_visible_line())
		return false;
	else
		return true;
}


/*************************************
 *
 *  Machine driver
 *
 *************************************/

void snes_console_state::machine_start()
{
	snes_state::machine_start();

	if (m_cartslot && m_cartslot->exists())
	{
		m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x000000, 0x7dffff, read8m_delegate(*this, FUNC(snes_console_state::snes20_lo_r)), write8m_delegate(*this, FUNC(snes_console_state::snes20_lo_w)));
		m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x800000, 0xffffff, read8m_delegate(*this, FUNC(snes_console_state::snes20_hi_r)), write8m_delegate(*this, FUNC(snes_console_state::snes20_hi_w)));
		m_maincpu->set_5a22_map();

		m_type = m_cartslot->get_type();

		switch (m_type)
		{
			// LoROM & LoROM + addons
			case SNES_MODE20:
			case SNES_BSXLO:
			case SNES_SUFAMITURBO:
			case SNES_CX4:      // this still uses the old simulation instead of emulating the CPU
			case SNES_ST010:    // this requires two diff kinds of chip access, so we handle it in snes20_lo/hi_r/w
			case SNES_ST011:    // this requires two diff kinds of chip access, so we handle it in snes20_lo/hi_r/w
				break;
			case SNES_ST018:
				m_maincpu->space(AS_PROGRAM).install_read_handler(0x003800, 0x0038ff, 0, 0xbf0000, 0, read8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_read)));
				m_maincpu->space(AS_PROGRAM).install_write_handler(0x003800, 0x0038ff, 0, 0xbf0000, 0, write8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_write)));
				break;
			case SNES_Z80GB:      // skeleton support
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x000000, 0x7dffff, read8m_delegate(*this, FUNC(snes_console_state::snessgb_lo_r)), write8m_delegate(*this, FUNC(snes_console_state::snessgb_lo_w)));
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x800000, 0xffffff, read8m_delegate(*this, FUNC(snes_console_state::snessgb_hi_r)), write8m_delegate(*this, FUNC(snes_console_state::snessgb_hi_w)));
				m_maincpu->set_5a22_map();
				break;
			case SNES_SA1:      // skeleton support
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x000000, 0x7dffff, read8m_delegate(*this, FUNC(snes_console_state::snessa1_lo_r)), write8m_delegate(*this, FUNC(snes_console_state::snessa1_lo_w)));
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x800000, 0xffffff, read8m_delegate(*this, FUNC(snes_console_state::snessa1_hi_r)), write8m_delegate(*this, FUNC(snes_console_state::snessa1_hi_w)));
				m_maincpu->set_5a22_map();
				break;
			case SNES_DSP:
				m_maincpu->space(AS_PROGRAM).install_read_handler(0x208000, 0x20ffff, 0, 0x9f0000, 0, read8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_read)));
				m_maincpu->space(AS_PROGRAM).install_write_handler(0x208000, 0x20ffff, 0, 0x9f0000, 0, write8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_write)));
				break;
			case SNES_DSP_2MB:
				m_maincpu->space(AS_PROGRAM).install_read_handler(0x600000, 0x607fff, 0, 0x8f0000, 0, read8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_read)));
				m_maincpu->space(AS_PROGRAM).install_write_handler(0x600000, 0x607fff, 0, 0x8f0000, 0, write8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_write)));
				break;
			case SNES_DSP4:
				m_maincpu->space(AS_PROGRAM).install_read_handler(0x308000, 0x30ffff, 0, 0x8f0000, 0, read8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_read)));
				m_maincpu->space(AS_PROGRAM).install_write_handler(0x308000, 0x30ffff, 0, 0x8f0000, 0, write8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_write)));
				break;
			case SNES_OBC1:
				m_maincpu->space(AS_PROGRAM).install_read_handler(0x006000, 0x007fff, 0, 0xbf0000, 0, read8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_read)));
				m_maincpu->space(AS_PROGRAM).install_write_handler(0x006000, 0x007fff, 0, 0xbf0000, 0, write8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_write)));
				break;
			case SNES_GSU1:
			case SNES_GSU2:
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x000000, 0x7dffff, read8m_delegate(*this, FUNC(snes_console_state::snessfx_lo_r)), write8m_delegate(*this, FUNC(snes_console_state::snessfx_lo_w)));
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x800000, 0xffffff, read8m_delegate(*this, FUNC(snes_console_state::snessfx_hi_r)), write8m_delegate(*this, FUNC(snes_console_state::snessfx_hi_w)));
				m_maincpu->set_5a22_map();
				break;
			case SNES_SDD1:
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x000000, 0x7dffff, read8m_delegate(*this, FUNC(snes_console_state::snessdd1_lo_r)), write8m_delegate(*this, FUNC(snes_console_state::snessdd1_lo_w)));
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x800000, 0xffffff, read8m_delegate(*this, FUNC(snes_console_state::snessdd1_hi_r)), write8m_delegate(*this, FUNC(snes_console_state::snessdd1_hi_w)));
				m_maincpu->set_5a22_map();
				break;
			case SNES_BSX:
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x000000, 0x7dffff, read8m_delegate(*this, FUNC(snes_console_state::snesbsx_lo_r)), write8m_delegate(*this, FUNC(snes_console_state::snesbsx_lo_w)));
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x800000, 0xffffff, read8m_delegate(*this, FUNC(snes_console_state::snesbsx_hi_r)), write8m_delegate(*this, FUNC(snes_console_state::snesbsx_hi_w)));
				m_maincpu->set_5a22_map();
				break;
			// HiROM & HiROM + addons
			case SNES_MODE21:
			case SNES_BSXHI:
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x000000, 0x7dffff, read8m_delegate(*this, FUNC(snes_console_state::snes21_lo_r)), write8m_delegate(*this, FUNC(snes_console_state::snes21_lo_w)));
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x800000, 0xffffff, read8m_delegate(*this, FUNC(snes_console_state::snes21_hi_r)), write8m_delegate(*this, FUNC(snes_console_state::snes21_hi_w)));
				m_maincpu->set_5a22_map();
				break;
			case SNES_DSP_MODE21:
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x000000, 0x7dffff, read8m_delegate(*this, FUNC(snes_console_state::snes21_lo_r)), write8m_delegate(*this, FUNC(snes_console_state::snes21_lo_w)));
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x800000, 0xffffff, read8m_delegate(*this, FUNC(snes_console_state::snes21_hi_r)), write8m_delegate(*this, FUNC(snes_console_state::snes21_hi_w)));
				m_maincpu->space(AS_PROGRAM).install_read_handler(0x006000, 0x007fff, 0, 0x9f0000, 0, read8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_read)));
				m_maincpu->space(AS_PROGRAM).install_write_handler(0x006000, 0x007fff, 0, 0x9f0000, 0, write8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_write)));
				m_maincpu->set_5a22_map();
				break;
			case SNES_SRTC:
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x000000, 0x7dffff, read8m_delegate(*this, FUNC(snes_console_state::snes21_lo_r)), write8m_delegate(*this, FUNC(snes_console_state::snes21_lo_w)));
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x800000, 0xffffff, read8m_delegate(*this, FUNC(snes_console_state::snes21_hi_r)), write8m_delegate(*this, FUNC(snes_console_state::snes21_hi_w)));
				m_maincpu->space(AS_PROGRAM).install_read_handler(0x002800, 0x002800, 0, 0xbf0000, 0, read8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_read)));
				m_maincpu->space(AS_PROGRAM).install_write_handler(0x002801, 0x002801, 0, 0xbf0000, 0, write8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_write)));
				m_maincpu->set_5a22_map();
				break;
			case SNES_SPC7110:
			case SNES_SPC7110_RTC:
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x000000, 0x7dffff, read8m_delegate(*this, FUNC(snes_console_state::snes7110_lo_r)), write8m_delegate(*this, FUNC(snes_console_state::snes7110_lo_w)));
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x800000, 0xffffff, read8m_delegate(*this, FUNC(snes_console_state::snes7110_hi_r)), write8m_delegate(*this, FUNC(snes_console_state::snes7110_hi_w)));
				m_maincpu->set_5a22_map();
				break;
			case SNES_PFEST94:
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x000000, 0x7dffff, read8m_delegate(*this, FUNC(snes_console_state::pfest94_lo_r)), write8m_delegate(*this, FUNC(snes_console_state::pfest94_lo_w)));
				m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x800000, 0xffffff, read8m_delegate(*this, FUNC(snes_console_state::pfest94_hi_r)), write8m_delegate(*this, FUNC(snes_console_state::pfest94_hi_w)));
				m_maincpu->set_5a22_map();
				break;
			// pirate 'mappers'
			case SNES_POKEMON:
				m_maincpu->space(AS_PROGRAM).install_read_handler(0x800000, 0x80ffff, 0, 0x780000, 0, read8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_read)));
				m_maincpu->space(AS_PROGRAM).install_write_handler(0x800000, 0x80ffff, 0, 0x780000, 0, write8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_write)));
				break;
			case SNES_TEKKEN2:
				m_maincpu->space(AS_PROGRAM).install_read_handler(0x808000, 0x8087ff, 0, 0x3f0000, 0, read8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_read)));
				m_maincpu->space(AS_PROGRAM).install_write_handler(0x808000, 0x8087ff, 0, 0x3f0000, 0, write8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_write)));
				break;
			case SNES_MCPIR1:
			case SNES_MCPIR2:
				m_maincpu->space(AS_PROGRAM).install_write_handler(0xffff00, 0xffffff, write8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_write)));
				break;
			case SNES_20COL:
				m_maincpu->space(AS_PROGRAM).install_write_handler(0x008000, 0x008fff, write8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_write)));
				break;
			case SNES_SOULBLAD:
				// reads from xxx0-xxx3in range [80-bf] return a fixed sequence of 4bits; reads in range [c0-ff] return open bus
				m_maincpu->space(AS_PROGRAM).install_read_handler(0x808000, 0x808003, 0, 0x3f7ffc, 0, read8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_read)));
				m_maincpu->space(AS_PROGRAM).install_read_handler(0xc00000, 0xffffff, read8smo_delegate(*this, FUNC(snes_console_state::snes_open_bus_r)));
				break;
			case SNES_BUGS:
			case SNES_BANANA:
//              m_maincpu->space(AS_PROGRAM).install_read_handler(0x808000, 0x80ffff, 0, 0x780000, 0, read8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_read)));
//              m_maincpu->space(AS_PROGRAM).install_write_handler(0x808000, 0x80ffff, 0, 0x780000, 0, write8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_write)));
//              m_maincpu->set_5a22_map();
				break;
		}
		// install memory access taps (send address pin to cartridge slot)
		m_maincpu->space(AS_PROGRAM).install_readwrite_tap
		(
			0x000000, 0xffffff, "cartslot_tap",
			[this](offs_t offset, u8 &, u8)
			{
				m_cartslot->set_address(offset);
			},
			[this](offs_t offset, u8 &, u8)
			{
				m_cartslot->set_address(offset);
			}
		);
		m_cartslot->save_ram();
	}
}

void snes_console_state::machine_reset()
{
	snes_state::machine_reset();
}


void snes_console_state::snes(machine_config &config)
{
	/* basic machine hardware */
	_5A22(config, m_maincpu, MCLK_NTSC);   // Nintendo S-CPU 5A22-0x, 21.477272MHz / (6, 8, 12) = 1.79 MHz, 2.68 MHz, also 3.58 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &snes_console_state::snes_map);

	// runs at 24.576 MHz / 12 = 2.048 MHz
	S_SMP(config, m_soundcpu, XTAL(24'576'000) / 12);
	m_soundcpu->set_addrmap(AS_DATA, &snes_console_state::spc_map);
	m_soundcpu->dsp_io_read_callback().set(m_s_dsp, FUNC(s_dsp_device::dsp_io_r));
	m_soundcpu->dsp_io_write_callback().set(m_s_dsp, FUNC(s_dsp_device::dsp_io_w));

	//config.set_maximum_quantum(attotime::from_hz(48000));
	config.set_perfect_quantum(m_maincpu);

	INPUT_MERGER_ANY_HIGH(config, m_scpu_irq);
	m_scpu_irq->output_handler().set_inputline(m_maincpu, G65816_LINE_IRQ);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(DOTCLK_NTSC * 2, SNES_HTOTAL * 2, 0, SNES_SCR_WIDTH * 2, SNES_VTOTAL_NTSC, 0, SNES_SCR_HEIGHT_NTSC);
	m_screen->set_video_attributes(VIDEO_VARIABLE_WIDTH);
	m_screen->set_screen_update(FUNC(snes_state::screen_update));

	SNES_PPU(config, m_ppu, MCLK_NTSC);
	m_ppu->open_bus_callback().set(FUNC(snes_console_state::snes_open_bus_r));
	m_ppu->set_screen("screen");

	SNES_CONTROL_PORT(config, m_ctrl1, snes_control_port_devices, "joypad");
	m_ctrl1->set_onscreen_callback(FUNC(snes_console_state::onscreen_cb));
	SNES_CONTROL_PORT(config, m_ctrl2, snes_control_port_devices, "joypad");
	m_ctrl2->set_onscreen_callback(FUNC(snes_console_state::onscreen_cb));
	m_ctrl2->set_gunlatch_callback(FUNC(snes_console_state::gun_latch_cb));

	/* sound hardware */
	SPEAKER(config, "speaker", 2).front();

	S_DSP(config, m_s_dsp, XTAL(24'576'000));
	m_s_dsp->set_addrmap(0, &snes_console_state::spc_map);
	m_s_dsp->add_route(0, "speaker", 1.00, 0);
	m_s_dsp->add_route(1, "speaker", 1.00, 1);

	SNS_CART_SLOT(config, m_cartslot, MCLK_NTSC, snes_cart, nullptr);
	m_cartslot->set_must_be_loaded(true);
	m_cartslot->irq_callback().set(m_scpu_irq, FUNC(input_merger_device::in_w<1>)); // FIXME: conflict with video interrupt, it should be ORed
	m_cartslot->open_bus_callback().set(FUNC(snes_console_state::snes_open_bus_r));
	m_cartslot->set_scanlines(SNES_VTOTAL_NTSC);

	SOFTWARE_LIST(config, "cart_list").set_original("snes");
	SOFTWARE_LIST(config, "bsx_list").set_original("snes_bspack");
	SOFTWARE_LIST(config, "st_list").set_original("snes_strom");
}

void snes_console_state::snespal(machine_config &config)
{
	snes(config);
	m_maincpu->set_clock(MCLK_PAL);

	m_screen->set_raw(DOTCLK_PAL * 2, SNES_HTOTAL * 2, 0, SNES_SCR_WIDTH * 2, SNES_VTOTAL_PAL, 0, SNES_SCR_HEIGHT_PAL);

	m_ppu->set_clock(MCLK_PAL);
	m_cartslot->set_clock(MCLK_PAL);
	m_cartslot->set_scanlines(SNES_VTOTAL_PAL);
}



/*************************************
 *
 *  ROM definition(s)
 *
 *************************************/

ROM_START( snes )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
ROM_END

#define rom_snespal rom_snes

/*************************************
 *
 *  Game driver(s)
 *
 *************************************/

/*    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT  CLASS               INIT        COMPANY     FULLNAME                                      FLAGS */
CONS( 1989, snes,    0,      0,      snes,    snes,  snes_console_state, empty_init, "Nintendo", "Super Nintendo Entertainment System / Super Famicom (NTSC)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
CONS( 1991, snespal, snes,   0,      snespal, snes,  snes_console_state, empty_init, "Nintendo", "Super Nintendo Entertainment System (PAL)",  MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )



snspell.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap, Jonathan Gevaryahu
// thanks-to:Sean Riddle, David Viens, Kevin Horton
/*******************************************************************************

Texas Instruments 1st-gen. Speak & Spell family

These devices, mostly edu-toys, are based around an MCU(TMS0270), TMS51xx speech,
and VSM ROM(s). Newer devices, such as Speak & Music, are based around the
TMP50C40 and belong in another driver, probably.

MAME external artwork is not required. But it is objectively a large improvement.
For the 'Compact' series, see the snspellc driver.

TODO:
- why doesn't lantransp work? maybe incompatible with released modules?

================================================================================

Known devices on this hardware: (* denotes not dumped)

Speak & Spell:

This is the original Speak & Spell. TI had done educational toys before, like
Wiz-A-Tron or Little Professor. But the popularity of this product was much
above expectations. TI continued to manufacture many products for this line.

Speak & Spell (US), 1978
- MCU: TMC0271, label TMC0271NL DBS (die label: T0270B, 0271B)
- TMS51xx: TMC0281
- VSM(1/2): 16KB TMC0351NL
- VSM(2/2): 16KB TMC0352NL
- VFD: NEC FIP8A5AR no. 3A
- notes: keyboard has buttons instead of cheap membrane

Speak & Spell (US), 1980
- MCU: TMC0271, label TMC0271H-N2L FDS (die label: T0270D, 0271H)
- TMS51xx: TMC0281
- VSM(1/2): 16KB TMC0351N2L
- VSM(2/2): 16KB TMC0352N2L
- notes: fixed a funny bug with gibberish-talk when Module button is pressed
  with no module inserted, MCU ROM contents differs from 1978 version

Speak & Spell (US), 1981
- MCU: same as 1980 version
- TMS51xx: TMC0281D
- VSM: 16KB CD2350A
- notes: only 1 VSM, meaning much smaller internal vocabulary

Speak & Spell (Japan), 1980
- MCU: TMC0271 (assume same as US 1980 version)
- TMS51xx: TMC0281
- VSM(1/2): 16KB CD2321
- VSM(2/2): 16KB CD2322
- notes: no local name for the product, words are in English but very low difficulty

Speak & Spell (UK), 1978
- MCU: TMC0271 (assume same as US 1978 version)
- TMS51xx: TMC0281
- VSM(1/2): 16KB CD2303
- VSM(2/2): 16KB CD2304
- notes: voice data was manually altered to give it a UK accent,
  here's a small anecdote from developer:
  "(...) I cannot bear to listen the product even now. I remember the
   word 'butcher' took 3 days - I still don't know if it sounds right."

Speak & Spell (UK), 1981
- MCU: TMC0271 (assume same as US 1980 version)
- TMS51xx: CD2801
- VSM: 16KB CD62175
- VFD: some seen with the one from Speak & Math(!)
- notes: this one has a dedicated voice actor

Speak & Spell (Spanish, prototype), 1980
- MCU: CD2701N2L P (die label: T0270D, 2701)
- TMS51xx: TMC0281 (die label: T0280A 0281)
- VSM(1/2): 16KB CD2319
- VSM(2/2): 16KB CD2320
- VFD: 8 digits with 14 segments, DP and accent mark

Speak & Spell (France) "La Dictée Magique", 1981
- MCU: CD2702, label CD2702AN2L (die label: TMC0270F, 2702A)
- TMS51xx: CD2801
- VSM: 16KB CD2352

Speak & Spell (Germany) "Buddy", 1980 (stylized as "buddy")
- MCU & TMS51xx: same as French version
- VSM(1/2): 16KB CD2345*
- VSM(2/2): 16KB CD2346*
- VFD: has umlaut instead of apostrophe

Speak & Spell (Italy) "Grillo Parlante", 1983
- MCU & TMS51xx: same as French version
- VSM: 16KB CD62190
- VFD: same as Speak & Math

Speak & Spell modules:
Note that they are interchangeable, eg. you can use a French module on a US Speak & Spell.

English:
- Vowel Power: VSM: 16KB CD2302
- Number Stumpers 4-6: VSM: 16KB CD2305
- Number Stumpers 7-8: VSM: 16KB CD2307A
- Basic Builders: VSM: 16KB CD2308
- Mighty Verbs: VSM: 16KB CD2309(rev.B)
- Homonym Heroes: VSM: 16KB CD2310
- Vowel Ventures: VSM: 16KB CD2347(rev.C)
- Noun Endings: VSM: 16KB CD2348
- Magnificent Modifiers: VSM: 16KB CD2349
- E.T. Fantasy: VSM: 16KB CD2360

French:
- No.1: Les Mots de Base: VSM: 16KB CD2353 (1st release was called "Module No. 1 de Jacques Capelovici")
- No.2: Les Mots Difficiles (aka Les Mots de Base): VSM: 16KB CD62177A
- No.3: Les Animaux Familiers: VSM: 16KB CD62047*
- No.4: Les Magasins de la Rue: VSM: 16KB CD62048
- No.5: Les Extra-Terrestres: VSM: 16KB CD62178*

Italian:
- Super Modulo: VSM: 16KB? CD62313*

================================================================================

Speak & Math:

Speak & Math (US), 1980 (renamed to "Speak & Maths" in UK, but is the same product)
- MCU: CD2704, label CD2704B-N2L (die label: TMC0270F, 2704B) - 2nd revision?(mid-1982)
- TMS51xx: CD2801
- VSM(1/2): 16KB CD2392
- VSM(2/2): 16KB CD2393
- VFD: Futaba 9SY -02Z 7E
- notes: As with the Speak & Spell, the voice actor was a radio announcer.
  However, the phrase "is greater than or less than" had to be added by one
  of the TI employees in a hurry, the day before a demo. Apparently QA
  never found out and it ended up in the final product.

Speak & Math (US), 1984
- MCU: CD2708, label CD2708N2L (die label: TMC0270F, 2708A)
- TMS51xx: CD2801
- VSM(1/2): 16KB CD2381
- VSM(2/2): 4KB CD2614

================================================================================

Speak & Read:

Speak & Read (US), 1980
- MCU: CD2705, label CD2705B-N2L (die label: TMC0270E, 2705B) - 2nd revision?(late-1981)
- TMS51xx: CD2801
- VSM(1/2): 16KB CD2394A
- VSM(2/2): 16KB CD2395A
- VFD: same as Language Translator, rightmost digit unused

Speak & Read modules:

English:
- Sea Sights: VSM: 16KB CD2396A
- Who's Who at the Zoo: VSM: 16KB CD2397
- A Dog on a Log: VSM: 16KB CD3534A
- The Seal That Could Fly: VSM: 16KB CD3535
- A Ghost in the House: VSM: 16KB CD3536
- On the Track: VSM: 16KB CD3538
- The Third Circle: VSM: 16KB CD3539
- The Millionth Knight: VSM: 16KB CD3540

================================================================================

Language Translator/Tutor:

Initially sold as Language Translator, renamed to Language Tutor a year later.
It was rebranded from translator to a 'language aid'.

Language Translator (US), 1979
- MCU: TMC0275 (die label: T0270D, 0275B)
- TMS51xx: CD2801 (die label: T0280B 2801)
- VFD: Itron FG106A2
- notes: external module is required (see below)

Language Tutor (US), 1980
- notes: identical hardware, module stickers differ but have same VSMs

Language Translator modules:

- Ingles(1/4): VSM: 16KB CD2311
- Ingles(2/4): VSM: 16KB CD2312
- Ingles(3/4): VSM: 16KB CD2313
- Ingles(4/4): VSM: 16KB CD2314

- Spanish(1/4): VSM: 16KB CD2315
- Spanish(2/4): VSM: 16KB CD2316
- Spanish(3/4): VSM: 16KB CD2317
- Spanish(4/4): VSM: 16KB CD2318

- French(1/4): VSM: 16KB CD2327
- French(2/4): VSM: 16KB CD2328
- French(3/4): VSM: 16KB CD2329
- French(4/4): VSM: 16KB CD2330

- German(1/4): VSM: 16KB CD2331
- German(2/4): VSM: 16KB CD2332
- German(3/4): VSM: 16KB CD2333
- German(4/4): VSM: 16KB CD2334

- English(1/4): VSM: 16KB CD3526
- English(2/4): VSM: 16KB CD3527
- English(3/4): VSM: 16KB CD3528
- English(4/4): VSM: 16KB CD3529

Some other language modules were announced by TI, but not released.

================================================================================

Language Teacher:

A cost-reduced version of the translator. Similar PCB, the speaker was removed.
Modules require less ROMs due to missing speech data. Translator/Teacher modules
are not interchangeable.

Language Teacher (US), 1980
- MCU: TMC0270N2LP CD2706 (die label: T0270D, 2706)
- other: see Language Translator

Language Teacher modules (only 1 known released):

- German For Travel: VSM: 16KB CD3509

*******************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/tms1000/tms0270.h"
#include "machine/tms6100.h"
#include "sound/tms5110.h"
#include "video/pwm.h"

#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "snmath.lh"
#include "snread.lh"
#include "snspell.lh"
#include "snspellsp.lh"


namespace {

// The master clock is a single stage RC oscillator into TMS5100 RCOSC:
// In an early 1979 Speak & Spell, C is 68pf, R is a 50kohm trimpot which is set to around 33.6kohm
// (measured in-circuit). CPUCLK is this osc freq /2, ROMCLK is this osc freq /4.
// The typical osc freq curve for TMS5100 is unknown. Let's assume it is set to the default frequency,
// which is 640kHz for 8KHz according to the TMS5100 documentation.

static constexpr u32 MASTER_CLOCK = 640'000;


class snspell_state : public driver_device
{
public:
	snspell_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_tms5100(*this, "tms5100"),
		m_tms6100(*this, "tms6100"),
		m_cart(*this, "cartslot"),
		m_softlist(*this, "cart_list"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void init_snspell();
	void init_lantrans();

	// machine configs
	void tms5110_route(machine_config &config);
	void sns_tmc0281(machine_config &config);
	void sns_tmc0281d(machine_config &config);
	void sns_cd2801(machine_config &config);
	void snspellit(machine_config &config);
	void snspellsp(machine_config &config);
	void snmath(machine_config &config);
	void snread(machine_config &config);
	void lantrans(machine_config &config);
	void lanteach(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(power_on);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices
	required_device<tms0270_cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<tms5110_device> m_tms5100;
	required_device<tms6100_device> m_tms6100;
	optional_device<generic_slot_device> m_cart;
	optional_device<software_list_device> m_softlist;
	required_ioport_array<9> m_inputs;

	u32 m_cart_max_size = 0;
	u8 *m_cart_base = nullptr;

	bool m_power_on = false;
	u32 m_r = 0;
	u16 m_grid = 0;
	u16 m_plate = 0;

	void power_off();
	void update_display();

	u8 read_k();
	void write_o(u16 data);
	void write_r(u32 data);

	void snmath_write_o(u16 data);
	void lantrans_write_r(u32 data);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
};

void snspell_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_power_on));
	save_item(NAME(m_r));
	save_item(NAME(m_grid));
	save_item(NAME(m_plate));
}



/*******************************************************************************
    Power
*******************************************************************************/

void snspell_state::machine_reset()
{
	m_power_on = true;
	m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
}

INPUT_CHANGED_MEMBER(snspell_state::power_on)
{
	if (newval && !m_power_on)
		machine_reset();
}

void snspell_state::power_off()
{
	m_power_on = false;
	m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
	m_display->clear();
}



/*******************************************************************************
    Cartridge
*******************************************************************************/

DEVICE_IMAGE_LOAD_MEMBER(snspell_state::cart_load)
{
	u32 const size = m_cart->common_get_size("rom");

	if (size > m_cart_max_size)
	{
		return std::make_pair(
				image_error::INVALIDLENGTH,
				util::string_format("Invalid file size (must be no more than %u bytes)", m_cart_max_size));
	}

	m_cart->common_load_rom(m_cart_base, size, "rom");
	return std::make_pair(std::error_condition(), std::string());
}

void snspell_state::init_snspell()
{
	m_cart_max_size = 0x4000;
	m_cart_base = memregion("tms6100")->base() + 0x8000;
}

void snspell_state::init_lantrans()
{
	m_cart_max_size = 0x10000;
	m_cart_base = memregion("tms6100")->base();
}



/*******************************************************************************
    I/O
*******************************************************************************/

// common/snspell

void snspell_state::update_display()
{
	u16 gridmask = m_display->row_on(15) ? 0xffff : 0x8000;
	m_display->matrix(m_grid & gridmask, m_plate);
}

void snspell_state::write_r(u32 data)
{
	// R0-R7: input mux and select digit (+R8 if the device has 9 digits)
	// R15: filament on
	// other bits: MCU internal use
	m_grid = data & 0x81ff;
	update_display();

	// R13: power-off request, on falling edge
	if (~data & m_r & 0x2000)
		power_off();

	m_r = data;
}

void snspell_state::write_o(u16 data)
{
	// reorder opla to led14seg, plus DP as d14 and AP as d15:
	// note: lantrans and snread VFD has an accent triangle instead of DP, and no AP
	// E,D,C,G,B,A,I,M,L,K,N,J,[AP],H,F,[DP] (sidenote: TI KLMN = MAME MLNK)
	m_plate = bitswap<16>(data,12,15,10,7,8,9,11,6,13,3,14,0,1,2,4,5);
	update_display();
}

u8 snspell_state::read_k()
{
	u8 data = 0;

	// K: multiplexed inputs
	for (int i = 0; i < 8; i++)
		if (BIT(m_r, i))
			data |= m_inputs[i]->read();

	// Vss row is always on
	return data | m_inputs[8]->read();
}


// snmath specific

void snspell_state::snmath_write_o(u16 data)
{
	// reorder opla to led14seg, plus DP as d14 and CT as d15:
	// [DP],D,C,H,F,B,I,M,L,K,N,J,[CT],E,G,A (sidenote: TI KLMN = MAME MLNK)
	m_plate = bitswap<16>(data,12,0,10,7,8,9,11,6,3,14,4,13,1,2,5,15);
	update_display();
}


// lantrans specific

void snspell_state::lantrans_write_r(u32 data)
{
	// same as default, except R13 is used for an extra digit instead of power-off
	m_r = data;
	m_grid = data & 0xa1ff;
	update_display();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( snspell )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_NAME("Module Select")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_NAME("Erase")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("Enter")

	PORT_START("IN.6") // R6
	PORT_BIT( 0x1f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.7") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_NAME("Off") // -> auto_power_off
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_HOME) PORT_NAME("Go")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_NAME("Replay")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_NAME("Repeat")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_NAME("Clue")

	PORT_START("IN.8") // Vss
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_NAME("Mystery Word")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_NAME("Secret Code")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_NAME("Letter")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_NAME("Say It")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_F1) PORT_NAME("Spell/On") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(snspell_state::power_on), 0)
INPUT_PORTS_END

static INPUT_PORTS_START( snspellfr ) // French button names
	PORT_INCLUDE( snspell )

	PORT_MODIFY("IN.5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_NAME("Module 2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_NAME("Efface")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("Essaie")

	PORT_MODIFY("IN.7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_NAME(u8"Arrét") // -> auto_power_off
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_HOME) PORT_NAME(u8"Départ")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_NAME("Rejoue")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_NAME("Repete")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_NAME("Aide")

	PORT_MODIFY("IN.8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_NAME("Mot Mystere")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_NAME("Code Secret")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_NAME("Lettre")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_NAME("Dis-le")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_F1) PORT_NAME("Epelle/Marche") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(snspell_state::power_on), 0)
INPUT_PORTS_END

static INPUT_PORTS_START( snspellit ) // Italian button names
	PORT_INCLUDE( snspell )

	PORT_MODIFY("IN.5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_NAME("Moduli")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_NAME("Cancella")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("Controllo")

	PORT_MODIFY("IN.7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_NAME("Stop") // -> auto_power_off
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_HOME) PORT_NAME("Via")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_NAME("Ritorno")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_NAME("Replica")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_NAME("Aiuto")

	PORT_MODIFY("IN.8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_NAME("Indovina")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_NAME("Codice")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_NAME("Alfabeto")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_NAME("Ripeti")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_F1) PORT_NAME("Scrivi") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(snspell_state::power_on), 0)
INPUT_PORTS_END

static INPUT_PORTS_START( snspellsp ) // Spanish button names, different alphabet
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_NAME("CH")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_NAME("LL")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(U'Ñ')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("RR")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("Entra")

	PORT_START("IN.6") // R6
	PORT_BIT( 0x1f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.7") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_NAME("Acento")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_HOME) PORT_NAME("Listo")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_NAME("Programa")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_NAME("Borra")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_NAME("Repite")

	PORT_START("IN.8") // Vss
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_NAME("Otra Vez")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_NAME("Palabra Secreta")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_NAME("Dilo")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_NAME("Off") // -> auto_power_off
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_F1) PORT_NAME("Deletrea/On") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(snspell_state::power_on), 0)
INPUT_PORTS_END


static INPUT_PORTS_START( snmath )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_CODE(KEYCODE_HOME) PORT_NAME("Go")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F2) PORT_NAME("Off") // -> auto_power_off
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Clear")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_COMMA) PORT_NAME("<")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_NAME(">")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("Repeat")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("Mix It")

	PORT_START("IN.6") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_I) PORT_NAME("Number Stumper")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_U) PORT_NAME("Write It")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Y) PORT_NAME("Greater/Less")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Word Problems")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_F1) PORT_NAME("Solve It/On") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(snspell_state::power_on), 0)

	PORT_START("IN.7")
	PORT_BIT( 0x1f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.8")
	PORT_BIT( 0x1f, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


static INPUT_PORTS_START( snread )
	PORT_INCLUDE( snspell )

	PORT_MODIFY("IN.7")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_NAME("Word Zapper")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_NAME("Word Maker")

	PORT_MODIFY("IN.8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_NAME("Read It")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_NAME("Picture Read")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_NAME("Letter Stumper")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_NAME("Hear It")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_F1) PORT_NAME("Word Zap/On") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(snspell_state::power_on), 0)
INPUT_PORTS_END


static INPUT_PORTS_START( lantrans )
	PORT_INCLUDE( snspell )

	PORT_MODIFY("IN.5")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("Diacritical")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_NAME("Space")

	PORT_MODIFY("IN.6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_NAME("4")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_NAME("5")

	PORT_MODIFY("IN.7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_NAME("6")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_NAME("7")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_NAME("8")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_NAME("9")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_NAME("0")

	PORT_MODIFY("IN.8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_NAME("Translate")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_NAME("Learn")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_NAME("Phrase")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("Link")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_NAME("Repeat")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void snspell_state::tms5110_route(machine_config &config)
{
	// sound hardware
	m_tms5100->m0().set(m_tms6100, FUNC(tms6100_device::m0_w));
	m_tms5100->m1().set(m_tms6100, FUNC(tms6100_device::m1_w));
	m_tms5100->addr().set(m_tms6100, FUNC(tms6100_device::add_w));
	m_tms5100->data().set(m_tms6100, FUNC(tms6100_device::data_line_r));
	m_tms5100->romclk().set(m_tms6100, FUNC(tms6100_device::clk_w));
	m_tms5100->add_route(ALL_OUTPUTS, "mono", 0.5);
}

void snspell_state::sns_tmc0281(machine_config &config)
{
	// basic machine hardware
	TMS0270(config, m_maincpu, MASTER_CLOCK/2);
	m_maincpu->read_k().set(FUNC(snspell_state::read_k));
	m_maincpu->write_o().set(FUNC(snspell_state::write_o));
	m_maincpu->write_r().set(FUNC(snspell_state::write_r));

	m_maincpu->read_ctl().set("tms5100", FUNC(tms5110_device::ctl_r));
	m_maincpu->write_ctl().set("tms5100", FUNC(tms5110_device::ctl_w));
	m_maincpu->write_pdc().set("tms5100", FUNC(tms5110_device::pdc_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(16, 16);
	m_display->set_segmask(0x21ff, 0x3fff);
	config.set_default_layout(layout_snspell);

	// sound hardware
	TMS6100(config, m_tms6100, MASTER_CLOCK/4);

	SPEAKER(config, "mono").front_center();
	TMC0281(config, m_tms5100, MASTER_CLOCK);
	tms5110_route(config);

	// cartridge
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "snspell", "vsm,bin");
	m_cart->set_device_load(FUNC(snspell_state::cart_load));

	SOFTWARE_LIST(config, m_softlist).set_original("snspell");
}

void snspell_state::sns_tmc0281d(machine_config &config)
{
	sns_tmc0281(config);

	// sound hardware
	TMC0281D(config.replace(), m_tms5100, MASTER_CLOCK);
	tms5110_route(config);
}

void snspell_state::sns_cd2801(machine_config &config)
{
	sns_tmc0281(config);

	// sound hardware
	CD2801(config.replace(), m_tms5100, MASTER_CLOCK);
	tms5110_route(config);
}

void snspell_state::snspellit(machine_config &config)
{
	sns_cd2801(config);

	config.set_default_layout(layout_snmath);
}

void snspell_state::snspellsp(machine_config &config)
{
	sns_tmc0281(config);

	config.set_default_layout(layout_snspellsp);
}


void snspell_state::snmath(machine_config &config)
{
	sns_cd2801(config);

	// basic machine hardware
	m_maincpu->write_o().set(FUNC(snspell_state::snmath_write_o));

	config.set_default_layout(layout_snmath);

	// no cartridge
	config.device_remove("cartslot");
	config.device_remove("cart_list");
}


void snspell_state::snread(machine_config &config)
{
	sns_cd2801(config);

	config.set_default_layout(layout_snread);

	// cartridge
	m_cart->set_interface("snread");
	m_softlist->set_original("snread");
}


void snspell_state::lantrans(machine_config &config)
{
	sns_cd2801(config);

	// basic machine hardware
	m_maincpu->write_r().set(FUNC(snspell_state::lantrans_write_r));

	config.set_default_layout(layout_snread);

	// cartridge
	m_cart->set_interface("lantrans");
	m_cart->set_must_be_loaded(true);
	m_softlist->set_original("lantrans");
}

void snspell_state::lanteach(machine_config &config)
{
	lantrans(config);

	// cartridge
	m_cart->set_interface("lanteach");
	m_softlist->set_original("lanteach");

	// no sound
	m_tms5100->reset_routes();
	config.device_remove("mono");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( snspell )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "tmc0271h-n2l_fds", 0x0000, 0x1000, CRC(f83b5d2d) SHA1(10155b0b7f7f1583c7def8a693553cd35944ea6f) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_tmc271h_output.pla", 0, 1246, CRC(2478c595) SHA1(9a8ac690902731e1e01533279a1c9223011e1537) )

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // 8000-bfff = space reserved for cartridge
	ROM_LOAD( "tmc0351n2l", 0x0000, 0x4000, CRC(2d03b292) SHA1(a3e9a365307ae936c7472f720a7a8240741531d6) )
	ROM_LOAD( "tmc0352n2l", 0x4000, 0x4000, CRC(a6d56883) SHA1(eebf9c07f2f9001679dec06c2367d4a50596d04b) )
ROM_END

ROM_START( snspellp )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "us4189779", 0x0000, 0x1000, CRC(d3f5a37d) SHA1(f75ab617a6067d4d3a954a9f86126d2089554df8) ) // typed in from patent US4189779, verified by 2 sources

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_tmc271_output.pla", 0, 1246, CRC(9ebe12ab) SHA1(acb4e07ba26f2daca5f1c234885ac0371c7ce87f) ) // using the one from 1st version

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // 8000-bfff = space reserved for cartridge
	ROM_LOAD( "tmc0351nl", 0x0000, 0x4000, CRC(beea3373) SHA1(8b0f7586d2f12c3d4a885fdb528cf23feffa1a3b) ) // using the one from 1st version
	ROM_LOAD( "tmc0352nl", 0x4000, 0x4000, CRC(d51f0587) SHA1(ddaa484be1bba5fef46b481cafae517e4acaa8ed) ) // "
ROM_END

ROM_START( snspellua )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "tmc0271nl_dbs", 0x0000, 0x1000, CRC(c2a7b747) SHA1(05f3716cd3cc33b8dc897e75301efdd531932ec5) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_tmc271_output.pla", 0, 1246, CRC(9ebe12ab) SHA1(acb4e07ba26f2daca5f1c234885ac0371c7ce87f) )

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // 8000-bfff = space reserved for cartridge
	ROM_LOAD( "tmc0351nl", 0x0000, 0x4000, CRC(beea3373) SHA1(8b0f7586d2f12c3d4a885fdb528cf23feffa1a3b) )
	ROM_LOAD( "tmc0352nl", 0x4000, 0x4000, CRC(d51f0587) SHA1(ddaa484be1bba5fef46b481cafae517e4acaa8ed) )
ROM_END

ROM_START( snspellub )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "tmc0271h-n2l_fds", 0x0000, 0x1000, CRC(f83b5d2d) SHA1(10155b0b7f7f1583c7def8a693553cd35944ea6f) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_tmc271h_output.pla", 0, 1246, CRC(2478c595) SHA1(9a8ac690902731e1e01533279a1c9223011e1537) )

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // uses only 1 rom, 8000-bfff = space reserved for cartridge
	ROM_LOAD( "cd2350a", 0x0000, 0x4000, CRC(2adda742) SHA1(3f868ed8284b723c815a30343057e03467c043b5) )
ROM_END

ROM_START( snspelluk )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "tmc0271nl_dbs", 0x0000, 0x1000, CRC(c2a7b747) SHA1(05f3716cd3cc33b8dc897e75301efdd531932ec5) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_tmc271_output.pla", 0, 1246, CRC(9ebe12ab) SHA1(acb4e07ba26f2daca5f1c234885ac0371c7ce87f) )

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // 8000-bfff = space reserved for cartridge
	ROM_LOAD( "cd2303", 0x0000, 0x4000, CRC(0fae755c) SHA1(b68c3120a63a61db474feb5d71a6e5dd67910d80) )
	ROM_LOAD( "cd2304", 0x4000, 0x4000, CRC(e2a270eb) SHA1(c13c95ad15f1923a4841f66504e0f22646e71d99) )
ROM_END

ROM_START( snspelluka )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "tmc0271h-n2l_fds", 0x0000, 0x1000, CRC(f83b5d2d) SHA1(10155b0b7f7f1583c7def8a693553cd35944ea6f) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_tmc271h_output.pla", 0, 1246, CRC(2478c595) SHA1(9a8ac690902731e1e01533279a1c9223011e1537) )

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // uses only 1 rom, 8000-bfff = space reserved for cartridge
	ROM_LOAD( "cd62175", 0x0000, 0x4000, CRC(6e1063d4) SHA1(b5c66c51148c5921ecb8ffccd7a460ae639cdb68) )
ROM_END

ROM_START( snspelljp )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "tmc0271h-n2l_fds", 0x0000, 0x1000, CRC(f83b5d2d) SHA1(10155b0b7f7f1583c7def8a693553cd35944ea6f) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_tmc271h_output.pla", 0, 1246, CRC(2478c595) SHA1(9a8ac690902731e1e01533279a1c9223011e1537) )

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // 8000-bfff = space reserved for cartridge
	ROM_LOAD( "cd2321", 0x0000, 0x4000, CRC(ac010cce) SHA1(c0200d857b62be696248ac2d684a390c66ab0c31) )
	ROM_LOAD( "cd2322", 0x4000, 0x4000, CRC(b6f4bba4) SHA1(65d686a9385b5ef3f080a5f47c6b2418bb9455b0) )
ROM_END

ROM_START( snspellsp )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "cd2701n2l_p", 0x0000, 0x1000, CRC(5230612a) SHA1(3ec75b207380af2efe84e865fe83ea29e79e8eea) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_cd2701_output.pla", 0, 1246, CRC(f26980bd) SHA1(8d0c98fe5240541cb53c1e1d14c2a4560e7a7f32) )

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // 8000-bfff = space reserved for cartridge
	ROM_LOAD( "cd2319", 0x0000, 0x4000, CRC(f293ac2f) SHA1(6f941743efcc2f05e514ce07167c094c554dca5d) )
	ROM_LOAD( "cd2320", 0x4000, 0x4000, CRC(16b68766) SHA1(a9ea335b4487cc333268bfd2e71428258968461d) )
ROM_END

ROM_START( snspellfr )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "cd2702an2l", 0x0000, 0x1000, CRC(895d6a4e) SHA1(a8bc118c83a84260033734191dcaa71a93dfa52b) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common1_micro.pla", 0, 2127, CRC(504b96bb) SHA1(67b691e7c0b97239410587e50e5182bf46475b43) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_cd2702_output.pla", 0, 1246, CRC(2478c595) SHA1(9a8ac690902731e1e01533279a1c9223011e1537) )

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // uses only 1 rom, 8000-bfff = space reserved for cartridge
	ROM_LOAD( "cd2352", 0x0000, 0x4000, CRC(181a239e) SHA1(e16043766c385e152b7005c1c010be4c5fccdd9b) )
ROM_END

ROM_START( snspellit )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "cd2702an2l", 0x0000, 0x1000, CRC(895d6a4e) SHA1(a8bc118c83a84260033734191dcaa71a93dfa52b) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common1_micro.pla", 0, 2127, CRC(504b96bb) SHA1(67b691e7c0b97239410587e50e5182bf46475b43) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_cd2702_output.pla", 0, 1246, CRC(2478c595) SHA1(9a8ac690902731e1e01533279a1c9223011e1537) )

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // uses only 1 rom, 8000-bfff = space reserved for cartridge
	ROM_LOAD( "cd62190", 0x0000, 0x4000, CRC(63832002) SHA1(ea8124b2bf0f5908c5f1a56d60063f2468a10143) )
ROM_END


ROM_START( snmath )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "cd2704b-n2l", 0x0000, 0x1000, CRC(7e06c7c5) SHA1(d60a35a8163ab593c31afc840a0d8a9b3a762f29) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common1_micro.pla", 0, 2127, CRC(504b96bb) SHA1(67b691e7c0b97239410587e50e5182bf46475b43) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_cd2704_output.pla", 0, 1246, CRC(5a2eb949) SHA1(8bb161d4884f229af65f8d155e59b9d8966fe3d1) )

	ROM_REGION( 0x8000, "tms6100", ROMREGION_ERASEFF )
	ROM_LOAD( "cd2392", 0x0000, 0x4000, CRC(4ed2e920) SHA1(8896f29e25126c1e4d9a47c9a325b35dddecc61f) )
	ROM_LOAD( "cd2393", 0x4000, 0x4000, CRC(571d5b5a) SHA1(83284755d9b77267d320b5b87fdc39f352433715) )
ROM_END

ROM_START( snmatha )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "cd2708-n2l", 0x0000, 0x1000, CRC(35937360) SHA1(69c362c75bb459056c09c7fab37c91040485474b) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common1_micro.pla", 0, 2127, CRC(504b96bb) SHA1(67b691e7c0b97239410587e50e5182bf46475b43) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_cd2708_output.pla", 0, 1246, CRC(1abad753) SHA1(53d20b519ed73ce248368047a056836afbe3cd46) )

	ROM_REGION( 0x8000, "tms6100", ROMREGION_ERASEFF )
	ROM_LOAD( "cd2381", 0x0000, 0x4000, CRC(f048dc81) SHA1(e97667d1002de40ab3d702c63b82311480032e0f) )
	ROM_LOAD( "cd2614", 0x4000, 0x1000, CRC(11989074) SHA1(0e9cf906de9bcdf4acb425535dc442846fc48fa2) )
	ROM_RELOAD(             0x5000, 0x1000 )
	ROM_RELOAD(             0x6000, 0x1000 )
	ROM_RELOAD(             0x7000, 0x1000 )
ROM_END

ROM_START( snmathp )
	ROM_REGION( 0x1000, "maincpu", 0 )
	// typed in from patent 4946391, verified with source code
	// BTANB note: Mix It does not work at all, this is an original bug in the patent listing. There are probably other minor bugs too.
	ROM_LOAD( "us4946391_t2074", 0x0000, 0x1000, CRC(011f0c2d) SHA1(d2e14d72e03ca864abd51da78ffb71a9da82f624) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common1_micro.pla", 0, 2127, CRC(504b96bb) SHA1(67b691e7c0b97239410587e50e5182bf46475b43) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_cd2704_output.pla", 0, 1246, CRC(5a2eb949) SHA1(8bb161d4884f229af65f8d155e59b9d8966fe3d1) ) // using the one from 1st version

	ROM_REGION( 0x8000, "tms6100", ROMREGION_ERASEFF )
	ROM_LOAD( "cd2392", 0x0000, 0x4000, CRC(4ed2e920) SHA1(8896f29e25126c1e4d9a47c9a325b35dddecc61f) ) // using the one from 1st version
	ROM_LOAD( "cd2393", 0x4000, 0x4000, CRC(571d5b5a) SHA1(83284755d9b77267d320b5b87fdc39f352433715) ) // "
ROM_END


ROM_START( snread )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "cd2705b-n2l", 0x0000, 0x1000, CRC(c235636e) SHA1(57b24dd8414bf76ec786a51d10cb8a5898b60e18) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common1_micro.pla", 0, 2127, CRC(504b96bb) SHA1(67b691e7c0b97239410587e50e5182bf46475b43) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_cd2705_output.pla", 0, 1246, CRC(bf859848) SHA1(66b297fbf534968fa6db7413b99ef0e81cc35ddc) )

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // 8000-bfff = space reserved for cartridge
	ROM_LOAD( "cd2394a", 0x0000, 0x4000, CRC(cbb0e2b1) SHA1(5e322c683baf806523de171310258ae371671327) )
	ROM_LOAD( "cd2395a", 0x4000, 0x4000, CRC(3d519504) SHA1(76b19ba5a9a3486005e09c98e8a6abc8b88288dd) )
ROM_END


ROM_START( lantrans )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "tmc0275nl_dbs", 0x0000, 0x1000, CRC(43535d2a) SHA1(cf190fc0b1b2d9d11b167896adda116bc28348fd) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_tmc0275_output.pla", 0, 1246, CRC(3c67e7b8) SHA1(06134367754a687e933ed629a105b6956fc30375) )

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // cartridge area
ROM_END

ROM_START( lantransp )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "us4631748", 0x0000, 0x1000, CRC(22818845) SHA1(1a84f15fb18ca66b1f2bf7491d76fbc56068984d) ) // extracted visually from patent US4631748, verified with source code

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_tmc0275_output.pla", 0, 1246, CRC(3c67e7b8) SHA1(06134367754a687e933ed629a105b6956fc30375) ) // using the one from release version

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // cartridge area
ROM_END

ROM_START( lanteach )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "cd2706", 0x0000, 0x1000, CRC(4eca02d7) SHA1(1b13b7679b868c6c978d3047b01a372abfd7f01a) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_cd2706_output.pla", 0, 1246, CRC(c290b20f) SHA1(377205266b2b8932d06abd98a6071ea75eec834e) )

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // cartridge area
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME        PARENT    COMPAT  MACHINE       INPUT       CLASS          INIT           COMPANY, FULLNAME, FLAGS
SYST( 1980, snspell,    0,        0,      sns_tmc0281,  snspell,    snspell_state, init_snspell,  "Texas Instruments", "Speak & Spell (US, 1980 version)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )
SYST( 1978, snspellua,  snspell,  0,      sns_tmc0281,  snspell,    snspell_state, init_snspell,  "Texas Instruments", "Speak & Spell (US, 1978 version)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )
SYST( 1981, snspellub,  snspell,  0,      sns_tmc0281d, snspell,    snspell_state, init_snspell,  "Texas Instruments", "Speak & Spell (US, 1981 version)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND ) // less speech data
SYST( 1978, snspellp,   snspell,  0,      sns_tmc0281,  snspell,    snspell_state, init_snspell,  "Texas Instruments", "Speak & Spell (US, patent)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )
SYST( 1978, snspelluk,  snspell,  0,      sns_tmc0281,  snspell,    snspell_state, init_snspell,  "Texas Instruments", "Speak & Spell (UK, 1978 version)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )
SYST( 1981, snspelluka, snspell,  0,      sns_cd2801,   snspell,    snspell_state, init_snspell,  "Texas Instruments", "Speak & Spell (UK, 1981 version)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND ) // less speech data
SYST( 1980, snspelljp,  snspell,  0,      sns_tmc0281,  snspell,    snspell_state, init_snspell,  "Texas Instruments", "Speak & Spell (Japan)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND ) // speaks English
SYST( 1980, snspellsp,  snspell,  0,      snspellsp,    snspellsp,  snspell_state, init_snspell,  "Texas Instruments", "Speak & Spell (Spanish, prototype)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )
SYST( 1981, snspellfr,  snspell,  0,      sns_cd2801,   snspellfr,  snspell_state, init_snspell,  "Texas Instruments", u8"La Dictée Magique (France)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )
SYST( 1983, snspellit,  snspell,  0,      snspellit,    snspellit,  snspell_state, init_snspell,  "Texas Instruments / Clementoni", "Grillo Parlante (Italy)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )

SYST( 1980, snmath,     0,        0,      snmath,       snmath,     snspell_state, empty_init,    "Texas Instruments", "Speak & Math (US, 1980 version)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )
SYST( 1984, snmatha,    snmath,   0,      snmath,       snmath,     snspell_state, empty_init,    "Texas Instruments", "Speak & Math (US, 1984 version)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND ) // less speech data
SYST( 1980, snmathp,    snmath,   0,      snmath,       snmath,     snspell_state, empty_init,    "Texas Instruments", "Speak & Math (US, patent)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND | MACHINE_IS_INCOMPLETE )

SYST( 1980, snread,     0,        0,      snread,       snread,     snspell_state, init_snspell,  "Texas Instruments", "Speak & Read (US)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )

SYST( 1979, lantrans,   0,        0,      lantrans,     lantrans,   snspell_state, init_lantrans, "Texas Instruments", "Language Translator", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )
SYST( 1979, lantransp,  lantrans, 0,      lantrans,     lantrans,   snspell_state, init_lantrans, "Texas Instruments", "Language Translator (patent)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING )

SYST( 1980, lanteach,   0,        0,      lanteach,     lantrans,   snspell_state, init_lantrans, "Texas Instruments", "Language Teacher", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )



snspellc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle, David Viens, Kevin Horton
/*******************************************************************************

Texas Instruments Speak & Spell Compact, Touch & Tell

Speak & Spell Compact (US), 1981
- MCU: CD8011, label CD8011A-NL (die label: 1100B, CD8011A)
- TMS51xx: TMC0281D (die label: T0280F 0281D)
- VSM: 16KB CD2354, CD2354A
- notes: no display, MCU is TMS1100 instead of TMS0270, overall similar to Touch & Tell

Speak & Spell Compact (UK) "Speak & Write", 1981
- MCU: same as US 1981 version
- TMS51xx: CD2801A
- VSM: 16KB CD62174A
- notes: BTANB: gibberish when the module button is pressed (with module present)

Speak & Spell Compact is compatible with Speak & Spell modules.

Anecdotes from the developer, the same person working on the original Speak & Spell
UK version: "We included a pencil and writing pad - it was now about 'writing'."

And one about the welcome message: "I had to manually create a sentence of digital
speech from thin air. I had to write down a 20 character code which would create
each 10/s sound bite that made up the phrase "Welcome to Speak and Write". It took
me 1 week. (...) Even Larry Brantingham was amazed."

================================================================================

Speak & Math 'Compact' (France) "Les Maths Magiques", 1982
- MCU: CP3447-NL (die label: 1100F, MP3447)
- TMS51xx: CD2801A
- VSM: 16KB CD62173A
- notes: this is not the same as "Le Calcul Magique", that's from a
  series centered around a TMS50C40 instead of MCU+TMS51xx

There is no English version, but likewise there is no French version of the
regular Speak & Math. No known external modules were released.

================================================================================

Touch & Tell (US), 1981
- MCU: CD8012, label CD8012NL (die label: 1100G CD8012)
- TMS51xx: CD2802
- VSM: 4KB CD2610
- notes: MCU is TMS1100 instead of TMS0270. CD8010 is seen in some devices
  too, maybe an earlier version? For some reason, it doesn't use the standard
  TMS1100 microinstructions, the opcodes are scrambled.

Touch & Tell (UK), 1981
- MCU & TMS51xx: same as US version
- VSM: 16KB CD62170

Touch & Tell (France) "Le Livre Magique", 1981
- MCU & TMS51xx: same as US version
- VSM: 16KB CD62171

Touch & Tell (Germany) "Tipp & Sprich", 1981
- MCU & TMS51xx: same as US version
- VSM: 16KB? CD62172*

Touch & Tell (Italy) "Libro Parlante", 1982
- MCU & TMS51xx: same as US version
- VSM: 16KB? CD62176* (on a module)

Vocaid (US), 1982
- MCU & TMS51xx: same as Touch & Tell (US)
- VSM: 16KB CD2357
- notes: MCU is the same as in Touch & Tell, but instead of a toddler's toy,
  you get a serious medical aid device for the voice-impaired. The PCB is
  identical, it includes the edge connector for modules but no external slot.

Touch & Tell modules:

English:
- Alphabet Fun: VSM: 4KB CD2611
- Animal Friends: VSM: 16KB CD2355
- Number Fun: VSM: 4KB CD2612*, CD2612A
- All About Me: VSM: 4KB CD2613
- World of Transportation: VSM: 16KB CD2361
- Little Creatures: VSM: 16KB CD2362
- E.T.: VSM: 16KB CD2363

(* denotes not dumped)

Touch & Tell/Vocaid overlay reference:

tntell CD2610:
- $04: a - Colors
- $01: b - Objects
- $05: c - Shapes
- $09: d - Home Scene
tntelluk CD62170, tntellfr CD62171:
- see tntell
- see numfun(not A)
- see animalfr
- $08: ? - Clown Face
- $0B: ? - Body Parts
vocaid CD2357:
- $1C: 1 - Leisure
- $1E: 2 - Telephone
- $1B: 3 - Bedside
- $1D: 4 - Alphabet
alphabet CD2611:
- $0E: 1a - Alphabet A-M
- $0D: 1b - Alphabet N-Z
- $0C: 1c - Letter Jumble A-M
- $0B: 1d - Letter Jumble N-Z
animalfr CD2355:
- $0A: 2a - Farm Animals
- $0F: 2b - At The Farm
- $0E: 2c - Animal Babies
- $0D: 2d - In The Jungle
numfun CD2612:
- $02/$0A(rev.A): 3a - Numbers 1-10
- $03/$0F(rev.A): 3b - Numbers 11-30
- $07/$0D(rev.A): 3c - How Many?
- $06/$0E(rev.A): 3d - Hidden Numbers
aboutme CD2613:
- $0E: 4a - Clown Face
- $0B: 4b - Body Parts
- $0D: 4c - Things to Wear
- $0C: 4d - Just For Me
wot CD2361:
- $0A: 5a - On Land
- $0B: 5b - In The Air
- $0C: 5c - On The Water
- $0D: 5d - In Space
- $10: 5e - What Belongs Here?
- $11: 5f - How It Used To Be
- $12: 5g - Word Fun
- $13: 5h - In the Surprise Garage
lilcreat CD2362:
- $14: 6a - In The Park
- $15: 6b - In The Sea
- $16: 6c - In The Woods
- $17: 6d - Whose House?
- $18: 6e - Hide & Seek
- $1A: 6f - Who Is It?
- $19: 6g - But It's Not
- $1B: 6h - Word Fun
et CD2363:
- $0F: 7a - The Adventure On Earth I
- $10: 7b - The Adventure On Earth II
- $11: 7c - Fun And Friendship I
- $12: 7d - Fun And Friendship II
- $13: 7e - E.T. The Star I
- $14: 7f - E.T. The Star II
- $15: 7g - Do You Remember? I
- $16: 7h - Do You Remember? II

$00: none inserted, and $1F is for diagnostics

*******************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/tms1000/tms1400.h"
#include "machine/timer.h"
#include "machine/tms6100.h"
#include "sound/tms5110.h"

#include "render.h"
#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "tntell.lh"


namespace {

// master clock for TMS5100, see snspell driver

static constexpr u32 MASTER_CLOCK = 640'000;


// Speak & Spell Compact / common

class snspellc_state : public driver_device
{
public:
	snspellc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_tms5100(*this, "tms5100"),
		m_tms6100(*this, "tms6100"),
		m_cart(*this, "cartslot"),
		m_inputs(*this, "IN.%u", 0),
		m_power_on(*this, "power")
	{ }

	// machine configs
	void tms5110_route(machine_config &config);
	void snspellc(machine_config &config);
	void snwrite(machine_config &config);
	void mathsmag(machine_config &config);

	void init_snspellc();

	DECLARE_INPUT_CHANGED_MEMBER(power_on);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// devices/pointers
	required_device<tms1100_cpu_device> m_maincpu;
	required_device<tms5110_device> m_tms5100;
	required_device<tms6100_device> m_tms6100;
	optional_device<generic_slot_device> m_cart;
	required_ioport_array<10> m_inputs;
	output_finder<> m_power_on;

	u8 *m_cart_base = nullptr;
	u16 m_o = 0;
	u32 m_r = 0;

	void power_off();
	virtual u8 read_k();
	void write_o(u16 data);
	void write_r(u32 data);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
};

void snspellc_state::machine_start()
{
	m_power_on.resolve();

	// register for savestates
	save_item(NAME(m_o));
	save_item(NAME(m_r));
}


// Touch & Tell

class tntell_state : public snspellc_state
{
public:
	tntell_state(const machine_config &mconfig, device_type type, const char *tag) :
		snspellc_state(mconfig, type, tag),
		m_overlay_inp(*this, "OVERLAY"),
		m_overlay_out(*this, "ol%u", 1U)
	{ }

	void tntell(machine_config &config);
	void vocaid(machine_config &config);

	void init_tntell();

protected:
	virtual void machine_start() override ATTR_COLD;

	virtual u8 read_k() override;

private:
	optional_ioport m_overlay_inp;
	output_finder<5> m_overlay_out;

	u8 m_overlay_code = 0;

	u8 get_hexchar(const char c);
	TIMER_DEVICE_CALLBACK_MEMBER(get_overlay);
};

void tntell_state::machine_start()
{
	snspellc_state::machine_start();

	m_overlay_out.resolve();
	save_item(NAME(m_overlay_code));
}



/*******************************************************************************
    Power
*******************************************************************************/

void snspellc_state::machine_reset()
{
	m_power_on = 1;
	m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
}

INPUT_CHANGED_MEMBER(snspellc_state::power_on)
{
	if (newval && !m_power_on)
		machine_reset();
}

void snspellc_state::power_off()
{
	m_power_on = 0;
	m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
}



/*******************************************************************************
    Cartridge
*******************************************************************************/

DEVICE_IMAGE_LOAD_MEMBER(snspellc_state::cart_load)
{
	u32 const size = m_cart->common_get_size("rom");

	if (size > 0x4000)
		return std::make_pair(image_error::INVALIDLENGTH, "Invalid file size (must be no more than 16K)");

	m_cart->common_load_rom(m_cart_base, size, "rom");
	return std::make_pair(std::error_condition(), std::string());
}

void snspellc_state::init_snspellc()
{
	m_cart_base = memregion("tms6100")->base() + 0x8000;
}

void tntell_state::init_tntell()
{
	m_cart_base = memregion("tms6100")->base() + 0x4000;
}



/*******************************************************************************
    I/O
*******************************************************************************/

void snspellc_state::write_r(u32 data)
{
	// R10: TMS5100 PDC pin
	m_tms5100->pdc_w(data >> 10 & 1);

	// R9: power-off request, on falling edge
	if (~data & m_r & 0x200)
		power_off();

	// R0-R8: input mux
	m_r = data;
}

void snspellc_state::write_o(u16 data)
{
	// O3210: TMS5100 CTL8124
	m_tms5100->ctl_w(bitswap<4>(data,3,0,1,2));
	m_o = data;
}

u8 snspellc_state::read_k()
{
	// K4: TMS5100 CTL1
	u8 data = m_tms5100->ctl_r() << 2 & 4;

	// K: multiplexed inputs (note: the Vss row is always on)
	for (int i = 0; i < 9; i++)
		if (BIT(m_r, i))
			data |= m_inputs[i]->read();

	return data | m_inputs[9]->read();
}

u8 tntell_state::read_k()
{
	// K8: overlay code from R5,O4-O7
	u8 k8 = (((m_r >> 1 & 0x10) | (m_o >> 4 & 0xf)) & m_overlay_code) ? 8 : 0;

	// rest is same as snpellc
	return k8 | snspellc_state::read_k();
}



/*******************************************************************************
    Overlay (Touch & Tell)
*******************************************************************************/

u8 tntell_state::get_hexchar(const char c)
{
	if (c >= '0' && c <= '9') return c - '0';
	if (c >= 'A' && c <= 'F') return c - 'A' + 10;
	if (c >= 'a' && c <= 'f') return c - 'a' + 10;

	return 0;
}

TIMER_DEVICE_CALLBACK_MEMBER(tntell_state::get_overlay)
{
	// Each keyboard overlay insert has 5 holes, used by the game to determine
	// which one is active(if any). If it matches with the internal ROM or
	// external module, the game continues.

	// pick overlay code from input config, see comment section above for reference
	m_overlay_code = m_overlay_inp->read();

	// try to get it from (external) layout
	if (m_overlay_code == 0x20)
	{
		// as output value, eg. with defstate (in decimal)
		m_overlay_code = output().get_value("overlay_code") & 0x1f;

		// and from current view name ($ + 2 hex digits)
		render_target *target = machine().render().first_target();
		const char *name = target->view_name(target->view());

		for (int i = 0; name && i < strlen(name); i++)
			if (name[i] == '$' && strlen(&name[i]) > 2)
				m_overlay_code = (get_hexchar(name[i + 1]) << 4 | get_hexchar(name[i + 2])) & 0x1f;
	}

	// overlay holes
	for (int i = 0; i < 5; i++)
		m_overlay_out[i] = BIT(m_overlay_code, i);
}

static INPUT_PORTS_START( overlay )
	PORT_START("OVERLAY")
	PORT_CONFNAME( 0x3f, 0x20, "Overlay Code" )
	PORT_CONFSETTING(    0x20, "From Artwork View" )
	PORT_CONFSETTING(    0x00, "$00 (None)" )
	PORT_CONFSETTING(    0x01, "$01" )
	PORT_CONFSETTING(    0x02, "$02" )
	PORT_CONFSETTING(    0x03, "$03" )
	PORT_CONFSETTING(    0x04, "$04" )
	PORT_CONFSETTING(    0x05, "$05" )
	PORT_CONFSETTING(    0x06, "$06" )
	PORT_CONFSETTING(    0x07, "$07" )
	PORT_CONFSETTING(    0x08, "$08" )
	PORT_CONFSETTING(    0x09, "$09" )
	PORT_CONFSETTING(    0x0a, "$0A" )
	PORT_CONFSETTING(    0x0b, "$0B" )
	PORT_CONFSETTING(    0x0c, "$0C" )
	PORT_CONFSETTING(    0x0d, "$0D" )
	PORT_CONFSETTING(    0x0e, "$0E" )
	PORT_CONFSETTING(    0x0f, "$0F" )
	PORT_CONFSETTING(    0x10, "$10" )
	PORT_CONFSETTING(    0x11, "$11" )
	PORT_CONFSETTING(    0x12, "$12" )
	PORT_CONFSETTING(    0x13, "$13" )
	PORT_CONFSETTING(    0x14, "$14" )
	PORT_CONFSETTING(    0x15, "$15" )
	PORT_CONFSETTING(    0x16, "$16" )
	PORT_CONFSETTING(    0x17, "$17" )
	PORT_CONFSETTING(    0x18, "$18" )
	PORT_CONFSETTING(    0x19, "$19" )
	PORT_CONFSETTING(    0x1a, "$1A" )
	PORT_CONFSETTING(    0x1b, "$1B" )
	PORT_CONFSETTING(    0x1c, "$1C" )
	PORT_CONFSETTING(    0x1d, "$1D" )
	PORT_CONFSETTING(    0x1e, "$1E" )
	PORT_CONFSETTING(    0x1f, "$1F (Diagnostic)" )
INPUT_PORTS_END



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( snspellc )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_NAME("Letter Stumper")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_NAME("Review")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_NAME("Repeat")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_NAME("Erase")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_NAME("Module")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')

	PORT_START("IN.6") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_HOME) PORT_NAME("Go")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')

	PORT_START("IN.7") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("Enter")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')

	PORT_START("IN.8") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'')

	PORT_START("IN.9") // Vss
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_F1) PORT_NAME("Spell/On") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(snspellc_state::power_on), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_CUSTOM ) // speech chip data
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_NAME("Off") // -> auto_power_off
INPUT_PORTS_END

static INPUT_PORTS_START( snwrite )
	PORT_INCLUDE( snspellc )

	PORT_MODIFY("IN.9")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_F1) PORT_NAME("Write/On") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(snspellc_state::power_on), 0) // just the label changed from Spell to Write
INPUT_PORTS_END


static INPUT_PORTS_START( mathsmag )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME(u8"Répète")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_NAME(">")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_COMMA) PORT_NAME("<")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Efface")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME("*")

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")

	PORT_START("IN.5") // R5
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.6") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME(u8"Problèmes")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("Plus grand, plus petit que")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME(u8"Dictée")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.7") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_HOME) PORT_NAME(u8"Départ / Continue")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Essaie")

	PORT_START("IN.8") // R8
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.9") // Vss
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F1) PORT_NAME("Marche / Calcule") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(snspellc_state::power_on), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F2) PORT_NAME(u8"Arrét") // -> auto_power_off
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


static INPUT_PORTS_START( tntell )
	PORT_INCLUDE( overlay )

	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("Grid 1-1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("Grid 1-2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_NAME("Grid 1-4")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("Grid 1-3")

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_NAME("Grid 2-1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_NAME("Grid 2-2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_NAME("Grid 2-4")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_NAME("Grid 2-3")

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Grid 3-1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("Grid 3-2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("Grid 3-4")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("Grid 3-3")

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_U) PORT_NAME("Grid 4-1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_I) PORT_NAME("Grid 4-2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_P) PORT_NAME("Grid 4-4")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_O) PORT_NAME("Grid 4-3")

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Grid 5-1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Grid 5-2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("Grid 5-4")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("Grid 5-3")

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_H) PORT_NAME("Grid 5-6")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_NAME("Grid 6-5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_G) PORT_NAME("Grid 5-5")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) // overlay code

	PORT_START("IN.6") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Grid 3-5")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("Grid 2-5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_NAME("Grid 4-5")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_NAME("Grid 1-5")

	PORT_START("IN.7") // R7
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Y) PORT_NAME("Grid 3-6")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("Grid 2-6")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_NAME("Grid 4-6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_NAME("Grid 1-6")

	PORT_START("IN.8") // R8
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Z) PORT_CODE(KEYCODE_F2) PORT_NAME("Grid 6-1 (Off)") // -> auto_power_off
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME("Grid 6-2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_V) PORT_NAME("Grid 6-4")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("Grid 6-3")

	PORT_START("IN.9") // Vss
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_N) PORT_CODE(KEYCODE_F1) PORT_NAME("Grid 6-6 (On)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tntell_state::power_on), 0)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_CUSTOM ) // speech chip data
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void snspellc_state::tms5110_route(machine_config &config)
{
	// sound hardware
	m_tms5100->m0().set(m_tms6100, FUNC(tms6100_device::m0_w));
	m_tms5100->m1().set(m_tms6100, FUNC(tms6100_device::m1_w));
	m_tms5100->addr().set(m_tms6100, FUNC(tms6100_device::add_w));
	m_tms5100->data().set(m_tms6100, FUNC(tms6100_device::data_line_r));
	m_tms5100->romclk().set(m_tms6100, FUNC(tms6100_device::clk_w));
	m_tms5100->add_route(ALL_OUTPUTS, "mono", 0.5);
}

void snspellc_state::snspellc(machine_config &config)
{
	// basic machine hardware
	TMS1100(config, m_maincpu, MASTER_CLOCK/2);
	m_maincpu->read_k().set(FUNC(snspellc_state::read_k));
	m_maincpu->write_o().set(FUNC(snspellc_state::write_o));
	m_maincpu->write_r().set(FUNC(snspellc_state::write_r));

	// sound hardware
	TMS6100(config, m_tms6100, MASTER_CLOCK/4);

	SPEAKER(config, "mono").front_center();
	TMC0281D(config, m_tms5100, MASTER_CLOCK);
	tms5110_route(config);

	// cartridge
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "snspell", "vsm,bin");
	m_cart->set_device_load(FUNC(snspellc_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("snspell");
}

void snspellc_state::snwrite(machine_config &config)
{
	snspellc(config);

	// sound hardware
	CD2801(config.replace(), m_tms5100, MASTER_CLOCK); // CD2801A!
	tms5110_route(config);
}

void snspellc_state::mathsmag(machine_config &config)
{
	snwrite(config);

	// cartridge
	m_cart->set_interface("mathsmag");
	config.device_remove("cart_list"); // N/A
}


void tntell_state::tntell(machine_config &config)
{
	snspellc(config);

	// basic machine hardware
	TIMER(config, "ol_timer").configure_periodic(FUNC(tntell_state::get_overlay), attotime::from_msec(50));
	config.set_default_layout(layout_tntell);

	// sound hardware
	CD2802(config.replace(), m_tms5100, MASTER_CLOCK);
	tms5110_route(config);

	// cartridge
	m_cart->set_interface("tntell");
	subdevice<software_list_device>("cart_list")->set_original("tntell");
}

void tntell_state::vocaid(machine_config &config)
{
	tntell(config);

	// no external module slot
	config.device_remove("cartslot");
	config.device_remove("cart_list");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( snspellc )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "cd8011a-nl", 0x0000, 0x0800, CRC(8a82a467) SHA1(fa4f8a232392603721bd8136c141a340fd5936a0) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_cd8011_micro.pla", 0, 867, CRC(bbb64ddc) SHA1(602cd5eef897c9115c12db7367c5654ab2297fa1) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_cd8011_output.pla", 0, 365, CRC(b400dd75) SHA1(5a4b5d4532a8932cf4b469ddb71ad6b3b9911672) )

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // 8000-bfff = space reserved for cartridge
	ROM_LOAD( "cd2354a", 0x0000, 0x4000, CRC(548a940c) SHA1(c37e620c4c70a05cbaaff9a166c6da2e2420196f) )
ROM_END

ROM_START( snspellca )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "cd8011a-nl", 0x0000, 0x0800, CRC(8a82a467) SHA1(fa4f8a232392603721bd8136c141a340fd5936a0) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_cd8011_micro.pla", 0, 867, CRC(bbb64ddc) SHA1(602cd5eef897c9115c12db7367c5654ab2297fa1) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_cd8011_output.pla", 0, 365, CRC(b400dd75) SHA1(5a4b5d4532a8932cf4b469ddb71ad6b3b9911672) )

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // 8000-bfff = space reserved for cartridge
	ROM_LOAD( "cd2354", 0x0000, 0x4000, CRC(3af3232e) SHA1(f89d90dca209ee612634d664d5d4562f1d1786cf) )
ROM_END

ROM_START( snwrite )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "cd8011a-nl", 0x0000, 0x0800, CRC(8a82a467) SHA1(fa4f8a232392603721bd8136c141a340fd5936a0) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_cd8011_micro.pla", 0, 867, CRC(bbb64ddc) SHA1(602cd5eef897c9115c12db7367c5654ab2297fa1) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_cd8011_output.pla", 0, 365, CRC(b400dd75) SHA1(5a4b5d4532a8932cf4b469ddb71ad6b3b9911672) )

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // 8000-bfff = space reserved for cartridge
	ROM_LOAD( "cd62174a", 0x0000, 0x4000, CRC(b7bbaaf3) SHA1(9eb949fcf522982f9c3c4649f207703b746b90ef) )
ROM_END


ROM_START( mathsmag )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "cp3447-nl", 0x0000, 0x0800, CRC(890751a1) SHA1(bd8bbd50f8ed31b2ae2d567c22cd92b21021bd20) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_common1_micro.pla", 0, 867, CRC(62445fc9) SHA1(d6297f2a4bc7a870b76cc498d19dbb0ce7d69fec) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_mathsmag_output.pla", 0, 365, CRC(d21f19a2) SHA1(9781da173d473c255fa5cc5fcc8ae09c097c682d) )

	ROM_REGION( 0x10000, "tms6100", ROMREGION_ERASEFF ) // 8000-bfff = space reserved for cartridge?
	ROM_LOAD( "cd62173a", 0x0000, 0x4000, CRC(a7230863) SHA1(8a6d1742fb94555f3b3fe37554a7c46fe4213116) )
ROM_END


ROM_START( tntell )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "cd8012nl", 0x0000, 0x0800, CRC(3d0fee24) SHA1(8b1b1df03d50ffe8adea59ece212dece5245fe86) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_cd8012_micro.pla", 0, 867, CRC(46d936c8) SHA1(b0aad486a90a5dec7fd2fb07caa503be771f91c8) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_cd8012_output.pla", 0, 365, CRC(5ada9306) SHA1(a4140118dd535af45a691832530d55cd86a23510) )

	ROM_REGION( 0x8000, "tms6100", ROMREGION_ERASEFF ) // 4000-7fff = space reserved for cartridge
	ROM_LOAD( "cd2610", 0x0000, 0x1000, CRC(6db34e5a) SHA1(10fa5db20fdcba68034058e7194f35c90b9844e6) )
ROM_END

ROM_START( tntelluk )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "cd8012nl", 0x0000, 0x0800, CRC(3d0fee24) SHA1(8b1b1df03d50ffe8adea59ece212dece5245fe86) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_cd8012_micro.pla", 0, 867, CRC(46d936c8) SHA1(b0aad486a90a5dec7fd2fb07caa503be771f91c8) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_cd8012_output.pla", 0, 365, CRC(5ada9306) SHA1(a4140118dd535af45a691832530d55cd86a23510) )

	ROM_REGION( 0x8000, "tms6100", ROMREGION_ERASEFF ) // 4000-7fff = space reserved for cartridge
	ROM_LOAD( "cd62170", 0x0000, 0x4000, CRC(6dc9d072) SHA1(9d2c9ff57c4f8fe69768666ffa41fcac649279ef) )
ROM_END

ROM_START( tntellfr )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "cd8012nl", 0x0000, 0x0800, CRC(3d0fee24) SHA1(8b1b1df03d50ffe8adea59ece212dece5245fe86) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_cd8012_micro.pla", 0, 867, CRC(46d936c8) SHA1(b0aad486a90a5dec7fd2fb07caa503be771f91c8) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_cd8012_output.pla", 0, 365, CRC(5ada9306) SHA1(a4140118dd535af45a691832530d55cd86a23510) )

	ROM_REGION( 0x8000, "tms6100", ROMREGION_ERASEFF ) // 4000-7fff = space reserved for cartridge
	ROM_LOAD( "cd62171", 0x0000, 0x4000, CRC(cc26f7d1) SHA1(2b03e37b3bf3cbeca36980acfc45246dac706b83) )
ROM_END

ROM_START( tntellp )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "us4403965_cd1100", 0x0000, 0x0800, BAD_DUMP CRC(863a1c9e) SHA1(f2f9eb0ae17eedd4ef2b887b34601e75b4f6c720) ) // typed in from patent US4403965/EP0048835A2, may have errors

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_cd8012_micro.pla", 0, 867, CRC(46d936c8) SHA1(b0aad486a90a5dec7fd2fb07caa503be771f91c8) ) // from cd8012, matches patent source code
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_us4403965_output.pla", 0, 365, CRC(66cfb3c3) SHA1(80a05e5d729518e1f35d8f26438f56e80ffbd003) )

	ROM_REGION( 0x8000, "tms6100", ROMREGION_ERASEFF ) // 4000-7fff = space reserved for cartridge
	ROM_LOAD( "cd2610", 0x0000, 0x1000, CRC(6db34e5a) SHA1(10fa5db20fdcba68034058e7194f35c90b9844e6) )
ROM_END


ROM_START( vocaid )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "cd8012nl", 0x0000, 0x0800, CRC(3d0fee24) SHA1(8b1b1df03d50ffe8adea59ece212dece5245fe86) )

	ROM_REGION( 867, "maincpu:mpla", 0 )
	ROM_LOAD( "tms1100_cd8012_micro.pla", 0, 867, CRC(46d936c8) SHA1(b0aad486a90a5dec7fd2fb07caa503be771f91c8) )
	ROM_REGION( 365, "maincpu:opla", 0 )
	ROM_LOAD( "tms1100_cd8012_output.pla", 0, 365, CRC(5ada9306) SHA1(a4140118dd535af45a691832530d55cd86a23510) )

	ROM_REGION( 0x8000, "tms6100", ROMREGION_ERASEFF ) // same hw as tntell, but no external slot
	ROM_LOAD( "cd2357", 0x0000, 0x4000, CRC(19c251fa) SHA1(8f8163069f32413379e7e1681ce6a4d0819d4ebc) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT    COMPAT  MACHINE   INPUT     CLASS           INIT           COMPANY, FULLNAME, FLAGS
SYST( 1982, snspellc,  0,        0,      snspellc, snspellc, snspellc_state, init_snspellc, "Texas Instruments", "Speak & Spell Compact (US, 1982 version)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )
SYST( 1981, snspellca, snspellc, 0,      snspellc, snspellc, snspellc_state, init_snspellc, "Texas Instruments", "Speak & Spell Compact (US, 1981 version)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )
SYST( 1982, snwrite,   snspellc, 0,      snwrite,  snwrite,  snspellc_state, init_snspellc, "Texas Instruments", "Speak & Write (UK)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )

SYST( 1982, mathsmag,  0,        0,      mathsmag, mathsmag, snspellc_state, init_snspellc, "Texas Instruments", "Les Maths Magiques (France)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )

SYST( 1981, tntell,    0,        0,      tntell,   tntell,   tntell_state,   init_tntell,   "Texas Instruments", "Touch & Tell (US)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND | MACHINE_REQUIRES_ARTWORK )
SYST( 1980, tntellp,   tntell,   0,      tntell,   tntell,   tntell_state,   init_tntell,   "Texas Instruments", "Touch & Tell (US, patent)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND | MACHINE_REQUIRES_ARTWORK | MACHINE_NOT_WORKING )
SYST( 1981, tntelluk,  tntell,   0,      tntell,   tntell,   tntell_state,   init_tntell,   "Texas Instruments", "Touch & Tell (UK)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND | MACHINE_REQUIRES_ARTWORK )
SYST( 1981, tntellfr,  tntell,   0,      tntell,   tntell,   tntell_state,   init_tntell,   "Texas Instruments", "Le Livre Magique (France)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND | MACHINE_REQUIRES_ARTWORK )

SYST( 1982, vocaid,    0,        0,      vocaid,   tntell,   tntell_state,   empty_init,    "Texas Instruments", "Vocaid", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND | MACHINE_REQUIRES_ARTWORK )



socrates.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Jonathan Gevaryahu
/******************************************************************************
*
*  V-tech Socrates-series devices
*  Copyright (C) 2009-2021 Jonathan Gevaryahu AKA Lord Nightmare
*  with dumping help from Kevin 'kevtris' Horton
*
*  The devices in this driver all use a similar ASIC, presumably produced by
*  Toshiba for Vtech/Yeno, in a QFP package with 100 pins (30+20+30+20)
*  The asic variants seen in the wild are:
*  27-0769 TC17G032AF-0248 (Socrates NTSC)
*  27-0883 1732-8277 (Video Painter and Socrates PAL)
*
TODO (socrates):
    * The speech chip is a Toshiba tc8802AF (which is pin and speech
      compatible with the older Toshiba t6803, but adds vsm rom read mode and
      apparently does away with the melody mode); the chip is running at
      800khz clock/10khz output with between 1 and 4 t6684F vsm roms
      attached; create a sound driver for this!
    * hook up mouse
    * add waitstates for ram access (lack of this causes the system to run
      way too fast)
      This will require some probing with the LA and the fluke to figure out
      how many cycles the waitstates are for for rom/ram/etc access.
    * figure out what bit 6 of the status register actually does; is this an
      ir mcu busy flag?
    * keyboard IR decoder MCU is HLE'd for now, needs decap and cpu core
    * iqunlimz keyboard MCU simulation does not handle key repeats


  Socrates Educational Video System
        FFFF|----------------|
            | RAM (window 1) |
            |                |
        C000|----------------|
            | RAM (window 0) |
            |                |
        8000|----------------|
            | ROM (banked)   |
            | *Cartridge     |
        4000|----------------|
            | ROM (fixed)    |
            |                |
        0000|----------------|

    * cartridge lives in banks 10 onward, see below

        Banked rom area (4000-7fff) bankswitching
        Bankswitching is achieved by writing to I/O port 0 (mirrored on 1-7)
        Bank       ROM_REGION        Contents
        0          0x00000 - 0x03fff System ROM page 0
        1          0x04000 - 0x07fff System ROM page 1
        2          0x08000 - 0x0bfff System ROM page 2
        ... etc ...
        E          0x38000 - 0x38fff System ROM page E
        F          0x3c000 - 0x3ffff System ROM page F
        10         0x40000 - 0x43fff Expansion Cartridge page 0 (cart ROM 0x0000-0x3fff)
        11         0x44000 - 0x47fff Expansion Cartridge page 1 (cart ROM 0x4000-0x7fff)
        ... etc ...

        Banked ram area (z80 0x8000-0xbfff window 0 and z80 0xc000-0xffff window 1)
        Bankswitching is achieved by writing to I/O port 8 (mirrored to 9-F), only low nybble
        byte written: 0b****BBAA
        where BB controls ram window 1 and AA controls ram window 0
        hence:
        Write    [window 0]         [window 1]
        0        0x0000-0x3fff      0x0000-0x3fff
        1        0x4000-0x7fff      0x0000-0x3fff
        2        0x8000-0xbfff      0x0000-0x3fff
        3        0xc000-0xffff      0x0000-0x3fff
        4        0x0000-0x3fff      0x4000-0x7fff
        5        0x4000-0x7fff      0x4000-0x7fff
        6        0x8000-0xbfff      0x4000-0x7fff
        7        0xc000-0xffff      0x4000-0x7fff
        8        0x0000-0x3fff      0x8000-0xbfff
        9        0x4000-0x7fff      0x8000-0xbfff
        A        0x8000-0xbfff      0x8000-0xbfff
        B        0xc000-0xffff      0x8000-0xbfff
        C        0x0000-0x3fff      0xc000-0xffff
        D        0x4000-0x7fff      0xc000-0xffff
        E        0x8000-0xbfff      0xc000-0xffff
        F        0xc000-0xffff      0xc000-0xffff

******************************************************************************/

#include "emu.h"
#include "socrates_a.h"

#include "cpu/z80/z80.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "machine/bankdev.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class socrates_state : public driver_device
{
public:
	socrates_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_sound(*this, "soc_snd"),
		m_screen(*this, "screen"),
		m_cart(*this, "cartslot"),
		m_bios_reg(*this, "maincpu"),
		m_vram_reg(*this, "vram"),
		m_rombank1(*this, "rombank1"),
		m_rombank2(*this, "rombank2"),
		m_rambank1(*this, "rambank1"),
		m_rambank2(*this, "rambank2"),
		m_kbdrow(*this, "IN%u", 0)
	{ }

	void socrates(machine_config &config);
	void socrates_pal(machine_config &config);
	void vpainter_pal(machine_config &config);

	void init_socrates();
	void init_iqunlimz();
	void init_vpainter();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

	void socrates_mem(address_map &map) ATTR_COLD;
	void socrates_io(address_map &map) ATTR_COLD;
	void socrates_rambank_map(address_map &map) ATTR_COLD;
	void socrates_rombank_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<socrates_snd_device> m_sound;
	required_device<screen_device> m_screen;
	optional_device<generic_slot_device> m_cart;
	memory_region *m_cart_reg;
	required_memory_region m_bios_reg;
	required_shared_ptr<u8> m_vram_reg;
	required_device<address_map_bank_device> m_rombank1;
	optional_device<address_map_bank_device> m_rombank2; // iqunlimz only
	required_device<address_map_bank_device> m_rambank1;
	required_device<address_map_bank_device> m_rambank2;
	optional_ioport_array<0xC> m_kbdrow;

	uint8_t m_rom_bank[2];
	uint8_t m_ram_bank;
	uint16_t m_scroll_offset;
	uint16_t m_kb_spi_buffer;
	bool m_kb_spi_request;
	uint8_t m_kbmcu_type; // 0 for socrates, 1 for iqunlimz, 2 for vpainter
	uint16_t m_oldkeyvalue; // previous key pressed
	uint16_t m_keyrepeat_holdoffcounter; // keyrepeat holdoff countdown
	uint8_t m_io40_latch; // what was last written to speech reg (for open bus)?
	uint8_t m_speech_running; // is speech synth talking?
	uint32_t m_speech_address; // address in speech space
	uint8_t m_speech_settings; // speech settings (nybble 0: ? externrom ? ?; nybble 1: ? ? ? ?)
	uint8_t m_speech_dummy_read; // have we done a dummy read yet?
	uint8_t m_speech_load_address_count; // number of times load address has happened
	uint8_t m_speech_load_settings_count; // number of times load settings has happened

	emu_timer *m_kbmcu_sim_timer;
	emu_timer *m_clear_speech_timer;
	emu_timer *m_clear_irq_timer;

	struct
	{
		uint16_t   buffer[8];
		uint8_t    head;
		uint8_t    tail;
		uint8_t    count;
	} m_kb_queue;

	void palette_init(palette_device &palete) const;

	uint8_t common_rom_bank_r(offs_t offset);
	void common_rom_bank_w(offs_t offset, uint8_t data);
	uint8_t common_ram_bank_r();
	void common_ram_bank_w(uint8_t data);
	uint8_t cart_r(offs_t offset);
	uint8_t read_f3();
	void kbmcu_reset(uint8_t data);
	uint8_t status_r();
	void speech_command(uint8_t data);
	uint8_t keyboard_buffer_read(offs_t offset);
	void keyboard_buffer_update(uint8_t data);
	void kbmcu_sim_reset();
	void kbmcu_sim_fifo_enqueue(uint16_t data);
	uint16_t kbmcu_sim_fifo_dequeue();
	uint16_t kbmcu_sim_fifo_peek();
	void kbmcu_sim_fifo_head_clear();
	void reset_speech(uint8_t data);
	void scroll_w(offs_t offset, uint8_t data);
	void sound_w(offs_t offset, uint8_t data);

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	INTERRUPT_GEN_MEMBER(assert_irq);
	TIMER_CALLBACK_MEMBER(kbmcu_sim_cb);
	TIMER_CALLBACK_MEMBER(clear_speech_cb);
	TIMER_CALLBACK_MEMBER(clear_irq_cb);
	static rgb_t create_color(uint8_t color);
};


class iqunlimz_state : public socrates_state
{
public:
	iqunlimz_state(const machine_config &mconfig, device_type type, const char *tag) :
		socrates_state(mconfig, type, tag)
	{ }

	void iqunlimz(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER( send_input );

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void colors_w(offs_t offset, uint8_t data);
	uint8_t video_regs_r(offs_t offset);
	void video_regs_w(offs_t offset, uint8_t data);
	uint8_t status_r();

	void iqunlimz_io(address_map &map) ATTR_COLD;
	void iqunlimz_mem(address_map &map) ATTR_COLD;
	void iqunlimz_rambank_map(address_map &map) ATTR_COLD;
	void iqunlimz_rombank_map(address_map &map) ATTR_COLD;

	int get_color(int index, int y);

	uint8_t   m_colors[8];
	uint8_t   m_video_regs[4];

};

/* Defines */

// number of mcu "full cycles" before key repeat
#define KEYREPEAT_HOLDOFF 0x1f
// number of mcu "full cycles" between key repeats
#define KEYREPEAT_REPEAT 1

/* Components */

/* Devices */

/******************************************************************************

Socrates Keyboard MCU simulation

*******************************************************************************
the tmp50c40 MCU seems to have an 8? word, 12 bit wide internal fifo, which is fed by IR decoded from the
remote keyboard. The socrates reads this data out via SPI? by asserting a /cs? bit when keyboard_buffer_update
is written to.
the bits are returned in two registers, handled by keyboard_buffer_read
bits are read out in the following order:
offset0  offset1
76543210 76543210
HHHHMMMM F000LLLL
where HHHH is the high nybble, MMMM is the middle nybble, LLLL is the low nybble
and F is the fifo status, 1 if the fifo had any data in it at all, and 0 if not
if the fifo is empty, the mcu shifts out nothing, and the spi reg holds 00000000 00000001
******************************************************************************/

/******************************************************************************

IQUnlimited Keyboard MCU simulation

*******************************************************************************
iq unlimited has a different mcu than socrates, which has an 8-word 8-bit wide
fifo instead.
As in socrates, the bits are returned in two registers, handled by keyboard_buffer_read
bits are read out in the following order:
offset0  offset1
76543210 76543210
HHHHLLLL F000KACS
where HHHH is the high nybble, LLLL is the low nybble, KACS is capslock, alt, ctrl and shift,
and F is the fifo status, 1 if the fifo had any data in it at all, and 0 if not.
KACS are NOT from the fifo, but a live status of the key states.
F is also binary ORed with the vblank state???
if the fifo is empty, the mcu shifts out nothing, and the spi reg holds 00000000 F000KACS
******************************************************************************/

void socrates_state::kbmcu_sim_reset()
{
	m_kb_queue.head = m_kb_queue.tail = m_kb_queue.count = 0;
	for (uint8_t i = 0; i < (sizeof(m_kb_queue.buffer)/sizeof(m_kb_queue.buffer[0])); i++)
		m_kb_queue.buffer[i]=0;
}


void socrates_state::kbmcu_sim_fifo_enqueue(uint16_t data)
{
	//logerror("kbmcu_sim_fifo_enqueue called with %02x, fifo count was %d\n", data, m_kb_queue.count);
	if (m_kb_queue.count < 8)
	{
		m_kb_queue.buffer[m_kb_queue.tail] = data;
		m_kb_queue.tail = (m_kb_queue.tail + 1) % (sizeof(m_kb_queue.buffer)/sizeof(m_kb_queue.buffer[0]));
		m_kb_queue.count++;
	}
}

uint16_t socrates_state::kbmcu_sim_fifo_dequeue()
{
	if (m_kb_queue.count == 0) fatalerror("kbmcu_sim_fifo_dequeue called with queue count of zero. This should never happen, contact MAMEDEV!");
	uint16_t retval = m_kb_queue.buffer[m_kb_queue.head];
	m_kb_queue.count--;
	m_kb_queue.head = (m_kb_queue.head + 1) % (sizeof(m_kb_queue.buffer)/sizeof(m_kb_queue.buffer[0]));
	//logerror("kbmcu_sim_fifo_dequeue was called, returning %02x, fifo count is now %d\n", retval, m_kb_queue.count);
	return retval;
}

uint16_t socrates_state::kbmcu_sim_fifo_peek()
{
	if (m_kb_queue.count == 0) fatalerror("kbmcu_sim_fifo_peek called with queue count of zero. This should never happen, contact MAMEDEV!");
	uint16_t retval = m_kb_queue.buffer[m_kb_queue.head];
	//logerror("kbmcu_sim_fifo_peek was called, returning %02x, fifo count is now %d\n", retval, m_kb_queue.count);
	return retval;
}

void socrates_state::kbmcu_sim_fifo_head_clear()
{
	m_kb_queue.buffer[m_kb_queue.head] = 0;
}

void socrates_state::machine_start()
{
	m_kbmcu_sim_timer = timer_alloc(FUNC(socrates_state::kbmcu_sim_cb), this);
	m_kbmcu_sim_timer->adjust(attotime::from_hz((XTAL(21'477'272)/6)/3000)); // timer rate is a massive guess, depends on instructions per loop of mcu
	m_clear_speech_timer = timer_alloc(FUNC(socrates_state::clear_speech_cb), this);
	m_clear_irq_timer = timer_alloc(FUNC(socrates_state::clear_irq_cb), this);
	save_item(NAME(m_rom_bank));
	save_item(NAME(m_ram_bank));
	save_item(NAME(m_scroll_offset));
	save_item(NAME(m_kb_spi_request));
	save_item(NAME(m_kb_spi_buffer));
	save_item(NAME(m_oldkeyvalue));
	save_item(NAME(m_keyrepeat_holdoffcounter));
	save_item(NAME(m_io40_latch));
	save_item(NAME(m_speech_running));
	save_item(NAME(m_speech_address));
	save_item(NAME(m_speech_settings));
	save_item(NAME(m_speech_dummy_read));
	save_item(NAME(m_speech_load_address_count));
	save_item(NAME(m_speech_load_settings_count));
	m_rom_bank[0] = m_rom_bank[1] = 0x0;
	m_rombank1->set_bank(0x0); // actually set semi-randomly on real console but we need to initialize it somewhere...
	m_ram_bank = 0;
	m_rambank1->set_bank(0x0);// the actual console sets it semi randomly on power up, and the bios cleans it up.
	m_rambank2->set_bank(0x0);
	// ASIC SPI buffer starts with garbage in it for one word, simulate this
	m_kb_spi_buffer = 0xFF8F;
}

void socrates_state::machine_reset()
{
	m_cart_reg = m_cart ? memregion(util::string_format("%s%s", m_cart->tag(), GENERIC_ROM_REGION_TAG).c_str()) : nullptr;
	kbmcu_sim_reset();
	m_kb_spi_request = true;
	m_oldkeyvalue = 0;
	m_keyrepeat_holdoffcounter = KEYREPEAT_HOLDOFF;
	m_io40_latch = 0;
	m_speech_running = 0;
	m_speech_address = 0;
	m_speech_settings = 0;
	m_speech_dummy_read = 0;
	m_speech_load_address_count = 0;
	m_speech_load_settings_count = 0;
}

void socrates_state::init_socrates()
{
	/* fill vram with its init powerup bit pattern, so startup has the checkerboard screen */
	for (int i = 0; i < 0x10000; i++)
		m_vram_reg[i] = (((i&0x1)?0x00:0xFF)^((i&0x100)?0x00:0xff));
	m_maincpu->set_clock_scale(0.45); /// TODO: RAM access waitstates etc. aren't emulated - slow the CPU to compensate
	m_kbmcu_type = 0;
}

void socrates_state::init_iqunlimz()
{
	/* fill vram with its init powerup bit pattern, so startup has the checkerboard screen... is this even right for the iqunlimz? */
	for (int i = 0; i < 0x20000; i++)
		m_vram_reg[i] = (((i&0x1)?0x00:0xFF)^((i&0x100)?0x00:0xff));
	//m_maincpu->set_clock_scale(0.45); /// TODO: RAM access waitstates etc. aren't emulated - slow the CPU to compensate
	m_kbmcu_type = 1;
}

void socrates_state::init_vpainter()
{
	/* fill vram with its init powerup bit pattern, so startup has the checkerboard screen */
	for (int i = 0; i < 0x10000; i++)
		m_vram_reg[i] = (((i&0x1)?0x00:0xFF)^((i&0x100)?0x00:0xff));
	m_maincpu->set_clock_scale(0.45); /// TODO: RAM access waitstates etc. aren't emulated - slow the CPU to compensate
	m_kbmcu_type = 2;
}

uint8_t socrates_state::common_rom_bank_r(offs_t offset)
{
	return m_rom_bank[offset];
}

void socrates_state::common_rom_bank_w(offs_t offset, uint8_t data)
{
	m_rom_bank[offset] = data;
	if (offset && m_rombank2)
		m_rombank2->set_bank(data);
	else
		m_rombank1->set_bank(data);
}

uint8_t socrates_state::common_ram_bank_r()
{
	return m_ram_bank;
}

void socrates_state::common_ram_bank_w(uint8_t data)
{
	m_ram_bank = data;
	m_rambank1->set_bank(((data>>2) & 0x0c) | ((data>>0) & 0x03));
	m_rambank2->set_bank(((data>>4) & 0x0c) | ((data>>2) & 0x03));
}

uint8_t socrates_state::cart_r(offs_t offset)
{
	///TODO: do m_rombank->space(AS_PROGRAM).install_write_handler(0x0002, 0x0002, write8_delegate(FUNC(dac_byte_interface::data_w), (dac_byte_interface *)m_dac)); style stuff
	// demangle the offset, offset passed is bits 11111111 11111111 00000000 00000000
	// where . is 0                               EDCBA987 65432.10 FEDCBA98 76543210
	offset = ((offset&0x3FFFF)|((offset&0xF80000)>>1));
	if (m_cart_reg)
	{
		assert(m_cart);
		offset &= m_cart->get_rom_size()-1;
		return *(m_cart_reg->base()+offset);
	}
	else
		return 0xF3;
}

uint8_t socrates_state::read_f3()// used for read-only i/o ports as mame/mess doesn't have a way to set the unmapped area to read as 0xF3
{
	return 0xF3;
}

void socrates_state::kbmcu_reset(uint8_t data) // reset the keyboard MCU, clear its fifo
{
	//logerror("0x%04X: kbmcu written with %02X!\n", m_maincpu->pc(), data); //if (m_maincpu->pc() != 0x31D)
	kbmcu_sim_reset();
}

uint8_t socrates_state::status_r()// read 0x4x, some sort of status reg
{
// bit 7 - speech status: high when speech is playing, low when it is not (or when speech cart is not present)
// bit 6 - unknown, usually set, possibly mcu ready state?
// bit 5 - vblank status, high when not in vblank
// bit 4 - hblank status, high when not in hblank
// bit 3 - speech chip bit 3
// bit 2 - speech chip bit 2
// bit 1 - speech chip bit 1
// bit 0 - speech chip bit 0
	uint8_t *speechromint = memregion("speechint")->base();
	uint8_t *speechromext = memregion("speechext")->base();
	int temp = 0;
	temp |= (m_speech_running)?0x80:0;
	temp |= (1)?0x40:0; // unknown, possibly IR mcu ready?
	temp |= (m_screen->vblank())?0:0x20;
	temp |= (m_screen->hblank())?0:0x10;
	switch(m_io40_latch&0xF0) // what was last opcode sent?
	{
		case 0x60: case 0xE0:// speech status 'read' register
			if(m_speech_settings&0x04) // external speech roms (outside of speech ic but still in cart) enabled
			{
			logerror("reading external speech rom nybble from nybble address %x (byte address %x)\n",m_speech_address, m_speech_address>>1);
			temp |= ((speechromext[((m_speech_address>>1)&0xffff)]>>((m_speech_address&1)*4))&0xF);
			}
			else
			{
			logerror("reading internal speech rom nybble from nybble address %x (byte address %x)\n",m_speech_address, m_speech_address>>1);
			temp |= ((speechromint[((m_speech_address>>1)&0x1fff)]>>((m_speech_address&1)*4))&0xF);
			}
			if (m_speech_dummy_read == 0) // if we havent done the dummy read yet, do so now
			{
				m_speech_dummy_read++;
			}
			else
			{
				m_speech_address++;
			}
			break;
		default:
			temp |= m_io40_latch&0xF; // read open bus
			break;
	}
	logerror("read from i/o 0x4x of %x\n", temp);
	return temp;
}

TIMER_CALLBACK_MEMBER(socrates_state::clear_speech_cb)
{
	m_speech_running = 0;
	m_speech_load_address_count = 0; // should this be here or in the write functuon subpart which is speak command?
	m_speech_load_settings_count = 0;
}

void socrates_state::speech_command(uint8_t data) // write 0x4x
{
	/*
	 * 76543210
	 * |||||||\-- SEL0
	 * ||||||\--- SEL1
	 * |||||\---- SEL2
	 * ||||\----- SEL3
	 * |||\------ SEL4
	 * ||\------- SEL5
	 * |\-------- 64UP (if LOW the chip is in 'dumb' mode and can only play 64 fixed phrases; if HIGH the chip is in 'cpu controlled' mode and commands work) (not 100% sure about this but suspect it is correct)
	 * \--------- START (in cpu mode must be toggled from low->high for a cpu command to 'take'; in 'dumb' mode must be toggled low->high to start that 'word')
	 */
	logerror("write to i/o 0x4x of %x\n", data);
/*
// old readback test, probably get rid of this junk:
// 00-0f: readback: 70-7f
// 10-1f: readback: 70-7f
// 20-2f: readback: 70-7f
// 30-3f: readback: 70-7f
// 40-5f: readback: 70-7f
// 50-5f: readback: 70-7f
// 60-6f: readback: ALL 7f
// 70-7f: readback: 50, 71-7f (force vblank?)
// 80-8f: 80 starts speech reads as f0, rest read as 71-7f
// 90-9f: all 70-7f
// a0-af: 70-7f
// b0-bf: 70-7f
// c0-cf: 70-7f
// d0-df: 70-7f
// e0-ef: readback ALL 76
// f0-ff: 70-7f
*/
/* The speech chip is a toshiba tc8802, it is NOT a t6803 since the t6803
   does not have the rom read mode, which the socrates definitely uses! */
/* Commands (tc8802):
SEL 5 4 3 2 1 0
    0 0 n n n n -  ADLD - ADdress LoaD - writes one nybble to the address
                          of vsm rom selected (internal or external) starting
                          from the low 4 bits; each subsequent write writes to
                          the next higher 4 bits; resetting the chip resets
                          the position
    0 1 n n n n -  CNDT - CoNDiTion (aka 'setup') - writes one nybble to the
                          setting register, starting from the low 4 bits; the
                          subsequent write will be to the high 4 bits;
                          resetting the chip will reset the position

                          CNDT bits (? are from T6803 datasheet, which could be wrong on the tc8802AF here):
                           * 76543210
                           * |||||||\-- ?bits/frame bit 1 (0 = see bit 2; 1 = 98bits/frame (these may be backwards))
                           * ||||||\--- ?melody mode select if high
                           * |||||\---- speech rom select; 0 is internal 8k mask, 1 is external vsm bus
                           * ||||\----- ?voiced source select (0 = A; 1 = B)
                           * |||\------ ?filter stages (0 = lpc-10; 1 = lpc-8)
                           * ||\------- ?always 0
                           * |\-------- ?frame length (0 = 20ms; 1 = 10ms)
                           * \--------- ?bits/frame bit 2 (if bit 1 is not set: 0 = 56 bits/frame; 1 = 50 bits/frame)
                           *
    1 0 x x x x -  DTRD - DaTa ReaD - reads a nybble from the address as loaded
                          and increments the address by one nybble. The first
                          DTRD command after any other commands is a 'dummy
                          read' which serves to reset the bus direction of the
                          low 4 SEL lines, identical to the way the TMS5100
                          does things. it doesn't affect the bus direction of
                          the high two SEL lines.
    1 1 0 x x x - START - Starts speech at the address as loaded.
    1 1 1 x x x - RESET - Resets the internal chip state (clears shifters and
                          counters) and forces speech to end/silence
                          immediately.
*/
	switch(data&0xF8)
	{
		case 0x80: case 0x88: case 0x90: case 0x98: case 0xA0: case 0xA8: case 0xB0: case 0xB8:
			/* 'dumb' mode speech command: write me: start talking */
			m_speech_running = 1;
			m_clear_speech_timer->adjust(attotime::from_seconds(4)); // hack
			break;
		case 0xC0: case 0xC8: // ADLD: load address to vsm
			m_speech_address |= (((int)data&0xF)<<(m_speech_load_address_count*4))<<1;
			m_speech_load_address_count++;
			logerror("loaded address nybble %X, byte address is currently %5X with %d nybbles loaded\n", data&0xF, m_speech_address>>1, m_speech_load_address_count);
			break;
		case 0xD0: case 0xD8: // CNDT: load settings
			m_speech_settings |= ((data&0xF)<<(m_speech_load_settings_count*4));
			m_speech_load_settings_count++;
			break;
		case 0xE0: case 0xE8: // DTRD: read byte, handled elsewhere
			break;
		case 0xF0: // SPEAK
			m_speech_running = 1;
			m_clear_speech_timer->adjust(attotime::from_seconds(4)); // hack
			break;
		case 0xF8: // RESET
			m_speech_running = 0;
			m_speech_address = 0;
			m_speech_settings = 0;
			m_speech_dummy_read = 0;
			m_speech_load_address_count = 0;
			m_speech_load_settings_count = 0;
			m_io40_latch &= 0x0f; // set last command to 0 to prevent problems
			break;
		default: // 00 through 70 are packets without the write bit set, ignore them
			break;
	}
	m_io40_latch = data;
}

uint8_t socrates_state::keyboard_buffer_read(offs_t offset)
{
	if (m_kbmcu_type == 0)
	{
		if (offset == 1) return m_kb_spi_buffer&0xFF;
		else return (m_kb_spi_buffer&0xFF00)>>8;
	}
	else // if ( (m_kbmcu_type == 1) || (m_kbmcu_type == 2) ) // iqunlimz hack, the system won't work without this?!?!
	{
		if (offset == 1) return (m_screen->vblank()?0x80:0)|(m_kbdrow[0]->read()&0xf);
		else return (m_kb_spi_buffer&0xFF00)>>8;
	}
}

void socrates_state::keyboard_buffer_update(uint8_t data)
{
	m_kb_spi_request = true;
	m_kb_spi_buffer = 0x0001;
}

void socrates_state::reset_speech(uint8_t data)// i/o 60: reset speech synth
{
	m_speech_running = 0;
	m_speech_address = 0;
	m_speech_settings = 0;
	m_speech_dummy_read = 0;
	m_speech_load_address_count = 0;
	m_speech_load_settings_count = 0;
	m_io40_latch &= 0x0f; // set last command to 0 to prevent problems
logerror("write to i/o 0x60 of %x\n",data);
}

/* stuff below belongs in video/socrates.c */
/* graphics section:
    0x20 - W - lsb offset of screen display
    0x21 - W - msb offset of screen display
    resulting screen line is one of 512 total offsets on 128-byte boundaries in the whole 64k ram
    */
void socrates_state::scroll_w(offs_t offset, uint8_t data)
{
	if (offset == 0)
	m_scroll_offset = (m_scroll_offset&0x100) | data;
	else
	m_scroll_offset = (m_scroll_offset&0xFF) | ((data&1)<<8);
}

/* NTSC-based Palette stuff */
// max for I and Q
#define M_I 0.5957
#define M_Q 0.5226
	/* luma amplitudes, measured on scope */
#define LUMAMAX 1.420
#define LUMA_COL_0 0.355, 0.139, 0.205, 0, 0.569, 0.355, 0.419, 0.205, 0.502, 0.288, 0.358, 0.142, 0.720, 0.502, 0.571, 0.358,
#define LUMA_COL_COMMON 0.52, 0.52, 0.52, 0.52, 0.734, 0.734, 0.734, 0.734, 0.667, 0.667, 0.667, 0.667, 0.885, 0.885, 0.885, 0.885,
#define LUMA_COL_2 0.574, 0.6565, 0.625, 0.71, 0.792, 0.87, 0.8425, 0.925, 0.724, 0.8055, 0.7825, 0.865, 0.94275, 1.0225, 0.99555, 1.07525,
#define LUMA_COL_5 0.4585, 0.382, 0.4065, 0.337, 0.6715, 0.5975, 0.6205, 0.5465, 0.6075, 0.531, 0.5555, 0.45, 0.8255, 0.7455, 0.774, 0.6985,
#define LUMA_COL_F 0.690, 0.904, 0.830, 1.053, 0.910, 1.120, 1.053, 1.270, 0.840, 1.053, 0.990, 1.202, 1.053, 1.270, 1.202, 1.420
	/* chroma amplitudes, measured on scope */
#define CHROMAMAX 0.42075
#define CHROMA_COL_COMMON 0.148, 0.3125, 0.26475, 0.42075, 0.148, 0.3125, 0.26475, 0.42075, 0.148, 0.3125, 0.26475, 0.42075, 0.148, 0.3125, 0.26475, 0.42075,
#define CHROMA_COL_2 0.125125, 0.27525, 0.230225, 0.384875, 0.125125, 0.27525, 0.230225, 0.384875, 0.125125, 0.27525, 0.230225, 0.384875, 0.125125, 0.27525, 0.230225, 0.384875,
#define CHROMA_COL_5 0.1235, 0.2695, 0.22625, 0.378, 0.1235, 0.2695, 0.22625, 0.378, 0.1235, 0.2695, 0.22625, 0.378, 0.1235, 0.2695, 0.22625, 0.378,
// gamma: this needs to be messed with... may differ on different systems... attach to slider somehow?
#define GAMMA 1.5

rgb_t socrates_state::create_color(uint8_t color)
{
	static constexpr double lumatable[256] = {
			LUMA_COL_0
			LUMA_COL_COMMON
			LUMA_COL_2
			LUMA_COL_COMMON
			LUMA_COL_COMMON
			LUMA_COL_5
			LUMA_COL_COMMON
			LUMA_COL_COMMON
			LUMA_COL_COMMON
			LUMA_COL_COMMON
			LUMA_COL_COMMON
			LUMA_COL_COMMON
			LUMA_COL_COMMON
			LUMA_COL_COMMON
			LUMA_COL_COMMON
			LUMA_COL_F };
	static constexpr double chromaintensity[256] = {
			0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
			CHROMA_COL_COMMON
			CHROMA_COL_2
			CHROMA_COL_COMMON
			CHROMA_COL_COMMON
			CHROMA_COL_5
			CHROMA_COL_COMMON
			CHROMA_COL_COMMON
			CHROMA_COL_COMMON
			CHROMA_COL_COMMON
			CHROMA_COL_COMMON
			CHROMA_COL_COMMON
			CHROMA_COL_COMMON
			CHROMA_COL_COMMON
			CHROMA_COL_COMMON
			0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
	/* chroma colors and phases:
	 0: black-through-grey (0 assumed chroma)
	 1: purple (90 chroma seems correct)
	 2: light blue/green (210 or 240 chroma, 210 seems slightly closer)
	 3: bright blue (150 seems correct)
	 4: green (270 seems correct)
	 5: red (30 seems correct, does have some blue in it)
	 6: orange (0 seems correct, does have some red in it)
	 7: yellow/gold (330 is closest but conflicts with color C, hence 315 seems close, and must have its own delay line separate from the other phases which use a standard 12 phase scheme)
	 8: blue with a hint of green in it (180 seems correct)
	 9: blue-green (210 seems correct)
	 A: forest green (240 seems correct)
	 B: yellow-green (300 seems correct)
	 C: yellow-orange (330 is close but this conflicts with color 7, and is not quite the same; color 7 has more green in it than color C)
	 D: magenta (60 is closest)
	 E: blue-purple (more blue than color 1, 120 is closest)
	 F: grey-through-white (0 assumed chroma)
	*/
	static constexpr double phaseangle[16] = { 0, 90, 220, 150, 270, 40, 0, 315, 180, 210, 240, 300, 330, 60, 120, 0 }; // note: these are guessed, not measured yet!
	int const chromaindex = color&0x0F;
	int const swappedcolor = ((color&0xf0)>>4)|((color&0x0f)<<4);
	double finalY = (1/LUMAMAX) * lumatable[swappedcolor];
	double const finalI = (M_I * (cos((phaseangle[chromaindex]/180)*M_PI)))* ((1/CHROMAMAX)*chromaintensity[swappedcolor]);
	double const finalQ = (M_Q * (sin((phaseangle[chromaindex]/180)*M_PI)))* ((1/CHROMAMAX)*chromaintensity[swappedcolor]);
	if (finalY > 1) finalY = 1; // clamp luma
	// calculate the R, G and B values here, neato matrix math
	double finalR = (finalY*1)+(finalI*0.9563)+(finalQ*0.6210);
	double finalG = (finalY*1)+(finalI*-0.2721)+(finalQ*-0.6474);
	double finalB = (finalY*1)+(finalI*-1.1070)+(finalQ*1.7046);
	// scale/clamp to 0-255 range
	if (finalR<0) finalR = 0;
	if (finalR>1) finalR = 1;
	if (finalG<0) finalG = 0;
	if (finalG>1) finalG = 1;
	if (finalB<0) finalB = 0;
	if (finalB>1) finalB = 1;
	// gamma correction: 1.0 to GAMMA:
	finalR = pow(finalR, 1/GAMMA)*255;
	finalG = pow(finalG, 1/GAMMA)*255;
	finalB = pow(finalB, 1/GAMMA)*255;
	return rgb_t((int)finalR,(int)finalG,(int)finalB);
}


void socrates_state::palette_init(palette_device &palette) const
{
	for (int i = 0; i < 256; i++)
		palette.set_pen_color(i, create_color(i));
}

void socrates_state::video_start()
{
	m_scroll_offset = 0;
}

uint32_t socrates_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	static const uint8_t fixedcolors[8] = {
			0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xF7 };
	uint8_t const *const videoram = m_vram_reg;
	int lineoffset = 0; // if display ever tries to display data at 0xfxxx, offset line displayed by 0x1000
	for (int y = 0; y < 228; y++)
	{
		if ((((y+m_scroll_offset)*128)&0xffff) >= 0xf000) lineoffset = 0x1000; // see comment above
		for (int x = 0; x < 264; x++)
		{
			int color;
			if (x < 256)
			{
				int colidx =videoram[(((y+m_scroll_offset)*128)+(x>>1)+lineoffset)&0xffff];
				if (x&1) colidx >>=4;
				colidx &= 0xF;
				if (colidx > 7) color=videoram[0xF000+(colidx<<8)+((y+m_scroll_offset)&0xFF)];
				else color=fixedcolors[colidx];
			}
			else
			{
				int colidx = videoram[(((y+m_scroll_offset)*128)+(127)+lineoffset)&0xffff];
				colidx >>=4;
				colidx &= 0xF;
				if (colidx > 7) color=videoram[0xF000+(colidx<<8)+((y+m_scroll_offset)&0xFF)];
				else color=fixedcolors[colidx];
			}
			bitmap.pix(y, x) = color;
		}
	}
	return 0;
}

/* below belongs in audio/socrates.cpp */

void socrates_state::sound_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
		case 0:
		m_sound->reg0_w(data);
		break;
		case 1:
		m_sound->reg1_w(data);
		break;
		case 2:
		m_sound->reg2_w(data);
		break;
		case 3:
		m_sound->reg3_w(data);
		break;
		case 4: case 5: case 6: case 7: default:
		m_sound->reg4_w(data);
		break;
	}
}



//-----------------------------------------------------------------------------
//
//        IQ Unlimited
//
//-----------------------------------------------------------------------------

int iqunlimz_state::get_color(int index, int y)
{
	if (index < 8)
		return m_colors[index];
	else
		return m_vram_reg[0xf000 + ((index & 0x0f) << 8) + ((m_scroll_offset + y + 1) & 0xff)];
}

uint32_t iqunlimz_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint8_t *videoram = m_vram_reg;

	// bitmap layer
	for (int y=0; y<224; y++)
	{
		if (y >= m_video_regs[0x03])    break;
		for (int x=0; x<128; x++)
		{
			uint8_t data = videoram[(m_scroll_offset + y) * 0x80 + x];

			for(int b=0; b<2; b++)
			{
				bitmap.pix(y, x*2 + b) = get_color(data & 0x0f, y);
				data >>= 4;
			}
		}
	}

	// text layer
	bool mode_80 = m_video_regs[0x02] & 0x02;   // 80 chars mode
	int line_len = mode_80 ? 80 : 40;
	for (int y=0; y<28; y++)
	{
		for (int x=0; x<line_len; x++)
		{
			uint8_t c = videoram[0x8400 + (y - 1) * (mode_80 ? 0x80 : 0x40) + x];
			uint8_t *gfx = &videoram[0x8000 + (c & 0x7f) * 8];

			for (int cy=0; cy<8; cy++)
			{
				int py = y * 8 + cy;
				if ((m_video_regs[0x02] & 0x01) || py >= m_video_regs[0x03])
				{
					uint8_t col0 = get_color(0x0e, py);
					uint8_t col1 = get_color(0x0f, py);
					uint8_t data = 0;

					if (BIT(m_video_regs[0x02],4) && m_video_regs[0x00] == x && m_video_regs[0x01] == ((y - 1) + (0x400 / (mode_80 ? 0x80 : 0x40)))) // cursor position start at 0x8000
						data = 0xff;
					else if (y > 0 && y < 26)
						data = gfx[cy] ^ (c & 0x80 ? 0xff : 0);

					if (x == 0) bitmap.plot_box(0, py, 8 + line_len*6 + 8, 1, col0);
					for (int cx=0; cx<6; cx++)
					{
						int px = 8 + x*6 + cx;
						bitmap.pix(py, px) = BIT(data, 7) ? col1 : col0;
						data <<= 1;
					}
				}
			}
		}
	}

	return 0;
}

uint8_t iqunlimz_state::status_r()
{
	// ---x ----    main battery status
	// --x- ----    backup battery status

	return 0x30;
}

uint8_t iqunlimz_state::video_regs_r(offs_t offset)
{
	return m_video_regs[offset];
}

void iqunlimz_state::video_regs_w(offs_t offset, uint8_t data)
{
	if (offset == 2 && ((m_video_regs[offset] ^ data) & 0x02))
	{
		rectangle visarea = m_screen->visible_area();
		visarea.set(0, (data & 0x02 ? 496 : 256) - 1, 0, 224 - 1);
		m_screen->configure(data & 0x02 ? 496 : 256 , 224, visarea, m_screen->frame_period().attoseconds());
	}

	m_video_regs[offset] = data;
}

void iqunlimz_state::machine_reset()
{
	socrates_state::machine_reset();

	memset(m_colors, 0, 8);
	memset(m_video_regs, 0, 4);
	kbmcu_sim_reset();
}

void iqunlimz_state::colors_w(offs_t offset, uint8_t data)
{
	m_colors[offset] = data;
}


// IQ Unlimited keyboard MCU simulation

INPUT_CHANGED_MEMBER( iqunlimz_state::send_input )
{
	uint8_t data = (uint8_t)param;

	if (newval)
	{
		kbmcu_sim_fifo_enqueue(data<<4);
	}
}

/******************************************************************************
 Address Maps
******************************************************************************/

void socrates_state::socrates_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).rom(); /* system rom, bank 0 (fixed) */
	map(0x4000, 0x7fff).m(m_rombank1, FUNC(address_map_bank_device::amap8)); /* banked rom space; system rom is banks 0 through F, cartridge rom is banks 10 onward, usually banks 10 through 17. area past the end of the cartridge, and the whole 10-ff area when no cartridge is inserted, reads as 0xF3 */
	map(0x8000, 0xbfff).m(m_rambank1, FUNC(address_map_bank_device::amap8)); /* banked ram 'window' 0 */
	map(0xc000, 0xffff).m(m_rambank2, FUNC(address_map_bank_device::amap8)); /* banked ram 'window' 1 */
}

void socrates_state::socrates_rombank_map(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x03ffff).rom().region("maincpu", 0).mirror(0xF00000);      // xxxx 00** **** **** **** ****
	map(0x040000, 0x07ffff).r(FUNC(socrates_state::cart_r)).select(0xF80000);            // **** *1** **** **** **** ****
	map(0x080000, 0x0bffff).r(FUNC(socrates_state::read_f3));                                        // xxxx 10** **** **** **** ****
}

void socrates_state::socrates_rambank_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).ram().share("vram").mirror(0x30000);
}

void socrates_state::socrates_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x00).rw(FUNC(socrates_state::common_rom_bank_r), FUNC(socrates_state::common_rom_bank_w)).mirror(0x7); /* rom bank select - RW - 8 bits */
	map(0x08, 0x08).rw(FUNC(socrates_state::common_ram_bank_r), FUNC(socrates_state::common_ram_bank_w)).mirror(0x7); /* ram banks select - RW - 4 low bits; Format: 0b****HHLL where LL controls whether window 0 points at ram area: 0b00: 0x0000-0x3fff; 0b01: 0x4000-0x7fff; 0b10: 0x8000-0xbfff; 0b11: 0xc000-0xffff. HH controls the same thing for window 1 */
	map(0x10, 0x17).rw(FUNC(socrates_state::read_f3), FUNC(socrates_state::sound_w)).mirror(0x8); /* sound section:
	0x10 - W - frequency control for channel 1 (louder channel) - 01=high pitch, ff=low; time between 1->0/0->1 transitions = (XTAL(21'477'272)/(512+256) / (freq_reg+1)) (note that this is double the actual frequency since each full low and high squarewave pulse is two transitions)
	0x11 - W - frequency control for channel 2 (softer channel) - 01=high pitch, ff=low; same equation as above
	0x12 - W - 0b***EVVVV enable, volume control for channel 1
	0x13 - W - 0b***EVVVV enable, volume control for channel 2
	0x14-0x17 - 0bE??????? enable, unknown for channel 3; produces well defined dmc waves when bit 7 is set, and one or two other bits
	This may be some sort of debug register for serial-dmc banging out some internal rom from the asic, maybe color data?
	No writes to ram seem to change the waveforms produced, in my limited testing.
	0x80 produces about a very very quiet 1/8 duty cycle wave at 60hz or so
	0xC0 produces a DMC wave read from an unknown address at around 342hz
	<todo: test the others, maybe take samples?>
	*/
	map(0x20, 0x21).rw(FUNC(socrates_state::read_f3), FUNC(socrates_state::scroll_w)).mirror(0xE);
	map(0x30, 0x30).rw(FUNC(socrates_state::read_f3), FUNC(socrates_state::kbmcu_reset)).mirror(0xF); /* resets the keyboard IR decoder MCU */
	map(0x40, 0x40).rw(FUNC(socrates_state::status_r), FUNC(socrates_state::speech_command)).mirror(0xF); /* reads status register for vblank/hblank/speech, also reads and writes speech module */
	map(0x50, 0x51).rw(FUNC(socrates_state::keyboard_buffer_read), FUNC(socrates_state::keyboard_buffer_update)).mirror(0xE); /* Keyboard fifo read, pop fifo on write */
	map(0x60, 0x60).rw(FUNC(socrates_state::read_f3), FUNC(socrates_state::reset_speech)).mirror(0xF); /* reset the speech module, or perhaps fire an NMI?  */
	map(0x70, 0xFF).r(FUNC(socrates_state::read_f3)); // nothing mapped here afaik
}

void iqunlimz_state::iqunlimz_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).m(m_rombank2, FUNC(address_map_bank_device::amap8));
	map(0x4000, 0x7fff).m(m_rombank1, FUNC(address_map_bank_device::amap8));
	map(0x8000, 0xbfff).m(m_rambank1, FUNC(address_map_bank_device::amap8));
	map(0xc000, 0xffff).m(m_rambank2, FUNC(address_map_bank_device::amap8));
}

void iqunlimz_state::iqunlimz_rombank_map(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x03ffff).rom().region("maincpu", 0).mirror(0xF00000);      // xxxx 00** **** **** **** ****
	map(0x040000, 0x07ffff).r(FUNC(iqunlimz_state::cart_r)).select(0xF80000);            // **** *1** **** **** **** ****
	map(0x080000, 0x0bffff).rom().region("maincpu", 0x40000).mirror(0xF00000);// xxxx 10** **** **** **** ****
}

void iqunlimz_state::iqunlimz_rambank_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3ffff).ram().share("vram");
}

void iqunlimz_state::iqunlimz_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x01).rw(FUNC(iqunlimz_state::common_rom_bank_r), FUNC(iqunlimz_state::common_rom_bank_w)).mirror(0x06);
	map(0x08, 0x08).rw(FUNC(iqunlimz_state::common_ram_bank_r), FUNC(iqunlimz_state::common_ram_bank_w)).mirror(0x07);
	map(0x10, 0x17).w(FUNC(iqunlimz_state::sound_w)).mirror(0x08);
	map(0x20, 0x21).w(FUNC(iqunlimz_state::scroll_w)).mirror(0x0E);
	// 30: writes an incrementing value here, once per keypress?
	// 40: some sort of serial select/reset or enable, related to 0x60
	map(0x50, 0x51).rw(FUNC(iqunlimz_state::keyboard_buffer_read), FUNC(iqunlimz_state::keyboard_buffer_update)).mirror(0xE);
	// 60: some sort of serial read/write port, related to 0x40
	map(0x70, 0x73).rw(FUNC(iqunlimz_state::video_regs_r), FUNC(iqunlimz_state::video_regs_w)).mirror(0x0C);
	map(0x80, 0x81).nopw(); // LCD
	map(0xb1, 0xb1).nopw();
	map(0xa0, 0xa0).r(FUNC(iqunlimz_state::status_r)).mirror(0x0F);
	map(0xe0, 0xe7).w(FUNC(iqunlimz_state::colors_w)).mirror(0x08);
}



/******************************************************************************
 Input Ports
******************************************************************************/

/* socrates keyboard codes:
keycode low
|   keycode high
|   |   key name
00  01  No key pressed
// pads on the sides of the kb; this acts like a giant bitfield, both dpads/buttons can send data at once
00  81  left dpad right
00  82  left dpad up
00  84  left dpad left
00  88  left dpad down
01  80  right dpad down
02  80  right dpad left
04  80  right dpad up
08  80  right dpad right
10  80  left red button
20  80  right red button
// top row (right to left)
44  82  ENTER
44  83  MENU
44  84  ANSWER
44  85  HELP
44  86  ERASE
44  87  divide_sign
44  88  multiply_sign
44  89  minus_sign
44  80  plus_sign
//second row (right to left)
43  81  0
43  82  9
43  83  8
43  84  7
43  85  6
43  86  5
43  87  4
43  88  3
43  89  2
43  80  1
// third row (right to left)
42  82  I/La
42  83  H/So
42  84  G/Fa
42  85  F/Mi
42  86  E/Re
42  87  D/Do
42  88  C/Ti.
42  89  B/La.
42  80  A/So.
42  81  hyphen/period
// fourth row (right to left)
41  81  S
41  82  R
41  83  Q/NEW
41  84  P/PLAY
41  85  O/PAUSE
41  86  N/Fa`
41  87  M/Mi`
41  88  L/Re`
41  89  K/Do`
41  80  J/Ti
// fifth row (right to left)
40  82  SPACE
40  83  Z
40  84  Y
40  85  X
40  86  W
40  87  V
40  88  U
40  89  T
50  80  SHIFT
// socrates mouse pad (separate from keyboard)
8x  8y  mouse movement
x: down = 1 (small) through 7 (large), up = 8 (small) through F (large)
y: right = 1 (small) through 7 (large), left = 8 (small) through F (large)
90  80  right click
A0  80  left click
B0  80  both buttons click
90  81  right click (mouse movement in queue, will be in regs after next latch clear)
A0  81  left click (mouse movement in queue, will be in regs after next latch clear)
B0  81  both buttons click (mouse movement in queue, will be in regs after next latch clear)
// socrates touch pad
// unknown yet, but probably uses the 60/70/c0/d0/e0/f0 low reg vals
*/
static INPUT_PORTS_START( socrates )

	PORT_START("IN5") // joypad keys
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD)) PORT_NAME("Left D-pad Right") // 00 81
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) PORT_NAME("Left D-pad Up") // 00 82
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) PORT_NAME("Left D-pad Left") // 00 84
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) PORT_NAME("Left D-pad Down") // 00 88
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_NAME("Right D-pad Down") // 01 80
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_NAME("Right D-pad Left") // 02 80
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_NAME("Right D-pad Up") // 04 80
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_NAME("Right D-pad Right") // 08 80
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_NAME("Left D-pad Button") // 10 80
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(RALT)) PORT_NAME("Right D-pad Button") // 20 80
	PORT_BIT(0xfc00, IP_ACTIVE_HIGH, IPT_UNUSED)
	/* alt w/left and right keypad keys swapped
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_NAME("Left D-pad Right") // 00 81
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_NAME("Left D-pad Up") // 00 82
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_NAME("Left D-pad Left") // 00 84
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_NAME("Left D-pad Down") // 00 88
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) PORT_NAME("Right D-pad Down") // 01 80
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) PORT_NAME("Right D-pad Left") // 02 80
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) PORT_NAME("Right D-pad Up") // 04 80
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD)) PORT_NAME("Right D-pad Right") // 08 80
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(RALT)) PORT_NAME("Left D-pad Button") // 10 80
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_NAME("Right D-pad Button") // 20 80
	PORT_BIT(0xfc00, IP_ACTIVE_HIGH, IPT_UNUSED)
	*/

	PORT_START("IN6") // lowest 'row' (technically the shift key is on the 5th row but it has its own keycode)
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PORT_NAME("SHIFT") // 5x xx
	PORT_BIT(0xfffe, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN0") // 5th row
	PORT_BIT(0x0003, IP_ACTIVE_HIGH, IPT_UNUSED) // 40 80 and 40 81
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') PORT_NAME("SPACE") // 40 82
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_NAME("Z") // 40 83
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_NAME("Y") // 40 84
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_NAME("X") // 40 85
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_NAME("W") // 40 86
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_NAME("V") // 40 87
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_NAME("U") // 40 88
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_NAME("T") // 40 89
	PORT_BIT(0xfc00, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN1") // 4th row
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_NAME("J/Ti") // 41 80
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_NAME("S") // 41 81
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_NAME("R") // 41 82
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_NAME("Q/NEW") // 41 83
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_NAME("P/PLAY") // 41 84
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_NAME("O/PAUSE") // 41 85
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_NAME("N/Fa'") // 41 86
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_NAME("M/Mi'") // 41 87
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_NAME("L/Re'") // 41 88
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_NAME("K/Do'") // 41 89
	PORT_BIT(0xfc00, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN2") // 3rd row
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_NAME("A/So.") // 42 80
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('-') PORT_NAME("-/.") // 42 81
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_NAME("I/La") // 42 82
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_NAME("H/So") // 42 83
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_NAME("G/Fa") // 42 84
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_NAME("F/Mi") // 42 85
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_NAME("E/Re") // 42 86
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_NAME("D/Do") // 42 87
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_NAME("C/Ti.") // 42 88
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_NAME("B/La.") // 42 89
	PORT_BIT(0xfc00, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN3") // 2nd row
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_NAME("1") // 43 80
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_NAME("0") // 43 81
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_NAME("9") // 43 82
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_NAME("8") // 43 83
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_NAME("7") // 43 84
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_NAME("6") // 43 85
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_NAME("5") // 43 86
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_NAME("4") // 43 87
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_NAME("3") // 43 88
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_NAME("2") // 43 89
	PORT_BIT(0xfc00, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN4") // 1st row
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('+') PORT_NAME("+") // 44 80
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_UNUSED) // 44 81
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("ENTER") // 44 82
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_NAME("MENU") // 44 83
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP) PORT_CHAR(UCHAR_MAMEKEY(PGUP)) PORT_NAME("ANSWER") // 44 84
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN) PORT_CHAR(UCHAR_MAMEKEY(PGDN)) PORT_NAME("HELP") // 44 85
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_NAME("ERASE") // 44 86
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_NAME("/") // 44 87
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR('*') PORT_NAME("*") // 44 88
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_NAME("-") // 44 89
	PORT_BIT(0xfc00, IP_ACTIVE_HIGH, IPT_UNUSED)

	// mouse goes here
INPUT_PORTS_END


static INPUT_PORTS_START( iqunlimz )
	PORT_START( "IN0" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_LSHIFT )  PORT_CODE( KEYCODE_RSHIFT )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_LCONTROL )  PORT_CODE( KEYCODE_RCONTROL )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_LALT )  PORT_CODE( KEYCODE_RALT )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_CAPSLOCK )
	PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START( "IN1" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_N ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x10)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_M ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x11)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_COMMA ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x12)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_B ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x13)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_C ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x14)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_Z ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x15)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_V ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x16)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_X ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x17)

	PORT_START( "IN2" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) // PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x20)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_2_PAD ) PORT_CODE( KEYCODE_DOWN ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x21)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_5_PAD ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x22)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_DEL_PAD ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x23)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_SLASH_PAD ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x24)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_8_PAD ) PORT_CODE( KEYCODE_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x25)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_ASTERISK ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x26)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_7_PAD ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x27)

	PORT_START( "IN3" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) // PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x30)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) // PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x31)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED ) // PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x32)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) // PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x33)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_F1 ) PORT_NAME( "Help" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x34)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_9 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x35)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_BACKSPACE ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x36)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_0 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x37)

	PORT_START( "IN4" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_H ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x40)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_J ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x41)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_K ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x42)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_G ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x43)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_D ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x44)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_A ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x45)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_F ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x46)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_S ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x47)

	PORT_START( "IN5" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) // PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x50)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) // PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x51)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED ) // PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x52)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) // PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x53)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_EQUALS ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x54)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_O ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x55)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_NUMLOCK ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x56)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_P ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x57)

	PORT_START( "IN6" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_Y ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x60)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_U ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x61)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_I ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x62)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_T ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x63)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_E ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x64)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_Q ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x65)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_R ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x66)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_W ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x67)

	PORT_START( "IN7" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_MINUS ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x70)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_SLASH ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x71)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED ) // PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x72)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_STOP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x73)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_QUOTE ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x74)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_L ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x75)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_ENTER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x76)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_COLON ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x77)

	PORT_START( "IN8" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_6 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x80)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_7 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x81)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_8 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x82)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_5 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x83)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_3 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x84)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x85)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_4 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x86)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_2 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x87)

	PORT_START( "IN9" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_4_PAD ) PORT_CODE( KEYCODE_LEFT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x90)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_3_PAD ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x91)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_6_PAD ) PORT_CODE( KEYCODE_RIGHT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x92)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_PLUS_PAD ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x93)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_0_PAD ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x94)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_9_PAD ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x95)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_MINUS_PAD ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x96)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_1_PAD ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0x97)

	PORT_START( "INA" )
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) // PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0xa0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) // PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0xa1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED ) // PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0xa2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) // PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0xa3)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_F2 ) PORT_NAME("Answer") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0xa4)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_ESC ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0xa5)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_SPACE ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0xa6)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE( KEYCODE_TAB ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(iqunlimz_state::send_input), 0xa7)
INPUT_PORTS_END

/******************************************************************************
 Machine Drivers
******************************************************************************/
TIMER_CALLBACK_MEMBER(socrates_state::clear_irq_cb)
{
	m_maincpu->set_input_line(0, CLEAR_LINE);
}

INTERRUPT_GEN_MEMBER(socrates_state::assert_irq)
{
	device.execute().set_input_line(0, ASSERT_LINE);
	m_clear_irq_timer->adjust(m_maincpu->cycles_to_attotime(44));
	// 44 is a complete and total guess, need to properly measure how many clocks/microseconds the int line is high for.
}

TIMER_CALLBACK_MEMBER(socrates_state::kbmcu_sim_cb)
{
	// timer rate is a massive guess; we're assuming the mcu runs at the same speed as the cpu does,
	// and the refresh rate depends on instructions per loop of mcu, which we've randomly guessed is 60 cycles.
	m_kbmcu_sim_timer->adjust(m_maincpu->cycles_to_attotime(3000));
	/// TODO: dump the mcus and get rid of this...
	if (m_kbmcu_type == 0)
	{
		// socrates keyboard MCU simulation: if a keyboard key is pressed, enqueue it into the fifo as needed
		uint16_t keyvalue = 0;
		// check for joypad buttons
		keyvalue = m_kbdrow[5]->read();
		if (keyvalue == 0) // if joypad wasn't pushed...
		{
			// next check for mouse movement.
			/// TODO: this isn't written yet
			// next check for basic keyboard "4x xx" stuff
			// check if shift is down
			keyvalue = (m_kbdrow[6]->read())?0x500:0;
			bool keyfound = false;
			// find what row and bit we're on...
			for (int8_t row = 4; row>=0; row--)
			{
				uint16_t tempkey = m_kbdrow[row]->read();
				if (tempkey != 0)
				{
					for (int8_t powerof2 = 9; ((powerof2 >= 0) && (keyfound == false)); powerof2--) // continue until we find the first key only
					{
						if ((tempkey&(1<<powerof2)) == (1<<powerof2))
						{
							keyvalue |= (0x400 | (row<<4) | powerof2);
							keyfound = true;
						}
					}
				}
			}
		}
		if (keyvalue != 0) // if a key is down...
		{
			if (keyvalue == m_oldkeyvalue) // and its the same key as it was the last run...
			{
				if (m_keyrepeat_holdoffcounter > 0) // and the key repeat holdoff counter is > 0...
				{
					m_keyrepeat_holdoffcounter--; // decrement the holdoff counter
				}
				else // the key repeat holdoff counter is zero
				{
					kbmcu_sim_fifo_enqueue(0x1000|keyvalue); // queue the key with bit 12 set as a flag that this is a repeat
					m_keyrepeat_holdoffcounter += KEYREPEAT_REPEAT; // increment the holdoff counter by the repeat value
				}
			}
			else // it isn't the same key as it was the last run
			{
				kbmcu_sim_fifo_enqueue(keyvalue); // queue the key
				m_keyrepeat_holdoffcounter = KEYREPEAT_HOLDOFF; // reset the holdoff counter
				m_oldkeyvalue = keyvalue; // set this new key to be the 'previous key'
			}
		}
		else // keyvalue is zero, reset the holdoff counter
		{
			m_keyrepeat_holdoffcounter = KEYREPEAT_HOLDOFF;
			m_oldkeyvalue = keyvalue; // set this new key to be the 'previous key'
		}
	}
	//else if (m_kbmcu_type == 1) // this is currently hacked around by the input system so no code here
	//{
	//}

	if ((m_kb_spi_request) && (m_kb_queue.count > 0)) // if the console requested data from the MCU AND the MCU has data to send...
	{
		uint16_t tempbuffer = 0;
		m_kb_spi_request = false;
		tempbuffer = kbmcu_sim_fifo_peek();
		if (tempbuffer&0x1000) // repeat bit was set
		{
			kbmcu_sim_fifo_head_clear(); // clear the head byte of the fifo, but leave it enqueued so it is returned next time. socrates wants this...
		}
		else tempbuffer = kbmcu_sim_fifo_dequeue();
		m_kb_spi_buffer = ((tempbuffer&0xFF0)<<4)|(tempbuffer&0xF)|0x80;
	}
}

void socrates_state::socrates(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(21'477'272)/6);  /* Toshiba TMPZ84C00AP @ 3.579545 MHz, verified, xtal is divided by 6 */
	m_maincpu->set_addrmap(AS_PROGRAM, &socrates_state::socrates_mem);
	m_maincpu->set_addrmap(AS_IO, &socrates_state::socrates_io);
	m_maincpu->set_vblank_int("screen", FUNC(socrates_state::assert_irq));
	config.set_maximum_quantum(attotime::from_hz(60));

	ADDRESS_MAP_BANK(config, "rombank1").set_map(&socrates_state::socrates_rombank_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);
	ADDRESS_MAP_BANK(config, "rambank1").set_map(&socrates_state::socrates_rambank_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);
	ADDRESS_MAP_BANK(config, "rambank2").set_map(&socrates_state::socrates_rambank_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_size(264, 228); // technically the screen size is 256x228 but super painter abuses what I suspect is a hardware bug to display repeated pixels of the very last pixel beyond this horizontal space, well into hblank
	m_screen->set_visarea(0, 263, 0, 219); // the last few rows are usually cut off by the screen bottom but are indeed displayed if you mess with v-hold
	m_screen->set_screen_update(FUNC(socrates_state::screen_update));
	m_screen->set_palette("palette");

	PALETTE(config, "palette", FUNC(socrates_state::palette_init), 256);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SOCRATES_SOUND(config, m_sound, XTAL(21'477'272)/(512+256)).add_route(ALL_OUTPUTS, "mono", 0.25); // this is correct, as strange as it sounds.

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "socrates_cart");

	/* Software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("socrates");
}

void socrates_state::socrates_pal(machine_config &config)
{
	socrates(config);

	m_maincpu->set_clock(XTAL(26'601'712)/8); // XTAL verified, divider NOT verified; this is a later ASIC so the divider may be different

	config.set_maximum_quantum(attotime::from_hz(50));

	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // not accurate
	m_screen->set_size(264, 238); // technically the screen size is 256x228 but super painter abuses what I suspect is a hardware bug to display repeated pixels of the very last pixel beyond this horizontal space, well into hblank
	m_screen->set_visarea(0, 263, 0, 229); // the last few rows are usually cut off by the screen bottom but are indeed displayed if you mess with v-hold
	m_screen->set_screen_update(FUNC(socrates_state::screen_update));

	m_sound->set_clock(XTAL(26'601'712)/(512+256)); // this is correct, as strange as it sounds.
}

void socrates_state::vpainter_pal(machine_config &config)
{
	socrates_pal(config);

	config.device_remove("cartslot");
	config.device_remove("cart_list");
}

void iqunlimz_state::iqunlimz(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'000'000)); /* not accurate */
	m_maincpu->set_addrmap(AS_PROGRAM, &iqunlimz_state::iqunlimz_mem);
	m_maincpu->set_addrmap(AS_IO, &iqunlimz_state::iqunlimz_io);
	m_maincpu->set_vblank_int("screen", FUNC(iqunlimz_state::assert_irq));

	ADDRESS_MAP_BANK(config, "rombank1").set_map(&iqunlimz_state::iqunlimz_rombank_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);
	ADDRESS_MAP_BANK(config, "rombank2").set_map(&iqunlimz_state::iqunlimz_rombank_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);
	ADDRESS_MAP_BANK(config, "rambank1").set_map(&iqunlimz_state::iqunlimz_rambank_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);
	ADDRESS_MAP_BANK(config, "rambank2").set_map(&iqunlimz_state::iqunlimz_rambank_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_screen_update(FUNC(iqunlimz_state::screen_update));
	m_screen->set_size(256, 224);
	m_screen->set_visarea(0, 256-1, 0, 224-1);
	m_screen->set_palette("palette");

	PALETTE(config, "palette", FUNC(iqunlimz_state::palette_init), 256);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SOCRATES_SOUND(config, m_sound, XTAL(21'477'272)/(512+256)).add_route(ALL_OUTPUTS, "mono", 0.25);

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, nullptr);
}

/******************************************************************************
 ROM Definitions
******************************************************************************/

ROM_START(socrates)
	ROM_REGION(0x400000, "maincpu", ROMREGION_ERASEVAL(0xF3)) /* can technically address 4mb of rom via bankswitching; open bus area reads as 0xF3 on fluke */
	/* Socrates English (same ROM for NTSC and PAL) */
	/* All cart ROMs are 28 pin 23c1000/tc531000 128Kx8 ROMs */
	/* Cart port pinout:
	(looking into end of disk-shaped cartridge with label/top side pointing to the right)
	A15 -> 19  18 -- VCC
	A14 -> 20  17 <- A16
	A13 -> 21  16 <- A12
	 A8 -> 22  15 <- A7
	 A9 -> 23  14 <- A6
	A11 -> 24  13 <- A5
	 A3 -> 25  12 <- A4
	 A2 -> 26  11 <- A10
	 D7 <- 27  10 <- A1
	 D6 <- 28  9 <- A0
	 D5 <- 29  8 -> D0
	 D4 <- 30  7 -> D1
	 D3 <- 31  6 -> D2
	  ? ?? 32  5 ?? ?
	A17 -> 33  4 ?? ?
	  ? ?? 34  3 ?? ?
	/CE -> 35  2 ?? ?
	GND -- 36  1 -- GND
	Note that a17 goes to what would be pin 2 on a rom chip if a 32 pin rom were installed, which is not the case. (pins 1, 31 and 32 would be tied to vcc)
	It is likely that at least one of the 6 unknown lines is R/W from the z80, and another may be phi1/m1/clock etc to allow for ram to live in cart space

	Cartridge check procedure by Socrates is, after screen init and check for speech synth,
	bankswitch to bank 0x10 (i.e. first 0x4000 of cart appears at 4000-7fff in z80 space),
	do following tests; if any tests fail, jump to 0x0015 (Socrates main menu)
	* read 0x7ff0(0x3ff0 in cart rom) and compare to 0xAA
	* read 0x7ff1(0x3ff1 in cart rom) and compare to 0x55
	* read 0x7ff2(0x3ff2 in cart rom) and compare to 0xE7
	* read 0x7ff3(0x3ff3 in cart rom) and compare to 0x18
	if all tests passed, jump to 0x4000 (0x0000 in cart rom)
	*/
	ROM_LOAD("27-00817-000-000.u1", 0x00000, 0x40000, CRC(80f5aa20) SHA1(4fd1ff7f78b5dd2582d5de6f30633e4e4f34ca8f)) // Label: "(Vtech) 27-00817-000-000 // (C)1987 VIDEO TECHNOLOGY // 8811 D"

	ROM_REGION(0x800, "kbmcu", ROMREGION_ERASEFF)
	ROM_LOAD("tmp42c40p1844.u6", 0x000, 0x200, NO_DUMP) /* keyboard IR decoder MCU */

	/* english speech cart has a green QC sticker */
	ROM_REGION(0x2000, "speechint", ROMREGION_ERASE00) // speech data inside of the speech chip; fill with 00, if no speech cart is present socrates will see this
	ROM_LOAD_OPTIONAL("speech_eng_internal.bin", 0x0000, 0x2000, CRC(edc1fb3f) SHA1(78b4631fc3b1c038e14911047f9edd6c4e8bae58)) // 8k on the speech chip itself

	ROM_REGION(0x10000, "speechext", ROMREGION_ERASE00) // speech serial modules outside of the speech chip but still on speech cart
	ROM_LOAD_OPTIONAL("speech_eng_vsm1.bin", 0x0000, 0x4000, CRC(888e3ddd) SHA1(33af6a21ba6d826071c9d48557b1c9012752570b)) // 16k in serial rom
	ROM_LOAD_OPTIONAL("speech_eng_vsm2.bin", 0x4000, 0x4000, CRC(de4ac89d) SHA1(3dfa853b02df756a9b72def94a39310992ee11c7)) // 16k in serial rom
	ROM_LOAD_OPTIONAL("speech_eng_vsm3.bin", 0x8000, 0x4000, CRC(972384aa) SHA1(ffcb1d633ca6bffc7f481ec505da447e5b847f16)) // 16k in serial rom
	ROM_FILL(0xC000, 0x4000, 0xff) // last vsm isn't present, FF fill
ROM_END

ROM_START(socratfc)
	ROM_REGION(0x80000, "maincpu", ROMREGION_ERASEVAL(0xF3))
	/* Socrates SAITOUT (French Canadian) NTSC */
	ROM_LOAD("27-00884-001-000.u1", 0x00000, 0x40000, CRC(042d9d21) SHA1(9ffc67b2721683b2536727d0592798fbc4d061cb)) // Label: "(Vtech) 27-00884-001-000 // (C)1988 VIDEO TECHNOLOGY // 8911 D"

	ROM_REGION(0x800, "kbmcu", ROMREGION_ERASEFF)
	ROM_LOAD("tmp42c40p1844.u6", 0x000, 0x200, NO_DUMP) /* keyboard IR decoder MCU */

	ROM_REGION(0x2000, "speechint", ROMREGION_ERASE00) // speech data inside of the speech chip; fill with 00, if no speech cart is present socrates will see this
	ROM_LOAD_OPTIONAL("speech_fra_internal.bin", 0x0000, 0x2000, NO_DUMP)

	ROM_REGION(0x10000, "speechext", ROMREGION_ERASE00) // speech serial modules outside of the speech chip but still on speech cart
	ROM_LOAD_OPTIONAL("speech_fra_vsm1.bin", 0x0000, 0x4000, NO_DUMP) // 16k in serial rom
	ROM_LOAD_OPTIONAL("speech_fra_vsm2.bin", 0x4000, 0x4000, NO_DUMP) // 16k in serial rom
	ROM_LOAD_OPTIONAL("speech_fra_vsm3.bin", 0x8000, 0x4000, NO_DUMP) // 16k in serial rom
	ROM_FILL(0xC000, 0x4000, 0xff) // last vsm isn't present, FF fill
ROM_END

ROM_START(profweis)
	ROM_REGION(0x80000, "maincpu", ROMREGION_ERASEVAL(0xF3))
	/* Yeno Professor Weiss-Alles (German PAL) */
	ROM_SYSTEM_BIOS(0, "89", "1989")
	ROMX_LOAD("lh53216d.u1", 0x00000, 0x40000, CRC(6e801762) SHA1(b80574a3abacf18133dacb9d3a8d9e2916730423), ROM_BIOS(0)) // Label: "(Vtech) LH53216D // (C)1989 VIDEO TECHNOLOGY // 9119 D"
	ROM_SYSTEM_BIOS(1, "88", "1988")
	ROMX_LOAD("27-00885-000-000.u1", 0x00000, 0x40000, CRC(fcaf8850) SHA1(a99011ee6a1ef63461c00d062278951252f117db), ROM_BIOS(1)) // Label: "(Vtech) 27-00885-000-000 // (C)1988 VIDEO TECHNOLOGY // 8844 D"

	ROM_REGION(0x800, "kbmcu", ROMREGION_ERASEFF)
	ROM_LOAD("tmp42c40p1844.u6", 0x000, 0x200, NO_DUMP) /* keyboard IR decoder MCU */

	ROM_REGION(0x2000, "speechint", ROMREGION_ERASE00) // speech data inside of the speech chip; fill with 00, if no speech cart is present socrates will see this
	ROM_LOAD_OPTIONAL("speech_ger_internal.bin", 0x0000, 0x2000, CRC(5ff0fdc6) SHA1(8ef128561a846762a20e3fe9513a4a22aaadc7f6))

	ROM_REGION(0x10000, "speechext", ROMREGION_ERASE00) // speech serial modules outside of the speech chip but still on speech cart
	ROM_LOAD_OPTIONAL("speech_ger_vsm1.bin", 0x0000, 0x4000, CRC(a979a00b) SHA1(0290844619dbdf336757003aaab3ffd0a75b7ee9)) // 16k in serial rom
	ROM_FILL(0x4000, 0xc000, 0xff) // last 3 vsms aren't present, FF fill
ROM_END

ROM_START( iqunlimz )
	ROM_REGION( 0x80000, "maincpu", 0 )
	ROM_LOAD( "vtech.bin", 0x000000, 0x080000, CRC(f100c8a7) SHA1(6ad2a8accae2dd5c5c46ae953eef33cdd1ea3cf9) )

	ROM_REGION(0x1000, "kbmcu", ROMREGION_ERASEFF)
	ROM_LOAD("kbmcu.bin", 0x0000, 0x1000, NO_DUMP) /* keyboard reader MCU */
ROM_END

ROM_START( vpainter )
	ROM_REGION(0x80000, "maincpu", ROMREGION_ERASEVAL(0xF3))
	ROM_LOAD("lh531g02.u1", 0x00000, 0x20000, CRC(898defac) SHA1(8307e00b5ce3675ce71960e7cf2d1334197a1dce)) // Label: "(Vtech)LH531G02 // (C)1991 VIDEO TECHNOLOGY // 9211 D"

	ROM_REGION(0x1000, "kbmcu", ROMREGION_ERASEFF)
	ROM_LOAD("tmp47c241nj408.u6", 0x0000, 0x1000, NO_DUMP) /* key/touchpad reader MCU */

	///TODO: get rid of these regions in the status_read function or make it socrates-specific
	ROM_REGION(0x2000, "speechint", ROMREGION_ERASE00) // doesn't exist? on vpainter, presumably reads as all zeroes
	ROM_REGION(0x10000, "speechext", ROMREGION_ERASE00) // doesn't exist? on vpainter, presumably reads as all zeroes
ROM_END

} // anonymous namespace


/******************************************************************************
 Drivers
******************************************************************************/

//    YEAR  NAME      PARENT    COMPAT  MACHINE       INPUT     STATE           INIT           COMPANY                    FULLNAME                             FLAGS
COMP( 1988, socrates, 0,        0,      socrates,     socrates, socrates_state, init_socrates, "Video Technology",        "Socrates Educational Video System", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) // English, no title copyright, same ROM for NTSC and PAL
COMP( 1988, socratfc, socrates, 0,      socrates,     socrates, socrates_state, init_socrates, "Video Technology",        "Socrates SAITOUT",                  MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) // French Canandian NTSC, 1988 title copyright
// Yeno Professeur Saitout (French SECAM) matches the Socrates SAITOUT dump (same ROM 27-00884-001-000)
COMP( 1988, profweis, socrates, 0,      socrates_pal, socrates, socrates_state, init_socrates, "Video Technology / Yeno", "Professor Weiss-Alles",             MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) // German PAL, 1988 title copyright
// ? goes here (Spanish PAL)

COMP( 1991, iqunlimz, 0,        0,      iqunlimz,     iqunlimz, iqunlimz_state, init_iqunlimz, "Video Technology",        "IQ Unlimited (Z80)",                MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

COMP( 1991, vpainter, 0,        0,      vpainter_pal, socrates, socrates_state, init_vpainter, "Video Technology",        "Video Painter (PAL)",               MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
// Master Video Painter goes here



softbox.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder, Mike Naberezny
/*

    SSE SoftBox

    http://mikenaberezny.com/hardware/pet-cbm/sse-softbox-z80-computer/


    Standalone vs. PET/CBM Peripheral Mode
    --------------------------------------

    The SoftBox can be used as a standalone computer with an RS-232 terminal,
    or as a PET/CBM peripheral.  This is an emulation of the standalone mode.
    For the peripheral mode, see: src/devices/bus/ieee488/softbox.c.


    Using the Corvus hard disk
    --------------------------

    The SoftBox distribution disk (softbox-distrib.d80) is configured for
    a CBM 8050 as CP/M drives A/B and a 10MB Corvus hard disk as drives C/D.

    Use the CHDMAN utility to create a 10MB hard disk image for the Corvus:

    $ chdman createhd -o /path/to/corvus10mb.chd -chs 358,3,20 -ss 512

    Start the SoftBox emulator with the floppy and hard disk images mounted:

    $ mame softbox -flop1 /path/to/softbox-distrib.d80 \
                   -hard1 /path/to/corvus10mb.chd

    Before the Corvus can be used under CP/M, it must be prepared
    by running DIAG.COM and FORMAT.COM.

    DIAG.COM

    Enter "diag" (no arguments) at the CP/M prompt to run the Corvus diagnostics
    program.  This program will perform the Corvus low-level format.

    Select option 6 (Update Controller Code) at the menu.
    Enter "corvb184.fmt" when prompted for the filename.
    Enter "y" at the confirmation prompts.
    Enter "1" for the Corvus drive number (two prompts).
    After formatting is complete, it will return to the menu.

    Select option 3 (Read Controller Code Version #) at the menu.
    Enter "1" for the Corvus drive number.
    It should report "V18.4AP" and then return to the menu.

    Select option 9 to return to CP/M.

    FORMAT.COM

    Enter "format" (no arguments) at the CP/M prompt to run the SoftBox disk
    format program.  This program will perform the CP/M filesystem format.

    Enter drive letter "c" at the prompt.
    Enter "y" to confirm the format.
    After formatting is complete, it will prompt for a drive letter again.

    Enter drive letter "d" at the prompt.
    Enter "y" to confirm the format.
    After formatting is complete, it will prompt for a drive letter again.

    Press RETURN to return to CP/M.

    STAT.COM

    After all steps are completed, drives C and D should be usable from
    CP/M.  Each drive is one half of the Corvus 10MB disk.  Running the
    command "stat c: dsk:" should show 4712 kilobyte drive capacity.
    Drive D should show the same information.


    Using other Corvus hard disk sizes
    ----------------------------------

    The SoftBox supports 5, 10, and 20 MB hard disks.  The distribution disk
    is configured for 10 MB as explained above.  To use other sizes, make
    a new image with CHDMAN.  See the top of src/devices/machine/corvushd.h
    for the parameters for the other drives.

    After the image has been created and the SoftBox emulator started with
    it mounted, the SoftBox BIOS needs to be told what size Corvus hard
    disk is attached.  Use the NEWSYS.COM utility to reconfigure the drive
    size.  When NEWSYS prompts for a source drive, enter "a" (the drive letter
    of the CP/M distribution disk).  Use option "d" (Disk drive assignment)
    to reconfigure the Corvus size.  After the change has been made, use option
    "s" (Save new system) to write the configuration to the floppy (drive A) and
    option "e" (Execute new system) to restart CP/M with the configuration.
    DIAG.COM and FORMAT.COM can then be used to format the hard disk.

*/

#include "emu.h"

#include "bus/ieee488/ieee488.h"
#include "bus/imi7000/imi7000.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "imagedev/harddriv.h"
#include "machine/corvushd.h"
#include "machine/com8116.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "softlist_dev.h"

namespace {

#define Z80_TAG         "z80"
#define I8251_TAG       "ic15"
#define I8255_0_TAG     "ic17"
#define I8255_1_TAG     "ic16"
#define COM8116_TAG     "ic14"
#define RS232_TAG       "rs232"
#define CORVUS_HDC_TAG  "corvus"

class softbox_state : public driver_device
{
public:
	softbox_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, Z80_TAG)
		, m_ieee(*this, IEEE488_TAG)
		, m_hdc(*this, CORVUS_HDC_TAG)
		, m_leds(*this, "led%u", 0U)
	{ }

	void softbox(machine_config &config);

private:
	// device_ieee488_interface overrides
	virtual void ieee488_ifc(int state);

	uint8_t ppi0_pa_r();
	void ppi0_pb_w(uint8_t data);

	uint8_t ppi1_pa_r();
	void ppi1_pb_w(uint8_t data);
	uint8_t ppi1_pc_r();
	void ppi1_pc_w(uint8_t data);

	enum
	{
		LED_A = 0,
		LED_B,
		LED_READY
	};

	void softbox_io(address_map &map) ATTR_COLD;
	void softbox_mem(address_map &map) ATTR_COLD;
	int m_ifc = 0;  // Tracks previous state of IEEE-488 IFC line

	virtual void machine_start() override ATTR_COLD;
	virtual void device_reset_after_children() override;

	required_device<cpu_device> m_maincpu;
	required_device<ieee488_device> m_ieee;
	required_device<corvus_hdc_device> m_hdc;
	output_finder<3> m_leds;
};


//**************************************************************************
//  READ/WRITE HANDLERS
//**************************************************************************


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( softbox_mem )
//-------------------------------------------------

void softbox_state::softbox_mem(address_map &map)
{
	map(0x0000, 0xefff).ram();
	map(0xf000, 0xffff).rom().region(Z80_TAG, 0);
}


//-------------------------------------------------
//  ADDRESS_MAP( softbox_io )
//-------------------------------------------------

void softbox_state::softbox_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x08, 0x09).rw(I8251_TAG, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x0c, 0x0c).w(COM8116_TAG, FUNC(com8116_device::stt_str_w));
	map(0x10, 0x13).rw(I8255_0_TAG, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x14, 0x17).rw(I8255_1_TAG, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x18, 0x18).rw(m_hdc, FUNC(corvus_hdc_device::read), FUNC(corvus_hdc_device::write));
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( softbox )
//-------------------------------------------------

static INPUT_PORTS_START( softbox )
	/* An 8-position DIP switch may be installed at SW1.  Some
	   SoftBox units have it and some do not.  The switches are
	   not used by the SoftBox BIOS. */
	PORT_START("SW1")
	PORT_DIPUNUSED_DIPLOC( 0x01, IP_ACTIVE_LOW, "SW1:1" )
	PORT_DIPUNUSED_DIPLOC( 0x02, IP_ACTIVE_LOW, "SW1:2" )
	PORT_DIPUNUSED_DIPLOC( 0x04, IP_ACTIVE_LOW, "SW1:3" )
	PORT_DIPUNUSED_DIPLOC( 0x08, IP_ACTIVE_LOW, "SW1:4" )
	PORT_DIPUNUSED_DIPLOC( 0x10, IP_ACTIVE_LOW, "SW1:5" )
	PORT_DIPUNUSED_DIPLOC( 0x20, IP_ACTIVE_LOW, "SW1:6" )
	PORT_DIPUNUSED_DIPLOC( 0x40, IP_ACTIVE_LOW, "SW1:7" )
	PORT_DIPUNUSED_DIPLOC( 0x80, IP_ACTIVE_LOW, "SW1:8" )
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  I8255A 0 Interface
//-------------------------------------------------

uint8_t softbox_state::ppi0_pa_r()
{
	return m_ieee->dio_r() ^ 0xff;
}

void softbox_state::ppi0_pb_w(uint8_t data)
{
	m_ieee->host_dio_w(data ^ 0xff);
}

//-------------------------------------------------
//  I8255A 1 Interface
//-------------------------------------------------

uint8_t softbox_state::ppi1_pa_r()
{
	/*

	  bit     description

	  PA0     ATN
	  PA1     DAV
	  PA2     NDAC
	  PA3     NRFD
	  PA4     EOI
	  PA5     SRQ
	  PA6     REN
	  PA7     IFC

	*/

	uint8_t data = 0;

	data |= !m_ieee->atn_r();
	data |= !m_ieee->dav_r() << 1;
	data |= !m_ieee->ndac_r() << 2;
	data |= !m_ieee->nrfd_r() << 3;
	data |= !m_ieee->eoi_r() << 4;
	data |= !m_ieee->srq_r() << 5;
	data |= !m_ieee->ren_r() << 6;
	data |= !m_ieee->ifc_r() << 7;

	return data;
}

void softbox_state::ppi1_pb_w(uint8_t data)
{
	/*

	  bit     description

	  PB0     ATN
	  PB1     DAV
	  PB2     NDAC
	  PB3     NRFD
	  PB4     EOI
	  PB5     SRQ
	  PB6     REN
	  PB7     IFC

	*/

	m_ieee->host_atn_w(!BIT(data, 0));
	m_ieee->host_dav_w(!BIT(data, 1));
	m_ieee->host_ndac_w(!BIT(data, 2));
	m_ieee->host_nrfd_w(!BIT(data, 3));
	m_ieee->host_eoi_w(!BIT(data, 4));
	m_ieee->host_srq_w(!BIT(data, 5));
	m_ieee->host_ren_w(!BIT(data, 6));
	m_ieee->host_ifc_w(!BIT(data, 7));
}

uint8_t softbox_state::ppi1_pc_r()
{
	/*

	  bit     description

	  PC0
	  PC1
	  PC2
	  PC3
	  PC4     Corvus READY
	  PC5     Corvus DIRC
	  PC6
	  PC7

	*/

	uint8_t status = m_hdc->status_r();
	uint8_t data = 0;

	data |= (status & corvus_hdc_device::CONTROLLER_BUSY) ? 0 : 0x10;
	data |= (status & corvus_hdc_device::CONTROLLER_DIRECTION) ? 0 : 0x20;

	return data;
}

void softbox_state::ppi1_pc_w(uint8_t data)
{
	/*

	  bit     description

	  PC0     LED "A"
	  PC1     LED "B"
	  PC2     LED "READY"
	  PC3
	  PC4
	  PC5
	  PC6
	  PC7

	*/

	m_leds[LED_A] = BIT(~data, 0);
	m_leds[LED_B] = BIT(~data, 1);
	m_leds[LED_READY] = BIT(~data, 2);
}

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( softbox )
//-------------------------------------------------

void softbox_state::machine_start()
{
	m_leds.resolve();
}


//-------------------------------------------------
//  device_reset_after_children - device-specific
//    reset that must happen after child devices
//    have performed their resets
//-------------------------------------------------

void softbox_state::device_reset_after_children()
{
	/* The Z80 starts at address 0x0000 but the SoftBox has RAM there and
	   needs to start from the BIOS at 0xf000.  The PCB has logic and a
	   74S287 PROM that temporarily changes the memory map so that the
	   IC3 EPROM at 0xf000 is mapped to 0x0000 for the first instruction
	   fetch only.  The instruction normally at 0xf000 is an absolute jump
	   into the BIOS.  On reset, the Z80 will fetch it from 0x0000 and set
	   its PC, then the normal map will be restored before the next
	   instruction fetch.  Here we just set the PC to 0xf000 after the Z80
	   resets, which has the same effect. */

	m_maincpu->set_state_int(Z80_PC, 0xf000);
}


//-------------------------------------------------
//  ieee488_ifc - interface clear (reset)
//-------------------------------------------------

void softbox_state::ieee488_ifc(int state)
{
	if (!m_ifc && state)
	{
		device_reset();
	}

	m_ifc = state;
}



//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

void softbox_state::softbox(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, XTAL(8'000'000)/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &softbox_state::softbox_mem);
	m_maincpu->set_addrmap(AS_IO, &softbox_state::softbox_io);

	// devices
	i8251_device &i8251(I8251(config, I8251_TAG, 0));
	i8251.txd_handler().set(RS232_TAG, FUNC(rs232_port_device::write_txd));
	i8251.dtr_handler().set(RS232_TAG, FUNC(rs232_port_device::write_dtr));
	i8251.rts_handler().set(RS232_TAG, FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, RS232_TAG, default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(I8251_TAG, FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set(I8251_TAG, FUNC(i8251_device::write_dsr));
	rs232.cts_handler().set(I8251_TAG, FUNC(i8251_device::write_cts));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	i8255_device &ppi0(I8255A(config, I8255_0_TAG));
	ppi0.in_pa_callback().set(FUNC(softbox_state::ppi0_pa_r));
	ppi0.out_pb_callback().set(FUNC(softbox_state::ppi0_pb_w));
	ppi0.in_pc_callback().set_ioport("SW1");

	i8255_device &ppi1(I8255A(config, I8255_1_TAG));
	ppi1.in_pa_callback().set(FUNC(softbox_state::ppi1_pa_r));
	ppi1.out_pb_callback().set(FUNC(softbox_state::ppi1_pb_w));
	ppi1.in_pc_callback().set(FUNC(softbox_state::ppi1_pc_r));
	ppi1.out_pc_callback().set(FUNC(softbox_state::ppi1_pc_w));

	com8116_device &dbrg(COM8116(config, COM8116_TAG, 5.0688_MHz_XTAL));
	dbrg.fr_handler().set(I8251_TAG, FUNC(i8251_device::write_rxc));
	dbrg.ft_handler().set(I8251_TAG, FUNC(i8251_device::write_txc));

	ieee488_device::add_cbm_devices(config, "c8050");

	CORVUS_HDC(config, m_hdc, 0);
	HARDDISK(config, "harddisk1", "corvus_hdd");
	HARDDISK(config, "harddisk2", "corvus_hdd");
	HARDDISK(config, "harddisk3", "corvus_hdd");
	HARDDISK(config, "harddisk4", "corvus_hdd");

	IMI7000_BUS(config, "imi7000").set_slot_default_options("imi5000h", nullptr, nullptr, nullptr);

	// software lists
	SOFTWARE_LIST(config, "flop_list").set_original("softbox");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( softbox )
//-------------------------------------------------

ROM_START( softbox )
	ROM_REGION( 0x1000, Z80_TAG, 0 )
	ROM_DEFAULT_BIOS("19830609")
	ROM_SYSTEM_BIOS( 0, "19810908", "8/9/81" )
	ROMX_LOAD( "375.ic3", 0x000, 0x800, CRC(177580e7) SHA1(af6a97495de825b80cdc9fbf72329d5440826177), ROM_BIOS(0) )
	ROMX_LOAD( "376.ic4", 0x800, 0x800, CRC(edfee5be) SHA1(5662e9071cc622a1c071d89b00272fc6ba122b9a), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "19811027", "27-Oct-81" )
	ROMX_LOAD( "379.ic3", 0x000, 0x800, CRC(7b5a737c) SHA1(2348590884b026b7647f6864af8c9ba1c6f8746b), ROM_BIOS(1) )
	ROMX_LOAD( "380.ic4", 0x800, 0x800, CRC(65a13029) SHA1(46de02e6f04be298047efeb412e00a5714dc21b3), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "19830609", "09-June-1983" )
	ROMX_LOAD( "389.ic3", 0x000, 0x800, CRC(d66e581a) SHA1(2403e25c140c41b0e6d6975d39c9cd9d6f335048), ROM_BIOS(2) )
	ROMX_LOAD( "390.ic4", 0x800, 0x800, CRC(abe6cb30) SHA1(4b26d5db36f828e01268f718799f145d09b449ad), ROM_BIOS(2) )
ROM_END

} // anonymous namespace

//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY                      FULLNAME   FLAGS
COMP( 1981, softbox, 0,      0,      softbox, softbox, softbox_state, empty_init, "Small Systems Engineering", "SoftBox", MACHINE_NO_SOUND_HW )



solbourne.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************************

2017-10-28 Skeleton

Solbourne computer workstation. This looks like the Series 5E which uses the Cypress CY7C601 CPU @ 40MHz.

************************************************************************************************************************************/

#include "emu.h"


namespace {

class solbourne_state : public driver_device
{
public:
	solbourne_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		//      , m_maincpu(*this, "maincpu")
	{ }

	void solbourne(machine_config &config);
private:
	//  required_device<cpu_device> m_maincpu;
};

static INPUT_PORTS_START( solbourne )
INPUT_PORTS_END

void solbourne_state::solbourne(machine_config &config)
{
}

ROM_START( sols5e )
	ROM_REGION( 0x20000, "maincpu", 0 )
	ROM_LOAD( "s5e_3.4_8e36_00.bin", 0x000000, 0x020000, CRC(bc44765a) SHA1(3727af2e683d7cdd4c65865268c689533758c6bb) )
	ROM_LOAD( "s5e_3.4_6d2e_01.bin", 0x000000, 0x020000, CRC(9c1f95ab) SHA1(73d416ba50c2b448b6e69cd30909d6dfe46f9265) )
	ROM_LOAD( "s5e_3.4_eaa8_02.bin", 0x000000, 0x020000, CRC(4b6ee28a) SHA1(c7d5d588a0bec7fc996daef6a76151bf763b5f1c) )
	ROM_LOAD( "s5e_3.4_3a44_03.bin", 0x000000, 0x020000, CRC(cb5dc0ca) SHA1(7fd94f26a9aa605b8ca6a444a131e8011b681d1b) )
	ROM_LOAD( "s5e_3.5_8e0a_00.bin", 0x000000, 0x020000, CRC(60e7c977) SHA1(ac9e3219b6e190cfe912b751cbfe835adf045ab9) )
	ROM_LOAD( "s5e_3.5_2306_01.bin", 0x000000, 0x020000, CRC(935f2d7f) SHA1(5b302701c6bfd7d0156120d474ba1e6df74bdee4) )
	ROM_LOAD( "s5e_3.5_a5aa_02.bin", 0x000000, 0x020000, CRC(b381cfdd) SHA1(90e91aaaff76f8ffcca692aa20750c2afd7c3c93) )
	ROM_LOAD( "s5e_3.5_93bc_03.bin", 0x000000, 0x020000, CRC(ec92be58) SHA1(e645d1573bc9e5b34707d1556df4edf43632b09e) )
	ROM_LOAD( "s5e_3.6b_45aa_00.bin", 0x000000, 0x020000, CRC(52315d71) SHA1(4e6cdfadd1c8a1dd39f6e1b9e09a90f1d9e6b54c) )
	ROM_LOAD( "s5e_3.6b_fdd0_01.bin", 0x000000, 0x020000, CRC(454e8e62) SHA1(dba27acd4abd5e9873dacfaff982c59175de34f6) )
	ROM_LOAD( "s5e_3.6b_623a_02.bin", 0x000000, 0x020000, CRC(56c582fb) SHA1(367b2a9bedf5e5fc7a2aa1f7a05e8c1101b09dd7) )
	ROM_LOAD( "s5e_3.6b_d732_03.bin", 0x000000, 0x020000, CRC(bb6ce8d6) SHA1(6d83e28f9ebac794ad05f47bdd578ef9c1e07de8) )
	ROM_LOAD( "channel_exp_2816.bin", 0x000000, 0x000800, CRC(f9ac0bc6) SHA1(685c2425f58e3c861df4f9f37f39bae343567e13) )
	ROM_LOAD( "30341id.bin",  0x000000, 0x000800, CRC(cdf8cffe) SHA1(725279d995100a37c8a01edfdb3af71332a55c89) )
	ROM_LOAD( "10812id.bin",  0x000000, 0x000800, CRC(07645046) SHA1(673b0be54daf0b890580c8244926835ed323a270) )
	ROM_LOAD( "10454id.bin",  0x000000, 0x000800, CRC(c94b3371) SHA1(9ef9792ffe26302965023041a969d57749d101f7) )
ROM_END

} // anonymous namespace


COMP( 198?, sols5e, 0, 0, solbourne, solbourne, solbourne_state, empty_init, "Solbourne Computer Inc", "Series 5E Computer Workstation", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



sorcerer.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Kevin Thacker, Robbbert
/******************************************************************************

  Exidy Sorcerer system driver

    The UART controls rs232 and cassette i/o. The chip is a AY-3-1014A or AY-3-1015.


    port fc:
    ========
    input/output:
        uart data

    port fd:
    ========
    input: uart status
        bit 4: parity error (RPE)
        bit 3: framing error (RFE)
        bit 2: over-run (RDP)
        bit 1: data available (RDA)
        bit 0: transmit buffer empty (TPMT)

    output:
        bit 4: no parity (NPB)
        bit 3: parity type (POE)
        bit 2: number of stop bits (NSB)
        bit 1: number of bits per char bit 2 (NDB2)
        bit 0: number of bits per char bit 1 (NDB1)

    port fe:
    ========

    output:

        bit 7: rs232 enable (1=rs232, 0=cassette)
        bit 6: baud rate (1=1200, 0=300)
        bit 5: cassette motor 2
        bit 4: cassette motor 1
        bit 3..0: keyboard line select

    input:
        bit 7..6: parallel control (not emulated)
                7: must be 1 to read data from parallel port via PARIN
                6: must be 1 to send data out of parallel port via PAROUT
        bit 5: vsync
        bit 4..0: keyboard line data

    port ff:
    ========
      parallel port in/out

    -------------------------------------------------------------------------------------

    When cassette is selected, it is connected to the uart data input via the cassette
    interface hardware.

    The cassette interface hardware converts square-wave pulses into bits which the uart receives.

    1. the cassette format: "frequency shift" is converted
    into the uart data format "non-return to zero"

    2. on cassette a 1 data bit is stored as a high frequency
    and a 0 data bit as a low frequency
    - At 1200 baud, a logic 1 is 1 cycle of 1200 Hz and a logic 0 is 1/2 cycle of 600 Hz.
    - At 300 baud, a logic 1 is 8 cycles of 2400 Hz and a logic 0 is 4 cycles of 1200 Hz.

    Attenuation is applied to the signal and the square wave edges are rounded.

    A manchester encoder is used. A flip-flop synchronises input
    data on the positive-edge of the clock pulse.

    Interestingly the data on cassette is stored in xmodem-checksum.


    Due to bugs in the hardware and software of a real Sorcerer, the serial
    interface misbehaves.
    1. Sorcerer I had a hardware problem causing rs232 idle to be a space (+9v)
    instead of mark (-9v). Fixed in Sorcerer II.
    2. When you select a different baud for rs232, it was "remembered" but not
    sent to port fe. It only gets sent when motor on was requested. Motor on is
    only meaningful in a cassette operation.
    3. The monitor software always resets the device to cassette whenever the
    keyboard is scanned, motors altered, or an error occurred.
    4. The above problems make rs232 communication impractical unless you write
    your own routines or create a corrected monitor rom.

    Sound:

    External speaker connected to the parallel port.
    There was a dac you could make instead, this is supported.


    Kevin Thacker [MESS driver]
    Robbbert [Various corrections and additions over the years]

 ******************************************************************************

    The CPU clock speed is 2.106 MHz, which was increased to 4.0 MHz on the last production runs.

    The Sorcerer has a bus connection for S100 equipment. This allows the connection
    of disk drives, provided that suitable driver/boot software is loaded.

*******************************************************************************

Real machines had optional RAM sizes of 8k, 16k, 32k, 48k (officially).
Unofficially, the cart could hold another 8k of static RAM (4x 6116), giving 56k total.

On the back of the machine is a 50-pin expansion port, which could be hooked to the
expansion unit.

Also is a 25-pin parallel port, allowing inwards and outwards communication.
It was often hooked to a printer, a joystick, a music card, or a speaker.

We emulate the printer and the speaker.

Another 25-pin port provided two-way serial communications. Only two speeds are
available - 300 baud and 1200 baud. There is no handshaking.

Other pins on this connector provided for two cassette players. The connections
for cassette unit 1 are duplicated on a set of phono plugs.

We emulate the use of two cassette units. An option allows you to hear the sound
of the tape during playback.

********************************************************************************

Progress with floppy-disk systems:

It appears that a number of companies made floppy-disk controllers for the Sorcerer.
We will attempt to emulate whatever we have disk images for.

Disk system on sorcererd:
- Uses a Micropolis controller. Top of RAM is BBFF.
- To boot CP/M, insert the 325k disk, then type GO BC00 at the monitor prompt.
- To try the video/disk unit, insert the 78k disk, then type GO BF00 at the monitor prompt.
  (see notes below)

Disk system on sorcerera:
- Uses an almost-Microbee clone board called the Dreamdisk.
- To boot, just insert the proper 841k disk. The bios will wait for it.

Disk system on sorcererb:
- SCUAMON will try using ports 30,32,34 and 38. Uses digitrio hardware (also a 841k disk).
  scuamon64 requires you to enter DI to boot, and it doesn't work, confirmed buggy.
  scuamon80 will successfully boot by itself, but the 80 column display is of course scrambled.

Other disk-enabled bioses:
- TVIMON uses ports 04,30-34. Type in BO to attempt to boot. Unknown hardware.
- There's a bunch of 251k disks, also requiring unknown hardware.


Exidy Sorcerer Video/Disk Unit:
- Contains a screen and 2 floppy drives. No details available. Partial
  emulation done but it's all guesswork. The unit contains 2 dipswitch units
  (one has 4 switches and the other has 8), a 8 MHz crystal, 3 proms and a
  FD1793-B01 fdc. Going by the code, it would appear to place the Z-80 into
  WAIT while reading a sector. To try it out: GO BF00 at the monitor prompt.
  Currently the CP/M sign-on message appears, followed by lockup due to a
  fdc problem. Uses ports 28-2C. Run it under debug, when it gets stuck you'll
  see the A register has 21. Change it to 24 and it will boot up and work.

********************************************************************************/

#include "emu.h"
#include "sorcerer.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


void sorcerer_state::sorcerer_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).ram();
	//map(0xc000, 0xdfff).rom();      // mapped by the cartslot
	map(0xe000, 0xefff).rom().region("maincpu", 0).nopw();    // bios
	map(0xf800, 0xfbff).rom().region("chargen", 0).nopw();    // inbuilt characters
	map(0xfc00, 0xffff).share("pcg");                   // PCG
}

void sorcererd_state::sorcererd_mem(address_map &map)
{
	map.unmap_value_high();
	sorcerer_mem(map);
	map(0xbc00, 0xbfff).rom().region("diskboot", 0).nopw();
	map(0xbe00, 0xbe03).rw(m_fdc, FUNC(micropolis_device::read), FUNC(micropolis_device::write));
}

void sorcerer_state::sorcerer_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0xfc, 0xfc).rw(m_uart, FUNC(ay31015_device::receive), FUNC(ay31015_device::transmit));
	map(0xfd, 0xfd).rw(FUNC(sorcerer_state::portfd_r), FUNC(sorcerer_state::portfd_w));
	map(0xfe, 0xfe).rw(FUNC(sorcerer_state::portfe_r), FUNC(sorcerer_state::portfe_w));
	map(0xff, 0xff).r("cent_status_in", FUNC(input_buffer_device::read));
	map(0xff, 0xff).w(FUNC(sorcerer_state::portff_w));
}

void sorcerer_state::sorcerera_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	sorcerer_io(map);
	map(0x44, 0x47).rw(m_fdc4, FUNC(wd2793_device::read), FUNC(wd2793_device::write));
	map(0x48, 0x4b).rw(FUNC(sorcerer_state::port48_r), FUNC(sorcerer_state::port48_w));
}

void sorcerer_state::sorcererb_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	sorcerer_io(map);
	map(0x30, 0x33).rw(m_fdc3, FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	map(0x34, 0x37).rw(FUNC(sorcerer_state::port34_r), FUNC(sorcerer_state::port34_w));
	map(0x38, 0x3b).rw(m_dma, FUNC(z80dma_device::read), FUNC(z80dma_device::write));
}

void sorcererd_state::sorcererd_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	sorcerer_io(map);
	map(0x28, 0x2b).rw(m_fdc2, FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	map(0x2c, 0x2f).w(FUNC(sorcererd_state::port2c_w));
}

static INPUT_PORTS_START(sorcerer)
	PORT_START("VS")
	/* vblank */
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("screen", FUNC(screen_device::vblank))

	/* line 0 */
	PORT_START("X.0")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Control") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Graphic") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Stop") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	/* line 1 */
	PORT_START("X.1")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("(Sel)") PORT_CODE(KEYCODE_F2) PORT_CHAR(27)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Skip") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Repeat") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Clear") PORT_CODE(KEYCODE_F5) PORT_CHAR(12)
	/* line 2 */
	PORT_START("X.2")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') PORT_CHAR(0x11)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR(0x01)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(0x1a)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(0x18)
	/* line 3 */
	PORT_START("X.3")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR(0x17)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR(0x13)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR(0x04)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR(0x03)
	/* line 4 */
	PORT_START("X.4")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(0x05)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR(0x12)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR(0x06)
	/* line 5 */
	PORT_START("X.5")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(0x14)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR(0x07)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR(0x16)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR(0x02)
	/* line 6 */
	PORT_START("X.6")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR(0x19)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') PORT_CHAR(0x08)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(0x0e)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') PORT_CHAR(0x0d)
	/* line 7 */
	PORT_START("X.7")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(0x15)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') PORT_CHAR(0x0a)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') PORT_CHAR(0x09)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR(0x0b)
	/* line 8 */
	PORT_START("X.8")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(0x0f)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR(0x0c)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	/* line 9 */
	PORT_START("X.9")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR(0x10)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("; +") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	/* line 10 */
	PORT_START("X.10")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(": *") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[ {") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')  PORT_CHAR('{')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("] }") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')  PORT_CHAR('}')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@ `") PORT_CODE(KEYCODE_F7) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\ |") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\')  PORT_CHAR('|')
	/* line 11 */
	PORT_START("X.11")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("- =") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^ ~") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LINE FEED") PORT_CODE(KEYCODE_F6) PORT_CHAR(10)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("_ Rub") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR('_') PORT_CHAR(8)
	/* line 12 */
	PORT_START("X.12")
	PORT_BIT(0x10, 0x10, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("- (PAD)") PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/ (PAD)") PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("* (PAD)") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("+ (PAD)") PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))
	/* line 13 */
	PORT_START("X.13")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 (PAD)") PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 (PAD)") PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 (PAD)") PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 (PAD)") PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0 (PAD)") PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	/* line 14 */
	PORT_START("X.14")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 (PAD)") PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 (PAD)") PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 (PAD)") PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 (PAD)") PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(". (PAD)") PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	/* line 15 */
	PORT_START("X.15")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 (PAD)") PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("= (PAD)") PORT_CODE(KEYCODE_NUMLOCK)
	PORT_BIT(0x04, 0x04, IPT_UNUSED)
	PORT_BIT(0x02, 0x02, IPT_UNUSED)
	PORT_BIT(0x01, 0x01, IPT_UNUSED)

	/* Enhanced options not available on real hardware */
	PORT_START("CONFIG")
	PORT_CONFNAME( 0x01, 0x01, "Autorun on Quickload")
	PORT_CONFSETTING(    0x00, DEF_STR(No))
	PORT_CONFSETTING(    0x01, DEF_STR(Yes))
	/* hardware connected to printer port */
	PORT_CONFNAME( 0x02, 0x02, "Parallel port" )
	PORT_CONFSETTING(    0x00, "7-bit" )
	PORT_CONFSETTING(    0x02, "8-bit" )
	PORT_CONFNAME( 0x08, 0x08, "Cassette Speaker")
	PORT_CONFSETTING(    0x08, DEF_STR(On))
	PORT_CONFSETTING(    0x00, DEF_STR(Off))
INPUT_PORTS_END

/**************************** F4 CHARACTER DISPLAYER ******************************************************/

static const gfx_layout charlayout =
{
	8, 8,                   // 8 x 8 characters
	128,                    // number of characters
	1,                      // bits per pixel
	{ 0 },                  // no bitplanes
	// x offsets
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	// y offsets
	{  0*8,  1*8,  2*8,  3*8,  4*8,  5*8,  6*8,  7*8 },
	8*8                     // every char takes 8 bytes
};

// This will show the 128 characters in the ROM + whatever happens to be in the PCG
static GFXDECODE_START( gfx_sorcerer )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
	GFXDECODE_RAM  ( "pcg",     0x0000, charlayout, 0, 1 )
GFXDECODE_END

uint32_t sorcerer_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	address_space& program = m_maincpu->space(AS_PROGRAM);
	uint16_t sy=0,ma=0xf080;

	for (uint8_t y = 0; y < 30; y++)
	{
		for (uint8_t ra = 0; ra < 8; ra++)
		{
			uint16_t *p = &bitmap.pix(sy++);

			for (uint16_t x = ma; x < ma+64; x++)
			{
				uint8_t const chr = program.read_byte(x);

				/* get pattern of pixels for that character scanline */
				uint8_t const gfx = program.read_byte(0xf800 | (chr<<3) | ra);

				/* Display a scanline of a character */
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma+=64;
	}
	return 0;
}


/**********************************************************************************************************/

static const floppy_interface sorcerer_floppy_interface =
{
	FLOPPY_STANDARD_8_SSSD,
	LEGACY_FLOPPY_OPTIONS_NAME(sorcerer),
	"floppy_8"
};

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_1200 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_1200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END

void sorcerer_state::sorcerer(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, ES_CPU_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &sorcerer_state::sorcerer_mem);
	m_maincpu->set_addrmap(AS_IO, &sorcerer_state::sorcerer_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(ES_VIDEO_CLOCK, 806, 0, 512, 261, 0, 240); // TODO: 313 lines in 50 Hz mode
	screen.set_screen_update(FUNC(sorcerer_state::screen_update));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_sorcerer);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	AY31015(config, m_uart);
	m_uart->set_auto_rdav(true);

	CLOCK(config, m_uart_clock, ES_UART_CLOCK);
	m_uart_clock->signal_handler().set(m_uart, FUNC(ay31015_device::write_tcp));
	m_uart_clock->signal_handler().append(m_uart, FUNC(ay31015_device::write_rcp));

	RS232_PORT(config, "rs232", default_rs232_devices, "null_modem").set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	/* printer */
	/* The use of the parallel port as a general purpose port is not emulated.
	Currently the only use is to read the printer status in the Centronics CENDRV bios routine. */
	CENTRONICS(config, m_centronics, centronics_devices, "covox");
	m_centronics->busy_handler().set("cent_status_in", FUNC(input_buffer_device::write_bit7));

	INPUT_BUFFER(config, "cent_status_in");

	/* quickload */
	quickload_image_device &quickload(QUICKLOAD(config, "quickload", "bin,snp", attotime::from_seconds(4)));
	quickload.set_load_callback(FUNC(sorcerer_state::quickload_cb));
	quickload.set_interface("sorcerer_quik");

	CASSETTE(config, m_cassette1);
	m_cassette1->set_formats(sorcerer_cassette_formats);
	m_cassette1->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette1->add_route(ALL_OUTPUTS, "mono", 0.05); // cass1 speaker
	m_cassette1->set_interface("sorcerer_cass");

	CASSETTE(config, m_cassette2);
	m_cassette2->set_formats(sorcerer_cassette_formats);
	m_cassette2->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette2->add_route(ALL_OUTPUTS, "mono", 0.05); // cass2 speaker
	m_cassette2->set_interface("sorcerer_cass");

	/* cartridge */
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "sorcerer_cart", "bin,rom");

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("sorcerer_cart");
	SOFTWARE_LIST(config, "cass_list").set_original("sorcerer_cass");
	SOFTWARE_LIST(config, "quik_list").set_original("sorcerer_quik");

	// internal ram
	RAM(config, RAM_TAG).set_default_size("48K").set_extra_options("8K,16K,32K");
}

static void floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

#define FLOPPY_0 "floppy0"
#define FLOPPY_1 "floppy1"
#define FLOPPY_2 "floppy2"
#define FLOPPY_3 "floppy3"

void sorcererd_state::sorcererd(machine_config &config)
{
	sorcerer(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &sorcererd_state::sorcererd_mem);
	m_maincpu->set_addrmap(AS_IO, &sorcererd_state::sorcererd_io);

	MICROPOLIS(config, m_fdc, 0);
	m_fdc->set_drive_tags(FLOPPY_0, FLOPPY_1, FLOPPY_2, FLOPPY_3);

	LEGACY_FLOPPY(config, FLOPPY_0, 0, &sorcerer_floppy_interface);
	LEGACY_FLOPPY(config, FLOPPY_1, 0, &sorcerer_floppy_interface);
	LEGACY_FLOPPY(config, FLOPPY_2, 0, &sorcerer_floppy_interface);
	LEGACY_FLOPPY(config, FLOPPY_3, 0, &sorcerer_floppy_interface);

	FD1793(config, m_fdc2, 8_MHz_XTAL / 8);  // confirmed clock
	m_fdc2->set_force_ready(true);
	m_fdc2->intrq_wr_callback().set([this] (bool state) { sorcererd_state::intrq2_w(state); });
	m_fdc2->drq_wr_callback().set([this] (bool state) { sorcererd_state::drq2_w(state); });
	FLOPPY_CONNECTOR(config, "fdc2:0", floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc2:1", floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	SOFTWARE_LIST(config, "flop_list").set_original("sorcerer_flop");
}

void sorcerer_state::sorcerera(machine_config &config)
{
	sorcerer(config);
	m_maincpu->set_addrmap(AS_IO, &sorcerer_state::sorcerera_io);
	m_maincpu->halt_cb().set([this] (bool state) { m_halt = state; }); // 1 = halted

	WD2793(config, m_fdc4, 4_MHz_XTAL / 2);
	m_fdc4->intrq_wr_callback().set([this] (bool state) { sorcerer_state::intrq4_w(state); });
	m_fdc4->drq_wr_callback().set([this] (bool state) { sorcerer_state::intrq4_w(state); });
	FLOPPY_CONNECTOR(config, "fdc4:0", floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc4:1", floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc4:2", floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc4:3", floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	//SOFTWARE_LIST(config, "flop_list").set_original("sorcerer_flop");   // no suitable software yet

	// internal ram
	config.device_remove(RAM_TAG);
	RAM(config, RAM_TAG).set_default_size("48K");   // must have 48k to be able to boot floppy
}

void sorcerer_state::sorcererb(machine_config &config)
{
	sorcerer(config);
	m_maincpu->set_addrmap(AS_IO, &sorcerer_state::sorcererb_io);
	m_maincpu->busack_cb().set(m_dma, FUNC(z80dma_device::bai_w));

	Z80DMA(config, m_dma, ES_CPU_CLOCK);
	m_dma->out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	m_dma->in_mreq_callback().set(FUNC(sorcerer_state::memory_read_byte));
	m_dma->out_mreq_callback().set(FUNC(sorcerer_state::memory_write_byte));
	m_dma->in_iorq_callback().set(FUNC(sorcerer_state::io_read_byte));
	m_dma->out_iorq_callback().set(FUNC(sorcerer_state::io_write_byte));

	FD1793(config, m_fdc3, 4_MHz_XTAL / 2);
	m_fdc3->set_force_ready(true);
	m_fdc3->drq_wr_callback().set(m_dma, FUNC(z80dma_device::rdy_w));
	FLOPPY_CONNECTOR(config, "fdc3:0", floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc3:1", floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc3:2", floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc3:3", floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	//SOFTWARE_LIST(config, "flop_list").set_original("sorcerer_flop");   // no suitable software yet

	config.device_remove("cart_list");
	config.device_remove("cartslot");

	// internal ram
	config.device_remove(RAM_TAG);
	RAM(config, RAM_TAG).set_default_size("56K");   // must have 56k to be able to boot CP/M floppy
}


/***************************************************************************

  Game driver(s)

***************************************************************************/
ROM_START(sorcerer)
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("exmo1-1.1e",          0x0000, 0x0800, CRC(ac924f67) SHA1(72fcad6dd1ed5ec0527f967604401284d0e4b6a1) )
	ROM_LOAD("exmo1-2.2e",          0x0800, 0x0800, CRC(ead1d0f6) SHA1(c68bed7344091bca135e427b4793cc7d49ca01be) )

	ROM_REGION( 0x0400, "chargen", 0)
	ROM_LOAD("exchr-1.20d",         0x0000, 0x0400, CRC(4a7e1cdd) SHA1(2bf07a59c506b6e0c01ec721fb7b747b20f5dced) )

	ROM_REGION( 0x0400, "proms", 0 )   // unused
	ROM_LOAD_OPTIONAL("bruce.15b",  0x0000, 0x0020, CRC(fae922cb) SHA1(470a86844cfeab0d9282242e03ff1d8a1b2238d1) ) // video prom type 6331
ROM_END

ROM_START(sorcererd)
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_SYSTEM_BIOS(0, "standard", "Standard") // To boot floppy in flop1, GO BC00
	ROMX_LOAD("exmo1-1.1e",         0x0000, 0x0800, CRC(ac924f67) SHA1(72fcad6dd1ed5ec0527f967604401284d0e4b6a1), ROM_BIOS(0) )
	ROMX_LOAD("exmo1-2.2e",         0x0800, 0x0800, CRC(ead1d0f6) SHA1(c68bed7344091bca135e427b4793cc7d49ca01be), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "sm705", "ESAG 1.3/B") // To boot floppy in flop5, press Ctrl-X
	ROMX_LOAD("right.1e",           0x0000, 0x0800, CRC(95586fea) SHA1(9263b0c5f059b70799e0704aa18437b04487e1b0), ROM_BIOS(1) )
	ROM_IGNORE(0x800)
	ROMX_LOAD("left.2e",            0x0800, 0x0800, CRC(153d1628) SHA1(e9421e8eeaa5945d0e1e5135058bfe9796db8458), ROM_BIOS(1) )
	ROM_IGNORE(0x800)
	ROM_SYSTEM_BIOS(2, "sm658", "Standard Monitor 658 ver 1.3/C") // To boot floppy in flop5, press Ctrl-X
	ROMX_LOAD("13c.1e",             0x0000, 0x0800, CRC(c3c56505) SHA1(6b88f9911b897825b10f8184ddf27af5d8cbdc4d), ROM_BIOS(2) )
	ROMX_LOAD("13c.2e",             0x0800, 0x0800, CRC(e1ac92a8) SHA1(302096c500cc87f0441f000a01b5ddfa3c102662), ROM_BIOS(2) )

	ROM_REGION( 0x0400, "chargen", 0)
	ROM_LOAD("exchr-1.20d",         0x0000, 0x0400, CRC(4a7e1cdd) SHA1(2bf07a59c506b6e0c01ec721fb7b747b20f5dced) )

	ROM_REGION( 0x0400, "diskboot", 0)
	ROM_LOAD("diskboot.dat",        0x0000, 0x0100, CRC(d82a40d6) SHA1(cd1ef5fb0312cd1640e0853d2442d7d858bc3e3b) ) // micropolis floppy boot
	ROM_LOAD("boot.bin",            0x0300, 0x0100, CRC(352e36bc) SHA1(99678e3cc4f315a0cf7d52aae511e405dc314190) ) // video/disk unit floppy boot

	ROM_REGION( 0x0400, "proms", 0 )   // unused
	ROM_LOAD_OPTIONAL("bruce.15b",  0x0000, 0x0020, CRC(fae922cb) SHA1(470a86844cfeab0d9282242e03ff1d8a1b2238d1) ) // video prom type 6331
	// from video/disk unit
	ROM_LOAD_OPTIONAL("sad4e.4e",   0x0100, 0x0100, CRC(b468a3f9) SHA1(8546f834901349baf59fc436c1a7cc57d541cddd) )
	ROM_LOAD_OPTIONAL("l.2e",       0x0200, 0x0100, CRC(9cb2500e) SHA1(d473c8dc042a4ace75174a93069fc0e9451763bd) )
	ROM_LOAD_OPTIONAL("h.3e",       0x0300, 0x0100, CRC(3c6163fb) SHA1(60ecefe461357eacfca64427931db6472283d0e3) )
ROM_END

ROM_START(sorcerer2)
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_SYSTEM_BIOS(0, "standard", "Standard")
	ROMX_LOAD("exm011-1.1e",        0x0000, 0x0800, CRC(af9394dc) SHA1(d7e0ada64d72d33e0790690be86a36020b41fd0d), ROM_BIOS(0) )
	ROMX_LOAD("exm011-2.2e",        0x0800, 0x0800, CRC(49978d6c) SHA1(b94127bfe99e5dc1cf5dbbb7d1b099b0ca036cd0), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "standard12", "EXMON 1.2") // unknown if this is a real Exidy monitor that leaked, or a hack.
	ROMX_LOAD("ex1-2.1e",           0x0000, 0x0800, CRC(7f915d7b) SHA1(9b8cd779019bd736595af888dc86b1ec0e7066c2), ROM_BIOS(1) )
	ROMX_LOAD("ex1-2.2e",           0x0800, 0x0800, CRC(dc859453) SHA1(c3130a34365a1a7c5ef63188ade90a17780d7b0a), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS(2, "dwmon22a", "DWMON 2.2A")
	ROMX_LOAD("dwmon22a.1e",        0x0000, 0x0800, CRC(82f78769) SHA1(6b999738c160557452fc25cbbe9339cfe651768b), ROM_BIOS(2) )
	ROMX_LOAD("dwmon22a.2e",        0x0800, 0x0800, CRC(6239871b) SHA1(e687bc9669c310a3d2debb87f79d168017f35f34), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS(3, "dwmon22c", "DWMON 2.2C")
	ROMX_LOAD("dwmon22c.1e",        0x0000, 0x0800, CRC(a22db498) SHA1(ebedbce7454007f5a02fafe449fd09169173d7b3), ROM_BIOS(3) )
	ROMX_LOAD("dwmon22c.2e",        0x0800, 0x0800, CRC(7b22b65a) SHA1(7f23dd308f34b6d795d6df06f2387dfd17f69edd), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS(4, "ddmon", "DDMON 1.3")
	ROMX_LOAD("ddmon.1e",           0x0000, 0x0800, CRC(6ce481da) SHA1(c927762b29a281b7c13d59bb17ea56494c64569b), ROM_BIOS(4) )
	ROMX_LOAD("ddmon.2e",           0x0800, 0x0800, CRC(50069b13) SHA1(0808018830fac15cceaed8ff2b19900f77447470), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS(5, "adsmon", "ADSMON") // This requires an unemulated 80-column card. You can type 64 to get 64-columns, but it's mostly off the side.
	ROMX_LOAD("adsmon.1e",          0x0000, 0x0800, CRC(460f981a) SHA1(bdae1d87b9e8ae2cae11663acd349b9ed2387094), ROM_BIOS(5) )
	ROMX_LOAD("adsmon.2e",          0x0800, 0x0800, CRC(cb3f1dda) SHA1(3fc14306e83d73b9b9afd9b543566e52ba3e008f), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS(6, "tvc", "TVI-MON-C-V1.5") // unknown disk support (BO is the floppy boot command)
	ROMX_LOAD("tvc-1.1e",           0x0000, 0x0800, CRC(efc15a18) SHA1(3dee821270a0d83453b18baed88a024dfd0d7a6c), ROM_BIOS(6) )
	ROMX_LOAD("tvc-2.2e",           0x0800, 0x0800, CRC(bc194487) SHA1(dcfd916558e3e3be22091c5558ea633c332cf6c7), ROM_BIOS(6) )

	ROM_REGION( 0x0400, "chargen", 0)
	ROM_LOAD("exchr-1.20d",         0x0000, 0x0400, CRC(4a7e1cdd) SHA1(2bf07a59c506b6e0c01ec721fb7b747b20f5dced) )

	ROM_REGION( 0x0400, "proms", 0 )   // unused
	ROM_LOAD_OPTIONAL("bruce.15b",  0x0000, 0x0020, CRC(fae922cb) SHA1(470a86844cfeab0d9282242e03ff1d8a1b2238d1) ) // video prom type 6331
ROM_END

ROM_START(sorcerera)
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_SYSTEM_BIOS(0, "scuamon6434", "SCUAMON64 3.4")
	ROMX_LOAD("scua34.1e",          0x0000, 0x1000, CRC(7ff21d97) SHA1(b936cda0f2acb655fb4c1a4e7976274558543c7e), ROM_BIOS(0) )

	ROM_REGION( 0x0400, "chargen", 0)
	ROM_LOAD("exchr-1.20d",         0x0000, 0x0400, CRC(4a7e1cdd) SHA1(2bf07a59c506b6e0c01ec721fb7b747b20f5dced) )

	ROM_REGION( 0x0400, "proms", 0 )   // unused
	ROM_LOAD_OPTIONAL("bruce.15b",  0x0000, 0x0020, CRC(fae922cb) SHA1(470a86844cfeab0d9282242e03ff1d8a1b2238d1) ) // video prom type 6331
ROM_END

ROM_START(sorcererb)
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_SYSTEM_BIOS(0, "scuamon64", "SCUAMON64")   // DI to boot floppy
	ROMX_LOAD("scua1.1e",           0x0000, 0x0800, CRC(0fcf1de9) SHA1(db8371eabf50a9da43ec7f717279a31754351359), ROM_BIOS(0) )
	ROM_CONTINUE(0x0000, 0x800)
	ROMX_LOAD("scua1.2e",           0x0800, 0x0800, CRC(aa9a6ca6) SHA1(bcaa7457a1b892ed82c1a04ee21a619faa7c1a16), ROM_BIOS(0) )
	ROM_CONTINUE(0x0800, 0x800)
	ROM_SYSTEM_BIOS(1, "scuamon80", "SCUAMON80 1.0") // This works with disks, but requires an unemulated 80-column card.
	ROMX_LOAD("scua1.1e",           0x0000, 0x0800, CRC(0fcf1de9) SHA1(db8371eabf50a9da43ec7f717279a31754351359), ROM_BIOS(1) )
	ROM_IGNORE(0x800)
	ROMX_LOAD("scua1.2e",           0x0800, 0x0800, CRC(aa9a6ca6) SHA1(bcaa7457a1b892ed82c1a04ee21a619faa7c1a16), ROM_BIOS(1) )
	ROM_IGNORE(0x800)
	ROM_SYSTEM_BIOS(2, "scuamon64dd", "SCUAMON64DD")   // DI to boot floppy
	ROMX_LOAD("devinb.1e",          0x0000, 0x0800, CRC(a2ea2f93) SHA1(8f9298f1641806dfba819ead318a4838385223fe), ROM_BIOS(2) )
	ROM_CONTINUE(0x0000, 0x800)
	ROMX_LOAD("devinb.2e",          0x0800, 0x0800, CRC(4d9ea9a5) SHA1(1a3c8cf98d4caed6044b1b01cd79dcd9c61dc1e1), ROM_BIOS(2) )
	ROM_CONTINUE(0x0800, 0x800)

	ROM_REGION( 0x0400, "chargen", 0)
	ROM_LOAD("exchr-1.20d",         0x0000, 0x0400, CRC(4a7e1cdd) SHA1(2bf07a59c506b6e0c01ec721fb7b747b20f5dced) )

	ROM_REGION( 0x0400, "proms", 0 )   // unused
	ROM_LOAD_OPTIONAL("bruce.15b",  0x0000, 0x0020, CRC(fae922cb) SHA1(470a86844cfeab0d9282242e03ff1d8a1b2238d1) ) // video prom type 6331
ROM_END

/*    YEAR  NAME       PARENT    COMPAT  MACHINE    INPUT     STATE            INIT           COMPANY      FULLNAME */
COMP( 1979, sorcerer,  0,        0,      sorcerer,  sorcerer, sorcerer_state,  empty_init, "Exidy Inc", "Sorcerer", MACHINE_SUPPORTS_SAVE )
COMP( 1979, sorcerer2, sorcerer, 0,      sorcerer,  sorcerer, sorcerer_state,  empty_init, "Exidy Inc", "Sorcerer 2", MACHINE_SUPPORTS_SAVE )
COMP( 1979, sorcererd, sorcerer, 0,      sorcererd, sorcerer, sorcererd_state, empty_init, "Exidy Inc", "Sorcerer (with Micropolis fdc)", MACHINE_SUPPORTS_SAVE )
COMP( 1979, sorcerera, sorcerer, 0,      sorcerera, sorcerer, sorcerer_state,  empty_init, "Exidy Inc", "Sorcerer (with Dreamdisk fdc)", MACHINE_SUPPORTS_SAVE )
COMP( 1979, sorcererb, sorcerer, 0,      sorcererb, sorcerer, sorcerer_state,  empty_init, "Exidy Inc", "Sorcerer (with Digitrio fdc)", MACHINE_SUPPORTS_SAVE )




source.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:m1macrophage

/*
The Moog Source is a CPU-controlled analog monosynth. It lacks knobs and
sliders. Sound parameters are modified by pressing a button for a specific
parameter and using the encoder wheel to modify it.

The architecture of this synthesizer is typical of digitally-controlled analog
synthesizers. The firmware is responsible for:
* Scanning and reacting to membrane button presses. This is a typical key
  matrix setup (see buttons_latch_w(), buttons_a_r(), buttons_b_r()).
* Detecting which key is pressed on the keyboard (see get_keyboard_v()).
* Setting Control Voltages (aka CVs). Detailed info in cv_w().
* Configuring audio and modulation routing through 4016 switches. See
  output_latch_a_w(), output_latch_b_w().
* Controlling the Loudness and Filter envelope generators (EGs). Starts the
  Attack phase when a key is pressed, detects when an EG peaks and transitions
  it to the Decay phase, and transitions EGs to the Release phase when a key is
  released.
* Controlling the LED displays and cassette I/O.

The 16 sound programs are stored in battery-backed NVRAM, and can also be stored
to (and loaded from) a cassette.

This driver is based on the Source's schematics. Most of the circuitry
relevant to this driver is on Board 3 (digital board). Component designations in
comments refer to Board 3, unless otherwise noted.

This driver attempts to accurately emulate the digital and digital-analog
interface of the synthesizer, including all analog behavior that is relevant to
the firmware. The analog audio circuit is not emulated. The driver includes an
interactive layout, and is intended as an educational tool.
*/

#include "emu.h"

#include "nl_source.h"

#include "cpu/z80/z80.h"
#include "machine/netlist.h"
#include "machine/nvram.h"
#include "machine/rescap.h"
#include "machine/timer.h"
#include "sound/va_eg.h"

#include "moog_source.lh"

#define LOG_CV                  (1U << 1)
#define LOG_BUTTONS             (1U << 2)
#define LOG_ENCODER             (1U << 3)
#define LOG_KEYBOARD            (1U << 4)
#define LOG_CV_KEYBOARD_APPROX  (1U << 5)
#define LOG_LFO                 (1U << 6)
#define LOG_LFO_TIMER           (1U << 7)
#define LOG_CONTOUR             (1U << 8)

#define VERBOSE (LOG_GENERAL | LOG_CV)
//#define LOG_OUTPUT_FUNC osd_printf_info

#include "logmacro.h"

namespace {

constexpr const char MAINCPU_TAG[] = "z80";
constexpr const char NVRAM_TAG[] = "nvram";

class source_state : public driver_device
{
public:
	static constexpr feature_type unemulated_features() { return feature::TAPE; }

	source_state(const machine_config &mconfig, device_type type, const char *tag) ATTR_COLD
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, MAINCPU_TAG)
		, m_contour(*this, "contour_%d", 0)
		, m_contour_rate(*this, "source_nl:cntr_rate_%d", 0)
		, m_contour_range(*this, "contour_range_%d",0)
		, m_lfo_timer(*this, "lfo_timer")
		, m_lfo_rate(*this, "source_nl:lfo_rate")
		, m_lfo_range(*this, "lfo_range")
		, m_octave_io(*this, "octave_buttons")
		, m_button_a_io(*this, "button_group_a_%d", 0U)
		, m_button_b_io(*this, "button_group_b_%d", 0U)
		, m_keyboard_io(*this, "keyboard_oct_%d", 1U)
		, m_encoder(*this, "incremental_controller")
		, m_trigger_io(*this, "trigger_in")
		, m_octave_led(*this, "octave_led_%d")
		, m_lfo_rate_led(*this, "mod_rate_led")
		, m_program_display(*this, "program_digit_%d")
		, m_edit_display(*this, "edit_digit_%d")
		, m_edit_led(*this, "edit_led")
		, m_kb_track(*this, "kb_track")
		, m_osc_waveform(*this, "osc_%d_waveform", 1U)
		, m_sync(*this, "sync")
		, m_lfo_to_filter(*this, "lfo_to_filter")
		, m_lfo_to_osc(*this, "lfo_to_osc")
		, m_lfo_shape(*this, "lfo_shape")
		, m_trigger_out(*this, "trigger_out")
		, m_cv(int(CV::SIZE), 0)
	{}

	void source(machine_config &config) ATTR_COLD;

	DECLARE_INPUT_CHANGED_MEMBER(octave_button_pressed);
	DECLARE_INPUT_CHANGED_MEMBER(encoder_moved);

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

private:
	void update_octave_leds();

	void edit_latch_w(u8 data);
	void output_latch_a_w(u8 data);
	void output_latch_b_w(u8 data);
	void buttons_latch_w(u8 data);
	void program_latch_w(u8 data);
	void cassette_w(u8 data);
	void cv_w(offs_t offset, u8 data);

	bool contour_peaked(const va_rc_eg_device &eg) const;
	float get_keyboard_v() const;
	u8 keyboard_r();
	u8 buttons_r(const required_ioport_array<6> &button_io, const char *name) const;
	u8 buttons_a_r();
	u8 buttons_b_r();
	u8 encoder_r();

	template<int Which> NETDEV_ANALOG_CALLBACK_MEMBER(contour_cv_changed);
	template<int Which> TIMER_CALLBACK_MEMBER(update_contour);

	NETDEV_ANALOG_CALLBACK_MEMBER(lfo_cv_changed);
	TIMER_CALLBACK_MEMBER(update_lfo_timer);
	TIMER_DEVICE_CALLBACK_MEMBER(lfo_timer_tick);

	void memory_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;

	required_device_array<va_rc_eg_device, 2> m_contour;
	required_device_array<netlist_mame_analog_input_device, 2> m_contour_rate;
	required_ioport_array<2> m_contour_range;

	required_device<timer_device> m_lfo_timer;
	required_device<netlist_mame_analog_input_device> m_lfo_rate;
	required_ioport m_lfo_range;

	required_ioport m_octave_io;
	required_ioport_array<6> m_button_a_io;
	required_ioport_array<6> m_button_b_io;
	required_ioport_array<4> m_keyboard_io;
	required_ioport m_encoder;
	required_ioport m_trigger_io;

	output_finder<2> m_octave_led;
	output_finder<> m_lfo_rate_led;
	output_finder<2> m_program_display;
	output_finder<2> m_edit_display;
	output_finder<> m_edit_led;
	output_finder<> m_kb_track;
	output_finder<2> m_osc_waveform;
	output_finder<> m_sync;
	output_finder<> m_lfo_to_filter;
	output_finder<> m_lfo_to_osc;
	output_finder<> m_lfo_shape;
	output_finder<> m_trigger_out;

	bool m_octave_hi = true;  // `true` due to internal pullups of 74LS367 and 7404.
	u8 m_button_row_latch = 0xff;
	bool m_encoder_incr = false;
	bool m_lfo_state = false;  // Square output of the LFO. -14V (false) to 14V (true).

	float m_lfo_cc = 0;  // Control current into the LFO OTA.
	std::array<float, 2> m_contour_cc = { 0, 0 };  // Control currents into the EG OTAs.
	std::vector<float> m_cv;

	enum contour_type
	{
		FILTER_CONTOUR = 0,
		LOUDNESS_CONTOUR
	};

	// All MUXes are CD4051B.
	// Component designations refer to board 2 (synthesizer board).
	// The enum names match the CV labels in the schematic, but some
	// abbreviations are expanded.
	enum class CV : int
	{
		// U2
		CUTOFF_COARSE = 0,
		AUTO_TUNE_2,
		INT_COARSE,
		CUTOFF_FINE,
		PW_1,
		PW_2,
		INT_FINE,
		OCT_2,

		// U4
		FILTER_CONTOUR_LEVEL,
		OCT_1,
		GLIDE,
		LOUDNESS_CONTOUR_LEVEL,
		OSC_2,
		NOISE,
		UNUSED,  // Sampled in (C22, U10A), but not used.
		OSC_1,

		// U5
		EMPHASIS,
		NOT_CONNECTED,  // U5, Y1 (pin 14) is not connected.
		AMT,  // Filter contout amount.
		MOD_RATE,  // Modulation (LFO) rate.
		KEYBOARD_APPROX,
		KEYBOARD_CV,
		FILTER_CONTOUR_RATE,
		LOUDNESS_CONTOUR_RATE,

		SIZE
	};

	static inline constexpr float VMINUS = -15;  // In Volts.
	static inline constexpr float MAX_CV = 10;  // In Volts.
	static inline constexpr float CA3080_VABC = VMINUS + 0.7;  // 1 diode drop above -15.
	static inline constexpr float CONTOUR_C = CAP_U(0.047);  // C57 (filter), C56 (loudness).
	static inline constexpr u8 PATTERNS_7447[16] =
	{
		0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7c, 0x07,
		0x7f, 0x67, 0x58, 0x4c, 0x62, 0x69, 0x78, 0x00,
	};
};

void source_state::update_octave_leds()
{
	m_octave_led[0] = m_octave_hi ? 0 : 1;
	m_octave_led[1] = m_octave_hi ? 1 : 0;
}

void source_state::edit_latch_w(u8 data)
{
	// U3 (74LS378) 0-4 (D0-D4) -> U3 (7447, Board 4) A-D -> U4 (MAN 3610A).
	// U3 4-5 not connected.
	m_edit_display[0] = PATTERNS_7447[data & 0x0f];

	// U4 (74LS378) 0-4 (D4-D7)-> U5 (7447, Board 4) A-D -> U6 (MAN 3610A).
	m_edit_display[1] = PATTERNS_7447[(data >> 4) & 0x0f];

	// U4 (74LS378) 5, 6 (D0, D7) -> J1-5 (cassette interface, "cassette out").
	// TODO: Add cassette support.
}

void source_state::output_latch_a_w(u8 data)
{
	// Latch is U11, 74LS378 (Board 3). 6-bit latch, top 2 bits ignored.
	// All component designations are for Board 2.

	// Keyboard tracking for filter, bits D0 and D1.
	const u8 kb_track = data & 0x03;
	if (kb_track == 1)
	{
		// 1/2 tracking. U36C on and U36B off.
		// Keyboard CV mixed in via 342Kohm resistance (2 x 121K: R142, R143).
		m_kb_track = 1;
	}
	else if (kb_track == 2 || kb_track == 3)
	{
		// Full tracking.
		// Only U36B on (kb_track == 2), or both U36B and U36C on
		// (kb_track == 3).
		// In both cases, keyboard CV is mixed in via a 121KOhm resistor (R142).
		m_kb_track = 2;
	}
	else
	{
		// Both U36B and U36B are off. Keyboard CV does not make it through.
		m_kb_track = 0;
	}

	// Osc 2 waveform, bits D2 and D3.
	// 0 - Sawtooth (U32A closed, U32B open, U32C closed, U32D closed).
	// 1 - Triangle (U32A closed, U32B closed, U32D open, U32C open).
	// 2 - Square/pulse (U32A open, U32B closed, U32C open, U32D closed).
	// 3 - Mix of Triange and Square/Pulse (probably unused)
	//     (U32A open, U32B closed, U32C open, U32D open).
	m_osc_waveform[1] = (data >> 2) & 0x03;

	// Osc 1 waveform, bits D4 and D5.
	// Same swithc configuration as above, but replace U32 with U23.
	m_osc_waveform[0] = (data >> 4) & 0x03;
}

void source_state::output_latch_b_w(u8 data)
{
	// Latch is U12, 74LS378 (Board 3). 6-bit latch, top 2 bits ignored.

	// D0 -> S21-16 -> Sync. Synchronizes osc 2 to osc 1.
	// When sync is on, the pitch wheel is only routed to Osc 2.
	// When 1, Q2 is "off", U18B and C are off, U18A on, pitch wheel routed
	// to osc 2 only.
	// When 0, Q2 is "on", U18B is on (routes pitch wheel to central pitch),
	// U18C is "on", which turns off U18A (disables direct route to osc 2).
	m_sync = BIT(data, 0);

	// D1 -> inverted by U2A (Board 3), and connected to cathode of led.
	// The led is active low, but the inverter presents it as active high.
	m_edit_led = BIT(data, 1);

	// D2 -> J2 S-TRIG OUT, inverted via Q1, R21, R20 (Board 3).
	m_trigger_out = BIT(data, 2) ? 0 : 1;

	// Component designations below refer to Board 2.

	// D3 -> S22-4 -> U46A (inverted and level-shifted to -5-5V through 4007B
	// and R213): mod to filter.
	m_lfo_to_filter = BIT(data, 3) ? 0 : 1;

	// D4 -> S22-3 -> U18D (inverted and level-shifted to -5-5V through 4007B
	// and R61): mod to osc.
	m_lfo_to_osc = BIT(data, 4) ? 0 : 1;

	// D5 -> S22-2 (level shifted & inverted to -5-5V through 4007B and R214)
	//       0 - Triange (U46C on, U46B on, turns off U46D). -1.5V - 1.5V.
	//       1 - Square (U46C off, U46B off, U46D on/off controlled by square
	//           wave. Translates -14V - 14V wave to 0-5V.
	m_lfo_shape = BIT(data, 5);
}

void source_state::buttons_latch_w(u8 data)
{
	// U5, 74LS378. All output connected to diode cathodes.
	// Connected to "Membrane switch interface", P1, "top left".
	// (D0, D1, D2, D3, D4, D5) -> (P1-1, P1-5, P1-6, P1-2, P1-4, P1-3)
	m_button_row_latch = data & 0x3f;  // Only D0-D5 connected.
}

void source_state::program_latch_w(u8 data)
{
	// U1, 74LS378
	// D0-D3 -> U1 (7447, Board 4) A-D -> right digit of MAN6630.
	// D4 -> inverted (U2E, U2D, 7404) -> left 1/2 digit of MAN6630
	//       (inputs a and b).
	// D5 -> inverted (U2A, 7404) -> HOLD ->"plus" sign of MAN6630.
	// Note that MAN6630 has 3 "digits". From right to left:
	// - 7-segment digit.
	// - 2-segment digit (can represent a "1").
	// - "+" sign.
	// Here, we simulate this with two 7-segment digits.

	m_program_display[0] = PATTERNS_7447[data & 0x0f];

	u8 digit1 = PATTERNS_7447[15];  // All segments off.
	if (BIT(data, 4))
	{
		digit1 |= PATTERNS_7447[1];  // Turn on segments for "1".
	}
	if (BIT(data, 5))
	{
		// This enables two segments on the MAN6630 that display a "+" symbol.
		// Since 7-segment displays don't support that, enable the segment for
		// "-" instead.
		digit1 |= 0x40;
	}
	m_program_display[1] = digit1;
}

void source_state::cassette_w(u8 data)
{
	// Z80 D4 controlls a normally-open relay (K1) through U22A (74LS74).
	// A low D4 powers the relay, which connects cassette jack J1-1 to J1-3.
	// TODO: Add cassette support.
}

void source_state::cv_w(offs_t offset, u8 data)
{
	// CVs are generated by writing to an AM6012 12-bit DAC, but only 8 bits are
	// used: the 8 MSBs are connected to the data bus, and the 4 LSBs are
	// grounded. The DAC is mapped to the Z80's port IO space.
	// The DAC and support circuitry convert the 8 bit data (0-255) to a voltage
	// (0-10V). That voltage is routed to the Sample & Hold circuit (a
	// capacitor and a buffer) of a specific CV, controlled by A0-A4.

	// Interesting tidbit: In most designs, the DAC inputs are
	// latched. In the Source, the DAC inputs are directly connected to the data
	// bus. This means the DAC output voltage is constantly changing, in an
	// attempt to track the data bus.

	if (!machine().side_effects_disabled())
	{
		// U14, U15B, U16D,E and U17D generate WAIT states whenever there is
		// an IO write. This lasts 32 cycles.
		// For the first 8 cycles, no MUX is selected, to allow the DAC to
		// settle. Then a specific channel in a specific MUX is enabled (based
		// on the port address), and there's a WAIT for another 24 cycles, to
		// allow the selected Sample & Hold capacitor to (dis)charge.
		m_maincpu->adjust_icount(-(8 + 24));
	}

	// Z80 A0,A1,A2 connected to the A,B,C inputs (respectively) of all MUXes.
	// Z80 A3,A4 select which MUX to enable via decoder 74LS155.
	// The fourth output of the decoder is not connected. There are 3 muxes.

	if (offset >= offs_t(CV::SIZE))
		return;

	const float cv = MAX_CV * data / 255.0F;
	if (cv == m_cv[offset])
		return;
	m_cv[offset] = cv;

	switch (offset)
	{
		case offs_t(CV::MOD_RATE):
			m_lfo_rate->write(cv);
			break;
		case offs_t(CV::LOUDNESS_CONTOUR_RATE):
			m_contour_rate[LOUDNESS_CONTOUR]->write(cv);
			break;
		case offs_t(CV::LOUDNESS_CONTOUR_LEVEL):
			machine().scheduler().synchronize(timer_expired_delegate(FUNC(source_state::update_contour<LOUDNESS_CONTOUR>), this));
			break;
		case offs_t(CV::FILTER_CONTOUR_RATE):
			m_contour_rate[FILTER_CONTOUR]->write(cv);
			break;
		case offs_t(CV::FILTER_CONTOUR_LEVEL):
			machine().scheduler().synchronize(timer_expired_delegate(FUNC(source_state::update_contour<FILTER_CONTOUR>), this));
			break;
	}

	if (offset == offs_t(CV::KEYBOARD_APPROX))
		LOGMASKED(LOG_CV_KEYBOARD_APPROX, "CV %d: 0x%02x, %f\n", offset, data, cv);
	else
		LOGMASKED(LOG_CV, "CV %d: 0x%02x, %f\n", offset, data, cv);
}

bool source_state::contour_peaked(const va_rc_eg_device &eg) const
{
	// The peak detector circuits for the filter and loudness contour generators
	// are identical. They are located on board 2, and based on an LM393 comparator
	// (U41B and U41A respectively). The threshold is set to ~9.984V, with a
	// +-0.005V hysteresis. The EG output is connected to the inverting input.
	// The comparator output is 0V when the EG output is above the threshold, and
	// 5V otherwise (open collector output pulled to 5V via 10K resistors, R193
	// and R190 respectively).

	constexpr float R192 = RES_M(4.7);  // R188 for loudness EG.
	constexpr float R191 = RES_K(10);   // R189 for loudness EG.
	constexpr float RISING_THRESHOLD = 5 * RES_VOLTAGE_DIVIDER(R191, R192) + 5;
	constexpr float FALLING_THRESHOLD = 10 * RES_VOLTAGE_DIVIDER(R191, R192);
	static_assert(RISING_THRESHOLD > FALLING_THRESHOLD);

	const float eg_v = eg.get_v();
	if (eg_v > RISING_THRESHOLD)
	{
		return true;
	}
	else if (eg_v < FALLING_THRESHOLD)
	{
		return false;
	}
	else  // eg_v is within the hysteresis range.
	{
		// Proper emulation of hysteresis would require a streaming (or otherwise
		// stateful) comparator. But a heuristic tailored to this use case works
		// fine: if the EG voltage is falling, assume we hit the 'rising' threshold
		// in the past, so the 'falling' threshold is the active one.

		// This heuristic will pick the wrong threshold if a key is released while
		// the EG is within the hysteresis range. But that should be rare (the
		// hysteresis range is ~0.01V), and inconsequential. The difference in
		// thresholds is very small, and the firmware is the one that initiates
		// the EG release and is probably ignoring this input until the next
		// attack.

		const float future_eg_v = eg.get_v(machine().time() + attotime::from_msec(1));
		return future_eg_v < eg_v;
	}
}

float source_state::get_keyboard_v() const
{
	// *** Detect which key is pressed.

	constexpr int OCTAVES = 4;
	constexpr int KEYS_PER_OCTAVE = 12;
	constexpr int KEYS = 3 * KEYS_PER_OCTAVE + 1;
	constexpr int OCTAVE_KEYS[4] =
	{
		KEYS_PER_OCTAVE, KEYS_PER_OCTAVE, KEYS_PER_OCTAVE, 1
	};

	// The circuit is structure such that the lowest note has priority.
	// Scan from lowest, and exit the loop once a pressed key is found.
	int pressed_key = -1;
	for (int octave = 0; octave < OCTAVES; ++octave)
	{
		const u32 keys = m_keyboard_io[octave]->read();
		for (int key = 0; key < OCTAVE_KEYS[octave]; ++key)
		{
			if (BIT(keys, key))
			{
				pressed_key = octave * KEYS_PER_OCTAVE + key;
				break;
			}
		}
		if (pressed_key >= 0)
			break;
	}

	// *** Convert pressed key to a voltage.

	constexpr float KEYBOARD_VREF = 8.24F;  // From schematic.
	constexpr float RKEY = RES_R(100);
	constexpr float R74 = RES_R(150);
	constexpr float R76 = RES_K(220);
	constexpr float R77 = RES_K(2.2);

	float kb_voltage = 0;
	if (pressed_key >= 0)
	{
		// Pressing a key forms a voltage devider consisting of the lower and
		// upper resistances as shown below. The resulting voltage is further
		// reduced by another voltage divider (R77-R76) before being fed to
		// comparator U31A.
		const float lower_r = R74 + pressed_key * RKEY;
		const float upper_r = (KEYS - pressed_key - 1) * RKEY;
		const float v = KEYBOARD_VREF * RES_VOLTAGE_DIVIDER(upper_r, lower_r);
		kb_voltage = v * RES_VOLTAGE_DIVIDER(R77, R76);
		LOGMASKED(LOG_KEYBOARD, "Key %d - %f - %f\n", pressed_key, v,
				  kb_voltage);
	}
	return kb_voltage;
}

u8 source_state::keyboard_r()
{
	// U32: 74LS367
	// U18: 74LS125

	// D0 <- U32, KEYBD.
	// Output of comparator U31A. Compares the "KYBD APPROX" CV with the voltage
	// generated by the keyboad (see get_keyboard_r()). This bit is used by a
	// successive approximation algorithm to detect the keyboard voltage. The
	// firmware does a binary search by checking the result of the comparison
	// and updating the "KYBD APPROX" CV accordingly.
	// TODO: Compute keyboard voltage in an input callback.
	const u8 d0 = (get_keyboard_v() >= m_cv[int(CV::KEYBOARD_APPROX)]) ? 1 : 0;

	// D1 - Filter contour peak reached (active low).
	// D1 <- U32, FILT CNTR <- S22-11 <- Comparator (U41B, LM393).
	const u8 d1 = contour_peaked(*m_contour[FILTER_CONTOUR]) ? 0 : 1;

	// D2 - Loudness contour peak reached (active low).
	// D2 <- U32, LOUD CNTR <- S22-10 <- Comparator (U41A, LM393).
	const u8 d2 = contour_peaked(*m_contour[LOUDNESS_CONTOUR]) ? 0 : 1;

	// D3: Octave. <- U32, OCT (P34-2 (octave 0 button) and P34-1 (octave +1
	//                button) via U2B and U2C).
	const u8 d3 = m_octave_hi ? 1 : 0;

	// D4 <- J1-4, CASSETTE IN (through "cassette return" circuit and U18D).
	const u8 d4 = 1;  // TODO: Implement.

	// D5 <- U32 D5 <- MOD.
	// The square wave output of the LFO (~ -14V - 14V, connection S33-5) is
	// inverted and level-shifted by Q5, R85, R86. The emitter of Q4 (signal
	// name "MOD") is connected to U32's D5, and to the cathode of the "MOD
	// RATE" LED (connection P34-5).
	const u8 d5 = m_lfo_state ? 0 : 1;

	// D6 <- J2-5, S-TRIG IN, through U18C, pulled up by R23 and protected by
	//       R22.
	const u8 d6 = BIT(m_trigger_io->read(), 0);

	// D7 <- U32, N.C. <- 1 (data bus is pulled high).
	const u8 d7 = 1;

	return (d7 << 7) | (d6 << 6) | (d5 << 5) | (d4 << 4) |
		   (d3 << 3) | (d2 << 2) | (d1 << 1) | d0;
}

u8 source_state::buttons_r(
	const required_ioport_array<6> &button_io, const char *name) const
{
	// Button presses are active low, but the result is inverted by a CD4502.
	// So they look active high to the firmware.
	u8 pressed = 0x00;
	for (int i = 0; i < 6; ++i)
	{
		if (!BIT(m_button_row_latch, i))
			pressed |= u8(~button_io[i]->read() & 0xff);
	}
	// Bits 6 and 7 are not connected to the button input and pulled high.
	pressed |= 0xc0;
	if (pressed & 0x3f)
	{
		LOGMASKED(LOG_BUTTONS, "Button read %s - %02X: %02X\n",
				  name, m_button_row_latch, pressed);
	}
	return pressed;
}

u8 source_state::buttons_a_r()
{
	// U8, CD4502B (connceted to "Membrane switch interface", P2, "Bottom left")
	// (D0, D1, D2, D3, D4, D5) <- (P2-1, P2-3, P2-2, P2-6, P2-5, P2-4)
	return buttons_r(m_button_a_io, "A");
}

u8 source_state::buttons_b_r()
{
	// U9, CD4502B (connected to "Membrane switch interface", P3, "Bottom right")
	// (D0, D1, D2, D3, D4, D5, D6, D7) <- (P3-2, P3-1, P3-3, P3-6, P3-5, P3-4)
	return buttons_r(m_button_b_io, "B");
}

u8 source_state::encoder_r()
{
	// D0 contains whether the encoder was last incremented or decremented.
	LOGMASKED(LOG_ENCODER,
			  "Encoder read: %d - %d\n", m_encoder->read(), m_encoder_incr);
	// Reading the encoder's state also clears /INT (via U21B, U7A and U15A).
	if (!machine().side_effects_disabled())
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
	return m_encoder_incr ? 1 : 0;
}

template<int Which> NETDEV_ANALOG_CALLBACK_MEMBER(source_state::contour_cv_changed)
{
	// The control current (Iabc) into each envelope generator ("contour") CA3080
	// is determined by a voltage-to-exponential-current converter (see relevant
	// netlist).

	// This callback is invoked by the netlist simulation when the control current
	// changes. This happens when the firmware sets a new (dis)charge rate, or if
	// the "range" trimmer is adjusted.

	static_assert(Which == FILTER_CONTOUR || Which == LOUDNESS_CONTOUR);
	constexpr const char *CONTOUR_NAME = (Which == FILTER_CONTOUR) ? "Filter" : "Loudness";
	constexpr int RATE_CV_INDEX =
		(Which == FILTER_CONTOUR) ? int(CV::FILTER_CONTOUR_RATE) : int(CV::LOUDNESS_CONTOUR_RATE);

	// The netlist outputs a voltage. Convert it to a current.
	m_contour_cc[Which] = (data - CA3080_VABC) / RES_K(10);  // R198 for filter, R186 for loudness.
	machine().scheduler().synchronize(timer_expired_delegate(FUNC(source_state::update_contour<Which>), this));

	LOGMASKED(LOG_CONTOUR, "%s contour CC: %f uA, rate CV: %f, range trimmer: %d\n",
			  CONTOUR_NAME, m_contour_cc[Which] * 1e6F, m_cv[RATE_CV_INDEX], m_contour_range[Which]->read());
}

// Must be called with machine().scheduler().synchronize(...), to ensure the EG
// updates use the global time.
template<int Which> TIMER_CALLBACK_MEMBER(source_state::update_contour)
{
	// Each of the voltage-controlled envelope generators (called "contours" on
	// the schematic) are based on a CA3080 OTA configured as a current-controlled
	// resistor. This configuration is explained in the first 10 minutes of
	// https://www.youtube.com/watch?v=pTHHzFsa4Ss

	// The OTA (dis)charges a capacitor to the level set by the firmware.
	// Charge rate is controlled by the Iabc current into the OTA, which is
	// also contrlled by the firmware (see contour_cv_changed()).

	static_assert(Which == FILTER_CONTOUR || Which == LOUDNESS_CONTOUR);
	constexpr const char *CONTOUR_NAME = (Which == FILTER_CONTOUR) ? "Filter" : "Loudness";
	constexpr int LEVEL_CV_INDEX =
		(Which == FILTER_CONTOUR) ? int(CV::FILTER_CONTOUR_LEVEL) : int(CV::LOUDNESS_CONTOUR_LEVEL);

	// All componets are on board 2. All resistors have 1% tolerance.
	//                           Filter contour          Loudness contour
	constexpr float R196 = RES_K(18.2);  // R183
	constexpr float R197 = RES_R(100);   // R184
	constexpr float R195 = RES_K(20);    // R187
	constexpr float R194 = RES_R(100);   // R182

	// Voltage dividers at the OTA's + and - inputs.
	constexpr float OTA_DIVIDER_PLUS = RES_VOLTAGE_DIVIDER(R196, R197);
	constexpr float OTA_DIVIDER_MINUS = RES_VOLTAGE_DIVIDER(R195, R194);

	if (m_contour_cc[Which] <= 0)
	{
		// The netlist solver might transiently send negative values.
		LOG("%s EG received a non-positive control current. Skipping update.\n", CONTOUR_NAME);
		return;
	}

	// Ideal OTA transconductance at room temparature.
	const float g = 19.2F * m_contour_cc[Which];
	// Note the sligh difference in the calculations below, compared to the video
	// linked above, due to the resistive dividers at the two OTA inputs not
	// being identical.
	const float effective_r = 1.0F / (g * OTA_DIVIDER_MINUS);
	m_contour[Which]->set_r(effective_r);

	const float level_cv = m_cv[LEVEL_CV_INDEX];  // 0V - 10V.
	const float target_v = level_cv * OTA_DIVIDER_PLUS / OTA_DIVIDER_MINUS;  // 0V - ~10.98V.
	m_contour[Which]->set_target_v(target_v);

	LOGMASKED(LOG_CONTOUR, "%s EG update - Level CV: %f, target_v: %f, R: %f, tau: %f\n",
			  CONTOUR_NAME, level_cv, target_v, effective_r, effective_r * CONTOUR_C);
}

NETDEV_ANALOG_CALLBACK_MEMBER(source_state::lfo_cv_changed)
{
	// The calculation of the LFO control current is very similar to that of the
	// EGs. See contour_cv_changed().
	m_lfo_cc = (data - CA3080_VABC) / RES_K(10);  // R227
	machine().scheduler().synchronize(timer_expired_delegate(FUNC(source_state::update_lfo_timer), this));
	LOGMASKED(LOG_LFO, "LFO CC: %f uA, rate CV: %f, range trimmer: %d\n",
			  m_lfo_cc * 1e6F, m_cv[int(CV::MOD_RATE)], m_lfo_range->read());
}

TIMER_CALLBACK_MEMBER(source_state::update_lfo_timer)
{
	// The LFO ("MOD OSC" in the schematic) is a triangle core oscillator based
	// on a CA3080 OTA (U49). The OTA's Iabc is determined by a voltage-to-exponential-current
	// converter, whose voltage is set by the firmware.
	// The OTA is configured to (dis)charges the capacitor (C58) with a constant
	// current, resulting in a triangle wave (-/+ ~1.5V). The oscillator also
	// produces a square wave (-/+ ~14V) as part of its operation.

	// All components on board 2.
	constexpr float R219 = RES_K(100);
	constexpr float R220 = RES_K(12);
	constexpr float C58 = CAP_U(0.33);

	// Approximate max magnitude of opamp output, according to schematic (supply voltage is 15V).
	constexpr float V_PEAK_SQUARE = 14;
	constexpr float V_PEAK_TRIANGLE = V_PEAK_SQUARE * RES_VOLTAGE_DIVIDER(R219, R220);  // ~1.5V

	// The differential input at the OTA will be +/- V_PEAK_TRIANGLE. This is well
	// beyond the "linear" range (-/+ ~10-20mV), so the output current will be
	// saturated to (almost) +/- Iabc (m_lfo_cc).
	const float i_out = m_lfo_cc;

	// Time it takes to charge the capacitor from -V_PEAK_TRIANGLE to +V_PEAK_TRIANGLE
	// with a constant current. This is the half-period of the LFO, which is what
	// we need for our timer.
	const float t_half = 2 * V_PEAK_TRIANGLE * C58 / i_out;

	// Continue from the current position in the cycle.
	const double t_remaining = t_half * m_lfo_timer->remaining().as_double() / m_lfo_timer->period().as_double();

	if (i_out > 0)
		m_lfo_timer->adjust(attotime::from_double(t_remaining), 0, attotime::from_double(t_half));
	else
		m_lfo_timer->reset();

	LOGMASKED(LOG_LFO, "LFO frequency updated - Icharge: %f uA, t_remaining: %f, t_half: %f, f: %f\n",
			  i_out * 1e6F, t_remaining, t_half, 1.0F / (2.0F * t_half));
}

TIMER_DEVICE_CALLBACK_MEMBER(source_state::lfo_timer_tick)
{
	m_lfo_state = !m_lfo_state;
	m_lfo_rate_led = !m_lfo_state;  // LED (LED 3, board 5) is active low.
	LOGMASKED(LOG_LFO_TIMER, "LFO Timer ticked: %d\n", m_lfo_state);
}

void source_state::memory_map(address_map &map)
{
	// Address decoding done through U26, 74LS138, E1=E2=0, E3=1,
	// A0-A2 = Z80 A13-A15.
	// Z80 A12 is not connected.
	// The signal names below (e.g. "ROM /EN", "RAM /EN") match those in the
	// schematics.

	// ROM /EN: 0x0000-0x1fff.
	// 1 x 2532 (4K, 8bit) ROM, U23.
	map(0x0000, 0x0fff).mirror(0x1000).rom();
	// 2 x 74LS378. Z80 and latch data lines are not connected in order.
	map(0x0000, 0x0000).mirror(0x1fff).w(FUNC(source_state::edit_latch_w));

	// RAM /EN: 0x2000-0x3fff.
	// 2 x 6514 (1K, 4bit) NVRAMs. U27: D0-D3, U28: D4-D7.
	// Z80 A0-A1 -> RAM A0-A1. Z80 A2-A8 -> RAM A3-A9. Z80 A9 -> RAM A2.
	map(0x2000, 0x23ff).mirror(0x1c00).ram().share(NVRAM_TAG);

	// OUTPUT /EN: 0x4000-0x5fff.
	// 2 output latches (74LS378, U11 and U12) enabled by 74LS155 (U13B),
	// with Z80 A3-A4 as inputs to A0-A1. O2 and O3 are not connected, so
	// A4=1 does not enable anything.
	map(0x4000, 0x4000).mirror(0x1fe7).w(FUNC(source_state::output_latch_a_w));
	map(0x4008, 0x4008).mirror(0x1fe7).w(FUNC(source_state::output_latch_b_w));

	// KYBD /EN: 0x6000-0x7fff.
	// 74LS367, U32
	map(0x6000, 0x6000).mirror(0x1fff).r(FUNC(source_state::keyboard_r));
	// 74LS74, U22A. D <- Z80 D4.
	map(0x6000, 0x6000).mirror(0x1fff).w(FUNC(source_state::cassette_w));

	// FRONT PANEL /EN1: 0x8000-0x9fff.
	// CD4502, U8.
	map(0x8000, 0x8000).mirror(0x1fff).r(FUNC(source_state::buttons_a_r));
	// 74LS378, U5.
	map(0x8000, 0x8000).mirror(0x1fff).w(FUNC(source_state::buttons_latch_w));

	// FRONT PANEL /EN2: 0xa000-0xbfff.
	// CD4502, U9
	map(0xa000, 0xa000).mirror(0x1fff).r(FUNC(source_state::buttons_b_r));

	// DISPLAY /EN: 0xc000-0xdfff.
	// 74LS378, U1.
	map(0xc000, 0xc000).mirror(0x1fff).w(FUNC(source_state::program_latch_w));

	// CNTRL /EN: 0xe000-0xffff. (typo in schematic: 0xefff-0xffff).
	map(0xe000, 0xe000).mirror(0x1fff).r(FUNC(source_state::encoder_r));
}

void source_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x1f).mirror(0xe0).w(FUNC(source_state::cv_w));
}

void source_state::machine_start()
{
	m_octave_led.resolve();
	m_lfo_rate_led.resolve();
	m_program_display.resolve();
	m_edit_display.resolve();
	m_edit_led.resolve();
	m_kb_track.resolve();
	m_osc_waveform.resolve();
	m_sync.resolve();
	m_lfo_to_filter.resolve();
	m_lfo_to_osc.resolve();
	m_lfo_shape.resolve();
	m_trigger_out.resolve();

	save_item(NAME(m_octave_hi));
	save_item(NAME(m_button_row_latch));
	save_item(NAME(m_encoder_incr));
	save_item(NAME(m_lfo_state));
	save_item(NAME(m_lfo_cc));
	save_item(NAME(m_contour_cc));
	save_item(NAME(m_cv));
}

void source_state::machine_reset()
{
	update_octave_leds();
	update_contour<FILTER_CONTOUR>(0);
	update_contour<LOUDNESS_CONTOUR>(0);
	update_lfo_timer(0);

	// If an input port has its default value at startup, its write callback will
	// not be invoked. Ensure the netlist inputs are initialized even in that
	// scenario.
	subdevice<netlist_mame_analog_input_device>("source_nl:cntr_range_0")->write(m_contour_range[0]->read());
	subdevice<netlist_mame_analog_input_device>("source_nl:cntr_range_1")->write(m_contour_range[1]->read());
	subdevice<netlist_mame_analog_input_device>("source_nl:lfo_range")->write(m_lfo_range->read());
}

void source_state::source(machine_config &config)
{
	// /M1, /RFSH not Connected.
	// /HALT, /NMI pulled up to 5V, with no other connection.
	Z80(config, m_maincpu, 4_MHz_XTAL / 2);  // Divided by 2 through U22B.
	m_maincpu->set_addrmap(AS_PROGRAM, &source_state::memory_map);
	m_maincpu->set_addrmap(AS_IO, &source_state::io_map);

	NVRAM(config, NVRAM_TAG, nvram_device::DEFAULT_ALL_0);  // 2x6514: U27, U28.

	VA_RC_EG(config, m_contour[FILTER_CONTOUR]).set_c(CONTOUR_C);  // C57 (Board 2).
	VA_RC_EG(config, m_contour[LOUDNESS_CONTOUR]).set_c(CONTOUR_C);  // C56 (Board 2).
	TIMER(config, m_lfo_timer).configure_generic(FUNC(source_state::lfo_timer_tick));

	config.set_default_layout(layout_moog_source);


	NETLIST_CPU(config, "source_nl", netlist::config::DEFAULT_CLOCK()).set_source(NETLIST_NAME(moogsource));

	NETLIST_ANALOG_INPUT(config, "source_nl:cntr_range_0", "R201.DIAL");
	NETLIST_ANALOG_INPUT(config, m_contour_rate[FILTER_CONTOUR], "FLT_CNTR_RATE.IN");
	NETLIST_ANALOG_OUTPUT(config, "source_nl:cntr_cv_0")
		.set_params("FLT_CNTR_CV", FUNC(source_state::contour_cv_changed<FILTER_CONTOUR>));

	NETLIST_ANALOG_INPUT(config, "source_nl:cntr_range_1", "R179.DIAL");
	NETLIST_ANALOG_INPUT(config,  m_contour_rate[LOUDNESS_CONTOUR], "LOUD_CNTR_RATE.IN");
	NETLIST_ANALOG_OUTPUT(config, "source_nl:cntr_cv_1")
		.set_params("LOUD_CNTR_CV", FUNC(source_state::contour_cv_changed<LOUDNESS_CONTOUR>));

	NETLIST_ANALOG_INPUT(config, "source_nl:lfo_range", "R223.DIAL");
	NETLIST_ANALOG_INPUT(config, m_lfo_rate, "MOD_RATE.IN");
	NETLIST_ANALOG_OUTPUT(config, "source_nl:lfo_cv")
		.set_params("MOD_CV", FUNC(source_state::lfo_cv_changed));
}

DECLARE_INPUT_CHANGED_MEMBER(source_state::octave_button_pressed)
{
	// Inverters U2B and U2C (Board 3) are configured as an SR flip-flop, with
	// SW1 and SW2 (Board 5) as Reset and Set respectively.

	// Inputs are active low.
	const u8 input = m_octave_io->read();
	const bool octave_0 = (input & 0x01) == 0;  // "0", SW1 (Board 5).
	const bool octave_p1 = (input & 0x02) == 0;  // "+1", SW2 (Board 5).
	if (!octave_0 && octave_p1)
	{
		m_octave_hi = true;
	}
	else if (octave_0 && !octave_p1)
	{
		m_octave_hi = false;
	}
	else if (octave_0 && octave_p1)
	{
		// The selected octave is undefined in this case, so it is not updated.
		// An octave will be selected when one of the two buttons is released.
	}
	else
	{
		// No buttons pressed. No change in selected octave.
	}
	update_octave_leds();
}

DECLARE_INPUT_CHANGED_MEMBER(source_state::encoder_moved)
{
	constexpr int WRAP_BUFFER = 10;
	const bool overflowed = newval <= WRAP_BUFFER &&
							oldval >= 240 - WRAP_BUFFER;
	const bool underflowed = newval >= 240 - WRAP_BUFFER &&
							 oldval <= WRAP_BUFFER;
	m_encoder_incr = ((newval > oldval) || overflowed) && !underflowed;
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
	LOGMASKED(LOG_ENCODER, "Encoder changed: %d %d\n", newval, m_encoder_incr);
}

INPUT_PORTS_START(source)
	PORT_START("button_group_a_0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Memory STORE") PORT_CODE(KEYCODE_S)  // r2p6
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Program 13")  // r2p4
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Program 12")  // r2p5
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Program 16")  // r2p1
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Program 15")  // r2p2
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Program 14")  // r2p3

	PORT_START("button_group_b_0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Level 2") // r1p5
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Level 1")  //r1p6
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Filter Contour Decay")  //r1p4
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Filter Contour Amount")  //r1p1
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Filter Contour Release")  //r1p2
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Filter Contour Sustain")  // r1p3

	PORT_START("button_group_a_1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) //NC
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Program 3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Program 2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Program 6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Program 5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Program 4") PORT_CODE(KEYCODE_4)

	PORT_START("button_group_b_1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Filter Attack")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Mixer: OSC 2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC 2 Shape: Pulse")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Filter KB Track: OFF")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Filter Emphasis")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Filter Cutoff")

	PORT_START("button_group_a_2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) // NC
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Program 10") PORT_CODE(KEYCODE_0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Program 11")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Program 7") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Program 8") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Program 9") PORT_CODE(KEYCODE_9)

	PORT_START("button_group_b_2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Loudness Decay")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Loudness Attack")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("MOD Rate")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) // NC / PROGRAM 1
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Loudness Release")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Loudness Sustain")

	PORT_START("button_group_a_3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) // NC
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("MOD To Filter: OFF")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Program 1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC 2 Footage: 32'")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) // NC / PROGRAM 1
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("MOD To Filter: ON")

	PORT_START("button_group_b_3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Mixer: NOISE")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC 2 Interval")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC 2 Shape: Sawtooth")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC 1 Shape: Pulse")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Filter KB Track: 1/2")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Filter KB Track: FULL")

	PORT_START("button_group_a_4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) // NC
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("MOD to Osc: OFF")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Memory HOLD") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("KB Glide")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("MOD Shape: Triangle")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("MOD to Osc: ON")

	PORT_START("button_group_b_4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC 2 Shape: Triange")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC 2 Footage: 16'")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SYNC: ON")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC 1 Footage: 32'")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC 1 Footage: 16'")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Mixer: OSC 1")

	PORT_START("button_group_a_5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) // NC
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Trigger MULTI")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Trigger SINGLE")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) // NC / PROGRAM 1
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("MOD Shape: Square")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) // NC / PROGRAM 1

	PORT_START("button_group_b_5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC 2 Footage: 8'")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) // NC / PROGRAM 1
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SYNC: OFF")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC 1 Footage: 8'")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC 1 Shape: Triange")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OSC 1 Shape: Sawtooth")

	PORT_START("octave_buttons")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Octave 0")  // SW1 (Board 5).
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(source_state::octave_button_pressed), 0x01)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Octave +1")  // SW2 (Board 5).
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(source_state::octave_button_pressed), 0x02)

	PORT_START("incremental_controller")
	PORT_BIT(0xff, 0x00, IPT_POSITIONAL) PORT_POSITIONS(240) PORT_WRAPS
		PORT_SENSITIVITY(25) PORT_KEYDELTA(3)
		PORT_CODE_DEC(KEYCODE_LEFT) PORT_CODE_INC(KEYCODE_RIGHT) PORT_FULL_TURN_COUNT(240)
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(source_state::encoder_moved), 1)

	PORT_START("keyboard_oct_1")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C2
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_CS2
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_D2
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_DS2
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_E2
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_F2
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_FS2
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_G2
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_GS2
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_A2
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_AS2
	PORT_BIT(0x800, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_B2

	PORT_START("keyboard_oct_2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C3
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_CS3
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_D3
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_DS3
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_E3
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_F3
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_FS3
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_G3
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_GS3
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_A3
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_AS3
	PORT_BIT(0x800, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_B3

	PORT_START("keyboard_oct_3")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C4
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_CS4
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_D4
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_DS4
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_E4
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_F4
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_FS4
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_G4
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_GS4
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_A4
	PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_AS4
	PORT_BIT(0x800, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_B4

	PORT_START("keyboard_oct_4")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_OTHER) PORT_GM_C5

	PORT_START("trigger_in")  // External trigger input (see keyboard_r()).
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("S TRIG IN") PORT_CODE(KEYCODE_T)

	PORT_START("contour_range_0")  // R201 (Board 2), 100K trimpot.
	PORT_ADJUSTER(50, "FILTER_CONTOUR_RANGE") NETLIST_ANALOG_PORT_CHANGED("source_nl", "cntr_range_0")

	PORT_START("contour_range_1")  // R179 (Board 2), 100K trimpot.
	PORT_ADJUSTER(50, "LOUDNESS_CONTOUR_RANGE") NETLIST_ANALOG_PORT_CHANGED("source_nl", "cntr_range_1")

	PORT_START("lfo_range")  // R223 (Board 2), 100K trimpot.
	// A default of 0 takes us the closest to the advertised highest LFO frequency of 30 Hz.
	PORT_ADJUSTER(0, "LFO RANGE") NETLIST_ANALOG_PORT_CHANGED("source_nl", "lfo_range")
INPUT_PORTS_END

// It seems like the Source was launched with firmware Revision 2.2.
// There was also a Revision 3.2, and the last official firmware release was
// Revision 3.3.
ROM_START(moogsource)
	ROM_REGION(0x1000, MAINCPU_TAG, 0)
	ROM_DEFAULT_BIOS("r3.3")

	ROM_SYSTEM_BIOS(0, "r3.3", "Rev 3.3")
	ROMX_LOAD("3p3.u23", 0x000000, 0x001000, CRC(4211331f) SHA1(8767ef6b1cbb032a89a78bdb77bb7dbc1c187974), ROM_BIOS(0))
ROM_END

}  // anonymous namespace.

SYST(1981, moogsource, 0, 0, source, source, source_state, empty_init, "Moog Music", "Moog Source", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND)



spc1000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************

Samsung SPC-1000 driver by Miodrag Milanovic
This was the first computer from Samsung. It featured a cassette player
embedded in the righthand side of the keyboard.

2009-05-10 Preliminary driver.
2014-02-16 Added cassette, many games are playable

ToDo:
- Find out if any of the unconnected parts of 6000,4000,4001 are used


Hardware details of the fdc: Intelligent device, Z80 CPU,
XTAL(8'000'000), PPI 8255, FDC uPD765C, 2 RAM chips, 28 other
small ics.

2014-10-11: Added code for the floppy disk [Meeso Kim]

2015-06-19: Added code for the centronics printer port

2016-01-14: Cassette tape motor fixed to work properly and ROM file changed for CP/M disk loading

****************************************************************************/
/*
 * SAMSUNG SPC-1000 Series (info from zannylim)
 *
 * YEAR MODEL           MainVideo       PRT     FDD
 * ---- --------------  ------  -----   ------  -------
 * 1982 SPC-1000        S68047  RGB     buffer  -
 * 1983 SPC-1000        S68047  RF-TV   buffer  -
 * 1983 SPC-1100        S68047  RF-TV   -       -
 * 1983 SPC-1000/1100   MC6847  RF-TV   direct  support
 * 1985 SPC-1000A       MC6847  RF-TV   direct  support
 *
 * 2nd Video Display Processor type 1 : VDP UNIT (TMS9918 + 4KB VRAM) by staticsoft
 * 2nd Video Display Processor type 2 : SOFT BOX (TMS9918 + 4KB VRAM with BIOS) by sammi computer
 *
 * Intelligence FDD : SD-725(2FDD, RS232C), SD-720(1FDD), SD-725A(2FDD), SD-725B(Desktop 2FDD, RS232C) - EPSON TF20 F100
 * External FDD with Expansion slot : KWE-1000 by kyungwoo
 *
 * Network device : ISAM-1000 by samsung
 *
 *              +---------PRT------RGB----TVRF--+   SPC-1000
 *              +                               +
 *              +   ROM0                        +   CPU : Z80A (4MHz)
 *              +   ROM1                        +   RAM : 64KB
 *      +-------+   ROM2  AY-3-8910             +   VRAM : 6KB
 *      +                             SPC-1000  +   VDG : AMI S68047 with TTL RGB output
 *     IPL                                      +   PSG : AY-3-8910
 *     RESET                          S68047    +
 *      +           Z80A                        +   Include Internal Data-recorder
 *      +    ROM3                               +
 *      +                                       +   ROM : 32KB (8KB x 4)
 *      +---------------------------------------+
 *
 *              +---------PRT----VIDEO----TVRF--+   SPC-1000
 *              +                               +
 *              +   ROM0                 LM1889 +   Support RF TV Support, but Removed RGB output
 *              +   ROM1                        +
 *      +-------+   ROM2  AY-3-8910   SPC-1000  +
 *      +                                 1100  +
 *     IPL                                      +
 *     RESET                          S68047    +
 *      +           Z80A                        +
 *      +    ROM3                               +
 *      +                                       +
 *      +---------------------------------------+
 *
 *              +----------------VIDEO----TVRF--+   SPC-1100
 *              +                               +
 *              +   ROM0                 LM1889 +   Removed Printer port
 *              +   ROM1       LM386(5V)        +
 *      +-------+   ROM2  AY-3-8910   SPC-1000  +
 *      +                                 1100  +
 *     IPL                                      +
 *     RESET                          S68047    +
 *      +           Z80A                        +
 *      +    ROM3                               +
 *      +                                       +
 *      +---------------------------------------+
 *
 *              +---------PRT----VIDEO----TVRF--+   SPC-1000, SPC-1100
 *              +                               +
 *              +   ROM0                 MC1372 +   New Video Display Generator : MC6847
 *              +   ROM1       LM386(5V)        +
 *      +-------+   ROM2  AY-3-8910             +
 *      +           ROM3         SPC-1000/1100  +
 *     IPL                                      +
 *     RESET                          MC6847    +
 *      +           Z80A                        +
 *      +                                       +
 *      +                                       +
 *      +---------------------------------------+
 *
 *              +---------PRT----VIDEO----TVRF--+   SPC-1000, SPC-1100
 *              +                               +
 *              +   ROM0              S4 MC1372 +   REV PCB No.839291
 *              +   ROM1       LM386(5V)        +
 *      +-------+   ROM2  AY-3-8910             +   Add composite color on/off switch
 *      +           ROM3         SPC-1000/1100  +
 *     IPL                                      +
 *     RESET                          MC6847    +
 *      +           Z80A                        +
 *      +                                       +
 *      +                                       +
 *      +---------------------------------------+
 *
 *              +---------PRT----VIDEO----TVRF--+   SPC-1000A
 *              +                               +
 *              +   ROM0              S4 MC1372 +   Internal Data-recorder with Cassette Audio Player
 *              +   ROM1       LM386(12V)       +   Add FDD auto detect
 *      +-------+   ROM2  AY-3-8910             +   Remove IPL button
 *      + SPC-1000A ROM3                        +   Change DRAM refresh circuit
 *     IPL                                      +   Use 64K DRAM made by Samsung
 *      +                             MC6847    +   Use TTL IC made by Goldstar
 *      +           Z80A                        +
 *      +                                       +
 *      +                                       +
 *      +---------------------------------------+
 *
 */

#include "emu.h"

#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "machine/ram.h"
#include "sound/ay8910.h"
#include "video/mc6847.h"

#include "bus/centronics/ctronics.h"
#include "bus/spc1000/exp.h"
#include "bus/spc1000/fdd.h"
#include "bus/spc1000/vdp.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "formats/spc1000_cas.h"


namespace {

class spc1000_state : public driver_device
{
public:
	spc1000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_vdg(*this, "mc6847")
		, m_cass(*this, "cassette")
		, m_ram(*this, RAM_TAG)
		, m_p_videoram(*this, "videoram")
		, m_io_kb(*this, "LINE.%u", 0U)
		, m_io_joy(*this, "JOY")
		, m_centronics(*this, "centronics")
	{}

	void spc1000(machine_config &config);

private:
	void iplk_w(uint8_t data);
	uint8_t iplk_r();
	void irq_w(int state);
	void gmode_w(uint8_t data);
	uint8_t gmode_r();
	uint8_t porta_r();
	void centronics_busy_w(int state) { m_centronics_busy = state; }
	uint8_t mc6847_videoram_r(offs_t offset);
	void cass_w(uint8_t data);
	uint8_t keyboard_r(offs_t offset);
	MC6847_GET_CHARROM_MEMBER(get_char_rom)
	{
		return m_p_videoram[0x1000 + (ch & 0x7f) * 16 + line];
	}

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	uint8_t m_IPLK = 0U;
	uint8_t m_GMODE = 0U;
	uint16_t m_page = 0U;
	std::unique_ptr<uint8_t[]> m_work_ram;
	attotime m_time;
	bool m_centronics_busy = false;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	required_device<z80_device> m_maincpu;
	required_device<mc6847_base_device> m_vdg;
	required_device<cassette_image_device> m_cass;
	required_device<ram_device> m_ram;
	required_shared_ptr<uint8_t> m_p_videoram;
	required_ioport_array<10> m_io_kb;
	required_ioport m_io_joy;
	required_device<centronics_device> m_centronics;
};

void spc1000_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).bankr("bank1").bankw("bank2");
	map(0x8000, 0xffff).bankr("bank3").bankw("bank4");
}

void spc1000_state::iplk_w(uint8_t data)
{
	m_IPLK = m_IPLK ? 0 : 1;
	membank("bank1")->set_entry(m_IPLK);
	membank("bank3")->set_entry(m_IPLK);
}

uint8_t spc1000_state::iplk_r()
{
	m_IPLK = m_IPLK ? 0 : 1;
	membank("bank1")->set_entry(m_IPLK);
	membank("bank3")->set_entry(m_IPLK);

	return 0;
}

void spc1000_state::cass_w(uint8_t data)
{
	attotime time = machine().scheduler().time();
	m_cass->output(BIT(data, 0) ? -1.0 : 1.0);
	if (BIT(data, 1) || (((time - m_time).as_attoseconds()/ATTOSECONDS_PER_MILLISECOND) < 1000))
	{
		m_cass->change_state(CASSETTE_MOTOR_ENABLED, CASSETTE_MASK_MOTOR);
		m_time = time;
	}
	else
		m_cass->change_state(CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);

	m_centronics->write_strobe(BIT(data, 2) ? true : false);
}

void spc1000_state::gmode_w(uint8_t data)
{
	m_GMODE = data;

	// m_GMODE layout: CSS|NA|PS2|PS1|~A/G|GM0|GM1|NA
	//  [PS2,PS1] is used to set screen 0/1 pages
	m_vdg->gm1_w(BIT(data, 1));
	m_vdg->gm0_w(BIT(data, 2));
	m_vdg->ag_w(BIT(data, 3));
	m_vdg->css_w(BIT(data, 7));
	m_page = ((BIT(data, 5) << 1) | BIT(data, 4)) * 0x200;
}

uint8_t spc1000_state::gmode_r()
{
	return m_GMODE;
}

uint8_t spc1000_state::keyboard_r(offs_t offset)
{
	// most games just read kb in $8000-$8009 but a few of them
	// (e.g. Toiler Adventure II and Vela) use mirrored addr instead
	offset &= 0xf;

	if (offset <= 9)
		return m_io_kb[offset]->read();
	else
		return 0xff;
}


void spc1000_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).ram().share("videoram");
	map(0x2000, 0x3fff).rw(FUNC(spc1000_state::gmode_r), FUNC(spc1000_state::gmode_w));
	map(0x4000, 0x4000).w("ay8910", FUNC(ay8910_device::address_w));
	map(0x4001, 0x4001).rw("ay8910", FUNC(ay8910_device::data_r), FUNC(ay8910_device::data_w));
	map(0x6000, 0x6000).w(FUNC(spc1000_state::cass_w));
	map(0x8000, 0x9fff).r(FUNC(spc1000_state::keyboard_r));
	map(0xa000, 0xa000).rw(FUNC(spc1000_state::iplk_r), FUNC(spc1000_state::iplk_w));
	map(0xc000, 0xdfff).rw("ext1", FUNC(spc1000_exp_device::read), FUNC(spc1000_exp_device::write));
}

/* Input ports */
static INPUT_PORTS_START( spc1000 )
	PORT_START("LINE.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_RSHIFT) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_RCONTROL) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_PAUSE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Graph") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^ ~") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('^') PORT_CHAR('~') PORT_CHAR(0x1e)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Home") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR(0x03)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR(0x01)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') PORT_CHAR(0x16)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 !") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')

	PORT_START("LINE.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Caps") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(0x1a)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("] }") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}') PORT_CHAR(0x1d)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR(0x16)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR(0x13)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR(0x17)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 \"") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')

	PORT_START("LINE.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_CHAR(0x12)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(0x1b)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[ {") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{') PORT_CHAR(0x1b)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR(0x02)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR(0x04)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(0x05)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 #") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')

	PORT_START("LINE.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\ |") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|') PORT_CHAR(0x1c) // shows symbols
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(0x0e)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR(0x06)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR(0x12)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 $") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')

	PORT_START("LINE.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') PORT_CHAR(0x0d)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR(0x07)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(0x14)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 %") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')

	PORT_START("LINE.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@ `") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(0x18)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') PORT_CHAR(0x08)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR(0x19)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 &") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')

	PORT_START("LINE.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR(0x10)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') PORT_CHAR(0x0a)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(0x15)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 '") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("LINE.8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(": *") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR(0x0b)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') PORT_CHAR(0x09)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 (") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')

	PORT_START("LINE.9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_START)    PORT_NAME("IPL") PORT_CODE(KEYCODE_END)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("- =") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("; +") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR(0x0c)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(0x0e)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 )") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')

	PORT_START("JOY")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED) // Button 2?
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED) // Cassette related
INPUT_PORTS_END


void spc1000_state::machine_start()
{
	m_work_ram = make_unique_clear<uint8_t[]>(0x10000);

	uint8_t *mem = memregion("maincpu")->base();
	uint8_t *ram = m_ram->pointer();

	// configure and intialize banks 1 & 3 (read banks)
	membank("bank1")->configure_entry(0, ram);
	membank("bank1")->configure_entry(1, mem);
	membank("bank3")->configure_entry(0, ram + 0x8000);
	membank("bank3")->configure_entry(1, mem);

	// intialize banks 2 & 4 (write banks)
	membank("bank2")->set_base(ram);
	membank("bank4")->set_base(ram + 0x8000);

	m_time = machine().scheduler().time();

	save_item(NAME(m_IPLK));
	save_item(NAME(m_GMODE));
	save_item(NAME(m_page));
	save_pointer(NAME(m_work_ram), 0x10000);
	save_item(NAME(m_time));
	save_item(NAME(m_centronics_busy));
}

void spc1000_state::machine_reset()
{
	m_IPLK = 1;
	membank("bank1")->set_entry(1);
	membank("bank3")->set_entry(1);
}

uint8_t spc1000_state::mc6847_videoram_r(offs_t offset)
{
	if (offset == ~0)
		return 0xff;

	// m_GMODE layout: CSS|NA|PS2|PS1|~A/G|GM0|GM1|NA
	if (!BIT(m_GMODE, 3))
	{   // text mode (~A/G set to A)
		uint8_t data = m_p_videoram[offset + m_page + 0x800];
		m_vdg->inv_w(BIT(data, 0));
		m_vdg->css_w(BIT(data, 1));
		m_vdg->as_w (BIT(data, 2));
		m_vdg->intext_w(BIT(data, 3));
		return m_p_videoram[offset + m_page];
	}
	else
	{    // graphics mode: uses full 6KB of VRAM
		return m_p_videoram[offset];
	}
}

uint8_t spc1000_state::porta_r()
{
	uint8_t data = 0x3f;
	data |= (m_cass->input() > 0.0038) ? 0x80 : 0;
	data |= ((m_cass->get_state() & CASSETTE_MASK_UISTATE) == CASSETTE_STOPPED || ((m_cass->get_state() & CASSETTE_MASK_MOTOR) == CASSETTE_MOTOR_DISABLED)) ? 0x40 : 0;
	data &= ~(m_io_joy->read() & 0x3f);
	data &= ~((m_centronics_busy == 0)<< 5);
	return data;
}

// irq is inverted in emulation, so we need this trampoline
void spc1000_state::irq_w(int state)
{
	m_maincpu->set_input_line(0, state ? CLEAR_LINE : HOLD_LINE);
}

//-------------------------------------------------
//  address maps
//-------------------------------------------------

void spc1000_exp(device_slot_interface &device)
{
	device.option_add("fdd", SPC1000_FDD_EXP);
	device.option_add("vdp", SPC1000_VDP_EXP);
}

void spc1000_state::spc1000(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &spc1000_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &spc1000_state::io_map);

	/* video hardware */
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	MC6847(config, m_vdg, XTAL(3'579'545));
	m_vdg->set_screen("screen");
	m_vdg->fsync_wr_callback().set(FUNC(spc1000_state::irq_w));
	m_vdg->input_callback().set(FUNC(spc1000_state::mc6847_videoram_r));
	m_vdg->set_get_char_rom(FUNC(spc1000_state::get_char_rom));
	m_vdg->set_get_fixed_mode(mc6847_device::MODE_GM2);
	// other lines not connected

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	ay8910_device &ay8910(AY8910(config, "ay8910", XTAL(4'000'000) / 1));
	ay8910.port_a_read_callback().set(FUNC(spc1000_state::porta_r));
	ay8910.port_b_write_callback().set("cent_data_out", FUNC(output_latch_device::write));
	ay8910.add_route(ALL_OUTPUTS, "mono", 1.00);

	SPC1000_EXP_SLOT(config, "ext1", spc1000_exp);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(spc1000_state::centronics_busy_w));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	INPUT_BUFFER(config, "cent_status_in");

	CASSETTE(config, m_cass);
	m_cass->set_formats(spc1000_cassette_formats);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_DISABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cass->set_interface("spc1000_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("spc1000_cass");

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("64K");
}

/* ROM definition */
ROM_START( spc1000 )
	ROM_REGION(0x8000, "maincpu", ROMREGION_ERASEFF)
	//ROM_LOAD("spcall.rom", 0x0000, 0x8000, CRC(2fbb6eca) SHA1(cc9a076b0f00d54b2aec31f1f558b10f43ef61c8))  // bad?
	ROM_LOAD("spcall.rom", 0x0000, 0x8000, CRC(240426be) SHA1(8eb32e147c17a6d0f947b8bb3c6844750a7b64a8))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY    FULLNAME    FLAGS
COMP( 1982, spc1000, 0,      0,      spc1000, spc1000, spc1000_state, empty_init, "Samsung", "SPC-1000", MACHINE_SUPPORTS_SAVE )



spc1500.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miso Kim
/***************************************************************************

Samsung SPC-1500 driver by Miso Kim

  2015-12-16 preliminary driver initialized
  2015-12-18 cassette tape supported
  2015-12-26 80/40 column mode supported
  2015-12-28 double access mode supported for I/O
  2016-01-02 Korean character input method and display enabled
  2016-01-03 user defined char (PCG, Programmable Character Generator) support
  2016-01-05 detection of color palette initialization
  2016-01-06 80x16 mode graphic mode support
  2016-01-10 double character support
  2016-01-12 PCG addressing improved
  2016-01-13 Cassette tape motor improved

TODO:
  - Verify PCG ram read for Korean character (English character is fine)
  - Support floppy disk drive with SD-1500A controller card

****************************************************************************/

/*
 * SAMSUNG SPC-1500 Series
 *
 * Brief history
 * ---------------------------------------------------------
 * First release on 02-26-1987
 * Press release on 03-14-1987
 * Market price 365,000 won ($430) on 04-01-1987

 Hardware Specification

 1) SPC-1500
 RAM 122KB
  - Main Memory: 64KB
  - Video RAM: 10KB (text 2KB, character attributes 2KB, custom letter: 6KB)
  - Graphics RAM: 48KB (16KB on three sides by assigning each of RGB)
 ROM 96KB
  - BIOS: 32KB (including IPL and a part of BASIC)
  - BASIC ROM: 32KB (Hangul Samsung BASIC - Korean character display)
  - English font: 8KB (sizes 8x8 and 8x16 size of each character in ROM)
  - Hangul fonts: 24KB (head 8KB, middle 8KB, last 8KB)

 Peripherals
  - Built-in cassette deck
  - 1 composite video output to connect a monitor
  - 1 TTL output for RGB monitor connection
  - 1 RF output for TV connection
  - 1 printer port
  - 1 joystick port

 Expansion Slots
  - Built-in 50-pin 3 slot
  - One is using the memory expansion card by default

 Two external power connector for FDD connection
  - DIP switch settings for the screen
  - Volume control

 2) SPC-1500A
  July 1987 Release
  RF modulator only remove the product from an existing model

 3) SPC-1500V
  This product can not confirm the release date because of PCB level modification.
  It equipped SPC-1500V VLSI chip embedded products and removed a lot of TTLs and the memory expansion card.
  - IOCS ROM Version: 1.6
  - Two internal 50-pin expansion slots

 Firmware

  IOCS ROM
    The various versions with 32KB of capacity existed to date has confirmed the final version number 1.8
  - Version 1.3:
  - Version 1.4:
  - Version 1.5:
  - Version 1.6:
  - Version 1.8: supports a variety of peripherals such as external hard disk, FM-Sound, RS-232C from Static soft (C)
                 various memu appears on the initial screen.

  BASIC ROM
   Capacity and the final version number of the currently identified 32KB 1.3

  English ROM
   The final version of the verification of the capacity 8KB SS150-1222
   The character set of a 8x8 size, and are stored with the size 8x16 8x16 is a part of the size of the font data are written differently and 8x8.

  Hangul ROM
   8KB each is divided by a consonant and consonant and neutral.
   - Initial (Choseong)  SS151-1223: 8 types of initial character (actual 6 types)
   - Middle (Jungseong) SS152-1224: 2 types of middle character
   - Final  (Jongseong) SS153-1225: 2 types of final character

  Peripherals - Monitor
   - , high-resolution monitor SM, color monitor model was to distinguish it from CD.

  1) MD-1255H (Low resolution monitors MD)
   - 12 inches Composite 15.734KHz / 60Hz
   - N displayed after the model name in the model is non - CRT scanning products
   - Stand adopted: if you put the rest on the bottom that can be placed slightly tilted back.

  2) MD-9052H (Low resolution monitors MD)
   - 9 inches Composite 15.734KHz / 60Hz
   - N from model name means 'anti-glare'
   - All parts except for the appearance and size is the same as the CRT 1255H.

  3) MD-2563 (color monitor SM)
  4) SM-1439A (high-resolution monitor SM)
  5) SM-1422 (high-resolution monitor SM)
   - RGB monitors
  6) SM-1231 (high-resolution monitor SM)
   - High-resolution monochrome monitor
  7) SM-1231A (high-resolution monitor SM)
   - The other part is other than the appearance of the stand is attached to the same as the model SM-1231

  8) CD-1451D (color monitor SM)
   - Composite color monitors
  9) CD-1462X (color monitor SM)
  10)CD-1464W (color monitor SM)
  11)CW-4644

  FDD (floppy disk drive)

  1) SD-1500A
   - 5.25 "floppy drive for 2D composed of external disk drives diskettes
  2) SD-1500B
   - Dual external disk drives
   - The two models are identical except the number of FDD. They need the expansion controller card named by SFC-1500.
   - IBM PC XT compatible FDD can be quipped. SFD-5x0 model is a genuine FDD from Samsung Electronics.

  HDD (Hard Disk Drive)

  1) STH-20
   - External hard disk drive set having a capacity of 20MB SCSI controller and the way
   - The controller had not solved alone but the controller can be used to mount another hard disk products.
   - Release price: 450,000 won ($530).

  Joysticks
   - Joystick was limited to 1 as possible (The PCB was designed by supporting two joysticks.

  1) SJ-1500
   - Release price: 8,000 won ($9.4)
   - SPC-1000A, MSX-compatible

  Printer

  1) SP-510S
   - Bitmap image output method Hangul support
   - Recommended 80 columns dot-matrix printer
  2) SP-570H
   - Recommended 132 columns dot-matrix printer
  3) SP-510L
  4) SP-510T
  5) SP-570B

  Expansion Cards

  1) SFC-1500
   - External FDD capacity of the floppy disk controller 5.25 inches / 320KB can connect up to two.

  2) Multi-controller
   - Floppy disk controllers and hard disk controllers on the same PCB.

  3) ST-PAC
   - FM sound card can play with up to 9 simultaneous sound or 5 simultaneous sound and 5 drum tones at the same time (FM-PAC compatible MSX)
   - Line output and speaker output volume, tone adjustment built-in volume
   - it can be used as a synthesizer by connecting the ST-KEY2 product
   - Release price: 60,000won ($71)

  SPC-1500 VDP card
   - MSX game support
   - Release price: 35,000won ($41) with composite output only
   - Release price: 60,000won ($71) with composite and RGB outputs simultaneously

  VDP UNIT I
   - Composite video output with built-in card expansion card using the same video chip and MSX (static soft)
   - Release price: 40,000won ($47).

  VDP UNIT II
   - Expansion using the same video chip and video card with built-in card MSX with an RGB output (static soft)
   - Release price: 55,000won ($59).

  LAN card (SAMNET-K)
   - It uses serial communication instead of an Ethernet network card has a way with two serial ports.
   - There are two kinds of host card without a DIP switch and the DIP switch is in the client card.
   - It was mainly supplied to the teacher / student in an educational institution.

  Super Pack Card
   - Expansion cards that enable the external expansion slot, etc.

  RS-232C card
   - At least 300bps, an external modem connected to the serial communication card that supports up to 19,200bps
     additionally available communications services using the PSTN network (general switched telephone network)
     and may also be connected to a 9-pin serial mouse.
   - Support for common serial communications functions,
     and if IOCS ROM version 1.8 or higher to connect an external modem to the PC communication card is available.
   - When used in this communication program is super soft static net programs (XMODEM protocol, FS 220-6 compatible
     and supporting Samsung/Sambo combination korean character code, and an 8-bit code completion support Hangul) is used.
   - Release price: 60,000won ($71).

  SS-1 ROM pack unit
   - The VDP unit containing 1 cartridge slot card and ROM pack
   - ROM pack was not compatible with original MSX ROM pack
   - Release price: 49,900won ($58)

  Super Pack
   - External ROM cartridge from Static Soft (C)
   - 1 cartridge slot and 3 expansion slots (up to five expansion slots available)
   - It is available to use the MSX ROM packs without any modification with the static soft VDP card
   - Release price: 60,000won ($71)

  ST-KEY2
   - For synthesizer external keyboard

  * Compatibility with X1 series of Sharp Electronics
   - Almost the key components is the same as X1 models of Sharp Electronics and except for the keyboard input.
   - To port the X1 software to SPC-1500, Text attribute, keyboard input and DMA related code should be modified

*/

#include "emu.h"

#include "bus/centronics/ctronics.h"
#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "machine/i8255.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "sound/ay8910.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/spc1000_cas.h"

#include "utf8.h"


namespace {

#define VDP_CLOCK  XTAL(42'954'545)

class spc1500_state : public driver_device
{
public:
	spc1500_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_vdg(*this, "mc6845")
		, m_cass(*this, "cassette")
		, m_ram(*this, RAM_TAG)
		, m_p_videoram(*this, "videoram")
		, m_pcgram(*this, "pcgram")
		, m_io_kb(*this, "LINE.%u", 0)
		, m_io_joy(*this, "JOY")
		, m_dipsw(*this, "DIP_SWITCH")
		, m_centronics(*this, "centronics")
		, m_pio(*this, "ppi8255")
		, m_sound(*this, "ay8910")
		, m_palette(*this, "palette")
	{ }

	void spc1500(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;


private:
	uint8_t psga_r();
	uint8_t porta_r();
	void centronics_busy_w(int state) { m_centronics_busy = state; }
	uint8_t mc6845_videoram_r(offs_t offset);
	uint8_t keyboard_r(offs_t offset);
	void palet_w(offs_t offset, uint8_t data);
	void priority_w(uint8_t data);
	void pcg_w(offs_t offset, uint8_t data);
	uint8_t pcg_r(offs_t offset);
	void crtc_w(offs_t offset, uint8_t data);
	uint8_t crtc_r(offs_t offset);
	void romsel(uint8_t data);
	void ramsel(uint8_t data);
	void portb_w(uint8_t data);
	void psgb_w(uint8_t data);
	void portc_w(uint8_t data);
	uint8_t portb_r();
	void double_w(offs_t offset, uint8_t data);
	uint8_t io_r(offs_t offset);
	void spc_palette(palette_device &palette) const;
	MC6845_UPDATE_ROW(crtc_update_row);
	MC6845_RECONFIGURE(crtc_reconfig);
	TIMER_DEVICE_CALLBACK_MEMBER(timer);

	void spc1500_double_io(address_map &map) ATTR_COLD;
	void spc1500_mem(address_map &map) ATTR_COLD;

	uint8_t *m_p_ram = nullptr;
	uint8_t m_ipl = 0;
	uint8_t m_palet[3]{};
	uint8_t m_paltbl[8]{};
	uint8_t m_pcg_char = 0, m_pcg_attr = 0, m_char_change = 0;
	uint16_t m_pcg_offset[3]{};
	int m_char_count = 0;
	attotime m_time;
	bool m_romsel = false;
	bool m_double_mode = false;
	bool m_p5bit = false;
	bool m_motor = false;
	bool m_motor_toggle = false;
	uint8_t m_crtc_vreg[0x100]{};
	bool m_centronics_busy = false;
	required_device<z80_device> m_maincpu;
	required_device<mc6845_device> m_vdg;
	required_device<cassette_image_device> m_cass;
	required_device<ram_device> m_ram;
	required_shared_ptr<uint8_t> m_p_videoram;
	required_shared_ptr<uint8_t> m_pcgram;
	required_ioport_array<10> m_io_kb;
	required_ioport m_io_joy;
	required_ioport m_dipsw;
	required_device<centronics_device> m_centronics;
	required_device<i8255_device> m_pio;
	required_device<ay8910_device> m_sound;
	required_device<palette_device> m_palette;
	uint8_t *m_font = nullptr;
	uint8_t m_priority = 0;
	emu_timer *m_timer = nullptr;
	void get_pcg_addr();
};

uint8_t spc1500_state::keyboard_r(offs_t offset)
{
	offset &= 0xf;

	if (offset <= 9)
		return m_io_kb[offset]->read();
	else
		return 0xff;
}

void spc1500_state::romsel(uint8_t data)
{
	m_romsel = 1;
	if (m_ipl)
		membank("bank1")->set_entry(0);
	else
		membank("bank1")->set_entry(1);
}

void spc1500_state::ramsel(uint8_t data)
{
	m_romsel = 0;
	membank("bank1")->set_entry(2);
}

void spc1500_state::portb_w(uint8_t data)
{
//  m_ipl = data & (1 << 1);
}

void spc1500_state::psgb_w(uint8_t data)
{
	int elapsed_time = m_timer->elapsed().as_attoseconds()/ATTOSECONDS_PER_MICROSECOND;
	if (m_ipl != ((data>>1)&1))
	{
		m_ipl = ((data>>1)&1);
		membank("bank1")->set_entry(m_ipl ? 0 : 1);
	}
	//m_cass->change_state(BIT(data, 6) ? CASSETTE_SPEAKER_ENABLED : CASSETTE_SPEAKER_MUTED, CASSETTE_MASK_SPEAKER);
	if (m_motor && !BIT(data, 7) && (elapsed_time > 100))
	{
		m_cass->change_state((m_cass->get_state() & CASSETTE_MASK_MOTOR) == CASSETTE_MOTOR_DISABLED ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
		m_timer->reset();
	}
	m_motor = BIT(data, 7);
}

void spc1500_state::portc_w(uint8_t data)
{
	m_cass->output(BIT(data, 0) ? -1.0 : 1.0);
	m_centronics->write_strobe(BIT(data, 7));
	m_double_mode = (!m_p5bit && BIT(data, 5)); // double access I/O mode
	m_p5bit = BIT(data, 5);
	m_vdg->set_unscaled_clock(VDP_CLOCK/(BIT(data, 2) ? 48 : 24));
}

uint8_t spc1500_state::portb_r()
{
	uint8_t data = 0;
	data |= ((m_cass->get_state() & CASSETTE_MASK_UISTATE) == CASSETTE_STOPPED || ((m_cass->get_state() & CASSETTE_MASK_MOTOR) == CASSETTE_MOTOR_DISABLED));
	data |= (m_dipsw->read() & 1) << 4;
	data |= (m_cass->input() > 0.0038)<<1;
	data |= m_vdg->vsync_r()<<7;
	data &= ~((m_centronics_busy==0)<<3);
	return data;
}

void spc1500_state::crtc_w(offs_t offset, uint8_t data)
{
	static int m_crtc_index;
	if((offset & 1) == 0)
	{
		m_crtc_index = data & 0x1f;
		m_vdg->address_w(data);
	}
	else
	{
		m_crtc_vreg[m_crtc_index] = data;
		m_vdg->register_w(data);
	}
}

uint8_t spc1500_state::crtc_r(offs_t offset)
{
	if (offset & 1)
	{
		return m_vdg->register_r();
	}
	return 0;
}

TIMER_DEVICE_CALLBACK_MEMBER(spc1500_state::timer)
{
	if(m_motor_toggle == true)
	{
		m_cass->change_state((m_cass->get_state() & CASSETTE_MASK_MOTOR) == CASSETTE_MOTOR_DISABLED ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
		m_motor_toggle = false;
	}
}

void spc1500_state::get_pcg_addr()
{
	uint16_t vaddr = 0;
	if(m_p_videoram[0x7ff] & 0x20) {
		vaddr = 0x7ff;
	} else if(m_p_videoram[0x3ff] & 0x20) {
		vaddr = 0x3ff;
	} else if(m_p_videoram[0x5ff] & 0x20) {
		vaddr = 0x5ff;
	} else if(m_p_videoram[0x1ff] & 0x20) {
		vaddr = 0x1ff;
	} else {
		vaddr = 0x3ff;
	}
	m_pcg_char = m_p_videoram[0x1000 + vaddr];
	m_pcg_attr = m_p_videoram[vaddr];
	if (m_pcg_char != m_char_change)
	{
		m_char_change = m_pcg_char;
		m_pcg_offset[0] = 0;
		m_pcg_offset[1] = 0;
		m_pcg_offset[2] = 0;
	}
}

void spc1500_state::pcg_w(offs_t offset, uint8_t data)
{
	int reg = (offset>>8)-0x15;
	get_pcg_addr();

	m_pcgram[m_pcg_char * 8 + m_pcg_offset[reg] + (reg*0x800)] = data;
	if (m_pcg_offset[reg] == 7)
		m_pcg_offset[reg] = 0;
	else
		m_pcg_offset[reg]++;
}

uint8_t spc1500_state::pcg_r(offs_t offset)
{
	int reg = (offset>>8)-0x15;
	uint8_t data = 0;
	get_pcg_addr();
	if (reg < 0) reg = 2;
	if (BIT(m_pcg_attr,5)) // PCG font
	{
		data = m_pcgram[m_pcg_char * 8 + m_pcg_offset[reg]+(reg*0x800)];
	}
	else // ROM font
	{
		data = m_font[(m_crtc_vreg[0x9]==15?0x1000:0)+(m_pcg_char * 16)+m_pcg_offset[reg]];
	}
	if (m_pcg_offset[reg]++ > m_crtc_vreg[0x9]-1)
		m_pcg_offset[reg] = 0;
	return data;
}

void spc1500_state::priority_w(uint8_t data)
{
	m_priority = data;
}

void spc1500_state::palet_w(offs_t offset, uint8_t data)
{
	m_palet[(offset>>8)&0x0f] = data;
	for(int i=1, j=0; i < 0x100; i<<=1, j++)
	{
		m_paltbl[j] = (m_palet[0]&i?1:0)|(m_palet[1]&i?2:0)|(m_palet[2]&i?4:0);
	}
}

void spc1500_state::spc_palette(palette_device &palette) const
{
	palette.set_pen_color(0,rgb_t(0x00,0x00,0x00));
	palette.set_pen_color(1,rgb_t(0x00,0x00,0xff));
	palette.set_pen_color(2,rgb_t(0xff,0x00,0x00));
	palette.set_pen_color(3,rgb_t(0xff,0x00,0xff));
	palette.set_pen_color(4,rgb_t(0x00,0xff,0x00));
	palette.set_pen_color(5,rgb_t(0x00,0xff,0xff));
	palette.set_pen_color(6,rgb_t(0xff,0xff,0x00));
	palette.set_pen_color(7,rgb_t(0xff,0xff,0xff));
}


MC6845_RECONFIGURE(spc1500_state::crtc_reconfig)
{
//  printf("reconfig. w:%d, h:%d, %f (%d,%d,%d,%d)\n", width, height, (float)frame_period, visarea.left(), visarea.top(), visarea.right(), visarea.bottom());
//  printf("register. m_vert_disp:%d, m_horiz_disp:%d, m_max_ras_addr:%d, m_vert_char_total:%d\n", m_crtc_vreg[6], m_crtc_vreg[1],  m_crtc_vreg[9], m_crtc_vreg[0x4]);
}

MC6845_UPDATE_ROW(spc1500_state::crtc_update_row)
{
	uint8_t const *pf;
	uint16_t hfnt;
	uint32_t *p = &bitmap.pix(y);

	unsigned char cho[] ={1,1,1,1,1,1,1,1,0,0,1,1,1,3,5,5,0,0,5,3,3,5,5,5,0,0,3,3,5,1};
	unsigned char jong[]={0,0,0,1,1,1,1,1,0,0,1,1,1,2,2,2,0,0,2,2,2,2,2,2,0,0,2,2,1,1};
	char hs = (m_crtc_vreg[0x9] < 15 ? 3 : 4);
	int n = y & (m_crtc_vreg[0x9]);
	bool ln400 = (hs == 4 && m_crtc_vreg[0x4] > 20);
	uint8_t const *const vram = &m_p_videoram[0] + (m_crtc_vreg[12] << 8) + m_crtc_vreg[13];
	for (int i = 0; i < x_count; i++)
	{
		uint8_t const *const pp = &vram[0x2000+((y>>hs)*x_count+(((y)&7)<<11))+i+(((hs==4)&&(y&8))?0x400:0)];
		uint8_t const *const pv = &vram[(y>>hs)*x_count + i];
		uint8_t ascii = *(pv+0x1000);
		uint8_t attr = *pv;
		bool inv = (attr & 0x8 ? true : false);
		uint8_t color = attr & 0x7;
		uint8_t pixelb = *(pp+0);
		uint8_t pixelr = *(pp+0x4000);
		uint8_t pixelg = *(pp+0x8000);
		bool nopalet = ((m_palet[0] | m_palet[1] | m_palet[2])==0 || ln400);
		uint8_t pen = (nopalet ? color : m_paltbl[color]);
		if (hs == 4 && (ascii & 0x80))
		{
			uint16_t wpixelb = (pixelb << 8) + (*(pp+1));
			uint16_t wpixelr = (pixelr << 8) + (*(pp+0x4001));
			uint16_t wpixelg = (pixelg << 8) + (*(pp+0x8001));
			if (ascii != 0xfa)
			{
				uint8_t han2 = *(pv+0x1001);
				int h1 = (ascii>>2)&0x1f;
				int h2 = ((ascii<<3)|(han2>>5))&0x1f;
				int h3 = (han2)&0x1f;
				pf = &m_font[0x2000+(h1 * 32) + (cho[h2] + (h3 != 0) -1) * 16 * 2 * 32 + n];
				hfnt = (*pf << 8) | (*(pf+16));
				pf = &m_font[0x4000+(h2 * 32) + (h3 == 0 ? 0 : 1) * 16 * 2 * 32 + n];
				hfnt = hfnt & ((*pf << 8) | (*(pf+16)));
				pf = &m_font[0x6000+(h3 * 32) + (jong[h2]-1) * 16 * 2 * 32 + n];
				hfnt = hfnt & ((*pf << 8) | (*(pf+16)));
			}
			else
			{
				ascii = *(pv+0x1001);
				pf = &m_font[0x6000+(ascii*32) + n];
				hfnt = (*pf << 8) | (*(pf+16));
			}
			hfnt = (inv ? 0xffff - hfnt : hfnt);
			for (int j = 0x8000; j > 0; j>>=1)
			{
				uint8_t pixel = ((wpixelg&j ? 4:0 )|(wpixelr&j? 2:0)|(wpixelb&j ? 1:0));
				uint8_t pixelpen = (nopalet ? pixel : m_paltbl[pixel]);
				*p++ = m_palette->pen(((hfnt & j) || (m_priority & (1<<pixel))) ? pixelpen : pen);
			}
			i++;
		}
		else if (attr & 0x20)
		{
			uint8_t const *pa = &m_pcgram[(ascii*(m_crtc_vreg[0x9]+1))+n];
			uint8_t b = *pa;
			uint8_t r = *(pa+0x800);
			uint8_t g = *(pa+0x1000);
			for (int j = 0x80; j > 0; j>>=1)
			{
				uint8_t pixel = ((g & j)?4:0)|((r & j)?2:0)|((b & j)?1:0);
				pen = (pixel == 7 ? color : pixel);
				pixel = (pixelg&j ? 4 : 0)|(pixelr&j ? 2:0)|(pixelb&j ? 1:0 );
				uint8_t pixelpen = (nopalet ? pixel : m_paltbl[pixel]);
				*p++ = m_palette->pen((m_priority & (1<<pixel)) ? pixelpen : pen);
			}
		}
		else
		{
			uint8_t fnt = m_font[(hs == 4 ? 0x1000 : (attr & (1<<6) ? 0x80<<4 : 0)) + (ascii<<4) + n];
			if (ascii == 0 && (attr & 0x08) && inv)
			{
				fnt = 0xff;
			}
			fnt = (inv ? 0xff - fnt : fnt);
			for (int j = 0x80; j > 0; j>>=1)
			{
				uint8_t pixel = ((pixelg&j) ? 4 : 0)|(pixelr&j ? 2:0)|(pixelb&j ? 1:0 );
				uint8_t pixelpen = (nopalet ? pixel : m_paltbl[pixel]);
				if (ascii == 0 && attr == 0 && !inv)
					*p++ = m_palette->pen(pixelpen);
				else
					*p++ = m_palette->pen(((fnt & j) || (m_priority & (1<<pixel))) ? pixelpen : pen);
			}
		}
	}
}

void spc1500_state::double_w(offs_t offset, uint8_t data)
{
	if (m_double_mode)
	{
		if (offset < 0x4000) { offset += 0x2000; m_p_videoram[offset] = m_p_videoram[offset + 0x4000] = m_p_videoram[offset + 0x8000] = data; } else
		if (offset < 0x8000) { offset += 0x2000; m_p_videoram[offset + 0x8000] = m_p_videoram[offset + 0x4000] = data; } else
		if (offset < 0xc000) { offset += 0x2000; m_p_videoram[offset] = m_p_videoram[offset + 0x8000] = data; } else
		if (offset < 0x10000){ offset += 0x2000; m_p_videoram[offset] = m_p_videoram[offset - 0x4000] = data; }
	}
	else
	{
		if (offset < 0x1000) {} else
		if (offset < 0x1300) { palet_w(offset, data); } else
		if (offset < 0x1400) { priority_w(data); } else
		if (offset < 0x1800) { pcg_w(offset, data); } else
		if (offset < 0x1900) { crtc_w(offset, data); } else
		if (offset < 0x1a00) {} else
		if (offset < 0x1b00) { m_pio->write(offset, data); } else
		if (offset < 0x1c00) { m_sound->data_w(data);} else
		if (offset < 0x1d00) { m_sound->address_w(data);} else
		if (offset < 0x1e00) { romsel(data);} else
		if (offset < 0x1f00) { ramsel(data);} else
		if (offset < 0x2000) {} else
		if (offset < 0x10000)
		{
			if (offset < 0x4000)
			{
				offset &= 0xf7ff;
				m_p_videoram[offset-0x1800] = m_p_videoram[offset-0x2000] = data;
			}
			else
				m_p_videoram[offset-0x2000] = data;
		};
	}
}

uint8_t spc1500_state::io_r(offs_t offset)
{
	m_double_mode = false;
	if (offset < 0x1000) {} else
	if (offset < 0x1400) {} else
	if (offset < 0x1800) { return pcg_r(offset); } else
	if (offset < 0x1900) { return crtc_r(offset); } else
	if (offset < 0x1a00) { return keyboard_r(offset); } else
	if (offset < 0x1b00) { return m_pio->read(offset); } else
	if (offset < 0x1c00) { return m_sound->data_r(); } else
	if (offset < 0x2000) {} else
	if (offset < 0x10000){
		if (offset < 0x4000)
			offset &= 0xf7ff;
		return m_p_videoram[offset - 0x2000]; }
	return 0xff;
}

void spc1500_state::spc1500_double_io(address_map &map)
{
	map.unmap_value_high();
	map(0x2000, 0xffff).ram().share("videoram");
	map(0x0000, 0x17ff).ram().share("pcgram");
	map(0x0000, 0xffff).rw(FUNC(spc1500_state::io_r), FUNC(spc1500_state::double_w));
}

/* Input ports */
static INPUT_PORTS_START( spc1500 )

	PORT_START("DIP_SWITCH")
	PORT_DIPNAME( 0x01, 0x00, "40/80" )
	PORT_DIPSETTING(    0x00, "40COL" )
	PORT_DIPSETTING(    0x01, "80COL" )
	PORT_DIPNAME( 0x02, 0x02, "Language" )
	PORT_DIPSETTING(    0x02, "Korean" )
	PORT_DIPSETTING(    0x00, "English" )
	PORT_DIPNAME( 0x04, 0x00, "V-Res" )
	PORT_DIPSETTING(    0x04, "400" )
	PORT_DIPSETTING(    0x00, "200" )
	PORT_DIPNAME( 0x08, 0x08, "X1" )
	PORT_DIPSETTING(    0x08, "Compatible Mode" )
	PORT_DIPSETTING(    0x00, "Non Compatible" )

	PORT_START("LINE.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_RSHIFT) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_RCONTROL) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_PAUSE)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("\\ |") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|') PORT_CHAR(0x1c)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Graph") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("LINE.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("= +") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR(0x03)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR(0x01)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') PORT_CHAR(0x16)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1 !") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')

	PORT_START("LINE.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Caps") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(0x1a)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("] }") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}') PORT_CHAR(0x1d)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR(0x16)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR(0x13)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR(0x17)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2 @") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')

	PORT_START("LINE.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_INSERT) PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_UP)  PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(0x1b)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("` ~") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('`') PORT_CHAR('~')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR(0x02)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR(0x04)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(0x05)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3 #") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')

	PORT_START("LINE.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Del Ins") PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD)) PORT_CHAR(8)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Home") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(0x0e)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR(0x06)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4 $") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')

	PORT_START("LINE.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') PORT_CHAR(0x0d)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR(0x07)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(0x14)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5 %") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')

	PORT_START("LINE.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("[ {") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{') PORT_CHAR(0x1b)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(0x18)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') PORT_CHAR(0x08)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR(0x19)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6 ^") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')

	PORT_START("LINE.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR(0x10)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') PORT_CHAR(0x0a)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(0x15)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7 &") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')

	PORT_START("LINE.8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("\' \"") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('\"')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR(0x0b)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') PORT_CHAR(0x09)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8 *") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')

	PORT_START("LINE.9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Hangul") PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("- _") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0 )") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("; :") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR(0x0c)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(0x0e)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9 (") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')

	PORT_START("JOY")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT(0x10, IP_ACTIVE_HIGH,IPT_UNUSED) // DIP SW2 for Korean/English
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT(0x80, IP_ACTIVE_HIGH,IPT_UNUSED) // DIP SW3 for 200/400 line
INPUT_PORTS_END

void spc1500_state::spc1500_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).bankr("bank1").bankw("bank2");
	map(0x8000, 0xffff).bankrw("bank4");
}

void spc1500_state::machine_start()
{
	uint8_t *mem_basic = memregion("basic")->base();
	uint8_t *mem_ipl = memregion("ipl")->base();
	m_p_ram = m_ram->pointer();
	m_font = memregion("font1")->base();
	// configure and intialize banks 1 (read banks)
	membank("bank1")->configure_entry(0, mem_ipl);
	membank("bank1")->configure_entry(1, mem_basic);
	membank("bank1")->configure_entry(2, m_p_ram);
	membank("bank1")->set_entry(0);
	m_romsel = 1;
	// intialize banks 2, 3, 4 (write banks)
	membank("bank2")->set_base(m_p_ram);
	membank("bank4")->set_base(m_p_ram + 0x8000);
	m_timer = machine().scheduler().timer_alloc(timer_expired_delegate());
	m_timer->adjust(attotime::zero);
}

void spc1500_state::machine_reset()
{
	m_motor = false;
	m_time = machine().scheduler().time();
	m_double_mode = false;
	memset(&m_paltbl[0], 1, 8);
	m_char_count = 0;
}

[[maybe_unused]] uint8_t spc1500_state::mc6845_videoram_r(offs_t offset)
{
	return m_p_videoram[offset];
}

uint8_t spc1500_state::psga_r()
{
	uint8_t data = 0;
	data |= (BIT(m_dipsw->read(),1)<<4) | (BIT(m_dipsw->read(),2)<<7);
	data |= (m_io_joy->read() & 0x6f);
	return data;
}

[[maybe_unused]] uint8_t spc1500_state::porta_r()
{
	uint8_t data = 0x3f;
	data |= (m_cass->input() > 0.0038) ? 0x80 : 0;
	data |= ((m_cass->get_state() & CASSETTE_MASK_UISTATE) != CASSETTE_STOPPED) && ((m_cass->get_state() & CASSETTE_MASK_MOTOR) == CASSETTE_MOTOR_ENABLED)  ? 0x00 : 0x40;
	data &= ~((m_centronics_busy == 0)<< 5);
	return data;
}

void spc1500_state::spc1500(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &spc1500_state::spc1500_mem);
	//m_maincpu->set_addrmap(AS_IO, &spc1500_state::spc1500_io);
	m_maincpu->set_addrmap(AS_IO, &spc1500_state::spc1500_double_io);
	m_maincpu->set_periodic_int(FUNC(spc1500_state::irq0_line_hold), attotime::from_hz(60));

	/* video hardware */

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 400);
	screen.set_visarea(0,640-1,0,400-1);
	screen.set_screen_update("mc6845", FUNC(mc6845_device::screen_update));

	PALETTE(config, m_palette, FUNC(spc1500_state::spc_palette), 8);

	MC6845(config, m_vdg, (VDP_CLOCK/48)); //unknown divider
	m_vdg->set_screen("screen");
	m_vdg->set_show_border_area(false);
	m_vdg->set_char_width(8);
	m_vdg->set_update_row_callback(FUNC(spc1500_state::crtc_update_row));
	m_vdg->set_reconfigure_callback(FUNC(spc1500_state::crtc_reconfig));

	I8255(config, m_pio);
	m_pio->out_pa_callback().set("cent_data_out", FUNC(output_latch_device::write));
	m_pio->in_pb_callback().set(FUNC(spc1500_state::portb_r));
	m_pio->out_pb_callback().set(FUNC(spc1500_state::portb_w));
	m_pio->out_pc_callback().set(FUNC(spc1500_state::portc_w));

	TIMER(config, "1hz").configure_periodic(FUNC(spc1500_state::timer), attotime::from_hz(1));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	AY8910(config, m_sound, XTAL(4'000'000) / 2);
	m_sound->port_a_read_callback().set(FUNC(spc1500_state::psga_r));
	m_sound->port_b_write_callback().set(FUNC(spc1500_state::psgb_w));
	m_sound->add_route(ALL_OUTPUTS, "mono", 1.00);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(spc1500_state::centronics_busy_w));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	INPUT_BUFFER(config, "cent_status_in");

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_DISABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cass->set_formats(spc1000_cassette_formats);
	m_cass->set_interface("spc1500_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("spc1500_cass");

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("64K");
}

/* ROM definition */
ROM_START( spc1500 )
	ROM_REGION(0x8000, "ipl", ROMREGION_ERASEFF)
	ROM_LOAD("ipl.rom", 0x0000, 0x8000, CRC(80d0704a) SHA1(01e4cbe8baad72effbbe01addd477c5b0ec85c16))
	ROM_REGION(0x8000, "basic", ROMREGION_ERASEFF)
	ROM_LOAD("basic.rom", 0x0000, 0x8000, CRC(f48328e1) SHA1(fb874ea7d20078726682f2d0e03ea0d1f8bdbb07))
	ROM_REGION(0x8000, "font1", 0)
	ROM_LOAD( "ss150fnt.bin", 0x0000, 0x2000, CRC(affdc5c0) SHA1(2a93582fcccf9e40b99ae238ce585d189afe9a5a) )
	ROM_LOAD( "ss151fnt.bin", 0x2000, 0x2000, CRC(83c2eb8d) SHA1(2adf7816206dc74b9f0d32cb3b56cbab31fa6044) )
	ROM_LOAD( "ss152fnt.bin", 0x4000, 0x2000, CRC(f4a5a590) SHA1(c9a02756107083bf602ae7c90cfe29b8b964e0df) )
	ROM_LOAD( "ss153fnt.bin", 0x6000, 0x2000, CRC(8677d5fa) SHA1(34bfacc855c3846744cd586c150c72e5cbe948b0) )
ROM_END

} // Anonymous namespace


/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY    FULLNAME    FLAGS
COMP( 1987, spc1500, 0,      0,      spc1500, spc1500, spc1500_state, empty_init, "Samsung", "SPC-1500", 0 )



spec128.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Kevin Thacker
/***************************************************************************

    NOTE: ****** Specbusy: press N, R, or E to boot *************


        Spectrum/Inves/TK90X etc. memory map:

    CPU:
        0000-3fff ROM
        4000-ffff RAM

        Spectrum 128/+2/+2a/+3 memory map:

        CPU:
                0000-3fff Banked ROM/RAM (banked rom only on 128/+2)
                4000-7fff Banked RAM
                8000-bfff Banked RAM
                c000-ffff Banked RAM

        TS2068 memory map: (Can't have both EXROM and DOCK active)
        The 8K EXROM can be loaded into multiple pages.

    CPU:
                0000-1fff     ROM / EXROM / DOCK (Cartridge)
                2000-3fff     ROM / EXROM / DOCK
                4000-5fff \
                6000-7fff  \
                8000-9fff  |- RAM / EXROM / DOCK
                a000-bfff  |
                c000-dfff  /
                e000-ffff /


Interrupts:

Changes:

29/1/2000   KT -    Implemented initial +3 emulation.
30/1/2000   KT -    Improved input port decoding for reading and therefore
            correct keyboard handling for Spectrum and +3.
31/1/2000   KT -    Implemented buzzer sound for Spectrum and +3.
            Implementation copied from Paul Daniel's Jupiter driver.
            Fixed screen display problems with dirty chars.
            Added support to load .Z80 snapshots. 48k support so far.
13/2/2000   KT -    Added Interface II, Kempston, Fuller and Mikrogen
            joystick support.
17/2/2000   DJR -   Added full key descriptions and Spectrum+ keys.
            Fixed Spectrum +3 keyboard problems.
17/2/2000   KT -    Added tape loading from WAV/Changed from DAC to generic
            speaker code.
18/2/2000   KT -    Added tape saving to WAV.
27/2/2000   KT -    Took DJR's changes and added my changes.
27/2/2000   KT -    Added disk image support to Spectrum +3 driver.
27/2/2000   KT -    Added joystick I/O code to the Spectrum +3 I/O handler.
14/3/2000   DJR -   Tape handling dipswitch.
26/3/2000   DJR -   Snapshot files are now classified as snapshots not
            cartridges.
04/4/2000   DJR -   Spectrum 128 / +2 Support.
13/4/2000   DJR -   +4 Support (unofficial 48K hack).
13/4/2000   DJR -   +2a Support (rom also used in +3 models).
13/4/2000   DJR -   TK90X, TK95 and Inves support (48K clones).
21/4/2000   DJR -   TS2068 and TC2048 support (TC2048 Supports extra video
            modes but doesn't have bank switching or sound chip).
09/5/2000   DJR -   Spectrum +2 (France, Spain), +3 (Spain).
17/5/2000   DJR -   Dipswitch to enable/disable disk drives on +3 and clones.
27/6/2000   DJR -   Changed 128K/+3 port decoding (sound now works in Zub 128K).
06/8/2000   DJR -   Fixed +3 Floppy support
10/2/2001   KT  -   Re-arranged code and split into each model emulated.
            Code is split into 48k, 128k, +3, tc2048 and ts2048
            segments. 128k uses some of the functions in 48k, +3
            uses some functions in 128, and tc2048/ts2048 use some
            of the functions in 48k. The code has been arranged so
            these functions come in some kind of "override" order,
            read functions changed to use  READ8_HANDLER and write
            functions changed to use WRITE8_HANDLER.
            Added Scorpion256 preliminary.
18/6/2001   DJR -   Added support for Interface 2 cartridges.
xx/xx/2001  KS -    TS-2068 sound fixed.
            Added support for DOCK cartridges for TS-2068.
            Added Spectrum 48k Psycho modified rom driver.
            Added UK-2086 driver.
23/12/2001  KS -    48k machines are now able to run code in screen memory.
                Programs which keep their code in screen memory
                like monitors, tape copiers, decrunchers, etc.
                works now.
                Fixed problem with interrupt vector set to 0xffff (much
        more 128k games works now).
                A useful used trick on the Spectrum is to set
                interrupt vector to 0xffff (using the table
                which contain 0xff's) and put a byte 0x18 hex,
                the opcode for JR, at this address. The first
                byte of the ROM is a 0xf3 (DI), so the JR will
                jump to 0xfff4, where a long JP to the actual
                interrupt routine is put. Due to unideal
                bankswitching in MAME this JP were to 0001 what
                causes Spectrum to reset. Fixing this problem
                made much more software running (i.e. Paperboy).
            Corrected frames per second value for 48k and 128k
            Sinclair machines.
                There are 50.08 frames per second for Spectrum
                48k what gives 69888 cycles for each frame and
                50.021 for Spectrum 128/+2/+2A/+3 what gives
                70908 cycles for each frame.
            Remapped some Spectrum+ keys.
                Pressing F3 to reset was setting 0xf7 on keyboard
                input port. Problem occurred for snapshots of
                some programs where it was read as pressing
                key 4 (which is exit in Tapecopy by R. Dannhoefer
                for example).
            Added support to load .SP snapshots.
            Added .BLK tape images support.
                .BLK files are identical to .TAP ones, extension
                is an only difference.
08/03/2002  KS -    #FF port emulation added.
                Arkanoid works now, but is not playable due to
                completely messed timings.

Initialisation values used when determining which model is being emulated:
 48K        Spectrum doesn't use either port.
 128K/+2    Bank switches with port 7ffd only.
 +3/+2a     Bank switches with both ports.

Notes:
 1. No contented memory.
 2. No hi-res colour effects (need contended memory first for accurate timing).
 3. Multiface 1 and Interface 1 not supported.
 4. Horace and the Spiders cartridge doesn't run properly.
 5. Tape images not supported:
    .TZX, .SPC, .ITM, .PAN, .TAP(Warajevo), .VOC, .ZXS.
 6. Snapshot images not supported:
    .ACH, .PRG, .RAW, .SEM, .SIT, .SNX, .ZX, .ZXS, .ZX82.
 7. 128K emulation is not perfect - the 128K machines crash and hang while
    running quite a lot of games.
 8. Disk errors occur on some +3 games.
 9. Video hardware of all machines is timed incorrectly.
10. EXROM and HOME cartridges are not emulated.
11. The TK90X and TK95 roms output 0 to port #df on start up.
12. The purpose of this port is unknown (probably display mode as TS2068) and
    thus is not emulated.

Very detailed infos about the ZX Spectrum +3e can be found at

http://www.z88forever.org.uk/zxplus3e/


The hc2000 corrupts its memory, especially if you type something, and the
resulting mess can be seen in the F4 viewer display.

*******************************************************************************/

#include "emu.h"
#include "spec128.h"

#include "bus/spectrum/ay/slot.h"
#include "bus/spectrum/dma/slot.h"
#include "cpu/z80/z80.h"

#include "screen.h"

#include "formats/tzx_cas.h"


/****************************************************************************
 * Spectrum 128 specific functions
 ****************************************************************************/
void spectrum_128_state::video_start()
{
	spectrum_state::video_start();
	m_screen_location = m_ram->pointer() + (5 << 14);
}

uint8_t spectrum_128_state::spectrum_128_pre_opcode_fetch_r(offs_t offset)
{
	m_ula->m1(offset);

	/* this allows expansion devices to act upon opcode fetches from MEM addresses
	   for example, interface1 detection fetches requires fetches at 0008 / 0708 to
	   enable paged ROM and then fetches at 0700 to disable it
	*/
	m_exp->pre_opcode_fetch(offset);
	uint8_t retval = m_program.read_byte(offset);
	m_exp->post_opcode_fetch(offset);
	return retval;
}

void spectrum_128_state::spectrum_128_rom_w(offs_t offset, uint8_t data)
{
	m_exp->mreq_w(offset, data);
}

u8 spectrum_128_state::spectrum_128_rom_r(offs_t offset)
{
	return m_exp->romcs()
		? m_exp->mreq_r(offset)
		: ((u8*)m_bank_rom[0]->base())[offset];
}

template <u8 Bank> void spectrum_128_state::spectrum_128_ram_w(offs_t offset, u8 data)
{
	u16 addr = 0x4000 * Bank + offset;
	m_ula->data_w(addr);
	if (is_vram_write(addr)) m_screen->update_now();

	((u8*)m_bank_ram[Bank]->base())[offset] = data;
}

// Base 128 models typically don't share RAM in bank0. Reserved for extension in 256+.
template void spectrum_128_state::spectrum_128_ram_w<0>(offs_t offset, u8 data);

template <u8 Bank> u8 spectrum_128_state::spectrum_128_ram_r(offs_t offset)
{
	u16 addr = 0x4000 * Bank + offset;
	m_ula->data_r(addr);

	return ((u8*)m_bank_ram[Bank]->base())[offset];
}

// D0-D2: RAM page located at 0x0c000-0x0ffff
//    D3: Screen select (screen 0 in ram page 5, screen 1 in ram page 7
//    D4: ROM select - which rom paged into 0x0000-0x03fff
//    D5: Disable paging
void spectrum_128_state::spectrum_128_port_7ffd_w(offs_t offset, uint8_t data)
{
	m_ula->ula_w(offset);

	// disable paging?
	if (m_port_7ffd_data & 0x20) return;

	// store new state
	m_port_7ffd_data = data;

	// update memory
	spectrum_128_update_memory();

	m_exp->iorq_w(offset | 1, data);
}

void spectrum_128_state::spectrum_128_update_memory()
{
	m_bank_rom[0]->set_entry(BIT(m_port_7ffd_data, 4));
	// select ram at 0x0c000-0x0ffff
	m_bank_ram[3]->set_entry(m_port_7ffd_data & 0x07);
	m_ula->bank3_pg_w(m_port_7ffd_data & 0x07);

	m_screen->update_now();
	if (BIT(m_port_7ffd_data, 3))
		m_screen_location = m_ram->pointer() + (7 << 14);
	else
		m_screen_location = m_ram->pointer() + (5 << 14);
}

uint8_t spectrum_128_state::spectrum_port_r(offs_t offset)
{
	m_ula->io_r(offset);

	// Pass through to expansion device if present
	if (m_exp->get_card_device())
		return m_exp->iorq_r(offset | 1);

	return floating_bus_r();
}

void spectrum_128_state::spectrum_128_io(address_map &map)
{
	map(0x0000, 0x0000).select(0xfffe).rw(FUNC(spectrum_128_state::spectrum_ula_r), FUNC(spectrum_128_state::spectrum_ula_w));
	map(0x0001, 0x0001).select(0xfffe).rw(FUNC(spectrum_128_state::spectrum_port_r), FUNC(spectrum_128_state::spectrum_port_w));
	map(0x0001, 0x0001).select(0x7ffc).w(FUNC(spectrum_128_state::spectrum_128_port_7ffd_w));   // (A15 | A1) == 0, note: reading from this port does write to it by value from data bus
	map(0x8000, 0x8000).mirror(0x3ffd).w("ay_slot", FUNC(ay_slot_device::data_w));
	map(0xc000, 0xc000).mirror(0x3ffd).rw("ay_slot", FUNC(ay_slot_device::data_r), FUNC(ay_slot_device::address_w));
}

void spectrum_128_state::spectrum_128_mem(address_map &map)
{
	map(0x0000, 0x3fff).rw(FUNC(spectrum_128_state::spectrum_128_rom_r), FUNC(spectrum_128_state::spectrum_128_rom_w));
	map(0x4000, 0x7fff).rw(FUNC(spectrum_128_state::spectrum_128_ram_r<1>), FUNC(spectrum_128_state::spectrum_128_ram_w<1>));
	map(0x8000, 0xbfff).rw(FUNC(spectrum_128_state::spectrum_128_ram_r<2>), FUNC(spectrum_128_state::spectrum_128_ram_w<2>));
	map(0xc000, 0xffff).rw(FUNC(spectrum_128_state::spectrum_128_ram_r<3>), FUNC(spectrum_128_state::spectrum_128_ram_w<3>));
}

void spectrum_128_state::spectrum_128_fetch(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(spectrum_128_state::spectrum_128_pre_opcode_fetch_r));
}

static INPUT_PORTS_START( spec_plus_joys )
	PORT_START("JOY2") // 0xF7FE
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)  PORT_8WAY PORT_PLAYER(2) PORT_CODE(JOYCODE_X_LEFT_SWITCH)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_8WAY PORT_PLAYER(2) PORT_CODE(JOYCODE_X_RIGHT_SWITCH)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)  PORT_8WAY PORT_PLAYER(2) PORT_CODE(JOYCODE_Y_DOWN_SWITCH)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)    PORT_8WAY PORT_PLAYER(2) PORT_CODE(JOYCODE_Y_UP_SWITCH)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_BUTTON1)        PORT_PLAYER(2) PORT_CODE(JOYCODE_BUTTON1)

	PORT_START("JOY1") // 0xEFFE
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON1)        PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)    PORT_8WAY PORT_PLAYER(1) PORT_CODE(JOYCODE_Y_UP_SWITCH)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)  PORT_8WAY PORT_PLAYER(1) PORT_CODE(JOYCODE_Y_DOWN_SWITCH)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_8WAY PORT_PLAYER(1) PORT_CODE(JOYCODE_X_RIGHT_SWITCH)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)  PORT_8WAY PORT_PLAYER(1) PORT_CODE(JOYCODE_X_LEFT_SWITCH)
INPUT_PORTS_END

/* These keys need not to be mapped in natural mode because Spectrum+ supports both these and the Spectrum sequences above.
   Hence, we can simply keep using such sequences in natural keyboard emulation */
INPUT_PORTS_START( spec128 )
	PORT_INCLUDE( spectrum )

	PORT_START("PLUS0") // Spectrum+ Keys (Same as CAPS + 1-5)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("EDIT") PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CAPS LOCK") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TRUE VID") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("INV VID") PORT_CODE(KEYCODE_END)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cursor Left") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("PLUS1") // Spectrum+ Keys (Same as CAPS + 6-0)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DEL") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("GRAPH") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cursor Right") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cursor Up") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cursor Down") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("PLUS2") // Spectrum+ Keys (Same as CAPS + SPACE and CAPS + SYMBOL)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BREAK") PORT_CODE(KEYCODE_PAUSE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("EXT MODE") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0xfc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("PLUS3") // Spectrum+ Keys (Same as SYMBOL SHIFT + O/P)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\"") PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0xfc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("PLUS4") // Spectrum+ Keys (Same as SYMBOL SHIFT + N/M)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0xf3, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

INPUT_PORTS_START( spec_plus )
	PORT_INCLUDE( spec128 )
	PORT_INCLUDE( spec_plus_joys )
INPUT_PORTS_END

void spectrum_128_state::machine_start()
{
	spectrum_state::machine_start();

	save_item(NAME(m_port_7ffd_data));

	// rom 0 is 128K rom, rom 1 is 48 BASIC
	memory_region *rom = memregion("maincpu");
	m_bank_rom[0]->configure_entries(0, 2, rom->base() + 0x10000, 0x4000);

	auto ram_entries = m_ram->size() / 0x4000;
	for (auto i = 1; i < 4; i++)
		m_bank_ram[i]->configure_entries(0, ram_entries, m_ram->pointer(), 0x4000);

	m_bank_ram[1]->set_entry(ram_entries > 5 ? 5 : (ram_entries - 1)); // Bank 5 is always in 0x4000 - 0x7fff
	m_bank_ram[2]->set_entry(2); // Bank 2 is always in 0x8000 - 0xbfff
}

void spectrum_128_state::machine_reset()
{
	spectrum_state::machine_reset();

	// set initial ram config
	m_port_7ffd_data = 0;
	spectrum_128_update_memory();
}

bool spectrum_128_state::is_vram_write(offs_t offset) {
	return (BIT(m_port_7ffd_data, 3) && m_bank_ram[3]->entry() == 7)
		? offset >= 0xc000 && offset < 0xdb00
		: spectrum_state::is_vram_write(offset);
}

u8 *spectrum_128_state::snow_pattern1_base(u8 i_reg)
{
	const bool is_alt_scr_selected = BIT(m_port_7ffd_data, 3);
	const bool is_alt_scr = i_reg & 0x80;
	const u8 i_pg = is_alt_scr ? (m_bank_ram[3]->entry() & 0b101) : 5;

	return m_ram->pointer() + ((i_pg | (is_alt_scr_selected << 1)) << 14);
}

static const gfx_layout spectrum_charlayout =
{
	8, 8,           // 8 x 8 characters
	96,             // 96 characters
	1,              // 1 bits per pixel
	{ 0 },          // no bitplanes
	{STEP8(0, 1)},  // x offsets
	{STEP8(0, 8)},  // y offsets
	8*8             // every char takes 8 bytes
};

static GFXDECODE_START( spec128 )
	GFXDECODE_ENTRY( "maincpu", 0x17d00, spectrum_charlayout, 7, 8 )
GFXDECODE_END

rectangle spectrum_128_state::get_screen_area()
{
	return { SPEC_LEFT_BORDER, SPEC_LEFT_BORDER + SPEC_DISPLAY_XSIZE - 1,
			SPEC128_UNSEEN_LINES + SPEC_TOP_BORDER, SPEC128_UNSEEN_LINES + SPEC_TOP_BORDER + SPEC_DISPLAY_YSIZE - 1 };
}

void spectrum_128_state::spectrum_128(machine_config &config)
{
	spectrum(config);

	Z80(config.replace(), m_maincpu, X1_128_SINCLAIR / 10);
	m_maincpu->set_memory_map(&spectrum_128_state::spectrum_128_mem);
	m_maincpu->set_io_map(&spectrum_128_state::spectrum_128_io);
	m_maincpu->set_m1_map(&spectrum_128_state::spectrum_128_fetch);
	m_maincpu->set_vblank_int("screen", FUNC(spectrum_128_state::spec_interrupt));
	m_maincpu->refresh_cb().set(FUNC(spectrum_128_state::spectrum_refresh_w));
	m_maincpu->nomreq_cb().set("ula", FUNC(spectrum_ula_device::nomem_rq));
	m_maincpu->busack_cb().set("dma", FUNC(dma_slot_device::bai_w));

	config.set_maximum_quantum(attotime::from_hz(60));

	// video hardware
	rectangle visarea = { get_screen_area().left() - SPEC_LEFT_BORDER, get_screen_area().right() + SPEC_RIGHT_BORDER,
		get_screen_area().top() - SPEC_TOP_BORDER, get_screen_area().bottom() + SPEC_BOTTOM_BORDER };
	m_screen->set_raw(X1_128_SINCLAIR / 5, SPEC128_CYCLES_PER_LINE * 2, SPEC128_UNSEEN_LINES + SPEC_SCREEN_HEIGHT, visarea);

	subdevice<gfxdecode_device>("gfxdecode")->set_info(spec128);

	SPECTRUM_ULA_128K(config.replace(), m_ula);
	m_ula->set_z80(m_maincpu);
	m_ula->set_screen(m_screen, get_screen_area());

	// sound hardware
	AY_SLOT(config, "ay_slot", X1_128_SINCLAIR / 20, default_ay_slot_devices, "ay_ay8912")
		.add_route(ALL_OUTPUTS, "speakers", 0.25);

	// expansion port
	SPECTRUM_EXPANSION_SLOT(config.replace(), m_exp, spec128_expansion_devices, nullptr);
	m_exp->irq_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_exp->nmi_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_exp->fb_r_handler().set(FUNC(spectrum_128_state::floating_bus_r));

	dma_slot_device &dma(DMA_SLOT(config.replace(), "dma", X1_128_SINCLAIR / 10, default_dma_slot_devices, nullptr));
	dma.set_io_space(m_maincpu, AS_IO);
	dma.out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	dma.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	dma.in_mreq_callback().set([this](offs_t offset) { return m_program.read_byte(offset); });
	dma.out_mreq_callback().set([this](offs_t offset, u8 data) { m_program.write_byte(offset, data); });
	dma.in_iorq_callback().set([this](offs_t offset) { return m_io.read_byte(offset); });
	dma.out_iorq_callback().set([this](offs_t offset, u8 data) { m_io.write_byte(offset, data); });

	// internal ram
	m_ram->set_default_size("128K");
}


/***************************************************************************

  Game driver(s)

***************************************************************************/

ROM_START(spec128)
	ROM_REGION(0x18000,"maincpu",0)
	ROM_SYSTEM_BIOS( 0, "en", "English" )
	ROMX_LOAD("zx128_0.rom",0x10000,0x4000, CRC(e76799d2) SHA1(4f4b11ec22326280bdb96e3baf9db4b4cb1d02c5), ROM_BIOS(0))
	ROMX_LOAD("zx128_1.rom",0x14000,0x4000, CRC(b96a36be) SHA1(80080644289ed93d71a1103992a154cc9802b2fa), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "sp", "Spanish" )
	ROMX_LOAD("zx128s0.rom",0x10000,0x4000, CRC(453d86b2) SHA1(968937b1c750f0ef6205f01c6db4148da4cca4e3), ROM_BIOS(1))
	ROMX_LOAD("zx128s1.rom",0x14000,0x4000, CRC(6010e796) SHA1(bea3f397cc705eafee995ea629f4a82550562f90), ROM_BIOS(1))
ROM_END

ROM_START(specpls2)
	ROM_REGION(0x18000,"maincpu",0)
	ROM_SYSTEM_BIOS( 0, "en", "English" )
	ROMX_LOAD("zxp2_0.rom",0x10000,0x4000, CRC(5d2e8c66) SHA1(72703f9a3e734f3c23ec34c0727aae4ccbef9a91), ROM_BIOS(0))
	ROMX_LOAD("zxp2_1.rom",0x14000,0x4000, CRC(98b1320b) SHA1(de8b0d2d0379cfe7c39322a086ca6da68c7f23cb), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "fr", "French" )
	ROMX_LOAD("plus2fr0.rom",0x10000,0x4000, CRC(c684c535) SHA1(56684c4c85a616e726a50707483b9a42d8e724ed), ROM_BIOS(1))
	ROMX_LOAD("plus2fr1.rom",0x14000,0x4000, CRC(f5e509c5) SHA1(7e398f62689c9d90a36d3a101351ec9987207308), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "sp", "Spanish" )
	ROMX_LOAD("plus2sp0.rom",0x10000,0x4000, CRC(e807d06e) SHA1(8259241b28ff85441f1bedc2bee53445767c51c5), ROM_BIOS(2))
	ROMX_LOAD("plus2sp1.rom",0x14000,0x4000, CRC(41981d4b) SHA1(ec0d5a158842d20601b4fbeaefc6668db979d0e1), ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "ao", "ZX Spectrum +2c (Andrew Owen)" )
	ROMX_LOAD("plus2c-0.rom",0x10000,0x4000, CRC(bfddf748) SHA1(3eba870bcb2c5efa906f2ca3febe960fc35d66bb), ROM_BIOS(3))
	ROMX_LOAD("plus2c-1.rom",0x14000,0x4000, CRC(fd8552b6) SHA1(5ffcf79f2154ba2cf42cc1d9cb4be93cb5043e73), ROM_BIOS(3))
	ROM_SYSTEM_BIOS( 4, "namco", "ZX Spectrum +2c (Namco)" )
	ROMX_LOAD("pl2namco.rom",0x10000,0x8000, CRC(72a54e75) SHA1(311400157df689450dadc3620f4c4afa960b05ad), ROM_BIOS(4))
ROM_END

ROM_START(hc128)  // Romanian clone, "ICE Felix HC91+" (aka HC-128), AY-8910 was optional
	ROM_REGION(0x18000,"maincpu",0)
	ROM_LOAD("zx128_0.rom",0x10000,0x4000, CRC(e76799d2) SHA1(4f4b11ec22326280bdb96e3baf9db4b4cb1d02c5))
	ROM_LOAD("hc128.rom",  0x14000,0x4000, CRC(0241e960) SHA1(cea0d14391b9e571460a816088a1c00ecb24afa3))
ROM_END


//    YEAR  NAME      PARENT   COMPAT  MACHINE       INPUT      STATE               INIT        COMPANY                  FULLNAME           FLAGS
COMP( 1986, spec128,  0,       0,      spectrum_128, spec128,   spectrum_128_state, empty_init, "Sinclair Research Ltd", "ZX Spectrum 128", 0 )
COMP( 1986, specpls2, spec128, 0,      spectrum_128, spec_plus, spectrum_128_state, empty_init, "Amstrad plc",           "ZX Spectrum +2",  0 )
COMP( 1991, hc128,    spec128, 0,      spectrum_128, spec_plus, spectrum_128_state, empty_init, "ICE-Felix",             "HC-91+ (HC-128)", 0 )



special.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/*****************************************************************************************************************

Specialist driver by Miodrag Milanovic

2008-03-15 Preliminary driver.
2008-03-20 Cassette support

Notes:
- Scrolling is noticeably slow.
- Hit Alt to switch between normal and Russian characters.

- Special, specialp, lik, erik: At the first prompt, press Enter for the Monitor. All other keys just beep.

- Specimx: Press Enter to go into a ramdisk File Manager. Press F3 to load a tape (which errors).
           If you had loaded a disk, press F6 to boot it.
- Specimx -bios 1 and 2 don't set a colour, so a default has been chosen instead of a black screen.
- Specimx -bios 1 lists a number of programs on the ramdisk, but most of them don't work.

- Lik: B to enter Basic. Haven't found an official way to exit, but you can enter MLOAD and hit Enter twice.

- Unga: It can load and run a tape, like Special. Or, press Y and you're taken to BASIC. You might find that the
  keyboard is stuck in lowercase, so hit Left-Alt to switch to upper. Now it will work. If you enter SYSTEM to
  exit, it corrupts itself and runs into the weeds. At the initial prompt, if you hit Enter it jumps to 0000 and
  thence into the weeds.

- Anakonda: It complains of a disk problem (not emulated), before falling into a cassette loader. If you had
  loaded a tape from the command line, hit enter 3 times quickly before the tape starts to play. It will load
  and run.

Important tape notes:
- If you press any key (including F2 to start the tape), the tape either will not load, or the machine hangs.
  Same problem occurs if you use the Tab menu.
- To load a tape from the swlist, you must include the software name on the command-line, or use a frontend.
  It will load and run by itself at start. DON'T touch anything until it's finished.
- Tapes from the swlist only work with "special"; they may load on others but unlikely to run.

ToDo:
- Much mystery surrounds the usage of cassette.
- Keyboard is a bit strange, especially with Enter key
- What does or does not get changed by Shift key is all over the place
- Lik: Space and < keys not working
- Pioner: Not all keys have been found (Function keys, Left-arrow, more?)
- Pioner: Monitor roms missing, used ones from "special".
- Special -bios 2 contains Basic. How to access it?
- Erik: should have colour and floppy disks, unknown how to access them. No disk software available.
- Erik: erik_disk_reg_w function is commented out, is it needed? Appears necessary for floppy operation.
- Specimx: how to access the disks from swlist?
- Specimx: Machine tends to crash/freeze internally after a while.


****************************************************************************************************************/

#include "emu.h"
#include "special.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


/* Address maps */
void special_state::specialist_mem(address_map &map)
{
	map(0x0000, 0x8fff).ram().share("mainram");
	map(0x0000, 0x2fff).bankr("bank1");
	map(0x9000, 0xbfff).ram().share("videoram");
	map(0xc000, 0xefff).rom().region("maincpu",0);
	map(0xf800, 0xf803).mirror(0x7fc).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
}

void special_state::specialp_mem(address_map &map)
{
	map(0x0000, 0x7fff).ram().share("mainram");
	map(0x0000, 0x2fff).bankr("bank1");
	map(0x8000, 0xbfff).ram().share("videoram");
	map(0xc000, 0xefff).rom().region("maincpu",0);
	map(0xf800, 0xf803).mirror(0x7fc).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
}

void special_state::erik_mem(address_map &map)
{
	map(0x0000, 0x3fff).bankrw("bank1");
	map(0x4000, 0x8fff).bankrw("bank2");
	map(0x9000, 0xbfff).bankrw("bank3");
	map(0xc000, 0xefff).bankrw("bank4");
	map(0xf000, 0xf7ff).bankrw("bank5");
	map(0xf800, 0xffff).bankrw("bank6");
}

void special_state::erik_io(address_map &map)
{
	map.global_mask(0xff);
	map(0xf1, 0xf1).rw(FUNC(special_state::erik_rr_reg_r), FUNC(special_state::erik_rr_reg_w));
	map(0xf2, 0xf2).rw(FUNC(special_state::erik_rc_reg_r), FUNC(special_state::erik_rc_reg_w));
	map(0xf3, 0xf3).rw(FUNC(special_state::erik_disk_reg_r), FUNC(special_state::erik_disk_reg_w));
	map(0xf4, 0xf7).rw(m_fdc, FUNC(fd1793_device::read), FUNC(fd1793_device::write));
}

void special_state::specimx_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x8fff).bankrw("bank1");
	map(0x9000, 0xbfff).bankrw("bank2");
	map(0xc000, 0xffbf).bankrw("bank3");
	map(0xffc0, 0xffdf).bankrw("bank4");
	map(0xffe0, 0xffe3).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xffe4, 0xffe7).rw("ppi2", FUNC(i8255_device::read), FUNC(i8255_device::write)); //external 8255
	map(0xffe8, 0xffeb).rw(m_fdc, FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	map(0xffec, 0xffef).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xfff0, 0xfff3).rw(FUNC(special_state::specimx_disk_ctrl_r), FUNC(special_state::specimx_disk_ctrl_w));
	map(0xfff8, 0xfffb).rw(FUNC(special_state::specimx_video_color_r), FUNC(special_state::specimx_video_color_w));
	map(0xfffc, 0xffff).w(FUNC(special_state::specimx_select_bank));
}

/* Input ports */

/* Inputs will need to be adapted when/if support for non-latin keys is added to natural keyboard:
these systems have different keys to switch among alphabets!
*/
static INPUT_PORTS_START( special )
/* Alt switches between Latin and Cyrillic alphabets */
	PORT_START("LINE0")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR('_')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ins") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START("LINE1")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("End") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('\\')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11))

	PORT_START("LINE2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10))

	PORT_START("LINE3")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F12) PORT_CHAR(UCHAR_MAMEKEY(F12))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9))

	PORT_START("LINE4")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))

	PORT_START("LINE5")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))

	PORT_START("LINE6")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))

	PORT_START("LINE7")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))

	PORT_START("LINE8")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))

	PORT_START("LINE9")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))

	PORT_START("LINE10")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))

	PORT_START("LINE11")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Lat / Cyr") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('C')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_START("LINE12")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
INPUT_PORTS_END

static INPUT_PORTS_START( specialp )
	PORT_INCLUDE( special )

	/* Shift itself switches between Latin and Cyrillic alphabets */
	PORT_MODIFY("LINE0")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Delete") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_CHAR('_')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('=') PORT_CHAR('-')

	PORT_MODIFY("LINE11")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Alt") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
INPUT_PORTS_END

static INPUT_PORTS_START( lik )
	PORT_INCLUDE( special )

	/* 2009-05 FP: Shift + Numbers produces still numbers: does it work differently outside monitor? */
	PORT_MODIFY("LINE0")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Delete") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)

	PORT_MODIFY("LINE2")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',')// PORT_CHAR('<')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9')// PORT_CHAR(')')

	PORT_MODIFY("LINE3")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8')// PORT_CHAR('(')

	PORT_MODIFY("LINE4")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7')// PORT_CHAR('\'')

	PORT_MODIFY("LINE5")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6')// PORT_CHAR('&')

	PORT_MODIFY("LINE6")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5')// PORT_CHAR('%')

	PORT_MODIFY("LINE7")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4')// PORT_CHAR('$')

	PORT_MODIFY("LINE8")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3')// PORT_CHAR('#')

	PORT_MODIFY("LINE9")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2')// PORT_CHAR('"')

	PORT_MODIFY("LINE10")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1')// PORT_CHAR('!')
INPUT_PORTS_END


static INPUT_PORTS_START( specimx )
/* Alt switches between Latin and Cyrillic alphabets */
	PORT_START("LINE0")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR('_')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ins") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START("LINE1")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("End") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9))

	PORT_START("LINE2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))

	PORT_START("LINE3")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))

	PORT_START("LINE4")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))

	PORT_START("LINE5")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))

	PORT_START("LINE6")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))

	PORT_START("LINE7")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR(0xA4)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))

	PORT_START("LINE8")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))

	PORT_START("LINE9")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_START("LINE10")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Home") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10))

	PORT_START("LINE11")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Alt") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))

	PORT_START("LINE12")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
INPUT_PORTS_END

static INPUT_PORTS_START( pioner )
/* Left Alt switches between Latin and Cyrillic alphabets */
	PORT_START("LINE0")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 _") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('_')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RV off") PORT_CODE(KEYCODE_PGUP) // reverse off
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE1")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 ,") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR(',')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')

	PORT_START("LINE2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Alt") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 \"") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('"')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')

	PORT_START("LINE3")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 -") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('-')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("# *") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('#') PORT_CHAR('*')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')

	PORT_START("LINE4")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 )") PORT_CODE(KEYCODE_1) PORT_CHAR(')')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("+ (") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('+') PORT_CHAR('(')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE5")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 /") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('/')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')

	PORT_START("LINE6")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 :") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR(':')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')

	PORT_START("LINE7")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Repeat") PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 .") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('.')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE8")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Home") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 ?") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('?')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RV") PORT_CODE(KEYCODE_PGDN)  // reverse video on
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("> @") PORT_CODE(KEYCODE_STOP) PORT_CHAR('>') PORT_CHAR('@')

	PORT_START("LINE9")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0 %") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('%')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("$ ;") PORT_CODE(KEYCODE_COLON) PORT_CHAR('$') PORT_CHAR(';')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("< ^") PORT_CODE(KEYCODE_COMMA) PORT_CHAR('<') PORT_CHAR('^')

	PORT_START("LINE10")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("= !") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('!')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Clear") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END))  // clear screen
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)

	PORT_START("LINE11")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("& ]") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('&') PORT_CHAR(']')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Newline") PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(DEL))  // newline
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("' [") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('[')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))

	PORT_START("LINE12")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
INPUT_PORTS_END

void special_state::specimx_floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_SMX_FORMAT);
}

static void specimx_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

/* Machine driver */
void special_state::special(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, 2000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &special_state::specialist_mem);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(384, 256);
	screen.set_visarea(0, 384-1, 0, 256-1);
	screen.set_screen_update(FUNC(special_state::screen_update_special));
	screen.set_palette(m_palette);

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	/* audio hardware */
	SPEAKER(config, "mono").front_center();
	DAC_1BIT(config, "dac", 0).add_route(ALL_OUTPUTS, "mono", 0.0625);

	/* Devices */
	I8255(config, m_ppi);
	m_ppi->in_pa_callback().set(FUNC(special_state::porta_r));
	m_ppi->out_pa_callback().set(FUNC(special_state::porta_w));
	m_ppi->in_pb_callback().set(FUNC(special_state::portb_r));
	m_ppi->out_pb_callback().set(FUNC(special_state::portb_w));
	m_ppi->in_pc_callback().set(FUNC(special_state::portc_r));
	m_ppi->out_pc_callback().set(FUNC(special_state::portc_w));

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(rks_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("special_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("special_cass");
}

void special_state::specialp(machine_config &config)
{
	special(config);
	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &special_state::specialp_mem);

	screen_device &screen(*subdevice<screen_device>("screen"));
	screen.set_screen_update(FUNC(special_state::screen_update_specialp));
	screen.set_size(512, 256);
	screen.set_visarea(0, 512-1, 0, 256-1);
}

void special_state::specialm(machine_config &config)
{
	special(config);
	m_ppi->in_pb_callback().set(FUNC(special_state::specimx_portb_r));
}

void special_state::specimx(machine_config &config)
{
	special(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &special_state::specimx_mem);

	/* video hardware */
	subdevice<screen_device>("screen")->set_screen_update(FUNC(special_state::screen_update_specimx));

	m_palette->set_init(FUNC(special_state::specimx_palette));
	m_palette->set_entries(16);

	/* audio hardware */
	SPECIMX_SND(config, "custom", 0).add_route(ALL_OUTPUTS, "mono", 1.0);

	/* Devices */
	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(2000000);
	m_pit->out_handler<0>().set("custom", FUNC(specimx_sound_device::set_input_ch0));
	m_pit->set_clk<1>(2000000);
	m_pit->out_handler<1>().set("custom", FUNC(specimx_sound_device::set_input_ch1));
	m_pit->set_clk<2>(2000000);
	m_pit->out_handler<2>().set("custom", FUNC(specimx_sound_device::set_input_ch2));
	TIMER(config, m_pit_timer).configure_generic(FUNC(special_state::pit_timer));

	m_ppi->in_pb_callback().set(FUNC(special_state::specimx_portb_r));
	m_ppi->out_pc_callback().set(FUNC(special_state::specimx_portc_w));

	I8255(config, "ppi2");

	FD1793(config, m_fdc, 8_MHz_XTAL / 8);
	m_fdc->drq_wr_callback().set(FUNC(special_state::fdc_drq));
	FLOPPY_CONNECTOR(config, "fd0", specimx_floppies, "525qd", special_state::specimx_floppy_formats);
	FLOPPY_CONNECTOR(config, "fd1", specimx_floppies, "525qd", special_state::specimx_floppy_formats);
	SOFTWARE_LIST(config, "flop_list").set_original("special_flop");

	/* internal ram */
	RAM(config, m_ram).set_default_size("128K").set_default_value(0x00);
}

void special_state::erik(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 4000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &special_state::erik_mem);
	m_maincpu->set_addrmap(AS_IO, &special_state::erik_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(384, 256);
	screen.set_visarea(0, 384-1, 0, 256-1);
	screen.set_screen_update(FUNC(special_state::screen_update_erik));
	screen.set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(special_state::erik_palette), 8);

	/* audio hardware */
	SPEAKER(config, "mono").front_center();
	DAC_1BIT(config, "dac", 0).add_route(ALL_OUTPUTS, "mono", 0.0625);

	/* Devices */
	CASSETTE(config, m_cassette);
	m_cassette->set_formats(rks_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("special_cass");

	I8255(config, m_ppi);
	m_ppi->in_pa_callback().set(FUNC(special_state::porta_r));
	m_ppi->out_pa_callback().set(FUNC(special_state::porta_w));
	m_ppi->in_pb_callback().set(FUNC(special_state::portb_r));
	m_ppi->out_pb_callback().set(FUNC(special_state::portb_w));
	m_ppi->in_pc_callback().set(FUNC(special_state::portc_r));
	m_ppi->out_pc_callback().set(FUNC(special_state::specimx_portc_w));

	FD1793(config, m_fdc, 8_MHz_XTAL / 8);
	m_fdc->drq_wr_callback().set(FUNC(special_state::fdc_drq));
	FLOPPY_CONNECTOR(config, "fd0", specimx_floppies, "525qd", special_state::specimx_floppy_formats);
	FLOPPY_CONNECTOR(config, "fd1", specimx_floppies, "525qd", special_state::specimx_floppy_formats);

	/* internal ram */
	RAM(config, m_ram).set_default_size("192K").set_default_value(0x00);
}

/* ROM definition */
ROM_START( special )
	ROM_REGION( 0x3000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "2nd", "2nd rev.")
	ROMX_LOAD( "monitor2_1.rom",  0x0000, 0x0800, CRC(52abde77) SHA1(66ba2ef9eac14a5c0df510224ea25fd5745399cd), ROM_BIOS(0))
	ROMX_LOAD( "monitor2_2.rom",  0x0800, 0x0800, CRC(c425f719) SHA1(1c322591b4e5c8b01b81362c6801aa6fd9fc1492), ROM_BIOS(0))
	ROMX_LOAD( "monitor2_3.rom",  0x1000, 0x0800, CRC(d804aeba) SHA1(1585f354719c25e1f59c7cb8b3a3f5d309a7e8fb), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "2rom", "2nd rev. rom disk")
	ROMX_LOAD( "monitor2_1.rom",  0x0000, 0x0800, CRC(52abde77) SHA1(66ba2ef9eac14a5c0df510224ea25fd5745399cd), ROM_BIOS(1))
	ROMX_LOAD( "m2_rom-disk.rom", 0x0800, 0x0800, CRC(7bd3d476) SHA1(232341755ae794f8aab4f6181c8d499a66016af2), ROM_BIOS(1))
	ROMX_LOAD( "monitor2_3.rom",  0x1000, 0x0800, CRC(d804aeba) SHA1(1585f354719c25e1f59c7cb8b3a3f5d309a7e8fb), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "1b", "1st rev. + BASIC") // pressing Enter corrupts monitor prompt gfx
	ROMX_LOAD( "root.rom",        0x0000, 0x0800, CRC(62de741d) SHA1(6c6a29d4340b0b1230c708f9c04ff2ed1c012a76), ROM_BIOS(2))
	ROMX_LOAD( "pzu2.rom",        0x0800, 0x0800, CRC(49937e13) SHA1(872ae5a7c3496d4404cdd577caa2236424016c66), ROM_BIOS(2))
	ROMX_LOAD( "pzu3.rom",        0x1000, 0x0800, CRC(dc817a08) SHA1(8101fe924386f38c10ef929e6dea5a83fcf34600), ROM_BIOS(2))
	ROMX_LOAD( "pzu4.rom",        0x1800, 0x0800, CRC(6793ba23) SHA1(3a27b5dbc6561ea7af5fb30513dc83ec64f1d94c), ROM_BIOS(2))
	ROMX_LOAD( "pzu5.rom",        0x2000, 0x0800, CRC(13a1a0dc) SHA1(3a0818cb8f36c2c5a9b9916669538ad1702a7710), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "1st", "1st rev.")
	ROMX_LOAD( "special1.rom",    0x0000, 0x1000, CRC(217414bd) SHA1(345cd1410fbca8f75421d12d1419f27f81cd35d6), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "2col", "2nd rev. color")
	ROMX_LOAD( "col_mon2.rom",    0x0000, 0x0800, CRC(8cebb1b5) SHA1(0c912a25220de8c5135e16c443e4796e6bb6f805), ROM_BIOS(4))
	ROMX_LOAD( "monitor2_2.rom",  0x0800, 0x0800, CRC(c425f719) SHA1(1c322591b4e5c8b01b81362c6801aa6fd9fc1492), ROM_BIOS(4))
	ROMX_LOAD( "monitor2_3.rom",  0x1000, 0x0800, CRC(d804aeba) SHA1(1585f354719c25e1f59c7cb8b3a3f5d309a7e8fb), ROM_BIOS(4))
ROM_END

ROM_START( specialm )
	ROM_REGION( 0x3000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "pzu1-m.rom",       0x0000, 0x0800, CRC(61e94485) SHA1(dbe212dcd377cb8cd16000516cd4b3b760ce779e))
	ROM_LOAD( "pzu2-m.rom",       0x0800, 0x0800, CRC(83d76815) SHA1(9d139eaa6ca6b241fa9fac94ede72abb56d83674))
	ROM_LOAD( "pzu3-m.rom",       0x1000, 0x0800, CRC(2121ad65) SHA1(b43811d058d818f8c1cee59b405b515f96958656))
	ROM_LOAD( "pzu4-m.rom",       0x1800, 0x0800, CRC(8bf8218e) SHA1(bd1174c384b04a40dfcaa35aa373c5070304f37b))
ROM_END

ROM_START( specialp )
	ROM_REGION( 0x3000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "special6.rom",     0x0000, 0x1000, CRC(f0c5a0ac) SHA1(50b53bd7c05117930aa84653a9ea0fc0c6f0f496) )
ROM_END

ROM_START( lik )
	ROM_REGION( 0x3000, "maincpu", 0 )
	ROM_SYSTEM_BIOS(0, "1st", "1st rev.")
	ROMX_LOAD( "lik.rom",         0x0000, 0x3000, CRC(705bb3a0) SHA1(f90b009ec9d3303bbda228714dd24de057e744b6), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "2nd", "2nd rev.")
	ROMX_LOAD( "lik2.rom",        0x0000, 0x3000, CRC(71820e43) SHA1(a85b4fc33b1ea96a1b8fe0c791f1aab8e967bb44), ROM_BIOS(1))
ROM_END

ROM_START( specimx )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "fos", "ROM FOS") // colour
	ROMX_LOAD( "specimx.rom",     0x0000, 0xb800,  CRC(db68f9b1) SHA1(c79888449f8a605267ec3e10dcc8e6e6f43b3a95), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "nc", "NC") // monochrome
	ROMX_LOAD( "ncrdy.rom",       0x0000, 0x10000, CRC(5d04c522) SHA1(d7daa7fe14cd8e0c6f87fd6453ec3e94ea2c259f) ,ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "ramfos", "RAMFOS") // monochrome
	ROMX_LOAD( "ramfos.rom",      0x0000, 0x3000,  CRC(83e19df4) SHA1(20e5e53eb45729a24c1c7c63e114dbd14e3c4184) ,ROM_BIOS(2))
ROM_END

ROM_START( erik )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_SYSTEM_BIOS(0, "0", "0")
	ROMX_LOAD( "erik.bin",        0x0000, 0x10000, CRC(6f3208f4) SHA1(41f6e2763ef60d3c7214c98893e580d25346fa2d), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "1", "1")
	ROMX_LOAD( "ericrom2.bin",    0x0000, 0x10000, CRC(712e1f1b) SHA1(6b44db4fe6b0834e5ff8a2091e71610f77f81b5e), ROM_BIOS(1))
ROM_END

ROM_START( pioner )
	// Ukranian clone with 16KB RAM
	ROM_REGION( 0x3000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "pioner.rf2",       0x0000, 0x0800, CRC(d6250ab2) SHA1(b953517d883c64857e63139fed52436f77d371cb))
	// monitor roms missing from this set. Using ones from 'special'.
	ROM_LOAD( "monitor2_2.rom",   0x0800, 0x0800, BAD_DUMP CRC(c425f719) SHA1(1c322591b4e5c8b01b81362c6801aa6fd9fc1492))
	ROM_LOAD( "monitor2_3.rom",   0x1000, 0x0800, BAD_DUMP CRC(d804aeba) SHA1(1585f354719c25e1f59c7cb8b3a3f5d309a7e8fb))
ROM_END

ROM_START( unga )
	ROM_REGION( 0x3000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "unga.rom",        0x0000, 0x3000, CRC(b45f2971) SHA1(561b907ee0a305bc5e963ecd70ecb2b132f936f3) )
ROM_END

ROM_START( anakonda )
	ROM_REGION( 0x3000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "anakonda.rom",    0x0000, 0x0800, CRC(50ba6462) SHA1(f55a9f8e78a3e97012e343ea35cd787fc45dae35) )
ROM_END

ROM_START( kharkovsky ) // Made in Kharkiv, Ukraine
	ROM_REGION( 0x3000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "zagr.bin",        0x0000, 0x0800, CRC(f86ba4db) SHA1(1f48c74ffce88f6e804f776c66d6c03059fca117) )
	ROM_LOAD( "mon.bin",         0x0800, 0x0800, CRC(2dd1b6d2) SHA1(d5a02a9645f57eb0295d6b8d8cd8c589b47ba591) )
ROM_END

/* Driver */

//    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT     CLASS          INIT          COMPANY      FULLNAME                    FLAGS
COMP( 1985, special,    0,       0,    special,  special,  special_state, init_special, "<unknown>", "Specialist",               MACHINE_SUPPORTS_SAVE )
COMP( 1985, specialm,   special, 0,    specialm, special,  special_state, init_special, "<unknown>", "Specialist M",             MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1993, pioner,     special, 0,    special,  pioner,   special_state, init_special, "<unknown>", "Pioner",                   MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1985, specialp,   special, 0,    specialp, specialp, special_state, init_special, "<unknown>", "Specialist + hires graph", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1985, lik,        special, 0,    special,  lik,      special_state, init_special, "<unknown>", "Lik",                      MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1985, specimx,    special, 0,    specimx,  specimx,  special_state, empty_init,   "<unknown>", "Specialist MX",            MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1994, erik,       special, 0,    erik,     special,  special_state, init_erik,    "<unknown>", "Erik",                     MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 198?, unga,       special, 0,    special,  special,  special_state, init_special, "<unknown>", "Unga",                     MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1989, anakonda,   special, 0,    special,  special,  special_state, init_special, "<unknown>", "Anakonda",                 MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 198?, kharkovsky, special, 0,    special,  special,  special_state, init_special, "<unknown>", "Kharkovsky",               MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )




specnext.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Andrei I. Holub
/**********************************************************************
    Spectrum Next

    Versions: TBBlue 1.2, Issue 0, Issue 1, Issue 2,
              Issue 2B (Kickstarter 1), Issue 2D, Issue 2E, Issue 2H,
              Issue 4 (Kickstarter 2)
    Current implementation is based on Issue 4. Only limited difference
    tracked through PORT_CONFIG

    TODO:
    * improve zxnDMA
    * contention
    * internal_port_enable() support
    * (1) invalidate tiles/sprites caches on region w, not every frame

**********************************************************************/

#include "emu.h"

#include "beta_m.h"
#include "screen_ula.h"
#include "spec128.h"
#include "specnext_copper.h"
#include "specnext_ctc.h"
#include "specnext_divmmc.h"
#include "specnext_dma.h"
#include "specnext_multiface.h"
#include "specnext_layer2.h"
#include "specnext_lores.h"
#include "specnext_sprites.h"
#include "specnext_tiles.h"

#include "bus/spectrum/zxbus/bus.h"
#include "cpu/z80/z80n.h"
#include "machine/ds1307.h"
#include "machine/spi_sdcard.h"
#include "sound/ay8910.h"
#include "sound/dac.h"
#include "speaker.h"


#define LOG_IO    (1U << 1)
#define LOG_MEM   (1U << 2)
#define LOG_WARN  (1U << 3)

//#define VERBOSE ( LOG_GENERAL | LOG_IO | /*LOG_MEM |*/ LOG_WARN )
#include "logmacro.h"

#define LOGIO(...)    LOGMASKED(LOG_IO,    __VA_ARGS__)
#define LOGMEM(...)   LOGMASKED(LOG_MEM,   __VA_ARGS__)
#define LOGWARN(...)  LOGMASKED(LOG_WARN,  __VA_ARGS__)


namespace {

#define TIMINGS_PERFECT     0

struct video_timings_info {
	u16 min_hblank;
	u16 int_h;

	u16 min_hsync;
	u16 max_hsync;
	u16 max_hblank;
	u16 min_hactive; // 256x192 area
	u16 max_hc;

	u16 min_vblank;
	u16 int_v;
	u16 min_vsync; // displays don't like vsync = vblank
	u16 max_vsync;
	u16 max_vblank;
	u16 min_vactive; // 256x192 area
	u16 max_vc;

	// hdmi 360x288
	u16 hdmi_xmin;
	u16 hdmi_xmax;
	u16 hdmi_ymin;
	u16 hdmi_ymax;
	u16 hdmi_ysync;
};

class specnext_state : public spectrum_128_state
{
public:
	specnext_state(const machine_config &mconfig, device_type type, const char *tag)
		: spectrum_128_state(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_io_shadow_view(*this, "io_shadow_view")
		, m_bank_boot_rom(*this, "bootrom")
		, m_bank_ram(*this, "bank_ram%u", 0U)
		, m_view0(*this, "mem_view0")
		, m_view1(*this, "mem_view1")
		, m_view2(*this, "mem_view2")
		, m_view3(*this, "mem_view3")
		, m_view4(*this, "mem_view4")
		, m_view5(*this, "mem_view5")
		, m_view6(*this, "mem_view6")
		, m_view7(*this, "mem_view7")
		, m_copper(*this, "copper")
		, m_ctc(*this, "ctc")
		, m_dma(*this, "ndma")
		, m_i2c(*this, "i2c")
		, m_sdcard(*this, "sdcard")
		, m_ay(*this, "ay%u", 0U)
		, m_dac(*this, "dac%u", 0U)
		, m_palette(*this, "palette")
		, m_regs_map(*this, "regs_map")
		, m_mf(*this, "multiface")
		, m_divmmc(*this, "divmmc")
		, m_ula_scr(*this, "ula_scr")
		, m_tiles(*this, "tiles")
		, m_layer2(*this, "layer2")
		, m_lores(*this, "lores")
		, m_sprites(*this, "sprites")
		, m_io_issue(*this, "ISSUE")
		, m_io_video(*this, "VIDEO")
		, m_io_mouse(*this, "mouse_input%u", 1U)
	{}

	void tbblue(machine_config &config);

	INPUT_CHANGED_MEMBER(on_mf_nmi);
	INPUT_CHANGED_MEMBER(on_divmmc_nmi);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	void reset_hard();
	virtual void video_start() override ATTR_COLD;
	virtual void spectrum_128_update_memory() override {}
	void update_video_mode();

	u8 do_m1(offs_t offset);
	void do_mf_nmi();
	void leave_nmi(int status);
	void map_fetch(address_map &map) ATTR_COLD;
	void map_mem(address_map &map) ATTR_COLD;
	void map_io(address_map &map) ATTR_COLD;
	void map_regs(address_map &map) ATTR_COLD;
	u8 reg_r(offs_t reg);
	void reg_w(offs_t reg, u8 data);
	void mmu_w(offs_t bank, u8 data);
	void mmu_x2_w(offs_t bank, u8 data);
	u8 dma_r(bool dma_mode);
	void dma_w(bool dma_mode, u8 data);
	u8 dma_mreq_r(offs_t offset);
	u8 spi_data_r();
	void spi_data_w(u8 data);
	void spi_miso_w(u8 data);
	void i2c_scl_w(u8 data);
	void palette_val_w(u8 nr_palette_priority, u16 nr_palette_value);
	u8 port_ff_r();
	void port_ff_w(u8 data);
	void turbosound_address_w(u8 data);
	u8 mf_port_r(offs_t addr);
	void mf_port_w(offs_t addr, u8 data);
	attotime copper_until_pos_r(u16 pos);

	void bank_update(u8 bank, u8 count);
	void bank_update(u8 bank);
	void memory_change(u16 port, u8 data);
	u16 get_layer2_active_page(u8 bank);
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	required_device<z80n_device> m_maincpu;

private:
	static const u8 MACHINE_TBBLUE = 0x08;
	static const u8 MACHINE_NEXT = 0x0a;
	static const u8 MACHINE_NEXT_AB = 0xfa; // Anti Brick (reset disabled, bootrom)
	static const u8 G_VERSION = 0x32; // 3.02
	static const u8 G_SUB_VERSION = 0x01;
	static const u8 G_VIDEO_INC = 0b11;
	static const u16 UTM_FALLBACK_PEN = 0x800;

	virtual TIMER_CALLBACK_MEMBER(irq_off) override;
	virtual TIMER_CALLBACK_MEMBER(irq_on) override;
	INTERRUPT_GEN_MEMBER(specnext_interrupt);
	TIMER_CALLBACK_MEMBER(line_irq_on);
	INTERRUPT_GEN_MEMBER(line_interrupt);
	IRQ_CALLBACK_MEMBER(irq_callback);
	TIMER_CALLBACK_MEMBER(spi_clock);
	void line_irq_adjust();

	u8 g_machine_id() { return m_io_issue->read() ? MACHINE_NEXT : MACHINE_TBBLUE; }
	u8 g_board_issue() { return m_io_issue->read(); }
	bool machine_type_48() const { return m_nr_03_machine_type == 0 || m_nr_03_machine_type == 1; }
	bool machine_type_128() const { return m_nr_03_machine_type == 2 || m_nr_03_machine_type == 4; }
	bool machine_type_p3() const { return !machine_type_48() && !machine_type_128(); }

	void nr_02_w(u8 nr_wr_dat);
	bool nr_02_iotrap() { return m_nr_da_iotrap_cause & 3; }
	void nr_07_cpu_speed_w(u8 data);

	void nr_0a_mf_type_w(u8 data) { m_nr_0a_mf_type = data; m_mf->mf_mode_w(m_nr_0a_mf_type); }
	void nr_12_layer2_active_bank_w(u8 data) { m_nr_12_layer2_active_bank = data; bank_update(0, 6); m_layer2->layer2_active_bank_w(m_nr_12_layer2_active_bank); }
	void nr_13_layer2_shadow_bank_w(u8 data) { m_nr_13_layer2_shadow_bank = data; bank_update(0, 6); }
	void nr_14_global_transparent_rgb_w(u8 data);
	void nr_15_sprite_priority_w(bool data) { m_nr_15_sprite_priority = data; m_sprites->zero_on_top_w(m_nr_15_sprite_priority); }
	void nr_15_sprite_border_clip_en_w(bool data) { m_nr_15_sprite_border_clip_en = data; m_sprites->border_clip_en_w(m_nr_15_sprite_border_clip_en); }
	void nr_15_sprite_over_border_en_w(bool data) { m_nr_15_sprite_over_border_en = data; m_sprites->over_border_w(m_nr_15_sprite_over_border_en); }
	void nr_16_layer2_scrollx_w(u8 data) { m_nr_16_layer2_scrollx = data; m_layer2->scroll_x_w((m_nr_71_layer2_scrollx_msb << 8) | m_nr_16_layer2_scrollx); }
	void nr_17_layer2_scrolly_w(u8 data) { m_nr_17_layer2_scrolly = data; m_layer2->scroll_y_w(m_nr_17_layer2_scrolly); }
	void nr_18_layer2_clip_x1_w(u8 data) { m_nr_18_layer2_clip_x1 = data; m_layer2->clip_x1_w(m_nr_18_layer2_clip_x1); }
	void nr_18_layer2_clip_x2_w(u8 data) { m_nr_18_layer2_clip_x2 = data; m_layer2->clip_x2_w(m_nr_18_layer2_clip_x2); }
	void nr_18_layer2_clip_y1_w(u8 data) { m_nr_18_layer2_clip_y1 = data; m_layer2->clip_y1_w(m_nr_18_layer2_clip_y1); }
	void nr_18_layer2_clip_y2_w(u8 data) { m_nr_18_layer2_clip_y2 = data; m_layer2->clip_y2_w(m_nr_18_layer2_clip_y2); }
	void nr_19_sprite_clip_x1_w(u8 data) { m_nr_19_sprite_clip_x1 = data; m_sprites->clip_x1_w(m_nr_19_sprite_clip_x1); }
	void nr_19_sprite_clip_x2_w(u8 data) { m_nr_19_sprite_clip_x2 = data; m_sprites->clip_x2_w(m_nr_19_sprite_clip_x2); }
	void nr_19_sprite_clip_y1_w(u8 data) { m_nr_19_sprite_clip_y1 = data; m_sprites->clip_y1_w(m_nr_19_sprite_clip_y1); }
	void nr_19_sprite_clip_y2_w(u8 data) { m_nr_19_sprite_clip_y2 = data; m_sprites->clip_y2_w(m_nr_19_sprite_clip_y2); }
	void nr_1a_ula_clip_x1_w(u8 data) { m_nr_1a_ula_clip_x1 = data; m_ula_scr->ula_clip_x1_w(m_nr_1a_ula_clip_x1); m_lores->clip_x1_w(m_nr_1a_ula_clip_x1); }
	void nr_1a_ula_clip_x2_w(u8 data) { m_nr_1a_ula_clip_x2 = data; m_ula_scr->ula_clip_x2_w(m_nr_1a_ula_clip_x2); m_lores->clip_x2_w(m_nr_1a_ula_clip_x2); }
	void nr_1a_ula_clip_y1_w(u8 data) { m_nr_1a_ula_clip_y1 = data; m_ula_scr->ula_clip_y1_w(m_nr_1a_ula_clip_y1); m_lores->clip_y1_w(m_nr_1a_ula_clip_y1); }
	void nr_1a_ula_clip_y2_w(u8 data);
	void nr_1b_tm_clip_x1_w(u8 data) { m_nr_1b_tm_clip_x1 = data; m_tiles->clip_x1_w(m_nr_1b_tm_clip_x1); }
	void nr_1b_tm_clip_x2_w(u8 data) { m_nr_1b_tm_clip_x2 = data; m_tiles->clip_x2_w(m_nr_1b_tm_clip_x2); }
	void nr_1b_tm_clip_y1_w(u8 data) { m_nr_1b_tm_clip_y1 = data; m_tiles->clip_y1_w(m_nr_1b_tm_clip_y1); }
	void nr_1b_tm_clip_y2_w(u8 data) { m_nr_1b_tm_clip_y2 = data; m_tiles->clip_y2_w(m_nr_1b_tm_clip_y2); }
	void nr_26_ula_scrollx_w(u8 data) { m_nr_26_ula_scrollx = data; m_ula_scr->ula_scroll_x_w(m_nr_26_ula_scrollx); }
	void nr_27_ula_scrolly_w(u8 data) { m_nr_27_ula_scrolly = data; m_ula_scr->ula_scroll_y_w(m_nr_27_ula_scrolly); }

	void nr_30_tm_scrollx_w(u16 data) { m_nr_30_tm_scrollx = data; m_tiles->tm_scroll_x_w(m_nr_30_tm_scrollx); }
	void nr_31_tm_scrolly_w(u8 data) { m_nr_31_tm_scrolly = data; m_tiles->tm_scroll_y_w(m_nr_31_tm_scrolly); }
	void nr_32_lores_scrollx_w(u8 data) { m_nr_32_lores_scrollx = data; m_lores->scroll_x_w(m_nr_32_lores_scrollx); }
	void nr_33_lores_scrolly_w(u8 data) { m_nr_33_lores_scrolly = data; m_lores->scroll_y_w(m_nr_33_lores_scrolly); }

	void nr_42_ulanext_format_w(u8 data) { m_nr_42_ulanext_format = data; m_ula_scr->ulanext_format_w(m_nr_42_ulanext_format); }
	void nr_43_ulanext_en_w(bool data) { m_nr_43_ulanext_en = data; m_ula_scr->ulanext_en_w(m_nr_43_ulanext_en); m_lores->ulap_en_w(m_port_ff3b_ulap_en && !m_nr_43_ulanext_en); }
	void nr_43_active_ula_palette_w(bool data) { m_nr_43_active_ula_palette = data; m_ula_scr->ula_palette_select_w(m_nr_43_active_ula_palette); m_lores->lores_palette_select_w(m_nr_43_active_ula_palette); }
	void nr_43_active_layer2_palette_w(bool data) { m_nr_43_active_layer2_palette = data; m_layer2->layer2_palette_select_w(m_nr_43_active_layer2_palette); }
	void nr_43_active_sprite_palette_w(bool data) { m_nr_43_active_sprite_palette = data; m_sprites->sprite_palette_select_w(m_nr_43_active_sprite_palette); }

	void nr_4b_sprite_transparent_index_w(u8 data) { m_nr_4b_sprite_transparent_index = data; m_sprites->transp_colour_w(m_nr_4b_sprite_transparent_index); }
	void nr_4c_tm_transparent_index_w(u8 data) { m_nr_4c_tm_transparent_index = data; m_tiles->transp_colour_w(m_nr_4c_tm_transparent_index); }

	void nr_62_copper_mode_w(u8 data) { m_nr_62_copper_mode = data; m_copper->copper_en_w(m_nr_62_copper_mode); }
	void nr_68_ula_fine_scroll_x_w(bool data) { m_nr_68_ula_fine_scroll_x = data; m_ula_scr->ula_fine_scroll_x_w(m_nr_68_ula_fine_scroll_x); }

	void nr_6a_lores_radastan_w(bool data) { m_nr_6a_lores_radastan = data; m_lores->mode_w(m_nr_6a_lores_radastan); }
	void nr_6a_lores_radastan_xor_w(bool data) { m_nr_6a_lores_radastan_xor = data; m_lores->dfile_w(BIT(m_port_ff_data, 0) != m_nr_6a_lores_radastan_xor); }
	void nr_6a_lores_palette_offset_w(u8 data) { m_nr_6a_lores_palette_offset = data; m_lores->lores_palette_offset_w(m_nr_6a_lores_palette_offset); }
	void nr_6b_tm_control_w(u8 data) { m_nr_6b_tm_control = data; m_tiles->control_w(m_nr_6b_tm_control); }
	void nr_6c_tm_default_attr_w(u8 data) { m_nr_6c_tm_default_attr = data; m_tiles->default_flags_w(m_nr_6c_tm_default_attr); }
	void nr_6e_tilemap_base_w(bool data7, u8 data5_0) { m_nr_6e_tilemap_base_7 = data7; m_nr_6e_tilemap_base = data5_0; m_tiles->tm_map_base_w((m_nr_6e_tilemap_base_7 << 6) | m_nr_6e_tilemap_base); }
	void nr_6f_tilemap_tiles_w(bool data7, u8 data5_0) { m_nr_6f_tilemap_tiles_7 = data7; m_nr_6f_tilemap_tiles = data5_0; m_tiles->tm_tile_base_w((m_nr_6f_tilemap_tiles_7 << 6) | m_nr_6f_tilemap_tiles); }

	void nr_70_layer2_resolution_w(u8 data) { m_nr_70_layer2_resolution = data; m_layer2->resolution_w(m_nr_70_layer2_resolution); }
	void nr_70_layer2_palette_offset_w(u8 data) { m_nr_70_layer2_palette_offset = data; m_layer2->palette_offset_w(m_nr_70_layer2_palette_offset); }
	void nr_71_layer2_scrollx_msb_w(bool data) { m_nr_71_layer2_scrollx_msb = data; m_layer2->scroll_x_w((m_nr_71_layer2_scrollx_msb << 8) | m_nr_16_layer2_scrollx); }

	bool nr_8c_altrom_en() const { return BIT(m_nr_8c_altrom, 7); }
	bool nr_8c_altrom_rw() const { return BIT(m_nr_8c_altrom, 6); }
	bool nr_8c_altrom_lock_rom1() const { return BIT(m_nr_8c_altrom, 5); }
	bool nr_8c_altrom_lock_rom0() const { return BIT(m_nr_8c_altrom, 4); }

	bool nr_8f_mapping_mode_profi() const { return 0; }
	bool nr_8f_mapping_mode_pentagon() const { return m_nr_8f_mapping_mode == 0b10 || nr_8f_mapping_mode_pentagon_1024_en(); }
	bool nr_8f_mapping_mode_pentagon_1024() const { return m_nr_8f_mapping_mode == 0b11; }
	bool nr_8f_mapping_mode_pentagon_1024_en() const { return nr_8f_mapping_mode_pentagon_1024() && BIT(~m_port_eff7_data, 2); }

	void nr_c0_im2_vector_w(u8 data) { m_nr_c0_im2_vector = data; m_ctc->write(0, m_nr_c0_im2_vector << 5); }

	u32 internal_port_enable() const;
	bool port_ff_io_en() const { return BIT(internal_port_enable(), 0); }
	bool port_7ffd_io_en() const { return BIT(internal_port_enable(), 1); }
	bool port_dffd_io_en() const { return BIT(internal_port_enable(), 2); }
	bool port_1ffd_io_en() const { return BIT(internal_port_enable(), 3); }
	bool port_p3_floating_bus_io_en() const { return BIT(internal_port_enable(), 4); }
	bool port_dma_6b_io_en() const { return BIT(internal_port_enable(), 5); }
	bool port_1f_io_en() const { return BIT(internal_port_enable(), 6); }
	bool port_37_io_en() const { return BIT(internal_port_enable(), 7); }
	bool port_divmmc_io_en() const { return BIT(internal_port_enable(), 8); }
	bool port_multiface_io_en() const { return BIT(internal_port_enable(), 9); }
	bool port_i2c_io_en() const { return BIT(internal_port_enable(), 10); }
	bool port_spi_io_en() const { return BIT(internal_port_enable(), 11); }
	bool port_uart_io_en() const { return BIT(internal_port_enable(), 12); }
	bool port_mouse_io_en() const { return BIT(internal_port_enable(), 13); }
	bool port_sprite_io_en() const { return BIT(internal_port_enable(), 14); }
	bool port_layer2_io_en() const { return BIT(internal_port_enable(), 15); }
	bool port_ay_io_en() const { return BIT(internal_port_enable(), 16); }
	bool port_dac_sd1_ABCD_1f0f4f5f_io_en() const { return BIT(internal_port_enable(), 17); }
	bool port_dac_sd2_ABCD_f1f3f9fb_io_en() const { return BIT(internal_port_enable(), 18); }
	bool port_dac_stereo_AD_3f5f_io_en() const { return BIT(internal_port_enable(), 19); }
	bool port_dac_stereo_BC_0f4f_io_en() const { return BIT(internal_port_enable(), 20); }
	bool port_dac_mono_AD_fb_io_en() const { return BIT(internal_port_enable(), 21); }
	bool port_dac_mono_BC_b3_io_en() const { return BIT(internal_port_enable(), 22); }
	bool port_dac_mono_AD_df_io_en() const { return BIT(internal_port_enable(), 23); }
	bool port_ulap_io_en() const { return BIT(internal_port_enable(), 24); }
	bool port_dma_0b_io_en() const { return BIT(internal_port_enable(), 25); }
	bool port_eff7_io_en() const { return BIT(internal_port_enable(), 26); }
	bool port_ctc_io_en() const { return BIT(internal_port_enable(), 27); }

	u8 port_7ffd_bank() const { return (((nr_8f_mapping_mode_pentagon() || nr_8f_mapping_mode_profi()) ? 0 : BIT(m_port_dffd_data, 3)) << 6) | ((!nr_8f_mapping_mode_pentagon() ? BIT(m_port_dffd_data, 2) : (nr_8f_mapping_mode_pentagon_1024_en() && BIT(m_port_7ffd_data, 5))) << 5) | ((nr_8f_mapping_mode_pentagon() ? BIT(m_port_7ffd_data, 6, 2) : (m_port_dffd_data & 3)) << 3) | (m_port_7ffd_data & 7); }
	bool port_7ffd_shadow() const { return BIT(m_port_7ffd_data, 3); }
	bool port_7ffd_locked() const { return (nr_8f_mapping_mode_pentagon_1024_en() || (nr_8f_mapping_mode_profi() && BIT(m_port_dffd_data, 4))) ? 0 : BIT(m_port_7ffd_data, 5); }
	bool port_1ffd_special() const { return BIT(m_port_1ffd_data, 0); }
	u8 port_1ffd_rom() const { return (BIT(m_port_1ffd_data, 2) << 1) | BIT(m_port_7ffd_data, 4); }
	void port_7ffd_reg_w(u8 data);
	bool port_ff_interrupt_disable() { return BIT(m_port_ff_data, 6); }

	void port_123b_layer2_en_w(bool data) { m_screen->update_now(); m_port_123b_layer2_en = data; m_layer2->layer2_en_w(m_port_123b_layer2_en); }

	void port_ff3b_ulap_en_w(bool data) { m_port_ff3b_ulap_en = data; m_ula_scr->ulap_en_w(m_port_ff3b_ulap_en); m_lores->ulap_en_w(m_port_ff3b_ulap_en && !m_nr_43_ulanext_en); }
	u16 nr_palette_dat();

	void port_e3_reg_w(u8 data);
	void port_e7_reg_w(u8 data);

	memory_access<8, 0, 0, ENDIANNESS_LITTLE>::specific m_next_regs;
	memory_view m_io_shadow_view;
	memory_bank_creator m_bank_boot_rom;
	memory_bank_array_creator<8> m_bank_ram;
	memory_view m_view0, m_view1, m_view2, m_view3, m_view4, m_view5, m_view6, m_view7;
	required_device<specnext_copper_device> m_copper;
	required_device<specnext_ctc_device> m_ctc;
	required_device<specnext_dma_device> m_dma;
	optional_device<i2c_ds1307_device> m_i2c;
	required_device<spi_sdcard_device> m_sdcard;
	required_device_array<ym2149_device, 3> m_ay;
	required_device_array<dac_byte_interface, 4> m_dac;
	required_device<device_palette_interface> m_palette;
	required_device<address_map_bank_device> m_regs_map;
	required_device<specnext_multiface_device> m_mf;
	required_device<specnext_divmmc_device> m_divmmc;
	required_device<screen_ula_device> m_ula_scr;
	required_device<specnext_tiles_device> m_tiles;
	required_device<specnext_layer2_device> m_layer2;
	required_device<specnext_lores_device> m_lores;
	required_device<specnext_sprites_device> m_sprites;
	required_ioport m_io_issue;
	optional_ioport m_io_video;
	required_ioport_array<3> m_io_mouse;

	video_timings_info m_video_timings;
	rectangle m_clip256x192;
	rectangle m_clip320x256;
	int m_page_shadow[8];
	bool m_bootrom_en;
	u8 m_port_ff_data;
	bool m_port_1ffd_special_old;
	u8 m_port_1ffd_data;
	u8 m_port_7ffd_data;
	u8 m_port_dffd_data;
	u8 m_port_eff7_data;
	u8 m_port_e7_reg;
	u8 m_nr_register;
	u8 m_port_e3_reg;
	bool m_divmmc_delayed_check;
	u16 m_global_transparent;

	u8 m_sram_rom;
	bool m_sram_rom3;
	bool m_sram_alt_128_n;

	u8 m_mmu[8];
	bool m_nr_02_bus_reset;
	bool m_nr_02_generate_mf_nmi;
	bool m_nr_02_generate_divmmc_nmi;
	bool m_nr_02_hard_reset;
	u8 m_nr_02_reset_type; // u3
	u8 m_nr_03_machine_type; // u3
	bool m_nr_03_user_dt_lock;
	u8 m_nr_03_machine_timing; // u3
	bool m_nr_03_config_mode;
	u8 m_nr_04_romram_bank; // u7
	u8 m_nr_05_joy1; // u2
	u8 m_nr_05_joy0; // u2
	bool m_nr_05_5060;
	u8 m_nr_06_psg_mode; // u2
	bool m_nr_06_ps2_mode;
	bool m_nr_06_button_m1_nmi_en;
	bool m_nr_06_button_drive_nmi_en;
	bool m_nr_06_hotkey_5060_en;
	bool m_nr_06_internal_speaker_beep;
	bool m_nr_06_hotkey_cpu_speed_en;
	u8 m_nr_07_cpu_speed; // u2
	bool m_nr_08_keyboard_issue2;
	bool m_nr_08_psg_turbosound_en;
	bool m_nr_08_port_ff_rd_en;
	bool m_nr_08_dac_en;
	bool m_nr_08_internal_speaker_en;
	bool m_nr_08_psg_stereo_mode;
	bool m_nr_08_contention_disable;
	bool m_nr_09_hdmi_audio_en;
	bool m_nr_09_sprite_tie;
	u8 m_nr_09_psg_mono; // u3
	u8 m_nr_0a_mouse_dpi; // u2
	bool m_nr_0a_mouse_button_reverse;
	bool m_nr_0a_divmmc_automap_en;
	u8 m_nr_0a_mf_type; // u2
	bool m_nr_0b_joy_iomode_0;
	u8 m_nr_0b_joy_iomode; // u2
	bool m_nr_0b_joy_iomode_en;
	u8 m_nr_10_coreid; // u5
	u8 m_nr_11_video_timing; // u3
	bool m_nr_10_flashboot;
	u8 m_nr_12_layer2_active_bank; // u7
	u8 m_nr_13_layer2_shadow_bank; // u7
	u8 m_nr_14_global_transparent_rgb;
	bool m_nr_15_sprite_en;
	bool m_nr_15_sprite_over_border_en;
	u8 m_nr_15_layer_priority; // u3
	bool m_nr_15_sprite_border_clip_en;
	bool m_nr_15_sprite_priority;
	bool m_nr_15_lores_en;
	u8 m_nr_16_layer2_scrollx;
	u8 m_nr_17_layer2_scrolly;
	u8 m_nr_18_layer2_clip_x1;
	u8 m_nr_18_layer2_clip_x2;
	u8 m_nr_18_layer2_clip_y1;
	u8 m_nr_18_layer2_clip_y2;
	u8 m_nr_18_layer2_clip_idx; // u2
	u8 m_nr_19_sprite_clip_x1;
	u8 m_nr_19_sprite_clip_x2;
	u8 m_nr_19_sprite_clip_y1;
	u8 m_nr_19_sprite_clip_y2;
	u8 m_nr_19_sprite_clip_idx; // u2
	u8 m_nr_1a_ula_clip_x1;
	u8 m_nr_1a_ula_clip_x2;
	u8 m_nr_1a_ula_clip_y1;
	u8 m_nr_1a_ula_clip_y2;
	u8 m_nr_1a_ula_clip_idx; // u2
	u8 m_nr_1b_tm_clip_x1;
	u8 m_nr_1b_tm_clip_x2;
	u8 m_nr_1b_tm_clip_y1;
	u8 m_nr_1b_tm_clip_y2;
	u8 m_nr_1b_tm_clip_idx; // u2
	bool m_nr_22_line_interrupt_en;
	u16 m_nr_23_line_interrupt; // u9
	u8 m_nr_26_ula_scrollx;
	u8 m_nr_27_ula_scrolly;
	u8 m_nr_2d_i2s_sample; // u2
	u16 m_nr_30_tm_scrollx; // u10
	u8 m_nr_31_tm_scrolly;
	u8 m_nr_32_lores_scrollx;
	u8 m_nr_33_lores_scrolly;
	u8 m_nr_palette_idx;
	bool m_nr_palette_sub_idx;
	u8 m_nr_42_ulanext_format;
	bool m_nr_43_palette_autoinc_disable;
	u8 m_nr_43_palette_write_select; // u3
	bool m_nr_43_active_sprite_palette;
	bool m_nr_43_active_layer2_palette;
	bool m_nr_43_active_ula_palette;
	bool m_nr_43_ulanext_en;
	u8 m_nr_stored_palette_value;
	u8 m_nr_4a_fallback_rgb;
	u8 m_nr_4b_sprite_transparent_index;
	u8 m_nr_4c_tm_transparent_index; // u4
	u16 m_nr_copper_addr; // u11
	u8 m_nr_copper_data_stored;
	u8 m_nr_62_copper_mode; // u2
	u8 m_nr_64_copper_offset;
	bool m_nr_68_ula_en;
	u8 m_nr_68_blend_mode; // u2
	bool m_nr_68_cancel_extended_keys;
	bool m_nr_68_ula_fine_scroll_x;
	bool m_nr_68_ula_stencil_mode;
	bool m_nr_6a_lores_radastan;
	bool m_nr_6a_lores_radastan_xor;
	u8 m_nr_6a_lores_palette_offset; // u4
	bool m_nr_6b_tm_en;
	u8 m_nr_6b_tm_control; // u7
	u8 m_nr_6c_tm_default_attr;
	u8 m_nr_6e_tilemap_base; // u6
	bool m_nr_6e_tilemap_base_7;
	u8 m_nr_6f_tilemap_tiles; // u6
	bool m_nr_6f_tilemap_tiles_7;
	u8 m_nr_70_layer2_resolution; // u2
	u8 m_nr_70_layer2_palette_offset; // u4
	bool m_nr_71_layer2_scrollx_msb;
	u8 m_nr_7f_user_register_0;
	u8 m_nr_80_expbus;
	u8 m_nr_81_expbus_speed; // u2
	bool m_nr_81_expbus_clken;
	bool m_nr_81_expbus_nmi_debounce_disable;
	bool m_nr_81_expbus_ula_override;
	u8 m_nr_82_internal_port_enable;
	u8 m_nr_83_internal_port_enable;
	u8 m_nr_84_internal_port_enable;
	u8 m_nr_85_internal_port_enable; // u4
	bool m_nr_85_internal_port_reset_type;
	u8 m_nr_86_bus_port_enable;
	u8 m_nr_87_bus_port_enable;
	u8 m_nr_88_bus_port_enable;
	u8 m_nr_89_bus_port_enable; // u4
	bool m_nr_89_bus_port_reset_type;
	u8 m_nr_8a_bus_port_propagate; // u6
	u8 m_nr_8c_altrom;
	u8 m_nr_8f_mapping_mode; // u2
	u8 m_nr_90_pi_gpio_o_en;
	u8 m_nr_91_pi_gpio_o_en;
	u8 m_nr_92_pi_gpio_o_en;
	u8 m_nr_93_pi_gpio_o_en; // u4
	u8 m_nr_98_pi_gpio_o;
	u8 m_nr_99_pi_gpio_o;
	u8 m_nr_9a_pi_gpio_o;
	u8 m_nr_9b_pi_gpio_o; // u4
	u8 m_nr_a0_pi_peripheral_en;
	u8 m_nr_a2_pi_i2s_ctl;
	bool m_nr_a8_esp_gpio0_en;
	bool m_nr_a9_esp_gpio0;
	u8 m_nr_b8_divmmc_ep_0;
	u8 m_nr_b9_divmmc_ep_valid_0;
	u8 m_nr_ba_divmmc_ep_timing_0;
	u8 m_nr_bb_divmmc_ep_1;
	u8 m_nr_c0_im2_vector; // u3
	bool m_nr_c0_stackless_nmi;
	bool m_nr_c0_int_mode_pulse_0_im2_1;
	u8 m_nr_c2_retn_address_lsb;
	u8 m_nr_c3_retn_address_msb;
	bool m_nr_c4_int_en_0_expbus;
	u8 m_nr_c6_int_en_2_654; // u3
	u8 m_nr_c6_int_en_2_210; // u3
	bool m_nr_cc_dma_int_en_0_7;
	u8 m_nr_cc_dma_int_en_0_10; // u2
	u8 m_nr_cd_dma_int_en_1;
	u8 m_nr_ce_dma_int_en_2_654; // u3
	u8 m_nr_ce_dma_int_en_2_210; // u3
	bool m_nr_d8_io_trap_fdc_en;
	u8 m_nr_d9_iotrap_write;
	u8 m_nr_da_iotrap_cause; // u2
	bool m_nr_f0_select;
	bool m_nr_f0_xdna_en;
	bool m_nr_f0_xadc_en;
	u8 m_nr_f0_xdev_cmd;
	bool m_nr_f0_xadc_eoc;
	bool m_nr_f0_xadc_eos;
	bool m_nr_f8_xadc_dwe;
	u8 m_nr_f8_xadc_daddr; // u7
	bool m_nr_f8_xadc_den;
	u8 m_nr_f9_xadc_d0;
	u8 m_nr_fa_xadc_d1;

	bool m_pulse_int_n;
	u8 m_nr_09_scanlines; // u2

	u8 m_eff_nr_03_machine_timing; // u3
	bool m_eff_nr_05_5060;
	bool m_eff_nr_05_scandouble_en;
	bool m_eff_nr_08_contention_disable;
	u8 m_eff_nr_09_scanlines; // u2

	bool m_port_123b_layer2_en;
	bool m_port_123b_layer2_map_wr_en;
	bool m_port_123b_layer2_map_rd_en;
	bool m_port_123b_layer2_map_shadow;
	u8 m_port_123b_layer2_map_segment; // u2
	u8 m_port_123b_layer2_offset; // u3

	u8 m_port_bf3b_ulap_mode; // u2
	u8 m_port_bf3b_ulap_index; // u6
	bool m_port_ff3b_ulap_en;

	u8 m_ay_select; // u2

	emu_timer *m_irq_line_timer;
	emu_timer *m_spi_clock;
	int m_spi_clock_cycles;
	bool m_spi_clock_state;
	u8 m_spi_mosi_dat;
	u8 m_spi_miso_dat;
	bool m_i2c_scl_data;
	bool m_i2c_sda_data;

	u16 m_irq_mask;
};

void specnext_state::bank_update(u8 bank, u8 count)
{
	for (auto b = bank; count; ++b, --count)
		bank_update(b);
}

void specnext_state::bank_update(u8 bank)
{
	using views_link = std::reference_wrapper<memory_view>;
	views_link views[] = { m_view0, m_view1, m_view2, m_view3, m_view4, m_view5, m_view6, m_view7 };

	const bool is_rom = (bank >> 1) == 0;
	if (m_bootrom_en && is_rom)
		return views[bank].get().select(1);

	if (machine_type_48())
	{
		m_sram_rom = 0b00;
		m_sram_rom3 = 1;
		m_sram_alt_128_n = !(!nr_8c_altrom_lock_rom1() && nr_8c_altrom_lock_rom0());
	}
	else if (machine_type_p3())
	{
		if (nr_8c_altrom_lock_rom1() || nr_8c_altrom_lock_rom0())
		{
			m_sram_rom = (nr_8c_altrom_lock_rom1() << 1) | nr_8c_altrom_lock_rom0();
			m_sram_rom3 = nr_8c_altrom_lock_rom1() && nr_8c_altrom_lock_rom0();
			m_sram_alt_128_n = nr_8c_altrom_lock_rom1();
		}
		else
		{
			m_sram_rom = port_1ffd_rom();
			m_sram_rom3 = port_1ffd_rom() == 0x03;
			m_sram_alt_128_n = port_1ffd_rom() & 1;
		}
	}
	else
	{
		if (nr_8c_altrom_lock_rom1() || nr_8c_altrom_lock_rom0())
		{
			m_sram_rom = nr_8c_altrom_lock_rom1();
			m_sram_rom3 = nr_8c_altrom_lock_rom1();
			m_sram_alt_128_n = nr_8c_altrom_lock_rom1();
		}
		else
		{
			m_sram_rom = port_1ffd_rom() & 1;
			m_sram_rom3 = port_1ffd_rom() & 1;
			m_sram_alt_128_n = port_1ffd_rom() & 1;
		}
	}

	const u8 mem_active_page = m_mmu[bank];
	const bool mem_active_bank5 = mem_active_page == 0x0a || mem_active_page == 0x0b;
	const bool mem_active_bank7 = mem_active_page == 0x0e;
	const u16 mmu_A21_A13 = ((0b0001 + BIT(mem_active_page, 5, 3)) << 5) | BIT(mem_active_page, 0, 5);

	const u8 layer2_active_bank_offset_pre = m_port_123b_layer2_map_segment == 0b11 ? BIT(bank, 1, 2) : m_port_123b_layer2_map_segment;
	const u8 layer2_active_bank_offset = layer2_active_bank_offset_pre + m_port_123b_layer2_offset;
	const u8 layer2_active_bank = m_port_123b_layer2_map_shadow ? m_nr_13_layer2_shadow_bank : m_nr_12_layer2_active_bank;
	const u8 layer2_active_page = ((layer2_active_bank + layer2_active_bank_offset) << 1) | BIT(bank, 0);
	const u16 layer2_A21_A13 = ((0b0001 + BIT(layer2_active_page, 5, 3)) << 5) | BIT(layer2_active_page, 0, 5);

	// sram_pre_romcs_replace = expbus_romcs_replace;
	const bool sram_pre_alt_en = nr_8c_altrom_en();
	const bool sram_pre_alt_128_n = m_sram_alt_128_n;
	const bool sram_pre_rom3 = m_sram_rom3;
	const bool sram_pre_layer2_rd_en = m_port_123b_layer2_map_rd_en;
	const bool sram_pre_layer2_wr_en = m_port_123b_layer2_map_wr_en;
	const u16 sram_pre_layer2_A21_A13 = layer2_A21_A13;

	u8 sram_pre_A20_A13, sram_pre_override; // u3: divmmc & layer 2 & romcs
	bool sram_pre_active, sram_pre_bank5, sram_pre_bank7, sram_pre_rdonly;
	if (is_rom) // 0-1
	{
		const bool cpu_a13 = bank & 1;
		m_mf->enable_w(port_multiface_io_en());
		if (m_mf->mf_enabled_r())
		{
			sram_pre_A20_A13 = 0b00001010 | cpu_a13;
			sram_pre_active = 1;
			sram_pre_bank5 = sram_pre_bank7 = 0;
			sram_pre_rdonly = !cpu_a13;
			sram_pre_override = 0b000;
		}
		else if (BIT(~mmu_A21_A13, 8))
		{
			sram_pre_A20_A13 = mmu_A21_A13 & 0xff;
			sram_pre_active = !mem_active_bank5 && !mem_active_bank7;
			sram_pre_bank5 = mem_active_bank5;
			sram_pre_bank7 = mem_active_bank7;
			sram_pre_rdonly = 0;
			sram_pre_override = 0b110;
		}
		else if (m_nr_03_config_mode)
		{
			sram_pre_A20_A13 = (m_nr_04_romram_bank << 1) | cpu_a13;
			sram_pre_active = 1;
			sram_pre_bank5 = sram_pre_bank7 = 0;
			sram_pre_rdonly = 0;
			sram_pre_override = 0b110;
		}
		else
		{
			sram_pre_A20_A13 = (m_sram_rom << 1) | cpu_a13;
			sram_pre_active = 1;
			sram_pre_bank5 = sram_pre_bank7 = 0;
			sram_pre_rdonly = !(nr_8c_altrom_en() && nr_8c_altrom_rw());
			sram_pre_override = 0b111;
		}
	}
	else
	{
		sram_pre_A20_A13 = mmu_A21_A13 & 0xff;
		sram_pre_active = BIT(~mmu_A21_A13, 8) && !mem_active_bank5 && !mem_active_bank7;
		sram_pre_bank5 = mem_active_bank5;
		sram_pre_bank7 = mem_active_bank7;
		sram_pre_rdonly = 0;
		sram_pre_override = (((bank & 0x06) != 0x06) && (m_port_123b_layer2_map_segment == 0x03)) << 1;
	}

	const bool sram_pre_romcs_n = 0; // expbus_eff_en && !expbus_eff_disable_mem;
	const bool sram_romcs = BIT(sram_pre_override, 0) && sram_pre_romcs_n;
	const bool sram_divmmc_automap_en = BIT(sram_pre_override, 2);

	m_divmmc->cpu_a_15_13_w(bank);
	m_divmmc->en_w(port_divmmc_io_en());
	m_divmmc->automap_reset_w(!port_divmmc_io_en() || !m_nr_0a_divmmc_automap_en);
	m_divmmc->automap_active_w(sram_divmmc_automap_en);
	m_divmmc->retn_seen_w(0);
	m_divmmc->divmmc_button_w(m_nr_02_generate_divmmc_nmi);

	for (s8 cpu_rd_n = 1; cpu_rd_n >= 0; --cpu_rd_n) // check W then R
	{
		const bool sram_layer2_map_en = BIT(sram_pre_override, 1) && ((sram_pre_layer2_wr_en && cpu_rd_n) || (sram_pre_layer2_rd_en && !cpu_rd_n));
		const bool sram_altrom_en = !(BIT(~sram_pre_override, 0) || !sram_pre_alt_en || (sram_pre_rdonly && cpu_rd_n) || (!sram_pre_rdonly && !cpu_rd_n));
		const bool sram_divmmc_automap_rom3_en = ((sram_pre_override & 0x05) == 0x05) && !sram_layer2_map_en && !sram_romcs && ((sram_altrom_en && sram_pre_alt_128_n) || (sram_pre_rom3 && !sram_altrom_en));

		m_divmmc->automap_rom3_active_w(sram_divmmc_automap_rom3_en);
		m_divmmc->clock_w();

		u8 sram_A20_A13;
		bool sram_active, sram_bank5, sram_bank7, sram_rdonly, sram_romcs_en, sram_mem_hide_n;
		if (BIT(sram_pre_override, 2) && m_divmmc->divmmc_rom_en_r())
		{
			sram_A20_A13 = 0b00001000;
			sram_active = 1;
			sram_bank5 = sram_bank7 = 0;
			sram_rdonly = 1;
			sram_romcs_en = 0;
			sram_mem_hide_n = 0;
		}
		else if (BIT(sram_pre_override, 2) && m_divmmc->divmmc_ram_en_r())
		{
			sram_A20_A13 = 0b00010000 | m_divmmc->divmmc_ram_bank_r();
			sram_active = 1;
			sram_bank5 = sram_bank7 = 0;
			sram_rdonly = m_divmmc->divmmc_rdonly_r();
			sram_romcs_en = 0;
			sram_mem_hide_n = 0;
		}
		else if (sram_layer2_map_en)
		{
			sram_A20_A13 = sram_pre_layer2_A21_A13 & 0xff;
			sram_active = BIT(~sram_pre_layer2_A21_A13, 8);
			sram_bank5 = sram_bank7 = 0;
			sram_rdonly = 0;
			sram_romcs_en = 0;
			sram_mem_hide_n = 0;
		}
		else if (sram_romcs)
		{
			sram_A20_A13 = 0b00011110 | BIT(sram_pre_A20_A13, 0);
			sram_active = 1;
			sram_bank5 = sram_bank7 = 0;
			sram_rdonly = 1;
			sram_romcs_en = 1;
			sram_mem_hide_n = 1;
		}
		else if (sram_altrom_en)
		{
			sram_A20_A13 = 0b00001100 | (sram_pre_alt_128_n << 1) | BIT(sram_pre_A20_A13, 0);
			sram_active = 1;
			sram_bank5 = sram_bank7 = 0;
			sram_rdonly = sram_pre_rdonly;
			sram_romcs_en = 0;
			sram_mem_hide_n = 1;
		}
		else
		{
			sram_A20_A13 = sram_pre_A20_A13;
			sram_active = sram_pre_active;
			sram_bank5 = sram_pre_bank5;
			sram_bank7 = sram_pre_bank7;
			sram_rdonly = sram_pre_rdonly;
			sram_romcs_en = 0;
			sram_mem_hide_n = BIT(sram_pre_override, 0);
		}

		if (false) // unused in current implementation, makes compiler happy
			printf("%d", sram_active + sram_bank5 + sram_romcs_en + sram_mem_hide_n);

		if (cpu_rd_n)
		{
			m_page_shadow[bank] = sram_rdonly ? ~0 : sram_A20_A13;
		}
		else
		{
			m_bank_ram[bank]->set_entry(sram_A20_A13);
			if (sram_rdonly || (m_page_shadow[bank] != sram_A20_A13))
			{
				views[bank].get().select(0);
				LOGMEM("ROM%d = %x\n", bank, sram_A20_A13);
			}
			else
			{
				if (m_page_shadow[bank] == sram_A20_A13)
					m_page_shadow[bank] = ~0;
				views[bank].get().disable();
				LOGMEM("RAM%d = %x\n", bank, sram_A20_A13);
			}
		}
	}
}

void specnext_state::memory_change(u16 port, u8 data) // port_memory_change_dly
{
	if (port_1ffd_special())
	{
		u8 b3 = (BIT(m_port_1ffd_data, 2) || BIT(m_port_1ffd_data, 1)) << 3;
		mmu_x2_w(0, b3 | 0b000);
		const u8 b2 = (BIT(m_port_1ffd_data, 2) && BIT(m_port_1ffd_data, 1)) << 2;
		mmu_x2_w(2, b3 | b2 | 0b10);
		mmu_x2_w(4, b3 | 0b100);
		b3 = (BIT(~m_port_1ffd_data, 2) && BIT(m_port_1ffd_data, 1)) << 3;
		mmu_x2_w(6, b3 | 0b110);
	}
	else
	{
		const bool mode_profi = nr_8f_mapping_mode_profi();

		if (BIT(m_port_eff7_data, 3) || (mode_profi && BIT(m_port_dffd_data, 4)))
			mmu_x2_w(0, 0x00);
		else
			mmu_x2_w(0, 0xff);

		if (mode_profi && BIT(m_port_dffd_data, 3))
			mmu_x2_w(2, (port_7ffd_bank() << 1) | 0);
		else if (mode_profi || m_port_1ffd_special_old)
			mmu_x2_w(2, 0x0a);

		if (mode_profi && BIT(m_port_dffd_data, 6))
			mmu_x2_w(4, 0x0c);
		else if (mode_profi || m_port_1ffd_special_old)
			mmu_x2_w(4, 0x04);

		const bool port_memory_ram_change_dly = !(port == 0x8e && !BIT(data, 3));
		if (mode_profi && BIT(m_port_dffd_data, 3))
			mmu_x2_w(6, 0x0e);
		else if (m_port_1ffd_special_old || port_memory_ram_change_dly)
			mmu_x2_w(6, (port_7ffd_bank() << 1) | 0);
	}

	m_port_1ffd_special_old = port_1ffd_special();
}

void specnext_state::update_video_mode()
{
	std::string machine_name;
	if (BIT(m_nr_03_machine_timing, 2))
	{
		machine_name = "Pentagon";
		m_video_timings = // Pentagon is always 50 Hz
		{
			0, 448 + 3 - 12, 16, 47, 63, 128, 447,
			0, 319, 1, 14, 15, 80, 319,
			76, 435, 24, 311, 24 + 0
		};
		m_nr_05_5060 = 0;
	}
	else if (BIT(m_nr_03_machine_timing, 1))
	{
		machine_name = BIT(m_nr_03_machine_timing, 0) ? "+3" : "128K";
		if (!m_nr_05_5060)
			m_video_timings = // 128K 50 Hz
			{
				0, u16(!BIT(m_nr_03_machine_timing, 0) ? 136 + 4 - 12 /* 128 */ : 136 + 2 - 12 /* +3 */), 16, 47, 95, 136, 455,
				0, 1, 1, 4, 7, 64, 310,
				88, 447, 16, 303, 16 + 4
			};
		else
			m_video_timings = // 128K 60 Hz
			{
				0, u16(!BIT(m_nr_03_machine_timing, 0) ? 136 + 4 - 12 /* 128 */ : 136 + 2 - 12 /* +3 */), 16, 47, 95, 136, 455,
				0, 0, 1, 4, 7, 40, 263,
				88, 447, 16, 255, 16 + 0
			};
	}
	else
	{
		machine_name = "48K";
		if (!m_nr_05_5060)
			m_video_timings = // 48K 50 Hz
			{
				0, 128 + 0 - 12, 16, 47, 95, 128, 447,
				0, 0, 1, 4, 7, 64, 311,
				80, 439, 16, 303, 16 + 4
			};
		else
			m_video_timings = // 48K 60 Hz
			{
				0, 128 + 0 - 12, 16, 47, 95, 128, 447,
				0, 0, 1, 4, 7, 40, 263,
				80, 439, 16, 255, 16 + 0
			};
	}

	const bool is_hdmi = ~m_io_video.read_safe(1) & 1;
	const int left = m_video_timings.min_hactive << 1;
	const int top = m_video_timings.min_vactive;
	const int width = (m_video_timings.max_hc + 1) << 1;
	const int height = m_video_timings.max_vc + 1;
	m_clip256x192 = rectangle(left, left + (256 << 1) - 1, top, top + 192 - 1);
	m_clip320x256 = rectangle(left - (32 << 1), left + ((256 + 32) << 1) - 1, top - 32, top + 192 + 32 - 1);

	m_screen->configure(width, height
		, is_hdmi
			? rectangle(m_video_timings.hdmi_xmin << 1, m_video_timings.hdmi_xmax << 1,m_video_timings.hdmi_ymin, m_video_timings.hdmi_ymax)
			: m_clip320x256
		, HZ_TO_ATTOSECONDS(28_MHz_XTAL / 2) * width * height);
	m_ula_scr->set_raster_offset(left, top);
	m_lores->set_raster_offset(left, top);
	m_tiles->set_raster_offset(left, top);
	m_layer2->set_raster_offset(left, top);
	m_sprites->set_raster_offset(left, top);

	m_eff_nr_03_machine_timing = m_nr_03_machine_timing;
	m_eff_nr_05_5060 = m_nr_05_5060;
	LOG("%s: %s %dHz", machine_name, is_hdmi ? "HDMI" : "VGA", m_nr_05_5060 ? 60 : 50);
}

u32 specnext_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	rectangle clip256x192 = m_clip256x192;
	clip256x192 &= cliprect;
	rectangle clip320x256 = m_clip320x256;
	clip320x256 &= cliprect;

	screen.priority().fill(0, cliprect);
	const bool flash = u64(screen.frame_number() / m_frame_invert_count) & 1;
	if (m_nr_15_layer_priority < 0b110)
	{
		// background
		if (m_nr_68_ula_en)
		{
			m_ula_scr->draw_border(bitmap, cliprect, m_port_fe_data & 0x07);
		}
		else {
			bitmap.fill(m_palette->pen_color(UTM_FALLBACK_PEN), cliprect);
		}

		static const u8 lcfg[][3] =
		{
			// tiles+ula priority; l2 prioryty; l2 mask
			// + l2 pushes priority colors to 8 (foreground)
			{ 1, 1, 0 }, // SLU
			{ 1, 8, 0 }, // LSU
			{ 1, 1, 1 }, // SUL
			{ 8, 8, 0 }, // LUS
			{ 8, 1, 8 }, // USL
			{ 8, 8, 8 }  // ULS
		};

		const u8 (&l)[3] = lcfg[m_nr_15_layer_priority];
		if (m_nr_6b_tm_en) m_tiles->draw(screen, bitmap, clip320x256, TILEMAP_DRAW_CATEGORY(1), l[0]);
		if (m_nr_68_ula_en && BIT(~m_nr_6b_tm_control, 3))
		{
			if (m_nr_15_lores_en) m_lores->draw(screen, bitmap, clip256x192, l[0]);
			else m_ula_scr->draw(screen, bitmap, clip256x192, flash, l[0]);
		}
		if (m_nr_6b_tm_en) m_tiles->draw(screen, bitmap, clip320x256, TILEMAP_DRAW_CATEGORY(2), l[0]);
		m_layer2->draw(screen, bitmap, clip320x256, l[1], l[2]);
	}
	else // colors mixing case
	{
		bitmap.fill(m_palette->pen_color(UTM_FALLBACK_PEN), cliprect);

		if (m_nr_68_blend_mode != 0b11)
		//if (m_nr_68_blend_mode == 0b00)
		{
			if (m_nr_68_ula_en && BIT(~m_nr_6b_tm_control, 3))
			{
				if (m_nr_15_lores_en) m_lores->draw(screen, bitmap, clip256x192, 1);
				else m_ula_scr->draw(screen, bitmap, clip256x192, flash, 1);
			}
			if (m_nr_6b_tm_en) m_tiles->draw(screen, bitmap, clip320x256, TILEMAP_DRAW_CATEGORY(1), 2);
			if (m_nr_6b_tm_en) m_tiles->draw(screen, bitmap, clip320x256, TILEMAP_DRAW_CATEGORY(2), 8);
		}
		/* TODO No tests for modes below yet
		else if (m_nr_68_blend_mode == 0b10)
		{
		}
		else if (m_nr_68_blend_mode == 0b11)
		{
		}
		*/
		else
		{
			if (m_nr_68_ula_en && BIT(~m_nr_6b_tm_control, 3))
			{
				if (m_nr_15_lores_en) m_lores->draw(screen, bitmap, clip256x192, 1);
				else m_ula_scr->draw(screen, bitmap, clip256x192, flash, 1);
			}
			if (m_nr_6b_tm_en) m_tiles->draw(screen, bitmap, clip320x256, TILEMAP_DRAW_CATEGORY(1), 2);
			if (m_nr_6b_tm_en) m_tiles->draw(screen, bitmap, clip320x256, TILEMAP_DRAW_CATEGORY(2), 2);
		}
		// mixes only to 1
		m_layer2->draw_mix(screen, bitmap, clip320x256, m_nr_15_layer_priority & 1);
	}
	// sprites below foreground
	if (m_nr_15_sprite_en) m_sprites->draw(screen, bitmap, clip320x256, GFX_PMASK_8);

	return 0;
}

u32 specnext_state::internal_port_enable() const
{
	u32 ports_en = (m_nr_85_internal_port_enable << 24) | (m_nr_84_internal_port_enable << 16) | (m_nr_83_internal_port_enable << 8) | (m_nr_82_internal_port_enable << 0);
	if (m_nr_80_expbus & 0x80)
	{
		ports_en &= (m_nr_89_bus_port_enable << 24) | (m_nr_88_bus_port_enable << 16) | (m_nr_87_bus_port_enable << 8) | (m_nr_86_bus_port_enable << 0);
	}
	return ports_en;
}

u16 specnext_state::nr_palette_dat()
{
	u16 nr_palette_index = (BIT(m_nr_43_palette_write_select, 1) << 9) | (BIT(m_nr_43_palette_write_select, 2) << 8) | m_nr_palette_idx;
	if ((BIT(m_nr_43_palette_write_select, 1) != BIT(m_nr_43_palette_write_select, 0)))
		nr_palette_index |= 0x400;

	rgb_t rgb = m_palette->pen_color(nr_palette_index);
	return ((rgb.r() >> 5) << 6) | ((rgb.g() >> 5) << 3) | ((rgb.b() >> 5) << 0);
}

void specnext_state::palette_val_w(u8 nr_palette_priority, u16 nr_palette_value)
{
	m_screen->update_now();
	u16 nr_palette_index = (BIT(m_nr_43_palette_write_select, 1) << 9) | (BIT(m_nr_43_palette_write_select, 2) << 8) | m_nr_palette_idx;
	if ((BIT(m_nr_43_palette_write_select, 1) xor BIT(m_nr_43_palette_write_select, 0)))
		nr_palette_index |= 0x400;

	m_palette->set_pen_color(nr_palette_index, rgbexpand<3,3,3>(nr_palette_value, 6, 3, 0));
	m_layer2->pen_priority_w(nr_palette_index, BIT(nr_palette_priority, 1));
}

u8 specnext_state::port_ff_r()
{
	return (m_nr_08_port_ff_rd_en && port_ff_io_en())
		? m_port_ff_data // ==port_ff_dat_tmx
		: floating_bus_r();
}

void specnext_state::port_ff_w(u8 data)
{
	m_port_ff_data = data; // ==port_ff_dat_tmx
	m_ula_scr->port_ff_reg_w(m_port_ff_data);
	nr_6a_lores_radastan_xor_w(m_nr_6a_lores_radastan_xor);

	// TODO confirm this
	u16 nr_palette_value = (data << 1) | BIT(data, 1) | BIT(data, 0);
	u16 nr_palette_index_utm = (BIT(m_nr_43_palette_write_select, 2) << 8) | (0b11 << 6) | m_port_bf3b_ulap_index;

	m_palette->set_pen_color(nr_palette_index_utm, rgbexpand<3,3,3>(nr_palette_value, 6, 3, 0));
}

void specnext_state::port_7ffd_reg_w(u8 data)
{
	m_port_7ffd_data = data;
	m_ula_scr->ula_shadow_en_w(port_7ffd_shadow());
}

void specnext_state::port_e3_reg_w(u8 data)
{
	m_port_e3_reg = data & ~0x30;
	m_divmmc->divmmc_reg_w(m_port_e3_reg & ~0x30);
	bank_update(0, 2);
}

void specnext_state::port_e7_reg_w(u8 data)
{
	if ((data & 3) == 0b10)
		m_port_e7_reg = 0xfe;
	else if ((data & 3) == 0x01)
		m_port_e7_reg = 0xfd;
	else if ((data == 0xfb) || (data == 0xf7))
		m_port_e7_reg = data;
	else if ((data == 0x7f) && (m_nr_03_config_mode || BIT(m_nr_02_reset_type, 2)))
		m_port_e7_reg = 0x7f;
	else
		m_port_e7_reg = 0xff;

	// bit 7 = fpga flash, bit 3 = rpi1, bit 2 = rpi0, bit 1 = sd1, bit 0 = sd0
	m_sdcard->spi_ss_w(BIT(~m_port_e7_reg, 0));
}

u8 specnext_state::spi_data_r()
{
	u8 din = m_spi_miso_dat;
	if (!machine().side_effects_disabled())
		spi_data_w(0xff);

	return din;
}

void specnext_state::spi_data_w(u8 data)
{
	m_spi_mosi_dat = data;
#if TIMINGS_PERFECT
	m_spi_clock_cycles = 8;
	m_spi_clock->adjust(m_maincpu->clocks_to_attotime(1) / 4, 0, m_maincpu->clocks_to_attotime(1) / 4);
#else
	for (u8 m = 0x80; m; m >>= 1)
	{
		m_sdcard->spi_mosi_w(m_spi_mosi_dat & m ? 1 : 0);
		m_sdcard->spi_clock_w(CLEAR_LINE);
		m_sdcard->spi_clock_w(ASSERT_LINE);
	}
#endif
}

void specnext_state::spi_miso_w(u8 data)
{
	m_spi_miso_dat <<= 1;
	m_spi_miso_dat |= data;
}

void specnext_state::i2c_scl_w(u8 data)
{
	if (port_i2c_io_en())
	{
		m_i2c_scl_data = data & 1;
		m_i2c->scl_write(m_i2c_scl_data);
	}
}

void specnext_state::turbosound_address_w(u8 data)
{
	if ((data & 0x9c) == 0x9c)
		m_ay_select = (data & 3) % 3;
	else
		m_ay[m_nr_08_psg_turbosound_en ? m_ay_select : 0]->address_w(data);
}

u8 specnext_state::mf_port_r(offs_t addr)
{
	if (!machine().side_effects_disabled())
	{
		const u8 port = addr & 0xff;
		u8 port_mf_enable_io_a = 0x3f;
		u8 port_mf_disable_io_a = 0xbf;
		if (m_nr_0a_mf_type & 2)
		{
			port_mf_enable_io_a = 0x9f;
			port_mf_disable_io_a = 0x1f;
		}
		else if (m_nr_0a_mf_type & 1)
		{
			port_mf_enable_io_a = 0xbf;
			port_mf_disable_io_a = 0x3f;
		}

		m_mf->port_mf_enable_rd_w(port_multiface_io_en() && (port == port_mf_enable_io_a));
		m_mf->port_mf_disable_rd_w(port_multiface_io_en() && (port == port_mf_disable_io_a));
		m_mf->port_mf_enable_wr_w(0);
		m_mf->port_mf_disable_wr_w(0);
		m_mf->clock_w();

		bank_update(0, 2);
	}

	u8 data;
	if (!m_mf->mf_port_en_r())
		data = 0x00;
	else if (m_nr_0a_mf_type != 0b00)
		data = (BIT(m_port_7ffd_data, 3) << 7) | 0x7f;
	else
	{
		switch (BIT(addr, 12, 4))
		{
			case 0b0001: data = m_port_1ffd_data; break;
			case 0b0111: data = m_port_7ffd_data; break;
			case 0b1101: data = m_port_dffd_data; break;
			case 0b1110: data = m_port_eff7_data & 0xc0; break;
			default: data = m_port_fe_data & 0x07;
		}
	}

	if (!machine().side_effects_disabled())
	{
		m_mf->port_mf_enable_rd_w(0);
		m_mf->port_mf_disable_rd_w(0);
	}
	return data;
}

void specnext_state::mf_port_w(offs_t addr, u8 data)
{
	const u8 port = addr & 0xff;
	u8 port_mf_enable_io_a = 0x3f;
	u8 port_mf_disable_io_a = 0xbf;
	if (m_nr_0a_mf_type & 2)
	{
		port_mf_enable_io_a = 0x9f;
		port_mf_disable_io_a = 0x1f;
	}
	else if (m_nr_0a_mf_type & 1)
	{
		port_mf_enable_io_a = 0xbf;
		port_mf_disable_io_a = 0x3f;
	}

	m_mf->port_mf_enable_rd_w(0);
	m_mf->port_mf_disable_rd_w(0);
	m_mf->port_mf_enable_wr_w(port_multiface_io_en() && (port == port_mf_enable_io_a));
	m_mf->port_mf_disable_wr_w(port_multiface_io_en() && (port == port_mf_disable_io_a));
	m_mf->clock_w();
	bank_update(0, 2);
	m_mf->port_mf_enable_rd_w(0);
	m_mf->port_mf_disable_rd_w(0);
}

attotime specnext_state::copper_until_pos_r(u16 pos)
{
	const u16 vcount = BIT(pos, 0, 9);
	const u16 hcount = ((BIT(pos, 9, 6) << 3) + (BIT(pos, 15) ? 12 : 0)) << 1;
	return ((vcount < m_screen->height()) && (hcount < m_screen->width()))
		? m_screen->time_until_pos(m_clip256x192.top() + vcount - m_nr_64_copper_offset, m_clip256x192.left() + hcount)
		: attotime::never;
}


TIMER_CALLBACK_MEMBER(specnext_state::spi_clock)
{
	if (m_spi_clock_cycles > 0)
	{
		if (m_spi_clock_state)
		{
			m_sdcard->spi_clock_w(ASSERT_LINE);
			m_spi_clock_cycles--;
		}
		else
		{
			m_sdcard->spi_mosi_w(BIT(m_spi_mosi_dat, m_spi_clock_cycles - 1));
			m_sdcard->spi_clock_w(CLEAR_LINE);
		}

		m_spi_clock_state = !m_spi_clock_state;
	}
	else
	{
		m_spi_clock_state = false;
		m_spi_clock->reset();
	}
}

void specnext_state::mmu_w(offs_t bank, u8 data)
{
	m_mmu[bank] = data;
	bank_update(bank);
}

void specnext_state::mmu_x2_w(offs_t bank, u8 data)
{
	mmu_w(bank, data);
	mmu_w(bank | 1, data == 0xff ? 0xff : (data | 1));
}

u8 specnext_state::dma_r(bool dma_mode)
{
	m_dma->dma_mode_w(dma_mode);
	return m_dma->read();
}

void specnext_state::dma_w(bool dma_mode, u8 data)
{
	m_dma->dma_mode_w(dma_mode);
	m_dma->write(data);
}

u8 specnext_state::dma_mreq_r(offs_t offset)
{
	if (m_nr_07_cpu_speed == 0b11)
	{
		m_dma->adjust_wait(1);
	}
	return m_program.read_byte(offset);
}

u8 specnext_state::reg_r(offs_t nr_register)
{
	u8 port_253b_dat;

	switch (nr_register)
	{
	case 0x00:
		port_253b_dat = g_machine_id();
		break;
	case 0x01:
		port_253b_dat = G_VERSION;
		break;
	case 0x02:
		port_253b_dat = (m_nr_02_bus_reset << 7) | (0b00 << 5) | (nr_02_iotrap() << 4) | (m_nr_02_generate_mf_nmi << 3) | (m_nr_02_generate_divmmc_nmi << 2) | BIT(m_nr_02_reset_type, 0, 2);
		break;
	case 0x03:
		port_253b_dat = (m_nr_palette_sub_idx << 7) | (0b00 << 5) | (m_nr_03_machine_timing << 4) | (m_nr_03_user_dt_lock << 3) | m_nr_03_machine_type;
		break;
	case 0x05:
		port_253b_dat = (BIT(m_nr_05_joy0, 0, 2) << 6) | (BIT(m_nr_05_joy1, 0, 2) << 4) | (BIT(m_nr_05_joy0, 2) << 3) | (m_eff_nr_05_5060 << 2) | (BIT(m_nr_05_joy1, 2) << 1) | m_eff_nr_05_scandouble_en;
		break;
	case 0x06:
		port_253b_dat = (m_nr_06_hotkey_cpu_speed_en << 7) | (m_nr_06_internal_speaker_beep << 6) | (m_nr_06_hotkey_5060_en << 5) | (m_nr_06_button_drive_nmi_en << 4) | (m_nr_06_button_m1_nmi_en << 3) | (m_nr_06_ps2_mode << 2) | m_nr_06_psg_mode;
		break;
	case 0x07:
		{
			u8 cpu_speed = 0;
			for (u8 clock_scale = BIT(u8(m_maincpu->clock_scale()), 0, 3) >> 1; clock_scale; clock_scale >>= 1, ++cpu_speed);
			port_253b_dat = (0b00 << 6) | (cpu_speed << 4) | (0b00 << 2) | m_nr_07_cpu_speed;
		}
		break;
	case 0x08:
		port_253b_dat = ((!port_7ffd_locked()) << 7) | (m_eff_nr_08_contention_disable << 6) | (m_nr_08_psg_stereo_mode << 5) | (m_nr_08_internal_speaker_en << 4) | (m_nr_08_dac_en << 3) | (m_nr_08_port_ff_rd_en << 2) | (m_nr_08_psg_turbosound_en << 1) | m_nr_08_keyboard_issue2;
		break;
	case 0x09:
		port_253b_dat = (m_nr_09_psg_mono  << 5) | (m_nr_09_sprite_tie << 4) | (0 << 3) | ((!m_nr_09_hdmi_audio_en) << 2) | m_nr_09_scanlines; // m_eff_nr_09_scanlines
		break;
	case 0x0a:
		port_253b_dat = (m_nr_0a_mf_type << 6) | (0 << 5) | (m_nr_0a_divmmc_automap_en << 4) | (m_nr_0a_mouse_button_reverse << 3) | (0 << 2) | m_nr_0a_mouse_dpi;
		break;
	case 0x0b:
		port_253b_dat = (m_nr_0b_joy_iomode_en << 7) | (0 << 6) | (m_nr_0b_joy_iomode << 4) | (0b000 << 1) | m_nr_0b_joy_iomode_0;
		break;
	case 0x0e:
		port_253b_dat = G_SUB_VERSION;
		break;
	case 0x0f:
		port_253b_dat = (0b0000 << 4) | g_board_issue();
		break;
	case 0x10:
		port_253b_dat = (0 << 7) | (m_nr_10_coreid << 2) | 0b00;// | i_SPKEY_BUTTONS(1 downto 0);
		break;
	case 0x11:
		port_253b_dat = (0b00000 << 3) | m_nr_11_video_timing;
		break;
	case 0x12:
		port_253b_dat = (0 << 7) | m_nr_12_layer2_active_bank;
		break;
	case 0x13:
		port_253b_dat = (0 << 7) | m_nr_13_layer2_shadow_bank;
		break;
	case 0x14:
		port_253b_dat = m_nr_14_global_transparent_rgb;
		break;
	case 0x15:
		port_253b_dat = (m_nr_15_lores_en << 7) | (m_nr_15_sprite_priority << 6) | (m_nr_15_sprite_border_clip_en << 5) | (m_nr_15_layer_priority << 2) | (m_nr_15_sprite_over_border_en << 1) | m_nr_15_sprite_en;
		break;
	case 0x16:
		port_253b_dat = m_nr_16_layer2_scrollx;
		break;
	case 0x17:
		port_253b_dat = m_nr_17_layer2_scrolly;
		break;
	case 0x18:
		switch (m_nr_18_layer2_clip_idx)
		{
		case 0b00:
			port_253b_dat = m_nr_18_layer2_clip_x1;
			break;
		case 0b01:
			port_253b_dat = m_nr_18_layer2_clip_x2;
			break;
		case 0b10:
			port_253b_dat = m_nr_18_layer2_clip_y1;
			break;
		default:
			port_253b_dat = m_nr_18_layer2_clip_y2;
		}
		break;
	case 0x19:
		switch (m_nr_19_sprite_clip_idx)
		{
		case 0b00:
			port_253b_dat = m_nr_19_sprite_clip_x1;
			break;
		case 0b01:
			port_253b_dat = m_nr_19_sprite_clip_x2;
			break;
		case 0b10:
			port_253b_dat = m_nr_19_sprite_clip_y1;
			break;
		default:
			port_253b_dat = m_nr_19_sprite_clip_y2;
		}
		break;
	case 0x1a:
		switch (m_nr_1a_ula_clip_idx)
		{
		case 0b00:
			port_253b_dat = m_nr_1a_ula_clip_x1;
			break;
		case 0b01:
			port_253b_dat = m_nr_1a_ula_clip_x2;
			break;
		case 0b10:
			port_253b_dat = m_nr_1a_ula_clip_y1;
			break;
		default:
			port_253b_dat = m_nr_1a_ula_clip_y2;
		}
		break;
	case 0x1b:
		switch (m_nr_1b_tm_clip_idx)
		{
		case 0b00:
			port_253b_dat = m_nr_1b_tm_clip_x1;
			break;
		case 0b01:
			port_253b_dat = m_nr_1b_tm_clip_x2;
			break;
		case 0b10:
			port_253b_dat = m_nr_1b_tm_clip_y1;
			break;
		default:
			port_253b_dat = m_nr_1b_tm_clip_y2;
		}
		break;
	case 0x1c:
		port_253b_dat = (m_nr_1b_tm_clip_idx << 6) | (m_nr_1a_ula_clip_idx << 4) | (m_nr_19_sprite_clip_idx << 2) | m_nr_18_layer2_clip_idx;
		break;
	case 0x1e:
		port_253b_dat = BIT((m_nr_64_copper_offset + m_screen->vpos() - m_clip256x192.top() + m_screen->height()) % m_screen->height(), 8);
		break;
	case 0x1f:
		port_253b_dat = ((m_nr_64_copper_offset + m_screen->vpos() - m_clip256x192.top() + m_screen->height()) % m_screen->height())  & 0xff;
		break;
	case 0x20:
		port_253b_dat = 0;//(BIT(im2_int_status, 0) <<) | (BIT(im2_int_status, 11) <<) | (0b00 <<) | BIT(im2_int_status, 3, 4);
		break;
	case 0x22:
		port_253b_dat = ((!m_pulse_int_n) << 7) | (0b0000 << 3) | (port_ff_interrupt_disable() << 2) | (m_nr_22_line_interrupt_en << 1) | BIT(m_nr_23_line_interrupt, 8);
		break;
	case 0x23:
		port_253b_dat = BIT(m_nr_23_line_interrupt, 0, 8);
		break;
	case 0x26:
		port_253b_dat = m_nr_26_ula_scrollx;
		break;
	case 0x27:
		port_253b_dat = m_nr_27_ula_scrolly;
		break;
	case 0x28:
		port_253b_dat = m_nr_stored_palette_value;
		break;
	case 0x2c:
		port_253b_dat = 0;//pi_audio_L(9 downto 2);
		//m_nr_2d_i2s_sample = pi_audio_L(1 downto 0);
		break;
	case 0x2d:
		port_253b_dat = (m_nr_2d_i2s_sample << 6) | 0b000000;
		break;
	case 0x2e:
		port_253b_dat = 0;//pi_audio_R(9 downto 2);
		//m_nr_2d_i2s_sample = pi_audio_R(1 downto 0);
		break;
	case 0x2f:
		port_253b_dat = (0b000000 << 2) | BIT(m_nr_30_tm_scrollx, 8, 2);
		break;
	case 0x30:
		port_253b_dat = BIT(m_nr_30_tm_scrollx, 0, 8);
		break;
	case 0x31:
		port_253b_dat = m_nr_31_tm_scrolly;
		break;
	case 0x32:
		port_253b_dat = m_nr_32_lores_scrollx;
		break;
	case 0x33:
		port_253b_dat = m_nr_33_lores_scrolly;
		break;
	case 0x34:
		port_253b_dat = m_sprites->mirror_num_r() & 0x7f;
		break;
	case 0x40:
		port_253b_dat = m_nr_palette_idx;
		break;
	case 0x41:
		port_253b_dat = BIT(nr_palette_dat(), 1, 8);
		break;
	case 0x42:
		port_253b_dat = m_nr_42_ulanext_format;
		break;
	case 0x43:
		port_253b_dat = (m_nr_43_palette_autoinc_disable << 7) | (m_nr_43_palette_write_select << 4) | (m_nr_43_active_sprite_palette << 3) | (m_nr_43_active_layer2_palette << 2) | (m_nr_43_active_ula_palette << 1) | m_nr_43_ulanext_en;
		break;
	case 0x44:
		port_253b_dat = (BIT(nr_palette_dat(), 9, 2) << 6) | (0b00000 << 1) | BIT(nr_palette_dat(), 0);
		break;
	case 0x4a:
		port_253b_dat = m_nr_4a_fallback_rgb;
		break;
	case 0x4b:
		port_253b_dat = m_nr_4b_sprite_transparent_index;
		break;
	case 0x4c:
		port_253b_dat = (0b0000 << 4) | m_nr_4c_tm_transparent_index;
		break;
	case 0x50: case 0x51: case 0x52: case 0x53:
	case 0x54: case 0x55: case 0x56: case 0x57:
		port_253b_dat = m_mmu[nr_register - 0x50];
		break;
	case 0x61:
		port_253b_dat = BIT(m_nr_copper_addr, 0, 8);
		break;
	case 0x62:
		port_253b_dat = (m_nr_62_copper_mode << 6) | (0b000 << 3) | BIT(m_nr_copper_addr, 8, 3);
		break;
	case 0x64:
		port_253b_dat = m_nr_64_copper_offset;
		break;
	case 0x68:
		port_253b_dat = ((!m_nr_68_ula_en) << 7) | (m_nr_68_blend_mode << 5) | (m_nr_68_cancel_extended_keys << 4) | (m_port_ff3b_ulap_en << 3) | (m_nr_68_ula_fine_scroll_x << 2) | (0 << 1) | m_nr_68_ula_stencil_mode;
		break;
	case 0x69:
		port_253b_dat = (m_port_123b_layer2_en << 7) | (port_7ffd_shadow() << 6) | BIT(m_port_ff_data, 0, 6);
		break;
	case 0x6a:
		port_253b_dat = (0b00 << 6) | (m_nr_6a_lores_radastan << 5) | (m_nr_6a_lores_radastan_xor << 4) | m_nr_6a_lores_palette_offset;
		break;
	case 0x6b:
		port_253b_dat = (m_nr_6b_tm_en << 7) | m_nr_6b_tm_control;
		break;
	case 0x6c:
		port_253b_dat = m_nr_6c_tm_default_attr;
		break;
	case 0x6e:
		port_253b_dat = (m_nr_6e_tilemap_base_7 << 7) | (0 << 6) | m_nr_6e_tilemap_base;
		break;
	case 0x6f:
		port_253b_dat = (m_nr_6f_tilemap_tiles_7 << 7) | (0 << 6) | m_nr_6f_tilemap_tiles;
		break;
	case 0x70:
		port_253b_dat = (0b00 << 6) | (m_nr_70_layer2_resolution << 4) | m_nr_70_layer2_palette_offset;
		break;
	case 0x71:
		port_253b_dat = (0b0000000 << 1) | m_nr_71_layer2_scrollx_msb;
		break;
	case 0x7f:
		port_253b_dat = m_nr_7f_user_register_0;
		break;
	case 0x80:
		port_253b_dat = m_nr_80_expbus;
		break;
	case 0x81:
		port_253b_dat = /*(i_BUS_ROMCS_n <<) |*/ (m_nr_81_expbus_ula_override << 6) | (m_nr_81_expbus_nmi_debounce_disable << 5) | (m_nr_81_expbus_clken << 4) | (0b00 << 2) | m_nr_81_expbus_speed;
		break;
	case 0x82:
		port_253b_dat = m_nr_82_internal_port_enable;
		break;
	case 0x83:
		port_253b_dat = m_nr_83_internal_port_enable;
		break;
	case 0x84:
		port_253b_dat = m_nr_84_internal_port_enable;
		break;
	case 0x85:
		port_253b_dat = (m_nr_85_internal_port_reset_type << 7) | (0b000 << 4) | m_nr_85_internal_port_enable;
		break;
	case 0x86:
		port_253b_dat = m_nr_86_bus_port_enable;
		break;
	case 0x87:
		port_253b_dat = m_nr_87_bus_port_enable;
		break;
	case 0x88:
		port_253b_dat = m_nr_88_bus_port_enable;
		break;
	case 0x89:
		port_253b_dat = (m_nr_89_bus_port_reset_type << 7) | (0b000 << 4) | m_nr_89_bus_port_enable;
		break;
	case 0x8a:
		port_253b_dat = (0b00 << 6) | m_nr_8a_bus_port_propagate;
		break;
	case 0x8c:
		port_253b_dat = m_nr_8c_altrom;
		break;
	case 0x8e:
		port_253b_dat = (BIT(m_port_dffd_data, 0) << 7) | (BIT(m_port_7ffd_data, 0, 3) << 4) | (1 << 3) | (BIT(m_port_1ffd_data, 0) << 2)
			| (BIT(m_port_1ffd_data, 2) << 1) | ((BIT(m_port_7ffd_data, 4) && !BIT(m_port_1ffd_data, 0)) || (BIT(m_port_1ffd_data, 1) && BIT(m_port_1ffd_data, 0)));
		break;
	case 0x8f:
		port_253b_dat = (0b000000 << 2) | m_nr_8f_mapping_mode;
		break;
	case 0x90:
		port_253b_dat = m_nr_90_pi_gpio_o_en;
		break;
	case 0x91:
		port_253b_dat = m_nr_91_pi_gpio_o_en;
		break;
	case 0x92:
		port_253b_dat = m_nr_92_pi_gpio_o_en;
		break;
	case 0x93:
		port_253b_dat = (0b0000 << 4) | m_nr_93_pi_gpio_o_en;
		break;
	case 0x98:
		port_253b_dat = 0;//i_GPIO(7 downto 0);
		break;
	case 0x99:
		port_253b_dat = 0;//i_GPIO(15 downto 8);
		break;
	case 0x9a:
		port_253b_dat = 0;//i_GPIO(23 downto 16);
		break;
	case 0x9b:
		port_253b_dat = (0b0000 << 4);// | i_GPIO(27 downto 24);
		break;
	case 0xa0:
		port_253b_dat = (0b00 << 6) | (BIT(m_nr_a0_pi_peripheral_en, 3, 3) << 2) | (0b00 << 1) | BIT(m_nr_a0_pi_peripheral_en, 0);
		break;
	case 0xa2:
		port_253b_dat = (BIT(m_nr_a2_pi_i2s_ctl, 6, 2) << 6) | (0 << 5) | (BIT(m_nr_a2_pi_i2s_ctl, 2, 3) << 2) | (1 << 1) | BIT(m_nr_a2_pi_i2s_ctl, 0);
		break;
	case 0xa8:
		port_253b_dat = (0b0000000 << 7) | m_nr_a8_esp_gpio0_en;
		break;
	case 0xa9:
		port_253b_dat = (0b00000 << 3);// | (i_ESP_GPIO_20(2) <<) | (0 << 1) | i_ESP_GPIO_20(0);
		break;
	case 0xb0:
		port_253b_dat = 0;//(i_KBD_EXTENDED_KEYS(8) <<) | (i_KBD_EXTENDED_KEYS(9) <<) | (i_KBD_EXTENDED_KEYS(10) <<) | (i_KBD_EXTENDED_KEYS(11) <<) | (i_KBD_EXTENDED_KEYS(1) <<) | i_KBD_EXTENDED_KEYS(15 downto 13);
		break;
	case 0xb1:
		port_253b_dat = 0;//(i_KBD_EXTENDED_KEYS(12) <<) | (i_KBD_EXTENDED_KEYS(7 downto 2) <<) | i_KBD_EXTENDED_KEYS(0);
		break;
	case 0xb2:
		port_253b_dat = 0;//(i_JOY_RIGHT(10 downto 8) <<) | (i_JOY_RIGHT(11) <<) | (i_JOY_LEFT(10 downto 8) <<) | i_JOY_LEFT(11);
		break;
	case 0xb8:
		port_253b_dat = m_nr_b8_divmmc_ep_0;
		break;
	case 0xb9:
		port_253b_dat = m_nr_b9_divmmc_ep_valid_0;
		break;
	case 0xba:
		port_253b_dat = m_nr_ba_divmmc_ep_timing_0;
		break;
	case 0xbb:
		port_253b_dat = m_nr_bb_divmmc_ep_1;
		break;
	case 0xc0:
		port_253b_dat = (m_nr_c0_im2_vector << 5) | (0 << 4) | (m_nr_c0_stackless_nmi << 3) | /*(z80_im_mode << 1) |*/ m_nr_c0_int_mode_pulse_0_im2_1;
		break;
	case 0xc2:
		port_253b_dat = m_nr_c2_retn_address_lsb;
		break;
	case 0xc3:
		port_253b_dat = m_nr_c3_retn_address_msb;
		break;
	case 0xc4:
		{
			const u8 ula_int_en = (m_nr_22_line_interrupt_en << 1) | !port_ff_interrupt_disable();
			port_253b_dat = (m_nr_c4_int_en_0_expbus << 7) | (0b00000 << 2) | ula_int_en;
		}
		break;
	case 0xc5:
		port_253b_dat = 0;//ctc_int_en;
		break;
	case 0xc6:
		port_253b_dat = (0 << 7) | (m_nr_c6_int_en_2_654 << 4) | (0 << 3) | m_nr_c6_int_en_2_210;
		break;
	case 0xc8:
		port_253b_dat = (0b000000 << 2);// | (im2_int_status(0) << 1) | im2_int_status(11);
		break;
	case 0xc9:
		port_253b_dat = 0;//im2_int_status(10 downto 3);
		break;
	case 0xca:
		port_253b_dat = (0 << 7);// | (im2_int_status(13) <<  6) | (im2_int_status(2) << 5) | (im2_int_status(2) << 4) | (0 << 3) | (im2_int_status(12) << 2) | (im2_int_status(1) << 1) | im2_int_status(1);
		break;
	case 0xcc:
		port_253b_dat = (m_nr_cc_dma_int_en_0_7 << 7) | (0b00000 << 2) | m_nr_cc_dma_int_en_0_10;
		break;
	case 0xcd:
		port_253b_dat = m_nr_cd_dma_int_en_1;
		break;
	case 0xce:
		port_253b_dat = (0 << 7) | (m_nr_ce_dma_int_en_2_654 << 4) | (0 << 3) | m_nr_ce_dma_int_en_2_210;
		break;
	case 0xd8:
		port_253b_dat = (0b0000000 << 1) | m_nr_d8_io_trap_fdc_en;
		break;
	case 0xd9:
		port_253b_dat = m_nr_d9_iotrap_write;
		break;
	case 0xda:
		port_253b_dat = (0b000000 << 2) | m_nr_da_iotrap_cause;
		break;
	case 0xf0:
		port_253b_dat = m_nr_f0_xdev_cmd;
		break;
	case 0xf8:
		port_253b_dat = (0 << 7) | m_nr_f8_xadc_daddr;
		break;
	case 0xf9:
		port_253b_dat = m_nr_f9_xadc_d0;
		break;
	case 0xfa:
		port_253b_dat = m_nr_fa_xadc_d1;
		break;
	default:
		port_253b_dat = 0x00;
		if (!machine().side_effects_disabled())
			LOGWARN("rR: %X -> %x\n", nr_register, port_253b_dat);
	}

	return port_253b_dat;
}

void specnext_state::reg_w(offs_t nr_wr_reg, u8 nr_wr_dat)
{
	switch (nr_wr_reg)
	{
	case 0x02:
		nr_02_w(nr_wr_dat);
		break;
	case 0x03:
		m_bootrom_en = 0;

		if (BIT(nr_wr_dat, 7) == 1 && m_nr_03_user_dt_lock == 0 && BIT(nr_wr_dat, 3) == 0)
		{
			switch (BIT(nr_wr_dat, 4, 3))
			{
			case 0b000:
				m_nr_03_machine_timing = 0b001;
				break;
			case 0b001:
				m_nr_03_machine_timing = 0b001;
				break;
			case 0b010:
				m_nr_03_machine_timing = 0b010;
				break;
			case 0b011:
				m_nr_03_machine_timing = 0b011;
				break;
			case 0b100:
				m_nr_03_machine_timing = 0b100;
				break;
			default:
				m_nr_03_machine_timing = 0b011;
			}
		}

		m_nr_03_user_dt_lock = m_nr_03_user_dt_lock ^ BIT(nr_wr_dat, 3);

		if (m_nr_03_config_mode == 1)
		{
			switch (BIT(nr_wr_dat, 0, 3))
			{
			case 0b001:
				m_nr_03_machine_type = 0b001;
				break;
			case 0b010:
				m_nr_03_machine_type = 0b010;
				break;
			case 0b011:
				m_nr_03_machine_type = 0b011;
				break;
			case 0b100:
				m_nr_03_machine_type = 0b100;
				break;
			}
		}

		if (BIT(nr_wr_dat, 0, 3) == 0b111)
			m_nr_03_config_mode = 1;
		else if (BIT(nr_wr_dat, 0, 3) != 0b000)
			m_nr_03_config_mode = 0;

		break;
	case 0x04:
		m_nr_04_romram_bank = BIT(nr_wr_dat, 0, 7);
		bank_update(0, 2);
		break;
	case 0x05:
		m_nr_05_joy0 = (BIT(nr_wr_dat, 3) << 2) | BIT(nr_wr_dat, 6, 2);
		m_nr_05_joy1 = (BIT(nr_wr_dat, 1) << 2) | BIT(nr_wr_dat, 4, 2);
		m_nr_05_5060 = BIT(nr_wr_dat, 2);
		break;
	case 0x06:
		m_nr_06_hotkey_cpu_speed_en = BIT(nr_wr_dat, 7);
		m_nr_06_internal_speaker_beep = BIT(nr_wr_dat, 6);
		m_nr_06_hotkey_5060_en = BIT(nr_wr_dat, 5);
		m_nr_06_button_drive_nmi_en = BIT(nr_wr_dat, 4);
		m_nr_06_button_m1_nmi_en = BIT(nr_wr_dat, 3);
		if (m_nr_03_config_mode == 1)
			m_nr_06_ps2_mode = BIT(nr_wr_dat, 2);
		m_nr_06_psg_mode = BIT(nr_wr_dat, 0, 2);
		break;
	case 0x07:
		nr_07_cpu_speed_w(BIT(nr_wr_dat, 0, 2));
		break;
	case 0x08:
		m_nr_08_contention_disable = BIT(nr_wr_dat, 6);
		m_eff_nr_08_contention_disable = m_nr_08_contention_disable;
		m_nr_08_psg_stereo_mode = BIT(nr_wr_dat, 5);
		m_nr_08_internal_speaker_en = BIT(nr_wr_dat, 4);
		m_nr_08_dac_en = BIT(nr_wr_dat, 3);
		m_nr_08_port_ff_rd_en = BIT(nr_wr_dat, 2);
		m_nr_08_psg_turbosound_en = BIT(nr_wr_dat, 1);
		m_nr_08_keyboard_issue2 = BIT(nr_wr_dat, 0);

		if (BIT(nr_wr_dat, 7))
			port_7ffd_reg_w(m_port_7ffd_data &= ~0x20);
		break;
	case 0x09:
		m_nr_09_psg_mono = BIT(nr_wr_dat, 5, 3);
		m_nr_09_sprite_tie = BIT(nr_wr_dat, 4);
		m_sprites->mirror_tie_w(m_nr_09_sprite_tie);
		m_nr_09_hdmi_audio_en = not BIT(nr_wr_dat, 2);

		if (BIT(nr_wr_dat, 3))
			port_e3_reg_w(m_port_e3_reg & ~0x40);
		m_nr_09_scanlines = BIT(nr_wr_dat, 0, 2);
		break;
	case 0x0a:
		if (m_nr_03_config_mode == 1)
			nr_0a_mf_type_w(BIT(nr_wr_dat, 6, 2));
		m_nr_0a_divmmc_automap_en = BIT(nr_wr_dat, 4);
		m_nr_0a_mouse_button_reverse = BIT(nr_wr_dat, 3);
		m_nr_0a_mouse_dpi = BIT(nr_wr_dat, 0, 2);
		break;
	case 0x0b:
		m_nr_0b_joy_iomode_en = BIT(nr_wr_dat, 7);
		m_nr_0b_joy_iomode = BIT(nr_wr_dat, 4, 2);
		m_nr_0b_joy_iomode_0 = BIT(nr_wr_dat, 0);
		break;
	case 0x10:
		m_nr_10_flashboot = BIT(nr_wr_dat, 7);
		if (m_nr_03_config_mode)
		{
			if (g_board_issue() != 2)
			{
				m_nr_10_coreid = BIT(nr_wr_dat, 0, 5);
			}
			else if (g_board_issue() == 2)
			{
				if (BIT(~nr_wr_dat, 4) && BIT(nr_wr_dat, 0, 4) != 0b1111)
					m_nr_10_coreid = BIT(nr_wr_dat, 0, 4);
			}
		}
		break;
	case 0x11:
		if (m_nr_03_config_mode == 1)
		{
			if (BIT(nr_wr_dat, 0, 3) == 0b111)
				m_nr_11_video_timing = 0b000;
			else if (G_VIDEO_INC == 0b10)
				m_nr_11_video_timing = (0b00 << 1) | BIT(nr_wr_dat, 0);
			else
				m_nr_11_video_timing = BIT(nr_wr_dat, 0, 3);
		}
		break;
	case 0x12:
		nr_12_layer2_active_bank_w(nr_wr_dat);
		break;
	case 0x13:
		nr_13_layer2_shadow_bank_w(nr_wr_dat);
		break;
	case 0x14:
		nr_14_global_transparent_rgb_w(nr_wr_dat);
		break;
	case 0x15:
		m_screen->update_now();
		m_nr_15_lores_en = BIT(nr_wr_dat, 7);
		nr_15_sprite_priority_w(BIT(nr_wr_dat, 6));
		nr_15_sprite_border_clip_en_w(BIT(nr_wr_dat, 5));
		m_nr_15_layer_priority = BIT(nr_wr_dat, 2, 3);
		nr_15_sprite_over_border_en_w(BIT(nr_wr_dat, 1));
		m_nr_15_sprite_en = BIT(nr_wr_dat, 0);
		break;
	case 0x16:
		m_screen->update_now();
		nr_16_layer2_scrollx_w(nr_wr_dat);
		break;
	case 0x17:
		m_screen->update_now();
		nr_17_layer2_scrolly_w(nr_wr_dat);
		break;
	case 0x18:
		switch (m_nr_18_layer2_clip_idx)
		{
		case 0b00:
			nr_18_layer2_clip_x1_w(nr_wr_dat);
			break;
		case 0b01:
			nr_18_layer2_clip_x2_w(nr_wr_dat);
			break;
		case 0b10:
			nr_18_layer2_clip_y1_w(nr_wr_dat);
			break;
		default:
			nr_18_layer2_clip_y2_w(nr_wr_dat);
		}
		++m_nr_18_layer2_clip_idx &= 0x03;
		break;
	case 0x19:
		switch (m_nr_19_sprite_clip_idx)
		{
		case 0b00:
			nr_19_sprite_clip_x1_w(nr_wr_dat);
			break;
		case 0b01:
			nr_19_sprite_clip_x2_w(nr_wr_dat);
			break;
		case 0b10:
			nr_19_sprite_clip_y1_w(nr_wr_dat);
			break;
		default:
			nr_19_sprite_clip_y2_w(nr_wr_dat);
		}
		++m_nr_19_sprite_clip_idx &= 0x03;
		break;
	case 0x1a:
		switch (m_nr_1a_ula_clip_idx)
		{
		case 0b00:
			nr_1a_ula_clip_x1_w(nr_wr_dat);
			break;
		case 0b01:
			nr_1a_ula_clip_x2_w(nr_wr_dat);
			break;
		case 0b10:
			nr_1a_ula_clip_y1_w(nr_wr_dat);
			break;
		default:
			nr_1a_ula_clip_y2_w(nr_wr_dat);
		}
		++m_nr_1a_ula_clip_idx &= 0x03;
		break;
	case 0x1b:
		switch (m_nr_1b_tm_clip_idx)
		{
		case 0b00:
			nr_1b_tm_clip_x1_w(nr_wr_dat);
			break;
		case 0b01:
			nr_1b_tm_clip_x2_w(nr_wr_dat);
			break;
		case 0b10:
			nr_1b_tm_clip_y1_w(nr_wr_dat);
			break;
		default:
			nr_1b_tm_clip_y2_w(nr_wr_dat);
		}
		++m_nr_1b_tm_clip_idx &= 0x03;
		break;
	case 0x1c:
		if (BIT(nr_wr_dat, 0) == 1)
			m_nr_18_layer2_clip_idx = 0b00;
		if (BIT(nr_wr_dat, 1) == 1)
			m_nr_19_sprite_clip_idx = 0b00;
		if (BIT(nr_wr_dat, 2) == 1)
			m_nr_1a_ula_clip_idx = 0b00;
		if (BIT(nr_wr_dat, 3) == 1)
			m_nr_1b_tm_clip_idx = 0b00;
		break;
	case 0x22:
		m_nr_22_line_interrupt_en = BIT(nr_wr_dat, 1);
		m_nr_23_line_interrupt = (m_nr_23_line_interrupt & ~0x0100) | (BIT(nr_wr_dat, 0) << 8);
		line_irq_adjust();
		m_port_ff_data = (m_port_ff_data & 0xbf) | (BIT(nr_wr_dat, 1) << 6);
		break;
	case 0x23:
		m_nr_23_line_interrupt = (m_nr_23_line_interrupt & ~0x00ff) | nr_wr_dat;
		line_irq_adjust();
		break;
	case 0x26:
		m_screen->update_now();
		nr_26_ula_scrollx_w(nr_wr_dat);
		break;
	case 0x27:
		m_screen->update_now();
		nr_27_ula_scrolly_w(nr_wr_dat);
		break;
	case 0x28:
		//nr_keymap_sel <= nr_wr_dat(7);
		//nr_keymap_addr(8) <= nr_wr_dat(0);
		break;
	case 0x29:
		//nr_keymap_addr(7 downto 0) <= nr_wr_dat;
		break;
	case 0x2a:
		break;
	case 0x2b:
		// nr_keymap_addr <= nr_keymap_addr + 1;
		break;
	case 0x2c:
		m_dac[1]->data_w(nr_wr_dat);
		break;
	case 0x2d:
		m_dac[0]->data_w(nr_wr_dat);
		m_dac[3]->data_w(nr_wr_dat);
		break;
	case 0x2e:
		m_dac[2]->data_w(nr_wr_dat);
		break;
	case 0x2f:
		m_screen->update_now();
		nr_30_tm_scrollx_w((m_nr_30_tm_scrollx & ~0x0300) | (BIT(nr_wr_dat, 0, 2) << 8));
		break;
	case 0x30:
		m_screen->update_now();
		nr_30_tm_scrollx_w((m_nr_30_tm_scrollx & ~0x00ff) | nr_wr_dat);
		break;
	case 0x31:
		m_screen->update_now();
		nr_31_tm_scrolly_w(nr_wr_dat);
		break;
	case 0x32:
		m_screen->update_now();
		nr_32_lores_scrollx_w(nr_wr_dat);
		break;
	case 0x33:
		m_screen->update_now();
		nr_33_lores_scrolly_w(nr_wr_dat);
		break;
	case 0x34:
		m_sprites->mirror_data_w(nr_wr_dat);
		break;
	case 0x35: case 0x36:  case 0x37: case 0x38: case 0x39:
	case 0x75: case 0x76:  case 0x77: case 0x78: case 0x79:
		m_sprites->mirror_inc_w(BIT(nr_wr_reg, 6));
		m_sprites->mirror_index_w((nr_wr_reg & 0x3f) - 0x35);
		m_sprites->mirror_data_w(nr_wr_dat);
		break;
	case 0x40:
		m_nr_palette_idx = nr_wr_dat;
		m_nr_palette_sub_idx = 0;
		break;
	case 0x41:
		palette_val_w(0b00, (nr_wr_dat << 1) | BIT(nr_wr_dat, 1) | BIT(nr_wr_dat, 0));
		if (m_nr_43_palette_autoinc_disable == 0)
			++m_nr_palette_idx;
		m_nr_palette_sub_idx = 0;
		break;
	case 0x42:
		nr_42_ulanext_format_w(nr_wr_dat);
		break;
	case 0x43:
		m_nr_43_palette_autoinc_disable = BIT(nr_wr_dat, 7);
		m_nr_43_palette_write_select = BIT(nr_wr_dat, 4, 3);
		nr_43_active_sprite_palette_w(BIT(nr_wr_dat, 3));
		nr_43_active_layer2_palette_w(BIT(nr_wr_dat, 2));
		nr_43_active_ula_palette_w(BIT(nr_wr_dat, 1));
		nr_43_ulanext_en_w(BIT(nr_wr_dat, 0));
		m_nr_palette_sub_idx = 0;
		break;
	case 0x44:
		if (m_nr_palette_sub_idx == 0)
			m_nr_stored_palette_value = nr_wr_dat;
		else
		{
			palette_val_w(BIT(nr_wr_dat, 6, 2), (m_nr_stored_palette_value << 1) | BIT(nr_wr_dat, 0));
			if (m_nr_43_palette_autoinc_disable == 0)
				++m_nr_palette_idx;
		}
		m_nr_palette_sub_idx = !m_nr_palette_sub_idx;
		break;
	case 0x4a:
		{
			m_nr_4a_fallback_rgb = nr_wr_dat;
			rgb_t fb = rgbexpand<3,3,3>((m_nr_4a_fallback_rgb << 1) | BIT(m_nr_4a_fallback_rgb, 1) | BIT(m_nr_4a_fallback_rgb, 0), 6, 3, 0);
			m_palette->set_pen_color(UTM_FALLBACK_PEN, fb);
		}
		break;
	case 0x4b:
		nr_4b_sprite_transparent_index_w(nr_wr_dat);
		break;
	case 0x4c:
		nr_4c_tm_transparent_index_w(BIT(nr_wr_dat, 0, 4));
		break;
	case 0x50: case 0x51: case 0x52: case 0x53:
	case 0x54: case 0x55: case 0x56: case 0x57:
		mmu_w(nr_wr_reg - 0x50, nr_wr_dat);
		break;
	case 0x60:
	case 0x63:
		{
			const bool nr_copper_write_8 = nr_wr_reg == 0x60;
			if (BIT(m_nr_copper_addr, 0) == 0)
			{
				if (nr_copper_write_8)
					m_copper->data_w(m_nr_copper_addr, nr_wr_dat); // msb
			}
			else
			{
				if (!nr_copper_write_8)
					m_copper->data_w(m_nr_copper_addr & ~1, m_nr_copper_data_stored); // msb
				m_copper->data_w(m_nr_copper_addr, nr_wr_dat); // lsb
			}

			if (BIT(m_nr_copper_addr, 0) == 0)
				m_nr_copper_data_stored = nr_wr_dat;
			++m_nr_copper_addr %= 0x800;
		}
		break;
	case 0x61:
		m_nr_copper_addr = (m_nr_copper_addr & ~0x00ff) | nr_wr_dat;
		break;
	case 0x62:
		nr_62_copper_mode_w(BIT(nr_wr_dat, 6, 2));
		m_nr_copper_addr = (m_nr_copper_addr & ~0x0700) | (BIT(nr_wr_dat, 0, 3) << 8);
		break;
	case 0x64:
		m_nr_64_copper_offset = nr_wr_dat;
		break;
	case 0x68:
		m_screen->update_now();
		m_nr_68_ula_en = not BIT(nr_wr_dat, 7);
		m_nr_68_blend_mode = BIT(nr_wr_dat, 5, 2);
		m_nr_68_cancel_extended_keys = BIT(nr_wr_dat, 4);
		port_ff3b_ulap_en_w(BIT(nr_wr_dat, 3));
		nr_68_ula_fine_scroll_x_w(BIT(nr_wr_dat, 2));
		m_nr_68_ula_stencil_mode = BIT(nr_wr_dat, 0);
		break;
	case 0x69:
		m_port_ff_data = (m_port_ff_data & 0xc0) | (nr_wr_dat & 0x3f);
		port_7ffd_reg_w((m_port_7ffd_data & ~0x08) | (BIT(nr_wr_dat, 6) << 3));
		port_123b_layer2_en_w(BIT(nr_wr_dat, 7));
		break;
	case 0x6a:
		nr_6a_lores_radastan_w(BIT(nr_wr_dat, 5));
		nr_6a_lores_radastan_xor_w(BIT(nr_wr_dat, 4));
		nr_6a_lores_palette_offset_w(BIT(nr_wr_dat, 0, 4));
		break;
	case 0x6b:
		m_screen->update_now();
		m_nr_6b_tm_en = BIT(nr_wr_dat, 7);
		nr_6b_tm_control_w(BIT(nr_wr_dat, 0, 7));
		break;
	case 0x6c:
		nr_6c_tm_default_attr_w(nr_wr_dat);
		break;
	case 0x6e:
		nr_6e_tilemap_base_w(BIT(nr_wr_dat, 7), BIT(nr_wr_dat, 0, 6));
		break;
	case 0x6f:
		nr_6f_tilemap_tiles_w(BIT(nr_wr_dat, 7), BIT(nr_wr_dat, 0, 6));
		break;
	case 0x70:
		nr_70_layer2_resolution_w(BIT(nr_wr_dat, 4, 2));
		nr_70_layer2_palette_offset_w(BIT(nr_wr_dat, 0, 4));
		break;
	case 0x71:
		nr_71_layer2_scrollx_msb_w(BIT(nr_wr_dat, 0));
		break;
	case 0x7f:
		m_nr_7f_user_register_0 = nr_wr_dat;
		break;
	case 0x80:
		m_nr_80_expbus = nr_wr_dat;
		break;
	case 0x81:
		m_nr_81_expbus_ula_override = BIT(nr_wr_dat, 6);
		m_nr_81_expbus_nmi_debounce_disable = BIT(nr_wr_dat, 5);
		m_nr_81_expbus_clken = BIT(nr_wr_dat, 4);
		m_nr_81_expbus_speed = 0b00;
		break;
	case 0x82:
		m_nr_82_internal_port_enable = nr_wr_dat;
		break;
	case 0x83:
		m_nr_83_internal_port_enable = nr_wr_dat;
		break;
	case 0x84:
		m_nr_84_internal_port_enable = nr_wr_dat;
		break;
	case 0x85:
		m_nr_85_internal_port_enable = BIT(nr_wr_dat, 0, 4);
		m_nr_85_internal_port_reset_type = BIT(nr_wr_dat, 7);
		break;
	case 0x86:
		m_nr_86_bus_port_enable = nr_wr_dat;
		break;
	case 0x87:
		m_nr_87_bus_port_enable = nr_wr_dat;
		break;
	case 0x88:
		m_nr_88_bus_port_enable = nr_wr_dat;
		break;
	case 0x89:
		m_nr_89_bus_port_enable = BIT(nr_wr_dat, 0, 4);
		m_nr_89_bus_port_reset_type = BIT(nr_wr_dat, 7);
		break;
	case 0x8a:
		m_nr_8a_bus_port_propagate = BIT(nr_wr_dat, 0, 6);
		break;
	case 0x8c:
		m_nr_8c_altrom = nr_wr_dat;
		bank_update(0, 2);
		break;
	case 0x8e:
		LOGMEM("8e (%x): 1f=%x 7f=%x df=%x\n", nr_wr_dat, m_port_1ffd_data, m_port_7ffd_data, m_port_dffd_data);
		if (BIT(nr_wr_dat, 3))
		{
			if (!nr_8f_mapping_mode_profi())
				m_port_dffd_data &= ~0x08;
			m_port_dffd_data = (m_port_dffd_data & ~0x07) | BIT(nr_wr_dat, 7);
			port_7ffd_reg_w((m_port_7ffd_data & ~0x07) | BIT(nr_wr_dat, 4, 3));
		}

		if (BIT(~nr_wr_dat, 2))
			port_7ffd_reg_w((m_port_7ffd_data & ~0x10) | ((nr_wr_dat & 1) << 4));

		m_port_1ffd_data = (m_port_1ffd_data & ~0x04) | (BIT(nr_wr_dat, 1) << 2);
		m_port_1ffd_data = (m_port_1ffd_data & ~0x02) | (BIT(nr_wr_dat, 0) << 1);
		m_port_1ffd_data = (m_port_1ffd_data & ~0x01) | BIT(nr_wr_dat, 2);

		memory_change(0x8e, nr_wr_dat);
		LOGMEM("8e: 1f=%x 7f=%x df=%x\n", m_port_1ffd_data, m_port_7ffd_data, m_port_dffd_data);
		break;
	case 0x8f:
		m_nr_8f_mapping_mode = BIT(nr_wr_dat, 0, 2);
		memory_change(0x8f, nr_wr_dat);
		break;
	case 0x90:
		m_nr_90_pi_gpio_o_en = (BIT(nr_wr_dat, 2, 6) << 2) | 0b00;
		break;
	case 0x91:
		m_nr_91_pi_gpio_o_en = nr_wr_dat;
		break;
	case 0x92:
		m_nr_92_pi_gpio_o_en = nr_wr_dat;
		break;
	case 0x93:
		m_nr_93_pi_gpio_o_en = BIT(nr_wr_dat, 0, 4);
		break;
	case 0x98:
		m_nr_98_pi_gpio_o = nr_wr_dat;
		break;
	case 0x99:
		m_nr_99_pi_gpio_o = nr_wr_dat;
		break;
	case 0x9a:
		m_nr_9a_pi_gpio_o = nr_wr_dat;
		break;
	case 0x9b:
		m_nr_9b_pi_gpio_o = BIT(nr_wr_dat, 0, 4);
		break;
	case 0xa0:
		m_nr_a0_pi_peripheral_en = nr_wr_dat;
		break;
	case 0xa2:
		m_nr_a2_pi_i2s_ctl = nr_wr_dat;
		break;
	case 0xa8:
		m_nr_a8_esp_gpio0_en = BIT(nr_wr_dat, 0);
		break;
	case 0xa9:
		m_nr_a9_esp_gpio0 = BIT(nr_wr_dat, 0);
		break;
	case 0xb8:
		m_nr_b8_divmmc_ep_0 = nr_wr_dat;
		break;
	case 0xb9:
		m_nr_b9_divmmc_ep_valid_0 = nr_wr_dat;
		break;
	case 0xba:
		m_nr_ba_divmmc_ep_timing_0 = nr_wr_dat;
		break;
	case 0xbb:
		m_nr_bb_divmmc_ep_1 = nr_wr_dat;
		break;
	case 0xc0:
		nr_c0_im2_vector_w(BIT(nr_wr_dat, 5, 3));
		m_nr_c0_stackless_nmi = BIT(nr_wr_dat, 3);
		m_maincpu->nmi_stackless_w(m_nr_c0_stackless_nmi);
		m_nr_c0_int_mode_pulse_0_im2_1 = BIT(nr_wr_dat, 0);
		break;
	case 0xc2:
		m_nr_c2_retn_address_lsb = nr_wr_dat;
		break;
	case 0xc3:
		m_nr_c3_retn_address_msb = nr_wr_dat;
		break;
	case 0xc4:
		m_nr_c4_int_en_0_expbus = BIT(nr_wr_dat, 7);
		m_nr_22_line_interrupt_en = BIT(nr_wr_dat, 1);
		m_port_ff_data = (m_port_ff_data & 0xbf) | (BIT(~nr_wr_dat, 0) << 6);
		break;
	case 0xc5:
		{
			u8 active = BIT(nr_wr_dat, 0, 4);
			for (auto ch = 0; ch < 4; ++ch, active >>= 1)
				m_ctc->write(ch, 1 | ((active & 1) ? 0 : 2)); // for inactive: CONTROL + RESET
		}
		break;
	case 0xc6:
		m_nr_c6_int_en_2_654 = (BIT(nr_wr_dat, 6) << 2) | (BIT(nr_wr_dat, 5) << 1) | BIT(nr_wr_dat, 4);
		m_nr_c6_int_en_2_210 = (BIT(nr_wr_dat, 2) << 2) | (BIT(nr_wr_dat, 1) << 1) | BIT(nr_wr_dat, 0);
		break;
	case 0xcc:
		m_nr_cc_dma_int_en_0_7 = BIT(nr_wr_dat, 7);
		m_nr_cc_dma_int_en_0_10 = BIT(nr_wr_dat, 0, 2);
		break;
	case 0xcd:
		m_nr_cd_dma_int_en_1 = nr_wr_dat;
		break;
	case 0xce:
		m_nr_ce_dma_int_en_2_654 = BIT(nr_wr_dat, 4, 3);
		m_nr_ce_dma_int_en_2_210 = BIT(nr_wr_dat, 0, 3);
		break;
	case 0xd8:
		m_nr_d8_io_trap_fdc_en = BIT(nr_wr_dat, 0);
		break;
	case 0xd9:
		m_nr_d9_iotrap_write = nr_wr_dat;
		break;
	case 0xff:
		LOG("Debug: #%02X\n", nr_wr_dat); // LED
		break;
	default:
		LOGWARN("wR: %X <- %x\n", nr_wr_reg, nr_wr_dat);
		break;
	}

	m_sprites->mirror_index_w(0b111);
	m_sprites->mirror_inc_w(0);
}

void specnext_state::nr_02_w(u8 nr_wr_dat)
{
	m_nr_02_bus_reset = BIT(nr_wr_dat, 7);
	if (BIT(~nr_wr_dat, 4))
		m_nr_da_iotrap_cause = 0;

	m_nr_02_generate_mf_nmi = BIT(nr_wr_dat, 3);
	do_mf_nmi();

	if (BIT(nr_wr_dat, 2))
	{
		if (m_nr_06_button_drive_nmi_en)
		{
			m_nr_02_generate_divmmc_nmi = 1;
			m_maincpu->nmi();
		}
	}
	else
	{
		m_nr_02_generate_divmmc_nmi = 0;
	}

	if (BIT(nr_wr_dat, 1)) // hard reset
	{
		m_nr_02_hard_reset = 1;
		machine().schedule_soft_reset();
	}
	else if (BIT(nr_wr_dat, 0)) // soft reset
	{
		m_nr_02_reset_type = (BIT(m_nr_02_reset_type, 2) << 1) | BIT(m_nr_02_reset_type, 1) | BIT(m_nr_02_reset_type, 0);
		machine().schedule_soft_reset();
	}
}

void specnext_state::nr_07_cpu_speed_w(u8 data)
{
	m_nr_07_cpu_speed = data & 3;
	m_maincpu->set_clock_scale(1 << m_nr_07_cpu_speed);
	m_dma->set_clock_scale(1 << m_nr_07_cpu_speed);
}

void specnext_state::nr_14_global_transparent_rgb_w(u8 data)
{
	m_nr_14_global_transparent_rgb = data;
	m_global_transparent = (m_nr_14_global_transparent_rgb << 1) | BIT(m_nr_14_global_transparent_rgb, 1) | BIT(m_nr_14_global_transparent_rgb, 0);
	m_ula_scr->set_global_transparent(data);
	m_lores->set_global_transparent(data);
	m_layer2->set_global_transparent(data);
}

void specnext_state::nr_1a_ula_clip_y2_w(u8 data)
{
	m_nr_1a_ula_clip_y2 = data;
	const u8 ula_clip_y2_0 = ((m_nr_1a_ula_clip_y2 & 0xc0) == 0xc0) ? 0xbf : m_nr_1a_ula_clip_y2;
	m_ula_scr->ula_clip_y2_w(ula_clip_y2_0);
	m_lores->clip_y2_w(ula_clip_y2_0);
}

static const z80_daisy_config z80_daisy_chain[] =
{
	{ "ndma" },
	{ "ctc" },
	{ nullptr }
};

TIMER_CALLBACK_MEMBER(specnext_state::irq_off)
{
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
	m_irq_mask = 0;
}

TIMER_CALLBACK_MEMBER(specnext_state::irq_on)
{
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
	m_irq_mask |= 1 << 11;
	m_irq_off_timer->adjust(m_maincpu->clocks_to_attotime(32));
}

TIMER_CALLBACK_MEMBER(specnext_state::line_irq_on)
{
	m_screen->update_now();
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
	m_irq_mask |= 1 << 0;
	m_irq_off_timer->adjust(m_maincpu->clocks_to_attotime(32));
}

INTERRUPT_GEN_MEMBER(specnext_state::specnext_interrupt)
{
	m_tiles->control_w(m_nr_6b_tm_control); // TODO (1): Santa's Pressie, The Next War

	if (m_eff_nr_05_5060 != m_nr_05_5060 || m_nr_03_machine_timing != m_eff_nr_03_machine_timing)
		update_video_mode();

	line_irq_adjust();
	if (!port_ff_interrupt_disable())
	{
		m_irq_on_timer->adjust(m_screen->time_until_pos(m_video_timings.int_v, m_video_timings.int_h << 1));
	}
}

void specnext_state::line_irq_adjust()
{
	if (m_nr_22_line_interrupt_en) {
		u16 line = m_nr_64_copper_offset + m_nr_23_line_interrupt;
		if (line < m_screen->width())
			m_irq_line_timer->adjust(m_screen->time_until_pos(
				(m_clip256x192.top() - 1 + line) % m_screen->height(),
				m_clip256x192.right() + 2));
		else
			m_irq_line_timer->reset();
	}
}

IRQ_CALLBACK_MEMBER(specnext_state::irq_callback)
{
	if (!m_nr_02_bus_reset)
	{
		return 0xff;
	}
	else
	{
		u8 vector = 11;
		if (m_irq_mask)
		{
			vector = 0;
			u16 i = 1;
			for (; ~m_irq_mask & i; ++vector, i <<= 1);
			m_irq_mask &= ~i;

		}
		return (m_nr_c0_im2_vector << 5) | (vector << 2);
	}
}

INPUT_CHANGED_MEMBER(specnext_state::on_mf_nmi)
{
	m_nr_02_generate_mf_nmi = newval & 1;
	do_mf_nmi();
	m_nr_02_generate_mf_nmi = 0;
}

INPUT_CHANGED_MEMBER(specnext_state::on_divmmc_nmi)
{
	m_nr_02_generate_divmmc_nmi = newval & 1;
	if (m_nr_06_button_drive_nmi_en && m_nr_02_generate_divmmc_nmi)
		m_maincpu->nmi();
}

void specnext_state::do_mf_nmi()
{
	if (m_nr_06_button_m1_nmi_en && m_nr_02_generate_mf_nmi)
	{
		m_maincpu->nmi();
		m_mf->button_w(1);
		m_mf->clock_w();
		m_mf->button_w(0);
	}
}

void specnext_state::leave_nmi(int status)
{
	m_mf->cpu_retn_seen_w(1);
	m_mf->clock_w();

	m_mf->cpu_retn_seen_w(0);
	m_mf->clock_w();
	bank_update(0, 2);
}

u8 specnext_state::do_m1(offs_t offset)
{
	u8 bank = offset >> 13;
	// pre M1
	m_divmmc->cpu_mreq_n_w(1);
	m_divmmc->clock_w();

	// M1
	m_divmmc->cpu_mreq_n_w(0);
	m_divmmc->cpu_m1_n_w(0);
	m_divmmc->clock_w();
	if (bank < 2)
		bank_update(bank);
	const u8 data = m_program.read_byte(offset);

	// after M1
	m_divmmc->automap_instant_on_w(0);
	m_divmmc->automap_delayed_on_w(0);
	m_divmmc->automap_delayed_off_w(0);
	m_divmmc->automap_rom3_instant_on_w(0);
	m_divmmc->automap_rom3_delayed_on_w(0);
	m_divmmc->automap_nmi_instant_on_w(0);
	m_divmmc->automap_nmi_delayed_on_w(0);

	m_divmmc->cpu_m1_n_w(1);
	m_divmmc->clock_w();
	bank_update(0, 2);

	m_divmmc_delayed_check = 1;
	return data;
}

void specnext_state::map_fetch(address_map &map)
{
	map(0x0000, 0xffff).lr8(NAME([this](offs_t offset)
	{
		if (m_divmmc_delayed_check && !machine().side_effects_disabled())
		{
			/* Happens after RW cycles (before next M1 fetch).
			Fell like side effects check must be ignored here,
			because doesn't matter who reset this lines and such
			approach gives better experience in debugger UI. */
			do_m1(offset);
			m_divmmc_delayed_check = 0;

			// do_m1 performs read from m_program with waits, we need to take it back
			if (!machine().side_effects_disabled() && (m_nr_07_cpu_speed == 0b11))
			{
				m_maincpu->adjust_icount(1);
			}
		}

		return m_program.read_byte(offset);
	}));
	map(0x0000, 0x0000).select(0x0038).lr8(NAME([this](offs_t offset)
	{
		if (!machine().side_effects_disabled())
		{
			const u8 b = offset >> 3;
			const bool divmmc_rst_ep = BIT(m_nr_b8_divmmc_ep_0, b);
			const bool divmmc_rst_ep_valid = BIT(m_nr_b9_divmmc_ep_valid_0, b);
			const bool divmmc_rst_ep_timing = BIT(m_nr_ba_divmmc_ep_timing_0, b);

			m_divmmc->automap_instant_on_w(divmmc_rst_ep && divmmc_rst_ep_valid && divmmc_rst_ep_timing);
			m_divmmc->automap_delayed_on_w(divmmc_rst_ep && divmmc_rst_ep_valid && !divmmc_rst_ep_timing);
			m_divmmc->automap_rom3_instant_on_w(divmmc_rst_ep && !divmmc_rst_ep_valid && divmmc_rst_ep_timing);
			m_divmmc->automap_rom3_delayed_on_w(divmmc_rst_ep && !divmmc_rst_ep_valid && !divmmc_rst_ep_timing);

			return do_m1(offset);
		}
		else
			return m_program.read_byte(offset);
	}));
	map(0x1ff8, 0x1fff).lr8(NAME([this](offs_t offset)
	{
		if (!machine().side_effects_disabled())
		{
			m_divmmc->automap_delayed_off_w(BIT(m_nr_bb_divmmc_ep_1, 6));
			return do_m1(0x1ff8 + offset);
		}
		else
			return m_program.read_byte(0x1ff8 + offset);
	}));
	map(0x3d00, 0x3dff).lr8(NAME([this](offs_t offset)
	{
		if (!machine().side_effects_disabled())
		{
			m_divmmc->automap_rom3_instant_on_w(BIT(m_nr_bb_divmmc_ep_1, 7));
			return do_m1(0x3d00 + offset);
		}
		else
			return m_program.read_byte(0x3d00 + offset);
	}));
	map(0x04c6, 0x04c6).lr8(NAME([this](offs_t offset)
	{
		if (!machine().side_effects_disabled())
		{
			m_divmmc->automap_rom3_delayed_on_w(BIT(m_nr_bb_divmmc_ep_1, 2));
			return do_m1(0x04c6 + offset);
		}
		else
			return m_program.read_byte(0x04c6 + offset);
	}));
	map(0x0562, 0x0562).lr8(NAME([this](offs_t offset)
	{
		if (!machine().side_effects_disabled())
		{
			m_divmmc->automap_rom3_delayed_on_w(BIT(m_nr_bb_divmmc_ep_1, 3));
			return do_m1(0x0562 + offset);
		}
		else
			return m_program.read_byte(0x0562 + offset);
	}));
	map(0x04d7, 0x04d7).lr8(NAME([this](offs_t offset)
	{
		if (!machine().side_effects_disabled())
		{
			m_divmmc->automap_rom3_delayed_on_w(BIT(m_nr_bb_divmmc_ep_1, 4));
			return do_m1(0x04d7 + offset);
		}
		else
			return m_program.read_byte(0x04d7 + offset);
	}));
	map(0x056a, 0x056a).lr8(NAME([this](offs_t offset)
	{
		if (!machine().side_effects_disabled())
		{
			m_divmmc->automap_rom3_delayed_on_w(BIT(m_nr_bb_divmmc_ep_1, 5));
			return do_m1(0x056a + offset);
		}
		else
			return m_program.read_byte(0x056a + offset);
	}));
	map(0x0066, 0x0066).lr8(NAME([this](offs_t offset)
	{
		if (!machine().side_effects_disabled())
		{
			m_mf->cpu_m1_n_w(0);
			m_mf->cpu_a_0066_w(1);
			m_mf->cpu_mreq_n_w(0);
			m_mf->clock_w();

			m_divmmc->automap_nmi_instant_on_w(BIT(m_nr_bb_divmmc_ep_1, 1));
			m_divmmc->automap_nmi_delayed_on_w(BIT(m_nr_bb_divmmc_ep_1, 0));
			u8 data = do_m1(0x0066 + offset);

			m_mf->cpu_a_0066_w(0);
			m_mf->cpu_m1_n_w(1);
			m_mf->clock_w();

			return data;
		}
		else
			return m_program.read_byte(0x0066 + offset);
	}));
}

void specnext_state::map_mem(address_map &map)
{
	using views_link = std::reference_wrapper<memory_view>;
	views_link views[] = { m_view0, m_view1, m_view2, m_view3, m_view4, m_view5, m_view6, m_view7 };

	for (auto i = 0; i < 8; i++)
	{
		map(0x0000 + i * 0x2000, 0x1fff + i * 0x2000).bankrw(m_bank_ram[i]);
		map(0x0000 + i * 0x2000, 0x1fff + i * 0x2000).view(views[i].get());
		views[i].get()[0](0x0000 + i * 0x2000, 0x1fff + i * 0x2000).nopw();
	}
	views[0].get()[1](0x0000, 0x1fff).bankr(m_bank_boot_rom);
	views[1].get()[1](0x2000, 0x3fff).bankr(m_bank_boot_rom);
}

void specnext_state::map_io(address_map &map)
{
	map.unmap_value_low();
	map(0x0000, 0xffff).unmaprw();

	map(0x0000, 0x0000).select(0xfffe).rw(FUNC(specnext_state::spectrum_ula_r), FUNC(specnext_state::spectrum_ula_w));
	map(0x00ff, 0x00ff).mirror(0xff00).rw(FUNC(specnext_state::port_ff_r), FUNC(specnext_state::port_ff_w));

	map(0x001f, 0x001f).select(0xff00).lrw8(NAME([this](offs_t offset)
	{
		return mf_port_r(offset | 0x1f);
	}), NAME([this](offs_t offset, u8 data) {
		mf_port_w(offset | 0x1f, data);
	}));
	map(0x003f, 0x003f).select(0xff00).lrw8(NAME([this](offs_t offset)
	{
		return mf_port_r(offset | 0x3f);
	}), NAME([this](offs_t offset, u8 data) {
		mf_port_w(offset | 0x3f, data);
	}));
	map(0x009f, 0x009f).select(0xff00).lrw8(NAME([this](offs_t offset)
	{
		return mf_port_r(offset | 0x8f);
	}), NAME([this](offs_t offset, u8 data) {
		mf_port_w(offset | 0x8f, data);
	}));
	map(0x00bf, 0x00bf).select(0xff00).lrw8(NAME([this](offs_t offset)
	{
		return mf_port_r(offset | 0xbf);
	}), NAME([this](offs_t offset, u8 data) {
		mf_port_w(offset | 0xbf, data);
	}));

	map(0x0001, 0x0001).mirror(0xfff4).lr8(NAME([this]() { // #bff5
		return m_nr_08_psg_turbosound_en ? m_ay_select : 0;
	}));
	map(0xc005, 0xc005).mirror(0x3ff8).lr8(NAME([this]() { // #fffd
		return m_ay[m_nr_08_psg_turbosound_en ? m_ay_select : 0]->data_r();
	})).w(FUNC(specnext_state::turbosound_address_w));
	map(0x8005, 0x8005).mirror(0x3ff8).lw8(NAME([this](u8 data) { // #bffd
		m_ay[m_nr_08_psg_turbosound_en ? m_ay_select : 0]->data_w(data);
	}));

	map(0x0001, 0x0001).select(0x7ffc).lw8(NAME([this](offs_t offset, u8 data) {
		const bool p3_timing_hw_en = (m_nr_03_machine_timing & 3) == 0b11;
		if (port_7ffd_io_en() && (BIT(offset, 14) || !p3_timing_hw_en) && !port_7ffd_locked())
		{
			port_7ffd_reg_w(data);
			memory_change(0x7ffd, data);
		}
	}));
	map(0xd001, 0xd001).mirror(0x0ffc).lw8(NAME([this](u8 data) {
		if (port_dffd_io_en() && (!port_7ffd_locked() || nr_8f_mapping_mode_profi()))
		{
			m_port_dffd_data = data;
			memory_change(0xdffd, data);
		}
	}));
	map(0x1001, 0x1001).mirror(0x0ffc).lw8(NAME([this](u8 data) {
		if (port_1ffd_io_en() && !port_7ffd_locked())
		{
			m_port_1ffd_data = data;
			memory_change(0x1ffd, data);
		}
	}));
	map(0xe0f7, 0xe0f7).mirror(0x0f00).lw8(NAME([this](u8 data) {
		if(port_eff7_io_en())
		{
			m_port_eff7_data = data;
			memory_change(0xeff7, data);
		}
	}));

	map(0x2001, 0x2001).mirror(0x0ffc).lr8(NAME([]() {
		return /*m_nr_d8_io_trap_fdc_en ? ... :*/ 0x00;
	}));
	map(0x3001, 0x3001).mirror(0x0ffc).lrw8(NAME([]() {
		return /*m_nr_d8_io_trap_fdc_en ? ... :*/ 0x00;
	}), NAME([this](u8 data) {
		if (m_nr_d8_io_trap_fdc_en)
		{
		}
	}));


	map(0x103b, 0x103b).lr8(NAME([this]() {
		return port_i2c_io_en() ? (0xfe | m_i2c_scl_data) : 0x00;
	})).w(FUNC(specnext_state::i2c_scl_w));
	map(0x113b, 0x113b).lrw8(NAME([this]() {
		return port_i2c_io_en() ? (0xfe | (m_i2c_sda_data & 1)) : 0x00;
	}), NAME([this](u8 data) {
		if(port_i2c_io_en())
			m_i2c->sda_write(data & 1);
	}));
	map(0x183b, 0x183b).select(0x0700).lrw8(NAME([this](offs_t offset) {
		u8 chanel = offset >> 8;
		return port_ctc_io_en() && (chanel < 4) ? m_ctc->read(chanel) : 0x00;
	}), NAME([this](offs_t offset, u8 data) {
		u8 chanel = offset >> 8;
		if(port_ctc_io_en() && (chanel < 4))
			m_ctc->write(chanel, data);
	}));
	map(0x123b, 0x123b).lrw8(NAME([this]() {
		return (m_port_123b_layer2_map_segment << 6) | (0b00 << 4) | (m_port_123b_layer2_map_shadow << 3) | (m_port_123b_layer2_map_rd_en << 2) | (m_port_123b_layer2_en << 1) | m_port_123b_layer2_map_wr_en;
	}), NAME([this](u8 data) {
		if (BIT(~data, 4))
		{
			port_123b_layer2_en_w(BIT(data, 1));
			m_port_123b_layer2_map_wr_en = BIT(data, 0);
			m_port_123b_layer2_map_rd_en = BIT(data, 2);
			m_port_123b_layer2_map_shadow = BIT(data, 3);
			m_port_123b_layer2_map_segment = BIT(data, 6, 2);
		}
		else
			m_port_123b_layer2_offset = BIT(data, 0, 3);

		bank_update(0, 6);
	}));
	map(0x243b, 0x243b).lrw8(NAME([this]() { return m_nr_register; })
		, NAME([this](u8 data) { m_nr_register = data; }));
	map(0x253b, 0x253b).lrw8(NAME([this]() { return m_next_regs.read_byte(m_nr_register); })
		, NAME([this](u8 data) { m_next_regs.write_byte(m_nr_register, data); }));
	map(0x303b, 0x303b).lrw8(NAME([this]() {
		return port_sprite_io_en() ? m_sprites->mirror_num_r() : 0x00;
	}), NAME([this](u8 data) {
		if (port_sprite_io_en())
			m_sprites->io_w(0x303b, data);
	}));
	map(0xbf3b, 0xbf3b).lw8(NAME([this](u8 data) {
		if (port_ulap_io_en())
		{
			m_port_bf3b_ulap_mode = BIT(data, 6, 2);
			if (BIT(data, 6, 2) == 0b00)
				m_port_bf3b_ulap_index = BIT(data, 0, 6);
		}
	})); // ULA+ Register
	map(0xff3b, 0xff3b).lrw8(NAME([this]() {
		u8 port_ff3b_dat = 0x00;
		if (port_ulap_io_en())
		{
			port_ff3b_dat = m_port_bf3b_ulap_mode == 0b00
				? bitswap<8>(nr_palette_dat(), 5, 4, 3, 8, 7, 6, 2, 1)
				: m_port_ff3b_ulap_en;
		}
		return port_ff3b_dat;
	}), NAME([this](u8 data) {
		if (port_ulap_io_en())
		{
			if (m_port_bf3b_ulap_mode == 0b01)
				port_ff3b_ulap_en_w(BIT(data, 0));
		}
	})); // ULA+ Data
	map(0x0057, 0x0057).mirror(0xff00).lw8(NAME([this](u8 data) {
		if (port_sprite_io_en())
			m_sprites->io_w(0x0057, data);
	}));
	map(0x005b, 0x005b).mirror(0xff00).lw8(NAME([this](u8 data) {
		if (port_sprite_io_en())
			m_sprites->io_w(0x005b, data);
	}));
	map(0x00e7, 0x00e7).mirror(0xff00).w(FUNC(specnext_state::port_e7_reg_w));
	map(0x00e3, 0x00e3).mirror(0xff00).lrw8(NAME([this]() { return port_divmmc_io_en() ? m_port_e3_reg & ~0x30 : 0x00; })
		, NAME([this](u8 data) { if (port_divmmc_io_en()) { port_e3_reg_w((m_port_e3_reg & 0x40) | data); }}));
	map(0x00eb, 0x00eb).mirror(0xff00).rw(FUNC(specnext_state::spi_data_r), FUNC(specnext_state::spi_data_w));

	map(0x000b, 0x000b).mirror(0xff00).lrw8(NAME([this]() { return dma_r(1); }), NAME([this](u8 data) { dma_w(1, data); }));
	map(0x006b, 0x006b).mirror(0xff00).lrw8(NAME([this]() { return dma_r(0); }), NAME([this](u8 data) { dma_w(0, data); }));

	map(0x0bdf, 0x0bdf).mirror(0xf000).lr8(NAME([this]() -> u8 { return m_io_mouse[0]->read(); }));                 // #fbdf
	map(0x0fdf, 0x0fdf).mirror(0xf000).lr8(NAME([this]() -> u8 { return ~m_io_mouse[1]->read(); }));                // #ffdf
	map(0x0adf, 0x0adf).mirror(0xf000).lr8(NAME([this]() -> u8 { return 0x80 | (m_io_mouse[2]->read() & 0x07); })); // #fadf

	// TODO resolve conflicts mf+joy+DAC: 1f, 3f
	//map(0x001f, 0x001f).mirror(0xff00).lr8(NAME([]() -> u8 { return 0x00; /* Joy1,2*/ })).lw8(NAME([this](u8 data) {
	//  if (m_nr_08_dac_en)
	//      m_dac[0]->data_w(data);
	//}));
	map(0x00f1, 0x00f1).mirror(0xff00).lw8(NAME([this](u8 data) {
		if (m_nr_08_dac_en)
			m_dac[0]->data_w(data);
	}));
	//map(0x003f, 0x003f).mirror(0xff00).lw8(NAME([this](u8 data) {
	//  if (m_nr_08_dac_en)
	//      m_dac[0]->data_w(data);
	//}));
	map(0x000f, 0x000f).mirror(0xff00).lw8(NAME([this](u8 data) {
		if (m_nr_08_dac_en)
			m_dac[1]->data_w(data);
	}));
	map(0x00f3, 0x00f3).mirror(0xff00).lw8(NAME([this](u8 data) {
		if (m_nr_08_dac_en)
			m_dac[1]->data_w(data);
	}));
	map(0x00df, 0x00df).mirror(0xff00).lw8(NAME([this](u8 data) {
		if (m_nr_08_dac_en && port_dac_mono_AD_df_io_en())
		{
			m_dac[0]->data_w(data);
			m_dac[3]->data_w(data);
		}
	}));
	map(0x00fb, 0x00fb).mirror(0xff00).lw8(NAME([this](u8 data) {
		if (m_nr_08_dac_en && port_dac_mono_AD_fb_io_en())
		{
			m_dac[0]->data_w(data);
			m_dac[3]->data_w(data);
		}
	}));
	map(0x00b3, 0x00b3).mirror(0xff00).lw8(NAME([this](u8 data) {
		if (m_nr_08_dac_en && port_dac_mono_BC_b3_io_en())
		{
			m_dac[1]->data_w(data);
			m_dac[2]->data_w(data);
		}
	}));
	map(0x004f, 0x004f).mirror(0xff00).lw8(NAME([this](u8 data) {
		if (m_nr_08_dac_en)
			m_dac[2]->data_w(data);
	}));
	map(0x00f9, 0x00f9).mirror(0xff00).lw8(NAME([this](u8 data) {
		if (m_nr_08_dac_en)
			m_dac[2]->data_w(data);
	}));
	map(0x005f, 0x005f).mirror(0xff00).lw8(NAME([this](u8 data) {
		if (m_nr_08_dac_en)
			m_dac[3]->data_w(data);
	}));

	map(0x0000, 0xffff).view(m_io_shadow_view);
	subdevice<zxbus_device>("zxbus")->set_io_space(m_io_shadow_view[0], m_io_shadow_view[1]);
	m_io_shadow_view.select(0);
}

void specnext_state::map_regs(address_map &map)
{
	map(0x00, 0xff).rw(FUNC(specnext_state::reg_r), FUNC(specnext_state::reg_w));
}

INPUT_PORTS_START(specnext)
	PORT_INCLUDE(spec_plus)

	PORT_START("ISSUE")
	PORT_CONFNAME(0x03, 0x02, "Hardware Version" )
	PORT_CONFSETTING(0x00, "Issue 1 (TBBLUE)" )
	PORT_CONFSETTING(0x01, "Issue 2 (KS 1)" )
	PORT_CONFSETTING(0x02, "Issue 4 (KS 2)" )
	PORT_BIT(0xfc, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("VIDEO")
	PORT_CONFNAME(0x01, 0x00, "Video" )
	PORT_CONFSETTING(0x00, "HDMI" )
	PORT_CONFSETTING(0x01, "VGA" )
	PORT_BIT(0xfe, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("mouse_input1")
	PORT_BIT(0xff, 0, IPT_MOUSE_X) PORT_SENSITIVITY(40)

	PORT_START("mouse_input2")
	PORT_BIT(0xff, 0, IPT_MOUSE_Y) PORT_SENSITIVITY(40)

	PORT_START("mouse_input3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON4) PORT_NAME("Left mouse button") PORT_CODE(MOUSECODE_BUTTON2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON5) PORT_NAME("Right mouse button") PORT_CODE(MOUSECODE_BUTTON1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON6) PORT_NAME("Middle mouse button") PORT_CODE(MOUSECODE_BUTTON3)

	PORT_MODIFY("NMI")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("NMI MF") PORT_CODE(KEYCODE_F12) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(specnext_state::on_mf_nmi), 0)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("NMI DivMMC") PORT_CODE(KEYCODE_F11) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(specnext_state::on_divmmc_nmi), 0)
INPUT_PORTS_END

void specnext_state::machine_start()
{
	spectrum_128_state::machine_start();

	m_irq_line_timer = timer_alloc(FUNC(specnext_state::line_irq_on), this);
	m_spi_clock = timer_alloc(FUNC(specnext_state::spi_clock), this);

	m_regs_map->space(AS_PROGRAM).specific(m_next_regs);

	for (auto i = 0; i < 8; i++)
		m_bank_ram[i]->configure_entries(0, m_ram->size() / 0x2000, m_ram->pointer(), 0x2000);
	m_bank_boot_rom->configure_entry(0, memregion("maincpu")->base());

	const u8 *ram = m_ram->pointer() + 0x40000;
	m_ula_scr->set_host_ram_ptr(ram);
	m_tiles->set_host_ram_ptr(ram);
	m_layer2->set_host_ram_ptr(ram);
	m_lores->set_host_ram_ptr(ram);

	m_nr_02_hard_reset = 1;

	// Save
	save_pointer(NAME(m_page_shadow), 8);
	save_item(NAME(m_bootrom_en));
	save_item(NAME(m_port_ff_data));
	save_item(NAME(m_port_1ffd_special_old));
	save_item(NAME(m_port_1ffd_data));
	//save_item(NAME(m_port_7ffd_data));
	save_item(NAME(m_port_dffd_data));
	save_item(NAME(m_port_eff7_data));
	save_item(NAME(m_port_e7_reg));
	save_item(NAME(m_nr_register));
	save_item(NAME(m_port_e3_reg));
	save_item(NAME(m_divmmc_delayed_check));
	save_item(NAME(m_global_transparent));
	save_item(NAME(m_sram_rom));
	save_item(NAME(m_sram_rom3));
	save_item(NAME(m_sram_alt_128_n));
	save_pointer(NAME(m_mmu), 8);
	save_item(NAME(m_nr_02_bus_reset));
	save_item(NAME(m_nr_02_generate_mf_nmi));
	save_item(NAME(m_nr_02_generate_divmmc_nmi));
	save_item(NAME(m_nr_02_hard_reset));
	save_item(NAME(m_nr_02_reset_type));
	save_item(NAME(m_nr_03_machine_type));
	save_item(NAME(m_nr_03_user_dt_lock));
	save_item(NAME(m_nr_03_machine_timing));
	save_item(NAME(m_nr_03_config_mode));
	save_item(NAME(m_nr_04_romram_bank));
	save_item(NAME(m_nr_05_joy1));
	save_item(NAME(m_nr_05_joy0));
	save_item(NAME(m_nr_05_5060));
	save_item(NAME(m_nr_06_psg_mode));
	save_item(NAME(m_nr_06_ps2_mode));
	save_item(NAME(m_nr_06_button_m1_nmi_en));
	save_item(NAME(m_nr_06_button_drive_nmi_en));
	save_item(NAME(m_nr_06_hotkey_5060_en));
	save_item(NAME(m_nr_06_internal_speaker_beep));
	save_item(NAME(m_nr_06_hotkey_cpu_speed_en));
	save_item(NAME(m_nr_07_cpu_speed));
	save_item(NAME(m_nr_08_keyboard_issue2));
	save_item(NAME(m_nr_08_psg_turbosound_en));
	save_item(NAME(m_nr_08_port_ff_rd_en));
	save_item(NAME(m_nr_08_dac_en));
	save_item(NAME(m_nr_08_internal_speaker_en));
	save_item(NAME(m_nr_08_psg_stereo_mode));
	save_item(NAME(m_nr_08_contention_disable));
	save_item(NAME(m_nr_09_hdmi_audio_en));
	save_item(NAME(m_nr_09_sprite_tie));
	save_item(NAME(m_nr_09_psg_mono));
	save_item(NAME(m_nr_0a_mouse_dpi));
	save_item(NAME(m_nr_0a_mouse_button_reverse));
	save_item(NAME(m_nr_0a_divmmc_automap_en));
	save_item(NAME(m_nr_0a_mf_type));
	save_item(NAME(m_nr_0b_joy_iomode_0));
	save_item(NAME(m_nr_0b_joy_iomode));
	save_item(NAME(m_nr_0b_joy_iomode_en));
	save_item(NAME(m_nr_10_coreid));
	save_item(NAME(m_nr_11_video_timing));
	save_item(NAME(m_nr_10_flashboot));
	save_item(NAME(m_nr_12_layer2_active_bank));
	save_item(NAME(m_nr_13_layer2_shadow_bank));
	save_item(NAME(m_nr_14_global_transparent_rgb));
	save_item(NAME(m_nr_15_sprite_en));
	save_item(NAME(m_nr_15_sprite_over_border_en));
	save_item(NAME(m_nr_15_layer_priority));
	save_item(NAME(m_nr_15_sprite_border_clip_en));
	save_item(NAME(m_nr_15_sprite_priority));
	save_item(NAME(m_nr_15_lores_en));
	save_item(NAME(m_nr_16_layer2_scrollx));
	save_item(NAME(m_nr_17_layer2_scrolly));
	save_item(NAME(m_nr_18_layer2_clip_x1));
	save_item(NAME(m_nr_18_layer2_clip_x2));
	save_item(NAME(m_nr_18_layer2_clip_y1));
	save_item(NAME(m_nr_18_layer2_clip_y2));
	save_item(NAME(m_nr_18_layer2_clip_idx));
	save_item(NAME(m_nr_19_sprite_clip_x1));
	save_item(NAME(m_nr_19_sprite_clip_x2));
	save_item(NAME(m_nr_19_sprite_clip_y1));
	save_item(NAME(m_nr_19_sprite_clip_y2));
	save_item(NAME(m_nr_19_sprite_clip_idx));
	save_item(NAME(m_nr_1a_ula_clip_x1));
	save_item(NAME(m_nr_1a_ula_clip_x2));
	save_item(NAME(m_nr_1a_ula_clip_y1));
	save_item(NAME(m_nr_1a_ula_clip_y2));
	save_item(NAME(m_nr_1a_ula_clip_idx));
	save_item(NAME(m_nr_1b_tm_clip_x1));
	save_item(NAME(m_nr_1b_tm_clip_x2));
	save_item(NAME(m_nr_1b_tm_clip_y1));
	save_item(NAME(m_nr_1b_tm_clip_y2));
	save_item(NAME(m_nr_1b_tm_clip_idx));
	save_item(NAME(m_nr_22_line_interrupt_en));
	save_item(NAME(m_nr_23_line_interrupt));
	save_item(NAME(m_nr_26_ula_scrollx));
	save_item(NAME(m_nr_27_ula_scrolly));
	save_item(NAME(m_nr_2d_i2s_sample));
	save_item(NAME(m_nr_30_tm_scrollx));
	save_item(NAME(m_nr_31_tm_scrolly));
	save_item(NAME(m_nr_32_lores_scrollx));
	save_item(NAME(m_nr_33_lores_scrolly));
	save_item(NAME(m_nr_palette_idx));
	save_item(NAME(m_nr_palette_sub_idx));
	save_item(NAME(m_nr_42_ulanext_format));
	save_item(NAME(m_nr_43_palette_autoinc_disable));
	save_item(NAME(m_nr_43_palette_write_select));
	save_item(NAME(m_nr_43_active_sprite_palette));
	save_item(NAME(m_nr_43_active_layer2_palette));
	save_item(NAME(m_nr_43_active_ula_palette));
	save_item(NAME(m_nr_43_ulanext_en));
	save_item(NAME(m_nr_stored_palette_value));
	save_item(NAME(m_nr_4a_fallback_rgb));
	save_item(NAME(m_nr_4b_sprite_transparent_index));
	save_item(NAME(m_nr_4c_tm_transparent_index));
	save_item(NAME(m_nr_copper_addr));
	save_item(NAME(m_nr_copper_data_stored));
	save_item(NAME(m_nr_62_copper_mode));
	save_item(NAME(m_nr_64_copper_offset));
	save_item(NAME(m_nr_68_ula_en));
	save_item(NAME(m_nr_68_blend_mode));
	save_item(NAME(m_nr_68_cancel_extended_keys));
	save_item(NAME(m_nr_68_ula_fine_scroll_x));
	save_item(NAME(m_nr_68_ula_stencil_mode));
	save_item(NAME(m_nr_6a_lores_radastan));
	save_item(NAME(m_nr_6a_lores_radastan_xor));
	save_item(NAME(m_nr_6a_lores_palette_offset));
	save_item(NAME(m_nr_6b_tm_en));
	save_item(NAME(m_nr_6b_tm_control));
	save_item(NAME(m_nr_6c_tm_default_attr));
	save_item(NAME(m_nr_6e_tilemap_base));
	save_item(NAME(m_nr_6e_tilemap_base_7));
	save_item(NAME(m_nr_6f_tilemap_tiles));
	save_item(NAME(m_nr_6f_tilemap_tiles_7));
	save_item(NAME(m_nr_70_layer2_resolution));
	save_item(NAME(m_nr_70_layer2_palette_offset));
	save_item(NAME(m_nr_71_layer2_scrollx_msb));
	save_item(NAME(m_nr_7f_user_register_0));
	save_item(NAME(m_nr_80_expbus));
	save_item(NAME(m_nr_81_expbus_speed));
	save_item(NAME(m_nr_81_expbus_clken));
	save_item(NAME(m_nr_81_expbus_nmi_debounce_disable));
	save_item(NAME(m_nr_81_expbus_ula_override));
	save_item(NAME(m_nr_82_internal_port_enable));
	save_item(NAME(m_nr_83_internal_port_enable));
	save_item(NAME(m_nr_84_internal_port_enable));
	save_item(NAME(m_nr_85_internal_port_enable));
	save_item(NAME(m_nr_85_internal_port_reset_type));
	save_item(NAME(m_nr_86_bus_port_enable));
	save_item(NAME(m_nr_87_bus_port_enable));
	save_item(NAME(m_nr_88_bus_port_enable));
	save_item(NAME(m_nr_89_bus_port_enable));
	save_item(NAME(m_nr_89_bus_port_reset_type));
	save_item(NAME(m_nr_8a_bus_port_propagate));
	save_item(NAME(m_nr_8c_altrom));
	save_item(NAME(m_nr_8f_mapping_mode));
	save_item(NAME(m_nr_90_pi_gpio_o_en));
	save_item(NAME(m_nr_91_pi_gpio_o_en));
	save_item(NAME(m_nr_92_pi_gpio_o_en));
	save_item(NAME(m_nr_93_pi_gpio_o_en));
	save_item(NAME(m_nr_98_pi_gpio_o));
	save_item(NAME(m_nr_99_pi_gpio_o));
	save_item(NAME(m_nr_9a_pi_gpio_o));
	save_item(NAME(m_nr_9b_pi_gpio_o));
	save_item(NAME(m_nr_a0_pi_peripheral_en));
	save_item(NAME(m_nr_a2_pi_i2s_ctl));
	save_item(NAME(m_nr_a8_esp_gpio0_en));
	save_item(NAME(m_nr_a9_esp_gpio0));
	save_item(NAME(m_nr_b8_divmmc_ep_0));
	save_item(NAME(m_nr_b9_divmmc_ep_valid_0));
	save_item(NAME(m_nr_ba_divmmc_ep_timing_0));
	save_item(NAME(m_nr_bb_divmmc_ep_1));
	save_item(NAME(m_nr_c0_im2_vector));
	save_item(NAME(m_nr_c0_stackless_nmi));
	save_item(NAME(m_nr_c0_int_mode_pulse_0_im2_1));
	save_item(NAME(m_nr_c2_retn_address_lsb));
	save_item(NAME(m_nr_c3_retn_address_msb));
	save_item(NAME(m_nr_c4_int_en_0_expbus));
	save_item(NAME(m_nr_c6_int_en_2_654));
	save_item(NAME(m_nr_c6_int_en_2_210));
	save_item(NAME(m_nr_cc_dma_int_en_0_7));
	save_item(NAME(m_nr_cc_dma_int_en_0_10));
	save_item(NAME(m_nr_cd_dma_int_en_1));
	save_item(NAME(m_nr_ce_dma_int_en_2_654));
	save_item(NAME(m_nr_ce_dma_int_en_2_210));
	save_item(NAME(m_nr_d8_io_trap_fdc_en));
	save_item(NAME(m_nr_d9_iotrap_write));
	save_item(NAME(m_nr_da_iotrap_cause));
	save_item(NAME(m_nr_f0_select));
	save_item(NAME(m_nr_f0_xdna_en));
	save_item(NAME(m_nr_f0_xadc_en));
	save_item(NAME(m_nr_f0_xdev_cmd));
	save_item(NAME(m_nr_f0_xadc_eoc));
	save_item(NAME(m_nr_f0_xadc_eos));
	save_item(NAME(m_nr_f8_xadc_dwe));
	save_item(NAME(m_nr_f8_xadc_daddr));
	save_item(NAME(m_nr_f8_xadc_den));
	save_item(NAME(m_nr_f9_xadc_d0));
	save_item(NAME(m_nr_fa_xadc_d1));
	save_item(NAME(m_pulse_int_n));
	save_item(NAME(m_nr_09_scanlines));
	save_item(NAME(m_eff_nr_03_machine_timing));
	save_item(NAME(m_eff_nr_05_5060));
	save_item(NAME(m_eff_nr_05_scandouble_en));
	save_item(NAME(m_eff_nr_08_contention_disable));
	save_item(NAME(m_eff_nr_09_scanlines));
	save_item(NAME(m_port_123b_layer2_en));
	save_item(NAME(m_port_123b_layer2_map_wr_en));
	save_item(NAME(m_port_123b_layer2_map_rd_en));
	save_item(NAME(m_port_123b_layer2_map_shadow));
	save_item(NAME(m_port_123b_layer2_map_segment));
	save_item(NAME(m_port_123b_layer2_offset));
	save_item(NAME(m_port_bf3b_ulap_mode));
	save_item(NAME(m_port_bf3b_ulap_index));
	save_item(NAME(m_port_ff3b_ulap_en));
	save_item(NAME(m_ay_select));
	save_item(NAME(m_spi_clock_cycles));
	save_item(NAME(m_spi_clock_state));
	save_item(NAME(m_spi_mosi_dat));
	save_item(NAME(m_spi_miso_dat));
	save_item(NAME(m_i2c_scl_data));
	save_item(NAME(m_i2c_sda_data));
	save_item(NAME(m_irq_mask));
}

void specnext_state::reset_hard()
{
	m_nr_02_hard_reset = 0;
	m_bootrom_en = 1;

	m_dma->dma_mode_w(0);
	// nmi_mf = 0;
	// nmi_divmmc = 0;
	// nmi_expbus = 0;
	m_nr_80_expbus = 0;
	// expbus_eff_en = 0;
	// expbus_eff_disable_io = 0;
	// expbus_eff_disable_mem = 0;
	// expbus_eff_clken = 0;
	m_nr_8c_altrom = 0;
	// bus_iorq_ula = 0;
	// port_253b_rd_qq = 0;
	// sram_req_d = 1;
	// sram_wait_n = 1;
	// m_port_103b_dat = 0xff;
	// m_port_113b_dat = 0xff;
	port_e7_reg_w(0xff);
	// joy_iomode_pin7 = 1;
	// port_fe_border = 0;
	m_nr_8f_mapping_mode = 0;
	// m_port_123b_dat = 0;
	m_nr_02_bus_reset = 0;
	m_nr_03_machine_timing = 0b011;
	m_nr_03_user_dt_lock = 0;
	m_nr_03_config_mode = 1;
	m_nr_03_machine_type = 0b011;
	m_nr_04_romram_bank = 0;
	m_nr_05_joy0 = 0b001;
	m_nr_05_joy1 = 0b000;
	m_nr_05_5060 = 0;
	m_nr_06_hotkey_cpu_speed_en = 1;
	m_nr_06_hotkey_5060_en = 1;
	m_nr_06_button_drive_nmi_en = 0;
	m_nr_06_button_m1_nmi_en = 0;
	m_nr_06_ps2_mode = 0;
	m_nr_06_psg_mode = 0b00;
	nr_07_cpu_speed_w(0b00);
	m_nr_06_internal_speaker_beep = 0;
	m_nr_08_contention_disable = 0;
	m_nr_08_psg_stereo_mode = 0;
	m_nr_08_internal_speaker_en = 1;
	m_nr_08_dac_en = 0;
	m_nr_08_port_ff_rd_en = 0;
	m_nr_08_psg_turbosound_en = 0;
	m_nr_08_keyboard_issue2 = 0;
	m_nr_09_psg_mono = 0;
	m_nr_09_hdmi_audio_en = 1;
	m_nr_09_sprite_tie = 0;
	nr_0a_mf_type_w(0b00);
	m_nr_0a_divmmc_automap_en = 0;
	m_nr_0a_mouse_button_reverse = 0;
	m_nr_0a_mouse_dpi = 0b01;
	m_nr_10_flashboot = 0;
	m_nr_10_coreid = 0b00001;
	m_nr_7f_user_register_0 = 0xff;
	m_nr_81_expbus_ula_override = 0;
	m_nr_81_expbus_nmi_debounce_disable = 0;
	m_nr_81_expbus_clken = 0;
	m_nr_81_expbus_speed = 0b00;
	m_nr_82_internal_port_enable = 0xff;
	m_nr_83_internal_port_enable = 0xff;
	m_nr_84_internal_port_enable = 0xff;
	m_nr_85_internal_port_enable = 0x0f;
	m_nr_85_internal_port_reset_type = 1;
	m_nr_86_bus_port_enable = 0xff;
	m_nr_87_bus_port_enable = 0xff;
	m_nr_88_bus_port_enable = 0xff;
	m_nr_89_bus_port_enable = 0x0f;
	m_nr_89_bus_port_reset_type = 1;
	m_nr_8a_bus_port_propagate = 0x3f;
	m_nr_90_pi_gpio_o_en = 0;
	m_nr_91_pi_gpio_o_en = 0;
	m_nr_92_pi_gpio_o_en = 0;
	m_nr_93_pi_gpio_o_en = 0;
	m_nr_a8_esp_gpio0_en = 0;
	m_nr_a9_esp_gpio0 = 1;
	m_nr_f0_select = 1;
	m_nr_f0_xdna_en = 0;
	m_nr_f0_xadc_en = 0;
	m_nr_f0_xdev_cmd = 0x00;
	m_nr_f0_xadc_eoc = 0;
	m_nr_f0_xadc_eos = 0;
	m_nr_f8_xadc_dwe = 0;
	m_nr_f8_xadc_daddr = 0x00;
	m_nr_f8_xadc_den = 0;
	m_nr_f9_xadc_d0 = 0;
	m_nr_fa_xadc_d1 = 0;
	//m_nr_05_scandouble_en = 1;
	m_nr_09_scanlines = 0b00;
	m_nr_02_reset_type = 0b100;
	// nr_keymap_sel = 0;
	// nr_keymap_addr = 0;
	// hotkeys_1 = 0;
	// hotkeys_0 = 0;
	// video_timing_change_d = 0;
	m_eff_nr_03_machine_timing = 0b011;
	m_eff_nr_08_contention_disable = 0;
	// xdna_load = 0;
	// xdna_shift = 0;
	// xadc_reset = 0;
	// xadc_convst = 0;
	m_eff_nr_05_scandouble_en = 0;
	m_eff_nr_09_scanlines = 0;
	m_nr_2d_i2s_sample = 0b00;

	// m_port_00_data = 0;
	m_nr_0a_divmmc_automap_en = 1;

	update_video_mode();
}

void specnext_state::machine_reset()
{
	// TODO prevent from soft reset in config mode?
	spectrum_128_state::machine_reset();

	m_irq_line_timer->reset();

	if (m_nr_02_hard_reset)
		reset_hard();

	m_spi_clock->reset();
	m_spi_clock_cycles = 0;
	m_spi_clock_state = false;

	port_ff_w(0x00);
	m_divmmc_delayed_check = 0;

	m_dma->dma_mode_w(0);
	//z80_retn_seen_28_d  = 0;
	//im2_dma_delay  = 0;
	m_pulse_int_n  = 1;
	m_nr_c2_retn_address_lsb  = 0x00;
	m_nr_c3_retn_address_msb  = 0x00;
	//z80_stackless_retn_en  = 0;
	//nmi_mf  = 0;
	//nmi_divmmc  = 0;
	//nmi_expbus  = 0;
	//nmi_state  = S_NMI_IDLE;
	m_nr_80_expbus = (m_nr_80_expbus << 4) | (m_nr_80_expbus & 0x0f);
	m_nr_8c_altrom = (m_nr_8c_altrom << 4) | (m_nr_8c_altrom & 0x0f);
	//port_253b_rd_qq  = 0b00;
	//sram_req_d  = 0;
	m_i2c_scl_data = 1;
	m_i2c_sda_data = 1;
	port_e7_reg_w(0xff);
	//joy_iomode_pin7  = 1;

	port_7ffd_reg_w(0x00);
	m_port_1ffd_data = 0;
	m_port_1ffd_special_old = 0;
	m_port_dffd_data = 0;
	m_port_eff7_data = 0;
/* TODO don't use inherited
    port_fe_reg  = 0x00;
    port_ff_reg  = 0x00;
    port_7ffd_reg  = 0x00;
    port_dffd_reg  = 0x00;
    port_dffd_reg_6  = 0;
    port_1ffd_reg  = 0x00;
    port_1ffd_special_old  = 0;
    port_eff7_reg_2  = 0;
    port_eff7_reg_3  = 0;
*/
	m_nr_02_generate_mf_nmi  = 0;
	m_nr_02_generate_divmmc_nmi  = 0;
	m_nr_da_iotrap_cause  = 0x00;
	m_nr_d9_iotrap_write  = 0x00;
	port_123b_layer2_en_w(0);
	m_port_123b_layer2_map_wr_en  = 0;
	m_port_123b_layer2_map_rd_en  = 0;
	m_port_123b_layer2_map_shadow = 0;
	m_port_123b_layer2_map_segment  = 0x00;
	m_port_123b_layer2_offset  = 0x00;
	port_e3_reg_w(0x00);
	m_port_bf3b_ulap_mode  = 0x00;
	m_port_bf3b_ulap_index  = 0x00;
	port_ff3b_ulap_en_w(0);
	m_nr_register = 0x24;
	//copper_requester_d  = 0;
	//copper_req  = 0;
	//copper_nr_reg  = 0x00;
	//copper_nr_dat  = 0x00;
	//cpu_requester_d  = 0;
	//cpu_req  = 0;
	//cpu_nr_reg  = 0x00;
	//cpu_nr_dat  = 0x00;
	nr_07_cpu_speed_w(0b00);
	m_eff_nr_08_contention_disable  = 0;

	//expbus_eff_en  = expbus_en;
	//expbus_eff_disable_io  = expbus_disable_io;
	//expbus_eff_disable_mem  = expbus_disable_mem;
	//expbus_eff_clken  = expbus_clken;


	m_nr_06_hotkey_cpu_speed_en = 1;
	m_nr_06_hotkey_5060_en = 1;

	m_nr_08_contention_disable = 0;

	m_nr_09_sprite_tie = 0;

	m_nr_0b_joy_iomode_en = 0;
	m_nr_0b_joy_iomode = 0b00;
	m_nr_0b_joy_iomode_0 = 1;

	nr_12_layer2_active_bank_w(0b0001000);
	nr_13_layer2_shadow_bank_w(0b0001011);

	nr_14_global_transparent_rgb_w(0xe3);

	m_nr_15_lores_en = 0;
	nr_15_sprite_priority_w(0);
	nr_15_sprite_border_clip_en_w(0);
	m_nr_15_layer_priority = 0x00;
	nr_15_sprite_over_border_en_w(0);
	m_nr_15_sprite_en = 0;

	nr_16_layer2_scrollx_w(0x00);
	nr_17_layer2_scrolly_w(0x00);
	nr_18_layer2_clip_x1_w(0x00);
	nr_18_layer2_clip_x2_w(0xff);
	nr_18_layer2_clip_y1_w(0x00);
	nr_18_layer2_clip_y2_w(0xbf);
	m_nr_18_layer2_clip_idx = 0b00;

	nr_19_sprite_clip_x1_w(0x00);
	nr_19_sprite_clip_x2_w(0xff);
	nr_19_sprite_clip_y1_w(0x00);
	nr_19_sprite_clip_y2_w(0xbf);
	m_nr_19_sprite_clip_idx = 0b00;

	nr_1a_ula_clip_x1_w(0x00);
	nr_1a_ula_clip_x2_w(0xff);
	nr_1a_ula_clip_y1_w(0x00);
	nr_1a_ula_clip_y2_w(0xbf);
	m_nr_1a_ula_clip_idx = 0b00;

	nr_1b_tm_clip_x1_w(0x00);
	nr_1b_tm_clip_x2_w(0x9f);
	nr_1b_tm_clip_y1_w(0x00);
	nr_1b_tm_clip_y2_w(0xff);
	m_nr_1b_tm_clip_idx = 0b00;

	m_nr_22_line_interrupt_en = 0;
	m_nr_23_line_interrupt = 0x00;
	nr_26_ula_scrollx_w(0x00);
	nr_27_ula_scrolly_w(0x00);
	nr_30_tm_scrollx_w(0x00);
	nr_31_tm_scrolly_w(0x00);
	nr_32_lores_scrollx_w(0x00);
	nr_33_lores_scrolly_w(0x00);

	m_nr_palette_idx = 0x00;
	m_nr_palette_sub_idx = 0;

	nr_42_ulanext_format_w(0x07);

	m_nr_43_palette_autoinc_disable = 0;
	m_nr_43_palette_write_select = 0b000;
	nr_43_active_sprite_palette_w(0);
	nr_43_active_layer2_palette_w(0);
	nr_43_active_ula_palette_w(0);
	nr_43_ulanext_en_w(0);

	m_nr_stored_palette_value = 0x00;

	reg_w(0x4a, 0xe3); // m_nr_4a_fallback_rgb = 0xe3;
	nr_4b_sprite_transparent_index_w(0xe3);
	nr_4c_tm_transparent_index_w(0xf);

	nr_62_copper_mode_w(0b00);
	m_nr_copper_addr = 0x00;
	m_nr_copper_data_stored = 0x00;

	m_nr_64_copper_offset = 0x00;

	m_nr_68_ula_en = 1;
	m_nr_68_blend_mode = 0b00;
	m_nr_68_cancel_extended_keys = 0;
	nr_68_ula_fine_scroll_x_w(0);
	m_nr_68_ula_stencil_mode = 0;

	nr_6a_lores_radastan_w(0);
	nr_6a_lores_palette_offset_w(0x00);
	nr_6a_lores_radastan_xor_w(0);

	m_nr_6b_tm_en = 0;
	nr_6b_tm_control_w(0x00);
	nr_6c_tm_default_attr_w(0x00);
	nr_6e_tilemap_base_w(0, 0b101100);
	nr_6f_tilemap_tiles_w(0, 0b001100);

	nr_70_layer2_resolution_w(0b00);
	nr_70_layer2_palette_offset_w(0x00);

	nr_71_layer2_scrollx_msb_w(0);

	if (m_nr_85_internal_port_reset_type)
	{
		m_nr_82_internal_port_enable = 0xff;
		m_nr_83_internal_port_enable = 0xff;
		m_nr_84_internal_port_enable = 0xff;
		m_nr_85_internal_port_enable = 0xf;
	}

	if (!m_nr_89_bus_port_reset_type)
	{
		m_nr_86_bus_port_enable = 0xff;
		m_nr_87_bus_port_enable = 0xff;
		m_nr_88_bus_port_enable = 0xff;
		m_nr_89_bus_port_enable = 0xf;
	}

	m_nr_98_pi_gpio_o = 0xff;
	m_nr_99_pi_gpio_o = 0x01;
	m_nr_9a_pi_gpio_o = 0x00;
	m_nr_9b_pi_gpio_o = 0x00;

	m_nr_90_pi_gpio_o_en = 0x00;
	m_nr_91_pi_gpio_o_en = 0x00;
	m_nr_92_pi_gpio_o_en = 0x00;
	m_nr_93_pi_gpio_o_en = 0x00;

	m_nr_a0_pi_peripheral_en = 0x00;
	m_nr_a2_pi_i2s_ctl = 0x00;

	m_nr_a8_esp_gpio0_en = 0;
	m_nr_a9_esp_gpio0 = 1;

	m_nr_b8_divmmc_ep_0 = 0x83;
	m_nr_b9_divmmc_ep_valid_0 = 0x01;
	m_nr_ba_divmmc_ep_timing_0 = 0x00;
	m_nr_bb_divmmc_ep_1 = 0xcd;

	nr_c0_im2_vector_w(0x00);
	m_nr_c0_stackless_nmi = 0;
	m_maincpu->nmi_stackless_w(m_nr_c0_stackless_nmi);
	m_nr_c0_int_mode_pulse_0_im2_1 = 0;

	m_nr_c4_int_en_0_expbus = 1;

	m_nr_c6_int_en_2_654 = 0x00;
	m_nr_c6_int_en_2_210 = 0x00;

	m_nr_cc_dma_int_en_0_7 = 0;
	m_nr_cc_dma_int_en_0_10 = 0x00;
	m_nr_cd_dma_int_en_1 = 0x00;
	m_nr_ce_dma_int_en_2_654 = 0x00;
	m_nr_ce_dma_int_en_2_210 = 0x00;

	m_nr_d8_io_trap_fdc_en = 0;

	if (m_nr_03_config_mode)
		m_bootrom_en = 1;

	mmu_x2_w(0, 0xff);
	mmu_x2_w(2, 0x0a);
	mmu_x2_w(4, 0x04);
	mmu_x2_w(6, 0x00);

	m_ay_select = 0;
	m_irq_mask = 0;
}

static const gfx_layout bootrom_charlayout =
{
	8, 8,          // 8 x 8 characters */
	96,            // 96 characters */
	1,             // 1 bits per pixel */
	{ 0 },           // no bitplanes
	{ STEP8(0, 1) }, // x offsets
	{ STEP8(0, 8) }, // y offsets
	8 * 8          // every char takes 8 bytes
};

static GFXDECODE_START(gfx_tbblue)
	GFXDECODE_ENTRY("maincpu", 0x1584, bootrom_charlayout, 0xf7, 1)
GFXDECODE_END

void specnext_state::video_start()
{
	spectrum_128_state::video_start();

	address_space &prg = m_maincpu->space(AS_PROGRAM);
	prg.install_write_tap(0x0000, 0xbfff, "shadow_w", [this](offs_t offset, u8 &data, u8 mem_mask)
	{
		u8 bank8 = offset >> 13;
		if (~m_page_shadow[bank8])
		{
			u8 *to = m_ram->pointer() + (m_page_shadow[bank8] << 13);
			to[offset & 0x1fff] = data;
		}
	});
	prg.install_read_tap(0x0000, 0xffff, "mem_wait_r", [this](offs_t offset, u8 &data, u8 mem_mask)
	{
		// The 28MHz with core 3.0.5 is adding extra wait state to every instruction opcode fetch and memory read
		if (!machine().side_effects_disabled() && (m_nr_07_cpu_speed == 0b11))
		{
			m_maincpu->adjust_icount(-1);
		}
	});
}

void specnext_state::tbblue(machine_config &config)
{
	spectrum_128(config);
	config.device_remove("exp");
	config.device_remove("dma");
	// m_ram->set_default_size("1M").set_extra_options("2M");
	m_ram->set_default_size("2M").set_default_value(0);

	Z80N(config.replace(), m_maincpu, 28_MHz_XTAL / 8);
	m_maincpu->set_daisy_config(z80_daisy_chain);
	m_maincpu->set_m1_map(&specnext_state::map_fetch);
	m_maincpu->set_memory_map(&specnext_state::map_mem);
	m_maincpu->set_io_map(&specnext_state::map_io);
	m_maincpu->set_vblank_int("screen", FUNC(specnext_state::specnext_interrupt));
	m_maincpu->set_irq_acknowledge_callback(FUNC(specnext_state::irq_callback));
	m_maincpu->out_nextreg_cb().set(FUNC(specnext_state::reg_w));
	m_maincpu->in_nextreg_cb().set(FUNC(specnext_state::reg_r));
	m_maincpu->out_retn_seen_cb().set(FUNC(specnext_state::leave_nmi));
	m_maincpu->busack_cb().set(m_dma, FUNC(specnext_dma_device::bai_w));

	SPECNEXT_CTC(config, m_ctc, 28_MHz_XTAL / 8);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	SPECNEXT_DMA(config, m_dma, 28_MHz_XTAL / 8);
	m_dma->out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	m_dma->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_dma->in_mreq_callback().set(FUNC(specnext_state::dma_mreq_r));
	m_dma->out_mreq_callback().set([this](offs_t offset, u8 data) { m_program.write_byte(offset, data); });
	m_dma->in_iorq_callback().set([this](offs_t offset) { return m_io.read_byte(offset); });
	m_dma->out_iorq_callback().set([this](offs_t offset, u8 data) { m_io.write_byte(offset, data); });

	ADDRESS_MAP_BANK(config, m_regs_map).set_map(&specnext_state::map_regs).set_options(ENDIANNESS_LITTLE, 8, 8, 0);

	I2C_DS1307(config, m_i2c);
	m_i2c->sda_callback().set([this](int state) { m_i2c_sda_data = state & 1; });

	SPI_SDCARD(config, m_sdcard, 0);
	m_sdcard->set_prefer_sdhc();
	m_sdcard->spi_miso_callback().set(FUNC(specnext_state::spi_miso_w));

	SPEAKER(config.replace(), "speakers", 2).front();

	DAC_8BIT_R2R(config, m_dac[0], 0).add_route(ALL_OUTPUTS, "speakers", 0.75, 0);
	DAC_8BIT_R2R(config, m_dac[1], 0).add_route(ALL_OUTPUTS, "speakers", 0.75, 0);
	DAC_8BIT_R2R(config, m_dac[2], 0).add_route(ALL_OUTPUTS, "speakers", 0.75, 1);
	DAC_8BIT_R2R(config, m_dac[3], 0).add_route(ALL_OUTPUTS, "speakers", 0.75, 1);

	config.device_remove("ay_slot");
	for (auto i = 0; i < 3; ++i)
	{
		YM2149(config, m_ay[i], 14_MHz_XTAL / 8)
			.add_route(0, "speakers", 0.50, 0)
			.add_route(1, "speakers", 0.25, 0)
			.add_route(1, "speakers", 0.25, 1)
			.add_route(2, "speakers", 0.50, 1);
	}

	SPECNEXT_MULTIFACE(config, m_mf, 0);
	SPECNEXT_DIVMMC(config, m_divmmc, 0);

	zxbus_device &zxbus(ZXBUS(config, "zxbus", 0));
	ZXBUS_SLOT(config, "zxbus:1", 0, zxbus, zxbus_cards, nullptr);

	m_screen->set_raw(28_MHz_XTAL / 2, 456 << 1, 312,  { 0, 360 << 1, 0, 288 });
	m_screen->set_screen_update(FUNC(specnext_state::screen_update));
	m_screen->set_no_palette();

	PALETTE(config.replace(), m_palette, palette_device::BLACK, 512 * 4 + 1); // ulatm, l2s, +1 == fallback
	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_tbblue);
	SPECTRUM_ULA_UNCONTENDED(config.replace(), m_ula);

	SCREEN_ULA_NEXT (config, m_ula_scr, 0).set_palette(m_palette->device().tag(), 0x000, 0x100);
	SPECNEXT_LORES  (config, m_lores,   0).set_palette(m_palette->device().tag(), 0x000, 0x100);
	SPECNEXT_TILES  (config, m_tiles,   0).set_palette(m_palette->device().tag(), 0x200, 0x300);
	SPECNEXT_LAYER2 (config, m_layer2,  0).set_palette(m_palette->device().tag(), 0x400, 0x500);
	SPECNEXT_SPRITES(config, m_sprites, 0).set_palette(m_palette->device().tag(), 0x600, 0x700);

	SPECNEXT_COPPER(config, m_copper, 28_MHz_XTAL);
	m_copper->out_nextreg_cb().set(FUNC(specnext_state::reg_w));
	m_copper->set_in_until_pos_cb(FUNC(specnext_state::copper_until_pos_r));

	config.device_remove("snapshot");
}

ROM_START(tbblue)
	ROM_REGION(0x4000, "maincpu", ROMREGION_ERASEFF)
	ROM_DEFAULT_BIOS("boot")

	ROM_SYSTEM_BIOS(0, "boot", "BootROM")
	ROMX_LOAD( "bootrom.fa55357d.bin", 0x0000, 0x2000, CRC(ccbd55ba) SHA1(8b3c2a301f486904d1c74929b94845a7731bf230), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "bootab", "BootROM - AntiBrick")
	ROMX_LOAD( "bootrom-ab.cfffa702.bin", 0x0000, 0x2000, CRC(1d16e9d4) SHA1(6f9c8771e5a9ef5a6b52a31b2e65f0698f0f5cfa), ROM_BIOS(1))
ROM_END

} // Anonymous namespace

/*    YEAR   NAME     PARENT    COMPAT  MACHINE  INPUT      CLASS            INIT         COMPANY                                            FULLNAME                     FLAGS */
COMP( 2017,  tbblue,  spec128,  0,      tbblue,  specnext,  specnext_state,  empty_init,  "SpecNext Ltd., Victor Trucco, Fabio Belavenuto",  "ZX Spectrum Next: TBBlue",  MACHINE_SUPPORTS_SAVE )



specpls3.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Kevin Thacker
/***************************************************************************

    NOTE: ****** Specbusy: press N, R, or E to boot *************


        Spectrum/Inves/TK90X etc. memory map:

    CPU:
        0000-3fff ROM
        4000-ffff RAM

        Spectrum 128/+2/+2a/+3 memory map:

        CPU:
                0000-3fff Banked ROM/RAM (banked rom only on 128/+2)
                4000-7fff Banked RAM
                8000-bfff Banked RAM
                c000-ffff Banked RAM

        TS2068 memory map: (Can't have both EXROM and DOCK active)
        The 8K EXROM can be loaded into multiple pages.

    CPU:
                0000-1fff     ROM / EXROM / DOCK (Cartridge)
                2000-3fff     ROM / EXROM / DOCK
                4000-5fff \
                6000-7fff  \
                8000-9fff  |- RAM / EXROM / DOCK
                a000-bfff  |
                c000-dfff  /
                e000-ffff /


Interrupts:

Changes:

29/1/2000   KT -    Implemented initial +3 emulation.
30/1/2000   KT -    Improved input port decoding for reading and therefore
            correct keyboard handling for Spectrum and +3.
31/1/2000   KT -    Implemented buzzer sound for Spectrum and +3.
            Implementation copied from Paul Daniel's Jupiter driver.
            Fixed screen display problems with dirty chars.
            Added support to load .Z80 snapshots. 48k support so far.
13/2/2000   KT -    Added Interface II, Kempston, Fuller and Mikrogen
            joystick support.
17/2/2000   DJR -   Added full key descriptions and Spectrum+ keys.
            Fixed Spectrum +3 keyboard problems.
17/2/2000   KT -    Added tape loading from WAV/Changed from DAC to generic
            speaker code.
18/2/2000   KT -    Added tape saving to WAV.
27/2/2000   KT -    Took DJR's changes and added my changes.
27/2/2000   KT -    Added disk image support to Spectrum +3 driver.
27/2/2000   KT -    Added joystick I/O code to the Spectrum +3 I/O handler.
14/3/2000   DJR -   Tape handling dipswitch.
26/3/2000   DJR -   Snapshot files are now classified as snapshots not
            cartridges.
04/4/2000   DJR -   Spectrum 128 / +2 Support.
13/4/2000   DJR -   +4 Support (unofficial 48K hack).
13/4/2000   DJR -   +2a Support (rom also used in +3 models).
13/4/2000   DJR -   TK90X, TK95 and Inves support (48K clones).
21/4/2000   DJR -   TS2068 and TC2048 support (TC2048 Supports extra video
            modes but doesn't have bank switching or sound chip).
09/5/2000   DJR -   Spectrum +2 (France, Spain), +3 (Spain).
17/5/2000   DJR -   Dipswitch to enable/disable disk drives on +3 and clones.
27/6/2000   DJR -   Changed 128K/+3 port decoding (sound now works in Zub 128K).
06/8/2000   DJR -   Fixed +3 Floppy support
10/2/2001   KT  -   Re-arranged code and split into each model emulated.
            Code is split into 48k, 128k, +3, tc2048 and ts2048
            segments. 128k uses some of the functions in 48k, +3
            uses some functions in 128, and tc2048/ts2048 use some
            of the functions in 48k. The code has been arranged so
            these functions come in some kind of "override" order,
            read functions changed to use  READ8_HANDLER and write
            functions changed to use WRITE8_HANDLER.
            Added Scorpion256 preliminary.
18/6/2001   DJR -   Added support for Interface 2 cartridges.
xx/xx/2001  KS -    TS-2068 sound fixed.
            Added support for DOCK cartridges for TS-2068.
            Added Spectrum 48k Psycho modified rom driver.
            Added UK-2086 driver.
23/12/2001  KS -    48k machines are now able to run code in screen memory.
                Programs which keep their code in screen memory
                like monitors, tape copiers, decrunchers, etc.
                works now.
                Fixed problem with interrupt vector set to 0xffff (much
            more 128k games works now).
                A useful used trick on the Spectrum is to set
                interrupt vector to 0xffff (using the table
                which contain 0xff's) and put a byte 0x18 hex,
                the opcode for JR, at this address. The first
                byte of the ROM is a 0xf3 (DI), so the JR will
                jump to 0xfff4, where a long JP to the actual
                interrupt routine is put. Due to unideal
                bankswitching in MAME this JP were to 0001 what
                causes Spectrum to reset. Fixing this problem
                made much more software running (i.e. Paperboy).
            Corrected frames per second value for 48k and 128k
            Sinclair machines.
                There are 50.08 frames per second for Spectrum
                48k what gives 69888 cycles for each frame and
                50.021 for Spectrum 128/+2/+2A/+3 what gives
                70908 cycles for each frame.
            Remapped some Spectrum+ keys.
                Pressing F3 to reset was setting 0xf7 on keyboard
                input port. Problem occurred for snapshots of
                some programs where it was read as pressing
                key 4 (which is exit in Tapecopy by R. Dannhoefer
                for example).
            Added support to load .SP snapshots.
            Added .BLK tape images support.
                .BLK files are identical to .TAP ones, extension
                is an only difference.
08/03/2002  KS -    #FF port emulation added.
                Arkanoid works now, but is not playable due to
                completely messed timings.

Initialisation values used when determining which model is being emulated:
 48K        Spectrum doesn't use either port.
 128K/+2    Bank switches with port 7ffd only.
 +3/+2a     Bank switches with both ports.

Notes:
 1. No contented memory.
 2. No hi-res colour effects (need contended memory first for accurate timing).
 3. Multiface 1 and Interface 1 not supported.
 4. Horace and the Spiders cartridge doesn't run properly.
 5. Tape images not supported:
    .TZX, .SPC, .ITM, .PAN, .TAP(Warajevo), .VOC, .ZXS.
 6. Snapshot images not supported:
    .ACH, .PRG, .RAW, .SEM, .SIT, .SNX, .ZX, .ZXS, .ZX82.
 7. 128K emulation is not perfect - the 128K machines crash and hang while
    running quite a lot of games.
 8. Disk errors occur on some +3 games.
 9. Video hardware of all machines is timed incorrectly.
10. EXROM and HOME cartridges are not emulated.
11. The TK90X and TK95 roms output 0 to port #df on start up.
12. The purpose of this port is unknown (probably display mode as TS2068) and
    thus is not emulated.

Very detailed infos about the ZX Spectrum +3e can be found at

http://www.z88forever.org.uk/zxplus3e/

*******************************************************************************/

#include "emu.h"
#include "specpls3.h"

#include "bus/spectrum/ay/slot.h"

#include "screen.h"
#include "softlist_dev.h"

#include "formats/ipf_dsk.h"
#include "formats/tzx_cas.h"

#define VERBOSE 0
#include "logmacro.h"

/****************************************************************************************************/
/* Spectrum + 3 specific functions */
/* This driver uses some of the spectrum_128 functions. The +3 is similar to a spectrum 128
but with a disc drive */


static const int spectrum_plus3_memory_selections[]=
{
	0, 1, 2, 3,
	4, 5, 6, 7,
	4, 5, 6, 3,
	4, 7, 6, 3
};

void specpls3_state::port_3ffd_w(offs_t offset, uint8_t data)
{
	if (m_upd765.found()) m_upd765->fifo_w(data);

	/* mface3 needs to see this port */
	if (m_exp) m_exp->iorq_w(offset | 0x3000, data);
}

uint8_t specpls3_state::port_3ffd_r()
{
	return m_upd765.found() ? m_upd765->fifo_r() : 0xff;
}


uint8_t specpls3_state::port_2ffd_r()
{
	return m_upd765.found() ? m_upd765->msr_r() : 0xff;
}


void specpls3_state::plus3_update_memory()
{
	m_screen->update_now();
	if (m_port_7ffd_data & 8)
	{
		LOG("+3 SCREEN 1: BLOCK 7\n");
		m_screen_location = m_ram->pointer() + (7 << 14);
	}
	else
	{
		LOG("+3 SCREEN 0: BLOCK 5\n");
		m_screen_location = m_ram->pointer() + (5 << 14);
	}

	if (m_port_1ffd_data & 0x01)
	{
		/* Extended memory paging */
		int MemorySelection = (m_port_1ffd_data >> 1) & 0x03;
		const int *memory_selection = &spectrum_plus3_memory_selections[(MemorySelection << 2)];
		m_bank_ram[0]->set_entry(memory_selection[0]);
		m_bank_ram[1]->set_entry(memory_selection[1]);
		m_bank_ram[2]->set_entry(memory_selection[2]);
		m_bank_ram[3]->set_entry(memory_selection[3]);
		LOG("extended memory paging: %02x\n", MemorySelection);
	}
	else
	{
		m_bank_rom[0]->set_entry(BIT(m_port_7ffd_data, 4) | ((m_port_1ffd_data >> 1) & 0x02));

		/* Reset memory between 0x4000 - 0xbfff in case extended paging was being used */
		/* Bank 5 in 0x4000 - 0x7fff */
		m_bank_ram[1]->set_entry(5);
		/* Bank 2 in 0x8000 - 0xbfff */
		m_bank_ram[2]->set_entry(2);
		/* select ram at 0x0c000-0x0ffff */
		int ram_page = m_port_7ffd_data & 0x07;
		m_bank_ram[3]->set_entry(ram_page);
		LOG("RAM at 0xc000: %02x\n", ram_page);
	}
	m_ula->bank3_pg_w(m_bank_ram[3]->entry());
}


void specpls3_state::rom_w(offs_t offset, uint8_t data)
{
	if (m_exp->romcs())
		m_exp->mreq_w(offset, data);
	else if (m_port_1ffd_data & 0x01)
		((u8*)m_bank_ram[0]->base())[offset] = data;
}

uint8_t specpls3_state::rom_r(offs_t offset)
{
	return m_exp->romcs()
		? m_exp->mreq_r(offset)
		: (m_port_1ffd_data & 0x01)
		  ? ((u8*)m_bank_ram[0]->base())[offset]
		  : ((u8*)m_bank_rom[0]->base())[offset];
}

void specpls3_state::port_7ffd_w(offs_t offset, uint8_t data)
{
	/* D0-D2 - RAM page located at 0x0c000-0x0ffff */
	/* D3    - Screen select (screen 0 in ram page 5, screen 1 in ram page 7 */
	/* D4    - ROM select low bit - which rom paged into 0x0000-0x03fff */
	/* D5    - Disable paging (permanent until reset) */

	/* mface3 needs to see this port */
	if (m_exp) m_exp->iorq_w(offset | 0x4000, data);

	/* paging disabled? */
	if (m_port_7ffd_data & 0x20) return;

	/* store new state */
	m_port_7ffd_data = data;

	/* update memory */
	plus3_update_memory();
}

void specpls3_state::port_1ffd_w(offs_t offset, uint8_t data)
{
	/* D0=0 - Normal ROM/RAM paging mode */
	/*   D1 - Not used */
	/*   D2 - Rom select high bit */
	/* D0=1 - Special RAM paging mode (all-RAM CP/M modes) */
	/*  D1-D2 - Special paging mode 0-3 */
	/* D3   - Disk motor on/off */
	/* D4   - Parallel port strobe */

	if (m_upd765.found())
	{
		for (auto &flop : m_flop)
			if (flop->get_device()) flop->get_device()->mon_w(!BIT(data, 3));
	}

	/* mface3 needs to see this port */
	if (m_exp) m_exp->iorq_w(offset | 0x1000, data);

	/* paging disabled? */
	if ((m_port_7ffd_data & 0x20)==0)
	{
		/* no */
		m_port_1ffd_data = data;
		plus3_update_memory();
	}
	else
	{
		/* yes, update only non-memory related */
		m_port_1ffd_data &= 0x7;
		m_port_1ffd_data |= data & 0xf8;
	}
}

void specpls3_state::video_start()
{
	spectrum_128_state::video_start();
}

/* ports are not decoded full.
The function decodes the ports appropriately */
void specpls3_state::plus3_io(address_map &map)
{
	map(0x0000, 0xffff).rw(m_exp, FUNC(spectrum_expansion_slot_device::iorq_r), FUNC(spectrum_expansion_slot_device::iorq_w));
	map(0x0000, 0x0000).select(0xfffe).rw(FUNC(specpls3_state::spectrum_ula_r), FUNC(specpls3_state::spectrum_ula_w));
	map(0x4000, 0x4000).select(0x3ffd).w(FUNC(specpls3_state::port_7ffd_w));
	map(0x8000, 0x8000).mirror(0x3ffd).w("ay_slot", FUNC(ay_slot_device::data_w));
	map(0xc000, 0xc000).mirror(0x3ffd).rw("ay_slot", FUNC(ay_slot_device::data_r), FUNC(ay_slot_device::address_w));
	map(0x1000, 0x1000).select(0x0ffd).w(FUNC(specpls3_state::port_1ffd_w));
	map(0x2000, 0x2000).mirror(0x0ffd).r(FUNC(specpls3_state::port_2ffd_r));
	map(0x3000, 0x3000).select(0x0ffd).rw(FUNC(specpls3_state::port_3ffd_r), FUNC(specpls3_state::port_3ffd_w));
}

void specpls3_state::plus3_mem(address_map &map)
{
	map(0x0000, 0x3fff).rw(FUNC(specpls3_state::rom_r), FUNC(specpls3_state::rom_w));
	map(0x4000, 0x7fff).rw(FUNC(specpls3_state::spectrum_128_ram_r<1>), FUNC(specpls3_state::spectrum_128_ram_w<1>));
	map(0x8000, 0xbfff).rw(FUNC(specpls3_state::spectrum_128_ram_r<2>), FUNC(specpls3_state::spectrum_128_ram_w<2>));
	map(0xc000, 0xffff).rw(FUNC(specpls3_state::spectrum_128_ram_r<3>), FUNC(specpls3_state::spectrum_128_ram_w<3>));
}

INPUT_PORTS_START( spec_plus2a )
	PORT_INCLUDE( spec_plus )

	PORT_MODIFY("CONFIG")
	PORT_CONFNAME( 0x80, 0x00, "Hardware Version" )
	PORT_CONFSETTING(    0x00, "Issue 2" )
	PORT_CONFSETTING(    0x80, "Issue 3" )
	PORT_BIT(0x7f, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

void specpls3_state::machine_start()
{
	spectrum_128_state::machine_start();

	save_item(NAME(m_port_1ffd_data));

	// reconfigure ROMs
	memory_region *rom = memregion("maincpu");
	m_bank_rom[0]->configure_entries(0, 4, rom->base() + 0x10000, 0x4000);

	m_bank_ram[0]->configure_entries(0, m_ram->size() / 0x4000, m_ram->pointer(), 0x4000);
}

void specpls3_state::machine_reset()
{
	/* Initial configuration */
	m_port_fe_data = -1;
	m_port_7ffd_data = 0;
	m_port_1ffd_data = 0;
	plus3_update_memory();
}

void specpls3_state::plus3_us_w(uint8_t data)
{
	// US1 is not connected, so US0 alone selects either drive
	floppy_image_device *flop = m_flop[data & 1]->get_device();
	m_upd765->set_floppy(flop);
	if (flop) flop->ds_w(data & 1);
}

static void specpls3_floppies(device_slot_interface &device)
{
	device.option_add("3ssdd", FLOPPY_3_SSDD);
	device.option_add("35ssdd", FLOPPY_35_SSDD);
}

void specpls3_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_IPF_FORMAT);
}

/* F4 Character Displayer */
static const gfx_layout spectrum_charlayout =
{
	8, 8,          /* 8 x 8 characters */
	96,            /* 96 characters */
	1,             /* 1 bits per pixel */
	{ 0 },         /* no bitplanes */
	{STEP8(0, 1)}, /* x offsets */
	{STEP8(0, 8)}, /* y offsets */
	8*8            /* every char takes 8 bytes */
};

static GFXDECODE_START( specpls3 )
	GFXDECODE_ENTRY( "maincpu", 0x1fd00, spectrum_charlayout, 7, 8 )
GFXDECODE_END


void specpls3_state::spectrum_plus2(machine_config &config)
{
	spectrum_128(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &specpls3_state::plus3_mem);
	m_maincpu->set_addrmap(AS_IO, &specpls3_state::plus3_io);
	m_maincpu->refresh_cb().remove();
	m_maincpu->nomreq_cb().remove();

	subdevice<gfxdecode_device>("gfxdecode")->set_info(specpls3);

	SPECTRUM_ULA_PLUS2A(config.replace(), m_ula);
	m_ula->set_z80(m_maincpu);
	m_ula->set_screen(m_screen, get_screen_area());

	SPECTRUM_EXPANSION_SLOT(config.replace(), m_exp, specpls3_expansion_devices, nullptr);
	m_exp->irq_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_exp->nmi_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);
	// these models don't have floating bus
	m_exp->fb_r_handler().set([]() { return 0xff; });
}

void specpls3_state::spectrum_plus3(machine_config &config)
{
	spectrum_plus2(config);

	UPD765A(config, m_upd765, 16_MHz_XTAL / 4, true, false); // clocked through SED9420
	m_upd765->us_wr_callback().set(FUNC(specpls3_state::plus3_us_w));
	FLOPPY_CONNECTOR(config, "upd765:0", specpls3_floppies, "3ssdd", specpls3_state::floppy_formats).enable_sound(true); // internal drive
	FLOPPY_CONNECTOR(config, "upd765:1", specpls3_floppies, nullptr, specpls3_state::floppy_formats).enable_sound(true); // external drive

	SOFTWARE_LIST(config, "flop_list").set_original("specpls3_flop");
}

/***************************************************************************

  Game driver(s)

***************************************************************************/

/* Amstrad built +2A/+2B/+3/+3B models:

    +2A/B has built-in tape "datacorder", +3/B has built-in 3" fdd
    +2A/+3 use common z70830 pcb with v4.0 rom  (fdc etc. unpopulated on +2A)
    +2B/+3B use unique z70833/z70835 pcbs but use same v4.1 rom

   Note, +2 (non-A/B, aka "grey case") although Amstrad built is essentially a re-cased Sinclair 128K, see spec128.cpp
*/
ROM_START(specpl2a)
	ROM_REGION(0x20000,"maincpu",0)
	ROM_SYSTEM_BIOS( 0, "en", "English v4.0" )  // +2A
	ROMX_LOAD("40092.ic7",0x10000,0x8000, CRC(9bc85686) SHA1(5992daf925f6e225fc0d01f7640282954d092ef4), ROM_BIOS(0))
	ROMX_LOAD("40093.ic8",0x18000,0x8000, CRC(db551783) SHA1(a0432adcca03f849fb39b6dce6414740cf4aecd2), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "sp", "Spanish v4.0" )
	ROMX_LOAD("40094.ic7",0x10000,0x8000, CRC(392242fb) SHA1(976ae88951f8d1beb5d107f048950118a7133823), ROM_BIOS(1))
	ROMX_LOAD("40101.ic8",0x18000,0x8000, CRC(5daaae01) SHA1(09ca25b4dbec064a4964ab7a41d48404199afd77), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "enb", "English v4.1" )  // +2B
	ROMX_LOAD("40092u.ic7",0x10000,0x8000, CRC(80808d82) SHA1(b9e88ec18f844ce42ecb7802d82c2bda65f9c4f2), ROM_BIOS(2))
	ROMX_LOAD("40093u.ic8",0x18000,0x8000, CRC(61f2b50c) SHA1(d062765ceb1f3cd2c94ea51cb737cac7ad6151b4), ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "spb", "Spanish v4.1" )
	ROMX_LOAD("40094s.ic7",0x10000,0x8000, CRC(9d102acf) SHA1(c525bd23f79ca968d34a0efdcc47b2eb342007f5), ROM_BIOS(3))
	ROMX_LOAD("40101s.ic8",0x18000,0x8000, CRC(1408ddce) SHA1(56eb124d44ee8c8daef130be4d7e735ec412c4ba), ROM_BIOS(3))
ROM_END

ROM_START(specpls3)
	ROM_REGION(0x20000,"maincpu",0)
	ROM_SYSTEM_BIOS( 0, "en", "English v4.0" )  // +3
	ROMX_LOAD("40092.ic7",0x10000,0x8000, CRC(9bc85686) SHA1(5992daf925f6e225fc0d01f7640282954d092ef4), ROM_BIOS(0))
	ROMX_LOAD("40093.ic8",0x18000,0x8000, CRC(db551783) SHA1(a0432adcca03f849fb39b6dce6414740cf4aecd2), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "sp", "Spanish v4.0" )
	ROMX_LOAD("40094.ic7",0x10000,0x8000, CRC(392242fb) SHA1(976ae88951f8d1beb5d107f048950118a7133823), ROM_BIOS(1))
	ROMX_LOAD("40101.ic8",0x18000,0x8000, CRC(5daaae01) SHA1(09ca25b4dbec064a4964ab7a41d48404199afd77), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "enb", "English v4.1" )  // +3B
	ROMX_LOAD("40092u.ic7",0x10000,0x8000, CRC(80808d82) SHA1(b9e88ec18f844ce42ecb7802d82c2bda65f9c4f2), ROM_BIOS(2))
	ROMX_LOAD("40093u.ic8",0x18000,0x8000, CRC(61f2b50c) SHA1(d062765ceb1f3cd2c94ea51cb737cac7ad6151b4), ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "spb", "Spanish v4.1" )
	ROMX_LOAD("40094s.ic7",0x10000,0x8000, CRC(9d102acf) SHA1(c525bd23f79ca968d34a0efdcc47b2eb342007f5), ROM_BIOS(3))
	ROMX_LOAD("40101s.ic8",0x18000,0x8000, CRC(1408ddce) SHA1(56eb124d44ee8c8daef130be4d7e735ec412c4ba), ROM_BIOS(3))
	ROM_SYSTEM_BIOS( 4, "4ms", "Customize 3.5\" 4ms" )  // unofficial 3.5" fdd hacks by Cristian Secară (v4.1 english)
	ROMX_LOAD("p3_01_4m.rom",0x10000,0x8000, CRC(ad99380a) SHA1(4e5d114b72d464cefdde0566457f52a3c0c1cae2), ROM_BIOS(4))
	ROMX_LOAD("p3_23_4m.rom",0x18000,0x8000, CRC(07727895) SHA1(752cdd6a083ab9910348995e483541d60bb6372b), ROM_BIOS(4))
	ROM_SYSTEM_BIOS( 5, "12ms", "Customize 3.5\" 12ms" )
	ROMX_LOAD("p3_01_cm.rom",0x10000,0x8000, CRC(ad99380a) SHA1(4e5d114b72d464cefdde0566457f52a3c0c1cae2), ROM_BIOS(5))
	ROMX_LOAD("p3_23_cm.rom",0x18000,0x8000, CRC(61f2b50c) SHA1(d062765ceb1f3cd2c94ea51cb737cac7ad6151b4), ROM_BIOS(5))
ROM_END

ROM_START(specpl3e)
	ROM_REGION(0x20000,"maincpu",0)
	ROM_SYSTEM_BIOS( 0, "en", "English" )
	ROMX_LOAD("roma-en.rom",0x10000,0x8000, CRC(2d533344) SHA1(5ff2dae32eb745d87e0b54c595d1d20a866f316f), ROM_BIOS(0))
	ROMX_LOAD("romb-en.rom",0x18000,0x8000, CRC(ef8d5d92) SHA1(983aa53aa76e25a3af123c896016bacf6829b72b), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "sp", "Spanish" )
	ROMX_LOAD("roma-es.rom",0x10000,0x8000, CRC(ba694b4b) SHA1(d15d9e43950483cffc79f1cfa89ecb114a88f6c2), ROM_BIOS(1))
	ROMX_LOAD("romb-es.rom",0x18000,0x8000, CRC(61ed94db) SHA1(935b14c13db75d872de8ad0d591aade0adbbc355), ROM_BIOS(1))
ROM_END

ROM_START(sp3e8bit)
	ROM_REGION(0x20000,"maincpu",0)
	ROM_SYSTEM_BIOS( 0, "en", "English" )
	ROMX_LOAD("3e8biten.rom",0x10000,0x10000, CRC(beee3bf6) SHA1(364ec903916282d5401901c5fb0cb93a142038b3), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "sp", "Spanish" )
	ROMX_LOAD("3e8bites.rom",0x10000,0x10000, CRC(cafe4c35) SHA1(8331d273d29d3e37ec1324053bb050874d2c1434), ROM_BIOS(1))
ROM_END

ROM_START(sp3ezcf)
	ROM_REGION(0x20000,"maincpu",0)
	ROM_SYSTEM_BIOS( 0, "en", "English" )
	ROMX_LOAD("3ezcfen.rom",0x10000,0x10000, CRC(43993f11) SHA1(27cbfbe8b5ef9eec6056026fa0b84fe158ba2f45), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "sp", "Spanish" )
	ROMX_LOAD("3ezcfes.rom",0x10000,0x10000, CRC(1325a0d7) SHA1(521cf47e10f46c8a621c8889ef1f008454c7e10b), ROM_BIOS(1))
ROM_END

ROM_START(sp3eata)
	ROM_REGION(0x20000,"maincpu",0)
	ROM_SYSTEM_BIOS( 0, "en", "English" )
	ROMX_LOAD("3ezxaen.rom",0x10000,0x10000, CRC(dfb676dc) SHA1(37618bc66ae33dbf686be8a92867e4a9144b65dc), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "sp", "Spanish" )
	ROMX_LOAD("3ezxaes.rom",0x10000,0x10000, CRC(8f0ae91a) SHA1(71693e18b30c90914be58cba26682ca025c924ea), ROM_BIOS(1))
ROM_END

/*    YEAR  NAME      PARENT    COMPAT  MACHINE         INPUT       CLASS           INIT        COMPANY        FULLNAME                         FLAGS */
COMP( 1987, specpl2a, 0,        0,     spectrum_plus2, spec_plus2a, specpls3_state, empty_init, "Amstrad plc", "ZX Spectrum +2a",              0 )
COMP( 1987, specpls3, specpl2a, 0,     spectrum_plus3, spec_plus2a, specpls3_state, empty_init, "Amstrad plc", "ZX Spectrum +3",               0 )
COMP( 2000, specpl3e, 0,        0,     spectrum_plus3, spec_plus2a, specpls3_state, empty_init, "Amstrad plc", "ZX Spectrum +3e",              MACHINE_UNOFFICIAL )
COMP( 2002, sp3e8bit, 0,        0,     spectrum_plus3, spec_plus2a, specpls3_state, empty_init, "Amstrad plc", "ZX Spectrum +3e 8bit IDE",     MACHINE_UNOFFICIAL )
COMP( 2002, sp3eata,  0,        0,     spectrum_plus3, spec_plus2a, specpls3_state, empty_init, "Amstrad plc", "ZX Spectrum +3e 8bit ZXATASP", MACHINE_UNOFFICIAL )
COMP( 2002, sp3ezcf,  0,        0,     spectrum_plus3, spec_plus2a, specpls3_state, empty_init, "Amstrad plc", "ZX Spectrum +3e 8bit ZXCF",    MACHINE_UNOFFICIAL )



spectrum.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Kevin Thacker
/***************************************************************************

    NOTE: ****** Specbusy: press N, R, or E to boot *************


        Spectrum/Inves/TK90X etc. memory map:

    CPU:
        0000-3fff ROM
        4000-ffff RAM

        Spectrum 128/+2/+2a/+3 memory map:

        CPU:
                0000-3fff Banked ROM/RAM (banked rom only on 128/+2)
                4000-7fff Banked RAM
                8000-bfff Banked RAM
                c000-ffff Banked RAM

        TS2068 memory map: (Can't have both EXROM and DOCK active)
        The 8K EXROM can be loaded into multiple pages.

    CPU:
                0000-1fff     ROM / EXROM / DOCK (Cartridge)
                2000-3fff     ROM / EXROM / DOCK
                4000-5fff \
                6000-7fff  \
                8000-9fff  |- RAM / EXROM / DOCK
                a000-bfff  |
                c000-dfff  /
                e000-ffff /


Interrupts:

Changes:

29/1/2000   KT -    Implemented initial +3 emulation.
30/1/2000   KT -    Improved input port decoding for reading and therefore
            correct keyboard handling for Spectrum and +3.
31/1/2000   KT -    Implemented buzzer sound for Spectrum and +3.
            Implementation copied from Paul Daniel's Jupiter driver.
            Fixed screen display problems with dirty chars.
            Added support to load .Z80 snapshots. 48k support so far.
13/2/2000   KT -    Added Interface II, Kempston, Fuller and Mikrogen
            joystick support.
17/2/2000   DJR -   Added full key descriptions and Spectrum+ keys.
            Fixed Spectrum +3 keyboard problems.
17/2/2000   KT -    Added tape loading from WAV/Changed from DAC to generic
            speaker code.
18/2/2000   KT -    Added tape saving to WAV.
27/2/2000   KT -    Took DJR's changes and added my changes.
27/2/2000   KT -    Added disk image support to Spectrum +3 driver.
27/2/2000   KT -    Added joystick I/O code to the Spectrum +3 I/O handler.
14/3/2000   DJR -   Tape handling dipswitch.
26/3/2000   DJR -   Snapshot files are now classified as snapshots not
            cartridges.
04/4/2000   DJR -   Spectrum 128 / +2 Support.
13/4/2000   DJR -   +4 Support (unofficial 48K hack).
13/4/2000   DJR -   +2a Support (rom also used in +3 models).
13/4/2000   DJR -   TK90X, TK95 and Inves support (48K clones).
21/4/2000   DJR -   TS2068 and TC2048 support (TC2048 Supports extra video
            modes but doesn't have bank switching or sound chip).
09/5/2000   DJR -   Spectrum +2 (France, Spain), +3 (Spain).
17/5/2000   DJR -   Dipswitch to enable/disable disk drives on +3 and clones.
27/6/2000   DJR -   Changed 128K/+3 port decoding (sound now works in Zub 128K).
06/8/2000   DJR -   Fixed +3 Floppy support
10/2/2001   KT  -   Re-arranged code and split into each model emulated.
            Code is split into 48k, 128k, +3, tc2048 and ts2048
            segments. 128k uses some of the functions in 48k, +3
            uses some functions in 128, and tc2048/ts2048 use some
            of the functions in 48k. The code has been arranged so
            these functions come in some kind of "override" order,
            read functions changed to use  READ8_HANDLER and write
            functions changed to use WRITE8_HANDLER.
            Added Scorpion256 preliminary.
18/6/2001   DJR -   Added support for Interface 2 cartridges.
xx/xx/2001  KS -    TS-2068 sound fixed.
            Added support for DOCK cartridges for TS-2068.
            Added Spectrum 48k Psycho modified rom driver.
            Added UK-2086 driver.
23/12/2001  KS -    48k machines are now able to run code in screen memory.
                Programs which keep their code in screen memory
                like monitors, tape copiers, decrunchers, etc.
                works now.
                Fixed problem with interrupt vector set to 0xffff (much
            more 128k games works now).
                A useful used trick on the Spectrum is to set
                interrupt vector to 0xffff (using the table
                which contain 0xff's) and put a byte 0x18 hex,
                the opcode for JR, at this address. The first
                byte of the ROM is a 0xf3 (DI), so the JR will
                jump to 0xfff4, where a long JP to the actual
                interrupt routine is put. Due to unideal
                bankswitching in MAME this JP were to 0001 what
                causes Spectrum to reset. Fixing this problem
                made much more software running (i.e. Paperboy).
            Corrected frames per second value for 48k and 128k
            Sinclair machines.
                There are 50.08 frames per second for Spectrum
                48k what gives 69888 cycles for each frame and
                50.021 for Spectrum 128/+2/+2A/+3 what gives
                70908 cycles for each frame.
            Remapped some Spectrum+ keys.
                Pressing F3 to reset was setting 0xf7 on keyboard
                input port. Problem occurred for snapshots of
                some programs where it was read as pressing
                key 4 (which is exit in Tapecopy by R. Dannhoefer
                for example).
            Added support to load .SP snapshots.
            Added .BLK tape images support.
                .BLK files are identical to .TAP ones, extension
                is an only difference.
08/03/2002  KS -    #FF port emulation added.
                Arkanoid works now, but is not playable due to
                completely messed timings.
25/10/2012  DH - simplified border emulation to be a (manual) partial
                 update with bitmap
                 Removed legacy fff4 interrupt hack, modern version of
                 MAME can handle this just fine

Initialisation values used when determining which model is being emulated:
 48K        Spectrum doesn't use either port.
 128K/+2    Bank switches with port 7ffd only.
 +3/+2a     Bank switches with both ports.

TODO: (the best resource for ZX Spectrum HW informations is the http://www.zxdesign.info/
       blog, a project devored to reverse-engineer the ZX Spectrum in h/w).

 1. No contended memory. The different Spectrum models have different contention timings
    Updated info can be found here: http://scratchpad.wikia.com/wiki/Contended_memory
    Of course, neither "late timing" contention effects are emulated.

 2. Russian models (at least Pentagon and Scorpion) reportedly don't have contended memory.
    Information about screen timings are here: http://www.worldofspectrum.org/rusfaq/

 3. Similarly, contended I/O is not emulated: http://scratchpad.wikia.com/wiki/Contended_IO

 4. Finally, the Floating Bus implementation is incomplete: http://scratchpad.wikia.com/wiki/Floating_bus
    The page hosts a couple of testing programs.

 5. A NTSC model exists: it's not the Timex, but a plain ZX Spectrum made for the Chilean market:
    http://scratchpad.wikia.com/wiki/NTSC_Spectrum

 6. No hi-res colour effects (need contended memory first for accurate timing).

 7. Keyboard autorepeat is broken.

 8. Port decoding on all models needs to be checked and, in case, corrected.

 9. The actual Issue 2/3 effect is a bit more complicated than implemented:
    http://piters.tripod.com/cassport.htm for an explanation/test suite.

10. Tape formats not supported:
    .001, .CSW, .LTP, .PZX, .SPC, .STA, .ITM/.PAN, .TAP(Warajevo), .VOC, .ZXS, .ZXT

11. Disk formats not supported:
    .CPD, .DSK, .FDD, .FDI, .HDF, .IMG, .MDR, .MGT, .SCL, .TRD, .ZXD

12. 128K emulation is not perfect - the 128K machines crash and hang while
    running quite a lot of games.

13. Disk errors occur on some +3 games.

14. EXROM and HOME cartridges are not emulated on Timex machines.

15. The TK90X and TK95 roms output 0 to port #df on start up. The purpose of this port is unknown
    (probably display mode as TS2068) and thus is not emulated.

16. Very detailed infos about the ZX Spectrum +3e can be found at
    http://www.worldofspectrum.org/zxplus3e/

17. The ZX Spectrum is known to support a huge number of peripherals, daisy-chained through its expansion bus.
    In the earlier part of its life cycle, a common configuration consisted of a "docking station"-type
    peripheral providing drive controllers (the disk drives were not part of the packages), a game
    interface (joystick and/or sound chip) and the ZX Printer (which, if present, must terminate the chain).

    Later, all-in-one peripherals became more common. The following list is just a little part of the huge
    catalog of peripherals available to the ZX Spectrum; however these peripherals are both the most successful
    and the better documented ones.

    Except when noted, technical and usage manuals are available at http://www.worldofspectrum.org/

------------------------
Mass storage controllers
------------------------

ZX Interface 1
    up to 8 Microdrives
    1 RS-232 port
    2 ZX Network ports - http://scratchpad.wikia.com/wiki/ZX_Net

Opus Discovery
    up to 2 Disk drives
    1 Centronics port
    1 Joystick port (Kempston)

DISCiPLE Interface
    up to 2 Disk drives
    1 Centronics port
    2 Joystick ports (RH: Sinclair/Kempston, LH: Sinclair)
    2 ZX Network ports

Plus D Interface
    up to 2 Disk drives
    1 Centronics port

Rotronics Wafadrive
    2 "Stringy" drives
    1 RS-232 port
    1 Centronics port

Kempston Disc Interface
    up to 4 Disk drives (KDOS)

Beta 128 Disk Interface
    up to 4 Disk drives (TR-DOS)

Philips Disc ROM
    ???

----------------
Game controllers
----------------

ZX Interface 2
    2 Joystick ports (Sinclair)
    1 ROM slot

Kempston Interface
    1 Joystick port (Kempston)

Protek/AGF/Cursor Interface
    1 Joystick port (Protek)

Fuller Box
    1 Joystick port (Fuller)
    1 G1-AY-3-8912 sound chip - the chip is identical to the one present on the 128 model,
                                but the I/O ports are different: http://scratchpad.wikia.com/wiki/AY-3-8912(a) and
                                http://scratchpad.wikia.com/wiki/Timex_2000_series#Sound_Chip

Mikro-Plus Interface
    1 Joystick port (Mikrogen)
    1 ROM slot - http://www.worldofspectrum.org/showmag.cgi?mag=Crash/Issue19/Pages/Crash1900020.jpg

------------
Misc devices
------------

DK'Tronics Light Pen (identical to TK90X light pen)

AMX Mouse
    Schematics and test software at http://velesoft.speccy.cz/othermouse-cz.htm

Kempston Mouse
    Schematics and test software at http://8bit.yarek.pl/hardware/zx.mouse/

Currah uSpeech
    Speech synthesis through allophones (SP0256-AL2)

MGT Messenger
    Allows connection between ZX Spectrum and SAM Coup??

Soft-ROM
    A development system board - http://www.wearmouth.demon.co.uk/softrom.htm

Multiface 1/128/+3
    The classic snapshot interface for ZX Spectrum, 128 and +3 models, with 8K internal RAM.

SamRam
    A more versatile "Multiface I"-type interface - http://8bit.yarek.pl/upgrade/zx.samram/samram.html


*******************************************************************************/

#include "emu.h"
#include "spectrum.h"

#include "bus/spectrum/dma/slot.h"
#include "cpu/z80/z80.h"
#include "spec_snqk.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/tzx_cas.h"


/****************************************************************************************************/
/* Spectrum 48k functions */

uint8_t spectrum_state::pre_opcode_fetch_r(offs_t offset)
{
	m_ula->m1(offset);

	/* this allows expansion devices to act upon opcode fetches from MEM addresses
	   for example, interface1 detection fetches requires fetches at 0008 / 0708 to
	   enable paged ROM and then fetches at 0700 to disable it
	*/
	m_exp->pre_opcode_fetch(offset);
	uint8_t retval = m_specmem->read8(offset);
	m_exp->post_opcode_fetch(offset);
	return retval;
}

uint8_t spectrum_state::spectrum_data_r(offs_t offset)
{
	m_ula->data_r(offset);

	m_exp->pre_data_fetch(offset);
	uint8_t retval = m_specmem->read8(offset);
	m_exp->post_data_fetch(offset);
	return retval;
}

void spectrum_state::spectrum_data_w(offs_t offset, uint8_t data)
{
	m_ula->data_w(offset);
	if (is_vram_write(offset)) m_screen->update_now();

	m_specmem->write8(offset,data);
}

void spectrum_state::spectrum_rom_w(offs_t offset, uint8_t data)
{
	m_exp->mreq_w(offset, data);
}

uint8_t spectrum_state::spectrum_rom_r(offs_t offset)
{
	return m_exp->romcs()
		? m_exp->mreq_r(offset)
		: m_rom[offset];
}

/*
 bit 7-5: not used
 bit 4: Ear output/Speaker
 bit 3: MIC/Tape Output
 bit 2-0: border colour
*/
void spectrum_state::spectrum_ula_w(offs_t offset, uint8_t data)
{
	m_ula->ula_w(offset);

	u8 changed = m_port_fe_data ^ data;

	/* D0-D2: border colour changed? */
	if (changed & 0x07) m_screen->update_now();

	/* D3-D4: MIC+DAC output state */
	if (changed & 0x18)
	{
		/* D3: write cassette data */
		if (BIT(changed, 3)) m_cassette->output(BIT(data, 3) ? -1.0 : +1.0);

		m_speaker->level_w(BIT(data, 3, 2));
	}

	// Some exp devices use ula port unused bits 5-7:
	// Beta v2/3/plus use bit 7, Beta clones use bits 6 and 7
	if (m_exp) m_exp->iorq_w(offset, data);

	m_port_fe_data = data;
}

/* KT: more accurate keyboard reading */
/* DJR: Spectrum+ keys added */
uint8_t spectrum_state::spectrum_ula_r(offs_t offset)
{
	m_ula->ula_r(offset);

	int lines = offset >> 8;
	int data = 0xff;

	int cs_extra1 = m_io_plus0.read_safe(0x1f) & 0x1f;
	int cs_extra2 = m_io_plus1.read_safe(0x1f) & 0x1f;
	int cs_extra3 = m_io_plus2.read_safe(0x1f) & 0x1f;
	int ss_extra1 = m_io_plus3.read_safe(0x1f) & 0x1f;
	int ss_extra2 = m_io_plus4.read_safe(0x1f) & 0x1f;
	int joy1 = m_io_joy1.read_safe(0x1f) & 0x1f;
	int joy2 = m_io_joy2.read_safe(0x1f) & 0x1f;

	/* expansion port */
	if (m_exp) data = m_exp->iorq_r(offset);

	/* Caps - V */
	if ((lines & 1) == 0)
	{
		data &= m_io_line0->read();
		/* CAPS for extra keys */
		if (cs_extra1 != 0x1f || cs_extra2 != 0x1f || cs_extra3 != 0x1f)
			data &= ~0x01;
	}

	/* A - G */
	if ((lines & 2) == 0)
		data &= m_io_line1->read();

	/* Q - T */
	if ((lines & 4) == 0)
		data &= m_io_line2->read();

	/* 1 - 5 */
	if ((lines & 8) == 0)
		data &= m_io_line3->read() & cs_extra1 & joy2;

	/* 6 - 0 */
	if ((lines & 16) == 0)
		data &= m_io_line4->read() & cs_extra2 & joy1;

	/* Y - P */
	if ((lines & 32) == 0)
		data &= m_io_line5->read() & ss_extra1;

	/* H - Enter */
	if ((lines & 64) == 0)
		data &= m_io_line6->read();

	/* B - Space */
	if ((lines & 128) == 0)
	{
		data &= m_io_line7->read() & cs_extra3 & ss_extra2;
		/* SYMBOL SHIFT for extra keys */
		if (ss_extra1 != 0x1f || ss_extra2 != 0x1f)
			data &= ~0x02;
	}

	data |= (0xe0); /* Set bits 5-7 - as reset above */

	/* cassette input from wav */
	if (m_cassette->input() > 0.0038 )
	{
		data &= ~0x40;
	}

	/* Issue 2 Spectrums default to having bits 5, 6 & 7 set.
	Issue 3 Spectrums default to having bits 5 & 7 set and bit 6 reset. */
	if ((m_io_config->read() & 0x80) == 0)
		data ^= (0x40);

	return data;
}

void spectrum_state::spectrum_port_w(offs_t offset, uint8_t data)
{
	m_ula->io_w(offset);

	// Pass through to expansion device if present
	if (m_exp->get_card_device())
		m_exp->iorq_w(offset | 1, data);
}

uint8_t spectrum_state::spectrum_port_r(offs_t offset)
{
	m_ula->io_r(offset);

	// Pass through to expansion device if present
	if (m_exp->get_card_device())
		return m_exp->iorq_r(offset | 1);

	return floating_bus_r();
}

uint8_t spectrum_state::spectrum_clone_port_r(offs_t offset)
{
	// Pass through to expansion device if present
	if (m_exp->get_card_device())
		return m_exp->iorq_r(offset | 1);

	// no floating bus for clones
	return 0xff;
}

uint8_t spectrum_state::floating_bus_r()
{
	/*
	*  "floating bus"
	*  (cpu can peek at the video bus via odd i/o read, some games use it for timing instead of vblank)
	*
	*  It seems this trick was found quite late in the life of the original Sinclair models,
	*  then found not to work properly on the later Amstrad models, so was mostly abandoned by devs.
	*  This means most games that use it were released in a relatively small window of mid-late 1986.
	*
	*  known games:
	*    Arkanoid
	*    Cobra
	*    Sidewize
	*    Short Circuit
	*    Renegade
	*    Terra Cresta
	*
	*    ...others?
	*
	*  (Sidewize uses port 0x40ff, others use 0x28ff)
	*
	*  Note, some were later re-released as "fixed" +2A compatible versions with the floating bus code removed (Arkanoid, Cobra, others?).
	*/

	u8 data = 0xff;

	// peek into attribute ram when beam is in display area
	// ula always returns ff when in border area (or h/vblank)
	if (m_ula->is_in_contended_area())
	{
		const u64 vpos = m_screen->vpos();
		const u64 now = m_maincpu->total_cycles() - m_ula->get_irq_at();
		const u64 cf = vpos * m_ula->get_video_line_clocks() + m_ula->get_raster_contention_offset();
		const u64 ct = cf + m_ula->get_raster_line_clocks();
		if (cf <= now && now < ct)
		{
			u64 clocks = now - cf;
			if (!BIT(clocks, 2))
			{
				u16 y = vpos - get_screen_area().top();
				u16 x = (clocks >> 2) + BIT(clocks, 1);
				data = clocks & 1
					? m_screen_location[0x1800 + (((y & 0xf8) << 2) | x)]
					: m_screen_location[((y & 7) << 8) | ((y & 0x38) << 2) | ((y & 0xc0) << 5) | x];
			}
		}
	}

	return data;
}


/* Memory Maps */

void spectrum_state::spectrum_map(address_map &map)
{
	map(0x0000, 0x3fff).rw(FUNC(spectrum_state::spectrum_rom_r), FUNC(spectrum_state::spectrum_rom_w));
	map(0x4000, 0x5aff).ram().share("video_ram");
	// installed later depending on ramsize
	//map(0x5b00, 0x7fff).ram();
	//map(0x8000, 0xffff).ram();
}

void spectrum_state::spectrum_opcodes(address_map &map)
{
	map(0x0000, 0xffff).r(FUNC(spectrum_state::pre_opcode_fetch_r));
}

void spectrum_state::spectrum_data(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(spectrum_state::spectrum_data_r), FUNC(spectrum_state::spectrum_data_w));
}

// The ula is usually accessed with port 0xfe but is only partially decoded with a single address line (A0),
// so actually responds to any even numbered port.
// Exp devices therefore shouldn't use any even numbered ports, but in fact some do:
// for write, some devices use bits 5-7 as they are not used by the ula (eg. Beta)
// for read, some devices (eg. Interface II and some other joystick interfaces) simply override the data lines to simulate key presses
//  (series resistors between the ula and cpu/exp slot allow this)
void spectrum_state::spectrum_io(address_map &map)
{
	map(0x00, 0x00).rw(FUNC(spectrum_state::spectrum_ula_r), FUNC(spectrum_state::spectrum_ula_w)).select(0xfffe);
	map(0x01, 0x01).rw(FUNC(spectrum_state::spectrum_port_r), FUNC(spectrum_state::spectrum_port_w)).select(0xfffe);
}

void spectrum_state::spectrum_clone_io(address_map &map)
{
	map(0x00, 0x00).rw(FUNC(spectrum_state::spectrum_ula_r), FUNC(spectrum_state::spectrum_ula_w)).select(0xfffe);
	map(0x01, 0x01).rw(FUNC(spectrum_state::spectrum_clone_port_r), FUNC(spectrum_state::spectrum_port_w)).select(0xfffe);
}


/* Input ports */

/****************************************************************************************************
Spectrum keyboard is quite complicate to emulate. Each key can have 5 or 6 different functions, depending on which input mode we are in:

-------------------------------------------------------------------------------------------------------------------
    Mode           |  Sequence on Spectrum              |  Sequence in MESS
-------------------------------------------------------------------------------------------------------------------
    KEY Mode       |  Simply press the key              |  Simply press the key
    CAPS Mode      |  Press Key + CAPS SHIFT            |  Press Key + LShift (default mapping)
    SYMBOL Mode    |  Press Key + SYMBOL SHIFT          |  Press Key + RShift (default mapping)
    EXT Mode       |  Press CAPS + SYMBOL once,         |  Press LShift + RShift (to enter EXT Mode),
                   |     then press Key                 |     then press Key
    EXT+Shift Mode |  In EXT Mode, press Key + SHIFT    |  In EXT Mode, press Key + LShift or Key + RShift
                   |    (no matter if CAPS and SYMBOL)  |
    BASIC Mode     |  At BASIC Prompt, press the key    |  At BASIC Prompt, press the key
-------------------------------------------------------------------------------------------------------------------

Number Keys are the only keys not having a function in BASIC Mode (hence, they only have 5 functions)

2009-04: Added natural keyboard support. The commented out PORT_CHARs are not reachable in natural keyboard mode (they are entered
in EXT+Shift Mode on a real Spectrum).
*/

/* TO DO: replace PORT_CHAR(0xD7) with an 'empty' PORT_CHAR. I used \xD7 (multiplication sign) just as a placeholder. There should be no
PORT_CHAR for those functions (which have no equivalent on modern keyboards), but something is needed (to correctly have natural support for
a few keys in SYMBOL Mode) and I found no EMPTY PORT_CHAR in MAME */
INPUT_PORTS_START( spectrum )
	/* PORT_NAME =  KEY Mode    CAPS Mode    SYMBOL Mode   EXT Mode   EXT+Shift Mode   BASIC Mode  */
	PORT_START("LINE0") /* 0xFEFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CAPS SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("z    Z    :      LN       BEEP   COPY") PORT_CODE(KEYCODE_Z)     PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(':')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"x    X    £      EXP      INK    CLEAR") PORT_CODE(KEYCODE_X)  PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(U'£')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("c    C    ?      LPRINT   PAPER  CONT") PORT_CODE(KEYCODE_C)     PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR('?')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("v    V    /      LLIST    FLASH  CLS") PORT_CODE(KEYCODE_V)      PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR('/')

	PORT_START("LINE1") /* 0xFDFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("a    A    STOP   READ      ~     NEW") PORT_CODE(KEYCODE_A)      PORT_CHAR('a') PORT_CHAR('A')// PORT_CHAR('~')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("s    S    NOT    RESTORE   |     SAVE") PORT_CODE(KEYCODE_S)     PORT_CHAR('s') PORT_CHAR('S')// PORT_CHAR('|')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("d    D    STEP   DATA      \\     DIM") PORT_CODE(KEYCODE_D)     PORT_CHAR('d') PORT_CHAR('D')// PORT_CHAR('\\')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f    F    TO     SGN       {     FOR") PORT_CODE(KEYCODE_F)      PORT_CHAR('f') PORT_CHAR('F')// PORT_CHAR('{')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("g    G    THEN   ABS       }     GOTO") PORT_CODE(KEYCODE_G)     PORT_CHAR('g') PORT_CHAR('G')// PORT_CHAR('}')

	PORT_START("LINE2") /* 0xFBFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("q    Q    <=     SIN      ASN      PLOT") PORT_CODE(KEYCODE_Q)   PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("w    W    <>     COS      ACS      DRAW") PORT_CODE(KEYCODE_W)   PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("e    E    >=     TAN      ATN      REM") PORT_CODE(KEYCODE_E)    PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("r    R    <      INT      VERIFY   RUN") PORT_CODE(KEYCODE_R)    PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR('<')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("t    T    >      RND      MERGE    RAND") PORT_CODE(KEYCODE_T)   PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR('>')

	/* interface II uses this port for joystick */
	PORT_START("LINE3") /* 0xF7FE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1   EDIT       !    BLUE     DEF FN") PORT_CODE(KEYCODE_1)   PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2   CAPS LOCK  @    RED      FN") PORT_CODE(KEYCODE_2)       PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3   TRUE VID   #    MAGENTA  LINE") PORT_CODE(KEYCODE_3)     PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4   INV VID    $    GREEN    OPEN#") PORT_CODE(KEYCODE_4)    PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5   Left       %    CYAN     CLOSE#") PORT_CODE(KEYCODE_5)   PORT_CHAR('5') PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CHAR('%')

	/* protek clashes with interface II! uses 5 = left, 6 = down, 7 = up, 8 = right, 0 = fire */
	PORT_START("LINE4") /* 0xEFFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0   DEL        _    BLACK    FORMAT") PORT_CODE(KEYCODE_0)   PORT_CHAR('0') PORT_CHAR(8) PORT_CHAR('_')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9   GRAPH      )             POINT") PORT_CODE(KEYCODE_9)    PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8   Right      (             CAT") PORT_CODE(KEYCODE_8)      PORT_CHAR('8') PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR('(')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7   Up         '    WHITE    ERASE") PORT_CODE(KEYCODE_7)    PORT_CHAR('7') PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CHAR('\'')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6   Down       &    YELLOW   MOVE") PORT_CODE(KEYCODE_6)     PORT_CHAR('6') PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CHAR('&')

	PORT_START("LINE5") /* 0xDFFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("p    P    \"      TAB      (c)    PRINT") PORT_CODE(KEYCODE_P)   PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR('"')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("o    O    ;      PEEK     OUT    POKE") PORT_CODE(KEYCODE_O)     PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(';')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("i    I    AT     CODE     IN     INPUT") PORT_CODE(KEYCODE_I)    PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("u    U    OR     CHR$     ]      IF") PORT_CODE(KEYCODE_U)       PORT_CHAR('u') PORT_CHAR('U')// PORT_CHAR(']')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("y    Y    AND    STR$     [      RETURN") PORT_CODE(KEYCODE_Y)   PORT_CHAR('y') PORT_CHAR('Y')// PORT_CHAR('[')

	PORT_START("LINE6") /* 0xBFFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("l    L    =      USR      ATTR     LET") PORT_CODE(KEYCODE_L)    PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR('=')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("k    K    +      LEN      SCREEN$  LIST") PORT_CODE(KEYCODE_K)   PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR('+')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("j    J    -      VAL      VAL$     LOAD") PORT_CODE(KEYCODE_J)   PORT_CHAR('j') PORT_CHAR('J') PORT_CHAR('-')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("h    H    ^      SQR      CIRCLE   GOSUB") PORT_CODE(KEYCODE_H)  PORT_CHAR('h') PORT_CHAR('H') PORT_CHAR('^')

	PORT_START("LINE7") /* 0x7FFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SYMBOL SHIFT") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("m    M    .      PI       INVERSE  PAUSE") PORT_CODE(KEYCODE_M)  PORT_CHAR('m') PORT_CHAR('M') PORT_CHAR('.')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("n    N    ,      INKEY$   OVER     NEXT") PORT_CODE(KEYCODE_N)   PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(',')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("b    B    *      BIN      BRIGHT   BORDER") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR('*')

	PORT_START("NMI")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("NMI") PORT_CODE(KEYCODE_F12)

	PORT_START("CONFIG")
	PORT_CONFNAME( 0x80, 0x00, "Hardware Version" )
	PORT_CONFSETTING(    0x00, "Issue 2" )
	PORT_CONFSETTING(    0x80, "Issue 3" )
	PORT_CONFNAME( 0x01, 0x00, "Contention" ) PORT_CHANGED_MEMBER("ula", FUNC(spectrum_ula_device::on_contention_changed), 0)
	PORT_CONFSETTING(    0x00, "Early" )
	PORT_CONFSETTING(    0x01, "Late" )
	PORT_BIT(0x7e, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

/* Machine initialization */
void spectrum_state::init_spectrum()
{
	m_specmem->space(AS_PROGRAM).install_ram(0x5b00, m_ram->size() + 0x3fff, m_ram->pointer() + 0x1b00);
}

void spectrum_state::machine_start()
{
	save_item(NAME(m_port_fe_data));

	m_maincpu->space(AS_PROGRAM).specific(m_program);
	m_maincpu->space(AS_IO).specific(m_io);
}

void spectrum_state::machine_reset()
{
	/* Initial value/behaviour of FE port is not confirmed. Startup of real devices produce 'random' border
	color which need to be investigated. */
	m_port_fe_data = -1;
	m_port_7ffd_data = -1;
	m_port_1ffd_data = -1;
	m_irq_on_timer->adjust(attotime::never);
	m_irq_off_timer->adjust(attotime::never);
}

/* F4 Character Displayer */
static const gfx_layout spectrum_charlayout =
{
	8, 8,           /* 8 x 8 characters */
	96,             /* 96 characters */
	1,              /* 1 bits per pixel */
	{ 0 },          /* no bitplanes */
	{STEP8(0, 1)},  /* x offsets */
	{STEP8(0, 8)},  /* y offsets */
	8*8             /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_spectrum )
	GFXDECODE_ENTRY( "maincpu", 0x3d00, spectrum_charlayout, 7, 8 )
GFXDECODE_END

TIMER_CALLBACK_MEMBER(spectrum_state::irq_on)
{
	m_ula->on_irq();
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
	m_irq_off_timer->adjust(m_maincpu->clocks_to_attotime(32 + m_ula->get_irq_ext_length()));
}

TIMER_CALLBACK_MEMBER(spectrum_state::irq_off)
{
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
}

INTERRUPT_GEN_MEMBER(spectrum_state::spec_interrupt)
{
	m_irq_on_timer->adjust(m_screen->time_until_pos(0, get_screen_area().left()));
}

void spectrum_state::spectrum_common(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, X1 / 4);        /* This is verified only for the ZX Spectrum. Other clones are reported to have different clocks */
	m_maincpu->set_m1_map(&spectrum_state::spectrum_opcodes);
	m_maincpu->set_memory_map(&spectrum_state::spectrum_data);
	m_maincpu->set_io_map(&spectrum_state::spectrum_io);
	m_maincpu->set_vblank_int("screen", FUNC(spectrum_state::spec_interrupt));
	m_maincpu->refresh_cb().set(FUNC(spectrum_state::spectrum_refresh_w));
	m_maincpu->nomreq_cb().set("ula", FUNC(spectrum_ula_device::nomem_rq));
	m_maincpu->busack_cb().set("dma", FUNC(dma_slot_device::bai_w));

	ADDRESS_MAP_BANK(config, m_specmem).set_map(&spectrum_state::spectrum_map).set_options(ENDIANNESS_LITTLE, 8, 16, 0x10000);

#if 1 // change to 0 in order to get working "Proceed 1" VC1541 FDC
	config.set_maximum_quantum(attotime::from_hz(60));
#else
	config.set_perfect_quantum(m_maincpu);
#endif

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);

	rectangle visarea = { get_screen_area().left() - SPEC_LEFT_BORDER, get_screen_area().right() + SPEC_RIGHT_BORDER,
		get_screen_area().top() - SPEC_TOP_BORDER, get_screen_area().bottom() + SPEC_BOTTOM_BORDER };
	m_screen->set_raw(X1 / 2, SPEC_CYCLES_PER_LINE * 2, SPEC_UNSEEN_LINES + SPEC_SCREEN_HEIGHT, visarea);
	m_screen->set_screen_update(FUNC(spectrum_state::screen_update_spectrum));
	m_screen->set_palette("palette");

	PALETTE(config, "palette", FUNC(spectrum_state::spectrum_palette), 16);
	GFXDECODE(config, "gfxdecode", "palette", gfx_spectrum);
	SPECTRUM_ULA_48K(config, m_ula);
	m_ula->set_screen(m_screen, get_screen_area());
	m_ula->set_z80(m_maincpu);

	/* sound hardware */
	SPEAKER(config, "speakers").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "speakers", 0.50);
	static const double speaker_levels[4] = { 0.0, 0.33, 0.66, 1.0 };
	m_speaker->set_levels(4, speaker_levels);

	/* expansion port */
	SPECTRUM_EXPANSION_SLOT(config, m_exp, spectrum_expansion_devices, "kempjoy");
	m_exp->irq_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_exp->nmi_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_exp->fb_r_handler().set(FUNC(spectrum_state::floating_bus_r));

	dma_slot_device &dma(DMA_SLOT(config, "dma", X1 / 4, default_dma_slot_devices, nullptr));
	dma.set_io_space(m_maincpu, AS_IO);
	dma.out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	dma.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	dma.in_mreq_callback().set([this](offs_t offset) { return m_program.read_byte(offset); });
	dma.out_mreq_callback().set([this](offs_t offset, u8 data) { m_program.write_byte(offset, data); });
	dma.in_iorq_callback().set([this](offs_t offset) { return m_io.read_byte(offset); });
	dma.out_iorq_callback().set([this](offs_t offset, u8 data) { m_io.write_byte(offset, data); });

	SNAPSHOT(config, "snapshot", "ach,frz,plusd,prg,sem,sit,sna,snp,snx,sp,z80,zx").set_load_callback(FUNC(spectrum_state::snapshot_cb));
	QUICKLOAD(config, "quickload", "raw,scr", attotime::from_seconds(2)).set_load_callback(FUNC(spectrum_state::quickload_cb)); // The delay prevents the screen from being cleared by the RAM test at boot

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(tzx_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "speakers", 0.05);
	m_cassette->set_interface("spectrum_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("spectrum_cass");
}

void spectrum_state::spectrum(machine_config &config)
{
	spectrum_common(config);

	/* internal ram */
	RAM(config, m_ram).set_default_size("48K").set_extra_options("16K").set_default_value(0xff);
	// This configuration is verified only for the original ZX Spectrum.
	// It's likely, but still to be checked, that many clones were produced only
	// in the 48k configuration, while others have extra memory (80k, 128K, 1024K)
	// available via bankswitching.
}

void spectrum_state::spectrum_clone(machine_config &config)
{
	spectrum(config);

	// no floating bus
	m_maincpu->set_io_map(&spectrum_state::spectrum_clone_io);
	m_exp->fb_r_handler().set([]() { return 0xff; });
}

/***************************************************************************

  Game driver(s)

***************************************************************************/

ROM_START(spectrum)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROMX_LOAD("spectrum.rom", 0x0000, 0x4000, CRC(ddee531f) SHA1(5ea7c2b824672e914525d1d5c419d71b84a426a2), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "sp", "Spanish")
	ROMX_LOAD("48e.rom", 0x0000, 0x4000, CRC(f051746e) SHA1(9e535e2e24231ccb65e33d107f6d0ceb23e99477), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "p", "Prototype") // breadboard PCB with a maze of wires underneath, keyboard has handwritten labels
	ROMX_LOAD("0,1.eprom1", 0x0000, 0x2000, CRC(6b807b7a) SHA1(7d14f1929c086b0954edb386f02b06ed283b602a), ROM_BIOS(2))
	ROMX_LOAD("2,3.eprom2", 0x2000, 0x2000, CRC(f54a389b) SHA1(eab62eda635a56c24132022947fdeefcd18215f5), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "bsrom118", "BusySoft Upgrade v1.18")
	ROMX_LOAD("bsrom118.rom", 0x0000, 0x4000, CRC(1511cddb) SHA1(ab3c36daad4325c1d3b907b6dc9a14af483d14ec), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "bsrom140", "BusySoft Upgrade v1.40")
	ROMX_LOAD("bsrom140.rom", 0x0000, 0x4000, CRC(07017c6d) SHA1(2ee2dbe6ab96b60d7af1d6cb763b299374c21776), ROM_BIOS(4))
	ROM_SYSTEM_BIOS(5, "groot", "De Groot's Upgrade")
	ROMX_LOAD("groot.rom", 0x0000, 0x4000, CRC(abf18c45) SHA1(51165cde68e218512d3145467074bc7e786bf307), ROM_BIOS(5))
	ROM_SYSTEM_BIOS(6, "48turbo", "48 Turbo")
	ROMX_LOAD("48turbo.rom", 0x0000, 0x4000, CRC(56189781) SHA1(e62a431b0938af414b7ab8b1349a18b3c4407f70), ROM_BIOS(6))
	ROM_SYSTEM_BIOS(7, "psycho", "Maly's Psycho Upgrade")
	ROMX_LOAD("psycho.rom", 0x0000, 0x4000, CRC(cd60b589) SHA1(0853e25857d51dd41b20a6dbc8e80f028c5befaa), ROM_BIOS(7))
	ROM_SYSTEM_BIOS(8, "turbo23", "Turbo 2.3")
	ROMX_LOAD("turbo2_3.rom", 0x0000, 0x4000, CRC(fd3b0413) SHA1(84ea64af06adaf05e68abe1d69454b4fc6888505), ROM_BIOS(8))
	ROM_SYSTEM_BIOS(9, "turbo44", "Turbo 4.4")
	ROMX_LOAD("turbo4_4.rom", 0x0000, 0x4000, CRC(338b6e87) SHA1(21ad93ffe41a4458704c866cca2754f066f6a560), ROM_BIOS(9))
	ROM_SYSTEM_BIOS(10, "imc", "Ian Collier's Upgrade")
	ROMX_LOAD("imc.rom", 0x0000, 0x4000, CRC(d1be99ee) SHA1(dee814271c4d51de257d88128acdb324fb1d1d0d), ROM_BIOS(10))
	ROM_SYSTEM_BIOS(11, "plus4", "ZX Spectrum +4")
	ROMX_LOAD("plus4.rom",0x0000,0x4000, CRC(7e0f47cb) SHA1(c103e89ef58e6ade0c01cea0247b332623bd9a30), ROM_BIOS(11))
	ROM_SYSTEM_BIOS(12, "deutsch", "German unofficial (Andrew Owen)")
	ROMX_LOAD("deutsch.rom",0x0000,0x4000, CRC(1a9190f4) SHA1(795c20324311dd5a56300e6e4ec49b0a694ac0b3), ROM_BIOS(12))
	ROM_SYSTEM_BIOS(13, "hdt", "HDT-ISO HEX-DEC-TEXT")
	ROMX_LOAD("hdt-iso.rom",0x0000,0x4000, CRC(b81c570c) SHA1(2a9745ba3b369a84c4913c98ede66ec87cb8aec1), ROM_BIOS(13))
	ROM_SYSTEM_BIOS(14, "sc", "The Sea Change Minimal ROM V1.78")
	ROMX_LOAD("sc01.rom",0x0000,0x4000, CRC(73b4057a) SHA1(c58ff44a28db47400f09ed362ca0527591218136), ROM_BIOS(14))
	ROM_SYSTEM_BIOS(15, "gosh", "GOSH Wonderful ZX Spectrum ROM V1.32")
	ROMX_LOAD("gw03.rom",0x0000,0x4000, CRC(5585d7c2) SHA1(a701c3d4b698f7d2be537dc6f79e06e4dbc95929), ROM_BIOS(15))
	ROM_SYSTEM_BIOS(16, "1986es", "1986ES Snapshot")
	ROMX_LOAD("1986es.rom",0x0000,0x4000, CRC(9e0fdaaa) SHA1(f9d23f25640c51bcaa63e21ed5dd66bb2d5f63d4), ROM_BIOS(16))
	ROM_SYSTEM_BIOS(17, "jgh", "JGH ROM V0.74 (C) J.G.Harston")
	ROMX_LOAD("jgh.rom",0x0000,0x4000, CRC(7224138e) SHA1(d7f02ed66455f1c08ac0c864c7038a92a88ba94a), ROM_BIOS(17))
	ROM_SYSTEM_BIOS(18, "iso22", "ISO ROM V2.2")
	ROMX_LOAD("isomoje.rom",0x0000,0x4000, CRC(62ab3640) SHA1(04adbdb1380d6ccd4ab26ddd61b9ccbba462a60f), ROM_BIOS(18))
	ROM_SYSTEM_BIOS(19, "iso8", "ISO ROM 8")
	ROMX_LOAD("iso8bm.rom",0x0000,0x4000, CRC(43e9c2fd) SHA1(5752e6f789769475711b91e0a75911fa5232c767), ROM_BIOS(19))
	ROM_SYSTEM_BIOS(20, "diagv28", "DiagROM v1.28")
	ROMX_LOAD("diagrom.v28",0x0000,0x4000, CRC(aadf967e) SHA1(6fbdb11e475499655df36343a22f15d67f578165), ROM_BIOS(20))
	ROM_SYSTEM_BIOS(21, "diagv31", "DiagROM v1.31")
	ROMX_LOAD("diagrom.v31",0x0000,0x4000, CRC(cddbb11e) SHA1(3ed203034fe1562ee7216fad06309d3f7033a967), ROM_BIOS(21))
	ROM_SYSTEM_BIOS(22, "diagv33", "DiagROM v1.33")
	ROMX_LOAD("diagrom.v33",0x0000,0x4000, CRC(0fb080e1) SHA1(222f535ae277d0b8ce6c27b75319da9c45f8cadc), ROM_BIOS(22))
	ROM_SYSTEM_BIOS(23, "diagv34", "DiagROM v1.34")
	ROMX_LOAD("diagrom.v34",0x0000,0x4000, CRC(50478f29) SHA1(1e55af6e7bb24090ce052e7536e91f92492fe168), ROM_BIOS(23))
	ROM_SYSTEM_BIOS(24, "diagv35", "DiagROM v1.35")
	ROMX_LOAD("diagrom.v35",0x0000,0x4000, CRC(c869b642) SHA1(61eb786249a207ea57cdea6bb01fcd0b95c218f2), ROM_BIOS(24))
	ROM_SYSTEM_BIOS(25, "diagv47", "DiagROM v1.47")
	ROMX_LOAD("diagrom.v47",0x0000,0x4000, CRC(be329fc0) SHA1(24dc875753e729452f1ff941d2e0ae49f9031bc3), ROM_BIOS(25))
	ROM_SYSTEM_BIOS(26, "diagv50", "DiagROM v1.50")
	ROMX_LOAD("diagrom.v50",0x0000,0x4000, CRC(054a2d6a) SHA1(2fa6f3b31d7bb610df78be5c4c80537cf6e5911d), ROM_BIOS(26))
	ROM_SYSTEM_BIOS(27, "diagv51", "DiagROM v1.51")
	ROMX_LOAD("diagrom.v51",0x0000,0x4000, CRC(83034df6) SHA1(e57b2c8a8e3563ea02a20eecd1d4cb6be9f9c2df), ROM_BIOS(27))
	ROM_SYSTEM_BIOS(28, "diagv56", "DiagROM v1.56")
	ROMX_LOAD("diagrom.v56",0x0000,0x4000, CRC(0ed22f7a) SHA1(37caf0bbc2d023ca5afaa12b3856ac90dbc83c51), ROM_BIOS(28))
	ROM_SYSTEM_BIOS(29, "diagv59", "DiagROM v1.59")
	ROMX_LOAD("diagrom.v59",0x0000,0x4000, CRC(90e2ca5a) SHA1(dac1f877cbfdd516a8bf6336d8eff5902bd06438), ROM_BIOS(29))
	ROM_SYSTEM_BIOS(30, "alford37", "Brian Alford's Test ROM v0.37")
	ROMX_LOAD("testromv037.bin", 0x0000,0x4000, CRC(a7ea3d1c) SHA1(f699b73abfb1ab53c063ac02ac6283705864c734), ROM_BIOS(30))
ROM_END

ROM_START(specide)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("zxide.rom", 0x0000, 0x4000, CRC(bd48db54) SHA1(54c2aa958902b5395c260770a0b25c7ba5685de9))
ROM_END

ROM_START(spec80k)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("80-lec.rom", 0x0000, 0x4000, CRC(5b5c92b1) SHA1(bb7a77d66e95d2e28ebb610e543c065e0d428619))
ROM_END

/* Official Sinclair licensed Spanish clone */

ROM_START(inves)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("inves.rom",0x0000,0x4000, CRC(8ff7a4d1) SHA1(d020440638aff4d39467128413ef795677be9c23))
ROM_END

/* Brazilian clones */

ROM_START(tk90x)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("tk90x.rom",0x0000,0x4000, CRC(3e785f6f) SHA1(9a943a008be13194fb006bddffa7d22d2277813f))
ROM_END

ROM_START(tk95)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("tk95.rom",0x0000,0x4000, CRC(17368e07) SHA1(94edc401d43b0e9a9cdc1d35de4b6462dc414ab3))
ROM_END

/* Romanian clones */

ROM_START(hc85)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("hc85.rom",0x0000,0x4000, CRC(3ab60fb5) SHA1(a4189db0bcdf8b39ed782b398828efb408fc4817))
ROM_END

ROM_START(hc88)
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "hc88.bin", 0x0000, 0x0800, CRC(33be5134) SHA1(b15a6e7085710de8b818e42d329707cb737627e3))
ROM_END

ROM_START(hc90)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("hc90.rom",0x0000,0x4000, CRC(78c14d9a) SHA1(25ef81905bed90497a749770170c24632efb2039))
ROM_END

ROM_START(hc91)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("hc91.rom",0x0000,0x4000, CRC(8bf53761) SHA1(967d5179ba2823e9c8dd9ddfb0430465aaddb554))
ROM_END

ROM_START(hc2000)
	// HC-91 with internal fdd, 64KB ram, dual-os (BASIC, CP/M), built-in Interface I
	// user manual states "16KB BASIC rom, 16KB CP/M rom, 10KB Interface I rom"
	// some pcb have 2x 27256 eproms, some have single 28-pin socket (27512?)
	// for 2 eprom model, seems #1 is combined BASIC+CP/M, #2 is intf1
	// BASIC should boot first, then optionally CP/M can be booted with RANDOMIZE USR 14446
	// CP/M can use 56KB ram, fdc is UM8272A, disks are 3.5" 720KB
	ROM_REGION(0x10000,"maincpu",0)
	ROM_SYSTEM_BIOS( 0, "basicv1", "BASIC v1" )
	ROMX_LOAD("hc2000.v1",  0x0000,0x4000, CRC(453c1a5a) SHA1(f8139fc38478691cf44944dc83fd6e70b0f002fb), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "basicv2", "BASIC v2" )
	ROMX_LOAD("hc2000.v2",  0x0000,0x4000, CRC(65d90464) SHA1(5e2096e6460ff2120c8ada97579fdf82c1199c09), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "cpmv1", "CP/M v1" )  // not working
	ROMX_LOAD("hc2000c.v1",  0x0000,0x4000, CRC(aaa373fe) SHA1(55a30d99c37c86c55ce6c8ecbe593b81d9819e90), ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "cpmv2", "CP/M v2" )  // not working, data arranged differently to v1
	ROMX_LOAD("hc2000c.v2",  0x0000,0x2000, CRC(69c757fe) SHA1(c2bcab2398fb493a31cf6c18ff1e4e9f55690d41), ROM_BIOS(3))
	ROM_REGION(0x8000,"opt",0)  // Interface 1 roms
	ROM_LOAD_OPTIONAL("hc2000i.v12", 0x4000, 0x4000, CRC(182d5c0c) SHA1(b76d2bebcd938238f790e395859d0d237637d33e))  // 190892 v1, v2 pcb
	ROM_LOAD_OPTIONAL("hc2000i.vx", 0x0000, 0x4000, CRC(39967a21) SHA1(e190001ae318982ed31f6c02b4201ca9126a739a))   // 221191 unknown pcb ver
ROM_END

ROM_START(cip03)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("cip03.rom",0x0000,0x4000, CRC(c7d0cd3c) SHA1(811055b44fc74076137e2bf8db206b2a70287cc2))
ROM_END

ROM_START(cip01)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("cip01.rom",0x0000,0x4000, CRC(0516a329) SHA1(4e3e0c5719a64d3b4fb224db499b4bef7d146917))
ROM_END

ROM_START(jet)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("jet.rom",0x0000,0x4000, CRC(e56a7d11) SHA1(e76be9ee71bae6aa1c2ff969276fb599ed68cb50))
ROM_END

ROM_START(cobrasp)
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "v1", "V1")
	ROMX_LOAD( "boot64k_v1.bin", 0x0000, 0x0800, CRC(a54aae6d) SHA1(8f5134ce24aea59065ed166ad79e864e17ce812f), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v2", "V2")
	ROMX_LOAD( "boot64k_v2.bin", 0x0000, 0x0800, CRC(ee91cc89) SHA1(37dea7fe0734068adf99b91fdcbf3119095c350d), ROM_BIOS(1))
ROM_END

ROM_START(cobra80)
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "v1", "V1")
	ROMX_LOAD( "boot80k_v1.bin", 0x0000, 0x0800, CRC(f42d2342) SHA1(8aa1b3b056e311674a051ffc6a49af60cae409f3), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v2", "V2")
	ROMX_LOAD( "boot80k_v2.bin", 0x0000, 0x0800, CRC(df6bd954) SHA1(5b858b59e697d0368ea631ead14f5b2aa7954ccd), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v3", "V3")
	ROMX_LOAD( "boot80k_v3.bin", 0x0000, 0x0800, CRC(8580494c) SHA1(91af3f3fa50f2071f8ff081536bdf7e21e9823d9), ROM_BIOS(2))
ROM_END

/* Czechoslovakian clones*/

/* TODO: need to add memory handling for 80K RAM */

ROM_START(dgama87)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("dgama87.rom",0x0000,0x4000, CRC(43104909) SHA1(f62d1f3f35fda467cae468e890995614f6ec2357))
ROM_END

ROM_START(dgama88)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("dgama88.rom",0x0000,0x4000, CRC(4ec7e078) SHA1(09a91f85e82efa7f974d1b88c69636a02063d563))
ROM_END

ROM_START(dgama89)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_SYSTEM_BIOS(0, "default", "Original")
	ROMX_LOAD("dgama89.rom",0x0000,0x4000, CRC(45c29401) SHA1(8466a9da0169666210ccff5d43376d70bae0ae9b), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "g81", "Gama 81")
	ROMX_LOAD("g81.rom",0x0000,0x4000, CRC(c169a63b) SHA1(71652005c2e7a4301caa7e95ae989b69cb5a6a0d), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "iso", "ISO")
	ROMX_LOAD("iso.rom",0x0000,0x4000, CRC(2ee3a992) SHA1(2e39995dd032036d33a6dd88a38b750057bca19d), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "isopolak", "ISO Polak")
	ROMX_LOAD("isopolak.rom",0x0000,0x4000, CRC(5e3f1f66) SHA1(61713117c944fc6afcb96c647bdba5ad36fd6a4b), ROM_BIOS(3))
ROM_END

ROM_START(didakt90)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("didakt90.rom",0x0000,0x4000, CRC(76f2db1e) SHA1(daee355a8ee58bc406873c1dd81eecb6161dd4bd))
ROM_END

ROM_START(didakm91)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("didakm91.rom",0x0000,0x4000, CRC(beab69b8) SHA1(71d4d1a05fb936f616bcb05c3a276f79343ecd4d))
ROM_END

ROM_START(didakm92)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("didakm92.bin",0x0000,0x4000, CRC(57264d4f) SHA1(23644fe949cbf527747959d07b72db01de378c4c))
ROM_END

ROM_START(didaktk)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("didaktk.rom",0x0000,0x4000, CRC(8ec8a625) SHA1(cba35517d33a5c97e3d9110f12a417c6c5cdeca8))
ROM_END

ROM_START(didakm93)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("didakm93.rom",0x0000,0x4000, CRC(ec274b1b) SHA1(a3470d8d1a996ee2a1ffff8bd8044da6e907e07e))
ROM_END

ROM_START(mistrum)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("mistrum.rom",0x0000,0x4000, CRC(d496103e) SHA1(cca1c5b059dc3a29ca4282e8621e34a65efaa1a3))
ROM_END

/* Russian clones */

ROM_START(bk08)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD( "right.bin", 0x0000, 0x2000, CRC(fb253544) SHA1(6b79487e3013d0acdea8d224b21c937e88105a2f) )
	ROM_LOAD( "left.bin",  0x2000, 0x2000, CRC(a092b5f3) SHA1(06b8d98a398f61daf6604c68bcee4596c283c2cd) )
ROM_END

ROM_START(blitzs)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("blitz.rom",0x0000,0x4000, CRC(91e535a8) SHA1(14f09d45dc3803cbdb05c33adb28eb12dbad9dd0))
ROM_END

ROM_START(orizon)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("orizon.rom",0x0000,0x4000, CRC(ed4d9787) SHA1(3e8b29862e06be03344393c320a64a109fd9aff5))
ROM_END

ROM_START(quorum48)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("quorum48.rom",0x0000,0x4000, CRC(48085b0e) SHA1(8e01581643f7bdfa773f68207a6437911b631e53))
ROM_END

ROM_START(magic6)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("magic6.rom",0x0000,0x4000, CRC(cb63ae06) SHA1(533ad1f50534e6bdeec50eb5a9a4976c3d010dc7))
ROM_END

ROM_START(compani1)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("compani1.rom",0x0000,0x4000, CRC(bcfa6068) SHA1(40074b55c91a947698598e9d6ac5b8495e8cc840))
ROM_END

ROM_START(spektrbk)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("spektr-bk001.rom", 0x0000, 0x4000, CRC(c011eecc) SHA1(35fdc8cd083e50452655997a997873627b131520))
ROM_END

ROM_START(sintez2)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD( "sntez2.bin",   0x000000, 0x004000, CRC(490ded3a) SHA1(a1c6c18e1d93761c0891291ec75191f54929ed7b) )
ROM_END

ROM_START(zvezda)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD( "2764-near-cpu_red.bin", 0x0000, 0x2000, CRC(a4ae4938) SHA1(ea1763b9dee29381ddcf882fbc4e404ba5366942))
	ROM_LOAD( "2764-far-cpu_blue.bin", 0x2000, 0x2000, CRC(ebab64bc) SHA1(8c98a8b6e927b02cf602c20a1b50838e60f7785b))
ROM_END

ROM_START(santaka2)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD( "01.01.d29", 0x0000, 0x0800, CRC(537db879) SHA1(601c69c8f9c77cb00ccd653a0ac0922044c13c86))
	ROM_LOAD( "01.02.d30", 0x0800, 0x0800, CRC(6b5178bc) SHA1(ef5417e24cb80b3f417054a3ef89af5bbef94598))
	ROM_LOAD( "01.03.d31", 0x1000, 0x0800, CRC(379c0032) SHA1(a10ebcd8b990ce26ec54dadf5c70b9c6eab7d314))
	ROM_LOAD( "01.04.d32", 0x1800, 0x0800, CRC(765fc052) SHA1(40085c34fccb9b2274fe0887efa532e3771ffaf7))
	ROM_LOAD( "01.05.d33", 0x2000, 0x0800, CRC(c177cd1e) SHA1(8dc8f0e086bdcdc1c60b9e7a1b4a1a6799e8f0bd))
	ROM_LOAD( "01.06.d34", 0x2800, 0x0800, CRC(69072150) SHA1(0edd6db5e5b6f65921ec5c87d6d0d4ef3b3a9c03))
	ROM_LOAD( "01.07.d35", 0x3000, 0x0800, CRC(2cb5126d) SHA1(1b951ca8172ce2e7912df9ab3713f48d247311a4))
	ROM_LOAD( "01.08.d36", 0x3800, 0x0800, CRC(4d6e54cb) SHA1(7f01b9b0790064024012430fe47710e4fb905360))
ROM_END

/*  Any clone known to have the same "floating bus" behaviour as official Sinclair models should be changed to use the "spectrum" machine  */

//    YEAR  NAME      PARENT    COMPAT  MACHINE         INPUT      CLASS           INIT           COMPANY                  FULLNAME                 FLAGS
COMP( 1982, spectrum, 0,        0,      spectrum,       spectrum,  spectrum_state, init_spectrum, "Sinclair Research Ltd", "ZX Spectrum" ,          0 )
COMP( 1987, spec80k,  spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "<unknown>",             "ZX Spectrum 80K",       MACHINE_UNOFFICIAL )
COMP( 1995, specide,  spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "<unknown>",             "ZX Spectrum IDE",       MACHINE_UNOFFICIAL )
COMP( 1986, inves,    spectrum, 0,      spectrum_clone, spec_plus, spectrum_state, init_spectrum, "Investronica",          "Inves Spectrum 48K+",   0 )
COMP( 1985, tk90x,    spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "Micro Digital",         "TK 90X Color Computer", 0 )
COMP( 1986, tk95,     spectrum, 0,      spectrum_clone, spec_plus, spectrum_state, init_spectrum, "Micro Digital",         "TK 95 Color Computer",  0 )
COMP( 1985, hc85,     spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "ICE-Felix",             "HC-85",                 0 )
COMP( 1988, hc88,     spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "ICE-Felix",             "HC-88",                 MACHINE_NOT_WORKING )
COMP( 1990, hc90,     spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "ICE-Felix",             "HC-90",                 0 )
COMP( 1991, hc91,     spectrum, 0,      spectrum_clone, spec_plus, spectrum_state, init_spectrum, "ICE-Felix",             "HC-91",                 0 )
COMP( 1992, hc2000,   spectrum, 0,      spectrum_clone, spec_plus, spectrum_state, init_spectrum, "ICE-Felix",             "HC-2000",               MACHINE_NOT_WORKING )  // dual-os, only BASIC roms working
COMP( 1988, cobrasp,  spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "ITCI",                  "Cobra (ITCI)",          MACHINE_NOT_WORKING )
COMP( 1988, cobra80,  spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "ITCI",                  "Cobra 80K (ITCI)",      MACHINE_NOT_WORKING )
COMP( 1987, cip01,    spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "Electronica",           "CIP-01",                0 )  // keyboard should be spectrum, but image was not clear
COMP( 1988, cip03,    spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "Electronica",           "CIP-03",                0 )  // keyboard should be spectrum, but image was not clear
COMP( 1990, jet,      spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "Electromagnetica",      "JET",                   0 )  // keyboard should be spectrum, but image was not clear
COMP( 1987, dgama87,  spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "Didaktik Skalica",      "Didaktik Gama 87",      0 )
COMP( 1988, dgama88,  spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "Didaktik Skalica",      "Didaktik Gama 88",      0 )
COMP( 1989, dgama89,  spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "Didaktik Skalica",      "Didaktik Gama 89",      0 )
COMP( 1990, didakt90, spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "Didaktik Skalica",      "Didaktik 90",           0 )
COMP( 1991, didakm91, spectrum, 0,      spectrum_clone, spec_plus, spectrum_state, init_spectrum, "Didaktik Skalica",      "Didaktik M 91",         0 )
COMP( 1992, didakm92, spectrum, 0,      spectrum_clone, spec_plus, spectrum_state, init_spectrum, "Didaktik Skalica",      "Didaktik M 92",         0 )
COMP( 1992, didaktk,  spectrum, 0,      spectrum_clone, spec_plus, spectrum_state, init_spectrum, "Didaktik Skalica",      "Didaktik Kompakt",      0 )
COMP( 1993, didakm93, spectrum, 0,      spectrum_clone, spec_plus, spectrum_state, init_spectrum, "Didaktik Skalica",      "Didaktik M 93",         0 )
COMP( 1988, mistrum,  spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "Amaterske RADIO",       "Mistrum",               0 )  // keyboard could be spectrum in some models (since it was a build-yourself design)
COMP( 198?, bk08,     spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "Orel",                  "BK-08",                 0 )
COMP( 1990, blitzs,   spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "<unknown>",             "Blic",                  0 )  // no keyboard images found
COMP( 199?, orizon,   spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "<unknown>",             "Orizon-Micro",          0 )  // no keyboard images found
COMP( 1993, quorum48, spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "<unknown>",             "Kvorum 48K",            MACHINE_NOT_WORKING )
COMP( 1993, magic6,   spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "<unknown>",             "Magic 6",               MACHINE_NOT_WORKING )   // keyboard should be spectrum, but image was not clear
COMP( 1990, compani1, spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "<unknown>",             "Kompanion 1",           0 )  // no keyboard images found
COMP( 1990, spektrbk, spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "<unknown>",             "Spektr BK-001",         0 )
COMP( 1989, sintez2,  spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "Signal",                "Sintez 2",              0 )
COMP( 1990, zvezda,   spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "<unknown>",             "Zvezda",                0 )
COMP( 1991, santaka2, spectrum, 0,      spectrum_clone, spectrum,  spectrum_state, init_spectrum, "<unknown>",             "Santaka-002",           0 )



spellb.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Texas Instruments Spelling B hardware

The Spelling B was introduced together with the Speak & Spell. It is a handheld
educational toy with booklet. Two revisions of the hardware exist.

1st version:

Spelling B (US), 1978
- TMS0270 MCU TMC0272 (die label: 0272A T0270B)
- TMS1980 MCU TMC1984 (die label: 1980A 84A)
- 8-digit cyan VFD (seen with and without apostrophe)

2nd version:

Spelling B (US), 1980
- TMS0270 MCU TMC0274
- TMC0355 4KB VSM ROM CD2602
- 8-digit cyan VFD
- 1-bit sound (indicated by a music note symbol on the top-right of the casing)
- note: much rarer than the 1978 version, not much luck finding one on eBay.
  The words/indexes from the documentation are the same as the older version.

Spelling ABC (UK), 1980: exact same hardware as US 2nd version (the 1st version
was also sold in the UK earlier, but not renamed)

Spelling ABC (Germany), 1980: different VSM
- TMC0355 4KB VSM ROM CD2607

Mr. Challenger (US), 1979
- TMS0270 MCU TMC0273
- TMC0355 4KB VSM ROM CD2601
- 8-digit cyan VFD
- 1-bit sound

Letterlogic (UK), 1980: exact same hardware as US Mr. Challenger
- note: stylized as "LETTERlogic", same for other language versions

Letterlogic (France), 1980: different VSM
- TMC0355 4KB VSM ROM CD2603

Letterlogic (Germany), 1980: different VSM
- TMC0355 4KB VSM ROM CD2604

*******************************************************************************/

#include "emu.h"

#include "cpu/tms1000/tms0270.h"
#include "cpu/tms1000/tms0980.h"
#include "machine/tms6100.h"
#include "sound/spkrdev.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "spellb.lh"


namespace {

class spellb_state : public driver_device
{
public:
	spellb_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_subcpu(*this, "subcpu"),
		m_display(*this, "display"),
		m_tms6100(*this, "tms6100"),
		m_speaker(*this, "speaker"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void rev1(machine_config &config);
	void rev2(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(power_on);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices
	required_device<tms0270_cpu_device> m_maincpu;
	optional_device<tms1980_cpu_device> m_subcpu;
	required_device<pwm_display_device> m_display;
	optional_device<tms6100_device> m_tms6100;
	optional_device<speaker_sound_device> m_speaker;
	required_ioport_array<8> m_inputs;

	bool m_power_on = false;
	u32 m_r = 0;
	u16 m_grid = 0;
	u16 m_plate = 0;
	u16 m_sub_o = 0;
	u16 m_sub_r = 0;
	u8 m_rev1_ctl = 0;

	void power_off();
	void power_subcpu();
	void update_display();

	u8 main_read_k();
	void main_write_o(u16 data);
	void main_write_r(u32 data);

	u8 rev1_ctl_r();
	void rev1_ctl_w(u8 data);
	u8 sub_read_k();
	void sub_write_o(u16 data);
	void sub_write_r(u16 data);

	void rev2_write_o(u16 data);
	void rev2_write_r(u32 data);
};

void spellb_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_power_on));
	save_item(NAME(m_r));
	save_item(NAME(m_grid));
	save_item(NAME(m_plate));
	save_item(NAME(m_sub_o));
	save_item(NAME(m_sub_r));
	save_item(NAME(m_rev1_ctl));
}



/*******************************************************************************
    Power
*******************************************************************************/

void spellb_state::machine_reset()
{
	m_power_on = true;
	m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
	power_subcpu();
}

INPUT_CHANGED_MEMBER(spellb_state::power_on)
{
	if (newval && !m_power_on)
		machine_reset();
}

void spellb_state::power_off()
{
	m_power_on = false;
	m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
	power_subcpu();

	m_display->clear();
}

void spellb_state::power_subcpu()
{
	if (m_subcpu)
		m_subcpu->set_input_line(INPUT_LINE_RESET, m_power_on ? CLEAR_LINE : ASSERT_LINE);
}



/*******************************************************************************
    I/O
*******************************************************************************/

// common

void spellb_state::update_display()
{
	// almost same as snspell
	u16 gridmask = m_display->row_on(15) ? 0xffff : 0x8000;
	m_display->matrix(m_grid & gridmask, m_plate);
}

void spellb_state::main_write_o(u16 data)
{
	// reorder opla to led14seg, plus DP as d14 and AP as d15, same as snspell
	m_plate = bitswap<16>(data,12,15,10,7,8,9,11,6,13,3,14,0,1,2,4,5);
	update_display();
}

void spellb_state::main_write_r(u32 data)
{
	// R0-R7: select digit
	// R15: filament on
	m_grid = data & 0x80ff;
	update_display();

	// R13: power-off request, on falling edge
	if (~data & m_r & 0x2000)
		power_off();

	// R0-R6: input mux
	m_r = data;
}

u8 spellb_state::main_read_k()
{
	u8 data = 0;

	// K: multiplexed inputs
	for (int i = 0; i < 7; i++)
		if (BIT(m_r, i))
			data |= m_inputs[i]->read();

	// Vss row is always on
	return data | m_inputs[7]->read();
}


// 1st revision mcu/mcu comms

void spellb_state::rev1_ctl_w(u8 data)
{
	// main CTL write data
	m_rev1_ctl = data & 0xf;
}

u8 spellb_state::sub_read_k()
{
	// sub K8421 <- main CTL3210 (does not use external CS)
	if (m_r & 0x1000)
		return m_sub_o | m_rev1_ctl;
	else
		return m_sub_o | (m_plate & 0xe) | (m_plate >> 6 & 1);
}

void spellb_state::sub_write_o(u16 data)
{
	// sub O write data
	m_sub_o = bitswap<4>(data,6,0,4,3);
}

u8 spellb_state::rev1_ctl_r()
{
	// main CTL3210 <- sub O6043
	return m_sub_o;
}

void spellb_state::sub_write_r(u16 data)
{
	// sub R: unused?
	m_sub_r = data;
}


// 2nd revision specifics

void spellb_state::rev2_write_o(u16 data)
{
	// SEG DP: speaker out
	m_speaker->level_w(data >> 15 & 1);

	// SEG DP and SEG AP are not connected to VFD, rest is same as rev1
	main_write_o(data & 0x6fff);
}

void spellb_state::rev2_write_r(u32 data)
{
	// R12: TMC0355 CS
	// R4: TMC0355 M1
	// R6: TMC0355 M0
	m_tms6100->cs_w(data >> 12 & 1);
	m_tms6100->m1_w(data >> 4 & 1);
	m_tms6100->m0_w(data >> 6 & 1);
	m_tms6100->clk_w(1);
	m_tms6100->clk_w(0);

	// rest is same as rev1
	main_write_r(data);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( spellb )
	PORT_START("IN.0") // R0
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')

	PORT_START("IN.1") // R1
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("IN.2") // R2
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')

	PORT_START("IN.3") // R3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')

	PORT_START("IN.4") // R4
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')

	PORT_START("IN.5") // R5
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_NAME("Memory")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_NAME("Clue")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_NAME("Erase")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("Enter")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')

	PORT_START("IN.6") // R6
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_HOME) PORT_CHAR('1') PORT_NAME("Go")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_NAME("Off") // -> auto_power_off
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_NAME("Level")

	PORT_START("IN.7") // Vss
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_NAME("Missing Letter")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_NAME("Mystery Word")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_NAME("Scramble")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_F1) PORT_CHAR('6') PORT_NAME("Spelling B/On") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(spellb_state::power_on), 0)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_NAME("Starts With")
INPUT_PORTS_END

static INPUT_PORTS_START( spellabc )
	PORT_INCLUDE( spellb )

	PORT_MODIFY("IN.5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_NAME("Speicher")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_NAME("Rat")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_NAME("Tilgen")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("Eingabe")

	PORT_MODIFY("IN.6")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_NAME("Stufe")

	PORT_MODIFY("IN.7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_NAME("Was Fehlt?")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_NAME(u8"Wörter Rätsel")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_NAME("Wirr Warr")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_F1) PORT_CHAR('6') PORT_NAME("Lerne ABC/On") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(spellb_state::power_on), 0)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_NAME("Anfang Mit")
INPUT_PORTS_END


static INPUT_PORTS_START( mrchalgr )
	PORT_INCLUDE( spellb ) // same key layout as spellb

	PORT_MODIFY("IN.5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_NAME("2nd Player")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("Score")

	PORT_MODIFY("IN.7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_NAME("Crazy Letters")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_NAME("Letter Guesser")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_NAME("Word Challenge")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_F1) PORT_CHAR('6') PORT_NAME("Mystery Word/On") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(spellb_state::power_on), 0)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_NAME("Replay")
INPUT_PORTS_END

static INPUT_PORTS_START( letterlf )
	PORT_INCLUDE( mrchalgr )

	PORT_MODIFY("IN.5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_NAME("Joueur 2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_NAME("Aide")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_NAME("Effacez")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("Marque")

	PORT_MODIFY("IN.6")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_NAME("Niveau")

	PORT_MODIFY("IN.7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_NAME("Suite Folle")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_NAME("Devin")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_NAME("Duel")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_F1) PORT_CHAR('6') PORT_NAME("Mot Mystere/On") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(spellb_state::power_on), 0)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_NAME("Rejouez")
INPUT_PORTS_END

static INPUT_PORTS_START( letterlg )
	PORT_INCLUDE( mrchalgr )

	PORT_MODIFY("IN.5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_NAME("Spieler 2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_NAME("Rat")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_NAME("Tilgen")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("Punkte")

	PORT_MODIFY("IN.6")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_NAME("Stufe")

	PORT_MODIFY("IN.7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_NAME("Lettern Salat")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_NAME("Lettern Rater")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_NAME("Wettstreit")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_F1) PORT_CHAR('6') PORT_NAME(u8"Wörter Rätsel/On") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(spellb_state::power_on), 0)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_NAME("Wiedergabe")
INPUT_PORTS_END



/******************************************************************************
    Machine Configs
******************************************************************************/

void spellb_state::rev1(machine_config &config)
{
	// basic machine hardware
	TMS0270(config, m_maincpu, 320000); // approximation
	m_maincpu->read_k().set(FUNC(spellb_state::main_read_k));
	m_maincpu->write_o().set(FUNC(spellb_state::main_write_o));
	m_maincpu->write_r().set(FUNC(spellb_state::main_write_r));
	m_maincpu->read_ctl().set(FUNC(spellb_state::rev1_ctl_r));
	m_maincpu->write_ctl().set(FUNC(spellb_state::rev1_ctl_w));

	TMS1980(config, m_subcpu, 320000); // approximation
	m_subcpu->read_k().set(FUNC(spellb_state::sub_read_k));
	m_subcpu->write_o().set(FUNC(spellb_state::sub_write_o));
	m_subcpu->write_r().set(FUNC(spellb_state::sub_write_r));

	config.set_perfect_quantum(m_maincpu);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(16, 16);
	m_display->set_segmask(0xff, 0x3fff);
	config.set_default_layout(layout_spellb);

	// no sound!
}

void spellb_state::rev2(machine_config &config)
{
	// basic machine hardware
	TMS0270(config, m_maincpu, 320000); // approximation
	m_maincpu->read_k().set(FUNC(spellb_state::main_read_k));
	m_maincpu->write_o().set(FUNC(spellb_state::rev2_write_o));
	m_maincpu->write_r().set(FUNC(spellb_state::rev2_write_r));
	m_maincpu->read_ctl().set(m_tms6100, FUNC(tms6100_device::data_r));
	m_maincpu->write_ctl().set(m_tms6100, FUNC(tms6100_device::add_w));

	TMS6100(config, m_tms6100, 320000);
	m_tms6100->enable_4bit_mode(true);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(16, 16);
	m_display->set_segmask(0xff, 0x3fff);
	config.set_default_layout(layout_spellb);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( spellb )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "tmc0274n2l", 0x0000, 0x1000, CRC(98e3bd32) SHA1(e79b59ac29b0183bf1ee8d84b2944450c5e5d8fb) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_spellb_output.pla", 0, 1246, CRC(b95e35e6) SHA1(430917486856c9e6c28af10ff3758242048096c4) )

	ROM_REGION( 0x1000, "tms6100", 0 )
	ROM_LOAD( "cd2602", 0x0000, 0x1000, CRC(dd1fff8c) SHA1(f1760b29aa50fc96a1538db814cc73289654ac25) )
ROM_END

ROM_START( spellabc )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "tmc0274n2l", 0x0000, 0x1000, CRC(98e3bd32) SHA1(e79b59ac29b0183bf1ee8d84b2944450c5e5d8fb) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_spellb_output.pla", 0, 1246, CRC(b95e35e6) SHA1(430917486856c9e6c28af10ff3758242048096c4) )

	ROM_REGION( 0x1000, "tms6100", 0 )
	ROM_LOAD( "cd2607", 0x0000, 0x1000, CRC(875090c0) SHA1(73b87fff64054f6ab3b7e69d89585582145dbaa7) )
ROM_END

ROM_START( spellba )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "tmc0272nl", 0x0000, 0x1000, CRC(f90318ff) SHA1(7cff03fafbc66b0e07b3c70a513fbb0b11eef4ea) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_spellba_output.pla", 0, 1246, CRC(3e021cbd) SHA1(c9bdfe10601b8a5a70442fe4805e4bfed8bbed35) )

	ROM_REGION( 0x1000, "subcpu", 0 )
	ROM_LOAD( "tmc1984nl", 0x0000, 0x1000, CRC(78c9c83a) SHA1(6307fe2a0228fd1b8d308fcaae1b8e856d40fe57) )

	ROM_REGION( 1246, "subcpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "subcpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 525, "subcpu:opla", 0 )
	ROM_LOAD( "tms1980_spellba_output.pla", 0, 525, CRC(1e26a719) SHA1(eb031aa216fe865bc9e40b070ca5de2b1509f13b) )
ROM_END


ROM_START( mrchalgr )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "tmc0273nll", 0x0000, 0x1000, CRC(ef6d23bd) SHA1(194e3b022c299e99a731bbcfba5bf8a3a9f0d07e) ) // matches patent US4421487

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_mrchalgr_output.pla", 0, 1246, CRC(4785289c) SHA1(60567af0ea120872a4ccf3128e1365fe84722aa8) )

	ROM_REGION( 0x1000, "tms6100", 0 )
	ROM_LOAD( "cd2601", 0x0000, 0x1000, CRC(a9fbe7e9) SHA1(9d480cb30313b8cbce2d048140c1e5e6c5b92452) )
ROM_END

ROM_START( letterlf )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "tmc0273nll", 0x0000, 0x1000, CRC(ef6d23bd) SHA1(194e3b022c299e99a731bbcfba5bf8a3a9f0d07e) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_mrchalgr_output.pla", 0, 1246, CRC(4785289c) SHA1(60567af0ea120872a4ccf3128e1365fe84722aa8) )

	ROM_REGION( 0x1000, "tms6100", 0 )
	ROM_LOAD( "cd2603", 0x0000, 0x1000, CRC(70ac954b) SHA1(5593a5844063acdf399600e3e842f0fbe712ba69) )
ROM_END

ROM_START( letterlg )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "tmc0273nll", 0x0000, 0x1000, CRC(ef6d23bd) SHA1(194e3b022c299e99a731bbcfba5bf8a3a9f0d07e) )

	ROM_REGION( 1246, "maincpu:ipla", 0 )
	ROM_LOAD( "tms0980_common1_instr.pla", 0, 1246, CRC(42db9a38) SHA1(2d127d98028ec8ec6ea10c179c25e447b14ba4d0) )
	ROM_REGION( 2127, "maincpu:mpla", 0 )
	ROM_LOAD( "tms0270_common2_micro.pla", 0, 2127, CRC(86737ac1) SHA1(4aa0444f3ddf88738ea74aec404c684bf54eddba) )
	ROM_REGION( 1246, "maincpu:opla", 0 )
	ROM_LOAD( "tms0270_mrchalgr_output.pla", 0, 1246, CRC(4785289c) SHA1(60567af0ea120872a4ccf3128e1365fe84722aa8) )

	ROM_REGION( 0x1000, "tms6100", 0 )
	ROM_LOAD( "cd2604", 0x0000, 0x1000, CRC(cdb6f039) SHA1(56f512720c5e80cd74b65e31d5a19bf1260017fb) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT    COMPAT  MACHINE  INPUT     CLASS         INIT        COMPANY, FULLNAME, FLAGS
SYST( 1980, spellb,   0,        0,      rev2,    spellb,   spellb_state, empty_init, "Texas Instruments", "Spelling B (US, 1980 version)", MACHINE_SUPPORTS_SAVE )
SYST( 1978, spellba,  spellb,   0,      rev1,    spellb,   spellb_state, empty_init, "Texas Instruments", "Spelling B (US, 1978 version)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1980, spellabc, spellb,   0,      rev2,    spellabc, spellb_state, empty_init, "Texas Instruments", "Spelling ABC (Germany)", MACHINE_SUPPORTS_SAVE )

SYST( 1979, mrchalgr, 0,        0,      rev2,    mrchalgr, spellb_state, empty_init, "Texas Instruments", "Mr. Challenger (US)", MACHINE_SUPPORTS_SAVE )
SYST( 1980, letterlf, mrchalgr, 0,      rev2,    letterlf, spellb_state, empty_init, "Texas Instruments", "Letterlogic (France)", MACHINE_SUPPORTS_SAVE )
SYST( 1980, letterlg, mrchalgr, 0,      rev2,    letterlg, spellb_state, empty_init, "Texas Instruments", "Letterlogic (Germany)", MACHINE_SUPPORTS_SAVE )



spg110.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/******************************************************************************

    Short Description:

        die markings show

        "SunPlus PA7801" ( known as Sunplus SPG110? )
        JAKKS Classic Arcade Pinball
        JAKKS Spiderman 5-in-1 (original release)
        Conny TV Virtual Tennis
        Conny Ping Pong
        Conny TV Virtual Fighter
        JAKKS EA Sports (NHL 95 + Fifa 96) (EU)

        assumed:
        JAKKS EA Sports (NHL 95 + Madden 95) (US)

*******************************************************************************/

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "cpu/unsp/unsp.h"

#include "machine/nvram.h"
#include "machine/spg110.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class spg110_game_state : public driver_device
{
public:
	spg110_game_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen")
	{ }

	void spg110_base(machine_config &config);
	void spg110_base_pal(machine_config &config);

	ioport_value plunger_r();

	void init_crc();

protected:
	required_device<spg110_device> m_maincpu;
	required_device<screen_device> m_screen;

	virtual void mem_map(address_map &map) ATTR_COLD;
};

class spg110_easports_game_state : public spg110_game_state
{
public:
	spg110_easports_game_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg110_game_state(mconfig, type, tag)
	{ }
	void easports(machine_config &config);
	void easports_pal(machine_config &config);

private:
	virtual void mem_map(address_map &map) override ATTR_COLD;
};

class spg110_sstarkar_game_state : public spg110_game_state
{
public:
	spg110_sstarkar_game_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg110_game_state(mconfig, type, tag),
		m_cart(*this, "cartslot"),
		m_cartrom(*this, "cartrom")
	{ }

	void sstarkar(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<generic_slot_device> m_cart;
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
	optional_memory_bank m_cartrom;

	void mem_map_cart(address_map &map) ATTR_COLD;
};

/*************************
*    Machine Hardware    *
*************************/

void spg110_game_state::mem_map(address_map &map)
{
	map(0x004000, 0x3fffff).rom().region("maincpu", 0x8000);
}

void spg110_sstarkar_game_state::mem_map_cart(address_map &map)
{
	map(0x004000, 0x0fffff).bankr("cartrom");
}

void spg110_easports_game_state::mem_map(address_map &map)
{
	//map(0x001000, 0x007fff).ram(); // ??
	map(0x004000, 0x2fffff).rom().region("maincpu", 0x8000);
	map(0x3e0000, 0x3effff).ram().share("nvram"); // size? is all of it backed up?
	map(0x3f0000, 0x3fffff).ram(); // seems to only be used for GFX data
}


static INPUT_PORTS_START( jak_capb )
	PORT_START("PA")
	PORT_DIPNAME( 0x0001, 0x0000, "PA" )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0001, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( On ) )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Menu")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Left Flipper") // there are 2 physical buttons connected to this
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Nudge")// nudge (motion control)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Right Flipper") // there are 2 physical buttons connected to this
	PORT_DIPNAME( 0x1000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( On ) )

	PORT_START("PB")
	PORT_DIPNAME( 0x0001, 0x0000, "PB" )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0001, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( On ) )

	PORT_START("PC")
	PORT_DIPNAME( 0x0001, 0x0000, "PC" )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0001, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( On ) )

	PORT_START("JOYX")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("JOYY")
	PORT_BIT( 0x03ff, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(spg110_game_state::plunger_r))

	PORT_START("JOYY_REAL")
	PORT_BIT(0x00ff, 0x0000, IPT_PEDAL ) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_MINMAX(0x00,0x00ff) PORT_NAME("Plunger")  PORT_CENTERDELTA(255)
INPUT_PORTS_END


ioport_value spg110_game_state::plunger_r()
{
	// this is only needed because our PORT_CENTERDELTA doesn't work if set > 255 (and is limited in the menu to that) such a value that doesn't center quickly enough for the plunger to be effective
	return ioport("JOYY_REAL")->read()<<2;
}



static INPUT_PORTS_START( spg110_base )
	PORT_START("PA")
	PORT_DIPNAME( 0x0001, 0x0000, "PA" )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0001, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( On ) )

	PORT_START("PB")
	PORT_DIPNAME( 0x0001, 0x0000, "PB" )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0001, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( On ) )

	PORT_START("PC")
	PORT_DIPNAME( 0x0001, 0x0000, "PC" )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0001, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x0000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( On ) )

	PORT_START("JOYX")

	PORT_START("JOYY")
INPUT_PORTS_END


static INPUT_PORTS_START( jak_spdmo )
	PORT_INCLUDE( spg110_base )

	PORT_MODIFY("PA")
	PORT_BIT( 0x001f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Menu / Pause")
	PORT_BIT( 0x00c0, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON4 )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON3 )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("Joypad Right")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1) PORT_NAME("Joypad Left")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1) PORT_NAME("Joypad Down")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1) PORT_NAME("Joypad Up")
INPUT_PORTS_END

static INPUT_PORTS_START( jak_bobb )
	PORT_INCLUDE( spg110_base )

	PORT_MODIFY("PA")
	PORT_BIT( 0x001f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Menu / Pause")
	PORT_BIT( 0x00c0, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	// not technically a joystick, so 2 opposing buttons could be pressed, but maps where Joystick usually maps
	// using PORT_16WAY for now like ksys573.cpp
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("Right / Blue") PORT_16WAY
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1) PORT_NAME("Left / Green") PORT_16WAY
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1) PORT_NAME("Down / Red") PORT_16WAY
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1) PORT_NAME("Up / Yellow") PORT_16WAY
INPUT_PORTS_END

static INPUT_PORTS_START( easports )
	PORT_INCLUDE( spg110_base )

	PORT_MODIFY("PA")
	// not all units had 2 control pads
	// hold right and button 2 (B) on startup for ROM test
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(2) // START
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) // B
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) // A
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(2) // C

	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(1) // START
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) // B
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) // A
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) // C
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1)

	PORT_MODIFY("PC") // different configurations were hardwired, this can't be changed by the user
	PORT_CONFNAME(0x01, 0x00, "Pad Configuration")
	PORT_CONFSETTING(0x01, "Single")
	PORT_CONFSETTING(0x00, "Double")
INPUT_PORTS_END

static INPUT_PORTS_START( conyteni )
	PORT_INCLUDE( spg110_base )

	PORT_MODIFY("PB")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Console Back")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Console Enter")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_NAME("Console Right")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_NAME("Console Down")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_NAME("Console Left")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_NAME("Console Up")
INPUT_PORTS_END

void spg110_game_state::spg110_base(machine_config &config)
{
	SPG110(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg110_game_state::mem_map);
	m_maincpu->porta_in().set_ioport("PA");
	m_maincpu->portb_in().set_ioport("PB");
	m_maincpu->portc_in().set_ioport("PC");
	m_maincpu->adc_in<0>().set_ioport("JOYX");
	m_maincpu->adc_in<3>().set_ioport("JOYY");

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320, 262);
	m_screen->set_visarea(0, 320-1, 0, 240-1);
	m_screen->set_screen_update("maincpu", FUNC(spg110_device::screen_update));
	m_screen->screen_vblank().set(m_maincpu, FUNC(spg110_device::vblank));

	SPEAKER(config, "speaker", 2).front();
	m_maincpu->add_route(ALL_OUTPUTS, "speaker", 0.5, 0);
	m_maincpu->add_route(ALL_OUTPUTS, "speaker", 0.5, 1);
}

void spg110_game_state::spg110_base_pal(machine_config &config)
{
	spg110_base(config);
	m_screen->set_refresh_hz(50);
}


void spg110_easports_game_state::easports(machine_config &config)
{
	spg110_base(config);
	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);
}

void spg110_easports_game_state::easports_pal(machine_config &config)
{
	spg110_base_pal(config);
	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);
}



void spg110_sstarkar_game_state::machine_start()
{
	// if there's a cart, override the standard mapping
	if (m_cart && m_cart->exists())
	{
		m_cartrom->configure_entries(0, 1, m_cart->get_rom_base()+0x8000, 0x200000-0x8000);
		m_cartrom->set_entry(0);
	}
}

DEVICE_IMAGE_LOAD_MEMBER(spg110_sstarkar_game_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}


void spg110_sstarkar_game_state::sstarkar(machine_config &config)
{
	spg110_game_state::spg110_base(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &spg110_sstarkar_game_state::mem_map_cart);

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "singingstarkaraoke_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(spg110_sstarkar_game_state::cart_load));
	m_cart->set_must_be_loaded(true);

	SOFTWARE_LIST(config, "cart_list").set_original("singingstarkaraoke_cart");
}

void spg110_game_state::init_crc()
{
	// several games have a byte sum checksum listed at the start of ROM, this little helper function logs what it should match.
	const int length = memregion("maincpu")->bytes();
	const uint8_t* rom = memregion("maincpu")->base();
	int checksum_start = 0x10; // start at 0x10 because the 'checksum' text itself should not be checksummed
	uint32_t checksum = 0x00000000;
	for (int i = checksum_start; i < length; i++)
	{
		checksum += rom[i];
	}

	logerror("Calculated Byte Sum of bytes from %02x to 0x%08x is %08x)\n", checksum_start, length - 1, checksum);
}

ROM_START( jak_capb )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "classicarcadepinball.bin", 0x000000, 0x200000, CRC(b643dab0) SHA1(f57d546758ba442e28b5f0f48b3819b2fc2eb7f7) )
ROM_END

ROM_START( jak_sbdd )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	// header lists checksum (bytesum) as 05472c55, calculated (minus 0x10 byte header) gives 05472c17 (off by 0x3e, so there could be a bad byte or two)
	ROM_LOAD16_WORD_SWAP( "pineapple.u2", 0x000000, 0x100000, BAD_DUMP CRC(8a815acc) SHA1(6b174617b4c099c5c41fadb35e6cb1a8207045eb) )
ROM_END

ROM_START( jak_blue )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	// checksum matches header
	ROM_LOAD16_WORD_SWAP( "jakksbluesroom.u1", 0x000000, 0x100000, CRC(797b10f4) SHA1(3389fb940b67bebc4ce8976c9d67335787b2ecb3) )
ROM_END

ROM_START( jak_dood )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	// The checksum does *NOT* match the header here 03547c9a calculated, 0355a1ac in header  which means the checksum
	// is off by 00012512 so is unlikely to be flipped bits
	// The ROM also seems to abruptly end in the middle of a sound sample, but there is definitely no higher address
	// line connected (the CPU glob was entirely removed to check)
	// It seems most likely the game was just over 1Mbyte but the additonal data wasn't needed, so it got chopped down
	// without the checksum being corrected.
	ROM_LOAD16_WORD_SWAP( "jakksteledoodle.u2", 0x000000, 0x100000, CRC(ddc623a9) SHA1(92f72747c99f37525a6565e9587b357d629c560c) )
ROM_END

ROM_START( jak_ssmo )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	// checksum matches header
	ROM_LOAD16_WORD_SWAP( "jakkssillymakeover.u2", 0x000000, 0x100000, CRC(80cf7b10) SHA1(493b257eff0d479889bb5944ce8e7676f15639a4) )
ROM_END

ROM_START( jak_spdmo )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "spidermaneyes.bin", 0x000000, 0x200000, CRC(d5eaa6ae) SHA1(df226d378b41cf6ef90b9f72e48ff5e66385dcba) )
ROM_END

ROM_START( jak_spdmoa )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "spiderman.bin", 0x000000, 0x200000, CRC(b2a5a55a) SHA1(93c87ac0387997b753d4f0fb894a0dd02138b460) )
ROM_END

ROM_START( jak_bobb )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "bob.bin", 0x000000, 0x400000, CRC(16b0b39f) SHA1(43a45e5346d108a9ec1b672fa727e97722b4eaa1) )
ROM_END

ROM_START( jak_bobbo )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksbobfeb2006.bin", 0x000000, 0x400000, CRC(206712c4) SHA1(be07662e1776ebe6ea31c69d09e1d08c0886bb49) )
ROM_END


ROM_START( jak_thom )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksthomasus.u3", 0x000000, 0x400000, CRC(f43d5d12) SHA1(863a1cf08bc45f7bca7db7e2aae45071ac25adfa) )
ROM_END

ROM_START( jak_thomp )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksthomasuk.u3", 0x000000, 0x400000, CRC(2bbfa218) SHA1(d13d7d093c962d04f2c84571cceacf87e3768a78) )
ROM_END

ROM_START( easports )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "ea.u3", 0x000000, 0x400000, CRC(d750089e) SHA1(426f04c3d841103d434a892561db55ade684db54) )
	ROM_LOAD16_WORD_SWAP( "ea.u4", 0x400000, 0x200000, CRC(20a63445) SHA1(8c3383a353638c7d6b795d15a751275043eacbd0) )

	ROM_REGION( 0x20000, "nvram", ROMREGION_ERASE00 )
	// There is a coin style battery backing up at least some of the RAM.
	// The games seems to fail to initialize it properly first time, resulting in a hang shortly after
	// selecting a game. Provide a default (at least for now)
	// Random default fills, All 1, or All 0 do not work in this case
	ROM_LOAD( "nvram", 0x000000, 0x20000, CRC(bfcbd206) SHA1(0f5b730679762547a0658c2cd0d4fa5169b857af) )
ROM_END


ROM_START( easportsu )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "ea.u2", 0x000000, 0x400000, CRC(ca896683) SHA1(0d655e8a5e07e1259c358328fdc9f0084709ecc3) )
ROM_END


ROM_START( jak_wpt )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakk_worldpokertour.bin", 0x000000, 0x200000, CRC(6eac3bae) SHA1(80bf3ec0e0aa26b4bac72c1828939a18b7750f29) )

	// also has an LCD display on the unit, is the display driven by the main program, or something else?
ROM_END

ROM_START( jak_diso )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksdisneyred.u2", 0x000000, 0x200000, CRC(e88e117f) SHA1(0d5ffc831d47e46e72657d1b6bbd6ca18f76b2aa) )
ROM_END


ROM_START( conyteni )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "tennis_m29w160eb.bin", 0x000000, 0x200000, CRC(70050f17) SHA1(929f0d8599b7380b5994684424bb91063c4f6569) )

	// MCU (I/O?) read protected TODO: add NO_DUMP
ROM_END

ROM_START( conyping )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "tvvirtualpingpong.bin", 0x000000, 0x200000, CRC(6f5bf22e) SHA1(005e1580c4ab66db38682e797faebe8776eb58f7) )

	// MCU (I/O?) read protected TODO: add NO_DUMP
ROM_END

ROM_START( conyfght )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "mx29lw320.u2", 0x000000, 0x400000, CRC(515cce2c) SHA1(647cee206d23ff815d01f49cec391701b9a87de9) )

	// MCU (I/O?) read protected TODO: add NO_DUMP
ROM_END

ROM_START( sstarkar )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	// no internal BIOS
ROM_END

} // anonymous namespace


// JAKKS Pacific Inc TV games
CONS( 2004, jak_capb,  0,        0, spg110_base, jak_capb,  spg110_game_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd",      "Classic Arcade Pinball (JAKKS Pacific TV Game)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2004, jak_spdmo, jak_spdm, 0, spg110_base, jak_spdmo, spg110_game_state, empty_init, "JAKKS Pacific Inc / Digital Eclipse", "Spider-Man (JAKKS Pacific TV Game) (SPG110 hardware, set 1)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // this is the smaller more 'square' style joystick that was originally released before the GameKey slot was added.
CONS( 2004, jak_spdmoa,jak_spdm, 0, spg110_base, jak_spdmo, spg110_game_state, empty_init, "JAKKS Pacific Inc / Digital Eclipse", "Spider-Man (JAKKS Pacific TV Game) (SPG110 hardware, set 2)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // this is the redesigned stick, but before the GameKey release

CONS( 2004, jak_wpt,  0,         0, spg110_base, jak_spdmo, spg110_game_state, init_crc, "JAKKS Pacific Inc / Digital Eclipse", "World Poker Tour (JAKKS Pacific TV Game)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2004, jak_diso, jak_disn,  0, spg110_base, jak_spdmo, spg110_game_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd", "Disney (JAKKS Pacific TV Game) (SPG110 hardware, 28 MAY 2004 A)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// ROM bytesum is off by 0x3e, so likely a bad byte
// sound doesn't work properly after a few initial sounds (expected, SPG110 emulation issues)
// fill position in the 'Color Me Spongy' mini-game is completely wrong, maybe dump issue, maybe SPG110/unSP issue.
CONS( 2004, jak_sbdd,  0,        0, spg110_base, jak_spdmo, spg110_game_state, init_crc, "JAKKS Pacific Inc", "SpongeBob SquarePants Dilly Dabbler (JAKKS Pacific TV Game)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2004, jak_blue,  0,        0, spg110_base, jak_spdmo, spg110_game_state, init_crc, "JAKKS Pacific Inc", "Blue's Room: Coloring With Blue (JAKKS Pacific TV Game)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2004, jak_dood,  0,        0, spg110_base, jak_spdmo, spg110_game_state, init_crc, "JAKKS Pacific Inc", "Tele-Doodle (JAKKS Pacific TV Game)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2004, jak_ssmo,  0,        0, spg110_base, jak_spdmo, spg110_game_state, init_crc, "JAKKS Pacific Inc", "Super Silly Makeover (JAKKS Pacific TV Game)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )


// has Game-Key strings in test mode even if there were no SPG110 Game-Key units at all
CONS( 2006, jak_bobb,  0,        0, spg110_base, jak_bobb,  spg110_game_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd", "Bob the Builder - Project: Build It (JAKKS Pacific TV Game) (Jun  2 2006 14:42:01)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // UK?
CONS( 2006, jak_bobbo, jak_bobb, 0, spg110_base, jak_bobb,  spg110_game_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd", "Bob the Builder - Project: Build It (JAKKS Pacific TV Game) (Feb 28 2006 10:48:40)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // US?

CONS( 2006, jak_thom,  0,        0, spg110_base,     jak_bobb,  spg110_game_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd", "Thomas & Friends - Right on Time (JAKKS Pacific TV Game) (Jun 28 2006 18:24:37) (US, NTSC)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2006, jak_thomp, jak_thom, 0, spg110_base_pal, jak_bobb,  spg110_game_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd", "Thomas & Friends - Right on Time (JAKKS Pacific TV Game) (Jun 28 2006 18:01:22) (UK, PAL)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // PAL versions have different voices
// another version of Thomas & Friends (with a different screen layout, no map in the corner) also exists
// https://www.youtube.com/watch?v=PfEELfnXaXE
// is it an earlier version, or an SPG2xx port that loses the map for some reason?

// this was sold by SDW Games for the US market, ROM not yet verified to be the same, also appears in some multigames?
CONS( 2003, conyteni,  0,        0, spg110_base, conyteni,  spg110_game_state, empty_init, "Conny",      "TV Virtual Tennis", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // needs motion inputs, and video fixes, setting to PAL

// from a US SDW Games unit, has SDW Games banners in background so ROM might differ to other regions
CONS( 2003, conyping,  0,        0, spg110_base, conyteni,  spg110_game_state, empty_init, "Conny / SDW Games",      "Virtual Ping Pong (Conny / SDW Games)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// from a Big Ben 'TV Virtual Fighter' unit, although shows Free Fight Kung Fu on title screen
// Also sold by SDW Games as both TV Virtual Fighter and TV Kickboxing (unit still has TV Virtual Fighter stickers even when box is TV Kickboxing - possibly just box changed due to Sega?)
CONS( 200?, conyfght,  0,        0, spg110_base, conyteni,  spg110_game_state, empty_init, "Conny / Big Ben",      "TV Virtual Fighter / Free Fight Kung Fu (Conny / Big Ben)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// The unit contains no BIOS ROM, was sold by Taikee as Singing Star Karaoke, but also by Imaginarium / ItsMagical in Spain as Karao Kids.  Cartridges are compatible.
CONS( 200?, sstarkar,  0,        0, sstarkar, conyteni,  spg110_sstarkar_game_state, empty_init, "Taikee",      "Singing Star Karaoke (World) / Karao Kids (Spain)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // "ItsMagical" brand from Imaginarium

CONS( 2004, easports,  0,        0, easports_pal, easports,  spg110_easports_game_state, empty_init, "JAKKS Pacific Inc / Digital Eclipse",      "EA Sports Classics: NHL 95 & FIFA Soccer 96 (JAKKS Pacific TV Game)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2004, easportsu, easports, 0, easports,     easports,  spg110_easports_game_state, empty_init, "JAKKS Pacific Inc / Digital Eclipse",      "EA Sports Classics: NHL 95 & Madden 95 (JAKKS Pacific TV Game)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )



spg29x.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

        Mattel HyperScan

        08/17/2013 Skeleton driver by Sandro Ronco

        HyperScan TODO:
        - Various graphics glitches
        - Sound
        - USB

        Hyperscan has a hidden test menu that can be accessed with a specific inputs sequence:
        - During boot press and hold Select + Left Shoulder + Green until 'PLEASE WAIT' is shown on the screen
        - Press and release Red, Red, Green, Green, Yellow, Blue

****************************************************************************

        SPG290 Interrupt:

        Vector      Source
          63        SPU FIQ
          62        SPU Beatirq
          61        SPU Envirq
          60        CD servo
          59        ADC gain overflow / ADC recorder / FIFO overflow
          58        General purpose ADC
          57        Timer base
          56        Timer
          55        TV vblanking start
          54        LCD vblanking start
          53        PPU vblanking start
          52        TV
          51        Sensor frame end
          50        Sensor coordinate hit
          49        Sensor motion frame end
          48        Sensor capture done + sensor debug IRQ
          47        TV coordinate hit
          46        PPU coordinate hit
          45        USB host+device
          44        SIO
          43        SPI
          42        Uart (IrDA)
          41        NAND
          40        SD
          39        I2C master
          38        I2S slave
          37        APBDMA CH1
          36        APBDMA CH2
          35        LDM_DMA
          34        BLN_DMA
          33        APBDMA CH3
          32        APBDMA CH4
          31        Alarm + HMS
          30        MP4
          29        C3 (ECC module)
          28        GPIO
          27        Bufctl (for debug) + TV/PPU vblanking end (for debug)
          26        RESERVED1
          25        RESERVED2
          24        RESERVED3


-------

        CPU die markings on Big Buck Hunter "SunplusmM LU9001"


****************************************************************************/

#include "emu.h"
#include "cpu/score/score.h"
#include "imagedev/snapquik.h"
#include "machine/spg290_cdservo.h"
#include "machine/spg290_i2c.h"
#include "machine/spg290_ppu.h"
#include "machine/spg290_timer.h"
#include "hyperscan_card.h"
#include "hyperscan_ctrl.h"
#include "screen.h"
#include "softlist_dev.h"

#include "multibyte.h"


namespace {

class spg29x_game_state : public driver_device
{
public:
	spg29x_game_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen"),
		m_ppu(*this, "ppu"),
		m_i2c(*this, "i2c"),
		m_timers(*this, "timer%u", 0U),
		m_hyperscan_card(*this, "card"),
		m_hyperscan_ctrl(*this, "ctrl%u", 0U),
		m_leds(*this, "led%u", 0U)
	{ }

	void spg29x(machine_config &config);
	void hyperscan(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;

	required_device<score7_cpu_device> m_maincpu;
private:

	virtual void machine_start() override ATTR_COLD;

	uint32_t spg290_screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_hyper_exe);

	void spg290_mem(address_map &map) ATTR_COLD;
	void spg290_bios_mem(address_map &map) ATTR_COLD;

	void space_byte_w(offs_t offset, uint8_t data) { return m_maincpu->space(AS_PROGRAM).write_byte(offset, data); }
	uint32_t space_dword_r(offs_t offset)          { return m_maincpu->space(AS_PROGRAM).read_dword(offset); }

	uint16_t i2c_r(offs_t offset);

	required_device<screen_device> m_screen;
	required_device<spg290_ppu_device> m_ppu;
	required_device<spg290_i2c_device> m_i2c;
	required_device_array<spg290_timer_device, 6> m_timers;
	optional_device<hyperscan_card_device> m_hyperscan_card;
	optional_device_array<hyperscan_ctrl_device, 2> m_hyperscan_ctrl;
	output_finder<8> m_leds;

	void tve_control_w(offs_t offset, uint32_t data, uint32_t mem_mask);
	void gpio_out_w(offs_t offset, uint32_t data, uint32_t mem_mask);
	void timers_clk_sel_w(offs_t offset, uint32_t data, uint32_t mem_mask);

	uint16_t m_tve_control = 0;
	uint8_t  m_tve_fade_offset = 0;
	uint16_t m_timers_clk_sel = 0;
	uint8_t  m_tve_buffer_ctrl = 3 ;
	uint32_t m_tv_start_addr[3] = { 0 };
	uint16_t m_gpio_out = 0;
};

class spg29x_nand_game_state : public spg29x_game_state
{
public:
	spg29x_nand_game_state(const machine_config& mconfig, device_type type, const char* tag) :
		spg29x_game_state(mconfig, type, tag)
	{ }

	void nand_init(int blocksize, int blocksize_stripped);
	void nand_jak_bbh();
	void nand_jak_bbsf();

protected:
	void machine_reset() override ATTR_COLD;

	std::vector<uint8_t> m_strippedrom;

private:
	int m_firstvector = 0;
};

class spg29x_zonefamf_game_state : public spg29x_nand_game_state
{
public:
	spg29x_zonefamf_game_state(const machine_config& mconfig, device_type type, const char* tag) :
		spg29x_nand_game_state(mconfig, type, tag)
	{ }

	void nand_zonefamf();

protected:
	void machine_reset() override ATTR_COLD;

private:
};





void spg29x_game_state::timers_clk_sel_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	COMBINE_DATA(&m_timers_clk_sel);

	auto clock = 27_MHz_XTAL / ((data & 0xff) + 1);

	uint32_t mask = 0x100;
	for(int i=0; i<m_timers.size(); i++)
	{
		if (data & mask)
			m_timers[i]->set_clock(32.768_kHz_XTAL);
		else
			m_timers[i]->set_clock(clock);

		mask <<= 1;
	}
}

void spg29x_game_state::tve_control_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	COMBINE_DATA(&m_tve_control);
	rectangle visarea;
	switch(m_tve_control & 0xc)
	{
	case 0x0: // QVGA
		visarea.set(0, 320-1, 0, 240-1);
		break;
	case 0x4: // VGA
		visarea.set(0, 640-1, 0, 480-1);
		break;
	case 0x8: // HVGA
		visarea.set(0, 640-1, 0, 240-1);
		break;
	}

	int interlaced = m_tve_control & 1;
	if (m_tve_control & 2)
		m_screen->configure(864, 625, visarea, HZ_TO_ATTOSECONDS(27_MHz_XTAL) * 864 * 625 * (interlaced ? 2 : 1));      // PAL
	else
		m_screen->configure(858, 525, visarea, HZ_TO_ATTOSECONDS(27_MHz_XTAL) * 858 * 525 * (interlaced ? 2 : 1));      // NTSC
}

void spg29x_game_state::gpio_out_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	COMBINE_DATA(&m_gpio_out);

	if (ACCESSING_BITS_0_7)
		m_hyperscan_card->write(BIT(m_gpio_out,1));

	for(int i=0; i<8; i++)
		m_leds[i] = BIT(m_gpio_out, 5 + i);
}

uint16_t spg29x_game_state::i2c_r(offs_t offset)
{
	int port = (offset >> 4) & 0x0f;

	if (port < 2)
		return m_hyperscan_ctrl[port]->read(offset);

	return 0xffff;
}

uint32_t spg29x_game_state::spg290_screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	m_ppu->screen_update(screen, bitmap, cliprect);

	// TVE frame buffer
	if (m_tve_buffer_ctrl < 3)
	{
		auto &space = m_maincpu->space(AS_PROGRAM);
		const bool interlaced = m_tve_control & 1;
		for (int y = cliprect.min_y; y <= cliprect.max_y; y += (interlaced ? 1 : 2))
			for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
			{
				const uint16_t rgb = space.read_word(m_tv_start_addr[m_tve_buffer_ctrl] + (y * cliprect.width() + x) * 2);
				const rgb_t pix = rgb_t(pal5bit(rgb >> 11), pal6bit(rgb >> 5), pal5bit(rgb >> 0));

				bitmap.pix(y, x) = pix;
				if (!interlaced && cliprect.contains(x, y + 1))
					bitmap.pix(y + 1, x) = pix;
			}
	}

	if (m_tve_fade_offset)
	{
		int fade_offset = 255 - m_tve_fade_offset;
		for (int y=0; y <= cliprect.max_y; y++)
			for (int x=0; x <= cliprect.max_x; x++)
			{
				rgb_t pix(bitmap.pix(y, x));
				bitmap.pix(y, x) = rgb_t(pix.r() * fade_offset / 255, pix.g() * fade_offset / 255, pix.b() * fade_offset / 255);
			}
	}

	return 0;
}

void spg29x_game_state::spg290_mem(address_map &map)
{
	map.global_mask(0x1fffffff);
	map(0x00000000, 0x00ffffff).ram().mirror(0x07000000);

	map(0x08030000, 0x08030003).w(FUNC(spg29x_game_state::tve_control_w)).lr32(NAME([this](uint32_t data) { return m_tve_control; }));
	map(0x0803000c, 0x0803000f).lw32(NAME([this](uint32_t data) { m_tve_fade_offset = data & 0xff; }));
	map(0x08070000, 0x0807000b).lr32(NAME([this](offs_t offset) { return m_tv_start_addr[offset]; }));
	map(0x08070000, 0x0807000b).lw32(NAME([this](offs_t offset, uint32_t data, uint32_t mem_mask) { COMBINE_DATA(&m_tv_start_addr[offset]); }));
	map(0x08090020, 0x08090023).lw32(NAME([this](uint32_t data) { m_tve_buffer_ctrl = data & 3; }));
	map(0x0807006c, 0x0807006f).lr32(NAME([]() { return 0x01;}));               // MUI Status: SDRAM is in the self-refresh mode
	//map(0x08150000, 0x08150000).lw8(NAME([this](uint8_t data) { printf("%c", data); })); // UART
	map(0x082100e4, 0x082100e7).w(FUNC(spg29x_game_state::timers_clk_sel_w)).lr32(NAME([this]() { return m_timers_clk_sel; }));       // Timer Source Clock Selection
	map(0x08240000, 0x0824000f).noprw();

	//map(0x08000000, 0x0800ffff);  // CSI
	map(0x08010000, 0x0801ffff).m("ppu", FUNC(spg290_ppu_device::map));
	//map(0x08020000, 0x0802ffff);  // JPG
	//map(0x08030000, 0x0803ffff);  // TV
	//map(0x08040000, 0x0804ffff);  // LCD
	//map(0x08050000, 0x0805ffff);  // SPU
	map(0x08060000, 0x0806ffff).rw("cdservo", FUNC(spg290_cdservo_device::read), FUNC(spg290_cdservo_device::write));
	//map(0x08070000, 0x0807ffff);  // MIU
	//map(0x08080000, 0x0808ffff);  // APBDMA
	//map(0x08090000, 0x0809ffff);  // BUFCTL
	//map(0x080a0000, 0x080affff);  // IRQCTL
	//map(0x080b0000, 0x080bffff);  // GPUBUF
	//map(0x080c0000, 0x080cffff);  // LDMDMA
	//map(0x080d0000, 0x080dffff);  // BLNDMA
	//map(0x080e0000, 0x080effff);  // TPGBUF
	//map(0x080f0000, 0x080fffff);  // AHBDEC
	//map(0x08100000, 0x0810ffff);  // GPIO
	//map(0x08110000, 0x0811ffff);  // SPI
	//map(0x08120000, 0x0812ffff);  // SIO
	map(0x08130000, 0x0813ffff).rw("i2c", FUNC(spg290_i2c_device::read), FUNC(spg290_i2c_device::write));
	//map(0x08140000, 0x0814ffff);  // I2S
	//map(0x08150000, 0x0815ffff);  // UART
	map(0x08160000, 0x08160fff).rw(m_timers[0], FUNC(spg290_timer_device::read), FUNC(spg290_timer_device::write));
	map(0x08161000, 0x08161fff).rw(m_timers[1], FUNC(spg290_timer_device::read), FUNC(spg290_timer_device::write));
	map(0x08162000, 0x08162fff).rw(m_timers[2], FUNC(spg290_timer_device::read), FUNC(spg290_timer_device::write));
	map(0x08163000, 0x08163fff).rw(m_timers[3], FUNC(spg290_timer_device::read), FUNC(spg290_timer_device::write));
	map(0x08164000, 0x08164fff).rw(m_timers[4], FUNC(spg290_timer_device::read), FUNC(spg290_timer_device::write));
	map(0x08165000, 0x08165fff).rw(m_timers[5], FUNC(spg290_timer_device::read), FUNC(spg290_timer_device::write));
	//map(0x08166000, 0x08166fff);  // RTC
	//map(0x08170000, 0x0817ffff);  // WDOG
	//map(0x08180000, 0x0818ffff);  // SD
	//map(0x08190000, 0x0819ffff);  // FLASH
	//map(0x081a0000, 0x081affff);  // ADC
	//map(0x081b0000, 0x081bffff);  // USB device
	//map(0x081c0000, 0x081cffff);  // USB host
	//map(0x081d0000, 0x081dffff);  // reserved
	//map(0x081e0000, 0x081effff);  // Reserved
	//map(0x081f0000, 0x081fffff);  // reserved
	//map(0x08200000, 0x0820ffff);  // SFTCFG
	//map(0x08210000, 0x0821ffff);  // CKG
	map(0x0821006c, 0x0821006f).w(m_timers[0], FUNC(spg290_timer_device::control_w));
	map(0x08210070, 0x08210073).w(m_timers[1], FUNC(spg290_timer_device::control_w));
	map(0x08210074, 0x08210077).w(m_timers[2], FUNC(spg290_timer_device::control_w));
	map(0x08210078, 0x0821007b).w(m_timers[3], FUNC(spg290_timer_device::control_w));
	map(0x0821007c, 0x0821007f).w(m_timers[4], FUNC(spg290_timer_device::control_w));
	map(0x08210080, 0x08210083).w(m_timers[5], FUNC(spg290_timer_device::control_w));
	//map(0x08220000, 0x0822ffff);  // MP4
	//map(0x08230000, 0x0823ffff);  // MIU2
	//map(0x08240000, 0x0824ffff);  // ECC

	map(0x0a000000, 0x0a003fff).ram();                         // internal SRAM
	map(0x0b000000, 0x0b007fff).rom().region("spg290", 0);  // internal ROM
}

void spg29x_game_state::spg290_bios_mem(address_map& map)
{
	spg290_mem(map);
	map(0x08200024, 0x08200027).w(FUNC(spg29x_game_state::gpio_out_w)).lr32(NAME([this]() { return m_gpio_out; }));
	map(0x08200068, 0x0820006b).lr32(NAME([this]() { return m_hyperscan_card->read(); }));
	map(0x10000000, 0x100fffff).rom().region("bios", 0).mirror(0x0ff00000);
}

/* Input ports */
static INPUT_PORTS_START( hyperscan )
INPUT_PORTS_END


void spg29x_game_state::machine_start()
{
	m_leds.resolve();

	save_item(NAME(m_tve_control));
	save_item(NAME(m_tve_fade_offset));
	save_item(NAME(m_timers_clk_sel));
	save_item(NAME(m_tve_buffer_ctrl));
	save_item(NAME(m_tv_start_addr));
	save_item(NAME(m_gpio_out));
}

void spg29x_game_state::machine_reset()
{
	m_tve_control = 0;
	m_tve_fade_offset = 0;
	m_timers_clk_sel = 0;
	m_tve_buffer_ctrl = 3;
	m_tv_start_addr[0] = m_tv_start_addr[1] = m_tv_start_addr[2] = 0;
	m_gpio_out = 0;

	// disable JTAG
	m_maincpu->set_state_int(SCORE_CR + 29, 0x20000000);

	// boot from Internal ROM - doesn't currently work as the internal ROM needs to correctly detect the external configuration before booting
	// m_maincpu->set_state_int(SCORE_PC, 0x8b000000);
}

void spg29x_nand_game_state::machine_reset()
{
	spg29x_game_state::machine_reset();

	uint32_t bootstrap_ram_start = get_u32le(&m_strippedrom[m_firstvector+0]);
	uint32_t bootstrap_ram_end   = get_u32le(&m_strippedrom[m_firstvector+4]);
	uint32_t bootstrap_ram_boot  = get_u32le(&m_strippedrom[m_firstvector+8]);

	// there is a 0x01 at 0x26, possibly related to source location / block in NAND to copy from?

	logerror("NAND Bootstrap RAM start: %08x RAM end: %08x RAM boot: %08x", bootstrap_ram_start, bootstrap_ram_end, bootstrap_ram_boot);

	uint32_t sourceaddr = 0x10000;
	for (uint32_t addr = bootstrap_ram_start; addr <= bootstrap_ram_end; addr++)
	{
		address_space& mem = m_maincpu->space(AS_PROGRAM);
		uint8_t byte = m_strippedrom[sourceaddr];
		mem.write_byte(addr, byte);
		sourceaddr++;
	}

	// probably jumped to from internal ROM?
	m_maincpu->set_state_int(SCORE_PC, bootstrap_ram_boot);
}

void spg29x_zonefamf_game_state::machine_reset()
{
	spg29x_game_state::machine_reset();

	uint32_t sourceaddr = 0x80000;
	for (uint32_t addr = 0; addr <= 0x80000; addr++)
	{
		address_space& mem = m_maincpu->space(AS_PROGRAM);
		uint8_t byte = m_strippedrom[sourceaddr];
		mem.write_byte(addr, byte);
		sourceaddr++;
	}

	m_maincpu->set_state_int(SCORE_PC, 0x4);
}



QUICKLOAD_LOAD_MEMBER(spg29x_game_state::quickload_hyper_exe)
{
	const uint32_t length = image.length();

	auto [err, ptr, actual] = read(image.image_core_file(), length);
	if (err || (actual != length))
		return std::make_pair(err ? err : std::errc::io_error, std::string());

	auto &space = m_maincpu->space(AS_PROGRAM);
	for (uint32_t i = 0; i < length; i++)
		space.write_byte(0xa00901fc + i, ptr[i]);

	m_maincpu->set_state_int(SCORE_PC, 0xa0091000); // Game entry point

	return std::make_pair(std::error_condition(), std::string());
}

void spg29x_game_state::spg29x(machine_config &config)
{
	/* basic machine hardware */
	SCORE7(config, m_maincpu, 27_MHz_XTAL * 4);   // 108MHz S+core 7
	m_maincpu->set_addrmap(AS_PROGRAM, &spg29x_game_state::spg290_mem);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(27_MHz_XTAL, 858, 0, 640, 525, 0, 480);
	m_screen->set_screen_update(FUNC(spg29x_game_state::spg290_screen_update));
	m_screen->screen_vblank().set(m_ppu, FUNC(spg290_ppu_device::screen_vblank));

	for (int i=0; i<6; i++)
	{
		SPG290_TIMER(config, m_timers[i], 27_MHz_XTAL);
		m_timers[i]->irq_cb().set_inputline(m_maincpu, 56);
	}

	SPG290_PPU(config, m_ppu, 27_MHz_XTAL, m_screen);
	m_ppu->vblank_irq_cb().set_inputline(m_maincpu, 53);
	m_ppu->space_read_cb().set(FUNC(spg29x_game_state::space_dword_r));

	spg290_cdservo_device &cdservo(SPG290_CDSERVO(config, "cdservo", 27_MHz_XTAL, "cdrom"));
	cdservo.irq_cb().set_inputline(m_maincpu, 60);
	cdservo.space_write_cb().set(FUNC(spg29x_game_state::space_byte_w));

	SPG290_I2C(config, m_i2c, 27_MHz_XTAL);
	m_i2c->irq_cb().set_inputline(m_maincpu, 39);
}

void spg29x_game_state::hyperscan(machine_config &config)
{
	spg29x(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg29x_game_state::spg290_bios_mem);

	m_i2c->i2c_read_cb().set(FUNC(spg29x_game_state::i2c_r));

	CDROM(config, "cdrom").set_interface("cdrom");

	HYPERSCAN_CTRL(config, m_hyperscan_ctrl[0], 0);
	HYPERSCAN_CTRL(config, m_hyperscan_ctrl[1], 0);

	HYPERSCAN_CARD(config, m_hyperscan_card, 0);

	SOFTWARE_LIST(config, "cd_list").set_original("hyperscan");
	SOFTWARE_LIST(config, "card_list").set_original("hyperscan_card");

	QUICKLOAD(config, "quickload", "exe").set_load_callback(FUNC(spg29x_game_state::quickload_hyper_exe));
}

void spg29x_nand_game_state::nand_init(int blocksize, int blocksize_stripped)
{
	uint8_t* rom = memregion("nand")->base();
	int size = memregion("nand")->bytes();

	int numblocks = size / blocksize;

	m_strippedrom.resize(numblocks * blocksize_stripped);

	for (int i = 0; i < numblocks; i++)
	{
		const int base = i * blocksize;
		const int basestripped = i * blocksize_stripped;

		for (int j = 0; j < blocksize_stripped; j++)
		{
			m_strippedrom[basestripped + j] = rom[base + j];
		}
	}

	// debug to allow for easy use of unidasm.exe
	if (0)
	{
		auto filename = "stripped_" + std::string(machine().system().name);
		auto fp = fopen(filename.c_str(), "w+b");
		if (fp)
		{
			fwrite(&m_strippedrom[0], blocksize_stripped * numblocks, 1, fp);
			fclose(fp);
		}
	}
}

void spg29x_nand_game_state::nand_jak_bbh()
{
	nand_init(0x210, 0x200);
	m_firstvector = 0xc;
}

void spg29x_nand_game_state::nand_jak_bbsf()
{
	nand_init(0x210, 0x200);
	m_firstvector = 0x8;
}

void spg29x_zonefamf_game_state::nand_zonefamf()
{
	nand_init(0x840, 0x800);
//  m_firstvector = 0x8;
}

/* ROM definition */
ROM_START( hyprscan )
	ROM_REGION( 0x100000, "bios", ROMREGION_32BIT | ROMREGION_LE )
	ROM_LOAD32_DWORD("hyperscan.bin", 0x000000, 0x100000, CRC(ce346a14) SHA1(560cb747e7193e6781d4b8b0bd4d7b45d3d28690))

	ROM_REGION( 0x008000, "spg290", ROMREGION_32BIT | ROMREGION_LE )
	ROM_LOAD32_DWORD("spg290.bin", 0x000000, 0x008000, CRC(41aad748) SHA1(3f65f8e88b1c5e9cbc8b39bb3228ebf616aced5a) ) // 256Kbit SPG290 internal ROM
ROM_END

// the sets below might be using the same SPG290 internal ROM as the above but configured to load from NAND
// however as the CPU dies were under epoxy globs the exact chip models are not confirmed

ROM_START( jak_bbh )
	ROM_REGION( 0x4200000, "nand", 0 ) // ID returned C25A, read as what appears to be a compatible type.
	ROM_LOAD("bigbuckhunterpro_as_hy27us0812a_c25a.bin", 0x000000, 0x4200000, CRC(e2627540) SHA1(c8c6e5fbc4084fa695390bbb4e1e52e671f050da) )

	ROM_REGION( 0x008000, "spg290", ROMREGION_32BIT | ROMREGION_LE )
	ROM_LOAD32_DWORD("internal.rom", 0x000000, 0x008000, NO_DUMP)
ROM_END


ROM_START( jak_bbsf )
	ROM_REGION( 0x4200000, "nand", 0 )
	ROM_LOAD("bigbucksafari.bin", 0x000000, 0x4200000, CRC(dc5f9bf1) SHA1(27893c396d62f353ced52ef88fd9ade5c051598f) )

	ROM_REGION( 0x008000, "spg290", ROMREGION_32BIT | ROMREGION_LE )
	ROM_LOAD32_DWORD("internal.rom", 0x000000, 0x008000, NO_DUMP)
ROM_END

ROM_START( zonefamf )
	ROM_REGION( 0x21000000, "nand", 0 )
	ROM_LOAD("hy27uf084g2m_withspare.u1", 0x000000, 0x21000000, CRC(ee12b689) SHA1(fd9c708b6bb2e7574173a140d8839869a8c9f51a) )

	ROM_REGION( 0x008000, "spg290", ROMREGION_32BIT | ROMREGION_LE )
	ROM_LOAD32_DWORD("internal.rom", 0x000000, 0x008000, NO_DUMP)

	//has 1x 48LC8M16A2 (128Mbit/16MByte SDRAM) for loading game into
ROM_END

ROM_START( prail07 )
	ROM_REGION( 0x8400000, "nand", 0 )
	ROM_LOAD("hy27uf081g2a.u13", 0x000000, 0x8400000, CRC(2bbe73a7) SHA1(f6af701a372f2600ed4d7df957d8fcaf164bb61b) )

	ROM_REGION( 0x400, "seeprom", 0 ) // probably just unlockables
	ROM_LOAD("24c08.u0", 0x000000, 0x400, CRC(b998dfb0) SHA1(6ab8d7299a1c04d2797cedfaa35ac09bfabfd001) )

	ROM_REGION( 0x008000, "spg290", ROMREGION_32BIT | ROMREGION_LE )
	ROM_LOAD32_DWORD("internal.rom", 0x000000, 0x008000, NO_DUMP)
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME  PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY   FULLNAME     FLAGS
COMP( 2006, hyprscan,   0,      0,      hyperscan, hyperscan, spg29x_game_state, empty_init, "Mattel", "HyperScan", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )

// There were 1 player and 2 player versions for these JAKKS guns.  The 2nd gun appears to be simply a controller (no AV connectors) but as they were separate products with the 2 player verisons being released up to a year after the original, the code could differ.
// If they differ, it is currently uncertain which versions these ROMs are from
COMP( 2009, jak_bbh,    0,      0,      spg29x, hyperscan, spg29x_nand_game_state, nand_jak_bbh, "JAKKS Pacific Inc", "Big Buck Hunter Pro (JAKKS Pacific TV Game)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) //has ISSI 404A (24C04)
COMP( 2011, jak_bbsf,   0,      0,      spg29x, hyperscan, spg29x_nand_game_state, nand_jak_bbsf,"JAKKS Pacific Inc", "Big Buck Safari (JAKKS Pacific TV Game)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // has ISSI 416A (24C16)

COMP( 201?, zonefamf,  0,      0,      spg29x, hyperscan, spg29x_zonefamf_game_state, nand_zonefamf,"Zone", "Zone Family Fit", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
// uses SPG291A-Hl171
COMP( 2007, prail07,   0,      0,      spg29x, hyperscan, spg29x_zonefamf_game_state, nand_zonefamf,"Tomy Takara", "Boku wa Plarail Untenshi - Shinkansen de Ikou! (2007 version) (Japan)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )

// the sets in spg29x_lexibook_jg7425.cpp probably also belong here, as they use an SPG293 which has the same peripheral mappings (but they make use of additional features)
// see emu293 https://github.com/gatecat/emu293



spg29x_lexibook_jg7425.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood


/*

NOTE: these are SPG293 based, which seems to be the same as SPG290 (hyperscan) but maybe with some different on-die modules available.
      see https://github.com/gatecat/emu293

---------

a 221 games console which uses a 4GB sd-card for gamestorage and a MX29LV160 flashrom for the internal bios. (only 512kb are used from the 2mb romspace)
Starting the console without SD-Card just show's a looping video with "Please insert Memory Card".

SD card image produced with WinHex (hardware write blocker used to prevent Windows from corrupting data)
compressed with "chdman createhd -i 4GBSD.img -o lexibook_jg7425_4gbsd.chd" (is this correct?)

TODO:
is there an internal ROM / bootstrap area, or does this SunPlus core use vectors in a different way to the one in hyperscan.cpp?

(only noteworthy features of PCB are ROM + RAM + Cpu Glob)

*/

#include "emu.h"
#include "screen.h"
#include "cpu/score/score.h"


namespace {

class lexibook_jg7425_state : public driver_device
{
public:
	lexibook_jg7425_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen"),
		m_mainram(*this, "mainram"),
		m_romregion(*this, "extrom")
	{ }

	void lexibook_jg7425(machine_config &config);

	void map(address_map &map) ATTR_COLD;

protected:
	uint32_t screen_update(screen_device& screen, bitmap_rgb32& bitmap, const rectangle& cliprect);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<score7_cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_shared_ptr<uint32_t> m_mainram;
	required_region_ptr<uint8_t> m_romregion;
};



void lexibook_jg7425_state::machine_start()
{
}

void lexibook_jg7425_state::machine_reset()
{
	// I think this code should be running from RAM at least, probably some kind of bootstrap / internal ROM to copy it? (hyperscan.cpp indicates that SoC can have internal ROM at least)

	/*

	the first 0x20 bytes of the ROM seem to be some pointers

	F7 FF FE FF
	FF FF FF FF
	FF FF FF FF
	FC 01 50 A0 (address A05001FC) - load address for the main program
	FC 01 70 A0 (address A07001FC) - maybe top of program to copy?
	00 10 50 A0 (address A0501000) - entry point??
	20 00 00 98 (address 98000020)
	00 00 00 00

	then there is a program which loads at A05001FC between 0x20 - 0x6DBD3
	this program is then partially repeated until it is abruptly cut off
	with a block of 00 at 0x7ff20
	the 2nd half of the ROM (0x80000+) is just 0xff fill

	*/

	uint32_t loadaddr = (m_romregion[0x0c] << 0) | (m_romregion[0x0d] << 8) | (m_romregion[0x0e] << 16) | (m_romregion[0x0f] << 24);
	uint32_t endaddr  = (m_romregion[0x10] << 0) | (m_romregion[0x11] << 8) | (m_romregion[0x12] << 16) | (m_romregion[0x13] << 24);
	uint32_t entry    = (m_romregion[0x14] << 0) | (m_romregion[0x15] << 8) | (m_romregion[0x16] << 16) | (m_romregion[0x17] << 24);

	uint8_t* rom = (uint8_t*)&m_romregion[0];

	for (int i = 0; i < endaddr-loadaddr; i++)
	{
		uint32_t data = rom[0x20 + i];

		m_maincpu->space(AS_PROGRAM).write_byte(loadaddr+i, data);
	}

	m_maincpu->set_state_int(SCORE_PC, entry);
}

static INPUT_PORTS_START( lexibook_jg7425 )
INPUT_PORTS_END


uint32_t lexibook_jg7425_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void lexibook_jg7425_state::map(address_map &map)
{

	map(0xa0000000, 0xa0ffffff).ram().share("mainram");

	map(0x9f000000, 0x9fffffff).ram().share("mainram");
	map(0xbf000000, 0xbfffffff).ram().share("mainram");

	// it quickly ends up jumping to BF000024, which is probably internal ROM - can we simulate what it wants?
}


void lexibook_jg7425_state::lexibook_jg7425(machine_config &config)
{
	/* basic machine hardware */
	SCORE7(config, m_maincpu, XTAL(27'000'000) * 4);   // ? not certain on exact type
	m_maincpu->set_addrmap(AS_PROGRAM, &lexibook_jg7425_state::map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320, 262);
	m_screen->set_visarea(0, 320-1, 0, 240-1);
	m_screen->set_screen_update(FUNC(lexibook_jg7425_state::screen_update));
}

ROM_START( fundr200 )
	ROM_REGION( 0x200000, "extrom", ROMREGION_ERASEFF )
	ROM_LOAD( "mx29lv160.u6", 0x000000, 0x200000, CRC(43c90080) SHA1(4c9e5c8f880d40bd684357ce67ae45c3f5d24b62) )

	DISK_REGION( "ata:0:hdd" ) /* 4GB SD Card */
	DISK_IMAGE( "funderdome", 0, SHA1(ffe80581455ed41acb2e968d25f29a2c2a173b54) )
ROM_END

ROM_START( lx_jg7425 )
	ROM_REGION( 0x200000, "extrom", ROMREGION_ERASEFF )
	ROM_LOAD( "mx29lv160.u6", 0x000000, 0x200000, CRC(43c90080) SHA1(4c9e5c8f880d40bd684357ce67ae45c3f5d24b62) )

	DISK_REGION( "ata:0:hdd" ) /* 4GB SD Card */
	DISK_IMAGE( "lexibook_jg7425_4gbsd", 0, SHA1(dc0985103edec3992efdd493feef6185daedb3fd) )
ROM_END

ROM_START( lx_aven )
	ROM_REGION( 0x200000, "extrom", ROMREGION_ERASEFF )
	ROM_LOAD( "29lv800.bin", 0x000000, 0x100000, CRC(7b107f6c) SHA1(3a8e37e51dab5cab9977261e0ac17ba5194a9370) )

	DISK_REGION( "ata:0:hdd" ) /* 4GB SD Card */
	DISK_IMAGE( "sd-card", 0, SHA1(911da7bf7dac391e3329e17e3f411caafac52f0f) )
ROM_END

ROM_START( lx_frozen )
	ROM_REGION( 0x200000, "extrom", ROMREGION_ERASEFF )
	ROM_LOAD( "29lv800.bin", 0x000000, 0x100000, CRC(7b107f6c) SHA1(3a8e37e51dab5cab9977261e0ac17ba5194a9370) )

	DISK_REGION( "ata:0:hdd" ) /* 4GB SD Card */
	DISK_IMAGE( "sdcard", 0, SHA1(0d727815ba06d7bfe8e092007e24d4931b302ef9) )
ROM_END

ROM_START( zone3d )
	ROM_REGION( 0x100000, "extrom", 0 ) // SPI ROM in this case
	ROM_LOAD("zone_25l8006e_c22014.bin", 0x000000, 0x100000, CRC(8c571771) SHA1(cdb46850286d31bf58d45b75ffc396ed774ac4fd) )

	/*
	model: Lexar SD
	revision: LX01
	serial number: 00000000XL10

	size: 362.00 MiB (741376 sectors * 512 bytes)
	unk1: 0000000000000007
	unk2: 00000000000000fa
	unk3: 01

	The SD card has no label, but there's some printing on the back:
	MMAGF0380M3085-WY
	TC00201106 by Taiwan

	--
	Dumped with hardware write blocker, so this image is correct, and hasn't been corrupted by Windows

	Image contains a FAT filesystem with a number of compressed? programs that presumably get loaded into RAM by
	the bootloader in the serial flash ROM
	*/

	DISK_REGION( "cfcard" )
	DISK_IMAGE( "zone3d", 0, SHA1(77971e2dbfb2ceac12f482d72539c2e042fd9108) )

	ROM_REGION( 0x008000, "spg290", ROMREGION_32BIT | ROMREGION_LE )
	ROM_LOAD32_DWORD("internal.rom", 0x000000, 0x008000, NO_DUMP)
ROM_END



ROM_START( ubox30 )
	ROM_REGION( 0x200000, "extrom", 0 ) // SPI ROM in this case
	ROM_LOAD("ubox_xm25qe16bzig_204015.bin", 0x000000, 0x200000, CRC(f8135947) SHA1(f2a075ae2b0bae186202f1019b566ffc411742bb) )
	ROM_IGNORE(0x300)

	DISK_REGION( "cfcard" )
	DISK_IMAGE( "ubox_sd_512", 0, SHA1(99f2f1437d644a5e1fe48ce1445acf48fb8b0359) )

	ROM_REGION( 0x008000, "spg290", ROMREGION_32BIT | ROMREGION_LE )
	ROM_LOAD32_DWORD("internal.rom", 0x000000, 0x008000, NO_DUMP)
ROM_END


} // anonymous namespace

CONS( 2015, fundr200,    0,         0,     lexibook_jg7425,   lexibook_jg7425, lexibook_jg7425_state, empty_init, "Funderdome", "Funderdome Video Game Entertainment System 200+ Games (FUN-GAME32-1)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING ) // FUN-GAME32-1 on manual
CONS( 2015, lx_jg7425,   0,         0,     lexibook_jg7425,   lexibook_jg7425, lexibook_jg7425_state, empty_init, "Lexibook", "Lexibook JG7425 221-in-1", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2016, lx_aven,     0,         0,     lexibook_jg7425,   lexibook_jg7425, lexibook_jg7425_state, empty_init, "Lexibook", "Marvel Avengers TV Game Console (32-bit, Lexibook)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2016, lx_frozen,   0,         0,     lexibook_jg7425,   lexibook_jg7425, lexibook_jg7425_state, empty_init, "Lexibook", "Disney Frozen TV Game Console (32-bit, Lexibook, JG7420FZ)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )

// slightly different, but same basic structure of the external ROM
COMP( 201?, zone3d,      0,         0,      lexibook_jg7425,  lexibook_jg7425, lexibook_jg7425_state, empty_init,"Zone", "Zone 3D", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )

// Unknown hardware, HDMI dongle with wireless pads.
// Uses standard chips, not globs, but surface details on CPU/SoC have been erased.
//
// It has the GPspispi header, so is definitely a GeneralPlus / SunPlus chip, and it has a mix of '32-bit' games
// as well as some 8-bit NES/FC games presumably running on an emulator like the above units, but the code in the
// SPI ROM does not seem to disassemble to anything meaningful, maybe compressed?
// Front and back have slightly different product names, unknown if NubSup is the manufacturer or part of the product name
COMP( 201?, ubox30,      0,         0,      lexibook_jg7425,  lexibook_jg7425, lexibook_jg7425_state, empty_init,"<unknown>", "NubSup TV Interactive Extreme u-box / Extreme u-box Game Station 32Bit Interactive System - New 30", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



spg2xx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood
/******************************************************************************

    Short Description:

        Systems which run on the SPG243 SoC

        die markings show
        "SunPlus QL8041" ( also known as Sunplus SPG240 & PAC300 )

            All GameKeyReady units
                Disney Princess (GKR)
                Wheel of Fortune (GKR)
                JAKKS WWE (GKR)
                Fantastic 4 (GKR)
                Justice League (GKR)
                Dora the Explorer Nursery Rhyme (GKR)
                Dora the Explorer Play Park (GKR)
                Spiderman 5-in-1 (GKR)
                etc.

            (other non GKR JAKKS games)
            X-Men (Wolverine pad)
            Avatar: The Last Airbender
            Superman in Super Villain Showdown

            (other games)
            Mattel Classic Sports

        "SunPlus QL8041C" ( known as Sunplus SPG2??, seems to be compatible with above, so probably just a chip revision )

            Clickstart ( see clickstart.cpp instead)
            Wheel of Fortune 2nd Edition
            Spider-man - Villain Roundup
            Dream Life Superstar
            Designer's World
            Star Wars TV Touch

        "SunPlus QU7074-P69A"

            The Batman
            Star Wars (non-gamekey, which model? falcon? - check)
            Dream Life

        "SunPlus QL8167b" (is the scrambling built into the CPU, or external?)

            Lexibook Zeus IG900 20-in-1

        "SunPlus QL8139C"

            Radica Cricket
            V Smile Baby (Sweden) - see vsmileb.cpp

        ---

        Very likely the same

        "Sunplus QL8167" (these might have ROM scrambling if that is a 8167 feature)

            Disney Princess Magical Adventure
            Go Diego Go
            Shrek - Over the Hedge (this unit shows a 'GameKey Unlock More Games' on startup, but has no port, not even on the internal PCB)
            Marvel Heroes (Spider-man)
            Spiderman 3 (Movie - black)


        ---

        It is unknown if the following are close to this architecture or not (no dumps yet)

        "SunPlus QU7073-P69A"

            Mortal Kombat

        "Sunplus PU7799-P680?" (difficult to read)

            Mission Paintball

        ---

        These are definitely different but still unSP based

        "SunPlus PA7801" ( known as Sunplus SPG110? )
        - see spg110.cpp instead

        "GCM394" (this is clearly newer, has extra opcodes, different internal map etc. also scaling and higher resolutions based on Spongebob)
        - see sunplus_unsp20soc.cpp instead

    Status:

        Mostly working

    To-Do:

        Proper driver_device inheritance to untangle the mess of members

    Detailed list of bugs:

        All systems:
            Various inaccuracies in samples/envelopes.

        jak_wall, jak_sdoo:
            Game seems unhappy with NVRAM, clears contents on each boot.
        jak_disf:
            Shows corrupt logo on first boot with no valid nvram (possibly hardware does too - verify if possible to invalidate EEPROM on device)
        lexizeus:
            Some corrupt sound effects and a few corrupt ground tiles a few minutes in. (checksum is good, and a video recorded
             from one of these doesn't exhibit these problems, so either emulation issue or alt revision?)
        pvmil:
            Question order depends on SoC RNG, only reads when it wants a new value, so unless RNG runs on a timer question order ends up the same

        vii:
            When loading a cart from file manager, sometimes MAME will crash.
            The "MOTOR" option in the diagnostic menu does nothing when selected.
            The "SPEECH IC" option in the diagnostic menu does nothing when selected.
            On 'vii_vc1' & 'vii_vc2' cart, the left-right keys are transposed with the up-down keys.
            - This is not a bug per se, as the games are played with the controller physically rotated 90 degrees.

    Note:
        Cricket, Skateboarder, Skannerz and Football 2 list a 32-bit checksum at the start of ROM.
        This is the byte sum of the file, excluding the first 16 byte (where the checksum is stored)

        Test Modes:
        Justice League : press UP, DOWN, LEFT, BT3 on the JAKKS logo in that order, quickly, to get test menu
        WWE : press UP, BT1, BT2 together during startup logos

        Disney Friends, MS Pacman, WallE, Batman (and some other HotGen GameKeys) for test mode, hold UP,
        press A, press DOWN during startup

        Capcom test (same access as other Hotgen games) mode looks like this (tested on PAL unit, same ROM as dumped one)

        RAM OK     2800
                111111
                5432109876543210
        IOA    ............111.          (values go from . to 1 when inputs are moved, never 0 as in MAME!, core bug?)
                        GAMEKEY E0
        IOB0
        IOC    XXX.........X...
        SPRITES

        Care Bears : Hold analog stck up, rotate stick 360 degress back to up, press 'A' while still holding up

    TODO:
        Work out how to access the hidden TEST menus for all games (most JAKKS games should have one at least)

*******************************************************************************/

#include "emu.h"
#include "spg2xx.h"


/*************************
*    Common Helper    *
*************************/

void spg2xx_game_state::decrypt_ac_ff(uint16_t* ROM, int size)
{
	for (int i = 0; i < size / 2; i++)
	{
		ROM[i] = bitswap<16>(ROM[i], 15, 13, 14, 12,
									 7,  6,  5,  4,
									 11, 10, 9,  8,
									 3,  1,  2,  0);

		ROM[i] = ROM[i] ^ 0xfafa;
	}
}


/*************************
*    Machine Hardware    *
*************************/

void spg2xx_game_state::switch_bank(uint32_t bank)
{
	if (m_bank)
	{
		if (bank != m_current_bank)
		{
			m_current_bank = bank;
			m_bank->set_entry(bank);
			m_maincpu->invalidate_cache();
		}
	}
}

void spg2xx_game_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: porta_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
		(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
		(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
		(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
		(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
		(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
		(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
		(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
		(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
		(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
		(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
		(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
		(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
		(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
		(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
		(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
		(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');
}

void spg2xx_game_state::portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: portb_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
		(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
		(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
		(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
		(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
		(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
		(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
		(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
		(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
		(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
		(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
		(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
		(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
		(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
		(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
		(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
		(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');
}

void spg2xx_game_state::portc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: portc_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
		(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
		(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
		(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
		(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
		(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
		(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
		(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
		(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
		(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
		(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
		(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
		(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
		(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
		(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
		(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
		(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');
}

void spg2xx_game_state::i2c_w(offs_t offset, uint8_t data)
{
	logerror("%s: i2c_w %05x %04x\n", machine().describe_context(), offset, data);
}

uint8_t spg2xx_game_state::i2c_r(offs_t offset)
{
	logerror("%s: i2c_r %04x\n", machine().describe_context(), offset);
	return 0x0000;
}

uint16_t spg2xx_game_state::base_porta_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t data = m_io_p1->read();
	logerror("%s: Port A Read: %04x (%04x)\n", machine().describe_context(), data, mem_mask);
	return data;
}

uint16_t spg2xx_game_state::base_portb_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t data = m_io_p2->read();
	logerror("%s: Port B Read: %04x (%04x)\n", machine().describe_context(), data, mem_mask);
	return data;
}

uint16_t spg2xx_game_state::base_portc_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t data = m_io_p3->read();
	logerror("%s: Port C Read: %04x (%04x)\n", machine().describe_context(), data, mem_mask);
	return data;
}

uint16_t spg2xx_game_state::base_guny_r()
{
	uint16_t data = m_io_guny->read();
	logerror("%s: Gun Y Read: %04x\n", machine().describe_context(), data);
	return data;
}

uint16_t spg2xx_game_state::base_gunx_r()
{
	uint16_t data = m_io_gunx->read();
	logerror("%s: Gun X Read: %04x\n", machine().describe_context(), data);
	return data;
}



void spg2xx_game_state::mem_map_4m(address_map &map)
{
	map(0x000000, 0x3fffff).bankr("cartbank");
}

void spg2xx_game_state::mem_map_2m(address_map &map)
{
	map(0x000000, 0x1fffff).mirror(0x200000).bankr("cartbank");
}

void spg2xx_game_state::mem_map_1m(address_map &map)
{
	map(0x000000, 0x0fffff).mirror(0x300000).bankr("cartbank");
}

void spg2xx_game_gssytts_state::mem_map_upperbank(address_map &map)
{
	map(0x000000, 0x1fffff).bankr("cartbank");
	map(0x200000, 0x3fffff).bankr("upperbank");
}


void spg2xx_game_wfcentro_state::mem_map_wfcentro(address_map &map)
{
	map(0x000000, 0x37ffff).bankr("cartbank");
	map(0x380000, 0x3fffff).ram();
}


void spg2xx_game_lexiart_state::mem_map_lexiart(address_map &map)
{
	map(0x000000, 0x3fffff).bankr("cartbank");
	map(0x3f0000, 0x3f7fff).ram(); // 2 * 32Kb RAMs on PCB
}

void spg2xx_game_pdcj_state::mem_map_upperbank(address_map &map)
{
	map(0x000000, 0x1fffff).bankr("cartbank");
	map(0x200000, 0x3fffff).bankr("upperbank");
}


static INPUT_PORTS_START( spg2xx ) // base structure for easy debugging / figuring out of inputs
	PORT_START("P1")
	PORT_DIPNAME( 0x0001, 0x0001, "P1:0001" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0001, "0001" )
	PORT_DIPNAME( 0x0002, 0x0002, "P1:0002" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0002, "0002" )
	PORT_DIPNAME( 0x0004, 0x0004, "P1:0004" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0004, "0004" )
	PORT_DIPNAME( 0x0008, 0x0008, "P1:0008" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0008, "0008" )
	PORT_DIPNAME( 0x0010, 0x0010, "P1:0010" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0010, "0010" )
	PORT_DIPNAME( 0x0020, 0x0020, "P1:0020" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0020, "0020" )
	PORT_DIPNAME( 0x0040, 0x0040, "P1:0040" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0040, "0040" )
	PORT_DIPNAME( 0x0080, 0x0080, "P1:0080" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0080, "0080" )
	PORT_DIPNAME( 0x0100, 0x0100, "P1:0100" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0100, "0100" )
	PORT_DIPNAME( 0x0200, 0x0200, "P1:0200" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0200, "0200" )
	PORT_DIPNAME( 0x0400, 0x0400, "P1:0400" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0400, "0400" )
	PORT_DIPNAME( 0x0800, 0x0800, "P1:0800" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0800, "0800" )
	PORT_DIPNAME( 0x1000, 0x1000, "P1:1000" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x1000, "1000" )
	PORT_DIPNAME( 0x2000, 0x2000, "P1:2000" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x2000, "2000" )
	PORT_DIPNAME( 0x4000, 0x4000, "P1:4000" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x4000, "4000" )
	PORT_DIPNAME( 0x8000, 0x8000, "P1:8000" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x8000, "8000" )

	PORT_START("P2")
	PORT_DIPNAME( 0x0001, 0x0001, "P2:0001" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0001, "0001" )
	PORT_DIPNAME( 0x0002, 0x0002, "P2:0002" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0002, "0002" )
	PORT_DIPNAME( 0x0004, 0x0004, "P2:0004" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0004, "0004" )
	PORT_DIPNAME( 0x0008, 0x0008, "P2:0008" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0008, "0008" )
	PORT_DIPNAME( 0x0010, 0x0010, "P2:0010" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0010, "0010" )
	PORT_DIPNAME( 0x0020, 0x0020, "P2:0020" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0020, "0020" )
	PORT_DIPNAME( 0x0040, 0x0040, "P2:0040" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0040, "0040" )
	PORT_DIPNAME( 0x0080, 0x0080, "P2:0080" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0080, "0080" )
	PORT_DIPNAME( 0x0100, 0x0100, "P2:0100" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0100, "0100" )
	PORT_DIPNAME( 0x0200, 0x0200, "P2:0200" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0200, "0200" )
	PORT_DIPNAME( 0x0400, 0x0400, "P2:0400" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0400, "0400" )
	PORT_DIPNAME( 0x0800, 0x0800, "P2:0800" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0800, "0800" )
	PORT_DIPNAME( 0x1000, 0x1000, "P2:1000" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x1000, "1000" )
	PORT_DIPNAME( 0x2000, 0x2000, "P2:2000" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x2000, "2000" )
	PORT_DIPNAME( 0x4000, 0x4000, "P2:4000" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x4000, "4000" )
	PORT_DIPNAME( 0x8000, 0x8000, "P2:8000" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x8000, "8000" )

	PORT_START("P3")
	PORT_DIPNAME( 0x0001, 0x0001, "P3:0001" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0001, "0001" )
	PORT_DIPNAME( 0x0002, 0x0002, "P3:0002" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0002, "0002" )
	PORT_DIPNAME( 0x0004, 0x0004, "P3:0004" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0004, "0004" )
	PORT_DIPNAME( 0x0008, 0x0008, "P3:0008" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0008, "0008" )
	PORT_DIPNAME( 0x0010, 0x0010, "P3:0010" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0010, "0010" )
	PORT_DIPNAME( 0x0020, 0x0020, "P3:0020" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0020, "0020" )
	PORT_DIPNAME( 0x0040, 0x0040, "P3:0040" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0040, "0040" )
	PORT_DIPNAME( 0x0080, 0x0080, "P3:0080" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0080, "0080" )
	PORT_DIPNAME( 0x0100, 0x0100, "P3:0100" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0100, "0100" )
	PORT_DIPNAME( 0x0200, 0x0200, "P3:0200" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0200, "0200" )
	PORT_DIPNAME( 0x0400, 0x0400, "P3:0400" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0400, "0400" )
	PORT_DIPNAME( 0x0800, 0x0800, "P3:0800" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x0800, "0800" )
	PORT_DIPNAME( 0x1000, 0x1000, "P3:1000" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x1000, "1000" )
	PORT_DIPNAME( 0x2000, 0x2000, "P3:2000" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x2000, "2000" )
	PORT_DIPNAME( 0x4000, 0x4000, "P3:4000" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x4000, "4000" )
	PORT_DIPNAME( 0x8000, 0x8000, "P3:8000" )
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x8000, "8000" )
INPUT_PORTS_END

static INPUT_PORTS_START( smartcyc )
	PORT_START("P1")
	// lower bits are related to steering position?
	PORT_BIT( 0x007f, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(spg2xx_game_smartcycle_state::unknown_random_r))
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) // needs to be held for test mode
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("Top Left")
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Top Right")
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Bottom Left") // needs to be held for test mode
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Bottom Middle")
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Bottom Right") // needs to be held for test mode

	PORT_START("P2")
	// maybe unused
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(spg2xx_game_smartcycle_state::unknown_random_r))

	PORT_START("P3")
	// maybe unused
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(spg2xx_game_smartcycle_state::unknown_random_r))

	// A / B in test mode coming from elsewhere?
INPUT_PORTS_END

static INPUT_PORTS_START( lpetshop )
	PORT_START("P1")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Pause / Menu")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("C")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("B")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("A")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0xff00, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( mylpony )
	PORT_START("P1")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Pause / Menu")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Yellow")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Red / Select")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Purple")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0xff00, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


static INPUT_PORTS_START( carled99 )
	PORT_INCLUDE( spg2xx )

	// controls seem to be 100% digital despite it being a wheel + pedals controller similar in appearance to ford racing
	PORT_MODIFY("P1")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_NAME("Steer Left")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_NAME("Steer Right")

	PORT_MODIFY("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Menu Select")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Menu Back")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("Accelerate")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("Brake")
INPUT_PORTS_END

static INPUT_PORTS_START( wordlnch )
	PORT_INCLUDE( spg2xx )

	PORT_MODIFY("P1")
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_BUTTON2 ) // might be multiple inputs here
	PORT_DIPNAME( 0x8000, 0x8000, "PAL/NTSC" ) // shows in test mode, changes Z from 'Zed' to 'Zee'
	PORT_DIPSETTING(      0x0000, "NTSC" )
	PORT_DIPSETTING(      0x8000, "PAL" )

	PORT_MODIFY("P2")
	PORT_DIPNAME( 0x0040, 0x0000, "Test Pin?" )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( On ) )
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON3 )

	PORT_MODIFY("P3")
	PORT_DIPNAME( 0x4000, 0x0000, "P3:4000 (Battery?)" ) // state of this can cause shutdowns too?
	PORT_DIPSETTING(      0x0000, "0000" )
	PORT_DIPSETTING(      0x4000, "4000" )
INPUT_PORTS_END

static INPUT_PORTS_START( epo_tetr ) // all inputs verified against hidden test mode
	PORT_INCLUDE( spg2xx )

	PORT_MODIFY("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("P1 A")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("P1 B")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("P1 L")
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("P1 R")
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_START2 )
	PORT_BIT( 0xfc00, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_MODIFY("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_MODIFY("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("EXTRA")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("P2 L")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("P2 R")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 B")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 A")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)

INPUT_PORTS_END

static INPUT_PORTS_START( dmbtjunc )
	PORT_INCLUDE( spg2xx )

	PORT_MODIFY("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("P1 Red")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("P1 Green")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("P1 Blue")
	// there's a 2nd set of buttons for P2, where do they map?
	// battery state is likely in here too
INPUT_PORTS_END

static INPUT_PORTS_START( ban_krkk )
	// inputs shown in hidden text mode, although it refers to the physical placement of the each button on the mat rather than the colours / symbols
	PORT_INCLUDE( spg2xx )

	PORT_MODIFY("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("P1 Blue/Top-Left")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("P1 Yellow/Top-Right")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("P1 Red/Bottom-Left")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("P1 Green/Bottom-Right")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(1) PORT_NAME("P1 Select")

	PORT_MODIFY("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 Blue/Top-Left")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 Yellow/Top-Right")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("P2 Red/Bottom-Left")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("P2 Green/Bottom-Right")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(2) PORT_NAME("P2 Select")

	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_CUSTOM ) // Battery State
INPUT_PORTS_END


static INPUT_PORTS_START( drumsups )
	PORT_INCLUDE( spg2xx )

	PORT_MODIFY("P1")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Start / Enter")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Drum pad 1: Blue")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Drum pad 2: Yellow")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Drum pad 3: Purple")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Drum pad 4: Red")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Drum pad 5: Green")
INPUT_PORTS_END

static INPUT_PORTS_START( lexiart )
	PORT_INCLUDE( spg2xx )

	PORT_MODIFY("P1")
	PORT_DIPNAME( 0x0100, 0x0000, "Battery State" )
	PORT_DIPSETTING(      0x0000, "Ok" )
	PORT_DIPSETTING(      0x0100, "Low" )
INPUT_PORTS_END

static INPUT_PORTS_START( itvphone ) // hold 8 and ENTER for Diagnostics mode
	PORT_START("P1") // note, the physical inputs are in 'phone' order, so 1 is top left, not bottom left like a PC Keypad
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 8") PORT_CODE(KEYCODE_8_PAD) // needed for DIAGNOSTICS mode
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad +") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad *") PORT_CODE(KEYCODE_ASTERISK)
	PORT_CONFNAME( 0x7000, 0x0000, "Non-TV Mode Game" )
	PORT_CONFSETTING(      0x1000, "Learning Game" )
	PORT_CONFSETTING(      0x2000, "Finding Game" )
	PORT_CONFSETTING(      0x4000, "Memory Game" )
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Enter") // needed for DIAGNOSTICS mode
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Bell")
	PORT_CONFNAME( 0x0040, 0x0000, "TV / Non-TV mode" ) // this is shown as a button in DIAGNOSTICS mode
	PORT_CONFSETTING(      0x0000, "TV" )
	PORT_CONFSETTING(      0x0040, "Non-TV" )
	PORT_BIT( 0xff80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x0fff, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_NAME("Reset") // reset? back?
	PORT_BIT( 0xe000, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END


static INPUT_PORTS_START( rad_skat )
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Full Left")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("Full Right")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_NAME("Slight Left") // you have to use this for the menus (eg trick lists)
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_NAME("Slight Right")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_NAME("Front")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_NAME("Back")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_UNUSED )
	// there only seem to be 3 buttons on the pad part, so presumably all the above are the skateboard, and below are the pad?
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("M Button")
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("X Button")
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("O Button")
	PORT_BIT( 0xf800, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED ) // read but unused?

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_CUSTOM ) // NTSC (1) / PAL (0) flag
INPUT_PORTS_END

static INPUT_PORTS_START( rad_skatp )
	PORT_INCLUDE(rad_skat)

	PORT_MODIFY("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_CUSTOM ) // NTSC (1) / PAL (0) flag
INPUT_PORTS_END




static INPUT_PORTS_START( tvsprt10 )
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x0180, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON1 ) // Start
	PORT_BIT( 0xfc00, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN )
INPUT_PORTS_END

static INPUT_PORTS_START( decathln )
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x0180, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON1 ) // Start
	PORT_BIT( 0xfc00, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN )
INPUT_PORTS_END

static INPUT_PORTS_START( mattelcs ) // there is a 'secret test mode' that previously got activated before inputs were mapped, might need unused inputs to active?
	PORT_START("P1")
	PORT_BIT( 0x0007, IP_ACTIVE_LOW, IPT_UNUSED ) // must be IP_ACTIVE_LOW or you can't switch to Football properly?
	PORT_DIPNAME( 0x0018, 0x0000, "Game Select Slider" ) // technically not a dipswitch, a 3 position slider, but how best map it?
	PORT_DIPSETTING(      0x0008, "Baseball (Left)" )
	PORT_DIPSETTING(      0x0010, "Basketball (Middle)" )
	PORT_DIPSETTING(      0x0000, "Football (Right)" )
	// no 4th position possible
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Difficulty ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Hard ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( Normal ) )
	PORT_BIT( 0xffa0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_NAME("Joypad Up")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_NAME("Joypad Down")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_NAME("Joypad Left")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_NAME("Joypad Right")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_NAME("Sound") // toggles between sound+music, sound only, and no sound
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 )        PORT_NAME("Hike / Pitch")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON3 )        PORT_NAME("Shoot / Run")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON4 )        PORT_NAME("Kick / Hit")
	PORT_BIT( 0xff00, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

/* hold 'Console Down' while powering up to get the test menu, including input tests
   the ball (Wired) and bat (IR) are read some other way as they don't seem to appear in the ports. */
static INPUT_PORTS_START( rad_crik )
	PORT_START("P1")
	PORT_BIT( 0x003f, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Console Enter") // these are the controls on the base unit
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_NAME("Console Down")
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_NAME("Console Left")
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_NAME("Console Right")
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_NAME("Console Up")
	PORT_BIT( 0xf800, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

static INPUT_PORTS_START( rad_fb2 ) // controls must be multiplexed somehow, as there's no room for P2 controls otherwise (unless P2 controls were never finished and it was only sold in a single mat version, Radica left useless P2 menu options in the mini Genesis consoles)
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) // 'left'
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) // 'up'
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) // 'right'
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1) // acts a 'motion ball' in menu (this is an analog input from the ball tho? at least in rad_fb in xavix.cpp so this might just be a debug input here)
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) // 'p2 right'
	// none of the remaining inputs seem to do anything
	PORT_BIT( 0xffe0, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_CUSTOM ) // NTSC (1) / PAL (0) flag
INPUT_PORTS_END

static INPUT_PORTS_START( abltenni )
	PORT_INCLUDE( spg2xx )

	PORT_MODIFY("P1") // Down and both buttons on startup for Diagnostics Menu
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	// all remaining bits in this port will stop the demo mode, also having them high/low determines if you get 2P demos or Vs. CPU demos, not sure what the real state would be
	PORT_BIT( 0xffc0, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_MODIFY("P2")
	PORT_DIPNAME( 0x0020, 0x0000, "Used" ) // doesn't boot otherwise
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_MODIFY("P3")
INPUT_PORTS_END


static INPUT_PORTS_START( ordentv )
	PORT_INCLUDE( spg2xx )

	PORT_MODIFY("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 )
INPUT_PORTS_END

static INPUT_PORTS_START( fordrace )
	PORT_INCLUDE( spg2xx )

	PORT_MODIFY("P1") // hold button 1 on powerup for test
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Select / Start")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Gear Up")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Gear Down")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Pause")

	PORT_MODIFY("P2")

	PORT_MODIFY("P3")

	PORT_START("AD0") // 12-bit port, Accelerator
	PORT_BIT(0x0fff, 0x0000, IPT_PEDAL ) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_MINMAX(0x00,0x0fff)

	PORT_START("AD1") // 12-bit port, Brake
	PORT_BIT(0x0fff, 0x0000, IPT_PEDAL2 ) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_MINMAX(0x00,0x0fff)

	PORT_START("AD2") // 12-bit port, Wheel is split across 2 ports, value added together?
	PORT_BIT( 0x0fff, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(spg2xx_game_fordrace_state::wheel2_r))

	PORT_START("AD3") // 12-bit port, Wheel (see above)
	PORT_BIT( 0x0fff, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(spg2xx_game_fordrace_state::wheel_r))

	PORT_START("WHEEL_REAL")
	PORT_BIT(0x1fff, 0x0000, IPT_AD_STICK_X ) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_MINMAX(0x00,0x1fff) PORT_NAME("Wheel")
INPUT_PORTS_END

static INPUT_PORTS_START( totspies )
	PORT_INCLUDE( spg2xx )

	PORT_MODIFY("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("B")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("A")
	PORT_BIT( 0xff80, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_MODIFY("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	// unit also has a 'select' button next to 'OK' and while test mode shows it onscreen too, it doesn't get tested, so probably isn't connected to anything?
	PORT_MODIFY("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("OK")
	PORT_BIT( 0xfffe, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

static INPUT_PORTS_START( doyousud )
	PORT_INCLUDE( spg2xx )

	PORT_MODIFY("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("Menu")
	// is the on/off button visible at 0020?
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Higher / Up")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Pencil")
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Lower / Down")
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Ok")

INPUT_PORTS_END


ioport_value spg2xx_game_fordrace_state::wheel_r()
{
	return ioport("WHEEL_REAL")->read() >> 1;
}

ioport_value spg2xx_game_fordrace_state::wheel2_r()
{
//  return 0x0800;
	uint16_t dat = ioport("WHEEL_REAL")->read();

	return ((dat >> 1) ^ 0xfff) + (dat & 1);
}

static INPUT_PORTS_START( senspeed )
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_NAME("Accelerate / Select")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_NAME("Reverse / Confirm")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("A (Autojacks)")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("B (Belt Tires)")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("C (Cutting Saw)")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("D (Deflector)")
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("E (Evening Eyes)")
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("F (Frogman Mode)")
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON7 ) PORT_NAME("G (Go Robot)")
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_START1 ) PORT_NAME("Start / Pause")
	PORT_BIT( 0xf000, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_UNUSED ) // eeprom bit, handled in read function
	PORT_BIT( 0xfffe, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( ablkickb )
	PORT_INCLUDE( spg2xx )

	PORT_MODIFY("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_DIPNAME( 0x0040, 0x0040, "Show Coleco Logo" ) // must be wired inside unit for Coleco distributed ones (US?)
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_MODIFY("P2")

	PORT_MODIFY("P3")
INPUT_PORTS_END

static INPUT_PORTS_START( lxspidaj )
	PORT_START("P1") // base controller has dpad, 2 regular buttons, 2 turbo buttons, start button, reset button.  IR connected JetSki pad has the same inputs but in handlebar form.
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0xff80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON3 ) // is this a button or comms with the controller on the Jetski, check code (possibly just 'start' tho)
	PORT_BIT( 0xfffe, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( comil )
	PORT_START("EXTRA0")
	PORT_BIT( 0x1, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("50:50")
	PORT_BIT( 0x2, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("Phone A Friend")
	PORT_BIT( 0x4, IP_ACTIVE_LOW, IPT_BUTTON7 ) PORT_NAME("Ask The Audience")
	PORT_BIT( 0x8, IP_ACTIVE_LOW, IPT_BUTTON8 ) PORT_NAME("Walk Away")

	PORT_START("EXTRA1")
	PORT_BIT( 0x1, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("A")
	PORT_BIT( 0x2, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("B")
	PORT_BIT( 0x4, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("C")
	PORT_BIT( 0x8, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("D")

	PORT_START("EXTRA2")
	PORT_BIT( 0xf, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("EXTRA3")
	PORT_BIT( 0xf, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("EXTRA4")
	PORT_BIT( 0xf, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("EXTRA5")
	PORT_BIT( 0xf, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("EXTRA6")
	PORT_BIT( 0xf, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("EXTRA7")
	PORT_BIT( 0xf, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P1")
	PORT_BIT( 0x00ff, IP_ACTIVE_LOW, IPT_UNUSED ) // multiplex select for Port B
	PORT_BIT( 0xff00, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0x000f, IP_ACTIVE_LOW, IPT_UNUSED ) // multiplexed inputs
	PORT_BIT( 0xfff0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( guitarfv )
	PORT_START("P1")  // Button 1 + 2 and start for service mode
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON3 )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON4 )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON5 )
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("Wheel / Whammy") // 'Wheel' in test mode
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0xfe00, IP_ACTIVE_LOW, IPT_UNKNOWN ) // unused?

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN ) // unused?

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN ) // unused?
INPUT_PORTS_END

static INPUT_PORTS_START( guitarss )
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("B1: Blue / Up")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("B2: Yellow / Down")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("B3: Purple")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("B4: Red")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("B5: Green")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_NAME("Strum / Select")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED ) // unused, strum is single direction here
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Whammy")
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_NAME("Start / Select") // pause
	PORT_BIT( 0xfe00, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // unused?

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // unused?

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // unused?

INPUT_PORTS_END


static INPUT_PORTS_START( senwfit )
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )  PORT_16WAY
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_16WAY
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )  PORT_16WAY
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_16WAY
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Select")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Start")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Weight Left")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Weight Right")
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Up-Left")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Down-Left")
	PORT_BIT( 0x1c00, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_NAME("Up-Right")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_BUTTON8 ) PORT_NAME("Down-Right")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // unused?

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // unused?
INPUT_PORTS_END

static INPUT_PORTS_START( jjstrip )
	PORT_START("P1") // active LOW or HIGH?
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 ) // on pad but not used?
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_SELECT )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_START )
	PORT_BIT( 0xff00, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( pballpup )
	PORT_START("GUNY")
	PORT_BIT(0x0ff, 0x80, IPT_LIGHTGUN_Y) PORT_CROSSHAIR(Y, 256.0f / 240.0f, 0.0, 0) PORT_MINMAX(0x000, 0x0ff) PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_PLAYER(1)

	PORT_START("GUNX")
	PORT_BIT(0x1ff, 0x100, IPT_LIGHTGUN_X) PORT_CROSSHAIR(X, 512.0f / 320.0f, -0.03f, 0) PORT_MINMAX(0x000, 0x1ff) PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_PLAYER(1)

	PORT_START("P1")
	PORT_BIT( 0x003f, IP_ACTIVE_HIGH, IPT_UNUSED ) // lower bits are seeprom
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0xfc00, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x000f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON4 ) // pause
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON1 ) // trigger
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON2 ) // hide
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON3 ) // reload
	PORT_BIT( 0xff00, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( mpntball )
	PORT_START("GUNY") // not verified
	PORT_BIT(0x0ff, 0x80, IPT_LIGHTGUN_Y) PORT_CROSSHAIR(Y, 256.0f / 240.0f, 0.0, 0) PORT_MINMAX(0x000, 0x0ff) PORT_SENSITIVITY(40) PORT_KEYDELTA(10) PORT_PLAYER(1)

	PORT_START("GUNX") // not verified
	PORT_BIT(0x1ff, 0x100, IPT_LIGHTGUN_X) PORT_CROSSHAIR(X, 512.0f / 320.0f, -0.06f, 0) PORT_MINMAX(0x000, 0x1ff) PORT_SENSITIVITY(40) PORT_KEYDELTA(10) PORT_PLAYER(1)

	PORT_START("P1")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0x000f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Reload") // beeps if you hold on startup
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Pause")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Trigger")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Hide")
	PORT_BIT( 0xff00, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( mpntbalt )
	PORT_START("GUNY") // not verified
	PORT_BIT(0x0ff, 0x80, IPT_LIGHTGUN_Y) PORT_CROSSHAIR(Y, 256.0f / 240.0f, 0.0, 0) PORT_MINMAX(0x000, 0x0ff) PORT_SENSITIVITY(40) PORT_KEYDELTA(10) PORT_PLAYER(1)

	PORT_START("GUNX") // not verified
	PORT_BIT(0x1ff, 0x100, IPT_LIGHTGUN_X) PORT_CROSSHAIR(X, 512.0f / 320.0f, -0.06f, 0) PORT_MINMAX(0x000, 0x1ff) PORT_SENSITIVITY(40) PORT_KEYDELTA(10) PORT_PLAYER(1)

	PORT_START("P1")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0x000f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Menu")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Trigger")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Hide")
	PORT_BIT( 0xff80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


static INPUT_PORTS_START( swclone )
	PORT_START("GUNY")
	PORT_BIT(0x0ff, 0x80, IPT_LIGHTGUN_Y) PORT_CROSSHAIR(Y, 1.0f, 0.0, 0) PORT_MINMAX(0x000, 0x0ff) PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_PLAYER(1)

	PORT_START("GUNX")
	PORT_BIT(0x1ff, 0x100, IPT_LIGHTGUN_X) PORT_CROSSHAIR(X, 512.0f / 320.0f, -0.105f, 0) PORT_MINMAX(0x000, 0x1ff) PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_PLAYER(1)

	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_UNUSED ) // i2cmem here
	PORT_BIT( 0x003e, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0xfc00, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x000f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON4 ) // pause
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON1 ) // trigger
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON2 ) // grenade
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON3 ) // reload (doesn't exist here?)
	PORT_BIT( 0xff00, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( tmntmutm )
	PORT_START("GUNY")
	PORT_BIT(0x0ff, 0x80, IPT_LIGHTGUN_Y) PORT_CROSSHAIR(Y, 256.0f / 240.0f, -0.032f, 0) PORT_MINMAX(0x000, 0x0ff) PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_PLAYER(1)

	PORT_START("GUNX")
	PORT_BIT(0x1ff, 0x100, IPT_LIGHTGUN_X) PORT_CROSSHAIR(X, 512.0f / 320.0f, -0.127f, 0) PORT_MINMAX(0x000, 0x1ff) PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_PLAYER(1)

	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_BUTTON1 ) // trigger
	PORT_BIT( 0xfffe, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( dreamlss )
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("A")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("B")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("C")
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_SERVICE ) PORT_NAME("Test (Debug)") // not externally connected on unit
	PORT_BIT( 0xfe00, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


static INPUT_PORTS_START( knd )
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_START1 )
	PORT_BIT( 0xfc00, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


static INPUT_PORTS_START( tmntbftc )
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0180, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_START1 )
	PORT_BIT( 0xfc00, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( spidm2 )
	PORT_START("P1") // the motion controls seem to map to simple button presses
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Left Hand Punch")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Web")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Right Hand Punch")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Swing")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("Jump")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("Kick")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_UNKNOWN ) // unused?
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON7 ) PORT_NAME("Pause")
	PORT_BIT( 0xff00, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END


static INPUT_PORTS_START( rocksock )
	PORT_START("P1")
	PORT_BIT( 0x00ff, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0xc000, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0xffc0, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

static INPUT_PORTS_START( barbpet )
	PORT_START("P1")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON3 )
	PORT_BIT( 0xff80, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN )
INPUT_PORTS_END

static INPUT_PORTS_START( hotwhls )
	PORT_START("P1")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("B")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("A")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("J")
	PORT_BIT( 0xff80, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN )
INPUT_PORTS_END


static INPUT_PORTS_START( whacmole )
	PORT_START("P1")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Blue / Top Left")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Yellow / Bottom Left")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Red / Bottom Right")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Green / Top Right")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Pause / Menu")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Special")
	PORT_BIT( 0xffc0, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END


static INPUT_PORTS_START( backybbs )
	PORT_START("P1")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P2") // inputs MUST be ACTIVE_LOW to function properly
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("A")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("X") // used to select in menus
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Y")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("B") // used to go back in menus
	PORT_BIT( 0xff00, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P3")
	PORT_BIT( 0x0003, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("Pause / Menu")
	PORT_BIT( 0xfff8, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

static INPUT_PORTS_START( hotwheels )
	// 2 pads, each pad has 4 directions and 1 button, and an internal solder pad to select type, but input reading code seems a bit more complex
	// the unit this was dumped from was a PAL, with P1 as 'Bling' and P2 as 'Tuner' so those are the defaults used
	PORT_START("P1")
	PORT_BIT( 0x00ff, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_CONFNAME( 0x0500, 0x0000, "Player 2 Controller Type" )
	PORT_CONFSETTING(      0x0000, "Tuner" )
	PORT_CONFSETTING(      0x0100, "Off-Road" )
	//PORT_CONFSETTING(      0x0400, "Tuner" )
	PORT_CONFSETTING(      0x0500, "Nothing" )
	PORT_BIT( 0x0a00, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0xf000, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_CONFNAME( 0x0080, 0x0000, "Player 1 Controller Type" )
	PORT_CONFSETTING(      0x0000, "Bling" )
	PORT_CONFSETTING(      0x0080, "Rally" )
	PORT_BIT( 0xff00, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P1EXTRA")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PAL/NTSC flag  (ACTIVE_HIGH = NTSC, ACTIVE_LOW = PAL)
	PORT_BIT( 0x00fc, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )   PORT_PLAYER(2)
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )   PORT_PLAYER(2)
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )   PORT_PLAYER(2)
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON1 )    PORT_PLAYER(2)
	PORT_BIT( 0xe000, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P3") // never read?
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


// TODO: work out how to access hidden test mode again
static INPUT_PORTS_START( doraphone )
	PORT_START("P1")
	PORT_CONFNAME( 0x0070, 0x0060, "On/Off Mode Slider" )
	PORT_CONFSETTING(      0x0030, "Play Alone (no video)" )
	PORT_CONFSETTING(      0x0060, "Play on TV" )

	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("Lift Handset") // this could be done as a toggle, although note, handset being down is treated like a button being held
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_CUSTOM ) // this setting is US NTSC, ACTIVE_LOW gives US PAL (invalid?) no way to switch to non-US?
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_CUSTOM ) // must be 0x0200 or resets over and over
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)

	PORT_START("P1_ROW1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 'Repeat'") PORT_CODE(KEYCODE_PLUS_PAD)

	PORT_START("P1_ROW2")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 0") PORT_CODE(KEYCODE_0_PAD)

	PORT_START("P1_ROW3")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Phone Pad 'Help'") PORT_CODE(KEYCODE_MINUS_PAD)

	PORT_START("P1_ROW4")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("Show Answer") // Not 'answer phone'
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("Enter")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_PLAYER(1) PORT_NAME("Reset")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON10 ) PORT_PLAYER(1) PORT_NAME("Hear Dora (non-TV mode only)")

	PORT_START("P1_ROW5")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON12 ) PORT_PLAYER(1) PORT_NAME("Dora The Explorer Logo Button (non-TV mode only)")  // manual doesn't list this? speech says 'Dora the Explorer' in alone mode, presumably when you press the logo
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("Exit")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON9 ) PORT_PLAYER(1) PORT_NAME("Adventure Mode")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON11 ) PORT_PLAYER(1) PORT_NAME("Hear Boots (non-TV mode only)")

	PORT_START("P1_ROW6")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(1) PORT_NAME("Amusement Park")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON8 ) PORT_PLAYER(1) PORT_NAME("Quick Play")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(1) PORT_NAME("Big Drum Parade")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON7 ) PORT_PLAYER(1) PORT_NAME("Banana Grove")

	PORT_START("P2")
	PORT_BIT( 0xff7f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_CUSTOM ) // battery state

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( doraphonep )
	PORT_INCLUDE( doraphone )

	PORT_MODIFY("P1")
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_CUSTOM ) // PAL mode
INPUT_PORTS_END


static INPUT_PORTS_START( doraglobe )
	PORT_START("P1")
	PORT_CONFNAME( 0x0070, 0x0060, "On/Off Mode Slider" )
	PORT_CONFSETTING(      0x0030, "Play Alone (no video)" )
	PORT_CONFSETTING(      0x0060, "Play on TV" )

	// TODO: check if these region bits are the same
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_CUSTOM ) // this setting is US NTSC, ACTIVE_LOW gives US PAL (invalid?) no way to switch to non-US?
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_CUSTOM ) // must be 0x0200 or resets over and over
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P1_ROW1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("Continent Button: Asia")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("Continent Button: North America")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON10 ) PORT_PLAYER(1) PORT_NAME("Mode Button: Learn and Explore")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON14 ) PORT_PLAYER(1) PORT_NAME("Repeat")

	PORT_START("P1_ROW2")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(1) PORT_NAME("Continent Button: Europe")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(1) PORT_NAME("Continent Button: South America")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("Enter")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("Back")

	PORT_START("P1_ROW3")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON7 ) PORT_PLAYER(1) PORT_NAME("Continent Button: Africa")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON8 ) PORT_PLAYER(1) PORT_NAME("Continent Button: Antarctica")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON11 ) PORT_PLAYER(1) PORT_NAME("Mode Button: Adventure Play")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON13 ) PORT_PLAYER(1) PORT_NAME("Show Answer")

	PORT_START("P1_ROW4")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON9 ) PORT_PLAYER(1) PORT_NAME("Continent Button: Australia")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_UNUSED ) // skips over some cutscenes and makes a 'button press' sound, but doesn't seem to be a real input on the device
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_PLAYER(1) PORT_NAME("Reset")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON12 ) PORT_PLAYER(1) PORT_NAME("Mode Button: Explore and Find")

	PORT_START("P1_ROW5")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)

	PORT_START("P1_ROW6")
	PORT_BIT( 0x000f, IP_ACTIVE_LOW, IPT_UNUSED ) // no response to these

	PORT_START("P2")
	PORT_BIT( 0xff7f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_CUSTOM ) // battery state

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( virtbb )
	PORT_INCLUDE( spg2xx )

	PORT_MODIFY("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 ) // why does this also act as 'hit'? doesn't seem likely the motion control sends this?
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON2 )

	PORT_MODIFY("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_BUTTON3 ) // footmat
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON4 )

	PORT_MODIFY("P3")
INPUT_PORTS_END

static INPUT_PORTS_START( virtten )
	PORT_INCLUDE( spg2xx )

	PORT_MODIFY("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON2 )

	PORT_MODIFY("P2")

	PORT_MODIFY("P3")
INPUT_PORTS_END


static INPUT_PORTS_START( ddr33v )
	PORT_INCLUDE( spg2xx )

	PORT_MODIFY("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 ) // quits out of songs
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_START1 )
	PORT_BIT( 0xffc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_MODIFY("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_MODIFY("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

INPUT_PORTS_END


static INPUT_PORTS_START( pdcj )
	PORT_INCLUDE( spg2xx )

	PORT_MODIFY("P1")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_MODIFY("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Pause")
	PORT_BIT( 0xff80, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_MODIFY("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN )
INPUT_PORTS_END


static const ioport_value handle_table[3] =
{
	0x00, 0x01, 0x03,
};

// the on/off slider has 3 positions; off, on (low sound), on (high sound) but this seems to be a hardware feature, not read by the code
static INPUT_PORTS_START( prail )
	PORT_INCLUDE( spg2xx )

	PORT_MODIFY("P1")
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Doors")
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Horn")
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Lights")
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Conductor")
	PORT_BIT( 0xc000, 0x0000, IPT_POSITIONAL_V ) PORT_POSITIONS(3) PORT_REMAP_TABLE(handle_table) PORT_SENSITIVITY(15) PORT_KEYDELTA(1) PORT_CENTERDELTA(0) PORT_PLAYER(1)

	PORT_MODIFY("P2")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_CUSTOM ) // battery state

	PORT_MODIFY("P3")
	PORT_BIT( 0x0003, 0x0000, IPT_POSITIONAL_V ) PORT_POSITIONS(3) PORT_REMAP_TABLE(handle_table) PORT_SENSITIVITY(15) PORT_KEYDELTA(1) PORT_CENTERDELTA(0) PORT_PLAYER(2)
INPUT_PORTS_END

void spg2xx_game_state::machine_start()
{
	if (m_bank)
	{
		m_bank->configure_entries(0, (memregion("maincpu")->bytes() + 0x7fffff) / 0x800000, memregion("maincpu")->base(), 0x800000);
		m_bank->set_entry(0);
	}

	save_item(NAME(m_current_bank));
}

void spg2xx_game_state::machine_reset()
{
	m_current_bank = -1;
	switch_bank(0);
	m_maincpu->reset();
}

void spg2xx_game_state::spg2xx_base(machine_config &config)
{
	m_maincpu->porta_out().set(FUNC(spg2xx_game_state::porta_w));
	m_maincpu->portb_out().set(FUNC(spg2xx_game_state::portb_w));
	m_maincpu->portc_out().set(FUNC(spg2xx_game_state::portc_w));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320, 262);
	m_screen->set_visarea(0, 320-1, 0, 240-1);
	m_screen->set_screen_update("maincpu", FUNC(spg2xx_device::screen_update));
	m_screen->screen_vblank().set(m_maincpu, FUNC(spg2xx_device::vblank));

	SPEAKER(config, "speaker", 2).front();
	m_maincpu->add_route(ALL_OUTPUTS, "speaker", 0.5, 0);
	m_maincpu->add_route(ALL_OUTPUTS, "speaker", 0.5, 1);
}

void spg2xx_game_state::non_spg_base(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_state::mem_map_4m);

	spg2xx_base(config);
}

void spg2xx_game_state::rad_skat(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set_ioport("P1");
	m_maincpu->portb_in().set_ioport("P2");
	m_maincpu->portc_in().set_ioport("P3");
	m_maincpu->i2c_w().set(FUNC(spg2xx_game_state::i2c_w));
	m_maincpu->i2c_r().set(FUNC(spg2xx_game_state::i2c_r));
}

void spg2xx_game_state::spg2xx(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(spg2xx_game_state::base_porta_r));
	m_maincpu->portb_in().set(FUNC(spg2xx_game_state::base_portb_r));
	m_maincpu->portc_in().set(FUNC(spg2xx_game_state::base_portc_r));
}

void spg2xx_game_state::spg2xx_pal(machine_config& config)
{
	spg2xx(config);

	m_maincpu->set_pal(true);
	m_screen->set_refresh_hz(50);
}

void spg2xx_game_fordrace_state::fordrace(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_fordrace_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(spg2xx_game_fordrace_state::base_porta_r));
	m_maincpu->portb_in().set(FUNC(spg2xx_game_fordrace_state::base_portb_r));
	m_maincpu->portc_in().set(FUNC(spg2xx_game_fordrace_state::base_portc_r));

	// these do something in test mode, but in game the ADC interrupt is never generated?
	m_maincpu->adc_in<0>().set_ioport("AD0"); // pedals1
	m_maincpu->adc_in<1>().set_ioport("AD1"); // pedal2
	m_maincpu->adc_in<2>().set_ioport("AD2"); // steering
	m_maincpu->adc_in<3>().set_ioport("AD3"); // steering

}

uint16_t spg2xx_game_senspeed_state::portb_r()
{
	uint16_t ret = 0x0000;
	ret = m_i2cmem->read_sda() ? 1: 0;

	logerror("%s: spg2xx_game_senspeed_state::portb_r (%04x)\n", machine().describe_context(), ret);
	return ret;
}

void spg2xx_game_senspeed_state::portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (BIT(mem_mask, 1))
		m_i2cmem->write_scl(BIT(data, 1));
	if (BIT(mem_mask, 0))
		m_i2cmem->write_sda(BIT(data, 0));
}

void spg2xx_game_senspeed_state::senspeed(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_senspeed_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set_ioport("P1");
	m_maincpu->portb_in().set(FUNC(spg2xx_game_senspeed_state::portb_r));
	m_maincpu->portc_in().set_ioport("P3");

	m_maincpu->portb_out().set(FUNC(spg2xx_game_senspeed_state::portb_w));

	/*
	    ATMLH806
	    02B 1
	    A7J4565E
	*/
	I2C_24C01(config, "i2cmem", 0); // saves 0x80 bytes, but loading fails?
}


uint16_t spg2xx_game_comil_state::porta_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t data = m_porta_data;
	logerror("%s: Port A Read: %04x (%04x)\n", machine().describe_context(), data, mem_mask);
	return data;
}

void spg2xx_game_comil_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: Port A Write: %04x (%04x)\n", machine().describe_context(), data, mem_mask);
	m_porta_data = data;
}

uint16_t spg2xx_game_comil_state::portb_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t data = m_io_p2->read() & 0xfff0;

	if (!(m_porta_data & 0x0001)) data |= (m_extra_in[0]->read() & 0xf);
	if (!(m_porta_data & 0x0002)) data |= (m_extra_in[1]->read() & 0xf);
	if (!(m_porta_data & 0x0004)) data |= (m_extra_in[2]->read() & 0xf);
	if (!(m_porta_data & 0x0008)) data |= (m_extra_in[3]->read() & 0xf);
	if (!(m_porta_data & 0x0010)) data |= (m_extra_in[4]->read() & 0xf);
	if (!(m_porta_data & 0x0020)) data |= (m_extra_in[5]->read() & 0xf);
	if (!(m_porta_data & 0x0040)) data |= (m_extra_in[6]->read() & 0xf);
	if (!(m_porta_data & 0x0080)) data |= (m_extra_in[7]->read() & 0xf);

	logerror("%s: Port B Read: %04x (%04x)\n", machine().describe_context(), data, mem_mask);
	return data;
}

void spg2xx_game_comil_state::comil(machine_config &config)
{
	SPG28X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_comil_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->set_pal(true);
	m_screen->set_refresh_hz(50);

	m_maincpu->porta_in().set(FUNC(spg2xx_game_comil_state::porta_r));
	m_maincpu->portb_in().set(FUNC(spg2xx_game_comil_state::portb_r));

	m_maincpu->porta_out().set(FUNC(spg2xx_game_comil_state::porta_w));
	//m_maincpu->portb_out().set(FUNC(spg2xx_game_comil_state::portb_w));
}

void spg2xx_game_state::guitarfv(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->set_pal(true);
	m_screen->set_refresh_hz(50);

	m_maincpu->porta_in().set_ioport("P1");
	m_maincpu->portb_in().set_ioport("P2");
	m_maincpu->portc_in().set_ioport("P3");
}

uint16_t spg2xx_game_hasbro_93lc66_state::whacmole_porta_r()
{
	uint16_t ret = 0x0000;
	logerror("%s: porta_r\n", machine().describe_context());
	ret |= m_eeprom->do_read() << 3;
	return ret;
}

void spg2xx_game_hasbro_93lc66_state::whacmole_porta_w(uint16_t data)
{
	logerror("%s: porta_w (%04x)\n", machine().describe_context(), data);
	m_eeprom->di_write(BIT(data, 2));
	m_eeprom->cs_write(BIT(data, 0) ? ASSERT_LINE : CLEAR_LINE);
	m_eeprom->clk_write(BIT(data, 1) ? ASSERT_LINE : CLEAR_LINE);
}

void spg2xx_game_hasbro_93lc66_state::mylpony(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_hasbro_93lc66_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(spg2xx_game_hasbro_93lc66_state::whacmole_porta_r));
	m_maincpu->porta_out().set(FUNC(spg2xx_game_hasbro_93lc66_state::whacmole_porta_w));

	m_maincpu->portb_in().set(FUNC(spg2xx_game_hasbro_93lc66_state::base_portb_r));
	m_maincpu->portc_in().set(FUNC(spg2xx_game_hasbro_93lc66_state::base_portc_r));

	EEPROM_93C66_16BIT(config, m_eeprom); // HT93LC66A
}

void spg2xx_game_hasbro_93lc66_state::whacmole(machine_config &config)
{
	mylpony(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_hasbro_93lc66_state::mem_map_2m);
}


void spg2xx_game_state::tvsprt10(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_state::mem_map_2m);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(spg2xx_game_state::base_porta_r));
	m_maincpu->portb_in().set(FUNC(spg2xx_game_state::base_portb_r));
	m_maincpu->portc_in().set(FUNC(spg2xx_game_state::base_portc_r));
}

void spg2xx_game_state::spg28x(machine_config &config)
{
	SPG28X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(spg2xx_game_state::base_porta_r));
	m_maincpu->portb_in().set(FUNC(spg2xx_game_state::base_portb_r));
	m_maincpu->portc_in().set(FUNC(spg2xx_game_state::base_portc_r));
}


uint16_t spg2xx_game_tmntmutm_state::guny_r()
{
	int frame = m_screen->frame_number() & 1; // game will not register shots if the co-ordinates are exactly the same as previous shot
	uint16_t data = m_io_guny->read() ^ frame;
	logerror("%s: Gun Y Read: %04x\n", machine().describe_context(), data);
	return data;
}

uint16_t spg2xx_game_tmntmutm_state::gunx_r()
{
	int frame = (m_screen->frame_number() >> 1) & 1;
	uint16_t data = m_io_gunx->read() ^ frame;
	logerror("%s: Gun X Read: %04x\n", machine().describe_context(), data);
	return data;
}



void spg2xx_game_tmntmutm_state::tmntmutm(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_tmntmutm_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(spg2xx_game_tmntmutm_state::base_porta_r));
	m_maincpu->portb_in().set(FUNC(spg2xx_game_tmntmutm_state::base_portb_r));
	m_maincpu->portc_in().set(FUNC(spg2xx_game_tmntmutm_state::base_portc_r));

	m_maincpu->guny_in().set(FUNC(spg2xx_game_tmntmutm_state::guny_r));
	m_maincpu->gunx_in().set(FUNC(spg2xx_game_tmntmutm_state::gunx_r));

	I2C_24C08(config, "i2cmem", 0);
}

uint16_t spg2xx_game_albkickb_state::portb_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t data = machine().rand();// TODO
	logerror("%s: Port B Read: %04x (%04x)\n", machine().describe_context(), data, mem_mask);
	return data;
}

void spg2xx_game_albkickb_state::ablkickb(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_albkickb_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(spg2xx_game_albkickb_state::base_porta_r));
	m_maincpu->portb_in().set(FUNC(spg2xx_game_albkickb_state::portb_r));
	m_maincpu->portc_in().set(FUNC(spg2xx_game_albkickb_state::base_portc_r));
}


uint16_t spg2xx_game_pballpup_state::porta_r()
{
	uint16_t ret = m_io_p1->read() & 0xfff7;
//  logerror("%s: spg2xx_game_pballpup_state::porta_r\n", machine().describe_context());
	ret |= m_eeprom->do_read() ? 0x8 : 0x0;
	return ret;
}

void spg2xx_game_pballpup_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: spg2xx_game_pballpup_state::porta_w (%04x)\n", machine().describe_context(), data);

	porta_nobank_w(offset, data, mem_mask);

	// this can actually change bank from running code, because the code part in each bank is almost identical, just the data changes
	switch_bank((data & 0x1000) ? 1 : 0);
}

void spg2xx_game_pballpup_state::porta_nobank_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: spg2xx_game_pballpup_state::porta_nobank_w (%04x)\n", machine().describe_context(), data);
	m_eeprom->di_write(BIT(data, 2));
	m_eeprom->cs_write(BIT(data, 0) ? ASSERT_LINE : CLEAR_LINE);
	m_eeprom->clk_write(BIT(data, 1) ? ASSERT_LINE : CLEAR_LINE);
}


void spg2xx_game_pballpup_state::pballpup(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_pballpup_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(spg2xx_game_pballpup_state::porta_r));
	m_maincpu->portb_in().set(FUNC(spg2xx_game_pballpup_state::base_portb_r));
	m_maincpu->portc_in().set(FUNC(spg2xx_game_pballpup_state::base_portc_r));

	m_maincpu->porta_out().set(FUNC(spg2xx_game_pballpup_state::porta_w));
	m_maincpu->portb_out().set(FUNC(spg2xx_game_pballpup_state::portb_w));
	m_maincpu->portc_out().set(FUNC(spg2xx_game_pballpup_state::portc_w));

	m_maincpu->guny_in().set(FUNC(spg2xx_game_pballpup_state::base_guny_r));
	m_maincpu->gunx_in().set(FUNC(spg2xx_game_pballpup_state::base_gunx_r));

	EEPROM_93C66_16BIT(config, m_eeprom); // type?
}

TIMER_DEVICE_CALLBACK_MEMBER(spg2xx_game_pballpup_state::gun_irq)
{
	m_maincpu->extint_w(0, 1);
}


void spg2xx_game_pballpup_state::mpntball(machine_config &config)
{
	pballpup(config);
	m_maincpu->porta_out().set(FUNC(spg2xx_game_pballpup_state::porta_nobank_w));

	// the gun can track (even without the screen flash) so this must be tied to movement, or periodic
	TIMER(config, "guntimer").configure_periodic(FUNC(spg2xx_game_pballpup_state::gun_irq), attotime::from_hz(1000));
}

void spg2xx_game_pballpup_state::mpntbalt(machine_config &config)
{
	mpntball(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_pballpup_state::mem_map_2m);
}

uint16_t spg2xx_game_swclone_state::porta_r()
{
	uint16_t ret = m_io_p1->read() & 0xfffe;
	ret |= m_i2cmem->read_sda() ? 0x1: 0x0;

	//logerror("%s: spg2xx_game_swclone_state::porta_r (%04x)\n", machine().describe_context(), ret);
	return ret;
}

void spg2xx_game_swclone_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	//logerror("%s: spg2xx_game_swclone_state::porta_w (%04x & %04x)\n", machine().describe_context(), data, mem_mask);

	if (BIT(mem_mask, 1))
		m_i2cmem->write_scl(BIT(data, 1));
	if (BIT(mem_mask, 0))
		m_i2cmem->write_sda(BIT(data, 0));

	m_porta_data = data;
}


void spg2xx_game_swclone_state::swclone(machine_config &config)
{
	SPG2XX_128(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_swclone_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(spg2xx_game_swclone_state::porta_r));
	m_maincpu->portb_in().set(FUNC(spg2xx_game_swclone_state::base_portb_r));
	m_maincpu->portc_in().set(FUNC(spg2xx_game_swclone_state::base_portc_r));

	m_maincpu->porta_out().set(FUNC(spg2xx_game_swclone_state::porta_w));
	m_maincpu->portb_out().set(FUNC(spg2xx_game_swclone_state::portb_w));
	m_maincpu->portc_out().set(FUNC(spg2xx_game_swclone_state::portc_w));

	m_maincpu->guny_in().set(FUNC(spg2xx_game_swclone_state::base_guny_r));
	m_maincpu->gunx_in().set(FUNC(spg2xx_game_swclone_state::base_gunx_r));

	I2C_24C08(config, "i2cmem", 0);
}



uint16_t spg2xx_game_dreamlss_state::porta_r()
{
	uint16_t ret = m_io_p1->read()&0xefff;
	ret |= m_porta_data & 0x1000; // needs to be able to read back current bank
	logerror("%s: spg2xx_game_dreamlss_state::porta_r\n", machine().describe_context());
	return ret;
}

void spg2xx_game_dreamlss_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: spg2xx_game_dreamlss_state::porta_w (%04x)\n", machine().describe_context(), data);

	m_porta_data = data;

	switch_bank((data & 0x1000) ? 1 : 0);
}

uint16_t spg2xx_game_dreamlss_state::portb_r()
{
	uint16_t ret = m_portb_data & 0xfffe;
	ret |= m_i2cmem->read_sda() ? 0x1: 0x0;

	//logerror("%s: spg2xx_game_dreamlss_state::portb_r (%04x)\n", machine().describe_context(), ret);
	return ret;
}

void spg2xx_game_dreamlss_state::portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	//logerror("%s: spg2xx_game_dreamlss_state::portb_w (%04x & %04x)\n", machine().describe_context(), data, mem_mask);

	if (BIT(mem_mask, 1))
		m_i2cmem->write_scl(BIT(data, 1));
	if (BIT(mem_mask, 0))
		m_i2cmem->write_sda(BIT(data, 0));

	m_portb_data = data;
}


void spg2xx_game_dreamlss_state::dreamlss(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_dreamlss_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(spg2xx_game_dreamlss_state::porta_r));
	m_maincpu->portb_in().set(FUNC(spg2xx_game_dreamlss_state::portb_r));
	m_maincpu->portc_in().set(FUNC(spg2xx_game_dreamlss_state::base_portc_r));

	m_maincpu->porta_out().set(FUNC(spg2xx_game_dreamlss_state::porta_w));
	m_maincpu->portb_out().set(FUNC(spg2xx_game_dreamlss_state::portb_w));
	m_maincpu->portc_out().set(FUNC(spg2xx_game_dreamlss_state::portc_w));

	I2C_24C08(config, "i2cmem", 0);
}

uint16_t spg2xx_game_lpetshop_state::porta_r()
{
	uint16_t ret = 0;
	ret |= m_i2cmem->read_sda() ? 0x1: 0x0;
	return ret;
}

void spg2xx_game_lpetshop_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	//logerror("%s: spg2xx_game_lpetshop_state::porta_w (%04x & %04x)\n", machine().describe_context(), data, mem_mask);
	if (BIT(mem_mask, 1))
		m_i2cmem->write_scl(BIT(data, 1));
	if (BIT(mem_mask, 0))
		m_i2cmem->write_sda(BIT(data, 0));
}


void spg2xx_game_lpetshop_state::lpetshop(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_lpetshop_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(spg2xx_game_lpetshop_state::porta_r));
	m_maincpu->portb_in().set(FUNC(spg2xx_game_lpetshop_state::base_portb_r));
	m_maincpu->portc_in().set(FUNC(spg2xx_game_lpetshop_state::base_portc_r));

	m_maincpu->porta_out().set(FUNC(spg2xx_game_lpetshop_state::porta_w));
	m_maincpu->portb_out().set(FUNC(spg2xx_game_lpetshop_state::portb_w));
	m_maincpu->portc_out().set(FUNC(spg2xx_game_lpetshop_state::portc_w));

	I2C_24C08(config, "i2cmem", 0);
}


void spg2xx_game_gssytts_state::portc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	int bank = 0;

	logerror("%s: portc_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
		(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
		(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
		(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
		(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
		(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
		(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
		(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
		(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
		(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
		(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
		(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
		(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
		(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
		(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
		(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
		(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');

	if (mem_mask & 1)
		if (data & 1)
			bank |= 1;

	if (mem_mask & 2)
		if (data & 2)
			bank |= 2;

	m_upperbank->set_entry(bank);
	m_maincpu->invalidate_cache();
}

void spg2xx_game_gssytts_state::machine_start()
{
	m_upperbank->configure_entries(0, memregion("maincpu")->bytes()/0x400000, memregion("maincpu")->base(), 0x400000);
	m_upperbank->set_entry(1);

	spg2xx_game_state::machine_start();
}

void spg2xx_game_gssytts_state::machine_reset()
{
	m_upperbank->set_entry(1);

	spg2xx_game_state::machine_reset();
}


void spg2xx_game_gssytts_state::gssytts(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_gssytts_state::mem_map_upperbank);

	spg2xx_base(config);

//  m_maincpu->porta_out().set(FUNC(spg2xx_game_state::porta_w));
//  m_maincpu->portb_out().set(FUNC(spg2xx_game_state::portb_w));
//  m_maincpu->portc_out().set(FUNC(spg2xx_game_state::portc_w));

	m_maincpu->porta_in().set_ioport("P1");
	m_maincpu->portb_in().set_ioport("P2");
	m_maincpu->portc_in().set_ioport("P3");
}


void spg2xx_game_wfcentro_state::wfcentro(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_wfcentro_state::mem_map_wfcentro);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(spg2xx_game_wfcentro_state::base_porta_r));
	m_maincpu->portb_in().set(FUNC(spg2xx_game_wfcentro_state::base_portb_r));
	m_maincpu->portc_in().set(FUNC(spg2xx_game_wfcentro_state::base_portc_r));
}

void spg2xx_game_lexiart_state::lexiart(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_lexiart_state::mem_map_lexiart);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(spg2xx_game_lexiart_state::base_porta_r));
	m_maincpu->portb_in().set(FUNC(spg2xx_game_lexiart_state::base_portb_r));
	m_maincpu->portc_in().set(FUNC(spg2xx_game_lexiart_state::base_portc_r));
}

void spg2xx_game_senwfit_state::portc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	int bank = 0;

	logerror("%s: portc_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
		(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
		(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
		(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
		(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
		(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
		(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
		(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
		(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
		(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
		(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
		(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
		(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
		(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
		(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
		(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
		(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');

	if (mem_mask & 1)
		if (data & 1)
			bank |= 1;

	if (mem_mask & 2)
		if (data & 2)
			bank |= 2;

	if (mem_mask & 4)
		if (data & 4)
			bank |= 4;

	m_upperbank->set_entry(bank);
	m_maincpu->invalidate_cache();
}


void spg2xx_game_senwfit_state::init_senwfit()
{
	uint8_t *src = memregion("maincpu")->base();
	int len = memregion("maincpu")->bytes();

	std::vector<u8> buffer(len);

	for (int i = 0; i < len; i++)
	{
		int newaddr = bitswap<25>(i, 24,23,22,20,9,19,18,21,17,16,15,14,13,12,11,10,8,7,6,5,4,3,2,1,0);
		buffer[i] = src[newaddr];
	}
	std::copy(buffer.begin(), buffer.end(), &src[0]);

}

void spg2xx_game_state::rad_skatp(machine_config &config)
{
	rad_skat(config);
	m_maincpu->set_pal(true);
	m_screen->set_refresh_hz(50);
//  m_screen->set_size(320, 312);
}


void spg2xx_game_state::rad_crik(machine_config &config)
{
	SPG28X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set_ioport("P1");
	m_maincpu->portb_in().set_ioport("P2");
	m_maincpu->portc_in().set_ioport("P3");
	m_maincpu->i2c_w().set(FUNC(spg2xx_game_state::i2c_w));
	m_maincpu->i2c_r().set(FUNC(spg2xx_game_state::i2c_r));
}

uint16_t spg2xx_game_ordentv_state::ordentv_portc_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t data = m_io_p3->read() ^ (machine().rand() & 1);
	logerror("%s: Port C Read: %04x (%04x)\n", machine().describe_context(), data, mem_mask);
	return data;
}

void spg2xx_game_ordentv_state::ordentv(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_ordentv_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(spg2xx_game_ordentv_state::base_porta_r));
	m_maincpu->portb_in().set(FUNC(spg2xx_game_ordentv_state::base_portb_r));
	m_maincpu->portc_in().set(FUNC(spg2xx_game_ordentv_state::ordentv_portc_r));
}


void spg2xx_game_hotwheels_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: porta_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
		(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
		(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
		(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
		(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
		(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
		(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
		(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
		(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
		(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
		(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
		(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
		(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
		(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
		(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
		(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
		(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');

	m_porta_dat_hot = data;
}

uint16_t spg2xx_game_hotwheels_state::hotwheels_porta_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t data;
	if (m_porta_dat_hot)
		data = m_io_p1->read();
	else
		data = m_io_p1_extra->read();

	logerror("%s: Port A Read: %04x (%04x)\n", machine().describe_context(), data, mem_mask);
	return data;
}

void spg2xx_game_hotwheels_state::hotwheels(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_hotwheels_state::mem_map_2m);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(spg2xx_game_hotwheels_state::hotwheels_porta_r));
	m_maincpu->portb_in().set(FUNC(spg2xx_game_hotwheels_state::base_portb_r));
	m_maincpu->portc_in().set(FUNC(spg2xx_game_hotwheels_state::base_portc_r));

	m_maincpu->porta_out().set(FUNC(spg2xx_game_hotwheels_state::porta_w));

	m_maincpu->set_pal(true);
	m_screen->set_refresh_hz(50);
}

uint16_t spg2xx_game_doraphone_state::porta_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t matrix = 0x000f;
	for (int b = 1; 6 >= b; ++b)
	{
		if (!BIT(m_portb_data, b))
			matrix &= m_io_p1_rows[b - 1]->read();
	}

	return matrix | (m_io_p1->read() & 0xfff0);
}

void spg2xx_game_doraphone_state::portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	m_portb_data = data;
}

void spg2xx_game_doraphone_state::doraphone(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_doraphone_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(spg2xx_game_doraphone_state::porta_r));
	m_maincpu->portb_out().set(FUNC(spg2xx_game_doraphone_state::portb_w));
	m_maincpu->portb_in().set(FUNC(spg2xx_game_doraphone_state::base_portb_r));
	m_maincpu->portc_in().set(FUNC(spg2xx_game_doraphone_state::base_portc_r));
}

void spg2xx_game_prail_state::prail_portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (mem_mask & 0x0f)
	{
		uint8_t const bank = bitswap<4>(data, 3, 2, 0, 1);
		switch_bank(bank);
	}

	portb_w(offset, data & ~0x000f, mem_mask & ~0x000f);
}

void spg2xx_game_prail_state::prail(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen); // SPG243
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_prail_state::mem_map_4m);

	spg2xx_base(config);
	m_maincpu->porta_in().set(FUNC(spg2xx_game_prail_state::base_porta_r));
	m_maincpu->portb_in().set(FUNC(spg2xx_game_prail_state::base_portb_r));
	m_maincpu->portc_in().set(FUNC(spg2xx_game_prail_state::base_portc_r));

	m_maincpu->portb_out().set(FUNC(spg2xx_game_prail_state::prail_portb_w));

	// TODO: this is not currently hooked up, it's used to store the unlock states for the gallery
	I2C_24C02(config, "i2cmem", 0); // ATMLH13402C (24C02 compatible)
}


void spg2xx_game_doraphone_state::doraphonep(machine_config &config)
{
	doraphone(config);

	m_maincpu->set_pal(true);
	m_screen->set_refresh_hz(50);
}


void epo_tetr_game_state::machine_start()
{
	spg2xx_game_state::machine_start();

	save_item(NAME(m_old_portb_data));
	save_item(NAME(m_old_portb_extra_latch));
}

void epo_tetr_game_state::machine_reset()
{
	spg2xx_game_state::machine_reset();

	m_old_portb_data = 0;
	m_old_portb_extra_latch = 0;
}

uint16_t epo_tetr_game_state::epo_tetr_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t data = 0;

	int eeprom = m_i2cmem->read_sda();
	data |= (eeprom << 1);

	int p2 = m_old_portb_extra_latch & 1;
	data |= (p2 << 6);

	if (!machine().side_effects_disabled())
		logerror("%s: epo_tetr_r: %04x (%04x)\n", machine().describe_context(), data, mem_mask);

	return data;
}

void epo_tetr_game_state::epo_tetr_portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	portb_w(offset, data, mem_mask);

	if (BIT(mem_mask, 0))
		m_i2cmem->write_scl(BIT(data, 0));
	if (BIT(mem_mask, 1))
		m_i2cmem->write_sda(BIT(data, 1));

	if (BIT(mem_mask, 4))
	{
		if ((BIT(data, 4)) && (!(BIT(m_old_portb_data, 4))))
		{
			m_old_portb_extra_latch = m_ioextra->read();
		}
	}

	if (BIT(mem_mask, 5))
	{
		if ((BIT(data, 5)) && (!(BIT(m_old_portb_data, 5))))
		{
			m_old_portb_extra_latch >>= 1;
		}
	}

	m_old_portb_data = data;
}


void epo_tetr_game_state::epo_tetr(machine_config& config)
{
	spg2xx(config);

	I2C_24C02(config, "i2cmem", 0); // S24CS02A

	m_maincpu->portb_in().set(FUNC(epo_tetr_game_state::epo_tetr_r));
	m_maincpu->portb_out().set(FUNC(epo_tetr_game_state::epo_tetr_portb_w));
}


void spg2xx_game_smartcycle_state::machine_start()
{
	spg2xx_game_state::machine_start();

	// if there's a cart, override the standard banking
	if (m_cart && m_cart->exists())
	{
		std::string region_tag;
		m_cart_region = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());
		m_bank->configure_entries(0, (m_cart_region->bytes() + 0x7fffff) / 0x800000, m_cart_region->base(), 0x800000);
		m_bank->set_entry(0);
	}
}

DEVICE_IMAGE_LOAD_MEMBER(spg2xx_game_smartcycle_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	if (size > 0x80'0000)
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported cartridge size (must be no more than 8M)");

	m_cart->rom_alloc(0x800000, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

void spg2xx_game_smartcycle_state::smartcycle(machine_config &config)
{
	spg2xx(config);

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "smartcycle_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(spg2xx_game_smartcycle_state::cart_load));
	m_cart->set_must_be_loaded(true);

	SOFTWARE_LIST(config, "smartcycle_cart").set_original("smartcycle_cart");
}

void spg2xx_game_pdcj_state::portc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if ((data & mem_mask) & 0x0800)
		m_upperbank->set_entry(2);
	else
		m_upperbank->set_entry(1);

	m_maincpu->invalidate_cache();
}

void spg2xx_game_pdcj_state::machine_start()
{
	m_upperbank->configure_entries(0, memregion("maincpu")->bytes() / 0x400000, memregion("maincpu")->base(), 0x400000);
	m_upperbank->set_entry(1);
	spg2xx_game_state::machine_start();
}

void spg2xx_game_pdcj_state::machine_reset()
{
	m_upperbank->set_entry(1);
	spg2xx_game_state::machine_reset();
}

void spg2xx_game_pdcj_state::pdcj(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_pdcj_state::mem_map_upperbank);

	spg2xx_base(config);

	m_maincpu->porta_in().set_ioport("P1");
	m_maincpu->portb_in().set_ioport("P2");
	m_maincpu->portc_in().set_ioport("P3");
}


void spg2xx_game_ddr33v_state::init_ddr33v()
{
	// what is this checking? timer? battery state? protection? it goes to a blank screen after the boot logo otherwise
	uint16_t* rom = (uint16_t*)memregion("maincpu")->base();
	rom[0x208055] = 0x4440;
}

ROM_START( rad_skat )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "skateboarder.bin", 0x000000, 0x400000, CRC(08b9ab91) SHA1(6665edc4740804956136c68065890925a144626b) )
ROM_END

ROM_START( rad_skatp ) // rom was dumped from the NTSC version, but region comes from an io port, so ROM is probably the same
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "skateboarder.bin", 0x000000, 0x400000, CRC(08b9ab91) SHA1(6665edc4740804956136c68065890925a144626b) )
ROM_END

ROM_START( rad_fb2 )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "football2.bin", 0x000000, 0x400000, CRC(96b4f0d2) SHA1(e91f2ac679fb0c026ffe216eb4ab58802f361a17) )
ROM_END

ROM_START( rad_jib )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jibbi.u6", 0x000000, 0x200000, CRC(33f93a80) SHA1(1ecfc66435405206003f5c23c4a0aa9195a7fe0d) )

	// has an AT24C02
ROM_END

ROM_START( rad_crik ) // only released in EU?
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "cricket.bin", 0x000000, 0x200000, CRC(6fa0aaa9) SHA1(210d2d4f542181f59127ce2f516d0408dc6de7a8) )
ROM_END

ROM_START( mattelcs )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "mattelclassicsports.bin", 0x000000, 0x100000, CRC(e633e7ad) SHA1(bf3e325a930cf645a7e32195939f3c79c6d35dac) )
ROM_END

ROM_START( abltenni )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "ablpnpwirelesstennis.bin", 0x000000, 0x400000, CRC(66bd8ef1) SHA1(a83640d5d9e84e10d29a065a61e0d7bbec16c6e4) )
ROM_END

ROM_START( totspies )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "w321tg.u2", 0x000000, 0x400000, CRC(76152ad7) SHA1(b37ea950670eb927f3f0ab5e38d0e2a5f3ca7904) )
ROM_END

ROM_START( ablkickb )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "ablkickboxing.bin", 0x000000, 0x800000,  CRC(61394c45) SHA1(291d28a39edcb32a8f5d776a5e5c05e6fd0abece) )
ROM_END

ROM_START( lxspidaj )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "mx29lv320ct.u2", 0x000000, 0x400000, CRC(e7e03c62) SHA1(ab13452f0436efb767f01dff54dd48a528538e3f) )
ROM_END

ROM_START( lxairjet )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "w321tg.u5", 0x000000, 0x400000, CRC(105b226f) SHA1(2622678a8586ee6edae17715657037a5419e3321) )
ROM_END


ROM_START( fordrace )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "fordracing_29lv320ct_00c222a7.bin", 0x000000, 0x400000, CRC(998cad17) SHA1(98a65e9e0ec17e3366e0ac6ddc2d852a7efb360e) )
ROM_END

ROM_START( carled99 )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "carledwards.u2", 0x000000, 0x400000, CRC(8921f13a) SHA1(e7216e42745ab6bfd1ad02382eedbc5e741a86e9) )
ROM_END

ROM_START( comil )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "ukmillionaire.bin", 0x000000, 0x400000, CRC(b7e8e126) SHA1(fc76dba672eb5c4c115e16d8ea4a45a6e859f87c) )
ROM_END

ROM_START( tvsprt10 )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "tvsports10in1.bin", 0x000000, 0x400000, CRC(98b79889) SHA1(b0ba534d59b794bb38c071c70ab5bcf711364e06) )
ROM_END

ROM_START( decathln )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "decathlon.bin", 0x000000, 0x400000, CRC(63c8e6b6) SHA1(6a25b68b45336e04a2bfd75b43a494349024d714) )
ROM_END

ROM_START( decathlna )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "abldecathlon2.bin", 0x000000, 0x400000, CRC(594ed954) SHA1(6ddd9df8f645ac8e93ee37337ca9fb5f7f942827) )
ROM_END

ROM_START( guitarfv )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "mx26l64.bin", 0x000000, 0x800000, CRC(eaadd2c2) SHA1(0c3fe004dbaa52a335c6ddcecb9e9f5582d7ef35) )
ROM_END

ROM_START( guitarss )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "guitar_superstar_stratocaster.bin", 0x000000, 0x800000, CRC(63950016) SHA1(28b9613571f47c49995aa35c4d4a2d6f68389813) )
ROM_END

ROM_START( guitarssa )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "guitar_superstar_flying_v.bin", 0x000000, 0x800000, CRC(af0c837c) SHA1(f04c9a4292f811d92311d19fb35dcee3f1649a14) )
ROM_END

ROM_START( drumsups )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "drumsuperstar.bin", 0x000000, 0x800000, CRC(f3d5fd6d) SHA1(4d0c9ba7531b3df68bd9c020e46d07445301adf9) )
ROM_END


ROM_START( tmntbftc )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "tmntbftc.bin", 0x000000, 0x400000, CRC(f923da5b) SHA1(79b290b75d06dabd0f579800edc4453b044c8fd4) )
ROM_END

ROM_START( knd )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "knd_sst39vf3201_00bf235b.bin", 0x000000, 0x400000, CRC(3b82479d) SHA1(2a4ddd5c6af2376e4725aeb44e79b0f9c45ca8c1) )
ROM_END

ROM_START( gssytts )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "guitarssytts.bin", 0x000000, 0x800000, CRC(ec3de9e1) SHA1(690efe2676c664c2be52cda00d6dcb9d60a26e9a) ) // no data
	ROM_CONTINUE(0x000000, 0x800000) // 1st 8mb
	ROM_CONTINUE(0x800000, 0x800000) // no data
	ROM_CONTINUE(0x800000, 0x800000) // 2nd 8mb
ROM_END

ROM_START( senwfit )
	ROM_REGION( 0x2000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "wirelessfit.bin", 0x000000, 0x2000000, CRC(bfdc9c56) SHA1(dd0d4262720fcc3fab5b66d39df9be3419b07178) )
ROM_END


ROM_START( jjstrip )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "strippoker.bin", 0x000000, 0x200000, CRC(7a70e6c8) SHA1(3d5da4774b00977939f309f3e71473dde9b70435) )
ROM_END

ROM_START( tmntmutm )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "tmntmutantsandmonsters_sst39vf3201_00bf235b.bin", 0x000000, 0x400000, CRC(93ab5ff7) SHA1(e78a5d380663d351ad9be5087ec8434d9be16ba7) )
ROM_END

ROM_START( pballpup )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "paintballpoweredup.bin", 0x000000, 0x1000000, CRC(57dbdfd1) SHA1(d98cb7321cc7af092f6f4f83e85fabbdbc1bbd95) )

	ROM_REGION16_BE( 0x200, "eeprom", ROMREGION_ERASE00 )
	// ensure eeprom defaults to 00 or there are unwanted invalid entries already saved
ROM_END

ROM_START( dreamlss )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "dreamlifesuperstar.bin", 0x000000, 0x1000000, CRC(aefad2c3) SHA1(cf962082f09e27d7d24cfc722ae978d9aa735a57) )
ROM_END

ROM_START( swclone )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "swclone.bin", 0x000000, 0x800000, CRC(2c983509) SHA1(6138f21fe0b82a7121c4639b6833d4014d5aeb74) )

	ROM_REGION( 0x400, "i2cmem", ROMREGION_ERASE00 )
	// ensure eeprom defaults to 00 or there are unwanted invalid entries already saved
ROM_END

ROM_START( vtechtvssp )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "vtechtvstation_sp.bin", 0x000000, 0x800000, CRC(4a2e91eb) SHA1(1ff9cc0360b670cc0ad7efa9de0edd2c68d4d8e3) )
ROM_END

ROM_START( vtechtvsgr )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "vtechtvstation_gr.bin", 0x000000, 0x800000, CRC(879f1b12) SHA1(c14d52bead2c190130ce88cbdd4f5e93145f13f9) )
ROM_END

ROM_START( itvphone )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "inttvphone.bin", 0x000000, 0x200000, CRC(2ecbb0ad) SHA1(2b6babaaf1582e6b1de944258eba87ddf30406c5) )
ROM_END

ROM_START( jouet )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jouet.bin", 0x000000, 0x400000, CRC(da46097e) SHA1(f760f4d126a8291b7dacdea7a70691b25ad8b989) )
ROM_END

ROM_START( doraphon )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "doraphone.bin", 0x000000, 0x800000, CRC(a79c154b) SHA1(f5b9bf63ea52d058252ab6702508b519fbdee0cc) )
ROM_END

ROM_START( doraphonf )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "doraphone_fr.u4", 0x000000, 0x800000, CRC(216632a1) SHA1(b2bd81656a261e09814792f52428eead2ea7ce1f) )
ROM_END

ROM_START( doraglob )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "doraglobe.bin", 0x000000, 0x800000, CRC(6f454c50) SHA1(201e2de3d90abe017a8dc141613cbf6383423d13) )
ROM_END

ROM_START( doraglobuk )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "doraglobeuk.u4", 0x000000, 0x800000, CRC(b20a22b8) SHA1(f7e42a86479e68092b27068535cff90ca686f361) )
ROM_END

ROM_START( doraglobf )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "doraglobefrance.bin", 0x000000, 0x800000, CRC(7124edc1) SHA1(b144fc1f13a28299ef14f1d01f7acd2677e4ebb9) )
ROM_END

ROM_START( doraglobg )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "doraglobegerman.bin", 0x000000, 0x800000, CRC(538aa197) SHA1(e97e0641df04074a0e45d02cecb43fbec91a4ce6) )
ROM_END

ROM_START( senspeed )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "speedracer.bin", 0x000000, 0x800000, CRC(4efbcd39) SHA1(2edffbaa9ea309ad308fa60f32d8b7a98ee313c7) )
ROM_END

ROM_START( ordentv )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "taikeeordenadortv.bin", 0x000000, 0x800000, CRC(ba15895a) SHA1(0a18076cbc3264c91473b8518dfb10d679321b47) )
ROM_END

ROM_START( wfcentro )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "winfuncentro.bin", 0x000000, 0x800000, CRC(fd6ad052) SHA1(78af844729bf4843dc70531349e38a8c25caf748) )
ROM_END

ROM_START( wfart )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "artstudio.bin", 0x000000, 0x800000, CRC(f5fd657e) SHA1(0005826a5b22a17cafffaf7328092c8d84217398) )
ROM_END

ROM_START( lexiart )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "lexibookartstudio.u3", 0x000000, 0x800000, CRC(fc417abb) SHA1(c0a18a2cf11c47086722f0ec88410614fed7c6f7) )
ROM_END

ROM_START( lexibds )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "barbiedrawingstudio.u3", 0x000000, 0x400000, CRC(16b5b52e) SHA1(e3719523d92d1302883f0b0c2d4b3fabedc34319) ) // no chip markings, dumped as 29LV320
ROM_END

ROM_START( tiktokmm )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "webcamthingy.bin", 0x000000, 0x800000, CRC(54c0d4a9) SHA1(709ee607ca447baa6f7e686268df1998372fe617) )
ROM_END

ROM_START( jeuint )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jeuinteractiftv.bin", 0x000000, 0x800000, CRC(27103086) SHA1(d1313f416ae8ec85e523fefd523d6f4b7970eaf3) )
ROM_END

ROM_START( hotwhl2p )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "hotwheels.bin", 0x000000, 0x400000, CRC(f3520b74) SHA1(02a53558d68cf3640a9ab09514cd6cebff8b30af) )
ROM_END

ROM_START( rocksock )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "rsrobots.u2", 0x000000, 0x400000, CRC(1f86aeab) SHA1(0b4afeb113ecdb5349629fa7cc9ceaf282490e01) )
ROM_END


ROM_START( doyousud )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "doyousudoku.bin", 0x000000, 0x100000, CRC(83cafebb) SHA1(a84c7191bc6b0d321415af0b7d2dd69e52c134a1) )

	ROM_REGION( 0x800, "eeprom", ROMREGION_ERASE00 ) // probably just used for saving puzzle progress
	ROM_LOAD( "at24c16a.u3", 0x000, 0x800, CRC(414ea94d) SHA1(8565a66fd0228104c64a169cdb20715e7b23cfaf) )
ROM_END

ROM_START( virtbb )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "virtualbb.bin", 0x000000, 0x400000, CRC(7cb7a69f) SHA1(eae0c516c1ff89a369662d09321feafc8a8054b0) )
ROM_END

ROM_START( virtten )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "virttennis.bin", 0x000000, 0x400000, CRC(e665bea9) SHA1(8c2c9f879c929e224cd885165ed60aed8baeb19d) )
ROM_END

ROM_START( ddr33v )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "ddr33v.bin", 0x000000, 0x800000, CRC(56c7015c) SHA1(a1faef2ab6eb191dea1497f8cfd4ccbd8c504e6d) )
ROM_END

ROM_START( anpantv )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "anpanman_tv.bin", 0x000000, 0x800000, CRC(5e32dc1a) SHA1(bae260ffc56f5315cdafd5bc40966ec6d31e267f) )
ROM_END

ROM_START( dmbtjunc )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "battlejunction.u3", 0x000000, 0x800000, CRC(31471c53) SHA1(94fdd8c4c67914054e304a55042c10710af2e596) )
ROM_END

ROM_START( ban_krkk )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "quiz.bin", 0x000000, 0x400000, CRC(6f51180a) SHA1(38017ecaae4eead38482aeb04c90b5a5eeebd6ca) )
ROM_END

ROM_START( ban_tam2 )
	// has "#12175 TV Game Tamagotchi 2" on PCB
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "tvgametamagotchi2.bin", 0x000000, 0x800000, CRC(cc4322fa) SHA1(7ba0d3e8b9012dfa631a921cc2c7da9a21e535bd) )
ROM_END


ROM_START( epo_tetr )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "mx29lv320mb.bin", 0x000000, 0x400000, CRC(b4ad30e0) SHA1(83e30e199854c647f9a197562d1bf1f3bc847fff) )
ROM_END


ROM_START( prail )
	ROM_REGION( 0x8000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "traingame.u1", 0x000000, 0x8000000, CRC(5c96d526) SHA1(cda0280b320762bda7a7358ec7ce29690aa815fb) )
ROM_END

ROM_START( whacmole )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "whacamole.u1a", 0x000000, 0x400000, CRC(5841ba80) SHA1(b3ec922e1899a1f2e34069a50e36721e925afb9f) )
ROM_END

ROM_START( mpntbalt )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "paintball.u1", 0x000000, 0x400000, CRC(888e140e) SHA1(2406cfe7d9e40f112b6f161aba4886472524157e) )

	//ROM_REGION16_BE( 0x200, "eeprom", ROMREGION_ERASE00 ) // dumped, but just contains user profiles / settings
	//ROM_LOAD16_WORD_SWAP( "93c66.u2", 0x000, 0x200, CRC(3b5cf033) SHA1(5ac730141d2f44da6a18ab1ccb540543bace7553) )
ROM_END

ROM_START( mpntball )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "missionpaintball.u1", 0x000000, 0x800000, CRC(3962731a) SHA1(f33e69c681fb69204cf04174f725ebae30da6a43) )

	//ROM_REGION16_BE( 0x200, "eeprom", ROMREGION_ERASE00 ) // dumped, but just contains user profiles / settings
	//ROM_LOAD16_WORD_SWAP( "93c66.u4", 0x000, 0x200, CRC(cb6b9c9f) SHA1(78f485ee9a1f724428d08e4e2e152e95485777bb) )
ROM_END

ROM_START( backybbs )
	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "backyardbs.bin", 0x000000, 0x400000, CRC(9c378f27) SHA1(ca0c212482d0743db10046f550fee511a13e1ddb) )
ROM_END

ROM_START( barbpet )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "barbiepetrescue.u3", 0x000000, 0x200000, CRC(a740b19d) SHA1(e3c5bc3e1b38457ca826d6eba6e492c33d04b97e) )
ROM_END

ROM_START( lpetshop )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "littlestpetshop.u1a", 0x000000, 0x800000, CRC(b5a2a922) SHA1(681bd2dd6eae87ec285307f3fc50b3b2c384edaf) )
ROM_END

ROM_START( hotwhls )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	// this dump was consistent when using one programmer to dump it, but has broken shadow on TRACK text in menu
	//ROM_LOAD16_WORD_SWAP( "hotwheels.u2", 0x000000, 0x200000, CRC(d8a07cec) SHA1(dcdf876073f69b5565eb83fee80c4ea0deea1ff9) )
	// this dump has a lot of differences to above, and was consistent with another programmer, no corrupt shadow
	// leaving BAD_DUMP flag due to low confidence though, could be the ROM was failing.
	ROM_LOAD16_WORD_SWAP( "hotwheels.u2", 0x000000, 0x200000, BAD_DUMP CRC(0237d6b2) SHA1(e962720cf950f841d9aed2924d4878201763b309) )
ROM_END

ROM_START( mylpony )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "mylittlepony.u1", 0x000000, 0x800000, CRC(4df05ad2) SHA1(7b0c19c1fda57c91717db33b7a960fcd77949a04) )
ROM_END

ROM_START( wordlnch )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "wordplay.u7", 0x000000, 0x800000, CRC(604f59ff) SHA1(024d554a15e6c3a6b9c3a15bfd657964d1deba83) )

	// these are needed to boot right now, checksum passes without them
	// patch start-up check
	ROM_FILL(                      0x97f9e, 1, 0x42 )
	ROM_FILL(                      0x97f9f, 1, 0x40 )

	// patch loop on leapfrog logo
	ROM_FILL(                      0x95bc8, 1, 0x44 )
	ROM_FILL(                      0x95bc9, 1, 0x40 )
ROM_END

ROM_START( smartcyc )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	/* no system ROM, all game data is on cartridges */
ROM_END

ROM_START( spidm2 )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "spiderman2.bin", 0x000000, 0x400000, CRC(7ed425d8) SHA1(25b2078cdc83f5b54425a863336424c56ff20121) )
ROM_END

ROM_START( dinothun )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "dinothunder.bin", 0x000000, 0x400000, CRC(03e82604) SHA1(c39d72aa8a0750ee38ab01b317e77a46e1d6004e) )
ROM_END

ROM_START( pdcj )
	ROM_REGION( 0xc00000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "chip1.u2", 0x000000, 0x800000, CRC(ba4f2279) SHA1(cd6bc20f21e096251ca43b539c46cb51c790769c) )
	ROM_LOAD16_WORD_SWAP( "chip2.u3", 0x800000, 0x400000, CRC(4fb335ac) SHA1(d6499816acb3da5c4f6a582bd3104c707422eb34) )
ROM_END

void spg2xx_game_state::init_crc()
{
	// several games have a byte sum checksum listed at the start of ROM, this little helper function logs what it should match.
	const int length = memregion("maincpu")->bytes();
	const uint8_t* rom = memregion("maincpu")->base();

	uint32_t checksum = 0x00000000;
	// the first 0x10 bytes are where the "chksum:xxxxxxxx " string is listed, so skip over them
	for (int i = 0x10; i < length; i++)
	{
		checksum += rom[i];
	}

	logerror("Calculated Byte Sum of bytes from 0x10 to 0x%08x is %08x)\n", length - 1, checksum);
}


void spg2xx_game_state::init_tvsprt10()
{
	uint16_t* rom = (uint16_t*)memregion("maincpu")->base();
	// hack 'MASK' check (some kind of EEPROM maybe?)
	// this hack means the ROM CRC fails, but without it the CRC is OK, so dump is good.
	/*
	port b 0010 = chip select? / always set when accessing?
	       0008 = write enable (set when writing, not when reading)
	       0004 = chip select? / always set when accessing?
	       0002 = clock? (toggles)
	       0001 = data in / out
	*/
	if (rom[0x18c55d] == 0x9240) rom[0x18c55d] ^= 0x0001; // tvsprt10
	if (rom[0x179911] == 0x9240) rom[0x179911] ^= 0x0001; // decathln
	if (rom[0x16fa67] == 0x9240) rom[0x16fa67] ^= 0x0001; // decathlna
}

void spg2xx_game_swclone_state::init_swclone()
{
	uint16_t* rom = (uint16_t*)memregion("maincpu")->base();
	rom[0x2649d1] = 0x0000; // don't write 0x1234 to the start of the RAM that is copied to spriteram on startup (this is an explicit write, probably actually a failure condition for something?)
}

void spg2xx_game_albkickb_state::init_ablkickb()
{
	uint16_t *ROM = (uint16_t*)memregion("maincpu")->base();
	int size = memregion("maincpu")->bytes();

	decrypt_ac_ff(ROM, size);
}

void spg2xx_game_ordentv_state::init_ordentv()
{
	// the game will die by jumping to an infinite loop if this check fails, what is it checking?
	uint16_t* rom = (uint16_t*)memregion("maincpu")->base();
	rom[0x4fef8] = 0xee07;
}

void spg2xx_game_ordentv_state::init_jeuint()
{
	// the game will die by jumping to an infinite loop if this check fails, what is it checking?
	uint16_t* rom = (uint16_t*)memregion("maincpu")->base();
	rom[0x53376] = 0xee07;
}

void spg2xx_game_state::init_itvphone()
{
	// the game will die by jumping to an infinite loop if this check fails, what is it checking?
	uint16_t* rom = (uint16_t*)memregion("maincpu")->base();
	rom[0xf152] = 0xee08;
}

// year, name, parent, compat, machine, input, class, init, company, fullname, flags

// Radica TV games
CONS( 2006, rad_skat,   0,        0, rad_skat,  rad_skat,  spg2xx_game_state,          init_crc,      "Radica",                                                 "Play TV Skateboarder (NTSC)",                                           MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2006, rad_skatp,  rad_skat, 0, rad_skatp, rad_skatp, spg2xx_game_state,          init_crc,      "Radica",                                                 "Connectv Skateboarder (PAL)",                                           MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2006, rad_crik,   0,        0, rad_crik,  rad_crik,  spg2xx_game_state,          init_crc,      "Radica",                                                 "Connectv Cricket (PAL)",                                                MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) // Version 3.00 20/03/06 is listed in INTERNAL TEST

CONS( 2007, rad_fb2,    0,        0, rad_skat,  rad_fb2,   spg2xx_game_state,          init_crc,      "Radica",                                                 "Play TV Football 2",                                                    MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) // offers a 2 player option in menus, but seems to have only been programmed for, and released as, a single player unit, P2 controls appear unfinished.

// this is 'voice activated' and one of the other globs is likely doing the voice processing
CONS( 2006, rad_jib,    0,        0, spg28x,    spg2xx,   spg2xx_game_state,          init_crc,      "Radica",                                                 "Jibbi",                                                                 MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING )

// ABL TV Games
CONS( 2006, abltenni,   0,        0, spg2xx,    abltenni,  spg2xx_game_state,          empty_init,    "Advance Bright Ltd / V-Tac Technology Co Ltd.",          "Wireless Tennis (WT2000, ABL TV Game)",                                 MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2006, ablkickb,   0,        0, ablkickb,  ablkickb,  spg2xx_game_albkickb_state, init_ablkickb, "Advance Bright Ltd / Coleco / V-Tac Technology Co Ltd.", "Kick Boxing (BJ8888, ABL TV Game)",                                     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // 4 motion sensors, one for each limb

CONS( 2007, lxspidaj,   0,        0, spg2xx_pal,lxspidaj,  spg2xx_game_albkickb_state, init_ablkickb, "Lexibook",                                               "Spider-Man Super TV Air Jet (Lexibook Junior, JG6000SP)",               MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2007, lxairjet,   0,        0, spg2xx_pal,lxspidaj,  spg2xx_game_albkickb_state, init_ablkickb, "Lexibook",                                               "Super TV Air Jet 6-in-1 (Lexibook Junior)",                             MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2006, totspies,   0,        0, spg2xx_pal,totspies,  spg2xx_game_state,          empty_init,    "Senario / Marathon - Mystery Animation Inc.",            "Totally Spies! (France)",                                               MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2006, fordrace,   0,        0, fordrace,  fordrace,  spg2xx_game_fordrace_state, empty_init,    "Excalibur Electronics",                                  "Ford Racing",                                                           MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 200?, carled99,   0,        0, spg2xx,    carled99,  spg2xx_game_state,          empty_init,    "Excalibur Electronics",                                  "Carl Edwards 99",                                                       MACHINE_IMPERFECT_SOUND )

CONS( 2008, comil,      0,        0, comil,     comil,     spg2xx_game_comil_state,    empty_init,    "Character Options",                                      "Who Wants to Be a Millionaire? (Character Options, Plug and Play, UK)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// Same as Excalibur Decathlon? Not identical to the ABL game below, but built on the same engine
CONS( 2006, tvsprt10,   0,        0, tvsprt10,  tvsprt10,  spg2xx_game_state,          init_tvsprt10, "Simba / V-Tac Technology Co Ltd.",                       "TV Sports 10-in-1 / Decathlon Athletic Sport Games",                    MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 200?, decathln,   0,        0, tvsprt10,  decathln,  spg2xx_game_state,          init_tvsprt10, "Advance Bright Ltd / V-Tac Technology Co Ltd.",          "Decathlon (set 1)",                                                     MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // unit found in Spain
CONS( 200?, decathlna,  decathln, 0, tvsprt10,  decathln,  spg2xx_game_state,          init_tvsprt10, "Advance Bright Ltd / V-Tac Technology Co Ltd.",          "Decathlon (set 2, SM570, ABL TV Game)",                                 MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // unit found in UK

CONS( 2007, guitarfv,   0,        0, guitarfv,  guitarfv,  spg2xx_game_state,          empty_init,    "Advance Bright Ltd",                                     "Guitar Fever (2007.07.03 Ver 2.7)",                                     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// The box for these has 'YOU take the stage' text, but unlike the sequel, it is not part of the ingame title screen, this sometimes causes confusion
CONS( 200?, guitarss,   0,        0, spg28x,    guitarss,  spg2xx_game_state,          empty_init,    "Senario",                                                "Guitar Super Star ('Fender Stratocaster' style)",                       MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 200?, guitarssa,  guitarss, 0, spg28x,    guitarss,  spg2xx_game_state,          empty_init,    "Senario",                                                "Guitar Super Star (red 'Gibson Flying V' style)",                       MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// The sequel has 'You Take The Stage' on both the box and title screen
CONS( 2009, gssytts,    0,        0, gssytts,   guitarss,  spg2xx_game_gssytts_state,  empty_init,    "Senario",                                                "Guitar Super Star: You Take The Stage",                                 MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2007, drumsups,   0,        0, spg28x,    drumsups,  spg2xx_game_state,          empty_init,    "Senario",                                                "Drum Super Star",                                                       MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2009, senwfit,    0,        0, gssytts,   senwfit,   spg2xx_game_senwfit_state,  init_senwfit,  "Senario",                                                "Wireless Fitness / Dance Fit (Senario)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// VTech "TV Station" / "TV Learning Station" / "Nitro Vision"
CONS( 2006, vtechtvssp, 0,        0, spg2xx,    spg2xx,    spg2xx_game_state,          empty_init,    "VTech",                                                  "TV Station (VTech, Spain)",                                             MACHINE_NOT_WORKING )
CONS( 2006, vtechtvsgr, 0,        0, spg2xx,    spg2xx,    spg2xx_game_state,          empty_init,    "VTech",                                                  "TV Learning Station (VTech, Germany)",                                  MACHINE_NOT_WORKING )

CONS( 2007, itvphone,   0,        0, spg2xx_pal, itvphone, spg2xx_game_state,          init_itvphone, "Taikee / Oregon Scientific / V-Tac Technology Co Ltd.",  u8"Teléfono interactivo de TV (Spain)",                                  MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// "Boots's" is used on the title screen and in the manual, even if "Boots'" is usually used outside of this game.
CONS( 2006, doraphon,   0,        0, doraphone, doraphone, spg2xx_game_doraphone_state,empty_init,    "VTech",                                                  "Dora the Explorer - Dora TV Explorer Phone / Boots's Special Day (US/UK)",         MACHINE_IMPERFECT_SOUND ) // same ROM confirmed on both US and UK units
CONS( 2006, doraphonf,  doraphon, 0, doraphonep,doraphonep,spg2xx_game_doraphone_state,empty_init,    "VTech",                                                  "Dora the Explorer - Dora TV Explorer Phone / L'anniversaire de Babouche (France)", MACHINE_IMPERFECT_SOUND )
// This was from a 'cost reduced' unit with the 'non-TV' mode switch and internal speaker removed, however it looks like the code was not disabled or removed as the mode is fully functional.
// The ZC-Infinity video for this on YouTube shows the map scrolling to center the continent, there doesn't appear to be an input for this, different revision?
// a Dutch localized version also exists, which again must be different code
CONS( 2007, doraglob,   0,        0, doraphone, doraglobe, spg2xx_game_doraphone_state,empty_init,    "VTech",                                                  "Dora the Explorer - Dora TV Adventure Globe (US)",                      MACHINE_IMPERFECT_SOUND )
CONS( 2007, doraglobuk, doraglob, 0, doraphone, doraglobe, spg2xx_game_doraphone_state,empty_init,    "VTech",                                                  "Dora the Explorer - Dora TV Adventure Globe (UK)",                      MACHINE_IMPERFECT_SOUND )
CONS( 2007, doraglobf,  doraglob, 0, doraphone, doraglobe, spg2xx_game_doraphone_state,empty_init,    "VTech",                                                  "Dora the Explorer - Dora TV Globe-Trotter (France)",                    MACHINE_IMPERFECT_SOUND )
CONS( 2007, doraglobg,  doraglob, 0, doraphone, doraglobe, spg2xx_game_doraphone_state,empty_init,    "VTech",                                                  "Dora the Explorer - Doras Abenteuer-Globus (Germany)",                  MACHINE_IMPERFECT_SOUND )


// ROM checksum fails, but is expecting 0 as a result? shows 'CopyRight' when booting normally? protection?
CONS( 200?, jouet, 0,             0, spg2xx,    spg2xx,    spg2xx_game_state,          empty_init,    "<unknown>",                                              "10 Jeux Interactifs / Jeux Pour Filles (France)",                       MACHINE_NOT_WORKING )

CONS( 2008, senspeed,  0,         0, senspeed,  senspeed,  spg2xx_game_senspeed_state, empty_init,    "Senario",                                                "Speed Racer (Senario)",                                                 MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 200?, jjstrip,    0,        0, tvsprt10,  jjstrip,   spg2xx_game_state,          empty_init,    "Shiggles Inc.",                                          "Club Jenna Presents: Jenna Jameson's Strip Poker",                      MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2005, tmntbftc,   0,        0, spg2xx,    tmntbftc,  spg2xx_game_state,          empty_init,    "Tech2Go / WayForward",                                   "Teenage Mutant Ninja Turtles: Battle for the City",                     MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// The black flashing square on startup is part of the tilemap layer, it doesn't appear to happen on hardware
// P.L.U.G.G.U.H.S. = Play Lots of Unbelievable Games, Getting Ultra High Scores
CONS( 2005, knd,        0,        0, spg2xx,    knd,       spg2xx_game_state,          init_crc,      "Tech2Go / One Man Band",                                 "Codename: Kids Next Door - Operation: P.L.U.G.G.U.H.S.",                MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2005, tmntmutm,   0,        0, tmntmutm,  tmntmutm,  spg2xx_game_tmntmutm_state, empty_init,    "Tech2Go / WayForward",                                   "Teenage Mutant Ninja Turtles: Mutant and Monster Mayhem",               MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2006, pballpup,   0,        0, pballpup,  pballpup,  spg2xx_game_pballpup_state, empty_init,    "Hasbro / Tiger Electronics",                             "Mission: Paintball Powered Up",                                         MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2005, mpntbalt,   0,        0, mpntbalt,  mpntbalt,  spg2xx_game_pballpup_state, empty_init,    "Hasbro / Tiger Electronics",                             "Mission: Paintball Trainer",                                            MACHINE_IMPERFECT_SOUND )

CONS( 2004, mpntball,   0,        0, mpntball,  mpntball,  spg2xx_game_pballpup_state, empty_init,    "Hasbro / Tiger Electronics",                             "Mission: Paintball",                                                    MACHINE_IMPERFECT_SOUND )

CONS( 2007, dreamlss,   0,        0, dreamlss,  dreamlss,  spg2xx_game_dreamlss_state, empty_init,    "Hasbro / Tiger Electronics",                             "Dream Life Superstar (Version 0.3, Mar 16 2007)",                       MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// Needs a hack to not show garbage sprite on startup
CONS( 2008, swclone,    0,        0, swclone,   swclone,   spg2xx_game_swclone_state,  init_swclone,  "Hasbro / Tiger Electronics",                             "Star Wars: The Clone Wars - Clone Trooper Blaster Game",                MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// Mattel games
CONS( 2005, mattelcs,   0,        0, rad_skat,  mattelcs,  spg2xx_game_state,          empty_init,    "Mattel",                                                 "Mattel Classic Sports",                                                 MACHINE_IMPERFECT_SOUND )

CONS( 2005, hotwhls,    0,        0, spg2xx,    hotwhls,   spg2xx_game_state,          empty_init,    "Mattel",                                                 "Hot Wheels (steering wheel controller)",                                MACHINE_IMPERFECT_SOUND )

CONS( 2006, hotwhl2p,   0,        0, hotwheels, hotwheels, spg2xx_game_hotwheels_state,empty_init,    "Mattel",                                                 "Hot Wheels (2 player, pad controllers)",                                MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2006, rocksock,   0,        0, spg2xx,    rocksock,  spg2xx_game_state,          empty_init,    "Mattel",                                                 "Rock 'Em Sock 'Em Robots (TV Game)",                                    MACHINE_IMPERFECT_SOUND )

// there was also an English release of this, simply titled "Interactive TV Computer"
CONS( 2007, ordentv,    0,        0, ordentv,   ordentv,   spg2xx_game_ordentv_state,  init_ordentv,  "Taikee / V-Tac",                                         "Ordenador-TV (Spain)",                                                  MACHINE_NOT_WORKING )
CONS( 2007, jeuint,     ordentv,  0, ordentv,   ordentv,   spg2xx_game_ordentv_state,  init_jeuint,   "Taikee / V-Tac",                                         u8"Jeu Intéractif TV (France)",                                          MACHINE_NOT_WORKING)

CONS( 200?, wfart,      0,        0, wfcentro,  spg2xx,    spg2xx_game_wfcentro_state, empty_init,    "WinFun",                                                 "TV Art Design Center",                                                  MACHINE_NOT_WORKING )
CONS( 200?, wfcentro,   wfart,    0, wfcentro,  spg2xx,    spg2xx_game_wfcentro_state, empty_init,    "WinFun",                                                 "Centro TV de Diseno Artistico (Spain)",                                 MACHINE_NOT_WORKING )

CONS( 200?, lexiart,    0,        0, lexiart,   lexiart,   spg2xx_game_lexiart_state,  empty_init,    "Lexibook",                                               "Lexibook Junior My 1st Drawing Studio",                                 MACHINE_NOT_WORKING )

CONS( 200?, lexibds,    0,        0, spg2xx,    spg2xx,    spg2xx_game_state,          empty_init,    "Lexibook",                                               "Lexibook Junior Barbie Drawing Board / Barbie Drawing Studio",          MACHINE_NOT_WORKING )

// set 2862 to 0003 (irq enable) when it stalls on boot to show something (doesn't turn on IRQs again otherwise?) needs camera emulating
CONS( 200?, tiktokmm,   0,        0, spg2xx,    spg2xx,    spg2xx_game_wfcentro_state, empty_init,    "TikTokTech Ltd. / 3T Games / Senario",                   "Moving Music (MM-TV110)",                                               MACHINE_NOT_WORKING )

CONS( 2005, doyousud,   0,        0, spg2xx,    doyousud,  spg2xx_game_state,          empty_init,    "SDW Games",                                              "Sudoku: Do You Sudoku?",                                                MACHINE_NOT_WORKING )

CONS( 200?, virtbb,     0,        0, spg2xx,    virtbb,    spg2xx_game_state,          empty_init,    "VTG Interactive",                                        "Virtual Baseball (VTG)",                                                MACHINE_NOT_WORKING ) // motion controls not fully understood
CONS( 200?, virtten,    0,        0, spg2xx,    virtten,   spg2xx_game_state,          empty_init,    "VTG Interactive",                                        "Virtual Tennis (VTG)",                                                  MACHINE_NOT_WORKING ) // motion controls not fully understood

// 2007 ingame, 2008 on box.  Hyperkin is mentioned as being the registered trademark holder alongside DDRGame on the box.
// Songs "composed by Kenneth Baylon"
CONS( 2008, ddr33v,     0,        0, spg2xx,    ddr33v,    spg2xx_game_ddr33v_state,   init_ddr33v,   "DDRGame / Hyperkin",                                    "16-bit TV Dance Pad with 15 songs / Dance Dance Party Mix (DDRGame)",   MACHINE_IMPERFECT_SOUND )

// PCB has 'Anpanman TV 2006 Ver 1.4' printed on it, ROM has SPG260 header.  Uses custom built-in keyboard, no display built into the unit.
CONS( 2006, anpantv,    0,        0, spg2xx,    spg2xx,    spg2xx_game_state,          empty_init,    "Bandai",                                                "Anpanman TV (Japan)",                                                   MACHINE_NOT_WORKING )

// Has an AT24C08, not currently hooked up (probably for storing database unlocks)
//
// There is also a card reader/scanner which can read barcodes from Digimon cards
// and IR connectivity which allowed for data exchange with various services using
// an external device, including transfering characters to/from an arcade game.
// Neither is currently emulated
//
// Will report 'ERROR' sometimes, maybe as a result of these not being hooked up.
CONS( 2006, dmbtjunc,   0,        0, spg2xx,    dmbtjunc,  spg2xx_game_state,          empty_init,    "Bandai",                                                "Let's! TV Play Digital Monster Battle Junction (Japan)",                MACHINE_NOT_WORKING )

// Let's!TVプレイ 脳と体を鍛える 体感頭脳ファミリーマットレ  - Let's! TV Play branding appears on the box
CONS( 2006, ban_krkk,   0,        0, spg2xx,    ban_krkk,  spg2xx_game_state,          init_crc,      "Bandai",                                                "Let's! TV Play Nou to Karada o Kitaeru Taikan Zunou Family Mattore (Japan)", MACHINE_IMPERFECT_SOUND )

// Let's!TVプレイ チームたいこー! カードでパワーUP! たまごっちスクール選手権 - has IR for optional connectivity with external Tamagotchi device, and a card scanner used for gameplay
CONS( 2007, ban_tam2,   0,        0, spg2xx,    spg2xx,    spg2xx_game_state,          init_crc,      "Bandai",                                                "Let's! TV Play Team Taikou! Card de Power Up! Tamagotchi School Senshuken (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

CONS( 2007, epo_tetr,   0,        0, epo_tetr,  epo_tetr,  epo_tetr_game_state,        empty_init,    "Epoch",                                                 "Minna no Tetris (Japan)", MACHINE_IMPERFECT_SOUND )

// Train Game V1.4 2012-08-15 on PCB. SPG243 headers in each chunk.
// Last few bytes of SEEPROM have 'JUNGT' in them, is this developed by JungleSoft/JungleTac?
CONS( 2012, prail,      0,        0, prail,     prail,     spg2xx_game_prail_state,    empty_init,    "Takara Tomy",                                           "Boku wa Plarail Untenshi - Shinkansen de Ikou! (Japan)", MACHINE_IMPERFECT_SOUND )
// the 'plus' version from 2015 runs on newer hardware, see generalplus_gpl16250_spi.cpp

CONS( 2007, wordlnch,   0,        0, spg2xx,    wordlnch,  spg2xx_game_state,          empty_init,    "LeapFrog",                                              "Word Launch (UK)", MACHINE_NOT_WORKING ) // seems to have a PAL/NTSC flag so US ROM might be the same

CONS( 2005, whacmole,   0,        0, whacmole,  whacmole,  spg2xx_game_hasbro_93lc66_state, empty_init, "Hasbro / Milton Bradley",                             "Whac-A-Mole (TV Game)", MACHINE_IMPERFECT_SOUND )

CONS( 2005, backybbs,   0,        0, whacmole,  backybbs,  spg2xx_game_hasbro_93lc66_state, empty_init, "Hasbro / Milton Bradley / Atari",                     "Backyard Baseball & Soccer", MACHINE_IMPERFECT_SOUND )

CONS( 2006, mylpony,    0,        0, mylpony,   mylpony,   spg2xx_game_hasbro_93lc66_state, empty_init, "Hasbro / Milton Bradley",                             "My Little Pony - Grand Puzzleventure", MACHINE_IMPERFECT_SOUND )

CONS( 2007, lpetshop,   0,        0, lpetshop,  lpetshop,  spg2xx_game_lpetshop_state,      empty_init, "Hasbro",                                              "Littlest Pet Shop", MACHINE_IMPERFECT_SOUND )

CONS( 2005, barbpet,    0,        0, spg2xx,    barbpet,   spg2xx_game_state,               empty_init, "Mattel",                                              "Barbie: I Love Pets - Pet Rescue", MACHINE_IMPERFECT_SOUND )

CONS( 200?, smartcyc,   0,        0, smartcycle, smartcyc, spg2xx_game_smartcycle_state,    empty_init, "Fisher Price",                                        "Smart Cycle", MACHINE_NOT_WORKING )

CONS( 2004, spidm2,     0,        0, spg2xx,     spidm2,   spg2xx_game_state,               empty_init, "N-Vision",                                            "Spider-Man 2 Web Action", MACHINE_IMPERFECT_SOUND )

CONS( 2004, dinothun,   0,        0, spg2xx,    spg2xx,    spg2xx_game_state,               empty_init, "N-Vision / Toy Quest",                                "Power Rangers Dino Thunder: Thunder Action", MACHINE_NOT_WORKING )

// this Japan version uses different banking to spg2xx_pdc.cpp so is in here instead
CONS( 2006, pdcj,       0,        0, pdcj,      pdcj,      spg2xx_game_pdcj_state,          empty_init, "Conny / Takara",                                      "PDC - Pocket Dream Console (Japan)", MACHINE_IMPERFECT_SOUND )



spg2xx_digimake.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

// Digi Makeover - this uses a camera and touchpad, both currently unsupported
//  - why do we need a hack to boot?

#include "emu.h"
#include "spg2xx.h"
#include "machine/nvram.h"


namespace {

class spg2xx_game_digimake_state : public spg2xx_game_state
{
public:
	spg2xx_game_digimake_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_state(mconfig, type, tag)
	{ }

	void digimake(machine_config &config);
	void mem_map_digi(address_map &map) ATTR_COLD;

private:
	virtual void portc_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
};

void spg2xx_game_digimake_state::mem_map_digi(address_map &map)
{
	map(0x000000, 0x1fffff).bankr("cartbank");
	// there is also a write/read on 37FFFF before anything is configured as RAM
	map(0x380000, 0x3fffff).ram();
}
static INPUT_PORTS_START( rad_digi ) // some of the inputs seem to act like buttons, others are positional touchpad?
	PORT_START("P1")
	PORT_DIPNAME( 0x0001, 0x0001, "Used 1" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, "1" )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, "Used 2" )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("6")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("5")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("4")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("3")
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("2")
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("1")
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P2")
	PORT_DIPNAME( 0x0001, 0x0001, "2" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P3")
	PORT_DIPNAME( 0x0001, 0x0001, "3" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, "Save" )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, "Something" )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, "Touch?" )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, "Forward" )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, "Back?" )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
INPUT_PORTS_END

void spg2xx_game_digimake_state::digimake(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_digimake_state::mem_map_digi);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(spg2xx_game_digimake_state::base_porta_r));
	m_maincpu->portb_in().set(FUNC(spg2xx_game_digimake_state::base_portb_r));
	m_maincpu->portc_in().set(FUNC(spg2xx_game_digimake_state::base_portc_r));

	m_maincpu->portc_out().set(FUNC(spg2xx_game_digimake_state::portc_w));

}

void spg2xx_game_digimake_state::portc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: portc_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
		(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
		(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
		(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
		(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
		(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
		(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
		(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
		(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
		(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
		(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
		(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
		(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
		(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
		(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
		(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
		(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');

	// HACK! force vbl interrupt to be on, I'm guessing some check is failing causing it to remain off otherwise?
	if (m_maincpu->pc() == 0xaf5a)
	{
		address_space& mem = m_maincpu->space(AS_PROGRAM);
		mem.write_word(0x2862, 0x0003);
	}
}

ROM_START( rad_digi )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "digimakeover.bin", 0x000000, 0x400000, CRC(bbe60bc2) SHA1(fb0c96d1f35af85d6d0fa76c390e42e2eda301ae) )
ROM_END

} // anonymous namespace


CONS( 2005, rad_digi,  0,        0, digimake, rad_digi,   spg2xx_game_digimake_state, init_crc, "Radica", "Digi Makeover (Girl Tech)",   MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING )



spg2xx_dreamlife.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood
/*
    TODO:
        Service mode inputs for Giga Pets Explorer
        Dump and emulate Giga Pets handheld game units
*/

#include "emu.h"
#include "spg2xx.h"

#include "machine/eepromser.h"


namespace {

class dreamlif_state : public spg2xx_game_state
{
public:
	dreamlif_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_state(mconfig, type, tag),
		m_eeprom(*this, "eeprom")
	{ }

	void dreamlif(machine_config &config);

private:
	uint16_t portb_r();
	virtual void portb_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;

	required_device<eeprom_serial_93cxx_device> m_eeprom;
};


uint16_t dreamlif_state::portb_r()
{
	uint16_t ret = 0x0000;
	logerror("%s: portb_r\n", machine().describe_context());
	ret |= m_eeprom->do_read() << 3;
	return ret;
}

void dreamlif_state::portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: portb_w (%04x)\n", machine().describe_context(), data);
	m_eeprom->di_write(BIT(data, 2));
	m_eeprom->cs_write(BIT(data, 0) ? ASSERT_LINE : CLEAR_LINE);
	m_eeprom->clk_write(BIT(data, 1) ? ASSERT_LINE : CLEAR_LINE);
}



static INPUT_PORTS_START( dreamlif )
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("A")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("B")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("C")
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Yes")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("No")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // must be low or the Tiger logo gets skipped, also must be low for service mode (hold pause while booting) to work
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_NAME("Pause")
	PORT_BIT( 0xf000, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( dsgnwrld )
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Select")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Cancel")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Menu")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // must be low or the Tiger logo gets skipped, also must be low for service mode (hold pause while booting) to work
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_NAME("Pause")
	PORT_BIT( 0xf000, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( gigapets )
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Select")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Back") // PCB marking is "Cancel"
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Menu")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	// TODO: Likely handheld game unit inputs
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON4 )
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_BUTTON5 )
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_BUTTON6 )
	PORT_BIT( 0xc000, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

void dreamlif_state::dreamlif(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &dreamlif_state::mem_map_4m);

	spg2xx_base(config);

	EEPROM_93C66_16BIT(config, m_eeprom); // HT93LC66A

	m_maincpu->porta_in().set_ioport("P1");
	m_maincpu->portb_in().set(FUNC(dreamlif_state::portb_r));
	m_maincpu->portb_out().set(FUNC(dreamlif_state::portb_w));

}

ROM_START( dreamlif )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "dreamlife.bin", 0x000000, 0x800000, CRC(632e0237) SHA1(a8586e8a626d75cf7782f13cfd9f1b938af23d56) )
ROM_END

ROM_START( dsgnwrld )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "designersworld.bin", 0x000000, 0x800000, CRC(b3987161) SHA1(f7d03b172fd0accc6370d9ccc340b3aa6317426f) )
ROM_END

// PCB Markings: 3059L-R3 0626 / 3059TV-R3B / 3059R-R1 0626
// Dumped as K8D6X16UTM
ROM_START( gigapets )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "rom.u7", 0x000000, 0x800000, CRC(beacc99c) SHA1(d6e3ed69e297282b9d85661a7468e0c0a6815d31) )
ROM_END

} // anonymous namespace


// Hasbro games
CONS( 2005, dreamlif,  0,        0, dreamlif, dreamlif,   dreamlif_state, empty_init, "Hasbro", "Dream Life (Version 1.0, Feb 07 2005)",  MACHINE_IMPERFECT_SOUND )
CONS( 2005, dsgnwrld,  0,        0, dreamlif, dsgnwrld,   dreamlif_state, empty_init, "Hasbro", "Designer's World (Version 1.0, Dec 20 2005)",  MACHINE_IMPERFECT_SOUND )
CONS( 2006, gigapets,  0,        0, dreamlif, gigapets,   dreamlif_state, empty_init, "Hasbro", "Giga Pets Explorer (Version 1.34.1, Mar 17 2006)",  MACHINE_IMPERFECT_SOUND )



spg2xx_ican.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

/* Fisher Price / Mattel "I Can..." systems */

#include "emu.h"
#include "spg2xx.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "softlist_dev.h"


namespace {

class icanguit_state : public spg2xx_game_state
{
public:
	icanguit_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_state(mconfig, type, tag),
		m_cart(*this, "cartslot"),
		m_cart_region(nullptr),
		m_porta_in(*this, "P1_%u", 0U),
		m_portc_in(*this, "P3_%u", 0U)
	{ }

	void icanguit(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load_icanguit);

	uint16_t porta_r();
	virtual uint16_t portb_r();
	uint16_t portc_r();

	virtual void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
	virtual void portb_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
	virtual void portc_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;

	required_device<generic_slot_device> m_cart;
	memory_region *m_cart_region;

	uint16_t m_inlatch_a = 0;
	uint16_t m_inlatch_c = 0;
	optional_ioport_array<6> m_porta_in;
	optional_ioport_array<6> m_portc_in;

};

class icanpian_state : public icanguit_state
{
public:
	icanpian_state(const machine_config &mconfig, device_type type, const char *tag) :
		icanguit_state(mconfig, type, tag)
	//  m_eeprom(*this, "eeprom")
	{ }

	void icanpian(machine_config &config);

protected:
	virtual uint16_t portb_r() override;

	virtual void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
	virtual void portb_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;

	//optional_device<eeprom_serial_93cxx_device> m_eeprom;
};


// there is a speaker volume for the 'guitar' mode, but it's presumably an analog feature, not read by the game.
static INPUT_PORTS_START( icanguit )
	PORT_START("P1")
	// uses multiplexed ports instead, see below

	PORT_START("P1_0")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Fret 1, Row 1")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_NAME("Fret 2, Row 1")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH2) PORT_NAME("Fret 3, Row 1")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_NAME("Fret 4, Row 1")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_NAME("Fret 5, Row 1")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_NAME("Fret 6, Row 1") // Frets 6-12 only have 2 rows (1 and 6)
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_NAME("Fret 9, Row 1")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_NAME("Fret 10, Row 1")
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_NAME("Fret 11, Row 1")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_NAME("Fret 12, Row 1")
	PORT_BIT( 0xfc00, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P1_1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_NAME("Fret 1, Row 2")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_NAME("Fret 2, Row 2")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_NAME("Fret 3, Row 2")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_NAME("Fret 4, Row 2")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_NAME("Fret 5, Row 2")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_NAME("Fret 7, Row 1")
	PORT_BIT( 0xffc0, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P1_2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_NAME("Fret 1, Row 3")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_NAME("Fret 2, Row 3")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_NAME("Fret 3, Row 3")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_NAME("Fret 4, Row 3")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_NAME("Fret 5, Row 3")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_NAME("Fret 8, Row 1")
	PORT_BIT( 0xffc0, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P1_3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_NAME("Fret 1, Row 4")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_NAME("Fret 2, Row 4")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_NAME("Fret 3, Row 4")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_NAME("Fret 4, Row 4")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_NAME("Fret 5, Row 4")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_NAME("Fret 8, Row 6")
	PORT_BIT( 0xffc0, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P1_4")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_NAME("Fret 1, Row 5")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_NAME("Fret 2, Row 5")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_NAME("Fret 3, Row 5")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_NAME("Fret 4, Row 5")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_NAME("Fret 5, Row 5")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_NAME("Fret 7, Row 6")
	PORT_BIT( 0xffc0, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P1_5")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_NAME("Fret 1, Row 6")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_NAME("Fret 2, Row 6")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_NAME("Fret 3, Row 6")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_NAME("Fret 4, Row 6")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_NAME("Fret 5, Row 6")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RSHIFT) PORT_NAME("Fret 6, Row 6") // Frets 6-12 only have 2 rows (1 and 6)
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_NAME("Fret 9, Row 6")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_NAME("Fret 10, Row 6")
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_NAME("Fret 11, Row 6")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_NAME("Fret 12, Row 6")
	PORT_BIT( 0xfc00, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // might be some kind of seeprom in here?

	PORT_START("P3")
	// uses multiplexed ports instead, see below

	PORT_START("P3_0")
	PORT_BIT( 0x0007, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("String 1") // these seem to respond on release, but are definitely active high based on visual indicators
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("String 2")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("String 3")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("String 4")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("String 5")
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("String 6")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_DIPNAME( 0x0400, 0x0000, "TV or Guitar Mode" )
	PORT_DIPSETTING(      0x0000, "TV Mode" )
	PORT_DIPSETTING(      0x0400, "Guitar Mode" )
	PORT_BIT( 0xf800, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P3_1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0xfffc, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P3_2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Home")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Enter")
	PORT_BIT( 0xfffc, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P3_3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Pause")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0xfffc, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P3_4")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) // doesn't highlight during menus, but changes sound in 'Guitar Mode' and switches between levels after selecting song
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) // doesn't highlight during menus, but changes sound in 'Guitar Mode' and switches between levels after selecting song
	PORT_BIT( 0xfff8, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P3_5")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("Whammy Up")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("Whammy Down")
	PORT_BIT( 0xfff8, IP_ACTIVE_HIGH, IPT_UNKNOWN )
INPUT_PORTS_END

// this has an entire piano keyboard + extras
// there is a volume dial for the internal speakers when used in non-TV mode, but presumably it is not CPU visible
// there should be a metronome key, but nothing seems to have that effect, maybe due to incomplete sound emulation?
static INPUT_PORTS_START( icanpian )
	PORT_START("P1")
	// uses multiplexed ports instead, see below

	PORT_START("P1_0")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH2)PORT_NAME("Octave 0 F (Green)")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)         PORT_NAME("Octave 0 F# (Purple)")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)         PORT_NAME("Octave 0 G (Yellow)")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)         PORT_NAME("Octave 0 G# (Dark Blue)")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)         PORT_NAME("Octave 0 A (Flesh)")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)         PORT_NAME("Octave 0 A# (Dark Green)")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)         PORT_NAME("Octave 0 B (Pink)")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)         PORT_NAME("Octave 0 C (White)")
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)         PORT_NAME("Octave 0 C# (Black)")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)         PORT_NAME("Octave 0 D (Blue)")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)         PORT_NAME("Octave 0 D# (Red)")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)         PORT_NAME("Octave 0 E (Orange)")
	PORT_BIT( 0xf000, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P1_1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)         PORT_NAME("Octave 1 F (Green)")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)         PORT_NAME("Octave 1 F# (Purple)")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA)     PORT_NAME("Octave 1 G (Yellow)")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)         PORT_NAME("Octave 1 G# (Dark Blue)")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)      PORT_NAME("Octave 1 A (Flesh)")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)     PORT_NAME("Octave 1 A# (Dark Green)")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)         PORT_NAME("Octave 1 B (Pink)")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)         PORT_NAME("Octave 1 C (White)")
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)         PORT_NAME("Octave 1 C# (Black)")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)         PORT_NAME("Octave 1 D (Blue)")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)         PORT_NAME("Octave 1 D# (Red)")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)         PORT_NAME("Octave 1 E (Orange)")
	PORT_BIT( 0xf000, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P1_2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)         PORT_NAME("Octave 2 F (Green)")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6)         PORT_NAME("Octave 2 F# (Purple)")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)         PORT_NAME("Octave 2 G (Yellow)")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7)         PORT_NAME("Octave 2 G# (Dark Blue)")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)         PORT_NAME("Octave 2 A (Flesh)")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8)         PORT_NAME("Octave 2 A# (Dark Green)")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)         PORT_NAME("Octave 2 B (Pink)")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)         PORT_NAME("Octave 2 C (White)")
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0)         PORT_NAME("Octave 2 C# (Black)")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)         PORT_NAME("Octave 2 D (Blue)")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)     PORT_NAME("Octave 2 D# (Red)")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_NAME("Octave 2 E (Orange)")
	PORT_BIT( 0xf000, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // might be some kind of seeprom in here? (or not? only I Can Play Guitar seems to offer a 'resume', something does get accessed on startup tho? and the machine tells you 'high scores')

	PORT_START("P3")
	// uses multiplexed ports instead, see below

	PORT_START("P3_0")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Change Instrument")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Cycle Hands")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Display Mode 1")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Display Mode 2")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Display Mode 3")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Display Mode 4")
	PORT_BIT( 0x07c0, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // unused?
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // presumably power / low battery, kils the game
	PORT_BIT( 0xf000, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P3_1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD)  PORT_NAME("Tempo Up")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ASTERISK)  PORT_NAME("Tempo Default")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("Tempo Down")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7_PAD)     PORT_NAME("Pause")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8_PAD)     PORT_NAME("Metronome")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNUSED ) // will skip intro scenes etc. like other buttons but no more physical buttons on KB to map here
	PORT_BIT( 0xffc0, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P3_2") // the system ALWAYS requires a cartridge, but has 2 modes of operation depending on a switch.  The only way to use it as a normal keyboard is by flipping this switch.
	PORT_DIPNAME( 0x0001, 0x0000, "System Mode" ) // or implement this as a toggle key? (it's a slider switch)
	PORT_DIPSETTING(      0x0001, "Keyboard Mode (no TV output)" )
	PORT_DIPSETTING(      0x0000, "TV Mode" )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )                       PORT_NAME("Scroll Up")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )                     PORT_NAME("Scroll Down")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_BUTTON1 )                           PORT_NAME("Enter")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON2 )                           PORT_NAME("Home")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNUSED ) // will skip intro scenes etc. like other buttons but no more physical buttons on KB to map here
	PORT_BIT( 0xffc0, IP_ACTIVE_HIGH, IPT_UNKNOWN )
INPUT_PORTS_END




uint16_t icanguit_state::porta_r()
{
	logerror("%s: porta_r\n", machine().describe_context());
	return m_inlatch_a;
}


uint16_t icanguit_state::portc_r()
{
	logerror("%s: portc_r\n", machine().describe_context());
	return m_inlatch_c;
}


void icanguit_state::portc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: portc_w (%04x)\n", machine().describe_context(), data);
}


uint16_t icanguit_state::portb_r()
{
	logerror("%s: portb_r\n", machine().describe_context());
	return m_io_p2->read();
}

void icanguit_state::portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: portb_w (%04x)\n", machine().describe_context(), data);
}

void icanguit_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	//logerror("%s: porta_w (%04x)\n", machine().describe_context(), data);

	if (data == 0x0000)
	{
		m_inlatch_a = m_inlatch_c = 0x0000;
	}
	else if (data == 0x0400)
	{
		m_inlatch_a = m_porta_in[5]->read();
		m_inlatch_c = m_portc_in[5]->read();
	}
	else if (data == 0x0800)
	{
		m_inlatch_a = m_porta_in[4]->read();
		m_inlatch_c = m_portc_in[4]->read();
	}
	else if (data == 0x1000)
	{
		m_inlatch_a = m_porta_in[3]->read();
		m_inlatch_c = m_portc_in[3]->read();
	}
	else if (data == 0x2000)
	{
		m_inlatch_a = m_porta_in[2]->read();
		m_inlatch_c = m_portc_in[2]->read();
	}
	else if (data == 0x4000)
	{
		m_inlatch_a = m_porta_in[1]->read();
		m_inlatch_c = m_portc_in[1]->read();
	}
	else if (data == 0x8000)
	{
		m_inlatch_a = m_porta_in[0]->read();
		m_inlatch_c = m_portc_in[0]->read();
	}
	else
	{
		logerror("%s: unknown porta_w (%04x)\n", machine().describe_context(), data);
	}
}

// icanpian differences

void icanpian_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (data == 0x0000)
	{
		m_inlatch_a = m_inlatch_c = 0x0000;
	}
	else if (data == 0x1000)
	{
		m_inlatch_a = m_porta_in[2]->read();
		m_inlatch_c = m_portc_in[2]->read();
	}
	else if (data == 0x2000)
	{
		m_inlatch_a = m_porta_in[1]->read();
		m_inlatch_c = m_portc_in[1]->read();
	}
	else if (data == 0x4000)
	{
		m_inlatch_a = m_porta_in[0]->read();
		m_inlatch_c = m_portc_in[0]->read();
	}
	else
	{
		logerror("%s: unknown porta_w (%04x)\n", machine().describe_context(), data);
	}
}

// accesses are made for what appears to be a serial eeprom on port B, very similar to dreamlif, but beyond blanking it at the start it doesn't
// seem to ever be used, maybe it was never added to hardware, or just never used?
uint16_t icanpian_state::portb_r()
{
/*
    uint16_t ret = 0x0000;
    logerror("%s: portbxx_r\n", machine().describe_context());
    ret |= m_eeprom->do_read() ? 0xffff : 0x0000;
    return ret;
*/
	return 0x0000;
}

void icanpian_state::portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
/*
    logerror("%s: portbxx_w (%04x)\n", machine().describe_context(), data);
    m_eeprom->di_write(BIT(data, 2));
    m_eeprom->cs_write(BIT(data, 0) ? ASSERT_LINE : CLEAR_LINE);
    m_eeprom->clk_write(BIT(data, 1) ? ASSERT_LINE : CLEAR_LINE);
*/
}

void icanguit_state::machine_start()
{
	spg2xx_game_state::machine_start();

	// if there's a cart, override the standard banking
	if (m_cart && m_cart->exists())
	{
		std::string region_tag;
		m_cart_region = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());
		m_bank->configure_entries(0, (m_cart_region->bytes() + 0x7fffff) / 0x800000, m_cart_region->base(), 0x800000);
		m_bank->set_entry(0);
	}
}

void icanguit_state::machine_reset()
{
	spg2xx_game_state::machine_reset();
	m_inlatch_a = 0x0000;
	m_inlatch_c = 0x0000;
}


DEVICE_IMAGE_LOAD_MEMBER(icanguit_state::cart_load_icanguit)
{
	uint32_t const size = m_cart->common_get_size("rom");

	if (size < 0x80'0000)
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported cartridge size (must be at least 8M)");

	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}



void icanguit_state::icanguit(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &icanguit_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(icanguit_state::porta_r));
	m_maincpu->porta_out().set(FUNC(icanguit_state::porta_w));
	m_maincpu->portb_in().set(FUNC(icanguit_state::portb_r));
	m_maincpu->portb_out().set(FUNC(icanguit_state::portb_w));
	m_maincpu->portc_in().set(FUNC(icanguit_state::portc_r));
	m_maincpu->portc_out().set(FUNC(icanguit_state::portc_w));


	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "icanguit_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(icanguit_state::cart_load_icanguit));
	m_cart->set_must_be_loaded(true);

	SOFTWARE_LIST(config, "icanguit_cart").set_original("icanguit");
}

void icanpian_state::icanpian(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &icanpian_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(icanpian_state::porta_r));
	m_maincpu->porta_out().set(FUNC(icanpian_state::porta_w));
	m_maincpu->portb_in().set(FUNC(icanpian_state::portb_r));
	m_maincpu->portb_out().set(FUNC(icanpian_state::portb_w));
	m_maincpu->portc_in().set(FUNC(icanpian_state::portc_r));
	m_maincpu->portc_out().set(FUNC(icanpian_state::portc_w));

//  EEPROM_93C66_16BIT(config, m_eeprom); // unknown part
//  m_eeprom->erase_time(attotime::from_usec(1));
//  m_eeprom->write_time(attotime::from_usec(1));

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "icanpian_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(icanpian_state::cart_load_icanguit));
	m_cart->set_must_be_loaded(true);

	SOFTWARE_LIST(config, "icanpian_cart").set_original("icanpian");
}

ROM_START( icanguit )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	// no internal ROM, requires a cartridge
ROM_END

ROM_START( icanpian )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	// no internal ROM, requires a cartridge
ROM_END

} // anonymous namespace


// Fisher-Price games
CONS( 2007, icanguit,  0,        0, icanguit, icanguit,   icanguit_state, empty_init, "Fisher-Price", "I Can Play Guitar",     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND ) // how is data saved?
CONS( 2006, icanpian,  0,        0, icanpian, icanpian,   icanpian_state, empty_init, "Fisher-Price", "I Can Play Piano",  MACHINE_IMPERFECT_SOUND ) // 2006 date from Manual




spg2xx_jakks.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

// Mortal Kombat - attract demo almost always picked Johnny Cage vs. Johhny Cage until some unknown factor starts to randomize it
//               - Scorpion's 'get over here' sounds don't decode well

#include "emu.h"
#include "spg2xx.h"
#include "machine/nvram.h"


namespace {

class jakks_state : public spg2xx_game_state
{
public:
	jakks_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_state(mconfig, type, tag)
	{ }

	void base_config(machine_config& config);
	void spg2xx_jakks(machine_config& config);
	void mk(machine_config& config);
	void spg2xx_dpma(machine_config& config);
	void jakks_mpaco(machine_config& config);
	void jakks_mpac(machine_config& config);
	void jakks_rapm(machine_config& config);
	void jakks_sesa(machine_config& config);
	void spg2xx_hmbb(machine_config& config);
	void spg2xx_wof2(machine_config& config);

private:
	void mem_map_2m_mkram(address_map &map) ATTR_COLD;
	void mem_map_hmbb(address_map &map) ATTR_COLD;
	void portc_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;

	u16 wof2_wheel_r()
	{
		// bits 0x0003 are input?
		// read in the 4096hz timer interrupt
		// but the timer interrupt causes glitches on the wheel spin screen
		// is there an interrupt priority issue?
		u16 ret = 0x0003;
		logerror("%s: wof2_wheel_r returning %04x\n", machine().describe_context(), ret);
		return ret;
	}
};


void jakks_state::portc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (BIT(mem_mask, 1))
		if (m_i2cmem) m_i2cmem->write_scl(BIT(data, 1));
	if (BIT(mem_mask, 0))
		if (m_i2cmem) m_i2cmem->write_sda(BIT(data, 0));
}

static INPUT_PORTS_START( batman )
	PORT_START("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1) PORT_NAME("Joypad Up")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1) PORT_NAME("Joypad Down")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1) PORT_NAME("Joypad Left")
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("Joypad Right")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(1) PORT_NAME("A Button")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON2 )        PORT_PLAYER(1) PORT_NAME("Menu")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON3 )        PORT_PLAYER(1) PORT_NAME("B Button")
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON4 )        PORT_PLAYER(1) PORT_NAME("X Button")
	PORT_BIT( 0x00ff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
	PORT_BIT(0xfffe, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( spg2xx_jakks )
	PORT_START("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1) PORT_NAME("Joypad Up")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1) PORT_NAME("Joypad Down")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1) PORT_NAME("Joypad Left")
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("Joypad Right")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(1) PORT_NAME("A Button")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON2 )        PORT_PLAYER(1) PORT_NAME("B Button")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED ) // Button 3 if used
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED ) // Button 4 if used
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Menu / Pause")
	PORT_BIT( 0x000f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
	PORT_BIT( 0x0006, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PAL/NTSC flag, set to NTSC (unverified here)
	PORT_BIT( 0xfff0, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( jak_dond )
	PORT_START("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1) PORT_NAME("Joypad Up")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1) PORT_NAME("Joypad Down")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1) PORT_NAME("Joypad Left")
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("Joypad Right")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("No Deal Button")
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("Deal Button")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("Menu / Pause")
	PORT_BIT( 0x000f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
	PORT_BIT( 0x0006, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PAL/NTSC flag, set to NTSC (unverified here)
	PORT_BIT( 0xfff0, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


static INPUT_PORTS_START( spg2xx_jpdy )
	PORT_INCLUDE( spg2xx_jakks )

	PORT_MODIFY("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_NAME("Joypad Up")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_NAME("Joypad Down")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_NAME("Joypad Left")
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_NAME("Joypad Right")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(1) PORT_NAME("P1 Answer")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(2) PORT_NAME("P2 Answer")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(3) PORT_NAME("P3 Answer")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Menu / Pause")
INPUT_PORTS_END

static INPUT_PORTS_START( jak_spd3 )
	PORT_INCLUDE( spg2xx_jakks )

	PORT_MODIFY("P1")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("C Button")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("Menu / Pause")

INPUT_PORTS_END

static INPUT_PORTS_START( spg2xx_avtr )
	PORT_INCLUDE( spg2xx_jakks )

	PORT_MODIFY("P1")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(1) PORT_NAME("A Button")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON2 )        PORT_PLAYER(1) PORT_NAME("B Button")
INPUT_PORTS_END

static INPUT_PORTS_START( spg2xx_xmen )
	PORT_INCLUDE( spg2xx_jakks )

	PORT_MODIFY("P1")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(1) PORT_NAME("A Button")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON2 )        PORT_PLAYER(1) PORT_NAME("B Button")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON3 )        PORT_PLAYER(1) PORT_NAME("X Button")
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON4 )        PORT_PLAYER(1) PORT_NAME("Menu / Pause")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


static INPUT_PORTS_START( jak_supm )
	PORT_START("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1) PORT_NAME("Joypad Up")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1) PORT_NAME("Joypad Down")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1) PORT_NAME("Joypad Left")
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("Joypad Right")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(1) PORT_NAME("A Button")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON3 )        PORT_PLAYER(1) PORT_NAME("Menu / Pause")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON2 )        PORT_PLAYER(1) PORT_NAME("B Button")
	PORT_BIT( 0x01ff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
	PORT_BIT( 0x0006, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PAL/NTSC flag
	PORT_BIT( 0xfff0, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( spg2xx_spdv )
	PORT_START("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1) PORT_NAME("Joypad Up")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1) PORT_NAME("Joypad Down")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1) PORT_NAME("Joypad Left")
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("Joypad Right")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(1) PORT_NAME("A Button")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON2 )        PORT_PLAYER(1) PORT_NAME("B Button")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Menu / Pause")
	PORT_BIT( 0x00ff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
	PORT_BIT( 0x0006, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PAL/NTSC flag, set to NTSC
	PORT_BIT( 0xfff0, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( spg2xx_dpma )
	PORT_START("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1) PORT_NAME("Joypad Up")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1) PORT_NAME("Joypad Down")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1) PORT_NAME("Joypad Left")
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("Joypad Right")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(1) PORT_NAME("A Button")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Menu / Pause")
	PORT_BIT( 0x000f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x0007, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PAL/NTSC flag, set to NTSC (unverified here)
	PORT_BIT( 0xfff0, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( mk )
	PORT_START("P1")
	PORT_BIT( 0x001f, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON7 )        PORT_PLAYER(1) PORT_NAME("Pause / Menu")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON6 )        PORT_PLAYER(1) PORT_NAME("Block (alt)") // which one of these is actually connected to the button?
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON5 )        PORT_PLAYER(1) PORT_NAME("Block")
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON4 )        PORT_PLAYER(1) PORT_NAME("High Kick")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON3 )        PORT_PLAYER(1) PORT_NAME("High Punch")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON2 )        PORT_PLAYER(1) PORT_NAME("Low Kick")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(1) PORT_NAME("Low Punch")
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("Joypad Right")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1) PORT_NAME("Joypad Left")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1) PORT_NAME("Joypad Down")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1) PORT_NAME("Joypad Up")

	PORT_START("P3") // In addition to the "M/T" pad documented below, PCB also has "P/N" (PAL / NTSC) pad (not read?) and a "F/S" pad (also not read?)
	PORT_BIT( 0x0fff, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_CONFNAME( 0x1000, 0x1000, "Blood" ) // see code at 05EC30 - "M/T" (Mature / Teen?) pad on PCB, set at factory
	PORT_CONFSETTING(      0x0000, "Disabled" )
	PORT_CONFSETTING(      0x1000, "Enabled" )
	PORT_BIT( 0x6000, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_CONFNAME( 0x8000, 0x8000, "Link State" ) // see code at 05EA54
	PORT_CONFSETTING(      0x8000, DEF_STR( Off ) )
	PORT_CONFSETTING(      0x0000, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( jak_mpaco )
	PORT_START("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1)
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1)
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Menu")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON1 )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PAL/NTSC flag, set to NTSC
	PORT_BIT( 0xfff0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("DIALX") // for Pole Position, joystick can be twisted like a dial/wheel (limited?) (check range) (note, range is different to GKR unit)
	PORT_BIT(0x03ff, 0x0000, IPT_AD_STICK_X ) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_MINMAX(0x00,0x03ff)
INPUT_PORTS_END

static INPUT_PORTS_START( spg2xx_sesa )
	PORT_START("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("Red Button")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("Blue Button")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Menu / Pause")
	PORT_BIT( 0x000f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_UNUSED ) // no SEEPROM on this
	PORT_BIT( 0x0006, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // PAL/NTSC flag, set to PAL
	PORT_BIT( 0xfff0, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( jak_mpac )
	PORT_INCLUDE( jak_mpaco )

	PORT_MODIFY("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
INPUT_PORTS_END

static INPUT_PORTS_START( jak_spac )
	PORT_START("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1)
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1)
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Menu")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON1 )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PAL/NTSC flag, set to NTSC
	PORT_BIT( 0xfff0, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


static INPUT_PORTS_START( jak_rapm )
	PORT_INCLUDE( jak_spac )

	PORT_START("DIALX") // for Pole Position, joystick can be twisted like a dial/wheel (limited?) (check range)
	PORT_BIT(0x0fff, 0x0000, IPT_AD_STICK_X ) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_MINMAX(0x00,0x0fff)
INPUT_PORTS_END

static INPUT_PORTS_START( spg2xx_pacg )
	PORT_START("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1) PORT_NAME("Joypad Up")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1) PORT_NAME("Joypad Down")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1) PORT_NAME("Joypad Left")
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("Joypad Right")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(1) PORT_NAME("A Button")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Menu / Pause")
	PORT_BIT( 0x000f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
	PORT_BIT( 0x0006, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PAL/NTSC flag, set to NTSC (unverified here)
	PORT_BIT( 0xfff0, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( spg2xx_gdg )
	PORT_START("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1) PORT_NAME("Joypad Up")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1) PORT_NAME("Joypad Down")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1) PORT_NAME("Joypad Left")
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("Joypad Right")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(1) PORT_NAME("A Button")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Menu / Pause")
	PORT_BIT( 0x000f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_UNUSED ) // no SEEPROM on this
	PORT_BIT( 0x0006, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PAL/NTSC flag, set to NTSC (unverified here)
	PORT_BIT( 0xfff0, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( spg2xx_ntoonsc )
	PORT_START("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Menu / Pause")
	PORT_BIT( 0x000f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
	PORT_BIT( 0x0006, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PAL/NTSC flag, set to NTSC (unverified here)
	PORT_BIT( 0xfff0, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( spg2xx_1vs )
	PORT_INCLUDE( spg2xx_jakks )
	PORT_MODIFY("P1")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON3 )        PORT_PLAYER(1) PORT_NAME("C Button")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Menu / Pause")
INPUT_PORTS_END

void jakks_state::base_config(machine_config& config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(jakks_state::base_porta_r));
	m_maincpu->portc_in().set_ioport("P3");
	m_maincpu->portc_out().set(FUNC(jakks_state::portc_w));

	I2C_24C04(config, m_i2cmem, 0);
}

void jakks_state::spg2xx_jakks(machine_config &config)
{
	base_config(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_state::mem_map_2m);
}

void jakks_state::spg2xx_dpma(machine_config& config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(jakks_state::base_porta_r));
	m_maincpu->portc_in().set_ioport("P3");
	m_maincpu->portc_out().set(FUNC(jakks_state::portc_w));
	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_state::mem_map_2m);
}

void jakks_state::jakks_sesa(machine_config& config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	spg2xx_base(config);

	m_maincpu->set_pal(true);
	m_screen->set_refresh_hz(50);

	m_maincpu->porta_in().set(FUNC(jakks_state::base_porta_r));
	m_maincpu->portc_in().set_ioport("P3");
	m_maincpu->portc_out().set(FUNC(jakks_state::portc_w));
	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_state::mem_map_2m);
}



void jakks_state::mem_map_2m_mkram(address_map &map)
{
	map(0x000000, 0x1fffff).bankr("cartbank");
	map(0x3e0000, 0x3fffff).ram().share("nvram"); // backed up by the CR2032
}

void jakks_state::mem_map_hmbb(address_map &map)
{
	map(0x000000, 0x3fffff).bankr("cartbank");
	map(0x3e0000, 0x3fffff).ram();
}

void jakks_state::mk(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);

	spg2xx_base(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_state::mem_map_2m_mkram);

	m_maincpu->porta_in().set_ioport("P1");
	//m_maincpu->portb_in().set(FUNC(jakks_state::base_portb_r));
	m_maincpu->portc_in().set_ioport("P3");

	NVRAM(config, "nvram", nvram_device::DEFAULT_RANDOM);
}

void jakks_state::spg2xx_hmbb(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);

	spg2xx_base(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_state::mem_map_hmbb);

	m_maincpu->porta_in().set(FUNC(jakks_state::base_porta_r));
	m_maincpu->portc_in().set_ioport("P3");
	m_maincpu->portc_out().set(FUNC(jakks_state::portc_w));

	I2C_24C16(config, m_i2cmem, 0);
}


void jakks_state::jakks_mpaco(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(jakks_state::base_porta_r));
	m_maincpu->portc_in().set_ioport("P3");
	m_maincpu->portc_out().set(FUNC(jakks_state::portc_w));

	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_state::mem_map_1m);
	m_maincpu->adc_in<0>().set_ioport("DIALX");
}


void jakks_state::jakks_mpac(machine_config &config)
{
	jakks_mpaco(config);
	I2C_24C04(config, m_i2cmem, 0);
	m_maincpu->adc_in<0>().set_ioport("DIALX");
}

void jakks_state::jakks_rapm(machine_config &config)
{
	spg2xx_jakks(config);
	m_maincpu->adc_in<0>().set_ioport("DIALX");
}

void jakks_state::spg2xx_wof2(machine_config &config)
{
	spg2xx_jakks(config);
	m_maincpu->portb_in().set(FUNC(jakks_state::wof2_wheel_r));
}

ROM_START( jak_batm )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "batman.bin", 0x000000, 0x400000, CRC(46f848e5) SHA1(5875d57bb3fe0cac5d20e626e4f82a0e5f9bb94c) )
ROM_END

ROM_START( jak_wall )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakkwalle.u3", 0x000000, 0x400000, CRC(bf9b0a7a) SHA1(d998d940c8a80b32d535120e58fddb3e414ef455) )
ROM_END

ROM_START( jak_potc )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "pirates.u5", 0x000000, 0x400000, CRC(935fe66c) SHA1(8b5b11c61b7f32c313aa46e33a1c918ed82f7916) )
ROM_END

ROM_START( jak_cind )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakkscinderella.u5", 0x000000, 0x200000, CRC(73008a51) SHA1(89168bc2c64836daa341a6fbacb1fa63c2fef14b) )
ROM_END

ROM_START( jak_slpb )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakks_sleepingbeauty.u5", 0x000000, 0x200000, CRC(e5b20a73) SHA1(3c305c4b9265d9bbf090805daaf26ad43af54389) )
ROM_END

ROM_START( jak_hm1m )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakkshmoiam.u4", 0x000000, 0x400000, CRC(38f0ec0c) SHA1(15458aa3de77776a9e3419073a8af7f92e403b6e) )
ROM_END

ROM_START( jak_avtr )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksavatar.u2", 0x000000, 0x400000, CRC(e6f3fd64) SHA1(fe364eab4fb8d729a9c1c6a640779e54ad34fb24) )
ROM_END

ROM_START( jak_supm )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "superman.u3", 0x000000, 0x400000, CRC(626bdd85) SHA1(605b3193c17f606d2de5689f045b50ac0b7ff024) )
ROM_END

ROM_START( jak_sbjd )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "spongebobjelly.bin", 0x000000, 0x400000, CRC(804fbd87) SHA1(519aa7fada993837cb57fce26a1d721547af1861) )
ROM_END

ROM_START( jak_mk )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	// Sources indicate this should use a 6MB ROM.  The ROM here dosen't end on a blank fill and even the ROM checksum listed in the header seems to be about 50% off.
	// However no content actually seems to be missing, so are sources claiming a 6MB ROM just incorrect, with the checksum also being misleading?
	ROM_LOAD16_WORD_SWAP( "jakmk.bin", 0x000000, 0x400000, CRC(b7d7683e) SHA1(e54a020ee746d240267ef78bed7aea744351b421) )
ROM_END

ROM_START( jak_mpacw )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "wirelessnamco.bin", 0x000000, 0x200000, CRC(78a318ca) SHA1(3c2601cbb023edb6a1f3d4bce686e0be1ef63eee) )
ROM_END

ROM_START( jak_mpacq )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakks_qvc7in1.u2", 0x000000, 0x200000, CRC(a0a132a0) SHA1(acb5a98dfb597151015929bdd5b6eada1764e2fe) )
ROM_END

ROM_START( jak_mpaco )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakks_mspacmanrev1.u1", 0x000000, 0x100000, CRC(de4c2262) SHA1(2a5bacdcd10ccfdb1ae792d0aaaf2d38ec936f94) )
ROM_END

ROM_START( jak_rapm )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakks_retroarcade.u3", 0x000000, 0x400000, CRC(f2dcb1c8) SHA1(2361b8598279bc6642ca997bbf2072a9e6ac045e) )
ROM_END

ROM_START( jak_pacg )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakkspacmangold.u2", 0x000000, 0x200000, CRC(59904862) SHA1(f86a8847742fa918bdfa0fa927ec6e1f573ce31c) )
ROM_END

ROM_START( jak_spac ) // this is actually built as a gamekey, with the header text in it, sources suggest it was also going to be released as one
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakkssuperpacman.u2", 0x000000, 0x100000, CRC(490db7cc) SHA1(9b599f28b9fa8e4a502ae7a167c410d4738b8e7a) )
ROM_END

ROM_START( jak_spd3 )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "spiderman3.u4", 0x000000, 0x200000, CRC(87019271) SHA1(80d126af970236a1cecf7ade49f916caf8f67ceb) )
ROM_END

ROM_START( jak_powr ) // shows Game-Key screen but was never produced with a GK slot
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "powerrangers.u2", 0x000000, 0x200000, CRC(859c6cff) SHA1(12bb08657e333c2644d707deead8cd3e34a140b2) )
ROM_END

ROM_START( jak_pix )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakkspixar.bin", 0x000000, 0x200000, CRC(ec110f2b) SHA1(a57e1d45cfb537173f94d1a95323183a62976bb4) )
ROM_END

ROM_START( jak_shrk )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakkshrek.u2", 0x000000, 0x400000, CRC(4abb910d) SHA1(e663abd2ba6eacca2f4a046d8b8ebac2cf3fd36a) )
ROM_END

ROM_START( jak_marv )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakmarvelheroes.u4", 0x000000, 0x200000, CRC(e63259a3) SHA1(8745f3071ba460c8ba4a5bd376a72154b2edd8bd) )
ROM_END

ROM_START( jak_spdv )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakkspiderweb.u2", 0x000000, 0x200000, CRC(408c94bc) SHA1(350f7b84abf3d0d56f081647b3a228505751ff70) )
ROM_END

ROM_START( jak_dpma )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakks_magicaladventures.u2", 0x000000, 0x200000, CRC(3c3fdf54) SHA1(9847412a0ee21819cc714ca1a2dd519edb892c95) )
ROM_END

ROM_START( jak_hsm )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakkshighschoolmusical.u3", 0x000000, 0x400000, CRC(ad83549a) SHA1(1d198209055e418402c9f3f389bccde156ca5a43) )
ROM_END

ROM_START( jak_ctah )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakkscheetahgirls.u4", 0x000000, 0x400000, CRC(cab222c2) SHA1(2fda5599d86850ffaaa35824c7788d153bd1d615) )
ROM_END

ROM_START( jak_dwa )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksdorawa.u3", 0x000000, 0x200000, CRC(fd519d3a) SHA1(a68a0dca5b722d83258c452a1c9243d4e9cb9de0) )
ROM_END

ROM_START( jak_5thg )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakks5thgrader.u2", 0x000000, 0x200000, CRC(d460d360) SHA1(b6547a5ba93b40dcd7c993c4dfc917c1f415f4f0) )
ROM_END

ROM_START( jak_gdg )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksgodiegogo.u4", 0x000000, 0x200000, CRC(eaffbf9f) SHA1(7a415b817c8fed44569e1b66a42e80de9a81d4de) )
ROM_END

ROM_START( jak_sesa )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakkssesamestreet.u2", 0x000000, 0x400000, CRC(bae5f43a) SHA1(24e7875cea236e51c10013a33cc41f3c1816514c) )
ROM_END

ROM_START( jak_ntsc )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakkssummercamp.bin", 0x000000, 0x200000, CRC(4b6711a0) SHA1(694eb97be72233adb5de322dfc00633c4db79113) )
ROM_END

ROM_START( jak_wof2 )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakkswheeloffortun2nd.u4", 0x000000, 0x200000, CRC(9eff79a8) SHA1(342b736aba13705d63f49f58bb3a2ef2505620a4) )
ROM_END

ROM_START( jak_dond )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksdond.u4", 0x000000, 0x200000, CRC(0ae0706f) SHA1(6144190d126b36378c05b6e0a633ab2b53b3fa39) )
ROM_END

ROM_START( jak_1vs )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	// byte 0xb5a27 originally gave 0xff with consistent reads (but bytesum was wrong)
	// with different settings gave 0xfe and consistent reads (bytesum in header matches)
	// ingame checksum screen seems to be broken (gives black screen after calculating)
	ROM_LOAD16_WORD_SWAP( "jakks1vs100.u3", 0x000000, 0x200000, CRC(4a8cdedf) SHA1(e6d035262d9b022e9cd19f2322ba6ef1a1db7b38) )
ROM_END

ROM_START( jak_jpdy )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksjeopardy.u2", 0x000000, 0x200000, CRC(6ccf00b7) SHA1(51e6fe60f3169e52d03f46469923253231a7262e) )
ROM_END

ROM_START( jak_hmbb )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakkhmbobw.bin", 0x000000, 0x800000, CRC(4e72cbf9) SHA1(efcec76da39c8373e8ef769c4fa0a35d379896d8) )
ROM_END

ROM_START( jak_xmen )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakkxmen.u3", 0x000000, 0x400000, CRC(f4383dca) SHA1(9850782e498ab0036850aa1f8926299c658a6099) )
ROM_END

ROM_START( jak_xmenp )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "xmen.bin", 0x000000, 0x400000, CRC(1fa271e0) SHA1(c32652e9eddf82ab496e3609f8fa444e447fb509) )
ROM_END

ROM_START( jak_dwmn )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "dreamworks.bin", 0x000000, 0x400000, CRC(3ae9f786) SHA1(46451be3af459fbdb75d1155b3817543afe183d5) )
ROM_END

ROM_START( jak_dwmno )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "dw_spg300_test.bin", 0x000000, 0x400000, CRC(1ca2817b) SHA1(39ae519457c102c4420fae3699b2db0557ef1cf5) )
ROM_END

} // anonymous namespace


// Pre-GameKey units

CONS( 2004, jak_batm, 0, 0, spg2xx_jakks,  batman,        jakks_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd",      "The Batman (JAKKS Pacific TV Game)", MACHINE_IMPERFECT_SOUND )

// this is an older unit than the jak_mpac Game Key Ready set and features no GameKey branding
CONS( 2004, jak_mpacw, 0,         0, jakks_mpac, jak_mpac,   jakks_state, empty_init, "JAKKS Pacific Inc / Namco / HotGen Ltd",      "Ms. Pac-Man Collection 7-in-1 (JAKKS Pacific TV Game) (wireless, 18 AUG 2004 A)", MACHINE_IMPERFECT_SOUND )
CONS( 2004, jak_mpacq, jak_mpacw, 0, jakks_mpac, jak_mpac,   jakks_state, empty_init, "JAKKS Pacific Inc / Namco / HotGen Ltd",      "Ms. Pac-Man Collection 7-in-1 (JAKKS Pacific TV Game) (QVC version, 12 JUL 2004 A)", MACHINE_IMPERFECT_SOUND )

CONS( 2004, jak_mpaco, jak_mpac,  0, jakks_mpaco, jak_mpaco,   jakks_state, empty_init, "JAKKS Pacific Inc / Namco / HotGen Ltd",      "Ms. Pac-Man Collection 5-in-1 (JAKKS Pacific TV Game) (01 APR 2004 A)", MACHINE_IMPERFECT_SOUND )

// you could link 2 pads of this together for 2 player mode as you could with WWE (feature not emulated)
CONS( 2004, jak_mk,   0, 0, mk,     mk,     jakks_state, empty_init, "JAKKS Pacific Inc / Digital Eclipse", "Mortal Kombat (JAKKS Pacific TV Game)", MACHINE_IMPERFECT_SOUND )

// Post-GameKey units (all of these still have GameKey references in the code even if the physical connector was no longer present on the PCB)

// This was available in 2 different case styles, initially an underwater / jellyfish themed one, then later
// reissued in a 'SpongeBob head' style case reminiscent of the undumpable 2003 SpongeBob plug and play but
// with 2 buttons in the top left corner instead of 1
//
// The software on both versions of Jellyfish Dodge is believed to be the same, the build date can be seen in
// the 'hidden' test mode.
//
// A further updated version of this, adapted for touch controls, was released as a 'TV Touch' unit, see
// spg2xx_jakks_tvtouch.cpp
CONS( 2007, jak_sbjd, 0, 0, spg2xx_jakks,  spg2xx_jakks,  jakks_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd",      "SpongeBob SquarePants Jellyfish Dodge (JAKKS Pacific TV Game) (Apr 5 2007)", MACHINE_IMPERFECT_SOUND )

CONS( 2008, jak_wall, 0, 0, spg2xx_jakks,  spg2xx_jakks,  jakks_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd",      "Wall-E (JAKKS Pacific TV Game) (Dec 18 2007 11:34:25)", MACHINE_IMPERFECT_SOUND )

CONS( 2007, jak_potc, 0, 0, spg2xx_jakks,  spg2xx_jakks,  jakks_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd",      "Pirates of the Caribbean - Islands of Fortune (JAKKS Pacific TV Game) (Jun 1 2007 12:34:28)", MACHINE_IMPERFECT_SOUND )

CONS( 2006, jak_supm, 0, 0, spg2xx_jakks,  jak_supm,      jakks_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd",      "Superman in Super Villain Showdown (JAKKS Pacific TV Game) (26 Jan 2006 A)", MACHINE_IMPERFECT_SOUND ) // has AT24C04

CONS( 2006, jak_spdv, 0, 0, spg2xx_jakks,  spg2xx_spdv,   jakks_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd",      "The Amazing Spider-Man in Villain Round-Up (JAKKS Pacific TV Game) (24 Apr 2006 A)", MACHINE_IMPERFECT_SOUND )

// the physical 2nd edition does't have a dpad, you need to use the wheel to navigate (although the inputs still work in emulation)
// test mode button code is unknown (changed from usual HotGen code due to lack of dpad) routine is at 0xd5fd
CONS( 2007, jak_wof2, 0, 0, spg2xx_wof2,  spg2xx_jakks,  jakks_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd",      "Wheel of Fortune - 2nd Edition (JAKKS Pacific TV Game) (Mar 15 2007 PAK2)", MACHINE_IMPERFECT_SOUND )

CONS( 2007, jak_pacg, 0, 0, spg2xx_jakks,  spg2xx_pacg,   jakks_state, empty_init, "JAKKS Pacific Inc / Namco / HotGen Ltd", "Arcade Gold featuring Pac-Man (20 APR 2007 A SKU O)", MACHINE_IMPERFECT_SOUND )

CONS( 2006, jak_spac, 0, 0, spg2xx_jakks,  jak_mpac,      jakks_state, empty_init, "JAKKS Pacific Inc / Namco / HotGen Ltd", "Super Pac-Man Collection (26 JAN 2006 A SKU L)", MACHINE_IMPERFECT_SOUND )

CONS( 2008, jak_rapm, 0, 0, jakks_rapm,    jak_rapm,      jakks_state, empty_init, "JAKKS Pacific Inc / Namco / HotGen Ltd", "Retro Arcade featuring Pac-Man (20 AUG 2008 A SKU N)", MACHINE_IMPERFECT_SOUND )

CONS( 2007, jak_slpb, 0, 0, spg2xx_jakks,  spg2xx_pacg,   jakks_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd",  "Sleeping Beauty - Tales of Enchantment (JAKKS Pacific TV Game) (Sep 17 2007 14:45:02)", MACHINE_IMPERFECT_SOUND )

CONS( 2007, jak_hm1m, 0, 0, spg2xx_jakks,  spg2xx_jakks,  jakks_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd",  "Hannah Montana - One in a Million (JAKKS Pacific TV Game) (Aug 13 2007 15:42:29)", MACHINE_IMPERFECT_SOUND )

CONS( 2006, jak_avtr, 0, 0, spg2xx_jakks,  spg2xx_avtr,   jakks_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd",  "Avatar: The Last Airbender - Book One Challenges (JAKKS Pacific TV Game) (06 Jun 2006 A)", MACHINE_IMPERFECT_SOUND )

CONS( 2007, jak_hmbb, 0, 0, spg2xx_hmbb,   spg2xx_jakks,  jakks_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd",  "Hannah Montana - Best of Both Worlds (JAKKS Pacific TV Game) (Aug 17 2007 22:47:47)", MACHINE_IMPERFECT_SOUND )

// from a PAL unit, and seems to have timing issues on the audio (speech cutting off / starting before previous has finished) when using an NTSC machine config, so maybe the NTSC ROM is different?
// test mode combination isn't the usual HotGen one, but can be accessed by setting a breakpoint at 0xa6ba and setting r2 to 0x0a - TODO: figure out combination so version can be checked against an NTSC unit.
CONS( 2006, jak_sesa, 0, 0, jakks_sesa,    spg2xx_sesa,   jakks_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd",  "Sesame Street Beat (JAKKS Pacific TV Game) (Aug 23 2006 19:12:03, PAL/UK)", MACHINE_IMPERFECT_SOUND )


CONS( 2005, jak_powr, 0, 0, spg2xx_jakks,  spg2xx_jakks,  jakks_state, empty_init, "JAKKS Pacific Inc / Handheld Games",  "Power Rangers S.P.D. (JAKKS Pacific TV Game)", MACHINE_IMPERFECT_SOUND )

CONS( 2006, jak_pix,  0, 0, spg2xx_jakks,  spg2xx_jakks,  jakks_state, empty_init, "JAKKS Pacific Inc / Handheld Games",  "Disney Pixar Classics (JAKKS Pacific TV Game)", MACHINE_IMPERFECT_SOUND )

// menu sounds don't work until you go into a game (work after a reset, bad default initializations?)
CONS( 2006, jak_shrk, 0, 0, spg2xx_jakks,  spg2xx_jakks,  jakks_state, empty_init, "JAKKS Pacific Inc / Handheld Games",  "Dreamworks Shrek / Over The Hedge (JAKKS Pacific TV Game)", MACHINE_IMPERFECT_SOUND )

CONS( 2006, jak_marv, 0, 0, spg2xx_jakks,  spg2xx_jakks,  jakks_state, empty_init, "JAKKS Pacific Inc / Handheld Games",  "Marvel Heroes: Ultimate Action (JAKKS Pacific TV Game)", MACHINE_IMPERFECT_SOUND )

CONS( 2006, jak_ntsc, 0, 0, spg2xx_jakks,  spg2xx_ntoonsc,jakks_state, empty_init, "JAKKS Pacific Inc / Handheld Games",  "Nicktoons - Summer Camp (JAKKS Pacific TV Game)", MACHINE_IMPERFECT_SOUND )

CONS( 2006, jak_dpma, 0, 0, spg2xx_dpma,  spg2xx_dpma,    jakks_state, empty_init, "JAKKS Pacific Inc / Handheld Games",  "Disney Princess Magical Adventures (JAKKS Pacific TV Game)", MACHINE_IMPERFECT_SOUND )

CONS( 2007, jak_hsm,  0, 0, spg2xx_jakks,  spg2xx_jakks,  jakks_state, empty_init, "JAKKS Pacific Inc / Handheld Games",  "High School Musical (JAKKS Pacific TV Game) (Dec 19 2007 17:08:20)", MACHINE_IMPERFECT_SOUND )

CONS( 2007, jak_ctah, 0, 0, spg2xx_jakks,  spg2xx_jakks,  jakks_state, empty_init, "JAKKS Pacific Inc / Handheld Games",  "The Cheetah Girls - Passport to Fame (JAKKS Pacific TV Game) (Aug 1 2007 10:32:50)", MACHINE_IMPERFECT_SOUND )

CONS( 2007, jak_spd3, 0, 0, spg2xx_jakks,  jak_spd3,      jakks_state, empty_init, "JAKKS Pacific Inc / Handheld Games",  "Spider-Man 3 (JAKKS Pacific TV Game)", MACHINE_IMPERFECT_SOUND )

CONS( 2007, jak_cind, 0, 0, spg2xx_jakks,  spg2xx_pacg,   jakks_state, empty_init, "JAKKS Pacific Inc / Handheld Games",  "Cinderella - Once Upon a Midnight (JAKKS Pacific TV Game) (Aug 29 2007 11:15:55)", MACHINE_IMPERFECT_SOUND )

CONS( 2007, jak_dwa,  0, 0, spg2xx_jakks,  spg2xx_jakks,  jakks_state, empty_init, "JAKKS Pacific Inc / Handheld Games",  "Dora the Explorer - Dora's World Adventure! (JAKKS Pacific TV Game)", MACHINE_IMPERFECT_SOUND )

CONS( 2007, jak_5thg, 0, 0, spg2xx_jakks,  spg2xx_jakks,  jakks_state, empty_init, "JAKKS Pacific Inc / Handheld Games",  "Are You Smarter than a 5th Grader? (JAKKS Pacific TV Game)", MACHINE_IMPERFECT_SOUND )

CONS( 2006, jak_jpdy, 0, 0, spg2xx_jakks,  spg2xx_jpdy,   jakks_state, empty_init, "JAKKS Pacific Inc / 5000ft, Inc",  "Jeopardy! (JAKKS Pacific TV Game)", MACHINE_IMPERFECT_SOUND )

CONS( 2006, jak_gdg,  0, 0, spg2xx_dpma,   spg2xx_gdg,    jakks_state, empty_init, "JAKKS Pacific Inc / 1st Playable Productions",  "Go Diego Go! (JAKKS Pacific TV Game)", MACHINE_IMPERFECT_SOUND )

CONS( 2006, jak_dond, 0, 0, spg2xx_jakks,  jak_dond,      jakks_state, init_crc,   "JAKKS Pacific Inc / Pronto Games",    "Deal or No Deal (JAKKS Pacific TV Game)", MACHINE_IMPERFECT_SOUND )

CONS( 2007, jak_1vs,  0, 0, spg2xx_jakks,  spg2xx_1vs,    jakks_state, init_crc,   "JAKKS Pacific Inc / Pronto Games",    "1 Vs 100 (JAKKS Pacific TV Game)", MACHINE_IMPERFECT_SOUND )

CONS( 2005, jak_xmen, 0,        0, spg2xx_jakks,  spg2xx_xmen,  jakks_state, empty_init, "JAKKS Pacific Inc / Amaze Entertainment",     "X-Men - Mutant Reign (JAKKS Pacific TV Game)", MACHINE_IMPERFECT_SOUND )
CONS( 2005, jak_xmenp,jak_xmen, 0, spg2xx_jakks,  spg2xx_xmen,  jakks_state, empty_init, "JAKKS Pacific Inc / Amaze Entertainment",     "X-Men - Mutant Reign (JAKKS Pacific TV Game, prototype)", MACHINE_IMPERFECT_SOUND )

// Dreamworks Movie Night was never manufactured
CONS( 2006, jak_dwmn, 0,        0, spg2xx_jakks, spg2xx_jakks,  jakks_state, empty_init, "JAKKS Pacific Inc / Amaze Entertainment",      "Dreamworks Movie Night (JAKKS Pacific TV Game, Oct 18 2006, prototype)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2006, jak_dwmno,jak_dwmn, 0, spg2xx_jakks, spg2xx_jakks,  jakks_state, empty_init, "JAKKS Pacific Inc / Amaze Entertainment",      "Dreamworks Movie Night (JAKKS Pacific TV Game, Apr 24 2006, test program)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )



spg2xx_jakks_gkr.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

// Note: even after the GameKey port was physically removed and the PCBs redesigned, many of the test modes still show the value read from the port (and many games still show the Game Key Ready splash screen on startup)

#include "emu.h"
#include "spg2xx.h"

#include "bus/jakks_gamekey/slot.h"
#include "softlist_dev.h"


namespace {

class jakks_gkr_state : public spg2xx_game_state
{
public:
	jakks_gkr_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_state(mconfig, type, tag),
		m_porta_key_mode(false),
		m_cart(*this, "cartslot"),
		m_cart_region(nullptr),
		m_analog_x(*this, "JOYX"),
		m_analog_y(*this, "JOYY")
	{ }

	void jakks_gkr(machine_config &config);
	void jakks_gkr_i2c(machine_config &config);
	void jakks_gkr_1m_i2c(machine_config &config);
	void jakks_gkr_2m_i2c(machine_config &config);
	void jakks_gkr_nk(machine_config &config);
	void jakks_gkr_nk_i2c(machine_config &config);
	void jakks_gkr_dy(machine_config &config);
	void jakks_gkr_dy_i2c(machine_config &config);
	void jakks_gkr_dp_i2c(machine_config &config);
	void jakks_gkr_sw_i2c(machine_config &config);
	void jakks_gkr_nm_i2c(machine_config &config);
	void jakks_gkr_cc_i2c(machine_config &config);
	void jakks_gkr_wf_i2c(machine_config &config);
	void jakks_gkr_mv_i2c(machine_config &config);
	void jakks_gkr_wp(machine_config &config);
	void jakks_gkr_cb(machine_config &config);

	int i2c_gkr_r();

protected:
	[[maybe_unused]] uint16_t jakks_porta_r();
	[[maybe_unused]] void jakks_porta_w(uint16_t data);
	void jakks_portb_w(uint16_t data);

private:
	virtual void machine_start() override ATTR_COLD;

	uint16_t joy_x_read();
	uint16_t joy_y_read();

	uint16_t jakks_porta_key_io_r();

	void gkr_portc_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	void jakks_porta_key_io_w(uint16_t data);
	bool m_porta_key_mode;

	optional_device<jakks_gamekey_slot_device> m_cart;
	memory_region *m_cart_region;

	optional_ioport m_analog_x;
	optional_ioport m_analog_y;
};

int jakks_gkr_state::i2c_gkr_r()
{
	if (m_cart && m_cart->exists())
	{
		return m_cart->read_cart_seeprom();
	}
	else
	{
		return m_i2cmem->read_sda();
	}
}

void jakks_gkr_state::gkr_portc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (m_cart && m_cart->exists())
	{
		m_cart->write_cart_seeprom(offset, data, mem_mask);
	}
	else
	{
		if (m_i2cmem)
		{
			m_i2cmem->write_scl(BIT(data | ~mem_mask, 1));
			m_i2cmem->write_sda(BIT(data | ~mem_mask, 0));
		}
	}
}

uint16_t jakks_gkr_state::jakks_porta_r()
{
	//logerror("%s: jakks_porta_r\n", machine().describe_context());
	return m_io_p1->read();
}

void jakks_gkr_state::jakks_porta_w(uint16_t data)
{
	//logerror("%s: jakks_porta_w %04x\n", machine().describe_context(), data);
}

void jakks_gkr_state::jakks_portb_w(uint16_t data)
{
	//logerror("%s: jakks_portb_w %04x\n", machine().describe_context(), data);
}

uint16_t jakks_gkr_state::joy_x_read()
{
	const uint16_t data = m_analog_x->read();
	return data > 0x0fff ? 0x0fff : data;
}

uint16_t jakks_gkr_state::joy_y_read()
{
	const uint16_t data = m_analog_y->read();
	return data > 0x0fff ? 0x0fff : data;
}

uint16_t jakks_gkr_state::jakks_porta_key_io_r()
{
	//logerror("%s: jakks_porta_key_io_r\n", machine().describe_context());
	if (m_porta_key_mode == false)
	{
		return m_io_p1->read();
	}
	else
	{
		/* masks with 0xf, inverts, and combines it with a previous read (when data written to jakks_porta_key_io_w was 0x0000) and expects result to be 0x0000
		   could just expect data written to be returned, but that would be a strange check.
		   all systems seem to respond to the same result, so how is the per-system lock implemented? */
		return (m_io_p1->read() & 0xfff0) | 0x000f;
	}
}

void jakks_gkr_state::jakks_porta_key_io_w(uint16_t data)
{
	logerror("%s: jakks_porta_key_io_w %04x\n", machine().describe_context(), data);
	// only seen 0xffff and 0x0000 written here.. writes 0xffff before the 2nd part of the port a gamekey check read.
	if (data == 0xffff)
	{
		m_porta_key_mode = true;
	}
	else
	{
		m_porta_key_mode = false;
	}
}


static INPUT_PORTS_START( jak_sith_i2c )
	PORT_START("P1")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON3 )
	PORT_BIT( 0xf3df, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(jakks_gkr_state::i2c_gkr_r))
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PAL/NTSC flag, set to NTSC (unverified here)
	PORT_BIT( 0xfff6, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("JOYX")
	PORT_BIT(0x0fff, 0x0800, IPT_AD_STICK_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_MINMAX(0x0000,0x0fff)

	PORT_START("JOYY")
	PORT_BIT(0x0fff, 0x0800, IPT_AD_STICK_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_MINMAX(0x0000,0x0fff)
INPUT_PORTS_END

static INPUT_PORTS_START( jak_pooh )
	PORT_START("P1")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 )  PORT_NAME("Menu / Pause")
	PORT_BIT( 0xf7df, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0xfff7, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PAL/NTSC flag, set to NTSC (unverified here)

	PORT_START("JOYX")
	PORT_BIT(0x0fff, 0x0000, IPT_AD_STICK_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_MINMAX(0x00,0x0fff)

	PORT_START("JOYY")
	PORT_BIT(0x0fff, 0x0000, IPT_AD_STICK_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_MINMAX(0x00,0x0fff)
INPUT_PORTS_END

static INPUT_PORTS_START( jak_care )
	PORT_START("P1")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 )  PORT_NAME("Menu / Pause")
	PORT_BIT( 0xf7df, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0xfff7, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PAL/NTSC flag, set to NTSC (unverified here)

	PORT_START("JOYX")
	PORT_BIT(0x0fff, 0x0000, IPT_AD_STICK_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_MINMAX(0x00,0x0fff)

	PORT_START("JOYY")
	PORT_BIT(0x0fff, 0x0000, IPT_AD_STICK_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_MINMAX(0x00,0x0fff)
INPUT_PORTS_END

static INPUT_PORTS_START( jak_nm_i2c )
	PORT_START("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1)
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1)
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Menu")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON1 )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(jakks_gkr_state::i2c_gkr_r))
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PAL/NTSC flag, set to NTSC
	PORT_BIT( 0xfff0, IP_ACTIVE_HIGH, IPT_UNUSED )


	PORT_START("DIALX") // for Pole Position, joystick can be twisted like a dial/wheel (limited?) (check range)
	PORT_BIT(0x0fff, 0x0000, IPT_AD_STICK_X ) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_MINMAX(0x00,0x0fff)
INPUT_PORTS_END

static INPUT_PORTS_START( jak_cc_i2c )
	PORT_START("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1)
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1)
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("A")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("B")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("C")
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("D")

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(jakks_gkr_state::i2c_gkr_r))
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PAL/NTSC flag, set to NTSC
	PORT_BIT( 0xfff0, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( jak_wf_i2c )
	PORT_START("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1)
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1)
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("A")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("B")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x01c0, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Menu")
	PORT_BIT( 0x001f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(jakks_gkr_state::i2c_gkr_r))
	PORT_BIT( 0xfff6, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PAL/NTSC flag, set to NTSC

	/* on real unit you can spin the wheel (and must make sure it completes a full circle, or you lose your turn) instead of pressing 'B' for a random spin but where does it map? (it can be tested in secret test mode)
	PORT_START("DIALX")
	PORT_BIT(0x0fff, 0x0000, IPT_AD_STICK_X ) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_MINMAX(0x00,0x0fff)

	PORT_START("DIALY")
	PORT_BIT(0x0fff, 0x0000, IPT_AD_STICK_Y ) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_MINMAX(0x00,0x0fff)
	*/
INPUT_PORTS_END

static INPUT_PORTS_START( jak_gkr )
	PORT_START("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1) PORT_NAME("Joypad Up")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1) PORT_NAME("Joypad Down")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1) PORT_NAME("Joypad Left")
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("Joypad Right")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON3 )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON4 )
	PORT_BIT( 0x00c0, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Menu / Pause")
	PORT_BIT( 0x001f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_DIPNAME( 0x0001, 0x0001, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PAL/NTSC flag, set to NTSC (not verified for all games, state can be seen in secret test menu of many tho)
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) ) // this causes WWE to think the unit is a '2nd Controller' and tells you to plug the 1st one in.
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( jak_sdoo_i2c ) // GameKeyReady units had 2 main buttons, later releases reduced that to 1 button (as the internal games don't require 2 and no GameKeys were released)
	PORT_START("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1)
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1)
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED ) // debug input, skips levels!
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x00c0, IP_ACTIVE_HIGH, IPT_UNUSED ) // must be low or other inputs don't work?
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Menu / Pause")
	PORT_BIT( 0x001f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(jakks_gkr_state::i2c_gkr_r))
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PAL/NTSC flag, set to NTSC (unverified here)
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
INPUT_PORTS_END


static INPUT_PORTS_START( jak_gkr_i2c )
	PORT_INCLUDE(jak_gkr)

	PORT_MODIFY("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(jakks_gkr_state::i2c_gkr_r))
INPUT_PORTS_END

static INPUT_PORTS_START( jak_dpr_i2c )
	PORT_START("P1")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1)
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1)
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x00c0, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Start / Menu / Pause")
	PORT_BIT( 0x001f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(jakks_gkr_state::i2c_gkr_r))
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PAL/NTSC flag, set to NTSC (unverified here)
	PORT_BIT( 0xfff6, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


void jakks_gkr_state::machine_start()
{
	spg2xx_game_state::machine_start();

	// if there's a cart, override the standard banking
	if (m_cart && m_cart->exists())
	{
		std::string region_tag;
		m_cart_region = memregion(region_tag.assign(m_cart->tag()).append(JAKKSSLOT_ROM_REGION_TAG).c_str());
		m_bank->configure_entries(0, (m_cart_region->bytes() + 0x7fffff) / 0x800000, m_cart_region->base(), 0x800000);
		m_bank->set_entry(0);
	}
}

void jakks_gkr_state::jakks_gkr(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_gkr_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(jakks_gkr_state::jakks_porta_key_io_r));
	m_maincpu->porta_out().set(FUNC(jakks_gkr_state::jakks_porta_key_io_w));
	m_maincpu->portb_out().set(FUNC(jakks_gkr_state::jakks_portb_w));
	m_maincpu->portc_in().set_ioport("P3");
	m_maincpu->portc_out().set(FUNC(jakks_gkr_state::gkr_portc_w));

	JAKKS_GAMEKEY_SLOT(config, m_cart, 0, jakks_gamekey, nullptr);
}

void jakks_gkr_state::jakks_gkr_i2c(machine_config &config)
{
	jakks_gkr(config);
	I2C_24C16(config, m_i2cmem, 0); // ?
}


void jakks_gkr_state::jakks_gkr_1m_i2c(machine_config &config)
{
	jakks_gkr_i2c(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_gkr_state::mem_map_1m);
}

void jakks_gkr_state::jakks_gkr_2m_i2c(machine_config &config)
{
	jakks_gkr_i2c(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_gkr_state::mem_map_2m);
}

void jakks_gkr_state::jakks_gkr_nk(machine_config &config)
{
	jakks_gkr(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_gkr_state::mem_map_1m);
	SOFTWARE_LIST(config, "jakks_gamekey_nk").set_original("jakks_gamekey_nk");
}

void jakks_gkr_state::jakks_gkr_nk_i2c(machine_config &config)
{
	jakks_gkr_i2c(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_gkr_state::mem_map_1m);
	SOFTWARE_LIST(config, "jakks_gamekey_nk").set_original("jakks_gamekey_nk");
}

void jakks_gkr_state::jakks_gkr_dy(machine_config &config)
{
	jakks_gkr(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_gkr_state::mem_map_1m);
	SOFTWARE_LIST(config, "jakks_gamekey_dy").set_original("jakks_gamekey_dy");
}

void jakks_gkr_state::jakks_gkr_dy_i2c(machine_config &config)
{
	jakks_gkr_i2c(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_gkr_state::mem_map_1m);
	SOFTWARE_LIST(config, "jakks_gamekey_dy").set_original("jakks_gamekey_dy");
}

void jakks_gkr_state::jakks_gkr_mv_i2c(machine_config &config)
{
	jakks_gkr_i2c(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_gkr_state::mem_map_1m);
	SOFTWARE_LIST(config, "jakks_gamekey_mv").set_original("jakks_gamekey_mv");
}


void jakks_gkr_state::jakks_gkr_dp_i2c(machine_config &config)
{
	jakks_gkr_i2c(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_gkr_state::mem_map_1m);
	SOFTWARE_LIST(config, "jakks_gamekey_dp").set_original("jakks_gamekey_dp");
}

void jakks_gkr_state::jakks_gkr_sw_i2c(machine_config &config)
{
	jakks_gkr_i2c(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_gkr_state::mem_map_2m);
	m_maincpu->adc_in<0>().set(FUNC(jakks_gkr_state::joy_x_read));
	m_maincpu->adc_in<1>().set(FUNC(jakks_gkr_state::joy_x_read));
	m_maincpu->adc_in<2>().set(FUNC(jakks_gkr_state::joy_y_read));
	m_maincpu->adc_in<3>().set(FUNC(jakks_gkr_state::joy_y_read));
	SOFTWARE_LIST(config, "jakks_gamekey_sw").set_original("jakks_gamekey_sw");
}

void jakks_gkr_state::jakks_gkr_wp(machine_config &config)
{
	jakks_gkr(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_gkr_state::mem_map_1m);
	m_maincpu->adc_in<0>().set_ioport("JOYX");
	m_maincpu->adc_in<2>().set_ioport("JOYY");
	//SOFTWARE_LIST(config, "jakks_gamekey_wp").set_original("jakks_gamekey_wp"); // NO KEYS RELEASED

	m_maincpu->set_force_no_drc(true); // the Light Tag game seems to hang maybe once every 7 times with the DRC, appears more stable without (could just be chance tho)
}

void jakks_gkr_state::jakks_gkr_cb(machine_config &config)
{
	jakks_gkr(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_gkr_state::mem_map_1m);
	m_maincpu->adc_in<0>().set_ioport("JOYX");
	m_maincpu->adc_in<2>().set_ioport("JOYY");
	//SOFTWARE_LIST(config, "jakks_gamekey_cb").set_original("jakks_gamekey_cb"); // NO KEYS RELEASED
}

void jakks_gkr_state::jakks_gkr_nm_i2c(machine_config &config)
{
	jakks_gkr_i2c(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_gkr_state::mem_map_1m);
	m_maincpu->adc_in<0>().set_ioport("DIALX");
	SOFTWARE_LIST(config, "jakks_gamekey_nm").set_original("jakks_gamekey_nm");
}

void jakks_gkr_state::jakks_gkr_cc_i2c(machine_config &config)
{
	jakks_gkr_i2c(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_gkr_state::mem_map_1m);
	// shows 'E0' in gamekey test menu on real HW (maybe related to value key needs to return if one existed)
	//SOFTWARE_LIST(config, "jakks_gamekey_cc").set_original("jakks_gamekey_cc"); // no game keys were released
}

void jakks_gkr_state::jakks_gkr_wf_i2c(machine_config &config)
{
	jakks_gkr_i2c(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_gkr_state::mem_map_1m);
	//m_maincpu->adc_in<0>().set_ioport("DIALX"); // wheel does not seem to map here
	//m_maincpu->adc_in<1>().set_ioport("DIALY");
	//SOFTWARE_LIST(config, "jakks_gamekey_wf").set_original("jakks_gamekey_wf"); // no game keys were released
}

ROM_START( jak_wwe )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakkswwegkr.bin", 0x000000, 0x200000, CRC(b078a812) SHA1(7d97c0e2171b3fd91b280480c9ffd5651828195a) )
ROM_END

ROM_START( jak_fan4 )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksffgkr.bin", 0x000000, 0x200000, CRC(8755a1f7) SHA1(7214da15fe61881da27b81575fbdb54cc0f1d6aa) )
ROM_END

ROM_START( jak_just )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksjlagkr.bin", 0x000000, 0x200000, CRC(182989f0) SHA1(799229c537d6fe629ba9e1e4051d1bb9ca445d44) )
ROM_END

ROM_START( jak_dora )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksdoragkr.bin", 0x000000, 0x200000, CRC(bcaa132d) SHA1(3894b980fbc4144731b2a7a94acebb29e30de67c) )
ROM_END

ROM_START( jak_nick )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksnicktoonsgkr.bin", 0x000000, 0x200000, CRC(4dec1656) SHA1(b3002ab15e75068102f4955a3f0c52fb6d5cda56) )
ROM_END

ROM_START( jak_sbfc )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksspongebobgkr.bin", 0x000000, 0x200000, CRC(9871303c) SHA1(78bc2687e1514094db8bb875e1117df3fcb3d201) )
ROM_END

ROM_START( jak_dorr )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksdora2gkr.bin", 0x000000, 0x200000, CRC(6c09bcd9) SHA1(4bcad79658832f319d16b4f63257e127f6862d79) )
ROM_END


ROM_START( jak_spdm )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksspidermangkr.bin", 0x000000, 0x200000, CRC(1b2ee700) SHA1(30ea69c489e1238b004f473f972b682e35573138) )
ROM_END

ROM_START( jak_pooh )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakkspoohgkr.bin", 0x000000, 0x200000, CRC(0d97df55) SHA1(f108621a83c7b2263dd1531d82311627c3a02002) )
ROM_END

ROM_START( jak_care )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "carebeargkr.bin", 0x000000, 0x200000, CRC(e6096eb7) SHA1(92ee1a6df374f8b355ba2280dc43d764f6f69dfe) )
ROM_END

ROM_START( jak_wof )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakkswheeloffortunegkr.bin", 0x000000, 0x200000, CRC(6a879620) SHA1(95478764a61741569041c2299528f6464651d593) )
ROM_END

ROM_START( jak_disn )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "disneygkr.bin", 0x000000, 0x100000,  CRC(7a5ebcd7) SHA1(9add8c2a6e3f0409c8957a2ba2d054fd2c4c39c1) )
ROM_END

ROM_START( jak_disf )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "disneyfriendsgkr.bin", 0x000000, 0x200000, CRC(77bca50b) SHA1(6e0f4fd229ee11eac721b5dbe79cf9002d3dbd64) )
ROM_END

ROM_START( jak_dpr )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksdisneyprincessgkr.bin", 0x000000, 0x200000, CRC(e26003ce) SHA1(ee15243281df6f09b96185c34582d7091604c954) )
ROM_END

ROM_START( jak_dprs )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "disneyprincess2gkr.bin", 0x000000, 0x200000, CRC(b670bdde) SHA1(c33ce7ada72a0c44bc881b5792cd33a9f2f0fb08) )
ROM_END

ROM_START( jak_mpac )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksmspacmangkr.bin", 0x000000, 0x100000, CRC(cab40f77) SHA1(30731acc461150d96aafa7a0451cfb1a25264678) )
ROM_END

ROM_START( jak_sdoo )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksscoobydoogkr.bin", 0x000000, 0x400000, CRC(61062ce5) SHA1(9d21767fd855385ef83e4209c429ecd4bf7e5384) )
ROM_END

ROM_START( jak_dbz )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksdragonballzgkr.bin", 0x000000, 0x200000, CRC(d52c3b20) SHA1(fd5ce41c143cad9bca3372054f4ff98b52c33874) )
ROM_END

ROM_START( jak_sith )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "jakksstarwarsgkr.bin", 0x000000, 0x200000, CRC(932cde19) SHA1(b88b748c235e9eeeda574e4d5b4077ae9da6fbd0) )
	ROM_RELOAD(0x200000,0x200000)
ROM_END

ROM_START( jak_sithp )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "starwars_prototype.bin", 0x000000, 0x400000, CRC(796b7d90) SHA1(9bcb9899dcaae57288316fe60e7724512e80c905) )
ROM_END

ROM_START( jak_swot )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "starwars_originaltrilogy.bin", 0x000000, 0x400000, CRC(3dda7aff) SHA1(970044a6b2f14863353e559f5d2a4e928c8de439) )
ROM_END

ROM_START( jak_capc )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "capcomgkr.bin", 0x000000, 0x200000, CRC(6d47cce4) SHA1(263926a991d55459aa3cee90049d2202c1e3a70e) )
ROM_END

} // anonymous namespace


// 'Game-Key Ready' JAKKS games (these can also take per-game specific expansion cartridges, although not all games had them released)
// Some of these were available in versions without Game-Key ports, it is unconfirmed if code was the same unless otherwise stated
// For units released AFTER the GameKey promotion was cancelled it appears the code is the same as the PCB inside is the same, just the external port closed off, earlier units might be different hardware in some cases.
// units released BEFORE the GameKey support were sometimes different hardware, eg. the Spider-Man and Disney units were SPG110 based
CONS( 2005, jak_wwe,   0,        0, jakks_gkr_1m_i2c, jak_gkr_i2c,  jakks_gkr_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd",              "WWE (JAKKS Pacific TV Game, Game-Key Ready)",            MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // WW (no game-keys released)
CONS( 2005, jak_fan4,  0,        0, jakks_gkr_1m_i2c, jak_gkr_i2c,  jakks_gkr_state, empty_init, "JAKKS Pacific Inc / Digital Eclipse",         "Fantastic Four (JAKKS Pacific TV Game, Game-Key Ready)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // F4 (no game-keys released)
CONS( 2005, jak_just,  0,        0, jakks_gkr_1m_i2c, jak_gkr_i2c,  jakks_gkr_state, empty_init, "JAKKS Pacific Inc / Taniko",                  "Justice League (JAKKS Pacific TV Game, Game-Key Ready)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // DC (no game-keys released)
CONS( 2005, jak_dora,  0,        0, jakks_gkr_nk,     jak_gkr,      jakks_gkr_state, empty_init, "JAKKS Pacific Inc / Handheld Games",          "Dora the Explorer - Nursery Rhyme Adventure (JAKKS Pacific TV Game, Game-Key Ready)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // uses NK keys (same as Nicktoons & Spongebob) (3 released) - The upper part of this one is pink/purple.
CONS( 2005, jak_dorr,  0,        0, jakks_gkr_nk_i2c, jak_gkr_i2c,  jakks_gkr_state, empty_init, "JAKKS Pacific Inc / Handheld Games",          "Dora the Explorer - Race to Play Park (JAKKS Pacific TV Game, Game-Key Ready)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // uses NK keys (same as Nicktoons & Spongebob) (3 released) - The upper part of this one is blue
CONS( 2004, jak_nick,  0,        0, jakks_gkr_nk_i2c, jak_gkr_i2c,  jakks_gkr_state, empty_init, "JAKKS Pacific Inc / Handheld Games",          "Nicktoons (JAKKS Pacific TV Game, Game-Key Ready)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // uses NK keys
CONS( 2005, jak_sbfc,  0,        0, jakks_gkr_nk_i2c, jak_gkr_i2c,  jakks_gkr_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd",              "SpongeBob SquarePants - The Fry Cook Games (JAKKS Pacific TV Game, Game-Key Ready) (AUG 18 2005 21:31:56)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // uses NK keys
CONS( 2005, jak_sdoo,  0,        0, jakks_gkr_2m_i2c, jak_sdoo_i2c, jakks_gkr_state, empty_init, "JAKKS Pacific Inc / Jolliford Management",    "Scooby-Doo! and the Mystery of the Castle (JAKKS Pacific TV Game, Game-Key Ready)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) //  SD (no game-keys released)  (was dumped from a later unit with GameKey port missing, but internal PCB still supported it, code likely the same)
CONS( 2005, jak_disn,  0,        0, jakks_gkr_dy,     jak_gkr,      jakks_gkr_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd",              "Disney (JAKKS Pacific TV Game, Game-Key Ready) (08 FEB 2005 A)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // uses DY keys (3 released)
CONS( 2005, jak_disf,  0,        0, jakks_gkr_dy_i2c, jak_gkr_i2c,  jakks_gkr_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd",              "Disney Friends (JAKKS Pacific TV Game, Game-Key Ready) (17 MAY 2005 A)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // uses DY keys (3 released)
CONS( 2005, jak_dpr,   0,        0, jakks_gkr_dp_i2c, jak_dpr_i2c,  jakks_gkr_state, empty_init, "JAKKS Pacific Inc / 5000ft, Inc",             "Disney Princess (JAKKS Pacific TV Game, Game-Key Ready)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // uses DP keys (1 key released)
CONS( 2005, jak_dprs,  0,        0, jakks_gkr_dp_i2c, jak_dpr_i2c,  jakks_gkr_state, empty_init, "JAKKS Pacific Inc / 5000ft, Inc",             "Disney Princesses (JAKKS Pacific TV Game, Game-Key Ready)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // uses DP keys (1 key released) (unit looks identical to above, including just having 'Disney Princess' logo, but this one has the 'board game' as a frontend and a slightly different on-screen title)
// Some versions of the 'Revenge of the Sith' box art show 'Classic Battles' below the Star Wars logo
CONS( 2005, jak_sith,  0,        0, jakks_gkr_sw_i2c, jak_sith_i2c, jakks_gkr_state, empty_init, "JAKKS Pacific Inc / Griptonite Games",        "Star Wars - Revenge of the Sith (JAKKS Pacific TV Game, Game-Key Ready)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // uses SW keys (1 released)
CONS( 2005, jak_sithp, jak_sith, 0, jakks_gkr_sw_i2c, jak_sith_i2c, jakks_gkr_state, empty_init, "JAKKS Pacific Inc / Griptonite Games",        "Star Wars - Revenge of the Sith (JAKKS Pacific TV Game, Game-Key Ready, prototype)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // ^
CONS( 2006, jak_swot,  0,        0, jakks_gkr_sw_i2c, jak_sith_i2c, jakks_gkr_state, empty_init, "JAKKS Pacific Inc / Amaze Entertainment",     "Star Wars - Original Trilogy (JAKKS Pacific TV Game)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // was designed with SW keys in mind, but retail lacked the port
CONS( 2005, jak_dbz,   0,        0, jakks_gkr_1m_i2c, jak_gkr_i2c,  jakks_gkr_state, empty_init, "JAKKS Pacific Inc / Handheld Games",          "Dragon Ball Z (JAKKS Pacific TV Game, Game-Key Ready)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // DB (no game-keys released, 1 in development but cancelled)
CONS( 2005, jak_mpac,  0,        0, jakks_gkr_nm_i2c, jak_nm_i2c,   jakks_gkr_state, empty_init, "JAKKS Pacific Inc / Namco / HotGen Ltd",      "Ms. Pac-Man Collection 5-in-1 (JAKKS Pacific TV Game, Game-Key Ready) (07 FEB 2005 A SKU F)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // uses NM (3 keys available [Dig Dug, New Rally-X], [Rally-X, Pac-Man, Bosconian], [Pac-Man, Bosconian])
CONS( 2005, jak_capc,  0,        0, jakks_gkr_cc_i2c, jak_cc_i2c,   jakks_gkr_state, empty_init, "JAKKS Pacific Inc / Capcom / HotGen Ltd",     "Capcom 3-in-1 (1942, Commando, Ghosts'n Goblins) (JAKKS Pacific TV Game, Game-Key Ready) (29 MAR 2005 B)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // uses CC keys (no game-keys released)
CONS( 2005, jak_wof,   0,        0, jakks_gkr_wf_i2c, jak_wf_i2c,   jakks_gkr_state, empty_init, "JAKKS Pacific Inc / HotGen Ltd",              "Wheel of Fortune (JAKKS Pacific TV Game, Game-Key Ready) (Jul 11 2005 ORIG)",  MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // uses WF keys (no game-keys released)  analog wheel not emulated
// There is a 'Second Edition' version of Wheel of Fortune with a Gold case, GameKey port removed, and a '2' over the usual Game Key Ready logo, internals are different too, not Game-Key Ready
CONS( 2004, jak_spdm,  0,        0, jakks_gkr_mv_i2c, jak_gkr_i2c,  jakks_gkr_state, empty_init, "JAKKS Pacific Inc / Digital Eclipse",         "Spider-Man (JAKKS Pacific TV Game, Game-Key Ready)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) //  MV (1 key available)
CONS( 2005, jak_pooh,  0,        0, jakks_gkr_wp,     jak_pooh,     jakks_gkr_state, empty_init, "JAKKS Pacific Inc / Backbone Entertainment",  "Winnie the Pooh - Piglet's Special Day (JAKKS Pacific TV Game, Game-Key Ready)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // WP (no game-keys released)
CONS( 2005, jak_care,  0,        0, jakks_gkr_cb,     jak_care,     jakks_gkr_state, empty_init, "JAKKS Pacific Inc / Backbone Entertainment",  "Care Bears TV Games (JAKKS Pacific TV Game, Game-Key Ready)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // CB (no game-keys released)



spg2xx_jakks_sharp.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

// Sharp Cookie units were published by JAKKS Pacific under the 'Child Guidance' brand (battery compartments etc. still have JAKKS branding)

#include "emu.h"
#include "spg2xx.h"
#include "machine/nvram.h"


namespace {

class jakks_sharp_state : public spg2xx_game_state
{
public:
	jakks_sharp_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_state(mconfig, type, tag)
	{ }

	void base_config(machine_config& config);
	void base_config_pal(machine_config& config);

private:
};

static INPUT_PORTS_START( jak_sharp )
	PORT_START("P1")
	PORT_BIT( 0x001f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Menu / Pause")
	PORT_BIT( 0x00c0, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x0007, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // PAL/NTSC flag, set to PAL (based on other JAKKS units, possibly not the case here as not read / no effect)
	PORT_BIT( 0xfff0, IP_ACTIVE_HIGH, IPT_UNKNOWN )
INPUT_PORTS_END

void jakks_sharp_state::base_config(machine_config& config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);

	spg2xx_base(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_sharp_state::mem_map_1m);

	m_maincpu->porta_in().set_ioport("P1");
	m_maincpu->portb_in().set_ioport("P2");
	m_maincpu->portc_in().set_ioport("P3");
}

void jakks_sharp_state::base_config_pal(machine_config& config)
{
	base_config(config);

	m_maincpu->set_pal(true);
	m_screen->set_refresh_hz(50);
}


ROM_START( jsc_thom )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "shckthomas.bin", 0x000000, 0x200000, CRC(bc9549ed) SHA1(7925a8ac166f9c7a56bc5f9d4f9f774af1c92d05) )
ROM_END

ROM_START( jsc_thomu )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "shckthomasus.bin", 0x000000, 0x200000, CRC(28d0887d) SHA1(31caf4bb4e823a572010de9c58e76590c4346e92) )
ROM_END

ROM_START( jsc_spid )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "sharpcookiespiderman.bin", 0x000000, 0x200000, CRC(84cf58bf) SHA1(ac0be079c2469c9c0dea3decd7a7318806cc7ac0) )
ROM_END

ROM_START( jsc_gdg )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "shckdiego.bin", 0x000000, 0x200000, CRC(8069147a) SHA1(3f90dd3deff89d7d66b4f14b6246c2bf63c44586) )
ROM_END

ROM_START( jsc_dora )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "shckdora.bin", 0x000000, 0x200000, CRC(4a973046) SHA1(13b38b5db23169731ebf1a4657d95e34fc88b9b8) )
ROM_END

ROM_START( jsc_sdoo )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "shckscooby.bin", 0x000000, 0x200000, CRC(ce7039a4) SHA1(d5815149b75262253d03fac946b10c43e96945c0) )
ROM_END

} // anonymous namespace


// The UK version has UK specific voice actors
CONS( 2007, jsc_thom,  0,        0, base_config_pal,     jak_sharp,      jakks_sharp_state, empty_init, "JAKKS Pacific Inc / Child Guidance / Pronto Games",          "Thomas & Friends - Learning Circus Express (Sharp Cookie) (PAL, UK)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
// the US version appears to be an earlier build with no "Sir Topham Hatt" pre-game instruction screens, different narrator, no visible freight carriage in the 3rd game etc.
CONS( 2007, jsc_thomu, jsc_thom, 0, base_config,         jak_sharp,      jakks_sharp_state, empty_init, "JAKKS Pacific Inc / Child Guidance / Pronto Games",          "Thomas & Friends - Learning Circus Express (Sharp Cookie) (NTSC, US)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2007, jsc_spid,  0,        0, base_config,         jak_sharp,      jakks_sharp_state, empty_init, "JAKKS Pacific Inc / Child Guidance / Pronto Games",          "The Amazing Spider-Man - Great Math Caper (Sharp Cookie) (NTSC, US)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// from a UK unit but still says 'Zee' instead of 'Zed' for 'Z'  This does not appear to be controlled by a PAL/NTSC flag in the inputs.
CONS( 2007, jsc_gdg,   0,        0, base_config_pal,     jak_sharp,      jakks_sharp_state, empty_init, "JAKKS Pacific Inc / Child Guidance / Pronto Games",          "Go Diego Go! - Aztec ABC Adventure (Sharp Cookie) (PAL, UK)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2007, jsc_dora,  0,        0, base_config_pal,     jak_sharp,      jakks_sharp_state, empty_init, "JAKKS Pacific Inc / Child Guidance / Handheld Games",        "Dora the Explorer - Dora Saves the Mermaids (Sharp Cookie) (PAL, UK)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2007, jsc_sdoo,  0,        0, base_config_pal,     jak_sharp,      jakks_sharp_state, empty_init, "JAKKS Pacific Inc / Child Guidance / Handheld Games",        "Scooby-Doo! and The Pirate's Puzzles (Sharp Cookie) (PAL, UK)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )



spg2xx_jakks_tvtouch.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

#include "emu.h"
#include "spg2xx.h"


namespace {

class jakks_tvtouch_state : public spg2xx_game_state
{
public:
	jakks_tvtouch_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_state(mconfig, type, tag)
	{ }

	void tvtouch(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint16_t porta_r(offs_t offset, uint16_t mem_mask = ~0);
	uint16_t portb_r(offs_t offset, uint16_t mem_mask = ~0);
	uint16_t portc_r();
	void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
	void portb_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
	void portc_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
	void spi_w(int state);

	uint8_t m_spi_bit = 0;
	uint8_t m_spi_val = 0;
};

static INPUT_PORTS_START( tvtouch )
	PORT_START("P1")
	PORT_DIPNAME( 0x0001, 0x0001, "P1" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P2")
	PORT_DIPNAME( 0x0001, 0x0001, "P2" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P3")
	PORT_DIPNAME( 0x0001, 0x0001, "P3" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON1 ) // might not only be a button input, check code
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
INPUT_PORTS_END

void jakks_tvtouch_state::machine_start()
{
	spg2xx_game_state::machine_start();

	save_item(NAME(m_spi_bit));
	save_item(NAME(m_spi_val));
}

void jakks_tvtouch_state::machine_reset()
{
	spg2xx_game_state::machine_reset();

	m_spi_bit = 7;
	m_spi_val = 0x00;
}

uint16_t jakks_tvtouch_state::porta_r(offs_t offset, uint16_t mem_mask)
{
	logerror("%s: porta_r: %04x & %04x\n", machine().describe_context(), 0, mem_mask);
	return 0;
}

uint16_t jakks_tvtouch_state::portb_r(offs_t offset, uint16_t mem_mask)
{
	logerror("%s: portb_r: %04x & %04x\n", machine().describe_context(), 0, mem_mask);
	return 0;
}

uint16_t jakks_tvtouch_state::portc_r()
{
	uint16_t data = (m_i2cmem->read_sda() & 1) | (m_io_p3->read() & 0xfffe);
	return data;
}

void jakks_tvtouch_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: porta_w: %04x & %04x\n", machine().describe_context(), data, mem_mask);
}

void jakks_tvtouch_state::portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: portb_w: %04x & %04x\n", machine().describe_context(), data, mem_mask);
}

void jakks_tvtouch_state::portc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (BIT(mem_mask, 1))
		m_i2cmem->write_scl(BIT(data, 1));
	if (BIT(mem_mask, 0))
		m_i2cmem->write_sda(BIT(data, 0));
}

void jakks_tvtouch_state::spi_w(int state)
{
	m_spi_val |= state << m_spi_bit;
	if (m_spi_bit == 0)
	{
		logerror("Received via SPI: %02x\n", m_spi_val);
		m_spi_bit = 7;
		m_spi_val = 0x00;
		/*static uint8_t s_value = 0x40;
		for (int bit = 7; bit >= 0; bit--)
		{
		    m_maincpu->spi_rx(BIT(s_value, bit));
		}
		s_value ^= 0x80;*/
	}
	else
	{
		m_spi_bit--;
	}
}

void jakks_tvtouch_state::tvtouch(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &jakks_tvtouch_state::mem_map_2m);
	m_maincpu->spi_tx().set(FUNC(jakks_tvtouch_state::spi_w));
	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(jakks_tvtouch_state::porta_r));
	m_maincpu->portb_in().set(FUNC(jakks_tvtouch_state::portb_r));
	m_maincpu->portc_in().set(FUNC(jakks_tvtouch_state::portc_r));
	m_maincpu->porta_out().set(FUNC(jakks_tvtouch_state::porta_w));
	m_maincpu->portb_out().set(FUNC(jakks_tvtouch_state::portb_w));
	m_maincpu->portc_out().set(FUNC(jakks_tvtouch_state::portc_w));

	I2C_24C04(config, m_i2cmem, 0);
}

ROM_START( tvtchsw )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "touchstarwars.bin", 0x000000, 0x400000, CRC(db5ccc31) SHA1(786af933ef1fb644faf8eed935c448b93296bc33) )
ROM_END

ROM_START( tvtchspd )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "touchspiderman.bin", 0x000000, 0x400000, CRC(7646f265) SHA1(3b029d9d1dc57f4cae809f177205d8372d722461) )
ROM_END

ROM_START( tvtchsb )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "touchspongebob.bin", 0x000000, 0x400000, CRC(a6d7f544) SHA1(fc15b3d2bbbd951d82c81bef59f45506d6c4e2e3) )
ROM_END

} // anonymous namespace


// TV Touch Games (these are re-release versions of classic JAKKS games but using a touchpad controller)
CONS( 2012, tvtchsw,  0, 0, tvtouch, tvtouch, jakks_tvtouch_state, empty_init, "JAKKS Pacific Inc / Code Mystics", "TV Touch Games: Star Wars Original Trilogy", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // Touch games have 24C04
CONS( 2012, tvtchspd, 0, 0, tvtouch, tvtouch, jakks_tvtouch_state, empty_init, "JAKKS Pacific Inc / Code Mystics", "TV Touch Games: Spider-Man in Villain Round-Up", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2012, tvtchsb,  0, 0, tvtouch, tvtouch, jakks_tvtouch_state, empty_init, "JAKKS Pacific Inc / Code Mystics", "TV Touch Games: SpongeBob SquarePants Jellyfish Dodge", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
// Cut the Rope was planned but never released



spg2xx_lexibook.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

// JungleTac developed systems sold by Performance Designed Products, Lexibook etc.
// these are mostly handhelds with built in screens, and seem to be newer(?) revisions of the software
// of note, the way the audio is stored for Tiger Rescue has been updated and the core does not handle it properly at the moment

#include "emu.h"
#include "spg2xx.h"


namespace {

class spg2xx_lexizeus_game_state : public spg2xx_game_state
{
public:
	spg2xx_lexizeus_game_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_state(mconfig, type, tag)
	{ }

	void lexizeus(machine_config &config);

	void init_zeus();

protected:
	//virtual void machine_start() override ATTR_COLD;
	//virtual void machine_reset() override ATTR_COLD;
};


class spg2xx_lexiseal_game_state : public spg2xx_lexizeus_game_state
{
public:
	spg2xx_lexiseal_game_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_lexizeus_game_state(mconfig, type, tag)
	{ }

	void lexiseal(machine_config& config);

protected:
	//virtual void machine_start() override ATTR_COLD;
	//virtual void machine_reset() override ATTR_COLD;

	virtual void portb_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
};

class spg2xx_vsplus_game_state : public spg2xx_lexizeus_game_state
{
public:
	spg2xx_vsplus_game_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_lexizeus_game_state(mconfig, type, tag)
	{ }

	void vsplus(machine_config &config);

	void init_vsplus();

protected:
	//virtual void machine_start() override ATTR_COLD;
	//virtual void machine_reset() override ATTR_COLD;

	virtual void portb_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
};



static INPUT_PORTS_START( lexizeus ) // how many buttons does this have?  I accidentally entered a secret test mode before that seemed to indicate 6, but can't get there again
	PORT_START("P1")
	PORT_DIPNAME( 0x0001, 0x0001, "P1" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Button 1") // shoot in Tiger Rescue & Deep
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("Pause")

	PORT_START("P2")
	PORT_DIPNAME( 0x0001, 0x0001, "P2" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Button 1 Rapid") // same function as button 1 but with rapid toggle on/off
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Button 2 Rapid") // same function as button 2 but with rapid toggle on/off
	PORT_DIPNAME( 0x0004, 0x0004, "P3" )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Button 2") // toggles ball / number view in pool
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
INPUT_PORTS_END



static INPUT_PORTS_START( lexiseal )
	PORT_START("P1")
	PORT_BIT( 0x00ff, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_UNUSED ) // doesn't respond as 'select'
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_BUTTON3 ) // pause / start

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON4 )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON5 )
	PORT_BIT( 0xfffc, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( vsplus )
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("B")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("A")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("Pause")
	PORT_BIT( 0xff00, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Rapid A")
	PORT_BIT( 0x0006, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Rapid B")
	PORT_BIT( 0xfff0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END


void spg2xx_lexiseal_game_state::portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: portb_w %04x %04x\n", machine().describe_context(), data, mem_mask);

	// mem_mask is set to fc when writing banks

	if ((data&mem_mask) == 0x5c)
		switch_bank(0);
	else if ((data&mem_mask) == 0xdc)
		switch_bank(1);
	else if ((data&mem_mask) == 0x7c)
		switch_bank(2);
}


void spg2xx_vsplus_game_state::portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (mem_mask & 0x0080)
	{
		if (data & 0x0080)
			switch_bank(1);
		else
			switch_bank(0);
	}
}

void spg2xx_lexiseal_game_state::lexiseal(machine_config &config)
{
	non_spg_base(config);
	m_maincpu->portb_out().set(FUNC(spg2xx_lexiseal_game_state::portb_w));
	m_maincpu->porta_in().set_ioport("P1");
	m_maincpu->portb_in().set_ioport("P2");
	m_maincpu->portc_in().set_ioport("P3");
}

void spg2xx_lexizeus_game_state::lexizeus(machine_config &config)
{
	non_spg_base(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_lexiseal_game_state::mem_map_4m);
	m_maincpu->porta_in().set_ioport("P1");
	m_maincpu->portb_in().set_ioport("P2");
	m_maincpu->portc_in().set_ioport("P3");
}

void spg2xx_vsplus_game_state::vsplus(machine_config &config)
{
	non_spg_base(config);
	m_maincpu->portb_out().set(FUNC(spg2xx_vsplus_game_state::portb_w));
	m_maincpu->porta_in().set_ioport("P1");
	m_maincpu->portb_in().set_ioport("P2");
	m_maincpu->portc_in().set_ioport("P3");

	// output was PAL when connected to TV at least
	m_maincpu->set_pal(true);
	m_screen->set_refresh_hz(50);
}


void spg2xx_lexizeus_game_state::init_zeus()
{
	uint16_t *ROM = (uint16_t*)memregion("maincpu")->base();
	int size = memregion("maincpu")->bytes();

	for (int i = 0x8000 / 2; i < size / 2; i++)
	{
		// global 16-bit xor
		ROM[i] = ROM[i] ^ 0x8a1d;

		// 4 single bit conditional xors
		if (ROM[i] & 0x0020)
			ROM[i] ^= 0x0100;

		if (ROM[i] & 0x0040)
			ROM[i] ^= 0x1000;

		if (ROM[i] & 0x4000)
			ROM[i] ^= 0x0001;

		if (ROM[i] & 0x0080)
			ROM[i] ^= 0x0004;

		// global 16-bit bitswap
		ROM[i] = bitswap<16>(ROM[i], 7, 12, 9, 14, 4, 6, 0, 10, 15, 1, 3, 2, 5, 13, 8, 11);
	}
}

void spg2xx_vsplus_game_state::init_vsplus()
{
	uint16_t *ROM = (uint16_t*)memregion("maincpu")->base();
	int size = memregion("maincpu")->bytes();

	decrypt_ac_ff(ROM, size);
}

ROM_START( lexizeus )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "lexibook1g900us.bin", 0x0000, 0x800000, CRC(c2370806) SHA1(cbb599c29c09b62b6a9951c724cd9fc496309cf9))
ROM_END

ROM_START( arcade3d )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "arcade3d.u3", 0x0000, 0x800000, CRC(130843a5) SHA1(f6494a34d162e702121cf71d384a4e57e0113498) )
ROM_END

ROM_START( vsplus )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "vsplus.bin", 0x0000, 0x1000000, CRC(2b13d2cc) SHA1(accae7606d83a313b8ec0232d2d67b63c9c617af) )
ROM_END

ROM_START( lexiseal )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "lexibook_seal.bin", 0x0000, 0x1000000, CRC(3529f154) SHA1(f5f142600c6b2d037b97e007364ea2fa228e0163) )
ROM_END

ROM_START( discpal )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "disneyhh.bin", 0x0000, 0x400000, CRC(5fb7f32e) SHA1(795c992826ad4ac66d5438207f1c9b48f9fadc44) )
ROM_END

ROM_START( disppal )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "vgpocketdisney.u3", 0x0000, 0x400000, CRC(051bd073) SHA1(e453677437206e11fb50b8b86853e466978338a2) )
ROM_END

/*

VG Caplet ROM pinout from Sean Riddle (2 ROMs in single package)

BSIC M6MLT947 pinout
wide SOP64 footprint
end with 2 circles denotes pin 1 end
24MB with 2 chip enables
/CE1 L, CE2 H is 16MB, /CE1 H, CE2 L is 8MB
do not drive /CE1 and /CE2 both L at the same time

1  N/C          64 N/C
2  A15          63 A16
3  A14          62 /CE1
4  A13          61 VCC
5  A12          60 N/C
6  A11          59 GND
7  A10          58 N/C
8  A9           57 D15
9  A8           56 D7
10 A21          55 D14
11 A20          54 D6
12 N/C          53 D13
13 N/C          52 D5
14 VCC          51 D12
15 VCC          50 D4
16 N/C          49 GND
17 N/C          48 VCC
18 N/C          47 D11
19 N/C          46 D3
20 A22          45 D10
21 also A21     44 D2
22 A19          43 D9
23 A18          42 D1
24 A17          41 D8
25 A7           40 D0
26 A6           39 /OE
27 A5           38 GND
28 A4           37 N/C
29 A3           36 /CE2
30 A2           35 A0
31 A1           34 N/C
32 N/C          33 N/C

*/

ROM_START( vgcaplet )
	ROM_REGION( 0x2000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "capleth.bin", 0x0000000, 0x1000000, CRC(f6cad3a7) SHA1(54167ab7651cf7a758ae36c443e74d3919e7fd6d) )
	ROM_LOAD16_WORD_SWAP( "capletl.bin", 0x1000000, 0x0800000, CRC(1ae2fa49) SHA1(62f41d45da011c1dfb35c1111a478798c2b33aaf) )
ROM_END

ROM_START( vgcap35 )
	ROM_REGION( 0x2000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "vgpocket.bin", 0x0000000, 0x1000000, CRC(b4dd781b) SHA1(b060c83c2f96a2b78f075d1c8143f654016ff0ec) )
	ROM_RELOAD(0x1000000,0x1000000)
ROM_END

} // anonymous namespace


// these all have the same ROM scrambling

CONS( 200?, lexizeus,    0,     0,        lexizeus,     lexizeus, spg2xx_lexizeus_game_state, init_zeus, "Lexibook / JungleTac", "Zeus IG900 20-in-1 (US?)",          MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // bad sound and some corrupt bg tilemap entries in Tiger Rescue, verify ROM data (same game runs in Zone 60 without issue)

CONS( 200?, arcade3d,    0,     0,        lexizeus,     lexiseal, spg2xx_lexizeus_game_state, init_zeus, "Millennium 2000 GmbH / JungleTac", "Millennium Arcade 3D 15-in-1",          MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // bad sound and some corrupt bg tilemap entries in Tiger Rescue, verify ROM data (same game runs in Zone 60 without issue)

CONS( 200?, vsplus,      0,     0,        vsplus,     vsplus, spg2xx_vsplus_game_state, init_vsplus, "<unknown> / JungleTac", "Vs Power Plus 30-in-1",          MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )


CONS( 200?, lexiseal,    0,     0,        lexiseal,     lexiseal, spg2xx_lexiseal_game_state, init_zeus, "Lexibook / Sit Up Limited / JungleTac", "Seal 50-in-1",          MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // also has bad sound in Tiger Rescue, but no corrupt tilemap
// There are versions of the Seal 50-in-1 that actually show Lexibook on the boot screen rather than it just being on the unit.  The Seal name was also used for some VT systems

CONS( 2006, discpal,     0,     0,        lexizeus,     lexiseal, spg2xx_lexizeus_game_state, init_zeus, "Performance Designed Products / Disney / Jungle Soft", "Disney Game It! Classic Pals",          MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2006, disppal,     0,     0,        lexizeus,     lexiseal, spg2xx_lexizeus_game_state, init_zeus, "Performance Designed Products / Disney / Jungle Soft", "Disney Game It! Princess Pals",         MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2006, vgcaplet,    0,     0,        lexiseal,     lexiseal, spg2xx_lexiseal_game_state, init_zeus, "Performance Designed Products (licensed by Taito / Data East) / JungleTac", "VG Pocket Caplet Fast Acting 50-in-1",          MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2006, vgcap35,     0,     0,        lexiseal,     lexiseal, spg2xx_lexiseal_game_state, init_zeus, "Performance Designed Products (licensed by Taito / Data East) / JungleTac", "VG Pocket Caplet Fast Acting 35-in-1",          MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )



spg2xx_mysprtch.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood


#include "emu.h"
#include "spg2xx.h"


namespace {

class spg2xx_game_mysprt_plus_state : public spg2xx_game_state
{
public:
	spg2xx_game_mysprt_plus_state(const machine_config& mconfig, device_type type, const char* tag) :
		spg2xx_game_state(mconfig, type, tag),
		m_romregion(*this, "maincpu")
	{ }

	void mysprtch(machine_config& config);
	void mgt20in1(machine_config& config);

	void init_mysprtcp();
	void init_mgt20in1();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void device_post_load() override;

	void mem_map_mysprtch(address_map &map) ATTR_COLD;

	virtual void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask) override;

	int m_romsize = 0;

	int m_mysprtch_rombase = 0;
	uint16_t m_prev_porta = 0;
	int m_bank_enabled = 0;

private:
	uint16_t mysprtch_rom_r(offs_t offset);
	required_region_ptr<uint16_t> m_romregion;
};

class spg2xx_game_mysprt_orig_state : public spg2xx_game_mysprt_plus_state
{
public:
	spg2xx_game_mysprt_orig_state(const machine_config& mconfig, device_type type, const char* tag) :
		spg2xx_game_mysprt_plus_state(mconfig, type, tag)
	{ }

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask) override;

private:
};

void spg2xx_game_mysprt_plus_state::device_post_load()
{
	// load state can change the bank, so we must invalide cache
	m_maincpu->invalidate_cache();
}


uint16_t spg2xx_game_mysprt_plus_state::mysprtch_rom_r(offs_t offset)
{
	// due to granularity of rom bank this manual method is safer
	return m_romregion[(offset + (m_mysprtch_rombase * 0x200000)) & (m_romsize-1)];
}

void spg2xx_game_mysprt_plus_state::mem_map_mysprtch(address_map &map)
{
	map(0x000000, 0x3fffff).r(FUNC(spg2xx_game_mysprt_plus_state::mysprtch_rom_r));
}

void spg2xx_game_mysprt_plus_state::machine_start()
{
	spg2xx_game_state::machine_start();

	m_romsize = (memregion("maincpu")->bytes()/2);

	save_item(NAME(m_mysprtch_rombase));
	save_item(NAME(m_prev_porta));
	save_item(NAME(m_bank_enabled));
}

void spg2xx_game_mysprt_plus_state::machine_reset()
{
	spg2xx_game_state::machine_reset();

	m_mysprtch_rombase = 2;
	m_prev_porta = 0x0000;
	m_bank_enabled = 0;

	m_maincpu->invalidate_cache();
	m_maincpu->reset();
}

void spg2xx_game_mysprt_orig_state::machine_reset()
{
	spg2xx_game_state::machine_reset();

	m_mysprtch_rombase = 3;
	m_prev_porta = 0x0000;
	m_bank_enabled = 0;

	m_maincpu->invalidate_cache();
	m_maincpu->reset();
}

static INPUT_PORTS_START( mysprtch ) // Down + Button 1 and Button 2 for service mode
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("P1 RF Key")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("P1 LF Wave")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("P1 LF Key") // doesn't show in test mode but still read at times
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 RF Wave")

	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("P1 RF Wave")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 RF Key")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 LF Wave")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("P2 LF Key") // doesn't show in test mode but still read at times
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P3")
	PORT_DIPNAME( 0x0001, 0x0001, "P3" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( mgt20in1 ) // this seems to expect rotated controls by default (although the bowling expects you to rotate the controller to match mysprtchl)
	PORT_INCLUDE(mysprtch)

	PORT_MODIFY("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("P1 RF Key")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("P1 LF Wave")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("P1 LF Key") // doesn't show in test mode but still read at times
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 RF Wave")
INPUT_PORTS_END


void spg2xx_game_mysprt_orig_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	// this seems like nice clean logic, based on writes and how the port direction is set,
	// mysprtch and mysptqvc work fine with this logic, but mysprtcp is more problematic, especially in test mode, see other function

	logerror("%s: porta_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
		(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
		(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
		(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
		(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
		(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
		(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
		(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
		(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
		(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
		(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
		(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
		(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
		(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
		(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
		(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
		(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');

	int oldrombank = m_mysprtch_rombase;

	if (mem_mask & 0x0400)
	{
		if (data & 0x0400)
			m_mysprtch_rombase |= 1;
		else
			m_mysprtch_rombase &= ~1;
	}

	if (mem_mask & 0x0800)
	{
		if (data & 0x0800)
			m_mysprtch_rombase |= 2;
		else
			m_mysprtch_rombase &= ~2;
	}

	if (mem_mask & 0x0200)
	{
		if (data & 0x0200)
			m_mysprtch_rombase &= ~4; // inverted
		else
			m_mysprtch_rombase |= 4;
	}

	if (oldrombank != m_mysprtch_rombase)
		m_maincpu->invalidate_cache();

	m_prev_porta = data;
}




void spg2xx_game_mysprt_plus_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	// this is very ugly guesswork based on use and testmode
	// what is even more problematic here is that mysprtcp has mem_mask as 0x0000, ie all ports are set to INPUT mode?! so we can't use mem_mask to
	// see if a port is active.  Using mem_mask works fine for the other sets, as port direction gets set as expected by the spg2xx_io core

	logerror("%s: porta_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
		(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
		(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
		(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
		(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
		(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
		(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
		(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
		(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
		(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
		(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
		(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
		(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
		(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
		(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
		(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
		(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');

	int oldrombank = m_mysprtch_rombase;

	if ((m_prev_porta & 0x00ff) != (data & 0x00ff))
	{
		logerror("lower changed\n");

		if ((data & 0x00ff) == 0x0000)
		{
			m_bank_enabled = 1;
			logerror("bank enabled\n");
		}
		else
		{
			m_bank_enabled = 0;
			logerror("bank disabled\n");
		}
	}

	if ((data & 0xff00) == 0x1e00) // gets written in test mode and expects the default bank to be restored
	{
		m_mysprtch_rombase = 2;
	}
	else if (m_bank_enabled)
	{
		logerror("potential bank %02x\n", data >> 9);

		int bank = 0;
		bank |= (data & 0x0400) ? 1 : 0;
		bank |= (data & 0x0800) ? 2 : 0;
		bank |= (data & 0x0200) ? 0 : 4; // inverted

		m_mysprtch_rombase = bank;
	}

	if (oldrombank != m_mysprtch_rombase)
		m_maincpu->invalidate_cache();

	m_prev_porta = data;
}


void spg2xx_game_mysprt_plus_state::mysprtch(machine_config& config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_game_mysprt_plus_state::mem_map_mysprtch);
	m_maincpu->set_force_no_drc(true); // uses JVS opcode, not implemented in recompiler

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(spg2xx_game_mysprt_plus_state::base_porta_r));
	m_maincpu->portb_in().set(FUNC(spg2xx_game_mysprt_plus_state::base_portb_r));
	m_maincpu->portc_in().set(FUNC(spg2xx_game_mysprt_plus_state::base_portc_r));

	m_maincpu->porta_out().set(FUNC(spg2xx_game_mysprt_plus_state::porta_w));
}

void spg2xx_game_mysprt_plus_state::mgt20in1(machine_config& config)
{
	mysprtch(config);

	m_maincpu->set_pal(true);
	m_screen->set_refresh_hz(50);
}

void spg2xx_game_mysprt_plus_state::init_mysprtcp()
{
	uint16_t *ROM = (uint16_t*)memregion("maincpu")->base();
	int size = memregion("maincpu")->bytes();

	decrypt_ac_ff(ROM, size);
}



void spg2xx_game_mysprt_plus_state::init_mgt20in1()
{
	uint16_t *ROM = (uint16_t*)memregion("maincpu")->base();
	int size = memregion("maincpu")->bytes();

	for (int i = 0; i < size/2; i++)
	{
		ROM[i] = ROM[i] ^ 0x4ec4;

		uint16_t res = 0;

		if (ROM[i] & 0x0001) res ^= 0x0040;
		if (ROM[i] & 0x0002) res ^= 0x0002;
		if (ROM[i] & 0x0004) res ^= 0x0020;
		if (ROM[i] & 0x0008) res ^= 0x1200; // 2 bits changed

		if (ROM[i] & 0x0010) res ^= 0x0100;
		if (ROM[i] & 0x0020) res ^= 0x4000;
		if (ROM[i] & 0x0040) res ^= 0x0010;
		if (ROM[i] & 0x0080) res ^= 0x0800;

		if (ROM[i] & 0x0100) res ^= 0x4400; // 2 bits changed
		if (ROM[i] & 0x0200) res ^= 0x0080;
		if (ROM[i] & 0x0400) res ^= 0x0001;
		if (ROM[i] & 0x0800) res ^= 0x2000;

		if (ROM[i] & 0x1000) res ^= 0x0004;
		if (ROM[i] & 0x2000) res ^= 0x000a; // 2 bits changed
		if (ROM[i] & 0x4000) res ^= 0x0200;
		if (ROM[i] & 0x8000) res ^= 0x8010; // 2 bits changed

		ROM[i] = res;
	}
}

ROM_START( mysprtch )
	ROM_REGION( 0x2000000, "maincpu", ROMREGION_ERASE00 ) // SOP64 M6MLT947, has two /CE lines so internally this '24MByte / 192Mbit' chip is likely 2 ROM dies in a single package
	ROM_LOAD16_WORD_SWAP( "senariomysportschallengesop64h.bin", 0x0000000, 0x1000000, CRC(3714df21) SHA1(f725dad48b9dfeba188879a6fd28652a7330d3e5) )
	ROM_LOAD16_WORD_SWAP( "senariomysportschallengesop64l.bin", 0x1000000, 0x0800000, CRC(0f71099f) SHA1(6e4b9ce329edbb6f0b962cb5669e04c6bd209596) )
ROM_END

ROM_START( mysptqvc )
	ROM_REGION( 0x2000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "qvcmysportschallenge.bin", 0x0000000, 0x2000000, CRC(04783adc) SHA1(a173145ec307fc12f231d3e3f6efa60f8c2f0c89) ) // last 8MB is unused
ROM_END

ROM_START( mysprtcp )
	ROM_REGION( 0x2000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "mysportschallengeplus.bin", 0x0000, 0x2000000, CRC(6911d19c) SHA1(c71bc38595e5505434395b6d59320caabfc7bce3) )
ROM_END

ROM_START( mgt20in1 )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "m29gl128.u2", 0x000000, 0x1000000, CRC(41d594e3) SHA1(351890455bed28bcaf173d8fd9a4cc997c404d94) )
ROM_END

} // anonymous namespace


// Original release, with 24MB ROM package, Unit has Black surround to power button
CONS( 200?, mysprtch,  0, 0, mysprtch, mysprtch, spg2xx_game_mysprt_orig_state, init_mysprtcp, "Senario / V-Tac Technology Co Ltd.",                "My Sports Challenge (5-in-1 version)",  MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
// from a QVC licensed unit with a different physical shape etc. uses a 32MByte rom with only 24MByte used
CONS( 200?, mysptqvc,  0, 0, mysprtch, mysprtch, spg2xx_game_mysprt_orig_state, init_mysprtcp, "Senario / V-Tac Technology Co Ltd. (QVC license)",  "My Sports Challenge (6-in-1 version, QVC license)",  MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
// Unit is same shape as regular (non-QVC release) but with Blue surround to power button. Box shows 'Wireless Sports Plus' but title screen shots "My Sports Challenge Plus"  Appears to be V-Tac developed as it has the common V-Tac test mode.
CONS( 200?, mysprtcp,  0, 0, mysprtch, mysprtch, spg2xx_game_mysprt_plus_state, init_mysprtcp, "Senario / V-Tac Technology Co Ltd.",                "My Sports Challenge Plus / Wireless Sports Plus",  MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
// 2009 date on PCB, not actually in German, so maybe sold under different brands?
CONS( 2009, mgt20in1,  0, 0, mgt20in1, mgt20in1, spg2xx_game_mysprt_plus_state, init_mgt20in1, "MGT",                                               "MGT 20-in-1 TV-Spielekonsole (Germany)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )



spg2xx_pdc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

#include "emu.h"
#include "spg2xx.h"


namespace {

class spg2xx_pdc_game_state : public spg2xx_game_state
{
public:
	spg2xx_pdc_game_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_state(mconfig, type, tag),
		m_numbanks(-1)
	{ }

	void pdc100(machine_config& config);
	void pdc_tactile(machine_config& config);

	void init_pdc40t();
	void init_pdc150t();
	void init_pdc200();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;

	uint16_t touch_xpos_r()
	{ // >= 0x800 = right of screen
		uint16_t ret = ioport("AD1")->read();
		return ret;
	}

	uint16_t touch_ypos_r()
	{ // >= 0x800 = top of screen
		uint16_t ret = ioport("AD2")->read();
		return ret;
	}

	int m_numbanks;
};

class spg2xx_pdc150t_game_state : public spg2xx_pdc_game_state
{
public:
	spg2xx_pdc150t_game_state(const machine_config& mconfig, device_type type, const char* tag) :
		spg2xx_pdc_game_state(mconfig, type, tag)
	{ }

protected:
	virtual void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
};

class spg2xx_pdc200_game_state : public spg2xx_pdc_game_state
{
public:
	spg2xx_pdc200_game_state(const machine_config& mconfig, device_type type, const char* tag) :
		spg2xx_pdc_game_state(mconfig, type, tag)
	{ }

protected:
	virtual void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
};

static INPUT_PORTS_START( pdc100 )
	PORT_START("P1")
	PORT_BIT( 0x00ff, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Left Trigger")
	PORT_BIT( 0x0e00, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Right Trigger")
	PORT_BIT( 0xe000, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Pause")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0xff00, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN )


INPUT_PORTS_END


static INPUT_PORTS_START( pdc_tactile )
	PORT_START("P1")
	PORT_BIT( 0x01ff, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("A")
	PORT_BIT( 0xfc00, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Touch")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("B")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Pause / Menu")
	PORT_BIT( 0x0038, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // if this bit flips you get black screen (soft power off?)
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0600, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0xe000, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("AD1")
	PORT_BIT(0xfff, 0x800, IPT_LIGHTGUN_X) PORT_CROSSHAIR(X, 1.10f, -0.055f, 0) PORT_MINMAX(0x000, 0xfff) PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_PLAYER(1)

	PORT_START("AD2")
	PORT_BIT(0xfff, 0x800, IPT_LIGHTGUN_Y) PORT_INVERT PORT_CROSSHAIR(Y, 1.148f, -0.1f, 0) PORT_MINMAX(0x000, 0xfff) PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_PLAYER(1)

	PORT_START("AD3")
	PORT_BIT( 0x003f, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // unk, causes powerdown
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // unk, causes powerdown
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // unk, causes powerdown
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // unk, causes powerdown
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // unk, causes powerdown
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // unk, causes powerdown
INPUT_PORTS_END

static INPUT_PORTS_START( vjpp2 )
	PORT_START("P1")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Pause")
	PORT_BIT( 0xff80, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNKNOWN )
INPUT_PORTS_END

void spg2xx_pdc_game_state::machine_start()
{
	spg2xx_game_state::machine_start();
	m_numbanks = memregion("maincpu")->bytes() / 0x800000;
}

void spg2xx_pdc_game_state::machine_reset()
{
	m_current_bank = -1;
	switch_bank(m_numbanks - 1); // pdc100 must boot from upper bank
	m_maincpu->reset();
}

// pdc100 simply writes 0000 at times during bootup while initializing stuff, which causes an invalid bankswitch mid-code execution
// pdc200 does similar
// direction bits don't appear to be being set correctly on port writes (similar issue to many other systems)

void spg2xx_pdc_game_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if ((m_maincpu->pc() < 0x2800) && (data & 0xff00))
	{
		int bank = data & 0x7;
		bank &= (m_numbanks - 1);
		switch_bank(bank);
	}
}

void spg2xx_pdc150t_game_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if ((m_maincpu->pc() < 0x2800) && (data & 0xff00))
	{
		int bank = data & 0x7;
		bank |= (data & 0x0100) ? 8 : 0;
		bank &= (m_numbanks - 1);
		switch_bank(bank);
	}
}

void spg2xx_pdc200_game_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if ((m_maincpu->pc() < 0x2800) && (data & 0xff00))
	{
		int bank = data & 0x7;
		bank |= (data & 0x8000) ? 8 : 0;
		bank &= (m_numbanks - 1);
		switch_bank(bank);
	}
}

void spg2xx_pdc_game_state::pdc100(machine_config &config)
{
	non_spg_base(config);
	m_maincpu->porta_out().set(FUNC(spg2xx_pdc_game_state::porta_w));
	m_maincpu->porta_in().set_ioport("P1");
	m_maincpu->portb_in().set_ioport("P2");
	m_maincpu->portc_in().set_ioport("P3"); // not used?

}

void spg2xx_pdc_game_state::pdc_tactile(machine_config& config)
{
	pdc100(config);

	m_maincpu->porta_in().set_ioport("P1");
	m_maincpu->portb_in().set_ioport("P2");
	m_maincpu->portc_in().set_ioport("P3");

	m_maincpu->adc_in<0>().set(FUNC(spg2xx_pdc_game_state::touch_xpos_r));
	m_maincpu->adc_in<1>().set(FUNC(spg2xx_pdc_game_state::touch_ypos_r));
	m_maincpu->adc_in<2>().set_ioport("AD3");
}


void spg2xx_pdc_game_state::init_pdc40t()
{
	uint8_t *src = memregion("maincpu")->base();
	int len = memregion("maincpu")->bytes();

	std::vector<u8> buffer(len);

	for (int i = 0; i < len; i++)
	{
		buffer[i] = src[bitswap<26>(i, 18, 20, 16, 17, 24, 25, 19, 23, 22, 21, 5, 6, 7, 8, 13, 15, 14, 9, 10, 12, 11, 1, 2, 3, 4, 0)];
	}
	std::copy(buffer.begin(), buffer.end(), &src[0]);
}


void spg2xx_pdc_game_state::init_pdc150t()
{
	uint16_t *src = (uint16_t*)memregion("maincpu")->base();
	int len = memregion("maincpu")->bytes();

	for (int i = 0; i < len/2; i++)
	{
		src[i] = bitswap<16>(src[i], 3^8,11^8,2^8,10^8,1^8,9^8,0^8,8^8, 12^8,4^8,13^8,5^8,14^8,6^8,15^8,7^8 );
	}

#if 0
	{
		for (int bank = 0; bank < 16; bank++)
		{
			const int length = 0x800000 - 0x10;
			const int start = (0x800000 * bank) + 0x10;
			const uint8_t* rom = memregion("maincpu")->base();

			uint32_t checksum = 0x00000000;
			// the first 0x10 bytes are where the "chksum:xxxxxxxx " string is listed, so skip over them
			for (int i = start; i < start + length; i++)
			{
				checksum += rom[i];
			}

			printf("Calculated Byte Sum of bytes is %08x)\n", checksum);


			FILE *fp;
			char filename[256];
			sprintf(filename,"decrypted_%s_%d", machine().system().name, bank);
			fp=fopen(filename, "w+b");
			if (fp)
			{
				fwrite(&rom[0x800000*bank], 0x800000, 1, fp);
				fclose(fp);
			}
		}
	}
#endif
}




ROM_START( pdc100 )
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASE00 )
	// only 1st half of this is used "Jumper resistor (0 ohm) that short A25 to ground"
	// 2nd half just contains what seems to be random garbage
	ROM_LOAD16_WORD_SWAP( "pdc100.bin", 0x000000, 0x4000000, CRC(57285b49) SHA1(cfb4be7877ec263d24063a004c56985db5c0f4e2) )
	ROM_IGNORE(0x4000000)
ROM_END

ROM_START( pdc30p )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "pdc_pink.u2", 0x000000, 0x1000000, CRC(cd12c6c3) SHA1(5b423d589358dd53a2e5765bcb94da2d44200e56) )
ROM_END

ROM_START( pdc50 )
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "pdc50_7050.u3", 0x000000, 0x1000000, CRC(9b4eb348) SHA1(81d2ff5af7b6dc5e1f9277b45f259762e7d24cce) )
	ROM_RELOAD(0x1000000,0x1000000)
	ROM_RELOAD(0x2000000,0x1000000)
	ROM_RELOAD(0x3000000,0x1000000)
ROM_END

ROM_START( pdc200 )
	ROM_REGION( 0x8000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "pdc200.bin", 0x000000, 0x8000000, CRC(9da99f0f) SHA1(0dda8a3deb794e493685d3d41790ee371f9b736e) )
ROM_END

ROM_START( pdc40t )
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "pdc_5060.bin", 0x000000, 0x4000000, CRC(28e0c16e) SHA1(fef4af00c737fab2716eef550badbbe0628f26a8) )
ROM_END

ROM_START( pdc150t )
	ROM_REGION( 0x8000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "pdc5070.bin", 0x000000, 0x8000000, CRC(b10e9f29) SHA1(551d62a9ffc18159f7ace12e4363223e0c5cf3c8) )
ROM_END

ROM_START( ouipdc )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "pdc_ouioui.u2", 0x000000, 0x1000000, CRC(ddcd03ab) SHA1(de0695c240fdc2edc67694ceacaed093e6693767) )
ROM_END

ROM_START( tmntpdc )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "pdc_turtles.bin", 0x000000, 0x800000, CRC(ee9e70a3) SHA1(7620f1b7aeaec8032faa8eb7552f775e8d6d14ba) )
ROM_END

ROM_START( dorapdc )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "pdc_dora_5030.bin", 0x000000, 0x800000, CRC(cea549ad) SHA1(b6ac8ea186d7c624451dd6121932cecb38c1f25f) )
ROM_END

ROM_START( vjpp1 )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "29lv160.u2", 0x000000, 0x200000, CRC(acadbcc3) SHA1(3cf6fddcfdd1f858e775976f690b32deef1de67f) )
ROM_END

ROM_START( vjpp2 )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "29lv320at.u2", 0x000000, 0x400000, CRC(de2592eb) SHA1(9b537205808c502cf872e62f9701357ef8e28f3c) )
ROM_END

ROM_START( vjpp3 )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "29lv320.u2", 0x000000, 0x400000, CRC(f664d3f4) SHA1(59279e54e5d9ac2f956241e78dffe526b5de14fd) )
ROM_END

ROM_START( vjpp4 )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "29lv160.u2", 0x000000, 0x200000, CRC(2272f1f4) SHA1(0ffb606d4ac93da27a671b369800ea71e68452f3) )
ROM_END


} // anonymous namespace


// there were older models eg. PDC30 with fewer games, and some differences (eg "Jo Ma" instead of "Jo Ma 2")
// "Jo Ma 2" shows "Licensed by Mitchell Corporation" (Mitchell made the original Puzzloop on which this style of game is based)  Videos of the original Jo Ma show it lacking this text.

// Other known units
// PDC 30
// PDC 40
// PDC 20 Sports
// + more

// This was dumped from an Anncia branded unit, although there's no ingame branding, so ROM is probably the same for all PDC100 units
CONS( 2008, pdc100,  0,         0, pdc100,      pdc100,       spg2xx_pdc_game_state,     empty_init,   "Conny / Anncia",   "PDC100 - Pocket Dream Console (Anncia, US)", MACHINE_IMPERFECT_SOUND )

// there seem to be at least 3 different pink PDC30 units, possibly with different games / software revisions
// design differences can be seen in the packaging and d-pad style at least
CONS( 2008, pdc30p,  0,         0, pdc100,      pdc100,       spg2xx_pdc_game_state,     empty_init,   "Conny / VideoJet", "PDC30 - Pocket Dream Console (Pink version) (VideoJet, France)", MACHINE_IMPERFECT_SOUND )

// interestingly this is newer than the PDC100 above, despite containing fewer games
CONS( 2010, pdc50,    0,        0, pdc100,      pdc100,       spg2xx_pdc_game_state,     empty_init,   "Conny / VideoJet", "PDC50 - Pocket Dream Console (VideoJet, France)", MACHINE_IMPERFECT_SOUND )

CONS( 2012, pdc200,   0,        0, pdc100,      pdc100,       spg2xx_pdc200_game_state,  init_pdc150t, "Conny / VideoJet", "PDC200 - Pocket Dream Console (VideoJet, France)", MACHINE_IMPERFECT_SOUND )

CONS( 2011, pdc40t,   0,        0, pdc_tactile, pdc_tactile,  spg2xx_pdc_game_state,     init_pdc40t,  "Conny / VideoJet", "PDC40 Tactile - Pocket Dream Console (VideoJet, France)", MACHINE_IMPERFECT_SOUND )

CONS( 2011, pdc150t,  0,        0, pdc_tactile, pdc_tactile,  spg2xx_pdc150t_game_state, init_pdc150t, "Conny / VideoJet", "PDC150 Tactile - Pocket Dream Console (VideoJet, France)", MACHINE_IMPERFECT_SOUND )

CONS( 2008, ouipdc,   0,        0, pdc100,      pdc100,       spg2xx_pdc_game_state,     empty_init,   "Conny / VideoJet", "Oui-Oui - Pocket Dream Console (VideoJet, France)", MACHINE_IMPERFECT_SOUND )

CONS( 2013, tmntpdc,  0,        0, pdc100,      pdc100,       spg2xx_pdc_game_state,     empty_init,   "Conny / VideoJet", "Teenage Mutant Ninja Turtles - Pocket Dream Console (VideoJet, France)", MACHINE_IMPERFECT_SOUND )

CONS( 2013, dorapdc,  0,        0, pdc100,      pdc100,       spg2xx_pdc_game_state,     empty_init,   "Conny / VideoJet", "Dora l'exploratrice - Pocket Dream Console (VideoJet, France)", MACHINE_IMPERFECT_SOUND )

CONS( 200?, vjpp1,    0,        0, pdc100,      vjpp2,        spg2xx_pdc_game_state,     empty_init,   "Conny / VideoJet", "Plug Play TV Games 1 (4-in-1) (VideoJet, France)", MACHINE_IMPERFECT_SOUND )

CONS( 200?, vjpp2,    0,        0, pdc100,      vjpp2,        spg2xx_pdc_game_state,     empty_init,   "Conny / VideoJet", "Plug Play TV Games 2 (4-in-1) (VideoJet, France)", MACHINE_IMPERFECT_SOUND )

CONS( 200?, vjpp3,    0,        0, pdc100,      vjpp2,        spg2xx_pdc_game_state,     empty_init,   "Conny / VideoJet", "Plug Play TV Games 3 (4-in-1) (VideoJet, France)", MACHINE_IMPERFECT_SOUND )

CONS( 200?, vjpp4,    0,        0, pdc100,      vjpp2,        spg2xx_pdc_game_state,     empty_init,   "Conny / VideoJet", "Plug Play TV Games 4 (4-in-1) (VideoJet, France)", MACHINE_IMPERFECT_SOUND )



spg2xx_playvision.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

#include "emu.h"
#include "spg2xx.h"

#include "pvmil.lh"


namespace {

class pvmil_state : public spg2xx_game_state
{
public:
	pvmil_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_state(mconfig, type, tag),
		m_portcdata(0x0000),
		m_latchcount(0),
		m_latchbit(0),
		m_outdat(0),
		m_p4inputs(*this, "EXTRA"),
		m_leds(*this, "led%u", 0U)
	{ }

	void pvmil(machine_config &config);

	int pvmil_p4buttons_r();

protected:
	virtual void machine_start() override ATTR_COLD;

	virtual void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
	virtual void portb_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
	virtual void portc_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;

private:
	uint16_t m_portcdata;
	int m_latchcount;
	int m_latchbit;
	uint16_t m_outdat;
	optional_ioport m_p4inputs;
	output_finder<4> m_leds;
};


void pvmil_state::machine_start()
{
	spg2xx_game_state::machine_start();

	m_leds.resolve();
	save_item(NAME(m_portcdata));
	save_item(NAME(m_latchcount));
	save_item(NAME(m_latchbit));
	save_item(NAME(m_outdat));
}


void pvmil_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: pvmil_porta_w %04x\n", machine().describe_context(), data);
}

void pvmil_state::portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: pvmil_portb_w %04x\n", machine().describe_context(), data);
}


int pvmil_state::pvmil_p4buttons_r()
{
	return m_latchbit;
}


void pvmil_state::portc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	// ---- -432 1--- r-?c
	// 4,3,2,1 = player controller LEDs
	// r = reset input multiplexer
	// ? = unknown
	// m = input multiplexer clock

	// p4 input reading
	// the code to read them is interesting tho, it even includes loops that poll port a 16 times before/after, why?
	logerror("%s: pvmil_portc_w %04x\n", machine().describe_context(), data);

	uint16_t bit;

	// for logging bits changed on the port
	if (0)
	{
		for (int i = 0; i < 16; i++)
		{
			bit = 1 << i;
			if ((m_portcdata & bit) != (data & bit))
			{
				if (data & bit)
				{
					logerror("port c %04x low to high\n", bit);
				}
				else
				{
					logerror("port c %04x high to low\n", bit);
				}
			}

			if ((m_portcdata & 0x0400) != (data & 0x0400))
			{
				logerror("-------------------------------------------------\n");
			}
		}
	}

	// happens on startup, before it starts reading inputs for the first time, assume 'reset counter'
	bit = 0x0008;
	if ((m_portcdata & bit) != (data & bit))
	{
		if (data & bit)
		{
			logerror("reset read counter\n");
			m_latchcount = 0;
		}
	}

	bit = 0x0001;
	if ((m_portcdata & bit) != (data & bit))
	{
		if (!(data & bit))
		{
			//logerror("latch with count of %d (outbit is %d)\n", m_latchcount, (m_portcdata & 0x0002)>>1 );
			// what is bit 0x0002? it gets flipped in the same code as the inputs are read.
			// it doesn't follow any obvious pattern
			m_outdat &= ~(1 << m_latchcount);
			m_outdat |= ((data & 0x0002) >> 1) << m_latchcount;
			if (0)
				popmessage("%d %d %d %d   %d %d %d %d   %d %d %d %d   %d %d %d %d",
					(m_outdat & 0x8000) ? 1 : 0, (m_outdat & 0x4000) ? 1 : 0, (m_outdat & 0x2000) ? 1 : 0, (m_outdat & 0x1000) ? 1 : 0,
					(m_outdat & 0x0800) ? 1 : 0, (m_outdat & 0x0400) ? 1 : 0, (m_outdat & 0x0200) ? 1 : 0, (m_outdat & 0x0100) ? 1 : 0,
					(m_outdat & 0x0080) ? 1 : 0, (m_outdat & 0x0040) ? 1 : 0, (m_outdat & 0x0020) ? 1 : 0, (m_outdat & 0x0010) ? 1 : 0,
					(m_outdat & 0x0008) ? 1 : 0, (m_outdat & 0x0004) ? 1 : 0, (m_outdat & 0x0002) ? 1 : 0, (m_outdat & 0x0001) ? 1 : 0);


			m_latchbit = (((m_p4inputs->read()) << m_latchcount) & 0x8000) ? 1 : 0;

			m_latchcount++;
			if (m_latchcount == 16)
				m_latchcount = 0;
		}
	}

	m_portcdata = data;

	if (0)
		popmessage("%d %d %d %d   %d %d %d %d   %d %d %d %d   %d %d %d %d",
			(m_portcdata & 0x8000) ? 1 : 0, (m_portcdata & 0x4000) ? 1 : 0, (m_portcdata & 0x2000) ? 1 : 0, (m_portcdata & 0x1000) ? 1 : 0,
			(m_portcdata & 0x0800) ? 1 : 0, (m_portcdata & 0x0400) ? 1 : 0, (m_portcdata & 0x0200) ? 1 : 0, (m_portcdata & 0x0100) ? 1 : 0,
			(m_portcdata & 0x0080) ? 1 : 0, (m_portcdata & 0x0040) ? 1 : 0, (m_portcdata & 0x0020) ? 1 : 0, (m_portcdata & 0x0010) ? 1 : 0,
			(m_portcdata & 0x0008) ? 1 : 0, (m_portcdata & 0x0004) ? 1 : 0, (m_portcdata & 0x0002) ? 1 : 0, (m_portcdata & 0x0001) ? 1 : 0);

	m_leds[0] = (m_portcdata & 0x0080) ? 0 : 1;
	m_leds[1] = (m_portcdata & 0x0100) ? 0 : 1;
	m_leds[2] = (m_portcdata & 0x0200) ? 0 : 1;
	m_leds[3] = (m_portcdata & 0x0400) ? 0 : 1;
}


static INPUT_PORTS_START( pvmil ) // hold "console start" + "console select" on boot for test mode
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("Player 1 A")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("Player 1 B")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("Player 1 C")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("Player 1 D")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("Player 2 A")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("Player 2 B")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("Player 2 C")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("Player 2 D")
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(3) PORT_NAME("Player 3 A")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(3) PORT_NAME("Player 3 B")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(3) PORT_NAME("Player 3 C")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(3) PORT_NAME("Player 3 D")
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_PLAYER(1) PORT_NAME("Player 1 Lifeline")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_START ) PORT_CODE(KEYCODE_1) PORT_NAME("Console Start")
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CODE(KEYCODE_5) PORT_NAME("Console Select")

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x0003, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(pvmil_state::pvmil_p4buttons_r)) // Player 4 buttons read through here
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_PLAYER(2) PORT_NAME("Player 2 Lifeline")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_PLAYER(3) PORT_NAME("Player 3 Lifeline")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_PLAYER(4) PORT_NAME("Player 4 Lifeline")
	PORT_BIT( 0xff80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("EXTRA")
	PORT_BIT( 0x0fff, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(4) PORT_NAME("Player 4 A")
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(4) PORT_NAME("Player 4 B")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(4) PORT_NAME("Player 4 C")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(4) PORT_NAME("Player 4 D")
INPUT_PORTS_END


void pvmil_state::pvmil(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &pvmil_state::mem_map_4m);
	m_maincpu->set_pal(true);

	spg2xx_base(config);

	m_screen->set_refresh_hz(50);
	//m_screen->set_size(320, 312);

	m_maincpu->porta_in().set_ioport("P1");
	m_maincpu->portb_in().set_ioport("P2");
	m_maincpu->portc_in().set_ioport("P3");
	m_maincpu->porta_out().set(FUNC(pvmil_state::porta_w));
	m_maincpu->portb_out().set(FUNC(pvmil_state::portb_w));
	m_maincpu->portc_out().set(FUNC(pvmil_state::portc_w));

	config.set_default_layout(layout_pvmil);
}

ROM_START( pvmil )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 ) // Fujitsu 29Z0002TN, read as ST M29W320FB
	ROM_LOAD16_WORD_SWAP( "millionare4.bin", 0x000000, 0x400000, CRC(9c43d0f2) SHA1(fb4ba0115000b10b7c0e0d44b9fa3234c900e694) )
ROM_END

} // anonymous namespace


// see note for the pvmil8 set in tvgames/elan_eu3a05.cpp
CONS( 2006, pvmil,       0,     0,        pvmil,        pvmil,    pvmil_state, empty_init, "Play Vision", "Who Wants to Be a Millionaire? (Play Vision, Plug and Play, UK, 16-bit version)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )



spg2xx_senario.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

/*
    General Senario games on SunPlus hardware

    these check for flash ROM and actually save user data at 0x700000 (senmil/senbbs/senapren) in the flash ROM

    TODO:
    senmil - Are the LEDs on the controllers meant to go out as players select answers like with pvmil, or are they just to show that the controller is connected?
    sencosmo - fix Flash hookup (crashes if you use a Flash chip right now)
    senapren - should it actually save data? chip really seems to be 2MB, data written at 7MB can't be saved at mirrored 1MB address or it would erase game code / data
    senpmate - again seems to actually be a 2MB chip

*/

#include "emu.h"
#include "spg2xx.h"

#include "machine/intelfsh.h"


namespace {

class spg2xx_senario_state : public spg2xx_game_state
{
public:
	spg2xx_senario_state(const machine_config& mconfig, device_type type, const char* tag) :
		spg2xx_game_state(mconfig, type, tag)
	{ }

protected:
	//virtual void machine_start() override ATTR_COLD;
	//virtual void machine_reset() override ATTR_COLD;

private:
};

class spg2xx_senario_bbs_state : public spg2xx_senario_state
{
public:
	spg2xx_senario_bbs_state(const machine_config& mconfig, device_type type, const char* tag) :
		spg2xx_senario_state(mconfig, type, tag)
	{ }

	void senbbs(machine_config& config);
	void mem_map_flash(address_map &map) ATTR_COLD;

protected:
	//virtual void machine_start() override ATTR_COLD;
	//virtual void machine_reset() override ATTR_COLD;

private:
};

class spg2xx_senario_cosmo_state : public spg2xx_senario_bbs_state
{
public:
	spg2xx_senario_cosmo_state(const machine_config& mconfig, device_type type, const char* tag) :
		spg2xx_senario_bbs_state(mconfig, type, tag),
		m_romregion(*this, "flash")
	{ }

	void sencosmo(machine_config& config);
	void mem_map_flash_bypass(address_map &map) ATTR_COLD;

protected:
	//virtual void machine_start() override ATTR_COLD;
	//virtual void machine_reset() override ATTR_COLD;

	uint16_t read_bypass(offs_t offset) { return m_romregion[offset]; }
	void write_bypass(offs_t offset, uint16_t data) { logerror("Write to ROM area %08x %04x\n", offset, data); }

private:
	required_region_ptr<uint16_t> m_romregion;
};


class spg2xx_senario_mil_state : public spg2xx_senario_bbs_state
{
public:
	spg2xx_senario_mil_state(const machine_config& mconfig, device_type type, const char* tag) :
		spg2xx_senario_bbs_state(mconfig, type, tag),
		m_portc_data(0)
	{ }

	void senmil(machine_config& config);

protected:
	//virtual void machine_start() override ATTR_COLD;
	//virtual void machine_reset() override ATTR_COLD;

	uint16_t portc_r();

	virtual void portc_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;

private:
	uint16_t m_portc_data;
};

static INPUT_PORTS_START( senmil ) // reset with Console Start and Console Select held down for test mode
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("Player 1 A")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("Player 1 B")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("Player 1 C")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("Player 1 D")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("Player 2 A")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("Player 2 B")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("Player 2 C")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("Player 2 D")
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(3) PORT_NAME("Player 3 A")
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(3) PORT_NAME("Player 3 B")
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(3) PORT_NAME("Player 3 C")
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(3) PORT_NAME("Player 3 D")
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(4) PORT_NAME("Player 4 A")
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(4) PORT_NAME("Player 4 B")
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(4) PORT_NAME("Player 4 C")
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(4) PORT_NAME("Player 4 D")

	PORT_START("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(1) PORT_NAME("Player 1 Select")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(1) PORT_NAME("Player 1 OK")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(2) PORT_NAME("Player 2 Select")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(2) PORT_NAME("Player 2 OK")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(3) PORT_NAME("Player 3 Select")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(3) PORT_NAME("Player 3 OK")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(4) PORT_NAME("Player 4 Select")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(4) PORT_NAME("Player 4 OK")
	PORT_BIT( 0xff00, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P3")
	PORT_BIT( 0x00ff, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // Pad Connection Status (see spg2xx_senario_mil_state::portc_r)
	PORT_BIT( 0x0300, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // Low Battery sensor
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_START ) PORT_CODE(KEYCODE_1) PORT_NAME("Console Start")
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_SELECT ) PORT_CODE(KEYCODE_5) PORT_NAME("Console Select")
	PORT_BIT( 0xe000, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END


static INPUT_PORTS_START( senbbs ) // reset with Select and Spin held down for test mode
	PORT_START("P1")
	// To use Gambling controls or not? This is a Plug and Play themed controller, not an actual gambling unit
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Spin")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Lever")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Select")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("OK")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("Bet")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON7 ) PORT_NAME("Cash Out")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("Bet Max")
	PORT_BIT( 0x7f80, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // Low Battery sensor

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

static INPUT_PORTS_START( senappren )
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("Red Team Select")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("Red Team Ok")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("Blue Team Select")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("Blue Team Ok")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CODE(KEYCODE_1) PORT_NAME("Console Ok")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_CODE(KEYCODE_5) PORT_NAME("Console Select")
	PORT_BIT( 0x7f80, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // Low Battery sensor

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END


static INPUT_PORTS_START( sencosmo ) // hold Pause during power on for Test Menu
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("Player 1 A")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("Player 1 B")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("Player 1 C")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("Player 1 D")
	PORT_BIT( 0x0030, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_CODE(KEYCODE_1) PORT_NAME("Console Pause")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("Player 2 A")
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("Player 2 B")
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("Player 2 C")
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("Player 2 D")
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_CODE(KEYCODE_5) PORT_NAME("Console Power")
	PORT_BIT( 0x6000, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // Low Battery sensor

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

static INPUT_PORTS_START( senpmate ) // hold Pause during power on for Test Menu
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Player A")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Player B")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Player C")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Player D")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON7 ) PORT_CODE(KEYCODE_F2) PORT_NAME("Console Reset")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("Player Select")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("Player Start")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_UNKNOWN ) // responds for 'any button' presses, doesn't appear to be a real button
	PORT_BIT( 0x7f00, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // Low Battery sensor

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END


void spg2xx_senario_bbs_state::mem_map_flash(address_map &map)
{
	map(0x000000, 0x3fffff).rw("flash", FUNC(spansion_s29gl064s_device::read), FUNC(spansion_s29gl064s_device::write));
}

// this is meant to be flash, but it crashes with invalid flash command when answering questions?
void spg2xx_senario_cosmo_state::mem_map_flash_bypass(address_map &map)
{
	map(0x000000, 0x1fffff).rw(FUNC(spg2xx_senario_cosmo_state::read_bypass), FUNC(spg2xx_senario_cosmo_state::write_bypass));
}


void spg2xx_senario_mil_state::portc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	// -4-3 -2-1
	// change when a button for that player is pressed, none of these seem to be for the LEDs on the controllers (or are they 'always on')

	logerror("%s: spg2xx_senario_mil_state::portc_w %04x ---- %04x %04x \n", machine().describe_context(), data, data & 0x55, data & 0xaa);
	m_portc_data = data;
}

uint16_t spg2xx_senario_mil_state::portc_r()
{
	uint16_t ret = m_io_p3->read() & 0xffaa; // 0xaa must be set to register all controllers as turned on
	ret |= m_portc_data & 0x0055;
	return ret;
}

void spg2xx_senario_bbs_state::senbbs(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_senario_bbs_state::mem_map_flash);

	spg2xx_base(config);

	m_maincpu->porta_in().set_ioport("P1");
	m_maincpu->portb_in().set_ioport("P2");
	m_maincpu->portc_in().set_ioport("P3");

	SPANSION_S29GL064S(config, "flash");
}

void spg2xx_senario_cosmo_state::sencosmo(machine_config& config)
{
	senbbs(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &spg2xx_senario_cosmo_state::mem_map_flash_bypass);
	/* the game crashes if you get no matches after the 2nd spin on the 'Fashion Disaster' slot machine mini-game.
	   this could be a real game bug (as to trigger it you'd have to make a poor choice not to hold any matches after the first spin)
	   however with the recompiler execution of bad data causes MAME to immediately drop to commandline with no error message
	   without recompiler the game just hangs */
	m_maincpu->set_force_no_drc(true);

}


void spg2xx_senario_mil_state::senmil(machine_config& config)
{
	senbbs(config);

	m_maincpu->portc_in().set(FUNC(spg2xx_senario_mil_state::portc_r));
	m_maincpu->portc_out().set(FUNC(spg2xx_senario_mil_state::portc_w));
}

// is ROM_REGION16_BE correct here, allows us to use ROM_LOAD16_WORD_SWAP as when loading SunPlus stuff in other cases

ROM_START( senapren )
	ROM_REGION16_BE( 0x800000, "flash", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "apprentice.bin", 0x000000, 0x200000, CRC(1c919e72) SHA1(20efcb992bd3ff8ab78470bd484f4f0b226e6c15) )
	// That one has a SOP44 COB instead of a TSOP48 chip.  Pin 1 is N/C, 32 is grounded, and 33 is tied high.  That means a max of A0-A20 = 21 16-bit address lines, for 4MB
	// Data repeated dumped as 4MB, so 2MB? (but game attempts to write to 0x700000 for flash user data which would erase game data in a 2MB ROM)
	// ROM also had a sticker that says D44B 16M 050818.  16Mbit is 2MB, and the 16-bit sum of the 2MB file is D44B.
	// Maybe the Flash ROM save just isn't meant to work here?
ROM_END

ROM_START( senpmate )
	ROM_REGION16_BE( 0x800000, "flash", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "perfectmate.bin", 0x000000, 0x200000, CRC(fa7f8ca0) SHA1(fcc78f8efb183e9c65545eb502da475225253a94) )
	// Perfect Mate's COB also had a sticker: 7DC1 16M 050822.  The 2MB file I dumped sums to 7DC1
	// The Perfect Mate checksum in the ROM header matches the sum of bytes from 0x10 to the end.
ROM_END

ROM_START( sencosmo )
	ROM_REGION16_BE( 0x400000, "flash", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "cosmo.bin", 0x000000, 0x400000, CRC(1ec50795) SHA1(621c4e03b5713f3678d2935f8938f15c5d4a5fdf) )
	// attempts to write to 0x380000 for flash user data? different Flash type?
ROM_END

ROM_START( senstriv )
	ROM_REGION16_BE( 0x400000, "flash", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "senariosportstriviapro_m5m29gt320_001c0020.bin", 0x000000, 0x400000, CRC(095ffbca) SHA1(d91328855a9ca542ba38253d2353545dc8b47fa4) ) // chip was 'flipped' (reverse pinout)
	// attempts to write to 0x380000 for flash user data? different Flash type?
ROM_END

ROM_START( senmil )
	ROM_REGION16_BE( 0x800000, "flash", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "wwtbam_nouserdata.bin", 0x000000, 0x800000, CRC(b2626df6) SHA1(f06943d63dbb1c9d211cb35b40dcb18cb8b39ecd) )
ROM_END

ROM_START( senbbs )
	ROM_REGION16_BE( 0x800000, "flash", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "bigbonusslots.bin", 0x000000, 0x800000, CRC(071effc3) SHA1(892c05a8b64a388b331ad0d361bf4c523c6c14c9) )
ROM_END

} // anonymous namespace


CONS( 2005, senbbs,      0,     0,        senbbs,       senbbs,    spg2xx_senario_bbs_state,   empty_init, "Senario", "Big Bonus Slots (Senario, Plug and Play)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2005, senapren,    0,     0,        sencosmo,     senappren, spg2xx_senario_cosmo_state, empty_init, "Senario", "The Apprentice (Senario, Plug and Play)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2005, senpmate,    0,     0,        senbbs,       senpmate,  spg2xx_senario_bbs_state,   empty_init, "Senario", "The Perfect Mate (Senario, Plug and Play)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2005, sencosmo,    0,     0,        sencosmo,     sencosmo,  spg2xx_senario_cosmo_state, empty_init, "Senario", "Cosmo Girl (Senario, Plug and Play)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2005, senstriv,    0,     0,        sencosmo,     sencosmo,  spg2xx_senario_cosmo_state, empty_init, "Senario", "Sports Trivia Professional Edition (Senario, Plug and Play)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2005?,senmil,      0,     0,        senmil,       senmil,    spg2xx_senario_mil_state,   empty_init, "Senario", "Who Wants to Be a Millionaire? (Senario, Plug and Play, US)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )



spg2xx_senario_poker.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

/* Senario 6 Player Poker systems

TODO:
improve controller hookup / simulation and remove PC-based hacks!

*/

#include "emu.h"
#include "spg2xx.h"

#include "sentx6p.lh"


namespace {

class sentx6p_state : public spg2xx_game_state
{
public:
	sentx6p_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_state(mconfig, type, tag),
		m_porta_data(0x0000),
		m_suite1(*this, "SUITE_LEFT_BZ%u", 0U),
		m_suite2(*this, "SUITE_RIGHT_BZ%u", 0U),
		m_number1(*this, "NUMBER_LEFT_BZ%u", 0U),
		m_number2(*this, "NUMBER_RIGHT_BZ%u", 0U),
		m_select_fold(*this, "SELECT_FOLD_BZ%u", 0U),
		m_select_check(*this, "SELECT_CHECK_BZ%u", 0U),
		m_select_bet(*this, "SELECT_BET_BZ%u", 0U),
		m_select_call(*this, "SELECT_CALL_BZ%u", 0U),
		m_select_raise(*this, "SELECT_RAISE_BZ%u", 0U),
		m_select_allin(*this, "SELECT_ALLIN_BZ%u", 0U),
		m_option_fold(*this, "OPTION_FOLD_BZ%u", 0U),
		m_option_check(*this, "OPTION_CHECK_BZ%u", 0U),
		m_option_bet(*this, "OPTION_BET_BZ%u", 0U),
		m_option_call(*this, "OPTION_CALL_BZ%u", 0U),
		m_option_raise(*this, "OPTION_RAISE_BZ%u", 0U),
		m_option_allin(*this, "OPTION_ALLIN_BZ%u", 0U),

		m_led(*this, "LED_BZ%u", 0U)
	{ }

	void sentx6p(machine_config &config);

	void init_sentx6p();
	void init_sentx6puk();
	void init_sentx6pd();

	void mem_map_2m_texas(address_map &map) ATTR_COLD;

	DECLARE_INPUT_CHANGED_MEMBER(ok_latch)
	{
		if (newval == 1)
		{
			m_inputlatches[param] |= 0x02;
			logerror("latching OK button for Player %d\n", param+1);
		}
	}

	DECLARE_INPUT_CHANGED_MEMBER(select_latch)
	{
		if (newval == 1)
		{
			m_inputlatches[param] |= 0x01;
			logerror("latching Select button for Player %d\n", param+1);
		}
	}

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	uint16_t sentx_porta_r();
	uint16_t sentx_portb_r();
	uint16_t sentx_portc_r();

	virtual void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
	virtual void portb_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
	virtual void portc_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;

	void sentx_tx_w(uint8_t data);

	uint8_t m_lcd_card1[6];
	uint8_t m_lcd_card2[6];
	uint8_t m_lcd_options[6];
	uint8_t m_lcd_options_select[6];
	uint8_t m_lcd_led[6];

	void set_card1(uint8_t value, int select_bits);
	void set_card2(uint8_t value, int select_bits);
	void set_options(uint8_t value, int select_bits);
	void set_options_select(uint8_t value, int select_bits);
	void set_controller_led(uint8_t value, int select_bits);

	void controller_send_data(int which);

	uint8_t m_inputlatches[6];

	uint16_t m_porta_data;

	output_finder<6> m_suite1;
	output_finder<6> m_suite2;
	output_finder<6> m_number1;
	output_finder<6> m_number2;

	output_finder<6> m_select_fold;
	output_finder<6> m_select_check;
	output_finder<6> m_select_bet;
	output_finder<6> m_select_call;
	output_finder<6> m_select_raise;
	output_finder<6> m_select_allin;

	output_finder<6> m_option_fold;
	output_finder<6> m_option_check;
	output_finder<6> m_option_bet;
	output_finder<6> m_option_call;
	output_finder<6> m_option_raise;
	output_finder<6> m_option_allin;

	output_finder<6> m_led;

	// for I/O hacks
	uint32_t m_pchackaddress1 = 0;
	uint32_t m_pchackaddress2 = 0;
	uint16_t m_controller_sense_addr = 0;
};


void sentx6p_state::mem_map_2m_texas(address_map &map)
{
	map(0x000000, 0x3fffff).bankr("cartbank");
	map(0x3f0000, 0x3f7fff).ram();
}

static INPUT_PORTS_START( sentx6p )
	PORT_START("P1")
	PORT_DIPNAME( 0x0001, 0x0001, "Port 1" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	// is this really just the battery sensor, or does it have some other meaning, like data ready?
	PORT_DIPNAME( 0x8000, 0x0000, "Low Battery" )
	PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( On ) )

	PORT_START("P2")
	// these must be sense lines
	PORT_DIPNAME( 0x0001, 0x0001, "Player 1 Connected" )
	PORT_DIPSETTING(      0x0000, DEF_STR( No ) )
	PORT_DIPSETTING(      0x0001, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x0002, 0x0002, "Player 2 Connected" )
	PORT_DIPSETTING(      0x0000, DEF_STR( No ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x0004, 0x0004, "Player 3 Connected" )
	PORT_DIPSETTING(      0x0000, DEF_STR( No ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x0008, 0x0008, "Player 4 Connected" )
	PORT_DIPSETTING(      0x0000, DEF_STR( No ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x0010, 0x0010, "Player 5 Connected" )
	PORT_DIPSETTING(      0x0000, DEF_STR( No ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x0020, 0x0020, "Player 6 Connected" )
	PORT_DIPSETTING(      0x0000, DEF_STR( No ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Yes ) )

	PORT_DIPNAME( 0x0040, 0x0040, "Port 2" )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P3")
	PORT_DIPNAME( 0x0001, 0x0001, "Port 3" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CODE(KEYCODE_5) PORT_NAME("Console Select") // the Console buttons also work for Player 1
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_CODE(KEYCODE_1) PORT_NAME("Console Ok")
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	// these are presuambly read through the UART as the LCD screens are driven by it, currently not hooked up
	// using PORT_CHANGED_MEMBER because we assume the controller latches them for sending as inputs are only read every few frames
	PORT_START("CTRL1")
	PORT_BIT( 0x1, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sentx6p_state::select_latch), 0)
	PORT_BIT( 0x2, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sentx6p_state::ok_latch), 0)

	PORT_START("CTRL2")
	PORT_BIT( 0x1, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sentx6p_state::select_latch), 1)
	PORT_BIT( 0x2, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sentx6p_state::ok_latch), 1)

	PORT_START("CTRL3")
	PORT_BIT( 0x1, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(3) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sentx6p_state::select_latch), 2)
	PORT_BIT( 0x2, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(3) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sentx6p_state::ok_latch), 2)

	PORT_START("CTRL4")
	PORT_BIT( 0x1, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(4) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sentx6p_state::select_latch), 3)
	PORT_BIT( 0x2, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(4) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sentx6p_state::ok_latch), 3)

	PORT_START("CTRL5")
	PORT_BIT( 0x1, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(5) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sentx6p_state::select_latch), 4)
	PORT_BIT( 0x2, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(5) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sentx6p_state::ok_latch), 4)

	PORT_START("CTRL6")
	PORT_BIT( 0x1, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(6) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sentx6p_state::select_latch), 5)
	PORT_BIT( 0x2, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(6) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sentx6p_state::ok_latch), 5)

INPUT_PORTS_END


void sentx6p_state::machine_start()
{
	spg2xx_game_state::machine_start();

	m_suite1.resolve();
	m_suite2.resolve();
	m_number1.resolve();
	m_number2.resolve();

	m_select_fold.resolve();
	m_select_check.resolve();
	m_select_bet.resolve();
	m_select_call.resolve();
	m_select_raise.resolve();
	m_select_allin.resolve();

	m_option_fold.resolve();
	m_option_check.resolve();
	m_option_bet.resolve();
	m_option_call.resolve();
	m_option_raise.resolve();
	m_option_allin.resolve();

	m_led.resolve();
}

void sentx6p_state::machine_reset()
{
	spg2xx_game_state::machine_reset();

	for (int i = 0; i < 6; i++)
	{
		m_lcd_card1[i] = 0x00;
		m_lcd_card2[i] = 0x00;
		m_lcd_options[i] = 0x00;
		m_lcd_options_select[i] = 0x00;
		m_lcd_led[i] = 0x00;

		m_inputlatches[i] = 0x00;
	}

	/* HACK: this address needs to contain '1' so that it thinks the first controller
	   is present, otherwise it hangs on boot.  How does this get set?
	   following addresses are for the other controllers, and get set based on Port B
	   status flags */
	address_space &mem = m_maincpu->space(AS_PROGRAM);
	mem.write_word(m_controller_sense_addr,0x0001);
}

void sentx6p_state::controller_send_data(int which)
{
	// 0x78 have to be set, this is probably because there is an unused space in the commands
	// going the other way at 0x78 - 0x7f
	// bit 3 (0x04) can also be set, doesn't care

	uint8_t send = m_inputlatches[which] | 0x78;

	m_maincpu->uart_rx( send );

	if (m_inputlatches[which] & 0x03)
	{
		m_inputlatches[which] = 0x00;
		logerror("clearing input latches for player %d\n", which + 1);
	}
}

uint16_t sentx6p_state::sentx_porta_r()
{
	int select_bits = (m_porta_data >> 8) & 0x3f;
	//logerror("%s: sentx_porta_r (with controller select bits %02x)\n", machine().describe_context(), select_bits);

	/* 0000 = no controller? (system buttons only?)
	   0100 = controller 1?
	   0200 = controller 2?
	   0400 = controller 3?
	   0800 = controller 4?
	   1000 = controller 5?
	   2000 = controller 6?

	   this is an assumption based on startup, where the port is polled after writing those values
	*/

	// the code around 029811 uses a ram value shifted left 8 times as the select bits (select_bits) on write
	// then does a mask with them on the reads from this port, without shifting, comparing with 0

	// the 'select bits' must also be active when the controller wants to send data, the UART read function
	// won't proceed if they're zero, but the port is written with 0 before that

	//logerror("%08x\n", m_maincpu->pc());

	// regular logic for writing to the LCDs
	uint16_t ret = (m_io_p1->read() & 0xffc0) | select_bits;

	// hacks needed for reading from the UART to work

	// the select bit must be high here to read from the controller?
	if (m_maincpu->pc() == m_pchackaddress1)
	{
		ret ^= 0x3f;
	}

	// but must be low again here after writing the select bits in order to not
	// get stuck in a timeout loop
	if (m_maincpu->pc() == m_pchackaddress2)
	{
		ret &= ~0x3f;

		// presumably the inputs can only be sent once the controller is actually selected, otherwise
		// there would be competing UART sources?
		for (int i = 0; i < 6; i++)
		{
			if (select_bits & (1 << i))
			{
				controller_send_data(i);
			}
		}
	}

	return ret;
}

uint16_t sentx6p_state::sentx_portb_r()
{
	return m_io_p2->read();
}

uint16_t sentx6p_state::sentx_portc_r()
{
	return m_io_p3->read();
}

void sentx6p_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: sentx_porta_w %04x\n", machine().describe_context(), data);

	COMBINE_DATA(&m_porta_data);
}

void sentx6p_state::portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: sentx_portb_w %04x\n", machine().describe_context(), data);
}

void sentx6p_state::portc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: sentx_portc_w %04x\n", machine().describe_context(), data);
}

/*
    Card Table
    (the controller must contain an MCU under the glob to receive thes commands
     and convert them to actual LCD segments)

         off      00
    | A  diamonds 01 | A  hearts 0e   | A  spades 1b  | A  clubs 28 |
    | 2  diamonds 02 | 2  hearts 0f   | 2  spades 1c  | 2  clubs 29 |
    | 3  diamonds 03 | 3  hearts 10   | 3  spades 1d  | 3  clubs 2a |
    | 4  diamonds 04 | 4  hearts 11   | 4  spades 1e  | 4  clubs 2b |
    | 5  diamonds 05 | 5  hearts 12   | 5  spades 1f  | 5  clubs 2c |
    | 6  diamonds 06 | 6  hearts 13   | 6  spades 20  | 6  clubs 2d |
    | 7  diamonds 07 | 7  hearts 14   | 7  spades 21  | 7  clubs 2e |
    | 8  diamonds 08 | 8  hearts 15   | 8  spades 22  | 8  clubs 2f |
    | 9  diamonds 09 | 9  hearts 16   | 9  spades 23  | 9  clubs 30 |
    | 10 diamonds 0a | 10 hearts 17   | 10 spades 24  | 10 clubs 31 |
    | J  diamonds 0b | J  hearts 18   | J  spades 25  | J  clubs 32 |
    | Q  diamonds 0c | Q  hearts 19   | Q  spades 26  | Q  clubs 33 |
    | K  diamonds 0d | K  hearts 1a   | K  spades 27  | K  clubs 34 |

*/

void sentx6p_state::set_card1(uint8_t value, int select_bits)
{
	for (int i = 0; i < 6; i++)
	{
		if (select_bits & (1 << i))
		{
			m_lcd_card1[i] = value;

			int val = m_lcd_card1[i];
			if (val == 0)
			{
				m_suite1[i] = 0;
				m_number1[i] = 0;
			}
			else
			{
				int suite = (val-1) / 13;
				int number = (val-1) % 13;

				m_suite1[i] = suite+1;
				m_number1[i] = number+1;
			}
		}
	}
}

void sentx6p_state::set_card2(uint8_t value, int select_bits)
{
	for (int i = 0; i < 6; i++)
	{
		if (select_bits & (1 << i))
		{
			m_lcd_card2[i] = value;

			int val = m_lcd_card2[i];
			if (val == 0)
			{
				m_suite2[i] = 0;
				m_number2[i] = 0;
			}
			else
			{
				int suite = (val-1) / 13;
				int number = (val-1) % 13;

				m_suite2[i] = suite+1;
				m_number2[i] = number+1;
			}
		}
	}
}

void sentx6p_state::set_options(uint8_t value, int select_bits)
{
	for (int i = 0; i < 6; i++)
	{
		if (select_bits & (1 << i))
		{
			m_lcd_options[i] = value;

			// assume same mapping as selector bit below
			m_option_fold[i] =  (value & 0x01) ? 1 : 0;
			m_option_check[i] = (value & 0x02) ? 1 : 0;
			m_option_bet[i] =   (value & 0x04) ? 1 : 0;
			m_option_call[i] =  (value & 0x08) ? 1 : 0;
			m_option_raise[i] = (value & 0x10) ? 1 : 0;
			m_option_allin[i] = (value & 0x20) ? 1 : 0;
		}
	}
}

/*
    c0 = no selection highlight (00)
    c1 = fold selected (01)
    c2 = check selected (02)
    c4 = bet selected (04)
    c8 = call selected (08)
    d0 = raise selected (10)
    e0 = all in selected (20)
*/

void sentx6p_state::set_options_select(uint8_t value, int select_bits)
{
	for (int i = 0; i < 6; i++)
	{
		if (select_bits & (1 << i))
		{
			m_lcd_options_select[i] = value;

			m_select_fold[i] =  (value & 0x01) ? 1 : 0;
			m_select_check[i] = (value & 0x02) ? 1 : 0;
			m_select_bet[i] =   (value & 0x04) ? 1 : 0;
			m_select_call[i] =  (value & 0x08) ? 1 : 0;
			m_select_raise[i] = (value & 0x10) ? 1 : 0;
			m_select_allin[i] = (value & 0x20) ? 1 : 0;
		}
	}
}



void sentx6p_state::set_controller_led(uint8_t value, int select_bits)
{
	for (int i = 0; i < 6; i++)
	{
		if (select_bits & (1 << i))
		{
			m_lcd_led[i] = value;

			m_led[i] = value ^ 1;
		}
	}
}

void sentx6p_state::sentx_tx_w(uint8_t data)
{
	int select_bits = (m_porta_data >> 8) & 0x3f;

	// TX function is at 0x029773
	// starts by writing controller select, then checking if controller is selected, then transmits data

	// RX function is at 0x029811
	// similar logic to write controller ID, check if selected, then recieve data

	switch (data)
	{
	case 0x00: // card 1 off
	case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: // card 1 show Diamonds A,2,3,4,5,6,7,8,10,J,Q,K
	case 0x0e: case 0x0f: case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: case 0x19: case 0x1a: // card 1 show Hearts   A,2,3,4,5,6,7,8,10,J,Q,K
	case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f: case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: // card 1 show Spades   A,2,3,4,5,6,7,8,10,J,Q,K
	case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f: case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: // card 1 show Clubs    A,2,3,4,5,6,7,8,10,J,Q,K
		set_card1(data & 0x3f, select_bits);
		break;

	case 0x40: // card 2 off
	case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: // card 1 show Diamonds A,2,3,4,5,6,7,8,10,J,Q,K
	case 0x4e: case 0x4f: case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57: case 0x58: case 0x59: case 0x5a: // card 1 show Hearts   A,2,3,4,5,6,7,8,10,J,Q,K
	case 0x5b: case 0x5c: case 0x5d: case 0x5e: case 0x5f: case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: // card 1 show Spades   A,2,3,4,5,6,7,8,10,J,Q,K
	case 0x68: case 0x69: case 0x6a: case 0x6b: case 0x6c: case 0x6d: case 0x6e: case 0x6f: case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: // card 1 show Clubs    A,2,3,4,5,6,7,8,10,J,Q,K
		set_card2(data & 0x3f, select_bits);
		break;

	// cases 0x78 - 0x7f are transmissions in the other direction (from controller to main unit, used for sending buttons)

	case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87: case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f: // show selection options
	case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97: case 0x98: case 0x99: case 0x9a: case 0x9b: case 0x9c: case 0x9d: case 0x9e: case 0x9f:
	case 0xa0: case 0xa1: case 0xa2: case 0xa3: case 0xa4: case 0xa5: case 0xa6: case 0xa7: case 0xa8: case 0xa9: case 0xaa: case 0xab: case 0xac: case 0xad: case 0xae: case 0xaf:
	case 0xb0: case 0xb1: case 0xb2: case 0xb3: case 0xb4: case 0xb5: case 0xb6: case 0xb7: case 0xb8: case 0xb9: case 0xba: case 0xbb: case 0xbc: case 0xbd: case 0xbe: case 0xbf:
		set_options(data & 0x3f, select_bits);
		break;

	case 0xc0: case 0xc1: case 0xc2: case 0xc3: case 0xc4: case 0xc5: case 0xc6: case 0xc7: case 0xc8: case 0xc9: case 0xca: case 0xcb: case 0xcc: case 0xcd: case 0xce: case 0xcf: // show selection cursor
	case 0xd0: case 0xd1: case 0xd2: case 0xd3: case 0xd4: case 0xd5: case 0xd6: case 0xd7: case 0xd8: case 0xd9: case 0xda: case 0xdb: case 0xdc: case 0xdd: case 0xde: case 0xdf:
	case 0xe0: case 0xe1: case 0xe2: case 0xe3: case 0xe4: case 0xe5: case 0xe6: case 0xe7: case 0xe8: case 0xe9: case 0xea: case 0xeb: case 0xec: case 0xed: case 0xee: case 0xef:
	case 0xf0: case 0xf1: case 0xf2: case 0xf3: case 0xf4: case 0xf5: case 0xf6: case 0xf7: case 0xf8: case 0xf9: case 0xfa: case 0xfb: case 0xfc: case 0xfd: case 0xfe: case 0xff:
		set_options_select(data & 0x3f, select_bits);
		break;

	case 0x38:
	case 0x39:
		set_controller_led(data & 0x01, select_bits);
		break;

	case 0x3a:
		// reset controller? (the input code also seems to check for 3a in one place coming in the other direction)
		break;

	default:
		logerror("unknown LCD command %02x with controller select %02x\n", data, select_bits);
		break;
	}

}




void sentx6p_state::sentx6p(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &sentx6p_state::mem_map_2m_texas);
	m_maincpu->set_pal(true);

	spg2xx_base(config);

	m_screen->set_refresh_hz(50);

	m_maincpu->porta_in().set(FUNC(sentx6p_state::sentx_porta_r));
	m_maincpu->portb_in().set(FUNC(sentx6p_state::sentx_portb_r));
	m_maincpu->portc_in().set(FUNC(sentx6p_state::sentx_portc_r));

	m_maincpu->porta_out().set(FUNC(sentx6p_state::porta_w));
	m_maincpu->portb_out().set(FUNC(sentx6p_state::portb_w));
	m_maincpu->portc_out().set(FUNC(sentx6p_state::portc_w));

	m_maincpu->uart_tx().set(FUNC(sentx6p_state::sentx_tx_w));

	config.set_default_layout(layout_sentx6p);
}

ROM_START( sentx6p )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "vsmaxxtexasholdem.bin", 0x000000, 0x200000, CRC(d9d7ea1d) SHA1(ae531958d6074ea2034910f53e043d5fa10eaf8c) )
ROM_END

ROM_START( sentx6puk )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	// chksum:05B58350 is @ 0x3000.
	// This is the correct sum of 0x3010 - 0x1fffff (the first half of the ROM area after the checksum string)
	// The 2nd half is not summed (and not used by the game?) but definitely exists in ROM (same data on 2 units)
	ROM_LOAD16_WORD_SWAP( "senario texas holdem.bin", 0x000000, 0x400000, CRC(7c7d2d33) SHA1(71631074ba66e3b0cdeb86ebca3931599f3a911c) )
ROM_END

ROM_START( sentx6pd )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "senariodeluxetvpoker.bin", 0x000000, 0x800000, CRC(e2b6844e) SHA1(2413195dfe7a08d16ca870c070a19034c9fe8c30) )
ROM_END

void sentx6p_state::init_sentx6puk()
{
	m_pchackaddress1 = 0x2981e;
	m_pchackaddress2 = 0x29834;
	m_controller_sense_addr = 0x1e08;
}

void sentx6p_state::init_sentx6p()
{
	m_pchackaddress1 = 0x29754;
	m_pchackaddress2 = 0x2976a;
	m_controller_sense_addr = 0x0804;
}

void sentx6p_state::init_sentx6pd()
{
	m_pchackaddress1 = 0xca20;
	m_pchackaddress2 = 0xca36;
	m_controller_sense_addr = 0x1e3a;
}

} // anonymous namespace


CONS( 2004, sentx6p,    0,           0,        sentx6p,     sentx6p, sentx6p_state, init_sentx6p,   "Senario",               "Vs Maxx Texas Hold'em TV Poker - 6 Player Edition (US)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // from a US version, values in USD
CONS( 2004, sentx6puk,  sentx6p,     0,        sentx6p,     sentx6p, sentx6p_state, init_sentx6puk, "Senario / Play Vision", "Vs Maxx Texas Hold'em TV Poker - 6 Player Edition (UK)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // from a UK Play Vision branded box, values in GBP

CONS( 2004, sentx6pd,   0,           0,        sentx6p,     sentx6p, sentx6p_state, init_sentx6pd,  "Senario",               "Deluxe TV Poker - Texas Hold'em, Blackjack & Video Poker (US)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
// Deluxe version wasn't released outside of US?



spg2xx_senca.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

/* Shenzhen Senca Technology Co., Ltd developed "Family Sport" systems - sold by various manufacturers */

#include "emu.h"
#include "spg2xx.h"


namespace {

class zon32bit_state : public spg2xx_game_state
{
public:
	zon32bit_state(const machine_config& mconfig, device_type type, const char* tag) :
		spg2xx_game_state(mconfig, type, tag),
		m_romregion(*this, "maincpu")
	{ }

	void zon32bit(machine_config& config);
	void zon32bit_bat(machine_config& config);

	void mem_map_zon32bit(address_map &map) ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void device_post_load() override;

	uint16_t z32_rom_r(offs_t offset);

	required_region_ptr<uint16_t> m_romregion;

	virtual uint16_t porta_r();
	virtual uint16_t portb_r();
	virtual uint16_t portc_r();

	virtual void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
	virtual void portb_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
	virtual void portc_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;

	uint16_t an3_r();

	int m_porta_dat = 0;
	int m_portb_dat = 0;
	int m_portc_dat = 0;
	int m_porta_mask = 0;

	int m_upperbank = 0;

	int m_basebank = 0;
};

class mywicogt_state : public zon32bit_state
{
public:
	mywicogt_state(const machine_config& mconfig, device_type type, const char* tag) :
		zon32bit_state(mconfig, type, tag)
	{ }

protected:
	virtual void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
};


class mywicodx_state : public zon32bit_state
{
public:
	mywicodx_state(const machine_config& mconfig, device_type type, const char* tag) :
		zon32bit_state(mconfig, type, tag)
	{ }

protected:
	virtual void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;

	virtual void machine_reset() override ATTR_COLD;
};

class oplayer_100in1_state : public mywicodx_state
{
public:
	oplayer_100in1_state(const machine_config& mconfig, device_type type, const char* tag) :
		mywicodx_state(mconfig, type, tag)
	{ }

	void init_oplayer();
	void init_m505neo();
	void init_cdlyoko();

protected:
	virtual uint16_t porta_r() override;
	virtual uint16_t portb_r() override;
	virtual uint16_t portc_r() override;
};

class denver_200in1_state : public mywicodx_state
{
public:
	denver_200in1_state(const machine_config& mconfig, device_type type, const char* tag) :
		mywicodx_state(mconfig, type, tag)
	{ }

	void init_denver();
	void init_m521neo();

protected:
	virtual void machine_reset() override ATTR_COLD;

	virtual uint16_t porta_r() override;
	virtual uint16_t portb_r() override;
	virtual uint16_t portc_r() override;

	virtual void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
};

uint16_t zon32bit_state::an3_r()
{
	int status = ioport("BATT")->read();
	if (status)
		return 0xfff;
	else
		return 0x000;
}

void zon32bit_state::device_post_load()
{
	// load state can change the bank, so we must invalide cache
	m_maincpu->invalidate_cache();
}

uint16_t zon32bit_state::porta_r()
{
	return m_porta_dat;
}


void zon32bit_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (0)
		logerror("%s: porta_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
			(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
			(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
			(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
			(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
			(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
			(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
			(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
			(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
			(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
			(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
			(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
			(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
			(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
			(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
			(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
			(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');

	m_porta_dat = data;

	// The banking on zon32bit doesn't seem the same as mywicodx.
	// The same values get written here both in the case of switching to upper bank and switching to lower bank, so presumably it must be some kind of toggle
	if (data == 0x0e01)
	{
		m_basebank ^= 1;
		logerror("bank is now %d\n", m_basebank);
		m_maincpu->invalidate_cache();
	}
}


void mywicogt_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (1)
		logerror("%s: porta_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
			(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
			(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
			(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
			(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
			(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
			(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
			(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
			(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
			(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
			(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
			(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
			(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
			(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
			(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
			(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
			(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');

//[:] ':maincpu' (000508): porta_w 0b00 (0f00) x x x x | 1 0 1 1 | x x x x | x x x x
//[:] ':maincpu' (000510): porta_w 0b00 (0f00) x x x x | 1 0 1 1 | x x x x | x x x x
//[:] ':maincpu' (000518): porta_w 0f00 (0f00) x x x x | 1 1 1 1 | x x x x | x x x x

	if (m_maincpu->pc() < 0x1000)
	{
		if (data == 0x0f00)
		{
			m_basebank = 1;
			m_maincpu->invalidate_cache();
			logerror("changing to bank 1\n");

		}
		else if (data == 0x0b00)
		{
			m_basebank = 0;
			m_maincpu->invalidate_cache();
			logerror("changing to bank 0\n");
		}
	}

	m_porta_dat = data;
}


void mywicodx_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (0)
		logerror("%s: porta_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
			(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
			(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
			(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
			(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
			(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
			(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
			(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
			(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
			(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
			(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
			(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
			(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
			(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
			(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
			(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
			(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');


	m_porta_dat = data;

	int oldbank = m_basebank;

	if (mem_mask & 0x0400)
	{
		if (data & 0x0400)
			m_basebank |= 1;
		else
			m_basebank &= ~1;
	}

	if (mem_mask & 0x0800)
	{
		if (data & 0x0800)
			m_basebank |= 2;
		else
			m_basebank &= ~2;
	}

	if (oldbank != m_basebank)
		m_maincpu->invalidate_cache();


}


void denver_200in1_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (0)
	{
		if (m_maincpu->pc() < 0x10000)
		{
			logerror("%s: porta_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
				(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
				(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
				(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
				(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
				(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
				(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
				(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
				(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
				(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
				(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
				(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
				(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
				(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
				(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
				(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
				(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');
		}
	}

	if (m_maincpu->pc() < 0x10000)
	{
		int oldbank = m_basebank;

		if (mem_mask & 0x0200)
		{
			if (data & 0x0200)
			{
				m_basebank |= 4;
			}
			else
			{
				m_basebank &= ~4;
			}
		}

		if (mem_mask & 0x0400)
		{
			if (data & 0x0400)
			{
				m_basebank |= 1;
			}
			else
			{
				m_basebank &= ~1;
			}
		}

		if (mem_mask & 0x0800)
		{
			if (data & 0x0800)
			{
				m_basebank |= 2;
			}
			else
			{
				m_basebank &= ~2;
			}
		}

		if (oldbank != m_basebank)
			m_maincpu->invalidate_cache();
	}
}


uint16_t zon32bit_state::portc_r()
{
	// 0x03ff seem to be inputs for buttons (and some kind of output?)
	// 0xfc00 gets masked for other reasons (including banking?)

	// returning same value written for 0x0400 means controls don't respond (some kind of direction flag?)

	uint16_t dat = m_io_p3->read() & ~0xf800;

	dat |= (m_portc_dat & 0xf800);

	return dat;
}


uint16_t zon32bit_state::portb_r()
{
	return m_portb_dat;
}

void zon32bit_state::portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (data != 0x0001)
		logerror("%s: portb_w %04x (%04x)\n", machine().describe_context(), data, mem_mask);

	m_portb_dat = data;
}

void zon32bit_state::portc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	// very noisy
	// is the code actually sending the sound to the remotes?
	if (0)
		logerror("%s: portc_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c\n", machine().describe_context(), data, mem_mask,
			(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
			(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
			(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
			(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
			(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
			(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
			(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
			(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
			(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
			(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
			(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
			(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
			(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
			(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
			(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
			(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');

	int oldbank = m_upperbank;

	if (mem_mask & 0x1000)
	{
		if (data & 0x1000)
			m_upperbank |= 0x1000;
		else
			m_upperbank &= ~0x1000;
	}

	if (mem_mask & 0x0800)
	{
		if (data & 0x0800)
			m_upperbank |= 0x0800;
		else
			m_upperbank &= ~0x0800;
	}

	if (oldbank != m_basebank)
		m_maincpu->invalidate_cache();

	m_portc_dat = data;
}


uint16_t oplayer_100in1_state::portc_r()
{
	return m_io_p3->read();
}

uint16_t oplayer_100in1_state::portb_r()
{
	return m_io_p2->read();
}

uint16_t oplayer_100in1_state::porta_r()
{
	return 0x0ff8 | (machine().rand()&1);
}

void zon32bit_state::mem_map_zon32bit(address_map &map)
{
	map(0x000000, 0x3fffff).r(FUNC(zon32bit_state::z32_rom_r));
}

uint16_t zon32bit_state::z32_rom_r(offs_t offset)
{
	/*
	    This has upper and lower bank, which can be changed independently.
	    Banking hookup is currently very hacky as bank values are written
	    to ports then erased at the moment, maybe they latch somehow?
	*/

	int base = 0x0000000;

	if (m_basebank & 4)  base |= 0x4000000;
	if (m_basebank & 2)  base |= 0x2000000;
	if (m_basebank & 1)  base |= 0x1000000;

	if (offset < 0x200000)
	{
		return m_romregion[offset + (base / 2)];
	}
	else
	{
		offset &= 0x1fffff;

		if (m_upperbank & 0x1000) base |= 0x0400000;
		if (m_upperbank & 0x0800) base |= 0x0800000;

		return m_romregion[offset + (base / 2)];
	}

	return 0x0000;// m_romregion[offset];
}

uint16_t denver_200in1_state::portc_r()
{
	return m_io_p3->read();
}

uint16_t denver_200in1_state::portb_r()
{
	return m_io_p2->read();
}

uint16_t denver_200in1_state::porta_r()
{
	return 0x0ff8 | (machine().rand()&1);
}


void zon32bit_state::machine_start()
{
	spg2xx_game_state::machine_start();

	m_basebank = 0;

	save_item(NAME(m_porta_dat));
	save_item(NAME(m_portb_dat));
	save_item(NAME(m_portc_dat));
	save_item(NAME(m_porta_mask));
	save_item(NAME(m_upperbank));
	save_item(NAME(m_basebank));
}


void zon32bit_state::machine_reset()
{
	spg2xx_game_state::machine_reset();

	m_porta_dat = 0xffff;
	m_portb_dat = 0x0000;

	m_basebank = 0;
	m_maincpu->invalidate_cache();
}

void mywicodx_state::machine_reset()
{
	zon32bit_state::machine_reset();
	m_basebank = 3;
	m_maincpu->invalidate_cache();
}

void denver_200in1_state::machine_reset()
{
	zon32bit_state::machine_reset();
	m_basebank = 6;
	m_maincpu->invalidate_cache();
}



static INPUT_PORTS_START( zon32bit )
	PORT_START("P1")
	PORT_DIPNAME( 0x0001, 0x0001, "P1" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P2")
	PORT_DIPNAME( 0x0001, 0x0001, "P2" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_NAME("Up (vertical) Left (horizontal)")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_NAME("Down (vertical) Right (horizontal)")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_NAME("Left (vertical) Down (horizontal)")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_NAME("Right (vertical) Up (horizontal)")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON4 )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON3 )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("B")
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Pause / Menu")
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( mywicogt )
	PORT_START("P1")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Green / Left")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Red / Right")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Yellow")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Blue")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Orange")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Start / Pause / Menu")
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Back")
	PORT_BIT( 0xfe00, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


static INPUT_PORTS_START( oplayer )
	PORT_START("P1")
	PORT_DIPNAME( 0x0001, 0x0001, "P1" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON3 )
	PORT_BIT( 0xff80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_DIPNAME( 0x0001, 0x0001, "P3" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("BATT")
	PORT_CONFNAME( 0x0001,  0x0001, "Battery Status" )
	PORT_CONFSETTING(       0x0000, "Low" )
	PORT_CONFSETTING(       0x0001, "High" )
INPUT_PORTS_END


void zon32bit_state::zon32bit(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &zon32bit_state::mem_map_zon32bit);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(zon32bit_state::porta_r));
	m_maincpu->portb_in().set(FUNC(zon32bit_state::portb_r));
	m_maincpu->portc_in().set(FUNC(zon32bit_state::portc_r));

	m_maincpu->porta_out().set(FUNC(zon32bit_state::porta_w));
	m_maincpu->portb_out().set(FUNC(zon32bit_state::portb_w));
	m_maincpu->portc_out().set(FUNC(zon32bit_state::portc_w));
}

void zon32bit_state::zon32bit_bat(machine_config& config)
{
	zon32bit(config);
	m_maincpu->adc_in<3>().set(FUNC(zon32bit_state::an3_r));
}

ROM_START( mywicodx )
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASE00 )
	// the first bank contains the Mi Guitar game, the 2nd half of the ROM is where the Menu starts
	ROM_LOAD16_WORD_SWAP( "mywicodx.u2", 0x0000000, 0x4000000, CRC(ec7c5d2f) SHA1(330fb839c485713f7bec5bf9d2d42841612c5b45))
ROM_END


ROM_START( zon32bit )
	ROM_REGION( 0x2000000, "maincpu", ROMREGION_ERASE00 ) // probably should just swap upper 2 line of ROM, as pinout was unknown
	ROM_LOAD16_WORD_SWAP( "41sports.bin", 0x0000000, 0x0800000, CRC(86eee6e0) SHA1(3f6cab6649aebf596de5a8af21658bb1a27edb10) )
	ROM_CONTINUE(0x1000000, 0x0800000)
	ROM_CONTINUE(0x0800000, 0x0800000)
	ROM_CONTINUE(0x1800000, 0x0800000)
ROM_END

ROM_START( mywicogt )
	ROM_REGION( 0x2000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "mywicoguitar.bin", 0x0000000, 0x0800000, CRC(3c037c50) SHA1(3b9a28fb643c9f90563d653be0f38eba09c26f26) )
	ROM_CONTINUE(0x1000000, 0x0800000)
	ROM_CONTINUE(0x0800000, 0x0800000)
	ROM_CONTINUE(0x1800000, 0x0800000)
ROM_END


/*
    Following pinout was used for dumping

      +------------------+
  VCC -|01              70|- VCC
  A23 -|02              69|- A21
  VCC -|03              68|- A22
  A18 -|04              67|- A08
  A24 -|05              66|- A09
  A07 -|06              65|- A10
  A06 -|07              64|- A11
  A05 -|08              63|- A12
  A04 -|09              62|- A13
  A03 -|10              61|- A14
  A02 -|11              60|- A15
  A01 -|12              59|- A16
  A00 -|13              58|- A17
   NC -|14              57|- A20
   NC -|15              56|- A19
  /CE -|16              55|- VCC (/BYTE)
   NC -|17              54|- NC
  GND -|18   55LV512    53|- GND
   NC -|19              52|- NC
   NC -|20              51|- NC
   NC -|21              50|- NC
   NC -|22              49|- NC
   NC -|23              48|- NC
  /OE -|24              47|- NC
   NC -|25              46|- NC
  D08 -|26              45|- NC
  D09 -|27              44|- D00
  D10 -|28              43|- D01
  D11 -|29              42|- D02
   NC -|30              41|- D03
  D12 -|31              40|- D04
  D13 -|32              39|- D05
  D14 -|33              38|- D06
  D15 -|34              37|- D07
  GND -|35              36|- VCC
       +------------------+
*/

// Sunplus QL8041C die
ROM_START( oplayer )
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "oplayer.bin", 0x0000000, 0x4000000, CRC(aa09c358) SHA1(df2855cdfdf2b693636cace8768e579b9d5bc657) )
ROM_END

ROM_START( cdlyoko ) // P1-25IN1-MAIN-V11 2011.05.09 on PCB
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "clg001z.u2", 0x0000000, 0x1000000, CRC(1e73b910) SHA1(6f6cece054fcf91ff78962358804471794c58615) )
	ROM_RELOAD(0x1000000,0x1000000)
	ROM_RELOAD(0x2000000,0x1000000)
	ROM_RELOAD(0x3000000,0x1000000)
ROM_END

ROM_START( dnv200fs )
	ROM_REGION( 0x8000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "famsport200in1.u2", 0x0000000, 0x8000000, CRC(f59221e2) SHA1(d532cf5a80ffe9d527efcccbf380a7a860f0fbd9) )
ROM_END

ROM_START( m505neo )
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASE00 )
	// 1st and 2nd half are identical apart from one bit.  Likely used double capacity ROM and only wired up half of it because ROM had a problem?
	// the data segment in question is identical to one in oplayer and suggests the first half of the ROM here is correct with the bit being set
	// incorrectly in the 2nd half of the ROM.
	ROM_LOAD16_WORD_SWAP( "m505arcadeneo.u2", 0x0000000, 0x4000000, CRC(b72bdbe1) SHA1(263b60148980ac1f82546e2449b1dd938b7b827c) )
	ROM_IGNORE(0x4000000)
ROM_END

ROM_START( m521neo )
	ROM_REGION( 0x8000000, "maincpu", ROMREGION_ERASE00 ) // was this dumped with some address lines swapped?
	ROM_LOAD16_WORD_SWAP( "6gu-1cd-a.u2", 0x0000000, 0x800000, CRC(7cb31b4c) SHA1(8de44756747a292c5d39bd491048d6fac4219953) )
	ROM_CONTINUE(0x01000000, 0x800000)
	ROM_CONTINUE(0x00800000, 0x800000)
	ROM_CONTINUE(0x01800000, 0x800000)

	ROM_CONTINUE(0x02000000, 0x800000)
	ROM_CONTINUE(0x03000000, 0x800000)
	ROM_CONTINUE(0x02800000, 0x800000)
	ROM_CONTINUE(0x03800000, 0x800000)

	ROM_CONTINUE(0x04000000, 0x800000)
	ROM_CONTINUE(0x05000000, 0x800000)
	ROM_CONTINUE(0x04800000, 0x800000)
	ROM_CONTINUE(0x05800000, 0x800000)

	ROM_CONTINUE(0x06000000, 0x800000)
	ROM_CONTINUE(0x07000000, 0x800000)
	ROM_CONTINUE(0x06800000, 0x800000)
	ROM_CONTINUE(0x07800000, 0x800000)
ROM_END


void oplayer_100in1_state::init_oplayer()
{
	// TODO: remove these hacks
	uint16_t* rom = (uint16_t*)memregion("maincpu")->base();
	// port a checks when starting a game in any given bank / starting the system
	rom[0x0f851 + (0x0000000 / 2)] = 0xf165;
	rom[0xaad1e + (0x1000000 / 2)] = 0xf165;
	rom[0x47d2d + (0x2000000 / 2)] = 0xf165;
	rom[0x1fb00 + (0x3000000 / 2)] = 0xf165;
	// port a checks when exiting a game in any given bank
	rom[0x7a506 + (0x0000000 / 2)] = 0xf165;
	rom[0xad051 + (0x1000000 / 2)] = 0xf165;
	rom[0xc351e + (0x3000000 / 2)] = 0xf165;
}

/*

oplayer   m505
---------------
0001 |    0001    0
0002 |    0100    8
0004 |    0002    1
0008 |    0200    9
0010 |    0004    2
0020 |    0400   10
0040 |    0008    3
0080 |    0800   11


0100 |    8000   15
0200 |    0080    7
0400 |    4000   14
0800 |    0040    6
1000 |    2000   13
2000 |    0020    5
4000 |    1000   12
8000 |    0010    4

*/

void oplayer_100in1_state::init_m505neo()
{
	uint16_t *ROM = (uint16_t*)memregion("maincpu")->base();
	int size = memregion("maincpu")->bytes();

	for (int i = 0; i < size / 2; i++)
	{
		ROM[i] = bitswap<16>(ROM[i],
									 11,  3,   10,  2,
									 9,  1,  8,  0,

									 4, 12, 5, 13,
									 6, 14,  7,  15
			);

	}

	// TODO: remove these hacks
	// port a checks when starting the system
	ROM[0x43c30 + (0x2000000 / 2)] = 0xf165; // boot main bank
}

void oplayer_100in1_state::init_cdlyoko()
{
	uint16_t* rom16 = (uint16_t*)memregion("maincpu")->base();
	int size = memregion("maincpu")->bytes();

	std::vector<u16> buffer(size / 2);

	for (int i = 0; i < size / 2; i++)
	{
		buffer[bitswap<25>(i, 0x18, 0x17, 0x16, 0x15, 0x13, 0x8, 0x12, 0x11, 0x14, 0x10, 0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0)] = rom16[i];
	}

	std::copy(buffer.begin(), buffer.end(), &rom16[0]);

	// patch a startup check, like oplayer
	for (int i = 0; i < 4; i++)
		rom16[0x493ed + ((0x1000000 * i) / 2)] = 0xf165;
}


void denver_200in1_state::init_denver()
{
	// TODO: remove these hacks

	// patch checks when booting each bank, similar to oplayer
	uint16_t* rom = (uint16_t*)memregion("maincpu")->base();
	//rom[0x175f7 + (0x0000000 / 2)] = 0xf165;
	//rom[0x18f47 + (0x1000000 / 2)] = 0xf165;
	//rom[0x33488 + (0x2000000 / 2)] = 0xf165;
	//rom[0x87f81 + (0x3000000 / 2)] = 0xf165;
	//rom[0x764d9 + (0x4000000 / 2)] = 0xf165;
	//rom[0xb454e + (0x5000000 / 2)] = 0xf165;
	rom[0x43c30 + (0x6000000 / 2)] = 0xf165; // boot main bank
	//rom[0x1fb00 + (0x7000000 / 2)] = 0xf165;
}

void denver_200in1_state::init_m521neo()
{
	uint16_t* rom = (uint16_t*)memregion("maincpu")->base();
	rom[0x43c30 + (0x6000000 / 2)] = 0xf165; // boot main bank
}

} // anonymous namespace


// Box advertises this as '40 Games Included' but the cartridge, which was glued directly to the PCB, not removable, is a 41-in-1.  Maybe some versions exist with a 40 game selection.
CONS( 200?, zon32bit,  0, 0, zon32bit, zon32bit, zon32bit_state,  empty_init,      "Ultimate Products (HK) Ltd / Senca",    "Zone 32-bit Gaming Console System (Family Sport 41-in-1)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
// My Wico Deluxe was also available under the MiWi brand (exact model unknown, but it was a cart there instead of built in)
// Box claimed 53 Arcade Games + 8 Sports games + 24 Music games, although it's unclear where 24 Music Games comes from, there are 3, which are identical aside from the title screen.
// The Mi Guitar menu contains 24 games, but they're dupes, and just counting those would exclude the other Mi Fit and Mi Papacon menus (which also contain dupes)
CONS( 200?, mywicodx,  0, 0, zon32bit, zon32bit, mywicodx_state,  empty_init,      "<unknown> / Senca",                                   "My Wico Deluxe (Family Sport 85-in-1)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// Shows Mi Guitar 2 in the menu, it seems likely that there was an earlier version on VT1682 hardware as there is a very similar Guitar game (with the same song selection) in those multigames
CONS( 200?, mywicogt,  0, 0, zon32bit, mywicogt, mywicogt_state,  empty_init,      "<unknown> / Senca",                                   "My Wico Guitar", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 200?, oplayer,   0, 0, zon32bit_bat, oplayer, oplayer_100in1_state, init_oplayer, "OPlayer / Senca", "OPlayer Mobile Game Console (MGS03-white) (Family Sport 100-in-1)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2012, m505neo,   0, 0, zon32bit_bat, oplayer, oplayer_100in1_state, init_m505neo, "Millennium 2000 GmbH / Senca", "Millennium M505 Arcade Neo Portable Spielkonsole (Family Sport 100-in-1)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2011, cdlyoko,   0, 0, zon32bit_bat, oplayer, oplayer_100in1_state, init_cdlyoko, "Ingo Devices SL / Senca", "Code Lyoko (25-in-1 handheld)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// a version of this exists with the 'newer' style title screen seen in m505neo
CONS( 2012, m521neo,   0, 0, zon32bit_bat, oplayer, denver_200in1_state,  init_m521neo, "Millennium 2000 GmbH / Senca", "Millennium M521 Arcade Neo 2.0 (Family Sport 220-in-1) ", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

/*
DENVER(r)

PO:9075
Model: GMP-270CMK2
OPERATED BY 3 X AAA-BATTERIES (NOT INCL)
OR MINI-USB POWER SOURCE
Power consumption:0.6W/hour
Standby consumption:0.25mW/hour
Imported by:
DENVER ELECTRONICS A/S
Stavneagervej 22
DK-8250 EGAA
DENMARK

*/

CONS( 200?, dnv200fs,   0, 0, zon32bit_bat, oplayer, denver_200in1_state, init_denver, "Denver / Senca", "Denver (GMP-270CMK2) (Family Sport 200-in-1)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )





spg2xx_shredmjr.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

#include "emu.h"
#include "spg2xx.h"


namespace {

class shredmjr_game_state : public spg2xx_game_state
{
public:
	shredmjr_game_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_state(mconfig, type, tag),
		m_porta_data(0x0000),
		m_shiftamount(0)
	{ }

	void shredmjr(machine_config &config);
	void taikeegr(machine_config &config);
	void taikeegrp(machine_config &config);

	void init_taikeegr();

protected:
	uint16_t porta_r();
	virtual void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;

private:
	uint16_t m_porta_data;
	int m_shiftamount;

};


// Shredmaster Jr uses the same input order as the regular Taikee Guitar, but reads all inputs through a single multiplexed bit
void shredmjr_game_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (data != m_porta_data)
	{
		if ((data & 0x0800) != (m_porta_data & 0x0800))
		{
			if (data & 0x0800)
			{
				//logerror("0x0800 low -> high\n");
			}
			else
			{
				//logerror("0x0800 high -> low\n");
			}
		}

		if ((data & 0x0200) != (m_porta_data & 0x0200))
		{
			if (data & 0x0200)
			{
				//logerror("0x0200 low -> high\n");
				m_shiftamount++;
			}
			else
			{
				//logerror("0x0200 high -> low\n");
			}
		}

		if ((data & 0x0100) != (m_porta_data & 0x0100))
		{
			if (data & 0x0100)
			{
				//logerror("0x0100 low -> high\n");
				m_shiftamount = 0;
			}
			else
			{
				//logerror("0x0100 high -> low\n");
			}
		}
	}

	m_porta_data = data;
}

uint16_t shredmjr_game_state::porta_r()
{
	//logerror("porta_r with shift amount %d \n", m_shiftamount);
	uint16_t ret = 0x0000;

	uint16_t portdata = m_io_p1->read();

	portdata = (portdata >> m_shiftamount) & 0x1;

	if (portdata)
		ret |= 0x0400;

	return ret;
}

static INPUT_PORTS_START( taikeegr )
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )   PORT_NAME("Strum Bar Down")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_NAME("Strum Bar Up")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("Whamming Bar")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Yellow")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Green")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Red")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Blue")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("Pink")
	PORT_BIT( 0xff00, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

static INPUT_PORTS_START( guitarstp )
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )   PORT_NAME("Strum Bar Down")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_NAME("Strum Bar Up")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("Whamming Bar")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Yellow")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Blue")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Red")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Green")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("Orange")
	PORT_BIT( 0xff00, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END


void shredmjr_game_state::shredmjr(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &shredmjr_game_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(shredmjr_game_state::porta_r));
	m_maincpu->porta_out().set(FUNC(shredmjr_game_state::porta_w));
}

void shredmjr_game_state::taikeegr(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &shredmjr_game_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set_ioport("P1");
}

void shredmjr_game_state::taikeegrp(machine_config &config)
{
	taikeegr(config);

	m_maincpu->set_pal(true);
	m_screen->set_refresh_hz(50);
}


void shredmjr_game_state::init_taikeegr()
{
	u16 *src = (u16*)memregion("maincpu")->base();

	for (int i = 0x00000; i < 0x800000/2; i++)
	{
		u16 dat = src[i];
		dat = bitswap<16>(dat,  15,14,13,12,   11,10,9,8,    7,6,5,4,   0,1,2,3 );
		src[i] = dat;
	}

	std::vector<u16> buffer(0x800000/2);

	for (int i = 0; i < 0x800000/2; i++)
	{
		int j = 0;

		switch (i & 0x00e00)
		{
		case 0x00000: j = (i & 0xfff1ff) | 0x000; break;
		case 0x00200: j = (i & 0xfff1ff) | 0x800; break;
		case 0x00400: j = (i & 0xfff1ff) | 0x400; break;
		case 0x00600: j = (i & 0xfff1ff) | 0xc00; break;
		case 0x00800: j = (i & 0xfff1ff) | 0x200; break;
		case 0x00a00: j = (i & 0xfff1ff) | 0xa00; break;
		case 0x00c00: j = (i & 0xfff1ff) | 0x600; break;
		case 0x00e00: j = (i & 0xfff1ff) | 0xe00; break;
		}

		buffer[j] = src[i];
	}

	std::copy(buffer.begin(), buffer.end(), &src[0]);
}


ROM_START( taikeegr )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "taikee_guitar.bin", 0x000000, 0x800000, CRC(8cbe2feb) SHA1(d72e816f259ba6a6260d6bbaf20c5e9b2cf7140b) )
ROM_END

ROM_START( rockstar )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "29gl064.bin", 0x000000, 0x800000, CRC(40de50ff) SHA1(b33ae7a3d32911addf833998d7419f4830be5a07) )
ROM_END

ROM_START( shredmjr )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "shredmasterjr.bin", 0x000000, 0x800000, CRC(95a6dcf1) SHA1(44893cd6ebe6b7f33a73817b72ae7be70c3126dc) )
ROM_END

ROM_START( guitarst )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "guitarstar_s29gl064m11tfir4_0001227e.bin", 0x000000, 0x800000, CRC(feaace47) SHA1(dd426bb4f03a16b1b96b63b4e0d79ea75097bf72) )
ROM_END

ROM_START( guitarstp )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "29gl064.u2", 0x000000, 0x800000, CRC(1dbcff73) SHA1(b179e4da6f38e7d5ec796bf846a63492d30eb0f5) )
ROM_END

} // anonymous namespace




// These were all sold as different products, use a different sets of songs / presentation styles (2D or perspective gameplay, modified titlescreens etc.)
// and sometimes even slightly different hardware, so aren't set as clones of each other

// box title not confirmed, Guitar Rock on title screen, has Bon Jovi etc.
CONS( 2007, taikeegr,    0,        0,        taikeegrp,    taikeegr, shredmjr_game_state, init_taikeegr, "TaiKee", "Guitar Rock (PAL)", MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // timing not quite correct yet

// Plug 'N' Play Rockstar Guitar on box, Guitar Rock on title screen, has Manic Street Preachers etc.
CONS( 2007, rockstar,    0,        0,        taikeegrp,    taikeegr, shredmjr_game_state, init_taikeegr, "Ultimate Products / TaiKee", "Plug 'N' Play Rockstar Guitar / Guitar Rock (PAL)", MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // timing not quite correct yet

// dreamGEAR branded presentation, modified hardware (buttons read in a different way) same song seletion as taikeegr
CONS( 2007, shredmjr,    0,        0,        shredmjr,     taikeegr, shredmjr_game_state, init_taikeegr, "dreamGEAR", "Shredmaster Jr (NTSC)", MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // ^

// doesn't have a Senario logo ingame, but does on box.  unique song selection
CONS( 200?, guitarst,    0,        0,        taikeegr,     taikeegr, shredmjr_game_state, init_taikeegr, "Senario", "Guitar Star (US, Senario, NTSC)", MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // ^

// This one has the same songs as 'rockstar' but different game style / presentation.
// Unit found in Ireland "imported by Cathay Product Sourcing Ltd." on the box, with address in Ireland
// ITEM #01109 on instruction sheet, no manufacturer named on either box or instructions
CONS( 200?, guitarstp,   0,        0,        taikeegrp,    guitarstp,shredmjr_game_state, init_taikeegr, "<unknown>", "Guitar Star (Europe, PAL)", MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // ^



spg2xx_skannerztv.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

// Skannerz TV

/* I/O related notes from Tahg

Summary:
SEND(from console)              RECEIVE (to console)
--- Load monsters / items
1                               2
128 + 0                         M/I[0] HIBYTE, LOBYTE
...                             ...
128 + 99                        M/I[99] HIBYTE, LOBYTE
--- Save monsters / items
4                               5
128 + 0, M/I[0] HIBYTE, LOBYTE  6
...                             ...
128 + 99, M/I[99] HIBYTE, LOBYTE 6
--- Buttons
3                               BHI, BLO

Buttons
BLO:
Bit 0     Up
Bit 1     Down
Bit 2     2/B
Bit 3     1/A
Bit 4     Left
Bit 5     Right
Bit 6     Unused
Bit 7     Unused
BHI:      Unused

Port A
Bit 0     Enable "Left" Controller
Bit 1     Enable "Right" Controller

Player
0:
8:
B:
D:
E:
65:  monsters
B5:  items

doPlayer(player)
     var_3 = player - 1
     pData = Player[var_3]
     switch(PlayerState[var_3])
          case 0: // Request controller -> console state
               r1 = 79
               while(--r1 >= 0)
               PlayerState[var_3] = 1
               P_UART_Ctrl |= TxEn
               P_UART_Ctrl |= TxIntEn
               while(P_UART_Status & TXBUSY)
               P_UART_Ctrl &= ~RxEn
               P_UART_TxBuf = 1
               WaitCycle[var_3] = 6
               CurMI[var_3] = 0
               break
          case 1: // Receive controller -> console state (expects 2)
               if(Uartp != Uartg)
                    byte_1241 = UARTBuffer[Uartg]
                    if(byte_1241 == 2)
                         PlayerState[var_3] = 2
                         InitPlayer(var_3)
                    Uartg = Uartg == 7 ? 0 : Uartg + 1
               break
          case 2: // Sync monster / item
               if(CurMI[var_3] != 100)
                    var_6 = CurMI[var_3] | 80h
                    while(P_UART_Status & TXBUSY)
                    P_UART_Ctrl &= ~RxEn
                    P_UART_TxBuf = var_6
                    PlayerState[var_3] = 3
                    WaitCycle[var_3] = 6
                    Retries[var_3] = 3
                    CurRecByte[var_3] = 0
               else if(player == 1 && byte_1238 & 100h)
                    PlayerState[var_3] = 6
                    byte_1238 &= ~100h
               else if(player == 2 && byte_1238 & 200h)
                    PlayerState[var_3] = 6
                    byte_1238 &= ~200h
               else
                    PlayerState[var_3] = 4
                    if(player == 1)
                         P_IOA_Buffer |= 1
                         byte_1238 |= 11
                    else if(player == 2)
                         P_IOA_Buffer |= 2
                         byte_1238 |= 22
               break
          case 3: // Receive monster / item
               if(Uartp != Uartg)
                    r4 = CurMI[var_3]
                    var_A = UARTBuffer[Uartg]
                    if(CurRecByte[var_3] == 0)
                         if(r4 > 79)
                              pData.items[r4-80] = var_A << 8
                         else pData.monsters[r4] = var_A << 8
                         CurRecByte[var_3] += 1
                    else if(CurRecByte[var_3] == 1)
                         if(r4 > 79)
                              pData.items[r4-80] |= var_A
                         else pData.monsters[r4] |= var_A
                         PlayerState[var_3] = 2
                         CurMI[var_3] += 1
                    Uartg = Uartg == 7 ? 0 : Uartg + 1
               break
          case 4:
               if(player == 1)
                    if((byte_1238 & 4) == 0)
                         byte_1238 |= 4
                         r1 = 79
                         while(--r1 >= 0)
                         while(P_UART_Status & TXBUSY)
                    else break
               else if(player == 2)
                    if((byte_1238 & 8) == 0)
                         byte_1238 |= 8
                         r1 = 79
                         while(--r1 >= 0)
                         while(P_UART_Status & TXBUSY)
                         break
                    else break
               else break
               P_UART_Ctrl &= ~RxEn
               P_UART_TxBuf = 3
               PlayerState[var_3] = 5
               WaitCycle[var_3] = 6
               Retries[var_3] = 3
               CurRecByte[var_3] = 0
               break
          case 5:
               if(Uartp != Uartg)
                    var_4 = CurRecByte[var_3]
                    if(var_4 == 0)
                         byte_1242[var_3] = UARTBuffer[Uartg] & 3fh
                         CurRecByte[var_3] += 1
                    else if(var_4 == 1)
                         byte_1234[var_3] = UARTBuffer[Uartg] & 3fh
                         PlayerState = 2
                         WaitCycle[var_3] = 0
                         if(player == 1)
                              P_IOA_Buffer |= 1
                              byte_1238 &= ~4
                         else if(player == 2)
                              P_IOA_Buffer |= 2
                              byte_1238 &= ~8
                    Uartg = Uartg == 7 ? 0 : Uartg + 1
               break:
          case 6: // Request console -> controller state
               r1 = 79
               while(--r1 >= 0)
               while(P_UART_Status & TXBUSY)
               P_UART_Ctrl &= ~RxEn
               P_UART_TxBuf = 4
               PlayerState[var_3] = 7
               WaitCycle[var_3] = 6
               CurMI[var_3] = 0
               Retries[var_3] = 3
          case 7: // Receive console -> controller state (expects 5)
               if(Uartp != Uartg)
                    if(UARTBuffer[Uartg] == 5)
                         PlayerState = 8
                         WaitCycle[var_3] = 6
                         CurSendByte[var_3] = 0
                         Retries[var_3] = 3
                    Uartg = Uartg == 7 ? 0 : Uartg + 1
               break
          case 8: // Send monster / item
               r4 = CurMI[var_3]
               if( r4 <= 99)
                    var_9 = CurSendByte[var_3]
                    var_8 = 0
                    if((P_UART_Status & TXBUSY) == 0)
                         var_8 = 1
                    if(var_8)
                         if(var_9 == 0)
                              var_1 = r4 | 80h
                              while(P_UART_Status & TXBUSY)
                              P_UART_Ctrl &= ~RxEn
                              P_UART_TxBuf = var_1
                              WaitCycle[var_3] = 6
                              CurSendByte[var_3] += 1
                         else if(var_9 == 1)
                              if (r4 <= 79)
                                   var_1 = (pData.monsters[r4] & 0xFF00) >> 8
                                   while(P_UART_Status & TXBUSY)
                                   P_UART_Ctrl &= ~RxEn
                                   P_UART_TxBuf = var_1
                              else
                                   var_1 = (pData.monsters[r4-80] & 0xFF00) >> 8
                                   while(P_UART_Status & TXBUSY)
                                   P_UART_Ctrl &= ~RxEn
                                   P_UART_TxBuf = var_1
                              WaitCycle[var_3] = 6
                              CurSendByte[var_3] += 1
                         else if(var_9 == 2)
                              if (r4 <= 79)
                                   var_1 = pData.monsters[r4] & 0xFF
                                   while(P_UART_Status & TXBUSY)
                                   P_UART_Ctrl &= ~RxEn
                                   P_UART_TxBuf = var_1
                              else
                                   var_1 = pData.monsters[r4-80] & 0xFF
                                   while(P_UART_Status & TXBUSY)
                                   P_UART_Ctrl &= ~RxEn
                                   P_UART_TxBuf = var_1
                              WaitCycle[var_3] = 6
                              CurSendByte[var_3] += 1
                              PlayerState[var_3] = 9
               else
                    PlayerState = 4
                    if(player == 1)
                         P_IOA_Buffer |= 1
                         byte_1238 |= 4
                    else if(player == 2)
                         P_IOA_Buffer |= 2
                         byte_1238 |= 8
               break
          case 9: // Receive monster / item sent state (expects 6)
               if(Uartp != Uartg)
                    if(UARTBuffer[Uartg] == 6)
                         PlayerState[var_3] = 8
                         WaitCycle[var_3] = 0
                         CurSendByte[var_3] = 0
                         CurMI[var_3] += 1
                         Retries[var_3] = 3
               break
     doPlayerMore(player)

doPlayerMore(player)
     var_2 = WaitCycle[player-1]
     if(var_2 == 0) return
     var_2--
     WaitCycle[player-1] = var_2
     if(var_2 != 0) return
     switch(PlayerState[player-1])
          case 3:
               if(Retries[player-1]--)
                    var_3 = CurMI[player-1] | 80h
                    while(P_UART_Status & TXBUSY)
                    P_UART_Ctrl = var_1 = P_UART_Ctrl & ~RxEn
                    P_UART_TxBuf = var_3
                    WaitCycle[player-1] = 6
                    CurRecByte[player-1] = 0
                    return
               break
          case 5:
               if(Retries[player-1]--)
                    while(P_UART_Status & TXBUSY)
                    P_UART_Ctrl = var_1 = P_UART_Ctrl & ~RxEn
                    P_UART_TxBuf = 3
                    WaitCycle[player-1] = 6
                    CurRecByte[player-1] = 0
                    return
               break
          case 7:
               if(Retries[player-1]--)
                    while(P_UART_Status & TXBUSY)
                    P_UART_Ctrl = var_1 = P_UART_Ctrl & ~RxEn
                    P_UART_TxBuf = 4
                    WaitCycle[player-1] = 6
                    return
               break
          case 9:
               if(Retries[player-1]--)
                    PlayerState = 8
                    CurSendByte[player] = 0
               else
                    ClearPlayer(player)
               return
          case 1:
               ClearPlayer(player)
               return
     ClearPlayer(player)
     if(player == 1)
          byte_1238 |= 40h
          byte_1238 &= ~10h
     else if(player == 2)
          byte_1238 |= 80h
          byte_1238 &= ~20h

*/

#include "emu.h"
#include "spg2xx.h"
#include "machine/nvram.h"


namespace {

class skannerztv_state : public spg2xx_game_state
{
public:
	skannerztv_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_state(mconfig, type, tag)
	{ }

	void rad_sktv(machine_config& config);

private:
};

static INPUT_PORTS_START( rad_sktv )
	/* how does the Scanner connect? probably some serial port with comms protocol, not IO ports?
	   internal test mode shows 'uart' ports (which currently fail)

	   To access internal test hold DOWN and BUTTON1 together on startup until a coloured screen appears.
	   To cycle through the tests again hold DOWN and press BUTTON1 */

	PORT_START("P1")
	PORT_DIPNAME( 0x0001, 0x0001, "IN0" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P2")
	PORT_DIPNAME( 0x0001, 0x0001, "IN1" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

void skannerztv_state::rad_sktv(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &skannerztv_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(skannerztv_state::base_porta_r));
	m_maincpu->portb_in().set(FUNC(skannerztv_state::base_portb_r));
	m_maincpu->portc_in().set(FUNC(skannerztv_state::base_portc_r));
	//m_maincpu->i2c_w().set(FUNC(skannerztv_state::i2c_w));
	//m_maincpu->i2c_r().set(FUNC(skannerztv_state::i2c_r));
}


ROM_START( rad_sktv )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "skannerztv.bin", 0x000000, 0x200000, CRC(e92278e3) SHA1(eb6bee5e661128d83784960dfff50379c36bfaeb) )

	/* The external scanner MCU is a Winbond from 2000: SA5641
	   the scanner plays sound effects when scanning, without being connected to the main unit, so a way to dump / emulate
	   this MCU is also needed for complete emulation

	   TODO: find details on MCU so that we know capacity etc. */
ROM_END

} // anonymous namespace


CONS( 2007, rad_sktv,  0,        0, rad_sktv, rad_sktv,   skannerztv_state, init_crc, "Radica", "Skannerz TV",                 MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING )



spg2xx_smarttv.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

// Sold in the US by Walmart under the KidConnection brand as SmarTV Adventures (only 2 US carts made?)
// Sold in Italy by Clementoni as Sapientino Smart TV (many different base units and carts)

// it's possible some of the base units have ROM, although the US version does not.

#include "emu.h"
#include "spg2xx.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "softlist_dev.h"


namespace {

class smarttv_state : public spg2xx_game_state
{
public:
	smarttv_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_state(mconfig, type, tag),
		m_cart(*this, "cartslot"),
		m_cart_region(nullptr)
	{ }

	void smarttv(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load_smarttv);

	required_device<generic_slot_device> m_cart;
	memory_region *m_cart_region;
};

// set 2862 to 0003 (irq enable) when it stalls on boot / between scenes (value is 0002 which doesn't have the required level enabled) is there an issue with the IRQ handling in the SoC core or is it disabling due to other reasons?
static INPUT_PORTS_START( smarttv )
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_DIPNAME( 0x0010, 0x0010, "1" )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Pause") // brings up menu
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Green") // verified from game
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Blue") // ^
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Yellow") // ^
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Red")
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x0000, "Must Be On" ) // will auto power-off going into menu otherwise
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, "PAL/NTSC (or region)" )
	PORT_DIPSETTING(      0x2000, "NTSC / US" ) // 'Zee' in Alphabet Caravans
	PORT_DIPSETTING(      0x0000, "PAL / UK") // 'Zed'
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Enter")
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P2")
	PORT_DIPNAME( 0x0001, 0x0001, "P2" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P3")
	PORT_DIPNAME( 0x0001, 0x0001, "P3" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
INPUT_PORTS_END


void smarttv_state::machine_start()
{
	spg2xx_game_state::machine_start();

	// if there's a cart, override the standard banking
	if (m_cart && m_cart->exists())
	{
		std::string region_tag;
		m_cart_region = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());
		m_bank->configure_entries(0, (m_cart_region->bytes() + 0x7fffff) / 0x800000, m_cart_region->base(), 0x800000);
		m_bank->set_entry(0);
	}
}

void smarttv_state::machine_reset()
{
	spg2xx_game_state::machine_reset();
}

DEVICE_IMAGE_LOAD_MEMBER(smarttv_state::cart_load_smarttv)
{
	uint32_t const size = m_cart->common_get_size("rom");

	if (size > 0x80'0000)
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported cartridge size (must be no more than 8M)");

	m_cart->rom_alloc(0x800000, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

void smarttv_state::smarttv(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &smarttv_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set_ioport("P1");
	m_maincpu->portb_in().set_ioport("P2");
	m_maincpu->portc_in().set_ioport("P3");

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "smarttv_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(smarttv_state::cart_load_smarttv));
	m_cart->set_must_be_loaded(true);

	SOFTWARE_LIST(config, "smarttv_cart").set_original("smarttv_cart");
}

ROM_START( smartvad )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	// no internal ROM (just the single SunPlus SoC glob)
ROM_END

} // anonymous namespace


// Toyquest games
CONS( 2005, smartvad,  0,        0, smarttv, smarttv,   smarttv_state, empty_init, "WinFun / KidConnection", "SmarTV Adventures",     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )




spg2xx_telestory.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

#include "emu.h"
#include "spg2xx.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "softlist_dev.h"


namespace {

class telestory_state : public spg2xx_game_state
{
public:
	telestory_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_state(mconfig, type, tag),
		m_cart(*this, "cartslot"),
		m_cart_region(nullptr)
	{ }

	void telestory(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void mem_map_4m_tsram(address_map &map) ATTR_COLD;

	uint16_t porta_r();
	uint16_t portb_r();
	uint16_t portc_r();

	virtual void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
	virtual void portb_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
	virtual void portc_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load_telestory);

	required_device<generic_slot_device> m_cart;
	memory_region *m_cart_region;
};


void telestory_state::mem_map_4m_tsram(address_map &map)
{
	map(0x000000, 0x3fffff).bankr("cartbank");
	map(0x3e0000, 0x3fffff).ram(); // is this in the cart or in the system?
}

static INPUT_PORTS_START( telestory ) // there is a hidden test mode, if you return rand() on this port you get it, TODO: figure out button combination
	PORT_START("P1") // I/O test also lists 'Headphone 1' but it isn't mapped to any of these ports?
	PORT_DIPNAME( 0x0001, 0x0001, "Port 1" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_NAME("Pause / Menu")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON8 ) PORT_NAME("Read To Me")
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Red / Triangle")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Blue / Square")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Yellow / Star")
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Green / Circle")
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Right Arrow / Next Page") // Test mode calls this Right Arrow / LED (so probalby LED output too)
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) ) // port changes on these bits advance the word in manual mode (scroll wheel)
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )  // port changes on these bits advance the word in manual mode (scroll wheel)
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Left Arrow / Previous Page")


	PORT_START("P2")
	PORT_DIPNAME( 0x0001, 0x0001, "Port 2" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P3")
	PORT_DIPNAME( 0x0001, 0x0001, "Port 3" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
INPUT_PORTS_END


uint16_t telestory_state::porta_r()
{
	uint16_t data = m_io_p1->read();
	//logerror("%s: porta_r: %04x\n", machine().describe_context(), data);
	return data;
}

void telestory_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	/*
	not used as an output very often

	':maincpu' (0145D5): porta_w (0000)
	':maincpu' (0145DC): porta_w (0000)
	':maincpu' (0145F4): porta_w (0000)
	':maincpu' (0145F4): porta_w (6000)
	*/
	logerror("%s: porta_w (%04x)\n", machine().describe_context(), data);
}

uint16_t telestory_state::portb_r()
{
	// not used?
	uint16_t data = m_io_p2->read();
	logerror("%s: portb_r %04x\n", machine().describe_context(), data);
	return data;
}

void telestory_state::portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	// not used?
	logerror("%s: portb_w (%04x)\n", machine().describe_context(), data);
}

// What is here? accessed using serial protocol with clocked bits
uint16_t telestory_state::portc_r()
{
	//logerror("%s: portc_r\n", machine().describe_context());
	return machine().rand() & 0x0c00; // bits need to change state or it doesn't boot
}

void telestory_state::portc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	//logerror("%s: portc_w (%04x)\n", machine().describe_context(), data);
}



void telestory_state::machine_start()
{
	spg2xx_game_state::machine_start();

	// if there's a cart, override the standard banking
	if (m_cart && m_cart->exists())
	{
		std::string region_tag;
		m_cart_region = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());
		m_bank->configure_entries(0, (m_cart_region->bytes() + 0x7fffff) / 0x800000, m_cart_region->base(), 0x800000);
		m_bank->set_entry(0);
	}
}

void telestory_state::machine_reset()
{
	spg2xx_game_state::machine_reset();
}

DEVICE_IMAGE_LOAD_MEMBER(telestory_state::cart_load_telestory)

{
	uint32_t const size = m_cart->common_get_size("rom");

	if (size > 0x80'0000)
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported cartridge size (must be no more than 8M)");

	m_cart->rom_alloc(0x800000, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

void telestory_state::telestory(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &telestory_state::mem_map_4m_tsram);

	spg2xx_base(config);

	m_maincpu->porta_in().set(FUNC(telestory_state::porta_r));
	m_maincpu->porta_out().set(FUNC(telestory_state::porta_w));
	m_maincpu->portb_in().set(FUNC(telestory_state::portb_r));
	m_maincpu->portb_out().set(FUNC(telestory_state::portb_w));
	m_maincpu->portc_in().set(FUNC(telestory_state::portc_r));
	m_maincpu->portc_out().set(FUNC(telestory_state::portc_w));

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "telestory_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(telestory_state::cart_load_telestory));
	m_cart->set_must_be_loaded(true);

	SOFTWARE_LIST(config, "telestory_cart").set_original("telestory_cart");
}

ROM_START( telestry )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	// no internal ROM, only RAM
ROM_END

} // anonymous namespace


/* Sound data for the narrator is written to the SIO data port
   013127: D319 3D54 [3d54] = r1   (Cinderella)  - 4-bit data?
   The hidden test mode calls the speech test 'CELP' (possibly "Code-excited linear prediction" based?)
   https://en.wikipedia.org/wiki/Code-excited_linear_prediction

   TODO: check if it's a common implementation eg.  Speex
   SIO port is not currently implemented in spg2xx_io.cpp however
*/
CONS( 2006, telestry,  0,        0, telestory, telestory,   telestory_state, empty_init, "JAKKS Pacific Inc / Toymax", "Telestory",     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )



spg2xx_tvgogo.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

#include "emu.h"
#include "spg2xx.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "softlist_dev.h"


namespace {

class tvgogo_state : public spg2xx_game_state
{
public:
	tvgogo_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_state(mconfig, type, tag),
		m_cart(*this, "cartslot"),
		m_cart_region(nullptr)
	{ }

	void tvgogo(machine_config &config);

private:
	uint8_t m_i2cunk = 0;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void tvg_i2c_w(offs_t offset, uint8_t data);
	uint8_t tvg_i2c_r(offs_t offset);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load_tvgogo);

	required_device<generic_slot_device> m_cart;
	memory_region *m_cart_region;
};


static INPUT_PORTS_START( tvgogo )
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Back")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Select")
	PORT_DIPNAME( 0x0004, 0x0004, "P1" )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P2")
	PORT_DIPNAME( 0x0001, 0x0001, "P2" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P3")
	PORT_DIPNAME( 0x0001, 0x0001, "P3" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
INPUT_PORTS_END


void tvgogo_state::machine_start()
{
	spg2xx_game_state::machine_start();

	// if there's a cart, override the standard banking
	if (m_cart && m_cart->exists())
	{
		std::string region_tag;
		m_cart_region = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());
		m_bank->configure_entries(0, (m_cart_region->bytes() + 0x7fffff) / 0x800000, m_cart_region->base(), 0x800000);
		m_bank->set_entry(0);
	}
}

void tvgogo_state::machine_reset()
{
	spg2xx_game_state::machine_reset();
}


DEVICE_IMAGE_LOAD_MEMBER(tvgogo_state::cart_load_tvgogo)
{
	uint32_t const size = m_cart->common_get_size("rom");

	if (size > 0x80'0000)
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported cartridge size (must be no more than 8M)");

	m_cart->rom_alloc(0x800000, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}


void tvgogo_state::tvg_i2c_w(offs_t offset, uint8_t data)
{
	// unsure what is mapped here (Camera?) but it expects to be able to read back the same byte it writes before it boots.
	m_i2cunk = data;
	logerror("%s: tvg_i2c_w %04x %02x\n", machine().describe_context(), offset, data);
}

uint8_t tvgogo_state::tvg_i2c_r(offs_t offset)
{
	uint8_t ret = m_i2cunk;
	logerror("%s: tvg_i2c_r %04x %02x\n", machine().describe_context(), offset, ret);
	return ret;
}


void tvgogo_state::tvgogo(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &tvgogo_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_in().set_ioport("P1");
	m_maincpu->portb_in().set_ioport("P2");
	m_maincpu->portc_in().set_ioport("P3");

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "tvgogo_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(tvgogo_state::cart_load_tvgogo));
	m_cart->set_must_be_loaded(true);

	m_maincpu->i2c_w().set(FUNC(tvgogo_state::tvg_i2c_w));
	m_maincpu->i2c_r().set(FUNC(tvgogo_state::tvg_i2c_r));

	SOFTWARE_LIST(config, "tvgogo_cart").set_original("tvgogo");
}

ROM_START( tvgogo )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	// no internal ROM? (Camera might have an MCU tho)
ROM_END

} // anonymous namespace


// Toyquest games
CONS( 2005, tvgogo,  0,        0, tvgogo, tvgogo,   tvgogo_state, empty_init, "Toyquest", "GoGo TV Video Vision",     MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND ) // or TV Go Go - the game addons call it the 'Video Vision console'



spg2xx_vii.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

#include "emu.h"
#include "spg2xx.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "softlist_dev.h"


namespace {

class vii_state : public spg2xx_game_state
{
public:
	vii_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_state(mconfig, type, tag),
		m_cart(*this, "cartslot"),
		m_io_motionx(*this, "MOTIONX"),
		m_io_motiony(*this, "MOTIONY"),
		m_io_motionz(*this, "MOTIONZ"),
		m_cart_region(nullptr),
		m_ctrl_poll_timer(nullptr)
	{ }

	void vii(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void vii_portb_w(uint16_t data);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load_vii);

	TIMER_CALLBACK_MEMBER(poll_controls);

	required_device<generic_slot_device> m_cart;
	required_ioport m_io_motionx;
	required_ioport m_io_motiony;
	required_ioport m_io_motionz;
	memory_region *m_cart_region;

	emu_timer *m_ctrl_poll_timer;
	uint8_t m_controller_input[8];
};


void vii_state::vii_portb_w(uint16_t data)
{
	switch_bank(((data & 0x80) >> 7) | ((data & 0x20) >> 4));
}


static INPUT_PORTS_START( vii )
	PORT_START("P1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1) PORT_NAME("Joypad Up")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1) PORT_NAME("Joypad Down")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1) PORT_NAME("Joypad Left")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("Joypad Right")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(1) PORT_NAME("Button A")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON2 )        PORT_PLAYER(1) PORT_NAME("Button B")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON3 )        PORT_PLAYER(1) PORT_NAME("Button C")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON4 )        PORT_PLAYER(1) PORT_NAME("Button D")

	PORT_START("MOTIONX")
	PORT_BIT( 0x3ff, 0x200, IPT_PADDLE ) PORT_MINMAX(0x000, 0x3ff) PORT_SENSITIVITY(50) PORT_KEYDELTA(50) PORT_NAME("Motion Control X")

	PORT_START("MOTIONY")
	PORT_BIT( 0x3ff, 0x200, IPT_PADDLE ) PORT_MINMAX(0x000, 0x3ff) PORT_SENSITIVITY(50) PORT_KEYDELTA(50) PORT_NAME("Motion Control Y") PORT_PLAYER(2)

	PORT_START("MOTIONZ")
	PORT_BIT( 0x3ff, 0x200, IPT_PADDLE ) PORT_MINMAX(0x000, 0x3ff) PORT_SENSITIVITY(50) PORT_KEYDELTA(50) PORT_NAME("Motion Control Z") PORT_PLAYER(3)
INPUT_PORTS_END

void vii_state::machine_start()
{
	spg2xx_game_state::machine_start();

	// if there's a cart, override the standard banking
	if (m_cart && m_cart->exists())
	{
		std::string region_tag;
		m_cart_region = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());
		m_bank->configure_entries(0, (m_cart_region->bytes() + 0x7fffff) / 0x800000, m_cart_region->base(), 0x800000);
		m_bank->set_entry(0);
	}

	m_ctrl_poll_timer = timer_alloc(FUNC(vii_state::poll_controls), this);
	m_ctrl_poll_timer->adjust(attotime::never);

	save_item(NAME(m_controller_input));
}

void vii_state::machine_reset()
{
	spg2xx_game_state::machine_reset();

	m_controller_input[0] = 0;
	m_controller_input[4] = 0;
	m_controller_input[6] = 0xff;
	m_controller_input[7] = 0;

	m_ctrl_poll_timer->adjust(attotime::from_hz(60), 0, attotime::from_hz(60));
}


TIMER_CALLBACK_MEMBER(vii_state::poll_controls)
{
	int32_t x = m_io_motionx ? ((int32_t)m_io_motionx->read() - 0x200) : 0;
	int32_t y = m_io_motiony ? ((int32_t)m_io_motiony->read() - 0x200) : 0;
	int32_t z = m_io_motionz ? ((int32_t)m_io_motionz->read() - 0x200) : 0;

	uint8_t old_input[8];
	memcpy(old_input, m_controller_input, 8);

	m_controller_input[0] = m_io_p1->read();
	m_controller_input[1] = (uint8_t)x;
	m_controller_input[2] = (uint8_t)y;
	m_controller_input[3] = (uint8_t)z;
	m_controller_input[4] = 0;
	x = (x >> 8) & 3;
	y = (y >> 8) & 3;
	z = (z >> 8) & 3;
	m_controller_input[5] = (z << 4) | (y << 2) | x;
	m_controller_input[6] = 0xff;
	m_controller_input[7] = 0;

	if (memcmp(old_input, m_controller_input, 8))
	{
		for(int i = 0; i < 8; i++)
			m_maincpu->uart_rx(m_controller_input[i]);
	}
}

DEVICE_IMAGE_LOAD_MEMBER(vii_state::cart_load_vii)
{
	uint32_t const size = m_cart->common_get_size("rom");

	if (size < 0x80'0000)
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported cartridge size (must be at least 8M)");

	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

void vii_state::vii(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &vii_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->portb_out().set(FUNC(vii_state::vii_portb_w));
	m_maincpu->i2c_w().set(FUNC(vii_state::i2c_w));
	m_maincpu->i2c_r().set(FUNC(vii_state::i2c_r));

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "vii_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(vii_state::cart_load_vii));

	SOFTWARE_LIST(config, "vii_cart").set_original("vii");
}

ROM_START( vii )
	ROM_REGION( 0x2000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "vii.bin", 0x0000, 0x2000000, CRC(04627639) SHA1(f883a92d31b53c9a5b0cdb112d07cd793c95fc43))
ROM_END

} // anonymous namespace


// Jungle Soft TV games
CONS( 2007, vii,      0, 0, vii,        vii,      vii_state,         empty_init,      "Jungle Soft / KenSingTon / Siatronics",       "Vii",         MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // motion controls are awkward, but playable for the most part



spg2xx_wiwi.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

/* These all have some kind of startup check
   the Virtual Interactive 'Vi' probably also fits here

   The WiWi could also run NES games; SunPlus hardware was in the cartridge, it was a 'fake' system.

    ---

    some of these are just checking a external timer on a port, I think this is one of the SPG features
    so might need making more generic.
*/



#include "emu.h"
#include "spg2xx.h"


namespace {

class spg2xx_game_wiwi18_state : public spg2xx_game_state
{
public:
	spg2xx_game_wiwi18_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_state(mconfig, type, tag)
	{ }

	void init_wiwi18();

protected:

private:
	void portb_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
};

class spg2xx_game_marc101_state : public spg2xx_game_state
{
public:
	spg2xx_game_marc101_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_state(mconfig, type, tag),
		m_prev_porta(0),
		m_prev_portb(0),
		m_toggle(false)
	{ }

	void marc101(machine_config &config);

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

	virtual uint16_t porta_r();
	virtual void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
	virtual void portb_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;

	TIMER_CALLBACK_MEMBER(toggle_tick);

	uint16_t m_prev_porta;
	uint16_t m_prev_portb;
	bool m_toggle;
	emu_timer *m_pulse_timer = nullptr;
};

class spg2xx_game_guitrbus_state : public spg2xx_game_marc101_state
{
public:
	spg2xx_game_guitrbus_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_marc101_state(mconfig, type, tag)
	{ }

	void guitrbus(machine_config &config);

protected:

	virtual uint16_t portb_r();
	virtual void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
	virtual void portb_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
};

class spg2xx_game_ddmsup_state : public spg2xx_game_marc101_state
{
public:
	spg2xx_game_ddmsup_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_marc101_state(mconfig, type, tag)
	{ }

	void ddmsup(machine_config &config);

protected:

//  virtual uint16_t portb_r();
	virtual void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
//  virtual void portb_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
};


class spg2xx_game_marc250_state : public spg2xx_game_marc101_state
{
public:
	spg2xx_game_marc250_state(const machine_config &mconfig, device_type type, const char *tag) :
		spg2xx_game_marc101_state(mconfig, type, tag),
		m_toggle2(false)
	{ }

	void init_m527();

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

	virtual uint16_t porta_r() override;
	virtual void porta_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
	virtual void portb_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;

	TIMER_CALLBACK_MEMBER(toggle2_tick);

private:
	bool m_toggle2;
	emu_timer *m_pulse_timer2 = nullptr;
};

TIMER_CALLBACK_MEMBER(spg2xx_game_marc101_state::toggle_tick)
{
	m_toggle = !m_toggle;
}

TIMER_CALLBACK_MEMBER(spg2xx_game_marc250_state::toggle2_tick)
{
	m_toggle2 = !m_toggle2;
}


void spg2xx_game_marc101_state::machine_start()
{
	spg2xx_game_state::machine_start();
	m_pulse_timer = timer_alloc(FUNC(spg2xx_game_marc101_state::toggle_tick), this);
	m_pulse_timer->adjust(attotime::never);

}

void spg2xx_game_marc101_state::machine_reset()
{
	spg2xx_game_state::machine_reset();
	m_pulse_timer->adjust(attotime::never);
	m_prev_porta = 0;
	m_prev_portb = 0;
	m_toggle = false;
}

void spg2xx_game_marc250_state::machine_start()
{
	spg2xx_game_marc101_state::machine_start();
	m_pulse_timer2 = timer_alloc(FUNC(spg2xx_game_marc250_state::toggle2_tick), this);
	m_pulse_timer2->adjust(attotime::never);

	// hack, makes x-racer3 and some others more stable, TODO: find out what is really wrong
	m_maincpu->set_clock_scale(2.0);

}

void spg2xx_game_marc250_state::machine_reset()
{
	spg2xx_game_marc101_state::machine_reset();
	switch_bank(31);
	m_maincpu->reset();

	m_pulse_timer2->adjust(attotime::never);
	m_toggle2 = false;
}


void spg2xx_game_marc101_state::marc101(machine_config &config)
{
	spg2xx(config);

	m_maincpu->set_pal(true);
	m_screen->set_refresh_hz(50);

	m_maincpu->porta_in().set(FUNC(spg2xx_game_marc101_state::porta_r));
	m_maincpu->porta_out().set(FUNC(spg2xx_game_marc101_state::porta_w));
}

void spg2xx_game_guitrbus_state::guitrbus(machine_config &config)
{
	spg2xx(config);

	m_maincpu->portb_in().set(FUNC(spg2xx_game_guitrbus_state::portb_r));
	m_maincpu->porta_out().set(FUNC(spg2xx_game_guitrbus_state::porta_w));
	m_maincpu->portb_out().set(FUNC(spg2xx_game_guitrbus_state::portb_w));
}

void spg2xx_game_ddmsup_state::ddmsup(machine_config &config)
{
	spg2xx(config);

//  m_maincpu->portb_in().set(FUNC(spg2xx_game_ddmsup_state::portb_r));
	m_maincpu->porta_in().set(FUNC(spg2xx_game_ddmsup_state::porta_r));
	m_maincpu->porta_out().set(FUNC(spg2xx_game_ddmsup_state::porta_w));
//  m_maincpu->portb_out().set(FUNC(spg2xx_game_ddmsup_state::portb_w));
}



// are these Port A behaviors related to IO A Special mode on the SoC?
// the bits being tested do seem to be 'ExtClk2 / ExtClk1'

uint16_t spg2xx_game_marc101_state::porta_r()
{
	uint16_t ret = m_io_p1->read() &~ 0x2000;

	ret |= m_toggle ? 0x2000 : 0x0000;

	return ret;
}

uint16_t spg2xx_game_guitrbus_state::portb_r()
{
	uint16_t ret = m_io_p2->read() &~ 0x0008;

	ret |= m_toggle ? 0x0000 : 0x0008;

	return ret;
}

void spg2xx_game_guitrbus_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: porta_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
		(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
		(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
		(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
		(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
		(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
		(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
		(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
		(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
		(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
		(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
		(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
		(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
		(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
		(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
		(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
		(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');

	m_prev_porta = data;
}

void spg2xx_game_guitrbus_state::portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: portb_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
		(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
		(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
		(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
		(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
		(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
		(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
		(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
		(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
		(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
		(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
		(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
		(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
		(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
		(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
		(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
		(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');

	if ((data & 0x0002) != (m_prev_portb & 0x0002))
	{
		if ((data & 0x0002))
		{
			logerror("ext timer reset\n");
			m_pulse_timer->adjust(attotime::from_hz(32), 0, attotime::from_hz(32));
		}
	}

	m_prev_portb = data;
}




void spg2xx_game_marc101_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: porta_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
		(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
		(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
		(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
		(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
		(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
		(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
		(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
		(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
		(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
		(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
		(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
		(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
		(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
		(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
		(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
		(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');

	// see function at 053E19 (marc101)
	// it has several states
	// 00 - wait a while
	// 01 - set 0x0400 in port a high
	// 02 - clear 0x0400 in port a
	// 07 - measure number of times 0x2000 on port a changes, with min/max acceptable values and a timeout
	// ff - failure (causes blank screen / shutdown + inf loop)
	if ((data & 0x0400) != (m_prev_porta & 0x0400))
	{
		if (!(data & 0x0400))
		{
			//logerror("pulse / timer reset\n");
			m_pulse_timer->adjust(attotime::from_hz(32), 0, attotime::from_hz(32));
		}
	}

	m_prev_porta = data;
}

uint16_t spg2xx_game_marc250_state::porta_r()
{
	uint16_t ret = m_io_p1->read() &~ 0x6000;
	ret |= m_toggle ? 0x2000 : 0x0000;
	ret |= m_toggle2 ? 0x4000 : 0x0000;
//  printf("porta %04x\n", ret);

	return ret;
}


void spg2xx_game_marc250_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: porta_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
		(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
		(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
		(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
		(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
		(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
		(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
		(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
		(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
		(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
		(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
		(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
		(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
		(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
		(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
		(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
		(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');

	// see function at 8AB2 (marc250 menu)
	// it has several states
	// 00 - wait a while
	// 01 - clear 0x1000 in port a
	// 02 - wait a while
	// 05 - check rate of port a bit 0x4000

	// 06 - wait a while
	// 07 - clear 0x1000 in port a
	// 08 - wait a while
	// 09 - check rate of port a bit 0x2000

	// ff - failure (causes blank screen / shutdown + inf loop)

	// function is at 04D918 in x-racer 3 (values tweaked to pass)
	// but x-racer 3 also seems to need faster CPU or it randomly corrupts (or its an IRQ problem?)

	if ((data & 0x1000) != (m_prev_porta & 0x1000))
	{
		if (!(data & 0x1000))
		{
			// these values stop the marc250 menu from failing, but not the games
			//printf("pulse / timer reset\n");
			m_pulse_timer->adjust(attotime::from_hz(6), 0, attotime::from_hz(6));
			m_pulse_timer2->adjust(attotime::from_hz(12), 0, attotime::from_hz(12));
		}
	}

	m_prev_porta = data;
}

void spg2xx_game_ddmsup_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: porta_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
		(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
		(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
		(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
		(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
		(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
		(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
		(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
		(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
		(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
		(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
		(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
		(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
		(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
		(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
		(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
		(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');

	if (data & 0x4000)
	{
		m_pulse_timer->adjust(attotime::from_hz(6), 0, attotime::from_hz(6));
	}

	m_prev_porta = data;
}


static INPUT_PORTS_START( wiwi18 )
	PORT_START("P1")
	PORT_DIPNAME( 0x0001, 0x0001, "IN0" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, "Possible Input" )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P2")
	PORT_DIPNAME( 0x0001, 0x0001, "IN1" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

static INPUT_PORTS_START( lexifit )
	PORT_START("P1")
	PORT_DIPNAME( 0x0001, 0x0001, "IN0" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P2")
	PORT_DIPNAME( 0x0001, 0x0001, "IN1" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0100, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0200, 0x0200, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0200, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0400, 0x0400, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0400, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0800, 0x0800, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0800, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x1000, 0x1000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x1000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x2000, 0x2000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x2000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x4000, 0x4000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x4000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x8000, 0x8000, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x8000, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_CUSTOM )
INPUT_PORTS_END

static INPUT_PORTS_START( m489 )
	PORT_START("P1")
	PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0x0003, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0xff00, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_UNUSED ) // acts as Button 1 autofire (not connected on unit?)
	PORT_BIT( 0xfffc, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( guitrbus )
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Red / Do")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Yellow / Re")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Purple / Mi")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Green / Fa")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Menu")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Start")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Blue / Sol")

	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_UNKNOWN ) // Logo (either WinFun or no logo)

	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_BUTTON8 ) PORT_NAME("Whammy Bar")
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_NAME("Strum")

	PORT_START("P2")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END




static INPUT_PORTS_START( ddmsup )
	PORT_START("P1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_NAME("P1 Pad Up") PORT_16WAY // NOT A JOYSTICK!!
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_NAME("P1 Pad Left") PORT_16WAY
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_NAME("P1 Pad Right") PORT_16WAY
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_NAME("P1 Pad Down") PORT_16WAY
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Back")
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Select")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P1 Pad Up-Left")
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("P1 Pad Up-Right")
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("P1 Pad Down-Left")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("P1 Pad Down-Right")
	PORT_BIT( 0x1c00, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_UNUSED ) // must toggle at rate measured by code for ddmsup
	PORT_BIT( 0xc000, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_NAME("P2 Pad Up") PORT_PLAYER(2) PORT_16WAY // NOT A JOYSTICK!!
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_NAME("P2 Pad Left") PORT_PLAYER(2) PORT_16WAY
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_NAME("P2 Pad Right") PORT_PLAYER(2) PORT_16WAY
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_NAME("P2 Pad Down") PORT_PLAYER(2) PORT_16WAY
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P2 Pad Up-Left") PORT_PLAYER(2)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("P2 Pad Up-Right") PORT_PLAYER(2)
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("P2 Pad Down-Left") PORT_PLAYER(2)
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("P2 Pad Down-Right") PORT_PLAYER(2)
	PORT_BIT( 0xff00, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P3")
	PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


void spg2xx_game_wiwi18_state::init_wiwi18()
{
	// workaround for security checks on startup

	uint16_t* rom = (uint16_t*)memregion("maincpu")->base();
	if (rom[0x1ca259]==0x4e04) rom[0x1ca259] = 0xf165; // wiwi18
	if (rom[0x1355a4]==0x4e04) rom[0x1355a4] = 0xf165; // foxsport

	if (rom[0x362e1c]==0x4e04) rom[0x362e1c] = 0xf165; // lexifit
	if (rom[0x4c7f4d]==0x4e04) rom[0x4c7f4d] = 0xf165; // lexifit (2nd bank)

}

void spg2xx_game_wiwi18_state::portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	logerror("%s: portb_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
		(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
		(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
		(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
		(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
		(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
		(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
		(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
		(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
		(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
		(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
		(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
		(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
		(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
		(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
		(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
		(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');

	if (m_maincpu->pc() < 0x2000)
	{
		if ((data & 0x0003) == 0x0000)
			switch_bank(1);
		else
			switch_bank(0);
	}
}



void spg2xx_game_marc101_state::portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (m_maincpu->pc() < 0x2000)
	{
		logerror("%s: portb_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
			(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
			(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
			(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
			(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
			(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
			(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
			(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
			(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
			(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
			(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
			(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
			(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
			(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
			(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
			(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
			(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');
	}


	if (m_maincpu->pc() < 0x2000)
	{
		// bit 0x1000 isn't set as an output, but clearly needs to be treated as one
		switch (data & 0x1003)
		{
		case 0x0000: switch_bank(7); break;
		case 0x0001: switch_bank(6); break; // good - heroic pilot
		case 0x0002: switch_bank(5); break; // good - horrific collapser
		case 0x0003: switch_bank(4); break; // good - under sea war
		case 0x1000: switch_bank(3); break; // good - roadman etc.
		case 0x1001: switch_bank(2); break; // good - santa etc
		case 0x1002: switch_bank(1); break;
		case 0x1003: switch_bank(0); break;
		}
	}
}

void spg2xx_game_marc250_state::init_m527()
{

	// the code being patched turns the IRQ off, code looks a bit like smarttv code..  is SoC IRQ handling wrong?
	// or is this more security, or something else? games don't run with the IRQ off...
#if 1
	uint16_t* rom = (uint16_t*)memregion("maincpu")->base();

	uint16_t ident2[6] = { 0x9512, 0x2862, 0xa70a, 0x0002, 0xd71b, 0x2862 };

	for (int i = 0; i < (0x10000000 / 2) - 6; i++)
	{
		bool found = true;
		for (int j = 0; j < 6; j++)
		{
			uint16_t cmp1 = ident2[j];
			uint16_t cmp2 = rom[i + j];

			if (cmp1 != cmp2)
				found = false;
		}

		if (found)
		{
			printf("found at %08x\n", i + 3);
			rom[i + 3] = 0x0003;
		}
	}


	// pass maze road
	//rom[((12 * 0x800000) / 2) | 0x0284b5] = 0x0003;
	// learn numbers
	//rom[((13 * 0x800000) / 2) | 0x00c055] = 0x0003;
	// bowling
	//rom[((17 * 0x800000) / 2) | 0x015e58] = 0x0003;
	// cliff overhang / gym dancing
	//rom[((18 * 0x800000) / 2) | 0x01cab4] = 0x0003;
	rom[((18 * 0x800000) / 2) | 0x021e25] = 0xffff; // secondary 'turn off' on gym dancing
	// jump chess
	//rom[((19 * 0x800000) / 2) | 0x012c3a] = 0x0003;
	// boxing, basketball etc.
	//rom[((23 * 0x800000) / 2) | 0x00c1bd] = 0x0003;
	// dash motor
	//rom[((24 * 0x800000) / 2) | 0x00d62c] = 0x0003;
	// same for curling
	//rom[((25 * 0x800000) / 2) | 0x0103c5] = 0x0003;
	// balloon puyo
	//rom[((26 * 0x800000) / 2) | 0x0084dd] = 0x0003;
	// 4 score
	//rom[((27 * 0x800000) / 2) | 0x145cdd] = 0x0003;
#endif
}

void spg2xx_game_marc250_state::portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (m_maincpu->pc() < 0x2000)
	{
		logerror("%s: portb_w %04x (%04x) %c %c %c %c | %c %c %c %c | %c %c %c %c | %c %c %c %c  \n", machine().describe_context(), data, mem_mask,
			(mem_mask & 0x8000) ? ((data & 0x8000) ? '1' : '0') : 'x',
			(mem_mask & 0x4000) ? ((data & 0x4000) ? '1' : '0') : 'x',
			(mem_mask & 0x2000) ? ((data & 0x2000) ? '1' : '0') : 'x',
			(mem_mask & 0x1000) ? ((data & 0x1000) ? '1' : '0') : 'x',
			(mem_mask & 0x0800) ? ((data & 0x0800) ? '1' : '0') : 'x',
			(mem_mask & 0x0400) ? ((data & 0x0400) ? '1' : '0') : 'x',
			(mem_mask & 0x0200) ? ((data & 0x0200) ? '1' : '0') : 'x',
			(mem_mask & 0x0100) ? ((data & 0x0100) ? '1' : '0') : 'x',
			(mem_mask & 0x0080) ? ((data & 0x0080) ? '1' : '0') : 'x',
			(mem_mask & 0x0040) ? ((data & 0x0040) ? '1' : '0') : 'x',
			(mem_mask & 0x0020) ? ((data & 0x0020) ? '1' : '0') : 'x',
			(mem_mask & 0x0010) ? ((data & 0x0010) ? '1' : '0') : 'x',
			(mem_mask & 0x0008) ? ((data & 0x0008) ? '1' : '0') : 'x',
			(mem_mask & 0x0004) ? ((data & 0x0004) ? '1' : '0') : 'x',
			(mem_mask & 0x0002) ? ((data & 0x0002) ? '1' : '0') : 'x',
			(mem_mask & 0x0001) ? ((data & 0x0001) ? '1' : '0') : 'x');
	}

	// bank 0  = (unused) 'ROM 18 64M' (dupe)
	// bank 1  = (unused) 'ROM 18 64M' (dupe)
	// bank 2  = (unused) 'ROM 18 64M' (dupe)
	// bank 3  = (unused) 'ROM 18 64M' (dupe)
	// bank 4  = (unused) 'ROM 18 64M' (dupe)
	// bank 5  = (unused) 'ROM 18 64M' (dupe)
	// bank 6  = (used) boots 'kung fu high style' (no bank number check)
	// bank 7  = (unused) 'ROM 18 64M' (dupe)

	// bank 8  = (used) 'ROM 24 64M'
	// bank 9  = (used) 'ROM 23 64M'
	// bank 10 = (unused) 'ROM 18 64M' (dupe)
	// bank 11 = (unused) 'ROM 18 64M' (dupe)
	// bank 12 = (used) (doesn't boot)
	// bank 13 = (used) (doesn't boot)
	// bank 14 = (used) 'ROM 18 64M'
	// bank 15 = (unused) 'ROM 18 64M' (dupe)

	// bank 16 = (used) 'ROM 16 64M' (error)
	// bank 17 = (used) (plays music)
	// bank 18 = (used) (doesn't boot)
	// bank 19 = (used)
	// bank 20 = (used)
	// bank 21 = (used)
	// bank 22 = (used)
	// bank 23 = (used)

	// bank 24 = (used)
	// bank 25 = (used)
	// bank 26 = (used)
	// bank 27 = (used)
	// bank 28 = (used)
	// bank 29 = (used)
	// bank 30 = (used) 'ROM 2 64M'
	// bank 31 = (used) menu (no bank number check)

	// bits 0x1804 aren't set as an output, but clearly need to be treated as output, also bad values of 0x00ff and 0x0000 are written on startup, with direction bits set!
	// this seems similar to issues with banking in some other units.
	if ((m_maincpu->pc() < 0x2000) && (data != 0x00ff) && (data != 0x0000))
	{
		switch (data & 0x1807)
		{
		case 0x0000: switch_bank(0); break; // unused
		case 0x0001: switch_bank(1); break; // unused
		case 0x0002: switch_bank(2); break; // unused
		case 0x0003: switch_bank(3); break; // unused

		case 0x1000: switch_bank(4); break; // unused
		case 0x1001: switch_bank(5); break; // unused
		case 0x1002: switch_bank(6); break; // kung fu (ok)
		case 0x1003: switch_bank(7); break; // unused

		case 0x0800: switch_bank(8); break; // jogging (ok)
		case 0x0801: switch_bank(9); break; // power kick (ok)
		case 0x0802: switch_bank(10); break; // unused
		case 0x0803: switch_bank(11); break; // unused

		case 0x1800: switch_bank(12); break; // pass maze road
		case 0x1801: switch_bank(13); break; // learn number
		case 0x1802: switch_bank(14); break; // final race, motor race
		case 0x1803: switch_bank(15); break; // unused

		case 0x0004: switch_bank(16); break; // thunder race, speed race, night shooter
		case 0x0005: switch_bank(17); break; // rescue bees
		case 0x0006: switch_bank(18); break; // cliff overhang
		case 0x0007: switch_bank(19); break; // jump chess

		case 0x1004: switch_bank(20); break; // mind link, mystery cave
		case 0x1005: switch_bank(21); break; // bumper cars, stone age, dance party (need hack)
		case 0x1006: switch_bank(22); break; // xracer (need hack)
		case 0x1007: switch_bank(23); break; // basketball (but irq gets turned off?)

		case 0x0804: switch_bank(24); break; // button jumper  dash motor
		case 0x0805: switch_bank(25); break; // cross river
		case 0x0806: switch_bank(26); break; // balloon puyu
		case 0x0807: switch_bank(27); break; // 4 Score

		case 0x1804: switch_bank(28); break; // bubble, malibu
		case 0x1805: switch_bank(29); break; // goalkeeper
		case 0x1806: switch_bank(30); break; // fish journey, brick, happy zoo, crazy tramcar
		case 0x1807: switch_bank(31); break; // menu

		}
	}
}

ROM_START( foxsport )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "foxsports.bin", 0x000000, 0x1000000, CRC(a5092f49) SHA1(feb4d432486b17d6cd2aed8cefd3d084f77c1733) ) // only a tiny amount of data in the 2nd half, what's it used for (if anything)
ROM_END

ROM_START( wiwi18 )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "26gl128.bin", 0x000000, 0x1000000, CRC(0b103ac9) SHA1(14434908f429942096fb8db5b5630603fd54fb2c) )
ROM_END

ROM_START( lexifit )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "lexibook_tv_fitness_center.bin", 0x000000, 0x1000000, CRC(38021230) SHA1(2b949d723a475bfac23d9da4d1a30ea71b332ccb) )
ROM_END

ROM_START( marc101 )
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "m489.u6", 0x0000000, 0x4000000, CRC(0a01695f) SHA1(1a13c5eb9dffdc91fc68a98e8f35bd8a019a8373) )
	ROM_IGNORE(0x4000000) // 2nd half is just random fill unrelated to the game, ROM was twice needed capacity
ROM_END

ROM_START( marc250 )
	ROM_REGION( 0x10000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "m527.u6", 0x0000000, 0x10000000, CRC(4b856cab) SHA1(41c66bbdb0bb1442d7e11da18e9e6b20048445ba) )
ROM_END

ROM_START( 265games )
	ROM_REGION( 0x10000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "265games.bin", 0x0000000, 0x10000000, CRC(761c92cb) SHA1(3e32bc6079610861bedafe8703302ed9620c9120) )
ROM_END


ROM_START( guitrbus )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "winfunguitar.bin", 0x000000, 0x400000, CRC(17419a27) SHA1(19377fcd18b08d3ae8e20de0244b3aaef1b5a66a) )
ROM_END

ROM_START( ddmsup )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "dancemaniasupreme.bin", 0x000000, 0x800000, CRC(4066a36b) SHA1(1fc8553b03b81bc02c3fcbcdafa2e54d1f2c2306) )
ROM_END

ROM_START( ddmmeg12 )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "doubledancemania.bin", 0x000000, 0x800000, CRC(fcffb21e) SHA1(802a3c2d6bcbd729b9a483d42d912ca8b9abdccd) )
ROM_END

} // anonymous namespace


// box marked 'Wireless game console' 'Drahtlose Spielekonsole' 87 Sports games included : 18 hyper sports games, 69 arcade games.
// Unit marked 'Hamy System' 'WiWi'
// actually a cartridge, but all hardware is in the cart, overriding any internal hardware entirely.  see nes_vt.cp 'mc_sp69' for the '69 arcade game' part
CONS( 200?, wiwi18,   0,        0, rad_skat, wiwi18,  spg2xx_game_wiwi18_state, init_wiwi18, "Hamy System",           "WiWi 18-in-1 Sports Game", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2009, lexifit,  0,        0, rad_skat, lexifit, spg2xx_game_wiwi18_state, init_wiwi18, "Lexibook",              "TV Fitness Center (Lexibook)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 200?, foxsport, 0,        0, rad_skat, wiwi18,  spg2xx_game_wiwi18_state, init_wiwi18, "Excalibur Electronics", "Fox Sports 7 in 1 Sports Games Plug n' Play", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// thtere is another 'Drahtlose Spielekonsole 48-in-1' with '11 hyper sports games' (including Running) which are clearly SunPlus and would fit here, with the 37 non-hyper sports games presumably again being a NES/Famiclone cart

CONS( 2014, marc101,     0,        0, marc101, m489,  spg2xx_game_marc101_state, empty_init, "Millennium 2000 GmbH", "Millennium Arcade 101 (M489) (Game Station 2 101-in-1)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 200?, ddmsup,      0,        0, ddmsup, ddmsup,  spg2xx_game_ddmsup_state, empty_init,    "Senario", "Double Dance Mania: Supreme / Dance Supreme", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // titlescreen is just 'Dance Supreme'

CONS( 2007, ddmmeg12,    0,        0, ddmsup, ddmsup,  spg2xx_game_ddmsup_state,  empty_init,    "Senario",  "Double Dance Mania: Mega 12",  MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 2015, marc250,     0,        0, marc101, m489,  spg2xx_game_marc250_state, init_m527, "Millennium 2000 GmbH", "Millennium Arcade 250 (M527)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

CONS( 201?, 265games,    0,        0, marc101, m489,  spg2xx_game_marc250_state, init_m527, "<unknown>", "265-in-1 Handheld Game (SPG2xx based)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )

// has the following strings at the start of the ROM
// "Copyright(C) 2009-2012 ShenZhen Multi-Content Software CO., LTD"
// "LisencedTo: MCS"
CONS( 2012, guitrbus,   0,        0, guitrbus,  guitrbus,  spg2xx_game_guitrbus_state,          empty_init,    "WinFun",  "Guitar Buster", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )



spg2xx_zone.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz, David Haywood

// JungleTac developed systems sold by under the 'Zone' and 'Wireless' brands these are mostly higer capacity (60+ game) units
// also Waixing developed systems for the lower capacity (~40 game) units

#include "emu.h"
#include "spg2xx.h"


namespace {

class wireless60_state : public spg2xx_game_state
{
public:
	wireless60_state(const machine_config& mconfig, device_type type, const char* tag) :
		spg2xx_game_state(mconfig, type, tag),
		m_bankmask(0x7)
	{ }

	void wireless60(machine_config& config);

	void init_lx_jg7415();
	void init_zone100();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint8_t m_w60_controller_input = 0;
	uint16_t m_w60_porta_data = 0;
	uint16_t m_w60_p1_ctrl_mask = 0;
	uint16_t m_w60_p2_ctrl_mask = 0;
	uint8_t m_bankmask;

	void wireless60_porta_w(uint16_t data);
	void wireless60_portb_w(uint16_t data);
	uint16_t wireless60_porta_r();

private:
};

class zone40_state : public wireless60_state
{
public:
	zone40_state(const machine_config& mconfig, device_type type, const char* tag) :
		wireless60_state(mconfig, type, tag),
		m_romregion(*this, "maincpu")
	{ }

	void zone40(machine_config &config);
	void zone40p(machine_config &config);

	void init_zone40();
	void init_reactmd();


protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	virtual void mem_map_z40(address_map &map) ATTR_COLD;
	uint16_t z40_rom_r(offs_t offset);
	uint16_t zone40_porta_r();
	void zone40_porta_w(uint16_t data);
	required_region_ptr<uint16_t> m_romregion;
	uint16_t m_z40_rombase = 0;
	uint16_t m_porta_dat = 0;
	int m_romsize = 0;
};


void wireless60_state::wireless60_porta_w(uint16_t data)
{
	//logerror("%s: wireless60_porta_w %04x\n", machine().describe_context(), data);

	m_w60_porta_data = (data & 0x300) | m_w60_p1_ctrl_mask | m_w60_p2_ctrl_mask;
	switch (m_w60_porta_data & 0x300)
	{
	case 0x300:
		m_w60_controller_input = -1;
		break;

	case 0x200:
		m_w60_controller_input++;
		break;

	default:
		uint16_t temp1 = m_io_p1->read();
		uint16_t temp2 = m_io_p2->read();
		uint16_t temp3 = 1 << m_w60_controller_input;
		if (temp1 & temp3) m_w60_porta_data ^= m_w60_p1_ctrl_mask;
		if (temp2 & temp3) m_w60_porta_data ^= m_w60_p2_ctrl_mask;
		break;
	}
}

uint16_t wireless60_state::wireless60_porta_r()
{
	//logerror("%s: wireless60_porta_r\n", machine().describe_context());
	return m_w60_porta_data;
}

void wireless60_state::wireless60_portb_w(uint16_t data)
{
	logerror("%s: wireless60_portb_w (bankswitch) %04x\n", machine().describe_context(), data);
	switch_bank(data & m_bankmask);
}

void zone40_state::zone40_porta_w(uint16_t data)
{
	wireless60_porta_w(data);

	m_z40_rombase = (m_z40_rombase & 0xff00) | (data & 0x0ff);

	// toggled twice before games in the same 64MB bank, toggled once before games in a different 64MB bank
	if ((data & 0x2000) && (!(m_porta_dat & 0x2000)))
		m_z40_rombase ^= 0x0100;

	m_porta_dat = data;

	//logerror("%s: zone40_porta_w %04x z80 bank is now %04x \n", machine().describe_context(), data, m_z40_rombase);

	m_maincpu->invalidate_cache();

}

uint16_t zone40_state::zone40_porta_r()
{
	uint16_t ret = wireless60_porta_r() & (0x0300 | m_w60_p1_ctrl_mask | m_w60_p2_ctrl_mask);
	ret = (ret & 0xdf00) | (m_porta_dat & 0x20ff);
	return ret;
}


uint16_t zone40_state::z40_rom_r(offs_t offset)
{
	// due to granularity of rom bank this manual method is safer
	return m_romregion[(offset + (m_z40_rombase * 0x20000)) & (m_romsize-1)];
}

void zone40_state::mem_map_z40(address_map &map)
{
	map(0x000000, 0x3fffff).r(FUNC(zone40_state::z40_rom_r));
}

void wireless60_state::machine_start()
{
	spg2xx_game_state::machine_start();

	save_item(NAME(m_w60_controller_input));
	save_item(NAME(m_w60_porta_data));

	m_w60_p1_ctrl_mask = 0x0400;
	m_w60_p2_ctrl_mask = 0x0800;
}

void zone40_state::machine_start()
{
	wireless60_state::machine_start();

	save_item(NAME(m_z40_rombase));
	save_item(NAME(m_porta_dat));

	m_z40_rombase = 0xe0;
	m_porta_dat = 0x20e0;
	m_w60_p1_ctrl_mask = 0x0400;
	m_w60_p2_ctrl_mask = 0x1000;

	m_romsize = (memregion("maincpu")->bytes()/2);
}

void wireless60_state::machine_reset()
{
	spg2xx_game_state::machine_reset();
	m_w60_controller_input = -1;
	m_w60_porta_data = 0;
}

void zone40_state::machine_reset()
{
	wireless60_state::machine_reset();
	m_z40_rombase = 0x1e0;
	m_maincpu->invalidate_cache();
	m_maincpu->reset();
}

static INPUT_PORTS_START( wirels60 )
	PORT_START("P1")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(1) PORT_NAME("Joypad Up")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1) PORT_NAME("Joypad Down")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1) PORT_NAME("Joypad Left")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("Joypad Right")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(1) PORT_NAME("A Button")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 )        PORT_PLAYER(1) PORT_NAME("B Button")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 )        PORT_PLAYER(1) PORT_NAME("Menu")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 )        PORT_PLAYER(1) PORT_NAME("Start")

	PORT_START("P2")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )    PORT_PLAYER(2) PORT_NAME("Joypad Up")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )  PORT_PLAYER(2) PORT_NAME("Joypad Down")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )  PORT_PLAYER(2) PORT_NAME("Joypad Left")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_NAME("Joypad Right")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 )        PORT_PLAYER(2) PORT_NAME("A Button")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 )        PORT_PLAYER(2) PORT_NAME("B Button")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 )        PORT_PLAYER(2) PORT_NAME("Menu")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 )        PORT_PLAYER(2) PORT_NAME("Start")
INPUT_PORTS_END


void wireless60_state::wireless60(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &wireless60_state::mem_map_4m);

	spg2xx_base(config);

	m_maincpu->porta_out().set(FUNC(wireless60_state::wireless60_porta_w));
	m_maincpu->portb_out().set(FUNC(wireless60_state::wireless60_portb_w));
	m_maincpu->porta_in().set(FUNC(wireless60_state::wireless60_porta_r));
}

void zone40_state::zone40(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &zone40_state::mem_map_z40);

	spg2xx_base(config);

	m_maincpu->porta_out().set(FUNC(zone40_state::zone40_porta_w));
	m_maincpu->porta_in().set(FUNC(zone40_state::zone40_porta_r));
}

void zone40_state::zone40p(machine_config &config)
{
	zone40(config);
	m_maincpu->set_pal(true);
	m_screen->set_refresh_hz(50);
}


void wireless60_state::init_lx_jg7415()
{
	uint8_t blocks[32] = {
		// these parts of the ROM contain the code that gets selected
		0x00, 0x01, 0x06, 0x07, 0x08, 0x09, 0x0e, 0x0f,   0x10, 0x11, 0x16, 0x17, 0x18, 0x19, 0x1e, 0x1f,
		// these parts of the ROM contain code / data but go unused, this seems intentional, some of these areas don't read consistently so likely this double size ROM was used knowing that some areas were bad and could be avoided
		0x02, 0x03, 0x04, 0x05, 0x0a, 0x0b, 0x0c, 0x0d,   0x12, 0x13, 0x14, 0x15, 0x1a, 0x1b, 0x1c, 0x1d
	};

	uint8_t *src = memregion("maincpu")->base();
	std::vector<u8> buffer(0x10000000);

	for (int addr = 0; addr < 0x10000000; addr++)
	{
		int bank = (addr & 0xf800000) >> 23;
		int newbank = blocks[bank];
		int newaddr = (addr & 0x07fffff) | (newbank << 23);
		buffer[addr] = src[newaddr];
	}

	std::copy(buffer.begin(), buffer.end(), &src[0]);

	m_bankmask = 0xf;
}

void wireless60_state::init_zone100()
{
	m_bankmask = 0xf;
}

void zone40_state::init_zone40()
{
	uint16_t *ROM = (uint16_t*)memregion("maincpu")->base();
	int size = memregion("maincpu")->bytes();

	for (int i = 0; i < size/2; i++)
	{
		ROM[i] = bitswap<16>(ROM[i], 11, 10, 3,  2,  4,  12, 5,  13,
									 9,  1,  8,  0,  6,  7,  14, 15);

		ROM[i] = ROM[i] ^ 0xa5a5;
	}
}


void zone40_state::init_reactmd()
{
	uint16_t *ROM = (uint16_t*)memregion("maincpu")->base();
	int size = memregion("maincpu")->bytes();

	for (int i = 0; i < size/2; i++)
	{
		ROM[i] = bitswap<16>(ROM[i], 15, 13, 14, 12,  7,  6,  5,  4,
									 11, 10, 9,  8,   3,  1,  2,  0);

		ROM[i] = ROM[i] ^ 0xa5a5;
	}
}



ROM_START( zone40 )
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "zone40.bin", 0x0000, 0x4000000, CRC(4ba1444f) SHA1(de83046ab93421486668a247972ad6d3cda19440) )
ROM_END



ROM_START( itvg49 )
	ROM_REGION( 0x8000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "49in1sports.bin", 0x0000, 0x8000000, CRC(bb8a1c4e) SHA1(a493177de7365037b67ead0155a902313722a61c) )
ROM_END


ROM_START( zone60 )
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "zone60.bin", 0x0000, 0x4000000, CRC(4cb637d1) SHA1(1f97cbdb4299ac0fbafc2a3aa592066cb0727066))
ROM_END

ROM_START( wirels60 )
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "wirels60.bin", 0x0000, 0x4000000, CRC(b4df8b28) SHA1(00e3da542e4bc14baf4724ad436f66d4c0f65c84))
ROM_END

ROM_START( zone100 )
	ROM_REGION( 0x8000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "zone100.bin", 0x0000, 0x8000000, CRC(b966a54e) SHA1(e38156ebc4e2f2935b1acbeca33d1866d45c4f65) )
ROM_END

// PCB marked 'Zone 100 110728 V2.1'
ROM_START( lx_jg7415 )
	ROM_REGION( 0x10000000, "maincpu", ROMREGION_ERASE00 )
	// reads of some unused areas were not 100% consistent, but this seems intentional, the game has a ROM twice the size it needs and is wired up in such a way that those bad areas are unused by the game
	// if adding a clone make sure to check if there are actual differences in the used areas
	ROM_LOAD16_WORD_SWAP( "rom.bin", 0x0000, 0x10000000, CRC(59442e00) SHA1(7e91cf6b19c37f9b4fa4dc21e241c6634d6a6f95) )
ROM_END

ROM_START( zonemini )
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "zonemini.bin", 0x0000, 0x4000000, CRC(ba8c367c) SHA1(92ce2e895145ad76ea68ab7575d2c52aa0c0c5a9) )
ROM_END

ROM_START( react )
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "reactor.bin", 0x0000, 0x0800000, CRC(0378c594) SHA1(b2214e3e235f26fb501df6c66a9b2c0da87b1c73))
	ROM_CONTINUE(0x1000000, 0x0800000)
	ROM_CONTINUE(0x0800000, 0x0800000)
	ROM_CONTINUE(0x1800000, 0x0800000)
	ROM_CONTINUE(0x2000000, 0x0800000)
	ROM_CONTINUE(0x3000000, 0x0800000)
	ROM_CONTINUE(0x2800000, 0x0800000)
	ROM_CONTINUE(0x3800000, 0x0800000)
ROM_END

ROM_START( lx_jg7410 )
	ROM_REGION( 0x8000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD16_WORD_SWAP( "55lv100.u4", 0x0000, 0x8000000, CRC(60476576) SHA1(83592e43d9169c95f6b22903e8d708e96ad02611) )
ROM_END

} // anonymous namespace


// These have games from Waixing and were likely manufactured  by Subor and sold by Ultimate Products Ltd.
// Many of these games are rewrites of VT1682 based titles, which in turn were based on older NES/VT ones
// Badminton hangs in units where it is present (cause not yet investigated), otherwise everything runs

// Waixing = "Fuzhou Waixing Computer Science & Technology Co.,LTD"

CONS( 2009, zone40,   0, 0, zone40,     wirels60, zone40_state,      init_zone40,     "Ultimate Products Ltd. / Waixing",                      "Zone 40",                             MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2009, itvg49,   0, 0, zone40p,    wirels60, zone40_state,      init_reactmd,    "TaiKee / Waixing",                                      "Interactive TV Games 49-in-1 (PAL)",  MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 200?, zonemini, 0, 0, zone40,     wirels60, zone40_state,      init_reactmd,    "Ultimate Products Ltd. / Waixing",                      "Zone Mini",                           MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2009, react,    0, 0, zone40,     wirels60, zone40_state,      init_reactmd,    "Ultimate Products Ltd. / Waixing",                      "Reactor 32-in-1 (NTSC)",              MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )


// These have a newer selection of games by JungleTac instead of the Waixing ones

CONS( 2010, zone60,   0, 0, wireless60, wirels60, wireless60_state,  empty_init,      "Ultimate Products (HK) Ltd / Jungle's Soft",  "Zone 60",                             MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 200?, zone100,  0, 0, wireless60, wirels60, wireless60_state,  init_zone100,    "Ultimate Products (HK) Ltd / Jungle's Soft",  "Zone 100",                            MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // unit was black, menus still show white controllers, unlike wireless 60
CONS( 2010, wirels60, 0, 0, wireless60, wirels60, wireless60_state,  empty_init,      "Kids Station Toys Inc / Jungle Soft",         "Wireless 60",                         MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2011, lx_jg7415,0, 0, wireless60, wirels60, wireless60_state,  init_lx_jg7415,  "Lexibook / JungleTac",                        "Lexibook JG7415 120-in-1",            MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2012, lx_jg7410,0, 0, wireless60, wirels60, wireless60_state,  init_zone100,    "Lexibook / JungleTac",                        "Lexibook JG7410 100-in-1",            MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS ) // (c)2012 and JG7410_01 on box, JG7410 in other places





sphinx40.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

CXG "Adversary" Sphinx 40 / 50

This is a modular chesscomputer, similar to Mephisto's 3-drawers one.
Chesscomputer on the right, LCD in the middle, and future expansion on the left.
The only difference between 40 and 50 is the board size (40cm vs 50cm).

The chess engine is Cyrus 68K, by Mark Taylor, with advice from David Levy.
It's not related to the Z80 version of Cyrus, only by name.

This chessboard was also used on the Sphinx 40 / 50 Plus, which is another
incarnation of Frans Morsch's Dominator program.

TODO:
- unmapped read from 0x200000, looks like expansion ROM
- verify XTAL and irq source/frequency

Hardware notes:

Distribution board:
- PCB label: C 1987 CXG SYSTEMS S.A 68K 600 203
- Hitachi HD46821P (6821 PIA)
- piezo, connector to chessboard (magnet sensors, 8*8 leds)

Program/CPU module:
- PCB label: (C) 1987 NEWCREST TECHNOLOGY LTD, CXG-68K-600-001
- daughterboard for buttons, label CXG SYSTEMS, 68K-600-101
- Signetics SCN68000C8N64 @ 8MHz
- 64KB ROM (2*GI 27256-20)
- 128KB RAM (4*KM41464AP-12 64kx4 DRAM)
- 2KB battery-backed RAM (NEC D449C)

LCD module
- PCB label: CXG-68K-600-302
- Hitachi HD61603 LCD Driver
- 2 displays (4 digits each)

*******************************************************************************/

#include "emu.h"

#include "cpu/m68000/m68000.h"
#include "machine/6821pia.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"
#include "video/hd61603.h"

#include "speaker.h"

// internal artwork
#include "cxg_sphinx40.lh"


namespace {

class sphinx40_state : public driver_device
{
public:
	sphinx40_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_nvram(*this, "nvram", 0x800, ENDIANNESS_BIG),
		m_pia(*this, "pia"),
		m_lcd(*this, "lcd"),
		m_display(*this, "display"),
		m_board(*this, "board"),
		m_inputs(*this, "IN.%u", 0),
		m_out_digit(*this, "digit%u", 0U),
		m_out_lcd(*this, "lcd%u", 0U)
	{ }

	// machine configs
	void sphinx40(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	memory_share_creator<u8> m_nvram;
	required_device<pia6821_device> m_pia;
	required_device<hd61603_device> m_lcd;
	required_device<pwm_display_device> m_display;
	required_device<sensorboard_device> m_board;
	required_ioport_array<4> m_inputs;
	output_finder<8> m_out_digit;
	output_finder<64> m_out_lcd;

	u8 m_cb_mux = 0;
	u8 m_led_data = 0;
	u8 m_inp_mux = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void lcd_seg_w(u64 data);

	void update_display();
	void cb_mux_w(u8 data);
	void cb_leds_w(u8 data);
	u8 cb_r();

	u8 input_r();
	void input_w(u8 data);

	u8 nvram_r(offs_t offset) { return m_nvram[offset]; }
	void nvram_w(offs_t offset, u8 data) { m_nvram[offset] = data; }
};

void sphinx40_state::machine_start()
{
	m_out_digit.resolve();
	m_out_lcd.resolve();

	// register for savestates
	save_item(NAME(m_cb_mux));
	save_item(NAME(m_led_data));
	save_item(NAME(m_inp_mux));
}



/*******************************************************************************
    I/O
*******************************************************************************/

// HD61603 LCD

void sphinx40_state::lcd_seg_w(u64 data)
{
	// output individual segments
	for (int i = 0; i < 64; i++)
		m_out_lcd[i] = BIT(data, i);

	// output digits
	for (int i = 0; i < 8; i++)
	{
		m_out_digit[i] = data & 0x7f;
		data >>= 8;
	}
}


// 6821 PIA

void sphinx40_state::update_display()
{
	m_display->matrix(m_cb_mux, m_led_data);
}

void sphinx40_state::cb_mux_w(u8 data)
{
	// PA0-PA7: chessboard input/led mux
	m_cb_mux = ~data;
	update_display();
}

void sphinx40_state::cb_leds_w(u8 data)
{
	// PB0-PB7: chessboard leds
	m_led_data = ~data;
	update_display();
}


// TTL

u8 sphinx40_state::cb_r()
{
	u8 data = 0;

	// d0-d7: multiplexed inputs (chessboard)
	for (int i = 0; i < 8; i++)
		if (BIT(m_cb_mux, i))
			data |= m_board->read_rank(i, true);

	return data;
}

void sphinx40_state::input_w(u8 data)
{
	// d0-d3: input mux (buttons)
	m_inp_mux = ~data & 0xf;
}

u8 sphinx40_state::input_r()
{
	u8 data = 0;

	// d0-d4: multiplexed inputs (buttons)
	for (int i = 0; i < 4; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read();

	return ~data & 0x1f;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void sphinx40_state::main_map(address_map &map)
{
	map(0x000000, 0x00ffff).rom();
	map(0x100000, 0x11ffff).ram();
	map(0x400000, 0x400fff).rw(FUNC(sphinx40_state::nvram_r), FUNC(sphinx40_state::nvram_w)).umask16(0x00ff);
	map(0x70fcf1, 0x70fcf1).r(FUNC(sphinx40_state::cb_r));
	map(0x70fd71, 0x70fd71).r(FUNC(sphinx40_state::input_r));
	map(0x70fdb1, 0x70fdb1).w(FUNC(sphinx40_state::input_w));
	map(0x70fde0, 0x70fde0).w(m_lcd, FUNC(hd61603_device::data_w));
	map(0x70fff0, 0x70fff7).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write)).umask16(0xff00);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( sphinx40 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Clock")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Move")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Function")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Hint")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4) PORT_NAME("Rook")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("Black")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_EQUALS) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("Forwards")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("What If?")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3) PORT_NAME("Bishop")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2) PORT_NAME("Knight")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Sound")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("Backwards")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Analysis")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1) PORT_NAME("Pawn")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6) PORT_NAME("King")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("White")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Set-Up")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5) PORT_NAME("Queen")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void sphinx40_state::sphinx40(machine_config &config)
{
	// basic machine hardware
	M68000(config, m_maincpu, 8'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &sphinx40_state::main_map);

	const attotime irq_period = attotime::from_hz(8'000'000 / 0x1000);
	m_maincpu->set_periodic_int(FUNC(sphinx40_state::irq4_line_hold), irq_period);

	PIA6821(config, m_pia);
	m_pia->writepa_handler().set(FUNC(sphinx40_state::cb_mux_w));
	m_pia->writepb_handler().set(FUNC(sphinx40_state::cb_leds_w));
	m_pia->cb2_handler().set("dac", FUNC(dac_1bit_device::write));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::MAGNETS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	HD61603(config, m_lcd, 0);
	m_lcd->write_segs().set(FUNC(sphinx40_state::lcd_seg_w));

	PWM_DISPLAY(config, m_display).set_size(8, 8);
	config.set_default_layout(layout_cxg_sphinx40);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, "dac").add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( sphinx40 )
	ROM_REGION16_BE( 0x10000, "maincpu", 0 )
	ROM_LOAD16_BYTE("gold.u3",   0x0000, 0x8000, CRC(e7cccd12) SHA1(4542f62963ab78796626c0c938e39e715d1c19f8) )
	ROM_LOAD16_BYTE("orange.u2", 0x0001, 0x8000, CRC(9e0bbd15) SHA1(5867f35489d15c1e395f6b2aa91a76d74ad6f2f4) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1987, sphinx40, 0,      0,      sphinx40, sphinx40, sphinx40_state, empty_init, "CXG Systems / Newcrest Technology / Intelligent Chess Software", "Sphinx 40", MACHINE_SUPPORTS_SAVE )



sprachmg.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: natarii, Dirk Best
/***************************************************************************

    Gerät 32620 (aka Sprach/Morsegenerator, Stimme and more)

    Digital speech generator. Used by the Ministerium für Staatssicherheit
    to send coded messages to agents using shortwave "Number Stations".

    More info: https://www.cryptomuseum.com/spy/owvl/32620/index.htm

    Hardware:
    - UB880 (Z80)
    - D2764D
    - D446C x2
    - UB8560D
    - UB855D x2
    - 9.832 MHz XTAL
    - UB855D x2 (on expansion board)
    - MBM2764-20 x12 (can be less, on speech module)

    TODO:
    - Tape input
    - DAC gain
    - Verify daisy chain
    - Remote

    Notes:
    - Emulated is "version 2", it's possible that there are many hardware
      variations (the available documention is for a slighly different
      version for example)
    - Serial protocol is undocumented (settings: 1200 Baud, 8-O-1) and
      might not even be fully implemented in this version
    - It uses a 10-bit DAC, but only 8 bits are used

***************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/clock.h"
#include "machine/nvram.h"
#include "machine/z80daisy.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"
#include "sound/dac.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "sprachmg.lh"


namespace {


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class sprachmg_state : public driver_device
{
public:
	sprachmg_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_pio(*this, "pio%u", 0U),
		m_sio(*this, "sio"),
		m_speech_module(*this, "speech"),
		m_dac(*this, "dac"),
		m_keys(*this, "keypad%u", 0U),
		m_special(*this, "special"),
		m_remote(*this, "remote"),
		m_chargen(*this, "chargen"),
		m_dmd(*this, "dot%u%u", 0U, 0U),
		m_led_speech(*this, "led_speech"),
		m_led_morse(*this, "led_morse"),
		m_led_standard(*this, "led_standard"),
		m_display_data(0x00),
		m_key_scan(0x1f),
		m_speech_select(0xff),
		m_speech_module_pcb1(nullptr),
		m_speech_module_pcb2(nullptr)
		{ }

	DECLARE_INPUT_CHANGED_MEMBER(keypad_res);

	void sprachmg(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;
	required_device_array<z80pio_device, 4> m_pio;
	required_device<z80sio_device> m_sio;
	required_device<generic_slot_device> m_speech_module;
	required_device<dac_word_interface> m_dac;
	required_ioport_array<5> m_keys;
	required_ioport m_special;
	required_ioport m_remote;
	required_region_ptr<uint8_t> m_chargen;
	output_finder<8, 7> m_dmd;
	output_finder<> m_led_speech;
	output_finder<> m_led_morse;
	output_finder<> m_led_standard;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	void display_data_w(uint8_t data);
	void display_column_w(uint8_t data);

	uint8_t key_r();

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(module_load);
	uint8_t speech_r(offs_t offset);

	void sys_w(uint8_t data);

	uint8_t m_display_data;
	uint8_t m_key_scan;
	uint8_t m_speech_select;
	uint8_t *m_speech_module_pcb1;
	uint8_t *m_speech_module_pcb2;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void sprachmg_state::mem_map(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x2000, 0x2fff).ram().share("nvram");
	map(0x4000, 0xffff).r(FUNC(sprachmg_state::speech_r));
}

void sprachmg_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x7c, 0x7f).rw(m_pio[2], FUNC(z80pio_device::read), FUNC(z80pio_device::write)); // on expansion board
	map(0xbc, 0xbf).rw(m_pio[3], FUNC(z80pio_device::read), FUNC(z80pio_device::write)); // on expansion board
	map(0xc0, 0xc3).rw(m_sio, FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
	map(0xc8, 0xcb).rw(m_pio[0], FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0xd0, 0xd3).rw(m_pio[1], FUNC(z80pio_device::read), FUNC(z80pio_device::write));
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( sprachmg )
	PORT_START("keypad0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_E)     PORT_NAME("EX")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_LEFT)  PORT_NAME("<")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_RIGHT) PORT_NAME(">")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("keypad1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_SPACE)    PORT_NAME("SPC")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_SLASH)    PORT_NAME("/")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_COLON)    PORT_NAME(":")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_C)        PORT_NAME("CLR")
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("keypad2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_Q)     PORT_NAME("?")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3 -")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_M)     PORT_NAME("MODE")
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("keypad3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("=")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_2_PAD)  PORT_NAME("2 .")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_5_PAD)  PORT_NAME("5")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_8_PAD)  PORT_NAME("8")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_O)      PORT_NAME("OUT")
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("keypad4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0 NO")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1 YES")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_I)     PORT_NAME("INP")
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("special")
	PORT_BIT(0x1f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("STA/STP") PORT_WRITE_LINE_DEVICE_MEMBER("pio0", FUNC(z80pio_device::pb5_w))
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("remote")
	PORT_BIT(0x3f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("STA/STP (Remote)") PORT_WRITE_LINE_DEVICE_MEMBER("pio0", FUNC(z80pio_device::pb6_w))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("reset")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("RES") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sprachmg_state::keypad_res), 0)
INPUT_PORTS_END


//**************************************************************************
//  KEYPAD
//**************************************************************************

INPUT_CHANGED_MEMBER( sprachmg_state::keypad_res )
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? CLEAR_LINE : ASSERT_LINE);

	if (newval && !oldval)
		m_sio->reset();
}

uint8_t sprachmg_state::key_r()
{
	// 7-------  not used
	// -6------  start/stop on remote
	// --5-----  start/stop
	// ---43210  keypad selected row

	uint8_t data = 0xff;

	for (unsigned i = 0; i < 5; i++)
		if (BIT(m_key_scan, i) == 0)
			data &= m_keys[i]->read();

	data &= m_special->read();
	data &= m_remote->read();

	return data;
}


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

void sprachmg_state::display_data_w(uint8_t data)
{
	m_display_data = data;

	m_pio[1]->strobe_b(1);
	m_pio[1]->strobe_b(0);
}

void sprachmg_state::display_column_w(uint8_t data)
{
	for (unsigned i = 0; i < 7; i++)
		m_dmd[data & 0x07][i] = m_chargen[0x100 + (i << 8) + m_display_data];
}


//**************************************************************************
//  SPEECH MODULE
//**************************************************************************

DEVICE_IMAGE_LOAD_MEMBER( sprachmg_state::module_load )
{
	if (!image.loaded_through_softlist())
		return std::make_pair(image_error::UNSUPPORTED, "Speech modules can only be loaded using a software list");

	uint32_t const pcb1_size = image.get_software_region_length("pcb1");
	if (pcb1_size != 0xc000)
		return std::make_pair(image_error::BADSOFTWARE, "Invalid 'pcb1' data area length (must be 48K)");

	m_speech_module_pcb1 = image.get_software_region("pcb1");

	uint32_t const pcb2_size = image.get_software_region_length("pcb2");
	if (pcb2_size > 0 && pcb2_size != 0xc000)
		return std::make_pair(image_error::BADSOFTWARE, "Invalid 'pcb2' data area length (must be 48K)");

	m_speech_module_pcb2 = image.get_software_region("pcb2");

	return std::make_pair(std::error_condition(), std::string());
}

uint8_t sprachmg_state::speech_r(offs_t offset)
{
	if (BIT(m_speech_select, 1) == 0 && m_speech_module_pcb1)
		return m_speech_module_pcb1[offset];

	if (BIT(m_speech_select, 0) == 0 && m_speech_module_pcb2)
		return m_speech_module_pcb2[offset];

	return 0xff;
}


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void sprachmg_state::sys_w(uint8_t data)
{
	// 7-------  speech card 1 (active low)
	// -6------  speech card 2 (active low)
	// --5-----  led standard
	// ---4----  led speech
	// ----3---  led morse
	// -----210  data to remote (ende, ausgabe, morse)

	m_led_morse = BIT(data, 3);
	m_led_speech = BIT(data, 4);
	m_led_standard = BIT(data, 5);

	m_speech_select = BIT(data, 6, 2);
}

void sprachmg_state::machine_start()
{
	// resolve outputs
	m_dmd.resolve();
	m_led_speech.resolve();
	m_led_morse.resolve();
	m_led_standard.resolve();

	// register for save states
	save_item(NAME(m_display_data));
	save_item(NAME(m_key_scan));
	save_item(NAME(m_speech_select));
}

void sprachmg_state::machine_reset()
{
	m_key_scan = 0x1f;
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

static const z80_daisy_config z80_daisy_chain[] =
{
	{ "pio0" },
	{ "pio1" },
	{ "sio" },
	{ "pio2" },
	{ "pio3" },
	{ nullptr }
};

void sprachmg_state::sprachmg(machine_config &config)
{
	Z80(config, m_maincpu, 9.8304_MHz_XTAL / 4);
	m_maincpu->set_daisy_config(z80_daisy_chain);
	m_maincpu->set_addrmap(AS_PROGRAM, &sprachmg_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &sprachmg_state::io_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_NONE);

	Z80PIO(config, m_pio[0], 9.8304_MHz_XTAL / 4);
	m_pio[0]->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio[0]->out_pa_callback().set([this](uint8_t data) { m_key_scan = data & 0x1f; });
	m_pio[0]->in_pb_callback().set(FUNC(sprachmg_state::key_r));

	Z80PIO(config, m_pio[1], 9.8304_MHz_XTAL / 4);
	m_pio[1]->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio[1]->out_pa_callback().set(FUNC(sprachmg_state::display_data_w));
	m_pio[1]->out_pb_callback().set(FUNC(sprachmg_state::display_column_w));

	Z80PIO(config, m_pio[2], 0);
	m_pio[2]->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio[2]->out_pa_callback().set([this](uint8_t data) { m_dac->write(data); });
	m_pio[2]->out_pb_callback().set(FUNC(sprachmg_state::sys_w));

	Z80PIO(config, m_pio[3], 0);
	m_pio[3]->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	// port a: tape
	// port b: tape, dac gain

	Z80SIO(config, m_sio, 9.8304_MHz_XTAL / 4);
	m_sio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_sio->out_txda_callback().set("rs232", FUNC(rs232_port_device::write_txd));

	rs232_port_device &serial(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	serial.rxd_handler().set(m_sio, FUNC(z80sio_device::rxa_w));

	clock_device &serclk(CLOCK(config, "serclk", 9.8304_MHz_XTAL / 4 / 128)); // cd4020b
	serclk.signal_handler().set(m_sio, FUNC(z80sio_device::rxca_w));
	serclk.signal_handler().append(m_sio, FUNC(z80sio_device::txca_w));

	SPEAKER(config, "speaker").front_center();

	AD7520(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 1.0);

	GENERIC_CARTSLOT(config, m_speech_module, generic_plain_slot, "sprachmg");
	m_speech_module->set_device_load(FUNC(sprachmg_state::module_load));

	SOFTWARE_LIST(config, "module_list").set_original("sprachmg");

	config.set_default_layout(layout_sprachmg);
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( sprachmg )
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("bu01_8-87.bin", 0x0000, 0x2000, CRC(7d9a92a6) SHA1(c9ca4a0d118b2c30e2505de051671769ad08a1c5))

	ROM_REGION(0x800, "chargen", 0)
	ROM_LOAD("zg-625_1-12-86.bin", 0x000, 0x800, CRC(9ffd1e15) SHA1(759660404dfe479d13a1bdd4beb19e6035a34e17))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY                         FULLNAME                               FLAGS
COMP( 1985, sprachmg, 0,      0,      sprachmg, sprachmg, sprachmg_state, empty_init, "Institut für Kosmosforschung", "Gerät 32620 (Sprach/Morsegenerator)", MACHINE_SUPPORTS_SAVE )



sprinter.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Andrei I. Holub
/*******************************************************************************************

Sprinter Sp2000 (Peters Plus Ltd)

Hardware:
- CPU                   Z84C15 (21MHz/3.5MHz)
- RAM                   4Mb (64Mb)
- Fast RAM              64Kb
- ROM                   256Kb
- Video RAM             256Kb (512Kb)
- FDD controller        WD1793
- Support FDD:          3,5" disk (1.44Mb/720Kb) / 5,25" disk (720Kb)
- CMOS                  DALLAS
- HDD controller        IDE/AT
- Keyboard controler    101key/AT
- Mouse controller      MS-Mouse
- Slots                 ISA-8
- Audio out             AY-3-8910 (in PLD), Stereo 8 bit (16 bit)
- Video out             TV, CGA analog monitor, RGB
- Graphic mode          320x256x256, 640x256x16, Spectrum standard screen
- Text mode             80x32x16

Refs:
    https://web.archive.org/web/20030208004427/http://www.petersplus.com/sprinter/

Emulation NOTES:
Following manual configuration adjustments are recommended for better experience:
- CDROM CDDA Sound is only connected to ata1:1
- Input Settings > Keyboard Selection >
        Microsoft Natural Keyboard [root:kbd:ms_naturl]: Enabled
- Use '-rs232 microsoft_mouse'
- Input Settings > Input Assignments (this system) > Microsoft 2-Button Serial Mouse (HLE) [root:rs232:microsoft_mouse]
        Mouse X 3 Analog:                                Mouse X    (MOUSECODE_1_XAXIS)
        Mouse X 3 Analog Inc:                            Mouse X -  (MOUSECODE_1_XAXIS_NEG_SWITCH)
        Mouse X 3 Analog Dec:                            Mouse X +  (MOUSECODE_1_XAXIS_POS_SWITCH)
        Mouse Y 3 Analog:                                Mouse Y    (MOUSECODE_1_YAXIS)
        Mouse Y 3 Analog Inc:                            Mouse Y -  (MOUSECODE_1_YAXIS_NEG_SWITCH)
        Mouse Y 3 Analog Dec:                            Mouse Y +  (MOUSECODE_1_YAXIS_POS_SWITCH)

TODO:
- ISA memory slots
- fully untied from Spectrum parent
- better rendering (currently not fully discovered) in Game Configuration

*******************************************************************************************/

#include "emu.h"

#include "beta_m.h"
#include "spec128.h"

#include "bus/ata/atapicdr.h"
#include "bus/ata/ataintf.h"
#include "bus/ata/hdd.h"
#include "bus/isa/isa.h"
#include "bus/isa/isa_cards.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "bus/rs232/hlemouse.h"
#include "bus/rs232/rs232.h"
#include "bus/spectrum/zxbus/bus.h"
#include "cpu/z80/z84c015.h"
#include "machine/ds128x.h"
#include "machine/input_merger.h"
#include "sound/ay8910.h"
#include "sound/dac.h"

#include "speaker.h"
#include "tilemap.h"

#include <algorithm>
#include <iterator>

#include "sprinter.lh"


#define LOG_IO    (1U << 1)
#define LOG_MEM   (1U << 2)
#define LOG_ACCEL (1U << 3)
#define LOG_WARN  (1U << 4)

#define VERBOSE ( /*LOG_GENERAL | LOG_IO | LOG_MEM | LOG_ACCEL |*/ LOG_WARN )
#include "logmacro.h"

#define LOGIO(...)    LOGMASKED(LOG_IO,    __VA_ARGS__)
#define LOGMEM(...)   LOGMASKED(LOG_MEM,   __VA_ARGS__)
#define LOGACCEL(...) LOGMASKED(LOG_ACCEL, __VA_ARGS__)
#define LOGWARN(...)  LOGMASKED(LOG_WARN,  __VA_ARGS__)

namespace {

class sprinter_state : public spectrum_128_state
{
public:
	sprinter_state(const machine_config &mconfig, device_type type, const char *tag)
		: spectrum_128_state(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_isa(*this, "isa8%u", 0U)
		, m_irqs(*this, "irqs")
		, m_rtc(*this, "rtc")
		, m_ata(*this, "ata%u", 1U)
		, m_beta(*this, BETA_DISK_TAG)
		, m_ay8910(*this, "ay8912")
		, m_ldac(*this, "ldac")
		, m_rdac(*this, "rdac")
		, m_kbd(*this, "kbd")
		, m_io_line(*this, "IO_LINE%u", 0U)
		, m_io_mouse(*this, "mouse_input%u", 1U)
		, m_io_turbo(*this, "TURBO")
		, m_palette(*this, "palette")
		, m_gfxdecode(*this, "gfxdecode")
		, m_vram(*this, "vram", 0x40000, ENDIANNESS_LITTLE)
		, m_fastram(*this, "fastram", 0x10000, ENDIANNESS_LITTLE)
		, m_bank0_fastram(*this, "bank0_fastram")
		, m_bank_view0(*this, "bank_view0")
		, m_bank_view3(*this, "bank_view3")
		, m_turbo_led(*this, "turbo_led")
	{ }

	void sprinter(machine_config &config);

	INPUT_CHANGED_MEMBER(turbo_changed);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;
	virtual void device_post_load() override ATTR_COLD;

	void map_io(address_map &map) ATTR_COLD;
	void map_mem(address_map &map) ATTR_COLD;
	void map_fetch(address_map &map) ATTR_COLD;
	u8 m1_r(offs_t offset);
	void cio_dtrb_w(int state);
	u8 joy_ctrl_r(int num);
	void pio_b_w(int state);

	void init_taps();

	void update_memory();
	void update_cpu();
	void update_video(bool is312);

	virtual TIMER_CALLBACK_MEMBER(irq_on) override;
	virtual TIMER_CALLBACK_MEMBER(irq_off) override;
	TIMER_CALLBACK_MEMBER(cbl_tick);
	TIMER_CALLBACK_MEMBER(acc_tick);
	TIMER_CALLBACK_MEMBER(wait_off);

	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void screen_update_graph(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void screen_update_game(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void draw_tile(u8* mode, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void draw_symbol(u8* mode, bitmap_ind16 &bitmap, const rectangle &cliprect, bool flash);
	u8* as_mode(u8 a, u8 b);
	std::pair<u8, u8> lookback_scroll(u8 a, u8 b);

	required_device<z84c015_device> m_maincpu;
	required_device_array<isa8_device, 2> m_isa;

private:
	enum accel_state : u8
	{
		MODE_AND   = 0xa6, // and (hl)
		MODE_XOR   = 0xae, // xor (hl)
		MODE_OR    = 0xb6, //  or (hl)
		MODE_NOP   = 0xbe  //  cp (hl)
	};
	enum access_state : u8
	{
		ACCEL_OFF = 0,
		ACCEL_GO,
		ACCEL_ON
	};

	static constexpr XTAL X_SP                 = 42_MHz_XTAL; // TODO X1 after spectrumless

	static constexpr u16  SPRINT_WIDTH         = 896;
	static constexpr u16  SPRINT_BORDER_RIGHT  = 48;
	static constexpr u16  SPRINT_SCREEN_XSIZE  = 640;
	static constexpr u16  SPRINT_BORDER_LEFT   = 48;
	static constexpr u16  SPRINT_XVIS          = SPRINT_BORDER_RIGHT + SPRINT_SCREEN_XSIZE + SPRINT_BORDER_LEFT;

	static constexpr u16  SPRINT_HEIGHT        = 320;
	static constexpr u16  SPRINT_BORDER_TOP    = 16;
	static constexpr u16  SPRINT_SCREEN_YSIZE  = 256;
	static constexpr u16  SPRINT_BORDER_BOTTOM = 16;
	static constexpr u16  SPRINT_YVIS          = SPRINT_BORDER_TOP + SPRINT_SCREEN_YSIZE + SPRINT_BORDER_BOTTOM;

	static constexpr u16 BANK_RAM_MASK         = 1 << 8;
	static constexpr u16 BANK_FASTRAM_MASK     = 1 << 9;
	static constexpr u16 BANK_ISA_MASK         = 1 << 10;
	static constexpr u16 BANK_WRDISBL_MASK     = 1 << 12;

	bool acc_ena()     const { return BIT(m_all_mode, 0); }
	bool cbl_mode()    const { return BIT(m_cbl_xx, 7); }
	bool cbl_stereo()  const { return BIT(m_cbl_xx, 6); }
	bool cbl_mode16()  const { return BIT(m_cbl_xx, 5); }
	bool cbl_int_ena() const { return BIT(m_cbl_xx, 4); }
	u8 ram1()          const { return m_ram_pages[0xe9 - 0xc0]; }
	u8 ram2()          const { return m_ram_pages[0xea - 0xc0]; }


	u8 dcp_r(offs_t offset);
	void dcp_w(offs_t offset, u8 data);

	u8 bootstrap_r(offs_t offset);
	void bootstrap_w(offs_t offset, u8 data);
	template <u8 Bank> u8 ram_r(offs_t offset);
	template <u8 Bank> void ram_w(offs_t offset, u8 data);
	void vram_w(offs_t offset, u8 data);
	void update_int(bool recalculate);
	u8 isa_r(offs_t offset);
	void isa_w(offs_t offset, u8 data);
	void do_mem_wait(u8 cpu_taken);

	void check_accel(bool is_read, offs_t offset, u8 &data);
	void accel_control_r(u8 data);
	void do_accel_block(bool is_read);
	void accel_mem_r(offs_t offset);
	void accel_mem_w(offs_t offset, u8 data);
	u8 &accel_buffer(u8 idx);
	void update_accel_buffer(u8 idx, u8 data);

	u8 kbd_fe_r(offs_t offset);
	void on_kbd_data(int state);

	required_device<input_merger_device> m_irqs;
	required_device<ds12885_device> m_rtc;
	required_device_array<ata_interface_device, 2> m_ata;
	required_device<beta_disk_device> m_beta;
	required_device<ay8910_device> m_ay8910;
	required_device<dac_word_interface> m_ldac;
	required_device<dac_word_interface> m_rdac;
	required_device<pc_kbdc_device> m_kbd;
	required_ioport_array<8> m_io_line;
	required_ioport_array<3> m_io_mouse;
	required_ioport m_io_turbo;
	required_device<device_palette_interface> m_palette;
	required_device<gfxdecode_device> m_gfxdecode;
	tilemap_t *m_tilemap;
	memory_share_creator<u8> m_vram;
	memory_share_creator<u8> m_fastram;
	memory_bank_creator m_bank0_fastram;
	memory_view m_bank_view0;
	memory_view m_bank_view3;
	output_finder<> m_turbo_led;

	TILE_GET_INFO_MEMBER(get_tile_info);

	u8 *m_dcp_location;
	u8 m_ram_pages[0x40] = {}; // 0xc0 - 0xff
	u16 m_pages[4] = {}; // internal state for faster calculations

	bool    m_z80_m1;
	offs_t  m_z80_addr;
	u8      m_z80_data;
	bool    m_z80_wait;
	u8      m_wait_ticks_count;
	std::list<std::pair<u16, u16>> m_ints;
	u8      m_joy1_ctrl;
	u8      m_joy2_ctrl;

	u8 m_conf;
	bool m_conf_loading;
	bool m_starting;
	u16 m_bitstream_count;
	u32 m_bitstream_hash;
	bool m_dos; // 0-on, 1-off
	bool m_cash_on;

	u8 m_cnf;
	bool m_rom_sys;
	bool m_ram_sys;
	bool m_sys_pg;
	bool m_turbo;
	bool m_turbo_hard;
	bool m_arom16;
	u8 m_rom_rg;
	u8 m_pn;
	u8 m_sc;
	u8 m_all_mode;
	u8 m_port_y;
	u8 m_rgmod;
	u8 m_pg3;
	u8 m_isa_addr_ext;
	std::pair<s8, s8> m_hold;
	u8 m_kbd_data_cnt;
	bool m_in_out_cmd;

	bool m_ata_selected; // 0-primary, 1-secondary
	u8 m_ata_data_latch;

	// Accelerator
	u8 m_prf_d;
	u8 m_rgacc;
	u8 m_acc_cnt;
	u8 m_accel_buffer[256] = {};
	bool m_alt_acc;
	u16 m_aagr;
	u8 m_xcnt;
	u8 m_xagr;
	u8 m_acc_dir;
	accel_state m_fn_acc;
	access_state m_access_state;

	// Covox Blaster
	u8 m_cbl_xx;
	u16 m_cbl_data[256] = {};
	u8 m_cbl_cnt;
	u8 m_cbl_wa;
	bool m_cbl_wae;
	emu_timer *m_cbl_timer = nullptr;
	emu_timer *m_acc_timer = nullptr;
	emu_timer *m_wait_off_timer = nullptr;
};

void sprinter_state::update_memory()
{
	const bool pre_rom = m_rom_sys || m_cash_on;
	const bool pre_cash = !m_cash_on;
	if (!pre_rom && pre_cash)
	{
		m_pages[0] = (m_rom_rg & 0x0f) ^ (!m_sys_pg << 3);
		m_bank_rom[0]->set_entry(m_pages[0]);
		m_bank_view0.select(1);
	}
	else if (pre_rom && !pre_cash)
	{
		m_pages[0] = BANK_FASTRAM_MASK | (m_rom_rg & 3);
		m_bank0_fastram->set_entry(m_pages[0] & 0xff);
		m_bank_view0.select(2);
	}
	else
	{
		const bool cash_on = 0;
		const bool nmi_ena = 1;
		const bool sc0 = BIT(m_sc, 0);
		const bool sc_lc = !(sc0 && m_ram_sys) && !cash_on;
		const u8 spr_ = BIT(m_sc, 1) ? 0 : ((m_dos << 1) | (BIT(m_pn, 4) || !m_dos));
		const u8 pg0 = 0x20 // 0xe0
			| ((sc0 || !m_ram_sys || cash_on || !nmi_ena) << 3)
			| (((m_arom16 && !(sc0 && m_ram_sys)) || (cash_on && nmi_ena)) << 2)
			| (((BIT(spr_, 1) && sc_lc) || !m_ram_sys || !nmi_ena) << 1)
			| ((BIT(spr_, 0) && sc_lc) || !m_ram_sys || !nmi_ena);

		m_pages[0] = BANK_RAM_MASK | m_ram_pages[pg0];
		m_bank_ram[0]->set_entry(m_pages[0] & 0xff);
		if (sc0 && m_ram_sys)
			m_bank_view0.disable();
		else
		{
			m_pages[0] |= BANK_WRDISBL_MASK;
			m_bank_view0.select(0);
		}
	}

	m_pages[1] = BANK_RAM_MASK | ram1();
	m_bank_ram[1]->set_entry(ram1());
	m_pages[2] = BANK_RAM_MASK | ram2();
	m_bank_ram[2]->set_entry(ram2());

	// 0xd0, 0xf0
	m_pg3 = (BIT(~m_pn, 7) << 5) | 0x10 | (((BIT(m_sc, 4) && BIT(~m_cnf, 7)) || (BIT(m_cnf, 7) && BIT(m_pn, 6))) << 3) | (m_pn & 0x07);
	m_pages[3] = m_starting ? 0x40 : m_ram_pages[m_pg3];
	m_bank_ram[3]->set_entry(m_pages[3] & 0xff);
	if (BIT(m_sc, 4) && ((m_pages[3] & 0xf9) == 0xd0))
	{
		m_pages[3] |= BANK_ISA_MASK;
		m_bank_view3.select(0);
	}
	else
	{
		m_bank_ram[3]->set_entry(m_pages[3]);
		m_bank_view3.disable();
		m_pages[3] |= BANK_RAM_MASK;
	}

	LOGMEM("MEM: %x %x %x %x\n", m_pages[0], m_pages[1], m_pages[2], m_pages[3]);
}

void sprinter_state::update_cpu()
{
	m_turbo_led = m_turbo && m_turbo_hard;
	m_maincpu->set_clock_scale((m_turbo && m_turbo_hard) ? 6 : 1); // 1 - 21MHz, 0 - 3.5MHz
}

void sprinter_state::update_video(bool is312)
{
	const u16 vtotal = SPRINT_HEIGHT - (8 * is312);
	m_screen->configure(SPRINT_WIDTH, vtotal, m_screen->visible_area(), HZ_TO_ATTOSECONDS(X_SP / 3) * SPRINT_WIDTH * vtotal);
	update_int(true);
}

u32 sprinter_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	if (m_conf)
		screen_update_game(screen, bitmap, cliprect);
	else
		screen_update_graph(screen, bitmap, cliprect);

	return 0;
}

void sprinter_state::screen_update_graph(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	const bool flash = BIT(screen.frame_number(), 4);
	for (u16 vpos = cliprect.top(); vpos <= cliprect.bottom();)
	{
		const u16 scr_height = screen.height();
		const u16 b8 = (scr_height + vpos - SPRINT_BORDER_TOP - m_hold.second) % scr_height;
		for (u16 hpos = cliprect.left(); hpos <= cliprect.right();)
		{
			const u16 a16 = (SPRINT_WIDTH + hpos - SPRINT_BORDER_LEFT - m_hold.first) % SPRINT_WIDTH;
			u8* mode = as_mode(a16 >> 4, b8 >> 3);
			const rectangle tile {hpos, std::min(cliprect.right(), hpos + 15 - (a16 & 15)), vpos, std::min(cliprect.bottom(), vpos + 7 - (b8 & 7))};
			if(BIT(mode[0], 4))
				draw_symbol(mode, bitmap, tile, flash);
			else
				draw_tile(mode, bitmap, tile);

			hpos += tile.width();
		}
		vpos += 8 - (b8 & 7);
	}
}

void sprinter_state::draw_tile(u8* mode, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 *pix = &(bitmap.pix(cliprect.top(), cliprect.left()));
	const u16 pal = BIT(mode[0], 6, 2) << 8;
	u16 x = (BIT(mode[0], 0, 4) << 6) | (BIT(mode[1], 0, 3) << 3);
	u8 y = BIT(mode[1], 3, 5) << 3;
	const bool lowres = BIT(mode[2], 2);
	if (lowres)
	{
		x += 4 * BIT(mode[2], 0);
		y += 4 * BIT(mode[2], 1);
	}
	for (auto dy = cliprect.top(); dy <= cliprect.bottom(); dy++)
	{
		for (auto dx = cliprect.left(); dx <= cliprect.right(); dx++)
		{
			const u8 color = m_vram[(y + (((dy - m_hold.second) & 7) >> (lowres ? 1 : 0))) * 1024 + x + (((dx - m_hold.first) & 15) >> (1 + lowres))];
			*pix++ = pal + (BIT(mode[0], 5) ? color : (((dx - m_hold.first) & 1) ? (color & 0x0f) : (color >> 4)));
		}
		pix += SPRINT_WIDTH - cliprect.width();
	}
}

void sprinter_state::draw_symbol(u8* mode, bitmap_ind16 &bitmap, const rectangle &cliprect, bool flash)
{
	rectangle partrect = cliprect;
	do
	{
		const bool is_partial = cliprect.right() != partrect.right();
		if (is_partial)
		{
			partrect.setx(partrect.right() + 1, cliprect.right());
			mode += 1024;
		}
		else if (!BIT(mode[0], 5))
		{
			if (BIT(cliprect.left() - m_hold.first, 3))
				mode += 1024;
			else
				partrect.setx(partrect.left(), std::min(cliprect.right(), cliprect.left() + 7 - ((cliprect.left() - m_hold.first) & 7)));
		}

		u16 *pix = &(bitmap.pix(partrect.top(), partrect.left()));
		u8 attr = 0;
		u16 pal = 0x400;
		if (~mode[0] & 0xfc) // !blank
		{
			attr = m_vram[(mode[2] << 10) | (BIT(mode[0], 0, 4) << 6) | (BIT(m_pn, 3) << 5) | 0b11000 | BIT(mode[0], 6, 2)];
			if ((BIT(mode[0], 5, 3) == 7) && ((mode[0] & 0x0c) != 0x0c)) // border
				pal = 0x400 | ((m_port_fe_data & 0x07) << 3) | (m_port_fe_data & 0x07);
		}

		for (auto dy = partrect.top(); dy <= partrect.bottom(); dy++)
		{
			const u8 symb = (~mode[0] & 0xfc) ? m_vram[(mode[1] << 10) | (BIT(mode[0], 0, 4) << 6) | (BIT(m_pn, 3) << 5) | (BIT(mode[0], 6, 2) << 3) | ((dy - m_hold.second) & 7)] : 0;
			for (auto dx = partrect.left(); dx <= partrect.right(); dx++)
			{
				if (BIT(mode[0], 5, 3) != 7)
				{
					const u8 bit = 1 << (7 - (((dx - m_hold.first) >> BIT(mode[0], 5)) & 7));
					pal = attr + 0x400 + 0x100 * bool(symb & bit) + 0x200 * flash;
				}
				*pix++ = pal;
			}
			pix += SPRINT_WIDTH - partrect.width();
		}
	} while (cliprect.right() != partrect.right());
}

 // Game Config - used in Thunder in the Deep
void sprinter_state::screen_update_game(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (u16 vpos = cliprect.top(); vpos <= cliprect.bottom(); vpos++)
	{
		const u16 scr_height = screen.height();
		const u8 b = ((scr_height + vpos - SPRINT_BORDER_TOP - m_hold.second) % scr_height) >> 3;
		const u8 a = ((SPRINT_WIDTH + cliprect.left() - SPRINT_BORDER_LEFT - m_hold.first) % SPRINT_WIDTH) >> 4;
		std::pair<u8, u8> scroll = lookback_scroll(a, b);

		u8* mode = nullptr;
		u16 pal, x;
		u8 y;
		u16 *pix = &(bitmap.pix(vpos, cliprect.left()));

		for (u16 hpos = cliprect.left(); hpos <= cliprect.right(); hpos++)
		{
			u16 a16 = (SPRINT_WIDTH + hpos + (scroll.first << 1) - SPRINT_BORDER_LEFT - m_hold.first) % SPRINT_WIDTH;
			if ((mode != nullptr) && BIT(mode[0], 2) && (((a16 - (scroll.first << 1)) & 15) == 0))
			{
				scroll = {mode[3] & 0x0f, mode[3] >> 4};
				a16 = (SPRINT_WIDTH + hpos + (scroll.first << 1) - SPRINT_BORDER_LEFT - m_hold.first) % SPRINT_WIDTH;
			}
			const u16 b8 = (scr_height + vpos + scroll.second - SPRINT_BORDER_TOP - m_hold.second) % scr_height;

			if (mode == nullptr)
			{
				mode = as_mode(a16 >> 4, b8 >> 3);
				pal = BIT(mode[0], 6, 2) << 8;
				x = ((BIT(mode[0], 0, 2) << 8) | mode[1]);
				y = mode[2];
			}

			if ((BIT(mode[0], 5, 3) == 7))
				*pix++ = 0x400 | (((mode[0] & 0x0c) == 0x0c) ? 0 : ((m_port_fe_data & 0x07) << 3) | (m_port_fe_data & 0x07));
			else
				*pix++ = pal + m_vram[(y + (b8 & 7)) * 1024 + x + ((a16 & 15) >> 1)];

			if ((a16 & 15) == 15)
			{
				if (BIT(mode[0], 2))
					scroll = {mode[3] & 0x0f, mode[3] >> 4};
				mode = nullptr;
			}
		}
	}
}

u8* sprinter_state::as_mode(u8 a, u8 b)
{
	const u32 line1 = (1 + a * 2 + 0x80 * (m_rgmod & 1)) * 1024;
	const u16 la = 0x300 + b * 4;

	return m_vram + line1 + la;
}

std::pair<u8, u8> sprinter_state::lookback_scroll(u8 a, u8 b)
{
	for (auto v = b; b; b--, a = 55)
	{
		for (auto h = a; a; a--)
		{
			const u8* mode = as_mode(h, v);
			if (BIT(mode[0], 2) && (h != a) && (v != b))
				return { mode[3] & 0x0f, mode[3] >> 4 };
		}
	}

	return { 0, 0 };
}

u8 sprinter_state::dcp_r(offs_t offset)
{
	if (m_starting) m_starting = 0;

	if (!machine().side_effects_disabled())
	{
		if (((offset & 0x7f) == 0x7b))
		{
			m_cash_on = BIT(offset, 7);
			update_memory();
		}
		do_mem_wait(4);
	}

	const u16 dcp_offset = (BIT(m_cnf, 3, 2) << 12) | (BIT(m_pn, 5) << 11) | (m_dos << 10) | (1 << 9) | (BIT(offset, 14, 2) << 7) | (BIT(offset, 13) << 4) | (BIT(offset, 7) << 3) | (offset & 0x67);
	const u8 dcpp = m_dcp_location[dcp_offset];
	u8 data = 0xff;
	switch (dcpp)
	{
	case 0x00: // no port
		data = 0xff;
		break;

	case 0x10:
		data = m_beta->status_r();
		break;
	case 0x11:
		data = m_beta->track_r();
		break;
	case 0x12:
		data = m_beta->sector_r();
		break;
	case 0x13:
		data = m_beta->data_r();
		break;
	case 0x15:
		data = m_beta->state_r() & joy_ctrl_r(1);
		break;

	case 0x1c:
		data = m_rtc->data_r();
		break;

	case 0x20:
		if (!machine().side_effects_disabled() && BIT(~offset, 8))
		{
			const u16 tmp = m_ata[m_ata_selected]->cs0_r(0);
			m_ata_data_latch = tmp >> 8;
			data = tmp;
		}
		else
			data = m_ata_data_latch;
		break;
	case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
		if (BIT(~offset, 8))
			data = m_ata[m_ata_selected]->cs0_r(dcpp & 0x07);
		break;
	case 0x28:
		if (BIT(~offset, 8))
			data = m_ata[m_ata_selected]->cs1_r(6);
		break;
	case 0x29:
		if (BIT(~offset, 8))
			data = m_ata[m_ata_selected]->cs1_r(7);
		break;

	case 0x40:
		data = kbd_fe_r(offset);
		break;

	case 0x52: // AY8910
		data = m_ay8910->data_r();
		break;

	case 0x58: // Kempston Mouse
		switch (offset >> 8)
		{
		case 0xfa:
			data = m_io_mouse[2]->read();
			break;
		case 0xfb:
			data = m_io_mouse[0]->read();
			break;
		case 0xff:
			data = m_io_mouse[1]->read();
			break;
		default:
			data = 0xff;
		}
		break;

	case 0x89:
		data = m_cbl_xx;
		break;

	case 0xc0: case 0xc1: case 0xc2: case 0xc3: case 0xc4: case 0xc5: case 0xc6: case 0xc7:
	case 0xc8: case 0xc9: case 0xca: case 0xcb: case 0xcc: case 0xcd: case 0xce: case 0xcf:
	case 0xd0: case 0xd1: case 0xd2: case 0xd3: case 0xd4: case 0xd5: case 0xd6: case 0xd7:
	case 0xd8: case 0xd9: case 0xda: case 0xdb: case 0xdc: case 0xdd: case 0xde: case 0xdf:
	case 0xe0: case 0xe1: case 0xe2: case 0xe3: case 0xe4: case 0xe5: case 0xe6: case 0xe7:
	case 0xe8: case 0xe9: case 0xea: case 0xeb: case 0xec: case 0xed: case 0xee: case 0xef:
		data = m_ram_pages[dcpp - 0xc0];
		break;
	case 0xf0: case 0xf1: case 0xf2: case 0xf3: case 0xf4: case 0xf5: case 0xf6: case 0xf7:
	case 0xf8: case 0xf9: case 0xfa: case 0xfb: case 0xfc: case 0xfd: case 0xfe: case 0xff:
		data = m_ram_pages[m_pg3];
		break;

	default:
		LOGIO("IOr: %02X / %X > %x\n", dcpp, offset, data);
		break;
	}

	return data;
}

void sprinter_state::dcp_w(offs_t offset, u8 data)
{
	if (m_starting)
		return;

	if ((offset & 0xbf) == 0x3c)
	{
		m_rom_sys = BIT(~offset, 6);
		if (BIT(~data, 1))
			m_sys_pg = BIT(m_rom_rg, 4) || BIT(data, 0);
		update_memory();
	}
	if (!m_rom_sys && ((offset & 0xff) == 0x5c))
	{
		m_rom_rg = data;
		m_sys_pg |= BIT(m_rom_rg, 4);
		update_memory();
	}
	do_mem_wait(4);

	const u16 dcp_offset = (BIT(m_cnf, 3, 2) << 12) | (BIT(m_pn, 5) << 11) | (m_dos << 10) | (0 << 9) | (BIT(offset, 14, 2) << 7) | (BIT(offset, 13) << 4) | (BIT(offset, 7) << 3) | (offset & 0x67);
	const u8 dcpp = m_dcp_location[dcp_offset];
	if ((dcpp >= 0xc0) && (dcpp < 0xf0))
		m_ram_pages[dcpp - 0xc0] = data;
	switch (dcpp)
	{
	case 0x10:
		m_beta->command_w(data);
		break;
	case 0x11:
		m_beta->track_w(data);
		break;
	case 0x12:
		m_beta->sector_w(data);
		break;
	case 0x13:
		m_beta->data_w(data);
		break;
	case 0x14:
		m_beta->param_w(data);
		break;
	case 0x16:
	case 0x17:
		m_beta->turbo_w(dcpp & 1);
		if (data & 2)
			m_beta->disable();
		else
			m_beta->enable();
		break;

	case 0x1b:
		if (data & 0x80)
		{
			// RESET
		}
		if (data & 0x40)
		{
			// AEN
		}
		m_isa_addr_ext = data & 0x3f;
		break;

	case 0x1d:
		m_rtc->address_w(data);
		break;
	case 0x1e:
		m_rtc->data_w(data);
		break;

	case 0x20:
		if (BIT(offset, 8))
			m_ata[m_ata_selected]->cs0_w(0, (data << 8) | m_ata_data_latch);
		else
			m_ata_data_latch = data;
		break;
	case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
		if (BIT(offset, 8))
			m_ata[m_ata_selected]->cs0_w(dcpp & 0x07, data);
		break;
	case 0x28:
		if (BIT(offset, 8))
			m_ata[m_ata_selected]->cs1_w(6, data);
		break;
	case 0x2a: // HDD1 - secondary
		m_ata_selected = 1;
		break;
	case 0x2b: // HDD2 - primary
		m_ata_selected = 0;
		break;
	case 0x2c: // 320
	case 0x2d: // 312
		update_video(dcpp & 1);
		break;
	case 0x2e:
		m_conf = 0;
		m_conf_loading = 1;
		machine().schedule_soft_reset();
		break;

	case 0x88:
		if (cbl_mode())
			m_cbl_data[m_cbl_wa++] = data << 8;
		else
		{
			m_ldac->write(data << 8);
			m_rdac->write(data << 8);
		}
		break;

	case 0x89:
	{
		m_cbl_xx = data;
		m_cbl_cnt = 0;
		m_cbl_wa = 0;
		m_cbl_wae = cbl_mode16();
		const u8 divs[16] = {13, 9, 0, 0, 0, 0, 0, 0, 27, 19, 13, 9, 6, 4, 3, 1};
		attotime rate;
		if (cbl_mode() && divs[m_cbl_xx & 15])
		{
			rate = attotime::from_ticks(divs[m_cbl_xx & 15] + 1, X_SP / 192);
		}
		else
		{
			rate = attotime::never;
			m_irqs->in_clear<2>();
		}
		m_cbl_timer->adjust(rate, 0, rate);
		break;
	}

	case 0x8f: // rom & fastram page
		m_rom_rg = data;
		m_sys_pg = BIT(m_rom_rg, 4) || BIT(data, 0);
		update_memory();
		break;

	case 0x90: // AY8910
		m_ay8910->address_w(data);
		break;
	case 0x91:
		m_ay8910->data_w(data);
		break;

	case 0xc0: // 1FFD
	case 0xc8:
		m_sc = data;
		if (BIT(m_cnf, 6)) m_sc = 0; // CNF_SC_CLEAN
		update_memory();
		break;
	case 0xc1: // 7FFD
	case 0xc9:
		m_pn = data;
		if (BIT(~m_cnf, 7)) m_pn &= 0x3f; // CNF_PN[7..6]_CLEAN
		if (BIT(~m_cnf, 7) && BIT(m_cnf, 5)) m_pn &= 0xdf; // CNF_PN[5]_CLEAN
		if (BIT(m_cnf, 5)) m_pn &= 0xe0;  // CNF_PN[4..0]_CLEAN
		update_memory();
		break;
	case 0xc2:
		m_screen->update_now();
		spectrum_ula_w(offset, data);
		break;
	case 0xc3:
		m_all_mode = data;
		break;
	case 0xcb:
		m_hold = {(7 - (data & 0x0f)) * 2, 7 - (data >> 4)};
		break;
	case 0xc4:
	case 0xcc:
		m_port_y = data;
		break;
	case 0xc5:
	case 0xcd:
		m_screen->update_now();
		m_rgmod = data;
		update_int(true);
		break;

	case 0xc6: // CNF/SYS
	case 0xce:
		m_ram_sys = BIT(~offset, 6);
		if (BIT(data, 1))
		{
			m_turbo = BIT(data, 0);
			update_cpu();
		}
		else
			m_arom16 = BIT(data, 0);

		if (BIT(data, 2))
		{
			m_cnf = data;
			if (BIT(m_cnf, 6)) m_sc = 0;      // CNF_SC_CLEAN
			if (BIT(~m_cnf, 7)) m_pn &= 0x3f; // CNF_PN[7..6]_CLEAN
			if (BIT(~m_cnf, 7) && BIT(m_cnf, 5)) m_pn &= 0xdf; // CNF_PN[5]_CLEAN
			if (BIT(m_cnf, 5)) m_pn &= 0xe0;  // CNF_PN[4..0]_CLEAN
		}

		update_memory();
		break;

	case 0xc7:
	case 0xcf:
		m_alt_acc = 1;
		m_aagr = (offset & 0x300) | data;
		m_xcnt = BIT(offset, 10, 6);
		m_xagr = 0;
		break;

	case 0xd0: case 0xd1: case 0xd2: case 0xd3: case 0xd4: case 0xd5: case 0xd6: case 0xd7:
	case 0xd8: case 0xd9: case 0xda: case 0xdb: case 0xdc: case 0xdd: case 0xde: case 0xdf:
	case 0xe0: case 0xe1: case 0xe2: case 0xe3: case 0xe4: case 0xe5: case 0xe6: case 0xe7:
	case 0xe8: case 0xe9: case 0xea: case 0xeb: case 0xec: case 0xed: case 0xee: case 0xef:
		update_memory();
		break;
	case 0xf0: case 0xf1: case 0xf2: case 0xf3: case 0xf4: case 0xf5: case 0xf6: case 0xf7:
	case 0xf8: case 0xf9: case 0xfa: case 0xfb: case 0xfc: case 0xfd: case 0xfe: case 0xff:
		m_ram_pages[m_pg3] = data;
		update_memory();
		break;

	// no port
	case 0x00:
		break;

	default:
		LOGIO("IOw: %02X / %X < %x\n", dcpp, offset, data);
		break;
	}
}

void sprinter_state::accel_control_r(u8 data)
{
	const bool is_prefix = (data == 0xcb) || (data == 0xdd) || (data == 0xed) || (data == 0xfd);
	if (acc_ena() && !is_prefix && !m_prf_d) // neither prefix nor prefixed
	{
		if ((((data & 0x1b) == 0x00) || ((data & 0x1b) == 0x09) || ((data & 0x1b) == 0x12) || ((data & 0x1b) == 0x1b))
			&& (((data & 0xe4) == 0x40) || ((data & 0xe4) == 0x64)))
		{
			switch(data & 7)
			{
				case 0: m_acc_dir = 0b00000000; break; // LD B,B
				case 1: m_acc_dir = 0b00100101; break; // LD C,C % % fill by constant
				case 2: m_acc_dir = 0b00001001; break; // LD D,D % % load count accelerator
				case 3: m_acc_dir = 0b00010101; break; // LD E,E % % fill by constant VERTICAL
				case 4: m_acc_dir = 0b01000001; break; // LD H,H % % double byte fn
				case 5: m_acc_dir = 0b00100111; break; // LD L,L % % copy line
				case 6: m_acc_dir = 0b00000000; break; // HALT
				case 7: m_acc_dir = 0b00010111; break; // LD A,A % % copy line VERTICAL
			}
			m_fn_acc = MODE_NOP;
		}
		else
		{
			const accel_state state_candidate = static_cast<accel_state>(data);
			switch(state_candidate)
			{
				case MODE_AND:
				case MODE_XOR:
				case MODE_OR:
					m_fn_acc = state_candidate;
					break;
				default:
					break;
			}
		}
	}
	m_prf_d = is_prefix;
}

TIMER_CALLBACK_MEMBER(sprinter_state::acc_tick)
{
	assert(m_access_state == ACCEL_GO);
	m_acc_cnt = m_rgacc;
	m_access_state = ACCEL_ON;

	const bool is_read = param & 1;
	int ticks42 = 0;
	bool is_block_op = BIT(m_acc_dir, 2);
	while (m_access_state != ACCEL_OFF)
	{
		if (is_block_op)
			do_accel_block(is_read);

		if (BIT(m_acc_dir, 3)) // buffer size
		{
			m_rgacc = m_z80_data;
			LOGACCEL("Accel buffer: %d\n", m_rgacc ? m_rgacc : 256);
		}
		else if (BIT(m_acc_dir, 6) && !is_read) // double writes
		{
			accel_mem_w(m_z80_addr ^ 1, m_z80_data);
			ticks42 += 6;
		}

		if (m_acc_cnt == 1 || !is_block_op)
		{
			m_access_state = ACCEL_OFF;
		}
		else
		{
			ticks42 += 6;
			m_acc_cnt--;
		}
	};

	if (is_block_op)
		m_wait_off_timer->adjust(attotime::from_ticks(ticks42, X_SP), is_read);
	else
		// non block ops call method directly without timer
		m_maincpu->adjust_icount(-(ticks42 >> 1));
}

TIMER_CALLBACK_MEMBER(sprinter_state::wait_off)
{
	const bool is_read = param & 1;
	if (!is_read)
		m_z80_wait = false;
	m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, CLEAR_LINE);
}

void sprinter_state::check_accel(bool is_read, offs_t offset, u8 &data)
{
	if (is_read && m_in_out_cmd && !m_z80_m1)
	{
		const bool is_ram = m_pages[BIT(offset, 14, 2)] & BANK_RAM_MASK;
		if (data == 0x1f && is_ram)
			data = 0x0f;
		m_in_out_cmd = false;
	}

	const bool accel_go_case = m_access_state == ACCEL_OFF && !m_z80_m1 && m_acc_dir && acc_ena();
	if (accel_go_case)
	{
		if (!m_z80_wait)
		{
			m_access_state = ACCEL_GO;
			m_z80_addr = offset;
			m_z80_data = data;

			if (BIT(m_acc_dir, 2)) // block operation
			{
				// fastram doesn't apply waits, hence m_wait_cycles_count is not updated
				if (is_read && ~(m_pages[BIT(offset, 14, 2)] & BANK_FASTRAM_MASK))
					m_maincpu->adjust_icount(m_wait_ticks_count);

				m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE);
				m_acc_timer->adjust(attotime::zero, is_read);
				m_z80_wait = true;

				if (is_read)
					m_maincpu->defer_access();
			}
			else
			{
				acc_tick(is_read);
			}
		}
		else
		{
			// deferred read
			assert(is_read);
			data = m_z80_data;
			m_z80_wait = false;
		}
	}
}

void sprinter_state::do_accel_block(bool is_read)
{
	const bool ram_wr = BIT(m_acc_dir, 1);
	if (is_read)
	{
		accel_mem_r(m_z80_addr);
		if (ram_wr)
			update_accel_buffer(m_acc_cnt, m_z80_data);
	}
	else
	{
		if (ram_wr)
		{
			m_z80_data = accel_buffer(m_acc_cnt);

			const u8 pg = m_pages[BIT(m_z80_addr, 14, 2)];
			if (pg == 0xfd)
			{
				if (!cbl_mode16())
				{
					m_cbl_data[m_cbl_wa++] = (m_z80_data << 8);
				}
				else
				{
					if (m_cbl_wae)
						m_cbl_data[m_cbl_wa] = m_z80_data;
					else
					{
						m_cbl_data[m_cbl_wa] |= ((m_z80_data ^ 0x80) << 8);
						m_cbl_wa++;
					}
					m_cbl_wae = !m_cbl_wae;
				}
			}
		}
		accel_mem_w(m_z80_addr, m_z80_data);
	}

	if (BIT(m_acc_dir, 4)) // graph line
		m_port_y++;
	else
		m_z80_addr++;
}

void sprinter_state::accel_mem_r(offs_t offset)
{
	if (m_pages[BIT(offset, 14, 2)] & BANK_RAM_MASK)
	{
		m_z80_data = m_program.read_byte(offset);
	}
}

void sprinter_state::accel_mem_w(offs_t offset, u8 data)
{
	if (m_pages[BIT(offset, 14, 2)] & BANK_RAM_MASK)
	{
		m_program.write_byte(offset, data);
	}
}

u8 &sprinter_state::accel_buffer(u8 idx)
{
	if (m_alt_acc)
	{
		idx = m_xcnt;
		const u16 xcnt_agr = ((m_xcnt << 8) | m_xagr) + m_aagr;
		m_xcnt = xcnt_agr >> 8;
		m_xagr = xcnt_agr & 0xff;
	}

	return m_accel_buffer[idx];
}

void sprinter_state::update_accel_buffer(u8 idx, u8 data)
{
	switch (m_fn_acc)
	{
		case MODE_AND: accel_buffer(idx) &= data; break;
		case MODE_OR:  accel_buffer(idx) |= data; break;
		case MODE_XOR: accel_buffer(idx) ^= data; break;
		case MODE_NOP: accel_buffer(idx) = data; break;
		default: assert(false); break;
	}
}

u8 sprinter_state::bootstrap_r(offs_t offset)
{
	const u16 addr = offset & 0xffff;
	return m_conf_loading
		? m_fastram[addr]
		: m_program.read_byte(0x10000 | addr);
}

void sprinter_state::bootstrap_w(offs_t offset, u8 data)
{
	if (!m_conf_loading)
	{
		m_program.write_byte(0x10000 | u16(offset), data);
	}
	else
	{
		m_fastram[offset & 0xffff] = data;
		m_bitstream_hash += data << (8 * (m_bitstream_count % 4));
		if (++m_bitstream_count > 0xfff)
		{
			m_conf_loading = 0;
			m_conf = !(m_maincpu->csbr_r() & 0x0f); // cs0 disabled => loader reads config from fastram
			m_conf &= m_bitstream_hash == 0x3861cfa4; // Game Config
			m_ram_pages[0x2e] = m_conf ? 0x41 : 0x00;
			machine().schedule_soft_reset();
		}
	}
}

template <u8 Bank> u8 sprinter_state::ram_r(offs_t offset)
{
	static_assert(Bank < 4, "unexpected bank number");

	if (!machine().side_effects_disabled())
		do_mem_wait(3);

	return ((m_pages[Bank] & 0xf0) == 0x50)
		? m_ram->pointer()[(0x50 << 14) + m_port_y * 1024 + (offset & 0x3ff)]
		: reinterpret_cast<u8 *>(m_bank_ram[Bank]->base())[offset & 0x3fff];
}

template <u8 Bank> void sprinter_state::ram_w(offs_t offset, u8 data)
{
	static_assert(Bank < 4, "unexpected bank number");

	do_mem_wait(3);

	offset = (Bank << 14) | (offset & 0x3fff);
	const u8 page = m_pages[Bank] & 0xff;
	if ((Bank == 3) && (m_sc == 0x10) && (m_pages[3] == (BANK_RAM_MASK | 0xa0)))
		machine().schedule_soft_reset();

	if ((page & 0xf0) == 0x50)
	{
		const u32 vaddr = m_port_y * 1024 + (offset & 0x3ff);
		if (BIT(~page, 2))
			m_ram->pointer()[(0x50 << 14) + vaddr] = data;
		if (!(BIT(page, 3) && (data == 0xff)))
			vram_w(vaddr, data);
	}
	else
	{
		if (~m_all_mode & 1)
		{
			const bool sp_scr = BIT(offset, 14) && (BIT(~offset, 15) || ((m_pg3 & 0x3d) == 0x35)); // B"1101X1"
			const bool zx_screen = sp_scr && !(BIT(offset, 13) && BIT(~m_port_y, 7)) && BIT(~m_port_y, 6);
			if (zx_screen)
			{
				const bool zxa15 = BIT(offset, 15) ? BIT(m_pg3, 1) : 0; // SP_SA
				const u8 zxs = m_port_y & 0x3f;
				const u32 vxa = (BIT(offset, 0, 8) << 10) | (BIT(zxs, 1, 4) << 6) | ((BIT(zxs, 0) ^ zxa15 ^ BIT(offset, 13)) << 5) | BIT(offset, 8, 5);
				vram_w(vxa, data);
			}
		}
		reinterpret_cast<u8 *>(m_bank_ram[Bank]->base())[offset & 0x3fff] = data;
	}
}

void sprinter_state::vram_w(offs_t offset, u8 data)
{
	m_screen->update_now();
	const u16 laddr = offset & 0x3ff;
	const bool is_int_updated = (laddr >= 0x300) && (laddr < 0x3a0) && ((offset & 0x403) == 0x400) && (((m_vram[offset] & 0xfc) == 0xfc) || ((data & 0xfc) == 0xfc)) && (m_vram[offset] ^ data);
	m_vram[offset] = data;

	const u8 col = (offset >> 3) & 0x7f;
	const u8 row = offset >> 13;
	m_tilemap->mark_tile_dirty(row * 128 + col);
	m_gfxdecode->gfx(1)->mark_dirty(row * 128 * 8 + col);

	if (is_int_updated)
		update_int(true);
	else if (laddr >= 0x3e0)
	{
		const u16 pen = BIT(offset, 2, 3) * 256 + (offset >> 10);
		const u32 p_red = offset & ~0x3;
		m_palette->set_pen_color(pen, rgb_t(m_vram[p_red], m_vram[p_red + 1], m_vram[p_red + 2]));
	}
}

u8 sprinter_state::isa_r(offs_t offset)
{
	const u8 ctrl = m_ram_pages[m_pg3];
	if ((ctrl & 0xf9) == 0xd0)
	{
		if (!machine().side_effects_disabled())
			do_mem_wait(3);

		return BIT(ctrl, 2) // D:2 0-mem, 1-io
			? m_isa[BIT(ctrl, 1)]->io_r((m_isa_addr_ext << 14) | offset)
			// as far as no connected memory yet, pull up and avoid log about unmapped mem
			: 0xff; //m_isa[BIT(ctrl, 1)]->mem_r((m_isa_addr_ext << 14) | offset);
	}

	return 0xff;
}

void sprinter_state::isa_w(offs_t offset, u8 data)
{
	const u8 ctrl = m_ram_pages[m_pg3];
	if ((ctrl & 0xf9) == 0xd0)
	{
		do_mem_wait(3);

		if (BIT(ctrl, 2))
			m_isa[BIT(ctrl, 1)]->io_w((m_isa_addr_ext << 14) | offset, data);
		else
			// as far as no connected memory yet, nop write and avoid log about unmapped mem
			/*m_isa[BIT(ctrl, 1)]->mem_w((m_isa_addr_ext << 14) | offset, data)*/;
	}
}

void sprinter_state::update_int(bool recalculate)
{
	if (recalculate)
		m_ints.clear();

	const u8 height = m_screen->height() / 8;
	if (m_ints.empty())
	{
		for (auto scr_b = 0; scr_b < height; scr_b++)
		{
			bool pre_int = false;
			const u8 b = (scr_b + height - 2) % height; // 2-top border
			for (auto scr_a = 0; scr_a <= 55; scr_a++)
			{
				const u8 a = (scr_a + 56 - 6) % 56; // 3-left border, 3-teared blank?
				const u8* line1 = as_mode(a, b);
				if ((*line1 & 0xfd) == 0xfd)
					pre_int = true;
				else
				{
					if (pre_int)
						m_ints.push_back({scr_b * 8 + 7, scr_a * 16}); // +7=bottom of the tile
					pre_int = false;
				}
			}
		}
	}

	if (!m_ints.empty())
	{
		attotime min = attotime::never;
		for (std::list<std::pair<u16, u16>>::iterator it=m_ints.begin(); it != m_ints.end(); ++it)
			min = std::min(min, m_screen->time_until_pos((*it).first, (*it).second));
		m_irq_on_timer->adjust(min);
	}
}

u8 sprinter_state::m1_r(offs_t offset)
{
	m_z80_m1 = 1;
	u8 data = m_program.read_byte(offset);
	m_z80_m1 = 0;

	if (!machine().side_effects_disabled())
	{
		m_in_out_cmd = !m_prf_d && (data & 0xf7) == 0xd3; // d3/db - only non-prefixed
		accel_control_r(data);
	}

	return data;
}

void sprinter_state::cio_dtrb_w(int state)
{
	if ((state ^ m_joy1_ctrl) & 1)
	{
		++m_joy1_ctrl;
	}
}

void sprinter_state::pio_b_w(int state)
{
	if (((m_maincpu->pb_r() >> 7) ^ m_joy2_ctrl) & 1)
	{
		++m_joy2_ctrl;
	}
}

u8 sprinter_state::joy_ctrl_r(int num)
{
	const bool is_joy2 = num == 2;
	u16 joy_data = is_joy2 ? m_io_joy2->read() : m_io_joy1->read();
	switch (is_joy2 ? m_joy2_ctrl : m_joy1_ctrl)
	{
		case 0b001:
			joy_data = (joy_data >> 6) | 0x03;
			break;
		case 0b101:
			joy_data = (joy_data >> 6) | 0x0f;
			break;
		case 0b110:
			joy_data = joy_data >> 12;
			break;
		case 0b111:
			joy_data = (joy_data >> 6) & 0x30;
			break;
		default:
			break;
	}

	joy_data |= 0xc0;
	if (is_joy2)
	{
		joy_data ^= 0xff;
	}
	return joy_data;
}

void sprinter_state::map_fetch(address_map &map)
{
	// Overlap with previous because we want real addresses on the 3e00-3fff range
	map(0x0000, 0x3fff).mirror(0x10000).lr8(NAME([this](offs_t offset)
	{
		return m1_r(offset + 0x10000);
	}));
	map(0x3d00, 0x3dff).mirror(0x10000).lr8(NAME([this](offs_t offset)
	{
		if (!machine().side_effects_disabled() && m_dos && BIT(m_pn, 4))
		{
			m_dos = 0;
			update_memory();
		}
		return m1_r(offset + 0x13d00);
	}));
	map(0x4000, 0xffff).mirror(0x10000).lr8(NAME([this](offs_t offset)
	{
		if (!machine().side_effects_disabled() && !m_dos)
		{
			m_dos = 1;
			update_memory();
		}
		return m1_r(offset + 0x14000);
	}));
}

void sprinter_state::map_mem(address_map &map)
{
	map(0x00000, 0x3ffff).rw(FUNC(sprinter_state::bootstrap_r), FUNC(sprinter_state::bootstrap_w));  // bootstrap

	map(0x10000, 0x13fff).rw(FUNC(sprinter_state::ram_r<0>), FUNC(sprinter_state::ram_w<0>));
	map(0x10000, 0x13fff).view(m_bank_view0);
	m_bank_view0[0](0x10000, 0x13fff).nopw(); // RAM RO
	m_bank_view0[1](0x10000, 0x13fff).nopw().bankr(m_bank_rom[0]);
	m_bank_view0[2](0x10000, 0x13fff).bankrw(m_bank0_fastram);

	map(0x14000, 0x17fff).rw(FUNC(sprinter_state::ram_r<1>), FUNC(sprinter_state::ram_w<1>));
	map(0x18000, 0x1bfff).rw(FUNC(sprinter_state::ram_r<2>), FUNC(sprinter_state::ram_w<2>));

	map(0x1c000, 0x1ffff).rw(FUNC(sprinter_state::ram_r<3>), FUNC(sprinter_state::ram_w<3>));
	map(0x1c000, 0x1ffff).view(m_bank_view3);
	m_bank_view3[0](0x1c000, 0x1ffff).rw(FUNC(sprinter_state::isa_r), FUNC(sprinter_state::isa_w)); // ISA
}

void sprinter_state::map_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(sprinter_state::dcp_r), FUNC(sprinter_state::dcp_w));
}

void sprinter_state::init_taps()
{
	address_space &prg = m_maincpu->space(AS_PROGRAM);
	prg.install_read_tap(0x10000, 0x1ffff, "accel_read", [this](offs_t offset, u8 &data, u8 mem_mask)
	{
		if (!machine().side_effects_disabled())
		{
			check_accel(true, offset, data);
		}
	});
	prg.install_write_tap(0x10000, 0x1ffff, "accel_write", [this](offs_t offset, u8 &data, u8 mem_mask)
	{
		if (!machine().side_effects_disabled())
		{
			check_accel(false, offset, data);
		}
	});

	m_maincpu->space(AS_IO).install_write_tap(0x0000, 0xffff, "cpu_io_w", [this](offs_t offset, u8 &data, u8 mem_mask)
	{
		// Internal z84 ports are not accessible through IO map, hence they need special case here
		// Keep these in ascending order
		constexpr u8 z84_int[] = {
			0x10, 0x11, 0x12, 0x13,
			0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
			0xee, 0xef,
			0xf0, 0xf1, 0xf4
		};
		const auto found = std::lower_bound(std::begin(z84_int), std::end(z84_int), offset);
		if ((found != std::end(z84_int)) && (*found == offset))
			dcp_w(offset, data);
	});
}

void sprinter_state::machine_start()
{
	m_isa[0]->space(isa8_device::AS_ISA_IO).unmap_value_high();
	m_isa[1]->space(isa8_device::AS_ISA_IO).unmap_value_high();

	spectrum_128_state::machine_start();

	m_turbo_led.resolve();

	save_item(NAME(m_ram_pages));
	save_item(NAME(m_pages));
	save_item(NAME(m_z80_m1));
	save_item(NAME(m_z80_addr));
	save_item(NAME(m_z80_data));
	save_item(NAME(m_z80_wait));
	save_item(NAME(m_wait_ticks_count));
	save_item(NAME(m_joy1_ctrl));
	save_item(NAME(m_joy2_ctrl));
	save_item(NAME(m_conf));
	save_item(NAME(m_conf_loading));
	save_item(NAME(m_starting));
	save_item(NAME(m_bitstream_count));
	save_item(NAME(m_bitstream_hash));
	save_item(NAME(m_dos));
	save_item(NAME(m_cash_on));
	save_item(NAME(m_cnf));
	save_item(NAME(m_rom_sys));
	save_item(NAME(m_ram_sys));
	save_item(NAME(m_sys_pg));
	save_item(NAME(m_turbo));
	save_item(NAME(m_turbo_hard));
	save_item(NAME(m_arom16));
	save_item(NAME(m_rom_rg));
	save_item(NAME(m_pn));
	save_item(NAME(m_sc));
	save_item(NAME(m_all_mode));
	save_item(NAME(m_port_y));
	save_item(NAME(m_rgmod));
	save_item(NAME(m_pg3));
	save_item(NAME(m_isa_addr_ext));
	save_item(NAME(m_hold.first));
	save_item(NAME(m_hold.second));
	save_item(NAME(m_kbd_data_cnt));
	save_item(NAME(m_in_out_cmd));
	save_item(NAME(m_ata_selected));
	save_item(NAME(m_ata_data_latch));
	save_item(NAME(m_prf_d));
	save_item(NAME(m_rgacc));
	save_item(NAME(m_acc_cnt));
	save_item(NAME(m_accel_buffer));
	save_item(NAME(m_alt_acc));
	save_item(NAME(m_aagr));
	save_item(NAME(m_xcnt));
	save_item(NAME(m_xagr));
	save_item(NAME(m_acc_dir));
	save_item(NAME(m_fn_acc));
	save_item(NAME(m_access_state));
	save_item(NAME(m_cbl_xx));
	save_item(NAME(m_cbl_data));
	save_item(NAME(m_cbl_cnt));
	save_item(NAME(m_cbl_wa));
	save_item(NAME(m_cbl_wae));

	m_beta->enable();

	// reconfigure ROMs
	memory_region *rom = memregion("maincpu");
	m_bank_rom[0]->configure_entries(0, rom->bytes() / 0x4000, rom->base(), 0x4000);
	m_bank0_fastram->configure_entries(0, m_fastram.bytes() / 0x4000, m_fastram.target(), 0x4000);
	for (auto i = 0; i < 4; i++)
		m_bank_ram[i]->configure_entries(0, m_ram->size() / 0x4000, m_ram->pointer(), 0x4000);

	m_dcp_location = m_ram->pointer() + (0x40 << 14);

	const u8 port_default[0x40] = {
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Cx - SYS PORTS COPIES
		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, // Dx - RAM PAGES
		0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00, 0x05, 0x02, 0x41, 0xff, 0x00, 0x00, 0x41, // Ex - ROM PAGES
		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // Fx - RAM PAGES
	};
	std::copy(std::begin(port_default), std::end(port_default), std::begin(m_ram_pages));

	m_all_mode = 0x00;   // c3
	m_port_y   = 0x00;   // c4
	m_rgmod    = 0x00;   // c5
	m_hold     = {0, 0}; // cb
	m_conf_loading = 1;
	m_conf = 0;
}

void sprinter_state::machine_reset()
{
	m_acc_timer->reset();
	m_cbl_timer->reset();
	m_wait_off_timer->reset();
	m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, CLEAR_LINE);

	spectrum_128_state::machine_reset();

	m_z80_wait = false;
	m_wait_ticks_count = 0;
	m_starting = 1;
	m_dos = 1; // off
	m_rom_sys = 0;
	m_ram_sys = 0;
	m_sys_pg = 0;
	m_arom16 = 0;
	m_cnf = 0x00;
	m_pn = 0x00;
	m_sc = 0x00;
	m_rom_rg = 0x00;
	m_cash_on = 0;
	m_isa_addr_ext = 0;
	m_joy1_ctrl = m_joy2_ctrl = 0;

	m_access_state = ACCEL_OFF;
	m_prf_d = false;
	m_acc_dir = 0;
	m_alt_acc = 0;

	m_cbl_xx = 0;
	m_cbl_wa = 0;

	m_ata_selected = 0;

	m_kbd_data_cnt = 0;
	m_in_out_cmd = false;
	m_turbo_hard = 1;

	if (m_conf_loading)
	{
		m_bitstream_count = 0;
		m_bitstream_hash = 0;
		m_bank_rom[0]->set_entry(0x0c);
		m_bank_view0.select(1);
		m_bank_view3.disable();
	}
	else
	{
		update_memory();
		update_video(0);
	}
}

void sprinter_state::device_post_load()
{
	spectrum_128_state::device_post_load();
	m_ints.clear();
}

static const gfx_layout sprinter_charlayout =
{
	8, 8,            // 8 x 8 characters
	256,             // 256 characters
	1,               // 1 bits per pixel
	{ 0 },           // no bitplanes
	{ STEP8(0, 1) }, // x offsets
	{ STEP8(0, 8) }, // y offsets
	1024 * 8         // every char takes 8 bytes
};

static const gfx_layout sprinter_tiles =
{
	8, 8,
	128 * 32 * 8,
	8,
	{ STEP8(0, 1) },
	{ STEP8(0, 8) },
	{ STEP8(0, 1024 * 8) },
	8 * 8
};

static GFXDECODE_START( gfx_sprinter )
	GFXDECODE_RAM( "vram", 0x2c0, sprinter_charlayout, 0x70f, 1 )
	GFXDECODE_RAM( "vram", 0,     sprinter_tiles,      0x100, 256 )
GFXDECODE_END

TILE_GET_INFO_MEMBER(sprinter_state::get_tile_info)
{
	const u8 col = tile_index % 128;
	const u8 row = tile_index / 128;
	tileinfo.set(1, row * 128 * 8 + col, 0, 0);
}

void sprinter_state::video_start()
{
	spectrum_state::video_start();

	m_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(sprinter_state::get_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 128, 32);

	init_taps();

	m_acc_timer = timer_alloc(FUNC(sprinter_state::acc_tick), this);
	m_cbl_timer = timer_alloc(FUNC(sprinter_state::cbl_tick), this);
	m_wait_off_timer = timer_alloc(FUNC(sprinter_state::wait_off), this);
}

static void sprinter_ata_devices(device_slot_interface &device)
{
	device.option_add("hdd", IDE_HARDDISK);
	device.option_add("cdrom", ATAPI_CDROM);
	device.option_add("dvdrom", ATAPI_DVDROM);
}

static void cdrom_config(device_t *device)
{
	device->subdevice<cdda_device>("cdda")->add_route(0, "^^speakers", 0.5, 0);
	device->subdevice<cdda_device>("cdda")->add_route(1, "^^speakers", 0.5, 1);
}

u8 sprinter_state::kbd_fe_r(offs_t offset)
{
	u8 data = 0xff;

	u8 oi = offset >> 8;
	u8 shifts = 0xff;
	for (u8 i = 0; i < 8; i++, oi >>= 1)
	{
		const u8 line_data = m_io_line[i]->read();
		shifts &= line_data;
		if ((oi & 1) == 0)
			data &= line_data;
	}

	if (((offset & 0x0100) == 0) && BIT(~shifts, 6))
		data &= ~0x01; // CS

	if (((offset & 0x8000) == 0) && BIT(~shifts, 7))
		data &= ~0x02; // SS

	data |= 0xe0;
	data ^= 0x40;

	/* cassette input from wav */
	if (m_cassette->input() > 0.0038 )
		data &= ~0x40;

	if (cbl_mode())
	{
		data &= ~0xa0;
		data |= (m_screen->vpos() >= (SPRINT_BORDER_TOP + SPRINT_SCREEN_YSIZE)) << 5;
		data |= (cbl_int_ena() ? (m_cbl_cnt ^ m_cbl_wa) : m_cbl_cnt) & 0x80;
	}

	return data;
}

void sprinter_state::on_kbd_data(int state)
{
	if (state && ((m_all_mode & 0x09) == 0x09))
	{
		m_kbd_data_cnt++;
		m_kbd_data_cnt %= 11;
		if (!m_kbd_data_cnt)
		{
			m_irqs->in_set<1>();
			m_irq_off_timer->adjust(attotime::from_ticks(32, m_maincpu->unscaled_clock()));
		}
	}
}

void sprinter_state::do_mem_wait(u8 cpu_taken = 0)
{
	m_wait_ticks_count = 0;
	if (m_turbo && m_turbo_hard && !m_z80_wait)
	{
		u8 over = m_maincpu->total_cycles() % 6;
		over = over ? (6 - over) : 0;
		m_wait_ticks_count = over + 6 - cpu_taken;

		m_maincpu->adjust_icount(-m_wait_ticks_count);
	}
}

TIMER_CALLBACK_MEMBER(sprinter_state::irq_on)
{
	m_irqs->in_set<0>();
	m_irq_off_timer->adjust(attotime::from_ticks(32, m_maincpu->unscaled_clock()));

	m_joy1_ctrl = m_joy2_ctrl = 0;
	update_int(false);
}

TIMER_CALLBACK_MEMBER(sprinter_state::irq_off)
{
	m_irqs->in_clear<0>(); // screen
	m_irqs->in_clear<1>(); // keyboard
}

TIMER_CALLBACK_MEMBER(sprinter_state::cbl_tick)
{
	u16 left = m_cbl_data[m_cbl_cnt++];
	u16 right = cbl_stereo() ? m_cbl_data[m_cbl_cnt++] : left;
	if (cbl_mode16())
	{
		using std::swap;
		swap(left, right);
	}
	m_ldac->write(left);
	m_rdac->write(right);

	if (cbl_int_ena() && !(m_cbl_cnt & 0x7f))
	{
		m_cbl_wa = m_cbl_cnt ^ 0x80;
		m_irqs->in_set<2>();
	}
}

INPUT_CHANGED_MEMBER(sprinter_state::turbo_changed)
{
	m_turbo_hard = !m_turbo_hard;
	update_cpu();
}

INPUT_PORTS_START( sprinter )
	/* PORT_NAME =  KEY Mode    CAPS Mode    SYMBOL Mode   EXT Mode   EXT+Shift Mode   BASIC Mode  */
	PORT_START("IO_LINE0") /* 0xFEFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CAPS SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)  PORT_CHAR(UCHAR_SHIFT_1) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("z    Z    :      LN       BEEP   COPY") PORT_CODE(KEYCODE_Z)      PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(':')
																	 PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("x    X    $      EXP      INK    CLEAR") PORT_CODE(KEYCODE_X)     PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR('$')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("c    C    ?      LPRINT   PAPER  CONT") PORT_CODE(KEYCODE_C)      PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR('?')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("v    V    /      LLIST    FLASH  CLS") PORT_CODE(KEYCODE_V)       PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR('/')
																	 PORT_CODE(KEYCODE_SLASH) PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line0")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line0") PORT_CODE(KEYCODE_BACKSLASH) PORT_CODE(KEYCODE_SLASH)  PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE1") /* 0xFDFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("a    A    STOP   READ      ~     NEW") PORT_CODE(KEYCODE_A)      PORT_CHAR('a') PORT_CHAR('A')// PORT_CHAR('~')
																	 PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("s    S    NOT    RESTORE   |     SAVE") PORT_CODE(KEYCODE_S)     PORT_CHAR('s') PORT_CHAR('S')// PORT_CHAR('|')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("d    D    STEP   DATA      \\    DIM") PORT_CODE(KEYCODE_D)      PORT_CHAR('d') PORT_CHAR('D')// PORT_CHAR('\\')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f    F    TO     SGN       {     FOR") PORT_CODE(KEYCODE_F)      PORT_CHAR('f') PORT_CHAR('F')// PORT_CHAR('{')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("g    G    THEN   ABS       }     GOTO") PORT_CODE(KEYCODE_G)     PORT_CHAR('g') PORT_CHAR('G')// PORT_CHAR('}')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line1")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line1") PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE2") /* 0xFBFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("q    Q    <=     SIN      ASN      PLOT") PORT_CODE(KEYCODE_Q)   PORT_CHAR('q') PORT_CHAR('Q')
																	 PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("w    W    <>     COS      ACS      DRAW") PORT_CODE(KEYCODE_W)   PORT_CHAR('w') PORT_CHAR('W')
																	 PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("e    E    >=     TAN      ATN      REM") PORT_CODE(KEYCODE_E)    PORT_CHAR('e') PORT_CHAR('E')
																	 PORT_CODE(KEYCODE_END)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("r    R    <      INT      VERIFY   RUN") PORT_CODE(KEYCODE_R)    PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR('<')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("t    T    >      RND      MERGE    RAND") PORT_CODE(KEYCODE_T)   PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR('>')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line2")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line2") PORT_CODE(KEYCODE_HOME) PORT_CODE(KEYCODE_INSERT) PORT_CODE(KEYCODE_END)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE3") /* 0xF7FE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1   EDIT       !    BLUE     DEF FN") PORT_CODE(KEYCODE_1)       PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CHAR('1') PORT_CHAR('!')
														   PORT_CODE(KEYCODE_F1)
															   PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2   CAPS LOCK  @    RED      FN")     PORT_CODE(KEYCODE_2)       PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CHAR('2') PORT_CHAR('@')
														   PORT_CODE(KEYCODE_F2)
															   PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3   TRUE VID   #    MAGENTA  LINE")   PORT_CODE(KEYCODE_3)       PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CHAR('3') PORT_CHAR('#')
														   PORT_CODE(KEYCODE_F3)
															   PORT_CODE(KEYCODE_PGUP)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4   INV VID    $    GREEN    OPEN#")  PORT_CODE(KEYCODE_4)       PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_CHAR('4') PORT_CHAR('$')
														   PORT_CODE(KEYCODE_F4)
															   PORT_CODE(KEYCODE_PGDN)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5   Left       %    CYAN     CLOSE#") PORT_CODE(KEYCODE_5)       PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CHAR('5') PORT_CHAR('%')
														   PORT_CODE(KEYCODE_F5)
															   PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line3") PORT_CODE(KEYCODE_TAB) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CODE(KEYCODE_PGUP) PORT_CODE(KEYCODE_PGDN) PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line3")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE4") /* 0xEFFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0   DEL        _    BLACK    FORMAT") PORT_CODE(KEYCODE_0)       PORT_CHAR(UCHAR_MAMEKEY(F10)) PORT_CHAR('0') PORT_CHAR('_')
														   PORT_CODE(KEYCODE_F10)
															   PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9   GRAPH      )             POINT")  PORT_CODE(KEYCODE_9)       PORT_CHAR(UCHAR_MAMEKEY(F9)) PORT_CHAR('9') PORT_CHAR(')')
														   PORT_CODE(KEYCODE_F9)
															   PORT_CODE(KEYCODE_DEL)
																		  PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8   Right      (             CAT")    PORT_CODE(KEYCODE_8)       PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_CHAR('8') PORT_CHAR('(')
														   PORT_CODE(KEYCODE_F8)
															   PORT_CODE(KEYCODE_RIGHT) PORT_CODE(KEYCODE_6_PAD)
																		  PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7   Up         '    WHITE    ERASE")  PORT_CODE(KEYCODE_7)       PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_CHAR('7') PORT_CHAR('\'')
														   PORT_CODE(KEYCODE_F7)
															   PORT_CODE(KEYCODE_UP) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6   Down       &    YELLOW   MOVE")   PORT_CODE(KEYCODE_6)       PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_CHAR('6') PORT_CHAR('&')
														   PORT_CODE(KEYCODE_F6)
															   PORT_CODE(KEYCODE_DOWN) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line4") PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_RIGHT) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_UP) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_DOWN) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line4") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE5") /* 0xDFFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("p    P    \"     TAB      (c)    PRINT") PORT_CODE(KEYCODE_P)    PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR('"')
																	 PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("o    O    ;      PEEK     OUT    POKE") PORT_CODE(KEYCODE_O)     PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(';')
																	 PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("i    I    AT     CODE     IN     INPUT") PORT_CODE(KEYCODE_I)    PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("u    U    OR     CHR$     ]      IF") PORT_CODE(KEYCODE_U)       PORT_CHAR('u') PORT_CHAR('U')// PORT_CHAR(']')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("y    Y    AND    STR$     [      RETURN") PORT_CODE(KEYCODE_Y)   PORT_CHAR('y') PORT_CHAR('Y')// PORT_CHAR('[')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line5")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line5") PORT_CODE(KEYCODE_QUOTE) PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE6") /* 0xBFFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)    PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("l    L    =      USR      ATTR     LET") PORT_CODE(KEYCODE_L)    PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR('=')
																	 PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("k    K    +      LEN      SCREEN$  LIST") PORT_CODE(KEYCODE_K)   PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR('+')
																	 PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("j    J    -      VAL      VAL$     LOAD") PORT_CODE(KEYCODE_J)   PORT_CHAR('j') PORT_CHAR('J') PORT_CHAR('-')
																	 PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("h    H    ^      SQR      CIRCLE   GOSUB") PORT_CODE(KEYCODE_H)  PORT_CHAR('h') PORT_CHAR('H') PORT_CHAR('^')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line6")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line6") PORT_CODE(KEYCODE_EQUALS) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IO_LINE7") /* 0x7FFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE)                                       PORT_CHAR(' ')
														   PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SYMBOL SHIFT") PORT_CODE(KEYCODE_RCONTROL) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))
														   PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("m    M    .      PI       INVERSE  PAUSE") PORT_CODE(KEYCODE_M)        PORT_CHAR('m') PORT_CHAR('M') PORT_CHAR('.')
																	 PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("n    N    ,      INKEY$   OVER     NEXT") PORT_CODE(KEYCODE_N)         PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(',')
																	 PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("b    B    *      BIN      BRIGHT   BORDER") PORT_CODE(KEYCODE_B)       PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR('*')
																	 PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line7") PORT_CODE(KEYCODE_ESC) PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line7") PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_COMMA) PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)


	PORT_START("mouse_input1")
	PORT_BIT(0xff, 0, IPT_MOUSE_X) PORT_SENSITIVITY(60)

	PORT_START("mouse_input2")
	PORT_BIT(0xff, 0, IPT_MOUSE_Y) PORT_INVERT PORT_SENSITIVITY(60)

	PORT_START("mouse_input3")
	PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON4) PORT_NAME("Left mouse button") PORT_CODE(MOUSECODE_BUTTON1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON5) PORT_NAME("Right mouse button") PORT_CODE(MOUSECODE_BUTTON2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON6) PORT_NAME("Middle mouse button") PORT_CODE(MOUSECODE_BUTTON3)

	PORT_START("JOY1")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_PLAYER(1) PORT_CODE(JOYCODE_HAT1RIGHT) PORT_CODE(JOYCODE_X_RIGHT_SWITCH) PORT_8WAY
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT)  PORT_PLAYER(1) PORT_CODE(JOYCODE_HAT1LEFT) PORT_CODE(JOYCODE_X_LEFT_SWITCH) PORT_8WAY
	PORT_BIT(0x0104, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN)  PORT_PLAYER(1) PORT_CODE(JOYCODE_HAT1DOWN) PORT_CODE(JOYCODE_Y_DOWN_SWITCH) PORT_8WAY
	PORT_BIT(0x0208, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP)    PORT_PLAYER(1) PORT_CODE(JOYCODE_HAT1UP) PORT_CODE(JOYCODE_Y_UP_SWITCH) PORT_8WAY
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_BUTTON2)        PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON2) PORT_NAME("%p B")
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_BUTTON5)        PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON5) PORT_NAME("%p C")
	PORT_BIT(0x00c0, IP_ACTIVE_LOW,  IPT_UNUSED)

	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_BUTTON1)        PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON1) PORT_NAME("%p A")
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_BUTTON8)        PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON8) PORT_NAME("%p Start")

	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_BUTTON7)        PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON7) PORT_NAME("%p Select")
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_BUTTON3)        PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON3) PORT_NAME("%p X")
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_BUTTON4)        PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON4) PORT_NAME("%p Y")
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_BUTTON6)        PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON6) PORT_NAME("%p Z")

	PORT_START("JOY2")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_PLAYER(2) PORT_CODE(JOYCODE_HAT1RIGHT) PORT_CODE(JOYCODE_X_RIGHT_SWITCH) PORT_8WAY
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT)  PORT_PLAYER(2) PORT_CODE(JOYCODE_HAT1LEFT) PORT_CODE(JOYCODE_X_LEFT_SWITCH) PORT_8WAY
	PORT_BIT(0x0104, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN)  PORT_PLAYER(2) PORT_CODE(JOYCODE_HAT1DOWN) PORT_CODE(JOYCODE_Y_DOWN_SWITCH) PORT_8WAY
	PORT_BIT(0x0208, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP)    PORT_PLAYER(2) PORT_CODE(JOYCODE_HAT1UP) PORT_CODE(JOYCODE_Y_UP_SWITCH) PORT_8WAY
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_BUTTON2)        PORT_PLAYER(2) PORT_CODE(JOYCODE_BUTTON2) PORT_NAME("%p B")
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_BUTTON5)        PORT_PLAYER(2) PORT_CODE(JOYCODE_BUTTON5) PORT_NAME("%p C")
	PORT_BIT(0x00c0, IP_ACTIVE_LOW,  IPT_UNUSED)

	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_BUTTON1)        PORT_PLAYER(2) PORT_CODE(JOYCODE_BUTTON1) PORT_NAME("%p A")
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_BUTTON8)        PORT_PLAYER(2) PORT_CODE(JOYCODE_BUTTON8) PORT_NAME("%p Start")

	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_BUTTON7)        PORT_PLAYER(2) PORT_CODE(JOYCODE_BUTTON7) PORT_NAME("%p Select")
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_BUTTON3)        PORT_PLAYER(2) PORT_CODE(JOYCODE_BUTTON3) PORT_NAME("%p X")
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_BUTTON4)        PORT_PLAYER(2) PORT_CODE(JOYCODE_BUTTON4) PORT_NAME("%p Y")
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_BUTTON6)        PORT_PLAYER(2) PORT_CODE(JOYCODE_BUTTON6) PORT_NAME("%p Z")


	PORT_START("TURBO")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("TURBO") PORT_CODE(KEYCODE_F12) PORT_TOGGLE PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(sprinter_state::turbo_changed), 0)
INPUT_PORTS_END

void sprinter_state::sprinter(machine_config &config)
{
	spectrum_128(config);
	config.device_remove("palette");
	config.device_remove("exp");
	config.device_remove("dma");
	config.set_default_layout(layout_sprinter);

	m_ram->set_default_size("64M");

	INPUT_MERGER_ANY_HIGH(config, m_irqs).output_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z84C015(config.replace(), m_maincpu, X_SP / 12); // 3.5MHz default
	m_maincpu->set_m1_map(&sprinter_state::map_fetch);
	m_maincpu->set_memory_map(&sprinter_state::map_mem);
	m_maincpu->set_io_map(&sprinter_state::map_io);
	m_maincpu->set_irq_acknowledge_callback(NAME([](device_t &, int){ return 0xff; }));
	m_maincpu->irqack_cb().set(m_irqs, FUNC(input_merger_any_high_device::in_clear<2>));
	m_maincpu->irqack_cb().append(m_irqs, FUNC(input_merger_any_high_device::in_clear<1>));
	m_maincpu->irqack_cb().append(m_irqs, FUNC(input_merger_any_high_device::in_clear<0>));

	DS12885(config, m_rtc, XTAL(32'768)); // should be DS12887A
	ATA_INTERFACE(config, m_ata[0]).options(sprinter_ata_devices, "hdd", "cdrom", false);
	m_ata[0]->slot(1).set_option_machine_config("cdrom", cdrom_config);
	ATA_INTERFACE(config, m_ata[1]).options(sprinter_ata_devices, "hdd", "hdd", false);

	BETA_DISK(config, m_beta, 0);

	ISA8(config, m_isa[0], X_SP / 5);
	m_isa[0]->set_custom_spaces();
	ISA8_SLOT(config, "isa0", 0, m_isa[0], pc_isa8_cards, "zxbus_adapter", false);

	ISA8(config, m_isa[1], X_SP / 5);
	m_isa[1]->set_custom_spaces();
	ISA8_SLOT(config, "isa1", 0, m_isa[1], pc_isa8_cards, nullptr, false);

	m_screen->set_raw(X_SP / 3, SPRINT_WIDTH, SPRINT_HEIGHT, { 0, SPRINT_XVIS - 1, 0, SPRINT_YVIS - 1 });
	m_screen->set_screen_update(FUNC(sprinter_state::screen_update));

	PALETTE(config, "palette", palette_device::BLACK).set_entries(256 * 8);
	SPECTRUM_ULA_UNCONTENDED(config.replace(), m_ula);

	PC_KBDC(config, m_kbd, pc_at_keyboards, STR_KBD_MICROSOFT_NATURAL);
	m_kbd->out_data_cb().set(m_maincpu, FUNC(z84c015_device::rxa_w)); // KBD_DATR
	m_kbd->out_clock_cb().set(m_maincpu, FUNC(z84c015_device::rxca_w)); // KBD_CLKR
	m_kbd->out_clock_cb().append(m_maincpu, FUNC(z84c015_device::txca_w));
	m_kbd->out_clock_cb().append(FUNC(sprinter_state::on_kbd_data));

	m_maincpu->set_clk_trg<0>(X_SP / 48);
	m_maincpu->set_clk_trg<1>(X_SP / 48);
	m_maincpu->set_clk_trg<2>(X_SP / 48);
	m_maincpu->out_dtrb_callback().set(FUNC(sprinter_state::cio_dtrb_w)); // joy1 ctrl
	m_maincpu->out_pb_callback().set(FUNC(sprinter_state::pio_b_w)); // joy2 ctrl
	m_maincpu->in_pa_callback().set([this]() { return joy_ctrl_r(2); });

	rs232_port_device &m_rs232(RS232_PORT(config, "rs232", default_rs232_devices, "microsoft_mouse"));
	m_rs232.option_add("microsoft_mouse", MSFT_HLE_SERIAL_MOUSE);
	m_rs232.option_add("logitech_mouse", LOGITECH_HLE_SERIAL_MOUSE);
	m_rs232.option_add("wheel_mouse", WHEEL_HLE_SERIAL_MOUSE);
	m_rs232.rxd_handler().set(m_maincpu, FUNC(z84c015_device::rxb_w)); // MOUSE_D
	m_maincpu->out_txdb_callback().set("rs232", FUNC(rs232_port_device::write_txd)); // TXDB
	m_maincpu->zc_callback<0>().set(m_maincpu, FUNC(z84c015_device::rxcb_w)); // CLK_COM1
	m_maincpu->zc_callback<0>().append(m_maincpu, FUNC(z84c015_device::txcb_w));
	m_maincpu->zc_callback<2>().set(m_maincpu, FUNC(z84c015_device::trg3));

	SPEAKER(config.replace(), "speakers", 2).front();

	config.device_remove("ay_slot");
	AY8910(config, "ay8912", X_SP / 24)
		.add_route(0, "speakers", 0.50, 0)
		.add_route(1, "speakers", 0.25, 0)
		.add_route(1, "speakers", 0.25, 1)
		.add_route(2, "speakers", 0.50, 1);

	DAC_16BIT_R2R(config, m_ldac, 0).add_route(ALL_OUTPUTS, "speakers", 0.5, 0);
	DAC_16BIT_R2R(config, m_rdac, 0).add_route(ALL_OUTPUTS, "speakers", 0.5, 1);

	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_sprinter);
}


ROM_START( sprinter )
	ROM_REGION(0x040000, "maincpu", ROMREGION_ERASEFF)
	ROM_DEFAULT_BIOS("v3.04")

	ROM_SYSTEM_BIOS(0, "v2.13", "Firmware v2.13, 23.01.2002") // dd.mm.yyyy
	ROMX_LOAD( "sp2k-2.13.rom", 0x000000, 0x40000, CRC(6495575f) SHA1(a9ca06b27e7c5b2b5b9ff8fc2d19ee24ed64c258), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "v2.17", "Firmware v2.17, 03.03.2002")
	ROMX_LOAD( "sp2k-2.17.rom", 0x000000, 0x40000, CRC(3c7f1025) SHA1(d5c3d10b3b67f9ef87d3ce8a52ae3c33b95b9171), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "v3.00", "Firmware v3.00, 07.04.2002")
	ROMX_LOAD( "sp2k-3.00.rom", 0x000000, 0x40000, CRC(193de3da) SHA1(428dcb1253a88e7b5aedcd68b5bf6d2487592e10), ROM_BIOS(2))

	ROM_SYSTEM_BIOS(3, "v3.03", "Firmware v3.03, 13.05.2003")
	ROMX_LOAD( "sp2k-3.03.rom", 0x000000, 0x40000, CRC(fe26f578) SHA1(ef6d0fe4ec1bae7bda572a4fb3b9497a8910b885), ROM_BIOS(3))

	ROM_SYSTEM_BIOS(4, "v3.04", "Firmware v3.04, 17.06.2003")
	ROMX_LOAD( "sp2k-3.04.rom", 0x000000, 0x40000, CRC(1729cb5c) SHA1(fb4c9f80651aa87526f141839fb4d6cb86b654c7), ROM_BIOS(4))

	ROM_SYSTEM_BIOS(5, "v3.05", "Firmware v3.05, 01.09.2022")
	ROMX_LOAD( "sp2k-3.05.rom", 0x000000, 0x40000, CRC(fe1c2685) SHA1(10e4e29bdc058cd4380837fb8831ce4f5977f6b8), ROM_BIOS(5))

	ROM_SYSTEM_BIOS(6, "v3.06", "Firmware v3.06, 25.06.2025")
	ROMX_LOAD( "sp2k-3.06.rom", 0x000000, 0x40000, CRC(187f4382) SHA1(717ed28c59f9533a9b3f9d24098b536a0d3c1573), ROM_BIOS(6))
ROM_END
} // Anonymous namespace

ALLOW_SAVE_TYPE(sprinter_state::accel_state);
ALLOW_SAVE_TYPE(sprinter_state::access_state);


/*    YEAR  NAME        PARENT   COMPAT MACHINE   INPUT      CLASS           INIT        COMPANY                 FULLNAME           FLAGS */
// 1996 - Sp97 Prototype
COMP( 2000, sprinter,   spec128, 0,     sprinter, sprinter,  sprinter_state, empty_init, "Peters Plus, Ivan Mak", "Sprinter Sp2000", MACHINE_SUPPORTS_SAVE)



squale.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Jean-Francois DEL NERO
/***************************************************************************

    Apollo 7 Squale

    The following hardware description is an extract of the Squale hardware analysis
    presented on this page : http://hxc2001.free.fr/Squale.
    This is a work in progress and is subject to changes

    PCB Ref Qty Manufacturer Ref    Description / Datasheet
    ============================================================================================
    U1              1   EF6809P             8-BIT MICROPROCESSOR UNIT (MPU)
    U2              1   27C32 / 27C64       EPROM
    U72,U75         1   EF6821P             PERIPHERAL INTERFACE ADAPTER (PIA)
    U69             1   EF6850              ASYNCHRONOUS COMMUNICATIONS INTERFACE ADAPTER (ACIA)
    U59             1   EF9365P             GRAPHIC DISPLAY PROCESSOR (GDP)
    U65             1   AY-3-8910A          PROGRAMMABLE SOUND GENERATOR
    U16,U17,U18,U19,
    U20,U21,U22,U23 8   MK4564              65,536 x 1-BIT DYNAMIC RAM
    U38,U39,U40,U41,
    U42,U43,U44,U45,
    U46,U47,U48,U49,
    U50,U51,U52,U53 16  TMS4116             16,384-BIT DYNAMIC RAM
    U68             1   EFB7510             SINGLE CHIP ASYNCHRONOUS FSK MODEM


    Memory map
    ==========

    Devices                                     Adresses
    =========================================================
    EPROM                                       0xF100-0xFFFF
    Extension Port                              0xF080-0xF0FF
    FREE                                        0xF070-0xF07F
    AY-3-8910A                                  0xF060-0xF06F
    ACIA EF6850 (Modem + K7)                    0xF050-0xF05F
    Restricted area                             0xF04C-0xF04F
    PIO EF6821 (Printer + Cartridge)            0xF048-0xF04B
    PIO EF6821 (Keyboard)                       0xF044-0xF047
    FREE                                        0xF040-0xF043
    VID_RD2                                     0xF030-0xF03F
    VID_RD1                                     0xF020-0xF02F
    REG1                                        0xF010-0xF01F
    Video Controller EF9365                     0xF000-0xF00F
    System RAM                                  0x0000-0xEFFF


    Notes:
    1) For 8KB versions of the monitor, the bank switching is done with the bit 7 of REG1.
    2) VID_RD1 : [7..0] = I0,R0,G0,B0,I1,R1,G1,B1 (I=Intensity,R=Red,G=Green,B=Blue)
    3) VID_RD2 : [7..0] = I2,R2,G2,B2,I3,R3,G3,B3 (I=Intensity,R=Red,G=Green,B=Blue)
    3) REG1 : [7..0] = EPROM Bank,-,Modem,K7,I,R,G,B (I=Intensity,R=Red,V=Green,B=Blue)


LIST OF MONITOR COMMANDS (all addresses must be 4 characters)
C         Load a file from tape
D         Load DOS from floppy
E         Load a file from tape and run it
G a       Go (a = address)
M a       Memory dump (a = start address) Use the arrow keys to move around.
R         Reset then load DOS from floppy
S a b c   Save memory to tape  (a = start, b = end, c = exec)

KEYBOARD
- When started, the Lock is engaged, so numbers appear as symbols. Hitting
  Shift will unlock this and return the keyboard to normal.
- To use the natural keyboard, hit > (or some other shifted character),
  then proceed as normal.
- To paste, use the emulated keyboard and hit Shift, then do the paste.
- The monitor command-line cannot handle the arrow keys or Del correctly.
- It is thought that the BASIC cartridge has better keyboard handling.

TODO
- Cassette
- ACIA

****************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/m6809/m6809.h"
#include "imagedev/floppy.h"
#include "machine/6821pia.h"
#include "machine/6850acia.h"
#include "machine/timer.h"
#include "machine/wd_fdc.h"
#include "sound/ay8910.h"
#include "video/ef9365.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


#define VERBOSE (0)
#include "logmacro.h"


namespace {

class squale_state : public driver_device
{
public:
	squale_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_acia(*this, "ef6850")
		, m_ay8910(*this, "ay8910")
		, m_pia_u72(*this, "pia_u72")
		, m_pia_u75(*this, "pia_u75")
		, m_ef9365(*this, "ef9365")
		, m_maincpu(*this, "maincpu")
		, m_rom_bank(*this, "rom_bank")
		, m_kbd(*this, "X%u", 0U)
		, m_ay_keys(*this, "ay_keys")
		, m_ay_joy(*this, "ay_joy_%u", 1U)
		, m_fdc(*this, "wd1770")
		, m_floppy(*this, "wd1770:%u", 0U)
		, m_cart(*this, "cartslot")
	{ }

	void squale(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void ctrl_w(uint8_t data);
	uint8_t video_ram_read_reg1();
	uint8_t video_ram_read_reg2();
	void fdc_sel0_w(uint8_t data);
	uint8_t fdc_sel0_r();
	void fdc_sel1_w(uint8_t data);
	uint8_t fdc_sel1_r();
	uint8_t pia_u72_porta_r();
	uint8_t pia_u72_portb_r();
	uint8_t pia_u75_porta_r();
	uint8_t pia_u75_portb_r();
	void pia_u72_porta_w(uint8_t data);
	void pia_u72_portb_w(uint8_t data);
	void pia_u75_porta_w(uint8_t data);

	uint8_t ay_porta_r();
	uint8_t ay_portb_r();

	void pia_u72_ca2_w(int state);
	void pia_u72_cb2_w(int state);

	void pia_u75_cb2_w(int state);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER( cart_load );

	uint8_t keyboard_line = 0U;
	uint8_t fdc_sel0 = 0U;
	uint8_t fdc_sel1 = 0U;

	uint8_t  cart_addr_counter_inc_ck = 0U;
	uint8_t  cart_addr_counter_reset = 0U;
	uint16_t cart_addr_counter = 0U;

	TIMER_DEVICE_CALLBACK_MEMBER(squale_scanline);

	void squale_mem(address_map &map) ATTR_COLD;

	required_device<acia6850_device> m_acia;
	required_device<ay8910_device> m_ay8910;
	required_device<pia6821_device> m_pia_u72;
	required_device<pia6821_device> m_pia_u75;
	required_device<ef9365_device> m_ef9365;
	required_device<cpu_device> m_maincpu;
	memory_bank_creator m_rom_bank;
	required_ioport_array<8> m_kbd;
	required_ioport m_ay_keys;
	required_ioport_array<2> m_ay_joy;
	required_device<wd1770_device> m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<generic_slot_device> m_cart;
};

/*****************************************
* Machine control register I/O Handlers  *
******************************************/

void squale_state::ctrl_w(uint8_t data)
{
	LOG("write ctrl reg : 0x%2X\n",data);

	m_rom_bank->set_entry(BIT(data, 7));

	m_ef9365->set_color_filler(data & 0xf);
}

uint8_t  squale_state::video_ram_read_reg1()
{
	uint8_t data = 0x00;

	//D7             D0
	//I2R2G2B2 I3R3G3B3

	for(int p = 0; p < 4 ; p++)
	{
		if( m_ef9365->get_last_readback_word(p, nullptr) & 8 )
		{
			data |= (0x01 << p);
		}
	}

	data <<= 4;

	for(int p = 0; p < 4 ; p++)
	{
		if( m_ef9365->get_last_readback_word(p, nullptr) & 4 )
		{
			data |= (0x01 << p);
		}
	}

	LOG("read video_ram_read_reg1 reg : 0x%X\n",data);

	return data;
}

uint8_t squale_state::video_ram_read_reg2()
{
	uint8_t data = 0x00;

	//D7             D0
	//I0R0G0B0 I1R1G1B1

	for(int p = 0; p < 4 ; p++)
	{
		if( m_ef9365->get_last_readback_word(p, nullptr) & 2 )
		{
			data |= (0x01 << p);
		}
	}

	data <<= 4;

	for(int p = 0; p < 4 ; p++)
	{
		if( m_ef9365->get_last_readback_word(p, nullptr) & 1 )
		{
			data |= (0x01 << p);
		}
	}

	LOG("read video_ram_read_reg2 reg : 0x%X\n",data);

	return data;
}

/**********************************
* Floppy controller I/O Handlers  *
***********************************/

void squale_state::fdc_sel0_w(uint8_t data)
{
	floppy_image_device *floppy = nullptr;

	LOG("%s: write fdc_sel0_w reg : 0x%X\n",machine().describe_context(),data);

	fdc_sel0 = data;

	if( BIT(data, 3) ) // Drive 0
	{
		floppy = m_floppy[0]->get_device();
		if(!floppy || !floppy->dskchg_r())
		{
			fdc_sel0 &= ~(0x08);
		}
	}

	if( BIT(data, 2) ) // Drive 1
	{
		floppy = m_floppy[1]->get_device();
		if(!floppy || !floppy->dskchg_r())
		{
			fdc_sel0 &= ~(0x04);
		}
	}

	if(floppy)
	{
		floppy->ss_w(BIT(data, 4)); // Side selector bit
	}

	//m_fdc->mon_w(BIT(data, 3));
	m_fdc->dden_w(BIT(data, 5));    // Double / Single density selector bit
	m_fdc->set_floppy(floppy);
}

void squale_state::fdc_sel1_w(uint8_t data)
{
	LOG("%s: write fdc_sel1_w reg : 0x%X\n",machine().describe_context(),data);

	fdc_sel1 = data;
}

uint8_t squale_state::fdc_sel0_r()
{
	uint8_t data = fdc_sel0;

	LOG("%s: read fdc_sel0_r 0x%.2X\n",machine().describe_context(),data);

	return data;
}

uint8_t squale_state::fdc_sel1_r()
{
	uint8_t data = fdc_sel1;

	LOG("%s: read fdc_sel1_r 0x%.2X\n",machine().describe_context(),data);

	return data;
}

/**********************************
*      Keyboard I/O Handlers      *
***********************************/

void squale_state::pia_u75_porta_w(uint8_t data)
{
	// U75 PIA Port A : Keyboard rows output
	LOG("%s: write pia_u75_porta_w : 0x%.2X\n",machine().describe_context(),data);

	keyboard_line = data;
}

uint8_t squale_state::pia_u75_porta_r()
{
	// U75 PIA Port A : Keyboard rows output
	uint8_t data = keyboard_line;

	LOG("%s: read pia_u75_porta_r\n",machine().describe_context());

	return data;
}

uint8_t squale_state::pia_u75_portb_r()
{
	// U75 PIA Port B : Keyboard column input
	uint8_t data = 0xff;

	for (int i = 0; i < 8; i++)
	{
		for( int j = 0; j < 8 ; j++)
		{
			if( !BIT(keyboard_line, j) )
			{
				if ( BIT(m_kbd[i]->read(), j) )
				{
					data &= ~( 0x01 << i);
				}
			}
		}
	}

	LOG("%s: read pia_u75_portb_r : 0x%.2X\n",machine().describe_context(),data);

	return data;
}

/***********************************
*      AY-8910 I/O Handlers        *
* (Joysticks, Ctrl/Shift keys,...) *
************************************/

uint8_t squale_state::ay_portb_r()
{
	// AY-8910 Port B : Joystick 2, Shift, Shift Lock, Ctrl Keys
	// B7 : Joystick 2 - Fire
	// B6 : Keyboard   - Control
	// B5 : Keyboard   - Shift
	// B4 : Keyboard   - Shift Lock
	// B3 : Joystick 2 - Up
	// B2 : Joystick 2 - Down
	// B1 : Joystick 2 - Left
	// B0 : Joystick 2 - Right

	uint8_t data;

	data =  m_ay_keys->read() & 0x70;
	data |= m_ay_joy[1]->read() & 0x8f;

	LOG("%s: read ay_portb_r : 0x%.2X\n",machine().describe_context(),data);

	return data;
}

uint8_t squale_state::ay_porta_r()
{
	// AY-8910 Port A : Joystick 1, light pen
	// B7 : Joystick 1 - Fire
	// B6 : -
	// B5 : Light pen Int.
	// B4 : -
	// B3 : Joystick 1 - Up
	// B2 : Joystick 1 - Down
	// B1 : Joystick 1 - Left
	// B0 : Joystick 1 - Right

	LOG("%s: read ay_porta_r\n",machine().describe_context());

	return m_ay_joy[0]->read() & 0x8f;
}

/***********************************
*      Cartridge I/O Handlers      *
************************************/

uint8_t squale_state::pia_u72_porta_r()
{
	// U72 PIA Port A : Cartridge data bus
	uint8_t data = 0xff;

	LOG("%s: read pia_u72_porta_r\n",machine().describe_context());

	if( m_cart->exists() )
		data = m_cart->read_rom( cart_addr_counter % m_cart->get_rom_size() );

	return data;
}

void squale_state::pia_u72_porta_w(uint8_t data)
{
	// U72 PIA Port A : Cartridge data bus

	LOG("%s: write pia_u72_porta_w : 0x%.2X\n",machine().describe_context(),data);
}

void squale_state::pia_u72_ca2_w(int state)
{
	// U72 PIA CA2 : Cartridge address control

	LOG("%s: U72 PIA Port CA2 Set to %d\n", machine().describe_context(),state);

	if( state )
	{
		cart_addr_counter_inc_ck = 1;
	}
	else
	{
		// If not in reset state, increment the address counter (U73 & U74) at the falling edge of ca2.
		if( cart_addr_counter_inc_ck && !cart_addr_counter_reset )
		{
			cart_addr_counter++;
		}

		cart_addr_counter_inc_ck = 0;
	}
}

void squale_state::pia_u75_cb2_w(int state)
{
	// U75 PIA CB2 : Cartridge address reset

	LOG("%s: U75 PIA Port CB2 Set to %d\n", machine().describe_context(),state);

	if( state )
	{
		// Cartridge address counter (U73 & U74) reset to 0
		cart_addr_counter_reset = 1;
		cart_addr_counter = 0x0000;
	}
	else
	{
		cart_addr_counter_reset = 0;
	}
}

/**********************************
*      Printer I/O Handlers      *
***********************************/

uint8_t squale_state::pia_u72_portb_r()
{
	// U72 PIA Port B : Printer data bus

	uint8_t data = 0xff;

	LOG("%s: read pia_u72_portb_r\n",machine().describe_context());

	return data;
}

void squale_state::pia_u72_portb_w(uint8_t data)
{
	// U72 PIA Port B : Printer data bus

	LOG("%s: write pia_u72_portb_w : 0x%.2X\n",machine().describe_context(),data);
}

void squale_state::pia_u72_cb2_w(int state)
{
	// U72 PIA CB2 : Printer Data Strobe line

	LOG("%s: U72 PIA Port CB2 Set to %d\n", machine().describe_context(),state);
}

DEVICE_IMAGE_LOAD_MEMBER( squale_state::cart_load )
{
	uint32_t const size = m_cart->common_get_size("rom");

	if (!size || size > 0x10000)
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported cartridge size (must be more than 64K)");

	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

TIMER_DEVICE_CALLBACK_MEMBER( squale_state::squale_scanline )
{
	m_ef9365->update_scanline((uint16_t)param);
}

void squale_state::squale_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xefff).ram();
	map(0xf000, 0xffff).bankr("rom_bank");
	map(0xf000, 0xf00f).rw(m_ef9365, FUNC(ef9365_device::data_r), FUNC(ef9365_device::data_w));
	map(0xf010, 0xf01f).w(FUNC(squale_state::ctrl_w));
	map(0xf020, 0xf02f).r(FUNC(squale_state::video_ram_read_reg1));
	map(0xf030, 0xf03f).r(FUNC(squale_state::video_ram_read_reg2));
	map(0xf044, 0xf047).rw(m_pia_u75, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xf048, 0xf04b).rw(m_pia_u72, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xf050, 0xf05f).rw(m_acia, FUNC(acia6850_device::data_r), FUNC(acia6850_device::data_w));
	map(0xf060, 0xf06f).rw(m_ay8910, FUNC(ay8910_device::data_r), FUNC(ay8910_device::address_data_w));
	map(0xf080, 0xf083).rw(m_fdc, FUNC(wd1770_device::read), FUNC(wd1770_device::write));
	map(0xf08a, 0xf08a).rw(FUNC(squale_state::fdc_sel0_r), FUNC(squale_state::fdc_sel0_w));
	map(0xf08b, 0xf08b).rw(FUNC(squale_state::fdc_sel1_r), FUNC(squale_state::fdc_sel1_w));
}

/* Input ports */
static INPUT_PORTS_START( squale )
	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"\u2191") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) // U+2191 = ↑

	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Del") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(DEL)) // 0x7f
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Ret") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)

	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR(39)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('-') PORT_CHAR('=')

	PORT_START("X4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"\u2190") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) // U+2190 = ←

	PORT_START("X6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"\u2192") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) // U+2192 = →

	PORT_START("X7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"\u2193") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) // U+2193 = ↓

	PORT_START("ay_keys")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Lock") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)

	PORT_START("ay_joy_1")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP)    PORT_CODE(KEYCODE_8_PAD) PORT_CODE(JOYCODE_Y_UP_SWITCH)    PORT_PLAYER(1)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN)  PORT_CODE(KEYCODE_2_PAD) PORT_CODE(JOYCODE_Y_DOWN_SWITCH)  PORT_PLAYER(1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT)  PORT_CODE(KEYCODE_4_PAD) PORT_CODE(JOYCODE_X_LEFT_SWITCH)  PORT_PLAYER(1)
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(JOYCODE_X_RIGHT_SWITCH) PORT_PLAYER(1)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_BUTTON1)        PORT_CODE(KEYCODE_0_PAD) PORT_CODE(JOYCODE_BUTTON1)        PORT_PLAYER(1)

	PORT_START("ay_joy_2")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP)    PORT_CODE(KEYCODE_8_PAD) PORT_CODE(JOYCODE_Y_UP_SWITCH)    PORT_PLAYER(2)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN)  PORT_CODE(KEYCODE_2_PAD) PORT_CODE(JOYCODE_Y_DOWN_SWITCH)  PORT_PLAYER(2)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT)  PORT_CODE(KEYCODE_4_PAD) PORT_CODE(JOYCODE_X_LEFT_SWITCH)  PORT_PLAYER(2)
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(JOYCODE_X_RIGHT_SWITCH) PORT_PLAYER(2)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_BUTTON1)        PORT_CODE(KEYCODE_0_PAD) PORT_CODE(JOYCODE_BUTTON1)        PORT_PLAYER(2)
INPUT_PORTS_END

static void squale_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

void squale_state::machine_start()
{
	fdc_sel0 = 0x00;
	fdc_sel1 = 0x00;

	cart_addr_counter_reset = 0;
	cart_addr_counter = 0x0000;

	m_rom_bank->configure_entries(0, 2, memregion("maincpu")->base(), 0x1000);

	// Generate Squale hardware palette
	for(int i=0;i<8;i++)
	{
		m_ef9365->set_color_entry(i,(((i&4)>>2)^1) * 255,(((i&2)>>1)^1) * 255, ((i&1)^1) * 255 );
	}

	for(int i=0;i<8;i++)
	{
		m_ef9365->set_color_entry(i + 8,(((i&4)>>2)^1) * 127,(((i&2)>>1)^1) * 127, ((i&1)^1) * 127 );
	}
}

void squale_state::machine_reset()
{
	m_rom_bank->set_entry( 0 );
}

void squale_state::squale(machine_config &config)
{
	/* basic machine hardware */
	MC6809(config, m_maincpu, 14_MHz_XTAL/4);
	m_maincpu->set_addrmap(AS_PROGRAM, &squale_state::squale_mem);

	/* Cartridge pia */
	PIA6821(config, m_pia_u72);
	m_pia_u72->readpa_handler().set(FUNC(squale_state::pia_u72_porta_r));
	m_pia_u72->readpb_handler().set(FUNC(squale_state::pia_u72_portb_r));
	m_pia_u72->writepa_handler().set(FUNC(squale_state::pia_u72_porta_w));
	m_pia_u72->writepb_handler().set(FUNC(squale_state::pia_u72_portb_w));
	m_pia_u72->ca2_handler().set(FUNC(squale_state::pia_u72_ca2_w));
	m_pia_u72->cb2_handler().set(FUNC(squale_state::pia_u72_cb2_w));

	/* Keyboard pia */
	PIA6821(config, m_pia_u75);
	m_pia_u75->readpa_handler().set(FUNC(squale_state::pia_u75_porta_r));
	m_pia_u75->readpb_handler().set(FUNC(squale_state::pia_u75_portb_r));
	m_pia_u75->writepa_handler().set(FUNC(squale_state::pia_u75_porta_w));
	m_pia_u75->cb2_handler().set(FUNC(squale_state::pia_u75_cb2_w));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	AY8910(config, m_ay8910, 14_MHz_XTAL/8);
	// TODO : Add port I/O handler
	m_ay8910->port_a_read_callback().set(FUNC(squale_state::ay_porta_r));
	m_ay8910->port_b_read_callback().set(FUNC(squale_state::ay_portb_r));
	m_ay8910->add_route(ALL_OUTPUTS, "mono", 0.50);

	ACIA6850(config, m_acia, 0);

	/* screen */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_screen_update("ef9365", FUNC(ef9365_device::screen_update));
	screen.set_size(256, 256);
	screen.set_visarea(0, 256-1, 0, 256-1);

	PALETTE(config, "palette").set_entries(16);

	EF9365(config, m_ef9365, 14_MHz_XTAL/8);
	m_ef9365->set_palette_tag("palette");
	m_ef9365->set_nb_bitplanes(4);
	m_ef9365->set_display_mode(ef9365_device::DISPLAY_MODE_256x256);
	m_ef9365->irq_handler().set_inputline(m_maincpu, M6809_IRQ_LINE);

	TIMER(config, "squale_sl").configure_scanline(FUNC(squale_state::squale_scanline), "screen", 0, 10);

	/* Floppy */
	WD1770(config, m_fdc, 8_MHz_XTAL);
	FLOPPY_CONNECTOR(config, "wd1770:0", squale_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "wd1770:1", squale_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	//SOFTWARE_LIST(config, "flop525_list").set_original("squale_flop");   // list does not exist

	/* Cartridge slot */
	GENERIC_CARTSLOT(config, "cartslot", generic_linear_slot, "squale_cart").set_device_load(FUNC(squale_state::cart_load));
	SOFTWARE_LIST(config, "cart_list").set_original("squale_cart");
}

/* ROM definition */
ROM_START( squale )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_DEFAULT_BIOS("v201")

	ROM_SYSTEM_BIOS(0, "v201", "Version 2.1")
	ROMX_LOAD( "sqmon_2r1.bin", 0x0000, 0x2000, CRC(ed57c707) SHA1(c8bd33a6fb07fe7f881f2605ad867b7e82366bfc), ROM_BIOS(0) )

	// place ROM v1.2 signature here.
ROM_END

} // anonymous namespace


//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY     FULLNAME  FLAGS
COMP( 1984, squale, 0,      0,      squale,  squale, squale_state, empty_init, "Apollo 7", "Squale", 0 )



ssem.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/*
    Manchester Small-Scale Experimental Machine (SSEM)

    Driver by Ryan Holtz
*/

#include "emu.h"
#include "cpu/ssem/ssem.h"
#include "imagedev/snapquik.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include <sstream>


namespace {

class ssem_state : public driver_device
{
public:
	ssem_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_store(*this, "store"),
		m_screen(*this, "screen")
	{
	}

	void ssem(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(panel_check);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	uint32_t screen_update_ssem(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);
	inline uint32_t reverse(uint32_t v);
	std::string read_line(device_image_interface &image);

	void ssem_map(address_map &map) ATTR_COLD;

	template <typename Format, typename... Params>
	void glyph_print(bitmap_rgb32 &bitmap, int32_t x, int32_t y, Format &&fmt, Params &&...args);

	required_device<ssem_device> m_maincpu;
	required_shared_ptr<uint8_t> m_store;
	required_device<screen_device> m_screen;

	uint8_t m_store_line = 0;

	util::ovectorstream m_glyph_print_buf;
};



/****************************************************\
* General helper functions                           *
\****************************************************/

// The SSEM stores its data, visually, with the leftmost bit corresponding to the least significant bit.
// The de facto snapshot format for other SSEM simulators stores the data physically in that format as well.
// Therefore, in MESS, every 32-bit word has its bits reversed, too, and as a result the values must be
// un-reversed before being used.
inline uint32_t ssem_state::reverse(uint32_t v)
{
	// Taken from http://www.graphics.stanford.edu/~seander/bithacks.html#ReverseParallel
	// swap odd and even bits
	v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1);
	// swap consecutive pairs
	v = ((v >> 2) & 0x33333333) | ((v & 0x33333333) << 2);
	// swap nibbles ...
	v = ((v >> 4) & 0x0F0F0F0F) | ((v & 0x0F0F0F0F) << 4);
	// swap bytes
	v = ((v >> 8) & 0x00FF00FF) | ((v & 0x00FF00FF) << 8);
	// swap 2-byte long pairs
	v = ( v >> 16 ) | ( v << 16);

	return v;
}

/****************************************************\
* Address map                                        *
\****************************************************/

void ssem_state::ssem_map(address_map &map)
{
	map(0x00, 0x7f).ram().share("store");// Primary store
}

/****************************************************\
* Input ports and front panel handling               *
\****************************************************/

enum
{
	// Bit-edit buttons
	PANEL_BIT0,  PANEL_BIT1,  PANEL_BIT2,  PANEL_BIT3,  PANEL_BIT4,  PANEL_BIT5,  PANEL_BIT6,  PANEL_BIT7,
	PANEL_BIT8,  PANEL_BIT9,  PANEL_BIT10, PANEL_BIT11, PANEL_BIT12, PANEL_BIT13, PANEL_BIT14, PANEL_BIT15,
	PANEL_BIT16, PANEL_BIT17, PANEL_BIT18, PANEL_BIT19, PANEL_BIT20, PANEL_BIT21, PANEL_BIT22, PANEL_BIT23,
	PANEL_BIT24, PANEL_BIT25, PANEL_BIT26, PANEL_BIT27, PANEL_BIT28, PANEL_BIT29, PANEL_BIT30, PANEL_BIT31,

	// Page up, page down
	PANEL_PGUP, PANEL_PGDN,

	// Line up, line down
	PANEL_LNUP, PANEL_LNDN,

	// Halt
	PANEL_HALT
};

INPUT_CHANGED_MEMBER(ssem_state::panel_check)
{
	uint8_t edit0_state = ioport("EDIT0")->read();
	uint8_t edit1_state = ioport("EDIT1")->read();
	uint8_t edit2_state = ioport("EDIT2")->read();
	uint8_t edit3_state = ioport("EDIT3")->read();
	uint8_t misc_state = ioport("MISC")->read();

	switch( (int)param )
	{
		case PANEL_BIT0:
			if(edit0_state & 0x01) m_store[(m_store_line << 2) | 0] ^= 0x80;
			break;
		case PANEL_BIT1:
			if(edit0_state & 0x02) m_store[(m_store_line << 2) | 0] ^= 0x40;
			break;
		case PANEL_BIT2:
			if(edit0_state & 0x04) m_store[(m_store_line << 2) | 0] ^= 0x20;
			break;
		case PANEL_BIT3:
			if(edit0_state & 0x08) m_store[(m_store_line << 2) | 0] ^= 0x10;
			break;
		case PANEL_BIT4:
			if(edit0_state & 0x10) m_store[(m_store_line << 2) | 0] ^= 0x08;
			break;
		case PANEL_BIT5:
			if(edit0_state & 0x20) m_store[(m_store_line << 2) | 0] ^= 0x04;
			break;
		case PANEL_BIT6:
			if(edit0_state & 0x40) m_store[(m_store_line << 2) | 0] ^= 0x02;
			break;
		case PANEL_BIT7:
			if(edit0_state & 0x80) m_store[(m_store_line << 2) | 0] ^= 0x01;
			break;
		case PANEL_BIT8:
			if(edit1_state & 0x01) m_store[(m_store_line << 2) | 1] ^= 0x80;
			break;
		case PANEL_BIT9:
			if(edit1_state & 0x02) m_store[(m_store_line << 2) | 1] ^= 0x40;
			break;
		case PANEL_BIT10:
			if(edit1_state & 0x04) m_store[(m_store_line << 2) | 1] ^= 0x20;
			break;
		case PANEL_BIT11:
			if(edit1_state & 0x08) m_store[(m_store_line << 2) | 1] ^= 0x10;
			break;
		case PANEL_BIT12:
			if(edit1_state & 0x10) m_store[(m_store_line << 2) | 1] ^= 0x08;
			break;
		case PANEL_BIT13:
			if(edit1_state & 0x20) m_store[(m_store_line << 2) | 1] ^= 0x04;
			break;
		case PANEL_BIT14:
			if(edit1_state & 0x40) m_store[(m_store_line << 2) | 1] ^= 0x02;
			break;
		case PANEL_BIT15:
			if(edit1_state & 0x80) m_store[(m_store_line << 2) | 1] ^= 0x01;
			break;
		case PANEL_BIT16:
			if(edit2_state & 0x01) m_store[(m_store_line << 2) | 2] ^= 0x80;
			break;
		case PANEL_BIT17:
			if(edit2_state & 0x02) m_store[(m_store_line << 2) | 2] ^= 0x40;
			break;
		case PANEL_BIT18:
			if(edit2_state & 0x04) m_store[(m_store_line << 2) | 2] ^= 0x20;
			break;
		case PANEL_BIT19:
			if(edit2_state & 0x08) m_store[(m_store_line << 2) | 2] ^= 0x10;
			break;
		case PANEL_BIT20:
			if(edit2_state & 0x10) m_store[(m_store_line << 2) | 2] ^= 0x08;
			break;
		case PANEL_BIT21:
			if(edit2_state & 0x20) m_store[(m_store_line << 2) | 2] ^= 0x04;
			break;
		case PANEL_BIT22:
			if(edit2_state & 0x40) m_store[(m_store_line << 2) | 2] ^= 0x02;
			break;
		case PANEL_BIT23:
			if(edit2_state & 0x80) m_store[(m_store_line << 2) | 2] ^= 0x01;
			break;
		case PANEL_BIT24:
			if(edit3_state & 0x01) m_store[(m_store_line << 2) | 3] ^= 0x80;
			break;
		case PANEL_BIT25:
			if(edit3_state & 0x02) m_store[(m_store_line << 2) | 3] ^= 0x40;
			break;
		case PANEL_BIT26:
			if(edit3_state & 0x04) m_store[(m_store_line << 2) | 3] ^= 0x20;
			break;
		case PANEL_BIT27:
			if(edit3_state & 0x08) m_store[(m_store_line << 2) | 3] ^= 0x10;
			break;
		case PANEL_BIT28:
			if(edit3_state & 0x10) m_store[(m_store_line << 2) | 3] ^= 0x08;
			break;
		case PANEL_BIT29:
			if(edit3_state & 0x20) m_store[(m_store_line << 2) | 3] ^= 0x04;
			break;
		case PANEL_BIT30:
			if(edit3_state & 0x40) m_store[(m_store_line << 2) | 3] ^= 0x02;
			break;
		case PANEL_BIT31:
			if(edit3_state & 0x80) m_store[(m_store_line << 2) | 3] ^= 0x01;
			break;
		case PANEL_LNUP:
			if(misc_state & 0x01)
			{
				m_store_line--;
			}
			break;
		case PANEL_LNDN:
			if(misc_state & 0x02)
			{
				m_store_line++;
			}
			break;
		case PANEL_HALT:
			if(misc_state & 0x04)
			{
				m_maincpu->set_state_int(SSEM_HALT, 1 - m_maincpu->state_int(SSEM_HALT));
			}
			break;
	}
}

static INPUT_PORTS_START( ssem )
	PORT_START("EDIT0")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 0")  PORT_CODE(KEYCODE_1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT0)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 1")  PORT_CODE(KEYCODE_2) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT1)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 2")  PORT_CODE(KEYCODE_3) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT2)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 3")  PORT_CODE(KEYCODE_4) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT3)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 4")  PORT_CODE(KEYCODE_5) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT4)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 5")  PORT_CODE(KEYCODE_6) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT5)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 6")  PORT_CODE(KEYCODE_7) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT6)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 7")  PORT_CODE(KEYCODE_8) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT7)

	PORT_START("EDIT1")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 8")  PORT_CODE(KEYCODE_Q) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT8)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 9")  PORT_CODE(KEYCODE_W) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT9)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 10")    PORT_CODE(KEYCODE_E) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT10)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 11")    PORT_CODE(KEYCODE_R) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT11)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 12")    PORT_CODE(KEYCODE_T) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT12)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 13")    PORT_CODE(KEYCODE_Y) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT13)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 14")    PORT_CODE(KEYCODE_U) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT14)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 15")    PORT_CODE(KEYCODE_I) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT15)

	PORT_START("EDIT2")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 16")    PORT_CODE(KEYCODE_A) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT16)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 17")    PORT_CODE(KEYCODE_S) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT17)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 18")    PORT_CODE(KEYCODE_D) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT18)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 19")    PORT_CODE(KEYCODE_F) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT19)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 20")    PORT_CODE(KEYCODE_G) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT20)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 21")    PORT_CODE(KEYCODE_H) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT21)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 22")    PORT_CODE(KEYCODE_J) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT22)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 23")    PORT_CODE(KEYCODE_K) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT23)

	PORT_START("EDIT3")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 24")    PORT_CODE(KEYCODE_Z) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT24)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 25")    PORT_CODE(KEYCODE_X) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT25)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 26")    PORT_CODE(KEYCODE_C) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT26)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 27")    PORT_CODE(KEYCODE_V) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT27)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 28")    PORT_CODE(KEYCODE_B) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT28)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 29")    PORT_CODE(KEYCODE_N) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT29)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 30")    PORT_CODE(KEYCODE_M) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT30)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Bit 31")    PORT_CODE(KEYCODE_COMMA) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_BIT31)

	PORT_START("MISC")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Line Up")   PORT_CODE(KEYCODE_UP)   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_LNUP)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Line Down") PORT_CODE(KEYCODE_DOWN) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_LNDN)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Halt") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssem_state::panel_check), PANEL_HALT)
		PORT_BIT(0xf8, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

/****************************************************\
* Video hardware                                     *
\****************************************************/

static const uint8_t char_glyphs[0x80][8] =
{
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x10, 0x38, 0x10, 0x00, 0x00, 0x00 },
	{ 0x38, 0x7c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x00 },
	{ 0xff, 0xff, 0xef, 0xc7, 0xef, 0xff, 0xff, 0xff },
	{ 0xc7, 0x83, 0x01, 0x01, 0x01, 0x83, 0xc7, 0xff },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00 },
	{ 0x66, 0x66, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00 },
	{ 0x18, 0x3e, 0x60, 0x3c, 0x06, 0x7c, 0x18, 0x00 },
	{ 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00 },
	{ 0x78, 0xcc, 0x78, 0x70, 0xce, 0xcc, 0x7e, 0x00 },
	{ 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x0c, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00 },
	{ 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00 },
	{ 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00 },
	{ 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30 },
	{ 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00 },
	{ 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00 },
	{ 0x3c, 0x66, 0x76, 0x7e, 0x6e, 0x66, 0x3c, 0x00 },
	{ 0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00 },
	{ 0x3c, 0x66, 0x06, 0x0c, 0x30, 0x60, 0x7e, 0x00 },
	{ 0x3c, 0x66, 0x06, 0x0c, 0x06, 0x66, 0x3c, 0x00 },
	{ 0x66, 0x66, 0x66, 0x7e, 0x06, 0x06, 0x06, 0x00 },
	{ 0x7e, 0x60, 0x7c, 0x06, 0x06, 0x66, 0x3c, 0x00 },
	{ 0x3c, 0x66, 0x60, 0x7c, 0x66, 0x66, 0x3c, 0x00 },
	{ 0x7e, 0x66, 0x06, 0x0c, 0x18, 0x18, 0x18, 0x00 },
	{ 0x3c, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x3c, 0x00 },
	{ 0x3c, 0x66, 0x66, 0x3e, 0x06, 0x66, 0x3c, 0x00 },
	{ 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00 },
	{ 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30 },
	{ 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00 },
	{ 0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00 },
	{ 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00 },
	{ 0x3c, 0x66, 0x06, 0x0c, 0x18, 0x00, 0x18, 0x00 },
	{ 0x3c, 0x66, 0x6e, 0x6e, 0x60, 0x66, 0x3c, 0x00 },
	{ 0x3c, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00 },
	{ 0x7c, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x7c, 0x00 },
	{ 0x3c, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3c, 0x00 },
	{ 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00 },
	{ 0x7e, 0x60, 0x60, 0x78, 0x60, 0x60, 0x7e, 0x00 },
	{ 0x7e, 0x60, 0x60, 0x78, 0x60, 0x60, 0x60, 0x00 },
	{ 0x3c, 0x66, 0x60, 0x6e, 0x66, 0x66, 0x3c, 0x00 },
	{ 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00 },
	{ 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00 },
	{ 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00 },
	{ 0x66, 0x6c, 0x78, 0x70, 0x78, 0x6c, 0x66, 0x00 },
	{ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00 },
	{ 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00 },
	{ 0x66, 0x76, 0x7e, 0x7e, 0x6e, 0x66, 0x66, 0x00 },
	{ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00 },
	{ 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x00 },
	{ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x06 },
	{ 0x7c, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x00 },
	{ 0x3c, 0x66, 0x60, 0x3c, 0x06, 0x66, 0x3c, 0x00 },
	{ 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00 },
	{ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00 },
	{ 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00 },
	{ 0xc6, 0xc6, 0xd6, 0xfe, 0xfe, 0xee, 0xc6, 0x00 },
	{ 0x66, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x66, 0x00 },
	{ 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00 },
	{ 0x7e, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00 },
	{ 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00 },
	{ 0x80, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00 },
	{ 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00 },
	{ 0x10, 0x38, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f },
	{ 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x3c, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00 },
	{ 0x7c, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x7c, 0x00 },
	{ 0x3c, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3c, 0x00 },
	{ 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00 },
	{ 0x7e, 0x60, 0x60, 0x78, 0x60, 0x60, 0x7e, 0x00 },
	{ 0x7e, 0x60, 0x60, 0x78, 0x60, 0x60, 0x60, 0x00 },
	{ 0x3c, 0x66, 0x60, 0x6e, 0x66, 0x66, 0x3c, 0x00 },
	{ 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00 },
	{ 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00 },
	{ 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00 },
	{ 0x66, 0x6c, 0x78, 0x70, 0x78, 0x6c, 0x66, 0x00 },
	{ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00 },
	{ 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00 },
	{ 0x66, 0x76, 0x7e, 0x7e, 0x6e, 0x66, 0x66, 0x00 },
	{ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00 },
	{ 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x00 },
	{ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x06 },
	{ 0x7c, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x00 },
	{ 0x3c, 0x66, 0x60, 0x3c, 0x06, 0x66, 0x3c, 0x00 },
	{ 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00 },
	{ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00 },
	{ 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00 },
	{ 0xc6, 0xc6, 0xd6, 0xfe, 0xfe, 0xee, 0xc6, 0x00 },
	{ 0x66, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x66, 0x00 },
	{ 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00 },
	{ 0x7e, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00 },
	{ 0x0c, 0x18, 0x18, 0x30, 0x18, 0x18, 0x0c, 0x00 },
	{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00 },
	{ 0x30, 0x18, 0x18, 0x0c, 0x18, 0x18, 0x30, 0x00 },
	{ 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00 },
	{ 0xff, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xff },
};

template <typename Format, typename... Params>
void ssem_state::glyph_print(bitmap_rgb32 &bitmap, int32_t x, int32_t y, Format &&fmt, Params &&...args)
{
	const rectangle &visarea = m_screen->visible_area();

	m_glyph_print_buf.clear();
	m_glyph_print_buf.seekp(0, util::ovectorstream::beg);
	util::stream_format(m_glyph_print_buf, std::forward<Format>(fmt), std::forward<Params>(args)...);
	m_glyph_print_buf.put('\0');

	for(char const *buf = &m_glyph_print_buf.vec()[0]; *buf; buf++)
	{
		uint8_t cur = uint8_t(*buf);
		if(cur < 0x80)
		{
			int32_t line = 0;
			for(line = 0; line < 8; line++)
			{
				uint32_t *const d = &bitmap.pix(y + line);
				for(uint32_t bit = 0; bit < 8; bit++)
				{
					if(char_glyphs[cur][line] & (1 << (7 - bit)))
					{
						d[x+bit] = 0xffffffff;
					}
					else
					{
						d[x+bit] = 0;
					}
				}
			}
		}

		x += 8;
		if(x >= visarea.max_x)
		{
			x = 0;
			y += 8;
			if(y >= visarea.max_y)
			{
				y = 0;
			}
		}
	}
}

uint32_t ssem_state::screen_update_ssem(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	uint32_t line = 0;
	uint32_t accum = m_maincpu->state_int(SSEM_A);
	uint32_t bit = 0;
	uint32_t word = 0;

	for(line = 0; line < 32; line++)
	{
		word = (m_store[(line << 2) | 0] << 24) |
				(m_store[(line << 2) | 1] << 16) |
				(m_store[(line << 2) | 2] <<  8) |
				(m_store[(line << 2) | 3] <<  0);
		for(bit = 0; bit < 32; bit++)
		{
			if(word & (1 << (31 - bit)))
			{
				glyph_print(bitmap, bit << 3, line << 3, "%c", line == m_store_line ? 4 : 2);
			}
			else
			{
				glyph_print(bitmap, bit << 3, line << 3, "%c", line == m_store_line ? 3 : 1);
			}
		}
	}

	for(bit = 0; bit < 32; bit++)
	{
		if(accum & (1 << bit))
		{
			glyph_print(bitmap, bit << 3, 264, "%c", 2);
		}
		else
		{
			glyph_print(bitmap, bit << 3, 264, "%c", 1);
		}
	}

	word = reverse((m_store[(m_store_line << 2) | 0] << 24) |
					(m_store[(m_store_line << 2) | 1] << 16) |
					(m_store[(m_store_line << 2) | 2] <<  8) |
					(m_store[(m_store_line << 2) | 3] <<  0));
	glyph_print(bitmap, 0, 272, "LINE:%02u  VALUE:%08x  HALT:%d", m_store_line, word, m_maincpu->state_int(SSEM_HALT));
	return 0;
}

/****************************************************\
* Image helper functions                             *
\****************************************************/

std::string ssem_state::read_line(device_image_interface &image)
{
	std::ostringstream stream;
	for (u8 i = 0; i < 100; i++)
	{
		char c = 0;
		if (image.fread(&c, 1) != 1)
			break;
		// convert opcode to lower case
		if (c >= 'A' && c <= 'Z')
			c += 32;
		if (c >= 32)
			stream << c;
		else
		{
			image.fread(&c, 1); // skip LF
			break;
		}
	}
	return std::move(stream).str();
}


/****************************************************\
* Image loading                                      *
\****************************************************/

QUICKLOAD_LOAD_MEMBER(ssem_state::quickload_cb)
{
	address_space &space = m_maincpu->space(AS_PROGRAM);
	std::string buffer;
	u32 num_lines = 0;

	std::string image_line = read_line(image);
	if (image_line.empty())
		return std::make_pair(image_error::INVALIDIMAGE, "Invalid image file: no data in line 1");

	sscanf(image_line.c_str(), "%d", &num_lines);  //num_lines = std::stoul(image_line);

	if (num_lines < 1)
		return std::make_pair(image_error::INVALIDIMAGE, "Invalid image file: no data to process");

	for (u32 i = 0; i < num_lines; i++)
	{
		u32 line = 0, word = 0;
		image_line = read_line(image);
		u32 length = image_line.length();

		if (length < 8)
		{
			return std::make_pair(
					image_error::INVALIDIMAGE,
					util::string_format("Bad data (%s) in line %d", image_line, i + 2));
		}

		// Isolate and convert 4-digit decimal address
		buffer = image_line.substr(0, 4);
		sscanf(buffer.c_str(), "%04u", &line);

		if (image.is_filetype("snp"))
		{
			if (length < 37)
			{
				return std::make_pair(
						image_error::INVALIDIMAGE,
						util::string_format("Bad data (%s) in line %d", image_line, i + 2));
			}

			// Parse a line such as: 0000:00000110101001000100000100000100
			for (u8 b = 0; b < 32; b++)
				if (image_line[5 + b] == '1')
					word |= 1 << (31 - b);
		}
		else
		if (image.is_filetype("asm"))
		{
			int32_t value = 0;
			uint32_t unsigned_value = 0;

			// Isolate the value
			if (length > 8)
			{
				buffer = image_line.substr(9);
				sscanf(buffer.c_str(), "%d", &value);
				unsigned_value = reverse((uint32_t)value);
			}

			// Isolate the opcode
			buffer = image_line.substr(5, 3);

			if (buffer == "num")
				word = unsigned_value;
			else if (buffer == "jmp")
				word = 0x00000000 | unsigned_value ;
			else if (buffer == "jrp")
				word = 0x00040000 | unsigned_value;
			else if (buffer == "ldn")
				word = 0x00020000 | unsigned_value;
			else if (buffer == "sto")
				word = 0x00060000 | unsigned_value;
			else if (buffer == "sub")
				word = 0x00010000 | unsigned_value;
			else if (buffer == "cmp")
				word = 0x00030000 | unsigned_value;
			else if (buffer == "stp")
				word = 0x00070000 | unsigned_value;
			else
				logerror("Unknown opcode (%s) in line %d\n",buffer,i+2);
		}

		space.write_byte((line << 2) + 0, BIT(word, 24, 8));
		space.write_byte((line << 2) + 1, BIT(word, 16, 8));
		space.write_byte((line << 2) + 2, BIT(word, 8,  8));
		space.write_byte((line << 2) + 3, BIT(word, 0,  8));
	}

	return std::make_pair(std::error_condition(), std::string());
}

/****************************************************\
* Machine definition                                 *
\****************************************************/

void ssem_state::machine_start()
{
	save_item(NAME(m_store_line));
	m_glyph_print_buf.reserve(1024);
}

void ssem_state::machine_reset()
{
	m_store_line = 0;
}

void ssem_state::ssem(machine_config &config)
{
	/* basic machine hardware */
	SSEMCPU(config, m_maincpu, 700);
	m_maincpu->set_addrmap(AS_PROGRAM, &ssem_state::ssem_map);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(0));
	m_screen->set_size(256, 280);
	m_screen->set_visarea(0, 255, 0, 279);
	m_screen->set_screen_update(FUNC(ssem_state::screen_update_ssem));
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* quickload */
	quickload_image_device &quickload(QUICKLOAD(config, "quickload", "snp,asm", attotime::from_seconds(1)));
	quickload.set_load_callback(FUNC(ssem_state::quickload_cb));
	quickload.set_interface("ssemquik");
	SOFTWARE_LIST(config, "quik_list").set_original("ssem_quik");
}


ROM_START( ssem )
	ROM_REGION( 0x80, "maincpu", ROMREGION_ERASE00 )  /* Main Store */
ROM_END

} // anonymous namespace


//   YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY                  FULLNAME
COMP(1948, ssem, 0,      0,      ssem,    ssem,  ssem_state, empty_init, "Manchester University", "Small-Scale Experimental Machine (SSEM), 'Baby'", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE)



ssystem3.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

SciSys / Novag Chess Champion: Super System III (aka MK III)

It was distributed by both SciSys and Novag. Which company was responsible for
which part of the production chain is unknown. The copyright was assigned to SciSys
(no mention of Novag in the ROM, it has "COPYRIGHT SCISYS LTD 1979").

This is their 1st original product. MK II was licensed from Peter Jennings, and
MK I was, to put it bluntly, a bootleg. The chess engine is by Mike Johnson, with
support from David Levy, Philidor Software.

Hardware notes:

Master Unit:
- PCB label: 201041 (Rev.A to Rev.E)
- Synertek 6502A @ 2MHz (4MHz XTAL)
- Synertek 6522 VIA
- 8KB ROM (2*Synertek 2332)
- 1KB RAM (2*HM472114P-3)
- MD4332BE or HLCD0438 + a bunch of TTL for the LCD
- 13 buttons, 4 switches, no leds or sensorboard
- connectors for: PSU, Power Pack, Chess Unit, Printer Unit

Chess Unit:
- PCB label: Radofin XM-2057-0C
- Fairchild F6808P CPU @ ~6.8MHz (M6808 compatible)
- Fairchild F6821P PIA
- 2KB ROM(2316), 128x8 RAM(F6810P)
- 2*HLCD0438, chessboard LCD

Printer Unit:
- unknown hardware, assume own CPU like the chess unit

PSU ("permanent storage unit"?) is just a 256x4 battery-backed RAM (TC5501P)
module, not sure why it was so expensive (~180DM).

A chess clock accessory was also announced but unreleased.

SciSys Super System IV (AKA MK IV) is on similar hardware. It was supposed to
be a modular chesscomputer, not only with accessory hardware like MK III, but
also a module slot for the program. The box mentions other modules, such as a
reversi program called "The Moor". The chesscomputer was discontinued soon after
release, and none of the accessories or other games came out.

TODO:
- 2nd 7474 /2 clock divider on each 4000-7fff access, this also applies to 6522 clock
  (doesn't affect chess calculation speed, only I/O access, eg. beeper pitch).
  Should be doable to add, but 6522 device doesn't support live clock changes.
- LCD TC pin? connects to the display, source is a 50hz timer(from power supply),
  probably to keep refreshing the LCD when inactive, there is no need to emulate it
- dump/add printer unit
- dump/add other ssystem3 program revisions, were the BTANB fixed in the 1980 version?
  known undumped: C19081 + C19082 (instead of C19081E), C45000 + C45012
- ssystem4 softwarelist if a prototype cartridge is ever dumped

BTANB (ssystem3):
- If the TIME switch is held up, it will sometimes recognize the wrong input when
  another button is pressed. I assume they noticed this bug too late and tried to
  lessen the chance by adding a spring to the switch.
- Similar to the TIME switch bug, pressing 2 buttons simultaneously can cause it
  to malfunction, eg. press A+CE or C+CE and an "8" appears in the display.
- chess unit screen briefly flickers at power-on and when the subcpu receives an
  NMI in the middle of updating the LCD, it is mentioned in the manual

*******************************************************************************/

#include "emu.h"

#include "cpu/m6502/m6502.h"
#include "cpu/m6800/m6800.h"
#include "machine/6522via.h"
#include "machine/6821pia.h"
#include "machine/nvram.h"
#include "sound/dac.h"
#include "video/hlcd0438.h"
#include "video/md4330b.h"
#include "video/pwm.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "saitek_ssystem3.lh"
#include "saitek_ssystem4.lh"


namespace {

class ssystem3_state : public driver_device
{
public:
	ssystem3_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_subcpu(*this, "subcpu"),
		m_via(*this, "via"),
		m_pia(*this, "pia"),
		m_lcd1(*this, "lcd1"),
		m_lcd2(*this, "lcd2_%u", 0),
		m_display(*this, "display%u", 0),
		m_dac(*this, "dac"),
		m_nvram(*this, "nvram"),
		m_inputs(*this, "IN.%u", 0),
		m_out_lcd2(*this, "s%u.%u", 0U, 0U)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(cu_plug);

	// machine configs
	void ssystem3(machine_config &config);
	void ssystem4(machine_config &config);

	void init_ssystem3() { m_xor_kludge = true; }

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	optional_device<cpu_device> m_subcpu;
	required_device<via6522_device> m_via;
	optional_device<pia6821_device> m_pia;
	required_device<md4332b_device> m_lcd1;
	optional_device_array<hlcd0438_device, 2> m_lcd2;
	optional_device_array<pwm_display_device, 2> m_display;
	required_device<dac_1bit_device> m_dac;
	optional_shared_ptr<u8> m_nvram;
	optional_ioport_array<4+3> m_inputs;
	output_finder<8, 48> m_out_lcd2;

	u8 m_inp_mux = 0;
	u8 m_control = 0;
	u8 m_shift = 0;
	u32 m_lcd1_data = 0;
	u64 m_lcd2_data = 0;
	u8 m_lcd2_select = 0;

	bool m_xor_kludge = false;

	// address maps
	void ssystem3_map(address_map &map) ATTR_COLD;
	void ssystem4_map(address_map &map) ATTR_COLD;
	void chessunit_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void lcd1_output_w(u32 data) { m_lcd1_data = data; }
	void input_w(u8 data);
	u8 input_r();
	void control_w(u8 data);
	u8 control_r();

	void nvram_w(offs_t offset, u8 data);
	u8 nvram_r(offs_t offset);

	void lcd2_pwm_w(offs_t offset, u8 data);
	void lcd2_update();
	template<int N> void lcd2_output_w(offs_t offset, u32 data);
	void cu_pia_a_w(u8 data);
	u8 cu_pia_a_r();
	void cu_pia_b_w(u8 data);
	u8 cu_pia_b_r();
};

void ssystem3_state::machine_start()
{
	m_out_lcd2.resolve();

	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_control));
	save_item(NAME(m_shift));
	save_item(NAME(m_lcd1_data));
	save_item(NAME(m_lcd2_data));
	save_item(NAME(m_lcd2_select));
}



/*******************************************************************************
    I/O
*******************************************************************************/

// Master Unit

void ssystem3_state::input_w(u8 data)
{
	// PA0-PA7: input mux
	m_inp_mux = ~data;
}

u8 ssystem3_state::input_r()
{
	u8 data = m_inp_mux;

	// PA1-PA3: multiplexed inputs from PA4-PA7
	// PA0: blocked by diodes
	for (int i = 0; i < 4; i++)
		if (BIT(m_inp_mux, i+4))
			data |= m_inputs[i]->read() & 0xe;

	// PA4-PA7: multiplexed inputs from PA0-PA3
	for (int i = 0; i < 4; i++)
		if (m_inp_mux & m_inputs[i]->read())
			data |= 1 << (i+4);

	// PA5-PA7: freq sel from _PA0
	if (~m_inp_mux & 1)
		data |= m_inputs[5]->read() & 0xe0;

	return ~data;
}

void ssystem3_state::control_w(u8 data)
{
	// PB0: speaker out
	m_dac->write(~data & m_inputs[4]->read() & 1);

	// PB1: LCD DI
	// PB2: LCD CLK
	m_lcd1->di_w(BIT(data, 1));
	m_lcd1->clk_w(BIT(data, 2));

	// PB2 also clocks a 4015B
	// DA: LCD DO, DB: Q3A
	if (data & ~m_control & 4)
	{
		m_shift = m_shift << 1 | m_lcd1->do_r();

		// weird TTL maze, I assume it's a hw kludge to fix a bug after the maskroms were already manufactured
		u8 xorval = m_xor_kludge && (BIT(m_shift, 3) & ~(BIT(m_shift, 1) ^ BIT(m_shift, 4)) & ~(BIT(m_lcd1_data, 7) & BIT(m_lcd1_data, 23))) ? 0x12 : 0;

		// update display
		for (int i = 0; i < 4; i++)
			m_display[0]->write_row(i, m_lcd1_data >> (8*i) & 0xff);
		m_display[0]->write_row(4, (m_shift ^ xorval) | 0x100);
	}

	// PB3: device serial out
	if (m_inputs[5]->read() & 2)
		m_pia->ca1_w(BIT(~data, 3));

	// PB7: tied to PB6 (pulse timer 2)
	m_via->write_pb6(BIT(data, 7));

	m_control = data;
}

u8 ssystem3_state::control_r()
{
	u8 data = 0;

	// PB4: device busy (unused on chess unit)
	// PB5: device attached
	if (m_inputs[5]->read() & 2)
		data ^= 0x30;

	return ~data;
}


// PSU

void ssystem3_state::nvram_w(offs_t offset, u8 data)
{
	// nvram is only d0-d3
	if (m_inputs[5]->read() & 1)
		m_nvram[offset] = data & 0x0f;
}

u8 ssystem3_state::nvram_r(offs_t offset)
{
	return (m_inputs[5]->read() & 1) ? (m_nvram[offset] & 0x0f) : 0;
}


// Chess Unit

INPUT_CHANGED_MEMBER(ssystem3_state::cu_plug)
{
	m_subcpu->set_input_line(INPUT_LINE_RESET, newval ? CLEAR_LINE : ASSERT_LINE);

	if (newval)
		m_pia->reset();
	else
		m_display[1]->clear();
}

void ssystem3_state::lcd2_pwm_w(offs_t offset, u8 data)
{
	m_out_lcd2[offset & 0x3f][offset >> 6] = data;
}

void ssystem3_state::lcd2_update()
{
	if (m_inputs[5]->read() & 2)
		m_display[1]->matrix(1 << m_lcd2_select, m_lcd2_data);
}

template<int N>
void ssystem3_state::lcd2_output_w(offs_t offset, u32 data)
{
	if (!offset)
		data = 0;

	m_lcd2_data = u64(data) << 32 | m_lcd2_data >> 32;

	if constexpr (N == 1)
		lcd2_update();
}

void ssystem3_state::cu_pia_a_w(u8 data)
{
	// PA0-PA2: CD4051 to LCD column
	m_lcd2_select = data & 7;
	lcd2_update();
}

u8 ssystem3_state::cu_pia_a_r()
{
	// PA7: serial data in
	return ~m_control << 4 & 0x80;
}

void ssystem3_state::cu_pia_b_w(u8 data)
{
	// PB3: LCD LOAD (both)
	// PB4: LCD LCD (both) + CD4051 COM OUT/IN
	// PB6: LCD CLOCK (both)
	// PB7: LCD DATA IN (1st)
	m_lcd2[0]->data_w(BIT(data, 7));

	for (int i = 0; i < 2; i++)
		m_lcd2[i]->clock_w(BIT(data, 6));

	for (int i = 0; i < 2; i++)
		m_lcd2[i]->load_w(BIT(~data, 3));

	for (int i = 0; i < 2; i++)
		m_lcd2[i]->lcd_w(BIT(data, 4));
}

u8 ssystem3_state::cu_pia_b_r()
{
	// PB5: look switch
	return m_inputs[6]->read() << 5 & 0x20;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void ssystem3_state::ssystem3_map(address_map &map)
{
	map(0x0000, 0x03ff).mirror(0x3c00).ram();
	map(0x4000, 0x40ff).mirror(0x1f00).ram().rw(FUNC(ssystem3_state::nvram_r), FUNC(ssystem3_state::nvram_w)).share("nvram");
	map(0x6000, 0x600f).mirror(0x1ff0).m(m_via, FUNC(via6522_device::map));
	map(0x8000, 0x9fff).mirror(0x6000).rom();
}

void ssystem3_state::ssystem4_map(address_map &map)
{
	map(0x0000, 0x03ff).ram();
	map(0x4400, 0x440f).m(m_via, FUNC(via6522_device::map));
	map(0x4800, 0x48ff).noprw(); // no nvram
	map(0xd000, 0xffff).rom();
}

void ssystem3_state::chessunit_map(address_map &map)
{
	map(0x3000, 0x307f).mirror(0x0f80).ram();
	map(0x4000, 0x47ff).mirror(0xb800).rom();
	map(0x8000, 0x8003).mirror(0x3ffc).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( ssystem4 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9 / EP / C.Square")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0 / MD / C.Board")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_F) PORT_NAME("F 6 / Knight / Clock")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_E) PORT_CODE(KEYCODE_LEFT) PORT_NAME("E 5 / Bishop / Left")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_I) PORT_NAME("CE / Interrupt")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("G 7 / Pawn / Right")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_D) PORT_NAME("D 4 / Rook / #")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_A) PORT_NAME("A 1 / White")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_TOGGLE PORT_CODE(KEYCODE_T) PORT_NAME("Time")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_H) PORT_NAME("H 8 / Black")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_C) PORT_NAME("C 3 / Queen / #50")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_B) PORT_NAME("B 2 / King / FP")

	PORT_START("IN.4") // switches
	PORT_CONFNAME( 0x01, 0x01, "Sound" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
	PORT_CONFNAME( 0x02, 0x02, "LCD 1 Light" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x02, DEF_STR( On ) )

	PORT_START("IN.5") // accessories/diodes
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( ssystem3 )
	PORT_INCLUDE( ssystem4 )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9 / EP / C SQ")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0 / MD / C Board")

	PORT_MODIFY("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Time") // spring-loaded

	PORT_MODIFY("IN.4") // switches
	PORT_CONFNAME( 0x04, 0x04, "LCD 2 Light" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x04, DEF_STR( On ) )

	PORT_MODIFY("IN.5") // accessories/diodes
	PORT_CONFNAME( 0x01, 0x01, "Memory Unit" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
	PORT_CONFNAME( 0x02, 0x02, "Chess Unit" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ssystem3_state::cu_plug), 0)
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x02, DEF_STR( On ) )
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_CUSTOM)

	PORT_START("IN.6") // chess unit
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_TOGGLE PORT_CODE(KEYCODE_O) PORT_NAME("Look")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void ssystem3_state::ssystem4(machine_config &config)
{
	// basic machine hardware
	M6502(config, m_maincpu, 4_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &ssystem3_state::ssystem4_map);

	MOS6522(config, m_via, 4_MHz_XTAL / 2);
	m_via->writepa_handler().set(FUNC(ssystem3_state::input_w));
	m_via->readpa_handler().set(FUNC(ssystem3_state::input_r));
	m_via->writepb_handler().set(FUNC(ssystem3_state::control_w));
	m_via->readpb_handler().set(FUNC(ssystem3_state::control_r));

	// video hardware
	MD4332B(config, m_lcd1);
	m_lcd1->write_q().set(FUNC(ssystem3_state::lcd1_output_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1920/2, 729/2);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display[0]).set_size(5, 9);
	m_display[0]->set_bri_levels(0.25);

	config.set_default_layout(layout_saitek_ssystem4);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void ssystem3_state::ssystem3(machine_config &config)
{
	ssystem4(config);

	// basic machine hardware
	m_maincpu->set_addrmap(AS_PROGRAM, &ssystem3_state::ssystem3_map);

	M6808(config, m_subcpu, 6'800'000); // LC circuit, measured
	m_subcpu->set_addrmap(AS_PROGRAM, &ssystem3_state::chessunit_map);

	config.set_perfect_quantum(m_maincpu);

	PIA6821(config, m_pia);
	m_pia->irqa_handler().set_inputline(m_subcpu, INPUT_LINE_NMI);
	m_pia->writepa_handler().set(FUNC(ssystem3_state::cu_pia_a_w));
	m_pia->readpa_handler().set(FUNC(ssystem3_state::cu_pia_a_r));
	m_pia->writepb_handler().set(FUNC(ssystem3_state::cu_pia_b_w));
	m_pia->readpb_handler().set(FUNC(ssystem3_state::cu_pia_b_r));
	m_pia->cb2_handler().set(m_pia, FUNC(pia6821_device::cb1_w));
	m_pia->cb2_handler().append(m_pia, FUNC(pia6821_device::ca2_w));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	// video hardware
	HLCD0438(config, m_lcd2[0], 0);
	m_lcd2[0]->write_segs().set(FUNC(ssystem3_state::lcd2_output_w<0>));
	m_lcd2[0]->write_data().set(m_lcd2[1], FUNC(hlcd0438_device::data_w));

	HLCD0438(config, m_lcd2[1], 0);
	m_lcd2[1]->write_segs().set(FUNC(ssystem3_state::lcd2_output_w<1>));

	screen_device &screen(SCREEN(config, "chessunit", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1060/1.5, 1080/1.5);
	screen.set_visarea_full();

	PWM_DISPLAY(config, m_display[1]).set_size(8, 48);
	m_display[1]->output_x().set(FUNC(ssystem3_state::lcd2_pwm_w));

	m_display[0]->set_segmask(0xf, 0x7f); // 7segs are at expected positions
	config.set_default_layout(layout_saitek_ssystem3);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( ssystem3 )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD("c19081e_ss-3-lrom.u4", 0x8000, 0x1000, CRC(9ea46ed3) SHA1(34eef85b356efbea6ddac1d1705b104fc8e2731a) ) // 2332
	ROM_LOAD("c19082_ss-3-hrom.u5",  0x9000, 0x1000, CRC(52741e0b) SHA1(2a7b950f9810c5a14a1b9d5e6b2bd93da621662e) ) // "

	ROM_REGION(0x10000, "subcpu", 0)
	ROM_LOAD("c28a97m_ss-3l-rom", 0x4000, 0x0800, CRC(bf0b2a84) SHA1(286f56aca2e50b78ac1fae4a89413659aceb71d9) ) // 2316

	ROM_REGION(0x100, "nvram", 0) // default settings
	ROM_LOAD("nvram", 0, 0x100, CRC(b5dddc7b) SHA1(3be9ec8359cc9ef16a04f28dfd24f9ffe1a2fca9) )

	ROM_REGION(53552, "screen", 0)
	ROM_LOAD("ssystem3.svg", 0, 53552, CRC(6047f88f) SHA1(2ff9cfce01cd3811a3f46f84b47fdc4ea2cf2ba8) )

	ROM_REGION(748939, "chessunit", 0)
	ROM_LOAD("chessunit.svg", 0, 748939, CRC(713a46fd) SHA1(6119162fb7c00f81aeca0dfe274475dc8575dd70) )
ROM_END

ROM_START( ssystem4 )
	ROM_REGION(0x10000, "maincpu", 0) // roms in a cartridge
	ROM_LOAD("c45021_ss4-lrom", 0xd000, 0x1000, CRC(fc86a4fc) SHA1(ee292925165d4bf7b948c60a81d95f7a4064e797) ) // 2332
	ROM_LOAD("c45022_ss4-mrom", 0xe000, 0x1000, CRC(c6110af1) SHA1(4b63454a23b2fe6b5c8f3fa6718eb49770cb6907) ) // "
	ROM_LOAD("c45023_ss4-hrom", 0xf000, 0x1000, CRC(ab4a4343) SHA1(6eeee7168e13dc1115cb5833f1938a8ea8c01d69) ) // "

	ROM_REGION(53552, "screen", 0) // looks same, but different pinout
	ROM_LOAD("ssystem4.svg", 0, 53552, CRC(b69b12e3) SHA1(c2e39d015397d403309f1c23619fe8abc3745d87) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT           COMPANY, FULLNAME, FLAGS
SYST( 1979, ssystem3, 0,      0,      ssystem3, ssystem3, ssystem3_state, init_ssystem3, "SciSys / Novag Industries / Philidor Software", "Chess Champion: Super System III", MACHINE_SUPPORTS_SAVE )

SYST( 1980, ssystem4, 0,      0,      ssystem4, ssystem4, ssystem3_state, empty_init,    "SciSys / Philidor Software", "Chess Champion: Super System IV", MACHINE_SUPPORTS_SAVE )



st17xx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Justin Kerk
/***************************************************************************

    Saturn ST-17xx series DVD players

    Skeleton driver.

****************************************************************************/

/*

    TODO:

    - everything

    Technical info:
    DVD-player-on-a-chip designs from Mediatek:
    http://www.mediatek.com/en/products/home-entertainment/consumer-dvd-blu-ray/

    MT1379:
    http://pdf.datasheetcatalog.com/datasheets/134/477326_DS.pdf

    MT1389:
    http://newage.mpeg4-players.info/mt1389/mt1389.html
    http://groups.yahoo.com/group/MEDIATEK1389/
    http://www.sigmatek-xm600.borec.cz/chip.html
    http://www.sigmatek-xm600.borec.cz/Info%20-%20MT1389%20v0.3b%20English.doc (rename to .rtf)
    Includes an 8032, ARM7TDMI, and unknown DSP

    SUNPLUS SPHE8200A:
    http://pdf.datasheetcatalog.com/datasheets/2300/499420_DS.pdf
    https://004code.googlecode.com/svn/trunk/h/regmapo_8200.h
    "32-bit RISC" architecture, but which one?

*/

#include "emu.h"
#include "cpu/arm7/arm7.h"
#include "emupal.h"
#include "screen.h"


namespace {

#define SCREEN_TAG "screen"

class st17xx_state : public driver_device
{
public:
	st17xx_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag) { }

	void st17xx(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;

	virtual void video_start() override ATTR_COLD;
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void cpu_map(address_map &map) ATTR_COLD;
};

/* Memory Maps */

void st17xx_state::cpu_map(address_map &map)
{
	map(0x00000000, 0x000fffff).rom();
}

/* Input Ports */

static INPUT_PORTS_START( st17xx )
INPUT_PORTS_END

/* Video */

void st17xx_state::video_start()
{
}

uint32_t st17xx_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

/* Machine Initialization */

void st17xx_state::machine_start()
{
}

/* Machine Driver */

void st17xx_state::st17xx(machine_config &config)
{
	/* basic machine hardware */
	arm7_cpu_device &maincpu(ARM7(config, "maincpu", 50000000)); /* speed unknown */
	maincpu.set_addrmap(AS_PROGRAM, &st17xx_state::cpu_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(st17xx_state::screen_update));
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);

	PALETTE(config, "palette").set_entries(64);
}

/* ROMs */

/*
Uses MT1379 DVD player chip. There are two versions - with outputs for headphones and karaoke.
Reads: AudioCD, VCD, MP3, Pictures Kodak, Xvid
*/
ROM_START( st1700h )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD32_DWORD( "st1700headphones.bin", 0x000000, 0x100000, CRC(c219e9df) SHA1(6769bcd2c6b19a2cd4e0e36f01824508a7342e4e) )
ROM_END

ROM_START( st1701 )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD32_DWORD( "st1701.bin", 0x000000, 0x100000, CRC(e6c08dae) SHA1(00ed616fc4c7955be036ca739d9c34038c0ecd58) )
ROM_END

ROM_START( st1702 )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD32_DWORD( "st1702.bin", 0x000000, 0x0f67dc, CRC(ca5a1a97) SHA1(21a0632cff4bcb25f9a5f7a4e9bd8aeaa9f715c3) )
ROM_END

ROM_START( st1703 )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD32_DWORD( "st1703.bin", 0x000000, 0x100000, CRC(04963c10) SHA1(ef7eb45de2fd6dab826c362939ea67370d9bd84b) )
ROM_END

/*
Uses MT1389 DVD player chip.
Reads: Audio CD, VCD, MP3, MPEG4, Pictures Kodak, Xvid
*/
ROM_START( st1704 )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD32_DWORD( "st-1704.bin", 0x000000, 0x0feef0, CRC(b834d37e) SHA1(d048b793c2c838f07fdae9592b07fd4fb2ec73b8) )
ROM_END

ROM_START( st1705 )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD32_DWORD( "st1705.bin", 0x000000, 0x100000, CRC(efda2beb) SHA1(d30505552fc9ffd37ac12f576d792212deb10d84) )
ROM_END

/*
SATURN
ST 1706, 1707, 1708.
Chip: SUNPLUS SPHE 8200A
Servo: SUNPLUS SPHE 6300A
Flash: EN29F040A-70P or ID39F040-70P
Eeprom: 24C02
Driver: CD5954CB
Indication & Key processor : CS16312EN
Laser head: CSQCL33EI38 (requires clarification)
*/
ROM_START( st1706 )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD32_DWORD( "1706_sphe8200.bin", 0x000000, 0x080000, CRC(6a92fa45) SHA1(d40327ebfacd0c3690b170f802d8059e22848aa0) )
ROM_END

ROM_START( st1707 )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD32_DWORD( "1707_sphe8200.bin", 0x000000, 0x080000, CRC(4d90d176) SHA1(7058b37413c90fd9e7f845944191f7ebe9e03250) )
ROM_END

ROM_START( st1708 )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD32_DWORD( "1708_sphe8200.bin", 0x000000, 0x080000, CRC(5176c819) SHA1(2f1ae3389380be27fdd6a66da119e3ccdaa2fd59) )
ROM_END

ROM_START( st1714 )
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_LOAD32_DWORD( "st1714.bin", 0x000000, 0x200000, CRC(08fc0a1b) SHA1(74dfd5595e1ab45fb9aff50a6c365fd9c9b33c33) )
ROM_END

} // anonymous namespace


/* System Drivers */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS          INIT        COMPANY   FULLNAME                       FLAGS
CONS( 200?, st1700h, 0,      0,      st17xx,  st17xx, st17xx_state,  empty_init, "Saturn", "ST-1700 (headphone version)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 200?, st1701,  0,      0,      st17xx,  st17xx, st17xx_state,  empty_init, "Saturn", "ST-1701",                     MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 200?, st1702,  0,      0,      st17xx,  st17xx, st17xx_state,  empty_init, "Saturn", "ST-1702",                     MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 200?, st1703,  0,      0,      st17xx,  st17xx, st17xx_state,  empty_init, "Saturn", "ST-1703",                     MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 200?, st1704,  0,      0,      st17xx,  st17xx, st17xx_state,  empty_init, "Saturn", "ST-1704",                     MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 200?, st1705,  0,      0,      st17xx,  st17xx, st17xx_state,  empty_init, "Saturn", "ST-1705",                     MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 200?, st1706,  0,      0,      st17xx,  st17xx, st17xx_state,  empty_init, "Saturn", "ST-1706",                     MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 200?, st1707,  0,      0,      st17xx,  st17xx, st17xx_state,  empty_init, "Saturn", "ST-1707",                     MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 200?, st1708,  0,      0,      st17xx,  st17xx, st17xx_state,  empty_init, "Saturn", "ST-1708",                     MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 200?, st1714,  0,      0,      st17xx,  st17xx, st17xx_state,  empty_init, "Saturn", "ST-1714",                     MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



st2302u_bbl_rom.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood, AJR

#include "emu.h"
#include "cpu/m6502/st2205u.h"
#include "bl_handhelds_lcdc.h"
#include "screen.h"
#include "speaker.h"


namespace {

class st22xx_bbl338_state : public driver_device
{
public:
	st22xx_bbl338_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_lcdc(*this, "lcdc")
		, m_input_matrix(*this, "IN%u", 1U)
		, m_portb(0xff)
	{
	}

	void st22xx_bbl338(machine_config &config);
	void st22xx_dphh8213(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

	required_device<st2205u_base_device> m_maincpu;

private:
	u8 porta_r();
	void portb_w(u8 data);
	void porta_w(u8 data);

	void st22xx_bbl338_map(address_map &map) ATTR_COLD;
	void st22xx_dphh8213_map(address_map &map) ATTR_COLD;

	required_device<screen_device> m_screen;
	required_device<bl_handhelds_lcdc_device> m_lcdc;
	required_ioport_array<4> m_input_matrix;

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	u8 m_portb;
};

class st22xx_bbl338_sim_state : public st22xx_bbl338_state
{
public:
	st22xx_bbl338_sim_state(const machine_config& mconfig, device_type type, const char* tag)
		: st22xx_bbl338_state(mconfig, type, tag)
	{
	}

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u8 sim152_r();
	void sim152_w(u8 data);
	u8 m_152_dat = 0;
};

u8 st22xx_bbl338_sim_state::sim152_r()
{
	u16 pc = m_maincpu->pc();

	if (!machine().side_effects_disabled() && pc == 0x150)
	{
		u8 command = (u8)m_maincpu->state_int(M6502_X);
		switch (command)
		{
		case 0x00: break; // command 0x00 = draw jewels in columns
		// 0x02 not seen
		case 0x04: break; // command 0x04 = draw text number
		case 0x06: break; // command 0x06 = draw boxes in sudoku
		case 0x08: break; // command 0x08 = some kind of positioning logic for things (characters in risker)
		case 0x0a:
		{
			// this is related to drawing a graphic element on dphh8213 (skipping one call causes a single menu item to not show)
			// same here?
			address_space& mainspace = m_maincpu->space(AS_PROGRAM);
			u8 param0 = mainspace.read_byte(0x100);
			u8 param1 = mainspace.read_byte(0x101);
			u8 param2 = mainspace.read_byte(0x102);
			u8 param3 = mainspace.read_byte(0x103);
			u8 param4 = mainspace.read_byte(0x104);
			u8 param5 = mainspace.read_byte(0x105);

			// Flags --- --Ff  f = flipX F = flipY, others not checked
			logerror("command 0x0a (draw?) using params Xpos: %02x Ypos: %02x ObjectNum: %02x%02x Flags: %02x Saturation: %02x\n", param0, param1, param3, param2, param4, param5);

			break;
		}
		case 0x0c: break; // command 0x0c = related to drawing platforms in risker?
		case 0x0e: break; // important for king boxing to show anything outside of char select
		case 0x10: break; // command 0x10 = clear out some line buffer for rendering?
		case 0x12: break; // command 0x12 = clear background in angry pigs?
		case 0x14: break; // command 0x14 = draw basic text (dphh8213 test mode, see 0x28 for bbl338)
		case 0x16: break; // important for collisions in risker?
		// 0x18 not seen
		case 0x1a: break; // when pause is pressed? maybe music related?
		case 0x1c: break; // unknown, little effect? maybe play music?
		// 0x1e not seen
		// 0x20 not seen
		case 0x22: break; // command 0x22 = unknown, used before 'shooting zombies' titlescreen
		case 0x24: break; // command 0x24 = play sound
		case 0x26: break; // command 0x26 = force stop sound(s)?

		 case 0x28:
		 {
			// on bbl338 this is used for the 'draw text' functionality in test mode instead of 0x14, see 0x4866 which is
			// the 'draw letter stored in A at position' function, calling this command.
			// The equivalent code in dphh8213 is at 0x43e4 instead and calls 0x14
			address_space& mainspace = m_maincpu->space(AS_PROGRAM);
			u8 param0 = mainspace.read_byte(0x100);
			u8 param1 = mainspace.read_byte(0x101);
			u8 param2 = mainspace.read_byte(0x102);
			u8 param3 = mainspace.read_byte(0x103);

			logerror("command 0x28 (draw text direct) using params Xpos: %02x Ypos: %02x char '%c' unk %02x\n", param0, param1, param2, param3);

			break;
		}

		default:
		{
			logerror("%04x: reached 0x152, need to execute BIOS simulation for command %02x\n", pc, command);
		}

		}

		//if (command == 0x00)
		//  return 0x60;
	}
	return m_152_dat;
}

void st22xx_bbl338_sim_state::sim152_w(u8 data)
{
	m_152_dat = data;
}


void st22xx_bbl338_sim_state::machine_reset()
{
	address_space& mainspace = m_maincpu->space(AS_PROGRAM);

	mainspace.install_readwrite_handler(0x0152, 0x0152, read8smo_delegate(*this, FUNC(st22xx_bbl338_sim_state::sim152_r)), write8smo_delegate(*this, FUNC(st22xx_bbl338_sim_state::sim152_w)));

	// The code that needs to be in RAM doesn't seem to be in the ROM
	const uint8_t ramcode[40] = {

		// this is the 'execute BIOS function' call
		0xa4, 0x33,       // 000150:         ldy $33 |- Push current bank onto stack
		0x5a,             // 000152:         phy     |
		0xa4, 0x32,       // 000153:         ldy $32 |
		0x5a,             // 000155:         phy     /
		0x64, 0x32,       // 000156:         stz $32 | - Zero Bank (manually optimized compared to the dphh8213 implementation to reduce code size so call to 0x0164 is correct)
		0x64, 0x33,       // 000158:         stz $33 /
		//0x20, 0x3d, 0x41, // 000152:         jsr $xxxx   -- this needs to go to a jump table to process the command stored in X
		0xea, 0xea, 0xea, // NOP above out for now as it isn't clear where to jump to
		0x7a,             // 00015d:         ply     |- restore previous bank
		0x84, 0x32,       // 00015e:         sty $32 |
		0x7a,             // 000160:         ply     |
		0x84, 0x33,       // 000161:         sty $33 /
		0x60,             // 000163:         rts

		// this is the 2nd call to RAM, the bank to call is in y/x with the address modified in the code here before calling
		0xa5, 0x33,       // 000164:         lda $33 |- store old bank on stack
		0x48,             // 000166:         pha     |
		0xa5, 0x32,       // 000167:         lda $32 |
		0x48,             // 000169:         pha     /
		0x84, 0x33,       // 00016a:         sty $33 |- set bank from X and Y  (y is always 0 in dphh8213, not here, suggesting ROM doesn't map at 0)
		0x86, 0x32,       // 00016c:         stx $32 /
		0x20, 0x00, 0x00, // 00016e:         jsr xxxx the address here is set before the call with a ldx #$93 / stx $016f, ldx #$42 / stx $0170 type sequence
		0x7a,             // 000171:         ply     |- restore old bank
		0x84, 0x32,       // 000172:         sty $32 |
		0x7a,             // 000174:         ply     |
		0x84, 0x33,       // 000175:         sty $33 /
		0x60              // 000177:         rts
	};

	for (int i = 0; i < 40; i++)
		mainspace.write_byte(0x150+i, ramcode[i]);

	// force it to boot from external space (missing internal code to copy above segment to RAM however)
	mainspace.write_byte(0x32, 0x00);
	mainspace.write_byte(0x33, 0x04);

	// force interrupts to be fetched from external space (complete?)
	mainspace.write_byte(0x30, 0x00);
	mainspace.write_byte(0x31, 0x04);

	// timers / timer interrupt init sequence
	mainspace.write_byte(0x28, 0x08);
	mainspace.write_byte(0x29, 0xc0);
	mainspace.write_byte(0x26, 0xff);
	mainspace.write_byte(0x27, 0xdf);
	mainspace.write_byte(0x3e, 0x10);
	mainspace.write_byte(0x3c, 0x00);
	mainspace.write_byte(0x39, 0x12);
}

void st22xx_bbl338_sim_state::machine_start()
{
	st22xx_bbl338_state::machine_start();
	save_item(NAME(m_152_dat));
}

void st22xx_bbl338_state::machine_start()
{
	save_item(NAME(m_portb));
}

void st22xx_bbl338_state::st22xx_dphh8213_map(address_map &map)
{
	map(0x0000000, 0x01fffff).rom().region("maincpu", 0);

	map(0x0600000, 0x0600000).w(m_lcdc, FUNC(bl_handhelds_lcdc_device::lcdc_command_w));
	map(0x0604000, 0x0604000).rw(m_lcdc, FUNC(bl_handhelds_lcdc_device::lcdc_data_r), FUNC(bl_handhelds_lcdc_device::lcdc_data_w));
}

void st22xx_bbl338_state::st22xx_bbl338_map(address_map &map)
{
	//map(0x0000000, 0x0003fff).rom().region("internal", 0); // not dumped, so ensure any accesses here are logged
	map(0x1000000, 0x13fffff).rom().region("maincpu", 0);

	map(0x0600000, 0x0600000).w(m_lcdc, FUNC(bl_handhelds_lcdc_device::lcdc_command_w));
	map(0x0604000, 0x0604000).rw(m_lcdc, FUNC(bl_handhelds_lcdc_device::lcdc_data_r), FUNC(bl_handhelds_lcdc_device::lcdc_data_w));
}

u32 st22xx_bbl338_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return m_lcdc->render_to_bitmap(screen, bitmap, cliprect);
}

u8 st22xx_bbl338_state::porta_r()
{
	u8 input = 0x3f;

	// irregular port configuration
	if (!BIT(m_portb, 0))
		input &= m_input_matrix[0]->read();
	for (int i = 1; i < 4; i++)
		if (!BIT(m_portb, i * 2 - 1))
			input &= m_input_matrix[i]->read();

	// TODO: bit 7 is I/O for some bitbanged SPI device (used for the menu)
	input |= 0xc0;

	logerror("%s: port a read\n", machine().describe_context());

	return input;
}

void st22xx_bbl338_state::porta_w(u8 data)
{
	// Menu control writes?
	logerror("%s: port a write %02x\n", machine().describe_context(), data);
}

void st22xx_bbl338_state::portb_w(u8 data)
{
//  logerror("%s: port b write %02x\n", machine().describe_context(), data);
	m_portb = data;
}

// input multiplexing not verified for bbl338
static INPUT_PORTS_START(dphh8213)
	// P2 controls work with some of the games, but there was no obvious way to connect a 2nd pad?
	// document them for now, but maybe comment them out later for accuracy.
	PORT_START("IN1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_START1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("P1 A")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("P2 B")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) // bbl338 - must be IP_ACTIVE_HIGH to avoid system hanging with 'wai' opcode after code turns on port interrupt if not in test mode (power off?)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("P2 A") PORT_PLAYER(2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_START2)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT) PORT_PLAYER(2)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_UP) PORT_PLAYER(2)

	PORT_START("IN4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("P2 B") PORT_PLAYER(2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(2)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN) PORT_PLAYER(2)

	PORT_START("PORTC")
	PORT_CONFNAME( 0x01,  0x01, DEF_STR( Language ) )
	PORT_CONFSETTING( 0x00, "Chinese" )
	PORT_CONFSETTING( 0x01, "English" )
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xf6, IP_ACTIVE_LOW, IPT_UNUSED) // probably unused
INPUT_PORTS_END


void st22xx_bbl338_state::st22xx_dphh8213(machine_config &config)
{
	ST2302U(config, m_maincpu, 24000000);
	m_maincpu->set_addrmap(AS_DATA, &st22xx_bbl338_state::st22xx_dphh8213_map);
	m_maincpu->in_pa_callback().set(FUNC(st22xx_bbl338_state::porta_r));
	m_maincpu->out_pa_callback().set(FUNC(st22xx_bbl338_state::porta_w));

	m_maincpu->out_pb_callback().set(FUNC(st22xx_bbl338_state::portb_w));
	m_maincpu->in_pc_callback().set_ioport("PORTC");

	SPEAKER(config, "mono").front_center();
	m_maincpu->add_route(0, "mono", 1.00);
	m_maincpu->add_route(1, "mono", 1.00);
	m_maincpu->add_route(2, "mono", 1.00);
	m_maincpu->add_route(3, "mono", 1.00);


	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(0));
	m_screen->set_size(160, 128);
	m_screen->set_visarea(0, 160 - 1, 0, 128 - 1);
	m_screen->set_screen_update(FUNC(st22xx_bbl338_state::screen_update));

	BL_HANDHELDS_LCDC(config, m_lcdc, 0);
}

void st22xx_bbl338_state::st22xx_bbl338(machine_config &config)
{
	ST2302U(config, m_maincpu, 24000000);
	m_maincpu->set_addrmap(AS_DATA, &st22xx_bbl338_state::st22xx_bbl338_map);
	m_maincpu->in_pa_callback().set(FUNC(st22xx_bbl338_state::porta_r));
	m_maincpu->out_pb_callback().set(FUNC(st22xx_bbl338_state::portb_w));
	m_maincpu->in_pc_callback().set_ioport("PORTC");

	// incorrect for bbl338
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(0));
	m_screen->set_size(160, 128);
	m_screen->set_visarea(0, 160 - 1, 0, 128 - 1);
	m_screen->set_screen_update(FUNC(st22xx_bbl338_state::screen_update));

	// incorrect for bbl338 (or will need changes to support higher resolutions)
	BL_HANDHELDS_LCDC(config, m_lcdc, 0);
}


ROM_START( bbl338 )
	ROM_REGION( 0x4000, "internal", ROMREGION_ERASEFF )
	ROM_LOAD( "internal.rom", 0x000000, 0x4000, NO_DUMP ) // unsure of exact size for this model

	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "en29lv160ab.u1", 0x000000, 0x200000, CRC(2c73e16c) SHA1(e2c69b3534e32ef384c0c2f5618118a419326e3a) )
ROM_END

ROM_START( class200 )
	ROM_REGION( 0x4000, "internal", ROMREGION_ERASEFF )
	ROM_LOAD( "internal.rom", 0x000000, 0x4000, NO_DUMP ) // unsure of exact size for this model

	ROM_REGION( 0x400000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "m29w320eb.bin", 0x000000, 0x400000, CRC(3067b5f6) SHA1(9a159b16898054a74cfb32b5c597b505132f004e) )
ROM_END

ROM_START( dphh8213 )
	// internal area not used

	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "mx29lv160cb.u1", 0x000000, 0x200000, CRC(c8e7e355) SHA1(726f28c2c9ab012a6842f9f30a0a71538741ba14) )
	ROM_FILL( 0x00009f, 2, 0xea ) // NOP out SPI check
ROM_END

} // anonymous namespace


// this is uses a higher resolution display than the common units, but not as high as the SunPlus based ones
COMP( 201?, bbl338,   0,      0,      st22xx_bbl338, dphh8213, st22xx_bbl338_sim_state, empty_init, "BaoBaoLong", "Portable Game Player BBL-338 (BaoBaoLong, 48-in-1)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
// also appears to be the higher resolution display
COMP( 201?, class200, 0,      0,      st22xx_bbl338, dphh8213, st22xx_bbl338_sim_state, empty_init, "<unknown>", "Color LCD Classic Game 200-in-1", MACHINE_NO_SOUND | MACHINE_NOT_WORKING ) // no manufacturer name or product code anywhere

// Language controlled by port bit, set at factory, low resolution
COMP( 201?, dphh8213, 0,      0,      st22xx_dphh8213, dphh8213, st22xx_bbl338_state, empty_init, "<unknown>", "Digital Pocket Hand Held System 20-in-1 - Model 8213", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



st2302u_bbl_spi.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

// Handhelds based on the ST2205U or ST23XX architecture

// the BBL 380 - 180 in 1 features similar menus / presentation / games to the 'ORB Gaming Retro Arcade Pocket Handheld Games Console with 153 Games' (eg has Matchstick Man, Gang Tie III etc.)
// https://www.youtube.com/watch?v=NacY2WHd-CY

// these games were ported to unSP hardware at some point, generalplus_gpl162xx_lcdtype.cpp

// BIOS calls are made very frequently to the firmware (undumped for some sets).
// The most common call ($6058 in bbl380, $6074 in mc_cb203, $6062 in ragc153 & dphh8630, $6052 in pg118 & toumapet) seems to involve downloading a snippet of code from SPI and executing it from RAM at $0300.
// A variant of this call ($60d2 in bbl380, $60ed in mc_cb203, $60e3 in ragc153 & dphh8630, $60de in pg118 & toumapet) is invoked with jsr.
// For these calls, a 24-bit starting address is specified in $82:$81:$80, and the length in bytes is twice the number specified in $84:$83.
// There is a configurable XOR specified in $99 on ragc153 & dphh8630.
// $6003 performs a table lookup, depositing a sequence of data at $008e.
// $6000 is some sort of macro call with the X register as function selector
// (X = $24 should display the character in $0102 on screen).
// One other BIOS call ($6975 in bbl380, $69d2 in ragc153) has an unknown purpose.

/*
   Some sets contain games not indexed by the menu code, some of these games are broken / in a state of mid-reskinning, others seem to be functional

   Menu index list locations in ROM
   supreme 0x243e
*/

// TODO: convert to use generic_spi_flash.cpp (but there seems to be some buffering of writes / reads?)

#include "emu.h"

#include "cpu/m6502/st2205u.h"
#include "machine/bl_handhelds_menucontrol.h"
#include "bl_handhelds_lcdc.h"

#include "screen.h"
#include "emupal.h"
#include "speaker.h"


namespace {

class bbl380_state : public driver_device
{
public:
	bbl380_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen"),
		m_spirom(*this, "spi"),
		m_io_p1(*this, "IN0"),
		m_io_p2(*this, "IN1"),
		m_menucontrol(*this, "menucontrol"),
		m_lcdc(*this, "lcdc")
	{ }

	void bbl380(machine_config &config);
	void bbl380_menuprot(machine_config &config);
	void bbl380_24mhz(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void bbl380_do_maincpu_config();

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void bbl380_map(address_map &map) ATTR_COLD;

	required_device<st2205u_base_device> m_maincpu;
	required_device<screen_device> m_screen;

	void output_w(u8 data);
	void output2_w(u8 data);

	u8 m_output2val;

	enum spistate : u8
	{
		SPI_STATE_READY = 0,
		SPI_STATE_WAITING_HIGH_ADDR = 1,
		SPI_STATE_WAITING_MID_ADDR = 2,
		SPI_STATE_WAITING_LOW_ADDR = 3,
		SPI_STATE_WAITING_DUMMY1_ADDR = 4,
		SPI_STATE_WAITING_DUMMY2_ADDR = 5,
		SPI_STATE_READING = 6,
	};

	u8 m_spistate;
	u32 m_spiaddress;
	u8 m_delay;

	void spi_w(u8 data);
	u8 spi_r();

	required_region_ptr<u8> m_spirom;
	required_ioport m_io_p1;
	required_ioport m_io_p2;
	required_device<bl_handhelds_menucontrol_device> m_menucontrol;
	required_device<bl_handhelds_lcdc_device> m_lcdc;

	u8 ff_r() { return 0xff; }
};


void bbl380_state::output_w(u8 data)
{
	m_spistate = SPI_STATE_READY;
}

void bbl380_state::output2_w(u8 data)
{
	if ((data & 0x40) != (m_output2val & 0x40))
	{
		if (data & 0x40)
			m_menucontrol->reset_w(1);
	}

	m_menucontrol->data_w((data & 0x08) >> 3);
	m_menucontrol->clock_w((data & 0x04) >> 2);

	m_output2val = data;
}

u32 bbl380_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return m_lcdc->render_to_bitmap(screen, bitmap, cliprect);
}

void bbl380_state::machine_start()
{
	// port related
	save_item(NAME(m_output2val));

	// SPI related
	save_item(NAME(m_spistate));
	save_item(NAME(m_spiaddress));
	save_item(NAME(m_delay));
}


void bbl380_state::machine_reset()
{
	m_output2val = 0;

	// TODO: handle these things in the core via callbacks etc. once correct behavior is agreed upon
	m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x0010, 0x0011, read8smo_delegate(*this, FUNC(bbl380_state::spi_r)), write8smo_delegate(*this, FUNC(bbl380_state::spi_w))); // SPI related
	m_maincpu->space(AS_PROGRAM).install_read_handler(0x0014, 0x0014, read8smo_delegate(*this, FUNC(bbl380_state::ff_r))); // SPI related
	m_maincpu->space(AS_PROGRAM).install_write_handler(0x0000, 0x0000, write8smo_delegate(*this, FUNC(bbl380_state::output_w))); // Port A output hack, SPI state needs resetting on every port write here or some gfx won't copy fully eg red squares on right of parachute, Soc implementation filters writes
}


void bbl380_state::spi_w(u8 data)
{
	switch (m_spistate)
	{
	case SPI_STATE_READY:
	{
		if (data == 0x03)
		{
			m_spistate = SPI_STATE_WAITING_HIGH_ADDR;
		}
		else
		{
			logerror("%s: invalid state request %02x\n", machine().describe_context(), data);
		}
		break;
	}

	case SPI_STATE_WAITING_HIGH_ADDR:
	{
		m_spiaddress = (m_spiaddress & 0xff00ffff) | data << 16;
		m_spistate = SPI_STATE_WAITING_MID_ADDR;
		break;
	}

	case SPI_STATE_WAITING_MID_ADDR:
	{
		m_spiaddress = (m_spiaddress & 0xffff00ff) | data << 8;
		m_spistate = SPI_STATE_WAITING_LOW_ADDR;
		break;
	}

	case SPI_STATE_WAITING_LOW_ADDR:
	{
		m_spiaddress = (m_spiaddress & 0xffffff00) | data;
		m_spistate = SPI_STATE_READING;
		m_delay = 2;
		break;
	}

	case SPI_STATE_READING:
	{
		// writes when in read mode clock in data?
		m_delay = 1;
		break;
	}

	case SPI_STATE_WAITING_DUMMY1_ADDR:
	{
		m_spistate = SPI_STATE_WAITING_DUMMY2_ADDR;
		break;
	}

	case SPI_STATE_WAITING_DUMMY2_ADDR:
	{
		//  m_spistate = SPI_STATE_READY;
		break;
	}

	}
}

u8 bbl380_state::spi_r()
{
	switch (m_spistate)
	{
	case SPI_STATE_READING:
	{
		if (m_delay > 0)
		{
			m_delay--;
			return 0x00;
		}
		else
		{
			u8 dat = m_spirom[m_spiaddress & 0x3fffff];
			//logerror("%s: reading SPI %02x from SPI Address %08x\n", machine().describe_context(), dat, m_spiaddress);
			m_spiaddress++;
			return dat;
		}
	}

	default:
	{
		//logerror("%s: reading FIFO in unknown state\n", machine().describe_context() );
		return 0x00;
	}
	}

	return 0x00;
}


void bbl380_state::bbl380_map(address_map &map)
{
	map(0x0002000, 0x0003fff).rom().region("maincpu", 0);
	map(0x1800000, 0x1800000).w(m_lcdc, FUNC(bl_handhelds_lcdc_device::lcdc_command_w));
	map(0x1804000, 0x1804000).rw(m_lcdc, FUNC(bl_handhelds_lcdc_device::lcdc_data_r), FUNC(bl_handhelds_lcdc_device::lcdc_data_w));
}

static INPUT_PORTS_START(bbl380)
	PORT_START("IN0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED) // maybe ON/OFF
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_START1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_BUTTON3) PORT_NAME("SOUND")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("B")

	PORT_START("IN1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("A")
	PORT_BIT(0x06, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START(bbl380_prot)
	PORT_INCLUDE(bbl380)

	PORT_MODIFY("IN1")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("menucontrol", FUNC(bl_handhelds_menucontrol_device::data_r))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("menucontrol", FUNC(bl_handhelds_menucontrol_device::status_r))
INPUT_PORTS_END

void bbl380_state::bbl380_do_maincpu_config()
{
	m_maincpu->set_addrmap(AS_DATA, &bbl380_state::bbl380_map);
	m_maincpu->in_pa_callback().set_ioport("IN0");
	m_maincpu->in_pb_callback().set_ioport("IN1");
	m_maincpu->out_pa_callback().set(FUNC(bbl380_state::output_w));
	//m_maincpu->spi_in_callback().set(FUNC(bbl380_state::spi_r));  // TODO, hook these up properly
	//m_maincpu->spi_out_callback().set(FUNC(bbl380_state::spi_w));

	m_maincpu->add_route(0, "mono", 1.00);
	m_maincpu->add_route(1, "mono", 1.00);
	m_maincpu->add_route(2, "mono", 1.00);
	m_maincpu->add_route(3, "mono", 1.00);
}

void bbl380_state::bbl380(machine_config &config)
{
	ST2302U(config, m_maincpu, 32'000'000);  // 32MHz clock correct for music tempo. SoC type not confirmed
	SPEAKER(config, "mono").front_center();

	bbl380_do_maincpu_config();

	SCREEN(config, m_screen, SCREEN_TYPE_LCD); // TFT color LCD
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(0));
	m_screen->set_size(160, 128);
	m_screen->set_visarea(0, 160 - 1, 0, 128 - 1);
	m_screen->set_screen_update(FUNC(bbl380_state::screen_update));

	BL_HANDHELDS_MENUCONTROL(config, m_menucontrol, 0);
	BL_HANDHELDS_LCDC(config, m_lcdc, 0);

	// LCD controller seems to be either Sitronix ST7735R or (if RDDID bytes match) Ilitek ILI9163C
	// (SoC's built-in LCDC is unused or nonexistent?)
	// Several other LCDC models are identified by ragc153 and dphh8630
}

void bbl380_state::bbl380_menuprot(machine_config &config)
{
	bbl380(config);
	m_maincpu->out_pb_callback().set(FUNC(bbl380_state::output2_w));
}

void bbl380_state::bbl380_24mhz(machine_config &config)
{
	bbl380(config);
	ST2302U(config.replace(), m_maincpu, 24'000'000); // 24MHz clock correct for music tempo. SoC type not confirmed
	bbl380_do_maincpu_config();
}

// internal OTPROM BIOS, dumped from dgun2953 PCB, 6000-7fff range
#define INTERNAL_ROM_TYPE1 \
	ROM_REGION(0x2000, "maincpu", ROMREGION_ERASEFF) \
	ROM_LOAD("st2x_internal_type1.bin", 0x0000, 0x2000, CRC(f4dc1fc2) SHA1(bbc11539c48eb612ebae50da45e03b6fde440941))

// internal OTPROM BIOS, dumped from retro150a PCB, 6000-7fff range
#define INTERNAL_ROM_TYPE2 \
	ROM_REGION(0x2000, "maincpu", ROMREGION_ERASEFF) \
	ROM_LOAD("st2x_internal_type2.bin", 0x0000, 0x2000, CRC(32d96794) SHA1(9d7e3e284f1656d8b2f7dae754cab1f82b3a1d61))


// sets with unknown version of internal ROM

ROM_START(bbl380)
	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASEFF)
	ROM_LOAD("bbl380_st2205u.bin", 0x000000, 0x004000, NO_DUMP) // internal OTPROM BIOS (addresses are different from other sets)

	ROM_REGION(0x800000, "spi", ROMREGION_ERASEFF)
	ROM_LOAD("bbl 380 180 in 1.bin", 0x000000, 0x400000, BAD_DUMP CRC(146c88da) SHA1(7f18526a6d8cf991f86febce3418d35aac9f49ad))
	// 0x0022XX, 0x0026XX, 0x002AXX, 0x002CXX, 0x002DXX, 0x0031XX, 0x0036XX, etc. should not be FF fill
ROM_END

ROM_START(mc_cb203)
	ROM_REGION(0x800000, "maincpu", ROMREGION_ERASEFF)
	ROM_LOAD("cb230_st2205u.bin", 0x000000, 0x004000, NO_DUMP) // internal OTPROM BIOS (addresses are different from other sets, including bbl380)

	ROM_REGION(0x800000, "spi", ROMREGION_ERASEFF)
	ROM_LOAD("s25fl032.bin", 0x000000, 0x400000, CRC(33c4e67b) SHA1(5787db4c8ce4c2569a5f9e9054cbb1944c1b3092))
ROM_END

// sets with 1nd version of internal ROM

ROM_START(rhhc152)
	INTERNAL_ROM_TYPE1

	ROM_REGION(0x800000, "spi", ROMREGION_ERASEFF)
	ROM_LOAD("152_mk25q32amg_ef4016.bin", 0x000000, 0x400000, CRC(5f553895) SHA1(cd21c6ff225e0455531f6b1d9f1c66a284948516))
ROM_END

ROM_START(ragc153)
	INTERNAL_ROM_TYPE1

	ROM_REGION(0x800000, "spi", ROMREGION_ERASEFF)
	ROM_LOAD("25q32ams.bin", 0x000000, 0x400000, CRC(de328d73) SHA1(d17b97e9057be4add68b9f5a26e04c9f0a139673)) // first 0x100 bytes would read as 0xff at regular speed, but give valid looking consistent data at a slower rate
ROM_END

ROM_START(dphh8630)
	INTERNAL_ROM_TYPE1

	ROM_REGION(0x800000, "spi", ROMREGION_ERASEFF)
	ROM_LOAD("bg25q16.bin", 0x000000, 0x200000, CRC(277850d5) SHA1(740087842e1e63bf99b4ca9c1b2053361f267269))
ROM_END

ROM_START(dgun2953)
	INTERNAL_ROM_TYPE1

	ROM_REGION(0x800000, "spi", ROMREGION_ERASEFF)
	ROM_LOAD("dg160_25x32v_ef3016.bin", 0x000000, 0x400000, CRC(2e993bac) SHA1(4b310e326a47df1980aeef38aa9a59018d7fe76f))
ROM_END

ROM_START(arcade10)
	INTERNAL_ROM_TYPE1

	ROM_REGION(0x800000, "spi", ROMREGION_ERASEFF)
	ROM_LOAD("25q40.bin", 0x000000, 0x080000, CRC(62784666) SHA1(ba1a4abed0a41b2fb3868543306243e68ea6b2e1))
ROM_END

ROM_START(supreme)
	INTERNAL_ROM_TYPE1

	ROM_REGION(0x800000, "spi", ROMREGION_ERASEFF)
	ROM_LOAD("25q32.bin", 0x000000, 0x400000, CRC(93072a3d) SHA1(9f8770839032922e64d5ddd8864441357623c45f))
ROM_END

ROM_START(throwbck)
	INTERNAL_ROM_TYPE1

	ROM_REGION(0x800000, "spi", ROMREGION_ERASEFF)
	ROM_LOAD("25q32egig.bin", 0x000000, 0x400000, CRC(959eb09d) SHA1(901738e6b6c8fdfe4ed9b268ba3ddd1444551442))
ROM_END

// sets with 2nd version of internal ROM

ROM_START(dphh8633)
	INTERNAL_ROM_TYPE2

	ROM_REGION(0x800000, "spi", ROMREGION_ERASEFF)
	ROM_LOAD("25lq032.u2", 0x000000, 0x400000, CRC(45b8609a) SHA1(d03615a68465a1a365ba07db0b352424680d62d0) )
ROM_END

ROM_START(retro150)
	INTERNAL_ROM_TYPE2

	ROM_REGION(0x800000, "spi", ROMREGION_ERASEFF)
	ROM_LOAD("p25d32sh.u2", 0x000000, 0x400000, CRC(294290aa) SHA1(078892b2bb10e347ed07273bafed486e0f52c909) )
ROM_END

ROM_START(retro150a)
	INTERNAL_ROM_TYPE2

	ROM_REGION(0x800000, "spi", ROMREGION_ERASEFF)
	ROM_LOAD("by25q32ess.bin", 0x000000, 0x400000, CRC(ef9e8091) SHA1(5b924d5fd4419956d49379a695b87435df7a1155) )
ROM_END

ROM_START(pg118)
	INTERNAL_ROM_TYPE2

	ROM_REGION(0x800000, "spi", ROMREGION_ERASEFF)
	ROM_LOAD("25vq32.bin", 0x000000, 0x400000, CRC(e99f1621) SHA1(f907c36a1a884d892331b7de294a8fd58f7bf9d5) )
ROM_END

ROM_START(toumapet)
	INTERNAL_ROM_TYPE2 // still does't boot with this one, is it different internal ROM again, or just different mappings?

	ROM_REGION(0x800000, "spi", ROMREGION_ERASEFF)
	ROM_LOAD("p25d32sh.bin", 0x000000, 0x400000, CRC(25498f00) SHA1(c5c410e29f540d7f1fd4bbb333467f8a3eaccc15) )
ROM_END


} // anonymous namespace


// older releases (primarily for Asian market?)

CONS( 201?, bbl380,        0,       0,      bbl380,   bbl380, bbl380_state, empty_init, "BaoBaoLong", "BBL380 - 180 in 1", MACHINE_NOT_WORKING )

CONS( 201?, mc_cb203,      0,       0,      bbl380,   bbl380, bbl380_state, empty_init, "Coolboy", "Coolboy RS-17 - 203 in 1", MACHINE_NOT_WORKING )

// newer releases (more heavily censored, for export markets?) internal ROM was changed for these

CONS( 201?, dphh8630,      0,       0,      bbl380_menuprot,   bbl380_prot, bbl380_state, empty_init, "<unknown>", "Digital Pocket Hand Held System 230-in-1 - Model 8630", MACHINE_IMPERFECT_SOUND ) // sometimes sold as PCP

CONS( 201?, rhhc152,       0,       0,      bbl380_menuprot,   bbl380_prot, bbl380_state, empty_init, "Orb", "Retro Handheld Console 152-in-1", MACHINE_IMPERFECT_SOUND ) // looks like a mini GameBoy - 'Over 150 games' on box

CONS( 201?, ragc153,       0,       0,      bbl380_menuprot,   bbl380_prot, bbl380_state, empty_init, "Orb", "Retro Arcade Game Controller 153-in-1", MACHINE_IMPERFECT_SOUND ) // looks like a Game & Watch

CONS( 201?, dgun2953,      0,       0,      bbl380_menuprot,   bbl380_prot, bbl380_state, empty_init, "dreamGEAR", "My Arcade Gamer Mini 160-in-1 (DGUN-2953)", MACHINE_IMPERFECT_SOUND )

CONS( 201?, arcade10,      0,       0,      bbl380_menuprot,   bbl380_prot, bbl380_state, empty_init, "Fizz Creations", "Mini Arcade Console (Arcade 10-in-1)", MACHINE_IMPERFECT_SOUND )

CONS( 201?, supreme,       0,       0,      bbl380_menuprot,   bbl380_prot, bbl380_state, empty_init, "Fizz Creations", "Arcade Classics Mini Handheld Arcade (Supreme 150)", MACHINE_IMPERFECT_SOUND )

CONS( 201?, throwbck,      0,       0,      bbl380_menuprot,   bbl380_prot, bbl380_state, empty_init, "Westminster", "Throwback Pocket Video Game Console 150+ 8-Bit Games", MACHINE_IMPERFECT_SOUND )

// releases with different internal ROM, these currently have rendering issues for unknown reasons

// for the UK market, runs at a slightly slower clock
CONS( 201?, retro150,      0,       0,      bbl380_24mhz,   bbl380, bbl380_state, empty_init, "Red5", "Retro Arcade Game Controller (150-in-1) (set 1)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
CONS( 201?, retro150a,     retro150,0,      bbl380_24mhz,   bbl380, bbl380_state, empty_init, "Red5", "Retro Arcade Game Controller (150-in-1) (set 2)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
// these are for the Japanese market, the ROM is the same between the Pocket Game and Game Computer but the form factor is different.
CONS( 2019, pg118,         0,       0,      bbl380_menuprot,   bbl380_prot, bbl380_state, empty_init, "Pocket Game / Game Computer", "Pocket Game 118-in-1 / Game Computer 118-in-1", MACHINE_NOT_WORKING )
// it is unclear if dphh8633 refers to the case style, rather than the software, as the dphh8630 set was also noted as previously being found in an 8633 unit
CONS( 201?, dphh8633,      0,       0,      bbl380_menuprot,   bbl380_prot, bbl380_state, empty_init, "<unknown>", "Digital Pocket Hand Held System 268-in-1 - Model 8633", MACHINE_NOT_WORKING )

// also has the 0xE4 XOR, also doesn't currently boot, could be yet another internal ROM
CONS( 2021, toumapet,      0,       0,      bbl380,   bbl380, bbl380_state, empty_init, "Shenzhen Shiji New Technology", "Tou ma Pet", MACHINE_NOT_WORKING )




stc3910.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/**********************************************************************

    STC 3910 Executel

    TODO:
    - requires tape to be fully usable.
    - sound, beeps at startup.

**********************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/i8085/i8085.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/clock.h"
#include "machine/mdcr.h"
#include "machine/mm58174.h"
#include "machine/saa5070.h"
#include "video/mr9735.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class executel_state : public driver_device
{
public:
	executel_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rombank(*this, "rombank")
		, m_keyb(*this, "keyb")
		, m_tape(*this, "tape")
		, m_videoram(*this, "videoram")
		, m_kbd(*this, "KEY%u", 0)
		, m_mdcr(*this, "mdcr")
		, m_hfree_led(*this, "hfree_led")
		, m_vdata_led(*this, "vdata_led")
	{ }

	void executel(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_memory_bank m_rombank;
	required_device<i8741a_device> m_keyb;
	required_device<i8741a_device> m_tape;
	required_shared_ptr<uint8_t> m_videoram;
	required_ioport_array<16> m_kbd;
	required_device<mdcr_device> m_mdcr;
	output_finder<> m_hfree_led;
	output_finder<> m_vdata_led;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	uint8_t kbd_p1_r();
	uint8_t kbd_p2_r();
	void kbd_p2_w(uint8_t data);

	uint8_t tape_p1_r();
	uint8_t tape_p2_r();
	void tape_p2_w(uint8_t data);

	void bank_w(uint8_t data);

	uint8_t m_keycol;
};


void executel_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).rom().region("maincpu", 0);
	map(0x4000, 0x7fff).bankr("rombank");
	map(0x8000, 0xfbff).ram();
	map(0xfc00, 0xffff).ram().share("videoram");
}

void executel_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	//map(0x30, 0x30) // printer
	map(0x70, 0x71).rw(m_keyb, FUNC(upi41_cpu_device::upi41_master_r), FUNC(upi41_cpu_device::upi41_master_w));
	map(0xb0, 0xbf).rw("rtc", FUNC(mm58174_device::read), FUNC(mm58174_device::write));
	map(0xc0, 0xc0).w(FUNC(executel_state::bank_w));
	map(0xd0, 0xd1).rw(m_tape, FUNC(upi41_cpu_device::upi41_master_r), FUNC(upi41_cpu_device::upi41_master_w));
	map(0xe0, 0xef).rw("viop", FUNC(saa5070_device::read_direct), FUNC(saa5070_device::write_direct));
}


static INPUT_PORTS_START( executel )
	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)                            PORT_CHAR(UCHAR_MAMEKEY(F7))         PORT_NAME("HANDS FREE")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11)                           PORT_CHAR(UCHAR_MAMEKEY(F11))        PORT_NAME("CALL")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10)                           PORT_CHAR(UCHAR_MAMEKEY(F10))        PORT_NAME("EXCH")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)                        PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)                            PORT_CHAR(UCHAR_MAMEKEY(F8))         PORT_NAME("REDIAL")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)    PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)    PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)    PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)                         PORT_CHAR('*')

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)                            PORT_CHAR(UCHAR_MAMEKEY(F6))         PORT_NAME("VIEW DATA")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)    PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)    PORT_CHAR('5')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)    PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD)    PORT_CHAR('0')

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9)                            PORT_CHAR(UCHAR_MAMEKEY(F9))         PORT_NAME("AUTO DIAL")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)    PORT_CHAR('3') PORT_CHAR(0xa3) // £
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)    PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD)    PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)                     PORT_CHAR('#')

	PORT_START("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)                            PORT_CHAR(UCHAR_MAMEKEY(F5))         PORT_NAME("CALC")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD)                     PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD))  PORT_NAME(u8"÷")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK)                      PORT_CHAR(UCHAR_MAMEKEY(ASTERISK))   PORT_NAME(u8"× AM")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD)                      PORT_CHAR('+')                       PORT_NAME("+")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD)                     PORT_CHAR('-')                       PORT_NAME("- PM")

	PORT_START("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)                            PORT_CHAR(UCHAR_MAMEKEY(F1))         PORT_NAME("TIMER")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)                             PORT_CHAR('q') PORT_CHAR('Q')        PORT_NAME("Q JAN")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)                        PORT_CHAR('=')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)                          PORT_CHAR('.')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)                     PORT_CHAR(8)                         PORT_NAME("DELETE")

	PORT_START("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME)                          PORT_CHAR(UCHAR_MAMEKEY(HOME))       PORT_NAME("NEW")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)                             PORT_CHAR('w') PORT_CHAR('W')        PORT_NAME("W FEB")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)                             PORT_CHAR('a') PORT_CHAR('A')        PORT_NAME("A")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD)                                                  PORT_CHAR('%')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)                                                              PORT_NAME("CHANGE")

	PORT_START("KEY7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)                            PORT_CHAR(UCHAR_MAMEKEY(F2))          PORT_NAME("REPTRY")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)                             PORT_CHAR('e') PORT_CHAR('E')         PORT_NAME("E MAR")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)                             PORT_CHAR('s') PORT_CHAR('S')         PORT_NAME("S")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)                             PORT_CHAR('z') PORT_CHAR('Z')         PORT_NAME("Z")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)                         PORT_CHAR('?')

	PORT_START("KEY8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)                     PORT_CHAR(UCHAR_MAMEKEY(OPENBRACE))  PORT_NAME("MONTH")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)                             PORT_CHAR('r') PORT_CHAR('R')        PORT_NAME("R APR")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)                             PORT_CHAR('d') PORT_CHAR('D')        PORT_NAME("D")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)                             PORT_CHAR('x') PORT_CHAR('X')        PORT_NAME("X")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)                         PORT_CHAR(' ')                       PORT_NAME("SPACE")

	PORT_START("KEY9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)                            PORT_CHAR(UCHAR_MAMEKEY(F4))         PORT_NAME("NOTES")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)                             PORT_CHAR('t') PORT_CHAR('T')        PORT_NAME("T MAY")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)                             PORT_CHAR('f') PORT_CHAR('F')        PORT_NAME("F JUL")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)                             PORT_CHAR('c') PORT_CHAR('C')        PORT_NAME("C")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)                            PORT_CHAR(UCHAR_MAMEKEY(F3))         PORT_NAME("NAME")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)                             PORT_CHAR('y') PORT_CHAR('Y')        PORT_NAME("Y JUN")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)                             PORT_CHAR('g') PORT_CHAR('G')        PORT_NAME("G AUG")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)                             PORT_CHAR('v') PORT_CHAR('V')        PORT_NAME("V")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED) // >

	PORT_START("KEY11")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)                          PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)                             PORT_CHAR('u') PORT_CHAR('U')        PORT_NAME("U")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)                             PORT_CHAR('h') PORT_CHAR('H')        PORT_NAME("H SEP")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)                             PORT_CHAR('b') PORT_CHAR('B')        PORT_NAME("B")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)                          PORT_CHAR(UCHAR_MAMEKEY(LEFT))

	PORT_START("KEY12")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)                    PORT_CHAR(UCHAR_MAMEKEY(CLOSEBRACE)) PORT_NAME("DAY")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)                             PORT_CHAR('i') PORT_CHAR('I')        PORT_NAME("I")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)                             PORT_CHAR('j') PORT_CHAR('J')        PORT_NAME("J OCT")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)                             PORT_CHAR('n') PORT_CHAR('N')        PORT_NAME("N")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)                            PORT_CHAR(UCHAR_MAMEKEY(UP))

	PORT_START("KEY13")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)                         PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)                             PORT_CHAR('o') PORT_CHAR('O')        PORT_NAME("O")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)                             PORT_CHAR('k') PORT_CHAR('K')        PORT_NAME("K NOV")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)                             PORT_CHAR('m') PORT_CHAR('M')        PORT_NAME("M")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)                                                                PORT_NAME("TODAY")

	PORT_START("KEY14")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_END)                           PORT_CHAR(UCHAR_MAMEKEY(END))        PORT_NAME("FINISH")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)                             PORT_CHAR('p') PORT_CHAR('P')        PORT_NAME("P")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)                             PORT_CHAR('l') PORT_CHAR('L')        PORT_NAME("L DEC")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)                         PORT_CHAR(',')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RCONTROL)                      PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))   PORT_NAME("COPY") // =

	PORT_START("KEY15")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END


void executel_state::machine_start()
{
	m_hfree_led.resolve();
	m_vdata_led.resolve();

	m_rombank->configure_entries(0, 3, memregion("maincpu")->base() + 0x4000, 0x4000);
}

void executel_state::machine_reset()
{
	m_rombank->set_entry(0);

	m_keycol = 0;
}


uint8_t executel_state::kbd_p1_r()
{
	return m_kbd[m_keycol]->read();
}

uint8_t executel_state::kbd_p2_r()
{
	return 0xff;
}

void executel_state::kbd_p2_w(uint8_t data)
{
	m_keycol = data & 0x0f;

	//if (m_keycol == 0x0f) set CTS

	m_hfree_led = BIT(data, 4);
	m_vdata_led = BIT(data, 5);
}


uint8_t executel_state::tape_p1_r()
{
	uint8_t data = 0x00;

	data |=  m_mdcr->bet() << 0;
	data |= !m_mdcr->cip() << 1;
	data |= !m_mdcr->wen() << 2;

	return data;
}

uint8_t executel_state::tape_p2_r()
{
	return 0xff;
}

void executel_state::tape_p2_w(uint8_t data)
{
	m_mdcr->wda(!BIT(data, 2));
	m_mdcr->wdc(!BIT(data, 3));

	m_maincpu->set_input_line(I8085_RST55_LINE, BIT(data, 4, 2) ? 0 : 1);

	m_mdcr->rev(!BIT(data, 6));
	m_mdcr->fwd(!BIT(data, 7));
}


void executel_state::bank_w(uint8_t data)
{
	//logerror("bank_w: %d\n", data);
	m_rombank->set_entry(data & 3);
}


void executel_state::executel(machine_config &config)
{
	I8085A(config, m_maincpu, 6_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &executel_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &executel_state::io_map);
	//m_maincpu->in_sid_func().set(FUNC(executel_state::sid_r));
	//m_maincpu->out_sod_func().set(FUNC(executel_state::sod_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(6_MHz_XTAL, 384, 0, 240, 313, 0, 240);
	screen.set_screen_update("vdg", FUNC(mr9735_002_device::screen_update));
	screen.screen_vblank().set_inputline(m_maincpu, I8085_RST65_LINE);

	PALETTE(config, "palette", palette_device::RGB_3BIT);

	mr9735_002_device &vdg(MR9735_002(config, "vdg", 6_MHz_XTAL));
	vdg.data_cb().set([this](offs_t offset) { return ~m_videoram[offset & 0x3ff]; });
	vdg.set_interlace(false);

	CLOCK(config, "1200hz", 1200).signal_handler().set_inputline("maincpu", I8085_RST75_LINE); // 1.2kHz output from MR9735

	saa5070_device &viop(SAA5070(config, "viop", 6_MHz_XTAL / 6));
	//viop.readpa_handler().set([this]() { logerror("viop_pa_r: 00\n"); return 0x00; });
	//viop.writepa_handler().set([this](uint8_t data) { logerror("viop_pa_w: %02x\n", data); });
	//viop.readpb_handler().set([this]() { logerror("viop_pb_r: 00\n"); return 0x00; });
	//viop.writepb_handler().set([this](uint8_t data) { logerror("viop_pb_w: %02x\n", data); });
	viop.txdata_handler().set("modem", FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232(RS232_PORT(config, "modem", default_rs232_devices, nullptr)); // SAA5070 (internal modem)
	rs232.rxd_handler().set("viop", FUNC(saa5070_device::write_rxdata));
	rs232.dcd_handler().set("viop", FUNC(saa5070_device::write_cardet));

	MM58174(config, "rtc", 32.768_kHz_XTAL);

	I8741A(config, m_keyb, 3.579545_MHz_XTAL);
	m_keyb->p1_in_cb().set(FUNC(executel_state::kbd_p1_r));
	m_keyb->p2_in_cb().set(FUNC(executel_state::kbd_p2_r));
	m_keyb->p2_out_cb().set(FUNC(executel_state::kbd_p2_w));
	m_keyb->t0_in_cb().set_constant(1);
	m_keyb->t1_in_cb().set_constant(1);

	I8741A(config, m_tape, 3.579545_MHz_XTAL);
	m_tape->p1_in_cb().set(FUNC(executel_state::tape_p1_r));
	m_tape->p2_in_cb().set(FUNC(executel_state::tape_p2_r));
	m_tape->p2_out_cb().set(FUNC(executel_state::tape_p2_w));
	m_tape->t0_in_cb().set(m_mdcr, FUNC(mdcr_device::rda)).invert();
	m_tape->t1_in_cb().set(m_mdcr, FUNC(mdcr_device::rdc)).invert();

	SPEAKER(config, "mono").front_center();

	MDCR(config, m_mdcr);
}


ROM_START(executel)
	ROM_REGION(0x10000, "maincpu", ROMREGION_ERASE00)

	ROM_SYSTEM_BIOS(0, "v111", "V1.11")
	ROMX_LOAD("711_30624_0633_01_12d.bin", 0x0000, 0x2000, CRC(d9a6eabe) SHA1(e3231192665982be7487ecd71049a70fe6e53789), ROM_BIOS(0))
	ROMX_LOAD("711_30625_0533_01_13d.bin", 0x2000, 0x2000, CRC(3865774f) SHA1(6ad4ebcc0677f05563213d96a48674ec61a9ed9f), ROM_BIOS(0))
	ROMX_LOAD("711_30626_0833_01_14d.bin", 0x4000, 0x2000, CRC(6fb2d323) SHA1(2e995c268b834f87803e23c3cb4f87fd834158b2), ROM_BIOS(0))
	ROMX_LOAD("711_30627_0633_01_15d.bin", 0x6000, 0x2000, CRC(20aeac5f) SHA1(0ff9615a1ea7f51dce61f307d7179ae134e904e0), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "v19", "V1.9") // unreleased US version 'Buckingham'
	ROMX_LOAD("v1-9_us_1.bin", 0x0000, 0x2000, CRC(8b2a568b) SHA1(5c5396864f5effd4bff3ad1d2168da93604b9a17), ROM_BIOS(1))
	ROMX_LOAD("v1-9_us_2.bin", 0x2000, 0x2000, CRC(347fa3ea) SHA1(1a01c026070bb70837e2b51f32f761d25d3dcb43), ROM_BIOS(1))
	ROMX_LOAD("v1-9_us_3.bin", 0x4000, 0x2000, CRC(445a7954) SHA1(f4d06a3d43425728cb6ead6b1ce581b171f43476), ROM_BIOS(1))
	ROMX_LOAD("v1-9_us_4.bin", 0x6000, 0x2000, CRC(a3cbb691) SHA1(65399c979bc94b7666fc6f4a81cc640532f7bfb9), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "v21", "V2.1")
	ROMX_LOAD("711_31191_0033_12d.bin", 0x0000, 0x4000, CRC(5cdcf42f) SHA1(442348578b965b9c5d63348a0c9088b2de69c9d7), ROM_BIOS(2))
	ROMX_LOAD("711_31192_0033_13d.bin", 0x4000, 0x4000, CRC(d40ce85d) SHA1(9d04318ec69a853c5c316e2a151beecae96df817), ROM_BIOS(2))
	ROMX_LOAD("711_31193_0033_14d.bin", 0x8000, 0x4000, CRC(338f17db) SHA1(3019ec6ea94f379d375490c0387bbacc62878832), ROM_BIOS(2))

	ROM_SYSTEM_BIOS(3, "v41", "V4.1")
	ROMX_LOAD("executel_6-4-83_v4-1_1.bin", 0x0000, 0x2000, CRC(bb3290e4) SHA1(75276a1953ba1443cfc5dd6ee461300c337b87e6), ROM_BIOS(3))
	ROMX_LOAD("executel_6-4-83_v4-1_2.bin", 0x2000, 0x2000, CRC(72763244) SHA1(2946ea80923c8d91a6d3f99e89c7b50781d7c327), ROM_BIOS(3))
	ROMX_LOAD("executel_6-4-83_v4-1_3.bin", 0x4000, 0x2000, CRC(351f1288) SHA1(c3d71a5f2667a577e6b00e90d5fb0849097d4451), ROM_BIOS(3))
	ROMX_LOAD("executel_6-4-83_v4-1_4.bin", 0x6000, 0x2000, CRC(2e054911) SHA1(d6e7f30a5110c7dc23b1ef0d24d67cc8d14e8d72), ROM_BIOS(3))

	ROM_SYSTEM_BIOS(4, "v6", "V6")
	ROMX_LOAD("12d_v6.bin", 0x0000, 0x2000, CRC(a8fd2a66) SHA1(146432d45e6c33e134887ebfd127e124d807fc32), ROM_BIOS(4))
	ROM_IGNORE(0x2000)
	ROMX_LOAD("13d_v6.bin", 0x2000, 0x2000, CRC(bb0d431e) SHA1(9e6878c2f8ba6b2363e6d69686026affb513397d), ROM_BIOS(4))
	ROM_IGNORE(0x2000)
	ROMX_LOAD("14d_v6.bin", 0x4000, 0x2000, CRC(e78d766b) SHA1(8eb192cd70dcf9c62743054e5466c92a4ca60f26), ROM_BIOS(4))
	ROM_IGNORE(0x2000)
	ROMX_LOAD("15d_v6.bin", 0x6000, 0x2000, CRC(bb0d431e) SHA1(9e6878c2f8ba6b2363e6d69686026affb513397d), ROM_BIOS(4))
	ROM_IGNORE(0x2000)

	ROM_REGION(0x400, "keyb", 0)
	ROM_LOAD("d8741a_kbd.bin", 0x0000, 0x0400, CRC(1cb00127) SHA1(28e61ea9a924e0caf4f3a107c5a3667adbbbfcbc))

	ROM_REGION(0x400, "tape", 0)
	ROM_LOAD("d8741a_tape.bin", 0x0000, 0x0400, CRC(06e350e9) SHA1(b5e84f8cab8daefe0bbcb2a1c6da6de8d29525a5))

	ROM_REGION(0x100, "prom", 0) // used for ROM enable
	ROM_LOAD("dm74s287n.bin", 0x0000, 0x0100, CRC(b7086783) SHA1(452c7b1d20bf00276b2f876ef09eb28bc4a34c8c))
ROM_END

} // anonymous namespace


//    YEAR  NAME       PARENT  COMPAT  MACHINE     INPUT      CLASS           INIT        COMPANY                         FULLNAME              FLAGS
COMP( 1984, executel,  0,      0,      executel,   executel,  executel_state, empty_init, "STC Telecommunications Ltd.",  "STC 3910 Executel",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



stmpc.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    STM PC

    All-in-One IBM PC compatible portable computer

    Hardware (incomplete):
    - Intel 186 at 8 MHz
    - 256 KB RAM (expandable to 512 KB)
    - SCN2674B
    - NEC D8251AC
    - Monochrome LCD display with 80x16 lines (512x128 resolution)
    - External monitor support (RGB, composite)
    - Canon MDD413 dual 5.25" QD drive
    - SCSI interface
    - 2x RS232 port
    - Centronics port
    - Internal thermal printer
    - 83-key keyboard with RJ-11 connector
    - OS: MS-DOS 2.11

    TODO:
    - Everything

***************************************************************************/

#include "emu.h"
#include "cpu/i86/i186.h"
#include "machine/clock.h"
#include "video/scn2674.h"
#include "emupal.h"
#include "screen.h"


namespace {


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class stmpc_state : public driver_device
{
public:
	stmpc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_avdc(*this, "avdc")
	{ }

	void stmpc(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	SCN2674_DRAW_CHARACTER_MEMBER(draw_character);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void char_map(address_map &map) ATTR_COLD;

	required_device<i80186_cpu_device> m_maincpu;
	required_device<scn2674_device> m_avdc;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void stmpc_state::mem_map(address_map &map)
{
	map(0x00000, 0x3ffff).ram();
	map(0xb8000, 0xbffff).ram();
	map(0xfc000, 0xfffff).rom().region("maincpu", 0);
}

void stmpc_state::io_map(address_map &map)
{
	map(0x0700, 0x070f).rw(m_avdc, FUNC(scn2674_device::read), FUNC(scn2674_device::write)).umask16(0x00ff);
}

void stmpc_state::char_map(address_map &map)
{
	map(0x0000, 0xffff).nopr();
}


//**************************************************************************
//  INPUT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( stmpc )
INPUT_PORTS_END


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

static const gfx_layout char_layout_8x8 =
{
	8, 8,
	RGN_FRAC(1, 4),
	1,
	{ 0 },
	{ STEP8(0, 1) },
	{ STEP8(0, 8) },
	8 * 8
};

static const gfx_layout char_layout_8x10 =
{
	8, 10,
	RGN_FRAC(1, 4),
	1,
	{ 0 },
	{ STEP8(0, 1) },
	{ RGN_FRAC(1,4) + 0*8, RGN_FRAC(1,4) + 1*8, RGN_FRAC(1,4) + 2*8, RGN_FRAC(1,4) + 3*8,
	  RGN_FRAC(1,4) + 4*8, RGN_FRAC(1,4) + 5*8, RGN_FRAC(1,4) + 6*8, RGN_FRAC(1,4) + 7*8,
	  RGN_FRAC(3,4) + 6*8, RGN_FRAC(3,4) + 7*8 },
	8 * 8
};

static const gfx_layout char_layout_8x14 =
{
	8, 14,
	RGN_FRAC(1, 4),
	1,
	{ 0 },
	{ STEP8(0, 1) },
	{ RGN_FRAC(2,4) + 0*8, RGN_FRAC(2,4) + 1*8, RGN_FRAC(2,4) + 2*8, RGN_FRAC(2,4) + 3*8,
	  RGN_FRAC(2,4) + 4*8, RGN_FRAC(2,4) + 5*8, RGN_FRAC(2,4) + 6*8, RGN_FRAC(2,4) + 7*8,
	  RGN_FRAC(3,4) + 0*8, RGN_FRAC(3,4) + 1*8, RGN_FRAC(3,4) + 2*8, RGN_FRAC(3,4) + 3*8,
	  RGN_FRAC(3,4) + 4*8, RGN_FRAC(3,4) + 5*8 },
	8 * 8
};

static GFXDECODE_START(chars)
	GFXDECODE_ENTRY("chargen", 0, char_layout_8x8, 0, 1)
	GFXDECODE_ENTRY("chargen", 0, char_layout_8x10, 0, 1)
	GFXDECODE_ENTRY("chargen", 0, char_layout_8x14, 0, 1)
GFXDECODE_END

SCN2674_DRAW_CHARACTER_MEMBER(stmpc_state::draw_character)
{
}


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void stmpc_state::machine_start()
{
}

void stmpc_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void stmpc_state::stmpc(machine_config &config)
{
	I80186(config, m_maincpu, 8000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &stmpc_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &stmpc_state::io_map);

	PALETTE(config, "palette", palette_device::MONOCHROME);

	GFXDECODE(config, "gfxdecode", "palette", chars);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_raw(16000000, 1344, 0, 1280, 113, 0, 104);
	screen.set_screen_update(m_avdc, FUNC(scn2674_device::screen_update));

	CLOCK(config, "tmrin1", 20000).signal_handler().set(m_maincpu, FUNC(i80186_cpu_device::tmrin1_w)); // FIXME: figure out the actual source of this

	SCN2674(config, m_avdc, 16000000 / 8);
	m_avdc->set_screen("screen");
	m_avdc->set_character_width(8);
	m_avdc->set_addrmap(0, &stmpc_state::char_map);
	m_avdc->set_display_callback(FUNC(stmpc_state::draw_character));
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( stmpc )
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_LOAD16_BYTE("200000890_rev_2_even.u98", 0x00000, 0x2000, CRC(5e6b9212) SHA1(4790d4adee408ba85fcb6b1c52d59c2a5217a25a))
	ROM_LOAD16_BYTE("200000889_rev_2_odd.u126", 0x00001, 0x2000, CRC(90b12ca0) SHA1(19a775562c42b3e9972d4f156a3538b70e3671e8))

	ROM_REGION(0x2000, "chargen", 0)
	 // might be a BAD_DUMP, missing data compared to rev a below?
	ROM_LOAD("200000447_rev_b.u50", 0x0000, 0x2000, CRC(15f3a2d1) SHA1(cf061b596cbec96195ea6224ab6295f4df1fabeb))
ROM_END

// The dumper notes that this might be a prototype or early field trial version
ROM_START( stmpcp )
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_LOAD16_BYTE("200000446_rev_e_even.u98", 0x00000, 0x2000, CRC(168cdf3e) SHA1(a2d7524ceabbaeca1f1bb17b015ddb42946bcf34))
	ROM_LOAD16_BYTE("200000445_rev_e_odd.u126", 0x00001, 0x2000, CRC(9d86f7ed) SHA1(7367697505f256cd82569865c39b208fe16a233f))

	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD("200000447_rev_a.u50", 0x0000, 0x2000, CRC(4a6001a2) SHA1(36c10195f0655bdc8e10ceee06d176cebeb7383d))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY            FULLNAME               FLAGS
COMP( 1984, stmpc,  0,      0,      stmpc,   stmpc, stmpc_state, empty_init, "STM Electronics", "STM PC",              MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1984, stmpcp, stmpc,  0,      stmpc,   stmpc, stmpc_state, empty_init, "STM Electronics", "STM PC (prototype?)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



storio.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/******************************************************************************

    VTech Storio and VTech V.Reader
    Main processor: Nuvoton W55FA9363SDN (ARM926EJ-S CPU core)
        http://www.dingsung.com.cn/download/n32905/1507301944.pdf

    Storio (Europe) and V.Reader (North America) cartridges are physically
    different, but there is no software region lock, so you can play
    Storio games on the V.Reader (and viceversa) by modifying the game's
    plastic cartrige (so it can fit in).

    Skeleton driver, to reference Software List so that it gets validated

    TODO: everything!

    NAND types

    Storio Spanish old BIOS TC58NVG0S3ETA00 (2048+64) x 64 x 1024

*******************************************************************************/

#include "emu.h"

#include "cpu/arm7/arm7.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class vtech_storio_state : public driver_device
{
public:
	vtech_storio_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_cart(*this, "cartslot")
		, m_cart_region(nullptr)
	{ }

	void vtech_storio(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void vtech_storio_base(machine_config &config);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	required_device<cpu_device> m_maincpu;

	required_device<screen_device> m_screen;
	required_device<generic_slot_device> m_cart;
	memory_region *m_cart_region;

	uint32_t screen_update_storio(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};

uint32_t vtech_storio_state::screen_update_storio(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void vtech_storio_state::machine_start()
{
	// if there's a cart, override the standard mapping
	if (m_cart && m_cart->exists())
	{
		std::string region_tag;
		m_cart_region = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());
	}
}

void vtech_storio_state::machine_reset()
{
}

DEVICE_IMAGE_LOAD_MEMBER(vtech_storio_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

static INPUT_PORTS_START( vtech_storio )
INPUT_PORTS_END


void vtech_storio_state::vtech_storio_base(machine_config &config)
{
	ARM9(config, m_maincpu, 240000000); // ARM926EJ-S CPU core (probably 240MHz, but not sure)

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320, 262);
	m_screen->set_visarea(0, 320-1, 0, 240-1);
	m_screen->set_screen_update(FUNC(vtech_storio_state::screen_update_storio));

	SPEAKER(config, "speaker", 2).front();

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "vtech_storio_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(vtech_storio_state::cart_load));

}

void vtech_storio_state::vtech_storio(machine_config &config)
{
	vtech_storio_base(config);
	SOFTWARE_LIST(config, "cart_list").set_original("vtech_storio_cart");
}

// BIOS is 1 GBIT (128M × 8 BIT) CMOS NAND EEPROM (Toshiba TC58NVG0S3ETA00)

// ROM image from VTech, not padded to the real ROM size
ROM_START( vreader )
	ROM_REGION( 0x038e906c, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "useng-pack_20111017.bin", 0x000000, 0x038e906c, CRC(add3f7e5) SHA1(43ecfb0ba3c98c5852f93ed620021697167aa156) )
ROM_END

// ROM image from VTech, not padded to the real ROM size
ROM_START( vreadercaen )
	ROM_REGION( 0x038e906c, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "caeng-pack_20111017.bin", 0x000000, 0x038e906c, CRC(0b64caf3) SHA1(79648e2b315c59f60aaf8cb8806fdbe773e484a2) )
ROM_END

// ROM image from VTech, not padded to the real ROM size
ROM_START( vreadercafr )
	ROM_REGION( 0x037d93a6, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "cafre-pack_20111017.bin", 0x000000, 0x037d93a6, CRC(d3e0039c) SHA1(3d69f4afcf56ba40261bba0af335680c3c05b319) )
ROM_END

// ROM image from VTech, not padded to the real ROM size
ROM_START( storio )
	ROM_REGION( 0x01bf3dcb, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "gbeng-pack_20111017.bin", 0x000000, 0x01bf3dcb, CRC(b0962d2c) SHA1(4f316cbcc87ae24022568a358ac94c7b4cac39a6) )
ROM_END

// ROM image from VTech, not padded to the real ROM size
ROM_START( storiode )
	ROM_REGION( 0x03740a0d, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "deger-pack_20111017.bin", 0x000000, 0x03740a0d, CRC(548c8882) SHA1(e64474be082bd3ae3c365c6c766b2ec5081f3ebd) )
ROM_END

// ROM image from VTech, not padded to the real ROM size
ROM_START( storioes )
	ROM_REGION( 0x03c62bfc, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "esspa-pack_20111017.bin", 0x000000, 0x03c62bfc, CRC(fe9b78f9) SHA1(c114a8f82799861a0cca432ee145e436aca5f400) )
ROM_END

// ROM image dumped from a real Spanish VTech Storio console.
// Seems to be the "2011.06.17" compilation, although there is a "Copyright (c) 2009 - 2012 Nuvoton" text on the ROM
ROM_START( storioesa )
	ROM_REGION( 0x08400000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "storiospanisholdbios.bin", 0x000000, 0x08400000, CRC(c462cac4) SHA1(37e5497342a3a27366288b5c5dffd00d0826e183) )
ROM_END

// ROM image from VTech, not padded to the real ROM size
ROM_START( storiofr )
	ROM_REGION( 0x038c2a19, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "frfre-pack_20111017.bin", 0x000000, 0x038c2a19, CRC(f3d87f50) SHA1(240ddebd4cb1c4be24afb4da35c65ddf64628034) )
ROM_END

// ROM image from VTech, not padded to the real ROM size
ROM_START( storionl )
	ROM_REGION( 0x03af81c6, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "nldut-pack_20111017.bin", 0x000000, 0x03af81c6, CRC(6cfac599) SHA1(d16b45fd287c9d823bde13b88eb6c8158ac2b475) )
ROM_END

} // anonymous namespace


//    year, name,         parent,  compat, machine,      input,        class,              init,       company,  fullname,                             flags
CONS( 2011, vreader,      0,       0,      vtech_storio, vtech_storio, vtech_storio_state, empty_init, "VTech", "V.Reader (US, English, 2011-10-17)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2011, vreadercaen,  vreader, 0,      vtech_storio, vtech_storio, vtech_storio_state, empty_init, "VTech", "V.Reader (CA, English, 2011-10-17)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2011, vreadercafr,  vreader, 0,      vtech_storio, vtech_storio, vtech_storio_state, empty_init, "VTech", "V.Reader (CA, French, 2011-10-17)",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2011, storio,       vreader, 0,      vtech_storio, vtech_storio, vtech_storio_state, empty_init, "VTech", "Storio (GB, English, 2011-10-17)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2011, storiode,     vreader, 0,      vtech_storio, vtech_storio, vtech_storio_state, empty_init, "VTech", "Storio (DE, German, 2011-10-17)",    MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2011, storioes,     vreader, 0,      vtech_storio, vtech_storio, vtech_storio_state, empty_init, "VTech", "Storio (ES, Spanish, 2011-10-17)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2011, storioesa,    vreader, 0,      vtech_storio, vtech_storio, vtech_storio_state, empty_init, "VTech", "Storio (ES, Spanish, 2011-06-17?)",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2011, storiofr,     vreader, 0,      vtech_storio, vtech_storio, vtech_storio_state, empty_init, "VTech", "Storio (FR, French, 2011-10-17)",    MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2011, storionl,     vreader, 0,      vtech_storio, vtech_storio, vtech_storio_state, empty_init, "VTech", "Storio (NL, Dutch, 2011-10-17)",     MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



stratos.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert, hap
// thanks-to:Berger
/*******************************************************************************

SciSys/Saitek Stratos chesscomputer family (1987-1990)
(SciSys renamed themselves to Saitek in 1987)

- Stratos
- Turbo King
- Corona --> corona.cpp

IMPORTANT: The user is expected to press the STOP button to turn off the computer.
When not using -autosave, press that button before exiting MAME, or NVRAM can get
corrupt. If that happens, the chesscomputer will become unresponsive on next boot.
To force a cold boot, hold the PLAY button and trigger a power on/reset (F3).

TODO:
- emulate LCD at lower level, probably an MCU with embedded LCDC
- LCD status bit handling is guessed. stratos expects it to be high after lcd
  command 0xf, but tking2 won't work if it's done that way, and corona differs too
- irq timing is derived from the main XTAL, but result should be similar with 5MHz
  and 5.67MHz, there are a couple of "FREQ. SEL" nodes on the PCB, maybe related
  (not the ones in input ports). irq source should be from HELIOS pin 2
- tking(old revisions) and stratos slow responsive buttons, related to irq timing,
  but if that's changed, the led blinking and in-game clock is too fast
- does nvram.u7 work? it's cleared during boot, but not used after

================================================================================

Hardware notes:
- W65C02 or R65C02 at 5MHz or ~5.6MHz (for latter, box says 6MHz but that's a marketing lie)
- 2*32KB ROM + optional 32KB Endgame ROM sold separately
- 8KB RAM + another 8KB RAM(latter not populated on every PCB)
- NEC gate array for all I/O, Saitek calls it HELIOS
- side leds are tri-color (red + green, combined = yellow)
- unknown LCDC under epoxy blob, suspected to be an MCU

Stratos/Turbo King are identical.
Corona has magnet sensors and two NEC gate arrays.

There is no official Saitek program versioning for these. The D/D+ versions are
known since they're the same chess engine as later Saitek modules, such as the
Analyst module. Likewise, officially there isn't a "Turbo King II" or "Corona II",
these 'sequels' are titled as such by the chesscomputer community. Saitek simply
advertised them as an improved program.

The initial Stratos/Turbo King (PRG ROM labels known: M,K,L,P) are probably
engine version B, very few bytes difference between revisions. The first Corona
is engine version C.

*******************************************************************************/

#include "emu.h"
#include "stratos.h"

#include "cpu/m6502/w65c02.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "saitek_stratos.lh"
#include "saitek_tking.lh"


class stratos_state : public stratos_base_state
{
public:
	stratos_state(const machine_config &mconfig, device_type type, const char *tag) :
		stratos_base_state(mconfig, type, tag),
		m_banked_nvram(*this, "nvram.u7", 0x2000, ENDIANNESS_LITTLE),
		m_nvrambank(*this, "nvrambank"),
		m_rombank(*this, "rombank"),
		m_extrom(*this, "extrom"),
		m_board(*this, "board"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	int lcd_ready_r() { return m_lcd_ready ? 1 : 0; }

	// machine configs
	void stratos(machine_config &config);
	void tking(machine_config &config);
	void tking2(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	memory_share_creator<u8> m_banked_nvram;
	required_memory_bank m_nvrambank;
	required_memory_bank m_rombank;
	required_device<generic_slot_device> m_extrom;
	required_device<sensorboard_device> m_board;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<8+2> m_inputs;

	u8 m_select = 0;
	u8 m_control = 0;
	u8 m_led_data = 0;

	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_leds();
	void select_w(u8 data);
	u8 chessboard_r();
	void sound_w(u8 data);
	void leds_w(u8 data);
	u8 control_r();
	void control_w(u8 data);
	u8 lcd_data_r();
	u8 extrom_r(offs_t offset);
};


// stratos_base_state

void stratos_base_state::machine_start()
{
	// resolve outputs
	m_out_digit.resolve();
	m_out_lcd.resolve();

	// register for savestates
	save_item(NAME(m_power));
	save_item(NAME(m_lcd_ready));
	save_item(NAME(m_lcd_count));
	save_item(NAME(m_lcd_command));
	save_item(NAME(m_lcd_data));
}

void stratos_base_state::machine_reset()
{
	m_power = true;
	m_lcd_ready = false;
	m_lcd_count = 0;
	clear_lcd();
}

INPUT_CHANGED_MEMBER(stratos_base_state::change_cpu_freq)
{
	// known officially* released CPU speeds: 5MHz, 5.626MHz, 5.67MHz
	// *not including reseller overclocks, user mods, or the "Turbo Kit"
	static const XTAL xtal[3] = { 5_MHz_XTAL, 5.626_MHz_XTAL, 5.67_MHz_XTAL };
	m_maincpu->set_unscaled_clock(xtal[newval % 3]);
}


// stratos_state

void stratos_state::machine_start()
{
	stratos_base_state::machine_start();

	// init banks
	m_rombank->configure_entries(0, 2, memregion("maincpu")->base(), 0x8000);
	m_nvrambank->configure_entries(0, 2, m_banked_nvram, 0x1000);

	// register for savestates
	save_item(NAME(m_select));
	save_item(NAME(m_control));
	save_item(NAME(m_led_data));
}

void stratos_state::machine_reset()
{
	stratos_base_state::machine_reset();

	m_control = 0;
	m_rombank->set_entry(0);
	m_nvrambank->set_entry(0);
}



/*******************************************************************************
    I/O
*******************************************************************************/

// soft power on/off

INPUT_CHANGED_MEMBER(stratos_base_state::go_button)
{
	if (newval && !m_power)
	{
		m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
		machine_reset();
	}
}

void stratos_base_state::power_off()
{
	m_power = false;
	m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);

	// clear display
	m_display->clear();
	clear_lcd();
	update_lcd();
}


// LCD HLE

void stratos_base_state::update_lcd()
{
	// output individual segments
	for (int i = 0; i < 0x40; i++)
		for (int j = 0; j < 4; j++)
			m_out_lcd[i >> 4][i & 0xf][j] = BIT(m_lcd_data[i], j);

	m_out_digit[0] = (m_lcd_data[0] & 4) ? 6 : 0; // "1"

	// upper digits (colon is at 0x00)
	for (int i = 0; i < 4; i++)
		m_out_digit[i + 1] = (m_lcd_data[0x01 + i * 2] << 4 | m_lcd_data[0x01 + i * 2 + 1]) & 0x7f;

	// lower digits (colon is at 0x10)
	for (int i = 0; i < 4; i++)
		m_out_digit[i + 5] = (m_lcd_data[0x11 + i * 2] << 4 | m_lcd_data[0x11 + i * 2 + 1]) & 0x7f;
}

void stratos_base_state::lcd_data_w(u8 data)
{
	// d0-d3: lcd data
	// d4-d7: unused?
	data &= 0xf;

	if (m_lcd_count == 0)
		m_lcd_command = data;
	else
	{
		// write to lcd row
		if (m_lcd_command > 0 && m_lcd_command <= 4)
			m_lcd_data[(((m_lcd_command - 1) << 4) + (m_lcd_count - 1)) & 0x3f] = data;
	}

	// it expects a specific number of writes for each row
	const u8 maxcount[5] = { 0, 9, 9, 1, 12 };
	if (m_lcd_command > 4 || m_lcd_count == maxcount[m_lcd_command])
	{
		// reset/start?
		if (m_lcd_command & 8)
			m_lcd_ready = true;

		m_lcd_count = 0;
		update_lcd();
	}
	else
		m_lcd_count++;
}


// HELIOS

void stratos_state::update_leds()
{
	m_display->matrix_partial(0, 2, 1 << (m_control >> 5 & 1), (~m_led_data & 0xff) | (~m_control << 6 & 0x100));
}

void stratos_state::leds_w(u8 data)
{
	// d0-d7: button led data
	m_led_data = data;
	update_leds();

	m_dac->write(0); // guessed
}

void stratos_state::sound_w(u8 data)
{
	m_dac->write(1);
}

void stratos_state::select_w(u8 data)
{
	// d0-d3: input/led mux
	// d4-d7: chessboard led data
	m_select = data;
	m_display->matrix_partial(2, 4, ~m_select >> 4 & 0xf, 1 << (m_select & 0xf));
}

u8 stratos_state::chessboard_r()
{
	// d0-d7: chessboard sensors
	return ~m_board->read_file(m_select & 0xf);
}

u8 stratos_state::control_r()
{
	u8 data = 0;
	u8 sel = m_select & 0xf;

	if (sel == 8)
	{
		// d5: lcd status flag?
		data |= m_inputs[9]->read();

		if (!machine().side_effects_disabled())
			m_lcd_ready = false;

		// d7: battery low
		data |= m_inputs[8]->read() << 7;
	}

	// read button panel
	if (sel < 8)
		data |= m_inputs[sel]->read() << 5;

	return data;
}

void stratos_state::control_w(u8 data)
{
	u8 prev = m_control;
	m_control = data;

	// d0: main rom bank
	// d1: ext rom bank
	// d1: nvram bank
	m_rombank->set_entry(data & 1);
	m_nvrambank->set_entry(data >> 1 & 1);

	// d2: mode led state
	// d5: button led select
	update_leds();

	// d6 falling edge: power-off request
	if (~data & prev & 0x40)
		power_off();
}

u8 stratos_state::extrom_r(offs_t offset)
{
	u16 bank = BIT(m_control, 1) * 0x4000;
	return (m_extrom->exists()) ? m_extrom->read_rom(offset | bank) : 0xff;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void stratos_state::main_map(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("nvram.u6");
	map(0x2000, 0x2000).w(FUNC(stratos_state::select_w));
	map(0x2200, 0x2200).rw(FUNC(stratos_state::chessboard_r), FUNC(stratos_state::sound_w));
	map(0x2400, 0x2400).w(FUNC(stratos_state::leds_w));
	map(0x2600, 0x2600).rw(FUNC(stratos_state::control_r), FUNC(stratos_state::control_w));
	map(0x2800, 0x37ff).bankrw("nvrambank");
	map(0x3800, 0x3800).w(FUNC(stratos_state::lcd_data_w));
	map(0x4000, 0x7fff).r(FUNC(stratos_state::extrom_r));
	map(0x8000, 0xffff).bankr("rombank");
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

INPUT_PORTS_START( saitek_stratos )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Set Up")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_CUSTOM) // freq sel

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Sound")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("Stop")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("Play")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("Tab / Color")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")

	PORT_START("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_EQUALS) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Function")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_CUSTOM) // freq sel

	PORT_START("IN.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Library")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Info")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_CUSTOM) // freq sel

	PORT_START("IN.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Analysis")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Normal")

	PORT_START("IN.8")
	PORT_CONFNAME( 0x01, 0x01, "Battery Status" )
	PORT_CONFSETTING(    0x00, "Low" )
	PORT_CONFSETTING(    0x01, DEF_STR( Normal ) )

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(stratos_base_state::go_button), 0) PORT_NAME("Go")

	PORT_START("CPU")
	PORT_CONFNAME( 0x03, 0x00, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(stratos_base_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "5MHz" )
	PORT_CONFSETTING(    0x01, "5.626MHz" )
	PORT_CONFSETTING(    0x02, "5.67MHz" )
INPUT_PORTS_END

static INPUT_PORTS_START( stratos )
	PORT_INCLUDE( saitek_stratos )

	PORT_MODIFY("IN.6")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_CUSTOM)

	PORT_START("IN.9")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(stratos_state::lcd_ready_r))
INPUT_PORTS_END

static INPUT_PORTS_START( tking ) // same buttons, but different locations
	PORT_INCLUDE( stratos )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Set Up")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_L) PORT_NAME("Level")

	PORT_MODIFY("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Sound")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_J) PORT_NAME("Stop")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_K) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")

	PORT_MODIFY("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Play Normal")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Tab / Color")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")

	PORT_MODIFY("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_CODE(KEYCODE_EQUALS) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Function")

	PORT_MODIFY("IN.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Library")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Info")

	PORT_MODIFY("IN.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Analysis")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Normal")

	PORT_MODIFY("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(stratos_base_state::go_button), 0) PORT_NAME("Go")
INPUT_PORTS_END

static INPUT_PORTS_START( tking2 )
	PORT_INCLUDE( tking )

	PORT_MODIFY("IN.5")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_CUSTOM)

	PORT_MODIFY("IN.9")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_CUSTOM)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void stratos_state::stratos(machine_config &config)
{
	// basic machine hardware
	W65C02(config, m_maincpu, 5_MHz_XTAL); // see change_cpu_freq
	m_maincpu->set_addrmap(AS_PROGRAM, &stratos_state::main_map);
	m_maincpu->set_periodic_int(FUNC(stratos_state::irq0_line_hold), attotime::from_hz(76));

	NVRAM(config, "nvram.u6", nvram_device::DEFAULT_ALL_0);
	NVRAM(config, "nvram.u7", nvram_device::DEFAULT_ALL_0);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(350));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(2+4, 8+1);
	config.set_default_layout(layout_saitek_stratos);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);

	// extension rom
	GENERIC_SOCKET(config, "extrom", generic_plain_slot, "saitek_egr");
	SOFTWARE_LIST(config, "cart_list").set_original("saitek_egr");
}

void stratos_state::tking(machine_config &config)
{
	stratos(config);
	config.set_default_layout(layout_saitek_tking);
}

void stratos_state::tking2(machine_config &config)
{
	tking(config);
	m_maincpu->set_periodic_int(FUNC(stratos_state::irq0_line_hold), attotime::from_hz(107));

	// seems much more responsive (not just because of higher irq rate)
	m_board->set_delay(attotime::from_msec(200));
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( stratos )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("w1yo1f_728m_u3.u3", 0x0000, 0x8000, CRC(b58a7256) SHA1(75b3a3a65f4ca8d52aa5b17a06319bff59d9014f) )
	ROM_LOAD("bw1_819n_u4.u4", 0x8000, 0x8000, CRC(cb0de631) SHA1(f78d40213be21775966cbc832d64acd9b73de632) )
ROM_END

ROM_START( stratosa )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("w1yo1f_728l_u3.u3", 0x0000, 0x8000, CRC(19a22058) SHA1(a5ca54d870c70b7ce9c7be2951800bf49cc57527) )
	ROM_LOAD("bw1_819n_u4.u4", 0x8000, 0x8000, CRC(cb0de631) SHA1(f78d40213be21775966cbc832d64acd9b73de632) )
ROM_END


ROM_START( tking ) // PCB rev. 10
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("yo1f_713d_u3.u3", 0x0000, 0x8000, CRC(b8c6d853) SHA1(98923f44bbbd2ea17c269850971d3df229e6057e) )
	ROM_LOAD("yo1f_712a_u4.u4", 0x8000, 0x8000, CRC(7d3f8f7b) SHA1(8be5d8d988ff0577ccfec0a773bfd94599f2534f) )
ROM_END

ROM_START( tkinga ) // PCB rev. 7
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("w1yo1f_728p_u3.u3", 0x0000, 0x8000, CRC(ad77f83e) SHA1(598fdb1e40267d9d43a3d8f287723070b9afa349) )
	ROM_LOAD("yo1f-b_819o_u4.u4", 0x8000, 0x8000, CRC(336040d4) SHA1(aca662b8cc4d6bafd61ca158c768ba8896117169) )
ROM_END

ROM_START( tkingb ) // PCB rev. 3, also PCB rev. 5
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("w1yo1f_728l_u3.u3", 0x0000, 0x8000, CRC(19a22058) SHA1(a5ca54d870c70b7ce9c7be2951800bf49cc57527) )
	ROM_LOAD("yo1f-b_819o_u4.u4", 0x8000, 0x8000, CRC(336040d4) SHA1(aca662b8cc4d6bafd61ca158c768ba8896117169) )
ROM_END



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT   COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1987, stratos,  0,       0,      stratos, stratos, stratos_state, empty_init, "SciSys / Heuristic Software", "Kasparov Stratos (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1987, stratosa, stratos, 0,      stratos, stratos, stratos_state, empty_init, "SciSys / Heuristic Software", "Kasparov Stratos (set 2)", MACHINE_SUPPORTS_SAVE )

SYST( 1990, tking,    stratos, 0,      tking2,  tking2,  stratos_state, empty_init, "Saitek / Heuristic Software", "Kasparov Turbo King (ver. D)", MACHINE_SUPPORTS_SAVE ) // aka Turbo King II
SYST( 1988, tkinga,   stratos, 0,      tking,   tking,   stratos_state, empty_init, "Saitek / Heuristic Software", "Kasparov Turbo King (ver. B, set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1988, tkingb,   stratos, 0,      tking,   tking,   stratos_state, empty_init, "Saitek / Heuristic Software", "Kasparov Turbo King (ver. B, set 2)", MACHINE_SUPPORTS_SAVE )



studio2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

RCA Studio II

PCB Layout
----------

1809746-S1-E

|----------------------------------------------------------------------------------------------------------|
|                                  |----------------------------|                                          |
|                                  |----------------------------|                          CA555   7805    |
|      SPKR                                     CN1                                                        |
|                                                                                                          |
|                                                                                           |-----------|  |
|                                                                                           | Output    |  |
|   ROM.4  ROM.3  ROM.2  ROM.1  CDP1822  CDP1822  CDP1822  CDP1822                          |TV Module  |  |
|                                                                                           |           |  |
|                                                                 CDP1802  TA10171V1        |           |  |
|                                                                                           |           |  |
|                                                                                           |           |  |
|                                                                                           |           |  |
|                                                                                           |-----------|  |
|                                                                                                          |
|                       CD4042  CD4001  CD4515                                                             |
|                                                                                                          |
|      CN2                                                                                       CN3       |
|----------------------------------------------------------------------------------------------------------|

Notes:
      All IC's shown.

      CDP1802 - RCA CDP1802CE Microprocessor
      TA10171V1 - RCA TA10171V1 NTSC Video Display Controller (VDC) (= RCA CDP1861)
      CDP1822 - RCA CDP1822NCE 256 x4 RAM (= Mitsubishi M58721P)
      ROM.x   - RCA CDP1831CE 512 x8 mask ROM. All ROMs are marked 'PROGRAM COPYRIGHT (C) RCA CORP. 1977'
      CD4001  - 4001 Quad 2-Input NOR Buffered B Series Gate (4000-series CMOS TTL logic IC)
      CD4042  - 4042 Quad Clocked D Latch (4000-series CMOS TTL logic IC)
      CD4515  - 4515 4-Bit Latched/4-to-16 Line Decoders (4000-series CMOS TTL logic IC)
      CA555   - CA555CG General Purpose Single Bipolar Timer (= NE555)
      7805    - Voltage regulator, input 10V-35V, output +5V
      SPKR    - Loudspeaker, 8 ohms, 0.3 W
      CN1     - ROM cartridge connector, 2x22 pins, 0.154" spacing
      CN2     - Player A keypad connector, 1x12 pins
      CN3     - Player B keypad connector, 1x12 pins
*/

/*

Toshiba Visicom Console (RCA Studio II clone)
Toshiba, 1978

PCB Layout                                            Many resistors/caps
----------                                7.5VDC    / transistors in this area
                                           \/   /---|-----/
|------------------------------------------||---|-------|   |-----|C|------|
|D235  POT       TC4011     CART_SLOT           |       |   | TV Modulator |
|HEATSINK                                       |       |   |              |
|           TC4515          TC4049 TC4011 74LS08|       |   | SW (VHF Ch1) |
|DIODES(x20)                       74LS74 74LS73|       |   |    (VHF Ch2) |
|-----------|                                 3.579545MHz   |              |
            |        CDP1802                    |  POT  |   |--------------|
            |TMM331                             |       |
            |               CDP1861   TC5012 TC5012     |
            |                     TC4021 TC4021 |       |
            |                          TC5012   \-----/ |
            |          2111      2111   2111   74LS74   |
            |TC4042    2111      2111   2111   TC4011   |
            |-------------------------------------------|
Notes: (all chips shown above)
      CDP1802 - RCA CDP1802 CPU (DIP40), clock 1.7897725MHz [3.579545/2]
      CDP1861 - RCA CDP1861 Video Controller (DIP24)
                VSync - 60.4533Hz   \ (measured on pin 6)
                HSync - 15.8387kHz  /
                Clock - 223.721562kHz [3.579545/16] (measured on pin 1)
      2111    - NEC D2111AL-4 256 bytes x4 SRAM (DIP18, x6). Total 1.5k
      C       - Composite Video Output to TV from TV Modulator
      TMM331  - Toshiba TMM331AP 2k x8 mask ROM (DIP24)
                Pinout:
                           TMM331
                        |----\/----|
                     A7 |1       24| VCC
                     A8 |2       23| D0
                     A9 |3       22| D1
                    A10 |4       21| D2
                     A0 |5       20| D3
                     A1 |6       19| D4
                     A2 |7       18| D5
                     A3 |8       17| D6
                     A4 |9       16| D7
                     A5 |10      15| E0 (measured LOW)
                     A6 |11      14| E1 (NC?)
                    GND |12      13| E2 (measured LOW)
                        |----------|

      E0 - E2 are Programmable Chip Select Inputs
      TMM331 is compatible with AMI S6831A, AMD AM9217,
      Intel 2316A/8316A, MOSTEK MK31000, GI RO-3-8316,
      NATIONAL/NEC/SYNERTEK 2316A etc


Cartridges
----------

Inside is a Toshiba TMM331AP ROM, which is pin compatible with the Signetics S6831.
The cartridge to TMM331 pin connections are as follows, with cartridge pin 1 being the leftmost angled contact:

Pin 1 to ROM pins 12,13 (GND and E2)
Pin 2 to ROM pins 24,15 (VCC and E0)
Pin 3 to ROM pin 23 (D0)
Pin 4 to ROM pin 22 (D1)
Pin 5 to ROM pin 21 (D2)
Pin 6 to ROM pin 20 (D3)
Pin 7 to ROM pin 19 (D4)
Pin 8 to ROM pin 18 (D5)
Pin 9 to ROM pin 17 (D6)
Pin 10 to ROM pin 16 (D7)
Pin 11 to ROM pin 14 (E1)
Pin 12 to ROM pin 11 (A6)
Pin 13 to ROM pin 10 (A5)
Pin 14 to ROM pin 9 (A4)
Pin 15 to ROM pin 8 (A3)
Pin 16 to ROM pin 7 (A2)
Pin 17 to ROM pin 6 (A1)
Pin 18 to ROM pin 5 (A0)
Pin 19 to ROM pin 1 (A7)
Pin 20 to ROM pin 4 (A10)
Pin 21 to ROM pin 3 (A9)
Pin 22 to ROM pin 2 (A8)

*/

/*

Mustang 9016 Telespiel Computer

PCB Layout
----------

|----------------------------------------------------------------------------------------------------------|
|7805                              |----------------------------|                          CD4069  MC14001 |
|                                  |----------------------------|                                          |
|                                               CN1                                                        |
|                                                                                                          |
|       ROM.IC13  ROM.IC14      CDP1822  CDP1822 CDP1822 CDP1822                            |-----------|  |
|                                                                                           | Output    |  |
|                                                                                           |TV Module? |  |
| ROM.IC12                                                        CDP1802  CDP1864          |           |  |
|                 CDP1822                                                                   |           |  |
|                          CD4019 CDP1858 CD4081 CD4069                                     |           |  |
|                                                                                           |           |  |
|                                                        CD4515                             |           |  |
|                                                                          1.750MHz         |-----------|  |
|                                                                                              4.3236MHz   |
|                                                                                                          |
|                                                                                                          |
|                                                                                                          |
|----------------------------------------------------------------------------------------------------------|

Notes:
      All IC's shown.

      CDP1802 - RCA CDP1802CE Microprocessor
      CDP1864 - RCA CDP1864CE PAL Video Display Controller (VDC)
      CDP1822 - RCA CDP1822NCE 256 x4 RAM (= Mitsubishi M58721P)
      ROM.ICx - RCA CDP1833 1k x8 mask ROM. All ROMs are marked 'PROGRAM COPYRIGHT (C) RCA CORP. 1978'
      CD4019  - 4019 Quad AND-OR Select Gate (4000-series CMOS TTL logic IC)
      CDP1858 - RCA CDP1858E Latch/Decoder - 4-bit
      CD4081  - 4081 Quad 2-Input AND Buffered B Series Gate (4000-series CMOS TTL logic IC)
      CD4069  - 4069 Hex Buffer, Inverter (4000-series CMOS TTL logic IC)
      CD4515  - 4515 4-Bit Latched/4-to-16 Line Decoders (4000-series CMOS TTL logic IC)
      7805    - Voltage regulator, input 10V-35V, output +5V
      CN1     - ROM cartridge connector, 2x22 pins, 0.154" spacing
*/

/*

    TODO:

    - NE555 discrete sound


    Usage
    - All variants: Boot up, then press F3, then press a letter (Q,W,E,A) to choose an inbuilt game.
    - If using a cart, boot up, press F3, then follow the instructions that came with the cart (usually press Q).
    - Visicom has no support for st2 files.
    - Visicom always reserves buttons 1,2,3,4,7(Q,W,E,A,Z) for the internal games, which are always available.
      The cartridges use 5(S) to start, except gambler1 which uses 9(C).

    Memory organisation of the Studio II:
    - RAM is mirrored everywhere except:
      (a) when A9 is high;
      (b) when a rom is active;
      (c) when the cartridge wants to disable it;
      so in effect, RAM exists at 0800-09FF then every 0x400 boundary onwards.
    - The system ROM exists at 0000-03FF and cannot be deactivated.
    - The inbuilt games exist at 0400-07FF and are always swapped out when a cart is used.
    - The cart "grandpak" also uses 0C00-0FFF with a 2nd rom.
    - Some homebrews make use of 0C00-0DFF (asteroids, berzerk, pacman, scramble). The
      ST2 loader will enable the extra rombank as needed.

*/

#include "emu.h"

#include "cpu/cosmac/cosmac.h"
#include "sound/beep.h"
#include "sound/cdp1864.h"
#include "sound/discrete.h"
#include "video/cdp1861.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

#define CDP1802_TAG     "ic1"
#define CDP1861_TAG     "ic2"
#define CDP1864_TAG     "cdp1864"
#define SCREEN_TAG      "screen"

class studio2_state : public driver_device
{
public:

	studio2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, CDP1802_TAG)
		, m_rom(*this, CDP1802_TAG)
		, m_beeper(*this, "beeper")
		, m_vdc(*this, CDP1861_TAG)
		, m_cart(*this, "cartslot")
		, m_clear(*this, "CLEAR")
		, m_a(*this, "A")
		, m_b(*this, "B")
		, m_screen(*this, "screen")
	{ }

	void studio2_cartslot(machine_config &config);
	void studio2(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER( reset_w );

	uint8_t cart_400(offs_t offset);
	uint8_t cart_c00(offs_t offset);
	uint8_t rom_000(offs_t offset);
	uint8_t rom_400(offs_t offset);

protected:
	required_device<cosmac_device> m_maincpu;
	optional_region_ptr<u8> m_rom;
	required_device<beep_device> m_beeper;
	optional_device<cdp1861_device> m_vdc;
	required_device<generic_slot_device> m_cart;
	required_ioport m_clear;
	required_ioport m_a;
	required_ioport m_b;
	required_device<screen_device> m_screen;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint8_t dispon_r();
	void keylatch_w(uint8_t data);
	void dispon_w(uint8_t data);
	int clear_r();
	int ef3_r();
	int ef4_r();
	void q_w(int state);
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER( cart_load );

	/* keyboard state */
	uint8_t m_keylatch = 0;

	void studio2_io_map(address_map &map) ATTR_COLD;
	void studio2_map(address_map &map) ATTR_COLD;
};

class visicom_state : public studio2_state
{
public:
	visicom_state(const machine_config &mconfig, device_type type, const char *tag)
		: studio2_state(mconfig, type, tag)
		, m_color0_ram(*this, "color0_ram")
		, m_color1_ram(*this, "color1_ram")
	{ }

	void visicom(machine_config &config);

private:
	virtual uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	required_shared_ptr<uint8_t> m_color0_ram;
	required_shared_ptr<uint8_t> m_color1_ram;

	virtual void machine_start() override ATTR_COLD;

	void dma_w(offs_t offset, uint8_t data);
	void visicom_io_map(address_map &map) ATTR_COLD;
	void visicom_map(address_map &map) ATTR_COLD;
};

class mpt02_state : public studio2_state
{
public:
	mpt02_state(const machine_config &mconfig, device_type type, const char *tag)
		: studio2_state(mconfig, type, tag)
		, m_cti(*this, CDP1864_TAG)
		, m_color_ram(*this, "color_ram")
	{ }

	void mpt02(machine_config &config);

private:
	required_device<cdp1864_device> m_cti;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void dma_w(offs_t offset, uint8_t data);
	int rdata_r();
	int bdata_r();
	int gdata_r();

	/* video state */
	required_shared_ptr<uint8_t> m_color_ram;
	uint8_t m_color = 0;
	void mpt02_io_map(address_map &map) ATTR_COLD;
	void mpt02_map(address_map &map) ATTR_COLD;
};


/***************************************************************************
    PARAMETERS
***************************************************************************/

#define LOG 0

/***************************************************************************
    IMPLEMENTATION
***************************************************************************/

/* Read/Write Handlers */

void studio2_state::keylatch_w(uint8_t data)
{
	m_keylatch = data & 0x0f;
}

uint8_t studio2_state::dispon_r()
{
	m_vdc->disp_on_w(1);
	m_vdc->disp_on_w(0);

	return 0xff;
}

void studio2_state::dispon_w(uint8_t data)
{
	m_vdc->disp_on_w(1);
	m_vdc->disp_on_w(0);
}

/* Memory Maps */

void studio2_state::studio2_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x01ff).mirror(0xfc00).ram();
}

void studio2_state::studio2_io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x01, 0x01).r(FUNC(studio2_state::dispon_r));
	map(0x02, 0x02).w(FUNC(studio2_state::keylatch_w));
}

void visicom_state::visicom_map(address_map &map)
{
	map(0x0000, 0x07ff).rom();
	map(0x0800, 0x0fff).r(m_cart, FUNC(generic_slot_device::read_rom));
	map(0x1000, 0x10ff).ram();
	map(0x1100, 0x11ff).ram().share("color0_ram");
	map(0x1300, 0x13ff).ram().share("color1_ram");
}

void visicom_state::visicom_io_map(address_map &map)
{
	map(0x01, 0x01).w(FUNC(visicom_state::dispon_w));
	map(0x02, 0x02).w(FUNC(visicom_state::keylatch_w));
}

void mpt02_state::mpt02_map(address_map &map)
{
	map(0x0000, 0x07ff).rom();
	map(0x0800, 0x09ff).ram();
	map(0x0b00, 0x0b3f).ram().share("color_ram");
	map(0x0c00, 0x0fff).rom();
}

void mpt02_state::mpt02_io_map(address_map &map)
{
	map(0x01, 0x01).rw(m_cti, FUNC(cdp1864_device::dispon_r), FUNC(cdp1864_device::step_bgcolor_w));
	map(0x02, 0x02).w(FUNC(mpt02_state::keylatch_w));
	map(0x04, 0x04).rw(m_cti, FUNC(cdp1864_device::dispoff_r), FUNC(cdp1864_device::tone_latch_w));
}

/* Input Ports */

INPUT_CHANGED_MEMBER( studio2_state::reset_w )
{
	if (oldval && !newval)
	{
		machine_reset();
	}
}

static INPUT_PORTS_START( studio2 )
	PORT_START("A")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("A 0") PORT_CODE(KEYCODE_LALT)
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("A 1") PORT_CODE(KEYCODE_Q)
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("A 2") PORT_CODE(KEYCODE_W)
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("A 3") PORT_CODE(KEYCODE_E)
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("A 4") PORT_CODE(KEYCODE_A)
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("A 5") PORT_CODE(KEYCODE_S)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("A 6") PORT_CODE(KEYCODE_D)
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("A 7") PORT_CODE(KEYCODE_Z)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("A 8") PORT_CODE(KEYCODE_X)
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("A 9") PORT_CODE(KEYCODE_C)

	PORT_START("B")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("B 0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("B 1") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("B 2") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("B 3") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("B 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("B 5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("B 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("B 7") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("B 8") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("B 9") PORT_CODE(KEYCODE_3_PAD)

	PORT_START("CLEAR")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Clear") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(studio2_state::reset_w), 0)
INPUT_PORTS_END

/* Video */

static const rgb_t VISICOM_PALETTE[] =
{
	rgb_t(0x00, 0x40, 0x00),
	rgb_t(0xaf, 0xdf, 0xe4),
	rgb_t(0xb9, 0xc4, 0x2f),
	rgb_t(0xef, 0x45, 0x4a)
};

uint32_t visicom_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	m_vdc->screen_update(screen, bitmap, cliprect);

	m_vdc->m_bitmap.fill(VISICOM_PALETTE[0], cliprect);

	return 0;
}

int mpt02_state::rdata_r()
{
	return BIT(m_color, 0);
}

int mpt02_state::bdata_r()
{
	return BIT(m_color, 1);
}

int mpt02_state::gdata_r()
{
	return BIT(m_color, 2);
}

/* CDP1802 Configuration */

int studio2_state::clear_r()
{
	return BIT(m_clear->read(), 0);
}

int studio2_state::ef3_r()
{
	return BIT(m_a->read(), m_keylatch);
}

int studio2_state::ef4_r()
{
	return BIT(m_b->read(), m_keylatch);
}

void studio2_state::q_w(int state)
{
	m_beeper->set_state(state);
}

void visicom_state::dma_w(offs_t offset, uint8_t data)
{
	int sx = m_screen->hpos() + 4;
	int y = m_screen->vpos();

	uint8_t addr = offset & 0xff;
	uint8_t color0 = m_color0_ram[addr];
	uint8_t color1 = m_color1_ram[addr];

	for (int x = 0; x < 8; x++)
	{
		int color = (BIT(color1, 7) << 1) | BIT(color0, 7);
		m_vdc->m_bitmap.pix(y, sx + x) = VISICOM_PALETTE[color];
		color0 <<= 1;
		color1 <<= 1;
	}
}

void mpt02_state::dma_w(offs_t offset, uint8_t data)
{
	uint8_t addr = ((offset & 0xe0) >> 2) | (offset & 0x07);

	m_color = m_color_ram[addr];

	m_cti->con_w(0); // HACK
	m_cti->dma_w(data);
}

/* Machine Initialization */

// trampolines to cartridge
uint8_t studio2_state::rom_000(offs_t offset) { return m_rom[offset]; }
uint8_t studio2_state::rom_400(offs_t offset) { return m_rom[offset+0x400]; }
uint8_t studio2_state::cart_400(offs_t offset) { return m_cart->read_rom(offset); }
uint8_t studio2_state::cart_c00(offs_t offset) { return m_cart->read_rom(offset + 0x800); }

void visicom_state::machine_start()
{
	// register for state saving
	save_item(NAME(m_keylatch));
}

void studio2_state::machine_start()
{
	m_maincpu->space(AS_PROGRAM).unmap_readwrite(0x0000, 0x07ff);
	m_maincpu->space(AS_PROGRAM).install_read_handler(0x0000, 0x03ff, read8sm_delegate(*this, FUNC(studio2_state::rom_000)));

	if (m_cart->exists())
	{
		// cart always overlaps the inbuilt game roms
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x0400, 0x07ff, read8sm_delegate(*this, FUNC(studio2_state::cart_400)));
	}
	else
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x0400, 0x07ff, read8sm_delegate(*this, FUNC(studio2_state::rom_400)));

	// register for state saving
	save_item(NAME(m_keylatch));
}

void mpt02_state::machine_start()
{
	if (m_cart->exists())
	{
		// cart always overlaps the inbuilt game roms
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x0400, 0x07ff, read8sm_delegate(*this, FUNC(studio2_state::cart_400)));
	}

	// register for state saving
	save_item(NAME(m_keylatch));
}

void studio2_state::machine_reset()
{
	m_vdc->reset();
}

void mpt02_state::machine_reset()
{
	m_cti->reset();
}

DEVICE_IMAGE_LOAD_MEMBER( studio2_state::cart_load )
{
	uint32_t size;

	// always alloc 3K, even if range $400-$600 is not read by the system (RAM is present there)
	m_cart->rom_alloc(0xc00, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);

	if (!image.loaded_through_softlist())
	{
		if (image.is_filetype("st2"))
		{
			char header[0x100];
			uint8_t pages[64];
			uint8_t blocks;

			if (image.length() < 0x200)
				return std::make_pair(image_error::INVALIDLENGTH, "Invalid .ST2 ROM file (must be at least 512 bytes)");

			image.fread(&header, 0x100);

			// validate
			if (strncmp((const char *)header, "RCA2", 4))
				return std::make_pair(image_error::INVALIDIMAGE, "Not an .ST2 ROM file (missing header signature)");

			blocks = header[4];
			if ((blocks < 2) || (blocks > 11))
				return std::make_pair(image_error::INVALIDIMAGE, "Invalid .ST2 ROM file (must be 2 to 11 blocks)");

			if (image.length() != (blocks << 8))
				logerror("Wrong sized image: Expected 0x%04X; Found 0x%04X\n",blocks<<8,image.length());

			char* catalogue = &header[16];
			char* title = &header[32];
			memcpy(&pages, &header[64], 64);

			logerror("ST2 Catalogue: %s\n", catalogue);
			logerror("ST2 Title: %s\n", title);

			/* read ST2 cartridge into memory */
			for (int block = 0; block < (blocks - 1); block++)
			{
				u16 offset = pages[block] << 8;
				if (pages[block] < 4)
					logerror("ST2 invalid block %u to 0x%04x\n", block, offset);
				else
				{
					logerror("ST2 Reading block %u to 0x%04x\n", block, offset);
					if (pages[block] == 0xC)
						m_maincpu->space(AS_PROGRAM).install_read_handler(0x0c00, 0x0fff, read8sm_delegate(*this, FUNC(studio2_state::cart_c00)));
					image.fread(m_cart->get_rom_base() + offset - 0x400, 0x100);
				}
			}
		}
		else
		{
			size = image.length();
			if (size > 0x400)
				return std::make_pair(image_error::INVALIDIMAGE, "Unsupported cartridge size (must be not more than 1K)");

			image.fread(m_cart->get_rom_base(), size);
		}
	}
	else
	{
		// Studio II and MPT-2 carts might map their data at $400-$7ff and $c00-$fff
		if (image.get_software_region("rom_400"))
			memcpy(m_cart->get_rom_base() + 0x000, image.get_software_region("rom_400"), image.get_software_region_length("rom_400"));
		if (image.get_software_region("rom_c00"))
		{
			m_maincpu->space(AS_PROGRAM).install_read_handler(0x0c00, 0x0fff, read8sm_delegate(*this, FUNC(studio2_state::cart_c00)));
			memcpy(m_cart->get_rom_base() + 0x800, image.get_software_region("rom_c00"), image.get_software_region_length("rom_c00"));
		}
	}

	return std::make_pair(std::error_condition(), std::string());
}


/* Machine Drivers */

void studio2_state::studio2_cartslot(machine_config &config)
{
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "studio2_cart", "st2,bin,rom").set_device_load(FUNC(studio2_state::cart_load));

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("studio2");
}

void studio2_state::studio2(machine_config &config)
{
	/* basic machine hardware */
	CDP1802(config, m_maincpu, 1760000); /* the real clock is derived from an oscillator circuit */
	m_maincpu->set_addrmap(AS_PROGRAM, &studio2_state::studio2_map);
	m_maincpu->set_addrmap(AS_IO, &studio2_state::studio2_io_map);
	m_maincpu->wait_cb().set_constant(1);
	m_maincpu->clear_cb().set(FUNC(studio2_state::clear_r));
	m_maincpu->ef3_cb().set(FUNC(studio2_state::ef3_r));
	m_maincpu->ef4_cb().set(FUNC(studio2_state::ef4_r));
	m_maincpu->q_cb().set(FUNC(studio2_state::q_w));
	m_maincpu->dma_wr_cb().set(m_vdc, FUNC(cdp1861_device::dma_w));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	CDP1861(config, m_vdc, 1760000).set_screen(m_screen);
	m_vdc->int_cb().set_inputline(m_maincpu, COSMAC_INPUT_LINE_INT);
	m_vdc->dma_out_cb().set_inputline(m_maincpu, COSMAC_INPUT_LINE_DMAOUT);
	m_vdc->efx_cb().set_inputline(m_maincpu, COSMAC_INPUT_LINE_EF1);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 300).add_route(ALL_OUTPUTS, "mono", 1.00);

	studio2_cartslot(config);
}

void visicom_state::visicom(machine_config &config)
{
	/* basic machine hardware */
	CDP1802(config, m_maincpu, XTAL(3'579'545)/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &visicom_state::visicom_map);
	m_maincpu->set_addrmap(AS_IO, &visicom_state::visicom_io_map);
	m_maincpu->wait_cb().set_constant(1);
	m_maincpu->clear_cb().set(FUNC(visicom_state::clear_r));
	m_maincpu->ef3_cb().set(FUNC(visicom_state::ef3_r));
	m_maincpu->ef4_cb().set(FUNC(visicom_state::ef4_r));
	m_maincpu->q_cb().set(FUNC(visicom_state::q_w));
	m_maincpu->dma_wr_cb().set(FUNC(visicom_state::dma_w));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_screen_update(FUNC(visicom_state::screen_update));

	CDP1861(config, m_vdc, XTAL(3'579'545)/2).set_screen(m_screen);
	m_vdc->int_cb().set_inputline(m_maincpu, COSMAC_INPUT_LINE_INT);
	m_vdc->dma_out_cb().set_inputline(m_maincpu, COSMAC_INPUT_LINE_DMAOUT);
	m_vdc->efx_cb().set_inputline(m_maincpu, COSMAC_INPUT_LINE_EF1);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 300).add_route(ALL_OUTPUTS, "mono", 1.00);

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "visicom_cart", "bin,rom");

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("visicom");
}

void mpt02_state::mpt02(machine_config &config)
{
	/* basic machine hardware */
	CDP1802(config, m_maincpu, 1.75_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mpt02_state::mpt02_map);
	m_maincpu->set_addrmap(AS_IO, &mpt02_state::mpt02_io_map);
	m_maincpu->wait_cb().set_constant(1);
	m_maincpu->clear_cb().set(FUNC(mpt02_state::clear_r));
	m_maincpu->ef3_cb().set(FUNC(mpt02_state::ef3_r));
	m_maincpu->ef4_cb().set(FUNC(mpt02_state::ef4_r));
	m_maincpu->q_cb().set(FUNC(mpt02_state::q_w));
	m_maincpu->dma_wr_cb().set(FUNC(mpt02_state::dma_w));

	/* video/sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 300).add_route(ALL_OUTPUTS, "mono", 1.00);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	CDP1864(config, m_cti, 1.75_MHz_XTAL).set_screen(m_screen);
	m_cti->inlace_cb().set_constant(0);
	m_cti->int_cb().set_inputline(m_maincpu, COSMAC_INPUT_LINE_INT);
	m_cti->dma_out_cb().set_inputline(m_maincpu, COSMAC_INPUT_LINE_DMAOUT);
	m_cti->efx_cb().set_inputline(m_maincpu, COSMAC_INPUT_LINE_EF1);
	m_cti->rdata_cb().set(FUNC(mpt02_state::rdata_r));
	m_cti->bdata_cb().set(FUNC(mpt02_state::bdata_r));
	m_cti->gdata_cb().set(FUNC(mpt02_state::gdata_r));
	m_cti->set_chrominance(RES_K(4.7), RES_K(8.2), RES_K(4.7), RES_K(22));
	m_cti->add_route(ALL_OUTPUTS, "mono", 0.25);

	studio2_cartslot(config);
}

/* ROMs */

ROM_START( studio2 )
	ROM_REGION( 0x1000, CDP1802_TAG, 0 )
	ROM_LOAD( "84932.ic11", 0x000, 0x200, CRC(283b7e65) SHA1(4b6d21cde59712ecb5941ff63d8eb161420b0aac) )
	ROM_LOAD( "84933.ic12", 0x200, 0x200, CRC(a396b77c) SHA1(023517f67af61790e6916b6c4dbe2d9dc07ae3ff) )
	ROM_LOAD( "85456.ic13", 0x400, 0x200, CRC(d25cf97f) SHA1(d489f41f1125c76cc8ed9defa82a877ae014ef21) )
	ROM_LOAD( "85457.ic14", 0x600, 0x200, CRC(74aa724f) SHA1(085832f29e0d2a387c75463d66c54fb6c1e9e72c) )
ROM_END

ROM_START( visicom )
	ROM_REGION( 0x1000, CDP1802_TAG, 0 )
	ROM_LOAD( "visicom.q003", 0x000, 0x800, CRC(23d22074) SHA1(a0a8be23f70621a2bd8010b1134e8a0019075bf1) )
ROM_END

ROM_START( mpt02 )
	ROM_REGION( 0x1000, CDP1802_TAG, 0 )
	ROM_LOAD( "86676.ic13",  0x000, 0x400, CRC(a7d0dd3b) SHA1(e1881ab4d67a5d735dd2c8d7e924e41df6f2aeec) )
	ROM_LOAD( "86677b.ic14", 0x400, 0x400, CRC(82a2d29e) SHA1(37e02089d611db10bad070d89c8801de41521189) )
	ROM_LOAD( "87201.ic12",  0xc00, 0x400, CRC(8006a1e3) SHA1(b67612d98231485fce55d604915abd19b6d64eac) )
ROM_END

#define rom_mtc9016 rom_mpt02
#define rom_shmc1200 rom_mpt02
#define rom_cm1200 rom_mpt02
#define rom_apollo80 rom_mpt02

ROM_START( mpt02h ) // doesn't have built-in games. It came with the pack-in cart 'Grand Pack'
	ROM_REGION( 0x1000, CDP1802_TAG, 0 )
	ROM_LOAD( "86676.ic13",  0x000, 0x400, CRC(a7d0dd3b) SHA1(e1881ab4d67a5d735dd2c8d7e924e41df6f2aeec) )
ROM_END

} // Anonymous namespace


/* Game Drivers */

//    YEAR  NAME      PARENT   COMPAT  MACHINE  INPUT    STATE          INIT        COMPANY     FULLNAME                                        FLAGS
CONS( 1977, studio2,  0,       0,      studio2, studio2, studio2_state, empty_init, "RCA",      "Studio II",                                    MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
CONS( 1978, visicom,  studio2, 0,      visicom, studio2, visicom_state, empty_init, "Toshiba",  "Visicom COM-100 (Japan)",                      MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
CONS( 1978, mpt02,    studio2, 0,      mpt02,   studio2, mpt02_state,   empty_init, "Soundic",  "Victory MPT-02 Home TV Programmer (Austria)",  MACHINE_SUPPORTS_SAVE ) // It seems to have been sold in various countries, not only Austria
CONS( 1978, mpt02h,   studio2, 0,      mpt02,   studio2, mpt02_state,   empty_init, "Hanimex",  "MPT-02 Jeu TV Programmable (France)",          MACHINE_SUPPORTS_SAVE )
CONS( 1978, mtc9016,  studio2, 0,      mpt02,   studio2, mpt02_state,   empty_init, "Mustang",  "9016 Telespiel Computer (Germany)",            MACHINE_SUPPORTS_SAVE )
CONS( 1978, shmc1200, studio2, 0,      mpt02,   studio2, mpt02_state,   empty_init, "Sheen",    "M1200 Micro Computer (Australia)",             MACHINE_SUPPORTS_SAVE )
CONS( 1978, cm1200,   studio2, 0,      mpt02,   studio2, mpt02_state,   empty_init, "Conic",    "M-1200 (?)",                                   MACHINE_SUPPORTS_SAVE )
CONS( 1978, apollo80, studio2, 0,      mpt02,   studio2, mpt02_state,   empty_init, "Academy",  "Apollo 80 (Germany)",                          MACHINE_SUPPORTS_SAVE )



stvdev.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/**************************************************************************************************

Sega ST-V dev-box unit "837-12764 486 BD FOR ST-V"

Presumably for stvbios "hold F2 at boot -> System Configuration" cart dev mode.

ALi/ACER FINALi 486 PCI motherboard
M1489 A1 (northbridge) + M1487 B1 (southbridge)
M5113 (super i/o, sorta compatible with FDC37C665 as per PSC-586VGA single board)
DP8432V-33
CAK0003UA
2x Altera EPM7032LC44-10 near northbridge
Two empty sockets, u0621 (BIOS extension?) and u0629 (user space?)

**************************************************************************************************/

#include "emu.h"
#include "cpu/i386/i386.h"
#include "machine/pci.h"


namespace {

class stvdev_state : public driver_device
{
public:
	stvdev_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }


	void stvdev(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	void stvdev_io(address_map &map) ATTR_COLD;
	void stvdev_map(address_map &map) ATTR_COLD;
};


void stvdev_state::stvdev_map(address_map &map)
{
	map(0x00000000, 0x0009ffff).ram();
	map(0x000e0000, 0x000fffff).rom().region("bios", 0);
	map(0xfffe0000, 0xffffffff).rom().region("bios", 0);
}

void stvdev_state::stvdev_io(address_map &map)
{
}

static INPUT_PORTS_START(stvdev)
INPUT_PORTS_END

void stvdev_state::stvdev(machine_config &config)
{
	// TODO: two other clocks, one at 36 MHz near CAK chip and another at 14.318 MHz near 486 socket
	I486(config, m_maincpu, 33'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &stvdev_state::stvdev_map);
	m_maincpu->set_addrmap(AS_IO, &stvdev_state::stvdev_io);

	PCI_ROOT(config, "pci", 0);
	// ...
}


ROM_START( stvdev )
	ROM_REGION32_LE( 0x20000, "bios", ROMREGION_ERASEFF )
	ROM_LOAD( "epr-19159.u0628", 0, 0x20000, CRC(70735b87) SHA1(825954978a6b09d307ec33ef46128a45a76ff63b) )
ROM_END

} // anonymous namespace


// NOTE: stvbios mentions HP and SUN archs being supported, "PC" suffix comes from there
COMP(1998, stvdev, 0, 0, stvdev, stvdev, stvdev_state, empty_init, "Sega / ALi", "ST-V 486 dev box PC", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



sun1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

        Sun-1 Models
        ------------

    Sun-1

        Processor(s):   68000
        P/N:            270-0001
        Notes:          Large black desktop boxes with 17" monitors.
                        Uses the original Stanford-designed video board
                        and a parallel microswitch keyboard (type 1) and
                        parallel mouse (Sun-1).

    100
        Processor(s):   68000 @ 10MHz
        Bus:            Multibus, serial
        Notes:          Uses a design similar to original SUN (Stanford
                        University Network) CPU. The version 1.5 CPU can
                        take larger RAMs.

    100U
        Processor(s):   68010 @ 10MHz
        CPU:            501-1007
        Bus:            Multibus, serial
        Notes:          "Brain transplant" for 100 series. Replaced CPU
                        and memory boards with first-generation Sun-2
                        CPU and memory boards so original customers
                        could run SunOS 1.x. Still has parallel kb/mouse
                        interface so type 1 keyboards and Sun-1 mice
                        could be connected.

    170
        Processor(s):   68010?
        Bus:            Multibus?
        Chassis type:   rackmount
        Notes:          Server. Slightly different chassis design than
                        2/170's


        Documentation:
            http://www.bitsavers.org/pdf/sun/sun1/800-0345_Sun-1_System_Ref_Man_Jul82.pdf
            (page 39,40 of pdf contain memory map)

        This "Draft Version 1.0" reference claims a 10MHz clock for the
        MC68000 and a 5MHz clock for the Am9513; though the original design
        may have specified a 10MHz CPU, and though this speed may have been
        realized in later models, schematics suggest the system's core
        devices actually run at 8/4MHz (divided from a 16MHz XTAL), which
        lets the 1.0 monitor ROM's Am9513 configuration generate a more
        plausible baud rate.

        04/12/2009 Skeleton driver.

        04/04/2011 Modernised, added terminal keyboard.

 TODO:
  - graphic cards
  - network cards
  - storage cards

****************************************************************************/

#include "emu.h"

#include "sun1_cpu.h"

#include "bus/multibus/multibus.h"

namespace {

class sun1_state : public driver_device
{
public:
	sun1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_bus(*this, "bus")
	{
	}

	void sun1(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<multibus_device> m_bus;
};

void sun1_state::machine_start()
{
}

void sun1_state::machine_reset()
{
}

static void sun1_cards(device_slot_interface &device)
{
	device.option_add("sun1cpu", MULTIBUS_SUN1CPU);
}

void sun1_state::sun1(machine_config &config)
{
	// FIXME: CPU board can optionally drive Multibus clocks
	MULTIBUS(config, m_bus, 19.6608_MHz_XTAL / 2);

	// slot 1 is physically located at top, only slots 1-3 have P2
	MULTIBUS_SLOT(config, "slot1", m_bus, sun1_cards, nullptr, false); // memory expansion 2
	MULTIBUS_SLOT(config, "slot2", m_bus, sun1_cards, nullptr, false); // memory expansion 1
	MULTIBUS_SLOT(config, "slot3", m_bus, sun1_cards, "sun1cpu", false); // processor
	MULTIBUS_SLOT(config, "slot4", m_bus, sun1_cards, nullptr, false); // bus master 1
	MULTIBUS_SLOT(config, "slot5", m_bus, sun1_cards, nullptr, false); // bus master 2
	MULTIBUS_SLOT(config, "slot6", m_bus, sun1_cards, nullptr, false); // multibus memory, optional
	MULTIBUS_SLOT(config, "slot7", m_bus, sun1_cards, nullptr, false); // graphics
}

ROM_START(sun1)
ROM_END

} // anonymous namespace

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY             FULLNAME  FLAGS
COMP( 1982, sun1, 0,      0,      sun1,    0,     sun1_state, empty_init, "Sun Microsystems", "Sun-1",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



sun2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, R. Belmont
/***************************************************************************

        Sun-2 Models
        ------------

    2/120
        Processor(s):   68010 @ 10MHz
        CPU:            501-1007/1051
        Chassis type:   deskside
        Bus:            Multibus (9 slots)
        Oscillator(s):  39.3216MHz
        Memory:         7M physical
        Notes:          First machines in deskside chassis. Serial
                        microswitch keyboard (type 2), Mouse Systems
                        optical mouse (Sun-2).

    2/100U
        Processor(s):   68010 @ 10MHz
        CPU:            501-1007
        Bus:            Multibus
        Notes:          Upgraded Sun 100. Replaced CPU and memory boards
                        with first-generation Sun-2 CPU and memory
                        boards so original customers could run SunOS
                        1.x. Still has parallel kb/mouse interface so
                        type 1 keyboards and Sun-1 mice could be
                        connected.

    2/150U
        Notes:          Apparently also an upgraded Sun-1.

    2/170
        Chassis type:   rackmount
        Bus:            Multibus (15 slots)
        Notes:          Rackmount version of 2/120, with more slots.

    2/50
        Processor(s):   68010 @ 10MHz
        CPU:            501-1141/1142/1143/1426/1427/1428
        Chassis type:   wide pizza box
        Bus:            VME (2 slots)
        Oscillator(s):  19.6608MHz, 16MHz (Ethernet/VMEbus), 100MHz
                        (video), 24MHz ("for special applications")
        Memory:         7M physical
        Notes:          The (type 2) keyboard and mouse attach via an
                        adapter that accepts two modular plugs and
                        attaches to a DB15 port; later on, units were
                        apparently shipped with type 3 keyboards. The
                        CPU boards have a double-width back panel but
                        are otherwise identical to those in the 2/130
                        and 2/160.

    2/130
    2/160
        Processor(s):   68010 @ 10MHz
        CPU:            501-1144/1145/1146/1429/1430/1431
        Chassis type:   deskside
        Bus:            VME (12 slots)
        Memory:         7M physical
        Notes:          First machine in 12-slot deskside VME chassis.
                        Has four-fan cooling tray instead of six as in
                        later machines, which led to cooling problems
                        with lots of cards. Backplane has only four P2
                        memory connectors bussed instead of six as in
                        later 12-slot backplanes; SCSI passthrough is in
                        slot 6 instead of 7 as in later 12-slot
                        backplanes. Upgradeable to a 3/160 by replacing
                        the CPU board. No information on the differences
                        between the 2/130 and the 2/160.


        25/08/2009 Skeleton driver.
        31/05/2016 Main screen turn on.

How the architecture works:
    - There are 3 address sub-spaces: CPU layer, MMU layer, and device layer
    - CPU layer uses MOVS instructions to output FC 3.
    - CPU layer: the low-order address bits A4-A1 specify the device
        0100x = ID Prom
        0101x = Diagnostic register (8 bits, 8 LEDs, bit = 0 for ON, 1 for OFF)
        0110x = Bus error register
        0111x = System enable register

        Bits A5+ address the actual individual parts of these things.  ID Prom bytes
        are at 0x0008, 0x0808, 0x1008, 0x1808, 0x2008, 0x2808, 0x3008, etc.

        System enable bits:
            b0 = enable parity generation
            b1 = cause level 1 IRQ
            b2 = cause level 2 IRQ
            b3 = cause level 3 IRQ
            b4 = enable parity error checking
            b5 = enable DVMA
            b6 = enable all interrupts
            b7 = boot state (0 = boot, 1 = normal)
                In boot state, all supervisor program reads go to the EPROM.

    - MMU layer: also accessed via FC 3
        PAGE MAP at 0 + V
        SEGMENT MAP at 4 + V
        CONTEXT REG at 6 + V

    There are 8 hardware contexts.  Supervisor and User FCs can have different contexts.

    Segment map is 4096 entries, from bits 23-15 of the virtual address + 3 context bits.
    Entries are 8 bits, which point to a page map entry group (PMEG), which is 16 consecutive
    page table entries (32 KB of space).

    Page map is 4096 entries each mapping a 2K page.  There are 256 groups of 16 entries;
    the PMEG points to these 256 groups.  The page map contains a 20-bit page number,
    which combines with the 11 low bits of the original address to get a 31-bit physical address.
    The entry from 0-15 is picked with bits 15-11 of the original address.

    Page map entries are written to the PMEG determined by their segment map entry; you must
    set the segment map validly in order to write to the page map.  This is how they get away
    with having 16 MB of segment entries and only 8 MB of PMEGs.

    See http://sunstuff.org/Sun-Hardware-Ref/s2hr/part2
****************************************************************************/

#include "emu.h"
#include "cpu/m68000/m68010.h"
#include "machine/ram.h"
#include "machine/am9513.h"
#include "machine/i82586.h"
#include "machine/mm58167.h"
#include "machine/z80scc.h"
#include "machine/bankdev.h"
#include "machine/input_merger.h"
#include "bus/rs232/rs232.h"
#include "screen.h"


namespace {

#define SCC1_TAG        "scc1"
#define SCC2_TAG        "scc2"
#define RS232A_TAG      "rs232a"
#define RS232B_TAG      "rs232b"

// page table entry constants
#define PM_VALID    (0x80000000)    // page is valid
#define PM_PROTMASK (0x7e000000)    // protection mask
#define PM_TYPEMASK (0x01c00000)    // type mask
#define PM_ACCESSED (0x00200000)    // accessed flag
#define PM_MODIFIED (0x00100000)    // modified flag

class sun2_state : public driver_device
{
public:
	sun2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "bootprom")
		, m_idprom(*this, "idprom")
		, m_ram(*this, RAM_TAG)
		, m_type0space(*this, "type0")
		, m_type1space(*this, "type1")
		, m_type2space(*this, "type2")
		, m_type3space(*this, "type3")
		, m_edlc(*this, "edlc")
		, m_bw2_vram(*this, "bw2_vram")
	{ }

	void sun2mbus(machine_config &config);
	void sun2vme(machine_config &config);

private:
	required_device<m68010_device> m_maincpu;
	required_memory_region m_rom, m_idprom;
	required_device<ram_device> m_ram;
	required_device<address_map_bank_device> m_type0space, m_type1space, m_type2space, m_type3space;
	optional_device<i82586_device> m_edlc;
	required_shared_ptr<uint16_t> m_bw2_vram;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint16_t mmu_r(offs_t offset, uint16_t mem_mask = ~0);
	void mmu_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t tl_mmu_r(uint8_t fc, offs_t offset, uint16_t mem_mask);
	void tl_mmu_w(uint8_t fc, offs_t offset, uint16_t data, uint16_t mem_mask);
	uint16_t video_ctrl_r();
	void video_ctrl_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t ram_r(offs_t offset);
	void ram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint8_t ethernet_r();
	void ethernet_w(uint8_t data);
	void ethernet_int_w(int state);
	uint16_t edlc_mmu_r(offs_t offset, uint16_t mem_mask);
	void edlc_mmu_w(offs_t offset, uint16_t data, uint16_t mem_mask);

	uint32_t bw2_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void mbustype0space_map(address_map &map) ATTR_COLD;
	void mbustype1space_map(address_map &map) ATTR_COLD;
	void mbustype2space_map(address_map &map) ATTR_COLD;
	void mbustype3space_map(address_map &map) ATTR_COLD;
	void sun2_mem(address_map &map) ATTR_COLD;
	void edlc_mem(address_map &map) ATTR_COLD;
	void vmetype0space_map(address_map &map) ATTR_COLD;
	void vmetype1space_map(address_map &map) ATTR_COLD;
	void vmetype2space_map(address_map &map) ATTR_COLD;
	void vmetype3space_map(address_map &map) ATTR_COLD;

	uint16_t *m_rom_ptr, *m_ram_ptr;
	uint8_t *m_idprom_ptr;
	uint16_t m_diagreg, m_sysenable, m_buserror;
	uint16_t m_context;
	uint8_t m_segmap[8][512];
	uint32_t m_pagemap[4097];
	uint32_t m_ram_size, m_ram_size_words;
	uint16_t m_bw2_ctrl;
	uint8_t m_ethernet_status;
};

uint16_t sun2_state::ram_r(offs_t offset)
{
	if (offset < m_ram_size_words) return m_ram_ptr[offset];
	return 0xffff;
}

void sun2_state::ram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (offset < m_ram_size_words) COMBINE_DATA(&m_ram_ptr[offset]);
}

uint16_t sun2_state::mmu_r(offs_t offset, uint16_t mem_mask)
{
	return tl_mmu_r(m_maincpu->get_fc(), offset, mem_mask);
}

uint16_t sun2_state::tl_mmu_r(uint8_t fc, offs_t offset, uint16_t mem_mask)
{
	if ((fc == 3) && !machine().side_effects_disabled())
	{
		if (offset & 0x4)   // set for CPU space
		{
			switch (offset & 7)
			{
				case 4:
					//printf("sun2: Read IDPROM @ %x (PC=%x)\n", offset<<1, m_maincpu->pc());
					return m_idprom_ptr[(offset>>10) & 0x1f]<<8;

				case 5:
					//printf("sun2: Read diag reg\n");
					return m_diagreg;

				case 6:
					//printf("sun2: Read bus error @ PC %x\n", m_maincpu->pc());
					return m_buserror;

				case 7:
					//printf("sun2: Read sysenable\n");
					return m_sysenable;
			}
		}
		else                // clear for MMU space
		{
			int page;

			switch (offset & 3)
			{
				case 0: // page map
				case 1:
					page = m_segmap[m_context & 7][offset >> 14] << 4;
					page += ((offset >> 10) & 0xf);

					//printf("sun2: Read page map at %x (entry %d)\n", offset<<1, page);
					if (offset & 1) // low-order 16 bits
					{
						return m_pagemap[page] & 0xffff;
					}
					return m_pagemap[page] >> 16;

				case 2: // segment map
					//printf("sun2: Read segment map at %x (entry %d, user ctx %d)\n", offset<<1, offset>>14, m_context & 7);
					return m_segmap[m_context & 7][offset >> 14];

				case 3: // context reg
					//printf("sun2: Read context reg\n");
					return m_context;
			}
		}
	}

	// boot mode?
	if ((fc == M68K_FC_SUPERVISOR_PROGRAM) && !(m_sysenable & 0x80))
	{
		return m_rom_ptr[offset & 0x3fff];
	}

	// debugger hack
	if (machine().side_effects_disabled() && (offset >= (0xef0000>>1)) && (offset <= (0xef8000>>1)))
	{
		return m_rom_ptr[offset & 0x3fff];
	}

	// it's translation time
	uint8_t context = (fc & 4) ? ((m_context >> 8) & 7) : (m_context & 7);
	uint8_t pmeg = m_segmap[context][offset >> 14];
	uint32_t entry = (pmeg << 4) + ((offset >> 10) & 0xf);

	//  printf("sun2: Context = %d, pmeg = %d, offset >> 14 = %x, entry = %d, page = %d\n", context, pmeg, offset >> 14, entry, (offset >> 10) & 0xf);

	if (m_pagemap[entry] & PM_VALID)
	{
		m_pagemap[entry] |= PM_ACCESSED;

		// Sun2 implementations only use 12 bits from the page entry
		uint32_t tmp = (m_pagemap[entry] & 0xfff) << 10;
		tmp |= (offset & 0x3ff);

	//  if (!machine().side_effects_disabled())
	//      printf("sun2: Translated addr: %08x, type %d (page %d page entry %08x, orig virt %08x, FC %d)\n", tmp << 1, (m_pagemap[entry] >> 22) & 7, entry, m_pagemap[entry], offset<<1, fc);

		switch ((m_pagemap[entry] >> 22) & 7)
		{
			case 0: // type 0 space
				return m_type0space->read16(tmp, mem_mask);

			case 1: // type 1 space
				// EPROM space is special: the MMU has a trap door
				// where the original bits of the virtual address are
				// restored so that the entire 32K EPROM can be
				// accessed via a 2K single page view.  This isn't
				// obvious in the sun2 manual, but the sun3 manual
				// (sun3 has the same mechanism) explains it well.
				// the 2/50 ROM tests this specifically at $EF0DF0.
				if (m_idprom_ptr[1] == 0x02)    // 2/50 VMEbus has EPROM at 0x7F0000
				{
					if ((tmp >= (0x7f0000>>1)) && (tmp <= (0x7f07ff>>1)))
					{
						return m_rom_ptr[offset & 0x3fff]; // the mask here is probably &0x7fff, change it if any 8KW (1.x?) romset shows up for a VME machine
					}
				}
				else    // Multibus has EPROM at 0x000000
				{
					if (tmp <= (0x7ff>>1))
					{
						return m_rom_ptr[offset & 0x7fff];
					}
				}

				//printf("read device space @ %x\n", tmp<<1);
				return m_type1space->read16(tmp, mem_mask);

			case 2: // type 2 space
				return m_type2space->read16(tmp, mem_mask);

			case 3: // type 3 space
				return m_type3space->read16(tmp, mem_mask);
		}
	}
	else
	{
		if (!machine().side_effects_disabled()) printf("sun2: pagemap entry not valid!\n");
	}

	if (!machine().side_effects_disabled()) printf("sun2: Unmapped read @ %08x (FC %d, mask %04x, PC=%x, seg %x)\n", offset<<1, fc, mem_mask, m_maincpu->pc(), offset>>15);

	return 0xffff;
}

void sun2_state::mmu_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	tl_mmu_w(m_maincpu->get_fc(), offset, data, mem_mask);
}

void sun2_state::tl_mmu_w(uint8_t fc, offs_t offset, uint16_t data, uint16_t mem_mask)
{
	//printf("sun2: Write %04x (FC %d, mask %04x, PC=%x) to %08x\n", data, fc, mem_mask, m_maincpu->pc(), offset<<1);

	if (fc == 3)
	{
		if (offset & 0x4)   // set for CPU space
		{
			switch (offset & 7)
			{
				case 4:
					//printf("sun2: Write? IDPROM @ %x\n", offset<<1);
					return;

				case 5:
					// XOR to match Table 2-1 in the 2/50 Field Service Manual
					printf("sun2: CPU LEDs to %02x (PC=%x) => ", (data & 0xff) ^ 0xff, m_maincpu->pc());
					m_diagreg = data & 0xff;
					for (int i = 0; i < 8; i++)
					{
						if (m_diagreg & (1<<(7-i)))
						{
							printf("*");
						}
						else
						{
							printf("O");
						}
					}
					printf("\n");
					return;

				case 6:
					//printf("sun2: Write %04x to bus error not allowed\n", data);
					return;

				case 7:
					//printf("sun2: Write %04x to system enable\n", data);
					COMBINE_DATA(&m_sysenable);
					return;
			}
		}
		else                // clear for MMU space
		{
			int page;

			switch (offset & 3)
			{
				case 0: // page map
				case 1:
					page = m_segmap[m_context & 7][offset >> 14] << 4;
					page += ((offset >> 10) & 0xf);

					//printf("sun2: Write %04x to page map at %x (entry %d), ", data, offset<<1, page);
					if (offset & 1) // low-order 16 bits
					{
						m_pagemap[page] &= 0xffff0000;
						m_pagemap[page] |= data;
					}
					else
					{
						m_pagemap[page] &= 0x0000ffff;
						m_pagemap[page] |= (data<<16);
					}
					//printf("entry now %08x (adr %08x  PC=%x)\n", m_pagemap[page], (m_pagemap[page] & 0xfffff) << 11, m_maincpu->pc());
					return;

				case 2: // segment map
					//printf("sun2: Write %02x to segment map at %x (entry %d, user ctx %d PC=%x)\n", data & 0xff, offset<<1, offset>>14, m_context & 7, m_maincpu->pc());
					m_segmap[m_context & 7][offset >> 14] = data & 0xff;
					return;

				case 3: // context reg
					//printf("sun2: Write %04x to context\n", data);
					COMBINE_DATA(&m_context);
					return;
			}
		}
	}

	// it's translation time
	uint8_t context = (fc & 4) ? ((m_context >> 8) & 7) : (m_context & 7);
	uint8_t pmeg = m_segmap[context][offset >> 14];
	uint32_t entry = (pmeg << 4) + ((offset >> 10) & 0xf);

	if (m_pagemap[entry] & PM_VALID)
	{
		m_pagemap[entry] |= (PM_ACCESSED | PM_MODIFIED);

		// only 12 of the 20 bits in the page table entry are used on either Sun2 implementation
		uint32_t tmp = (m_pagemap[entry] & 0xfff) << 10;
		tmp |= (offset & 0x3ff);

		//if (!machine().side_effects_disabled()) printf("sun2: Translated addr: %08x, type %d (page entry %08x, orig virt %08x)\n", tmp << 1, (m_pagemap[entry] >> 22) & 7, m_pagemap[entry], offset<<1);

		switch ((m_pagemap[entry] >> 22) & 7)
		{
			case 0: // type 0
				m_type0space->write16(tmp, data, mem_mask);
				return;

			case 1: // type 1
				//printf("write device space @ %x\n", tmp<<1);
				m_type1space->write16(tmp, data, mem_mask);
				return;

			case 2: // type 2
				m_type2space->write16(tmp, data, mem_mask);
				return;

			case 3: // type 3
				m_type3space->write16(tmp, data, mem_mask);
				return;
		}
	}
	else
	{
		if (!machine().side_effects_disabled()) printf("sun2: pagemap entry not valid!\n");
	}

	printf("sun2: Unmapped write %04x (FC %d, mask %04x, PC=%x) to %08x\n", data, fc, mem_mask, m_maincpu->pc(), offset<<1);
}

// BW2 video control
uint16_t sun2_state::video_ctrl_r()
{
	return m_bw2_ctrl;
}

void sun2_state::video_ctrl_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	//printf("sun2: BW2: %x to video_ctrl\n", data);
	COMBINE_DATA(&m_bw2_ctrl);
}

// 82586 Ethernet Data Link Controller interface
uint8_t sun2_state::ethernet_r()
{
	return m_ethernet_status;
}

void sun2_state::ethernet_w(uint8_t data)
{
	m_edlc->reset_w(!BIT(data, 7));
	m_edlc->set_loopback(!BIT(data, 6)); // LBC on MB502
	m_edlc->ca(BIT(data, 5));

	m_ethernet_status = (data & 0xf0) | (m_ethernet_status & 0x0f);
	m_maincpu->set_input_line(M68K_IRQ_3, BIT(m_ethernet_status, 0) && BIT(m_ethernet_status, 4) ? ASSERT_LINE : CLEAR_LINE);
}

void sun2_state::ethernet_int_w(int state)
{
	if (state)
	{
		m_ethernet_status |= 0x01;
		if (BIT(m_ethernet_status, 4))
			m_maincpu->set_input_line(M68K_IRQ_3, ASSERT_LINE);
	}
	else
	{
		m_ethernet_status &= 0xfe;
		if (BIT(m_ethernet_status, 4))
			m_maincpu->set_input_line(M68K_IRQ_3, CLEAR_LINE);
	}
}

uint16_t sun2_state::edlc_mmu_r(offs_t offset, uint16_t mem_mask)
{
	return swapendian_int16(tl_mmu_r(M68K_FC_SUPERVISOR_DATA, offset, swapendian_int16(mem_mask)));
}

void sun2_state::edlc_mmu_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	tl_mmu_w(M68K_FC_SUPERVISOR_DATA, offset, swapendian_int16(data), swapendian_int16(mem_mask));
}

void sun2_state::sun2_mem(address_map &map)
{
	map(0x000000, 0xffffff).rw(FUNC(sun2_state::mmu_r), FUNC(sun2_state::mmu_w));
}

void sun2_state::edlc_mem(address_map &map)
{
	map(0x000000, 0xffffff).rw(FUNC(sun2_state::edlc_mmu_r), FUNC(sun2_state::edlc_mmu_w));
}

// VME memory spaces
// type 0 device space
void sun2_state::vmetype0space_map(address_map &map)
{
	map(0x000000, 0x7fffff).rw(FUNC(sun2_state::ram_r), FUNC(sun2_state::ram_w));
}

// type 1 device space
void sun2_state::vmetype1space_map(address_map &map)
{
	map(0x000000, 0x01ffff).ram().share(m_bw2_vram);
	map(0x020000, 0x020001).rw(FUNC(sun2_state::video_ctrl_r), FUNC(sun2_state::video_ctrl_w));
	map(0x7f0000, 0x7f07ff).rom().region("bootprom", 0);    // uses MMU loophole to read 32k from a 2k window
	map(0x7f0800, 0x7f0800).mirror(0x7fe).rw(FUNC(sun2_state::ethernet_r), FUNC(sun2_state::ethernet_w)).cswidth(16);
	// 7f1000-7f17ff: AM9518 encryption processor
	//map(0x7f1800, 0x7f1800).rw(SCC1_TAG, FUNC(z80scc_device::cb_r), FUNC(z80scc_device::cb_w));
	//map(0x7f1802, 0x7f1802).rw(SCC1_TAG, FUNC(z80scc_device::db_r), FUNC(z80scc_device::db_w));
	map(0x7f1804, 0x7f1805).nopr();
	//map(0x7f1804, 0x7f1804).rw(SCC1_TAG, FUNC(z80scc_device::ca_r), FUNC(z80scc_device::ca_w));
	//map(0x7f1806, 0x7f1806).rw(SCC1_TAG, FUNC(z80scc_device::da_r), FUNC(z80scc_device::da_w));
	map(0x7f2000, 0x7f2000).rw(SCC2_TAG, FUNC(z80scc_device::cb_r), FUNC(z80scc_device::cb_w));
	map(0x7f2002, 0x7f2002).rw(SCC2_TAG, FUNC(z80scc_device::db_r), FUNC(z80scc_device::db_w));
	map(0x7f2004, 0x7f2004).rw(SCC2_TAG, FUNC(z80scc_device::ca_r), FUNC(z80scc_device::ca_w));
	map(0x7f2006, 0x7f2006).rw(SCC2_TAG, FUNC(z80scc_device::da_r), FUNC(z80scc_device::da_w));
	map(0x7f2800, 0x7f2803).mirror(0x7fc).rw("timer", FUNC(am9513_device::read16), FUNC(am9513_device::write16));
}

// type 2 device space
void sun2_state::vmetype2space_map(address_map &map)
{
}

// type 3 device space
void sun2_state::vmetype3space_map(address_map &map)
{
}

// Multibus memory spaces
// type 0 device space
void sun2_state::mbustype0space_map(address_map &map)
{
	map(0x000000, 0x3fffff).rw(FUNC(sun2_state::ram_r), FUNC(sun2_state::ram_w));
	// 7f80000-7f807ff: Keyboard/mouse SCC8530
	//map(0x7f8000, 0x7f8007).rw(SCC1_TAG, FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w)).umask16(0xff00);
	map(0x700000, 0x71ffff).ram().share("bw2_vram");
	map(0x781800, 0x781801).rw(FUNC(sun2_state::video_ctrl_r), FUNC(sun2_state::video_ctrl_w));
}

// type 1 device space
void sun2_state::mbustype1space_map(address_map &map)
{
	map(0x000000, 0x0007ff).rom().region("bootprom", 0);    // uses MMU loophole to read 32k from a 2k window
	// 001000-0017ff: AM9518 encryption processor
	// 001800-001fff: Parallel port
	map(0x002000, 0x0027ff).rw(SCC2_TAG, FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w)).umask16(0xff00);
	map(0x002800, 0x002803).mirror(0x7fc).rw("timer", FUNC(am9513_device::read16), FUNC(am9513_device::write16));
	map(0x003800, 0x00383f).mirror(0x7c0).rw("rtc", FUNC(mm58167_device::read), FUNC(mm58167_device::write)).umask16(0xff00); // 12 wait states generated by PAL16R6 (U415)
}

// type 2 device space (Multibus memory space)
void sun2_state::mbustype2space_map(address_map &map)
{
}

// type 3 device space (Multibus I/O space)
void sun2_state::mbustype3space_map(address_map &map)
{
}

uint32_t sun2_state::bw2_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	static const uint32_t palette[2] = { 0, 0xffffff };
	auto const vram = util::big_endian_cast<uint8_t const>(m_bw2_vram.target());

	if (!(m_bw2_ctrl & 0x8000)) return 0;

	for (int y = 0; y < 900; y++)
	{
		uint32_t *scanline = &bitmap.pix(y);
		for (int x = 0; x < 1152/8; x++)
		{
			uint8_t const pixels = vram[(y * (1152 / 8)) + x];

			*scanline++ = palette[BIT(pixels, 7)];
			*scanline++ = palette[BIT(pixels, 6)];
			*scanline++ = palette[BIT(pixels, 5)];
			*scanline++ = palette[BIT(pixels, 4)];
			*scanline++ = palette[BIT(pixels, 3)];
			*scanline++ = palette[BIT(pixels, 2)];
			*scanline++ = palette[BIT(pixels, 1)];
			*scanline++ = palette[BIT(pixels, 0)];
		}
	}

	return 0;
}

/* Input ports */
static INPUT_PORTS_START( sun2 )
INPUT_PORTS_END

void sun2_state::machine_start()
{
	m_rom_ptr = (uint16_t *)m_rom->base();
	m_idprom_ptr = (uint8_t *)m_idprom->base();
	m_ram_ptr = (uint16_t *)m_ram->pointer();
	m_ram_size = m_ram->size();
	m_ram_size_words = m_ram_size >> 1;

	m_bw2_ctrl = 0;

	m_ethernet_status = 0;
}

void sun2_state::machine_reset()
{
	m_diagreg = 0;
	m_sysenable = 0;
	m_context = 0;
	m_buserror = 0;
	memset(m_segmap, 0, sizeof(m_segmap));
	memset(m_pagemap, 0, sizeof(m_pagemap));

	if (m_edlc.found())
		ethernet_w(0);
}

void sun2_state::sun2vme(machine_config &config)
{
	/* basic machine hardware */
	M68010(config, m_maincpu, 19.6608_MHz_XTAL / 2); // or 24_MHz_XTAL / 2 by jumper setting
	m_maincpu->set_addrmap(AS_PROGRAM, &sun2_state::sun2_mem);

	RAM(config, RAM_TAG).set_default_size("2M").set_extra_options("4M,6M,8M").set_default_value(0x00);

	// MMU Type 0 device space
	ADDRESS_MAP_BANK(config, "type0").set_map(&sun2_state::vmetype0space_map).set_options(ENDIANNESS_BIG, 16, 32, 0x1000000);

	// MMU Type 1 device space
	ADDRESS_MAP_BANK(config, "type1").set_map(&sun2_state::vmetype1space_map).set_options(ENDIANNESS_BIG, 16, 32, 0x1000000);

	// MMU Type 2 device space
	ADDRESS_MAP_BANK(config, "type2").set_map(&sun2_state::vmetype2space_map).set_options(ENDIANNESS_BIG, 16, 32, 0x1000000);

	// MMU Type 3 device space
	ADDRESS_MAP_BANK(config, "type3").set_map(&sun2_state::vmetype3space_map).set_options(ENDIANNESS_BIG, 16, 32, 0x1000000);

	screen_device &bwtwo(SCREEN(config, "bwtwo", SCREEN_TYPE_RASTER));
	bwtwo.set_screen_update(FUNC(sun2_state::bw2_update));
	bwtwo.set_raw(100_MHz_XTAL, 1600, 0, 1152, 937, 0, 900);

	I82586(config, m_edlc, 16_MHz_XTAL / 2);
	m_edlc->set_addrmap(0, &sun2_state::edlc_mem);
	m_edlc->out_irq_cb().set(FUNC(sun2_state::ethernet_int_w));

	am9513a_device &timer(AM9513A(config, "timer", 19.6608_MHz_XTAL / 4));
	timer.fout_cb().set("timer", FUNC(am9513_device::gate1_w));
	timer.out1_cb().set_inputline(m_maincpu, M68K_IRQ_7);
	timer.out2_cb().set("irq5", FUNC(input_merger_device::in_w<0>));
	timer.out3_cb().set("irq5", FUNC(input_merger_device::in_w<1>));
	timer.out4_cb().set("irq5", FUNC(input_merger_device::in_w<2>));
	timer.out5_cb().set("irq5", FUNC(input_merger_device::in_w<3>));

	INPUT_MERGER_ANY_HIGH(config, "irq5").output_handler().set_inputline(m_maincpu, M68K_IRQ_5); // 74LS05 open collectors

	SCC8530(config, SCC1_TAG, 19.6608_MHz_XTAL / 4);
	scc8530_device& scc2(SCC8530(config, SCC2_TAG, 19.6608_MHz_XTAL / 4));
	scc2.out_txda_callback().set(RS232A_TAG, FUNC(rs232_port_device::write_txd));
	scc2.out_txdb_callback().set(RS232B_TAG, FUNC(rs232_port_device::write_txd));
	scc2.out_int_callback().set_inputline(m_maincpu, M68K_IRQ_6);

	rs232_port_device &rs232a(RS232_PORT(config, RS232A_TAG, default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(SCC2_TAG, FUNC(z80scc_device::rxa_w));
	rs232a.dcd_handler().set(SCC2_TAG, FUNC(z80scc_device::dcda_w));
	rs232a.cts_handler().set(SCC2_TAG, FUNC(z80scc_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, RS232B_TAG, default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(SCC2_TAG, FUNC(z80scc_device::rxb_w));
	rs232b.dcd_handler().set(SCC2_TAG, FUNC(z80scc_device::dcdb_w));
	rs232b.cts_handler().set(SCC2_TAG, FUNC(z80scc_device::ctsb_w));
}

void sun2_state::sun2mbus(machine_config &config)
{
	/* basic machine hardware */
	M68010(config, m_maincpu, 39.3216_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &sun2_state::sun2_mem);

	RAM(config, RAM_TAG).set_default_size("2M").set_extra_options("4M").set_default_value(0x00);

	// MMU Type 0 device space
	ADDRESS_MAP_BANK(config, "type0").set_map(&sun2_state::mbustype0space_map).set_options(ENDIANNESS_BIG, 16, 32, 0x1000000);

	// MMU Type 1 device space
	ADDRESS_MAP_BANK(config, "type1").set_map(&sun2_state::mbustype1space_map).set_options(ENDIANNESS_BIG, 16, 32, 0x1000000);

	// MMU Type 2 device space
	ADDRESS_MAP_BANK(config, "type2").set_map(&sun2_state::mbustype2space_map).set_options(ENDIANNESS_BIG, 16, 32, 0x1000000);

	// MMU Type 3 device space
	ADDRESS_MAP_BANK(config, "type3").set_map(&sun2_state::mbustype3space_map).set_options(ENDIANNESS_BIG, 16, 32, 0x1000000);

	screen_device &bwtwo(SCREEN(config, "bwtwo", SCREEN_TYPE_RASTER));
	bwtwo.set_screen_update(FUNC(sun2_state::bw2_update));
	bwtwo.set_raw(100_MHz_XTAL, 1600, 0, 1152, 937, 0, 900);
	//bwtwo.set_raw(100_MHz_XTAL, 1600, 0, 1024, 1061, 0, 1024);

	am9513a_device &timer(AM9513A(config, "timer", 39.3216_MHz_XTAL / 8));
	timer.fout_cb().set("timer", FUNC(am9513_device::gate1_w));
	timer.out1_cb().set_inputline(m_maincpu, M68K_IRQ_7);
	timer.out2_cb().set("irq5", FUNC(input_merger_device::in_w<0>));
	timer.out3_cb().set("irq5", FUNC(input_merger_device::in_w<1>));
	timer.out4_cb().set("irq5", FUNC(input_merger_device::in_w<2>));
	timer.out5_cb().set("irq5", FUNC(input_merger_device::in_w<3>));

	INPUT_MERGER_ANY_HIGH(config, "irq5").output_handler().set_inputline(m_maincpu, M68K_IRQ_5); // 74LS05 open collectors

	SCC8530(config, SCC1_TAG, 39.3216_MHz_XTAL / 8);
	scc8530_device& scc2(SCC8530(config, SCC2_TAG, 39.3216_MHz_XTAL / 8));
	scc2.out_txda_callback().set(RS232A_TAG, FUNC(rs232_port_device::write_txd));
	scc2.out_txdb_callback().set(RS232B_TAG, FUNC(rs232_port_device::write_txd));
	scc2.out_int_callback().set_inputline(m_maincpu, M68K_IRQ_6);

	rs232_port_device &rs232a(RS232_PORT(config, RS232A_TAG, default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(SCC2_TAG, FUNC(z80scc_device::rxa_w));
	rs232a.dcd_handler().set(SCC2_TAG, FUNC(z80scc_device::dcda_w));
	rs232a.cts_handler().set(SCC2_TAG, FUNC(z80scc_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, RS232B_TAG, default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(SCC2_TAG, FUNC(z80scc_device::rxb_w));
	rs232b.dcd_handler().set(SCC2_TAG, FUNC(z80scc_device::dcdb_w));
	rs232b.cts_handler().set(SCC2_TAG, FUNC(z80scc_device::ctsb_w));

	MM58167(config, "rtc", 32.768_kHz_XTAL);
}

/* ROM definition */
ROM_START( sun2_120 ) // ROMs are located on the '501-1007' CPU PCB at locations B11 and B10; J400 is set to 1-2 for 27128 EPROMs and 3-4 for 27256 EPROMs
	ROM_REGION16_BE(0x10000, "bootprom", ROMREGION_ERASEFF)
	// There is an undumped revision 1.1.2, which uses 27256 EPROMs
	ROM_SYSTEM_BIOS(0, "rev10f", "Bootrom Rev 1.0F")
	ROMX_LOAD("1.0f.b11", 0x0000, 0x8000, CRC(8fb0050a) SHA1(399cdb894b2a66d847d76d8a5d266906fb1d3430), ROM_SKIP(1) | ROM_BIOS(0)) // actual rom stickers had fallen off
	ROMX_LOAD("1.0f.b10", 0x0001, 0x8000, CRC(70de816d) SHA1(67e980497f463dbc529f64ec5f3e0046b3901b7e), ROM_SKIP(1) | ROM_BIOS(0)) // "
	ROM_SYSTEM_BIOS(1, "revr", "Bootrom Rev R")
	ROMX_LOAD("520-1102-03.b11", 0x0000, 0x4000, CRC(020bb0a8) SHA1(a7b60e89a40757975a5d345d57ea02781dea4f89), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("520-1101-03.b10", 0x0001, 0x4000, CRC(b97c61f7) SHA1(9f08fe232cfc3da48539fa66673fc1f89a362b1e), ROM_SKIP(1) | ROM_BIOS(1))
	// There is an undumped revision Q, with roms:
	//ROM_SYSTEM_BIOS( 8, "revq", "Bootrom Rev Q")
	// ROMX_LOAD( "520-1104-02.b11", 0x0000, 0x4000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(8))
	// ROMX_LOAD( "520-1103-02.b10", 0x0001, 0x4000, NO_DUMP, ROM_SKIP(1) | ROM_BIOS(8))
	ROM_SYSTEM_BIOS( 2, "revn", "Bootrom Rev N") // SunOS 2.0 requires this bootrom version at a minimum; this version supports the sun-2 keyboard
	ROMX_LOAD("revn.b11", 0x0000, 0x4000, CRC(b1e70965) SHA1(726b3ed9323750a1ae238cf6dccaed6ff5981ad1), ROM_SKIP(1) | ROM_BIOS(2)) // actual rom stickers had fallen off
	ROMX_LOAD("revn.b10", 0x0001, 0x4000, CRC(95fd9242) SHA1(1eee2d291f4b18f6aafdde1a9521d88e454843b9), ROM_SKIP(1) | ROM_BIOS(2)) // "
	ROM_SYSTEM_BIOS( 3, "revm", "Bootrom Rev M") // SunOS 1.0 apparently requires this bootrom revision; this version might only support the sun-1 keyboard?
	ROMX_LOAD("sun2-revm-8.b11", 0x0000, 0x4000, CRC(98b8ae55) SHA1(55485f4d8fd1ebc218aa8527c8bb62752c34abf7), ROM_SKIP(1) | ROM_BIOS(3)) // handwritten label: "SUN2-RevM-8"
	ROMX_LOAD("sun2-revm-0.b10", 0x0001, 0x4000, CRC(5117f431) SHA1(fce85c11ada1614152dde35bb329350f6fb2ecd9), ROM_SKIP(1) | ROM_BIOS(3)) // handwritten label: "SUN2-RevM-0"

	ROM_REGION(0x20, "idprom", ROMREGION_ERASEFF)
	ROM_LOAD("sun2120-idprom.bin", 0x000000, 0x000020, CRC(eec8cd1d) SHA1(6a78dc0ea6f9cc7687cffea754d65864fb751ebf))
ROM_END

ROM_START( sun2_50 )
	ROM_REGION16_BE(0x8000, "bootprom", ROMREGION_ERASEFF)
	// There is at least one undumped revision (Rev 1.1.2) which uses 27256 EPROMs; the sun2/50 board handles up to 27512 EPROMs
	// bootrom rev Q
	ROM_LOAD16_BYTE("250_q_8.rom", 0x0000, 0x4000, CRC(5bfacb5c) SHA1(ec7fb3fb0217b0138ba4748b7c79b8ff0cad896b))
	ROM_LOAD16_BYTE("250_q_0.rom", 0x0001, 0x4000, CRC(2ee29abe) SHA1(82f52b9f25e92387329581f7c8ba50a171784968))

	ROM_REGION(0x20, "idprom", ROMREGION_ERASEFF)
	ROM_LOAD("sun250-idprom.bin", 0x000000, 0x000020, CRC(927744ab) SHA1(d29302b69128165e69dd3a79b8c8d45f2163b88a))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT  CLASS       INIT        COMPANY             FULLNAME     FLAGS
COMP( 1984, sun2_50,  0,      0,      sun2vme,  sun2,  sun2_state, empty_init, "Sun Microsystems", "Sun 2/50",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
COMP( 1984, sun2_120, 0,      0,      sun2mbus, sun2,  sun2_state, empty_init, "Sun Microsystems", "Sun 2/120", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



sun3.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, R. Belmont
/***************************************************************************

  sun3.c: preliminary driver for Sun 3 models

    Sun-3 Models
    ------------

    3/160
        Processor(s):   68020 @ 16.67MHz, 68881, Sun-3 MMU, 8 hardware
                        contexts, 2 MIPS
        CPU:            501-1074/1096/1163/1164/1208
        Chassis type:   deskside
        Bus:            VME, 12 slots
        Memory:         16M physical (documented), 256M virtual, 270ns cycle
        Notes:          First 68020-based Sun machine. Uses the 3004
                        "Carrera" CPU, which is used in most other Sun
                        3/1xx models and the 3/75. Sun supplied 4M
                        memory expansion boards; third parties had up to
                        32M on one card. SCSI optional. One variant of
                        the memory card holds a 6U VME SCSI board; there
                        is also a SCSI board which sits in slot 7 of the
                        backplane and runs the SCSI bus out the back of
                        the backplane to the internal disk/tape (slot 6
                        in very early backplanes). CPU has two serial
                        ports, Ethernet, keyboard. Type 3 keyboard plugs
                        into the CPU; Sun-3 mouse plugs into the
                        keyboard. Upgradeable to a 3/260 by replacing
                        CPU and memory boards.

    3/75
        Processor(s):   68020 @ 16.67MHz, 68881, Sun-3 MMU, 8 hardware
                        contexts, 2 MIPS
        CPU:            501-1074/1094/1163/1164
        Chassis type:   wide pizza box
        Bus:            VME, 2 slot
        Memory:         16M physical (documented), 256M virtual, 270ns cycle
        Notes:          Optional SCSI sits on memory expansion board in
                        second slot.

    3/140
        Processor(s):   68020 @ 16.67MHz, 68881, Sun-3 MMU, 8 hardware
                        contexts, 2 MIPS
        CPU:            501-1074/1094/1163/1164/1208
        Chassis type:   deskside
        Bus:            VME, 3 slots
        Memory:         16M physical (documented), 256M virtual, 270ns cycle

    3/150
        Processor(s):   68020 @ 16.67MHz, 68881, Sun-3 MMU, 8 hardware
                        contexts, 2 MIPS
        CPU:            501-1074/1094/1163/1164/1208
        Chassis type:   deskside
        Bus:            VME, 6 slots
        Memory:         16M physical (documented), 256M virtual, 270ns cycle

    3/180
        Processor(s):   68020 @ 16.67MHz, 68881, Sun-3 MMU, 8 hardware
                        contexts, 2 MIPS
        CPU:            501-1074/1094/1163/1164/1208
        Chassis type:   rackmount
        Bus:            VME, 12 slots
        Memory:         16M physical (documented), 256M virtual, 270ns cycle
        Notes:          Rackmount version of 3/160. Upgradeable to a
                        3/280 by replacing the CPU and memory boards.
                        Very early backplanes have the special SCSI
                        hookup on slot 6 rather than 7.

    3/110
        Processor(s):   68020
        CPU:            501-1134/1209
        Chassis type:   deskside
        Bus:            VME, 3 slots
        Notes:          Similar to the "Carerra" CPU, but has 8-bit
                        color framebuffer (cgfour) on board and uses 1M
                        RAM chips for 4M on-CPU memory. Code-named
                        "Prism".

    3/50
        Processor(s):   68020 @ 15.7MHz, 68881 (socket for
                        501-1075/1133/1162, installed for 501-1207),
                        Sun-3 MMU, 8 hardware contexts, 1.5 MIPS
        CPU:            501-1075/1133/1162/1207
        Chassis type:   wide pizza box
        Bus:            none
        Memory:         4M physical (documented), 256M virtual, 270ns cycle
        Notes:          Cycle-stealing monochrome frame buffer. 4M
                        memory maximum stock, but third-party memory
                        expansion boards were sold, allowing up to at
                        least 12M. No bus or P4 connector. Onboard SCSI.
                        Thin coax or AUI Ethernet. Code-named "Model
                        25".

    3/60
        Processor(s):   68020 @ 20MHz, 68881 (stock), Sun-3 MMU,
                        8 hardware contexts, 3 MIPS
        CPU:            501-1205/1322/1334/1345
        Chassis type:   wide pizza box
        Bus:            P4 connector (not same as P4 on 3/80)
        Memory:         24M physical, 256M virtual, 200ns cycle
        Notes:          VRAM monochome frame buffer for 501-1205/1334.
                        Optional color frame buffer (can run mono and
                        color simultaneously) on P4 connector. Onboard
                        SCSI. SIMM memory (100ns 1M x 9 SIMMs). High
                        (1600 * 1100) or low (1152 * 900) resolution
                        mono selectable by jumper. Thin coax or AUI
                        Ethernet. Code-named "Ferrari". 4M stock on
                        501-1205/1322, 0M stock on 501-1322/1345.

    3/60LE
        Processor(s):   68020 @ 20MHz, 68881 (stock), Sun-3 MMU,
                        8 hardware contexts, 3 MIPS
        CPU:            501-1378
        Bus:            P4 connector (not same as P4 on 3/80)
        Memory:         12M physical, 256M virtual, 200ns cycle
        Notes:          A version of the 3/60 with no onboard
                        framebuffer and limited to 12M of RAM (4M of
                        256K SIMMs and 8M of 1M SIMMs).

    3/260
        Processor(s):   68020 @ 25MHz, 68881 @ 20MHz (stock), Sun-3 MMU,
                        8 hardware contexts, 4 MIPS
        CPU:            501-1100/1206
        Chassis type:   deskside
        Bus:            VME, 12 slot
        Memory:         64M (documented) physical with ECC, 256M virtual;
                        64K write-back cache, direct-mapped,
                        virtually-indexed and virtually-tagged, with
                        16-byte lines; 80ns cycle
        Notes:          Two serial ports, AUI Ethernet, keyboard, and
                        video on CPU. Video is mono, high-resolution
                        only. Sun supplied 8M memory boards. Sun 4/2xx
                        32M boards work up to 128M. First Sun with an
                        off-chip cache. Upgradeable to a 4/260 by
                        replacing the CPU board. Code-named "Sirius".

    3/280
        Processor(s):   68020 @ 25MHz, 68881 @ 20MHz (stock), Sun-3 MMU,
                        8 hardware contexts, 4 MIPS
        CPU:            501-1100/1206
        Chassis type:   rackmount
        Bus:            VME, 12 slot
        Memory:         64M (documented) physical with ECC, 256M virtual;
                        64K write-back cache, direct-mapped,
                        virtually-indexed and virtually-tagged, with
                        16-byte lines; 80ns cycle
        Notes:          Rackmount version of the 3/260. Upgradeable to a
                        4/280 by replacing the CPU board. Code-named
                        "Sirius".

    3/E
        Processor(s):   68020
        CPU:            501-8028
        Bus:            VME
        Notes:          Single-board VME Sun-3, presumably for use as a
                        controller, not as a workstation. 6U form
                        factor. Serial and keyboard ports. External RAM,
                        framebuffer, and SCSI/ethernet boards
                        available.

3/60 ROM breakpoints of interest
fefb104 - bus error test
fefb18e - interrupt test
fefb1da - clock IRQ test
fefb344 - MMU page valid test
fefb3c4 - MMU read-only permissions test
fefb45e - parity test: no spurious NMI generated from normal parity operation
fefb50c - parity test: NMI is generated when we write with inverted parity and enable parity NMIs
fefb5c8 - size memory by bus error
fef581c - EPROM mapping check
fef02b2 - main screen turn on

3/260 ROM breakpoints of interest
fefcb34 - bpset to stop before each test
fefc0c2 - start of ECC test
fefc34a - start of mem_size, which queries ECC registers for each memory board

****************************************************************************/

#include "emu.h"

#include "cpu/m68000/m68020.h"
#include "machine/am9516.h"
#include "machine/bankdev.h"
#include "machine/nvram.h"
#include "machine/ram.h"
#include "machine/timekpr.h"
#include "machine/timer.h"
#include "machine/z80scc.h"
#include "machine/am79c90.h"
#include "machine/ncr5380.h"
#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"
#include "bus/nscsi/tape.h"

#include "bus/rs232/rs232.h"
#include "bus/sunkbd/sunkbd.h"
#include "bus/sunmouse/sunmouse.h"

#include "screen.h"


namespace {

#define TIMEKEEPER_TAG  "timekpr"
#define SCC1_TAG        "scc1"
#define SCC2_TAG        "scc2"
#define RS232A_TAG      "rs232a"
#define RS232B_TAG      "rs232b"
#define KEYBOARD_TAG    "keyboard"
#define MOUSE_TAG       "mouseport"

// page table entry constants
#define PM_VALID    (0x80000000)    // page is valid
#define PM_WRITEMASK (0x40000000)   // writable?
#define PM_SYSMASK  (0x20000000)    // system use only?
#define PM_CACHE    (0x10000000)    // cachable?
#define PM_TYPEMASK (0x0c000000)    // type mask
#define PM_ACCESSED (0x02000000)    // accessed flag
#define PM_MODIFIED (0x01000000)    // modified flag

#define BE_FPENABLE (0x04)  // FPU not enabled
#define BE_FPBERR   (0x08)  // FPU encountered a bus error
#define BE_VMEBERR  (0x10)  // VME encountered a bus error
#define BE_TIMEOUT  (0x20)  // timeout - memory doesn't exist
#define BE_PROTERR  (0x40)  // protection failed on MMU page lookup
#define BE_INVALID  (0x80)  // invalid entry on MMU page lookup

class sun3_state : public driver_device
{
public:
	sun3_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_scc1(*this, SCC1_TAG),
		m_scc2(*this, SCC2_TAG),
		m_sbc(*this, "scsibus:7:sbc"),
		m_udc(*this, "udc"),
		m_p_ram(*this, "p_ram"),
		m_bw2_vram(*this, "bw2_vram"),
		m_type0space(*this, "type0"),
		m_type1space(*this, "type1"),
		m_type2space(*this, "type2"),
		m_type3space(*this, "type3"),
		m_rom(*this, "user1"),
		m_idprom(*this, "idprom"),
		m_ram(*this, RAM_TAG),
		m_lance(*this, "lance"),
		m_diagsw(*this, "diagsw")
	{ }

	void sun3(machine_config &config);
	void sun3e(machine_config &config);
	void sun3_60(machine_config &config);
	void sun3200(machine_config &config);
	void sun3_50(machine_config &config);

	void ncr5380(device_t *device);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<m68020_device> m_maincpu;
	required_device<z80scc_device> m_scc1;
	required_device<z80scc_device> m_scc2;
	required_device<ncr5380_device> m_sbc;
	required_device<am9516_device> m_udc;

	optional_shared_ptr<uint32_t> m_p_ram;
	optional_shared_ptr<uint32_t> m_bw2_vram;
	optional_device<address_map_bank_device> m_type0space, m_type1space, m_type2space, m_type3space;
	required_memory_region m_rom, m_idprom;
	required_device<ram_device> m_ram;
	required_device<am79c90_device> m_lance;
	required_ioport m_diagsw;

	uint32_t tl_mmu_r(offs_t offset, uint32_t mem_mask = ~0);
	void tl_mmu_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t ram_r(offs_t offset);
	void ram_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t parity_r(offs_t offset);
	void parity_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t ecc_r(offs_t offset);
	void ecc_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t irqctrl_r();
	void irqctrl_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint8_t rtc7170_r(offs_t offset);
	void rtc7170_w(offs_t offset, uint8_t data);
	uint16_t scsictrl_r();
	void scsictrl_w(uint16_t data);

	template <unsigned W, unsigned H>
	uint32_t bw2_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	uint32_t bw2_350_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	TIMER_DEVICE_CALLBACK_MEMBER(sun3_timer);

	void sun3_mem(address_map &map) ATTR_COLD;
	void vmetype0space_map(address_map &map) ATTR_COLD;
	void vmetype0space_novram_map(address_map &map) ATTR_COLD;
	void vmetype1space_map(address_map &map) ATTR_COLD;
	void vmetype2space_map(address_map &map) ATTR_COLD;
	void vmetype3space_map(address_map &map) ATTR_COLD;

	uint32_t enable_r();

	uint32_t *m_rom_ptr, *m_ram_ptr;
	uint8_t *m_idprom_ptr;
	uint32_t m_enable, m_diag, m_dvma_enable, m_parregs[8], m_irqctrl, m_ecc[4];
	uint16_t m_scsictrl;
	uint8_t m_buserr;

	uint32_t m_context;
	uint8_t m_segmap[8][2048];
	uint32_t m_pagemap[4096];
	uint32_t m_ram_size, m_ram_size_words;
	bool m_bInBusErr;

	uint32_t m_cache_tags[0x4000], m_cache_data[0x4000];
};

static void sun_cdrom(device_t *device)
{
	downcast<nscsi_cdrom_device &>(*device).set_block_size(512);
}

void sun3_state::ncr5380(device_t *device)
{
	devcb_base *devcb;
	(void)devcb;
//  downcast<ncr5380_device &>(*device).drq_handler().set(FUNC(sun3_state::drq_w));
}

static void scsi_devices(device_slot_interface &device)
{
	device.option_add("cdrom", NSCSI_CDROM);
	device.option_add("harddisk", NSCSI_HARDDISK);
	device.option_add("tape", NSCSI_TAPE);
	device.set_option_machine_config("cdrom", sun_cdrom);
}

uint32_t sun3_state::ram_r(offs_t offset)
{
	if (m_ecc[0] == 0x10c00000)
	{
		//printf("ECC testing, RAM read ofs %x\n", offset);
		m_ecc[1] &= 0x00ffffff;

		if (m_ecc[2] == 0x01000000) // single-bit error test
		{
			m_ecc[1] |= 0x8f000000; // put in the syndrome code the first ECC test wants

			if (offset == 0)
			{
				return 0x80000000;
			}
		}
		else if (m_ecc[2] == 0x01000200)    // double-bit error test
		{
			m_ecc[1] |= 0xfc000000;
		}
	}

	if (offset < m_ram_size_words) return m_ram_ptr[offset];

	if (!m_bInBusErr)
	{
		//printf("ram_r: bus error on timeout, access to invalid addr %08x, PC=%x\n", offset<<2, m_maincpu->pc());
		//fflush(stdout);
		m_buserr = BE_TIMEOUT;
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
	}

	return 0xffffffff;
}

void sun3_state::ram_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	// if writing bad parity is enabled
	if (((m_parregs[0] & 0x20000000) == 0x20000000) &&
		(m_irqctrl & 0x01000000) &&
		!(m_bInBusErr))
	{
		m_parregs[1] = offset<<2;
		//printf("Generating parity error, mem_mask %08x\n", mem_mask);
		switch (mem_mask)
		{
			case 0xff000000:
				m_parregs[0] |= 0x08<<24;
				break;

			case 0x00ff0000:
				m_parregs[1] += 1;
				m_parregs[0] |= 0x04<<24;
				break;

			case 0x0000ff00:
				m_parregs[1] += 2;
				m_parregs[0] |= 0x02<<24;
				break;

			case 0x000000ff:
				m_parregs[1] += 3;
				m_parregs[0] |= 0x01<<24;
				break;

			case 0x0000ffff:
				m_parregs[1] += 2;
				m_parregs[0] |= 0x03<<24;
				break;

			case 0xffff0000:
				m_parregs[0] |= 0x0c<<24;
				break;

			case 0xffffffff:    // no address adjust, show all 4 lanes as problematic
				m_parregs[0] |= 0x0f<<24;
				break;
		}

		// indicate parity interrupt
		m_parregs[0] |= 0x80000000;

		// and can we take that now?
		if (m_parregs[0] & 0x40000000)
		{
			m_bInBusErr = true; // prevent recursion
			m_maincpu->set_input_line(M68K_IRQ_7, ASSERT_LINE);
		}
	}

	if (offset < m_ram_size_words)
	{
		COMBINE_DATA(&m_ram_ptr[offset]);
		return;
	}

	if (!m_bInBusErr)
	{
		//printf("ram_w: bus error on timeout, access to invalid addr %08x, PC=%x\n", offset<<2, m_maincpu->pc());
		fflush(stdout);
		m_buserr = BE_TIMEOUT;
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
	}
}

uint32_t sun3_state::tl_mmu_r(offs_t offset, uint32_t mem_mask)
{
	uint8_t fc = m_maincpu->get_fc();

	if ((fc == 3) && !machine().side_effects_disabled())
	{
		int page;

		switch (offset >> 26)
		{
			case 0: // IDPROM
				//printf("sun3: Read IDPROM at %x (mask %08x)\n", offset, mem_mask);
				return m_idprom_ptr[(offset*4)] << 24 | m_idprom_ptr[(offset*4)+1]<<16 | m_idprom_ptr[(offset*4)+2]<<8 | m_idprom_ptr[(offset*4)+3];

			case 1: // page map
				page = m_segmap[m_context & 7][(offset >> 15) & 0x7ff] << 4;
				page += (offset >> 11) & 0xf;
				//printf("sun3: Read page map at %x (entry %d)\n", offset<<1, page);
				return m_pagemap[page];

			case 2: // segment map
				//printf("sun3: Read segment map at %x (entry %d, user ctx %d mask %x)\n", offset<<2, (offset & ~0x3c000000) >> 15, m_context & 7, mem_mask);
				return m_segmap[m_context & 7][(offset >> 15) & 0x7ff]<<24;

			case 3: // context reg
				//printf("sun3: Read context reg\n");
				return m_context<<24;

			case 4: // enable reg
				return enable_r();

			case 5: // DVMA enable
				return m_dvma_enable<<24;

			case 6: // bus error
				m_bInBusErr = false;
				//printf("Reading bus error: %02x\n", m_buserr);
				return m_buserr<<24;

			case 7: // diagnostic reg
				return 0;

			case 8: // cache tags
				//printf("sun3: read cache tags @ %x, PC = %x\n", offset, m_maincpu->pc());
				return m_cache_tags[(offset & 0x3fff) >> 2];

			case 9: // cache data
				//printf("sun3: read cache data @ %x, PC = %x\n", offset, m_maincpu->pc());
				return m_cache_data[(offset & 0x3fff)];

			case 10: // flush cache
				return 0xffffffff;

			case 11: // block copy
				printf("sun3: read block copy @ %x, PC = %x\n", offset, m_maincpu->pc());
				return 0xffffffff;

			case 15: // UART bypass
				//printf("sun3: read UART bypass @ %x, PC = %x, mask = %08x\n", offset, m_maincpu->pc(), mem_mask);
				return 0xffffffff;
		}
	}

	// boot mode?
	if ((fc == M68K_FC_SUPERVISOR_PROGRAM) && !(enable_r() & 0x80))
	{
		return m_rom_ptr[offset & 0x3fff];
	}

	// debugger hack
	if (machine().side_effects_disabled() && (offset >= (0xfef0000>>2)) && (offset <= (0xfefffff>>2)))
	{
		return m_rom_ptr[offset & 0x3fff];
	}

	// it's translation time
	uint8_t pmeg = m_segmap[m_context & 7][(offset >> 15) & 0x7ff];
	uint32_t entry = (pmeg << 4) + ((offset >> 11) & 0xf);

	//printf("sun3: Context = %d, pmeg = %d, offset >> 15 = %x, entry = %d, page = %d\n", m_context&7, pmeg, (offset >> 15) & 0x7ff, entry, (offset >> 11) & 0xf);

	if (m_pagemap[entry] & PM_VALID)
	{
		if ((m_pagemap[entry] & PM_SYSMASK) && (fc < M68K_FC_SUPERVISOR_DATA))
		{
			m_buserr = BE_PROTERR;
			m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
			m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
			m_bInBusErr = true;
			return 0xffffffff;
		}

		m_pagemap[entry] |= PM_ACCESSED;

		uint32_t tmp = (m_pagemap[entry] & 0x7ffff) << 11;
		tmp |= (offset & 0x7ff);

		//printf("pmeg %d, entry %d = %08x, virt %08x => tmp %08x\n", pmeg, entry, m_pagemap[entry], offset << 2, tmp);

	//  if (!machine().side_effects_disabled())
		//printf("sun3: Translated addr: %08x, type %d (page %d page entry %08x, orig virt %08x, FC %d)\n", tmp << 2, (m_pagemap[entry] >> 26) & 3, entry, m_pagemap[entry], offset<<2, fc);

		switch ((m_pagemap[entry] >> 26) & 3)
		{
			case 0: // type 0 space
				return m_type0space->read32(tmp, mem_mask);

			case 1: // type 1 space
				// magic ROM bypass
				if ((tmp >= (0x100000>>2)) && (tmp <= (0x10ffff>>2)))
				{
					return m_rom_ptr[offset & 0x3fff];
				}
				return m_type1space->read32(tmp, mem_mask);

			case 2: // type 2 space
				return m_type2space->read32(tmp, mem_mask);

			case 3: // type 3 space
				return m_type3space->read32(tmp, mem_mask);
		}
	}
	else
	{
//      if (!machine().side_effects_disabled()) printf("sun3: pagemap entry not valid! (PC=%x)\n", m_maincpu->pc());
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
		m_buserr = BE_INVALID;
		m_bInBusErr = true;
		return 0xffffffff;
	}

	if (!machine().side_effects_disabled()) logerror("sun3: Unmapped read @ %08x (FC %d, mask %08x, PC=%x, seg %x)\n", offset<<2, fc, mem_mask, m_maincpu->pc(), offset>>15);

	return 0xffffffff;
}

void sun3_state::tl_mmu_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	uint8_t fc = m_maincpu->get_fc();

	//printf("sun3: Write %08x (FC %d, mask %08x, PC=%x) to %08x\n", data, fc, mem_mask, m_maincpu->pc(), offset<<1);

	if (fc == 3)    // control space
	{
		int page;

		switch (offset >> 26)
		{
			case 0: // IDPROM
				return;

			case 1: // page map  fefaf32
				page = m_segmap[m_context & 7][(offset >> 15) & 0x7ff] << 4;
				//printf("context = %d, segment = %d, PMEG = %d, add = %d\n", m_context & 7, (offset >> 15) & 0x7ff, page, (offset >> 11) & 0xf);
				page += (offset >> 11) & 0xf;

				//printf("sun3: Write %04x to page map at %x (entry %d), ", data, offset<<2, page);
				COMBINE_DATA(&m_pagemap[page]);

				//printf("entry now %08x (adr %08x  PC=%x mask %x)\n", m_pagemap[page], (m_pagemap[page] & 0xfffff) << 13, m_maincpu->pc(), mem_mask);
				return;

			case 2: // segment map
				//printf("sun3: Write %02x to segment map at %x (entry %d, user ctx %d PC=%x mask %x)\n", (data>>24) & 0xff, offset<<2, (offset & ~0x3c000000)>>15, m_context & 7, m_maincpu->pc(), mem_mask);
				m_segmap[m_context & 7][(offset >> 15) & 0x7ff] = (data>>24) & 0xff;
				//printf("segment map[%d][%d] now %x\n", m_context & 7, (offset & ~0x3c000000) >> 15, m_segmap[m_context & 7][(offset & ~0x3c000000) >> 15] = (data>>24) & 0xff);
				return;

			case 3: // context reg
				//printf("sun3: Write (%x) %x to context\n", data, data>>24);
				m_context = data >> 24;
				return;

			case 4: // enable reg
				//printf("sun3: Write %x to enable, PC=%x\n", data, m_maincpu->pc());
				COMBINE_DATA(&m_enable);
				return;

			case 5: // DVMA enable
				m_dvma_enable = data>>24;
				return;

			case 6: // bus error (read-only)
				return;

			case 7: // diagnostic reg
				m_diag = data >> 24;
				#if 0
				printf("sun3: CPU LEDs to %02x (PC=%x) => ", ((data>>24) & 0xff) ^ 0xff, m_maincpu->pc());
				for (int i = 0; i < 8; i++)
				{
					if (m_diag & (1<<i))
					{
						printf(".");
					}
					else
					{
						printf("*");
					}
				}
				printf("\n");
				#endif
				return;

			case 8: // cache tags
				//printf("sun3: %08x to cache tags @ %x, PC = %x, mask = %08x\n", data, offset, m_maincpu->pc(), mem_mask);
				m_cache_tags[(offset & 0x3fff) >> 2] = data;
				return;

			case 9: // cache data
				//printf("sun3: %08x to cache data @ %x, PC = %x, mask = %08x\n", data, offset, m_maincpu->pc(), mem_mask);
				m_cache_data[(offset & 0x3fff)] = data;
				return;

			case 10: // flush cache
				return;

			case 11: // block copy
				printf("sun3: %08x to block copy @ %x, PC = %x\n", data, offset, m_maincpu->pc());
				return;

			case 15: // UART bypass
				//printf("sun3: %08x to UART bypass @ %x, PC = %x\n", data, offset, m_maincpu->pc());
				return;
			}
	}

	// it's translation time
	uint8_t pmeg = m_segmap[m_context & 7][(offset >> 15) & 0x7ff];
	uint32_t entry = (pmeg << 4) + ((offset >> 11) & 0xf);

	if (m_pagemap[entry] & PM_VALID)
	{
		if ((!(m_pagemap[entry] & PM_WRITEMASK)) ||
			((m_pagemap[entry] & PM_SYSMASK) && (fc < M68K_FC_SUPERVISOR_DATA)))
		{
			//printf("sun3: write protect MMU error (PC=%x)\n", m_maincpu->pc());
			m_buserr = BE_PROTERR;
			m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
			m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
			m_bInBusErr = true;
			return;
		}

		m_pagemap[entry] |= (PM_ACCESSED | PM_MODIFIED);

		uint32_t tmp = (m_pagemap[entry] & 0x7ffff) << 11;
		tmp |= (offset & 0x7ff);

		//if (!machine().side_effects_disabled()) printf("sun3: Translated addr: %08x, type %d (page entry %08x, orig virt %08x)\n", tmp << 2, (m_pagemap[entry] >> 26) & 3, m_pagemap[entry], offset<<2);

		switch ((m_pagemap[entry] >> 26) & 3)
		{
			case 0: // type 0
				m_type0space->write32(tmp, data, mem_mask);
				return;

			case 1: // type 1
				//printf("write device space @ %x\n", tmp<<1);
				m_type1space->write32(tmp, data, mem_mask);
				return;

			case 2: // type 2
				m_type2space->write32(tmp, data, mem_mask);
				return;

			case 3: // type 3
				m_type3space->write32(tmp, data, mem_mask);
				return;
		}
	}
	else
	{
		//if (!machine().side_effects_disabled()) printf("sun3: pagemap entry not valid!\n");
		m_buserr = BE_INVALID;
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
		m_bInBusErr = true;
		return;
	}

	logerror("sun3: Unmapped write %04x (FC %d, mask %04x, PC=%x) to %08x\n", data, fc, mem_mask, m_maincpu->pc(), offset<<2);
}

uint32_t sun3_state::parity_r(offs_t offset)
{
	uint32_t rv = m_parregs[offset];

	if (offset == 0)    // clear interrupt if any
	{
		m_maincpu->set_input_line(M68K_IRQ_7, CLEAR_LINE);
		m_parregs[offset] &= ~0x8f000000;
	}

	return rv;
}

void sun3_state::parity_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	//printf("sun3: %08x to parity registers @ %x (mask %08x)\n", data, offset, mem_mask);

	if (offset == 0)
	{
		m_parregs[0] &= 0x8f000000;

		if ((m_parregs[0] & 0x80000000) && (data & 0x40000000))
		{
			m_maincpu->set_input_line(M68K_IRQ_7, ASSERT_LINE);
		}

		m_parregs[0] |= (data & 0x70000000);
	}
	else
	{
		COMBINE_DATA(&m_parregs[offset]);
	}
}

void sun3_state::sun3_mem(address_map &map)
{
	map(0x00000000, 0xffffffff).rw(FUNC(sun3_state::tl_mmu_r), FUNC(sun3_state::tl_mmu_w));
}

// type 0 device space
void sun3_state::vmetype0space_map(address_map &map)
{
	map(0x00000000, 0x08ffffff).rw(FUNC(sun3_state::ram_r), FUNC(sun3_state::ram_w));
	map(0xfe400000, 0xfe41ffff).ram(); // not sure what's going on here (3/110)
	map(0xff000000, 0xff03ffff).ram().share(m_bw2_vram);
}

// type 0 without VRAM (3/50)
void sun3_state::vmetype0space_novram_map(address_map &map)
{
	map(0x00000000, 0x08ffffff).rw(FUNC(sun3_state::ram_r), FUNC(sun3_state::ram_w));
}

// type 1 device space
void sun3_state::vmetype1space_map(address_map &map)
{
	map(0x00000000, 0x0000000f).rw(m_scc1, FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w)).umask32(0xff00ff00);
	map(0x00020000, 0x0002000f).rw(m_scc2, FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w)).umask32(0xff00ff00);
	map(0x00040000, 0x000407ff).ram().share("nvram");   // type 2816 parallel EEPROM
	map(0x00060000, 0x0006ffff).rw(FUNC(sun3_state::rtc7170_r), FUNC(sun3_state::rtc7170_w));
	map(0x00080000, 0x0008000f).rw(FUNC(sun3_state::parity_r), FUNC(sun3_state::parity_w));
	map(0x000a0000, 0x000a0003).rw(FUNC(sun3_state::irqctrl_r), FUNC(sun3_state::irqctrl_w));
	map(0x00100000, 0x0010ffff).rom().region("user1", 0);
	map(0x00120000, 0x00120003).rw(m_lance, FUNC(am79c90_device::regs_r), FUNC(am79c90_device::regs_w));
	map(0x00140000, 0x00140007).rw(m_sbc, FUNC(ncr5380_device::read), FUNC(ncr5380_device::write)).umask32(0xffffffff);
	map(0x00140010, 0x00140011).rw(m_udc, FUNC(am9516_device::data_r), FUNC(am9516_device::data_w));
	map(0x00140012, 0x00140013).rw(m_udc, FUNC(am9516_device::addr_r), FUNC(am9516_device::addr_w));
	map(0x00140018, 0x00140019).rw(FUNC(sun3_state::scsictrl_r), FUNC(sun3_state::scsictrl_w));
	map(0x001e0000, 0x001e00ff).rw(FUNC(sun3_state::ecc_r), FUNC(sun3_state::ecc_w));
}

// type 2 device space
void sun3_state::vmetype2space_map(address_map &map)
{
}

// type 3 device space
void sun3_state::vmetype3space_map(address_map &map)
{
}

uint32_t sun3_state::enable_r()
{
	// Incorporate diag switch value.
	const uint32_t diagsw = m_diagsw->read() << 24;
	return (m_enable & ~(u32(1) << 24)) | diagsw;
}

uint32_t sun3_state::irqctrl_r()
{
	return m_irqctrl;
}

void sun3_state::irqctrl_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	//printf("sun3: %08x to interrupt control (mask %08x)\n", data, mem_mask);
	COMBINE_DATA(&m_irqctrl);

	if (data & 0x01000000)
	{
		if (data & 0x02000000)
		{
			m_maincpu->set_input_line(M68K_IRQ_1, ASSERT_LINE);
		}
		if (data & 0x04000000)
		{
			m_maincpu->set_input_line(M68K_IRQ_2, ASSERT_LINE);
		}
		if (data & 0x08000000)
		{
			m_maincpu->set_input_line(M68K_IRQ_3, ASSERT_LINE);
		}
	}
	else    // master enable clear, clear all interrupts
	{
		m_maincpu->set_input_line(M68K_IRQ_1, CLEAR_LINE);
		m_maincpu->set_input_line(M68K_IRQ_2, CLEAR_LINE);
		m_maincpu->set_input_line(M68K_IRQ_3, CLEAR_LINE);
		m_maincpu->set_input_line(M68K_IRQ_4, CLEAR_LINE);
		m_maincpu->set_input_line(M68K_IRQ_5, CLEAR_LINE);
		m_maincpu->set_input_line(M68K_IRQ_6, CLEAR_LINE);
		m_maincpu->set_input_line(M68K_IRQ_7, CLEAR_LINE);
	}
}

uint8_t sun3_state::rtc7170_r(offs_t offset)
{
	//printf("read 7170 @ %x, PC=%x\n", offset, m_maincpu->pc());

	return 0xff;
}

void sun3_state::rtc7170_w(offs_t offset, uint8_t data)
{
	//printf("%02x to 7170 @ %x\n", data, offset);

	if ((offset == 0x11) && (data == 0x1c))
	{
		if ((m_irqctrl & 0x21000000) == 0x21000000)
		{
			m_maincpu->set_input_line(M68K_IRQ_5, ASSERT_LINE);
		}
	}
}

uint16_t sun3_state::scsictrl_r()
{
	return m_scsictrl;
}

void sun3_state::scsictrl_w(uint16_t data)
{
	if (BIT(data ^ m_scsictrl, 0))
	{
		m_sbc->reset();
		m_udc->reset();
	}

	m_scsictrl = (m_scsictrl & 0xff00) | (data & 0x000f);
}

uint32_t sun3_state::ecc_r(offs_t offset)
{
	//printf("read ECC @ %x, PC=%x\n", offset, m_maincpu->pc());
	// fefc34a
	int mbram = (m_ram_size / (1024*1024));
	int beoff = (mbram / 32) * 0x10;

	//printf("offset %x MB %d beoff %x\n", offset, mbram, beoff);

	if (offset >= beoff)
	{
		m_buserr = BE_TIMEOUT;
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
	}

	uint32_t rv = m_ecc[offset & 0xf];

	if ((offset & 0xf) == 0) rv |= 0x06000000;  // indicate each ECC board is 32MB, for 128MB total

	return rv;
}

void sun3_state::ecc_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	//printf("%08x to ecc @ %x, mask %08x\n", data, offset, mem_mask);

	offset &= 0xf;
	m_ecc[offset] = data;
}

TIMER_DEVICE_CALLBACK_MEMBER(sun3_state::sun3_timer)
{
	if ((m_irqctrl & 0x81000000) == 0x81000000)
	{
		//printf("NMI tick\n");
		m_maincpu->set_input_line(M68K_IRQ_7, CLEAR_LINE);
		m_maincpu->set_input_line(M68K_IRQ_7, ASSERT_LINE);
	}
}

template <unsigned W, unsigned H>
uint32_t sun3_state::bw2_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	static const uint32_t palette[2] = { 0, 0xffffff };
	auto const vram = util::big_endian_cast<uint8_t const>(m_bw2_vram.target());

	for (int y = 0; y < H; y++)
	{
		uint32_t *scanline = &bitmap.pix(y);
		for (int x = 0; x < W/8; x++)
		{
			uint8_t const pixels = vram[(y * (W / 8)) + x];

			*scanline++ = palette[BIT(pixels, 7)];
			*scanline++ = palette[BIT(pixels, 6)];
			*scanline++ = palette[BIT(pixels, 5)];
			*scanline++ = palette[BIT(pixels, 4)];
			*scanline++ = palette[BIT(pixels, 3)];
			*scanline++ = palette[BIT(pixels, 2)];
			*scanline++ = palette[BIT(pixels, 1)];
			*scanline++ = palette[BIT(pixels, 0)];
		}
	}
	return 0;
}

uint32_t sun3_state::bw2_350_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	static const uint32_t palette[2] = { 0, 0xffffff };
	auto const vram = util::big_endian_cast<uint8_t const>(m_ram_ptr + (0x100000 >> 2));

	for (int y = 0; y < 900; y++)
	{
		uint32_t *scanline = &bitmap.pix(y);
		for (int x = 0; x < 1152/8; x++)
		{
			uint8_t const pixels = vram[(y * (1152 / 8)) + x];

			*scanline++ = palette[BIT(pixels, 7)];
			*scanline++ = palette[BIT(pixels, 6)];
			*scanline++ = palette[BIT(pixels, 5)];
			*scanline++ = palette[BIT(pixels, 4)];
			*scanline++ = palette[BIT(pixels, 3)];
			*scanline++ = palette[BIT(pixels, 2)];
			*scanline++ = palette[BIT(pixels, 1)];
			*scanline++ = palette[BIT(pixels, 0)];
		}
	}
	return 0;
}

/* Input ports */
static INPUT_PORTS_START( sun3 )
	PORT_START("diagsw")
	PORT_CONFNAME(1, 0, "Diagnostic Switch")
	PORT_CONFSETTING(0, "Normal")
	PORT_CONFSETTING(1, "Diagnostic")
INPUT_PORTS_END

void sun3_state::machine_start()
{
	m_rom_ptr = (uint32_t *)m_rom->base();
	m_ram_ptr = (uint32_t *)m_ram->pointer();
	m_idprom_ptr = (uint8_t *)m_idprom->base();
	m_ram_size = m_ram->size();
	m_ram_size_words = m_ram_size >> 2;
	std::fill(std::begin(m_parregs), std::end(m_parregs), 0);
	m_scsictrl = 0;
}

void sun3_state::machine_reset()
{
	m_enable = 0;
	m_buserr = 0;
	m_diag = 1;
	m_dvma_enable = 0;
	m_irqctrl = 0;
	m_bInBusErr = false;

	scsictrl_w(0);
}

// The base Sun 3004 CPU board
void sun3_state::sun3(machine_config &config)
{
	/* basic machine hardware */
	M68020(config, m_maincpu, 16670000);
	m_maincpu->set_addrmap(AS_PROGRAM, &sun3_state::sun3_mem);

	screen_device &bwtwo(SCREEN(config, "bwtwo", SCREEN_TYPE_RASTER));
	bwtwo.set_screen_update(NAME((&sun3_state::bw2_update<1152, 900>)));
	bwtwo.set_size(1600,1100);
	bwtwo.set_visarea(0, 1152-1, 0, 900-1);
	bwtwo.set_refresh_hz(72);

	RAM(config, m_ram).set_default_size("4M").set_extra_options("6M,8M,12M,16M,20M,24M,28M,32M").set_default_value(0x00);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	// MMU Type 0 device space
	ADDRESS_MAP_BANK(config, "type0").set_map(&sun3_state::vmetype0space_map).set_options(ENDIANNESS_BIG, 32, 32, 0x80000000);

	// MMU Type 1 device space
	ADDRESS_MAP_BANK(config, "type1").set_map(&sun3_state::vmetype1space_map).set_options(ENDIANNESS_BIG, 32, 32, 0x80000000);

	// MMU Type 2 device space
	ADDRESS_MAP_BANK(config, "type2").set_map(&sun3_state::vmetype2space_map).set_options(ENDIANNESS_BIG, 32, 32, 0x80000000);

	// MMU Type 3 device space
	ADDRESS_MAP_BANK(config, "type3").set_map(&sun3_state::vmetype3space_map).set_options(ENDIANNESS_BIG, 32, 32, 0x80000000);

	TIMER(config, "timer").configure_periodic(FUNC(sun3_state::sun3_timer), attotime::from_hz(100));

	SCC8530(config, m_scc1, 4.9152_MHz_XTAL);
	m_scc1->out_txda_callback().set(KEYBOARD_TAG, FUNC(sun_keyboard_port_device::write_txd));
	m_scc1->out_txdb_callback().set(MOUSE_TAG, FUNC(sun_mouse_port_device::write_txd));

	SUNKBD_PORT(config, KEYBOARD_TAG, default_sun_keyboard_devices, "type3hle").rxd_handler().set(m_scc1, FUNC(z80scc_device::rxa_w));

	SUNMOUSE_PORT(config, MOUSE_TAG, default_sun_mouse_devices, "hle1200").rxd_handler().set(m_scc1, FUNC(z80scc_device::rxb_w));

	SCC8530(config, m_scc2, 4.9152_MHz_XTAL);
	m_scc2->out_txda_callback().set(RS232A_TAG, FUNC(rs232_port_device::write_txd));
	m_scc2->out_txdb_callback().set(RS232B_TAG, FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232a(RS232_PORT(config, RS232A_TAG, default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_scc2, FUNC(z80scc_device::rxa_w));
	rs232a.dcd_handler().set(m_scc2, FUNC(z80scc_device::dcda_w));
	rs232a.cts_handler().set(m_scc2, FUNC(z80scc_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, RS232B_TAG, default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_scc2, FUNC(z80scc_device::rxb_w));
	rs232b.dcd_handler().set(m_scc2, FUNC(z80scc_device::dcdb_w));
	rs232b.cts_handler().set(m_scc2, FUNC(z80scc_device::ctsb_w));

	AM79C90(config, m_lance, 10'000'000); // clock is a guess

	NSCSI_BUS(config, "scsibus");
	NSCSI_CONNECTOR(config, "scsibus:0", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:1", scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsibus:2", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:3", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:4", scsi_devices, "tape");
	NSCSI_CONNECTOR(config, "scsibus:5", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:6", scsi_devices, "cdrom");
	NSCSI_CONNECTOR(config, "scsibus:7").option_set("sbc", NCR5380).machine_config([this] (device_t *device) { ncr5380(device); });

	AM9516(config, m_udc, 16_MHz_XTAL / 2);
}

// Sun 3/60
void sun3_state::sun3_60(machine_config &config)
{
	sun3(config);
	M68020(config.replace(), m_maincpu, 20000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &sun3_state::sun3_mem);

	screen_device &bwtwo(*subdevice<screen_device>("bwtwo"));
	bwtwo.set_screen_update(NAME((&sun3_state::bw2_update<1600, 1100>)));
	bwtwo.set_size(1600,1100);
	bwtwo.set_visarea(0, 1600-1, 0, 1100-1);
	bwtwo.set_refresh_hz(72);
}

// Sun 3/E
void sun3_state::sun3e(machine_config &config)
{
	sun3(config);

	M68020(config.replace(), m_maincpu, 20000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &sun3_state::sun3_mem);
}

// 3/260 and 3/280 (the Sun 3200 board)
void sun3_state::sun3200(machine_config &config)
{
	sun3(config);
	M68020(config.replace(), m_maincpu, 25000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &sun3_state::sun3_mem);

	screen_device &bwtwo(*subdevice<screen_device>("bwtwo"));
	bwtwo.set_screen_update(NAME((&sun3_state::bw2_update<1600, 1100>)));
	bwtwo.set_size(1600,1100);
	bwtwo.set_visarea(0, 1600-1, 0, 1100-1);
	bwtwo.set_refresh_hz(72);

	m_ram->set_default_size("32M").set_extra_options("64M,96M,128M");
}

void sun3_state::sun3_50(machine_config &config)
{
	/* basic machine hardware */
	M68020(config, m_maincpu, 15700000);
	m_maincpu->set_addrmap(AS_PROGRAM, &sun3_state::sun3_mem);

	AM79C90(config, m_lance, 10'000'000); // clock is a guess

	screen_device &bwtwo(SCREEN(config, "bwtwo", SCREEN_TYPE_RASTER));
	bwtwo.set_screen_update(FUNC(sun3_state::bw2_350_update));
	bwtwo.set_size(1600,1100);
	bwtwo.set_visarea(0, 1152-1, 0, 900-1);
	bwtwo.set_refresh_hz(72);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	TIMER(config, "timer").configure_periodic(FUNC(sun3_state::sun3_timer), attotime::from_hz(100));

	RAM(config, m_ram).set_default_size("4M").set_default_value(0x00);

	// MMU Type 0 device space
	ADDRESS_MAP_BANK(config, "type0").set_map(&sun3_state::vmetype0space_novram_map).set_options(ENDIANNESS_BIG, 32, 32, 0x80000000);

	// MMU Type 1 device space
	ADDRESS_MAP_BANK(config, "type1").set_map(&sun3_state::vmetype1space_map).set_options(ENDIANNESS_BIG, 32, 32, 0x80000000);

	// MMU Type 2 device space
	ADDRESS_MAP_BANK(config, "type2").set_map(&sun3_state::vmetype2space_map).set_options(ENDIANNESS_BIG, 32, 32, 0x80000000);

	// MMU Type 3 device space
	ADDRESS_MAP_BANK(config, "type3").set_map(&sun3_state::vmetype3space_map).set_options(ENDIANNESS_BIG, 32, 32, 0x80000000);

	SCC8530(config, m_scc1, 4.9152_MHz_XTAL);
	m_scc1->out_txda_callback().set(KEYBOARD_TAG, FUNC(sun_keyboard_port_device::write_txd));
	m_scc1->out_txdb_callback().set(MOUSE_TAG, FUNC(sun_mouse_port_device::write_txd));

	SUNKBD_PORT(config, KEYBOARD_TAG, default_sun_keyboard_devices, "type3hle").rxd_handler().set(m_scc1, FUNC(z80scc_device::rxa_w));

	SUNMOUSE_PORT(config, MOUSE_TAG, default_sun_mouse_devices, "hle1200").rxd_handler().set(m_scc1, FUNC(z80scc_device::rxb_w));

	SCC8530(config, m_scc2, 4.9152_MHz_XTAL);
	m_scc2->out_txda_callback().set(RS232A_TAG, FUNC(rs232_port_device::write_txd));
	m_scc2->out_txdb_callback().set(RS232B_TAG, FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232a(RS232_PORT(config, RS232A_TAG, default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_scc2, FUNC(z80scc_device::rxa_w));
	rs232a.dcd_handler().set(m_scc2, FUNC(z80scc_device::dcda_w));
	rs232a.cts_handler().set(m_scc2, FUNC(z80scc_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, RS232B_TAG, default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_scc2, FUNC(z80scc_device::rxb_w));
	rs232b.dcd_handler().set(m_scc2, FUNC(z80scc_device::dcdb_w));
	rs232b.cts_handler().set(m_scc2, FUNC(z80scc_device::ctsb_w));

	NSCSI_BUS(config, "scsibus");
	NSCSI_CONNECTOR(config, "scsibus:0", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:1", scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsibus:2", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:3", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:4", scsi_devices, "tape");
	NSCSI_CONNECTOR(config, "scsibus:5", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:6", scsi_devices, "cdrom");
	NSCSI_CONNECTOR(config, "scsibus:7").option_set("sbc", NCR5380).machine_config([this] (device_t *device) { ncr5380(device); });

	AM9516(config, m_udc, 16_MHz_XTAL / 2);
}

/* ROM definition */

ROM_START( sun3_50 )
	ROM_REGION32_BE( 0x10000, "user1", ROMREGION_ERASEFF )
/*
Sun 3/50 V1.2 Bootprom
Sun 3/50 V1.4 Bootprom
Sun 3/50 V1.6 Bootprom
Sun 3/50 V1.8 Bootprom (Req. to load SunOS QIC-24 1/4" tapes)
Sun 3/50 V2.0 Bootprom
Sun 3/50 V2.1 Bootprom
Sun 3/50 V2.3 Bootprom
Sun 3/50 V2.5 Bootprom (Req. to load SunOS QIC-24 1/4" tapes from a Sun-2 Shoebox)
Sun 3/50 V2.6 Bootprom
Sun 3/50 V2.7 Bootprom
Sun 3/50 V2.8 Bootprom
*/
	ROM_SYSTEM_BIOS(0, "rev28", "Rev 2.8")
	ROMX_LOAD( "sun3_50_v2.8", 0x0000, 0x10000, CRC(1ca6b0e8) SHA1(5773ac1c46399501d29d1758aa342862b03ec472), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "rev27", "Rev 2.7")
	ROMX_LOAD( "sun3_50_v2.7", 0x0000, 0x10000, CRC(7c4a9e20) SHA1(6dcd4883a170538050fd0e1f151fae413ec9ea52), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "rev26", "Rev 2.6")
	ROMX_LOAD( "sun3_50_v2.6", 0x0000, 0x10000, CRC(08abbb3b) SHA1(6bfb8d5c97d801cd7bb7d564de0e68a48fb807c4), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "rev23", "Rev 2.3")
	ROMX_LOAD( "sun3_50_v2.3", 0x0000, 0x10000, CRC(163500b3) SHA1(437c8d539e12d442ca6877566dbbe165d577fcab), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "rev16", "Rev 1.6")
	ROMX_LOAD( "sun3_50_v1.6", 0x0000, 0x10000, CRC(8be20826) SHA1(2a4d73fcb7fe0f0c83eb0f4c91d957b7bf88b7ed), ROM_BIOS(4))

	ROM_REGION( 0x20, "idprom", ROMREGION_ERASEFF)
	ROM_LOAD( "sun3-50-idprom.bin", 0x000000, 0x000020, CRC(80610dbe) SHA1(0f37e31ed209b8905c5dc7c2663fa01a9b9baaba) )
ROM_END

ROM_START( sun3_60 )
	ROM_REGION32_BE( 0x10000, "user1", ROMREGION_ERASEFF )
/*
Sun 3/60 V1.0 Bootprom
Sun 3/60 V1.3 Bootprom
Sun 3/60 V1.5 Bootprom
Sun 3/60 V1.6 Bootprom (Req. to load SunOS QIC-24 1/4" tapes
Sun 3/60 V1.9 Bootprom
Sun 3/60 V2.8.3 Bootprom
Sun 3/60 V3.0.1 Bootprom
*/
	ROM_SYSTEM_BIOS(0, "rev301", "Rev 3.0.1")
	ROMX_LOAD( "sun_3.60v3.0.1", 0x0000, 0x10000, CRC(e55dc1d8) SHA1(6e48414ce2139282e69f57612b20f7d5c475e74c), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "rev283", "Rev 2.8.3")
	ROMX_LOAD( "sun_3.60v2.8.3", 0x0000, 0x10000, CRC(de4ec54d) SHA1(e621a9c1a2a7df4975b12fa3a0d7f106383736ef), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "rev19", "Rev 1.9")
	ROMX_LOAD( "sun_3.60v1.9",   0x0000, 0x10000, CRC(32b6d3a9) SHA1(307756ba5698611d51059881057f8086956ce895), ROM_BIOS(2))

	ROM_REGION( 0x20, "idprom", ROMREGION_ERASEFF)
	ROM_LOAD( "sun3-60-idprom.bin", 0x000000, 0x000020, CRC(117e766a) SHA1(f01547be0156bd4e06bbdee4c342d1b38c7646ae) )
ROM_END

ROM_START( sun3_110 )
	ROM_REGION32_BE( 0x10000, "user1", ROMREGION_ERASEFF )
/*
Sun 3/110 V1.8 Bootprom
Sun 3/110 V2.1 Bootprom
Sun 3/110 V2.3 Bootprom
Sun 3/110 V2.6 Bootprom
Sun 3/110 V2.7 Bootprom
Sun 3/110 V2.8 Bootprom
Sun 3/110 V3.0 Bootprom
*/
	ROM_SYSTEM_BIOS(0, "rev30", "Rev 3.0")
	ROMX_LOAD( "sun3_110_v3.0", 0x0000, 0x10000, CRC(a193b26b) SHA1(0f54212ee3a5709f70e921069cca1ddb8c143b1b), ROM_BIOS(0))

	ROM_REGION( 0x20, "idprom", ROMREGION_ERASEFF)
	ROM_LOAD( "sun3-110-idprom.bin", 0x000000, 0x000020, CRC(d6cd934a) SHA1(b0913708fe733250ef5c1289c10146dcef6d1a67) )
ROM_END

ROM_START( sun3_150 )
	ROM_REGION32_BE( 0x10000, "user1", ROMREGION_ERASEFF )
/*
Sun 3/1[4,5,6,8]0 V1.3 Bootprom
Sun 3/1[4,5,6,8]0 V1.4 Bootprom
Sun 3/1[4,5,6,8]0 V1.5 Bootprom
Sun 3/1[4,5,6,8]0 V1.8 Bootprom (Req. to load SunOS QIC-24 1/4" tapes)
Sun 3/1[4,5,6,8]0 V2.1 Bootprom
Sun 3/1[4,5,6,8]0 V2.1 Bootprom with Capricot Rimfire 3200/3400 support (b rf(0,0,0) works)
Sun 3/1[4,5,6,8]0 V2.3 Bootprom
Sun 3/1[4,5,6,8]0 V2.6 Bootprom (Req. to load SunOS QIC-24 1/4" tapes from a Sun-2 Shoebox and for Xylogics 7053)
Sun 3/1[4,5,6,8]0 V2.7 Bootprom
Sun 3/1[4,5,6,8]0 V2.8 Bootprom
Sun 3/1[4,5,6,8]0 V2.8.4 Bootprom
Sun 3/1[4,5,6,8]0 V3.0 Bootprom
*/
	ROM_SYSTEM_BIOS(0, "rev30", "Rev 3.0")
	ROMX_LOAD( "sun3_160_v3.0",   0x0000, 0x10000, CRC(fee6e4d6) SHA1(440d532e1848298dba0f043de710bb0b001fb675), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "rev284", "Rev 2.8.4")
	ROMX_LOAD( "sun3_160_v2.8.4", 0x0000, 0x10000, CRC(3befd013) SHA1(f642bb42200b794e6e32e2fe6c87d5c269c8656d), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "rev23", "Rev 2.3")
	ROMX_LOAD( "sun3_160_v2.3",   0x0000, 0x10000, CRC(09585745) SHA1(1de1725dd9e27f5a910989bbb5b51acfbdc1d70b), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "rev21rf", "Rev 2.1 RF")
	ROMX_LOAD( "sun3_160_v2.1_rf",   0x0000, 0x10000, CRC(5c7e9271) SHA1(5e4dbb50859a21f9e1d3e4a06c42494d13a9a8eb), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "rev15", "Rev 1.5")
	ROMX_LOAD( "sun3_160_v1.5",   0x0000, 0x10000, CRC(06daee37) SHA1(b9873cd48d78ad8e0c85d69966fc20c21cfc99aa), ROM_BIOS(4))


	ROM_REGION( 0x20, "idprom", ROMREGION_ERASEFF)
	ROM_LOAD( "sun3-150-idprom.bin", 0x000000, 0x000020, CRC(58956a93) SHA1(7334936dc945e05d63a94a33340e963a371672c9) )
ROM_END

ROM_START( sun3_260 )
	ROM_REGION32_BE( 0x10000, "user1", ROMREGION_ERASEFF )
/*
Sun 3/260/280 V1.8 Bootprom
Sun 3/260/280 V2.1 Bootprom ( 2x^G cause system to beep 'till reset)
Sun 3/260/280 V2.3 Bootprom
Sun 3/260/280 V2.6 Bootprom (Req. for Xylogics 7053)
Sun 3/260/280 V2.7 Bootprom
Sun 3/260/280 V2.8 Bootprom
Sun 3/260/280 V2.8.4 Bootprom
Sun 3/260/280 V3.0 Bootprom
*/
	ROM_SYSTEM_BIOS(0, "rev30", "Rev 3.0")
	ROMX_LOAD( "sun3_260_v3.0", 0x0000, 0x10000, CRC(f43ed1d3) SHA1(204880436bd087ede136f853610403d75e60bd75), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "rev27", "Rev 2.7")
	ROMX_LOAD( "sun3_260_v2.7", 0x0000, 0x10000, CRC(099fcaab) SHA1(4a5233c778676f48103bdd8bab03b4264686b4aa), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "rev26", "Rev 2.6")
	ROMX_LOAD( "sun3_260_v2.6", 0x0000, 0x10000, CRC(e8b17951) SHA1(e1fdef42670a349d99b0eca9c50c8566b8bb7c56), ROM_BIOS(2))

	ROM_REGION( 0x20, "idprom", ROMREGION_ERASEFF)
	ROM_LOAD( "sun3-260-idprom.bin", 0x000000, 0x000020, CRC(d51794f3) SHA1(17930c773b6fe9a32819094ffaf69e5453d1ea4d) )
ROM_END

ROM_START( sun3_e )
	ROM_REGION32_BE( 0x10000, "user1", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "rev28", "Rev 3.2")
	ROMX_LOAD( "sun3_e.32", 0x0000, 0x10000, CRC(acedde7e) SHA1(1ab6ec28f4365a613a5e326c34cb37585c3f0ecc), ROM_BIOS(0))

	ROM_REGION( 0x20, "idprom", ROMREGION_ERASEFF)
	ROM_LOAD( "sun3-e-idprom.bin", 0x000000, 0x000020, CRC(d1a92116) SHA1(4836f3188f2c3dd5ba49ab66e0b55caa6b1b1791) )
ROM_END

} // Anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY             FULLNAME                    FLAGS
COMP( 198?, sun3_50,  0,      0,      sun3_50, sun3,  sun3_state, empty_init, "Sun Microsystems", "Sun 3/50",                 MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // Model 25
COMP( 1988, sun3_60,  0,      0,      sun3_60, sun3,  sun3_state, empty_init, "Sun Microsystems", "Sun 3/60",                 MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // Ferrari
COMP( 198?, sun3_110, 0,      0,      sun3,    sun3,  sun3_state, empty_init, "Sun Microsystems", "Sun 3/110",                MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // Prism
COMP( 1985, sun3_150, 0,      0,      sun3,    sun3,  sun3_state, empty_init, "Sun Microsystems", "Sun 3/75/140/150/160/180", MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // AKA Carrera
COMP( 198?, sun3_260, 0,      0,      sun3200, sun3,  sun3_state, empty_init, "Sun Microsystems", "Sun 3/260/280",            MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // Prism
COMP( 198?, sun3_e,   0,      0,      sun3e,   sun3,  sun3_state, empty_init, "Sun Microsystems", "Sun 3/E",                  MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // Polaris



sun3x.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, R. Belmont
/***************************************************************************

  sun3.c: preliminary driver for Sun 3x models.

  status: 3/80 POSTs, 3/460 needs its unique RTC chip (also used by non-3x Sun 3s).

  TODO:
    - Improve interrupt controller emulation.
    - Figure out how the IOMMU works.
    - Sun custom MMU for original Sun 3 models.
    - AM7990 LANCE chip support for everyone.
    - Figure out how the parallel printer port maps to Centronics and make it so.
    - Much more...


    Sun-3x Models
    ------------

    3/80
        Processor(s):   68030 @ 20MHz, 68882 @ 20MHz, 68030 on-chip
                        MMU, 3 MIPS, 0.16 MFLOPS
        CPU:            501-1401/1650
        Chassis type:   square pizza box
        Bus:            P4 connector (not same as P4 on 3/60)
        Memory:         16M or 40M physical, 4G virtual, 100ns cycle
        Notes:          Similar packaging to SparcStation 1. Parallel
                        port, SCSI port, AUI Ethernet, 1.44M 3.5" floppy
                        (720K on early units?). No onboard framebuffer.
                        Code-named "Hydra". Type-4 keyboard and Sun-4
                        mouse, plugged together and into the machine
                        with a small DIN plug. 1M x 9 30-pin 100ns
                        SIMMs. Boot ROM versions 3.0.2 and later allow
                        using 4M SIMMs in some slots for up to 40M (see
                        Misc Q&A #15).

    3/460
        Processor(s):   68030 @ 33 MHz, 68882, 68030 on-chip MMU,
                        7 MIPS, 0.6 MFLOPS
        CPU:            501-1299/1550
        Bus:            VME
        Memory:         128M physical with ECC, 4G/process virtual,
                        64K cache, 80ns cycle
        Notes:          A 3/260 upgraded with a 3/4xx CPU board. Uses
                        original 3/2xx memory boards.

    3/470
        Processor(s):   68030 @ 33 MHz, 68882, 68030 on-chip MMU,
                        7 MIPS, 0.6 MFLOPS
        CPU:            501-1299/1550
        Chassis type:   deskside
        Bus:            VME
        Memory:         128M physical with ECC, 4G/process virtual,
                        64K cache, 80ns cycle
        Notes:          Rare. Code-named "Pegasus". 8M standard, uses
                        same memory boards as 3/2xx.

    3/480
        Processor(s):   68030 @ 33 MHz, 68882, 68030 on-chip MMU,
                        7 MIPS, 0.6 MFLOPS
        CPU:            501-1299/1550
        Chassis type:   rackmount
        Bus:            VME
        Memory:         128M physical with ECC, 4G/process virtual,
                        64K cache, 80ns cycle
        Notes:          Rare. Code-named "Pegasus". 8M standard, uses
                        same memory boards as 3/2xx.

    Sun3X notes from NetBSD and Linux:

    RAM_END    0x40000000
    P4DAC      0x50200000
    VIDEO_P4ID 0x50300000
    BW2_ADDR   0x50400000
    ENA_PLANE  0x50600000
    FPA_ADDR   0x5c000000
    IOMMU      0x60000000
    ENABLEREG  0x61000000
    BUSERRREG  0x61000400
    DIAGREG    0x61000800
    IDPROM1    0x61000c00 (3/470)
    MEMREG     0x61001000
    INTERREG   0x61001400
    SCC1       0x62000000 (keyboard/mouse)
    SCC2       0x62002000 (serial console)
    EEPROM     0x64000000
    IDPROM2    0x640007d8 (3/80)
    CLOCK2     0x640007f8 (3/80 Mostek 48T02)
    CLOCK1     0x64002000 (3/470 Intersil 7170)
    INTELETH   0x65000000
    LANCEETH   0x65002000
    EMULEXSCSI 0x66000000 (3/80 5394)
    EMULLEXDMA 0x66001000 (3/80)
    PCACHETAG  0x68000000
    ECCPARREG  0x6a1e0000
    IOCTAGS    0x6c000000
    IOCFLUSH   0x6d000000
    FDC        0x6e000000 (3/80 Intel 82077)
    FDC_CNTRL  0x6e000400
    FDC_VEC    0x6e000800
    PRINTER    0x6f00003c (3/80)

    The Sun3x System Enable Register controls the function of a few
    on-board devices and general system operation.  It is cleared when
    the system is reset.

    15                                                               0
    +---+---+---+---+---+---+---+---+---+---+---+---+---+---.---.---+
    |BT |FPP|DMA| 0 |VID|RES|FPA|DIA| 0 |CCH|IOC|LBK|DCH|  UNUSED   |
    +---+---+---+---+---+---+---+---+---+---+---+---+---+---.---.---+

    Where: DCH = debug mode for system cache
    LBK = VME loopback
    IOC = I/O cache enable
    CCH = system cache enable
    DIA = diagnostic switch
    FPA = enable floating-point accelerator
    RES = 0 for hi-res, 1 for low res
    VID = enable video display
    DMA = enable system DVMA
    FPP = enable 68881/2 FPU
    BT  = 0 for boot state, 1 for normal state

    bad '030 MMU mapping: L fef82000 -> P 00000000

****************************************************************************/

#include "emu.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"
#include "bus/nscsi/tape.h"
#include "cpu/m68000/m68030.h"
#include "imagedev/floppy.h"
#include "machine/icm7170.h"
#include "machine/ncr53c90.h"
#include "machine/timekpr.h"
#include "machine/timer.h"
#include "machine/upd765.h"
#include "machine/z80scc.h"

#include "bus/rs232/rs232.h"
#include "bus/sunkbd/sunkbd.h"
#include "bus/sunmouse/sunmouse.h"

#include "screen.h"

#include "formats/flopimg.h"


namespace {

#define TIMEKEEPER_TAG  "timekpr"
#define SCC1_TAG        "scc1"
#define SCC2_TAG        "scc2"
#define FDC_TAG         "fdc"
#define FLOPPY_CONN_TAG "fdc:0"
#define RS232A_TAG      "rs232a"
#define RS232B_TAG      "rs232b"
#define KEYBOARD_TAG    "keyboard"
#define MOUSE_TAG       "mouseport"

class sun3x_state : public driver_device
{
public:
	sun3x_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_scc1(*this, SCC1_TAG),
		m_scc2(*this, SCC2_TAG),
		m_esp(*this, "scsibus:7:esp"),
		m_fdc(*this, FDC_TAG),
		m_floppy_connector(*this, FLOPPY_CONN_TAG),
		m_p_ram(*this, "p_ram"),
		m_bw2_vram(*this, "bw2_vram")
	{ }

	void sun3_80(machine_config &config);
	void sun3_460(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	required_device<z80scc_device> m_scc1;
	required_device<z80scc_device> m_scc2;
	optional_device<ncr53c90_device> m_esp;
	optional_device<n82077aa_device> m_fdc;
	optional_device<floppy_connector> m_floppy_connector;
	virtual void machine_reset() override ATTR_COLD;

	required_shared_ptr<uint32_t> m_p_ram;
	optional_shared_ptr<uint32_t> m_bw2_vram;

	uint32_t enable_r();
	void enable_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t buserr_r();
	void buserr_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t diag_r();
	void diag_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t printer_r();
	void printer_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t iommu_r(offs_t offset);
	void iommu_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t irqctrl_r();
	void irqctrl_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t memreg_r();
	void memreg_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t memrerraddr_r();
	void memrerraddr_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t fdc_control_r();
	void fdc_control_w(uint32_t data);
	uint32_t cause_buserr_r();
	void cause_buserr_w(uint32_t data);
	void ramwrite_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t fpa_r();
	uint32_t p4id_r();

	TIMER_DEVICE_CALLBACK_MEMBER(sun380_timer);

	uint32_t bw2_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void sun3_460_mem(address_map &map) ATTR_COLD;
	void sun3_80_mem(address_map &map) ATTR_COLD;

	uint32_t m_enable = 0, m_buserr = 0, m_diag = 0, m_printer = 0, m_irqctrl = 0, m_memreg = 0, m_memerraddr = 0;
	uint32_t m_iommu[0x800]{};
	bool m_bInBusErr = false;
};

void sun3x_state::sun3_80_mem(address_map &map)
{
	map(0x00000000, 0x03ffffff).ram().share("p_ram").w(FUNC(sun3x_state::ramwrite_w));
	map(0x40000000, 0x40000003).rw(FUNC(sun3x_state::cause_buserr_r), FUNC(sun3x_state::cause_buserr_w));
	map(0x50300000, 0x50300003).r(FUNC(sun3x_state::p4id_r));
	map(0x50400000, 0x504fffff).ram().share(m_bw2_vram);
	map(0x60000000, 0x60001fff).rw(FUNC(sun3x_state::iommu_r), FUNC(sun3x_state::iommu_w));
	map(0x61000000, 0x61000003).rw(FUNC(sun3x_state::enable_r), FUNC(sun3x_state::enable_w));
	map(0x61000400, 0x61000403).rw(FUNC(sun3x_state::buserr_r), FUNC(sun3x_state::buserr_w));
	map(0x61000800, 0x61000803).rw(FUNC(sun3x_state::diag_r), FUNC(sun3x_state::diag_w));
	map(0x61001000, 0x61001003).rw(FUNC(sun3x_state::memreg_r), FUNC(sun3x_state::memreg_w));
	map(0x61001004, 0x61001007).rw(FUNC(sun3x_state::memrerraddr_r), FUNC(sun3x_state::memrerraddr_w));
	map(0x61001400, 0x61001403).rw(FUNC(sun3x_state::irqctrl_r), FUNC(sun3x_state::irqctrl_w));
	map(0x62000000, 0x6200000f).rw(m_scc1, FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w)).umask32(0xff00ff00);
	map(0x62002000, 0x6200200f).rw(m_scc2, FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w)).umask32(0xff00ff00);
	map(0x63000000, 0x6301ffff).rom().region("user1", 0);
	map(0x64000000, 0x640007ff).rw(TIMEKEEPER_TAG, FUNC(timekeeper_device::read), FUNC(timekeeper_device::write));
	map(0x66000000, 0x6600003f).m(m_esp, FUNC(ncr53c90_device::map)).umask32(0xff000000);
	map(0x6e000000, 0x6e000007).m(m_fdc, FUNC(n82077aa_device::map));
	map(0x6e000400, 0x6e000403).rw(FUNC(sun3x_state::fdc_control_r), FUNC(sun3x_state::fdc_control_w));
	map(0x6f00003c, 0x6f00003f).rw(FUNC(sun3x_state::printer_r), FUNC(sun3x_state::printer_w));
	map(0xfefe0000, 0xfefeffff).rom().region("user1", 0);
}

void sun3x_state::sun3_460_mem(address_map &map)
{
	map(0x00000000, 0x03ffffff).ram().share("p_ram").w(FUNC(sun3x_state::ramwrite_w));
	map(0x09000000, 0x09000003).rw(FUNC(sun3x_state::cause_buserr_r), FUNC(sun3x_state::cause_buserr_w));
	map(0x50300000, 0x50300003).r(FUNC(sun3x_state::p4id_r));
	map(0x50400000, 0x504fffff).ram().share(m_bw2_vram);
	map(0x5c000f14, 0x5c000f17).r(FUNC(sun3x_state::fpa_r));
	map(0x60000000, 0x60001fff).rw(FUNC(sun3x_state::iommu_r), FUNC(sun3x_state::iommu_w));
	map(0x61000000, 0x61000003).rw(FUNC(sun3x_state::enable_r), FUNC(sun3x_state::enable_w));
	map(0x61000400, 0x61000403).rw(FUNC(sun3x_state::buserr_r), FUNC(sun3x_state::buserr_w));
	map(0x61000800, 0x61000803).rw(FUNC(sun3x_state::diag_r), FUNC(sun3x_state::diag_w));
	map(0x61001000, 0x61001003).rw(FUNC(sun3x_state::memreg_r), FUNC(sun3x_state::memreg_w));
	map(0x61001004, 0x61001007).rw(FUNC(sun3x_state::memrerraddr_r), FUNC(sun3x_state::memrerraddr_w));
	map(0x61001400, 0x61001403).rw(FUNC(sun3x_state::irqctrl_r), FUNC(sun3x_state::irqctrl_w));
	map(0x62000000, 0x6200000f).rw(m_scc1, FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w)).umask32(0xff00ff00);
	map(0x62002000, 0x6200200f).rw(m_scc2, FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w)).umask32(0xff00ff00);
	map(0x63000000, 0x6301ffff).rom().region("user1", 0);
	map(0x64002000, 0x64002011).rw("rtc", FUNC(icm7170_device::read), FUNC(icm7170_device::write));
	map(0x6f00003c, 0x6f00003f).rw(FUNC(sun3x_state::printer_r), FUNC(sun3x_state::printer_w));
	map(0xfefe0000, 0xfefeffff).rom().region("user1", 0);
}

uint32_t sun3x_state::p4id_r()
{
	return (1<<24); // 0 = hires bw2 1600x1280, 1 = bw2 1152x900, 0x45 is "Ibis" color, blt 0x68 is "Lego" color
}

void  sun3x_state::fdc_control_w(uint32_t data)
{
	logerror("FDC write %02x (%08x)\n", data >> 24, m_maincpu->pc());
}

uint32_t sun3x_state::fdc_control_r()
{
	// Type of floppy present
	// 0 = no floppy in drive
	// 1 = ed
	// 2 = hd
	// 3 = dd

	if (m_fdc)
	{
		floppy_image_device *fdev = m_floppy_connector->get_device();
		if(fdev->exists()) {
			uint32_t variant = fdev->get_variant();
			switch(variant) {
			case floppy_image::SSSD:
			case floppy_image::SSDD:
			case floppy_image::DSDD:
				return 3 << 24;

			case floppy_image::DSHD:
				return 2 << 24;

			case floppy_image::DSED:
				return 1 << 24;
			}
		}
	}

	return 0 << 24;
}

void sun3x_state::ramwrite_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	uint32_t *pRAM = (uint32_t *)m_p_ram.target();

	if (((m_memreg & 0xf0000000) == 0x70000000) &&
		(m_irqctrl & 0x01000000) &&
		!(m_bInBusErr))
	{
		m_memerraddr = offset<<2;

		// low 4 bits of memreg are the byte lane(s) involved, negative logic
		m_memreg |= 0x0f;
		switch (mem_mask)
		{
			case 0xff000000:
				m_memreg &= ~0x08;
				break;

			case 0x00ff0000:
				m_memerraddr += 1;
				m_memreg &= ~0x04;
				break;

			case 0x0000ff00:
				m_memerraddr += 2;
				m_memreg &= ~0x02;
				break;

			case 0x000000ff:
				m_memerraddr += 3;
				m_memreg &= ~0x01;
				break;

			case 0x0000ffff:
				m_memerraddr += 2;
				m_memreg &= ~0x03;
				break;

			case 0xffff0000:
				m_memreg &= ~0x0c;
				break;

			case 0xffffffff:    // no address adjust, show all 4 lanes as problematic
				break;
		}

		m_bInBusErr = true; // prevent recursion
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
	}

	COMBINE_DATA(&pRAM[offset]);
}

uint32_t sun3x_state::enable_r()
{
	return m_enable;
}

void sun3x_state::enable_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
//  printf("sun3x: %08x to enable (mask %08x)\n", data, mem_mask);
	COMBINE_DATA(&m_enable);
}

uint32_t sun3x_state::buserr_r()
{
	uint32_t rv = m_buserr;
	m_buserr = 0;
	return rv;
}

void sun3x_state::buserr_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
//  printf("sun3x: %08x to buserr (mask %08x)\n", data, mem_mask);
	COMBINE_DATA(&m_buserr);
}

uint32_t sun3x_state::diag_r()
{
	return m_diag;
}

void sun3x_state::diag_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
//  printf("sun3x: %08x to diag (mask %08x)\n", data, mem_mask);
	COMBINE_DATA(&m_diag);
}

uint32_t sun3x_state::printer_r()
{
	return m_printer;
}

void sun3x_state::printer_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
//  printf("sun3x: %08x to printer (mask %08x)\n", data, mem_mask);
	COMBINE_DATA(&m_printer);
}

uint32_t sun3x_state::irqctrl_r()
{
	return m_irqctrl;
}

void sun3x_state::irqctrl_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
//  printf("sun3x: %08x to interrupt control (mask %08x)\n", data, mem_mask);
	COMBINE_DATA(&m_irqctrl);

	if (data & 0x01000000)
	{
		if (data & 0x02000000)
		{
			m_maincpu->set_input_line(M68K_IRQ_1, ASSERT_LINE);
		}
		if (data & 0x04000000)
		{
			m_maincpu->set_input_line(M68K_IRQ_2, ASSERT_LINE);
		}
		if (data & 0x08000000)
		{
			m_maincpu->set_input_line(M68K_IRQ_3, ASSERT_LINE);
		}
		if (!(data & 0x80000000))
		{
			m_maincpu->set_input_line(M68K_IRQ_7, CLEAR_LINE);
		}
	}
	else    // master enable clear, clear all interrupts
	{
		m_maincpu->set_input_line(M68K_IRQ_1, CLEAR_LINE);
		m_maincpu->set_input_line(M68K_IRQ_2, CLEAR_LINE);
		m_maincpu->set_input_line(M68K_IRQ_3, CLEAR_LINE);
		m_maincpu->set_input_line(M68K_IRQ_4, CLEAR_LINE);
		m_maincpu->set_input_line(M68K_IRQ_5, CLEAR_LINE);
		m_maincpu->set_input_line(M68K_IRQ_6, CLEAR_LINE);
		m_maincpu->set_input_line(M68K_IRQ_7, CLEAR_LINE);
	}
}

uint32_t sun3x_state::memreg_r()
{
	return m_memreg;
}

void sun3x_state::memreg_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
//  printf("sun3x: %08x to memory control (mask %08x)\n", data, mem_mask);
	COMBINE_DATA(&m_memreg);
}

uint32_t sun3x_state::memrerraddr_r()
{
	m_bInBusErr = false;
	m_maincpu->set_input_line(M68K_IRQ_7, CLEAR_LINE);
	return m_memerraddr;
}

void sun3x_state::memrerraddr_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
//  printf("sun3x: %08x to memory error address (mask %08x)\n", data, mem_mask);
	COMBINE_DATA(&m_memerraddr);
}

uint32_t sun3x_state::iommu_r(offs_t offset)
{
	return m_iommu[offset];
}

// IOMMU entry defs:
// address mask:  0x03ffe000
// cache inhibit: 0x00000040
// full block:    0x00000020
// modified:      0x00000010
// used:          0x00000008
// write prot:    0x00000004
// bad:           0x00000002
// valid:         0x00000001
void sun3x_state::iommu_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	COMBINE_DATA(&m_iommu[offset]);
}

uint32_t sun3x_state::fpa_r()
{
	m_buserr |= 0x04000000;
	m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
	m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
	return 0xffffffff;
}

uint32_t sun3x_state::cause_buserr_r()
{
	m_buserr |= 0x20000000;
	m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
	m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
	return 0xffffffff;
}

void sun3x_state::cause_buserr_w(uint32_t data)
{
	m_buserr |= 0x20000000;
	m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
	m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
}

TIMER_DEVICE_CALLBACK_MEMBER(sun3x_state::sun380_timer)
{
	if ((m_irqctrl & 0x81000000) == 0x81000000)
	{
		m_maincpu->set_input_line(M68K_IRQ_7, CLEAR_LINE);
		m_maincpu->set_input_line(M68K_IRQ_7, ASSERT_LINE);
	}
}

uint32_t sun3x_state::bw2_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	static const uint32_t palette[2] = { 0, 0xffffff };
	auto const vram = util::big_endian_cast<uint8_t const>(m_bw2_vram.target());

	for (int y = 0; y < 900; y++)
	{
		uint32_t *scanline = &bitmap.pix(y);
		for (int x = 0; x < 1152/8; x++)
		{
			uint8_t const pixels = vram[(y * (1152 / 8)) + x];

			*scanline++ = palette[BIT(pixels, 7)];
			*scanline++ = palette[BIT(pixels, 6)];
			*scanline++ = palette[BIT(pixels, 5)];
			*scanline++ = palette[BIT(pixels, 4)];
			*scanline++ = palette[BIT(pixels, 3)];
			*scanline++ = palette[BIT(pixels, 2)];
			*scanline++ = palette[BIT(pixels, 1)];
			*scanline++ = palette[BIT(pixels, 0)];
		}
	}

	return 0;
}

/* Input ports */
static INPUT_PORTS_START( sun3x )
INPUT_PORTS_END


void sun3x_state::machine_reset()
{
	uint8_t* user1 = memregion("user1")->base();

	memcpy((uint8_t*)m_p_ram.target(),user1,0x10000);

	memset(m_iommu, 0, sizeof(m_iommu));

	m_enable = 0;
	m_buserr = 0;
	m_diag = 0;
	m_printer = 0;
	m_irqctrl = 0;
	m_memreg = 0;
	m_memerraddr = 0;
	m_bInBusErr = false;
}

static void sun_floppies(device_slot_interface &device)
{
	device.option_add("35hd", FLOPPY_35_HD);
}

static void sun_cdrom(device_t *device)
{
	downcast<nscsi_cdrom_device &>(*device).set_block_size(512);
}

static void scsi_devices(device_slot_interface &device)
{
	device.option_add("cdrom", NSCSI_CDROM);
	device.option_add("harddisk", NSCSI_HARDDISK);
	device.option_add("tape", NSCSI_TAPE);
	device.set_option_machine_config("cdrom", sun_cdrom);
}

void sun3x_state::sun3_80(machine_config &config)
{
	/* basic machine hardware */
	M68030(config, m_maincpu, 20000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &sun3x_state::sun3_80_mem);

	M48T02(config, TIMEKEEPER_TAG, 0);

	SCC8530(config, m_scc1, 4.9152_MHz_XTAL);
	m_scc1->out_txda_callback().set(KEYBOARD_TAG, FUNC(sun_keyboard_port_device::write_txd));
	m_scc1->out_txdb_callback().set(MOUSE_TAG, FUNC(sun_mouse_port_device::write_txd));

	SUNKBD_PORT(config, KEYBOARD_TAG, default_sun_keyboard_devices, "type3hle").rxd_handler().set(m_scc1, FUNC(z80scc_device::rxa_w));
	SUNMOUSE_PORT(config, MOUSE_TAG, default_sun_mouse_devices, "hle1200").rxd_handler().set(m_scc1, FUNC(z80scc_device::rxb_w));

	SCC8530(config, m_scc2, 4.9152_MHz_XTAL);
	m_scc2->out_txda_callback().set(RS232A_TAG, FUNC(rs232_port_device::write_txd));
	m_scc2->out_txdb_callback().set(RS232B_TAG, FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232a(RS232_PORT(config, RS232A_TAG, default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_scc2, FUNC(z80scc_device::rxa_w));
	rs232a.dcd_handler().set(m_scc2, FUNC(z80scc_device::dcda_w));
	rs232a.cts_handler().set(m_scc2, FUNC(z80scc_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, RS232B_TAG, default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_scc2, FUNC(z80scc_device::rxb_w));
	rs232b.dcd_handler().set(m_scc2, FUNC(z80scc_device::dcdb_w));
	rs232b.cts_handler().set(m_scc2, FUNC(z80scc_device::ctsb_w));

	NSCSI_BUS(config, "scsibus");
	NSCSI_CONNECTOR(config, "scsibus:0", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:1", scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsibus:2", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:3", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:4", scsi_devices, "tape");
	NSCSI_CONNECTOR(config, "scsibus:5", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:6", scsi_devices, "cdrom");
	NSCSI_CONNECTOR(config, "scsibus:7").option_set("esp", NCR53C90).clock(20000000/2); // Emulex 2400138 (68-pin PLCC)

	N82077AA(config, m_fdc, 24000000, n82077aa_device::mode_t::PS2);
	FLOPPY_CONNECTOR(config, "fdc:0", sun_floppies, "35hd", floppy_image_device::default_pc_floppy_formats);

	// the timekeeper has no interrupt output, so 3/80 includes a dedicated timer circuit
	TIMER(config, "timer").configure_periodic(FUNC(sun3x_state::sun380_timer), attotime::from_hz(100));

	screen_device &bwtwo(SCREEN(config, "bwtwo", SCREEN_TYPE_RASTER));
	bwtwo.set_screen_update(FUNC(sun3x_state::bw2_update));
	bwtwo.set_size(1152,900);
	bwtwo.set_visarea(0, 1152-1, 0, 900-1);
	bwtwo.set_refresh_hz(72);
}

void sun3x_state::sun3_460(machine_config &config)
{
	/* basic machine hardware */
	M68030(config, m_maincpu, 33000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &sun3x_state::sun3_460_mem);

	ICM7170(config, "rtc", 32768).irq().set_inputline(m_maincpu, M68K_IRQ_7);

	SCC8530(config, m_scc1, 4.9152_MHz_XTAL);
	SCC8530(config, m_scc2, 4.9152_MHz_XTAL);
	m_scc2->out_txda_callback().set(RS232A_TAG, FUNC(rs232_port_device::write_txd));
	m_scc2->out_txdb_callback().set(RS232B_TAG, FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232a(RS232_PORT(config, RS232A_TAG, default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_scc2, FUNC(z80scc_device::rxa_w));
	rs232a.dcd_handler().set(m_scc2, FUNC(z80scc_device::dcda_w));
	rs232a.cts_handler().set(m_scc2, FUNC(z80scc_device::ctsa_w));

	rs232_port_device &rs232b(RS232_PORT(config, RS232B_TAG, default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_scc2, FUNC(z80scc_device::rxb_w));
	rs232b.dcd_handler().set(m_scc2, FUNC(z80scc_device::dcdb_w));
	rs232b.cts_handler().set(m_scc2, FUNC(z80scc_device::ctsb_w));
}

/* ROM definition */

ROM_START( sun3_80 )
	ROM_REGION32_BE( 0x20000, "user1", ROMREGION_ERASEFF )
/*
Sun 3/80 V1.0 Bootprom
Sun 3/80 V2.2 Bootprom
Sun 3/80 V2.3 Bootprom
Sun 3/80 V2.9.2 Bootprom
Sun 3/80 V3.0 Bootprom
Sun 3/80 V3.0.2 Bootprom
Sun 3/80 V3.0.3 Bootprom
*/
	ROM_SYSTEM_BIOS(0, "rev303", "Rev 3.0.3")
	ROMX_LOAD( "sun3_80_v3.0.3", 0x0000, 0x20000, CRC(8f983115) SHA1(e4be2dcbb29fc5c60ed9d838ab241c634fdd24e5), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "rev302", "Rev 3.0.2")
	ROMX_LOAD( "sun3_80_v3.0.2", 0x0000, 0x20000, CRC(c09a3592) SHA1(830187dfe58e65289533717a797d2c42da86ac4e), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "rev30", "Rev 3.0")
	ROMX_LOAD( "sun3_80_v3.0",   0x0000, 0x20000, CRC(47e3b012) SHA1(1e045b6f542aaf7808d6567c28a9e734a8c5d815), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "rev292", "Rev 2.9.2")
	ROMX_LOAD( "sun3_80_v2.9.2", 0x0000, 0x20000, CRC(32bcf711) SHA1(7ecd4a0d0988c1d1d53fd79ac16c8456ed73ace1), ROM_BIOS(3))

	// default NVRAM: includes valid settings for console on framebuffer, boot from SCSI disk, Ethernet ID, more
	ROM_REGION( 0x800, TIMEKEEPER_TAG, 0 )
	ROM_LOAD( "timekpr_380.bin", 0x000000, 0x000800, CRC(e76f1aae) SHA1(8e7c36e3928887a94a8133e8416ee4126c31edd7) )
ROM_END

ROM_START( sun3_460 )
	ROM_REGION32_BE( 0x20000, "user1", ROMREGION_ERASEFF )
/*
Sun 3/460/480 V1.2.3 Bootprom
Sun 3/460/480 V2.9.1 Bootprom (2 Files, one for odd and one for even addresses)
Sun 3/460/480 V2.9.2 Bootprom
Sun 3/460/480 V2.9.3 Bootprom
Sun 3/460/480 V3.0 Bootprom (2 Files, one for odd and one for even addresses)
*/
	ROM_SYSTEM_BIOS(0, "rev30", "Rev 3.0")
	ROMX_LOAD( "3_400_l.300", 0x00000, 0x10000, CRC(1312a04b) SHA1(6c3b67ba3567991897a48fe20f589ebbfcf0a35d), ROM_BIOS(0))
	ROMX_LOAD( "3_400_h.300", 0x10000, 0x10000, CRC(8d688672) SHA1(a5593844ce6af6c4f7f39bb653dc8f964b73b095), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "rev291", "Rev 2.9.1")
	ROMX_LOAD( "sun3_460_v2.9.1_0", 0x00000, 0x10000, CRC(d62dbf09) SHA1(4a6b5fd7840b44fe93c9058a8973d8dd3c9f7d24), ROM_BIOS(1))
	ROMX_LOAD( "sun3_460_v2.9.1_1", 0x10000, 0x10000, CRC(3b5a5942) SHA1(ed6250e3c07d7cb62d4dd517a8637c8d37e16dc5), ROM_BIOS(1))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT  CLASS        INIT        COMPANY             FULLNAME             FLAGS
COMP( 1989, sun3_80,  0,      0,      sun3_80,  sun3x, sun3x_state, empty_init, "Sun Microsystems", "Sun 3/80",          MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // Hydra
COMP( 1989, sun3_460, 0,      0,      sun3_460, sun3x, sun3x_state, empty_init, "Sun Microsystems", "Sun 3/460/470/480", MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // Pegasus



sun4.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, R. Belmont, Ryan Holtz
/***************************************************************************

        Sun-4 Models
        ------------

    4/260
        Processor(s):   SF9010 @ 16.67MHz, Weitek 1164/1165, Sun-4 MMU,
                        16 hardware contexts
        Speed ratings:  10 MIPS, 1.6 MFLOPS
        CPU:            501-1274/1491/1522
        Chassis type:   deskside
        Bus:            VME (12 slot)
        Memory:         128M (documented) physical with ECC, 1G/process virtual,
                        60ns cycle
        Architecture:   sun4
        Notes:          First SPARC machine. Code-named "Sunrise". Cache
                        much like Sun-3/2xx, uses same memory boards.
                        May be upgraded 3/260.

    4/110
        Processor(s):   MB86900 @ 14.28MHz, Weitek 1164/1165, Sun-4 MMU,
                        16 hardware contexts
        Speed ratings:  7 MIPS
        CPU:            501-1199/1237/1462/1463/1464/1465/1512/1513/
                            1514/1515/1516/1517/1656/1657/1658/1659/
                            1660/1661
        Chassis type:   deskside
        Bus:            VME (3 slot), P4
        Memory:         32M physical with parity, 1G/process virtual,
                        70ns cycle
        Architecture:   sun4
        Notes:          First desktop-able SPARC. CPU doesn't support
                        VME busmaster cards (insufficient room on CPU
                        board for full VME bus interface), so DMA disk
                        and tape boards won't work with it. Originally
                        intended as single-board machine, although there
                        are a few slave-only VME boards (such as the
                        ALM-2 and second ethernet controller) which work
                        with it. SIMM memory (static column?).
                        Code-named "Cobra". CPUs 501-1199/1462/1464/1512/
                        1514/1516/1656/1658/1660 do not have an FPU;
                        501-1237/1463/1465/1513/1515/1517/1657/1659/1661
                        have an FPU.

    4/280
        Chassis type:   rackmount
        Notes:          Rackmount version of 4/260. May be upgraded
                        3/280.

    4/150
        Chassis type:   deskside
        Bus:            VME (6 slot)
        Notes:          See 4/110.

    SPARCstation 1 (4/60)
        Processor(s):   MB86901A or LSI L64801 @ 20MHz, Weitek 3170,
                        Sun-4c MMU, 8 hardware contexts
        Speed ratings:  12.5 MIPS, 1.4 MFLOPS, 10 SPECmark89
        CPU:            501-1382/1629
        Chassis type:   square pizza box
        Bus:            SBus @ 20MHz (3 slots, slot 3 slave-only)
        Memory:         64M physical with synchronous parity,
                        512M/process virtual, 50 ns cycle
        Cache:          64K write-through, direct-mapped, virtually
                        indexed, virtually tagged, 16-byte lines
        Architecture:   sun4c
        Notes:          Code name "Campus". SIMM memory. 3.5" floppy.
                        First supported in SunOS 4.0.3c.

    SPARCserver 1
        Notes:          SPARCstation 1 without a monitor/framebuffer.

    4/330 (SPARCstation 330, SPARCserver 330)
        Processor(s):   CY7C601 @ 25MHz, TI8847, Sun-4 MMU, 16 hardware
                        contexts
        Speed ratings:  16 MIPS, 2.6 MFLOPS, 11.3 SPECmark89
        CPU:            501-1316/1742
        Bus:            VME (3 9U slots, 2 each 6U and 3U), P4
        Memory:         56M/72M (documented) physical with synchronous
                        parity, 1G/process virtual, 40ns cycle
        Cache:          128K
        Architecture:   sun4
        Notes:          SIMM memory. Cache similar to 4/2xx but
                        write-through. Code-named "Stingray". 56M limit
                        only for early versions of ROM.

    4/310
        Chassis:        deskside
        Bus:            VME (3 slots), P4
        Notes:          See 4/330.

    4/350
        Chassis:        deskside
        Bus:            VME (6 slots), P4
        Notes:          See 4/330.

    4/360
        Notes:          4/260 upgraded with a 4/3xx CPU and memory boards.

    4/370 (SPARCstation 370, SPARCserver 370)
        Chassis:        deskside
        Bus:            VME (12 slots), P4
        Notes:          See 4/330.

    4/380
        Notes:          4/280 upgraded with a 4/3xx CPU and memory boards..

    4/390 (SPARCserver 390)
        Chassis:        rackmount
        Bus:            VME (16 slots)
        Notes:          See 4/330.

    4/470 (SPARCstation 470, SPARCserver 470)
        Processor(s):   CY7C601 @ 33MHz, TI8847 (?), 64 MMU hardware
                        contexts
        Speed ratings:  22 MIPS, 3.8 MFLOPS, 17.6 SPECmark89
        CPU:            501-1381/1899
        Chassis:        deskside
        Bus:            VME (12 slots), P4
        Memory:         96M (documented) physical
        Cache:          128K
        Architecture:   sun4
        Notes:          Write-back rather than write-through cache,
                        3-level rather than 2-level Sun-style MMU.
                        Code-name "Sunray" (which was also the code name
                        for the 7C601 CPU).

    4/490 (SPARCserver 490)
        Chassis:        rackmount
        Bus:            VME (16 slots), P4
        Notes:          See 4/470.

    SPARCstation SLC (4/20)
        Processor(s):   MB86901A or LSI L64801 @ 20MHz
        Speed ratings:  12.5 MIPS, 1.2 MFLOPS, 8.6 SPECmark89
        CPU:            501-1627/1680/1720/1748 (1776/1777 ?)
        Chassis type:   monitor
        Bus:            none
        Memory:         16M physical
        Cache:          64K write-through, direct-mapped, virtually
                        indexed, virtually tagged, 16-byte lines
        Architecture:   sun4c
        Notes:          Code name "Off-Campus". SIMM memory. No fan.
                        Built into 17" mono monitor. First supported in
                        SunOS 4.0.3c.

    SPARCstation IPC (4/40)
        Processor(s):   MB86901A or LSI L64801 @ 25MHz
        Speed ratings:  13.8 SPECint92, 11.1 SPECfp92, 327
                        SPECintRate92, 263 SPECfpRate92
        CPU:            501-1689/1835/1870/1974 (1690?)
        Chassis type:   lunchbox
        Bus:            SBus @ 25MHz (2 slots)
        Memory:         48M physical
        Cache:          64K write-through, direct-mapped, virtually
                        indexed, virtually tagged, 16-byte lines
        Architecture:   sun4c
        Notes:          Code name "Phoenix". SIMM memory. Onboard mono
                        framebuffer. 3.5" floppy. First supported in
                        SunOS 4.0.3c.

    SPARCstation 1+ (4/65)
        Processor(s):   LSI L64801 @ 25MHz, Weitek 3172, Sun-4c MMU,
                        8 hardware contexts
        Speed ratings:  15.8 MIPS, 1.7 MFLOPS, 12 SPECmark89
        CPU:            501-1632
        Chassis type:   square pizza box
        Bus:            SBus @ 25MHz (3 slots, slot 3 slave-only)
        Memory:         64M (40M?) physical with synchronous parity,
                        512M/process virtual, 50ns cycle
        Cache:          64K write-through, direct-mapped, virtually
                        indexed, virtually tagged, 16-byte lines
        Architecture:   sun4c
        Notes:          Code name "Campus B". SIMM memory. 3.5" floppy.
                        Essentially same as SPARCstation 1, just faster
                        clock and improved SCSI controller. First
                        supported in SunOS 4.0.3c.

    SPARCserver 1+
        Notes:          SPARCstation 1+ without a monitor/framebuffer.

    SPARCstation 2 (4/75)
        Processor(s):   CY7C601 @ 40MHz, TI TMS390C601A (602A ?), Sun-4c
                        MMU, 16 hardware contexts
        Speed ratings:  28.5 MIPS, 4.2 MFLOPS, 21.8 SPECint92, 22.8
                        SPECfp92, 517 SPECintRate92, 541 SPECfpRate92
        CPU:            501-1638/1744/1858/1859/1912/1926/1989/1995
        Chassis type:   square pizza box
        Bus:            SBus @ 20MHz (3 slots)
        Memory:         64M physical on motherboard/128M total
        Cache:          64K write-through, direct-mapped, virtually
                        indexed, virtually tagged, 32-byte lines
        Architecture:   sun4c
        Notes:          Code name "Calvin". SIMMs memory. 3.5" floppy.
                        Case slightly larger and has more ventilation.
                        (Some models apparently have LSI L64811 @
                        40MHz?) Expansion beyond 64M is possible with a
                        32M card which can take a 32M daughterboard
                        (card blocks SBus slot). First supported in
                        SunOS 4.1.1.

    SPARCserver 2
        Notes:          SPARCstation 2 without a monitor/framebuffer.

    SPARCstation ELC (4/25)
        Processor(s):   Fujitsu MB86903 or Weitek W8701 @ 33MHz, FPU on
                        CPU chip, Sun-4c MMU, 8 hardware contexts
        Speed ratings:  21 MIPS, 3 MFLOPS, 18.2 SPECint92, 17.9
                        SPECfp92, 432 SPECintRate92, 425 SPECfpRate92
        CPU:            501-1861 (1730?)
        Chassis type:   monitor
        Bus:            none
        Memory:         64M physical
        Cache:          64K write-through, direct-mapped, virtually
                        indexed, virtually tagged, 32-byte lines
        Architecture:   sun4c
        Notes:          Code name "Node Warrior". SIMM memory. No fan.
                        Built into 17" mono monitor. first supported in
                        SunOS 4.1.1c.

    SPARCstation IPX (4/50)
        Processor(s):   Fujitsu MB86903 or Weitek W8701 @ 40MHz, FPU on
                        CPU chip, Sun-4c MMU, 8 hardware contexts
        Speed ratings:  28.5 MIPS, 4.2 MFLOPS, 21.8 SPECint92,
                        21.5 SPECfp92, 517 SPECintRate92, 510
                        SPECfpRate92
        CPU:            501-1780/1810/1959/2044
        Chassis type:   lunchbox
        Bus:            SBus @ 20MHz (2 slots)
        Memory:         64M physical
        Cache:          64K write-through cache, direct-mapped,
                        virtually indexed, virtually tagged, 32-byte
                        lines
        Architecture:   sun4c
        Notes:          Code name "Hobbes". SIMM memory. Onboard
                        GX-accelerated cg6 color framebuffer (not usable
                        with ECL mono monitors, unlike SBus version).
                        Picture of Hobbes (from Watterson's "Calvin and
                        Hobbes" comic strip) silkscreened on
                        motherboard. 3.5" floppy. First supported in
                        SunOS 4.1.1 (may require IPX supplement).

    SPARCengine 1 (4/E)
        CPU:            501-8035/8058/8064
        Bus:            VME (6U form factor), SBus (1 slot)
        Notes:          Single-board VME SPARCstation 1 (or 1+?),
                        presumably for use as a controller, not as a
                        workstation. 8K MMU pages rather than 4K.
                        External RAM, framebuffer, and SCSI/ethernet
                        boards available. Code name "Polaris".

    SPARCserver 630MP (4/630)
        Processor(s):   MBus modules
        CPU:            501-1686/2055
        Chassis type:   deskside
        Bus:            VME (3 9U slots, 2 each 6U and 3U), SBus @ 20MHz
                        (4 slots), MBus (2 slots)
        Memory:         640M physical
        Architecture:   sun4m
        Notes:          First MBus-based machine. Code name "Galaxy".
                        SIMM memory.

    SPARCserver 670MP (4/670)
        Chassis type:   deskside
        Bus:            VME (12 slots), SBus @ 20MHz (4 slots), MBus (2
                        slots)
        Notes:          Like SPARCserver 630MP. More SBus slots can be
                        added via VME expansion boards.

    SPARCserver 690MP (4/690)
        Chassis type:   rackmount
        Bus:            VME (16 slots), SBus @ 20MHz (4 slots), MBus (2
                        slots)
        Notes:          See SPARCserver 670MP.

    SPARCclassic (SPARCclassic Server)(SPARCstation LC) (4/15)
        Processor(s):   microSPARC @ 50MHz
        Speed ratings:  59.1 MIPS, 4.6 MFLOPS, 26.4 SPECint92, 21.0
                        SPECfp92, 626 SPECintRate92, 498 SPECfpRate92
        CPU:            501-2200/2262/2326
        Chassis type:   lunchbox
        Bus:            SBus @ 20MHz (2 slots)
        Memory:         96M physical
        Architecture:   sun4m
        Notes:          Sun4m architecture, but no MBus (uniprocessor
                        only). SIMM memory. Shares code name "Sunergy"
                        with LX. 3.5" floppy. Soldered CPU chip. Onboard
                        cgthree framebuffer, AMD79C30 8-bit audio chip.
                        First supported in SunOS 4.1.3c.

    SPARCclassic X (4/10)
        CPU:            501-2079/2262/2313
        Notes:          Essentially the same as SPARCclassic, but
                        intended for use as an X terminal (?).

    SPARCstation LX/ZX (4/30)
        Processor(s):   microSPARC @ 50MHz
        Speed ratings:  59.1 MIPS, 4.6 MFLOPS, 26.4 SPECint92, 21.0
                        SPECfp92, 626 SPECintRate92, 498 SPECfpRate92
        CPU:            501-2031/2032/2233/2474
        Chassis type:   lunchbox
        Bus:            SBus @ 20MHz (2 slots)
        Memory:         96M physical
        Architecture:   sun4m
        Notes:          Sun4m architecture, but no MBus (uniprocessor
                        only). SIMM memory. Shares code name "Sunergy"
                        with SPARCclassic. Soldered CPU chip. Onboard
                        cgsix framebuffer, 1M VRAM standard, expandable
                        to 2M. DBRI 16-bit audio/ISDN chip. First
                        supported in SunOS 4.1.3c.

   SPARCstation Voyager
        Processors(s):  microSPARC II @ 60MHz
        Speed ratings:  47.5 SPECint92, 40.3 SPECfp92, 1025
                        SPECintRate92, 859 SPECfpRate92
        Bus:            SBus; PCMCIA type II (2 slots)
        Memory:         80M physical
        Architecture:   sun4m
        Notes:          Portable (laptop?). 16M standard, two memory
                        expansion slots for Voyager-specific SIMMs (16M
                        or 32M). Code-named "Gypsy". 14" 1152x900 mono
                        or 12" 1024x768 color flat panel displays. DBRI
                        16-bit audio/ISDN chip.

    SPARCstation 3
        Notes:          Although this model appeared in a few Sun price
                        lists, it was renamed the SPARCstation 10 before
                        release.

    SPARCstation 10/xx
        Processor(s):   MBus modules
        Motherboard:    501-1733/2259/2274/2365 (-2274 in model 20 only)
        Chassis type:   square pizza box
        Bus:            SBus @ 16.6/20MHz (model 20) or 18/20MHz (other
                        models) (4 slots); MBus (2 slots)
        Memory:         512M physical
        Architecture:   sun4m
        Notes:          Code name "Campus-2". 3.5" floppy. SIMM memory.
                        Some models use double-width MBus modules which
                        block SBus slots. Also, the inner surface of the
                        chassis is conductive, so internal disk drives
                        must be mounted with insulating hardware.

    SPARCserver 10/xx
        Notes:          SPARCstation 10/xx without monitor/framebuffer.

    SPARCcenter 2000
        Processor(s):   MBus modules
        Motherboard:    501-1866/2334/2362
        Bus:            XDBus * 2 (20 slots); SBus @ 20MHz (4
                        slots/motherboard); MBus (2 slots/motherboard)
        Memory:         5G physical
        Cache:          2M/motherboard
        Architecture:   sun4d
        Notes:          Dual XDBus backplane with 20 slots. One board
                        type that carries dual MBus modules with 2M
                        cache (1M for each XDBus), 512M memory and 4
                        SBus slots. Any combination can be used; memory
                        is *not* tied to the CPU modules but to an
                        XDBus. Solaris 2.x releases support an
                        increasing number of CPUs (up to twenty), due to
                        tuning efforts in the kernel. First supported in
                        Solaris 2.2 (SunOS 5.2). Code name "Dragon".

    SPARCserver 1000
        Processor(s):   MBus modules
        Motherboard:    501-2336 (2338?)
        Bus:            XDBus; SBus @ 20MHz (3 slots/motherboard); MBus
                        (2 slots/motherboard)
        Memory:         2G physical
        Cache:          1M/motherboard
        Architecture:   sun4d
        Notes:          Single XDBus design with "curious L-shaped
                        motherboards". Three SBus slots per motherboard,
                        512M, two MBus modules per motherboard. Four
                        motherboards total, or a disk tray with four 1"
                        high 3.5" disks. Code name "Scorpion". First
                        supported in Solaris 2.2 (SunOS 5.2).

        21/11/2011 Skeleton driver.
        20/06/2016 Much less skeletony.

        // sun4:  16 contexts, 4096 segments, each PMEG is 32 PTEs, each PTE is 8K
        // VA lower 13 bits in page, next 5 bits select PTE in PMEG, next 12 bits select PMEG, top 2 must be 00 or 11.

        4/60 ROM notes:

        ffe809fc: call to print "Sizing Memory" to the UART
        ffe80a70: call to "Setting up RAM for monitor" that goes wrong
        ffe80210: testing memory
        ffe80274: loop that goes wobbly and fails
        ffe80dc4: switch off boot mode, MMU maps ROM to copy in RAM from here on
        ffe82000: start of FORTH (?) interpreter once decompressed
                  text in decompressed area claims to be FORTH-83 FCode, but the opcodes
                  do not match the documented OpenFirmware FCode ones at all.

        4/3xx ROM notes:
        sun4: CPU LEDs to 00 (PC=ffe92398) => ........
        sun4: CPU LEDs to 01 (PC=ffe92450) => *.......
        sun4: CPU LEDs to 02 (PC=ffe9246c) => .*......
        sun4: CPU LEDs to 03 (PC=ffe9aa54) => **......
        sun4: CPU LEDs to 04 (PC=ffe9aa54) => ..*.....
        sun4: CPU LEDs to 05 (PC=ffe9aa54) => *.*.....
        sun4: CPU LEDs to 06 (PC=ffe9aa54) => .**.....
        sun4: CPU LEDs to 07 (PC=ffe9aa54) => ***.....


****************************************************************************/

#include "emu.h"

#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"
#include "bus/nscsi/tape.h"
#include "bus/rs232/rs232.h"
#include "bus/sunkbd/sunkbd.h"
#include "bus/sunmouse/sunmouse.h"
#include "bus/sbus/sbus.h"
#include "bus/sbus/bwtwo.h"
#include "cpu/sparc/sparc.h"
#include "imagedev/floppy.h"
#include "machine/am79c90.h"
#include "machine/bankdev.h"
#include "machine/ncr53c90.h"
#include "machine/nscsi_bus.h"
#include "machine/nvram.h"
#include "machine/ram.h"
#include "machine/sun4c_mmu.h"
#include "machine/timekpr.h"
#include "machine/upd765.h"
#include "machine/z80scc.h"

#include "debug/debugcon.h"
#include "debugger.h"
#include "screen.h"

#define LOG_AUXIO               (1U << 1)
#define LOG_IRQ_READS           (1U << 2)
#define LOG_IRQ_WRITES          (1U << 3)
#define LOG_TMR0_COUNT_READS    (1U << 4)
#define LOG_TMR0_LIMIT_READS    (1U << 5)
#define LOG_TMR1_COUNT_READS    (1U << 6)
#define LOG_TMR1_LIMIT_READS    (1U << 7)
#define LOG_TMR0_COUNT_WRITES   (1U << 8)
#define LOG_TMR0_LIMIT_WRITES   (1U << 9)
#define LOG_TMR1_COUNT_WRITES   (1U << 10)
#define LOG_TMR1_LIMIT_WRITES   (1U << 11)
#define LOG_DMA_IRQS            (1U << 12)
#define LOG_DMA_WRITES          (1U << 13)
#define LOG_DMA_WRITE_DATA      (1U << 14)
#define LOG_DMA_READS           (1U << 15)
#define LOG_DMA_READ_DATA       (1U << 16)
#define LOG_DMA_CTRL_READS      (1U << 17)
#define LOG_DMA_CTRL_WRITES     (1U << 18)
#define LOG_DMA_DRAINS          (1U << 19)
#define LOG_SCSI_IRQ            (1U << 20)
#define LOG_SCSI_DRQ            (1U << 21)
#define LOG_FDC_IRQ             (1U << 22)
#define LOG_FDC_READS           (1U << 23)
#define LOG_FDC_WRITES          (1U << 24)
#define LOG_IRQS                (LOG_IRQ_READS | LOG_IRQ_WRITES | LOG_DMA_IRQS | LOG_SCSI_IRQ)
#define LOG_TIMER_READS         (LOG_TMR0_COUNT_READS | LOG_TMR0_LIMIT_READS | LOG_TMR1_COUNT_READS | LOG_TMR1_LIMIT_READS)
#define LOG_TIMER_WRITES        (LOG_TMR0_COUNT_WRITES | LOG_TMR0_LIMIT_WRITES | LOG_TMR1_COUNT_WRITES | LOG_TMR1_LIMIT_WRITES)
#define LOG_DMA                 (LOG_DMA_IRQS | LOG_DMA_WRITES | LOG_DMA_WRITE_DATA | LOG_DMA_READS | LOG_DMA_READ_DATA | LOG_DMA_CTRL_READS | LOG_DMA_CTRL_WRITES | LOG_DMA_DRAINS)
#define LOG_FDC                 (LOG_FDC_IRQ | LOG_FDC_READS | LOG_FDC_WRITES)
#define LOG_ALL                 (LOG_AUXIO | LOG_IRQS | LOG_TIMER_READS | LOG_TIMER_WRITES | LOG_FDC)

//#define VERBOSE (LOG_ALL)
#include "logmacro.h"

#define SUN4_LOG_FCODES (0)

// DMA controller constants
#define DMA_DEV_ID      (0x80000000)
#define DMA_L           (0x00008000)    // use ILACC
#define DMA_TC          (0x00004000)    // terminal count
#define DMA_EN_CNT      (0x00002000)    // enable count
#define DMA_BYTE_ADDR   (0x00001800)    // next byte number to be accessed
#define DMA_BYTE_ADDR_SHIFT (11)
#define DMA_REQ_PEND    (0x00000400)    // request pending
#define DMA_EN_DMA      (0x00000200)    // enable DMA
#define DMA_WRITE       (0x00000100)    // DMA device->mem if 1, otherwise mem->device
#define DMA_RESET       (0x00000080)    // DMA hardware reset
#define DMA_DRAIN       (0x00000040)    // force remaining pack bytes to memory
#define DMA_FLUSH       (0x00000020)    // force PACK_CNT and ERR_PEND to 0
#define DMA_INT_EN      (0x00000010)    // interrupt enable
#define DMA_PACK_CNT    (0x0000000c)    // number of bytes in pack register
#define DMA_PACK_CNT_SHIFT  (2)
#define DMA_ERR_PEND    (0x00000002)    // error pending, set when memory exception occurs
#define DMA_INT_PEND    (0x00000001)    // interrupt pending, set when TC=1
#define DMA_READ_ONLY   (DMA_TC | DMA_BYTE_ADDR | DMA_REQ_PEND | DMA_PACK_CNT | DMA_ERR_PEND | DMA_INT_PEND)
#define DMA_WRITE_ONLY  (DMA_FLUSH)
#define DMA_READ_WRITE  (DMA_EN_CNT | DMA_EN_DMA | DMA_WRITE | DMA_RESET | DMA_DRAIN | DMA_INT_EN)
#define DMA_CTRL        (0)
#define DMA_ADDR        (1)
#define DMA_BYTE_COUNT  (2)
#define DMA_XTAL        (25_MHz_XTAL)

#define AUXIO_DENSITY   (0x20)
#define AUXIO_DISK_CHG  (0x10)
#define AUXIO_DRIVE_SEL (0x08)
#define AUXIO_TC        (0x04)
#define AUXIO_EJECT     (0x02)
#define AUXIO_LED       (0x01)

namespace
{
const sparc_disassembler::asi_desc_map::value_type sun4_asi_desc[] = {
														{ 0x10, { nullptr, "Flush I-Cache (Segment)" } },
														{ 0x11, { nullptr, "Flush I-Cache (Page)"    } },
	{ 0x02, { nullptr, "System Space"           } }, { 0x12, { nullptr, "Flush I-Cache (Context)" } },
	{ 0x03, { nullptr, "Segment Map"            } }, { 0x13, { nullptr, "Flush I-Cache (User)"    } },
	{ 0x04, { nullptr, "Page Map"               } }, { 0x14, { nullptr, "Flush D-Cache (Segment)" } },
	{ 0x05, { nullptr, "Block Copy"             } }, { 0x15, { nullptr, "Flush D-Cache (Page)"    } },
	{ 0x06, { nullptr, "Region Map"             } }, { 0x16, { nullptr, "Flush D-Cache (Context)" } },
	{ 0x07, { nullptr, "Flush Cache (Region)"   } }, { 0x17, { nullptr, "Flush D-Cache (User)"    } },
	{ 0x08, { nullptr, "User Instruction"       } },
	{ 0x09, { nullptr, "Supervisor Instruction" } },
	{ 0x0a, { nullptr, "User Data"              } },
	{ 0x0b, { nullptr, "Supervisor Data"        } }, { 0x1b, { nullptr, "Flush I-Cache (Region)"  } },
	{ 0x0c, { nullptr, "Flush Cache (Segment)"  } },
	{ 0x0d, { nullptr, "Flush Cache (Page)"     } },
	{ 0x0e, { nullptr, "Flush Cache (Context)"  } },
	{ 0x0f, { nullptr, "Flush Cache (User)"     } }, { 0x1f, { nullptr, "Flush D-Cache (Region)"  } }
};

const sparc_disassembler::asi_desc_map::value_type sun4c_asi_desc[] = {
	{ 0x02, { nullptr, "System Space"           } },
	{ 0x03, { nullptr, "Segment Map"            } },
	{ 0x04, { nullptr, "Page Map"               } },
	{ 0x08, { nullptr, "User Instruction"       } },
	{ 0x09, { nullptr, "Supervisor Instruction" } },
	{ 0x0a, { nullptr, "User Data"              } },
	{ 0x0b, { nullptr, "Supervisor Data"        } },
	{ 0x0c, { nullptr, "Flush Cache (Segment)"  } },
	{ 0x0d, { nullptr, "Flush Cache (Page)"     } },
	{ 0x0e, { nullptr, "Flush Cache (Context)"  } }
};
}

class sun4_base_state : public driver_device
{
public:
	sun4_base_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mmu(*this, "mmu")
		, m_timekpr(*this, "timekpr")
		, m_keyboard(*this, "keyboard")
		, m_mouse(*this, "mouseport")
		, m_rs232(*this, "rs232%c", (u32)'a')
		, m_scc(*this, "scc%u", 1U)
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:0")
		, m_lance(*this, "lance")
		, m_scsibus(*this, "scsibus")
		, m_scsi(*this, "scsibus:7:ncr53c90")
		, m_type1space(*this, "type1")
		, m_ram(*this, RAM_TAG)
		, m_rom(*this, "user1")
	{
	}

	void sun4_base(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

	u32 debugger_r(offs_t offset, u32 mem_mask = ~0);
	void debugger_w(offs_t offset, u32 data, u32 mem_mask = ~0);

	template <int Timer> TIMER_CALLBACK_MEMBER(timer_tick);

	template <int Timer> u32 timer_count_r();
	template <int Timer> u32 timer_limit_r();
	template <int Timer> void timer_count_w(offs_t offset, u32 data, u32 mem_mask = ~0);
	template <int Timer> void timer_limit_w(offs_t offset, u32 data, u32 mem_mask = ~0);

	u8 irq_r();
	void irq_w(u8 data);
	u8 fdc_r(offs_t offset);
	void fdc_w(offs_t offset, u8 data);
	u8 auxio_r();
	void auxio_w(u8 data);
	u32 dma_ctrl_r();
	u32 dma_addr_r();
	u32 dma_count_r();
	void dma_ctrl_w(offs_t offset, u32 data, u32 mem_mask = ~0);
	void dma_addr_w(offs_t offset, u32 data, u32 mem_mask = ~0);
	void dma_count_w(offs_t offset, u32 data, u32 mem_mask = ~0);

	void scsi_irq(int state);
	void scsi_drq(int state);

	template <int Chip> void scc_int(int state);

	void fdc_irq(int state);

	void ncr53c90(device_t *device);

	void debugger_map(address_map &map) ATTR_COLD;
	void system_asi_map(address_map &map) ATTR_COLD;
	void segment_asi_map(address_map &map) ATTR_COLD;
	void page_asi_map(address_map &map) ATTR_COLD;
	void hw_segment_flush_asi_map(address_map &map) ATTR_COLD;
	void hw_page_flush_asi_map(address_map &map) ATTR_COLD;
	void hw_context_flush_asi_map(address_map &map) ATTR_COLD;
	void user_insn_asi_map(address_map &map) ATTR_COLD;
	void super_insn_asi_map(address_map &map) ATTR_COLD;
	void user_data_asi_map(address_map &map) ATTR_COLD;
	void super_data_asi_map(address_map &map) ATTR_COLD;
	void sw_segment_flush_asi_map(address_map &map) ATTR_COLD;
	void sw_page_flush_asi_map(address_map &map) ATTR_COLD;
	void sw_context_flush_asi_map(address_map &map) ATTR_COLD;
	void hw_flush_all_asi_map(address_map &map) ATTR_COLD;

	void type1space_base_map(address_map &map) ATTR_COLD;

	required_device<sparc_base_device> m_maincpu;
	required_device<sun4_mmu_base_device> m_mmu;

	required_device<m48t02_device> m_timekpr;

	required_device<sun_keyboard_port_device> m_keyboard;
	required_device<sun_mouse_port_device> m_mouse;

	required_device_array<rs232_port_device, 2> m_rs232;
	required_device_array<z80scc_device, 2> m_scc;

	required_device<upd765_family_device> m_fdc;
	required_device<floppy_connector> m_floppy;
	required_device<am79c90_device> m_lance;
	required_device<nscsi_bus_device> m_scsibus;
	required_device<ncr53c90_device> m_scsi;

	required_device<address_map_bank_device> m_type1space;
	required_device<ram_device> m_ram;
	required_memory_region m_rom;

	u8 m_auxio;
	u32 m_timer_count[2];
	u32 m_timer_limit[2];
	u32 m_dma_ctrl;
	u32 m_dma_addr;
	u32 m_dma_count;
	bool m_dma_irq;
	bool m_dma_tc_read;
	u32 m_dma_pack_register;
	bool m_scsi_irq;
	bool m_fdc_irq;

	u8 m_irq_reg;    // IRQ control
	u8 m_scc_int[2];

	emu_timer *m_timer[2];

	void dma_check_interrupts();
	void dma_transfer();
	void dma_transfer_write();
	void dma_transfer_read();

	void fcodes_command(int ref, const std::vector<std::string_view> &params);

	static constexpr inline u32 LOG_TIMER_COUNT_WRITES[2] = { LOG_TMR0_COUNT_WRITES, LOG_TMR1_COUNT_WRITES };
	static constexpr inline u32 LOG_TIMER_LIMIT_WRITES[2] = { LOG_TMR0_LIMIT_WRITES, LOG_TMR1_LIMIT_WRITES };
	static constexpr inline u32 LOG_TIMER_COUNT_READS[2] = { LOG_TMR0_COUNT_READS, LOG_TMR1_COUNT_READS };
	static constexpr inline u32 LOG_TIMER_LIMIT_READS[2] = { LOG_TMR0_LIMIT_READS, LOG_TMR1_LIMIT_READS };
	static constexpr inline int TIMER_IRQ_LINES[2] = { SPARC_IRQ10, SPARC_IRQ14 };
	static constexpr inline u8 TIMER_IRQ_MASKS[2] = { 0x21, 0x81 };
};

class sun4_state : public sun4_base_state
{
public:
	sun4_state(const machine_config &mconfig, device_type type, const char *tag)
		: sun4_base_state(mconfig, type, tag)
	{
	}

	void sun4(machine_config &config);

private:
	void type1space_map(address_map &map) ATTR_COLD;
};

class sun4c_state : public sun4_base_state
{
public:
	sun4c_state(const machine_config &mconfig, device_type type, const char *tag)
		: sun4_base_state(mconfig, type, tag)
		, m_sbus(*this, "sbus")
		, m_sbus_slot(*this, "slot%u", 1U)
	{
	}

	void sun4c(machine_config &config);
	void sun4_20(machine_config &config);
	void sun4_25(machine_config &config);
	void sun4_40(machine_config &config);
	void sun4_50(machine_config &config);
	void sun4_60(machine_config &config);
	void sun4_65(machine_config &config);
	void sun4_75(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	template <int Line> void sbus_irq_w(int state);

	void type1space_map(address_map &map) ATTR_COLD;

	required_device<sbus_device> m_sbus;
	required_device_array<sbus_slot_device, 3> m_sbus_slot;
};

u32 sun4_base_state::debugger_r(offs_t offset, u32 mem_mask)
{
	return m_mmu->insn_data_r<sun4_mmu_base_device::SUPER_INSN>(offset, mem_mask);
}

void sun4_base_state::debugger_w(offs_t offset, u32 data, u32 mem_mask)
{
	m_mmu->insn_data_w<sun4_mmu_base_device::SUPER_INSN>(offset, data, mem_mask);
}

void sun4_base_state::fcodes_command(int ref, const std::vector<std::string_view> &params)
{
#if SUN4_LOG_FCODES
	if (params.length() < 1)
		return;

	bool is_on = (params[0] == "on");
	bool is_off = (params[0] == "off");

	if (!is_on && !is_off)
	{
		machine().debugger().console().printf("Please specify 'on' or 'off'.\n");
		return;
	}

	bool enabled = is_on;

	m_maincpu->enable_log_fcodes(enabled);
#endif
}

// make debugger fetches emulate supervisor program for best compatibility with boot PROM execution

void sun4_base_state::debugger_map(address_map &map)
{
	map(0x00000000, 0xffffffff).rw(FUNC(sun4_base_state::debugger_r), FUNC(sun4_base_state::debugger_w));
}

void sun4_base_state::type1space_base_map(address_map &map)
{
	map(0x00000000, 0xffffffff).rw(m_mmu, FUNC(sun4_mmu_base_device::type1_timeout_r), FUNC(sun4_mmu_base_device::type1_timeout_w));
	map(0x00000000, 0x0000000f).rw(m_scc[0], FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w)).umask32(0xff00ff00);
	map(0x01000000, 0x0100000f).rw(m_scc[1], FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w)).umask32(0xff00ff00);
	map(0x02000000, 0x020007ff).rw(m_timekpr, FUNC(timekeeper_device::read), FUNC(timekeeper_device::write));
	map(0x03000000, 0x03000003).rw(FUNC(sun4_base_state::timer_count_r<0>), FUNC(sun4_base_state::timer_count_w<0>)).mirror(0xfffff0);
	map(0x03000004, 0x03000007).rw(FUNC(sun4_base_state::timer_limit_r<0>), FUNC(sun4_base_state::timer_limit_w<0>)).mirror(0xfffff0);
	map(0x03000008, 0x0300000b).rw(FUNC(sun4_base_state::timer_count_r<1>), FUNC(sun4_base_state::timer_count_w<1>)).mirror(0xfffff0);
	map(0x0300000c, 0x0300000f).rw(FUNC(sun4_base_state::timer_limit_r<1>), FUNC(sun4_base_state::timer_limit_w<1>)).mirror(0xfffff0);
	map(0x04000000, 0x04000007).rw(m_mmu, FUNC(sun4_mmu_base_device::parity_r), FUNC(sun4_mmu_base_device::parity_w));
	map(0x05000000, 0x05000003).rw(FUNC(sun4_base_state::irq_r), FUNC(sun4_base_state::irq_w));
	map(0x06000000, 0x0607ffff).rom().region("user1", 0);
	map(0x07200000, 0x07200007).rw(FUNC(sun4_base_state::fdc_r), FUNC(sun4_base_state::fdc_w));
	map(0x07400003, 0x07400003).rw(FUNC(sun4_base_state::auxio_r), FUNC(sun4_base_state::auxio_w));
	map(0x08000000, 0x08000003).lr32(NAME([](offs_t offset) { return 0xfe810101; })); // flag that internal slot 0 has integrated SCSI/DMA/Ethernet
	map(0x08400000, 0x08400003).rw(FUNC(sun4_base_state::dma_ctrl_r), FUNC(sun4_base_state::dma_ctrl_w));
	map(0x08400004, 0x08400007).rw(FUNC(sun4_base_state::dma_addr_r), FUNC(sun4_base_state::dma_addr_w));
	map(0x08400008, 0x0840000b).rw(FUNC(sun4_base_state::dma_count_r), FUNC(sun4_base_state::dma_count_w));
	map(0x08800000, 0x0880002f).m(m_scsi, FUNC(ncr53c90_device::map)).umask32(0xff000000);
	map(0x08c00000, 0x08c00003).rw(m_lance, FUNC(am79c90_device::regs_r), FUNC(am79c90_device::regs_w));
}

void sun4c_state::type1space_map(address_map &map)
{
	type1space_base_map(map);
	map(0x0a000000, 0x0fffffff).rw(m_sbus, FUNC(sbus_device::read), FUNC(sbus_device::write));
}

void sun4_state::type1space_map(address_map &map)
{
	type1space_base_map(map);
}

void sun4_base_state::system_asi_map(address_map &map)
{
	map(0x30000000, 0x3fffffff).rw(m_mmu, FUNC(sun4_mmu_base_device::context_reg_r), FUNC(sun4_mmu_base_device::context_reg_w));
	map(0x40000000, 0x4fffffff).rw(m_mmu, FUNC(sun4_mmu_base_device::system_enable_r), FUNC(sun4_mmu_base_device::system_enable_w));
	map(0x60000000, 0x6fffffff).rw(m_mmu, FUNC(sun4_mmu_base_device::bus_error_r), FUNC(sun4_mmu_base_device::bus_error_w));
	map(0x80000000, 0x8fffffff).rw(m_mmu, FUNC(sun4_mmu_base_device::cache_tag_r), FUNC(sun4_mmu_base_device::cache_tag_w));
	map(0x90000000, 0x9fffffff).rw(m_mmu, FUNC(sun4_mmu_base_device::cache_data_r), FUNC(sun4_mmu_base_device::cache_data_w));
	map(0xf0000000, 0xffffffff).rw(m_mmu, FUNC(sun4_mmu_base_device::uart_r), FUNC(sun4_mmu_base_device::uart_w));
}

void sun4_base_state::segment_asi_map(address_map &map)
{
	map(0x00000000, 0xffffffff).rw(m_mmu, FUNC(sun4_mmu_base_device::segment_map_r), FUNC(sun4_mmu_base_device::segment_map_w));
}

void sun4_base_state::page_asi_map(address_map &map)
{
	map(0x00000000, 0xffffffff).rw(m_mmu, FUNC(sun4_mmu_base_device::page_map_r), FUNC(sun4_mmu_base_device::page_map_w));
}

void sun4_base_state::hw_segment_flush_asi_map(address_map &map)
{
	map(0x00000000, 0xffffffff).w(m_mmu, FUNC(sun4_mmu_base_device::hw_segment_flush_w));
}

void sun4_base_state::hw_page_flush_asi_map(address_map &map)
{
	map(0x00000000, 0xffffffff).w(m_mmu, FUNC(sun4_mmu_base_device::hw_page_flush_w));
}

void sun4_base_state::hw_context_flush_asi_map(address_map &map)
{
	map(0x00000000, 0xffffffff).w(m_mmu, FUNC(sun4_mmu_base_device::hw_context_flush_w));
}

void sun4_base_state::user_insn_asi_map(address_map &map)
{
	map(0x00000000, 0xffffffff).rw(m_mmu, FUNC(sun4_mmu_base_device::insn_data_r<sun4_mmu_base_device::USER_INSN>), FUNC(sun4_mmu_base_device::insn_data_w<sun4_mmu_base_device::USER_INSN>));
}

void sun4_base_state::super_insn_asi_map(address_map &map)
{
	map(0x00000000, 0xffffffff).rw(m_mmu, FUNC(sun4_mmu_base_device::insn_data_r<sun4_mmu_base_device::SUPER_INSN>), FUNC(sun4_mmu_base_device::insn_data_w<sun4_mmu_base_device::SUPER_INSN>));
}

void sun4_base_state::user_data_asi_map(address_map &map)
{
	map(0x00000000, 0xffffffff).rw(m_mmu, FUNC(sun4_mmu_base_device::insn_data_r<sun4_mmu_base_device::USER_DATA>), FUNC(sun4_mmu_base_device::insn_data_w<sun4_mmu_base_device::USER_DATA>));
}

void sun4_base_state::super_data_asi_map(address_map &map)
{
	map(0x00000000, 0xffffffff).rw(m_mmu, FUNC(sun4_mmu_base_device::insn_data_r<sun4_mmu_base_device::SUPER_DATA>), FUNC(sun4_mmu_base_device::insn_data_w<sun4_mmu_base_device::SUPER_DATA>));
}

void sun4_base_state::sw_segment_flush_asi_map(address_map &map)
{
	map(0x00000000, 0xffffffff).w(m_mmu, FUNC(sun4_mmu_base_device::segment_flush_w));
}

void sun4_base_state::sw_page_flush_asi_map(address_map &map)
{
	map(0x00000000, 0xffffffff).w(m_mmu, FUNC(sun4_mmu_base_device::page_flush_w));
}

void sun4_base_state::sw_context_flush_asi_map(address_map &map)
{
	map(0x00000000, 0xffffffff).w(m_mmu, FUNC(sun4_mmu_base_device::context_flush_w));
}

void sun4_base_state::hw_flush_all_asi_map(address_map &map)
{
	map(0x00000000, 0xffffffff).w(m_mmu, FUNC(sun4_mmu_base_device::hw_flush_all_w));
}

/* Input ports */
static INPUT_PORTS_START( sun4 )
INPUT_PORTS_END

void sun4_base_state::machine_start()
{
	if (machine().debug_flags & DEBUG_FLAG_ENABLED)
	{
		using namespace std::placeholders;
		#if SUN4_LOG_FCODES
		machine().debugger().console().register_command("fcodes", CMDFLAG_NONE, 0, 1, 1, std::bind(&sun4_base_state::fcodes_command, this, _1, _2));
		#endif
	}

	// allocate timers for the built-in two channel timer
	m_timer[0] = timer_alloc(FUNC(sun4_base_state::timer_tick<0>), this);
	m_timer[1] = timer_alloc(FUNC(sun4_base_state::timer_tick<1>), this);
	m_timer[0]->adjust(attotime::from_usec(1), 0, attotime::from_usec(1));
	m_timer[1]->adjust(attotime::from_usec(1), 0, attotime::from_usec(1));

	save_item(NAME(m_auxio));
	save_item(NAME(m_timer_count));
	save_item(NAME(m_timer_limit));
	save_item(NAME(m_dma_ctrl));
	save_item(NAME(m_dma_addr));
	save_item(NAME(m_dma_count));
	save_item(NAME(m_dma_irq));
	save_item(NAME(m_dma_tc_read));
	save_item(NAME(m_dma_pack_register));
	save_item(NAME(m_scsi_irq));
	save_item(NAME(m_fdc_irq));

	save_item(NAME(m_irq_reg));
	save_item(NAME(m_scc_int));
}

void sun4c_state::machine_start()
{
	sun4_base_state::machine_start();
}

void sun4_base_state::machine_reset()
{
	m_auxio = 0xc0;
	m_irq_reg = 0;
	m_scc_int[0] = 0;
	m_scc_int[1] = 0;
	m_scsi_irq = false;
	m_fdc_irq = false;
	m_dma_irq = false;
	m_dma_tc_read = false;
	m_dma_pack_register = 0;

	m_timer_count[0] = 0;
	m_timer_count[1] = 0;
	m_timer_limit[0] = 0;
	m_timer_limit[1] = 0;

	m_dma_ctrl = 0;
	m_dma_addr = 0;
	m_dma_count = 0;
}

void sun4c_state::machine_reset()
{
	sun4_base_state::machine_reset();
}

u8 sun4_base_state::fdc_r(offs_t offset)
{
	u8 data = 0;
	if (machine().side_effects_disabled())
		return data;

	switch(offset)
	{
		case 0: // Main Status (R, 82072)
			data = m_fdc->msr_r();
			LOGMASKED(LOG_FDC_READS, "%s: fdc_r, Main Status Register (i82072), %02x\n", machine().describe_context(), data);
			break;

		case 1: // FIFO Data Port (R, 82072)
			data = m_fdc->fifo_r();
			LOGMASKED(LOG_FDC_READS, "%s: fdc_r, Data Port (i82072), %02x\n", machine().describe_context(), data);
			break;

		case 2: // Digital Output Register (R, 82077)
			data = m_fdc->dor_r();
			LOGMASKED(LOG_FDC_READS, "%s: fdc_r, Digital Output Register (i82077), %02x\n", machine().describe_context(), data);
			break;

		case 4: // Main Status Register (R, 82077)
			data = m_fdc->msr_r();
			LOGMASKED(LOG_FDC_READS, "%s: fdc_r, Main Status Register (i82077), %02x\n", machine().describe_context(), data);
			break;

		case 5: // FIFO Data Port (R, 82077)
			data = m_fdc->fifo_r();
			LOGMASKED(LOG_FDC_READS, "%s: fdc_r, Data Port (i82077), %02x\n", machine().describe_context(), data);
			break;

		case 7: // Digital Input Register (R, 82077)
			data = m_fdc->dir_r();
			LOGMASKED(LOG_FDC_READS, "%s: fdc_r, Digital Input Register (i82077), %02x\n", machine().describe_context(), data);
			break;

		default:
			LOGMASKED(LOG_FDC_READS, "%s: fdc_r, Unknown Register (%d), %02x\n", machine().describe_context(), (int)offset, data);
			break;
	}

	return data;
}

void sun4_base_state::fdc_w(offs_t offset, u8 data)
{
	switch(offset)
	{
		case 0: // Data Rate Select Register (W, 82072)
			LOGMASKED(LOG_FDC_WRITES, "%s: fdc_w, Data Rate Select Register (i82072), %02x\n", machine().describe_context(), data);
			m_fdc->dsr_w(data);
			break;

		case 1: // FIFO Data Port (W, 82072)
			LOGMASKED(LOG_FDC_WRITES, "%s: fdc_w, FIFO Data Port (i82072), %02x\n", machine().describe_context(), data);
			m_fdc->fifo_w(data);
			break;

		case 2: // Digital Output Register (W, 82077)
			LOGMASKED(LOG_FDC_WRITES, "%s: fdc_w, Digital Output Register (i82077), %02x\n", machine().describe_context(), data);
			m_fdc->dor_w(data);
			break;

		case 4: // Data Rate Select Register (W, 82077)
			LOGMASKED(LOG_FDC_WRITES, "%s: fdc_w, Data Rate Select Register (i82077), %02x\n", machine().describe_context(), data);
			m_fdc->dsr_w(data);
			break;

		case 5: // FIFO Data Port (W, 82077)
			LOGMASKED(LOG_FDC_WRITES, "%s: fdc_w, FIFO Data Port (i82077), %02x\n", machine().describe_context(), data);
			m_fdc->fifo_w(data);
			break;

		case 7: // Configuration Control Register (W, 82077)
			LOGMASKED(LOG_FDC_WRITES, "%s: fdc_w, Configuration Control Register (i82077), %02x\n", machine().describe_context(), data);
			m_fdc->ccr_w(data);
			break;

		default:
			LOGMASKED(LOG_FDC_WRITES, "%s: fdc_w, Unknown Register (%d), %02x\n", machine().describe_context(), (int)offset, data);
			break;
	}
}

u8 sun4_base_state::auxio_r()
{
	LOGMASKED(LOG_AUXIO, "%s: auxio_r: %02x\n", machine().describe_context(), m_auxio);
	return m_auxio;
}

void sun4_base_state::auxio_w(u8 data)
{
	LOGMASKED(LOG_AUXIO, "%s: auxio_w: %02x, drive_sel:%d tc:%d eject:%d LED:%d\n", machine().describe_context(), data, BIT(data, 3), BIT(data, 2), BIT(data, 1), BIT(data, 0));
	m_auxio = (m_auxio & 0xf0) | (data & 0x0f);
	if (!(m_auxio & AUXIO_DRIVE_SEL))
	{
		m_auxio &= ~(AUXIO_DENSITY | AUXIO_DISK_CHG);
	}
	else
	{
		m_auxio |= AUXIO_DISK_CHG; // Report no disk inserted
		m_fdc->tc_w(data & AUXIO_TC);
	}
}

u8 sun4_base_state::irq_r()
{
	LOGMASKED(LOG_IRQ_READS, "%s: irq_r: %02x\n", machine().describe_context(), m_irq_reg);
	return m_irq_reg;
}

void sun4_base_state::irq_w(u8 data)
{
	const u8 old_irq = m_irq_reg;
	m_irq_reg = data;
	const u8 changed = old_irq ^ data;

	LOGMASKED(LOG_IRQ_WRITES, "%s: irq_w = %02x\n", machine().describe_context(), data);

	if (!changed)
		return;

	if (BIT(changed, 0))
	{
		int enabled_state = BIT(data, 0) ? ASSERT_LINE : CLEAR_LINE;
		if (BIT(m_irq_reg, 7) && BIT(m_timer_count[1] | m_timer_limit[1], 31))
			m_maincpu->set_input_line(SPARC_IRQ14, enabled_state);
		if (BIT(m_irq_reg, 5) && BIT(m_timer_count[0] | m_timer_limit[0], 31))
			m_maincpu->set_input_line(SPARC_IRQ10, enabled_state);
		if (BIT(m_irq_reg, 3))
			m_maincpu->set_input_line(SPARC_IRQ6, enabled_state);
		if (BIT(m_irq_reg, 2))
			m_maincpu->set_input_line(SPARC_IRQ4, enabled_state);
		if (BIT(m_irq_reg, 1))
			m_maincpu->set_input_line(SPARC_IRQ1, enabled_state);
		m_maincpu->set_input_line(SPARC_IRQ12, ((m_scc_int[0] || m_scc_int[1]) && enabled_state) ? ASSERT_LINE : CLEAR_LINE);
	}
	else if (BIT(m_irq_reg, 0))
	{
		if (BIT(changed, 7) && BIT(m_timer_count[1] | m_timer_limit[1], 31))
			m_maincpu->set_input_line(SPARC_IRQ14, BIT(m_irq_reg, 7) ? ASSERT_LINE : CLEAR_LINE);
		if (BIT(changed, 5) && BIT(m_timer_count[0] | m_timer_limit[0], 31))
			m_maincpu->set_input_line(SPARC_IRQ10, BIT(m_irq_reg, 5) ? ASSERT_LINE : CLEAR_LINE);
		if (BIT(changed, 3))
			m_maincpu->set_input_line(SPARC_IRQ6, BIT(m_irq_reg, 3) ? ASSERT_LINE : CLEAR_LINE);
		if (BIT(changed, 2))
			m_maincpu->set_input_line(SPARC_IRQ4, BIT(m_irq_reg, 2) ? ASSERT_LINE : CLEAR_LINE);
		if (BIT(changed, 1))
			m_maincpu->set_input_line(SPARC_IRQ1, BIT(m_irq_reg, 1) ? ASSERT_LINE : CLEAR_LINE);
	}
}

template <int Chip>
void sun4_base_state::scc_int(int state)
{
	LOGMASKED(LOG_IRQ_WRITES, "scc_int<%d>: %d (%d)\n", Chip, state, m_scc_int[0] || m_scc_int[1]);
	m_scc_int[Chip] = state;

	m_maincpu->set_input_line(SPARC_IRQ12, (m_scc_int[0] || m_scc_int[1]) && (m_irq_reg & 0x01));
}

template <int Timer>
TIMER_CALLBACK_MEMBER(sun4_base_state::timer_tick)
{
	m_timer_count[Timer] += 1 << 10;
	if ((m_timer_count[Timer] & 0x7fffffff) == (m_timer_limit[Timer] & 0x7fffffff))
	{
		m_timer_count[Timer] = 0x80000000 | (1 << 10);
		m_timer_limit[Timer] |= 0x80000000;
		if ((m_irq_reg & TIMER_IRQ_MASKS[Timer]) == TIMER_IRQ_MASKS[Timer])
		{
			m_maincpu->set_input_line(TIMER_IRQ_LINES[Timer], ASSERT_LINE);
		}
	}
}

template <int Timer>
u32 sun4_base_state::timer_count_r()
{
	LOGMASKED(LOG_TIMER_COUNT_READS[Timer], "%s: timer_count_r<%d>: %08x\n", machine().describe_context(), Timer, m_timer_count[Timer]);
	return m_timer_count[Timer];
}

template <int Timer>
u32 sun4_base_state::timer_limit_r()
{
	LOGMASKED(LOG_TIMER_LIMIT_READS[Timer], "%s: timer_limit_r<%d>: %08x\n", machine().describe_context(), Timer, m_timer_limit[Timer]);
	m_timer_count[Timer] &= ~0x80000000;
	m_timer_limit[Timer] &= ~0x80000000;
	m_maincpu->set_input_line(TIMER_IRQ_LINES[Timer], CLEAR_LINE);
	return m_timer_limit[Timer];
}

template <int Timer>
void sun4_base_state::timer_count_w(offs_t offset, u32 data, u32 mem_mask)
{
	COMBINE_DATA(&m_timer_count[Timer]);
	LOGMASKED(LOG_TIMER_COUNT_WRITES[Timer], "%s: timer_count_w<%d>: %08x & %08x\n", machine().describe_context(), Timer, data, mem_mask);
}

template <int Timer>
void sun4_base_state::timer_limit_w(offs_t offset, u32 data, u32 mem_mask)
{
	COMBINE_DATA(&m_timer_limit[Timer]);
	LOGMASKED(LOG_TIMER_LIMIT_WRITES[Timer], "%s: timer_limit_w<%d>: %08x & %08x\n", machine().describe_context(), Timer, data, mem_mask);
	m_timer_count[Timer] = 1 << 10;
}

void sun4_base_state::dma_check_interrupts()
{
	const bool tc_interrupt = (m_dma_ctrl & DMA_TC) && !m_dma_tc_read;

	m_dma_ctrl &= ~DMA_INT_PEND;
	if (tc_interrupt || m_scsi_irq)
		m_dma_ctrl |= DMA_INT_PEND;

	const bool irq_or_err_pending = (m_dma_ctrl & (DMA_INT_PEND | DMA_ERR_PEND));
	const bool irq_enabled = (m_dma_ctrl & DMA_INT_EN);
	const bool old_irq = m_dma_irq;
	m_dma_irq = irq_or_err_pending && irq_enabled;
	if (old_irq != m_dma_irq)
	{
		LOGMASKED(LOG_DMA_IRQS, "dma_check_interrupts: Setting m_dma_irq to %d (irq_or_err_pending %d, irq_enabled %d)\n", m_dma_irq, irq_or_err_pending, irq_enabled);
		m_maincpu->set_input_line(SPARC_IRQ3, m_dma_irq);
	}
}

void sun4_base_state::dma_transfer_write()
{
	LOGMASKED(LOG_DMA_WRITES, "dma_transfer_write: Transferring from device to RAM\n");

	u8 pack_cnt = (m_dma_ctrl & DMA_PACK_CNT) >> DMA_PACK_CNT_SHIFT;
	while (m_dma_ctrl & DMA_REQ_PEND)
	{
		const u32 bit_index = (3 - pack_cnt) * 8;
		const u8 dma_value = m_scsi->dma_r();
		LOGMASKED(LOG_DMA_WRITE_DATA, "dma_transfer_write: %02x from device (pack_cnt %d)\n", dma_value, pack_cnt);
		m_dma_pack_register |= dma_value << bit_index;

		if (m_dma_ctrl & DMA_EN_CNT)
			m_dma_count--;

		pack_cnt++;
		if (pack_cnt == 4)
		{
			m_mmu->insn_data_w<sun4c_mmu_device::SUPER_DATA>(m_dma_addr >> 2, m_dma_pack_register, ~0);
			pack_cnt = 0;
			m_dma_pack_register = 0;
			m_dma_addr += 4;
		}
	}

	m_dma_ctrl &= ~DMA_PACK_CNT;
	m_dma_ctrl |= pack_cnt << DMA_PACK_CNT_SHIFT;
}

void sun4_base_state::dma_transfer_read()
{
	LOGMASKED(LOG_DMA_READS, "dma_transfer_read: Transferring from RAM to device\n");

	bool word_cached = false;
	u32 current_word = 0;
	while (m_dma_ctrl & DMA_REQ_PEND)
	{
		if (!word_cached)
		{
			current_word = m_mmu->insn_data_r<sun4c_mmu_device::SUPER_DATA>(m_dma_addr >> 2, ~0);
			word_cached = true;
			LOGMASKED(LOG_DMA_READ_DATA, "dma_transfer_read: Current word: %08x\n", current_word);
		}

		const u32 bit_index = (3 - (m_dma_addr & 3)) * 8;
		const u8 dma_value = current_word >> bit_index;
		LOGMASKED(LOG_DMA_READ_DATA, "dma_transfer_read: %02x to device\n", dma_value);
		m_scsi->dma_w(dma_value);

		if ((m_dma_addr & 3) == 3)
			word_cached = false;

		m_dma_addr++;
		if (m_dma_ctrl & DMA_EN_CNT)
			m_dma_count--;
	}
}

void sun4_base_state::dma_transfer()
{
	if (m_dma_ctrl & DMA_WRITE)
		dma_transfer_write();
	else
		dma_transfer_read();

	if (m_dma_count == 0 && (m_dma_ctrl & DMA_EN_CNT))
	{
		m_dma_ctrl |= DMA_TC;
		m_dma_tc_read = false;
		dma_check_interrupts();
	}
}

u32 sun4_base_state::dma_ctrl_r()
{
	if (m_dma_ctrl & DMA_TC)
	{
		m_dma_tc_read = true;
		dma_check_interrupts();
	}
	LOGMASKED(LOG_DMA_CTRL_READS, "%s: dma_ctrl_r: %08x\n", machine().describe_context(), m_dma_ctrl);
	return (m_dma_ctrl & ~(DMA_WRITE_ONLY | DMA_BYTE_ADDR)) | DMA_DEV_ID;
}

u32 sun4_base_state::dma_addr_r()
{
	LOGMASKED(LOG_DMA_CTRL_READS, "%s: dma_addr_r: %08x\n", machine().describe_context(), m_dma_addr);
	return m_dma_addr;
}

u32 sun4_base_state::dma_count_r()
{
	LOGMASKED(LOG_DMA_CTRL_READS, "%s: dma_count_r: %08x\n", machine().describe_context(), m_dma_count);
	return m_dma_count;
}

void sun4_base_state::dma_ctrl_w(offs_t offset, u32 data, u32 mem_mask)
{
	LOGMASKED(LOG_DMA_CTRL_WRITES, "%s: dma_ctrl_w = %08x & %08x\n", machine().describe_context(), data, mem_mask);

	// clear write-only bits
	mem_mask &= DMA_READ_WRITE;
	COMBINE_DATA(&m_dma_ctrl);

	if (data & DMA_RESET)
	{
		LOGMASKED(LOG_DMA_CTRL_WRITES, "%s:        Reset\n", machine().describe_context());
		m_dma_ctrl &= ~(DMA_ERR_PEND | DMA_PACK_CNT | DMA_INT_EN | DMA_FLUSH | DMA_DRAIN | DMA_WRITE | DMA_EN_DMA | DMA_REQ_PEND | DMA_EN_CNT | DMA_TC);
	}
	else if (data & DMA_FLUSH)
	{
		LOGMASKED(LOG_DMA_CTRL_WRITES, "%s:        Flush\n", machine().describe_context());
		m_dma_ctrl &= ~(DMA_PACK_CNT | DMA_ERR_PEND | DMA_TC);

		dma_check_interrupts();
	}
	else if (data & DMA_DRAIN)
	{
		constexpr u32 DRAIN_MASKS[4] = { 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff };
		LOGMASKED(LOG_DMA_CTRL_WRITES, "%s:        Drain\n", machine().describe_context());
		m_dma_ctrl &= ~DMA_DRAIN;
		const u8 pack_cnt = (m_dma_ctrl & DMA_PACK_CNT) >> DMA_PACK_CNT_SHIFT;
		for (u8 i = 0; i < pack_cnt; i++)
		{
			LOGMASKED(LOG_DMA_DRAINS, "%s:        Draining %02x to %02x\n", machine().describe_context(), (u8)(m_dma_pack_register >> ((3 - i) * 8)), m_dma_addr >> 2);
			m_mmu->insn_data_w<sun4c_mmu_device::SUPER_DATA>(m_dma_addr >> 2, m_dma_pack_register, DRAIN_MASKS[i]);
			m_dma_addr++;
		}
		m_dma_pack_register = 0;

		m_dma_ctrl &= ~DMA_PACK_CNT;
	}

	if (data & DMA_EN_DMA && (m_dma_ctrl & DMA_REQ_PEND))
		dma_transfer();
}

void sun4_base_state::dma_addr_w(offs_t offset, u32 data, u32 mem_mask)
{
	LOGMASKED(LOG_DMA_CTRL_WRITES, "%s: dma_addr_w = %08x & %08x\n", machine().describe_context(), data, mem_mask);
	m_dma_addr = data;
}

void sun4_base_state::dma_count_w(offs_t offset, u32 data, u32 mem_mask)
{
	LOGMASKED(LOG_DMA_CTRL_WRITES, "%s: dma_count_w = %08x & %08x\n", machine().describe_context(), data, mem_mask);
	m_dma_count = data;
}

void sun4_base_state::scsi_irq(int state)
{
	const bool old_irq = m_scsi_irq;
	m_scsi_irq = state;
	if (m_scsi_irq != old_irq)
	{
		LOGMASKED(LOG_SCSI_IRQ, "scsi_irq %d, checking interrupts\n", state);
		dma_check_interrupts();
	}
}

void sun4_base_state::scsi_drq(int state)
{
	LOGMASKED(LOG_SCSI_DRQ, "scsi_drq %d\n", state);
	m_dma_ctrl &= ~DMA_REQ_PEND;
	if (state)
	{
		LOGMASKED(LOG_SCSI_DRQ, "    DMA pending\n");
		m_dma_ctrl |= DMA_REQ_PEND;
		if (m_dma_ctrl & DMA_EN_DMA)
		{
			LOGMASKED(LOG_SCSI_DRQ, "    DMA enabled, starting DMA\n");
			dma_transfer();
		}
	}
}

void sun4_base_state::fdc_irq(int state)
{
	const bool old_irq = m_fdc_irq;
	m_fdc_irq = state;
	if (old_irq != m_fdc_irq)
	{
		LOGMASKED(LOG_FDC_IRQ, "fdc_irq %d\n", state);
		m_maincpu->set_input_line(SPARC_IRQ11, state ? ASSERT_LINE : CLEAR_LINE);
	}
}

template <int Line>
void sun4c_state::sbus_irq_w(int state)
{
	m_maincpu->set_input_line(Line, state);
}

static void sun_floppies(device_slot_interface &device)
{
	device.option_add("35hd", FLOPPY_35_HD);
}

static void sun4_cdrom(device_t *device)
{
	downcast<nscsi_cdrom_device &>(*device).set_block_size(512);
}

static void sun_scsi_devices(device_slot_interface &device)
{
	device.option_add("cdrom", NSCSI_CDROM);
	device.option_add("harddisk", NSCSI_HARDDISK);
	device.option_add("tape", NSCSI_TAPE);
	device.option_add_internal("ncr53c90", NCR53C90);
	device.set_option_machine_config("cdrom", sun4_cdrom);
}

void sun4_base_state::ncr53c90(device_t *device)
{
	ncr53c90_device &adapter = downcast<ncr53c90_device &>(*device);

	adapter.set_clock(10000000);
	adapter.irq_handler_cb().set(*this, FUNC(sun4_base_state::scsi_irq));
	adapter.drq_handler_cb().set(*this, FUNC(sun4_base_state::scsi_drq));
}

void sun4_base_state::sun4_base(machine_config &config)
{
	RAM(config, m_ram).set_default_size("16M").set_default_value(0x00);
	m_ram->set_extra_options("4M,8M,12M,16M,20M,24M,28M,32M,36M,40M,48M,52M,64M");

	M48T02(config, m_timekpr, 0);

	N82077AA(config, m_fdc, 24_MHz_XTAL);
	m_fdc->set_ready_line_connected(false);
	m_fdc->intrq_wr_callback().set(FUNC(sun4_base_state::fdc_irq));
	FLOPPY_CONNECTOR(config, m_floppy, sun_floppies, "35hd", floppy_image_device::default_pc_floppy_formats);

	// Ethernet
	AM79C90(config, m_lance);
	m_lance->dma_in().set([this](offs_t offset)
	{
		u32 const data = m_mmu->insn_data_r<sun4c_mmu_device::SUPER_DATA>((0xff000000U | offset) >> 2, 0xffffffffU);

		return (offset & 2) ? u16(data) : u16(data >> 16);
	});
	m_lance->dma_out().set([this](offs_t offset, u16 data, u16 mem_mask)
	{
		if (offset & 2)
			m_mmu->insn_data_w<sun4c_mmu_device::SUPER_DATA>((0xff000000U | offset) >> 2, data, mem_mask);
		else
			m_mmu->insn_data_w<sun4c_mmu_device::SUPER_DATA>((0xff000000U | offset) >> 2, u32(data) << 16, u32(mem_mask) << 16);
	});

	// Keyboard/mouse
	SCC85C30(config, m_scc[0], 4.9152_MHz_XTAL);
	m_scc[0]->out_int_callback().set(FUNC(sun4_base_state::scc_int<0>));
	m_scc[0]->out_txda_callback().set(m_keyboard, FUNC(sun_keyboard_port_device::write_txd));
	// no mouse TxD connection - replaced with soft power request input

	SUNKBD_PORT(config, m_keyboard, default_sun_keyboard_devices, "type5hle").rxd_handler().set(m_scc[0], FUNC(z80scc_device::rxa_w));
	SUNMOUSE_PORT(config, m_mouse, default_sun_mouse_devices, "hle1200").rxd_handler().set(m_scc[0], FUNC(z80scc_device::rxb_w));

	// RS232 serial ports
	SCC85C30(config, m_scc[1], 4.9152_MHz_XTAL);
	m_scc[1]->out_int_callback().set(FUNC(sun4_base_state::scc_int<1>));
	m_scc[1]->out_txda_callback().set(m_rs232[0], FUNC(rs232_port_device::write_txd));
	m_scc[1]->out_txdb_callback().set(m_rs232[1], FUNC(rs232_port_device::write_txd));

	RS232_PORT(config, m_rs232[0], default_rs232_devices, nullptr);
	m_rs232[0]->rxd_handler().set(m_scc[1], FUNC(z80scc_device::rxa_w));
	m_rs232[0]->dcd_handler().set(m_scc[1], FUNC(z80scc_device::dcda_w));
	m_rs232[0]->cts_handler().set(m_scc[1], FUNC(z80scc_device::ctsa_w));

	RS232_PORT(config, m_rs232[1], default_rs232_devices, nullptr);
	m_rs232[1]->rxd_handler().set(m_scc[1], FUNC(z80scc_device::rxb_w));
	m_rs232[1]->dcd_handler().set(m_scc[1], FUNC(z80scc_device::dcdb_w));
	m_rs232[1]->cts_handler().set(m_scc[1], FUNC(z80scc_device::ctsb_w));

	NSCSI_BUS(config, "scsibus");
	NSCSI_CONNECTOR(config, "scsibus:0", sun_scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsibus:1", sun_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:2", sun_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:3", sun_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:4", sun_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:5", sun_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsibus:6", sun_scsi_devices, "cdrom");
	NSCSI_CONNECTOR(config, "scsibus:7", sun_scsi_devices, "ncr53c90", true).set_option_machine_config("ncr53c90", [this] (device_t *device) { ncr53c90(device); });
}

void sun4_state::sun4(machine_config &config)
{
	/* basic machine hardware */
	SPARCV7(config, m_maincpu, 16'670'000);
	m_maincpu->add_asi_desc([](sparc_disassembler *dasm) { dasm->add_asi_desc(sun4_asi_desc); });
	m_maincpu->set_addrmap(0, &sun4_state::debugger_map);

	sun4_base(config);

	// add a tape drive at SCSI ID 4
	subdevice<nscsi_connector>("scsibus:4")->set_default_option("tape");

	// MMU Type 1 device space
	ADDRESS_MAP_BANK(config, m_type1space).set_map(&sun4_state::type1space_map).set_options(ENDIANNESS_BIG, 32, 32, 0x80000000);

	SUN4_MMU(config, m_mmu, 25'000'000);
	m_mmu->type1_r().set(m_type1space, FUNC(address_map_bank_device::read32));
	m_mmu->type1_w().set(m_type1space, FUNC(address_map_bank_device::write32));
	m_mmu->set_cpu(m_maincpu);
	m_mmu->set_ram(m_ram);
	m_mmu->set_rom("user1");
	m_mmu->set_scc(m_scc[1]);
	m_maincpu->set_mmu(m_mmu);
}

void sun4c_state::sun4c(machine_config &config)
{
	/* basic machine hardware */
	SPARCV7(config, m_maincpu, 20'000'000);
	m_maincpu->add_asi_desc([](sparc_disassembler *dasm) { dasm->add_asi_desc(sun4c_asi_desc); });
	m_maincpu->set_addrmap(0x00, &sun4c_state::debugger_map);
	m_maincpu->set_addrmap(0x12, &sun4c_state::system_asi_map);
	m_maincpu->set_addrmap(0x13, &sun4c_state::segment_asi_map);
	m_maincpu->set_addrmap(0x14, &sun4c_state::page_asi_map);
	m_maincpu->set_addrmap(0x15, &sun4c_state::hw_segment_flush_asi_map);
	m_maincpu->set_addrmap(0x16, &sun4c_state::hw_page_flush_asi_map);
	m_maincpu->set_addrmap(0x17, &sun4c_state::hw_context_flush_asi_map);
	m_maincpu->set_addrmap(0x18, &sun4c_state::user_insn_asi_map);
	m_maincpu->set_addrmap(0x19, &sun4c_state::super_insn_asi_map);
	m_maincpu->set_addrmap(0x1a, &sun4c_state::user_data_asi_map);
	m_maincpu->set_addrmap(0x1b, &sun4c_state::super_data_asi_map);
	m_maincpu->set_addrmap(0x1c, &sun4c_state::sw_segment_flush_asi_map);
	m_maincpu->set_addrmap(0x1d, &sun4c_state::sw_page_flush_asi_map);
	m_maincpu->set_addrmap(0x1e, &sun4c_state::sw_context_flush_asi_map);
	m_maincpu->set_addrmap(0x1f, &sun4c_state::hw_flush_all_asi_map);

	sun4_base(config);

	// MMU Type 1 device space
	ADDRESS_MAP_BANK(config, m_type1space).set_map(&sun4c_state::type1space_map).set_options(ENDIANNESS_BIG, 32, 32, 0x80000000);

	SUN4C_MMU(config, m_mmu, 20'000'000);
	m_mmu->type1_r().set(m_type1space, FUNC(address_map_bank_device::read32));
	m_mmu->type1_w().set(m_type1space, FUNC(address_map_bank_device::write32));
	m_mmu->set_cpu(m_maincpu);
	m_mmu->set_ram(m_ram);
	m_mmu->set_rom("user1");
	m_mmu->set_scc(m_scc[1]);
	m_mmu->set_cache_line_size(16);
	m_maincpu->set_mmu(m_mmu);

	// SBus
	SBUS(config, m_sbus, 20'000'000, m_maincpu, m_type1space, 0);
	m_sbus->irq<0>().set(FUNC(sun4c_state::sbus_irq_w<SPARC_IRQ1>));
	m_sbus->irq<1>().set(FUNC(sun4c_state::sbus_irq_w<SPARC_IRQ2>));
	m_sbus->irq<2>().set(FUNC(sun4c_state::sbus_irq_w<SPARC_IRQ3>));
	m_sbus->irq<3>().set(FUNC(sun4c_state::sbus_irq_w<SPARC_IRQ5>));
	m_sbus->irq<4>().set(FUNC(sun4c_state::sbus_irq_w<SPARC_IRQ7>));
	m_sbus->irq<5>().set(FUNC(sun4c_state::sbus_irq_w<SPARC_IRQ8>));
	m_sbus->irq<6>().set(FUNC(sun4c_state::sbus_irq_w<SPARC_IRQ9>));
	SBUS_SLOT(config, m_sbus_slot[0], 20'000'000, m_sbus, 0, sbus_cards, nullptr);
	SBUS_SLOT(config, m_sbus_slot[1], 20'000'000, m_sbus, 1, sbus_cards, nullptr);
	SBUS_SLOT(config, m_sbus_slot[2], 20'000'000, m_sbus, 2, sbus_cards, nullptr);
}

void sun4c_state::sun4_20(machine_config &config)
{
	sun4c(config);

	m_ram->set_extra_options("4M,8M,12M,16M");

	m_sbus_slot[0]->set_fixed(true);
	m_sbus_slot[1]->set_fixed(true);
	m_sbus_slot[2]->set_default_option("bwtwo");
	m_sbus_slot[2]->set_fixed(true);
}

void sun4c_state::sun4_25(machine_config& config)
{
	sun4c(config);

	m_ram->set_extra_options("16M,32M,48M,64M");

	//m_mmu->set_ctx_mask(0xf);
	m_mmu->set_pmeg_mask(0xff);
	m_mmu->set_cache_line_size(32);
	m_mmu->set_clock(33'000'000);
	m_maincpu->set_clock(33'000'000);

	m_ram->set_default_size("64M");

	m_sbus->set_clock(25'000'000);
	m_sbus_slot[0]->set_clock(25'000'000);
	m_sbus_slot[1]->set_clock(25'000'000);
	m_sbus_slot[2]->set_clock(25'000'000);

	m_sbus_slot[0]->set_fixed(true);
	m_sbus_slot[1]->set_fixed(true);
	m_sbus_slot[2]->set_default_option("bwtwo");
	m_sbus_slot[2]->set_fixed(true);
}

void sun4c_state::sun4_40(machine_config &config)
{
	sun4c(config);

	m_ram->set_extra_options("4M,8M,12M,16M,20M,24M,32M,36M,48M");

	m_mmu->set_clock(25'000'000);
	m_maincpu->set_clock(25'000'000);

	m_sbus->set_clock(25'000'000);
	m_sbus_slot[0]->set_clock(25'000'000);
	m_sbus_slot[1]->set_clock(25'000'000);
	m_sbus_slot[2]->set_clock(25'000'000);
	m_sbus_slot[2]->set_default_option("bwtwo");
	m_sbus_slot[2]->set_fixed(true);
}

void sun4c_state::sun4_50(machine_config &config)
{
	sun4c(config);

	m_mmu->set_ctx_mask(0xf);
	m_mmu->set_pmeg_mask(0xff);
	m_mmu->set_cache_line_size(32);

	m_mmu->set_clock(40'000'000);
	m_maincpu->set_clock(40'000'000);

	m_sbus->set_clock(20'000'000);
	m_sbus_slot[0]->set_clock(20'000'000);
	m_sbus_slot[1]->set_clock(20'000'000);
	m_sbus_slot[2]->set_clock(20'000'000);
	m_sbus_slot[2]->set_default_option("turbogx"); // not accurate, should be gxp, not turbogx
	m_sbus_slot[2]->set_fixed(true);
}

void sun4c_state::sun4_60(machine_config &config)
{
	sun4c(config);
}

void sun4c_state::sun4_65(machine_config &config)
{
	sun4c(config);

	m_mmu->set_clock(25'000'000);
	m_maincpu->set_clock(25'000'000);

	m_sbus->set_clock(25'000'000);
	m_sbus_slot[0]->set_clock(25'000'000);
	m_sbus_slot[1]->set_clock(25'000'000);
	m_sbus_slot[2]->set_clock(25'000'000);
	m_sbus_slot[2]->set_default_option("bwtwo");
}

void sun4c_state::sun4_75(machine_config &config)
{
	sun4c(config);

	m_mmu->set_ctx_mask(0xf);
	m_mmu->set_pmeg_mask(0xff);
	m_mmu->set_cache_line_size(32);

	m_mmu->set_clock(40'000'000);
	m_maincpu->set_clock(40'000'000);
}

/*
Boot PROM

Sun-4c Architecture

SPARCstation SLC (Sun-4/20) - 128K x 8
U1001       Revision
========================================
520-2748-01 1.2 Version 3
520-2748-02 1.3
520-2748-03 1.3
520-2748-04 1.4 Version 2
595-2250-xx Sun-4/20 Boot PROM Kit

SPARCstation ELC (Sun-4/25) - 256K x 8
U0806       Revision
========================================
520-3085-01
520-3085-02 2.3 Version 95
520-3085-03 2.4 Version 96
520-3085-04 2.6 Version 102 (not used)
520-3085-04 2.9 Version 7


SPARCstation IPC (Sun-4/40) - 256K x 8
U0902       Revision
========================================
525-1085-01
525-1085-02
525-1085-03 1.6 Version 151
525-1191-01 1.7 Version 3 and 2.4 Version 362
525-1191-02 1.7 Version 3 and 2.6 Version 411
525-1191-03 1.7 Version 3 and 2.9 Version 24


SPARCstation IPX (Sun-4/50) - 256K x 8
U0501       Revision
========================================
525-1177-01 2.1 Version 66
525-1177-02 2.2 Version 134
525-1177-03 2.3 Version 263
525-1177-04 2.4 Version 347
525-1177-05 2.6 Version 410
525-1177-06 2.9 Version 20


SPARCstation 1 (Sun-4/60) - 128K x 8
U0837       Revision
========================================
525-1043-01
525-1043-02 0.1
525-1043-03
525-1043-04 1.0
525-1043-05 1.0
525-1043-06 1.0
525-1043-07 1.1
525-1043-08 1.3 Version 3
595-1963-xx Sun-4/60 Boot PROM Kit
525-1207-01 2.4 Version 95
525-1207-02 2.9 Version 9 (2.x is only available from the spare parts price list)
560-1805-xx Sun-4/60 2.4 Boot PROM Kit


SPARCstation 1+ (Sun-4/65) - 128K x 8
U0837 Revision
========================================
525-1108-01
525-1108-02
525-1108-03 1.1 Version 13
525-1108-04 1.2
525-1108-05 1.3 Version 4
525-1208-01 2.4 Version 116
525-1208-02 2.9 Version 9 (2.x is only available from the spare parts price list)
560-1806-xx Sun-4/65 2.4 Boot PROM Kit


SPARCstation 2 (Sun-4/75) - 256K x 8
U0501       Revision
========================================
525-1107-01 2.0Beta0
525-1107-02 2.0Beta1
525-1107-03 2.0
525-1107-04 2.0 Version 865 (fails with Weitek Power ?p)
525-1107-05 2.1 Version 931 (fails with Weitek Power ?p)
525-1107-06 2.2 Version 947
525-1107-07 2.4 Version 990
525-1107-08 2.4.1 Version 991
525-1107-09 2.6 Version 1118
525-1107-10 2.9 Version 16
595-2249-xx Sun-4/75 Boot PROM Kit

*/

ROM_START( sun4_110 )
	ROM_REGION32_BE( 0x80000, "user1", ROMREGION_ERASEFF )
	ROM_LOAD32_BYTE( "520-1651-09_2.8.1.bin", 0x000003, 0x010000, CRC(9b439222) SHA1(b3589f65478e53338aee6355567484421a913d00) )
	ROM_LOAD32_BYTE( "520-1652-09_2.8.1.bin", 0x000002, 0x010000, CRC(2bed25ec) SHA1(a9ff6c94ec8e0d6b084a300ff7bd8f2126c7a3b1) )
	ROM_LOAD32_BYTE( "520-1653-09_2.8.1.bin", 0x000001, 0x010000, CRC(d44b7f76) SHA1(2acea449d7782a10fda7f6529279a7e1882549e3) )
	ROM_LOAD32_BYTE( "520-1654-09_2.8.1.bin", 0x000000, 0x010000, CRC(1bef8469) SHA1(d5a89d29df7ffc01b305cd12d0b6eb77e126dcbf) )
ROM_END

// Sun 4/300, Cypress Semiconductor CY7C601, Texas Instruments 8847 FPU
ROM_START( sun4_300 )
	ROM_REGION32_BE( 0x80000, "user1", ROMREGION_ERASEFF )
	ROM_LOAD32_BYTE( "1035-09.rom", 0x00003, 0x10000, CRC(4ae2f2ad) SHA1(9c17a80b3ce3efdf18b5eca969f1565ddaad3116))
	ROM_LOAD32_BYTE( "1036-09.rom", 0x00000, 0x10000, CRC(cb3d45a7) SHA1(9d5da09ff87ec52dc99ffabd1003d30811eafdb0))
	ROM_LOAD32_BYTE( "1037-09.rom", 0x00001, 0x10000, CRC(4f005bea) SHA1(db3f6133ea7c497ba440bc797123dde41abea6fd))
	ROM_LOAD32_BYTE( "1038-09.rom", 0x00002, 0x10000, CRC(1e429d31) SHA1(498ce4d34a74ea6e3e369bb7eb9c2b87e12bd080))
ROM_END

ROM_START( sun4_400 )
	ROM_REGION32_BE( 0x80000, "user1", ROMREGION_ERASEFF )
	ROM_LOAD32_BYTE( "525-1103-06_4.1.1.bin", 0x000000, 0x010000, CRC(c129c0a8) SHA1(4ecd51fb924e65f773a09cae35ce16b1744bd7b9) )
	ROM_LOAD32_BYTE( "525-1104-06_4.1.1.bin", 0x000001, 0x010000, CRC(fe3a95fc) SHA1(c3ebb89eb07d421ed4f3d7e1a66eb286f5a743e9) )
	ROM_LOAD32_BYTE( "525-1105-06_4.1.1.bin", 0x000002, 0x010000, CRC(0dc3564f) SHA1(c86e640be0ef14636a4de065ab73b5671501c555) )
	ROM_LOAD32_BYTE( "525-1106-06_4.1.1.bin", 0x000003, 0x010000, CRC(4464a98b) SHA1(41fd033296904476b53dfe7513eb8da403d7acd4) )
ROM_END

// SPARCstation IPC (Sun 4/40)
/* SCC init 1 for the keyboard is identical to Sun 4/75 init 3 */
ROM_START( sun4_40 )
	ROM_REGION32_BE( 0x80000, "user1", ROMREGION_ERASEFF )
	ROM_LOAD( "4.40_v2.9.rom", 0x0000, 0x40000, CRC(532fc20d) SHA1(d86d9e958017b3fecdf510d728a3e46a0ce3281d))
ROM_END

// SPARCstation IPX (Sun 4/50)
/* SCC init 1-2 for the keyboard is identical to Sun 4/75 init 1-2 */
ROM_START( sun4_50 )
	ROM_REGION32_BE( 0x80000, "user1", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v29", "V2.9" )
	ROMX_LOAD( "ipx-29.h1.u0501", 0x0000, 0x40000, CRC(1910aa65) SHA1(7d8832fea8e299b89e6ec7137fcde497673c14f8), ROM_BIOS(0)) // 525-1177-06(?) Boot (Version 2.9 version 20, supposedly?)
	ROM_SYSTEM_BIOS( 1, "v26", "V2.6" )
	ROMX_LOAD( "525-1177-05__=c=_sun_1992.am27c020.h1.u0501", 0x0000, 0x40000, CRC(aad28dee) SHA1(18075afa479fdc8d318df9aef9847dfb20591d79), ROM_BIOS(1)) // 525-1177-05 Boot (Version 2.6 version 410, supposedly?)
	ROM_SYSTEM_BIOS( 2, "v23", "V2.3" )
	ROMX_LOAD( "525-1177-03.h1.u0501", 0x0000, 0x40000, CRC(dcc1e66c) SHA1(a4dc3d8631aaa8416e22de273707c4ed7a2fe561), ROM_BIOS(2)) // 525-1177-03 Boot (Version 2.3)
ROM_END

// SPARCstation SLC (Sun 4/20)
/* SCC init 1 for the keyboard
 * :scc1 A Reg 09 <- 02 Master Interrupt Control - No Reset, No vector
 * :scc1 A Reg 04 <- 46 Setting up asynchronous frame format and clock, Parity Enable=0, Even Parity, Stop Bits 1, Clock Mode 16X
 * :scc1 A Reg 03 <- c0 Setting up the receiver, Receiver Enable 0, Auto Enables 0, Receiver Bits/Character 8
 * :scc1 A Reg 05 <- e2 Setting up the transmitter, Transmitter Enable 0, Transmitter Bits/Character 8, Send Break 0, RTS=1 DTR=1
 * :scc1 A Reg 0e <- 82 Misc Control Bits Baudrate Generator Input DPLL Command - not implemented
 * :scc1 A Reg 0b <- 55 Clock Mode Control 55 Clock type TTL level on RTxC pin, RCV CLK=BRG, TRA CLK=BRG, TRxC pin is Output, TRxC CLK=TRA CLK - not_implemented
 * :scc1 A Reg 0c <- 0e Low byte of Time Constant for Baudrate generator  -> 9600 baud
 * :scc1 A Reg 0d <- 00 High byte of Time Constant for Baudrate generator
 * :scc1 A Reg 03 <- c1 Setting up the receiver, Receiver Enable 1, Auto Enables 0, Receiver Bits/Character 8
 * :scc1 A Reg 05 <- ea Setting up the transmitter, Transmitter Enable 1, Transmitter Bits/Character 8, Send Break 0, RTS=1, DTR=1
 * :scc1 A Reg 0e <- 83 Misc Control Bits DPLL SRC=BRG Command - not implemented, BRG enabled SRC=PCLK, BRG SRC bps=38400=PCLK 4915200/128, BRG OUT 1200=38400/16
 * :scc1 A Reg 00 <- 10 Reset External/Status Interrupt
 * :scc1 A Reg 00 <- 01 Null command, register resetted by read of WR0
 * :scc1 A Reg 0c <- 0e Low byte of Time Constant for Baudrate generator  -> 9600 baud
 * :scc1 A Reg 00 <- 01 Null command, register resetted by read of WR0
 * :scc1 A Reg 0f <- c0 External/Status Control Bits, DCD Interrupt=1, Status FIFO enable=1, Zero detect interrupt:1 WR7 Prime enable:1 - not implemented
*/
ROM_START( sun4_20 )
	ROM_REGION32_BE( 0x80000, "user1", ROMREGION_ERASEFF )
	ROM_LOAD( "520-2748-04.rom", 0x0000, 0x20000, CRC(e85b3fd8) SHA1(4cbc088f589375e2d5983f481f7d4261a408702e))
ROM_END

// SPARCstation ELC (Sun 4/25)
ROM_START(sun4_25)
	ROM_REGION32_BE( 0x80000, "user1", ROMREGION_ERASEFF )
	ROM_LOAD( "520-3085-03.rom", 0x0000, 0x40000, CRC(faafaf0d) SHA1(23a1b78392883b06eff9f7828e955399b6daa3d6))
ROM_END

// SPARCstation 1 (Sun 4/60)
/* SCC init 1 for the keyboard is identical to Sun 4/75 init 3 */
ROM_START( sun4_60 )
	ROM_REGION32_BE( 0x80000, "user1", ROMREGION_ERASEFF )
	ROM_LOAD( "ss1v29.rom", 0x0000, 0x20000, CRC(e3f103a9) SHA1(5e95835f1090ea94859bd005757f0e7b5e86181b))
ROM_END

// SPARCstation 1+ (Sun 4/65)
ROM_START( sun4_65 )
	ROM_REGION32_BE( 0x80000, "user1", ROMREGION_ERASEFF )
	ROM_LOAD( "525-1108-05_1.3_ver_4.bin", 0x000000, 0x020000, CRC(67f1b3e2) SHA1(276ec5ca1dcbdfa202120560f55d52036720f87d) )
ROM_END

// SPARCstation 2 (Sun 4/75)
/* SCC init 1 for the keyboard
 *----------------------------
 * :scc1 A Reg 09 <- c0 Master Interrupt Control - Device reset  c0 A&B: RTS=1 DTR=1 INT=0
 * :scc1 int: 0
 * :scc1 A Reg 04 <- 46 Setting up asynchronous frame format and clock, Parity Enable=0, Even Parity, Stop Bits 1, Clock Mode 16X                                     * :scc1 A Reg 03 <- c0 Setting up the receiver, Receiver Enable 0, Auto Enables 0, Receiver Bits/Character 8
 * :scc1 A Reg 05 <- e2 Setting up the transmitter, Transmitter Enable 0, Transmitter Bits/Character 8, Send Break 0, RTS=1 DTR=1
 * :scc1 A Reg 09 <- 02 Master Interrupt Control - No reset  02 A&B: RTS=1 DTR=1 INT=0
 * :scc1 A Reg 0b <- 55 Clock Mode Control 55 Clock type TTL level on RTxC pin, RCV CLK=BRG, TRA CLK=BRG, TRxC pin is Output, TRxC CLK=TRA CLK - not_implemented
 * :scc1 A Reg 0c <- 7e Low byte of Time Constant for Baudrate generator
 * :scc1 A Reg 0d <- 00 High byte of Time Constant for Baudrate generator
 * :scc1 A Reg 0e <- 82 Misc Control Bits Baudrate Generator Input DPLL Command - not implemented
 * :scc1 A Reg 03 <- c1 Setting up the receiver, Receiver Enable 1, Auto Enables 0, Receiver Bits/Character 8
 * :scc1 A Reg 05 <- ea Setting up the transmitter, Transmitter Enable 1, Transmitter Bits/Character 8, Send Break 0, RTS=1, DTR=1
 * :scc1 A Reg 0e <- 83 Misc Control Bits DPLL SRC=BRG Command - not implemented, BRG enabled SRC=PCLK, BRG SRC bps=38400=PCLK 4915200/128, BRG OUT 1200=38400/16
 * :scc1 A Reg 00 <- 10 Reset External/Status Interrupt
 * :scc1 A Reg 00 <- 10 Reset External/Status Interrupt
 *
 * SCC init 2 for the keyboard - is Identical to init 1
 *
 * SCC init 3 for the keyboard - tricky one that reprogramms the baudrate constant as the last step.
 * -------------------------------------------------------------------------------------------------
 * :scc1 A Reg 09 <- 02 Master Interrupt Control - No Reset, No vector
 * :scc1 A Reg 04 <- 44 Setting up asynchronous frame format and clock, Parity Enable=0, Even Odd, Stop Bits 1, Clock Mode 16X
 * :scc1 A Reg 03 <- c0 Setting up the receiver, Receiver Enable 0, Auto Enables 0, Receiver Bits/Character 8
 * :scc1 A Reg 05 <- 60 Setting up the transmitter, Transmitter Enable 0, Transmitter Bits/Character 8, Send Break 0, RTS=0 DTR=0
 * :scc1 A Reg 0e <- 82 Misc Control Bits Baudrate Generator Input DPLL Command - not implemented
 * :scc1 A Reg 0b <- 55 Clock Mode Control 55 Clock type TTL level on RTxC pin, RCV CLK=BRG, TRA CLK=BRG, TRxC pin is Output, TRxC CLK=TRA CLK - not_implemented
 * :scc1 A Reg 0c <- 0e Low byte of Time Constant for Baudrate generator  -> 9600 baud
 * :scc1 A Reg 0d <- 00 High byte of Time Constant for Baudrate generator
 * :scc1 A Reg 03 <- c1 Setting up the receiver, Receiver Enable 1, Auto Enables 0, Receiver Bits/Character 8
 * :scc1 A Reg 05 <- 68 Setting up the transmitter, Transmitter Enable 1, Transmitter Bits/Character 8, Send Break 0, RTS=0, DTR=0
 * :scc1 A Reg 0e <- 83 Misc Control Bits DPLL SRC=BRG Command - not implemented, BRG enabled SRC=PCLK, BRG SRC bps=307200=PCLK 4915200/16, BRG OUT 9600=307200/16
 * :scc1 A Reg 00 <- 10 Reset External/Status Interrupt
 * :scc1 A Reg 00 <- 10 Reset External/Status Interrupt
 * :scc1 A Reg 0c <- 7e Low byte of Time Constant for Baudrate generator -> 1200 baud
*/
ROM_START( sun4_75 )
	ROM_REGION32_BE( 0x80000, "user1", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v29", "V2.9" )
	ROMX_LOAD( "ss2-29.rom", 0x0000, 0x40000, CRC(d04132b3) SHA1(ef26afafa2800b8e2e5e994b3a76ca17ce1314b1), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v22", "V2.2" )
	ROMX_LOAD( "525-1107-06.rom", 0x0000, 0x40000, CRC(7f5b58b4) SHA1(10a3eb3ddee667e7cf3c04aef6f6549e1b7f8311), ROM_BIOS(1) )
ROM_END

// SPARCstation 10 (Sun S10)
ROM_START( sun_s10 )
	ROM_REGION32_BE( 0x80000, "user1", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "r225", "Rev 2.2.5")
	ROMX_LOAD( "ss10_v2.25.rom", 0x0000, 0x80000, CRC(c7a48fd3) SHA1(db13d85b02f181eb7fce4c38b11996ff64116619), ROM_BIOS(0))
	// SPARCstation 10 and 20
	ROM_SYSTEM_BIOS(1, "r225r", "Rev 2.2.5r")
	ROMX_LOAD( "ss10-20_v2.25r.rom", 0x0000, 0x80000, CRC(105ba132) SHA1(58530e88369d1d26ab11475c7884205f2299d255), ROM_BIOS(1))
ROM_END

// SPARCstation 20
ROM_START( sun_s20 )
	ROM_REGION32_BE( 0x80000, "user1", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "r225", "Rev 2.2.5")
	ROMX_LOAD( "ss20_v2.25.rom", 0x0000, 0x80000, CRC(b4f5c547) SHA1(ee78312069522094950884d5bcb21f691eb6f31e), ROM_BIOS(0))
	// SPARCstation 10 and 20
	ROM_SYSTEM_BIOS(1, "r225r", "Rev 2.2.5r")
	ROMX_LOAD( "ss10-20_v2.25r.rom", 0x0000, 0x80000, CRC(105ba132) SHA1(58530e88369d1d26ab11475c7884205f2299d255), ROM_BIOS(1))
ROM_END

/* Drivers */

//    YEAR  NAME      PARENT    COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY             FULLNAME                       FLAGS
// sun4
COMP( 198?, sun4_110, 0,        0,      sun4,    sun4,  sun4_state,  empty_init, "Sun Microsystems", "Sun 4/110",                   MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1987, sun4_300, 0,        0,      sun4,    sun4,  sun4_state,  empty_init, "Sun Microsystems", "Sun 4/3x0",                   MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 198?, sun4_400, 0,        0,      sun4,    sun4,  sun4_state,  empty_init, "Sun Microsystems", "Sun 4/4x0",                   MACHINE_NOT_WORKING | MACHINE_NO_SOUND )

// sun4c
COMP( 1990, sun4_40,  sun4_300, 0,      sun4_40, sun4,  sun4c_state, empty_init, "Sun Microsystems", "SPARCstation IPC (Sun 4/40)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )
COMP( 1991, sun4_50,  sun4_300, 0,      sun4_50, sun4,  sun4c_state, empty_init, "Sun Microsystems", "SPARCstation IPX (Sun 4/50)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 199?, sun4_20,  sun4_300, 0,      sun4_20, sun4,  sun4c_state, empty_init, "Sun Microsystems", "SPARCstation SLC (Sun 4/20)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1992, sun4_25,  sun4_300, 0,      sun4_25, sun4,  sun4c_state, empty_init, "Sun Microsystems", "SPARCstation ELC (Sun 4/25)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1989, sun4_60,  sun4_300, 0,      sun4_60, sun4,  sun4c_state, empty_init, "Sun Microsystems", "SPARCstation 1 (Sun 4/60)",   MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )
COMP( 1990, sun4_65,  sun4_300, 0,      sun4_65, sun4,  sun4c_state, empty_init, "Sun Microsystems", "SPARCstation 1+ (Sun 4/65)",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1990, sun4_75,  sun4_300, 0,      sun4_75, sun4,  sun4c_state, empty_init, "Sun Microsystems", "SPARCstation 2 (Sun 4/75)",   MACHINE_NOT_WORKING | MACHINE_NO_SOUND )

// sun4m (using the SPARC "reference MMU", probably will go to a separate driver)
COMP( 1992, sun_s10,  sun4_300, 0,      sun4c,   sun4,  sun4c_state, empty_init, "Sun Microsystems", "SPARCstation 10 (Sun S10)",   MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1994, sun_s20,  sun4_300, 0,      sun4c,   sun4,  sun4c_state, empty_init, "Sun Microsystems", "SPARCstation 20",             MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



super6.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

TODO:
- peripheral interfaces

- Fix floppy. It needs to WAIT the cpu whenever port 0x14 is read, wait
  for either DRQ or INTRQ to assert, then release the cpu and then do the
  actual port read. But it doesn't work properly at the moment. It gets stuck
  if you load up the cpm disk (from software list). The other disks are useless.

  The schematic isn't clear, but it seems the 2 halves of U16 (as shown) have
  a common element, so that activity on one side can affect what happens on
  the other side.

*/

#include "emu.h"
#include "bus/rs232/rs232.h"
//#include "bus/s100/s100.h"
#include "super6.h"
#include "softlist_dev.h"

//**************************************************************************
//  MEMORY BANKING
//**************************************************************************

//-------------------------------------------------
//  bankswitch -
//-------------------------------------------------

void super6_state::bankswitch()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	uint8_t *ram = m_ram->pointer();

	// power on jump
	if (!BIT(m_bank0, 6)) { program.install_rom(0x0000, 0x07ff, 0xf800, m_rom); return; }

	// first 64KB of memory
	program.install_ram(0x0000, 0xffff, ram);

	// second 64KB of memory
	int map = (m_bank1 >> 4) & 0x07;

	switch (map)
	{
	case 0:
		if (BIT(m_bank1, 0)) program.install_ram(0x0000, 0x3fff, ram + 0x10000);
		if (BIT(m_bank1, 1)) program.install_ram(0x4000, 0x7fff, ram + 0x14000);
		if (BIT(m_bank1, 2)) program.install_ram(0x8000, 0xbfff, ram + 0x18000);
		if (BIT(m_bank1, 3)) program.install_ram(0xc000, 0xffff, ram + 0x1c000);
		break;

	case 1:
		if (BIT(m_bank1, 0)) program.install_ram(0x0000, 0x3fff, ram + 0x10000);
		if (BIT(m_bank1, 1)) program.install_ram(0x4000, 0x7fff, ram + 0x14000);
		if (BIT(m_bank1, 2)) program.install_ram(0x8000, 0xbfff, ram + 0x18000);
		if (BIT(m_bank1, 3)) program.install_ram(0xc000, 0xffff, ram + 0x0000);
		break;

	case 2:
		if (BIT(m_bank1, 0)) program.install_ram(0x0000, 0x3fff, ram + 0x10000);
		if (BIT(m_bank1, 1)) program.install_ram(0x4000, 0x7fff, ram + 0x14000);
		if (BIT(m_bank1, 2)) program.install_ram(0x8000, 0xbfff, ram + 0x4000);
		if (BIT(m_bank1, 3)) program.install_ram(0xc000, 0xffff, ram + 0x1c000);
		break;

	case 3:
		if (BIT(m_bank1, 0)) program.install_ram(0x0000, 0x3fff, ram + 0x10000);
		if (BIT(m_bank1, 1)) program.install_ram(0x4000, 0x7fff, ram + 0x14000);
		if (BIT(m_bank1, 2)) program.install_ram(0x8000, 0xbfff, ram + 0x0000);
		if (BIT(m_bank1, 3)) program.install_ram(0xc000, 0xffff, ram + 0x4000);
		break;

	case 4:
		if (BIT(m_bank1, 0)) program.install_ram(0x0000, 0x3fff, ram + 0xc000);
		if (BIT(m_bank1, 1)) program.install_ram(0x4000, 0x7fff, ram + 0x14000);
		if (BIT(m_bank1, 2)) program.install_ram(0x8000, 0xbfff, ram + 0x18000);
		if (BIT(m_bank1, 3)) program.install_ram(0xc000, 0xffff, ram + 0x1c000);
		break;
	}

	// bank 0 overrides
	if (BIT(m_bank0, 0)) program.install_ram(0x0000, 0x3fff, ram + 0x0000);
	if (BIT(m_bank0, 1)) program.install_ram(0x4000, 0x7fff, ram + 0x4000);
	if (BIT(m_bank0, 2)) program.install_ram(0x8000, 0xbfff, ram + 0x8000);
	if (BIT(m_bank0, 3)) program.install_ram(0xc000, 0xffff, ram + 0xc000);

	// PROM enabled
	if (!BIT(m_bank0, 5)) program.install_rom(0xf000, 0xf7ff, 0x800, m_rom);
}


//-------------------------------------------------
//  s100_w - S-100 bus extended address A16-A23
//-------------------------------------------------

void super6_state::s100_w(uint8_t data)
{
	/*

	    bit     description

	    0       A16
	    1       A17
	    2       A18
	    3       A19
	    4       A20
	    5       A21
	    6       A22
	    7       A23

	*/

	m_s100 = data;
}


//-------------------------------------------------
//  bank0_w - on-board memory control port #0
//-------------------------------------------------

void super6_state::bank0_w(uint8_t data)
{
	/*

	    bit     description

	    0       memory bank 0 (0000-3fff)
	    1       memory bank 1 (4000-7fff)
	    2       memory bank 2 (8000-bfff)
	    3       memory bank 3 (c000-ffff)
	    4
	    5       PROM enabled (0=enabled, 1=disabled)
	    6       power on jump reset
	    7       parity check enable

	*/

	m_bank0 = data;

	bankswitch();
}


//-------------------------------------------------
//  bank1_w - on-board memory control port #1
//-------------------------------------------------

void super6_state::bank1_w(uint8_t data)
{
	/*

	    bit     description

	    0       memory bank 4
	    1       memory bank 5
	    2       memory bank 6
	    3       memory bank 7
	    4       map select 0
	    5       map select 1
	    6       map select 2
	    7

	*/

	m_bank1 = data;

	bankswitch();
}



//**************************************************************************
//  PERIPHERALS
//**************************************************************************

//-------------------------------------------------
//  floppy_r - FDC synchronization/drive/density
//-------------------------------------------------

uint8_t super6_state::fdc_r()
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4
	    5
	    6
	    7       FDC INTRQ

	*/

	if (!machine().side_effects_disabled())
	{
		if (!m_z80_wait)
		{
			m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE);
			m_maincpu->retry_access();
		}

		m_z80_wait = !m_z80_wait;
	}

	return m_fdc->intrq_r() ? 0x7f : 0xff;
}


//-------------------------------------------------
//  floppy_w - FDC synchronization/drive/density
//-------------------------------------------------

void super6_state::fdc_w(uint8_t data)
{
	/*

	    bit     description

	    0       disk drive select 0
	    1       disk drive select 1
	    2       head select (0=head 1, 1=head 2)
	    3       disk density (0=single, 1=double)
	    4       size select (0=8", 1=5.25")
	    5
	    6
	    7

	    Codes passed to here during boot are 0x00, 0x08, 0x38
	*/

	// disk drive select
	floppy_image_device *floppy = nullptr;

	if ((data & 3) == 0)
		floppy = m_floppy[0]->get_device();
	if ((data & 3) == 1)
		floppy = m_floppy[1]->get_device();

	m_fdc->set_floppy(floppy);
	if (floppy) floppy->mon_w(0);

	// head select
	if (floppy) floppy->ss_w(BIT(data, 2));

	// disk density
	m_fdc->dden_w(!BIT(data, 3));

	// disk size
	m_fdc->enmf_w(!BIT(data, 4));
}



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( super6_mem )
//-------------------------------------------------

void super6_state::super6_mem(address_map &map)
{
}


//-------------------------------------------------
//  ADDRESS_MAP( super6_io )
//-------------------------------------------------

void super6_state::super6_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x03).rw(m_dart, FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
	map(0x04, 0x07).rw(m_pio, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x08, 0x0b).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x0c, 0x0f).rw(m_fdc, FUNC(wd2793_device::read), FUNC(wd2793_device::write));
	map(0x10, 0x10).mirror(0x03).rw(m_dma, FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	map(0x14, 0x14).rw(FUNC(super6_state::fdc_r), FUNC(super6_state::fdc_w));
	map(0x15, 0x15).portr(m_j7).w(FUNC(super6_state::s100_w));
	map(0x16, 0x16).w(FUNC(super6_state::bank0_w));
	map(0x17, 0x17).w(FUNC(super6_state::bank1_w));
	map(0x18, 0x18).mirror(0x03).w(BR1945_TAG, FUNC(com8116_device::stt_str_w));
//  map(0x40, 0x40) ?
//  map(0xe0, 0xe7) HDC?
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( super6 )
//-------------------------------------------------

static INPUT_PORTS_START( super6 )
	PORT_START("J7")
	PORT_DIPNAME( 0x0f, 0x0e, "SIO Channel A Baud Rate" ) PORT_DIPLOCATION("J7:1,2,3,4")
	PORT_DIPSETTING(    0x00, "50" )
	PORT_DIPSETTING(    0x01, "75" )
	PORT_DIPSETTING(    0x02, "110" )
	PORT_DIPSETTING(    0x03, "134.5" )
	PORT_DIPSETTING(    0x04, "150" )
	PORT_DIPSETTING(    0x05, "300" )
	PORT_DIPSETTING(    0x06, "600" )
	PORT_DIPSETTING(    0x07, "1200" )
	PORT_DIPSETTING(    0x08, "1800" )
	PORT_DIPSETTING(    0x09, "2000" )
	PORT_DIPSETTING(    0x0a, "2400" )
	PORT_DIPSETTING(    0x0b, "3600" )
	PORT_DIPSETTING(    0x0c, "4800" )
	PORT_DIPSETTING(    0x0d, "7200" )
	PORT_DIPSETTING(    0x0e, "9600" )
	PORT_DIPSETTING(    0x0f, "19200" )
	PORT_DIPNAME( 0x70, 0x70, "SIO Channel B Baud Rate" ) PORT_DIPLOCATION("J7:5,6,7")
	PORT_DIPSETTING(    0x00, "50" )
	PORT_DIPSETTING(    0x10, "75" )
	PORT_DIPSETTING(    0x20, "110" )
	PORT_DIPSETTING(    0x30, "134.5" )
	PORT_DIPSETTING(    0x40, "150" )
	PORT_DIPSETTING(    0x50, "300" )
	PORT_DIPSETTING(    0x60, "600" )
	PORT_DIPSETTING(    0x70, "1200" )
	PORT_DIPNAME( 0x80, 0x00, "Disk Drive Type" ) PORT_DIPLOCATION("J7:8")
	PORT_DIPSETTING(    0x80, "Single Sided" )
	PORT_DIPSETTING(    0x00, "Double Sided" )
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  Z80CTC
//-------------------------------------------------


//-------------------------------------------------
//  Z80DMA
//-------------------------------------------------

uint8_t super6_state::memory_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	return prog_space.read_byte(offset);
}

void super6_state::memory_write_byte(offs_t offset, uint8_t data)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	prog_space.write_byte(offset, data);
}

uint8_t super6_state::io_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_IO);
	return prog_space.read_byte(offset);
}

void super6_state::io_write_byte(offs_t offset, uint8_t data)
{
	address_space& prog_space = m_maincpu->space(AS_IO);
	prog_space.write_byte(offset, data);
}


//-------------------------------------------------
//  floppy_formats
//-------------------------------------------------

static void super6_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_QD);
}

void super6_state::fdc_intrq_w(int state)
{
	if (state) m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, CLEAR_LINE);

	m_ctc->trg3(state);   // J6 pin 7-8
}

void super6_state::fdc_drq_w(int state)
{
	if (state) m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, CLEAR_LINE);

	m_dma->rdy_w(state);
}


//-------------------------------------------------
//  z80_daisy_config super6_daisy_chain
//-------------------------------------------------

// no evidence of daisy chain in use - removed for now
//static const z80_daisy_config super6_daisy_chain[] =
//{
//  { Z80CTC_TAG },
//  { Z80DART_TAG },
//  { Z80PIO_TAG },
//  { nullptr }
//};


//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( super6 )
//-------------------------------------------------

void super6_state::machine_start()
{
	// state saving
	save_item(NAME(m_z80_wait));
	save_item(NAME(m_s100));
	save_item(NAME(m_bank0));
	save_item(NAME(m_bank1));
}


void super6_state::machine_reset()
{
	m_z80_wait = false;
	m_bank0 = m_bank1 = 0;

	bankswitch();
}



//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

//-------------------------------------------------
//  machine_config( super6 )
//-------------------------------------------------

void super6_state::super6(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 24_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &super6_state::super6_mem);
	m_maincpu->set_addrmap(AS_IO, &super6_state::super6_io);
	//m_maincpu->set_daisy_config(super6_daisy_chain);
	m_maincpu->busack_cb().set(m_dma, FUNC(z80dma_device::bai_w));

	// devices
	Z80CTC(config, m_ctc, 24_MHz_XTAL / 4);
	m_ctc->set_clk<0>(24_MHz_XTAL / 16);   // J6 pin 1-14 (1.5MHz)
	m_ctc->zc_callback<0>().set(m_ctc, FUNC(z80ctc_device::trg1));   // J6 pin 2-3
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80DMA(config, m_dma, 24_MHz_XTAL / 6);
	m_dma->out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	m_dma->out_int_callback().set(m_ctc, FUNC(z80ctc_device::trg2));
	m_dma->in_mreq_callback().set(FUNC(super6_state::memory_read_byte));
	m_dma->out_mreq_callback().set(FUNC(super6_state::memory_write_byte));
	m_dma->in_iorq_callback().set(FUNC(super6_state::io_read_byte));
	m_dma->out_iorq_callback().set(FUNC(super6_state::io_write_byte));

	Z80PIO(config, m_pio, 24_MHz_XTAL / 4);
	m_pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	WD2793(config, m_fdc, 24_MHz_XTAL / 12);
	m_fdc->set_force_ready(true);
	m_fdc->intrq_wr_callback().set(FUNC(super6_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(super6_state::fdc_drq_w));

	FLOPPY_CONNECTOR(config, m_floppy[0], super6_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], super6_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	Z80DART(config, m_dart, 24_MHz_XTAL / 4);
	m_dart->out_txda_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_txd));
	m_dart->out_dtra_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_dtr));
	m_dart->out_rtsa_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_rts));
	m_dart->out_txdb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_txd));
	m_dart->out_dtrb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_dtr));
	m_dart->out_rtsb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_rts));
	m_dart->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	rs232_port_device &rs232a(RS232_PORT(config, RS232_A_TAG, default_rs232_devices, "terminal"));
	rs232a.rxd_handler().set(m_dart, FUNC(z80dart_device::rxa_w));

	rs232_port_device &rs232b(RS232_PORT(config, RS232_B_TAG, default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_dart, FUNC(z80dart_device::rxb_w));

	COM8116(config, m_brg, 5.0688_MHz_XTAL);
	m_brg->fr_handler().set(m_dart, FUNC(z80dart_device::txca_w));
	m_brg->fr_handler().append(m_dart, FUNC(z80dart_device::rxca_w));
	m_brg->fr_handler().append(m_ctc, FUNC(z80ctc_device::trg1));
	m_brg->ft_handler().set(m_dart, FUNC(z80dart_device::rxtxcb_w));

	// internal ram
	RAM(config, RAM_TAG).set_default_size("128K");

	// software list
	SOFTWARE_LIST(config, "flop_list").set_original("super6");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( super6 )
//-------------------------------------------------

ROM_START( super6 )
	ROM_REGION( 0x800, Z80_TAG, 0 )
	ROM_DEFAULT_BIOS( "v36" )
	ROM_SYSTEM_BIOS( 0, "v36", "ADC S6 v3.6" )
	ROMX_LOAD( "adcs6_v3.6.u29", 0x000, 0x800, CRC(386fd22a) SHA1(9c177990aa180ab93be9c4641e92ae934627e661), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v12", "Digitex Monitor v1.2a" )
	ROMX_LOAD( "digitex monitor 1.2a 6oct1983.u29", 0x000, 0x800, CRC(a4c33ce4) SHA1(46dde43ea51d295f2b3202c2d0e1883bde1a8da7), ROM_BIOS(1) )

	ROM_REGION( 0x800, "plds", 0 )
	ROM_LOAD( "pal16l8.u16", 0x000, 0x800, NO_DUMP )
	ROM_LOAD( "pal16l8.u36", 0x000, 0x800, NO_DUMP )
ROM_END



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY                         FULLNAME     FLAGS
COMP( 1983, super6, 0,      0,      super6,  super6, super6_state, empty_init, "Advanced Digital Corporation", "Super Six", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



super80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/*****************************************************************************

Super80.cpp written by Robbbert, 2005-2010.

2010-12-19: Added V3.7 bios freshly dumped today.
2014-04-28: Added disk system and did cleanups

See the MAME sysinfo and wiki for usage
documentation. Below for the most technical bits:

= Architecture (super80):

  * Z80 @ 2MHz
  * 16k, 32k or 48k RAM (0000-BFFF)
  * 12k ROM (C000-EFFF)
  * 3.5k RAM (F000-FDFF), comes with the "64k ram" modification
  * 0.5k RAM (FE00-FFFF) for Chipspeed colour board

= Architecture (super80v):

  * Z80 @ 2MHz
  * 16k, 32k or 48k RAM (0000-BFFF)
  * 12k ROM (C000-EFFF)
  * 2k Video RAM (F000-F7FF) banked with Colour RAM (modified Chipspeed board)
  * 2k PCG RAM (F800-FFFF) banked with Character Generator ROM

= Super80 ports:

  port $F0: General Purpose output port
    Bit 0 - cassette output
    Bit 1 - cassette relay control; 0=relay on
    Bit 2 - turns screen on and off;0=screen off
    Bit 3 - Available for user projects [We will use it for sound]
    Bit 4 - Available for user projects
    Bit 5 - cassette LED; 0=LED on
    Bit 6/7 - not decoded

  port $F1: Video page output port
    Bit 0 - not decoded [we will use it for video switching]
    Bits 1 to 7 - choose video page to display
    Bit 1 controls A9, bit 2 does A10, etc

  port $F2: General purpose input port
    Bit 0 - cassette input
    Bit 1 - Available for user projects
    Bit 2 - Available for user projects
    Bit 3 - not decoded
    Bit 4 - Switch A [These switches are actual DIP switches on the motherboard]
    Bit 5 - Switch B
    Bit 6 - Switch C
    Bit 7 - Switch D

= Super80v ports:

  port $10: MC6845 control port

  port $11: MC6845 data port

  port $F0: General Purpose output port
    Bit 0 - cassette output
    Bit 1 - Cassette relay control; 0=relay on
    Bit 2 - Colour banking (0 = Colour Ram, 1 = Video Ram)
    Bit 3 - Sound
    Bit 4 - PCG banking (0 = PROM, 1 = PCG)
    Bit 5 - cassette LED; 0=LED on
    Bit 6/7 - not decoded

  port $F2: General purpose input port - same as for Super80.

= Cassette information:

The standard cassette system uses Kansas City format. Data rates available are 300, 400, 600, and 1200 baud.
The user has to adjust some bytes in memory in order to select a different baud rate.

  BDF8  BDF9    Baud
  ---------------------
  F8    04       300
  BA    03       400
  7C    02       600
  3E    01      1200

The enhanced Monitor roms (those not supplied by Dick Smith) have extra commands to change the rates
without the tedium of manually modifying memory.

When saving, the OS toggles the cassette bit (bit 0 of port F0) at the required frequencies directly.

When loading, the signal passes through a filter, then into a 4046 PLL (Phase-Locked-Loop) chip.
This acts as a frequency-to-voltage converter. The output of this device is passed to the "+" input
of a LM311 op-amp. The "-" input is connected to a 10-turn trimpot, which is adjusted by the owner
at construction time. It sets the switching midpoint voltage. Voltages above a certain level become
a "1" level at the output, while voltages below become a "0" level. This output in turn connects
to the cassette input bit of port F2.

The monitor loading routine (at C066 in most monitor roms), waits for a high-to-low transition
(the low is the beginning of the start bit), then waits for half a bit, checks it is still low,
waits for a full bit, then reads the state (this is the first bit), then cycles between waiting
a bit and reading the next, until a full byte has been constructed. Lastly, the stop bit is
checked that it is at a high level.

This means that we cannot attempt to convert frequency to voltage ourselves, since the OS only
"looks" once a bit. The solution is to use a mame timer running at a high enough rate (40 kHz)
to read the wave state. While the wave state stays constant, a counter is incremented. When the
state changes, the output is set according to how far the counter has progressed. The counter is
then reset ready for the next wave state. The code for this is in the TIMER_CALLBACK.

A kit was produced by ETI magazine, which plugged into the line from your cassette player earphone
socket. The computer line was plugged into this box instead of the cassette player. The box was
fitted with a speaker and a volume control. You could listen to the tones, to assist with head
alignment, and with debugging. In MAME, a config switch has been provided so that you can turn
this sound on or off as needed.

= About the 1 MHz / 2 MHz switching:

The original hardware runs with a 2 MHz clock, but operates in an unusual way. There is no video
processor chip, just a huge bunch of TTL. The system spends half the time running the CPU,
and half the time displaying the picture. The timing will activate the BUSREQ line, and the CPU will
finish its current instruction, activate the BUSACK line, and go to sleep. The video circuits will
read the video RAM and show the picture. At the end, the BUSREQ line is released, and processing can
continue.

The processing time occurs during the black parts of the screen, therefore half the screen will be
black unless you expand the image with the monitor's controls. This method ensures that there will
be no memory contention, and thus, no snow. The processor will run at 2 MHz pulsed at 48.8 Hz, which
is an effective speed of 1 MHz.

When saving or loading a cassette file, this pulsing would cause the save tone to be modulated with
a loud hum. When loading, the synchronisation to the start bit could be missed, causing errors.
Therefore the screen can be turned off via an output bit. This disables the BUSREQ control, which
in turn prevents screen refresh, and gives the processor a full uninterrupted 2 MHz speed.

To obtain accurate timing, the video update routine will toggle the HALT line on alternate frames.
Although physically incorrect, it is the only way to accurately emulate the speed change function.
The video update routine emulates the blank screen by filling it with spaces.

For the benefit of those who like to experiment, config switches have been provided in MAME to
allow you to leave the screen on at all times, and to always run at 2 MHz if desired. These options
cannot exist in real hardware.

= Quickload:

This was not a standard feature. It is a hardware facility I added to my machine when age threatened
to kill off my cassette player and tapes. The tapes were loaded up one last time, and transferred to
a hard drive on a surplus 386 PC, in binary format (NOT a wave file). Special roms were made to allow
loading and saving to the S-100 board and its ports. These ports were plugged into the 386 via cables.
A QBASIC program on the 386 monitored the ports and would save and load files when requested by the
Super-80. This worked (and still works) very well, and is a huge improvement over the cassette, both
speedwise and accuracy-wise.

The modified rom had the autorun option built in. Autorun was never available for cassettes.

In MAME, the same file format is used - I can transfer files between MAME and the 386 seamlessly.
MAME has one difference - the program simply appears in memory without the processor being aware
of it. To accomplish autorun therefore requires that the processor pc register be set to the start
address of the program. BASIC programs may need some preprocessing before they can be started. This
is not necessary on a Super-80 or a Microbee, but is needed on any system running Microsoft BASIC,
such as the Exidy Sorcerer or the VZ-200.

In MAME, quickload is available for all Super80 variants (otherwise you would not have any games
to play). MAME features a config switch so the user can turn autorun on or off as desired.


= Start of Day circuit:

When the computer is turned on or reset, the Z80 will want to start executing at 0000. Since this is
RAM, this is not a good idea. The SOD circuit forcibly disables the RAM and enables the ROMs, so they
will appear to be at 0000. Thus, the computer will boot.

The Master Reset signal (power-on or Reset button pushed), triggers a flipflop that disables RAM and
causes C000 to FFFF to appear at 0000 to 3FFF. This will be reset (back to normal) when A14 and A15
are high, and /M1 is active. This particular combination occurs on all ROM variants, when reading the
fifth byte in the ROM. In reality, the switchover can take place any time between the 4th byte until
the computer has booted up. This is because the low RAM does not contain any system areas.


Super80 disk WD2793, Z80DMA:

Port(hex)  Role       Comment
---------  -----      --------
30         DMA I/O    Z80A DMA Controller Command Register
38         WDSTCM     WD2793 Command Status
39         WDTRK      WD2793 Track Register
3A         WDSEC      WD2793 Sector Register
3B         WDDATA     WD2793 Data Register

3E         UFDSTAT    FDD and WD2793 Status input
   Bit0    INTRQ      1 = WD2793 Interrupt Request
   Bit1    DATARQ     1 = Data Ready to Receive or Send
   Bit2    MOTOR ON   1 = FDD Motor On

3F         UFDCOM     WD2793 Command Register output
   Bit0    5/8        1 = 8" Drive Selected
   Bit1    ENMF*      0 = 1MHz, 1 = 2MHz (for 8" double density)
   Bit2    DR0SEL     1 = Drive 0 (Drive A) selected
   Bit3    DR1SEL     1 = Drive 1 (Drive B) selected
   Bit4    DR2SEL     1 = Drive 2 (Drive C) selected
   Bit5    DR3SEL     1 = Drive 3 (Drive D) selected
   Bit6    SIDESEL    0 = Select Disk side 0, 1 = Select Disk side 1
   Bit7    DDEN*      0 = MFM (Double Density), 1 = FM (Single Density)

ToDo:
- Fix Paste: Shift operates randomly (only super80m is suitable, the others drop characters because
       of the horrible inline editor they use)


***********************************************************************************************************/

#include "emu.h"
#include "super80.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "utf8.h"

#include "super80.lh"


#define MASTER_CLOCK    (12_MHz_XTAL)
#define PIXEL_CLOCK (MASTER_CLOCK/2)
#define HTOTAL      (384)
#define HBEND       (0)
#define HBSTART     (256)
#define VTOTAL      (240)
#define VBEND       (0)
#define VBSTART     (160)

#define SUPER80V_SCREEN_WIDTH       (560)
#define SUPER80V_SCREEN_HEIGHT      (300)
#define SUPER80V_DOTS           (7)


/**************************** MEMORY AND I/O MAPPINGS *****************************************************************/

void super80_state::super80_map(address_map &map)
{
	map(0x0000, 0xbfff).ram().share("mainram");
	map(0xc000, 0xefff).rom().region("maincpu", 0).nopw();
	map(0xf000, 0xffff).lr8(NAME([] () { return 0xff; })).nopw();
}

void super80_state::super80m_map(address_map &map)
{
	map(0x0000, 0xffff).ram().share("mainram");
	map(0xc000, 0xefff).rom().region("maincpu", 0).nopw();
}

void super80v_state::super80v_map(address_map &map)
{
	map(0x0000, 0xbfff).ram().share("mainram");
	map(0xc000, 0xefff).rom().region("maincpu", 0).nopw();
	map(0xf000, 0xf7ff).rw(FUNC(super80v_state::low_r),  FUNC(super80v_state::low_w));
	map(0xf800, 0xffff).rw(FUNC(super80v_state::high_r), FUNC(super80v_state::high_w));
}

void super80r_state::super80r_map(address_map &map)
{
	super80v_map(map);
	map(0xf000, 0xf7ff).rw(FUNC(super80r_state::low_r),  FUNC(super80r_state::low_w));
	map(0xf800, 0xffff).rw(FUNC(super80r_state::high_r), FUNC(super80r_state::high_w));
}

void super80_state::super80_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0xdc, 0xdc).r("cent_status_in", FUNC(input_buffer_device::read));
	map(0xdc, 0xdc).w(FUNC(super80_state::portdc_w));
	map(0xe0, 0xe0).mirror(0x14).w(FUNC(super80_state::portf0_w));
	map(0xe1, 0xe1).mirror(0x14).w(FUNC(super80_state::portf1_w));
	map(0xe2, 0xe2).mirror(0x14).r(FUNC(super80_state::portf2_r));
	map(0xf8, 0xfb).mirror(0x04).rw(m_pio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
}

void super80_state::super80e_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0xbc, 0xbc).r("cent_status_in", FUNC(input_buffer_device::read));
	map(0xbc, 0xbc).w(FUNC(super80_state::portdc_w));
	map(0xe0, 0xe0).mirror(0x14).w(FUNC(super80_state::portf0_w));
	map(0xe1, 0xe1).mirror(0x14).w(FUNC(super80_state::portf1_w));
	map(0xe2, 0xe2).mirror(0x14).r(FUNC(super80_state::portf2_r));
	map(0xf8, 0xfb).mirror(0x04).rw(m_pio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
}


void super80v_state::super80v_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x10, 0x10).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x11, 0x11).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x30, 0x30).rw(m_dma, FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	map(0x38, 0x3b).rw(m_fdc, FUNC(wd2793_device::read), FUNC(wd2793_device::write));
	map(0x3e, 0x3e).r(FUNC(super80v_state::port3e_r));
	map(0x3f, 0x3f).w(FUNC(super80v_state::port3f_w));
	map(0xdc, 0xdc).r("cent_status_in", FUNC(input_buffer_device::read));
	map(0xdc, 0xdc).w(FUNC(super80v_state::portdc_w));
	map(0xe0, 0xe0).mirror(0x14).w(FUNC(super80v_state::portf0_w));
	map(0xe2, 0xe2).mirror(0x14).r(FUNC(super80v_state::portf2_r));
	map(0xf8, 0xfb).mirror(0x04).rw(m_pio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
}

/**************************** DIPSWITCHES, KEYBOARD, HARDWARE CONFIGURATION ****************************************/

	/* Enhanced options not available on real hardware */
static INPUT_PORTS_START( super80_cfg )
	PORT_START("CONFIG")
	PORT_CONFNAME( 0x01, 0x01, "Autorun on Quickload")
	PORT_CONFSETTING(    0x00, DEF_STR(No))
	PORT_CONFSETTING(    0x01, DEF_STR(Yes))
	PORT_CONFNAME( 0x02, 0x02, "2 MHz always")
	PORT_CONFSETTING(    0x02, DEF_STR(No))
	PORT_CONFSETTING(    0x00, DEF_STR(Yes))
	PORT_CONFNAME( 0x04, 0x04, "Screen on always")
	PORT_CONFSETTING(    0x04, DEF_STR(No))
	PORT_CONFSETTING(    0x00, DEF_STR(Yes))
	PORT_CONFNAME( 0x08, 0x08, "Cassette Speaker")
	PORT_CONFSETTING(    0x08, DEF_STR(On))
	PORT_CONFSETTING(    0x00, DEF_STR(Off))
	PORT_CONFNAME( 0x60, 0x40, "Colour")
	PORT_CONFSETTING(    0x60, "White")
	PORT_CONFSETTING(    0x40, "Green")
INPUT_PORTS_END

static INPUT_PORTS_START( super80 )
	PORT_START("DSW")
	PORT_BIT( 0xf, 0xf, IPT_UNUSED )
	PORT_DIPNAME( 0x10, 0x00, "Switch A") PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(    0x10, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x20, 0x20, "Switch B") PORT_DIPLOCATION("SW1:2")
	PORT_DIPSETTING(    0x20, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x40, 0x40, "Switch C") PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(    0x40, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x80, 0x00, "Switch D") PORT_DIPLOCATION("SW1:4")
	PORT_DIPSETTING(    0x80, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))

	PORT_START("KEY.0")  // The physical keys show some shifted characters that are not actually available
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@  `") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('P') PORT_CHAR(0x10)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('X') PORT_CHAR(0x18)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1  !") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9  )") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("REPT") PORT_CODE(KEYCODE_LALT)
	PORT_START("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('A') PORT_CHAR(0x01)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('Q') PORT_CHAR(0x11)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('Y') PORT_CHAR(0x19)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2  \"") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(":  *") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(0x08)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_START("KEY.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('B') PORT_CHAR(0x02)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('R') PORT_CHAR(0x12)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('Z') PORT_CHAR(0x1a)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3  #") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";  +") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TAB") PORT_CODE(KEYCODE_TAB) PORT_CHAR(0x09)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("(Fire)") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_START("KEY.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('K') PORT_CHAR(0x0b)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('S') PORT_CHAR(0x13)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[  {") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4  $") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",  <") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LINEFEED") PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(0x0a)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_START("KEY.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('D') PORT_CHAR(0x04)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('L') PORT_CHAR(0x0c)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('T') PORT_CHAR(0x14)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\  |")PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('\\') PORT_CHAR(0x1c)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5  %") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BRK") PORT_CODE(KEYCODE_NUMLOCK) PORT_CHAR(0x03)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(0x0d)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_START("KEY.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('E') PORT_CHAR(0x05)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('U') PORT_CHAR(0x15)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]  }") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR(']') PORT_CHAR(0x1d)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6  &") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".  >") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(0x1b)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_START("KEY.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('F') PORT_CHAR(0x06)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('N') PORT_CHAR(0x0e)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('V') PORT_CHAR(0x16)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^  ~") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') PORT_CHAR('^') PORT_CHAR(0x1e)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7  \'") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/  ?") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL) PORT_CHAR(0x7f) PORT_CHAR(0x7f) PORT_CHAR(0x1f) // natural kbd, press ctrl-backspace to DEL
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_START("KEY.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('G') PORT_CHAR(0x07)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('O') PORT_CHAR(0x0f)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('W') PORT_CHAR(0x17)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-  =") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8  (") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(' ')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LOCK") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_INCLUDE( super80_cfg )
INPUT_PORTS_END

static INPUT_PORTS_START( super80d )
	PORT_START("DSW")
	PORT_BIT( 0xf, 0xf, IPT_UNUSED )
	PORT_DIPNAME( 0x10, 0x00, "Switch A") PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(    0x10, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x20, 0x20, "Switch B") PORT_DIPLOCATION("SW1:2")
	PORT_DIPSETTING(    0x20, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x40, 0x40, "Switch C") PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(    0x40, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x80, 0x00, "Switch D") PORT_DIPLOCATION("SW1:4")
	PORT_DIPSETTING(    0x80, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))

	PORT_START("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@  `") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('`') PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p') PORT_CHAR(0x10)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x') PORT_CHAR(0x18)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1  !") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9  )") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("REPT") PORT_CODE(KEYCODE_LALT)
	PORT_START("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a') PORT_CHAR(0x01)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q') PORT_CHAR(0x11)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y') PORT_CHAR(0x19)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2  \"") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(":  *") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(0x08)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_START("KEY.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b') PORT_CHAR(0x02)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r') PORT_CHAR(0x12)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z') PORT_CHAR(0x1a)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3  #") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";  +") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TAB") PORT_CODE(KEYCODE_TAB) PORT_CHAR(0x09)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("(Fire)") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_START("KEY.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k') PORT_CHAR(0x0b)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s') PORT_CHAR(0x13)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[  {") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4  $") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",  <") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LINEFEED") PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(0x0a)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_START("KEY.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d') PORT_CHAR(0x04)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l') PORT_CHAR(0x0c)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t') PORT_CHAR(0x14)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\  |")PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|') PORT_CHAR(0x1c)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5  %") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BRK") PORT_CODE(KEYCODE_NUMLOCK) PORT_CHAR(0x03)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(0x0d)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_START("KEY.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e') PORT_CHAR(0x05)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u') PORT_CHAR(0x15)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]  }") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}') PORT_CHAR(0x1d)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 &") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".  >") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(0x1b)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_START("KEY.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f') PORT_CHAR(0x06)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n') PORT_CHAR(0x0e)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v') PORT_CHAR(0x16)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^  ~") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') PORT_CHAR('~') PORT_CHAR(0x1e)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7  \'") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/  ?") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL) PORT_CHAR(0x7f) PORT_CHAR(0x5f) PORT_CHAR(0x1f) // natural kbd, press ctrl-backspace to DEL
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_START("KEY.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g') PORT_CHAR(0x07)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o') PORT_CHAR(0x0f)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w') PORT_CHAR(0x17)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-  =") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8  (") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(' ')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LOCK") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_INCLUDE( super80_cfg )
INPUT_PORTS_END

static INPUT_PORTS_START( super80e )
	PORT_INCLUDE( super80d )
	PORT_MODIFY("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@  `") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@') PORT_CHAR('`') // fix mistake in super80d matrix
INPUT_PORTS_END

static INPUT_PORTS_START( super80m )
	PORT_INCLUDE( super80e )

	PORT_MODIFY("CONFIG")

	/* Enhanced options not available on real hardware */
	PORT_CONFNAME( 0x10, 0x10, "Swap CharGens")
	PORT_CONFSETTING(    0x10, DEF_STR(No))
	PORT_CONFSETTING(    0x00, DEF_STR(Yes))
	PORT_CONFNAME( 0x60, 0x40, "Colour")
	PORT_CONFSETTING(    0x60, "White")
	PORT_CONFSETTING(    0x40, "Green")
	PORT_CONFSETTING(    0x00, "Composite")
	PORT_CONFSETTING(    0x20, "RGB")
INPUT_PORTS_END

static INPUT_PORTS_START( super80v )
	PORT_INCLUDE( super80m )

	PORT_MODIFY("CONFIG")
	PORT_BIT( 0x16, 0x16, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( super80r )
	PORT_INCLUDE( super80d )

	PORT_MODIFY("CONFIG")
	PORT_BIT( 0x16, 0x16, IPT_UNUSED )
INPUT_PORTS_END


/**************************** F4 CHARACTER DISPLAYER ***********************************************************/

static const gfx_layout super80_charlayout =
{
	8,10,                   /* 8 x 10 characters */
	64,                 /* 64 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{  0*8,  2*8,  4*8,  6*8,  8*8, 10*8, 12*8, 14*8, 1*8,  3*8,  5*8,  7*8,  9*8, 11*8, 13*8, 15*8 },
	8*16                    /* every char takes 16 bytes */
};

static const gfx_layout super80d_charlayout =
{
	8,10,                   /* 8 x 10 characters */
	128,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{  0*8,  2*8,  4*8,  6*8,  8*8, 10*8, 12*8, 14*8, 1*8,  3*8,  5*8,  7*8,  9*8, 11*8, 13*8, 15*8 },
	8*16                    /* every char takes 16 bytes */
};

static const gfx_layout super80e_charlayout =
{
	8,10,                   /* 8 x 10 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{  0*8,  2*8,  4*8,  6*8,  8*8, 10*8, 12*8, 14*8, 1*8,  3*8,  5*8,  7*8,  9*8, 11*8, 13*8, 15*8 },
	8*16                    /* every char takes 16 bytes */
};

static const gfx_layout super80v_charlayout =
{
	8,16,                   /* 8 x 16 characters */
	128,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{  0*8,  1*8,  2*8,  3*8,  4*8,  5*8,  6*8,  7*8, 8*8,  9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_super80 )
	GFXDECODE_ENTRY( "chargen", 0x0000, super80_charlayout, 16, 1 )
GFXDECODE_END

static GFXDECODE_START( gfx_super80d )
	GFXDECODE_ENTRY( "chargen", 0x0000, super80d_charlayout, 16, 1 )
GFXDECODE_END

static GFXDECODE_START( gfx_super80e )
	GFXDECODE_ENTRY( "chargen", 0x0000, super80e_charlayout, 16, 1 )
GFXDECODE_END

static GFXDECODE_START( gfx_super80m )
	GFXDECODE_ENTRY( "chargen", 0x0000, super80e_charlayout, 2, 6 )
	GFXDECODE_ENTRY( "chargen", 0x1000, super80d_charlayout, 2, 6 )
GFXDECODE_END

static GFXDECODE_START( gfx_super80v )
	GFXDECODE_ENTRY( "chargen", 0x0000, super80v_charlayout, 2, 6 )
GFXDECODE_END



/**************************** BASIC MACHINE CONSTRUCTION ***********************************************************/


static const z80_daisy_config super80_daisy_chain[] =
{
	{ "z80pio" },
	{ nullptr }
};

//-------------------------------------------------
//  Z80DMA
//-------------------------------------------------

uint8_t super80v_state::memory_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	return prog_space.read_byte(offset);
}

void super80v_state::memory_write_byte(offs_t offset, uint8_t data)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	prog_space.write_byte(offset, data);
}

uint8_t super80v_state::io_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_IO);
	return prog_space.read_byte(offset);
}

void super80v_state::io_write_byte(offs_t offset, uint8_t data)
{
	address_space& prog_space = m_maincpu->space(AS_IO);
	prog_space.write_byte(offset, data);
}

static void super80_floppies(device_slot_interface &device)
{
	device.option_add("s80flop", FLOPPY_525_QD);
}


static const char *const relay_sample_names[] =
{
	"*relay",
	"relayoff",
	"relayon",
	nullptr
};


void super80_state::super80(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, MASTER_CLOCK/6);        /* 2 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &super80_state::super80_map);
	m_maincpu->set_addrmap(AS_IO, &super80_state::super80_io);
	m_maincpu->set_daisy_config(super80_daisy_chain);

	Z80PIO(config, m_pio, MASTER_CLOCK/6);
	m_pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio->out_pa_callback().set([this](u8 data){ super80_state::pio_port_a_w(data); });
	m_pio->in_pb_callback().set([this](){ return super80_state::pio_port_b_r(); });

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(48.8);
	m_screen->set_raw(PIXEL_CLOCK, HTOTAL, HBEND, HBSTART, VTOTAL, VBEND, VBSTART);
	m_screen->set_screen_update(FUNC(super80_state::screen_update_super80));
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette, FUNC(super80_state::super80m_palette), 32);
	GFXDECODE(config, m_gfxdecode, m_palette, gfx_super80);

	config.set_default_layout(layout_super80);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);
	SAMPLES(config, m_samples);
	m_samples->set_channels(1);
	m_samples->set_samples_names(relay_sample_names);
	m_samples->add_route(ALL_OUTPUTS, "mono", 1.0);

	/* printer */
	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set("cent_status_in", FUNC(input_buffer_device::write_bit7));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	INPUT_BUFFER(config, "cent_status_in", 0);

	/* quickload */
	quickload_image_device &quickload(QUICKLOAD(config, "quickload", "bin", attotime::from_seconds(3)));
	quickload.set_load_callback(FUNC(super80_state::quickload_cb));
	quickload.set_interface("super80_quik");

	/* cassette */
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("super80_cass");

	TIMER(config, "kansas_r").configure_periodic(FUNC(super80_state::kansas_r), attotime::from_hz(40000)); // cass read
	TIMER(config, "timer_k").configure_periodic(FUNC(super80_state::timer_k), attotime::from_hz(300)); // keyb scan
	TIMER(config, "timer_h").configure_periodic(FUNC(super80_state::timer_h), attotime::from_hz(100)); // half-speed

	// software list
	SOFTWARE_LIST(config, "cass_list").set_original("super80_cass").set_filter("DEF");
	SOFTWARE_LIST(config, "quik_list").set_original("super80_quik").set_filter("DEF");
}

void super80_state::super80d(machine_config &config)
{
	super80(config);
	m_gfxdecode->set_info(gfx_super80d);
	m_screen->set_screen_update(FUNC(super80_state::screen_update_super80d));

	// software list
	SOFTWARE_LIST(config.replace(), "cass_list").set_original("super80_cass").set_filter("D");
	SOFTWARE_LIST(config.replace(), "quik_list").set_original("super80_quik").set_filter("D");
}

void super80_state::super80e(machine_config &config)
{
	super80(config);
	m_maincpu->set_addrmap(AS_IO, &super80_state::super80e_io);
	m_gfxdecode->set_info(gfx_super80e);
	m_screen->set_screen_update(FUNC(super80_state::screen_update_super80e));

	// software list
	SOFTWARE_LIST(config.replace(), "cass_list").set_original("super80_cass").set_filter("E");
	SOFTWARE_LIST(config.replace(), "quik_list").set_original("super80_quik").set_filter("E");
}

void super80_state::super80m(machine_config &config)
{
	super80(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &super80_state::super80m_map);

	m_gfxdecode->set_info(gfx_super80m);

	m_screen->set_screen_update(FUNC(super80_state::screen_update_super80m));
	m_screen->screen_vblank().set([this](bool state) { super80_state::screen_vblank_super80m(state); });

	// software list
	SOFTWARE_LIST(config.replace(), "cass_list").set_original("super80_cass").set_filter("M");
	SOFTWARE_LIST(config.replace(), "quik_list").set_original("super80_quik").set_filter("M");
}

void super80v_state::super80v(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, MASTER_CLOCK/6);        /* 2 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &super80v_state::super80v_map);
	m_maincpu->set_addrmap(AS_IO, &super80v_state::super80v_io);
	m_maincpu->set_daisy_config(super80_daisy_chain);
	m_maincpu->busack_cb().set(m_dma, FUNC(z80dma_device::bai_w));

	Z80PIO(config, m_pio, MASTER_CLOCK/6);
	m_pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio->out_pa_callback().set([this](u8 data){ super80v_state::pio_port_a_w(data); });
	m_pio->in_pb_callback().set([this](){ return super80v_state::pio_port_b_r(); });

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_size(SUPER80V_SCREEN_WIDTH, SUPER80V_SCREEN_HEIGHT);
	m_screen->set_visarea(0, SUPER80V_SCREEN_WIDTH-1, 0, SUPER80V_SCREEN_HEIGHT-1);
	m_screen->set_screen_update(FUNC(super80v_state::screen_update_super80v));
	m_screen->screen_vblank().set([this](bool state) { super80v_state::screen_vblank_super80m(state); });

	PALETTE(config, m_palette, FUNC(super80v_state::super80m_palette), 32);
	GFXDECODE(config, m_gfxdecode, m_palette, gfx_super80v);

	MC6845(config, m_crtc, MASTER_CLOCK / SUPER80V_DOTS);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(SUPER80V_DOTS);
	m_crtc->set_update_row_callback(FUNC(super80v_state::crtc_update_row));

	config.set_default_layout(layout_super80);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);
	SAMPLES(config, m_samples);
	m_samples->set_channels(1);
	m_samples->set_samples_names(relay_sample_names);
	m_samples->add_route(ALL_OUTPUTS, "mono", 1.0);

	/* printer */
	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set("cent_status_in", FUNC(input_buffer_device::write_bit7));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	INPUT_BUFFER(config, "cent_status_in", 0);

	/* quickload */
	quickload_image_device &quickload(QUICKLOAD(config, "quickload", "bin", attotime::from_seconds(3)));
	quickload.set_load_callback(FUNC(super80v_state::quickload_cb));
	quickload.set_interface("super80_quik");

	/* cassette */
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("super80_cass");

	TIMER(config, "kansas_r").configure_periodic(FUNC(super80v_state::kansas_r), attotime::from_hz(40000)); // cass read
	TIMER(config, "timer_k").configure_periodic(FUNC(super80v_state::timer_k), attotime::from_hz(300)); // keyb scan

	Z80DMA(config, m_dma, MASTER_CLOCK/6);
	m_dma->out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	m_dma->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	//ba0 - not connected
	m_dma->in_mreq_callback().set(FUNC(super80v_state::memory_read_byte));
	m_dma->out_mreq_callback().set(FUNC(super80v_state::memory_write_byte));
	m_dma->in_iorq_callback().set(FUNC(super80v_state::io_read_byte));
	m_dma->out_iorq_callback().set(FUNC(super80v_state::io_write_byte));

	WD2793(config, m_fdc, 2_MHz_XTAL);
	m_fdc->drq_wr_callback().set(m_dma, FUNC(z80dma_device::rdy_w));
	FLOPPY_CONNECTOR(config, "fdc:0", super80_floppies, "s80flop", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", super80_floppies, "s80flop", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:2", super80_floppies, "s80flop", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:3", super80_floppies, "s80flop", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	// software list
	SOFTWARE_LIST(config, "cass_list").set_original("super80_cass").set_filter("V");
	SOFTWARE_LIST(config, "quik_list").set_original("super80_quik").set_filter("V");
	SOFTWARE_LIST(config, "flop_list").set_original("super80_flop");
}

void super80r_state::super80r(machine_config &config)
{
	super80v(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &super80r_state::super80r_map);

	// software list
	SOFTWARE_LIST(config.replace(), "cass_list").set_original("super80_cass").set_filter("R");
	SOFTWARE_LIST(config.replace(), "quik_list").set_original("super80_quik").set_filter("R");
}

/**************************** ROMS *****************************************************************/

ROM_START( super80 )
	ROM_REGION(0x3000, "maincpu", 0)
	ROM_LOAD("super80.u26",   0x0000, 0x1000, CRC(6a6a9664) SHA1(2c4fcd943aa9bf7419d58fbc0e28ffb89ef22e0b) )
	ROM_LOAD("super80.u33",   0x1000, 0x1000, CRC(cf8020a8) SHA1(2179a61f80372cd49e122ad3364773451531ae85) )
	ROM_LOAD("super80.u42",   0x2000, 0x1000, CRC(a1c6cb75) SHA1(d644ca3b399c1a8902f365c6095e0bbdcea6733b) )

	ROM_REGION(0x0400, "chargen", 0)    // 2513 prom
	ROM_LOAD("super80.u27",   0x0000, 0x0400, CRC(d1e4b3c6) SHA1(3667b97c6136da4761937958f281609690af4081) )
ROM_END

ROM_START( super80d )
	ROM_REGION(0x3000, "maincpu", 0)
	ROM_SYSTEM_BIOS(0, "super80d", "V2.2")
	ROMX_LOAD("super80d.u26", 0x0000, 0x1000, CRC(cebd2613) SHA1(87b94cc101a5948ce590211c68272e27f4cbe95a), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "super80f", "MDS (original)")
	ROMX_LOAD("super80f.u26", 0x0000, 0x1000, CRC(d39775f0) SHA1(b47298ee028924612e9728bb2debd0f47399add7), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "super80g", "MDS (upgraded)")
	ROMX_LOAD("super80g.u26", 0x0000, 0x1000, CRC(7386f507) SHA1(69d7627033d62bd4e886ccc136e89f1524d38f47), ROM_BIOS(2))
	ROM_LOAD("super80.u33",   0x1000, 0x1000, CRC(cf8020a8) SHA1(2179a61f80372cd49e122ad3364773451531ae85) )
	ROM_LOAD("super80.u42",   0x2000, 0x1000, CRC(a1c6cb75) SHA1(d644ca3b399c1a8902f365c6095e0bbdcea6733b) )

	ROM_REGION(0x0800, "chargen", 0)    // 2716 eprom
	ROM_LOAD("super80d.u27",  0x0000, 0x0800, CRC(cb4c81e2) SHA1(8096f21c914fa76df5d23f74b1f7f83bd8645783) )
ROM_END

ROM_START( super80e )
	ROM_REGION(0x3000, "maincpu", 0)
	ROM_LOAD("super80e.u26",  0x0000, 0x1000, CRC(bdc668f8) SHA1(3ae30b3cab599fca77d5e461f3ec1acf404caf07) )
	ROM_LOAD("super80.u33",   0x1000, 0x1000, CRC(cf8020a8) SHA1(2179a61f80372cd49e122ad3364773451531ae85) )
	ROM_LOAD("super80.u42",   0x2000, 0x1000, CRC(a1c6cb75) SHA1(d644ca3b399c1a8902f365c6095e0bbdcea6733b) )

	ROM_REGION(0x1000, "chargen", 0)    // 2732 eprom
	ROM_LOAD("super80e.u27",  0x0000, 0x1000, CRC(ebe763a7) SHA1(ffaa6d6a2c5dacc5a6651514e6707175a32e83e8) )
ROM_END

ROM_START( super80m )
	ROM_REGION(0x3000, "maincpu", 0)
	ROM_SYSTEM_BIOS(0, "8r0", "8R0")
	ROMX_LOAD("s80-8r0.u26",  0x0000, 0x1000, CRC(48d410d8) SHA1(750d984abc013a3344628300288f6d1ba140a95f), ROM_BIOS(0) )
	ROMX_LOAD("s80-8r0.u33",  0x1000, 0x1000, CRC(9765793e) SHA1(4951b127888c1f3153004cc9fb386099b408f52c), ROM_BIOS(0) )
	ROMX_LOAD("s80-8r0.u42",  0x2000, 0x1000, CRC(5f65d94b) SHA1(fe26b54dec14e1c4911d996c9ebd084a38dcb691), ROM_BIOS(0) )
#if 0
	/* Temporary patch to fix crash when lprinting a tab */
	ROM_FILL(0x0c44,1,0x46)
	ROM_FILL(0x0c45,1,0xc5)
	ROM_FILL(0x0c46,1,0x06)
	ROM_FILL(0x0c47,1,0x20)
	ROM_FILL(0x0c48,1,0xcd)
	ROM_FILL(0x0c49,1,0xc7)
	ROM_FILL(0x0c4a,1,0xcb)
	ROM_FILL(0x0c4b,1,0xc1)
	ROM_FILL(0x0c4c,1,0x10)
	ROM_FILL(0x0c4d,1,0xf7)
	ROM_FILL(0x0c4e,1,0x00)
	ROM_FILL(0x0c4f,1,0x00)
#endif
	ROM_SYSTEM_BIOS(1, "v37", "V3.7")
	ROMX_LOAD("s80-v37.u26",  0x0000, 0x1000, CRC(46043035) SHA1(1765105df4e4af83d56cafb88e158ed462d4709e), ROM_BIOS(1) )
	ROMX_LOAD("s80-v37.u33",  0x1000, 0x1000, CRC(afb52b15) SHA1(0a2c25834074ce44bf12ac8532b4add492bcf950), ROM_BIOS(1) )
	ROMX_LOAD("s80-v37.u42",  0x2000, 0x1000, CRC(7344b27a) SHA1(f43fc47ddb5c12bffffa63488301cd5eb386cc9a), ROM_BIOS(1) )

	ROM_SYSTEM_BIOS(2, "8r2", "8R2")
	ROMX_LOAD("s80-8r2.u26",  0x0000, 0x1000, CRC(1e166c8c) SHA1(15647614be9300cdd2956da913e83234c36b36a9), ROM_BIOS(2) )
	ROMX_LOAD("s80-8r0.u33",  0x1000, 0x1000, CRC(9765793e) SHA1(4951b127888c1f3153004cc9fb386099b408f52c), ROM_BIOS(2) )
	ROMX_LOAD("s80-8r0.u42",  0x2000, 0x1000, CRC(5f65d94b) SHA1(fe26b54dec14e1c4911d996c9ebd084a38dcb691), ROM_BIOS(2) )

	ROM_SYSTEM_BIOS(3, "8r3", "8R3")
	ROMX_LOAD("s80-8r3.u26",  0x0000, 0x1000, CRC(ee7dd90b) SHA1(c53f8eef82e8f943642f6ddfc2cb1bfdc32d25ca), ROM_BIOS(3) )
	ROMX_LOAD("s80-8r0.u33",  0x1000, 0x1000, CRC(9765793e) SHA1(4951b127888c1f3153004cc9fb386099b408f52c), ROM_BIOS(3) )
	ROMX_LOAD("s80-8r0.u42",  0x2000, 0x1000, CRC(5f65d94b) SHA1(fe26b54dec14e1c4911d996c9ebd084a38dcb691), ROM_BIOS(3) )

	ROM_SYSTEM_BIOS(4, "8r4", "8R4")
	ROMX_LOAD("s80-8r4.u26",  0x0000, 0x1000, CRC(637d001d) SHA1(f26b5ecc33fd44b05b1f199d79e0f072ec8d0e23), ROM_BIOS(4) )
	ROMX_LOAD("s80-8r0.u33",  0x1000, 0x1000, CRC(9765793e) SHA1(4951b127888c1f3153004cc9fb386099b408f52c), ROM_BIOS(4) )
	ROMX_LOAD("s80-8r0.u42",  0x2000, 0x1000, CRC(5f65d94b) SHA1(fe26b54dec14e1c4911d996c9ebd084a38dcb691), ROM_BIOS(4) )

	ROM_SYSTEM_BIOS(5, "8r5", "8R5")
	ROMX_LOAD("s80-8r5.u26",  0x0000, 0x1000, CRC(294f217c) SHA1(f352d54e84e94bf299726dc3af4eb7b2d06d317c), ROM_BIOS(5) )
	ROMX_LOAD("s80-8r0.u33",  0x1000, 0x1000, CRC(9765793e) SHA1(4951b127888c1f3153004cc9fb386099b408f52c), ROM_BIOS(5) )
	ROMX_LOAD("s80-8r0.u42",  0x2000, 0x1000, CRC(5f65d94b) SHA1(fe26b54dec14e1c4911d996c9ebd084a38dcb691), ROM_BIOS(5) )

	ROM_REGION(0x1800, "chargen", 0)
	ROM_LOAD("super80e.u27",  0x0000, 0x1000, CRC(ebe763a7) SHA1(ffaa6d6a2c5dacc5a6651514e6707175a32e83e8) )
	ROM_LOAD("super80d.u27",  0x1000, 0x0800, CRC(cb4c81e2) SHA1(8096f21c914fa76df5d23f74b1f7f83bd8645783) )
ROM_END

ROM_START( super80r )
	ROM_REGION( 0x3000, "maincpu", 0 )
	ROM_SYSTEM_BIOS(0, "super80r", "MCE (original)")
	ROMX_LOAD("super80r.u26", 0x0000, 0x1000, CRC(01bb6406) SHA1(8e275ecf5141b93f86e45ff8a735b965ea3e8d44), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "super80s", "MCE (upgraded)")
	ROMX_LOAD("super80s.u26", 0x0000, 0x1000, CRC(3e29d307) SHA1(b3f4667633e0a4eb8577e39b5bd22e1f0bfbc0a9), ROM_BIOS(1))

	ROM_LOAD("super80.u33",   0x1000, 0x1000, CRC(cf8020a8) SHA1(2179a61f80372cd49e122ad3364773451531ae85) )
	ROM_LOAD("super80.u42",   0x2000, 0x1000, CRC(a1c6cb75) SHA1(d644ca3b399c1a8902f365c6095e0bbdcea6733b) )

	ROM_REGION(0x0800, "chargen", 0)    // 2716 eprom
	ROM_LOAD("s80hmce.ic24",  0x0000, 0x0800, CRC(a6488a1e) SHA1(7ba613d70a37a6b738dcd80c2bb9988ff1f011ef) )
ROM_END

ROM_START( super80v )
	ROM_REGION( 0x3000, "maincpu", 0 )
	ROM_LOAD("s80-v37v.u26",  0x0000, 0x1000, CRC(01e0c0dd) SHA1(ef66af9c44c651c65a21d5bda939ffa100078c08) )
	ROM_LOAD("s80-v37v.u33",  0x1000, 0x1000, CRC(812ad777) SHA1(04f355bea3470a7d9ea23bb2811f6af7d81dc400) )
	ROM_LOAD("s80-v37v.u42",  0x2000, 0x1000, CRC(e02e736e) SHA1(57b0264c805da99234ab5e8e028fca456851a4f9) )

	ROM_REGION(0x0800, "chargen", 0)    // 2716 eprom
	ROM_LOAD("s80hmce.ic24",  0x0000, 0x0800, CRC(a6488a1e) SHA1(7ba613d70a37a6b738dcd80c2bb9988ff1f011ef) )
ROM_END

/*    YEAR  NAME      PARENT COMPAT MACHINE   INPUT     CLASS          INIT          COMPANY                   FULLNAME */
COMP( 1981, super80,  0,       0,   super80,  super80,  super80_state,  empty_init, "Dick Smith Electronics", "Super-80 (V1.2)" , MACHINE_SUPPORTS_SAVE )
COMP( 1981, super80d, super80, 0,   super80d, super80d, super80_state,  empty_init, "Dick Smith Electronics", "Super-80 (V2.2)" , MACHINE_SUPPORTS_SAVE )
COMP( 1981, super80e, super80, 0,   super80e, super80e, super80_state,  empty_init, "Dick Smith Electronics", "Super-80 (El Graphix 4)" , MACHINE_UNOFFICIAL | MACHINE_SUPPORTS_SAVE )
COMP( 1981, super80m, super80, 0,   super80m, super80m, super80_state,  empty_init, "Dick Smith Electronics", "Super-80 (with colour)" , MACHINE_UNOFFICIAL | MACHINE_SUPPORTS_SAVE )
COMP( 1981, super80r, super80, 0,   super80r, super80r, super80r_state, empty_init, "Dick Smith Electronics", "Super-80 (with VDUEB)" , MACHINE_UNOFFICIAL | MACHINE_SUPPORTS_SAVE )
COMP( 1981, super80v, super80, 0,   super80v, super80v, super80v_state, empty_init, "Dick Smith Electronics", "Super-80 (with enhanced VDUEB)" , MACHINE_UNOFFICIAL | MACHINE_SUPPORTS_SAVE )




superslave.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/**************************************************************************

Monitor commands
Dxxxx,yyyy      = Dump memory
Fxxxx,yyyy,zz   = Fill memory
Gxxxx           = Goto
Ixx             = In port
Lxxxx           = Load
Mxxxx,yyyy,zzzz = Move x-y to z
Oxx,yy          = Out port
-               = Edit memory
.               = Edit memory


    TODO:

    - all

****************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
//#include "bus/s100/s100.h"
#include "cpu/z80/z80.h"
#include "machine/z80daisy.h"
#include "machine/am9519.h"
#include "machine/com8116.h"
#include "machine/ram.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"


namespace {

#define Z80_TAG         "u45"
#define Z80DART_0_TAG   "u14"
#define Z80DART_1_TAG   "u30"
#define Z80PIO_TAG      "u43"
#define AM9519_TAG      "u13"
#define BR1941_TAG      "u12"
#define RS232_A_TAG     "rs232a"
#define RS232_B_TAG     "rs232b"
#define RS232_C_TAG     "rs232c"
#define RS232_D_TAG     "rs232d"

class superslave_state : public driver_device
{
public:
	superslave_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, Z80_TAG)
		, m_dbrg(*this, BR1941_TAG)
		, m_ram(*this, RAM_TAG)
		, m_rs232a(*this, RS232_A_TAG)
		, m_rs232b(*this, RS232_B_TAG)
		, m_rs232c(*this, RS232_C_TAG)
		, m_rs232d(*this, RS232_D_TAG)
		, m_rom(*this, Z80_TAG)
		, m_memctrl(0x01)
		, m_cmd(0x01)
	{ }

	void superslave(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	void superslave_io(address_map &map) ATTR_COLD;
	void superslave_mem(address_map &map) ATTR_COLD;

	uint8_t read(offs_t offset);
	void write(offs_t offset, uint8_t data);
	void memctrl_w(uint8_t data);
	uint8_t status_r();
	void cmd_w(uint8_t data);

	required_device<z80_device> m_maincpu;
	required_device<com8116_device> m_dbrg;
	required_device<ram_device> m_ram;
	required_device<rs232_port_device> m_rs232a;
	required_device<rs232_port_device> m_rs232b;
	required_device<rs232_port_device> m_rs232c;
	required_device<rs232_port_device> m_rs232d;
	required_memory_region m_rom;

	uint8_t m_memctrl;
	uint8_t m_cmd;
};



//**************************************************************************
//  READ/WRITE HANDLERS
//**************************************************************************

//-------------------------------------------------
//  read -
//-------------------------------------------------

uint8_t superslave_state::read(offs_t offset)
{
	uint8_t data = 0;

	offs_t boundary = 0xc000 | ((m_memctrl & 0xf0) << 6);

	if ((offset < 0x1000) && BIT(m_cmd, 0))
	{
		data = m_rom->base()[offset & 0x7ff];
	}
	else if (offset < boundary)
	{
		if (BIT(m_memctrl, 0))
		{
			data = m_ram->pointer()[offset];
		}
		else if (BIT(m_memctrl, 1) && (m_ram->size() > 0x10000))
		{
			data = m_ram->pointer()[0x10000 | offset];
		}
	}
	else
	{
		data = m_ram->pointer()[offset];
	}

	return data;
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

void superslave_state::write(offs_t offset, uint8_t data)
{
	offs_t boundary = 0xc000 | ((m_memctrl & 0xf0) << 6);

	if (offset < boundary)
	{
		if (BIT(m_memctrl, 0))
		{
			m_ram->pointer()[offset] = data;
		}
		else if (BIT(m_memctrl, 1) && (m_ram->size() > 0x10000))
		{
			m_ram->pointer()[0x10000 | offset] = data;
		}
	}
	else
	{
		m_ram->pointer()[offset] = data;
	}
}


//-------------------------------------------------
//  memctrl_w -
//-------------------------------------------------

void superslave_state::memctrl_w(uint8_t data)
{
	/*

	    bit     description

	    0       Memory bank 0 on
	    1       Memory bank 1 on
	    2
	    3
	    4       Unswitched memory boundary bit 0
	    5       Unswitched memory boundary bit 1
	    6       Unswitched memory boundary bit 2
	    7       Unswitched memory boundary bit 3

	*/

	m_memctrl = data;
}


//-------------------------------------------------
//  status_r -
//-------------------------------------------------

uint8_t superslave_state::status_r()
{
	/*

	    bit     description

	    0       Sense Switch (0=closed)
	    1       Parity error (1=error)
	    2       Syncerr (1=error)
	    3       Service Request
	    4       Data Set Ready 3
	    5       Data Set Ready 2
	    6       Data Set Ready 1
	    7       Data Set Ready 0

	*/

	uint8_t data = 1;

	// RS-232
	data |= m_rs232d->dsr_r() << 4;
	data |= m_rs232c->dsr_r() << 5;
	data |= m_rs232b->dsr_r() << 6;
	data |= m_rs232a->dsr_r() << 7;

	return data;
}


//-------------------------------------------------
//  cmd_w -
//-------------------------------------------------

void superslave_state::cmd_w(uint8_t data)
{
	/*

	    bit     description

	    0       Prom enable (1=enabled)
	    1       Clear Parity (1=cleared)
	    2       Clear Syncerr (1=cleared)
	    3       Service Request
	    4       Wait Protocol (0=Wait)
	    5       Command bit 5
	    6       Command bit 6
	    7       Command bit 7

	*/

	m_cmd = data;
}



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( superslave_mem )
//-------------------------------------------------

void superslave_state::superslave_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(superslave_state::read), FUNC(superslave_state::write));
}


//-------------------------------------------------
//  ADDRESS_MAP( superslave_io )
//-------------------------------------------------

void superslave_state::superslave_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).rw(Z80DART_0_TAG, FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
	map(0x0c, 0x0f).rw(Z80DART_1_TAG, FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
	map(0x10, 0x10).mirror(0x03).w(BR1941_TAG, FUNC(com8116_device::stt_str_w));
	map(0x14, 0x17).rw(Z80PIO_TAG, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x18, 0x18).mirror(0x02).rw(AM9519_TAG, FUNC(am9519_device::data_r), FUNC(am9519_device::data_w));
	map(0x19, 0x19).mirror(0x02).rw(AM9519_TAG, FUNC(am9519_device::stat_r), FUNC(am9519_device::cmd_w));
	map(0x1d, 0x1d).w(FUNC(superslave_state::memctrl_w));
	map(0x1e, 0x1e).noprw(); // master communications
	map(0x1f, 0x1f).rw(FUNC(superslave_state::status_r), FUNC(superslave_state::cmd_w));
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( superslave )
//-------------------------------------------------

static INPUT_PORTS_START( superslave )
	PORT_START("SW1")
	PORT_DIPNAME( 0x7f, 0x38, "Slave Address" ) PORT_DIPLOCATION("SW1:1,2,3,4,5,6,7")
	PORT_DIPSETTING(    0x38, "70H (0)" )
	PORT_DIPSETTING(    0x39, "72H (1)" )
	PORT_DIPSETTING(    0x3a, "74H (2)" )
	PORT_DIPSETTING(    0x3b, "76H (3)" )
	PORT_DIPSETTING(    0x3c, "78H (4)" )
	PORT_DIPSETTING(    0x3d, "7AH (5)" )
	PORT_DIPSETTING(    0x3e, "7CH (6)" )
	PORT_DIPSETTING(    0x3f, "7EH (7)" )
	PORT_DIPSETTING(    0x40, "80H (8)" )
	PORT_DIPSETTING(    0x41, "82H (9)" )
	PORT_DIPSETTING(    0x42, "84H (10)" )
	PORT_DIPSETTING(    0x43, "86H (11)" )
	PORT_DIPSETTING(    0x44, "88H (12)" )
	PORT_DIPSETTING(    0x45, "8AH (13)" )
	PORT_DIPSETTING(    0x46, "8CH (14)" )
	PORT_DIPSETTING(    0x47, "8EH (15)" )
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END


//-------------------------------------------------
//  z80_daisy_config superslave_daisy_chain
//-------------------------------------------------

static const z80_daisy_config superslave_daisy_chain[] =
{
	{ Z80DART_0_TAG },
	{ Z80DART_1_TAG },
	{ Z80PIO_TAG },
	{ nullptr }
};



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  machine_start()
//-------------------------------------------------

void superslave_state::machine_start()
{
	// state saving
	save_item(NAME(m_memctrl));
	save_item(NAME(m_cmd));
}


void superslave_state::machine_reset()
{
	m_memctrl = 0x01;
	m_cmd = 0x01;
}



//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

//-------------------------------------------------
//  machine_config( superslave )
//-------------------------------------------------

void superslave_state::superslave(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, XTAL(8'000'000)/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &superslave_state::superslave_mem);
	m_maincpu->set_addrmap(AS_IO, &superslave_state::superslave_io);
	m_maincpu->set_daisy_config(superslave_daisy_chain);

	// devices
	am9519_device &am9519(AM9519(config, AM9519_TAG, 0));
	am9519.out_int_callback().set_inputline(Z80_TAG, INPUT_LINE_IRQ0);

	z80pio_device& pio(Z80PIO(config, Z80PIO_TAG, XTAL(8'000'000)/2));
	pio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80dart_device& dart0(Z80DART(config, Z80DART_0_TAG, XTAL(8'000'000)/2));
	dart0.out_txda_callback().set(m_rs232a, FUNC(rs232_port_device::write_txd));
	dart0.out_dtra_callback().set(m_rs232a, FUNC(rs232_port_device::write_dtr));
	dart0.out_rtsa_callback().set(m_rs232a, FUNC(rs232_port_device::write_rts));
	dart0.out_txdb_callback().set(m_rs232b, FUNC(rs232_port_device::write_txd));
	dart0.out_dtrb_callback().set(m_rs232b, FUNC(rs232_port_device::write_dtr));
	dart0.out_rtsb_callback().set(m_rs232b, FUNC(rs232_port_device::write_rts));
	dart0.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	RS232_PORT(config, m_rs232a, default_rs232_devices, "terminal");
	m_rs232a->rxd_handler().set(Z80DART_0_TAG, FUNC(z80dart_device::rxa_w));
	m_rs232a->dcd_handler().set(Z80DART_0_TAG, FUNC(z80dart_device::dcda_w));
	m_rs232a->cts_handler().set(Z80DART_0_TAG, FUNC(z80dart_device::ctsa_w));
	m_rs232a->set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	RS232_PORT(config, m_rs232b, default_rs232_devices, nullptr);
	m_rs232b->rxd_handler().set(Z80DART_0_TAG, FUNC(z80dart_device::rxb_w));
	m_rs232b->dcd_handler().set(Z80DART_0_TAG, FUNC(z80dart_device::dcdb_w));
	m_rs232b->cts_handler().set(Z80DART_0_TAG, FUNC(z80dart_device::ctsb_w));

	z80dart_device& dart1(Z80DART(config, Z80DART_1_TAG, XTAL(8'000'000)/2));
	dart1.out_txda_callback().set(m_rs232c, FUNC(rs232_port_device::write_txd));
	dart1.out_dtra_callback().set(m_rs232c, FUNC(rs232_port_device::write_dtr));
	dart1.out_rtsa_callback().set(m_rs232c, FUNC(rs232_port_device::write_rts));
	dart1.out_txdb_callback().set(m_rs232d, FUNC(rs232_port_device::write_txd));
	dart1.out_dtrb_callback().set(m_rs232d, FUNC(rs232_port_device::write_dtr));
	dart1.out_rtsb_callback().set(m_rs232d, FUNC(rs232_port_device::write_rts));
	dart1.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	RS232_PORT(config, m_rs232c, default_rs232_devices, nullptr);
	m_rs232c->rxd_handler().set(Z80DART_1_TAG, FUNC(z80dart_device::rxa_w));
	m_rs232c->dcd_handler().set(Z80DART_1_TAG, FUNC(z80dart_device::dcda_w));
	m_rs232c->cts_handler().set(Z80DART_1_TAG, FUNC(z80dart_device::ctsa_w));

	RS232_PORT(config, m_rs232d, default_rs232_devices, nullptr);
	m_rs232d->rxd_handler().set(Z80DART_1_TAG, FUNC(z80dart_device::rxb_w));
	m_rs232d->dcd_handler().set(Z80DART_1_TAG, FUNC(z80dart_device::dcdb_w));
	m_rs232d->cts_handler().set(Z80DART_1_TAG, FUNC(z80dart_device::ctsb_w));

	COM8116(config, m_dbrg, XTAL(5'068'800));
	m_dbrg->fr_handler().set(Z80DART_0_TAG, FUNC(z80dart_device::txca_w));
	m_dbrg->fr_handler().append(Z80DART_0_TAG, FUNC(z80dart_device::rxca_w));
	m_dbrg->fr_handler().append(Z80DART_1_TAG, FUNC(z80dart_device::txca_w));
	m_dbrg->fr_handler().append(Z80DART_1_TAG, FUNC(z80dart_device::rxca_w));
	m_dbrg->ft_handler().set(Z80DART_0_TAG, FUNC(z80dart_device::rxtxcb_w));
	m_dbrg->ft_handler().append(Z80DART_1_TAG, FUNC(z80dart_device::rxtxcb_w));

	// internal ram
	RAM(config, RAM_TAG).set_default_size("64K").set_extra_options("128K");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( superslv )
//-------------------------------------------------

ROM_START( superslv )
	ROM_REGION( 0x800, Z80_TAG, 0 )
	ROM_LOAD( "adcs6_slave_v3.2.bin", 0x000, 0x800, CRC(7f39322d) SHA1(2e9621e09378a1bb6fc05317bb58ae7865e52744) )
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME      PARENT  COMPAT  MACHINE     INPUT       CLASS             INIT        COMPANY                         FULLNAME        FLAGS
COMP( 1983, superslv, 0,      0,      superslave, superslave, superslave_state, empty_init, "Advanced Digital Corporation", "Super Slave",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



superstar.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

SciSys Superstar / Turbostar

Starting from Turbostar 432, SciSys started using the "Kasparov Chess Computer"
brand, and they added the "Kasparov" prefix to titles shortly afterwards.

Hardware notes (Superstar 28K):
- PCB label: YO1C-PE-017 REV2
- R6502AP @ ~2MHz (no XTAL, clock from RC circuit?)
- 4KB RAM (2*HM6116P-4)
- 24KB ROM (3*M5L2764K)
- TTL, buzzer, 28 LEDs, 8*8 chessboard buttons

Turbostar 432:
- PCB label: SUPERSTAR REV-3
- R65C02P4 @ 4MHz
- 4KB RAM (2*HM6116P-4)
- 32KB ROM (custom label), extension ROM slot

Superstar 36K:
- PCB label: YO1CD-PE-006 REV3
- SYU6502A @ 2MHz
- 4KB RAM (2*HM6116P-3)
- 32KB ROM (custom label, same program as tstar432), extension ROM slot
- piezo is very low pitch, this is normal

There are 2 versions of Turbostar 432, the 2nd Kasparov brand version has
a lighter shade and the top-right is gray instead of red. It came with the
KSO ROM included.

I.C.D. (a reseller in USA, NY) also sold a version overclocked to 5.53MHz,
and named it Turbostar 540+. The ROM is unmodified, so the internal chess
clock would run too fast.

TODO:
- verify sstar28k CPU speed
- do other I.C.D. Turbostar overclocked versions exist? there are references
  online for 540, 640, 740, but no evidence that they exist

*******************************************************************************/

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/m6502/m6502.h"
#include "cpu/m6502/r65c02.h"
#include "machine/clock.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "softlist_dev.h"
#include "speaker.h"

// internal artwork
#include "saitek_sstar28k.lh"
#include "saitek_tstar432.lh"


namespace {

class star_state : public driver_device
{
public:
	star_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_nmi_clock(*this, "nmi_clock"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	DECLARE_INPUT_CHANGED_MEMBER(change_cpu_freq);

	// machine configs
	void sstar28k(machine_config &config);
	void tstar432(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<clock_device> m_nmi_clock;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<2> m_inputs;

	u8 m_inp_mux = 0;

	// address maps
	void sstar28k_map(address_map &map) ATTR_COLD;
	void tstar432_map(address_map &map) ATTR_COLD;

	// I/O handlers
	void control_w(u8 data);
	u8 input_r();
};

void star_state::machine_start()
{
	save_item(NAME(m_inp_mux));
}

INPUT_CHANGED_MEMBER(star_state::change_cpu_freq)
{
	static const XTAL xtal[3] = { 2_MHz_XTAL, 4_MHz_XTAL, 5.5296_MHz_XTAL };
	u32 freq = xtal[newval % 3].value();
	m_maincpu->set_unscaled_clock(freq);

	// change NMI frequency as well
	u32 div = (newval == 0) ? 0x2000 : 0x4000;
	m_nmi_clock->set_clock(freq / div);
}



/*******************************************************************************
    I/O
*******************************************************************************/

void star_state::control_w(u8 data)
{
	// d0-d3: input mux, led select
	m_inp_mux = data & 0xf;

	// d4-d6: led data
	m_display->matrix(1 << m_inp_mux, ~data >> 4 & 7);

	// d7: speaker out
	m_dac->write(BIT(data, 7));
}

u8 star_state::input_r()
{
	u8 data = 0;

	// d0-d7: multiplexed inputs
	// read chessboard sensors
	if (m_inp_mux < 8)
		data = m_board->read_file(m_inp_mux);

	// read other buttons
	else if (m_inp_mux < 10)
		data = m_inputs[m_inp_mux - 8]->read();

	return data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void star_state::sstar28k_map(address_map &map)
{
	map(0x0000, 0x07ff).mirror(0x1800).ram();
	map(0x2000, 0x27ff).mirror(0x1800).ram();
	map(0x4000, 0x4000).w(FUNC(star_state::control_w));
	map(0x6000, 0x6000).r(FUNC(star_state::input_r));
	map(0xa000, 0xffff).rom();
}

void star_state::tstar432_map(address_map &map)
{
	sstar28k_map(map);

	map(0x4000, 0x5fff).r("extrom", FUNC(generic_slot_device::read_rom));
	map(0x8000, 0x9fff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( sstar28k )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Sound")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Display Move")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Set Up")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Multi Move")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Color")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Replay")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Move")
INPUT_PORTS_END

static INPUT_PORTS_START( tstar432 )
	PORT_INCLUDE( sstar28k )

	PORT_START("CPU")
	PORT_CONFNAME( 0x03, 0x01, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(star_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "2MHz (Superstar 36K)" )
	PORT_CONFSETTING(    0x01, "4MHz (Turbostar 432)" )
	PORT_CONFSETTING(    0x02, "5.53MHz (Turbostar 540+)" )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void star_state::sstar28k(machine_config &config)
{
	// basic machine hardware
	M6502(config, m_maincpu, 2'000'000); // no XTAL
	m_maincpu->set_addrmap(AS_PROGRAM, &star_state::sstar28k_map);

	CLOCK(config, m_nmi_clock, 2'000'000 / 0x2000); // 4020 Q13
	m_nmi_clock->signal_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(10, 3);
	config.set_default_layout(layout_saitek_sstar28k);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void star_state::tstar432(machine_config &config)
{
	sstar28k(config);

	// basic machine hardware
	R65C02(config.replace(), m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &star_state::tstar432_map);

	CLOCK(config.replace(), m_nmi_clock, 4_MHz_XTAL / 0x4000); // 4020 Q14
	m_nmi_clock->signal_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);

	config.set_default_layout(layout_saitek_tstar432);

	// extension rom
	GENERIC_SOCKET(config, "extrom", generic_plain_slot, "saitek_kso");
	SOFTWARE_LIST(config, "cart_list").set_original("saitek_kso");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( sstar28k )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("yo1c-v25_a0.u6", 0xa000, 0x2000, CRC(3c4bef09) SHA1(50349820d131db0138bd5dc4b62f38cc3aa1d7db) ) // M5L2764K
	ROM_LOAD("yo1c-v21_c0.u4", 0xc000, 0x2000, CRC(aae43b1b) SHA1(9acef9593f19ec3a6e9a671e82196d7bd054960e) ) // "
	ROM_LOAD("yo1c-v25_e0.u5", 0xe000, 0x2000, CRC(371b81fe) SHA1(c08dd0de8eebd7c1ed2d2281bf0241a83ee0f391) ) // "
ROM_END

ROM_START( tstar432 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("yo1d-j.u6", 0x8000, 0x8000, CRC(aa993096) SHA1(06db69a284eaf022b26e1087e09d8d459d270d03) )
ROM_END

ROM_START( tstar432a ) // dumped from a Superstar 36K
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("yo1d.u6", 0x8000, 0x8000, CRC(270c9a81) SHA1(5c9ef3a140651d7c9d9b801f2524cb93b0f92bb4) )
ROM_END

ROM_START( tstar432b ) // only 1 byte difference in the opening book (plus checksum), compared to tstar432a
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("tstar432a_80.bin", 0x8000, 0x4000, CRC(4f19f20c) SHA1(b921a440e8ab09a2a5e5f85d8aad3e4d7d815182) )
	ROM_LOAD("tstar432a_c0.bin", 0xc000, 0x2000, CRC(aae43b1b) SHA1(9acef9593f19ec3a6e9a671e82196d7bd054960e) )
	ROM_LOAD("tstar432a_e0.bin", 0xe000, 0x2000, CRC(6c504920) SHA1(588e23c9daff8a301c2f3d4f0c3fe62709f3accc) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT    COMPAT  MACHINE   INPUT     CLASS       INIT        COMPANY, FULLNAME, FLAGS
SYST( 1983, sstar28k,  0,        0,      sstar28k, sstar28k, star_state, empty_init, "SciSys / Heuristic Software", "Superstar 28K", MACHINE_SUPPORTS_SAVE )

SYST( 1985, tstar432,  0,        0,      tstar432, tstar432, star_state, empty_init, "SciSys / Heuristic Software", "Turbostar 432 (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1985, tstar432a, tstar432, 0,      tstar432, tstar432, star_state, empty_init, "SciSys / Heuristic Software", "Turbostar 432 (set 2)", MACHINE_SUPPORTS_SAVE )
SYST( 1985, tstar432b, tstar432, 0,      tstar432, tstar432, star_state, empty_init, "SciSys / Heuristic Software", "Turbostar 432 (set 3)", MACHINE_SUPPORTS_SAVE )



supracan.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Angelo Salese, Ryan Holtz
/**************************************************************************************************

Super A'Can (c) 1995 Funtech

References:
- https://gist.github.com/evadot/66cfdb8891544b41b4c9
- https://upload.wikimedia.org/wikipedia/commons/a/a6/Super-ACan-motherboard-flat.jpg
- https://github.com/angelosa/hw_docs/blob/main/funtech_superacan/pergame.md


===================================================================================================

INFO:

    The system unit contains a reset button.

    Controllers:
    - 4 directional buttons
    - A, B, X, Y, buttons
    - Start, select buttons
    - L, R shoulder buttons

STATUS:

    The driver is begging for a re-write or at least a split into video/supracan.c.  It will happen eventually.

    Sound chip is completely custom, and partially implemented.

    There are 6 interrupt sources on the 6502 side, all of which use the IRQ line.
    The register at 0x411 is bitmapped to indicate what source(s) are active.
    In priority order from most to least important, they are:

    411 value  How acked                     Notes
    0x40       read reg 0x16 of sound chip   used for DMA-driven sample playback. Register 0x16 may contain which DMA-driven samples are active.
    0x04       read at 0x405                 latch 1?  0xcd is magic value
    0x08       read at 0x404                 latch 2?  0xcd is magic value
    0x10       read at 0x409                 unknown, dispatched but not used in startup 6502 code
    0x20       read at 0x40a                 IRQ request from 68k, flags data available in shared-RAM mailbox
    0x80       read reg 0x14 of sound chip   depends on reg 0x14 of sound chip & 0x40: if not set writes 0x8f to reg 0x14,
                                             otherwise writes 0x4f to reg 0x14 and performs additional processing

    Known unemulated graphical effects and issues (outdated, check hash/supracan.xml instead):
    - All: Sprite sizing is still imperfect.
    - All: Sprites need to be converted to use scanline rendering for proper clipping.
    - All: Improperly-emulated 1bpp ROZ mode, used by the Super A'Can BIOS logo.
    - All: Unimplemented ROZ scaling tables, used by the Super A'Can BIOS logo and Speedy Dragon intro, among others.
    - All: Priorities are largely unknown.
    - C.U.G.: Gameplay backgrounds are broken.
    - Sango Fighter: Possible missing masking on the upper edges of the screen during gameplay.
    - Sango Fighter: Raster effects off by 1 line (btanb)
    - Sango Fighter: Specifies tiles out of range of video ram??
    - Speedy Dragon: Backgrounds are broken (wrong tile bank/region).
    - Super Taiwanese Baseball League: Does not boot, uses an unemulated DMA type
    - Super Taiwanese Baseball League: Missing window effect applied on tilemaps?
    - The Son of Evil: Many graphical issues.
    - Visible area, looks like it should be 224 pixels high at most, most games need 8 off the top and 8 off the bottom (or a global scroll)
      Sango looks like it needs 16 off the bottom instead
      Visible area is almost certainly 224 as Son of Evil has an explicit check in the vblank handler

    - All: are ALL the layers ROZ capable??

**************************************************************************************************/

#include "emu.h"

#include "bus/supracan/rom.h"
#include "bus/supracan/slot.h"
#include "cpu/m68000/m68000.h"
#include "cpu/m6502/w65c02.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "tilemap.h"
#include "umc6619_sound.h"
#include "umc6650.h"

#define LOG_UNKNOWNS    (1U << 1)
#define LOG_DMA         (1U << 2)
#define LOG_SPRDMA      (1U << 3)
#define LOG_SPRITES     (1U << 4)
#define LOG_TILEMAP0    (1U << 5)
#define LOG_TILEMAP1    (1U << 6)
#define LOG_TILEMAP2    (1U << 7)
#define LOG_ROZ         (1U << 8)
#define LOG_HFVIDEO     (1U << 9)
#define LOG_IRQS        (1U << 10)
#define LOG_SOUND       (1U << 11)
#define LOG_HFUNKNOWNS  (1U << 12)
#define LOG_68K_SOUND   (1U << 13)
#define LOG_CONTROLS    (1U << 14)
#define LOG_VIDEO       (LOG_SPRDMA | LOG_SPRITES | LOG_TILEMAP0 | LOG_TILEMAP1 | LOG_TILEMAP2 | LOG_ROZ)
#define LOG_ALL         (LOG_UNKNOWNS | LOG_HFUNKNOWNS | LOG_DMA | LOG_VIDEO | LOG_HFVIDEO | LOG_IRQS | LOG_SOUND | LOG_68K_SOUND | LOG_CONTROLS)
#define LOG_DEFAULT     (LOG_ALL & ~(LOG_HFVIDEO | LOG_HFUNKNOWNS))

#define VERBOSE         (LOG_GENERAL | LOG_UNKNOWNS | LOG_DMA | LOG_HFUNKNOWNS)
#include "logmacro.h"


namespace {

// NOTE: same as sega/segac2.cpp XL2
static constexpr XTAL U13_CLOCK = XTAL(53'693'175);
// TODO: bump to 4 after conversion of video_r/_w to um6618_map
static constexpr int ROZ_LAYER_NUMBER = 3;

#define DRAW_DEBUG_ROZ          (0)

#define DRAW_DEBUG_UNK_SPRITE   (0)

#define DEBUG_PRIORITY          (0)
#define DEBUG_PRIORITY_INDEX    (0) // 0-3

class supracan_state : public driver_device
{
public:
	supracan_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_soundcpu(*this, "soundcpu")
		, m_cart(*this, "cartslot")
		, m_lockout(*this, "lockout")
		, m_internal68(*this, "internal68")
		, m_internal6502(*this, "internal6502")
		, m_main_loview(*this, "main_loview")
		, m_main_hiview(*this, "main_hiview")
		, m_vram(*this, "vram")
		, m_soundram(*this, "soundram")
		, m_sound(*this, "acansnd")
		, m_gfxdecode(*this, "gfxdecode")
		, m_screen(*this, "screen")
		, m_pad(*this, "P%u", 1U)
	{
	}

	void supracan(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

private:
	void main_map(address_map &map) ATTR_COLD;
	void sound_map(address_map &map) ATTR_COLD;

	uint16_t _68k_soundram_r(offs_t offset, uint16_t mem_mask = ~0);
	void _68k_soundram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint8_t _6502_soundmem_r(offs_t offset);
	void _6502_soundmem_w(offs_t offset, uint8_t data);

	template <unsigned ch> void dma_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

	uint16_t video_r(offs_t offset, uint16_t mem_mask = 0);
	void video_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	void vram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

	uint8_t sound_ram_read(offs_t offset);

	struct dma_regs_t
	{
		uint32_t source[2]{};
		uint32_t dest[2]{};
		uint16_t count[2]{};
		uint16_t control[2]{};
	};

	struct sprdma_regs_t
	{
		uint32_t src = 0;
		uint16_t src_inc = 0;
		uint32_t dst = 0;
		uint16_t dst_inc = 0;
		uint16_t count = 0;
		uint16_t control = 0;
	};

	required_device<cpu_device> m_maincpu;
	required_device<cpu_device> m_soundcpu;
	required_device<superacan_cart_slot_device> m_cart;
	required_device<umc6650_device> m_lockout;
	required_region_ptr<uint16_t> m_internal68;
	required_region_ptr<uint8_t> m_internal6502;
	memory_view m_main_loview;
	memory_view m_main_hiview;

	required_shared_ptr<uint16_t> m_vram;
	required_shared_ptr<uint8_t> m_soundram;
	required_device<umc6619_sound_device> m_sound;

	required_device<gfxdecode_device> m_gfxdecode;
	required_device<screen_device> m_screen;

	required_ioport_array<2> m_pad;

	uint16_t m_sound_cpu_ctrl = 0;
	uint8_t m_soundcpu_irq_enable = 0;
	uint8_t m_soundcpu_irq_source = 0;
	uint8_t m_sound_cpu_shift_ctrl = 0;
	uint8_t m_sound_cpu_shift_regs[2]{};
	uint16_t m_latched_controls[2]{};
	uint8_t m_sound_status = 0;
	uint8_t m_sound_reg_addr = 0;

	emu_timer *m_video_timer = nullptr;
	emu_timer *m_line_on_timer = nullptr;
	emu_timer *m_line_off_timer = nullptr;

	std::vector<uint8_t> m_vram_addr_swapped{};

	uint16_t m_sprite_count = 0;
	uint32_t m_sprite_base_addr = 0;
	uint8_t m_sprite_mono_color = 0;
	uint8_t m_sprite_flags = 0;

	dma_regs_t m_dma_regs;
	sprdma_regs_t m_sprdma_regs;

	uint16_t m_video_flags = 0;
	uint32_t m_tilemap_base_addr[3]{};
	int m_tilemap_scrollx[3]{};
	int m_tilemap_scrolly[3]{};
	uint16_t m_tilemap_flags[3]{};
	uint16_t m_tilemap_mode[3]{};
	uint16_t m_tilemap_tile_mode[3]{};
	uint16_t m_tilemap_linescrollx_addr[3]{};
	uint16_t m_tilemap_lineselect_addr[3]{};

	uint16_t m_window_control[2]{};
	uint16_t m_window_start_addr[2]{};
	uint16_t m_window_scrollx[2]{};
	uint16_t m_window_scrolly[2]{};

	uint32_t m_roz_base_addr = 0;
	uint16_t m_roz_mode = 0;
	uint16_t m_roz_tile_mode = 0;
	uint32_t m_roz_scrollx = 0;
	uint32_t m_roz_scrolly = 0;
	uint16_t m_roz_tile_bank = 0;
	uint32_t m_roz_unk_base0 = 0;
	uint32_t m_roz_unk_base1 = 0;
	uint32_t m_roz_unk_base2 = 0;
	uint16_t m_roz_coeffa = 0;
	uint16_t m_roz_coeffb = 0;
	uint16_t m_roz_coeffc = 0;
	uint16_t m_roz_coeffd = 0;
	int32_t m_roz_changed = 0;
	u8 m_pixel_mode = 0;
	u8 m_gfx_mode = 0;

	uint16_t m_video_regs[256]{};

	tilemap_t *m_tilemap_sizes[4][5]{};
	bitmap_ind16 m_sprite_final_bitmap;
	bitmap_ind8 m_sprite_mask_bitmap;
	bitmap_ind8 m_prio_bitmap;

	void write_swapped_byte(int offset, uint8_t byte);
	TILE_GET_INFO_MEMBER(get_tilemap0_tile_info);
	TILE_GET_INFO_MEMBER(get_tilemap1_tile_info);
	TILE_GET_INFO_MEMBER(get_tilemap2_tile_info);
	TILE_GET_INFO_MEMBER(get_roz_tile_info);
	void sound_timer_irq(int state);
	void sound_dma_irq(int state);
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	TIMER_CALLBACK_MEMBER(frc_timer_cb);
	TIMER_CALLBACK_MEMBER(line_on_cb);
	TIMER_CALLBACK_MEMBER(line_off_cb);
	TIMER_CALLBACK_MEMBER(scanline_cb);
	int get_tilemap_region(int layer);
	void get_tilemap_info_common(int layer, tile_data &tileinfo, int count);
	void get_roz_tilemap_info(int layer, tile_data &tileinfo, int count);
	int get_tilemap_dimensions(int &xsize, int &ysize, int layer);
	void update_tilemap_flags(int layer);
	void draw_sprite_tile(bitmap_ind16 &dst, bitmap_ind8 &priomap, const rectangle &cliprect, gfx_element *gfx, int tile, int palette, bool xflip, bool yflip, int dstx, int dsty, int prio);
	void draw_sprite_tile_mask(bitmap_ind8 &dst, const rectangle &cliprect, gfx_element *gfx, int tile, bool xflip, bool yflip, int dstx, int dsty);
	void draw_sprite_tile_masked(bitmap_ind16 &dst, bitmap_ind8 &mask, bitmap_ind8 &priomap, const rectangle &cliprect, gfx_element *gfx, int tile, int palette, bool xflip, bool yflip, int dstx, int dsty, int prio);
	void draw_sprites(bitmap_ind16 &bitmap, bitmap_ind8 &maskmap, bitmap_ind8 &priomap, const rectangle &cliprect);
	void mark_active_tilemap_all_dirty(int layer);
	void draw_roz_layer(bitmap_ind16 &bitmap, const rectangle &cliprect, tilemap_t *tmap, uint32_t startx, uint32_t starty, int incxx, int incxy, int incyx, int incyy, int wraparound/*, int columnscroll, uint32_t *scrollram*/, int transmask);

	void set_sound_irq(uint8_t bit, uint8_t state);

	void host_um6619_map(address_map &map) ATTR_COLD;
	u8 m_irq_mask = 0;
	u16 m_frc_control = 0;
	u16 m_frc_frequency = 0;
	emu_timer *m_frc_timer = nullptr;
	void update_frc_state();
};


int supracan_state::get_tilemap_region(int layer)
{
	// TODO: anything that refers to 2bpp region (2) can actually be layer 1bpp selectable too
	// From layer mode bit 7?
	switch (layer)
	{
	case 0:
	{
		static const int layer0_mode[8] = { 2, 1, 0, 1, 0, 0, 0, 0 };
		return layer0_mode[m_gfx_mode & 7];
	}

	case 1:
	{
		static const int layer1_mode[8] = { 2, 1, 1, 1, 2, 2, 2, 2 };
		return layer1_mode[m_gfx_mode & 7];
	}

	case 2:
		return 2;

	case 3:
	{
		static const int s_roz_mode_lut[4] = { 4, 2, 1, 0 };
		return s_roz_mode_lut[m_roz_mode & 3];
	}
	}

	// TODO: 4th layer at $f00160 (gfx mode 0 only, ignored for everything else)
	throw emu_fatalerror("Error: layer = %d not defined", layer);
}

void supracan_state::get_tilemap_info_common(int layer, tile_data &tileinfo, int count)
{
	uint16_t *vram = m_vram;

	uint32_t base = m_tilemap_base_addr[layer];
	int gfx_mode = (m_tilemap_mode[layer] & 0x7000) >> 12;
	int region = get_tilemap_region(layer);

	count += base;

	const u16 tile_bank = gfx_mode << (8 + region);
	// speedyd and slghtsag hints that text layer color offsets in steps of 4
	const u16 palette_shift = (region == 2) ? 2 : 0;

	u8 palette_base = ((vram[count] & 0xf000) >> 12);

	// speedyd gameplay
	if (BIT(m_tilemap_tile_mode[layer], 9))
	{
		tileinfo.category = !BIT(palette_base, 3);
		palette_base |= 8;
	}

	u16 tile = (vram[count] & 0x03ff) + tile_bank;
	u8 flipxy = (vram[count] & 0x0c00) >> 10;
	u8 palette = palette_base << palette_shift;

	tileinfo.set(region, tile, palette, TILE_FLIPXY(flipxy));
}

// TODO: merge with normal layers.
void supracan_state::get_roz_tilemap_info(int layer, tile_data &tileinfo, int count)
{
	uint16_t *vram = m_vram;

	uint32_t base = m_roz_base_addr;

	int region = 1;
	uint16_t tile_bank = 0;

	region = get_tilemap_region(layer);

	switch (m_roz_mode & 3) // FIXME: fix gfx bpp order
	{
	case 0:
	{
		// HACK: case for startup logo
		// This isn't understood properly, it's rendering a single 64x64 tile, which for convenience we've rearranged and decoded as 8x8 for the tilemaps
		int tile = 0x880 + ((count & 7) * 2);
		// tile += (count & 0x070) >> 2;

		if (count & 0x20) tile ^= 1;
		tile |= (count & 0xc0) >> 2;

		tileinfo.set(region, tile, 0, 0);
		return;
	}

	case 1:
		tile_bank = (m_roz_tile_bank & 0xf000) >> 3;
		break;

	case 2:
		tile_bank = (m_roz_tile_bank & 0xf000) >> 3;
		break;

	case 3:
		tile_bank = (m_roz_tile_bank & 0xf000) >> 3;
		break;
	}

	count += base;

	u8 palette_base = ((vram[count] & 0xf000) >> 12);

	// sonevil gameplay
	if (BIT(m_roz_tile_mode, 9))
	{
		tileinfo.category = !BIT(palette_base, 3);
		palette_base |= 8;
	}

	u16 tile = (vram[count] & 0x03ff) + tile_bank;
	u8 flipxy = (vram[count] & 0x0c00) >> 10;
	u8 palette = palette_base;

	tileinfo.set(region, tile, palette, TILE_FLIPXY(flipxy));
}



TILE_GET_INFO_MEMBER(supracan_state::get_tilemap0_tile_info)
{
	get_tilemap_info_common(0, tileinfo, tile_index);
}

TILE_GET_INFO_MEMBER(supracan_state::get_tilemap1_tile_info)
{
	get_tilemap_info_common(1, tileinfo, tile_index);
}

TILE_GET_INFO_MEMBER(supracan_state::get_tilemap2_tile_info)
{
	get_tilemap_info_common(2, tileinfo, tile_index);
}

TILE_GET_INFO_MEMBER(supracan_state::get_roz_tile_info)
{
	get_roz_tilemap_info(3, tileinfo, tile_index);
}


void supracan_state::video_start()
{
	m_sprite_final_bitmap.allocate(1024, 1024, BITMAP_FORMAT_IND16);
	m_sprite_mask_bitmap.allocate(1024, 1024, BITMAP_FORMAT_IND8);
	m_prio_bitmap.allocate(1024, 1024, BITMAP_FORMAT_IND8);

	m_vram_addr_swapped.resize(0x20000); // hack for 1bpp layer at startup
	m_gfxdecode->gfx(4)->set_source(&m_vram_addr_swapped[0]);
	m_gfxdecode->gfx(4)->set_xormask(0);

	m_tilemap_sizes[0][0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(supracan_state::get_tilemap0_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 32, 32);
	m_tilemap_sizes[0][1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(supracan_state::get_tilemap0_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 64, 32);
	m_tilemap_sizes[0][2] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(supracan_state::get_tilemap0_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 128, 32);
	m_tilemap_sizes[0][3] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(supracan_state::get_tilemap0_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64);
	m_tilemap_sizes[0][4] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(supracan_state::get_tilemap0_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 16, 16);

	m_tilemap_sizes[1][0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(supracan_state::get_tilemap1_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 32, 32);
	m_tilemap_sizes[1][1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(supracan_state::get_tilemap1_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 64, 32);
	m_tilemap_sizes[1][2] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(supracan_state::get_tilemap1_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 128, 32);
	m_tilemap_sizes[1][3] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(supracan_state::get_tilemap1_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64);
	m_tilemap_sizes[1][4] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(supracan_state::get_tilemap1_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 16, 16);

	m_tilemap_sizes[2][0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(supracan_state::get_tilemap2_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 32, 32);
	m_tilemap_sizes[2][1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(supracan_state::get_tilemap2_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 64, 32);
	m_tilemap_sizes[2][2] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(supracan_state::get_tilemap2_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 128, 32);
	m_tilemap_sizes[2][3] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(supracan_state::get_tilemap2_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64);
	m_tilemap_sizes[2][4] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(supracan_state::get_tilemap2_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 16, 16);

	m_tilemap_sizes[3][0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(supracan_state::get_roz_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 32, 32);
	m_tilemap_sizes[3][1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(supracan_state::get_roz_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 64, 32);
	m_tilemap_sizes[3][2] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(supracan_state::get_roz_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 128, 32);
	m_tilemap_sizes[3][3] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(supracan_state::get_roz_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64);
	m_tilemap_sizes[3][4] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(supracan_state::get_roz_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 16, 16);
}

int supracan_state::get_tilemap_dimensions(int &xsize, int &ysize, int layer)
{
	xsize = 32;
	ysize = 32;

	int select;
	if (layer == ROZ_LAYER_NUMBER)
		select = m_roz_mode & 0x0f00;
	else
		select = m_tilemap_flags[layer] & 0x0f00;

	switch (select)
	{
	// formduel rain layer on game over
	// sonevil title screen
	case 0x200:
		xsize = 16;
		ysize = 16;
		return 4;

	case 0x400:
		xsize = 32;
		ysize = 32;
		return 0;

	case 0x600:
		xsize = 64;
		ysize = 32;
		return 1;

	case 0xa00:
		xsize = 128;
		ysize = 32;
		return 2;

	case 0xc00:
		xsize = 64;
		ysize = 64;
		return 3;

	default:
		// TODO: setting 0 is already tested by A'Can logo (in bitmap mode)
		// select & 0x100 is 16x16 tiles
		if (select != 0)
			LOGMASKED(LOG_HFUNKNOWNS, "Unsupported tilemap size for layer %d: %04x\n", layer, select);
		return 0;
	}
}

void supracan_state::update_tilemap_flags(int layer)
{
	const u32 attr = layer == ROZ_LAYER_NUMBER ? m_roz_mode : m_tilemap_flags[layer];
	const u32 flip_x = BIT(attr, 1) ? TILEMAP_FLIPX : 0;
	const u32 flip_y = BIT(attr, 0) ? TILEMAP_FLIPY : 0;

	// TODO: we should really track what's the current tilemap paging and update only that
	// Obviously we should also NOT mark all dirty, but just subscribe to what's the effective bank.
	// is a dynamic mapper even possible with tilemap.h?
	for (int i = 0; i < 4; i++)
	{
		m_tilemap_sizes[layer][i]->set_flip(flip_x | flip_y);
	}
}

void supracan_state::draw_sprite_tile(bitmap_ind16 &dst, bitmap_ind8 &priomap, const rectangle &cliprect, gfx_element *gfx, int tile, int palette,
	bool xflip, bool yflip, int dstx, int dsty, int prio)
{
	// compute final pixel in X and exit if we are entirely clipped
	int dstendx = dstx + 7;
	if (dstx > cliprect.right() || dstendx < cliprect.left())
		return;

	// apply left clip
	int srcx = 0;
	if (dstx < cliprect.left())
	{
		srcx = cliprect.left() - dstx;
		dstx = cliprect.left();
	}

	// apply right clip
	if (dstendx > cliprect.right())
		dstendx = cliprect.right();

	// compute final pixel in Y and exit if we are entirely clipped
	int dstendy = dsty + 7;
	if (dsty > cliprect.bottom() || dstendy < cliprect.top())
		return;

	// apply top clip
	int srcy = 0;
	if (dsty < cliprect.top())
	{
		srcy = cliprect.top() - dsty;
		dsty = cliprect.top();
	}

	// apply bottom clip
	if (dstendy > cliprect.bottom())
		dstendy = cliprect.bottom();

	// apply X flipping
	int dx = 1;
	if (xflip)
	{
		srcx = 7 - srcx;
		dx = -dx;
	}

	// apply Y flipping
	int dy = gfx->rowbytes();
	if (yflip)
	{
		srcy = 7 - srcy;
		dy = -dy;
	}

	const int color = gfx->colorbase() + gfx->granularity() * (palette % gfx->colors());
	const uint8_t *src_data = &gfx->get_data(tile % gfx->elements())[srcy * gfx->rowbytes()];
	for (int y = dsty; y <= dstendy; y++)
	{
		const uint8_t *srcp = &src_data[srcx];
		uint8_t *priop = &priomap.pix(y, dstx);
		uint16_t *dstp = &dst.pix(y, dstx);
		for (int x = dstx; x <= dstendx; x++)
		{
			const uint32_t srcdata = *srcp;
			if (srcdata != 0)
			{
				*dstp = (uint16_t)(srcdata + color);
				*priop = (*priop & 0xf0) | (uint8_t)prio;
			}
			srcp += dx;
			priop++;
			dstp++;
		}
		src_data += dy;
	}
}

void supracan_state::draw_sprite_tile_mask(bitmap_ind8 &dst, const rectangle &cliprect, gfx_element *gfx, int tile, bool xflip, bool yflip, int dstx, int dsty)
{
	// compute final pixel in X and exit if we are entirely clipped
	int dstendx = dstx + 7;
	if (dstx > cliprect.right() || dstendx < cliprect.left())
		return;

	// apply left clip
	int srcx = 0;
	if (dstx < cliprect.left())
	{
		srcx = cliprect.left() - dstx;
		dstx = cliprect.left();
	}

	// apply right clip
	if (dstendx > cliprect.right())
		dstendx = cliprect.right();

	// compute final pixel in Y and exit if we are entirely clipped
	int dstendy = dsty + 7;
	if (dsty > cliprect.bottom() || dstendy < cliprect.top())
		return;

	// apply top clip
	int srcy = 0;
	if (dsty < cliprect.top())
	{
		srcy = cliprect.top() - dsty;
		dsty = cliprect.top();
	}

	// apply bottom clip
	if (dstendy > cliprect.bottom())
		dstendy = cliprect.bottom();

	// apply X flipping
	int dx = 1;
	if (xflip)
	{
		srcx = 7 - srcx;
		dx = -dx;
	}

	// apply Y flipping
	int dy = gfx->rowbytes();
	if (yflip)
	{
		srcy = 7 - srcy;
		dy = -dy;
	}

	const uint8_t *src_data = &gfx->get_data(tile % gfx->elements())[srcy * gfx->rowbytes()];
	for (int y = dsty; y <= dstendy; y++)
	{
		const uint8_t *srcp = &src_data[srcx];
		uint8_t *dstp = &dst.pix(y, dstx);
		for (int x = dstx; x <= dstendx; x++)
		{
			if (*srcp)
				*dstp = 1;
			srcp += dx;
			dstp++;
		}
		src_data += dy;
	}
}

void supracan_state::draw_sprite_tile_masked(bitmap_ind16 &dst, bitmap_ind8 &mask, bitmap_ind8 &priomap, const rectangle &cliprect, gfx_element *gfx, int tile,
	int palette, bool xflip, bool yflip, int dstx, int dsty, int prio)
{
	// compute final pixel in X and exit if we are entirely clipped
	int dstendx = dstx + 7;
	if (dstx > cliprect.right() || dstendx < cliprect.left())
		return;

	// apply left clip
	int srcx = 0;
	if (dstx < cliprect.left())
	{
		srcx = cliprect.left() - dstx;
		dstx = cliprect.left();
	}

	// apply right clip
	if (dstendx > cliprect.right())
		dstendx = cliprect.right();

	// compute final pixel in Y and exit if we are entirely clipped
	int dstendy = dsty + 7;
	if (dsty > cliprect.bottom() || dstendy < cliprect.top())
		return;

	// apply top clip
	int srcy = 0;
	if (dsty < cliprect.top())
	{
		srcy = cliprect.top() - dsty;
		dsty = cliprect.top();
	}

	// apply bottom clip
	if (dstendy > cliprect.bottom())
		dstendy = cliprect.bottom();

	// apply X flipping
	int dx = 1;
	if (xflip)
	{
		srcx = 7 - srcx;
		dx = -dx;
	}

	// apply Y flipping
	int dy = gfx->rowbytes();
	if (yflip)
	{
		srcy = 7 - srcy;
		dy = -dy;
	}

	const int color = gfx->colorbase() + gfx->granularity() * (palette % gfx->colors());
	const uint8_t *src_data = &gfx->get_data(tile % gfx->elements())[srcy * gfx->rowbytes()];
	for (int y = dsty; y <= dstendy; y++)
	{
		const uint8_t *srcp = &src_data[srcx];
		uint16_t *dstp = &dst.pix(y, dstx);
		uint8_t *priop = &priomap.pix(y, dstx);
		uint8_t *maskp = &mask.pix(y, dstx);
		for (int x = dstx; x <= dstendx; x++)
		{
			const uint32_t srcdata = *srcp;
			if (srcdata != 0 && *maskp != 0)
			{
				// TODO: this is really color mix
				// rebelst select attack screens draws with this path with no "*maskp" set,
				// and expect *dstp to add up not just set.
				*dstp = (uint16_t)(srcdata + color);
				*priop = (*priop & 0xf0) | (uint8_t)prio;
			}
			srcp += dx;
			dstp++;
			priop++;
			maskp++;
		}
		src_data += dy;
	}
}

// [0]
// -e-- ---- ---- ---- sprite enable?
// ---h hhh- ---- ---- Y size (not always right)
// ---- ---y yyyy yyyy Y position
// [1]
// bbbb ---- ---- ---- Tile bank
// ---- h--- ---- ---- Horizontal flip
// ---- -v-- ---- ---- Vertical flip
// ---- --mm ---- ---- Masking mode
// ---- ---- ---- -www X size
// [2]
// zzz- ---- ---- ---- X scale
// ---- ???- ---- ---- Unknown, but often written.
//                     Values include 111 and 110 for the Super A'Can logo, 110 in the Sango Fighter intro, and 101/100 in the Boom Zoo intro.
// ---- ---x xxxx xxxx X position
// [3]
// d--- ---- ---- ---- Direct Sprite (use details from here, not looked up in vram)
// -ooo oooo oooo oooo Sprite address
void supracan_state::draw_sprites(bitmap_ind16 &bitmap, bitmap_ind8 &maskmap, bitmap_ind8 &priomap, const rectangle &cliprect)
{
	uint16_t *vram = m_vram;

	// ysizes are normally setting +1, except on higher ranges where values are higher
	// TODO: check on real HW
	static const int ysizes_table[16] =
	{
		1, 2, 3, 4, 5, 6, 7, 8, 9,
		// 0x9: speedyd intro dash frame, speedyd bonus stages, boomzoo intro
		// 11 would be more logical for former, except it will break latter and
		// is confirmed to "cut" feet anyway.
		10,
		// 0xa: A'Can logo
		12,
		// 0xb/0xc: jttlaugh stage 1-3 (particularly on the web scrolling jump platforms)
		16,
		20,
		// 0xd: slghtsag character select
		22,
		// 0xe/0xf: <unknown>, check me
		24,
		26
	};

	uint32_t skip_count = 0;
	uint32_t start_word = (m_sprite_base_addr >> 1) + skip_count * 4;
	uint32_t end_word = start_word + (m_sprite_count - skip_count) * 4;
	int region = (m_sprite_flags & 1) ? 0 : 1; // 8bpp : 4bpp
	// As per tilemaps banking is halved with 8bpp sprites
	// - rebelst during gameplay
	// - A'Can logo and slghtsag don't set any bank number.
	const u16 bank_size = 0x100 << region;

	static const uint16_t VRAM_MASK = 0xffff;

	for (int i = start_word; i < end_word; i += 4)
	{
		int x = vram[i + 2] & 0x01ff;
		int y = vram[i + 0] & 0x01ff;

		int bank = (vram[i + 1] & 0xf000) >> 12;
		int mask = (vram[i + 1] & 0x0300) >> 8;
		int sprite_xflip = (vram[i + 1] & 0x0800) >> 11;
		int sprite_yflip = (vram[i + 1] & 0x0400) >> 10;
		int prio = (vram[i + 2] >> 9) & 3;
		const u16 sprite_ptr = vram[i + 3];
		//int xscale = vram[i + 2] >> 13;
		gfx_element *gfx = m_gfxdecode->gfx(region);

		// wraparound
		if (y >= 0x180) y -= 0x200;
		if (x >= 0x180) x -= 0x200;

		if ((vram[i + 0] & 0x4000) && sprite_ptr)
		{
			int xsize = 1 << (vram[i + 1] & 7);
			int ysize = ysizes_table[(vram[i + 0] & 0x1e00) >> 9];

			// HACK: sonevil sets 1x1 tiles in game, and expecting to take this path.
			// Most likely former condition is wrong, and it just "direct sprite" when latter occurs.
			// magipool also wants latter, for the shot markers to work.
			if (sprite_ptr & 0x8000 || (xsize == 1 && ysize == 1))
			{
				int tile = (bank * bank_size) + (sprite_ptr & 0x03ff);

				int palette = (sprite_ptr & 0xf000) >> 12; // this might not be correct, due to the & 0x8000 condition above this would force all single tile sprites to be using palette >= 0x8 only

				// sonevil expect to flip X/Y thru the sprite pointer
				int tile_xflip = sprite_xflip ^ ((sprite_ptr & 0x0800) >> 11);
				int tile_yflip = sprite_yflip ^ ((sprite_ptr & 0x0400) >> 10);

				if (mask > 1)
					draw_sprite_tile_mask(maskmap, cliprect, gfx, tile, tile_xflip, tile_yflip, x, y);
				else if (mask == 1)
					draw_sprite_tile_masked(bitmap, maskmap, priomap, cliprect, gfx, tile, palette, tile_xflip, tile_yflip, x, y, prio);
				else
					draw_sprite_tile(bitmap, priomap, cliprect, gfx, tile, palette, tile_xflip, tile_yflip, x, y, prio);
			}
			else
			{
				// I think the xsize must influence the ysize somehow, there are too many conflicting cases otherwise
				// there don't appear to be any special markers in the actual looked up tile data to indicate skip / end of list
				for (int ytile = 0; ytile < ysize; ytile++)
				{
					for (int xtile = 0; xtile < xsize; xtile++)
					{
						uint16_t data = vram[((sprite_ptr << 1) + ytile * xsize + xtile) & VRAM_MASK];
						// magipool will draw garbage during gameplay if we don't skip empty entries.
						// NOTE: sets up both main table pointer and sub entries
						if (data == 0)
							continue;
						int tile = (bank * bank_size) + (data & 0x03ff);
						int palette = (data & 0xf000) >> 12;

						int xpos = sprite_xflip ? (x - (xtile + 1) * 8 + xsize * 8) : (x + xtile * 8);
						int ypos = sprite_yflip ? (y - (ytile + 1) * 8 + ysize * 8) : (y + ytile * 8);

						// magipool rolls back at 512 edge for the shot power menu
						// TODO: should also control clip inside the functions
						// (which should be merged as well)
						// TODO: verify ypos, likely same.
						xpos &= 0x1ff;

						int tile_xflip = sprite_xflip ^ ((data & 0x0800) >> 11);
						int tile_yflip = sprite_yflip ^ ((data & 0x0400) >> 10);

						if (mask > 1)
							draw_sprite_tile_mask(maskmap, cliprect, gfx, tile, tile_xflip, tile_yflip, xpos, ypos);
						else if (mask == 1)
							draw_sprite_tile_masked(bitmap, maskmap, priomap, cliprect, gfx, tile, palette, tile_xflip, tile_yflip, xpos, ypos, prio);
						else
							draw_sprite_tile(bitmap, priomap, cliprect, gfx, tile, palette, tile_xflip, tile_yflip, xpos, ypos, prio);
					}
				}
			}

			// TODO: scaling, find places where it occurs
#if 0
			if (xscale == 0) continue;
			uint32_t delta = (1 << 17) / xscale;
			for (int sy = 0; sy < ysize * 8; sy++)
			{
				uint16_t *src = &sprite_bitmap->pix(sy);
				uint16_t *dst = &bitmap.pix(y + sy);
				uint32_t dx = x << 16;
				for (int sx = 0; sx < xsize * 8; sx++)
				{
					dst[dx >> 16] = src[sx];
					dx += delta;
				}
			}
#endif

		}
	}
}



void supracan_state::mark_active_tilemap_all_dirty(int layer)
{
	int xsize = 0;
	int ysize = 0;

	int which_tilemap_size = get_tilemap_dimensions(xsize, ysize, layer);
//  for (int i = 0; i < 4; i++)
//    tilemap_mark_all_tiles_dirty(m_tilemap_sizes[layer][i]);
	m_tilemap_sizes[layer][which_tilemap_size]->mark_all_dirty();
}



/* draws tilemap with linescroll OR columnscroll to 16-bit indexed bitmap */
void supracan_state::draw_roz_layer(bitmap_ind16 &bitmap, const rectangle &cliprect, tilemap_t *tmap, uint32_t startx, uint32_t starty, int incxx, int incxy, int incyx, int incyy, int wraparound/*, int columnscroll, uint32_t *scrollram*/, int transmask)
{
	bitmap_ind16 &srcbitmap = tmap->pixmap();
	const int xmask = srcbitmap.width() - 1;
	const int ymask = srcbitmap.height() - 1;
	const int widthshifted = srcbitmap.width() << 16;
	const int heightshifted = srcbitmap.height() << 16;

	/* pre-advance based on the cliprect */
	startx += cliprect.min_x * incxx + cliprect.min_y * incyx;
	starty += cliprect.min_x * incxy + cliprect.min_y * incyy;

	/* extract start/end points */
	int sx = cliprect.min_x;
	int sy = cliprect.min_y;
	int ex = cliprect.max_x;
	int ey = cliprect.max_y;

	{
		/* loop over rows */
		while (sy <= ey)
		{
			/* initialize X counters */
			int x = sx;
			uint32_t cx = startx;
			uint32_t cy = starty;

			/* get dest and priority pointers */
			uint16_t *dest = &bitmap.pix(sy, sx);
			// TODO: somebody ate the priority pointer here ...

			/* loop over columns */
			while (x <= ex)
			{
				if ((wraparound) || (cx < widthshifted && cy < heightshifted)) // not sure how this will cope with no wraparound, but row/col scroll..
				{
					#if 0
					if (columnscroll)
					{
						int scroll = 0; // scrollram[(cx>>16)&0x3ff]);

						uint16_t data = &srcbitmap.pix(((cy >> 16) - scroll) & ymask, (cx >> 16) & xmask)[0];

						if ((data & transmask) != 0)
							dest[0] = data;
					}
					else
					#endif
					{
						int scroll = 0;//scrollram[(cy>>16)&0x3ff]);
						uint16_t data =  srcbitmap.pix((cy >> 16) & ymask, ((cx >> 16) - scroll) & xmask);

						if ((data & transmask) != 0)
							*dest = data;
					}
				}

				/* advance in X */
				cx += incxx;
				cy += incxy;
				x++;
				dest++;
			}

			/* advance in Y */
			startx += incyx;
			starty += incyy;
			sy++;
		}
	}
}


// VIDEO FLAGS                  ROZ MODE            TILEMAP FLAGS
//
//  Bit                         Bit                 Bit
// 15-9: Unknown                15-13: Priority?    15-13: Priority?
//    8: X ht. (256/320)        12: Unknown         12: Unknown
//    7: Tilemap 0 enable       11-8: Dims          11-8: Dims
//    6: Tilemap 1 enable       7-6: Unknown        7-6: Unknown
//    5: Tilemap 2 enable?      5: Wrap             5: Wrap
//    3: Sprite enable          4-2: Unknown        4-2: Mosaic
//    2: ROZ enable             1-0: Bit Depth      1-0: Bit Depth
//  1-0: Unknown

//                      Video Flags                 ROZ Mode                    Tilemap 0   Tilemap 1   Tilemap 2   VF Unk0
// A'Can logo:          120e: 0001 0010 0000 1110   4020: 0100 0000 0010 0000   4620        ----        ----        0x09
// Boomzoo Intro:       9a82: 1001 1010 1000 0010   0402: 0000 0100 0000 0010   6400        6400        4400        0x4d
// Boomzoo Title:       9acc: 1001 1010 1100 1100   0402: 0000 0100 0000 0010   6400        6400        4400        0x4d
// C.U.G. Intro:        11c8: 0001 0001 1100 1000   0402: 0000 0100 0000 0010   2400        4400        6400        0x08
// C.U.G. Title:        11cc: 0001 0001 1100 1100   0602: 0000 0110 0000 0010   2600        4600        ----        0x08
// Speedy Dragon Logo:  0388: 0000 0011 1000 1000   4020: 0100 0000 0010 0000   6c20        6c20        2600        0x01
// Speedy Dragon Title: 038c: 0000 0011 1000 1100   2603: 0010 0110 0000 0011   6c20        2c20        2600        0x01
// Sango Fighter Intro: 03c8: 0000 0011 1100 1000   ----: ---- ---- ---- ----   6c20        4620        ----        0x01
// Sango Fighter Game:  03ce: 0000 0011 1100 1110   0622: 0000 0110 0010 0010   2620        4620        ----        0x01

uint32_t supracan_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	m_sprite_final_bitmap.fill(0x00, cliprect);
	m_sprite_mask_bitmap.fill(0x00, cliprect);
	m_prio_bitmap.fill(0xff, cliprect);
	// Back layer normally fills with 0x00
	// - boomzoo (title) wants 0x00
	// - sangofgt (1st fighter stage) wants 0x00
	// - sonevil (intro) wants 0x00
	// - back layer overlay happens from mixing registers (A'Can BIOS sets 0x02 there)
	bitmap.fill(0x00, cliprect);

	draw_sprites(m_sprite_final_bitmap, m_sprite_mask_bitmap, m_prio_bitmap, cliprect);

	// mix screen
	int xsize = 0, ysize = 0;
//  int tilemap_num;

	for (int pri = 7; pri >= 0; pri--)
	{
		// Wanted like this by speedyd, formduel and magipool at very least
		for (int layer = 0; layer < ROZ_LAYER_NUMBER + 1; layer ++)
		{
			int enabled = 0;
			int layer_priority = 0;

			// ROZ
			if (layer == ROZ_LAYER_NUMBER)
			{
				enabled = BIT(m_video_flags, 2);
				if (!enabled)
					continue;
				layer_priority = ((m_roz_mode >> 13) & 7);
			}
			else
			{
				enabled = BIT(m_video_flags, 7 - layer);
				if (!enabled)
					continue;
				layer_priority = ((m_tilemap_flags[layer] >> 13) & 7);
			}

			if (layer_priority == pri)
			{
				int which_tilemap_size = get_tilemap_dimensions(xsize, ysize, layer);
				bitmap_ind16 &src_bitmap = m_tilemap_sizes[layer][which_tilemap_size]->pixmap();
				// TODO: per-tile priority, thru ->flagsmap()
				int gfx_region = get_tilemap_region(layer);
				int transmask = 0xff;

				switch (gfx_region)
				{
					case 0: transmask = 0xff; break;
					case 1: transmask = 0x0f; break;
					case 2: transmask = 0x03; break;
					case 3: transmask = 0x01; break;
					case 4: transmask = 0x01; break;
				}

				if (layer != ROZ_LAYER_NUMBER)
				{
					int wrap = (m_tilemap_flags[layer] & 0x20);

					// slghtsag wants a resolution of 12-bits for text and title
					int scrollx = m_tilemap_scrollx[layer] & 0xfff;
					int scrolly = m_tilemap_scrolly[layer] & 0xfff;

					if (scrollx & 0x800) scrollx -= 0x1000;
					if (scrolly & 0x800) scrolly -= 0x1000;

					// global flips also inverts scroll meanings
					// cfr. formduel left character and sangofgt 2nd fighter stage.
					if (BIT(m_tilemap_flags[layer], 1))
						scrollx ^= ((xsize * 8) - 1);

					if (BIT(m_tilemap_flags[layer], 0))
						scrolly ^= ((ysize * 8) - 1);

					int mosaic_count = (m_tilemap_flags[layer] & 0x001c) >> 2;
					int mosaic_mask = 0xffffffff << mosaic_count;

					// yes, it will draw a single line if you specify a cliprect as such (partial updates...)

					for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
					{
						// these will have to change to uint32_t* etc. once alpha blending is supported
						uint16_t *screen = &bitmap.pix(y);

						int actualy = y & mosaic_mask;
						int realy = actualy + scrolly;

						if (!wrap)
							if (scrolly + y < 0 || scrolly + y > ((ysize * 8) - 1))
								continue;

						// slghtsag enables lineselect on load game
						if (BIT(m_tilemap_tile_mode[layer], 11))
						{
							int16_t lineselect = (int16_t)m_vram[((m_tilemap_lineselect_addr[layer] << 1) + y ) & 0xffff];
							realy = (lineselect + scrolly) & ((ysize * 8) - 1);
						}

						uint16_t *src = &src_bitmap.pix(realy & ((ysize * 8) - 1));
						uint8_t *priop = &m_prio_bitmap.pix(y);
						int line_scroll_x = scrollx;

						// formduel main menu
						if (BIT(m_tilemap_tile_mode[layer], 14))
						{
							int16_t linescrollx = (int16_t)m_vram[((m_tilemap_linescrollx_addr[layer] << 1) + y) & 0xffff];
							line_scroll_x += linescrollx;
						}


						for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
						{
							int actualx = x & mosaic_mask;
							int realx = actualx + line_scroll_x;

							if (!wrap)
								if (line_scroll_x + x < 0 || line_scroll_x + x > ((xsize * 8) - 1))
									continue;

							uint16_t srcpix = src[realx & ((xsize * 8) - 1)];

							if ((srcpix & transmask) != 0 && layer_priority < (priop[x] >> 4))
							{
								screen[x] = srcpix;
								priop[x] = (priop[x] & 0x0f) | (layer_priority << 4);
							}
						}
					}
				}
				else
				{
					int wrap = m_roz_mode & 0x20;

					int incxx = m_roz_coeffa;
					int incyy = m_roz_coeffd;

					int incxy = m_roz_coeffc;
					int incyx = m_roz_coeffb;

					int scrollx = m_roz_scrollx;
					int scrolly = m_roz_scrolly;

					if (incyx & 0x8000) incyx -= 0x10000;
					if (incxy & 0x8000) incxy -= 0x10000;

					if (incyy & 0x8000) incyy -= 0x10000;
					if (incxx & 0x8000) incxx -= 0x10000;

					//popmessage("%04x %04x\n",m_video_flags, m_roz_mode);

					// roz mode..
					//4020 = enabled speedyd
					//6c22 = enabled speedyd
					//2c22 = enabled speedyd
					//4622 = disabled jttlaugh
					//2602 = disabled monopoly
					//0402 = disabled (sango title)
					// or is it always enabled, and only corrupt because we don't clear ram properly?
					// (probably not this register?)

					if (!(m_roz_mode & 0x0200) && (m_roz_mode & 0xf000)) // HACK - Not trusted: Acan Logo, Speedy Dragon Intro, Speed Dragon Bonus stage need it.  Monopoly and JTT *don't* causes graphical issues
					{
						// NOT accurate, causes issues when the attract mode loops and the logo is shown the 2nd time in some games - investigate
						for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
						{
							rectangle clip(cliprect.min_x, cliprect.max_x, y, y);

							scrollx = (m_roz_scrollx);
							scrolly = (m_roz_scrolly);
							incxx = (m_roz_coeffa);

							incxx += m_vram[m_roz_unk_base0/2 + y];

							scrollx += m_vram[m_roz_unk_base1/2 + y * 2] << 16;
							scrollx += m_vram[m_roz_unk_base1/2 + y * 2 + 1];

							scrolly += m_vram[m_roz_unk_base2/2 + y * 2] << 16;
							scrolly += m_vram[m_roz_unk_base2/2 + y * 2 + 1];

							if (incxx & 0x8000) incxx -= 0x10000;

							if (m_vram[m_roz_unk_base0/2 + y]) // incxx = 0, no draw?
								draw_roz_layer(bitmap, clip, m_tilemap_sizes[layer][which_tilemap_size], scrollx<<8, scrolly<<8, incxx<<8, incxy<<8, incyx<<8, incyy<<8, wrap, transmask);
						}
					}
					else
					{
						draw_roz_layer(bitmap, cliprect, m_tilemap_sizes[layer][which_tilemap_size], scrollx<<8, scrolly<<8, incxx<<8, incxy<<8, incyx<<8, incyy<<8, wrap, transmask);
					}
				}

			}
		}

		// TODO: secondary window control at $1d8-$1df (no SW sets it up so far)
		if (BIT(m_video_flags, 1))
		{
			// bit 15: enabled by magipool at circular intro, will show sprites above it if checked with.
			int layer_priority = ((m_window_control[0] >> 13) & 3);
			if (pri != layer_priority)
				continue;
			const u8 reverse_clip = BIT(m_window_control[0], 11);
			// magipool enables this on title screen
			// (for the white "overlay", revealing Funtech copyright progressively)
			// TODO: confirm implementation
			int window_scrollx = m_window_scrollx[0] & 0x3ff;
			// TODO: window_scrolly

			if (window_scrollx & 0x200)
				window_scrollx -= 0x400;

			const u8 window_pen = m_window_control[0] & 0xff;

			for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
			{
				// bit 8 is unset by sangofgt, where it uses only two entries of the table on transitions.
				const int ybase = BIT(m_window_control[0], 8) ? (y * 2) : 0;
				const u32 clip_base = ((m_window_start_addr[0] << 1) + ybase) & 0xffff;

				const int16_t clip_min_x = (m_vram[clip_base + 0] + window_scrollx);
				const int16_t clip_max_x = (m_vram[clip_base + 1] + window_scrollx);
				uint8_t *priop = &m_prio_bitmap.pix(y);

				for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
				{
					if (layer_priority >= (priop[x] >> 4))
						continue;

					if ((x >= clip_min_x && x < clip_max_x) ^ reverse_clip)
					{
						bitmap.pix(y, x) = window_pen;
						priop[x] = (priop[x] & 0x0f) | (layer_priority << 4);
					}
				}
			}
		}
	}


	// combine sprites
	if (BIT(m_video_flags, 3))
	{
		for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
		{
			uint16_t *dstp = &bitmap.pix(y);
			uint8_t *priop = &m_prio_bitmap.pix(y);
			uint16_t *spritep = &m_sprite_final_bitmap.pix(y);

			for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
			{
				uint16_t sprite_pix = spritep[x];
				uint8_t tile_prio = priop[x] >> 4;
				uint8_t sprite_prio = priop[x] & 0x0f;
				if (sprite_pix != 0 && sprite_prio <= tile_prio)
				{
					dstp[x] = sprite_pix;
				}
			}
		}
	}

	return 0;
}

void supracan_state::sound_timer_irq(int state)
{
	set_sound_irq(7, state);
}

void supracan_state::sound_dma_irq(int state)
{
	set_sound_irq(6, state);
}

template <unsigned ch> void supracan_state::dma_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	address_space &mem = m_maincpu->space(AS_PROGRAM);

	switch (offset)
	{
	case 0x00/2: // Source address MSW
		LOGMASKED(LOG_DMA, "dma_w: source msw %d: %04x\n", ch, data);
		m_dma_regs.source[ch] &= 0x0000ffff;
		m_dma_regs.source[ch] |= data << 16;
		break;
	case 0x02/2: // Source address LSW
		LOGMASKED(LOG_DMA, "dma_w: source lsw %d: %04x\n", ch, data);
		m_dma_regs.source[ch] &= 0xffff0000;
		m_dma_regs.source[ch] |= data;
		break;
	case 0x04/2: // Destination address MSW
		LOGMASKED(LOG_DMA, "dma_w: dest msw %d: %04x\n", ch, data);
		m_dma_regs.dest[ch] &= 0x0000ffff;
		m_dma_regs.dest[ch] |= data << 16;
		break;
	case 0x06/2: // Destination address LSW
		LOGMASKED(LOG_DMA, "dma_w: dest lsw %d: %04x\n", ch, data);
		m_dma_regs.dest[ch] &= 0xffff0000;
		m_dma_regs.dest[ch] |= data;
		break;
	case 0x08/2: // Byte count
		LOGMASKED(LOG_DMA, "dma_w: count %d: %04x\n", ch, data);
		m_dma_regs.count[ch] = data;
		break;
	case 0x0a/2: // Control
		LOGMASKED(LOG_DMA, "dma_w: control %d: %04x\n", ch, data);
		if (data & 0x8800)
		{
			LOGMASKED(LOG_DMA, "dma_w: Kicking off a DMA from %08x to %08x, %d bytes (%04x)\n", m_dma_regs.source[ch], m_dma_regs.dest[ch], m_dma_regs.count[ch] + 1, data);

			// formduel sets both for gameplay to work
			// TODO: verify which one is source and which destination
			const bool dest_dec = BIT(data, 10);
			const bool src_dec  = BIT(data, 9);

			if (dest_dec ^ src_dec)
				popmessage("DMA trigger %04x with one increment bit set %04x", data, data & 0x0600);

			for (int i = 0; i <= m_dma_regs.count[ch]; i++)
			{
				// staiwbbl wants to fill both VRAM and work RAM at startup,
				// and expects to transfer full count size for VRAM, half for work RAM.
				// Not providing this will cause all kinds of video and logic glitches.
				// TODO: pinpoint DMA modes here (at least upper bits 14-13 should do)
				if (data == 0xa800)
				{
					if ((m_dma_regs.dest[ch] & 0xfe0000) == 0xf40000)
					{
						mem.write_word(m_dma_regs.dest[ch], 0);
						m_dma_regs.dest[ch]   += dest_dec ? -2 : 2;
					}
					else
					{
						// rebelst expects D8-D15 to be transfered for the ROZ layer hex grids.
						const u8 src_byte = m_dma_regs.dest[ch] & 1;
						mem.write_byte(m_dma_regs.dest[ch], mem.read_byte(m_dma_regs.source[ch] + src_byte));
						m_dma_regs.dest[ch]   += dest_dec ? -1 : 1;
					}
				}
				else if (data & 0x1000)
				{
					mem.write_word(m_dma_regs.dest[ch], mem.read_word(m_dma_regs.source[ch]));
					m_dma_regs.dest[ch]   += dest_dec ? -2 : 2;
					m_dma_regs.source[ch] += src_dec  ? -2 : 2;
					if (data & 0x0100)
					{
						// staiwbbl, indirect transfers towards port $f00010-$1f
						// TODO: also used by sangofgt
						// will glitch on some super moves because it tries to transfer
						// multiple times to the VRAM port (normally size is set to 8x2 bytes)
						if ((m_dma_regs.dest[ch] & 0xf) == 0)
							m_dma_regs.dest[ch] -= 0x10;
					}
				}
				else
				{
					mem.write_byte(m_dma_regs.dest[ch], mem.read_byte(m_dma_regs.source[ch]));
					m_dma_regs.dest[ch]   += dest_dec ? -1 : 1;
					m_dma_regs.source[ch] += src_dec  ? -1 : 1;
				}
			}
			// TODO: are these DMA cycle steal?
			// There's no indication of a DMA status read so far that would indicate burst.
			//m_maincpu->spin_until_time(m_maincpu->cycles_to_attotime(m_dma_regs.count[ch] * 2));
		}
		else if (data != 0x0000) // fake DMA, used by C.U.G.
		{
			LOGMASKED(LOG_UNKNOWNS | LOG_DMA, "dma_w: Unknown DMA kickoff value of %04x (other regs %08x, %08x, %d)\n", data, m_dma_regs.source[ch], m_dma_regs.dest[ch], m_dma_regs.count[ch] + 1);
			fatalerror("dma_w: Unknown DMA kickoff value of %04x (other regs %08x, %08x, %d)\n",data, m_dma_regs.source[ch], m_dma_regs.dest[ch], m_dma_regs.count[ch] + 1);
		}
		break;
	default:
		LOGMASKED(LOG_UNKNOWNS | LOG_DMA, "dma_w: Unknown register: %08x = %04x & %04x\n", 0xe90020 + (offset << 1), data, mem_mask);
		break;
	}
}

// swap address around so that 64x64 tile can be decoded as 8x8 tiles..
void supracan_state::write_swapped_byte(int offset, uint8_t byte)
{
	int swapped_offset = bitswap<32>(offset, 31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,2,1,0,6,5,4,3);

	m_vram_addr_swapped[swapped_offset] = byte;
}

void supracan_state::vram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	COMBINE_DATA(&m_vram[offset]);

	// hack for 1bpp layer at startup
	write_swapped_byte(offset * 2, data >> 8);
	write_swapped_byte(offset * 2 + 1, (data & 0x00ff));

	// mark tiles of each depth as dirty
	m_gfxdecode->gfx(0)->mark_dirty((offset * 2) / 64);
	m_gfxdecode->gfx(1)->mark_dirty((offset * 2) / 32);
	m_gfxdecode->gfx(2)->mark_dirty((offset * 2) / 16);
	m_gfxdecode->gfx(3)->mark_dirty((offset * 2) / 512);
	m_gfxdecode->gfx(4)->mark_dirty((offset * 2) / 8);
}

void supracan_state::main_map(address_map &map)
{
	map(0x000000, 0x3fffff).view(m_main_loview);
	m_main_loview[0](0x000000, 0x3fffff).r(m_cart, FUNC(superacan_cart_slot_device::rom_r)),
	m_main_loview[0](0x000000, 0x000fff).rom().region(m_internal68, 0);
	m_main_loview[1](0x000000, 0x3fffff).r(m_cart, FUNC(superacan_cart_slot_device::rom_r));
	map(0xe80000, 0xe8ffff).rw(FUNC(supracan_state::_68k_soundram_r), FUNC(supracan_state::_68k_soundram_w));
	map(0xe90000, 0xe9001f).m(*this, FUNC(supracan_state::host_um6619_map));
	map(0xe90020, 0xe9002f).w(FUNC(supracan_state::dma_w<0>));
	map(0xe90030, 0xe9003f).w(FUNC(supracan_state::dma_w<1>));

	map(0xe90b3c, 0xe90b3d).noprw(); // noisy during lockout checks

	map(0xeb0d00, 0xeb0d03).rw(m_lockout, FUNC(umc6650_device::read), FUNC(umc6650_device::write)).umask16(0x00ff);

	map(0xec0000, 0xecffff).rw(m_cart, FUNC(superacan_cart_slot_device::nvram_r), FUNC(superacan_cart_slot_device::nvram_w)).umask16(0x00ff);

	map(0xf00000, 0xf001ff).rw(FUNC(supracan_state::video_r), FUNC(supracan_state::video_w));
	map(0xf00200, 0xf003ff).ram().w("palette", FUNC(palette_device::write16)).share("palette");
	map(0xf40000, 0xf5ffff).ram().w(FUNC(supracan_state::vram_w)).share("vram");
	map(0xf80000, 0xfbffff).view(m_main_hiview);
	m_main_hiview[0](0xf80000, 0xfbffff).r(m_cart, FUNC(superacan_cart_slot_device::rom_r));
	m_main_hiview[0](0xf80000, 0xf80fff).rom().region(m_internal68, 0);
	m_main_hiview[1](0xf80000, 0xfbffff).r(m_cart, FUNC(superacan_cart_slot_device::rom_r));
	map(0xfc0000, 0xfcffff).mirror(0x30000).ram(); /* System work ram */
}

uint8_t supracan_state::sound_ram_read(offs_t offset)
{
	return m_soundram[offset];
}

void supracan_state::set_sound_irq(uint8_t bit, uint8_t state)
{
	const uint8_t old = m_soundcpu_irq_source;
	if (state)
		m_soundcpu_irq_source |= 1 << bit;
	else
		m_soundcpu_irq_source &= ~(1 << bit);
	const uint8_t changed = old ^ m_soundcpu_irq_source;
	if (changed)
	{
		m_soundcpu->set_input_line(0, (m_soundcpu_irq_enable & m_soundcpu_irq_source) ? ASSERT_LINE : CLEAR_LINE);
	}
}

uint8_t supracan_state::_6502_soundmem_r(offs_t offset)
{
	uint8_t data = m_soundram[offset];

	switch (offset)
	{
	case 0x300: // Boot OK status
		break;
	case 0x402:
	case 0x403:
	{
		const uint8_t index = offset - 0x402;
		data = m_sound_cpu_shift_regs[index];
		if (!machine().side_effects_disabled())
		{
			LOGMASKED(LOG_SOUND, "%s: 6502_soundmem_r: Shift register %d read: %02x\n", machine().describe_context(), index, data);
		}
		break;
	}
	case 0x406:
		// staiwbbl: pad +5V presence?
		// Will flip and pass bits 1-0 to 0x407 writes
		return 0x00;
	case 0x410:
		data = m_soundcpu_irq_enable;
		if (!machine().side_effects_disabled())
		{
			LOGMASKED(LOG_SOUND, "%s: 6502_soundmem_r: IRQ enable read: %02x\n", machine().describe_context(), data);
		}
		break;
	case 0x411:
		data = m_soundcpu_irq_source;
		// TODO: should really check for further pending irqs before acking
		if (!machine().side_effects_disabled())
		{
			m_soundcpu_irq_source = 0;
			LOGMASKED(LOG_SOUND, "%s: %s: 6502_soundmem_r: Sound IRQ source read + clear: %02x\n", machine().describe_context(), machine().time().to_string(), data);
			m_soundcpu->set_input_line(0, CLEAR_LINE);
		}
		break;
	case 0x412:
		// NMI acknowledge
		break;
	case 0x420:
		if (!machine().side_effects_disabled())
		{
			data = m_sound_status;
			LOGMASKED(LOG_SOUND, "%s: %s: 6502_soundmem_r: Sound hardware status read:       0420 = %02x\n", machine().describe_context(), machine().time().to_string(), m_sound_status);
		}
		break;
	case 0x422:
		if (!machine().side_effects_disabled())
		{
			data = m_sound->read(m_sound_reg_addr);
			LOGMASKED(LOG_SOUND, "%s: %s: 6502_soundmem_r: Sound hardware reg data read:     0422 = %02x\n", machine().describe_context(), machine().time().to_string(), data);
		}
		break;
	case 0x404:
	case 0x405:
	case 0x409:
	case 0x414:
	case 0x416:
		// Intentional fall-through
	default:
		if (offset >= 0x400 && offset < 0x500)
		{
			if (!machine().side_effects_disabled())
			{
				LOGMASKED(LOG_SOUND | LOG_UNKNOWNS, "%s: 6502_soundmem_r: Unknown register %04x (%02x)\n", machine().describe_context(), offset, data);
			}
		}
		break;
	}

	return data;
}

void supracan_state::_6502_soundmem_w(offs_t offset, uint8_t data)
{
	static attotime s_curr_time = attotime::zero;
	attotime now = machine().time();

	switch (offset)
	{
	case 0x407:
	{
		LOGMASKED(LOG_CONTROLS, "%s: 6502_soundmem_w: Shift register control: %02x\n", machine().describe_context(), data);
		const uint8_t old = m_sound_cpu_shift_ctrl;
		m_sound_cpu_shift_ctrl = data;
		const uint8_t lowered = old & ~m_sound_cpu_shift_ctrl;
		for (uint8_t pad = 0; pad < 2; pad++)
		{
			if (BIT(lowered, pad + 0))
			{
				m_latched_controls[pad] = m_pad[pad]->read();
			}
			if (BIT(lowered, pad + 2))
			{
				m_sound_cpu_shift_regs[pad] <<= 1;
				m_sound_cpu_shift_regs[pad] |= BIT(m_latched_controls[pad], 15);
				m_latched_controls[pad] <<= 1;
			}
			if (BIT(lowered, pad + 4))
			{
				m_sound_cpu_shift_regs[pad] = 0;
			}
		}
		break;
	}
	// written with 0x06 at game startups, prior to enabling sound irqs
	// (NMI + master irq enable?)
	//case 0x409:
	//break;
	case 0x40a:
		// speedyd/magipool uses this to request main to kickoff a sound DMA.
		// gamblord/formduel just sets this just to poll a sound command
		// all sets up 0x40c/0x40d as a buffer, and 0x40a to check if the irq is valid
		// TODO: staiwbbl writes here from 68k side
		// which raises a nopped irq service for now, may just acknowledge instead
		m_maincpu->set_input_line(6, HOLD_LINE);
		m_soundram[0x40a] = data;
		break;
	case 0x410:
		m_soundcpu_irq_enable = data;
		// gamblord (at least) checks for pending irqs
		m_soundcpu->set_input_line(0, (m_soundcpu_irq_enable & m_soundcpu_irq_source) ? ASSERT_LINE : CLEAR_LINE);
		LOGMASKED(LOG_SOUND | LOG_IRQS, "%s: 6502_soundmem_w: IRQ enable: %02x\n", machine().describe_context(), data);
		break;
	case 0x420:
		LOGMASKED(LOG_SOUND, "%s: %s: 6502_soundmem_w: Sound addr write:                 0420 = %02x\n", machine().describe_context(), now.to_string(), data);
		m_sound_reg_addr = data;
		break;
	case 0x422:
	{
		attotime delta = (s_curr_time == attotime::zero ? attotime::zero : (now - s_curr_time));
		s_curr_time = now;
		LOGMASKED(LOG_SOUND, "%s: %s: 6502_soundmem_w: Sound data write:                 0422 = %02x (delta %0.3f)\n", machine().describe_context(), now.to_string(), data, (float)delta.as_double());
		m_sound->write(m_sound_reg_addr, data);
		break;
	}
	default:
		if (offset >= 0x400 && offset < 0x500)
		{
			LOGMASKED(LOG_SOUND | LOG_UNKNOWNS, "%s: 6502_soundmem_w: Unknown register %04x = %02x\n", machine().describe_context(), offset, data);
		}
		m_soundram[offset] = data;
		break;
	}
}

void supracan_state::sound_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(supracan_state::_6502_soundmem_r), FUNC(supracan_state::_6502_soundmem_w)).share("soundram");
}

void supracan_state::_68k_soundram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	m_soundram[offset * 2 + 1] = data & 0xff;
	m_soundram[offset * 2] = data >> 8;

	if (offset * 2 < 0x500 && offset * 2 >= 0x400)
	{
		if (ACCESSING_BITS_8_15)
		{
			_6502_soundmem_w(offset * 2, data >> 8);
		}
		if (ACCESSING_BITS_0_7)
		{
			_6502_soundmem_w(offset * 2 + 1, data & 0xff);
		}
	}
	LOGMASKED(LOG_68K_SOUND, "%s: 68k sound RAM write: %04x & %04x = %04x\n", machine().describe_context(), offset << 1, mem_mask, (uint16_t)data);
}

uint16_t supracan_state::_68k_soundram_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t data = m_soundram[offset * 2] << 8;
	data |= m_soundram[offset * 2 + 1];

	// formduel, jttlaugh & speedyd all wants polling in "direct mode" from 68k
	if ((offset * 2) == 0x200)
		return m_pad[0]->read() ^ 0xffff;

	if ((offset * 2) == 0x202)
		return m_pad[1]->read() ^ 0xffff;

	if (offset * 2 >= 0x400 && offset * 2 < 0x500)
	{
		data = 0;
		if (ACCESSING_BITS_8_15)
		{
			data |= _6502_soundmem_r(offset * 2) << 8;
		}
		if (ACCESSING_BITS_0_7)
		{
			data |= _6502_soundmem_r(offset * 2 + 1);
		}
	}
	//LOGMASKED(LOG_68K_SOUND, "%s: 68k sound RAM read: %04x & %04x (%04x)\n", machine().describe_context(), offset << 1, mem_mask, data);

	return data;
}

TIMER_CALLBACK_MEMBER(supracan_state::frc_timer_cb)
{
	m_maincpu->set_input_line(3, HOLD_LINE);

	update_frc_state();
}

void supracan_state::update_frc_state()
{
	if ((m_frc_control & 0xff00) == 0xa200)
	{
		const u32 period = ((m_frc_control & 0xff << 16) | (m_frc_frequency));

		// HACK: handle case by case until we resolve the equation
		// (particularly with variable frequencies)
		switch (m_frc_control & 0xf)
		{
		// speedyd: sets this up to 0xa0d6 / 0x0000 at boot, then goes 0xa200 0x013a
		// - dictates a very slow timer (pinpoint for what);
		// - would give massive slowdowns during gameplay if too many fires;
		case 0:
			m_frc_timer->adjust(attotime::from_hz(1));
			break;

		// magipool: sets 0xa201 / 0x0104 at startup, sometimes flips frequency to 0x0046
		// - causes a crash at boot if too fast;
		// - takes roughly 6 seconds for a title screen individual kanji to move right-to-left;
		case 1:
			m_frc_timer->adjust(m_maincpu->cycles_to_attotime(1024 * period), 0);
			break;

		// gamblord: sets 0xa20f normally, plays with frequency register a lot.
		// - takes ~13 seconds for title screen to complete animation;
		// - takes ~1 second for character screen to switch;
		// - during gameplay sometimes switches to 0xa200 / 0xffff;
		case 0xf:
			m_frc_timer->adjust(m_maincpu->cycles_to_attotime(8192 * period), 0);
			break;

		default:
			popmessage("Attempt to fire up FRC with %04x %04x", m_frc_control, m_frc_frequency);
			break;
		}
	}
	else
		m_frc_timer->adjust(attotime::never);
}

void supracan_state::host_um6619_map(address_map &map)
{
	map(0x04, 0x05).lr8(
		NAME([this] (offs_t offset) {
			const u8 res = m_soundram[0x40c + offset];
			LOGMASKED(LOG_SOUND, "%s DMA Request address from 6502 [%04x] %02x\n", machine().describe_context(), offset + 0x404, res);
			return res;
		})
	);
	// TODO: verify $b access
	map(0x0a, 0x0b).lw8(
		NAME([this] (offs_t offset, u8 data) {
			LOGMASKED(LOG_SOUND, "%s Sound CPU IRQ request [%02x]: %02x\n", machine().describe_context(), offset + 0x40a, data);
			set_sound_irq(5, 1);
		})
	);
	// TODO: verify $d access reads
	// noisy in staiwbbl, expects an 0xff value just after clearing $e8040a
	// Playing with it makes BGM and samples to be initialized properly in gameplay.
	map(0x0c, 0x0d).lr8(
		NAME([this] (offs_t offset) {
			const u8 res = m_soundram[0x40a];
			LOGMASKED(LOG_SOUND, "%s: DMA Request flag from 6502, [%02x]: %02x\n", machine().describe_context(), offset + 0x40c, res);
			return res;
		})
	);
	// games tend to write bytes, which implies a smearing mirror living here.
	map(0x10, 0x11).lrw8(
		NAME([this] (offs_t offset) {
			return m_irq_mask;
		}),
		NAME([this] (offs_t offset, u8 data) {
			// bit 7: enabled by slghtsag after BIOS (would otherwise address error)
			// other bits tbd (bit 3 doesn't seem irq 3 enable as per speedyd not enabling it)
			//logerror("irq mask %02x @ VPOS %d\n", data, m_screen->vpos());
			m_irq_mask = data;
		})
	);
	map(0x14, 0x15).lrw16(
		NAME([this] (offs_t offset) {
			return m_frc_control;
		}),
		NAME([this] (offs_t offset, u16 data, u16 mem_mask) {
			COMBINE_DATA(&m_frc_control);
			logerror("FRC control %04x & %04x\n", data, mem_mask);
			update_frc_state();
		})
	);
	map(0x16, 0x17).lrw16(
		NAME([this] (offs_t offset) {
			return m_frc_frequency;
		}),
		NAME([this] (offs_t offset, u16 data, u16 mem_mask) {
			COMBINE_DATA(&m_frc_frequency);
			logerror("FRC frequency %04x & %04x\n", data, mem_mask);
			update_frc_state();
		})
	);
	map(0x18, 0x19).lr16(
		NAME([this] (offs_t offset) {
			// formduel uses this to scroll the game over rain layer, in both X & Y directions.
			// TODO: details, obviously.
			if (!machine().side_effects_disabled())
				logerror("$e90018: RNG read?\n");
			return m_soundcpu->total_cycles() % (0xffff);
		})
	);
	/**
	 * x--- to hiview lockout
	 * -x-- internal ROM lockout?
	 * --x- to loview lockout
	 * ---x sound reset
	 */
	// TODO: likely 8-bit
	map(0x1c, 0x1d).lrw16(
		NAME([this] (offs_t offset) {
			// BIOS rmw the result, at least in speedyd
			return m_sound_cpu_ctrl;
		}),
		NAME([this] (offs_t offset, u16 data, u16 mem_mask) {
			const uint16_t old = m_sound_cpu_ctrl;
			COMBINE_DATA(&m_sound_cpu_ctrl);
			const uint16_t changed = old ^ m_sound_cpu_ctrl;
			if (BIT(changed, 3) && BIT(data, 3))
			{
				m_main_hiview.select(1);
			}

			if (BIT(changed, 1) && BIT(data, 1))
			{
				m_main_loview.select(1);
			}

			if (BIT(changed, 0))
			{
				if (BIT(m_sound_cpu_ctrl, 0))
				{
					/* Reset and enable the sound cpu */
					m_soundcpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
					m_soundcpu->reset();
				}
				else
				{
					/* Halt the sound cpu */
					m_soundcpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
				}
			}
			LOGMASKED(LOG_SOUND, "%s: Sound CPU ctrl write: %04x\n", machine().describe_context(), data);
		})
	);
}

uint16_t supracan_state::video_r(offs_t offset, uint16_t mem_mask)
{
	uint16_t data = m_video_regs[offset];

	switch (offset)
	{
	case 0x00/2: // Video IRQ flags
		data = m_screen->vpos() >= 240 ? 0x8000 : 0;
		// checked by sonevil in vblank routine, assume ODD flag
		if (m_screen->frame_number() & 1)
			data |= 2;
		if (!machine().side_effects_disabled())
		{
			LOGMASKED(LOG_HFVIDEO, "read video IRQ flags (%04x)\n", data);
			// TODO: should likely ack from the UM6619 bit 7 == 0 not here
			// sonevil will flip vblank mask a lot,
			// which may explain why it checks the current scanline inside irq service.
			m_maincpu->set_input_line(7, CLEAR_LINE);
		}
		return data;
	case 0x02/2: // Current scanline
		data = m_screen->vpos();
		LOGMASKED(LOG_VIDEO, "read current scanline (%04x / %d)\n", data, data);
		return data;
	case 0x08/2: // Unknown (not video flags!) - gambling lord disagrees, it MUST read back what it wrote because it reads it before turning on/off layers and writes it back
		LOGMASKED(LOG_VIDEO, "read video flags [0x08] %04x & %04x\n", m_video_flags, mem_mask);
		return m_video_flags;
	case 0x100/2:
		if (!machine().side_effects_disabled())
		{
			LOGMASKED(LOG_TILEMAP0, "read tilemap_flags[0] (%04x)\n", data);
		}
		break;
	case 0x106/2:
		if (!machine().side_effects_disabled())
		{
			LOGMASKED(LOG_TILEMAP0, "read tilemap_scrolly[0] (%04x)\n", data);
		}
		break;
	case 0x120/2:
		if (!machine().side_effects_disabled())
		{
			LOGMASKED(LOG_TILEMAP1, "read tilemap_flags[1] (%04x)\n", data);
		}
		break;
	case 0x1f0/2:
		return m_pixel_mode | m_gfx_mode;
	default:
		if (!machine().side_effects_disabled())
		{
			LOGMASKED(LOG_UNKNOWNS, "video_r: Unknown register: %08x (%04x & %04x)\n", 0xf00000 + (offset << 1), data, mem_mask);
		}
		break;
	}

	return data;
}


TIMER_CALLBACK_MEMBER(supracan_state::line_on_cb)
{
	m_maincpu->set_input_line(5, HOLD_LINE);

	m_line_on_timer->adjust(attotime::never);
}

TIMER_CALLBACK_MEMBER(supracan_state::line_off_cb)
{
	m_maincpu->set_input_line(5, CLEAR_LINE);

	m_line_on_timer->adjust(attotime::never);
}

// TODO: derive from param, merge with line_on/_off stuff
TIMER_CALLBACK_MEMBER(supracan_state::scanline_cb)
{
	int vpos = m_screen->vpos();

	switch (vpos)
	{
	case 0:
		// we really need better management of this
		mark_active_tilemap_all_dirty(0);
		mark_active_tilemap_all_dirty(1);
		mark_active_tilemap_all_dirty(2);
		mark_active_tilemap_all_dirty(3);
		break;

	case 240:
		if (BIT(m_irq_mask, 7))
		{
			LOGMASKED(LOG_IRQS, "Triggering VBL IRQ\n\n");
			m_maincpu->set_input_line(7, HOLD_LINE);
			// TODO: ack, from $412?
			// staiwbbl requires this for inputs to work
			m_soundcpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
		}
		break;
	default:
		// Effectively used by sangofgt only for clipping effects
		// gamblord, monopoly, magipool also enables this but service is rte for all.
		if (vpos < 240 && BIT(m_irq_mask, 4))
			m_maincpu->set_input_line(4, HOLD_LINE);
		break;
	}

	m_video_timer->adjust(m_screen->time_until_pos((vpos + 1) % 262, 0));
}

void supracan_state::video_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	address_space &mem = m_maincpu->space(AS_PROGRAM);

	// if any of this changes we need a partial update (see sangofgt intro)
	m_screen->update_partial(m_screen->vpos());

	COMBINE_DATA(&m_video_regs[offset]);
	data = m_video_regs[offset];

	switch (offset)
	{
	case 0x10/2: // Byte count
		LOGMASKED(LOG_SPRDMA, "sprite dma word count: %04x\n", data);
		m_sprdma_regs.count = data;
		break;
	case 0x12/2: // Destination address MSW
		m_sprdma_regs.dst &= 0x0000ffff;
		m_sprdma_regs.dst |= data << 16;
		LOGMASKED(LOG_SPRDMA, "sprite dma dest msw: %04x\n", data);
		break;
	case 0x14/2: // Destination address LSW
		m_sprdma_regs.dst &= 0xffff0000;
		m_sprdma_regs.dst |= data;
		LOGMASKED(LOG_SPRDMA, "sprite dma dest lsw: %04x\n", data);
		break;
	case 0x16/2: // Source word increment
		LOGMASKED(LOG_SPRDMA, "sprite dma dest word inc: %04x\n", data);
		m_sprdma_regs.dst_inc = data;
		break;
	case 0x18/2: // Source address MSW
		m_sprdma_regs.src &= 0x0000ffff;
		m_sprdma_regs.src |= data << 16;
		LOGMASKED(LOG_SPRDMA, "sprite dma src msw: %04x\n", data);
		break;
	case 0x1a/2: // Source address LSW
		LOGMASKED(LOG_SPRDMA, "sprite dma src lsw: %04x\n", data);
		m_sprdma_regs.src &= 0xffff0000;
		m_sprdma_regs.src |= data;
		break;
	case 0x1c/2: // Source word increment
		LOGMASKED(LOG_SPRDMA, "sprite dma src word inc: %04x\n", data);
		m_sprdma_regs.src_inc = data;
		break;
	case 0x1e/2:
		LOGMASKED(LOG_SPRDMA, "video_w: Kicking off a DMA from %08x to %08x, %d bytes (%04x)\n", m_sprdma_regs.src, m_sprdma_regs.dst, m_sprdma_regs.count, data);

		/* TODO: what's 0x2000 and 0x4000 for? */
		if (data & 0x8000)
		{
			if (data & 0x2000 || data & 0x4000)
			{
				m_sprdma_regs.dst |= 0xf40000;
			}

			if (data & 0x2000)
			{
				//m_sprdma_regs.count <<= 1;
			}

			for (int i = 0; i <= m_sprdma_regs.count; i++)
			{
				if (data & 0x0100) // dma 0x00 fill (or fixed value?)
				{
					mem.write_word(m_sprdma_regs.dst, 0);
					m_sprdma_regs.dst += 2 * m_sprdma_regs.dst_inc;
					//memset(vram, 0x00, 0x020000);
				}
				else
				{
					mem.write_word(m_sprdma_regs.dst, mem.read_word(m_sprdma_regs.src));
					m_sprdma_regs.dst += 2 * m_sprdma_regs.dst_inc;
					m_sprdma_regs.src += 2 * m_sprdma_regs.src_inc;
				}
			}
		}
		else
		{
			LOGMASKED(LOG_SPRDMA | LOG_UNKNOWNS, "dma_w: Attempting to kick off a DMA without bit 15 set! (%04x)\n", data);
		}
		break;
	case 0x08/2:
		if (data != m_video_flags)
		{
			LOGMASKED(LOG_VIDEO, "video_flags = %04x\n", data);

			if (data & 0xc00)
				popmessage("Interlace enable %04x", data & 0xc00);

			// TODO: verify if this support midframe switching
			if ((data & 0x300) != (m_video_flags & 0x300))
			{
				rectangle visarea = m_screen->visible_area();
				const int h320_mode = BIT(data, 8);
				// enabled by sangofgt (224 + 16 borders), magipool wants (240)
				const int overscan_mode = BIT(data, 9);

				const int htotal = h320_mode ? 455 : 342;
				const int divider = h320_mode ? 8 : 10;

				const int vdisplay_start = overscan_mode ? 8 : 0;
				const int vdisplay_end = overscan_mode ? 232 : 240;

				visarea.set(0, (h320_mode ? 320 : 256) - 1, vdisplay_start, vdisplay_end - 1);
				m_screen->configure(htotal, 262, visarea, attotime::from_ticks(htotal * 262, U13_CLOCK / divider).as_attoseconds());
				//m_screen->reset_origin(0, 0);
			}

			m_video_flags = data;
		}
		break;
	case 0x0a/2:
		// raster interrupt
		LOGMASKED(LOG_IRQS, "Raster 'line on' IRQ Trigger write? = %04x\n", data);
		if (data & 0x8000)
		{
			// NOTE: sangofgt draws a garbage line strip at target line, but this happens on real HW as well
			m_line_on_timer->adjust(m_screen->time_until_pos((data & 0x00ff), 0));
		}
		else
		{
			m_line_on_timer->adjust(attotime::never);
		}
		break;

	case 0x0c/2:
		LOGMASKED(LOG_IRQS, "Raster 'line off' IRQ Trigger write? = %04x\n", data);
		if (data & 0x8000)
		{
			m_line_off_timer->adjust(m_screen->time_until_pos(data & 0x00ff, 0));
		}
		else
		{
			m_line_off_timer->adjust(attotime::never);
		}
		break;

	/* Sprites */
	case 0x20/2: m_sprite_base_addr = data << 2; LOGMASKED(LOG_SPRITES, "sprite_base_addr = %04x\n", data); break;
	case 0x22/2: m_sprite_count = data + 1; LOGMASKED(LOG_SPRITES, "sprite_count = %d\n", data + 1); break;
	case 0x24/2: m_sprite_mono_color = data & 0xff; break;
	case 0x26/2: m_sprite_flags = data; LOGMASKED(LOG_SPRITES, "sprite_flags = %04x\n", data); break;

	/* Tilemap 0 */
	case 0x100/2:
	{
		m_tilemap_flags[0] = data;
		LOGMASKED(LOG_TILEMAP0, "tilemap_flags[0] = %04x\n", data);
		update_tilemap_flags(0);
		break;
	}
	case 0x102/2: m_tilemap_tile_mode[0] = data; break;
	case 0x104/2: m_tilemap_scrollx[0] = data; LOGMASKED(LOG_TILEMAP0, "tilemap_scrollx[0] = %04x\n", data); break;
	case 0x106/2: m_tilemap_scrolly[0] = data; LOGMASKED(LOG_TILEMAP0, "tilemap_scrolly[0] = %04x\n", data); break;
	case 0x108/2: m_tilemap_base_addr[0] = data << 1; LOGMASKED(LOG_TILEMAP0, "tilemap_base_addr[0] = %05x\n", data << 2); break;
	case 0x10a/2: m_tilemap_mode[0] = data; LOGMASKED(LOG_TILEMAP0, "tilemap_mode[0] = %04x\n", data); break;
	case 0x10c/2: m_tilemap_linescrollx_addr[0] = data; break;
	case 0x10e/2: m_tilemap_lineselect_addr[0] = data; break;

	/* Tilemap 1 */
	case 0x120/2:
	{
		m_tilemap_flags[1] = data;
		LOGMASKED(LOG_TILEMAP1, "tilemap_flags[1] = %04x\n", data);
		update_tilemap_flags(1);
		break;
	}
	case 0x122/2: m_tilemap_tile_mode[1] = data; break;
	case 0x124/2: m_tilemap_scrollx[1] = data; LOGMASKED(LOG_TILEMAP1, "tilemap_scrollx[1] = %04x\n", data); break;
	case 0x126/2: m_tilemap_scrolly[1] = data; LOGMASKED(LOG_TILEMAP1, "tilemap_scrolly[1] = %04x\n", data); break;
	case 0x128/2: m_tilemap_base_addr[1] = data << 1; LOGMASKED(LOG_TILEMAP1, "tilemap_base_addr[1] = %05x\n", data << 2); break;
	case 0x12a/2: m_tilemap_mode[1] = data; LOGMASKED(LOG_TILEMAP1, "tilemap_mode[1] = %04x\n", data); break;
	case 0x12c/2: m_tilemap_linescrollx_addr[1] = data; break;
	case 0x12e/2: m_tilemap_lineselect_addr[1] = data; break;

	/* Tilemap 2 */
	case 0x140/2:
	{
		m_tilemap_flags[2] = data;
		LOGMASKED(LOG_TILEMAP2, "tilemap_flags[2] = %04x\n", data);
		update_tilemap_flags(2);
		break;
	}
	case 0x142/2: m_tilemap_tile_mode[2] = data; break;
	case 0x144/2: m_tilemap_scrollx[2] = data; LOGMASKED(LOG_TILEMAP2, "tilemap_scrollx[2] = %04x\n", data); break;
	case 0x146/2: m_tilemap_scrolly[2] = data; LOGMASKED(LOG_TILEMAP2, "tilemap_scrolly[2] = %04x\n", data); break;
	case 0x148/2: m_tilemap_base_addr[2] = data << 1; LOGMASKED(LOG_TILEMAP2, "tilemap_base_addr[2] = %05x\n", data << 2); break;
	case 0x14a/2: m_tilemap_mode[2] = data; LOGMASKED(LOG_TILEMAP2, "tilemap_mode[2] = %04x\n", data); break;
	case 0x14c/2: m_tilemap_linescrollx_addr[2] = data; break;
	case 0x14e/2: m_tilemap_lineselect_addr[2] = data; break;

	/* ROZ */
	case 0x180/2:
	{
		m_roz_mode = data;
		LOGMASKED(LOG_ROZ, "roz_mode = %04x\n", data);
		//update_tilemap_flags(ROZ_LAYER_NUMBER);
		break;
	}
	case 0x182/2: m_roz_tile_mode = data; break;
	case 0x184/2: m_roz_scrollx = (data << 16) | (m_roz_scrollx & 0xffff); m_roz_changed |= 1; LOGMASKED(LOG_ROZ, "roz_scrollx = %08x\n", m_roz_scrollx); break;
	case 0x186/2: m_roz_scrollx = (data) | (m_roz_scrollx & 0xffff0000); m_roz_changed |= 1; LOGMASKED(LOG_ROZ, "roz_scrollx = %08x\n", m_roz_scrollx); break;
	case 0x188/2: m_roz_scrolly = (data << 16) | (m_roz_scrolly & 0xffff); m_roz_changed |= 2; LOGMASKED(LOG_ROZ, "roz_scrolly = %08x\n", m_roz_scrolly); break;
	case 0x18a/2: m_roz_scrolly = (data) | (m_roz_scrolly & 0xffff0000); m_roz_changed |= 2; LOGMASKED(LOG_ROZ, "roz_scrolly = %08x\n", m_roz_scrolly); break;
	case 0x18c/2: m_roz_coeffa = data; LOGMASKED(LOG_ROZ, "roz_coeffa = %04x\n", data); break;
	case 0x18e/2: m_roz_coeffb = data; LOGMASKED(LOG_ROZ, "roz_coeffb = %04x\n", data); break;
	case 0x190/2: m_roz_coeffc = data; LOGMASKED(LOG_ROZ, "roz_coeffc = %04x\n", data); break;
	case 0x192/2: m_roz_coeffd = data; LOGMASKED(LOG_ROZ, "roz_coeffd = %04x\n", data); break;
	case 0x194/2: m_roz_base_addr = data << 1; LOGMASKED(LOG_ROZ, "roz_base_addr = %05x\n", data << 2); break;
	case 0x196/2: m_roz_tile_bank = data; LOGMASKED(LOG_ROZ, "roz_tile_bank = %04x\n", data); break; //tile bank
	case 0x198/2: m_roz_unk_base0 = data << 2; LOGMASKED(LOG_ROZ, "roz_unk_base0 = %05x\n", data << 2); break;
	case 0x19a/2: m_roz_unk_base1 = data << 2; LOGMASKED(LOG_ROZ, "roz_unk_base1 = %05x\n", data << 2); break;
	case 0x19e/2: m_roz_unk_base2 = data << 2; LOGMASKED(LOG_ROZ, "roz_unk_base2 = %05x\n", data << 2); break;

	// color mixing stuff goes here
	case 0x1d0/2: COMBINE_DATA(&m_window_control[0]); break;
	case 0x1d2/2: COMBINE_DATA(&m_window_start_addr[0]); break;
	case 0x1d4/2: COMBINE_DATA(&m_window_scrollx[0]); break;
	case 0x1d6/2: COMBINE_DATA(&m_window_scrolly[0]); break;
	case 0x1d8/2: COMBINE_DATA(&m_window_control[1]); break;
	case 0x1da/2: COMBINE_DATA(&m_window_start_addr[1]); break;
	case 0x1dc/2: COMBINE_DATA(&m_window_scrollx[1]); break;
	case 0x1de/2: COMBINE_DATA(&m_window_scrolly[1]); break;

	case 0x1f0/2:
		m_pixel_mode = data & 0x18;
		m_gfx_mode = data & 0x7;
		if (m_pixel_mode & 0x10)
			popmessage("Special pixel mode enabled!");
		if (m_gfx_mode >= 5)
			popmessage("Reserved GFX mode set %02x", data);
		//LOGMASKED(LOG_IRQS, "irq_mask = %04x\n", data);
		break;
	default:
		LOGMASKED(LOG_UNKNOWNS, "video_w: Unknown register: %08x = %04x & %04x\n", 0xf00000 + (offset << 1), data, mem_mask);
		break;
	}
//  m_video_regs[offset] = data;
}

static INPUT_PORTS_START( supracan )
	PORT_START("P1")
	PORT_BIT(0x000f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT(0x0010, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(1) PORT_NAME("P1 Button R")
	PORT_BIT(0x0020, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(1) PORT_NAME("P1 Button L")
	PORT_BIT(0x0040, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("P1 Button Y")
	PORT_BIT(0x0080, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("P1 Button X")
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("P1 Joypad Right")
	PORT_BIT(0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_NAME("P1 Joypad Left")
	PORT_BIT(0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_NAME("P1 Joypad Down")
	PORT_BIT(0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_NAME("P1 Joypad Up")
	PORT_BIT(0x1000, IP_ACTIVE_LOW, IPT_SELECT ) PORT_PLAYER(1)
	PORT_BIT(0x2000, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT(0x4000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("P1 Button B")
	PORT_BIT(0x8000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("P1 Button A")

	PORT_START("P2")
	PORT_BIT(0x000f, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT(0x0010, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(2) PORT_NAME("P2 Button R")
	PORT_BIT(0x0020, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(2) PORT_NAME("P2 Button L")
	PORT_BIT(0x0040, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("P2 Button Y")
	PORT_BIT(0x0080, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 Button X")
	PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_NAME("P2 Joypad Right")
	PORT_BIT(0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_NAME("P2 Joypad Left")
	PORT_BIT(0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_NAME("P2 Joypad Down")
	PORT_BIT(0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2) PORT_NAME("P2 Joypad Up")
	PORT_BIT(0x1000, IP_ACTIVE_LOW, IPT_SELECT ) PORT_PLAYER(2)
	PORT_BIT(0x2000, IP_ACTIVE_LOW, IPT_START2 )
	PORT_BIT(0x4000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("P2 Button B")
	PORT_BIT(0x8000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 Button A")
INPUT_PORTS_END


void supracan_state::machine_start()
{
	save_item(NAME(m_dma_regs.source));
	save_item(NAME(m_dma_regs.dest));
	save_item(NAME(m_dma_regs.count));
	save_item(NAME(m_dma_regs.control));

	save_item(NAME(m_sprdma_regs.src));
	save_item(NAME(m_sprdma_regs.src_inc));
	save_item(NAME(m_sprdma_regs.dst));
	save_item(NAME(m_sprdma_regs.dst_inc));
	save_item(NAME(m_sprdma_regs.count));
	save_item(NAME(m_sprdma_regs.control));

	save_item(NAME(m_sound_cpu_ctrl));
	save_item(NAME(m_soundcpu_irq_enable));
	save_item(NAME(m_soundcpu_irq_source));
	save_item(NAME(m_sound_cpu_shift_ctrl));
	save_item(NAME(m_sound_cpu_shift_regs));
	save_item(NAME(m_latched_controls));
	save_item(NAME(m_sound_status));
	save_item(NAME(m_sound_reg_addr));

	save_item(NAME(m_sprite_count));
	save_item(NAME(m_sprite_base_addr));
	save_item(NAME(m_sprite_flags));

	save_item(NAME(m_tilemap_base_addr));
	save_item(NAME(m_tilemap_scrollx));
	save_item(NAME(m_tilemap_scrolly));
	save_item(NAME(m_video_flags));
	save_item(NAME(m_tilemap_flags));
	save_item(NAME(m_tilemap_mode));
	save_item(NAME(m_irq_mask));

	save_item(NAME(m_roz_base_addr));
	save_item(NAME(m_roz_mode));
	save_item(NAME(m_roz_scrollx));
	save_item(NAME(m_roz_scrolly));
	save_item(NAME(m_roz_tile_bank));
	save_item(NAME(m_roz_unk_base0));
	save_item(NAME(m_roz_unk_base1));
	save_item(NAME(m_roz_unk_base2));
	save_item(NAME(m_roz_coeffa));
	save_item(NAME(m_roz_coeffb));
	save_item(NAME(m_roz_coeffc));
	save_item(NAME(m_roz_coeffd));
	save_item(NAME(m_roz_changed));
	save_item(NAME(m_window_control));
	save_item(NAME(m_window_start_addr));
	save_item(NAME(m_window_scrollx));
	save_item(NAME(m_window_scrolly));

	save_item(NAME(m_video_regs));

	m_video_timer = timer_alloc(FUNC(supracan_state::scanline_cb), this);
	m_frc_timer = timer_alloc(FUNC(supracan_state::frc_timer_cb), this);
	m_line_on_timer = timer_alloc(FUNC(supracan_state::line_on_cb), this);
	m_line_off_timer = timer_alloc(FUNC(supracan_state::line_off_cb), this);
}


void supracan_state::machine_reset()
{
	m_main_loview.select(0);
	m_main_hiview.select(0);

	m_sprite_count = 0;
	m_sprite_base_addr = 0;
	m_sprite_flags = 0;

	m_sound_cpu_ctrl = 0;
	m_soundcpu_irq_enable = 0;
	m_soundcpu_irq_source = 0;
	m_sound_cpu_shift_ctrl = 0;
	std::fill(std::begin(m_sound_cpu_shift_regs), std::end(m_sound_cpu_shift_regs), 0);
	std::fill(std::begin(m_latched_controls), std::end(m_latched_controls), 0);
	m_sound_status = 0;
	m_sound_reg_addr = 0;

	m_soundcpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);

	m_video_timer->adjust(m_screen->time_until_pos(0, 0));
	m_irq_mask = 0;

	m_roz_base_addr = 0;
	m_roz_mode = 0;
	std::fill(std::begin(m_tilemap_base_addr), std::end(m_tilemap_base_addr), 0);

	// TODO: sound BIOS may really view select with a register bit
	// 0x0000-0x0fff empty except for a 0x08 at $40f.
	// 0x1000-0x3fff samples, BIOS uses only a small portion of it at 0x2e00-0x2eff (?)
	std::copy_n(&m_internal6502[0], 0x4000, &m_soundram[0]);
}

static const gfx_layout supracan_gfx8bpp =
{
	8, 8,
	RGN_FRAC(1, 1),
	8,
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	{ STEP8(0, 8*8) },
	8*8*8
};



static const gfx_layout supracan_gfx4bpp =
{
	8, 8,
	RGN_FRAC(1, 1),
	4,
	{ 0, 1, 2, 3 },
	{ 0*4, 1*4, 2*4, 3*4, 4*4, 5*4, 6*4, 7*4 },
	{ 0*32, 1*32, 2*32, 3*32, 4*32, 5*32, 6*32, 7*32 },
	8*32
};

static const gfx_layout supracan_gfx2bpp =
{
	8, 8,
	RGN_FRAC(1, 1),
	2,
	{ 0, 1 },
	{ 0*2, 1*2, 2*2, 3*2, 4*2, 5*2, 6*2, 7*2 },
	{ 0*16, 1*16, 2*16, 3*16, 4*16, 5*16, 6*16, 7*16 },
	8*16
};

static const uint32_t xtexlayout_xoffset[64] =
{
	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,
	24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,
	44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63
};

static const uint32_t xtexlayout_yoffset[64] =
{
	0*64,1*64,2*64,3*64,4*64,5*64,6*64,7*64,
	8*64,9*64,10*64,11*64,12*64,13*64,14*64,15*64,
	16*64,17*64,18*64,19*64,20*64,21*64,22*64,23*64,
	24*64,25*64,26*64,27*64,28*64,29*64,30*64,31*64,
	32*64,33*64,34*64,35*64,36*64,37*64,38*64,39*64,
	40*64,41*64,42*64,43*64,44*64,45*64,46*64,47*64,
	48*64,49*64,50*64,51*64,52*64,53*64,54*64,55*64,
	56*64,57*64,58*64,59*64,60*64,61*64,62*64,63*64
};

static const gfx_layout supracan_gfx1bpp =
{
	64, 64,
	RGN_FRAC(1, 1),
	1,
	{ 0 },
	EXTENDED_XOFFS,
	EXTENDED_YOFFS,
	64*64,
	xtexlayout_xoffset,
	xtexlayout_yoffset
};

static const gfx_layout supracan_gfx1bpp_alt =
{
	8, 8,
	RGN_FRAC(1, 1),
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};


static GFXDECODE_START( gfx_supracan )
	GFXDECODE_RAM( "vram", 0, supracan_gfx8bpp, 0, 1 )
	GFXDECODE_RAM( "vram", 0, supracan_gfx4bpp, 0, 0x10 )
	GFXDECODE_RAM( "vram", 0, supracan_gfx2bpp, 0, 0x40 )
	GFXDECODE_RAM( "vram", 0, supracan_gfx1bpp, 0, 0x80 )
	GFXDECODE_RAM( "vram", 0, supracan_gfx1bpp_alt, 0, 0x80 )
GFXDECODE_END

static void superacan_cart_types(device_slot_interface &device)
{
	device.option_add_internal("std",      SUPERACAN_ROM_STD);
}


void supracan_state::supracan(machine_config &config)
{
	// M68000P10
	M68000(config, m_maincpu, U13_CLOCK / 6);
	m_maincpu->set_addrmap(AS_PROGRAM, &supracan_state::main_map);

	// TODO: Verify type and actual clock
	// /4 makes speedyd to fail booting
	W65C02(config, m_soundcpu, U13_CLOCK / 6 / 2);
	m_soundcpu->set_addrmap(AS_PROGRAM, &supracan_state::sound_map);

	config.set_perfect_quantum(m_soundcpu);

	UMC6650(config, m_lockout, 0);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(U13_CLOCK / 10, 342, 0, 256, 262, 8, 232);
	m_screen->set_screen_update(FUNC(supracan_state::screen_update));
	m_screen->set_palette("palette");
	//m_screen->screen_vblank().set(FUNC(supracan_state::screen_vblank));

	PALETTE(config, "palette").set_format(palette_device::xBGR_555, 256);

	GFXDECODE(config, m_gfxdecode, "palette", gfx_supracan);

	SPEAKER(config, "speaker", 2).front();

	// TODO: derive and verify from U13_CLOCK
	UMC6619_SOUND(config, m_sound, XTAL(3'579'545));
	m_sound->ram_read().set(FUNC(supracan_state::sound_ram_read));
	m_sound->timer_irq_handler().set(FUNC(supracan_state::sound_timer_irq));
	m_sound->dma_irq_handler().set(FUNC(supracan_state::sound_dma_irq));
	m_sound->add_route(0, "speaker", 1.0, 0);
	m_sound->add_route(1, "speaker", 1.0, 1);

	// TODO: clock for cart is (again) unconfirmed
	SUPERACAN_CART_SLOT(config, m_cart, U13_CLOCK / 6, superacan_cart_types, nullptr).set_must_be_loaded(true);

	SOFTWARE_LIST(config, "cart_list").set_original("supracan");
}

ROM_START( supracan )
	ROM_REGION16_BE(0x1000, "internal68", ROMREGION_ERASEFF)
	// 68k internal ROM (security related)
	ROM_LOAD16_WORD_SWAP( "internal_68k.bin", 0x0000,  0x1000, CRC(8d575662) SHA1(a8e75633662978d0a885f16a4ed0f898f278a10a) )

	ROM_REGION(0x4000, "internal6502", ROMREGION_ERASEFF)
	// 2 additional blocks of ROM can be seen next to the 68k ROM on a die shot from Furrtek
	ROM_LOAD( "internal_6502_1.bin", 0x0000,  0x2000, CRC(fc9fb05f) SHA1(8bf17bf311afeb9974bee058ba63eef5e8b6f5c1) )
	ROM_LOAD( "internal_6502_2.bin", 0x2000,  0x2000, CRC(bf950ab7) SHA1(ab8f15506308b89d2f8ef01b88aa2595d4e1e779) )
ROM_END

} // Anonymous namespace


/*    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     STATE           INIT        COMPANY                  FULLNAME        FLAGS */
CONS( 1995, supracan, 0,      0,      supracan, supracan, supracan_state, empty_init, "Funtech Entertainment", "Super A'Can",  MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING )



sv8000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol, Robbbert
/***************************************************************************

Bandai Super Vision 8000 (TV-Jack Micro Computer System) (aka TV Jack 8000)
TV JACK スーパービジョン8000

driver by Wilbert Pol, Robbbert, ranger_lennier, and Charles McDonald

The Bandai Super Vision 8000 contains:
- NEC D780C (Z80)
- AY-3-8910
- AMI S68047P (6847 variant)
- NEC D8255C

Looking at the code of the cartridges it seems there is:
- 1KB of main system RAM
- 3KB of video RAM

TODO:
- Check configuration of S68047P pins through 8910 port A against PCB
- Verify clocks, verify exact model of soundchip
- Verify colors, it kind of matches videos/photos, but it's much worse with
  the default S68047P palette (games don't use the first 8 colors)

****************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/z80/z80.h"
#include "machine/i8255.h"
#include "sound/ay8910.h"
#include "video/mc6847.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class sv8000_state : public driver_device
{
public:
	sv8000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_s68047p(*this, "s68047p")
		, m_videoram(*this, "videoram")
		, m_io_row(*this, "ROW%u", 0)
	{ }

	void sv8000(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void ay_port_a_w(u8 data);

	u8 i8255_porta_r();
	u8 i8255_portb_r();
	void i8255_portc_w(u8 data);

	u8 mc6847_videoram_r(offs_t offset);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<s68047_device> m_s68047p;
	required_shared_ptr<u8> m_videoram;
	required_ioport_array<3> m_io_row;

	u8 m_select = 0xff;
	u8 m_ag = 0U;
};

void sv8000_state::machine_start()
{
	save_item(NAME(m_select));
	save_item(NAME(m_ag));
}



/*******************************************************************************
    I/O
*******************************************************************************/

// Palette, most notably for the 2nd color set it expects a blue background instead of black

static const u32 sv8000_palette[16] =
{
	rgb_t(0x30, 0xd2, 0x00), // GREEN
	rgb_t(0xc1, 0xe5, 0x00), // YELLOW
	rgb_t(0x41, 0xaf, 0x71), // CYAN
	rgb_t(0x9a, 0x32, 0x36), // RED
	rgb_t(0x4c, 0x3a, 0xb4), // BLUE
	rgb_t(0x20, 0xd8, 0xe0), // CYAN/BLUE
	rgb_t(0xc8, 0x4e, 0xf0), // MAGENTA
	rgb_t(0xd4, 0x7f, 0x00), // ORANGE

	rgb_t(0x20, 0x40, 0x00), // BLACK -> DARK GREEN
	rgb_t(0xff, 0xf8, 0x70), // GREEN -> YELLOW
	rgb_t(0x18, 0x30, 0xb0), // BLACK -> BLUE
	rgb_t(0xf0, 0xff, 0xff), // CYAN/BLUE -> BRIGHT CYAN

	rgb_t(0x20, 0x40, 0x00), // BLACK -> DARK GREEN
	rgb_t(0xff, 0xf8, 0x70), // GREEN -> YELLOW
	rgb_t(0x18, 0x30, 0xb0), // BLACK -> BLUE
	rgb_t(0xf0, 0xff, 0xff), // BLUE -> BRIGHT CYAN
};

void sv8000_state::ay_port_a_w(u8 data)
{
	// Lacking schematics, these are all wild guesses, see below for values written
	/*
	misvader:
	0x42 01000010 text
	0x5A 01011010 graphics

	spfire:
	0x42 01000010 text
	0x5A 01011010 graphics

	othello:
	0x02 00000010 text
	0x58 01011000 graphics

	gunprof:
	0x00 00000000 text
	0x38 00111000 graphics

	pacpac:
	0x00 00000000 text
	0x5A 01011010 graphics

	submar:
	0x00 00000000 text
	0x5A 01011010 graphics

	beamgala:
	0x5A 01011010 graphics
	*/

	m_s68047p->css_w(BIT(data, 1));
	m_s68047p->ag_w(BIT(data, 3));
	m_s68047p->gm0_w(BIT(data, 4));
	m_s68047p->gm1_w(BIT(data, 5));
	m_s68047p->gm2_w(BIT(data, 6));

	m_ag = BIT(data, 3);
}

u8 sv8000_state::mc6847_videoram_r(offs_t offset)
{
	offset &= 0xfff;
	if (offset >= 0xc00)
		return 0xff;

	// Graphics
	if (m_ag)
		return m_videoram[offset];

	// Standard text
	u8 data = m_videoram[offset];
	if (data == 0)
		data = 0x20; // bodge

	m_s68047p->inv_w(BIT(data, 7));

	return data;
}

u8 sv8000_state::i8255_portb_r()
{
	u8 data = 0xff;

	// Read keypads
	for (int i = 0; i < 3; i++)
		if (!BIT(m_select, i))
			data &= m_io_row[i]->read();

	return data;
}

void sv8000_state::i8255_portc_w(u8 data)
{
	m_select = data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void sv8000_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).r("cartslot", FUNC(generic_slot_device::read_rom));
	map(0x8000, 0x83ff).ram(); // Work RAM
	map(0xc000, 0xcfff).ram().share(m_videoram);
}

void sv8000_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x80, 0x83).rw("i8255", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xc0, 0xc0).w("ay8910", FUNC(ay8910_device::data_w));
	map(0xc1, 0xc1).w("ay8910", FUNC(ay8910_device::address_w));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

/*
On the main console:

1 2 3                   1 2 3
4 5 6                   4 5 6
7 8 9                   7 8 9
* 0 #                   * 0 #

D-Pad   POWER   RESET   D-Pad
*/
static INPUT_PORTS_START( sv8000 )
	PORT_START("ROW0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME("Right *")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Right 7")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Right 4")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Right 1")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("Left *")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Left 7")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Left 4")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Left 1")

	PORT_START("ROW1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("Right 0")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Right 8")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Right 5")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Right 2")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("Left 0")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Left 8")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Left 5")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Left 2")

	PORT_START("ROW2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Right #")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("Right 9")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Right 6")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Right 3")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Left #")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Left 9")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Left 6")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Left 3")

	PORT_START("JOY")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP) PORT_NAME("Right Up")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN) PORT_NAME("Right Down")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT) PORT_NAME("Right Left")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_NAME("Right Right")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Left Up")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_K) PORT_NAME("Left Down")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_J) PORT_NAME("Left Left")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Left Right")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void sv8000_state::sv8000(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 3.579545_MHz_XTAL/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &sv8000_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &sv8000_state::io_map);
	m_maincpu->set_vblank_int("screen", FUNC(sv8000_state::irq0_line_hold));

	i8255_device &ppi(I8255(config, "i8255"));
	ppi.in_pa_callback().set_ioport("JOY");
	ppi.in_pb_callback().set(FUNC(sv8000_state::i8255_portb_r));
	ppi.out_pc_callback().set(FUNC(sv8000_state::i8255_portc_w));

	// video hardware
	S68047(config, m_s68047p, 3.579545_MHz_XTAL);
	m_s68047p->input_callback().set(FUNC(sv8000_state::mc6847_videoram_r));
	m_s68047p->set_palette(sv8000_palette);
	m_s68047p->set_screen("screen");

	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	ay8910_device &ay8910(AY8910(config, "ay8910", 3.579545_MHz_XTAL/2));
	ay8910.port_a_write_callback().set(FUNC(sv8000_state::ay_port_a_w));
	ay8910.add_route(ALL_OUTPUTS, "mono", 0.50);

	// cartridge
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "sv8000_cart").set_must_be_loaded(true);
	SOFTWARE_LIST(config, "cart_list").set_original("sv8000");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( sv8000 )
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASEFF ) // Mapped by the cartridge slot
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT   MACHINE  INPUT   STATE         INIT        COMPANY   FULLNAME             FLAGS
CONS( 1979, sv8000, 0,      0,       sv8000,  sv8000, sv8000_state, empty_init, "Bandai", "Super Vision 8000", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_COLORS )



svi318.cpp
<---------------------------------------------------------------------->
// license: GPL-2.0+
// copyright-holders: Dirk Best
// thanks-to: Tomas Karlsson, Sean Young
/***************************************************************************

    Spectravideo SVI-318/328

***************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "machine/bankdev.h"
#include "machine/i8255.h"
#include "machine/ram.h"
#include "sound/ay8910.h"
#include "sound/spkrdev.h"
#include "video/tms9928a.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "bus/svi3x8/expander/expander.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "formats/svi_cas.h"

#include "utf8.h"


namespace {

//**************************************************************************
//  CONSTANTS & MACROS
//**************************************************************************

#define IS_SVI328  (m_ram->size() == 64 * 1024)

#define CCS1       (m_cart == 0 && offset < 0x4000)
#define CCS2       (m_cart == 0 && offset >= 0x4000 && offset < 0x8000)
#define CCS3       (m_cart == 0 && m_rom2 == 0 && offset >= 0x8000 && offset < 0xc000)
#define CCS4       (m_cart == 0 && m_rom3 == 0 && offset >= 0xc000)
#define ROMCS      (m_romdis == 1 && offset < 0x8000)
#define RAMCS      (m_ramdis == 1 && offset >= 0x8000)


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class svi3x8_state : public driver_device
{
public:
	svi3x8_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ram(*this, RAM_TAG),
		m_io(*this, "io"),
		m_basic(*this, "basic"),
		m_vdp(*this, "vdp"),
		m_speaker(*this, "speaker"),
		m_cassette(*this, "cassette"),
		m_cart_rom(*this, "cartslot"),
		m_expander(*this, "exp"),
		m_keyboard(*this, "KEY.%u", 0),
		m_buttons(*this, "BUTTONS"),
		m_led_caps_lock(*this, "led_caps_lock"),
		m_intvdp(0), m_intexp(0),
		m_romdis(1), m_ramdis(1),
		m_cart(1), m_bk21(1),
		m_rom2(1), m_rom3(1),
		m_ctrl1(-1),
		m_keyboard_row(0)
	{}

	void svi318(machine_config &config);
	void svi328n(machine_config &config);
	void svi318p(machine_config &config);
	void svi318n(machine_config &config);
	void svi328p(machine_config &config);

private:
	uint8_t ppi_port_a_r();
	uint8_t ppi_port_b_r();
	void ppi_port_c_w(uint8_t data);
	void bank_w(uint8_t data);
	void intvdp_w(int state);

	uint8_t mreq_r(offs_t offset);
	void mreq_w(offs_t offset, uint8_t data);

	// from expander bus
	void intexp_w(int state);
	void romdis_w(int state);
	void ramdis_w(int state);
	void ctrl1_w(int state);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	void svi3x8_io(address_map &map) ATTR_COLD;
	void svi3x8_io_bank(address_map &map) ATTR_COLD;
	void svi3x8_mem(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<address_map_bank_device> m_io;
	required_memory_region m_basic;
	required_device<tms9928a_device> m_vdp;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cassette;
	required_device<generic_slot_device> m_cart_rom;
	required_device<svi_expander_device> m_expander;
	required_ioport_array<16> m_keyboard;
	required_ioport m_buttons;
	output_finder<> m_led_caps_lock;

	int m_intvdp;
	int m_intexp;
	int m_romdis;
	int m_ramdis;
	int m_cart;
	int m_bk21;
	int m_rom2;
	int m_rom3;
	int m_ctrl1;

	uint8_t m_keyboard_row;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void svi3x8_state::svi3x8_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(svi3x8_state::mreq_r), FUNC(svi3x8_state::mreq_w));
}

void svi3x8_state::svi3x8_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0xff).m(m_io, FUNC(address_map_bank_device::amap8));
}

void svi3x8_state::svi3x8_io_bank(address_map &map)
{
	map(0x000, 0x0ff).rw(m_expander, FUNC(svi_expander_device::iorq_r), FUNC(svi_expander_device::iorq_w));
	map(0x100, 0x17f).rw(m_expander, FUNC(svi_expander_device::iorq_r), FUNC(svi_expander_device::iorq_w));
	map(0x180, 0x181).mirror(0x22).w(m_vdp, FUNC(tms9928a_device::write));
	map(0x184, 0x185).mirror(0x22).r(m_vdp, FUNC(tms9928a_device::read));
	map(0x188, 0x188).mirror(0x23).w("psg", FUNC(ay8910_device::address_w));
	map(0x18c, 0x18c).mirror(0x23).w("psg", FUNC(ay8910_device::data_w));
	map(0x190, 0x190).mirror(0x23).r("psg", FUNC(ay8910_device::data_r));
	map(0x194, 0x197).w("ppi", FUNC(i8255_device::write));
	map(0x198, 0x19a).r("ppi", FUNC(i8255_device::read));
}


//**************************************************************************
//  INPUTS
//**************************************************************************

static INPUT_PORTS_START( svi318 )
	PORT_START("KEY.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)           PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)           PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)           PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)           PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)           PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)           PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)           PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)           PORT_CHAR('7') PORT_CHAR('&')

	PORT_START("KEY.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)           PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)           PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)       PORT_CHAR(':') PORT_CHAR(';')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)       PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)       PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)      PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)        PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)       PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("KEY.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)       PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)           PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)           PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)           PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)           PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)           PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)           PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)           PORT_CHAR('g') PORT_CHAR('G')

	PORT_START("KEY.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)           PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)           PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)           PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)           PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)           PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)           PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)           PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)           PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("KEY.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)           PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)           PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)           PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)           PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)           PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)           PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)           PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)           PORT_CHAR('w') PORT_CHAR('W')

	PORT_START("KEY.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)           PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)           PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)           PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)   PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)   PORT_CHAR('\\') PORT_CHAR('~')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)  PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)   PORT_CHAR(8)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)          PORT_CHAR(UCHAR_MAMEKEY(UP))   PORT_NAME(UTF8_UP)

	PORT_START("KEY.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)      PORT_CODE(KEYCODE_RSHIFT)      PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)    PORT_CHAR(UCHAR_SHIFT_2)       PORT_NAME("Ctrl")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT)        PORT_CHAR(UCHAR_MAMEKEY(PGUP)) PORT_NAME("Left Grph")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RALT)        PORT_CHAR(UCHAR_MAMEKEY(PGDN)) PORT_NAME("Right Grph")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)         PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_END)         PORT_CHAR(UCHAR_MAMEKEY(END))  PORT_NAME("Stop")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)       PORT_CHAR(13)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)        PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_NAME(UTF8_LEFT)

	PORT_START("KEY.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)          PORT_CHAR(UCHAR_MAMEKEY(F1))     PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)          PORT_CHAR(UCHAR_MAMEKEY(F2))     PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)          PORT_CHAR(UCHAR_MAMEKEY(F3))     PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)          PORT_CHAR(UCHAR_MAMEKEY(F4))     PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)          PORT_CHAR(UCHAR_MAMEKEY(F5))     PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME)        PORT_CHAR(UCHAR_MAMEKEY(HOME))   PORT_NAME("CLS/HM  Copy")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT)      PORT_CHAR(UCHAR_MAMEKEY(INSERT)) PORT_NAME("Ins  Paste")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)        PORT_CHAR(UCHAR_MAMEKEY(DOWN))   PORT_NAME(UTF8_DOWN)

	PORT_START("KEY.8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)       PORT_CHAR(' ')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)         PORT_CHAR('\t')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)         PORT_CHAR(UCHAR_MAMEKEY(DEL))      PORT_NAME("Del  Cut")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK)    PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_NAME("Caps Lock")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PAUSE)       PORT_CHAR(UCHAR_MAMEKEY(F11))      PORT_NAME("Select")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PRTSCR)      PORT_CHAR(UCHAR_MAMEKEY(PRTSCR))   PORT_NAME("Print")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)       PORT_CHAR(UCHAR_MAMEKEY(RIGHT))    PORT_NAME(UTF8_RIGHT)

	PORT_START("KEY.9")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.10")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.11")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.12")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.13")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.14")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY.15")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("JOY")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)    PORT_PLAYER(1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)  PORT_PLAYER(1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)  PORT_PLAYER(1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(1)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP)    PORT_PLAYER(2)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN)  PORT_PLAYER(2)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT)  PORT_PLAYER(2)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(2)

	PORT_START("BUTTONS")
	PORT_BIT(0x0f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_PLAYER(1)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_PLAYER(2)
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( svi328 )
	PORT_INCLUDE(svi318)

	PORT_MODIFY("KEY.9")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)      PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)      PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)      PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)      PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)      PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)      PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)      PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)      PORT_CHAR(UCHAR_MAMEKEY(7_PAD))

	PORT_MODIFY("KEY.10")
	PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)      PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)      PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD)   PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))
	PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD)  PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))
	PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK)   PORT_CHAR(UCHAR_MAMEKEY(ASTERISK))
	PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD)  PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD))
	PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD)  PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
	PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD)    PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD))
INPUT_PORTS_END


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

void svi3x8_state::intvdp_w(int state)
{
	m_intvdp = state;

	if (m_ctrl1 == 0)
		m_maincpu->set_input_line(INPUT_LINE_NMI, m_intvdp ? ASSERT_LINE : CLEAR_LINE);
	else
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, (m_intvdp || m_intexp) ? ASSERT_LINE : CLEAR_LINE);
}


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void svi3x8_state::machine_start()
{
	m_led_caps_lock.resolve();

	// register for save states
	save_item(NAME(m_intvdp));
	save_item(NAME(m_intexp));
	save_item(NAME(m_romdis));
	save_item(NAME(m_ramdis));
	save_item(NAME(m_cart));
	save_item(NAME(m_bk21));
	save_item(NAME(m_rom2));
	save_item(NAME(m_rom3));
	save_item(NAME(m_ctrl1));
	save_item(NAME(m_keyboard_row));
}

void svi3x8_state::machine_reset()
{
	m_intvdp = 0;
	m_intexp = 0;
	m_romdis = 1;
	m_ramdis = 1;
	m_cart = 1;
	m_bk21 = 1;
	m_rom2 = 1;
	m_rom3 = 1;
	m_keyboard_row = 0;

	if (m_ctrl1 == -1)
		ctrl1_w(1);
}

uint8_t svi3x8_state::mreq_r(offs_t offset)
{
	// ctrl1 inverts a15
	if (m_ctrl1 == 0)
		offset ^= 0x8000;

	if (CCS1 || CCS2 || CCS3 || CCS4)
		return m_cart_rom->read_rom(offset);

	uint8_t data = m_expander->mreq_r(offset);

	if (ROMCS)
		data = m_basic->as_u8(offset);

	if (m_bk21 == 0 && IS_SVI328 && offset < 0x8000)
		data = m_ram->read(offset);

	if (RAMCS && (IS_SVI328 || offset >= 0xc000))
		data = m_ram->read(IS_SVI328 ? offset : offset - 0xc000);

	return data;
}

void svi3x8_state::mreq_w(offs_t offset, uint8_t data)
{
	// ctrl1 inverts a15
	if (m_ctrl1 == 0)
		offset ^= 0x8000;

	if (CCS1 || CCS2 || CCS3 || CCS4)
		return;

	m_expander->mreq_w(offset, data);

	if (m_bk21 == 0 && IS_SVI328 && offset < 0x8000)
		m_ram->write(offset, data);

	if (RAMCS && (IS_SVI328 || offset >= 0xc000))
		m_ram->write(IS_SVI328 ? offset : offset - 0xc000, data);
}

void svi3x8_state::bank_w(uint8_t data)
{
	logerror("bank_w: %02x\n", data);

	m_cart = BIT(data, 0);
	m_bk21 = BIT(data, 1);

	m_expander->bk21_w(BIT(data, 1));
	m_expander->bk22_w(BIT(data, 2));
	m_expander->bk31_w(BIT(data, 3));
	m_expander->bk32_w(BIT(data, 4));

	m_rom2 = BIT(data, 6);
	m_rom3 = BIT(data, 7);

	m_led_caps_lock = BIT(data, 5);
}

uint8_t svi3x8_state::ppi_port_a_r()
{
	uint8_t data = 0x3f;

	// bit 0-3, paddle or tablet input

	// bit 4-5, joystick buttons
	data &= m_buttons->read();

	// bit 6-7, cassette
	data |= m_cassette->exists() ? 0x00 : 0x40;
	data |= (m_cassette->input() > 0.0038) ? 0x80 : 0x00;

	return data;
}

uint8_t svi3x8_state::ppi_port_b_r()
{
	// bit 0-7, keyboard data
	return m_keyboard[m_keyboard_row]->read();
}

void svi3x8_state::ppi_port_c_w(uint8_t data)
{
	// bit 0-3, keyboard row
	m_keyboard_row = data & 0x0f;

	// bit 4-6, cassette
	m_cassette->change_state(BIT(data, 4) ? CASSETTE_MOTOR_DISABLED : CASSETTE_MOTOR_ENABLED, CASSETTE_MASK_MOTOR);
	m_cassette->output(BIT(data, 5) ? -1.0 : +1.0);
	//m_cassette->change_state(BIT(data, 6) ? CASSETTE_SPEAKER_ENABLED : CASSETTE_SPEAKER_MUTED, CASSETTE_MASK_SPEAKER);

	// bit 7, mix psg sound (keyboard click)
	m_speaker->level_w(BIT(data, 7));
}

void svi3x8_state::intexp_w(int state)
{
	m_intexp = state;

	if (m_ctrl1 == 0)
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, m_intexp ? ASSERT_LINE : CLEAR_LINE);
	else
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, (m_intvdp || m_intexp) ? ASSERT_LINE : CLEAR_LINE);
}

void svi3x8_state::romdis_w(int state)
{
	m_romdis = state;
}

void svi3x8_state::ramdis_w(int state)
{
	m_ramdis = state;
}

void svi3x8_state::ctrl1_w(int state)
{
	m_ctrl1 = state;

	// ctrl1 disables internal io address decoding
	m_io->set_bank(m_ctrl1);
}


//**************************************************************************
//  CARTRIDGE
//**************************************************************************

DEVICE_IMAGE_LOAD_MEMBER( svi3x8_state::cart_load )
{
	uint32_t const size = m_cart_rom->common_get_size("rom");

	m_cart_rom->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart_rom->common_load_rom(m_cart_rom->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void svi3x8_state::svi318(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, XTAL(10'738'635) / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &svi3x8_state::svi3x8_mem);
	m_maincpu->set_addrmap(AS_IO, &svi3x8_state::svi3x8_io);

	RAM(config, m_ram).set_default_size("16K");

	ADDRESS_MAP_BANK(config, "io").set_map(&svi3x8_state::svi3x8_io_bank).set_data_width(8).set_addr_width(9).set_stride(0x100);

	i8255_device &ppi(I8255(config, "ppi"));
	ppi.in_pa_callback().set(FUNC(svi3x8_state::ppi_port_a_r));
	ppi.in_pb_callback().set(FUNC(svi3x8_state::ppi_port_b_r));
	ppi.out_pc_callback().set(FUNC(svi3x8_state::ppi_port_c_w));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);
	ay8910_device &psg(AY8910(config, "psg", XTAL(10'738'635) / 6));
	psg.port_a_read_callback().set_ioport("JOY");
	psg.port_b_write_callback().set(FUNC(svi3x8_state::bank_w));
	psg.add_route(ALL_OUTPUTS, "mono", 0.75);

	// cassette
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_formats(svi_cassette_formats);
	m_cassette->set_interface("svi318_cass");
	SOFTWARE_LIST(config, "cass_list").set_original("svi318_cass");

	// cartridge slot
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "svi318_cart", "bin,rom").set_device_load(FUNC(svi3x8_state::cart_load));
	SOFTWARE_LIST(config, "cart_list").set_original("svi318_cart");

	// expander bus
	SVI_EXPANDER(config, m_expander, svi_expander_modules);
	m_expander->int_handler().set(FUNC(svi3x8_state::intexp_w));
	m_expander->romdis_handler().set(FUNC(svi3x8_state::romdis_w));
	m_expander->ramdis_handler().set(FUNC(svi3x8_state::ramdis_w));
	m_expander->ctrl1_handler().set(FUNC(svi3x8_state::ctrl1_w));
	m_expander->excsr_handler().set(m_vdp, FUNC(tms9928a_device::read));
	m_expander->excsw_handler().set(m_vdp, FUNC(tms9928a_device::write));
	m_expander->add_route(ALL_OUTPUTS, "mono", 1.00);
}

void svi3x8_state::svi318p(machine_config &config)
{
	svi318(config);

	// video hardware
	TMS9929A(config, m_vdp, XTAL(10'738'635)).set_screen("screen");
	m_vdp->set_vram_size(0x4000);
	m_vdp->int_callback().set(FUNC(svi3x8_state::intvdp_w));
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);
}

void svi3x8_state::svi318n(machine_config &config)
{
	svi318(config);
	TMS9928A(config, m_vdp, XTAL(10'738'635)).set_screen("screen");
	m_vdp->set_vram_size(0x4000);
	m_vdp->int_callback().set(FUNC(svi3x8_state::intvdp_w));
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);
}

void svi3x8_state::svi328p(machine_config &config)
{
	svi318p(config);
	m_ram->set_default_size("64K");
}

void svi3x8_state::svi328n(machine_config &config)
{
	svi318n(config);
	m_ram->set_default_size("64K");
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

// note: should be split into two 16k chips, document differences
ROM_START( svi318 )
	ROM_REGION(0x8000, "basic", 0)
	ROM_SYSTEM_BIOS(0, "v111", "SV BASIC v1.11")
	ROMX_LOAD("svi111.rom", 0x0000, 0x8000, CRC(bc433df6) SHA1(10349ce675f6d6d47f0976e39cb7188eba858d89), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v11", "SV BASIC v1.1")
	ROMX_LOAD("svi110.rom", 0x0000, 0x8000, CRC(709904e9) SHA1(7d8daf52f78371ca2c9443e04827c8e1f98c8f2c), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v10", "SV BASIC v1.0")
	ROMX_LOAD("svi100.rom", 0x0000, 0x8000, CRC(98d48655) SHA1(07758272df475e5e06187aa3574241df1b14035b), ROM_BIOS(2))
ROM_END

#define rom_svi318n rom_svi318
#define rom_svi328  rom_svi318
#define rom_svi328n rom_svi318

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY         FULLNAME          FLAGS
COMP( 1983, svi318,  0,      0,      svi318p, svi318, svi3x8_state, empty_init, "Spectravideo", "SVI-318 (PAL)",  MACHINE_SUPPORTS_SAVE )
COMP( 1983, svi318n, svi318, 0,      svi318n, svi318, svi3x8_state, empty_init, "Spectravideo", "SVI-318 (NTSC)", MACHINE_SUPPORTS_SAVE )
COMP( 1983, svi328,  0,      0,      svi328p, svi328, svi3x8_state, empty_init, "Spectravideo", "SVI-328 (PAL)",  MACHINE_SUPPORTS_SAVE )
COMP( 1983, svi328n, svi328, 0,      svi328n, svi328, svi3x8_state, empty_init, "Spectravideo", "SVI-328 (NTSC)", MACHINE_SUPPORTS_SAVE )



svision.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Peter Trauner

/******************************************************************************
 watara supervision handheld

 PeT mess@utanet.at in december 2000
******************************************************************************/

#include "emu.h"

#include "svis_snd.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/m6502/w65c02.h"
#include "machine/timer.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "svision.lh"


// configurable logging
#define LOG_REGS     (1U << 1)

//#define VERBOSE (LOG_GENERAL | LOG_REGS)

#include "logmacro.h"

#define LOGREGS(...)     LOGMASKED(LOG_REGS,     __VA_ARGS__)


namespace {

class svision_state : public driver_device
{
public:
	svision_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_sound(*this, "custom")
		, m_cart(*this, "cartslot")
		, m_reg(*this, "reg")
		, m_videoram(*this, "videoram")
		, m_screen(*this, "screen")
		, m_joy(*this, "JOY")
		, m_palette(*this, "palette")
		, m_bank(*this, "bank%u", 1U)
	{ }

	void svisionp(machine_config &config);
	void svision(machine_config &config);
	void svisionn(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<svision_sound_device> m_sound;
	required_device<generic_slot_device> m_cart;
	required_shared_ptr<uint8_t> m_reg;
	required_shared_ptr<uint8_t> m_videoram;
	required_device<screen_device> m_screen;
	required_ioport m_joy;
	required_device<palette_device> m_palette;

	required_memory_bank_array<2> m_bank;

	memory_region *m_cart_rom = nullptr;

	enum
	{
		XSIZE = 0x00,
		XPOS  = 0x02,
		YPOS  = 0x03,
		BANK  = 0x26,
	};

	emu_timer *m_timer1 = nullptr;
	uint8_t m_timer_shot = 0;
	bool m_dma_finished = false;

	void sound_irq_w(int state);
	uint8_t regs_r(offs_t offset);
	void regs_w(offs_t offset, uint8_t data);

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void frame_int_w(int state);
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	void svision_palette(palette_device &palette) const;
	void svisionp_palette(palette_device &palette) const;
	void svisionn_palette(palette_device &palette) const;

	[[maybe_unused]] static constexpr uint32_t make8_rgb32(uint8_t red3, uint8_t green3, uint8_t blue2) { return (red3 << (16 + 5)) | (green3 << (8 + 5)) | (blue2 << (0 + 6)); }
	static constexpr uint32_t make9_rgb32(uint8_t red3, uint8_t green3, uint8_t blue3) { return (red3 << (16 + 5)) | (green3 << (8 + 5)) | (blue3 << (0 + 5)); }
	static constexpr uint32_t make12_rgb32(uint8_t red4, uint8_t green4, uint8_t blue4) { return (red4 << (16 + 4)) | (green4 << (8 + 4)) | (blue4 << (0 + 4)); }
	static constexpr uint32_t make24_rgb32(uint8_t red8, uint8_t green8, uint8_t blue8) { return ((red8 & 0xf8) << 16) | ((green8 & 0xf8) << 8) | (blue8 & 0xf8); }

	void check_irq();

	TIMER_CALLBACK_MEMBER(timer);

	void program_map(address_map &map) ATTR_COLD;

	void svision_base(machine_config &config);
};

class svisions_state : public svision_state
{
public:
	svisions_state(const machine_config &mconfig, device_type type, const char *tag)
		: svision_state(mconfig, type, tag)
		, m_joy2(*this, "JOY2")
	{ }

	void svisions(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_ioport m_joy2;

	uint8_t m_state = 0;
	uint8_t m_clock = 0, m_data = 0;
	uint8_t m_input = 0;
	emu_timer *m_timer = nullptr;

	uint8_t regs_r(offs_t offset);

	TIMER_CALLBACK_MEMBER(pet_timer);
	TIMER_DEVICE_CALLBACK_MEMBER(pet_timer_dev);

	void program_map(address_map &map) ATTR_COLD;
};

class tvlink_state : public svision_state
{
public:
	tvlink_state(const machine_config &mconfig, device_type type, const char *tag)
		: svision_state(mconfig, type, tag)
	{ }

	void tvlinkp(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	uint32_t m_tvlink_palette[4]{}; // 0x40? rgb8
	uint8_t m_palette_on = 0;

	uint8_t regs_r(offs_t offset);
	void regs_w(offs_t offset, uint8_t data);

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void program_map(address_map &map) ATTR_COLD;
};

TIMER_CALLBACK_MEMBER(svisions_state::pet_timer)
{
	switch (m_state)
	{
		case 0x00:
			m_input = m_joy2->read();
			[[fallthrough]];

		case 0x02: case 0x04: case 0x06: case 0x08:
		case 0x0a: case 0x0c: case 0x0e:
			m_clock = m_state & 2;
			m_data = m_input & 1;
			m_input >>= 1;
			m_state++;
			break;

		case 0x1f:
			m_state = 0;
			break;

		default:
			m_state++;
			break;
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(svisions_state::pet_timer_dev)
{
	pet_timer(param);
}

void svision_state::sound_irq_w(int state)
{
	m_dma_finished = true;
	check_irq();
}

void svision_state::check_irq()
{
	bool irq = m_timer_shot && BIT(m_reg[BANK], 1);
	irq = irq || (m_dma_finished && BIT(m_reg[BANK], 2));

	m_maincpu->set_input_line(W65C02_IRQ_LINE, irq ? ASSERT_LINE : CLEAR_LINE);
}

TIMER_CALLBACK_MEMBER(svision_state::timer)
{
	m_timer_shot = true;
	m_timer1->enable(false);
	check_irq();
}

uint8_t svision_state::regs_r(offs_t offset)
{
	int data = m_reg[offset];
	switch (offset)
	{
		case 0x20:
			return m_joy->read();

		case 0x21:
			data &= ~0xf;
			data |= m_reg[0x22] & 0xf;
			break;

		case 0x27:
			data &= ~3;
			if (m_timer_shot)
			{
				data |= 1;
			}
			if (m_dma_finished)
			{
				data |= 2;
			}
			break;

		case 0x24:
			m_timer_shot = false;
			check_irq();
			break;

		case 0x25:
			m_dma_finished = false;
			check_irq();
			break;

		default:
			LOGREGS("%.6f svision read %04x %02x\n", machine().time().as_double(), offset, data);
			break;
	}

	return data;
}

uint8_t svisions_state::regs_r(offs_t offset)
{
	int data = m_reg[offset];
	switch (offset)
	{
		case 0x20:
			return m_joy->read();

		case 0x21:
			data &= ~0xf;
			data |= m_reg[0x22] & 0xf;
			if (!m_clock)
			{
				data &= ~4;
			}
			if (!m_data)
			{
				data &= ~8;
			}
			break;

		case 0x27:
			data &= ~3;
			if (m_timer_shot)
			{
				data |= 1;
			}
			if (m_dma_finished)
			{
				data |= 2;
			}
			break;

		case 0x24:
			m_timer_shot = false;
			check_irq();
			break;

		case 0x25:
			m_dma_finished = false;
			check_irq();
			break;

		default:
			LOGREGS("%.6f svision read %04x %02x\n", machine().time().as_double(), offset, data);
			break;
	}

	return data;
}

void svision_state::regs_w(offs_t offset, uint8_t data)
{
	m_reg[offset] = data;

	switch (offset)
	{
		case 0x02:
		case 0x03:
			break;

		case 0x26: // bits 5,6 memory management for a000?
		{
			LOGREGS("%.6f svision write %04x %02x\n", machine().time().as_double(), offset, data);
			const int bank = ((m_reg[0x26] & 0xe0) >> 5) % (m_cart_rom->bytes() / 0x4000);
			m_bank[0]->set_entry(bank);
			check_irq();
			break;
		}

		case 0x23: // delta hero irq routine write
		{
			int delay = (data == 0) ? 0x100 : data;
			delay *= (BIT(m_reg[BANK], 4)) ? 0x4000 : 0x100;
			m_timer1->enable(true);
			m_timer1->reset(m_maincpu->cycles_to_attotime(delay));
			break;
		}

		case 0x10: case 0x11: case 0x12: case 0x13:
			m_sound->soundport_w(0, offset & 3, data);
			break;

		case 0x14: case 0x15: case 0x16: case 0x17:
			m_sound->soundport_w(1, offset & 3, data);
			break;

		case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c:
			m_sound->sounddma_w(offset - 0x18, data);
			break;

		case 0x28: case 0x29: case 0x2a:
			m_sound->noise_w(offset - 0x28, data);
			break;

		default:
			LOGREGS("%.6f svision write %04x %02x\n", machine().time().as_double(), offset, data);
			break;
	}
}

uint8_t tvlink_state::regs_r(offs_t offset)
{
	switch(offset)
	{
		default:
			if (offset >= 0x800 && offset < 0x840)
			{
				// strange effects when modifying palette
				return svision_state::regs_r(offset);
			}
			else
			{
				return svision_state::regs_r(offset);
			}
	}
}

void tvlink_state::regs_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
		case 0x0e:
			m_reg[offset] = data;
			m_palette_on = data & 1;
			if (m_palette_on)
			{
				// hack, normally initialising with palette from RAM
				m_tvlink_palette[0] = make12_rgb32(163 / 16, 172 / 16, 115 / 16); // these are the tron colors measured from screenshot
				m_tvlink_palette[1] = make12_rgb32(163 / 16, 155 / 16, 153 / 16);
				m_tvlink_palette[2] = make12_rgb32(77 / 16, 125 / 16, 73 / 16);
				m_tvlink_palette[3] = make12_rgb32(59 / 16, 24 / 16, 20 / 16);
			}
			else
			{
				// cleaner to use colors from compile time palette, or compose from "fixed" palette values
				m_tvlink_palette[0] = make12_rgb32(0, 0, 0);
				m_tvlink_palette[1] = make12_rgb32(5 * 16 / 256, 18 * 16 / 256, 9 * 16 / 256);
				m_tvlink_palette[2] = make12_rgb32(48 * 16 / 256, 76 * 16 / 256, 100 * 16 / 256);
				m_tvlink_palette[3] = make12_rgb32(190 * 16 / 256, 190 * 16 / 256, 190 * 16 / 256);
			}
			break;
		default:
			svision_state::regs_w(offset, data);
			if (offset >= 0x800 && offset < 0x840)
			{
				if (offset == 0x803 && data == 0x07)
				{
					// tron hack
					m_reg[0x0804] = 0x00;
					m_reg[0x0805] = 0x01;
					m_reg[0x0806] = 0x00;
					m_reg[0x0807] = 0x00;
				}
				uint16_t c = m_reg[0x800] | (m_reg[0x804] << 8);
				m_tvlink_palette[0] = make9_rgb32((c >> 0) & 7, (c >> 3) & 7, (c >> 6) & 7);
				c = m_reg[0x801] | (m_reg[0x805] << 8);
				m_tvlink_palette[1] = make9_rgb32((c >> 0) & 7, (c >> 3) & 7, (c >> 6) & 7);
				c = m_reg[0x802] | (m_reg[0x806]<<8);
				m_tvlink_palette[2] = make9_rgb32((c >> 0) & 7, (c >> 3) & 7, (c >> 6) & 7);
				c = m_reg[0x803] | (m_reg[0x807]<<8);
				m_tvlink_palette[3] = make9_rgb32((c >> 0) & 7, (c >> 3) & 7, (c >> 6) & 7);
				/* writes to palette effect video color immediately
				   some writes modify other registers,
				   encoding therefor not known (rgb8 or rgb9) */
			}
	}
}

void svision_state::program_map(address_map &map)
{
	map(0x0000, 0x1fff).ram();
	map(0x2000, 0x3fff).rw(FUNC(svision_state::regs_r), FUNC(svision_state::regs_w)).share(m_reg);
	map(0x4000, 0x5fff).ram().share(m_videoram);
	map(0x6000, 0x7fff).noprw();
	map(0x8000, 0xbfff).bankr(m_bank[0]);
	map(0xc000, 0xffff).bankr(m_bank[1]);
}

void svisions_state::program_map(address_map &map)
{
	svision_state::program_map(map);

	map(0x2000, 0x3fff).rw(FUNC(svisions_state::regs_r), FUNC(svisions_state::regs_w)).share(m_reg);
}

void tvlink_state::program_map(address_map &map)
{
	svision_state::program_map(map);

	map(0x2000, 0x3fff).rw(FUNC(tvlink_state::regs_r), FUNC(tvlink_state::regs_w)).share(m_reg);
}

static INPUT_PORTS_START( svision )
	PORT_START("JOY")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP   )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("B")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("A")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SELECT) PORT_NAME("Select")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START) PORT_NAME("Start/Pause")
INPUT_PORTS_END

static INPUT_PORTS_START( svisions )
	PORT_START("JOY")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP   ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("B") PORT_PLAYER(1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("A") PORT_PLAYER(1)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SELECT) PORT_NAME("Select") PORT_PLAYER(1)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START) PORT_NAME("Start/Pause") PORT_PLAYER(1)

	PORT_START("JOY2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP   ) PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("2nd B") PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("2nd A") PORT_PLAYER(2)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SELECT) PORT_NAME("2nd Select") PORT_PLAYER(2)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START) PORT_NAME("2nd Start/Pause") PORT_PLAYER(2)
INPUT_PORTS_END

// most games contain their graphics in ROMs, and have hardware to draw complete rectangular objects

// palette in red, green, blue triples
static constexpr rgb_t svision_pens[] =
{
#if 0
	// greens grabbed from a scan of a handheld in its best adjustment for contrast
	{ 86, 121,  86 },
	{ 81, 115,  90 },
	{ 74, 107, 101 },
	{ 54,  78,  85 }
#else
	// grabbed from Chris Covell's black & white pics
	{ 0xe0, 0xe0, 0xe0 },
	{ 0xb9, 0xb9, 0xb9 },
	{ 0x54, 0x54, 0x54 },
	{ 0x12, 0x12, 0x12 }
#endif
};

// palette in RGB triplets
static constexpr rgb_t svisionp_pens[] =
{
	// pal
	{   1,   1,   3 },
	{   5,  18,   9 },
	{  48,  76, 100 },
	{ 190, 190, 190 }
};

// palette in RGB triplets
static constexpr rgb_t svisionn_pens[] =
{
	{   0,   0,   0 },
	{ 188, 242, 244 }, // darker
	{ 129, 204, 255 },
	{ 245, 249, 248 }
};

void svision_state::svision_palette(palette_device &palette) const
{
	palette.set_pen_colors(0, svision_pens);
}
void svision_state::svisionn_palette(palette_device &palette) const
{
	palette.set_pen_colors(0, svisionn_pens);
}
void svision_state::svisionp_palette(palette_device &palette) const
{
	palette.set_pen_colors(0, svisionp_pens);
}

uint32_t svision_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	if (BIT(m_reg[BANK], 3))
	{
		int j = m_reg[XPOS] / 4 + m_reg[YPOS] * 0x30;
		for (int y = 0; y < 160; y++)
		{
			const int start_x = 3 - (m_reg[XPOS] & 3);
			const int end_x = std::min(163, m_reg[XSIZE] | 3);
			uint16_t *line = &bitmap.pix(y, start_x);
			for (int x = start_x, i = 0; x < end_x; x += 4, i++)
			{
				uint8_t b = m_videoram[j + i];
				for (int pix = 0; pix < 4; pix++)
				{
					*line = b & 3;
					b >>= 2;
					line++;
				}
			}
			j += 0x30;
			if (j >= 0x1fe0)
				j = 0; //sssnake
		}
	}
	else
	{
		bitmap.plot_box(3, 0, 162, 159, 0);
	}
	return 0;
}

uint32_t tvlink_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if (BIT(m_reg[BANK], 3))
	{
		int j = m_reg[XPOS] / 4 + m_reg[YPOS] * 0x30;
		for (int y = 0; y < 160; y++)
		{
			const int start_x = 3 - (m_reg[XPOS] & 3);
			const int end_x = std::min(163, m_reg[XSIZE] | 3);
			uint32_t *line = &bitmap.pix(y, start_x);
			for (int x = start_x, i = 0; x < end_x; x += 4, i++)
			{
				uint8_t b = m_videoram[j + i];
				for (int pix = 0; pix < 4; pix++)
				{
					*line = m_tvlink_palette[b & 3];
					b >>= 2;
					line++;
				}
			}
			j += 0x30;
			if (j >= 0x1fe0)
				j = 0; //sssnake
		}
	}
	else
	{
		bitmap.plot_box(3, 0, 162, 159, m_palette->pen(0));
	}
	return 0;
}

void svision_state::frame_int_w(int state)
{
	if (!state)
		return;

	if (BIT(m_reg[BANK], 0))
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);

	m_sound->sound_decrement();
}

DEVICE_IMAGE_LOAD_MEMBER(svision_state::cart_load)
{
	uint32_t size = m_cart->common_get_size("rom");

	if (size > 0x80000)
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported cartridge size (must be no larger than 512K)");

	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

void svision_state::machine_start()
{
	m_timer1 = timer_alloc(FUNC(svision_state::timer), this);
	m_dma_finished = false;

	std::string region_tag(m_cart->tag());
	region_tag.append(GENERIC_ROM_REGION_TAG);
	m_cart_rom = memregion(region_tag.c_str());

	if (m_cart_rom)
	{
		const int num_banks = m_cart_rom->bytes() / 0x4000;
		m_bank[0]->configure_entries(0, num_banks, m_cart_rom->base(), 0x4000);
		m_bank[1]->set_base(m_cart_rom->base() + (num_banks - 1) * 0x4000); // bank2 is set to the last bank
	}

	save_item(NAME(m_timer_shot));
	save_item(NAME(m_dma_finished));
}

void svisions_state::machine_start()
{
	svision_state::machine_start();

	m_timer = timer_alloc(FUNC(svisions_state::pet_timer), this);

	save_item(NAME(m_state));
	save_item(NAME(m_clock));
	save_item(NAME(m_data));
	save_item(NAME(m_input));
}

void tvlink_state::machine_start()
{
	svision_state::machine_start();

	save_item(NAME(m_tvlink_palette));
	save_item(NAME(m_palette_on));
}

void svision_state::machine_reset()
{
	m_timer_shot = false;
	m_dma_finished = false;
}


void tvlink_state::machine_reset()
{
	svision_state::machine_reset();

	m_palette_on = false;

	memset(m_reg + 0x800, 0xff, 0x40); // normally done from m_tvlink microcontroller
	m_reg[0x82a] = 0xdf;

	m_tvlink_palette[0] = make24_rgb32(svisionp_pens[0].r(), svisionp_pens[0].g(), svisionp_pens[0].b());
	m_tvlink_palette[1] = make24_rgb32(svisionp_pens[1].r(), svisionp_pens[1].g(), svisionp_pens[1].b());
	m_tvlink_palette[2] = make24_rgb32(svisionp_pens[2].r(), svisionp_pens[2].g(), svisionp_pens[2].b());
	m_tvlink_palette[3] = make24_rgb32(svisionp_pens[3].r(), svisionp_pens[3].g(), svisionp_pens[3].b());
}

void svision_state::svision_base(machine_config &config)
{
	config.set_default_layout(layout_svision);

	SPEAKER(config, "speaker", 2).front();

	SVISION_SND(config, m_sound, 4'000'000, m_maincpu, m_bank[0]);
	m_sound->add_route(0, "speaker", 0.50, 0);
	m_sound->add_route(1, "speaker", 0.50, 1);
	m_sound->irq_cb().set(FUNC(svision_state::sound_irq_w));

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "svision_cart", "bin,ws,sv");
	m_cart->set_must_be_loaded(true);
	m_cart->set_device_load(FUNC(svision_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("svision");
}

void svision_state::svision(machine_config &config)
{
	svision_base(config);

	W65C02(config, m_maincpu, 4'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &svision_state::program_map);

	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(61);
	m_screen->set_size(3+160+3, 160);
	m_screen->set_visarea(3+0, 3+160-1, 0, 160-1);
	m_screen->set_screen_update(FUNC(svision_state::screen_update));
	m_screen->set_palette(m_palette);
	m_screen->screen_vblank().set(FUNC(svision_state::frame_int_w));

	PALETTE(config, m_palette, FUNC(svision_state::svision_palette), std::size(svision_pens));
}

void svisions_state::svisions(machine_config &config)
{
	svision(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &svisions_state::program_map);

	TIMER(config, "pet_timer").configure_periodic(FUNC(svisions_state::pet_timer_dev), attotime::from_seconds(8));
}

void svision_state::svisionp(machine_config &config)
{
	svision(config);

	m_maincpu->set_clock(4'430'000);

	m_screen->set_refresh(HZ_TO_ATTOSECONDS(50));

	m_palette->set_init(FUNC(svision_state::svisionp_palette));
}

void svision_state::svisionn(machine_config &config)
{
	svision(config);

	m_maincpu->set_clock(3'560'000); // ?

	m_screen->set_refresh(HZ_TO_ATTOSECONDS(60));

	m_palette->set_init(FUNC(svision_state::svisionn_palette));
}

void tvlink_state::tvlinkp(machine_config &config)
{
	svisionp(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &tvlink_state::program_map);

	m_screen->set_no_palette();
	m_screen->set_screen_update(FUNC(tvlink_state::screen_update));
}

ROM_START(svision)
	ROM_REGION(0x80000, "maincpu", ROMREGION_ERASE00)
ROM_END

ROM_START(tvlinkp)
	ROM_REGION(0x80000, "maincpu", ROMREGION_ERASE00)

	ROM_REGION(0x10000, "bezel", 0)
	ROM_LOAD( "9307md_512d.glob", 0x00000, 0x10000, CRC(bc8b981b) SHA1(3328da4fd9462286e8cefe4372ffd17c8f5a229e) )
ROM_END

} // anonymous namespace


/***************************************************************************

  Game driver(s)

***************************************************************************/

#define rom_svisions rom_svision
#define rom_svisionn rom_svision
#define rom_svisionp rom_svision

//    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT     STATE           INIT           COMPANY   FULLNAME                                       FLAGS
// marketed under a ton of firms and names
CONS(1992,  svision,  0,       0,      svision,  svision,  svision_state,  empty_init,    "Watara", "Super Vision",                                MACHINE_SUPPORTS_SAVE )
// svdual 2 connected via communication port
CONS( 1992, svisions, svision, 0,      svisions, svisions, svisions_state, empty_init,    "Watara", "Super Vision (PeT Communication Simulation)", MACHINE_SUPPORTS_SAVE )

CONS( 1993, svisionp, svision, 0,      svisionp, svision,  svision_state,  empty_init,    "Watara", "Super Vision (PAL TV Link Colored)",          MACHINE_SUPPORTS_SAVE )
CONS( 1993, svisionn, svision, 0,      svisionn, svision,  svision_state,  empty_init,    "Watara", "Super Vision (NTSC TV Link Colored)",         MACHINE_SUPPORTS_SAVE )
// svtvlink (2 supervisions)
// tvlink (pad supervision simulated)
CONS( 199?, tvlinkp,  svision, 0,      tvlinkp,  svision,  tvlink_state,   empty_init,    "Watara", "TV Link PAL",                                 MACHINE_SUPPORTS_SAVE )



svmu.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

        Sega Visual Memory Unit

        driver by Sandro Ronco

        TODO:
        - add more bios versions
        - serial

****************************************************************************/

#include "emu.h"
#include "cpu/lc8670/lc8670.h"
#include "imagedev/snapquik.h"
#include "machine/intelfsh.h"
#include "sound/spkrdev.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "svmu.lh"


namespace {

#define     PIXEL_SIZE          7
#define     PIXEL_DISTANCE      1

class svmu_state : public driver_device
{
public:
	svmu_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_flash(*this, "flash")
		, m_speaker(*this, "speaker")
		, m_bios(*this, "bios")
		, m_battery(*this, "BATTERY")
		, m_file_icon(*this, "file_icon")
		, m_game_icon(*this, "game_icon")
		, m_clock_icon(*this, "clock_icon")
		, m_flash_icon(*this, "flash_icon")
	{ }

	void svmu(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	LC8670_LCD_UPDATE(svmu_lcd_update);
	void svmu_palette(palette_device &palette) const;
	void page_w(uint8_t data);
	uint8_t prog_r(offs_t offset);
	void prog_w(offs_t offset, uint8_t data);
	uint8_t p1_r();
	void p1_w(uint8_t data);
	uint8_t p7_r();
	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);

	void svmu_io_mem(address_map &map) ATTR_COLD;
	void svmu_mem(address_map &map) ATTR_COLD;

	required_device<lc8670_cpu_device> m_maincpu;
	required_device<intelfsh8_device> m_flash;
	required_device<speaker_sound_device> m_speaker;
	required_region_ptr<uint8_t> m_bios;
	required_ioport m_battery;
	output_finder<> m_file_icon;
	output_finder<> m_game_icon;
	output_finder<> m_clock_icon;
	output_finder<> m_flash_icon;

	uint8_t       m_page;
};


void svmu_state::page_w(uint8_t data)
{
	m_page = data & 0x03;
}

uint8_t svmu_state::prog_r(offs_t offset)
{
	if (m_page == 1)
		return m_flash->read(offset);
	else if (m_page == 2)
		return m_flash->read(0x10000 + offset);
	else
		return m_bios[offset];
}

void svmu_state::prog_w(offs_t offset, uint8_t data)
{
	if (m_page == 1)
		m_flash->write(offset, data);
	else if (m_page == 2)
		m_flash->write(0x10000 + offset, data);
}

/*
    Port 1

    x--- ----   PWM output
    -x-- ----   BUZ
    --x- ----   SCK1
    ---x ----   SB1
    ---- x---   SO1
    ---- -x--   SCK0
    ---- --x-   SB0
    ---- ---x   SO0

*/

uint8_t svmu_state::p1_r()
{
	return 0;
}

void svmu_state::p1_w(uint8_t data)
{
	m_speaker->level_w(BIT(data, 7));
}


/*
    Port 7

    ---- x---   ID1
    ---- -x--   ID0
    ---- --x-   battery low voltage
    ---- ---x   5V detection
*/

uint8_t svmu_state::p7_r()
{
	return (m_battery->read()<<1);
}


void svmu_state::svmu_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(svmu_state::prog_r), FUNC(svmu_state::prog_w));
}

void svmu_state::svmu_io_mem(address_map &map)
{
	map(LC8670_PORT1, LC8670_PORT1).rw(FUNC(svmu_state::p1_r), FUNC(svmu_state::p1_w));
	map(LC8670_PORT3, LC8670_PORT3).portr("P3");
	map(LC8670_PORT7, LC8670_PORT7).r(FUNC(svmu_state::p7_r));
}

/* Input ports */
static INPUT_PORTS_START( svmu )
	PORT_START( "P3" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP) PORT_NAME("Up")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN) PORT_NAME("Down")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT) PORT_NAME("Left")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_NAME("Right")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("Button A")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("Button B")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("MODE")   PORT_CODE( KEYCODE_M )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("SLEEP")  PORT_CODE( KEYCODE_S )
	PORT_START("BATTERY")
	PORT_CONFNAME( 0x01, 0x01, "Battery" )
	PORT_CONFSETTING( 0x01, "Good" )
	PORT_CONFSETTING( 0x00, "Poor" )
INPUT_PORTS_END

void svmu_state::machine_start()
{
	m_file_icon.resolve();
	m_game_icon.resolve();
	m_clock_icon.resolve();
	m_flash_icon.resolve();
}

void svmu_state::machine_reset()
{
	m_page = 0;
}

void svmu_state::svmu_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}

LC8670_LCD_UPDATE(svmu_state::svmu_lcd_update)
{
	if (lcd_enabled)
	{
		for (int y=0; y<32; y++)
			for (int x=0; x<6; x++)
			{
				int gfx = vram[y*6 + x];

				for (int b = 0; b < 8; b++)
					bitmap.plot_box((x*8 + b) * (PIXEL_SIZE + PIXEL_DISTANCE), y * (PIXEL_SIZE + PIXEL_DISTANCE), PIXEL_SIZE, PIXEL_SIZE, BIT(gfx,7-b));
			}
	}
	else
	{
		bitmap.fill(0, cliprect);
	}

	m_file_icon  = lcd_enabled ? BIT(vram[0xc1],6) : 0;
	m_game_icon  = lcd_enabled ? BIT(vram[0xc2],4) : 0;
	m_clock_icon = lcd_enabled ? BIT(vram[0xc3],2) : 0;
	m_flash_icon = lcd_enabled ? BIT(vram[0xc4],0) : 0;

	return 0;
}


inline void vmufat_write_byte(uint8_t* flash, uint8_t block, offs_t offset, uint8_t data)
{
	flash[(block * 512) + offset] = data;
}

inline void vmufat_write_word(uint8_t* flash, uint8_t block, offs_t offset, uint16_t data)
{
	// 16-bit data are stored in little endian
	flash[(block * 512) + offset + 0] = data & 0xff;
	flash[(block * 512) + offset + 1] = (data>>8) & 0xff;
}

QUICKLOAD_LOAD_MEMBER(svmu_state::quickload_cb)
{
	uint32_t size = image.length();
	uint8_t *flash = m_flash->base();

	image.fread(flash, size);

	// verify if image is already a valid VMUFAT file system
	bool valid_vmufat = true;
	if (size == 0x20000)
	{
		for (int i=0; i<0x10; i++)
			if (flash[255 * 512 + i] != 0x55)
			{
				valid_vmufat = false;
				break;
			}
	}
	else
	{
		valid_vmufat = false;
	}

	if (!valid_vmufat)
	{
		// more info about the VMUFAT here: http://mc.pp.se/dc/vms/flashmem.html

		//-------------------------------- Formatting --------------------------------
		memset(flash + 241*512, 0, 15*512);                 // clears the last 15 blocks that contain file system information

		for (int i=0; i<0x10; i++)
			vmufat_write_byte(flash, 255, i, 0x55);         // first 16 bytes should be 0x55 to indicate a properly formatted card

		vmufat_write_byte(flash, 255, 0x10, 0x00);          // custom VMS colour (1 = use custom colours, 0 = standard colour)
		vmufat_write_byte(flash, 255, 0x11, 0x00);          // VMS colour blue component
		vmufat_write_byte(flash, 255, 0x12, 0x00);          // VMS colour green component
		vmufat_write_byte(flash, 255, 0x13, 0x00);          // VMS colour red component
		vmufat_write_byte(flash, 255, 0x14, 0x00);          // VMS colour alpha component
		vmufat_write_byte(flash, 255, 0x30, 0x19);          // Century (BCD)
		vmufat_write_byte(flash, 255, 0x31, 0x99);          // Year (BCD)
		vmufat_write_byte(flash, 255, 0x32, 0x01);          // Month (BCD)
		vmufat_write_byte(flash, 255, 0x33, 0x01);          // Day (BCD)
		vmufat_write_byte(flash, 255, 0x34, 0x00);          // Hour (BCD)
		vmufat_write_byte(flash, 255, 0x35, 0x00);          // Minute (BCD)
		vmufat_write_byte(flash, 255, 0x36, 0x00);          // Second (BCD)
		vmufat_write_byte(flash, 255, 0x37, 0x00);          // Day of week (0 = Monday, 6 = Sunday)
		vmufat_write_word(flash, 255, 0x44, 0x00ff);        // location of Root
		vmufat_write_word(flash, 255, 0x46, 0x00fe);        // location of FAT (254)
		vmufat_write_word(flash, 255, 0x48, 0x0001);        // size of FAT in blocks (1)
		vmufat_write_word(flash, 255, 0x4a, 0x00fd);        // location of Directory (253)
		vmufat_write_word(flash, 255, 0x4c, 0x000d);        // size of Directory in blocks (13)
		vmufat_write_word(flash, 255, 0x4e, 0x0000);        // icon shape for this VMS (0-123)
		vmufat_write_word(flash, 255, 0x50, 0x00c8);        // number of user blocks (200)

		for (int i=0; i<256; i++)
			vmufat_write_word(flash, 254, i<<1, 0xfffc);    // marks all blocks as unallocated

		for (int i=253; i>241; --i)
			vmufat_write_word(flash, 254, i<<1, i - 1);     // marsk all Directory blocks as allocate

		vmufat_write_word(flash, 254, 0x1e2, 0xfffa);       // marks last Directory block
		vmufat_write_word(flash, 254, 0x1fc, 0xfffa);       // marks FAT block as allocated
		vmufat_write_word(flash, 254, 0x1fe, 0xfffa);       // marks Root block as allocated

		//-------------------------------- Create the vms file --------------------------------
		int vms_blocks = (size / 512) + (size & 0x1ff ? 1 : 0); // number of blocks required for store the vms file

		for (int i=0; i<vms_blocks - 1; i++)
			vmufat_write_word(flash, 254, i<<1, i + 1);     // marks blocks where the file is allocated

		vmufat_write_word(flash, 254, (vms_blocks-1)<<1, 0xfffa);   // last block for this file

		vmufat_write_byte(flash, 253, 0x00, 0xcc);          // file type (0x00 = no file, 0x33 = data, 0xcc = game)
		vmufat_write_byte(flash, 253, 0x01, 0x00);          // copy protect (0x00 = no, 0xff = yes)
		vmufat_write_word(flash, 253, 0x02, 0x0000);        // location of first file block

		const char *vms_filename = image.basename_noext();
		for (int i=0; i<12; i++)
		{
			if (i < strlen(vms_filename))
				vmufat_write_byte(flash, 253, i + 4, vms_filename[i]);  // 12 bytes filename
			else
				vmufat_write_byte(flash, 253, i + 4, 0x20);             // space padded
		}

		vmufat_write_byte(flash, 253, 0x10, 0x19);          // Century (BCD)
		vmufat_write_byte(flash, 253, 0x11, 0x99);          // Year (BCD)
		vmufat_write_byte(flash, 253, 0x12, 0x01);          // Month (BCD)
		vmufat_write_byte(flash, 253, 0x13, 0x01);          // Day (BCD)
		vmufat_write_byte(flash, 253, 0x14, 0x00);          // Hour (BCD)
		vmufat_write_byte(flash, 253, 0x15, 0x00);          // Minute (BCD)
		vmufat_write_byte(flash, 253, 0x16, 0x00);          // Second (BCD)
		vmufat_write_byte(flash, 253, 0x17, 0x00);          // Day of week (0 = Monday, 6 = Sunday)
		vmufat_write_word(flash, 253, 0x18, vms_blocks);    // file size (in blocks)
		vmufat_write_word(flash, 253, 0x1a, 0x0001);        // offset of header (in blocks) from file start
	}

	return std::make_pair(std::error_condition(), std::string());
}


void svmu_state::svmu(machine_config &config)
{
	/* basic machine hardware */
	LC8670(config, m_maincpu, XTAL(32'768));
	m_maincpu->set_addrmap(AS_PROGRAM, &svmu_state::svmu_mem);
	m_maincpu->set_addrmap(AS_IO, &svmu_state::svmu_io_mem);

	/* specific LC8670 configurations */
	m_maincpu->set_clock_sources(XTAL(32'768), 600000, XTAL(6'000'000)); // tolerance range of the RC oscillator is 600kHz to 1200kHz
	m_maincpu->bank_cb().set(FUNC(svmu_state::page_w));
	m_maincpu->set_lcd_update_cb(FUNC(svmu_state::svmu_lcd_update));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // not accurate
	screen.set_size(48 * (PIXEL_SIZE + PIXEL_DISTANCE), 32 * (PIXEL_SIZE + PIXEL_DISTANCE));
	screen.set_visarea(0, 48*(PIXEL_SIZE + PIXEL_DISTANCE) - 1, 0, 32*(PIXEL_SIZE + PIXEL_DISTANCE) - 1);
	screen.set_screen_update("maincpu", FUNC(lc8670_cpu_device::screen_update));
	screen.set_palette("palette");

	config.set_default_layout(layout_svmu);
	PALETTE(config, "palette", FUNC(svmu_state::svmu_palette), 2);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* devices */
	ATMEL_29C010(config, m_flash);

	quickload_image_device &quickload(QUICKLOAD(config, "quickload", "vms,bin"));
	quickload.set_load_callback(FUNC(svmu_state::quickload_cb));
	quickload.set_interface("svmu_quik");

	/* Software lists */
	SOFTWARE_LIST(config, "quik_list").set_original("svmu");
}


/* ROM definition */
ROM_START( svmu )
	// some ROMs come from the Sega Katana SDK and are scrambled, a simple way to restore them is to remove the first 4 bytes and xor the whole file for the xor key.

	ROM_REGION(0x10000, "bios", 0)
	ROM_DEFAULT_BIOS("en1005b")

	// Version 1.005,1999/04/28,315-6124-07,SEGA Visual Memory System BIOS Produced by Sue
	ROM_SYSTEM_BIOS(0, "en1005a", "VMS English BIOS (1.005 1999/04/28)")
	ROMX_LOAD("en1005-19990428-315-6124-07.bin", 0x0000, 0x10000, CRC(dfd77f4e) SHA1(4a7bfd1b8eb599d87883312df0bb48e0edd13034), ROM_BIOS(0)) // extracted with trojan

	// Version 1.005,1999/10/26,315-6208-05,SEGA Visual Memory System BIOS Produced by Sue
	ROM_SYSTEM_BIOS(1, "en1005b", "VMS English BIOS (1.005 1999/10/26)")
	ROMX_LOAD("en1005-19991026-315-6208-05.bin", 0x0000, 0x10000, CRC(c825003a) SHA1(6242320d705c156f8369969d6caa8c737f01e4f3), ROM_BIOS(1)) // extracted with trojan

	// Version 1.001,1998/05/28,315-6124-02,SEGA Visual Memory System BIOS Produced by Sue
	ROM_SYSTEM_BIOS(2, "jp1001", "VMS Japanese BIOS (1.001 1998/05/28)")
	ROMX_LOAD("jp1001-19980528-315-6124-02.bin", 0x0000, 0x10000, CRC(e6339f4a) SHA1(688b2e1ff8c60bde6e8b07a2d2695cdacc07bd0c), ROM_BIOS(2))

	// Version 1.002,1998/06/04,315-6124-03,SEGA Visual Memory System BIOS Produced by Sue
	ROM_SYSTEM_BIOS(3, "jp1002", "VMS Japanese BIOS (1.002 1998/06/04)")
	ROMX_LOAD("jp1002-19980604-315-6124-03.bin", 0x0000, 0x10000, CRC(6c020d48) SHA1(9ee7c87d7b033235e0b315a0b421e70deb547c7a), ROM_BIOS(3))

	// Version 1.004,1998/09/30,315-6208-01,SEGA Visual Memory System BIOS Produced by Sue
	ROM_SYSTEM_BIOS(4, "jp1004", "VMS Japanese BIOS (1.004, 1998/09/30)")
	ROMX_LOAD("jp1004-19980930-315-6208-01.bin", 0x0000, 0x10000, CRC(8e0f867a) SHA1(dc2fa2963138a1049a43f7f36439ad0a416ee8b4), ROM_BIOS(4)) // from Sega Katana SDK (original file: fbios.sbf, CRC: c7c77b3c, xor key: 0x37)

	// Version 1.005,1998/12/09,315-6124-05,SEGA Visual Memory System BIOS Produced by Sue
	ROM_SYSTEM_BIOS(5, "jp1005a", "VMS Japanese BIOS (1.005 1998/12/09)")
	ROMX_LOAD("jp1005-19981209-315-6124-05.bin", 0x0000, 0x10000, CRC(47623324) SHA1(fca1aceff8a2f8c6826f3a865f4d5ef88dfd9ed1), ROM_BIOS(5))

	// Version 1.005,1999/10/26,315-6208-04,SEGA Visual Memory System BIOS Produced by Sue
	ROM_SYSTEM_BIOS(6, "jp1005b", "VMS Japanese BIOS (1.005 1999/10/26)")
	ROMX_LOAD("jp1005-19991026-315-6208-04.bin", 0x0000, 0x10000, CRC(6cab02c2) SHA1(6cc2fbf4a67770988922117c300d006aa20899ac), ROM_BIOS(6)) // extracted with trojan

	// Version 1.004,1998/09/30,315-6208-01,SEGA Visual Memory System BIOS Produced by Sue
	ROM_SYSTEM_BIOS(7, "dev1004", "VMS Japanese Development BIOS (1.004 1998/09/30)") // automatically boot the first game found in the flash
	ROMX_LOAD( "jp1004-19980930-315-6208-01-dev.bin", 0x0000, 0x10000, CRC(395e25f2) SHA1(37dea034322b5b80b35b2de784298d32c71ba7a3), ROM_BIOS(7)) // from Sega Katana SDK (original file: qbios.sbf, CRC: eed5524c, xor key: 0x43)
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  STATE       INIT        COMPANY  FULLNAME              FLAGS */
COMP( 1998, svmu, 0,      0,      svmu,    svmu,  svmu_state, empty_init, "Sega",  "Visual Memory Unit", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



swtpc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:68bit
/***************************************************************************

        SWTPC 6800 Computer System

        http://www.swtpc.com/mholley/swtpc_6800.htm

    MIKBUG is made for a PIA (parallel) interface.
    SWTBUG is made for a ACIA (serial) interface at the same address.
    MIKBUG will actually read the bits as they arrive and assemble a byte.
    Its delay loops are based on an underclocked XTAL.

    Note: All commands must be in uppercase. See the SWTBUG manual.

    ToDo:
        - Emulate MP-A2 revision of CPU board, with four 2716 ROM sockets
          and allowance for extra RAM boards at A000-BFFF and C000-DFFF

    The MP-B motherboard decodes 0x8000 to 0x9fff as being I/O on the
    motherboard, but the MP-B2 motherboard narrowed this to 0x8000 to 0x8fff
    allowing other boards to use the address range 0x9000 to 0x9fff, and this
    narrower range is emulated here.

    The MP-B2 motherboard decoded I/O address range can be relocate to any 4K
    boundary from 0x8000 to 0xf000 with 0x8000 being the default, but
    relocation requires an alternative monitor. TODO could add config options
    for this choice if ever needed.

    Within the 4K I/O address range only A2 to A5 are decoded, and A6 to A11
    are not considered. By default A5 must be zero, but there is an option to
    alternatively select the I/O when A5 is high to allow two motherboards to
    be used together to support more cards, one for A5 low and the other for
    A5 high. TODO might consider expanding the emulated SS30 bus to support
    two banks of 8 I/O cards if anyone really need that many cards in the
    emulator.

    The address range 0x0000 to 0x7fff was used for memory expansion, off the
    motherboard.

    The MIKBUG or SWTBUG monitor ROMS required RAM in the address range 0xa000
    to 0xa07f, which could be provided by a MCM6810 installed on the
    MP-A. However FLEX 2 operating system requires RAM in the address range
    0xa000 to 0xbfff and the MCM6810 was disable and that RAM provided
    externally to the MB and this larger RAM configuration is emulated here.

    The address range 0xc000 to 0xdfff was usable for a 2K, 4K, 6K, or 8K
    PROM (the Low PROM), or for RAM (MP-8M). This emulator implements RAM in
    this region. TODO support a Low PROM.

    The address range 0xe000 to 0xffff was usable for MIKBUG, SWTBUG (1K), or
    a 2K, 4K, or 8K PROM (the High PROM). TODO support a High PROM.

    Although the maximum baud rate generated by the MP-A is 1200 baud, the
    documentation notes that the 14411 baud rate generator does generate other
    rates up to 9600 baud and documents the pins to jumper to use these other
    rates. This emulator implements such a modification supplying 9600 baud on
    the otherwise 150 baud line, which consistent with the rate that the SWTPC
    6809 supplies on this bus line.

    The FDC card, e.g. DC5, requires five addresses, four for the WD FDC and
    one for a control register, which is one more than the 4 byte I/O
    selection address range allows. On the 6800 the FDC was expected to be
    installed in slot 6 and a jumper wire installed from the slot 5 select
    line to the FDC card UD3 line input to select the control register. The
    6809 MB moved to 16 byte I/O ranges to cleanly address this
    limitation. This hardware patch does not fit nicely in the emulator bus so
    is implemented here by placing the card in slot 5 and adapting the FDC
    card emulation to decode an 8 byte address range rather than 4
    addresses. TODO reconsider if this can be handler better.

    The DC5 FDC card offers an extra control register that can be used to set
    the density and clock rate, see the DC5 source code for more
    information. This might help booting disk images that are purely double
    density with the SWTBUG disk boot support. E.g. For a 3.5" HD disk image
    enter 'M 8016' which should show '02', the DC5 version, and then enter
    '28' to select double density and a 2MHz clock. TODO include a better boot
    ROM.

Commands:
B Breakpoint
C Clear screen
D Disk boot
E End of tape
F Find a byte
G Goto
J Jump
L Ascii Load
M Memory change (enter to quit, - to display next byte)
O Optional Port
P Ascii Punch
R Register dump
Z Goto Prom (0xC000)

****************************************************************************/

#include "emu.h"
#include "cpu/m6800/m6800.h"
#include "machine/input_merger.h"
#include "machine/mc14411.h"
#include "machine/ram.h"
#include "bus/ss50/interface.h"


namespace {

class swtpc_state : public driver_device
{
public:
	swtpc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, "ram")
		, m_brg(*this, "brg")
		, m_maincpu_clock(*this, "MAINCPU_CLOCK")
		, m_swtbug_load_at_a100(*this, "SWTBUG_LOAD_AT_A100")
	{ }

	void swtpcm(machine_config &config);
	void swtpc(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(maincpu_clock_change);

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<mc14411_device> m_brg;
	required_ioport m_maincpu_clock;
	required_ioport m_swtbug_load_at_a100;
};

void swtpc_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x8000, 0x8003).mirror(0x0fc0).rw("io0", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0x8004, 0x8007).mirror(0x0fc0).rw("io1", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0x8008, 0x800b).mirror(0x0fc0).rw("io2", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0x800c, 0x800f).mirror(0x0fc0).rw("io3", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0x8010, 0x8013).mirror(0x0fc0).rw("io4", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	// For the FDC a hardware patch was necesseary routing IO5 select to
	// the FDC installed in slot 6. For the Emulator, give IO5 an 8 byte
	// range for the FDC.
	map(0x8014, 0x801b).mirror(0x0fc0).rw("io5", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	//map(0x8018, 0x801b).mirror(0x0fc0).rw("io6", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0x801c, 0x801f).mirror(0x0fc0).rw("io7", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xa000, 0xbfff).ram();
	// TODO low prom, 2K, 4K, 6K, or 8K.
	map(0xc000, 0xdfff).ram();
	// TODO high prom, 1K, 2K, 4K, 8K.
	map(0xe000, 0xe3ff).mirror(0x1c00).rom().region("mcm6830", 0);
}

/* Input ports */
static INPUT_PORTS_START( swtpc )
	// Support some clock variations. The MP-A did not use a crystal for
	// the CPU clock and the frequency was variable. The 6800 was
	// available at speeds up to 2MHz so that might not have been
	// impossible. An overclock option of 4MHz is also implemented.
	PORT_START("MAINCPU_CLOCK")
	PORT_CONFNAME(0xffffff, 1000000, "CPU clock") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(swtpc_state::maincpu_clock_change), 0)
	PORT_CONFSETTING( 898550, "0.89855 MHz")  // MIKBUG
	PORT_CONFSETTING( 921600, "0.92160 MHz")  // SWTPC
	PORT_CONFSETTING(1000000, "1.0 MHz")
	PORT_CONFSETTING(2000000, "2.0 MHz")
	PORT_CONFSETTING(4000000, "4.0 MHz")

	// Patch SWTBUG to load the disk boot code at 0xa100 rather than
	// 0x2400. The disk boot code is typically position dependent and many
	// disk images expect to have their boot code loaded at 0xa100. TODO
	// consider adding a separate machine using NEWBUG etc that loads at
	// 0xa100 or perhaps better implement support for the high PROM to
	// allow custom code to be used which is needed anyway as even NEWBUG
	// appears to have issues and is not optimized for the DC5 FDC.
	PORT_START("SWTBUG_LOAD_AT_A100")
	PORT_CONFNAME(0x1, 1, "SWTBUG disk boot patch, to load at 0xa100")
	PORT_CONFSETTING(0, "No")
	PORT_CONFSETTING(1, "Yes - apply patch")

INPUT_PORTS_END

static DEVICE_INPUT_DEFAULTS_START( dc5 )
	DEVICE_INPUT_DEFAULTS("ADDRESS_MODE", 0x1, 0)
	DEVICE_INPUT_DEFAULTS("FORCE_READY", 0x1, 1)
DEVICE_INPUT_DEFAULTS_END

INPUT_CHANGED_MEMBER(swtpc_state::maincpu_clock_change)
{
	m_maincpu->set_clock(newval);
}

void swtpc_state::machine_reset()
{
	uint32_t maincpu_clock = m_maincpu_clock->read();
	if (maincpu_clock)
		m_maincpu->set_clock(maincpu_clock);

	// TODO make these SWTBUG patches conditional on using SWTBUG!

	if (m_swtbug_load_at_a100->read())
	{
		// Patch SWTBUG to load the disk boot sector at 0xa100.
		uint8_t* swtbug = memregion("mcm6830")->base();
		swtbug[0x02a7] = 0xa1; // Load address
		swtbug[0x02a8] = 0x00;
		swtbug[0x02bb] = 0xa1; // Entry address
		swtbug[0x02bc] = 0x00;
	}
}

void swtpc_state::machine_start()
{
	m_brg->rsa_w(0);
	m_brg->rsb_w(1);

	m_maincpu->space(AS_PROGRAM).install_ram(0, m_ram->size() - 1, m_ram->pointer());
}

void swtpc_state::swtpc(machine_config &config)
{
	/* basic machine hardware */
	M6800(config, m_maincpu, XTAL(1'843'200) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &swtpc_state::mem_map);

	MC14411(config, m_brg, XTAL(1'843'200));
	// 1200b
	m_brg->out_f<7>().set("io0", FUNC(ss50_interface_port_device::f600_1200_w));
	m_brg->out_f<7>().append("io1", FUNC(ss50_interface_port_device::f600_1200_w));
	m_brg->out_f<7>().append("io2", FUNC(ss50_interface_port_device::f600_1200_w));
	m_brg->out_f<7>().append("io3", FUNC(ss50_interface_port_device::f600_1200_w));
	m_brg->out_f<7>().append("io4", FUNC(ss50_interface_port_device::f600_1200_w));
	m_brg->out_f<7>().append("io5", FUNC(ss50_interface_port_device::f600_1200_w));
	m_brg->out_f<7>().append("io6", FUNC(ss50_interface_port_device::f600_1200_w));
	m_brg->out_f<7>().append("io7", FUNC(ss50_interface_port_device::f600_1200_w));
	// 600b
	m_brg->out_f<8>().set("io0", FUNC(ss50_interface_port_device::f600_4800_w));
	m_brg->out_f<8>().append("io1", FUNC(ss50_interface_port_device::f600_4800_w));
	m_brg->out_f<8>().append("io2", FUNC(ss50_interface_port_device::f600_4800_w));
	m_brg->out_f<8>().append("io3", FUNC(ss50_interface_port_device::f600_4800_w));
	m_brg->out_f<8>().append("io4", FUNC(ss50_interface_port_device::f600_4800_w));
	m_brg->out_f<8>().append("io5", FUNC(ss50_interface_port_device::f600_4800_w));
	m_brg->out_f<8>().append("io6", FUNC(ss50_interface_port_device::f600_4800_w));
	m_brg->out_f<8>().append("io7", FUNC(ss50_interface_port_device::f600_4800_w));
	// 300b
	m_brg->out_f<9>().set("io0", FUNC(ss50_interface_port_device::f300_w));
	m_brg->out_f<9>().append("io1", FUNC(ss50_interface_port_device::f300_w));
	m_brg->out_f<9>().append("io2", FUNC(ss50_interface_port_device::f300_w));
	m_brg->out_f<9>().append("io3", FUNC(ss50_interface_port_device::f300_w));
	m_brg->out_f<9>().append("io4", FUNC(ss50_interface_port_device::f300_w));
	m_brg->out_f<9>().append("io5", FUNC(ss50_interface_port_device::f300_w));
	m_brg->out_f<9>().append("io6", FUNC(ss50_interface_port_device::f300_w));
	m_brg->out_f<9>().append("io7", FUNC(ss50_interface_port_device::f300_w));
	// 150b pin 11, modified to wire the 14411 pin 1 to the f150 line for 9600b
	m_brg->out_f<1>().set("io0", FUNC(ss50_interface_port_device::f150_9600_w));
	m_brg->out_f<1>().append("io1", FUNC(ss50_interface_port_device::f150_9600_w));
	m_brg->out_f<1>().append("io2", FUNC(ss50_interface_port_device::f150_9600_w));
	m_brg->out_f<1>().append("io3", FUNC(ss50_interface_port_device::f150_9600_w));
	m_brg->out_f<1>().append("io4", FUNC(ss50_interface_port_device::f150_9600_w));
	m_brg->out_f<1>().append("io5", FUNC(ss50_interface_port_device::f150_9600_w));
	m_brg->out_f<1>().append("io6", FUNC(ss50_interface_port_device::f150_9600_w));
	m_brg->out_f<1>().append("io7", FUNC(ss50_interface_port_device::f150_9600_w));
	// 110b
	m_brg->out_f<13>().set("io0", FUNC(ss50_interface_port_device::f110_w));
	m_brg->out_f<13>().append("io1", FUNC(ss50_interface_port_device::f110_w));
	m_brg->out_f<13>().append("io2", FUNC(ss50_interface_port_device::f110_w));
	m_brg->out_f<13>().append("io3", FUNC(ss50_interface_port_device::f110_w));
	m_brg->out_f<13>().append("io4", FUNC(ss50_interface_port_device::f110_w));
	m_brg->out_f<13>().append("io5", FUNC(ss50_interface_port_device::f110_w));
	m_brg->out_f<13>().append("io6", FUNC(ss50_interface_port_device::f110_w));
	m_brg->out_f<13>().append("io7", FUNC(ss50_interface_port_device::f110_w));

	ss50_interface_port_device &io0(SS50_INTERFACE(config, "io0", ss50_default_2rs_devices, nullptr));
	io0.irq_cb().set("mainirq", FUNC(input_merger_device::in_w<0>));
	io0.firq_cb().set("mainnmi", FUNC(input_merger_device::in_w<0>));
	ss50_interface_port_device &io1(SS50_INTERFACE(config, "io1", ss50_default_2rs_devices, "mps"));
	io1.irq_cb().set("mainirq", FUNC(input_merger_device::in_w<1>));
	io1.firq_cb().set("mainnmi", FUNC(input_merger_device::in_w<1>));
	ss50_interface_port_device &io2(SS50_INTERFACE(config, "io2", ss50_default_2rs_devices, nullptr));
	io2.irq_cb().set("mainirq", FUNC(input_merger_device::in_w<2>));
	io2.firq_cb().set("mainnmi", FUNC(input_merger_device::in_w<2>));
	ss50_interface_port_device &io3(SS50_INTERFACE(config, "io3", ss50_default_2rs_devices, nullptr));
	io3.irq_cb().set("mainirq", FUNC(input_merger_device::in_w<3>));
	io3.firq_cb().set("mainnmi", FUNC(input_merger_device::in_w<3>));
	ss50_interface_port_device &io4(SS50_INTERFACE(config, "io4", ss50_default_2rs_devices, "mpt"));
	io4.irq_cb().set("mainirq", FUNC(input_merger_device::in_w<4>));
	io4.firq_cb().set("mainnmi", FUNC(input_merger_device::in_w<4>));
	ss50_interface_port_device &io5(SS50_INTERFACE(config, "io5", ss50_default_2rs_devices, "dc5"));
	io5.irq_cb().set("mainirq", FUNC(input_merger_device::in_w<5>));
	io5.firq_cb().set("mainnmi", FUNC(input_merger_device::in_w<5>));
	ss50_interface_port_device &io6(SS50_INTERFACE(config, "io6", ss50_default_2rs_devices, nullptr));
	io6.irq_cb().set("mainirq", FUNC(input_merger_device::in_w<6>));
	io6.firq_cb().set("mainnmi", FUNC(input_merger_device::in_w<6>));
	ss50_interface_port_device &io7(SS50_INTERFACE(config, "io7", ss50_default_2rs_devices, nullptr));
	io7.irq_cb().set("mainirq", FUNC(input_merger_device::in_w<7>));
	io7.firq_cb().set("mainnmi", FUNC(input_merger_device::in_w<7>));

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, M6800_IRQ_LINE);
	INPUT_MERGER_ANY_HIGH(config, "mainnmi").output_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);

	RAM(config, RAM_TAG).set_default_size("32K").set_extra_options("4K,8K,12K,16K,20K,24K,28K,32K");

	io5.set_option_device_input_defaults("dc5", DEVICE_INPUT_DEFAULTS_NAME(dc5));
}

void swtpc_state::swtpcm(machine_config &config)
{
	swtpc(config);
	m_maincpu->set_clock(XTAL(1'797'100) / 2);

	m_brg->set_clock(XTAL(1'797'100));

	subdevice<ss50_interface_port_device>("io1")->set_default_option("mpc");
}

/* ROM definition */
ROM_START( swtpc )
	ROM_REGION( 0x0400, "mcm6830", 0 )
	ROM_LOAD("swtbug.bin", 0x0000, 0x0400, CRC(f9130ef4) SHA1(089b2d2a56ce9526c3e78ce5d49ce368b9eabc0c))
ROM_END

ROM_START( swtpcm )
	ROM_REGION( 0x0400, "mcm6830", 0 )
	ROM_LOAD("mikbug.bin", 0x0000, 0x0400, CRC(e7f4d9d0) SHA1(5ad585218f9c9c70f38b3c74e3ed5dfe0357621c))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY                                     FULLNAME      FLAGS
COMP( 1977, swtpc,  0,      0,      swtpc,   swtpc, swtpc_state, empty_init, "Southwest Technical Products Corporation", "SWTPC 6800 Computer System (with SWTBUG)", MACHINE_NO_SOUND_HW )
COMP( 1975, swtpcm, swtpc,  0,      swtpcm,  swtpc, swtpc_state, empty_init, "Southwest Technical Products Corporation", "SWTPC 6800 Computer System (with MIKBUG)", MACHINE_NO_SOUND_HW )



swtpc09.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robert Justice,68bit
/**************************************************************************

    SWTPC S/09
    Robert Justice ,2009-2014

    Emulates four different fixed combinations of hardware
    1. swtpc09
       MP-09 with SBUG rom, MP-ID, MP-S2, MP-T, DMAF2.
       Will boot Flex operating system
    2. swtpc09i
       MP-09 with SBUG rom + HDrom, MP-ID, MP-S2, MP-T, DMAF2, PIAIDE.
       Will boot Flex operating system
    3. swtpc09u
       MP-09 with UniBUG rom, MP-ID, MP-S2, DMAF2.
       Will boot UniFlex operating system
    4. swtpc09d3
       MP-09 with UniBUG U3 rom, MP-ID, MP-S2, DMAF3.
       Will boot UniFlex operating system

***************************************************************************/

#include "emu.h"
#include "swtpc09.h"
#include "bus/ss50/interface.h"
#include "formats/flex_dsk.h"
#include "formats/os9_dsk.h"
#include "formats/uniflex_dsk.h"

/**************************************************************************
 Address maps

 56K of RAM from 0x0000 to 0xdfff
 2K  of ROM from 0xf800 to 0xffff

 E000 - E003  S2   MC6850 ACIA1   (used for UniFlex console)
 E004 - E007  S2   MC6850 ACIA2   (used for Flex console)
 E014 - E015  DC5  Control reg.
 E016 - E017  DC5  Control reg. 2
 E018 - E01B  DC5  WD2797 FDC
 E040 - E043  MPT  MC6821 PIA + MK5009 timer/counter
 E080 - E08F  MPID MC6821 PIA
 E090 - E09F  MPID MC6840 PTM

 F000 - F01F  DMAF2 MC6844 DMAC
 F020 - F023  DMAF2 WD1791 FDC
 F024 - F03F  DMAF2 Drive select register
 F040 - F041  DMAF2 DMA Address register, and interrupt enable, latch.

 F800 - FFFF  ROM
 FFF0 - FFFF  DAT RAM (only for writes)


 for DMAF3 version
 F000 - F01F  DMAF3 MC6844 DMAC
 F020 - F023  DMAF3 WD1791 FDC
 F024 - F024  DMAF3 Drive select register
 F025 - F025  DMAF3 74LS374 latch for 4 extended address lines (bits 0-3), DMA.
 F030 - F03F  DMAF3 HDC WD1000
 F040 - F04F  DMAF3 VIA6522
 F050 - F050  DMAF3 HDC head load toggle
 F051 - F051  DMAF3 Winchester software reset
 F052 - F052  DMAF3 Archive reset
 F053 - F053  DMAF3 Archive clear
 F060 - ????  DMAF3 archive DMA preset

 F100 - ????  CDS disk 0 controller
 F300 - ????  CDS disk 1 controller

** Notes on use of MAME with common disk images **

MAME emulates the floppy disk controller more thoroughly than some other common
emulators for these systems. Other common emulators can ignore the disk density
and disk side driven by the software. FLEX numbers sectors contiguously from one
side to the other, so a simple emulator need only map the track and sector
numbers. Real disk images often mix single and double density formats, e.g.
single density for the first track, and the software may not have been updated
to correctly drive the side and density outputs, so disks for these other
emulators might not work in MAME. These disk images and their softare need to be
updated - this is not working around limitations of MAME, these would not be
expected to work on real hardware either.

However the disk controllers used by MAME have some options that can help with
this transition, help get these disk images running as-is. The disk density
selection can be overridden. The disk side selection can be overridden for a
given number of FLEX sectors per side and this can be done separately for
track 0 and side 0.

The FDC clock speed needs to be appropriate for the disk drives and format. The
historical controllers typically hard wired a clock rate, and the historical
software was not written to drive clock selection as available in some later
controllers. The CPU clock may also need to be adjusted for some combinations,
to meet the timing requirements.

The boot ROM many also need patching to boot a double density rather than
single density format disk, and this emulator has an opion to patch the ROM
for this.

Examples:

mame swtpc09 -fdc:0 35hd -flop1 DMAF2_BOOT.dsk
Options:
 DMAF2/3 FDC clock: 2.0MHz
 DMAF2/3 expected density: double density
 DMAF2/3 FLEX expected sectors per side: 36
Type D to boot FLEX operating system.

mame swtpc09u -flop1 uflxbtsd.dsk
Options (untested, but the DMAF2 is tested with FLEX):
 DMAF2/3 FDC clock: 2.0MHz
 DMAF2/3 expected density: double density
Type D to boot UniFLEX.

mame swtpc09d3 -hard1 wd0.chd -hard2 wd1.chd -fdc:0 8dsdd -flop1 UOS_3S_BOOT.mfi
Where the disks are prepared:
 chdman createhd -o wd0.chd -i wd0.dsk -chs 306,6,17 -s 512
 chdman createhd -o wd1.chd -i wd1.dsk -chs 306,6,17 -s 512
 floptool flopconvert uniflex mfi UOS_3S_BOOT.DSK UOS_3S_BOOT.mfi
Options:
 DMAF2/3 FDC clock: 2.0MHz
 DMAF2/3 expected density: double density
Type D to boot UniFLEX from the floppy disk, or W to boot from the hard disk.

mame swtpc09i -io1:dc5:fdc:0 35hd -flop1 FLEX292F.dsk
Options:
 DC5 Control register bit 7: Erroneous side select
 DC5 FDC clock: 2.0MHz
 DC5 Expected density: double density
 DC5 FLEX expected sectors per side: 36
 CPU clock: 4MHz
Type U to boot into FLEX

mame swtpc09i -hard 2048X18X32.chd
Where 2048X18X32.chd is derived from the 2048X18X32.dsk image.  MAME expects the
ROM at 0xf000 rather than 0xe800 as implement here (todo updated ROM?), so patch
at offset 0x68 0xf1 to 0xe9, and at offset 0x2c51c 0xf1 to 0xe9. The PIA IDE is
installed in IO6 here, yet this software expects it at IO1, so patch these to
0xe06* at offsets 0xd6, 0xd6, 0xdb, 0xe0, 0xe5, 0xe8, 0xed, 0xf2, 0xfa, 0xff,
0x104, 0x109, 0x10e, 0x113, 0x116. Then remove the trailing info, past
2048X18X32X256. Then fill every second byte with zero as only the lower 8 bits
of every 16 bit word are used. Then convert to a CHD disk file, adding the
geometry:
 chdman createhd -o 2048X18X32.chd -i 2048X18X32.dsk -chs 2048,18,32 -s 512
MAME appears to cold start FLEX at 0xcd00 as opposed to 0xc800, and the PIA IDE
ROM needs patching for that, and there is a machine option to do so. At the SBUG
prompt enter Ctrl-P and 'E800', then 'G' and it should boot into FLEX. MAME
might not match the ROM and hardware here.

mame swtpc09o -io1:dc5:fdc:0 qd -flop1 OS9BTBK3.DSK
 DC5 FDC clock: 1.0MHz
 CPU clock: 4MHz
Boots into OS9, asking for the date and time.

***************************************************************************/

/* Address map is dynamically setup when DAT memory is written to  */
/* only ROM from FF00-FFFF and DAT memory at FFF0-FFFF (write only) is guaranteed always*/

void swtpc09_state::mp09_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(swtpc09_state::main_r), FUNC(swtpc09_state::main_w));
	map(0xff00, 0xff0f).mirror(0xf0).writeonly().share("dat");
}

void swtpc09_state::flex_dmaf2_mem(address_map &map)
{
	map(0x00000, 0xfffff).ram(); // by default everything is ram, 1MB ram emulated
	map(0xfe000, 0xfe7ff).rw(FUNC(swtpc09_state::unmapped_r), FUNC(swtpc09_state::unmapped_w));

	// 0xfe004, 0xfe005 ACIA
	map(0xfe000, 0xfe00f).rw("io0", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe010, 0xfe01f).rw("io1", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe020, 0xfe02f).rw("io2", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe030, 0xfe03f).rw("io3", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe040, 0xfe04f).rw("io4", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe050, 0xfe05f).rw("io5", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe060, 0xfe06f).rw("io6", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe070, 0xfe07f).rw("io7", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));

	// MPID
	map(0xfe080, 0xfe083).mirror(0xc).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xfe090, 0xfe097).mirror(0x8).rw(m_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write));

	// DMAF2
	map(0xff000, 0xff01f).rw(FUNC(swtpc09_state::m6844_r), FUNC(swtpc09_state::m6844_w));
	map(0xff020, 0xff023).rw(FUNC(swtpc09_state::dmaf2_fdc_r), FUNC(swtpc09_state::dmaf2_fdc_w));
	map(0xff024, 0xff03f).rw(FUNC(swtpc09_state::dmaf2_control_reg_r), FUNC(swtpc09_state::dmaf2_control_reg_w));
	map(0xff040, 0xff040).rw(FUNC(swtpc09_state::dmaf2_dma_address_reg_r), FUNC(swtpc09_state::dmaf2_dma_address_reg_w));

	map(0xff800, 0xfffff).rom().region("bankdev", 0xff800);
}

void swtpc09_state::flex_dc5_piaide_mem(address_map &map)
{
	map(0x00000, 0xfffff).ram(); // by default everything is ram, 1MB ram emulated
	map(0xfe000, 0xfe7ff).rw(FUNC(swtpc09_state::unmapped_r), FUNC(swtpc09_state::unmapped_w));

	// 0xfe004, 0xfe005 ACIA
	map(0xfe000, 0xfe00f).rw("io0", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	// 0xfe014 0xfe018-0xfe01b DC5 FDC
	map(0xfe010, 0xfe01f).rw("io1", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe020, 0xfe02f).rw("io2", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe030, 0xfe03f).rw("io3", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe040, 0xfe04f).rw("io4", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe050, 0xfe05f).rw("io5", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	// PIA IDE
	map(0xfe060, 0xfe06f).rw("io6", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe070, 0xfe07f).rw("io7", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));

	// MPID
	map(0xfe080, 0xfe083).mirror(0xc).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xfe090, 0xfe097).mirror(0x8).rw(m_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write));

	// TODO How does this mapping change with the bank changes?
	map(0xfe800, 0xfefff).rom().region("bankdev", 0xfe800); //piaide rom

	map(0xff800, 0xfffff).rom().region("bankdev", 0xff800);
}

void swtpc09_state::uniflex_dmaf2_mem(address_map &map)
{
	map(0x00000, 0xfffff).ram(); // by default everything is ram, 1MB ram emulated
	map(0xfe000, 0xfffff).rw(FUNC(swtpc09_state::unmapped_r), FUNC(swtpc09_state::unmapped_w));

	// 0xfe000, 0xfe001 ACIA
	map(0xfe000, 0xfe00f).rw("io0", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe010, 0xfe01f).rw("io1", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe020, 0xfe02f).rw("io2", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe030, 0xfe03f).rw("io3", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe040, 0xfe04f).rw("io4", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe050, 0xfe05f).rw("io5", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe060, 0xfe06f).rw("io6", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe070, 0xfe07f).rw("io7", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));

	// MPID
	map(0xfe080, 0xfe083).mirror(0xc).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xfe090, 0xfe097).mirror(0x8).rw(m_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write));

	// DMAF2
	map(0xff000, 0xff01f).rw(FUNC(swtpc09_state::m6844_r), FUNC(swtpc09_state::m6844_w));
	map(0xff020, 0xff023).rw(FUNC(swtpc09_state::dmaf2_fdc_r), FUNC(swtpc09_state::dmaf2_fdc_w));
	map(0xff024, 0xff03f).rw(FUNC(swtpc09_state::dmaf2_control_reg_r), FUNC(swtpc09_state::dmaf2_control_reg_w));

	map(0xff800, 0xfffff).rom().region("bankdev", 0xff800);
}

void swtpc09_state::uniflex_dmaf3_mem(address_map &map)
{
	map(0x00000, 0xfffff).ram(); // by default everything is ram, 1MB ram emulated
	map(0xfe000, 0xfffff).rw(FUNC(swtpc09_state::unmapped_r), FUNC(swtpc09_state::unmapped_w));

	// 0xfe000, 0xfe001 ACIA
	map(0xfe000, 0xfe00f).rw("io0", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe010, 0xfe01f).rw("io1", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe020, 0xfe02f).rw("io2", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe030, 0xfe03f).rw("io3", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe040, 0xfe04f).rw("io4", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe050, 0xfe05f).rw("io5", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe060, 0xfe06f).rw("io6", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0xfe070, 0xfe07f).rw("io7", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));

	// MPID
	map(0xfe080, 0xfe083).mirror(0xc).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xfe090, 0xfe097).mirror(0x8).rw(m_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write));

	// DMAF3
	map(0xff000, 0xff01f).rw(FUNC(swtpc09_state::m6844_r), FUNC(swtpc09_state::m6844_w));
	map(0xff020, 0xff023).rw(FUNC(swtpc09_state::dmaf3_fdc_r), FUNC(swtpc09_state::dmaf3_fdc_w));
	map(0xff024, 0xff024).rw(FUNC(swtpc09_state::dmaf3_control_reg_r), FUNC(swtpc09_state::dmaf3_control_reg_w));
	map(0xff025, 0xff025).rw(FUNC(swtpc09_state::dmaf3_dma_address_reg_r), FUNC(swtpc09_state::dmaf3_dma_address_reg_w));
	map(0xff030, 0xff03f).rw(m_hdc, FUNC(wd1000_device::read), FUNC(wd1000_device::write));
	map(0xff040, 0xff04f).m(m_via, FUNC(via6522_device::map));
	map(0xff050, 0xff050).rw(FUNC(swtpc09_state::dmaf3_hdc_control_r), FUNC(swtpc09_state::dmaf3_hdc_control_w));
	map(0xff051, 0xff051).rw(FUNC(swtpc09_state::dmaf3_hdc_reset_r), FUNC(swtpc09_state::dmaf3_hdc_reset_w));
	map(0xff052, 0xff052).rw(FUNC(swtpc09_state::dmaf3_archive_reset_r), FUNC(swtpc09_state::dmaf3_archive_reset_w));
	map(0xff053, 0xff053).rw(FUNC(swtpc09_state::dmaf3_archive_clear_r), FUNC(swtpc09_state::dmaf3_archive_clear_w));

	map(0xff800, 0xfffff).rom().region("bankdev", 0xff800);
}

void swtpc09_state::os9_mem(address_map &map)
{
	map(0x00000, 0xfffff).ram(); // by default everything is ram, 1MB ram emulated
	map(0x0e000, 0x0e7ff).rw(FUNC(swtpc09_state::unmapped_r), FUNC(swtpc09_state::unmapped_w));

	// 0x0e004, 0x0e005 ACIA
	map(0x0e000, 0x0e00f).rw("io0", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	// 0x0e014 0x0e018-0x0e01b DC5 FDC
	map(0x0e010, 0x0e01f).rw("io1", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0x0e020, 0x0e02f).rw("io2", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0x0e030, 0x0e03f).rw("io3", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	// MPT
	map(0x0e040, 0x0e04f).rw("io4", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0x0e050, 0x0e05f).rw("io5", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0x0e060, 0x0e06f).rw("io6", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));
	map(0x0e070, 0x0e07f).rw("io7", FUNC(ss50_interface_port_device::read), FUNC(ss50_interface_port_device::write));

	// MPID
	map(0x0e080, 0x0e083).mirror(0xc).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x0e090, 0x0e097).mirror(0x8).rw(m_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write));

	map(0x0e400, 0x0ffff).rom().region("bankdev", 0x0e400);
}

/* Input ports */
static INPUT_PORTS_START( swtpc09 )
	// The MP09 was available in 4MHz or 8MHz XTALs, running the 6809 at
	// 1MHz or 2MHz respectively. Also allow an overclock option.
	//
	// Run the 6809 at 2MHz (an 8MHz XTAL) so that manual polling of FDC can
	// keep up with higher rate operation. The MPU09 did have the option of
	// 1MHz or 2MHz operation.
	PORT_START("MAINCPU_CLOCK")
	PORT_CONFNAME(0xffffff, 2000000, "CPU clock") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(swtpc09_state::maincpu_clock_change), 0)
	PORT_CONFSETTING(1000000, "1.0 MHz")
	PORT_CONFSETTING(2000000, "2.0 MHz")
	PORT_CONFSETTING(4000000, "4.0 MHz")

	// The DMAF2/3 clock can be jumpered selected for 1 or 2MHz, for 5.24"
	// and 8" drives. Some other options are also provided here.
	//
	// single or double density 5.24"  -  1.0MHz
	// 'standard' 3.5"  -  1.2MHz
	// 3.5" hd  -  2.0MHz
	// 8" 360rpm  -  2.4MHz
	PORT_START("FDC_CLOCK")
	PORT_CONFNAME(0xffffff, 2000000, "DMAF2/3 FDC clock") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(swtpc09_state::fdc_clock_change), 0)
	PORT_CONFSETTING(1000000, "1.0 MHz")
	PORT_CONFSETTING(1200000, "1.2 MHz")
	PORT_CONFSETTING(2000000, "2.0 MHz")
	PORT_CONFSETTING(2400000, "2.4 MHz")

	// The MP-ID board has jumpers to vary the baud rates generated. The
	// most useful is the Low/High rate jumper that provide a four times
	// rate increase.
	PORT_START("BAUD_RATE_HIGH")
	PORT_CONFNAME(0x1, 0, "High baud rate") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(swtpc09_state::baud_rate_high_change), 0)
	PORT_CONFSETTING(0, "Low (x1)")
	PORT_CONFSETTING(1, "High (x4)")

	// Debug aid to hard code the density. The OS9 format can use single
	// density for track zero head zero. The FLEX format can use single
	// density for track zero on all heads, and many virtual disks 'fix'
	// the format to be purely double density and often without properly
	// implementing driver support for that. This setting is an aid to
	// report unexpected usage, and it attempts to correct that.
	PORT_START("FLOPPY_EXPECTED_DENSITY")
	PORT_CONFNAME(0x7, 0, "DMAF2/3 expected density")
	PORT_CONFSETTING(0, "-")
	PORT_CONFSETTING(1, "single density")
	PORT_CONFSETTING(2, "double density, with single density track zero head zero")
	PORT_CONFSETTING(3, "double density, with single density track zero all heads")
	PORT_CONFSETTING(4, "double density")

	// Debug aid, to check that the sector head or side is set as expected
	// for the sector ID being read for the FLEX floppy disk format. Many
	// FLEX disk images were developed for vitural machines that have
	// little regard for the actual number of heads and can work off the
	// sector ID and their drivers can set the head incorrectly. E.g. a
	// disk with 18 sectors per side might have a driver incorrectly switch
	// heads for sectors above 10. Another issue is that double density
	// disk images are often 'fixed' so that they are pure double density
	// without being single density on the first track, and the drivers
	// might still set the head based on track zero being single
	// density. This aid is not intended to be a substitute for fixing the
	// drivers but it does help work through the issues while patching the
	// disks.
	PORT_START("FLOPPY_EXPECTED_SECTORS")
	PORT_CONFNAME(0xff, 0, "DMAF2/3 FLEX expected sectors per side")
	PORT_CONFSETTING(0, "-")
	PORT_CONFSETTING(10, "10") // 5 1/4" single density 256B
	PORT_CONFSETTING(15, "15") // 8" single density 256B
	PORT_CONFSETTING(16, "16") // 5 1/4" double density 256B, 8" DD 512B
	PORT_CONFSETTING(18, "18") // 5 1/4" double density 256B
	PORT_CONFSETTING(26, "26") // 8" double density 256B
	PORT_CONFSETTING(36, "36") // 3.5" 1.4M QD 256B
	// The track zero expected sectors if different from the above. FLEX
	// 6800 disks did format track zero in single density and if the
	// driver sticks to that and if using a double density disk then set a
	// single density size here.
	PORT_START("FLOPPY_TRACK_ZERO_EXPECTED_SECTORS")
	PORT_CONFNAME(0xff, 0, "DMAF2/3 FLEX track zero expected sectors per side")
	PORT_CONFSETTING(0, "-")
	PORT_CONFSETTING(10, "10") // 5 1/4" single density 256B
	PORT_CONFSETTING(15, "15") // 8" single density 256B
	PORT_CONFSETTING(16, "16") // 5 1/4" double density 256B, 8" DD 512B
	PORT_CONFSETTING(18, "18") // 5 1/4" double density 256B
	PORT_CONFSETTING(26, "26") // 8" double density 256B
	PORT_CONFSETTING(36, "36") // 3.5" 1.4M QD 256B

	PORT_START("SBUG_DOUBLE_DENSITY")
	PORT_CONFNAME(0x1, 0, "SBUG patch for double density (SSO) disk boot")
	PORT_CONFSETTING(0, "No - single density")
	PORT_CONFSETTING(1, "Yes - double density")

	// The PIA IDE PROM includes initialization code that patches FLEX
	// after the disk boot code has loaded FLEX, and then it jumps to
	// 0xc850 to cold start FLEX. Have seen 0xcd00 being the cold start
	// address, so add an option to patch the PROM for that.
	PORT_START("PIAIDE_FLEX_BOOT_CD00")
	PORT_CONFNAME(0x1, 0, "PIA IDE PROM patch FLEX entry to 0xcd00")
	PORT_CONFSETTING(0, "No - FLEX entry 0xc850")
	PORT_CONFSETTING(1, "Yes - FLEX entry 0xcd00")

INPUT_PORTS_END


static DEVICE_INPUT_DEFAULTS_START( dc5 )
	DEVICE_INPUT_DEFAULTS("ADDRESS_MODE", 0x1, 1)
	DEVICE_INPUT_DEFAULTS("FORCE_READY", 0x1, 0)
DEVICE_INPUT_DEFAULTS_END


void swtpc09_state::floppy_flex_formats(format_registration &fr)
{
	fr.add_fm_containers();
	fr.add(FLOPPY_FLEX_FORMAT);
}

void swtpc09_state::floppy_uniflex_formats(format_registration &fr)
{
	fr.add_fm_containers();
	fr.add(FLOPPY_UNIFLEX_FORMAT);
}


// todo: implement floppy controller cards as slot devices and do this properly
static void swtpc09_flex_floppies(device_slot_interface &device)
{
	device.option_add("sssd35", FLOPPY_525_SSSD_35T); // 35 trks ss sd 5.25
	device.option_add("sssd",   FLOPPY_525_SSSD);     // 40 trks ss sd 5.25
	device.option_add("dssd35", FLOPPY_525_SD_35T);   // 35 trks ds sd 5.25
	device.option_add("dssd",   FLOPPY_525_SD);       // 40 trks ds sd 5.25
	device.option_add("ssdd",   FLOPPY_525_SSDD);     // 40 trks ss dd 5.25
	device.option_add("dd",     FLOPPY_525_DD);       // 40 trks ds dd 5.25
	device.option_add("ssqd",   FLOPPY_525_SSQD);     // 80 trks ss dd 5.25
	device.option_add("qd",     FLOPPY_525_QD);       // 80 trks ds dd 5.25
	device.option_add("8sssd",  FLOPPY_8_SSSD);       // 77 trks ss sd 8"
	device.option_add("8dssd",  FLOPPY_8_DSSD);       // 77 trks ds sd 8"
	device.option_add("8ssdd",  FLOPPY_8_SSDD);       // 77 trks ss dd 8"
	device.option_add("8dsdd",  FLOPPY_8_DSDD);       // 77 trks ds dd 8"
	device.option_add("35hd",   FLOPPY_35_HD);        // 1.44mb disk
}

static void swtpc09_uniflex_floppies(device_slot_interface &device)
{
	device.option_add("8dssd",  FLOPPY_8_DSSD);       // 8 inch ds sd
	device.option_add("8dsdd",  FLOPPY_8_DSDD);       // 8 inch ds dd
 }


/***************************************************************************
 Machine definitions
****************************************************************************/

/* Machine driver */
/* MPU09, MPID, MPS2 */
void swtpc09_state::swtpc09_base(machine_config &config)
{
	/* basic machine hardware */
	MC6809(config, m_maincpu, 8_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &swtpc09_state::mp09_mem);

	ADDRESS_MAP_BANK(config, m_bankdev, 0);
	m_bankdev->set_endianness(ENDIANNESS_BIG);
	m_bankdev->set_data_width(8);
	m_bankdev->set_addr_width(20);
	m_bankdev->set_addrmap(AS_PROGRAM, &swtpc09_state::flex_dmaf2_mem);

	// IO0 at 0xe000 is used for the MP-S2 ACIA.
	ss50_interface_port_device &io0(SS50_INTERFACE(config, "io0", ss50_default_2rs_devices, nullptr));
	io0.irq_cb().set("mainirq", FUNC(input_merger_device::in_w<0>));
	io0.firq_cb().set("mainfirq", FUNC(input_merger_device::in_w<0>));
	// IO1 at 0xe010 is used for the DC5 FDC.
	ss50_interface_port_device &io1(SS50_INTERFACE(config, "io1", ss50_default_2rs_devices, nullptr));
	io1.irq_cb().set("mainirq", FUNC(input_merger_device::in_w<1>));
	io1.firq_cb().set("mainfirq", FUNC(input_merger_device::in_w<1>));
	ss50_interface_port_device &io2(SS50_INTERFACE(config, "io2", ss50_default_2rs_devices, nullptr));
	io2.irq_cb().set("mainirq", FUNC(input_merger_device::in_w<2>));
	io2.firq_cb().set("mainfirq", FUNC(input_merger_device::in_w<2>));
	ss50_interface_port_device &io3(SS50_INTERFACE(config, "io3", ss50_default_2rs_devices, nullptr));
	io3.irq_cb().set("mainirq", FUNC(input_merger_device::in_w<3>));
	io3.firq_cb().set("mainfirq", FUNC(input_merger_device::in_w<3>));
	ss50_interface_port_device &io4(SS50_INTERFACE(config, "io4", ss50_default_2rs_devices, nullptr));
	io4.irq_cb().set("mainirq", FUNC(input_merger_device::in_w<4>));
	io4.firq_cb().set("mainfirq", FUNC(input_merger_device::in_w<4>));
	ss50_interface_port_device &io5(SS50_INTERFACE(config, "io5", ss50_default_2rs_devices, nullptr));
	io5.irq_cb().set("mainirq", FUNC(input_merger_device::in_w<5>));
	io5.firq_cb().set("mainfirq", FUNC(input_merger_device::in_w<5>));
	// IO6 at 0xe060 is used for PIA-IDE
	ss50_interface_port_device &io6(SS50_INTERFACE(config, "io6", ss50_default_2rs_devices, nullptr));
	io6.irq_cb().set("mainirq", FUNC(input_merger_device::in_w<6>));
	io6.firq_cb().set("mainfirq", FUNC(input_merger_device::in_w<6>));
	ss50_interface_port_device &io7(SS50_INTERFACE(config, "io7", ss50_default_2rs_devices, nullptr));
	io7.irq_cb().set("mainirq", FUNC(input_merger_device::in_w<7>));
	io7.firq_cb().set("mainfirq", FUNC(input_merger_device::in_w<7>));

	// IO8 at 0xe080 is used internally by the MPID board PIA.
	PIA6821(config, m_pia);
	m_pia->readpa_handler().set(FUNC(swtpc09_state::pia0_a_r));
	m_pia->ca1_w(0);
	m_pia->irqa_handler().set(FUNC(swtpc09_state::pia0_irq_a));

	// IO9 at 0xe090 is used internally by the MPID board 6840 timer.
	PTM6840(config, m_ptm, 2000000);
	m_ptm->set_external_clocks(50, 0, 50);
	m_ptm->o1_callback().set(FUNC(swtpc09_state::ptm_o1_callback));
	m_ptm->o3_callback().set(FUNC(swtpc09_state::ptm_o3_callback));
	m_ptm->irq_callback().set(FUNC(swtpc09_state::ptm_irq));

	// MP-ID baud rate generator
	MC14411(config, m_brg, 1.8432_MHz_XTAL);
	// 9600b / 38400b
	m_brg->out_f<1>().set("io0", FUNC(ss50_interface_port_device::f150_9600_w));
	m_brg->out_f<1>().append("io1", FUNC(ss50_interface_port_device::f150_9600_w));
	m_brg->out_f<1>().append("io2", FUNC(ss50_interface_port_device::f150_9600_w));
	m_brg->out_f<1>().append("io3", FUNC(ss50_interface_port_device::f150_9600_w));
	m_brg->out_f<1>().append("io4", FUNC(ss50_interface_port_device::f150_9600_w));
	m_brg->out_f<1>().append("io5", FUNC(ss50_interface_port_device::f150_9600_w));
	m_brg->out_f<1>().append("io6", FUNC(ss50_interface_port_device::f150_9600_w));
	m_brg->out_f<1>().append("io7", FUNC(ss50_interface_port_device::f150_9600_w));
	// 4800b / 19200b
	m_brg->out_f<3>().set("io0", FUNC(ss50_interface_port_device::f600_4800_w));
	m_brg->out_f<3>().append("io1", FUNC(ss50_interface_port_device::f600_4800_w));
	m_brg->out_f<3>().append("io2", FUNC(ss50_interface_port_device::f600_4800_w));
	m_brg->out_f<3>().append("io3", FUNC(ss50_interface_port_device::f600_4800_w));
	m_brg->out_f<3>().append("io4", FUNC(ss50_interface_port_device::f600_4800_w));
	m_brg->out_f<3>().append("io5", FUNC(ss50_interface_port_device::f600_4800_w));
	m_brg->out_f<3>().append("io6", FUNC(ss50_interface_port_device::f600_4800_w));
	m_brg->out_f<3>().append("io7", FUNC(ss50_interface_port_device::f600_4800_w));
	// 1200b / 4800b
	m_brg->out_f<7>().set("io0", FUNC(ss50_interface_port_device::f600_1200_w));
	m_brg->out_f<7>().append("io1", FUNC(ss50_interface_port_device::f600_1200_w));
	m_brg->out_f<7>().append("io2", FUNC(ss50_interface_port_device::f600_1200_w));
	m_brg->out_f<7>().append("io3", FUNC(ss50_interface_port_device::f600_1200_w));
	m_brg->out_f<7>().append("io4", FUNC(ss50_interface_port_device::f600_1200_w));
	m_brg->out_f<7>().append("io5", FUNC(ss50_interface_port_device::f600_1200_w));
	m_brg->out_f<7>().append("io6", FUNC(ss50_interface_port_device::f600_1200_w));
	m_brg->out_f<7>().append("io7", FUNC(ss50_interface_port_device::f600_1200_w));
	// 300b / 1200b
	m_brg->out_f<9>().set("io0", FUNC(ss50_interface_port_device::f300_w));
	m_brg->out_f<9>().append("io1", FUNC(ss50_interface_port_device::f300_w));
	m_brg->out_f<9>().append("io2", FUNC(ss50_interface_port_device::f300_w));
	m_brg->out_f<9>().append("io3", FUNC(ss50_interface_port_device::f300_w));
	m_brg->out_f<9>().append("io4", FUNC(ss50_interface_port_device::f300_w));
	m_brg->out_f<9>().append("io5", FUNC(ss50_interface_port_device::f300_w));
	m_brg->out_f<9>().append("io6", FUNC(ss50_interface_port_device::f300_w));
	m_brg->out_f<9>().append("io7", FUNC(ss50_interface_port_device::f300_w));
	// 110b / 440b
	m_brg->out_f<13>().set("io0", FUNC(ss50_interface_port_device::f110_w));
	m_brg->out_f<13>().append("io1", FUNC(ss50_interface_port_device::f110_w));
	m_brg->out_f<13>().append("io2", FUNC(ss50_interface_port_device::f110_w));
	m_brg->out_f<13>().append("io3", FUNC(ss50_interface_port_device::f110_w));
	m_brg->out_f<13>().append("io4", FUNC(ss50_interface_port_device::f110_w));
	m_brg->out_f<13>().append("io5", FUNC(ss50_interface_port_device::f110_w));
	m_brg->out_f<13>().append("io6", FUNC(ss50_interface_port_device::f110_w));
	m_brg->out_f<13>().append("io7", FUNC(ss50_interface_port_device::f110_w));

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set(FUNC(swtpc09_state::io_irq_w));
	INPUT_MERGER_ANY_HIGH(config, "mainfirq").output_handler().set_inputline(m_maincpu, M6809_FIRQ_LINE);

	subdevice<ss50_interface_port_device>("io0")->set_default_option("mps2");
}

/* MPU09, MPID, MPS2, DMAF2 */
void swtpc09_state::swtpc09(machine_config &config)
{
	swtpc09_base(config);

	// DMAF2
	FD1797(config, m_fdc, 2000000);
	FLOPPY_CONNECTOR(config, "fdc:0", swtpc09_flex_floppies, "dd", swtpc09_state::floppy_flex_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", swtpc09_flex_floppies, "dd", swtpc09_state::floppy_flex_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:2", swtpc09_flex_floppies, "dd", swtpc09_state::floppy_flex_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:3", swtpc09_flex_floppies, "dd", swtpc09_state::floppy_flex_formats).enable_sound(true);

	m_fdc->intrq_wr_callback().set(FUNC(swtpc09_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(swtpc09_state::fdc_drq_w));
	m_fdc->sso_wr_callback().set(FUNC(swtpc09_state::fdc_sso_w));
}

/* MPU09, MPID, MPS2, DC5, MPT, PIAIDE*/
void swtpc09_state::swtpc09i(machine_config &config)
{
	swtpc09_base(config);
	m_bankdev->set_addrmap(AS_PROGRAM, &swtpc09_state::flex_dc5_piaide_mem);

	subdevice<ss50_interface_port_device>("io1")->set_default_option("dc5");
	subdevice<ss50_interface_port_device>("io1")->set_option_device_input_defaults("dc5", DEVICE_INPUT_DEFAULTS_NAME(dc5));
	subdevice<ss50_interface_port_device>("io4")->set_default_option("mpt");
	subdevice<ss50_interface_port_device>("io6")->set_default_option("piaide");
}


void swtpc09_state::swtpc09u(machine_config &config)
{
	swtpc09_base(config);
	m_bankdev->set_addrmap(AS_PROGRAM, &swtpc09_state::uniflex_dmaf2_mem);

	// DMAF2
	FD1797(config, m_fdc, 2400000);
	FLOPPY_CONNECTOR(config, "fdc:0", swtpc09_uniflex_floppies, "8dsdd", swtpc09_state::floppy_uniflex_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", swtpc09_uniflex_floppies, "8dsdd", swtpc09_state::floppy_uniflex_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:2", swtpc09_uniflex_floppies, "8dsdd", swtpc09_state::floppy_uniflex_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:3", swtpc09_uniflex_floppies, "8dsdd", swtpc09_state::floppy_uniflex_formats).enable_sound(true);

	m_fdc->intrq_wr_callback().set(FUNC(swtpc09_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(swtpc09_state::fdc_drq_w));
	m_fdc->sso_wr_callback().set(FUNC(swtpc09_state::fdc_sso_w));
}


/* MPU09, MPID, MPS2 DMAF3 */
void swtpc09_state::swtpc09d3(machine_config &config)
{
	swtpc09_base(config);
	m_pia->set_clock(2000000);
	m_bankdev->set_addrmap(AS_PROGRAM, &swtpc09_state::uniflex_dmaf3_mem);

	// DMAF3
	FD1797(config, m_fdc, 2400000);
	FLOPPY_CONNECTOR(config, "fdc:0", swtpc09_uniflex_floppies, "8dsdd", swtpc09_state::floppy_uniflex_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", swtpc09_uniflex_floppies, "8dsdd", swtpc09_state::floppy_uniflex_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:2", swtpc09_uniflex_floppies, "8dsdd", swtpc09_state::floppy_uniflex_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:3", swtpc09_uniflex_floppies, "8dsdd", swtpc09_state::floppy_uniflex_formats).enable_sound(true);

	m_fdc->intrq_wr_callback().set(FUNC(swtpc09_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(swtpc09_state::fdc_drq_w));
	m_fdc->sso_wr_callback().set(FUNC(swtpc09_state::fdc_sso_w));

	// Hard disk
	WD1000(config, m_hdc, 5_MHz_XTAL);
	m_hdc->intrq_wr_callback().set(FUNC(swtpc09_state::dmaf3_hdc_intrq_w));
	m_hdc->drq_wr_callback().set(FUNC(swtpc09_state::dmaf3_hdc_drq_w));
	HARDDISK(config, "hdc:0", 0);
	HARDDISK(config, "hdc:1", 0);
	HARDDISK(config, "hdc:2", 0);
	HARDDISK(config, "hdc:3", 0);

	via6522_device &via(MOS6522(config, "via", 4_MHz_XTAL / 4));
	via.readpa_handler().set(FUNC(swtpc09_state::dmaf3_via_read_porta));
	via.readpb_handler().set(FUNC(swtpc09_state::dmaf3_via_read_portb));
	via.writepa_handler().set(FUNC(swtpc09_state::dmaf3_via_write_porta));
	via.writepb_handler().set(FUNC(swtpc09_state::dmaf3_via_write_portb));
	//via.ca1_handler().set(FUNC(swtpc09_state::dmaf3_via_write_ca1));
	via.irq_handler().set(FUNC(swtpc09_state::dmaf3_via_irq));

	INPUT_MERGER_ALL_HIGH(config, "via_cb2").output_handler().set(m_via, FUNC(via6522_device::write_cb2));

}

/* MPU09, MPID, MPS2, DC5, MPT */
void swtpc09_state::swtpc09o(machine_config &config)
{
	swtpc09_base(config);
	m_bankdev->set_addrmap(AS_PROGRAM, &swtpc09_state::os9_mem);

	subdevice<ss50_interface_port_device>("io1")->set_default_option("dc5");
	subdevice<ss50_interface_port_device>("io1")->set_option_device_input_defaults("dc5", DEVICE_INPUT_DEFAULTS_NAME(dc5));
	subdevice<ss50_interface_port_device>("io4")->set_default_option("mpt");
}


/* ROM definition */
ROM_START( swtpc09 )
	ROM_REGION( 0x100000, "bankdev", 0 )
	ROM_SYSTEM_BIOS(0, "sbug19", "S-BUG 1.9")
	ROMX_LOAD("sbugh1-9.bin", 0xff800, 0x0800, CRC(4a23c2d0) SHA1(da52ce76ac8b848d6c9580bd5ada4864eeb21e58), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "sbug18", "S-BUG 1.8")
	ROMX_LOAD("sbugh1-8.bin", 0xff800, 0x0800, CRC(10a045a7) SHA1(de547b77653951c7424a069520d52c5b0432e98d), ROM_BIOS(1))
ROM_END

ROM_START( swtpc09i )
	ROM_REGION( 0x100000, "bankdev", 0 )
	ROM_LOAD ( "hd-rom.bin", 0xfe800, 0x0800, CRC(b898b4d7) SHA1(2806633eda7da4e9a243fc534f15526ee928b6bc) )
	ROM_LOAD ( "sbugh1-8.bin", 0xff800, 0x0800, CRC(10a045a7) SHA1(de547b77653951c7424a069520d52c5b0432e98d) )
ROM_END

ROM_START( swtpc09u )
	ROM_REGION( 0x100000, "bankdev", 0 )
	ROM_LOAD ( "unibug.bin", 0xff800, 0x00800, CRC(92e1cbf2) SHA1(db00f17ee9accdbfa1775fe0162d3556159b8e70) )
ROM_END

ROM_START( swtpc09d3 )
	ROM_REGION( 0x100000, "bankdev", 0 )
	ROM_LOAD ( "uos3.bin", 0xff800, 0x00800, CRC(e95eb3e0) SHA1(3e971d3b7e143bc87e4b506e18a8c928c089c25a) )
ROM_END

ROM_START( swtpc09o )
	ROM_REGION(0x100000, "bankdev", 0)
	ROM_SYSTEM_BIOS(0, "os9l1v12", "OS9 Level 1 version 1.2")
	ROMX_LOAD("os9l1v12.bin", 0x0e400, 0x01c00, CRC(f2ee7523) SHA1(4e47f672c43ee2dbb5f332feb276d2c109f08e78), ROM_BIOS(0))
ROM_END

/* Driver */

//    YEAR  NAME       PARENT   COMPAT  MACHINE    INPUT    CLASS          INIT            COMPANY  FULLNAME                    FLAGS
COMP( 1980, swtpc09,   0,       0,      swtpc09,   swtpc09, swtpc09_state, init_swtpc09,   "SWTPC", "swtpc S/09 Sbug",          MACHINE_NO_SOUND_HW )
COMP( 1980, swtpc09i,  swtpc09, 0,      swtpc09i,  swtpc09, swtpc09_state, init_swtpc09i,  "SWTPC", "swtpc S/09 Sbug + piaide", MACHINE_NO_SOUND_HW )
COMP( 1980, swtpc09u,  swtpc09, 0,      swtpc09u,  swtpc09, swtpc09_state, init_swtpc09u,  "SWTPC", "swtpc S/09 UNIBug + DMAF2", MACHINE_NO_SOUND_HW )
COMP( 1980, swtpc09d3, swtpc09, 0,      swtpc09d3, swtpc09, swtpc09_state, init_swtpc09d3, "SWTPC", "swtpc S/09 UNIBug + DMAF3", MACHINE_NO_SOUND_HW )
COMP( 1980, swtpc09o,  swtpc09, 0,      swtpc09o,  swtpc09, swtpc09_state, init_swtpc09o,  "SWTPC", "swtpc S/09 OS9",           MACHINE_NO_SOUND_HW )



swtpc8212.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:68bit
/***************************************************************************

SWTPC 8212 Video Terminal

Front end interfacing the terminal device to a MAME RS232 port.

****************************************************************************/

#include "emu.h"
#include "machine/swtpc8212.h"
#include "bus/rs232/rs232.h"


namespace {

class swtpc8212_state : public driver_device
{
public:
	swtpc8212_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag)
		, m_swtpc8212(*this, "swtpc8212")
		, m_rs232(*this, "rs232")
	{ }

	void swtpc8212(machine_config &config);

private:
	required_device<swtpc8212_device> m_swtpc8212;
	required_device<rs232_port_device> m_rs232;
};


void swtpc8212_state::swtpc8212(machine_config &config)
{
	SWTPC8212(config, m_swtpc8212, 0);

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->dcd_handler().set(m_swtpc8212, FUNC(swtpc8212_device::rs232_conn_dcd_w));
	m_rs232->dsr_handler().set(m_swtpc8212, FUNC(swtpc8212_device::rs232_conn_dsr_w));
	m_rs232->ri_handler().set(m_swtpc8212, FUNC(swtpc8212_device::rs232_conn_ri_w));
	m_rs232->cts_handler().set(m_swtpc8212, FUNC(swtpc8212_device::rs232_conn_cts_w));
	m_rs232->rxd_handler().set(m_swtpc8212, FUNC(swtpc8212_device::rs232_conn_rxd_w));

	m_swtpc8212->rs232_conn_txd_handler().set(m_rs232, FUNC(rs232_port_device::write_txd));
	m_swtpc8212->rs232_conn_dtr_handler().set(m_rs232, FUNC(rs232_port_device::write_dtr));
	m_swtpc8212->rs232_conn_rts_handler().set(m_rs232, FUNC(rs232_port_device::write_rts));
}


ROM_START(swtpc8212)
ROM_END

} // anonymous namespace


//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY  FULLNAME       FLAGS
COMP(1980, swtpc8212, 0, 0, swtpc8212, 0, swtpc8212_state, empty_init, "Southwest Technical Products", "SWTPC 8212 Video Terminal", 0 )



swyft.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Jonathan Gevaryahu
/***************************************************************************

    IAI Swyft Model P0001
    Copyright (C) 2009-2013 Miodrag Milanovic and Jonathan Gevaryahu AKA Lord Nightmare
    With information and help from John "Sandy" Bumgarner, Dwight Elvey,
    Charles Springer, Terry Holmes, Jonathan Sand, Aza Raskin and others.

    See cat.c for the machine which the swyft later became.

    This driver is dedicated in memory of Jef Raskin, Dave Boulton, and John "Sandy" Bumgarner (1941-2016)

IAI detailed credits:
Scott Kim - responsible for fonts on swyft and cat
Ralph Voorhees - Model construction and mockups (swyft 'flat cat')

Cat HLSL stuff:
*scanlines:
the cat has somewhat visible and fairly close scanlines with very little fuzziness
try hlsl options:
hlsl_prescale_x           4
hlsl_prescale_y           4
scanline_alpha            0.3
scanline_size             1.0
scanline_height           0.7
scanline_bright_scale     1.0
scanline_bright_offset    0.6
*phosphor persistence of the original cat CRT is VERY LONG and fades to a greenish-yellow color, though the main color itself is white
try hlsl option:
phosphor_life             0.93,0.95,0.87
which is fairly close but may actually be too SHORT compared to the real thing.


Swyft versions:
There are at least 4 variants of machines called 'swyft':
* The earliest desktop units which use plexi or rubber-tooled case and an
  angled monitor; about a dozen were made and at least two of clear plexi.
  These are sometimes called "wrinkled" swyfts. 5.25" drive, they may be able
  to read Apple2 Swyftware/Swyftdisk and Swyftcard-created disks.
  It is possible no prototypes of this type got beyond the 'runs forth console only' stage.
  http://archive.computerhistory.org/resources/access/physical-object/2011/09/102746929.01.01.lg.JPG
  http://www.digibarn.com/collections/systems/swyft/Swyft-No2-05-1271.jpg
  http://www.digibarn.com/friends/jef-raskin/slides/iai/A%20-687%20SWYFTPRO.JPG
* The early "flat cat" or "roadkill" portable LCD screen units with a white
  case and just a keyboard. Model SP0001
  http://www.digibarn.com/collections/systems/swyft/Image82.jpg
* The later "ur-cat" desktop units which use a machine tooled case and look
  more or less like the canon cat. Around 100-200 were made. 3.5" drive.
  These have a fully functional EDDE editor as the cat does, and can even compile
  forth programs.
  (the 'swyft' driver is based on one of these)
* The very late portable LCD units with a dark grey case and a row of hotkey
  buttons below the screen. Not dumped yet. At least one functional prototype exists.
  At least one plastic mockup exists with no innards.
  http://www.digibarn.com/collections/systems/swyft/swyft.jpg

IAI Swyft:
Board name: 950-0001C
"INFORMATION APPLIANCE INC. COPYRIGHT 1985"
 _________________|||||||||___________________________________________________________________________________
|                     J8               [=======J3=======]  ____                              Trans-           |
==                                                        /    \  (E2)                       former           |
==Jx                   74LS107   J5                       |PB1 |        uA339     MC3403                 -----|
|                                          7407           \____/                                         J7   =
| Y1       "TIMING B" 74LS132    74LS175                                                                 -----|
|                                                  ____________                            4N37  Relay   -----|
| TMS4256   74LS161  "DECODE E" "DISK 3.5C"       |            |                                         J6   =
|                                                 |  MC6850P   |                                         -----|
| TMS4256   74LS166   74HCT259   74LS299          '------------'        MC3403    MC3403                 _____|
|                      ___________________     ___________________             ||                       |     =
| TMS4256   74LS373   |                   |   |                   |            J2                       | J1  =
|                     |   MC68008P8       |   |       R6522P      |            ||              I   P R  |     =
| TMS4256   74F153    '-------------------'   '-------------------'     MN4053 || MN4053       N   R E  | B   =
|                                    (E1)                                                      D   O S  | R   =
| TMS4256   74F153    74HCT08     __________   ___________________      MC14412   DS1489       U E T I  | E   =
|                                |          | |                   | ||                         C S E S  | A   =
| TMS4256   74F153    74HC4040E  | AM27256  | |       R6522P      | ||                         T D C T  | K   =
|                                '----------' '-------------------' ||  INFORMATION            O   T O  | O   =
| TMS4256   74F153    "VIDEO 2B" .----------.                       J4  APPLIANCE INC.         R   I R  | U   =
|                                | AM27256  |   74HC02     74HC374  ||  Copyright 1985         S   O S  | T   =
| TMS4256   74F153    74LS393  B1|__________|                       ||  UM95089  Y2                N    |     =
|_____________________________[________J9___]__________________________________________________D13______|_____=

*Devices of interest:
J1: breakout of joystick, serial/rs232, hex-keypad, parallel port, and forth switch (and maybe cassette?) pins
    DIL 60 pin 2-row right-angle rectangular connector with metal shield contact;
    not all 60 pins are populated in the connector, only pins 1-6, 8, 13-15, 17-18, 23-30, 35-60 are present
    (traced partly by dwight)
J2: unpopulated 8-pin sip header, serial/rs232-related?
    (vcc ? ? ? ? ? ? gnd) (random guess: txd, rxd, rts, cts, dsr, dtr, and one pin could be cd/ri though the modem circuit may do that separately?)
J3: Floppy Connector
    (standard DIL 34 pin 2-row rectangular connector for mini-shugart/pc floppy cable; pin 2 IS connected somewhere and ?probably? is used for /DISKCHANGE like on an Amiga, with pin 34 being /TRUEREADY?)
    (as opposed to normal ibm pc 3.5" drives where pin 2 is unconnected or is /DENSITY *input to drive*, and pin 34 is /DISKCHANGE)
J4: 18-pin sip header for keyboard ribbon cable; bottom edge of board is pin 1
    Pins:
    1: GND through 220k resistor r78
    2: ? phone hook related? anode of diode d7; one of the pins of relay k2; topmost (boardwise) pin of transistor Q10
    3: 74HCT34 pin

J5: locking-tab-type "CONN HEADER VERT 4POS .100 TIN" connector for supplying power
    through a small cable with a berg connector at the other end, to the floppy drive
    (5v gnd gnd 12v)
J6: Phone connector, rj11 jack
J7: Line connector, rj11 jack
J8: 9-pin Video out/power in connector "CONN RECEPT 6POS .156 R/A PCB" plus "CONN RECEPT 3POS .156 R/A PCB" acting as one 9-pin connector
    (NC ? ? ? NC NC ? 12v 5v) (video, vsync, hsync and case/video-gnd are likely here)
    (the video pinout of the cat is: (Video Vsync Hsync SyncGnd PwrGnd PwrGnd +5v(VCC) +12v(VDD) -12v(VEE)) which is not the same as the swyft.
J9: unpopulated DIL 40-pin straight connector for a ROM debug/expansion/RAM-shadow daughterboard
    the pins after pin 12 connect to that of the ROM-LO 27256 pinout counting pins 1,28,2,27,3,26,etc
    the ROM-HI rom has a different /HICE pin which is not connected to this connector
    /LOCE is a15
    /HICE is !a15
    /ROM_OE comes from pin 14 of DECODE_E pal, and is shorted to /ROM_OE' by the cuttable jumper B1 which is not cut
    /ROM_OE' goes to the two EPROMS
    DECODE_18 is DECODE_E pal pin 18
    pin 1 (GND) is in the lower left and the pins count low-high then to the right
    (gnd N/C   E_CLK     R/W    /ROM_OE a17 vcc a14 a13 a8 a9 a11 /ROM_OE' a10 a15 d7 d6 d5 d4 d3 )
    (GND /IPL1 DECODE_18 /RESET gnd     a16 vcc a12 a7  a6 a5 a4  a3       a2  a1  a0 d0 d1 d2 gnd)
Jx: 4 pin on top side, 6 pin on bottom side edge ?debug? connector (doesn't have a Jx number)
    (trace me!)
B1: a cuttable trace on the pcb. Not cut, affects one of the pins on the unpopulated J9 connector only.
E1: jumper, unknown purpose, not set
E2: jumper, unknown purpose, not set
D13: LED
R6522P (upper): parallel port via
R6522P (lower): keyboard via
UM95089: DTMF Tone generator chip (if you read the datasheet this is technically ROM based!)
Y1: 15.8976Mhz, main clock?
Y2: 3.579545Mhz, used by the DTMF generator chip UM95089 (connects to pins 7 and 8 of it)
TMS4256-15NL - 262144 x 1 DRAM
PB1 - piezo speaker

*Pals:
"TIMING B" - AMPAL16R4APC (marked on silkscreen "TIMING PAL")
"DECODE E" - AMPAL16L8PC (marked on silkscreen "DECODE PAL")
"VIDEO 2B" - AMPAL16R4PC (marked on silkscreen "VIDEO PAL")
"DISK 3.5C" - AMPAL16R4PC (marked on silkscreen "DISK PAL")

*Deviations from silkscreen:
4N37 (marked on silkscreen "4N35")
74F153 (marked on silkscreen "74ALS153")
74HCT259 is socketed, possibly intended that the rom expansion daughterboard will have a ribbon cable fit in its socket?


ToDo:
* Swyft
- Figure out the keyboard (interrupts are involved? or maybe an NMI on a
  timer/vblank? It is possible this uses a similar 'keyboard read int'
  to what the cat does)
- get the keyboard scanning actually working; the VIAs are going nuts right now.
- Beeper (on one of the vias?)
- vblank/hblank stuff
- Get the 6850 ACIA working for communications
- Floppy (probably similar to the Cat)
- Centronics port (attached to one of the VIAs)
- Joystick port (also likely on a via)
- Keypad? (also likely on a via done as a grid scan?)
- Forth button (on the port on the back; keep in mind shift-usefront-space ALWAYS enables forth on a swyft)
- Multple undumped firmware revisions exist (330 and 331 are dumped)

// 74ls107 @ u18 pin 1 is 68008 /BERR pin

// mc6850 @ u33 pin 2 (RX_DATA) is
// mc6850 @ u33 pin 3 (RX_CLK) is 6522 @ u35 pin 17 (PB7)
// mc6850 @ u33 pin 4 (TX_CLK) is 6522 @ u35 pin 17 (PB7)
// mc6850 @ u33 pin 5 (/RTS) is
// mc6850 @ u33 pin 6 (TX_DATA) is
// mc6850 @ u33 pin 7 (/IRQ) is 68008 /IPL1 pin 41
// mc6850 @ u33 pin 8 (CS0) is 68008 A12 pin 10
// mc6850 @ u33 pin 9 (CS2) is DECODE E pin 18
// mc6850 @ u33 pin 10 (CS1) is 68008 /BERR pin 40
// mc6850 @ u33 pin 11 (RS) is 68008 A0 pin 46
// mc6850 @ u33 pin 13 (R/W) is 68008 R/W pin 30
// mc6850 @ u33 pin 14 (E) is 68008 E pin 38
// mc6850 @ u33 pin 15-22 (D7-D0) are 68008 D7 to D0 pins 20-27
// mc6850 @ u33 pin 23 (/DCD) is 74hc02 @ u35 pin 1
// mc6850 @ u33 pin 24 (/CTS) is N/C?

// 6522 @ u34:
// pin 2 (PA0) :
// pin 3 (PA1) :
// pin 4 (PA2) :
// pin 5 (PA3) :
// pin 6 (PA4) :
// pin 7 (PA5) :
// pin 8 (PA6) :
// pin 9 (PA7) :
// pin 10 (PB0) :
// pin 11 (PB1) :
// pin 12 (PB2) :
// pin 13 (PB3) :
// pin 14 (PB4) :
// pin 15 (PB5) :
// pin 16 (PB6) :
// pin 17 (PB7) :
// pin 18 (CB1) : ?from/to? Floppy connector j3 pin 8
// pin 19 (CB2) : ?from/to? 6522 @ u35 pin 16 (PB6)
// pin 21 (/IRQ) : out to 68008 /IPL1 pin 41
// pin 22 (R/W) : in from 68008 R/W pin 30
// pin 23 (/CS2) : in from DECODE E pin 18
// pin 24 (CS1) : in from 68008 A13 pin 11
// pin 25 (Phi2) : in from 68008 E pin 38
// pins 26-33 : in/out from/to 68008 D7 to D0 pins 20-27
// pin 34 (/RES) : in from 68008 /RESET pin 37 AND 68008 /HALT pin 36
// pins 35-38 (RS3-RS0) are 68008 A9-A6 pins 7-4
// pin 39 (CA2) is through inductor L11 and resistor r128 to peripheral connector pin 35 <end minus 26>
// pin 40 (CA1) is through inductor L30 and resistor r138 to peripheral connector pin 53 <end minus 8>

// 6522 @ u35
// pin 2 (PA0) :
// pin 3 (PA1) :
// pin 4 (PA2) :
// pin 5 (PA3) :
// pin 6 (PA4) :
// pin 7 (PA5) :
// pin 8 (PA6) :
// pin 9 (PA7) :
// pin 10 (PB0) :
// pin 11 (PB1) : in from 74hc02 @ u36 pin 4
// pin 12 (PB2) :
// pin 13 (PB3) :
// pin 14 (PB4) :
// pin 15 (PB5) :
// pin 16 (PB6) : 6522 @ u34 pin 19 (CB2)
// pin 17 (PB7) : mc6850 @ u33 pins 3 and 4 (RX_CLK, TX_CLK)
// pin 18 (CB1) : ds1489an @ u45 pin 11
// pin 19 (CB2) : mn4053b @ u40 pin 11 and mc14412 @ u41 pin 10
// pin 21 (/IRQ) : out to 68008 /IPL1 pin 41
// pin 22 (R/W) : in from 68008 R/W pin 30
// pin 23 (/CS2) : in from DECODE E pin 18
// pin 24 (CS1) : in from 68008 A14 pin 12
// pin 25 (Phi2) : in from 68008 E pin 38
// pins 26-33 : in/out from/to 68008 D7 to D0 pins 20-27
// pin 34 (/RES) : in from 68008 /RESET pin 37 AND 68008 /HALT pin 36
// pins 35-38 (RS3-RS0) : in from 68008 A9-A6 pins 7-4
// pin 39 (CA2) : out to 74HCT34 pin 11 (CLK) (keyboard column latch)
// pin 40 (CA1) : out? to? ds1489an @ u45 pin 8

// 74hc02 @ u36:
// pin 1 (Y1) : out to mc6850 @ u33 pin 23 /DCD
// pin 2 (A1) : in from (2 places: resistor R58 to ua339 @ u38 pin 4 (In1-)) <where does this actually come from? modem offhook?>
// pin 3 (B1) : in from mn4053b @ u40  pin 10 <probably from rs232>
// pin 4 (Y2) : out to 6522 @u35 pin 11
// pin 5 (A2) : in from 4N37 @ u48 pin 5 (output side emitter pin) (tied via R189 to gnd) <ring indicator?>
// pin 6 (B2) : in from 4N37 @ u48 pin 5 (output side emitter pin) (tied via R189 to gnd) <ring indicator?>
// pin 8 (B3) :
// pin 9 (A3) :
// pin 10 (Y3) :
// pin 11 (B4) : in from 68008 A15
// pin 12 (A4) : in from 68008 A15
// pin 13 (Y4) : out to EPROM @ U31 /CE

****************************************************************************/

// Includes
#include "emu.h"
#include "bus/centronics/ctronics.h"
#include "cpu/m68000/m68008.h"
#include "machine/74259.h"
#include "machine/6522via.h"
#include "machine/6850acia.h"
#include "machine/clock.h"
#include "machine/input_merger.h"
#include "sound/spkrdev.h"
#include "emupal.h"
#include "screen.h"


// Defines

#undef DEBUG_GA2OPR_W
#undef DEBUG_VIDEO_CONTROL_W

#undef DEBUG_FLOPPY_CONTROL_W
#undef DEBUG_FLOPPY_CONTROL_R
#undef DEBUG_FLOPPY_DATA_W
#undef DEBUG_FLOPPY_DATA_R
#undef DEBUG_FLOPPY_STATUS_R

#undef DEBUG_PRINTER_DATA_W
#undef DEBUG_PRINTER_CONTROL_W

#undef DEBUG_MODEM_R
#undef DEBUG_MODEM_W

#undef DEBUG_DUART_OUTPUT_LINES
// data sent to rs232 port
#undef DEBUG_DUART_TXA
// data sent to modem chip
#undef DEBUG_DUART_TXB
#undef DEBUG_DUART_IRQ_HANDLER
#undef DEBUG_PRN_FF

#undef DEBUG_TEST_W

#define LOG_VIA0 (1U << 1)
#define LOG_VIA1 (1U << 2)

//#define VERBOSE (LOG_VIA0 | LOG_VIA1)
#include "logmacro.h"


namespace {

class swyft_state : public driver_device
{
public:
	swyft_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ctx(*this, "ctx"),
		m_ctx_data_out(*this, "ctx_data_out"),
		m_acia6850(*this, "acia6850"),
		m_via(*this, "via6522_%u", 0U),
		m_bitlatch(*this, "bitlatch"),
		m_speaker(*this, "speaker"),
		m_p_swyft_videoram(*this, "p_swyft_vram")
		/*m_y0(*this, "Y0"),
		m_y1(*this, "Y1"),
		m_y2(*this, "Y2"),
		m_y3(*this, "Y3"),
		m_y4(*this, "Y4"),
		m_y5(*this, "Y5"),
		m_y6(*this, "Y6"),
		m_y7(*this, "Y7")*/
	{ }

	void swyft(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

private:
	required_device<m68008_device> m_maincpu;
	optional_device<centronics_device> m_ctx;
	optional_device<output_latch_device> m_ctx_data_out;
	required_device<acia6850_device> m_acia6850;
	required_device_array<via6522_device, 2> m_via;
	required_device<hct259_device> m_bitlatch;
	optional_device<speaker_sound_device> m_speaker;
	required_shared_ptr<uint8_t> m_p_swyft_videoram;
	/*optional_ioport m_y0;
	optional_ioport m_y1;
	optional_ioport m_y2;
	optional_ioport m_y3;
	optional_ioport m_y4;
	optional_ioport m_y5;
	optional_ioport m_y6;
	optional_ioport m_y7;*/

	uint32_t screen_update_swyft(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	uint8_t bitlatch_r(offs_t offset);
	template<int B> void bitlatch_q_w(int state);

	uint8_t swyft_via0_r(offs_t offset);
	void swyft_via0_w(offs_t offset, uint8_t data);
	uint8_t via0_pa_r();
	void via0_pa_w(uint8_t data);
	void via0_ca2_w(int state);
	uint8_t via0_pb_r();
	void via0_pb_w(uint8_t data);
	void via0_cb1_w(int state);
	void via0_cb2_w(int state);

	uint8_t swyft_via1_r(offs_t offset);
	void swyft_via1_w(offs_t offset, uint8_t data);
	uint8_t via1_pa_r();
	void via1_pa_w(uint8_t data);
	void via1_ca2_w(int state);
	uint8_t via1_pb_r();
	void via1_pb_w(uint8_t data);
	void via1_cb1_w(int state);
	void via1_cb2_w(int state);

	void write_acia_clock(int state);

	void swyft_mem(address_map &map) ATTR_COLD;
};


static INPUT_PORTS_START( swyft )
// insert dwight and sandy's swyft keyboard map here once we figure out the byte line order
	PORT_START("Y0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR(0xA2)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')

	PORT_START("Y1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('n') PORT_CHAR('B')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')

	PORT_START("Y2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED) // totally unused
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')

	PORT_START("Y3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left USE FRONT") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED) // totally unused

	PORT_START("Y4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right USE FRONT") PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_F2) // intl only: latin diaresis and latin !; norway, danish and finnish * and '; others
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')

	PORT_START("Y5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(0xBD) PORT_CHAR(0xBC) //PORT_CHAR('}') PORT_CHAR('{')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')

	PORT_START("Y6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_F1) // intl only: latin inv ? and inv !; norway and danish ! and |; finnish <>; others
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left LEAP") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('[')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED) // totally unused

	PORT_START("Y7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_RSHIFT) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right Leap") PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Page") PORT_CODE(KEYCODE_PGUP) PORT_CODE(KEYCODE_PGDN)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift Lock") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Erase") PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED) // totally unused
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("UNDO") PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR(0xB1) PORT_CHAR(0xB0) // PORT_CHAR('\\') PORT_CHAR('~')
INPUT_PORTS_END


/* Swyft rom and ram notes:
rom:
**Vectors:
0x0000-0x0003: SP boot vector
0x0004-0x0007: PC boot vector
**unknown:
0x0009-0x00BF: ? table
0x00C0-0x01DF: ? table
0x01E0-0x02DF: ? table (may be part of next table)
0x02E0-0x03DF: ? table
0x03E0-0x0B3F: int16-packed jump table (expanded to int32s at ram at 0x46000-0x46EC0 on boot)
0x0B40-0x0E83: ? function index tables?
0x0E84-0x1544: binary code (purpose?)
0x1545-0x24CF: ?
**Fonts:
0x24D0-0x254F: ? (likely font 1 width lookup table)
0x2550-0x2BCF: Font 1 data
0x2BD0-0x2C4F: ? (likely font 2 width lookup table)
0x2C50-0x32CF: Font 2 data
**unknown?:
0x32D0-0x360F: String data (and control codes?)
0x3610-0x364F: ? fill (0x03 0xe8)
0x3650-0x369F: ? fill (0x03 0x20)
0x36A0-0x384d: ? forth code?
0x384e-0x385d: Lookup table for phone keypad
0x385e-...: ?
...-0xC951: ?
0xC952: boot vector
0xC952-0xCAAE: binary code (purpose?)
    0xCD26-0xCD3B: ?init forth bytecode?
0xCD3C-0xCEBA: 0xFF fill (unused?)
0xCEEB-0xFFFE: Forth dictionaries for compiling, with <word> then <3 bytes> afterward? (or before it? most likely afterward)

ram: (system dram ranges from 0x40000-0x7FFFF)
0x40000-0x425CF - the screen display ram
(?0x425D0-0x44BA0 - ?unknown (maybe screen ram page 2?))
0x44DC6 - SP vector
0x46000-0x46EC0 - jump tables to instructions for ? (each forth word?)


on boot:
copy/expand packed rom short words 0x3E0-0xB3F to long words at 0x46000-0x46EC0
copy 0x24f longwords of zero beyond that up to 0x47800
CD26->A5 <?pointer to init stream function?>
44DC6->A7 <reset SP... why it does this twice, once by the vector and once here, i'm gonna guess has to do with running the code in a debugger or on a development daughterboard like the cat had, where the 68008 wouldn't get explicitly reset>
44F2A->A6 <?pointer to work ram space?>
EA2->A4 <?function>
E94->A3 <?function>
EAE->A2 <?function>
41800->D7 <?forth? opcode index base; the '1800' portion gets the opcode type added to it then is multiplied by 4 to produce the jump table offset within the 0x46000-0x46EC0 range>
46e3c->D4 <?pointer to more work ram space?>
CD22->D5 <?pointer to another function?>
write 0xFFFF to d0004.l
jump to A4(EA2)

read first stream byte (which is 0x03) from address pointed to by A5 (which is CD26), inc A5, OR the opcode (0x03) to D7
 (Note: if the forth opcodes are in order in the dictionary, then 0x03 is "!char" which is used to read a char from an arbitrary address)
copy D7 to A0
Add A0 low word to itself
Add A0 low word to itself again
move the long word from address pointed to by A0 (i.e. the specific opcode's area at the 46xxx part of ram) to A1
Jump to A1(11A4)

11A4: move 41b00 to D0 (select an opcode "page" 1bxx)
jump to 118E

118E: read next stream byte (in this case, 0x8E) from address pointed to by A5 (which is CD27), inc A5, OR the opcode (0x8e) to D7
add to 41b00 in d0, for 41b8E
Add A0 low word to itself
Add A0 low word to itself again
move the long word from address pointed to by A0 (i.e. the specific opcode's area at the 46xxx part of ram) to A1
Jump to A1(CD06)

CD06: jump to A3 (E94)

E94: subtract D5 from A5 (cd28 - cd22 = 0x0006)
write 6 to address @A5(44f28) and decrement A5
write D4(46e3c) to address @a6(44f26) and decrement a5
lea ($2, A1), A5 - i.e. increment A1 by 2, and write that to A5, so write CD06+2=CD08 to A5
A1->D5
A0->D4
read next stream byte (in this case, 0x03) from address pointed to by A5 (which is CD08), inc A5, OR the opcode (0x03) to D7

*/

/* Swyft Memory map, based on watching the infoapp roms do their thing:
68k address map:
(a23,a22,a21,a20 lines don't exist on the 68008 so are considered unconnected)
a23 a22 a21 a20 a19 a18 a17 a16 a15 a14 a13 a12 a11 a10 a9  a8  a7  a6  a5  a4  a3  a2  a1  a0
x   x   x   x   0   0   ?   ?   0   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *        R   ROM-LO (/LOCE is 0, /HICE is 1)
x   x   x   x   0   0   ?   ?   1   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *        R   ROM-HI (/LOCE is 1, /HICE is 0)
x   x   x   x   0   1   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   a        RW  RAM
x   x   x   x   1   1  ?0? ?1?  ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   *   *   *   *        R   ? status of something? floppy?
x   x   x   x   1   1  ?1? ?0?  ?   0   0   1   x   x   x   x   x   x   x   x   x   x   x   *        RW  6850 acia @U33, gets 0x55 steadystate and 0x57 written to it to reset it
x   x   x   x   1   1  ?1? ?0?  ?   0   1   0   x   x   *   *   *   *   x   x   x   x   x   x        RW  Parallel VIA 0 @ U34
x   x   x   x   1   1  ?1? ?0?  ?   1   0   0   x   x   *   *   *   *   x   x   x   x   x   x        RW  Keyboard VIA 1 @ U35
              ^               ^               ^               ^               ^

*/

void swyft_state::swyft_mem(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xfffff);
	map(0x000000, 0x00ffff).rom(); // 64 KB ROM
	map(0x040000, 0x07ffff).ram().share("p_swyft_vram"); // 256 KB RAM
	map(0x0d0000, 0x0d000f).r(FUNC(swyft_state::bitlatch_r)).w("bitlatch", FUNC(hct259_device::write_a0));
	map(0x0e1000, 0x0e1001).rw(m_acia6850, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0x0e2000, 0x0e2fff).rw(FUNC(swyft_state::swyft_via0_r), FUNC(swyft_state::swyft_via0_w)); // io area with selector on a9 a8 a7 a6?
	map(0x0e4000, 0x0e4fff).rw(FUNC(swyft_state::swyft_via1_r), FUNC(swyft_state::swyft_via1_w));
}

void swyft_state::machine_start()
{
	for (auto &via : m_via)
	{
		via->write_ca1(1);
		via->write_ca2(1);
		via->write_cb1(1);
		via->write_cb2(1);
	}
}

void swyft_state::machine_reset()
{
}

void swyft_state::video_start()
{
}

uint32_t swyft_state::screen_update_swyft(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	int addr = 0;
	for (int y = 0; y < 242; y++)
	{
		int horpos = 0;
		for (int x = 0; x < 40; x++)
		{
			uint16_t code = m_p_swyft_videoram[addr++];
			for (int b = 7; b >= 0; b--)
			{
				bitmap.pix(y, horpos++) = (code >> b) & 0x01;
			}
		}
	}
	return 0;
}

template<int B>
void swyft_state::bitlatch_q_w(int state)
{
	logerror("74HCT259: Q%d %s\n", B, state ? "on" : "off");
}

uint8_t swyft_state::bitlatch_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		m_bitlatch->write_bit(offset >> 1, BIT(offset, 0));
	return 0xff;
}


// if bit is 1 enable: (obviously don't set more than one bit or you get bus contention!)
//                                           acia
//                                       via0
//                                    via1
// x   x   x   x   1   1  ?1? ?0?  ?   ^   ^   ^   ?   ?   *   *   *   *  ?*?  ?   ?   ?   ?   ?
//                                                         ^   ^   ^   ^  <- these four bits address the VIA registers? is this correct?
static const char *const swyft_via_regnames[] = { "0: ORB/IRB", "1: ORA/IRA", "2: DDRB", "3: DDRA", "4: T1C-L", "5: T1C-H", "6: T1L-L", "7: T1L-H", "8: T2C-L", "9: T2C-H", "A: SR", "B: ACR", "C: PCR", "D: IFR", "E: IER", "F: ORA/IRA*" };

uint8_t swyft_state::swyft_via0_r(offs_t offset)
{
	if (offset&0x000C3F) logerror("VIA0: read from invalid offset in 68k space: %06X!\n", offset);
	uint8_t data = m_via[0]->read((offset>>6)&0xF);
	LOGMASKED(LOG_VIA0, "VIA0 register %s read by cpu: returning %02x\n", swyft_via_regnames[(offset>>5)&0xF], data);
	return data;
}

void swyft_state::swyft_via0_w(offs_t offset, uint8_t data)
{
	LOGMASKED(LOG_VIA0, "VIA0 register %s written by cpu with data %02x\n", swyft_via_regnames[(offset>>5)&0xF], data);
	if (offset&0x000C3F) logerror("VIA0: write to invalid offset in 68k space: %06X, data: %02X!\n", offset, data);
	m_via[1]->write((offset>>6)&0xF, data);
}

uint8_t swyft_state::swyft_via1_r(offs_t offset)
{
	if (offset&0x000C3F) logerror("VIA1: read from invalid offset in 68k space: %06X!\n", offset);
	uint8_t data = m_via[1]->read((offset>>6)&0xF);
	LOGMASKED(LOG_VIA1, "VIA1 register %s read by cpu: returning %02x\n", swyft_via_regnames[(offset>>5)&0xF], data);
	return data;
}

void swyft_state::swyft_via1_w(offs_t offset, uint8_t data)
{
	LOGMASKED(LOG_VIA1, "VIA1 register %s written by cpu with data %02x\n", swyft_via_regnames[(offset>>5)&0xF], data);
	if (offset&0x000C3F) logerror("VIA1: write to invalid offset in 68k space: %06X, data: %02X!\n", offset, data);
	m_via[0]->write((offset>>6)&0xF, data);
}

// first via
uint8_t swyft_state::via0_pa_r()
{
	LOGMASKED(LOG_VIA0, "VIA0: Port A read!\n");
	return 0xFF;
}

void swyft_state::via0_pa_w(uint8_t data)
{
	LOGMASKED(LOG_VIA0, "VIA0: Port A written with data of 0x%02x!\n", data);
}

void swyft_state::via0_ca2_w(int state)
{
	LOGMASKED(LOG_VIA0, "VIA0: CA2 written with %d!\n", state);
}

uint8_t swyft_state::via0_pb_r()
{
	LOGMASKED(LOG_VIA0, "VIA0: Port B read!\n");
	return 0xFF;
}

void swyft_state::via0_pb_w(uint8_t data)
{
	LOGMASKED(LOG_VIA0, "VIA0: Port B written with data of 0x%02x!\n", data);
}

void swyft_state::via0_cb1_w(int state)
{
	LOGMASKED(LOG_VIA0, "VIA0: CB1 written with %d!\n", state);
}

void swyft_state::via0_cb2_w(int state)
{
	LOGMASKED(LOG_VIA0, "VIA0: CB2 written with %d!\n", state);
}

// second via
uint8_t swyft_state::via1_pa_r()
{
	LOGMASKED(LOG_VIA1, "VIA1: Port A read!\n");
	return 0xFF;
}

void swyft_state::via1_pa_w(uint8_t data)
{
	LOGMASKED(LOG_VIA1, "VIA1: Port A written with data of 0x%02x!\n", data);
}

void swyft_state::via1_ca2_w(int state)
{
	LOGMASKED(LOG_VIA1, "VIA1: CA2 written with %d!\n", state);
}

uint8_t swyft_state::via1_pb_r()
{
	LOGMASKED(LOG_VIA1, "VIA1: Port B read!\n");
	return 0xFF;
}

void swyft_state::via1_pb_w(uint8_t data)
{
	LOGMASKED(LOG_VIA1, "VIA1: Port B written with data of 0x%02x!\n", data);
}

void swyft_state::via1_cb1_w(int state)
{
	LOGMASKED(LOG_VIA1, "VIA1: CB1 written with %d!\n", state);
}

void swyft_state::via1_cb2_w(int state)
{
	LOGMASKED(LOG_VIA1, "VIA1: CB2 written with %d!\n", state);
}

void swyft_state::write_acia_clock(int state)
{
	m_acia6850->write_txc(state);
	m_acia6850->write_rxc(state);
}

void swyft_state::swyft(machine_config &config)
{
	/* basic machine hardware */
	M68008(config, m_maincpu, XTAL(15'897'600)/2); //MC68008P8, Y1=15.8976Mhz, clock GUESSED at Y1 / 2
	m_maincpu->set_addrmap(AS_PROGRAM, &swyft_state::swyft_mem);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(15.8976_MHz_XTAL / 2, 500, 0, 320, 265, 0, 242); // total guess
	screen.set_screen_update(FUNC(swyft_state::screen_update_swyft));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	ACIA6850(config, m_acia6850, 0);
	// acia rx and tx clocks come from one of the VIA pins and are tied together, fix this below? acia e clock comes from 68008
	clock_device &acia_clock(CLOCK(config, "acia_clock", (XTAL(15'897'600)/2)/5)); // out e clock from 68008, ~ 10in clocks per out clock
	acia_clock.signal_handler().set(FUNC(swyft_state::write_acia_clock));

	via6522_device &via0(MOS6522(config, "via6522_0", (XTAL(15'897'600)/2)/5)); // out e clock from 68008
	via0.readpa_handler().set(FUNC(swyft_state::via0_pa_r));
	via0.readpb_handler().set(FUNC(swyft_state::via0_pb_r));
	via0.writepa_handler().set(FUNC(swyft_state::via0_pa_w));
	via0.writepb_handler().set(FUNC(swyft_state::via0_pb_w));
	via0.cb1_handler().set(FUNC(swyft_state::via0_cb1_w));
	via0.ca2_handler().set(FUNC(swyft_state::via0_ca2_w));
	via0.cb2_handler().set(FUNC(swyft_state::via0_cb2_w));
	via0.irq_handler().set("viairq", FUNC(input_merger_device::in_w<0>));

	via6522_device &via1(MOS6522(config, "via6522_1", (XTAL(15'897'600)/2)/5)); // out e clock from 68008
	via1.readpa_handler().set(FUNC(swyft_state::via1_pa_r));
	via1.readpb_handler().set(FUNC(swyft_state::via1_pb_r));
	via1.writepa_handler().set(FUNC(swyft_state::via1_pa_w));
	via1.writepb_handler().set(FUNC(swyft_state::via1_pb_w));
	via1.cb1_handler().set(FUNC(swyft_state::via1_cb1_w));
	via1.ca2_handler().set(FUNC(swyft_state::via1_ca2_w));
	via1.cb2_handler().set(FUNC(swyft_state::via1_cb2_w));
	via1.irq_handler().set("viairq", FUNC(input_merger_device::in_w<1>));

	input_merger_device &viairq(INPUT_MERGER_ANY_HIGH(config, "viairq"));
	viairq.output_handler().set_inputline("maincpu", M68K_IRQ_2);

	HCT259(config, m_bitlatch);
	m_bitlatch->q_out_cb<0>().set(FUNC(swyft_state::bitlatch_q_w<0>));
	m_bitlatch->q_out_cb<1>().set(FUNC(swyft_state::bitlatch_q_w<1>));
	m_bitlatch->q_out_cb<2>().set(FUNC(swyft_state::bitlatch_q_w<2>));
	m_bitlatch->q_out_cb<3>().set(FUNC(swyft_state::bitlatch_q_w<3>));
	m_bitlatch->q_out_cb<4>().set(FUNC(swyft_state::bitlatch_q_w<4>));
	m_bitlatch->q_out_cb<5>().set(FUNC(swyft_state::bitlatch_q_w<5>));
	m_bitlatch->q_out_cb<6>().set(FUNC(swyft_state::bitlatch_q_w<6>));
	m_bitlatch->q_out_cb<7>().set(FUNC(swyft_state::bitlatch_q_w<7>));
}

/* ROM definition */
ROM_START( swyft )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v331", "IAI Swyft Version 331 Firmware")
	ROMX_LOAD( "331-lo.u30", 0x0000, 0x8000, CRC(d6cc2e2f) SHA1(39ff26c18b1cf589fc48793263f280ef3780cc61), ROM_BIOS(0))
	ROMX_LOAD( "331-hi.u31", 0x8000, 0x8000, CRC(4677630a) SHA1(8845d702fa8b8e1a08352f4c59d3076cc2e1307e), ROM_BIOS(0))
	/* this version of the swyft code identifies itself at 0x3FCB as version 330 */
	ROM_SYSTEM_BIOS( 1, "v330", "IAI Swyft Version 330 Firmware")
	ROMX_LOAD( "infoapp.lo.u30", 0x0000, 0x8000, CRC(52c1bd66) SHA1(b3266d72970f9d64d94d405965b694f5dcb23bca), ROM_BIOS(1))
	ROMX_LOAD( "infoapp.hi.u31", 0x8000, 0x8000, CRC(83505015) SHA1(693c914819dd171114a8c408f399b56b470f6be0), ROM_BIOS(1))
	ROM_REGION( 0x4000, "pals", ROMREGION_ERASEFF )
	/* Swyft PALs:
	 * The Swyft has four PALs, whose rough function can be derived from their names:
	 * TIMING - state machine for DRAM refresh/access; handles ras/cas and choosing whether the video out shifter or the 68k is accessing ram. also divides clock
	 * DECODE - address decoder for the 68008
	 * VIDEO - state machine for the video shifter (and vblank/hblank?)
	 * DISK 3.5 - state machine for the floppy drive interface
	 */
	/* U9: Timing AMPAL16R4
	 *
	 * pins:
	 * 111111111000000000
	 * 987654321987654321
	 * ??QQQQ??EIIIIIIIIC
	 * |||||||||||||||||\-< /CK input - 15.8976mhz crystal and transistor oscillator
	 * ||||||||||||||||\--< ?
	 * |||||||||||||||\---< ?
	 * ||||||||||||||\----< ?
	 * |||||||||||||\-----< ?<also input to decode pal pin 1, video pal pin 1, source is ?>
	 * ||||||||||||\------< ?
	 * |||||||||||\-------< ?
	 * ||||||||||\--------< ?
	 * |||||||||\---------< ?
	 * ||||||||\----------< /OE input - shorted to GND
	 * |||||||\-----------? ?
	 * ||||||\------------? ?
	 * |||||\------------Q> /ROM_OE (to both eproms through jumper b1 and optionally j9 connector)
	 * ||||\-------------Q? ?
	 * |||\--------------Q? ?
	 * ||\---------------Q> output to decode pal pin 2
	 * |\----------------->? output? to ram multiplexer 'A' pins
	 * \------------------< ?
	 */
	ROM_LOAD( "timing_b.ampal16r4a.u9.jed", 0x0000, 0xb08, CRC(643e6e83) SHA1(7db167883f9d6cf385ce496d08976dc16fc3e2c3))
	/* U20: Decode AMPAL16L8
	 *
	 * pins:
	 * 111111111000000000
	 * 987654321987654321
	 * O??????OIIIIIIIIII
	 * |||||||||||||||||\-< TIMING PAL pin 5
	 * ||||||||||||||||\--< TIMING PAL pin 17
	 * |||||||||||||||\---< 68008 R/W (pin 30)
	 * ||||||||||||||\----< 68008 /DS (pin 29)
	 * |||||||||||||\-----< 68008 E (pin 38)
	 * ||||||||||||\------< 68008 A19
	 * |||||||||||\-------< 68008 A18
	 * ||||||||||\--------< 68008 A17
	 * |||||||||\---------< 68008 A16
	 * ||||||||\----------< ?
	 * |||||||\-----------> ?
	 * ||||||\------------? 68008 /VPA (pin 39)
	 * |||||\-------------> /ROM_OE (to both eproms through jumper b1 and optionally j9 connector)
	 * ||||\--------------? ?
	 * |||\---------------? ?
	 * ||\----------------? ?
	 * |\-----------------? goes to j9 connector pin 5
	 * \------------------< ?
	 */
	ROM_LOAD( "decode_e.ampal16l8.u20.jed", 0x1000, 0xb08, CRC(0b1dbd76) SHA1(08c144ad7a7bbdd53eefd271b2f6813f8b3b1594))
	ROM_LOAD( "video_2b.ampal16r4.u25.jed", 0x2000, 0xb08, CRC(caf91148) SHA1(3f8ddcb512a1c05395c74ad9a6ba7b87027ce4ec))
	ROM_LOAD( "disk_3.5c.ampal16r4.u28.jed", 0x3000, 0xb08, CRC(fd994d02) SHA1(f910ab16587dd248d63017da1e5b37855e4c1a0c))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY                       FULLNAME  FLAGS
COMP( 1985, swyft, 0,      0,      swyft,   swyft, swyft_state, empty_init, "Information Applicance Inc", "Swyft",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



sx1000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * Casio SX1010 and SX1050.
 *
 * Sources:
 *
 * TODO:
 *   - skeleton only
 */

/*
 * Preliminary parts list for 600-CPU-PCA CPU board:
 *
 * MC68010L10
 * D8237AC-5
 * HD63484-8
 * D8251AFC
 * D72065C
 * D8259AC-2
 * D8251AFC
 * D65021G031    gate array
 * D65030G024    gate array
 * HN62301AP AA1 128k mask ROM
 * HN62301AP AA2 128k mask ROM
 * MB89321A      CMOS Programmable CRT Controller
 * MB4108        ASSP Floppy Disk VFO
 * RP5C15        RTC
 * 48.8MHz XTAL
 *
 * D41256C-15    262144x1 DRAM, x36 == 1M with parity main RAM?
 * MB81464-12    262144x1 DRAM, x16 == 512K video RAM?
 * HM6264ALSP-12 8192-word 8-bit High Speed CMOS Static RAM, x2 == 16K non-volatile RAM?
 */
// WIP: keyboard test at f040ae, if bypassed continues with more interesting tests

#include "emu.h"

#include "cpu/m68000/m68010.h"

// memory
#include "machine/ram.h"
#include "machine/nvram.h"

// various hardware
#include "machine/i8251.h"
#include "machine/pic8259.h"
#include "machine/upd765.h"
#include "machine/am9517a.h"
#include "machine/clock.h"

// video
#include "screen.h"
#include "emupal.h"
#include "video/mc6845.h"
#include "video/hd63484.h"

// busses and connectors
#include "bus/rs232/rs232.h"

#include "debugger.h"

#define VERBOSE 0
#include "logmacro.h"

namespace {

class sx1000_state : public driver_device
{
public:
	sx1000_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_ram(*this, "ram")
		, m_vram(*this, "vram")
		, m_eprom(*this, "eprom")
		, m_pic(*this, "pic")
		, m_screen_crtc(*this, "screen_crtc")
		, m_screen_acrtc(*this, "screen_acrtc")
		, m_palette_acrtc(*this, "palette_acrtc")
		, m_crtc(*this, "crtc")
		, m_acrtc(*this, "acrtc")
		, m_serial1(*this, "i8251_1")
		, m_serial2(*this, "i8251_2")
		, m_fontram(*this, "fontram")
		, m_sw1(*this, "SW1")
		, m_sw2(*this, "SW2")
	{
	}

	void sx1010(machine_config &config);
	void init_common();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void cpu_map(address_map &map) ATTR_COLD;
	void acrtc_map(address_map &map) ATTR_COLD;

	void common(machine_config &config);

private:
	MC6845_UPDATE_ROW(crtc_update_row);

	// devices
	required_device<m68010_device> m_cpu;
	required_device<ram_device> m_ram;
	required_shared_ptr<u16> m_vram;
	required_memory_region m_eprom;

	required_device<pic8259_device> m_pic;

	required_device<screen_device> m_screen_crtc;
	required_device<screen_device> m_screen_acrtc;
	required_device<palette_device> m_palette_acrtc;
	required_device<hd6345_device> m_crtc;
	required_device<hd63484_device> m_acrtc;
	required_device<i8251_device>  m_serial1;
	required_device<i8251_device>  m_serial2;

	required_shared_ptr<u16> m_fontram;

	required_ioport m_sw1;
	required_ioport m_sw2;
	u16 f14000_r();
	u16 f16000_r();
};

void sx1000_state::machine_start()
{
}

void sx1000_state::machine_reset()
{
}

void sx1000_state::init_common()
{
}

void sx1000_state::cpu_map(address_map &map)
{
	map(0x000000, 0x00ffff).rom().region(m_eprom, 0); // FIXME: probably mapped/unmapped during reset

	// FIXME: additional 1M RAM board seems to be mandatory?
	map(0x010000, 0x1fffff).ram();

	map(0xa00000, 0xbfffff).ram(); // Banking on f00000 writes, if it makes any sense? Would provide up to 32M of ram, bank is 4 bits

	map(0xf00000, 0xf0ffff).rom().region(m_eprom, 0);

	map(0xf11001, 0xf11001).rw(m_serial1, FUNC(i8251_device::data_r), FUNC(i8251_device::data_w));
	map(0xf11003, 0xf11003).rw(m_serial1, FUNC(i8251_device::status_r), FUNC(i8251_device::control_w));
	map(0xf11004, 0xf11005).lr16(NAME([this]() { return m_serial1->txrdy_r() ? 2 : 0; }));

	map(0xf13801, 0xf13801).w(m_crtc, FUNC(hd6345_device::address_w));
	map(0xf13901, 0xf13901).rw(m_crtc, FUNC(hd6345_device::register_r), FUNC(hd6345_device::register_w));

	// reads of f14000.w don't entirely pan out as pic, let's cheat a little
	map(0xf14001, 0xf14001).lrw8([this]() { return m_pic->read(0); }, "pic_r0", [this](u8 data) { m_pic->write(0, data); }, "pic_w0");
	map(0xf14101, 0xf14101).lrw8([this]() { return m_pic->read(1); }, "pic_r1", [this](u8 data) { m_pic->write(1, data); }, "pic_w1");

	map(0xf14000, 0xf14001).r(FUNC(sx1000_state::f14000_r));
	map(0xf16000, 0xf16001).r(FUNC(sx1000_state::f16000_r));

	map(0xf1a001, 0xf1a001).rw(m_serial2, FUNC(i8251_device::data_r), FUNC(i8251_device::data_w));
	map(0xf1a003, 0xf1a003).rw(m_serial2, FUNC(i8251_device::status_r), FUNC(i8251_device::control_w));
	map(0xf1a004, 0xf1a005).lr16(NAME([this]() { return m_serial2->txrdy_r() ? 2 : 0; }));

	map(0xf20000, 0xf23fff).ram().share(m_vram);

	map(0xf28000, 0xf28001).lrw16([this]() { return m_acrtc->read16(0); }, "acrtc_status_r", [this](u16 data) { m_acrtc->write16(0, data); }, "acrtc_address_w");
	map(0xf28100, 0xf28101).lrw16([this]() { return m_acrtc->read16(1); }, "acrtc_data_r", [this](u16 data) { m_acrtc->write16(1, data); }, "acrtc_data_w");

	map(0xf2e000, 0xf2ffff).ram().share(m_fontram);
}

void sx1000_state::acrtc_map(address_map &map)
{
	map(0x00000, 0x3ffff).ram();
}

u16 sx1000_state::f14000_r()
{
	u8 res = m_sw2->read();

	if(m_crtc->vsync_r())
		res |= 0x20;

	return res;
}

u16 sx1000_state::f16000_r()
{
	u8 res = m_sw1->read();

	return res;
}

MC6845_UPDATE_ROW( sx1000_state::crtc_update_row )
{
	//  logerror("ma=%x ra=%d y=%d x_count=%d cursor_x=%d de=%d hbp=%d vbp=%d\n", ma*2, ra, y, x_count, cursor_x, de, hbp, vbp);
	const u16 *vram = m_vram + ma;
	u32 *dest = &bitmap.pix(y);
	for(u32 x0 = 0; x0 != x_count; x0 ++) {
		u16 const data = *vram++;
		u16 const bitmap = m_fontram[((data & 0xff) << 4) | (ra)];
		for(u32 x1 = 0; x1 != 8; x1 ++) {
			u32 color = BIT(bitmap, 7-x1) ? 0xffffff : 0x000000;
			*dest ++ = color;
			*dest ++ = color;
		}
	}
}

static DEVICE_INPUT_DEFAULTS_START( terminal1 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_1200 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_1200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

static DEVICE_INPUT_DEFAULTS_START( terminal2 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void sx1000_state::common(machine_config &config)
{
	M68010(config, m_cpu, 10'000'000);
	m_cpu->set_addrmap(AS_PROGRAM, &sx1000_state::cpu_map);

	// 36 x D41256C-15 (256Kb DRAM) on CPU board
	RAM(config, m_ram);
	m_ram->set_default_size("1M");

	//  NVRAM(config, "nvram");

	PIC8259(config, m_pic);

	// M6845 config screen: HTOTAL: 944  VTOTAL: 444  MAX_X: 639  MAX_Y: 399  HSYNC: 720-823  VSYNC: 416-425  Freq: 76.347534fps
	SCREEN(config, m_screen_crtc, SCREEN_TYPE_RASTER);
	m_screen_crtc->set_raw(48'800'000, 944, 0, 640, 444, 0, 400);
	m_screen_crtc->set_screen_update(m_crtc, FUNC(hd6345_device::screen_update));

	// ACRTC: full 944x449 vis (200, 18)-(847, 417)
	SCREEN(config, m_screen_acrtc, SCREEN_TYPE_RASTER);
	m_screen_acrtc->set_raw(48'800'000, 944, 200, 847, 449, 18, 417);
	m_screen_acrtc->set_screen_update(m_acrtc, FUNC(hd63484_device::update_screen));
	m_screen_acrtc->set_palette(m_palette_acrtc);

	PALETTE(config, m_palette_acrtc).set_entries(16);

	// htotal = 117
	// hdisp = 80
	// vtotal = 26
	// vdisp = 25
	// mrast = 15
	// MB89321A
	HD6345(config, m_crtc, 4'000'000);
	m_crtc->set_screen(m_screen_crtc);
	m_crtc->set_update_row_callback(FUNC(sx1000_state::crtc_update_row));
	m_crtc->out_vsync_callback().set(m_pic, FUNC(pic8259_device::ir5_w));
	m_crtc->set_hpixels_per_column(8);

	HD63484(config, m_acrtc, 8'000'000);
	m_acrtc->set_screen(m_screen_acrtc);
	m_acrtc->set_addrmap(0, &sx1000_state::acrtc_map);

	auto &rs232_1(RS232_PORT(config, "serial1", default_rs232_devices, nullptr));
	rs232_1.rxd_handler().set(m_serial1, FUNC(i8251_device::write_rxd));
	rs232_1.dsr_handler().set(m_serial1, FUNC(i8251_device::write_dsr));
	rs232_1.cts_handler().set(m_serial1, FUNC(i8251_device::write_cts));
	rs232_1.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal1));

	I8251(config, m_serial1, 4'000'000);
	m_serial1->txd_handler().set(rs232_1, FUNC(rs232_port_device::write_txd));
	m_serial1->dtr_handler().set(rs232_1, FUNC(rs232_port_device::write_dtr));
	m_serial1->rts_handler().set(rs232_1, FUNC(rs232_port_device::write_rts));

	CLOCK(config, "clock1", 1200*16).signal_handler().set(m_serial1, FUNC(i8251_device::write_txc));

	auto &rs232_2(RS232_PORT(config, "serial2", default_rs232_devices, nullptr));
	rs232_2.rxd_handler().set(m_serial2, FUNC(i8251_device::write_rxd));
	rs232_2.dsr_handler().set(m_serial2, FUNC(i8251_device::write_dsr));
	rs232_2.cts_handler().set(m_serial2, FUNC(i8251_device::write_cts));
	rs232_2.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal2));

	I8251(config, m_serial2, 4'000'000);
	m_serial2->txd_handler().set(rs232_2, FUNC(rs232_port_device::write_txd));
	m_serial2->dtr_handler().set(rs232_2, FUNC(rs232_port_device::write_dtr));
	m_serial2->rts_handler().set(rs232_2, FUNC(rs232_port_device::write_rts));

	CLOCK(config, "clock2", 9600*16).signal_handler().set(m_serial2, FUNC(i8251_device::write_txc));
}

void sx1000_state::sx1010(machine_config &config)
{
	common(config);
}

static INPUT_PORTS_START(sx1010)
	PORT_START("SW1")
	PORT_DIPNAME( 0x20, 0x00, "Ignore keyboard error" ) PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(    0x00, "Off" )
	PORT_DIPSETTING(    0x20, "On" )

	PORT_START("SW2")
	PORT_DIPNAME( 0x02, 0x00, "Serial console" ) PORT_DIPLOCATION("SW2:3")
	PORT_DIPSETTING(    0x00, "Off" )
	PORT_DIPSETTING(    0x02, "On" )
INPUT_PORTS_END

ROM_START(sx1010)
	ROM_REGION16_BE(0x10000, "eprom", 0)
	ROM_SYSTEM_BIOS(0, "sx1010", "sx1010")
	ROMX_LOAD("pc1a.u6l8", 0x8001, 0x4000, CRC(75d0f02c) SHA1(dfcc7efc1b5e7b43fc1ee030bef5c75c23d5e742), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_CONTINUE (         0x0001, 0x4000)
	ROMX_LOAD("pc1a.u6h8", 0x8000, 0x4000, CRC(a928c0c9) SHA1(712601ee889a0790a7579fe06df20ec7e0a4bb49), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_CONTINUE (         0x0000, 0x4000)
ROM_END

} // anonymous namespace

/*   YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT         COMPANY  FULLNAME  FLAGS */
COMP(1987, sx1010, 0,      0,      sx1010,  sx1010, sx1000_state, init_common, "Casio", "SX1010", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



sx240.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Kawai SX-240 synthesizer.

****************************************************************************/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/nvram.h"
#include "machine/pit8253.h"


namespace {

class kawai_sx240_state : public driver_device
{
public:
	kawai_sx240_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_master(*this, "master")
		, m_slave(*this, "slave")
	{
	}

	void sx240(machine_config &config);

private:
	void sh_outport_w(u8 data);
	void asn_w(u8 data);
	void g_w(u8 data);

	void master_prog(address_map &map) ATTR_COLD;
	void master_ext(address_map &map) ATTR_COLD;
	void slave_prog(address_map &map) ATTR_COLD;
	void slave_ext(address_map &map) ATTR_COLD;

	required_device<mcs51_cpu_device> m_master;
	required_device<mcs51_cpu_device> m_slave;
};


void kawai_sx240_state::sh_outport_w(u8 data)
{
	// SAD1–SAD3 = SHA0–SHA2
	// SAD4, SAD5 -> NE555 for buzzer
}

void kawai_sx240_state::asn_w(u8 data)
{
}

void kawai_sx240_state::g_w(u8 data)
{
}

void kawai_sx240_state::master_prog(address_map &map)
{
	map.global_mask(0x3fff);
	map(0x0000, 0x3fff).rom().region("mcode", 0);
}

void kawai_sx240_state::master_ext(address_map &map)
{
	map(0x0000, 0x07ff).ram().share("ram0");
	map(0x0800, 0x0fff).ram().share("ram1");
	map(0x1000, 0x17ff).ram().share("ram2");
	map(0x1800, 0x1fff).ram().share("ram3");
	map(0x2100, 0x210f).nopr();
}

void kawai_sx240_state::slave_prog(address_map &map)
{
	map.global_mask(0x1fff);
	map(0x0000, 0x1fff).rom().region("scode", 0);
}

void kawai_sx240_state::slave_ext(address_map &map)
{
	map(0x0000, 0x07ff).mirror(0x800).ram();
	map(0x1000, 0x1003).mirror(0xfc0).rw("pit0", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x1004, 0x1007).mirror(0xfc0).rw("pit1", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x1008, 0x100b).mirror(0xfc0).rw("pit2", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x100c, 0x100f).mirror(0xfc0).rw("pit3", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x1010, 0x1013).mirror(0xfc0).rw("pit4", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x1014, 0x1017).mirror(0xfc0).rw("pit5", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x1018, 0x101b).mirror(0xfc0).rw("pit6", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x101c, 0x101f).mirror(0xfc0).rw("pit7", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x1020, 0x1023).mirror(0xfd8).rw("pit8", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x1024, 0x1027).mirror(0xfd8).rw("pit9", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x2000, 0x202f).mirror(0xfc0).nopw();
	map(0x2030, 0x2030).mirror(0xfc7).w(FUNC(kawai_sx240_state::sh_outport_w));
	map(0x2038, 0x2038).mirror(0xfc7).w(FUNC(kawai_sx240_state::asn_w));
	map(0x3000, 0x3000).mirror(0xfff).w(FUNC(kawai_sx240_state::g_w));
}


static INPUT_PORTS_START(sx240)
INPUT_PORTS_END

void kawai_sx240_state::sx240(machine_config &config)
{
	I8031(config, m_master, 12_MHz_XTAL);
	m_master->set_addrmap(AS_PROGRAM, &kawai_sx240_state::master_prog);
	m_master->set_addrmap(AS_IO, &kawai_sx240_state::master_ext);

	NVRAM(config, "ram0", nvram_device::DEFAULT_ALL_0); // HM6116ALSP-20 (I9) + battery
	NVRAM(config, "ram1", nvram_device::DEFAULT_ALL_0); // HM6116ALSP-20 (I10) + battery
	NVRAM(config, "ram2", nvram_device::DEFAULT_ALL_0); // HM6116ALSP-20 (I11) + battery
	NVRAM(config, "ram3", nvram_device::DEFAULT_ALL_0); // HM6116ALSP-20 (I12) + battery

	I8031(config, m_slave, 12_MHz_XTAL);
	m_slave->set_addrmap(AS_PROGRAM, &kawai_sx240_state::slave_prog);
	m_slave->set_addrmap(AS_IO, &kawai_sx240_state::slave_ext);

	PIT8253(config, "pit0"); // MSM82C53-5 (I25)
	PIT8253(config, "pit1"); // MSM82C53-5 (I26)
	PIT8253(config, "pit2"); // MSM82C53-5 (I27)
	PIT8253(config, "pit3"); // MSM82C53-5 (I28)
	PIT8253(config, "pit4"); // MSM82C53-5 (I29)
	PIT8253(config, "pit5"); // MSM82C53-5 (I30)
	PIT8253(config, "pit6"); // MSM82C53-5 (I31)
	PIT8253(config, "pit7"); // MSM82C53-5 (I32)
	PIT8253(config, "pit8"); // MSM82C53-5 (I33)
	PIT8253(config, "pit9"); // MSM82C53-5 (I34)
}

ROM_START(sx240)
	ROM_REGION(0x4000, "mcode", 0) // 27128-25
	ROM_SYSTEM_BIOS(0, "firmware", "Firmware (unknown)")
	ROMX_LOAD("kawai sx-240 rom firmware binary.bin", 0x0000, 0x4000, CRC(b057c3ee) SHA1(3d3f98ac2c5ccca70690e4fb3482d704a67a6f65), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "tauntek", "Tauntek CFW")
	ROMX_LOAD("sx240mst.i8", 0x0000, 0x4000, CRC(426aba62) SHA1(d19c14a9ce6c06f021fa2641f072a82332023b1e), ROM_BIOS(1))

	ROM_REGION(0x2000, "scode", 0) // 2764-25
	// From Tauntek package: "New Slave EPROM is only required for saving patches or sequences via sysex"
	ROM_LOAD("sx240slv.i38", 0x0000, 0x2000, CRC(c6bc6439) SHA1(4d51d8c5a198ff139956cb0b07225448357d17ef))
ROM_END

} // anonymous namespace


SYST(1984, sx240, 0, 0, sx240, sx240, kawai_sx240_state, empty_init, "Kawai Musical Instrument Manufacturing", "SX-240 8-Voice Programmable Polyphonic Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



sym1.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Dirk Best
/**********************************************************************************************

Synertek Systems Corp. SYM-1

Using the cassette (bios 0 only)
- To save: set tape to record, press F2, type id-start-end (e.g. 3-200-210), press enter.
- To load: press F1, type the id, press enter.

TODO:
- Digits should go blank during cassette save
- How to use cassette with -bios 1 ??
- TTY/CRT interfaces not working
- You can't show the TTY/CRT terminal screen and the SYM-1 screen at the same time
- Need software (SW-list is set up, but there's nothing for it)

***********************************************************************************************/

#include "emu.h"

#include "cpu/m6502/m6502.h"
#include "bus/rs232/rs232.h"
#include "imagedev/cassette.h"
#include "machine/6522via.h"
#include "machine/74145.h"
#include "machine/input_merger.h"
#include "machine/mos6530.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "sound/spkrdev.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "sym1.lh"


namespace {

//**************************************************************************
//  MACROS/CONSTANTS
//**************************************************************************

// SYM-1 main (and only) oscillator Y1
#define SYM1_CLOCK  XTAL(1'000'000)

#define LED_REFRESH_DELAY   attotime::from_usec(70)


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class sym1_state : public driver_device
{
public:
	sym1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
		, m_banks(*this, "bank%u", 0U)
		, m_ttl74145(*this, "ttl74145")
		, m_via1(*this, "via1")
		, m_crt(*this, "crt")
		, m_tty(*this, "tty")
		, m_cass(*this, "cassette")
		, m_row(*this, "ROW-%u", 0U)
		, m_wp(*this, "WP")
		, m_digits(*this, "digit%u", 0U)
		{ }

	void sym1(machine_config &config);

	void init_sym1();

private:
	uint8_t m_riot_port_a;
	uint8_t m_riot_port_b;
	emu_timer *m_led_update;
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override { m_digits.resolve(); }
	TIMER_CALLBACK_MEMBER(led_refresh);
	TIMER_DEVICE_CALLBACK_MEMBER(cass_r);
	void sym1_74145_output_0_w(int state);
	void sym1_74145_output_1_w(int state);
	void sym1_74145_output_2_w(int state);
	void sym1_74145_output_3_w(int state);
	void sym1_74145_output_4_w(int state);
	void sym1_74145_output_5_w(int state);
	void sym1_74145_output_7_w(int state);
	void via1_ca2_w(int state);
	void via1_cb2_w(int state);
	uint8_t riot_a_r();
	uint8_t riot_b_r();
	void riot_a_w(uint8_t data);
	void riot_b_w(uint8_t data);
	void via3_a_w(uint8_t data);

	std::unique_ptr<u8[]> m_riot_ram;
	std::unique_ptr<u8[]> m_dummy_ram;
	bool m_cb2 = false;
	void sym1_map(address_map &map) ATTR_COLD;

	required_device<m6502_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_memory_bank_array<10> m_banks;
	required_device<ttl74145_device> m_ttl74145;
	required_device<via6522_device> m_via1;
	required_device<rs232_port_device> m_crt;
	required_device<rs232_port_device> m_tty;
	required_device<cassette_image_device> m_cass;
	required_ioport_array<4> m_row;
	required_ioport m_wp;
	output_finder<6> m_digits;
};


//**************************************************************************
//  KEYBOARD INPUT & LED OUTPUT
//**************************************************************************

void sym1_state::sym1_74145_output_0_w(int state) { if (state) m_led_update->adjust(LED_REFRESH_DELAY, 0); }
void sym1_state::sym1_74145_output_1_w(int state) { if (state) m_led_update->adjust(LED_REFRESH_DELAY, 1); }
void sym1_state::sym1_74145_output_2_w(int state) { if (state) m_led_update->adjust(LED_REFRESH_DELAY, 2); }
void sym1_state::sym1_74145_output_3_w(int state) { if (state) m_led_update->adjust(LED_REFRESH_DELAY, 3); }
void sym1_state::sym1_74145_output_4_w(int state) { if (state) m_led_update->adjust(LED_REFRESH_DELAY, 4); }
void sym1_state::sym1_74145_output_5_w(int state) { if (state) m_led_update->adjust(LED_REFRESH_DELAY, 5); }
void sym1_state::sym1_74145_output_7_w(int state) { m_cass->output( state ? -1.0 : +1.0); }

void sym1_state::via1_cb2_w(int state)
{
	m_cb2 = state;
	m_cass->change_state(state ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
}

TIMER_CALLBACK_MEMBER(sym1_state::led_refresh)
{
	m_digits[param] = m_riot_port_a;
}

TIMER_DEVICE_CALLBACK_MEMBER(sym1_state::cass_r)
{
	if (!m_cb2)
		return;
	bool cass_ws = (m_cass->input() > +0.03) ? 1 : 0;
	m_via1->write_pb6(cass_ws);
}

uint8_t sym1_state::riot_a_r()
{
	int data = 0x7f;

	// scan keypad rows
	if (!(m_riot_port_a & 0x80)) data &= m_row[0]->read();
	if (!(m_riot_port_b & 0x01)) data &= m_row[1]->read();
	if (!(m_riot_port_b & 0x02)) data &= m_row[2]->read();
	if (!(m_riot_port_b & 0x04)) data &= m_row[3]->read();

	// determine column
	if ( ((m_riot_port_a ^ 0xff) & (m_row[0]->read() ^ 0xff)) & 0x7f )
		data &= ~0x80;

	return data;
}

uint8_t sym1_state::riot_b_r()
{
	int data = 0x3f;

	// determine column
	if ( ((m_riot_port_a ^ 0xff) & (m_row[1]->read() ^ 0xff)) & 0x7f )
		data &= ~0x01;

	if ( ((m_riot_port_a ^ 0xff) & (m_row[2]->read() ^ 0xff)) & 0x3f )
		data &= ~0x02;

	if ( ((m_riot_port_a ^ 0xff) & (m_row[3]->read() ^ 0xff)) & 0x1f )
		data &= ~0x04;

	// PB6 in from TTY keyboard
	data |= m_tty->rxd_r() << 6;

	// PB7 in from RS-232 CRT
	data |= m_crt->rxd_r() << 7;

	data &= ~0x80; // else hangs 8b02

	return data;
}

void sym1_state::riot_a_w(uint8_t data)
{
	logerror("%x: riot_a_w 0x%02x\n", m_maincpu->pc(), data);

	// save for later use
	m_riot_port_a = data;
}

void sym1_state::riot_b_w(uint8_t data)
{
	logerror("%x: riot_b_w 0x%02x\n", m_maincpu->pc(), data);

	// save for later use
	m_riot_port_b = data;

	// first 4 pins are connected to the 74145
	m_ttl74145->write(data & 0x0f);

	// PB4 out to RS-232 CRT
	m_crt->write_txd(BIT(data, 4));

	// PB5 out to TTY printer
	m_tty->write_txd(BIT(data, 5));
}

//**************************************************************************
//  INPUT PORTS
//**************************************************************************

static INPUT_PORTS_START( sym1 )
	PORT_START("ROW-0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0     USR 0") PORT_CODE(KEYCODE_0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4     USR 4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8     JUMP")  PORT_CODE(KEYCODE_8)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C     CALC")  PORT_CODE(KEYCODE_C)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CR    S DBL") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("GO    LD P")  PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LD 2  LD 1")  PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("ROW-1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1     USR 1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5     USR 5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9     VER")   PORT_CODE(KEYCODE_9)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D     DEP")   PORT_CODE(KEYCODE_D)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-     +")     PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("REG   SAV P") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SAV 2 SAV 1") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW-2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2     USR 2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6     USR 6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A     ASCII") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E     EXEC")  PORT_CODE(KEYCODE_E)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\xE2\x86\x92     \xE2\x86\x90") PORT_CODE(KEYCODE_RIGHT) PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MEM   WP")    PORT_CODE(KEYCODE_F5)
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW-3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3     USR 3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7     USR 7") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B     B MOV") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F     FILL")  PORT_CODE(KEYCODE_F)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT")       PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN4")           /* IN4 */
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DEBUG OFF") PORT_CODE(KEYCODE_F6)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DEBUG ON")  PORT_CODE(KEYCODE_F7)

	PORT_START("WP")
	PORT_DIPNAME(0x01, 0x01, "6532 RAM WP")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x01, DEF_STR(On))
	PORT_DIPNAME(0x02, 0x02, "1K RAM WP")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x02, DEF_STR(On))
	PORT_DIPNAME(0x04, 0x04, "2K RAM WP")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x04, DEF_STR(On))
	PORT_DIPNAME(0x08, 0x08, "3K RAM WP")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x08, DEF_STR(On))
INPUT_PORTS_END


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

// CA2 will be forced high when the VIA is reset, causing the ROM to be switched in
// When the bios clears POR, FF80-FFFF becomes a mirror of A600-A67F
void sym1_state::via1_ca2_w(int state)
{
	m_banks[8]->set_entry(state);
}

/*
    PA0: Write protect R6532 RAM
    PA1: Write protect RAM 0x400-0x7ff
    PA2: Write protect RAM 0x800-0xbff
    PA3: Write protect RAM 0xc00-0xfff
 */
void sym1_state::via3_a_w(uint8_t data)
{
	logerror("SYM1 VIA2 W 0x%02x\n", data);

	u8 sw = m_wp->read();

	// apply or remove write-protection as directed
	for (u8 i = 0; i < 4; i++)
	{
		// considered readonly if DIP is on AND databit is low; OR if memory not fitted
		if ((BIT(sw, i) && !BIT(data, i)) || m_banks[i*2]->entry() )
			m_banks[i*2+1]->set_entry(1);  // readonly
		else
			m_banks[i*2+1]->set_entry(0);  // readwrite
		//printf("Bank %d has entry %d\n",i*2+1,m_banks[i*2+1]->entry());
	}

	// POR write is same as bank1
	m_banks[9]->set_entry(m_banks[1]->entry());
}

void sym1_state::init_sym1()
{
	// m_ram 000-3FF not allocated to anything, so we use it as a dummy write area
	u8 *const m = memregion("maincpu")->base()+0x8f80;
	m_riot_ram = make_unique_clear<u8[]>(0x80);
	m_dummy_ram = make_unique_clear<u8[]>(0x400); // dummy read area, preset to FF
	u8 *w = m_riot_ram.get();
	u8 *x = m_dummy_ram.get();
	std::memset(x, 0xff, 0x400);
	u8 *r = m_ram->pointer();
	// RAM 400-7FF
	m_banks[2]->configure_entry(0, r+0x400);    // ram exist, readable
	m_banks[2]->configure_entry(1, x);          // ram not present
	m_banks[3]->configure_entry(0, r+0x400);    // ram exist, writable
	m_banks[3]->configure_entry(1, r);          // ram not present or readonly
	// RAM 800-BFF
	m_banks[4]->configure_entry(0, r+0x800);
	m_banks[4]->configure_entry(1, x);
	m_banks[5]->configure_entry(0, r+0x800);
	m_banks[5]->configure_entry(1, r);
	// RAM C00-FFF
	m_banks[6]->configure_entry(0, r+0xc00);
	m_banks[6]->configure_entry(1, x);
	m_banks[7]->configure_entry(0, r+0xc00);
	m_banks[7]->configure_entry(1, r);
	// RIOT RAM A600-A67F
	m_banks[0]->configure_entry(0, w);          // riot ram, readable
	m_banks[0]->configure_entry(1, w);
	m_banks[1]->configure_entry(0, w);          // riot ram, writable
	m_banks[1]->configure_entry(1, r);          // riot ram, readonly
	// POR
	m_banks[8]->configure_entry(0, w);          // point at riot-ram
	m_banks[8]->configure_entry(1, m);          // point at rom
	m_banks[9]->configure_entry(0, w);          // riot ram, writable
	m_banks[9]->configure_entry(1, r);          // riot ram, readonly

	for (auto &bank : m_banks)
		bank->set_entry(1);

	// allocate a timer to refresh the led display
	m_led_update = timer_alloc(FUNC(sym1_state::led_refresh), this);
}

void sym1_state::machine_reset()
{
	// Enable extra ram if it is fitted
	switch (m_ram->size())
	{
		case 4*1024:
			m_banks[6]->set_entry(0);
			[[fallthrough]];
		case 3*1024:
			m_banks[4]->set_entry(0);
			[[fallthrough]];
		case 2*1024:
			m_banks[2]->set_entry(0);
			[[fallthrough]];
		default:
			m_banks[0]->set_entry(0);
			break;
	}

	// Enable POR
	via1_ca2_w(1);
}


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void sym1_state::sym1_map(address_map &map)
{
	map(0x0000, 0x03ff).ram(); // U12/U13 RAM
	map(0x0400, 0x07ff).bankr("bank2").bankw("bank3");  // U14/U15 OPT RAM
	map(0x0800, 0x0bff).bankr("bank4").bankw("bank5");  // U16/U17 OPT RAM
	map(0x0c00, 0x0fff).bankr("bank6").bankw("bank7");  // U18/U19 OPT RAM
	map(0x8000, 0x8fff).rom(); // U20 Monitor ROM
	map(0xa000, 0xa00f).m("via1", FUNC(via6522_device::map));  // U25 VIA #1
	map(0xa400, 0xa41f).m("riot", FUNC(mos6532_device::io_map));  // U27 RIOT
	map(0xa600, 0xa67f).bankr("bank0").bankw("bank1");   // U27 RIOT RAM
	map(0xa800, 0xa80f).m("via2", FUNC(via6522_device::map));  // U28 VIA #2
	map(0xac00, 0xac0f).m("via3", FUNC(via6522_device::map));  // U29 VIA #3
	map(0xb000, 0xefff).rom();
	map(0xff80, 0xffff).bankr("bank8").bankw("bank9");   // POR
}


//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

void sym1_state::sym1(machine_config &config)
{
	// basic machine hardware
	M6502(config, m_maincpu, SYM1_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &sym1_state::sym1_map);

	config.set_default_layout(layout_sym1);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 1.0);

	// devices
	mos6532_device &riot(MOS6532(config, "riot", SYM1_CLOCK));
	riot.pa_rd_callback().set(FUNC(sym1_state::riot_a_r));
	riot.pa_wr_callback().set(FUNC(sym1_state::riot_a_w));
	riot.pb_rd_callback().set(FUNC(sym1_state::riot_b_r));
	riot.pb_wr_callback().set(FUNC(sym1_state::riot_b_w));

	TTL74145(config, m_ttl74145, 0);
	m_ttl74145->output_line_callback<0>().set(FUNC(sym1_state::sym1_74145_output_0_w));
	m_ttl74145->output_line_callback<1>().set(FUNC(sym1_state::sym1_74145_output_1_w));
	m_ttl74145->output_line_callback<2>().set(FUNC(sym1_state::sym1_74145_output_2_w));
	m_ttl74145->output_line_callback<3>().set(FUNC(sym1_state::sym1_74145_output_3_w));
	m_ttl74145->output_line_callback<4>().set(FUNC(sym1_state::sym1_74145_output_4_w));
	m_ttl74145->output_line_callback<5>().set(FUNC(sym1_state::sym1_74145_output_5_w));
	m_ttl74145->output_line_callback<6>().set("speaker", FUNC(speaker_sound_device::level_w));
	m_ttl74145->output_line_callback<7>().set(FUNC(sym1_state::sym1_74145_output_7_w));
	// lines 7-9 not connected

	MOS6522(config, m_via1, SYM1_CLOCK);
	m_via1->irq_handler().set("mainirq", FUNC(input_merger_device::in_w<0>));
	m_via1->ca2_handler().set(FUNC(sym1_state::via1_ca2_w));
	m_via1->cb2_handler().set(FUNC(sym1_state::via1_cb2_w));

	MOS6522(config, "via2", SYM1_CLOCK).irq_handler().set("mainirq", FUNC(input_merger_device::in_w<1>));

	via6522_device &via3(MOS6522(config, "via3", SYM1_CLOCK));
	via3.writepa_handler().set(FUNC(sym1_state::via3_a_w));
	via3.irq_handler().set("mainirq", FUNC(input_merger_device::in_w<2>));

	input_merger_device &merger(INPUT_MERGER_ANY_HIGH(config, "mainirq", 0)); // wire-or connection
	merger.output_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	RS232_PORT(config, "crt", default_rs232_devices, nullptr);
	RS232_PORT(config, "tty", default_rs232_devices, nullptr); // actually a 20 mA current loop; 110 bps assumed

	/* cassette */
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cass->set_interface("sym1_cass");
	TIMER(config, "cass_r").configure_periodic(FUNC(sym1_state::cass_r), attotime::from_hz(40000));

	// internal ram
	RAM(config, m_ram);
	m_ram->set_default_size("4K");
	m_ram->set_extra_options("1K,2K,3K");

	SOFTWARE_LIST(config, "cass_list").set_original("sym1_cass");
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( sym1 )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_SYSTEM_BIOS(0, "ver10",  "Version 1.0")
	ROMX_LOAD("symon1_0.bin", 0x8000, 0x1000, CRC(97928583) SHA1(6ac52c54adb7a086d51bc7f6d55dd30ab3a0a331), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "ver11",  "Version 1.1")
	ROMX_LOAD("symon1_1.bin", 0x8000, 0x1000, CRC(7a4b1e12) SHA1(cebdf815105592658cfb7af262f2101d2aeab786), ROM_BIOS(1))
	ROM_LOAD("rae_b000", 0xb000, 0x1000, CRC(f6429326) SHA1(6f2f10649b54f54217bb35c8c453b5d05434bd86) )
	ROM_LOAD("bas_c000", 0xc000, 0x1000, CRC(c168fe70) SHA1(7447a5e229140cbbde4cf90886966a5d93aa24e1) )
	ROM_LOAD("bas_d000", 0xd000, 0x1000, CRC(8375a978) SHA1(240301bf8bb8ddb99b65a585f17895e1ad872631) )
	ROM_LOAD("rae_e000", 0xe000, 0x1000, CRC(2255444b) SHA1(c7dd812962c2e2edd2faa7055e9cce4e769c0388) )
ROM_END

} // anonymous namespace


//**************************************************************************
//  GAME DRIVERS
//**************************************************************************

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT       COMPANY                   FULLNAME          FLAGS
COMP( 1978, sym1, 0,      0,      sym1,    sym1,  sym1_state, init_sym1, "Synertek Systems Corp.", "SYM-1/SY-VIM-1", 0 )



symbolics.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Jonathan Gevaryahu
// thanks-to:Ian F. & trinitr0n
/******************************************************************************
    Symbolics 36x0 (really in this case, 3670; the original 3600 is considerably rarer, 3670 is backwards compatible for the most part)
    TODO: add credits, backstory, history, etc here

    Layout of all the Lisp machine models symbolics made, roughly in chronological order:
    LM-2 - basically a CADR (i.e. a clone of the MIT CADR machine. this needs more research, as there may be differences.)
    3600 - precursor to the L-machine, 98% of the actual L machines- an extended and polished CADR. uses the pre-PE console
    3670 / 3640 {large, small} L-machines, same architecture but the large cabinet has more slots for boards and can take larger disks like EAGLEs
    3675 / 3645 - Faster L-machines; same core board set but they come with the later released FPA and instruction prefetch units; ~ 1.5x faster; this is the fastest pre-Ivory machine, faster than all the 36xx machines below
    3620 - Small G-machine, no room for color graphics etc
    3630 - Small G-machine with room for a color CADBUFFER (256 color). a "big" 3620
    3650 - Big G-machine; same core boards as a 3620 / 3630 but more slots and can take bigger disks. The size of the "Small" L-machine (3640)
    3653 - Basically 3 3620s in a 3650 case.
    3610 - A gimped 3620- no difference but the ID PROM. Licensed for application deployment but not development
    MacIvory - First gen ivory, basically a MacIvory II with worse cycle time
    XL400 - First gen ivory (same as MacIvory II) chips in a standalone machine
    XL1200 - Second gen ivory (same as MacIvory III) chips, faster, uses memory on the card itself, otherwise in the exact same setup as the XL400
    NXP1000 - An ivory in a standalone pizzabox. No console connection, FEP prompt over serial, Genera access only over network (X11 forwarding for gui), has scsi disks
    There were both 256 color (cad buffer) and true color w/framegrabber options for the 36xx family

    3670 new version 'NFEP' Front-end Processor dumped only, so far, plds/proms/pals not dumped yet

    TODO:
    The entire lispcpu half (more like 3/4) of the machine
    Framebuffer 1152x864? 1150x900? (lives on the i/o card)
    I8274 MPSC (z80sio.cpp) x2
    1024x4bit SRAM AM2148-50 x6 @F22-F27
    2048x8bit SRAM @F7 and @G7
    keyboard/mouse (a 68k based console dedicated to this machine; talks through one of the MPSC chips)
    am9517a-50 DMA controller
    'NanoFEP' i8749 mcu which runs the front panel and rtc clock (only on original 3600, the 3670 and all later machines lack this)

    DONE:
    ROM Loading
    256K DRAM

    Keyboard serial bit map:
    Local (#x01), Caps Lock (LED) (#x02), Hyper (left) (#x03), Meta (left) (#x04),
    Control (right) (#x05), Super (right) (#x06), Scroll (#x07), Mode Lock (LED) (#x08),
    Select (#x0c), Symbol (left) (#x0d), Super (left) (#x0e), Control (left) (#x0f),
    Space (#x10), Meta (right) (#x11), Hyper (right) (#x12), End (#x13),
    Z (#x17), C (#x18), B (#x19), M (#x1a),
    . / > (#x1b), Shift (right) (#x1c), Repeat (#x1d), Abort (#x1e),
    Shift (left) (#x22), X (#x23), V (#x24), N (#x25),
    , / < (#x26), / / ? (#x27), Symbol (right) (#x28), Help (#x29),
    Rubout (#x2d), S (#x2e), F (#x2f), H (#x30),
    K (#x31), ; / : (#x32), Return (#x33), Complete (#x34),
    Network (#x38), A (#x39), D (#x3a), G (#x3b),
    J (#x3c), L (#x3d), ' / " (#x3e), Line (#x3f),
    Function (#x43), W (#x44), R (#x45), Y (#x46),
    I (#x47), P (#x48), ) / ] (#x49), Page (#x4a),
    Tab (#x4e), Q (#x4f), E (#x50), T (#x51),
    U (#x52), O (#x53), ( / [ (#x54), Back Space (#x55),
    : (#x59), 2 / @ (#x5a), 4 / $ (#x5b), 6 / ^ (#x5c),
    8 / * (#x5d), 0 / ) (#x5e), = / + (#x5f), \ / { (#x60),
    1 / ! (#x64), 3 / # (#x65), 5 / % (#x66), 7 / & (#x67),
    9 / ( (#x68), - / _ (#x69), ` / ~ (#x6a), | / } (#x6b),
    Escape (#x6f), Refresh (#x70), Square (#x71), Circle (#x72),
    Triangle (#x73), Clear Input (#x74), Suspend (#x75), Resume (#x76)


    Notes from US Patent 4887235 which has some FEP source code and limited memory info:
    FEP main description starts on pdf page 47
    FEP access the SPY bus which allows poking at the workings of the lisp cpu, as well as reading the ethernet interface (on 3600, later g-machine 3650 fep seems to use a separate z80 for this?) see patent pdf page 33
    page 41 reveals the SPY bus is 8 data bits wide, and has an address of 6 bits
    see page 97 of http://bitsavers.trailing-edge.com/pdf/symbolics/3600_series/Lisp_Machine_Hardware_Memos.pdf which explains what addresses do what.
    FEP accesses a register called SQCLKC described with bits on pdf page 32, figure out where this maps

    http://bitsavers.trailing-edge.com/pdf/symbolics/3600_series/3600_TechnicalSummary_Feb83.pdf <- page 114 describes the nanofep
    http://bitsavers.trailing-edge.com/pdf/symbolics/3600_series/Symbolics_3600_Series_Basic_Field_Maint_Jul86.pdf <- page 84 describes the two types of FEP, older one with firmware v24 and newer one (emulated here) with firmware v127

    fonts:
    tiny7: 0x908 to 0xB08 in fep ROM is a thin font, 4 or 5 pixels wide, rows are in a weird scrambled order
    normal or cptfont: 0x11e8 to 0x16d8 is a thick/wide font, 10? pixels wide
    verylarge: 0x2a48 to 0x3638 is an even wider font, 14 pixels wide
    tvfont?: 0x134d8-0x13ad0 is a thin font about 7 pixels wide with the rows in a weird scrambled order
******************************************************************************/

/* Core includes */
#include "emu.h"
#include "cpu/m68000/m68000.h"


namespace {

class symbolics_state : public driver_device
{
public:

	symbolics_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu")
	{
	}

	void symbolics(machine_config &config);

	void init_symbolics();

private:
	required_device<m68000_device> m_maincpu;
	[[maybe_unused]] uint16_t fep_paddle_id_prom_r();
	//uint16_t ram_parity_hack_r(offs_t offset);
	//void ram_parity_hack_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	//bool m_parity_error_has_occurred[0x20000];
	//emu_timer *m_outfifo_timer = nullptr;

	// overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void m68k_mem(address_map &map) ATTR_COLD;

	//TIMER_CALLBACK_MEMBER(outfifo_read_cb);
};

uint16_t symbolics_state::fep_paddle_id_prom_r() // bits 8 and 9 do something special if both are set.
{
	return 0x0300;
}
/*
uint16_t symbolics_state::ram_parity_hack_r(offs_t offset)
{
    uint16_t *ram = (uint16_t *)(memregion("fepdram")->base());
    //m_maincpu->set_input_line(M68K_IRQ_7, CLEAR_LINE);
    m_maincpu->set_input_line(M68K_IRQ_7, CLEAR_LINE);
    if (!(m_parity_error_has_occurred[offset]))
    {
        //m_maincpu->set_input_line(M68K_IRQ_7, ASSERT_LINE);
        m_maincpu->set_input_line(M68K_IRQ_7, ASSERT_LINE);
        m_parity_error_has_occurred[offset] = true;
    }
    ram += offset;
    return *ram;
}

void symbolics_state::ram_parity_hack_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
    uint16_t *ram = (uint16_t *)(memregion("fepdram")->base());
    m_maincpu->set_input_line(M68K_IRQ_7, CLEAR_LINE);
    if (!(m_parity_error_has_occurred[offset]))
    {
        m_maincpu->set_input_line(M68K_IRQ_7, ASSERT_LINE);
        m_parity_error_has_occurred[offset] = true;
    }
    COMBINE_DATA(&ram[offset]);
}
*/

/******************************************************************************
 Address Maps
******************************************************************************/
/*
Address maps (x = ignored; * = selects address within this range, ? = unknown, 1/0 = decodes only when this bit is set to 1/0)
68k address map:
a23 a22 a21 a20 a19 a18 a17 a16 a15 a14 a13 a12 a11 a10 a9  a8  a7  a6  a5  a4  a3  a2  a1  (a0 via UDS/LDS)
?   ?   ?   ?   ?   0   0   0   0   0   *   *   *   *   *   *   *   *   *   *   *   *   *   0       R   ROM 00H @D13 first half
?   ?   ?   ?   ?   0   0   0   0   0   *   *   *   *   *   *   *   *   *   *   *   *   *   1       R   ROM 00L @D7 first half
?   ?   ?   ?   ?   0   0   0   0   1   *   *   *   *   *   *   *   *   *   *   *   *   *   0       R   ROM 04H @D14 first half
?   ?   ?   ?   ?   0   0   0   0   1   *   *   *   *   *   *   *   *   *   *   *   *   *   1       R   ROM 04L @D8 first half
?   ?   ?   ?   ?   0   0   0   1   0   *   *   *   *   *   *   *   *   *   *   *   *   *   0       R   ROM 10H @D16 first half
?   ?   ?   ?   ?   0   0   0   1   0   *   *   *   *   *   *   *   *   *   *   *   *   *   1       R   ROM 10L @D10 first half
?   ?   ?   ?   ?   0   0   0   1   1   *   *   *   *   *   *   *   *   *   *   *   *   *   0       R   Open Bus (socket @D17 first half)
?   ?   ?   ?   ?   0   0   0   1   1   *   *   *   *   *   *   *   *   *   *   *   *   *   1       R   Open Bus (socket @D11 first half)
?   ?   ?   ?   ?   0   0   1   0   0   *   *   *   *   *   *   *   *   *   *   *   *   *   0       R   ROM 00H @D13 second half
?   ?   ?   ?   ?   0   0   1   0   0   *   *   *   *   *   *   *   *   *   *   *   *   *   1       R   ROM 00L @D7 second half
?   ?   ?   ?   ?   0   0   1   0   1   *   *   *   *   *   *   *   *   *   *   *   *   *   0       R   ROM 04H @D14 second half
?   ?   ?   ?   ?   0   0   1   0   1   *   *   *   *   *   *   *   *   *   *   *   *   *   1       R   ROM 04L @D8 second half
?   ?   ?   ?   ?   0   0   1   1   0   *   *   *   *   *   *   *   *   *   *   *   *   *   0       R   ROM 10H @D16 second half
?   ?   ?   ?   ?   0   0   1   1   0   *   *   *   *   *   *   *   *   *   *   *   *   *   1       R   ROM 10L @D10 second half
?   ?   ?   ?   ?   0   0   1   1   1   *   *   *   *   *   *   *   *   *   *   *   *   *   0       R   Open Bus (socket @D17 second half)
?   ?   ?   ?   ?   0   0   1   1   1   *   *   *   *   *   *   *   *   *   *   *   *   *   1       R   Open Bus (socket @D11 second half)
?   ?   ?   ?   ?   0   1   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *       RW  RAM (4164 DRAMs; these have parity as well, which is checked in the i/o area somehow?)
a23 a22 a21 a20 a19 a18 a17 a16 a15 a14 a13 a12 a11 a10 a9  a8  a7  a6  a5  a4  a3  a2  a1  (a0 via UDS/LDS)
1   1   1   1   1   1   1   1   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   ?   *  Iiii ?   SPY BUS and FEP peripherals for OLD FEP hardware, not the NFEP we have here; the map of this area is listed in octal on pdf page 399 of us patent 4887235. The NFEP map is certainly not the same, but is probably similar. I is device ID from DEVNUM pal
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   1   0   0   *   *   *   *  0010 RW  SPY-CMEM (writes CMEM WD, reads UIR)
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   1   0   0   1   1   1   *  0010 W   SPY-SQ-CTL
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   1   0   0   1   1   1   0  0010 R   SPY-BOARD-ID (read a given board's id prom, board select is in U AMRA register)
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   1   0   1   0   0   0   *  0010 R   SPY-SQ-STATUS
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   1   0   1   0   0   1   *  0010 ?   SPY-NEXT-CPC
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   1   0   1   0   0   1   0? 0010 ?   SPY-TASK
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   1   0   1   0   0   1   1  0010 R   SPY-CTOS-HIGH
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   1   0   1   0   1   1   0? 0010 R   SPY-OPC
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   1   1   0   0   0   0   0? 0010 W   SPY-MC-CONTROL
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   1   1   0   0   0   0   0  0010 R   SPY-MC-ID
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   1   1   0   0   0   0   1  0010 R   SPY-MC-ERROR-STATUS
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   1   1   0   0   0   1   0  0010 R   SPY-ECC-SYNDROME
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   1   1   0   0   0   1   1  0010 R   SPY-ECC-ADDRESS
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   1   1   0   0   1   0   *? 0010 R   SPY-MC-STATUS
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   1   1   0   1   0   0   0  0010 W   SPY-NET-SELECT
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   1   1   0   1   0   0   1  0010 W   SPY-NET-CONTROL
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   1   1   1   x   x   x   x  0010 .   Open bus?
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   0   0   0   0   0   x?  0  0001 RW  MPSC-0-A
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   0   0   0   0   0   x?  1  0001 RW  MPSC-0-B
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   0   0   0   0   1   x?  0  0001 RW  MPSC-1-A
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   0   0   0   0   1   x?  1  0001 RW  MPSC-1-B
                                          [       |           ]                                         HA5
                                          [   |           |           |           ]                     HA10
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   0   0   0   1   1   x?  *  0110 RW  SPY-DMA-HIGH-ADDRS
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   0   0   1   x?  x?  x?  *  0111 RW  SPY-DMA-CONTROLLER
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   0   1   *?  *?  *?  *?  *  0100 R   FEP-PADDLE-ID-PROM
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   1   0   *?  *?  *?  *?  *  0101 R   FEP-BOARD-ID-PROM
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   1   1   0   0   0   0   1  0011 RW  FEP-BOARD-ID-CONTROL
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   1   1   0   0   0   1   1  0011 RW  FEP-DMA-AND-CLOCK-CTL
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   1   1   0   0   1   0   0  0011 RW  FEP-DMA-CONTROL
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   1   1   0   0   1   0   1  0011 RW  FEP-PROC-CONTROL
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   1   1   0   1   0   0   *  1100 RW  FEP(SPY)-LBUS-CONTROL
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   1   1   0   1   1   0   *  1101 RW  FEP-SERIAL-BAUD-RATE-0
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   1   1   0   1   1   1   *  1101 RW  FEP-SERIAL-BAUD-RATE-1
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   1   1   1   0   0   0   *  1001 RW  FEP-HSB-CONTROL
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   1   1   1   0   0   1   *  1001 RW  FEP-HSB-DATA
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   1   1   1   0   1   0   *  1001 RW  FEP-HSB-POINTER
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   1   1   1   0   1   1   x  1001 .   Open bus?
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   1   1   1   1   0   x?  *  1110 RW  P-PORT
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   1   1   1   1   1   0   *  1010 RW  NanoFEP i8749
1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   1   1   1   1   1   1   1   *  1011 RW  CART
                                                                                               0000 unused/no device selected
                                                                                               1000 unused
                                                                                               1111 unused
a23 a22 a21 a20 a19 a18 a17 a16 a15 a14 a13 a12 a11 a10 a9  a8  a7  a6  a5  a4  a3  a2  a1  (a0 via UDS/LDS)
Now for stuff the 3670 nfep code actually accesses:
              |               |               |               |               |               |     hex
          |           |           |           |           |           |           |           |     octal
Soft reset
[:maincpu] ':maincpu' (000310): unmapped program memory write to FF00E0 = 0202 & 00FF (FEP-BOARD-ID-CONTROL)
[:maincpu] ':maincpu' (00A2AA): unmapped program memory read from FF00B0 & FF00 (FEP-PADDLE-ID-PROM)
[:maincpu] ':maincpu' (00A2EA): unmapped program memory write to FF018A = 5555 & FFFF (unknown)
[:maincpu] ':maincpu' (006B48): unmapped program memory write to 000000 = 000A & FFFF off into the weeds?
[:maincpu] ':maincpu' (006B4C): unmapped program memory write to 000002 = 0000 & FFFF "
[:maincpu] ':maincpu' (006B4C): unmapped program memory write to 000004 = 0000 & FFFF "
[:maincpu] ':maincpu' (006B50): unmapped program memory write to 000006 = 0000 & FFFF "
[:maincpu] ':maincpu' (006B50): unmapped program memory write to 000008 = 0000 & FFFF "
[:maincpu] ':maincpu' (006B58): unmapped program memory write to 00000A = FFFF & FFFF "
[:maincpu] ':maincpu' (006B60): unmapped program memory write to FFFFFE = FFFF & FFFF "
[:maincpu] ':maincpu' (00A2AA): unmapped program memory read from FF00B0 & FF00

currently dies at context switch code loaded to ram around 38EE0, see patent 4887235 pages 441/442

*/

void symbolics_state::m68k_mem(address_map &map)
{
	map.unmap_value_high();
	//map(0x000000, 0x01ffff).rom(); /* ROM lives here */
	map(0x000000, 0x00bfff).rom();
	// 0x00c000-0x00ffff is open bus but decoded/auto-DTACKed, does not cause bus error
	map(0x010000, 0x01bfff).rom();
	// 0x01c000-0x01ffff is open bus but decoded/auto-DTACKed, does not cause bus error
	map(0x020000, 0x03ffff).ram().share("fepdram"); /* Local FEP ram seems to be here? there are 18 mcm4164s on the pcb which probably map here, plus 2 parity bits? */
	//map(0x020000, 0x03ffff).rw(FUNC(symbolics_state::ram_parity_hack_r), FUNC(symbolics_state::ram_parity_hack_w));
	//map(0x020002, 0x03ffff).ram().region("fepdram", 0); /* Local FEP ram seems to be here? there are 18 mcm4164s on the pcb which probably map here, plus 2 parity bits? */
	// 2x AM9128-10PC 2048x8 SRAMs @F7 and @G7 map somewhere
	// 6x AM2148-50 1024x4bit SRAMs @F22-F27 map somewhere
	//map(0x040000, 0xffffff).r(m_maincpu, FUNC(m68000_device::berr_r));
	//map(0x800000, 0xffffff).ram(); /* paged access to lispm ram? */
	//FF00B0 is readable, may be to read the MC/SQ/DP/AU continuity lines?
	map(0xff00a0, 0xff00bf).rom().region("fep_paddle_prom",0);
	map(0xff00c0, 0xff00df).rom().region("fep_prom",0);
	//FF00E1 is writable, may control the LBUS_POWER_RESET line, see http://bitsavers.trailing-edge.com/pdf/symbolics/3600_series/Lisp_Machine_Hardware_Memos.pdf page 90
	// or may be writing to FEP-LBUS-CONTROL bit 0x02 DOORBELL INT ENABLE ?
	// or may actually be setting LBUS_ID_REQ as the patent map shows
	//FF018A is writable, gets 0x5555 written to it
}

/******************************************************************************
 Input Ports
******************************************************************************/
static INPUT_PORTS_START( symbolics )
INPUT_PORTS_END

/******************************************************************************
 Machine Drivers
******************************************************************************/
/*TIMER_CALLBACK_MEMBER(symbolics_state::outfifo_read_cb)
{
    uint16_t data;
}
*/

/* Driver init: stuff that needs setting up which isn't directly affected by reset */
void symbolics_state::init_symbolics()
{
}

/* start */
void symbolics_state::machine_start()
{
	//save_item(NAME(m_parity_error_has_occurred));
	//m_outfifo_timer = timer_alloc(FUNC(symbolics_state::outfifo_read_cb), this);
}

/* reset */
void symbolics_state::machine_reset()
{
	/*for(int i=0; i < 0x20000; i++)
	    m_parity_error_has_occurred[i] = false;
	*/
}

void symbolics_state::symbolics(machine_config &config)
{
	/* basic machine hardware */
	// per page 159 of http://bitsavers.trailing-edge.com/pdf/symbolics/3600_series/Lisp_Machine_Hardware_Memos.pdf:
	//XTALS: 16MHz @H11 (68k CPU clock)
	//       4.9152MHz @J5 (driving the two MPSCs serial clocks)
	//       66.67MHz @J10 (main lispcpu/system clock)
	M68000(config, m_maincpu, XTAL(16'000'000)/2); /* MC68000L8 @A27; clock is derived from the 16Mhz xtal @ H11, verified from patent */
	m_maincpu->set_addrmap(AS_PROGRAM, &symbolics_state::m68k_mem);

	//ADD ME:
	// Framebuffer
	// DMA Controller
	// I8274 MPSC #1 (synchronous serial for keyboard)
	// I8274 MPSC #2 (EIA/debug console?)
}

/******************************************************************************
 ROM Definitions
******************************************************************************/

ROM_START( s3670 )
	ROM_REGION16_BE(0x40000,"maincpu", ROMREGION_ERASEFF)
	// the older 'FEP V24' has similar roms but a different hw layout and memory map
	ROM_SYSTEM_BIOS( 0, "v127", "Symbolics 3600 L-Machine 'NFEP V127'")
	ROMX_LOAD("00h.127.27c128.d13", 0x00000, 0x2000, CRC(b8d7c8da) SHA1(663a09359f5db63beeac00e5c2783ccc25b94250), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "00H.127" @D13
	ROM_CONTINUE( 0x10000, 0x2000 )
	ROMX_LOAD("00l.127.27128.d7", 0x00001, 0x2000, CRC(cc7bae9a) SHA1(057538eb821c4d00dde19cfe5136ccc0aee43800), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "00L.127" @D7
	ROM_CONTINUE( 0x10001, 0x2000 )
	ROMX_LOAD("04h.127.27128.d14", 0x04000, 0x2000, CRC(e01a717b) SHA1(b87a670f7be13553485ce88fad5fcf90f01473c4), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "04H.127" @D14
	ROM_CONTINUE( 0x14000, 0x2000 )
	ROMX_LOAD("04l.127.27128.d8", 0x04001, 0x2000, CRC(68d169fa) SHA1(d6fab3132fca332a9bedb174fddf5fc8c69d05b6), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "04L.127" @D8
	ROM_CONTINUE( 0x14001, 0x2000 )
	ROMX_LOAD("10h.127.27128.d16", 0x08000, 0x2000, CRC(2ea7a70d) SHA1(61cc97aada028612c24d788d946d77e82116cf30), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "10H.127" @D16
	ROM_CONTINUE( 0x18000, 0x2000 )
	ROMX_LOAD("10l.127.27c128.d10", 0x08001, 0x2000, CRC(b8ddb3c8) SHA1(e6c3b96340c5c767ef18abf48b73fa8e5d7353b9), ROM_SKIP(1) | ROM_BIOS(0)) // Label: "10L.127" @D10
	ROM_CONTINUE( 0x18001, 0x2000 )
	// D17, D11 are empty sockets; these would map to 0x0c000-0ffff and 0x1c000-0x1ffff; these are verified from real hardware to read as 0xFF, so there must be pull-up resistors on the EPROM bus/auto-DTACK area
	ROM_REGION16_BE( 0x20,"fep_paddle_prom", 0)
	ROM_LOAD("fpa-458.bin", 0x0000, 0x0020, CRC(5e034b33) SHA1(fea84183825013b2adc290f71d97e5cffd0cf7fd)) // nFEP Paddle S/N 458
	ROM_REGION16_BE( 0x20,"fep_prom", 0)
	ROM_LOAD("fep-3973.d5", 0x0000, 0x0020, CRC(29910aa0) SHA1(433ea99b8e21055abb80bebc1d7dfc9e3aacd097)) // nFEP S/N 3973
	// note: load all the PLAs, PALs and PROMs here

	// picture is at https://4310b1a9-a-11c96037-s-sites.googlegroups.com/a/ricomputermuseum.org/home/Home/equipment/symbolics-3645/Symbolics_3645_FEP.jpg
	/*
	    LBBUFA.4 mb7124   @A6 <- 4887235 page 630 has LBBUFC.UCODE rev27 \
	    LBBUFB.4 mb7124   @A7 <- 4887235 page 630 has LBBUFC.UCODE rev27  > all 3 of these are stored in the same file
	    LBBUFC.4 mb7124   @A9 <- 4887235 page 630 has LBBUFC.UCODE rev27 /
	    LBAAR.4           @A12 <- 4887235 page 625 has LBAAR rev4, pal16l8
	    LBPAR.4A          @A13 <- 4887235 page 624 has LBPAR rev9, pal16l8
	    PROCA.4  pal16R8A @A25 <- 4887235 page 621 has PROCA rev8, pal16r8
	    HSADR.4  pal1???? @C4  <- 4887235 page 626 has HSADR rev9, pal16r4
	    DYNMEM.5 pal16R8A @C20 <- 4887235 page 627 has DYNMEM rev15, pal16r8
	    PCDYNCTL          @C21 <- 4887235 page 628 has DYNCTL rev7, pal16l8
	    REQSEL.4A         @C22 <- 4887235 page 620 has REQSEL rev28, pal16l8
	    DV2ACK   pal16L8A @C23 <- 4887235 page 629 has DEVACK rev?, pal16l8 <- controls fep mem map; this is DIFFERENT between FEP v24 (DEVACK) and NFEP v127 (DV2ACK)
	    PROC.4   pal?     @C25 <- 4887235 page 622 has PROC rev4, pal16l8
	    UDMAHA.4 pal?     @D3 <- 4887235 page 619 has UDMAHA rev2, pal16l8
	    FEP 3973 16pprom? @D5 <- the number after FEP is the serial number of the FEP board (and is also supposedly stored in the prom), prom is readable at one of the local-io addresses
	    HSRQ.4   pal      @D6 <- 4887235 page 626 has HSRQ rev?, pal16l8
	    d7, d8, d10 are EPROMs, see above
	    d11 is empty socket marked 2764
	    d13, d14, d16 are EPROMs, see above
	    d17 is empty socket marked 2764
	    DV2NUM            @E21 <- 4887235 page 629 has DEVNUM rev8, pal16l8 <- controls fep mem map; this is DIFFERENT between FEP v24 (DEVNUM) and NFEP v127 (DV2NUM)
	    LBBD.4   pal16L8A @G18 <- 4887235 page 624 has LBBD rev6, pal16l8
	    PAGTAG.5          @H20 <- 4887235 page 623 has PAGTAG rev5, pal16l8
	    UDMABC.4 pal      @I4 <- 4887235 page 619 has UDMABC rev3, pal16l8
	    SERDMA.4          @I8 <- 4887235 page 620 has SERDMA rev8, pal16l8
	    SERIAB.4          @I9 <- 4887235 page 620 has SERIAB rev2, pal16l8
	    LBARB.4           @I18 <- 4887235 page 625 has LBARB rev1, pal16l8
	    SERCTL.4          @K6 <- 4887235 page 620 has SERCTL rev4, pal16l8
	*/

ROM_END

} // anonymous namespace


/******************************************************************************
 Drivers
******************************************************************************/

//    YEAR  NAME   PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT            COMPANY      FULLNAME  FLAGS
COMP( 1984, s3670, 0,      0,      symbolics, symbolics, symbolics_state, init_symbolics, "Symbolics", "3670",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



synthex.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Elka Synthex synthesizer.

****************************************************************************/

#include "emu.h"
#include "bus/midi/midi.h"
#include "cpu/m6502/m6502.h"
#include "machine/6850acia.h"
#include "machine/clock.h"
#include "machine/nvram.h"
#include "machine/watchdog.h"


namespace {

class synthex_state : public driver_device
{
public:
	synthex_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_midiacia(*this, "midiacia")
		, m_dac_data_latched(0)
		, m_dac_input_select(0)
		, m_sh_latch(0)
	{
	}

	void synthex(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	u8 ram2_r(offs_t offset);
	void ram2_w(offs_t offset, u8 data);
	u8 ram3_r(offs_t offset);
	void ram3_w(offs_t offset, u8 data);
	void c1_w(u8 data);
	void c2_w(u8 data);
	void sh_w(u8 data);
	u8 kom_r();

	u8 cs1_komp_r();
	u8 cs2_komp_r();
	u8 cs3_komp_r();
	u8 cs4_komp_r();

	u8 seq_exts3_r();
	u8 seq_extkyb_r(offs_t offset);

	virtual void driver_start() override;

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<acia6850_device> m_midiacia;

	std::unique_ptr<u8[]> m_ram2_ptr;
	std::unique_ptr<u8[]> m_ram3_ptr;

	u16 m_dac_data_latched;
	u8 m_dac_input_select;
	u8 m_sh_latch;
};

void synthex_state::machine_start()
{
	m_ram2_ptr = make_unique_clear<u8[]>(0x400);
	m_ram3_ptr = make_unique_clear<u8[]>(0x400);
	subdevice<nvram_device>("ram2")->set_base(&m_ram2_ptr[0], 0x400);
	subdevice<nvram_device>("ram3")->set_base(&m_ram3_ptr[0], 0x400);

	m_midiacia->write_dcd(0);
	m_midiacia->write_cts(0);

	save_pointer(NAME(m_ram2_ptr), 0x400);
	save_pointer(NAME(m_ram3_ptr), 0x400);
	save_item(NAME(m_dac_data_latched));
	save_item(NAME(m_dac_input_select));
	save_item(NAME(m_sh_latch));
}

u8 synthex_state::ram2_r(offs_t offset)
{
	return bitswap<8>(m_ram2_ptr[bitswap<10>(offset, 6, 1, 3, 7, 2, 4, 5, 9, 8, 0)], 5, 6, 4, 3, 7, 2, 1, 0);
}

void synthex_state::ram2_w(offs_t offset, u8 data)
{
	m_ram2_ptr[bitswap<10>(offset, 6, 1, 3, 7, 2, 4, 5, 9, 8, 0)] = bitswap<8>(data, 3, 6, 7, 5, 4, 2, 1, 0);
}

u8 synthex_state::ram3_r(offs_t offset)
{
	return bitswap<8>(m_ram3_ptr[bitswap<10>(offset, 6, 1, 3, 7, 2, 4, 5, 9, 8, 0)], 5, 6, 4, 3, 7, 2, 1, 0);
}

void synthex_state::ram3_w(offs_t offset, u8 data)
{
	m_ram3_ptr[bitswap<10>(offset, 6, 1, 3, 7, 2, 4, 5, 9, 8, 0)] = bitswap<8>(data, 3, 6, 7, 5, 4, 2, 1, 0);
}

void synthex_state::c1_w(u8 data)
{
	// Schematic incorrectly crosses D4 and D5 between LS174 and DAC
	m_dac_data_latched = (m_dac_data_latched & 0x300) | (data & 0x3f) << 2;
}

void synthex_state::c2_w(u8 data)
{
	m_dac_data_latched = u16(data & 0xc0) << 2 | (m_dac_data_latched & 0x0fc);
	m_dac_input_select = data & 0x07;
}

void synthex_state::sh_w(u8 data)
{
	m_sh_latch = (data & 0xfc) >> 2;
}

u8 synthex_state::kom_r()
{
	return 0xff;
}

u8 synthex_state::cs1_komp_r()
{
	return 0xff;
}

u8 synthex_state::cs2_komp_r()
{
	return 0xff;
}

u8 synthex_state::cs3_komp_r()
{
	return 0xff;
}

u8 synthex_state::cs4_komp_r()
{
	return 0xff;
}

u8 synthex_state::seq_exts3_r()
{
	// N.B. bit 5 = IRQ status
	return 0xff;
}

u8 synthex_state::seq_extkyb_r(offs_t offset)
{
	return 0xff;
}

void synthex_state::mem_map(address_map &map)
{
	// The logical schematic contains some errors.
	// On the LS42 at 3C (which decodes A14 and A15), pins 5 and 9 are both labeled "XF2", which 2E uses to decode the ROM area.
	// On the LS138 at 2F (which decodes A10–A13), pin 5 is labeled "F1", the 0x1700–0x177f decode produced on pin 8 of 4F.
	// The wiring diagram shows that pin 5 of the former IC is in fact connected to pin 5 of the latter.
	map(0x0000, 0x03ff).ram();
	map(0x0400, 0x07ff).rw(FUNC(synthex_state::ram2_r), FUNC(synthex_state::ram2_w));
	map(0x0800, 0x0bff).rw(FUNC(synthex_state::ram3_r), FUNC(synthex_state::ram3_w));
	map(0x0e00, 0x0e01).mirror(0x1e0).w(m_midiacia, FUNC(acia6850_device::write));
	map(0x0e02, 0x0e03).mirror(0x1e0).r(m_midiacia, FUNC(acia6850_device::read));
	map(0x0e04, 0x0e04).mirror(0x1e3).r(FUNC(synthex_state::seq_exts3_r));
	map(0x0e18, 0x0e1f).mirror(0x1e0).r(FUNC(synthex_state::seq_extkyb_r));
	map(0x1260, 0x1260).mirror(0x199).r(FUNC(synthex_state::cs1_komp_r));
	map(0x1262, 0x1262).mirror(0x199).r(FUNC(synthex_state::cs2_komp_r));
	map(0x1264, 0x1264).mirror(0x199).r(FUNC(synthex_state::cs3_komp_r));
	map(0x1266, 0x1266).mirror(0x199).r(FUNC(synthex_state::cs4_komp_r));
	map(0x1460, 0x1460).mirror(0x1f).w(FUNC(synthex_state::c2_w));
	map(0x14e0, 0x14e0).mirror(0x1f).w(FUNC(synthex_state::c1_w));
	map(0x1660, 0x1660).mirror(0x1f).w(FUNC(synthex_state::sh_w));
	map(0x17e0, 0x17e0).mirror(0x1f).w("watchdog", FUNC(watchdog_timer_device::reset_w));
	map(0x1ae0, 0x1ae0).mirror(0x1f).r(FUNC(synthex_state::kom_r));
	// Four 4K ROM areas are decoded (in descending order), but the PCB has no space for a ROM4
	map(0xd000, 0xffff).rom().region("program", 0);
}

static INPUT_PORTS_START(synthex)
INPUT_PORTS_END

void synthex_state::synthex(machine_config &config)
{
	M6502(config, m_maincpu, 4_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &synthex_state::mem_map);

	WATCHDOG_TIMER(config, "watchdog"); // timeout based on RC delays

	NVRAM(config, "ram2", nvram_device::DEFAULT_ALL_0); // 2x 5114 + battery
	NVRAM(config, "ram3", nvram_device::DEFAULT_ALL_0); // 2x 5114 + battery

	//AD7520(config, "maindac");

	ACIA6850(config, m_midiacia);
	m_midiacia->txd_handler().set("midiout", FUNC(midi_port_device::write_txd));
	m_midiacia->irq_handler().set_inputline(m_maincpu, m6502_device::NMI_LINE);

	clock_device &midiclock(CLOCK(config, "midiclock", 4_MHz_XTAL / 8));
	midiclock.signal_handler().set(m_midiacia, FUNC(acia6850_device::write_txc));
	midiclock.signal_handler().append(m_midiacia, FUNC(acia6850_device::write_rxc));

	MIDI_PORT(config, "midiin", midiin_slot, nullptr).rxd_handler().set(m_midiacia, FUNC(acia6850_device::write_rxd));
	MIDI_PORT(config, "midiout", midiout_slot, nullptr);
}

void synthex_state::driver_start()
{
	u8 *program = memregion("program")->base();
	std::vector<u8> romdata(&program[0x0000], &program[0x3000]);
	for (offs_t offset = 0x0000; offset < 0x3000; offset++)
	{
		u16 addr_swapped = bitswap<14>(offset, 13, 12, 11, 7, 3, 1, 0, 2, 4, 5, 6, 8, 9, 10);
		u8 data_swapped = bitswap<8>(romdata[addr_swapped], 5, 1, 4, 3, 2, 0, 6, 7);
		program[offset] = data_swapped;
	}
}

ROM_START(synthex)
	ROM_REGION(0x3000, "program", ROMREGION_ERASEFF)
	ROM_LOAD("sx_fm3.1d", 0x2000, 0x1000, CRC(589e5274) SHA1(3aa0d42367449a2eefcdf50a374780c1eb3674ee))
	ROM_LOAD("sx_em3.1f", 0x1000, 0x1000, CRC(f5eb1df5) SHA1(eedcc709a61c4ba7fbea4805e0f5b86828d81825))
	ROM_LOAD("sx_t41.1h", 0x0000, 0x0800, CRC(520459b9) SHA1(2c2bd1399e99bdc7f81de4e642de19b19547a6f6))
	// ROM4 decode is not used
ROM_END

} // anonymous namespace


SYST(1981, synthex, 0, 0, synthex, synthex, synthex_state, empty_init, "Elka", "Synthex", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



sys1121.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Katherine Rohl
/*
 *  Motorola SYS1121 VME chassis.
 *
 *  The basic configuration was an MVME12x MPU,
 *  an MVME050 system controller, and an MVME320 disk
 *  controller.
 */

#include "emu.h"

#include "bus/vme/vme.h"
#include "bus/vme/vme_cards.h"


namespace {

class sys1121_state : public driver_device
{
public:
	sys1121_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
	{
	}

	void sys1121(machine_config &config);
};

// Input ports
INPUT_PORTS_START(sys1121)
INPUT_PORTS_END

void sys1121_state::sys1121(machine_config &config)
{
	VME(config, "vme");
	VME_SLOT(config, "vme:slot1", vme_cards, "mvme120");
	VME_SLOT(config, "vme:slot2", vme_cards, nullptr);
	VME_SLOT(config, "vme:slot3", vme_cards, nullptr);
	VME_SLOT(config, "vme:slot4", vme_cards, nullptr);
	VME_SLOT(config, "vme:slot5", vme_cards, nullptr);
	VME_SLOT(config, "vme:slot6", vme_cards, nullptr);
	VME_SLOT(config, "vme:slot7", vme_cards, nullptr);
	VME_SLOT(config, "vme:slot8", vme_cards, nullptr);
}

// This is a VME chassis so any ROMs are contained in the cards.
ROM_START(sys1121)
ROM_END

} // anonymous namespace

//    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY     FULLNAME    FLAGS
COMP( 1984, sys1121, 0,       0,      sys1121, sys1121, sys1121_state, empty_init, "Motorola", "SYS1121",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



sys16.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * National Semiconductor SYS16 Multi-User Development System for the NS16000 Microprocessor Family
 *
 * Sources:
 *  - NS16000 Databook, National Semiconductor Corporation, 1983 (OEE1894, DA-RRD35M94)
 *
 * TODO:
 *  - gpib, printer ports
 *  - sio/disk/tape boards
 *
 * WIP:
 *  - multiple startup diagnostic tests failing
 *  - can exit and run monitor
 *
 *  p/f test                notes
 *   p  0 - timer
 *   p  1 - mmu
 *   p  2 - fpu             passes with firmware hack
 *   p  3 - cpu ram
 *   f  4 - emb ram
 *   f  5 - pio (printer)
 *   f  6 - gpib            requires 8291 talker/listener "loopback" mode
 *   f  7 - sio board
 *   f  8 - tape subsystem
 *   f  9 - disk subsystem
 *
 * Interrupts       (default is level triggered, active low)
 *  0
 *  1 timer         (edge triggered)
 *  2
 *  3 disk          (edge triggered)
 *  4 tape          (edge triggered)
 *  5
 *  6 gpib          (active high)
 *  7
 *  8
 *  9 rx
 * 10 tx            (edge triggered)
 * 11 graf
 * 12 bpio          (edge triggered)
 * 13 apio          (edge triggered, active high)
 * 14 sio
 * 15 softclock
 */

#include "emu.h"

// cpu cluster
#include "cpu/ns32000/ns32000.h"
#include "machine/ns32081.h"
#include "machine/ns32082.h"
#include "machine/ns32202.h"

// other devices
#include "machine/i8255.h"
#include "machine/i8291a.h"
#include "machine/pit8253.h"
#include "machine/scn_pci.h"

// busses and connectors
#include "bus/rs232/rs232.h"

// disk board
#include "cpu/mcs48/mcs48.h"
#include "machine/i8243.h"

#define VERBOSE 0
#include "logmacro.h"

namespace {

class sys16_state : public driver_device
{
public:
	sys16_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_fpu(*this, "fpu")
		, m_mmu(*this, "mmu")
		, m_icu(*this, "icu")
		, m_pci(*this, "pci")
		, m_ppi(*this, "ppi")
		, m_pit(*this, "pit")
		, m_gtl(*this, "gtl")
		, m_console(*this, "console")
		, m_eprom(*this, "eprom")
		, m_hdc_cpu(*this, "hdc_cpu")
		, m_boot(*this, "boot")
	{
	}

	void sys16(machine_config &config);
	void init();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	template <unsigned ST> void cpu_map(address_map &map) ATTR_COLD;
	void hdc_mem(address_map &map) ATTR_COLD;

	required_device<ns32016_device> m_cpu;
	required_device<ns32081_device> m_fpu;
	required_device<ns32082_device> m_mmu;
	required_device<ns32202_device> m_icu;

	required_device<scn2651_device> m_pci;
	required_device<i8255_device> m_ppi;
	required_device<pit8253_device> m_pit;
	required_device<i8291a_device> m_gtl;

	required_device<rs232_port_device> m_console;

	required_region_ptr<u16> m_eprom;
	required_device<i8039_device> m_hdc_cpu;

	memory_view m_boot;

private:
	u8 nmi_r() { return m_nmi; }
	void dcr_w(u8 data);

	u16 opt_r();

	u8 m_dcr; // diagnostic control register
	u8 m_nmi; // nmi error register
};

void sys16_state::init()
{
	/*
	 * HACK: the FPU diagnostic test executes the following code after
	 * triggering an inexact result exception:
	 *
	 *   00e07db7  sfsr r0
	 *   00e07dba  movd 0x0,r1
	 *   00e07dc0  lfsr r1
	 *   00e07dc3  cmpw 0x66,r4
	 *
	 * Either the r0 at e07db7 should be r4, or the r4 at e07dc3 should be r0
	 * to correctly verify the FSR contains the expected trap type.
	 */
	m_eprom[0x7dc4 >> 1] = 0x00a0; // cmpw 0x66,r0
	m_eprom[0x930a >> 1] = 0x5e00; // checksum
}

void sys16_state::machine_start()
{
	save_item(NAME(m_dcr));
}

void sys16_state::machine_reset()
{
	m_dcr = 0;
	m_nmi = 6; // HACK: pass diagnostic

	m_boot.select(0);
}

template <unsigned ST> void sys16_state::cpu_map(address_map &map)
{
	if (ST == 0)
	{
		map(0x00'0000, 0x03'ffff).view(m_boot);
		m_boot[0](0x00'0000, 0x00'9fff).rom().region("eprom", 0);
		m_boot[1](0x00'0000, 0x03'ffff).ram();

		map(0x04'0000, 0x3f'ffff).noprw();

		map(0xa0'0c00, 0xa0'1bff).ram(); // serial board ram?

		map(0xe0'0000, 0xe0'9fff).rom().region("eprom", 0);

		// extended memory board control regs
		// ffe802 embcra
		// ffe602 embcra1
		// ffe402 embcra2
		// ffea00 ieee488 system controller
		map(0xff'ec00, 0xff'ec0f).umask16(0x00ff).m(m_gtl, FUNC(i8291a_device::map));
		map(0xff'ee00, 0xff'ee01).umask16(0x00ff).w(FUNC(sys16_state::dcr_w));

		// fff000 led control
		map(0xff'f200, 0xff'f200).umask16(0x00ff).r(FUNC(sys16_state::nmi_r));
		map(0xff'f400, 0xff'f401).r(FUNC(sys16_state::opt_r));
		map(0xff'f600, 0xff'f607).umask16(0x00ff).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
		map(0xff'f800, 0xff'f807).umask16(0x00ff).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
		map(0xff'fa00, 0xff'fa07).umask16(0x00ff).rw(m_pci, FUNC(scn2651_device::read), FUNC(scn2651_device::write));
	}

	map(0xff'fe00, 0xff'feff).umask16(0x00ff).m(m_icu, FUNC(ns32202_device::map<BIT(ST, 1)>));
}

void sys16_state::hdc_mem(address_map &map)
{
	map(0x0000, 0x0fff).rom().region("hdc_rom", 0);
}

static INPUT_PORTS_START(sys16)
INPUT_PORTS_END

void sys16_state::sys16(machine_config &config)
{
	NS32016(config, m_cpu, 6'000'000); // NS32016D-10
	m_cpu->set_addrmap(0, &sys16_state::cpu_map<0>);
	m_cpu->set_addrmap(4, &sys16_state::cpu_map<4>);
	m_cpu->set_addrmap(6, &sys16_state::cpu_map<6>);

	NS32081(config, m_fpu, 6'000'000); // NS32081D-10
	m_cpu->set_fpu(m_fpu);

	NS32082(config, m_mmu, 6'000'000); // NS32082D-10
	m_cpu->set_mmu(m_mmu);

	NS32202(config, m_icu, 18.432_MHz_XTAL / 10); // NS32202-10
	m_icu->out_int().set_inputline(m_cpu, INPUT_LINE_IRQ0).invert();

	I8255(config, m_ppi); // QP8255A

	PIT8253(config, m_pit); // P8253-5
	m_pit->set_clk<0>(1.8432_MHz_XTAL);
	m_pit->set_clk<1>(1.8432_MHz_XTAL);
	m_pit->set_clk<2>(1.8432_MHz_XTAL);
	m_pit->out_handler<0>().set(m_pci, FUNC(scn2651_device::rxc_w));
	m_pit->out_handler<1>().set(m_pci, FUNC(scn2651_device::txc_w));
	m_pit->out_handler<2>().set(m_icu, FUNC(ns32202_device::ir_w<1>));

	SCN2651(config, m_pci, 1.8432_MHz_XTAL); // INS2651N

	RS232_PORT(config, m_console, default_rs232_devices, "terminal");
	m_console->rxd_handler().set(m_pci, FUNC(scn2651_device::rxd_w));
	m_pci->txd_handler().set(m_console, FUNC(rs232_port_device::write_txd));

	I8291A(config, m_gtl, 6'000'000);
	m_gtl->int_write().set(m_icu, FUNC(ns32202_device::ir_w<6>));

	// TODO: serial controller board
	// MK3880BN-6 Z80-CPU
	// INS2651N (x8)
	// 5.0688MHz
	// MM2114N-15 1024x4 Static RAM (x8) -> 4096 bytes

	// TODO: hard disk controller board
	// INS8039J-11
	// 11.0MHz
	// INS8243N input/output expander
	// 25.0MHz
	// AM2901BPC (x2)
	// IDM2911ANC (x2) microprogram sequencer
	// AM93L422PCB (x8) 256 x 4-Bit Low Power TTL Bipolar IMOX RAM
	// AM25LS2569PC (x2) binary counter
	// N9403N (x2) 64-bit fifo buffer memory

	I8039(config, m_hdc_cpu, 11_MHz_XTAL);
	m_hdc_cpu->set_addrmap(AS_PROGRAM, &sys16_state::hdc_mem);
}

enum nmi_mask : u8
{
	NMI_POWER  = 0x01, // power fail
	NMI_ECC    = 0x02, // extended memory board error
	NMI_PARITY = 0x04, // ram parity error
};

enum dcr_bits : u8
{
	DCR_RAM    = 0, // ram switch
	DCR_RESET  = 1, // reset flop
	DCR_ION    = 2, // interrupt enable
	DCR_POWER  = 3, // power fail enable
	DCR_PARITY = 4, // parity error enable
	DCR_NMI    = 5, // nmi error enable
	DCR_PERROR = 6, // force parity errors
	DCR_XRESET = 7, // external reset low
};

void sys16_state::dcr_w(u8 data)
{
	LOG("dcr_w 0x%02x (%s)\n", data, machine().describe_context());

	if (BIT(data, DCR_RAM))
		m_boot.select(1);

	m_dcr = data;
}

enum opt_mask : u16
{
	OPT_EMB   = 0x0001, // extended memory board (0=256K, 1=1.2M)
	OPT_ECHO  = 0x0002, // echo serial to monitor
	OPT_DIAG  = 0x0004, // run diagnostics
	OPT_FPU   = 0x0100, // FPU installed
	OPT_MOVSU = 0x0200, // MOVSU instruction works
	OPT_WORK  = 0x0400, // SYS16 or workstation
	OPT_BOOT  = 0x0800, // auto boot unix (0=boot /vmunix, 1=monitor)
	OPT_DUMP  = 0x1000, // breakpoint (0=bpt, 1=dump)
	OPT_GPIB  = 0xe000, // GPIB address
};

// TODO: change to dip switches?
u16 sys16_state::opt_r()
{
	return OPT_WORK | OPT_MOVSU | OPT_FPU | OPT_DIAG | OPT_ECHO;
}

ROM_START(sys16)
	ROM_REGION16_LE(0xa000, "eprom", 0)

	ROM_SYSTEM_BIOS(0, "v3.4", "National 16032 ROM Monitor (Rev. 3.4) (kernel) Fri Jul 29 12:51:11 PDT 1983")
	ROMX_LOAD("7000__001d.u163", 0x0000, 0x1000, CRC(62190bba) SHA1(43dfa1f05ffe18540fc9642b00b1e9211d73e947), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("7000__006d.u184", 0x0001, 0x1000, CRC(a6ecae9e) SHA1(e98d038edac8083b487bebe176daf788f4713a88), ROM_BIOS(0) | ROM_SKIP(1))

	ROMX_LOAD("7000__002d.u164", 0x2000, 0x1000, CRC(5df7b3d1) SHA1(71f8961f2cb6d592034f3929d6abcda9d6a35212), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("7000__007d.u185", 0x2001, 0x1000, CRC(72117cb2) SHA1(cbac772399375a99075b85f18619fb671a99f3bd), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("7000__003d.u165", 0x4000, 0x1000, CRC(27ae56d2) SHA1(53b097eb8c56d49bc85d2954c2fa799ccd503484), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("7000__008d.u186", 0x4001, 0x1000, CRC(3909d280) SHA1(d0db32a804dfde3cd2fcd0b136726868d7592498), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("7000__004d.u166", 0x6000, 0x1000, CRC(34553048) SHA1(03c2bd7de8192412e4efea3b2d7ba14db18d65e8), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("7000__009d.u187", 0x6001, 0x1000, CRC(910119fd) SHA1(2c103cf5175585d9748877f71bcfaf0ca591e6f7), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("7000__005d.u167", 0x8000, 0x1000, CRC(f5243c77) SHA1(243780f652867bb70634b2df8c7a02d634675029), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("7000__010d.u188", 0x8001, 0x1000, CRC(508bbaf1) SHA1(ed8a7f5f65f1fb19c34cc7f5d2e2b65207c9a1c8), ROM_BIOS(0) | ROM_SKIP(1))

	ROM_REGION(0x1000, "hdc_rom", 0)
	ROM_LOAD("hdc_rom.u157", 0x0000, 0x800, CRC(cc17d74a) SHA1(b6604043527ad24c3472d84c5747ef378433629f))
	ROM_LOAD("hdc_rom.u158", 0x0800, 0x800, CRC(512bad81) SHA1(6bc262028a397bdf33951ebd775e42189b03b7bd))
ROM_END

} // anonymous namespace

/*   YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT  COMPANY                   FULLNAME  FLAGS */
COMP(1983, sys16, 0,      0,      sys16,   sys16, sys16_state, init, "National Semiconductor", "SYS16",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)



sys2900.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/******************************************************************************************

Systems Group (a division of Measurement Systems and Controls) System 2900 S100 computer

2009-05-12 Skeleton driver.

Photos: https://www.vintagecomputer.net/Systems-Group/2900/

The system contains 4 S100 boards, 2 8" floppies, enormous power supply,
1/4" thick mother board, 4 RS232 I/F boards, and an Australian made
"Computer Patch Board".

The S100 boards that make up the unit are:
            CPU board - Model CPC2810 Rev D
                    The board has a MK3880N-4 (Z80A CPU), CTC, 2x MK3884N-4 (Z80SIO), PIO.
                    It has a lot of jumpers and an 8 way DIP switch.
                    Crystals: 8.0000MHz, 4.91520MHz
            Memory board - Model HDM2800 Rev B
                    This contains 18 4164's and a lot of logic.
                    Again, a lot of jumpers, and several banks of links.
                    Also contains 2 large Motorola chips:
                    MC3480P (dynamic memory controller) and
                    MC3242AP (memory address multiplexer).
                    Dips: 2x 8-sw, 2x 4-sw, 1x 2-sw.
            Disk Controller board - Model FDC2800 Rev D
                    This board is damaged. The small voltage regulators
                    for +/- 12V have been fried.
                    Also, 3 sockets are empty - U1 and U38 (16 pin)
                    appear to be spares, and U10 (14 pin).
            8 port Serial I/O board - Model INO2808 Rev C
                    Room for 8 8251 USARTs.
                    I have traced out this board and managed to get it
                    working in another S100 system.

The "Computer Patch Board" seems to provide some sort of watch dog facility.

Status:
- Appears to be looping waiting for a disk

*****************************************************************************************/

#include "emu.h"

//#include "bus/s100/s100.h"
#include "cpu/z80/z80.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"

#include "emupal.h"
#include "screen.h"


namespace {

class sys2900_state : public driver_device
{
public:
	sys2900_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
	{ }

	void sys2900(machine_config &config);

private:
	uint32_t screen_update_sys2900(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	memory_passthrough_handler m_rom_shadow_tap;
	required_device<z80_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
};


void sys2900_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).ram().share("mainram");
	map(0xf000, 0xf7ff).rom().region("maincpu", 0);
}

void sys2900_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x20, 0x23).rw("sio1", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x24, 0x27).rw("pio", FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x28, 0x2b).rw("sio2", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x2c, 0x2f).rw("ctc", FUNC(z80ctc_device::read),     FUNC(z80ctc_device::write));
	map(0x80, 0x83); // unknown device, disk related?
	map(0xa0, 0xaf); // unknown device
	map(0xc0, 0xc3); // unknown device
}

/* Input ports */
static INPUT_PORTS_START( sys2900 )
INPUT_PORTS_END

void sys2900_state::machine_reset()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x0000, 0x07ff, m_rom);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0xf000, 0xf7ff,
			"rom_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// reinstall RAM over the ROM shadow
					m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x07ff, m_ram);
				}
			},
			&m_rom_shadow_tap);
}

void sys2900_state::machine_start()
{
}

uint32_t sys2900_state::screen_update_sys2900(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void sys2900_state::sys2900(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(8'000'000) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &sys2900_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &sys2900_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	screen.set_screen_update(FUNC(sys2900_state::screen_update_sys2900));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	Z80CTC(config, "ctc", 0);
	Z80PIO(config, "pio", 0);
	Z80SIO(config, "sio1", 0);
	Z80SIO(config, "sio2", 0);
}

/* ROM definition */
ROM_START( sys2900 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "104401cpc.bin", 0x0000, 0x0800, CRC(6c8848bc) SHA1(890e0578e5cb0e3433b4b173e5ed71d72a92af26)) // label says BE 5 1/4 107701
ROM_END

} // anonymous namespace

/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT          COMPANY          FULLNAME       FLAGS
COMP( 1981, sys2900, 0,      0,      sys2900, sys2900, sys2900_state, empty_init, "Systems Group", "System 2900", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



sys9002.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

Mannesmann Kienzle System 9002 Terminal

2017-08-17 Skeleton driver.

Chips used:
- Siemens SAB8085A-P
- NEC D8251AFC * 2
- NEC D4016C-3 * 4 + 2
- ST M2764A-4F1 * 4
- HD6845P

Chargen is missing.

Both uarts are programmed as synchronous. A conversion box is needed
to talk with RS-232.


****************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "video/mc6845.h"
#include "emupal.h"
#include "screen.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "bus/rs232/rs232.h"


namespace {

class sys9002_state : public driver_device
{
public:
	sys9002_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_vram(*this, "videoram")
		, m_palette(*this, "palette")
		, m_p_chargen(*this, "chargen")
	{ }

	void sys9002(machine_config &config);

private:
	MC6845_UPDATE_ROW(crtc_update_row);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	u8 port11_r();

	required_device<cpu_device> m_maincpu;
	required_shared_ptr<uint8_t> m_vram;
	required_device<palette_device> m_palette;
	required_region_ptr<u8> m_p_chargen;
};


void sys9002_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).rom(); // 4 * 4K ROM
	map(0x8000, 0x9fff).ram(); // 4 * 2k RAM
	map(0xa000, 0xa7ff).ram().share("videoram"); // 2k RAM
	map(0xc000, 0xc7ff).ram(); // attributes??
	map(0xe000, 0xefff).ram(); // ??
}

void sys9002_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x04, 0x04).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x05, 0x05).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x08, 0x09).rw("uart1", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x10, 0x11).r(FUNC(sys9002_state::port11_r)); //nopr();  // continuous read
	map(0x1c, 0x1d).rw("uart2", FUNC(i8251_device::read), FUNC(i8251_device::write));
}

u8 sys9002_state::port11_r()
{
	static u8 data = 0;
	data += 0x08;
	return data;
}

/* Input ports */
static INPUT_PORTS_START( sys9002 )
INPUT_PORTS_END

MC6845_UPDATE_ROW( sys9002_state::crtc_update_row )
{
	rgb_t const *const pens = m_palette->palette()->entry_list_raw();
	uint32_t *p = &bitmap.pix(y);

	for (uint16_t x = 0; x < x_count; x++)
	{
		uint16_t mem = (ma + x) & 0x7ff;
		uint8_t chr = m_vram[mem] & 0x7f;

		/* get pattern of pixels for that character scanline */
		uint8_t gfx = m_p_chargen[(chr<<4) | ra] ^ ((x == cursor_x) ? 0xff : 0);

		/* Display a scanline of a character (8 pixels) */
		*p++ = pens[BIT(gfx, 7)];
		*p++ = pens[BIT(gfx, 6)];
		*p++ = pens[BIT(gfx, 5)];
		*p++ = pens[BIT(gfx, 4)];
		*p++ = pens[BIT(gfx, 3)];
		*p++ = pens[BIT(gfx, 2)];
		*p++ = pens[BIT(gfx, 1)];
		*p++ = pens[BIT(gfx, 0)];
	}
}


void sys9002_state::sys9002(machine_config &config)
{
	/* basic machine hardware */
	I8085A(config, m_maincpu, XTAL(2'000'000)); // XTAL not visible on images
	m_maincpu->set_addrmap(AS_PROGRAM, &sys9002_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &sys9002_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // not correct
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	screen.set_size(32*8, 32*8);
	screen.set_visarea(0*8, 32*8-1, 2*8, 30*8-1);
	//GFXDECODE(config, "gfxdecode", m_palette, gfx_sys9002);
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	/* Devices */
	mc6845_device &crtc(MC6845(config, "crtc", XTAL(2'000'000))); // clk unknown
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(8);
	crtc.set_update_row_callback(FUNC(sys9002_state::crtc_update_row));

	clock_device &uart_clock(CLOCK(config, "uart_clock", 9600));
	uart_clock.signal_handler().set("uart1", FUNC(i8251_device::write_txc));
	uart_clock.signal_handler().append("uart1", FUNC(i8251_device::write_rxc));
	uart_clock.signal_handler().append("uart2", FUNC(i8251_device::write_txc));
	uart_clock.signal_handler().append("uart2", FUNC(i8251_device::write_rxc));

	i8251_device &uart1(I8251(config, "uart1", 0)); // sync
	uart1.txd_handler().set("rs232a", FUNC(rs232_port_device::write_txd));
	uart1.dtr_handler().set("rs232a", FUNC(rs232_port_device::write_dtr));
	uart1.rts_handler().set("rs232a", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, nullptr));
	rs232a.rxd_handler().set("uart1", FUNC(i8251_device::write_rxd));
	rs232a.dsr_handler().set("uart1", FUNC(i8251_device::write_dsr));
	rs232a.cts_handler().set("uart1", FUNC(i8251_device::write_cts));

	i8251_device &uart2(I8251(config, "uart2", 0)); // sync
	uart2.txd_handler().set("rs232b", FUNC(rs232_port_device::write_txd));
	uart2.dtr_handler().set("rs232b", FUNC(rs232_port_device::write_dtr));
	uart2.rts_handler().set("rs232b", FUNC(rs232_port_device::write_rts));
	uart2.rxrdy_handler().set_inputline("maincpu", I8085_RST55_LINE);

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, "terminal"));
	rs232b.rxd_handler().set("uart2", FUNC(i8251_device::write_rxd));
	rs232b.dsr_handler().set("uart2", FUNC(i8251_device::write_dsr));
	rs232b.cts_handler().set("uart2", FUNC(i8251_device::write_cts));
}

/* ROM definition */
ROM_START( sys9002 )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD( "55-040.bin", 0x0000, 0x2000, CRC(781eaca9) SHA1(1bdae2bcc43deaef2eb1d6ec302fbadbb779fd48))
	ROM_LOAD( "55-041.bin", 0x2000, 0x2000, CRC(0f89fe81) SHA1(2dc8de7dabaf11a150cfd34460c5b47612cf5e61))
	ROM_LOAD( "55-042.bin", 0x4000, 0x2000, CRC(e6fbc837) SHA1(fc11f6a6927709552bedf06b9eb0dc66e9a81264))
	ROM_LOAD( "55-048.bin", 0x6000, 0x2000, CRC(879ef945) SHA1(a54fc01ac26a3cd05f6d1e1139d6d99198556575))

	ROM_REGION( 0x0800, "chargen", 0 )  // chargen not dumped, using one from mbee for now
	ROM_LOAD("charrom.bin",  0x0000,  0x0800, BAD_DUMP CRC(b149737b) SHA1(a3cd4f5d0d3c71137cd1f0f650db83333a2e3597) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY               FULLNAME                FLAGS
COMP( 198?, sys9002, 0,      0,      sys9002, sys9002, sys9002_state, empty_init, "Mannesmann Kienzle", "System 9002 Terminal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



systec.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Miodrag Milanovic
/***************************************************************************

Systec Z80

More info:
 http://www.hd64180-cpm.de/html/systecz80.html

2010-08-30 Skeleton driver
2011-12-22 connected to a terminal [Robbbert]

Systec Platine 1
        SYSTEC 155.1L
        EPROM 2764 CP/M LOADER 155 / 9600 Baud
        Z8400APS CPU
        Z8420APS PIO
        Z8430APS CTC
        Z8470APS DART

Systec Platine 2
        SYSTEC 100.3B
        MB8877A FDC Controller
        FDC9229BT SMC 8608
        Z8410APS DMA
        Z8420APS PIO

        MB8877A Compatible FD1793

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/z80sio.h"
#include "machine/clock.h"
#include "bus/rs232/rs232.h"


namespace {

class systec_state : public driver_device
{
public:
	systec_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, "mainram")
	{ }

	void systec(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_shared_ptr<u8> m_ram;
};

void systec_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).ram().share("mainram");
}

void systec_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x68, 0x6b); // fdc?
	map(0x6c, 0x6c); // motor control?
	map(0xc4, 0xc7).rw("sio", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
}

/* Input ports */
static INPUT_PORTS_START( systec )
INPUT_PORTS_END

void systec_state::machine_reset()
{
	uint8_t *m = memregion("roms")->base();
	memcpy(m_ram, m, 0x2000);
}


void systec_state::systec(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(16'000'000) / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &systec_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &systec_state::io_map);

	clock_device &uart_clock(CLOCK(config, "uart_clock", 153600));
	uart_clock.signal_handler().set("sio", FUNC(z80sio_device::txca_w));
	uart_clock.signal_handler().append("sio", FUNC(z80sio_device::rxca_w));

	/* Devices */
	z80sio_device& sio(Z80SIO(config, "sio", XTAL(4'000'000)));
	//sio2.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0); // no evidence of a daisy chain because IM2 is not set
	sio.out_txda_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	sio.out_dtra_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	sio.out_rtsa_callback().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("sio", FUNC(z80sio_device::rxa_w));
	rs232.cts_handler().set("sio", FUNC(z80sio_device::ctsa_w));
}


/* ROM definition */
ROM_START( systec )
	ROM_REGION( 0x2000, "roms", 0 )
	ROM_LOAD( "systec.bin",   0x0000, 0x2000, CRC(967108ab) SHA1(a414db032ca7db0f9fdbe22aa68a099a93efb593))
ROM_END

} // anonymous namespace


/* Driver */

//   YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY   FULLNAME      FLAGS
COMP(19??, systec, 0,      0,      systec,  systec, systec_state, empty_init, "Systec", "Systec Z80", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



systel1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for Systel System 1/System 100 computer.

    This minimalistic luggable computer has a green-screen monitor and
    5¼-inch floppy drive (Shugart SA455) built into the unit. The detached
    keyboard may be replaced with a standard electric typewriter.

***************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/clock.h"
#include "machine/input_merger.h"
#include "machine/i8251.h"
#include "machine/i8257.h"
#include "machine/wd_fdc.h"
#include "video/i8275.h"
#include "screen.h"


namespace {

class systel1_state : public driver_device
{
public:
	systel1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_dmac(*this, "dmac")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:0")
		, m_rom(*this, "bootrom")
		, m_chargen(*this, "chargen")
	{
	}

	void systel1(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void hrq_w(int state);
	u8 memory_r(offs_t offset);
	void memory_w(offs_t offset, u8 data);
	void floppy_control_w(u8 data);
	void rts_w(int state);
	void dtr_w(int state);

	I8275_DRAW_CHARACTER_MEMBER(draw_character);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<i8257_device> m_dmac;
	required_device<fd1797_device> m_fdc;
	required_device<floppy_connector> m_floppy;
	required_region_ptr<u8> m_rom;
	required_region_ptr<u8> m_chargen;

	std::unique_ptr<u8[]> m_ram;

	bool m_boot_read = false;
};

void systel1_state::machine_start()
{
	m_ram = make_unique_clear<u8[]>(0x10000);
	m_fdc->set_floppy(m_floppy->get_device());

	save_item(NAME(m_boot_read));
	save_pointer(NAME(m_ram), 0x10000);
}

void systel1_state::machine_reset()
{
	m_boot_read = true;
}

void systel1_state::hrq_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state);
	m_dmac->hlda_w(state);
}

u8 systel1_state::memory_r(offs_t offset)
{
	if (m_boot_read && offset < 0x4000)
		return m_rom[offset & 0x7ff];
	else
		return m_ram[offset];
}

void systel1_state::memory_w(offs_t offset, u8 data)
{
	m_ram[offset] = data;
}

I8275_DRAW_CHARACTER_MEMBER(systel1_state::draw_character)
{
	using namespace i8275_attributes;
	u8 dots = BIT(attrcode, LTEN) ? 0xff : BIT(attrcode, VSP) ? 0 : m_chargen[(charcode << 4) | linecount];
	if (BIT(attrcode, RVV))
		dots ^= 0xff;

	for (int i = 0; i < 7; i++)
	{
		bitmap.pix(y, x + i) = BIT(dots, 7) ? rgb_t::white() : rgb_t::black();
		dots <<= 1;
	}
}

void systel1_state::floppy_control_w(u8 data)
{
	if (m_floppy->get_device() != nullptr)
		m_floppy->get_device()->ss_w(!BIT(data, 0));

	m_boot_read = false;
}

void systel1_state::rts_w(int state)
{
	m_fdc->mr_w(state);
	if (m_floppy->get_device() != nullptr)
		m_floppy->get_device()->mon_w(!state);
}

void systel1_state::dtr_w(int state)
{
	// probably floppy-related
}

void systel1_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(systel1_state::memory_r), FUNC(systel1_state::memory_w));
}

void systel1_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x00).portr("JUMPERS");
	map(0x10, 0x11).rw("usart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x20, 0x29).rw(m_dmac, FUNC(i8257_device::read), FUNC(i8257_device::write));
	map(0x30, 0x33).rw(m_fdc, FUNC(fd1797_device::read), FUNC(fd1797_device::write));
	map(0x40, 0x41).rw("crtc", FUNC(i8276_device::read), FUNC(i8276_device::write));
	map(0x70, 0x70).w(FUNC(systel1_state::floppy_control_w));
}

static INPUT_PORTS_START(systel1)
	PORT_START("JUMPERS")
	PORT_DIPNAME(0x01, 0x00, "System Mode") // TYPE/WP switch on the front of the unit
	PORT_DIPSETTING(0x01, "Typewriter")
	PORT_DIPSETTING(0x00, "Word Processor")
	PORT_DIPNAME(0x02, 0x00, "Screen Refresh") // probably at J6
	PORT_DIPSETTING(0x02, "50 Hz")
	PORT_DIPSETTING(0x00, "60 Hz")
INPUT_PORTS_END

static void systel1_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

static DEVICE_INPUT_DEFAULTS_START(keyboard)
	DEVICE_INPUT_DEFAULTS("RS232_TXBAUD", 0xff, RS232_BAUD_1200)
DEVICE_INPUT_DEFAULTS_END

void systel1_state::systel1(machine_config &config)
{
	Z80(config, m_maincpu, 10.8864_MHz_XTAL / 4); // Z8400A; clock verified
	m_maincpu->set_addrmap(AS_PROGRAM, &systel1_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &systel1_state::io_map);

	input_merger_device &mainint(INPUT_MERGER_ANY_HIGH(config, "mainint"));
	mainint.output_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	I8257(config, m_dmac, 10.8864_MHz_XTAL / 4); // P8257-5
	m_dmac->out_hrq_cb().set(FUNC(systel1_state::hrq_w));
	m_dmac->in_memr_cb().set(FUNC(systel1_state::memory_r));
	m_dmac->out_memw_cb().set(FUNC(systel1_state::memory_w));
	m_dmac->in_ior_cb<1>().set(m_fdc, FUNC(fd1797_device::data_r));
	m_dmac->out_iow_cb<1>().set(m_fdc, FUNC(fd1797_device::data_w));
	m_dmac->out_iow_cb<2>().set("crtc", FUNC(i8276_device::dack_w));

	i8251_device &usart(I8251(config, "usart", 10.8864_MHz_XTAL / 4)); // AMD P8251A
	usart.rxrdy_handler().set("mainint", FUNC(input_merger_device::in_w<0>));
	usart.rts_handler().set(FUNC(systel1_state::rts_w));
	usart.dtr_handler().set(FUNC(systel1_state::dtr_w));

	clock_device &baudclock(CLOCK(config, "baudclock", 2_MHz_XTAL / 104)); // 19.23 kHz verified; rate probably fixed
	baudclock.signal_handler().set("usart", FUNC(i8251_device::write_rxc));
	baudclock.signal_handler().append("usart", FUNC(i8251_device::write_txc));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_raw(10.8864_MHz_XTAL, 672, 0, 560, 270, 0, 250);
	screen.set_screen_update("crtc", FUNC(i8276_device::screen_update));

	i8276_device &crtc(I8276(config, "crtc", 10.8864_MHz_XTAL / 7)); // WD8276PL-00
	crtc.set_screen("screen");
	crtc.set_character_width(7);
	crtc.set_display_callback(FUNC(systel1_state::draw_character));
	crtc.drq_wr_callback().set(m_dmac, FUNC(i8257_device::dreq2_w));
	crtc.irq_wr_callback().set("mainint", FUNC(input_merger_device::in_w<1>));

	FD1797(config, m_fdc, 2_MHz_XTAL / 2);
	m_fdc->drq_wr_callback().set(m_dmac, FUNC(i8257_device::dreq1_w));

	FLOPPY_CONNECTOR(config, m_floppy, systel1_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);
	m_floppy->set_fixed(true);
	m_floppy->enable_sound(true);

	rs232_port_device &kb(RS232_PORT(config, "kb", default_rs232_devices, "keyboard"));
	kb.set_option_device_input_defaults("keyboard", DEVICE_INPUT_DEFAULTS_NAME(keyboard));
	kb.rxd_handler().set("usart", FUNC(i8251_device::write_rxd));
}

// RAM: 8x MCM6665AP20
// XTALs: 10.8864 (Y1), 2.000 (Y2)
// Silkscreened on PCB: "SYSTEL / SYSTEM 1 / 1031-3101-00 REV H"
ROM_START(systel100)
	ROM_REGION(0x800, "bootrom", 0) // TMS2716JL-45
	ROM_LOAD("u11.bin", 0x000, 0x800, CRC(29f1414a) SHA1(c87adfaaec45d0e5f4cf5946d2f04de0332b3413))

	ROM_REGION(0x800, "chargen", 0) // TMS2516JL-45
	ROM_LOAD("u16.bin", 0x000, 0x800, CRC(61a8d742) SHA1(69dada638a17353f91bff34a1e2319a35d8a3ebf))
ROM_END

} // anonymous namespace


COMP(198?, systel100, 0, 0, systel1, systel1, systel1_state, empty_init, "Systel Computers", "System 100", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



t7000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Preliminary driver for Wicat T7000 terminal.

    This fairly typical ASCII video display terminal was sold for use with
    the Wicat System 100. It shows a basic 80x25 monochrome text display
    with an optional status line, and supports the VT52 and ANSI command
    sets. Keytronic Model L2207 is the specified keyboard. Settings can be
    saved to nonvolatile memory by typing "PERM" (all caps) in Set-Up mode.

    Currently the optional touch panel is not supported.

****************************************************************************/

#include "emu.h"

#include "bus/keytronic/keytronic.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/74259.h"
#include "machine/input_merger.h"
#include "machine/scn_pci.h"
#include "machine/x2212.h"
#include "video/i8275.h"

#include "screen.h"


namespace {

class t7000_state : public driver_device
{
public:
	t7000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mainint(*this, "mainint")
		, m_outlatch(*this, "outlatch")
		, m_pci(*this, "pci%u", 0U)
		, m_crtc(*this, "crtc%u", 0U)
		, m_vram(*this, "vram")
		, m_attr_vram(*this, "attr_vram", 0x4000, ENDIANNESS_LITTLE)
		, m_vram_view(*this, "vram")
		, m_chargen(*this, "chargen")
		, m_vblint(false)
		, m_attr_latch(0)
	{
	}

	void t7000(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	I8275_DRAW_CHARACTER_MEMBER(display_character);

	void vram_w(offs_t offset, u8 data);
	u8 vram_dma_r(offs_t offset);
	void attr_latch_w(u8 data);
	u8 vblint_status_r();
	void vblint_enable_w(int state);
	void dma_enable_w(int state);
	void vblint_w(int state);
	void crtc_combined_w(offs_t offset, u8 data);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<input_merger_device> m_mainint;
	required_device<ls259_device> m_outlatch;
	required_device_array<scn_pci_device, 2> m_pci;
	required_device_array<i8275_device, 2> m_crtc;
	required_shared_ptr<u8> m_vram;
	memory_share_creator<u8> m_attr_vram;
	memory_view m_vram_view;
	required_region_ptr<u8> m_chargen;

	bool m_vblint;
	u8 m_attr_latch;
};

void t7000_state::machine_start()
{
	save_item(NAME(m_vblint));
	save_item(NAME(m_attr_latch));
}

I8275_DRAW_CHARACTER_MEMBER(t7000_state::display_character)
{
	// TODO: blinking and blanked characters
	using namespace i8275_attributes;
	u16 dots = 0;
	if (!BIT(attrcode, VSP))
	{
		if (BIT(attrcode, LTEN + 8) || (BIT(charcode, 12) && linecount == 10))
			dots = 0x3ff; // underscore
		else
			dots = m_chargen[(charcode & 0x7f) << 4 | linecount] << 1;
		if (BIT(attrcode, RVV + 8) || BIT(charcode, 11))
			dots ^= 0x3ff; // reverse video
	}

	rgb_t fg = rgb_t::white();
	rgb_t bg = rgb_t::black();
	if (m_outlatch->q2_r())
	{
		using std::swap;
		swap(fg, bg);
	}
	if (BIT(charcode, 8))
		fg = rgb_t(0xc0, 0xc0, 0xc0); // reduced intensity

	for (int i = 0; i < 10; i++)
	{
		bitmap.pix(y, x + i) = BIT(dots, 9) ? fg : bg;
		dots <<= 1;
	}
}

void t7000_state::vram_w(offs_t offset, u8 data)
{
	m_vram[offset] = data;
	m_attr_vram[offset] = m_attr_latch;
}

u8 t7000_state::vram_dma_r(offs_t offset)
{
	u8 data = m_vram[offset];
	if (!machine().side_effects_disabled())
	{
		m_crtc[0]->dack_w(data);
		m_crtc[1]->dack_w(m_attr_vram[offset]);
	}
	return data;
}

void t7000_state::attr_latch_w(u8 data)
{
	m_attr_latch = data;
}

u8 t7000_state::vblint_status_r()
{
	// Only bit 5 is ever examined
	return m_vblint ? 0x20 : 0x00;
}

void t7000_state::vblint_enable_w(int state)
{
	if (!state && m_vblint)
	{
		m_vblint = false;
		m_mainint->in_w<4>(0);
	}
}

void t7000_state::dma_enable_w(int state)
{
	if (state)
		m_vram_view.select(0);
	else
		m_vram_view.disable();
}

void t7000_state::vblint_w(int state)
{
	if (state && m_outlatch->q0_r() && !m_vblint)
	{
		m_vblint = true;
		m_mainint->in_w<4>(1);
	}
}

void t7000_state::crtc_combined_w(offs_t offset, u8 data)
{
	for (auto &crtc : m_crtc)
		crtc->write(BIT(offset, 0), data);
}

void t7000_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).rom().region("program", 0);
	map(0x4000, 0x7fff).ram().share(m_vram).w(FUNC(t7000_state::vram_w));
	map(0x4000, 0x7fff).view(m_vram_view);
	m_vram_view[0](0x4000, 0x7fff).r(FUNC(t7000_state::vram_dma_r));
	map(0x8000, 0x803f).rw("novram", FUNC(x2210_device::read), FUNC(x2210_device::write));
}

void t7000_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x80, 0x80).r(FUNC(t7000_state::vblint_status_r));
	map(0xa1, 0xa1).nopr(); // touch panel status?
	map(0xb0, 0xb1).w(FUNC(t7000_state::crtc_combined_w));
	map(0xb2, 0xb3).rw(m_crtc[0], FUNC(i8275_device::read), FUNC(i8275_device::write));
	map(0xb4, 0xb5).rw(m_crtc[1], FUNC(i8275_device::read), FUNC(i8275_device::write));
	map(0xc0, 0xc3).rw(m_pci[0], FUNC(scn_pci_device::read), FUNC(scn_pci_device::write));
	map(0xd0, 0xd3).rw(m_pci[1], FUNC(scn_pci_device::read), FUNC(scn_pci_device::write));
	map(0xe0, 0xe0).w(FUNC(t7000_state::attr_latch_w));
	map(0xf0, 0xf7).w("outlatch", FUNC(ls259_device::write_d0));
}

static INPUT_PORTS_START(t7000)
INPUT_PORTS_END

void t7000_state::t7000(machine_config &config)
{
	Z80(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &t7000_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &t7000_state::io_map);

	INPUT_MERGER_ANY_HIGH(config, m_mainint).output_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	INPUT_MERGER_ALL_HIGH(config, "mainnmi").output_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);

	X2210(config, "novram"); // U39

	SCN2651(config, m_pci[0], 5.0688_MHz_XTAL);
	m_pci[0]->txd_handler().set("serial", FUNC(rs232_port_device::write_txd));
	m_pci[0]->rts_handler().set("serial", FUNC(rs232_port_device::write_rts));
	m_pci[0]->dtr_handler().set("serial", FUNC(rs232_port_device::write_dtr));
	m_pci[0]->txrdy_handler().set(m_mainint, FUNC(input_merger_device::in_w<0>));
	m_pci[0]->rxrdy_handler().set(m_mainint, FUNC(input_merger_device::in_w<1>));

	SCN2651(config, m_pci[1], 5.0688_MHz_XTAL);
	m_pci[1]->txd_handler().set("keyboard", FUNC(keytronic_connector_device::ser_in_w));
	m_pci[1]->txrdy_handler().set(m_mainint, FUNC(input_merger_device::in_w<2>));
	m_pci[1]->rxrdy_handler().set(m_mainint, FUNC(input_merger_device::in_w<3>));

	keytronic_connector_device &keyboard(KEYTRONIC_CONNECTOR(config, "keyboard", ascii_terminal_keyboards, "l2207"));
	keyboard.ser_out_callback().set(m_pci[1], FUNC(scn_pci_device::rxd_w));

	LS259(config, m_outlatch); // U43
	m_outlatch->q_out_cb<0>().set(FUNC(t7000_state::vblint_enable_w));
	m_outlatch->q_out_cb<1>().set(FUNC(t7000_state::dma_enable_w));
	m_outlatch->q_out_cb<5>().set("novram", FUNC(x2210_device::recall)).invert();
	m_outlatch->q_out_cb<6>().set("mainnmi", FUNC(input_merger_device::in_w<0>));
	m_outlatch->q_out_cb<7>().set("novram", FUNC(x2210_device::store));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green()); // "Phospher Type...P31-green (mediam persistence)" (manual p. 6-2)
	screen.set_raw(19.6608_MHz_XTAL, 1020, 0, 800, 324, 0, 300);
	screen.set_screen_update(m_crtc[0], FUNC(i8275_device::screen_update));

	I8276(config, m_crtc[0], 19.6608_MHz_XTAL / 10);
	m_crtc[0]->set_character_width(10);
	m_crtc[0]->set_display_callback(FUNC(t7000_state::display_character));
	m_crtc[0]->drq_wr_callback().set("mainnmi", FUNC(input_merger_device::in_w<1>));
	m_crtc[0]->vrtc_wr_callback().set(FUNC(t7000_state::vblint_w));
	m_crtc[0]->set_screen("screen");
	m_crtc[0]->set_next_crtc(m_crtc[1]);

	I8276(config, m_crtc[1], 19.6608_MHz_XTAL / 10);
	m_crtc[1]->set_character_width(10);
	m_crtc[1]->set_screen("screen");

	rs232_port_device &serial(RS232_PORT(config, "serial", default_rs232_devices, nullptr));
	serial.rxd_handler().set(m_pci[0], FUNC(scn_pci_device::rxd_w));
	serial.cts_handler().set(m_pci[0], FUNC(scn_pci_device::cts_w));

	// TODO: RS232C printer port ("same as above except SEND only")
}

ROM_START(t7000)
	ROM_REGION(0x4000, "program", 0)
	ROM_LOAD("t7000_0_8-17-82.u35", 0x0000, 0x1000, CRC(d1645232) SHA1(cdc203942af5b8b3e6bd189c4c7121e480ce1e17)) // all Intel D2732A-3 or MBM2732A-30
	ROM_LOAD("t7000_1_8-17-82.u36", 0x1000, 0x1000, CRC(3441e9cc) SHA1(323d97308170ec6a52a64a60bb8d4554e11e9c12))
	ROM_LOAD("t7000_2_8-17-82.u37", 0x2000, 0x1000, CRC(43a50f3e) SHA1(ae25d3d586ff7027d326e7ce061523b435c2d651))
	ROM_LOAD("t7000_3_8-17-82.u38", 0x3000, 0x1000, CRC(d4ef7293) SHA1(e8c331f629c29d9441723dad9e01f7447638202d))

	ROM_REGION(0x800, "chargen", 0)
	ROM_LOAD("blank.u45", 0x000, 0x800, CRC(0fadcbc8) SHA1(b939b204e76b5d390814a3e575f5473b0a4cbf9d)) // MM2716Q-1
ROM_END

} // anonymous namespace

SYST(1982, t7000, 0, 0, t7000, t7000, t7000_state, empty_init, "Wicat Systems", "T7000 Video Terminal", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE)



tabe22.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: AJR, Dirk Best
/***************************************************************************

    Tab Products "E22" Terminal

    VT52/VT100/VT220

    Hardware:
    - P8085AH
    - SCN2674B with SCB2675T
    - SCN2681
    - P8251A
    - 3x HM6264LP-12 (8k)
    - 3x HM6116LP-2 (2k)
    - XTAL: 3.6864 MHz, 10.0000 MHz, 21.7566 MHz, 35.8344 MHz

    TODO:
    - Dump keyboard controller and emulate it (currently HLE'd)
    - NVRAM / memory layout
    - Needs a hack to send out data on the RS232 port:
        Set $f7a3 = 0 after startup

    Notes:
    - The hardware has some similarities to cit220.cpp
    - Everything here is guessed (including the system name), no docs available
    - Other (undumped) terminals from Tab:
      * 132/15: VT52/VT100/VT132 (1982)
      * 132/15-G: Tektronix graphics (1982)
      * 132/15-H: Honeywell (1983)

***************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/bankdev.h"
#include "e22_kbd.h"
#include "machine/i8251.h"
#include "machine/mc68681.h"
#include "machine/nvram.h"
#include "video/scn2674.h"
#include "bus/rs232/rs232.h"
#include "bus/rs232/printer.h"
#include "emupal.h"
#include "screen.h"


namespace {


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class tabe22_state : public driver_device
{
public:
	tabe22_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen"),
		m_palette(*this, "palette"),
		m_avdc(*this, "avdc"),
		m_chargen(*this, "chargen"),
		m_vram_bank(*this, "vrambank")
	{ }

	void tabe22(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<i8085a_cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<scn2674_device> m_avdc;
	required_region_ptr<uint8_t> m_chargen;
	required_device<address_map_bank_device> m_vram_bank;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void vram_map(address_map &map) ATTR_COLD;

	void char_map(address_map &map) ATTR_COLD;
	void attr_map(address_map &map) ATTR_COLD;

	void video_ctrl_w(uint8_t data);
	void crt_brightness_w(uint8_t data);
	SCN2674_DRAW_CHARACTER_MEMBER(draw_character);
	void palette(palette_device &palette) const;

	bool m_screen_light = false;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void tabe22_state::mem_map(address_map &map)
{
	map(0x0000, 0xbfff).rom().region("maincpu", 0);
	map(0xc000, 0xcfff).rw(m_vram_bank, FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0xd000, 0xdfff).ram();
	map(0xe000, 0xffff).ram();
}

void tabe22_state::vram_map(address_map &map)
{
	map(0x0000, 0x0fff).ram().share("charram");
	map(0x1000, 0x1fff).ram().share("attrram");
}

void tabe22_state::io_map(address_map &map)
{
	map(0x00, 0x0f).rw("duart", FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0x20, 0x27).rw(m_avdc, FUNC(scn2674_device::read), FUNC(scn2674_device::write));
	map(0x40, 0x41).rw("usart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x60, 0x60).w(FUNC(tabe22_state::video_ctrl_w));
	map(0x61, 0x61).w(FUNC(tabe22_state::crt_brightness_w));
}


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

void tabe22_state::char_map(address_map &map)
{
	map(0x0000, 0x0fff).ram().share("charram");
}

void tabe22_state::attr_map(address_map &map)
{
	map(0x0000, 0x0fff).ram().share("attrram");
}

void tabe22_state::video_ctrl_w(uint8_t data)
{
	// 7-------  unknown
	// -6------  screen light/dark
	// --5-----  char ram/attribute ram switch
	// ---4----  unknown
	// ----3---  80/132 col switch
	// -----210  unknown

	m_screen_light = bool(BIT(data, 6));
	m_vram_bank->set_bank(BIT(data, 5));
	m_avdc->set_unscaled_clock(BIT(data, 3) ? 35.8344_MHz_XTAL / 9 : 21.7566_MHz_XTAL / 9);
}

void tabe22_state::crt_brightness_w(uint8_t data)
{
	// unknown algorithm for the brightness
	// default value is 7, range is 15 (off) to 0 (brightest)
	m_screen->set_brightness(0xff - ((data & 0x0f) * 6));
}

SCN2674_DRAW_CHARACTER_MEMBER( tabe22_state::draw_character )
{
	// 7-------  chargen high bit
	// -6------  unknown
	// --5-----  shaded
	// ---4----  unknown
	// ----3---  bold
	// -----2--  blink
	// ------1-  underline
	// -------0  reverse

	uint16_t data = m_chargen[(BIT(attrcode, 7) << 12) | charcode << 4 | linecount] << 2;
	const pen_t *const pen = m_palette->pens();

	if (ul && (BIT(attrcode, 1)))
		data = 0x1ff;

	if (blink && (BIT(attrcode, 2)))
		data = 0x000;

	if (BIT(attrcode, 0))
		data = ~data;

	if (cursor)
		data = ~data;

	// foreground/background colors
	rgb_t fg = BIT(attrcode, 5) ? pen[1] : BIT(attrcode, 3) ? pen[3] : pen[2];
	rgb_t bg = m_screen_light ? pen[1] : pen[0];

	// draw 9 pixels of the character
	for (int i = 0; i < 9; i++)
		bitmap.pix(y, x + i) = BIT(data, 8 - i) ? fg : bg;
}

static const gfx_layout char_layout =
{
	8,16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP8(0,1) },
	{ STEP16(0,8) },
	8*16
};

static GFXDECODE_START(chars)
	GFXDECODE_ENTRY("chargen", 0, char_layout, 0, 1)
GFXDECODE_END

void tabe22_state::palette(palette_device &palette) const
{
	static constexpr rgb_t pens[4] =
	{
		{ 0x00, 0x00, 0x00 }, // black
		{ 0x7f, 0x7f, 0x7f }, // gray
		{ 0xcf, 0xcf, 0xcf }, // white
		{ 0xff, 0xff, 0xff }, // highlight
	};

	palette.set_pen_colors(0, pens);
}


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void tabe22_state::machine_start()
{
	// register for save states
	save_item(NAME(m_screen_light));
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void printer_devices(device_slot_interface &device)
{
	device.option_add("printer", SERIAL_PRINTER);
}

static DEVICE_INPUT_DEFAULTS_START( printer_defaults )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_4800 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_ODD )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

void tabe22_state::tabe22(machine_config &config)
{
	I8085A(config, m_maincpu, 10_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &tabe22_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &tabe22_state::io_map);

	ADDRESS_MAP_BANK(config, m_vram_bank, 0);
	m_vram_bank->set_map(&tabe22_state::vram_map);
	m_vram_bank->set_addr_width(13);
	m_vram_bank->set_data_width(8);
	m_vram_bank->set_stride(0x1000);

//  NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_color(rgb_t::amber());
	m_screen->set_raw(21.7566_MHz_XTAL, 918, 0, 720, 395, 0, 378); // 80 column mode
//  m_screen->set_raw(35.8344_MHz_XTAL, 1494, 0, 1188, 395, 0, 378); // 132 column mode
	m_screen->set_screen_update(m_avdc, FUNC(scn2674_device::screen_update));

	PALETTE(config, m_palette, FUNC(tabe22_state::palette), 4);

	GFXDECODE(config, "gfxdecode", m_palette, chars);

	SCN2674(config, m_avdc, 21.7566_MHz_XTAL / 9);
	m_avdc->intr_callback().set_inputline(m_maincpu, I8085_RST75_LINE);
	m_avdc->set_character_width(9);
	m_avdc->set_display_callback(FUNC(tabe22_state::draw_character));
	m_avdc->set_addrmap(0, &tabe22_state::char_map);
	m_avdc->set_addrmap(1, &tabe22_state::attr_map);
	m_avdc->set_screen("screen");

	scn2681_device &duart(SCN2681(config, "duart", 3.6864_MHz_XTAL));
	duart.irq_cb().set_inputline(m_maincpu, I8085_RST65_LINE);
	duart.a_tx_cb().set("printer", FUNC(rs232_port_device::write_txd));
	duart.b_tx_cb().set("host", FUNC(rs232_port_device::write_txd));
	duart.outport_cb().set("usart", FUNC(i8251_device::write_txc)).bit(3);
	duart.outport_cb().append("usart", FUNC(i8251_device::write_rxc)).bit(3);

	rs232_port_device &rs232_host(RS232_PORT(config, "host", default_rs232_devices, nullptr));
	rs232_host.rxd_handler().set("duart", FUNC(scn2681_device::rx_b_w));
	rs232_host.cts_handler().set("duart", FUNC(scn2681_device::ip1_w));

	rs232_port_device &rs232_printer(RS232_PORT(config, "printer", printer_devices, nullptr));
	rs232_printer.set_option_device_input_defaults("printer", DEVICE_INPUT_DEFAULTS_NAME(printer_defaults));

	i8251_device &usart(I8251(config, "usart", 10_MHz_XTAL / 4)); // divider guessed
	usart.rxrdy_handler().set_inputline(m_maincpu, I8085_RST55_LINE);
	usart.txd_handler().set("kbd", FUNC(e22_kbd_hle_device::rx_w));

	e22_kbd_hle_device &kbd(E22_KBD_HLE(config, "kbd"));
	kbd.tx_handler().set("usart", FUNC(i8251_device::write_rxd));
	kbd.cts_handler().set("usart", FUNC(i8251_device::write_cts));
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START(tabe22)
	ROM_REGION(0xc000, "maincpu", 0)
	ROM_LOAD("e22_u3__2.17.86__v2.00.bin", 0x0000, 0x8000, CRC(fd931bc5) SHA1(013d5a5ed759bb9684bff18a3e848fa6d1167e10))
	ROM_LOAD("e22_u2__2.17.86__v2.00.bin", 0x8000, 0x4000, CRC(45d5e895) SHA1(a2b4e04dbc881230462a38de98ed843d5683c1b9))

	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD("e22_u43__char_gen.bin", 0x0000, 0x2000, CRC(33880908) SHA1(7f26dede2feaf3591312d67e3dabfc1ad8bb3181))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME    PARENT   COMPAT  MACHINE  INPUT  CLASS         INIT        COMPANY         FULLNAME                 FLAGS
COMP( 1986, tabe22, 0,       0,      tabe22,  0,     tabe22_state, empty_init, "Tab Products", "E-22 Display Terminal", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



taitowlf.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ville Linde, Angelo Salese
/*  P5TX-LA / Taito Wolf System

Original legacy PCI driver by Ville Linde,
rewritten by Angelo Salese to use the new PCI model

Three board system consisting of a P5TX-LA PC motherboard, a Taito main board and a rom board.

TODO:
- The Retro Web MB pic lists a Winbond w83877tf Super I/O but neither BIOSes properly init that,
  eventually failing with p5txla later on with PnP sequence. Missing power on default?
- p5txla: Rage VGA chip sets up screen with 8x1, making MAME unresponsive.
          Needs x86 VGA legacy map bridge to fix.
- pf2012: verify ISA irq 7 source (particularly ACK, PORT_IMPULSE(1) won't work),
          pinpoint coin counters output and verify tc0510nio write 4 EEPROM style write
          on coin insertion;
- pf2012: Should show bootscreen when Voodoo fbiInit0 bit 0 is off (vga_pass), cfr. GH #11343;
- pf2012: boots in service mode the first time around, needs default EEPROM set;
- pf2012: service mode RTC item always initializes to 0 for hour count;
- pf2012: PC portion returns an EMM386 "WARNING: Unable to set page frame base address"
          during boot up, safe to ignore?

Notes:
- pf2012: -isa1 svga_et4k if you need PC side logs;
- pf2012: game responds to CTRL+ALT+DEL, which will reset the machine.
- pf2012: according to manual hold A+B+service button for 10 seconds for entering
          test mode during initial bootup sequence.
          Board and input test menu doesn't seem to have a dedicated test mode switch
          Update: it just goes "SERVICE SW ERROR"?

===================================================================================================

Hardware configuration:

P5TX-LA Motherboard:
-CPU: Intel SL27J Pentium MMX @ 200 MHz
-Onboard sound: Crystal CS4237B ISA Audio
-Onboard VGA: ATI Rage II 3D Graph (removed from motherboard)

Chipsets (430TX PCIset):
-82439TX Northbridge
-82371AB PIIX4 PCI-ISA Southbridge

Taito W Main Board:
-AMD M4-128N/64 CPLD stamped 'E58-01'
-AMD MACH231 CPLD stamped 'E58-02'
-AMD MACH211 CPLD stamped 'E58-03'
-Panasonic MN1020019 (MN10200 based) Sound CPU
-Zoom ZFX-2 DSP (TMS57002 DSP)
-Zoom ZSG-2 Sound PCM chip
-Taito TC0510NIO I/O chip
-1x RAM NEC 42S4260
-1x RAM GM71C4400
-12x RAM Alliance AS4C256K16E0-35 (256k x 16)
-Mitsubishi M66220 256 x 8-bit CMOS memory
-Fujitsu MB87078 6-bit, 4-channel Electronic Volume Controller
-Atmel 93C66 EEPROM (4kb probably for high scores, test mode settings etc)
-ICS5342-3 GENDAC 16-Bit Integrated Clock-LUT-DAC
-3DFX 500-0003-03 F805281.1 FBI
-3DFX 500-0004-02 F804701.1 TMU
-Rom: E58-04 (bootscreen)
-XTALs 50MHz (near 3DFX) and 14.31818MHz (near RAMDAC)

Taito W Rom Board:
-AMD M4-128N/64 CPLD stamped 'E58-05'
-Program, Sound roms

*/

#include "emu.h"

#include "bus/isa/isa_cards.h"
#include "bus/rs232/hlemouse.h"
#include "bus/rs232/null_modem.h"
#include "bus/rs232/rs232.h"
#include "bus/rs232/sun_kbd.h"
#include "bus/rs232/terminal.h"
#include "cpu/i386/i386.h"
//#include "machine/w83977tf.h"
#include "machine/8042kbdc.h"
#include "machine/ds128x.h"
#include "machine/i82371sb.h"
#include "machine/i82439tx.h"
#include "machine/pci-ide.h"
#include "machine/pci.h"
#include "video/atirage.h"

// Specific to taitowlf
#include "machine/bankdev.h"
#include "machine/eepromser.h"
#include "video/voodoo_pci.h"
#include "taitoio.h"


/***********************
 *
 * Taito extra ISA board
 *
 **********************/

class isa16_taito_rom_disk : public device_t, public device_isa16_card_interface
{
public:
	// construction/destruction
	isa16_taito_rom_disk(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	template <typename T> void set_program_rom_tag(T &&tag) { m_program_rom.set_tag(std::forward<T>(tag)); }
	template <typename T> void set_data_rom_tag(T &&tag) { m_data_rom.set_tag(std::forward<T>(tag)); }

	// TODO: confirm routing
	DECLARE_INPUT_CHANGED_MEMBER(coin_irq) { m_isa->irq7_w(ASSERT_LINE); };

protected:
	virtual void device_start() override ATTR_COLD;
	virtual void device_reset() override ATTR_COLD;
	virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
	virtual ioport_constructor device_input_ports() const override ATTR_COLD;

private:
	required_memory_region m_program_rom;
	required_memory_region m_data_rom;
	required_device<address_map_bank_device> m_bankdev;
	required_device<tc0510nio_device> m_tc0510nio;
	required_device<eeprom_serial_93cxx_device> m_eeprom;
	required_ioport m_eepromin;
	required_ioport m_eepromout;

	void io_map(address_map &map) ATTR_COLD;

	u8 m_program_bank = 0;
	u8 m_program_select = 0;

	u8 read_bank(offs_t offset);
	void write_bank(offs_t offset, u8 data);

	void remap(int space_id, offs_t start, offs_t end) override;

	void bankdev_map(address_map &map) ATTR_COLD;

	void nio3_w(u8 data);
	void nio4_w(u8 data);
};

DEFINE_DEVICE_TYPE(ISA16_TAITO_ROM_DISK, isa16_taito_rom_disk, "isa16_taito_rom_disk", "ISA16 Taito Wolf System ROM DISK")

isa16_taito_rom_disk::isa16_taito_rom_disk(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, ISA16_TAITO_ROM_DISK, tag, owner, clock)
	, device_isa16_card_interface(mconfig, *this)
	, m_program_rom(*this, finder_base::DUMMY_TAG)
	, m_data_rom(*this, finder_base::DUMMY_TAG)
	, m_bankdev(*this, "bankdev")
	, m_tc0510nio(*this, "tc0510nio")
	, m_eeprom(*this, "eeprom")
	, m_eepromin(*this, "EEPROMIN")
	, m_eepromout(*this, "EEPROMOUT")
{
}

void isa16_taito_rom_disk::device_add_mconfig(machine_config &config)
{
	ADDRESS_MAP_BANK(config, m_bankdev).set_map(&isa16_taito_rom_disk::bankdev_map).set_options(ENDIANNESS_LITTLE, 8, 16 + 14, 0x4000);

	TC0510NIO(config, m_tc0510nio, 0);
	m_tc0510nio->read_0_callback().set_ioport("IN0");
	m_tc0510nio->read_1_callback().set_ioport("IN1");
	m_tc0510nio->read_2_callback().set_ioport("IN2");
	m_tc0510nio->read_3_callback().set_ioport("IN3");
	m_tc0510nio->write_3_callback().set(FUNC(isa16_taito_rom_disk::nio3_w));
	m_tc0510nio->write_4_callback().set(FUNC(isa16_taito_rom_disk::nio4_w));
	m_tc0510nio->read_7_callback().set_ioport("IN7");

	// TODO: verify erase/write times
	EEPROM_93C66_16BIT(config, m_eeprom)
		.erase_time(attotime::from_usec(250))
		.write_time(attotime::from_usec(250));


	// TODO: sound CPU et al.
}

void isa16_taito_rom_disk::io_map(address_map &map)
{
	map.unmap_value_high();

	map(0x080, 0x081).rw(FUNC(isa16_taito_rom_disk::read_bank), FUNC(isa16_taito_rom_disk::write_bank));
	// writes watchdog to port 1, in 8-bit fashion
	map(0x200, 0x207).lrw8(
		NAME([this] (offs_t offset) { m_isa->irq7_w(CLEAR_LINE); return m_tc0510nio->read(offset ^ 1); }),
		NAME([this] (offs_t offset, u8 data) { m_tc0510nio->write(offset ^ 1, data); })
	);

	//map(0x400, 0x4ff) sound CPU shared RAM?

	map(0x600, 0x600).lw8(
		NAME([this] (offs_t offset, u8 data) {
			// 0x30 before showing 3dfx logo
			// 0x3d in-game
			// 0xf3 in service mode, 0xff when accessing sound volume/panning settings
			logerror("Write %02x to $600\n", data);
		})
	);
	map(0x601, 0x601).lrw8(
		NAME([this] (offs_t offset) { return m_eepromin->read(); }),
		NAME([this] (offs_t offset, u8 data) { m_eepromout->write(data, 0xff); })
	);

	//map(0x602, 0x603) to/from sound CPU
}

void isa16_taito_rom_disk::bankdev_map(address_map &map)
{
	map.unmap_value_high();
	// TODO: EMM386 and flush interactions only for writes?
	map(0x0000'0000, 0x003f'ffff).lr8(
		NAME([this] (offs_t offset) { return m_program_rom->base()[offset]; })
	);
	// TODO: unconfirmed upper bounds
	map(0x0080'0000, 0x047f'ffff).lr8(
		NAME([this] (offs_t offset) { return m_data_rom->base()[offset]; })
	);
}

void isa16_taito_rom_disk::device_start()
{
	set_isa_device();
}

void isa16_taito_rom_disk::device_reset()
{
}

void isa16_taito_rom_disk::remap(int space_id, offs_t start, offs_t end)
{
	if (space_id == AS_PROGRAM)
	{
		// emm386 /X=CB00-D400
		m_isa->install_memory(0xcb000, 0xcbfff, *this, &isa16_taito_rom_disk::io_map);
		//m_isa->install_memory(0xcb080, 0xcb081, read8sm_delegate(*this, FUNC(isa16_taito_rom_disk::read_bank)), write8sm_delegate(*this, FUNC(isa16_taito_rom_disk::write_bank)));

		m_isa->install_memory(0xd0000, 0xd3fff, *m_bankdev, &address_map_bank_device::amap8);
	}
}

u8 isa16_taito_rom_disk::read_bank(offs_t offset)
{
	logerror("isa16_taito_rom_disk: unconfirmed read bank\n");
	if (!offset)
		return m_program_bank;
	else
		return m_program_select;
}

void isa16_taito_rom_disk::write_bank(offs_t offset, u8 data)
{
	if (!offset)
		m_program_bank = data;
	else
		m_program_select = data;

	m_bankdev->set_bank(m_program_select << 8 | m_program_bank);
}

// may be unconnected
void isa16_taito_rom_disk::nio3_w(u8 data)
{
	logerror("NIO3 %02x state\n", data);
}

// TODO: reacts to each coin trigger
// Game has individual coin counters, this rather looks an i2c/EEPROM serial protocol at bits 3-0?
void isa16_taito_rom_disk::nio4_w(u8 data)
{
	logerror("NIO4 %02x state\n", data);
}

static INPUT_PORTS_START(pf2012)
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_START1 ) // marked as セレクト (Select) in test mode
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_COIN1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(isa16_taito_rom_disk::coin_irq), 0)

	PORT_START("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_START2 ) // as above
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_COIN2 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(isa16_taito_rom_disk::coin_irq), 0)

	PORT_START("IN2")
	PORT_DIPNAME( 0x0001, 0x0001, "IN2" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	// service mode doesn't explicitly tell, but goes service sw error if left on during boot
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(isa16_taito_rom_disk::coin_irq), 0)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_TILT )
	PORT_SERVICE( 0x80, IP_ACTIVE_LOW )

	PORT_START("IN3")
	PORT_DIPNAME( 0x0001, 0x0001, "IN3" )
	PORT_DIPSETTING(      0x0001, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0002, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0004, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0008, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0010, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0020, 0x0020, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0020, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0040, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )
	PORT_DIPNAME( 0x0080, 0x0080, DEF_STR( Unknown ) )
	PORT_DIPSETTING(      0x0080, DEF_STR( Off ) )
	PORT_DIPSETTING(      0x0000, DEF_STR( On ) )

	PORT_START("IN7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)

	PORT_START("EEPROMIN")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::do_read))
	PORT_BIT( 0xfe, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START( "EEPROMOUT" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::di_write))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::clk_write))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("eeprom", FUNC(eeprom_serial_93cxx_device::cs_write))
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

ioport_constructor isa16_taito_rom_disk::device_input_ports() const
{
	return INPUT_PORTS_NAME(pf2012);
}


/***********************
 *
 * Motherboard resources
 *
 **********************/

class isa16_p5txla_mb : public device_t, public device_isa16_card_interface
{
public:
	// construction/destruction
	isa16_p5txla_mb(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	required_device<ds12885_device> m_rtc;
	required_device<kbdc8042_device> m_kbdc;

protected:
	virtual void device_start() override ATTR_COLD;
	virtual void device_reset() override ATTR_COLD;
	virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;

private:
	void remap(int space_id, offs_t start, offs_t end) override;

	void device_map(address_map &map) ATTR_COLD;
};

DEFINE_DEVICE_TYPE(ISA16_P5TXLA_MB, isa16_p5txla_mb, "isa16_p5txla_mb", "ISA16 P5TX-LA Virtual MB resources")

isa16_p5txla_mb::isa16_p5txla_mb(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, ISA16_P5TXLA_MB, tag, owner, clock)
	, device_isa16_card_interface(mconfig, *this)
	, m_rtc(*this, "rtc")
	, m_kbdc(*this, "kbdc")
{
}

void isa16_p5txla_mb::device_add_mconfig(machine_config &config)
{
	// TODO: verify keyboard / RTC types, latter lies inside PIIX4?
	// need at least a DS12885 otherwise EMM386 will complain to not have enough memory
	DS12885(config, m_rtc, 32.768_kHz_XTAL);
	//m_rtc->irq().set(m_pic8259_2, FUNC(pic8259_device::ir0_w));
	m_rtc->set_century_index(0x32);

	KBDC8042(config, m_kbdc, 0);
	m_kbdc->set_keyboard_type(kbdc8042_device::KBDC8042_STANDARD);
	m_kbdc->system_reset_callback().set_inputline(":maincpu", INPUT_LINE_RESET);
	m_kbdc->gate_a20_callback().set_inputline(":maincpu", INPUT_LINE_A20);
	m_kbdc->input_buffer_full_callback().set(":pci:07.0", FUNC(i82371sb_isa_device::pc_irq1_w));
}


void isa16_p5txla_mb::device_start()
{
	set_isa_device();
}

void isa16_p5txla_mb::device_reset()
{

}

void isa16_p5txla_mb::remap(int space_id, offs_t start, offs_t end)
{
	if (space_id == AS_IO)
		m_isa->install_device(0x60, 0x7f, *this, &isa16_p5txla_mb::device_map);
}

void isa16_p5txla_mb::device_map(address_map &map)
{
	map(0x00, 0x0f).rw(m_kbdc, FUNC(kbdc8042_device::data_r), FUNC(kbdc8042_device::data_w));
	map(0x10, 0x1f).w(m_rtc, FUNC(mc146818_device::address_w)).umask32(0x00ff00ff);
	map(0x10, 0x1f).rw(m_rtc, FUNC(mc146818_device::data_r), FUNC(mc146818_device::data_w)).umask32(0xff00ff00);
}

namespace {

#define PCI_VIDEO_ID "pci:12.0"

class p5txla_state : public driver_device
{
public:
	p5txla_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void p5txla(machine_config &config);

protected:
	required_device<pentium_mmx_device> m_maincpu;

private:
	void p5txla_io(address_map &map) ATTR_COLD;
	void p5txla_map(address_map &map) ATTR_COLD;

//  static void winbond_superio_config(device_t *device);
};

class taitowlf_state : public p5txla_state
{
public:
	taitowlf_state(const machine_config &mconfig, device_type type, const char *tag)
		: p5txla_state(mconfig, type, tag)
		, m_voodoo(*this, PCI_VIDEO_ID)
		, m_screen(*this, "screen")
	{ }

	void taitowlf(machine_config &config);

protected:
	required_device<voodoo_1_pci_device> m_voodoo;
	required_device<screen_device> m_screen;

private:
	static void romdisk_config(device_t *device);

	u32 screen_update( screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect );
};

u32 taitowlf_state::screen_update( screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect )
{
// TODO: debug code for bootscreen, ISA card sub-device
// Also fixed area, not worth decoding more than once (bitmap_device?)
#if 0
	static bool enable_switch = 1;
	// bits 0-2 TAITOWOLF splash screen, bits 4-6 cross hatch
	static u8 base_pen = 0;
	const u8 *bootscreen_rom = memregion("bootscreen")->base();

	if (machine().input().code_pressed_once(KEYCODE_Z))
		enable_switch ^= 1;

	if (machine().input().code_pressed_once(KEYCODE_X))
		base_pen ^= 4;

	if (enable_switch)
	{
		for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
		{
			for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
			{
				const u8 pen = bootscreen_rom[y * 512 + x];
				// TODO: palette color routing (game barely uses red and white in both screens)
				const u32 to_rgb = rgb_t(
					pal1bit(BIT(pen, 0 + base_pen)),
					pal1bit(BIT(pen, 1 + base_pen)),
					pal1bit(BIT(pen, 2 + base_pen))
				);
				bitmap.pix(y, x) = to_rgb;
			}
		}

		return 0;
	}
#endif

	return m_voodoo->screen_update(screen, bitmap, cliprect);
}

void p5txla_state::p5txla_map(address_map &map)
{
	map.unmap_value_high();
}

void p5txla_state::p5txla_io(address_map &map)
{
	map.unmap_value_high();
}

/*****************************************************************************/

static void isa_internal_devices(device_slot_interface &device)
{
	// TODO: w83877tf
	// It actually don't seem to access any kind of Super I/O, wtf
//  device.option_add("w83977tf", W83977TF);
	device.option_add_internal("taito_romdisk", ISA16_TAITO_ROM_DISK);
	device.option_add_internal("p5txla_mb", ISA16_P5TXLA_MB);
}

#if 0
void p5txla_state::winbond_superio_config(device_t *device)
{
	w83977tf_device &fdc = *downcast<w83977tf_device *>(device);
//  fdc.set_sysopt_pin(1);
	fdc.gp20_reset().set_inputline(":maincpu", INPUT_LINE_RESET);
	fdc.gp25_gatea20().set_inputline(":maincpu", INPUT_LINE_A20);
	fdc.irq1().set(":pci:07.0", FUNC(i82371sb_isa_device::pc_irq1_w));
	fdc.irq8().set(":pci:07.0", FUNC(i82371sb_isa_device::pc_irq8n_w));
//  fdc.txd1().set(":serport0", FUNC(rs232_port_device::write_txd));
//  fdc.ndtr1().set(":serport0", FUNC(rs232_port_device::write_dtr));
//  fdc.nrts1().set(":serport0", FUNC(rs232_port_device::write_rts));
//  fdc.txd2().set(":serport1", FUNC(rs232_port_device::write_txd));
//  fdc.ndtr2().set(":serport1", FUNC(rs232_port_device::write_dtr));
//  fdc.nrts2().set(":serport1", FUNC(rs232_port_device::write_rts));
}
#endif

// TODO: PCI address mapping is unconfirmed
void p5txla_state::p5txla(machine_config &config)
{
	// P55C 133, 150, 166, 200, 233 MHz desktop options
	// 266, 300 MHz should be mobile only
	PENTIUM_MMX(config, m_maincpu, 133'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &p5txla_state::p5txla_map);
	m_maincpu->set_addrmap(AS_IO, &p5txla_state::p5txla_io);
	m_maincpu->set_irq_acknowledge_callback("pci:07.0:pic8259_master", FUNC(pic8259_device::inta_cb));
//  m_maincpu->smiact().set("pci:00.0", FUNC(i82439tx_host_device::smi_act_w));

	// FSB 66 MHz
	PCI_ROOT(config, "pci", 0);
	// 64MB for Taito Wolf HW, to be checked for base p5txla
	I82439TX(config, "pci:00.0", 0, m_maincpu, 64*1024*1024);

	// TODO: 82371AB
	i82371sb_isa_device &isa(I82371SB_ISA(config, "pci:07.0", 0, m_maincpu));
	isa.boot_state_hook().set([](u8 data) { /* printf("%02x\n", data); */ });
	isa.smi().set_inputline(m_maincpu, INPUT_LINE_SMI);

	i82371sb_ide_device &ide(I82371SB_IDE(config, "pci:07.1", 0, m_maincpu));
	ide.irq_pri().set("pci:07.0", FUNC(i82371sb_isa_device::pc_irq14_w));
	ide.irq_sec().set("pci:07.0", FUNC(i82371sb_isa_device::pc_mirq0_w));

//  ISA16_SLOT(config, "board4", 0, "pci:07.0:isabus", isa_internal_devices, "w83977tf", true).set_option_machine_config("w83977tf", winbond_superio_config);
	ISA16_SLOT(config, "board2", 0, "pci:07.0:isabus", isa_internal_devices, "p5txla_mb", true);
	ISA16_SLOT(config, "isa1", 0, "pci:07.0:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa2", 0, "pci:07.0:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa3", 0, "pci:07.0:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa4", 0, "pci:07.0:isabus", pc_isa16_cards, nullptr, false);
	ISA16_SLOT(config, "isa5", 0, "pci:07.0:isabus", pc_isa16_cards, nullptr, false);

#if 0
	rs232_port_device& serport0(RS232_PORT(config, "serport0", isa_com, nullptr)); // "microsoft_mouse"));
	serport0.rxd_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::rxd1_w));
	serport0.dcd_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::ndcd1_w));
	serport0.dsr_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::ndsr1_w));
	serport0.ri_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::nri1_w));
	serport0.cts_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::ncts1_w));

	rs232_port_device &serport1(RS232_PORT(config, "serport1", isa_com, nullptr));
	serport1.rxd_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::rxd2_w));
	serport1.dcd_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::ndcd2_w));
	serport1.dsr_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::ndsr2_w));
	serport1.ri_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::nri2_w));
	serport1.cts_handler().set("board4:w83977tf", FUNC(fdc37c93x_device::ncts2_w));
#endif

	// on-board
	ATI_RAGEIIDVD(config, PCI_VIDEO_ID, 0);
}

void taitowlf_state::romdisk_config(device_t *device)
{
	isa16_taito_rom_disk &romdisk = *downcast<isa16_taito_rom_disk *>(device);
	romdisk.set_program_rom_tag("program_rom");
	romdisk.set_data_rom_tag("data_rom");
}

void taitowlf_state::taitowlf(machine_config &config)
{
	p5txla_state::p5txla(config);

	m_maincpu->set_clock(200'000'000);

	ISA16_SLOT(config, "board1", 0, "pci:07.0:isabus", isa_internal_devices, "taito_romdisk", true).set_option_machine_config("taito_romdisk", romdisk_config);
	// TODO: remove keyboard slot option

	VOODOO_1_PCI(config.replace(), m_voodoo, 0, m_maincpu, m_screen);
	// TODO: unverified parameters
	m_voodoo->set_fbmem(2);
	m_voodoo->set_tmumem(4, 0);
	m_voodoo->set_status_cycles(1000);

	// TODO: displays bootscreen ROM contents (512x240 8bpp) while the board is in vga_pass off state
	// This is provided by one of the CPLDs that is on the Taito PCB stack, CRTC values needs to be verified in this state
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(57);
	m_screen->set_size(800, 262);
	m_screen->set_visarea(0, 512 - 1, 0, 240 - 1);
	m_screen->set_screen_update(FUNC(taitowlf_state::screen_update));
}

/*****************************************************************************/

ROM_START(p5txla)
	ROM_REGION32_LE(0x40000, "pci:07.0", 0)
	ROM_LOAD("p5tx-la.bin", 0x00000, 0x40000, CRC(072e6d51) SHA1(70414349b37e478fc28ecbaba47ad1033ae583b7))
ROM_END

ROM_START(pf2012)
	ROM_REGION32_LE(0x40000, "pci:07.0", ROMREGION_ERASEFF)
	// TAITO Ver1.0 1998/5/7
	ROM_LOAD("p5tx-la_861.u16", 0x20000, 0x20000, CRC(a4d4a0fc) SHA1(af3a49a1bee416b58a61af28473f3dac0a4160c8))

	ROM_REGION16_LE(0x400000, "board1:program_rom", 0) // Program ROM (FAT12)
	ROM_LOAD("u1.bin", 0x000000, 0x200000, CRC(8f4c09cb) SHA1(0969a92fec819868881683c580f9e01cbedf4ad2))
	ROM_LOAD("u2.bin", 0x200000, 0x200000, CRC(59881781) SHA1(85ff074ab2a922eac37cf96f0bf153a2dac55aa4))

	ROM_REGION16_LE(0x4000000, "board1:data_rom", 0) // Data ROM (FAT12)
	ROM_LOAD("e59-01.u20", 0x0000000, 0x800000, CRC(60f2ce4a) SHA1(322dd62022527997ecc655347fdf75a092aefa8a) )
	ROM_LOAD("e59-02.u23", 0x0800000, 0x800000, CRC(626df682) SHA1(35bb4f91201734ce7ccdc640a75030aaca3d1151) )
	ROM_LOAD("e59-03.u26", 0x1000000, 0x800000, CRC(74e4efde) SHA1(630235c2e4a11f615b5f3b8c93e1e645da09eefe) )
	ROM_LOAD("e59-04.u21", 0x1800000, 0x800000, CRC(c900e8df) SHA1(93c06b8f5082e33f0dcc41f1be6a79283de16c40) )
	ROM_LOAD("e59-05.u24", 0x2000000, 0x800000, CRC(85b0954c) SHA1(1b533d5888d56d1510c79f790e4fa708f77e836f) )
	ROM_LOAD("e59-06.u27", 0x2800000, 0x800000, CRC(0573a113) SHA1(ee76a71dfd31289a9a5428653a36d01d914fc5d9) )
	ROM_LOAD("e59-07.u22", 0x3000000, 0x800000, CRC(1f0ddcdc) SHA1(72ffe08f5effab093bdfe9863f8a11f80e914272) )
	ROM_LOAD("e59-08.u25", 0x3800000, 0x800000, CRC(8db38ffd) SHA1(4b71ea86fb774ba6a8ac45abf4191af64af007e7) )

	ROM_REGION(0x180000, "board1:taito_zoom:mn10200", 0) // MN10200 program
	ROM_LOAD("e59-12.u13", 0x000000, 0x80000, CRC(9a473a7e) SHA1(b0ec7b0ae2b33a32da98899aa79d44e8e318ceb7) )
	ROM_LOAD("e59-13.u15", 0x080000, 0x80000, CRC(77719880) SHA1(8382dd2dfb0dae60a3831ed6d3ff08539e2d94eb) )
	ROM_LOAD("e59-14.u14", 0x100000, 0x40000, CRC(d440887c) SHA1(d965871860d757bc9111e9adb2303a633c662d6b) )
	ROM_LOAD("e59-15.u16", 0x140000, 0x40000, CRC(eae8e523) SHA1(8a054d3ded7248a7906c4f0bec755ddce53e2023) )

	ROM_REGION(0x1400000, "board1:taito_zoom:zsg2", 0) // ZOOM sample data
	ROM_LOAD("e59-09.u29", 0x0000000, 0x800000, CRC(d0da5c50) SHA1(56fb3c38f35244720d32a44fed28e6b58c7851f7) )
	ROM_LOAD("e59-10.u32", 0x0800000, 0x800000, CRC(4c0e0a5c) SHA1(6454befa3a1dd532eb2a760129dcd7e611508730) )
	ROM_LOAD("e59-11.u33", 0x1000000, 0x400000, CRC(c90a896d) SHA1(2b62992f20e4ca9634e7953fe2c553906de44f04) )

	ROM_REGION(0x20000, "bootscreen", 0) // bootscreen
	ROM_LOAD("e58-04.u71", 0x000000, 0x20000, CRC(500e6113) SHA1(93226706517c02e336f96bdf9443785158e7becf) )
ROM_END

} // Anonymous namespace


/*****************************************************************************/

COMP(1997, p5txla, 0,   0, p5txla, 0, p5txla_state,   empty_init, "ECS",    "P5TX-LA (i430TX)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)

GAME(1998, pf2012, 0,   taitowlf, 0,  taitowlf_state, empty_init, ROT0, "Taito",  "Psychic Force 2012 (Ver 2.04J)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND) // 1998/05/07 18:30:00



talkingbb.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Kevin Horton
/*******************************************************************************

Parker Brothers Starting Lineup Talking Baseball

Hardware notes:
- PCB label KPT-349 REV 6C<1>
- 80C31 MCU @ 12MHz
- 2KB RAM, 128KB ROM, 2 cartridge slots
- 12 leds, 2 16-button keypads

ROM chip has (C) 1987 Tonka (Kenner Parker Toys was acquired by Tonka in 1987, Parker
Brothers itself was acquired by Kenner in 1985)

The speech driver is by Forrest S. Mozer's company.

The cartridge slots are for extra teams. There are around 8 cartridges, each one
included 3 or 4 baseball teams, with exception to the "Hall of Fame" cartridge.
Players enter a 3-digit code to select the team.

TODO:
- add cartridge slots
- Buttons are unresponsive at initial game setup, you need to hold the yes/no button
  until it responds. The keypad reading routine is at $1A5D, it gets skipped for several
  seconds at a time. Once in-game, everything is fine though. BTANB or different cause?

********************************************************************************

Field positions (used when substituting):

1: Pitcher
2: Catcher
3: First Base
4: Second Base
5: Third Base
6: Shortstop
7: Left Field
8: Center Field
9: Right Field

The game includes baseball cards with player stats. At the very least, the roster
cards are needed to play the game properly. See below for the default teams.

American Team:
--------------

Starting lineup batting order:

1: #23  Rickey Henderson, CF
2: #14  Don Mattingly, 1B
3: #18  Wade Boggs, 3B
4: #22  George Bell, LF
5: #24  Dave Winfield, RF
6: #16  Cal Ripken, SS
7: #11  Terry Kennedy, C
8: #15  Willie Randolph, 2B
9: #20  Alan Trammell, SS & DH
10:#27  Roger Clemens, P

Substitutes:

#19: George Brett, 3B
#12: Carlton Fisk, C
#25: Jack Morris, P
#13: Eddie Murray, 1B
#21: Kirby Puckett, OF
#30: Dan Quisenberry, P
#29: Dave Righetti, P
#28: Bret Saberhagen, P
#17: Lou Whitaker, 2B
#26: Robin Yount, SS

National Team:
--------------

Starting lineup batting order:

1: #23: Tim Raines, LF
2: #16: Ryne Sandberg, 2B
3: #22: Darryl Strawberry, RF
4: #19: Mike Schmidt, 3B
5: #13: Jack Clark, 1B
6: #20: Eric Davis, CF
7: #11: Gary Carter, C
8: #17: Ozzie Smith, SS
9: #18: Dale Murphy, OF & DH
10:#25: Mike Scott, P

Substitutes:

#15: Buddy Bell, 3B
#26: Jody Davis, C
#24: Andre Dawson, CF
#29: Dwight Gooden, P
#21: Tony Gwynn, OF
#14: Keith Hernandez, 1B
#30: Nolan Ryan, P
#12: Steve Sax, 2B
#28: Fernando Valenzuela, P
#27: Todd Worrell, P

*******************************************************************************/

#include "emu.h"

#include "cpu/mcs51/mcs51.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "talkingbb.lh"


namespace {

class talkingbb_state : public driver_device
{
public:
	talkingbb_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_rom(*this, "maincpu"),
		m_display(*this, "display"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void talkingbb(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<mcs51_cpu_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_device<pwm_display_device> m_display;
	required_ioport_array<5> m_inputs;

	u8 m_bank = 0;
	u8 m_inp_mux = 0;

	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	// I/O handlers
	void bank_w(u8 data);
	u8 bank_r(offs_t offset);
	void input_w(u8 data);
	u8 input_r();
	u8 switch_r();
};

void talkingbb_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_bank));
	save_item(NAME(m_inp_mux));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void talkingbb_state::bank_w(u8 data)
{
	// d0-d1: upper rom bank
	// d2-d4: upper rom enable (bus conflict possible)
	// d2: internal rom, d3: cart slot 1, d4: cart slot 2
	m_bank = data;
}

u8 talkingbb_state::bank_r(offs_t offset)
{
	u32 hi = (~m_bank & 3) << 15;
	u8 data = (m_bank & 4) ? 0xff : m_rom[offset | hi];

	// TODO: cartridge

	return data;
}

void talkingbb_state::input_w(u8 data)
{
	// no effect if P30 is low (never happens though)
	if (~m_bank & 1)
		return;

	// d4-d7: led select (also input mux)
	// d0-d2: led data
	m_inp_mux = data >> 4;
	m_display->matrix(m_inp_mux, ~data & 7);
}

u8 talkingbb_state::input_r()
{
	// open bus if P30 is low (never happens though)
	if (~m_bank & 1)
		return 0xff;

	u8 data = 0;

	// multiplexed inputs
	for (int i = 0; i < 4; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read();

	return ~data;
}

u8 talkingbb_state::switch_r()
{
	// d5: mode switch
	return ~m_inputs[4]->read();
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void talkingbb_state::main_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("maincpu", 0x18000);
	map(0x8000, 0xffff).r(FUNC(talkingbb_state::bank_r));
}

void talkingbb_state::main_io(address_map &map)
{
	map(0x0000, 0x07ff).mirror(0x7800).ram();
	map(0x8000, 0x8000).mirror(0x7fff).rw(FUNC(talkingbb_state::input_r), FUNC(talkingbb_state::input_w));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

/* keypad layout:        **** P1 = Batter ****  |  **** P2 = Pitcher ****
                                                |
[PINCH   [1       [2]       [3       [TIME OUT] |   [INFIELD  [1        [2        [3        [TIME OUT]
 HITTER]  BUNT]              POWER]             |    IN]       FAST]     CURVE]    CHANGE]
                                                |
[SCORE]  [4]      [5        [6]      [NO        |   [SCORE]   [4        [5        [6        [NO
                   SQUEEZE]           CANCEL]   |              PTCHOUT]  PICKOFF]  INTWALK]  CANCEL]
                                                |
[PINCH   [7       [8]       [9       [YES       |   [INSTANT  [7        [8]       [9        [YES
 RUNNER]  STEAL]             X-BASE]  ENTER]    |    REPLAY]   RELIEF]             SUB]      ENTER]
                                                |
                  [0                            |                       [0
                   SWING]                       |                        BALL]
*/

static INPUT_PORTS_START( talkingbb )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_R) PORT_NAME("P1 3 / Power")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_F) PORT_NAME("P1 6")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_V) PORT_NAME("P1 9 / Extra Base")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_MINUS) PORT_NAME("P1 Time Out")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_O) PORT_NAME("P2 3 / Change")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_L) PORT_NAME("P2 6 / Intentional Walk")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_STOP) PORT_NAME("P2 9 / Sub")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("P2 Time Out")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_E) PORT_NAME("P1 2")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_D) PORT_NAME("P1 5 / Squeeze")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_C) PORT_NAME("P1 8")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_LALT) PORT_NAME("P1 0 / Swing")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_I) PORT_NAME("P2 2 / Curve")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_K) PORT_NAME("P2 5 / Pickoff")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_COMMA) PORT_NAME("P2 8")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_RALT) PORT_NAME("P2 0 / Ball")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_W) PORT_NAME("P1 1 / Bunt")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_S) PORT_NAME("P1 4")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_X) PORT_NAME("P1 7 / Steal")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("P1 No / Cancel")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_U) PORT_NAME("P2 1 / Fast")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_J) PORT_NAME("P2 4 / Pitchout")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_M) PORT_NAME("P2 7 / Relief")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME("P2 No / Cancel")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("P1 Pinch Hitter")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("P1 Score")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("P1 Pinch Runner")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_NAME("P1 Yes / Enter")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("P2 Infield In")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("P2 Score")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("P2 Instant Replay")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("P2 Yes / Enter")

	PORT_START("IN.4")
	PORT_CONFNAME( 0x20, 0x00, "Mode" )
	PORT_CONFSETTING(    0x00, "Player" )
	PORT_CONFSETTING(    0x20, "Coach" )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void talkingbb_state::talkingbb(machine_config &config)
{
	// basic machine hardware
	I80C31(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &talkingbb_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &talkingbb_state::main_io);
	m_maincpu->port_out_cb<1>().set("dac", FUNC(dac_8bit_r2r_device::write));
	m_maincpu->port_out_cb<3>().set(FUNC(talkingbb_state::bank_w));
	m_maincpu->port_in_cb<3>().set(FUNC(talkingbb_state::switch_r));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 3);
	config.set_default_layout(layout_talkingbb);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_8BIT_R2R(config, "dac").add_route(ALL_OUTPUTS, "speaker", 0.5);
}



/******************************************************************************
    ROM Definitions
******************************************************************************/

ROM_START( talkingbb )
	ROM_REGION( 0x20000, "maincpu", 0 )
	ROM_LOAD("sp17208-002", 0x0000, 0x20000, CRC(e0e56217) SHA1(24a06b5c94a5c750799f61e3eb865e02d6cea68a) ) // GM231000-110
ROM_END

} // anonymous namespace



/******************************************************************************
    Drivers
******************************************************************************/

//    YEAR  NAME        PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY, FULLNAME, FLAGS
SYST( 1988, talkingbb,  0,      0,      talkingbb, talkingbb, talkingbb_state, empty_init, "Parker Brothers", "Starting Lineup Talking Baseball", MACHINE_SUPPORTS_SAVE )



talkingfb.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Kevin Horton
/*******************************************************************************

Parker Brothers Superstar Lineup Talking Football

Hardware notes:
- PCB label KPT-410 REV B1 (*KPT = Kenner Parker Toys)
- 80C31 MCU @ 12MHz
- 2KB RAM, 256KB ROM, 2 cartridge slots
- two 20-button keypads

Like Baseball, The ROM chip has (C) Tonka.
The speech driver is by Mozer, the ROM data includes employees names.

The cartridge slots are probably meant for extra teams. I can't find anything
about it in the manual or any proof that cartridges were released for this.
Maybe the toy went out of production soon after release.

The game includes formation cards, refer to the manual on which number is which strategy.

TODO:
- add cartridge slots (no need if no carts exist?)
- verify keypad (everything seems ok, but "Pursue" and "Go For Turnover" might
  be swapped, their function is nearly identical so it's hard to test)

*******************************************************************************/

#include "emu.h"

#include "cpu/mcs51/mcs51.h"
#include "sound/dac.h"

#include "speaker.h"


namespace {

class talkingfb_state : public driver_device
{
public:
	talkingfb_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_rom(*this, "maincpu"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void talkingfb(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<mcs51_cpu_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_ioport_array<5> m_inputs;

	u8 m_bank = 0;
	u8 m_inp_mux = 0;

	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	// I/O handlers
	void bank_w(u8 data);
	template<int Psen> u8 bank_r(offs_t offset);
	void input_w(u8 data);
	u8 input_r();
};

void talkingfb_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_bank));
	save_item(NAME(m_inp_mux));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void talkingfb_state::bank_w(u8 data)
{
	// d0-d2: upper rom bank
	// d3-d5: upper rom enable (bus conflict possible)
	// d3: cart slot 1, d4: cart slot 2, d5: internal rom
	m_bank = data;
}

template<int Psen>
u8 talkingfb_state::bank_r(offs_t offset)
{
	u32 hi = (m_bank & 7) << 15;
	u8 data = (m_bank & 0x20) ? 0xff : m_rom[offset | hi];

	// cartridge slots are only enabled if PSEN is high
	// TODO

	return data;
}

void talkingfb_state::input_w(u8 data)
{
	// d3-d7: input mux
	m_inp_mux = data >> 3;
}

u8 talkingfb_state::input_r()
{
	u8 data = 0;

	// multiplexed inputs
	for (int i = 0; i < 5; i++)
		if (BIT(~m_inp_mux, i))
			data |= m_inputs[i]->read();

	return ~data;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void talkingfb_state::main_map(address_map &map)
{
	map(0x0000, 0x7fff).rom();
	map(0x8000, 0xffff).r(FUNC(talkingfb_state::bank_r<0>));
}

void talkingfb_state::main_io(address_map &map)
{
	map(0x0000, 0x07ff).mirror(0x3800).ram();
	map(0x4000, 0x4000).mirror(0x3fff).rw(FUNC(talkingfb_state::input_r), FUNC(talkingfb_state::input_w));
	map(0x8000, 0xffff).r(FUNC(talkingfb_state::bank_r<1>));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

/* keypad layout is like this: (P1 = Home, P2 = Away, both keypads are same)

OFFENSIVE             [1       [2      [3        DEFENSIVE
 ACTION                HB]      FB]     ST]       ACTION

[SCRAM/     [SCORE]   [4       [5      [6        [PURSUE]
 EVADE]                FL]      WR]     TE]

[PASS OUT/  [TIME     [7]      [8      [9]       [GO FOR
 RUN OUT]    OUT]               SE]               TURNOVER]

[CHECK OFF  [REPLAY]  [YES/    [0      [NO/
 TO BACK]              ENTER]   DEMO]   CANCEL]

*/

static INPUT_PORTS_START( talkingfb )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("P2 Pursue")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("P2 Scram / Evade")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("P2 Pass Out / Run Out")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("P2 Check Off To Back")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Z) PORT_NAME("P1 Pursue")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("P1 Scram / Evade")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("P1 Pass Out / Run Out")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("P1 Check Off To Back")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("P2 Go For Turnover")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("P2 Score")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_J) PORT_NAME("P2 Time Out")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_K) PORT_NAME("P2 Replay")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_X) PORT_NAME("P1 Go For Turnover")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("P1 Score")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("P1 Time Out")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("P1 Replay")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("P2 1 / HB")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("P2 4 / FL")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("P2 7")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("P2 Yes / Enter")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("P1 1 / HB")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("P1 4 / FL")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("P1 7")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_NAME("P1 Yes / Enter")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("P2 2 / FB")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("P2 5 / WR")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("P2 8 / SE")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("P2 0 / Demo")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("P1 2 / FB")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("P1 5 / WR")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("P1 8 / SE")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_0) PORT_NAME("P1 0 / Demo")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("P2 3 / ST")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("P2 6 / TE")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("P2 9")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME("P2 No / Cancel")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("P1 3 / ST")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("P1 6 / TE")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_9) PORT_NAME("P1 9")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("P1 No / Cancel")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void talkingfb_state::talkingfb(machine_config &config)
{
	// basic machine hardware
	I80C31(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &talkingfb_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &talkingfb_state::main_io);
	m_maincpu->port_out_cb<1>().set("dac", FUNC(dac_8bit_r2r_device::write));
	m_maincpu->port_out_cb<3>().set(FUNC(talkingfb_state::bank_w));

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_8BIT_R2R(config, "dac").add_route(ALL_OUTPUTS, "speaker", 0.5);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( talkingfb )
	ROM_REGION( 0x40000, "maincpu", 0 )
	ROM_LOAD("sp17492-001", 0x0000, 0x40000, CRC(d03851c6) SHA1(e8ac9c342bee987657c3c18f24d466b3906d6fb0) ) // Sharp LH532378
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY, FULLNAME, FLAGS
SYST( 1989, talkingfb, 0,      0,      talkingfb, talkingfb, talkingfb_state, empty_init, "Parker Brothers", "Superstar Lineup Talking Football", MACHINE_SUPPORTS_SAVE )



tamagotchi_pix.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/******************************************************************************

main SoC is marked

SONIX
SN73231M1N-000
215EATB1e^^e3  (^^ are some kind of graphic)

unknown architecture

other sources mention that the Tamagotchi Pix uses a GeneralPlus GP32 (ARM)
series CPU, so are there mutliple hardware revisions or is that information
incorrect?

device has a camera

*******************************************************************************/

#include "emu.h"

#include "screen.h"
#include "speaker.h"


namespace {

class tamagotchi_pix_state : public driver_device
{
public:
	tamagotchi_pix_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_screen(*this, "screen")
	{ }

	void tamapix(machine_config &config);

protected:
	virtual void machine_start() override;
	virtual void machine_reset() override;

private:
	required_device<screen_device> m_screen;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};

uint32_t tamagotchi_pix_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void tamagotchi_pix_state::machine_start()
{

}

void tamagotchi_pix_state::machine_reset()
{
}

static INPUT_PORTS_START( tamapix )
INPUT_PORTS_END

void tamagotchi_pix_state::tamapix(machine_config &config)
{
	// unknown CPU

	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(256, 256); // unknown resolution
	m_screen->set_visarea(0, 255-1, 0, 255-1);
	m_screen->set_screen_update(FUNC(tamagotchi_pix_state::screen_update));

	SPEAKER(config, "mono").front_center();
}

ROM_START( tamapix )
	ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASEFF )
	// this is an SPI ROM so there's probably an internal bootstrap at least
	ROM_LOAD( "25q64.u5", 0x000000, 0x800000, CRC(559d0cc8) SHA1(bd5510a38cd4b293bc89bced99718d2998c5b893) )
ROM_END

} // anonymous namespace

CONS( 2020, tamapix,       0,              0,      tamapix, tamapix, tamagotchi_pix_state, empty_init, "Bandai", "Tamagotchi Pix", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



tandy1t.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/*
Tandy 1000
==========

Tandy 1000 machines are similar to the IBM 5160s with CGA graphics. Tandy
added some additional graphic capabilities similar, but not equal, to
those added for the IBM PC Jr.

The Tandy 1000's features include a pair of 6-pin mini-DIN connectors for
analog joysticks. They are compatible with CoCo joysticks but not standard
PC joysticks, though the software interface is identical. This difference
is not accurately reflected in the current slot device configuration.

Tandy 1000 (8088) variations:
1000                128KB-640KB RAM     4.77 MHz        v01.00.00, v01.01.00
1000A/1000HD        128KB-640KB RAM     4.77 MHz        v01.01.00
1000SX/1000AX       384KB-640KB RAM     7.16/4.77 MHz   v01.02.00
1000EX              256KB-640KB RAM     7.16/4.77 MHz   v01.02.00
1000HX              256KB-640KB RAM     7.16/4.77 MHz   v02.00.00

Tandy 1000 (8086) variations:
1000RL/1000RL-HD    512KB-768KB RAM     9.44/4.77 MHz   v02.00.00, v02.00.01
1000SL/1000PC       384KB-640KB RAM     8.0/4.77 MHz    v01.04.00, v01.04.01, v01.04.02, v02.00.01
1000SL/2            512KB-640KB RAM     8.0/4.77 MHz    v01.04.04

Tandy 1000 (80286) variations:
1000TX              640KB-768KB RAM     8.0/4.77 MHz    v01.03.00
1000TL              640KB-768KB RAM     8.0/4.77 MHz    v01.04.00, v01.04.01, v01.04.02
1000TL/2            640KB-768KB RAM     8.0/4.77 MHz    v02.00.00
1000TL/3            640KB-768KB RAM     10.0/5.0 MHz    v02.00.00
1000RLX             512KB-1024KB RAM    10.0/5.0 MHz    v02.00.00
1000RLX-HD          1024MB RAM          10.0/5.0 MHz    v02.00.00

Tandy 1000 (80386) variations:
1000RSX/1000RSX-HD  1M-9M RAM           25.0/8.0 MHz    v01.10.00

According to http://nerdlypleasures.blogspot.com/2014/04/the-original-8-bit-ide-interface.html
the 286 based Tandy 1000 TL/2, TL/3, RLX, RLX-B and the 8086 based Tandy 1000RL & RL-HD
used XTA (8-bit IDE) harddisks.
*/

#include "emu.h"
#include "machine/genpc.h"
#include "machine/pckeybrd.h"
#include "machine/nvram.h"
#include "machine/bankdev.h"
#include "pc_t1t.h"
#include "sound/sn76496.h"
#include "cpu/i86/i86.h"
#include "cpu/i86/i286.h"
#include "bus/pc_joy/pc_joy.h"
#include "screen.h"
#include "softlist_dev.h"

DECLARE_DEVICE_TYPE(T1000_MOTHERBOARD, t1000_mb_device)

class t1000_mb_device : public pc_noppi_mb_device
{
public:
	// construction/destruction
	t1000_mb_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
		: pc_noppi_mb_device(mconfig, T1000_MOTHERBOARD, tag, owner, clock)
	{ }
protected:
	virtual void device_start() override ATTR_COLD;
};

void t1000_mb_device::device_start()
{
}

DEFINE_DEVICE_TYPE(T1000_MOTHERBOARD, t1000_mb_device, "t1000_mb", "Tandy 1000 Motherboard")

class tandy1000_state : public driver_device
{
public:
	tandy1000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_biosbank(*this, "biosbank")
		, m_keyboard(*this, "pc_keyboard")
		, m_mb(*this, "mb")
		, m_video(*this, "pcvideo_t1000")
		, m_ram(*this, RAM_TAG)
		, m_vram_bank(0) { }

	void tandy1000_common(machine_config &config);
	void tandy1000_90key(machine_config &config);
	void tandy1000_101key(machine_config &config);
	void t1000tl(machine_config &config);
	void t1000tl2(machine_config &config);
	void t1000sx(machine_config &config);
	void t1000rl(machine_config &config);
	void t1000sl2(machine_config &config);
	void t1000hx(machine_config &config);
	void t1000tx(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;

// Memory regions for the machines that support rom banking
	optional_device<address_map_bank_device> m_biosbank;

	required_device<pc_keyboard_device> m_keyboard;
	required_device<t1000_mb_device> m_mb;
	required_device<pcvideo_t1000_device> m_video;
	required_device<ram_device> m_ram;

	void pc_t1t_p37x_w(offs_t offset, uint8_t data);
	uint8_t pc_t1t_p37x_r(offs_t offset);

	void tandy1000_pio_w(offs_t offset, uint8_t data);
	uint8_t tandy1000_pio_r(offs_t offset);
	uint8_t tandy1000_bank_r(offs_t offset);
	void tandy1000_bank_w(offs_t offset, uint8_t data);
	void nmi_vram_bank_w(uint8_t data);
	void vram_bank_w(uint8_t data);
	uint8_t vram_r(offs_t offset);
	void vram_w(offs_t offset, uint8_t data);
	void devctrl_w(uint8_t data);
	uint8_t unk_r();

	int tandy1000_read_eeprom();
	void tandy1000_write_eeprom(uint8_t data);
	void tandy1000_set_bios_bank();

	DECLARE_MACHINE_RESET(tandy1000rl);

	struct
	{
		uint8_t low = 0, high = 0;
	} m_eeprom_ee[0x40]; /* only 0 to 4 used in hx, addressing seems to allow this */

	int m_eeprom_state = 0;
	int m_eeprom_clock = 0;
	uint8_t m_eeprom_oper = 0;
	uint16_t m_eeprom_data = 0;

	uint8_t m_tandy_data[8]{};

	uint8_t m_tandy_bios_bank = 0;    /* I/O port FFEAh */
	uint8_t m_tandy_ppi_porta = 0, m_tandy_ppi_ack = 0;
	uint8_t m_tandy_ppi_portb = 0, m_tandy_ppi_portc = 0;
	uint8_t m_vram_bank = 0;
	static void cfg_fdc_35(device_t *device);
	static void cfg_fdc_525(device_t *device);

	void biosbank_map(address_map &map) ATTR_COLD;
	void tandy1000_16_io(address_map &map) ATTR_COLD;
	void tandy1000_286_map(address_map &map) ATTR_COLD;
	void tandy1000_286_bank_map(address_map &map) ATTR_COLD;
	void tandy1000_bank_io(address_map &map) ATTR_COLD;
	void tandy1000_bank_map(address_map &map) ATTR_COLD;
	void tandy1000_io(address_map &map) ATTR_COLD;
	void tandy1000_map(address_map &map) ATTR_COLD;
	void tandy1000tx_io(address_map &map) ATTR_COLD;
};

/* tandy 1000 eeprom
  hx and later
  clock, and data out lines at 0x37c
  data in at 0x62 bit 4 (0x10)

  8x read 16 bit word at x
  30 cx 30 4x 16bit 00 write 16bit at x
*/

int tandy1000_state::tandy1000_read_eeprom()
{
	if ((m_eeprom_state>=100)&&(m_eeprom_state<=199))
		return m_eeprom_data&0x8000;
	return 1;
}

void tandy1000_state::tandy1000_write_eeprom(uint8_t data)
{
	if (!m_eeprom_clock && (data&4) )
	{
//              logerror("!!!tandy1000 eeprom %.2x %.2x\n",m_eeprom_state, data);
		switch (m_eeprom_state)
		{
		case 0:
			if ((data&3)==0) m_eeprom_state++;
			break;
		case 1:
			if ((data&3)==2) m_eeprom_state++;
			break;
		case 2:
			if ((data&3)==3) m_eeprom_state++;
			break;
		case 3:
			m_eeprom_oper=data&1;
			m_eeprom_state++;
			break;
		case 4:
		case 5:
		case 6:
		case 7:
		case 8:
		case 9:
			m_eeprom_oper=(m_eeprom_oper<<1)|(data&1);
			m_eeprom_state++;
			break;
		case 10:
			m_eeprom_oper=(m_eeprom_oper<<1)|(data&1);
			logerror("!!!tandy1000 eeprom %.2x\n",m_eeprom_oper);
			if ((m_eeprom_oper&0xc0)==0x80)
			{
				m_eeprom_state=100;
				m_eeprom_data=m_eeprom_ee[m_eeprom_oper&0x3f].low
					|(m_eeprom_ee[m_eeprom_oper&0x3f].high<<8);
				logerror("!!!tandy1000 eeprom read %.2x,%.4x\n",m_eeprom_oper,m_eeprom_data);
			}
			else if ((m_eeprom_oper&0xc0)==0x40)
			{
				m_eeprom_state=200;
			}
			else
				m_eeprom_state=0;
			break;

			/* read 16 bit */
		case 100:
			m_eeprom_state++;
			break;
		case 101:
		case 102:
		case 103:
		case 104:
		case 105:
		case 106:
		case 107:
		case 108:
		case 109:
		case 110:
		case 111:
		case 112:
		case 113:
		case 114:
		case 115:
			m_eeprom_data<<=1;
			m_eeprom_state++;
			break;
		case 116:
			m_eeprom_data<<=1;
			m_eeprom_state=0;
			break;

			/* write 16 bit */
		case 200:
		case 201:
		case 202:
		case 203:
		case 204:
		case 205:
		case 206:
		case 207:
		case 208:
		case 209:
		case 210:
		case 211:
		case 212:
		case 213:
		case 214:
			m_eeprom_data=(m_eeprom_data<<1)|(data&1);
			m_eeprom_state++;
			break;
		case 215:
			m_eeprom_data=(m_eeprom_data<<1)|(data&1);
			logerror("tandy1000 %.2x %.4x written\n",m_eeprom_oper,m_eeprom_data);
			m_eeprom_ee[m_eeprom_oper&0x3f].low=m_eeprom_data&0xff;
			m_eeprom_ee[m_eeprom_oper&0x3f].high=m_eeprom_data>>8;
			m_eeprom_state=0;
			break;
		}
	}
	m_eeprom_clock=data&4;
}

void tandy1000_state::pc_t1t_p37x_w(offs_t offset, uint8_t data)
{
//  DBG_LOG(2,"T1T_p37x_w",("%.5x #%d $%02x\n", m_maincpu->pc( ),offset, data));
	if (offset!=4)
		logerror("T1T_p37x_w %.5x #%d $%02x\n", m_maincpu->pc( ),offset, data);
	m_tandy_data[offset]=data;
	switch( offset )
	{
		case 4:
			tandy1000_write_eeprom(data);
			break;
	}
}

uint8_t tandy1000_state::pc_t1t_p37x_r(offs_t offset)
{
	int data = m_tandy_data[offset];
//  DBG_LOG(1,"T1T_p37x_r",("%.5x #%d $%02x\n", m_maincpu->pc( ), offset, data));
	return data;
}

/* this is for tandy1000hx
   hopefully this works for all x models
   must not be a ppi8255 chip
   (think custom chip)
   port c:
   bit 4 input eeprom data in
   bit 3 output turbo mode
*/

void tandy1000_state::tandy1000_pio_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 1:
		m_tandy_ppi_portb = data;
		m_mb->m_pit8253->write_gate2(BIT(data, 0));
		m_mb->pc_speaker_set_spkrdata( data & 0x02 );
		// sx enables keyboard from bit 3, others bit 6, hopefully theres no conflict
		m_keyboard->enable(data&0x48);
		if ( data & 0x80 )
		{
			m_mb->m_pic8259->ir1_w(0);
			m_tandy_ppi_ack = 1;
		}
		break;
	case 2:
		m_tandy_ppi_portc = data;
		if (data & 8)
			m_maincpu->set_clock_scale(1);
		else
			m_maincpu->set_clock_scale(4.77/8);
		break;
	}
}

uint8_t tandy1000_state::tandy1000_pio_r(offs_t offset)
{
	int data=0xff;
	switch (offset)
	{
	case 0:
		if (m_tandy_ppi_ack)
		{
			m_tandy_ppi_porta = m_keyboard->read();
			m_tandy_ppi_ack = 0;
		}
		data = m_tandy_ppi_porta;
		break;
	case 1:
		data=m_tandy_ppi_portb;
		break;
	case 2:
//      if (tandy1000hx) {
//      data=m_tandy_ppi_portc; // causes problems (setuphx)
		if (!tandy1000_read_eeprom()) data&=~0x10;
//  }
		break;
	}
	return data;
}

void tandy1000_state::nmi_vram_bank_w(uint8_t data)
{
	m_mb->nmi_enable_w(data & 0x80);
	vram_bank_w(data & 0x1e);
	m_video->disable_w((data & 1) ? ASSERT_LINE : CLEAR_LINE);
}

void tandy1000_state::vram_bank_w(uint8_t data)
{
	m_vram_bank = (data >> 1) & 7;
}

void tandy1000_state::devctrl_w(uint8_t data)
{
	m_video->disable_w((data & 4) ? ASSERT_LINE : CLEAR_LINE);
}

uint8_t tandy1000_state::unk_r()
{
	return 0; // status port?
}

uint8_t tandy1000_state::vram_r(offs_t offset)
{
	uint8_t vram_base = (m_ram->size() >> 17) - 1;
	if((m_vram_bank - vram_base) == (offset >> 17))
		return m_ram->pointer()[offset & 0x1ffff];
	return 0xff;
}

void tandy1000_state::vram_w(offs_t offset, uint8_t data)
{
	uint8_t vram_base = (m_ram->size() >> 17) - 1;
	if((m_vram_bank - vram_base) == (offset >> 17))
		m_ram->pointer()[offset & 0x1ffff] = data;
}

void tandy1000_state::tandy1000_set_bios_bank()
{
	int bank;

	if ( m_tandy_bios_bank & 0x10 )
	{
		if (m_tandy_bios_bank & 0x04 )
		{
			bank = (m_tandy_bios_bank & 3) + 8;
		}
		else
		{
			bank = m_tandy_bios_bank & 3;
		}
	}
	else
	{
		bank = m_tandy_bios_bank & 0x0f;
	}

	m_biosbank->set_bank( bank );
}

MACHINE_RESET_MEMBER(tandy1000_state, tandy1000rl)
{
	m_tandy_bios_bank = 6;
	tandy1000_set_bios_bank();
}

void tandy1000_state::machine_start()
{
	m_maincpu->space(AS_PROGRAM).install_ram(0, m_ram->size() - (128*1024) - 1, &m_ram->pointer()[128*1024]);
	if(m_maincpu->space(AS_PROGRAM).data_width() == 8)
		m_maincpu->space(AS_PROGRAM).install_readwrite_handler(m_ram->size() - (128*1024), 640*1024 - 1,
				read8sm_delegate(*this, FUNC(tandy1000_state::vram_r)), write8sm_delegate(*this, FUNC(tandy1000_state::vram_w)));
	else
		m_maincpu->space(AS_PROGRAM).install_readwrite_handler(m_ram->size() - (128*1024), 640*1024 - 1,
				read8sm_delegate(*this, FUNC(tandy1000_state::vram_r)), write8sm_delegate(*this, FUNC(tandy1000_state::vram_w)), 0xffff);
	subdevice<nvram_device>("nvram")->set_base(m_eeprom_ee, sizeof(m_eeprom_ee));

	m_eeprom_state = 0;
}

uint8_t tandy1000_state::tandy1000_bank_r(offs_t offset)
{
	uint8_t data = 0xFF;

	logerror( "%s: tandy1000_bank_r: offset = %x\n", machine().describe_context(), offset );

	switch( offset )
	{
	case 0x00:  /* FFEA */
		data = m_tandy_bios_bank ^ 0x10; // Bit 4 is read back inverted
		break;
	}

	return data;
}


void tandy1000_state::tandy1000_bank_w(offs_t offset, uint8_t data)
{
	logerror( "%s: tandy1000_bank_w: offset = %x, data = %02x\n", machine().describe_context(), offset, data );

	switch( offset )
	{
	case 0x00:  /* FFEA */
		m_tandy_bios_bank = data;
		tandy1000_set_bios_bank();
		break;

	// UART clock, joystick, and sound enable
	// bit 0 - 0 = Clock divided by 13
	//         1 = Clock divided by 1
	// bit 1 - 0 = Disable joystick
	//         1 = Enable joystick
	// bit 2 - 0 = Disable Sound Chip
	//         1 = Enable Sound Chip
	case 0x01:
		break;
	}
}

static INPUT_PORTS_START( t1000 )
	PORT_START("IN0") /* IN0 */
	PORT_BIT ( 0xf0, 0xf0,   IPT_UNUSED )
	PORT_BIT ( 0x08, 0x08,   IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("pcvideo_t1000:screen", FUNC(screen_device::vblank))
	PORT_BIT ( 0x07, 0x07,   IPT_UNUSED )

	PORT_START("DSW0") /* IN1 */
	PORT_BIT ( 0xff, 0xff,   IPT_UNUSED )

	PORT_START("DSW1") /* IN2 */
	PORT_DIPNAME( 0x80, 0x80, "COM1: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x40, 0x40, "COM2: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Yes ) )
	PORT_BIT ( 0x30, 0x00,   IPT_UNUSED )
	PORT_DIPNAME( 0x08, 0x08, "LPT1: enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Yes ) )
	PORT_BIT ( 0x06, 0x00,   IPT_UNUSED )
	PORT_DIPNAME( 0x01, 0x00, "Game port enable")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x01, DEF_STR( Yes ) )

	PORT_START("DSW2") /* IN3 */
	PORT_DIPNAME( 0x08, 0x08, "HDC1 (C800:0 port 320-323)")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x04, 0x04, "HDC2 (CA00:0 port 324-327)")
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Yes ) )
	PORT_BIT( 0x02, 0x02,   IPT_UNUSED ) /* no turbo switch */
	PORT_BIT( 0x01, 0x01,   IPT_UNUSED )
INPUT_PORTS_END

/* 90-key Tandy Keyboard layout, used on earlier models
   later models use the Tandy Enhanced Keyboard with a standard 101-key Enhanced AT layout */
static INPUT_PORTS_START( t1000_keyboard )
	PORT_INCLUDE(pc_keyboard)

	PORT_MODIFY("pc_keyboard_2")
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Cursor Up") PORT_CODE(KEYCODE_UP) /*                             29  A9 */
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Cursor Left") PORT_CODE(KEYCODE_LEFT) /*                             2B  AB */

	PORT_MODIFY("pc_keyboard_3")
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Caps") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE   /* Caps Lock                   3A  BA */

	PORT_MODIFY("pc_keyboard_4")
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("NumLock") PORT_CODE(KEYCODE_NUMLOCK) PORT_TOGGLE /* Num Lock                    45  C5 */
	/* Hold corresponds to Scroll Lock, but pauses the system when pressed - leaving unmapped by default to avoid conflicting with the UI Toggle key */
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Hold")     /*                            46  C6 */
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 7 \\") PORT_CODE(KEYCODE_7_PAD) /* Keypad 7                    47  C7 */
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 8 ~") PORT_CODE(KEYCODE_8_PAD) /* Keypad 8                    48  C8 */
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 9 (PgUp)") PORT_CODE(KEYCODE_9_PAD) /* Keypad 9  (PgUp)            49  C9 */
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Cursor Down") PORT_CODE(KEYCODE_DOWN) /*                             4A  CA */
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 4 |") PORT_CODE(KEYCODE_4_PAD) /* Keypad 4                    4B  CB */
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 6") PORT_CODE(KEYCODE_6_PAD) /* Keypad 6                    4D  CD */
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Cursor Right") PORT_CODE(KEYCODE_RIGHT) /*                             4E  CE */
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 1 (End)") PORT_CODE(KEYCODE_1_PAD) /* Keypad 1  (End)             4F  CF */

	PORT_MODIFY("pc_keyboard_5")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 2 `") PORT_CODE(KEYCODE_2_PAD) /* Keypad 2                    50  D0 */
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 3 (PgDn)") PORT_CODE(KEYCODE_3_PAD) /* Keypad 3  (PgDn)            51  D1 */
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 0") PORT_CODE(KEYCODE_0_PAD) /* Keypad 0                    52  D2 */
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP - (Del)") PORT_CODE(KEYCODE_MINUS_PAD) /* - Delete                    53  D3 */
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_STOP) /* Break                       54  D4 */
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+ Insert") PORT_CODE(KEYCODE_PLUS_PAD) /* + Insert                    55  D5 */
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_DEL_PAD) /* .                           56  D6 */
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER_PAD) /* Enter                       57  D7 */
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Home") PORT_CODE(KEYCODE_HOME) /* HOME                        58  D8 */
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F11") PORT_CODE(KEYCODE_F11) /* F11                         59  D9 */
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F12") PORT_CODE(KEYCODE_F12) /* F12                         5a  Da */
INPUT_PORTS_END

class t1000_keyboard_device : public pc_keyboard_device
{
public:
	t1000_keyboard_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);

protected:
	virtual ioport_constructor device_input_ports() const override ATTR_COLD;
};

DEFINE_DEVICE_TYPE(T1000_KEYB, t1000_keyboard_device, "t1000_keyb", "Tandy 1000 Keyboard")

t1000_keyboard_device::t1000_keyboard_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	pc_keyboard_device(mconfig, T1000_KEYB, tag, owner, clock)
{
	m_type = KEYBOARD_TYPE::PC;
}

ioport_constructor t1000_keyboard_device::device_input_ports() const
{
	return INPUT_PORTS_NAME(t1000_keyboard);
}



void tandy1000_state::tandy1000_map(address_map &map)
{
	map.unmap_value_high();
	map(0xb8000, 0xbffff).m("pcvideo_t1000:vram", FUNC(address_map_bank_device::amap8));
	map(0xe0000, 0xfffff).rom().region("bios", 0);
}

void tandy1000_state::tandy1000_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m(m_mb, FUNC(t1000_mb_device::map));
	map(0x0060, 0x0063).rw(FUNC(tandy1000_state::tandy1000_pio_r), FUNC(tandy1000_state::tandy1000_pio_w));
	map(0x00a0, 0x00a0).w(FUNC(tandy1000_state::nmi_vram_bank_w));
	map(0x00c0, 0x00c0).w("sn76496", FUNC(ncr8496_device::write));
	map(0x0200, 0x0207).rw("pc_joy", FUNC(pc_joy_device::joy_port_r), FUNC(pc_joy_device::joy_port_w));
	map(0x0378, 0x037f).rw(FUNC(tandy1000_state::pc_t1t_p37x_r), FUNC(tandy1000_state::pc_t1t_p37x_w));
	map(0x03d0, 0x03df).r(m_video, FUNC(pcvideo_t1000_device::read)).w(m_video, FUNC(pcvideo_t1000_device::write));
}

void tandy1000_state::tandy1000_bank_map(address_map &map)
{
	map.unmap_value_high();
	map(0xb8000, 0xbffff).m("pcvideo_t1000:vram", FUNC(address_map_bank_device::amap8));
	map(0xe0000, 0xeffff).m(m_biosbank, FUNC(address_map_bank_device::amap16));
	map(0xf0000, 0xfffff).rom().region("rom", 0x70000);
}

void tandy1000_state::biosbank_map(address_map &map)
{
	map.unmap_value_high();
	map(0x80000, 0xfffff).rom().region("rom", 0);
}

void tandy1000_state::tandy1000_16_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m(m_mb, FUNC(t1000_mb_device::map));
	map(0x0060, 0x0063).rw(FUNC(tandy1000_state::tandy1000_pio_r), FUNC(tandy1000_state::tandy1000_pio_w));
	map(0x0065, 0x0065).w(FUNC(tandy1000_state::devctrl_w));
	map(0x00a0, 0x00a0).r(FUNC(tandy1000_state::unk_r));
	map(0x00c0, 0x00c1).w("sn76496", FUNC(ncr8496_device::write));
	map(0x0200, 0x0207).rw("pc_joy", FUNC(pc_joy_device::joy_port_r), FUNC(pc_joy_device::joy_port_w));
	map(0x0378, 0x037f).rw(FUNC(tandy1000_state::pc_t1t_p37x_r), FUNC(tandy1000_state::pc_t1t_p37x_w));
	map(0x03d0, 0x03df).r(m_video, FUNC(pcvideo_t1000_device::read)).w(m_video, FUNC(pcvideo_t1000_device::write));
	map(0xffe8, 0xffe8).w(FUNC(tandy1000_state::vram_bank_w));
}

void tandy1000_state::tandy1000_bank_io(address_map &map)
{
	map.unmap_value_high();
	tandy1000_16_io(map);
	map(0xffea, 0xffeb).rw(FUNC(tandy1000_state::tandy1000_bank_r), FUNC(tandy1000_state::tandy1000_bank_w));
}

void tandy1000_state::tandy1000tx_io(address_map &map)
{
	map.unmap_value_high();
	tandy1000_16_io(map);
	map(0x00a0, 0x00a0).w(FUNC(tandy1000_state::nmi_vram_bank_w));
}

void tandy1000_state::tandy1000_286_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x000fffff);
	map(0xb8000, 0xbffff).m("pcvideo_t1000:vram", FUNC(address_map_bank_device::amap8));
	map(0xe0000, 0xfffff).rom().region("bios", 0);
}

void tandy1000_state::tandy1000_286_bank_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x000fffff);
	map(0xb8000, 0xbffff).m("pcvideo_t1000:vram", FUNC(address_map_bank_device::amap8));
	map(0xe0000, 0xeffff).m(m_biosbank, FUNC(address_map_bank_device::amap16));
	map(0xf0000, 0xfffff).rom().region("rom", 0x70000);
}

static const gfx_layout t1000_charlayout =
{
	8, 16,
	256,
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0, 2048, 4096, 6144, 8192, 10240, 12288, 14336, 16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720 },
	8
};


void tandy1000_state::cfg_fdc_35(device_t *device)
{
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:0")).set_default_option("35dd");
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:0")).set_fixed(true);
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:1")).set_default_option(nullptr);
}

void tandy1000_state::cfg_fdc_525(device_t *device)
{
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:0")).set_fixed(true);
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:1")).set_default_option(nullptr);
}

static GFXDECODE_START( gfx_t1000 )
	GFXDECODE_ENTRY( "gfx1", 0x0000, t1000_charlayout, 3, 1 )
GFXDECODE_END

void tandy1000_state::tandy1000_common(machine_config &config)
{
	T1000_MOTHERBOARD(config, m_mb, 0);
	m_mb->set_cputag("maincpu");
	m_mb->int_callback().set_inputline("maincpu", 0);
	m_mb->nmi_callback().set_inputline("maincpu", INPUT_LINE_NMI);

	/* video hardware */
	PCVIDEO_T1000(config, m_video, 0);
	m_video->set_screen("pcvideo_t1000:screen");
	GFXDECODE(config, "gfxdecode", "pcvideo_t1000:palette", gfx_t1000);

	/* sound hardware */
	NCR8496(config, "sn76496", XTAL(14'318'181)/4).add_route(ALL_OUTPUTS, "mb:mono", 0.80);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	isa8_slot_device &isa_fdc(ISA8_SLOT(config, "isa_fdc", 0, "mb:isa", pc_isa8_cards, "fdc_xt", true)); // FIXME: determine ISA bus clock
	isa_fdc.set_option_machine_config("fdc_xt", cfg_fdc_35);

	ISA8_SLOT(config, "isa_lpt", 0, "mb:isa", pc_isa8_cards, "lpt", true);
	ISA8_SLOT(config, "isa_com", 0, "mb:isa", pc_isa8_cards, "com", true);

	PC_JOY(config, "pc_joy");

	/* internal ram */
	RAM(config, m_ram).set_default_size("640K");

	SOFTWARE_LIST(config, "disk_list").set_original("t1000");
	SOFTWARE_LIST(config, "pc_list").set_compatible("ibm5150");
}

void tandy1000_state::tandy1000_90key(machine_config &config)
{
	T1000_KEYB(config, m_keyboard);
	m_keyboard->keypress().set("mb:pic8259", FUNC(pic8259_device::ir1_w));
}

void tandy1000_state::tandy1000_101key(machine_config &config)
{
	AT_KEYB(config, m_keyboard, pc_keyboard_device::KEYBOARD_TYPE::AT, 1);
	m_keyboard->keypress().set("mb:pic8259", FUNC(pic8259_device::ir1_w));
}

void tandy1000_state::t1000hx(machine_config &config)
{
	I8088(config, m_maincpu, 8000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &tandy1000_state::tandy1000_map);
	m_maincpu->set_addrmap(AS_IO, &tandy1000_state::tandy1000_io);
	m_maincpu->set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	tandy1000_common(config);

	tandy1000_90key(config);

	// plus cards are isa with a nonstandard conntector
	ISA8_SLOT(config, "plus1", 0, "mb:isa", pc_isa8_cards, nullptr, false); // FIXME: determine ISA bus clock

	m_ram->set_extra_options("256K, 384K");
}

void tandy1000_state::t1000sx(machine_config &config)
{
	t1000hx(config);
	subdevice<isa8_slot_device>("isa_fdc")->set_option_machine_config("fdc_xt", cfg_fdc_525);

	ISA8_SLOT(config, "isa1", 0, "mb:isa", pc_isa8_cards, nullptr, false); // FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa2", 0, "mb:isa", pc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa3", 0, "mb:isa", pc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa4", 0, "mb:isa", pc_isa8_cards, nullptr, false);

	m_ram->set_extra_options("384K");
}

void tandy1000_state::t1000rl(machine_config &config)
{
	I8086(config, m_maincpu, XTAL(28'636'363) / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &tandy1000_state::tandy1000_bank_map);
	m_maincpu->set_addrmap(AS_IO, &tandy1000_state::tandy1000_bank_io);
	m_maincpu->set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	tandy1000_common(config);

	tandy1000_101key(config);

	ADDRESS_MAP_BANK(config, "biosbank").set_map(&tandy1000_state::biosbank_map).set_options(ENDIANNESS_LITTLE, 16, 20, 0x10000);

	MCFG_MACHINE_RESET_OVERRIDE(tandy1000_state,tandy1000rl)

	m_ram->set_extra_options("384K");
}

void tandy1000_state::t1000sl2(machine_config &config)
{
	t1000rl(config);
	m_maincpu->set_clock(XTAL(24'000'000) / 3);

	ISA8_SLOT(config, "isa1", 0, "mb:isa", pc_isa8_cards, nullptr, false); // FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa2", 0, "mb:isa", pc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa3", 0, "mb:isa", pc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa4", 0, "mb:isa", pc_isa8_cards, nullptr, false);
}

void tandy1000_state::t1000tl2(machine_config &config)
{
	I80286(config, m_maincpu, XTAL(28'636'363) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &tandy1000_state::tandy1000_286_map);
	m_maincpu->set_addrmap(AS_IO, &tandy1000_state::tandy1000_16_io);
	m_maincpu->set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	tandy1000_common(config);

	tandy1000_101key(config);

	ISA8_SLOT(config, "isa1", 0, "mb:isa", pc_isa8_cards, nullptr, false); // FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa2", 0, "mb:isa", pc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa3", 0, "mb:isa", pc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa4", 0, "mb:isa", pc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa5", 0, "mb:isa", pc_isa8_cards, nullptr, false);
}

void tandy1000_state::t1000tl(machine_config &config)
{
	t1000tl2(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &tandy1000_state::tandy1000_286_bank_map);
	m_maincpu->set_addrmap(AS_IO, &tandy1000_state::tandy1000_bank_io);

	ADDRESS_MAP_BANK(config, "biosbank").set_map(&tandy1000_state::biosbank_map).set_options(ENDIANNESS_LITTLE, 16, 20, 0x10000);
}

void tandy1000_state::t1000tx(machine_config &config)
{
	t1000tl2(config);
	m_maincpu->set_addrmap(AS_IO, &tandy1000_state::tandy1000tx_io);

	config.device_remove("pc_keyboard");
	tandy1000_90key(config);
}

#ifdef UNUSED_DEFINITION
ROM_START( t1000 )
	// Schematic shows 2 32KB ROMs at U9 and U10 for Tandy 1000; 1000A is a different mainboard.
	ROM_REGION(0x20000,"bios", 0)
	ROM_SYSTEM_BIOS( 0, "v010000", "v010000" )
	ROMX_LOAD("v010000.f0", 0x10000, 0x10000, NO_DUMP, ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v010100", "v010100" )
	ROMX_LOAD("v010100.f0", 0x10000, 0x10000, CRC(b6760881) SHA1(8275e4c48ac09cf36685db227434ca438aebe0b9), ROM_BIOS(1))

	// Part of video array at u76?
	ROM_REGION(0x08000,"gfx1", 0)
	ROM_LOAD("8079027.u76", 0x00000, 0x04000, CRC(33d64a11) SHA1(b63da2a656b6c0a8a32f2be8bdcb51aed983a450)) // TODO: Verify location
ROM_END

ROM_START( t1000a )
	ROM_REGION(0x20000,"bios", 0)
	// partlist says it has 1 128kbyte rom
	ROM_LOAD("t1000hx.e0", 0x00000, 0x10000, CRC(61dbf242) SHA1(555b58d8aa8e0b0839259621c44b832d993beaef))  // not sure about this one
	ROM_LOAD("v010100.f0", 0x10000, 0x10000, CRC(b6760881) SHA1(8275e4c48ac09cf36685db227434ca438aebe0b9))

	ROM_REGION(0x08000,"gfx1", 0)
	ROM_LOAD("8079027.u25", 0x00000, 0x04000, CRC(33d64a11) SHA1(b63da2a656b6c0a8a32f2be8bdcb51aed983a450)) // TODO: Verify location
ROM_END

ROM_START( t1000ex )
	ROM_REGION(0x20000,"bios", 0)
	// partlist says it has 1 128kb rom, schematics list a 32k x 8 rom
	// "8040328.u17"
	ROM_LOAD("t1000hx.e0", 0x00000, 0x10000, CRC(61dbf242) SHA1(555b58d8aa8e0b0839259621c44b832d993beaef))  // not sure about this one
	ROM_LOAD("v010200.f0", 0x10000, 0x10000, CRC(0e016ecf) SHA1(2f5ac8921b7cba56b02122ef772f5f11bbf6d8a2))

	// TODO: Add dump of the 8048 at u8 if it ever gets dumped
	ROM_REGION(0x400, "kbdc", 0)
	ROM_LOAD("8048.u8", 0x000, 0x400, NO_DUMP)

	// Most likely part of big blue at u28
	ROM_REGION(0x08000,"gfx1", 0)
	ROM_LOAD("8079027.u28", 0x00000, 0x04000, CRC(33d64a11) SHA1(b63da2a656b6c0a8a32f2be8bdcb51aed983a450)) // TODO: Verify location
ROM_END

// The T1000SL and T1000SL/2 only differ in amount of RAM installed and BIOS version (SL/2 has v01.04.04)
ROM_START( t1000sl )
	ROM_REGION(0x20000,"bios", 0)

	// 8076312.hu1 - most likely v01.04.00
	// 8075312.hu2


	// partlist says it has 1 128kbyte rom
	ROM_LOAD("t1000hx.e0", 0x00000, 0x10000, CRC(61dbf242) SHA1(555b58d8aa8e0b0839259621c44b832d993beaef))  // not sure about this one
	ROM_SYSTEM_BIOS( 0, "v010400", "v010400" )
	ROMX_LOAD("v010400.f0", 0x10000, 0x10000, NO_DUMP, ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v010401", "v010401" )
	ROMX_LOAD("v010401.f0", 0x10000, 0x10000, NO_DUMP, ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v010402", "v010402" )
	ROMX_LOAD("v010402.f0", 0x10000, 0x10000, NO_DUMP, ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "v020001", "v020001" )
	ROMX_LOAD("v020001.f0", 0x10000, 0x10000, NO_DUMP, ROM_BIOS(3) )

	ROM_REGION(0x08000,"gfx1", 0)
	ROM_LOAD("8079027.u25", 0x00000, 0x04000, CRC(33d64a11) SHA1(b63da2a656b6c0a8a32f2be8bdcb51aed983a450))
ROM_END


ROM_START( t1000tl )
	ROM_REGIoN(0x10000, "bios", ROMREGION_ERASE00)

	ROM_REGION(0x80000, "romcs0", 0)
	// These 2 sets most likely have the same contents
	// v01.04.00
	// 8076323.u55 - Sharp - 256KB
	// 8075323.u57 - Sharp - 256KB
	// v01.04.00
	// 8079025.u54 - Hitachi - 256KB
	// 8079026.u56 - Hitachi - 256KB
	ROM_REGION(0x80000, "romcs1", 0)

	// 2x 128x8 eeprom?? @ u58 and u59 - not mentioned in parts list

	ROM_REGION(0x80, "eeprom", 0)
	ROM_LOAD("8040346_9346.u12", xxx ) // 64x16 eeprom

	ROM_REGION(0x08000, "gfx1", 0)
	ROM_LOAD("8079027.u24", 0x00000, 0x04000, CRC(33d64a11) SHA1(b63da2a656b6c0a8a32f2be8bdcb51aed983a450))
ROM_END
#endif

ROM_START( t1000hx )
	ROM_REGION(0x20000,"bios", 0)
	ROM_LOAD("v020000.u12", 0x00000, 0x20000, CRC(6f3acd80) SHA1(976af8c04c3f6fde14d7047f6521d302bdc2d017)) // TODO: Rom label

	// TODO: Add dump of the 8048 at u9 if it ever gets dumped
	ROM_REGION(0x400, "kbdc", 0)
	ROM_LOAD("8048.u9", 0x000, 0x400, NO_DUMP)

	ROM_REGION(0x08000,"gfx1", 0)
	ROM_LOAD("8079027.u31", 0x00000, 0x04000, CRC(33d64a11) SHA1(b63da2a656b6c0a8a32f2be8bdcb51aed983a450)) // TODO: Verify location, probably internal to "big blue" at u31
ROM_END

ROM_START( t1000sx )
	ROM_REGION(0x20000,"bios", 0)
	ROM_LOAD("8040328.u41", 0x18000, 0x8000, CRC(4e2b9f0b) SHA1(e79a9ed9e885736e30d9b135557f0e596ce5a70b))

	// No character rom is listed in the schematics?
	// But disabling it results in no text being printed
	// Part of bigblue at u30??
	ROM_REGION(0x08000,"gfx1", 0)
	ROM_LOAD("8079027.u30", 0x00000, 0x04000, CRC(33d64a11) SHA1(b63da2a656b6c0a8a32f2be8bdcb51aed983a450)) // TODO: Verify location
ROM_END


ROM_START( t1000tx )
	ROM_REGION16_LE(0x20000, "bios", 0)
	// There should be 2 32KBx8 ROMs, one for odd at u38, one for even at u39
	// The machine already boots up with just this one rom
	ROM_LOAD("t1000tx.bin", 0x18000, 0x8000, BAD_DUMP CRC(9b34765c) SHA1(0b07e87f6843393f7d4ca4634b832b0c0bec304e))

	// No character rom is listed in the schematics?
	// It is most likely part of the big blue chip at u36
	ROM_REGION(0x08000,"gfx1", 0)
	ROM_LOAD("8079027.u36", 0x00000, 0x04000, CRC(33d64a11) SHA1(b63da2a656b6c0a8a32f2be8bdcb51aed983a450)) // TODO: Verify location
ROM_END


ROM_START( t1000rl )
	// bankable ROM regions
	ROM_REGION16_LE(0x80000, "rom", 0)
	/* v2.0.0.1 */
	/* Rom is labeled "(C) TANDY CORP. 1990 // 8079073 // LH534G70 JAPAN // 9034 D" */
	ROM_LOAD("8079073.u23", 0x00000, 0x80000, CRC(6fab50f7) SHA1(2ccc02bee4c250dc1b7c17faef2590bc158860b0) )

	ROM_REGION(0x08000,"gfx1", 0)
	/* Character rom located at U3 w/label "8079027 // NCR // 609-2495004 // F841030 A9025" */
	ROM_LOAD("8079027.u3", 0x00000, 0x04000, CRC(33d64a11) SHA1(b63da2a656b6c0a8a32f2be8bdcb51aed983a450)) // TODO: Verify location
ROM_END


ROM_START( t1000sl2 )
	// bankable ROM regions
	ROM_REGION16_LE(0x80000, "rom", 0)
	// v01.04.04 BIOS
	// Fix up memory region (highest address bit flipped??)
	ROM_LOAD16_BYTE("8079047.hu1", 0x40000, 0x20000, CRC(c773ec0e) SHA1(7deb71f14c2c418400b639d60066ab61b7e9df32))
	ROM_CONTINUE(0x00000, 0x20000)
	ROM_LOAD16_BYTE("8079048.hu2", 0x40001, 0x20000, CRC(0f3e6586) SHA1(10f1a7204f69b82a18bc94a3010c9660aec0c802))
	ROM_CONTINUE(0x00001, 0x20000)

	ROM_REGION(0x08000,"gfx1", 0)
	ROM_LOAD("8079027.u25", 0x00000, 0x04000, CRC(33d64a11) SHA1(b63da2a656b6c0a8a32f2be8bdcb51aed983a450))

	ROM_REGION(0x80, "nmc9246n", 0)
	ROM_LOAD("seeprom.bin", 0, 0x80, CRC(4fff41df) SHA1(41a7009694550c017996932beade608cff968f4a))
ROM_END


ROM_START( t1000tl )
	ROM_REGION16_LE(0x80000, "rom", 0)
	// v01.04.02 BIOS
	ROM_LOAD16_BYTE( "8079037.u55", 0x00000, 0x40000, CRC(869dd92e) SHA1(91422085ef541fefede8fcc9a454d0538298d087)) // ┬®TANDY CORP.1988 8079037 LH532370 8849 D.A-EVEN.U55
	ROM_LOAD16_BYTE( "8079038.u57", 0x00001, 0x40000, CRC(9520db5b) SHA1(9416e85cfcf04b18afc2efb8021a1ed79357ad33)) // ┬®TANDY CORP.1988 8079038 LH532371 8848 D.A-ODD.U57

	ROM_REGION(0x08000,"gfx1", 0)
	ROM_LOAD("8079027.u24", 0x00000, 0x04000, CRC(33d64a11) SHA1(b63da2a656b6c0a8a32f2be8bdcb51aed983a450)) // 8079027 NCR 609-2495004 F831628 A8834
ROM_END


ROM_START( t1000tl2 )
	ROM_REGION16_LE(0x20000, "bios", 0)
	ROM_LOAD( "t10000tl2.bin", 0x10000, 0x10000, CRC(e288f12c) SHA1(9d54ccf773cd7202c9906323f1b5a68b1b3a3a67))

	ROM_REGION(0x08000,"gfx1", 0)
	ROM_LOAD("8079027.u24", 0x00000, 0x04000, CRC(33d64a11) SHA1(b63da2a656b6c0a8a32f2be8bdcb51aed983a450)) // TODO: Verify location
ROM_END


// tandy 1000
//    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT  CLASS            INIT        COMPANY              FULLNAME           FLAGS
COMP( 1987, t1000hx,  ibm5150, 0,      t1000hx,  t1000, tandy1000_state, empty_init, "Tandy Radio Shack", "Tandy 1000 HX",   0 )
COMP( 1987, t1000sx,  ibm5150, 0,      t1000sx,  t1000, tandy1000_state, empty_init, "Tandy Radio Shack", "Tandy 1000 SX",   0 )
COMP( 1987, t1000tx,  ibm5150, 0,      t1000tx,  t1000, tandy1000_state, empty_init, "Tandy Radio Shack", "Tandy 1000 TX",   0 )
COMP( 1989, t1000rl,  ibm5150, 0,      t1000rl,  t1000, tandy1000_state, empty_init, "Tandy Radio Shack", "Tandy 1000 RL",   0 )
COMP( 1988, t1000tl,  ibm5150, 0,      t1000tl,  t1000, tandy1000_state, empty_init, "Tandy Radio Shack", "Tandy 1000 TL",   0 )
COMP( 1989, t1000tl2, ibm5150, 0,      t1000tl2, t1000, tandy1000_state, empty_init, "Tandy Radio Shack", "Tandy 1000 TL/2", 0 )
COMP( 1988, t1000sl2, ibm5150, 0,      t1000sl2, t1000, tandy1000_state, empty_init, "Tandy Radio Shack", "Tandy 1000 SL/2", 0 )



tandy2k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/***************************************************************************

    Tandy 2000

****************************************************************************/

/*

    TODO:

    - floppy
        - HDL is also connected to WP/TS input where TS is used to detect motor status
        - 3 second motor off delay timer
    - video (video RAM is at memory top - 0x1400, i.e. 0x1ec00)
    - keyboard ROM, same as earlier tandy 1000
    - WD1010
    - hard disk
    - clock/mouse 8042 mcu ROM, probably same as tandy 1000 isa clock/mouse adapter
    - sab3019 rtc

*/

#include "emu.h"
#include "tandy2k.h"

#include "screen.h"
#include "softlist.h"
#include "speaker.h"


#define LOG 1

// Read/Write Handlers

void tandy2k_state::update_drq()
{
	int drq0 = CLEAR_LINE;
	int drq1 = CLEAR_LINE;

	for (int i = 0; i < 4; i++)
	{
		if (BIT(m_dma_mux, 0 + i))
		{
			if (BIT(m_dma_mux, 4 + i))
				drq1 |= m_busdmarq[i];
			else
				drq0 |= m_busdmarq[i];
		}
	}

	m_maincpu->drq0_w(drq0);
	m_maincpu->drq1_w(drq1);
}

void tandy2k_state::dma_request(int line, int state)
{
	m_busdmarq[line] = state;

	update_drq();
}

void tandy2k_state::speaker_update()
{
	int level = !(m_spkrdata & m_outspkr);

	m_speaker->level_w(level);
}

uint8_t tandy2k_state::char_ram_r(offs_t offset)
{
	return m_char_ram[offset];
}

void tandy2k_state::char_ram_w(offs_t offset, uint8_t data)
{
	m_char_ram[offset] = data;
}

uint8_t tandy2k_state::videoram_r(offs_t offset)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	offs_t addr = (m_vram_base << 15) | (offset << 1);
	uint16_t data = program.read_word(addr);

	// character
	m_drb0->write(data & 0xff);

	// attributes
	m_drb1->write(data >> 8);

	return data & 0xff;
}

uint8_t tandy2k_state::enable_r()
{
	/*

	    bit     signal      description

	    0                   RS-232 ring indicator
	    1                   RS-232 carrier detect
	    2
	    3
	    4
	    5
	    6
	    7       _ACLOW

	*/

	uint8_t data = 0x80;

	data |= m_rs232->ri_r();
	data |= m_rs232->dcd_r() << 1;

	return data;
}

void tandy2k_state::enable_w(uint8_t data)
{
	/*

	    bit     signal      description

	    0       KBEN        keyboard enable
	    1       EXTCLK      external baud rate clock
	    2       SPKRGATE    enable periodic speaker output
	    3       SPKRDATA    direct output to speaker
	    4       RFRQGATE    enable refresh and baud rate clocks
	    5       FDCRESET*   reset 8272
	    6       TMRIN0      enable 80186 timer 0
	    7       TMRIN1      enable 80186 timer 1

	*/

	if (LOG) logerror("ENABLE %02x\n", data);

	// keyboard enable
	m_kb->power_w(BIT(data, 0));
	m_pc_keyboard->enable(BIT(data, 0));

	// external baud rate clock
	m_extclk = BIT(data, 1);

	// speaker gate
	m_pit->write_gate0(BIT(data, 2));

	// speaker data
	m_spkrdata = BIT(data, 3);
	speaker_update();

	// refresh and baud rate clocks
	m_pit->write_gate1(BIT(data, 4));
	m_pit->write_gate2(BIT(data, 4));

	// FDC reset
	m_fdc->reset_w(!BIT(data, 5));

	// timer 0 enable
	m_maincpu->tmrin0_w(BIT(data, 6));

	// timer 1 enable
	m_maincpu->tmrin1_w(BIT(data, 7));
}

void tandy2k_state::dma_mux_w(uint8_t data)
{
	/*

	    bit     description

	    0       DMA channel 0 enable
	    1       DMA channel 1 enable
	    2       DMA channel 2 enable
	    3       DMA channel 3 enable
	    4       DMA channel 0 select
	    5       DMA channel 1 select
	    6       DMA channel 2 select
	    7       DMA channel 3 select

	*/

	if (LOG) logerror("DMA MUX %02x\n", data);

	m_dma_mux = data;

	// check for DMA error
	int drq0 = 0;
	int drq1 = 0;

	for (int ch = 0; ch < 4; ch++)
	{
		if (BIT(data, ch)) { if (BIT(data, ch + 4)) drq1++; else drq0++; }
	}

	int dme = (drq0 > 2) || (drq1 > 2);

	m_pic1->ir6_w(dme);

	update_drq();
}

uint8_t tandy2k_state::kbint_clr_r()
{
	if (m_pb_sel == KBDINEN)
	{
		m_kb->busy_w(1);
		m_pic1->ir0_w(CLEAR_LINE);

		return m_pc_keyboard->read();
	}

	return 0xff;
}

uint8_t tandy2k_state::clkmouse_r(offs_t offset)
{
	uint8_t ret = 0;
	switch (offset)
	{
		case 0:
			if (!m_clkmouse_cnt)
				return 0;
			ret = m_clkmouse_cmd[--m_clkmouse_cnt];
			m_pic1->ir2_w(0);
			if (m_clkmouse_cnt > 0)
				m_mcu_delay->adjust(attotime::from_msec(1));
			break;
		case 2:
			ret = m_buttons->read();
			if (m_clkmouse_cnt)
				ret |= 1;
			break;
	}
	return ret;
}

void tandy2k_state::clkmouse_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
		case 0:
			m_pic1->ir2_w(0);
			if (m_clkmouse_cnt < 8)
				m_clkmouse_cmd[m_clkmouse_cnt++] = data;
			break;
		case 1:
			break;
		case 2:
			if (m_clkmouse_cnt < 8)
				m_clkmouse_cmd[m_clkmouse_cnt++] = data;
			switch (m_clkmouse_cmd[0])
			{
				case 0x01: //set time
					break;
				case 0x02: //read time
					break;
				case 0x08:
					if(m_clkmouse_cmd[1] > 0)
						m_clkmouse_irq |= MO_IRQ;
					else
						m_clkmouse_irq &= ~MO_IRQ;
					if(m_clkmouse_cmd[2] > 0)
						m_clkmouse_irq |= BT_IRQ;
					else
						m_clkmouse_irq &= ~BT_IRQ;
					break;
				case 0x20:
					if(m_clkmouse_cmd[1] > 0)
						m_mouse_timer->adjust(attotime::from_hz(40), 0, attotime::from_hz(40));
					else
						m_mouse_timer->adjust(attotime::never);
					break;
			}
			m_clkmouse_cnt = 0;
			break;
		case 3:
			m_pic1->ir2_w(0);
			m_clkmouse_cnt = 0;
			m_clkmouse_irq = 0;
			m_mouse_x = m_x_axis->read();
			m_mouse_y = m_y_axis->read();
			break;
	}
}

uint8_t tandy2k_state::fldtc_r()
{
	if (LOG) logerror("FLDTC\n");

	fldtc_w(0);

	return 0;
}

void tandy2k_state::fldtc_w(uint8_t data)
{
	m_fdc->tc_w(1);
	m_fdc->tc_w(false);
}

void tandy2k_state::addr_ctrl_w(uint8_t data)
{
	/*

	    bit     signal      description

	    8       A15         A15 of video access
	    9       A16         A16 of video access
	    10      A17         A17 of video access
	    11      A18         A18 of video access
	    12      A19         A19 of video access
	    13      CLKSP0      clock speed (0 = 22.4 MHz, 1 = 28 MHz)
	    14      CLKCNT      dots/char (0 = 10 [800x400], 1 = 8 [640x400])
	    15      VIDOUTS     selects the video source for display on monochrome monitor

	*/

	if (LOG) logerror("Address Control %02x\n", data);

	// video access
	m_vram_base = data & 0x1f;

	// video clock speed
	int clkspd = BIT(data, 5);
	int clkcnt = BIT(data, 6);

	if (m_clkspd != clkspd || m_clkcnt != clkcnt)
	{
		const XTAL busdotclk = 16_MHz_XTAL * 28 / (clkspd ? 16 : 20);
		const XTAL vidcclk = busdotclk / (clkcnt ? 8 : 10);

		m_vpac->set_character_width(clkcnt ? 8 : 10);
		m_vpac->set_unscaled_clock(vidcclk);

		m_vac->set_unscaled_clock(busdotclk);

		m_timer_vidldsh->adjust(attotime::from_hz(vidcclk), 0, attotime::from_hz(vidcclk));

		m_clkspd = clkspd;
		m_clkcnt = clkcnt;
	}

	// video source select
	m_vidouts = BIT(data, 7);
}

// Memory Maps

void tandy2k_state::vrambank_mem(address_map &map)
{
	map(0x00000, 0x17fff).ram().share("hires_ram");
	map(0x18000, 0x1ffff).noprw();
}

void tandy2k_state::tandy2k_mem(address_map &map)
{
	map.unmap_value_high();
	map(0xe0000, 0xe7fff).rw(m_vrambank, FUNC(address_map_bank_device::read16), FUNC(address_map_bank_device::write16));
	map(0xf8000, 0xfbfff).rw(FUNC(tandy2k_state::char_ram_r), FUNC(tandy2k_state::char_ram_w)).umask16(0x00ff);
	map(0xfc000, 0xfdfff).mirror(0x2000).rom().region(I80186_TAG, 0);
}

void tandy2k_state::tandy2k_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x00000).mirror(0x8).rw(FUNC(tandy2k_state::enable_r), FUNC(tandy2k_state::enable_w));
	map(0x00002, 0x00002).mirror(0x8).w(FUNC(tandy2k_state::dma_mux_w));
	map(0x00004, 0x00004).mirror(0x8).rw(FUNC(tandy2k_state::fldtc_r), FUNC(tandy2k_state::fldtc_w));
	map(0x00010, 0x00013).mirror(0xc).rw(m_uart, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0x00030, 0x00033).mirror(0xc).m(m_fdc, FUNC(i8272a_device::map)).umask16(0x00ff);
	map(0x00040, 0x00047).mirror(0x8).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0x00050, 0x00057).mirror(0x8).rw(m_i8255a, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
	map(0x00052, 0x00052).mirror(0x8).r(FUNC(tandy2k_state::kbint_clr_r));
	map(0x00060, 0x00063).mirror(0xc).rw(m_pic0, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x00070, 0x00073).mirror(0xc).rw(m_pic1, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x00080, 0x00080).mirror(0xe).rw(m_fdc, FUNC(i8272a_device::dma_r), FUNC(i8272a_device::dma_w));
	map(0x00100, 0x0017f).rw(m_vpac, FUNC(crt9007_device::read), FUNC(crt9007_device::write)).umask16(0x00ff);
	map(0x00100, 0x0017f).w(FUNC(tandy2k_state::addr_ctrl_w)).umask16(0xff00);
	map(0x00180, 0x00180).r(FUNC(tandy2k_state::hires_status_r)).umask16(0x00ff);
	map(0x00180, 0x0018f).mirror(0x10).w(m_colpal, FUNC(palette_device::write8)).umask16(0x00ff).share("colpal");
	map(0x001a0, 0x001a0).w(FUNC(tandy2k_state::hires_plane_w)).umask16(0x00ff);
	map(0x002fc, 0x002ff).rw(FUNC(tandy2k_state::clkmouse_r), FUNC(tandy2k_state::clkmouse_w));
}

void tandy2k_state::tandy2k_hd_io(address_map &map)
{
	tandy2k_io(map);
//  map(0x000e0, 0x000ff).w(FUNC(tandy2k_state::hdc_dack_w).umask16(0x00ff));
//  map(0x0026c, 0x0026c).rw(WD1010_TAG, FUNC(wd1010_device::hdc_reset_r), FUNC(wd1010_device::hdc_reset_w));
//  map(0x0026e, 0x0027e).rw(WD1010_TAG, FUNC(wd1010_device::wd1010_r), FUNC(wd1010_device::wd1010_w));
}

void tandy2k_state::vpac_mem(address_map &map)
{
	map(0x0000, 0x3fff).r(FUNC(tandy2k_state::videoram_r));
}

// Input Ports

static INPUT_PORTS_START( tandy2k )
	// defined in machine/tandy2kb.c
	PORT_START("MOUSEBTN")
	PORT_BIT( 0xff8f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CODE(MOUSECODE_BUTTON1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tandy2k_state::input_changed), 1)
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_CODE(MOUSECODE_BUTTON2) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tandy2k_state::input_changed), 1)
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_UNUSED )  /* this would be button three but AFAIK no tandy mouse ever had one */

	PORT_START("MOUSEX")
	PORT_BIT( 0xffff, 0x00, IPT_MOUSE_X ) PORT_SENSITIVITY(50) PORT_KEYDELTA(0) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tandy2k_state::input_changed), 0)

	PORT_START("MOUSEY")
	PORT_BIT( 0xffff, 0x00, IPT_MOUSE_Y ) PORT_SENSITIVITY(50) PORT_KEYDELTA(0) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tandy2k_state::input_changed), 0)
INPUT_PORTS_END

static INPUT_PORTS_START( tandy2kb_hle )
	PORT_INCLUDE(pc_keyboard)

	PORT_MODIFY("pc_keyboard_2")
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Cursor Up") PORT_CODE(KEYCODE_UP) /*                             29  A9 */
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Cursor Left") PORT_CODE(KEYCODE_LEFT) /*                             2B  AB */

	PORT_MODIFY("pc_keyboard_3")
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Caps") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE   /* Caps Lock                   3A  BA */

	PORT_MODIFY("pc_keyboard_4")
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("NumLock") PORT_CODE(KEYCODE_NUMLOCK) PORT_TOGGLE /* Num Lock                    45  C5 */
	/* Hold corresponds to Scroll Lock, but pauses the system when pressed - leaving unmapped by default to avoid conflicting with the UI Toggle key */
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Hold")     /*                            46  C6 */
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 7 \\") PORT_CODE(KEYCODE_7_PAD) /* Keypad 7                    47  C7 */
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 8 ~") PORT_CODE(KEYCODE_8_PAD) /* Keypad 8                    48  C8 */
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 9 (PgUp)") PORT_CODE(KEYCODE_9_PAD) /* Keypad 9  (PgUp)            49  C9 */
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Cursor Down") PORT_CODE(KEYCODE_DOWN) /*                             4A  CA */
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 4 |") PORT_CODE(KEYCODE_4_PAD) /* Keypad 4                    4B  CB */
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 6") PORT_CODE(KEYCODE_6_PAD) /* Keypad 6                    4D  CD */
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Cursor Right") PORT_CODE(KEYCODE_RIGHT) /*                             4E  CE */
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 1 (End)") PORT_CODE(KEYCODE_1_PAD) /* Keypad 1  (End)             4F  CF */

	PORT_MODIFY("pc_keyboard_5")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 2 `") PORT_CODE(KEYCODE_2_PAD) /* Keypad 2                    50  D0 */
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 3 (PgDn)") PORT_CODE(KEYCODE_3_PAD) /* Keypad 3  (PgDn)            51  D1 */
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 0") PORT_CODE(KEYCODE_0_PAD) /* Keypad 0                    52  D2 */
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP - (Del)") PORT_CODE(KEYCODE_MINUS_PAD) /* - Delete                    53  D3 */
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_STOP) /* Break                       54  D4 */
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+ Insert") PORT_CODE(KEYCODE_PLUS_PAD) /* + Insert                    55  D5 */
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_DEL_PAD) /* .                           56  D6 */
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER_PAD) /* Enter                       57  D7 */
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Home") PORT_CODE(KEYCODE_HOME) /* HOME                        58  D8 */
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F11") PORT_CODE(KEYCODE_F11) /* F11                         59  D9 */
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F12") PORT_CODE(KEYCODE_F12) /* F12                         5a  Da */
INPUT_PORTS_END

class tandy2kb_hle_device : public pc_keyboard_device
{
public:
	tandy2kb_hle_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);

protected:
	virtual ioport_constructor device_input_ports() const override ATTR_COLD;
};

DEFINE_DEVICE_TYPE(TANDY2K_HLE_KEYB, tandy2kb_hle_device, "tandy2kb_hle", "Tandy 2000 Keyboard HLE")

tandy2kb_hle_device::tandy2kb_hle_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	pc_keyboard_device(mconfig, TANDY2K_HLE_KEYB, tag, owner, clock)
{
	m_type = KEYBOARD_TYPE::PC;
}

ioport_constructor tandy2kb_hle_device::device_input_ports() const
{
	return INPUT_PORTS_NAME(tandy2kb_hle);
}

INPUT_CHANGED_MEMBER(tandy2k_state::input_changed)
{
	if (m_clkmouse_cnt || !m_clkmouse_irq)
		return;

	const bool is_button = bool(param);

	if ((m_clkmouse_irq & BT_IRQ) && is_button)
	{
		m_clkmouse_cnt = 1;
		m_clkmouse_cmd[0] = 'B';
	}
	else if ((m_clkmouse_irq & MO_IRQ))
	{
		uint16_t x = m_x_axis->read();
		uint16_t y = m_y_axis->read();
		uint16_t dx = x - m_mouse_x;
		uint16_t dy = y - m_mouse_y;
		m_mouse_x = x;
		m_mouse_y = y;
		m_clkmouse_cnt = 5;
		m_clkmouse_cmd[4] = 'M';
		m_clkmouse_cmd[3] = dx & 0xff;
		m_clkmouse_cmd[2] = dx >> 8;
		m_clkmouse_cmd[1] = dy & 0xff;
		m_clkmouse_cmd[0] = dy >> 8;
	}
	m_pic1->ir2_w(1);
}

// Video

uint32_t tandy2k_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const pen_t *cpen = m_colpal->pens();
	address_space &program = m_maincpu->space(AS_PROGRAM);

	for (int y = 0; y < 400; y++)
	{
		uint8_t cgra = y % 16;

		for (int sx = 0; sx < 80; sx++)
		{
			if (m_hires_en & 2)
			{
				uint8_t a = ((uint8_t *)m_hires_ram.target())[(y * 80) + sx];
				uint8_t b = ((uint8_t *)m_hires_ram.target())[(y * 80) + sx + 0x8000];
				uint8_t c = ((uint8_t *)m_hires_ram.target())[(y * 80) + sx + 0x8000 * 2];
				for (int x = 0; x < 8; x++)
				{
					int color = BIT(a, x) | (BIT(b, x) << 1) | (BIT(c, x) << 2);
					bitmap.pix(y, (sx * 8) + (7 - x)) = cpen[color];
				}
			}
			else
			{
				offs_t addr = m_ram->size() - 0x1400 + (((y / 16) * 80) + sx) * 2;
				uint16_t vidla = program.read_word(addr);
				uint8_t attr = vidla >> 8;
				uint8_t data = m_char_ram[((vidla & 0xff) << 4) | cgra];
				if(attr & 0x80)
					data = ~data;

				for (int x = 0; x < 8; x++)
				{
					int color = 4 | (BIT(attr, 6) << 1) | BIT(data, 7);
					bitmap.pix(y, (sx * 8) + x) = cpen[color];
					data <<= 1;
				}
			}
		}
	}

	return 0;
}

void tandy2k_state::vpac_vlt_w(int state)
{
	m_drb0->ren_w(state);
	m_drb0->clrcnt_w(state);

	m_drb1->ren_w(state);
	m_drb1->clrcnt_w(state);
}

void tandy2k_state::vpac_drb_w(int state)
{
	m_drb0->tog_w(state);
	m_drb1->tog_w(state);
}

void tandy2k_state::vpac_wben_w(int state)
{
	m_drb0->wen1_w(state);
	m_drb1->wen1_w(state);
}

void tandy2k_state::vpac_cblank_w(int state)
{
	m_cblank = state;
}

void tandy2k_state::vpac_slg_w(int state)
{
	m_slg = state;

	m_vac->slg_w(state);
}

void tandy2k_state::vpac_sld_w(int state)
{
	m_sld = state;

	m_vac->sld_w(state);
}

void tandy2k_state::hires_plane_w(uint8_t data)
{
	int bank = 3;
	if (((data & 1) + ((data >> 1) & 1) + ((data >> 2) & 1)) == 1)
		bank = (data & 1) ? 0 : (data & 2) ? 1 : (data & 4) ? 2 : 0;
	m_vrambank->set_bank(bank);
	m_hires_en = (data >> 4) & 3;
}

// bit 0 - 0 = hires board installed
// bit 1 - 0 = 1 plane, 1 = 3 planes
// bit 2-4 - board rev
uint8_t tandy2k_state::hires_status_r()
{
	return 2;
}

void tandy2k_state::vidla_w(uint8_t data)
{
	m_vidla = data;
}

void tandy2k_state::drb_attr_w(uint8_t data)
{
	/*

	    bit     description

	    0       BLC -> DBLC (delayed 2 CCLKs)
	    1       BKC -> DBKC (delayed 2 CCLKs)
	    2       CHABL
	    3       MS0
	    4       MS1
	    5       BLINK
	    6       INT
	    7       REVID

	*/

	m_blc = BIT(data, 0);
	m_bkc = BIT(data, 1);
	m_vac->chabl_w(BIT(data, 2));
	m_vac->ms0_w(BIT(data, 3));
	m_vac->ms1_w(BIT(data, 4));
	m_vac->blink_w(BIT(data, 5));
	m_vac->intin_w(BIT(data, 6));
	m_vac->revid_w(BIT(data, 7));
}

CRT9021_DRAW_CHARACTER_MEMBER( tandy2k_state::vac_draw_character )
{
	const pen_t *pen = m_colpal->pens();

	for (int i = 0; i < 8; i++)
	{
		int color = BIT(video, 7 - i);

		bitmap.pix(y, x++) = pen[color];
	}
}

TIMER_DEVICE_CALLBACK_MEMBER( tandy2k_state::vidldsh_tick )
{
	m_drb0->rclk_w(0);
	m_drb0->wclk_w(0);
	m_drb1->rclk_w(0);
	m_drb1->wclk_w(0);
	m_vac->ld_sh_w(0);

	// 1 busdotclk later
	m_vac->blc_w(BIT(m_dblc, 0));
	m_dblc >>= 1;
	m_dblc |= m_blc << 2;

	m_vac->bkc_w(BIT(m_dbkc, 0));
	m_dbkc >>= 1;
	m_dbkc |= m_bkc << 2;

	m_vac->retbl_w(BIT(m_dblank, 0));
	m_dblank >>= 1;
	m_dblank |= m_cblank << 2;

	if (!m_slg)
	{
		m_cgra >>= 1;
		m_cgra |= m_sld << 3;
	}

	uint8_t vidd = m_char_ram[(m_vidla << 4) | m_cgra];
	m_vac->write(vidd);

	m_drb0->rclk_w(1);
	m_drb0->wclk_w(1);
	m_drb1->rclk_w(1);
	m_drb1->wclk_w(1);
	m_vac->ld_sh_w(1);
}

// Intel 8251A Interface

void tandy2k_state::rxrdy_w(int state)
{
	m_rxrdy = state;
	m_pic0->ir2_w(m_rxrdy || m_txrdy);
}

void tandy2k_state::txrdy_w(int state)
{
	m_txrdy = state;
	m_pic0->ir2_w(m_rxrdy || m_txrdy);
}

// Intel 8253 Interface

void tandy2k_state::outspkr_w(int state)
{
	m_outspkr = state;
	speaker_update();
}

void tandy2k_state::intbrclk_w(int state)
{
	if (!m_extclk)
	{
		m_uart->write_txc(state);
		m_uart->write_rxc(state);
	}
}

void tandy2k_state::rfrqpulse_w(int state)
{
	// memory refresh counter up
}

// Intel 8255A Interface

void tandy2k_state::write_centronics_ack(int state)
{
	m_centronics_ack = state;
	m_i8255a->pc6_w(state);
}

void tandy2k_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

void tandy2k_state::write_centronics_perror(int state)
{
	m_centronics_perror = state;
}

void tandy2k_state::write_centronics_select(int state)
{
	m_centronics_select = state;
}

void tandy2k_state::write_centronics_fault(int state)
{
	m_centronics_fault = state;
}

uint8_t tandy2k_state::ppi_pb_r()
{
	/*

	    bit     signal          description

	    0       LPRIN0          auxiliary input 0
	    1       LPRIN1          auxiliary input 1
	    2       LPRIN2          auxiliary input 2
	    3       _LPRACK         acknowledge
	    4       _LPRFLT         fault
	    5       _LPRSEL         select
	    6       LPRPAEM         paper empty
	    7       LPRBSY          busy

	*/

	uint8_t data = 0;

	switch (m_pb_sel)
	{
	case LPINEN:
		// printer acknowledge
		data |= m_centronics_ack << 3;

		// printer fault
		data |= m_centronics_fault << 4;

		// printer select
		data |= m_centronics_select << 5;

		// paper empty
		data |= m_centronics_perror << 6;

		// printer busy
		data |= m_centronics_busy << 7;
		break;

	case KBDINEN:
		// keyboard data
		data = m_kbdin;
		break;

	case PORTINEN:
		// PCB revision
		data = 0x03;
		break;
	}

	return data;
}

void tandy2k_state::ppi_pc_w(uint8_t data)
{
	/*

	    bit     signal          description

	    0                       port A direction
	    1                       port B input select bit 0
	    2                       port B input select bit 1
	    3       LPRINT13        interrupt
	    4       STROBE IN
	    5       INBUFFULL
	    6       _LPRACK
	    7       _LPRDATSTB

	*/

	// input select
	m_pb_sel = (data >> 1) & 0x03;

	// interrupt
	m_pic1->ir3_w(BIT(data, 3));

	// printer strobe
	m_centronics->write_strobe(BIT(data, 7));
}


// Intel 8259 Interfaces

/*

    IR0     MEMINT00
    IR1     TMOINT01
    IR2     SERINT02
    IR3     BUSINT03
    IR4     FLDINT04
    IR5     BUSINT05
    IR6     HDCINT06
    IR7     BUSINT07

*/

/*

    IR0     KBDINT10
    IR1     VIDINT11
    IR2     RATINT12
    IR3     LPRINT13
    IR4     MCPINT14
    IR5     MEMINT15
    IR6     DMEINT16
    IR7     BUSINT17

*/

// Intel 8272 Interface

void tandy2k_state::fdc_drq_w(int state)
{
	dma_request(0, state);
}

void tandy2k_state::fdc_hdl_w(int state)
{
	m_floppy0->mon_w(!state);
	m_floppy1->mon_w(!state);
}

void tandy2k_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_TANDY_2000_FORMAT);
}

static void tandy2k_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

// Keyboard

void tandy2k_state::kbdclk_w(int state)
{
	if (!m_kbdclk && state)
	{
		m_kbdin >>= 1;
		m_kbdin |= m_kb->data_r() << 7;
	}

	m_kbdclk = state;
}

void tandy2k_state::kbddat_w(int state)
{
	if (!m_kbddat && state)
	{
		m_kb->busy_w(m_kbdclk);
		m_pic1->ir0_w(!m_kbdclk);
	}

	m_kbddat = state;
}

uint8_t tandy2k_state::irq_callback(offs_t offset)
{
	return (offset ? m_pic1 : m_pic0)->acknowledge();
}

// Machine Initialization

void tandy2k_state::machine_start()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	uint8_t *ram = m_ram->pointer();
	int ram_size = m_ram->size();

	program.install_ram(0x00000, ram_size - 1, ram);

	m_mouse_timer = timer_alloc(FUNC(tandy2k_state::update_mouse), this);
	m_mcu_delay = timer_alloc(FUNC(tandy2k_state::mcu_delay_cb), this);

	// register for state saving
	save_item(NAME(m_dma_mux));
	save_item(NAME(m_kbdclk));
	save_item(NAME(m_kbddat));
	save_item(NAME(m_kbdin));
	save_item(NAME(m_extclk));
	save_item(NAME(m_rxrdy));
	save_item(NAME(m_txrdy));
	save_item(NAME(m_pb_sel));
	save_item(NAME(m_vidouts));
	save_item(NAME(m_clkspd));
	save_item(NAME(m_clkcnt));
	save_item(NAME(m_outspkr));
	save_item(NAME(m_spkrdata));
	save_item(NAME(m_clkmouse_cmd));
	save_item(NAME(m_clkmouse_cnt));
	save_item(NAME(m_clkmouse_irq));
	save_item(NAME(m_mouse_x));
	save_item(NAME(m_mouse_y));
	save_item(NAME(m_hires_en));
}

void tandy2k_state::machine_reset()
{
	m_hires_en = 0;
	m_clkmouse_cnt = 0;
	m_clkmouse_irq = 0;
}

void tandy2k_state::device_reset_after_children()
{
	m_pc_keyboard->enable(0);
}

TIMER_CALLBACK_MEMBER(tandy2k_state::update_mouse)
{
	uint16_t x = m_x_axis->read();
	uint16_t y = m_y_axis->read();
	uint16_t dx = x - m_mouse_x;
	uint16_t dy = y - m_mouse_y;
	m_mouse_x = x;
	m_mouse_y = y;
	m_clkmouse_cnt = 5;
	m_clkmouse_cmd[4] = 'A';
	m_clkmouse_cmd[3] = dx & 0xff;
	m_clkmouse_cmd[2] = dx >> 8;
	m_clkmouse_cmd[1] = dy & 0xff;
	m_clkmouse_cmd[0] = dy >> 8;
	m_pic1->ir2_w(1);
}

TIMER_CALLBACK_MEMBER(tandy2k_state::mcu_delay_cb)
{
	m_pic1->ir2_w(1);
}

rgb_t tandy2k_state::IRGB(uint32_t raw)
{
	uint8_t i = (raw >> 3) & 1;
	return rgb_t(pal2bit(((raw & 4) >> 1) | i), pal2bit((raw & 2) | i), pal2bit(((raw & 1) << 1) | i));
}

// Machine Driver

void tandy2k_state::tandy2k(machine_config &config)
{
	// basic machine hardware
	I80186(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &tandy2k_state::tandy2k_mem);
	m_maincpu->set_addrmap(AS_IO, &tandy2k_state::tandy2k_io);
	m_maincpu->read_slave_ack_callback().set(FUNC(tandy2k_state::irq_callback));

	// video hardware
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // not accurate
	screen.set_size(640, 400);
	screen.set_visarea(0, 640-1, 0, 400-1);
	//screen.set_screen_update(CRT9021B_TAG, FUNC(crt9021_device::screen_update));
	screen.set_screen_update(FUNC(tandy2k_state::screen_update));

	PALETTE(config, m_colpal).set_format(1, &tandy2k_state::IRGB, 8);

	crt9007_device &vpac(CRT9007(config, CRT9007_TAG, 16_MHz_XTAL * 28 / 20 / 8));
	vpac.set_addrmap(0, &tandy2k_state::vpac_mem);
	vpac.set_character_width(8);
	vpac.int_callback().set(I8259A_1_TAG, FUNC(pic8259_device::ir1_w));
	vpac.vs_callback().set(CRT9021B_TAG, FUNC(crt9021_device::vsync_w));
	vpac.vlt_callback().set(FUNC(tandy2k_state::vpac_vlt_w));
	vpac.curs_callback().set(CRT9021B_TAG, FUNC(crt9021_device::cursor_w));
	vpac.drb_callback().set(FUNC(tandy2k_state::vpac_drb_w));
	vpac.wben_callback().set(FUNC(tandy2k_state::vpac_wben_w));
	vpac.cblank_callback().set(FUNC(tandy2k_state::vpac_cblank_w));
	vpac.slg_callback().set(FUNC(tandy2k_state::vpac_slg_w));
	vpac.sld_callback().set(FUNC(tandy2k_state::vpac_sld_w));
	vpac.set_screen(SCREEN_TAG);

	CRT9212(config, m_drb0, 0);
	m_drb0->set_wen2(1);
	m_drb0->dout().set(FUNC(tandy2k_state::vidla_w));

	CRT9212(config, m_drb1, 0);
	m_drb1->set_wen2(1);
	m_drb1->dout().set(FUNC(tandy2k_state::drb_attr_w));

	CRT9021(config, m_vac, 16_MHz_XTAL * 28 / 20);
	m_vac->set_screen(SCREEN_TAG);

	ADDRESS_MAP_BANK(config, m_vrambank).set_map(&tandy2k_state::vrambank_mem).set_options(ENDIANNESS_LITTLE, 16, 17, 0x8000);

	TIMER(config, "vidldsh").configure_generic(FUNC(tandy2k_state::vidldsh_tick));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	// devices
	I8255A(config, m_i8255a);
	m_i8255a->out_pa_callback().set("cent_data_out", FUNC(output_latch_device::write));
	m_i8255a->in_pb_callback().set(FUNC(tandy2k_state::ppi_pb_r));
	m_i8255a->out_pc_callback().set(FUNC(tandy2k_state::ppi_pc_w));

	I8251(config, m_uart, 0);
	m_uart->txd_handler().set(m_rs232, FUNC(rs232_port_device::write_txd));
	m_uart->dtr_handler().set(m_rs232, FUNC(rs232_port_device::write_dtr));
	m_uart->rts_handler().set(m_rs232, FUNC(rs232_port_device::write_rts));
	m_uart->rxrdy_handler().set(FUNC(tandy2k_state::rxrdy_w));
	m_uart->txrdy_handler().set(FUNC(tandy2k_state::txrdy_w));

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set(m_uart, FUNC(i8251_device::write_rxd));
	m_rs232->dsr_handler().set(m_uart, FUNC(i8251_device::write_dsr));
	// TODO pin 15 external transmit clock
	// TODO pin 17 external receiver clock

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(16_MHz_XTAL / 16);
	m_pit->out_handler<0>().set(FUNC(tandy2k_state::outspkr_w));
	m_pit->set_clk<1>(16_MHz_XTAL / 8);
	m_pit->out_handler<1>().set(FUNC(tandy2k_state::intbrclk_w));
	//m_pit->set_clk<2>(16_MHz_XTAL / 8);
	//m_pit->out_handler<2>().set(FUNC(tandy2k_state::rfrqpulse_w));

	PIC8259(config, m_pic0, 0);
	m_pic0->out_int_callback().set(m_maincpu, FUNC(i80186_cpu_device::int0_w));

	PIC8259(config, m_pic1, 0);
	m_pic1->out_int_callback().set(m_maincpu, FUNC(i80186_cpu_device::int1_w));

	I8272A(config, m_fdc, 16_MHz_XTAL / 4, true);
	m_fdc->set_select_lines_connected(true);
	m_fdc->intrq_wr_callback().set(m_pic0, FUNC(pic8259_device::ir4_w));
	m_fdc->drq_wr_callback().set(FUNC(tandy2k_state::fdc_drq_w));
	m_fdc->hdl_wr_callback().set(FUNC(tandy2k_state::fdc_hdl_w));
	FLOPPY_CONNECTOR(config, I8272A_TAG ":0", tandy2k_floppies, "525qd", tandy2k_state::floppy_formats);
	FLOPPY_CONNECTOR(config, I8272A_TAG ":1", tandy2k_floppies, "525qd", tandy2k_state::floppy_formats);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set(FUNC(tandy2k_state::write_centronics_ack));
	m_centronics->busy_handler().set(FUNC(tandy2k_state::write_centronics_busy));
	m_centronics->perror_handler().set(FUNC(tandy2k_state::write_centronics_perror));
	m_centronics->select_handler().set(FUNC(tandy2k_state::write_centronics_select));
	m_centronics->fault_handler().set(FUNC(tandy2k_state::write_centronics_fault));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	TANDY2K_KEYBOARD(config, m_kb, 0);
	m_kb->clock_wr_callback().set(FUNC(tandy2k_state::kbdclk_w));
	m_kb->data_wr_callback().set(FUNC(tandy2k_state::kbddat_w));

	// temporary until the tandy keyboard has a rom dump
	TANDY2K_HLE_KEYB(config, m_pc_keyboard, 0).keypress().set(I8259A_1_TAG, FUNC(pic8259_device::ir0_w));

	// software lists
	SOFTWARE_LIST(config, "flop_list").set_original("tandy2k");

	// internal ram
	RAM(config, RAM_TAG).set_default_size("128K").set_extra_options("256K,384K,512K,640K,768K,896K");
}

void tandy2k_state::tandy2k_hd(machine_config &config)
{
	tandy2k(config);
	// basic machine hardware
	m_maincpu->set_addrmap(AS_IO, &tandy2k_state::tandy2k_hd_io);

	// Tandon TM502 hard disk
	HARDDISK(config, "harddisk0", 0);
	//MCFG_WD1010_ADD(WD1010_TAG, wd1010_intf)
	//MCFG_WD1100_11_ADD(WD1100_11_TAG, wd1100_11_intf)
}

// ROMs

ROM_START( tandy2k )
	ROM_REGION( 0x2000, I80186_TAG, 0 )
	ROM_LOAD16_BYTE( "484a00.u48", 0x0000, 0x1000, CRC(a5ee3e90) SHA1(4b1f404a4337c67065dd272d62ff88dcdee5e34b) )
	ROM_LOAD16_BYTE( "474600.u47", 0x0001, 0x1000, CRC(345701c5) SHA1(a775cbfa110b7a88f32834aaa2a9b868cbeed25b) )

	ROM_REGION( 0x100, "plds", 0 )
	ROM_LOAD( "82s153.u62", 0x000, 0x100, NO_DUMP ) // interrupt/DMA
	ROM_LOAD( "82s153.u68", 0x000, 0x100, NO_DUMP ) // video
	ROM_LOAD( "82s153.u95", 0x000, 0x100, NO_DUMP ) // memory timing
	ROM_LOAD( "pal10l8.u82", 0x000, 0x100, NO_DUMP ) // video
	ROM_LOAD( "pal16l8a.u102", 0x000, 0x100, NO_DUMP ) // bus interface
	ROM_LOAD( "pal16l8a.u103", 0x000, 0x100, NO_DUMP ) // bus interface
	ROM_LOAD( "pal20l8.u103", 0x000, 0x100, NO_DUMP ) // bus interface, alternate
	ROM_LOAD( "pal16r6a.u16", 0x000, 0x100, NO_DUMP ) // HDC
ROM_END

#define rom_tandy2khd rom_tandy2k

// System Drivers

//    YEAR  NAME       PARENT   COMPAT  MACHINE     INPUT    CLASS          INIT        COMPANY              FULLNAME        FLAGS
COMP( 1983, tandy2k,   0,       0,      tandy2k,    tandy2k, tandy2k_state, empty_init, "Tandy Radio Shack", "Tandy 2000",   MACHINE_NOT_WORKING )
COMP( 1983, tandy2khd, tandy2k, 0,      tandy2k_hd, tandy2k, tandy2k_state, empty_init, "Tandy Radio Shack", "Tandy 2000HD", MACHINE_NOT_WORKING )



tasc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap, Sandro Ronco
/*******************************************************************************

Tasc ChessSystem

Commonly known as Tasc R30, it's basically a dedicated ChessMachine.
The King chess engines are also compatible with Tasc's The ChessMachine software
on PC, however the prototype Gideon 3.1(internally: 2.1, Rebel 2.01) is not.

The King 2.23 version was not sold to consumers. It has an opening book meant
for chesscomputer competitions.
For more information, see: http://chesseval.com/ChessEvalJournal/R30v223.htm

Gideon only uses 128KB program RAM, no matter the RAM configuration.

R30 hardware notes:
- ARM6 CPU(P60ARM/CG) @ 30MHz
- 256KB system ROM (2*27C010)
- 512KB program RAM (4*MT5C1008), 128KB permanent RAM (KM681000ALP-7L)
- Toshiba LCD drivers (3*T7778A, T7900, T6963C), TC5565AFL-15
- SB20 or SB30 "SmartBoard" chessboard with piece recognition

R40 hardware notes:
- ARM6 CPU(VY86C061PSTC) @ 40MHz
- +512KB extra RAM piggybacked
- rest same as R30

Documentation for the Toshiba chips is hard to find, but similar chips exist:
T7778 is equivalent to T6A39, T7900 is equivalent to T6A40.

EPROMs are interchangeable between R30 and R40.

references:
- https://www.schach-computer.info/wiki/index.php?title=Tasc_R30
- https://www.schach-computer.info/wiki/index.php?title=Tasc_R40
- https://www.schach-computer.info/wiki/index.php?title=Tasc_SmartBoard
- https://www.miclangschach.de/index.php?n=Main.TascR30

notes:
- holding LEFT+RIGHT on boot load the QC TestMode
- holding UP+DOWN on boot load the TestMode

TODO:
- bootrom disable timer shouldn't be needed, real ARM has already fetched the next opcode
- more accurate dynamic cpu clock divider (same problem as in saitek/risc2500.cpp),
  sound pitch is correct now though
- does the R40 version have the same clock divider value?

BTANB:
- R40 calls itself "R30" on the system information screen (there is a photo of
  an R40 that does say "R40", but it appears to be a modified ROM)

*******************************************************************************/

#include "emu.h"

#include "cpu/arm/arm.h"
#include "machine/nvram.h"
#include "machine/ram.h"
#include "machine/smartboard.h"
#include "sound/dac.h"
#include "video/t6963c.h"

#include "speaker.h"

// internal artwork
#include "tascr30.lh"


namespace {

class tasc_state : public driver_device
{
public:
	tasc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_boot_view(*this, "boot_view"),
		m_rom(*this, "maincpu"),
		m_ram(*this, "ram"),
		m_nvram(*this, "nvram", 0x20000, ENDIANNESS_LITTLE),
		m_lcd(*this, "lcd"),
		m_smartboard(*this, "smartboard"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0U),
		m_out_leds(*this, "pled%u", 0U)
	{ }

	void tasc(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(change_cpu_freq);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices/pointers
	required_device<arm_cpu_device> m_maincpu;
	memory_view m_boot_view;
	required_region_ptr<u32> m_rom;
	required_device<ram_device> m_ram;
	memory_share_creator<u8> m_nvram;
	required_device<lm24014h_device> m_lcd;
	required_device<tasc_sb30_device> m_smartboard;
	required_device<dac_2bit_ones_complement_device> m_dac;
	required_ioport_array<4> m_inputs;
	output_finder<2> m_out_leds;

	emu_timer *m_boot_timer;

	u32 m_control = 0;
	u32 m_prev_pc = 0;
	u64 m_prev_cycle = 0;

	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	u32 input_r();
	u32 rom_r(offs_t offset);
	void control_w(offs_t offset, u32 data, u32 mem_mask = ~0);

	u8 nvram_r(offs_t offset) { return m_nvram[offset]; }
	void nvram_w(offs_t offset, u8 data) { m_nvram[offset] = data; }

	TIMER_CALLBACK_MEMBER(disable_bootrom) { m_boot_view.select(1); }
};



/*******************************************************************************
    Initialization
*******************************************************************************/

void tasc_state::machine_start()
{
	m_out_leds.resolve();

	m_boot_timer = timer_alloc(FUNC(tasc_state::disable_bootrom), this);
	m_boot_view[1].install_ram(0, m_ram->size() - 1, m_ram->pointer());

	// register for savestates
	save_item(NAME(m_control));
	save_item(NAME(m_prev_pc));
	save_item(NAME(m_prev_cycle));
}

void tasc_state::machine_reset()
{
	m_boot_view.select(0);
	m_boot_timer->adjust(attotime::never);

	m_prev_pc = m_maincpu->pc();
	m_prev_cycle = m_maincpu->total_cycles();
}

INPUT_CHANGED_MEMBER(tasc_state::change_cpu_freq)
{
	// R30 is 30MHz, R40 is 40MHz
	m_maincpu->set_unscaled_clock((newval & 1) ? 40_MHz_XTAL : 30_MHz_XTAL);
}



/*******************************************************************************
    I/O
*******************************************************************************/

u32 tasc_state::input_r()
{
	if (!machine().side_effects_disabled())
	{
		// disconnect bootrom from the bus after next opcode
		if (m_boot_timer->remaining().is_never())
			m_boot_timer->adjust(m_maincpu->cycles_to_attotime(10));

		m_maincpu->set_input_line(ARM_FIRQ_LINE, CLEAR_LINE);
	}

	// read chessboard
	u32 data = m_smartboard->data_r();

	// read keypad
	for (int i = 0; i < 4; i++)
	{
		if (BIT(m_control, i))
			data |= (m_inputs[i]->read() << 24);
	}

	return data;
}

void tasc_state::control_w(offs_t offset, u32 data, u32 mem_mask)
{
	if (ACCESSING_BITS_24_31)
	{
		if (BIT(~m_control & data, 27))
			m_lcd->write(BIT(data, 26), data & 0xff);

		m_smartboard->data0_w(BIT(data, 30));
		m_smartboard->data1_w(BIT(data, 31));
	}
	else
	{
		m_out_leds[0] = BIT(data, 0);
		m_out_leds[1] = BIT(data, 1);
		m_dac->write((data >> 2) & 3);
	}

	COMBINE_DATA(&m_control);
}

u32 tasc_state::rom_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
	{
		// handle dynamic cpu clock divider when accessing rom
		s64 diff = m_maincpu->total_cycles() - m_prev_cycle;
		u32 pc = m_maincpu->pc();

		if (diff > 0)
		{
			static constexpr int arm_branch_cycles = 3;
			static constexpr int arm_max_cycles = 17; // datablock transfer
			static constexpr int divider = -7 + 1;

			// this takes care of almost all cases, otherwise, total cycles taken can't be determined
			if (diff <= arm_branch_cycles || (diff <= arm_max_cycles && (pc - m_prev_pc) == 4 && (pc & ~0x02000000) == (offset * 4)))
				m_maincpu->adjust_icount(divider * (int)diff);
			else
				m_maincpu->adjust_icount(divider);
		}

		m_prev_cycle = m_maincpu->total_cycles();
		m_prev_pc = pc;
	}

	return m_rom[offset];
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void tasc_state::main_map(address_map &map)
{
	map(0x00000000, 0x007fffff).view(m_boot_view);
	m_boot_view[0](0x00000000, 0x0003ffff).r(FUNC(tasc_state::rom_r));

	map(0x01000000, 0x01000003).rw(FUNC(tasc_state::input_r), FUNC(tasc_state::control_w));
	map(0x02000000, 0x0203ffff).r(FUNC(tasc_state::rom_r));
	map(0x03000000, 0x0307ffff).rw(FUNC(tasc_state::nvram_r), FUNC(tasc_state::nvram_w)).umask32(0x000000ff);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( tasc )
	PORT_START("IN.0")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("Play")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Left")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.1")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Back")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Right")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.2")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Menu")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_UP) PORT_NAME("Up")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Left Clock")

	PORT_START("IN.3")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Down")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Right Clock")

	PORT_START("CPU")
	PORT_CONFNAME( 0x01, 0x00, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tasc_state::change_cpu_freq), 0)
	PORT_CONFSETTING(    0x00, "30MHz (R30)" )
	PORT_CONFSETTING(    0x01, "40MHz (R40)" )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void tasc_state::tasc(machine_config &config)
{
	// basic machine hardware
	ARM(config, m_maincpu, 30_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &tasc_state::main_map);
	m_maincpu->set_copro_type(arm_cpu_device::copro_type::VL86C020);

	const attotime irq_period = attotime::from_hz(32.768_kHz_XTAL / 128); // 256Hz
	m_maincpu->set_periodic_int(FUNC(tasc_state::irq1_line_assert), irq_period);

	RAM(config, m_ram).set_extra_options("512K, 1M, 2M, 4M, 8M"); // see driver notes
	m_ram->set_default_size("512K");
	m_ram->set_default_value(0);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	TASC_SB30(config, m_smartboard);
	subdevice<sensorboard_device>("smartboard:board")->set_nvram_enable(true);

	// video hardware
	LM24014H(config, m_lcd, 0);
	m_lcd->set_fs(1); // font size 6x8

	config.set_default_layout(layout_tascr30);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_2BIT_ONES_COMPLEMENT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.125);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( tascr30 ) // system version V1.01 (17-Mar-95), program version 2.50 (26-Feb-95)
	ROM_REGION32_LE( 0x40000, "maincpu", 0 )
	ROM_LOAD32_WORD("r30_lo_1.01_king_2.5", 0x00000, 0x20000, CRC(9711c158) SHA1(87c60d2097cb437482df11916543f6ef7f18b0d3) )
	ROM_LOAD32_WORD_SWAP("r30_hi_1.01_king_2.5", 0x00002, 0x20000, CRC(df913abf) SHA1(1bc2ea4b6514bf9fec18f52c264f1440ba7c8c01) )
ROM_END

ROM_START( tascr30a ) // system version V0.31 (3-May-93), program version 2.20 (23-Apr-93)
	ROM_REGION32_LE( 0x40000, "maincpu", 0 )
	ROM_LOAD32_WORD("0.31_l", 0x00000, 0x20000, CRC(d30f81fe) SHA1(81957c7266bedec66b2c14b97008c4261bd67828) )
	ROM_LOAD32_WORD_SWAP("0.31_h", 0x00002, 0x20000, CRC(aeac3b46) SHA1(a757e0086636dfd3bf78e61cee46c7d92b39d3b9) )
ROM_END

ROM_START( tascr30b ) // system version V0.31 (3-May-93), program version 2.23 (16-May-93)
	ROM_REGION32_LE( 0x40000, "maincpu", 0 )
	ROM_LOAD32_WORD("r30_v2.23_lo", 0x00000, 0x20000, CRC(37251b1a) SHA1(4be768e861002b20ba59a18329f488dba0a0c9bf) )
	ROM_LOAD32_WORD_SWAP("r30_v2.23_hi", 0x00002, 0x20000, CRC(e546be93) SHA1(943ae65cf97ec4389b9730c6006e805935333072) )
ROM_END

ROM_START( tascr30g ) // system version V0.31 (3-May-93), program version 2.1 (3-Feb-93)
	ROM_REGION32_LE( 0x40000, "maincpu", 0 )
	ROM_LOAD32_WORD("r30_gideon_l", 0x00000, 0x20000, CRC(7041d051) SHA1(266843f375a8621320fc2cd1300775fb7a505c6e) )
	ROM_LOAD32_WORD_SWAP("r30_gideon_h", 0x00002, 0x20000, CRC(7345ee08) SHA1(9cad608bd32d804468b23196151be0a5f8cee214) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT   COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY, FULLNAME, FLAGS
SYST( 1995, tascr30,  0,       0,      tasc,    tasc,  tasc_state, empty_init, "Tasc", "ChessSystem R30 (The King 2.50)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_TIMING )
SYST( 1993, tascr30a, tascr30, 0,      tasc,    tasc,  tasc_state, empty_init, "Tasc", "ChessSystem R30 (The King 2.20)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_TIMING )
SYST( 1993, tascr30b, tascr30, 0,      tasc,    tasc,  tasc_state, empty_init, "Tasc", "ChessSystem R30 (The King 2.23, TM version)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_TIMING ) // competed in several chesscomputer tournaments
SYST( 1993, tascr30g, tascr30, 0,      tasc,    tasc,  tasc_state, empty_init, "Tasc", "ChessSystem R30 (Gideon 3.1, prototype)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_TIMING ) // made in 1993, later released in 2012



tatrain.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Saitek Kasparov Turbo Advanced Trainer

The chess engine is by Frans Morsch, it is the same as the one in GK 2000.

Hardware notes:

H8/323 version (1992):
- PCB label: ST14B-PE-003, PN/N 512090-00311, REV.1
- Hitachi H8/323 MCU, 20MHz XTAL
- piezo, 24 LEDs, button sensors chessboard

H8/3212 version (1997):
- PCB label: ST14B-PE 003, PN/N 512090-00312, REV.2
- Hitachi H8/3212 MCU, 10MHz XTAL
- rest is same as 1992 version

H8/323 A14 MCU is used in:
- Saitek Turbo Advanced Trainer (1992 version)
- Saitek Champion Advanced Trainer
- Saitek Virtuoso
- Saitek Mephisto Champion (Mephisto brand Champion Advanced Trainer)
- Hegener + Glaser Schach-Trainer (H+G brand Turbo Advanced Trainer)

Champion Advanced Trainer and Mephisto Champion are 14MHz instead of 20MHz.

H8/3212 V02 MCU is used in:
- Saitek Turbo Advanced Trainer (1997 version)
- Saitek Capella

Turbo Advanced Trainer looks similar to Saitek Team-Mate. Virtuoso and Capella
are in the same housing as SciSys Astral, they lack the coach LED and button.
Note that the H8/323 has an internal /2 clock divider, so even though the newer
version has a lower frequency XTAL, it runs at the same speed.

TODO:
- it does a cold boot at every reset, so nvram won't work properly unless MAME
  adds some kind of auxillary autosave state feature at power-off

*******************************************************************************/

#include "emu.h"

#include "cpu/h8/h83217.h"
#include "cpu/h8/h8325.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "saitek_tatrain.lh"


namespace {

class tatrain_state : public driver_device
{
public:
	tatrain_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "led_pwm"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	template <typename T> void cpu_config(T &maincpu);
	void shared(machine_config &config);
	void tatrain(machine_config &config);
	void tatraina(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(go_button);
	DECLARE_INPUT_CHANGED_MEMBER(tatraina_change_cpu_freq);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<h8_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<3> m_inputs;

	u16 m_inp_mux = 0;

	// I/O handlers
	template<int N> void leds_w(u8 data);
	u8 p4_r();
	void p5_w(u8 data);
	void p6_w(u8 data);
	void p7_w(u8 data);
};

void tatrain_state::machine_start()
{
	save_item(NAME(m_inp_mux));
}

INPUT_CHANGED_MEMBER(tatrain_state::tatraina_change_cpu_freq)
{
	// H8/323 16MHz and 24MHz versions don't exist, but the software supports it
	static const XTAL freq[4] = { 20_MHz_XTAL, 16_MHz_XTAL, 14_MHz_XTAL, 24_MHz_XTAL };
	m_maincpu->set_unscaled_clock(freq[newval & 3]);
}



/*******************************************************************************
    I/O
*******************************************************************************/

INPUT_CHANGED_MEMBER(tatrain_state::go_button)
{
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, newval ? ASSERT_LINE : CLEAR_LINE);
}

template <int N>
void tatrain_state::leds_w(u8 data)
{
	// P1x, P2x, P3x: leds (direct)
	m_display->write_row(N, ~data);
}

u8 tatrain_state::p4_r()
{
	// P40-P47: multiplexed inputs
	u8 data = 0;

	// read buttons
	for (int i = 0; i < 2; i++)
		if (BIT(m_inp_mux, i + 8))
			data |= m_inputs[i]->read();

	// read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_rank(i ^ 7);

	return ~data;
}

void tatrain_state::p5_w(u8 data)
{
	// P52,P53: input mux (buttons)
	m_inp_mux = (m_inp_mux & 0xff) | (~data << 6 & 0x300);
}

void tatrain_state::p6_w(u8 data)
{
	// P60: speaker out
	m_dac->write(data & 1);

	// P61-P63: N/C (appears to be compatible with Turbo 16K LCDs)
}

void tatrain_state::p7_w(u8 data)
{
	// P70-P77: input mux (chessboard)
	m_inp_mux = (m_inp_mux & 0x300) | (data ^ 0xff);
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( tatrain )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Non Auto")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("Play")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Set Up")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Stop")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Info")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Coach")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Color")

	PORT_START("IN.2")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tatrain_state::go_button), 0) PORT_NAME("Go / Stop")
	PORT_BIT(0xef, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("FREQ")
	PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( tatraina )
	PORT_INCLUDE( tatrain )

	PORT_MODIFY("FREQ")
	PORT_CONFNAME( 0x03, 0x00, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tatrain_state::tatraina_change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x02, "14Mhz (Champion Advanced Trainer)" )
	PORT_CONFSETTING(    0x01, "16MHz (unofficial)" )
	PORT_CONFSETTING(    0x00, "20Mhz (Turbo Advanced Trainer, Virtuoso)" )
	PORT_CONFSETTING(    0x03, "24MHz (unofficial)" )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

template <typename T>
void tatrain_state::cpu_config(T &maincpu)
{
	maincpu.nvram_enable_backup(true);
	maincpu.standby_cb().set(maincpu, FUNC(T::nvram_set_battery));
	maincpu.standby_cb().append([this](int state) { if (state) m_display->clear(); });
	maincpu.write_port1().set(FUNC(tatrain_state::leds_w<0>));
	maincpu.write_port2().set(FUNC(tatrain_state::leds_w<1>));
	maincpu.write_port3().set(FUNC(tatrain_state::leds_w<2>));
	maincpu.read_port4().set(FUNC(tatrain_state::p4_r));
	maincpu.read_port5().set_ioport("FREQ").invert();
	maincpu.write_port5().set(FUNC(tatrain_state::p5_w));
	maincpu.read_port6().set_ioport("IN.2").invert();
	maincpu.write_port6().set(FUNC(tatrain_state::p6_w));
	maincpu.write_port7().set(FUNC(tatrain_state::p7_w));
}

void tatrain_state::shared(machine_config &config)
{
	// basic machine hardware
	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	//m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(3, 8);
	config.set_default_layout(layout_saitek_tatrain);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void tatrain_state::tatrain(machine_config &config)
{
	H83212(config, m_maincpu, 10_MHz_XTAL);
	cpu_config<h83212_device>(downcast<h83212_device &>(*m_maincpu));

	shared(config);
}

void tatrain_state::tatraina(machine_config &config)
{
	H8323(config, m_maincpu, 20_MHz_XTAL);
	cpu_config<h8323_device>(downcast<h8323_device &>(*m_maincpu));

	shared(config);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( tatrain )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("97_saitek_86158430421_hd6433212v02p.u1", 0x0000, 0x4000, CRC(73f9abb6) SHA1(3a4c3a8ad668327fe9f61c4b054e31ec6af9c48d) )
ROM_END

ROM_START( tatraina )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("92_saitek_86069221x14_3238a14p.u1", 0x0000, 0x4000, CRC(ae2d536c) SHA1(6397c38d21a4291a992bf317e695aadbd4510260) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT     CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1997, tatrain,  0,       0,      tatrain,  tatrain,  tatrain_state, empty_init, "Saitek", "Kasparov Turbo Advanced Trainer (H8/3212 version)", MACHINE_SUPPORTS_SAVE )
SYST( 1992, tatraina, tatrain, 0,      tatraina, tatraina, tatrain_state, empty_init, "Saitek", "Kasparov Turbo Advanced Trainer (H8/323 version)", MACHINE_SUPPORTS_SAVE )



tavernie.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Robbbert
/*********************************************************************************

Tavernier CPU09 and IVG09 (Realisez votre ordinateur individuel)

2013-12-08 Skeleton driver.

This system was described in a French magazine "Le Haut-Parleur".

CPU09 includes 6809, 6821, 6840, 6850, cassette, rs232
IVG09 includes 6845, another 6821, beeper
IFD09 includes WD1795

ToDo:
- Graphics
- Character rom is not dumped
- Graphics rom is not dumped
        (note 2020-05-29: added what are thought to be the correct roms, but the proper
                          operation is uncertain).
- 3x 7611 proms not dumped
- Test FDC
- Need software (there are floppy images, but they are not yet in a supported format)


List of commands (must be in UPPERCASE):
A - Memory transfer
B - breakpoint management
C - call a user subroutine
D - Dump memory (^X to break)
G - coding of indexed addresses
I - memory initialisation
L - Load cassette (Use L 0), where the number is an offset from the original address.
M - examine/modify memory
N - terminal adaptation (??)
O - calculation of displacements
P - Save cassette
Q - printer activation
R - Display/Alter Registers
S - automatic inhibition of single-step
T - single-step operation
U - memory page change
V - Verify cassette
W - definition of a window
X - DOS loading from TAVBUG09
Y - launch of DOS since TAVBUG09
Z - more scan lines per row (cursor is bigger)
/ - fast memory exam
  - multiple single-steps


**********************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/m6809/m6809.h"
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "machine/6821pia.h"
#include "machine/6840ptm.h"
#include "machine/6850acia.h"
#include "machine/timer.h"
#include "machine/keyboard.h"
#include "machine/wd_fdc.h"
#include "sound/beep.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

namespace {

class cpu09_state : public driver_device
{
public:
	cpu09_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cass(*this, "cassette")
		, m_pia0(*this, "pia0")
		, m_acia(*this, "acia")
		, m_ptm(*this, "ptm")
	{ }

	void cpu09(machine_config &config);

protected:
	u8 pa_r();
	void pa_w(u8 data);
	void pb_w(u8 data);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);
	void cpu09_mem(address_map &map) ATTR_COLD;
	u8 m_pa = 0U;
	bool m_cassold = false;
	required_device<cpu_device> m_maincpu;
	required_device<cassette_image_device> m_cass;
	required_device<pia6821_device> m_pia0;
	required_device<acia6850_device> m_acia;
	required_device<ptm6840_device> m_ptm;

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
};

class ivg09_state : public cpu09_state
{
public:
	ivg09_state(const machine_config &mconfig, device_type type, const char *tag)
		: cpu09_state(mconfig, type, tag)
		, m_pia1(*this, "pia1")
		, m_crtc(*this, "crtc")
		, m_fdc(*this, "fdc")
		, m_floppy0(*this, "fdc:0")
		, m_beep(*this, "beeper")
		, m_palette(*this, "palette")
		, m_p_chargen(*this, "chargen")
	{ }

	void ivg09(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	void ivg09_palette(palette_device &palette) const;
	void ivg09_mem(address_map &map) ATTR_COLD;
	void pa_ivg_w(u8 data);
	u8 pb_ivg_r();
	void vram_w(offs_t offset, u8 data);
	u8 vram_r(offs_t offset);
	void kbd_put(u8 data);
	void ds_w(u8 data);
	MC6845_UPDATE_ROW(crtc_update_row);
	u8 m_term_data = 0U;
	u8 m_ivg_pa = 0U;
	u8 m_flashcnt = 0U;
	std::unique_ptr<u16[]> m_vram; // 12x 4044
	required_device<pia6821_device> m_pia1;
	required_device<mc6845_device> m_crtc;
	required_device<wd2795_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	required_device<beep_device> m_beep;
	required_device<palette_device> m_palette;
	required_region_ptr<u8> m_p_chargen;
};


void cpu09_state::cpu09_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x1000, 0x2081).noprw();
	map(0xeb00, 0xeb03).rw(m_pia0, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xeb04, 0xeb05).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0xeb08, 0xeb0f).rw(m_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write));
	map(0xec00, 0xefff).ram(); // 1Kx8 RAM MK4118
	map(0xf000, 0xffff).rom().region("roms", 0);
}

void ivg09_state::ivg09_mem(address_map &map)
{
	map.unmap_value_high();
	cpu09_mem(map);
	map(0x1000, 0x1fff).rw(FUNC(ivg09_state::vram_r), FUNC(ivg09_state::vram_w));
	map(0x2000, 0x2003).rw(m_pia1, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x2080, 0x2080).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x2081, 0x2081).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0xe000, 0xe003).rw(m_fdc, FUNC(wd2795_device::read), FUNC(wd2795_device::write));
	map(0xe080, 0xe080).w(FUNC(ivg09_state::ds_w));
}


/* Input ports */
static INPUT_PORTS_START( cpu09 )
	PORT_START("DSW")
	PORT_DIPNAME( 0x01, 0x00, "IRQ PTM") PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(    0x01, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x02, 0x00, "IRQ ACIA") PORT_DIPLOCATION("SW1:2")
	PORT_DIPSETTING(    0x02, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x60, 0x40, "Terminal") PORT_DIPLOCATION("SW1:3,4")
	PORT_DIPSETTING(    0x00, "110 baud" )
	PORT_DIPSETTING(    0x20, "300 baud" )
	PORT_DIPSETTING(    0x40, "1200 baud" )
	PORT_DIPSETTING(    0x60, "IVG09 (mc6845)" )
INPUT_PORTS_END

static INPUT_PORTS_START( ivg09 )
	PORT_START("DSW")
	PORT_DIPNAME( 0x01, 0x00, "IRQ PTM") PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(    0x01, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x02, 0x00, "IRQ ACIA") PORT_DIPLOCATION("SW1:2")
	PORT_DIPSETTING(    0x02, DEF_STR(Off))
	PORT_DIPSETTING(    0x00, DEF_STR(On))
	PORT_DIPNAME( 0x60, 0x60, "Terminal") PORT_DIPLOCATION("SW1:3,4")
	PORT_DIPSETTING(    0x00, "110 baud" )
	PORT_DIPSETTING(    0x20, "300 baud" )
	PORT_DIPSETTING(    0x40, "1200 baud" )
	PORT_DIPSETTING(    0x60, "IVG09 (mc6845)" )
INPUT_PORTS_END

void cpu09_state::machine_reset()
{
}

void ivg09_state::machine_reset()
{
	m_beep->set_state(1);
	m_term_data = 0;
	m_pia1->cb1_w(1);
}

void cpu09_state::machine_start()
{
	save_item(NAME(m_pa));
	save_item(NAME(m_cassold));
}

void ivg09_state::machine_start()
{
	// 4 bits of attribute ram
	m_vram = make_unique_clear<u16[]>(0x1000);
	save_pointer(NAME(m_vram), 0x1000);
	save_item(NAME(m_pa));
	save_item(NAME(m_cassold));
	save_item(NAME(m_term_data));
	save_item(NAME(m_ivg_pa));
	save_item(NAME(m_flashcnt));  // not essential
}

static void ifd09_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

// can support 3 drives
void ivg09_state::ds_w(u8 data)
{
	floppy_image_device *floppy = nullptr;
	if ((data & 3) == 1) floppy = m_floppy0->get_device();
	//if ((data & 3) == 2) floppy = m_floppy1->get_device();
	//if ((data & 3) == 3) floppy = m_floppy2->get_device();

	m_fdc->set_floppy(floppy);

	if (floppy)
	{
		floppy->mon_w(0);
		m_fdc->dden_w(!BIT(data, 2));
	}
}

// Attributes when high: 0 = alpha rom; 1 = flash; 2 = reverse video; 3 = highlight off
MC6845_UPDATE_ROW( ivg09_state::crtc_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u32 *p = &bitmap.pix(y);
	m_flashcnt++;

	for (u16 x = 0; x < x_count; x++)
	{
		u16 const mem = (ma + x) & 0xfff;
		u8 const attr = m_vram[mem] >> 8;
		u8 const inv = ((x == cursor_x) ^ (BIT(attr, 2)) ^ (BIT(attr, 1) && BIT(m_flashcnt, 6))) ? 0xff : 0;
		u8 const gfx = m_p_chargen[((m_vram[mem] & 0x1ff)<<4) | ra] ^ inv;   // takes care of attr bit 0 too
		u8 const pen = BIT(attr, 3) ? 1 : 2;

		/* Display a scanline of a character */
		*p++ = palette[BIT(gfx, 7) ? pen : 0];
		*p++ = palette[BIT(gfx, 6) ? pen : 0];
		*p++ = palette[BIT(gfx, 5) ? pen : 0];
		*p++ = palette[BIT(gfx, 4) ? pen : 0];
		*p++ = palette[BIT(gfx, 3) ? pen : 0];
		*p++ = palette[BIT(gfx, 2) ? pen : 0];
		*p++ = palette[BIT(gfx, 1) ? pen : 0];
		*p++ = palette[BIT(gfx, 0) ? pen : 0];
	}
}


u8 cpu09_state::pa_r()
{
	return ioport("DSW")->read() | m_pa;
}


/*
d0: A16
d1: A17 (for 256kb expansion)
d2, d3, d4: to 40-pin port
d5: S3
d6: S4
d7: cassout
*/
void cpu09_state::pa_w(u8 data)
{
	m_pa = data & 0x9f;
	m_cass->output(BIT(data, 7) ? -1.0 : +1.0);
}

// centronics
void cpu09_state::pb_w(u8 data)
{
}

u8 ivg09_state::pb_ivg_r()
{
	u8 ret = m_term_data;
	m_term_data = 0;
	return ret;
}

void ivg09_state::pa_ivg_w(u8 data)
{
// bits 0-3 are attribute bits
	m_ivg_pa = data & 15;
}

TIMER_DEVICE_CALLBACK_MEMBER( cpu09_state::kansas_r )
{
	if ((m_cass->get_state() & CASSETTE_MASK_UISTATE) != CASSETTE_PLAY)
		return;

	bool cass_ws = (m_cass->input() > +0.04) ? 1 : 0;
	if (cass_ws != m_cassold)
	{
		m_cassold = cass_ws;
		m_pia0->ca1_w(cass_ws);
	}
}

void ivg09_state::vram_w(offs_t offset, u8 data)
{
	m_vram[offset] = data | (m_ivg_pa << 8);
}

// return character; attributes cannot be read
u8 ivg09_state::vram_r(offs_t offset)
{
	return m_vram[offset] & 0xff;
}

static constexpr rgb_t ivg09_pens[3] =
{
	{ 0x00, 0x00, 0x00 }, // black
	{ 0xa0, 0xa0, 0xa0 }, // white
	{ 0xff, 0xff, 0xff }  // highlight
};

void ivg09_state::ivg09_palette(palette_device &palette) const
{
	palette.set_pen_colors(0, ivg09_pens);
}


/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 13,                   /* 8 x 9 characters */
	512,                    /* number of characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( ivg09_gfx )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END

void ivg09_state::kbd_put(u8 data)
{
	m_term_data = data;
	m_pia1->cb1_w(0);
	m_pia1->cb1_w(1);
}

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_1200 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_1200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END

void cpu09_state::cpu09(machine_config &config)
{
	/* basic machine hardware */
	MC6809(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &cpu09_state::cpu09_mem);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	/* Devices */
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	TIMER(config, "kansas_r").configure_periodic(FUNC(cpu09_state::kansas_r), attotime::from_hz(19200));

	PIA6821(config, m_pia0);
	m_pia0->readpa_handler().set(FUNC(cpu09_state::pa_r));
	m_pia0->ca1_w(0);
	m_pia0->writepa_handler().set(FUNC(cpu09_state::pa_w));
	m_pia0->writepb_handler().set(FUNC(cpu09_state::pb_w));

	PTM6840(config, m_ptm, 4_MHz_XTAL / 4);
	// all i/o lines connect to the 40-pin expansion connector
	m_ptm->set_external_clocks(0, 0, 0);
	m_ptm->o1_callback().set("acia", FUNC(acia6850_device::write_txc));
	m_ptm->o1_callback().append("acia", FUNC(acia6850_device::write_rxc));
	m_ptm->o2_callback().set_inputline("maincpu", INPUT_LINE_NMI);
	m_ptm->irq_callback().set_inputline("maincpu", M6809_IRQ_LINE);

	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_acia->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("acia", FUNC(acia6850_device::write_rxd));
	rs232.cts_handler().set("acia", FUNC(acia6850_device::write_cts));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));
}

void ivg09_state::ivg09(machine_config &config)
{
	cpu09(config);
	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &ivg09_state::ivg09_mem);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(80*8, 25*10);
	screen.set_visarea(0, 80*8-1, 0, 25*10-1);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));
	GFXDECODE(config, "gfxdecode", m_palette, ivg09_gfx);
	PALETTE(config, m_palette, FUNC(ivg09_state::ivg09_palette), 3);

	/* sound hardware */
	BEEP(config, m_beep, 950).add_route(ALL_OUTPUTS, "mono", 0.50); // guess

	/* Devices */
	subdevice<rs232_port_device>("rs232")->set_default_option(nullptr);

	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(ivg09_state::kbd_put));

	MC6845(config, m_crtc, 1008000); // unknown clock
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(ivg09_state::crtc_update_row));

	PIA6821(config, m_pia1);
	m_pia1->readpb_handler().set(FUNC(ivg09_state::pb_ivg_r));
	m_pia1->writepa_handler().set(FUNC(ivg09_state::pa_ivg_w));
	m_pia1->cb2_handler().set(m_beep, FUNC(beep_device::set_state));

	WD2795(config, m_fdc, 8_MHz_XTAL / 8);
	FLOPPY_CONNECTOR(config, "fdc:0", ifd09_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
}

/* ROM definition */
ROM_START( cpu09 )
	ROM_REGION( 0x1000, "roms", 0 )
	ROM_LOAD( "tavbug.bin",   0x0000, 0x1000, CRC(77945cae) SHA1(d89b577bc0b4e15e9a49a849998681bdc6cf5fbe) )
ROM_END

ROM_START( ivg09 )
	ROM_REGION( 0x19f0, "roms", 0 )
	ROM_LOAD( "tavbug.bin",   0x0000, 0x1000, CRC(77945cae) SHA1(d89b577bc0b4e15e9a49a849998681bdc6cf5fbe) )
	// these 2 are not used. Boottav is copied to ram at c100
	ROM_LOAD_OPTIONAL( "promon.bin",   0x1000, 0x0800, CRC(43256bf2) SHA1(e81acb5b659d50d7b019b97ad5d2a8f129da39f6) )
	ROM_LOAD_OPTIONAL( "boottav.bin",  0x1800, 0x01f0, CRC(ae1a858d) SHA1(ab2144a00afd5b75c6dcb15c2c3f9d6910a159ae) )

	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "big.bin",   0x0000, 0x1000, CRC(f27f6bfe) SHA1(d9509c6e1d10e042ad3cdfaec31114148dee9ff4)) // first half is rubbish
	ROM_CONTINUE(0x0000, 0x1000) // big
	ROM_LOAD( "small.bin", 0x1000, 0x1000, CRC(16e25eed) SHA1(5d31f127fe635be4bca06840b15a1bd77f971492)) // small
ROM_END

} // Anonymous namespace

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS           INIT        COMPANY         FULLNAME                      FLAGS
COMP( 1982, cpu09, 0,      0,      cpu09,   cpu09, cpu09_state, empty_init, "C. Tavernier", "CPU09",                      MACHINE_NOT_WORKING )
COMP( 1983, ivg09, cpu09,  0,      ivg09,   ivg09, ivg09_state, empty_init, "C. Tavernier", "CPU09 with IVG09 and IFD09", MACHINE_NOT_WORKING )



td831.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    Burroughs TD 831 terminal

    Hardware:
    - M6800
    - RAM: 4k + 4k (expansion)
    - ROM: 8x2k (space for a total of 32 ROMs)
    - EAROM 1400
    - 5x 6821 PIA
    - 3x 6850 ACIA
    - 1843.2 KHz XTAL
    - 80x25 characters display (maximum)
    - 5x7 or 7x9 pixels character size (depending on character generator)

    TODO:
    - Everything

    Notes:
    - Quickly jumps into an invalid location

***************************************************************************/

#include "emu.h"
#include "cpu/m6800/m6800.h"
#include "machine/6821pia.h"
#include "machine/6850acia.h"
#include "machine/er1400.h"


namespace {


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class td831_state : public driver_device
{
public:
	td831_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu")
	{
	}

	void td831(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<m6800_cpu_device> m_maincpu;

	void mem_map(address_map &map) ATTR_COLD;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void td831_state::mem_map(address_map &map)
{
	map(0x0000, 0x0fff).ram();
	map(0x4000, 0x4fff).ram(); // expansion
	map(0x7f00, 0x7fff).unmaprw(); // pia/acia registers
	map(0x8000, 0xbfff).rom().region("device", 0);
	map(0xc000, 0xffff).rom().region("system", 0);
}


//**************************************************************************
//  INPUT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( td831 )
INPUT_PORTS_END


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void td831_state::machine_start()
{
}

void td831_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void td831_state::td831(machine_config &config)
{
	M6800(config, m_maincpu, 1000000); // unknown clock
	m_maincpu->set_addrmap(AS_PROGRAM, &td831_state::mem_map);
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( td831 )
	ROM_REGION(0x4000, "device", 0)
	ROM_LOAD("1.bin", 0x2000, 0x0800, CRC(9d56f6c9) SHA1(ed878441046c94a02f84378ae9a23093e2c97e3e))
	ROM_LOAD("2.bin", 0x2800, 0x0800, CRC(60d79031) SHA1(53015c39e1f8ea2f26a9a68ab72682e22448752b))
	ROM_LOAD("3.bin", 0x3000, 0x0800, CRC(fd459a6f) SHA1(bc6fad318884f86bf7e3edb86cb33bdbb54103e5))
	ROM_LOAD("4.bin", 0x3800, 0x0800, CRC(d7dbab60) SHA1(9893f62aa90b655d9603245abf5aeeb502e71d06))

	ROM_REGION(0x4000, "system", 0)
	ROM_LOAD("5.bin", 0x2000, 0x0800, CRC(1995bd6c) SHA1(69b18306e4edca7b7ef72c169cf8422b2598581a))
	ROM_LOAD("6.bin", 0x2800, 0x0800, CRC(faccd4f8) SHA1(b7ea0e88556f332bc28de675cd95caf9eaf0eca0))
	ROM_LOAD("7.bin", 0x3000, 0x0800, CRC(1412d84a) SHA1(d4f6141322f2020a88ce4c434d9ee6cfa7ac989d))
	ROM_LOAD("8.bin", 0x3800, 0x0800, CRC(1e3473b9) SHA1(85e370a6b1f6b767a3aed750633359fac984043d))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY      FULLNAME  FLAGS
COMP( 1976, td831, 0,      0,      td831,   td831, td831_state, empty_init, "Burroughs", "TD 831", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



tdv2115l.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Frode van der Meeren
/***************************************************************************

    Tandberg TDV-2115L Terminal

    This driver uses the TDV-2100 series Display Logic module as a regular
    dumb-terminal, being the simplest configuration in the TDV-2100 series.

****************************************************************************/

#include "emu.h"

#include "tdv2100_disp_logic.h"
#include "tdv2100_kbd.h"

namespace {

class tdv2115l_state : public driver_device
{
public:
	tdv2115l_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_terminal(*this, "terminal"),
		m_keyboard(*this, "keyboard")
	{}

	void tdv2115l(machine_config &config);

private:
	required_device<tandberg_tdv2100_disp_logic_device> m_terminal;
	required_device<tandberg_tdv2100_keyboard_device> m_keyboard;
};

void tdv2115l_state::tdv2115l(machine_config& config)
{
	TANDBERG_TDV2100_DISPLAY_LOGIC(config, m_terminal);
	m_terminal->write_waitl_callback().set(m_keyboard, FUNC(tandberg_tdv2100_keyboard_device::waitl_w));
	m_terminal->write_onlil_callback().set(m_keyboard, FUNC(tandberg_tdv2100_keyboard_device::onlil_w));
	m_terminal->write_carl_callback().set(m_keyboard, FUNC(tandberg_tdv2100_keyboard_device::carl_w));
	m_terminal->write_errorl_callback().set(m_keyboard, FUNC(tandberg_tdv2100_keyboard_device::errorl_w));
	m_terminal->write_enql_callback().set(m_keyboard, FUNC(tandberg_tdv2100_keyboard_device::enql_w));
	m_terminal->write_ackl_callback().set(m_keyboard, FUNC(tandberg_tdv2100_keyboard_device::ackl_w));
	m_terminal->write_nakl_callback().set(m_keyboard, FUNC(tandberg_tdv2100_keyboard_device::nakl_w));

	TANDBERG_TDV2100_KEYBOARD(config, m_keyboard);
	m_keyboard->write_kstr_callback().set(m_terminal, FUNC(tandberg_tdv2100_disp_logic_device::process_keyboard_char));
	m_keyboard->write_cleark_callback().set(m_terminal, FUNC(tandberg_tdv2100_disp_logic_device::cleark_w));
	m_keyboard->write_linek_callback().set(m_terminal, FUNC(tandberg_tdv2100_disp_logic_device::linek_w));
	m_keyboard->write_transk_callback().set(m_terminal, FUNC(tandberg_tdv2100_disp_logic_device::transk_w));
	m_keyboard->write_break_callback().set(m_terminal, FUNC(tandberg_tdv2100_disp_logic_device::break_w));
}

static INPUT_PORTS_START( tdv2115l )
INPUT_PORTS_END

// ROM definition
ROM_START( tdv2115l )
ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY     FULLNAME     FLAGS
COMP( 1976, tdv2115l, 0,      0,      tdv2115l, tdv2115l, tdv2115l_state, empty_init, "Tandberg", "TDV-2115L", MACHINE_SUPPORTS_SAVE )



tdv2324.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder,Jonathan Gevaryahu
/*

    Tandberg TDV2324

    Skeleton driver
    By Curt Coder with some work by Lord Nightmare

    Status:
    * Main cpu is currently hacked to read i/o port 0xE6 as 0x10;
      it then seems to copy code to a ram area then jumps there
      (there may be some sort of overlay/banking mess going on to allow full 64kb of ram)
      The cpu gets stuck reading i/o port 0x30 in a loop.
      - interrupts and sio lines are not hooked up
    * Sub cpu does a bunch of unknown i/o accesses and also tries to
      sequentially read chunk of address space which it never writes to;
      this seems likely to be a shared ram or i/o mapped area especially since it seems
      to write to i/o port 0x60 before trying to read there.
      - interrupts and sio lines are not hooked up
    * Fdc cpu starts, does a rom checksum (which passes) and tests a ram area


    Board Notes:
    Mainboard (pictures P1010036 & P1010038)
    *28-pin: D27128, L4267096S,...(eprom, occluded by sticker: "965268 1", character set)
    *40-pin: TMS9937NL, DB 336, ENGLAND (VTAC Video Chip)
    *40-pin: P8085AH-2, F4265030, C INTEL '80 (cpus, there are 2 of these)
    *28-pin: JAPAN 8442, 00009SS0, HN4827128G-25 (eprom, sticker: "962107")
    *22-pin: ER3400, GI 8401HHA (EAROM)
    *  -pin: MOSTEK C 8424, MK3887N-4 (Z80-SIO/2 Serial I/O Controller)
    *20-pin: (pal, sticker: "961420 0")
    *24-pin: D2716, L3263271, INTEL '77 (eprom, sticker: "962058 1")
    *3 tiny 16-pins which look socketed (proms)
    *+B8412, DMPAL10L8NC
    *PAL... (can't remove the sticker to read the rest since there's electrical components soldered above the chip)
    *Am27S21DC, 835IDmm
    *AM27S13DC, 8402DM (x2)
    *TBP28L22N, GERMANY 406 A (x2)
    *PAL16L6CNS, 8406

    FD/HD Interface Board P/N 962013 Rev14 (pictures P1010031 & P1010033)
    *28-pin: TMS, 2764JL-25, GHP8414 (@U15, labeled "962014 // -4-", fdc cpu rom)
    *40-pin: MC68B02P, R1H 8340 (fdc cpu)
    *40-pin: WDC '79, FD1797PL-02, 8342 16 (fdc chip)
    *14-pin: MC4024P, MG 8341 (dual voltage controlled multivibrator)
    *24-pin: TMM2016AP-12 (@U14 and @U80, 120ns 2kx8 SRAM)

    Keyboard:
    *40-pin: NEC D8035LC (mcs-48 cpu)
    *24-pin: NEC D2716 (eprom)

    Main CPU:
    - PIT, SIO

    Sub CPU:
    - PIC, PIT, VTAC

*/
/*
'subcpu' (17CD): unmapped i/o memory write to 23 = 36 & FF
'subcpu' (17D1): unmapped i/o memory write to 23 = 76 & FF
'subcpu' (17D5): unmapped i/o memory write to 23 = B6 & FF
'subcpu' (17DB): unmapped i/o memory write to 20 = 1A & FF
'subcpu' (17DE): unmapped i/o memory write to 20 = 00 & FF
'subcpu' (17E0): unmapped i/o memory write to 3E = 00 & FF
'subcpu' (17E2): unmapped i/o memory write to 3A = 00 & FF
'subcpu' (17E6): unmapped i/o memory write to 30 = 74 & FF
'subcpu' (17EA): unmapped i/o memory write to 31 = 7F & FF
'subcpu' (17EE): unmapped i/o memory write to 32 = 6D & FF
'subcpu' (17F2): unmapped i/o memory write to 33 = 18 & FF
'subcpu' (17F6): unmapped i/o memory write to 34 = 49 & FF
'subcpu' (17FA): unmapped i/o memory write to 35 = 20 & FF
'subcpu' (17FE): unmapped i/o memory write to 36 = 18 & FF
'subcpu' (1801): unmapped i/o memory write to 3C = 00 & FF
'subcpu' (1803): unmapped i/o memory write to 3C = 00 & FF
'subcpu' (1805): unmapped i/o memory write to 3E = 00 & FF
'subcpu' (0884): unmapped i/o memory write to 10 = 97 & FF
'subcpu' (0888): unmapped i/o memory write to 10 = 96 & FF

'fdccpu' (E004): unmapped program memory read from 3C05 & FF  0011 1100 0000 0101
'fdccpu' (E007): unmapped program memory read from C000 & FF  1100 0000 0000 0000
'fdccpu' (E00A): unmapped program memory read from A000 & FF  1010 0000 0000 0000
'fdccpu' (E012): unmapped program memory write to F000 = D0 & FF 1111 0000 0000 0000 = 1101 0000
'fdccpu' (E015): unmapped program memory read from 3801 & FF  0011 1000 0000 0001
'fdccpu' (E018): unmapped program memory read from 3C06 & FF  0011 1100 0000 0110
'fdccpu' (E01B): unmapped program memory read from 3C04 & FF  0011 1100 0000 0100

'fdccpu' (E070): unmapped program memory write to 2101 = 01 & FF
'fdccpu' (E07C): unmapped program memory read from 6000 & FF
'fdccpu' (E07F): unmapped program memory read from 380D & FF
'fdccpu' (E082): unmapped program memory read from 380F & FF
'fdccpu' (E085): unmapped program memory read from 3803 & FF
'fdccpu' (E08B): unmapped program memory write to 6000 = 08 & FF
'fdccpu' (E08E): unmapped program memory write to 8000 = 08 & FF
'fdccpu' (E091): unmapped program memory write to 6000 = 00 & FF
'fdccpu' (E099): unmapped program memory write to F800 = 55 & FF
...
*/



#include "emu.h"
#include "tdv2324.h"

#include "emupal.h"
#include "screen.h"
#include "softlist.h"


uint8_t tdv2324_state::tdv2324_main_io_30()
{
	return 0xff;
}

// Not sure what this is for, i/o read at 0xE6 on maincpu, post fails if it does not return bit 4 set
uint8_t tdv2324_state::tdv2324_main_io_e6()
{
	return 0x10; // TODO: this should actually return something meaningful, for now is enough to pass early boot test
}

void tdv2324_state::tdv2324_main_io_e2(uint8_t data)
{
	printf("%c\n", data);
}


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( tdv2324_mem )
//-------------------------------------------------

void tdv2324_state::tdv2324_mem(address_map &map)
{
	map(0x0000, 0x07ff).mirror(0x0800).rom().region(P8085AH_0_TAG, 0);
	/* when copying code to 4000 area it runs right off the end of rom;
	 * I'm not sure if its supposed to mirror or read as open bus */
//  map(0x4000, 0x5fff).ram(); // 0x4000 has the boot code copied to it, 5fff and down are the stack
//  map(0x6000, 0x6fff).ram(); // used by the relocated boot code; shared?
	map(0x0800, 0xffff).ram();
}


//-------------------------------------------------
//  ADDRESS_MAP( tdv2324_io )
//-------------------------------------------------

void tdv2324_state::tdv2324_io(address_map &map)
{
	//map.global_mask(0xff);
	/* 0x30 is read by main code and if high bit isn't set at some point it will never get anywhere */
	/* e0, e2, e8, ea are written to */
	/* 30, e6 and e2 are readable */
	map(0x30, 0x30).r(FUNC(tdv2324_state::tdv2324_main_io_30));
//  map(0xe2, 0xe2).w(FUNC(tdv2324_state::tdv2324_main_io_e2)); console output
	map(0xe6, 0xe6).r(FUNC(tdv2324_state::tdv2324_main_io_e6));
//  map(0x, 0x).rw(P8253_5_0_TAG, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
//  map(0x, 0x).rw(MK3887N4_TAG, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
//  map(0x, 0x).rw(P8259A_TAG, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
}


//-------------------------------------------------
//  ADDRESS_MAP( tdv2324_sub_mem )
//-------------------------------------------------

void tdv2324_state::tdv2324_sub_mem(address_map &map)
{
	map(0x0000, 0x3fff).rom().region(P8085AH_1_TAG, 0);
	map(0x4000, 0x47ff).ram();
	map(0x5000, 0x53ff).ram(); // EAROM
	map(0x6000, 0x7fff).ram().share("video_ram");
}


//-------------------------------------------------
//  ADDRESS_MAP( tdv2324_sub_io )
//-------------------------------------------------

void tdv2324_state::tdv2324_sub_io(address_map &map)
{
	//map.global_mask(0xff);
	/* 20, 23, 30-36, 38, 3a, 3c, 3e, 60, 70 are written to */
	map(0x20, 0x23).rw(m_pit1, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0x30, 0x3f).rw(m_tms, FUNC(tms9927_device::read), FUNC(tms9927_device::write)); // TODO: this is supposed to be a 9937, which is not quite the same as 9927
}


//-------------------------------------------------
//  ADDRESS_MAP( tdv2324_fdc_mem )
//-------------------------------------------------

void tdv2324_state::tdv2324_fdc_mem(address_map &map)
{
	// the following two are probably enabled/disabled via the JP2 jumper block next to the fdc cpu
	//map(0x0000, 0x001f).ram(); // on-6802-die ram (optionally battery backed)
	//map(0x0020, 0x007f).ram(); // on-6802-die ram
	map(0x0000, 0x07ff).ram(); // TMM2016AP-12 @ U14, tested with A5,5A pattern
	//map(0x1000, 0x17ff).ram(); // TMM2016AP-12 @ U80, address is wrong
	// the 3xxx area appears to be closely involved in fdc or other i/o
	// in particular, reads from 30xx, 38xx, 3Cxx may be actually writes to certain fdc registers with data xx?
	// 0x2101 is something writable
	// 0x8000 is either a read from reg 0 (status reg) of the FD1797, OR a read from some sort of status from other cpus
	// 0x8000 can also be written to
	// 0x6000 can also be read from and written to
	// Somewhere in here, the FDC chip and the hard disk interface live
	map(0xe000, 0xffff).rom().region(MC68B02P_TAG, 0); // rom "962014 // -4-" @U15
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( tdv2324 )
//-------------------------------------------------

static INPUT_PORTS_START( tdv2324 )
INPUT_PORTS_END



//**************************************************************************
//  VIDEO
//**************************************************************************

void tdv2324_state::video_start()
{
}


uint32_t tdv2324_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  SLOT_INTERFACE( tdv2324_floppies )
//-------------------------------------------------

static void tdv2324_floppies(device_slot_interface &device)
{
	device.option_add("8dsdd", FLOPPY_8_DSDD);
}



//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  machine_config( tdv2324 )
//-------------------------------------------------

void tdv2324_state::tdv2324(machine_config &config)
{
	// basic system hardware
	i8085a_cpu_device &maincpu(I8085A(config, m_maincpu, 8700000/2)); // ???
	maincpu.set_addrmap(AS_PROGRAM, &tdv2324_state::tdv2324_mem);
	maincpu.set_addrmap(AS_IO, &tdv2324_state::tdv2324_io);
	maincpu.in_inta_func().set(P8259A_TAG, FUNC(pic8259_device::acknowledge));

	I8085A(config, m_subcpu, 8000000/2); // ???
	m_subcpu->set_addrmap(AS_PROGRAM, &tdv2324_state::tdv2324_sub_mem);
	m_subcpu->set_addrmap(AS_IO, &tdv2324_state::tdv2324_sub_io);

	M6802(config, m_fdccpu, 8000000/2); // ???
	m_fdccpu->set_ram_enable(false);
	m_fdccpu->set_addrmap(AS_PROGRAM, &tdv2324_state::tdv2324_fdc_mem);

	// video hardware
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_refresh_hz(50);
	screen.set_screen_update(FUNC(tdv2324_state::screen_update));
	screen.set_size(800, 400);
	screen.set_visarea(0, 800-1, 0, 400-1);

	PALETTE(config, "palette", palette_device::MONOCHROME);

	TMS9927(config, m_tms, 25.39836_MHz_XTAL / 8).set_char_width(8);

	// devices
	PIC8259(config, m_pic);

	PIT8253(config, m_pit0);

	PIT8253(config, m_pit1);

	Z80SIO(config, MK3887N4_TAG, 8000000/2);

	FD1797(config, FD1797PL02_TAG, 8000000/4);
	FLOPPY_CONNECTOR(config, FD1797PL02_TAG":0", tdv2324_floppies, "8dsdd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, FD1797PL02_TAG":1", tdv2324_floppies, "8dsdd", floppy_image_device::default_mfm_floppy_formats);

	// internal ram
	RAM(config, RAM_TAG).set_default_size("64K");

	// software list
	SOFTWARE_LIST(config, "flop_list").set_original("tdv2324");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( tdv2324 )
//-------------------------------------------------

ROM_START( tdv2324 )
	ROM_REGION( 0x800, P8085AH_0_TAG, 0 )
	ROM_LOAD( "962058-1.21g", 0x000, 0x800, CRC(3771aece) SHA1(36d3f03235f327d6c8682e5c167aed6dddfaa6ec) )

	ROM_REGION( 0x4000, P8085AH_1_TAG, 0 )
	ROM_LOAD( "962107-1.12c", 0x0000, 0x4000, CRC(29c1a139) SHA1(f55fa9075fdbfa6a3e94e5120270179f754d0ea5) )

	ROM_REGION( 0x2000, MC68B02P_TAG, 0 )
	ROM_LOAD( "962014-4.13c", 0x0000, 0x2000, CRC(d01c32cd) SHA1(1f00f5f5ff0c035eec6af820b5acb6d0c207b6db) )

	ROM_REGION( 0x800, "keyboard_8035", 0 )
	ROM_LOAD( "961294-3.u8", 0x000, 0x800, NO_DUMP )

	ROM_REGION( 0x4000, "chargen", 0 )
	ROM_LOAD( "965268-1.4g", 0x0000, 0x4000, CRC(7222a85f) SHA1(e94074b68d90698734ab1fc38d156407846df47c) )

	ROM_REGION( 0x200, "proms", 0 )
	ROM_LOAD( "961487-1.3f", 0x0000, 0x0200, NO_DUMP )
	ROM_LOAD( "prom.4f", 0x0000, 0x0200, NO_DUMP )
	ROM_LOAD( "prom.8g", 0x0000, 0x0200, NO_DUMP )
	ROM_LOAD( "prom.10f", 0x0000, 0x0200, NO_DUMP )
	ROM_LOAD( "961420-0.16f", 0x0000, 0x0200, NO_DUMP )
	ROM_LOAD( "962103-0.15g", 0x0000, 0x0200, NO_DUMP )

	ROM_REGION( 0x200, "plds", 0 )
	ROM_LOAD( "962108-0.2g", 0x0000, 0x0200, NO_DUMP )
	ROM_LOAD( "pal.12d", 0x0000, 0x0200, NO_DUMP )
	ROM_LOAD( "pal.13d", 0x0000, 0x0200, NO_DUMP )
ROM_END



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY     FULLNAME    FLAGS
COMP( 1983, tdv2324, 0,      0,      tdv2324, tdv2324, tdv2324_state, empty_init, "Tandberg", "TDV 2324", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



teammate.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Michael Kamprath
/*******************************************************************************

Logix Teammate Game Computer
Stylized as T.E.A.M.M.A.T.E.: Total Electronic Advanced Microprocessing
Maneuvers And Tactics Equipment

It's a tabletop with built-in mini games, led display overlays were included.
Two versions are known, one with black hexadecimal keys, and one with blue keys.

Hardware notes:
- Mostek 3870 @ ~3.6MHz
- 2*7seg led + 16 leds, 1-bit sound

*******************************************************************************/

#include "emu.h"

#include "cpu/f8/f8.h"
#include "machine/f3853.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "teammate.lh"


namespace {

class teammate_state : public driver_device
{
public:
	teammate_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void teammate(machine_config &config);

	// P3 button is tied to MCU RESET pin
	DECLARE_INPUT_CHANGED_MEMBER(reset_button) { m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE); }

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<dac_bit_interface> m_dac;
	required_ioport_array<3> m_inputs;

	u8 m_inp_sel = 0;
	u8 m_input = 0;
	u8 m_sound = 0;
	u8 m_select = 0;
	u8 m_led_data = 0;

	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	void select_w(u8 data);
	u8 select_r();
	void led_w(u8 data);
	u8 led_r();
	void input_w(u8 data);
	u8 input_r();
	void sound_w(u8 data);
	u8 sound_r();
};

void teammate_state::machine_start()
{
	save_item(NAME(m_inp_sel));
	save_item(NAME(m_input));
	save_item(NAME(m_sound));
	save_item(NAME(m_select));
	save_item(NAME(m_led_data));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void teammate_state::select_w(u8 data)
{
	// P00,P01,P06,P07: led select
	m_display->write_my((data >> 4 & 0xc) | (data & 3));

	// P02,P03,P05: input select
	// P04: N/C
	m_inp_sel = (data >> 3 & 4) | (data >> 2 & 3);
	m_select = data;
}

u8 teammate_state::select_r()
{
	return m_select;
}

void teammate_state::led_w(u8 data)
{
	// P10-P17: DS8817N to leds
	m_display->write_mx(bitswap<8>(~data,0,1,2,3,4,5,6,7));
	m_led_data = data;
}

u8 teammate_state::led_r()
{
	return m_led_data;
}

void teammate_state::input_w(u8 data)
{
	m_input = data;
}

u8 teammate_state::input_r()
{
	u8 data = 0;

	// P40-P47: read buttons
	for (int i = 0; i < 3; i++)
		if (BIT(m_inp_sel, i))
			data |= m_inputs[i]->read();

	return data | m_input;
}

void teammate_state::sound_w(u8 data)
{
	// P50: speaker out
	m_dac->write(data & 1);
	m_sound = data;
}

u8 teammate_state::sound_r()
{
	return m_sound;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void teammate_state::main_map(address_map &map)
{
	map.global_mask(0x7ff);
	map(0x0000, 0x07ff).rom();
}

void teammate_state::main_io(address_map &map)
{
	map(0x00, 0x00).rw(FUNC(teammate_state::select_r), FUNC(teammate_state::select_w));
	map(0x01, 0x01).rw(FUNC(teammate_state::led_r), FUNC(teammate_state::led_w));
	map(0x04, 0x07).rw("psu", FUNC(f38t56_device::read), FUNC(f38t56_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( teammate )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0')

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')

	PORT_START("IN.2")
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_NAME("P1")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_NAME("P4")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_NAME("P2")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(teammate_state::reset_button), 0) PORT_NAME("P3")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void teammate_state::teammate(machine_config &config)
{
	// basic machine hardware
	F8(config, m_maincpu, 3'600'000/2); // R/C osc, approximation
	m_maincpu->set_addrmap(AS_PROGRAM, &teammate_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &teammate_state::main_io);
	m_maincpu->set_irq_acknowledge_callback("psu", FUNC(f38t56_device::int_acknowledge));

	f38t56_device &psu(F38T56(config, "psu", 3'600'000/2));
	psu.set_int_vector(0x20);
	psu.int_req_callback().set_inputline("maincpu", F8_INPUT_LINE_INT_REQ);
	psu.read_a().set(FUNC(teammate_state::input_r));
	psu.write_a().set(FUNC(teammate_state::input_w));
	psu.read_b().set(FUNC(teammate_state::sound_r));
	psu.write_b().set(FUNC(teammate_state::sound_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 8);
	m_display->set_segmask(0xc, 0x7f);

	config.set_default_layout(layout_teammate);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( teammate )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD("mk14194n", 0x0000, 0x0800, CRC(15615d03) SHA1(4448a64888c68d6bf3555dcce736ad0126515843) )
ROM_END

} // anonymous namespace



/******************************************************************************
    Drivers
******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1978, teammate, 0,      0,      teammate, teammate, teammate_state, empty_init, "Logix", "Teammate Game Computer", MACHINE_SUPPORTS_SAVE )



tec1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

TEC-1 driver, written by Robbbert in April, 2009.

The TEC-1 was a single-board "computer" described in Talking Electronics
magazine, issues number 10 and 11. Talking Electronics do not have dates on
their issues, so the date is uncertain, although 1984 seems a reasonable
guess. Talking Electronics operated from Cheltenham, a suburb of Melbourne.

The hardware is quite simple, consisting of a Z80 cpu, 2x 8212 8-bit latch,
74C923 keyboard scanner, 20 push-button keys, 6-digit LED display, a speaker,
a 2k EPROM and sundry parts.

The cpu speed could be adjusted by using a potentiometer, the range being
250 kHz to 2MHz. This is a simple method of adjusting a game's difficulty.

We emulate the original version. Later enhancements included more RAM, speech
synthesis and various attachments, however I have no information on these.

2018-11-08 Obtained fresh dumps from the original designers.

Pasting:
        0-F : as is
        + (inc) : ^
        - (dec) : V
        AD : -
        GO : X

Keys:
0 to 9, A to F are on the key of the same name.
AD (input an address) is the '-' key.
+ and - (increment / decrement address) are the up and down-arrow keys.
GO (execute program at current address) is the X key.
SHIFT - later monitor versions utilised an extra shift button. Hold
        it down and press another key (use Left Shift).

Whenever a program listing mentions RESET, press Left-Alt key.

Each key causes a beep to be heard. You may need to press more than once
to get it to register.

Inbuilt games - press the following sequence of keys:
- Invaders:     RESET AD 0 0 0 8 GO GO (Paste: -0008XX)
- Nim:          RESET AD 0 0 1 0 GO GO (Paste: -0010XX)
- Lunar Lander: RESET AD 0 0 1 8 GO GO (Paste: -0018XX)
Tunes:
- Bealach An Doirin: RESET AD 0 0 2 8 GO GO (Paste: -0028XX)
- Biking up the strand: RESET AD 0 0 3 0 GO GO (Paste: -0030XX)

Thanks to Chris Schwartz who dumped his ROM for me way back in the old days.
It's only taken 25 years to get around to emulating it...


Differences between tec1 and tecjmon:

On the tec1 a keypress is indicated by an NMI from the 74C923; but on
the jmon it sets bit 6 of port 3 low instead. The NMI code is simply
a 'retn' in the jmon rom, so we can use the same code.
The jmon includes a cassette interface, a serial input connection,
and an optional LCD, but the games of the tec1 have been removed.



JMON ToDo:
- Add LCD display (2 rows by 16 characters)


***************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "machine/mm74c922.h"
#include "machine/rescap.h"
#include "sound/spkrdev.h"
#include "speaker.h"
#include "video/pwm.h"

#include "tec1.lh"


namespace {

class tec1_state : public driver_device
{
public:
	tec1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_speaker(*this, "speaker")
		, m_cass(*this, "cassette")
		, m_kb(*this, "keyboard")
		, m_io_shift(*this, "SHIFT")
		, m_display(*this, "display")
	{ }

	void tec1(machine_config &config);
	void tecjmon(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(reset_button);

private:
	virtual void machine_start() override ATTR_COLD;
	u8 kbd_r();
	u8 latch_r();
	void tec1_digit_w(u8 data);
	void tecjmon_digit_w(u8 data);
	void segment_w(u8 data);
	void da_w(int state);
	bool m_key_pressed = 0;
	u8 m_seg = 0U;
	u8 m_digit = 0U;
	void tec1_io(address_map &map) ATTR_COLD;
	void tec1_map(address_map &map) ATTR_COLD;
	void tecjmon_io(address_map &map) ATTR_COLD;
	void tecjmon_map(address_map &map) ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	required_device<speaker_sound_device> m_speaker;
	optional_device<cassette_image_device> m_cass;
	required_device<mm74c923_device> m_kb;
	required_ioport m_io_shift;
	required_device<pwm_display_device> m_display;
};




/***************************************************************************

    Display

***************************************************************************/

void tec1_state::segment_w(u8 data)
{
/*  d7 segment d
    d6 segment e
    d5 segment c
    d4 segment dot
    d3 segment b
    d2 segment g
    d1 segment f
    d0 segment a */

	m_seg = bitswap<8>(data, 4, 2, 1, 6, 7, 5, 3, 0);
	m_display->matrix(m_digit, m_seg);
}

void tec1_state::tec1_digit_w(u8 data)
{
/*  d7 speaker
    d6 not used
    d5 data digit 1
    d4 data digit 2
    d3 address digit 1
    d2 address digit 2
    d1 address digit 3
    d0 address digit 4 */

	m_speaker->level_w(BIT(data, 7));

	m_digit = data;
	m_display->matrix(m_digit, m_seg);
}

void tec1_state::tecjmon_digit_w(u8 data)
{
/*  d7 speaker & cassout
    d6 not used
    d5 data digit 1
    d4 data digit 2
    d3 address digit 1
    d2 address digit 2
    d1 address digit 3
    d0 address digit 4 */

	m_speaker->level_w(BIT(data, 7));
	m_cass->output(BIT(data, 7) ? -1.0 : +1.0);
	m_digit = data;
	m_display->matrix(m_digit, m_seg);
}


/***************************************************************************

    Keyboard

***************************************************************************/

u8 tec1_state::latch_r()
{
// bit 7 - cass in ; bit 6 low = key pressed
	u8 data = (m_key_pressed) ? 0 : 0x40;

	if (m_cass->input() > 0.03)
		data |= 0x80;

	return data;
}


u8 tec1_state::kbd_r()
{
	return m_kb->read() | m_io_shift->read();
}

void tec1_state::da_w(int state)
{
	m_key_pressed = state;
	m_maincpu->set_input_line(INPUT_LINE_NMI, state ? ASSERT_LINE : CLEAR_LINE);
}


/***************************************************************************

    Machine

***************************************************************************/

void tec1_state::machine_start()
{
	save_item(NAME(m_key_pressed));
	save_item(NAME(m_seg));
	save_item(NAME(m_digit));
}

/***************************************************************************

    Address Map

***************************************************************************/

void tec1_state::tec1_map(address_map &map)
{
	map.global_mask(0x3fff);
	map(0x0000, 0x07ff).rom();
	map(0x0800, 0x0fff).ram(); // on main board
	map(0x1000, 0x3fff).ram(); // expansion
}

void tec1_state::tec1_io(address_map &map)
{
	map.global_mask(0x07);
	map(0x00, 0x00).r(FUNC(tec1_state::kbd_r));
	map(0x01, 0x01).w(FUNC(tec1_state::tec1_digit_w));
	map(0x02, 0x02).w(FUNC(tec1_state::segment_w));
}


void tec1_state::tecjmon_map(address_map &map)
{
	map.global_mask(0x3fff);
	map(0x0000, 0x07ff).rom();
	map(0x0800, 0x37ff).ram();
	map(0x3800, 0x3fff).rom().region("maincpu", 0x0800);
}

void tec1_state::tecjmon_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x00).r(FUNC(tec1_state::kbd_r));
	map(0x01, 0x01).w(FUNC(tec1_state::tecjmon_digit_w));
	map(0x02, 0x02).w(FUNC(tec1_state::segment_w));
	map(0x03, 0x03).r(FUNC(tec1_state::latch_r));
	//map(0x04, 0x04).w(FUNC(tec1_state::lcd_en_w));
	//map(0x84, 0x84).w(FUNC(tec1_state::lcd_2nd_w));
}


/**************************************************************************

    Keyboard Layout

***************************************************************************/

static INPUT_PORTS_START( tec1 )
	PORT_START("LINE0") /* KEY ROW 0 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)    PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)    PORT_CHAR('4')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)    PORT_CHAR('8')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)    PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("+") PORT_CODE(KEYCODE_UP)   PORT_CHAR('^')

	PORT_START("LINE1") /* KEY ROW 1 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)    PORT_CHAR('1')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)    PORT_CHAR('5')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)    PORT_CHAR('9')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)    PORT_CHAR('D')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_DOWN) PORT_CHAR('V')

	PORT_START("LINE2") /* KEY ROW 2 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)    PORT_CHAR('2')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)    PORT_CHAR('6')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)    PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)    PORT_CHAR('E')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("GO") PORT_CODE(KEYCODE_X)   PORT_CHAR('X')

	PORT_START("LINE3") /* KEY ROW 3 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)    PORT_CHAR('3')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)    PORT_CHAR('7')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)    PORT_CHAR('B')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)    PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("AD") PORT_CODE(KEYCODE_MINUS)   PORT_CHAR('-')

	PORT_START("SHIFT")
	PORT_BIT(0x1f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0xc0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("RESET")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RESET") PORT_CODE(KEYCODE_LALT) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tec1_state::reset_button), 0)
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(tec1_state::reset_button)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}


/***************************************************************************

    Machine driver

***************************************************************************/

void tec1_state::tec1(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 1000000);   /* speed can be varied between 250kHz and 2MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &tec1_state::tec1_map);
	m_maincpu->set_addrmap(AS_IO, &tec1_state::tec1_io);

	/* video hardware */
	config.set_default_layout(layout_tec1);
	PWM_DISPLAY(config, m_display).set_size(6, 8);
	m_display->set_segmask(0x3f, 0xff);

	MM74C923(config, m_kb, 0);
	m_kb->set_cap_osc(CAP_N(100));
	m_kb->set_cap_debounce(CAP_U(1));
	m_kb->da_wr_callback().set(FUNC(tec1_state::da_w));
	m_kb->x1_rd_callback().set_ioport("LINE0");
	m_kb->x2_rd_callback().set_ioport("LINE1");
	m_kb->x3_rd_callback().set_ioport("LINE2");
	m_kb->x4_rd_callback().set_ioport("LINE3");

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);
}

void tec1_state::tecjmon(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(3'579'545) / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &tec1_state::tecjmon_map);
	m_maincpu->set_addrmap(AS_IO, &tec1_state::tecjmon_io);

	/* video hardware */
	config.set_default_layout(layout_tec1);
	PWM_DISPLAY(config, m_display).set_size(6, 8);
	m_display->set_segmask(0x3f, 0xff);

	MM74C923(config, m_kb, 0);
	m_kb->set_cap_osc(CAP_N(100));
	m_kb->set_cap_debounce(CAP_U(1));
	m_kb->da_wr_callback().set(FUNC(tec1_state::da_w));
	m_kb->x1_rd_callback().set_ioport("LINE0");
	m_kb->x2_rd_callback().set_ioport("LINE1");
	m_kb->x3_rd_callback().set_ioport("LINE2");
	m_kb->x4_rd_callback().set_ioport("LINE3");

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* Devices */
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
}


/***************************************************************************

    Game driver

***************************************************************************/

ROM_START(tec1)
	ROM_REGION(0x0800, "maincpu", 0 )
	ROM_SYSTEM_BIOS(0, "mon1", "MON1")
	ROMX_LOAD( "mon1.rom",   0x0000, 0x0800, CRC(5d379e6c) SHA1(5c810885a3f0d03c54aea74aaaa8fae8a2fd9ad4), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "mon1a", "MON1A")
	ROMX_LOAD("mon1a.rom",   0x0000, 0x0800, CRC(b3390c36) SHA1(18aabc68d473206b7fc4e365c6b57a4e218482c3), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "mon1b", "MON1B")
	//ROMX_LOAD("mon1b.rom",   0x0000, 0x0800, CRC(60daea3c) SHA1(383b7e7f02e91fb18c87eb03c5949e31156771d4), ROM_BIOS(2))
	ROMX_LOAD("mon1b.rom",   0x0000, 0x0800, CRC(6088811d) SHA1(2cec14a24fae769f22f6598b5a63fc79d90db394), ROM_BIOS(2))    // redump
	ROM_SYSTEM_BIOS(3, "mon2", "MON2")
	ROMX_LOAD("mon2.rom",    0x0000, 0x0800, CRC(082fd7e7) SHA1(7659add30ca22b15a03d1cbac0892a5c25e47ecd), ROM_BIOS(3))
ROM_END

ROM_START(tecjmon)
	ROM_REGION(0x1000, "maincpu", 0 )
	ROM_LOAD("jmon.rom",    0x0000, 0x0800, CRC(202c47a2) SHA1(701588ec5640d633d90d94b2ccd6f65422e19a70) )
	ROM_LOAD("util.rom",    0x0800, 0x0800, CRC(7c19700d) SHA1(dc5b3ade66bb11c54430056966ed99cdd299d82b) )
ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY                         FULLNAME            FLAGS
COMP( 1984, tec1,     0,      0,      tec1,    tec1,  tec1_state, empty_init, "Talking Electronics magazine", "TEC-1",            MACHINE_SUPPORTS_SAVE )
COMP( 1984, tecjmon,  tec1,   0,      tecjmon, tec1,  tec1_state, empty_init, "Talking Electronics magazine", "TEC-1A with JMON", MACHINE_SUPPORTS_SAVE )



tecnbras.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders: Felipe Sanches
/***************************************************************************

  TECNBRAS dot matrix display (70x7 pixels)
  Driver by Felipe Correa da Silva Sanches <juca@members.fsf.org>

    The display is composed of 14 blocks of 5x7 LEDs

    These LEDs are driven by several 74xx chips:
    * one 74138
    * several 74164 and ULN2003 chips

  Changelog:

   2014 JUN 23 [Felipe Sanches]:
   * Initial driver skeleton

================
*/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"

#include <algorithm>

#include "tecnbras.lh"


namespace {

class tecnbras_state : public driver_device
{
public:
	tecnbras_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_dmds(*this, "dmd_%u", 0U)
	{ }

	void tecnbras(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void set_x_position_w(offs_t offset, uint8_t data);
	void print_column_w(offs_t offset, uint8_t data);

	//void tecnbras_io_w(uint8_t data);
	//uint8_t tecnbras_io_r();
	void i80c31_io(address_map &map) ATTR_COLD;
	void i80c31_prg(address_map &map) ATTR_COLD;

	required_device<i80c31_device> m_maincpu;
	output_finder<14 * 7> m_dmds;

	int m_xcoord = 0;
	char m_digit[14][7]{};
};

void tecnbras_state::i80c31_prg(address_map &map)
{
	map(0x0000, 0x7FFF).rom();
	map(0x8000, 0xFFFF).ram();
}

#define DMD_OFFSET 24 //This is a guess. We should verify the real hardware behaviour
void tecnbras_state::i80c31_io(address_map &map)
{
	map(0x0100+DMD_OFFSET, 0x0145+DMD_OFFSET).w(FUNC(tecnbras_state::set_x_position_w));
	map(0x06B8, 0x06BC).w(FUNC(tecnbras_state::print_column_w));
}

void tecnbras_state::set_x_position_w(offs_t offset, uint8_t data)
{
	m_xcoord = offset;
}

void tecnbras_state::print_column_w(offs_t offset, uint8_t data)
{
	int const x = m_xcoord + offset;
	int const ch = x / 5;
	if (ch < std::size(m_digit)) {
		int const row = x % 5;
		for (int i = 0; i < 7; i++) {
			m_digit[ch][i] &= ~(1 << row);
			m_digit[ch][i] |= BIT(data, 7 - i) ? (1 << row) : 0;
			m_dmds[(ch * 7) + i] = 0x1F & m_digit[ch][i];
		}
	}
}

void tecnbras_state::machine_start()
{
	m_dmds.resolve();

	save_item(NAME(m_xcoord));
	save_item(NAME(m_digit));

	m_xcoord = 0;
	for (auto &elem : m_digit)
		std::fill(std::begin(elem), std::end(elem), 0);

#if 0
	for (int x = 0; x < std::size(m_digit); x++)
		for (int y = 0; y < 7; y++)
			m_dmds[(x * 7) + y] = y;
#endif
}

void tecnbras_state::machine_reset()
{
}

void tecnbras_state::tecnbras(machine_config &config)
{
	/* basic machine hardware */
	I80C31(config, m_maincpu, 12_MHz_XTAL); // verified on pcb
	m_maincpu->set_addrmap(AS_PROGRAM, &tecnbras_state::i80c31_prg);
	m_maincpu->set_addrmap(AS_IO, &tecnbras_state::i80c31_io);
	m_maincpu->port_out_cb<1>().set_nop(); // buzzer ?

/* TODO: Add an I2C RTC (Philips PCF8583P)
   pin 6 (SCL): cpu T0/P3.4 (pin 14)
   pin 5 (SDA): cpu T1/P3.5 (pin 15)
*/

/*
    TODO: Add a speaker
    CPU P1.0 (pin 1)
*/

/*
    TODO: Add a communications port to receive commands from the remote control
*/

	/* video hardware */
	config.set_default_layout(layout_tecnbras);
}

ROM_START( tecnbras )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD( "tecnbras.u2",  0x0000, 0x8000, CRC(1a1e18fc) SHA1(8907e72f0356a2e2e1097dabac6d6b0b3d717f85) )
ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT  CLASS           INIT        COMPANY     FULLNAME                            FLAGS
COMP( 200?, tecnbras, 0,      0,      tecnbras, 0,     tecnbras_state, empty_init, "Tecnbras", "Dot Matrix Display (70x7 pixels)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND )



tek405x.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/***************************************************************************

    Tektronix 4051/4052A

    Skeleton driver.

    http://www.electronixandmore.com/articles/teksystem.html

****************************************************************************/

/*

    TODO:

    - bank switch
    - keyboard
    - video (persistent vector display)
    - joystick
    - magnetic tape storage (3M 300)
    - communications backpack
    - 4051E01 ROM expander

*/


#include "emu.h"
#include "tek405x.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

enum {
	LBS_RBC = 0,    // ROM Bank Control
	LBS_BSOFL,      // Bank Switch Overflow (Select Overflow ROMs)
	LBS_BSCOM,      // Bank Switch Communication (Select Communication ROMs)
	LBS_INVALID,
	LBS_BS_L,       // Bank Switch Left (Select Left ROM Pack)
	LBS_BS_R,       // Bank Switch Right (Select Right ROM Pack)
	LBS_BSX_L,      // Bank Switch Expander (Select Left ROM Expander Unit)
	LBS_BSX_R       // Bank Switch Expander (Select Right ROM Expander Unit)
};


//**************************************************************************
//  INTERRUPTS
//**************************************************************************

void tek4051_state::update_irq()
{
	int state = m_kb_pia_irqa | m_kb_pia_irqb | m_x_pia_irqa | m_x_pia_irqb | m_gpib_pia_irqa | m_gpib_pia_irqb | m_com_pia_irqa | m_com_pia_irqb | m_acia_irq;

	m_maincpu->set_input_line(INPUT_LINE_IRQ0, state);
}

void tek4051_state::update_nmi()
{
	int state = m_y_pia_irqa | m_y_pia_irqb | m_tape_pia_irqa | m_tape_pia_irqb;

	m_maincpu->set_input_line(INPUT_LINE_NMI, state);
}


//**************************************************************************
//  KEYBOARD
//**************************************************************************

void tek4051_state::scan_keyboard()
{
}

TIMER_DEVICE_CALLBACK_MEMBER(tek4051_state::keyboard_tick)
{
	scan_keyboard();
}


//**************************************************************************
//  MEMORY BANKING
//**************************************************************************

void tek4051_state::bankswitch(uint8_t data)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	//int d = data & 0x07;
	int lbs = (data >> 3) & 0x07;

	switch (lbs)
	{
	case LBS_RBC:
		program.install_rom(0x8800, 0xa7ff, m_rom->base() + 0x800);
		break;

	case LBS_BSOFL:
		program.install_rom(0x8800, 0xa7ff, m_bsofl_rom->base());
		break;

	case LBS_BSCOM:
		program.install_rom(0x8800, 0xa7ff, m_bscom_rom->base());
		break;

	default:
		program.unmap_readwrite(0x8800, 0xa7ff);
	}
}

void tek4051_state::lbs_w(uint8_t data)
{
	/*

	    bit     description

	    0       ROM Expander Slot Address
	    1       ROM Expander Slot Address
	    2       ROM Expander Slot Address
	    3       Bank Switch
	    4       Bank Switch
	    5       Bank Switch
	    6
	    7

	*/

	logerror("LBS %02x\n", data);

	bankswitch(data);
}


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void tek4051_state::tek4051_mem(address_map &map)
{
	map(0x0000, 0x1fff).ram();
	map(0x2000, 0x7fff).ram(); // optional RAM
	map(0x8000, 0x877f).rom().region(MC6800_TAG, 0);
	map(0x878c, 0x878f).rw(MC6820_Y_TAG, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x8794, 0x8797).rw(MC6820_X_TAG, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x8798, 0x879b).rw(MC6820_TAPE_TAG, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x87a8, 0x87ab).rw(MC6820_KB_TAG, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x87b0, 0x87b3).rw(m_gpib_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x87c0, 0x87c0).mirror(0x03).w(FUNC(tek4051_state::lbs_w));
//  map(0x87c0, 0x87c3).rw(MC6820_COM_TAG, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
//  map(0x87c4, 0x87c5).mirror(0x02).rw(MC6850_TAG, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
//  map(0x87c8, 0x87cb) XPC2
//  map(0x87cc, 0x87cf) XPC3
//  map(0x87d0, 0x87d3) XPC4
	map(0x8800, 0xa7ff).rom().region(MC6800_TAG, 0x800);
	map(0xa800, 0xffff).rom().region(MC6800_TAG, 0x2800);
}

void tek4052_state::tek4052_mem(address_map &map)
{
}


//**************************************************************************
//  INPUT PORTS
//**************************************************************************

static INPUT_PORTS_START( tek4051 )
	PORT_START("Y0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y8")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y9")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y10")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y11")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y12")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y13")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y14")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y15")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("SPECIAL")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Left SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Right SHIFT") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("TTY LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
INPUT_PORTS_END


//**************************************************************************
//  VIDEO
//**************************************************************************

void tek4051_state::video_start()
{
}


void tek4052_state::video_start()
{
}


//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

uint8_t tek4051_state::x_pia_pa_r()
{
	/*

	    bit     description

	    PA0
	    PA1
	    PA2     DRBUSY-0
	    PA3     VPULSE-1
	    PA4
	    PA5
	    PA6
	    PA7

	*/

	return 0;
}

void tek4051_state::x_pia_pa_w(uint8_t data)
{
	/*

	    bit     description

	    PA0     X D/A
	    PA1     X D/A
	    PA2
	    PA3
	    PA4     ERASE-0
	    PA5     COPY-0
	    PA6     VECTOR-0
	    PA7     VEN-1

	*/
}

void tek4051_state::x_pia_pb_w(uint8_t data)
{
	/*

	    bit     description

	    PB0     X D/A
	    PB1     X D/A
	    PB2     X D/A
	    PB3     X D/A
	    PB4     X D/A
	    PB5     X D/A
	    PB6     X D/A
	    PB7     X D/A

	*/
}

void tek4051_state::adot_w(int state)
{
}

void tek4051_state::bufclk_w(int state)
{
}

void tek4051_state::x_pia_irqa_w(int state)
{
	m_x_pia_irqa = state;
	update_irq();
}

void tek4051_state::x_pia_irqb_w(int state)
{
	m_x_pia_irqb = state;
	update_irq();
}

uint8_t tek4051_state::sa_r()
{
	/*

	    bit     description

	    PA0     SA0
	    PA1     SA1
	    PA2     SA2
	    PA3     SA3
	    PA4     SA4
	    PA5     SA5
	    PA6     SA6
	    PA7     SA7

	*/

	return 0;
}

void tek4051_state::y_pia_pa_w(uint8_t data)
{
	/*

	    bit     description

	    PA0     Y D/A
	    PA1     Y D/A
	    PA2     Y CHAR D/A
	    PA3     Y CHAR D/A
	    PA4     Y CHAR D/A
	    PA5     X CHAR D/A
	    PA6     X CHAR D/A
	    PA7     X CHAR D/A

	*/
}

void tek4051_state::sb_w(uint8_t data)
{
	/*

	    bit     description

	    PB0     SB0
	    PB1     SB1
	    PB2     SB2
	    PB3     SB3
	    PB4     SB4
	    PB5     SB5
	    PB6     SB6
	    PB7     SB7

	*/
}

void tek4051_state::sot_w(int state)
{
}

void tek4051_state::y_pia_irqa_w(int state)
{
	m_y_pia_irqa = state;
	update_nmi();
}

void tek4051_state::y_pia_irqb_w(int state)
{
	m_y_pia_irqb = state;
	update_nmi();
}


uint8_t tek4051_state::kb_pia_pa_r()
{
	/*

	    bit     description

	    PA0     KC0-1
	    PA1     KC1-1
	    PA2     KC2-1
	    PA3     KC3-1
	    PA4     KC4-1
	    PA5     KC5-1
	    PA6     KC6-1
	    PA7     TTY-0

	*/

	uint8_t data = 0;
	uint8_t special = m_special->read();

	// keyboard column
	data = m_kc;

	// TTY lock
	data |= BIT(special, 3) << 7;

	return data;
}

uint8_t tek4051_state::kb_pia_pb_r()
{
	/*

	    bit     description

	    PB0     SHIFT-0
	    PB1     CTRL-0
	    PB2
	    PB3     LOAD-0
	    PB4
	    PB5
	    PB6
	    PB7

	*/

	uint8_t data = 0;
	uint8_t special = m_special->read();

	// shift
	data |= (BIT(special, 0) & BIT(special, 1));

	// ctrl
	data |= BIT(special, 2) << 1;

	return data;
}

void tek4051_state::kb_pia_pb_w(uint8_t data)
{
	/*

	    bit     description

	    PB0
	    PB1
	    PB2
	    PB3
	    PB4     EOI-1
	    PB5     BREAK-1
	    PB6     I/O-1
	    PB7     BUSY-1/REN-0/SPEAKER

	*/

	// lamps
	m_lamps[0] = BIT(~data, 5);
	m_lamps[1] = BIT(~data, 6);
	m_lamps[2] = BIT(~data, 7);

	// end or identify
	m_gpib->host_eoi_w(!BIT(data, 4));

	// speaker
	m_speaker->level_w(!BIT(data, 7));

	// remote enable
	m_gpib->host_ren_w(!BIT(data, 7));
}

void tek4051_state::kb_halt_w(int state)
{
}

void tek4051_state::kb_pia_irqa_w(int state)
{
	m_kb_pia_irqa = state;
	update_irq();
}

void tek4051_state::kb_pia_irqb_w(int state)
{
	m_kb_pia_irqb = state;
	update_irq();
}


uint8_t tek4051_state::tape_pia_pa_r()
{
	/*

	    bit     description

	    PA0     DELAY OUT-1
	    PA1
	    PA2     TUTS-1
	    PA3     SAFE-1
	    PA4     SAFE-1
	    PA5     JOYSTICK
	    PA6     FILFND-1
	    PA7     JOYSTICK

	*/

	return 0;
}

void tek4051_state::tape_pia_pa_w(uint8_t data)
{
	/*

	    bit     description

	    PA0
	    PA1     LDCLK-1
	    PA2
	    PA3
	    PA4
	    PA5     XERR-1
	    PA6
	    PA7     YERR-1

	*/
}

void tek4051_state::tape_pia_pb_w(uint8_t data)
{
	/*

	    bit     description

	    PB0     WR-0
	    PB1     FAST-1
	    PB2     REV-1
	    PB3     DRTAPE-0
	    PB4     GICG-1
	    PB5     FICG-1
	    PB6     WENABLE-1
	    PB7     FSENABLE-0

	*/
}

void tek4051_state::tape_pia_irqa_w(int state)
{
	m_tape_pia_irqa = state;
	update_nmi();
}

void tek4051_state::tape_pia_irqb_w(int state)
{
	m_tape_pia_irqb = state;
	update_nmi();
}

void tek4051_state::dio_w(uint8_t data)
{
	/*

	    bit     description

	    PA0     DIO1-1
	    PA1     DIO2-1
	    PA2     DIO3-1
	    PA3     DIO4-1
	    PA4     DIO5-1
	    PA5     DIO6-1
	    PA6     DIO7-1
	    PA7     DIO8-1

	*/

	if (m_talk)
	{
		m_gpib->host_dio_w(data);
	}
}

uint8_t tek4051_state::gpib_pia_pb_r()
{
	/*

	    bit     description

	    PB0
	    PB1
	    PB2
	    PB3
	    PB4     NRFD
	    PB5     SRQ-1
	    PB6     DAV-1
	    PB7     NDAC

	*/

	uint8_t data = 0;

	// service request
	data |= m_gpib->srq_r() << 5;

	// data valid
	data |= m_gpib->dav_r() << 6;

	if (!m_talk)
	{
		// not ready for data
		data |= m_gpib->nrfd_r() << 4;

		// not data acknowledged
		data |= m_gpib->ndac_r() << 7;
	}

	return data;
}

void tek4051_state::gpib_pia_pb_w(uint8_t data)
{
	/*

	    bit     description

	    PB0     EOI-1
	    PB1     IFC-1
	    PB2
	    PB3     ATN-0
	    PB4     NRFD
	    PB5
	    PB6
	    PB7     NDAC

	*/

	// end or identify
	m_gpib->host_eoi_w(!BIT(data, 0));

	// interface clear
	m_gpib->host_ifc_w(!BIT(data, 1));

	// attention
	m_gpib->host_atn_w(BIT(data, 3));

	if (m_talk)
	{
		// not ready for data
		m_gpib->host_nrfd_w(!BIT(data, 4));

		// not data acknowledged
		m_gpib->host_ndac_w(!BIT(data, 7));
	}
}

void tek4051_state::talk_w(int state)
{
	m_talk = state;

	if (!m_talk)
	{
		m_gpib->host_dio_w(0xff);
		m_gpib->host_nrfd_w(1);
		m_gpib->host_ndac_w(1);
	}
}

void tek4051_state::gpib_pia_irqa_w(int state)
{
	m_gpib_pia_irqa = state;
	update_irq();
}

void tek4051_state::gpib_pia_irqb_w(int state)
{
	m_gpib_pia_irqb = state;
	update_irq();
}

void tek4051_state::com_pia_pa_w(uint8_t data)
{
	/*

	    bit     description

	    PA0
	    PA1
	    PA2
	    PA3     Bank Switch Register
	    PA4     Bank Switch Register
	    PA5     Bank Switch Register
	    PA6
	    PA7

	*/

	bankswitch(data);
}

uint8_t tek4051_state::com_pia_pb_r()
{
	/*

	    bit     description

	    PB0     SRX
	    PB1     DTR
	    PB2     RTS
	    PB3
	    PB4
	    PB5
	    PB6
	    PB7

	*/

	uint8_t data = 0;

	// data terminal ready

	// request to send

	return data;
}

void tek4051_state::com_pia_pb_w(uint8_t data)
{
	/*

	    bit     description

	    PB0
	    PB1
	    PB2
	    PB3     CTS
	    PB4     STXA-C
	    PB5     Baud Rate Select
	    PB6     Baud Rate Select
	    PB7     Baud Rate Select

	*/

	// clear to send

	// baud rate select
	int osc = BIT(data, 7) ? 28160 : 38400;
	int div = 1;

	switch ((data >> 5) & 0x03)
	{
	case 2: div = 4; break;
	case 3: div = 2; break;
	}

	m_acia_clock->set_unscaled_clock(osc);
	m_acia_clock->set_clock_scale((double) 1 / div);
}

void tek4051_state::com_pia_irqa_w(int state)
{
	m_com_pia_irqa = state;
	update_irq();
}

void tek4051_state::com_pia_irqb_w(int state)
{
	m_com_pia_irqb = state;
	update_irq();
}

void tek4051_state::acia_irq_w(int state)
{
	m_acia_irq = state;
	update_irq();
}

void tek4051_state::write_acia_clock(int state)
{
	m_acia->write_txc(state);
	m_acia->write_rxc(state);
}


//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

void tek4051_state::machine_start()
{
	m_lamps.resolve();

	address_space &program = m_maincpu->space(AS_PROGRAM);

	// configure RAM
	switch (m_ram->size())
	{
	case 8*1024:
		program.unmap_readwrite(0x2000, 0x7fff);
		break;

	case 16*1024:
		program.unmap_readwrite(0x4000, 0x7fff);
		break;

	case 24*1024:
		program.unmap_readwrite(0x6000, 0x7fff);
		break;
	}

	m_x_pia_irqa = 0;
	m_x_pia_irqb = 0;
	m_y_pia_irqa = 0;
	m_y_pia_irqb = 0;
	m_tape_pia_irqa = 0;
	m_tape_pia_irqb = 0;
	m_kb_pia_irqa = 0;
	m_kb_pia_irqb = 0;
	m_gpib_pia_irqa = 0;
	m_gpib_pia_irqb = 0;
	m_com_pia_irqa = 0;
	m_com_pia_irqb = 0;
	m_acia_irq = 0;

	// register for state saving
}

void tek4052_state::machine_start()
{
}


//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

void tek4051_state::tek4051(machine_config &config)
{
	// basic machine hardware
	M6800(config, m_maincpu, XTAL(12'500'000)/15);
	m_maincpu->set_addrmap(AS_PROGRAM, &tek4051_state::tek4051_mem);

	// video hardware
	VECTOR(config, "vector", 0);
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_VECTOR));
	screen.set_color(rgb_t::green());
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // not accurate
	screen.set_size(1024, 780);
	screen.set_visarea(0, 1024-1, 0, 780-1);
	screen.set_screen_update("vector", FUNC(vector_device::screen_update));

	PALETTE(config, "palette", palette_device::MONOCHROME);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);

	// devices
	TIMER(config, "keyboard").configure_periodic(FUNC(tek4051_state::keyboard_tick), attotime::from_hz(XTAL(12'500'000)/15/4));

	pia6821_device &piax(PIA6821(config, MC6820_X_TAG));
	piax.readpa_handler().set(FUNC(tek4051_state::x_pia_pa_r));
	// CB1 viewcause
	piax.writepa_handler().set(FUNC(tek4051_state::x_pia_pa_w));
	piax.writepb_handler().set(FUNC(tek4051_state::x_pia_pb_w));
	piax.ca2_handler().set(FUNC(tek4051_state::adot_w));
	piax.cb2_handler().set(FUNC(tek4051_state::bufclk_w));
	piax.irqa_handler().set(FUNC(tek4051_state::x_pia_irqa_w));
	piax.irqb_handler().set(FUNC(tek4051_state::x_pia_irqb_w));

	pia6821_device &piay(PIA6821(config, MC6820_Y_TAG));
	piay.readpa_handler().set(FUNC(tek4051_state::sa_r));
	// CA1 rdbyte
	// CB1 mdata
	// CB2 fmark
	piay.writepa_handler().set(FUNC(tek4051_state::y_pia_pa_w));
	piay.writepb_handler().set(FUNC(tek4051_state::sb_w));
	piay.ca2_handler().set(FUNC(tek4051_state::sot_w));
	piay.irqa_handler().set(FUNC(tek4051_state::y_pia_irqa_w));
	piay.irqb_handler().set(FUNC(tek4051_state::y_pia_irqb_w));

	pia6821_device &piakbd(PIA6821(config, MC6820_KB_TAG));
	piakbd.readpa_handler().set(FUNC(tek4051_state::kb_pia_pa_r));
	piakbd.readpb_handler().set(FUNC(tek4051_state::kb_pia_pb_r));
	// CA1 key
	piakbd.writepb_handler().set(FUNC(tek4051_state::kb_pia_pb_w));
	piakbd.ca2_handler().set(FUNC(tek4051_state::kb_halt_w));
	piakbd.irqa_handler().set(FUNC(tek4051_state::kb_pia_irqa_w));
	piakbd.irqb_handler().set(FUNC(tek4051_state::kb_pia_irqb_w));

	pia6821_device &piatape(PIA6821(config, MC6820_TAPE_TAG));
	piatape.readpa_handler().set(FUNC(tek4051_state::tape_pia_pa_r));
	// CA1 rmark
	// CB1 lohole
	// CA2 filfnd
	// CB2 uphole
	piatape.writepa_handler().set(FUNC(tek4051_state::tape_pia_pa_w));
	piatape.writepb_handler().set(FUNC(tek4051_state::tape_pia_pb_w));
	piatape.irqa_handler().set(FUNC(tek4051_state::tape_pia_irqa_w));
	piatape.irqb_handler().set(FUNC(tek4051_state::tape_pia_irqb_w));

	PIA6821(config, m_gpib_pia);
	m_gpib_pia->readpa_handler().set(IEEE488_TAG, FUNC(ieee488_device::dio_r));
	m_gpib_pia->readpb_handler().set(FUNC(tek4051_state::gpib_pia_pb_r));
	m_gpib_pia->writepa_handler().set(FUNC(tek4051_state::dio_w));
	m_gpib_pia->writepb_handler().set(FUNC(tek4051_state::gpib_pia_pb_w));
	m_gpib_pia->cb2_handler().set(FUNC(tek4051_state::talk_w));
	m_gpib_pia->irqa_handler().set(FUNC(tek4051_state::gpib_pia_irqa_w));
	m_gpib_pia->irqb_handler().set(FUNC(tek4051_state::gpib_pia_irqb_w));

	PIA6821(config, m_com_pia);
	m_com_pia->readpb_handler().set(FUNC(tek4051_state::com_pia_pb_r));
	//CA1 - SRX (RS-232 pin 12)
	m_com_pia->writepa_handler().set(FUNC(tek4051_state::com_pia_pa_w));
	m_com_pia->writepb_handler().set(FUNC(tek4051_state::com_pia_pb_w));
	m_com_pia->irqa_handler().set(FUNC(tek4051_state::com_pia_irqa_w));
	m_com_pia->irqb_handler().set(FUNC(tek4051_state::com_pia_irqb_w));

	ACIA6850(config, m_acia, 0);
	m_acia->irq_handler().set(FUNC(tek4051_state::acia_irq_w));

	CLOCK(config, m_acia_clock, 38400);
	m_acia_clock->signal_handler().set(FUNC(tek4051_state::write_acia_clock));

	IEEE488(config, m_gpib);
	m_gpib->eoi_callback().set(MC6820_GPIB_TAG, FUNC(pia6821_device::ca1_w));
	m_gpib->srq_callback().set(MC6820_GPIB_TAG, FUNC(pia6821_device::cb1_w));

	// internal ram
	RAM(config, RAM_TAG).set_default_size("8K").set_extra_options("16K,24K,32K");

	// cartridge
	GENERIC_CARTSLOT(config, "cartslot1", generic_plain_slot, "tek4050_cart");
	GENERIC_CARTSLOT(config, "cartslot2", generic_plain_slot, "tek4050_cart");
}

void tek4052_state::tek4052(machine_config &config)
{
	// basic machine hardware
	m6800_cpu_device &cpu(M6800(config, AM2901A_TAG, 1000000)); // should be 4x AM2901A + AM2911
	cpu.set_addrmap(AS_PROGRAM, &tek4052_state::tek4052_mem);

	// video hardware
	VECTOR(config, "vector", 0);
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_VECTOR));
	screen.set_color(rgb_t::green());
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // not accurate
	screen.set_size(1024, 780);
	screen.set_visarea(0, 1024-1, 0, 780-1);
	screen.set_screen_update("vector", FUNC(vector_device::screen_update));

	PALETTE(config, "palette", palette_device::MONOCHROME);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.25);

	// internal ram
	RAM(config, RAM_TAG).set_default_size("32K").set_extra_options("64K");

	// cartridge
	GENERIC_CARTSLOT(config, "cartslot1", generic_plain_slot, "tek4050_cart");
	GENERIC_CARTSLOT(config, "cartslot2", generic_plain_slot, "tek4050_cart");

	// software lists
	SOFTWARE_LIST(config, "cart_list").set_original("tek4052_cart");
}

/*
void tek4054_state::tek4054(machine_config &config)
{
    // screen size: 4096, 3125
}
*/



//**************************************************************************
//  ROMS
//**************************************************************************

ROM_START( tek4051 )
	ROM_REGION( 0x8000, MC6800_TAG, 0 )
	ROM_LOAD( "156-0659-xx.u585", 0x0000, 0x0800, CRC(0017ba54) SHA1(533bfacb2e698c1df88a00acce6df6a8c536239d) ) // -01 or -02 ?
	ROM_LOAD( "156-0660-01.u581", 0x0800, 0x0800, CRC(c4e302e8) SHA1(a0438cbc70ddc1bab000e30c0f835f0948aae969) )
	ROM_LOAD( "156-0661-01.u487", 0x1000, 0x0800, CRC(edb097c8) SHA1(a07b777b6b20de496089730db8097b65b9a8ef31) )
	ROM_LOAD( "156-0662-01.u485", 0x1800, 0x0800, CRC(0a3af548) SHA1(01bcffa7d55b2585348ea96852b4319c6454d19a) )
	ROM_LOAD( "156-0663-01.u481", 0x2000, 0x0800, CRC(f220d24c) SHA1(fb5dc8a617f4b4d5c094c0cbabb9af158c19c2c2) )
	ROM_LOAD( "156-0664-xx.u385", 0x2800, 0x0800, CRC(aa1ae67d) SHA1(8816654c1b0ad0230bf5027f68c2ec1d315ad188) ) // -01 or -02 ?
	ROM_LOAD( "156-0665-xx.u381", 0x3000, 0x0800, CRC(90497138) SHA1(2d6bbe169a434581a49fffcab0d2ca18107b67da) ) // -01 or -02 ?
	ROM_LOAD( "156-0666-01.u285", 0x3800, 0x0800, CRC(c35ce405) SHA1(10cae122d000fe2886228a178a1a4e4d3ec2c3f5) )
	ROM_LOAD( "156-0667-xx.u595", 0x4000, 0x0800, CRC(e1723a06) SHA1(dde3e64eb6acc77d8e7234235a99ec338cc74717) ) // -01 or -02 ?
	ROM_LOAD( "156-0668-xx.u591", 0x4800, 0x0800, CRC(d8b80d7a) SHA1(6c1f00fa2f9ff6ebd84268c2100637ddbdb3bae6) ) // -01 or -02 ?
	ROM_LOAD( "156-0669-01.u497", 0x5000, 0x0800, CRC(f760ed39) SHA1(2b30d20d880002ce3950ee7abe40f4e1539208cc) )
	ROM_LOAD( "156-0670-01.u495", 0x5800, 0x0800, CRC(d40a303d) SHA1(111b399f6178e2fe50b1368536f290d4df9883fc) )
	ROM_LOAD( "156-0671-xx.u491", 0x6000, 0x0800, CRC(32060b64) SHA1(af254dc068686fcd7584128258724fd5415ea458) ) // -01 or -02 ?
	ROM_LOAD( "156-0672-xx.u395", 0x6800, 0x0800, CRC(93ad68d1) SHA1(8242058bfc89d11cd9ae82d967410714875eface) ) // -01 or -02 ?
	ROM_LOAD( "156-0673-xx.u391", 0x7000, 0x0800, CRC(ee1f5d4c) SHA1(a6fb8347a0dfa94268f091fb7ca091c8bfabeabd) ) // -01 or -02 or -03 ?
	ROM_LOAD( "156-0674-xx.u295", 0x7800, 0x0800, CRC(50582341) SHA1(1a028176e5d5ca83012e2f75e9daad88cd7d8fc3) ) // -01 or -02 ?

	ROM_REGION( 0x2000, "020_0147_00", 0 ) // Firmware Backpack (020-0147-00)
	ROM_LOAD( "156-0747-xx.u101", 0x0000, 0x0800, CRC(9e1facc1) SHA1(7e7a118c3e8c49630f630ee02c3de843dd95d7e1) ) // -00 or -01 ?
	ROM_LOAD( "156-0748-xx.u201", 0x0800, 0x0800, CRC(be42bfbf) SHA1(23575b411bd9dcb7d7116628820096e3064ff93b) ) // -00 or -01 ?

	ROM_REGION( 0x2000, "021_0188_00", 0 ) // Communications Backpack (021-0188-00)
	ROM_LOAD( "156-0712-00.u101", 0x0000, 0x0800, NO_DUMP )
	ROM_LOAD( "156-0712-01.u101", 0x0000, 0x0800, NO_DUMP )
	ROM_LOAD( "156-0713-00.u111", 0x0800, 0x0800, NO_DUMP )
	ROM_LOAD( "156-0713-01.u111", 0x0800, 0x0800, NO_DUMP )
	ROM_LOAD( "156-0714-00.u121", 0x1000, 0x0800, NO_DUMP )
	ROM_LOAD( "156-0714-01.u121", 0x1000, 0x0800, NO_DUMP )
	ROM_LOAD( "156-0715-01.u131", 0x1800, 0x0800, NO_DUMP )
/*
    ROM_REGION( 0x2000, "4051r01", 0 ) // 4051R01 Matrix Functions
    ROM_LOAD( "4051r01", 0x0000, 0x1000, NO_DUMP )

    ROM_REGION( 0x2000, "4051r05", 0 ) // 4051R05 Binary Program Loader
    ROM_LOAD( "156-0856-00.u1",  0x0000, 0x0800, NO_DUMP )
    ROM_LOAD( "156-0857-00.u11", 0x0800, 0x0800, NO_DUMP )

    ROM_REGION( 0x2000, "4051r06", 0 ) // 4051R06 Editor
    ROM_LOAD( "4051r06", 0x0000, 0x1000, NO_DUMP )
*/
ROM_END

ROM_START( tek4052a )
	ROM_REGION( 0x3800, AM2901A_TAG, 0 ) // ALU 670-7705-00 microcode
	ROM_LOAD( "160-1689-00.u340", 0x0000, 0x0800, CRC(97ff62d4) SHA1(e25b495fd1b3f8a5bfef5c8f20efacde8366e89c) )
	ROM_LOAD( "160-1688-00.u335", 0x0800, 0x0800, CRC(19033422) SHA1(0f6ea45be5123701940331c4278bcdc5db4f4147) )
	ROM_LOAD( "160-1687-00.u330", 0x1000, 0x0800, CRC(3b2e37dd) SHA1(294b626d0022fbf9bec50680b6b932f21a3cb049) )
	ROM_LOAD( "160-1686-00.u320", 0x1800, 0x0800, CRC(7916cd64) SHA1(d6b3ed783e488667ca203ec605aa27d78261c61b) )
	ROM_LOAD( "160-1695-00.u315", 0x2000, 0x0800, CRC(cdac76a7) SHA1(2a47f34ae63fb7fe96e7c6fc92ca4f629b0e31ec) )
	ROM_LOAD( "160-1694-00.u305", 0x2800, 0x0800, CRC(c33ae212) SHA1(25ed4cc3600391fbc93bb46c92d3f64ca2aca58e) )
	ROM_LOAD( "160-1693-00.u300", 0x3000, 0x0800, CRC(651b7af2) SHA1(7255c682cf74f2e77661d77c8406b36a567eea46) )

	ROM_REGION( 0x14000, "672_0799_08", 0 ) // Memory Access Sequencer 672-0799-08
	ROM_LOAD16_BYTE( "160-1698-00.u810", 0x00000, 0x2000, CRC(fd0b8bc3) SHA1(ea9caa151295024267a467ba636ad41aa0c517d3) )
	ROM_LOAD16_BYTE( "160-1682-00.u893", 0x00001, 0x2000, CRC(d54104ef) SHA1(b796c320c8f96f6e4b845718bfb62ccb5903f2db) )
	ROM_LOAD16_BYTE( "160-1699-00.u820", 0x04000, 0x2000, CRC(59cc6c2e) SHA1(b6d85cdfaaef360af2a4fc44eae55e512bb08b21) )
	ROM_LOAD16_BYTE( "160-1684-00.u870", 0x04001, 0x2000, CRC(b24cdbaf) SHA1(476032624110bc5c7a7f30d10458e8baffb2f2ff) )
	ROM_LOAD16_BYTE( "160-1700-00.u825", 0x08000, 0x2000, CRC(59f4a4e2) SHA1(81b5f74ec14bb13735382abc123bb9b09dfb7a63) )
	ROM_LOAD16_BYTE( "160-1683-00.u880", 0x08001, 0x2000, CRC(f6bb15d1) SHA1(eca06211876c12236cfeae0417131e8f04728d51) )
	ROM_LOAD16_BYTE( "160-1701-00.u835", 0x0c000, 0x2000, CRC(195988b9) SHA1(995a77963e869f1a461addf58db9ecbf2b9222ba) )
	ROM_LOAD16_BYTE( "160-1691-00.u885", 0x0c001, 0x2000, CRC(0670bf9e) SHA1(a8c39bd7f3f61436296c6bc5f734354371ae8123) )
	ROM_LOAD16_BYTE( "160-1702-00.u845", 0x10000, 0x2000, CRC(013344b1) SHA1(4a79654427e15d0fcedd9519914f6448938ecffd) )
	ROM_LOAD16_BYTE( "160-1685-00.u863", 0x10001, 0x2000, CRC(53ddc8f9) SHA1(431d6f329dedebb54232c623a924d5ecddc5e44e) )

	ROM_REGION( 0x2000, "020_0147_00", 0 ) // Firmware Backpack (020-0147-00)
	ROM_LOAD( "156-0747-xx.u101", 0x0000, 0x0800, CRC(9e1facc1) SHA1(7e7a118c3e8c49630f630ee02c3de843dd95d7e1) ) // -00 or -01 ?
	ROM_LOAD( "156-0748-xx.u201", 0x0800, 0x0800, CRC(be42bfbf) SHA1(23575b411bd9dcb7d7116628820096e3064ff93b) ) // -00 or -01 ?

	ROM_REGION( 0x2000, "021_0188_00", 0 ) // Communications Backpack (021-0188-00)
	ROM_LOAD( "156-0712-00.u101", 0x0000, 0x0800, NO_DUMP )
	ROM_LOAD( "156-0712-01.u101", 0x0000, 0x0800, NO_DUMP )
	ROM_LOAD( "156-0713-00.u111", 0x0800, 0x0800, NO_DUMP )
	ROM_LOAD( "156-0713-01.u111", 0x0800, 0x0800, NO_DUMP )
	ROM_LOAD( "156-0714-00.u121", 0x1000, 0x0800, NO_DUMP )
	ROM_LOAD( "156-0714-01.u121", 0x1000, 0x0800, NO_DUMP )
	ROM_LOAD( "156-0715-01.u131", 0x1800, 0x0800, NO_DUMP )
ROM_END



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME       PARENT   COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY      FULLNAME           FLAGS
COMP( 1975, tek4051,   0,       0,      tek4051, tek4051, tek4051_state, empty_init, "Tektronix", "Tektronix 4051",  MACHINE_NOT_WORKING )
COMP( 1978, tek4052a,  tek4051, 0,      tek4052, tek4051, tek4052_state, empty_init, "Tektronix", "Tektronix 4052A", MACHINE_NOT_WORKING )
//COMP( 1979, tek4054,   tek4051, 0,      tek4054, tek4054, tek4052_state, empty_init, "Tektronix", "Tektronix 4054",  MACHINE_NOT_WORKING )



tek410x.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder, AJR
/***************************************************************************

    Tektronix 4107A/4109A

    Skeleton driver.

****************************************************************************/

#include "emu.h"
#include "cpu/i86/i186.h"
#include "machine/i8255.h"
#include "machine/mc68681.h"
#include "tek410x_kbd.h"
#include "video/crt9007.h"
#include "emupal.h"
#include "screen.h"


namespace {

class tek4107a_state : public driver_device
{
public:
	tek4107a_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_duart(*this, "duart%u", 0U)
		, m_keyboard(*this, "keyboard")
		, m_vpac(*this, "vpac")
		, m_ppi_pc(0)
		, m_kb_rdata(true)
		, m_kb_tdata(true)
		, m_kb_rclamp(false)
		, m_graphics_control(0)
		, m_alpha_control(0)
		, m_x_position(0)
		, m_y_position(0)
		, m_x_cursor(0)
		, m_y_cursor(0)
	{ }

	void tek4109a(machine_config &config);
	void tek4107a(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	u8 vpac_r(offs_t offset);

	u16 nmi_enable_r();
	u16 nmi_disable_r();
	u16 system_reset_r();

	void ppi_pc_w(u8 data);
	void kb_rdata_w(int state);
	void kb_tdata_w(int state);
	void kb_rclamp_w(int state);

	void xpos_w(u16 data);
	void ypos_w(u16 data);
	void xcur_w(u16 data);
	void ycur_w(u16 data);
	void tbwin_w(u16 data);
	u16 gcntl_r();
	void gcntl_w(u16 data);
	u16 acntl_r();
	void acntl_w(u16 data);
	u16 test_r();
	u8 font_r();

	void tek4107a_io(address_map &map) ATTR_COLD;
	void tek4107a_mem(address_map &map) ATTR_COLD;

	required_device_array<scn2681_device, 2> m_duart;
	required_device<tek410x_keyboard_device> m_keyboard;
	required_device<crt9007_device> m_vpac;

	u8 m_ppi_pc;
	bool m_kb_rdata;
	bool m_kb_tdata;
	bool m_kb_rclamp;

	u16 m_graphics_control;
	u16 m_alpha_control;
	u16 m_x_position;
	u16 m_y_position;
	u16 m_x_cursor;
	u16 m_y_cursor;
};

u8 tek4107a_state::vpac_r(offs_t offset)
{
	return m_vpac->read(offset + 0x20);
}

u16 tek4107a_state::nmi_enable_r()
{
	// TODO
	return 0;
}

u16 tek4107a_state::nmi_disable_r()
{
	// TODO
	return 0;
}

u16 tek4107a_state::system_reset_r()
{
	// TODO
	return 0;
}

void tek4107a_state::ppi_pc_w(u8 data)
{
	if (!m_kb_rclamp && BIT(m_ppi_pc, 2) != BIT(data, 2))
		m_keyboard->kdo_w(!BIT(data, 2) || m_kb_tdata);

	m_ppi_pc = data;
}

void tek4107a_state::kb_rdata_w(int state)
{
	m_kb_rdata = state;
	if (!m_kb_rclamp)
		m_duart[0]->rx_a_w(state);
}

void tek4107a_state::kb_rclamp_w(int state)
{
	if (m_kb_rclamp != !state)
	{
		m_kb_rclamp = !state;

		// Clamp RXDA to 1 and KBRDATA to 0 when DUART asserts RxRDYA
		if (m_kb_tdata || !BIT(m_ppi_pc, 2))
			m_keyboard->kdo_w(state);
		m_duart[0]->rx_a_w(state ? m_kb_rdata : 1);
	}
}

void tek4107a_state::kb_tdata_w(int state)
{
	if (m_kb_tdata != state)
	{
		m_kb_tdata = state;

		m_duart[0]->ip4_w(!state);
		if (BIT(m_ppi_pc, 2) && m_kb_rdata && !m_kb_rclamp)
			m_keyboard->kdo_w(state);
	}
}

void tek4107a_state::xpos_w(u16 data)
{
	m_x_position = data & 0x03ff;
}

void tek4107a_state::ypos_w(u16 data)
{
	m_y_position = data & 0x01ff;
}

void tek4107a_state::xcur_w(u16 data)
{
	m_x_cursor = data & 0x03ff;
}

void tek4107a_state::ycur_w(u16 data)
{
	m_y_cursor = data & 0x01ff;
}

void tek4107a_state::tbwin_w(u16 data)
{
	// TODO
}

u16 tek4107a_state::gcntl_r()
{
	return m_graphics_control;
}

void tek4107a_state::gcntl_w(u16 data)
{
	m_graphics_control = data;
}

u16 tek4107a_state::acntl_r()
{
	return m_alpha_control;
}

void tek4107a_state::acntl_w(u16 data)
{
	m_alpha_control = data;
}

u16 tek4107a_state::test_r()
{
	// TODO
	return 0;
}

u8 tek4107a_state::font_r()
{
	// TODO
	return 0;
}


/* Memory Maps */

void tek4107a_state::tek4107a_mem(address_map &map)
{
	map(0x00000, 0x3ffff).ram();
	map(0x40000, 0x7ffff).ram().share("gfxram");
	map(0x80000, 0xbffff).rom().region("firmware", 0);
	map(0xf0000, 0xfffff).rom().region("firmware", 0x30000);
}

void tek4107a_state::tek4107a_io(address_map &map)
{
	map(0x0000, 0x001f).rw(m_duart[0], FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask16(0x00ff);
	map(0x0000, 0x001f).rw(m_duart[1], FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask16(0xff00);
	map(0x0080, 0x00bf).r(FUNC(tek4107a_state::vpac_r)).w(m_vpac, FUNC(crt9007_device::write)).umask16(0x00ff);
	map(0x00c0, 0x00c1).w(FUNC(tek4107a_state::xpos_w));
	map(0x00c2, 0x00c3).w(FUNC(tek4107a_state::ypos_w));
	map(0x00c4, 0x00c5).w(FUNC(tek4107a_state::xcur_w));
	map(0x00c6, 0x00c7).w(FUNC(tek4107a_state::ycur_w));
	map(0x00c8, 0x00c9).r(FUNC(tek4107a_state::test_r));
	map(0x00ca, 0x00ca).r(FUNC(tek4107a_state::font_r));
	map(0x00ca, 0x00cb).w(FUNC(tek4107a_state::tbwin_w));
	map(0x00cc, 0x00cd).rw(FUNC(tek4107a_state::gcntl_r), FUNC(tek4107a_state::gcntl_w));
	map(0x00ce, 0x00cf).rw(FUNC(tek4107a_state::acntl_r), FUNC(tek4107a_state::acntl_w));
	map(0x0100, 0x0107).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0xff00);
	map(0x0200, 0x0201).r(FUNC(tek4107a_state::nmi_enable_r));
	map(0x0280, 0x0281).r(FUNC(tek4107a_state::system_reset_r));
	map(0x0300, 0x0301).r(FUNC(tek4107a_state::nmi_disable_r));
}

/* Input Ports */

static INPUT_PORTS_START( tek4107a )
INPUT_PORTS_END

/* Video */

void tek4107a_state::video_start()
{
	save_item(NAME(m_graphics_control));
	save_item(NAME(m_alpha_control));
	save_item(NAME(m_x_position));
	save_item(NAME(m_y_position));
	save_item(NAME(m_x_cursor));
	save_item(NAME(m_y_cursor));
}

u32 tek4107a_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

static const gfx_layout tek4107a_charlayout =
{
	8, 15,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP8(0,1) },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8 },
	16*8
};

static GFXDECODE_START( gfx_tek4107a )
	GFXDECODE_ENTRY( "chargen", 0x0000, tek4107a_charlayout, 0, 1 )
GFXDECODE_END

/* Machine Initialization */

void tek4107a_state::machine_start()
{
	save_item(NAME(m_ppi_pc));
	save_item(NAME(m_kb_rdata));
	save_item(NAME(m_kb_tdata));
	save_item(NAME(m_kb_rclamp));
}

/* Machine Driver */

void tek4107a_state::tek4107a(machine_config &config)
{
	/* basic machine hardware */
	i80186_cpu_device &maincpu(I80186(config, "maincpu", 14.7456_MHz_XTAL));
	maincpu.set_addrmap(AS_PROGRAM, &tek4107a_state::tek4107a_mem);
	maincpu.set_addrmap(AS_IO, &tek4107a_state::tek4107a_io);

	SCN2681(config, m_duart[0], 14.7456_MHz_XTAL / 4);
	m_duart[0]->irq_cb().set("maincpu", FUNC(i80186_cpu_device::int0_w));
	m_duart[0]->outport_cb().set_inputline("maincpu", INPUT_LINE_NMI).bit(5).invert(); // RxRDYB
	m_duart[0]->outport_cb().append(FUNC(tek4107a_state::kb_rclamp_w)).bit(4);
	m_duart[0]->outport_cb().append(m_keyboard, FUNC(tek410x_keyboard_device::reset_w)).bit(3);
	m_duart[0]->a_tx_cb().set(m_keyboard, FUNC(tek410x_keyboard_device::kdi_w));

	SCN2681(config, m_duart[1], 14.7456_MHz_XTAL / 4);
	m_duart[1]->irq_cb().set("maincpu", FUNC(i80186_cpu_device::int2_w));

	i8255_device &ppi(I8255(config, "ppi"));
	ppi.in_pb_callback().set_constant(0x30);
	ppi.out_pc_callback().set(FUNC(tek4107a_state::ppi_pc_w));

	TEK410X_KEYBOARD(config, m_keyboard);
	m_keyboard->tdata_callback().set(FUNC(tek4107a_state::kb_tdata_w));
	m_keyboard->rdata_callback().set(FUNC(tek4107a_state::kb_rdata_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(25.2_MHz_XTAL, 800, 0, 640, 525, 0, 480);
	screen.set_screen_update(FUNC(tek4107a_state::screen_update));

	CRT9007(config, m_vpac, 25.2_MHz_XTAL / 8);
	m_vpac->set_screen("screen");
	m_vpac->set_character_width(8);
	m_vpac->int_callback().set("maincpu", FUNC(i80186_cpu_device::int1_w));

	PALETTE(config, "palette").set_entries(64);
	GFXDECODE(config, "gfxdecode", "palette", gfx_tek4107a);
}

void tek4107a_state::tek4109a(machine_config &config)
{
	tek4107a(config);

	/* video hardware */
	subdevice<palette_device>("palette")->set_entries(4096);
}

/* ROMs */

ROM_START( tek4107a )
	ROM_REGION16_LE( 0x40000, "firmware", 0 )
	ROM_LOAD16_BYTE( "160-2379-03.u60",  0x00000, 0x8000, NO_DUMP )
	ROM_LOAD16_BYTE( "160-2380-03.u160", 0x00001, 0x8000, NO_DUMP )
	ROM_LOAD16_BYTE( "160-2377-03.u70",  0x10000, 0x8000, NO_DUMP )
	ROM_LOAD16_BYTE( "160-2378-03.u170", 0x10001, 0x8000, CRC(feac272f) SHA1(f2018dc9bb5bd6840b2843c709cbc24689054dc0) )
	ROM_LOAD16_BYTE( "160-2375-03.u80",  0x20000, 0x8000, NO_DUMP )
	ROM_LOAD16_BYTE( "160-2376-03.u180", 0x20001, 0x8000, CRC(35a44e75) SHA1(278c7f218105ad3473409b16a8d27a0b9f7a859e) )
	ROM_LOAD16_BYTE( "160-2373-03.u90",  0x30000, 0x8000, CRC(d5e89b15) SHA1(d6eb1a0a684348a8194238d641528fb96fb29087) )
	ROM_LOAD16_BYTE( "160-2374-03.u190", 0x30001, 0x8000, CRC(a3fef76e) SHA1(902845c4aa0cbc392b62c726ac746ca62567d91c) )

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "160-2381 v1.0.u855", 0x0000, 0x1000, CRC(ac7ca279) SHA1(4c9de06d1c346f83eb8d0d09a0eb32c40bd8014c) )
ROM_END

ROM_START( tek4109a )
	// another set with 160-32xx-03 v10.5 labels exists
	ROM_REGION16_LE( 0x40000, "firmware", 0 )
	ROM_LOAD16_BYTE( "160-3283-02 v8.2.u60",  0x00000, 0x8000, CRC(2a821db6) SHA1(b4d8b74bd9fe43885dcdc4efbdd1eebb96e32060) )
	ROM_LOAD16_BYTE( "160-3284-02 v8.2.u160", 0x00001, 0x8000, CRC(ee567b01) SHA1(67b1b0648cfaa28d57473bcc45358ff2bf986acf) )
	ROM_LOAD16_BYTE( "160-3281-02 v8.2.u70",  0x10000, 0x8000, CRC(e2713328) SHA1(b0bb3471539ef24d79b18d0e33bc148ed27d0ec4) )
	ROM_LOAD16_BYTE( "160-3282-02 v8.2.u170", 0x10001, 0x8000, CRC(c109a4f7) SHA1(762019105c1f82200a9c99ccfcfd8ee81d2ac4fe) )
	ROM_LOAD16_BYTE( "160-3279-02 v8.2.u80",  0x20000, 0x8000, CRC(00822078) SHA1(a82e61dafccbaea44e67efaa5940e52ec6d07d7d) )
	ROM_LOAD16_BYTE( "160-3280-02 v8.2.u180", 0x20001, 0x8000, CRC(eec9f70f) SHA1(7b65336219f5fa0d11f8be2b37040b564a53c52f) )
	ROM_LOAD16_BYTE( "160-3277-02 v8.2.u90",  0x30000, 0x8000, CRC(cf6ebc97) SHA1(298db473874c57bf4eec788818179748030a9ad8) )
	ROM_LOAD16_BYTE( "160-3278-02 v8.2.u190", 0x30001, 0x8000, CRC(d6124cd1) SHA1(f826aee5ec07cf5ac369697d93def0259ad225bb) )

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "160-3087 v1.0.u855", 0x0000, 0x1000, CRC(97479528) SHA1(e9e15f1f64b3b6bd139accd51950bae71fdc2193) )
ROM_END

} // anonymous namespace


/* System Drivers */

//    YEAR  NAME      PARENT    COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY      FULLNAME           FLAGS
COMP( 1983, tek4107a, 0,        0,      tek4107a, tek4107a, tek4107a_state, empty_init, "Tektronix", "Tektronix 4107A", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1983, tek4109a, tek4107a, 0,      tek4109a, tek4107a, tek4107a_state, empty_init, "Tektronix", "Tektronix 4109A", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



tek43xx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/************************************************************************************************************

    Skeleton driver for Tektronix 4300 series MC68020-based graphics workstations.

************************************************************************************************************/

#include "emu.h"
#include "cpu/m68000/m68020.h"
#include "screen.h"


namespace {

class tek43xx_state : public driver_device
{
public:
	tek43xx_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void tek4319(machine_config &config);

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};


u32 tek43xx_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void tek43xx_state::mem_map(address_map &map)
{
	map(0x00000000, 0x00000007).rom().region("program", 0);
	map(0x00000008, 0x000fffff).ram();
	map(0x01cb0000, 0x01cb0003).nopr();
	map(0x10000000, 0x1001ffff).rom().region("program", 0);
}

static INPUT_PORTS_START(tek4319)
INPUT_PORTS_END

void tek43xx_state::tek4319(machine_config &config)
{
	M68020(config, m_maincpu, 20'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &tek43xx_state::mem_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(60 * 1600 * 1066, 1600, 0, 1280, 1066, 0, 1024); // not confirmed except for displayed resolution
	screen.set_screen_update(FUNC(tek43xx_state::screen_update));
}

ROM_START(tek4319)
	ROM_REGION32_BE(0x20000, "program", 0)
	ROM_LOAD16_BYTE("160-5528-00.bin", 0x00000, 0x10000, CRC(a04ffe7f) SHA1(601c27c47138c38dc54b9cbabca8467170cc580b))
	ROM_LOAD16_BYTE("160-5528-01.bin", 0x00001, 0x10000, CRC(869ec49f) SHA1(145a20272b1f9eb42618d0b4aefe9ea79a27dcce))
ROM_END

} // anonymous namespace

COMP(1988, tek4319, 0, 0, tek4319, tek4319, tek43xx_state, empty_init, "Tektronix", "4319 Graphics Workstation", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



tek440x.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont, AJR
/***************************************************************************

    Tektronix 440x "AI Workstations"

    skeleton by R. Belmont

    Hardware overview:
        * 68010 (4404) or 68020 (4405) with custom MMU
        * Intelligent floppy subsystem with 6502 driving a uPD765 controller
        * NS32081 FPU
        * 6551 debug console AICA
        * SN76496 PSG for sound
        * MC146818 RTC
        * MC68681 DUART / timer (3.6864 MHz clock) (serial channel A = keyboard, channel B = RS-232 port)
        * AM9513 timer (source of timer IRQ)
        * NCR5385 SCSI controller

        Video is a 640x480 1bpp window on a 1024x1024 VRAM area; smooth panning around that area
        is possible as is flat-out changing the scanout address.

    IRQ levels:
        7 = Debug (NMI)
        6 = VBL
        5 = UART
        4 = Spare (exp slots)
        3 = SCSI
        2 = DMA
        1 = Timer
        0 = Unused

    MMU info:
        Map control register (location unk): bit 15 = VM enable, bits 10-8 = process ID

        Map entries:
            bit 15 = dirty
            bit 14 = write enable
            bit 13-11 = process ID
            bits 10-0 = address bits 22-12 in the final address

***************************************************************************/

#include "emu.h"

#include "tek410x_kbd.h"
#include "tek_msu_fdc.h"

#include "bus/nscsi/hd.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68010.h"
#include "machine/am9513.h"
#include "machine/bankdev.h"
#include "machine/input_merger.h"
#include "machine/mc146818.h"
#include "machine/mc68681.h"
#include "machine/mos6551.h"    // debug tty
#include "machine/ncr5385.h"
#include "machine/ns32081.h"
#include "machine/nscsi_bus.h"
#include "sound/sn76496.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "logmacro.h"

namespace {

class tek440x_state : public driver_device
{
public:
	tek440x_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_vm(*this, "vm"),
		m_duart(*this, "duart"),
		m_keyboard(*this, "keyboard"),
		m_snsnd(*this, "snsnd"),
		m_rtc(*this, "rtc"),
		m_scsi(*this, "scsi:7:ncr5385"),
		m_vint(*this, "vint"),
		m_prom(*this, "maincpu"),
		m_mainram(*this, "mainram"),
		m_vram(*this, "vram"),
		m_map(*this, "map", 0x1000, ENDIANNESS_BIG),
		m_map_view(*this, "map"),
		m_boot(false),
		m_map_control(0),
		m_kb_rdata(true),
		m_kb_tdata(true),
		m_kb_rclamp(false),
		m_kb_loop(false)
	{ }

	void tek4404(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	u16 memory_r(offs_t offset, u16 mem_mask);
	void memory_w(offs_t offset, u16 data, u16 mem_mask);
	u16 map_r(offs_t offset);
	void map_w(offs_t offset, u16 data, u16 mem_mask);
	u8 mapcntl_r();
	void mapcntl_w(u8 data);
	void sound_w(u8 data);
	void diag_w(u8 data);

	void kb_rdata_w(int state);
	void kb_tdata_w(int state);
	void kb_rclamp_w(int state);

	void logical_map(address_map &map) ATTR_COLD;
	void physical_map(address_map &map) ATTR_COLD;

	required_device<m68010_device> m_maincpu;
	required_device<address_map_bank_device> m_vm;
	required_device<mc68681_device> m_duart;
	required_device<tek410x_keyboard_device> m_keyboard;
	required_device<sn76496_device> m_snsnd;
	required_device<mc146818_device> m_rtc;
	required_device<ncr5385_device> m_scsi;
	required_device<input_merger_all_high_device> m_vint;

	required_region_ptr<u16> m_prom;
	required_shared_ptr<u16> m_mainram;
	required_shared_ptr<u16> m_vram;
	memory_share_creator<u16> m_map;
	memory_view m_map_view;

	bool m_boot;
	u8 m_map_control;
	bool m_kb_rdata;
	bool m_kb_tdata;
	bool m_kb_rclamp;
	bool m_kb_loop;
};

/*************************************
 *
 *  Machine start
 *
 *************************************/

void tek440x_state::machine_start()
{
	save_item(NAME(m_boot));
	save_item(NAME(m_map_control));
	save_item(NAME(m_kb_rdata));
	save_item(NAME(m_kb_tdata));
	save_item(NAME(m_kb_rclamp));
	save_item(NAME(m_kb_loop));
}



/*************************************
 *
 *  Machine reset
 *
 *************************************/

void tek440x_state::machine_reset()
{
	m_boot = true;
	diag_w(0);
	m_keyboard->kdo_w(1);
	mapcntl_w(0);
	m_vint->in_w<1>(0);
}



/*************************************
 *
 *  Video refresh
 *
 *************************************/

u32 tek440x_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (int y = 0; y < 480; y++)
	{
		u16 *const line = &bitmap.pix(y);
		u16 const *video_ram = &m_vram[y * 64];

		for (int x = 0; x < 640; x += 16)
		{
			u16 const word = *(video_ram++);
			for (int b = 0; b < 16; b++)
			{
				line[x + b] = BIT(word, 15 - b);
			}
		}
	}

	return 0;
}



/*************************************
 *
 *  CPU memory handlers
 *
 *************************************/

u16 tek440x_state::memory_r(offs_t offset, u16 mem_mask)
{
	if (m_boot)
		return m_prom[offset & 0x3fff];

	const offs_t offset0 = offset;
	if (BIT(m_map_control, 4))
		offset = BIT(offset, 0, 11) | BIT(m_map[offset >> 11], 0, 11) << 11;
	if (offset < 0x300000 && offset >= 0x100000 && !machine().side_effects_disabled())
	{
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
		m_maincpu->set_buserror_details(offset0 << 1, 1, m_maincpu->get_fc());
	}

	return m_vm->read16(offset, mem_mask);
}

void tek440x_state::memory_w(offs_t offset, u16 data, u16 mem_mask)
{
	const offs_t offset0 = offset;
	if (BIT(m_map_control, 4))
		offset = BIT(offset, 0, 11) | BIT(m_map[offset >> 11], 0, 11) << 11;
	if (offset < 0x300000 && offset >= 0x100000 && !machine().side_effects_disabled())
	{
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
		m_maincpu->set_buserror_details(offset0 << 1, 0, m_maincpu->get_fc());
	}

	m_vm->write16(offset, data, mem_mask);
}

u16 tek440x_state::map_r(offs_t offset)
{
	return m_map[offset >> 11];
}

void tek440x_state::map_w(offs_t offset, u16 data, u16 mem_mask)
{
	COMBINE_DATA(&m_map[offset >> 11]);
}

u8 tek440x_state::mapcntl_r()
{
	return m_map_control;
}

void tek440x_state::mapcntl_w(u8 data)
{
	if (BIT(data, 5))
		m_map_view.select(0);
	else
		m_map_view.disable();
	m_map_control = data & 0x1f;
}

void tek440x_state::sound_w(u8 data)
{
	m_snsnd->write(data);
	m_boot = false;
}

void tek440x_state::diag_w(u8 data)
{
	if (!m_kb_rclamp && m_kb_loop != BIT(data, 7))
		m_keyboard->kdo_w(!BIT(data, 7) || m_kb_tdata);

	m_kb_loop = BIT(data, 7);
}

void tek440x_state::kb_rdata_w(int state)
{
	m_kb_rdata = state;
	if (!m_kb_rclamp)
		m_duart->rx_a_w(state);
}

void tek440x_state::kb_rclamp_w(int state)
{
	if (m_kb_rclamp != !state)
	{
		m_kb_rclamp = !state;

		// Clamp RXDA to 1 and KBRDATA to 0 when DUART asserts RxRDYA
		if (m_kb_tdata || !m_kb_loop)
			m_keyboard->kdo_w(state);
		m_duart->rx_a_w(state ? m_kb_rdata : 1);
	}
}

void tek440x_state::kb_tdata_w(int state)
{
	if (m_kb_tdata != state)
	{
		m_kb_tdata = state;

		m_duart->ip4_w(!state);
		if (m_kb_loop && m_kb_rdata && !m_kb_rclamp)
			m_keyboard->kdo_w(state);
	}
}

void tek440x_state::logical_map(address_map &map)
{
	map(0x000000, 0x7fffff).rw(FUNC(tek440x_state::memory_r), FUNC(tek440x_state::memory_w));
	map(0x800000, 0xffffff).view(m_map_view);
	m_map_view[0](0x800000, 0xffffff).rw(FUNC(tek440x_state::map_r), FUNC(tek440x_state::map_w));
}

void tek440x_state::physical_map(address_map &map)
{
	map(0x000000, 0x1fffff).ram().share("mainram");
	map(0x600000, 0x61ffff).ram().share("vram");

	// 700000-71ffff spare 0
	// 720000-73ffff spare 1
	map(0x740000, 0x747fff).rom().mirror(0x8000).region("maincpu", 0);
	map(0x760000, 0x760fff).ram().mirror(0xf000); // debug RAM

	// 780000-79ffff processor board I/O
	map(0x780000, 0x780000).rw(FUNC(tek440x_state::mapcntl_r), FUNC(tek440x_state::mapcntl_w));
	// 782000-783fff: video address registers
	// 784000-785fff: video control registers
	map(0x784000, 0x784000).lw8(
		[this](u8 data)
		{
			m_vint->in_w<0>(BIT(data, 6));
		}, "vcbpr_w");
	// 786000-787fff: spare
	map(0x788000, 0x788000).w(FUNC(tek440x_state::sound_w));
	// 78a000-78bfff: NS32081 FPU
	map(0x78c000, 0x78c007).rw("aica", FUNC(mos6551_device::read), FUNC(mos6551_device::write)).umask16(0xff00);
	// 78e000-78ffff: spare

	// 7a0000-7bffff peripheral board I/O
	// 7a0000-7affff: reserved
	map(0x7b0000, 0x7b0000).w(FUNC(tek440x_state::diag_w));
	// 7b1000-7b1fff: diagnostic registers
	// 7b2000-7b3fff: Centronics printer data
	map(0x7b4000, 0x7b401f).rw(m_duart, FUNC(mc68681_device::read), FUNC(mc68681_device::write)).umask16(0xff00);
	// 7b6000-7b7fff: Mouse
	map(0x7b8000, 0x7b8003).mirror(0x100).rw("timer", FUNC(am9513_device::read16), FUNC(am9513_device::write16));
	// 7ba000-7bbfff: MC146818 RTC
	map(0x7bc000, 0x7bc000).lw8(
		[this](u8 data)
		{
			m_scsi->set_own_id(data & 7);

			// TODO: bit 7 -> SCSI bus reset
			LOG("scsi bus reset %d\n", BIT(data, 7));
		}, "scsi_addr"); // 7bc000-7bdfff: SCSI bus address registers
	map(0x7be000, 0x7be01f).m(m_scsi, FUNC(ncr5385_device::map)).umask16(0xff00); //.mirror(0x1fe0) .cswidth(16);

	// 7c0000-7fffff EPROM application space
	map(0x7c0000, 0x7fffff).nopr();
}

/*************************************
 *
 *  Port definitions
 *
 *************************************/

static INPUT_PORTS_START( tek4404 )
INPUT_PORTS_END

/*************************************
 *
 *  Machine driver
 *
 *************************************/

static void scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
	device.option_add("tek_msu_fdc", TEK_MSU_FDC);
}

// interrupts
// 7 debug
// 6 vsync
// 5 uart
// 4 spare
// 3 scsi
// 2 dma (network?)
// 1 timer/printer

void tek440x_state::tek4404(machine_config &config)
{
	/* basic machine hardware */
	M68010(config, m_maincpu, 40_MHz_XTAL / 4); // MC68010L10
	m_maincpu->set_addrmap(AS_PROGRAM, &tek440x_state::logical_map);

	ADDRESS_MAP_BANK(config, m_vm);
	m_vm->set_addrmap(0, &tek440x_state::physical_map);
	m_vm->set_data_width(16);
	m_vm->set_addr_width(23);
	m_vm->set_endianness(ENDIANNESS_BIG);

	INPUT_MERGER_ALL_HIGH(config, m_vint);
	m_vint->output_handler().set_inputline(m_maincpu, M68K_IRQ_6);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_video_attributes(VIDEO_UPDATE_BEFORE_VBLANK);
	screen.set_raw(25.2_MHz_XTAL, 800, 0, 640, 525, 0, 480); // 31.5 kHz horizontal (guessed), 60 Hz vertical
	screen.set_screen_update(FUNC(tek440x_state::screen_update));
	screen.set_palette("palette");
	screen.screen_vblank().set(m_vint, FUNC(input_merger_all_high_device::in_w<1>));
	PALETTE(config, "palette", palette_device::MONOCHROME);

	mos6551_device &aica(MOS6551(config, "aica", 40_MHz_XTAL / 4 / 10));
	aica.set_xtal(1.8432_MHz_XTAL);
	aica.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	aica.irq_handler().set_inputline(m_maincpu, M68K_IRQ_7);

	MC68681(config, m_duart, 14.7456_MHz_XTAL / 4);
	m_duart->irq_cb().set_inputline(m_maincpu, M68K_IRQ_5); // auto-vectored
	m_duart->outport_cb().set(FUNC(tek440x_state::kb_rclamp_w)).bit(4);
	m_duart->outport_cb().append(m_keyboard, FUNC(tek410x_keyboard_device::reset_w)).bit(3);
	m_duart->a_tx_cb().set(m_keyboard, FUNC(tek410x_keyboard_device::kdi_w));

	TEK410X_KEYBOARD(config, m_keyboard);
	m_keyboard->tdata_callback().set(FUNC(tek440x_state::kb_tdata_w));
	m_keyboard->rdata_callback().set(FUNC(tek440x_state::kb_rdata_w));

	AM9513(config, "timer", 40_MHz_XTAL / 4 / 10); // from CPU E output

	MC146818(config, m_rtc, 32.768_MHz_XTAL);

	NSCSI_BUS(config, "scsi");
	// hard disk is a Micropolis 1304 (https://www.micropolis.com/support/hard-drives/1304)
	// with a Xebec 1401 SASI adapter inside the Mass Storage Unit
	NSCSI_CONNECTOR(config, "scsi:0", scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:1", scsi_devices, "tek_msu_fdc");
	NSCSI_CONNECTOR(config, "scsi:2", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr5385", NCR5385).clock(40_MHz_XTAL / 4).machine_config(
		[this](device_t *device)
		{
			ncr5385_device &adapter = downcast<ncr5385_device &>(*device);

			adapter.irq().set_inputline(m_maincpu, M68K_IRQ_3);
		});

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set("aica", FUNC(mos6551_device::write_rxd));
	rs232.dcd_handler().set("aica", FUNC(mos6551_device::write_dcd));
	rs232.dsr_handler().set("aica", FUNC(mos6551_device::write_dsr));
	rs232.cts_handler().set("aica", FUNC(mos6551_device::write_cts));

	SPEAKER(config, "mono").front_center();

	SN76496(config, m_snsnd, 25.2_MHz_XTAL / 8).add_route(ALL_OUTPUTS, "mono", 0.80);
}



/*************************************
 *
 *  ROM definition(s)
 *
 *************************************/

ROM_START( tek4404 )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD16_BYTE( "tek_u158.bin", 0x000000, 0x004000, CRC(9939e660) SHA1(66b4309e93e4ff20c1295dc2ec2a8d6389b2578c) )
	ROM_LOAD16_BYTE( "tek_u163.bin", 0x000001, 0x004000, CRC(a82dcbb1) SHA1(a7e4545e9ea57619faacc1556fa346b18f870084) )

	ROM_REGION( 0x2000, "scsimfm", 0 )
	ROM_LOAD( "scsi_mfm.bin", 0x000000, 0x002000, CRC(b4293435) SHA1(5e2b96c19c4f5c63a5afa2de504d29fe64a4c908) )
ROM_END

} // anonymous namespace


/*************************************
 *
 *  Game driver(s)
 *
 *************************************/
//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY      FULLNAME                               FLAGS
COMP( 1984, tek4404, 0,      0,      tek4404, tek4404, tek440x_state, empty_init, "Tektronix", "4404 Artificial Intelligence System", MACHINE_NOT_WORKING )



tekigw.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * Tektronix Intelligent Graphics Workstations (6110, 6120, 6130, 6205, 6210, 6212, 4132)
 *
 * Sources:
 *   - http://bitsavers.trailing-edge.com/pdf/tektronix/6130/6100_Series_Hardware_Descriptions.pdf
 *   - https://archive.org/details/tektronix_Tektronix_1985
 *
 * TODO:
 *   - slots and cards
 *   - gpib devices
 *   - graphics and keyboard
 */
/*
 * tek4132
 *  - novram chip select not asserted when trying to read ethernet mac address?
 *  - aborts to firmware monitor on second serial port after failing to boot
 *  - hangs during UTek boot
 *
booting /vmunix
<UTek>  RSA - RS-232 ports
<UTek>  RSADMA - RS-232 DMA
<UTek>  SCSI - SCSI interface
<UTek>  lna0: Ethernet= 8:0:11:0:0:f7 Ip=[42.128.0.1]
<UTek>  LNA - Local Area Network
<UTek>  CEPWR - Soft Power switch
<UTek> end configure
<UTek> Tektronix - UTek STRATOSIIW2.3 #1.25 Thu Oct 23 16:56:50 PDT 1986
<UTek>
<UTek> real mem = 1024k
<UTek> firstaddr = 0x3fda8
<UTek> firstaddr = 0x78600
<UTek> avail mem = 531456
<UTek> using 51 buffers containing 104448 bytes of memory
<UTek> inittodr: clock has gone backward, file system time used
<UTek>    file system time = 2eab0645
<UTek>    clock = 5bb2d3
 */
/*
 * tek6130
 * "Invalid menu file.  Dropping into shell.\n" - string at d789
 * accessed by printf at 7b82
 * message set at a6b0
 *
Booting from disk
Invalid menu file.  Dropping into shell.
diags>
 */

#include "emu.h"

// cpus and memory
#include "cpu/ns32000/ns32000.h"
#include "machine/ram.h"

// various hardware
#include "machine/am9516.h"
#include "machine/am9517a.h"
#include "machine/eepromser.h"
#include "machine/i82586.h"
#include "machine/mm58167.h"
#include "machine/ncr5385.h"
#include "machine/ns32081.h"
#include "machine/ns32082.h"
#include "machine/ns32202.h"
#include "machine/tms9914.h"
#include "machine/wd_fdc.h"
#include "machine/wd1010.h"
#include "machine/z80scc.h"

#include "machine/input_merger.h"

// busses and connectors
#include "bus/ieee488/ieee488.h"
#include "machine/nscsi_bus.h"
#include "bus/nscsi/hd.h"
#include "bus/rs232/rs232.h"
#include "imagedev/floppy.h"
#include "imagedev/harddriv.h"

// graphics
#include "cpu/mcs51/mcs51.h"
#include "video/mc6845.h"
#include "screen.h"

#define VERBOSE 0
#include "logmacro.h"

namespace {

class tekigw_state_base : public driver_device
{
protected:
	tekigw_state_base(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_fpu(*this, "fpu")
		, m_mmu(*this, "mmu")
		, m_icu(*this, "icu")
		, m_ram(*this, "ram")
		, m_scc(*this, "scc")
		, m_serial(*this, "port%u", 0U)
		, m_rtc(*this, "rtc")
		, m_nov(*this, "nov%u", 0U)
		, m_dma(*this, "dma")
		, m_lan(*this, "lan")
		, m_led(*this, "led")
	{
	}

	void common_config(machine_config &config);
	void common_init();

	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// address maps
	template <unsigned ST> void cpu_map(address_map &map) ATTR_COLD;
	void dma_map(address_map &map) ATTR_COLD;

	// computer board control registers
	u8 nov_r() { return m_nmr; }
	u8 scr_r() { return m_scr; }
	u8 ssr_r() { return m_ssr; }
	void led_w(u8 data) { m_led = data; }
	void nov_w(u8 data);
	void scr_w(u8 data);

	// novram data out handler
	void nov_do(int state);

	void buserror(s32 param);

	required_device<ns32016_device> m_cpu;
	required_device<ns32081_device> m_fpu;
	required_device<ns32082_device> m_mmu;
	required_device<ns32202_device> m_icu;
	required_device<ram_device> m_ram;

	required_device<scc8530_device> m_scc;
	required_device_array<rs232_port_device, 2> m_serial;
	required_device<mm58167_device> m_rtc;
	optional_device_array<eeprom_serial_x24c44_16bit_device, 2> m_nov;
	required_device<am9516_device> m_dma;
	required_device<i82586_device> m_lan;

	output_finder<> m_led;

	emu_timer *m_buserror = nullptr;

private:
	u8 m_nmr = 0; // nonvolatile memory register
	u16 m_per = 0; // parity error register
	u8 m_scr = 0; // system control register
	u8 m_ssr = 0; // system status register

	// ram parity state
	memory_passthrough_handler m_parity_mph;
	std::unique_ptr<u32 []> m_parity_flag;
	unsigned m_parity_bad = 0;
	bool m_parity_check = 0;
};

class tek6100_state : public tekigw_state_base
{
public:
	tek6100_state(machine_config const &mconfig, device_type type, char const *tag)
		: tekigw_state_base(mconfig, type, tag)
		, m_gpib(*this, "gpib")
		, m_fdc(*this, "fdc")
		, m_fdd(*this, "fdc:%u:525dd", 0U)
		, m_hdc(*this, "hdc")
		, m_hdd(*this, "hdc:0")
		, m_dpu_cpu(*this, "dpu_cpu")
		, m_dpu_cram(*this, "dpu_cram")
		, m_dpu_vram(*this, "dpu_vram")
	{
	}

	void tek6130(machine_config &config);
	void init() { tekigw_state_base::common_init(); }

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// address maps
	template <unsigned ST> void cpu_map(address_map &map) ATTR_COLD;
	void lan_map(address_map &map) ATTR_COLD;
	template <unsigned ST> void dpu_cpu_map(address_map &map) ATTR_COLD;

private:
	// computer board control registers
	void fcr_w(u8 data);
	void gcr_w(u8 data);
	void hcr_w(u8 data);

	// hard disk buffer handlers
	template <typename T> T buf_r();
	template <typename T> void buf_w(T data);

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect) { return 0; }

	required_device<tms9914_device> m_gpib;
	required_device<wd1770_device> m_fdc;
	optional_device_array<floppy_image_device, 2> m_fdd;
	required_device<wd1010_device> m_hdc;
	required_device<harddisk_image_device> m_hdd;

	optional_device<ns32016_device> m_dpu_cpu;
	optional_device<ram_device> m_dpu_cram;
	optional_device<ram_device> m_dpu_vram;

	u8 m_hcr = 0; // hard disk control register

	// hard disk controller buffer
	u16 m_hdc_ptr = 0;
	std::unique_ptr<u8[]> m_hdc_buf;
};

class tek4132_state : public tekigw_state_base
{
public:
	tek4132_state(machine_config const &mconfig, device_type type, char const *tag)
		: tekigw_state_base(mconfig, type, tag)
		, m_scsibus(*this, "scsi")
		, m_scsi(*this, "scsi:7:ncr5385")
		, m_sdma(*this, "sdma")
		, m_sirq(*this, "sirq")
	{
	}

	void tek4132(machine_config &config);
	void init() { tekigw_state_base::common_init(); }

protected:
	// driver_device overrides
	//virtual void machine_start() override ATTR_COLD;
	//virtual void machine_reset() override ATTR_COLD;

	// address maps
	template <unsigned ST> void cpu_map(address_map &map) ATTR_COLD;
	void lan_map(address_map &map) ATTR_COLD;

private:
	required_device<nscsi_bus_device> m_scsibus;
	required_device<ncr5385_device> m_scsi;
	required_device<am9517a_device> m_sdma;

	required_device<input_merger_all_high_device> m_sirq;
};

void tekigw_state_base::machine_start()
{
	save_item(NAME(m_nmr));
	save_item(NAME(m_per));
	save_item(NAME(m_scr));
	save_item(NAME(m_ssr));

	m_per = 0;
	m_ssr = 0;

	m_parity_check = false;

	m_buserror = timer_alloc(FUNC(tek4132_state::buserror), this);
}

void tekigw_state_base::machine_reset()
{
	nov_w(0);
	scr_w(0);

	m_icu->g_w<0>(1); // mmu present
	m_icu->g_w<7>(1); // fpu present
	m_icu->ir_w<12>(0);
}

void tek6100_state::machine_start()
{
	tekigw_state_base::machine_start();

	m_hdc_buf = std::make_unique<u8[]>(4096);

	save_item(NAME(m_hcr));

	save_item(NAME(m_hdc_ptr));
	save_pointer(NAME(m_hdc_buf), 4096);
}

void tek6100_state::machine_reset()
{
	tekigw_state_base::machine_reset();

	hcr_w(0);
	fcr_w(0);
}

void tekigw_state_base::common_init()
{
	m_led.resolve();

	m_lan->space(0).install_ram(0, m_ram->mask(), m_ram->pointer());
}

enum fcr_mask : u8
{
	FCR_DRIVE = 0x01, // drive select
	FCR_SIDE  = 0x02, // side select
	FCR_DDEN  = 0x04, // double density enable
	FCR_FDEN  = 0x08, // flexible disk enable
};

void tek6100_state::fcr_w(u8 data)
{
	floppy_image_device *fdd = nullptr;
	if (data & FCR_FDEN)
		fdd = m_fdd[data & FCR_DRIVE];

	m_fdc->set_floppy(fdd);
	if (fdd)
		fdd->ss_w(bool(data & FCR_SIDE));

	m_fdc->dden_w(bool(data & FCR_DDEN));
}

enum gcr_mask : u8
{
	GCR_SYSCTL = 0x02, // gpib line driver enable
};

void tek6100_state::gcr_w(u8 data)
{
	// TODO: enable/disable line drivers
}

enum scr_mask : u8
{
	SCR_RAME  = 0x01, // ram enable
	SCR_NMIE  = 0x04, // nmi enable
	SCR_LLOP  = 0x08, // lan loopback enable
	SCR_BERRE = 0x10, // bus error enable
	SCR_DMAE  = 0x20, // dma enable
	SCR_PARB  = 0x40, // parity bad
	SCR_PARE  = 0x80, // parity enable
};

enum ssr_mask : u8
{
	SSR_DMACH = 0x0f, // dma channel
	SSR_BERR  = 0x10, // bus error
	SSR_PWRSW = 0x20, // power switch
	SSR_PFAIL = 0x40, // power fail
	SSR_PERR  = 0x80, // parity error
};

enum per_mask : u16
{
	PER_PARADR = 0x3fff, // parity address
	PER_HIERR  = 0x4000,
	PER_LOERR  = 0x8000,
};

void tekigw_state_base::scr_w(u8 data)
{
	LOG("scr_w 0x%02x (%s)\n", data, machine().describe_context());

	if (!(m_scr & SCR_RAME) && (data & SCR_RAME))
		m_cpu->space(0).install_ram(0, m_ram->mask(), m_ram->pointer());

	if ((m_scr ^ data) & SCR_LLOP)
		m_lan->set_loopback(data & SCR_LLOP);

	if (m_scr & SCR_BERRE && !(data & SCR_BERRE))
		m_ssr &= ~(SSR_BERR | SSR_DMACH);

	// install parity handlers
	if (!(m_scr & SCR_PARB) && (data & SCR_PARB) && !m_parity_check)
	{
		m_parity_flag = std::make_unique<u32[]>(m_ram->size() / 32);
		m_parity_bad = 0;
		m_parity_check = true;

		m_parity_mph = m_cpu->space(0).install_readwrite_tap(
				0, m_ram->mask(),
				"parity",
				[this] (offs_t offset, u16 &data, u16 mem_mask)
				{
					if (m_scr & SCR_PARE)
					{
						bool error = false;

						// check bad parity (lo)
						if ((mem_mask & 0x00ff) && BIT(m_parity_flag[offset / 32], (offset & 31) + 0))
						{
							m_ssr |= SSR_PERR;
							m_per &= ~PER_PARADR;
							m_per |= PER_LOERR | ((offset >> 9) & PER_PARADR);

							error = true;
						}

						// check bad parity (hi)
						if ((mem_mask & 0xff00) && BIT(m_parity_flag[offset / 32], (offset & 31) + 1))
						{
							m_ssr |= SSR_PERR;
							m_per &= ~PER_PARADR;
							m_per |= PER_HIERR | ((offset >> 9) & PER_PARADR);

							error = true;
						}

						if (error && (m_scr & SCR_NMIE))
							m_cpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
					}
				},
				[this] (offs_t offset, u16 &data, u16 mem_mask)
				{
					if (m_scr & SCR_PARB)
					{
						// mark bad parity (lo)
						if ((mem_mask & 0x00ff) && !BIT(m_parity_flag[offset / 32], (offset & 31) + 0))
						{
							m_parity_flag[offset / 32] |= 1U << ((offset & 31) + 0);
							m_parity_bad++;
						}

						// mark bad parity (hi)
						if ((mem_mask & 0xff00) && !BIT(m_parity_flag[offset / 32], (offset & 31) + 1))
						{
							m_parity_flag[offset / 32] |= 1U << ((offset & 31) + 1);
							m_parity_bad++;
						}
					}
					else
					{
						// clear bad parity (lo)
						if ((mem_mask & 0x00ff) && BIT(m_parity_flag[offset / 32], (offset & 31) + 0))
						{
							m_parity_flag[offset / 32] &= ~(1U << ((offset & 31) + 0));
							m_parity_bad--;
						}

						// clear bad parity (hi)
						if ((mem_mask & 0xff00) && BIT(m_parity_flag[offset / 32], (offset & 31) + 1))
						{
							m_parity_flag[offset / 32] &= ~(1U << ((offset & 31) + 1));
							m_parity_bad--;
						}

						// stop checking parity if all clear
						if (!m_parity_bad)
						{
							m_parity_flag.reset();
							m_parity_mph.remove();
							m_parity_check = false;
						}
					}
				},
				&m_parity_mph);
	}

	// stop checking parity if all clear
	if ((m_scr & SCR_PARB) && !(data & SCR_PARB) && m_parity_check && !m_parity_bad)
	{
		m_parity_flag.reset();
		m_parity_mph.remove();
		m_parity_check = false;
	}

	if (data & SCR_PARE)
		m_per = 0;
	else
		m_ssr &= ~SSR_PERR;

	m_scr = data;
}

enum nov_mask : u8
{
	NOV_NDAT = 0x01, // nonvolatile memory data
	NOV_SK   = 0x02, // serial clock
	NOV_CS0  = 0x04, // chip select 0
	NOV_CS1  = 0x08, // chip select 1
	NOV_LED  = 0x10, // power-on led
	NOV_PON  = 0x20, // system power enable
	NOV_MSK  = 0x3e,
};

void tekigw_state_base::nov_w(u8 data)
{
	bool const cs0 = data & NOV_CS0;
	bool const cs1 = data & NOV_CS1;

	if (m_nov[0] && ((m_nmr ^ data) & NOV_CS0))
		m_nov[0]->cs_write(cs0);
	if (m_nov[1] && ((m_nmr ^ data) & NOV_CS1))
		m_nov[1]->cs_write(cs1);

	if (m_nov[0] && cs0)
	{
		m_nov[0]->di_write(bool(data & NOV_NDAT));
		m_nov[0]->clk_write(bool(data & NOV_SK));
	}

	if (m_nov[1] && cs1)
	{
		m_nov[1]->di_write(bool(data & NOV_NDAT));
		m_nov[1]->clk_write(bool(data & NOV_SK));
	}

	m_nmr = (m_nmr & ~NOV_MSK) | (data & NOV_MSK);
}

void tekigw_state_base::nov_do(int state)
{
	if (state)
		m_nmr |= NOV_NDAT;
	else
		m_nmr &= ~NOV_NDAT;
}

void tekigw_state_base::buserror(s32 param)
{
	if (m_scr & SCR_BERRE)
	{
		m_ssr |= SSR_BERR | (param & SSR_DMACH);

		if (m_scr & SCR_NMIE)
			m_cpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
	}
}

enum hcr_mask : u8
{
	HCR_HDSEL = 0x0f, // head select
	HCR_DRSEL = 0x10, // drive select
	HCR_MULT  = 0x20, // multiple sector
	HCR_BFRDY = 0x40, // buffer ready
	HCR_BCCLR = 0x80, // buffer counter clear
};

void tek6100_state::hcr_w(u8 data)
{
	m_hdc->drdy_w(m_hdd->exists() && bool(data & HCR_DRSEL));
	m_hdc->brdy_w(bool(data & HCR_BFRDY));

	if (!(data & HCR_BCCLR))
		m_hdc_ptr = 0;

	m_hdc->head_w(data & 7);
	m_hdc->sc_w(bool(data & HCR_DRSEL));

	m_hcr = data;
}

template <typename T> T tek6100_state::buf_r()
{
	T data = 0;

	for (unsigned i = 0; i < sizeof(T); i++)
		data |= T(m_hdc_buf[m_hdc_ptr++ & 0xfff]) << (i * 8);

	return data;
}

template <typename T> void tek6100_state::buf_w(T data)
{
	for (unsigned i = 0; i < sizeof(T); i++)
		m_hdc_buf[m_hdc_ptr++ & 0xfff] = u8(data >> (i * 8));
}

template <unsigned ST> void tekigw_state_base::cpu_map(address_map &map)
{
	map(0xfff004, 0xfff005).lw16([this](u16 data) { m_lan->ca(1); }, "lan_ca");
	map(0xfff006, 0xfff006).rw(FUNC(tekigw_state_base::nov_r), FUNC(tekigw_state_base::nov_w));
	map(0xfff008, 0xfff009).portr("config");
	map(0xfff008, 0xfff008).w(FUNC(tekigw_state_base::led_w));
	map(0xfff00a, 0xfff00b).lr16([this]() { return m_per; }, "per_r");
	map(0xfff00c, 0xfff00c).rw(FUNC(tekigw_state_base::scr_r), FUNC(tekigw_state_base::scr_w));
	map(0xfff00e, 0xfff00e).r(FUNC(tekigw_state_base::ssr_r));
	map(0xfff00f, 0xfff00f).lr8([]() { return 7; }, "mem_size"); // code at 804703 uses this to size memory

	map(0xfff800, 0xfff83f).rw(m_rtc, FUNC(mm58167_device::read), FUNC(mm58167_device::write)).umask16(0xff);
	map(0xfffa00, 0xfffa07).rw(m_scc, FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w)).umask16(0xff);
	//map(0xfffd00, 0xfffdff); // dma controller
	map(0xfffe00, 0xfffeff).m(m_icu, FUNC(ns32202_device::map<BIT(ST, 1)>)).umask16(0xff);
}

template <unsigned ST> void tek6100_state::cpu_map(address_map &map)
{
	tekigw_state_base::cpu_map<ST>(map);

	map(0x000000, 0x007fff).rom().region("kernel", 0);

	//map(0x300000, 0x7fffff); // external bus
	map(0x800000, 0x807fff).rom().region("kernel", 0);
	//map(0x820000, 0xfdffff); // external bus
	map(0xfe0000, 0xfeffff).lr8(
		[this](address_space &space)
		{
			// FIXME: Should be 20μs according to 6100 documentation; CPU is still
			// too fast, and timing may differ on 4132. Also DMACH field should be
			// 7 for CPU, but diagnostic expects F; maybe 4132 is different?
			m_buserror->adjust(attotime::from_ticks(10, m_cpu->clock()), 0x7);

			return space.unmap();
		}, "buserror"); // expansion I/O

	map(0xff0000, 0xff7fff).rw(FUNC(tek6100_state::buf_r<u16>), FUNC(tek6100_state::buf_w<u16>));

	map(0xfff000, 0xfff000).w(FUNC(tek6100_state::fcr_w));
	map(0xfff002, 0xfff002).w(FUNC(tek6100_state::gcr_w));

	map(0xfff900, 0xfff90f).rw(m_gpib, FUNC(tms9914_device::read), FUNC(tms9914_device::write)).umask16(0xff);
	map(0xfffb00, 0xfffb0f).rw(m_hdc, FUNC(wd1010_device::read), FUNC(wd1010_device::write)).umask16(0xff);
	map(0xfffb10, 0xfffb10).w(FUNC(tek6100_state::hcr_w));
	map(0xfffc00, 0xfffc07).rw(m_fdc, FUNC(wd1770_device::read), FUNC(wd1770_device::write)).umask16(0xff);
	map(0xffff00, 0xffffff).rom().region("kernel", 0x7f00);
}

void tek6100_state::lan_map(address_map &map)
{
	map.global_mask(0x3fffff);

	map(0x3fff00, 0x3fffff).rom().region("kernel", 0x7f00); // 0xf00000-0xffffff
}

template <unsigned ST> void tek6100_state::dpu_cpu_map(address_map &map)
{
	map(0x000000, 0x007fff).rom().region("dpu", 0);

	map(0x100000, 0x107fff).rom().region("dpu", 0);


	map(0x20000e, 0x20000e).lr8([]() { return 0x20; }, "fpu_present");

	//map(0x200026, 0x200026);
	map(0x200028, 0x200028).lw8(
		[this](u8 data)
		{
			if (!BIT(data, 7) && m_dpu_cpu)
			{
				m_dpu_cpu->space(0).install_ram(0, m_dpu_cram->mask(), m_dpu_cram->pointer());
				m_dpu_cpu->space(0).install_ram(0x400000, 0x400000 | m_dpu_vram->mask(), m_dpu_vram->pointer());
			}
		}, "ram_enable");
	//map(0x20002a, 0x20002a);
	//map(0x20002c, 0x20002c);

	//map(0x300000, 0x300000); // PUbram - pattern ram
	map(0xc00000, 0xc0000f).ram(); // PUpixm1 (1/2 bit x 4 planes)
}

enum csr_mask : u8
{
	CSR_IDENT  = 0x07, // scsi bus id (inverted)
	CSR_RESET  = 0x08, // scsi bus reset
	CSR_FEOP   = 0x10, // dma force eop
	CSR_IENBL  = 0x20, // scsi interrupt enable
	CSR_DIPEND = 0x40, // dma interrupt pending
	CSR_SIPEND = 0x80, // scsi interrupt pending
};

template <unsigned ST> void tek4132_state::cpu_map(address_map &map)
{
	map(0x000000, 0xffffff).lr8(
		[this](address_space &space, offs_t offset)
		{
			if (!machine().side_effects_disabled())
			{
				// FIXME: Should be 20μs according to 6100 documentation; CPU is still
				// too fast, and timing may differ on 4132. Also DMACH field should be
				// 7 for CPU, but diagnostic expects F; maybe 4132 is different?
				m_buserror->adjust(attotime::from_ticks(20, m_cpu->clock()), 0xf);
				logerror("buserror 0x%06x (%s)\n", offset, machine().describe_context());
			}
			return space.unmap();
		}, "buserror");

	tekigw_state_base::cpu_map<ST>(map);

	map(0x000000, 0x00ffff).rom().region("kernel", 0);
	map(0x800000, 0x80ffff).rom().region("kernel", 0);

	//map(0xfff000, 0xfff000).w(FUNC(tek6100_state::fcr_w)); // rsaa_dmaaos serial dma address offset register
	//map(0xfff002, 0xfff002).w(FUNC(tek6100_state::gcr_w)); // rsaa_auxlatch serial auxiliary control register
	//map(0xfff008, 0xfff008).w(); // rsaa_irqreset
	//map(0xfff00f, 0xfff00f).lr8([]() { return 0x7; }, "memsize"); // code at 804703 uses this to size memory

	map(0xfff900, 0xfff91f).rw(m_sdma, FUNC(am9517a_device::read), FUNC(am9517a_device::write)).umask16(0xff);
	//map(0xfffa08, 0xfffa08); // rsaa_status

	//map(0xfffc00, 0xfffc1f); // signature
	map(0xfffc20, 0xfffc3f).lw8(
		[this](offs_t offset, u8 data)
		{
			LOG("csr_w 0x%02x offset 0x%x (%s)\n", data, offset, machine().describe_context());

			m_scsi->set_own_id(~data & CSR_IDENT);
			m_sirq->in_w<0>(bool(data & CSR_IENBL));
		}, "csr_w");
	map(0xfffc40, 0xfffc5f).m(m_scsi, FUNC(ncr5385_device::map)).umask16(0xff);
	map(0xfffc60, 0xfffc61).rw(m_dma, FUNC(am9516_device::data_r), FUNC(am9516_device::data_w));
	map(0xfffc62, 0xfffc63).rw(m_dma, FUNC(am9516_device::addr_r), FUNC(am9516_device::addr_w));
	//map(0xfffd00, 0xfffdff).ram(); // stub out 9516

	map(0xffff00, 0xffffff).rom().region("kernel", 0xff00);
}

void tek4132_state::lan_map(address_map &map)
{
	map.global_mask(0x3fffff);

	map(0x3fff00, 0x3fffff).rom().region("kernel", 0xff00); // 0xf00000-0xffffff
}

void tekigw_state_base::dma_map(address_map &map)
{
	map(0x000000, 0xffffff).lrw16(
		[this](offs_t offset, u16 mem_mask)
		{
			return (mem_mask == 0xffff)
				? m_cpu->space(0).read_word(offset << 1)
				: swapendian_int16(m_cpu->space(0).read_word(offset << 1, swapendian_int16(mem_mask)));
		}, "dma_r",
		[this](offs_t offset, u16 data, u16 mem_mask)
		{
			if (mem_mask == 0xffff)
				m_cpu->space(0).write_word(offset << 1, data, mem_mask);
			else
				m_cpu->space(0).write_word(offset << 1, swapendian_int16(data), swapendian_int16(mem_mask));
		}, "dma_w");
}

void tekigw_state_base::common_config(machine_config &config)
{
	NS32016(config, m_cpu, 20_MHz_XTAL / 2);

	NS32081(config, m_fpu, 20_MHz_XTAL / 2);
	m_cpu->set_fpu(m_fpu);

	NS32082(config, m_mmu, 20_MHz_XTAL / 2);
	m_cpu->set_mmu(m_mmu);

	NS32202(config, m_icu, 20_MHz_XTAL / 1000);
	m_icu->out_int().set_inputline(m_cpu, INPUT_LINE_IRQ0).invert();

	RAM(config, m_ram);
	m_ram->set_default_size("1M");

	SCC8530(config, m_scc, 16_MHz_XTAL / 4);
	m_scc->out_int_callback().set(m_icu, FUNC(ns32202_device::ir_w<1>)).invert();
	m_scc->configure_channels(2'457'600, 0, 2'457'600, 0);

	RS232_PORT(config, m_serial[0], default_rs232_devices, "terminal");
	m_serial[0]->cts_handler().set(m_scc, FUNC(z80scc_device::ctsa_w));
	m_serial[0]->dcd_handler().set(m_scc, FUNC(z80scc_device::dcda_w));
	m_serial[0]->rxd_handler().set(m_scc, FUNC(z80scc_device::rxa_w));
	m_scc->out_dtra_callback().set(m_serial[0], FUNC(rs232_port_device::write_dtr));
	m_scc->out_rtsa_callback().set(m_serial[0], FUNC(rs232_port_device::write_rts));
	m_scc->out_txda_callback().set(m_serial[0], FUNC(rs232_port_device::write_txd));

	RS232_PORT(config, m_serial[1], default_rs232_devices, nullptr);
	m_serial[1]->cts_handler().set(m_scc, FUNC(z80scc_device::ctsb_w));
	m_serial[1]->dcd_handler().set(m_scc, FUNC(z80scc_device::dcdb_w));
	m_serial[1]->rxd_handler().set(m_scc, FUNC(z80scc_device::rxb_w));
	m_scc->out_dtrb_callback().set(m_serial[1], FUNC(rs232_port_device::write_dtr));
	m_scc->out_rtsb_callback().set(m_serial[1], FUNC(rs232_port_device::write_rts));
	m_scc->out_txdb_callback().set(m_serial[1], FUNC(rs232_port_device::write_txd));

	/*
	 * non-volatile memory: xx yy mm mm mm mm mm mm ii ii ii ii
	 *
	 * where (bit order reversed):
	 *   xx yy - checksum?
	 *   mm    - mac address
	 *   ii    - IP address
	 */
	MM58167(config, m_rtc, 32.768_kHz_XTAL);

	EEPROM_X24C44_16BIT(config, m_nov[0]); // X2443P
	m_nov[0]->do_callback().set(FUNC(tekigw_state_base::nov_do));

	AM9516(config, m_dma, 16_MHz_XTAL / 4); // TODO: 4132 has Am9516A-8
	m_dma->set_addrmap(am9516_device::SYSTEM_MEM, &tekigw_state_base::dma_map);
	m_dma->set_addrmap(am9516_device::NORMAL_MEM, &tekigw_state_base::dma_map);

	// TODO: channel B fdc

	I82586(config, m_lan, 16_MHz_XTAL / 2);
	m_lan->out_irq_cb().set(m_icu, FUNC(ns32202_device::ir_w<11>));
}

void tek6100_state::tek6130(machine_config &config)
{
	tekigw_state_base::common_config(config);

	m_cpu->set_addrmap(0, &tek6100_state::cpu_map<0>);
	m_cpu->set_addrmap(ns32000::ST_EIM, &tek6100_state::cpu_map<ns32000::ST_EIM>);

	m_lan->set_addrmap(0, &tek6100_state::lan_map);

	/*
	 * Interrupt sources
	 *  0 interval clock
	 *  1 serial
	 *  2 hard disk
	 *  3 display
	 *  4 backplane slot 1
	 *  5 backplane slot 2
	 *  6 backplane slot 3
	 *  7 backplane slot 4
	 *  8 backplane slot 5
	 *  9 backplane slot 6
	 * 10 gpib
	 * 11 local area network
	 * 12 power switch
	 * 13 flexible disk
	 * 14 network level
	 * 15 software clock
	 */

	WD1770(config, m_fdc, 8_MHz_XTAL); // clock?
	m_fdc->intrq_wr_callback().set(m_icu, FUNC(ns32202_device::ir_w<13>));
	m_fdc->drq_wr_callback().set(m_dma, FUNC(am9516_device::dreq_w<1>));
	FLOPPY_CONNECTOR(config, "fdc:0", "525dd", FLOPPY_525_DD, true, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	WD1010(config, m_hdc, 20_MHz_XTAL / 4);
	m_hdc->out_intrq_callback().set(m_icu, FUNC(ns32202_device::ir_w<2>));
	m_hdc->in_data_callback().set(FUNC(tek6100_state::buf_r<u8>));
	m_hdc->out_data_callback().set(FUNC(tek6100_state::buf_w<u8>));
	m_hdc->out_bcr_callback().set([this](int state) { if (state) m_hdc_ptr = 0; });

	/*
	 * Supported hard drives (c,h,s,bps):
	 *  - Micropolis 1302 (830,3,16,512)
	 *  - Micropolis 1304 (830,6,16,512)
	 *  - Maxtor XT-1105 (918,11,16,512)
	 */
	HARDDISK(config, m_hdd, 0);

	TMS9914(config, m_gpib, 20_MHz_XTAL / 4);
	m_gpib->int_write_cb().set(m_icu, FUNC(ns32202_device::ir_w<10>));
	m_gpib->dio_read_cb().set(IEEE488_TAG, FUNC(ieee488_device::dio_r));
	m_gpib->dio_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_dio_w));
	m_gpib->eoi_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_eoi_w));
	m_gpib->dav_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_dav_w));
	m_gpib->nrfd_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_nrfd_w));
	m_gpib->ndac_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ndac_w));
	m_gpib->ifc_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ifc_w));
	m_gpib->srq_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_srq_w));
	m_gpib->atn_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_atn_w));
	m_gpib->ren_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_ren_w));

	ieee488_device &ieee(IEEE488(config, IEEE488_TAG));
	ieee.eoi_callback().set(m_gpib, FUNC(tms9914_device::eoi_w));
	ieee.dav_callback().set(m_gpib, FUNC(tms9914_device::dav_w));
	ieee.nrfd_callback().set(m_gpib, FUNC(tms9914_device::nrfd_w));
	ieee.ndac_callback().set(m_gpib, FUNC(tms9914_device::ndac_w));
	ieee.ifc_callback().set(m_gpib, FUNC(tms9914_device::ifc_w));
	ieee.srq_callback().set(m_gpib, FUNC(tms9914_device::srq_w));
	ieee.atn_callback().set(m_gpib, FUNC(tms9914_device::atn_w));
	ieee.ren_callback().set(m_gpib, FUNC(tms9914_device::ren_w));

	IEEE488_SLOT(config, "ieee_rem", 0, remote488_devices, nullptr);

	// TODO: graphics board disabled for now
	if (false)
	{
		NS32016(config, m_dpu_cpu, 0); // 8'000'000);
		m_dpu_cpu->set_addrmap(0, &tek6100_state::dpu_cpu_map<0>);

		ns32081_device &dpu_fpu(NS32081(config, "dpu_fpu", 8'000'000));
		m_dpu_cpu->set_fpu(dpu_fpu);

		ns32202_device &dpu_icu(NS32202(config, "dpu_icu", 20'000));
		dpu_icu.out_int().set_inputline(m_dpu_cpu, INPUT_LINE_IRQ0).invert();

		i8744_device &dpu_mcu(I8744(config, "dpu_mcu", 0)); // 10'000'000)); // 8744H-10
		(void)dpu_mcu;

		mc6845_device &dpu_crtc(SY6845E(config, "dpu_crtc", 25.2_MHz_XTAL)); // SYP6845EA
		dpu_crtc.set_screen("dpu_screen");

		// 32 x HM50256-15 (256K x 1 DRAM) total 1MiB
		RAM(config, m_dpu_cram);
		m_dpu_cram->set_default_size("1MiB");

		// 32 x IMS2620P-12 (16K x 4 DRAM) total 256KiB
		RAM(config, m_dpu_vram);
		m_dpu_vram->set_default_size("256KiB");

		// 13-inch 640x480 60Hz 4-bit color
		// 15-inch 640x480 60Hz monochrome
		screen_device &dpu_screen(SCREEN(config, "dpu_screen", SCREEN_TYPE_RASTER));
		dpu_screen.set_raw(25200000, 800, 0, 640, 525, 0, 480);
		dpu_screen.set_screen_update(FUNC(tek6100_state::screen_update));
	}
}

static void scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
}

void tek4132_state::tek4132(machine_config &config)
{
	tekigw_state_base::common_config(config);

	m_cpu->set_addrmap(0, &tek4132_state::cpu_map<0>);
	m_cpu->set_addrmap(ns32000::ST_EIM, &tek4132_state::cpu_map<ns32000::ST_EIM>);
	//m_cpu->set_addrmap(ns32000::ST_ODT, &tek4132_state::cpu_map<ns32000::ST_ODT>);

	m_lan->set_addrmap(0, &tek4132_state::lan_map);

	m_scc->configure_channels(3'686'400, 0, 3'686'400, 0);

	/*
	 * Interrupt sources
	 *  0 interval clock
	 *  1 serial
	 *  2 display
	 *  3 serial dma
	 *  4 scsi controller
	 *  5 backplane slot 1
	 *  6 backplane slot 2
	 *  7 backplane slot 3
	 *  8 backplane slot 4
	 *  9 backplane slot 5
	 * 10 backplane slot 6
	 * 11 local area network
	 * 12 power switch interrupt
	 * 13 time of day clock
	 * 14 network level
	 * 15 software clock
	 */

	m_rtc->irq().set(m_icu, FUNC(ns32202_device::ir_w<13>)).invert(); // device emulation has inverted polarity

	EEPROM_X24C44_16BIT(config, m_nov[1]); // X2443P
	m_nov[1]->do_callback().set(FUNC(tek4132_state::nov_do));

	AM9517A(config, m_sdma, 4'000'000); // D8237A-5 (clock?)
	m_sdma->dreq_active_low();

	INPUT_MERGER_ALL_HIGH(config, m_sirq);
	//m_sirq->output_handler().set(m_icu, FUNC(ns32202_device::ir_w<4>));

	NSCSI_BUS(config, m_scsibus);
	NSCSI_CONNECTOR(config, "scsi:0", scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:1", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr5385", NCR5385).clock(10'000'000).machine_config(
		[this](device_t *device)
		{
			ncr5385_device &adapter = downcast<ncr5385_device &>(*device);

			//adapter.irq().set(m_sirq, FUNC(input_merger_all_high_device::in_w<1>));
			adapter.irq().set(m_icu, FUNC(ns32202_device::ir_w<4>));
			adapter.dreq().set(m_dma, FUNC(am9516_device::dreq_w<0>)).invert();

		});

	m_dma->flyby_byte_r<0>().set(":scsi:7:ncr5385", FUNC(ncr5385_device::dma_r));
	m_dma->flyby_byte_w<0>().set(":scsi:7:ncr5385", FUNC(ncr5385_device::dma_w));
}

static INPUT_PORTS_START(tekigw)
	PORT_START("config")
	PORT_DIPNAME(0x80, 0x00, "Mode") PORT_DIPLOCATION("SW:1")
	PORT_DIPSETTING(   0x00, "Normal")
	PORT_DIPSETTING(   0x80, "Service")
	PORT_DIPNAME(0x60, 0x20, "Console") PORT_DIPLOCATION("SW:3,2")
	PORT_DIPSETTING(   0x00, "Display")
	PORT_DIPSETTING(   0x20, "9600 baud RS-232-C terminal (port 0)")
	PORT_DIPSETTING(   0x40, "1200 baud RS-232-C modem/terminal (port 1)")
	PORT_DIPSETTING(   0x60, "300 baud model/terminal (port 1)")
	PORT_DIPNAME(0x10, 0x00, "Boot") PORT_DIPLOCATION("SW:4")
	PORT_DIPSETTING(   0x00, "UTek")
	PORT_DIPSETTING(   0x10, "File")
	PORT_DIPNAME(0x0c, 0x00, "Boot Device") PORT_DIPLOCATION("SW:6,5")
	PORT_DIPSETTING(   0x00, "Autoboot")
	PORT_DIPSETTING(   0x04, "Hard disk")
	PORT_DIPSETTING(   0x08, "Diskette drive")
	PORT_DIPSETTING(   0x0c, "LAN port")
	PORT_DIPNAME(0x03, 0x00, "Diagnostic") PORT_DIPLOCATION("SW:8,7")
	PORT_DIPSETTING(   0x00, "0")
	PORT_DIPSETTING(   0x01, "1")
	PORT_DIPSETTING(   0x02, "2")
	PORT_DIPSETTING(   0x03, "3")
INPUT_PORTS_END

ROM_START(tek6130)
	ROM_REGION16_LE(0x8000, "kernel", 0)
	ROM_SYSTEM_BIOS(0, "5.107", "5.107 85/02/20 13:51:51")
	ROMX_LOAD("160-2921-02.bin", 0x00000, 0x4000, CRC(44688258) SHA1(645ceaa6ea9ef76ba8b557dec0387fce73f669c0), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("160-2922-02.bin", 0x00001, 0x4000, CRC(5d84dff3) SHA1(08e74f0157a5c002397b3c8644613dc0ae8355a0), ROM_BIOS(0) | ROM_SKIP(1))

	ROM_SYSTEM_BIOS(1, "5.105", "5.105 85/02/16 11:03:32")
	ROMX_LOAD("pwr_up__5.105__adb_lo.bin", 0x00000, 0x4000, CRC(429a6a4a) SHA1(5ec5a045ec14a1b546d75e9406f0ad21163298f6), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("pwr_up__5.105__adb_hi.bin", 0x00001, 0x4000, CRC(1ad7a762) SHA1(98472c30f1148c43f43d2131ebc9644d8ebf376a), ROM_BIOS(1) | ROM_SKIP(1))

	ROM_REGION16_LE(0x8000, "dpu", 0)
	ROMX_LOAD("l__sdpu__1.73.bin", 0x00000, 0x4000, CRC(cd1be127) SHA1(9c16ab7e8b195e9ae660288b4b26566581a029e5), ROM_SKIP(1))
	ROMX_LOAD("h__sdpu__1.73.bin", 0x00001, 0x4000, CRC(1adf3130) SHA1(966f839de0b10aff3ffc6af64d94e4b19ece52c7), ROM_SKIP(1))

	ROM_REGION(0x1000, "dpu_mcu", 0)
	ROM_LOAD("2960-01.bin", 0x0, 0x1000, CRC(5974eaec) SHA1(362cdfb7be162636429d614807b9066ca8bbab28))
ROM_END

ROM_START(tek4132)
	ROM_REGION16_LE(0x10000, "kernel", 0)
	ROM_SYSTEM_BIOS(0, "1.7", "5.107 85/02/20 13:51:51")
	ROMX_LOAD("160-4018-00__v1.7.u3130", 0x00000, 0x8000, CRC(cdf154ab) SHA1(381d012b0ec85db03d8cb164ef22f92e33d40b57), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("160-4019-00__v1.7.u3140", 0x00001, 0x8000, CRC(5fe870b1) SHA1(369507763e37e947bbbfde080ba3d83bb6f3a7bf), ROM_BIOS(0) | ROM_SKIP(1))
ROM_END
} // anonymous namespace

/*   YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS          INIT  COMPANY      FULLNAME  FLAGS */
COMP(1984, tek6130, 0,      0,      tek6130, tekigw, tek6100_state, init, "Tektronix", "6130",   MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)
COMP(1984, tek4132, 0,      0,      tek4132, tekigw, tek4132_state, init, "Tektronix", "4132",   MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)



tekxp33x.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Justin Kerk
/***************************************************************************

    Tektronix TekXpress XP33x series X terminals

    Skeleton driver.

****************************************************************************/

/*

    TODO:

    - everything

    Technical info:
    https://www.linux-mips.org/wiki/Tektronix_TekXPress_XP338
    https://web.archive.org/web/20061013084616/http://tekxp-linux.hopto.org/pmwiki/pmwiki.php/Hardware/XP33x
    http://bio.gsi.de/DOCS/NCD/xp.html

    CPU: IDT 79R3052E (MIPS I R3000 embedded core. Big Endian. With MMU, without FPU.)
    Graphic Chipset: Texas Instruments (34020AGBL-40) TI 34010 (TIGA)
    Graphic RAMDAC: Brooktree BT458LPJ135 (Supported in XFree86)
    RAM: 4MB onboard, upgradeable to 52MB, 3 slots for 5V FPM NoParity SIMMs
    ROM: 256Kb, 2 x 27c1024 (64Kx16), with BootMonitor
    Peripherals:
        1x Ethernet Port (AUI and UTP) -- AMD AM79C98 Chipset
        2x Serial Ports -- Philips SCC2692 Dual UART chipset
        Keyboard and Mouse controller is a generic PC-Style i8742 with Phoenix BIOS.
        1x "ERGO" Port (Combined Monitor, Keyboard, Mouse)
    16K (2K*8) EEPROM is SEEQ NQ2816A-250, AT28C16 compatible. Contains Boot Monitor settings. The content is compressed.
    Micron MT56C0816 - ??? Flash

*/


#include "emu.h"
#include "cpu/mips/mips1.h"
#include "cpu/tms34010/tms34010.h"
#include "emupal.h"
#include "screen.h"


namespace {

#define SCREEN_TAG "screen"

class tekxp330_state : public driver_device
{
public:
	tekxp330_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag) { }

	void tekxp330(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;

	virtual void video_start() override ATTR_COLD;
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void cpu_map(address_map &map) ATTR_COLD;
	void tms_map(address_map &map) ATTR_COLD;
};

/* Memory Maps */

void tekxp330_state::cpu_map(address_map &map)
{
	map(0x00000000, 0x003fffff).ram();
	map(0x1fc00000, 0x1fdfffff).rom().region("maincpu", 0);
}

void tekxp330_state::tms_map(address_map &map)
{
}

/* Input Ports */

static INPUT_PORTS_START( tekxp330 )
INPUT_PORTS_END

/* Video */

void tekxp330_state::video_start()
{
}

uint32_t tekxp330_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

/* Machine Initialization */

void tekxp330_state::machine_start()
{
}

/* Machine Driver */

void tekxp330_state::tekxp330(machine_config &config)
{
	/* basic machine hardware */
	r3052e_device &maincpu(R3052E(config, "maincpu", XTAL(20'000'000))); /* IDT 79R3052E, clock unknown */
	maincpu.set_endianness(ENDIANNESS_BIG);
	maincpu.set_addrmap(AS_PROGRAM, &tekxp330_state::cpu_map);

	tms34010_device &tms(TMS34010(config, "tms", XTAL(40'000'000))); /* clock unknown */
	tms.set_addrmap(AS_PROGRAM, &tekxp330_state::tms_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(tekxp330_state::screen_update));
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);

	PALETTE(config, "palette").set_entries(64);
}

/* ROMs */

ROM_START( tekxp330 )
	ROM_REGION( 0x200000, "maincpu", 0 )
	/* TekXpress XP300 Boot ROM V1.0  Thu Feb 6 14:50:16 PST 1992 */
	ROM_LOAD32_DWORD( "xp300.bin", 0x000000, 0x200000, CRC(9a324588) SHA1(a6e10275f8215f446be91128bab4c643693da653) )
ROM_END

} // anonymous namespace


/* System Drivers */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY      FULLNAME           FLAGS
COMP( 1992, tekxp330, 0,      0,      tekxp330, tekxp330, tekxp330_state, empty_init, "Tektronix", "TekXpress XP330", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



teleray10.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Preliminary driver for Teleray 10 series terminal.

****************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/m6502/m6502.h"
#include "machine/74259.h"
#include "machine/input_merger.h"
#include "machine/scn_pci.h"
#include "machine/timer.h"
#include "sound/spkrdev.h"
#include "screen.h"
#include "speaker.h"


namespace {

class teleray10_state : public driver_device
{
public:
	teleray10_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mainirq(*this, "mainirq")
		, m_outreg(*this, "outreg")
		, m_screen(*this, "screen")
		, m_bell(*this, "bell")
		, m_pci(*this, "pci")
		, m_serialio(*this, "serialio")
		, m_peripheral(*this, "peripheral")
		, m_scratchpad(*this, "scratchpad")
		, m_displayram(*this, "displayram")
		, m_chargen(*this, "chargen")
		, m_keys(*this, "KA%X", 0U)
		, m_swmisc(*this, "SWMISC")
	{
	}

	void teleray10(machine_config &config);

	void key_interrupt_w(int state);
	int timer_expired_r();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	TIMER_DEVICE_CALLBACK_MEMBER(timer_expired);
	TIMER_CALLBACK_MEMBER(bell_toggle);
	void bell_update();

	void wide_mode_w(int state);
	void bell_off_w(int state);
	void reset_timer_w(int state);

	void scratchpad_w(offs_t offset, u8 data);
	u8 serial_io_r(offs_t offset);
	void serial_io_w(offs_t offset, u8 data);
	u8 kb_r(offs_t offset);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<input_merger_device> m_mainirq;
	required_device<ls259_device> m_outreg;
	required_device<screen_device> m_screen;
	required_device<speaker_sound_device> m_bell;
	required_device<scn2651_device> m_pci;
	required_device<rs232_port_device> m_serialio;
	required_device<rs232_port_device> m_peripheral;
	required_shared_ptr<u8> m_scratchpad;
	required_shared_ptr<u8> m_displayram;
	required_region_ptr<u8> m_chargen;
	required_ioport_array<16> m_keys;
	required_ioport m_swmisc;

	emu_timer *m_bell_timer;

	u8 m_topr;
	bool m_timer_expired;
};


void teleray10_state::machine_start()
{
	m_bell_timer = timer_alloc(FUNC(teleray10_state::bell_toggle), this);

	m_topr = 0;
	m_timer_expired = false;

	// HACK: force continuous CTS
	subdevice<input_merger_device>("cts")->in_w<2>(0);

	save_item(NAME(m_topr));
	save_item(NAME(m_timer_expired));
}

void teleray10_state::machine_reset()
{
	// Pins 5 (CTS), 6 (DSR), 8 (DCD) are pulled up to +12V on peripheral connector
	m_peripheral->write_rts(0);
	m_peripheral->write_dtr(0);
}


TIMER_DEVICE_CALLBACK_MEMBER(teleray10_state::timer_expired)
{
	// arbitrary VBLANK condition
	if (param >= 240 && param < 290)
	{
		int height = BIT(m_swmisc->read(), 2) ? 310 : 372;
		if (height != m_screen->height())
			m_screen->configure(1000, height, m_screen->visible_area(), attotime::from_ticks(1000 * height, 18.6_MHz_XTAL).as_attoseconds());
	}

	if (m_outreg->q7_r())
	{
		m_timer_expired = true;
		m_mainirq->in_w<1>(1);
	}
}

TIMER_CALLBACK_MEMBER(teleray10_state::bell_toggle)
{
	bell_update();
}

void teleray10_state::bell_update()
{
	int vpos = m_screen->vpos();
	int line = vpos % 12;
	if (line < 8)
	{
		m_bell->level_w(1);
		m_bell_timer->adjust(m_screen->time_until_pos(vpos - line + 8));
	}
	else
	{
		m_bell->level_w(0);
		if (vpos - line + 12 < m_screen->height())
			m_bell_timer->adjust(m_screen->time_until_pos(vpos - line + 12));
		else
			m_bell_timer->adjust(m_screen->time_until_pos(0));
	}
}


u32 teleray10_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	u16 offset = 0;

	for (unsigned y = 0; y < 24; y++)
	{
		for (unsigned scan = 0; scan < 12; scan++)
		{
			u32 *px = &bitmap.pix(y * 12 + scan);
			for (unsigned x = 0; x < 80; x++)
			{
				u8 ch = m_displayram[offset + x];
				u8 dots = m_chargen[u16(ch) << 4 | scan] & 0x7f;
				for (int i = 0; i < 10; i++)
				{
					*px++ = BIT(dots, 6) ? rgb_t::white() : rgb_t::black();
					dots <<= 1;
				}
			}
		}
		offset += 80;
	}
	return 0;
}


void teleray10_state::key_interrupt_w(int state)
{
	m_maincpu->set_input_line(m6502_device::NMI_LINE, state ? CLEAR_LINE : ASSERT_LINE);
}

void teleray10_state::wide_mode_w(int state)
{
}

void teleray10_state::bell_off_w(int state)
{
	if (state)
	{
		m_bell->level_w(0);
		m_bell_timer->adjust(attotime::never);
	}
	else
		bell_update();
}

void teleray10_state::reset_timer_w(int state)
{
	if (!state)
	{
		m_timer_expired = false;
		m_mainirq->in_w<1>(0);
	}
}

int teleray10_state::timer_expired_r()
{
	return m_timer_expired;
}

void teleray10_state::scratchpad_w(offs_t offset, u8 data)
{
	// Output control register and top of page register overlap with scratchpad RAM
	if (offset < 0x10)
		m_outreg->write_bit(~offset & 7, BIT(data, 0));
	else if (offset < 0x20)
		m_topr = data;

	m_scratchpad[offset] = data;
}

u8 teleray10_state::serial_io_r(offs_t offset)
{
	return m_pci->read(~offset & 3);
}

void teleray10_state::serial_io_w(offs_t offset, u8 data)
{
	m_pci->write(~offset & 3, data);
}

u8 teleray10_state::kb_r(offs_t offset)
{
	// DM74154 1 of 16 decoder on keyboard
	return m_keys[offset]->read();
}

void teleray10_state::mem_map(address_map &map)
{
	map.global_mask(0x9fff);
	map(0x0000, 0x000f).r(FUNC(teleray10_state::kb_r));
	map(0x0010, 0x0010).mirror(0xe).portr("SW1B");
	map(0x0011, 0x0011).mirror(0xe).portr("SW1A");
	map(0x0020, 0x0020).mirror(0xe).portr("SW2B");
	map(0x0021, 0x0021).mirror(0xe).portr("SW2A");
	map(0x0030, 0x0033).mirror(0xc).rw(FUNC(teleray10_state::serial_io_r), FUNC(teleray10_state::serial_io_w));
	map(0x0040, 0x03ff).ram().share("scratchpad").w(FUNC(teleray10_state::scratchpad_w));
	map(0x0400, 0x0bff).ram().share("displayram");
	// $0C00 to $0FFF = PROM #5 @ 1L (empty socket here)
	map(0x9000, 0x9fff).rom().region("proms", 0);
}


static INPUT_PORTS_START(teleray10)
	PORT_START("KA0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0') PORT_CHAR(')') PORT_CODE(KEYCODE_0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR('@') PORT_CODE(KEYCODE_2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR('$') PORT_CODE(KEYCODE_4)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR('^') PORT_CODE(KEYCODE_6)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR('&') PORT_CODE(KEYCODE_7)

	PORT_START("KA1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('8') PORT_CHAR('*') PORT_CODE(KEYCODE_8)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('9') PORT_CHAR('(') PORT_CODE(KEYCODE_9)
	PORT_BIT(0xfc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KA2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(' ') PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR(0x01) PORT_CODE(KEYCODE_A)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR(0x02) PORT_CODE(KEYCODE_B)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR(0x03) PORT_CODE(KEYCODE_C)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR(0x04) PORT_CODE(KEYCODE_D)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(0x05) PORT_CODE(KEYCODE_E)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR(0x06) PORT_CODE(KEYCODE_F)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR(0x07) PORT_CODE(KEYCODE_G)

	PORT_START("KA3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('h') PORT_CHAR('H') PORT_CODE(KEYCODE_H)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('i') PORT_CHAR('I') PORT_CODE(KEYCODE_I)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('j') PORT_CHAR('J') PORT_CODE(KEYCODE_J)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR(0x0b) PORT_CODE(KEYCODE_K)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR(0x0c) PORT_CODE(KEYCODE_L)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_M)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(0x0e) PORT_CODE(KEYCODE_N)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(0x0f) PORT_CODE(KEYCODE_O)

	PORT_START("KA4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR(0x10) PORT_CODE(KEYCODE_P)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('q') PORT_CHAR('Q') PORT_CHAR(0x11) PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR(0x12) PORT_CODE(KEYCODE_R)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR(0x13) PORT_CODE(KEYCODE_S)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(0x14) PORT_CODE(KEYCODE_T)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(0x15) PORT_CODE(KEYCODE_U)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR(0x16) PORT_CODE(KEYCODE_V)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR(0x17) PORT_CODE(KEYCODE_W)

	PORT_START("KA5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(0x18) PORT_CODE(KEYCODE_X)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR(0x19) PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(0x1a) PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('[') PORT_CHAR('{') PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\\') PORT_CHAR('|') PORT_CHAR(0x1c) PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(']') PORT_CHAR('}') PORT_CHAR(0x1d) PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('-') PORT_CHAR('_') PORT_CODE(KEYCODE_MINUS)

	PORT_START("KA6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('`') PORT_CHAR('~') PORT_CHAR(0x1e) PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\'') PORT_CHAR('"') PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHAR(':') PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',') PORT_CHAR('<') PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('=') PORT_CHAR('+') PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('.') PORT_CHAR('>') PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('/') PORT_CHAR('?') PORT_CHAR(0x1f) PORT_CODE(KEYCODE_SLASH)

	PORT_START("KA7")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KA8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Back Space") PORT_CHAR(0x08) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab  Back Tab") PORT_CHAR(0x09) PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Line Feed") PORT_CHAR(0x0a) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Escape") PORT_CHAR(0x1b) PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CHAR(0x0d) PORT_CODE(KEYCODE_ENTER) // also called CR
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Break")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Rub Out") PORT_CHAR(0x7f) // also called DEL

	PORT_START("KA9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Clear EOL") PORT_CODE(KEYCODE_F8)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab Set  Tab Clr") PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Clear Page") PORT_CODE(KEYCODE_F6)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Clear EOP") PORT_CODE(KEYCODE_F7)

	PORT_START("KAA")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del Line") PORT_CODE(KEYCODE_F9)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Insrt Line") PORT_CODE(KEYCODE_F10)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del Char") PORT_CHAR(UCHAR_MAMEKEY(DEL)) PORT_CODE(KEYCODE_F11)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Insrt Char") PORT_CHAR(UCHAR_MAMEKEY(INSERT)) PORT_CODE(KEYCODE_F12)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Xmit Line")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Xmit Msg")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Xmit Page")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Print") PORT_CHAR(UCHAR_MAMEKEY(PRTSCR)) PORT_CODE(KEYCODE_PRTSCR)

	PORT_START("KAB")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Home LF") PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CODE(KEYCODE_UP)
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KAC")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD)) PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD)) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD)) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD)) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD)) PORT_CODE(KEYCODE_7_PAD)

	PORT_START("KAD")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD)) PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD)) PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x18, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad CR") PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Control") PORT_CHAR(UCHAR_SHIFT_2) PORT_CODE(KEYCODE_LCONTROL)

	PORT_START("KAE")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x3e, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Caps Lock") PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CHAR(UCHAR_SHIFT_1) PORT_CODE(KEYCODE_LSHIFT)

	PORT_START("KAF")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_TOGGLE PORT_NAME("Page") // also called Scroll
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_TOGGLE PORT_NAME("Block")
	PORT_BIT(0x3c, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_TOGGLE PORT_NAME("Monitor") // also called Xprnt
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_TOGGLE PORT_NAME("Local")

	PORT_START("KINT")
	PORT_BIT(1, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Interrupt") PORT_WRITE_LINE_MEMBER(FUNC(teleray10_state::key_interrupt_w))

	PORT_START("SW1A")
	PORT_DIPNAME(0x01, 0x00, "Xmit ETX") PORT_DIPLOCATION("7A:3")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x01, DEF_STR(On))
	PORT_DIPNAME(0x02, 0x00, "Xmit CSR") PORT_DIPLOCATION("7A:2")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x02, DEF_STR(On))
	PORT_DIPNAME(0x04, 0x04, "Character Bits") PORT_DIPLOCATION("6A:8")
	PORT_DIPSETTING(0x00, "7")
	PORT_DIPSETTING(0x04, "8")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNKNOWN) // "Reserved" jumper
	PORT_DIPNAME(0x30, 0x30, "Parity") PORT_DIPLOCATION("6A:6,5")
	PORT_DIPSETTING(0x00, "Even")
	PORT_DIPSETTING(0x10, "Odd")
	PORT_DIPSETTING(0x30, "None/High")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_MEMBER(FUNC(teleray10_state::timer_expired_r))
	PORT_DIPNAME(0x80, 0x00, "Stop Bits") PORT_DIPLOCATION("6A:7")
	PORT_DIPSETTING(0x00, "1")
	PORT_DIPSETTING(0x80, "2")

	PORT_START("SW1B")
	PORT_DIPNAME(0x0f, 0x0e, "Baud Rate") PORT_DIPLOCATION("6A:4,3,2,1")
	PORT_DIPSETTING(0x00, "50")
	PORT_DIPSETTING(0x01, "75")
	PORT_DIPSETTING(0x02, "110")
	PORT_DIPSETTING(0x03, "134.5")
	PORT_DIPSETTING(0x04, "150")
	PORT_DIPSETTING(0x05, "300")
	PORT_DIPSETTING(0x06, "600")
	PORT_DIPSETTING(0x07, "1200")
	PORT_DIPSETTING(0x08, "1800")
	PORT_DIPSETTING(0x09, "2000")
	PORT_DIPSETTING(0x0a, "2400")
	PORT_DIPSETTING(0x0b, "3600")
	PORT_DIPSETTING(0x0c, "4800")
	PORT_DIPSETTING(0x0d, "7200")
	PORT_DIPSETTING(0x0e, "9600")
	PORT_DIPSETTING(0x0f, "Reserved")
	PORT_DIPNAME(0x10, 0x10, "Half/Full Duplex") PORT_DIPLOCATION("7A:4")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x10, DEF_STR(On))
	PORT_DIPNAME(0x20, 0x00, "Right Margin Wrap") PORT_DIPLOCATION("7A:5")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x20, DEF_STR(On))
	PORT_DIPNAME(0x40, 0x00, "LF NL/CR NL") PORT_DIPLOCATION("7A:7")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x40, DEF_STR(On))
	PORT_DIPNAME(0x80, 0x00, "New Line") PORT_DIPLOCATION("7A:6")
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x80, DEF_STR(On))

	PORT_START("SW2A")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("SW2B")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("SWMISC")
	PORT_DIPNAME(0x01, 0x01, "Display Attributes") PORT_DIPLOCATION("7A:1")
	PORT_DIPSETTING(0x01, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPNAME(0x02, 0x02, "Inverse Display") PORT_DIPLOCATION("INVRS:1") // internal jumper near 4M
	PORT_DIPSETTING(0x02, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPNAME(0x04, 0x04, "Refresh Rate") PORT_DIPLOCATION("50Hz:1") // internal jumper near 2J
	PORT_DIPSETTING(0x00, "50 Hz")
	PORT_DIPSETTING(0x04, "60 Hz")
	PORT_DIPNAME(0x08, 0x08, "Serial Loop") PORT_DIPLOCATION("7A:8")
	PORT_DIPSETTING(0x08, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))
INPUT_PORTS_END


void teleray10_state::teleray10(machine_config &config)
{
	M6502(config, m_maincpu, 18.6_MHz_XTAL / 20); // 2 characters loaded during each ϕ1 cycle
	m_maincpu->set_addrmap(AS_PROGRAM, &teleray10_state::mem_map);

	TIMER(config, "timer").configure_scanline(FUNC(teleray10_state::timer_expired), "screen", 10, 12);

	INPUT_MERGER_ANY_HIGH(config, m_mainirq).output_handler().set_inputline(m_maincpu, m6502_device::IRQ_LINE);

	LS259(config, m_outreg); // Output Control Register @ 7D
	m_outreg->q_out_cb<0>().set("xmitser", FUNC(input_merger_device::in_w<0>)).invert();
	m_outreg->q_out_cb<1>().set("xmitperiph", FUNC(input_merger_device::in_w<1>)).invert();
	// Q2 officially "reserved"
	m_outreg->q_out_cb<3>().set("cts", FUNC(input_merger_device::in_w<0>));
	m_outreg->q_out_cb<4>().set(m_serialio, FUNC(rs232_port_device::write_dtr));
	m_outreg->q_out_cb<5>().set(FUNC(teleray10_state::wide_mode_w));
	m_outreg->q_out_cb<6>().set(FUNC(teleray10_state::bell_off_w));
	m_outreg->q_out_cb<7>().set(FUNC(teleray10_state::reset_timer_w));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(18.6_MHz_XTAL, 1000, 0, 800, 310, 0, 288); // 372 total lines in 50 Hz mode
	m_screen->set_screen_update(FUNC(teleray10_state::screen_update));

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_bell).add_route(ALL_OUTPUTS, "mono", 0.5);

	SCN2651(config, m_pci, 5.0688_MHz_XTAL); // Signetics 2651 or equivalent
	m_pci->rxrdy_handler().set(m_mainirq, FUNC(input_merger_device::in_w<0>));
	m_pci->txd_handler().set("xmitser", FUNC(input_merger_device::in_w<1>));
	m_pci->txd_handler().append("xmitperiph", FUNC(input_merger_device::in_w<0>));
	m_pci->rts_handler().set(m_serialio, FUNC(rs232_port_device::write_rts));

	RS232_PORT(config, m_serialio, default_rs232_devices, nullptr);
	m_serialio->rxd_handler().set(m_pci, FUNC(scn2651_device::rxd_w));
	m_serialio->cts_handler().set("cts", FUNC(input_merger_device::in_w<1>));
	m_serialio->dcd_handler().set(m_pci, FUNC(scn2651_device::dcd_w));

	RS232_PORT(config, m_peripheral, default_rs232_devices, nullptr);

	INPUT_MERGER_ANY_HIGH(config, "xmitser").output_handler().set(m_serialio, FUNC(rs232_port_device::write_txd));
	INPUT_MERGER_ANY_HIGH(config, "xmitperiph").output_handler().set(m_peripheral, FUNC(rs232_port_device::write_txd));
	INPUT_MERGER_ALL_HIGH(config, "cts").output_handler().set(m_pci, FUNC(scn2651_device::cts_w));
}


ROM_START(teleray10)
	ROM_REGION(0x1000, "proms", 0) // 2708
	ROM_LOAD("a053730_4g.1g", 0x000, 0x400, CRC(6b7cb60f) SHA1(3031a8c2eb20f4345dc7308e073a7ac0ebeefab0))
	ROM_LOAD("a053730_3g.1j", 0x400, 0x400, CRC(634d7a31) SHA1(2cccc3b199e3cd79b3f6b73a94f9349363a102e1))
	ROM_LOAD("a053730_2g.1k", 0x800, 0x400, CRC(dd8174c1) SHA1(605f53ed348b87f2bb3fa25344e68ad59dd21159))
	ROM_LOAD("a053730_1g.1l", 0xc00, 0x400, CRC(01283a2e) SHA1(09edee6ded6eb15932467f226b9a97e07304c99e))

	ROM_REGION(0x800, "chargen", 0)
	ROM_LOAD("ka53895.7n", 0x000, 0x800, CRC(437cf3cc) SHA1(4da7eea06b6b5f6c0a3d995b727d6f8d14bb8b30))
ROM_END

} // anonymous namespace


COMP(1978, teleray10, 0, 0, teleray10, teleray10, teleray10_state, empty_init, "Research Inc.", "Teleray Model 10", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS)



telex1192.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/************************************************************************************************************

    Skeleton driver for Memorex Telex 1192 IBM-compatible video display terminal.

************************************************************************************************************/

#include "emu.h"
#include "cpu/bcp/dp8344.h"
#include "machine/nvram.h"
//#include "screen.h"


namespace {

class telex1192_state : public driver_device
{
public:
	telex1192_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_bcp(*this, "bcp")
		, m_datarom(*this, "datarom")
	{
	}

	void telex1192(machine_config &config);

private:
	u16 data_r(offs_t offset);

	void inst_map(address_map &map) ATTR_COLD;
	void data_map(address_map &map) ATTR_COLD;

	required_device<dp8344_device> m_bcp;
	required_region_ptr<u8> m_datarom;
};


u16 telex1192_state::data_r(offs_t offset)
{
	// FIXME: not how this code is actually loaded (at least addresses are wrong)
	return m_datarom[offset * 2] | m_datarom[offset * 2 + 1] << 8;
}

void telex1192_state::inst_map(address_map &map)
{
	map(0x0000, 0x1fff).rom().region("program", 0);
	map(0xc000, 0xcfff).r(FUNC(telex1192_state::data_r));
}

void telex1192_state::data_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).ram();
	map(0xa000, 0xa7ff).ram().share("nvram");
}


static INPUT_PORTS_START(telex1192)
INPUT_PORTS_END


void telex1192_state::telex1192(machine_config &config)
{
	DP8344A(config, m_bcp, 18.8696_MHz_XTAL);
	m_bcp->set_addrmap(AS_PROGRAM, &telex1192_state::inst_map);
	m_bcp->set_addrmap(AS_DATA, &telex1192_state::data_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // DS1220Y
}


// XTALs: 18.8696 (Y1), 6.1440 MHz (Y2), 46.2 MHz (Y3), 24.8443 MHz (Y4), 28.3046 MHz (Y5), 40.993430 MHz (Y6)
// CPU: NS DP8344AV BCP
// Gate array: Toshiba TC110G17AT
// RAM: 3x Motorola MCM6264P45, Toshiba TC55257APL-10, Dallas DS1220Y-150
ROM_START(telex1192)
	ROM_REGION16_LE(0x4000, "program", 0)
	ROM_LOAD16_BYTE("206252-003.u7", 0x0000, 0x2000, CRC(3632b762) SHA1(a45dbcd96485ad87c60257e95dc58f31d1e4ddee))
	ROM_LOAD16_BYTE("206251-003.u8", 0x0001, 0x2000, CRC(2852d830) SHA1(947612957daa441596a547f23c526d20306e6e0d))

	ROM_REGION(0x10000, "datarom", 0)
	ROM_LOAD("206253-003.u6", 0x00000, 0x10000, CRC(8d7d9356) SHA1(0b2eda8b04b2a6651f4fbf4cd6cdd129a70110ee))
ROM_END

} // anonymous namespace


COMP(1989, telex1192, 0, 0, telex1192, telex1192, telex1192_state, empty_init, "Memorex Telex", "Telex 1192", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



telex274.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/************************************************************************************************************

    Skeleton driver for Telex 274 IBM 3274-compatible coaxial controllers.

    The three main boards have the following components identified by PCB silkscreening and/or serial
    numbers:

    COMM IIS

        U1      Am27128A(?)-2DC         ODD (203981-053)
        U2      Am27128A(?)-2DC         EVEN (203982-053)
        U3      NEC D4361C-70           ODD PARITY
        U9      HM62256LP-12            ODD
        U10     HM62256LP-12            EVEN
        U11     NEC D4361C-70           EVEN PARITY
        U13     unknown 64-pin SDIP     DMA
        U45     PAL20L8ANC              ADD DECODE PAL (205264-002)
        U56     SCN68000CAN64           CPU
        U81     10.000000MHz            10 MHZ CLK
        U84     4.0000MHz               4 MHZ CLK
        U98     P8274                   COMM CHIP
        U99     Am9513APC               TIMER

    16 PORT COAX CONTROLLER

        U1      M5M4464P-12             BANK 0 D8:11
        U2      M5M4464P-12             BANK 1 D8:11
        U3      M5M4464P-12             BANK 0 D12:15
        U4      M5M4464P-12             BANK 1 D12:15
        U6      S 74F245N               BUF D8:15
        U7      S 74F245N               BUF D0:7
        U12     M5M4464P-12             BANK 0 D0:3
        U13     M5M4464P-12             BANK 1 D0:3
        U14     M5M4464P-12             BANK 0 D4:7
        U15     M5M4464P-12             BANK 1 D4:7
        U25     KM4164B-12              BANK 0 LOW
        U26     KM4164B-12              BANK 1 LOW
        U27     KM4164B-12              BANK 0 HIGH
        U28     KM4164B-12              BANK 1 HIGH
        U30     SN74LS195AN             WATCHDOG TIMER
        U38     AMPAL16R8PC             (205263-002)
        U41     PAL16R4ACN              (204143-02)
        U44     S 74LS138N              STATE DECODER
        U48     PAL20L8ANC              MEMORY DECODER (205264-002)
        U64/89  PAL16R4ACN              (204140-003)
        U65/90  PAL16R4ACN              (204141-003)
        U66     TC17G022AP-0091         DEV 0-7 COAX
        U67/102 PAL16L8ANC              (204142-001)
        U68     N82S129N                (204570-002)
        U69     SN74LS273N              MISC PORT OUT
        U83     S 74LS541N              MISC PORT IN
        U84/107 S 74LS541N              EXEC FIFO DRIVERS
        U101    TC17G022AP-0091         DEV 8-15 COAX

    EXAM BD  SS-B

        U1/16   TMS 4464-15NL           D12:D15
        U2/17   TMS 4464-15NL           D8:D11
        U3/18   TMS 4464-15NL           D4:D7
        U4/19   TMS 4464-15NL           D0:D3
        U41     AMPAL16R8PC             MEM CONTROL (205261-002)
        U62     PAL20L8ANC              MEM DECODE (205264-002)
        U85     AMPAL16R8PC             MEM CONTROL (205263-002)
        U92     PAL16R8BNC              BUS ARB (205266-002)
        U105    HYB 41256-15            D15
        U106    HYB 41256-15            D14
        U107    HYB 41256-15            D13
        U108    HYB 41256-15            D12
        U109    HYB 41256-15            D11
        U110    HYB 41256-15            D10
        U113    MK48Z02BU-25            (0678A)
        U120    HYB 41256-15            D9
        U121    HYB 41256-15            D8
        U122    HYB 41256-15            D7
        U123    HYB 41256-15            D6
        U124    HYB 41256-15            D5
        U125    HYB 41256-15            D4
        U134    HYB 41256-15            D3
        U135    HYB 41256-15            D2
        U136    HYB 41256-15            D1
        U137    HYB 41256-15            D0
        U138    HYB 41256-15            HI PAR
        U139    HYB 41256-15            LO PAR

    While each of these boards has a CPU, program ROMs are present only on the "COMM" board. Programs for
    the other two boards should be loaded off floppy disk through the WD2797A-PL FDC which resides on the
    front panel. There are also no visible oscillators on this front panel or the "EXAM BD" and only one
    on the "COAX" board (18.8696MHz, likely used to generate data clocks for the protocol gate arrays).

************************************************************************************************************/

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "machine/am9513.h"
#include "machine/z80sio.h"
//#include "machine/wd_fdc.h"


namespace {

class telex274_state : public driver_device
{
public:
	telex274_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_coaxcpu(*this, "coaxcpu")
		, m_examcpu(*this, "examcpu")
	{
	}

	void telex274(machine_config &config);

private:
	void main_mem(address_map &map) ATTR_COLD;
	void coax_mem(address_map &map) ATTR_COLD;
	void exam_mem(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<cpu_device> m_coaxcpu;
	required_device<cpu_device> m_examcpu;
};


void telex274_state::main_mem(address_map &map)
{
	map(0x000000, 0x007fff).rom().region("program", 0);
	map(0x008000, 0x01ffff).ram();
	map(0x080000, 0x09ffff).ram();
	map(0xff811e, 0xff811f).nopr();
	map(0xff8810, 0xff8817).rw("mpsc", FUNC(i8274_device::cd_ba_r), FUNC(i8274_device::cd_ba_w)).umask16(0x00ff);
	map(0xff8818, 0xff881b).rw("maintimer", FUNC(am9513a_device::read8), FUNC(am9513a_device::write8)).umask16(0x00ff);
}

void telex274_state::coax_mem(address_map &map)
{
}

void telex274_state::exam_mem(address_map &map)
{
}


static INPUT_PORTS_START(telex274)
INPUT_PORTS_END


void telex274_state::telex274(machine_config &config)
{
	M68000(config, m_maincpu, 10_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &telex274_state::main_mem);

	am9513a_device &maintimer(AM9513A(config, "maintimer", 4_MHz_XTAL));
	maintimer.out4_cb().set("mpsc", FUNC(i8274_device::rxca_w)); // ?
	maintimer.out4_cb().append("mpsc", FUNC(i8274_device::txca_w)); // ?
	maintimer.out5_cb().set("mpsc", FUNC(i8274_device::rxtxcb_w)); // ?

	I8274(config, "mpsc", 4'000'000);

	M68000(config, m_coaxcpu, 10'000'000);
	m_coaxcpu->set_addrmap(AS_PROGRAM, &telex274_state::coax_mem);
	m_coaxcpu->set_disable();

	M68000(config, m_examcpu, 10'000'000);
	m_examcpu->set_addrmap(AS_PROGRAM, &telex274_state::exam_mem);
	m_examcpu->set_disable();

	//AM9513A(config, "examtimer", 4'000'000);

	//WD2797(config, "fdc", 1'000'000);
}


ROM_START(telex274)
	ROM_REGION16_BE(0x8000, "program", 0)
	ROM_LOAD16_BYTE("203982-053_even.u2", 0x0000, 0x4000, CRC(56914ba9) SHA1(7a2a9e16491a024b4109c8589a2277d65536e629))
	ROM_LOAD16_BYTE("203981-053_odd.u1", 0x0001, 0x4000, CRC(04a9e2cf) SHA1(58255d6275b76aecd8b81d2ea23ca69167caf232))
ROM_END

} // anonymous namespace


COMP(1986, telex274, 0, 0, telex274, telex274, telex274_state, empty_init, "Telex Computer Products", "Telex 274-61C Sixteen Station Control Unit", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



telex277d.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/************************************************************************************************************

    Skeleton driver for Telex 277/D IBM 3277-compatible coaxial terminal.

************************************************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8155.h"


namespace {

class telex277d_state : public driver_device
{
public:
	telex277d_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_chargen(*this, "chargen")
	{
	}

	void telex277d(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_chargen;
};


void telex277d_state::mem_map(address_map &map)
{
	map(0x0000, 0x1fff).rom().region("program", 0);
	map(0x4000, 0x47ff).ram();
	map(0x8000, 0x80ff).rw("ramio", FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w));
}

void telex277d_state::io_map(address_map &map)
{
	map(0x80, 0x87).rw("ramio", FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
}


static INPUT_PORTS_START(telex277d)
INPUT_PORTS_END


void telex277d_state::telex277d(machine_config &config)
{
	I8085A(config, m_maincpu, 6.144_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &telex277d_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &telex277d_state::io_map);

	I8155(config, "ramio", 6.144_MHz_XTAL / 2);
}


ROM_START(telex277d) // D8085A, P8155. bank of 8 dips between these 2 chips. Xtals on cpu board = 6.144,14.286MHz; Xtal on video board = 16.414
	ROM_REGION(0x2000, "program", 0)
	ROM_LOAD("15834.bin", 0x0000, 0x0800, CRC(364602ed) SHA1(574b1052ab000cfb9e7f194454de65f5255c250e))
	ROM_LOAD("15835.bin", 0x0800, 0x0800, CRC(b587d005) SHA1(de38b1dcbb871dc5f7dcbc177dfcbc25ecc743c4))
	ROM_LOAD("15836.bin", 0x1000, 0x0800, CRC(33a7179f) SHA1(c4b2a8f9d2b3e2f97c7d9b5458cad314fc8c79c1))
	ROM_LOAD("15837.bin", 0x1800, 0x0800, CRC(6d726662) SHA1(cff3ea2f06b802b94acfb780014d0e389cb61c42))

	ROM_REGION(0x0400, "chargen", 0)
	ROM_LOAD("15181_font.bin", 0x0000, 0x0400, CRC(2a7abd0b) SHA1(4456723c59307671dd0615723e6439f6532df531))
ROM_END

} // anonymous namespace


COMP(1979, telex277d, 0, 0, telex277d, telex277d, telex277d_state, empty_init, "Telex Computer Products", "Telex 277-D Display Terminal (Model 2)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



teradrive.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Angelo Salese
// thanks-to: Mask of Destiny, Nemesis, Sik
/**************************************************************************************************

Sega Teradrive

IBM PS/2 Model 30 (PS/55 5510Z) + Japanese Sega Mega Drive (TMSS MD1 VA3 or VA4)

References (Teradrive specifics):
- https://www.retrodev.com/blastem/trac/wiki/TeradriveHardwareNotes
- https://plutiedev.com/mirror/teradrive-hardware-info
- https://github.com/RetroSwimAU/TeradriveCode
- https://www.youtube.com/watch?v=yjg3gmTo4WA
- https://www.asahi-net.or.jp/~ds6k-mtng/tera.htm

References (generic MD):
- https://plutiedev.com/cartridge-slot
- https://plutiedev.com/vdp-registers
- https://segaretro.org/Sega_Mega_Drive/VDP_general_usage
- https://segaretro.org/Sega_Mega_Drive/Technical_specifications
- https://gendev.spritesmind.net/forum/viewtopic.php?p=37011#p37011

NOTES (PC side):
- F1 at POST will bring a setup menu;
- F2 (allegedly at DOS/V boot) will dual boot the machine;
- a program named VVCHG switches back and forth between MD and PC, and setup 68k to 10 MHz mode;

NOTES (MD side):
- 16 KiB of Z80 RAM (vs. 8 of stock)
- 128 KiB of VDP RAM (vs. 64)
- 68k can switch between native 7.67 MHz or 10 MHz
- has discrete YM3438 in place of YM2612
- Mega CD expansion port working with DIY extension cable, 32x needs at least a passive cart adapter
- focus 3 in debugger is the current default for MD side
- MAME inability of handling differing refresh rates causes visible tearing in MD screen
  (cfr. koteteik intro). A partial workaround is to use Video mode = composite, so that
  VGA will downclock to ~60 Hz instead.

TODO:
- RAM size always gets detected as 2560K even when it's not (from chipset?);
- Quadtel EMM driver fails recognizing WD76C10 chipset with drv4;
- Cannot HDD format with floppy insthdd.bat, cannot boot from HDD (needs floppy first).
  Attached disk is a WDL-330PS with no geometry info available;
- MD side cart slot, expansion bay and VDP rewrites (WIP);
- TMSS unlock and respective x86<->MD bus grants are sketchy;
- SEGA TERADRIVE テラドライブ ユーザーズマニュアル known to exist (not scanned yet)
- "TIMER FAIL" when exiting from F1 setup menu (keyboard? reset from chipset?);

**************************************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"

#include "bus/isa/isa.h"
#include "bus/isa/isa_cards.h"
#include "bus/isa/svga_paradise.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "bus/sms_ctrl/controllers.h"
#include "bus/sms_ctrl/smsctrl.h"
#include "cpu/i86/i286.h"
//#include "cpu/i386/i386.h"
#include "cpu/m68000/m68000.h"
#include "cpu/z80/z80.h"
#include "machine/input_merger.h"
#include "machine/ram.h"
#include "machine/sega_md_ioport.h"
#include "machine/wd7600.h"
#include "sound/spkrdev.h"
#include "sound/ymopn.h"
#include "video/ym7101.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

/*
 * ISA16 IBM 79F2661 "bus switch"
 *
 * Motherboard resource also shared with undumped 5510Z Japanese DOS/V
 *
 */

class isa16_ibm_79f2661 : public device_t, public device_isa16_card_interface
{
public:
	// construction/destruction
	isa16_ibm_79f2661(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	template <typename T> void set_romdisk_tag(T &&tag) { m_romdisk.set_tag(std::forward<T>(tag)); }
	template <typename T> void set_md_space(T &&tag, int index) { m_md_space.set_tag(std::forward<T>(tag), index); }
	auto tmss_bank_callback() { return m_tmss_bank_cb.bind(); }
	auto reg_1163_read_callback() { return m_reg_1163_read_cb.bind(); }
	auto reg_1163_write_callback() { return m_reg_1163_write_cb.bind(); }
	auto reg_1164_callback() { return m_reg_1164_cb.bind(); }
	auto system_in_callback() { return m_system_in_cb.bind(); }

protected:
	virtual void device_start() override ATTR_COLD;
	virtual void device_reset() override ATTR_COLD;

private:
	required_memory_region m_romdisk;
	memory_bank_creator m_rom_window_bank;
	// TODO: may be optional for 5510Z
	required_address_space m_md_space;

	void io_map(address_map &map) ATTR_COLD;
	void md_mem_map(address_map &map) ATTR_COLD;

	void remap(int space_id, offs_t start, offs_t end) override;

	u8 m_rom_bank = 0;
	u8 m_rom_address = 0x0e;
	u8 m_reg_1163 = 0;
	u8 m_reg_1164 = 0;
	u16 m_68k_address = 0;
	bool m_68k_view = false;

	devcb_write8 m_tmss_bank_cb;
	devcb_read8 m_reg_1163_read_cb;
	devcb_write8 m_reg_1163_write_cb;
	devcb_write8 m_reg_1164_cb;
	devcb_read8 m_system_in_cb;
};

DEFINE_DEVICE_TYPE(ISA16_IBM_79F2661, isa16_ibm_79f2661, "isa16_ibm_79f2661", "ISA16 IBM 79F2661 \"bus switch\"")

isa16_ibm_79f2661::isa16_ibm_79f2661(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, ISA16_IBM_79F2661, tag, owner, clock)
	, device_isa16_card_interface(mconfig, *this)
	, m_romdisk(*this, finder_base::DUMMY_TAG)
	, m_rom_window_bank(*this, "rom_window_bank")
	, m_md_space(*this, finder_base::DUMMY_TAG, -1)
	, m_tmss_bank_cb(*this)
	, m_reg_1163_read_cb(*this, 0)
	, m_reg_1163_write_cb(*this)
	, m_reg_1164_cb(*this)
	, m_system_in_cb(*this, 0)
{
}

void isa16_ibm_79f2661::device_start()
{
	set_isa_device();
	m_rom_window_bank->configure_entries(0, 0x100, m_romdisk->base(), 0x2000);

	save_item(NAME(m_rom_bank));
	save_item(NAME(m_rom_address));
	save_item(NAME(m_reg_1163));
	save_item(NAME(m_reg_1164));
	save_item(NAME(m_68k_address));
	save_item(NAME(m_68k_view));
}

void isa16_ibm_79f2661::device_reset()
{
	m_rom_bank = 0;
	m_rom_address = 0x0e;
	m_reg_1163 = 0;
	m_68k_view = false;
	m_reg_1164 = 0;
	m_68k_address = 0;
	m_reg_1163_write_cb(0);
	m_reg_1164_cb(0);
	remap(AS_PROGRAM, 0, 0xfffff);
	remap(AS_IO, 0, 0xffff);
}

// TODO: prettier method for ISA16 memory swap
void isa16_ibm_79f2661::md_mem_map(address_map &map)
{
	map(0x0000, 0x1fff).lrw16(
		NAME([this] (offs_t offset, u16 mem_mask) {
			const u32 base_address = (offset << 1) + (m_68k_address << 12);
			if (!ACCESSING_BITS_0_7)
			{
				u8 res = m_md_space->read_byte(base_address ^ 1);
				return (u16)(res | (res << 8));
			}
			else if (!ACCESSING_BITS_8_15)
			{
				u8 res = m_md_space->read_byte(base_address);
				return (u16)(res | (res << 8));
			}

			return swapendian_int16(m_md_space->read_word(base_address, mem_mask));
		}),
		NAME([this] (offs_t offset, u16 data, u16 mem_mask) {
			const u32 base_address = (offset << 1) + (m_68k_address << 12);
			if (!ACCESSING_BITS_0_7)
				m_md_space->write_byte(base_address ^ 1, data >> 8);
			else if (!ACCESSING_BITS_8_15)
				m_md_space->write_byte(base_address, data);
			else
				m_md_space->write_word(base_address, swapendian_int16(data), mem_mask);
		})
	);
}

void isa16_ibm_79f2661::remap(int space_id, offs_t start, offs_t end)
{
	if (space_id == AS_PROGRAM)
	{
		const u32 base_addr = (m_rom_address << 12) | 0xc0000;

		if (m_68k_view)
		{
			m_isa->install_memory(base_addr, base_addr | 0x1fff, *this, &isa16_ibm_79f2661::md_mem_map, 0xffff);
		}
		else
		{
			//printf("%08x %02x\n", base_addr, m_rom_bank);
			m_isa->install_bank(base_addr, base_addr | 0x1fff, m_rom_window_bank);
		}
	}
	if (space_id == AS_IO)
	{
		m_isa->install_device(0x1160, 0x1167, *this, &isa16_ibm_79f2661::io_map);
	}
}

// +$1160 base
//  map(0x1160, 0x1160) romdisk bank * 0x2000, r/w
//  map(0x1161, 0x1161) <undefined>?
//  map(0x1162, 0x1162) romdisk segment start in ISA space (val & 0x1e | 0xc0)
//  map(0x1163, 0x1163) comms and misc handshake bits, partially reflected on 68k $ae0001 register
//  map(0x1164, 0x1164) boot control
//  map(0x1165, 0x1165) switches/bus timeout/TMSS unlock (r/o)
//  map(0x1166, 0x1167) 68k address space select & 0xffffe * 0x2000, r/w
void isa16_ibm_79f2661::io_map(address_map &map)
{
	map(0x00, 0x00).lrw8(
		NAME([this] (offs_t offset) {
			return m_rom_bank;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_rom_bank = data;
			m_rom_window_bank->set_entry(m_rom_bank);
			remap(AS_PROGRAM, 0, 0xfffff);
			m_tmss_bank_cb(data);
		})
	);
	map(0x01, 0x01).lr8(
		NAME([this] (offs_t offset) {
			logerror("%s: $1161 undefined access\n", machine().describe_context());
			return 0xff;
		})
	);

	map(0x02, 0x02).lrw8(
		NAME([this] (offs_t offset) {
			return m_rom_address | 0xc0;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_rom_address = data & 0x1e;
			remap(AS_PROGRAM, 0, 0xfffff);
		})
	);
/*
 * xx-- ---- 68k $ae0001 bits 3-2 (writes from this side)
 * --xx ---- 68k $ae0001 bits 1-0 (reads from this side)
 * ---- xx-- <unknown>
 * ---- --x- Enable 286 ISA memory window (and disables TMSS from 68k)
 * ---- ---x Enable auxiliary TMSS ROM (on 68k space?)
 */
	map(0x03, 0x03).lrw8(
		NAME([this] (offs_t offset) {
			return (m_reg_1163 & 0xcf) | (m_reg_1163_read_cb() & 0x30);
		}),
		NAME([this] (offs_t offset, u8 data) {
			//logerror("$1163: %02x\n", data);
			if (BIT(data, 1) != BIT(m_reg_1163, 1))
			{
				m_68k_view = !!BIT(data, 1);
				remap(AS_PROGRAM, 0, 0xfffff);
			}

			m_reg_1163 = data;
			m_reg_1163_write_cb(data);
		})
	);
/*
 * x--- ---- handshake bit? Clearable by 68k, not by 286
 * -x-- ---- Set on TMSS failure
 * --x- ---- <unknown>
 * ---x ---- <unknown>, if 1 then $1165 returns a stream of bytes
 * ---- x--- dual boot bit
 * ---- -x-- video switch
 * ---- --x- (0) Teradrive HW at 68k $0 (1) cart space at $0
 * ---- ---x (0) 68k assert RESET/286 clear HALT (1) 68k clear RESET/286 assert HALT
 */
	map(0x04, 0x04).lrw8(
		NAME([this] (offs_t offset) {
			return m_reg_1164;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_reg_1164 = data;
			m_reg_1164_cb(data);
		})
	);
/*
 * xx-- ---- <unknown, other bus error?>
 * --x- ---- TMSS unlocked
 * ---- x--- bus timeout on 68k space access from 286
 * ---- -x-- video switch (0) "video" (1) RGB
 * ---- ---x MD/PC switch (0) MD boot (1) PC boot
 */
	map(0x05, 0x05).lr8(
		NAME([this] (offs_t offset) {
			// HACK: MD TMSS never writes `SEGA` from $a14000
			return (1 << 5) | (m_system_in_cb() & 5);
		})
	);
	map(0x06, 0x07).lrw16(
		NAME([this] (offs_t offset) {
			return m_68k_address;
		}),
		NAME([this] (offs_t offset, u16 data, u16 mem_mask) {
			COMBINE_DATA(&m_68k_address);
			m_68k_address &= 0xffffe;
			//if (m_68k_view)
			//  remap(AS_PROGRAM, 0, 0xfffff);
		})
	);
}

/*
 * ISA16 WD90C10
 *
 * On motherboard, VGA ROM is in BIOS region and unmapped on ISA memory
 *
 */

// TODO: really WD90C10
class isa16_wd90c10_romless_device : public isa16_wd90c11_lr_device
{
public:
	isa16_wd90c10_romless_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

protected:
	virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD;
	virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
};

DEFINE_DEVICE_TYPE(ISA16_WD90C10_ROMLESS,  isa16_wd90c10_romless_device,  "wd90c10_romless",  "Western Digital WD90C10 ROM-less VGA")

// NOTE: it will still try to map a ROM during setup mode
ROM_START( wd90c10_romless )
	ROM_REGION(0x8000,"vga_rom", ROMREGION_ERASE00)
ROM_END

const tiny_rom_entry *isa16_wd90c10_romless_device::device_rom_region() const
{
	return ROM_NAME( wd90c10_romless );
}

isa16_wd90c10_romless_device::isa16_wd90c10_romless_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: isa16_wd90c11_lr_device(mconfig, ISA16_WD90C10_ROMLESS, tag, owner, clock)
{
}

void isa16_wd90c10_romless_device::device_add_mconfig(machine_config &config)
{
	isa16_wd90c11_lr_device::device_add_mconfig(config);
	// unknown source, assume standard NTSC (divided internally)
	// tested in Video mode
	m_vga->set_vclk2(14'318'181);
}


namespace {

class teradrive_state : public driver_device
{
public:
	teradrive_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_x86cpu(*this, "x86cpu")
		, m_chipset(*this, "chipset")
		, m_ram(*this, RAM_TAG)
		, m_isabus(*this, "isabus")
		, m_speaker(*this, "speaker")
		, m_system_in(*this, "SYSTEM_IN")
		, m_md68kcpu(*this, "md68kcpu")
		, m_mdz80cpu(*this, "mdz80cpu")
		, m_mdscreen(*this, "mdscreen")
		, m_tmss_bank(*this, "tmss_bank")
		, m_tmss_view(*this, "tmss_view")
		, m_md_cart(*this, "md_cart")
		, m_md_vdp(*this, "md_vdp")
		, m_opn(*this, "opn")
		, m_md_68k_sound_view(*this, "md_68k_sound_view")
		, m_md_ctrl_ports(*this, { "md_ctrl1", "md_ctrl2", "md_exp" })
		, m_md_ioports(*this, "md_ioport%u", 1U)
	{ }

	void teradrive(machine_config &config);
	void at_softlists(machine_config &config);

protected:
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

	void x86_io(address_map &map) ATTR_COLD;
	void x86_map(address_map &map) ATTR_COLD;

	void md_68k_map(address_map &map) ATTR_COLD;
	void md_cpu_space_map(address_map &map);
	void md_68k_z80_map(address_map &map) ATTR_COLD;
	void md_z80_map(address_map &map) ATTR_COLD;
	void md_z80_io(address_map &map) ATTR_COLD;
	void md_ioctrl_map(address_map &map) ATTR_COLD;
private:
	required_device<i80286_cpu_device> m_x86cpu;
	required_device<wd7600_device> m_chipset;
	required_device<ram_device> m_ram;
	required_device<isa16_device> m_isabus;
	required_device<speaker_sound_device> m_speaker;

	u16 wd7600_ior(offs_t offset)
	{
		if (offset < 4)
			return m_isabus->dack_r(offset);
		else
			return m_isabus->dack16_r(offset);
	}
	void wd7600_iow(offs_t offset, u16 data)
	{
		if (offset < 4)
			m_isabus->dack_w(offset, data);
		else
			m_isabus->dack16_w(offset, data);
	}
	void wd7600_hold(int state)
	{
		// halt cpu
		m_x86cpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);

		// and acknowledge hold
		m_chipset->hlda_w(state);
	}
	void wd7600_tc(offs_t offset, u8 data) { m_isabus->eop_w(offset, data); }
	void wd7600_spkr(int state) { m_speaker->level_w(state); }

	u16 m_heartbeat = 0;

	// bus switch semantics
	required_ioport m_system_in;

	static void romdisk_config(device_t *device);

	void tmss_bank_w(u8 data);
	u8 reg_1163_r();
	void reg_1163_w(u8 data);
	void reg_1164_w(u8 data);
	u8 system_in_r();

	// MD side
	required_device<m68000_device> m_md68kcpu;
	required_device<z80_device> m_mdz80cpu;
	required_device<screen_device> m_mdscreen;
	required_memory_bank m_tmss_bank;
	memory_view m_tmss_view;
	required_device<generic_slot_device> m_md_cart;
	required_device<ym7101_device> m_md_vdp;
	required_device<ym_generic_device> m_opn;
	memory_view m_md_68k_sound_view;
	required_device_array<sms_control_port_device, 3> m_md_ctrl_ports;
	required_device_array<megadrive_io_port_device, 3> m_md_ioports;

	// TODO: main PC screen can also swap the VGA with this
	// (roughly #5801 and #11343 league)
	u32 md_screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	u8 m_isa_address_bank = 0;
	u8 m_68k_hs = 0;
	std::unique_ptr<u8[]> m_sound_program;

	bool m_z80_reset = false;
	bool m_z80_busrq = false;
	u32 m_z80_main_address = 0;

	void flush_z80_state();
};

void teradrive_state::x86_map(address_map &map)
{
	map.unmap_value_high();
}

void teradrive_state::x86_io(address_map &map)
{
	map.unmap_value_high();
	// TODO: belongs to chipset
	map(0xfc72, 0xfc73).lr16(
		NAME([this] () {
			u16 res = m_heartbeat & 0x8000;
			if (!machine().side_effects_disabled())
				m_heartbeat ^= 0x8000;
			// other bits read
			return 0x7fff | res;
		})
	);
}

/*
 *
 * MD specifics
 *
 */

u32 teradrive_state::md_screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	m_md_vdp->screen_update(screen, bitmap, cliprect);
	return 0;
}


void teradrive_state::md_68k_map(address_map &map)
{
	map.unmap_value_high();
//  map(0x000000, 0x7fffff).view(m_cart_view);
	// when /CART pin is low
//  m_cart_view[0](0x000000, 0x3fffff).m(m_cart, FUNC(...::cart_map));
//  m_cart_view[0](0x000000, 0x003fff).view(m_tmss_view);
//  m_tmss_view[0](0x000000, 0x003fff).rom().region("tmss", 0);
//  m_cart_view[0](0x400000, 0x7fffff).m(m_exp, FUNC(...::expansion_map));

	// /CART high (matters for MCD SRAM at very least)
//  m_cart_view[1](0x000000, 0x3fffff).m(m_exp, FUNC(...::expansion_map));
//  m_cart_view[1](0x400000, 0x7fffff).m(m_cart, FUNC(...::cart_map));
//  m_cart_view[1](0x400000, 0x403fff).view(m_tmss_view);
//  m_tmss_view[0](0x400000, 0x403fff).rom().region("tmss", 0);

	map(0x000000, 0x3fffff).r(m_md_cart, FUNC(generic_slot_device::read_rom));
	map(0x000000, 0x000fff).view(m_tmss_view);
	m_tmss_view[0](0x000000, 0x000fff).bankr(m_tmss_bank);

//  map(0x800000, 0x9fffff) unmapped or 32X
	map(0xa00000, 0xa07fff).view(m_md_68k_sound_view);
	m_md_68k_sound_view[0](0xa00000, 0xa07fff).before_delay(NAME([](offs_t) { return 1; })).m(*this, FUNC(teradrive_state::md_68k_z80_map));
//  map(0xa07f00, 0xa07fff) Z80 VDP space (freezes machine if accessed from 68k)
//  map(0xa08000, 0xa0ffff) Z80 68k window (assume no DTACK), or just mirror of above according to TD HW notes?
//  map(0xa10000, 0xa100ff) I/O
	map(0xa10000, 0xa100ff).m(*this, FUNC(teradrive_state::md_ioctrl_map));

//  map(0xa11000, 0xa110ff) memory mode register
//  map(0xa11100, 0xa111ff) Z80 BUSREQ/BUSACK
	map(0xa11100, 0xa11101).lrw16(
		NAME([this] (offs_t offset, u16 mem_mask) {
			address_space &space = m_md68kcpu->space(AS_PROGRAM);
			// TODO: enough for all edge cases but timekill
			u16 open_bus = space.read_word(m_md68kcpu->pc() - 2) & 0xfefe;
			// printf("%06x -> %04x\n", m_md68kcpu->pc() - 2, open_bus);
			u16 res = (!m_z80_busrq || m_z80_reset) ^ 1;
			return (res << 8) | (res) | open_bus;
		}),
		NAME([this] (offs_t offset, u16 data, u16 mem_mask) {
			//printf("%04x %04x\n", data, mem_mask);
			if (!ACCESSING_BITS_0_7)
			{
				m_z80_busrq = !!BIT(~data, 8);
			}
			else if (!ACCESSING_BITS_8_15)
			{
				m_z80_busrq = !!BIT(~data, 0);
			}
			else // word access
			{
				m_z80_busrq = !!BIT(~data, 8);
			}
			flush_z80_state();
		})
	);
//  map(0xa11200, 0xa112ff) Z80 RESET
	map(0xa11200, 0xa11201).lw16(
		NAME([this] (offs_t offset, u16 data, u16 mem_mask) {
			if (!ACCESSING_BITS_0_7)
			{
				m_z80_reset = !!BIT(~data, 8);
			}
			else if (!ACCESSING_BITS_8_15)
			{
				m_z80_reset = !!BIT(~data, 0);
			}
			else // word access
			{
				m_z80_reset = !!BIT(~data, 8);
			}
			flush_z80_state();
		})
	);
//  map(0xa11300, 0xa113ff) <open bus>

//  map(0xa11400, 0xa1dfff) <unmapped> (no DTACK generation, freezes machine without additional HW)
//  map(0xa12000, 0xa120ff).m(m_exp, FUNC(...::fdc_map));
//  map(0xa13000, 0xa130ff).m(m_cart, FUNC(...::time_map));
//  map(0xa14000, 0xa14003) TMSS lock
//  map(0xa15100, 0xa153ff) 32X registers if present, <unmapped> otherwise
//  map(0xae0000, 0xae0003) Teradrive bus switch registers
	map(0xae0001, 0xae0001).lrw8(
		NAME([this] (offs_t offset) {
			return m_68k_hs;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_68k_hs = (data & 0xf3) | (m_68k_hs & 0x0c);
		})
	);
	map(0xae0003, 0xae0003).lrw8(
		NAME([this] (offs_t offset) {
			return m_isa_address_bank;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_isa_address_bank = data;
		})
	);
	map(0xaf0000, 0xafffff).rw(m_isabus, FUNC(isa16_device::io16_swap_r), FUNC(isa16_device::io16_swap_w));
	// NOTE: actually bank selectable from $ae0003 in 1 MiB units
	// pzlcnst loads from here (i.e. RAM)
	// TODO: verify requiring swapped endianness
	map(0xb00000, 0xbfffff).lrw16(
		NAME([this] (offs_t offset, u16 mem_mask) {
			return m_isabus->mem16_swap_r(offset | (m_isa_address_bank * 0x80000), mem_mask);
		}),
		NAME([this] (offs_t offset, u16 data, u16 mem_mask) {
			m_isabus->mem16_swap_w(offset | (m_isa_address_bank * 0x80000), data, mem_mask);
		})
	);
//  map(0xc00000, 0xdfffff) VDP and PSG (with mirrors and holes)
	map(0xc00000, 0xc0001f).m(m_md_vdp, FUNC(ym7101_device::if16_map));
	map(0xe00000, 0xe0ffff).mirror(0x1f0000).ram(); // Work RAM, usually accessed at $ff0000
}

// $a10000 base
void teradrive_state::md_ioctrl_map(address_map &map)
{
	// version, should be 0 for Teradrive, bit 5 for expansion bus not connected yet
	map(0x00, 0x01).lr8(NAME([] () { return 1 << 5; }));
	map(0x02, 0x03).rw(m_md_ioports[0], FUNC(megadrive_io_port_device::data_r), FUNC(megadrive_io_port_device::data_w));
	map(0x04, 0x05).rw(m_md_ioports[1], FUNC(megadrive_io_port_device::data_r), FUNC(megadrive_io_port_device::data_w));
	map(0x06, 0x07).rw(m_md_ioports[2], FUNC(megadrive_io_port_device::data_r), FUNC(megadrive_io_port_device::data_w));
	map(0x08, 0x09).rw(m_md_ioports[0], FUNC(megadrive_io_port_device::ctrl_r), FUNC(megadrive_io_port_device::ctrl_w));
	map(0x0a, 0x0b).rw(m_md_ioports[1], FUNC(megadrive_io_port_device::ctrl_r), FUNC(megadrive_io_port_device::ctrl_w));
	map(0x0c, 0x0d).rw(m_md_ioports[2], FUNC(megadrive_io_port_device::ctrl_r), FUNC(megadrive_io_port_device::ctrl_w));
	map(0x0e, 0x0f).rw(m_md_ioports[0], FUNC(megadrive_io_port_device::txdata_r), FUNC(megadrive_io_port_device::txdata_w));
	map(0x10, 0x11).r(m_md_ioports[0], FUNC(megadrive_io_port_device::rxdata_r));
	map(0x12, 0x13).rw(m_md_ioports[0], FUNC(megadrive_io_port_device::s_ctrl_r), FUNC(megadrive_io_port_device::s_ctrl_w));
	map(0x14, 0x15).rw(m_md_ioports[1], FUNC(megadrive_io_port_device::txdata_r), FUNC(megadrive_io_port_device::txdata_w));
	map(0x16, 0x17).r(m_md_ioports[1], FUNC(megadrive_io_port_device::rxdata_r));
	map(0x18, 0x19).rw(m_md_ioports[1], FUNC(megadrive_io_port_device::s_ctrl_r), FUNC(megadrive_io_port_device::s_ctrl_w));
	map(0x1a, 0x1b).rw(m_md_ioports[2], FUNC(megadrive_io_port_device::txdata_r), FUNC(megadrive_io_port_device::txdata_w));
	map(0x1c, 0x1d).r(m_md_ioports[2], FUNC(megadrive_io_port_device::rxdata_r));
	map(0x1e, 0x1f).rw(m_md_ioports[2], FUNC(megadrive_io_port_device::s_ctrl_r), FUNC(megadrive_io_port_device::s_ctrl_w));
}

void teradrive_state::md_cpu_space_map(address_map &map)
{
	map(0xfffff3, 0xfffff3).before_time(m_md68kcpu, FUNC(m68000_device::vpa_sync)).after_delay(m_md68kcpu, FUNC(m68000_device::vpa_after)).lr8(NAME([] () -> u8 { return 25; }));
	// TODO: IPL0 (external irq tied to VDP IE2)
	map(0xfffff5, 0xfffff5).before_time(m_md68kcpu, FUNC(m68000_device::vpa_sync)).after_delay(m_md68kcpu, FUNC(m68000_device::vpa_after)).lr8(NAME([] () -> u8 { return 26; }));
	map(0xfffff7, 0xfffff7).before_time(m_md68kcpu, FUNC(m68000_device::vpa_sync)).after_delay(m_md68kcpu, FUNC(m68000_device::vpa_after)).lr8(NAME([] () -> u8 { return 27; }));
	map(0xfffff9, 0xfffff9).before_time(m_md68kcpu, FUNC(m68000_device::vpa_sync)).after_delay(m_md68kcpu, FUNC(m68000_device::vpa_after)).lr8(NAME([this] () -> u8 {
		m_md_vdp->irq_ack();
		return 28;
	}));
	map(0xfffffb, 0xfffffb).before_time(m_md68kcpu, FUNC(m68000_device::vpa_sync)).after_delay(m_md68kcpu, FUNC(m68000_device::vpa_after)).lr8(NAME([] () -> u8 { return 29; }));
	map(0xfffffd, 0xfffffd).before_time(m_md68kcpu, FUNC(m68000_device::vpa_sync)).after_delay(m_md68kcpu, FUNC(m68000_device::vpa_after)).lr8(NAME([this] () -> u8 {
		m_md_vdp->irq_ack();
		return 30;
	}));
	map(0xffffff, 0xffffff).before_time(m_md68kcpu, FUNC(m68000_device::vpa_sync)).after_delay(m_md68kcpu, FUNC(m68000_device::vpa_after)).lr8(NAME([] () -> u8 { return 31; }));
}


void teradrive_state::flush_z80_state()
{
	m_mdz80cpu->set_input_line(INPUT_LINE_RESET, m_z80_reset ? ASSERT_LINE : CLEAR_LINE);
	m_mdz80cpu->set_input_line(Z80_INPUT_LINE_BUSRQ, m_z80_busrq ? CLEAR_LINE : ASSERT_LINE);
	if (m_z80_reset)
		m_opn->reset();
	if (m_z80_reset || !m_z80_busrq)
		m_md_68k_sound_view.select(0);
	else
		m_md_68k_sound_view.disable();
}

void teradrive_state::md_68k_z80_map(address_map &map)
{
	map(0x0000, 0x3fff).lrw16(
		NAME([this] (offs_t offset, u16 mem_mask) {
			u16 res = m_sound_program[(offset << 1) ^ 1] | (m_sound_program[offset << 1] << 8);
			return res;
		}),
		NAME([this] (offs_t offset, u16 data, u16 mem_mask) {
			if (!ACCESSING_BITS_0_7)
			{
				m_sound_program[(offset<<1)] = (data & 0xff00) >> 8;
			}
			else if (!ACCESSING_BITS_8_15)
			{
				m_sound_program[(offset<<1) ^ 1] = (data & 0x00ff);
			}
			else
			{
				m_sound_program[(offset<<1)] = (data & 0xff00) >> 8;
			}
		})
	);
	map(0x4000, 0x4003).mirror(0x1ffc).rw(m_opn, FUNC(ym_generic_device::read), FUNC(ym_generic_device::write));
}

void teradrive_state::md_z80_map(address_map &map)
{
	map(0x0000, 0x3fff).lrw8(
		NAME([this] (offs_t offset) {
			return m_sound_program[offset];
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_sound_program[offset] = data;
		})
	);
	map(0x4000, 0x4003).mirror(0x1ffc).rw(m_opn, FUNC(ym_generic_device::read), FUNC(ym_generic_device::write));
	map(0x6000, 0x60ff).lw8(NAME([this] (offs_t offset, u8 data) {
		m_z80_main_address = ((m_z80_main_address >> 1) | ((data & 1) << 23)) & 0xff8000;
	}));
	map(0x7f00, 0x7f1f).m(m_md_vdp, FUNC(ym7101_device::if8_map));
	map(0x8000, 0xffff).lrw8(
		NAME([this] (offs_t offset) {
			address_space &space68k = m_md68kcpu->space();
			u8 ret = space68k.read_byte(m_z80_main_address + offset);
			return ret;

		}),
		NAME([this] (offs_t offset, u8 data) {
			address_space &space68k = m_md68kcpu->space();
			space68k.write_byte(m_z80_main_address + offset, data);
		})
	);
}

void teradrive_state::md_z80_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	// juuouki reads $bf in irq service (SMS VDP left-over?)
	map(0x00, 0xff).nopr();
	// TODO: SMS mode thru cart
}

/*
 *
 * Bus Switch interface
 *
 */

void teradrive_state::tmss_bank_w(u8 data)
{
	// NOTE: half the granularity than 286 version, and the lower portion of it
	m_tmss_bank->set_entry((data * 2) + 1);
}

u8 teradrive_state::reg_1163_r()
{
	return (m_68k_hs & 3) << 4;
}

void teradrive_state::reg_1163_w(u8 data)
{
	m_68k_hs &= ~0x0c;
	m_68k_hs = (data & 0xc0) >> 4;
}

void teradrive_state::reg_1164_w(u8 data)
{
	if (BIT(data, 0))
	{
		m_x86cpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
		m_md68kcpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
	}
	else
	{
		m_x86cpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
		m_md68kcpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
	}

	if (BIT(data, 1))
	{
		m_tmss_view.disable();
	}
	else
	{
		m_tmss_view.select(0);
	}
}

u8 teradrive_state::system_in_r()
{
	return m_system_in->read();
}

void teradrive_state::romdisk_config(device_t *device)
{
	auto *state = device->subdevice<teradrive_state>(":");
	isa16_ibm_79f2661 &bus_switch = *downcast<isa16_ibm_79f2661 *>(device);
	bus_switch.set_romdisk_tag("romdisk");
	bus_switch.set_md_space(":md68kcpu", AS_PROGRAM);
	bus_switch.tmss_bank_callback().set(*state, FUNC(teradrive_state::tmss_bank_w));
	bus_switch.reg_1163_read_callback().set(*state, FUNC(teradrive_state::reg_1163_r));
	bus_switch.reg_1163_write_callback().set(*state, FUNC(teradrive_state::reg_1163_w));
	bus_switch.reg_1164_callback().set(*state, FUNC(teradrive_state::reg_1164_w));
	// TODO: using set_ioport doesn't work, will try to map from bus_switch device anyway
//  bus_switch.system_in_callback().set_ioport("SYSTEM_IN");
	bus_switch.system_in_callback().set(*state, FUNC(teradrive_state::system_in_r));
}

/*
 *
 * Machine generics
 *
 */

static INPUT_PORTS_START( teradrive )
	PORT_START("SYSTEM_IN")
	PORT_DIPNAME( 0x01, 0x01, "Boot mode" )
	PORT_DIPSETTING(    0x00, "MD boot" )
	PORT_DIPSETTING(    0x01, "PC boot" )
	PORT_DIPNAME( 0x04, 0x04, "Video mode" )
	PORT_DIPSETTING(    0x00, "Video" ) // composite
	PORT_DIPSETTING(    0x04, "RGB" )
INPUT_PORTS_END

void teradrive_state::machine_start()
{
	m_tmss_bank->configure_entries(0, 0x200, memregion("tmss")->base(), 0x1000);
	// doubled in space
	m_sound_program = std::make_unique<u8[]>(0x4000);

	save_item(NAME(m_heartbeat));

	save_item(NAME(m_isa_address_bank));
	save_item(NAME(m_68k_hs));
	save_pointer(NAME(m_sound_program), 0x4000);

	save_item(NAME(m_z80_reset));
	save_item(NAME(m_z80_busrq));
	save_item(NAME(m_z80_main_address));
}

void teradrive_state::machine_reset()
{
	m_68k_hs = 0;
	m_z80_reset = true;
	flush_z80_state();
}

void teradrive_isa_cards(device_slot_interface &device)
{
	device.option_add_internal("bus_switch", ISA16_IBM_79F2661);
	device.option_add_internal("wd90c10_romless", ISA16_WD90C10_ROMLESS);
}

void teradrive_state::at_softlists(machine_config &config)
{
	SOFTWARE_LIST(config, "pc_disk_list").set_original("ibm5150");
	SOFTWARE_LIST(config, "at_disk_list").set_original("ibm5170");
//  SOFTWARE_LIST(config, "at_cdrom_list").set_original("ibm5170_cdrom");
	SOFTWARE_LIST(config, "at_hdd_list").set_original("ibm5170_hdd");
	SOFTWARE_LIST(config, "midi_disk_list").set_compatible("midi_flop");
//  SOFTWARE_LIST(config, "photocd_list").set_compatible("photo_cd");

//  TODO: MD portion
	SOFTWARE_LIST(config, "cart_list").set_original("megadriv").set_filter("NTSC-J");
	SOFTWARE_LIST(config, "td_disk_list").set_original("teradrive_flop");
//  TODO: Teradrive HDD SW list
}

void teradrive_state::teradrive(machine_config &config)
{
	I80286(config, m_x86cpu, XTAL(10'000'000));
	m_x86cpu->set_addrmap(AS_PROGRAM, &teradrive_state::x86_map);
	m_x86cpu->set_addrmap(AS_IO, &teradrive_state::x86_io);
	m_x86cpu->set_irq_acknowledge_callback("chipset", FUNC(wd7600_device::intack_cb));

	// WD76C10LP system controller
	// WD76C30 peripheral controller
	WD7600(config, m_chipset, 50_MHz_XTAL / 2);
	m_chipset->set_cputag(m_x86cpu);
	m_chipset->set_ramtag(m_ram);
	m_chipset->set_biostag("bios");
	m_chipset->set_keybctag("keybc");
	m_chipset->hold_callback().set(FUNC(teradrive_state::wd7600_hold));
	m_chipset->nmi_callback().set_inputline(m_x86cpu, INPUT_LINE_NMI);
	m_chipset->intr_callback().set_inputline(m_x86cpu, INPUT_LINE_IRQ0);
	m_chipset->cpureset_callback().set_inputline(m_x86cpu, INPUT_LINE_RESET);
	m_chipset->a20m_callback().set_inputline(m_x86cpu, INPUT_LINE_A20);
	// isa dma
	m_chipset->ior_callback().set(FUNC(teradrive_state::wd7600_ior));
	m_chipset->iow_callback().set(FUNC(teradrive_state::wd7600_iow));
	m_chipset->tc_callback().set(FUNC(teradrive_state::wd7600_tc));
	// speaker
	m_chipset->spkr_callback().set(FUNC(teradrive_state::wd7600_spkr));

	// on board devices
	ISA16(config, m_isabus, 0);
	m_isabus->set_memspace(m_x86cpu, AS_PROGRAM);
	m_isabus->set_iospace(m_x86cpu, AS_IO);
	m_isabus->iochck_callback().set(m_chipset, FUNC(wd7600_device::iochck_w));
	m_isabus->irq2_callback().set(m_chipset, FUNC(wd7600_device::irq09_w));
	m_isabus->irq3_callback().set(m_chipset, FUNC(wd7600_device::irq03_w));
	m_isabus->irq4_callback().set(m_chipset, FUNC(wd7600_device::irq04_w));
	m_isabus->irq5_callback().set(m_chipset, FUNC(wd7600_device::irq05_w));
	m_isabus->irq6_callback().set(m_chipset, FUNC(wd7600_device::irq06_w));
	m_isabus->irq7_callback().set(m_chipset, FUNC(wd7600_device::irq07_w));
	m_isabus->irq10_callback().set(m_chipset, FUNC(wd7600_device::irq10_w));
	m_isabus->irq11_callback().set(m_chipset, FUNC(wd7600_device::irq11_w));
	m_isabus->irq12_callback().set(m_chipset, FUNC(wd7600_device::irq12_w));
	m_isabus->irq14_callback().set(m_chipset, FUNC(wd7600_device::irq14_w));
	m_isabus->irq15_callback().set(m_chipset, FUNC(wd7600_device::irq15_w));
	m_isabus->drq0_callback().set(m_chipset, FUNC(wd7600_device::dreq0_w));
	m_isabus->drq1_callback().set(m_chipset, FUNC(wd7600_device::dreq1_w));
	m_isabus->drq2_callback().set(m_chipset, FUNC(wd7600_device::dreq2_w));
	m_isabus->drq3_callback().set(m_chipset, FUNC(wd7600_device::dreq3_w));
	m_isabus->drq5_callback().set(m_chipset, FUNC(wd7600_device::dreq5_w));
	m_isabus->drq6_callback().set(m_chipset, FUNC(wd7600_device::dreq6_w));
	m_isabus->drq7_callback().set(m_chipset, FUNC(wd7600_device::dreq7_w));

	// NOTE: wants IBM BIOS over IBM keyboard only
	ps2_keyboard_controller_device &keybc(PS2_KEYBOARD_CONTROLLER(config, "keybc", 12_MHz_XTAL));
	keybc.hot_res().set("chipset", FUNC(wd7600_device::kbrst_w));
	keybc.gate_a20().set("chipset", FUNC(wd7600_device::gatea20_w));
	keybc.kbd_irq().set("chipset", FUNC(wd7600_device::irq01_w));
	keybc.kbd_clk().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	keybc.kbd_data().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));

	pc_kbdc_device &pc_kbdc(PC_KBDC(config, "kbd", pc_at_keyboards, STR_KBD_IBM_PC_AT_84));
	pc_kbdc.out_clock_cb().set("keybc", FUNC(ps2_keyboard_controller_device::kbd_clk_w));
	pc_kbdc.out_data_cb().set("keybc", FUNC(ps2_keyboard_controller_device::kbd_data_w));

	pc_kbdc_device &aux_con(PC_KBDC(config, "aux", ps2_mice, STR_HLE_PS2_MOUSE));
	aux_con.out_clock_cb().set("keybc", FUNC(ps2_keyboard_controller_device::aux_clk_w));
	aux_con.out_data_cb().set("keybc", FUNC(ps2_keyboard_controller_device::aux_data_w));

	// FIXME: determine ISA bus clock, unverified configuration
	// WD76C20
	ISA16_SLOT(config, "board1", 0, "isabus", pc_isa16_cards, "fdc_smc", true);
	ISA16_SLOT(config, "board2", 0, "isabus", pc_isa16_cards, "comat", true);
	// TODO: should be ESDI built-in interface on riser with IBM WDL-330PS 3.5" HDD, not IDE
	ISA16_SLOT(config, "board3", 0, "isabus", pc_isa16_cards, "side116", true);
	ISA16_SLOT(config, "board4", 0, "isabus", pc_isa16_cards, "lpt", true);
	ISA16_SLOT(config, "board5", 0, "isabus", teradrive_isa_cards, "wd90c10_romless", true);
	ISA16_SLOT(config, "board6", 0, "isabus", teradrive_isa_cards, "bus_switch", true).set_option_machine_config("bus_switch", romdisk_config);
	ISA16_SLOT(config, "isa1",   0, "isabus", pc_isa16_cards, nullptr, false);

	// 2.5MB is the max allowed by the BIOS (even if WD chipset can do more)
	// TODO: pcdos5v garbles font loading with 1664K, which should be the actual default
	RAM(config, RAM_TAG).set_default_size("2688K").set_extra_options("640K,1664K");

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	at_softlists(config);

	// MD section (NTSC)
	constexpr XTAL md_master_xtal(53.693175_MHz_XTAL);

	M68000(config, m_md68kcpu, md_master_xtal / 7);
	m_md68kcpu->set_addrmap(AS_PROGRAM, &teradrive_state::md_68k_map);
	m_md68kcpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &teradrive_state::md_cpu_space_map);
	// disallow TAS (gargoyle)
	m_md68kcpu->set_tas_write_callback(NAME([] (offs_t offset, u8 data) { }));

	Z80(config, m_mdz80cpu, md_master_xtal / 15);
	m_mdz80cpu->set_addrmap(AS_PROGRAM, &teradrive_state::md_z80_map);
	m_mdz80cpu->set_addrmap(AS_IO, &teradrive_state::md_z80_io);

	SCREEN(config, m_mdscreen, SCREEN_TYPE_RASTER);
	// NOTE: PAL is 423x312
	m_mdscreen->set_raw(md_master_xtal / 8, 427, 0, 320, 262, 0, 224);
	m_mdscreen->set_screen_update(FUNC(teradrive_state::md_screen_update));

	YM7101(config, m_md_vdp, md_master_xtal / 4);
	m_md_vdp->set_mclk(md_master_xtal);
	m_md_vdp->set_screen(m_mdscreen);
	// TODO: actual DTACK
	// TODO: accessing 68k bus from x86, defers access?
	m_md_vdp->dtack_cb().set_inputline(m_md68kcpu, INPUT_LINE_HALT);
	m_md_vdp->mreq_cb().set([this] (offs_t offset) {
		// TODO: can't read outside of base cart and RAM
		//printf("%06x\n", offset);
		address_space &space68k = m_md68kcpu->space();
		u16 ret = space68k.read_word(offset, 0xffff);

		return ret;
	});
	m_md_vdp->vint_cb().set_inputline(m_md68kcpu, 6);
	m_md_vdp->hint_cb().set_inputline(m_md68kcpu, 4);
	m_md_vdp->sint_cb().set_inputline(m_mdz80cpu, INPUT_LINE_IRQ0);
	m_md_vdp->add_route(ALL_OUTPUTS, "md_speaker", 0.50, 0);
	m_md_vdp->add_route(ALL_OUTPUTS, "md_speaker", 0.50, 1);

	auto &hl(INPUT_MERGER_ANY_HIGH(config, "hl"));
	// TODO: gated thru VDP
	hl.output_handler().set_inputline(m_md68kcpu, 2);

	MEGADRIVE_IO_PORT(config, m_md_ioports[0], 0);
	m_md_ioports[0]->hl_handler().set("hl", FUNC(input_merger_device::in_w<0>));

	MEGADRIVE_IO_PORT(config, m_md_ioports[1], 0);
	m_md_ioports[1]->hl_handler().set("hl", FUNC(input_merger_device::in_w<1>));

	MEGADRIVE_IO_PORT(config, m_md_ioports[2], 0);
	m_md_ioports[2]->hl_handler().set("hl", FUNC(input_merger_device::in_w<2>));

	for (int N = 0; N < 3; N++)
	{
		SMS_CONTROL_PORT(config, m_md_ctrl_ports[N], sms_control_port_devices, N != 2 ? SMS_CTRL_OPTION_MD_PAD : nullptr);
		m_md_ctrl_ports[N]->th_handler().set(m_md_ioports[N], FUNC(megadrive_io_port_device::th_w));
		m_md_ctrl_ports[N]->set_screen(m_mdscreen);

		m_md_ioports[N]->set_in_handler(m_md_ctrl_ports[N], FUNC(sms_control_port_device::in_r));
		m_md_ioports[N]->set_out_handler(m_md_ctrl_ports[N], FUNC(sms_control_port_device::out_w));
	}

	// TODO: vestigial
	GENERIC_CARTSLOT(config, m_md_cart, generic_plain_slot, "megadriv_cart");
	m_md_cart->set_width(GENERIC_ROM16_WIDTH);
	// TODO: generic_cartslot has issues with softlisted endianness (use loose for now)
	m_md_cart->set_endian(ENDIANNESS_BIG);

	SPEAKER(config, "md_speaker", 2).front();

	YM2612(config, m_opn, md_master_xtal / 7);
	m_opn->add_route(0, "md_speaker", 0.50, 0);
	m_opn->add_route(1, "md_speaker", 0.50, 1);
}

ROM_START( teradrive )
	ROM_REGION(0x20000, "bios", 0)
	ROM_LOAD( "bios-27c010.bin", 0x00000, 0x20000, CRC(32642518) SHA1(6bb6d0325b8e4150c4258fd16f3a870b92e88f75))

	ROM_REGION16_LE(0x200000, "board6:romdisk", ROMREGION_ERASEFF)
	// contains bootable PC-DOS 3.x + a MENU.EXE
	// 1ST AND 2ND HALF IDENTICAL
	ROM_LOAD( "tru-27c800.bin", 0x00000, 0x100000,  CRC(c2fe9c9e) SHA1(06ec0461dab425f41fb5c3892d9beaa8fa53bbf1))

	// firmware for 68k side, need to ROM_COPY to match endianness
	ROM_REGION16_BE(0x200000, "tmss", ROMREGION_ERASEFF)
	ROM_COPY("board6:romdisk", 0x00000, 0x00000, 0x200000 )
ROM_END

ROM_START( teradrive3 )
	// TODO: may just be a BIOS dump that needs to be trimmed
	ROM_REGION(0x80000, "rawbios", 0)
	ROM_LOAD( "model3.bin", 0x00000, 0x80000, CRC(dc757cb3) SHA1(c1489cc2d554fc62f986464604f7f7fbb219b438))

	ROM_REGION(0x20000, "bios", 0)
	ROM_COPY("rawbios", 0x60000, 0x00000, 0x20000)

	ROM_REGION16_LE(0x200000, "board6:romdisk", ROMREGION_ERASEFF)
	// contains bootable PC-DOS 3.x + a MENU.EXE
	// 1ST AND 2ND HALF IDENTICAL
	ROM_LOAD( "tru-27c800.bin", 0x00000, 0x100000,  CRC(c2fe9c9e) SHA1(06ec0461dab425f41fb5c3892d9beaa8fa53bbf1))

	ROM_REGION16_BE(0x200000, "tmss", ROMREGION_ERASEFF)
	ROM_COPY("board6:romdisk", 0x00000, 0x00000, 0x200000 )
ROM_END


} // anonymous namespace

COMP( 1991, teradrive,  0,         0,       teradrive, teradrive, teradrive_state, empty_init, "Sega / International Business Machines", "Teradrive (Japan, Model 2)", MACHINE_NOT_WORKING )
COMP( 1991, teradrive3, teradrive, 0,       teradrive, teradrive, teradrive_state, empty_init, "Sega / International Business Machines", "Teradrive (Japan, Model 3)", MACHINE_NOT_WORKING )



terak.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

Terak 8510A

2009-02-23 Skeleton driver.

Known chips: i8257 DMA, i8272a FDC
Floppies were 8 inch IBM format.

****************************************************************************/

#include "emu.h"
//#include "bus/qbus/qbus.h"
#include "cpu/t11/t11.h"
#include "emupal.h"
#include "screen.h"


namespace {

class terak_state : public driver_device
{
public:
	terak_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void terak(machine_config &config);

private:
	uint16_t terak_fdc_status_r();
	void terak_fdc_command_w(uint16_t data);
	uint16_t terak_fdc_data_r();
	void terak_fdc_data_w(uint16_t data);
	uint32_t screen_update_terak(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void mem_map(address_map &map) ATTR_COLD;

	uint8_t m_unit = 0;
	uint8_t m_cmd = 0;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;
	required_device<t11_device> m_maincpu;
};

uint16_t terak_state::terak_fdc_status_r()
{
	logerror("terak_fdc_status_r\n");
	if (m_cmd==3)
	{
		logerror("cmd is 3\n");
		return 0xffff;
	}
	return 0;
}

void terak_state::terak_fdc_command_w(uint16_t data)
{
	m_unit = (data >> 8) & 0x03;
	m_cmd  = (data >> 1) & 0x07;
	logerror("terak_fdc_command_w %04x [%d %d]\n",data,m_unit,m_cmd);
}

uint16_t terak_state::terak_fdc_data_r()
{
	logerror("terak_fdc_data_r\n");
	return 0;
}

void terak_state::terak_fdc_data_w(uint16_t data)
{
	logerror("terak_fdc_data_w %04x\n",data);
}

void terak_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xf5ff).ram(); // RAM

	// octal
	map(0173000, 0173177).rom(); // ROM
	map(0177000, 0177001).rw(FUNC(terak_state::terak_fdc_status_r), FUNC(terak_state::terak_fdc_command_w));
	map(0177002, 0177003).rw(FUNC(terak_state::terak_fdc_data_r), FUNC(terak_state::terak_fdc_data_w));
}

/* Input ports */
static INPUT_PORTS_START( terak )
INPUT_PORTS_END


void terak_state::machine_reset()
{
}

void terak_state::video_start()
{
}

uint32_t terak_state::screen_update_terak(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}


void terak_state::terak(machine_config &config)
{
	/* basic machine hardware */
	T11(config, m_maincpu, 4'000'000); // FIXME: actually LSI-11
	m_maincpu->set_initial_mode(6 << 13);
	m_maincpu->set_addrmap(AS_PROGRAM, &terak_state::mem_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	screen.set_screen_update(FUNC(terak_state::screen_update_terak));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);
}

/* ROM definition */
ROM_START( terak )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "terak.rom", 0173000, 0x0080, CRC(fd654b8e) SHA1(273a9933b68a290c5aedcd6d69faa7b1d22c0344))

	ROM_REGION( 0x2000, "kbd", 0)
	// keytronic keyboard, roms are unlabelled, type 6301-1J. CPU is 30293E-003. No crystal.
	ROM_LOAD( "82s129.z2", 0x0000, 0x0100, CRC(a5dce419) SHA1(819197a03eb9b6ea3318f5afc37c0b436dd747a7) )
	ROM_LOAD( "82s129.z1", 0x0100, 0x0100, CRC(f34e061f) SHA1(3cb354b2680056d4b3234c680958d4591279ac8a) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME       FLAGS
COMP( 1977, terak, 0,      0,      terak,   terak, terak_state, empty_init, "Terak", "Terak 8510A", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



terco.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Joakim Larsson Edstrom
/*
 * The 4490 CNC Mill Control unit
 *
 * History of Terco
 *------------------
 * Terco, founded 1963, is a privately held company in Sweden that develops and distribute equipment for
 * technical vocational educations worldwide. In the mid 80s they had a number of state of the art
 * products for educations on CNC machines, both mill and lathe, all based on Motorola 8-bit CPU:s.
 *
 * Known products
 * --------------
 * T4426 - CNC Programming Station, a ruggedized Tandy Color Computer + monitor in a metal case (see coco12.cpp)
 * T4490 - CNC Control System, a complete system including a small milling machine, can be programmed by T4426
 * T???? - A CAD/CAM sold in small numbers, an office computer based on a Tandy Color Computer PCB + two floppy drives
 *
 * Misc links about the machines supported by this driver.
 *--------------------------------------------------------
 * https://www.westauction.com/auction/712/item/terco-cnc-programming-system-37047
 * https://kiertonet.fi/tyokalut-ja-koneet/koneet-ja-laitteet/cnc-pora-45-terco-4403
 * http://www.repair--parts.com/Repair-Electronics-/Rectifiers-/Terco-table-top-cnc-mill.php5
 *
 *  TODO:
 * ------
 *  - Display
 *  - Clickable Artwork
 *  - Serial communication for printer, plotter, paper tape and download from the T4426
 *  - Identify expansion bus
 *  - Keyboard Controller
 *  - Dump keyboard ROM
 *  - Cassette i/o
 */

#include "emu.h"
#include "cpu/m6800/m6800.h"
#include "machine/6821pia.h"
#include "machine/6850acia.h"
//#include "machine/kb3600.h"
//#include "machine/mc14411.h"

#define LOG_SETUP   (1U << 1)
#define LOG_READ    (1U << 2)
#define LOG_BCD     (1U << 3)

//#define VERBOSE (LOG_BCD|LOG_SETUP)
//#define LOG_OUTPUT_STREAM std::cout

#include "logmacro.h"

#define LOGSETUP(...) LOGMASKED(LOG_SETUP,   __VA_ARGS__)
#define LOGR(...)     LOGMASKED(LOG_READ,    __VA_ARGS__)
#define LOGBCD(...)   LOGMASKED(LOG_BCD,     __VA_ARGS__)

#ifdef _MSC_VER
#define FUNCNAME __func__
#else
#define FUNCNAME __PRETTY_FUNCTION__
#endif
/*
 * Identified Chips
 * ----------------
 * 11442   - 7-seg driver
 * 14411   - Bit Rate Generator
 * 1488    - Quad Line EIA-232D Driver
 * 1489    - Quad Line EIA-232D Receivers
 * 15005   - Reed relay
 * 2114    - 4 x 1024 bits of data
 * 2732    - 4KB EPROM
 * 4527    - BCD Rate Multiplexer
 * 555     - analog timer circuit
 * 6800    - 8 bit CPU
 * 6821    - PIA parallel interface
 * 6850    - ACIA serial interface
 * 7400    - Quad 2 input NAND gates
 * 7402    - Quad 2 input NOR gates
 * 7408    - Quad 2 input AND gates
 * 7414    - 6 hex schmitt trigger inverters
 * 7420    - Dual 4 input NAND gates
 * 7421    - Dual 4 input AND gates
 * 7476    - Dual JK flip flops with preset and clear
 * 7490    - Decade Counters, divide by 2 and divide by 5 outputs
 * 74116   - Dual 4-bit latches
 * 74138   - 3 to 8 line decoder/demultiplexer
 * 74139   - Dual 2 to 4 line decoder/demultiplexer
 * 74154   - 4 line to 16 line decoders/multiplexers
 * 74164   - 8 bit paralell output serial shift register
 * 74174   - 6 D-type flip flops
 * 74240   - Inverted 3-state outputs
 * 74244   - Non-inverted 3-state outputs
 * AY3600  - Keyboard Controller
 * CA339   - Quad Voltage Comparators
 * CD4050B - CMOS Hex Non-Inverting Buffer and Converter
 * CIC2414 - CMOS RAM suspects
 * DS1210  - NVRAM controller, inhibits access and supply battery power on power down
 * MC6875  - Phase clock generator
 *
 * Board Layouts
 * -------------
 *
 *          T4490 CPU board
 *        ______________________________________________________________
 *      |\                              ..                 .---.      _o|__
 *      ||o+----------+  +---+ +---+ .------. +-------+    |470|     | |   |
 *      || | 2732     |  |CIC| |CIC| |battery |74LS240|    | uF|     | |   |
 *    ///> | 4490-F   |  2414E 2414E |      | +-------+    |   |     | |   |
 *       | +----------+  |   | |   | |      | +-------+    |   |     | |   |
 *       |               |   | |   | |      | |74LS240|    |   |     | |   |
 *       | +----------+  +---+ +---+ |      | +-------+    .___.     | |   |
 *       | | 2732     |              .______.                o       | |   |
 *       | | 4490-E   |     +-----+  +-------+                       | |   |
 *       | +----------+     |DS1210  |4527BE |    +-----------+      | |   |
 *       |                  +-----+  +-------+    |           |      | |   |
 *       | +----------+                           | 74116N    |      | |   |
 *       | | 2732     |                           +-----------+      | |   |
 *       | | 4490-D   |    +-------+ +-------+                       | |   |
 *       | +----------+    |74LS90N| |4527BE |                       | |   |
 *       |                 +-------+ +-------+                       | |   |
 *       | +----------+                           +-----------+      |_|   |
 *       | | 2732     |   +-------+  +-------+    |           |        |___|
 *       | | 4490-C   |   | 7476  |  |4527BE |    | 74116N    |        o|
 *       | +----------+   +-------+  +-------+    +-----------+         |
 *       |                                                              |
 *       | R2 varistor                                                  |
 *       |  +---+ R1     +---------+ +-------+                          |
 *       |  |555| 3.9K   | 74LS244 | |74LS00 |                          |
 *       |  +---+        +---------+ +-------+                          |
 *       | C1 0.01uF                                                    |
 *       | C2 0.01uF                                                   o|__
 *       |  +----+                                                    _|   |
 *       | -|XTAL|                                                   | |   |
 *       | -|8MHz|                                                   | |   |
 *       |  +----+       +-----------------+                         | |   |
 *       |               |   CPU           |            +-------+    | |   |
 *       | +-------+     |   68B00         |            |74LS139|    | |   |
 *       | | 6875L |     +-----------------+            +-------+    | |   |
 *       | +-------+                                                 | |   |
 *       |                                                           | |   |
 *       | +-------+         +--------+ +--------+      +-------+    | |   |
 *       | | 74LS08|         |74LS244 | |74LS244 |      |74LS138|    | |   |
 *       | +-------+         +--------+ +--------+      +-------+    | |   |
 *       |                                                           | |   |
 *       |                                                           | |   |
 *       | +-------+  +-------++-------+ +-------+      +-------+    | |   |
 *       | |74LS164|  |74LS14 ||74LS02N| |74LS20N|      |74LS138|    | |   |
 *    \\\> +-------+  +-------++-------+ +-------+      +-------+    |_|   |
 *      ||                                                             |___|
 *      ||o                                                             o|
 *      |/---------------------------------------------------------------+
 *
 *
 *       T4490 I/O board
 *      . ______________________________________________________________
 *      |\                                    o  o                    _o|__
 *      ||o                      96..+------+.----. +-------+        | |   |
 *      ||                       72..|BRG   ||XTAL| +74LS240|        | |   |
 *    ///>            +-------+  48..|14411 ||1.84| +-------+        | |   |
 *    +-+|O|+------+  |CA339A |  36..|      || 320|                  | |   |
 *  +-|    ||15005B|  +-------+  24..|      |.____. +-------+        | |   |
 *  | |  ==|+------+             18..|      |       |74LS240|        | |   |
 *  | |  ==|                     12..|      |       +-------+        | |   |
 *  | |  ==|                      6..+------+                        | |   |
 *  | |  ==|          +-------+   3.. .----------.                   | |   |
 *  | |  ==|          |1488P  |   2..o| 470uF    |o +-------+        | |   |
 *  | |  ==|          +-------+   1.5 .__________.  |74LS244|        | |   |
 *  | |  ==|                      1.1 +----------+  +-------+        | |   |
 *  | |  ==|                          | 2732A    |                   | |   |
 *  +-|    |+-------+ +-------+       | 4490-B   |                   | |   |
 *    +-+==||CD4050BE |1489P  |       +----------+ +-------+         | |   |
 *  \\   |O|+-------+ +-------+                    |74LS244|         |_|   |
 *   \\---++------+ +------+                       +-------+           |___|
 *  --+   O|PIA   | |PIA   |   +------+  +-----------+                 o|
 *    |   ||68B21P| |68B21P|   |ACIA  |  | 2732A     |                  |
 *    |   ||      | |      |   |68B50P|  | 4490-A    |                  |
 *    +-+ ||      | |      |   |      |  +-----------+                  |
 *    --| ||      | |      |   |      |                                 |
 *    +-+ ||      | |      |   |      |  +-----------+  +-------+       |
 *    |   ||      | |      |   +------+  | 2732A     |  |74LS139|       |
 *    |   ||      | |      |             | 4490-3    |  +-------+       |
 *    |   |+------+ +------+             +-----------+                 o|__
 *  --+   O                                             +-------+     _|   |
 *   //---+                                             |74LS00N|    | |   |
 *  // ==o==                                            +-------+    | |   |
 *     ==o==                                                         | |   |
 *       |  +-------+  +-------+   +-------+  +-------+ +-------+    | |   |
 *       |  | 2114  |  | 2114  |   | 2114  |  | 2114  | |74LS21N|    | |   |
 *       |  +-------+  +-------+   +-------+  +-------+ +-------+    | |   |
 *       |                                                           | |   |
 *       |  +-------+  +-------+   +-------+  +-------+              | |   |
 *       |  | 2114  |  | 2114  |   | 2114  |  | 2114  |              | |   |
 *       |  +-------+  +-------+   +-------+  +-------+  +-------+   | |   |
 *       |                                               |74LS08N|   | |   |
 *       |  +-------+  +-------+   +-------+  +-------+  +-------+   | |   |
 *       |  | 2114  |  | 2114  |   | 2114  |  | 2114  |              | |   |
 *       |  +-------+  +-------+   +-------+  +-------+              | |   |
 *       |                                                           | |   |
 *    \\\>  +-------+  +-------+   +-------+  +-------+  +-------+   |_|   |
 *      ||  | 2114  |  | 2114  |   | 2114  |  | 2114  |  |74LS138|     |___|
 *      ||o +-------+  +-------+   +-------+  +-------+  +-------+      o|
 *      |/---------------------------------------------------------------+
 *
 *
 *      T4490 Front Panel PCB
 * .--------------------------------------------------------------------------------------------------------------------.
 * |                                                                                                          +--------+|
 * |+-----+-----+-----+-----+-----+-----+-----+-----+-----+   +-----+   +-----+-----+                         |        ||
 * ||11442|11442|11442|11442|11442|11442|11442|11442|11442|   |11442|   |11442|11442|                         |74116N  ||
 * |+-----+-----+-----+-----+-----+-----+-----+-----+-----+   +-----+   +-----+-----+    POSITION REGISTER    +--------+|
 * | +---+---+---+     +---+---+     +---+---+---+     +---+    +---+     +---+---+  +------+  +---+---+---+---+---+---+|
 * N |7sg|7sg|7sg|   G |7sg|7sg|   F |7sg|7sg|7sg|   S |7sg|  T |7sg|   M |7sg|7sg|  |sparse|  |7sg|7sg|7sg|7sg|7sg|7sg||
 * | |BCD|BCD|BCD|     |BCD|BCD|     |BCD|BCD|BCD|     |BCD|    |BCD|     |BCD|BCD|  |DotLed|  |BCD|BCD|BCD|BCD|BCD|BCD||
 * | +---+---+---+     +---+---+     +---+---+---+     +---+    +---+     +---+---+  |matrix|  +---+---+---+---+---+---+|
 * |   BLOCK NBR        G-FUNCT       FEED-SPEED+-------+SPIN-  TOOL   +-------+ F-  +------++---+---+---+---+---+---+  |
 * |PWR  RUN  MAN  EXA  LOAD DUMP BBB  FULL PAR | 74116 | DLE    NBR   | 74116 | FUNCT       |114|114|114|114|114|114|  |
 * | O    O    O    O    O    O    O    O    O  +-------+              +-------+             | 42| 42| 42| 42| 42| 42|  |
 * | +------+  +--------+ +--------+ SYSTEM STATUS                                           |   |   |   |   |   |   |  |
 * | |74LS00|  |74LS240N| |74LS240N|                                                         +---+---+---+---+---+---+  |
 * | +------+  +--------+ +--------+                                                                                    |
 * ----------------------------------------------------------------------------------------------------------------------
 * O                                    O                                    O                                          O
 * ----------------------------------------------------------------------------------------------------------------------
 * |.----.----.----.----.----.----.----.----.        .----.----.----.----.----.----.----.----.            +-------+     |
 * ||STOP|STRT|PROG|EXAM|LOAD|DUMP|BLK |TO  |        |RST |SET |READ|CHNG|INS |ERA |END |END |            |74154J |     |
 * ||    |    |    |    |TAPE|TAPE|BBLK| REF|        |    | REF|    |    | BLK| BLK| BLK| PRG|      DATA  +-------+     |
 * |'----'----'----'----'----'----'----'----'        '----'----'----'----'----'----'----'----'        .----.----.----.  |
 * |                +--------+ SYSTEM CONTROL         PROGRAMMING CONTROL                             |    |    |    |  |
 * |                |74LS244N|                                                                        | 7  | 8  | 9  |  |
 * |                +--------+                                                                        +----+----+----+  |
 * |                                                                                                  |    |    |    |  |
 * |          JOG CONTROL                                 FUNCTION             COORDINATE             | 4  | 5  | 6  |  |
 * |.----.----.----.  +--------+                      .----.----.----.      .----.----.----.          +----+----+----+  |
 * ||    |    |    |  |74LS174N|                      |    |    |    |      |    |    |    |          |    |    |    |  |
 * || X+ | Y+ | Z+ |  +--------+.--.                  |CTRL| G  | F  |      | X  | Y  | Z  |          | 1  | 2  | 3  |  |
 * |+----+----+----+           |    |                 +----+----+----+      +----+----+----+          +----+----+----+  |
 * ||    |    |    |  +--------+'--'                  |    |    |    |      |    |    |    |          |    |    |    |  |
 * || X- | Y- | Z- |  |74LS240N|                      | S  | T  | M  |      | I  | J  | K  |          | +  | 0  | -  |  |
 * |+----+----+----+  +--------+                      +----+---++----+      +----+----+----+          '----'----'----'  |
 * |                                                       +---+                                                        |
 * |                  .-------------------.                |74 |                                                        |
 * |                  |       o    o      |                |240|                                                        |
 * |                  | +----+o    o+----+|+-----+         |   |              .-----------------------------------------'
 * |                  | |AY-5|o    o|    |||PIA  |         +---+              |
 * |                  | |3600|o    o|2732|||68B21|                            |
 * |                  | |PRO-|o    o|TBORD||     |     FEED OVERRIDE %        |
 * |                  | | 50 |o    o|    |||     |                            |
 * |                  | |    |o    o+----+||     |          .--.              |
 * |                  | |    |o    o      ||     |         |    |             |
 * |                  | +----+o    o      |+-----+          '--'              |
 * |                  '-------------------'                                   |
 * ---------------------------------------------------------------------------'------------------------------------------
 * O                                    O                                    O                                          O
 * ----------------------------------------------------------------------------------------------------------------------
 */


namespace {

/* Terco CNC Control Station 4490 */
class t4490_state : public driver_device
{
public:
	t4490_state(const machine_config &mconfig, device_type type, const char *tag)
	: driver_device(mconfig, type, tag)
	,m_maincpu(*this, "maincpu")
	,m_pia1(*this, "pia1")
	,m_pia2(*this, "pia2")
	//,m_pia3(*this, "pia3")
	//,m_acia(*this, "acia")
	//,m_brg(*this, "brg")
	//,m_ay3600(*this, "ay3600")
	{ }

	void t4490(machine_config &config);

private:
	void t4490_map(address_map &map) ATTR_COLD;
	required_device<m6800_cpu_device> m_maincpu;
  //    virtual void machine_reset() override { m_maincpu->reset(); LOG("--->%s()\n", FUNCNAME); };
	required_device<pia6821_device> m_pia1;
	required_device<pia6821_device> m_pia2;
	//required_device<pia6821_device> m_pia3;
	//required_device<acia6850_device> m_acia;
	//required_device<mc14411_device> m_brg;
	//required_device<ay3600_device> m_ay3600;
};

void t4490_state::t4490_map(address_map &map)
{
	map(0x0000, 0x1fff).ram();
	map(0x3000, 0x3fff).rom().region("maincpu", 0x3000);
	map(0x9500, 0x95ff).ram();
	map(0x9030, 0x9031).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0x9034, 0x9037).rw(m_pia1, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x9038, 0x903b).rw(m_pia2, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xa000, 0xffff).rom().region("maincpu", 0xa000);
}

/* Input ports */
static INPUT_PORTS_START( t4490 )
INPUT_PORTS_END

void t4490_state::t4490(machine_config &config)
{
	M6800(config, m_maincpu, XTAL(8'000'000)/4); // divided by a MC6875
	m_maincpu->set_addrmap(AS_PROGRAM, &t4490_state::t4490_map);

	/* devices */
	PIA6821(config, m_pia1);
	PIA6821(config, m_pia2);
	ACIA6850(config, "acia", 0);
}

ROM_START( t4490 )
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD( "terco4490-3861104.bin", 0x3000, 0x1000, CRC(d5fd17cc) SHA1(9a3564fa69b897ec51b49ad34f2d2696cb78ee9b) )
	ROM_LOAD( "terco4490-a861104.bin", 0xa000, 0x1000, CRC(65b8e7d0) SHA1(633217fc4aa301d87790bb8744b72ef030a4c262) )
	ROM_LOAD( "terco4490-b861104.bin", 0xb000, 0x1000, CRC(5a0ce3f2) SHA1(7ec455b9075454ce5943011a1dfb5725857168f5) )
	ROM_LOAD( "terco4490-c861104.bin", 0xc000, 0x1000, CRC(0627c68c) SHA1(bf733d3ffad3f1e75684e833afc9d10d33ca870f) )
	ROM_LOAD( "terco4490-d861104.bin", 0xd000, 0x1000, CRC(2156476d) SHA1(0d70c6285541746ef15cad0d47b2d752e228abfc) )
	ROM_LOAD( "terco4490-e861104.bin", 0xe000, 0x1000, CRC(b317fa37) SHA1(a2e037a3a88b5d780067a86e52c6f7c103711a98) )
	ROM_LOAD( "terco4490-f861104.bin", 0xf000, 0x1000, CRC(a45bc3e7) SHA1(e12efa9a4c72e4bce1d59ad359ee66d7c3babfa6) )
ROM_END

} // anonymous namespace


//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY     FULLNAME                       FLAGS
COMP( 1986, t4490, 0,      0,      t4490,   t4490, t4490_state, empty_init, "Terco AB", "Terco 4490 Mill CNC Control", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



terminal.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Skeleton driver for numerous random terminals

2018-08-08

http://oldcomputer.info/terminal/



****************************************************************************/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"


namespace {

class terminal_state : public driver_device
{
public:
	terminal_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void terminal(machine_config &config);

private:

	void mem_map(address_map &map) ATTR_COLD;
	required_device<cpu_device> m_maincpu;
};


void terminal_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).rom();
}



/* Input ports */
static INPUT_PORTS_START( terminal )
INPUT_PORTS_END


void terminal_state::terminal(machine_config &config)
{
	I8031(config, m_maincpu, 12'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &terminal_state::mem_map);
}


/* ROM definition */

// for French Minitel network
ROM_START( alcat258 ) // MSM80C154 (+ TS9347// 8k ram // b&w
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "polish.bin",   0x0000, 0x8000, CRC(ce90f550) SHA1(fca5311704ca9e4d57414cfed96bb2a8ff73a145) )

	ROM_REGION( 0x0100, "user1", 0 )
	ROM_LOAD( "serial.bin",   0x0000, 0x0100, CRC(f0b99b8f) SHA1(906c285fd327eba2ba9798695acc456535b84570) )
ROM_END


ROM_START( loewed ) // order unknown // i8031, i8051(xtal 11.000 next to it), ITT LOTTI // 64k ram + battery-backed nvram // b&w
	ROM_REGION( 0x28000, "maincpu", 0 )
	ROM_LOAD( "mainboard_18764_100.bin", 0x00000, 0x020000, CRC(f9ec7591) SHA1(1df7bdf33b8086166f1addb686a911a0c52dde32) )
	ROM_LOAD( "module_19315_056.bin",    0x20000, 0x008000, CRC(b333c5ed) SHA1(93cfa95e595bea83fe1b34a1426b80ceb1755c50) )
ROM_END


ROM_START( loewe715 ) // i8051, ITT LOTTI // 64k ram // colour
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "eprom.bin",    0x00000, 0x10000, CRC(2668b944) SHA1(b7773c4d7a1e0dde2a2b414ae76e5faa1fa5e324) )
ROM_END


ROM_START( 7951om ) // TTL (no cpu) // 1k x 6bits display ram 64-characters uppercase only, screen 40x12 // green
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "prom1_rear.bin", 0x0000, 0x0100, CRC(ab231a4c) SHA1(1412d0e9163125f28a777717c4dd9d5fd54b5196) )
	ROM_LOAD( "prom2.bin",      0x0100, 0x0100, CRC(5d65b9b6) SHA1(2ea22beb6edbedb1d215b4c55233af897cdeb535) )

	ROM_REGION( 0x0800, "chargen", ROMREGION_INVERT )
	ROM_LOAD( "7951om.bin",     0x0000, 0x0800, CRC(36fc61c6) SHA1(6b5e8701b185b32a1a2630ddfc5402345628ecba) )
ROM_END


ROM_START( teleguide ) // order unknown // i8051, i8031 (layout very similar to loewed) // 64k ram + battery-backed nvram // b&w
	ROM_REGION( 0x38000, "maincpu", 0 )
	ROM_LOAD( "cardreader_17044-068_349-1163.bin", 0x00000, 0x10000, CRC(3c980c0d) SHA1(9904ffd283a11defbe3daf2cb9029bcead8b02d0) )
	ROM_LOAD( "mainboard_18764-063_349-1173.bin",  0x10000, 0x20000, CRC(eb5c2d05) SHA1(dba2f72f928487e83741ad24d70b568e4510988e) )
	ROM_LOAD( "module_19315-051_349-01173.bin",    0x20000, 0x08000, CRC(29c4b49d) SHA1(9bf37616eb130cb6bf86954b4a4952ea99d43ce8) )
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME       PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT          COMPANY             FULLNAME                     FLAGS */
COMP( 1991, alcat258,  0,      0,      terminal, terminal, terminal_state, empty_init, "Alcatel",            "Terminatel 258",         MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1992, loewed,    0,      0,      terminal, terminal, terminal_state, empty_init, "Loewe",              "Multitel D",             MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1988, loewe715,  0,      0,      terminal, terminal, terminal_state, empty_init, "Loewe",              "Multicom 715L",          MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1987, 7951om,    0,      0,      terminal, terminal, terminal_state, empty_init, "Mera-Elzab",         "7951om",                 MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1992, teleguide, 0,      0,      terminal, terminal, terminal_state, empty_init, "Loewe / Televerket", "Teleguide",              MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



test_t400.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

    T400 uController test suite for COP410/420 series CPUs

    http://opencores.org/project,t400

*/

#include "emu.h"
#include "cpu/cop400/cop400.h"


namespace {

class t400_test_suite_state : public driver_device
{
public:
	t400_test_suite_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag) ,
		m_maincpu(*this, "maincpu") { }

	void test_t410(machine_config &config);
	void test_t420(machine_config &config);

private:
	void port_l_w(uint8_t data);
	required_device<cop400_cpu_device> m_maincpu;
};

void t400_test_suite_state::port_l_w(uint8_t data)
{
//  printf("L: %u\n", data);
}

void t400_test_suite_state::test_t410(machine_config &config)
{
	COP410(config, m_maincpu, 1000000);
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false);
	m_maincpu->write_l().set(FUNC(t400_test_suite_state::port_l_w));
}

void t400_test_suite_state::test_t420(machine_config &config)
{
	COP420(config, m_maincpu, 1000000);
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, true);
	m_maincpu->write_l().set(FUNC(t400_test_suite_state::port_l_w));
}

ROM_START( test410 )
	ROM_REGION( 0x200, "maincpu", 0 )
	ROM_LOAD( "rom_41x.bin", 0x000, 0x200, NO_DUMP )
ROM_END

ROM_START( test420 )
	ROM_REGION( 0x400, "maincpu", 0 )
	ROM_LOAD( "rom_42x.bin", 0x000, 0x400, BAD_DUMP CRC(e4e80001) SHA1(8fdca9d08de1cc83387a7d141f6b254117902442) )
ROM_END

} // anonymous namespace


COMP( 2008, test410, 0,       0, test_t410, 0, t400_test_suite_state, empty_init, "T400 uController project", "T410 test suite", MACHINE_NO_SOUND_HW )
COMP( 2008, test420, test410, 0, test_t420, 0, t400_test_suite_state, empty_init, "T400 uController project", "T420 test suite", MACHINE_NO_SOUND_HW )



testconsole.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Vas Crabb
/*

Westinghouse Test Console

This appears to be a wire-wrapped prototype of a device for field-
programming some kind of interlocking system (possibly railway
signalling or industrial automation).  Chips have date codes up to
8020, but there doesn't appear to be a copyright date in ROM, and the
nameplate has no date either.  There's a 110/220 switch on the side,
and the AC wiring uses the brown, blue, green/yellow standard.


Nameplate says:

Westinghouse
TEST CONSOLE
SERIAL #5
ELECTRONIC SYSTEMS DIVISION
WESTINGHOUSE CANADA LTD. ES-595C303


Key components:

* 6.144 MHz crystal
* Intel 8085A CPU
* Intel P8155 I/O, timer, RAM
* Intel 8255 PIA
* 2x D2732 EPROM (8 kB total)
* 8x 2114P45 RAM (4 kB total)
* 4x DL-1416 quad 16-segment displays
* 3x red LED
* 24-pin ZIF socket for burning 2716 or 2732
* 2x 4x4 keypad
* two insulated trimpots (programming voltage adjustment?)
* a sea of 7400 logic
* two DIP switches


Keypad layout (second pad is actually to the right of first):

MODE        TRAP/HEX    ARM/DEC     RESET/CLR
MNE/+       HARD/-      TRACE/×     REL/÷
REC/BCC     SOFT/LIST   DISP/VER    STEP/PROG
left/up     right/down  X/TRAN      ENTER

C/MW        D/IW        E/OF        F/IA
8/XX        9/HT        A/MR        B/IR
4           5           6           7
0           1           2           3

TODO:
* 8155/8255 peripherals
* Timers/interrupts
* EPROM programming
* Debugging target machine(s)
* DIP switches

*/

#include "emu.h"

#include "cpu/i8085/i8085.h"
#include "machine/i8155.h"
#include "machine/i8255.h"
#include "video/dl1416.h"

#include "whousetc.lh"

namespace {

class whouse_testcons_state : public driver_device
{
public:
	whouse_testcons_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_dsp(*this, "dsp%u", 0U)
		, m_digit(*this, "digit%u", 0U)
	{
	}

	void whousetc(machine_config &config);

private:
	template <unsigned Dsp> void update_dsp(offs_t offset, u16 data)
	{
		m_digit[(Dsp << 2) | offset] = data;
	}

	virtual void machine_start() override
	{
		m_digit.resolve();
	}

	virtual void machine_reset() override
	{
		for (required_device<dl1416_device> const &dsp : m_dsp)
			dsp->cu_w(1);
	}

	void io_map(address_map &map) ATTR_COLD;
	void program_map(address_map &map) ATTR_COLD;

	required_device_array<dl1416_device, 4> m_dsp;
	output_finder<16> m_digit;
};


void whouse_testcons_state::program_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom();
	map(0x2000, 0x2003).w("dsp0", FUNC(dl1416_device::bus_w));
	map(0x2004, 0x2007).w("dsp1", FUNC(dl1416_device::bus_w));
	map(0x2008, 0x200b).w("dsp2", FUNC(dl1416_device::bus_w));
	map(0x200c, 0x200f).w("dsp3", FUNC(dl1416_device::bus_w));
	map(0x2800, 0x28ff).rw("i8155", FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w));
	map(0x3000, 0x3fff).ram();
	map(0x8800, 0x8800).portr("row0");
	map(0x8801, 0x8801).portr("row1");
	map(0x8802, 0x8802).portr("row2");
	map(0x8803, 0x8803).portr("row3");
}

void whouse_testcons_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x80, 0x87).rw("i8155", FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
	map(0x88, 0x8b).rw("i8255", FUNC(i8255_device::read), FUNC(i8255_device::write));
}


INPUT_PORTS_START(whousetc)
	PORT_START("row0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1)     PORT_NAME("MODE")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2)     PORT_NAME("TRAP / HEX")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3)     PORT_NAME("ARM / DEC")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4)     PORT_NAME("RESET / CLR")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5)     PORT_NAME("C / MW")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6)     PORT_NAME("D / IW")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7)     PORT_NAME("E / OF")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8)     PORT_NAME("F / IA")

	PORT_START("row1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q)     PORT_NAME("MNE / +")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_W)     PORT_NAME("HARD / -")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_E)     PORT_NAME(u8"TRACE / ×")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_R)     PORT_NAME(u8"REL / ÷")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_T)     PORT_NAME("8 / XX")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_Y)     PORT_NAME("9 / HT")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_U)     PORT_NAME("A / MR")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_I)     PORT_NAME("B / IR")

	PORT_START("row2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_A)     PORT_NAME("REC / BCC")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_S)     PORT_NAME("SOFT / LIST")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_D)     PORT_NAME("DISP / VER")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_F)     PORT_NAME("STEP / PROG")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_G)     PORT_NAME("4")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_H)     PORT_NAME("5")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_J)     PORT_NAME("6")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_K)     PORT_NAME("7")

	PORT_START("row3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_Z)     PORT_NAME(u8"\u2190 / \u2191") // U+2190 = ←, U+2191 = ↑
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_X)     PORT_NAME(u8"\u2192 / \u2193") // U+2192 = →, U+2193 = ↓
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_C)     PORT_NAME("X / TRAN")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_V)     PORT_NAME("ENTER")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_B)     PORT_NAME("0")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_N)     PORT_NAME("1")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_M)     PORT_NAME("2")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_COMMA) PORT_NAME("3")
INPUT_PORTS_END


void whouse_testcons_state::whousetc(machine_config &config)
{
	i8085a_cpu_device &maincpu(I8085A(config, "maincpu", 6.144_MHz_XTAL));
	maincpu.set_addrmap(AS_PROGRAM, &whouse_testcons_state::program_map);
	maincpu.set_addrmap(AS_IO, &whouse_testcons_state::io_map);

	I8155(config, "i8155", 6.144_MHz_XTAL);

	I8255(config, "i8255", 0);

	DL1416B(config, m_dsp[0], u32(0));
	m_dsp[0]->update().set(FUNC(whouse_testcons_state::update_dsp<0>));

	DL1416B(config, m_dsp[1], u32(0));
	m_dsp[1]->update().set(FUNC(whouse_testcons_state::update_dsp<1>));

	DL1416B(config, m_dsp[2], u32(0));
	m_dsp[2]->update().set(FUNC(whouse_testcons_state::update_dsp<2>));

	DL1416B(config, m_dsp[3], u32(0));
	m_dsp[3]->update().set(FUNC(whouse_testcons_state::update_dsp<3>));

	config.set_default_layout(layout_whousetc);
}


ROM_START(whousetc)
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("ofm.g2", 0x0000, 0x1000, CRC(40bf8973) SHA1(7815d36532a40384f6ae2a38df3140c1e70a06aa))
	ROM_LOAD("ipc.a3", 0x1000, 0x1000, CRC(16f1e109) SHA1(0c6086f42c3cf6d79b1b54ab5c2dae8523c96d69))
ROM_END

} // anonymous namespace

//    YEAR   NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS                  INIT        COMPANY         FULLNAME                  FLAGS
COMP( 1980?, whousetc, 0,      0,      whousetc, whousetc, whouse_testcons_state, empty_init, "Westinghouse", "Test Console Serial #5", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )



testpat.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************

TV test pattern generators

Radio, 1983, N5
    http://radioway.ru/1983/05/generator_telesignalov.html
    http://radioway.ru/1984/04/generator_telesignalov.html

Radio, 1985, N6
    http://radioway.ru/1985/06/generator_ispytatelnyh_signalov.html

***************************************************************************/

#include "emu.h"

#include "machine/netlist.h"
#include "netlist/devices/net_lib.h"

#include "nl_tp1983.h"
#include "nl_tp1985.h"
#include "video/fixfreq.h"

#include "screen.h"

#include <cmath>


namespace {

#define MASTER_CLOCK    (4000000)
#define V_TOTAL_PONG    315
#define H_TOTAL_PONG    256     // tbc

class tp1983_state : public driver_device
{
public:
	tp1983_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_video(*this, "fixfreq")
	{
	}

	// devices
	required_device<netlist_mame_device> m_maincpu;
	required_device<fixedfreq_device> m_video;

	void tp1983(machine_config &config);

protected:
	// driver_device overrides
	virtual void machine_start() override { }
	virtual void machine_reset() override { }

private:
};


class tp1985_state : public driver_device
{
public:
	tp1985_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_video(*this, "fixfreq")
	{ }

	// devices
	required_device<netlist_mame_device> m_maincpu;
	required_device<fixedfreq_device> m_video;

	void tp1985(machine_config &config);

protected:
	// driver_device overrides
	virtual void machine_start() override { }
	virtual void machine_reset() override { }

private:
	NETDEV_ANALOG_CALLBACK_MEMBER(video_out_cb);
};

NETDEV_ANALOG_CALLBACK_MEMBER(tp1985_state::video_out_cb)
{
	m_video->update_composite_monochrome(4.0 - data, time);
}


static INPUT_PORTS_START(tp1983)
INPUT_PORTS_END

static INPUT_PORTS_START(tp1985)
INPUT_PORTS_END


void tp1983_state::tp1983(machine_config &config)
{
	NETLIST_CPU(config, m_maincpu, netlist::config::DEFAULT_CLOCK()).set_source(netlist_tp1983);

	NETLIST_ANALOG_OUTPUT(config, "maincpu:vid0").set_params("videomix", m_video, FUNC(fixedfreq_device::update_composite_monochrome));

	SCREEN(config, "screen", SCREEN_TYPE_RASTER);
	FIXFREQ(config, m_video).set_screen("screen");
	m_video->set_monitor_clock(MASTER_CLOCK);
	m_video->set_horz_params(H_TOTAL_PONG-64,H_TOTAL_PONG-40,H_TOTAL_PONG-8,H_TOTAL_PONG);
	m_video->set_vert_params(V_TOTAL_PONG-19,V_TOTAL_PONG-16,V_TOTAL_PONG-12,V_TOTAL_PONG);
	m_video->set_fieldcount(1);
	m_video->set_threshold(1);
	m_video->set_gain(0.36);
}

void tp1985_state::tp1985(machine_config &config)
{
	NETLIST_CPU(config, m_maincpu, netlist::config::DEFAULT_CLOCK()).set_source(netlist_tp1985);

	NETLIST_ANALOG_OUTPUT(config, "maincpu:vid0").set_params("videomix", FUNC(tp1985_state::video_out_cb));

	SCREEN(config, "screen", SCREEN_TYPE_RASTER);
	FIXFREQ(config, m_video).set_screen("screen");
	m_video->set_monitor_clock(MASTER_CLOCK);
	m_video->set_horz_params(H_TOTAL_PONG-80,H_TOTAL_PONG-56,H_TOTAL_PONG-8,H_TOTAL_PONG);
	m_video->set_vert_params(V_TOTAL_PONG-19,V_TOTAL_PONG-15,V_TOTAL_PONG-12,V_TOTAL_PONG);
	m_video->set_fieldcount(1);
	m_video->set_threshold(1);
	m_video->set_gain(0.36);
}


ROM_START( tp1983 ) /* dummy to satisfy game entry*/
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
ROM_END

ROM_START( tp1985 ) /* dummy to satisfy game entry*/
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
ROM_END

} // anonymous namespace


SYST(  1983, tp1983, 0, 0, tp1983,   tp1983,    tp1983_state,   empty_init, "Radio", "TV Test Pattern Generator 1983", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)
SYST(  1985, tp1985, 0, 0, tp1985,   tp1985,    tp1985_state,   empty_init, "Radio", "TV Test Pattern Generator 1985", MACHINE_NO_SOUND_HW)



textelcomp.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Skeleton driver for Textel Compact portable digital teletype machine.

*******************************************************************************/

#include "emu.h"
#include "cpu/m6502/g65sc02.h"
#include "machine/input_merger.h"
#include "machine/6522via.h"
#include "machine/mos6551.h"
#include "machine/msm58321.h"
#include "video/sed1330.h"
#include "emupal.h"
#include "screen.h"


namespace {

class textelcomp_state : public driver_device
{
public:
	textelcomp_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rtc(*this, "rtc")
		, m_chargen(*this, "chargen")
		, m_keys(*this, "KEY%X", 0U)
		, m_leds(*this, "led%u", 0U)
		, m_keyscan(0)
		, m_shift_register(0)
		, m_shift_data(true)
		, m_shift_clock(true)
	{ }

	void textelcomp(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	void keyscan_w(u8 data);
	u8 keyboard_r();
	void shift_data_w(int state);
	void shift_clock_w(int state);
	void update_shift_output();
	void rtc_w(u8 data);

	void mem_map(address_map &map) ATTR_COLD;
	void lcdc_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<msm58321_device> m_rtc;
	required_region_ptr<u8> m_chargen;
	required_ioport_array<16> m_keys;
	output_finder<16> m_leds;

	u8 m_keyscan;
	u16 m_shift_register;
	bool m_shift_data;
	bool m_shift_clock;
};


void textelcomp_state::machine_start()
{
	m_rtc->cs1_w(1);
	subdevice<mos6551_device>("acia")->write_cts(0);

	m_leds.resolve();

	save_item(NAME(m_keyscan));
	save_item(NAME(m_shift_register));
	save_item(NAME(m_shift_data));
	save_item(NAME(m_shift_clock));
}

void textelcomp_state::keyscan_w(u8 data)
{
	// 2x TC4094BP driving keyboard lights
	if (BIT(data, 4) && !BIT(m_keyscan, 4))
		update_shift_output();

	m_keyscan = data;
}

u8 textelcomp_state::keyboard_r()
{
	return m_keys[m_keyscan & 0x0f]->read();
}

void textelcomp_state::shift_data_w(int state)
{
	m_shift_data = state;
}

void textelcomp_state::shift_clock_w(int state)
{
	if (state && !m_shift_clock)
	{
		m_shift_clock = true;
		m_shift_register = (m_shift_register << 1) | m_shift_data;
		if (BIT(m_keyscan, 4))
			update_shift_output();
	}
	else if (!state && m_shift_clock)
		m_shift_clock = false;
}

void textelcomp_state::update_shift_output()
{
	for (int i = 0; i < 16; i++)
		m_leds[i] = BIT(m_shift_register, i);
}

void textelcomp_state::rtc_w(u8 data)
{
	// Minimum address/data setup time is given as 0 µs in Oki and Epson datasheets
	// Address and data are written to the VIA at the same time as the control strobes
	if (!BIT(data, 5))
		m_rtc->write_w(0);
	if (!BIT(data, 6))
		m_rtc->read_w(0);
	if (!BIT(data, 7))
		m_rtc->address_write_w(0);

	m_rtc->d0_w(BIT(data, 0));
	m_rtc->d1_w(BIT(data, 1));
	m_rtc->d2_w(BIT(data, 2));
	m_rtc->d3_w(BIT(data, 3));

	if (BIT(data, 5))
		m_rtc->write_w(1);
	if (BIT(data, 6))
		m_rtc->read_w(1);
	if (BIT(data, 7))
		m_rtc->address_write_w(1);
}


void textelcomp_state::mem_map(address_map &map)
{
	map(0x0000, 0x1eff).ram(); // MB8464A-10L (battery backed?)
	map(0x1f00, 0x1f0f).m("via0", FUNC(via6522_device::map));
	map(0x1f10, 0x1f1f).m("via1", FUNC(via6522_device::map));
	map(0x1f20, 0x1f2f).m("via2", FUNC(via6522_device::map));
	map(0x1f30, 0x1f3f).m("via3", FUNC(via6522_device::map));
	map(0x1f40, 0x1f43).rw("acia", FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x1f70, 0x1f70).rw("lcdc", FUNC(sed1330_device::status_r), FUNC(sed1330_device::data_w));
	map(0x1f71, 0x1f71).rw("lcdc", FUNC(sed1330_device::data_r), FUNC(sed1330_device::command_w));
	map(0x4000, 0x7fff).ram(); // HY62256ALP-10 (battery backed?)
	map(0x8000, 0x9fff).ram(); // MB8464A-10L (battery backed?)
	map(0xa000, 0xffff).rom().region("maincpu", 0x2000);
}

void textelcomp_state::lcdc_map(address_map &map)
{
	map(0x0000, 0x1fff).ram();
	map(0xf000, 0xffff).rom().region("chargen", 0x1000);
}


static INPUT_PORTS_START(textelcomp)
	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_SHIFT_1) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x10
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x13
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x80/0x81
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x90/0x91
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0xa0
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0xa1
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0xa4

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift Lock") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x11
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x14
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Lösch") PORT_CHAR(0x08) PORT_CODE(KEYCODE_TAB) // left of Q
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR(0x01) PORT_CODE(KEYCODE_A)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR(0x19) PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0xa5

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN) // modifier
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x12
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x15
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR('"') PORT_CODE(KEYCODE_2)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('q') PORT_CHAR('Q') PORT_CHAR(0x11) PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR(0x13) PORT_CODE(KEYCODE_S)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(0x18) PORT_CODE(KEYCODE_X)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0xa8

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_SHIFT_2) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR(0x17) PORT_CODE(KEYCODE_W)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR(0x04) PORT_CODE(KEYCODE_D)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR(0x03) PORT_CODE(KEYCODE_C)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0xa9

	PORT_START("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x1b
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x20
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x21
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR('+') PORT_CODE(KEYCODE_4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(0x05) PORT_CODE(KEYCODE_E)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR(0x06) PORT_CODE(KEYCODE_F)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR(0x16) PORT_CODE(KEYCODE_V)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x0d) PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x22
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR(0x12) PORT_CODE(KEYCODE_R)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR(0x07) PORT_CODE(KEYCODE_G)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('b') PORT_CHAR('B') PORT_CODE(KEYCODE_B)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(' ') PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR('&') PORT_CODE(KEYCODE_6)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(0x14) PORT_CODE(KEYCODE_T)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('h') PORT_CHAR('H') PORT_CODE(KEYCODE_H)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(0x0e) PORT_CODE(KEYCODE_N)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x40
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x44
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR('/') PORT_CODE(KEYCODE_7)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(0x1a) PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('j') PORT_CHAR('J') PORT_CHAR(0x0a) PORT_CODE(KEYCODE_J)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_M)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x41
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x45
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('8') PORT_CHAR('(') PORT_CODE(KEYCODE_8)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(0x15) PORT_CODE(KEYCODE_U)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR(0x0b) PORT_CODE(KEYCODE_K)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',') PORT_CHAR(';') PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x50
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x46
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('9') PORT_CHAR(')') PORT_CODE(KEYCODE_9)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('i') PORT_CHAR('I') PORT_CHAR(0x09) PORT_CODE(KEYCODE_I)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR(0x0c) PORT_CODE(KEYCODE_L)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('.') PORT_CHAR(':') PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEYA")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x43
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0') PORT_CODE(KEYCODE_0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(0x0f) PORT_CODE(KEYCODE_O)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ö") PORT_CHAR(0xf6, 0x7c) PORT_CHAR(0xd6, 0x5c) PORT_CHAR(0x1c) PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('-') PORT_CHAR('=') PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEYB")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x60
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0xdf, 0x7e) PORT_CHAR('?') PORT_CODE(KEYCODE_MINUS) // ß
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR(0x10) PORT_CODE(KEYCODE_P)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ä") PORT_CHAR(0xe4, 0x7b) PORT_CHAR(0xc4, 0x5b) PORT_CHAR(0x1b) PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEYC")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x30/0x31
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('*') PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Ü") PORT_CHAR(0xfc, 0x7d) PORT_CHAR(0xdc, 0x5d) PORT_CHAR(0x1d) PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEYD")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x71
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEYE")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN) // code 0x70
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEYF")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END


void textelcomp_state::textelcomp(machine_config &config)
{
	G65SC02(config, m_maincpu, 3.6864_MHz_XTAL / 2); // G65SC02P-2 (clock not verified)
	m_maincpu->set_addrmap(AS_PROGRAM, &textelcomp_state::mem_map);

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, G65SC02_IRQ_LINE);

	via6522_device &via0(R65C22(config, "via0", 3.6864_MHz_XTAL / 2)); // G65SC22P-2
	via0.irq_handler().set("mainirq", FUNC(input_merger_device::in_w<0>));

	R65C22(config, "via1", 3.6864_MHz_XTAL / 2); // G65SC22P-2
	// IRQ might be connected on hardware, but is never enabled

	via6522_device &via2(R65C22(config, "via2", 3.6864_MHz_XTAL / 2)); // G65SC22P-2
	via2.readpa_handler().set(FUNC(textelcomp_state::keyboard_r));
	via2.writepb_handler().set(FUNC(textelcomp_state::keyscan_w));
	via2.cb1_handler().set(FUNC(textelcomp_state::shift_clock_w));
	via2.cb2_handler().set(FUNC(textelcomp_state::shift_data_w));

	via6522_device &via3(R65C22(config, "via3", 3.6864_MHz_XTAL / 2)); // G65SC22P-2
	via3.writepa_handler().set(FUNC(textelcomp_state::rtc_w));
	via3.ca2_handler().set(m_rtc, FUNC(msm58321_device::cs2_w)).invert();
	via3.ca2_handler().append(m_rtc, FUNC(msm58321_device::stop_w)).invert();
	// TODO: PB7 toggling generates beeps?

	mos6551_device &acia(MOS6551(config, "acia", 3.6864_MHz_XTAL / 2)); // G65SC51P-2
	acia.set_xtal(3.6864_MHz_XTAL / 2);
	acia.irq_handler().set("mainirq", FUNC(input_merger_device::in_w<1>));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_color(rgb_t(0x00, 0xff, 0x80));
	screen.set_refresh_hz(50);
	screen.set_size(640, 201);
	screen.set_visarea(0, 640-1, 0, 201-1);
	screen.set_palette("palette");
	screen.set_screen_update("lcdc", FUNC(sed1330_device::screen_update));

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	sed1330_device &lcdc(SED1330(config, "lcdc", 6.4_MHz_XTAL)); // SED1330F + B&W LCD
	lcdc.set_addrmap(0, &textelcomp_state::lcdc_map);
	lcdc.set_screen("screen");

	MSM58321(config, m_rtc, 32.768_kHz_XTAL); // RTC58321A
	m_rtc->set_default_24h(true);
	m_rtc->d0_handler().set("via3", FUNC(via6522_device::write_pa0));
	m_rtc->d1_handler().set("via3", FUNC(via6522_device::write_pa1));
	m_rtc->d2_handler().set("via3", FUNC(via6522_device::write_pa2));
	m_rtc->d3_handler().set("via3", FUNC(via6522_device::write_pa3));
	m_rtc->busy_handler().set("via0", FUNC(via6522_device::write_ca1)); // source of periodic falling edge interrupt?
}


ROM_START(a1010)
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD("d15_31.bin",  0x0000, 0x8000, CRC(5ee1175d) SHA1(87ff6a3d5c64a53b0ab23d54aa343365c44d0407))

	ROM_REGION(0x8000, "chargen", 0)
	ROM_LOAD("chargen.bin", 0x0000, 0x8000, CRC(07daa70e) SHA1(8066a0ac238b06fbeeb99c3a2a8a9e70a27db7a9))
ROM_END

} // anonymous namespace


COMP(1993, a1010, 0, 0, textelcomp, textelcomp, textelcomp_state, empty_init, "Humantechnik", "Textel Compact A1010-0", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



tg100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:superctr
/*
 Yamaha TG100 AWM Tone Generator
 Skeleton written by superctr

 Roms dumped by vampirefrog
 Service manual scanned by bmos

 CPU: Hitachi HD6435208A00P (H8/520)
  - 20MHz clock
  - Mode bits 0,1 high, 2 low
  - 32kb RAM (1x HM65256 PSRAM)
 Sound generator: Yamaha YMW258-F (GEW8, identical to MultiPCM?)
  - 9.4MHz clock
  - 28 voices polyphony
  - 2MB sample ROM containing 140 12-bit PCM samples
 Effect DSP: Yamaha YM3413
  - clocked by sound generator
  - Effect memory: 64kb (2x HM65256 PSRAM)
 LCD:
  - 1x16 characters

 Other ICs:
  HG62E11R54FS (XK462A00) Gate array (LCD control, glue logic)

*/

#include "emu.h"

#include "cpu/h8500/h8520.h"
#include "sound/multipcm.h"

#include "screen.h"
#include "speaker.h"


namespace {

class tg100_state : public driver_device
{
public:
	tg100_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ymw258(*this, "ymw258")
	{ }

	void tg100(machine_config &config);

private:
	required_device<h8520_device> m_maincpu;
	required_device<multipcm_device> m_ymw258;
	void tg100_map(address_map &map) ATTR_COLD;
	void ymw258_map(address_map &map) ATTR_COLD;
};

/* all memory accesses are decoded by the gate array... */
void tg100_state::tg100_map(address_map &map)
{
	map(0x00000000, 0x0007ffff).ram(); /* gate array stuff */
	map(0x00000000, 0x000000ff).rom().region("prgrom", 0x00000);
	map(0x00080000, 0x0009ffff).rom().region("prgrom", 0x00000);
}

static INPUT_PORTS_START( tg100 )
INPUT_PORTS_END

void tg100_state::ymw258_map(address_map &map)
{
	map(0x000000, 0x1fffff).rom();
}

void tg100_state::tg100(machine_config &config)
{
	/* basic machine hardware */
	HD6435208(config, m_maincpu, XTAL(20'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &tg100_state::tg100_map);

	SPEAKER(config, "speaker", 2).front();

	MULTIPCM(config, m_ymw258, 9400000);
	m_ymw258->set_addrmap(0, &tg100_state::ymw258_map);
	m_ymw258->add_route(0, "speaker", 1.0, 0);
	m_ymw258->add_route(1, "speaker", 1.0, 1);
}

ROM_START( tg100 )
	ROM_REGION(0x20000, "prgrom", 0)
	ROM_LOAD( "xk731c0.ic4", 0x00000, 0x20000, CRC(8fb6139c) SHA1(483103a2ffc63a90a2086c597baa2b2745c3a1c2) )

	ROM_REGION(0x4000, "maincpu", 0)
	ROM_COPY( "prgrom", 0x0000, 0x0000, 0x4000 )

	ROM_REGION(0x200000, "ymw258", 0)
	ROM_LOAD( "xk992a0.ic6", 0x000000, 0x200000, CRC(01dc6954) SHA1(32ec77a46f4d005538c735f56ad48fa7243c63be) )
ROM_END

} // anonymous namespace


//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY   FULLNAME      FLAGS
CONS( 1991, tg100, 0,      0,      tg100,   tg100, tg100_state, empty_init, "Yamaha", "TG100",      MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



thaler.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/******************************************************************************

    Thaler CT-65 / MPS-65

    TODO:
    - CT-65 cassette interface (add-on board for MPS-65)

******************************************************************************/

#include "emu.h"
#include "cpu/m6502/m6502.h"
#include "machine/6522via.h"

#include "mps65.lh"


namespace {

class mps65_state : public driver_device
{
public:
	mps65_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_keypad(*this, "COL%u", 1U)
		, m_digit(*this, "digit%u", 1U)
		, m_keycol(0)
	{ }

	void mps65(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(reset_changed);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_ioport_array<6> m_keypad;
	output_finder<6> m_digit;

	void mps65_map(address_map &map) ATTR_COLD;

	uint8_t pa_r();
	void pa_w(uint8_t data);
	void pb_w(uint8_t data);

	uint8_t m_keycol;
};


void mps65_state::machine_start()
{
	m_digit.resolve();

	save_item(NAME(m_keycol));
}


uint8_t mps65_state::pa_r()
{
	uint8_t data = 0xff;

	if (m_keycol > 0 && m_keycol < 7)
		data = m_keypad[m_keycol - 1]->read() & 0x0f;

	return data;
}

void mps65_state::pa_w(uint8_t data)
{
	m_keycol = (data >> 4) & 7;
}

void mps65_state::pb_w(uint8_t data)
{
	if (m_keycol > 0 && m_keycol < 7)
	{
		m_digit[m_keycol - 1] = bitswap<8>(data, 7, 3, 5, 0, 1, 2, 4, 6) & 0x7f;
	}
}


void mps65_state::mps65_map(address_map &map)
{
	map(0x0000, 0x07ff).ram();
	map(0xa000, 0xa00f).mirror(0xf0).m("via", FUNC(via6522_device::map));
	map(0xf000, 0xffff).rom().region("rom", 0);
}


static INPUT_PORTS_START( mps65 )
	PORT_START("COL1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')

	PORT_START("COL2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1')

	PORT_START("COL3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2')

	PORT_START("COL4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3')

	PORT_START("COL5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_NAME("Go")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_NAME("Save")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_NAME("Load")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_NAME("Address")

	PORT_START("COL6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_NAME("Index")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_NAME("Backstep")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("Enter")

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_NAME("Reset") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(mps65_state::reset_changed), 0)
INPUT_PORTS_END


INPUT_CHANGED_MEMBER(mps65_state::reset_changed)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? CLEAR_LINE : ASSERT_LINE);

	if (newval)
	{
		machine().schedule_soft_reset();
	}
}


void mps65_state::mps65(machine_config &config)
{
	M6502(config, m_maincpu, 1_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mps65_state::mps65_map);

	config.set_default_layout(layout_mps65);

	via6522_device &via(MOS6522(config, "via", 1_MHz_XTAL));
	via.readpa_handler().set(FUNC(mps65_state::pa_r));
	via.writepa_handler().set(FUNC(mps65_state::pa_w));
	via.writepb_handler().set(FUNC(mps65_state::pb_w));
	via.irq_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);
}


ROM_START( ct65 )
	ROM_REGION(0x1000, "rom", 0)
	ROM_LOAD("ct6502.bin", 0x0000, 0x1000, CRC(832e0d2a) SHA1(f5f7d00360747a194fca01fb95f31c092505b523))
ROM_END

ROM_START( mps65 )
	ROM_REGION(0x1000, "rom", 0)
	ROM_LOAD("mps65.bin", 0x0800, 0x0800, CRC(512c8cde) SHA1(b6a43ad26d30bcf088b72f3c29e9bcbbf371f4cf))
ROM_END

} // anonymous namespace


//    YEAR  NAME    PARENT  COMPAT  MACHINE   INPUT    CLASS         INIT         COMPANY    FULLNAME   FLAGS
COMP( 1982, ct65,   mps65,  0,      mps65,    mps65,   mps65_state,  empty_init,  "Thaler",  "CT-65",   MACHINE_NO_SOUND_HW )
COMP( 1984, mps65,  0,      0,      mps65,    mps65,   mps65_state,  empty_init,  "Thaler",  "MPS-65",  MACHINE_NO_SOUND_HW )



thinkpad600.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Angelo Salese
/**************************************************************************************************

IBM ThinkPad 6xx and 7xx series.
More info from IBM: https://psref.lenovo.com/syspool/Sys/PDF/withdrawnbook/twbook.pdf

TODO:
- Intel e28f004b5t80 flash ROM;
- RTC (DS17485S-5);
- keyboard (thru H8/3437);

===================================================================================================

The IBM ThinkPad 600 series was a series of notebook computers introduced in 1998 by IBM as an lighter and
slimmer alternative to the 770 series. Three models were produced, the 600, 600E, and 600X.

Hardware for the 600X model.
  Main PCB:
    -Intel Pentium III 650 Mobile MMC-2 (PMM65002101AB).
       There were options for Pentium III at either 450 MHz, 500 MHz, or 650 MHz.
    -Intel PCIset FW82371MB (PCI-TO-ISA / IDE XCELERATOR PIIX4).
    -NeoMagic MagicMedia 256ZX NM2360A-A.
    -Crystal CS4624-CQ (CrystalClear SoundFusion PCI Audio Accelerator).
    -Crystal CS4297A-JQZ (CrystalClear SoundFusion Audio Codec).
    -National Semiconductor PC97338VJG (ACPI 1.0 and PC98/99 Compliant SuperI/O).
    -Hitachi HD64F3437TF.
    -Texas Instruments TI PCI1450GJG (PC Card Controller).
    -Atmel 24RF08CT (SEEPROM).
    -Other chips: IMI-SG577DYB, TI SN75LVDS84, 4 x IBM 0364164PT3B (64 MB of RAM on the motherboard),
                  IBM 20H2987, IBM 10L3953, Motorola MC145583V, Maxim MAX1632EAI, etc.
   Enhanced video PCB:
    -Chrontel CH7004C-T (Digital PC to TV Encoder with Macrovision).

Hardware for the 600E model.
  Main PCB:
    -Intel Pentium II 366 Mobile MMC-2 (PMG36602002AA).
       There were options for Pentium II at either 300 MHz, 366 MHz, or 400 MHz.
    -Texas Instruments PCIbus SN104698GFN.
    -Intel PCIset FW82371EB (PCI-TO-ISA / IDE XCELERATOR PIIX4).
    -NeoMagic MagicMedia 256AV NM2200C-A.
    -Crystal CS4610C-CQ (CrystalClear SoundFusion PCI Audio Accelerator).
    -Crystal CS4239-KQ (CrystalClear Portable ISA Audio System).
    -National Semiconductor PC97338VJG (ACPI 1.0 and PC98/99 Compliant SuperI/O).
    -Hitachi HD64F3437TF.
    -Atmel 24C01A (SEEPROM).
    -Dallas DS17485S-5 (real-time clock/calendar).
    -Other chips: IMI-SG577DYB, TI SN75LVDS84, 4 x IBM 0364164PT3B (64 MB of RAM on the motherboard),
                  IBM 20H2987, IBM 10L3953, Motorola MC145583V, Maxim MAX1632EAI, etc.
   Modem PCB:
    -Epson 11J9289.
    -Five IS62LV256L-15T (isn't it too much RAM for a modem?).
    -Atmel ATF1500AL.
    -TI TCM320AC36C (Voice-Band Audio Processor [VBAPE]).
    -Large BGA chip silkscreened "IPI I8L7360 F27904A".

Hardware for the 600 model.
  Main PCB:
    -Intel Pentium II 300 Mobile MMC-1 (PMD30005002AA).
       There were options for Pentium MMX at 233 MHz and Pentium II at 233, 266 or 300 MHz.
    -Texas Instruments PCIbus PCI1250AGFN.
    -Intel PCIset FW82371AB.
    -NeoMagic MagicMedia 128XD NM2160B.
    -Crystal CS4237-KQ (CrystalClear Advanced Audio System with 3D Sound).
    -National Semiconductor PC97338VJG (ACPI 1.0 and PC98/99 Compliant SuperI/O).
    -Hitachi HD64F3437TF.
    -Atmel 24C01A (SEEPROM).
    -Dallas DS17485S-5 (real-time clock/calendar).
    -Other chips: IMI-SG571BYB, TI SN75LVDS84, 4 x Mitsubishi M5M4V64S40ATP (64 MB of RAM on the motherboard),
                  IBM 20H2987, IBM 10L3932, Maxim MAX1631EAI, etc.
   Modem PCB (same as 600E model):
    -Epson 11J9289.
    -Five IS62LV256L-15T (isn't it too much RAM for a modem?).
    -Atmel ATF1500AL.
    -TI TCM320AC36C (Voice-Band Audio Processor [VBAPE]).
    -Large BGA chip silkscreened "IPI I8L7360 F27904A".

IBM ThinkPad 760 was a notebook computer introduced in 1995 by IBM as part of the ThinkPad 700-series.
Eleven models were produced: 760C, 760CD, 760L, 760LD, 760E, 760ED, 760EL, 760ELD, 760XL, 760XD, and 760D/L.

Hardware for the 760XD model.
  CPU PCB:
    -Intel Mobile Pentium MMX 166 (TT80503166).
    -Two Samsung KM732V589T-15 (Cache SRAM, 32KX32).
    -One IDT 71V256 (Lower Power 3.3V CMOS Fast SRAM 256K [32K x 8-Bit]).
  RAM PCB:
    -Four on-board Toshiba TC51V18165CFTS-60 (1M X 16 EDO DRAM).
    -Two DIMM slots.
  Main PCB:
    -Intel PCIset SB82437MX.
    -Intel PCIset SB82371FB.
    -Two Intel PCIset FA82438MX.
    -W48C60-402G.
    -Four M5M4V18165CTTP (1M x 16 EDO DRAM).
    -Dallas DS17485S-5 (real-time clock/calendar).
    -NEC 53G9037.
    -NEC 53G9038.
    -IBM 89G7219.
    -IBM 20H2888.
    -IBM 94G0207.
    -HD6433436A18F (mask ROM, IBM-branded).
    -Maxim MAX3243CAI.
    -C46CM6 (SEEPROM, for BIOS settings).
    -Texas Instruments PCIbus PCI1130PDV.
    -Texas Instruments 90G9510.
    -Intel Flash E28F004 (BIOS).
  Video PCB:
    -NEC 53G9037.
    -IBM 20H2929.
    -IBM 03H9515.
    -Trident Cyber9385T.
    -Two National Semiconductor DS90CF561MTD (LVDS 18-Bit Color Flat Panel Display [FPD] Link).
    -Six KM416V256DT-L6 (256K x 16Bit CMOS Dynamic RAM with Fast Page Mode).
    -AD722JR (RGB to NTSC/PAL Encoder).
    -Philips SAA7110A WP.
  Sound PCB:
    -HM62W4032HFP25.
    -Crystal CS4218-KQ.
    -Texas Instruments TCM320AC36C (Voice-Band Audio Processor).
  Two separate small PCBs with two IR transceivers.


IBM ThinkPad 770 was a laptop designed and manufactured by IBM targeted for the business, enterprise and professional user.
It was the last lineup in the ThinkPad 700-series, succeeding the 760 as the high-end laptop of the ThinkPad lineup.
The line was produced from October 1997 to May 2000, and eventually replaced by the ThinkPad models A20m and A20p.

Hardware for the 770Z model.

  Main PCB:
    -Intel Mobile Pentium II 366 MMC-2 (PMG36602002AA).
    -Texas Instruments PCIbus SN104698GFN.
    -Intel PCIset FW82371EB (PCI-TO-ISA / IDE XCELERATOR PIIX4)
    -Crystal CS4610C-CQ (CrystalClear SoundFusion PCI Audio Accelerator).
    -Crystal CS4239-KQ (CrystalClear Portable ISA Audio System).
    -National Semiconductor PC97338VJG (ACPI 1.0 and PC98/99 Compliant SuperI/O).
    -Hitachi HD64F3437TF (near a 33.868 MHz xtal).
    -Atmel 24C01A (SEEPROM).
    -Dallas DS17485S-5 (real-time clock/calendar).
    -Other chips: NEC-J 1K3153 919LW, National SemiconductorLMC6034IM, IBM 20H2987
                  IBM 10L3953, IMI SSC660EYB, IMI SG577DYB
   Modem PCB:
    -IBM 3780i Mwave DSP.
    -Epson 11J9289.
    -Five AS7C3256-15TC (RAM).
    -Atmel ATF1500AL.
    -TI TCM320AC36C (Voice-Band Audio Processor [VBAPE]).
   Video PCB:
    -Trident Cyber9397DVD.
    -Two KM4132G512TQ-8.
   DVD and enhaced video PCB.
    -IBM MPEGCD1MPFC21C (near a 27 MHz xtal).
    -Analog Devices ADV7175AKS (integrated digital video encoder).
    -M5M4V16S40CTP.
    -CS8404A-CS.
    -Philips SAA7111A (video input processor).


**************************************************************************************************/

#include "emu.h"

#include "bus/isa/isa_cards.h"
#include "bus/rs232/hlemouse.h"
#include "bus/rs232/null_modem.h"
#include "bus/rs232/rs232.h"
#include "bus/rs232/sun_kbd.h"
#include "bus/rs232/terminal.h"
#include "cpu/h8/h83337.h"
#include "cpu/i386/i386.h"
#include "machine/ds17x85.h"
#include "machine/pci.h"
#include "machine/pci-ide.h"
#include "machine/i82443bx_host.h"
#include "machine/i82371eb_isa.h"
#include "machine/i82371eb_ide.h"
#include "machine/i82371eb_acpi.h"
#include "machine/i82371eb_usb.h"
#include "machine/pc97338.h"

#include "speaker.h"


namespace {

class thinkpad600_state : public driver_device
{
public:
	thinkpad600_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void thinkpad600e(machine_config &config);
	void thinkpad600(machine_config &config);

protected:
	void thinkpad600_base(machine_config &config);

private:
	required_device<pentium2_device> m_maincpu;

	void main_map(address_map &map);
	void mcu_map(address_map &map);

	static void superio_config(device_t *device);
};

void thinkpad600_state::main_map(address_map &map)
{
	map.unmap_value_high();
}

void thinkpad600_state::mcu_map(address_map &map)
{
	map(0x0000, 0xf77f).rom().region("mcu", 0);
}

static INPUT_PORTS_START(thinkpad600)
INPUT_PORTS_END

void thinkpad600_state::superio_config(device_t *device)
{
	pc97338_device &fdc = *downcast<pc97338_device *>(device);
//  fdc.set_sysopt_pin(1);
//  fdc.gp20_reset().set_inputline(":maincpu", INPUT_LINE_RESET);
//  fdc.gp25_gatea20().set_inputline(":maincpu", INPUT_LINE_A20);
	fdc.irq1().set(":pci:07.0", FUNC(i82371eb_isa_device::pc_irq1_w));
	fdc.irq8().set(":pci:07.0", FUNC(i82371eb_isa_device::pc_irq8n_w));
	fdc.txd1().set(":serport0", FUNC(rs232_port_device::write_txd));
	fdc.ndtr1().set(":serport0", FUNC(rs232_port_device::write_dtr));
	fdc.nrts1().set(":serport0", FUNC(rs232_port_device::write_rts));
	fdc.txd2().set(":serport1", FUNC(rs232_port_device::write_txd));
	fdc.ndtr2().set(":serport1", FUNC(rs232_port_device::write_dtr));
	fdc.nrts2().set(":serport1", FUNC(rs232_port_device::write_rts));
}

static void isa_com(device_slot_interface &device)
{
	device.option_add("microsoft_mouse", MSFT_HLE_SERIAL_MOUSE);
	device.option_add("logitech_mouse", LOGITECH_HLE_SERIAL_MOUSE);
	device.option_add("wheel_mouse", WHEEL_HLE_SERIAL_MOUSE);
	device.option_add("msystems_mouse", MSYSTEMS_HLE_SERIAL_MOUSE);
	device.option_add("rotatable_mouse", ROTATABLE_HLE_SERIAL_MOUSE);
	device.option_add("terminal", SERIAL_TERMINAL);
	device.option_add("null_modem", NULL_MODEM);
	device.option_add("sun_kbd", SUN_KBD_ADAPTOR);
}

static void isa_internal_devices(device_slot_interface &device)
{
	device.option_add("pc97338", PC97338);
}

void thinkpad600_state::thinkpad600_base(machine_config &config)
{
	// TODO: move away, maps on MB resource, bump to H83437
	h83337_device &mcu(H83337(config, "mcu", 10_MHz_XTAL)); // Actually an Hitachi HD64F3437TF
	mcu.set_addrmap(AS_PROGRAM, &thinkpad600_state::mcu_map);
//  mcu.set_disable();

	DS17485(config, "rtc", 16'000'000); // Dallas DS17485S-5, unknown clock
}

void thinkpad600_state::thinkpad600e(machine_config &config)
{
	PENTIUM2(config, m_maincpu, 366'000'000); // Intel Pentium II 366 Mobile MMC-2 (PMG36602002AA)
	m_maincpu->set_addrmap(AS_PROGRAM, &thinkpad600_state::main_map);
	m_maincpu->set_irq_acknowledge_callback("pci:07.0:pic8259_master", FUNC(pic8259_device::inta_cb));
	m_maincpu->smiact().set("pci:00.0", FUNC(i82443bx_host_device::smi_act_w));

	// TODO: PCI config space guessed from a Fujitsu Lifebook, confirm me for ThinkPad
	PCI_ROOT(config, "pci", 0);
	I82443BX_HOST(config, "pci:00.0", 0, "maincpu", 64*1024*1024);
	i82371eb_isa_device &isa(I82371EB_ISA(config, "pci:07.0", 0, m_maincpu));
	isa.boot_state_hook().set([](u8 data) { /* printf("%02x\n", data); */ });
	isa.smi().set_inputline("maincpu", INPUT_LINE_SMI);

	i82371eb_ide_device &ide(I82371EB_IDE(config, "pci:07.1", 0, m_maincpu));
	ide.irq_pri().set("pci:07.0", FUNC(i82371eb_isa_device::pc_irq14_w));
	ide.irq_sec().set("pci:07.0", FUNC(i82371eb_isa_device::pc_mirq0_w));

	I82371EB_USB (config, "pci:07.2", 0);
	I82371EB_ACPI(config, "pci:07.3", 0);
	LPC_ACPI     (config, "pci:07.3:acpi", 0);
	SMBUS        (config, "pci:07.3:smbus", 0);

//  TODO: modem at "pci:10.0"
//  TODO: cardbus at "pci:12.0"
//  TODO: NeoMagic at "pci:14.0" / "pci:14.1" (video & AC'97 integrated sound, likely requires BIOS)

//  TODO: motherboard Super I/O resource here
	ISA16_SLOT(config, "board4", 0, "pci:07.0:isabus", isa_internal_devices, "pc97338", true).set_option_machine_config("pc97338", superio_config);

	rs232_port_device &serport0(RS232_PORT(config, "serport0", isa_com, nullptr));
	serport0.rxd_handler().set("board4:pc97338", FUNC(pc97338_device::rxd1_w));
	serport0.dcd_handler().set("board4:pc97338", FUNC(pc97338_device::ndcd1_w));
	serport0.dsr_handler().set("board4:pc97338", FUNC(pc97338_device::ndsr1_w));
	serport0.ri_handler().set("board4:pc97338", FUNC(pc97338_device::nri1_w));
	serport0.cts_handler().set("board4:pc97338", FUNC(pc97338_device::ncts1_w));

	rs232_port_device &serport1(RS232_PORT(config, "serport1", isa_com, nullptr));
	serport1.rxd_handler().set("board4:pc97338", FUNC(pc97338_device::rxd2_w));
	serport1.dcd_handler().set("board4:pc97338", FUNC(pc97338_device::ndcd2_w));
	serport1.dsr_handler().set("board4:pc97338", FUNC(pc97338_device::ndsr2_w));
	serport1.ri_handler().set("board4:pc97338", FUNC(pc97338_device::nri2_w));
	serport1.cts_handler().set("board4:pc97338", FUNC(pc97338_device::ncts2_w));

	thinkpad600_base(config);
}

void thinkpad600_state::thinkpad600(machine_config &config)
{
	PENTIUM2(config, m_maincpu, 300'000'000); // Intel Pentium II 300 Mobile MMC-1 (PMD30005002AA) on the 600 model
	m_maincpu->set_disable();

	// TODO: fill me, uses earlier PIIX4 AB
	PCI_ROOT(config, "pci", 0);

	thinkpad600_base(config);
}


ROM_START(thinkpad760xd)
	ROM_REGION( 0x80000, "pci:07.0", 0 )
	ROM_LOAD( "e28f004_89g8164_rev37_h1897m.u17",      0x00000, 0x80000, CRC(6092594f) SHA1(25681e4952a432e1170f69ae75f3260245b6b44b) ) // BIOS

	ROM_REGION( 0x0f780, "mcu", 0)
	ROM_LOAD( "ibm_hd6433436a18f_40h8792.u39",         0x00000, 0x0f780, NO_DUMP ) // Mask ROM, undumped

	ROM_REGION( 0x00080, "seeprom", 0)
	ROM_LOAD( "st93c46c.u30",                          0x00000, 0x00080, CRC(22cac7b5) SHA1(ee48ecf5d59e243e9afb0ca7e41ed8437eec8097) ) // BIOS settings
ROM_END

ROM_START(thinkpad600)
	ROM_REGION( 0x80000, "pci:07.0", 0 )
	ROM_LOAD( "tms28f004b_18l9949_rev16-i2298m.u76",   0x00000, 0x80000, CRC(00a52b32) SHA1(08db425b8edb3a036f22beb588caa6f050fc8eb2) )

	ROM_REGION(0x0f780, "mcu", 0)
	ROM_LOAD( "hd64f3437tf_10l9950_rev08_i2798m.u32",  0x00000, 0x0f780, CRC(546ec51c) SHA1(5d9b4be590307c4059ff11c434d0901819427649) )

	ROM_REGION(0x00080, "seeprom", 0)
	ROM_LOAD( "atmel_24c01a.u49",                      0x00000, 0x00080, CRC(9a2e2a18) SHA1(29e2832c97bc93debb4fb09fcbed582335b57efe) ) // BIOS settings

	ROM_REGION(0x00c39, "plds", 0)
	ROM_LOAD( "atf1500al-modemboard.u12",              0x00000, 0x00c39, CRC(7ecd4b79) SHA1(b69ef5fe227b466f331f863ba20efd7e23056809) ) // On modem PCB
ROM_END

ROM_START(thinkpad600e)
	ROM_REGION( 0x80000, "bios", 0 )
	ROM_LOAD( "e28f004b5t80-10l1056_rev15_h0399m.u60", 0x00000, 0x80000, CRC(fba7567b) SHA1(a84e7d4e5740150e78e5002714c9125705f3356a) )

	ROM_REGION( 0x80000, "pci:07.0", ROMREGION_ERASEFF )
	// TODO: in linear mapping it will boot to a terminal only Flash ROM BIOS programmer
	// 0x40000-0x5ffff contains standard x86 startup, this hookup needs confirmation later on.
	ROM_COPY( "bios", 0x40000, 0x60000, 0x20000 )
	ROM_COPY( "bios", 0x60000, 0x40000, 0x20000 )
	ROM_COPY( "bios", 0x00000, 0x20000, 0x20000 )
	ROM_COPY( "bios", 0x20000, 0x00000, 0x20000 )

	ROM_REGION(0x0f780, "mcu", 0)
	ROM_LOAD( "hd64f3437tf-10l1057_rev04_h0499m.u39",  0x00000, 0x0f780, CRC(c21c928b) SHA1(33e3e6966f003655ffc2f3ac07772d2d3245740d) )

	ROM_REGION(0x00080, "seeprom", 0)
	ROM_LOAD( "atmel_24c01a.u98",                      0x00000, 0x00080, CRC(7ce51001) SHA1(6f25666373a6373ce0014c04df73a066f4da938b) ) // BIOS settings

	ROM_REGION(0x00420, "seeprom2", 0)
	ROM_LOAD( "at24rf08bt.u99",                        0x00000, 0x00420, CRC(c7ce9600) SHA1(4e6ed66250fed838614c3f1f6044fd9a19a2d0de) )

	ROM_REGION(0x00c39, "plds", 0)
	ROM_LOAD( "atf1500al-modemboard.u12",              0x00000, 0x00c39, CRC(7ecd4b79) SHA1(b69ef5fe227b466f331f863ba20efd7e23056809) ) // On modem PCB
ROM_END

ROM_START(thinkpad600x)
	ROM_REGION( 0x80000, "pci:07.0", 0 )
	ROM_LOAD( "e28f004b5t80_08k3492_rev25_b0800m.u36", 0x00000, 0x80000, CRC(5c64ef91) SHA1(1aa2d68aff96c1ccc6859c5480fcfc5e73ab250d) )

	ROM_REGION(0x0f780, "mcu", 0)
	ROM_LOAD( "hd64f3437tf_08k3493_rev05_b1100m.u75",  0x00000, 0xc000,  CRC(99d21cad) SHA1(92a2809e5c7ca3a63489f9cd1c9a7dc57c6a1343) )

	ROM_REGION(0x00080, "seeprom", 0)
	ROM_LOAD( "atmel24rf08ct.u79",                     0x00000, 0x00080, NO_DUMP ) // BIOS settings
ROM_END

ROM_START(thinkpad770z)
	ROM_REGION( 0x80000, "pci:07.0", 0 )
	ROM_LOAD( "e28f004b5t80-10l1055-rev09-d0999m.u59", 0x00000, 0x80000, CRC(f9f255c5) SHA1(ee209802d08c6498a42e52c5c45ce469dc095ad4) )

	ROM_REGION(0x0f780, "mcu", 0)
	ROM_LOAD( "hd64f3437tf-10l1049-rev05-e2699m.u10",  0x00000, 0x0f780, CRC(c1bad151) SHA1(73d0d1b15c083a18aff6b80188443b0dc1d976c3) )

	ROM_REGION(0x00080, "seeprom", 0)
	ROM_LOAD( "atmel24c01a.u94",                       0x00000, 0x00080, CRC(7ce51001) SHA1(6f25666373a6373ce0014c04df73a066f4da938b) ) // BIOS settings

	ROM_REGION(0x00420, "seeprom2", 0)
	ROM_LOAD( "una-at24rf08bt.bin",                    0x00000, 0x00420, CRC(9ccddd43) SHA1(262af18b5649b01f70ef1587a14405f8f6cd8fe2) )

	ROM_REGION(0x00c39, "plds", 0)
	ROM_LOAD( "atf1500al-modemboard.u12",              0x00000, 0x00c39, CRC(7ecd4b79) SHA1(b69ef5fe227b466f331f863ba20efd7e23056809) ) // On modem PCB
ROM_END

} // anonymous namespace

//    YEAR, NAME,          PARENT, COMPAT, MACHINE,      INPUT,       CLASS,             INIT,       COMPANY, FULLNAME,         FLAGS
COMP( 1995, thinkpad760xd, 0,      0,      thinkpad600,  thinkpad600, thinkpad600_state, empty_init, "IBM",   "ThinkPad 760XD", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1998, thinkpad600,   0,      0,      thinkpad600,  thinkpad600, thinkpad600_state, empty_init, "IBM",   "ThinkPad 600",   MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1999, thinkpad600e,  0,      0,      thinkpad600e, thinkpad600, thinkpad600_state, empty_init, "IBM",   "ThinkPad 600E",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1999, thinkpad600x,  0,      0,      thinkpad600,  thinkpad600, thinkpad600_state, empty_init, "IBM",   "ThinkPad 600X",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1999, thinkpad770z,  0,      0,      thinkpad600,  thinkpad600, thinkpad600_state, empty_init, "IBM",   "ThinkPad 770Z",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



thinkpad8xx.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************
Skeleton driver for IBM ThinkPad Power Series.
The IBM ThinkPad Power Series (800/820/821/822/823/850/851/860) is a laptop series from the ThinkPad line
that was manufactured by IBM. It is based on the PowerPC architecture.
All of the PowerPC ThinkPads could run Windows NT 3.51 and 4.0, AIX 4.1.x, and Solaris Desktop 2.5.1
PowerPC Edition. It is also possible to run certain PowerPC versions of Linux on the 800 Series.
830 and 850 models can also run OS/2 Warp PowerPC Edition.

This has no chance of running until MAME's PowerPC CPU core supports little Endian mode and motherboards
wired for little Endian operating systems.

Hardware for the 850 model:
-SCSI hard disk and SCSI CD-ROM drive (with a NCR 53C810 PCI-SCSI I/O Processor).
-Two PC Card expansion slots (with a Ricoh RF53C366L PC Card interface controller and a
 MAX780 Dual-Slot PCMCIA Analog Power Controller).
-Video:
   -IBM 85G7815 (by Seiko/Epson).
   -Western Digital WD90C24A SVGA LCD controller.
   -Two Hitachi HM51S4260 262144 x 16bit DRAM (1MB of video display memory).
   -10.4" 640×480 or 800×600 screen.
-Video capture:
   -Brooktree Bt812 NTSC/PAL to RGB/YCrCb Decoder.
   -Two Hitachi HM530281 high speed 331776 x 8bit Frame buffer DRAM.
   -ASCII V7310AS (アスキー, Asukii) Video Capture Device.
-Crystal CS4231 16bit stereo codec for audio.
-Two DRAM DIMMs slots.
-Hitachi H8/338 (HD6473388) for main board supervision.
-CPU:
    -IBM PowerPC 603e @ 100MHz (PPCI603eFC100BPQ).
    -Two 32k x 36bits IBM043614 burst SRAM (256k L2 cache total).
    -IDT71216 240K (16K x 15bit) cache-tag RAM.
    -33.333 MHz xtal (tripled for 100MHz system clock).
-Dallas DS1585S RTC.
-Intel S82378ZB PCIset.
-National Semiconductor DP87322VF (SuperI/O III, Floppy Disk Controller with Dual UARTs,
 Enhanced Parallel Port, and IDE Interface).
-Motorola XPC105ARX66CD (PowerPC PCI Bridge/Memory Controller).
-Other ICs: S-MOS 85G7814, S-MOS 85G2680

More info: http://oldcomputer.info/portables/tp850/ibm_ppc_thinkpad_redbook.pdf

***********************************************************************************************************/

#include "emu.h"
#include "cpu/h8/h8325.h"
#include "cpu/powerpc/ppc.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class thinkpad8xx_state : public driver_device
{
public:
	thinkpad8xx_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }


	void thinkpad850(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
};


static INPUT_PORTS_START(thinkpad8xx)
INPUT_PORTS_END

void thinkpad8xx_state::thinkpad850(machine_config &config)
{
	PPC603(config, m_maincpu, 33.333_MHz_XTAL * 3); // IBM PPCI603eFC100BPQ

	// All BIOS ROM chip lines are routed through the S-MOS 85G7814

	H8325(config, "mcu", XTAL(10'000'000)); // Actually an H8/338 (HD6473388: 48k-byte ROM; 2k-byte RAM), unknown clock

	SPEAKER(config, "speaker", 2).front();

	SOFTWARE_LIST(config, "thinkpad8xx").set_original("thinkpad8xx");
}


ROM_START(thinkpad850)
	ROM_DEFAULT_BIOS("v101")
	ROM_SYSTEM_BIOS( 0, "v100", "v1.00 (91G0610, 07-03-1995)" )
	ROM_SYSTEM_BIOS( 1, "v101", "v1.01 (91G1671, 09-10-1996)" )

	ROM_REGION( 0x80000, "maincpu", 0 )
	ROMX_LOAD( "91g0610_ibm_dakota_v100_mbm29f040a.u21", 0x00000, 0x80000, CRC(169a79c4) SHA1(da74a2f346b732add62d08ca5f34f192cae5d033), ROM_BIOS(0) )
	ROMX_LOAD( "91g1671_ibm_dakota_v101_mbm29f040a.u21", 0x00000, 0x80000, CRC(5210dbd6) SHA1(8e0bbbe130e6fdb06ef307bb5addbcb993a8a41f), ROM_BIOS(1) ) // Needed for installing Windows NT

	ROM_REGION(0xe000, "mcu", 0)
	ROM_LOAD( "hd6473388.u15", 0x0000, 0xe000, NO_DUMP )
ROM_END

} // anonymous namespace

//    YEAR, NAME,        PARENT, COMPAT, MACHINE,     INPUT,       CLASS,             INIT,       COMPANY, FULLNAME,       FLAGS
COMP( 1996, thinkpad850, 0,      0,      thinkpad850, thinkpad8xx, thinkpad8xx_state, empty_init, "IBM",   "ThinkPad 850", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



thomson.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Antoine Mine
/**********************************************************************

  Copyright (C) Antoine Mine' 2006

  Thomson 8-bit micro-computers.

  A 6809E-based French family of personal computers developed in the 80's.
  Despite their rather high price and poor design , they were quite popular
  in France, probably due to the government plan "Informatique pour tous"
  (Computer Science for Everyone) to put a network of MO5 computers in every
  school during 1984-1986. And maybe their very popular light-pen.

  Drivers
  - t9000  Thomson T9000           (early TO7 prototype, oct 1980)
  - to7    Thomson TO7             (nov 1982)
  - to770  Thomson TO7/70          (scarcely enhanced TO7, 1984)
  - mo5    Thomson MO5             (entry-game, TO7-incompatible, jun 1984)
  - to9    Thomson TO9             (high-end TO7/70 successor, sep 1985)
  - mo5e   Thomson MO5E            (export MO5 version, 1986)
  - to8    Thomson TO8             (next generation of TO7/70, sep 1986)
  - to9p   Thomson TO9+            (improved TO9 with TO8 technology, sep 1986)
  - mo6    Thomson MO6             (improved MO5 with TO8 technology, sep 1986)
  - mo5nr  Thomson MO5NR           (network-enhanced MO6, 1986)
  - to8d   Thomson TO8D            (TO8 with integrated floppy, dec 1987)
  - pro128 Olivetti Prodest PC 128 (Italian MO6, built by Thomson, 1986)

  We do not consider here the few 16-bit computers built by Thomson
  (68000-based Micromega 32, 8088-based Micromega 16 or the TO16: Thomson's own
   8088-based PC).

  You may distinguish three families:
  * TO7,TO7/70,TO8,TO8D (family computers)
  * TO9,TO9+ (professional desktop look with separate keyboard and monitor)
  * MO5,MO5E,MO5NR,MO6 (cheaper, less extensible family)
  Computers in both TO families are compatible. Computers in the MO family are
  compatible. However, the TO and MO families are incompatible
  (different memory-mapping).

  Note that the TO8, TO9+ and MO6 were produced at the same time, using very
  similar technologies, but with different cost/feature trade-offs and
  different compatibility (MO6 is MO5-compatible, TO9+ is TO9-compatible and
  TO8 and TO9+ are TO7-compatible).
  Also note that the MO5NR is actually based on MO6 design, not MO5
  (although both MO5NR and MO6 are MO5-compatible)

  Also of interest are the Platini and Hinault versions of the MO5
  (plain MO5 inside, but with custom, signed box-case).
  There were several versions of TO7/70 and MO5 with alternate keyboards.

  Thomson stopped producing micro-computers in jan 1990.

**********************************************************************/

/* TODO (roughly in decreasing priority order):
   ----

   - many copy-protected cassettes that work in DCMOTO fail to load correctly
     (mostly by Infogrames and Loriciels)
   - floppy issues still remain (such as many recent TO8 slideshows giving I/O errors)
   - MO6/MO5NR cartridge is completely broken (garbage screen/hangs)
   - TO8/TO9+ cassette is completely broken (I/O error)
   - add several clones that are emulated in DCMOTO
   - internal, keyboard-attached TO9 mouse port (untested)
   - floppy: 2-sided or 4-sided .fd images, modernization
   - printer post-processing => postscript
   - RS232 serial port extensions: CC 90-232, RF 57-932
   - modem, teltel extension: MD 90-120 / MD 90-333 (need controller ROM?)
   - IEEE extension
   - TV overlay (IN 57-001) (@)
   - digitisation extension (DI 90-011) (@)
   - barcode reader (@)

   (@) means MAME is lacking support for this kind of device / feature anyway

*/

#include "emu.h"
#include "thomson.h"

#include "bus/centronics/ctronics.h"
#include "bus/thomson/cc90_232.h"
#include "bus/thomson/cd90_015.h"
#include "bus/thomson/cq90_028.h"
#include "bus/thomson/cd90_351.h"
#include "bus/thomson/cd90_640.h"
#include "bus/thomson/md90_120.h"
#include "bus/thomson/midipak.h"
#include "bus/thomson/nanoreseau.h"
#include "bus/thomson/rf57_932.h"
#include "bus/thomson/speech.h"
#include "machine/clock.h"
#include "machine/ram.h"
#include "machine/thmfc1.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/sap_dsk.h"
#include "formats/thom_cas.h"
#include "formats/thom_dsk.h"

#include "utf8.h"


/**************************** common *******************************/

#define KEY(pos,name,key)                   \
	PORT_BIT  ( 1<<(pos), IP_ACTIVE_LOW, IPT_KEYBOARD ) \
	PORT_NAME ( name )                  \
	PORT_CODE ( KEYCODE_##key )

#define PAD(mask,player,name,port,dir,key)              \
	PORT_BIT    ( mask, IP_ACTIVE_LOW, IPT_##port )         \
	PORT_NAME   ( "P" #player " " name )                \
	PORT_CODE( KEYCODE_##key )                  \
	PORT_PLAYER ( player )


/* ------------- game port ------------- */

/*
  Two generations of game port extensions were developed

  - CM 90-112 (model 1)
    connect up to two 8-position 1-button game pads

  - SX 90-018 (model 2)
    connect either two 8-position 2-button game pads
    or a 2-button mouse (not both at the same time!)

  We emulate the SX 90-018 as it is fully compatible with the CM 90-112.

  Notes:
  * all extensions are compatible with all Thomson computers.
  * the SX 90-018 extension is integrated within the TO8(D)
  * the TO9 has its own, different mouse port
  * all extensions are based on a Motorola 6821 PIA
  * all extensions include a 6-bit sound DAC
  * most pre-TO8 software (including TO9) do not recognise the mouse nor the
  second button of each pad
  * the mouse cannot be used at the same time as the pads: they use the same
  6821 input ports & physical port; we use a config switch to tell MESS
  whether pads or a mouse is connected
  * the mouse should not be used at the same time as the sound DAC: they use
  the same 6821 ports, either as input or output; starting from the TO8,
  there is a 'mute' signal to cut the DAC output and avoid producing an
  audible buzz whenever the mouse is moved; unfortunately, mute is not
  available on the TO7(/70), TO9 and MO5.
*/

static INPUT_PORTS_START( thom_game_port )

/* joysticks, common to CM 90-112 & SX 90-018 */
	PORT_START ( "game_port_directions" )
		PAD ( 0x01, 1, UTF8_UP, JOYSTICK_UP,    UP,    UP)
		PAD ( 0x02, 1, UTF8_DOWN, JOYSTICK_DOWN,  DOWN,  DOWN )
		PAD ( 0x04, 1, UTF8_LEFT, JOYSTICK_LEFT,  LEFT,  LEFT )
		PAD ( 0x08, 1, UTF8_RIGHT, JOYSTICK_RIGHT, RIGHT, RIGHT )
		PAD ( 0x10, 2, UTF8_UP, JOYSTICK_UP,    UP,    8_PAD )
		PAD ( 0x20, 2, UTF8_DOWN, JOYSTICK_DOWN,  DOWN,  2_PAD )
		PAD ( 0x40, 2, UTF8_LEFT, JOYSTICK_LEFT,  LEFT,  4_PAD )
		PAD ( 0x80, 2, UTF8_RIGHT, JOYSTICK_RIGHT, RIGHT, 6_PAD )

	PORT_START ( "game_port_buttons" )
		PAD ( 0x40, 1, "Action A", BUTTON1, BUTTON1, LCONTROL )
		PAD ( 0x80, 2, "Action A", BUTTON1, BUTTON1, RCONTROL )

/* joysticks, SX 90-018 specific */
		PAD ( 0x04, 1, "Action B", BUTTON2, BUTTON2, LALT )
		PAD ( 0x08, 2, "Action B", BUTTON2, BUTTON2, RALT )
	PORT_BIT  ( 0x30, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT  ( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED ) /* ? */

/* mouse, SX 90-018 specific */
	PORT_START ( "mouse_x" )
	PORT_BIT ( 0xffff, 0x00, IPT_MOUSE_X )
	PORT_NAME ( "Mouse X" )
	PORT_SENSITIVITY ( 150 )
	PORT_PLAYER (1)

	PORT_START ( "mouse_y" )
	PORT_BIT ( 0xffff, 0x00, IPT_MOUSE_Y )
	PORT_NAME ( "Mouse Y" )
	PORT_SENSITIVITY ( 150 )
	PORT_PLAYER (1)

	PORT_START ( "mouse_button" )
	PORT_BIT ( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_NAME ( "Left Mouse Button" )
	PORT_CODE( MOUSECODE_BUTTON1 )
	PORT_BIT ( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_NAME ( "Right Mouse Button" )

INPUT_PORTS_END


/* ------------ lightpen ------------ */

static INPUT_PORTS_START( thom_lightpen )

	PORT_START ( "lightpen_x" )
	PORT_BIT ( 0xffff, THOM_TOTAL_WIDTH/2, IPT_LIGHTGUN_X )
	PORT_NAME ( "Lightpen X" )
	PORT_MINMAX( 0, THOM_TOTAL_WIDTH )
	PORT_SENSITIVITY( 50 )
	PORT_CROSSHAIR(X, 1.0, 0.0, 0)

	PORT_START ( "lightpen_y" )
	PORT_BIT ( 0xffff, THOM_TOTAL_HEIGHT/2, IPT_LIGHTGUN_Y )
	PORT_NAME ( "Lightpen Y" )
	PORT_MINMAX ( 0, THOM_TOTAL_HEIGHT )
	PORT_SENSITIVITY( 50 )
	PORT_CROSSHAIR(Y, 1.0, 0.0, 0)

	PORT_START ( "lightpen_button" )
	PORT_BIT ( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_NAME ( "Lightpen Button" )
	PORT_CODE( MOUSECODE_BUTTON1 )

INPUT_PORTS_END

#define TO7_LIGHTPEN_DECAL 17 /* horizontal lightpen shift, stored in $60D2 */
#define MO5_LIGHTPEN_DECAL 12
#define TO9_LIGHTPEN_DECAL 8
#define TO8_LIGHTPEN_DECAL 16
#define MO6_LIGHTPEN_DECAL 12

/************************** T9000 / TO7 *******************************

TO7 (1982)
---

First computer by Thomson.
Note that the computer comes with only a minimal BIOS and requires an
external cartridge to be usable.
Most software are distributed on cassette and require the BASIC 1.0 cartridge
to be present (-cart basic.m7), as only it provides the necessary OS
capabilities (e.g., a cassette loader).
To use disks, you will need both a BASIC 1.0 cartridge and a BASIC DOS
boot floppy.

* chips:
  - 1 MHz Motorola 6809E CPU
  - 1 Motorola 6821 PIA (+3 for I/O, game, and modem extensions)
  - 1 Motorola 6846 timer, I/O, ROM

* memory:
  - 8 KB base user RAM
    + 16 KB extended user RAM (EM 90-016) = 24 KB total user RAM emulated
    + homebrew 8 KB RAM extension (Theophile magazine 6, sept 1984)
  - 6 KB BIOS ROM
  - 6-bit x 8 K color RAM + 8-bit x 8 K point RAM, bank switched
  - 2 to 8 KB ROM comes with the floppy drive / network controller

* video:
    320x200 pixels with color constraints (2 colors per horizontal
    8-pixel span), 8-color pixel palette,
    50 Hz (tweaked SECAM)

* devices:
  - AZERTY keyboard, 58-keys, French with accents
  - cartridge 16 KB (up to 64 KB using bank-switching),
    the MAME cartridge device is named -cart
  - cassette 900 bauds (frequency signals: 0=4.5 kHz, 1=6.3 kHz)
    the MAME cassette device is named -cass
  - 1-bit internal buzzer
  - lightpen, with 8-pixel horizontal resolution, 1-pixel vertical
  - SX 90-018 game & music extension
    . 6-bit DAC sound
    . two 8-position 2-button game pads
    . 2-button mouse
    . based on a Motorola 6821 PIA
  - CC 90-232 I/O extension:
    . CENTRONICS (printer)
    . RS232 (unemulated)
    . based on a Motorola 6821 PIA
    . NOTE: you cannot use the CENTRONICS and RS232 at the same time
  - RF 57-932: RS232 extension, based on a SY 6551 ACIA (unemulated)
  - MD 90-120: MODEM, TELETEL extension (unemulated)
    . 1 Motorola 6850 ACIA
    . 1 Motorola 6821 PIA
    . 1 EFB 7513 MODEM FSK V13, full duplex
    . PTT-, VELI7Y-, and V23-compatible MODEM (up to 1200 bauds)
    . seems to come with an extra ROM
  - 5"1/2 floppy drive extension
    . CD 90-640 floppy controller, based on a Western Digital 2793
    . DD 90-320 double-density double-sided 5"1/4 floppy
      (2 drives considered as 4 simple-face drives: 0/1 for the first drive,
       2/3 for the second drive, 1 and 3 for upper sides, 0 and 2 for lower
       sides)
    . floppies are 40 tracks/side, 16 sectors/track, 128 or 256 bytes/sector
      = from 80 KB one-sided single-density, to 320 KB two-sided double-density
    . MAME floppy devices are named -flop0 to -flop3
  - alternate 5"1/2 floppy drive extension
    . CD 90-015 floppy controller, based on a HD 46503 S
    . UD 90-070 5"1/4 single-sided single density floppy drive
  - alternate 3"1/2 floppy drive extension
    . CD 90-351 floppy controller, based on a custom Thomson gate-array
    . DD 90-352 3"1/2 floppy drives
  - alternate QDD floppy drive extension
    . CQ 90-028 floppy controller, based on a Motorola 6852 SSDA
    . QD 90-028 quickdrive 2"8 (QDD), only one drive, single side
  - speech synthesis extension: based on a Philips / Signetics MEA 8000
    (cannot be used with the MODEM)
  - MIDIPAK MIDI extension, uses a EF 6850 ACIA
  - NR 07-005: network extension, MC 6854 based, 2 KB ROM & 64 KB RAM
    (build by the French Leanord company)


T9000 (1980)
-----

Early TO7 prototype.
The hardware seems to be the exactly same. Only the BIOS is different.
It has some bug that were corrected later for the TO7.
Actually, the two computers are indistinguishable, except for the different
startup screen, and a couple BIOS addresses.
They can run the same software and accept the same devices and extensions.


**********************************************************************/

/* ------------ address maps ------------ */

void thomson_state::to7_map(address_map &map)
{

	map(0x0000, 0x3fff).bankr(THOM_CART_BANK).w(FUNC(thomson_state::to7_cartridge_w)); /* 4 * 16 KB */
	map(0x4000, 0x5fff).bankr(THOM_VRAM_BANK).w(FUNC(thomson_state::to7_vram_w));
	map(0x6000, 0x7fff).bankrw(THOM_BASE_BANK); /* 1 * 8 KB */
	map(0x8000, 0xdfff).bankrw(THOM_RAM_BANK);  /* 16 or 24 KB (for extension) */
	map(0xe7c0, 0xe7c7).rw(m_mc6846, FUNC(mc6846_device::read), FUNC(mc6846_device::write));
	map(0xe7c8, 0xe7cb).rw("pia_0", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
	map(0xe7cc, 0xe7cf).rw("pia_1", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
	map(0xe800, 0xefff).rom().region("mc6846", 0);
	map(0xf000, 0xffff).rom().region("monitor", 0);

/* 0x10000 - 0x1ffff: 64 KB external ROM cartridge */
/* 18 KB floppy / network ROM controllers */

/* RAM mapping:
   0x0000 - 0x3fff: 16 KB video RAM (actually 8 K x 8 bits + 8 K x 6 bits)
   0x4000 - 0x5fff:  8 KB base RAM
   0x6000 - 0x9fff: 16 KB extended RAM
   0xa000 - 0xbfff:  8 KB more extended RAM
 */
}



/* ------------ ROMS ------------ */

ROM_START ( to7 )
	ROM_REGION ( 0x1000, "monitor", 0 )
	ROM_LOAD ( "to7.u3", 0x0000, 0x1000, CRC(99f73da8) SHA1(416981860b44934b2ebf0192080b2cdd79c2c8d5) )

	ROM_REGION ( 0x800, "mc6846", 0 )
	ROM_LOAD ( "tha010_ef6846p.u1", 0x000, 0x800, CRC(39d74cec) SHA1(6428fe9439a1f09c2864697d40da9c0b72a52ca1) )

	ROM_REGION ( 0x20, "proms", 0 )
	ROM_LOAD ( "6331-1.u11", 0x00, 0x20, NO_DUMP ) // address decode

	ROM_REGION ( 0x10000, "cartridge", 0 )
	ROM_FILL ( 0x00000, 0x10000, 0x39 )
ROM_END

ROM_START ( t9000 )
	ROM_REGION ( 0x1000, "monitor", 0 )
	ROM_LOAD ( "t9000.bin", 0x0000, 0x1000, CRC(5cd41431) SHA1(8bf1a40964b76584ee8c83e62be6635de8d1ccd0) )

	ROM_REGION ( 0x800, "mc6846", 0 )
	ROM_LOAD ( "t9000_mc6846.bin", 0x000, 0x800, CRC(8987b838) SHA1(9fee6bb31dc3b39265e7ebc73b44943d4115a516) )

	ROM_REGION ( 0x10000, "cartridge", 0 )
	ROM_FILL ( 0x00000, 0x10000, 0x39 )
ROM_END


/* ------------ inputs   ------------ */

static INPUT_PORTS_START ( to7_config )
	PORT_START ( "config" )

	PORT_CONFNAME ( 0x01, 0x00, "Game Port" )
	PORT_CONFSETTING ( 0x00, DEF_STR( Joystick ) )
	PORT_CONFSETTING ( 0x01, "Mouse" )

INPUT_PORTS_END

static INPUT_PORTS_START ( to7_keyboard )
	PORT_START ( "keyboard.0" )
		KEY ( 0, "Shift", LSHIFT ) PORT_CODE ( KEYCODE_RSHIFT ) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT  ( 0xfe, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_START ( "keyboard.1" )
		KEY ( 0, "W", W )                PORT_CHAR('W')
		KEY ( 1, UTF8_UP, UP )    PORT_CHAR(UCHAR_MAMEKEY(UP))
		KEY ( 2, "C \303\247", C )       PORT_CHAR('C')
		KEY ( 3, "Clear", ESC )          PORT_CHAR(UCHAR_MAMEKEY(ESC))
		KEY ( 4, "Enter", ENTER )        PORT_CHAR(13)
		KEY ( 5, "Control", LCONTROL )   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
		KEY ( 6, "Accent", END )         PORT_CHAR(UCHAR_MAMEKEY(END))
		KEY ( 7, "Stop", TAB )           PORT_CHAR(27)
	PORT_START ( "keyboard.2" )
		KEY ( 0, "X", X )                PORT_CHAR('X')
		KEY ( 1, UTF8_LEFT, LEFT )  PORT_CHAR(UCHAR_MAMEKEY(LEFT))
		KEY ( 2, "V", V )                PORT_CHAR('V')
		KEY ( 3, "Q", Q )                PORT_CHAR('Q')
		KEY ( 4, "* :", QUOTE )          PORT_CHAR('*') PORT_CHAR(':')
		KEY ( 5, "A", A )                PORT_CHAR('A')
		KEY ( 6, "+ ;", EQUALS )         PORT_CHAR('+') PORT_CHAR(';')
		KEY ( 7, "1 !", 1 )              PORT_CHAR('1') PORT_CHAR('!')
	PORT_START ( "keyboard.3" )
		KEY ( 0, "Space Caps-Lock", SPACE ) PORT_CHAR(' ') PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
		KEY ( 1, UTF8_DOWN, DOWN )  PORT_CHAR(UCHAR_MAMEKEY(DOWN))
		KEY ( 2, "B", B )                PORT_CHAR('B')
		KEY ( 3, "S", S )                PORT_CHAR('S')
		KEY ( 4, "/ ?", SLASH )          PORT_CHAR('/') PORT_CHAR('?')
		KEY ( 5, "Z \305\223", Z)        PORT_CHAR('Z')
		KEY ( 6, "- =", MINUS )          PORT_CHAR('-') PORT_CHAR('=')
		KEY ( 7, "2 \" \302\250", 2 )    PORT_CHAR('2') PORT_CHAR('"')
	PORT_START ( "keyboard.4" )
		KEY ( 0, "@ \342\206\221", TILDE ) PORT_CHAR('@')
		KEY ( 1, UTF8_RIGHT, RIGHT ) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
		KEY ( 2, "M", M )                PORT_CHAR('M')
		KEY ( 3, "D", D )                PORT_CHAR('D')
		KEY ( 4, "P", P )                PORT_CHAR('P')
		KEY ( 5, "E", E )                PORT_CHAR('E')
		KEY ( 6, "0 \140", 0 )           PORT_CHAR('0') PORT_CHAR( 0140 )
		KEY ( 7, "3 #", 3 )              PORT_CHAR('3') PORT_CHAR('#')
	PORT_START ( "keyboard.5" )
		KEY ( 0, ". >", STOP )           PORT_CHAR('.') PORT_CHAR('>')
		KEY ( 1, "Home", HOME )          PORT_CHAR(UCHAR_MAMEKEY(HOME))
		KEY ( 2, "L", L )                PORT_CHAR('L')
		KEY ( 3, "F", F )                PORT_CHAR('F')
		KEY ( 4, "O", O )                PORT_CHAR('O')
		KEY ( 5, "R", R )                PORT_CHAR('R')
		KEY ( 6, "9 )", 9 )              PORT_CHAR('9') PORT_CHAR(')')
		KEY ( 7, "4 $", 4 )              PORT_CHAR('4') PORT_CHAR('$')
	PORT_START ( "keyboard.6" )
		KEY ( 0, ", <", COMMA )          PORT_CHAR(',') PORT_CHAR('<')
		KEY ( 1, "Insert", INSERT )      PORT_CHAR(UCHAR_MAMEKEY(INSERT))
		KEY ( 2, "K", K )                PORT_CHAR('K')
		KEY ( 3, "G", G )                PORT_CHAR('G')
		KEY ( 4, "I", I )                PORT_CHAR('I')
		KEY ( 5, "T", T )                PORT_CHAR('T')
		KEY ( 6, "8 (", 8 )              PORT_CHAR('8') PORT_CHAR('(')
		KEY ( 7, "5 %", 5 )              PORT_CHAR('5') PORT_CHAR('%')
	PORT_START ( "keyboard.7" )
		KEY ( 0, "N", N )                PORT_CHAR('N')
		KEY ( 1, "Delete", DEL )         PORT_CHAR(8)
		KEY ( 2, "J \305\222", J )       PORT_CHAR('J')
		KEY ( 3, "H \302\250", H )       PORT_CHAR('H')
		KEY ( 4, "U", U )                PORT_CHAR('U')
		KEY ( 5, "Y", Y )                PORT_CHAR('Y')
		KEY ( 6, "7 ' \302\264", 7 )     PORT_CHAR('7') PORT_CHAR('\'')
		KEY ( 7, "6 &", 6 )              PORT_CHAR('6') PORT_CHAR('&')

INPUT_PORTS_END

static INPUT_PORTS_START ( to7 )
	PORT_INCLUDE ( thom_lightpen )
	PORT_INCLUDE ( thom_game_port )
	PORT_INCLUDE ( to7_keyboard )
	PORT_INCLUDE ( to7_config )
INPUT_PORTS_END

static INPUT_PORTS_START ( t9000 )
	PORT_INCLUDE ( to7 )
INPUT_PORTS_END

static void to9_floppy_drives(device_slot_interface &device)
{
	device.option_add("dd90_352", FLOPPY_35_DD);
}

static void to8_floppy_drives(device_slot_interface &device)
{
	device.option_add("dd90_352", FLOPPY_35_DD);
	//  device.option_add("qd90_280", FLOPPY_28_QDD);
}

static void to35_floppy_formats(format_registration &fr)
{
	fr.add_pc_formats();
	fr.add(FLOPPY_THOMSON_35_FORMAT);
	fr.add(FLOPPY_SAP_FORMAT);
}

/* ------------ driver ------------ */

void thomson_state::to7_base(machine_config &config, bool is_mo)
{
	MCFG_MACHINE_START_OVERRIDE( thomson_state, to7 )
	MCFG_MACHINE_RESET_OVERRIDE( thomson_state, to7 )

/* cpu */
	MC6809E(config, m_maincpu, 16_MHz_XTAL / 16);
	m_maincpu->set_addrmap(AS_PROGRAM, &thomson_state::to7_map);

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, M6809_IRQ_LINE);

	INPUT_MERGER_ANY_HIGH(config, "mainfirq").output_handler().set_inputline(m_maincpu, M6809_FIRQ_LINE);

	if (!is_mo)
	{
		/* timer */
		MC6846(config, m_mc6846, 16_MHz_XTAL / 16);
		m_mc6846->out_port().set(FUNC(thomson_state::to7_timer_port_out));
		m_mc6846->in_port().set(FUNC(thomson_state::to7_timer_port_in));
		m_mc6846->cp2().set("buzzer", FUNC(dac_bit_interface::write));
		m_mc6846->cto().set(FUNC(thomson_state::to7_set_cassette));
		m_mc6846->irq().set("mainirq", FUNC(input_merger_device::in_w<0>));
	}

/* video */
	SCREEN(config, "screen", SCREEN_TYPE_RASTER).set_palette("palette");

	PALETTE(config, "palette", FUNC(thomson_state::thom_palette), 4097); // 12-bit color + transparency

/* sound */
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, "buzzer", 0).add_route(ALL_OUTPUTS, "speaker", 0.5);
	DAC_6BIT_R2R(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.5); // 6-bit game extension R-2R DAC (R=10K)

/* cassette */
	CASSETTE(config, m_cassette);
	m_cassette->set_formats(to7_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->set_interface("to_cass");

/* extension port */
	THOMSON_EXTENSION(config, m_extension, 16_MHz_XTAL / 16);
	m_extension->firq_callback().set("mainfirq", FUNC(input_merger_device::in_w<3>));
	m_extension->irq_callback().set("mainirq", FUNC(input_merger_device::in_w<3>));
	m_extension->option_add("cc90_232", CC90_232);
	m_extension->option_add("cd90_015", CD90_015);
	m_extension->option_add("cq90_028", CQ90_028);
	m_extension->option_add("cd90_351", CD90_351);
	m_extension->option_add("cd90_640", CD90_640);
	m_extension->option_add("md90_120", MD90_120);
	m_extension->option_add("midipak", LOGIMUS_MIDIPAK);
	m_extension->option_add("rf57_932", RF57_932);
	m_extension->option_add("speech", THOMSON_SPEECH);
	if(is_mo)
		m_extension->option_add("nanoreseau", NANORESEAU_MO);
	else
		m_extension->option_add("nanoreseau", NANORESEAU_TO);

/* pia */
	PIA6821(config, m_pia_sys);
	m_pia_sys->readpa_handler().set(FUNC(thomson_state::to7_sys_porta_in));
	m_pia_sys->readpb_handler().set(FUNC(thomson_state::to7_sys_portb_in));
	m_pia_sys->writepb_handler().set(FUNC(thomson_state::to7_sys_portb_out));
	m_pia_sys->ca2_handler().set(FUNC(thomson_state::to7_set_cassette_motor));
	m_pia_sys->cb2_handler().set(FUNC(thomson_state::to7_sys_cb2_out));
	m_pia_sys->irqa_handler().set("mainfirq", FUNC(input_merger_device::in_w<0>));
	m_pia_sys->irqb_handler().set("mainfirq", FUNC(input_merger_device::in_w<1>));

	PIA6821(config, m_pia_game);
	m_pia_game->readpa_handler().set(FUNC(thomson_state::to7_game_porta_in));
	m_pia_game->readpb_handler().set(FUNC(thomson_state::to7_game_portb_in));
	m_pia_game->writepb_handler().set(FUNC(thomson_state::to7_game_portb_out));
	m_pia_game->cb2_handler().set(FUNC(thomson_state::to7_game_cb2_out));
	m_pia_game->irqa_handler().set("mainirq", FUNC(input_merger_device::in_w<1>));
	m_pia_game->irqb_handler().set("mainirq", FUNC(input_merger_device::in_w<2>));

/* cartridge */
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "to_cart", "m7,rom").set_device_load(FUNC(thomson_state::to7_cartridge));

/* internal ram */
	RAM(config, m_ram).set_default_size("40K").set_extra_options("24K,48K");

/* software lists */
	SOFTWARE_LIST(config, "to7_cart_list").set_original("to7_cart");
	SOFTWARE_LIST(config, "to7_cass_list").set_original("to7_cass");
	SOFTWARE_LIST(config, "to_flop_list").set_original("to_flop");
	SOFTWARE_LIST(config, "to7_qd_list").set_original("to7_qd");
}

void thomson_state::to7(machine_config &config)
{
	to7_base(config, false);

	MC6809(config.replace(), m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &thomson_state::to7_map);

	TO7_VIDEO(config, m_video, 16_MHz_XTAL);
	m_video->set_screen("screen");
	m_video->set_lightpen_decal(TO7_LIGHTPEN_DECAL);
	m_video->set_lightpen_steps(3);
	m_video->set_vram_page_cb(FUNC(thomson_state::get_vram_page));
	m_video->set_lightpen_step_cb(FUNC(thomson_state::to7_lightpen_cb));
	m_video->init_cb().set(m_pia_sys, FUNC(pia6821_device::ca1_w));
}

void thomson_state::t9000(machine_config &config)
{
	to7(config);
}


COMP( 1982, to7, 0, 0, to7, to7, thomson_state, empty_init, "Thomson", "TO7", 0 )

COMP( 1980, t9000, to7, 0, t9000, t9000, thomson_state, empty_init, "Thomson", "T9000", 0 )


/***************************** TO7/70 *********************************

TO7/70 ( 1984)
------

Enhanced TO7.
The TO7/70 supports virtually all TO7 software and most TO7 devices and
extensions (floppy, game, communucation, etc.).
As the TO7, it is only usable with a cartridge, and most software require
the BASIC 1.0 cartridge to be present.
Though, you may also use the more advanced BASIC 128 (-cart basic128.m7):
it allows BASIC programs to access all the memory and the video capabilities,
includes its own DOS (no need for a boot disk), but may not be compatible
with all games.

It has the following modifications:

* chips:
  - custom logics for video, lightpen, address map has been replaced with an
    integrated Gate-Array (Motorola MC 1300 ALS)

* memory:
  - 48 KB user base RAM (16 KB unswitchable + 2 switchable banks of 16 KB)
    + 64 KB user extended RAM (EM 97-064, as 4 extra 16 KB banks)
    = 112 KB total user RAM emulated
  - now 8-bit x 8 K color RAM (instead of 6-bit x 8 K)

* video:
  - 16-color fixed palette instead of 8-color (but same constraints)
  - IN 57-001: TV overlay extension, not implemented
    (black becomes transparent and shows the TV image)

* devices:
  - lightpen management has changed, it now has 1-pixel horizontal resolution
  - keyboard management has changed (but the keys are the same)


TO7/70 arabic (198?)
-------------

TO7/70 with an alternate ROM.
Together with a special (64 KB) BASIC 128 cartridge (-cart basic128a.m7),
it allows typing in arabic.
Use Ctrl+W to switch to arabic, and Ctrl+F to switch back to latin.
In latin mode, Ctrl+U / Ctrl+X to start / stop typing in-line arabic.
In arabic mode, Ctrl+E / Ctrl+X to start / stop typing in-line latin.

**********************************************************************/

void thomson_state::to770_map(address_map &map)
{
	map(0x0000, 0x3fff).bankr(THOM_CART_BANK).w(FUNC(thomson_state::to7_cartridge_w)); /* 4 * 16 KB */
	map(0x4000, 0x5fff).bankr(THOM_VRAM_BANK).w(FUNC(thomson_state::to770_vram_w));
	map(0x6000, 0x9fff).bankrw(THOM_BASE_BANK); /* 16 KB */
	map(0xa000, 0xdfff).bankrw(THOM_RAM_BANK);  /* 6 * 16 KB */
	map(0xe7c0, 0xe7c7).rw(m_mc6846, FUNC(mc6846_device::read), FUNC(mc6846_device::write));
	map(0xe7c8, 0xe7cb).rw("pia_0", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
	map(0xe7cc, 0xe7cf).rw("pia_1", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
	map(0xe7e4, 0xe7e7).rw(m_video, FUNC(to770_video_device::gatearray_r), FUNC(to770_video_device::gatearray_w));
	map(0xe800, 0xefff).rom().region("mc6846", 0);
	map(0xf000, 0xffff).rom().region("monitor", 0);

/* 0x10000 - 0x1ffff: 64 KB external ROM cartridge */
/* 18 KB floppy / network ROM controllers */

/* RAM mapping:
   0x00000 - 0x03fff: 16 KB video RAM
   0x04000 - 0x07fff: 16 KB unbanked base RAM
   0x08000 - 0x1ffff: 6 * 16 KB banked extended RAM
 */

}



/* ------------ ROMS ------------ */

ROM_START ( to770 )
	ROM_REGION ( 0x1000, "monitor", 0 )
	ROM_LOAD ( "to770.i29", 0x0000, 0x1000, CRC(1ede9310) SHA1(264f0167b3e64a894f347ae5e9123f38b993ead1) )

	ROM_REGION ( 0x800, "mc6846", 0 )
	ROM_LOAD ( "tha010_ef6846p.i33", 0x000, 0x800, CRC(39d74cec) SHA1(6428fe9439a1f09c2864697d40da9c0b72a52ca1) )

	ROM_REGION ( 0x20, "proms", 0 )
	ROM_LOAD ( "a2.i23", 0x00, 0x20, NO_DUMP ) // palette

	ROM_REGION ( 0x10000, "cartridge", 0 )
	ROM_FILL ( 0x00000, 0x10000, 0x39 )
ROM_END

ROM_START ( to770a )
	ROM_REGION ( 0x1000, "monitor", 0 )
	ROM_LOAD ( "to770a.bin", 0x0000, 0x1000, CRC(de30bee8) SHA1(5f9bf37979d35a0fa7fa36538a0e2633065b1639) )

	ROM_REGION ( 0x800, "mc6846", 0 )
	ROM_LOAD ( "to770a_mc6846.bin", 0x000, 0x800, CRC(2bf67c9c) SHA1(6bb97045b591bd279c7b93616e703c85e0a5c9b5) )

	ROM_REGION ( 0x20, "proms", 0 )
	ROM_LOAD ( "a2.i23", 0x00, 0x20, NO_DUMP ) // palette

	ROM_REGION ( 0x10000, "cartridge", 0 )
	ROM_FILL ( 0x00000, 0x10000, 0x39 )
ROM_END


/* ------------ inputs   ------------ */

static INPUT_PORTS_START ( to770 )
	PORT_INCLUDE ( to7 )

	PORT_MODIFY ( "keyboard.1" )
		KEY ( 2, "C \302\250 \303\247", C )   PORT_CHAR('C')
	PORT_MODIFY ( "keyboard.4" )
		KEY ( 6, "0 \140 \303\240", 0 )       PORT_CHAR('0') PORT_CHAR( 0140 )
	PORT_MODIFY ( "keyboard.5" )
		KEY ( 6, "9 ) \303\247", 9 )          PORT_CHAR('9') PORT_CHAR(')')
	PORT_MODIFY ( "keyboard.6" )
		KEY ( 6, "8 ( \303\271", 8 )          PORT_CHAR('8') PORT_CHAR('(')
	PORT_MODIFY ( "keyboard.7" )
		KEY ( 6, "7 ' \303\250 \302\264", 7 ) PORT_CHAR('7') PORT_CHAR('\'')
		KEY ( 7, "6 & \303\251", 6 )          PORT_CHAR('6') PORT_CHAR('&')

INPUT_PORTS_END

/* arabic version (QWERTY keyboard) */
static INPUT_PORTS_START ( to770a )
	PORT_INCLUDE ( to770 )

	PORT_MODIFY ( "keyboard.1" )
		KEY ( 0, "Z", Z )                     PORT_CHAR('Z')
	PORT_MODIFY ( "keyboard.2" )
		KEY ( 3, "A", A )                     PORT_CHAR('A')
		KEY ( 4, "/ ?", QUOTE )               PORT_CHAR('/') PORT_CHAR('?')
		KEY ( 5, "Q", Q )                     PORT_CHAR('Q')
	PORT_MODIFY ( "keyboard.3" )
		KEY ( 4, "* :", SLASH )               PORT_CHAR('*') PORT_CHAR(':')
		KEY ( 5, "W", W)                      PORT_CHAR('W')
	PORT_MODIFY ( "keyboard.4" )
		KEY ( 0, ". >", STOP )                PORT_CHAR('.') PORT_CHAR('>')
		KEY ( 2, "@ \342\206\221", TILDE )    PORT_CHAR('@') PORT_CHAR('^')
		KEY ( 6, "0 \302\243 \302\260 \140", 0 )   PORT_CHAR('0') PORT_CHAR( 0140 )
	PORT_MODIFY ( "keyboard.5" )
		KEY ( 0, ", <", COMMA )               PORT_CHAR(',') PORT_CHAR('<')
		KEY ( 6, "9 ) \303\261", 9 )          PORT_CHAR('9') PORT_CHAR(')')
	PORT_MODIFY ( "keyboard.6" )
		KEY ( 0, "M", M )                     PORT_CHAR('M')
		KEY ( 6, "8 ( \303\274", 8 )          PORT_CHAR('8') PORT_CHAR('(')
	PORT_MODIFY ( "keyboard.7" )
		KEY ( 6, "7 ' \303\266 \302\264", 7 ) PORT_CHAR('7') PORT_CHAR('\'')
		KEY ( 7, "6 & \303\244", 6 )          PORT_CHAR('6') PORT_CHAR('&')

INPUT_PORTS_END


/* ------------ driver ------------ */

void thomson_state::to770(machine_config &config)
{
	to7_base(config, false);
	MCFG_MACHINE_START_OVERRIDE( thomson_state, to770 )
	MCFG_MACHINE_RESET_OVERRIDE( thomson_state, to770 )

	m_maincpu->set_addrmap(AS_PROGRAM, &thomson_state::to770_map);

	m_pia_sys->readpa_handler().set(FUNC(thomson_state::to770_sys_porta_in));
	m_pia_sys->readpb_handler().set_constant(0);
	m_pia_sys->writepb_handler().set(FUNC(thomson_state::to770_sys_portb_out));
	m_pia_sys->cb2_handler().set(FUNC(thomson_state::to770_sys_cb2_out));

	m_mc6846->out_port().set(FUNC(thomson_state::to770_timer_port_out));

	/* internal ram */
	m_ram->set_default_size("128K").set_extra_options("64K");

	TO770_VIDEO(config, m_video, 16_MHz_XTAL);
	m_video->set_screen("screen");
	m_video->set_lightpen_decal(TO7_LIGHTPEN_DECAL);
	m_video->set_lightpen_steps(3);
	m_video->set_vram_page_cb(FUNC(thomson_state::get_vram_page));
	m_video->set_lightpen_step_cb(FUNC(thomson_state::to7_lightpen_cb));
	m_video->init_cb().set(m_pia_sys, FUNC(pia6821_device::ca1_w));

	SOFTWARE_LIST(config, "t770_cart_list").set_original("to770_cart");
	SOFTWARE_LIST(config.replace(), "to7_cart_list").set_compatible("to7_cart");
}

void thomson_state::to770a(machine_config &config)
{
	to770(config);
	config.device_remove("t770_cart_list");
	SOFTWARE_LIST(config, "t770a_cart_list").set_original("to770a_cart");
}

COMP( 1984, to770, 0, 0, to770, to770, thomson_state, empty_init, "Thomson", "TO7/70", 0 )

COMP( 1984, to770a, to770, 0, to770a, to770a, thomson_state, empty_init, "Thomson", "TO7/70 (Arabic)", 0 )


/************************* MO5 / MO5E *********************************

MO5 (1984)
---

The MO5 is Thomson's attempt to provide a less costly micro-computer, using
the same technology as the TO7/70.
It has less memory and is less expandable. The MC6846 timer has disappeared.
The BIOS has been throughly rewritten and uses a more compact call scheme.
This, and the fact that the address map has changed, makes the MO5 completely
TO7 software incompatible (except for pure BASIC programs).
Moreover, the MO5 has incompatible cassette and cartridge formats.
Unlike the TO7, the BASIC 1.0 is integrated and the MO5 can be used "as-is".

* chips:
  - 1 MHz Motorola 6809E CPU
  - 1 Motorola 6821 PIA (+3 for I/O, game, and modem extensions)
  - Motorola 1300 ALS Gate-Array

* memory:
  - 32 KB base user RAM
  - 64 KB extended user RAM (4 x 16 KB banks) with the network extension
    (no available to BASIC programs)
  - 16 KB combined BASIC and BIOS ROM
  - 8 KB color RAM + 8 KB point RAM, bank switched
  - 2 to 8 KB floppy ROM comes with the floppy drive / network extension

* video:
  - as the TO7/70 but with different color encoding,
    320x200 pixels with color constraints, 16-color fixed palette
  - IN 57-001: TV overlay extension (not implemented)

* devices:
  - AZERTY keyboard, 58-keys, slightlty different from the TO7
    . the right SHIFT key has been replaced with a BASIC key
    . no caps-lock led
  - the famous lightpen is optional
  - cassette 1200 bauds (frequency signals: 0=4.5kHz, 1=6.3kHz),
    TO7-incompatible
  - optional cartridge, up to 64 KB, incompatible with TO7,
    masks the integrated BASIC ROM
  - game & music, I/O, floppy, network extensions: identical to TO7
  - speech synthesis extension: identical to TO7
  - MIDIPAK MIDI extension: identical to TO7

MO5E (1986)
----

This is a special MO5 version for the export market (mainly Germany).
Although coming in a different (nicer) case, it is internally similar to
the MO5 and is fully compatible.
Differences include:
 - much better keyboard; some are QWERTY instead of AZERTY (we emulate QWERTY)
 - a different BIOS and integrated BASIC
 - the game extension is integrated


**********************************************************************/

void mo5_state::mo5_map(address_map &map)
{

	map(0x0000, 0x1fff).bankr(THOM_VRAM_BANK).w(FUNC(mo5_state::to770_vram_w));
	map(0x2000, 0x9fff).bankrw(THOM_BASE_BANK);
	map(0xa7c0, 0xa7c3).rw("pia_0", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
	map(0xa7cb, 0xa7cb).w(FUNC(mo5_state::mo5_ext_w));
	map(0xa7cc, 0xa7cf).rw("pia_1", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
	map(0xa7e4, 0xa7e7).rw(m_video, FUNC(to770_video_device::gatearray_r), FUNC(to770_video_device::gatearray_w));
	map(0xb000, 0xefff).bankr(THOM_CART_BANK).w(FUNC(mo5_state::mo5_cartridge_w));
	map(0xf000, 0xffff).rom().region("basic", 0x4000);

/* 0x10000 - 0x1ffff: 16 KB integrated BASIC / 64 KB external cartridge */
/* 18 KB floppy / network ROM controllers */

/* RAM mapping:
   0x00000 - 0x03fff: 16 KB video RAM
   0x04000 - 0x0bfff: 32 KB unbanked base RAM
   0x0c000 - 0x1bfff: 4 * 16 KB bank extended RAM
 */

}



/* ------------ ROMS ------------ */

ROM_START ( mo5 )
	ROM_REGION ( 0x5000, "basic", 0 )
	ROM_LOAD ( "mo5.i04", 0x1000, 0x4000, CRC(237c60bf) SHA1(8d2865996a1a8d8a13fc9965c1bcf490f9621399) )

	ROM_REGION ( 0x20, "proms", 0 )
	ROM_LOAD ( "7603-5.i03", 0x00, 0x20, NO_DUMP ) // palette

	ROM_REGION ( 0x10000, "cartridge", 0 )
	ROM_FILL( 0x00000, 0x10000, 0x39 )
ROM_END

ROM_START ( mo5e )
	ROM_REGION ( 0x5000, "basic", 0 )
	ROM_LOAD ( "mo5e.bin", 0x1000, 0x4000, CRC(56f11cf3) SHA1(0f60c8ad391c48b2e7d02b646509586ad34b7417) )

	ROM_REGION ( 0x20, "proms", 0 )
	ROM_LOAD ( "7603-5.i03", 0x00, 0x20, NO_DUMP ) // palette

	ROM_REGION ( 0x10000, "cartridge", 0 )
	ROM_FILL( 0x00000, 0x10000, 0x39 )
ROM_END


/* ------------ inputs  ------------ */

static INPUT_PORTS_START ( mo5 )
	PORT_INCLUDE ( to770 )

	PORT_MODIFY ( "keyboard.0" )
		KEY ( 1, "BASIC", RCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))
	PORT_BIT  ( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )

INPUT_PORTS_END

/* QWERTY version */
static INPUT_PORTS_START ( mo5e )
	PORT_INCLUDE ( mo5 )

	PORT_MODIFY ( "keyboard.1" )
		KEY ( 0, "Z", Z )                     PORT_CHAR('Z')
	PORT_MODIFY ( "keyboard.2" )
		KEY ( 3, "A", A )                     PORT_CHAR('A')
		KEY ( 5, "Q", Q )                     PORT_CHAR('Q')
	PORT_MODIFY ( "keyboard.3" )
		KEY ( 5, "W", W)                      PORT_CHAR('W')
	PORT_MODIFY ( "keyboard.4" )
		KEY ( 0, ". >", STOP )                PORT_CHAR('.') PORT_CHAR('>')
		KEY ( 2, "@ \342\206\221", TILDE )    PORT_CHAR('@') PORT_CHAR('^')
		KEY ( 6, "0 \302\243 \302\260 \140", 0 )   PORT_CHAR('0') PORT_CHAR( 0140 )
	PORT_MODIFY ( "keyboard.5" )
		KEY ( 0, ", <", COMMA )               PORT_CHAR(',') PORT_CHAR('<')
		KEY ( 6, "9 ) \303\261", 9 )          PORT_CHAR('9') PORT_CHAR(')')
	PORT_MODIFY ( "keyboard.6" )
		KEY ( 0, "M", M )                     PORT_CHAR('M')
		KEY ( 6, "8 ( \303\274", 8 )          PORT_CHAR('8') PORT_CHAR('(')
	PORT_MODIFY ( "keyboard.7" )
		KEY ( 6, "7 ' \303\266 \302\264", 7 ) PORT_CHAR('7') PORT_CHAR('\'')
		KEY ( 7, "6 & \303\244", 6 )          PORT_CHAR('6') PORT_CHAR('&')

INPUT_PORTS_END

/* ------------ driver ------------ */

void mo5_state::mo5(machine_config &config)
{
	to7_base(config, true);
	MCFG_MACHINE_START_OVERRIDE( mo5_state, mo5 )
	MCFG_MACHINE_RESET_OVERRIDE( mo5_state, mo5 )

	m_maincpu->set_addrmap(AS_PROGRAM, &mo5_state::mo5_map);

	m_cassette->set_formats(mo5_cassette_formats);
	m_cassette->set_interface("mo_cass");

	subdevice<palette_device>("palette")->set_init(FUNC(mo5_state::mo5_palette));

	m_pia_sys->readpa_handler().set(FUNC(mo5_state::mo5_sys_porta_in));
	m_pia_sys->readpb_handler().set(FUNC(mo5_state::mo5_sys_portb_in));
	m_pia_sys->writepa_handler().set(FUNC(mo5_state::mo5_sys_porta_out));
	m_pia_sys->writepb_handler().set("buzzer", FUNC(dac_bit_interface::data_w));
	m_pia_sys->ca2_handler().set(FUNC(mo5_state::mo5_set_cassette_motor));
	m_pia_sys->cb2_handler().set_nop();
	m_pia_sys->irqb_handler().set("mainirq", FUNC(input_merger_device::in_w<0>)); // WARNING: differs from TO7 !

	GENERIC_CARTSLOT(config.replace(), "cartslot", generic_plain_slot, "mo_cart", "m5,rom").set_device_load(FUNC(mo5_state::mo5_cartridge));

	config.device_remove("to7_cart_list");
	config.device_remove("to7_cass_list");
	config.device_remove("to_flop_list");
	config.device_remove("to7_qd_list");

	SOFTWARE_LIST(config, "mo5_cart_list").set_original("mo5_cart");
	SOFTWARE_LIST(config, "mo5_cass_list").set_original("mo5_cass");
	SOFTWARE_LIST(config, "mo5_flop_list").set_original("mo5_flop");
	SOFTWARE_LIST(config, "mo5_qd_list").set_original("mo5_qd");

	/* internal ram */
	m_ram->set_default_size("112K");

	TO770_VIDEO(config, m_video, 16_MHz_XTAL);
	m_video->set_screen("screen");
	m_video->set_is_mo(true);
	m_video->set_lightpen_decal(MO5_LIGHTPEN_DECAL);
	m_video->set_lightpen_steps(3);
	m_video->set_vram_page_cb(FUNC(mo5_state::get_vram_page));
	m_video->set_lightpen_step_cb(FUNC(mo5_state::mo5_lightpen_cb));
	m_video->int_50hz_cb().set(m_pia_sys, FUNC(pia6821_device::cb1_w));
}

void mo5_state::mo5e(machine_config &config)
{
	mo5(config);
}


COMP( 1984, mo5, 0, 0, mo5, mo5, mo5_state, empty_init, "Thomson", "MO5", 0 )

COMP( 1986, mo5e, mo5, 0, mo5e, mo5e, mo5_state, empty_init, "Thomson", "MO5E", 0 )


/********************************* TO9 *******************************

TO9 (1985)
---

The TO9 is the successor of the TO7/70.
It is a high-end product: it integrates 96 KB of base RAM, 128 KB of
software in ROM, a floppy drive. It has improved graphics capabilities
(several video modes, a palette of 4096 colors, thanks to the use of
a dedicated video gate-array).
The ROM contains the old BASIC 1.0 for compatibility and the newer BASIC 128.
It has a more professional, desktop look, with a separate keyboard, and an
optional mouse.
It is also quite compatible with the TO7 and TO7/70 (but not the MO5).
However, it also has many problems. The integrated BASIC 128 can only access
128 KB of memory, which forces the 64 KB extension to be managed as a virtual
disk. The early versions of the software ROM has many bugs. The integrated
floppy drive is one-sided.
It was replaced quickly with the improved TO9+.

* chips:
  - 1 MHz Motorola 6809E CPU
  - 1 Motorola 6821 PIA (+2 for game, modem extensions)
  - 1 Motorola 6846 timer, PIA
  - 1 Motorola 6805 + 1 Motorola 6850 (keyboard & mouse control)
  - 1 Western Digital 2793 (disk controller)
  - 3 gate-arrays (address decoding, system, video)

* memory:
  - 112 KB base RAM
  - 64 KB extension RAM (as virtual disk)
  - 6 KB BIOS ROM + 2 KB floppy BIOS
  - 128 KB software ROM (BASIC 1, BASIC 128, extended BIOS,
    DOS and configuration GUI, two software: "Paragraphe" and
    "Fiches et dossiers")
  - 16 KB video RAM

* video:
  - 8 video modes:
    o 320x200, 16 colors with constraints (TO7/70 compatible)
    o 320x200, 4 colors without constraints
    o 160x200, 16 colors without constraints
    o 640x200, 2 colors
    o 320x200, 2 colors, two pages
       . page one
       . page two
       . pages overlaid
    o 160x200, 2 colors, four pages overlaid
  - palette: 16 colors can be chosen among 4096

* devices:
  - AZERTY keyboard, 81-keys, French with accents, keypad & function keys
  - cartridge, up to 64 KB, TO7 compatible
  - two-button mouse connected to the keyboard (not working yet)
  - lightpen, with 1-pixel vertical and horizontal resolution
  - 1-bit internal buzzer
  - cassette 900 bauds, TO7 compatible
  - integrated parallel CENTRONICS (printer emulated)
  - SX 90-018: game extension (identical to the TO7)
  - RF 57-932: RS232 extension (identical to the TO7)
  - MD 90-120: MODEM extension (identical to the TO7)
  - IEEE extension ? (unemulated)
  - floppy:
    . integrated floppy controller, based on WD1770 or WD2793
    . integrated one-sided double-density 3''1/2
    . external two-sided double-density 3''1/2, 5''1/4 or QDD (extension)
    . floppies are TO7 and MO5 compatible
  - speech synthesis extension: identical to TO7
  - MIDIPAK MIDI extension: identical to TO7

**********************************************************************/

void to9_state::to9_map(address_map &map)
{

	map(0x0000, 0x3fff).bankr(THOM_CART_BANK).w(FUNC(to9_state::to9_cartridge_w));/* 4 * 16 KB */
	map(0x4000, 0x5fff).bankr(THOM_VRAM_BANK).w(FUNC(to9_state::to770_vram_w));
	map(0x6000, 0x9fff).bankrw(THOM_BASE_BANK); /* 16 KB */
	map(0xa000, 0xdfff).bankrw(THOM_RAM_BANK);  /* 10 * 16 KB */
	map(0xe000, 0xe7af).rom().region("monitor", 0);
	map(0xe7c0, 0xe7c7).rw(m_mc6846, FUNC(mc6846_device::read), FUNC(mc6846_device::write));
	map(0xe7c8, 0xe7cb).rw("pia_0", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
	map(0xe7cc, 0xe7cf).rw("pia_1", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
	map(0xe7d0, 0xe7d3).mirror(4).rw(m_fdc, FUNC(wd_fdc_device_base::read), FUNC(wd_fdc_device_base::write));
	map(0xe7d8, 0xe7d8).rw(FUNC(to9_state::to9_floppy_control_r), FUNC(to9_state::to9_floppy_control_w));
	map(0xe7da, 0xe7db).rw(FUNC(to9_state::to9_vreg_r), FUNC(to9_state::to9_vreg_w));
	map(0xe7dc, 0xe7dc).w(m_video, FUNC(to9_video_device::video_mode_w));
	map(0xe7dd, 0xe7dd).w(m_video, FUNC(to9_video_device::border_color_w));
	map(0xe7de, 0xe7df).rw(m_to9_kbd, FUNC(to9_keyboard_device::kbd_acia_r), FUNC(to9_keyboard_device::kbd_acia_w));
	map(0xe7e4, 0xe7e7).rw(m_video, FUNC(to9_video_device::gatearray_r), FUNC(to9_video_device::gatearray_w));
/*  map(0xe7f0, 0xe7f7).rw(FUNC(to9_state::to9_ieee_r), FUNC(to9_state::to9_ieee_w )); */
	map(0xe800, 0xffff).rom().region("monitor", 0x800);

/* 0x10000 - 0x1ffff:  64 KB external ROM cartridge */
/* 0x20000 - 0x3ffff: 128 KB internal software ROM */
/* 18 KB external floppy / network ROM controllers */

/* RAM mapping:
   0x00000 - 0x03fff: 16 KB video RAM
   0x04000 - 0x07fff: 16 KB unbanked base RAM
   0x08000 - 0x2ffff: 10 * 16 KB banked extended RAM
 */

}



/* ------------ ROMS ------------ */

/* NOT WORKING
   these bios seem heavily patched (probably to work with specific emulators
   that trap some bios calls)
 */

ROM_START ( to9 )
	ROM_REGION ( 0x2000, "monitor", 0 )
	ROM_LOAD ( "monitor.i42", 0x0000, 0x2000, /* BIOS & floppy controller */
		CRC(f9278bf7)
		SHA1(9e99e6ae0285950f007b19161de642a4031fe46e) )

		/* BASIC & software */
	ROM_REGION ( 0x20000, "basic", 0 )
	ROM_LOAD ( "basic128.i39",   0x00000, 0x8000, CRC(c9bc204f) SHA1(e4c2a684e9186f49c8092d16f0f74764f51ad86c) )
	ROM_LOAD ( "basic1.i56",     0x08000, 0x8000, CRC(b1469ffc) SHA1(548c631d1272dfa25e3e925adc08f6eeb8e4448e) )
	ROM_LOAD ( "fiches.i38",     0x10000, 0x8000, CRC(3eba1a1a) SHA1(e8ed04d30fb70fda37ac31dd5c2c2e59248cd395) )
	ROM_LOAD ( "paragraphe.i40", 0x18000, 0x8000, CRC(1ff9e47e) SHA1(381c493c07271e3259be13bf9edfbf2b2c81a059) )

	ROM_REGION ( 0x10000, "cartridge", 0 )
	ROM_FILL( 0x00000, 0x10000, 0x39 )
ROM_END


/* ------------ inputs   ------------ */

static INPUT_PORTS_START ( to9 )
	PORT_INCLUDE ( thom_lightpen )
	PORT_INCLUDE ( thom_game_port )

	PORT_START ( "config" )
	PORT_BIT ( 0x01, 0x00, IPT_UNUSED )

	PORT_MODIFY ( "mouse_x" )
	PORT_BIT ( 0xffff, 0x00, IPT_UNUSED )

	PORT_MODIFY ( "mouse_y" )
	PORT_BIT ( 0xffff, 0x00, IPT_UNUSED )

	PORT_MODIFY ( "mouse_button" )
	PORT_BIT ( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT ( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

/* ------------ driver ------------ */

void to9_state::to9(machine_config &config)
{
	to7_base(config, false);
	MCFG_MACHINE_START_OVERRIDE( to9_state, to9 )
	MCFG_MACHINE_RESET_OVERRIDE( to9_state, to9 )

	m_maincpu->set_addrmap(AS_PROGRAM, &to9_state::to9_map);

	TO9_KEYBOARD(config, m_to9_kbd);
	m_to9_kbd->irq_cb().set(m_mainirq, FUNC(input_merger_device::in_w<4>));

	m_pia_sys->readpa_handler().set(FUNC(to9_state::to9_sys_porta_in));
	m_pia_sys->readpb_handler().set_constant(0);
	m_pia_sys->writepa_handler().set(FUNC(to9_state::to9_sys_porta_out));
	m_pia_sys->writepb_handler().set(FUNC(to9_state::to9_sys_portb_out));
	m_pia_sys->cb2_handler().set_nop();
	m_pia_sys->irqa_handler().set_nop();

	m_mc6846->out_port().set(FUNC(to9_state::to9_timer_port_out));

	WD1770(config, m_fdc, 16_MHz_XTAL / 2);
	FLOPPY_CONNECTOR(config, m_floppy[0], to9_floppy_drives, "dd90_352", to35_floppy_formats, true).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], to9_floppy_drives, nullptr,    to35_floppy_formats, false).enable_sound(true);

	m_extension->option_remove("cd90_015");
	m_extension->option_remove("cq90_028");
	m_extension->option_remove("cd90_351");
	m_extension->option_remove("cd90_640");
	m_extension->option_remove("nanoreseau");

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(to9_state::write_centronics_busy));

	/* internal ram */
	m_ram->set_default_size("192K").set_extra_options("128K");

	TO9_VIDEO(config, m_video, 16_MHz_XTAL);
	m_video->set_screen("screen");
	m_video->set_lightpen_decal(TO9_LIGHTPEN_DECAL);
	m_video->set_lightpen_steps(3);
	m_video->set_vram_page_cb(FUNC(to9_state::get_vram_page));
	m_video->set_lightpen_step_cb(FUNC(to9_state::to7_lightpen_cb));
}


COMP( 1985, to9, 0, 0, to9, to9, to9_state, empty_init, "Thomson", "TO9", MACHINE_IMPERFECT_COLORS )


/******************************** TO8 ********************************

TO8 (1986)
---

The TO8 was meant to replace the TO7/70 as a home-computer.
It includes and improves on the technology from the TO9 (improved video,
256 KB of RAM fully managed by the new BASIC 512, more integrated gate-array).
It has a more compact Amiga-like look, no separate keyboard, no integrated
floppy drive (although the controller is integrated), no software in ROM,
less extension slots. Also, the game & music extension is now integrated.
It is quite compatible with the TO7 and TO7/70, and with the TO9 to some
extent.
The TO8 was quite popular and became the de-facto gamming computer in the
Thomson family.

* chips:
  - 1 MHz Motorola 6809E CPU
  - 2 Motorola 6821 PIAs (system, game, +1 in modem extension)
  - 1 Motorola 6846 timer, PIA
  - 1 Motorola 6804 (keyboard)
  - 2 gate-arrays (system & video, floppy controller)

* memory:
  - 256 KB base RAM
    + 256 KB extended RAM (EM 88-256) = 512 KB total RAM emulated
  - 16 KB BIOS ROM
  - 64 KB software ROM (BASIC 1, BASIC 512, extended BIOS)
  - unified memory view via improved bank switching

* video:
  improved wrt TO9: a 9-th video mode, 4 video pages (shared with main RAM)
  border color has its 4-th index bit inverted

* devices:
  - same keyboard as T09: AZERTY 81-keys
    (but no 6850 controller, the 6804 is directly connected to the 6821 & 6846)
  - cartridge, up to 64 KB, TO7 compatible
  - two-button serial mouse (TO9-incompatible)
  - lightpen, with 1-pixel vertical and horizontal resolution
  - two 8-position 2-button game pads (SX 90-018 extension integrated)
  - 6-bit DAC sound (NOTE: 1-bit buzzer is gone)
  - cassette 900 bauds, TO7 compatible
  - integrated parallel CENTRONICS (printer emulated)
  - RF 57-932: RS232 extension (identical to the TO7)
  - MD 90-120: MODEM extension (identical to the TO7?)
  - IEEE extension ?
  - floppy:
   . integrated floppy controller, based on custom Thomson gate-array
   . no integrated drive
   . up to two external two-sided double-density 3"1/2, 5"1/4 or QDD drives
   . floppies are TO7 and MO5 compatible
  - speech synthesis extension: identical to TO7
  - MIDIPAK MIDI extension: identical to TO7

TO8D (1987)
----

The TO8D is simply a TO8 with an integrated 3"1/2 floppy drive.

**********************************************************************/


void to8_state::to8_map(address_map &map)
{
	map(0x0000, 0x3fff).bankr(THOM_CART_BANK).w(FUNC(to8_state::to8_cartridge_w)); /* 4 * 16 KB */
	map(0x4000, 0x5fff).bankr(THOM_VRAM_BANK).w(FUNC(to8_state::to770_vram_w));
	map(0x6000, 0x7fff).bankr(TO8_SYS_LO).w(FUNC(to8_state::to8_sys_lo_w));
	map(0x8000, 0x9fff).bankr(TO8_SYS_HI).w(FUNC(to8_state::to8_sys_hi_w));
	map(0xa000, 0xbfff).bankr(TO8_DATA_LO).w(FUNC(to8_state::to8_data_lo_w));
	map(0xc000, 0xdfff).bankr(TO8_DATA_HI).w(FUNC(to8_state::to8_data_hi_w));
	map(0xe000, 0xffff).bankr(TO8_BIOS_BANK);
	map(0xe7c0, 0xe7ff).unmaprw();
	map(0xe7c0, 0xe7c7).rw(m_mc6846, FUNC(mc6846_device::read), FUNC(mc6846_device::write));
	map(0xe7c8, 0xe7cb).rw("pia_0", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
	map(0xe7cc, 0xe7cf).rw("pia_1", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
	map(0xe7d0, 0xe7d7).m("thmfc1", FUNC(thmfc1_device::map));
	map(0xe7da, 0xe7dd).rw(FUNC(to8_state::to8_vreg_r), FUNC(to8_state::to8_vreg_w));
	map(0xe7e4, 0xe7e7).rw(m_video, FUNC(to8_video_device::gatearray_r), FUNC(to8_video_device::gatearray_w));
/*  map(0xe7f0, 0xe7f7).rw(FUNC(to8_state::to9_ieee_r), FUNC(to8_state::to9_ieee_w )); */

/* 0x10000 - 0x1ffff: 64 KB external ROM cartridge */
/* 0x20000 - 0x2ffff: 64 KB internal software ROM */
/* 0x30000 - 0x33fff: 16 KB BIOS ROM */
/* 18 KB external floppy / network ROM controllers */

/* RAM mapping: 512 KB flat (including video) */

}


/* ------------ ROMS ------------ */

ROM_START ( to8 )
		/* BIOS & floppy */
	ROM_REGION ( 0x4000, "monitor", 0 )
	ROM_LOAD ( "to8.iw17", 0x0000, 0x4000, CRC(c2610c13) SHA1(75fffd10494d1ebb78e9068e1b232ede6641ad8c) )

		/* BASIC */
	ROM_REGION ( 0x10000, "basic", 0 )
	ROM_LOAD ( "basic512.iw16", 0x0000, 0x8000, CRC(f45e3592) SHA1(8fd98973bd33f88fb63278a7fba86329076b473f) )
	ROM_LOAD ( "basic1.iw15",   0x8000, 0x8000, CRC(2f4f61fc) SHA1(e0eea9c941113c550ba0c55a5c15f55a64c39060) )

	ROM_REGION ( 0x10000, "cartridge", 0 )
	ROM_FILL( 0x00000, 0x10000, 0x39 )
ROM_END

ROM_START ( to8d )
		/* BIOS & floppy */
	ROM_REGION ( 0x4000, "monitor", 0 )
	ROM_LOAD ( "to8d.iw17", 0x0000, 0x4000, CRC(15fd82d5) SHA1(dd90c326abfec1d28ffc4fe974615870e33a597d) )

		/* BASIC */
	ROM_REGION ( 0x10000, "basic", 0 )
	ROM_LOAD ( "basic.iw15", 0x00000, 0x10000, CRC(ffff0512) SHA1(c474d74a1e315d61e21c74c6a1b26af499b385ea) )

	ROM_REGION ( 0x10000, "cartridge", 0 )
	ROM_FILL( 0x00000, 0x10000, 0x39 )
ROM_END


/* ------------ inputs   ------------ */

static INPUT_PORTS_START ( to8 )
	PORT_INCLUDE ( thom_lightpen )
	PORT_INCLUDE ( thom_game_port )
	PORT_INCLUDE ( to7_config )
INPUT_PORTS_END


static INPUT_PORTS_START ( to8d )
	PORT_INCLUDE ( to8 )
INPUT_PORTS_END

/* ------------ driver ------------ */

void to8_state::to8(machine_config &config)
{
	to7_base(config, false);
	MCFG_MACHINE_START_OVERRIDE( to8_state, to8 )
	MCFG_MACHINE_RESET_OVERRIDE( to8_state, to8 )

	m_maincpu->set_addrmap(AS_PROGRAM, &to8_state::to8_map);

	TO8_KEYBOARD(config, m_to8_kbd);
	m_to8_kbd->data_cb().set(m_mc6846, FUNC(mc6846_device::set_input_cp1));

	m_pia_sys->readpa_handler().set(FUNC(to8_state::to8_sys_porta_in));
	m_pia_sys->readpb_handler().set_constant(0);
	m_pia_sys->writepa_handler().set(FUNC(to8_state::to8_sys_porta_out));
	m_pia_sys->writepb_handler().set(FUNC(to8_state::to8_sys_portb_out));
	m_pia_sys->cb2_handler().set_nop();
	m_pia_sys->irqa_handler().set_nop();

	THMFC1(config, "thmfc1", 16_MHz_XTAL);
	FLOPPY_CONNECTOR(config, "thmfc1:0", to8_floppy_drives, "dd90_352", to35_floppy_formats, false).enable_sound(true);
	FLOPPY_CONNECTOR(config, "thmfc1:1", to8_floppy_drives, nullptr,    to35_floppy_formats, false).enable_sound(true);

	m_extension->option_remove("cd90_015");
	m_extension->option_remove("cq90_028");
	m_extension->option_remove("cd90_351");
	m_extension->option_remove("cd90_640");
	m_extension->option_remove("nanoreseau");

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(to8_state::write_centronics_busy));

	m_mc6846->out_port().set(FUNC(to8_state::to8_timer_port_out));
	m_mc6846->in_port().set(FUNC(to8_state::to8_timer_port_in));
	m_mc6846->cp2().set(FUNC(to8_state::to8_timer_cp2_out));

	/* internal ram */
	m_ram->set_default_size("512K").set_extra_options("256K");

	TO8_VIDEO(config, m_video, 16_MHz_XTAL);
	m_video->set_screen("screen");
	m_video->set_lightpen_decal(TO8_LIGHTPEN_DECAL);
	m_video->set_lightpen_steps(4);
	m_video->set_vram_page_cb(FUNC(to8_state::get_vram_page));
	m_video->set_update_ram_bank_cb(FUNC(to8_state::to8_update_ram_bank));
	m_video->set_update_cart_bank_cb(FUNC(to8_state::to8_update_cart_bank));
	m_video->lightpen_intr_cb().set(m_mainfirq, FUNC(input_merger_device::in_w<2>));

	SOFTWARE_LIST(config, "to8_cass_list").set_original("to8_cass");
	SOFTWARE_LIST(config, "to8_qd_list").set_original("to8_qd");
	SOFTWARE_LIST(config.replace(), "to7_cass_list").set_compatible("to7_cass");
	SOFTWARE_LIST(config.replace(), "to7_qd_list").set_compatible("to7_qd");
}

void to8_state::to8d(machine_config &config)
{
	to8(config);
	subdevice<floppy_connector>("thmfc1:0")->set_fixed(true);
}


COMP( 1986, to8, 0, 0, to8, to8, to8_state, empty_init, "Thomson", "TO8", 0 )

COMP( 1987, to8d, to8, 0, to8d, to8d, to8_state, empty_init, "Thomson", "TO8D", 0 )


/******************************** TO9+ *******************************

TO9+ (1986)
----

The TO9+ is the direct successor of the T09 as Thomson's high-end
product: desktop look, 512 KB of RAM, integrated floppy drive and
modem. Some software integrated in ROM on the TO9 are now supplied on
floppies.
Internally, the TO9+ is based more on TO8 technology than T09
(same gate-arrays).
It has enhanced communication capabilities by integrating either the
MODEM or the RS232 extension.
It should be compatible with the TO9 and, to some extent, with the TO7, TO7/70
and TO8.
It uses the same video gate-array and floppy controller.

The differences with the TO8 are:

* chips:
  - 1 Motorola 6805 + 1 Motorola 6850 (keyboard)
  - 3 Motorola 6821 PIAs (system, game, modem)

* memory:
  - 512 KB RAM (not extendable)

* devices:
  - same keyboard as T08/TO9 (AZERTY 81-keys) but different controller
  - RF 57-932: RS232 (identical to the TO7) sometimes integrated
  - MD 90-120: MODEM (identical to the TO7?) sometimes integrated
  - IEEE extension ?
  - floppy: one two-sided double-density 3''1/2 floppy drive is integrated
  - RS 52-932 RS232 extension ?
  - digitisation extension

**********************************************************************/

void to8_state::to9p_map(address_map &map)
{

	map(0x0000, 0x3fff).bankr(THOM_CART_BANK).w(FUNC(to8_state::to8_cartridge_w)); /* 4 * 16 KB */
	map(0x4000, 0x5fff).bankr(THOM_VRAM_BANK).w(FUNC(to8_state::to770_vram_w));
	map(0x6000, 0x7fff).bankr(TO8_SYS_LO).w(FUNC(to8_state::to8_sys_lo_w));
	map(0x8000, 0x9fff).bankr(TO8_SYS_HI).w(FUNC(to8_state::to8_sys_hi_w));
	map(0xa000, 0xbfff).bankr(TO8_DATA_LO).w(FUNC(to8_state::to8_data_lo_w));
	map(0xc000, 0xdfff).bankr(TO8_DATA_HI).w(FUNC(to8_state::to8_data_hi_w));
	map(0xe000, 0xffff).bankr(TO8_BIOS_BANK);
	map(0xe7c0, 0xe7ff).unmaprw();
	map(0xe7c0, 0xe7c7).rw(m_mc6846, FUNC(mc6846_device::read), FUNC(mc6846_device::write));
	map(0xe7c8, 0xe7cb).rw("pia_0", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
	map(0xe7cc, 0xe7cf).rw("pia_1", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
	map(0xe7d0, 0xe7d7).m("thmfc1", FUNC(thmfc1_device::map));
	map(0xe7da, 0xe7dd).rw(FUNC(to8_state::to8_vreg_r), FUNC(to8_state::to8_vreg_w));
	map(0xe7de, 0xe7df).rw(m_to9_kbd, FUNC(to9_keyboard_device::kbd_acia_r), FUNC(to9_keyboard_device::kbd_acia_w));
	map(0xe7e4, 0xe7e7).rw(m_video, FUNC(to8_video_device::gatearray_r), FUNC(to8_video_device::gatearray_w));
/*  map(0xe7f0, 0xe7f7).rw(FUNC(to8_state::to9_ieee_r), FUNC(to8_state::to9_ieee_w )); */

/* 0x10000 - 0x1ffff: 64 KB external ROM cartridge */
/* 0x20000 - 0x2ffff: 64 KB internal software ROM */
/* 0x30000 - 0x33fff: 16 KB BIOS ROM */
/* 18 KB external floppy / network ROM controllers */

/* RAM mapping: 512 KB flat (including video) */

}


/* ------------ ROMS ------------ */

ROM_START ( to9p )
		/* BIOS & floppy */
	ROM_REGION ( 0x4000, "monitor", 0 )
	ROM_LOAD ( "monitor.iw12", 0x0000, 0x4000, CRC(9e007126) SHA1(bd37a8099f5015c27fb49682559e68fddd532ddc) )

		/* BASIC */
	ROM_REGION ( 0x10000, "basic", 0 )
	ROM_LOAD ( "basic512.iw13", 0x0000, 0x8000, CRC(f45e3592) SHA1(8fd98973bd33f88fb63278a7fba86329076b473f) )
	ROM_LOAD ( "basic1.iw14",   0x8000, 0x8000, CRC(31f44ec6) SHA1(f7cf04d6560ea207672a6b611a5af5bac8ba3e13) )

	ROM_REGION ( 0x10000, "cartridge", 0 )
	ROM_FILL( 0x00000, 0x10000, 0x39 )
ROM_END


/* ------------ inputs   ------------ */

static INPUT_PORTS_START ( to9p )
	PORT_INCLUDE ( thom_lightpen )
	PORT_INCLUDE ( thom_game_port )
	PORT_INCLUDE ( to7_config )
INPUT_PORTS_END

/* ------------ driver ------------ */

void to8_state::to9p(machine_config &config)
{
	to7_base(config, false);
	MCFG_MACHINE_START_OVERRIDE( to8_state, to9p )
	MCFG_MACHINE_RESET_OVERRIDE( to8_state, to8 )

	m_maincpu->set_addrmap(AS_PROGRAM, &to8_state::to9p_map);

	TO9P_KEYBOARD(config, m_to9_kbd);
	m_to9_kbd->irq_cb().set(m_mainirq, FUNC(input_merger_device::in_w<4>));

	m_pia_sys->readpa_handler().set(FUNC(to8_state::to9p_sys_porta_in));
	m_pia_sys->readpb_handler().set_constant(0);
	m_pia_sys->writepa_handler().set(FUNC(to8_state::to8_sys_porta_out));
	m_pia_sys->writepb_handler().set(FUNC(to8_state::to8_sys_portb_out));
	m_pia_sys->cb2_handler().set_nop();
	m_pia_sys->irqa_handler().set_nop();
	m_pia_sys->irqb_handler().set("mainfirq", FUNC(input_merger_device::in_w<1>));

	THMFC1(config, "thmfc1", 16_MHz_XTAL);
	FLOPPY_CONNECTOR(config, "thmfc1:0", to8_floppy_drives, "dd90_352", to35_floppy_formats, true).enable_sound(true);
	FLOPPY_CONNECTOR(config, "thmfc1:1", to8_floppy_drives, nullptr,    to35_floppy_formats, false).enable_sound(true);

	m_extension->option_remove("cd90_015");
	m_extension->option_remove("cq90_028");
	m_extension->option_remove("cd90_351");
	m_extension->option_remove("cd90_640");
	m_extension->option_remove("nanoreseau");

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(to8_state::write_centronics_busy));

	m_mc6846->out_port().set(FUNC(to8_state::to9p_timer_port_out));
	m_mc6846->in_port().set(FUNC(to8_state::to9p_timer_port_in));
	m_mc6846->cp2().set(FUNC(to8_state::to8_timer_cp2_out));

	/* internal ram */
	m_ram->set_default_size("512K");

	TO8_VIDEO(config, m_video, 16_MHz_XTAL);
	m_video->set_screen("screen");
	m_video->set_lightpen_decal(TO8_LIGHTPEN_DECAL);
	m_video->set_lightpen_steps(4);
	m_video->set_vram_page_cb(FUNC(to8_state::get_vram_page));
	m_video->set_update_ram_bank_cb(FUNC(to8_state::to8_update_ram_bank));
	m_video->set_update_cart_bank_cb(FUNC(to8_state::to8_update_cart_bank));
	m_video->lightpen_intr_cb().set(m_mainfirq, FUNC(input_merger_device::in_w<2>));

	SOFTWARE_LIST(config, "to8_cass_list").set_original("to8_cass");
	SOFTWARE_LIST(config, "to8_qd_list").set_original("to8_qd");
	SOFTWARE_LIST(config.replace(), "to7_cass_list").set_compatible("to7_cass");
	SOFTWARE_LIST(config.replace(), "to7_qd_list").set_compatible("to7_qd");
}

COMP( 1986, to9p, 0, 0, to9p, to9p, to8_state, empty_init, "Thomson", "TO9+", 0 )



/******************************** MO6 ********************************

MO6 (1986)
---

The MO6 is the (long awaited) successor to the MO5.
It is based on TO8 technology (same system & video gate-array).
However, it is lower-end and cheaper: less memory (128 KB RAM, not
extensible), no floppy controller but an integrated cassette recorder.
The MO6 is MO5 compatible, but not compatible with the TO family.

* chips:
  - 1 MHz Motorola 6809E CPU
  - 2 Motorola 6821 PIAs (system, game)
  - 1 gate-array (system & video, identical to the TO8)

* memory:
  - 128 KB RAM (not extendable)
  - 8 KB BIOS ROM
  - 24 KB BASIC 1 ROM
  - 32 KB BASIC 128 & extended BIOS ROM

* video:
  all modes from the TO8, but the TO7/70-compatible mode is replaced with
  an MO5-compatible one

* devices:
  - AZERTY keyboard, 69 keys, no keyboard controller (scanning is done
    periodically by the 6809)
  - MO5-compatible cartridge
  - two-button mouse (TO8-like)
  - optional lightpen
  - integrated game port (similar to SX 90-018)
    . 6-bit DAC sound
    . two 8-position 2-button game pads
    . two-button mouse
  - integrated cassette reader 1200 bauds (MO5 compatible) and 2400 bauds
  - parallel CENTRONICS (printer emulated)
  - RF 57-932: RS232 extension (identical to the TO7), or RF 90-932 (???)
  - IEEE extension ?
  - no integrated controller, but external TO7 floppy controllers & drives
    are possible
  - speech synthesis extension: identical to TO7 ?
  - MIDIPAK MIDI extension: identical to TO7 ?


Olivetti Prodest PC 128 (1986)
-----------------------

Italian version of the MO6, built by Thomson and sold by Olivetti.
Except from the ROM, it is very similar to the MO6.
Do not confuse with the Olivetti Prodest PC 128 Systema (or 128s) which is
based on the Acorn BBC Master Compact. Or with the Olivetti PC 1, which is
a PC XT.


**********************************************************************/

void mo6_state::mo6_map(address_map &map)
{
	map(0x0000, 0x1fff).bankr(THOM_VRAM_BANK).w(FUNC(mo6_state::to770_vram_w));
	map(0x2000, 0x3fff).bankr(TO8_SYS_LO).w(FUNC(mo6_state::to8_sys_lo_w));
	map(0x4000, 0x5fff).bankr(TO8_SYS_HI).w(FUNC(mo6_state::to8_sys_hi_w));
	map(0x6000, 0x7fff).bankr(TO8_DATA_LO).w(FUNC(mo6_state::to8_data_lo_w));
	map(0x8000, 0x9fff).bankr(TO8_DATA_HI).w(FUNC(mo6_state::to8_data_hi_w));
	map(0xa7c0, 0xa7c3).rw("pia_0", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
	map(0xa7cb, 0xa7cb).w(FUNC(mo6_state::mo6_ext_w));
	map(0xa7cc, 0xa7cf).rw("pia_1", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
	map(0xa7da, 0xa7dd).rw(FUNC(mo6_state::mo6_vreg_r), FUNC(mo6_state::mo6_vreg_w));
	map(0xa7e4, 0xa7e7).rw(m_video, FUNC(to8_video_device::gatearray_r), FUNC(to8_video_device::gatearray_w));
/*  map(0xa7f0, 0xa7f7).rw(FUNC(mo6_state::to9_ieee_r), FUNC(homson_state::to9_ieee_w));*/
	map(0xb000, 0xbfff).bankr(MO6_CART_LO).w(FUNC(mo6_state::mo6_cartridge_w));
	map(0xc000, 0xefff).bankr(MO6_CART_HI).w(FUNC(mo6_state::mo6_cartridge_w));
	map(0xf000, 0xffff).bankr(TO8_BIOS_BANK);

/* 0x10000 - 0x1ffff: 64 KB external ROM cartridge */
/* 0x20000 - 0x2ffff: 64 KB BIOS ROM */
/* 16 KB floppy / network ROM controllers */

/* RAM mapping: 128 KB flat (including video) */

}


/* ------------ ROMS ------------ */

ROM_START ( mo6 )
		/* BASIC & BIOS */
	ROM_REGION ( 0x10000, "basic", 0 )
	ROM_LOAD ( "basic1.iw01",   0x0000, 0x8000, CRC(e04c98fc) SHA1(55a9c91a4da0ce455bf0402e6a86e8abdb3c93a0) )
	ROM_LOAD ( "basic128.iw02", 0x8000, 0x8000, CRC(f523ba0e) SHA1(e747a5310d5c137918e033e67b2dd83d12ec75c1) )

	ROM_REGION ( 0x10000, "cartridge", 0 )
	ROM_FILL ( 0x00000, 0x10000, 0x39 )
ROM_END

ROM_START ( pro128 )
		/* BASIC & BIOS */
	ROM_REGION ( 0x10000, "basic", 0 )
	ROM_LOAD ( "pro128.iw01", 0x0000, 0x8000, CRC(c5896603) SHA1(f0410456de778e650db7130f45e05fcd5bfd2024) )
	ROM_LOAD ( "basico.iw02", 0x8000, 0x8000, CRC(7c9a0174) SHA1(65f85edece4a88f3b5d5ed1f83df180705fa3d20) )

	ROM_REGION ( 0x10000, "cartridge", 0 )
	ROM_FILL ( 0x00000, 0x10000, 0x39 )
ROM_END


/* ------------ inputs   ------------ */

static INPUT_PORTS_START ( mo6_keyboard )

	PORT_START ( "keyboard.0" )
		KEY ( 0, "N", N )                   PORT_CHAR('N')
		KEY ( 1, ", ?", COMMA )             PORT_CHAR(',') PORT_CHAR('?')
		KEY ( 2, "; .", STOP )              PORT_CHAR(';') PORT_CHAR('.')
		KEY ( 3, "# @", TILDE )             PORT_CHAR('#') PORT_CHAR('@')
		KEY ( 4, "Space", SPACE )           PORT_CHAR(' ')
		KEY ( 5, "X", X )                   PORT_CHAR('X')
		KEY ( 6, "W", W )                   PORT_CHAR('W')
		KEY ( 7, "Shift", LSHIFT ) PORT_CODE ( KEYCODE_RSHIFT ) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_START ( "keyboard.1" )
		KEY ( 0, "Delete Backspace", DEL )  PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(BACKSPACE))
		KEY ( 1, "Insert", INSERT )         PORT_CHAR(UCHAR_MAMEKEY(INSERT))
		KEY ( 2, "> <", BACKSLASH2 )        PORT_CHAR('>') PORT_CHAR('<')
		KEY ( 3, UTF8_RIGHT, RIGHT )    PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
		KEY ( 4, UTF8_DOWN, DOWN )     PORT_CHAR(UCHAR_MAMEKEY(DOWN))
		KEY ( 5, UTF8_LEFT, LEFT )     PORT_CHAR(UCHAR_MAMEKEY(LEFT))
		KEY ( 6, UTF8_UP, UP )       PORT_CHAR(UCHAR_MAMEKEY(UP))
		KEY ( 7, "BASIC", RCONTROL )        PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))
	PORT_START ( "keyboard.2" )
		KEY ( 0, "J", J )                   PORT_CHAR('J')
		KEY ( 1, "K", K )                   PORT_CHAR('K')
		KEY ( 2, "L", L )                   PORT_CHAR('L')
		KEY ( 3, "M", M )                   PORT_CHAR('M')
		KEY ( 4, "B \302\264", B )          PORT_CHAR('B')
		KEY ( 5, "V", V )                   PORT_CHAR('V')
		KEY ( 6, "C \136", C )              PORT_CHAR('C')
		KEY ( 7, "Caps-Lock", CAPSLOCK )    PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_START ( "keyboard.3" )
		KEY ( 0, "H \302\250", H )          PORT_CHAR('H')
		KEY ( 1, "G", G )                   PORT_CHAR('G')
		KEY ( 2, "F", F )                   PORT_CHAR('F')
		KEY ( 3, "D", D )                   PORT_CHAR('D')
		KEY ( 4, "S", S )                   PORT_CHAR('S')
		KEY ( 5, "Q", Q )                   PORT_CHAR('Q')
		KEY ( 6, "Home Clear", HOME )       PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CHAR(UCHAR_MAMEKEY(ESC))
		KEY ( 7, "F1 F6", F1 )              PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_START ( "keyboard.4" )
		KEY ( 0, "U", U )                   PORT_CHAR('U')
		KEY ( 1, "I", I )                   PORT_CHAR('I')
		KEY ( 2, "O", O )                   PORT_CHAR('O')
		KEY ( 3, "P", P )                   PORT_CHAR('P')
		KEY ( 4, ": /", SLASH )             PORT_CHAR(':') PORT_CHAR('/')
		KEY ( 5, "$ &", CLOSEBRACE )        PORT_CHAR('$') PORT_CHAR('&')
		KEY ( 6, "Enter", ENTER )           PORT_CHAR(13)
		KEY ( 7, "F2 F7", F2 )              PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_START ( "keyboard.5" )
		KEY ( 0, "Y", Y )                   PORT_CHAR('Y')
		KEY ( 1, "T", T )                   PORT_CHAR('T')
		KEY ( 2, "R", R )                   PORT_CHAR('R')
		KEY ( 3, "E", E )                   PORT_CHAR('E')
		KEY ( 4, "Z", Z )                   PORT_CHAR('Z')
		KEY ( 5, "A \140", A )              PORT_CHAR('A')
		KEY ( 6, "Control", LCONTROL )      PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
		KEY ( 7, "F3 F8", F3 )              PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_START ( "keyboard.6" )
		KEY ( 0, "7 \303\250", 7 )          PORT_CHAR('7') PORT_CHAR( 0xe8 )
		KEY ( 1, "8 !", 8 )                 PORT_CHAR('8') PORT_CHAR('!')
		KEY ( 2, "9 \303\247", 9 )          PORT_CHAR('9') PORT_CHAR( 0xe7 )
		KEY ( 3, "0 \303\240", 0 )          PORT_CHAR('0') PORT_CHAR( 0xe0 )
		KEY ( 4, "- \\", BACKSPACE )        PORT_CHAR('-') PORT_CHAR('\\')
		KEY ( 5, "= +", EQUALS )            PORT_CHAR('=') PORT_CHAR('+')
		KEY ( 6, "Accent", END )            PORT_CHAR(UCHAR_MAMEKEY(END))
		KEY ( 7, "F4 F9", F4 )              PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_START ( "keyboard.7" )
		KEY ( 0, "6 _", 6 )                 PORT_CHAR('6') PORT_CHAR('_')
		KEY ( 1, "5 (", 5 )                 PORT_CHAR('5') PORT_CHAR('(')
		KEY ( 2, "4 '", 4 )                 PORT_CHAR('4') PORT_CHAR('\'')
		KEY ( 3, "3 \"", 3 )                PORT_CHAR('3') PORT_CHAR('"')
		KEY ( 4, "2 \303\251", 2 )          PORT_CHAR('2')  PORT_CHAR( 0xe9 )
		KEY ( 5, "1 *", 1 )                 PORT_CHAR('1') PORT_CHAR('*')
		KEY ( 6, "Stop", TAB )              PORT_CHAR(27)
		KEY ( 7, "F5 F10", F5 )             PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_START ( "keyboard.8" )
		KEY ( 0, "[ {", QUOTE )             PORT_CHAR('[') PORT_CHAR('{')
		KEY ( 1, "] }", BACKSLASH )         PORT_CHAR(']') PORT_CHAR('}')
		KEY ( 2, ") \302\260", MINUS )      PORT_CHAR(')') PORT_CHAR( 0xb0 )
		KEY ( 3, "\342\206\221 \302\250", OPENBRACE ) PORT_CHAR('^') PORT_CHAR( 0xa8 )
		KEY ( 4, "\303\271 %", COLON )      PORT_CHAR( 0xf9 ) PORT_CHAR('%')
	PORT_BIT  ( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

INPUT_PORTS_END

/* QWERTY version */
static INPUT_PORTS_START ( pro128_keyboard )
	PORT_INCLUDE ( mo6_keyboard )

	PORT_MODIFY ( "keyboard.0" )
		KEY ( 1, "M", M )                     PORT_CHAR('M')
		KEY ( 2, ", ;", COMMA )               PORT_CHAR(',') PORT_CHAR(';')
		KEY ( 3, "[ {", QUOTE  )              PORT_CHAR('[') PORT_CHAR('{')
		KEY ( 6, "Z", Z )                     PORT_CHAR('Z')
		KEY ( 7, "Shift", LSHIFT ) PORT_CODE ( KEYCODE_RSHIFT ) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_MODIFY ( "keyboard.1" )
		KEY ( 2, "- _", MINUS )               PORT_CHAR('-') PORT_CHAR('_')
	PORT_MODIFY ( "keyboard.2" )
		KEY ( 3, "\303\221", TILDE )          PORT_CHAR( 0xd1 )
	PORT_MODIFY ( "keyboard.3" )
		KEY ( 5, "A \140", A )                PORT_CHAR('A')
	PORT_MODIFY ( "keyboard.4" )
		KEY ( 4, ". :", STOP )                PORT_CHAR('.') PORT_CHAR(':')
		KEY ( 5, "+ *", BACKSPACE )           PORT_CHAR('+') PORT_CHAR('*')
	PORT_MODIFY ( "keyboard.5" )
		KEY ( 4, "W", W )                     PORT_CHAR('W')
		KEY ( 5, "Q", Q )                     PORT_CHAR('Q')
	PORT_MODIFY ( "keyboard.6" )
		KEY ( 0, "7 /", 7 )                   PORT_CHAR('7') PORT_CHAR('/')
		KEY ( 1, "8 (", 8 )                   PORT_CHAR('8') PORT_CHAR('(')
		KEY ( 2, "9 )", 9 )                   PORT_CHAR('9') PORT_CHAR(')')
		KEY ( 3, "0 =", 0 )                   PORT_CHAR('0') PORT_CHAR('=')
		KEY ( 4, "' \302\243", CLOSEBRACE )   PORT_CHAR('\'') PORT_CHAR( 0xa3 )
		KEY ( 5, "] }", BACKSLASH )           PORT_CHAR(']') PORT_CHAR('}')
	PORT_MODIFY ( "keyboard.7" )
		KEY ( 0, "6 &", 6 )                   PORT_CHAR('6') PORT_CHAR('&')
		KEY ( 1, "5 %", 5 )                   PORT_CHAR('5') PORT_CHAR('%')
		KEY ( 2, "4 $", 4 )                   PORT_CHAR('4') PORT_CHAR('$')
		KEY ( 3, "3 \302\247", 3 )            PORT_CHAR('3') PORT_CHAR( 0xa7 )
		KEY ( 4, "2 \"", 2 )                  PORT_CHAR('2') PORT_CHAR('"')
		KEY ( 5, "1 !", 1 )                   PORT_CHAR('1') PORT_CHAR('!')
	PORT_MODIFY ( "keyboard.8" )
		KEY ( 0, "> <", BACKSLASH2 )          PORT_CHAR('>') PORT_CHAR('<')
		KEY ( 1, "# \342\206\221", EQUALS )   PORT_CHAR('#') PORT_CHAR('^')
		KEY ( 2, "\303\247 ?", COLON )        PORT_CHAR( 0xe7 ) PORT_CHAR('?')
		KEY ( 3, "\302\277 @", SLASH )        PORT_CHAR( 0xbf ) PORT_CHAR('@')
		KEY ( 4, "\302\241 \302\250", OPENBRACE )  PORT_CHAR( 0xa1 ) PORT_CHAR( 0xa8 )

INPUT_PORTS_END


static INPUT_PORTS_START ( mo6 )
	PORT_INCLUDE ( thom_lightpen )
	PORT_INCLUDE ( thom_game_port )
	PORT_INCLUDE ( mo6_keyboard )
	PORT_INCLUDE ( to7_config )
INPUT_PORTS_END

static INPUT_PORTS_START ( pro128 )
	PORT_INCLUDE ( thom_lightpen )
	PORT_INCLUDE ( thom_game_port )
	PORT_INCLUDE ( pro128_keyboard )
	PORT_INCLUDE ( to7_config )
INPUT_PORTS_END


/* ------------ driver ------------ */

void mo6_state::mo6(machine_config &config)
{
	to7_base(config, true);
	MCFG_MACHINE_START_OVERRIDE( mo6_state, mo6 )
	MCFG_MACHINE_RESET_OVERRIDE( mo6_state, mo6 )

	m_maincpu->set_addrmap(AS_PROGRAM, &mo6_state::mo6_map);

	m_cassette->set_formats(mo5_cassette_formats);
	m_cassette->set_interface("mo_cass");

	m_pia_sys->readpa_handler().set(FUNC(mo6_state::mo6_sys_porta_in));
	m_pia_sys->readpb_handler().set(FUNC(mo6_state::mo6_sys_portb_in));
	m_pia_sys->writepa_handler().set(FUNC(mo6_state::mo6_sys_porta_out));
	m_pia_sys->writepb_handler().set("buzzer", FUNC(dac_bit_interface::data_w));
	m_pia_sys->ca2_handler().set(FUNC(mo6_state::mo5_set_cassette_motor));
	m_pia_sys->cb2_handler().set(FUNC(mo6_state::mo6_sys_cb2_out));
	m_pia_sys->irqb_handler().set("mainirq", FUNC(input_merger_device::in_w<0>)); // differs from TO

	m_pia_game->writepa_handler().set(FUNC(mo6_state::mo6_game_porta_out));
	m_pia_game->cb2_handler().set(FUNC(mo6_state::mo6_game_cb2_out));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(mo6_state::write_centronics_busy));

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	GENERIC_CARTSLOT(config.replace(), "cartslot", generic_plain_slot, "mo_cart", "m5,rom").set_device_load(FUNC(mo6_state::mo5_cartridge));

	/* internal ram */
	m_ram->set_default_size("128K");

	TO8_VIDEO(config, m_video, 16_MHz_XTAL);
	m_video->set_is_mo(true);
	m_video->set_screen("screen");
	m_video->set_lightpen_decal(MO6_LIGHTPEN_DECAL);
	m_video->set_lightpen_steps(3);
	m_video->set_vram_page_cb(FUNC(mo6_state::get_vram_page));
	m_video->set_update_ram_bank_cb(FUNC(mo6_state::mo6_update_ram_bank));
	m_video->set_update_cart_bank_cb(FUNC(mo6_state::mo6_update_cart_bank));
	m_video->lightpen_intr_cb().set(m_mainfirq, FUNC(input_merger_device::in_w<2>));
	m_video->int_50hz_cb().set(m_pia_sys, FUNC(pia6821_device::cb1_w));

	config.device_remove("to7_cart_list");
	config.device_remove("to7_cass_list");
	config.device_remove("to_flop_list");
	config.device_remove("to7_qd_list");

	SOFTWARE_LIST(config, "mo6_cass_list").set_original("mo6_cass");
	SOFTWARE_LIST(config, "mo6_flop_list").set_original("mo6_flop");

	SOFTWARE_LIST(config, "mo5_cart_list").set_compatible("mo5_cart");
	SOFTWARE_LIST(config, "mo5_cass_list").set_compatible("mo5_cass");
	SOFTWARE_LIST(config, "mo5_flop_list").set_compatible("mo5_flop");
	SOFTWARE_LIST(config, "mo5_qd_list").set_compatible("mo5_qd");
}

void mo6_state::pro128(machine_config &config)
{
	mo6(config);
	config.device_remove("mo6_cass_list");
	config.device_remove("mo6_flop_list");

	config.device_remove("mo5_cart_list");
	config.device_remove("mo5_cass_list");
	config.device_remove("mo5_flop_list");
	config.device_remove("mo5_qd_list");

	SOFTWARE_LIST(config, "p128_cart_list").set_original("pro128_cart");
	SOFTWARE_LIST(config, "p128_cass_list").set_original("pro128_cass");
	SOFTWARE_LIST(config, "p128_flop_list").set_original("pro128_flop");
}

COMP( 1986, mo6, 0, 0, mo6, mo6, mo6_state, empty_init, "Thomson", "MO6", 0 )

COMP( 1986, pro128, mo6, 0, pro128, pro128, mo6_state, empty_init, "Olivetti / Thomson", "Prodest PC 128", 0 )




/****************************** MO5NR ********************************

MO5 NR (1986)
------

Despite its name, the MO5 NR is much more related to the MO6 than to the MO5.
It can be though as the network-enhanced version of the MO6.
It is both MO5 and MO6 compatible (but not TO-compatible).

Here are the differences between the MO6 and MO5NR:

* chips:
  - integrated MC 6854 network controller

* memory:
  - extra 2 KB ROM for the integrated network controller,
    can be masked by the ROM from the external floppy controller

* video: identical

* devices:
  - AZERTY keyboard has only 58 keys, and no caps-lock led
  - CENTRONICS printer interface not built in (requires CC 90-232 extension)
  - MO5-compatible network (probably identical to NR 07-005 extension)
  - extern floppy controller & drive possible, masks the network

**********************************************************************/

void mo5nr_state::mo5nr_map(address_map &map)
{
	map(0x0000, 0x1fff).bankr(THOM_VRAM_BANK).w(FUNC(mo5nr_state::to770_vram_w));
	map(0x2000, 0x3fff).bankr(TO8_SYS_LO).w(FUNC(mo5nr_state::to8_sys_lo_w));
	map(0x4000, 0x5fff).bankr(TO8_SYS_HI).w(FUNC(mo5nr_state::to8_sys_hi_w));
	map(0x6000, 0x7fff).bankr(TO8_DATA_LO).w(FUNC(mo5nr_state::to8_data_lo_w));
	map(0x8000, 0x9fff).bankr(TO8_DATA_HI).w(FUNC(mo5nr_state::to8_data_hi_w));
	map(0xa000, 0xa7ff).view(m_extension_view);
	map(0xa7c0, 0xa7c3).rw("pia_0", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
	map(0xa7cb, 0xa7cb).w(FUNC(mo5nr_state::mo6_ext_w));
	map(0xa7cc, 0xa7cf).rw("pia_1", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
	m_extension_view[1](0xa7d8, 0xa7d9).r(FUNC(mo5nr_state::id_r));
	map(0xa7da, 0xa7dd).rw(FUNC(mo5nr_state::mo6_vreg_r), FUNC(mo5nr_state::mo6_vreg_w));
	map(0xa7e4, 0xa7e7).rw(m_video, FUNC(to8_video_device::gatearray_r), FUNC(to8_video_device::gatearray_w));
/*  map(0xa7f0, 0xa7f7).rw(FUNC(mo5nr_state::to9_ieee_r), FUNC(homson_state::to9_ieee_w));*/
	map(0xb000, 0xbfff).bankr(MO6_CART_LO).w(FUNC(mo5nr_state::mo6_cartridge_w));
	map(0xc000, 0xefff).bankr(MO6_CART_HI).w(FUNC(mo5nr_state::mo6_cartridge_w));
	map(0xf000, 0xffff).bankr(TO8_BIOS_BANK);

/* 0x10000 - 0x1ffff: 64 KB external ROM cartridge */
/* 0x20000 - 0x2ffff: 64 KB BIOS ROM */
/* 16 KB floppy / network ROM controllers */

/* RAM mapping: 128 KB flat (including video) */

}


/* ------------ ROMS ------------ */

ROM_START ( mo5nr )
		/* BASIC & BIOS */
	ROM_REGION ( 0x10000, "basic", 0 )
	ROM_LOAD ( "mo5nr.iw01",  0x0000, 0x8000, CRC(ade3c46d) SHA1(64bede6ecb58ad7409b2c546259773af097f162d) )
	ROM_LOAD ( "basicn.iw02", 0x8000, 0x8000, CRC(3a6981c3) SHA1(6d22e3f2ff2a19383401f950c5ace72e1560816c) )

	ROM_REGION ( 0x2000, "nr", 0 ) /* TODO: network ROM */
	ROM_LOAD ( "nr.iw20", 0x0000, 0x2000, NO_DUMP )

	ROM_REGION ( 0x100, "proms", 0 )
	ROM_LOAD ( "an-r.iw21", 0x000, 0x100, NO_DUMP ) // unknown purpose (N82S129AN)

	ROM_REGION ( 0x10000, "cartridge", 0 )
	ROM_FILL ( 0x00000, 0x10000, 0x39 )
ROM_END




/* ------------ inputs   ------------ */

static INPUT_PORTS_START ( mo5nr_keyboard )

	PORT_START ( "keyboard.0" )
		KEY ( 0, "N", N )                   PORT_CHAR('N')
		KEY ( 1, ", <", COMMA )             PORT_CHAR(',') PORT_CHAR('<')
		KEY ( 2, ". >", STOP )              PORT_CHAR('.') PORT_CHAR('>')
		KEY ( 3, "@ \342\206\221", TILDE )  PORT_CHAR('@') PORT_CHAR('^')
		KEY ( 4, "Space Caps-Lock", SPACE ) PORT_CHAR(' ') PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
		KEY ( 5, "X", X )                   PORT_CHAR('X')
		KEY ( 6, "W", W )                   PORT_CHAR('W')
		KEY ( 7, "Shift", LSHIFT ) PORT_CODE ( KEYCODE_RSHIFT ) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_START ( "keyboard.1" )
		KEY ( 0, "Delete Backspace", DEL )  PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(BACKSPACE))
		KEY ( 1, "Insert", INSERT )         PORT_CHAR(UCHAR_MAMEKEY(INSERT))
		KEY ( 2, "Home", HOME )             PORT_CHAR(UCHAR_MAMEKEY(HOME))
		KEY ( 3, UTF8_RIGHT, RIGHT )    PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
		KEY ( 4, UTF8_DOWN, DOWN )     PORT_CHAR(UCHAR_MAMEKEY(DOWN))
		KEY ( 5, UTF8_LEFT, LEFT )     PORT_CHAR(UCHAR_MAMEKEY(LEFT))
		KEY ( 6, UTF8_UP, UP )       PORT_CHAR(UCHAR_MAMEKEY(UP))
		KEY ( 7, "BASIC", RCONTROL )
	PORT_START ( "keyboard.2" )
		KEY ( 0, "J", J )                   PORT_CHAR('J')
		KEY ( 1, "K", K )                   PORT_CHAR('K')
		KEY ( 2, "L", L )                   PORT_CHAR('L')
		KEY ( 3, "M", M )                   PORT_CHAR('M')
		KEY ( 4, "B \140", B )              PORT_CHAR('B')
		KEY ( 5, "V", V )                   PORT_CHAR('V')
		KEY ( 6, "C \136", C )              PORT_CHAR('C')
	PORT_BIT  ( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_START ( "keyboard.3" )
		KEY ( 0, "H \302\250", H )          PORT_CHAR('H')
		KEY ( 1, "G", G )                   PORT_CHAR('G')
		KEY ( 2, "F", F )                   PORT_CHAR('F')
		KEY ( 3, "D", D )                   PORT_CHAR('D')
		KEY ( 4, "S", S )                   PORT_CHAR('S')
		KEY ( 5, "Q", Q )                   PORT_CHAR('Q')
		KEY ( 6, "Clear", ESC )             PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT  ( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_START ( "keyboard.4" )
		KEY ( 0, "U", U )                   PORT_CHAR('U')
		KEY ( 1, "I", I )                   PORT_CHAR('I')
		KEY ( 2, "O", O )                   PORT_CHAR('O')
		KEY ( 3, "P", P )                   PORT_CHAR('P')
		KEY ( 4, "/ ?", SLASH )             PORT_CHAR('/') PORT_CHAR('?')
		KEY ( 5, "* :", QUOTE )             PORT_CHAR('*') PORT_CHAR(':')
		KEY ( 6, "Enter", ENTER )           PORT_CHAR(13)
	PORT_BIT  ( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_START ( "keyboard.5" )
		KEY ( 0, "Y", Y )                   PORT_CHAR('Y')
		KEY ( 1, "T", T )                   PORT_CHAR('T')
		KEY ( 2, "R", R )                   PORT_CHAR('R')
		KEY ( 3, "E", E )                   PORT_CHAR('E')
		KEY ( 4, "Z", Z )                   PORT_CHAR('Z')
		KEY ( 5, "A \140", A )              PORT_CHAR('A')
		KEY ( 6, "Control", LCONTROL )      PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT  ( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_START ( "keyboard.6" )
		KEY ( 0, "7 ' \303\250", 7 )          PORT_CHAR('7') PORT_CHAR('\'' )
		KEY ( 1, "8 ( \303\271", 8 )          PORT_CHAR('8') PORT_CHAR('(')
		KEY ( 2, "9 ) \303\247", 9 )          PORT_CHAR('9') PORT_CHAR(')')
		KEY ( 3, "0 \303\240", 0 )            PORT_CHAR('0') PORT_CHAR( 0xe0 )
		KEY ( 4, "- =", MINUS )               PORT_CHAR('-') PORT_CHAR('=')
		KEY ( 5, "+ ;", EQUALS )              PORT_CHAR('+') PORT_CHAR(';')
		KEY ( 6, "Accent", END )              PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT  ( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_START ( "keyboard.7" )
		KEY ( 0, "6 & \303\251", 6 )          PORT_CHAR('6') PORT_CHAR('&')
		KEY ( 1, "5 %", 5 )                   PORT_CHAR('5') PORT_CHAR('%')
		KEY ( 2, "4 $", 4 )                   PORT_CHAR('4') PORT_CHAR('$')
		KEY ( 3, "3 #", 3 )                   PORT_CHAR('3') PORT_CHAR('#')
		KEY ( 4, "2 \"", 2 )                  PORT_CHAR('2') PORT_CHAR('"')
		KEY ( 5, "1 !", 1 )                   PORT_CHAR('1') PORT_CHAR('!')
		KEY ( 6, "Stop", TAB )                PORT_CHAR(27)
	PORT_BIT  ( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

INPUT_PORTS_END

static INPUT_PORTS_START ( mo5nr )
	PORT_INCLUDE ( thom_lightpen )
	PORT_INCLUDE ( thom_game_port )
	PORT_INCLUDE ( mo5nr_keyboard )
	PORT_INCLUDE ( to7_config )

	PORT_START ( "nanoreseau_config" )
	PORT_DIPNAME(0x01, 0x01, "Extension selection") PORT_DIPLOCATION("SW03:1")
	PORT_DIPSETTING(0x00, "Extension port")
	PORT_DIPSETTING(0x01, "Internal networking")

	PORT_DIPNAME(0x3e, 0x02, "Network ID")          PORT_DIPLOCATION("SW03:2,3,4,5,6")
	PORT_DIPSETTING(0x00, "0 (Master)")
	PORT_DIPSETTING(0x02, "1")
	PORT_DIPSETTING(0x04, "2")
	PORT_DIPSETTING(0x06, "3")
	PORT_DIPSETTING(0x08, "4")
	PORT_DIPSETTING(0x0a, "5")
	PORT_DIPSETTING(0x0c, "6")
	PORT_DIPSETTING(0x0e, "7")
	PORT_DIPSETTING(0x10, "8")
	PORT_DIPSETTING(0x12, "9")
	PORT_DIPSETTING(0x14, "10")
	PORT_DIPSETTING(0x16, "11")
	PORT_DIPSETTING(0x18, "12")
	PORT_DIPSETTING(0x1a, "13")
	PORT_DIPSETTING(0x1c, "14")
	PORT_DIPSETTING(0x1e, "15")
	PORT_DIPSETTING(0x20, "16")
	PORT_DIPSETTING(0x22, "17")
	PORT_DIPSETTING(0x24, "18")
	PORT_DIPSETTING(0x26, "19")
	PORT_DIPSETTING(0x28, "20")
	PORT_DIPSETTING(0x2a, "21")
	PORT_DIPSETTING(0x2c, "22")
	PORT_DIPSETTING(0x2e, "23")
	PORT_DIPSETTING(0x30, "24")
	PORT_DIPSETTING(0x32, "25")
	PORT_DIPSETTING(0x34, "26")
	PORT_DIPSETTING(0x36, "27")
	PORT_DIPSETTING(0x38, "28")
	PORT_DIPSETTING(0x3a, "29")
	PORT_DIPSETTING(0x3c, "30")
	PORT_DIPSETTING(0x3e, "31")
INPUT_PORTS_END


/* ------------ driver ------------ */

void mo5nr_state::mo5nr(machine_config &config)
{
	to7_base(config, true);
	MCFG_MACHINE_START_OVERRIDE( mo5nr_state, mo5nr )
	MCFG_MACHINE_RESET_OVERRIDE( mo5nr_state, mo5nr )

	m_maincpu->set_addrmap(AS_PROGRAM, &mo5nr_state::mo5nr_map);

	m_cassette->set_formats(mo5_cassette_formats);
	m_cassette->set_interface("mo_cass");

	m_pia_sys->readpa_handler().set(FUNC(mo5nr_state::mo6_sys_porta_in));
	m_pia_sys->readpb_handler().set(FUNC(mo5nr_state::mo5nr_sys_portb_in));
	m_pia_sys->writepa_handler().set(FUNC(mo5nr_state::mo5nr_sys_porta_out));
	m_pia_sys->writepb_handler().set("buzzer", FUNC(dac_bit_interface::data_w));
	m_pia_sys->ca2_handler().set(FUNC(mo5nr_state::mo5_set_cassette_motor));
	m_pia_sys->cb2_handler().set(FUNC(mo5nr_state::mo6_sys_cb2_out));
	m_pia_sys->irqb_handler().set("mainirq", FUNC(input_merger_device::in_w<0>)); // differs from TO

	GENERIC_CARTSLOT(config.replace(), "cartslot", generic_plain_slot, "mo_cart", "m5,rom").set_device_load(FUNC(mo5nr_state::mo5_cartridge));

	NANORESEAU_MO(config, m_nanoreseau, 0, true);

	m_extension->option_remove("nanoreseau");

	/* internal ram */
	m_ram->set_default_size("128K");

	TO8_VIDEO(config, m_video, 16_MHz_XTAL);
	m_video->set_is_mo(true);
	m_video->set_screen("screen");
	m_video->set_lightpen_decal(MO6_LIGHTPEN_DECAL);
	m_video->set_lightpen_steps(3);
	m_video->set_vram_page_cb(FUNC(mo5nr_state::get_vram_page));
	m_video->set_update_ram_bank_cb(FUNC(mo5nr_state::mo6_update_ram_bank));
	m_video->set_update_cart_bank_cb(FUNC(mo5nr_state::mo6_update_cart_bank));
	m_video->lightpen_intr_cb().set(m_mainfirq, FUNC(input_merger_device::in_w<2>));
	m_video->int_50hz_cb().set(m_pia_sys, FUNC(pia6821_device::cb1_w));

	config.device_remove("to7_cart_list");
	config.device_remove("to7_cass_list");
	config.device_remove("to_flop_list");
	config.device_remove("to7_qd_list");

	SOFTWARE_LIST(config, "mo6_cass_list").set_original("mo6_cass");
	SOFTWARE_LIST(config, "mo6_flop_list").set_original("mo6_flop");

	SOFTWARE_LIST(config, "mo5_cart_list").set_compatible("mo5_cart");
	SOFTWARE_LIST(config, "mo5_cass_list").set_compatible("mo5_cass");
	SOFTWARE_LIST(config, "mo5_flop_list").set_compatible("mo5_flop");
	SOFTWARE_LIST(config, "mo5_qd_list").set_compatible("mo5_qd");
}

COMP( 1986, mo5nr, 0, 0, mo5nr, mo5nr, mo5nr_state, empty_init, "Thomson", "MO5 NR", 0 )



ti630.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders: Felipe Sanches
/***************************************************************************

  Intelbras TI630 telephone
  Driver by Felipe Correa da Silva Sanches <juca@members.fsf.org>

  http://images.quebarato.com.br/T440x/telefone+ks+ti+630+seminovo+intelbras+sao+paulo+sp+brasil__2E255D_1.jpg

  Changelog:

   2014 JUN 17 [Felipe Sanches]:
   * Initial driver skeleton
   * LCD works

================
    Messages displayed on screen are in brazilian portuguese.
    During boot, it says:

"TI auto-test."
"Wait!"

    Then it says:

"Initializing..."
"Wait!"

    And finally:

"TI did not receive"
"the dial tone"

It means we probably would have to emulate a modem device for it to treat communications with a PABX phone hub.
================
*/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"

#define LOG_IO_PORTS (1U << 1)

#define VERBOSE (0)
#include "logmacro.h"


namespace {

class ti630_state : public driver_device
{
public:
	ti630_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "hd44780")
	{ }

	void ti630(machine_config &config);

	void init_ti630();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void i80c31_p1_w(uint8_t data);
	void i80c31_p3_w(uint8_t data);
	uint8_t i80c31_p1_r();
	void ti630_palette(palette_device &palette) const;
	void i80c31_io(address_map &map) ATTR_COLD;
	void i80c31_prg(address_map &map) ATTR_COLD;

	required_device<i80c31_device> m_maincpu;
	required_device<hd44780_device> m_lcdc;
};

void ti630_state::i80c31_prg(address_map &map)
{
	map(0x0000, 0xffff).rom();
}

void ti630_state::init_ti630()
{
}

void ti630_state::i80c31_io(address_map &map)
{
	map(0x0000, 0x0000) /*.mirror(?)*/ .w(m_lcdc, FUNC(hd44780_device::control_w));
	map(0x1000, 0x1000) /*.mirror(?)*/ .w(m_lcdc, FUNC(hd44780_device::data_w));
	map(0x2000, 0x2000) /*.mirror(?)*/ .r(m_lcdc, FUNC(hd44780_device::control_r));
	map(0x8000, 0xffff).ram(); /*TODO: verify the ammont of RAM and the correct address range to which it is mapped. This is just a first reasonable guess that apparently yields good results in the emulation */
}

void ti630_state::machine_start()
{
}

void ti630_state::machine_reset()
{
}

uint8_t ti630_state::i80c31_p1_r()
{
	uint8_t value = 0;
	LOGMASKED(LOG_IO_PORTS, "P1 read value:%02X\n", value);
	return value;
}

void ti630_state::i80c31_p1_w(uint8_t data)
{
	LOGMASKED(LOG_IO_PORTS, "Write to P1: %02X\n", data);
}

void ti630_state::i80c31_p3_w(uint8_t data)
{
	LOGMASKED(LOG_IO_PORTS, "Write to P3: %02X\n", data);
}

void ti630_state::ti630_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}

static const gfx_layout ti630_charlayout =
{
	5, 8,                   /* 5 x 8 characters */
	256,                    /* 256 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	{ 3, 4, 5, 6, 7},
	{ 0, 8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8},
	8*8                     /* 8 bytes */
};

static GFXDECODE_START( gfx_ti630 )
	GFXDECODE_ENTRY( "hd44780:cgrom", 0x0000, ti630_charlayout, 0, 1 )
GFXDECODE_END

void ti630_state::ti630(machine_config &config)
{
	/* basic machine hardware */
	I80C31(config, m_maincpu, XTAL(10'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &ti630_state::i80c31_prg);
	m_maincpu->set_addrmap(AS_IO, &ti630_state::i80c31_io);
	m_maincpu->port_in_cb<1>().set(FUNC(ti630_state::i80c31_p1_r));
	m_maincpu->port_out_cb<1>().set(FUNC(ti630_state::i80c31_p1_w));
	m_maincpu->port_out_cb<3>().set(FUNC(ti630_state::i80c31_p3_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(m_lcdc, FUNC(hd44780_device::screen_update));
	screen.set_size(6*16, 9*2);
	screen.set_visarea(0, 6*16-1, 0, 9*2-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(ti630_state::ti630_palette), 2);
	GFXDECODE(config, "gfxdecode", "palette", gfx_ti630);

	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 16);
}

ROM_START( ti630 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "ti630.ci11",  0x00000, 0x10000, CRC(2602cbdc) SHA1(98266bea52a5893e0af0b5872eca0a0a1e0c5f9c) )
ROM_END

} // anonymous namespace


//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY      FULLNAME           FLAGS
COMP( 1999, ti630, 0,      0,      ti630,   0,     ti630_state, init_ti630, "Intelbras", "TI630 telephone", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND )



ti74.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
/*******************************************************************************

Texas Instruments TI-74 BASICALC
Texas Instruments TI-95 PROCALC
hardware family: CC-40 -> TI-74 BASICALC -> TI-95 PROCALC

TI-74 PCB layout:
note: TI-95 PCB is nearly the same, just with a different size LCD screen,
its CPU is labeled C70011, and the system ROM is labeled HN61256PC95.

        DOCK-BUS
      --||||||||---
  C  ==           |
  a  ==           |
  r  ==  HN61256  |
  t  ==           ----------------------------
      |                                      |
-------            C70009          4MHz      |
|        HM6264                    RC4193N   |
|                                            |
|                                            |
|                                            |
|                                            |
---------------||||||||||||||||||||||||-------
               ||||||||||||||||||||||||
---------------||||||||||||||||||||||||-------
|              *HD44100H   *HD44780A00       |
|                                            |
|                                            |
|                                            |
|                                            |
----------                                   |
         | ----------------------------------|
         | |                                ||
         | |          LCD screen            ||
         | |                                ||
         | ----------------------------------|
         -------------------------------------

IC1 HN61256PC93 - Hitachi DIP-28 32KB CMOS Mask PROM
IC2 C70009      - Texas Instruments TMS70C46, 54 pins. Basically a TMS70C40 with some TI custom I/O mods.
                  128 bytes internal RAM, 4KB internal ROM, running at max 4MHz.
IC3 HM6264LP-15 - Hitachi 8KB SRAM (battery backed)
RC4193N         - Micropower Switching Regulator
HD44100H        - 60-pin QFP Hitachi HD44100 LCD Driver
HD44780A00      - 80-pin TFP Hitachi HD44780 LCD Controller

*               - indicates that it's on the other side of the PCB


Overall, the hardware is very similar to TI CC-40. A lot has been shuffled around
to cut down on complexity. To reduce power usage even more, the OS often idles while
waiting for any keypress that triggers an interrupt and wakes the processor up.

The machine is powered by 4 AAA batteries. These will also save internal RAM,
provided that the machine is turned off properly.


TODO:
- it runs too fast due to missing clock divider emulation in TMS70C46
- external ram cartridge (HM6264LFP-15 + coin battery)
- DOCK-BUS interface and peripherals, compatible with both TI-74 and TI-95
  * CI-7 cassette interface
  * PC-324 thermal printer
  (+ old Hexbus devices can be connected via a converter cable)
- verify ti74(d12) rom label

*******************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/tms7000/tms7000.h"
#include "machine/nvram.h"
#include "video/hd44780.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"

#include "ti74.lh"
#include "ti95.lh"


namespace {

class ti74_state : public driver_device
{
public:
	ti74_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_sysbank(*this, "sysbank"),
		m_cart(*this, "cartslot"),
		m_key_matrix(*this, "IN.%u", 0),
		m_battery_inp(*this, "BATTERY"),
		m_segs(*this, "seg%u", 0U)
	{ }

	void ti74(machine_config &config);
	void ti95(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(battery_status_changed);

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<tms70c46_device> m_maincpu;
	required_memory_bank m_sysbank;
	required_device<generic_slot_device> m_cart;
	required_ioport_array<8> m_key_matrix;
	required_ioport m_battery_inp;
	output_finder<80> m_segs;

	u8 m_key_select = 0;
	u8 m_power = 0;

	void update_lcd_indicator(u8 y, u8 x, int state);
	void update_battery_status(int state);

	u8 keyboard_r();
	void keyboard_w(u8 data);
	void bankswitch_w(u8 data);

	void ti74_palette(palette_device &palette) const;
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
	HD44780_PIXEL_UPDATE(ti74_pixel_update);
	HD44780_PIXEL_UPDATE(ti95_pixel_update);
	void main_map(address_map &map) ATTR_COLD;
};



/*******************************************************************************
    Initialisation
*******************************************************************************/

void ti74_state::machine_start()
{
	m_segs.resolve();

	if (m_cart->exists())
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x4000, 0xbfff, read8sm_delegate(*m_cart, FUNC(generic_slot_device::read_rom)));

	m_sysbank->configure_entries(0, 4, memregion("system")->base(), 0x2000);

	// register for savestates
	save_item(NAME(m_key_select));
	save_item(NAME(m_power));
}

void ti74_state::machine_reset()
{
	m_power = 1;

	m_sysbank->set_entry(0);
	update_battery_status(m_battery_inp->read());
}

void ti74_state::update_battery_status(int state)
{
	// battery ok/low status is on int1 line!
	m_maincpu->set_input_line(TMS7000_INT1_LINE, state ? ASSERT_LINE : CLEAR_LINE);
}



/*******************************************************************************
    Cartridge
*******************************************************************************/

DEVICE_IMAGE_LOAD_MEMBER(ti74_state::cart_load)
{
	u32 const size = m_cart->common_get_size("rom");

	// max size is 32KB
	if (size > 0x8000)
		return std::make_pair(image_error::INVALIDLENGTH, "Invalid file size (must be no more than 32K)");

	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}



/*******************************************************************************
    Video
*******************************************************************************/

void ti74_state::ti74_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148)); // background
	palette.set_pen_color(1, rgb_t(50, 45, 60)); // LCD pixel on
	palette.set_pen_color(2, rgb_t(131, 136, 139)); // LCD pixel off
}

void ti74_state::update_lcd_indicator(u8 y, u8 x, int state)
{
	// TI-74 ref._________________...
	// output#  |10     11     12     13     14      2      3      4
	// above    | <    SHIFT   CTL    FN     I/O    UCL    _LOW    >
	// ---- raw lcd screen here ----
	// under    |      BASIC   CALC   DEG    RAD    GRAD   STAT
	// output#  |       63     64      1     62     53     54
	//
	// TI-95 ref._________________...
	// output#  |  40   43     41   44   42     12  11  10/13/14  0    1    2
	// above    | _LOW _ERROR  2nd  INV  ALPHA  LC  INS  DEGRAD  HEX  OCT  I/O
	// screen-  | _P{70} <{71}                                             RUN{3}
	//   area   .                                                          SYS{4}
	m_segs[y * 10 + x] = state ? 1 : 0;
}

HD44780_PIXEL_UPDATE(ti74_state::ti74_pixel_update)
{
	// char size is 5x7 + cursor
	if (x > 4 || y > 7)
		return;

	if (line == 1 && pos == 15)
	{
		// the last char is used to control the 14 lcd indicators
		update_lcd_indicator(y, x, state);
	}
	else if (line < 2 && pos < 16)
	{
		// internal: 2*16, external: 1*31
		if (y == 7) y++; // the cursor is slightly below the character
		bitmap.pix(1 + y, 1 + line*16*6 + pos*6 + x) = state ? 1 : 2;
	}
}

HD44780_PIXEL_UPDATE(ti74_state::ti95_pixel_update)
{
	// char size is 5x7 + cursor
	if (x > 4 || y > 7)
		return;

	if (line == 1 && pos == 15)
	{
		// the last char is used to control the 17 lcd indicators
		update_lcd_indicator(y, x, state);
	}
	else if (line == 0 && pos < 16)
	{
		// 1st line is simply 16 chars
		if (y == 7) y++; // the cursor is slightly below the char
		bitmap.pix(10 + y, 1 + pos*6 + x) = state ? 1 : 2;
	}
	else if (line == 1 && pos < 15 && y < 7)
	{
		// 2nd line is segmented into 5 groups of 3 chars, there is no cursor
		// note: the chars are smaller than on the 1st line (this is handled in .lay file)
		const int gap = 9;
		int group = pos / 3;
		bitmap.pix(1 + y, 1 + group*gap + pos*6 + x) = state ? 1 : 2;
	}
}



/*******************************************************************************
    I/O
*******************************************************************************/

u8 ti74_state::keyboard_r()
{
	u8 data = 0;

	// read selected keyboard rows
	for (int i = 0; i < 8; i++)
	{
		if (m_key_select >> i & 1)
			data |= m_key_matrix[i]->read();
	}

	return data;
}

void ti74_state::keyboard_w(u8 data)
{
	// d0-d7: select keyboard column
	m_key_select = data;
}

void ti74_state::bankswitch_w(u8 data)
{
	// d0-d1: system rom bankswitch
	m_sysbank->set_entry(data & 3);

	// d2: power-on latch
	if (~data & 4 && m_power)
	{
		m_power = 0;
		m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); // stop running
	}

	// d3: N/C
}

void ti74_state::main_map(address_map &map)
{
	map.unmap_value_high();
	map(0x1000, 0x1001).rw("hd44780", FUNC(hd44780_device::read), FUNC(hd44780_device::write));
	map(0x2000, 0x3fff).ram().share("sysram.ic3");
	//map(0x4000, 0xbfff) // mapped by the cartslot
	map(0xc000, 0xdfff).bankr("sysbank");
}



/*******************************************************************************
    Inputs
*******************************************************************************/

INPUT_CHANGED_MEMBER(ti74_state::battery_status_changed)
{
	if (machine().phase() == machine_phase::RUNNING)
		update_battery_status(newval);
}

static INPUT_PORTS_START( ti74 )
	PORT_START("BATTERY")
	PORT_CONFNAME( 0x01, 0x01, "Battery Status" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ti74_state::battery_status_changed), 0)
	PORT_CONFSETTING(    0x00, "Low" )
	PORT_CONFSETTING(    0x01, DEF_STR( Normal ) )

	// 8x8 keyboard matrix, RESET and ON buttons are not on it. Unused entries are not connected, but some have a purpose for factory testing.
	// For convenience, number keys are mapped to number row too.
	// PORT_NAME lists functions under [SHIFT] and [MODE] or [STAT] as secondaries.
	PORT_START("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') PORT_NAME("m  M  Frac")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') PORT_NAME("k  K  Frq")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') PORT_NAME(u8"i  I  \u221ax") // U+221A = √
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_NAME(u8"\u2190     \u2190") // U+2190 = ←
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') PORT_NAME(u8"u  U  x²")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') PORT_NAME("j  J  nCr")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') PORT_NAME("n  N  Intg")

	PORT_START("IN.1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('%')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') PORT_NAME("l  L  (x,y)")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') PORT_NAME("o  O  1/x")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_NAME(u8"\u2192     EE" ) // U+2192 = →
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') PORT_NAME("y  Y  log")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') PORT_NAME("h  H  nPr")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') PORT_NAME("b  B  EXC")

	PORT_START("IN.2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') PORT_CHAR('\'') PORT_NAME(u8"SPACE  '  Δ%")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':') PORT_NAME(u8";  :  Σ+")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') PORT_NAME(u8"p  P  yˣ")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CHAR('(') PORT_NAME(u8"\u2191  (") // U+2191 = ↑
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') PORT_NAME("t  T  ln(x)")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') PORT_NAME("g  G  n!")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') PORT_NAME("v  V  SUM")

	PORT_START("IN.3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13) PORT_CHAR('=') PORT_NAME("ENTER  =")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(END)) PORT_NAME("CLR  UCL  CE/C")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CHAR(')') PORT_NAME(u8"\u2193  )") // U+2193 = ↓
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_NAME(u8"RUN     x\u2194y") // U+2194 = ↔
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') PORT_NAME(u8"r  R  π")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') PORT_NAME("f  F  P>R")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') PORT_NAME("c  C  RCL")

	PORT_START("IN.4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('~') PORT_CHAR('?') PORT_NAME("+/-  ?  CSR")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!') PORT_NAME("1  !  r")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') PORT_NAME(u8"4  $  Σx")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR(UCHAR_MAMEKEY(DEL)) PORT_NAME(u8"7  DEL  Σx²")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(PAUSE)) PORT_NAME("BREAK")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') PORT_NAME("e  E  tan")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') PORT_NAME("d  D  DRG>")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') PORT_NAME("x  X  STO")

	PORT_START("IN.5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('<') PORT_NAME(u8"0  <  xʹ")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"') PORT_NAME("2  \"  a")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('&') PORT_NAME(u8"5  &  Σy")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR(UCHAR_MAMEKEY(INSERT)) PORT_NAME(u8"8  INS  Σy²")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PGUP) PORT_CHAR(UCHAR_MAMEKEY(PGUP)) PORT_NAME("MODE")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') PORT_NAME("w  W  cos")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') PORT_NAME("s  S  DRG")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') PORT_NAME("z  Z  PRINT")

	PORT_START("IN.6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR('.') PORT_CHAR('>') PORT_NAME(u8".  >  yʹ") // 2 on the keyboard, same scancode
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#') PORT_NAME("3  #  b")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^') PORT_NAME("6  ^  n")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_NAME(u8"9  PB  Σxy")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PGDN) PORT_CHAR(UCHAR_MAMEKEY(PGDN)) PORT_NAME("OFF")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') PORT_NAME("q  Q  sin")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') PORT_NAME("a  A  DMS>DD")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("IN.7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR('+') PORT_NAME("+     s(y)")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_NAME("-     s(x)")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR('*') PORT_NAME(u8"*     ȳ")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_NAME(u8"/     x̄")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_NAME("FN     hyp")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2) PORT_NAME("CTL     STAT")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PORT_NAME("SHIFT     INV")
INPUT_PORTS_END

static INPUT_PORTS_START( ti95 )
	PORT_START("BATTERY")
	PORT_CONFNAME( 0x01, 0x01, "Battery Status" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ti74_state::battery_status_changed), 0)
	PORT_CONFSETTING(    0x00, "Low" )
	PORT_CONFSETTING(    0x01, DEF_STR( Normal ) )

	// 8x8 keyboard matrix, RESET and ON buttons are not on it.
	// For convenience, number keys are mapped to number row too.
	// PORT_NAME lists functions under [ALPHA] and [2nd] as secondaries.
	PORT_START("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PGDN) PORT_NAME("OFF")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_NAME("BREAK  Q")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_NAME("SIN  A")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_NAME("I/O  Z")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PGUP) PORT_NAME("HELP  ASM")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(')') PORT_NAME(")  ]  DRG")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD)) PORT_NAME(u8"÷  \\  DFN")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR('=') PORT_NAME("=  ~  TRACE")

	PORT_START("IN.1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_NAME("EE  {  ENG")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_NAME("HALT  W")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_NAME("COS  S")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_NAME("FILES  X")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT) PORT_NAME("ALPHA  PART")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_NAME("3  ;  SBL")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD)) PORT_NAME("+  &  RTN")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_NAME("2  :  GTL")

	PORT_START("IN.2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1) PORT_NAME("F1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_NAME(u8"Σ+  E")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_NAME("TAN  D")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_NAME("STAT  C")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_NAME("LEARN  PC")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_NAME("6  @  CP")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_NAME("-  _  13d")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_NAME("5  %  CMS")

	PORT_START("IN.3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) PORT_NAME("F2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_NAME("x~t  R  AH")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_NAME("LN  F")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_NAME("CONV  V")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_HOME) PORT_NAME("OLD  NOP")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_NAME("9  >  x!")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(ASTERISK)) PORT_NAME(u8"×  ^  π")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_NAME("8  <  nCr")

	PORT_START("IN.4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3) PORT_NAME("F3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_NAME("HYP  T  BH")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_NAME("LOG  G")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_NAME("NUM  B")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_NAME("RUN  SPACE")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_NAME("0  $  PAUSE")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR('.') PORT_NAME(".  ?  ADV")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("+/-  !  PRINT")

	PORT_START("IN.5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F4) PORT_NAME("F4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_NAME("INCR  Y  CH")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_NAME(u8"x²  H")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_NAME("FLAGS  N")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LEFT) PORT_NAME(u8"\u2190  DEL") // U+2190 = ←
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_NAME("RCL  O  FH")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_NAME("INV  P")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_NAME("7  }  nPr")

	PORT_START("IN.6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F5) PORT_NAME("F5")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_NAME("EXC  U  DH")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_NAME(u8"\u221ax  J") // U+221A = √
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_NAME("TESTS  M")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RIGHT) PORT_NAME(u8"\u2192  INS") // U+2192 = →
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_NAME(u8"yˣ  L")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_NAME("2nd")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_4) PORT_CHAR('4' ) PORT_NAME("4     IND")

	PORT_START("IN.7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('(') PORT_NAME("(  [  FIX")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_NAME("STO  I  EH")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_NAME("1/x  K")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_NAME("FUNC  ,  \"")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_END) PORT_NAME("CE  F:CLR")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_NAME("LIST  .  '")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_NAME("CLEAR")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_NAME("1  #  LBL")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void ti74_state::ti74(machine_config &config)
{
	// basic machine hardware
	TMS70C46(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &ti74_state::main_map);
	m_maincpu->in_porta().set(FUNC(ti74_state::keyboard_r));
	m_maincpu->out_portb().set(FUNC(ti74_state::bankswitch_w));
	m_maincpu->out_porte().set(FUNC(ti74_state::keyboard_w));

	NVRAM(config, "sysram.ic3", nvram_device::DEFAULT_ALL_0);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60); // arbitrary
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.set_size(6*31+1, 9*1+1+1);
	screen.set_visarea_full();
	screen.set_screen_update("hd44780", FUNC(hd44780_device::screen_update));
	screen.set_palette("palette");

	config.set_default_layout(layout_ti74);

	PALETTE(config, "palette", FUNC(ti74_state::ti74_palette), 3);

	hd44780_device &hd44780(HD44780(config, "hd44780", 270'000)); // OSC = 91K resistor
	hd44780.set_lcd_size(2, 16); // 2*16 internal
	hd44780.set_pixel_update_cb(FUNC(ti74_state::ti74_pixel_update));

	// cartridge
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "ti74_cart", "bin,rom,256").set_device_load(FUNC(ti74_state::cart_load));
	SOFTWARE_LIST(config, "cart_list").set_original("ti74_cart");
}

void ti74_state::ti95(machine_config &config)
{
	// basic machine hardware
	TMS70C46(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &ti74_state::main_map);
	m_maincpu->in_porta().set(FUNC(ti74_state::keyboard_r));
	m_maincpu->out_portb().set(FUNC(ti74_state::bankswitch_w));
	m_maincpu->out_porte().set(FUNC(ti74_state::keyboard_w));

	NVRAM(config, "sysram.ic3", nvram_device::DEFAULT_ALL_0);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60); // arbitrary
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	screen.set_size(200, 20);
	screen.set_visarea_full();
	screen.set_screen_update("hd44780", FUNC(hd44780_device::screen_update));
	screen.set_palette("palette");

	config.set_default_layout(layout_ti95);

	PALETTE(config, "palette", FUNC(ti74_state::ti74_palette), 3);

	hd44780_device &hd44780(HD44780(config, "hd44780", 270'000)); // OSC = 91K resistor
	hd44780.set_lcd_size(2, 16);
	hd44780.set_pixel_update_cb(FUNC(ti74_state::ti95_pixel_update));

	// cartridge
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "ti95_cart", "bin,rom,256").set_device_load(FUNC(ti74_state::cart_load));
	SOFTWARE_LIST(config, "cart_list").set_original("ti95_cart");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( ti74 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "c70009.ic2", 0x0000, 0x1000, CRC(55a2f7c0) SHA1(530e3de42f2e304c8f4805ad389f38a459ec4e33) ) // internal cpu rom

	ROM_REGION( 0x8000, "system", 0 )
	ROM_LOAD( "1060281-101_hn61256pd12.ic1", 0x0000, 0x8000, CRC(019aaa2f) SHA1(04a1e694a49d50602e45a7834846de4d9f7d587d) ) // system rom, banked
ROM_END

ROM_START( ti74a )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "c70009.ic2", 0x0000, 0x1000, CRC(55a2f7c0) SHA1(530e3de42f2e304c8f4805ad389f38a459ec4e33) ) // internal cpu rom

	ROM_REGION( 0x8000, "system", 0 )
	ROM_LOAD( "001060281-1_hn61256pc93.ic1", 0x0000, 0x8000, CRC(499b69d1) SHA1(ba333959bd047ac18f461066816c4d56fe73de85) ) // system rom, banked
ROM_END


ROM_START( ti95 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "c70011.ic2", 0x0000, 0x1000, CRC(b4d0a5c1) SHA1(3ff41946d014f72220a88803023b6a06d5086ce4) ) // internal cpu rom

	ROM_REGION( 0x8000, "system", 0 )
	ROM_LOAD( "01060281-11_hn61256pc95.ic1", 0x0000, 0x8000, CRC(c46d29ae) SHA1(c653f08590dbc28241a9f5a6c2541641bdb0208b) ) // system rom, banked
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY, FULLNAME, FLAGS
SYST( 1985, ti74,  0,      0,      ti74,    ti74,  ti74_state, empty_init, "Texas Instruments", "TI-74 Basicalc (set 1)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
SYST( 1985, ti74a, ti74,   0,      ti74,    ti74,  ti74_state, empty_init, "Texas Instruments", "TI-74 Basicalc (set 2)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )

SYST( 1986, ti95,  0,      0,      ti95,    ti95,  ti74_state, empty_init, "Texas Instruments", "TI-95 Procalc", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )



ti85.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Krzysztof Strzecha,Jon Sturm
/***************************************************************************
TI-85 and TI-86 drivers by Krzysztof Strzecha
TI-83 Plus, TI-84 Plus, and Silver Edition support by Jon Sturm

Notes:
1. After start TI-85 waits for ON key interrupt, so press ON key to start
   calculator. ************* PRESS THE "Q" KEY TO TURN IT ON. ********************
2. Only difference between all TI-85 drivers is ROM version.
3. TI-86 is TI-85 with more RAM and ROM.
4. Only difference between all TI-86 drivers is ROM version.
5. Video engine (with grayscale support) based on the idea found in VTI source
   emulator written by Rusty Wagner.
6. NVRAM is saved properly only when calculator is turned off before exiting MAME.
7. To receive data from TI press "R" immediately after TI starts to send data.
8. To request screen dump from calculator press "S".
9. TI-81 does not have a serial link.

Needed:
1. Info about ports 3 (bit 2 seems to be always 0) and 4.
2. Any info on TI-81 hardware.
3. ROM dumps of unemulated models.
4. Artworks.

New:
05/10/2002 TI-85 serial link works again.
17/09/2002 TI-85 snapshots loading fixed. Few code cleanups.
       TI-86 SNAPSHOT LOADING DOESNT WORK.
       TI-85, TI-86 SERIAL LINK DOESNT WORK.
08/09/2001 TI-81, TI-85, TI-86 modified to new core.
       TI-81, TI-85, TI-86 reset corrected.
21/08/2001 TI-81, TI-85, TI-86 NVRAM corrected.
20/08/2001 TI-81 ON/OFF fixed.
       TI-81 ROM bank switching added (port 5).
       TI-81 NVRAM support added.
15/08/2001 TI-81 kayboard is now mapped as it should be.
14/08/2001 TI-81 preliminary driver added.
05/07/2001 Serial communication corrected (transmission works now after reset).
02/07/2001 Many source cleanups.
       PCR added.
01/07/2001 Possibility to request screen dump from TI (received dumps are saved
       as t85i file).
29/06/2001 Received variables can be saved now.
19/06/2001 Possibility to receive variables from calculator (they are nor saved
       yet).
17/06/2001 TI-86 reset fixed.
15/06/2001 Possibility to receive memory backups from calculator.
07/06/2001 TI-85 reset fixed.
       Work on receiving data from calculator started.
04/06/2001 TI-85 is able to receive variables and memory backups.
14/05/2001 Many source cleanups.
11/05/2001 Release years corrected. Work on serial link started.
26/04/2001 NVRAM support added.
25/04/2001 Video engine totally rewritten so grayscale works now.
17/04/2001 TI-86 snapshots loading added.
       ti86grom driver added.
16/04/2001 Sound added.
       Five TI-86 drivers added (all features of TI-85 drivers without
       snapshot loading).
13/04/2001 Snapshot loading (VTI 2.0 save state files).
18/02/2001 Palette (not perfect).
       Contrast control (port 2) implemented.
       LCD ON/OFF implemented (port 3).
       Interrupts corrected (port 3) - ON/OFF and APD works now.
       Artwork added.
09/02/2001 Keypad added.
       200Hz timer interrupts implemented.
       ON key and its interrupts implemented.
       Calculator is now fully usable.
02/02/2001 Preliminary driver

To do:
- port 7 (TI-86)
- port 4 (all models)
- artwork (all models)
- port 0 link (TI-82 and TI-83)
- add TI-73, TI-83+ and T84+ drivers


TI-81 memory map

    CPU: Z80 2MHz
        0000-7fff ROM
        8000-ffff RAM (?)

TI-82 memory map

    CPU: Z80 6MHz
        0000-3fff ROM 0
        4000-7fff ROM 1-7 (switched)
        8000-ffff RAM

TI-83 memory map

    CPU: Z80 6MHz
        0000-3fff ROM 0
        4000-7fff ROM 1-15 (switched)
        8000-ffff RAM

TI-83Plus memory map

    CPU: Z80 8MHz (running at 6 MHz)
        0000-3fff ROM 0
        4000-7fff ROM 0-31 or RAM 0-1 (switched)
        7000-bfff ROM 0-31 or RAM 0-1 (switched)
        c000-ffff RAM 0-31 or RAM 0-1 (switched)

TI-85 memory map

    CPU: Z80 6MHz
        0000-3fff ROM 0
        4000-7fff ROM 1-7 (switched)
        8000-ffff RAM

TI-86 memory map

    CPU: Z80 6MHz
        0000-3fff ROM 0
        4000-7fff ROM 0-15 or RAM 0-7 (switched)
        7000-bfff ROM 0-15 or RAM 0-7 (switched)
        c000-ffff RAM 0

Interrupts:

    IRQ: 200Hz timer
         ON key

TI-81 ports:
    0: Video buffer offset (write only)
    1: Keypad
    2: Contrast (write only)
    3: ON status, LCD power
    4: Video buffer width, interrupt control (write only)
    5: ?
    6:
    7: ?

TI-82 ports:
    0: Link
    1: Keypad
    2: Memory page
    3: ON status, LCD power
    4: Video buffer width, interrupt control (write only)
    10: Control port for the display controller
    11: Data port for the display controller

TI-83 ports:
    0: Link + Memory page
    1: Keypad
    2: Memory page
    3: ON status, LCD power
    4: Video buffer width, interrupt control (write only)
    10: Control port for the display controller
    11: Data port for the display controller
    14: Battery Status

TI-83Plus ports:
    0: Link
    1: Keypad
    2: ?
    3: ON status, LCD power
    4: Interrupt status
    6: Memory page 1
    7: Memory page 2
    10: Control port for the display controller
    11: Data port for the display controller

TI-83PlusSE ports:
    0: Link
    1: Keypad
    2: ?
    3: ON status, LCD power
    4: Interrupt status
    5: Memory page 3
    6: Memory page 1
    7: Memory page 2
    10: Controll port for the display controller
    11: Data port for the display controller
    15: Asic Version

TI-85 ports:
    0: Video buffer offset (write only)
    1: Keypad
    2: Contrast (write only)
    3: ON status, LCD power
    4: Video buffer width, interrupt control (write only)
    5: Memory page
    6: Power mode
    7: Link

TI-86 ports:
    0: Video buffer offset (write only)
    1: Keypad
    2: Contrast (write only)
    3: ON status, LCD power
    4: Power mode
    5: Memory page
    6: Memory page
    7: Link

***************************************************************************/

#include "emu.h"
#include "ti85.h"

#include "cpu/z80/z80.h"
#include "cpu/z80/ez80.h"
#include "imagedev/snapquik.h"
#include "screen.h"


/* port i/o functions */

void ti85_state::ti81_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x0000, 0x0000).rw(FUNC(ti85_state::ti85_port_0000_r), FUNC(ti85_state::ti85_port_0000_w));
	map(0x0001, 0x0001).rw(FUNC(ti85_state::ti8x_keypad_r), FUNC(ti85_state::ti8x_keypad_w));
	map(0x0002, 0x0002).rw(FUNC(ti85_state::ti85_port_0002_r), FUNC(ti85_state::ti85_port_0002_w));
	map(0x0003, 0x0003).rw(FUNC(ti85_state::ti85_port_0003_r), FUNC(ti85_state::ti85_port_0003_w));
	map(0x0004, 0x0004).rw(FUNC(ti85_state::ti85_port_0004_r), FUNC(ti85_state::ti85_port_0004_w));
	map(0x0005, 0x0005).rw(FUNC(ti85_state::ti85_port_0005_r), FUNC(ti85_state::ti85_port_0005_w));
	map(0x0007, 0x0007).w(FUNC(ti85_state::ti81_port_0007_w));
}

void ti85_state::ti85_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x0000, 0x0000).rw(FUNC(ti85_state::ti85_port_0000_r), FUNC(ti85_state::ti85_port_0000_w));
	map(0x0001, 0x0001).rw(FUNC(ti85_state::ti8x_keypad_r), FUNC(ti85_state::ti8x_keypad_w));
	map(0x0002, 0x0002).rw(FUNC(ti85_state::ti85_port_0002_r), FUNC(ti85_state::ti85_port_0002_w));
	map(0x0003, 0x0003).rw(FUNC(ti85_state::ti85_port_0003_r), FUNC(ti85_state::ti85_port_0003_w));
	map(0x0004, 0x0004).rw(FUNC(ti85_state::ti85_port_0004_r), FUNC(ti85_state::ti85_port_0004_w));
	map(0x0005, 0x0005).rw(FUNC(ti85_state::ti85_port_0005_r), FUNC(ti85_state::ti85_port_0005_w));
	map(0x0006, 0x0006).rw(FUNC(ti85_state::ti85_port_0006_r), FUNC(ti85_state::ti85_port_0006_w));
	map(0x0007, 0x0007).rw(FUNC(ti85_state::ti8x_serial_r), FUNC(ti85_state::ti8x_serial_w));
}

void ti85_state::ti82_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x0000, 0x0000).rw(FUNC(ti85_state::ti8x_serial_r), FUNC(ti85_state::ti8x_serial_w));
	map(0x0001, 0x0001).rw(FUNC(ti85_state::ti8x_keypad_r), FUNC(ti85_state::ti8x_keypad_w));
	map(0x0002, 0x0002).rw(FUNC(ti85_state::ti82_port_0002_r), FUNC(ti85_state::ti82_port_0002_w));
	map(0x0003, 0x0003).rw(FUNC(ti85_state::ti85_port_0003_r), FUNC(ti85_state::ti85_port_0003_w));
	map(0x0004, 0x0004).rw(FUNC(ti85_state::ti85_port_0004_r), FUNC(ti85_state::ti85_port_0004_w));
	map(0x0010, 0x0010).rw("t6a04", FUNC(t6a04_device::control_read), FUNC(t6a04_device::control_write));
	map(0x0011, 0x0011).rw("t6a04", FUNC(t6a04_device::data_read), FUNC(t6a04_device::data_write));
}

void ti85_state::ti81v2_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x0001, 0x0001).rw(FUNC(ti85_state::ti8x_keypad_r), FUNC(ti85_state::ti8x_keypad_w));
	map(0x0002, 0x0002).rw(FUNC(ti85_state::ti82_port_0002_r), FUNC(ti85_state::ti82_port_0002_w));
	map(0x0003, 0x0003).rw(FUNC(ti85_state::ti85_port_0003_r), FUNC(ti85_state::ti85_port_0003_w));
	map(0x0004, 0x0004).rw(FUNC(ti85_state::ti85_port_0004_r), FUNC(ti85_state::ti85_port_0004_w));
	map(0x0010, 0x0010).rw("t6a04", FUNC(t6a04_device::control_read), FUNC(t6a04_device::control_write));
	map(0x0011, 0x0011).rw("t6a04", FUNC(t6a04_device::data_read), FUNC(t6a04_device::data_write));
}

void ti85_state::ti83_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x0000, 0x0000).rw(FUNC(ti85_state::ti83_port_0000_r), FUNC(ti85_state::ti83_port_0000_w));  //TODO
	map(0x0001, 0x0001).rw(FUNC(ti85_state::ti8x_keypad_r), FUNC(ti85_state::ti8x_keypad_w));
	map(0x0002, 0x0002).rw(FUNC(ti85_state::ti83_port_0002_r), FUNC(ti85_state::ti83_port_0002_w));
	map(0x0003, 0x0003).rw(FUNC(ti85_state::ti83_port_0003_r), FUNC(ti85_state::ti83_port_0003_w));
	map(0x0004, 0x0004).rw(FUNC(ti85_state::ti85_port_0004_r), FUNC(ti85_state::ti85_port_0004_w));
	map(0x0010, 0x0010).rw("t6a04", FUNC(t6a04_device::control_read), FUNC(t6a04_device::control_write));
	map(0x0011, 0x0011).rw("t6a04", FUNC(t6a04_device::data_read), FUNC(t6a04_device::data_write));
	map(0x0014, 0x0014).portr("BATTERY");
}

void ti85_state::ti83p_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x0000, 0x0000).rw(FUNC(ti85_state::ti8x_plus_serial_r), FUNC(ti85_state::ti8x_plus_serial_w));
	map(0x0001, 0x0001).rw(FUNC(ti85_state::ti8x_keypad_r), FUNC(ti85_state::ti8x_keypad_w));
	map(0x0002, 0x0002).r(FUNC(ti85_state::ti83p_port_0002_r));
	map(0x0003, 0x0003).rw(FUNC(ti85_state::ti83_port_0003_r), FUNC(ti85_state::ti83p_int_mask_w));
	map(0x0004, 0x0004).rw(FUNC(ti85_state::ti83p_port_0004_r), FUNC(ti85_state::ti83p_port_0004_w));
	map(0x0006, 0x0006).rw(FUNC(ti85_state::ti86_port_0005_r), FUNC(ti85_state::ti83p_port_0006_w));
	map(0x0007, 0x0007).rw(FUNC(ti85_state::ti86_port_0006_r), FUNC(ti85_state::ti83p_port_0007_w));
	map(0x0010, 0x0010).rw("t6a04", FUNC(t6a04_device::control_read), FUNC(t6a04_device::control_write));
	map(0x0011, 0x0011).rw("t6a04", FUNC(t6a04_device::data_read), FUNC(t6a04_device::data_write));
//  map(0x0014, 0x0014).w(FUNC(ti85_state::ti83p_port_0014_w));
}

void ti85_state::ti83pse_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x0000, 0x0000).rw(FUNC(ti85_state::ti8x_plus_serial_r), FUNC(ti85_state::ti8x_plus_serial_w));
	map(0x0001, 0x0001).rw(FUNC(ti85_state::ti8x_keypad_r), FUNC(ti85_state::ti8x_keypad_w));
	map(0x0002, 0x0002).rw(FUNC(ti85_state::ti83pse_port_0002_r), FUNC(ti85_state::ti83pse_int_ack_w));
	map(0x0003, 0x0003).rw(FUNC(ti85_state::ti83_port_0003_r), FUNC(ti85_state::ti83p_int_mask_w));
	map(0x0004, 0x0004).rw(FUNC(ti85_state::ti83p_port_0004_r), FUNC(ti85_state::ti83pse_port_0004_w));
	map(0x0005, 0x0005).rw(FUNC(ti85_state::ti83pse_port_0005_r), FUNC(ti85_state::ti83pse_port_0005_w));
	map(0x0006, 0x0006).rw(FUNC(ti85_state::ti86_port_0005_r), FUNC(ti85_state::ti83pse_port_0006_w));
	map(0x0007, 0x0007).rw(FUNC(ti85_state::ti86_port_0006_r), FUNC(ti85_state::ti83pse_port_0007_w));
	map(0x0009, 0x0009).r(FUNC(ti85_state::ti83pse_port_0009_r));
	map(0x0010, 0x0010).rw("t6a04", FUNC(t6a04_device::control_read), FUNC(t6a04_device::control_write));
	map(0x0011, 0x0011).rw("t6a04", FUNC(t6a04_device::data_read), FUNC(t6a04_device::data_write));
	map(0x0012, 0x0012).rw("t6a04", FUNC(t6a04_device::control_read), FUNC(t6a04_device::control_write));
	map(0x0013, 0x0013).rw("t6a04", FUNC(t6a04_device::data_read), FUNC(t6a04_device::data_write));
	map(0x0014, 0x0014).w(FUNC(ti85_state::ti83p_port_0014_w));
	map(0x0015, 0x0015).r(FUNC(ti85_state::ti83pse_port_0015_r));
	map(0x0020, 0x0020).rw(FUNC(ti85_state::ti83pse_port_0020_r), FUNC(ti85_state::ti83pse_port_0020_w));
	map(0x0021, 0x0021).rw(FUNC(ti85_state::ti83pse_port_0021_r), FUNC(ti85_state::ti83pse_port_0021_w));

	map(0x0030, 0x0030).rw(FUNC(ti85_state::ti83pse_ctimer1_setup_r), FUNC(ti85_state::ti83pse_ctimer1_setup_w));
	map(0x0031, 0x0031).rw(FUNC(ti85_state::ti83pse_ctimer1_loop_r), FUNC(ti85_state::ti83pse_ctimer1_loop_w));
	map(0x0032, 0x0032).rw(FUNC(ti85_state::ti83pse_ctimer1_count_r), FUNC(ti85_state::ti83pse_ctimer1_count_w));
	map(0x0033, 0x0033).rw(FUNC(ti85_state::ti83pse_ctimer2_setup_r), FUNC(ti85_state::ti83pse_ctimer2_setup_w));
	map(0x0034, 0x0034).rw(FUNC(ti85_state::ti83pse_ctimer2_loop_r), FUNC(ti85_state::ti83pse_ctimer2_loop_w));
	map(0x0035, 0x0035).rw(FUNC(ti85_state::ti83pse_ctimer2_count_r), FUNC(ti85_state::ti83pse_ctimer2_count_w));
	map(0x0036, 0x0036).rw(FUNC(ti85_state::ti83pse_ctimer3_setup_r), FUNC(ti85_state::ti83pse_ctimer3_setup_w));
	map(0x0037, 0x0037).rw(FUNC(ti85_state::ti83pse_ctimer3_loop_r), FUNC(ti85_state::ti83pse_ctimer3_loop_w));
	map(0x0038, 0x0038).rw(FUNC(ti85_state::ti83pse_ctimer3_count_r), FUNC(ti85_state::ti83pse_ctimer3_count_w));

	map(0x0055, 0x0055).r(FUNC(ti85_state::ti84pse_port_0055_r));
	map(0x0056, 0x0056).r(FUNC(ti85_state::ti84pse_port_0056_r));
}

void ti85_state::ti86_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x0000, 0x0000).rw(FUNC(ti85_state::ti85_port_0000_r), FUNC(ti85_state::ti85_port_0000_w));
	map(0x0001, 0x0001).rw(FUNC(ti85_state::ti8x_keypad_r), FUNC(ti85_state::ti8x_keypad_w));
	map(0x0002, 0x0002).rw(FUNC(ti85_state::ti85_port_0002_r), FUNC(ti85_state::ti85_port_0002_w));
	map(0x0003, 0x0003).rw(FUNC(ti85_state::ti85_port_0003_r), FUNC(ti85_state::ti85_port_0003_w));
	map(0x0004, 0x0004).rw(FUNC(ti85_state::ti85_port_0006_r), FUNC(ti85_state::ti85_port_0006_w));
	map(0x0005, 0x0005).rw(FUNC(ti85_state::ti86_port_0005_r), FUNC(ti85_state::ti86_port_0005_w));
	map(0x0006, 0x0006).rw(FUNC(ti85_state::ti86_port_0006_r), FUNC(ti85_state::ti86_port_0006_w));
	map(0x0007, 0x0007).rw(FUNC(ti85_state::ti8x_serial_r), FUNC(ti85_state::ti8x_serial_w));
}

/* memory w/r functions */

void ti85_state::ti81_mem(address_map &map)
{
	map(0x0000, 0x3fff).bankr("bank1");
	map(0x4000, 0x7fff).bankr("bank2");
	map(0x8000, 0xffff).ram().share("nvram");
}

void ti85_state::ti86_mem(address_map &map)
{
	map(0x0000, 0x3fff).bankr("bank1");
	map(0x4000, 0x7fff).bankrw("bank2");
	map(0x8000, 0xbfff).bankrw("bank3");
	map(0xc000, 0xffff).bankrw("bank4");
}

void ti85_state::ti83pse_banked_mem(address_map &map)
{
	map(0x0000, 0x1fffff).rw(m_flash, FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0x200000, 0x21BFFF).ram().share("nvram");
}


void ti85_state::ti84p_banked_mem(address_map &map)
{
	map(0x0000, 0xfffff).rw(m_flash, FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0x200000, 0x21BFFF).ram().share("nvram");
}

void ti85_state::ti83p_banked_mem(address_map &map)
{
	map(0x00000, 0x7ffff).rw(m_flash, FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
	map(0x100000, 0x107fff).ram().share("nvram");
}

void ti85_state::ti83p_asic_mem(address_map &map)
{
	map(0x0000, 0x3fff).rw(m_membank[0], FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0x4000, 0x7fff).w(m_membank[1], FUNC(address_map_bank_device::write8)).r(FUNC(ti85_state::ti83p_membank2_r));
	map(0x8000, 0xbfff).w(m_membank[2], FUNC(address_map_bank_device::write8)).r(FUNC(ti85_state::ti83p_membank3_r));
	map(0xc000, 0xffff).rw(m_membank[3], FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
}

/* keyboard input */

static INPUT_PORTS_START (ti81)
	PORT_START("BIT0")   /* bit 0 */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(-)") PORT_CODE(KEYCODE_M)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("GRAPH") PORT_CODE(KEYCODE_F5)
	PORT_START("BIT1")   /* bit 1 */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+") PORT_CODE(KEYCODE_EQUALS)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("STORE") PORT_CODE(KEYCODE_TAB)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TRACE") PORT_CODE(KEYCODE_F4)
	PORT_START("BIT2")   /* bit 2 */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LN") PORT_CODE(KEYCODE_BACKSLASH)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ZOOM") PORT_CODE(KEYCODE_F3)
	PORT_START("BIT3")   /* bit 3 */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("*") PORT_CODE(KEYCODE_L)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LOG") PORT_CODE(KEYCODE_QUOTE)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RANGE") PORT_CODE(KEYCODE_F2)
	PORT_START("BIT4")   /* bit 4 */
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(")") PORT_CODE(KEYCODE_CLOSEBRACE)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(") PORT_CODE(KEYCODE_OPENBRACE)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("EE") PORT_CODE(KEYCODE_END)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("x^2") PORT_CODE(KEYCODE_COLON)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y=") PORT_CODE(KEYCODE_F1)
	PORT_START("BIT5")   /* bit 5 */
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_P)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TAN") PORT_CODE(KEYCODE_PGUP)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("COS") PORT_CODE(KEYCODE_HOME)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SIN") PORT_CODE(KEYCODE_INSERT)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("x^-1") PORT_CODE(KEYCODE_COMMA)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2nd") PORT_CODE(KEYCODE_LALT)
	PORT_START("BIT6")   /* bit 6 */
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CLEAR") PORT_CODE(KEYCODE_PGDN)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("VARS") PORT_CODE(KEYCODE_F9)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("PRGM") PORT_CODE(KEYCODE_F8)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MATRX") PORT_CODE(KEYCODE_F7)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MATH") PORT_CODE(KEYCODE_F6)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("INS") PORT_CODE(KEYCODE_TILDE)
	PORT_START("BIT7")   /* bit 7 */
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MODE") PORT_CODE(KEYCODE_ESC)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X|T") PORT_CODE(KEYCODE_X)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ALPHA") PORT_CODE(KEYCODE_CAPSLOCK)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL)
	PORT_START("ON")   /* ON */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ON/OFF") PORT_CODE(KEYCODE_Q)
INPUT_PORTS_END

static INPUT_PORTS_START (ti85)
	PORT_START("BIT0")   /* bit 0 */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENTER   (ENTRY)") PORT_CODE(KEYCODE_ENTER)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(-)     (ANS     |_|)") PORT_CODE(KEYCODE_M)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(".       (:       Z)") PORT_CODE(KEYCODE_STOP)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0       (CHAR    Y)") PORT_CODE(KEYCODE_0)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F5      (M5)") PORT_CODE(KEYCODE_F5)
	PORT_START("BIT1")   /* bit 1 */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+       (MEM     X)") PORT_CODE(KEYCODE_EQUALS)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3       (VARS    W)") PORT_CODE(KEYCODE_3)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2       (TEST    V)") PORT_CODE(KEYCODE_2)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1       (BASE    U)") PORT_CODE(KEYCODE_1)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("STORE   (RCL     =)") PORT_CODE(KEYCODE_TAB)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F4      (M4)") PORT_CODE(KEYCODE_F4)
	PORT_START("BIT2")   /* bit 2 */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("-       (LIST    T)") PORT_CODE(KEYCODE_MINUS)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6       (STRNG   S)") PORT_CODE(KEYCODE_6)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5       (CONV    R)") PORT_CODE(KEYCODE_5)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4       (CONS    Q)") PORT_CODE(KEYCODE_4)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(",       (ANGLE   P)") PORT_CODE(KEYCODE_COMMA)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F3      (M3)") PORT_CODE(KEYCODE_F3)
	PORT_START("BIT3")   /* bit 3 */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("*       (MATH    O)") PORT_CODE(KEYCODE_L)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9       (CPLX    N)") PORT_CODE(KEYCODE_9)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8       (VECTR   M)") PORT_CODE(KEYCODE_8)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7       (MATRX   L)") PORT_CODE(KEYCODE_7)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("x^2     (SQRT    K)") PORT_CODE(KEYCODE_COLON)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F2      (M2)") PORT_CODE(KEYCODE_F2)
	PORT_START("BIT4")   /* bit 4 */
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/       (CALC    J)") PORT_CODE(KEYCODE_SLASH)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(")       (]       I)") PORT_CODE(KEYCODE_CLOSEBRACE)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(       ([       H)") PORT_CODE(KEYCODE_OPENBRACE)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("EE      (X^-1    G)") PORT_CODE(KEYCODE_END)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LN      (e^x     F)") PORT_CODE(KEYCODE_BACKSLASH)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F1      (M1)") PORT_CODE(KEYCODE_F1)
	PORT_START("BIT5")   /* bit 5 */
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("^       (PI      E)") PORT_CODE(KEYCODE_P)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TAN     (TAN^-1  D)") PORT_CODE(KEYCODE_PGUP)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("COS     (COS^-1  C)") PORT_CODE(KEYCODE_HOME)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SIN     (SIN^-1  B)") PORT_CODE(KEYCODE_INSERT)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LOG     (10^x    A)") PORT_CODE(KEYCODE_QUOTE)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2nd") PORT_CODE(KEYCODE_LALT)
	PORT_START("BIT6")   /* bit 6 */
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CLEAR   (TOLER)") PORT_CODE(KEYCODE_PGDN)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CUSTOM  (CATALOG)") PORT_CODE(KEYCODE_F9)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("PRGM    (POLY)") PORT_CODE(KEYCODE_F8)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("STAT    (SIMULT)") PORT_CODE(KEYCODE_F7)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("GRAPH   (SOLVER)") PORT_CODE(KEYCODE_F6)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("EXIT    (QUIT)") PORT_CODE(KEYCODE_ESC)
	PORT_START("BIT7")   /* bit 7 */
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DEL     (INS)") PORT_CODE(KEYCODE_DEL)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("x-VAR   (LINK    x)") PORT_CODE(KEYCODE_LCONTROL)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ALPHA   (alpha)") PORT_CODE(KEYCODE_CAPSLOCK)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MORE    (MODE)") PORT_CODE(KEYCODE_TILDE)
	PORT_START("ON")   /* ON */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ON/OFF") PORT_CODE(KEYCODE_Q)
INPUT_PORTS_END

static INPUT_PORTS_START (ti82)
	PORT_START("BIT0")   /* bit 0 */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(-)") PORT_CODE(KEYCODE_M)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("GRAPH") PORT_CODE(KEYCODE_F5)
	PORT_START("BIT1")   /* bit 1 */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+") PORT_CODE(KEYCODE_EQUALS)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("STORE") PORT_CODE(KEYCODE_S)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TRACE") PORT_CODE(KEYCODE_F4)
	PORT_START("BIT2")   /* bit 2 */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LN") PORT_CODE(KEYCODE_BACKSLASH)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ZOOM") PORT_CODE(KEYCODE_F3)
	PORT_START("BIT3")   /* bit 3 */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("*") PORT_CODE(KEYCODE_L)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LOG") PORT_CODE(KEYCODE_QUOTE)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("WINDOW") PORT_CODE(KEYCODE_F2)
	PORT_START("BIT4")   /* bit 4 */
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(")") PORT_CODE(KEYCODE_CLOSEBRACE)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(") PORT_CODE(KEYCODE_OPENBRACE)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_END)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("x^2") PORT_CODE(KEYCODE_COLON)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y=") PORT_CODE(KEYCODE_F1)
	PORT_START("BIT5")   /* bit 5 */
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_P)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TAN") PORT_CODE(KEYCODE_PGUP)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("COS") PORT_CODE(KEYCODE_HOME)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SIN") PORT_CODE(KEYCODE_INSERT)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("x^-1") PORT_CODE(KEYCODE_COMMA)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2nd") PORT_CODE(KEYCODE_LALT)
	PORT_START("BIT6")   /* bit 6 */
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CLEAR") PORT_CODE(KEYCODE_PGDN)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("VARS") PORT_CODE(KEYCODE_F9)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("PRGM") PORT_CODE(KEYCODE_F8)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MATRX") PORT_CODE(KEYCODE_F7)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MATH") PORT_CODE(KEYCODE_F6)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MODE") PORT_CODE(KEYCODE_ESC)
	PORT_START("BIT7")   /* bit 7 */
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("STAT") PORT_CODE(KEYCODE_TILDE)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("x-VAR") PORT_CODE(KEYCODE_X)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ALPHA") PORT_CODE(KEYCODE_LSHIFT)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL)
	PORT_START("ON")   /* ON */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ON/OFF") PORT_CODE(KEYCODE_Q)
INPUT_PORTS_END

static INPUT_PORTS_START (ti83)
	PORT_INCLUDE( ti82 )

	PORT_START("BATTERY")
		PORT_DIPNAME( 0x01, 0x01, "Battery Status" )
		PORT_DIPSETTING( 0x01, DEF_STR( Normal ) )
		PORT_DIPSETTING( 0x00, "Low Battery" )
INPUT_PORTS_END

/* machine definition */
void ti85_state::ti81(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 2000000);        /* 2 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &ti85_state::ti81_mem);
	m_maincpu->set_addrmap(AS_IO, &ti85_state::ti81_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(0);
	screen.set_size(96, 64);
	screen.set_visarea(0, 96-1, 0, 64-1);
	screen.set_screen_update(FUNC(ti85_state::screen_update_ti85));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(ti85_state::ti85_palette), 224, 224);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);
}


void ti85_state::ti85(machine_config &config)
{
	ti81(config);
	m_maincpu->set_clock(6000000);        /* 6 MHz */
	m_maincpu->set_addrmap(AS_IO, &ti85_state::ti85_io);

	MCFG_MACHINE_RESET_OVERRIDE(ti85_state, ti85 )

	subdevice<screen_device>("screen")->set_size(128, 64);
	subdevice<screen_device>("screen")->set_visarea(0, 128-1, 0, 64-1);

	TI8X_LINK_PORT(config, m_link_port, default_ti8x_link_devices, nullptr);
}


void ti85_state::ti85d(machine_config &config)
{
	ti85(config);
	SNAPSHOT(config, "snapshot", "sav").set_load_callback(FUNC(ti85_state::snapshot_cb));
	//TI85SERIAL(config, "tiserial");
}


void ti85_state::ti82(machine_config &config)
{
	ti81(config);
	m_maincpu->set_clock(6000000);        /* 6 MHz */
	m_maincpu->set_addrmap(AS_IO, &ti85_state::ti82_io);

	MCFG_MACHINE_RESET_OVERRIDE(ti85_state, ti85 )

	subdevice<screen_device>("screen")->set_screen_update("t6a04", FUNC(t6a04_device::screen_update));

	subdevice<palette_device>("palette")->set_entries(2).set_init(FUNC(ti85_state::ti82_palette));

	T6A04(config, "t6a04", 0).set_size(96, 64);

	TI8X_LINK_PORT(config, m_link_port, default_ti8x_link_devices, nullptr);
}

void ti85_state::ti81v2(machine_config &config)
{
	ti82(config);
	m_maincpu->set_addrmap(AS_IO, &ti85_state::ti81v2_io);

	config.device_remove("linkport");
}

void ti85_state::ti83(machine_config &config)
{
	ti81(config);
	m_maincpu->set_clock(6000000);        /* 6 MHz */
	m_maincpu->set_addrmap(AS_IO, &ti85_state::ti83_io);

	MCFG_MACHINE_RESET_OVERRIDE(ti85_state, ti85 )

	subdevice<screen_device>("screen")->set_screen_update("t6a04", FUNC(t6a04_device::screen_update));

	subdevice<palette_device>("palette")->set_entries(2).set_init(FUNC(ti85_state::ti82_palette));

	T6A04(config, "t6a04", 0).set_size(96, 64);
}

void ti85_state::ti86(machine_config &config)
{
	ti85(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &ti85_state::ti86_mem);
	m_maincpu->set_addrmap(AS_IO, &ti85_state::ti86_io);

	MCFG_MACHINE_START_OVERRIDE(ti85_state, ti86)
	MCFG_MACHINE_RESET_OVERRIDE(ti85_state, ti85)

	SNAPSHOT(config, "snapshot", "sav").set_load_callback(FUNC(ti85_state::snapshot_cb));
}

void ti85_state::ti83p(machine_config &config)
{
	ti81(config);
	m_maincpu->set_clock(6000000);        /* 8 MHz running at 6 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &ti85_state::ti83p_asic_mem);
	m_maincpu->set_addrmap(AS_IO, &ti85_state::ti83p_io);

	MCFG_MACHINE_START_OVERRIDE(ti85_state, ti83p )
	MCFG_MACHINE_RESET_OVERRIDE(ti85_state, ti83p )

	subdevice<screen_device>("screen")->set_screen_update("t6a04", FUNC(t6a04_device::screen_update));

	subdevice<palette_device>("palette")->set_entries(2).set_init(FUNC(ti85_state::ti82_palette));

	ADDRESS_MAP_BANK(config, m_membank[0]).set_map(&ti85_state::ti83p_banked_mem).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);
	ADDRESS_MAP_BANK(config, m_membank[1]).set_map(&ti85_state::ti83p_banked_mem).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);
	ADDRESS_MAP_BANK(config, m_membank[2]).set_map(&ti85_state::ti83p_banked_mem).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);
	ADDRESS_MAP_BANK(config, m_membank[3]).set_map(&ti85_state::ti83p_banked_mem).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);

	T6A04(config, "t6a04", 0).set_size(96, 64);

	TI8X_LINK_PORT(config, m_link_port, default_ti8x_link_devices, nullptr);

	AMD_29F400T(config, m_flash);
}

void ti85_state::ti83pse(machine_config &config)
{
	ti83p(config);
	m_maincpu->set_clock(15000000);
	m_maincpu->set_addrmap(AS_IO, &ti85_state::ti83pse_io);

	m_membank[0]->set_map(&ti85_state::ti83pse_banked_mem);

	m_membank[1]->set_map(&ti85_state::ti83pse_banked_mem);

	m_membank[2]->set_map(&ti85_state::ti83pse_banked_mem);

	m_membank[3]->set_map(&ti85_state::ti83pse_banked_mem);

	MCFG_MACHINE_START_OVERRIDE(ti85_state, ti83pse )
	FUJITSU_29F160TE(config.replace(), m_flash, 0);
}

void ti85_state::ti84p(machine_config &config)
{
	ti83pse(config);
	m_membank[0]->set_map(&ti85_state::ti84p_banked_mem);

	m_membank[1]->set_map(&ti85_state::ti84p_banked_mem);

	m_membank[2]->set_map(&ti85_state::ti84p_banked_mem);

	m_membank[3]->set_map(&ti85_state::ti84p_banked_mem);

	MCFG_MACHINE_START_OVERRIDE(ti85_state, ti84p )
	AMD_29F800T(config.replace(), m_flash, 0);
}

void ti85_state::ti84pse(machine_config &config)
{
	ti83pse(config);
	MCFG_MACHINE_START_OVERRIDE(ti85_state, ti84pse )
}

void ti85_state::ti84pce(machine_config &config)
{
	ti84pse(config);

	EZ80(config.replace(), m_maincpu, 15000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &ti85_state::ti83p_asic_mem);
	m_maincpu->set_addrmap(AS_IO, &ti85_state::ti83pse_io);
}

void ti85_state::ti73(machine_config &config)
{
	ti83p(config);
	config.device_remove("linkport");
	//TI73SERIAL(config, "tiserial");
}

ROM_START (ti73)
	ROM_REGION (0x80000, "flash",0)
	ROM_DEFAULT_BIOS("v160")
	ROM_SYSTEM_BIOS( 0, "v160", "V 1.60" )
	ROMX_LOAD( "ti73v160.bin", 0x00000, 0x80000, CRC(bb0e3a16) SHA1(d62c2c7532698962818a747a7f32e35e41dfe338), ROM_BIOS(0) )
ROM_END

ROM_START (ti73b)
	ROM_REGION (0x80000, "flash",0)
	ROM_DEFAULT_BIOS("v191")
	ROM_SYSTEM_BIOS( 0, "v13004", "V 1.3004" )
	ROMX_LOAD( "ti73bv13004.bin", 0x00000, 0x80000, CRC(453701d8) SHA1(371d1f74a5e26ed749e12baac104f0069f329f44), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v140", "V 1.40" )
	ROMX_LOAD( "ti73bv140.bin", 0x00000, 0x80000, CRC(057e85ae) SHA1(4c45c8b26190e887bb9cdc3b185fd7e703922cbc), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v150", "V 1.50" )
	ROMX_LOAD( "ti73bv150.bin", 0x00000, 0x80000, CRC(c0edfb53) SHA1(1049363587b6d7985356aa2467a0118e6cc6dc37), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "v160", "V 1.60" )
	ROMX_LOAD( "ti73bv160.bin", 0x00000, 0x80000, CRC(28d07d9d) SHA1(7795720a68ca7017e682a8f2fe617b0cd758c008), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "v180", "V 1.80" )
	ROMX_LOAD( "ti73bv180.bin", 0x00000, 0x80000, CRC(7d3b9ee6) SHA1(93bfc8d951c526e1be7c0e1bebc43dd20cd4c3b1), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "v185", "V 1.85" )
	ROMX_LOAD( "ti73bv185.bin", 0x00000, 0x80000, CRC(4e7d68e7) SHA1(52a8b71fee7cda11935d6e89825842b4aad046dd), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6, "v190", "V 1.90" )
	ROMX_LOAD( "ti73bv190.bin", 0x00000, 0x80000, CRC(8726a8db) SHA1(636551d75fd0bccbbc89ea6749bb1153e9545e26), ROM_BIOS(6) )
	ROM_SYSTEM_BIOS( 7, "v191", "V 1.91" )
	ROMX_LOAD( "ti73bv191.bin", 0x00000, 0x80000, CRC(f3785d57) SHA1(ad73d0c61ef6a51a04902a9b30a58992a2d860c4), ROM_BIOS(7) )
ROM_END

ROM_START (ti81)
	ROM_REGION (0x08000, "bios",0)
	ROM_DEFAULT_BIOS("v18")
	ROM_SYSTEM_BIOS( 0, "v11", "V 1.1K" )
	ROMX_LOAD( "ti81v11k.bin", 0x00000, 0x8000, CRC(0b860a63) SHA1(84a71cfc8818ca4b7d0caa76ffbf6d0463eaf7c6), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v16", "V 1.6K" )
	ROMX_LOAD( "ti81v16k.bin", 0x00000, 0x8000, CRC(452ca838) SHA1(92649f0f3bce7d8829d950cecd6532d7f7db1297), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v18", "V 1.8K" )
	ROMX_LOAD( "ti81v18k.bin", 0x00000, 0x8000, CRC(94ac58e2) SHA1(ba915cfe2fe50a452ef8287db8f2244e29056d54), ROM_BIOS(2) )
	//No dumps 1.0, and 2.0 from ticalc.org, less sure about 1.6K
ROM_END

ROM_START (ti81v2)
	ROM_REGION (0x08000, "bios",0)
	ROM_DEFAULT_BIOS("v20")
	ROM_SYSTEM_BIOS( 0, "v20", "V 2.0V" )
	ROMX_LOAD( "ti81v20v.bin", 0x00000, 0x8000, CRC(cfbd12da) SHA1(d2a923526d98f1046fcb583e46951939ba66bdb9), ROM_BIOS(0) )
ROM_END

ROM_START (ti82)
	ROM_REGION (0x20000, "bios",0)
	ROM_DEFAULT_BIOS("v19")
	ROM_SYSTEM_BIOS( 0, "v16", "V 16.0" )
	ROMX_LOAD( "ti82v16.bin", 0x00000, 0x20000, CRC(e2f5721c) SHA1(df300ae52e105faf2785a8ae9f42e84e4308d460), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v17", "V 17.0" )
	ROMX_LOAD( "ti82v17.bin", 0x00000, 0x20000, CRC(0fc956d4) SHA1(77eef7d2db5ad1fb5de9129086a18428ddf66195), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v18", "V 18.0" )
	ROMX_LOAD( "ti82v18.bin", 0x00000, 0x20000, CRC(6a320f03) SHA1(9ee15ebf0a1f8bde5bef982b5db4ce120c605d29), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "v19", "V 19.0" )
	ROMX_LOAD( "ti82v19.bin", 0x00000, 0x20000, CRC(ed4cf9ff) SHA1(10dc2d01c62b4e971a6ed7ebc75ca0f2e3dc4f95), ROM_BIOS(3) )
	//Rom versions according to ticalc.org 3*, 4*, 7*, 8.0, 10.0, 12.0, 15.0, 16.0, 17.0, 18.0, 19.0, 19.006
ROM_END

ROM_START (ti83)
	ROM_REGION (0x40000, "bios",0)
	ROM_DEFAULT_BIOS("v110")
	ROM_SYSTEM_BIOS( 0, "v102", "V 1.02" )
	ROMX_LOAD( "ti83v102.bin", 0x00000, 0x40000, CRC(7ee5d27b) SHA1(ce08f6a808701fc6672230a790167ee485157561), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v103", "V 1.03" )
	ROMX_LOAD( "ti83v103.bin", 0x00000, 0x40000, CRC(926f72a4) SHA1(8399e384804d8d29866caa4c8763d7a61946a467), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v104", "V 1.04" )
	ROMX_LOAD( "ti83v104.bin", 0x00000, 0x40000, CRC(dccb73d3) SHA1(33877ff637dc5f4c5388799fd7e2159b48e72893), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "v106", "V 1.06" )
	ROMX_LOAD( "ti83v106.bin", 0x00000, 0x40000, CRC(2eae1cf0) SHA1(3d65c2a1b771ce8e5e5a0476ec1aa9c9cdc0e833), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "v107", "V 1.07" )
	ROMX_LOAD( "ti83v107.bin", 0x00000, 0x40000, CRC(4bf05697) SHA1(ef66dad3e7b2b6a86f326765e7dfd7d1a308ad8f), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "v108", "V 1.08" )
	ROMX_LOAD( "ti83v108.bin", 0x00000, 0x40000, CRC(0c6aafcc) SHA1(9c74f0b61655e9e160e92164db472ad7ee02b0f8), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6, "v110", "V 1.10" )
	ROMX_LOAD( "ti83v110.bin", 0x00000, 0x40000, CRC(7faee2d2) SHA1(25b373b58523647bb7b904001d391615e0b79bee), ROM_BIOS(6) )
	//Rom versions according to ticalc.org 1.02, 1.03, 1.04, 1.06, 1.07, 1.08, 1.10
ROM_END

ROM_START (ti83p)
	ROM_REGION (0x80000, "flash",0)
	ROM_DEFAULT_BIOS("v116")
	ROM_SYSTEM_BIOS( 0, "v103", "V 1.03" )
	ROMX_LOAD( "ti83pv103.bin", 0x00000, 0x80000, CRC(da466be0) SHA1(37eaeeb9fb5c18fb494e322b75070e80cc4d858e), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v110", "V 1.10" )
	ROMX_LOAD( "ti83pv110.bin", 0x00000, 0x80000, CRC(62683990) SHA1(f86cdefe4ed5ef9965cd9eb667cb859e2cb10e19), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v112", "V 1.12" )
	ROMX_LOAD( "ti83pv112.bin", 0x00000, 0x80000, CRC(ddca5026) SHA1(6615df5554076b6b81bd128bf847d2ff046e556b), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "v113", "V 1.13" )
	ROMX_LOAD( "ti83pv113.bin", 0x00000, 0x80000, CRC(30a243aa) SHA1(9b79e994ea1ce7af05b68f8ecee8b1b1fc3f0810), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "v114", "V 1.14" )
	ROMX_LOAD( "ti83pv114.bin", 0x00000, 0x80000, CRC(b32059c7) SHA1(46c66ba0421c03fc42f5afb06c7d3af812786140), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "v115", "V 1.15" )
	ROMX_LOAD( "ti83pv115.bin", 0x00000, 0x80000, CRC(9288029b) SHA1(8bd05fd47cab4028f275d1cc5383fd4f0e193474), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6, "v116", "V 1.16" )
	ROMX_LOAD( "ti83pv116.bin", 0x00000, 0x80000, CRC(0b7cd006) SHA1(290bc81159ea061d8ccb56a6f63e042f150afb32), ROM_BIOS(6) )
	//Missing 1.17, 1.18, and 1.19
ROM_END

ROM_START (ti83pb)
	ROM_REGION (0x80000, "flash",0)
	ROM_DEFAULT_BIOS("v119")
	ROM_SYSTEM_BIOS( 0, "v103", "V 1.03" )
	ROMX_LOAD( "ti83pbv103.bin", 0x00000, 0x80000, CRC(745472fa) SHA1(e1707e0b56e72bb126fa1dda430c659a726beaf7), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v110", "V 1.10" )
	ROMX_LOAD( "ti83pbv110.bin", 0x00000, 0x80000, CRC(edf9a1d9) SHA1(edbb725f12c10dd1dd8d5c4a4f836bf03659411d), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v112", "V 1.12" )
	ROMX_LOAD( "ti83pbv112.bin", 0x00000, 0x80000, CRC(ce3f9427) SHA1(b8b8cd806ceac68f2d35ef34e6695fa9ea2d8ad1), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "v113", "V 1.13" )
	ROMX_LOAD( "ti83pbv113.bin", 0x00000, 0x80000, CRC(3327c8c0) SHA1(07830de8efc99ea6ceab388e6c0603c28a23454f), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "v114", "V 1.14" )
	ROMX_LOAD( "ti83pbv114.bin", 0x00000, 0x80000, CRC(408134b9) SHA1(791ff9fc2e184d5048e349fb5b65830719d5199b), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "v115", "V 1.15" )
	ROMX_LOAD( "ti83pbv115.bin", 0x00000, 0x80000, CRC(a16a4bff) SHA1(a0374a5d5f25e3f9dc1c241447233cf3a23e7946), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6, "v116", "V 1.16" )
	ROMX_LOAD( "ti83pbv116.bin", 0x00000, 0x80000, CRC(b5e00ef6) SHA1(23b131263b696c03f778eb5d37411be9a86cf752), ROM_BIOS(6) )
	ROM_SYSTEM_BIOS( 7, "v118", "V 1.18" )
	ROMX_LOAD( "ti83pbv118.bin", 0x00000, 0x80000, CRC(0915b0a0) SHA1(48c270c383c2d05058693a5bf58d462936bbb335), ROM_BIOS(7) )
	ROM_SYSTEM_BIOS( 8, "v119", "V 1.19" )
	ROMX_LOAD( "ti83pbv119.bin", 0x00000, 0x80000, CRC(58f14c79) SHA1(1fddd44d54f3ff12bfb548fcb03ce36b5a4f295a), ROM_BIOS(8) )
	//Missing 1.17
ROM_END

ROM_START (ti85)
	ROM_REGION (0x20000, "bios",0)
	ROM_DEFAULT_BIOS("v100")
	ROM_SYSTEM_BIOS( 0, "v30a", "V 3.0A" )
	ROMX_LOAD( "ti85v30a.bin", 0x00000, 0x20000, CRC(de4c0b1a) SHA1(f4cf4b8309372dbe26187bb279545f5d4bd48fc1), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v40",  "V 4.0" )
	ROMX_LOAD( "ti85v40.bin",  0x00000, 0x20000, CRC(a1723a17) SHA1(ff5866636bb3f206a6bf39cc9c9dc8308332aaf0), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v50",  "V 5.0" )
	ROMX_LOAD( "ti85v50.bin",  0x00000, 0x20000, CRC(781fa403) SHA1(bf20d520d8efd7e5ae269789ca4b3c71848ac32a), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "v60",  "V 6.0" )
	ROMX_LOAD( "ti85v60.bin",  0x00000, 0x20000, CRC(b694a117) SHA1(36d58e2723e5ae4ffe0f8da691fa9a83bfe9e06b), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "v80",  "V 8.0" )
	ROMX_LOAD( "ti85v80.bin",  0x00000, 0x20000, CRC(7f296338) SHA1(765d5c612b6ffc0d1ded8f79bcbe880b1b562a98), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "v90",  "V 9.0" )
	ROMX_LOAD( "ti85v90.bin",  0x00000, 0x20000, CRC(6a0a94d0) SHA1(7742bf8a6929a21d06f306b494fc03b1fbdfe3e4), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6, "v100", "V 10.0" )
	ROMX_LOAD( "ti85v100.bin", 0x00000, 0x20000, CRC(053325b0) SHA1(36da1080c34e7b53cbe8463be5804e30e4a50dc8), ROM_BIOS(6) )
	//No_dumps 1.0, 2.0 and 7.0 according to ticalc.org
ROM_END

ROM_START (ti86)
	ROM_REGION (0x40000, "bios",0)
	ROM_DEFAULT_BIOS("v16")
	ROM_SYSTEM_BIOS( 0, "v12", "V 1.2" )
	ROMX_LOAD( "ti86v12.bin", 0x00000, 0x40000, CRC(bdf16105) SHA1(e40b22421c31bf0af104518b748ae79cd21d9c57), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v13", "V 1.3" )
	ROMX_LOAD( "ti86v13.bin", 0x00000, 0x40000, CRC(073ef70f) SHA1(5702d4bb835bdcbfa8075ffd620fca0eaf3a1592), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v14", "V 1.4" )
	ROMX_LOAD( "ti86v14.bin", 0x00000, 0x40000, CRC(fe6e2986) SHA1(23e0fb9a1763d5b9a7b0e593f09c2ff30c760866), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "v15", "V 1.5" )
	ROMX_LOAD( "ti86v15.bin", 0x00000, 0x40000, BAD_DUMP CRC(e6e10546) SHA1(5ca63fdfc965ae3fb8e0695263cf9da41f6ecb90), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "v16", "V 1.6" )
	ROMX_LOAD( "ti86v16.bin", 0x00000, 0x40000, CRC(37e02acc) SHA1(b5ad204885e5dde23a22f18f8d5eaffca69d638d), ROM_BIOS(4) )
	//Rom versions according to ticalc.org 1.2, 1.3, 1.4, 1.5, 1.6
ROM_END

ROM_START (ti83pse)
	ROM_REGION (0x200000, "flash", 0)
	ROM_SYSTEM_BIOS( 0, "v116", "V 1.16" )
	ROM_DEFAULT_BIOS("v116")
	ROMX_LOAD( "ti83psev116.bin", 0x00000, 0x200000, CRC(d2570863) SHA1(d4214b3c0ebb26e10fe95294ac72a90d2ba99537), ROM_BIOS(0) )
ROM_END

ROM_START (ti83pseb)
	ROM_REGION (0x200000, "flash", 0)
	ROM_DEFAULT_BIOS("v112")
	ROM_SYSTEM_BIOS( 0, "v112", "V 1.12" )
	ROMX_LOAD( "ti83psebv112.bin", 0x00000, 0x200000, CRC(e8cfcdb7) SHA1(322929d289c17c247da7da3674d6115f1740fa49), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v113", "V 1.13" )
	ROMX_LOAD( "ti83psebv113.bin", 0x00000, 0x200000, CRC(cf90e998) SHA1(29b92a32e3ceae7d918fc404fec50a53f35b574c), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v114", "V 1.14" )
	ROMX_LOAD( "ti83psebv114.bin", 0x00000, 0x200000, CRC(4842c167) SHA1(2a1938474df970f92ee2349912b7824685a4da99), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "v115", "V 1.15" )
	ROMX_LOAD( "ti83psebv115.bin", 0x00000, 0x200000, CRC(79a7bcbb) SHA1(1c47f2299eedde8db21b9ec469ba01a1b14533db), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "v116", "V 1.16" )
	ROMX_LOAD( "ti83psebv116.bin", 0x00000, 0x200000, CRC(f75b896f) SHA1(75c8356ee89f35cb197684f3581cbfa3904c2f0a), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "v118", "V 1.18" )
	ROMX_LOAD( "ti83psebv118.bin", 0x00000, 0x200000, CRC(0ad0a741) SHA1(cb83a6f1517fc5d34a29cdf4b1d30ea2762b2a95), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6, "v119", "V 1.19" )
	ROMX_LOAD( "ti83psebv119.bin", 0x00000, 0x200000, CRC(200dd7d0) SHA1(8177bc6d5489d575cbfa9a004d097fc08c6f8c86), ROM_BIOS(6) )
ROM_END

ROM_START (ti84pse)
	ROM_REGION (0x200000, "flash",0)
	ROM_DEFAULT_BIOS("v255mp")
	ROM_SYSTEM_BIOS( 0, "v221", "V 2.21" )
	ROMX_LOAD( "ti84psev221.bin", 0x00000, 0x200000, CRC(da8b3c8e) SHA1(736fae1929089167a3af6290e04e9278b0a3d1a6), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v222", "V 2.22" )
	ROMX_LOAD( "ti84psev222.bin", 0x00000, 0x200000, CRC(dc2931db) SHA1(319f1ec6accdbe2309f9ffcd8d9970fa2a422c4d), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v230", "V 2.30" )
	ROMX_LOAD( "ti84psev230.bin", 0x00000, 0x200000, CRC(8800c73a) SHA1(cb9ad540137ede275ff22e293ae0f7cc31b6663d), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "v240", "V 2.40" )
	ROMX_LOAD( "ti84psev240.bin", 0x00000, 0x200000, CRC(2aed41c4) SHA1(6886f4c07718f0dfa43b397ff492a6b4b06ded15), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "v241", "V 2.41" )
	ROMX_LOAD( "ti84psev241.bin", 0x00000, 0x200000, CRC(3dcb18ba) SHA1(728834cb426c09f6b00d1fd89e81eb154488854c), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "v243", "V 2.43" )
	ROMX_LOAD( "ti84psev243.bin", 0x00000, 0x200000, CRC(1e9707f8) SHA1(767a5238882d97fac550971adbfbe48f82f2772f), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6, "v253mp", "V 2.53MP" )
	ROMX_LOAD( "ti84psev253mp.bin", 0x00000, 0x200000, CRC(3e52683a) SHA1(80050ae2a8f128b291d3a8973ab32e879172f2b9), ROM_BIOS(6) )
	ROM_SYSTEM_BIOS( 7, "v255mp", "V 2.55MP" )
	ROMX_LOAD( "ti84psev255mp.bin", 0x00000, 0x200000, CRC(70439fdd) SHA1(201e585caa64836829ea57c1291c6136c778ef55), ROM_BIOS(7) )
ROM_END

ROM_START (ti84psev3)
	ROM_REGION (0x200000, "flash",0)
	ROM_DEFAULT_BIOS("v255mp")
	ROM_SYSTEM_BIOS( 0, "v255mp", "V 2.55MP" )
	ROMX_LOAD( "ti84psev3v255mp.bin", 0x00000, 0x200000, CRC(daa7cb89) SHA1(eabdc9b46a1cb7fef60b0fabf36ab7d484cdb3bf), ROM_BIOS(0) )
ROM_END

ROM_START (ti84pseb)
	ROM_REGION (0x200000, "flash",0)
	ROM_DEFAULT_BIOS("v255mp")
	ROM_SYSTEM_BIOS( 0, "v221", "V 2.21" )
	ROMX_LOAD( "ti84psebv221.bin", 0x00000, 0x200000, CRC(cff9a231) SHA1(132ff36d4ebed04452fc0b54341b29db882d1292), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v222", "V 2.22" )
	ROMX_LOAD( "ti84psebv222.bin", 0x00000, 0x200000, CRC(c95baf64) SHA1(167a8bc911fadd62e0b9eb2c4f3c96009795fb2f), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v230", "V 2.30" )
	ROMX_LOAD( "ti84psebv230.bin", 0x00000, 0x200000, CRC(f7e19a09) SHA1(7d800eb350d6c7dd9fd6aaab44de6c2de70f6f49), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "v240", "V 2.40" )
	ROMX_LOAD( "ti84psebv240.bin", 0x00000, 0x200000, CRC(550c1cf7) SHA1(4cfcb232a310d42d252d5481ad4417ea1f55288e), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "v241", "V 2.41" )
	ROMX_LOAD( "ti84psebv241.bin", 0x00000, 0x200000, CRC(422a4589) SHA1(c70b5c58ba723e60787f8a5b0caef94ee9cec087), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "v243", "V 2.43" )
	ROMX_LOAD( "ti84psebv243.bin", 0x00000, 0x200000, CRC(61765acb) SHA1(a725f58532706deff2f60d700030da0e99a2c21d), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6, "v253mp", "V 2.53MP" )
	ROMX_LOAD( "ti84psebv253mp.bin", 0x00000, 0x200000, CRC(41b33509) SHA1(92ef7dd17d8998f21a652c5d0c3f631fc993677f), ROM_BIOS(6) )
	ROM_SYSTEM_BIOS( 7, "v255mp", "V 2.55MP" )
	ROMX_LOAD( "ti84psebv255mp.bin", 0x00000, 0x200000, CRC(0fa2c2ee) SHA1(ac01dbe4c5fb0f5c83ddc2b7907647992717fbce), ROM_BIOS(7) )
ROM_END

ROM_START (ti84p)
	ROM_REGION (0x100000, "flash",0)
	ROM_DEFAULT_BIOS("v255mp")
	ROM_SYSTEM_BIOS( 0, "v221", "V 2.21" )
	ROMX_LOAD( "ti84pv221.bin", 0x00000, 0x100000, CRC(2f55c4d9) SHA1(04e91c5d089b4a56f12e949f0bd6936df6539d2a), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v222", "V 2.22" )
	ROMX_LOAD( "ti84pv222.bin", 0x00000, 0x100000, CRC(bdb80f06) SHA1(7c6e1557acd90bb649a353616d3fc96fcbb59b10), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v230", "V 2.30" )
	ROMX_LOAD( "ti84pv230.bin", 0x00000, 0x100000, CRC(ce3229f6) SHA1(abbb228cb6efbca724008d15ac724919821163d8), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "v240", "V 2.40" )
	ROMX_LOAD( "ti84pv240.bin", 0x00000, 0x100000, CRC(4d0b1e08) SHA1(53ae06d4c492402ce5ae999c9d8be7437c0797ed), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "v241", "V 2.41" )
	ROMX_LOAD( "ti84pv241.bin", 0x00000, 0x100000, CRC(5879e99a) SHA1(8e7651fe10b05e870ab5e211c1ff800e60f2a933), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "v243", "V 2.43" )
	ROMX_LOAD( "ti84pv243.bin", 0x00000, 0x100000, CRC(0234caea) SHA1(d50b70fd2e141e920807e083b53056f6bbe2870c), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6, "v253mp", "V 2.53MP" )
	ROMX_LOAD( "ti84pv253mp.bin", 0x00000, 0x100000, CRC(84ab2f4f) SHA1(4083f416c8698af4e1c512b21c526bc7e1aa1c23), ROM_BIOS(6) )
	ROM_SYSTEM_BIOS( 7, "v255mp", "V 2.55MP" )
	ROMX_LOAD( "ti84pv255mp.bin", 0x00000, 0x100000, CRC(4af31251) SHA1(8f67269346644b87e7cd0f353f5f4030e787cf57), ROM_BIOS(7) )
ROM_END

ROM_START (ti84pv2)
	ROM_REGION (0x100000, "flash",0)
	ROM_DEFAULT_BIOS("v255mp")
	ROM_SYSTEM_BIOS( 0, "v221", "V 2.21" )
	ROMX_LOAD( "ti84pv2v221.bin", 0x00000, 0x100000, CRC(5a23a22e) SHA1(2e1dcf163c0a0ea725ed9e264ba12c3cae67c969), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v222", "V 2.22" )
	ROMX_LOAD( "ti84pv2v222.bin", 0x00000, 0x100000, CRC(c8ce69f1) SHA1(aba6291dd020d375093bd56174416462f0e44130), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v230", "V 2.30" )
	ROMX_LOAD( "ti84pv2v230.bin", 0x00000, 0x100000, CRC(bb444f01) SHA1(f75c1866f27ff29ce8e113ff676ccdc6a53553d6), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "v240", "V 2.40" )
	ROMX_LOAD( "ti84pv2v240.bin", 0x00000, 0x100000, CRC(387d78ff) SHA1(4ea9c7d56ce545fc3a4fb19f13dcc53638bf439f), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "v241", "V 2.41" )
	ROMX_LOAD( "ti84pv2v241.bin", 0x00000, 0x100000, CRC(2d0f8f6d) SHA1(5048cebac3814ed56e82c9d6f094be8ffaa15e10), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "v243", "V 2.43" )
	ROMX_LOAD( "ti84v2pv243.bin", 0x00000, 0x100000, CRC(7742ac1d) SHA1(dc02d658412e7f00205906bdf8ba6b252a193506), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6, "v253mp", "V 2.53MP" )
	ROMX_LOAD( "ti84pv2v253mp.bin", 0x00000, 0x100000, CRC(f1dd49b8) SHA1(c9b592f3451778df1a4ada76cdd2f859c6c5df26), ROM_BIOS(6) )
	ROM_SYSTEM_BIOS( 7, "v255mp", "V 2.55MP" )
	ROMX_LOAD( "ti84pv2v255mp.bin", 0x00000, 0x100000, CRC(3f8574a6) SHA1(0f88e719512f2691fff6c8bcc89292158086f841), ROM_BIOS(7) )
ROM_END

ROM_START (ti84pov2)
	ROM_REGION (0x100000, "flash",0)
	ROM_DEFAULT_BIOS("v255orn")
	ROM_SYSTEM_BIOS( 0, "v255orn", "V 2.55MP/ORn" )
	ROMX_LOAD( "ti84pov2v255orn.bin", 0x00000, 0x100000, CRC(13d7d311) SHA1(c0fd22cd822d77dde0b037c33123178e30275d27), ROM_BIOS(0) )
ROM_END

ROM_START (ti84pv3)
	ROM_REGION (0x100000, "flash",0)
	ROM_DEFAULT_BIOS("v255mp")
	ROM_SYSTEM_BIOS( 0, "v255mp", "V 2.55MP" )
	ROMX_LOAD( "ti84pv3v255mp.bin", 0x00000, 0x100000, CRC(a9b5d5a6) SHA1(d500540feca974f6e8fa269981cfb25dc951c338), ROM_BIOS(0) )
ROM_END

ROM_START (ti84pob)
	ROM_REGION (0x100000, "flash",0)
	ROM_DEFAULT_BIOS("v255orn")
	ROM_SYSTEM_BIOS( 0, "v255orn", "V 2.55/ORn" )
	ROMX_LOAD( "ti84pobv255orn.bin", 0x00000, 0x100000, CRC(b8992646) SHA1(643c2f03d0743ffe61d9ca5ed813bc748add7c44), ROM_BIOS(0) )
ROM_END

ROM_START (ti84pov3)
	ROM_REGION (0x100000, "flash",0)
	ROM_DEFAULT_BIOS("v255orn")
	ROM_SYSTEM_BIOS( 0, "v255orn", "V 2.55/ORn" )
	ROMX_LOAD( "ti84pov3v255orn.bin", 0x00000, 0x100000, CRC(85e77211) SHA1(32e1962f33ec1c3b76921cda2a96e95fd0a6c805), ROM_BIOS(0) )
ROM_END

ROM_START (ti84pb)
	ROM_REGION (0x100000, "flash",0)
	ROM_DEFAULT_BIOS("v255mp")
	ROM_SYSTEM_BIOS( 0, "v221", "V 2.21" )
	ROMX_LOAD( "ti84pbv221.bin", 0x00000, 0x100000, CRC(f16d5779) SHA1(8c81ea6046863a91ab50222f2dc4c4fa73b08e8f), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v222", "V 2.22" )
	ROMX_LOAD( "ti84pbv222.bin", 0x00000, 0x100000, CRC(63809ca6) SHA1(71a859ee1b8c23b8c09c718d1d96623e14a2728f), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v230", "V 2.30" )
	ROMX_LOAD( "ti84pbv230.bin", 0x00000, 0x100000, CRC(100aba56) SHA1(d0a34121dcc437f2df60a646b879442800912fd9), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "v240", "V 2.40" )
	ROMX_LOAD( "ti84pbv240.bin", 0x00000, 0x100000, CRC(93338da8) SHA1(37ffe852928124fab1bd61d66c44a4bb356b60d6), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "v241", "V 2.41" )
	ROMX_LOAD( "ti84pbv241.bin", 0x00000, 0x100000, CRC(86417a3a) SHA1(3be9456268b9c0b2e5cf3a65af2b148ef74ce89b), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "v243", "V 2.43" )
	ROMX_LOAD( "ti84pbv243.bin", 0x00000, 0x100000, CRC(dc0c594a) SHA1(2779db4987e22b3e3d946cecf5eb3942a0478eaa), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6, "v253mp", "V 2.53MP" )
	ROMX_LOAD( "ti84pbv253mp.bin", 0x00000, 0x100000, CRC(5a93bcef) SHA1(3535a0bcbed9d7949c2791695a26e4b2db1af8ba), ROM_BIOS(6) )
	ROM_SYSTEM_BIOS( 7, "v255mp", "V 2.55MP" )
	ROMX_LOAD( "ti84pbv255mp.bin", 0x00000, 0x100000, CRC(94cb81f1) SHA1(5bf30a7ebbebfa90f221cdddc931ae0b96c419db), ROM_BIOS(7) )
ROM_END

ROM_START (ti84pcse)
	ROM_REGION (0x400000, "flash",0)
	ROM_DEFAULT_BIOS("v42")
	ROM_SYSTEM_BIOS( 0, "v40", "V 4.0" )
	ROMX_LOAD( "ti84pcsev40.bin", 0x00000, 0x400000, CRC(e0b8ec78) SHA1(a4ffdfa0d2a8fc1b1356429675efc96b4f25fbc5), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v42", "V 4.2" )
	ROMX_LOAD( "ti84pcsev42.bin", 0x00000, 0x400000, CRC(57d5373d) SHA1(06acbd22c9cb31320e022791ac03ba695f058654), ROM_BIOS(1) )
ROM_END

ROM_START (ti84pcsev2)
	ROM_REGION (0x400000, "flash",0)
	ROM_DEFAULT_BIOS("v42")
	ROM_SYSTEM_BIOS( 0, "v42", "V 4.2" )
	ROMX_LOAD( "ti84pcsev42.bin", 0x00000, 0x400000, CRC(4b6c2342) SHA1(e2a9d0124f852af79643438c994f13abc47e07af), ROM_BIOS(0) )
ROM_END

ROM_START (ti84pce)
	ROM_REGION (0x400000, "flash",0)
	ROM_DEFAULT_BIOS("v530")
	ROM_SYSTEM_BIOS( 0, "v500", "V 5.0.0.0089" )
	ROMX_LOAD( "ti84pcev500.bin", 0x00000, 0x400000, CRC(e31ecdf9) SHA1(7f93a2e17b75debdeb5704e07092c48b3abfec9e), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v510", "V 5.1.0.0110" )
	ROMX_LOAD( "ti84pcev510.bin", 0x00000, 0x400000, CRC(042f7031) SHA1(6754edc7aefc9f74247bf5ff60e7546f77cd2898), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v515", "V 5.1.5.0019" )
	ROMX_LOAD( "ti84pcev515.bin", 0x00000, 0x400000, CRC(2a958b6a) SHA1(6302ab3b4e3fca1ee6a05a3441b086ef7a57bee8), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "v520", "V 5.2.0.0035" )
	ROMX_LOAD( "ti84pcev520.bin", 0x00000, 0x400000, CRC(a59c6633) SHA1(d02f20aa3c895254a0974db7e424dd91d075f859), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "v521", "V 5.2.1.0042" )
	ROMX_LOAD( "ti84pcev521.bin", 0x00000, 0x400000, CRC(89bc2ae1) SHA1(b0b83b2b0158e5b382fc12af95aa2a2e41f3ce6d), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "v522", "V 5.2.2.0043" )
	ROMX_LOAD( "ti84pcev522.bin", 0x00000, 0x400000, CRC(49ce1768) SHA1(f949c8f2832edd33a1b0dd4da0ab4c1f23e47b21), ROM_BIOS(5) )
	ROM_SYSTEM_BIOS( 6, "v530", "V 5.3.0.0042" )
	ROMX_LOAD( "ti84pcev530.bin", 0x00000, 0x400000, CRC(c72f36b8) SHA1(6856fb2a9d0a2e338a89b91bb7680180a69482d3), ROM_BIOS(6) )
	ROM_SYSTEM_BIOS( 7, "v531", "V 5.3.1.0058" )
	ROMX_LOAD( "ti84pcev531.bin", 0x00000, 0x400000, CRC(6d269f68) SHA1(9f9321a0cff17c331c92be127ec67ef67317968b), ROM_BIOS(7) )
ROM_END

ROM_START (ti83pcev15)
	ROM_REGION (0x400000, "flash",0)
	ROM_DEFAULT_BIOS("v530") // 5.3.0.0042 is the default because 5.3.1.0058 disables some features
	ROM_SYSTEM_BIOS( 0, "v515", "V 5.1.5.0019" )
	ROMX_LOAD( "ti83pcev15v515.bin", 0x00000, 0x400000, CRC(f924d8e6) SHA1(ffb100c0d0478c414e7ba4dd9d73791d026b40ca), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v520", "V 5.2.0.0035" )
	ROMX_LOAD( "ti83pcev15v520.bin", 0x00000, 0x400000, CRC(a403db1a) SHA1(a565d96e75bed354483c6904b9ee2b8054adc31e), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v521", "V 5.2.1.0042" )
	ROMX_LOAD( "ti83pcev15v521.bin", 0x00000, 0x400000, CRC(1dc2e3e3) SHA1(d8e44e1a8a6591b289766a881a81d33eca9a0ecc), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "v522", "V 5.2.2.0043" )
	ROMX_LOAD( "ti83pcev15v522.bin", 0x00000, 0x400000, CRC(6971746d) SHA1(321c7aeda6af8b42742d080e802979188421e06a), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "v530", "V 5.3.0.0042" )
	ROMX_LOAD( "ti83pcev15v530.bin", 0x00000, 0x400000, CRC(08ae7388) SHA1(6d9d98d090ac1b250d1f8ba8ef7c26eb448e7f8c), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "v531", "V 5.3.1.0058" )
	ROMX_LOAD( "ti83pcev15v531.bin", 0x00000, 0x400000, CRC(6643adb3) SHA1(b380e15946c1749a56600d18fee7d9d3c658dee3), ROM_BIOS(5) )
ROM_END

ROM_START (ti84pcev15)
	ROM_REGION (0x400000, "flash",0)
	ROM_DEFAULT_BIOS("v530") // 5.3.0.0042 is the default because 5.3.1.0058 disables some features
	ROM_SYSTEM_BIOS( 0, "v515", "V 5.1.5.0019" )
	ROMX_LOAD( "ti84pcev15v515.bin", 0x00000, 0x400000, CRC(0318a913) SHA1(44256e23708c71b8f63660d3100e767d597c377f), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v520", "V 5.2.0.0035" )
	ROMX_LOAD( "ti84pcev15v520.bin", 0x00000, 0x400000, CRC(c2029323) SHA1(827c1e7ead58f4eabe5b7b942d0f24abd46f1633), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v521", "V 5.2.1.0042" )
	ROMX_LOAD( "ti84pcev15v521.bin", 0x00000, 0x400000, CRC(880eefc9) SHA1(3449363553c093b6bb555306d91a4dd6dac8e891), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "v522", "V 5.2.2.0043" )
	ROMX_LOAD( "ti84pcev15v522.bin", 0x00000, 0x400000, CRC(b95fefd9) SHA1(db1515b3bd4eebeb5ee3c29597991fc7f1e8b84e), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "v530", "V 5.3.0.0042" )
	ROMX_LOAD( "ti84pcev15v530.bin", 0x00000, 0x400000, CRC(0148cc26) SHA1(72a10379bbd9d427c6e73afa9fe316cbd502f53c), ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "v531", "V 5.3.1.0058" )
	ROMX_LOAD( "ti84pcev15v531.bin", 0x00000, 0x400000, CRC(86511ea0) SHA1(ff14ec454fd1e0a2c436b4eed1eefca0d16aabfb), ROM_BIOS(5) )
ROM_END

ROM_START (ti84pcev30)
	ROM_REGION (0x400000, "flash",0)
	ROM_DEFAULT_BIOS("v530") // 5.3.0.0042 is the default because 5.3.1.0058 disables some features
	ROM_SYSTEM_BIOS( 0, "v530", "V 5.3.0.0042" )
	ROMX_LOAD( "ti84pcev30v530.bin", 0x00000, 0x400000, CRC(cc7a7047) SHA1(0d348e60dc57276b1f8d5ff87935e47cdd27455c), ROM_BIOS(0) )
ROM_END

//    YEAR  NAME        PARENT   COMPAT  MACHINE  INPUT  STATE       INIT        COMPANY              FULLNAME                                           FLAGS
COMP( 201?, ti84pob,    ti84p,   0,      ti84p,   ti82,  ti85_state, empty_init, "Texas Instruments", "Orion TI-84 Plus (bootleg)",                      MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 201?, ti84pov2,   ti84p,   0,      ti84p,   ti82,  ti85_state, empty_init, "Texas Instruments", "Orion TI-84 Plus (Boot Code 1.02)",               MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 201?, ti84pov3,   ti84p,   0,      ti84p,   ti82,  ti85_state, empty_init, "Texas Instruments", "Orion TI-84 Plus (Boot Code 1.03)",               MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1998, ti73,       0,       0,      ti73,    ti82,  ti85_state, empty_init, "Texas Instruments", "TI-73 Explorer",                                  MACHINE_NO_SOUND_HW )
COMP( 20??, ti73b,      ti73,    0,      ti73,    ti82,  ti85_state, empty_init, "Texas Instruments", "TI-73 Explorer (bootleg)",                        MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )
COMP( 1990, ti81,       0,       0,      ti81,    ti81,  ti85_state, empty_init, "Texas Instruments", "TI-81",                                           MACHINE_NO_SOUND_HW )
COMP( 1994, ti81v2,     ti81,    0,      ti81v2,  ti81,  ti85_state, empty_init, "Texas Instruments", "TI-81 v2.0",                                      MACHINE_NO_SOUND_HW )
COMP( 1993, ti82,       0,       0,      ti82,    ti82,  ti85_state, empty_init, "Texas Instruments", "TI-82",                                           MACHINE_NO_SOUND_HW )
COMP( 1996, ti83,       0,       0,      ti83,    ti83,  ti85_state, empty_init, "Texas Instruments", "TI-83",                                           MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )
COMP( 1999, ti83p,      0,       0,      ti83p,   ti82,  ti85_state, empty_init, "Texas Instruments", "TI-83 Plus (Boot Code 1.00)",                     MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )
COMP( 20??, ti83pb,     ti83p,   0,      ti83p,   ti82,  ti85_state, empty_init, "Texas Instruments", "TI-83 Plus (bootleg)",                            MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )
COMP( 2001, ti83pse,    0,       0,      ti83pse, ti82,  ti85_state, empty_init, "Texas Instruments", "TI-83 Plus Silver Edition (Boot Code 1.00)",      MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )
COMP( 20??, ti83pseb,   ti83pse, 0,      ti83pse, ti82,  ti85_state, empty_init, "Texas Instruments", "TI-83 Plus Silver Edition (bootleg)",             MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )
COMP( 2015, ti83pcev15, ti84pse, 0,      ti84pce, ti82,  ti85_state, empty_init, "Texas Instruments", "TI-83 Premium CE (Boot Code 5.1.5.0014)",         MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )
COMP( 2004, ti84p,      0,       0,      ti84p,   ti82,  ti85_state, empty_init, "Texas Instruments", "TI-84 Plus (Boot Code 1.00)",                     MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )
COMP( 200?, ti84pv2,    ti84p,   0,      ti84p,   ti82,  ti85_state, empty_init, "Texas Instruments", "TI-84 Plus (Boot Code 1.02)",                     MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )
COMP( 2011, ti84pv3,    ti84p,   0,      ti84p,   ti82,  ti85_state, empty_init, "Texas Instruments", "TI-84 Plus (Boot Code 1.03)",                     MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )
COMP( 20??, ti84pb,     ti84p,   0,      ti84p,   ti82,  ti85_state, empty_init, "Texas Instruments", "TI-84 Plus (bootleg)",                            MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )
COMP( 2013, ti84pcse,   ti84pse, 0,      ti84pse, ti82,  ti85_state, empty_init, "Texas Instruments", "TI-84 Plus C Silver Edition (Boot Code 4.0)",     MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )
COMP( 201?, ti84pcsev2, ti84pse, 0,      ti84pse, ti82,  ti85_state, empty_init, "Texas Instruments", "TI-84 Plus C Silver Edition (Boot Code 4.2)",     MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )
COMP( 2015, ti84pce,    ti84pse, 0,      ti84pce, ti82,  ti85_state, empty_init, "Texas Instruments", "TI-84 Plus CE (Boot Code 5.0.0.0089)",            MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )
COMP( 2016, ti84pcev15, ti84pse, 0,      ti84pce, ti82,  ti85_state, empty_init, "Texas Instruments", "TI-84 Plus CE (Boot Code 5.1.5.0014)",            MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )
COMP( 2017, ti84pcev30, ti84pse, 0,      ti84pce, ti82,  ti85_state, empty_init, "Texas Instruments", "TI-84 Plus CE (Boot Code 5.3.0.0037)",            MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )
COMP( 2004, ti84pse,    0,       0,      ti84pse, ti82,  ti85_state, empty_init, "Texas Instruments", "TI-84 Plus Silver Edition (Boot Code 1.00)",      MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )
COMP( 2011, ti84psev3,  ti84pse, 0,      ti84pse, ti82,  ti85_state, empty_init, "Texas Instruments", "TI-84 Plus Silver Edition (Boot Code 1.03)",      MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )
COMP( 20??, ti84pseb,   ti84pse, 0,      ti84pse, ti82,  ti85_state, empty_init, "Texas Instruments", "TI-84 Plus Silver Edition (bootleg)",             MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING )
COMP( 1992, ti85,       0,       0,      ti85d,   ti85,  ti85_state, empty_init, "Texas Instruments", "TI-85",                                           MACHINE_NO_SOUND_HW )
COMP( 1997, ti86,       0,       0,      ti86,    ti85,  ti85_state, empty_init, "Texas Instruments", "TI-86",                                           MACHINE_NO_SOUND_HW )



ti89.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

    TI-89, TI-92, TI-92 plus, Voyage 200 PLT and TI-89 Titanium

    Driver by Sandro Ronco

    Note:
     -NVRAM works only if the calculator is turned off (2nd + ON) before closing MESS

    TODO:
     -Link
     -HW 3 I/O port
     -RTC
     -LCD contrast

****************************************************************************/

#include "emu.h"

#include "cpu/m68000/m68000.h"
#include "machine/intelfsh.h"
#include "machine/nvram.h"
#include "machine/timer.h"

#include "emupal.h"
#include "screen.h"


namespace {

class ti68k_state : public driver_device
{
public:
	ti68k_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_flash(*this, "flash")
		, m_rom_base(*this, "flash")
		, m_ram_base(*this, "nvram")
		, m_inputs(*this, "IN.%u", 0)
	{ }

	void v200(machine_config &config);
	void ti92(machine_config &config);
	void ti89(machine_config &config);
	void ti92p(machine_config &config);
	void ti89t(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(ti68k_on_key);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// hardware versions
	enum { HW1=1, HW2, HW3, HW4 };

	required_device<cpu_device> m_maincpu;
	optional_device<intelfsh16_device> m_flash;
	required_region_ptr<uint16_t> m_rom_base;
	required_shared_ptr<uint16_t> m_ram_base;
	required_ioport_array<10> m_inputs;

	// HW specifications
	uint8_t m_hw_version = 0;
	bool m_ram_enabled = false;

	// keyboard
	uint16_t m_kb_mask = 0;
	uint8_t m_on_key = 0;

	// LCD
	uint8_t m_lcd_on = 0;
	uint32_t m_lcd_base = 0;
	uint16_t m_lcd_width = 0;
	uint16_t m_lcd_height = 0;
	uint16_t m_lcd_contrast = 0;

	// I/O
	uint16_t m_io_hw1[0x10]{};
	uint16_t m_io_hw2[0x80]{};

	// Timer
	uint8_t m_timer_on = 0;
	uint8_t m_timer_val = 0;
	uint16_t m_timer_mask = 0;
	uint64_t m_timer = 0;

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	uint8_t keypad_r();
	void ti68k_io_w(offs_t offset, uint16_t data);
	uint16_t ti68k_io_r(offs_t offset);
	void ti68k_io2_w(offs_t offset, uint16_t data);
	uint16_t ti68k_io2_r(offs_t offset);
	uint16_t rom_r(offs_t offset);
	uint16_t reset_overlay_r(offs_t offset);
	void ti68k_palette(palette_device &palette) const;

	TIMER_DEVICE_CALLBACK_MEMBER(ti68k_timer_callback);

	void ti89_mem(address_map &map) ATTR_COLD;
	void ti89t_mem(address_map &map) ATTR_COLD;
	void ti92_mem(address_map &map) ATTR_COLD;
	void ti92p_mem(address_map &map) ATTR_COLD;
	void v200_mem(address_map &map) ATTR_COLD;
};


uint8_t ti68k_state::keypad_r()
{
	uint8_t data = 0xff;

	for (int i = 0; i < 10; i++)
		if (!BIT(m_kb_mask, i))
			data &= m_inputs[i]->read();

	return data;
}


void ti68k_state::ti68k_io_w(offs_t offset, uint16_t data)
{
	switch(offset & 0x0f)
	{
		case 0x00:
			m_lcd_contrast = (m_lcd_contrast & 0xfe) | BIT(data, 5);
			break;
		case 0x02:
			if (!(data & 0x10) && data != m_io_hw1[offset])
				m_maincpu->suspend(SUSPEND_REASON_DISABLE, 1);
			break;
		case 0x08:
				m_lcd_base = data << 3;
			break;
		case 0x09:
				m_lcd_width = (0x40 - ((data >> 8) & 0xff)) * 0x10;
				m_lcd_height = 0x100 - (data & 0xff);
			break;
		case 0x0a:
			m_timer_on = BIT(data ,1);
			switch((data >> 4) & 0x03)
			{
				case 0: m_timer_mask = 0x0000; break;
				case 1: m_timer_mask = 0x000f; break;
				case 2: m_timer_mask = 0x007f; break;
				case 3: m_timer_mask = 0x1fff; break;
			}
			break;
		case 0x0b:
			m_timer_val = data & 0xff;
			break;
		case 0x0c:
			m_kb_mask = data & 0x03ff;
			break;
		case 0x0e:
			m_lcd_on = (((data >> 8) & 0x3c) == 0x3c) ? 0 : 1;
			m_lcd_contrast = (m_lcd_contrast & 0x01) | ((data & 0x0f) << 1);
			break;
		default: break;
	}
	m_io_hw1[offset & 0x0f] = data;
}


uint16_t ti68k_state::ti68k_io_r(offs_t offset)
{
	uint16_t data;

	switch (offset & 0x0f)
	{
		case 0x00:
			data = 0x0400 | ((m_lcd_contrast & 1) << 13);
			break;
		case 0x0b:
			data = m_timer_val;
			break;
		case 0x0d:
			data = ((!m_on_key) << 9) | keypad_r();
			break;
		default:
			data= m_io_hw1[offset & 0x0f];
	}
	return data;
}


void ti68k_state::ti68k_io2_w(offs_t offset, uint16_t data)
{
	switch(offset & 0x7f)
	{
		case 0x0b:
			m_lcd_base = 0x4c00 + 0x1000 * (data & 0x03);
			break;
		case 0x0e:
			m_lcd_on = (data & 0x02) ? 1 : 0;
			break;
		default: break;
	}
	m_io_hw2[offset & 0x7f] = data;
}


uint16_t ti68k_state::ti68k_io2_r(offs_t offset)
{
	uint16_t data;

	switch (offset & 0x7f)
	{
		default:
			data= m_io_hw2[offset & 0x7f];
	}
	return data;
}


uint16_t ti68k_state::rom_r(offs_t offset)
{
	return m_rom_base[offset];
}


uint16_t ti68k_state::reset_overlay_r(offs_t offset)
{
	if (m_ram_enabled)
		return m_ram_base[offset];
	else
	{
		// FIXME: probably triggered by something else
		if (offset == 3 && !machine().side_effects_disabled())
			m_ram_enabled = true;

		// FIXME: this reset vector happens to work for all Flash systems, but may not be loaded
		// first. It is algorithmically located by the preceding initialization code which looks
		// for the string of four CC bytes preceding it. This code has not been enabled since it
		// also contains a "Certificate Memory" self-test which fails.
		if (m_flash.found())
			return m_flash->read(offset + 0x12088/2);
		else
			return m_rom_base[offset];
	}
}


TIMER_DEVICE_CALLBACK_MEMBER(ti68k_state::ti68k_timer_callback)
{
	m_timer++;

	if (m_timer_on)
	{
		if (!(m_timer & m_timer_mask) && BIT(m_io_hw1[0x0a], 3))
		{
			if (m_timer_val)
				m_timer_val++;
			else
				m_timer_val = (m_io_hw1[0x0b]) & 0xff;
		}

		if (!BIT(m_io_hw1[0x0a], 7) && ((m_hw_version == HW1) || (!BIT(m_io_hw1[0x0f], 2) && !BIT(m_io_hw1[0x0f], 1))))
		{
			if (!(m_timer & 0x003f))
				m_maincpu->set_input_line(M68K_IRQ_1, HOLD_LINE);

			if (!(m_timer & 0x3fff) && !BIT(m_io_hw1[0x0a], 3))
				m_maincpu->set_input_line(M68K_IRQ_3, HOLD_LINE);

			if (!(m_timer & m_timer_mask) && BIT(m_io_hw1[0x0a], 3) && m_timer_val == 0)
				m_maincpu->set_input_line(M68K_IRQ_5, HOLD_LINE);
		}
	}

	if (keypad_r() != 0xff)
		m_maincpu->set_input_line(M68K_IRQ_2, HOLD_LINE);
}


void ti68k_state::ti92_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x0fffff).ram().share("nvram");
	map(0x000000, 0x000007).r(FUNC(ti68k_state::reset_overlay_r));
	map(0x200000, 0x5fffff).unmaprw();   // ROM
	map(0x600000, 0x6fffff).rw(FUNC(ti68k_state::ti68k_io_r), FUNC(ti68k_state::ti68k_io_w));
}


void ti68k_state::ti89_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x0fffff).ram().share("nvram");
	map(0x000000, 0x000007).r(FUNC(ti68k_state::reset_overlay_r));
	map(0x200000, 0x3fffff).rw(m_flash, FUNC(intelfsh16_device::read), FUNC(intelfsh16_device::write));
	map(0x400000, 0x5fffff).noprw();
	map(0x600000, 0x6fffff).rw(FUNC(ti68k_state::ti68k_io_r), FUNC(ti68k_state::ti68k_io_w));
	map(0x700000, 0x7fffff).rw(FUNC(ti68k_state::ti68k_io2_r), FUNC(ti68k_state::ti68k_io2_w));
}


void ti68k_state::ti92p_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x0fffff).ram().share("nvram");
	map(0x000000, 0x000007).r(FUNC(ti68k_state::reset_overlay_r));
	map(0x200000, 0x3fffff).noprw();
	map(0x400000, 0x5fffff).rw(m_flash, FUNC(intelfsh16_device::read), FUNC(intelfsh16_device::write));
	map(0x600000, 0x6fffff).rw(FUNC(ti68k_state::ti68k_io_r), FUNC(ti68k_state::ti68k_io_w));
	map(0x700000, 0x7fffff).rw(FUNC(ti68k_state::ti68k_io2_r), FUNC(ti68k_state::ti68k_io2_w));
}


void ti68k_state::v200_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x0fffff).ram().share("nvram");
	map(0x000000, 0x000007).r(FUNC(ti68k_state::reset_overlay_r));
	map(0x200000, 0x5fffff).rw(m_flash, FUNC(intelfsh16_device::read), FUNC(intelfsh16_device::write));
	map(0x600000, 0x6fffff).rw(FUNC(ti68k_state::ti68k_io_r), FUNC(ti68k_state::ti68k_io_w));
	map(0x700000, 0x70ffff).rw(FUNC(ti68k_state::ti68k_io2_r), FUNC(ti68k_state::ti68k_io2_w));
}


void ti68k_state::ti89t_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x0fffff).ram().mirror(0x200000).share("nvram");
	map(0x000000, 0x000007).r(FUNC(ti68k_state::reset_overlay_r));
	map(0x600000, 0x6fffff).rw(FUNC(ti68k_state::ti68k_io_r), FUNC(ti68k_state::ti68k_io_w));
	map(0x700000, 0x70ffff).rw(FUNC(ti68k_state::ti68k_io2_r), FUNC(ti68k_state::ti68k_io2_w));
	map(0x800000, 0xbfffff).rw(m_flash, FUNC(intelfsh16_device::read), FUNC(intelfsh16_device::write));
	map(0xc00000, 0xffffff).noprw();
}


INPUT_CHANGED_MEMBER(ti68k_state::ti68k_on_key)
{
	m_on_key = newval;

	if (m_on_key)
	{
		if (m_maincpu->suspended(SUSPEND_REASON_DISABLE))
			m_maincpu->resume(SUSPEND_REASON_DISABLE);

		m_maincpu->set_input_line(M68K_IRQ_6, HOLD_LINE);
	}
}


/* Input ports for TI-89 and TI-89 Titanium*/
static INPUT_PORTS_START (ti8x)
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2nd") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3nd") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ALPHA") PORT_CODE(KEYCODE_CAPSLOCK)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER  (ENTRY)") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("+") PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("*") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CLEAR") PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5)

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("(-)") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("<--") PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4)

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(")") PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CATALOG") PORT_CODE(KEYCODE_F8)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3)

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("(") PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Mode") PORT_CODE(KEYCODE_F12)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)

	PORT_START("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("APPS") PORT_CODE(KEYCODE_F11)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("STORE") PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("EE") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("|") PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("=") PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("HOME") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)

	PORT_START("IN.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ON") PORT_CODE(KEYCODE_F10) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ti68k_state::ti68k_on_key), 0)
	PORT_BIT(0xfc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IN.7")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IN.8")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("IN.9")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

/* Input ports for TI-92 and TI92 Plus*/
static INPUT_PORTS_START (ti9x)
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2nd") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3nd") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Clock") PORT_CODE(KEYCODE_F11)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F8") PORT_CODE(KEYCODE_F8)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("STORE") PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F7") PORT_CODE(KEYCODE_F7)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("(") PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(")") PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA)

	PORT_START("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F6") PORT_CODE(KEYCODE_F6)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SIN") PORT_CODE(KEYCODE_PGUP)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("COS") PORT_CODE(KEYCODE_END)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TAN") PORT_CODE(KEYCODE_INSERT)

	PORT_START("IN.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LN") PORT_CODE(KEYCODE_PGDN)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)

	PORT_START("IN.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("=") PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CLEAR") PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("APPS") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("*") PORT_CODE(KEYCODE_ASTERISK)

	PORT_START("IN.8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("<--") PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("o") PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("+") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MODE") PORT_CODE(KEYCODE_F12)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ON") PORT_CODE(KEYCODE_F10) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ti68k_state::ti68k_on_key), 0)

	PORT_START("IN.9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("(-)") PORT_CODE(KEYCODE_MINUS_PAD)
INPUT_PORTS_END


void ti68k_state::machine_start()
{
	if (m_flash.found())
	{
		uint32_t base = ((((m_rom_base[0x82]) << 16) | m_rom_base[0x83]) & 0xffff)>>1;

		if (m_rom_base[base] >= 8)
			m_hw_version = ((m_rom_base[base + 0x0b]) << 16) | m_rom_base[base + 0x0c];

		if (!m_hw_version)
			m_hw_version = HW1;
	}
	else
	{
		m_hw_version = HW1;
		uint32_t initial_pc = ((m_rom_base[2]) << 16) | m_rom_base[3];

		m_maincpu->space(AS_PROGRAM).unmap_read(0x200000, 0x5fffff);

		if (initial_pc > 0x400000)
		{
			m_maincpu->space(AS_PROGRAM).install_read_handler(0x400000, 0x5fffff, read16sm_delegate(*this, FUNC(ti68k_state::rom_r)));
		}
		else
		{
			m_maincpu->space(AS_PROGRAM).install_read_handler(0x200000, 0x3fffff, read16sm_delegate(*this, FUNC(ti68k_state::rom_r)));
		}
	}

	logerror("HW=v%x, Type=%s\n", m_hw_version, m_flash.found() ? "Flash" : "ROM");
}


void ti68k_state::machine_reset()
{
	m_ram_enabled = false;

	m_kb_mask = 0xff;
	m_on_key = 0;
	m_lcd_base = 0;
	m_lcd_width = 0;
	m_lcd_height = 0;
	m_lcd_on = 0;
	m_lcd_contrast = 0;
	std::fill(std::begin(m_io_hw1), std::end(m_io_hw1), 0);
	std::fill(std::begin(m_io_hw2), std::end(m_io_hw2), 0);
	m_timer_on = 0;
	m_timer_mask = 0xf;
	m_timer_val = 0;
}

/* video */
uint32_t ti68k_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	/* preliminary implementation, doesn't use the contrast value */
	uint8_t const width = screen.width();
	uint8_t const height = screen.height();

	if (!m_lcd_on || !m_lcd_base)
		bitmap.fill(0);
	else
		for (uint8_t y = 0; y < height; y++)
			for (uint8_t x = 0; x < width / 8; x++)
			{
				uint8_t s_byte= m_maincpu->space(AS_PROGRAM).read_byte(m_lcd_base + y * (width/8) + x);
				for (uint8_t b = 0; b<8; b++)
					bitmap.pix(y, x * 8 + (7 - b)) = BIT(s_byte, b);
			}

	return 0;
}

void ti68k_state::ti68k_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}

void ti68k_state::ti89(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, XTAL(10'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &ti68k_state::ti89_mem);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(ti68k_state::screen_update));
	screen.set_size(240, 128);
	screen.set_visarea(0, 160-1, 0, 100-1);
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(ti68k_state::ti68k_palette), 2);

	SHARP_LH28F160S3(config, m_flash);

	TIMER(config, "ti68k_timer").configure_periodic(FUNC(ti68k_state::ti68k_timer_callback), attotime::from_hz(1<<14));
}


void ti68k_state::ti92(machine_config &config)
{
	ti89(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &ti68k_state::ti92_mem);

	config.device_remove("flash");

	/* video hardware */
	subdevice<screen_device>("screen")->set_visarea(0, 240-1, 0, 128-1);
}


void ti68k_state::ti92p(machine_config &config)
{
	ti89(config);
	m_maincpu->set_clock(XTAL(12'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &ti68k_state::ti92p_mem);

	/* video hardware */
	subdevice<screen_device>("screen")->set_visarea(0, 240-1, 0, 128-1);
}


void ti68k_state::v200(machine_config &config)
{
	ti89(config);
	m_maincpu->set_clock(XTAL(12'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &ti68k_state::v200_mem);

	SHARP_LH28F320BF(config.replace(), m_flash);

	/* video hardware */
	subdevice<screen_device>("screen")->set_visarea(0, 240-1, 0, 128-1);
}


void ti68k_state::ti89t(machine_config &config)
{
	ti89(config);
	m_maincpu->set_clock(XTAL(16'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &ti68k_state::ti89t_mem);

	SHARP_LH28F320BF(config.replace(), m_flash);
}


/* ROM definition */
ROM_START( ti89 )
	ROM_REGION16_BE( 0x200000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v100", "V 1.00 - HW1" )
	ROMX_LOAD( "ti89v100.rom",   0x000000, 0x200000, CRC(264b34ad) SHA1(c87586a7e9b6d49fbe908fbb6f3c0038f3498573), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v100a", "V 1.00 [a] - HW1" )
	ROMX_LOAD( "ti89v100a.rom",  0x000000, 0x200000, CRC(95199934) SHA1(b8e3cdeb4705b0c7e0a15ab6c6f62bcde14a3a55), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "v100m", "V 1.00 [m] - HW1" )
	ROMX_LOAD( "ti89v100m.rom",  0x000000, 0x200000, CRC(b9059e06) SHA1(b33a7c2935eb9f73b210bcf6e7c7f32d1548a9d5), ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "v100m2", "V 1.00 [m2] - HW1" )
	ROMX_LOAD( "ti89v100m2.rom", 0x000000, 0x200000, CRC(cdd69d34) SHA1(1686362b0997bb9597f39b443490d4d8d85b56cc), ROM_BIOS(3))
	ROM_SYSTEM_BIOS( 4, "v105", "V 1.05 - HW1" )
	ROMX_LOAD( "ti89v105.rom",   0x000000, 0x200000, CRC(3bc0b474) SHA1(46fe0cd511eb81d53dc12cc4bdacec8a5bba5171), ROM_BIOS(4))
	ROM_SYSTEM_BIOS( 5, "v201", "V 2.01 - HW1" )
	ROMX_LOAD( "ti89v201.rom",   0x000000, 0x200000, CRC(fa6745e9) SHA1(e4ee6067df9b975356cef6c5a81d0ec664371c1d), ROM_BIOS(5))
	ROM_SYSTEM_BIOS( 6, "v203", "V 2.03 - HW1" )
	ROMX_LOAD( "ti89v203.rom",   0x000000, 0x200000, CRC(a3a74eca) SHA1(55aae3561722a96973b430e3d4cb4f513298ea4e), ROM_BIOS(6))
	ROM_SYSTEM_BIOS( 7, "v203m", "V 2.03 [m] - HW 1" )
	ROMX_LOAD( "ti89v203m.rom",  0x000000, 0x200000, CRC(d79068f7) SHA1(5b6f571417889b11ae19eef99a5fda4f027d5ec2), ROM_BIOS(7))
	ROM_SYSTEM_BIOS( 8, "v209",  "V 2.09 - HW 1" )
	ROMX_LOAD( "ti89v209.rom",   0x000000, 0x200000, CRC(f76f9c15) SHA1(66409ef4b20190a3b7c0d48cbd30257580b47dcd), ROM_BIOS(8))
	ROM_SYSTEM_BIOS( 9, "v105-2","V 1.05 - HW2" )
	ROMX_LOAD( "ti89v105-2.rom", 0x000000, 0x200000, CRC(83817402) SHA1(b2ddf785e973cc3f9a437d058a68abdf7ca52ea2), ROM_BIOS(9))
	ROM_SYSTEM_BIOS( 10, "v203-2",  "V 2.03 - HW2" )
	ROMX_LOAD( "ti89v203-2.rom", 0x000000, 0x200000, CRC(5e0400a9) SHA1(43c608ee72f15aed56cb5762948ec6a3c93dd9d8), ROM_BIOS(10))
	ROM_SYSTEM_BIOS( 11, "v203m-2", "V 2.03 [m] - HW2" )
	ROMX_LOAD( "ti89v203m-2.rom", 0x000000, 0x200000, CRC(04d5d76d) SHA1(14ca44b64c29aa1bf274508ca40fe69224f5a7cc), ROM_BIOS(11))
	ROM_SYSTEM_BIOS( 12, "v205-2", "V 2.05 - HW2" )
	ROMX_LOAD( "ti89v205-2.rom", 0x000000, 0x200000, CRC(37c4653c) SHA1(f48d00a57430230e489e243383513485009b1b98), ROM_BIOS(12))
	ROM_SYSTEM_BIOS( 13, "v205-2m", "V 2.05 [m] - HW2" )
	ROMX_LOAD( "ti89v205m-2.rom", 0x000000, 0x200000, CRC(e58a23f9) SHA1(d4cb23fb4b414a43802c37dc3c572a8ede670e0f), ROM_BIOS(13))
	ROM_SYSTEM_BIOS( 14, "v205-2m2","V 2.05 [m2] - HW2" )
	ROMX_LOAD( "ti89v205m2-2.rom", 0x000000, 0x200000, CRC(a8ba976c) SHA1(38bd25ada5e2066c64761d1008a9327a37d68654), ROM_BIOS(14))
	ROM_SYSTEM_BIOS( 15,"v209-2", "V 2.09 - HW2" )
	ROMX_LOAD( "ti89v209-2.rom", 0x000000, 0x200000, CRC(242a238f) SHA1(9668df314a0180ef210796e9cb651c5e9f17eb07), ROM_BIOS(15))
ROM_END

ROM_START( ti92 )
	ROM_REGION16_BE( 0x200000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v111", "V 1.11" )
	ROMX_LOAD( "ti92v111.rom",  0x000000, 0x100000, CRC(67878d52) SHA1(c0fdf162961922a76f286c93fd9b861ce20f23a3), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v13e", "V 1.3 [e]" )
	ROMX_LOAD( "ti92v13e.rom",  0x000000, 0x100000, CRC(316c8196) SHA1(82c8cd484c6aebe36f814a2023d2afad6d87f840), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "v14e", "V 1.4 [e]" )
	ROMX_LOAD( "ti92v14e.rom",  0x000000, 0x100000, CRC(239e9405) SHA1(df2f1ab17d490fda43a02f5851b5a15052361b28), ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "v17e", "V 1.7 [e]" )
	ROMX_LOAD( "ti92v17e.rom",  0x000000, 0x100000, CRC(83e27cc5) SHA1(aec5a6a6157ff94a4e665fa3fe747bacb6688cd4), ROM_BIOS(3))
	ROM_SYSTEM_BIOS( 4, "v111e", "V 1.11 [e]" )
	ROMX_LOAD( "ti92v111e.rom", 0x000000, 0x100000, CRC(4a343833) SHA1(ab4eaacc8c83a861c8d37df5c10e532d0d580460), ROM_BIOS(4))
	ROM_SYSTEM_BIOS( 5, "v112e", "V 1.12 [e]" )
	ROMX_LOAD( "ti92v112e.rom", 0x000000, 0x100000, CRC(9a6947a0) SHA1(8bb0538ca98711e9ad46c56e4dfd609d4699be30), ROM_BIOS(5))
	ROM_SYSTEM_BIOS( 6, "v21e", "V 2.1 [e]" )
	ROMX_LOAD( "ti92v21e.rom",  0x000000, 0x200000, CRC(5afb5863) SHA1(bf7b260d37d1502cc4b08dea5e1d55b523f27925), ROM_BIOS(6))
ROM_END

ROM_START( ti92p )
	ROM_REGION16_BE( 0x200000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v100", "V 1.00 - HW1" )
	ROMX_LOAD( "ti92pv100.rom", 0x0000, 0x200000, CRC(c651a586) SHA1(fbbf7e053e70eefe517f9aae40c072036bc614ea), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v101", "V 1.01 - HW1" )
	ROMX_LOAD( "ti92pv101.rom", 0x0000, 0x200000, CRC(826b1539) SHA1(dd8969511fc6233bf2047f83c3306ac8d2be5644), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "v101a","V 1.01 [a] - HW1" )
	ROMX_LOAD( "ti92pv101a.rom", 0x0000, 0x200000, CRC(18f9002f) SHA1(2bf13ba7da0212a8706c5a43853dc2ccb8c2257d), ROM_BIOS(2))
	ROM_SYSTEM_BIOS( 3, "v101m", "V 1.01 [m] - HW1" )
	ROMX_LOAD( "ti92pv101m.rom", 0x0000, 0x200000, CRC(fe2b6e77) SHA1(0e1bb8c677a726ee086c1a4280ab59de95b4abe2), ROM_BIOS(3))
	ROM_SYSTEM_BIOS( 4, "v105", "V 1.05 - HW1" )
	ROMX_LOAD( "ti92pv105.rom", 0x0000, 0x200000, CRC(cd945824) SHA1(6941aca243c6fd5c8a377253bffc2ffb5a84c41b), ROM_BIOS(4))
	ROM_SYSTEM_BIOS( 5, "v105-2", "V 1.05 - HW2" )
	ROMX_LOAD( "ti92pv105-2.rom", 0x0000, 0x200000, CRC(289aa84f) SHA1(c9395750e20d5a201401699d156b62f00530fcdd), ROM_BIOS(5))
	ROM_SYSTEM_BIOS( 6, "v203", "V 2.03 - HW2" )
	ROMX_LOAD( "ti92pv203.rom", 0x0000, 0x200000, CRC(1612213e) SHA1(1715dd5913bed12baedc4912e9abe0cb4e48cd45), ROM_BIOS(6))
	ROM_SYSTEM_BIOS( 7, "v204", "V 2.04 - HW2" )
	ROMX_LOAD( "ti92pv204.rom", 0x0000, 0x200000, CRC(86819be3) SHA1(78032a0f5f11d1e9a45ffbea91e7f9657fd1a8ae), ROM_BIOS(7))
	ROM_SYSTEM_BIOS( 8, "v205", "V 2.05 - HW2" )
	ROMX_LOAD( "ti92pv205.rom",  0x0000, 0x200000, CRC(9509c575) SHA1(703410d8bb98b8ec14277efcd8b7dda45a7cf358), ROM_BIOS(8))
	ROM_SYSTEM_BIOS( 9, "v205m", "V 2.05 [m] - HW2" )
	ROMX_LOAD( "ti92pv205m.rom",  0x000000, 0x200000, CRC(13ef4d57) SHA1(6ef290bb0dda72f645cd3eca9cc1185f6a2d32dc), ROM_BIOS(9))
	ROM_SYSTEM_BIOS( 10, "v209", "V 2.09 - HW2" )
	ROMX_LOAD( "ti92pv209.rom",  0x000000, 0x200000, CRC(4851ad52) SHA1(10e6c2cdc60623bf0be7ea72a9ec840259fb37c3), ROM_BIOS(10))
ROM_END

ROM_START( v200 )
	ROM_REGION16_BE( 0x400000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v209", "V 2.09" )
	ROMX_LOAD( "voyage200v209.rom", 0x0000, 0x400000, CRC(f805c7a6) SHA1(818b919058ba3bd7d15604f11fff6740010d07fc), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v310", "V 3.10" )
	ROMX_LOAD( "voyage200v310.rom", 0x0000, 0x400000, CRC(ed4cbfd2) SHA1(39cdb9932f314ff792b1cc5e3fe041d98b9fd101), ROM_BIOS(1))
ROM_END

ROM_START( ti89t )
	ROM_REGION16_BE( 0x400000, "flash", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v300", "V 3.00" )
	ROMX_LOAD( "ti89tv300.rom", 0x0000, 0x400000, CRC(55eb4f5a) SHA1(4f919d7752caf2559a79883ec8711a9701d19513), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v310", "V 3.10" )
	ROMX_LOAD( "ti89tv310.rom", 0x0000, 0x400000, CRC(b6967cca) SHA1(fb4f09e5c4500dee651b8de537e502ab97cb8328), ROM_BIOS(1))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY              FULLNAME          FLAGS
COMP( 1998, ti89,  0,       0,     ti89,    ti8x,  ti68k_state, empty_init, "Texas Instruments", "TI-89",          MACHINE_NO_SOUND_HW )
COMP( 1995, ti92,  0,       0,     ti92,    ti9x,  ti68k_state, empty_init, "Texas Instruments", "TI-92",          MACHINE_NO_SOUND_HW )
COMP( 1999, ti92p, 0,       0,     ti92p,   ti9x,  ti68k_state, empty_init, "Texas Instruments", "TI-92 Plus",     MACHINE_NO_SOUND_HW )
COMP( 2002, v200,  0,       0,     v200,    ti9x,  ti68k_state, empty_init, "Texas Instruments", "Voyage 200 PLT", MACHINE_NO_SOUND_HW )
COMP( 2004, ti89t, 0,       0,     ti89t,   ti8x,  ti68k_state, empty_init, "Texas Instruments", "TI-89 Titanium", MACHINE_NO_SOUND_HW )



ti931.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Skeleton driver for TI Model 931 RS-232/fiber-optic video display terminal.

*******************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
//#include "bus/rs232/rs232.h"
#include "machine/nvram.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "video/scn2674.h"
#include "screen.h"


namespace {

class ti931_state : public driver_device
{
public:
	ti931_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pvtc(*this, "pvtc")
		, m_chargen(*this, "chargen")
	{
	}

	void ti931(machine_config &config);

private:
	SCN2672_DRAW_CHARACTER_MEMBER(draw_character);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void char_map(address_map &map) ATTR_COLD;
	void attr_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_device<scn2672_device> m_pvtc;
	required_region_ptr<u8> m_chargen;
};

SCN2672_DRAW_CHARACTER_MEMBER(ti931_state::draw_character)
{
	u16 dots = m_chargen[(charcode & 0x7f) << 4 | linecount];
	const bool half_shift = BIT(dots, 7);
	dots = (dots & 0x7f) << 1;

	// TODO: attributes
	for (int i = 0; i < 9; i++)
	{
		if (!half_shift)
			dots <<= 1;
		bitmap.pix(y, x++) = BIT(dots, 8) ? rgb_t::white() : rgb_t::black();
		if (half_shift)
			dots <<= 1;
		bitmap.pix(y, x++) = BIT(dots, 8) ? rgb_t::white() : rgb_t::black();
	}
}

void ti931_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("program", 0);
	map(0x8000, 0x87ff).ram().share("charram");
	map(0x8800, 0x8fff).ram().share("attrram");
	map(0x9000, 0x97ff).ram().share("nvram");
	map(0x9800, 0x9fff).ram(); // also NVRAM?
}

void ti931_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x08, 0x0b).rw("dart", FUNC(z80dart_device::cd_ba_r), FUNC(z80dart_device::cd_ba_w));
	map(0x10, 0x17).rw(m_pvtc, FUNC(scn2672_device::read), FUNC(scn2672_device::write));
}

void ti931_state::char_map(address_map &map)
{
	map(0x0000, 0x07ff).ram().share("charram"); // HM6116LP-3
}

void ti931_state::attr_map(address_map &map)
{
	map(0x0000, 0x07ff).ram().share("attrram"); // HM6116LP-3
}

static INPUT_PORTS_START(ti931)
INPUT_PORTS_END

static const z80_daisy_config daisy_chain[] =
{
	{ "dart" },
	{ nullptr }
};

void ti931_state::ti931(machine_config &config)
{
	Z80(config, m_maincpu, 22.1184_MHz_XTAL / 6); // MK3880N-4
	m_maincpu->set_addrmap(AS_PROGRAM, &ti931_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &ti931_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // HM6116LP-3 or TC5517APL + battery

	z80ctc_device &ctc(Z80CTC(config, "ctc", 22.1184_MHz_XTAL / 6)); // Z8340APS
	ctc.set_clk<0>(22.1184_MHz_XTAL / 12);
	ctc.set_clk<1>(22.1184_MHz_XTAL / 12);
	ctc.set_clk<2>(22.1184_MHz_XTAL / 12); // possibly different from the other two
	ctc.zc_callback<0>().set("dart", FUNC(z80dart_device::rxtxcb_w));
	ctc.zc_callback<1>().set("dart", FUNC(z80dart_device::rxca_w));
	ctc.zc_callback<1>().append("dart", FUNC(z80dart_device::txca_w));
	// No CTC interrupts

	z80dart_device &dart(Z80DART(config, "dart", 22.1184_MHz_XTAL / 6)); // Z8370APS
	dart.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(22.1184_MHz_XTAL * 2, 954 * 2, 0, 720 * 2, 389, 0, 350);
	screen.set_screen_update("pvtc", FUNC(scn2672_device::screen_update));

	SCN2672(config, m_pvtc, 22.1184_MHz_XTAL / 9); // SCN2672B (with SCB2677B)
	m_pvtc->set_screen("screen");
	m_pvtc->set_character_width(9 * 2);
	m_pvtc->set_addrmap(0, &ti931_state::char_map);
	m_pvtc->set_addrmap(1, &ti931_state::attr_map);
	m_pvtc->set_display_callback(FUNC(ti931_state::draw_character));
	m_pvtc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
}

ROM_START(ti931)
	ROM_REGION(0x8000, "program", ROMREGION_ERASEFF)
	ROM_LOAD("2229208-0003_xu9.bin", 0x0000, 0x2000, CRC(a3c43008) SHA1(4bcce64487ed2b72cc8bc898c1c85291d9b766ab))
	ROM_LOAD("2229209-0003_xu7.bin", 0x2000, 0x2000, CRC(49481614) SHA1(b69b0b1f27f7e8910dbf501c00098b816e8920d7))
	ROM_LOAD("2229221-0003_xu5.bin", 0x4000, 0x2000, CRC(30f28d08) SHA1(81709b7d90899f5d9ea66026b2cf6add698e17c1))
	// XU3 socket is unpopulated

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD("2229199-2_xu1.bin", 0x0000, 0x0800, CRC(f76bb2bb) SHA1(57ec0392a2b5798eef43f0408347402c2941cee1)) // 01/29/85 CTE
ROM_END

} // anonymous namespace


COMP(1983, ti931, 0, 0, ti931, ti931, ti931_state, empty_init, "Texas Instruments", "Model 931 Video Display Terminal", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND)




ti99_2.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders: Michael Zapf
/*
    TI-99/2 driver
    --------------

    Drivers: ti99_224 - 24 KiB version
             ti99_232 - 32 KiB version

    Codenamed "Ground Squirrel", the TI-99/2 was designed to be an extremely
    low-cost, downstripped version of the TI-99/4A, competing with systems
    like the ZX81 or the Times/Sinclair 1000. The targeted price was below $100.

    The 99/2 was equipped with a TMS9995, the same CPU as used in the envisioned
    flag ship 99/8 and later in the Geneve 9640. In the 99/2 it is clocked
    at 10.7 MHz. Also, the CPU has on-chip RAM, unlike the version in the 99/8.

    At many places, the tight price constraint shows up.
    - 2 or 4 KiB of RAM
    - Video memory is part of the CPU RAM
    - Video controller is black/white only and has a fixed character set, no sprites
    - No sound
    - No GROMs (as the significant TI technology circuit)
    - No P-Box connection

    Main board
    ----------
    1 CPU @ 10.7 MHz (contrary to specs which state 5.35 MHz)
    2 RAM circuits   (4 KiB instead of 2 KiB in specs)
    3 or 4 EPROMs
    1 TAL-004 custom gate array as the video controller
    1 TAL-004 custom gate array as the I/O controller
    and six selector or latch circuits

    Connectors
    ----------
    - Built-in RF modulator, switch allows for setting the channel to VHF 3 or 4
    - Two jacks for cassette input and output, no motor control
    - Hexbus connector
    - System expansion slot for cartridges (ROM or RAM), 60 pins, on the back

    Keyboard: 48 keys, no Alpha Lock, two separate Shift keys, additional break key.

    Description
    -----------
    Prototypes were developed in 1983. Despite a late marketing campaign,
    including well-known faces, no devices were ever sold. A few of them
    survived in the hands of the engineers, and were later sold to private
    users.

    The Ground Squirrel underwent several design changes. In the initial
    design, only 2 KiB RAM were planned, and the included ROM was 24 KiB.
    Later, the 2 KiB were obviously expanded to 4 KiB, while the ROM remained
    the same in size. This can be proved here, since the console crashes with
    less than 4 KiB by an unmapped memory access. Also, the CPU is not clocked
    by 5.35 MHz as specified, but by the undivided 10.7 MHz; this was proved
    by running test programs on the real consoles.

    The next version showed an additional 8 KiB of ROM. Possibly in order
    to avoid taking away too much address space, two EPROMs are mapped to the
    same address space block, selectable by a CRU bit. ROM may be added as
    cartridges to be plugged into the expansion slot, and the same is true
    for RAM. Actually, since the complete bus is available on that connector,
    almost any kind of peripheral device could be added. Too bad, none were
    developed.

    However, the Hexbus seemed to have matured in the meantime, which became
    the standard peripheral bus system for the TI-99/8, and for smaller
    systems like the CC-40 and the TI-74. The TI-99/2 also offers a Hexbus
    interface so that any kind of Hexbus device can be connected, like, for
    example, the HX5102 floppy drive, a Wafertape drive, or the RS232 serial
    interface. The 24K version seems to have no proper Hexbus support for
    floppy drives; it always starts the cassette I/O instead.

    The address space layout looks like this:

    0000 - 3FFF:    ROM, unbanked
    4000 - 5FFF:    ROM, banked for 32K version
    6000 - DFFF:    ROM or RAM; ROM growing upwards, RAM growing downwards
    E000 - EFFF:    4K RAM
      EC00 - EEFF:  Area used by video controller (24 rows, 32 columns)
      EF00       :  Control byte for colors (black/white) for backdrop/border
    EF01 - EFFF:    RAM
    F000 - F0FB:    CPU-internal RAM
    F0FC - FFF9:    empty
    FFFA - FFFF:    CPU-internal decrementer and NMI vector

    RAM expansions may be offered via the cartridge port, but they must be
    contiguous with the built-in RAM, which means that it must grow
    downwards from E000. The space from F0FC up to FFF9 may also be covered
    by expansion RAM.

    From the other side, ROM or other memory-mapped devices may occupy
    address space, growing up from 6000.

    One peculiar detail is the memory-mapped video RAM. The video controller
    shares an area of RAM with the CPU. To avoid congestion, the video
    controller must HOLD the CPU while it accesses the video RAM area.

    This decreases processing speed of the CPU, of course. For that reason,
    a special character may be placed in every row after which the row will
    be filled with blank pixels, and the CPU will be released early. This
    means that with a screen full of characters, the processing speed is
    definitely slower than with a clear screen.

    To be able to temporarily get full CPU power, there is a pin VIDENA at
    the video controller which causes the screen to be blank when asserted,
    and to be restored as before when cleared. This is used during cassette
    transfer.

    Technical details
    -----------------
    - HOLD is asserted in every scanline when characters are drawn that are
      not the "Blank End of line" (BEOL). Once encountered, the remaining
      scanline remains blank.
    - During cassette transfer, the screen is blanked using the VIDENA line.
      This is expected and not a bug.
    - When a frame has been completed, the INT4 interrupt of the 9995 is
      triggered as a vblank interrupt.
    - All CRU operations are handled by the second gate array. Unfortunately,
      there is no known documentation about this circuit.
    - The two banks of the last 16 KiB of ROM are selected with the same
      line that is used for selecting keyboard row 0. This should mean that
      you cannot read the keyboard from the second ROM bank.

    I/O map (CRU map)
    -----------------
    0000 - 1DFE: unmapped
    1E00 - 1EFE: TMS9995-internal CRU addresses
    1F00 - 1FD8: unmapped
    1FDA:        TMS9995 MID flag
    1FDC - 1FFF: unmapped
    2000 - DFFE: unmapped
    E000 - E00E: Read: Keyboard column input
    E000 - E00A: Write: Keyboard row selection
    E00C:        Write: unmapped
    E00E:        Write: Video enable (VIDENA)
    E010 - E7FE: Mirrors of the above
    E800 - E80C: Hexbus
       E800 - E806: Data lines
       E808: HSK line
       E80A: BAV line
       E80C: Inhibit (Write only)
    E80E: Cassette

    ROM dumps
    ---------
    Although these machines are extremely rare, we were lucky to get in
    contact with users of both console variants and got dumps from
    their machines.

    The ROMs contain a stripped-down version of TI BASIC, but without
    the specific graphics subprograms. Programs written on the 99/2 should
    run on the 99/4A, but the opposite is not true. The 24K version is
    buggy and incomplete: OPEN and SAVE cause the CPU to restart itself.

    Original implementation: Raphael Nabet; December 1999, 2000

    Michael Zapf, May 2018

    References :
    [1] TI-99/2 main logic board schematics, 83/03/28-30
    [2] BYTE magazine, June 1983, pp. 128-134
*/

#include "emu.h"
#include "cpu/tms9900/tms9995.h"
#include "bus/ti99/internal/992board.h"
#include "machine/ram.h"
#include "imagedev/cassette.h"
#include "bus/hexbus/hexbus.h"

#define TI992_IO_TAG       "io"
#define TI992_RAM_TAG      "ram_region"
#define TI992_ROM          "rom_region"
#define TI992_SCREEN_TAG   "screen"

#define LOG_WARN           (1U << 1)   // Warnings
#define LOG_CRU            (1U << 2)   // CRU activities
#define LOG_SIGNALS        (1U << 3)   // Signals like HOLD/HOLDA

// Minimum log should be config and warnings
#define VERBOSE ( LOG_GENERAL | LOG_WARN )

#include "logmacro.h"


namespace {

class ti99_2_state : public driver_device
{
public:
	ti99_2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_videoctrl(*this, TI992_VDC_TAG),
		m_io992(*this, TI992_IO_TAG),
		m_cassette(*this, TI992_CASSETTE),
		m_ram(*this, TI992_RAM_TAG),
		m_expport(*this, TI992_EXPPORT_TAG),
		m_otherbank(false),
		m_rom(*this, TI992_ROM),
		m_ram_start(0xf000),
		m_first_ram_page(0)
		{ }

	void ti99_2(machine_config &config);
	void ti99_224(machine_config &config);
	void ti99_232(machine_config &config);

	// Lifecycle
	void driver_start() override;
	void driver_reset() override;

private:
	void intflag_write(offs_t offset, uint8_t data);

	uint8_t mem_read(offs_t offset);
	void mem_write(offs_t offset, uint8_t data);

	void hold(int state);
	void holda(int state);
	void interrupt(int state);
	void cassette_output(int state);

	void rombank_set(int state);

	void crumap(address_map &map) ATTR_COLD;
	void memmap(address_map &map) ATTR_COLD;

	required_device<tms9995_device> m_maincpu;
	required_device<bus::ti99::internal::video992_device> m_videoctrl;
	required_device<bus::ti99::internal::io992_device>    m_io992;

	required_device<cassette_image_device> m_cassette;
	required_device<ram_device> m_ram;

	required_device<bus::ti99::internal::ti992_expport_device> m_expport;

	bool m_otherbank;

	required_region_ptr<uint8_t> m_rom;
	int m_ram_start;
	int m_first_ram_page;
};

void ti99_2_state::driver_start()
{
	m_ram_start = 0xf000 - m_ram->default_size();
	m_first_ram_page = m_ram_start >> 12;
}

void ti99_2_state::driver_reset()
{
	m_otherbank = false;

	// Configure CPU to insert 1 wait state for each external memory access
	// by lowering the READY line on reset
	// This has been verified with the real machine, running test loops.
	m_maincpu->ready_line(CLEAR_LINE);
	m_maincpu->reset_line(ASSERT_LINE);
}

void ti99_2_state::rombank_set(int state)
{
	m_otherbank = (state==ASSERT_LINE);
}

/*
    Memory map - see description above
*/

void ti99_2_state::memmap(address_map &map)
{
	// 3 or 4 ROMs of 8 KiB. The 32K version has two ROMs mapped into 4000-5fff
	// Part of the ROM is accessed by the video controller for the
	// character definitions (1c00-1fff)
	map(0x0000, 0xffff).rw(FUNC(ti99_2_state::mem_read), FUNC(ti99_2_state::mem_write));
}

/*
    CRU map - see description above
*/
void ti99_2_state::crumap(address_map &map)
{
	map(0x0000, 0xffff).rw(m_io992, FUNC(bus::ti99::internal::io992_device::cruread), FUNC(bus::ti99::internal::io992_device::cruwrite));

	// Mirror of CPU-internal flags (1ee0-1efe). Don't read. Write is OK.
	map(0x1ee0, 0x1eff).nopr().w(FUNC(ti99_2_state::intflag_write));
}

/*
    These CRU addresses are actually inside the 9995 CPU, but they are
    propagated to the outside world, so we can watch the changes.
*/
void ti99_2_state::intflag_write(offs_t offset, uint8_t data)
{
	int addr = 0x1ee0 | (offset<<1);
	switch (addr)
	{
	case 0x1ee0:
		LOGMASKED(LOG_CRU, "Setting 9995 decrementer to %s mode\n", (data==1)? "event" : "timer");
		break;
	case 0x1ee2:
		LOGMASKED(LOG_CRU, "Setting 9995 decrementer to %s\n", (data==1)? "enabled" : "disabled");
		break;
	case 0x1ee4:
		if (data==0) LOGMASKED(LOG_CRU, "Clear INT1 latch\n");
		break;
	case 0x1ee6:
		if (data==0) LOGMASKED(LOG_CRU, "Clear INT3 latch\n");
		break;
	case 0x1ee8:
		if (data==0) LOGMASKED(LOG_CRU, "Clear INT4 latch\n");
		break;
	case 0x1eea:
		LOGMASKED(LOG_CRU, "Switch to bank %d\n", data);
		break;

	default:
		LOGMASKED(LOG_CRU, "Writing internal flag %04x = %d\n", addr, data);
		break;
	}
}

/*
    Called by CPU and video controller.
*/
uint8_t ti99_2_state::mem_read(offs_t offset)
{
	uint8_t value = 0;
	if (m_maincpu->is_onchip(offset)) return m_maincpu->debug_read_onchip_memory(offset&0xff);

	int page = offset >> 12;

	if (page>=0 && page<4)
	{
		// ROM, unbanked
		value = m_rom[offset];
	}
	if (page>=4 && page<6)
	{
		// ROM, banked on 32K version
		if (m_otherbank) offset = (offset & 0x1fff) | 0x10000;
		value = m_rom[offset];
	}

	if ((page >= m_first_ram_page) && (page < 15))
	{
		value = m_ram->pointer()[offset - m_ram_start];
	}

	m_expport->readz(offset, &value);

	return value;
}

void ti99_2_state::mem_write(offs_t offset, uint8_t data)
{
	int page = offset >> 12;

	if (page>=0 && page<4)
	{
		// ROM, unbanked
		LOGMASKED(LOG_WARN, "Writing to ROM at %04x ignored (data=%02x)\n", offset, data);
		return;
	}

	if (page>=4 && page<6)
	{
		LOGMASKED(LOG_WARN, "Writing to ROM at %04x (bank %d) ignored (data=%02x)\n", offset, m_otherbank? 1:0, data);
		return;
	}

	if ((page >= m_first_ram_page) && (page < 15))
	{
		m_ram->pointer()[offset - m_ram_start] = data;
		return;
	}

	m_expport->write(offset, data);
}

/*
    Called by the VDC as a vblank interrupt
*/
void ti99_2_state::interrupt(int state)
{
	LOGMASKED(LOG_SIGNALS, "Interrupt: %d\n", state);
	m_maincpu->set_input_line(INT_9995_INT4, state);
}

/*
    Called by the VDC to HOLD the CPU
*/
void ti99_2_state::hold(int state)
{
	LOGMASKED(LOG_SIGNALS, "HOLD: %d\n", state);
	m_maincpu->hold_line(state);
}

/*
    Called by the CPU to ack the HOLD
*/
void ti99_2_state::holda(int state)
{
	LOGMASKED(LOG_SIGNALS, "HOLDA: %d\n", state);
}

void ti99_2_state::ti99_224(machine_config& config)
{
	ti99_2(config);
	// Video hardware
	VIDEO99224(config, m_videoctrl, XTAL(10'738'635));
	m_videoctrl->readmem_cb().set(FUNC(ti99_2_state::mem_read));
	m_videoctrl->hold_cb().set(FUNC(ti99_2_state::hold));
	m_videoctrl->int_cb().set(FUNC(ti99_2_state::interrupt));

	using namespace bus::ti99::internal;
	screen_device& screen(SCREEN(config, TI992_SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_raw(XTAL(10'738'635) / 2,
			video992_device::TOTAL_HORZ,
			video992_device::HORZ_DISPLAY_START-12,
			video992_device::HORZ_DISPLAY_START + 256 + 12,
			video992_device::TOTAL_VERT_NTSC,
			video992_device::VERT_DISPLAY_START_NTSC - 12,
			video992_device::VERT_DISPLAY_START_NTSC + 192 + 12 );
	screen.set_screen_update(TI992_VDC_TAG, FUNC(video992_device::screen_update));

	// I/O interface circuit. No banking callback.
	IO99224(config, m_io992, 0);
}

void ti99_2_state::ti99_232(machine_config& config)
{
	ti99_2(config);
	// Video hardware
	VIDEO99232(config, m_videoctrl, XTAL(10'738'635));
	m_videoctrl->readmem_cb().set(FUNC(ti99_2_state::mem_read));
	m_videoctrl->hold_cb().set(FUNC(ti99_2_state::hold));
	m_videoctrl->int_cb().set(FUNC(ti99_2_state::interrupt));

	using namespace bus::ti99::internal;
	screen_device& screen(SCREEN(config, TI992_SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_raw(XTAL(10'738'635) / 2,
			video992_device::TOTAL_HORZ,
			video992_device::HORZ_DISPLAY_START-12,
			video992_device::HORZ_DISPLAY_START + 256 + 12,
			video992_device::TOTAL_VERT_NTSC,
			video992_device::VERT_DISPLAY_START_NTSC - 12,
			video992_device::VERT_DISPLAY_START_NTSC + 192 + 12 );
	screen.set_screen_update(TI992_VDC_TAG, FUNC(video992_device::screen_update));

	// I/O interface circuit
	IO99232(config, m_io992, 0).rombank_cb().set(FUNC(ti99_2_state::rombank_set));
}

void ti99_2_state::ti99_2(machine_config& config)
{
	// TMS9995, standard variant
	// Documents state that there is a divider by 2 for the clock rate
	// Experiments with real consoles proved them wrong.
	TMS9995(config, m_maincpu, XTAL(10'738'635));
	m_maincpu->set_addrmap(AS_PROGRAM, &ti99_2_state::memmap);
	m_maincpu->set_addrmap(AS_IO, &ti99_2_state::crumap);
	m_maincpu->holda_cb().set(FUNC(ti99_2_state::holda));

	// RAM 4 KiB
	// Early documents indicate 2 KiB RAM, but this does not work with
	// either ROM version, so we have to assume that the 2 KiB were never
	// sufficient, not even in the initial design
	RAM(config, TI992_RAM_TAG).set_default_size("4096").set_default_value(0);

	// Cassette drives
	// There is no route from the cassette to some audio input,
	// so we don't hear it.
	CASSETTE(config, "cassette", 0);

	// Hexbus
	HEXBUS(config, TI992_HEXBUS_TAG, 0, hexbus_options, nullptr);

	// Expansion port (backside)
	TI992_EXPPORT(config, m_expport, 0, ti992_expport_options, nullptr);
}

/*
  ROM loading
*/
ROM_START(ti99_224)
	// The 24K version is an early design; the PCB does not have any socket names
	ROM_REGION(0x6000, TI992_ROM ,0)
	ROM_LOAD("rom0000.bin", 0x0000, 0x2000, CRC(c57436f1) SHA1(71d9048fed0317cfcc4cd966dcbc3bc163080cf9))
	ROM_LOAD("rom2000.bin", 0x2000, 0x2000, CRC(be22c6c4) SHA1(931931d61732bacdab1da227c01b8045ca860f0b))
	ROM_LOAD("rom4000.bin", 0x4000, 0x2000, CRC(926ca20e) SHA1(91624a16aa2c62c7ebc23128308709efdebddca3))
ROM_END

ROM_START(ti99_232)
	ROM_REGION(0x12000, TI992_ROM, 0)
	// The 32K version is a more elaborate design; the ROMs for addresses 0000-1fff
	// and the second bank for 4000-5fff are stacked on the socket U2
	ROM_LOAD("rom0000.u2a", 0x0000, 0x2000, CRC(01b94f06) SHA1(ef2e0c5f0492d7d024ebfe3fad29c2b57ea849e1))
	ROM_LOAD("rom2000.u12", 0x2000, 0x2000, CRC(0a32f80a) SHA1(32ed98481998be295e637eaa2117337cfa4a7984))
	ROM_LOAD("rom4000a.u3", 0x4000, 0x2000, CRC(10c11fab) SHA1(d43e0952538e66e2cedc307b71b65cb388cbe8e3))
	ROM_LOAD("rom4000b.u2b", 0x10000, 0x2000, CRC(34dd52ed) SHA1(e01892b1b110d7d592a7e7f1f39f9f46ea0818db))
ROM_END

} // Anonymous namespace


//      YEAR    NAME      PARENT    COMPAT  MACHINE   INPUT   CLASS         INIT        COMPANY              FULLNAME                               FLAGS
COMP(   1983,   ti99_224, 0,        0,      ti99_224, 0, ti99_2_state, empty_init, "Texas Instruments", "TI-99/2 BASIC Computer (24 KiB ROM)" , MACHINE_NO_SOUND_HW )
COMP(   1983,   ti99_232, 0,        0,      ti99_232, 0, ti99_2_state, empty_init, "Texas Instruments", "TI-99/2 BASIC Computer (32 KiB ROM)" , MACHINE_NO_SOUND_HW )



ti99_4p.cpp
<---------------------------------------------------------------------->
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/****************************************************************************

    SNUG Second Generation CPU (SGCPU, aka TI-99/4P)

    This system is known both as the TI-99/4P ("Peripheral box", since the
    system is a card to be inserted in the peripheral box, instead of a
    self-contained console), and as the SGCPU ("Second Generation CPU",
    which was originally the name used in TI documentation to refer to either
    (or both) TI-99/5 and TI-99/8 projects).

    The SGCPU was designed and built by the SNUG (System 99 Users Group),
    namely by Michael Becker for the hardware part and Harald Glaab for the
    software part.  It has no relationship with TI.

    The card is a complete redesign of the original TI-99/4A mainboard to fit
    on a peripheral card, thus replacing the console. It shows no original
    circuits on its board; the concept is to cannibalize a TI-99/4A console,
    moving its main circuits (TMS9900, TMS9901) into the sockets on this board.

    The sound chip is not plugged on the SGCPU but on the EVPC card which
    provides the video processor for SGCPU card (see below).

    The card offers a PC-style keyboard interface which adapts the keyboard
    to the matrix organisation expected by the operating system of the TI.

    All decoding and further features are implemented by a MACH chip, which
    appears on many SNUG cards.

    On the card, most circuits are directly accessed by a 16-bit data bus,
    which ensures a significant speed-up compared to the original console. Only
    when accessing external devices via the PEB, a databus multiplexer comes into
    play which is implemented in the same way as the one in the original console,
    also contained in the MACH.

    The SGCPU offers a special connector at the back, containing the remaining
    8 data bus lines; by this feature, expansion cards can be connected at
    full 16 bit width. Only the HRD16 card (not yet emulated), which is a
    RAMDisk card, actually uses it.

    EPROM layout 64K
    ----------------
    The memory region is shifted by 4000 in the EPROM address space
    According to the designers, this is caused by the next-to most significant
    address line (2^14) being locked to 1. This is done to allow for smaller
    24pin EPROM to be used.

       Area        EPROM offset      Mapped at
       ---------------------------------------
       ROM0        4000   (0100)     0000
       DSR         C000   (1100)     4000
       ROM6A       6000   (0110)     6000
       ROM6B       E000   (1110)     6000

    System ROM
    ----------
    The GPL interpreter is located in the EPROM as ROM0 (see above). The
    SGCPU does not contain any GROM, which contain the actual TI operating
    system and the BASIC interpreter. The GROMs are replaced by the HSGPL card.

    ==== CAUTION ====: This means that the HSGPL must be properly set up before
    starting up the SGCPU. Otherwise, the emulation locks up immediately with a
    BLACK SCREEN.

    In the real environment, the HSGPL has usually been set up on delivery.
    In MAME we have to create a suitable HSGPL memory content. Best practice
    is to start the TI-99/4A console with EVPC support (driver ti99_4ev) with
    a plugged-in HSGPL and to go through the setup process there.
    Finally, the nvram files of the HSGPL must be copied into this driver's nvram
    subdirectory. The contents will be directly usable for the SGCPU.

    RAM: AEMS emulation
    --------------------
    The Asgard Expanded Memory System is a peripheral card whose successor
    (Super AMS) is available in MESS. The AEMS card is emulated inside the MACH
    chip of the SGCPU. For more information see samsmem.cpp.

    The first four address lines are used to select one of 16 mapper values with
    8 bits each. Instead of these first 4 lines, the 8 bits are prepended to
    the remaining address, yielding a 20 bit address space.

    The mapper values are mapped into the address space at 4000 by setting
    CRU bit 1E00. Only the even addresses are used, so the first mapper byte is
    at 4000, the second at 4002, the last one at 401E.

    The mapping mode can be turned on and off by the CRU bit at address 1E02.
    When turned off, the address is passed through to the RAM circuits.

    Since the only RAM areas on the TI systems are at 2000-3FFF and A000-FFFF,
    the typical usage is to use the AEMS as a 32K expansion in unmapped mode
    (the remaining 32K of the address space is decoded earlier, and does not
    affect the card), and to use it as paged memory in the 2000-3FFF and A000-FFFF
    areas by setting the mapper appropriately. Mapper registers referring to
    other memory areas have no effect.

    Video and sound
    ---------------
    The SGCPU relies on the EVPC or EVPC2 card to provide video capabilities.
    This card (rel.1) is emulated in MAME and is based on the v9938 video
    display processor.
    In order to route the VDP interrupt to the SGCPU card, the previously
    unused LCP* line in the Peripheral Expansion Box is used.

    The sound chip requires the video clock, and therefore it is moved from the
    console to the EVPC card.

    Joystick and cassette
    ---------------------
    The card features a 25-pin connector at the back which contains the lines
    for the joysticks and one cassette input/output. An adapter must be built
    to be able to use the common cables.

    Michael Zapf

*****************************************************************************/

#include "emu.h"
#include "bus/ti99/joyport/joyport.h"
#include "bus/ti99/peb/peribox.h"
#include "cpu/tms9900/tms9900.h"
#include "imagedev/cassette.h"
#include "machine/ram.h"
#include "machine/tms9901.h"
#include "speaker.h"

#define SGCPU_AMSRAM_TAG  "amsram1meg"
#define SGCPU_PADRAM_TAG  "scratchpad"
#define SGCPU_TMS9901_TAG     "tms9901"

// Debugging
#define LOG_WARN        (1U << 1)   // Warnings
#define LOG_ILLWRITE    (1U << 2)
#define LOG_READY       (1U << 3)
#define LOG_INTERRUPTS  (1U << 4)
#define LOG_ADDRESS     (1U << 5)
#define LOG_MEM         (1U << 6)
#define LOG_MUX         (1U << 7)

#define VERBOSE ( LOG_GENERAL | LOG_WARN )

#include "logmacro.h"


namespace {

class ti99_4p_state : public driver_device
{
public:
	ti99_4p_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_cpu(*this, "maincpu"),
		m_tms9901(*this, SGCPU_TMS9901_TAG),
		m_cassette(*this, "cassette"),
		m_peribox(*this, TI_PERIBOX_TAG),
		m_joyport(*this, TI_JOYPORT_TAG),
		m_scratchpad(*this, SGCPU_PADRAM_TAG),
		m_amsram(*this, SGCPU_AMSRAM_TAG),
		m_keyboard(*this, "COL%u", 0U),
		m_alpha(*this, "ALPHA"),
		m_rom(*this, "maincpu"),
		m_int1(0),
		m_int2(0),
		m_waitcount(0),
		m_addr_buf(0),
		m_decode(0),
		m_dbin(CLEAR_LINE),
		m_keyboard_column(0),
		m_check_alphalock(false),
		m_map_mode_active(false),
		m_dsr_active(false),
		m_mapper_access(false),
		m_rom6_active(false),
		m_rom6_upper(false)
	{ }

	void ti99_4p_60hz(machine_config &config);
	void driver_start() override;
	void driver_reset() override;

private:
	void ready_line(int state);
	void extint(int state);
	void notconnected(int state);
	uint8_t interrupt_level();

	void setaddress(offs_t mode, uint16_t address);
	uint16_t memread(offs_t offset);
	void memwrite(offs_t offset, uint16_t data);

	void external_operation(offs_t offset, uint8_t data);
	void clock_out(int state);

	// CRU (Communication Register Unit) handling
	uint8_t cruread(offs_t offset);
	void cruwrite(offs_t offset, uint8_t data);
	uint8_t psi_input(offs_t offset);
	void keyC0(int state);
	void keyC1(int state);
	void keyC2(int state);
	void cs_motor(int state);
	void audio_gate(int state);
	void cassette_output(int state);
	void tms9901_interrupt(offs_t offset, uint8_t data);
	void alphaW(int state);

	void video_interrupt_in(int state);

	void crumap(address_map &map) ATTR_COLD;
	void memmap(address_map &map) ATTR_COLD;
	void memmap_setaddress(address_map &map) ATTR_COLD;

	void    datamux_clock_in(int clock);

	// Devices
	required_device<tms9900_device>        m_cpu;
	required_device<tms9901_device>        m_tms9901;
	required_device<cassette_image_device> m_cassette;
	required_device<bus::ti99::peb::peribox_device>        m_peribox;
	required_device<bus::ti99::joyport::joyport_device>   m_joyport;
	required_device<ram_device> m_scratchpad;
	required_device<ram_device> m_amsram;

	required_ioport_array<6> m_keyboard;
	required_ioport m_alpha;

	int decode_address(int address);
	uint16_t debugger_read(offs_t offset);
	void debugger_write(offs_t offset, uint16_t data);
	void ready_join();
	void set_keyboard_column(int number, int data);

	// Pointer to EPROM
	required_region_ptr<uint16_t> m_rom;

	// First joystick. 6 for TI-99/4A
	static constexpr int FIRSTJOY=6;

	// Latch for 9901 INT1 and INT2 lines
	int m_int1;
	int m_int2;

	// Wait states
	int m_waitcount;

	// Value on address bus (after being set by setaddress)
	int m_addr_buf;

	// Address decoding result
	int m_decode;

	// Ready state of the databus multiplexer
	bool m_muxready;

	// Incoming Ready level
	int m_sysready;

	// State of the DBIN line
	int m_dbin;

	uint8_t   m_lowbyte;
	uint8_t   m_highbyte;
	uint8_t   m_latch;
	int     m_ready_prev;       // for debugging purposes only

	// Keyboard
	int  m_keyboard_column;
	bool m_check_alphalock;

	// true when mapper is active
	bool m_map_mode_active;

	// Internal DSR mapped in
	bool m_dsr_active;

	// Mapper visible in 4000 area
	bool m_mapper_access;

	// ROM6 visible in 6000
	bool m_rom6_active;

	// Upper bank of ROM6 selected
	bool m_rom6_upper;

	// Mapper registers
	uint8_t m_mapper[16];
};

enum
{
	ROM0BASE = 0x4000,
	DSRBASE = 0xc000,
	ROM6LBASE = 0x6000,
	ROM6UBASE = 0xe000
};

void ti99_4p_state::memmap(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(ti99_4p_state::memread), FUNC(ti99_4p_state::memwrite));
}

void ti99_4p_state::memmap_setaddress(address_map &map)
{
	map(0x0000, 0xffff).w(FUNC(ti99_4p_state::setaddress));
}

void ti99_4p_state::crumap(address_map &map)
{
	map(0x0000, 0x1fff).rw(FUNC(ti99_4p_state::cruread), FUNC(ti99_4p_state::cruwrite));
}

/*
    Input ports, used by machine code for TI keyboard and joystick emulation.

    Since the keyboard microcontroller is not emulated, we use the TI99/4a 48-key keyboard,
    plus two optional joysticks.
*/

static INPUT_PORTS_START(ti99_4p)
	/* 4 ports for keyboard and joystick */
	PORT_START("COL0")  // col 0
		PORT_BIT(0x88, IP_ACTIVE_LOW, IPT_UNUSED)
		/* The original control key is located on the left, but we accept the right control key as well */
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL")      PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
		/* TI99/4a has a second shift key which maps the same */
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
		/* The original function key is located on the right, but we accept the left alt key as well */
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("FCTN")      PORT_CODE(KEYCODE_RALT) PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_SHIFT_2)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("= + QUIT")  PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+') PORT_CHAR(UCHAR_MAMEKEY(F12))

	PORT_START("COL1")  // col 1
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)     PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(UCHAR_MAMEKEY(DOWN))
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)     PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR('~')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)     PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR(UCHAR_MAMEKEY(LEFT))
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)     PORT_CHAR('2') PORT_CHAR('@') PORT_CHAR(UCHAR_MAMEKEY(F2))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 ( BACK")  PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(') PORT_CHAR(UCHAR_MAMEKEY(F9))
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)     PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR('\'')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)     PORT_CHAR('l') PORT_CHAR('L')
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)  PORT_CHAR('.') PORT_CHAR('>')

	PORT_START("COL2")  // col 2
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)     PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR('`')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)     PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(UCHAR_MAMEKEY(UP))
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)     PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 # ERASE") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#') PORT_CHAR(UCHAR_MAMEKEY(F3))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 * REDO")  PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*') PORT_CHAR(UCHAR_MAMEKEY(F8))
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)     PORT_CHAR('i') PORT_CHAR('I') PORT_CHAR('?')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)     PORT_CHAR('k') PORT_CHAR('K')
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("COL3")  // col 3
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)     PORT_CHAR('v') PORT_CHAR('V')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)     PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR('[')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)     PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR('{')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 $ CLEAR") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') PORT_CHAR(UCHAR_MAMEKEY(F4))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 & AID")   PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&') PORT_CHAR(UCHAR_MAMEKEY(F7))
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)     PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR('_')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)     PORT_CHAR('j') PORT_CHAR('J')
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)     PORT_CHAR('m') PORT_CHAR('M')

	PORT_START("COL4")  // col 4
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)     PORT_CHAR('b') PORT_CHAR('B')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)     PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(']')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)     PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR('}')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 % BEGIN")  PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%') PORT_CHAR(UCHAR_MAMEKEY(F5))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 ^ PROC'D") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^') PORT_CHAR(UCHAR_MAMEKEY(F6))
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)     PORT_CHAR('y') PORT_CHAR('Y')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)     PORT_CHAR('h') PORT_CHAR('H')
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)     PORT_CHAR('n') PORT_CHAR('N')

	PORT_START("COL5")  // col 5
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)     PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR('\\')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)     PORT_CHAR('q') PORT_CHAR('Q')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)     PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR('|')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)     PORT_CHAR('1') PORT_CHAR('!') PORT_CHAR(UCHAR_MAMEKEY(DEL))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)     PORT_CHAR('0') PORT_CHAR(')') PORT_CHAR(UCHAR_MAMEKEY(F10))
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)     PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR('\"')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('-')

	PORT_START("ALPHA") /* one more port for Alpha line */
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Alpha Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE


INPUT_PORTS_END

enum
{
	SGCPU_NONE = 0,
	SGCPU_SYSROM,
	SGCPU_RAM,
	SGCPU_INTDSR,
	SGCPU_MAPPER,
	SGCPU_ROM6,
	SGCPU_PADRAM,
	SGCPU_PEB
};

int ti99_4p_state::decode_address(int address)
{
	int dec = SGCPU_NONE;
	switch (address & 0xe000)
	{
	case 0x0000:
		dec = SGCPU_SYSROM;
		break;
	case 0x2000:
	case 0xa000:
	case 0xc000:
	case 0xe000:
		dec = SGCPU_RAM;
		break;
	case 0x4000:
		if (m_dsr_active) dec = SGCPU_INTDSR;
		else if (m_mapper_access) dec = SGCPU_MAPPER;
		break;
	case 0x6000:
		if (m_rom6_active) dec = SGCPU_ROM6;
		break;
	case 0x8000:
		if ((address & 0x1c00)==0x0000) dec = SGCPU_PADRAM;
		break;

	default:
		break;
	}
	return dec;
}

/*
    Called when the memory access starts by setting the address bus. From that
    point on, we suspend the CPU until all operations are done.
*/
void ti99_4p_state::setaddress(offs_t address, uint16_t busctrl)
{
	m_addr_buf = address << 1;
	m_waitcount = 0;
	m_dbin = ((busctrl & TMS99xx_BUS_DBIN)!=0);

	LOGMASKED(LOG_ADDRESS, "set address %04x\n", m_addr_buf);

	m_decode = SGCPU_NONE;
	m_muxready = true;

	// Trigger the TMS9901 clock when A10 is 1
	if ((m_addr_buf & 0x0020) != 0)
		m_tms9901->update_clock();

	m_decode = decode_address(m_addr_buf);

	if (m_decode == SGCPU_NONE)
	{
		// not found - pass on to PEB, 8 bit access with wait states as in TI-99/4A console
		// PEB gets remaining accesses
		// HSGPL, EVPC, other devices
		m_decode = SGCPU_PEB;
		m_waitcount = 5;
		m_muxready = false;
		m_peribox->memen_in(ASSERT_LINE);
		m_peribox->setaddress_dbin(m_addr_buf+1, m_dbin);
	}

	ready_join();
}

uint16_t ti99_4p_state::memread(offs_t offset)
{
	int address = m_addr_buf;
	int decode = m_decode;
	int paddress = 0;

	uint8_t hbyte = 0;
	uint16_t value = 0;


	// If we use the debugger, decode the address now (normally done in setaddress)
	if (machine().side_effects_disabled())
	{
		address = offset << 1;
		decode = decode_address(address);
	}

	int addr_off8k = address & 0x1fff;

	switch (decode)
	{
	case SGCPU_SYSROM:
		value = m_rom[(ROM0BASE | addr_off8k) >> 1];
		break;

	case SGCPU_RAM:
		// Memory read. The AEMS emulation has two address areas: The memory is at locations
		// 0x2000-0x3fff and 0xa000-0xffff, and the mapper area is at 0x4000-0x401e
		// (only even addresses).
		if (m_map_mode_active)
			paddress = (m_mapper[(address & 0xf000)>>12] << 12) | (address & 0x0fff);
		else // transparent mode
			paddress = address;

		value = ((m_amsram->pointer()[paddress] & 0xff) << 8) | (m_amsram->pointer()[paddress+1] & 0xff);
		break;

	case SGCPU_INTDSR:
		value = m_rom[(DSRBASE | addr_off8k)>>1];
		break;

	case SGCPU_MAPPER:
		value = (m_mapper[address & 0x000f]<<8) & 0xff00;
		break;

	case SGCPU_ROM6:
		value = m_rom[((m_rom6_upper? ROM6UBASE : ROM6LBASE) | addr_off8k)>>1];
		break;
	case SGCPU_PADRAM:
		// Scratch pad RAM (16 bit)
		// 8000 ... 83ff (1K, 4 times the size of the internal RAM of the TI-99/4A)
		value = ((m_scratchpad->pointer()[address & 0x03ff] & 0xff)<<8)
				| (m_scratchpad->pointer()[(address & 0x03ff)+1] & 0xff);
		break;

	case SGCPU_PEB:
		if (machine().side_effects_disabled())
			return debugger_read(offset);

		// The byte from the odd address has already been read into the latch
		// Reading the even address now
		m_peribox->readz(address, &hbyte);
		m_peribox->memen_in(CLEAR_LINE);
		LOGMASKED(LOG_MEM, "Read even byte from address %04x -> %02x\n",  address, hbyte);
		value = (hbyte<<8) | m_latch;
	}

	return value;
}


void ti99_4p_state::memwrite(offs_t offset, uint16_t data)
{
	int address = m_addr_buf;
	int decode = m_decode;
	int paddress = 0;

	// If we use the debugger, decode the address now (normally done in setaddress)
	if (machine().side_effects_disabled())
	{
		address = offset << 1;
		decode = decode_address(address);
	}

	switch (decode)
	{
	case SGCPU_SYSROM:
		LOGMASKED(LOG_ILLWRITE, "Ignoring ROM write access at %04x\n", address);
		break;

	case SGCPU_RAM:
		// see above
		if (m_map_mode_active)
			paddress = (m_mapper[(address & 0xf000)>>12] << 12) | (address & 0x0fff);
		else // transparent mode
			paddress = address;

		m_amsram->pointer()[paddress] = (data >> 8) & 0xff;
		m_amsram->pointer()[paddress+1] = data & 0xff;
		break;

	case SGCPU_INTDSR:
		LOGMASKED(LOG_ILLWRITE, "Ignoring DSR write access at %04x\n", address);
		break;

	case SGCPU_MAPPER:
		m_mapper[(address>>1) & 0x000f] = data;  // writing both bytes, but only the first is accepted
		break;

	case SGCPU_ROM6:
		// Writing to 6002 sets upper bank
		m_rom6_upper = (address & 0x0002)!=0;
		break;

	case SGCPU_PADRAM:
		// Scratch pad RAM (16 bit)
		// 8000 ... 83ff (1K, 4 times the size of the internal RAM of the TI-99/4A)
		m_scratchpad->pointer()[address & 0x03ff] = (data >> 8) & 0xff;
		m_scratchpad->pointer()[(address & 0x03ff)+1] = data & 0xff;
		break;

	case SGCPU_PEB:
		if (machine().side_effects_disabled())
		{
			debugger_write(offset, data);
			return;
		}

		// Writing the even address now (addr)
		// The databus multiplexer puts the even value into the latch and outputs the odd value now.
		m_latch = (data >> 8) & 0xff;

		// write odd byte
		LOGMASKED(LOG_MEM, "datamux: write odd byte to address %04x <- %02x\n",  address+1, data & 0xff);
		m_peribox->write(address+1, data & 0xff);
		m_peribox->memen_in(CLEAR_LINE);
	}
}

/*
    Used when the debugger is reading values from PEB cards.
*/
uint16_t ti99_4p_state::debugger_read(offs_t offset)
{
	uint8_t lval = 0;
	uint8_t hval = 0;
	uint16_t addrb = offset << 1;
	m_peribox->memen_in(ASSERT_LINE);
	m_peribox->readz(addrb+1, &lval);
	m_peribox->readz(addrb, &hval);
	m_peribox->memen_in(CLEAR_LINE);
	return ((hval << 8)&0xff00) | (lval & 0xff);
}

/*
    Used when the debugger is writing values to PEB cards.
*/
void ti99_4p_state::debugger_write(offs_t offset, uint16_t data)
{
	int addrb = offset << 1;
	m_peribox->memen_in(ASSERT_LINE);
	m_peribox->write(addrb+1, data & 0xff);
	m_peribox->write(addrb,  (data>>8) & 0xff);
	m_peribox->memen_in(CLEAR_LINE);
}

/*
    The datamux is connected to the clock line in order to operate
    the wait state counter and to read/write the bytes.
*/
void ti99_4p_state::datamux_clock_in(int state)
{
	// return immediately if the datamux is currently inactive
	if (m_waitcount>0)
	{
		LOGMASKED(LOG_MUX, "datamux: wait count %d\n", m_waitcount);
		if (m_sysready==CLEAR_LINE)
		{
			LOGMASKED(LOG_MUX, "datamux: stalled due to external READY=0\n");
			return;
		}

		if (m_dbin==ASSERT_LINE)
		{
			// Reading
			if (state==ASSERT_LINE)
			{   // raising edge
				if (--m_waitcount==0)
				{
					m_muxready = true;
					ready_join();
				}
				if (m_waitcount==2)
				{
					// read odd byte
					m_peribox->readz(m_addr_buf+1, &m_latch);
					m_peribox->memen_in(CLEAR_LINE);

					LOGMASKED(LOG_MEM, "datamux: read odd byte from address %04x -> %02x\n",  m_addr_buf+1, m_latch);

					// do the setaddress for the even address
					m_peribox->memen_in(ASSERT_LINE);
					m_peribox->setaddress_dbin(m_addr_buf, m_dbin);
				}
			}
		}
		else    // write access
		{
			if (state==ASSERT_LINE)
			{   // raising edge
				if (--m_waitcount==0)
				{
					m_muxready = true;
					ready_join();
				}
			}
			else
			{   // falling edge
				if (m_waitcount==2)
				{
					// do the setaddress for the even address
					m_peribox->memen_in(ASSERT_LINE);
					m_peribox->setaddress_dbin(m_addr_buf, m_dbin);

					// write even byte
					LOGMASKED(LOG_MEM, "datamux: write even byte to address %04x <- %02x\n",  m_addr_buf, m_latch);
					m_peribox->write(m_addr_buf, m_latch);
					m_peribox->memen_in(CLEAR_LINE);
				}
			}
		}
	}
}

/***************************************************************************
   CRU interface
***************************************************************************/

#define MAP_CRU_BASE 0x0f00
#define SAMS_CRU_BASE 0x1e00

/*
    CRU write
*/
void ti99_4p_state::cruwrite(offs_t offset, uint8_t data)
{
	// Internal 9901
	// We cannot use the map because device in the Peribox may want to see the
	// CRU address on the bus (see sidmaster)
	if ((offset & 0xfc00)==0)
		m_tms9901->write(offset & 0x3f, data);

	int addroff = offset<<1;

	if ((addroff & 0xff00)==MAP_CRU_BASE)
	{
		if ((addroff & 0x000e)==0)  m_dsr_active = data;
		if ((addroff & 0x000e)==2)  m_rom6_active = data;
		if ((addroff & 0x000e)==4)  m_peribox->senila((data!=0)? ASSERT_LINE : CLEAR_LINE);
		if ((addroff & 0x000e)==6)  m_peribox->senilb((data!=0)? ASSERT_LINE : CLEAR_LINE);
		// TODO: more CRU bits? 8=Fast timing / a=KBENA
		return;
	}
	if ((addroff & 0xff00)==SAMS_CRU_BASE)
	{
		if ((addroff & 0x000e)==0) m_mapper_access = data;
		if ((addroff & 0x000e)==2) m_map_mode_active = data;
		return;
	}

	// No match - pass to peribox
	m_peribox->cruwrite(addroff, data);
}

uint8_t ti99_4p_state::cruread(offs_t offset)
{
	uint8_t value = 0;

	// Internal 9901
	// We cannot use the map because devices in the Peribox may want to see the
	// CRU address on the bus (see sidmaster)
	if ((offset & 0xfc00)==0)
		value = m_tms9901->read(offset & 0x3f);

	m_peribox->crureadz(offset<<1, &value);
	return value;
}

/***************************************************************************
    Keyboard/tape control
****************************************************************************/

uint8_t ti99_4p_state::psi_input(offs_t offset)
{
	switch (offset)
	{
	case tms9901_device::INT1:
		return (m_int1==CLEAR_LINE)? 1 : 0;
	case tms9901_device::INT2:
		return (m_int2==CLEAR_LINE)? 1 : 0;
	case tms9901_device::INT3:
	case tms9901_device::INT4:
	case tms9901_device::INT5:
	case tms9901_device::INT6:
		// Keyboard ACTIVE_LOW (s.o.)
		// Joysticks ACTIVE_LOW (handset.cpp)
		if (m_keyboard_column >= 6)
			return BIT(m_joyport->read_port(), offset-tms9901_device::INT3);
		else
			return BIT(m_keyboard[m_keyboard_column]->read(), offset-tms9901_device::INT3);

	case tms9901_device::INT7_P15:
		if (m_keyboard_column >= 6) // Joysticks
			return BIT(m_joyport->read_port(), offset-tms9901_device::INT3);
		else
		{
			if (m_check_alphalock)
			{
				return BIT(m_alpha->read(), offset-tms9901_device::INT3);
			}
			else
				return BIT(m_keyboard[m_keyboard_column]->read(), offset-tms9901_device::INT3);
		}

	case tms9901_device::INT8_P14:
	case tms9901_device::INT9_P13:
	case tms9901_device::INT10_P12:
		if (m_keyboard_column >= FIRSTJOY)  // no joystick lines after /INT7
			return 1;
		else
			return BIT(m_keyboard[m_keyboard_column]->read(), offset-tms9901_device::INT3);
	case tms9901_device::INT11_P11:
		// CS2 is write-only
		return (m_cassette->input() > 0);
	default:
		return 1;
	}
}

/*
    WRITE key column select (P2-P4)
*/
void ti99_4p_state::set_keyboard_column(int number, int data)
{
	if (data!=0)    m_keyboard_column |= 1 << number;
	else            m_keyboard_column &= ~(1 << number);

	if (m_keyboard_column >= FIRSTJOY)
	{
		m_joyport->write_port(m_keyboard_column - FIRSTJOY + 1);
	}
}

void ti99_4p_state::keyC0(int state)
{
	set_keyboard_column(0, state);
}

void ti99_4p_state::keyC1(int state)
{
	set_keyboard_column(1, state);
}

void ti99_4p_state::keyC2(int state)
{
	set_keyboard_column(2, state);
}

/*
    WRITE alpha lock line (P5)
*/
void ti99_4p_state::alphaW(int state)
{
	m_check_alphalock = (state==0);
}

/*
    command CS1 (only) tape unit motor (P6)
*/
void ti99_4p_state::cs_motor(int state)
{
	m_cassette->change_state((state!=0)? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR);
}

/*
    audio gate (P8)

    Set to 1 before using tape: this enables the mixing of tape input sound
    with computer sound.

    We do not really need to emulate this as the tape recorder generates sound
    on its own.
*/
void ti99_4p_state::audio_gate(int state)
{
}

/*
    tape output (P9)
*/
void ti99_4p_state::cassette_output(int state)
{
	m_cassette->output((state!=0)? +1 : -1);
}


/***************************************************************************
    Control lines
****************************************************************************/

/*
    Combine the external (sysready) and the own (muxready) READY states.
*/
void ti99_4p_state::ready_join()
{
	int combined = (m_sysready == ASSERT_LINE && m_muxready)? ASSERT_LINE : CLEAR_LINE;

	if (m_ready_prev != combined) LOGMASKED(LOG_READY, "READY level = %d\n", combined);
	m_ready_prev = combined;
	m_cpu->set_ready(combined);
}

/*
    Incoming READY line from other cards in the Peripheral Expansion Box.
*/
void ti99_4p_state::ready_line(int state)
{
	if (state != m_sysready) LOGMASKED(LOG_READY, "READY line from PBox = %d\n", state);
	m_sysready = (line_state)state;
	// Also propagate to CPU via driver
	ready_join();
}

void ti99_4p_state::extint(int state)
{
	LOGMASKED(LOG_INTERRUPTS, "EXTINT level = %02x\n", state);
	m_int1 = (line_state)state;
	m_tms9901->set_int_line(1, state);
}

void ti99_4p_state::notconnected(int state)
{
	LOGMASKED(LOG_INTERRUPTS, "Setting a not connected line ... ignored\n");
}

/*
    Clock line from the CPU. Used to control wait state generation.
*/
void ti99_4p_state::clock_out(int state)
{
	m_tms9901->phi_line(state);
	datamux_clock_in(state);
	m_peribox->clock_in(state);
}

void ti99_4p_state::tms9901_interrupt(offs_t offset, uint8_t data)
{
	// offset contains the interrupt level (0-15)
	// However, the TI board just ignores that level and hardwires it to 1
	// See below (interrupt_level)
	m_cpu->set_input_line(INT_9900_INTREQ, data);
}

uint8_t ti99_4p_state::interrupt_level()
{
	// On the TI-99 systems these IC lines are not used; the input lines
	// at the CPU are hardwired to level 1.
	return 1;
}

void ti99_4p_state::external_operation(offs_t offset, uint8_t data)
{
	static char const *const extop[8] = { "inv1", "inv2", "IDLE", "RSET", "inv3", "CKON", "CKOF", "LREX" };
	if (offset != IDLE_OP) logerror("External operation %s not implemented on the SGCPU board\n", extop[offset]);
}

/*****************************************************************************/

void ti99_4p_state::driver_start()
{
	m_peribox->senila(CLEAR_LINE);
	m_peribox->senilb(CLEAR_LINE);

	m_sysready = ASSERT_LINE;
	m_muxready = true;
	m_dbin = CLEAR_LINE;

	save_item(NAME(m_int1));
	save_item(NAME(m_int2));
	save_item(NAME(m_keyboard_column));
	save_item(NAME(m_check_alphalock));
	save_item(NAME(m_waitcount));
	save_item(NAME(m_addr_buf));
	save_item(NAME(m_decode));
	save_item(NAME(m_muxready));
	save_item(NAME(m_sysready));
	save_item(NAME(m_lowbyte));
	save_item(NAME(m_highbyte));
	save_item(NAME(m_latch));
	save_item(NAME(m_map_mode_active));
	save_item(NAME(m_dsr_active));
	save_item(NAME(m_mapper_access));
	save_item(NAME(m_rom6_active));
	save_item(NAME(m_rom6_upper));
	save_item(NAME(m_dbin));
	save_item(NAME(m_mapper));
}

/*
    set the state of int2 (called by the v9938)
*/
void ti99_4p_state::video_interrupt_in(int state)
{
	LOGMASKED(LOG_INTERRUPTS, "VDP INT2 from EVPC on tms9901, level=%d\n", state);
	m_int2 = (line_state)state;
	m_tms9901->set_int_line(2, state);
}

/*
    Reset the machine.
*/
void ti99_4p_state::driver_reset()
{
	m_cpu->set_ready(ASSERT_LINE);
	m_cpu->set_hold(CLEAR_LINE);
	m_int1 = m_int2 = CLEAR_LINE;
	m_peribox->reset_in(ASSERT_LINE);
	m_peribox->reset_in(CLEAR_LINE);
	m_check_alphalock = false;
	m_keyboard_column = 0;
	m_waitcount = 0;
	m_map_mode_active = false;
	m_mapper_access = false;
	m_dsr_active = false;
	m_rom6_active = m_rom6_upper = false;
	m_decode = 0;
	m_addr_buf = 0;
}

/*
    Machine description.
*/
void ti99_4p_state::ti99_4p_60hz(machine_config& config)
{
	/* basic machine hardware */
	/* TMS9900 CPU @ 3.0 MHz */
	TMS9900(config, m_cpu, 3000000);
	m_cpu->set_addrmap(AS_PROGRAM, &ti99_4p_state::memmap);
	m_cpu->set_addrmap(AS_IO, &ti99_4p_state::crumap);
	m_cpu->set_addrmap(tms99xx_device::AS_SETADDRESS, &ti99_4p_state::memmap_setaddress);
	m_cpu->extop_cb().set(FUNC(ti99_4p_state::external_operation));
	m_cpu->intlevel_cb().set(FUNC(ti99_4p_state::interrupt_level));
	m_cpu->clkout_cb().set(FUNC(ti99_4p_state::clock_out));

	// tms9901
	TMS9901(config, m_tms9901, 0);
	m_tms9901->read_cb().set(FUNC(ti99_4p_state::psi_input));
	m_tms9901->p_out_cb(2).set(FUNC(ti99_4p_state::keyC0));
	m_tms9901->p_out_cb(3).set(FUNC(ti99_4p_state::keyC1));
	m_tms9901->p_out_cb(4).set(FUNC(ti99_4p_state::keyC2));
	m_tms9901->p_out_cb(5).set(FUNC(ti99_4p_state::alphaW));
	m_tms9901->p_out_cb(6).set(FUNC(ti99_4p_state::cs_motor));
	m_tms9901->p_out_cb(8).set(FUNC(ti99_4p_state::audio_gate));
	m_tms9901->p_out_cb(9).set(FUNC(ti99_4p_state::cassette_output));
	m_tms9901->intreq_cb().set(FUNC(ti99_4p_state::tms9901_interrupt));

	// Peripheral expansion box (SGCPU composition)
	TI99_PERIBOX_SG(config, m_peribox, 0);
	m_peribox->inta_cb().set(FUNC(ti99_4p_state::extint));
	m_peribox->intb_cb().set(FUNC(ti99_4p_state::notconnected));
	m_peribox->ready_cb().set(FUNC(ti99_4p_state::ready_line));

	// The SGCPU actually makes use of this pin which was unused before
	m_peribox->lcp_cb().set(FUNC(ti99_4p_state::video_interrupt_in));

	// Scratch pad RAM 1024 bytes (4 times the size of the TI-99/4A)
	RAM(config, SGCPU_PADRAM_TAG).set_default_size("1K").set_default_value(0);

	// AMS RAM 1 MiB
	RAM(config, SGCPU_AMSRAM_TAG).set_default_size("1M").set_default_value(0);

	// Cassette drives
	SPEAKER(config, "cass_out").front_center();
	CASSETTE(config, "cassette", 0).add_route(ALL_OUTPUTS, "cass_out", 0.25);

	// Joystick port
	TI99_JOYPORT(config, m_joyport, 0, ti99_joyport_options_plain, "twinjoy");
}

ROM_START(ti99_4p)
	/*CPU memory space*/
	ROM_REGION16_BE(0x10000, "maincpu", 0)
	ROM_LOAD16_BYTE("sgcpu_hb.bin", 0x0000, 0x8000, CRC(aa100730) SHA1(35e585b2dcd3f2a0005bebb15ede6c5b8c787366) ) /* system ROMs */
	ROM_LOAD16_BYTE("sgcpu_lb.bin", 0x0001, 0x8000, CRC(2a5dc818) SHA1(dec141fe2eea0b930859cbe1ebd715ac29fa8ecb) ) /* system ROMs */
ROM_END

} // Anonymous namespace


//    YEAR  NAME     PARENT  COMPAT  MACHINE       INPUT    CLASS          INIT        COMPANY                 FULLNAME                FLAGS
COMP( 1996, ti99_4p, 0,      0,      ti99_4p_60hz, ti99_4p, ti99_4p_state, empty_init, "System-99 User Group", "SGCPU (aka TI-99/4P)", MACHINE_SUPPORTS_SAVE )



ti99_4x.cpp
<---------------------------------------------------------------------->
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/****************************************************************************

    MAME Driver for TI-99/4 and TI-99/4A Home Computers.
    TI99/4 info:

    Similar to TI99/4a, except for the following:
    * tms9918/9928 has no bitmap mode
    * smaller, 40-key keyboard
    * many small differences in the contents of system ROMs

    Historical notes: TI made several last minute design changes.
    * TI99/4 prototypes had an extra port for an I/R joystick and keypad interface.
    * early TI99/4 prototypes were designed for a tms9985, not a tms9900.

    Emulation architecture:
    (also see datamux.cpp, peribox.cpp)

              +---- video (upper 8 bits of databus)
              |
    CPU       |
    TMS9900 ==##== datamux --------+------ peribox ----------- [8] slots with peripheral devices
       |      ||   (16->8)         |
       |      ||                   +------ Console GROMs (0, 1, 2)
       |      ##=== Console ROM    |
    TMS9901   ||                   +------ gromport (cartridge port)
     (I/O)    ##=== 256 byte RAM   |
       |      ||                   +------ sound
       |      ##=== 32KiB RAM
   +---+--+         unofficial mod
   |      |         (16 bit)
  Cass  joyport

  Raphael Nabet, 1999-2003.
  Rewritten by Michael Zapf
  February 2012: Rewritten as class

*****************************************************************************/

#include "emu.h"
#include "cpu/tms9900/tms9900.h"

#include "machine/tms9901.h"
#include "imagedev/cassette.h"

#include "bus/ti99/internal/datamux.h"
#include "bus/ti99/gromport/gromport.h"
#include "bus/ti99/internal/evpcconn.h"

#include "bus/ti99/joyport/joyport.h"
#include "bus/ti99/internal/ioport.h"
#include "machine/ram.h"

#include "softlist_dev.h"
#include "speaker.h"

#define TI99_CONSOLEGROM     "cons_grom"
#define TI99_SCREEN_TAG      "screen"

// Debugging
#define LOG_WARN        (1U << 1)   // Warnings
#define LOG_CONFIG      (1U << 2)   // Configuration
#define LOG_READY       (1U << 3)
#define LOG_INTERRUPTS  (1U << 4)
#define LOG_CRU         (1U << 5)
#define LOG_CRUREAD     (1U << 6)
#define LOG_RESETLOAD   (1U << 7)

#define VERBOSE ( LOG_GENERAL | LOG_CONFIG | LOG_WARN | LOG_RESETLOAD )

#include "logmacro.h"


namespace {

/*
    The console.
*/
class ti99_4x_state : public driver_device
{
public:
	ti99_4x_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_keyboard_column(0),
		m_check_alphalock(false),
		m_nready_combined(0),
		m_nready_prev(0),
		m_model(0),
		m_int1(0),
		m_int2(0),
		m_int12(0),
		m_cpu(*this, "maincpu"),
		m_tms9901(*this, TI99_TMS9901_TAG),
		m_gromport(*this, TI99_GROMPORT_TAG),
		m_ioport(*this, TI99_IOPORT_TAG),
		m_joyport(*this, TI_JOYPORT_TAG),
		m_datamux(*this, TI99_DATAMUX_TAG),
		m_video(*this, TI99_VDP_TAG),
		m_cassette1(*this, "cassette1"),
		m_cassette2(*this, "cassette2"),
		m_keyboard(*this, "COL%u", 0U),
		m_alpha(*this, "ALPHA"),
		m_alpha1(*this, "ALPHA1"),
		m_alphabug(*this, "ALPHABUG")
	{ }

	// Configurations
	void ti99_4_common(machine_config &config);
	void ti99_4(machine_config &config);
	void ti99_4_50hz(machine_config &config);
	void ti99_4ev_60hz(machine_config &config);
	void ti99_4qi(machine_config &config);
	void ti99_4qi_60hz(machine_config &config);
	void ti99_4a_50hz(machine_config &config);
	void ti99_4a_60hz(machine_config &config);
	void ti99_4a(machine_config &config);
	void ti99_4_60hz(machine_config &config);

	// Lifecycle
	void driver_start() override;
	void driver_reset() override;

	// Interrupt triggers
	DECLARE_INPUT_CHANGED_MEMBER( load_interrupt );

private:
	// Processor connections with the main board
	uint8_t cruread(offs_t offset);
	uint8_t interrupt_level();
	void cruwrite(offs_t offset, uint8_t data);
	void external_operation(offs_t offset, uint8_t data);
	void clock_out(int state);

	// Connections from outside towards the CPU (callbacks)
	void console_ready_dmux(int state);
	void console_ready_sound(int state);
	[[maybe_unused]] void console_ready_pbox(int state);
	void console_ready_cart(int state);
	void console_ready_grom(int state);
	void console_reset(int state);
	[[maybe_unused]] void notconnected(int state);

	// GROM clock
	void gromclk_in(int state);

	// Connections with the system interface chip 9901
	void extint(int state);
	void video_interrupt_in(int state);
	void handset_interrupt_in(int state);

	// Connections with the system interface TMS9901
	uint8_t psi_input_4(offs_t offset);
	uint8_t psi_input_4a(offs_t offset);
	void keyC0(int state);
	void keyC1(int state);
	void keyC2(int state);
	void cs1_motor(int state);
	void audio_gate(int state);
	void cassette_output(int state);
	void tms9901_interrupt(int state);
	void handset_ack(int state);
	void cs2_motor(int state);
	void alphaW(int state);

	// Used by EVPC
	void video_interrupt_evpc_in(int state);
	TIMER_CALLBACK_MEMBER(gromclk_tick);

	void crumap(address_map &map) ATTR_COLD;
	void memmap(address_map &map) ATTR_COLD;
	void memmap_setaddress(address_map &map) ATTR_COLD;

	void    set_keyboard_column(int number, int data);
	int     m_keyboard_column;
	int     m_check_alphalock;

	// READY handling
	int     m_nready_combined;
	int     m_nready_prev;
	void    console_ready_join(int id, int state);

	// Console type
	int     m_model;

	// Latch for 9901 INT1, INT2, and INT12 lines
	int  m_int1;
	int  m_int2;
	int  m_int12;

	// Connected devices
	required_device<tms9900_device>     m_cpu;
	required_device<tms9901_device>     m_tms9901;
	required_device<bus::ti99::gromport::gromport_device>   m_gromport;
	required_device<bus::ti99::internal::ioport_device>     m_ioport;
	required_device<bus::ti99::joyport::joyport_device>     m_joyport;
	required_device<bus::ti99::internal::datamux_device>    m_datamux;
	optional_device<tms9928a_device>    m_video;
	required_device<cassette_image_device> m_cassette1;
	required_device<cassette_image_device> m_cassette2;

	optional_ioport_array<6> m_keyboard;
	optional_ioport m_alpha;
	optional_ioport m_alpha1;
	optional_ioport m_alphabug;

	// Timer for EVPC (provided by the TMS9929A, but EVPC replaces that VDP)
	emu_timer   *m_gromclk_timer;
};

/*
    Console models.
*/
enum
{
	MODEL_4,
	MODEL_4A,
	MODEL_4EV,
	MODEL_4QI
};

/*
    READY bits.
*/
enum
{
	READY_GROM = 1,
	READY_DMUX = 2,
	READY_PBOX = 4,
	READY_SOUND = 8,
	READY_CART = 16
};

/*
    Memory map. All of the work is done in the datamux (see datamux.c).
*/
void ti99_4x_state::memmap(address_map &map)
{
	map.global_mask(0xffff);
	map(0x0000, 0xffff).rw(TI99_DATAMUX_TAG, FUNC(bus::ti99::internal::datamux_device::read), FUNC(bus::ti99::internal::datamux_device::write));
}

void ti99_4x_state::memmap_setaddress(address_map &map)
{
	map.global_mask(0xffff);
	map(0x0000, 0xffff).w(TI99_DATAMUX_TAG, FUNC(bus::ti99::internal::datamux_device::setaddress));
}

/*
    CRU map
    TMS9900 CRU address space is 12 bits wide, attached to A3-A14, A0-A2 must
    be 000 (other values for external commands like RSET, LREX, CKON...),
    A15 is used as CRUOUT
    The TMS9901 is incompletely decoded
    ---0 00xx xxcc ccc0
    causing 16 mirrors (0000, 0040, 0080, 00c0, ... , 03c0)
*/
void ti99_4x_state::crumap(address_map &map)
{
	map(0x0000, 0x1fff).rw(FUNC(ti99_4x_state::cruread), FUNC(ti99_4x_state::cruwrite));
}


/*****************************************************************************
    Input ports
 ****************************************************************************/

static INPUT_PORTS_START(ti99_4)
	PORT_START("COL0")  // col 0
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 !") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q QUIT") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR(UCHAR_MAMEKEY(F12))
		/* TI99/4 has a second space key which maps the same */
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(' ')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0 )") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P \"") PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('"')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L =") PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('=')
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)

	PORT_START("COL1")  // col 1
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 @") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W BEGIN") PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR(UCHAR_MAMEKEY(F5))
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A AID") PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR(UCHAR_MAMEKEY(F7))
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z BACK") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR(UCHAR_MAMEKEY(F9))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 (") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O +") PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('+')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K /") PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('/')
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(", .") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(',')

	PORT_START("COL2")  // col 2
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 #") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E UP") PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR(UCHAR_MAMEKEY(UP))
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S LEFT") PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR(UCHAR_MAMEKEY(LEFT))
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X DOWN") PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR(UCHAR_MAMEKEY(DOWN))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 *") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I -") PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('-')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J ^") PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('^')
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M ;") PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR(';')
				/* col 3 */
	PORT_START("COL3")  // col 3
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 $") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R REDO") PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR(UCHAR_MAMEKEY(F8))
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D RIGHT") PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C CLEAR") PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR(UCHAR_MAMEKEY(F4))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 &") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U _") PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('_')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H <") PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('<')
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N :") PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR(':')

	PORT_START("COL4")  // col 4
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 %") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T ERASE") PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR(UCHAR_MAMEKEY(F3))
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F DEL") PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR(UCHAR_MAMEKEY(DEL))
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V PROC'D") PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR(UCHAR_MAMEKEY(F6))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 ^") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('\'')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y >") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('>')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G INS") PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR(UCHAR_MAMEKEY(F2))
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B ?") PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('?')

INPUT_PORTS_END

/* TI99/4a: 48-key keyboard */
static INPUT_PORTS_START(ti99_4a)
	PORT_START( "ALPHABUG" )
		PORT_CONFNAME( 0x01, 0x01, "Alpha Lock blocks joystick up" )
		PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
		PORT_CONFSETTING(    0x01, DEF_STR( On ) )

	PORT_START( "LOADINT")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Load interrupt") PORT_CODE(KEYCODE_PRTSCR) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ti99_4x_state::load_interrupt), 1)

	PORT_START("COL0")  // col 0
		PORT_BIT(0x88, IP_ACTIVE_LOW, IPT_UNUSED)
		/* The original control key is located on the left, but we accept the right control key as well */
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL")      PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
		/* TI99/4a has a second shift key which maps the same */
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
		/* The original function key is located on the right, but we accept the left alt key as well */
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("FCTN")      PORT_CODE(KEYCODE_RALT) PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_SHIFT_2)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("= + QUIT")  PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+') PORT_CHAR(UCHAR_MAMEKEY(F12))

	PORT_START("COL1")  // col 1
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)     PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(UCHAR_MAMEKEY(DOWN))
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)     PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR('~')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)     PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR(UCHAR_MAMEKEY(LEFT))
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)     PORT_CHAR('2') PORT_CHAR('@') PORT_CHAR(UCHAR_MAMEKEY(F2))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 ( BACK")  PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(') PORT_CHAR(UCHAR_MAMEKEY(F9))
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)     PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR('\'')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)     PORT_CHAR('l') PORT_CHAR('L')
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)  PORT_CHAR('.') PORT_CHAR('>')

	PORT_START("COL2")  // col 2
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)     PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR('`')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)     PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(UCHAR_MAMEKEY(UP))
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)     PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 # ERASE") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#') PORT_CHAR(UCHAR_MAMEKEY(F3))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 * REDO")  PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*') PORT_CHAR(UCHAR_MAMEKEY(F8))
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)     PORT_CHAR('i') PORT_CHAR('I') PORT_CHAR('?')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)     PORT_CHAR('k') PORT_CHAR('K')
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("COL3")  // col 3
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)     PORT_CHAR('v') PORT_CHAR('V')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)     PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR('[')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)     PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR('{')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 $ CLEAR") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') PORT_CHAR(UCHAR_MAMEKEY(F4))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 & AID")   PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&') PORT_CHAR(UCHAR_MAMEKEY(F7))
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)     PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR('_')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)     PORT_CHAR('j') PORT_CHAR('J')
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)     PORT_CHAR('m') PORT_CHAR('M')

	PORT_START("COL4")  // col 4
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)     PORT_CHAR('b') PORT_CHAR('B')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)     PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(']')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)     PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR('}')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 % BEGIN")  PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%') PORT_CHAR(UCHAR_MAMEKEY(F5))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 ^ PROC'D") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^') PORT_CHAR(UCHAR_MAMEKEY(F6))
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)     PORT_CHAR('y') PORT_CHAR('Y')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)     PORT_CHAR('h') PORT_CHAR('H')
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)     PORT_CHAR('n') PORT_CHAR('N')

	PORT_START("COL5")  // col 5
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)     PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR('\\')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)     PORT_CHAR('q') PORT_CHAR('Q')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)     PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR('|')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)     PORT_CHAR('1') PORT_CHAR('!') PORT_CHAR(UCHAR_MAMEKEY(DEL))
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)     PORT_CHAR('0') PORT_CHAR(')') PORT_CHAR(UCHAR_MAMEKEY(F10))
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)     PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR('\"')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('-')

	PORT_START("ALPHA") /* one more port for Alpha line */
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Alpha Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE

	/* another version of Alpha Lock which is non-toggling; this is useful when we want to attach
	    a real TI keyboard for input. For home computers, the Alpha Lock / Shift Lock was a physically
	    locking key. */
	PORT_START("ALPHA1")
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Alpha Lock non-toggle") PORT_CODE(KEYCODE_RWIN)

INPUT_PORTS_END


/*****************************************************************************
    Components
******************************************************************************/

uint8_t ti99_4x_state::cruread(offs_t offset)
{
	uint8_t value = 0;
	LOGMASKED(LOG_CRUREAD, "read access to CRU address %04x\n", offset << 1);

	// Internal 9901
	// We cannot use the map because devices in the Peribox may want to see the
	// CRU address on the bus (see sidmaster)
	if ((offset & 0xfc00)==0)
		value = m_tms9901->read(offset & 0x3f);

	// Let the gromport (not in the QI version) and the p-box behind the I/O port
	// decide whether they want to change the value at the CRU address
	if (m_model != MODEL_4QI) m_gromport->crureadz(offset<<1, &value);
	m_ioport->crureadz(offset<<1, &value);

	return value;
}

void ti99_4x_state::cruwrite(offs_t offset, uint8_t data)
{
	LOGMASKED(LOG_CRU, "Write access to CRU address %04x\n", offset << 1);

	// Internal 9901
	// We cannot use the map because device in the Peribox may want to see the
	// CRU address on the bus (see sidmaster)
	if ((offset & 0xfc00)==0)
		m_tms9901->write(offset & 0x3f, data);

	// The QI version does not propagate the CRU signals to the cartridge slot
	if (m_model != MODEL_4QI) m_gromport->cruwrite(offset<<1, data);
	m_ioport->cruwrite(offset<<1, data);
}

void ti99_4x_state::external_operation(offs_t offset, uint8_t data)
{
	static char const *const extop[8] = { "inv1", "inv2", "IDLE", "RSET", "inv3", "CKON", "CKOF", "LREX" };
	// Some games (e.g. Slymoids) actually use IDLE for synchronization
	if (offset == IDLE_OP) return;
	else
		LOGMASKED(LOG_WARN, "External operation %s not implemented on TI-99 board\n", extop[offset]);
}

/***************************************************************************
    TI99/4x-specific tms9901 I/O handlers

        Bit     Meaning
         0      - (control)   -
         1      /INT1         (input) EXTINT
         2      /INT2         (input) VDP
         3      /INT3         (input) Keyboard = line     / Joystick button
         4      /INT4         (input) Keyboard Space line / Joystick left
         5      /INT5         (input) Keyboard Enter line / Joystick right
         6      /INT6         (input) Keyboard 0 line     / Joystick down
         7  31  /INT7   P15   (input) Keyboard Fctn line  / Joystick up / AlphaLock

         8  30  /INT8   P14   (input) Keyboard Shift line
         9  29  /INT9   P13   (input) Keyboard Ctrl line
         10 28  /INT10  P12   (input) Keyboard Z line
         11 27  /INT11  P11   (input) Cassette audio
         12 26  /INT12  P10   (input) 99/4: Handset, 99/4A: 1
         13 25  /INT13  P9    (output) Cassette audio   (1?)
         14 24  /INT14  P8    (output) Audio Gate       (1?)
         15 23  /INT15  P7    (output) Motor CS2        (1?)

         16             P0    (output) 99/4: Handset ack (1?)
         17             P1    (input)  99/4: Handset     (1?)
         18             P2    (output) Key col 0         (1?)
         19             P3    (output) Key col 1         (1?)
         20             P4    (output) Key col 2         (1?)
         21             P5    (output) AlphaLock select  (1)
         22             P6    (output) Motor CS1         (1?)

         enum { INT1, ... INT7_P15, INT8_P14, ..., P5, P6 }

    The hardware bug of the TI-99/4A keyboard: You have to release the
    AlphaLock key when using joysticks.
    The AlphaLock key was obviously added to the 99/4 matrix in a quite adhoc
    way; a separate 9901 line (P5) is used to deliver the 0 level to be routed
    through the switch. When AlphaLock is depressed, it connects the /INT7
    line via two 470 ohm resistors to P5. When the AlphaLock key is not
    scanned, P5 is 1, pulling up the /INT7 line. Moving the joystick lever up
    should pull it down, but due to the additional resistance in the long
    cable in the joystick, the sum resistance becomes too high to safely
    pull down the level, and the 9901 does not sense a 0 on its /INT7 input.

                     Alpha
    P5 -----[470]-----/ +
                        |            Joy up
    /INT7--+--[470]-----+----[xxx]-----/ ---[280]--- 0 (if column=110 or 111)
           |
           +---[10k]--- 1
               pull-up

    The typical fix was to insert a diode at the Alphalock key.
***************************************************************************/

uint8_t ti99_4x_state::psi_input_4(offs_t offset)
{
	switch (offset)
	{
	case tms9901_device::INT1:
		return (m_int1==CLEAR_LINE)? 1 : 0;
	case tms9901_device::INT2:
		return (m_int2==CLEAR_LINE)? 1 : 0;
	case tms9901_device::INT3:
	case tms9901_device::INT4:
	case tms9901_device::INT5:
	case tms9901_device::INT6:
	case tms9901_device::INT7_P15:
		// Keyboard ACTIVE_LOW, Joysticks ACTIVE_LOW
		if (m_keyboard_column >= 5)
			return BIT(m_joyport->read_port(), offset-tms9901_device::INT3);
		else
			return BIT(m_keyboard[m_keyboard_column]->read(), offset-tms9901_device::INT3);
	case tms9901_device::INT8_P14:
	case tms9901_device::INT9_P13:
	case tms9901_device::INT10_P12:
		if (m_keyboard_column >= 5)  // no joystick lines after /INT7
			return 1;
		else
			return BIT(m_keyboard[m_keyboard_column]->read(), offset-tms9901_device::INT3);
	case tms9901_device::INT11_P11:
		return (m_cassette1->input() > 0);
	case tms9901_device::INT12_P10:
		return (m_int12==CLEAR_LINE)? 1 : 0;
	case tms9901_device::P1:
		return BIT(m_joyport->read_port(), 5);  // 0x20
	default:
		return 1;
	}
}

uint8_t ti99_4x_state::psi_input_4a(offs_t offset)
{
	int alphabias=0;

	switch (offset)
	{
	case tms9901_device::INT1:
		return (m_int1==CLEAR_LINE)? 1 : 0;
	case tms9901_device::INT2:
		return (m_int2==CLEAR_LINE)? 1 : 0;
	case tms9901_device::INT3:
	case tms9901_device::INT4:
	case tms9901_device::INT5:
	case tms9901_device::INT6:
		// Keyboard ACTIVE_LOW (s.o.)
		// Joysticks ACTIVE_LOW (handset.cpp)
		if (m_keyboard_column >= 6)
			return BIT(m_joyport->read_port(), offset-tms9901_device::INT3);
		else
			return BIT(m_keyboard[m_keyboard_column]->read(), offset-tms9901_device::INT3);

	case tms9901_device::INT7_P15:
		if (m_keyboard_column >= 6) // Joysticks
		{
			// If the Alpha Lock bug is not fixed
			if (m_alphabug->read()!=0)
				alphabias = ~(m_alpha->read() & m_alpha1->read());

			return BIT(m_joyport->read_port() | alphabias, offset-tms9901_device::INT3);
		}
		else
		{
			if (m_check_alphalock)
			{
				return BIT(m_alpha->read() & m_alpha1->read(), offset-tms9901_device::INT3);
			}
			else
				return BIT(m_keyboard[m_keyboard_column]->read(), offset-tms9901_device::INT3);
		}

	case tms9901_device::INT8_P14:
	case tms9901_device::INT9_P13:
	case tms9901_device::INT10_P12:
		if (m_keyboard_column >= 6)  // no joystick lines after /INT7
			return 1;
		else
			return BIT(m_keyboard[m_keyboard_column]->read(), offset-tms9901_device::INT3);
	case tms9901_device::INT11_P11:
		// CS2 is write-only
		return (m_cassette1->input()>0);
	default:
		return 1;
	}
}

/*
    Handler for TMS9901 P0 pin (handset data acknowledge); only for 99/4
*/
void ti99_4x_state::handset_ack(int state)
{
	// Write a value to the joyport. If there is a handset this will set its
	// ACK line.
	m_joyport->write_port(state==ASSERT_LINE? 0x01 : 0x00);
}

/*
    WRITE key column select (P2-P4), TI-99/4
*/
void ti99_4x_state::set_keyboard_column(int number, int data)
{
	if (data != 0)
		m_keyboard_column |= 1 << number;
	else
		m_keyboard_column &= ~ (1 << number);

	if (m_keyboard_column >= (m_model==MODEL_4? 5:6))
	{
		m_joyport->write_port(m_keyboard_column - (m_model==MODEL_4? 5:6) + 1);
	}

	// TI-99/4:  joystick 1 = column 5
	//           joystick 2 = column 6
	// (only for the prototype versions; the released versions had no IR
	// handset and the board was already redesigned to use columns 6 and 7)

	// TI-99/4A: joystick 1 = column 6
	//           joystick 2 = column 7
}

void ti99_4x_state::keyC0(int state)
{
	set_keyboard_column(0, state);
}

void ti99_4x_state::keyC1(int state)
{
	set_keyboard_column(1, state);
}

void ti99_4x_state::keyC2(int state)
{
	set_keyboard_column(2, state);
}

/*
    Select alpha lock line - TI99/4a only (P5)
*/
void ti99_4x_state::alphaW(int state)
{
	m_check_alphalock = (state==0);
}

/*
    Control CS1 tape unit motor (P6)
*/
void ti99_4x_state::cs1_motor(int state)
{
	m_cassette1->change_state(state==ASSERT_LINE? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR);
}

/*
    Control CS2 tape unit motor (P7)
*/
void ti99_4x_state::cs2_motor(int state)
{
	m_cassette2->change_state(state==ASSERT_LINE? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR);
}

/*
    Audio gate (P8)
    Set to 1 before using tape: this enables the mixing of tape input sound
    with computer sound.
    We do not really need to emulate this as the tape recorder generates sound
    on its own.
    TODO: Emulate a pop sound when turning on/off the audio gate; there are
    some few programs that generate a sound with this feature
*/
void ti99_4x_state::audio_gate(int state)
{
}

/*
    Tape output (P9)
    I think polarity is correct, but don't take my word for it.
*/
void ti99_4x_state::cassette_output(int state)
{
	m_cassette1->output(state==ASSERT_LINE? +1 : -1);
	m_cassette2->output(state==ASSERT_LINE? +1 : -1);
}

void ti99_4x_state::tms9901_interrupt(int state)
{
	m_cpu->set_input_line(INT_9900_INTREQ, state);
}

uint8_t ti99_4x_state::interrupt_level()
{
	// The interrupt level must be fetched from the 9901;
	// on the TI-99 systems these IC lines are not used; the input lines
	// at the CPU are hardwired to level 1.
	return 1;
}


/*
    Clock line from the CPU. Used to control wait state generation.
*/
void ti99_4x_state::clock_out(int state)
{
	m_tms9901->phi_line(state);
	m_datamux->clock_in(state);
	m_ioport->clock_in(state);
}

/*
    GROMCLK from VDP, propagating to datamux
*/
void ti99_4x_state::gromclk_in(int state)
{
	m_datamux->gromclk_in(state);
}

/*
    Used by the EVPC
*/
TIMER_CALLBACK_MEMBER(ti99_4x_state::gromclk_tick)
{
	// Pulse it
	if (m_datamux != nullptr)
	{
		gromclk_in(ASSERT_LINE);
		gromclk_in(CLEAR_LINE);
	}
}

/*****************************************************************************/

void ti99_4x_state::video_interrupt_evpc_in(int state)
{
	LOGMASKED(LOG_INTERRUPTS, "VDP INT2 from EVPC on tms9901, level=%d\n", state);
	m_int2 = (line_state)state;
	m_tms9901->set_int_line(2, state);
}

/*
    set the state of TMS9901's INT2 (called by the tms9928 core)
*/
void ti99_4x_state::video_interrupt_in(int state)
{
	LOGMASKED(LOG_INTERRUPTS, "VDP %s /INT2 on TMS9901\n", (state==ASSERT_LINE)? "asserts" : "clears");
	m_int2 = (line_state)state;
	m_tms9901->set_int_line(2, state);
	// Pulse for the handset
	if (m_model == MODEL_4) m_joyport->pulse_clock();
}

/*
    set the state of TMS9901's INT12 (called by the handset prototype of TI-99/4)
*/
void ti99_4x_state::handset_interrupt_in(int state)
{
	LOGMASKED(LOG_INTERRUPTS, "joyport INT12 on tms9901, level=%d\n", state);
	m_int12 = (line_state)state;
	m_tms9901->set_int_line(12, state);
}

/*
    One of the common hardware mods was to add a switch to trigger a LOAD
    interrupt
*/
INPUT_CHANGED_MEMBER( ti99_4x_state::load_interrupt )
{
	LOGMASKED(LOG_RESETLOAD, "LOAD interrupt, level=%d\n", newval);
	m_cpu->set_input_line(INT_9900_LOAD, (newval==0)? ASSERT_LINE : CLEAR_LINE);
}

/***********************************************************
    Links to external devices
***********************************************************/

/*
    We combine the incoming READY signals and propagate them to the CPU.
    An alternative would be to let the CPU get the READY state, but this would
    be a much higher overhead, as this happens in each clock tick.
*/
void ti99_4x_state::console_ready_join(int id, int state)
{
	if (state==CLEAR_LINE)
		m_nready_combined |= id;
	else
		m_nready_combined &= ~id;

	if (m_nready_prev != m_nready_combined)
		LOGMASKED(LOG_READY, "READY bits = %04x\n", ~m_nready_combined);

	m_nready_prev = m_nready_combined;
	m_cpu->set_ready(m_nready_combined==0);
}

/*
    Connections to the READY line. This might look a bit ugly; we need an
    implementation of a "Wired AND" device.
*/
void ti99_4x_state::console_ready_grom(int state)
{
	LOGMASKED(LOG_READY, "GROM ready = %d\n", state);
	console_ready_join(READY_GROM, state);
}

void ti99_4x_state::console_ready_dmux(int state)
{
	console_ready_join(READY_DMUX, state);
}

void ti99_4x_state::console_ready_pbox(int state)
{
	console_ready_join(READY_PBOX, state);
}

void ti99_4x_state::console_ready_sound(int state)
{
	console_ready_join(READY_SOUND, state);
}

void ti99_4x_state::console_ready_cart(int state)
{
	console_ready_join(READY_CART, state);
}

/*
    The RESET line leading to a reset of the CPU. This is asserted when a
    cartridge is plugged in.
*/
void ti99_4x_state::console_reset(int state)
{
	if (machine().phase() != machine_phase::INIT)
	{
		LOGMASKED(LOG_RESETLOAD, "Console reset line = %d\n", state);
		m_cpu->set_input_line(INT_9900_RESET, state);
		// Don't reset the (not existing) console video chip in the EVPC configuration
		if (m_model != MODEL_4EV)
			m_video->reset_line(state);
		m_ioport->reset_in(state);
	}
}

void ti99_4x_state::extint(int state)
{
	LOGMASKED(LOG_INTERRUPTS, "EXTINT level = %02x\n", state);
	m_int1 = (line_state)state;
	m_tms9901->set_int_line(1, state);
}

void ti99_4x_state::notconnected(int state)
{
	LOGMASKED(LOG_INTERRUPTS, "Setting a not connected line ... ignored\n");
}

/******************************************************************************
    Machine definitions
******************************************************************************/

void ti99_4x_state::driver_start()
{
	m_nready_combined = 0;
	// Removing the TMS9928a requires to add a replacement for the GROMCLK.
	// In the real hardware this is a circuit (REPL99x) that fits into the VDP socket
	if (m_model == MODEL_4EV)
		m_gromclk_timer = timer_alloc(FUNC(ti99_4x_state::gromclk_tick), this);

	save_item(NAME(m_keyboard_column));
	save_item(NAME(m_check_alphalock));
	save_item(NAME(m_nready_combined));
	save_item(NAME(m_nready_prev));
	save_item(NAME(m_model));
	save_item(NAME(m_int1));
	save_item(NAME(m_int2));
	save_item(NAME(m_int12));
}

void ti99_4x_state::driver_reset()
{
	m_cpu->set_ready(ASSERT_LINE);
	m_cpu->set_hold(CLEAR_LINE);
	m_int1 = CLEAR_LINE;
	m_int2 = CLEAR_LINE;
	m_int12 = CLEAR_LINE;
	if (m_model == MODEL_4EV)
		m_gromclk_timer->adjust(attotime::zero, 0, attotime::from_hz(XTAL(10'738'635)/24));
}

/**********************************************************
    Common configuration
**********************************************************/
void ti99_4x_state::ti99_4_common(machine_config& config)
{
	// CPU
	TMS9900(config, m_cpu, 3000000);
	m_cpu->set_addrmap(AS_PROGRAM, &ti99_4x_state::memmap);
	m_cpu->set_addrmap(AS_IO, &ti99_4x_state::crumap);
	m_cpu->set_addrmap(tms99xx_device::AS_SETADDRESS, &ti99_4x_state::memmap_setaddress);
	m_cpu->extop_cb().set(FUNC(ti99_4x_state::external_operation));
	m_cpu->intlevel_cb().set(FUNC(ti99_4x_state::interrupt_level));
	m_cpu->clkout_cb().set(FUNC(ti99_4x_state::clock_out));

	// Programmable system interface (driven by CLKOUT)
	TMS9901(config, m_tms9901, 0);
	m_tms9901->p_out_cb(2).set(FUNC(ti99_4x_state::keyC0));
	m_tms9901->p_out_cb(3).set(FUNC(ti99_4x_state::keyC1));
	m_tms9901->p_out_cb(4).set(FUNC(ti99_4x_state::keyC2));
	m_tms9901->p_out_cb(6).set(FUNC(ti99_4x_state::cs1_motor));
	m_tms9901->p_out_cb(7).set(FUNC(ti99_4x_state::cs2_motor));
	m_tms9901->p_out_cb(8).set(FUNC(ti99_4x_state::audio_gate));
	m_tms9901->p_out_cb(9).set(FUNC(ti99_4x_state::cassette_output));
	m_tms9901->intreq_cb().set(FUNC(ti99_4x_state::tms9901_interrupt));

	// Databus multiplexer
	TI99_DATAMUX(config, m_datamux, 0).ready_cb().set(FUNC(ti99_4x_state::console_ready_dmux));

	// Cartridge port (aka GROMport)
	TI99_GROMPORT(config, m_gromport, 0, ti99_gromport_options, "single");
	m_gromport->ready_cb().set(FUNC(ti99_4x_state::console_ready_cart));
	m_gromport->reset_cb().set(FUNC(ti99_4x_state::console_reset));

	// Scratch pad RAM 256 bytes
	RAM(config, TI99_PADRAM_TAG).set_default_size("256").set_default_value(0);

	// Optional RAM expansion
	RAM(config, TI99_EXPRAM_TAG).set_default_size("32K").set_default_value(0);

	// Software list
	SOFTWARE_LIST(config, "cart_list_ti99").set_original("ti99_cart");

	// Cassette drives. Second drive is record-only.
	SPEAKER(config, "cass_out").front_center();
	CASSETTE(config, "cassette1", 0).add_route(ALL_OUTPUTS, "cass_out", 0.25);
	CASSETTE(config, "cassette2", 0);

	// GROM devices
	TMC0430(config, TI99_GROM0_TAG, TI99_CONSOLEGROM, 0x0000, 0).ready_cb().set(FUNC(ti99_4x_state::console_ready_grom));
	TMC0430(config, TI99_GROM1_TAG, TI99_CONSOLEGROM, 0x2000, 1).ready_cb().set(FUNC(ti99_4x_state::console_ready_grom));
	TMC0430(config, TI99_GROM2_TAG, TI99_CONSOLEGROM, 0x4000, 2).ready_cb().set(FUNC(ti99_4x_state::console_ready_grom));
}

/**********************************************************************
    TI-99/4 - predecessor of the more popular TI-99/4A
***********************************************************************/

void ti99_4x_state::ti99_4(machine_config& config)
{
	// Common configuration
	ti99_4_common(config);
	m_model = MODEL_4;

	// Main board
	// Add handset interrupt to 9901
	m_tms9901->p_out_cb(0).set(FUNC(ti99_4x_state::handset_ack));
	m_tms9901->read_cb().set(FUNC(ti99_4x_state::psi_input_4)); // use a separate one for 99/4

	// Input/output port: normal config
	TI99_IOPORT(config, m_ioport, 0, ti99_ioport_options_plain, nullptr);
	m_ioport->extint_cb().set(FUNC(ti99_4x_state::extint));
	m_ioport->ready_cb().set(TI99_DATAMUX_TAG, FUNC(bus::ti99::internal::datamux_device::ready_line));

	// Sound hardware (not in EVPC variant)
	SPEAKER(config, "sound_out").front_center();
	sn94624_device& soundgen(SN94624(config, TI99_SOUNDCHIP_TAG, 3579545/8));
	soundgen.ready_cb().set(FUNC(ti99_4x_state::console_ready_sound));
	soundgen.add_route(ALL_OUTPUTS, "sound_out", 0.75);

	// Joystick port. We can connect a joyport mouse or a handset (99/4-specific).
	TI99_JOYPORT(config, m_joyport, 0, ti99_joyport_options_994, "twinjoy");
	m_joyport->int_cb().set(FUNC(ti99_4x_state::handset_interrupt_in));
}

/*
    US version: 60 Hz, NTSC
*/
void ti99_4x_state::ti99_4_60hz(machine_config &config)
{
	ti99_4(config);
	TMS9918(config, m_video, XTAL(10'738'635));
	m_video->set_vram_size(0x4000);
	m_video->int_callback().set(FUNC(ti99_4x_state::video_interrupt_in));
	m_video->gromclk_callback().set(FUNC(ti99_4x_state::gromclk_in));
	m_video->set_screen(TI99_SCREEN_TAG);

	SCREEN(config, TI99_SCREEN_TAG, SCREEN_TYPE_RASTER);
}

/*
    European version: 50 Hz, PAL
*/
void ti99_4x_state::ti99_4_50hz(machine_config &config)
{
	ti99_4(config);
	TMS9929(config, m_video, XTAL(10'738'635));
	m_video->set_vram_size(0x4000);
	m_video->int_callback().set(FUNC(ti99_4x_state::video_interrupt_in));
	m_video->gromclk_callback().set(FUNC(ti99_4x_state::gromclk_in));
	m_video->set_screen(TI99_SCREEN_TAG);

	SCREEN(config, TI99_SCREEN_TAG, SCREEN_TYPE_RASTER);
}

/**********************************************************************
    TI-99/4A - replaced the 99/4 and became the standard TI-99 console
***********************************************************************/

void ti99_4x_state::ti99_4a(machine_config& config)
{
	// Common configuration
	ti99_4_common(config);
	m_model = MODEL_4A;

	// Main board
	// Add Alphalock to 9901
	m_tms9901->p_out_cb(5).set(FUNC(ti99_4x_state::alphaW));
	m_tms9901->read_cb().set(FUNC(ti99_4x_state::psi_input_4a));

	// Input/output port: Normal config
	TI99_IOPORT(config, m_ioport, 0, ti99_ioport_options_plain, nullptr);
	m_ioport->extint_cb().set(FUNC(ti99_4x_state::extint));
	m_ioport->ready_cb().set(TI99_DATAMUX_TAG, FUNC(bus::ti99::internal::datamux_device::ready_line));

	// Sound hardware (not in EVPC variant)
	SPEAKER(config, "sound_out").front_center();
	sn94624_device& soundgen(SN94624(config, TI99_SOUNDCHIP_TAG, 3579545/8));
	soundgen.ready_cb().set(FUNC(ti99_4x_state::console_ready_sound));
	soundgen.add_route(ALL_OUTPUTS, "sound_out", 0.75);

	// Joystick port
	TI99_JOYPORT(config, m_joyport, 0, ti99_joyport_options_mouse, "twinjoy");
}

/*
    US version: 60 Hz, NTSC
*/
void ti99_4x_state::ti99_4a_60hz(machine_config &config)
{
	ti99_4a(config);
	TMS9918A(config, m_video, XTAL(10'738'635));
	m_video->set_vram_size(0x4000);
	m_video->int_callback().set(FUNC(ti99_4x_state::video_interrupt_in));
	m_video->gromclk_callback().set(FUNC(ti99_4x_state::gromclk_in));
	m_video->set_screen(TI99_SCREEN_TAG);

	SCREEN(config, TI99_SCREEN_TAG, SCREEN_TYPE_RASTER);
}

/*
    European version: 50 Hz, PAL
*/
void ti99_4x_state::ti99_4a_50hz(machine_config &config)
{
	ti99_4a(config);
	TMS9929A(config, m_video, XTAL(10'738'635));
	m_video->set_vram_size(0x4000);
	m_video->int_callback().set(FUNC(ti99_4x_state::video_interrupt_in));
	m_video->gromclk_callback().set(FUNC(ti99_4x_state::gromclk_in));
	m_video->set_screen(TI99_SCREEN_TAG);

	SCREEN(config, TI99_SCREEN_TAG, SCREEN_TYPE_RASTER);
}

/************************************************************************
    TI-99/4QI - the final version of the TI-99/4A
    This was a last modification of the console. One purpose was to lower
    production costs by a redesigned board layout. The other was that TI
    removed the ROM search for cartridges so that only cartridges with GROMs
    could be started, effectively kicking out all third-party cartridges like
    those from Atarisoft.
*************************************************************************/

/*
    US version: 60 Hz, NTSC
    There were no European versions.
*/
void ti99_4x_state::ti99_4qi_60hz(machine_config &config)
{
	ti99_4a(config);
	m_model = MODEL_4QI;

	TMS9918A(config, m_video, XTAL(10'738'635));
	m_video->set_vram_size(0x4000);
	m_video->int_callback().set(FUNC(ti99_4x_state::video_interrupt_in));
	m_video->gromclk_callback().set(FUNC(ti99_4x_state::gromclk_in));
	m_video->set_screen(TI99_SCREEN_TAG);

	SCREEN(config, TI99_SCREEN_TAG, SCREEN_TYPE_RASTER);
}

/************************************************************************
    TI-99/4A with 80-column support. Actually a separate expansion card (EVPC),
    replacing the console video processor.

    Note that the sound chip is also moved to this card, because the SGCPU,
    which is intended to use the EVPC, does not have an own sound chip.
*************************************************************************/

void ti99_4x_state::ti99_4ev_60hz(machine_config& config)
{
	// Common configuration
	ti99_4_common(config);
	m_model = MODEL_4EV;

	// Main board
	// Add Alphalock
	m_tms9901->p_out_cb(5).set(FUNC(ti99_4x_state::alphaW));
	m_tms9901->read_cb().set(FUNC(ti99_4x_state::psi_input_4a));

	// EVPC connector
	// This is needed for delivering the video interrupt from the
	// EVPC expansion card into the console, after the video processor has been removed
	TI99_EVPCCONN(config, TI99_EVPC_CONN_TAG, 0).vdpint_cb().set(FUNC(ti99_4x_state::video_interrupt_evpc_in));

	// Input/output port: Configure for EVPC
	TI99_IOPORT(config, m_ioport, 0, ti99_ioport_options_evpc, "peb");
	m_ioport->extint_cb().set(FUNC(ti99_4x_state::extint));
	m_ioport->ready_cb().set(TI99_DATAMUX_TAG, FUNC(bus::ti99::internal::datamux_device::ready_line));

	// Joystick port
	// No joyport mouse, since we have a bus mouse with the EVPC
	TI99_JOYPORT(config, m_joyport, 0, ti99_joyport_options_plain, "twinjoy");
}

/*****************************************************************************
    ROM loading
    Note that we use the same ROMset for 50Hz and 60Hz versions.
    ROMs for peripheral equipment have been moved to the respective files.
******************************************************************************/
#define rom_ti99_4e rom_ti99_4
#define rom_ti99_4ae rom_ti99_4a

ROM_START(ti99_4)
	// CPU memory space
	// ROM files do not have a CRC16 at their end
	ROM_REGION16_BE(0x2000, TI99_CONSOLEROM, 0)
	ROM_LOAD16_BYTE("994_rom_hb.u610", 0x0000, 0x1000, CRC(6fcf4b15) SHA1(d085213c64701d429ae535f9a4ac8a50427a8343)) /* CPU ROMs high */
	ROM_LOAD16_BYTE("994_rom_lb.u611", 0x0001, 0x1000, CRC(491c21d1) SHA1(7741ae9294c51a44a78033d1b77c01568a6bbfb9)) /* CPU ROMs low */

	// GROM memory space
	// GROM files do not have a CRC16 at their end
	ROM_REGION(0x6000, TI99_CONSOLEGROM, 0)
	ROM_LOAD("994_grom0.u500", 0x0000, 0x1800, CRC(aa757e13) SHA1(4658d3d01c0131c283a30cebd12e76754d41a84a)) /* system GROM 0 */
	ROM_LOAD("994_grom1.u501", 0x2000, 0x1800, CRC(c863e460) SHA1(6d849a76011273a069a98ed0c3feaf13831c942f)) /* system GROM 1 */
	ROM_LOAD("994_grom2.u502", 0x4000, 0x1800, CRC(b0eda548) SHA1(725e3f26f8c819f356e4bb405b4102b5ae1e0e70)) /* system GROM 2 */
ROM_END

ROM_START(ti99_4a)
	// CPU memory space
	// ROM files have valid CRC16 as last word
	ROM_REGION16_BE(0x2000, TI99_CONSOLEROM, 0)
	ROM_LOAD16_BYTE("994a_rom_hb.u610", 0x0000, 0x1000, CRC(ee859c5f) SHA1(a45245707c3dccea902b718554a882d214a82504)) /* CPU ROMs high */
	ROM_LOAD16_BYTE("994a_rom_lb.u611", 0x0001, 0x1000, CRC(37859301) SHA1(f4e774fd5913b387a763f1b8de5524c54b255434)) /* CPU ROMs low */

	// GROM memory space
	// GROM files have valid CRC16 as last word
	ROM_REGION(0x6000, TI99_CONSOLEGROM, 0)
	ROM_LOAD("994a_grom0.u500", 0x0000, 0x1800, CRC(2445a5e8) SHA1(ea15d8b0ac52112dc0d5f4ab9a79ac8ca1cc1bbc)) /* system GROM 0 */
	ROM_LOAD("994a_grom1.u501", 0x2000, 0x1800, CRC(b8f367ab) SHA1(3ecead4b83ec525084c70b6123d4053f8a80e1f7)) /* system GROM 1 */
	ROM_LOAD("994a_grom2.u502", 0x4000, 0x1800, CRC(e0bb5341) SHA1(e255f0d65d69b927cecb8fcfac7a4c17d585ea96)) /* system GROM 2 */
ROM_END

ROM_START(ti99_4qi)
	// CPU memory space
	// ROM files are the same as for TI-99/4A, but located in sockets u3 and u5
	ROM_REGION16_BE(0x2000, TI99_CONSOLEROM, 0)
	ROM_LOAD16_BYTE("994a_rom_hb.u610", 0x0000, 0x1000, CRC(ee859c5f) SHA1(a45245707c3dccea902b718554a882d214a82504)) /* CPU ROMs high */
	ROM_LOAD16_BYTE("994a_rom_lb.u611", 0x0001, 0x1000, CRC(37859301) SHA1(f4e774fd5913b387a763f1b8de5524c54b255434)) /* CPU ROMs low */

	// GROM memory space
	// GROM files have valid CRC16 as last word
	// GROM1 and GROM2 are the same as for TI-99/4A, located in u30 and u31
	ROM_REGION(0x6000, TI99_CONSOLEGROM, 0)
	ROM_LOAD("994qi_grom0.u29", 0x0000, 0x1800, CRC(8b07772d) SHA1(95dcf5b7350ade65297eadd2d680c27561cc975c)) /* system GROM 0 */
	ROM_LOAD("994a_grom1.u501", 0x2000, 0x1800, CRC(b8f367ab) SHA1(3ecead4b83ec525084c70b6123d4053f8a80e1f7)) /* system GROM 1 */
	ROM_LOAD("994a_grom2.u502", 0x4000, 0x1800, CRC(e0bb5341) SHA1(e255f0d65d69b927cecb8fcfac7a4c17d585ea96)) /* system GROM 2 */
ROM_END

ROM_START(ti99_4ev)
	// CPU memory space
	// ROM files have valid CRC16 as last word
	// ROM files are the same as for TI-99/4A
	ROM_REGION16_BE(0x2000, TI99_CONSOLEROM, 0)
	ROM_LOAD16_BYTE("994a_rom_hb.u610", 0x0000, 0x1000, CRC(ee859c5f) SHA1(a45245707c3dccea902b718554a882d214a82504)) /* CPU ROMs high */
	ROM_LOAD16_BYTE("994a_rom_lb.u611", 0x0001, 0x1000, CRC(37859301) SHA1(f4e774fd5913b387a763f1b8de5524c54b255434)) /* CPU ROMs low */

	// GROM memory space
	// GROM files have valid CRC16 as last word
	// GROM1 has been patched to support the EVPC, but the CRC16 was not updated, being invalid now
	ROM_REGION(0x6000, TI99_CONSOLEGROM, 0)
	ROM_LOAD("994a_grom0.u500", 0x0000, 0x1800, CRC(2445a5e8) SHA1(ea15d8b0ac52112dc0d5f4ab9a79ac8ca1cc1bbc)) /* system GROM 0 */
	ROM_LOAD("994ev_grom1.u501", 0x2000, 0x1800, CRC(6885326d) SHA1(1a98de5ee886dce705de5cce11034a7be31aceac)) /* system GROM 1 */
	ROM_LOAD("994a_grom2.u502", 0x4000, 0x1800, CRC(e0bb5341) SHA1(e255f0d65d69b927cecb8fcfac7a4c17d585ea96)) /* system GROM 2 */
ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT   COMPAT  MACHINE        INPUT    CLASS          INIT        COMPANY              FULLNAME                            FLAGS
COMP( 1979, ti99_4,   0,       0,      ti99_4_60hz,   ti99_4,  ti99_4x_state, empty_init, "Texas Instruments", "TI-99/4 Home Computer (US)",       MACHINE_SUPPORTS_SAVE)
COMP( 1980, ti99_4e,  ti99_4,  0,      ti99_4_50hz,   ti99_4,  ti99_4x_state, empty_init, "Texas Instruments", "TI-99/4 Home Computer (Europe)",   MACHINE_SUPPORTS_SAVE)
COMP( 1981, ti99_4a,  0,       0,      ti99_4a_60hz,  ti99_4a, ti99_4x_state, empty_init, "Texas Instruments", "TI-99/4A Home Computer (US)",      MACHINE_SUPPORTS_SAVE)
COMP( 1981, ti99_4ae, ti99_4a, 0,      ti99_4a_50hz,  ti99_4a, ti99_4x_state, empty_init, "Texas Instruments", "TI-99/4A Home Computer (Europe)",  MACHINE_SUPPORTS_SAVE)
COMP( 1983, ti99_4qi, ti99_4a, 0,      ti99_4qi_60hz, ti99_4a, ti99_4x_state, empty_init, "Texas Instruments", "TI-99/4QI Home Computer (US)",     MACHINE_SUPPORTS_SAVE)
COMP( 1994, ti99_4ev, ti99_4a, 0,      ti99_4ev_60hz, ti99_4a, ti99_4x_state, empty_init, "Texas Instruments", "TI-99/4A Home Computer with EVPC", MACHINE_SUPPORTS_SAVE)



ti99_8.cpp
<---------------------------------------------------------------------->
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/****************************************************************************

    TI-99/8

    The TI-99/8 was the envisaged successor to the TI-99/4A but never passed
    its prototype state. Only a few dozen consoles were built. The ROMs
    were not even finalized, so the few available consoles have different
    operating system versions and capabilities.


    Characteristics
    ---------------

    Name: "Texas Instruments Computer TI-99/8" (no "Home")

    Unofficial nickname: "Armadillo"

    CPU: Single-CPU system using a TMS9995, but as a variant named MP9537. This
         variant does not offer on-chip RAM or decrementer.

    Video: TMS9118 Video Display Processor with 16 KiB RAM. The 9118 has the
         same capabilities as the 9918/28 in the TI-99/4A, except for the
         missing GROM clock (which must be provided separately) and the
         different DRAM type (2 chips TMS 4416 16K*4). Delivers a 60 Hz
         interrupt to the CPU via the PSI.

    Keyboard: 50-key keyboard, slightly different to the TI-99/4A, but also with
         modifiers Control, Function, Shift, Caps Lock. Connects to the TMS 9901
         PSI like in the TI-99/4A, but the pin assignment and key matrix
         are different:
         - P0-P3: column select
         - INT6*-INT11*: row inputs (INT6* is only used for joystick fire)

    Cassette: Identical to TI-99/4A, except that the CS2 unit is not implemented

    Sound: SN94624 as used in the TI-99/4A

    Speech: TMS5200C, a rare variant of the TMS52xx family. Compatible to the
         speech data for the separate speech synthesizer for the TI-99/4A.
         Speech ROMs CD2325A, CD2326A (total 128K*1)

    ROM: TMS4764 (8K*8), called "ROM0" in the specifications [1]
         TMS47256 (32K*8), called "ROM1" [1]
         TMS47128 (16K*8), "P-Code ROM" (only available in late prototypes)
         See below for contents

    GROMs: TI-specific ROM circuits with internal address counter and 6 KiB
         capacity (see grom.c)
         3 GROMs (system GROMs, access via port at logical address F830)
         8 GROMs (Pascal / Text-to-speech GROMs, port at logical address F840)
         8 GROMs (Pascal GROMs, port at logical address F850)
         3 GROMs (Pascal GROMs, access via port at logical address F860)
         (total of 132 KiB GROM)

    RAM: 1 TMS4016 (SRAM 2K*8)
         8 TMS4164 (DRAM 64K*1)

    PSI: (programmable system interface) TMS9901 with connections to
         keyboard, joystick port, cassette port, and external interrupt lines
         (video, peripheral devices)

    External connectors:
         - Joystick port (compatible to TI-99/4A joystick slot)
         - Cassette port
         - Cartridge port (compatible to TI-99/4A cartridge slot, but vertically
           orientated, so cartridges are plugged in from the top)
         - I/O port (not compatible to TI-99/4A I/O port, needs a special P-Box
           card called "Armadillo interface")
         - Hexbus port (new peripheral system, also seen with later TI designs)
         - Video port (composite)

    Custom chips: Five custom chips contain mapping and selection logic
         - "Vaquerro": Logical address space decoder
         - "Mofetta" : Physical address space decoder
         - "Amigo"   : Mapper
         - "Pollo"   : DRAM controller
         - "Oso"     : Hexbus interface

    Modes:
         - Compatibility mode (TI-99/4A mode): Memory-mapped devices are
           placed at the same location as found in the TI-99/4A, thereby
           providing good downward compatibility.
           The console starts up in compatibility mode.
         - Native mode (Armadillo mode): Devices are located at positions above
           0xF000 that allow for a contiguous usage of memory.

    Mapper
    ------
    The mapper uses 4K pages (unlike the Geneve mapper with 8K pages) which
    are defined by a 32 bit word. The address bits A0-A3 serve as the page
    index, whereas bits A4-A15 are the offset in the page.
    From the 32 bits, 24 bits define the physical address, so this allows for
    a maximum of 16 MiB of mapped-addressable memory.

    See more about the mapper in the file 998board.cpp


    Availability of ROMs and documentation
    --------------------------------------
    By written consent, TI granted free use of all software and documentation
    concerning the TI-99/8, including all specifications, ROMs, and source code
    of ROMs.


    Acknowledgements
    ----------------
    Special thanks go to Ciro Barile of the TI99 Italian User Club
    (www.ti99iuc.it): By his courtesy we have a consistent dump of ROMs for
    one of the most evolved versions of the TI-99/8 with

    - complete GROM set (with Pascal)
    - complete ROM set (with Hexbus DSR and TTS)
    - complete speech ROM set

    Also, by applying test programs on his real console, many unclear
    specifications were resolved.


    References
    ----------
    [1] Texas Instruments: Armadillo Product Specifications, July 1983
    [2] Source code (Assembler and GPL) of the TI-99/8 ROMs and GROMs
    [3] Schematics of the TI-99/8


    Implementation
    --------------
    Initial version by Raphael Nabet, 2003.

    February 2012: Rewritten as class [Michael Zapf]
    November 2013: Included new dumps [Michael Zapf]

===========================================================================
Known Issues (MZ, 2019-05-10)

  KEEP IN MIND THAT TEXAS INSTRUMENTS NEVER RELEASED THE TI-99/8 AND THAT
  THERE ARE ONLY A FEW PROTOTYPES OF THE TI-99/8 AVAILABLE. ALL SOFTWARE
  MUST BE ASSUMED TO HAVE REMAINED IN A PRELIMINARY STATE.

- TI-99/4A disk controllers cannot be used with the TI-99/8 in Extended Basic II.
  In the 99/8, the peripheral access block (PAB, set of data defining the
  access to the device, like floppy) may be located in CPU RAM, while the
  controllers of the 99/4A expect the PAB to be in video RAM only. Exbasic II
  sets up the PAB in CPU RAM, which leads to a crash. Other cartridges from
  the 99/4A certainly use video RAM, and so the disk controller works.
  Therefore, the Hexbus floppy drive HX5102 is recommended for use with the
  TI-99/8. You do not even need to attach the Peripheral Box.

  mame ti99_8 -hexbus hx5102 -flop1 somedisk.dsk

- Multiple cartridges are not shown in the startup screen; only one
  cartridge is presented. You have to manually select the cartridges with the
  dip switch.

- SAVE and OLD MINIMEM do not work properly in XB II. It seems as if the
  mapper shadows the NVRAM of the cartridge. You will lose the contents when
  you turn off the machine.

*****************************************************************************/


#include "emu.h"
#include "cpu/tms9900/tms9995.h"

#include "sound/sn76496.h"
#include "machine/tms9901.h"
#include "machine/tmc0430.h"
#include "imagedev/cassette.h"

#include "bus/ti99/internal/998board.h"
#include "bus/ti99/gromport/gromport.h"
#include "bus/hexbus/hexbus.h"

#include "bus/ti99/joyport/joyport.h"
#include "bus/ti99/internal/ioport.h"

#include "softlist_dev.h"
#include "speaker.h"

// Debugging
#define LOG_WARN        (1U << 1)   // Warnings
#define LOG_CONFIG      (1U << 2)   // Configuration
#define LOG_READY       (1U << 3)
#define LOG_INTERRUPTS  (1U << 4)
#define LOG_CRU         (1U << 5)
#define LOG_CRUREAD     (1U << 6)
#define LOG_RESETLOAD   (1U << 7)

#define VERBOSE ( LOG_CONFIG | LOG_WARN | LOG_RESETLOAD )

#include "logmacro.h"


namespace {

/*
    READY bits.
*/
enum
{
	READY_GROM = 1,
	READY_MAPPER = 2,
	READY_PBOX = 4,
	READY_SOUND = 8,
	READY_CART = 16,
	READY_SPEECH = 32,
	READY_MAINBOARD = 64
};

class ti99_8_state : public driver_device
{
public:
	ti99_8_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_cpu(*this, "maincpu"),
		m_tms9901(*this, TI998_TMS9901_TAG),
		m_gromport(*this, TI99_GROMPORT_TAG),
		m_ioport(*this, TI99_IOPORT_TAG),
		m_mainboard(*this, TI998_MAINBOARD_TAG),
		m_joyport(*this, TI_JOYPORT_TAG),
		m_cassette(*this, "cassette"),
		m_keyboard(*this, "COL%u", 0U)
	{
	}

	void ti99_8(machine_config &config);
	void ti99_8_60hz(machine_config &config);
	void ti99_8_50hz(machine_config &config);

	// Lifecycle
	void driver_start() override;
	void driver_reset() override;

private:
	// Processor connections with the main board
	uint8_t cruread(offs_t offset);
	void cruwrite(offs_t offset, uint8_t data);
	void external_operation(offs_t offset, uint8_t data);
	void clock_out(int state);

	// Connections from outside towards the CPU (callbacks)
	void console_ready(int state);
	void console_reset(int state);
	void cpu_hold(int state);
	void notconnected(int state);

	// GROM clock (coming from Vaquerro)
	void gromclk_in(int state);

	// Connections with the system interface chip 9901
	void extint(int state);
	void video_interrupt(int state);

	// Connections with the system interface TMS9901
	uint8_t psi_input(offs_t offset);
	void keyC0(int state);
	void keyC1(int state);
	void keyC2(int state);
	void keyC3(int state);
	void audio_gate(int state);
	void cassette_output(int state);
	void cassette_motor(int state);
	void tms9901_interrupt(offs_t offset, uint8_t data);

	void crumap(address_map &map) ATTR_COLD;
	void memmap(address_map &map) ATTR_COLD;
	void memmap_setaddress(address_map &map) ATTR_COLD;

	// Keyboard support
	void    set_keyboard_column(int number, int data);
	int     m_keyboard_column = 0;

	// READY handling
	int m_ready_old = 0;

	// Latch for 9901 INT2, INT1 lines
	int  m_int1 = 0;
	int  m_int2 = 0;

	// Connected devices
	required_device<tms9995_device>     m_cpu;
	required_device<tms9901_device>     m_tms9901;
	required_device<bus::ti99::gromport::gromport_device> m_gromport;
	required_device<bus::ti99::internal::ioport_device>     m_ioport;
	required_device<bus::ti99::internal::mainboard8_device>  m_mainboard;
	required_device<bus::ti99::joyport::joyport_device> m_joyport;
	required_device<cassette_image_device> m_cassette;

	required_ioport_array<14> m_keyboard;
};

/*
    Memory map. We have a configurable mapper, so we need to delegate the
    job to the mapper completely.
*/
void ti99_8_state::memmap(address_map &map)
{
	map(0x0000, 0xffff).rw(TI998_MAINBOARD_TAG, FUNC(bus::ti99::internal::mainboard8_device::read), FUNC(bus::ti99::internal::mainboard8_device::write));
}

void ti99_8_state::memmap_setaddress(address_map &map)
{
	map(0x0000, 0xffff).w(TI998_MAINBOARD_TAG, FUNC(bus::ti99::internal::mainboard8_device::setaddress));
}

/*
    CRU map - see description above
    The TMS9901 is fully decoded according to the specification, so we only
    have 32 bits for it; the rest goes to the CRU bus
    (decoded by the "Vaquerro" chip, signal NNOICS*)
*/

void ti99_8_state::crumap(address_map &map)
{
	map(0x0000, 0x2fff).rw(FUNC(ti99_8_state::cruread), FUNC(ti99_8_state::cruwrite));
	map(0x0000, 0x003f).rw(m_tms9901, FUNC(tms9901_device::read), FUNC(tms9901_device::write));
}

/* ti99/8 : 54-key keyboard */
static INPUT_PORTS_START(ti99_8)

	/* 16 ports for keyboard and joystick */
	PORT_START("COL0")    /* col 0 */
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ALPHA LOCK") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("FCTN") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LSHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL1")    /* col 1 */
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 ! DEL") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("q Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("a A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("z Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL2")    /* col 2 */
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 @ INS") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("w W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("s S (LEFT)") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("x X (DOWN)") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL3")    /* col 3 */
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 #") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("e E (UP)") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("d D (RIGHT)") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("c C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL4")    /* col 4 */
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 $ CLEAR") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("r R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("v V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL5")    /* col 5 */
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 % BEGIN") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("t T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("g G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("b B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL6")    /* col 6 */
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 ^ PROC'D") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL7")    /* col 7 */
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 & AID") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL8")    /* col 8 */
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 * REDO") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL9")    /* col 9 */
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 ( BACK") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL10")    /* col 10 */
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL11")    /* col 11 */
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("= + QUIT") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RSHIFT") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL12")    /* col 12 */
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("(SPACE)") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL13")    /* col 13 */
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('`') PORT_CHAR('~')
	PORT_BIT(0x07, IP_ACTIVE_LOW, IPT_UNUSED)

INPUT_PORTS_END


uint8_t ti99_8_state::cruread(offs_t offset)
{
	LOGMASKED(LOG_CRUREAD, "read access to CRU address %04x\n", offset);
	uint8_t value = 0;

	// Let the mapper, the gromport, and the p-box decide whether they want
	// to change the value at the CRU address
	m_mainboard->crureadz(offset<<1, &value);
	m_gromport->crureadz(offset<<1, &value);
	m_ioport->crureadz(offset<<1, &value);

	LOGMASKED(LOG_CRU, "CRU %04x -> %x\n", offset<<1, value);
	return value;
}

void ti99_8_state::cruwrite(offs_t offset, uint8_t data)
{
	LOGMASKED(LOG_CRU, "CRU %04x <- %x\n", offset<<1, data);
	m_mainboard->cruwrite(offset<<1, data);
	m_gromport->cruwrite(offset<<1, data);
	m_ioport->cruwrite(offset<<1, data);
}

/***************************************************************************
    TI99/8-specific tms9901 I/O handlers
    These methods are callbacks from the TMS9901 system interface. That is,
    they deliver the values queried via the TMS9901, and they represent
    console functions which are under control of the TMS9901 (like the
    keyboard column selection.)
***************************************************************************/

uint8_t ti99_8_state::psi_input(offs_t offset)
{
	switch (offset)
	{
	case tms9901_device::INT1:
		return (m_int1==CLEAR_LINE)? 1 : 0;
	case tms9901_device::INT2:
		return (m_int2==CLEAR_LINE)? 1 : 0;

	case tms9901_device::INT6:
		if (m_keyboard_column >= 14)
			return BIT(m_joyport->read_port(),0);
		[[fallthrough]];
	case tms9901_device::INT7_P15:
		if (m_keyboard_column >= 14)
			return BIT(m_joyport->read_port(),4);
		[[fallthrough]];
	case tms9901_device::INT8_P14:
		if (m_keyboard_column >= 14)
			return BIT(m_joyport->read_port(),1);
		[[fallthrough]];
	case tms9901_device::INT9_P13:
		if (m_keyboard_column >= 14)
			return BIT(m_joyport->read_port(),2);
		[[fallthrough]];
	case tms9901_device::INT10_P12:
		if (m_keyboard_column >= 14)
			return BIT(m_joyport->read_port(),3);

		// return for last 5 cases if column<14
		return BIT(m_keyboard[m_keyboard_column]->read(), offset-tms9901_device::INT6);

	case tms9901_device::INT11_P11:
		return (m_cassette->input() > 0);

	default:
		return 1;
	}
}

/*
    WRITE key column select (P2-P4), TI-99/8
*/
void ti99_8_state::set_keyboard_column(int number, int data)
{
	if (data != 0)
		m_keyboard_column |= 1 << number;
	else
		m_keyboard_column &= ~(1 << number);

	if (m_keyboard_column >= 14)
	{
		m_joyport->write_port(m_keyboard_column - 13);
	}
}

void ti99_8_state::keyC0(int state)
{
	set_keyboard_column(0, state);
}

void ti99_8_state::keyC1(int state)
{
	set_keyboard_column(1, state);
}

void ti99_8_state::keyC2(int state)
{
	set_keyboard_column(2, state);
}

void ti99_8_state::keyC3(int state)
{
	set_keyboard_column(3, state);
}

/*
    Control cassette tape unit motor (P6)
*/
void ti99_8_state::cassette_motor(int state)
{
	m_cassette->change_state(state==ASSERT_LINE? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR);
}

/*
    Audio gate (P8)
    Set to 1 before using tape: this enables the mixing of tape input sound
    with computer sound.
    We do not really need to emulate this as the tape recorder generates sound
    on its own.
*/
void ti99_8_state::audio_gate(int state)
{
}

/*
    Tape output (P9)
    I think polarity is correct, but don't take my word for it.
*/
void ti99_8_state::cassette_output(int state)
{
	m_cassette->output(state==ASSERT_LINE? +1 : -1);
}

void ti99_8_state::tms9901_interrupt(offs_t offset, uint8_t data)
{
	m_cpu->set_input_line(INT_9995_INT1, data);
}

/*****************************************************************************/

/*
    set the state of TMS9901's INT2 (called by the VDP)
*/
void ti99_8_state::video_interrupt(int state)
{
	LOGMASKED(LOG_INTERRUPTS, "VDP int 2 on tms9901, level=%02x\n", state);
	m_int2 = (line_state)state;
	m_tms9901->set_int_line(2, state);
}

/***********************************************************
    Links to external devices
***********************************************************/

/*
    Propagate READY signals to the CPU.
*/
void ti99_8_state::console_ready(int state)
{
	if (m_ready_old != state)
		LOGMASKED(LOG_READY, "READY = %d\n", state);
	m_ready_old = (line_state)state;
	m_cpu->ready_line(state);
}

/*
    Enqueue a RESET signal.
*/
void ti99_8_state::console_reset(int state)
{
	LOGMASKED(LOG_RESETLOAD, "Incoming RESET line = %d\n", state);
	if (machine().phase() != machine_phase::INIT)
	{
		// RESET the 9901
		m_tms9901->rst1_line(state);

		// Pull up the CRUS and PTGEN lines (9901 outputs have been deactivated, pull-up resistors on the board show effect)
		m_mainboard->crus_in(true); // assert
		m_mainboard->ptgen_in(true); // clear

		// Setting ready to false so that automatic wait states are enabled
		m_cpu->ready_line(CLEAR_LINE);
		m_cpu->reset_line(ASSERT_LINE);

		// Send RESET to the IOPort
		m_ioport->reset_in(state);
	}
}

/*
    The HOLD line leading to the CPU entering the HOLD state.
*/
void ti99_8_state::cpu_hold(int state)
{
	LOGMASKED(LOG_INTERRUPTS, "Incoming HOLD line = %d\n", state);
	m_cpu->hold_line(state);
}

void ti99_8_state::extint(int state)
{
	LOGMASKED(LOG_INTERRUPTS, "EXTINT level = %02x\n", state);
	m_int1 = (line_state)state;
	m_tms9901->set_int_line(1, state);
}

[[maybe_unused]] void ti99_8_state::notconnected(int state)
{
	LOGMASKED(LOG_INTERRUPTS, "Setting a not connected line ... ignored\n");
}

void ti99_8_state::external_operation(offs_t offset, uint8_t data)
{
	static char const *const extop[8] = { "inv1", "inv2", "IDLE", "RSET", "inv3", "CKON", "CKOF", "LREX" };
	if (offset == IDLE_OP) return;
	else
		LOGMASKED(LOG_WARN, "External operation %s not implemented on TI-99/8 board\n", extop[offset]);
}

/*
    Clock line from the CPU. Used to control wait state generation.
*/
void ti99_8_state::clock_out(int state)
{
	m_tms9901->phi_line(state);
	m_mainboard->clock_in(state);
}

void ti99_8_state::driver_start()
{
	save_item(NAME(m_keyboard_column));
	save_item(NAME(m_ready_old));
	save_item(NAME(m_int1));
	save_item(NAME(m_int2));
}

void ti99_8_state::driver_reset()
{
	m_cpu->hold_line(CLEAR_LINE);

	// Pulling down the line on RESET configures the CPU to insert one wait
	// state on external memory accesses
	//  m_cpu->ready_line(ASSERT_LINE);

	// m_gromport->set_grom_base(0x9800, 0xfff1);

	m_keyboard_column = 0;

	// Clear INT1 and INT2 latch
	m_int1 = CLEAR_LINE;
	m_int2 = CLEAR_LINE;
	console_reset(ASSERT_LINE);
	console_reset(CLEAR_LINE);
}

void ti99_8_state::ti99_8(machine_config& config)
{
	using namespace bus::ti99::internal;
	// basic machine hardware */
	// TMS9995-MP9537 CPU @ 10.7 MHz
	// MP9537 mask: This variant of the TMS9995 does not contain on-chip RAM
	TMS9995_MP9537(config, m_cpu, XTAL(10'738'635));
	m_cpu->set_addrmap(AS_PROGRAM, &ti99_8_state::memmap);
	m_cpu->set_addrmap(AS_IO, &ti99_8_state::crumap);
	m_cpu->set_addrmap(tms9995_device::AS_SETADDRESS, &ti99_8_state::memmap_setaddress);
	m_cpu->extop_cb().set(FUNC(ti99_8_state::external_operation));
	m_cpu->clkout_cb().set(FUNC(ti99_8_state::clock_out));
	m_cpu->holda_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::holda_line));

	// 9901 configuration
	TMS9901(config, m_tms9901, 0);
	m_tms9901->read_cb().set(FUNC(ti99_8_state::psi_input));
	m_tms9901->p_out_cb(0).set(FUNC(ti99_8_state::keyC0));
	m_tms9901->p_out_cb(1).set(FUNC(ti99_8_state::keyC1));
	m_tms9901->p_out_cb(2).set(FUNC(ti99_8_state::keyC2));
	m_tms9901->p_out_cb(3).set(FUNC(ti99_8_state::keyC3));
	m_tms9901->p_out_cb(4).set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::crus_in));
	m_tms9901->p_out_cb(5).set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::ptgen_in));
	m_tms9901->p_out_cb(6).set(FUNC(ti99_8_state::cassette_motor));
	m_tms9901->p_out_cb(8).set(FUNC(ti99_8_state::audio_gate));
	m_tms9901->p_out_cb(9).set(FUNC(ti99_8_state::cassette_output));
	m_tms9901->intreq_cb().set(FUNC(ti99_8_state::tms9901_interrupt));

	// Mainboard with custom chips
	TI99_MAINBOARD8(config, m_mainboard, 0);
	m_mainboard->ready_cb().set(FUNC(ti99_8_state::console_ready));
	m_mainboard->reset_cb().set(FUNC(ti99_8_state::console_reset));
	m_mainboard->hold_cb().set(FUNC(ti99_8_state::cpu_hold));

	// Cartridge port
	TI99_GROMPORT(config, m_gromport, 0, ti99_gromport_options_998, "single").extend();
	m_gromport->ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::system_grom_ready));
	m_gromport->reset_cb().set(FUNC(ti99_8_state::console_reset));

	// RAM
	RAM(config, TI998_SRAM_TAG).set_default_size("2K").set_default_value(0);
	RAM(config, TI998_DRAM_TAG).set_default_size("64K").set_default_value(0);

	// Software list
	SOFTWARE_LIST(config, "cart_list_ti99").set_original("ti99_cart");

	// I/O port
	TI99_IOPORT(config, m_ioport, 0, ti99_ioport_options_plain, nullptr);
	m_ioport->extint_cb().set(FUNC(ti99_8_state::extint));
	m_ioport->ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::pbox_ready));

	// Hexbus
	HEXBUS(config, TI998_HEXBUS_TAG, 0, hexbus_options, nullptr);

	// Sound hardware
	SPEAKER(config, "sound_out").front_center();
	sn76496_device& soundgen(SN76496(config, TI998_SOUNDCHIP_TAG, 3579545));
	soundgen.ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::sound_ready));
	soundgen.add_route(ALL_OUTPUTS, "sound_out", 0.75);

	// Speech hardware
	// Note: SPEECHROM uses its tag for referencing the region
	SPEAKER(config, "speech_out").front_center();

	cd2501ecd_device& vsp(CD2501ECD(config, TI998_SPEECHSYN_TAG, 640000L));
	vsp.ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::speech_ready));
	vsp.add_route(ALL_OUTPUTS, "speech_out", 0.50);

	TMS6100(config, TI998_SPEECHROM_REG, 0);
	vsp.m0_cb().set(TI998_SPEECHROM_REG, FUNC(tms6100_device::m0_w));
	vsp.m1_cb().set(TI998_SPEECHROM_REG, FUNC(tms6100_device::m1_w));
	vsp.addr_cb().set(TI998_SPEECHROM_REG, FUNC(tms6100_device::add_w));
	vsp.data_cb().set(TI998_SPEECHROM_REG, FUNC(tms6100_device::data_line_r));
	vsp.romclk_cb().set(TI998_SPEECHROM_REG, FUNC(tms6100_device::clk_w));

	// Cassette drive
	SPEAKER(config, "cass_out").front_center();
	CASSETTE(config, "cassette", 0).add_route(ALL_OUTPUTS, "cass_out", 0.25);

	// GROM library
	using namespace bus::ti99::internal;
	TMC0430(config, TI998_SYSGROM0_TAG, TI998_SYSGROM_REG, 0x0000, 0).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::system_grom_ready));
	TMC0430(config, TI998_SYSGROM1_TAG, TI998_SYSGROM_REG, 0x2000, 1).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::system_grom_ready));
	TMC0430(config, TI998_SYSGROM2_TAG, TI998_SYSGROM_REG, 0x4000, 2).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::system_grom_ready));

	TMC0430(config, TI998_GLIB10_TAG, TI998_GROMLIB1_REG, 0x0000, 0).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::ptts_grom_ready));
	TMC0430(config, TI998_GLIB11_TAG, TI998_GROMLIB1_REG, 0x2000, 1).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::ptts_grom_ready));
	TMC0430(config, TI998_GLIB12_TAG, TI998_GROMLIB1_REG, 0x4000, 2).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::ptts_grom_ready));
	TMC0430(config, TI998_GLIB13_TAG, TI998_GROMLIB1_REG, 0x6000, 3).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::ptts_grom_ready));
	TMC0430(config, TI998_GLIB14_TAG, TI998_GROMLIB1_REG, 0x8000, 4).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::ptts_grom_ready));
	TMC0430(config, TI998_GLIB15_TAG, TI998_GROMLIB1_REG, 0xa000, 5).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::ptts_grom_ready));
	TMC0430(config, TI998_GLIB16_TAG, TI998_GROMLIB1_REG, 0xc000, 6).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::ptts_grom_ready));
	TMC0430(config, TI998_GLIB17_TAG, TI998_GROMLIB1_REG, 0xe000, 7).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::ptts_grom_ready));

	TMC0430(config, TI998_GLIB20_TAG, TI998_GROMLIB2_REG, 0x0000, 0).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::p8_grom_ready));
	TMC0430(config, TI998_GLIB21_TAG, TI998_GROMLIB2_REG, 0x2000, 1).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::p8_grom_ready));
	TMC0430(config, TI998_GLIB22_TAG, TI998_GROMLIB2_REG, 0x4000, 2).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::p8_grom_ready));
	TMC0430(config, TI998_GLIB23_TAG, TI998_GROMLIB2_REG, 0x6000, 3).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::p8_grom_ready));
	TMC0430(config, TI998_GLIB24_TAG, TI998_GROMLIB2_REG, 0x8000, 4).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::p8_grom_ready));
	TMC0430(config, TI998_GLIB25_TAG, TI998_GROMLIB2_REG, 0xa000, 5).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::p8_grom_ready));
	TMC0430(config, TI998_GLIB26_TAG, TI998_GROMLIB2_REG, 0xc000, 6).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::p8_grom_ready));
	TMC0430(config, TI998_GLIB27_TAG, TI998_GROMLIB2_REG, 0xe000, 7).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::p8_grom_ready));

	TMC0430(config, TI998_GLIB30_TAG, TI998_GROMLIB3_REG, 0x0000, 0).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::p3_grom_ready));
	TMC0430(config, TI998_GLIB31_TAG, TI998_GROMLIB3_REG, 0x2000, 1).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::p3_grom_ready));
	TMC0430(config, TI998_GLIB32_TAG, TI998_GROMLIB3_REG, 0x4000, 2).ready_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::p3_grom_ready));

	// Joystick port
	TI99_JOYPORT(config, m_joyport, 0, ti99_joyport_options_mouse, "twinjoy");
}

/*
    TI-99/8 US version (NTSC, 60 Hz)
*/
void ti99_8_state::ti99_8_60hz(machine_config &config)
{
	ti99_8(config);
	// Video hardware
	tms9118_device &video(TMS9118(config, TI998_VDP_TAG, XTAL(10'738'635)));
	video.set_vram_size(0x4000);
	video.int_callback().set(FUNC(ti99_8_state::video_interrupt));
	video.set_screen("screen");

	SCREEN(config, "screen", SCREEN_TYPE_RASTER);
}

/*
    TI-99/8 European version (PAL, 50 Hz)
*/
void ti99_8_state::ti99_8_50hz(machine_config &config)
{
	ti99_8(config);
	// Video hardware
	tms9129_device &video(TMS9129(config, TI998_VDP_TAG, XTAL(10'738'635)));
	video.set_vram_size(0x4000);
	video.int_callback().set(FUNC(ti99_8_state::video_interrupt));
	video.set_screen("screen");

	SCREEN(config, "screen", SCREEN_TYPE_RASTER);
}

/*
    All ROM dumps except the speech ROM have a CRC16 checksum as the final two
    bytes. ROM1 contains four 8K chunks of ROM contents with an own CRC at their
    ends. All GROMs, ROM0, and the four ROM1 parts were successfully
    validated.
*/
ROM_START(ti99_8)
	// Logical (CPU) memory space: ROM0
	ROM_REGION(0x2000, TI998_ROM0_REG, 0)
	ROM_LOAD("rom0.u4", 0x0000, 0x2000, CRC(901eb8d6) SHA1(13190c5e834baa9c0a70066b566cfcef438ed88a))

	// Physical memory space (mapped): ROM1
	ROM_REGION(0x8000, TI998_ROM1_REG, 0)
	ROM_LOAD("rom1.u25", 0x0000, 0x8000, CRC(b574461a) SHA1(42c6aed44802cfabdd26b565d6e5ddfcd689f11e))

	// Physical memory space (mapped): P-Code ROM
	// This circuit is only available in later versions of the console and seems
	// to be piggy-backed on ROM1.
	// To make things worse, the decoding logic of the custom chips do not show
	// the required select line for this ROM on the available schematics, so
	// they seem to be from the earlier version. The location in the address
	// space was determined by ROM disassembly.
	ROM_REGION(0x8000, TI998_PASCAL_REG, 0)
	ROM_LOAD("pascal.u25a", 0x0000, 0x4000, CRC(d7ed6dd6) SHA1(32212ce6426ceccbff73d342d4a3ef699c0ae1e4))

	// System GROMs. 3 chips @ f830
	// The schematics do not enumerate the circuits but only say
	// "circuits on board" (COB) so we name the GROMs as gM_N.bin where M is the
	// ID (0-7) and N is the access port in the logical address space.
	ROM_REGION(0x6000, TI998_SYSGROM_REG, 0)
	ROM_LOAD("g0_f830.bin", 0x0000, 0x1800, CRC(1026db60) SHA1(7327095bf4f390476e69d9fd8424e98ea1f2325a))
	ROM_LOAD("g1_f830.bin", 0x2000, 0x1800, CRC(93a43d65) SHA1(19be8a07d674bc7554c2bc9c7a5725d81e888e6e))
	ROM_LOAD("g2_f830.bin", 0x4000, 0x1800, CRC(06f2b901) SHA1(f65e0fcb2c63e230b4a9563c72f91259b94ce955))

	// TTS & Pascal library. 8 chips @ f840
	ROM_REGION(0x10000, TI998_GROMLIB1_REG, 0)
	ROM_LOAD("g0_f840.bin", 0x0000, 0x1800, CRC(44501071) SHA1(4b5ef7f1aa43a87e7ae4f02090944be5c39b1f26))
	ROM_LOAD("g1_f840.bin", 0x2000, 0x1800, CRC(5a271d9e) SHA1(bb95befa2ffba2cc17ac437386e069e8ff621248))
	ROM_LOAD("g2_f840.bin", 0x4000, 0x1800, CRC(d52502df) SHA1(17063e33ee8709d0df8030f38bb92c4322d55e1e))
	ROM_LOAD("g3_f840.bin", 0x6000, 0x1800, CRC(86c12396) SHA1(119b6df9211b5399245e017721fc51b88b60879f))
	ROM_LOAD("g4_f840.bin", 0x8000, 0x1800, CRC(f17a2ef8) SHA1(dcb044f71d7f8a165b41f39e35a368d8f2d63b67))
	ROM_LOAD("g5_f840.bin", 0xA000, 0x1800, CRC(7dc41301) SHA1(dff714da68de352db93fba309db8e5a8ae7cab1a))
	ROM_LOAD("g6_f840.bin", 0xC000, 0x1800, CRC(7e310a90) SHA1(e927d8b3f8b32aa4fb9f7d080d5262c566a77fc7))
	ROM_LOAD("g7_f840.bin", 0xE000, 0x1800, CRC(3a9d20df) SHA1(1e6f9f8ec7df4b997a7579be742d0a7d54bc8763))

	// Pascal library. 8 chips @ f850
	ROM_REGION(0x10000, TI998_GROMLIB2_REG, 0)
	ROM_LOAD("g0_f850.bin", 0x0000, 0x1800, CRC(2d948672) SHA1(cf15912d6dae5a450e0cfd796aa36ea5e521dc56))
	ROM_LOAD("g1_f850.bin", 0x2000, 0x1800, CRC(7d64a842) SHA1(d5884bb2af21c8027311478ee506beac6f46203d))
	ROM_LOAD("g2_f850.bin", 0x4000, 0x1800, CRC(e5ed8900) SHA1(03826882ce10fb5a6b3a9ccc85d3d1fe51979d0b))
	ROM_LOAD("g3_f850.bin", 0x6000, 0x1800, CRC(87aaf19e) SHA1(fdbe163773b8a30fa6b9508e679be6fa4f99bf7a))
	ROM_LOAD("g4_f850.bin", 0x8000, 0x1800, CRC(d3e789a5) SHA1(5ab06aa75ca694b1035ce5ac0bebacc928721388))
	ROM_LOAD("g5_f850.bin", 0xA000, 0x1800, CRC(49fd90bd) SHA1(44b2cef29c2d5304a0dcfedbdcdf9f21f2201bf9))
	ROM_LOAD("g6_f850.bin", 0xC000, 0x1800, CRC(31bac4ab) SHA1(e29049f0597d5de0bfd5c9c7bfea902abe858010))
	ROM_LOAD("g7_f850.bin", 0xE000, 0x1800, CRC(71534098) SHA1(75e87123efde885e27dd749e07cb189eb2cc45a8))

	// Pascal library. 3 chips @ f860
	ROM_REGION(0x6000, TI998_GROMLIB3_REG, 0)
	ROM_LOAD("g0_f860.bin", 0x0000, 0x1800, CRC(0ceef210) SHA1(b89957fbff094b758746391a69dea6907c66b950))
	ROM_LOAD("g1_f860.bin", 0x2000, 0x1800, CRC(fc87de25) SHA1(4695b7f979f59a01ec16c55e4587c3379482b658))
	ROM_LOAD("g2_f860.bin", 0x4000, 0x1800, CRC(e833e350) SHA1(6ffe501981a1112be1af596a489d96e287fc6be5))

	// Speech ROM
	ROM_REGION(0x8000, TI998_SPEECHROM_REG, 0)
	ROM_LOAD("cd2325a.vsm", 0x0000, 0x4000, CRC(1f58b571) SHA1(0ef4f178716b575a1c0c970c56af8a8d97561ffe))
	ROM_LOAD("cd2326a.vsm", 0x4000, 0x4000, CRC(65d00401) SHA1(a367242c2c96cebf0e2bf21862f3f6734b2b3020))
ROM_END

#define rom_ti99_8e rom_ti99_8

} // Anonymous namespace


//    YEAR  NAME     PARENT  COMPAT  MACHINE      INPUT   CLASS         INIT        COMPANY              FULLNAME                     FLAGS
COMP( 1983, ti99_8,  0,      0,      ti99_8_60hz, ti99_8, ti99_8_state, empty_init, "Texas Instruments", "TI-99/8 Computer (US)",     MACHINE_SUPPORTS_SAVE )
COMP( 1983, ti99_8e, ti99_8, 0,      ti99_8_50hz, ti99_8, ti99_8_state, empty_init, "Texas Instruments", "TI-99/8 Computer (Europe)", MACHINE_SUPPORTS_SAVE )



ti990_10.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Raphael Nabet
/*
    TI990/10 driver

    This driver boots the DX10 build tape and build a bootable system disk.
    I have been able to run a few programs (including most games from the
    "fun and games" tape), but I have been unable to perform system generation
    and install BASIC/COBOL/PASCAL.

TODO :
* programmer panel
* emulate TILINE fully: timings, tiline timeout, possibly memory error
* finish tape emulation (write support)
* add additional devices as need appears (931 VDT, FD800, card reader, ASR/KSR, printer)
* emulate 990/10A and 990/12 CPUs?
* find out why the computer locks when executing ALGS and BASIC/COBOL/PASCAL installation
*/

/*
    CRU map:

    990/10 CPU board:
    1fa0-1fbe: map file CRU interface
    1fc0-1fde: error interrupt register
    1fe0-1ffe: control panel

    optional hardware (default configuration):
    0000-001e: 733 ASR
    0020-003e: PROM programmer
    0040-005e: card reader
    0060-007e: line printer
    0080-00be: FD800 floppy disc
    00c0-00ee: 913 VDT, or 911 VDT
    0100-013e: 913 VDT #2, or 911 VDT
    0140-017e: 913 VDT #3, or 911 VDT
    1700-177e (0b00-0b7e, 0f00-0f7e): CI402 serial controller #0 (#1, #2) (for 931/940 VDT)
        (note that CRU base 1700 is used by the integrated serial controller in newer S300,
        S300A, 990/10A (and 990/5?) systems)
    1f00-1f1e: CRU expander #1 interrupt register
    1f20-1f3e: CRU expander #2 interrupt register
    1f40-1f5e: TILINE coupler interrupt control #1-8


    TPCS map:
    1ff800: disk controller #1 (system disk)
    1ff810->1ff870: extra disk controllers #2 through #8
    1ff880 (1ff890): tape controller #1 (#2)
    1ff900->1ff950: communication controllers #1 through #6
    1ff980 (1ff990, 1ff9A0): CI403/404 serial controller #1 (#2, #3) (for 931/940 VDT)
    1ffb00, 1ffb04, etc: ECC memory controller #1, #2, etc, diagnostic
    1ffb10, 1ffb14, etc: cache memory controller #1, #2, etc, diagnostic


    interrupt map (default configuration):
    0,1,2: CPU board
    3: free
    4: card reader
    5: line clock
    6: 733 ASR/KSR
    7: FD800 floppy (or FD1000 floppy)
    8: free
    9: 913 VDT #3
    10: 913 VDT #2
    11: 913 VDT
    12: free
    13: hard disk
    14 line printer
    15: PROM programmer (actually not used)
*/

#include "emu.h"

#include "bus/ti99x/990_hd.h"
#include "bus/ti99x/990_tap.h"
#include "cpu/tms9900/ti990_10.h"
#include "sound/beep.h"
#include "911_vdt.h"


namespace {

class ti990_10_state : public driver_device
{
public:
	ti990_10_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_terminal(*this, "vdt911")
		, m_intlines(0)
		, m_ckon_state(0) { }

	void ti990_10(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void main_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	void key_interrupt(int state);
	void line_interrupt(int state);
	void tape_interrupt(int state);
	void set_int13(int state);
	[[maybe_unused]] void ckon_ckof_callback(int state);
	uint8_t panel_read(offs_t offset);
	void panel_write(uint8_t data);

	TIMER_CALLBACK_MEMBER(clear_load);

	void hold_load();
	void set_int_line(int line, int state);

	required_device<cpu_device> m_maincpu;
	required_device<vdt911_device> m_terminal;
	uint16_t m_intlines;
	int m_ckon_state;
	emu_timer*  m_load_timer = nullptr;
};


/*
    Interrupt priority encoder.  Actually part of the CPU board.
*/

void ti990_10_state::set_int_line(int line, int state)
{
	int level;


	if (state)
		m_intlines |= (1 << line);
	else
		m_intlines &= ~ (1 << line);

	if (m_intlines)
	{
		for (level = 0; ! (m_intlines & (1 << level)); level++)
			;
		m_maincpu->set_input_line_and_vector(0, ASSERT_LINE, level);  /* TI990_10 - interrupt it, baby */
	}
	else
		m_maincpu->set_input_line(0, CLEAR_LINE);
}


void ti990_10_state::set_int13(int state)
{
	set_int_line(13, state);
}

/*
    hold and debounce load line (emulation is inaccurate)
*/

TIMER_CALLBACK_MEMBER(ti990_10_state::clear_load)
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
}

void ti990_10_state::hold_load()
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
	m_load_timer->adjust(attotime::from_msec(100));
}

/*
    line interrupt
*/

/* m_ckon_state: 1 if line clock active (RTCLR flip-flop on TI990/10 schematics -
SMI sheet 4) */
void ti990_10_state::line_interrupt(int state)
{
	// set_int10(state);
	if (m_ckon_state)
		set_int_line(5, 1);
}

void ti990_10_state::ckon_ckof_callback(int state)
{
	m_ckon_state = state;
	if (! m_ckon_state)
		set_int_line(5, 0);
}



/*
    Control panel emulation

    three panel types
    * operator panel
    * programmer panel
    * MDU (external unit connected instead of the control panel, as seen in
      945401-9701 p. 2-5 though 2-15)

    Operator panel:
    * Power led
    * Fault led
    * Off/On/Load switch

    Programmer panel:
    * 16 status light, 32 switches, IDLE, RUN leds
    * interface to a low-level debugger in ROMs

    * MDU:
    * includes a programmer panel, a tape unit, and a few parts
      (diagnostic tape, diagnostic ROMs, etc.)

    CRU output:
    0-7: lights 0-7
    8: increment scan
    9: clear scan (according to 990 handbook)
    A: run light (additionally sets all data LEDs to 1s, the scan count to 0b10 and enables the HALT/SIE switch)
    B: fault light
    C: Memory Error Interrupt clear
    D: Start panel timer
    E: Set SIE function (interrupt after 2 instructions are executed)
    F: flag (according to 990 handbook)

    input :
    0-7: switches 0-7 (or data from MDU tape)
    8: scan count bit 1
    9: scan count bit 0
    A: timer active
    B: programmer panel not present or locked
    C: char in MDU tape unit buffer?
    D: unused?
    E: if 0, MDU unit present
    F: flag (according to 990 handbook)
*/

uint8_t ti990_10_state::panel_read(offs_t offset)
{
	if (offset == 1)
		return 0x48;

	return 0;
}

void ti990_10_state::panel_write(uint8_t data)
{
}

void ti990_10_state::machine_start()
{
	m_load_timer = timer_alloc(FUNC(ti990_10_state::clear_load), this);

#if 0
	/* load specific ti990/12 rom page */
	const int page = 3;

	memmove(memregion("maincpu")->base() + 0x1ffc00, memregion("maincpu")->base() + 0x1ffc00 + page * 0x400, 0x400);
#endif
}

void ti990_10_state::machine_reset()
{
	hold_load();
}

/*
void ti990_10_state::rset_callback)
{
    ti990_cpuboard_reset();

    // clear controller panel and smi fault LEDs
}

void ti990_10_state::lrex_callback)
{
    // right???
    hold_load();
}
*/

/*
    TI990/10 video emulation.

    We emulate a single VDT911 CRT terminal.
*/

void ti990_10_state::key_interrupt(int state)
{
	// set_int10(state);
}

/*
  Memory map - see description above
*/

void ti990_10_state::main_map(address_map &map)
{

	map(0x000000, 0x0fffff).ram();     /* let's say we have 1MB of RAM */
	map(0x100000, 0x1ff7ff).noprw();     /* free TILINE space */
	map(0x1ff800, 0x1ff81f).rw("hdc", FUNC(ti990_hdc_device::read), FUNC(ti990_hdc_device::write));  /* disk controller TPCS */
	map(0x1ff820, 0x1ff87f).noprw();     /* free TPCS */
	map(0x1ff880, 0x1ff89f).rw("tpc", FUNC(tap_990_device::read), FUNC(tap_990_device::write)); /* tape controller TPCS */
	map(0x1ff8a0, 0x1ffbff).noprw();     /* free TPCS */
	map(0x1ffc00, 0x1fffff).rom();     /* LOAD ROM */

}


/*
  CRU map
*/

void ti990_10_state::io_map(address_map &map)
{
	map(0x10, 0x11).r(m_terminal, FUNC(vdt911_device::cru_r));
	map(0x80, 0x8f).w(m_terminal, FUNC(vdt911_device::cru_w));
	map(0x1fa, 0x1fb).noprw(); // .r(FUNC(ti990_10_state::ti990_10_mapper_cru_r));
	map(0x1fc, 0x1fd).noprw(); // .r(FUNC(ti990_10_state::ti990_10_eir_cru_r));
	map(0x1fe, 0x1ff).r(FUNC(ti990_10_state::panel_read));
	map(0xfd0, 0xfdf).noprw(); // .w(FUNC(ti990_10_state::ti990_10_mapper_cru_w));
	map(0xfe0, 0xfef).noprw(); // .w(FUNC(ti990_10_state::ti990_10_eir_cru_w));
	map(0xff0, 0xfff).w(FUNC(ti990_10_state::panel_write));

}

/*
    Callback from the tape controller.
*/
void ti990_10_state::tape_interrupt(int state)
{
	// set_int9(state);
}

void ti990_10_state::ti990_10(machine_config &config)
{
	/* basic machine hardware */
	/* TI990/10 CPU @ 4.0(???) MHz */
	TI990_10(config, m_maincpu, 4000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &ti990_10_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &ti990_10_state::io_map);

	// VDT 911 terminal
	VDT911(config, m_terminal, 0);
	m_terminal->keyint_cb().set(FUNC(ti990_10_state::key_interrupt));
	m_terminal->lineint_cb().set(FUNC(ti990_10_state::line_interrupt));

	// Hard disk
	ti990_hdc_device &hdc(TI990_HDC(config, "hdc", 0));
	hdc.set_memory_space(m_maincpu, AS_PROGRAM);
	hdc.int_cb().set(FUNC(ti990_10_state::set_int13));

	// Tape controller
	tap_990_device &tpc(TI990_TAPE_CTRL(config, "tpc", 0));
	tpc.set_memory_space(m_maincpu, AS_PROGRAM);
	tpc.int_cb().set(FUNC(ti990_10_state::tape_interrupt));
}


/*
  ROM loading
*/
ROM_START(ti990_10)

	/*CPU memory space*/
#if 0

	ROM_REGION16_BE(0x200000, "maincpu",0)

	/* TI990/10 : older boot ROMs for floppy-disk */
	ROM_LOAD16_BYTE("975383.31", 0x1FFC00, 0x100, CRC(64fcd040) SHA1(6cde5b97f8681a7d83816af5cd1a5ec93809a150))
	ROM_LOAD16_BYTE("975383.32", 0x1FFC01, 0x100, CRC(64277276) SHA1(b84c971714c3eef154f111292cc470376136cfa3))
	ROM_LOAD16_BYTE("975383.29", 0x1FFE00, 0x100, CRC(af92e7bf) SHA1(b0a46632e797310340715ee44fca2f073a305d0a))
	ROM_LOAD16_BYTE("975383.30", 0x1FFE01, 0x100, CRC(b7b40cdc) SHA1(ca520430b747505d54bbdb550b666ea3c4014dc5))

#elif 1

	ROM_REGION16_BE(0x200000, "maincpu",0)

	/* TI990/10 : newer "universal" boot ROMs  */
	ROM_LOAD16_BYTE("975383.45", 0x1FFC00, 0x100, CRC(391943c7) SHA1(bbd4da60b221d146542a6b547ae1570024e41b8a))
	ROM_LOAD16_BYTE("975383.46", 0x1FFC01, 0x100, CRC(f40f7c18) SHA1(03613bbf2263a4335c25dfc63cf2878c06bfe280))
	ROM_LOAD16_BYTE("975383.47", 0x1FFE00, 0x100, CRC(1ba571d8) SHA1(adaa18f149b643cc842fea8d7107ee868d6ffaf4))
	ROM_LOAD16_BYTE("975383.48", 0x1FFE01, 0x100, CRC(8852b09e) SHA1(f0df2abb438716832c16ab111e475da3ae612673))

#else

	ROM_REGION16_BE(0x202000, "maincpu",0)

	/* TI990/12 ROMs - actually incompatible with TI990/10, but I just wanted to disassemble them. */
	ROM_LOAD16_BYTE("ti2025-7", 0x1FFC00, 0x1000, CRC(4824f89c) SHA1(819a2d582afff4346898d9a32e687d4018987585))
	ROM_LOAD16_BYTE("ti2025-8", 0x1FFC01, 0x1000, CRC(51fef543) SHA1(2594f43cc47f55cdf57e2ec2f38597af8c8d0af2))
	/* the other half of this ROM is not loaded - it makes no sense as TI990/12 machine code, as
	it is microcode... */

#endif


	/* VDT911 character definitions */
	ROM_REGION(vdt911_device::chr_region_len, vdt911_chr_region, ROMREGION_ERASEFF)

ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT  CLASS           INIT        COMPANY              FULLNAME                               FLAGS
COMP( 1975, ti990_10, 0,      0,      ti990_10, 0,     ti990_10_state, empty_init, "Texas Instruments", "TI Model 990/10 Minicomputer System", MACHINE_NOT_WORKING )



ti990_4.cpp
<---------------------------------------------------------------------->
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/*
    ti990/4 driver

    We emulate a basic ti990/4 board, with a FD800 disk controller and an 733
    ASR terminal.  A little floppy-based software for this computer is
    available thanks to efforts by Dave Pitts: mostly, Forth and TX990.


    Board setup options:
    8kb of DRAM (onboard option, with optional parity): base 0x0000 or 0x2000
    4 banks of 512 bytes of ROM or SRAM: base 0x0000, 0x0800, 0xF000 or 0xF800
    power-up vector: 0x0000 (level 0) or 0xFFFC (load)
    optional memerr interrupt (level 2)
    optional power fail interrupt (level 1)
    optional real-time clock interrupt (level 5 or 7)


    Setup for the emulated system:
    0x0000: 8kb on-board DRAM + 24kb extension RAM (total 32kb)
    0xF800: 512 bytes SRAM
    0xFA00: 512 bytes SRAM (or empty?)
    0xFC00: 512 bytes self-test ROM
    0xFE00: 512 bytes loader ROM
    power-up vector: 0xFFFC (load)

    Note that only interrupt levels 3-7 are supported by the board (8-15 are not wired).

TODO:
* finish ASR emulation
* programmer panel
* emulate other devices: card reader, printer

    Original implementation: Raphael Nabet

    Rewritten by Michael Zapf 2014
*/

#include "emu.h"
#include "cpu/tms9900/tms9900.h"

#include "911_vdt.h"
#include "sound/beep.h"
#include "733_asr.h"

#include "bus/ti99x/990_dk.h"


namespace {

class ti990_4_state : public driver_device
{
public:
	ti990_4_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_fd800(*this, "fd800") { }

	void ti990_4v(machine_config &config);
	void ti990_4(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint8_t panel_read(offs_t offset);
	void panel_write(offs_t offset, uint8_t data);
	void external_operation(offs_t offset, uint8_t data);
	uint8_t interrupt_level();
	void fd_interrupt(int state);
	void asrkey_interrupt(int state);
	void vdtkey_interrupt(int state);
	void line_interrupt(int state);

	void crumap(address_map &map) ATTR_COLD;
	void crumap_v(address_map &map) ATTR_COLD;
	void memmap(address_map &map) ATTR_COLD;

	void        hold_load();
	TIMER_CALLBACK_MEMBER(clear_load);

	int         m_intlines = 0;
	int         m_int_level = 0;
	emu_timer*  m_load_timer = nullptr;
	void        reset_int_lines();
	void        set_int_line(int line, int state);

	bool        m_ckon_state = 0;

	// Connected devices
	required_device<tms9900_device>     m_maincpu;
	required_device<fd800_legacy_device> m_fd800;
};

void ti990_4_state::hold_load()
{
	m_maincpu->set_input_line(INT_9900_LOAD, ASSERT_LINE);
	logerror("ti990_4: Triggering LOAD interrupt\n");
	m_load_timer->adjust(attotime::from_msec(100));
}

/*
    LOAD interrupt trigger callback
*/
TIMER_CALLBACK_MEMBER(ti990_4_state::clear_load)
{
	m_maincpu->set_input_line(INT_9900_LOAD, CLEAR_LINE);
	logerror("ti990_4: Released LOAD interrupt\n");
}

uint8_t ti990_4_state::panel_read(offs_t offset)
{
	if (offset == 11 || offset == 14)
		return 1;

	return 0;
}

void ti990_4_state::panel_write(offs_t offset, uint8_t data)
{
	logerror("ti990_4: writing to panel @CRU %04x: %02x\n", offset<<1, data);
}

void ti990_4_state::set_int_line(int line, int state)
{
	if (state)
		m_intlines |= (1 << line);
	else
		m_intlines &= ~(1 << line);

	if (m_intlines != 0)
	{
		m_int_level = 0;
		while ((m_intlines & (1 << m_int_level))==0) m_int_level++;
		logerror("ti990_4: Setting int level to %x\n", m_int_level);
		m_maincpu->set_input_line(INT_9900_INTREQ, ASSERT_LINE);
	}
	else
		m_maincpu->set_input_line(INT_9900_INTREQ, CLEAR_LINE);
}

void ti990_4_state::reset_int_lines()
{
	m_intlines = 0;
}

/*
    Callback from the floppy controller.
*/
void ti990_4_state::fd_interrupt(int state)
{
	set_int_line(7, state);
}

/*
    Connection to VDT
*/
void ti990_4_state::vdtkey_interrupt(int state)
{
	set_int_line(3, state);
}

void ti990_4_state::line_interrupt(int state)
{
	if (m_ckon_state) set_int_line(5, state);
}

/*
    Callback from the terminal.
*/
void ti990_4_state::asrkey_interrupt(int state)
{
	set_int_line(6, state);
}

void ti990_4_state::external_operation(offs_t offset, uint8_t data)
{
	static char const *const extop[8] = { "inv1", "inv2", "IDLE", "RSET", "inv3", "CKON", "CKOF", "LREX" };
	switch (offset)
	{
	case IDLE_OP:
		return;
	case RSET_OP:
		m_ckon_state = false;
		// clear controller panel and smi fault LEDs
		break;
	case CKON_OP:
		m_ckon_state = true;
		break;
	case CKOF_OP:
		m_ckon_state = false;
		set_int_line(5, 0);
		break;
	case LREX_OP:
		// TODO: Verify this
		hold_load();
		break;

	default:
		logerror("ti99_4x: External operation %s not implemented on TI-990/4 board\n", extop[offset]);
	}
}

uint8_t ti990_4_state::interrupt_level()
{
	return m_int_level;
}

/*
    Memory map - see description above
*/

void ti990_4_state::memmap(address_map &map)
{
	map(0x0000, 0x7fff).ram(); /* dynamic RAM */
	map(0x8000, 0xf7ff).noprw(); /* reserved for expansion */
	map(0xf800, 0xfbff).ram(); /* static RAM? */
	map(0xfc00, 0xffff).rom(); /* LOAD ROM */
}


/*
    CRU map

    0x0000-0x1EFF: user devices
    0x1F00-0x1F3F: CRU interrupt + expansion control
    0x1F40-0x1F5F: TILINE coupler interrupt control
    0x1F60-0x1F9F: reserved
    0x1FA0-0x1FBF: memory mapping and memory protect
    0x1FC0-0x1FDF: internal interrupt control
    0x1FF0-0x1FFF: front panel

    Default user map:
    0x0000-0x001f: 733 ASR (int 6)
    0x0020-0x003f: PROM programmer (wired to int 15, unused)
    0x0040-0x005f: 804 card reader (int 4)
    0x0060-0x007f: line printer (wired to int 14, unused)
    0x0080-0x00bf: FD800 floppy controller (int 7)
    0x00c0-0x00ff: VDT1 (int 3 - wired to int 11, unused)
    0x0100-0x013f: VDT2, or CRU expansion (int ??? - wired to int 10, unused)
    0x0140-0x017f: VDT3 (int ??? - wired to int 9, unused)
*/

void ti990_4_state::crumap(address_map &map)
{
	map(0x0000, 0x001f).rw("asr733", FUNC(asr733_device::cru_r), FUNC(asr733_device::cru_w));
	map(0x0080, 0x00bf).rw(m_fd800, FUNC(fd800_legacy_device::cru_r), FUNC(fd800_legacy_device::cru_w));
	map(0x1fe0, 0x1fff).rw(FUNC(ti990_4_state::panel_read), FUNC(ti990_4_state::panel_write));
}

void ti990_4_state::crumap_v(address_map &map)
{
	map(0x0080, 0x00bf).rw(m_fd800, FUNC(fd800_legacy_device::cru_r), FUNC(fd800_legacy_device::cru_w));
	map(0x0100, 0x011f).rw("vdt911", FUNC(vdt911_device::cru_r), FUNC(vdt911_device::cru_w));
	map(0x1fe0, 0x1fff).rw(FUNC(ti990_4_state::panel_read), FUNC(ti990_4_state::panel_write));
}


/* static const floppy_interface ti990_4_floppy_interface =
{
    FLOPPY_STANDARD_8_DSSD,
    LEGACY_FLOPPY_OPTIONS_NAME(fd800),
    nullptr
}; */

void ti990_4_state::machine_start()
{
	m_load_timer = timer_alloc(FUNC(ti990_4_state::clear_load), this);
}

void ti990_4_state::machine_reset()
{
	hold_load();
	reset_int_lines();
	m_ckon_state = false;
	m_maincpu->set_ready(ASSERT_LINE);
}

void ti990_4_state::ti990_4(machine_config &config)
{
	/* basic machine hardware */
	/* TMS9900 CPU @ 3.0(???) MHz */
	TMS9900(config, m_maincpu, 3000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &ti990_4_state::memmap);
	m_maincpu->set_addrmap(AS_IO, &ti990_4_state::crumap);
	m_maincpu->extop_cb().set(FUNC(ti990_4_state::external_operation));
	m_maincpu->intlevel_cb().set(FUNC(ti990_4_state::interrupt_level));

	// Terminal
	asr733_device& term(ASR733(config, "asr733", 0));
	term.keyint_cb().set(FUNC(ti990_4_state::asrkey_interrupt));
	term.lineint_cb().set(FUNC(ti990_4_state::line_interrupt));

	// Floppy controller
	TI99X_FD800(config, "fd800", 0).int_cb().set(FUNC(ti990_4_state::fd_interrupt));

	//  TODO: Add floppy drives
}

void ti990_4_state::ti990_4v(machine_config &config)
{
	/* basic machine hardware */
	/* TMS9900 CPU @ 3.0(???) MHz */
	TMS9900(config, m_maincpu, 3000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &ti990_4_state::memmap);
	m_maincpu->set_addrmap(AS_IO, &ti990_4_state::crumap_v);
	m_maincpu->extop_cb().set(FUNC(ti990_4_state::external_operation));
	m_maincpu->intlevel_cb().set(FUNC(ti990_4_state::interrupt_level));

	// VDT 911 terminal
	vdt911_device& term(VDT911(config, "vdt911", 0));
	term.keyint_cb().set(FUNC(ti990_4_state::vdtkey_interrupt));
	term.lineint_cb().set(FUNC(ti990_4_state::line_interrupt));

	// Floppy controller
	TI99X_FD800(config, "fd800", 0).int_cb().set(FUNC(ti990_4_state::fd_interrupt));

	//  TODO: Add floppy drives
}

/*
  ROM loading
*/
ROM_START(ti990_4)
	/*CPU memory space*/
	ROM_REGION16_BE(0x10000, "maincpu",0)

#if 0
	/* ROM set 945121-5: "733 ASR ROM loader with self test (prototyping)"
	(cf 945401-9701 pp. 1-19) */

	/* test ROM */
	ROMX_LOAD("94519209.u39", 0xFC00, 0x100, CRC(0a0b0c42) SHA1(bb1f0c611b640cccadeaf8fbbabac7343fcb2a99), ROM_NIBBLE | ROM_SHIFT_NIBBLE_HI | ROM_SKIP(1))
	ROMX_LOAD("94519210.u55", 0xFC00, 0x100, CRC(d078af61) SHA1(6f7e90b0972b97d3a3820b05ac8ffbd579910351), ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO | ROM_SKIP(1))
	ROMX_LOAD("94519211.u61", 0xFC01, 0x100, CRC(6cf7d4a0) SHA1(c26f09cec612545c5c4511291b5eb432998c21b3), ROM_NIBBLE | ROM_SHIFT_NIBBLE_HI | ROM_SKIP(1))
	ROMX_LOAD("94519212.u78", 0xFC01, 0x100, CRC(d9522458) SHA1(8987e1f155da390ae39b45e41c2d847e62a3bc26), ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO | ROM_SKIP(1))

	/* LOAD ROM */
	ROMX_LOAD("94519113.u3", 0xFE00, 0x100, CRC(8719b04e) SHA1(63bdf5d9c25147bb9b608df6e9dbc2cf3789569b), ROM_NIBBLE | ROM_SHIFT_NIBBLE_HI | ROM_SKIP(1))
	ROMX_LOAD("94519114.u4", 0xFE00, 0x100, CRC(72a040e0) SHA1(3291ab8af7097f69f1c7c95c2c0eaf327d4e98d5), ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO | ROM_SKIP(1))
	ROMX_LOAD("94519115.u6", 0xFE01, 0x100, CRC(9ccf8cca) SHA1(0e726ea76ef15274bb1a4475bfb672005dc05e00), ROM_NIBBLE | ROM_SHIFT_NIBBLE_HI | ROM_SKIP(1))
	ROMX_LOAD("94519116.u7", 0xFE01, 0x100, CRC(fa387bf3) SHA1(d91de2335e0bb03e14bc4a951ee0bc04c6fff5ab), ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO | ROM_SKIP(1))

#else
	/* ROM set 945121-4(?): "Floppy disc loader with self test" (cf 945401-9701 pp. 1-19) */
	ROM_LOAD16_WORD("ti9904.rom", 0xFC00, 0x400, CRC(691e7d19) SHA1(58d9bed80490fdf71c743bfd3077c70840b7df8c))
#endif

	ROM_REGION(asr733_device::chr_region_len, asr733_chr_region, ROMREGION_ERASEFF)
ROM_END

ROM_START(ti990_4v)
	/*CPU memory space*/
	ROM_REGION16_BE(0x10000, "maincpu",0)
	ROM_LOAD16_WORD("ti9904.rom", 0xFC00, 0x400, CRC(691e7d19) SHA1(58d9bed80490fdf71c743bfd3077c70840b7df8c))

	/* VDT911 character definitions */
	ROM_REGION(vdt911_device::chr_region_len, vdt911_chr_region, ROMREGION_ERASEFF)
ROM_END

} // anonymous namespace


//    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT  CLASS          INIT        COMPANY              FULLNAME                                                           FLAGS
COMP( 1976, ti990_4,  0,       0,      ti990_4,  0,     ti990_4_state, empty_init, "Texas Instruments", "TI Model 990/4 Microcomputer System",                             MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1976, ti990_4v, ti990_4, 0,      ti990_4v, 0,     ti990_4_state, empty_init, "Texas Instruments", "TI Model 990/4 Microcomputer System with Video Display Terminal", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



tiki100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder, Frode van der Meeren
/***************************************************************************

    Tiki 100

    12/05/2009 Skeleton driver.

    Most of the driver is written by Curt Coder.

    2020-06-02:
       Screen-drawing redone by Frode van der Meeren.
       There is still some work that needs to get this
       even more accurate, but it's mostly down to
       triggering update at the right times.

****************************************************************************/

/*

    TODO:

    - winchester hard disk
    - analog/digital I/O
    - light pen
    - 8088 CPU card

*/

#include "emu.h"
#include "tiki100.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "utf8.h"


/* Memory Banking */

uint8_t tiki100_state::mrq_r(offs_t offset)
{
	bool mdis = 1;

	uint8_t data = m_exp->mrq_r(offset, 0xff, mdis);

	offs_t prom_addr = mdis << 5 | m_vire << 4 | m_rome << 3 | (offset >> 13);
	uint8_t prom = m_prom->base()[prom_addr] ^ 0xff;

	if (prom & ROM0)
	{
		data = m_rom->base()[offset & 0x3fff];
	}

	if (prom & ROM1)
	{
		data = m_rom->base()[0x2000 | (offset & 0x3fff)];
	}

	if (prom & VIR)
	{
		uint16_t addr = (offset + (m_scroll << 7)) & TIKI100_VIDEORAM_MASK;

		data = m_video_ram[addr];
	}

	if (prom & RAM0)
	{
		data = m_ram->pointer()[offset];
	}

	return data;
}

void tiki100_state::mrq_w(offs_t offset, uint8_t data)
{
	bool mdis = 1;
	offs_t prom_addr = mdis << 5 | m_vire << 4 | m_rome << 3 | (offset >> 13);
	uint8_t prom = m_prom->base()[prom_addr] ^ 0xff;

	if (prom & VIR)
	{
		uint16_t addr = (offset + (m_scroll << 7)) & TIKI100_VIDEORAM_MASK;

		m_video_ram[addr] = data;
	}

	if (prom & RAM0)
	{
		m_ram->pointer()[offset] = data;
	}

	m_exp->mrq_w(offset, data);
}

uint8_t tiki100_state::iorq_r(offs_t offset)
{
	uint8_t data = m_exp->iorq_r(offset, 0xff);

	switch ((offset & 0xff) >> 2)
	{
	case 0x00: // KEYS
		data = keyboard_r();
		break;

	case 0x01: // SERS
		data = m_dart->cd_ba_r(offset & 0x03);
		break;

	case 0x02: // PARS
		data = m_pio->read(offset & 0x03);
		break;

	case 0x04: // FLOP
		data = m_fdc->read(offset & 0x03);
		break;

	case 0x05: // VIPS
		switch (offset & 0x03)
		{
		case 3:
			data = m_psg->data_r();
			break;
		}
		break;

	case 0x06: // TIMS
		data = m_ctc->read(offset & 0x03);
		break;
	}

	return data;
}

void tiki100_state::iorq_w(offs_t offset, uint8_t data)
{
	m_exp->iorq_w(offset, data);

	switch ((offset & 0xff) >> 2)
	{
	case 0x00: // KEYS
		keyboard_w(data);
		break;

	case 0x01: // SERS
		m_dart->cd_ba_w(offset & 0x03, data);
		break;

	case 0x02: // PARS
		m_pio->write(offset & 0x03, data);
		break;

	case 0x03: // VIPB
		video_mode_w(data);
		break;

	case 0x04: // FLOP
		m_fdc->write(offset & 0x03, data);
		break;

	case 0x05: // VIPS
		switch (offset & 0x03)
		{
		case 0: case 1:
			palette_w(data);
			break;

		case 2:
			m_psg->address_w(data);
			break;

		case 3:
			m_psg->data_w(data);
			break;
		}
		break;

	case 0x06: // TIMS
		m_ctc->write(offset & 0x03, data);
		break;

	case 0x07: // SYL
		system_w(data);
		break;
	}
}

/* Read/Write Handlers */

uint8_t tiki100_state::keyboard_r()
{
	uint8_t data = 0xff;

	if (m_keylatch < 13)
	{
		data = m_y[m_keylatch]->read();
	}

	m_keylatch++;
	if (m_keylatch == 16) m_keylatch = 0;   // Column selected by a 4-bit counter

	return data;
}

void tiki100_state::keyboard_w(uint8_t data)
{
	m_keylatch = 0;
}

void tiki100_state::video_mode_w(uint8_t data)
{
	/*

	    bit     description

	    0       palette entry bit 0
	    1       palette entry bit 1
	    2       palette entry bit 2
	    3       palette entry bit 3
	    4       mode select bit 0
	    5       mode select bit 1
	    6       unused
	    7       write color during HBLANK

	*/

	m_mode = data;
}

void tiki100_state::palette_w(uint8_t data)
{
	/*

	    bit     description

	    0       blue intensity bit 0
	    1       blue intensity bit 1
	    2       green intensity bit 0
	    3       green intensity bit 1
	    4       green intensity bit 2
	    5       red intensity bit 0
	    6       red intensity bit 1
	    7       red intensity bit 2

	*/

	m_palette_val = data;
}

void tiki100_state::system_w(uint8_t data)
{
	/*

	    bit     signal  description

	    0       DRIS0   drive select 0
	    1       DRIS1   drive select 1
	    2       _ROME   enable ROM at 0000-3fff
	    3       VIRE    enable video RAM at 0000-7fff or 4000-bfff
	    4       SDEN    single density select (0=DD, 1=SD)
	    5       _LMP0   GRAFIKK key led
	    6       MOTON   floppy motor
	    7       _LMP1   LOCK key led

	*/

	// drive select
	floppy_image_device *floppy = nullptr;

	if (BIT(data, 0)) floppy = m_floppy0->get_device();
	if (BIT(data, 1)) floppy = m_floppy1->get_device();

	m_fdc->set_floppy(floppy);

	// density select
	m_fdc->dden_w(BIT(data, 4));

	// floppy motor
	if (floppy) floppy->mon_w(!BIT(data, 6));

	/* GRAFIKK key led */
	m_leds[0] = BIT(data, 5);

	/* LOCK key led */
	m_leds[1] = BIT(data, 7);

	/* bankswitch */
	m_rome = BIT(data, 2);
	m_vire = BIT(data, 3);
}

/* Memory Maps */

void tiki100_state::tiki100_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(tiki100_state::mrq_r), FUNC(tiki100_state::mrq_w));
}

void tiki100_state::tiki100_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(tiki100_state::iorq_r), FUNC(tiki100_state::iorq_w));
}

/* Input Ports */

static INPUT_PORTS_START( tiki100 )
/*
        |    0    |    1    |    2    |    3    |    4    |    5    |    6    |    7    |
    ----+---------+---------+---------+---------+---------+---------+---------+---------+
      1 | CTRL    | SHIFT   | BRYT    | RETUR   | MLMROM  | / (num) | SLETT   |         |
      2 | GRAFIKK | 1       | ANGRE   | a       | <       | z       | q       | LOCK    |
      3 | 2       | w       | s       | x       | 3       | e       | d       | c       |
      4 | 4       | r       | f       | v       | 5       | t       | g       | b       |
      5 | 6       | y       | h       | n       | 7       | u       | j       | m       |
      6 | 8       | i       | k       | ,       | 9       | o       | l       | .       |
      7 | 0       | p       | ?       | -       | +       | ?       | ?       | HJELP   |
      8 | @       | ^       | '       | VENSTRE | UTVID   | F1      | F4      | SIDEOPP |
      9 | F2      | F3      | F5      | F6      | OPP     | SIDENED | VTAB    | NED     |
     10 | + (num) | - (num) | * (num) | 7 (num) | 8 (num) | 9 (num) | % (num) | = (num) |
     11 | 4 (num) | 5 (num) | 6 (num) | HTAB    | 1 (num) | 0 (num) | . (num) |         |
     12 | HJEM    | H?YRE   | 2 (num) | 3 (num) | ENTER   |         |         |         |
     13 |         |         |         |         |         |         |         |         |
    ----+---------+---------+---------+---------+---------+---------+---------+---------+

    Unused bits may return some fixed values, an undocumented system call will read and return them.
*/
	PORT_START("Y1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BRYTT") PORT_CODE(KEYCODE_ESC)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("MLMROM") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad /") PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SLETT") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GRAFIKK") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ANGRE") PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('<') PORT_CHAR('>')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))

	PORT_START("Y3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')

	PORT_START("Y4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')

	PORT_START("Y5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('/')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')

	PORT_START("Y6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR(';')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(':')

	PORT_START("Y7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('=')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(U'ø') PORT_CHAR(U'Ø')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('+') PORT_CHAR('?')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(U'å') PORT_CHAR(U'Å')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(U'æ') PORT_CHAR(U'Æ')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("HJELP")

	PORT_START("Y8")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('^') PORT_CHAR('|')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\'') PORT_CHAR('*')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("UTVID") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SIDEOPP") PORT_CODE(KEYCODE_PGUP) PORT_CHAR(UCHAR_MAMEKEY(PGUP))

	PORT_START("Y9")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F5") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F6") PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SIDENED") PORT_CODE(KEYCODE_PGDN) PORT_CHAR(UCHAR_MAMEKEY(PGDN))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("VTAB")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))

	PORT_START("Y10")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad +") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad -") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad *") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad %")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad =")

	PORT_START("Y11")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("HTAB") PORT_CODE(KEYCODE_TAB) PORT_CHAR(UCHAR_MAMEKEY(TAB)) PORT_CHAR('\t')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad .") PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y12")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("HJEM") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad ENTER") PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y13")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ST")
	PORT_CONFNAME( 0x01, 0x01, "DART TxCA")
	PORT_CONFSETTING( 0x00, "BAR0" )
	PORT_CONFSETTING( 0x01, "BAR2" )
INPUT_PORTS_END

/* Video */

TIMER_DEVICE_CALLBACK_MEMBER( tiki100_state::scanline_start )
{
	// TODO: Optional assertion of VSYNC on one of the PIO-pins, as used by some demos
	//int scanline = param;

	// TODO: use TIMER to trigger this every 16-pixel chunk (20MHz/16) instead of once at the end of the scanline
	m_screen->update_now();

	// This point is by the end of a line. Changes in palette are applied here.
	if (BIT(m_mode, 7))
	{
		int color = m_mode & 0x0f;
		uint8_t colordata = ~m_palette_val;

		m_palette->set_pen_color(color, pal3bit(colordata >> 5), pal3bit(colordata >> 2), pal2bit(colordata >> 0));
	}
}

uint32_t tiki100_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	//
	//  Emulates the pixel-shifter and row/column counters from a
	//  starting point on screen to an ending point on screen.
	//
	//  For the most accurate results, this should be run once for
	//  every 16-pixel chunk, but per the spring of 2020 there is
	//  only one demo that require this degree of accuracy.
	//
	//

	rgb_t const *const palette = m_palette->palette()->entry_list_raw();

	for (int vaddr = cliprect.min_y; vaddr <= cliprect.max_y; vaddr++)
	{
		// This is at the start of a line
		int haddr = (cliprect.min_x>>4);
		int const haddr_end = (cliprect.max_x>>4);
		for (; haddr <= haddr_end; haddr++)
		{
			// This is at the start of a 16-dot cluster. Changes in m_scroll and m_video_ram come into effect here.
			uint16_t addr = ((vaddr+m_scroll)<<7) | (haddr<<1);
			uint16_t data = (m_video_ram[(addr+1) & TIKI100_VIDEORAM_MASK]<<8) | m_video_ram[addr & TIKI100_VIDEORAM_MASK];
			for(int dot = 0; dot < 16; dot++)
			{
				// This is at the start of a dot. Changes in m_mode are applied at this point.
				int mode = (m_mode >> 4) & 0x03;
				if((mode == 1) || (!(dot&0x01) && mode == 2) || (!(dot&0x03) && mode == 3))
				{
					// This is the point when a pixel is latched from the dot-shifter.
					//
					//   For the sake of readability:
					//   mode == 0: keep the old pixel
					//   mode == 1: latch new pixel once per dot
					//   mode == 2: latch new pixel once per 2 dots
					//   mode == 3: latch new pixel once per 4 dots
					//
					m_current_pixel = data&0x0F;
				}
				bitmap.pix(vaddr, (haddr<<4) + dot) = palette[m_current_pixel&0x0F];

				// This will run the dot-shifter and add the TEST-pattern to the upper bits (usually hidden by palette).
				data = (data>>1)|((haddr&0x02)<<14);
			}
		}
	}

	return 0;
}

/* Z80-PIO Interface */

void tiki100_state::write_centronics_ack(int state)
{
	m_centronics_ack = state;
}

void tiki100_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

void tiki100_state::write_centronics_perror(int state)
{
	m_centronics_perror = state;
}

uint8_t tiki100_state::pio_pb_r()
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4       ACK
	    5       BUSY
	    6       NO PAPER
	    7       UNIT SELECT, tape in

	*/

	uint8_t data = 0;

	// centronics
	data |= m_centronics_ack << 4;
	data |= m_centronics_busy << 5;
	data |= m_centronics_perror << 6;

	// cassette
	data |= (m_cassette->input() > 0.0) << 7;

	return data;
}

void tiki100_state::pio_pb_w(uint8_t data)
{
	/*

	    bit     description

	    0       STRB
	    1
	    2
	    3
	    4
	    5
	    6       tape out
	    7

	*/

	// centronics
	m_centronics->write_strobe(BIT(data, 0));

	// cassette
	m_cassette->output(BIT(data, 6) ? -1 : 1);
}

/* Z80-CTC Interface */

TIMER_DEVICE_CALLBACK_MEMBER(tiki100_state::ctc_tick)
{
	m_ctc->trg0(1);
	m_ctc->trg0(0);

	m_ctc->trg1(1);
	m_ctc->trg1(0);
}

void tiki100_state::bar0_w(int state)
{
	m_ctc->trg2(state);

	m_dart->rxca_w(state);

	if (!m_st) m_dart->txca_w(state);
}

void tiki100_state::bar2_w(int state)
{
	if (m_st) m_dart->txca_w(state);

	m_ctc->trg3(state);
}

/* FD1797 Interface */

void tiki100_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_TIKI100_FORMAT);
}

static void tiki100_floppies(device_slot_interface &device)
{
	device.option_add("525ssdd", FLOPPY_525_SSDD);
	device.option_add("525dd", FLOPPY_525_DD); // Tead FD-55A
	device.option_add("525qd", FLOPPY_525_QD); // Teac FD-55F
}

/* AY-3-8912 Interface */

void tiki100_state::video_scroll_w(uint8_t data)
{
	m_scroll = data;
}

/* Z80 Daisy Chain */

static const z80_daisy_config tiki100_daisy_chain[] =
{
	{ Z80CTC_TAG },
	{ Z80DART_TAG },
	{ Z80PIO_TAG },
	{ "slot1" },
	{ "slot2" },
	{ "slot3" },
	{ nullptr }
};

TIMER_DEVICE_CALLBACK_MEMBER( tiki100_state::tape_tick )
{
	m_pio->port_b_write((m_cassette->input() > 0.0) << 7);
}

void tiki100_state::busrq_w(int state)
{
	// since our Z80 has no support for BUSACK, we assume it is granted immediately
	m_maincpu->set_input_line(Z80_INPUT_LINE_BUSRQ, state);
	m_exp->busak_w(state);
}

/* Machine Start */

void tiki100_state::machine_start()
{
	m_leds.resolve();

	/* register for state saving */
	save_item(NAME(m_rome));
	save_item(NAME(m_vire));
	save_item(NAME(m_scroll));
	save_item(NAME(m_mode));
	save_item(NAME(m_palette_val));
	save_item(NAME(m_keylatch));
	save_item(NAME(m_centronics_ack));
	save_item(NAME(m_centronics_busy));
	save_item(NAME(m_centronics_perror));
	save_item(NAME(m_st));
}

void tiki100_state::machine_reset()
{
	system_w(0);

	m_st = m_st_io->read();
}

/* Machine Driver */

void tiki100_state::tiki100(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 8_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &tiki100_state::tiki100_mem);
	m_maincpu->set_addrmap(AS_IO, &tiki100_state::tiki100_io);
	m_maincpu->set_daisy_config(tiki100_daisy_chain);

	/* video hardware */
	TIMER(config, "scantimer").configure_scanline(FUNC(tiki100_state::scanline_start), "screen", 0, 1);
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(20_MHz_XTAL, 1280, 0, 1024, 312, 0, 256);
	m_screen->set_screen_update(FUNC(tiki100_state::screen_update));
	PALETTE(config, m_palette).set_entries(16);

	TIKI100_BUS(config, m_exp, 0);
	m_exp->irq_wr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_exp->nmi_wr_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_exp->busrq_wr_callback().set(FUNC(tiki100_state::busrq_w));
	m_exp->mrq_rd_callback().set(FUNC(tiki100_state::mrq_r));
	m_exp->mrq_wr_callback().set(FUNC(tiki100_state::mrq_w));
	TIKI100_BUS_SLOT(config, "slot1", m_exp, tiki100_cards, "8088");
	TIKI100_BUS_SLOT(config, "slot2", m_exp, tiki100_cards, "hdc");
	TIKI100_BUS_SLOT(config, "slot3", m_exp, tiki100_cards, nullptr);

	/* devices */
	Z80DART(config, m_dart, 8_MHz_XTAL / 4);
	m_dart->out_txda_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_txd));
	m_dart->out_dtra_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_dtr));
	m_dart->out_rtsa_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_rts));
	m_dart->out_txdb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_txd));
	m_dart->out_dtrb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_dtr));
	m_dart->out_rtsb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_rts));
	m_dart->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80PIO(config, m_pio, 8_MHz_XTAL / 4);
	m_pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio->in_pa_callback().set("cent_data_in", FUNC(input_buffer_device::read));
	m_pio->out_pa_callback().set("cent_data_out", FUNC(output_latch_device::write));
	m_pio->in_pb_callback().set(FUNC(tiki100_state::pio_pb_r));
	m_pio->out_pb_callback().set(FUNC(tiki100_state::pio_pb_w));

	Z80CTC(config, m_ctc, 8_MHz_XTAL / 4);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->zc_callback<0>().set(FUNC(tiki100_state::bar0_w));
	m_ctc->zc_callback<1>().set(m_dart, FUNC(z80dart_device::rxtxcb_w));
	m_ctc->zc_callback<2>().set(FUNC(tiki100_state::bar2_w));

	TIMER(config, "ctc").configure_periodic(FUNC(tiki100_state::ctc_tick), attotime::from_hz(8_MHz_XTAL / 4));

	FD1797(config, m_fdc, 8_MHz_XTAL / 8); // FD1767PL-02 or FD1797-PL
	FLOPPY_CONNECTOR(config, FD1797_TAG":0", tiki100_floppies, "525qd", tiki100_state::floppy_formats);
	FLOPPY_CONNECTOR(config, FD1797_TAG":1", tiki100_floppies, "525qd", tiki100_state::floppy_formats);

	rs232_port_device &rs232a(RS232_PORT(config, RS232_A_TAG, default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_dart, FUNC(z80dart_device::rxa_w));

	rs232_port_device &rs232b(RS232_PORT(config, RS232_B_TAG, default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_dart, FUNC(z80dart_device::rxb_w));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->set_data_input_buffer("cent_data_in");
	m_centronics->ack_handler().set(FUNC(tiki100_state::write_centronics_ack));
	m_centronics->busy_handler().set(FUNC(tiki100_state::write_centronics_busy));
	m_centronics->perror_handler().set(FUNC(tiki100_state::write_centronics_perror));

	INPUT_BUFFER(config, "cent_data_in");

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	AY8912(config, m_psg, 8_MHz_XTAL / 4);
	m_psg->set_flags(AY8910_SINGLE_OUTPUT);
	m_psg->port_a_write_callback().set(FUNC(tiki100_state::video_scroll_w));
	m_psg->add_route(ALL_OUTPUTS, "mono", 0.25);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	TIMER(config, "tape").configure_periodic(FUNC(tiki100_state::tape_tick), attotime::from_hz(44100));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("64K");

	// software list
	SOFTWARE_LIST(config, "flop_list").set_original("tiki100");
}

/* ROMs */

ROM_START( kontiki )
	ROM_REGION( 0x4000, Z80_TAG, ROMREGION_ERASEFF )
	ROM_LOAD( "tikirom-1.30.u10",  0x0000, 0x2000, CRC(c482dcaf) SHA1(d140706bb7fc8b1fbb37180d98921f5bdda73cf9) )

	ROM_REGION( 0x100, "u4", 0 )
	ROM_LOAD( "53ls140.u4", 0x000, 0x100, CRC(894b756f) SHA1(429e10de0e0e749246895801b18186ff514c12bc) )
ROM_END

ROM_START( tiki100 )
	ROM_REGION( 0x4000, Z80_TAG, ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS( "v203w" )

	ROM_SYSTEM_BIOS( 0, "v135", "TIKI ROM v1.35" )
	ROMX_LOAD( "tikirom-1.35.u10",  0x0000, 0x2000, CRC(7dac5ee7) SHA1(14d622fd843833faec346bf5357d7576061f5a3d), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v203w", "TIKI ROM v2.03 W" )
	ROMX_LOAD( "tikirom-2.03w.u10", 0x0000, 0x2000, CRC(79662476) SHA1(96336633ecaf1b2190c36c43295ac9f785d1f83a), ROM_BIOS(1) )

	ROM_REGION( 0x100, "u4", 0 )
	ROM_LOAD( "53ls140.u4", 0x000, 0x100, CRC(894b756f) SHA1(429e10de0e0e749246895801b18186ff514c12bc) )
ROM_END

/* System Drivers */

//    YEAR  NAME     PARENT   COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY             FULLNAME        FLAGS
COMP( 1984, kontiki, 0,       0,      tiki100, tiki100, tiki100_state, empty_init, "Kontiki Data A/S", "KONTIKI 100",  MACHINE_SUPPORTS_SAVE )
COMP( 1984, tiki100, kontiki, 0,      tiki100, tiki100, tiki100_state, empty_init, "Tiki Data A/S",    "TIKI 100",     MACHINE_SUPPORTS_SAVE )



tim011.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

        TIM-011

        04/09/2010 Skeleton driver.

****************************************************************************/

#include "emu.h"
#include "tim011_kbd.h"

#include "cpu/z180/z180.h"
#include "imagedev/floppy.h"
#include "formats/tim011_dsk.h"
#include "machine/upd765.h"
#include "bus/rs232/rs232.h"
#include "bus/tim011/exp.h"

#include "emupal.h"
#include "screen.h"

namespace {

class tim011_state : public driver_device
{
public:
	tim011_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0)
		, m_vram(*this, "videoram")
		, m_palette(*this, "palette")
		, m_exp(*this, "exp")
	{ }

	void tim011(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	uint32_t screen_update_tim011(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void print_w(uint8_t data);
	void scroll_w(uint8_t data);
	uint8_t print_r();
	uint8_t scroll_r();
	uint8_t m_scroll;

	required_device<z180_device> m_maincpu;
	required_device<upd765a_device> m_fdc;
	required_device_array<floppy_connector, 4> m_floppy;
	required_shared_ptr<u8> m_vram;
	required_device<palette_device> m_palette;
	required_device<bus::tim011::exp_slot_device> m_exp;

	void tim011_io(address_map &map) ATTR_COLD;
	void tim011_mem(address_map &map) ATTR_COLD;
	void tim011_palette(palette_device &palette) const;
};


void tim011_state::tim011_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x01fff).rom().mirror(0x3e000);
	map(0x40000, 0x7ffff).ram(); // 256KB RAM  8 * 41256 DRAM
}

void tim011_state::tim011_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x007f).ram(); /* Z180 internal registers */
	map(0x0080, 0x0081).mirror(0xff0e).m(m_fdc, FUNC(upd765a_device::map));
	map(0x00a0, 0x00a0).mirror(0xff0f).rw(m_fdc, FUNC(upd765a_device::dma_r), FUNC(upd765a_device::dma_w));
	map(0x00c0, 0x00c1).mirror(0xff0e).rw(FUNC(tim011_state::print_r), FUNC(tim011_state::print_w));
	map(0x00d0, 0x00d0).mirror(0xff0f).rw(FUNC(tim011_state::scroll_r), FUNC(tim011_state::scroll_w));
	map(0x8000, 0xffff).ram().share(m_vram); // Video RAM 43256 SRAM  (32KB)
}

/* Input ports */
static INPUT_PORTS_START( tim011 )
INPUT_PORTS_END

void tim011_state::machine_reset()
{
	m_scroll = 0;
	// motor is actually connected on TXS pin of CPU
	for (auto &drive : m_floppy)
	{
		if (drive->get_device())
			drive->get_device()->mon_w(0);
	}
}

uint32_t tim011_state::screen_update_tim011(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (int x = 0; x < 512/4; x++)
	{
		for (int y = cliprect.top(); y <= cliprect.bottom(); y++)
		{
			int horpos = x << 2;
			u8 code = m_vram[(x << 8) + ((y + m_scroll) & 0xff)];
			bitmap.pix(y, horpos++) =  (code >> 0) & 0x03;
			bitmap.pix(y, horpos++) =  (code >> 2) & 0x03;
			bitmap.pix(y, horpos++) =  (code >> 4) & 0x03;
			bitmap.pix(y, horpos++) =  (code >> 6) & 0x03;
		}
	}
	return 0;
}

void tim011_state::print_w(uint8_t data)
{
	//printf("print_w :%02x\n",data);
}

uint8_t tim011_state::print_r()
{
	//printf("print_r\n");
	return 0;
}

void tim011_state::scroll_w(uint8_t data)
{
	m_scroll = data;
}

uint8_t tim011_state::scroll_r()
{
	return m_scroll;
}

static void tim011_floppies(device_slot_interface &device)
{
	device.option_add("35dd", FLOPPY_35_DD);
}

static void tim011_floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_TIM011_FORMAT);
}

void tim011_state::tim011_palette(palette_device &palette) const
{
	static constexpr rgb_t tim011_pens[4] = {
		{ 0x00, 0x00, 0x00 }, // 0
		{ 0x00, 0x55, 0x00 }, // 1
		{ 0x00, 0xaa, 0x00 }, // 2
		{ 0x00, 0xff, 0x00 }, // 3
	};

	palette.set_pen_colors(0, tim011_pens);
}

void tim011_state::tim011(machine_config &config)
{
	/* basic machine hardware */
	HD64180RP(config, m_maincpu, 12.288_MHz_XTAL); // location U17 HD64180
	m_maincpu->set_addrmap(AS_PROGRAM, &tim011_state::tim011_mem);
	m_maincpu->set_addrmap(AS_IO, &tim011_state::tim011_io);
	m_maincpu->tend1_wr_callback().set(m_fdc, FUNC(upd765a_device::tc_line_w));
	m_maincpu->txa0_wr_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	m_maincpu->rts0_wr_callback().set("rs232", FUNC(rs232_port_device::write_rts));
	m_maincpu->rts0_wr_callback().append("rs232", FUNC(rs232_port_device::write_dtr));
	m_maincpu->txa1_wr_callback().set("keyboard", FUNC(tim011_keyboard_device::write_rxd));

	TIM011_KEYBOARD(config, "keyboard").txd_callback().set(m_maincpu, FUNC(z180_device::rxa1_w));

	// FDC9266 location U43
	UPD765A(config, m_fdc, 8_MHz_XTAL, true, true);
	m_fdc->intrq_wr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ2);
	m_fdc->drq_wr_callback().set_inputline(m_maincpu, Z180_INPUT_LINE_DREQ1);

	/* floppy drives */
	FLOPPY_CONNECTOR(config, m_floppy[0], tim011_floppies, "35dd",  tim011_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], tim011_floppies, nullptr, tim011_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[2], tim011_floppies, nullptr, tim011_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[3], tim011_floppies, nullptr, tim011_floppy_formats).enable_sound(true);

	/* video hardware */
	PALETTE(config, m_palette, FUNC(tim011_state::tim011_palette), 4);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(12.288_MHz_XTAL, 768, 0, 512, 320, 0, 256);
	screen.set_screen_update(FUNC(tim011_state::screen_update_tim011));
	screen.set_palette(m_palette);

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_maincpu, FUNC(z180_device::rxa0_w));
	rs232.cts_handler().set(m_maincpu, FUNC(z180_device::cts0_w));

	TIM011_EXPANSION_SLOT(config, m_exp, tim011_exp_devices, nullptr);
	m_exp->set_io_space(m_maincpu, AS_IO);
}

/* ROM definition */
ROM_START( tim011 )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "sys_tim011.u16", 0x0000, 0x2000, CRC(5b4f1300) SHA1(d324991c4292d7dcde8b8d183a57458be8a2be7b))
ROM_END

} // Anonymous namespace

/* Driver */

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY                    FULLNAME   FLAGS */
COMP( 1987, tim011, 0,      0,      tim011,  tim011, tim011_state, empty_init, "Mihajlo Pupin Institute", "TIM-011", MACHINE_NOT_WORKING)



tim100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/********************************************************************************************************

TIM-100 Terminal
Mihajlo Pupin Institute

2012/12/21 Skeleton driver.
2016/07/14 Fixed display etc [Robbbert]

Notes:
- CRTC is configured to display 17 rows of 40 characters, but code always blanks the last 3 rows.
- Serial keyboard appears to need 8 bits, 2 stop bits, odd parity
- Unable to type anything as it seems uarts want BRKDET activated all the time, which we cannot do.
- Unable to find any technical info at all, so it's all guesswork.

*******************************************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/i8085/i8085.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "video/i8275.h"
#include "emupal.h"
#include "screen.h"

namespace {

class tim100_state : public driver_device
{
public:
	tim100_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_charmap(*this, "chargen")
		, m_maincpu(*this, "maincpu")
		, m_palette(*this, "palette")
		, m_crtc(*this, "crtc")
		, m_mem(*this, "maincpu", AS_OPCODES)
		, m_dma_view(*this, "dma_view")
	{ }

	void tim100(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	u8 dma_r(offs_t offset);
	void sod_w(int state);
	I8275_DRAW_CHARACTER_MEMBER( crtc_display_pixels );

	void mem_map(address_map &map) ATTR_COLD;
	void mem_xfer_map(address_map &map) ATTR_COLD;

	required_region_ptr<uint8_t> m_charmap;
	required_device<i8085a_cpu_device> m_maincpu;
	required_device<palette_device> m_palette;
	required_device<i8276_device> m_crtc;
	required_address_space m_mem;
	memory_view m_dma_view;
};

u8 tim100_state::dma_r(offs_t offset)
{
	u8 data = m_mem->read_byte(offset);
	if (!machine().side_effects_disabled())
		m_crtc->dack_w(data);
	return data;
}

void tim100_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom().region("maincpu", 0); // 2764 at U16
	map(0x2000, 0x27ff).ram().share("videoram"); // 2KB static ram CDM6116A at U15
	map(0x6000, 0x6001).rw("uart_u17", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x8000, 0x8001).rw("uart_u18", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0xa000, 0xa000).nopw();   // continuously writes 00 here
	map(0xc000, 0xc001).rw(m_crtc, FUNC(i8276_device::read), FUNC(i8276_device::write)); // i8276
}

void tim100_state::mem_xfer_map(address_map &map)
{
	map.unmap_value_high();
	mem_map(map);
	map(0x0000, 0xffff).view(m_dma_view);
	m_dma_view[0](0x0000, 0xffff).r(FUNC(tim100_state::dma_r));
}


/* Input ports */
static INPUT_PORTS_START( tim100 )
INPUT_PORTS_END

static DEVICE_INPUT_DEFAULTS_START( keyboard )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_EVEN )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_2 )
DEVICE_INPUT_DEFAULTS_END

static const rgb_t tim100_palette[3] = {
	rgb_t(0x00, 0x00, 0x00), // black
	rgb_t(0xa0, 0xa0, 0xa0), // white
	rgb_t(0xff, 0xff, 0xff)  // highlight
};

void tim100_state::machine_start()
{
	m_palette->set_pen_colors(0, tim100_palette);
}

const gfx_layout charlayout =
{
	12, 16,             /* 8x16 characters */
	256,                /* 128 characters */
	1,              /* 1 bits per pixel */
	{0},                /* no bitplanes; 1 bit per pixel */
	{0,1,2,3,4,5,0x8000,0x8001,0x8002,0x8003,0x8004,0x8005},
	{0 * 8, 1 * 8, 2 * 8, 3 * 8, 4 * 8, 5 * 8, 6 * 8, 7 * 8,
		8 * 8, 9 * 8, 10 * 8, 11 * 8, 12 * 8, 13 * 8, 14 * 8, 15 * 8},
	8*16                /* space between characters */
};

static GFXDECODE_START( gfx_tim100 )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END


I8275_DRAW_CHARACTER_MEMBER( tim100_state::crtc_display_pixels )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	for (uint8_t i = 0; i < 2; i++)
	{
		using namespace i8275_attributes;
		uint8_t pixels = m_charmap[(i * 0x1000) | (linecount & 15) | (charcode << 4)];
		if (BIT(attrcode, VSP))
			pixels = 0;

		if (BIT(attrcode, LTEN))
			pixels = 0xff;

		if (BIT(attrcode, RVV))
			pixels ^= 0xff;

		bool hlgt = BIT(attrcode, HLGT);
		bitmap.pix(y, x++) = palette[BIT(pixels, 7) ? (hlgt ? 2 : 1) : 0];
		bitmap.pix(y, x++) = palette[BIT(pixels, 6) ? (hlgt ? 2 : 1) : 0];
		bitmap.pix(y, x++) = palette[BIT(pixels, 5) ? (hlgt ? 2 : 1) : 0];
		bitmap.pix(y, x++) = palette[BIT(pixels, 4) ? (hlgt ? 2 : 1) : 0];
		bitmap.pix(y, x++) = palette[BIT(pixels, 3) ? (hlgt ? 2 : 1) : 0];
		bitmap.pix(y, x++) = palette[BIT(pixels, 2) ? (hlgt ? 2 : 1) : 0];
	}
}

void tim100_state::sod_w(int state)
{
	if (state)
		m_dma_view.select(0);
	else
		m_dma_view.disable();
}


void tim100_state::tim100(machine_config &config)
{
	/* basic machine hardware */
	I8085A(config, m_maincpu, XTAL(4'915'200)); // divider unknown
	m_maincpu->set_addrmap(AS_PROGRAM, &tim100_state::mem_xfer_map);
	m_maincpu->set_addrmap(AS_OPCODES, &tim100_state::mem_map);
	m_maincpu->out_sod_func().set(FUNC(tim100_state::sod_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_screen_update("crtc", FUNC(i8276_device::screen_update));
	screen.set_raw(9'600'000, 600, 0, 480, 320, 0, 272);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_tim100);

	I8276(config, m_crtc, 800'000);
	m_crtc->set_character_width(12);
	m_crtc->set_display_callback(FUNC(tim100_state::crtc_display_pixels));
	m_crtc->drq_wr_callback().set_inputline(m_maincpu, I8085_RST75_LINE);
	m_crtc->irq_wr_callback().set_inputline(m_maincpu, I8085_RST65_LINE);
	m_crtc->set_screen("screen");

	PALETTE(config, m_palette).set_entries(3);

	i8251_device &uart_u17(I8251(config, "uart_u17", 0));
	uart_u17.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	uart_u17.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	uart_u17.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	uart_u17.rxrdy_handler().set_inputline(m_maincpu, I8085_INTR_LINE);

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "keyboard"));
	rs232.rxd_handler().set("uart_u17", FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set("uart_u17", FUNC(i8251_device::write_dsr));
	rs232.cts_handler().set("uart_u17", FUNC(i8251_device::write_cts));
	rs232.set_option_device_input_defaults("keyboard", DEVICE_INPUT_DEFAULTS_NAME(keyboard));

	i8251_device &uart_u18(I8251(config, "uart_u18", 0));
	uart_u18.txd_handler().set("rs232a", FUNC(rs232_port_device::write_txd));
	uart_u18.dtr_handler().set("rs232a", FUNC(rs232_port_device::write_dtr));
	uart_u18.rts_handler().set("rs232a", FUNC(rs232_port_device::write_rts));
	uart_u18.rxrdy_handler().set_inputline(m_maincpu, I8085_RST55_LINE);

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, "loopback"));
	rs232a.rxd_handler().set("uart_u18", FUNC(i8251_device::write_rxd));
	rs232a.dsr_handler().set("uart_u18", FUNC(i8251_device::write_dsr));
	rs232a.cts_handler().set("uart_u18", FUNC(i8251_device::write_cts));
	rs232a.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	clock_device &uart_clock(CLOCK(config, "uart_clock", 153'600));
	uart_clock.signal_handler().set("uart_u17", FUNC(i8251_device::write_txc));
	uart_clock.signal_handler().append("uart_u17", FUNC(i8251_device::write_rxc));
	uart_clock.signal_handler().append("uart_u18", FUNC(i8251_device::write_txc));
	uart_clock.signal_handler().append("uart_u18", FUNC(i8251_device::write_rxc));
}

/* ROM definition */
ROM_START( tim100 )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "tim 100 v.3.2.0.u16",   0x0000, 0x2000, CRC(4de9c8ad) SHA1(b0914d6e8d618e92a87b4b39c35391541251e8cc))

	// The first and 2nd halves of these roms are identical, confirmed ok
	ROM_REGION( 0x2000, "chargen", ROMREGION_INVERT )
	ROM_SYSTEM_BIOS( 0, "212", "v 2.1.2" )
	ROMX_LOAD( "tim 100kg v.2.1.2.u12", 0x0000, 0x0800, CRC(faf5743c) SHA1(310b662e9535878210f8aaab3e2b846fade60642), ROM_BIOS(0))
	ROM_CONTINUE (0x1000, 0x0800)
	ROM_CONTINUE (0x0800, 0x0800)
	ROM_CONTINUE (0x1800, 0x0800)
	ROM_SYSTEM_BIOS( 1, "220", "v 2.2.0" )
	ROMX_LOAD( "tim 100kg v.2.2.0.u12", 0x0000, 0x0800, CRC(358dbbd3) SHA1(14b7d6ee41b19bedf2f070f5b28b03aaff2cac4f), ROM_BIOS(1))
	ROM_CONTINUE (0x1000, 0x0800)
	ROM_CONTINUE (0x0800, 0x0800)
	ROM_CONTINUE (0x1800, 0x0800)
ROM_END

} // Anonymous namespace

COMP( 1985, tim100, 0, 0, tim100, tim100, tim100_state, empty_init, "Mihajlo Pupin Institute", "TIM-100", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



timex.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Kevin Thacker
/***************************************************************************

    NOTE: ****** Specbusy: press N, R, or E to boot *************


        Spectrum/Inves/TK90X etc. memory map:

    CPU:
        0000-3fff ROM
        4000-ffff RAM

        Spectrum 128/+2/+2a/+3 memory map:

        CPU:
                0000-3fff Banked ROM/RAM (banked rom only on 128/+2)
                4000-7fff Banked RAM
                8000-bfff Banked RAM
                c000-ffff Banked RAM

        TS2068 memory map: (Can't have both EXROM and DOCK active)
        The 8K EXROM can be loaded into multiple pages.

    CPU:
                0000-1fff     ROM / EXROM / DOCK (Cartridge)
                2000-3fff     ROM / EXROM / DOCK
                4000-5fff \
                6000-7fff  \
                8000-9fff  |- RAM / EXROM / DOCK
                a000-bfff  |
                c000-dfff  /
                e000-ffff /


Interrupts:

Changes:

29/1/2000   KT -    Implemented initial +3 emulation.
30/1/2000   KT -    Improved input port decoding for reading and therefore
            correct keyboard handling for Spectrum and +3.
31/1/2000   KT -    Implemented buzzer sound for Spectrum and +3.
            Implementation copied from Paul Daniel's Jupiter driver.
            Fixed screen display problems with dirty chars.
            Added support to load .Z80 snapshots. 48k support so far.
13/2/2000   KT -    Added Interface II, Kempston, Fuller and Mikrogen
            joystick support.
17/2/2000   DJR -   Added full key descriptions and Spectrum+ keys.
            Fixed Spectrum +3 keyboard problems.
17/2/2000   KT -    Added tape loading from WAV/Changed from DAC to generic
            speaker code.
18/2/2000   KT -    Added tape saving to WAV.
27/2/2000   KT -    Took DJR's changes and added my changes.
27/2/2000   KT -    Added disk image support to Spectrum +3 driver.
27/2/2000   KT -    Added joystick I/O code to the Spectrum +3 I/O handler.
14/3/2000   DJR -   Tape handling dipswitch.
26/3/2000   DJR -   Snapshot files are now classified as snapshots not
            cartridges.
04/4/2000   DJR -   Spectrum 128 / +2 Support.
13/4/2000   DJR -   +4 Support (unofficial 48K hack).
13/4/2000   DJR -   +2a Support (rom also used in +3 models).
13/4/2000   DJR -   TK90X, TK95 and Inves support (48K clones).
21/4/2000   DJR -   TS2068 and TC2048 support (TC2048 Supports extra video
            modes but doesn't have bank switching or sound chip).
09/5/2000   DJR -   Spectrum +2 (France, Spain), +3 (Spain).
17/5/2000   DJR -   Dipswitch to enable/disable disk drives on +3 and clones.
27/6/2000   DJR -   Changed 128K/+3 port decoding (sound now works in Zub 128K).
06/8/2000   DJR -   Fixed +3 Floppy support
10/2/2001   KT  -   Re-arranged code and split into each model emulated.
            Code is split into 48k, 128k, +3, tc2048 and ts2048
            segments. 128k uses some of the functions in 48k, +3
            uses some functions in 128, and tc2048/ts2048 use some
            of the functions in 48k. The code has been arranged so
            these functions come in some kind of "override" order,
            read functions changed to use  READ8_HANDLER and write
            functions changed to use WRITE8_HANDLER.
            Added Scorpion256 preliminary.
18/6/2001   DJR -   Added support for Interface 2 cartridges.
xx/xx/2001  KS -    TS-2068 sound fixed.
            Added support for DOCK cartridges for TS-2068.
            Added Spectrum 48k Psycho modified rom driver.
            Added UK-2086 driver.
23/12/2001  KS -    48k machines are now able to run code in screen memory.
                Programs which keep their code in screen memory
                like monitors, tape copiers, decrunchers, etc.
                works now.
                Fixed problem with interrupt vector set to 0xffff (much
            more 128k games works now).
                A useful used trick on the Spectrum is to set
                interrupt vector to 0xffff (using the table
                which contain 0xff's) and put a byte 0x18 hex,
                the opcode for JR, at this address. The first
                byte of the ROM is a 0xf3 (DI), so the JR will
                jump to 0xfff4, where a long JP to the actual
                interrupt routine is put. Due to unideal
                bankswitching in MAME this JP were to 0001 what
                causes Spectrum to reset. Fixing this problem
                made much more software running (i.e. Paperboy).
            Corrected frames per second value for 48k and 128k
            Sinclair machines.
                There are 50.08 frames per second for Spectrum
                48k what gives 69888 cycles for each frame and
                50.021 for Spectrum 128/+2/+2A/+3 what gives
                70908 cycles for each frame.
            Remapped some Spectrum+ keys.
                Pressing F3 to reset was setting 0xf7 on keyboard
                input port. Problem occurred for snapshots of
                some programs where it was read as pressing
                key 4 (which is exit in Tapecopy by R. Dannhoefer
                for example).
            Added support to load .SP snapshots.
            Added .BLK tape images support.
                .BLK files are identical to .TAP ones, extension
                is an only difference.
08/03/2002  KS -    #FF port emulation added.
                Arkanoid works now, but is not playable due to
                completely messed timings.

Initialisation values used when determining which model is being emulated:
 48K        Spectrum doesn't use either port.
 128K/+2    Bank switches with port 7ffd only.
 +3/+2a     Bank switches with both ports.

Notes:
 1. No contented memory.
 2. No hi-res colour effects (need contended memory first for accurate timing).
 3. Multiface 1 and Interface 1 not supported.
 4. Horace and the Spiders cartridge doesn't run properly.
 5. Tape images not supported:
    .TZX, .SPC, .ITM, .PAN, .TAP(Warajevo), .VOC, .ZXS.
 6. Snapshot images not supported:
    .ACH, .PRG, .RAW, .SEM, .SIT, .SNX, .ZX, .ZXS, .ZX82.
 7. 128K emulation is not perfect - the 128K machines crash and hang while
    running quite a lot of games.
 8. Disk errors occur on some +3 games.
 9. Video hardware of all machines is timed incorrectly.
10. EXROM and HOME cartridges are not emulated.
11. The TK90X and TK95 roms output 0 to port #df on start up.
12. The purpose of this port is unknown (probably display mode as TS2068) and
    thus is not emulated.

Very detailed infos about the ZX Spectrum +3e can be found at

http://www.z88forever.org.uk/zxplus3e/

*******************************************************************************/

#include "emu.h"
#include "timex.h"

#include "bus/spectrum/ay/slot.h"
#include "beta_m.h"
#include "cpu/z80/z80.h"

#include "screen.h"
#include "softlist_dev.h"

#include "formats/tzx_cas.h"


//**************************************************************************************************
// TS2048 specific functions


u8 ts2068_state::port_f4_r()
{
	return m_port_f4_data;
}

void ts2068_state::port_f4_w(u8 data)
{
	m_port_f4_data = data;
	ts2068_update_memory();
}

u8 tc2048_state::port_ff_r()
{
	return m_port_ff_data;
}

void ts2068_state::port_ff_w(offs_t offset, u8 data)
{
		/* Bits 0-2 Video Mode Select
		   Bits 3-5 64 column mode ink/paper selection
		            (See ts2068_vh_screenrefresh for more info)
		   Bit  6   17ms Interrupt Inhibit
		   Bit  7   Cartridge (0) / EXROM (1) select
		*/
	m_port_ff_data = data;
	ts2068_update_memory();
	logerror("Port %04x write %02x\n", offset, data);
}

/*******************************************************************
 *
 *      Bank switch between the 3 internal memory banks HOME, EXROM
 *      and DOCK (Cartridges). The HOME bank contains 16K ROM in the
 *      0-16K area and 48K RAM fills the rest. The EXROM contains 8K
 *      ROM and can appear in every 8K segment (ie 0-8K, 8-16K etc).
 *      The DOCK is empty and is meant to be occupied by cartridges
 *      you can plug into the cartridge dock of the 2068.
 *
 *      The address space is divided into 8 8K chunks. Bit 0 of port
 *      #f4 corresponds to the 0-8K chunk, bit 1 to the 8-16K chunk
 *      etc. If the bit is 0 then the chunk is controlled by the HOME
 *      bank. If the bit is 1 then the chunk is controlled by either
 *      the DOCK or EXROM depending on bit 7 of port #ff. Note this
 *      means that the Z80 can't see chunks of the EXROM and DOCK
 *      at the same time.
 *
 *******************************************************************/
void ts2068_state::ts2068_update_memory()
{
	uint8_t *messram = nullptr;
	if (m_ram) messram = m_ram->pointer();
	address_space &space = m_maincpu->space(AS_PROGRAM);
	uint8_t *DOCK = nullptr;
	if (m_dock_crt) DOCK = m_dock_crt->base();


	uint8_t *ExROM = memregion("maincpu")->base() + 0x014000;
	uint8_t *ChosenROM = nullptr;

	if (m_port_f4_data & 0x01)
	{
		if (m_port_ff_data & 0x80)
		{
				space.install_read_bank(0x0000, 0x1fff, membank("bank1"));
				space.unmap_write(0x0000, 0x1fff);
				membank("bank1")->set_base(ExROM);
				logerror("0000-1fff EXROM\n");
		}
		else
		{
			if (m_dock_cart_type == TIMEX_CART_DOCK)
			{
				membank("bank1")->set_base(DOCK);
				space.install_read_bank(0x0000, 0x1fff, membank("bank1"));
				if (m_ram_chunks & 0x01)
					space.install_write_bank(0x0000, 0x1fff, membank("bank9"));
				else
					space.unmap_write(0x0000, 0x1fff);


			}
			else
			{
				space.nop_read(0x0000, 0x1fff);
				space.unmap_write(0x0000, 0x1fff);
			}
			logerror("0000-1fff Cartridge\n");
		}
	}
	else
	{
		ChosenROM = memregion("maincpu")->base() + 0x010000;
		membank("bank1")->set_base(ChosenROM);
		space.install_read_bank(0x0000, 0x1fff, membank("bank1"));
		space.unmap_write(0x0000, 0x1fff);
		logerror("0000-1fff HOME\n");
	}

	if (m_port_f4_data & 0x02)
	{
		if (m_port_ff_data & 0x80)
		{
			membank("bank2")->set_base(ExROM);
			space.install_read_bank(0x2000, 0x3fff, membank("bank2"));
			space.unmap_write(0x2000, 0x3fff);
			logerror("2000-3fff EXROM\n");
		}
		else
		{
			if (m_dock_cart_type == TIMEX_CART_DOCK)
			{
				membank("bank2")->set_base(DOCK+0x2000);
				space.install_read_bank(0x2000, 0x3fff, membank("bank2"));
				if (m_ram_chunks & 0x02)
					space.install_write_bank(0x2000, 0x3fff, membank("bank10"));
				else
					space.unmap_write(0x2000, 0x3fff);

			}
			else
			{
				space.nop_read(0x2000, 0x3fff);
				space.unmap_write(0x2000, 0x3fff);
			}
			logerror("2000-3fff Cartridge\n");
		}
	}
	else
	{
		ChosenROM = memregion("maincpu")->base() + 0x012000;
		membank("bank2")->set_base(ChosenROM);
		space.install_read_bank(0x2000, 0x3fff, membank("bank2"));
		space.unmap_write(0x2000, 0x3fff);
		logerror("2000-3fff HOME\n");
	}

	if (m_port_f4_data & 0x04)
	{
		if (m_port_ff_data & 0x80)
		{
			membank("bank3")->set_base(ExROM);
			space.install_read_bank(0x4000, 0x5fff, membank("bank3"));
			space.unmap_write(0x4000, 0x5fff);
			logerror("4000-5fff EXROM\n");
		}
		else
		{
			if (m_dock_cart_type == TIMEX_CART_DOCK)
			{
				membank("bank3")->set_base(DOCK+0x4000);
				space.install_read_bank(0x4000, 0x5fff, membank("bank3"));
				if (m_ram_chunks & 0x04)
					space.install_write_bank(0x4000, 0x5fff, membank("bank11"));
				else
					space.unmap_write(0x4000, 0x5fff);
			}
			else
			{
				space.nop_read(0x4000, 0x5fff);
				space.unmap_write(0x4000, 0x5fff);
			}
			logerror("4000-5fff Cartridge\n");
		}
	}
	else
	{
		membank("bank3")->set_base(messram);
		membank("bank11")->set_base(messram);
		space.install_read_bank(0x4000, 0x5fff, membank("bank3"));
		space.install_write_bank(0x4000, 0x5fff, membank("bank11"));
		logerror("4000-5fff RAM\n");
	}

	if (m_port_f4_data & 0x08)
	{
		if (m_port_ff_data & 0x80)
		{
			membank("bank4")->set_base(ExROM);
			space.install_read_bank(0x6000, 0x7fff, membank("bank4"));
			space.unmap_write(0x6000, 0x7fff);
			logerror("6000-7fff EXROM\n");
		}
		else
		{
				if (m_dock_cart_type == TIMEX_CART_DOCK)
				{
					membank("bank4")->set_base(DOCK+0x6000);
					space.install_read_bank(0x6000, 0x7fff, membank("bank4"));
					if (m_ram_chunks & 0x08)
						space.install_write_bank(0x6000, 0x7fff, membank("bank12"));
					else
						space.unmap_write(0x6000, 0x7fff);
				}
				else
				{
					space.nop_read(0x6000, 0x7fff);
					space.unmap_write(0x6000, 0x7fff);
				}
				logerror("6000-7fff Cartridge\n");
		}
	}
	else
	{
		membank("bank4")->set_base(messram + 0x2000);
		membank("bank12")->set_base(messram + 0x2000);
		space.install_read_bank(0x6000, 0x7fff, membank("bank4"));
		space.install_write_bank(0x6000, 0x7fff, membank("bank12"));
		logerror("6000-7fff RAM\n");
	}

	if (m_port_f4_data & 0x10)
	{
		if (m_port_ff_data & 0x80)
		{
			membank("bank5")->set_base(ExROM);
			space.install_read_bank(0x8000, 0x9fff, membank("bank5"));
			space.unmap_write(0x8000, 0x9fff);
			logerror("8000-9fff EXROM\n");
		}
		else
		{
			if (m_dock_cart_type == TIMEX_CART_DOCK)
			{
				membank("bank5")->set_base(DOCK+0x8000);
				space.install_read_bank(0x8000, 0x9fff,membank("bank5"));
				if (m_ram_chunks & 0x10)
					space.install_write_bank(0x8000, 0x9fff,membank("bank13"));
				else
					space.unmap_write(0x8000, 0x9fff);
			}
			else
			{
				space.nop_read(0x8000, 0x9fff);
				space.unmap_write(0x8000, 0x9fff);
			}
			logerror("8000-9fff Cartridge\n");
		}
	}
	else
	{
		membank("bank5")->set_base(messram + 0x4000);
		membank("bank13")->set_base(messram + 0x4000);
		space.install_read_bank(0x8000, 0x9fff,membank("bank5"));
		space.install_write_bank(0x8000, 0x9fff,membank("bank13"));
		logerror("8000-9fff RAM\n");
	}

	if (m_port_f4_data & 0x20)
	{
		if (m_port_ff_data & 0x80)
		{
			membank("bank6")->set_base(ExROM);
			space.install_read_bank(0xa000, 0xbfff, membank("bank6"));
			space.unmap_write(0xa000, 0xbfff);
			logerror("a000-bfff EXROM\n");
		}
		else
		{
			if (m_dock_cart_type == TIMEX_CART_DOCK)
			{
				membank("bank6")->set_base(DOCK+0xa000);
				space.install_read_bank(0xa000, 0xbfff, membank("bank6"));
				if (m_ram_chunks & 0x20)
					space.install_write_bank(0xa000, 0xbfff, membank("bank14"));
				else
					space.unmap_write(0xa000, 0xbfff);

			}
			else
			{
				space.nop_read(0xa000, 0xbfff);
				space.unmap_write(0xa000, 0xbfff);
			}
			logerror("a000-bfff Cartridge\n");
		}
	}
	else
	{
		membank("bank6")->set_base(messram + 0x6000);
		membank("bank14")->set_base(messram + 0x6000);
		space.install_read_bank(0xa000, 0xbfff, membank("bank6"));
		space.install_write_bank(0xa000, 0xbfff, membank("bank14"));
		logerror("a000-bfff RAM\n");
	}

	if (m_port_f4_data & 0x40)
	{
		if (m_port_ff_data & 0x80)
		{
			membank("bank7")->set_base(ExROM);
			space.install_read_bank(0xc000, 0xdfff, membank("bank7"));
			space.unmap_write(0xc000, 0xdfff);
			logerror("c000-dfff EXROM\n");
		}
		else
		{
			if (m_dock_cart_type == TIMEX_CART_DOCK)
			{
				membank("bank7")->set_base(DOCK+0xc000);
				space.install_read_bank(0xc000, 0xdfff, membank("bank7"));
				if (m_ram_chunks & 0x40)
					space.install_write_bank(0xc000, 0xdfff, membank("bank15"));
				else
					space.unmap_write(0xc000, 0xdfff);
			}
			else
			{
				space.nop_read(0xc000, 0xdfff);
				space.unmap_write(0xc000, 0xdfff);
			}
			logerror("c000-dfff Cartridge\n");
		}
	}
	else
	{
		membank("bank7")->set_base(messram + 0x8000);
		membank("bank15")->set_base(messram + 0x8000);
		space.install_read_bank(0xc000, 0xdfff, membank("bank7"));
		space.install_write_bank(0xc000, 0xdfff, membank("bank15"));
		logerror("c000-dfff RAM\n");
	}

	if (m_port_f4_data & 0x80)
	{
		if (m_port_ff_data & 0x80)
		{
			membank("bank8")->set_base(ExROM);
			space.install_read_bank(0xe000, 0xffff, membank("bank8"));
			space.unmap_write(0xe000, 0xffff);
			logerror("e000-ffff EXROM\n");
		}
		else
		{
			if (m_dock_cart_type == TIMEX_CART_DOCK)
			{
				membank("bank8")->set_base(DOCK+0xe000);
				space.install_read_bank(0xe000, 0xffff, membank("bank8"));
				if (m_ram_chunks & 0x80)
					space.install_write_bank(0xe000, 0xffff, membank("bank16"));
				else
					space.unmap_write(0xe000, 0xffff);
			}
			else
			{
				space.nop_read(0xe000, 0xffff);
				space.unmap_write(0xe000, 0xffff);
			}
			logerror("e000-ffff Cartridge\n");
		}
	}
	else
	{
		membank("bank8")->set_base(messram + 0xa000);
		membank("bank16")->set_base(messram + 0xa000);
		space.install_read_bank(0xe000, 0xffff, membank("bank8"));
		space.install_write_bank(0xe000, 0xffff, membank("bank16"));
		logerror("e000-ffff RAM\n");
	}
}

void ts2068_state::ts2068_io(address_map &map)
{
	map(0xf4, 0xf4).mirror(0xff00).rw(FUNC(ts2068_state::port_f4_r), FUNC(ts2068_state::port_f4_w));
	map(0xf5, 0xf5).mirror(0xff00).w("ay_slot", FUNC(ay_slot_device::address_w));
	map(0xf6, 0xf6).mirror(0xff00).rw("ay_slot", FUNC(ay_slot_device::data_r), FUNC(ay_slot_device::data_w));
	map(0xfe, 0xfe).select(0xff00).rw(FUNC(ts2068_state::spectrum_ula_r), FUNC(ts2068_state::spectrum_ula_w));
	map(0xff, 0xff).mirror(0xff00).rw(FUNC(ts2068_state::port_ff_r), FUNC(ts2068_state::port_ff_w));
}

void ts2068_state::ts2068_mem(address_map &map)
{
	map(0x0000, 0x1fff).bankr("bank1").bankw("bank9");
	map(0x2000, 0x3fff).bankr("bank2").bankw("bank10");
	map(0x4000, 0x5fff).bankr("bank3").bankw("bank11");
	map(0x6000, 0x7fff).bankr("bank4").bankw("bank12");
	map(0x8000, 0x9fff).bankr("bank5").bankw("bank13");
	map(0xa000, 0xbfff).bankr("bank6").bankw("bank14");
	map(0xc000, 0xdfff).bankr("bank7").bankw("bank15");
	map(0xe000, 0xffff).bankr("bank8").bankw("bank16");
}


void ts2068_state::machine_reset()
{
	m_port_ff_data = 0;
	m_port_f4_data = 0;

	std::string region_tag;
	m_dock_crt = memregion(region_tag.assign(m_dock->tag()).append(GENERIC_ROM_REGION_TAG).c_str());
	m_dock_cart_type = m_dock_crt ? TIMEX_CART_DOCK : TIMEX_CART_NONE;

	ts2068_update_memory();
	spectrum_state::machine_reset();
}


//**************************************************************************************************
// TC2048 specific functions


void tc2048_state::port_ff_w(offs_t offset, uint8_t data)
{
	m_port_ff_data = data;
	logerror("Port %04x write %02x\n", offset, data);
}

void tc2048_state::tc2048_io(address_map &map)
{
	map(0x00, 0x00).select(0xfffe).rw(FUNC(tc2048_state::spectrum_ula_r), FUNC(tc2048_state::spectrum_ula_w));
	map(0xff, 0xff).mirror(0xff00).rw(FUNC(tc2048_state::port_ff_r), FUNC(tc2048_state::port_ff_w));
}

void tc2048_state::tc2048_mem(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	map(0x4000, 0xffff).bankr("bank1").bankw("bank2");
}

void tc2048_state::machine_reset()
{
	uint8_t *messram = m_ram->pointer();

	membank("bank1")->set_base(messram);
	membank("bank2")->set_base(messram);
	m_port_ff_data = 0;
	m_port_f4_data = -1;
	spectrum_state::machine_reset();
}


DEVICE_IMAGE_LOAD_MEMBER( ts2068_state::cart_load )
{
	uint32_t size = m_dock->common_get_size("rom");

	if (!image.loaded_through_softlist())
	{
		std::vector<uint8_t> header;
		header.resize(9);

		if (size % 0x2000 != 9)
			return std::make_pair(image_error::INVALIDLENGTH, "Unsupported cartridge data size (must be a multiple of 8K)");

		m_dock->rom_alloc(0x10000, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
		u8* DOCK = m_dock->get_rom_base();

		// check header
		image.fread(&header[0], 9);

		int chunks_in_file = 0;
		for (int i = 0; i < 8; i++)
			if (header[i + 1] & 0x02) chunks_in_file++;

		if (chunks_in_file * 0x2000 + 0x09 != size)
		{
			return std::make_pair(
					image_error::INVALIDIMAGE,
					util::string_format("Chunks in header (%d) do not match data size (%d)", chunks_in_file, (size - 0x09) / 0x2000));
		}

		switch (header[0])
		{
			case 0x00:  logerror ("DOCK cart\n");
				m_ram_chunks = 0;
				for (int i = 0; i < 8; i++)
				{
					m_ram_chunks = m_ram_chunks | ((header[i + 1] & 0x01) << i);
					if (header[i + 1] & 0x02)
						image.fread(DOCK + i * 0x2000, 0x2000);
					else
					{
						if (header[i + 1] & 0x01)
							memset(DOCK + i * 0x2000, 0x00, 0x2000);
						else
							memset(DOCK + i * 0x2000, 0xff, 0x2000);
					}
				}
				break;

			default:
				return std::make_pair(
						image_error::INVALIDIMAGE,
						util::string_format("Cartridge type 0x%02X not supported", header[0]));
		}

		logerror ("Cart loaded [Chunks %02x]\n", m_ram_chunks);
	}
	else
	{
		m_dock->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
		memcpy(m_dock->get_rom_base(), image.get_software_region("rom"), size);
	}

	return std::make_pair(std::error_condition(), std::string());
}


// F4 Character Displayer - tc2048 code is inherited from the spectrum
static const gfx_layout ts2068_charlayout =
{
	8, 8,                   // 8 x 8 characters
	96,                 // 96 characters
	1,                  // 1 bits per pixel
	{ 0 },                  // no bitplanes
	// x offsets
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	// y offsets
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 // every char takes 8 bytes
};

static GFXDECODE_START( gfx_ts2068 )
	GFXDECODE_ENTRY( "maincpu", 0x13d00, ts2068_charlayout, 0, 8 )
GFXDECODE_END

void ts2068_state::ts2068(machine_config &config)
{
	spectrum_128(config);

	config.device_remove("dma");

	Z80(config.replace(), m_maincpu, XTAL(14'112'000) / 4); // From Schematic; 3.528 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &ts2068_state::ts2068_mem);
	m_maincpu->set_addrmap(AS_IO, &ts2068_state::ts2068_io);
	m_maincpu->set_vblank_int("screen", FUNC(ts2068_state::spec_interrupt));
	config.set_maximum_quantum(attotime::from_hz(60));

	// video hardware
	// timings not confirmed! now same as spec128 but doubled for hires
	m_screen->set_raw(XTAL(14'112'000) / 2, 456 * 2, 311, {get_screen_area().left() - TS2068_LEFT_BORDER, get_screen_area().right() + TS2068_RIGHT_BORDER, get_screen_area().top() - TS2068_TOP_BORDER, get_screen_area().bottom() + TS2068_BOTTOM_BORDER});
	m_screen->set_refresh_hz(60);

	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_ts2068);

	// sound
	AY_SLOT(config.replace(), "ay_slot", XTAL(14'112'000) / 8, default_ay_slot_devices, "ay_ay8912") // From Schematic; 1.764 MHz
		.add_route(ALL_OUTPUTS, "speakers", 0.25);

	// cartridge
	GENERIC_CARTSLOT(config, "dockslot", generic_plain_slot, "timex_cart", "dck,bin").set_device_load(FUNC(ts2068_state::cart_load));

	// Software lists
	SOFTWARE_LIST(config, "cart_list").set_original("timex_dock");
	SOFTWARE_LIST(config, "cass_list_t").set_original("timex_cass");

	// internal ram
	m_ram->set_default_size("48K");
}


void ts2068_state::uk2086(machine_config &config)
{
	ts2068(config);
	m_screen->set_refresh_hz(50);
}

rectangle tc2048_state::get_screen_area() {
	return {TS2068_LEFT_BORDER, TS2068_LEFT_BORDER + TS2068_DISPLAY_XSIZE - 1, TS2068_TOP_BORDER, TS2068_TOP_BORDER + SPEC_DISPLAY_YSIZE - 1};
}

void tc2048_state::tc2048(machine_config &config)
{
	spectrum(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &tc2048_state::tc2048_mem);
	m_maincpu->set_addrmap(AS_IO, &tc2048_state::tc2048_io);

	// video hardware
	// timings not confirmed! now same as spec48 but doubled for hires
	m_screen->set_raw(X1 / 2, 448 * 2, 312, {get_screen_area().left() - TS2068_LEFT_BORDER, get_screen_area().right() + TS2068_RIGHT_BORDER, get_screen_area().top() - TS2068_TOP_BORDER, get_screen_area().bottom() + TS2068_BOTTOM_BORDER});
	m_screen->set_refresh_hz(50);

	// internal ram
	m_ram->set_default_size("48K");

	// Software lists
	SOFTWARE_LIST(config, "cass_list_t").set_original("timex_cass");
}



/***************************************************************************

  Game driver(s)

***************************************************************************/

ROM_START(tc2048)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("tc2048.rom",0x0000,0x4000, CRC(f1b5fa67) SHA1(febb2d495b6eda7cdcb4074935d6e9d9f328972d))
ROM_END

ROM_START(ts2068)
	ROM_REGION(0x16000,"maincpu",0)
	ROM_LOAD("ts2068_h.rom",0x10000,0x4000, CRC(bf44ec3f) SHA1(1446cb2780a9dedf640404a639fa3ae518b2d8aa))
	ROM_LOAD("ts2068_x.rom",0x14000,0x2000, CRC(ae16233a) SHA1(7e265a2c1f621ed365ea23bdcafdedbc79c1299c))
ROM_END

ROM_START(uk2086)
	ROM_REGION(0x16000,"maincpu",0)
	ROM_LOAD("uk2086_h.rom",0x10000,0x4000, CRC(5ddc0ca2) SHA1(1d525fe5cdc82ab46767f665ad735eb5363f1f51))
	ROM_LOAD("ts2068_x.rom",0x14000,0x2000, CRC(ae16233a) SHA1(7e265a2c1f621ed365ea23bdcafdedbc79c1299c))
ROM_END

//    YEAR  NAME    PARENT    COMPAT  MACHINE  INPUT     CLASS        INIT        COMPANY              FULLNAME             FLAGS
COMP( 1984, tc2048, spectrum, 0,      tc2048,  spectrum, tc2048_state, empty_init, "Timex of Portugal", "TC-2048" ,          0 )
COMP( 1983, ts2068, spectrum, 0,      ts2068,  spectrum, ts2068_state, empty_init, "Timex Sinclair",    "TS-2068" ,          0 )
COMP( 1986, uk2086, spectrum, 0,      uk2086,  spectrum, ts2068_state, empty_init, "Unipolbrit",        "UK-2086 ver. 1.2" , 0 )



titan_soc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/************************************************************************

    Titan 1.0C (System on a Chip - ARM based processor)

    used by

    Atari Flashback (not dumped)
    Colecovision Flashback
    Intellivision Flashback (not dumped)
    TecToy Mega Drive 4


    Notes:

    It is possible to connect a debug terminal
    Has a USB port for user to plug in a flash drive etc.
    4MB RAM

    Emulators run on the ARM, games don't use some modes 100% correctly compared to original
    hardware, only correct for the included emulator.  Some games are not emulation based.


    Colecovision Flashback, AtGames, 2014
    Hardware info by Guru
    ---------------------

    This is a plug & play mini console made in China that looks a bit like the real Colecovision
    console which has 60 built-in Colecovision games. There is another version with 61 games included.
    In the dumped ROM, plain text shows what appears to be an OS called 'Titan' and the Colecovision
    ROMs appear to be the same files dumped for emulators (.cv) then gzipped so the ROM files are
    compressed *.cv.gz
    The ROM is not full and has some empty space at 0x1a56e0 to the end (approx. 370kB).
    There is a fixed A/V cable attached with 2 RCA plugs for composite video and mono audio.
    Power input is 5VDC 500mA via a 5.5mm barrel jack. Regulators on the PCB make 3.3V and 1.8V.
    On top of the console are 2 buttons for reset (when in-game this jumps back to the menu)
    and power (toggle on/off).
    The main CPU/SOC is an epoxy-blob and is likely one of the very common ARM-based SOCs used in other
    cheap Chinese mini plug & play or x-in-1 retro-gaming consoles.

    PCB Layout
    ----------

    IN460_MAIN_V2.0_20140510
    |--------------------------------|
    |VA  1.8V                  ROM.U4|
    |3.3V              M12L16161A    |
    |G5R       BLOB                  |
    |   27MHz                        |
    |PAD1                        PAD2|
    |--------------------------------|
    Notes:
          M12L16161A - Elite Semiconductor Memory Technology Inc. M12L16161A 512kB x16bit x 2banks 3.3V Synchronous DRAM
                BLOB - Epoxy blob 1.8V System-On-a-Chip. This is likely one of the common Arm-based Chinese SOCs used in
                       many other mini-consoles. Clock input is 27.000MHz
              PAD1/2 - DB9 connectors for Colecovision look-alike joysticks
              ROM.U4 - SST SST39VF1602 1Mb x16bit CMOS Multi-Purpose 3.3V TSOP48 Flash ROM
                  VA - 3 Wires for ground, composite video and audio outputs
                 G5R - 3 Wires (GND, 5V, Reset) connecting to a small PCB containing DC Jack, power LED, ON/OFF toggle
                       switch and a reset momentary push button
*/

#include "emu.h"

#include "cpu/arm7/arm7.h"

#include "emupal.h"
#include "screen.h"


namespace {

class titan_soc_state : public driver_device
{
public:
	titan_soc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_mainram(*this, "mainram"),
		m_maincpu(*this, "maincpu")
	{ }

	void titan_soc(machine_config &config);

	void init_titan_soc();

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

private:
	required_shared_ptr<uint32_t> m_mainram;
	required_device<cpu_device> m_maincpu;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void map(address_map &map) ATTR_COLD;
};



void titan_soc_state::map(address_map &map)
{
	map(0x00000000, 0x0007ffff).ram().share("mainram");
}

static INPUT_PORTS_START( titan_soc )

INPUT_PORTS_END


void titan_soc_state::video_start()
{
}

uint32_t titan_soc_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void titan_soc_state::machine_reset()
{
}

void titan_soc_state::titan_soc(machine_config &config)
{
	/* basic machine hardware */
	ARM920T(config, m_maincpu, 200000000); // type + clock unknown
	m_maincpu->set_addrmap(AS_PROGRAM, &titan_soc_state::map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500) /* not accurate */);
	screen.set_size(320, 256);
	screen.set_visarea(0, 320-1, 0, 256-1);
	screen.set_screen_update(FUNC(titan_soc_state::screen_update));

	PALETTE(config, "palette").set_entries(256);
}



ROM_START( megadri4 )
	ROM_REGION( 0x80000, "serial", 0 ) // this was only dumped from one of the PCBS, not sure which one, might not be correct for both
	ROM_LOAD( "25l4005a - rom de boot megadrive4.bin",     0x000000, 0x80000, CRC(1b5c3c31) SHA1(97f301ae441ca23d3c52a901319e375654920867) )

	ROM_REGION( 0x08400000, "flash", 0 )
	ROM_LOAD( "ic9 megadrive4 titan.bin",     0x000000, 0x08400000, CRC(ed92b81a) SHA1(a3d51a2febf670820d6df009660b96ff6407f475) )
ROM_END

ROM_START( megadri4a )
	ROM_REGION( 0x80000, "serial", 0 ) // this was only dumped from one of the PCBS, not sure which one, might not be correct for both
	ROM_LOAD( "25l4005a - rom de boot megadrive4.bin",     0x000000, 0x80000, CRC(1b5c3c31) SHA1(97f301ae441ca23d3c52a901319e375654920867) )

	ROM_REGION( 0x08400000, "flash", 0 )
	ROM_LOAD( "ic9 megadrive4 titan segunda placa.bin",     0x000000, 0x08400000, CRC(4b423898) SHA1(293127d2f6169717a7fbfcf18f13e4b1735236f7) )
ROM_END

ROM_START( colecofl )
	ROM_REGION( 0x200000, "flash", 0 )
	ROM_LOAD( "colecovision_flashback_sst39vf1602c.u4", 0x000000, 0x200000, CRC(f78849a2) SHA1(d687b19dabcb404ae4787c798ae4cea69fa7acc7) )
ROM_END


void titan_soc_state::init_titan_soc()
{
	// can either run directly from serial ROM, or copies it to RAM on startup
	memcpy(m_mainram, memregion("serial")->base(), 0x80000);
}

} // anonymous namespace


CONS( 2009, megadri4,  0,        0, titan_soc, titan_soc, titan_soc_state, init_titan_soc, "Tectoy (licensed from Sega)", "Mega Drive 4 / Guitar Idol (set 1)",      MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2009, megadri4a, megadri4, 0, titan_soc, titan_soc, titan_soc_state, init_titan_soc, "Tectoy (licensed from Sega)", "Mega Drive 4 / Guitar Idol (set 2)",      MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
CONS( 2004, colecofl,  0,        0, titan_soc, titan_soc, titan_soc_state, empty_init,     "AtGames",                     "Colecovision Flashback",                  MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



tk2000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont, M. Capdeville
/***************************************************************************

    tk2000.cpp - Microdigital TK2000

    Driver by R. Belmont

    This system is only vaguely Apple II compatible.
    The keyboard works entirely differently, which is a big deal.

    TODO: emulate expansion connector (not wholly Apple II compatible)

    $C05A - banks RAM from c100-ffff
    $C05B - banks ROM from c100-ffff

    Added Multitech MPF-II support ( another not so apple2 compatible )

************************************************************************/

#include "emu.h"

#include "apple2video.h"

#include "cpu/m6502/m6502.h"
#include "bus/centronics/ctronics.h"
#include "imagedev/cassette.h"
#include "machine/74259.h"
#include "machine/bankdev.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "utf8.h"


namespace {

#define A2_CPU_TAG "maincpu"
#define A2_BUS_TAG "a2bus"
#define A2_SPEAKER_TAG "speaker"
#define A2_CASSETTE_TAG "tape"
#define A2_UPPERBANK_TAG "inhbank"
#define A2_VIDEO_TAG "a2video"

class tk2000_state : public driver_device
{
public:
	tk2000_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, A2_CPU_TAG),
		m_ram(*this, RAM_TAG),
		m_screen(*this, "screen"),
		m_video(*this, A2_VIDEO_TAG),
		m_row(*this, "ROW%u", 0U),
		m_kbspecial(*this, "keyb_special"),
		m_speaker(*this, A2_SPEAKER_TAG),
		m_cassette(*this, A2_CASSETTE_TAG),
		m_upperbank(*this, A2_UPPERBANK_TAG),
		m_softlatch(*this, "softlatch"),
		m_printer(*this, "printer")
	{ }

	void tk2000(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	required_device<screen_device> m_screen;
	required_device<a2_video_device_composite> m_video;
	required_ioport_array<8> m_row;
	required_ioport m_kbspecial;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cassette;
	required_device<address_map_bank_device> m_upperbank;
	required_device<addressable_latch_device> m_softlatch;
	required_device<centronics_device> m_printer;

	int m_speaker_state;
	int m_cassette_state;

	uint8_t m_kbout;

	uint8_t *m_ram_ptr;
	int m_ram_size;
	bool m_printer_busy;
	bool m_ctrl_key;

	TIMER_DEVICE_CALLBACK_MEMBER(apple2_interrupt);

	uint8_t ram_r(offs_t offset);
	void ram_w(offs_t offset, uint8_t data);

	void printer_busy_w(int state);
	void kbout_w(uint8_t data);
	uint8_t kbin_r();
	uint8_t casout_r();
	void casout_w(uint8_t data);
	uint8_t snd_r();
	void snd_w(uint8_t data);
	uint8_t switches_r(offs_t offset);
	uint8_t cassette_r();
	void color_w(int state);
	void motor_a_w(int state);
	void motor_b_w(int state);
	void rom_ram_w(int state);
	void ctrl_key_w(int state);
	uint8_t c080_r(offs_t offset);
	void c080_w(offs_t offset, uint8_t data);
	uint8_t c100_r(offs_t offset);
	void c100_w(offs_t offset, uint8_t data);

	void apple2_map(address_map &map) ATTR_COLD;
	void inhbank_map(address_map &map) ATTR_COLD;

	uint8_t read_floatingbus();
};

/***************************************************************************
    START/RESET
***************************************************************************/

void tk2000_state::machine_start()
{
	m_ram_ptr = m_ram->pointer();
	m_ram_size = m_ram->size();
	m_speaker_state = 0;
	m_speaker->level_w(m_speaker_state);
	m_cassette_state = 0;
	m_cassette->output(-1.0f);
	m_printer_busy = false;
	m_ctrl_key = false;

	// setup save states
	save_item(NAME(m_speaker_state));
	save_item(NAME(m_cassette_state));
	save_item(NAME(m_kbout));
	save_item(NAME(m_printer_busy));
	save_item(NAME(m_ctrl_key));

	// setup video pointers
	m_video->set_ram_pointers(m_ram_ptr, m_ram_ptr);
	m_video->set_char_pointer(nullptr, 0);  // no text modes on this machine
}

void tk2000_state::machine_reset()
{
	m_kbout = 0;
}

/***************************************************************************
    VIDEO
***************************************************************************/

TIMER_DEVICE_CALLBACK_MEMBER(tk2000_state::apple2_interrupt)
{
	int scanline = param;

	if((scanline % 8) == 0)
		m_screen->update_partial(m_screen->vpos());
}

/***************************************************************************
    I/O
***************************************************************************/

void tk2000_state::printer_busy_w(int state)
{
	m_printer_busy = state;
}

void tk2000_state::kbout_w(uint8_t data)
{
	// write row mask for keyboard scan
	m_kbout = data;

	m_printer->write_data0(BIT(data, 0));
	m_printer->write_data1(BIT(data, 1));
	m_printer->write_data2(BIT(data, 2));
	m_printer->write_data3(BIT(data, 3));
	m_printer->write_data4(BIT(data, 4));
	m_printer->write_data5(BIT(data, 5));
	m_printer->write_data6(BIT(data, 6));
	m_printer->write_data7(BIT(data, 7));
}

uint8_t tk2000_state::kbin_r()
{
	uint8_t kbin = 0;
	for (int i = 0; i < 8; i++)
		if (BIT(m_kbout, i))
			kbin |= m_row[i]->read();

	if (m_ctrl_key)
		kbin |= m_kbspecial->read();

	return kbin | (m_printer_busy ? 0x40 : 0);
}

uint8_t tk2000_state::casout_r()
{
	if (!machine().side_effects_disabled())
		casout_w(0);
	return read_floatingbus();
}

void tk2000_state::casout_w(uint8_t data)
{
	m_cassette_state ^= 1;
	m_cassette->output(m_cassette_state ? 1.0f : -1.0f);
}

uint8_t tk2000_state::snd_r()
{
	if (!machine().side_effects_disabled())
		snd_w(0);
	return read_floatingbus();
}

void tk2000_state::snd_w(uint8_t data)
{
	m_speaker_state ^= 1;
	m_speaker->level_w(m_speaker_state);
}

uint8_t tk2000_state::switches_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		m_softlatch->write_bit((offset & 0x0e) >> 1, offset & 0x01);
	return read_floatingbus();
}

uint8_t tk2000_state::cassette_r()
{
	return (m_cassette->input() > 0.0 ? 0x80 : 0) | (read_floatingbus() & 0x7f);
}

void tk2000_state::color_w(int state)
{
	// 0 = color, 1 = black/white
}

void tk2000_state::motor_a_w(int state)
{
}

void tk2000_state::motor_b_w(int state)
{
}

void tk2000_state::rom_ram_w(int state)
{
	// 0 = ROM, 1 = RAM
	m_upperbank->set_bank(state);
}

void tk2000_state::ctrl_key_w(int state)
{
	m_ctrl_key = state;
}

uint8_t tk2000_state::c080_r(offs_t offset)
{
	return read_floatingbus();
}

void tk2000_state::c080_w(offs_t offset, uint8_t data)
{
}

uint8_t tk2000_state::c100_r(offs_t offset)
{
	return m_ram_ptr[offset + 0xc100];
}

void tk2000_state::c100_w(offs_t offset, uint8_t data)
{
	m_ram_ptr[offset + 0xc100] = data;
}

// floating bus code from old machine/apple2: needs to be reworked based on real beam position to enable e.g. Bob Bishop's screen splitter
uint8_t tk2000_state::read_floatingbus()
{
	enum
	{
		// scanner types
		kScannerNone = 0, kScannerApple2, kScannerApple2e,

		// scanner constants
		kHBurstClock      =    53, // clock when Color Burst starts
		kHBurstClocks     =     4, // clocks per Color Burst duration
		kHClock0State     =  0x18, // H[543210] = 011000
		kHClocks          =    65, // clocks per horizontal scan (including HBL)
		kHPEClock         =    40, // clock when HPE (horizontal preset enable) goes low
		kHPresetClock     =    41, // clock when H state presets
		kHSyncClock       =    49, // clock when HSync starts
		kHSyncClocks      =     4, // clocks per HSync duration
		kNTSCScanLines    =   262, // total scan lines including VBL (NTSC)
		kNTSCVSyncLine    =   224, // line when VSync starts (NTSC)
		kPALScanLines     =   312, // total scan lines including VBL (PAL)
		kPALVSyncLine     =   264, // line when VSync starts (PAL)
		kVLine0State      = 0x100, // V[543210CBA] = 100000000
		kVPresetLine      =   256, // line when V state presets
		kVSyncLines       =     4, // lines per VSync duration
		kClocksPerVSync   = kHClocks * kNTSCScanLines // FIX: NTSC only?
	};

	// vars
	//
	int i, Hires, Mixed, Page2, _80Store, ScanLines, /* VSyncLine, ScanCycles,*/
		h_clock, h_state, h_0, h_1, h_2, h_3, h_4, h_5,
		v_line, v_state, v_A, v_B, v_C, v_0, v_1, v_2, v_3, v_4, /* v_5, */
		_hires, addend0, addend1, addend2, sum, address;

	// video scanner data
	//
	i = m_maincpu->total_cycles() % kClocksPerVSync; // cycles into this VSync

	// machine state switches
	//
	Hires    = 1;
	Mixed    = 0;
	Page2 = m_video->get_page2() ? 1 : 0;
	_80Store = 0;

	// calculate video parameters according to display standard
	//
	ScanLines  = 1 ? kNTSCScanLines : kPALScanLines; // FIX: NTSC only?
	// VSyncLine  = 1 ? kNTSCVSyncLine : kPALVSyncLine; // FIX: NTSC only?
	// ScanCycles = ScanLines * kHClocks;

	// calculate horizontal scanning state
	//
	h_clock = (i + kHPEClock) % kHClocks; // which horizontal scanning clock
	h_state = kHClock0State + h_clock; // H state bits
	if (h_clock >= kHPresetClock) // check for horizontal preset
	{
		h_state -= 1; // correct for state preset (two 0 states)
	}
	h_0 = (h_state >> 0) & 1; // get horizontal state bits
	h_1 = (h_state >> 1) & 1;
	h_2 = (h_state >> 2) & 1;
	h_3 = (h_state >> 3) & 1;
	h_4 = (h_state >> 4) & 1;
	h_5 = (h_state >> 5) & 1;

	// calculate vertical scanning state
	//
	v_line  = i / kHClocks; // which vertical scanning line
	v_state = kVLine0State + v_line; // V state bits
	if ((v_line >= kVPresetLine)) // check for previous vertical state preset
	{
		v_state -= ScanLines; // compensate for preset
	}
	v_A = (v_state >> 0) & 1; // get vertical state bits
	v_B = (v_state >> 1) & 1;
	v_C = (v_state >> 2) & 1;
	v_0 = (v_state >> 3) & 1;
	v_1 = (v_state >> 4) & 1;
	v_2 = (v_state >> 5) & 1;
	v_3 = (v_state >> 6) & 1;
	v_4 = (v_state >> 7) & 1;
	//v_5 = (v_state >> 8) & 1;

	// calculate scanning memory address
	//
	_hires = Hires;
	if (Hires && Mixed && (v_4 & v_2))
	{
		_hires = 0; // (address is in text memory)
	}

	addend0 = 0x68; // 1            1            0            1
	addend1 =              (h_5 << 5) | (h_4 << 4) | (h_3 << 3);
	addend2 = (v_4 << 6) | (v_3 << 5) | (v_4 << 4) | (v_3 << 3);
	sum     = (addend0 + addend1 + addend2) & (0x0F << 3);

	address = 0;
	address |= h_0 << 0; // a0
	address |= h_1 << 1; // a1
	address |= h_2 << 2; // a2
	address |= sum;      // a3 - aa6
	address |= v_0 << 7; // a7
	address |= v_1 << 8; // a8
	address |= v_2 << 9; // a9
	address |= ((_hires) ? v_A : (1 ^ (Page2 & (1 ^ _80Store)))) << 10; // a10
	address |= ((_hires) ? v_B : (Page2 & (1 ^ _80Store))) << 11; // a11
	if (_hires) // hires?
	{
		// Y: insert hires only address bits
		//
		address |= v_C << 12; // a12
		address |= (1 ^ (Page2 & (1 ^ _80Store))) << 13; // a13
		address |= (Page2 & (1 ^ _80Store)) << 14; // a14
	}
	else
	{
		// N: text, so no higher address bits unless Apple ][, not Apple //e
		//
		if ((1) && // Apple ][? // FIX: check for Apple ][? (FB is most useful in old games)
			(kHPEClock <= h_clock) && // Y: HBL?
			(h_clock <= (kHClocks - 1)))
		{
			address |= 1 << 12; // Y: a12 (add $1000 to address!)
		}
	}

	return m_ram_ptr[address % m_ram_size]; // FIX: this seems to work, but is it right!?
}

/***************************************************************************
    ADDRESS MAP
***************************************************************************/

uint8_t tk2000_state::ram_r(offs_t offset)
{
	if (offset < m_ram_size)
	{
		return m_ram_ptr[offset];
	}

	return 0xff;
}

void tk2000_state::ram_w(offs_t offset, uint8_t data)
{
	if (offset < m_ram_size)
	{
		m_ram_ptr[offset] = data;
	}
}

void tk2000_state::apple2_map(address_map &map)
{
	map(0x0000, 0xbfff).rw(FUNC(tk2000_state::ram_r), FUNC(tk2000_state::ram_w));
	map(0xc000, 0xc000).w(FUNC(tk2000_state::kbout_w)).nopr();
	map(0xc010, 0xc010).r(FUNC(tk2000_state::kbin_r));
	map(0xc020, 0xc020).rw(FUNC(tk2000_state::casout_r), FUNC(tk2000_state::casout_w));
	map(0xc030, 0xc030).rw(FUNC(tk2000_state::snd_r), FUNC(tk2000_state::snd_w));
	map(0xc050, 0xc05f).r(FUNC(tk2000_state::switches_r)).w(m_softlatch, FUNC(addressable_latch_device::write_a0));
	map(0xc060, 0xc060).mirror(8).r(FUNC(tk2000_state::cassette_r));
	map(0xc080, 0xc0ff).rw(FUNC(tk2000_state::c080_r), FUNC(tk2000_state::c080_w));
	map(0xc100, 0xffff).m(m_upperbank, FUNC(address_map_bank_device::amap8));
}

void tk2000_state::inhbank_map(address_map &map)
{
	map(0x0000, 0x3eff).rom().region("maincpu", 0x100);
	map(0x4000, 0x7eff).rw(FUNC(tk2000_state::c100_r), FUNC(tk2000_state::c100_w));
}

/***************************************************************************
    INPUT PORTS
***************************************************************************/

/*
    TK2000 matrix:

          0  1 2 3 4 5 6 7
       0SHIF B V C X Z
       1     G F D S A
       2 SPC T R E W Q
       3 LFT 5 4 3 2 1
       4 RGT 6 7 8 9 0
       5 DWN Y U I O P
       6 UP  H J K L :
       7 RTN N M , . ?

       write row mask 1/2/4/8/10/20/40/80 to $C000
       read column at $C010

       If $C05F is written, the Ctrl key is read in bit 0 of $C010 immediately afterwards.
*/
static INPUT_PORTS_START( tk2000 )
	PORT_START("ROW0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift")        PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)      PORT_CHAR('B')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)      PORT_CHAR('V')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)      PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)      PORT_CHAR('X')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)      PORT_CHAR('Z')

	PORT_START("ROW1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)      PORT_CHAR('G')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)      PORT_CHAR('F')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)      PORT_CHAR('D')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)      PORT_CHAR('S')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)      PORT_CHAR('A')

	PORT_START("ROW2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)  PORT_CHAR(' ')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)      PORT_CHAR('T')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)      PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)      PORT_CHAR('E')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)      PORT_CHAR('W')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)      PORT_CHAR('Q')

	PORT_START("ROW3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT)      PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)      PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)      PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)      PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)      PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)      PORT_CHAR('1') PORT_CHAR('!')

	PORT_START("ROW4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT)     PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)      PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)      PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)      PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)      PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)      PORT_CHAR('0') PORT_CHAR('*')

	PORT_START("ROW5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN)      PORT_CODE(KEYCODE_DOWN)     PORT_CHAR(10)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)      PORT_CHAR('Y')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)      PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)      PORT_CHAR('I') PORT_CHAR('-')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)      PORT_CHAR('O') PORT_CHAR('=')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)      PORT_CHAR('P') PORT_CHAR('+')

	PORT_START("ROW6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(UTF8_UP)        PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)      PORT_CHAR('H')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)      PORT_CHAR('J')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)      PORT_CHAR('K') PORT_CHAR('^')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)      PORT_CHAR('L') PORT_CHAR('@')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)  PORT_CHAR(':') PORT_CHAR(';')

	PORT_START("ROW7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Return")       PORT_CODE(KEYCODE_ENTER)    PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)      PORT_CHAR('N')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)      PORT_CHAR('M')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)  PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)   PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)  PORT_CHAR('?') PORT_CHAR('/')

	PORT_START("keyb_special")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Control")     PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

void tk2000_state::tk2000(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, 1021800);     /* close to actual CPU frequency of 1.020484 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &tk2000_state::apple2_map);

	TIMER(config, "scantimer").configure_scanline(FUNC(tk2000_state::apple2_interrupt), "screen", 0, 1);
	config.set_maximum_quantum(attotime::from_hz(60));

	APPLE2_VIDEO_COMPOSITE(config, m_video, XTAL(14'318'181)).set_screen(m_screen);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_size(280*2, 262);
	m_screen->set_visarea(0, (280*2)-1,0,192-1);
	m_screen->set_screen_update(m_video, NAME((&a2_video_device::screen_update<a2_video_device::model::II, false, false>)));
	m_screen->set_palette(m_video);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);

	/* /INH banking */
	ADDRESS_MAP_BANK(config, A2_UPPERBANK_TAG).set_map(&tk2000_state::inhbank_map).set_options(ENDIANNESS_LITTLE, 8, 32, 0x4000);

	LS259(config, m_softlatch); // U36
	m_softlatch->q_out_cb<0>().set(FUNC(tk2000_state::color_w));
	m_softlatch->q_out_cb<1>().set(FUNC(tk2000_state::motor_a_w));
	m_softlatch->q_out_cb<2>().set(m_video, FUNC(a2_video_device::scr_w));
	m_softlatch->q_out_cb<3>().set(FUNC(tk2000_state::motor_b_w));
	m_softlatch->q_out_cb<4>().set(m_printer, FUNC(centronics_device::write_strobe));
	m_softlatch->q_out_cb<5>().set(FUNC(tk2000_state::rom_ram_w));
	m_softlatch->q_out_cb<7>().set(FUNC(tk2000_state::ctrl_key_w));

	RAM(config, RAM_TAG).set_default_size("64K");

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	CENTRONICS(config, m_printer, centronics_devices, nullptr);
	m_printer->busy_handler().set(FUNC(tk2000_state::printer_busy_w));
}

/***************************************************************************

  Game driver(s)

***************************************************************************/
ROM_START(tk2000)
	ROM_REGION(0x4000,"maincpu",0)
	ROM_LOAD( "tk2000.rom",   0x000000, 0x004000, CRC(dfdbacc3) SHA1(bb37844c31616046630868a4399ee3d55d6df277) )
ROM_END

ROM_START(mpf2)
	ROM_REGION(0x4000,"maincpu",0)
	ROM_LOAD( "mpf_ii.rom",   0x000000, 0x004000, CRC(8780189f) SHA1(92378b0db561632b58a9b36a85f8fb00796198bb) )
ROM_END

} // anonymous namespace


/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY         FULLNAME */
COMP( 1984, tk2000, 0,      0,      tk2000,  tk2000, tk2000_state, empty_init, "Microdigital", "TK2000 Color Computer", MACHINE_NOT_WORKING )
COMP( 1982, mpf2,   tk2000, 0,      tk2000,  tk2000, tk2000_state, empty_init, "Multitech",    "Microprofessor II",     MACHINE_NOT_WORKING )



tk635.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Dirk Best
/****************************************************************************

    Termtek TK-635 terminal

    Hardware:
    - AMD N80C188-25
    - TERMTEK TKA-200
    - 128k + 32k RAM
    - 256 KB flash memory

    Features:
    - 31.5khz or 48.1khz horizontal
    - 70/72 hz vertical
    - 16 background/foreground/border colors
    - 24x80/132, 25x80/132, 42x80/132, 43x80/132
    - Standard PC/AT keyboard

    Notes:
    - Identical to the Qume QVT-72 Plus?

    TODO:
    - Everything

****************************************************************************/

#include "emu.h"
#include "cpu/i86/i186.h"
#include "emupal.h"
#include "screen.h"


namespace {

class tk635_state : public driver_device
{
public:
	tk635_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ram(*this, "ram"),
		m_charram(*this, "charram"),
		m_nmi_enable(false),
		m_voffset(0)
	{ }

	void tk635(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_shared_ptr<u8> m_ram;
	required_shared_ptr<u8> m_charram;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void vblank_w(int state);

	uint8_t unk_00_r();
	void voffset_lsb_w(uint8_t data);
	void voffset_msb_w(uint8_t data);
	uint8_t unk_11_r();
	uint8_t unk_19_r();
	void unk_f0_w(uint8_t data);

	bool m_nmi_enable;
	uint16_t m_voffset;
};

void tk635_state::mem_map(address_map &map)
{
	map(0x00000, 0x1ffff).ram().share("ram");
	map(0x20000, 0x23fff).ram().share("charram");
	map(0xc0000, 0xfffff).rom().region("maincpu", 0);
}

void tk635_state::io_map(address_map &map)
{
	map(0x00, 0x00).r(FUNC(tk635_state::unk_00_r));
	map(0x04, 0x04).w(FUNC(tk635_state::voffset_lsb_w));
	map(0x05, 0x05).w(FUNC(tk635_state::voffset_msb_w));
	map(0x11, 0x11).r(FUNC(tk635_state::unk_11_r));
//  map(0x13, 0x13).ram(); // host port
	map(0x19, 0x19).r(FUNC(tk635_state::unk_19_r));
//  map(0x1b, 0x1b).ram(); // aux port
	map(0xf0, 0xf0).w(FUNC(tk635_state::unk_f0_w));
}

static INPUT_PORTS_START( tk635 )
INPUT_PORTS_END

static const gfx_layout char_layout_8x16 =
{
	8, 16,
	512,
	1,
	{ 0 },
	{ STEP8(0, 1) },
	{ STEP16(0, 8) },
	8*32
};

static GFXDECODE_START(chars)
	GFXDECODE_RAM("charram", 0, char_layout_8x16, 0, 1)
GFXDECODE_END

uint32_t tk635_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	for (int y = 0; y < 26; y++)
	{
		for (int x = 0; x < 132; x++)
		{
			uint8_t code = m_ram[(0x10000 | m_voffset) + (y * 132) + x];

			// draw 16 lines
			for (int i = 0; i < 16; i++)
			{
				uint8_t data = m_charram[(code << 5) + i];

				// 8 pixels of the character
				for (int p = 0; p < 8; p++)
					bitmap.pix(y * 16 + i, x * 9 + p) = BIT(data, 7 - p) ? rgb_t::white() : rgb_t::black();

				// 9th pixel empty
				bitmap.pix(y * 16 + i, x * 9 + 8) = rgb_t::black();
			}
		}
	}

	return 0;
}

void tk635_state::vblank_w(int state)
{
	if (state == 1 && m_nmi_enable)
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}

uint8_t tk635_state::unk_00_r()
{
	logerror("unk_00_r: %02x\n", 0x10);
	return 0x10;
}

void tk635_state::voffset_lsb_w(uint8_t data)
{
	m_voffset = (m_voffset & 0xff00) | data;
}

void tk635_state::voffset_msb_w(uint8_t data)
{
	m_voffset = (data << 8) | (m_voffset & 0x00ff);
}

uint8_t tk635_state::unk_11_r()
{
	logerror("unk_11_r: %02x\n", 0x09);
	return 0x09;
}

uint8_t tk635_state::unk_19_r()
{
	logerror("unk_19_r: %02x\n", 0x09);
	return 0x09;
}

void tk635_state::unk_f0_w(uint8_t data)
{
	logerror("unk_f0_w: %02x\n", data);
	m_nmi_enable = true;
}

void tk635_state::machine_start()
{
	// register for save states
	save_item(NAME(m_nmi_enable));
	save_item(NAME(m_voffset));
}

void tk635_state::machine_reset()
{
	m_nmi_enable = false;
}

void tk635_state::tk635(machine_config &config)
{
	I80188(config, m_maincpu, 25000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &tk635_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &tk635_state::io_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(70);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); // not accurate
	screen.set_screen_update(FUNC(tk635_state::screen_update));
	screen.set_size(1188, 416);
	screen.set_visarea(0, 1188-1, 0, 416-1);
	screen.screen_vblank().set(FUNC(tk635_state::vblank_w));

	PALETTE(config, "palette").set_entries(16);

	GFXDECODE(config, "gfxdecode", "palette", chars);
}

ROM_START( tk635 )
	ROM_REGION(0x40000, "maincpu", 0)
	ROM_LOAD("fw_v0_23.bin", 0x00000, 0x40000, CRC(bec6fdae) SHA1(37dc46f6b761d874bd1627a1137bc4082e364698))
ROM_END

} // anonymous namespace


COMP( 199?, tk635, 0, 0, tk635, tk635, tk635_state, empty_init, "Termtek", "TK-635", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



tk80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************



NEC TK80 / MIKROLAB KR580IK80
*****************************
TK80 driver by Robbbert
Mikrolab driver by Micko
Merged by Robbbert.

TK80 (Training Kit 80) considered to be Japan's first home computer.
It consisted of 25 keys and 8 LED digits, and was programmed in hex.
The Mikrolab is a Russian clone which appears to be almost completely identical.

TK85 seems to be the same as TK80, except it has a 8085 and a larger ROM.
No schematics etc are available. Thanks to 'Nama' who dumped the rom.
It has 25 keys, so a few aren't defined yet.

ND-80Z : http://www.alles.or.jp/~thisida/nd80z3syokai.html (newer version)
Like the TK85, it has a 2KB rom. Thanks again to 'Nama' who dumped it.

When booted, the system begins at 0000 which is ROM. You need to change the
address to 8000 before entering a program. Here is a test to paste in:
8000-11^22^33^44^55^66^77^88^99^8000-
Press the right-arrow to confirm data has been entered.

Operation:
4 digits at left is the address; 2 digits at right is the data.
As you increment addresses, the middle 2 digits show the previous byte.
You can enter 4 digits, and pressing 'ADRS SET' will transfer this info
to the left, thus setting the address to this value. Press 'WRITE INCR' to
store new data and increment the address. Press 'READ INCR' and 'READ DECR'
to scan through data without updating it. Other keys unknown/not implemented.

ToDo:
- Add storage



ICS8080
- Keys labels are correct, but which key is which is not known
- Character B is corrupt
- Operation is different to the other systems

****************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "cpu/z80/z80.h"
#include "machine/i8255.h"
#include "machine/keyboard.h"
#include "tk80.lh"


namespace {

class tk80_state : public driver_device
{
public:
	tk80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ppi(*this, "ppi8255")
		, m_digit(*this, "digit%u", 0U)
	{ }

	void ics8080(machine_config &config);
	void tk80(machine_config &config);
	void mikrolab(machine_config &config);
	void nd80z(machine_config &config);
	void tk85(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;

	uint8_t key_matrix_r();
	uint8_t nd80z_key_r();
	uint8_t serial_r();
	void serial_w(uint8_t data);
	void mikrolab_serial_w(uint8_t data);
	uint8_t display_r(offs_t offset);
	void display_w(offs_t offset, uint8_t data);

	void ics8080_mem(address_map &map) ATTR_COLD;
	void mikrolab_io(address_map &map) ATTR_COLD;
	void nd80z_io(address_map &map) ATTR_COLD;
	void tk80_io(address_map &map) ATTR_COLD;
	void tk80_mem(address_map &map) ATTR_COLD;
	void tk85_mem(address_map &map) ATTR_COLD;

	uint8_t m_term_data = 0U;
	uint8_t m_keyb_press = 0U;
	uint8_t m_keyb_press_flag = 0U;
	uint8_t m_shift_press_flag = 0U;
	uint8_t m_ppi_portc = 0U;

	required_device<cpu_device> m_maincpu;
	required_device<i8255_device> m_ppi;
	output_finder<8> m_digit;
};


void tk80_state::machine_start()
{
	m_digit.resolve();

	save_item(NAME(m_term_data));
	save_item(NAME(m_keyb_press));
	save_item(NAME(m_keyb_press_flag));
	save_item(NAME(m_shift_press_flag));
	save_item(NAME(m_ppi_portc));
}

uint8_t tk80_state::display_r(offs_t offset)
{
	return m_digit[offset & 0x7];
}

void tk80_state::display_w(offs_t offset, uint8_t data)
{
	m_digit[offset & 0x7] = data;
}

void tk80_state::tk80_mem(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x83ff); // A10-14 not connected
	map(0x0000, 0x02ff).rom();
	map(0x0300, 0x03ff).ram(); // EEPROM
	map(0x8000, 0x83f7).ram(); // RAM
	map(0x83f8, 0x83ff).ram().rw(FUNC(tk80_state::display_r), FUNC(tk80_state::display_w));
}

void tk80_state::tk85_mem(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x87ff); // A10-14 not connected
	map(0x0000, 0x07ff).rom();
	map(0x8000, 0x83f7).ram();
	map(0x83f8, 0x83ff).ram().rw(FUNC(tk80_state::display_r), FUNC(tk80_state::display_w));
}

void tk80_state::ics8080_mem(address_map &map)
{
	map.unmap_value_high();
	//map.global_mask(0x87ff); // A10-14 not connected
	map(0x0000, 0x1fff).rom();
	map(0x8000, 0x83f7).ram();
	map(0x83f8, 0x83ff).ram().rw(FUNC(tk80_state::display_r), FUNC(tk80_state::display_w));
	map(0x8400, 0x8fff).ram();
}

void tk80_state::tk80_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x03);
	map(0x00, 0x03).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
}

void tk80_state::mikrolab_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x03);
	map(0x00, 0x03).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
}

void tk80_state::nd80z_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x03);
	map(0x00, 0x03).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
}

/* Input ports */
static INPUT_PORTS_START( tk80 )
	PORT_START("X0") /* KEY ROW 0 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')

	PORT_START("X1") /* KEY ROW 1 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("X2") /* KEY ROW 2 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RUN") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RET") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ADRS SET") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("READ DECR") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("READ INCR") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("WRITE INCR") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("STORE DATA") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LOAD DATA") PORT_CODE(KEYCODE_O)
INPUT_PORTS_END

INPUT_PORTS_START( mikrolab )
	PORT_INCLUDE( tk80 )
	PORT_MODIFY("X2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RUN") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RESUME") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ADDRESS") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("+") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SAVE") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("END") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_O)
INPUT_PORTS_END

INPUT_PORTS_START( ics8080 )
	PORT_INCLUDE( tk80 )
	PORT_MODIFY("X2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ADDR") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MEM") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BRK") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CLR") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("STEP") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NEXT") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("REG") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RUN") PORT_CODE(KEYCODE_O)
INPUT_PORTS_END

static INPUT_PORTS_START( tk85 )
	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0 / AF") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 / BC") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 / DE") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 / HL") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 / SP") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 / BR.P") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 / BR.D") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')

	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A / SAVE") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B / LOAD") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C / TM") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D / MOV") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E / OUT") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F / IN") PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RUN") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CONT") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ADRS SET") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("READ DEC") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("READ INC") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("WR / ENT") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MODE") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("REG") PORT_CODE(KEYCODE_R)
INPUT_PORTS_END

uint8_t tk80_state::key_matrix_r()
{
	// PA0-7 keyscan in

	uint8_t data = 0xff;

	if (BIT(m_ppi_portc, 4))
		data &= ioport("X0")->read();
	if (BIT(m_ppi_portc, 5))
		data &= ioport("X1")->read();
	if (BIT(m_ppi_portc, 6))
		data &= ioport("X2")->read();

	return data;
}

uint8_t tk80_state::nd80z_key_r()
{
	// PA0-7 keyscan in

	uint8_t data = 0xff, row = m_ppi_portc & 7;
	if (row == 6)
		data &= ioport("X0")->read();
	else if (row == 5)
		data &= ioport("X1")->read();
	else if (row == 3)
		data &= ioport("X2")->read();

	return data;
}

uint8_t tk80_state::serial_r()
{
	// PB0 - serial in
	//printf("B R\n");

	return 0;
}

void tk80_state::serial_w(uint8_t data)
{
	// PC0 - serial out
	// PC4-6 keyscan out
	// PC7 - display on/off
	m_ppi_portc = data ^ 0x70;
}

void tk80_state::mikrolab_serial_w(uint8_t data)
{
	// PC0 - serial out
	// PC4-6 keyscan out
	// PC7 - display on/off
	m_ppi_portc = data;
}

void tk80_state::tk80(machine_config &config)
{
	/* basic machine hardware */
	I8080A(config, m_maincpu, 18.432_MHz_XTAL / 9);
	m_maincpu->set_addrmap(AS_PROGRAM, &tk80_state::tk80_mem);
	m_maincpu->set_addrmap(AS_IO, &tk80_state::tk80_io);

	/* video hardware */
	config.set_default_layout(layout_tk80);

	/* Devices */
	I8255(config, m_ppi);
	m_ppi->in_pa_callback().set(FUNC(tk80_state::key_matrix_r));
	m_ppi->in_pb_callback().set(FUNC(tk80_state::serial_r));
	m_ppi->out_pc_callback().set(FUNC(tk80_state::serial_w));
}

void tk80_state::mikrolab(machine_config &config)
{
	tk80(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &tk80_state::tk85_mem);
	m_maincpu->set_addrmap(AS_IO, &tk80_state::mikrolab_io);

	/* Devices */
	m_ppi->out_pc_callback().set(FUNC(tk80_state::mikrolab_serial_w));
}

void tk80_state::nd80z(machine_config &config)
{
	Z80(config, m_maincpu, 1e6); // Sharp LH0080A, can't see writing on xtal
	m_maincpu->set_addrmap(AS_PROGRAM, &tk80_state::tk85_mem);
	m_maincpu->set_addrmap(AS_IO, &tk80_state::nd80z_io);

	/* video hardware */
	config.set_default_layout(layout_tk80);

	/* Devices */
	I8255(config, m_ppi);
	m_ppi->in_pa_callback().set(FUNC(tk80_state::nd80z_key_r));
	m_ppi->in_pb_callback().set(FUNC(tk80_state::serial_r));
	m_ppi->out_pc_callback().set(FUNC(tk80_state::mikrolab_serial_w));
}

void tk80_state::tk85(machine_config &config)
{
	I8085A(config, m_maincpu, 4.9152_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &tk80_state::tk85_mem);
	m_maincpu->set_addrmap(AS_IO, &tk80_state::tk80_io);
	// TODO: SID and SOD = serial cassette interface

	/* video hardware */
	config.set_default_layout(layout_tk80);

	/* Devices */
	I8255(config, m_ppi);
	m_ppi->in_pa_callback().set(FUNC(tk80_state::key_matrix_r));
	m_ppi->out_pc_callback().set(FUNC(tk80_state::serial_w));
}

void tk80_state::ics8080(machine_config &config)
{
	tk80(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &tk80_state::ics8080_mem);
}


/* ROM definition */
ROM_START( tk80 )
	ROM_REGION( 0x0300, "maincpu", 0 )
	ROM_LOAD( "tk80-1.bin", 0x0000, 0x0100, CRC(897295e4) SHA1(50fb42b07252fc48044830e2f228e218fc59481c))
	ROM_LOAD( "tk80-2.bin", 0x0100, 0x0100, CRC(d54480c3) SHA1(354962aca1710ac75b40c8c23a6c303938f9d596))
	ROM_LOAD( "tk80-3.bin", 0x0200, 0x0100, CRC(8d4b02ef) SHA1(2b5a1ee8f97db23ffec48b96f12986461024c995))
ROM_END

ROM_START( ics8080 )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD( "amtsmnonv27.rom1", 0x0000, 0x0800, CRC(82d40dc5) SHA1(9215457101c3b9b8706dbebe902196494993a282) )
	ROM_LOAD( "thmcplv11.rom2",   0x0800, 0x0800, CRC(51784f70) SHA1(c05c75d566c4ff8f681eba29cd48e72b95be89e0) )
	ROM_LOAD( "tunev08.rom3",     0x1000, 0x0800, CRC(aae2344b) SHA1(b02b22cadb43c7ac26c43d443688b9b19d465973) )
	ROM_LOAD( "mtrspdv12.rom4",   0x1800, 0x0800, CRC(920dda33) SHA1(631ee5e6314d9788e7be0ae00a97b55693eeb855) )
	ROM_FILL(0x2be,1,0x7c)    // Fix display of B
ROM_END

ROM_START( mikrolab )
	ROM_REGION( 0x0800, "maincpu", 0 )
	/* these dumps are taken from PDF so need check with real device */
	ROM_LOAD( "rom-1.bin", 0x0000, 0x0200, BAD_DUMP CRC(eed5f23b) SHA1(c82f7a16ce44c4fcbcb333245555feae1fcdf058))
	ROM_LOAD( "rom-2.bin", 0x0200, 0x0200, BAD_DUMP CRC(726a224f) SHA1(7ed8d2c6dd4fb7836475e207e1972e33a6a91d2f))
ROM_END

ROM_START( nectk85 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "tk85.bin",  0x0000, 0x0800, CRC(8a0b6d7e) SHA1(6acc8c04990692b08929043ccf638761b7301def))
ROM_END

ROM_START( nd80z )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "ndf.bin",  0x0000, 0x0800, CRC(fe829f1d) SHA1(6fff31884b8d984076d4450ca3a3e48efadeb648))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS       INIT        COMPANY      FULLNAME              FLAGS
COMP( 1976, tk80,     0,      0,      tk80,     tk80,     tk80_state, empty_init, "NEC",       "TK-80",              MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 1980, nectk85,  tk80,   0,      tk85,     tk85,     tk80_state, empty_init, "NEC",       "TK-85",              MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 19??, nd80z,    tk80,   0,      nd80z,    tk80,     tk80_state, empty_init, "Chunichi",  "ND-80Z",             MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 19??, mikrolab, tk80,   0,      mikrolab, mikrolab, tk80_state, empty_init, "<unknown>", "Mikrolab KR580IK80", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )
COMP( 19??, ics8080,  tk80,   0,      ics8080,  ics8080,  tk80_state, empty_init, "<unknown>", "ICS8080",            MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )



tk80bs.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese, Robbbert
/***************************************************************************


NEC TK80BS
**********
TK-80BS (c) 1980 NEC

Preliminary driver by Angelo Salese
Various additions by Robbbert

The TK80BS (Basic Station) has a plugin keyboard, BASIC in rom,
and connected to a tv.

TODO:
    - (try to) dump proper roms, the whole driver is based off fake roms;
    - bios 0 BASIC doesn't seem to work properly; (It does if you type NEW first)
    - bios 1 does not boot up because it runs off into the weeds
    - bios 2 also does that, somehow it starts up anyway, but no commands work


****************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8255.h"
#include "machine/keyboard.h"
#include "emupal.h"
#include "screen.h"


namespace {

class tk80bs_state : public driver_device
{
public:
	tk80bs_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_p_videoram(*this, "videoram")
		, m_maincpu(*this, "maincpu")
		, m_ppi(*this, "ppi")
		, m_gfxdecode(*this, "gfxdecode")
		, m_palette(*this, "palette")
	{ }

	void tk80bs(machine_config &config);

protected:
	 virtual void machine_start() override ATTR_COLD;

private:
	uint8_t ppi_custom_r(offs_t offse);
	void ppi_custom_w(offs_t offset, uint8_t data);
	void kbd_put(u8 data);
	uint8_t port_a_r();
	uint8_t port_b_r();
	uint32_t screen_update_tk80bs(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void mem_map(address_map &map) ATTR_COLD;
	uint8_t m_term_data = 0U;
	required_shared_ptr<uint8_t> m_p_videoram;
	required_device<cpu_device> m_maincpu;
	required_device<i8255_device> m_ppi;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<palette_device> m_palette;
};


uint32_t tk80bs_state::screen_update_tk80bs(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 count = 0;

	for(u8 y = 0; y < 16; y++)
	{
		for(u8 x = 0; x < 32; x++)
		{
			u8 tile = m_p_videoram[count++];

			m_gfxdecode->gfx(0)->opaque(bitmap,cliprect, tile, 0, 0, 0, x*8, y*8);
		}
	}

	return 0;
}

/* A0 and A1 are swapped at the 8255 chip */
uint8_t tk80bs_state::ppi_custom_r(offs_t offset)
{
	switch(offset)
	{
		case 1:
			return m_ppi->read(2);
		case 2:
			return m_ppi->read(1);
		default:
			return m_ppi->read(offset);
	}
}

void tk80bs_state::ppi_custom_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
		case 1:
			m_ppi->write(2, data);
			break;
		case 2:
			m_ppi->write(1, data);
			break;
		default:
			m_ppi->write(offset, data);
	}
}

void tk80bs_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).rom();
//  map(0x0c00, 0x7bff).rom(); // ext
	map(0x7df8, 0x7df9).noprw(); // i8251 sio
	map(0x7dfc, 0x7dff).rw(FUNC(tk80bs_state::ppi_custom_r), FUNC(tk80bs_state::ppi_custom_w));
	map(0x7e00, 0x7fff).ram().share("videoram"); // video ram
	map(0x8000, 0xcfff).ram(); // RAM
	map(0xd000, 0xefff).rom(); // BASIC
	map(0xf000, 0xffff).rom(); // BSMON
}


/* Input ports */
static INPUT_PORTS_START( tk80bs )
INPUT_PORTS_END

uint8_t tk80bs_state::port_a_r()
{
	uint8_t ret = m_term_data;
	m_term_data = 0;
	return ret;
}


uint8_t tk80bs_state::port_b_r()
{
	if (m_term_data)
	{
		m_ppi->pc4_w(0); // send a strobe pulse
		return 0x20;
	}
	else
		return 0;
}

void tk80bs_state::kbd_put(u8 data)
{
	data &= 0x7f;
	if (data > 0x5f) data-=0x20;
	m_term_data = data;
}

void tk80bs_state::machine_start()
{
	save_item(NAME(m_term_data));

	m_term_data = 0;
}


/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 8,
	512,
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};

static GFXDECODE_START( gfx_tk80bs )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END


void tk80bs_state::tk80bs(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, XTAL(1'000'000)); //unknown clock
	m_maincpu->set_addrmap(AS_PROGRAM, &tk80bs_state::mem_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(256, 128);
	screen.set_visarea(0, 256-1, 0, 128-1);
	screen.set_screen_update(FUNC(tk80bs_state::screen_update_tk80bs));
	screen.set_palette(m_palette);

	PALETTE(config, m_palette, palette_device::MONOCHROME);
	GFXDECODE(config, m_gfxdecode, m_palette, gfx_tk80bs);

	/* Devices */
	I8255(config, m_ppi);
	m_ppi->in_pa_callback().set(FUNC(tk80bs_state::port_a_r));
	m_ppi->in_pb_callback().set(FUNC(tk80bs_state::port_b_r));

	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(tk80bs_state::kbd_put));
}


ROM_START( tk80bs )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	/* all of these aren't taken from an original machine*/
	ROM_SYSTEM_BIOS(0, "psedo", "Pseudo LEVEL 1")
	ROMX_LOAD( "tk80.dummy", 0x0000, 0x0800, BAD_DUMP CRC(553b25ca) SHA1(939350d7fa56ce567ddf393c9f4b9db6ebc18a2c), ROM_BIOS(0))
	ROMX_LOAD( "ext.l1",     0x0c00, 0x6e46, BAD_DUMP CRC(d05ed3ff) SHA1(8544aa2cb58df9edf221f5be2cdafa248dd33828), ROM_BIOS(0))
	ROMX_LOAD( "lv1basic.l1",0xe000, 0x09a2, BAD_DUMP CRC(3ff67a71) SHA1(528c9331740637e853c099e1739ecdea6dd200bc), ROM_BIOS(0))
	ROMX_LOAD( "bsmon.l1",   0xf000, 0x0db0, BAD_DUMP CRC(5daa599b) SHA1(7e6ec5bfb3eea114f7ee9ef589a89246b8533b2f), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "psedo10", "Pseudo LEVEL 2 1.0")
	ROMX_LOAD( "tk80.dummy", 0x0000, 0x0800, BAD_DUMP CRC(553b25ca) SHA1(939350d7fa56ce567ddf393c9f4b9db6ebc18a2c), ROM_BIOS(1))
	ROMX_LOAD( "ext.10",     0x0c00, 0x3dc2, BAD_DUMP CRC(3c64d488) SHA1(919180d5b34b981ab3dd8b2885d3c0933203f355), ROM_BIOS(1))
	ROMX_LOAD( "lv2basic.10",0xd000, 0x2000, BAD_DUMP CRC(594fe70e) SHA1(5854c1be5fa78c1bfee365379495f14bc23e15e7), ROM_BIOS(1))
	ROMX_LOAD( "bsmon.10",   0xf000, 0x0daf, BAD_DUMP CRC(d0047983) SHA1(79e2b5dc47b574b55375cbafffff144744093ec1), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "psedo11", "Pseudo LEVEL 2 1.1")
	ROMX_LOAD( "tk80.dummy", 0x0000, 0x0800, BAD_DUMP CRC(553b25ca) SHA1(939350d7fa56ce567ddf393c9f4b9db6ebc18a2c), ROM_BIOS(2))
	ROMX_LOAD( "ext.11",     0x0c00, 0x3dd4, BAD_DUMP CRC(bd5c5169) SHA1(2ad70828348372328b76bac0fa93d3f6f17ade34), ROM_BIOS(2))
	ROMX_LOAD( "lv2basic.11",0xd000, 0x2000, BAD_DUMP CRC(3df9a3bd) SHA1(9539409c876bce27d630fe47d07a4316d2ce09cb), ROM_BIOS(2))
	ROMX_LOAD( "bsmon.11",   0xf000, 0x0ff6, BAD_DUMP CRC(fca7a609) SHA1(7c7eb5e5e4cf1e0021383bdfc192b88262aba6f5), ROM_BIOS(2))

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "font.rom",    0x0000, 0x1000, BAD_DUMP CRC(94d95199) SHA1(9fe741eab866b0c520d4108bccc6277172fa190c))
ROM_END

} // Anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY  FULLNAME   FLAGS
COMP( 1980, tk80bs, tk80,   0,      tk80bs,  tk80bs, tk80bs_state, empty_init, "NEC",   "TK-80BS", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



tm990189.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Raphael Nabet, Robbbert
/*
    Experimental tm990/189 ("University Module") driver.

    The tm990/189 is a simple board built around a tms9980 at 2.0 MHz.
    The board features:
    * a calculator-like alphanumeric keyboard, a 10-digit 8-segment display,
      a sound buzzer and 4 status LEDs
    * a 4kb ROM socket (0x3000-0x3fff), and a 2kb ROM socket (0x0800-0x0fff)
    * 1kb of RAM expandable to 2kb (0x0000-0x03ff and 0x0400-0x07ff)
    * a tms9901 controlling a custom parallel I/O port (available for
      expansion)
    * an optional on-board serial interface (either TTY or RS232): TI ROMs
      support a terminal connected to this port
    * an optional tape interface
    * an optional bus extension port for adding additional custom devices (TI
      sold a video controller expansion built around a tms9918, which was
      supported by University Basic)

    One tms9901 is set up so that it handles tms9980 interrupts.  The other
    tms9901, the tms9902, and extension cards can trigger interrupts on the
    interrupt-handling tms9901.

    TI sold two ROM sets for this machine: a monitor and assembler ("UNIBUG",
    packaged as one 4kb EPROM) and a Basic interpreter ("University BASIC",
    packaged as a 4kb and a 2kb EPROM).  Users could burn and install custom
    ROM sets, too.

    This board was sold to universities to learn either assembly language or
    BASIC programming.

    A few hobbyists may have bought one of these, too.  This board can actually
    be used as a development kit for the tms9980, but it was not supported as
    such (there was no EPROM programmer or mass storage system for the
    tm990/189, though you could definitively design your own and attach them to
    the extension port).

    - Raphael Nabet 2003

    Bug - The input buffer of character segments isn't fully cleared. If you
          press Shift, then Z enough times, garbage appears. This is because
          the boot process should have set 18C-1CB to FF, but only sets up to 1B3.

    Need a dump of the UNIBUG rom.

    Demo programs for the 990189v: You can get impressive colour displays (with
    sprites) from the 4 included demos. Press the Enter key after each instruction,
    and wait for the READY prompt before proceeding to the next.

    NEW
    LOADx (where x = 0,1,2,3)
    RUN

    University BASIC fully supports the tms9918 videocard option. For example, enter
    COLOR x (where x = 1 to 15), to get a coloured background.

    - Robbbert 2011

******************************************************************************************/

#include "emu.h"

#include "cpu/tms9900/tms9980a.h"
#include "imagedev/cassette.h"
#include "machine/timer.h"
#include "machine/tms9901.h"
#include "machine/tms9902.h"
#include "sound/spkrdev.h"
#include "video/tms9928a.h"

#include "speaker.h"

#include "tm990189.lh"
#include "tm990189v.lh"


#define TMS9901_0_TAG "tms9901_usr"
#define TMS9901_1_TAG "tms9901_sys"

class tm990189_state : public driver_device
{
	friend class tm990_189_rs232_image_device;

public:
	tm990189_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_tms9980a(*this, "maincpu")
		, m_speaker(*this, "speaker")
		, m_cass(*this, "cassette")
		, m_tms9901_usr(*this, TMS9901_0_TAG)
		, m_tms9901_sys(*this, TMS9901_1_TAG)
		, m_tms9902(*this, "tms9902")
		, m_digits(*this, "digit%u", 0U)
		, m_leds(*this, "led%u", 0U)
	{
	}

	void tm990_189(machine_config &config) ATTR_COLD;

	DECLARE_INPUT_CHANGED_MEMBER( load_interrupt );

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<tms9980a_device> m_tms9980a;

private:
	void external_operation(offs_t offset, uint8_t data);

	template <unsigned N> void usr9901_led_w(int state) { led_set(N, state); }
	void usr9901_interrupt_callback(int state);

	void sys9901_interrupt_callback(int state);
	uint8_t sys9901_r(offs_t offset);

	template <unsigned N> void sys9901_digitsel_w(int state) { digitsel(N, state); }
	template <unsigned N> void sys9901_segment_w(int state) { segment_set(N, state); }

	void sys9901_dsplytrgr_w(int state);
	void sys9901_shiftlight_w(int state);
	void sys9901_spkrdrive_w(int state);
	void sys9901_tapewdata_w(int state);

	void xmit_callback(uint8_t data);

	emu_timer *m_load_timer = nullptr;

	void tm990_189_cru_map(address_map &map) ATTR_COLD;
	void tm990_189_memmap(address_map &map) ATTR_COLD;

	void draw_digit(void);
	void led_set(int number, bool state);
	void segment_set(int offset, bool state);
	void digitsel(int offset, bool state);

	TIMER_DEVICE_CALLBACK_MEMBER(display_callback);
	TIMER_CALLBACK_MEMBER(clear_load);
	void hold_load();

	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cass;

	required_device<tms9901_device> m_tms9901_usr;
	required_device<tms9901_device> m_tms9901_sys;
	required_device<tms9902_device> m_tms9902;
	output_finder<10> m_digits;
	output_finder<7> m_leds;

	int m_load_state = 0;

	int m_digitsel = 0;
	int m_segment = 0;
	emu_timer *m_displayena_timer = 0;
	uint8_t m_segment_state[10]{};
	uint8_t m_old_segment_state[10]{};
	uint8_t m_LED_state = 0U;
	device_image_interface *m_rs232_fp = 0;
	//uint8_t m_rs232_rts;
};


class tm990189_v_state : public tm990189_state
{
public:
	tm990189_v_state(const machine_config &mconfig, device_type type, const char *tag)
		: tm990189_state(mconfig, type, tag)
		, m_tms9918(*this, "tms9918")
		, m_buttons(*this, "BUTTONS")
		, m_axes(*this, { "JOY1_X", "JOY1_Y", "JOY2_X", "JOY2_Y" })
	{
	}

	void tm990_189_v(machine_config &config) ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;

	uint8_t video_vdp_r(offs_t offset);
	void video_vdp_w(offs_t offset, uint8_t data);
	uint8_t video_joy_r();
	void video_joy_w(uint8_t data);

	void tm990_189_v_memmap(address_map &map) ATTR_COLD;

	required_device<tms9918_device> m_tms9918;
	required_ioport m_buttons;
	required_ioport_array<4> m_axes;

	emu_timer *m_joy1x_timer = nullptr;
	emu_timer *m_joy1y_timer = nullptr;
	emu_timer *m_joy2x_timer = nullptr;
	emu_timer *m_joy2y_timer = nullptr;

	uint8_t m_bogus_read_save = 0U;
};


#define displayena_duration attotime::from_usec(4500)   /* Can anyone confirm this? 74LS123 connected to C=0.1uF and R=100kOhm */

void tm990189_state::machine_start()
{
	m_digits.resolve();
	m_leds.resolve();
	m_displayena_timer = machine().scheduler().timer_alloc(timer_expired_delegate());

	m_digitsel = 0;
	m_LED_state = 0;

	m_load_timer = timer_alloc(FUNC(tm990189_state::clear_load), this);
}

void tm990189_v_state::machine_start()
{
	tm990189_state::machine_start();

	m_joy1x_timer = machine().scheduler().timer_alloc(timer_expired_delegate());
	m_joy1y_timer = machine().scheduler().timer_alloc(timer_expired_delegate());
	m_joy2x_timer = machine().scheduler().timer_alloc(timer_expired_delegate());
	m_joy2y_timer = machine().scheduler().timer_alloc(timer_expired_delegate());
}

void tm990189_state::machine_reset()
{
	m_tms9980a->set_ready(ASSERT_LINE);
	m_tms9980a->set_hold(CLEAR_LINE);
	hold_load();
}

/*
    hold and debounce load line (emulation is inaccurate)
*/

TIMER_CALLBACK_MEMBER(tm990189_state::clear_load)
{
	m_load_state = false;
	m_tms9980a->set_input_line(INT_9980A_LOAD, CLEAR_LINE);
}

void tm990189_state::hold_load()
{
	m_load_state = true;
	m_tms9980a->set_input_line(INT_9980A_LOAD, ASSERT_LINE);
	m_load_timer->adjust(attotime::from_msec(100));
}

/*
    LOAD interrupt switch
*/
INPUT_CHANGED_MEMBER( tm990189_state::load_interrupt )
{
	// When depressed, fire LOAD (neg logic)
	if (newval==CLEAR_LINE) hold_load();
}


/*
    tm990_189 video emulation.

    Has an integrated 10 digit 8-segment display.
    Supports EIA and TTY terminals, and an optional 9918 controller.
*/

void tm990189_state::draw_digit()
{
	m_segment_state[m_digitsel] |= ~m_segment;
}


TIMER_DEVICE_CALLBACK_MEMBER(tm990189_state::display_callback)
{
	// since the segment data is cleared after being used, the old_segment is there
	// in case the segment data hasn't been refreshed yet.
	for (uint8_t i = 0; i < 10; i++)
	{
		m_old_segment_state[i] |= m_segment_state[i];
		m_digits[i] = m_old_segment_state[i];
		m_old_segment_state[i] = m_segment_state[i];
		m_segment_state[i] = 0;
	}

	for (uint8_t i = 0; i < 7; i++)
		m_leds[i] = !BIT(m_LED_state, i);
}

/*
    tms9901 code
*/

void tm990189_state::usr9901_interrupt_callback(int state)
{
	// Triggered by internal timer (set by ROM to 1.6 ms cycle) on level 3
	// or by keyboard interrupt (level 6)
	if (!m_load_state)
	{
		m_tms9980a->set_input_line(m_tms9901_usr->get_int_level() & 7, ASSERT_LINE);
	}
}

void tm990189_state::led_set(int offset, bool state)
{
	if (state)
		m_LED_state |= (1 << offset);
	else
		m_LED_state &= ~(1 << offset);
}

void tm990189_state::sys9901_interrupt_callback(int state)
{
	// TODO: Check this
	m_tms9901_usr->set_int_line(5, state);
}

uint8_t tm990189_state::sys9901_r(offs_t offset)
{
	// |-|Cass|K|K|K|K|K|C|
	static const char *const keynames[] = { "LINE0", "LINE1", "LINE2", "LINE3", "LINE4", "LINE5", "LINE6", "LINE7", "LINE8" };

	offset &= 0x0F;
	switch (offset)
	{
		case tms9901_device::INT1:
		case tms9901_device::INT2:
		case tms9901_device::INT3:
		case tms9901_device::INT4:
		case tms9901_device::INT5:
			if (m_digitsel < 9)
				return BIT(ioport(keynames[m_digitsel])->read(), offset-tms9901_device::INT1);
			else return 0;

		case tms9901_device::INT6:
			return (m_cass->input() > 0);
		default:
			return 0;
	}
}

/*
      uint8_t data = 0;
      if (offset == tms9901_device::CB_INT7)
      {
              static const char *const keynames[] = { "LINE0", "LINE1", "LINE2", "LINE3", "LINE4", "LINE5", "LINE6", "LINE7", "LINE8" };
              // keyboard read
              if (m_digitsel < 9)
                    data |= ioport(keynames[m_digitsel])->read() << 1;
              // tape input
              if (m_cass->input() > 0.0)
                      data |= 0x40;
      }
      return data;
*/

void tm990189_state::digitsel(int offset, bool state)
{
	if (state)
		m_digitsel |= 1 << offset;
	else
		m_digitsel &= ~ (1 << offset);
}


void tm990189_state::segment_set(int offset, bool state)
{
	if (state)
		m_segment |= 1 << offset;
	else
	{
		m_segment &= ~ (1 << offset);
		if ((m_displayena_timer->remaining() > attotime::zero)  && (m_digitsel < 10))
			draw_digit();
	}
}


void tm990189_state::sys9901_dsplytrgr_w(int state)
{
	if ((!state) && (m_digitsel < 10))
	{
		m_displayena_timer->reset(displayena_duration);
		draw_digit();
	}
}

void tm990189_state::sys9901_shiftlight_w(int state)
{
	if (state)
		m_LED_state |= 0x10;
	else
		m_LED_state &= ~0x10;
}

void tm990189_state::sys9901_spkrdrive_w(int state)
{
	m_speaker->level_w(state);
}

void tm990189_state::sys9901_tapewdata_w(int state)
{
	m_cass->output(state ? +1.0 : -1.0);
}

class tm990_189_rs232_image_device : public device_t, public device_image_interface
{
public:
	// construction/destruction
	template <typename T> tm990_189_rs232_image_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&tms_tag)
		: tm990_189_rs232_image_device(mconfig, tag, owner, clock)
	{
		m_tms9902.set_tag(std::forward<T>(tms_tag));
	}

	tm990_189_rs232_image_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	// device_image_interface implementation
	virtual bool is_readable()  const noexcept override { return true; }
	virtual bool is_writeable() const noexcept override { return true; }
	virtual bool is_creatable() const noexcept override { return true; }
	virtual bool is_reset_on_load() const noexcept override { return false; }
	virtual bool support_command_line_image_creation() const noexcept override { return true; }
	virtual const char *file_extensions() const noexcept override { return ""; }
	virtual const char *image_type_name() const noexcept override { return "serial"; }
	virtual const char *image_brief_type_name() const noexcept override { return "serl"; }

	virtual std::pair<std::error_condition, std::string> call_load() override;
	virtual void call_unload() override;

protected:
	// device_t implementation
	virtual void device_start() override ATTR_COLD;

	TIMER_CALLBACK_MEMBER(rs232_input_tick);

	required_device<tms9902_device> m_tms9902;
	emu_timer *m_rs232_input_timer = nullptr;
};

DEFINE_DEVICE_TYPE(TM990_189_RS232, tm990_189_rs232_image_device, "tm990_189_rs232_image", "TM990/189 RS232 port")

tm990_189_rs232_image_device::tm990_189_rs232_image_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, TM990_189_RS232, tag, owner, clock)
	, device_image_interface(mconfig, *this)
	, m_tms9902(*this, finder_base::DUMMY_TAG)
{
}

void tm990_189_rs232_image_device::device_start()
{
	m_rs232_input_timer = timer_alloc(FUNC(tm990_189_rs232_image_device::rs232_input_tick), this);
}

TIMER_CALLBACK_MEMBER(tm990_189_rs232_image_device::rs232_input_tick)
{
	uint8_t buf;
	if (/*m_rs232_rts &&*/ /*(mame_ftell(m_rs232_fp) < mame_fsize(m_rs232_fp))*/1)
	{
		if (fread(&buf, 1) == 1)
			m_tms9902->rcv_data(buf);
	}
}

std::pair<std::error_condition, std::string> tm990_189_rs232_image_device::call_load()
{
	m_tms9902->rcv_dsr(ASSERT_LINE);
	m_rs232_input_timer->adjust(attotime::zero, 0, attotime::from_msec(10));
	return std::make_pair(std::error_condition(), std::string());
}


void tm990_189_rs232_image_device::call_unload()
{
	m_tms9902->rcv_dsr(CLEAR_LINE);
	m_rs232_input_timer->adjust(attotime::never);
}


/* static TMS9902_RTS_CALLBACK( rts_callback )
{
    tm990189 *state = device->machine().driver_data<tm990189>();
    state->m_rs232_rts = RTS;
    tms9902->set_cts(RTS);
} */

void tm990189_state::xmit_callback(uint8_t data)
{
	uint8_t buf = data;
	if (m_rs232_fp) m_rs232_fp->fwrite(&buf, 1);
}

/*
    External instruction decoding
*/
void tm990189_state::external_operation(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 2: // IDLE
		if (data)
			m_LED_state |= 0x40;
		else
			m_LED_state &= ~0x40;
		break;
	case 3: // RSET
		// Not used on the default board
		break;
	case 5: // CKON: set DECKCONTROL
		m_LED_state |= 0x20;
		m_cass->change_state(CASSETTE_MOTOR_ENABLED,CASSETTE_MASK_MOTOR);
		break;
	case 6: // CKOF: clear DECKCONTROL
		m_LED_state &= ~0x20;
		m_cass->change_state(CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR);
		break;
	case 7: // LREX: trigger LOAD
		hold_load();
		break;
	default: // undefined
		break;
	}
}

/*
    Video Board handling
*/

uint8_t tm990189_v_state::video_vdp_r(offs_t offset)
{
	int reply = 0;

	/* When the tms9980 reads @>2000 or @>2001, it actually does a word access:
	it reads @>2000 first, then @>2001.  According to schematics, both access
	are decoded to the VDP: read accesses are therefore bogus, all the more so
	since the two reads are too close (1us) for the VDP to be able to reload
	the read buffer: the read address pointer is probably incremented by 2, but
	only the first byte is valid.  There is a work around for this problem: all
	you need is reloading the address pointer before each read.  However,
	software always uses the second byte, which is very weird, particularly
	for the status port.  Presumably, since the read buffer has not been
	reloaded, the second byte read from the memory read port is equal to the
	first; however, this explanation is not very convincing for the status
	port.  Still, I do not have any better explanation, so I will stick with
	it. */

	if (offset & 2)
		reply = m_tms9918->register_read();
	else
		reply = m_tms9918->vram_read();

	if (!(offset & 1))
		m_bogus_read_save = reply;
	else
		reply = m_bogus_read_save;

	return reply;
}

void tm990189_v_state::video_vdp_w(offs_t offset, uint8_t data)
{
	if (offset & 1)
	{
		if (offset & 2)
			m_tms9918->register_write(data);
		else
			m_tms9918->vram_write(data);
	}
}

uint8_t tm990189_v_state::video_joy_r()
{
	uint8_t data = m_buttons->read();

	if (m_joy1x_timer->remaining() < attotime::zero)
		data |= 0x01;

	if (m_joy1y_timer->remaining() < attotime::zero)
		data |= 0x02;

	if (m_joy2x_timer->remaining() < attotime::zero)
		data |= 0x08;

	if (m_joy2y_timer->remaining() < attotime::zero)
		data |= 0x10;

	return data;
}

void tm990189_v_state::video_joy_w(uint8_t data)
{
	m_joy1x_timer->reset(attotime::from_usec(m_axes[0]->read()*28+28));
	m_joy1y_timer->reset(attotime::from_usec(m_axes[1]->read()*28+28));
	m_joy2x_timer->reset(attotime::from_usec(m_axes[2]->read()*28+28));
	m_joy2y_timer->reset(attotime::from_usec(m_axes[3]->read()*28+28));
}

/*
// user tms9901 setup
static const tms9901_interface usr9901reset_param =
{
    tms9901_device::INT1 | tms9901_device::INT2 | tms9901_device::INT3 | tms9901_device::INT4 | tms9901_device::INT5 | tms9901_device::INT6,    // only input pins whose state is always known

    // Read handler. Covers all input lines (see tms9901.h)
    DEVCB_NOOP,

    // write handlers
    {
        DEVCB_DRIVER_LINE_MEMBER(tm990189_state, usr9901_led_w<0>),
        DEVCB_DRIVER_LINE_MEMBER(tm990189_state, usr9901_led_w<1>),
        DEVCB_DRIVER_LINE_MEMBER(tm990189_state, usr9901_led_w<2>),
        DEVCB_DRIVER_LINE_MEMBER(tm990189_state, usr9901_led_w<3>),
        DEVCB_NOOP,
        DEVCB_NOOP,
        DEVCB_NOOP,
        DEVCB_NOOP,
        DEVCB_NOOP,
        DEVCB_NOOP,
        DEVCB_NOOP,
        DEVCB_NOOP,
        DEVCB_NOOP,
        DEVCB_NOOP,
        DEVCB_NOOP,
        DEVCB_NOOP
    },

    // interrupt handler
    DEVCB_DRIVER_MEMBER(tm990189_state, usr9901_interrupt_callback)
};
*/

/*
// system tms9901 setup
static const tms9901_interface sys9901reset_param =
{
    0,  // only input pins whose state is always known

    // Read handler. Covers all input lines (see tms9901.h)
    DEVCB_DRIVER_MEMBER(tm990189_state, sys9901_r),

    // write handlers
    {
        DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_digitsel_w<0>),
        DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_digitsel_w<1>),
        DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_digitsel_w<2>),
        DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_digitsel_w<3>),
        DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_segment_w<0>),
        DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_segment_w<1>),
        DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_segment_w<2>),
        DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_segment_w<3>),
        DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_segment_w<4>),
        DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_segment_w<5>),
        DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_segment_w<6>),
        DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_segment_w<7>),
        DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_dsplytrgr_w),
        DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_shiftlight_w),
        DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_spkrdrive_w),
        DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_tapewdata_w)
    },

    // interrupt handler
    DEVCB_DRIVER_MEMBER(tm990189_state, sys9901_interrupt_callback)
};

*/

/*
    Memory map:

    0x0000-0x03ff: 1kb RAM
    0x0400-0x07ff: 1kb onboard expansion RAM
    0x0800-0x0bff or 0x0800-0x0fff: 1kb or 2kb onboard expansion ROM
    0x1000-0x2fff: reserved for offboard expansion
        Only known card: Color Video Board
            0x1000-0x17ff (R): Video board ROM 1
            0x1800-0x1fff (R): Video board ROM 2
            0x2000-0x27ff (R): tms9918 read ports (bogus)
                (address & 2) == 0: data port (bogus)
                (address & 2) == 1: status register (bogus)
            0x2800-0x2fff (R): joystick read port
                d2: joy 2 switch
                d3: joy 2 Y (length of pulse after retrigger is proportional to axis position)
                d4: joy 2 X (length of pulse after retrigger is proportional to axis position)
                d2: joy 1 switch
                d3: joy 1 Y (length of pulse after retrigger is proportional to axis position)
                d4: joy 1 X (length of pulse after retrigger is proportional to axis position)
            0x1801-0x1fff ((address & 1) == 1) (W): joystick write port (retrigger)
            0x2801-0x2fff ((address & 1) == 1) (W): tms9918 write ports
                (address & 2) == 0: data port
                (address & 2) == 1: control register
    0x3000-0x3fff: 4kb onboard ROM
*/

void tm990189_state::tm990_189_memmap(address_map &map)
{
	map(0x0000, 0x07ff).ram();      // RAM
	map(0x0800, 0x0fff).rom();      // extra ROM - application programs with unibug, remaining 2kb of program for university basic
	map(0x1000, 0x2fff).noprw();    // reserved for expansion (RAM and/or tms9918 video controller)
	map(0x3000, 0x3fff).rom();      // main ROM - unibug or university basic
}

void tm990189_v_state::tm990_189_v_memmap(address_map &map)
{
	map(0x0000, 0x07ff).ram();                                          // RAM
	map(0x0800, 0x0fff).rom();                                          // extra ROM - application programs with unibug, remaining 2kb of program for university basic

	map(0x1000, 0x17ff).rom().nopw();                                   // video board ROM 1
	map(0x1800, 0x1fff).rom().w(FUNC(tm990189_v_state::video_joy_w));   // video board ROM 2 and joystick write port
	map(0x2000, 0x27ff).r(FUNC(tm990189_v_state::video_vdp_r)).nopw();  // video board TMS9918 read ports (bogus)
	map(0x2800, 0x2fff).rw(FUNC(tm990189_v_state::video_joy_r), FUNC(tm990189_v_state::video_vdp_w)); // video board joystick read port and TMS9918 write ports

	map(0x3000, 0x3fff).rom();                                          // main ROM - unibug or university basic
}

/*
    CRU map

    The board features one tms9901 for keyboard and sound I/O, another tms9901
    to drive the parallel port and a few LEDs and handle tms9980 interrupts,
    and one optional tms9902 for serial I/O.

    * bits >000->1ff (R12=>000->3fe): first TMS9901: parallel port, a few LEDs,
        interrupts
        - CRU bits 1-5: UINT1* through UINT5* (jumper connectable to parallel
            port or COMINT from tms9902)
        - CRU bit 6: KBINT (interrupt request from second tms9901)
        - CRU bits 7-15: mirrors P15-P7
        - CRU bits 16-31: P0-P15 (parallel port)
            (bits 16, 17, 18 and 19 control LEDs numbered 1, 2, 3 and 4, too)
    * bits >200->3ff (R12=>400->7fe): second TMS9901: display, keyboard and
        sound
        - CRU bits 1-5: KB1*-KB5* (inputs from current keyboard row)
        - CRU bit 6: RDATA (tape in)
        - CRU bits 7-15: mirrors P15-P7
        - CRU bits 16-19: DIGITSELA-DIGITSELD (select current display digit and
            keyboard row)
        - CRU bits 20-27: SEGMENTA*-SEGMENTG* and SEGMENTP* (drives digit
            segments)
        - CRU bit 28: DSPLYTRGR*: used as an alive signal for a watchdog circuit
                      which turns off the display when the program hangs and
                      LED segments would continuously emit light

        - bit 29: SHIFTLIGHT (controls shift light)
        - bit 30: SPKRDRIVE (controls buzzer)
        - bit 31: WDATA (tape out)
    * bits >400->5ff (R12=>800->bfe): optional TMS9902
    * bits >600->7ff (R12=>c00->ffe): reserved for expansion
    * write 0 to bits >1000->17ff: IDLE: flash IDLE LED
    * write 0 to bits >1800->1fff: RSET: not connected, but it is decoded and
        hackers can connect any device they like to this pin
    * write 1 to bits >0800->0fff: CKON: light FWD LED and activates
        DECKCONTROL* signal (part of tape interface)
    * write 1 to bits >1000->17ff: CKOF: light off FWD LED and deactivates
        DECKCONTROL* signal (part of tape interface)
    * write 1 to bits >1800->1fff: LREX: trigger load interrupt

    Keyboard map: see input ports

    Display segment designation:
           a
          ___
         |   |
        f|   |b
         |___|
         | g |
        e|   |c
         |___|  .p
           d
*/

void tm990189_state::tm990_189_cru_map(address_map &map)
{
	map(0x0000, 0x03ff).rw(m_tms9901_usr, FUNC(tms9901_device::read), FUNC(tms9901_device::write));    /* user I/O tms9901 */
	map(0x0400, 0x07ff).rw(m_tms9901_sys, FUNC(tms9901_device::read), FUNC(tms9901_device::write));    /* system I/O tms9901 */
	map(0x0800, 0x0bff).rw(m_tms9902, FUNC(tms9902_device::cruread), FUNC(tms9902_device::cruwrite));   /* optional tms9902 */
}

void tm990189_state::tm990_189(machine_config &config)
{
	/* basic machine hardware */
	TMS9980A(config, m_tms9980a, 8_MHz_XTAL); // clock divided by 4 internally
	m_tms9980a->set_addrmap(AS_PROGRAM, &tm990189_state::tm990_189_memmap);
	m_tms9980a->set_addrmap(AS_IO, &tm990189_state::tm990_189_cru_map);
	m_tms9980a->extop_cb().set(FUNC(tm990189_state::external_operation));

	/* Video hardware */
	config.set_default_layout(layout_tm990189);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	/* Devices */
	CASSETTE(config, "cassette", 0).add_route(ALL_OUTPUTS, "mono", 0.25);

	TMS9901(config, m_tms9901_usr, 8_MHz_XTAL / 4);
	m_tms9901_usr->p_out_cb(0).set(FUNC(tm990189_state::usr9901_led_w<0>));
	m_tms9901_usr->p_out_cb(1).set(FUNC(tm990189_state::usr9901_led_w<1>));
	m_tms9901_usr->p_out_cb(2).set(FUNC(tm990189_state::usr9901_led_w<2>));
	m_tms9901_usr->p_out_cb(3).set(FUNC(tm990189_state::usr9901_led_w<3>));
	m_tms9901_usr->intreq_cb().set(FUNC(tm990189_state::usr9901_interrupt_callback));

	TMS9901(config, m_tms9901_sys, 8_MHz_XTAL / 4);
	m_tms9901_sys->read_cb().set(FUNC(tm990189_state::sys9901_r));
	m_tms9901_sys->p_out_cb(0).set(FUNC(tm990189_state::sys9901_digitsel_w<0>));
	m_tms9901_sys->p_out_cb(1).set(FUNC(tm990189_state::sys9901_digitsel_w<1>));
	m_tms9901_sys->p_out_cb(2).set(FUNC(tm990189_state::sys9901_digitsel_w<2>));
	m_tms9901_sys->p_out_cb(3).set(FUNC(tm990189_state::sys9901_digitsel_w<3>));
	m_tms9901_sys->p_out_cb(4).set(FUNC(tm990189_state::sys9901_segment_w<0>));
	m_tms9901_sys->p_out_cb(5).set(FUNC(tm990189_state::sys9901_segment_w<1>));
	m_tms9901_sys->p_out_cb(6).set(FUNC(tm990189_state::sys9901_segment_w<2>));
	m_tms9901_sys->p_out_cb(7).set(FUNC(tm990189_state::sys9901_segment_w<3>));
	m_tms9901_sys->p_out_cb(8).set(FUNC(tm990189_state::sys9901_segment_w<4>));
	m_tms9901_sys->p_out_cb(9).set(FUNC(tm990189_state::sys9901_segment_w<5>));
	m_tms9901_sys->p_out_cb(10).set(FUNC(tm990189_state::sys9901_segment_w<6>));
	m_tms9901_sys->p_out_cb(11).set(FUNC(tm990189_state::sys9901_segment_w<7>));
	m_tms9901_sys->p_out_cb(12).set(FUNC(tm990189_state::sys9901_dsplytrgr_w));
	m_tms9901_sys->p_out_cb(13).set(FUNC(tm990189_state::sys9901_shiftlight_w));
	m_tms9901_sys->p_out_cb(14).set(FUNC(tm990189_state::sys9901_spkrdrive_w));
	m_tms9901_sys->p_out_cb(15).set(FUNC(tm990189_state::sys9901_tapewdata_w));
	m_tms9901_sys->intreq_cb().set(FUNC(tm990189_state::sys9901_interrupt_callback));

	TMS9902(config, m_tms9902, 8_MHz_XTAL / 4);
	m_tms9902->xmit_cb().set(FUNC(tm990189_state::xmit_callback)); // called when a character is transmitted
	TM990_189_RS232(config, "rs232", 0, m_tms9902);

	timer_device &display_timer(TIMER(config, "display_timer"));
	display_timer.configure_periodic(FUNC(tm990189_state::display_callback), attotime::from_hz(30));
	// Need to delay the timer, or it will spoil the initial LOAD
	// TODO: Fix this, probably inside CPU
	display_timer.set_start_delay(attotime::from_msec(150));
}

void tm990189_v_state::tm990_189_v(machine_config &config)
{
	tm990_189(config);

	/* basic machine hardware */
	m_tms9980a->set_addrmap(AS_PROGRAM, &tm990189_v_state::tm990_189_v_memmap);

	/* video hardware */
	TMS9918(config, m_tms9918, XTAL(10'738'635));
	m_tms9918->set_screen("screen");
	m_tms9918->set_vram_size(0x4000);

	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	config.set_default_layout(layout_tm990189v);
}


/*
  ROM loading
*/
ROM_START(990189)
	/*CPU memory space*/
	ROM_REGION(0x4000, "maincpu", 0 )

	/* extra ROM */
	ROM_LOAD("990-469.u32", 0x0800, 0x0800, CRC(08df7edb) SHA1(fa9751fd2e3e5d7ae03819fc9c7099e2ddd9fb53))

	/* boot ROM */
	ROM_LOAD("990-469.u33", 0x3000, 0x1000, CRC(e9b4ac1b) SHA1(96e88f4cb7a374033cdf3af0dc26ca5b1d55b9f9))
ROM_END

ROM_START(990189v)
	/*CPU memory space*/
	ROM_REGION(0x4000, "maincpu", 0 )

	/* extra ROM */
	ROM_LOAD("990-469.u32", 0x0800, 0x0800, CRC(08df7edb) SHA1(fa9751fd2e3e5d7ae03819fc9c7099e2ddd9fb53))

	/* extension ROM */
	ROM_LOAD_OPTIONAL("demo1000.u13", 0x1000, 0x0800, CRC(c0e16685) SHA1(d0d314134c42fa4682aafbace67f539f67f6ba65))

	/* extension ROM */
	ROM_LOAD_OPTIONAL("demo1800.u11", 0x1800, 0x0800, CRC(8737dc4b) SHA1(b87da7aa4d3f909e70f885c4b36999cc1abf5764))

	/* boot ROM */
	ROM_LOAD("990-469.u33", 0x3000, 0x1000, CRC(e9b4ac1b) SHA1(96e88f4cb7a374033cdf3af0dc26ca5b1d55b9f9))
	/*ROM_LOAD("unibasic.bin", 0x3000, 0x1000, CRC(de4d9744) SHA1(47afe7f6b04b564d2f30f21461e0ed7ea97fba4c) )*/ /* older, partial dump of university BASIC */
ROM_END

#define JOYSTICK_DELTA          10
#define JOYSTICK_SENSITIVITY    100

static INPUT_PORTS_START(tm990_189)

	PORT_START( "LOADINT")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Load interrupt") PORT_CODE(KEYCODE_PRTSCR) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tm990189_state::load_interrupt), 1)

	/* 45-key calculator-like alphanumeric keyboard... */
	PORT_START("LINE0")    /* row 0 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Sp *") PORT_CODE(KEYCODE_SPACE)  PORT_CHAR(' ')  PORT_CHAR('*')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ret '") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)   PORT_CHAR('\'')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("$ =") PORT_CODE(KEYCODE_STOP)    PORT_CHAR('$')  PORT_CHAR('=')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA)   PORT_CHAR(',')  PORT_CHAR('<')

	PORT_START("LINE1")    /* row 1 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("+ (") PORT_CODE(KEYCODE_OPENBRACE)   PORT_CHAR('+')  PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("- )") PORT_CODE(KEYCODE_CLOSEBRACE)  PORT_CHAR('-')  PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@ /") PORT_CODE(KEYCODE_MINUS)   PORT_CHAR('@')  PORT_CHAR('/')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("> %") PORT_CODE(KEYCODE_EQUALS)  PORT_CHAR('>')  PORT_CHAR('%')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0 ^") PORT_CODE(KEYCODE_0)   PORT_CHAR('0')  PORT_CHAR('^')

	PORT_START("LINE2")    /* row 2 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 .") PORT_CODE(KEYCODE_1)   PORT_CHAR('1')  PORT_CHAR('.')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 ;") PORT_CODE(KEYCODE_2)   PORT_CHAR('2')  PORT_CHAR(';')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 :") PORT_CODE(KEYCODE_3)   PORT_CHAR('3')  PORT_CHAR(':')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 ?") PORT_CODE(KEYCODE_4)   PORT_CHAR('4')  PORT_CHAR('?')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 !") PORT_CODE(KEYCODE_5)   PORT_CHAR('5')  PORT_CHAR('!')

	PORT_START("LINE3")    /* row 3 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 _") PORT_CODE(KEYCODE_6)   PORT_CHAR('6')  PORT_CHAR('_')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 \"") PORT_CODE(KEYCODE_7)  PORT_CHAR('7')  PORT_CHAR('\"')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 #") PORT_CODE(KEYCODE_8)   PORT_CHAR('8')  PORT_CHAR('#')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 (ESC)") PORT_CODE(KEYCODE_9)   PORT_CHAR('9')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A (SOH)") PORT_CODE(KEYCODE_A)   PORT_CHAR('A')

	PORT_START("LINE4")    /* row 4 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B (STH)") PORT_CODE(KEYCODE_B)   PORT_CHAR('B')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C (ETX)") PORT_CODE(KEYCODE_C)   PORT_CHAR('C')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D (EOT)") PORT_CODE(KEYCODE_D)   PORT_CHAR('D')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E (ENQ)") PORT_CODE(KEYCODE_E)   PORT_CHAR('E')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F (ACK)") PORT_CODE(KEYCODE_F)   PORT_CHAR('F')

	PORT_START("LINE5")    /* row 5 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G (BEL)") PORT_CODE(KEYCODE_G)   PORT_CHAR('G')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H (BS)") PORT_CODE(KEYCODE_H)    PORT_CHAR('H')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I (HT)") PORT_CODE(KEYCODE_I)    PORT_CHAR('I')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J (LF)") PORT_CODE(KEYCODE_J)    PORT_CHAR('J')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K (VT)") PORT_CODE(KEYCODE_K)    PORT_CHAR('K')

	PORT_START("LINE6")    /* row 6 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L (FF)") PORT_CODE(KEYCODE_L)    PORT_CHAR('L')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M (DEL)") PORT_CODE(KEYCODE_M)   PORT_CHAR('M')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N (SO)") PORT_CODE(KEYCODE_N)    PORT_CHAR('N')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O (SI)") PORT_CODE(KEYCODE_O)    PORT_CHAR('O')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P (DLE)") PORT_CODE(KEYCODE_P)   PORT_CHAR('P')

	PORT_START("LINE7")    /* row 7 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q (DC1)") PORT_CODE(KEYCODE_Q)   PORT_CHAR('Q')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R (DC2)") PORT_CODE(KEYCODE_R)   PORT_CHAR('R')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S (DC3)") PORT_CODE(KEYCODE_S)   PORT_CHAR('S')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T (DC4)") PORT_CODE(KEYCODE_T)   PORT_CHAR('T')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U (NAK)") PORT_CODE(KEYCODE_U)   PORT_CHAR('U')

	PORT_START("LINE8")    /* row 8 */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V <-D") PORT_CODE(KEYCODE_V)     PORT_CHAR('V')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W (ETB)") PORT_CODE(KEYCODE_W)   PORT_CHAR('W')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X (CAN)") PORT_CODE(KEYCODE_X)   PORT_CHAR('X')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y (EM)") PORT_CODE(KEYCODE_Y)    PORT_CHAR('Y')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z ->D") PORT_CODE(KEYCODE_Z)     PORT_CHAR('Z')

	/* analog joysticks (video board only) */

	PORT_START("BUTTONS")   /* joystick 1 & 2 buttons */
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_PLAYER(1)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_PLAYER(2)

	PORT_START("JOY1_X")    /* joystick 1, X axis */
	PORT_BIT( 0x3ff, 0x1aa,  IPT_AD_STICK_X) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0xd2,0x282 ) PORT_PLAYER(1)

	PORT_START("JOY1_Y")    /* joystick 1, Y axis */
	PORT_BIT( 0x3ff, 0x1aa,  IPT_AD_STICK_Y) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0xd2,0x282 ) PORT_PLAYER(1) PORT_REVERSE

	PORT_START("JOY2_X")    /* joystick 2, X axis */
	PORT_BIT( 0x3ff, 0x180,  IPT_AD_STICK_X) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0xd2,0x180 ) PORT_PLAYER(2)

	PORT_START("JOY2_Y")    /* joystick 2, Y axis */
	PORT_BIT( 0x3ff, 0x1aa,  IPT_AD_STICK_Y) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0xd2,0x282 ) PORT_PLAYER(2) PORT_REVERSE
INPUT_PORTS_END


//    YEAR  NAME     PARENT  COMPAT  MACHINE      INPUT      CLASS             INIT        COMPANY              FULLNAME                                                                FLAGS
COMP( 1978, 990189,  0,      0,      tm990_189,   tm990_189, tm990189_state,   empty_init, "Texas Instruments", "TM 990/189 University Board microcomputer",                            0 )
COMP( 1980, 990189v, 990189, 0,      tm990_189_v, tm990_189, tm990189_v_state, empty_init, "Texas Instruments", "TM 990/189 University Board microcomputer with Video Board Interface", 0 )



tmc1800.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

Telmac 2000

PCB Layout
----------

|-----------------------------------------------------------|
|                                                           |
|    |                  4051    4042        2114    2114    |
|   CN1                                                     |
|    |                  4051    4042        2114    2114    |
|                                                           |
|   4011                4013    |--------|  2114    2114    |
|                               |  4515  |                  |
|                       4013    |--------|  2114    2114    |
|                               4042                        |
|                       4011                2114    2114    |
|                               4011                        |
|                       4011                2114    2114    |
|SW1                                                        |
|                       4011    |--------|  2114    2114    |
|                               |  PROM  |                  |
|                       40107   |--------|  2114    2114    |
|   4050    1.75MHz                                         |
|                       |-------------|     2114    2114    |
|                       |   CDP1802   |                     |
|                       |-------------|     2114    2114    |
|           4049                                            |
|                       |-------------|     2114    2114    |
|       4.43MHz         |   CDP1864   |                     |
|                       |-------------|     2114    2114    |
|                                                           |
|               741             4502        2114    2114    |
|  -                                                        |
|  |                            2114        2114    2114    |
| CN2                                                       |
|  |            4001            4051        2114    2114    |
|  -                                                        |
|                                           2114    2114    |
|-----------------------------------------------------------|

Notes:
    All IC's shown.

    PROM    - MMI6341
    2114    - 2114 4096 Bit (1024x4) NMOS Static RAM
    CDP1802 - RCA CDP1802 CMOS 8-Bit Microprocessor @ 1.75 MHz
    CDP1864 - RCA CDP1864CE COS/MOS PAL Compatible Color TV Interface @ 1.75 MHz
    CN1     - keyboard connector
    CN2     - ASTEC RF modulator connector
    SW1     - Run/Reset switch

*/

/*

OSCOM Nano

PCB Layout
----------

OK 30379

|-------------------------------------------------|
|   CN1     CN2     CN3                 7805      |
|                       1.75MHz                   |
|                               741               |
|                       |-------------|         - |
|   741     741   4011  |   CDP1864   |         | |
|                       |-------------|         | |
|                 4013  |-------------|         | |
|                       |   CDP1802   |         | |
|                 4093  |-------------|         | |
|                                               C |
|            4051   4042    4017        4042    N |
|                           |-------|           4 |
|                           |  ROM  |   14556   | |
|                           |-------|           | |
|                           2114    2114        | |
|                                               | |
|                           2114    2114        | |
|                                               - |
|                           2114    2114          |
|                                                 |
|                           2114    2114          |
|-------------------------------------------------|

Notes:
    All IC's shown.

    ROM     - Intersil 5504?
    2114    - 2114UCB 4096 Bit (1024x4) NMOS Static RAM
    CDP1802 - RCA CDP1802E CMOS 8-Bit Microprocessor @ 1.75 MHz
    CDP1864 - RCA CDP1864CE COS/MOS PAL Compatible Color TV Interface @ 1.75 MHz
    CN1     - tape connector
    CN2     - video connector
    CN3     - power connector
    CN4     - expansion connector

Usage:
    Enter operating system with MONITOR key

    Commands:
    B N         Load N pages (of 256 bytes) from tape to current address, e.g. "MONITOR 0000 B 4" loads 4 pages to 0x0000
    F N         Save N pages (of 256 bytes) to tape starting from current address, e.g. "MONITOR 0200 F 4" saves 4 pages starting from 0x0200
    0           Write memory, e.g. "MONITOR 0400 0 A1 A3" writes 0xA1 to 0x0400 and 0xA3 to 0x0401
    A           Read memory, e.g. "MONITOR 0400 A 0 0 0" shows memory contents for 0x0400, 0x0401, 0x0402, 0x0403

    Loading software from tape:
    - Press MONITOR
    - Enter loading address (0000)
    - Press B
    - Enter length of program in pages (4)
    - Press RUN after program has loaded

Demo tape contents:
    Side A

    1. Esittelyohjelma (Demonstration Program) [length A pages]
    2. Reaktioaikatesti (Reaction Time Test) [4]
    3. Kaleidoskooppi (Kaleidoscope) [3] [CHIP-8]
    4. Labyrinttiohjelma (Labyrinth Program) [4]
    5. Labyrinttipeli (Labyrinth Game) [4] [CHIP-8]
    6. Yhteenlaskuohjelma (Addition Program) [4] [CHIP-8]
    7. Miinakenttä (Minefield) [4] [CHIP-8]
    8. Herästyskello (Alarm Clock) [4] [CHIP-8]
    9. Move Loop eli ansapeli [4]
    10. Pingis (Ping Pong) [6] [CHIP-8]
    11. Numeron arvaus (Number Guess) [3] [CHIP-8]
    12. Numeroiden kaato [4] [CHIP-8]
    13. Pyyhkäisypeli, yksinpelattava (Sweeping Game Single Player) [3] [CHIP-8]
    14. Pyyhkäisypeli, kaksinpelattava (Sweeping Game Dual Player) [6] [CHIP-8]
    15. Tikkupeli (Stick Game) [8] [CHIP-8]

    Side B

    16. Ampujaukko (Shooter Man) [4] [CHIP-8]
    17. Ufojen ammunta (UFO Shootout) [3]
    18. Jätkän shakki (Noughs and Crosses) [4] [CHIP-8]
    19. Jackpot [4]  [CHIP-8]
    20. Tankki ja ohjus (Tank and Missile) [5] [CHIP-8]
    21. Parien etsintä (Find the Pairs) [4] [CHIP-8]
    22. Tähtien ammunta (Star Shootout) [4] [CHIP-8]
    23. Vedonlyöntipeli (Betting Game) [4] [CHIP-8]
    24. Päättelytehtävä (Master Mind) [4] [CHIP-8]
    25. Piirtelyohjelma (Doodle) [4]  [CHIP-8]
    26. Säkkijärven polkka [4]
    27. Heksadesimaalikoodien harjoittelu (Hexadecimal Practice) [3]
    28. Histogrammaohjelma (Histogram) [4] [CHIP-8]
    29. M2-ohjelmointikieli (M2 Programming Language) [F]
    30. RAM-muistin testiohjelma (RAM Test) [?]

*/

/*

    TODO:

    - tape input/output
    - tmc2000: add missing keys
    - tmc2000: TOOL-2000 rom banking
    - nano: correct time constant for EF4 RC circuit


    Usage:
    - Same as VIP except the machine begins in the stopped mode.
    - So, to enter the monitor, hold C and press R
    - The support for chip-8 is not yet written, due to missing roms.
    - The screen for nano should be white not red (caused by using only the
      red output of a colour crt controller)
    - The monitor of the tmc1800 is difficult to read because the colour ram
      contains random values.
    - Both nano and tmc1800 seem to "work", but there's insufficient software
      to test with.

*/

#include "emu.h"
#include "tmc1800.h"

#include "sound/beep.h"
#include "speaker.h"


/* Read/Write Handlers */

void tmc1800_state::keylatch_w(uint8_t data)
{
	m_keylatch = data;
}

void osc1000b_state::keylatch_w(uint8_t data)
{
	m_keylatch = data;
}

void tmc2000_state::keylatch_w(uint8_t data)
{
	/*

	    bit     description

	    0       X0
	    1       X1
	    2       X2
	    3       Y0
	    4       Y1
	    5       Y2
	    6       EXP1
	    7       EXP2

	*/

	m_keylatch = data & 0x3f;
}

void nano_state::keylatch_w(uint8_t data)
{
	/*

	    bit     description

	    0       A
	    1       B
	    2       C
	    3       NY0
	    4       NY1
	    5
	    6
	    7

	*/

	m_keylatch = data & 0x1f;
}

void tmc2000_state::bankswitch()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	uint8_t *ram = m_ram->pointer();
	uint8_t *rom = m_rom->base();

	if (m_roc)
	{
		// monitor ROM
		program.install_rom(0x0000, 0x01ff, 0x7e00, rom);
	}
	else
	{
		// RAM
		switch (m_ram->size())
		{
		case 4 * 1024:
			program.install_ram(0x0000, 0x0fff, 0x7000, ram);
			break;

		case 16 * 1024:
			program.install_ram(0x0000, 0x3fff, 0x4000, ram);
			break;

		case 32 * 1024:
			program.install_ram(0x0000, 0x7fff, ram);
			break;
		}
	}

	if (m_rac)
	{
		// color RAM
		program.install_ram(0x8000, 0x81ff, 0x7e00, m_colorram);
		program.unmap_read(0x8000, 0xffff);
	}
	else
	{
		// monitor ROM
		program.install_rom(0x8000, 0x81ff, 0x7e00, rom);
	}
}

void tmc2000_state::bankswitch_w(uint8_t data)
{
	m_roc = 0;
	m_rac = BIT(data, 0);
	bankswitch();

	m_cti->tone_latch_w(data);
}

void nano_state::bankswitch_w(uint8_t data)
{
	/* enable RAM */
	address_space &program = m_maincpu->space(AS_PROGRAM);
	uint8_t *ram = m_ram->pointer();
	program.install_ram(0x0000, 0x0fff, 0x7000, ram);

	/* write to CDP1864 tone latch */
	m_cti->tone_latch_w(data);
}

uint8_t tmc1800_state::dispon_r()
{
	m_vdc->disp_on_w(1);
	m_vdc->disp_on_w(0);

	return 0xff;
}

void tmc1800_state::dispoff_w(uint8_t data)
{
	m_vdc->disp_off_w(1);
	m_vdc->disp_off_w(0);
}

/* Memory Maps */

// Telmac 1800

void tmc1800_state::tmc1800_map(address_map &map)
{
	map(0x0000, 0x07ff).mirror(0x7800).ram();
	map(0x8000, 0x81ff).mirror(0x7e00).rom().region(CDP1802_TAG, 0);
}

void tmc1800_state::tmc1800_io_map(address_map &map)
{
	map(0x01, 0x01).rw(FUNC(tmc1800_state::dispon_r), FUNC(tmc1800_state::dispoff_w));
	map(0x02, 0x02).w(FUNC(tmc1800_state::keylatch_w));
}

// OSCOM 1000B

void osc1000b_state::osc1000b_map(address_map &map)
{
	map(0x0000, 0x07ff).mirror(0x7800).ram();
	map(0x8000, 0x81ff).mirror(0x7e00).rom().region(CDP1802_TAG, 0);
}

void osc1000b_state::osc1000b_io_map(address_map &map)
{
	map(0x02, 0x02).w(FUNC(osc1000b_state::keylatch_w));
}

// Telmac 2000

void tmc2000_state::tmc2000_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff); // RAM / monitor ROM
	map(0x8000, 0xffff); // color RAM / monitor ROM
}

void tmc2000_state::tmc2000_io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x01, 0x01).rw(m_cti, FUNC(cdp1864_device::dispon_r), FUNC(cdp1864_device::step_bgcolor_w));
	map(0x02, 0x02).w(FUNC(tmc2000_state::keylatch_w));
	map(0x04, 0x04).r(m_cti, FUNC(cdp1864_device::dispoff_r)).w(FUNC(tmc2000_state::bankswitch_w));
}

// OSCOM Nano

void nano_state::nano_map(address_map &map)
{
	map(0x0000, 0x7fff); // RAM / monitor ROM
	map(0x8000, 0x81ff).mirror(0x7e00).rom().region(CDP1802_TAG, 0);
}

void nano_state::nano_io_map(address_map &map)
{
	map(0x01, 0x01).rw(m_cti, FUNC(cdp1864_device::dispon_r), FUNC(cdp1864_device::step_bgcolor_w));
	map(0x02, 0x02).w(FUNC(nano_state::keylatch_w));
	map(0x04, 0x04).r(m_cti, FUNC(cdp1864_device::dispoff_r)).w(FUNC(nano_state::bankswitch_w));
}

/* Input Ports */

static INPUT_PORTS_START( tmc1800 )
	PORT_START("Y0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7')

	PORT_START("Y1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("RUN")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Run/Reset") PORT_CODE(KEYCODE_R) PORT_TOGGLE
INPUT_PORTS_END

INPUT_CHANGED_MEMBER( tmc2000_state::run_pressed )
{
	if (oldval && !newval)
	{
		machine_reset();
	}
}

static INPUT_PORTS_START( tmc2000 )
	PORT_START("Y0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7')

	PORT_START("Y1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("RUN")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Run/Reset") PORT_CODE(KEYCODE_R) PORT_TOGGLE PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tmc2000_state::run_pressed), 0)

	PORT_START("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')

	PORT_START("Y3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')

	PORT_START("Y4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )
INPUT_PORTS_END

INPUT_CHANGED_MEMBER( nano_state::run_pressed )
{
	if (oldval && !newval)
	{
		machine_reset();
	}
}

INPUT_CHANGED_MEMBER( nano_state::monitor_pressed )
{
	if (oldval && !newval)
	{
		machine_reset();

		m_maincpu->set_input_line(COSMAC_INPUT_LINE_EF4, CLEAR_LINE);
	}
	else if (!oldval && newval)
	{
		// TODO: what are the correct values?
		int t = RES_K(27) * CAP_U(1) * 1000; // t = R26 * C1
		m_ef4_timer->adjust(attotime::from_msec(t));
	}
}

static INPUT_PORTS_START( nano )
	PORT_START("NY0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7')

	PORT_START("NY1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("RUN")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("RUN") PORT_CODE(KEYCODE_R) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(nano_state::run_pressed), 0)

	PORT_START("MONITOR")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("MONITOR") PORT_CODE(KEYCODE_M) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(nano_state::monitor_pressed), 0)
INPUT_PORTS_END

/* CDP1802 Interfaces */

// Telmac 1800

int tmc1800_state::clear_r()
{
	return BIT(m_run->read(), 0);
}

int tmc1800_state::ef2_r()
{
	return m_cassette->input() < 0;
}

int tmc1800_state::ef3_r()
{
	return CLEAR_LINE; // TODO
}

void tmc1800_state::q_w(int state)
{
	m_cassette->output(state ? 1.0 : -1.0);
}

// Oscom 1000B

int osc1000b_state::clear_r()
{
	return BIT(m_run->read(), 0);
}

int osc1000b_state::ef2_r()
{
	return m_cassette->input() < 0;
}

int osc1000b_state::ef3_r()
{
	return CLEAR_LINE; // TODO
}

void osc1000b_state::q_w(int state)
{
	m_cassette->output(state ? 1.0 : -1.0);
}

// Telmac 2000

int tmc2000_state::clear_r()
{
	return BIT(m_run->read(), 0);
}

int tmc2000_state::ef2_r()
{
	return (m_cassette)->input() < 0;
}

int tmc2000_state::ef3_r()
{
	uint8_t data = ~m_key_row[m_keylatch / 8]->read();

	return BIT(data, m_keylatch % 8);
}

void tmc2000_state::q_w(int state)
{
	/* CDP1864 audio output enable */
	m_cti->aoe_w(state);

	/* set Q led status */
	m_led = state ? 1 : 0;

	/* tape output */
	m_cassette->output(state ? 1.0 : -1.0);
}

void tmc2000_state::dma_w(offs_t offset, uint8_t data)
{
	m_color = ~(m_colorram[offset & 0x1ff]) & 0x07;

	m_cti->con_w(0); // HACK
	m_cti->dma_w(data);
}

// OSCOM Nano

int nano_state::clear_r()
{
	int run = BIT(m_run->read(), 0);
	int monitor = BIT(m_monitor->read(), 0);

	return run && monitor;
}

int nano_state::ef2_r()
{
	return m_cassette->input() < 0;
}

int nano_state::ef3_r()
{
	uint8_t data = 0xff;

	if (!BIT(m_keylatch, 3)) data &= m_ny0->read();
	if (!BIT(m_keylatch, 4)) data &= m_ny1->read();

	return !BIT(data, m_keylatch & 0x07);
}

void nano_state::q_w(int state)
{
	/* CDP1864 audio output enable */
	m_cti->aoe_w(state);

	/* set Q led status */
	m_led = state ? 1 : 0;

	/* tape output */
	m_cassette->output(state ? 1.0 : -1.0);
}

/* Machine Initialization */

// Telmac 1800

void tmc1800_state::machine_start()
{
	/* register for state saving */
	save_item(NAME(m_keylatch));
}

void tmc1800_state::machine_reset()
{
	/* reset CDP1861 */
	m_vdc->reset();

	/* initialize beeper */
	m_beeper->set_state(0);
	m_beeper->set_clock(0);
}

// OSCOM 1000B

void osc1000b_state::machine_start()
{
	/* register for state saving */
	save_item(NAME(m_keylatch));
}

void osc1000b_state::machine_reset()
{
}

// Telmac 2000

void tmc2000_state::machine_start()
{
	m_led.resolve();

	// randomize color RAM contents
	for (uint16_t addr = 0; addr < TMC2000_COLORRAM_SIZE; addr++)
	{
		m_colorram[addr] = machine().rand() & 0xff;
	}

	// state saving
	save_item(NAME(m_keylatch));
	save_item(NAME(m_rac));
	save_item(NAME(m_roc));
}

void tmc2000_state::machine_reset()
{
	// reset CDP1864
	m_cti->reset();

	// banking
	m_roc = 1;
	m_rac = 0;
	bankswitch();
}

// OSCOM Nano

TIMER_CALLBACK_MEMBER(nano_state::assert_ef4)
{
	m_maincpu->set_input_line(COSMAC_INPUT_LINE_EF4, ASSERT_LINE);
}

void nano_state::machine_start()
{
	m_led.resolve();

	/* register for state saving */
	save_item(NAME(m_keylatch));

	/* allocate timers */
	m_ef4_timer = timer_alloc(FUNC(nano_state::assert_ef4), this);
}

void nano_state::machine_reset()
{
	/* assert EF4 */
	m_ef4_timer->adjust(attotime::zero);

	/* reset CDP1864 */
	m_cti->reset();

	/* enable ROM */
	address_space &program = m_maincpu->space(AS_PROGRAM);
	uint8_t *rom = m_rom->base();
	program.install_rom(0x0000, 0x01ff, 0x7e00, rom);
}

/* Machine Drivers */

QUICKLOAD_LOAD_MEMBER(tmc1800_base_state::quickload_cb)
{
	int const size = image.length();

	if (size > m_ram->size()) // FIXME: comparing size to RAM size, but loading to ROM - seems incorrect
	{
		return std::make_pair(image_error::INVALIDLENGTH, std::string());
	}

	uint8_t *const ptr = m_rom->base();
	image.fread(ptr, size);

	return std::make_pair(std::error_condition(), std::string());
}

void tmc1800_state::tmc1800(machine_config &config)
{
	// basic system hardware
	CDP1802(config, m_maincpu, 1.75_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &tmc1800_state::tmc1800_map);
	m_maincpu->set_addrmap(AS_IO, &tmc1800_state::tmc1800_io_map);
	m_maincpu->wait_cb().set_constant(1);
	m_maincpu->clear_cb().set(FUNC(tmc1800_state::clear_r));
	m_maincpu->ef2_cb().set(FUNC(tmc1800_state::ef2_r));
	m_maincpu->ef3_cb().set(FUNC(tmc1800_state::ef3_r));
	m_maincpu->q_cb().set(FUNC(tmc1800_state::q_w));
	m_maincpu->dma_wr_cb().set(m_vdc, FUNC(cdp1861_device::dma_w));

	// video hardware
	tmc1800_video(config);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 0).add_route(ALL_OUTPUTS, "mono", 0.25);

	// devices
	QUICKLOAD(config, "quickload", "bin").set_load_callback(FUNC(tmc1800_base_state::quickload_cb));
	// The following can be enabled when the missing bios rom is found
	//quickload_image_device &quickload(QUICKLOAD(config, "quickload", "bin,c8", attotime::from_seconds(2)));
	//quickload.set_load_callback(FUNC(tmc1800_base_state_state::quickload_cb));
	//quickload.set_interface("chip8quik");
	//SOFTWARE_LIST(config, "quik_list").set_original("chip8_quik").set_filter("T"); // filter unknown until it can be tested

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	// internal ram
	RAM(config, RAM_TAG).set_default_size("2K").set_extra_options("4K");
}

void osc1000b_state::osc1000b(machine_config &config)
{
	// basic system hardware
	CDP1802(config, m_maincpu, 1.75_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &osc1000b_state::osc1000b_map);
	m_maincpu->set_addrmap(AS_IO, &osc1000b_state::osc1000b_io_map);
	m_maincpu->wait_cb().set_constant(1);
	m_maincpu->clear_cb().set(FUNC(osc1000b_state::clear_r));
	m_maincpu->ef2_cb().set(FUNC(osc1000b_state::ef2_r));
	m_maincpu->ef3_cb().set(FUNC(osc1000b_state::ef3_r));
	m_maincpu->q_cb().set(FUNC(osc1000b_state::q_w));

	// video hardware
	osc1000b_video(config);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 0).add_route(ALL_OUTPUTS, "mono", 0.25);

	// devices
	QUICKLOAD(config, "quickload", "bin").set_load_callback(FUNC(tmc1800_base_state::quickload_cb));
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	// internal ram
	RAM(config, RAM_TAG).set_default_size("2K").set_extra_options("4K");
}

void tmc2000_state::tmc2000(machine_config &config)
{
	// basic system hardware
	CDP1802(config, m_maincpu, 1.75_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &tmc2000_state::tmc2000_map);
	m_maincpu->set_addrmap(AS_IO, &tmc2000_state::tmc2000_io_map);
	m_maincpu->wait_cb().set_constant(1);
	m_maincpu->clear_cb().set(FUNC(tmc2000_state::clear_r));
	m_maincpu->ef2_cb().set(FUNC(tmc2000_state::ef2_r));
	m_maincpu->ef3_cb().set(FUNC(tmc2000_state::ef3_r));
	m_maincpu->q_cb().set(FUNC(tmc2000_state::q_w));
	m_maincpu->dma_wr_cb().set(FUNC(tmc2000_state::dma_w));

	// video hardware
	tmc2000_video(config);

	// devices
	QUICKLOAD(config, "quickload", "bin").set_load_callback(FUNC(tmc1800_base_state::quickload_cb));
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	// internal ram
	RAM(config, RAM_TAG).set_default_size("4K").set_extra_options("16K,32K");
}

void nano_state::nano(machine_config &config)
{
	// basic system hardware
	CDP1802(config, m_maincpu, 1.75_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &nano_state::nano_map);
	m_maincpu->set_addrmap(AS_IO, &nano_state::nano_io_map);
	m_maincpu->wait_cb().set_constant(1);
	m_maincpu->clear_cb().set(FUNC(nano_state::clear_r));
	m_maincpu->ef2_cb().set(FUNC(nano_state::ef2_r));
	m_maincpu->ef3_cb().set(FUNC(nano_state::ef3_r));
	m_maincpu->q_cb().set(FUNC(nano_state::q_w));
	m_maincpu->dma_wr_cb().set(m_cti, FUNC(cdp1864_device::dma_w));

	// video hardware
	nano_video(config);

	// devices
	QUICKLOAD(config, "quickload", "bin").set_load_callback(FUNC(tmc1800_base_state::quickload_cb));
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	// internal ram
	RAM(config, RAM_TAG).set_default_size("4K");
}

/* ROMs */

ROM_START( tmc1800 )
	ROM_REGION( 0x200, CDP1802_TAG, 0 )
	ROM_LOAD( "mmi6341-1.ic2", 0x000, 0x200, NO_DUMP ) // equivalent to 82S141
ROM_END

ROM_START( osc1000b )
	ROM_REGION( 0x200, CDP1802_TAG, 0 )
	ROM_LOAD( "mmi6341-1.ic2", 0x000, 0x0200, NO_DUMP ) // equivalent to 82S141

	ROM_REGION( 0x400, "gfx1", 0 )
	ROM_LOAD( "mmi6349.5d", 0x000, 0x200, NO_DUMP ) // equivalent to 82S147
	ROM_LOAD( "mmi6349.5c", 0x200, 0x200, NO_DUMP ) // equivalent to 82S147
ROM_END

ROM_START( tmc2000 )
	ROM_REGION( 0x800, CDP1802_TAG, 0 )
	ROM_SYSTEM_BIOS( 0, "prom200", "PROM N:o 200" )
	ROMX_LOAD( "200.m5",    0x000, 0x200, BAD_DUMP CRC(79da3221) SHA1(008da3ef4f69ab1a493362dfca856375b19c94bd), ROM_BIOS(0) ) // typed in from the manual
	ROM_SYSTEM_BIOS( 1, "prom202", "PROM N:o 202" )
	ROMX_LOAD( "202.m5",    0x000, 0x200, NO_DUMP, ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "tool2000", "TOOL-2000" )
	ROMX_LOAD( "tool2000",  0x000, 0x800, NO_DUMP, ROM_BIOS(2) )
ROM_END

ROM_START( nano )
	ROM_REGION( 0x200, CDP1802_TAG, 0 )
	ROM_LOAD( "mmi6349.ic", 0x000, 0x200, BAD_DUMP CRC(1ec1b432) SHA1(ac41f5e38bcd4b80bd7a5b277a2c600899fd5fb8) ) // equivalent to 82S141
ROM_END

/* System Drivers */

//    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT    CLASS           INIT          COMPANY        FULLNAME       FLAGS
COMP( 1977, tmc1800,  0,       0,      tmc1800,  tmc1800, tmc1800_state,  empty_init,   "Telercas Oy", "Telmac 1800", MACHINE_NOT_WORKING )
COMP( 1977, osc1000b, tmc1800, 0,      osc1000b, tmc1800, osc1000b_state, empty_init,   "OSCOM Oy",    "OSCOM 1000B", MACHINE_NOT_WORKING )
COMP( 1980, tmc2000,  0,       0,      tmc2000,  tmc2000, tmc2000_state,  empty_init,   "Telercas Oy", "Telmac 2000", MACHINE_SUPPORTS_SAVE )
COMP( 1980, nano,     tmc2000, 0,      nano,     nano,    nano_state,     empty_init,   "OSCOM Oy",    "OSCOM Nano",  MACHINE_WRONG_COLORS | MACHINE_SUPPORTS_SAVE )



tmc2000e.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

    Telmac 2000E
    ------------
    (c) 1980 Telercas Oy, Finland

    CPU:        CDP1802A    1.75 MHz
    RAM:        8 KB
    ROM:        8 KB

    Video:      CDP1864     1.75 MHz
    Color RAM:  1 KB

    Colors:     8 fg, 4 bg
    Resolution: 64x192
    Sound:      frequency control, volume on/off
    Keyboard:   ASCII (RCA VP-601/VP-611), KB-16/KB-64

    SBASIC:     24.0


    Telmac TMC-121/111/112
    ----------------------
    (c) 198? Telercas Oy, Finland

    CPU:        CDP1802A    ? MHz

    Built from Telmac 2000 series cards. Huge metal box.

*/

#include "emu.h"
#include "tmc2000e.h"

#include "screen.h"
#include "speaker.h"


/* Read/Write Handlers */

uint8_t tmc2000e_state::vismac_r()
{
	return 0;
}

void tmc2000e_state::vismac_w(uint8_t data)
{
}

uint8_t tmc2000e_state::floppy_r()
{
	return 0;
}

void tmc2000e_state::floppy_w(uint8_t data)
{
}

uint8_t tmc2000e_state::ascii_keyboard_r()
{
	return 0;
}

uint8_t tmc2000e_state::io_r()
{
	return 0;
}

void tmc2000e_state::io_w(uint8_t data)
{
}

void tmc2000e_state::io_select_w(uint8_t data)
{
}

void tmc2000e_state::keyboard_latch_w(uint8_t data)
{
	m_keylatch = data;
}

/* Memory Maps */

void tmc2000e_state::tmc2000e_map(address_map &map)
{
	map(0x0000, 0x1fff).ram();
	map(0xc000, 0xdfff).rom();
	map(0xfc00, 0xffff).writeonly().share("colorram");
}

void tmc2000e_state::tmc2000e_io_map(address_map &map)
{
	map(0x01, 0x01).w(m_cti, FUNC(cdp1864_device::tone_latch_w));
	map(0x02, 0x02).w(m_cti, FUNC(cdp1864_device::step_bgcolor_w));
	map(0x03, 0x03).rw(FUNC(tmc2000e_state::ascii_keyboard_r), FUNC(tmc2000e_state::keyboard_latch_w));
	map(0x04, 0x04).rw(FUNC(tmc2000e_state::io_r), FUNC(tmc2000e_state::io_w));
	map(0x05, 0x05).rw(FUNC(tmc2000e_state::vismac_r), FUNC(tmc2000e_state::vismac_w));
	map(0x06, 0x06).rw(FUNC(tmc2000e_state::floppy_r), FUNC(tmc2000e_state::floppy_w));
	map(0x07, 0x07).portr("DSW0").w(FUNC(tmc2000e_state::io_select_w));
}

/* Input Ports */

static INPUT_PORTS_START( tmc2000e )
	PORT_START("Y0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("Y7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("DSW0")  // System Configuration DIPs
	PORT_DIPNAME( 0x80, 0x00, "Keyboard Type" )
	PORT_DIPSETTING(    0x00, "ASCII" )
	PORT_DIPSETTING(    0x80, "Matrix" )
	PORT_DIPNAME( 0x40, 0x00, "Operating System" )
	PORT_DIPSETTING(    0x00, "TOOL-2000-E" )
	PORT_DIPSETTING(    0x40, "Load from disk" )
	PORT_DIPNAME( 0x30, 0x00, "Display Interface" )
	PORT_DIPSETTING(    0x00, "PAL" )
	PORT_DIPSETTING(    0x10, "CDG-80" )
	PORT_DIPSETTING(    0x20, "VISMAC" )
	PORT_DIPSETTING(    0x30, "UART" )
	PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("RUN")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Run/Reset") PORT_CODE(KEYCODE_R) PORT_TOGGLE
INPUT_PORTS_END

/* Video */

int tmc2000e_state::rdata_r()
{
	return BIT(m_color, 2);
}

int tmc2000e_state::bdata_r()
{
	return BIT(m_color, 1);
}

int tmc2000e_state::gdata_r()
{
	return BIT(m_color, 0);
}

/* CDP1802 Interface */

int tmc2000e_state::clear_r()
{
	return BIT(m_run->read(), 0);
}

int tmc2000e_state::ef2_r()
{
	return m_cassette->input() < 0;
}

int tmc2000e_state::ef3_r()
{
	uint8_t data = ~(m_key_row[m_keylatch / 8])->read();

	return BIT(data, m_keylatch % 8);
}

void tmc2000e_state::q_w(int state)
{
	// turn CDP1864 sound generator on/off
	m_cti->aoe_w(state);

	// set Q led status
	m_led = state ? 1 : 0;

	// tape out
	m_cassette->output(state ? -1.0 : +1.0);

	// floppy control (FDC-6)
}

void tmc2000e_state::dma_w(offs_t offset, uint8_t data)
{
	m_color = (m_colorram[offset & 0x3ff]) & 0x07; // 0x04 = R, 0x02 = B, 0x01 = G

	m_cti->con_w(0); // HACK
	m_cti->dma_w(data);
}


/* Machine Initialization */

void tmc2000e_state::machine_start()
{
	m_led.resolve();

	/* register for state saving */
	save_item(NAME(m_cdp1864_efx));
	save_item(NAME(m_keylatch));
}

void tmc2000e_state::machine_reset()
{
	m_cti->reset();

	// reset program counter to 0xc000
}

/* Machine Drivers */

void tmc2000e_state::tmc2000e(machine_config &config)
{
	// basic system hardware
	CDP1802(config, m_maincpu, 1.75_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &tmc2000e_state::tmc2000e_map);
	m_maincpu->set_addrmap(AS_IO, &tmc2000e_state::tmc2000e_io_map);
	m_maincpu->wait_cb().set_constant(1);
	m_maincpu->clear_cb().set(FUNC(tmc2000e_state::clear_r));
	m_maincpu->ef2_cb().set(FUNC(tmc2000e_state::ef2_r));
	m_maincpu->ef3_cb().set(FUNC(tmc2000e_state::ef3_r));
	m_maincpu->q_cb().set(FUNC(tmc2000e_state::q_w));
	m_maincpu->dma_wr_cb().set(FUNC(tmc2000e_state::dma_w));

	// video hardware
	SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	CDP1864(config, m_cti, 1.75_MHz_XTAL).set_screen(SCREEN_TAG);
	m_cti->inlace_cb().set_constant(0);
	m_cti->int_cb().set_inputline(m_maincpu, COSMAC_INPUT_LINE_INT);
	m_cti->dma_out_cb().set_inputline(m_maincpu, COSMAC_INPUT_LINE_DMAOUT);
	m_cti->efx_cb().set_inputline(m_maincpu, COSMAC_INPUT_LINE_EF1);
	m_cti->rdata_cb().set(FUNC(tmc2000e_state::rdata_r));
	m_cti->bdata_cb().set(FUNC(tmc2000e_state::bdata_r));
	m_cti->gdata_cb().set(FUNC(tmc2000e_state::gdata_r));
	m_cti->set_chrominance(RES_K(2.2), RES_K(1), RES_K(5.1), RES_K(4.7)); // unverified
	m_cti->add_route(ALL_OUTPUTS, "mono", 0.25);

	/* devices */
	CASSETTE(config, m_cassette).set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("8K").set_extra_options("40K");
}

/* ROMs */

ROM_START( tmc2000e )
	ROM_REGION( 0x10000, CDP1802_TAG, 0 )
	ROM_LOAD( "1", 0xc000, 0x0800, NO_DUMP )
	ROM_LOAD( "2", 0xc800, 0x0800, NO_DUMP )
	ROM_LOAD( "3", 0xd000, 0x0800, NO_DUMP )
	ROM_LOAD( "4", 0xd800, 0x0800, NO_DUMP )
ROM_END

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY        FULLNAME        STATE
COMP( 1980, tmc2000e, 0,      0,      tmc2000e, tmc2000e, tmc2000e_state, empty_init, "Telercas Oy", "Telmac 2000E", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



tmc600.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

Telmac TMC-600

PCB Layout
----------

HP14782-1

            CN2                    CN3
        |---------|     |------------------------|
|-------|---------|-----|------------------------|------------------------------------------|
|  CN1                                                  CN4 SW1       CN5 CN6 P1 SW2 CN7    |
|         |-------|                                4049         T3                          |
| T1 K1   |CDP1852|         4050  4050  4050            40107 T2         C1  C2 8.867238MHz |
|         |-------|         |-------|                                     5.6260MHz T4      |
| MC1374                    |CDP1852|                               ROM5    |---------|     |
|                           |-------|                                       | CDP1870 |     |
|    K2                                                             ROM4    |---------|     |
|                       |-------------|       4050   5114   5114                            |
|           4013        |   CDP1802   |       4050   5114   5114    ROM3    4030  ROM6  4060|
|           4013 3.57MHz|-------------|       4050   5114   5114                            |
|           4081  40106                       4051   5114   5114    ROM2    4076  5114  4011|
|                                             4556   5114   5114          CDP1856 5114      |
|                   |-------|                        5114   5114    ROM1  CDP1856 5114  4076|
|           LS1     |CDP1852| CDP1853       CDP1856  5114   5114                            |
|                   |-------|               CDP1856  5114   5114    ROM0    |---------| 4011|
|                                                                           | CDP1869 |     |
|   7805            4051      4051                                          |---------|     |
|                                                                                           |
|                     |--CN8--|                                                             |
|-------------------------------------------------------------------------------------------|

Notes:
    All IC's shown. TMCP-300 and TMC-700 expansions have been installed.

    ROM0-5  - Toshiba TMM2732DI 4Kx8 EPROM
    ROM6    - Hitachi HN462732G 4Kx8 EPROM
    5114    - RCA MWS5114E1 1024-Word x 4-Bit LSI Static RAM
    MC1374  - Motorola MC1374P TV Modulator
    CDP1802 - RCA CDP1802BE CMOS 8-Bit Microprocessor running at 3.57MHz
    CDP1852 - RCA CDP1852CE Byte-Wide Input/Output Port
    CDP1853 - RCA CDP1853CE N-Bit 1 of 8 Decoder
    CDP1856 - RCA CDP1856CE 4-Bit Memory Buffer
    CDP1869 - RCA CDP1869CE Video Interface System (VIS) Address and Sound Generator
    CDP1870 - RCA CDP1870CE Video Interface System (VIS) Color Video (DOT XTAL at 5.6260MHz, CHROM XTAL at 8.867238MHz)
    CN1     - RF connector [TMC-700]
    CN2     - 10x2 pin printer connector [TMC-700]
                    GND   1  2  D0
                    GND   3  4  D1
                    GND   5  6  D2
                    GND   7  8  D3
                    GND   9 10  D4
                    GND  11 12  D5
                    GND  13 14  D6
                    GND  15 16  D7
                    GND  17 18  BUSY
                    GND  19 20  _STROBE
    CN3     - 32x3 pin EURO connector
    CN4     - DIN5D tape connector
                1   input (500 mV / 47 Kohm)
                2   GND
                3   output (580 mV / 47 Kohm)
                4   input (500 mV / 47 Kohm)
                5   output (580 mV / 47 Kohm)
    CN5     - DIN5X video connector
                1   GND
                2   GND
                3   composite video output (75 ohm)
                4   GND
                5   composite video output (75 ohm)
    CN6     - DIN2 power connector
                1   input 8..12V DC..400Hz 300mA
                2   GND
    CN7     - DIN5D audio connector [TMCP-300]
                1   N/C
                2   GND
                3   mono audio output
                4   N/C
                5   mono audio output
    CN8     - 10x2 pin keyboard connector
    SW1     - RUN/STOP switch (left=run, right=stop)
    SW2     - internal speaker/external audio switch [TMCP-300]
    P1      - color phase lock adjustment potentiometer
    C1      - dot oscillator adjustment variable capacitor
    C2      - chroma oscillator adjustment variable capacitor
    T1      - RF signal strength adjustment potentiometer [TMC-700]
    T2      - tape recording level adjustment potentiometer (0.57 V p-p)
    T3      - video output level adjustment potentiometer (1 V p-p)
    T4      - video synchronization pulse adjustment potentiometer
    K1      - RF signal quality adjustment variable inductor [TMC-700]
    K2      - RF channel adjustment variable inductor (VHF I) [TMC-700]
    LS1     - loudspeaker

*/

/*

    TODO

    - screen update is too fast
    - cursor on text should blink as dark blue
    - PRWNOISE and PRBEEP return wrong values
    - CDP1869 white noise
    - connect expansion bus
    - series I ROMs
    - DOS ROMs

*/

#include "emu.h"
#include "tmc600.h"

#include "utf8.h"


//**************************************************************************
//  I/O
//**************************************************************************

uint8_t tmc600_state::rtc_r()
{
	m_rtc_int = m_vismac_reg_latch >> 3;

	return 0;
}

void tmc600_state::printer_w(uint8_t data)
{
	m_centronics->write_data0(BIT(data, 0));
	m_centronics->write_data1(BIT(data, 1));
	m_centronics->write_data2(BIT(data, 2));
	m_centronics->write_data3(BIT(data, 3));
	m_centronics->write_data4(BIT(data, 4));
	m_centronics->write_data5(BIT(data, 5));
	m_centronics->write_data6(BIT(data, 6));
	m_centronics->write_data7(BIT(data, 7));

	m_centronics->write_strobe(0);
	m_centronics->write_strobe(1);
}

int tmc600_state::ef2_r()
{
	return m_cassette->input() < 0;
}

int tmc600_state::ef3_r()
{
	return !BIT(m_key_row[(m_out3 >> 3) & 0x07]->read(), m_out3 & 0x07);
}

void tmc600_state::q_w(int state)
{
	m_cassette->output(state ? +1.0 : -1.0);
}

void tmc600_state::sc_w(uint8_t data)
{
	if (data == COSMAC_STATE_CODE_S3_INTERRUPT) {
		m_maincpu->int_w(CLEAR_LINE);
	}
}

void tmc600_state::out3_w(uint8_t data)
{
	m_out3 = data;
}

QUICKLOAD_LOAD_MEMBER(tmc600_state::quickload_cb)
{
	int size = image.length();

	if (size < 16)
		return std::make_pair(image_error::INVALIDLENGTH, "Image is too short");

	if ((size - 16) > (m_ram->size() - 0x300))
		return std::make_pair(image_error::INVALIDLENGTH, "Image is larger than RAM");

	address_space &program = m_maincpu->space(AS_PROGRAM);

	image.fseek(0x5, SEEK_SET);
	image.fread(program.get_write_ptr(0x6181), 4); // DEFUS and EOP
	image.fread(program.get_write_ptr(0x6192), 4); // STRING and ARRAY

	image.fseek(0x9, SEEK_SET);
	image.fread(program.get_write_ptr(0x6199), 2); // EOD

	image.fseek(0xf, SEEK_SET);
	image.fread(program.get_write_ptr(0x6300), size); // program

	return std::make_pair(std::error_condition(), std::string());
}


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void tmc600_state::tmc600_map(address_map &map)
{
	map(0x0000, 0x5fff).rom();
	map(0x6000, 0x7fff).ram();
	map(0xf400, 0xf7ff).m(m_vis, FUNC(cdp1869_device::char_map));
	map(0xf800, 0xffff).m(m_vis, FUNC(cdp1869_device::page_map));
}

void tmc600_state::tmc600_io_map(address_map &map)
{
	map(0x03, 0x03).w(m_bwio, FUNC(cdp1852_device::write));
	map(0x04, 0x04).w(CDP1852_TMC700_TAG, FUNC(cdp1852_device::write));
	map(0x05, 0x05).rw(FUNC(tmc600_state::rtc_r), FUNC(tmc600_state::vismac_data_w));
//  map(0x06, 0x06).w(FUNC(tmc600_state::floppy_w);
	map(0x07, 0x07).w(FUNC(tmc600_state::vismac_register_w));
}


//**************************************************************************
//  INPUT PORTS
//**************************************************************************

static INPUT_PORTS_START( tmc600 )
	PORT_START("Y0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0') PORT_CHAR('_')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("Y1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')

	PORT_START("Y3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O')

	PORT_START("Y4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W')

	PORT_START("Y5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(U'Å')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(U'Ä')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(U'Ö')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BREAK") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END),3)

	PORT_START("Y6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DEL") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC),27)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("E2") PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(RALT))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL1") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL2") PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("E1") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("Y7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("(unknown)") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LINE FEED") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CHAR(10)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\u2191") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\u2192") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\u2193") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\u2190") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))

	PORT_START("RUN")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Run/Stop") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_TOGGLE PORT_WRITE_LINE_DEVICE_MEMBER(CDP1802_TAG, FUNC(cosmac_device::clear_w))
INPUT_PORTS_END


//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

void tmc600_state::tmc600(machine_config &config)
{
	// CPU
	cdp1802_device &cpu(CDP1802(config, CDP1802_TAG, 3.57_MHz_XTAL));
	cpu.set_addrmap(AS_PROGRAM, &tmc600_state::tmc600_map);
	cpu.set_addrmap(AS_IO, &tmc600_state::tmc600_io_map);
	cpu.wait_cb().set_constant(1);
	cpu.ef2_cb().set(FUNC(tmc600_state::ef2_r));
	cpu.ef3_cb().set(FUNC(tmc600_state::ef3_r));
	cpu.q_cb().set(FUNC(tmc600_state::q_w));
	cpu.sc_cb().set(FUNC(tmc600_state::sc_w));
	cpu.tpb_cb().set(CDP1852_KB_TAG, FUNC(cdp1852_device::clock_w));
	cpu.tpb_cb().append(CDP1852_TMC700_TAG, FUNC(cdp1852_device::clock_w));

	// sound and video hardware
	tmc600_video(config);

	// keyboard output latch
	CDP1852(config, m_bwio); // clock is CDP1802 TPB
	m_bwio->mode_cb().set_constant(1);
	m_bwio->do_cb().set(FUNC(tmc600_state::out3_w));

	// address bus demux for expansion bus
	cdp1852_device &demux(CDP1852(config, CDP1852_BUS_TAG)); // clock is expansion bus TPA
	demux.mode_cb().set_constant(0);

	// printer output latch
	cdp1852_device &prtout(CDP1852(config, CDP1852_TMC700_TAG)); // clock is CDP1802 TPB
	prtout.mode_cb().set_constant(1);
	prtout.do_cb().set(FUNC(tmc600_state::printer_w));

	// printer connector
	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(CDP1802_TAG, FUNC(cosmac_device::ef4_w)).exor(1);

	// cassette
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);

	// quickload
	quickload_image_device &quickload(QUICKLOAD(config, "quickload", "tmc600"));
	quickload.set_load_callback(FUNC(tmc600_state::quickload_cb));
	quickload.set_interface("tmc600_quik");

	// expansion bus connector
	TMC600_EUROBUS_SLOT(config, m_bus, tmc600_eurobus_cards, nullptr);

	// internal RAM
	RAM(config, RAM_TAG).set_default_size("8K");

	// software lists
	SOFTWARE_LIST(config, "quik_list").set_original("tmc600_quik");
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

#if 0
ROM_START( tmc600s1 )
	ROM_REGION( 0x6000, CDP1802_TAG, 0 )
	ROM_LOAD( "sb20",       0x0000, 0x1000, NO_DUMP )
	ROM_LOAD( "sb21",       0x1000, 0x1000, NO_DUMP )
	ROM_LOAD( "sb22",       0x2000, 0x1000, NO_DUMP )
	ROM_LOAD( "sb23",       0x3000, 0x1000, NO_DUMP )
	ROM_SYSTEM_BIOS( 0, "sb040282", "SB040282" )
	ROMX_LOAD( "190482",    0x4000, 0x1000, NO_DUMP, ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "sbdos", "SBDOS" )
	ROMX_LOAD( "190482_",   0x4000, 0x1000, NO_DUMP, ROM_BIOS(1) )
	ROMX_LOAD( "190482_v",  0x5000, 0x1000, NO_DUMP, ROM_BIOS(1) )

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "chargen",    0x0000, 0x1000, CRC(93f92cbf) SHA1(371156fb38fa5319c6fde537ccf14eed94e7adfb) )
ROM_END
#endif

ROM_START( tmc600s2 )
	ROM_REGION( 0x6000, CDP1802_TAG, 0 )
	ROM_LOAD( "sb30",       0x0000, 0x1000, CRC(95d1292a) SHA1(1fa52d59d3005f8ac74a32c2164fdb22947c2748) )
	ROM_LOAD( "sb31",       0x1000, 0x1000, CRC(2c8f3d17) SHA1(f14e8adbcddeaeaa29b1e7f3dfa741f4e230f599) )
	ROM_LOAD( "sb32",       0x2000, 0x1000, CRC(dd58a128) SHA1(be9bdb0fc5e0cc3dcc7f2fb7ccab69bf5b043803) )
	ROM_LOAD( "sb33",       0x3000, 0x1000, CRC(b7d241fa) SHA1(6f3eadf86c4e3aaf93d123e302a18dc4d9db964b) )
	ROM_SYSTEM_BIOS( 0, "sb040282", "SB040282" )
	ROMX_LOAD( "151182",    0x4000, 0x1000, CRC(c1a8d9d8) SHA1(4552e1f06d0e338ba7b0f1c3a20b8a51c27dafde), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "sbdos", "SBDOS" )
	ROMX_LOAD( "151182_",   0x4000, 0x1000, NO_DUMP, ROM_BIOS(1) )
	ROMX_LOAD( "151182_v",  0x5000, 0x1000, NO_DUMP, ROM_BIOS(1) )

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "chargen",    0x0000, 0x1000, CRC(93f92cbf) SHA1(371156fb38fa5319c6fde537ccf14eed94e7adfb) )
ROM_END


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY        FULLNAME                     FLAGS
//COMP( 1982, tmc600s1, 0,      0,      tmc600,  tmc600, tmc600_state, empty_init, "Telercas Oy", "Telmac TMC-600 (Sarja I)",  MACHINE_NOT_WORKING )
COMP( 1982, tmc600s2, 0,      0,      tmc600,  tmc600, tmc600_state, empty_init, "Telercas Oy", "Telmac TMC-600 (Sarja II)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )



tnshc08.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

    http://www.sapi.cz/tns/hc08.php

    IC104 - address decoder
    /A5, /A3, /A3, /A2
     0    0    0    0     CS0 - not connected
     0    0    0    1     CS1 - SLOT 1/2
     0    0    1    0     CS2 - SLOT 1/2
     0    0    1    1     CS3 - not connected
     0    1    0    0     CS4 - 7474 clear (IC61)
     0    1    0    1     CS5 - 8255 (IC90) and 7474 clear (IC61)
     0    1    1    0     CS6 - 7474 preset (IC81)
     0    1    1    1     CS7 - mod on 7495 (IC62)
     1    0    0    0     CS8 - 7474 (IC77)
     1    0    0    1     CS9 - SLOT 1/2
     1    0    1    0     CS10 - SLOT 1/2
     1    0    1    1     CS11 - 8255 (IC89) and 74154 (IC111)
     1    1    0    0     CS12 - SIO (IC86)
     1    1    0    1     CS13 - PIO (IC51)
     1    1    1    0     CS14 - CTC (IC87)
     1    1    1    1     CS15 - CTC (IC88)

    8 x 4164   64K
    8 x 41256 256K

****************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/i8255.h"

namespace {

class tnshc08_state : public driver_device
{
public:
	tnshc08_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ppi(*this, "ic89")
	{ }

	void tnshc08(machine_config &config);

	u8 ppi_r() { return 0x20; }

private:
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_device<i8255_device> m_ppi;
};

void tnshc08_state::mem_map(address_map &map)
{
	map(0x0000, 0x0fff).rom();
	map(0x1000, 0xffff).ram();
}

void tnshc08_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x10, 0x13).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
}

/* Input ports */
static INPUT_PORTS_START( tnshc08 )
INPUT_PORTS_END

void tnshc08_state::tnshc08(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 12.288_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &tnshc08_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &tnshc08_state::io_map);

	I8255(config, m_ppi, 0);
	m_ppi->in_pb_callback().set(FUNC(tnshc08_state::ppi_r));
}


/* ROM definition */
ROM_START( tnshc08 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "fl1_29.ic53",  0x0000, 0x0800, CRC(e6581b55) SHA1(fb5ce8b30518f06b144a7c204a3b72fde9cab17c))
	ROM_LOAD( "fl2_29.ic54",  0x0800, 0x0800, CRC(c44d65e7) SHA1(395968b16398dbd89f3fc4b18b4f2f173496eeda))
	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "chargen.ic42", 0x0000, 0x1000, CRC(e607ff0f) SHA1(0c787593e26398d856c3d7c44250e462b138b424))
ROM_END

} // anonymous namespace

/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY            FULLNAME     FLAGS
COMP( 1988, tnshc08, 0,      0,      tnshc08, tnshc08, tnshc08_state, empty_init, u8"JZD Slušovice", "TNS HC-08", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



tomy_princ.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood, QUFB
/************************************************************************

    Tomy Prin-C driver for the following models:

    - Prin-C (ジヨイホビーコンピューター プリンシー): Built-in keyboard and unused(?) Mini-DIN-4 mouse port;
    - Prin-C Tablet (マルチホビーコンピューター プリンシータブレット): Built-in keyboard with touch-pad;

    TODO:

    - Flash ROM support (used by Memory Cassette)
    - Printer (graphics to print are previously transferred to work area);
    - Audio;

    Hardware
    --------

    [princnt]

    - U1,U2,U7,U9,U11 (Hex Inverter): Motorola 74HC04
    - U3 (ROM): Toshiba TC538000AF (1M x 8-bit)
    - U4 (SRAM): Hyundai HY62256A / Winbond W24257S-70LL (32K x 8-bit)
    - U5 (DRAM): IBM 014400J1F / Hitachi HM514400C (1M x 4-bit)
    - U6 (MCU): Fujitsu MB90611A (FPT-100P-M06 package, F2MC-16L ISA)
    - U8 (VDP): FInput(?) FIT-V01
    - U12,U13 (Stepper Motor): Shindengen MTD2003F
    - X1 (Crystal): D143K6

    There are 2 known revisions given by the mask ROM's serial: PRIN-C #1 and #3. #1 has
    an additional Motorola MC74HC107 (Dual J-K Flip-Flop) with bodge wires soldered to
    connector CN1 and motor U12, which is absent in #3.

    Although the mouse port is connected to MCU pins P90 and P91, it is only read once
    during system init, and stored in memory at 0xE07BD5. This variable is then
    accessed once during printing, and doesn't seem to influence its operation.

    Memory Cassette can be emulated by loading a cart using a 0x40000-sized zero-filled
    binary with the following header:

    00000000: 0000 0000 5241 4d20 5241 4d20 4441 5441  ....RAM RAM DATA

    Flash ROM access:

    [:maincpu] ':maincpu' (FCA6DE): unmapped program memory write to 805555 = F0 & FF
    [:maincpu] ':maincpu' (FCA6EA): unmapped program memory write to 805555 = AA & FF
    [:maincpu] ':maincpu' (FCA6F6): unmapped program memory write to 802AAA = 55 & FF
    [:maincpu] ':maincpu' (FCA702): unmapped program memory write to 805555 = 80 & FF
    [:maincpu] ':maincpu' (FCA70E): unmapped program memory write to 805555 = AA & FF
    [:maincpu] ':maincpu' (FCA71A): unmapped program memory write to 802AAA = 55 & FF
    [:maincpu] ':maincpu' (FCA726): unmapped program memory write to 805555 = 10 & FF

    [princ]

    - U2 (DRAM): NEC 424400-70 (1M x 4-bit)
    - U4 (Flash ROM): Fujitsu 29F800TA-90 (1M x 8/512K x 16-bit)
    - U5 (SRAM): Winbond W24257S-70LL (32K x 8-bit)
    - U6 (MCU): Fujitsu MB90611A (FPT-100P-M06 package, F2MC-16L ISA)
    - U7 (VDP): Tomy TEE-X01
    - U12,U13 (PWM Motor): Allegro MicroSystems A3964SLB
    - X1 (Crystal): D143C8

    Init trace:

    [:maincpu] ':maincpu' (FFC431): unmapped program memory write to 0000A1 = 00 & FF  CKSCR
    [:maincpu] ':maincpu' (FFC437): unmapped program memory write to 000048 = 04 & FF  CS control 0  (Enable out, region is 1 MByte @ F00000)
    [:maincpu] ':maincpu' (FFC43D): unmapped program memory write to 000049 = 04 & FF  CS control 1  (Enable out, region is 1 MByte @ E00000)
    [:maincpu] ':maincpu' (FFC443): unmapped program memory write to 00004A = 07 & FF  CS control 2  (Enable out, region is 128 byte @ 68FF80)
    [:maincpu] ':maincpu' (FFC449): unmapped program memory write to 00004B = 00 & FF  CS control 3  (No out, region is reserved)
    [:maincpu] ':maincpu' (FFC44F): unmapped program memory write to 0000A5 = D3 & FF  ARSR (3 cycle wait state from addrs 002000 to 7FFFFF, 3 waits from C0 to FF, 1 cycle wait on addresses > 800000)
    [:maincpu] ':maincpu' (FFC455): unmapped program memory write to 0000A6 = 00 & FF  HACR ()
    [:maincpu] ':maincpu' (FFC45B): unmapped program memory write to 0000A7 = 7F & FF  ECSR
    [:maincpu] ':maincpu' (FFC461): unmapped program memory write to 000011 = 00 & FF  Port 1 DDR
    [:maincpu] ':maincpu' (FFC467): unmapped program memory write to 000012 = FF & FF       2
    [:maincpu] ':maincpu' (FFC46D): unmapped program memory write to 000013 = FF & FF       3
    [:maincpu] ':maincpu' (FFC473): unmapped program memory write to 000014 = FF & FF       4
    [:maincpu] ':maincpu' (FFC479): unmapped program memory write to 000015 = 01 & FF       5
    [:maincpu] ':maincpu' (FFC47F): unmapped program memory write to 000016 = 1F & FF  Analog input enable
    [:maincpu] ':maincpu' (FFC485): unmapped program memory write to 000016 = E0 & FF       7
    [:maincpu] ':maincpu' (FFC48B): unmapped program memory write to 000017 = 30 & FF       8
    [:maincpu] ':maincpu' (FFC491): unmapped program memory write to 000018 = 0C & FF       9
    [:maincpu] ':maincpu' (FFC497): unmapped program memory write to 00001A = FF & FF       A
    [:maincpu] ':maincpu' (FFC189): unmapped program memory write to 00000A = 00 & FF  port A
    [:maincpu] ':maincpu' (FFC257): unmapped program memory write to 00000A = 80 & FF  port A
    [:maincpu] ':maincpu' (FE2C08): unmapped program memory write to 0000A9 = 96 & FF  TBTC - IRQ enabled, 16.384 ms timebase
    [:maincpu] ':maincpu' (FE2C11): unmapped program memory write to 0000BB = 06 & FF  ICR11 - level 6 interrupt, no intelligent I/O
    [:maincpu] ':maincpu' (FE2959): unmapped program memory write to 000017 = 30 & FF  port 7 DDR
    [:maincpu] ':maincpu' (FE2963): unmapped program memory write to 0000A9 = 96 & FF  TBTC
    [:maincpu] ':maincpu' (FE296C): unmapped program memory write to 0000BB = 06 & FF  ICR11
    [:maincpu] ':maincpu' (FE29CC): unmapped program memory write to 000007 = 00 & FF  port 7 out
    [:maincpu] ':maincpu' (FE2A69): unmapped program memory write to 0000A9 = 96 & FF  TBTC
    [:maincpu] ':maincpu' (FE2A72): unmapped program memory write to 0000BB = 06 & FF  ICR11
    [:maincpu] ':maincpu' (FC2AD5): unmapped program memory write to 000018 = 0C & FF  port 8 DDR
    [:maincpu] ':maincpu' (FC2ADE): unmapped program memory write to 000039 = 0C & FF  TMCSR0 (clock = phase 16 MHz / 2^1, trigger input, rising edge)
    [:maincpu] ':maincpu' (FC2AE8): unmapped program memory write to 000038 = F0 & FF  TMCSR0 (toggle output, H Level at start, no count or interrupt enable)
    [:maincpu] ':maincpu' (FC2AF1): unmapped program memory write to 00003D = 0C & FF  TMCSR1
    [:maincpu] ':maincpu' (FC2AFB): unmapped program memory write to 00003C = F0 & FF  TMCSR1
    [:maincpu] ':maincpu' (FE2B89): unmapped program memory write to 000007 = 00 & FF  port 7 out
    [:maincpu] ':maincpu' (FCE68D): unmapped program memory write to 000007 = 10 & FF  port 7 out
    [:maincpu] ':maincpu' (FCE6BA): unmapped program memory write to 000034 = 73 & FF  PRL0 (PPG0 reload)
    [:maincpu] ':maincpu' (FCE6D3): unmapped program memory write to 000036 = 0D & FF  PRL1 (PPG1 reload)
    [:maincpu] ':maincpu' (FCE6DE): unmapped program memory write to 000030 = 85 & FF  PPG0C0 (PPG0 control)  (PPG Enabled, 16 MHz / 16, no interrupts)

    Hidden Features
    ---------------

    [princnt] On reset, press one of the following key combinations:

    - "T" + "M": Developer credits are shown before the logo screen;
    - "Print" + "1".."7": Print 1 out of 7 pre-defined patterns;
    - "Menu" + "0": Label "デモ" (Demo) is overlayed on several screens;

************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/f2mc16/mb90610a.h"
#include "machine/timer.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#define LOG_VDP (1U << 1)
#define LOG_REG (1U << 2)

//#define VERBOSE (LOG_VDP | LOG_REG)
#include "logmacro.h"

// Renders each dedicated area/layer, toggled by pressing keys "ASDFG"
//#define VIDEO_DEBUG 1

namespace {

class tomy_princ_state : public driver_device
{
public:
	tomy_princ_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cart(*this, "cartslot")
		, m_screen(*this, "screen")
		, m_palette(*this, "palette")
		, m_scantimer(*this, "scantimer")
		, m_io_keys(*this, "KEY%u", 1U)
		, m_key(~0)
	{
#ifdef VIDEO_DEBUG
		SCREEN_W = BITPLANE_W;
		SCREEN_H = LAYER_H;
#else
		SCREEN_W = 360;
		SCREEN_H = 240;
#endif
		SRC_POS = 0x20;
		DST_POS = 0x24;
		SHAPE_SIZE = 0x28;
		SHAPE_DIRECTION = 0x2c;
		SHAPE_MODE = 0x2d;
		SHAPE_PAL = 0x30;
		RENDER_CMD = 0x31;
		RENDER2_CMD = 0x31;
		LAYER_MODE = 0xc2;
		LAYER2_MODE = 0xc2;
		LAYER_OFFSET_Y = 0xc3;
		LAYER_OFFSET_X = 0xc4;
		LAYER_CLIP_H = 0xc5;
		LAYER_CLIP_W = 0xc6;
		SCREEN_OFFSET = 0xc9;
		PAL_DATA = 0xd1;
		SPRITE1_DST_POS_Y = 0xd2;
		SPRITE1_DST_POS_X = 0xd3;
		SPRITE1_SELECT = 0xd4;
		SPRITE2_DST_POS_Y = 0xd5;
		SPRITE2_DST_POS_X = 0xd6;
		SPRITE2_SELECT = 0xd7;
	}

	void tomy_princ(machine_config &config);

protected:
	static inline constexpr u16 BITPLANE_W = 0x200;
	static inline constexpr u16 BITPLANE_H = 0x800;
	static inline constexpr u16 LAYER_H = 0x400;
	static inline constexpr u16 LAYER_WORKAREA_H = 0x3c0;
	static inline constexpr u16 LAYER_SPRITES_H = 0x40;
	static inline constexpr u16 SPRITE_SIZE = 0x20;

	// Visible area screen dimensions
	u16 SCREEN_W;
	u16 SCREEN_H;

	// VDP register indexes
	u16 SRC_POS;
	u16 DST_POS;
	u16 SHAPE_SIZE;
	u16 SHAPE_DIRECTION;
	u16 SHAPE_MODE;
	u16 SHAPE_PAL;
	u16 RENDER_CMD;
	u16 RENDER2_CMD;
	u16 LAYER_MODE;
	u16 LAYER2_MODE;
	u16 LAYER_OFFSET_Y;
	u16 LAYER_OFFSET_X;
	u16 LAYER_CLIP_H;
	u16 LAYER_CLIP_W;
	u16 SCREEN_OFFSET;
	u16 PAL_DATA;
	u16 SPRITE1_DST_POS_Y;
	u16 SPRITE1_DST_POS_X;
	u16 SPRITE1_SELECT;
	u16 SPRITE2_DST_POS_Y;
	u16 SPRITE2_DST_POS_X;
	u16 SPRITE2_SELECT;

	virtual u8 vdp_stat_r();

	u8 pdr8_r();
	u8 pdra_r();
	u8 key_r();
	virtual void pdr6_w(u8 data);
	virtual void pdr8_w(u8 data);

	enum render_cmd_type : u8
	{
		BUFFER_WRITE = 0x10,
		PUT_SHAPE_RECT = 0x20,
		BUFFER_READ = 0x30,
		PUT_FROM_BUFFER = 0x40,
		PUT_SHAPE_LINE = 0x50,
		BUFFER_FIND_BOUND = 0x60,
		BUFFER_READ_PIXEL = 0x70,
		PUT_SHAPE_PIXEL = 0x80,
	};

	required_device<mb90611a_device> m_maincpu;
	required_device<generic_slot_device> m_cart;
	required_device<screen_device> m_screen;

	/*
	    FIT-V01 is a single bitplane VDP, with graphics stored as 4bpp
	    indexed to 16 palette colors, where the first entry is used as
	    transparency. It has limited support for drawing lines and
	    rendering sprites (seems like up to 2 are visible).

	    This bitplane has width=0x200 and height=0x800, separated by
	    height value into 3 distinct ranges:

	    - Visible area:     0..0x400;
	    - Work area:    0x400..0x7c0;
	    - Sprites:      0x7c0..0x800;

	    Visible area is dynamically clipped to 360x240 and can also be xy-offset by
	    writing to specific VDP internal registers. These writes are done by
	    first setting the register offset at address 68FF41, then writing data to
	    address 68FF42, which implicitly increments the register offset.

	    TEE-X01 is functionally very close to FIT-V01, but with different register
	    offsets and usage patterns (e.g. allows reading directly from internal buffer, and
	    clearing the bitplane in a single operation spanning the full range, instead of
	    3 operations for each discrete area).
	*/
	struct vdp_state {
		u8 regs[0x100];
		u8 reg;
		u32 reg_data_i;
		u16 screen_offset_x;
		u16 screen_offset_y;
		u16 offset_x;
		u16 offset_y;
		u16 src_pos_x;
		u16 src_pos_y;
		u16 dst_pos_x;
		u16 dst_pos_y;
		u16 size_x;
		u16 size_y;
		u8 shape_direction;
		u8 shape_blend;
		u8 shape_transform;
		u8 shape_pal_i;
		u16 sprite_dst_pos_x;
		u16 sprite_dst_pos_y;
		u16 sprite_i;
		u16 sprite_transform;
		u16 sprite2_dst_pos_x;
		u16 sprite2_dst_pos_y;
		u16 sprite2_i;
		u16 sprite2_transform;
		u16 sprite_size_x;
		u16 sprite_size_y;
		u32 pending_pixels;
		u8 layer_mode;
		u8 pal_r;
		u8 pal_g;
		u8 pal_b;
		u32 pal_i;
		u16 bound_x;
	} m_vdp;

	bool m_is_bound_found;
	bool m_is_overlay_enabled;

	enum layer_target : u8
	{
		DEFAULT_TARGET,
		OVERLAY_TARGET,
		SPRITES_TARGET,
		SPRITES_PRIORITY_TARGET,
	};

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;
	virtual void video_reset() override ATTR_COLD;

	TIMER_DEVICE_CALLBACK_MEMBER(scan_interrupt);

	DEVICE_IMAGE_LOAD_MEMBER(cart_load);
	void princ_map(address_map &map) ATTR_COLD;

	void princ_palette(palette_device &palette) const;
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void screen_update_layer(u8 *layer, u16 layer_offset, u16 layer_w, u16 layer_h, screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void clear_screen();
	void clear_overlay();
	void vdp_update();
	void vdp_sprite_update();
	void vdp_reg_w(u8 data);
	u8 vdp_reg_data_r();
	void vdp_reg_data_w(u8 data);
	void vdp_pal_data_w(u8 data);
	u8 vdp_gfx_data_r();
	void vdp_gfx_data_w(u8 data);
	void draw_pixel(u8 color, u32 i, u8 layer_target, bool is_vertical, bool is_xor);
	u8 read_pixel(u32 i);
	void draw_pixel_at(u8 *layer, u16 layer_w, u16 layer_h, u16 pos_x, u16 pos_y, u8 color, u32 i, bool is_overlay, bool is_vertical, bool is_xor);
	u8 read_pixel_at(u8 *layer, u16 layer_w, u16 layer_h, u16 pos_x, u16 pos_y, u32 i);
	void draw_sprite(u32 dst_pos_x, u32 dst_pos_y, u32 i, u32 transform, bool is_priority);

	required_device<palette_device> m_palette;
	required_device<timer_device> m_scantimer;
	required_ioport_array<9> m_io_keys;

	std::unique_ptr<u8[]> m_layer;
	std::unique_ptr<u8[]> m_layer_overlay_sprites;
	std::unique_ptr<u8[]> m_layer_overlay_sprites_priority;

	u8 m_ext_reg0C;
	bool m_is_ext_reg00_read;
	uint16_t m_key;
};

TIMER_DEVICE_CALLBACK_MEMBER(tomy_princ_state::scan_interrupt)
{
	for (int i = 0; i < 128; i++)
	{
		m_maincpu->reload_timer<0>().tin(ASSERT_LINE);
		m_maincpu->reload_timer<0>().tin(CLEAR_LINE);
	}
}

void tomy_princ_state::machine_start()
{
	if (m_cart->exists())
	{
		memory_region *const cart_rom = m_cart->memregion("rom");
		m_maincpu->space(AS_PROGRAM).install_rom(0x800000, 0x87ffff, cart_rom->base());
	}

	save_item(NAME(m_ext_reg0C));
	save_item(NAME(m_is_ext_reg00_read));
	save_item(NAME(m_key));
}

void tomy_princ_state::machine_reset()
{
	m_ext_reg0C = 0;
	m_is_ext_reg00_read = false;
}

void tomy_princ_state::video_start()
{
	m_layer = std::make_unique<u8[]>(BITPLANE_W * BITPLANE_H);
	m_layer_overlay_sprites = std::make_unique<u8[]>(BITPLANE_W * LAYER_H);
	m_layer_overlay_sprites_priority = std::make_unique<u8[]>(BITPLANE_W * LAYER_H);
	save_pointer(NAME(m_layer), BITPLANE_W * BITPLANE_H);
	save_pointer(NAME(m_layer_overlay_sprites), BITPLANE_W * LAYER_H);
	save_pointer(NAME(m_layer_overlay_sprites_priority), BITPLANE_W * LAYER_H);

	save_item(NAME(m_is_bound_found));
	save_item(NAME(m_is_overlay_enabled));

	save_item(STRUCT_MEMBER(m_vdp, regs));
	save_item(STRUCT_MEMBER(m_vdp, reg));
	save_item(STRUCT_MEMBER(m_vdp, reg_data_i));
	save_item(STRUCT_MEMBER(m_vdp, screen_offset_x));
	save_item(STRUCT_MEMBER(m_vdp, screen_offset_y));
	save_item(STRUCT_MEMBER(m_vdp, offset_x));
	save_item(STRUCT_MEMBER(m_vdp, offset_y));
	save_item(STRUCT_MEMBER(m_vdp, src_pos_x));
	save_item(STRUCT_MEMBER(m_vdp, src_pos_y));
	save_item(STRUCT_MEMBER(m_vdp, dst_pos_x));
	save_item(STRUCT_MEMBER(m_vdp, dst_pos_y));
	save_item(STRUCT_MEMBER(m_vdp, size_x));
	save_item(STRUCT_MEMBER(m_vdp, size_y));
	save_item(STRUCT_MEMBER(m_vdp, shape_direction));
	save_item(STRUCT_MEMBER(m_vdp, shape_blend));
	save_item(STRUCT_MEMBER(m_vdp, shape_transform));
	save_item(STRUCT_MEMBER(m_vdp, shape_pal_i));
	save_item(STRUCT_MEMBER(m_vdp, sprite_dst_pos_x));
	save_item(STRUCT_MEMBER(m_vdp, sprite_dst_pos_y));
	save_item(STRUCT_MEMBER(m_vdp, sprite_i));
	save_item(STRUCT_MEMBER(m_vdp, sprite_transform));
	save_item(STRUCT_MEMBER(m_vdp, sprite2_dst_pos_x));
	save_item(STRUCT_MEMBER(m_vdp, sprite2_dst_pos_y));
	save_item(STRUCT_MEMBER(m_vdp, sprite2_i));
	save_item(STRUCT_MEMBER(m_vdp, sprite2_transform));
	save_item(STRUCT_MEMBER(m_vdp, sprite_size_x));
	save_item(STRUCT_MEMBER(m_vdp, sprite_size_y));
	save_item(STRUCT_MEMBER(m_vdp, pending_pixels));
	save_item(STRUCT_MEMBER(m_vdp, layer_mode));
	save_item(STRUCT_MEMBER(m_vdp, pal_r));
	save_item(STRUCT_MEMBER(m_vdp, pal_g));
	save_item(STRUCT_MEMBER(m_vdp, pal_b));
	save_item(STRUCT_MEMBER(m_vdp, pal_i));
	save_item(STRUCT_MEMBER(m_vdp, bound_x));
}

void tomy_princ_state::video_reset()
{
	memset(&m_vdp, 0, sizeof(m_vdp));
	m_vdp.sprite_size_x = SPRITE_SIZE;
	m_vdp.sprite_size_y = SPRITE_SIZE;

	clear_screen();
}

void tomy_princ_state::clear_screen()
{
	std::fill_n(m_layer.get(), BITPLANE_W * BITPLANE_H, 0);

	clear_overlay();
}

void tomy_princ_state::clear_overlay()
{
	std::fill_n(m_layer_overlay_sprites.get(), BITPLANE_W * LAYER_H, 0);
	std::fill_n(m_layer_overlay_sprites_priority.get(), BITPLANE_W * LAYER_H, 0);
}

u8 tomy_princ_state::pdr8_r()
{
	if (m_is_ext_reg00_read)
	{
		m_is_ext_reg00_read = false;
		return 0x20;
	}

	return 0x00;
}

u8 tomy_princ_state::pdra_r()
{
	return m_ext_reg0C == 0x0c ? 0 : 0x10;
}

void tomy_princ_state::vdp_reg_w(u8 data)
{
	LOGMASKED(LOG_VDP, "%s: reg=%02x\n", machine().describe_context(), data);

	m_vdp.reg = data;
	m_vdp.reg_data_i = 0;
	m_vdp.pal_i = 0;
}

void tomy_princ_state::vdp_update()
{
	m_vdp.src_pos_x = (m_vdp.regs[SRC_POS + 1] << 8) | m_vdp.regs[SRC_POS];
	m_vdp.src_pos_y = (m_vdp.regs[SRC_POS + 3] << 8) | m_vdp.regs[SRC_POS + 2];
	m_vdp.dst_pos_x = (m_vdp.regs[DST_POS + 1] << 8) | m_vdp.regs[DST_POS];
	m_vdp.dst_pos_y = (m_vdp.regs[DST_POS + 3] << 8) | m_vdp.regs[DST_POS + 2];

	m_vdp.size_x = ((m_vdp.regs[SHAPE_SIZE + 1] << 8) | m_vdp.regs[SHAPE_SIZE]) + 1;
	m_vdp.size_y = ((m_vdp.regs[SHAPE_SIZE + 3] << 8) | m_vdp.regs[SHAPE_SIZE + 2]) + 1;

	m_vdp.shape_direction = m_vdp.regs[SHAPE_DIRECTION];
	m_vdp.shape_blend = m_vdp.regs[SHAPE_MODE] & 0x0f;
	m_vdp.shape_transform = (m_vdp.regs[SHAPE_MODE] & 0xf0) >> 4;
	m_vdp.shape_pal_i = (m_vdp.regs[SHAPE_PAL] & 0xf0) >> 4;

	m_vdp.offset_y = (m_vdp.regs[SCREEN_OFFSET + 1] << 8) | m_vdp.regs[SCREEN_OFFSET];
	m_vdp.offset_x = (m_vdp.regs[SCREEN_OFFSET + 3] << 8) | m_vdp.regs[SCREEN_OFFSET + 2];
	if (m_vdp.regs[LAYER_OFFSET_X] != 0 && m_vdp.regs[LAYER_OFFSET_Y] != 0)
	{
		m_vdp.screen_offset_y = m_vdp.regs[LAYER_OFFSET_Y] * 2;
		m_vdp.screen_offset_x = m_vdp.regs[LAYER_OFFSET_X] * 2;
	}
	else
	{
		m_vdp.screen_offset_y = m_vdp.offset_y;
		m_vdp.screen_offset_x = m_vdp.offset_x;
	}
}

void tomy_princ_state::vdp_sprite_update()
{
	m_vdp.sprite_dst_pos_y = m_vdp.regs[SPRITE1_DST_POS_Y];
	m_vdp.sprite_dst_pos_x = m_vdp.regs[SPRITE1_DST_POS_X];
	m_vdp.sprite_i = m_vdp.regs[SPRITE1_SELECT] & 0xf;
	m_vdp.sprite_transform = (m_vdp.regs[SPRITE1_SELECT] >> 4) & 0xf;

	m_vdp.sprite2_dst_pos_y = m_vdp.regs[SPRITE2_DST_POS_Y];
	m_vdp.sprite2_dst_pos_x = m_vdp.regs[SPRITE2_DST_POS_X];
	m_vdp.sprite2_i = m_vdp.regs[SPRITE2_SELECT] & 0xf;
	m_vdp.sprite2_transform = (m_vdp.regs[SPRITE2_SELECT] >> 4) & 0xf;
}

u8 tomy_princ_state::vdp_reg_data_r()
{
	const u8 data = m_is_bound_found ? (m_vdp.bound_x >> (8 * m_vdp.reg_data_i++)) : 0;

	LOGMASKED(LOG_VDP, "%s: reg=%02x i=%02x r reg_data=%02x\n", machine().describe_context(), m_vdp.reg, m_vdp.reg_data_i, data);

	return data;
}

void tomy_princ_state::vdp_reg_data_w(u8 data)
{
	LOGMASKED(LOG_VDP, "%s: reg=%02x i=%02x reg_data=%02x\n", machine().describe_context(), m_vdp.reg, m_vdp.reg_data_i, data);

	m_vdp.regs[m_vdp.reg] = data;

	if (m_vdp.reg == RENDER_CMD || m_vdp.reg == RENDER2_CMD)
	{
		vdp_update();
		LOGMASKED(LOG_VDP, "%s:    src x=%04x y=%04x\n", machine().describe_context(), m_vdp.src_pos_x, m_vdp.src_pos_y);
		LOGMASKED(LOG_VDP, "%s:    dst x=%04x y=%04x\n", machine().describe_context(), m_vdp.dst_pos_x, m_vdp.dst_pos_y);
		LOGMASKED(LOG_VDP, "%s:   size x=%04x y=%04x\n", machine().describe_context(), m_vdp.size_x, m_vdp.size_y);
		LOGMASKED(LOG_VDP, "%s:  shape d=%02x b=%02x p=%02x\n", machine().describe_context(), m_vdp.shape_direction, m_vdp.shape_blend, m_vdp.shape_pal_i);

		switch (data)
		{
			case BUFFER_WRITE:
			case BUFFER_READ:
				m_vdp.pending_pixels = m_vdp.size_x * m_vdp.size_y;
				break;

			case BUFFER_READ_PIXEL:
				m_vdp.pending_pixels = 1;
				break;

			case PUT_SHAPE_RECT:
				for (size_t i = 0; i < m_vdp.size_x * m_vdp.size_y; i++)
				{
					draw_pixel(m_vdp.shape_pal_i, i, OVERLAY_TARGET, false, false);
				}
				break;

			case PUT_FROM_BUFFER:
				for (size_t i = 0; i < m_vdp.size_x * m_vdp.size_y; i++)
				{
					draw_pixel(read_pixel(i), i, OVERLAY_TARGET, false, false);
				}
				break;

			case PUT_SHAPE_LINE:
			{
				m_vdp.size_y = 1;
				u32 offset = 0;
				if (!BIT(m_vdp.shape_direction, 0))
				{
					// horizontal direction
					for (size_t i = 0; i < m_vdp.size_x; i++)
					{
						draw_pixel(m_vdp.shape_pal_i, offset, OVERLAY_TARGET, false, m_vdp.shape_blend == 0x6);
						if (!BIT(m_vdp.shape_direction, 3))
							offset++;
						else
							m_vdp.dst_pos_x--;
					}
				}
				else
				{
					// vertical direction
					for (size_t i = 0; i < m_vdp.size_x; i++)
					{
						draw_pixel(m_vdp.shape_pal_i, offset, OVERLAY_TARGET, true, m_vdp.shape_blend == 0x6);
						if (!BIT(m_vdp.shape_direction, 3))
							offset += m_vdp.size_x;
						else
							m_vdp.dst_pos_y--;
					}
				}
				break;
			}

			case BUFFER_FIND_BOUND:
				// Used by Prin-C Tablet's flood fill brush. Given the brush sprite's x-position,
				// left and right bounds for the same palette index are queried, followed by a
				// PUT_SHAPE_LINE command with size spanning these bounds.
				m_is_bound_found = false;
				m_vdp.bound_x = 0;
				m_vdp.size_x = BITPLANE_W - m_vdp.src_pos_x;
				m_vdp.size_y = 1;
				if (BIT(m_vdp.shape_direction, 1))
				{
					u16 i = 0;
					const u8 target_pixel = m_vdp.shape_pal_i;
					while ((m_vdp.src_pos_x + i >= 0) && (m_vdp.src_pos_x + i < BITPLANE_W))
					{
						const u8 candidate_pixel = read_pixel(i);
						if (candidate_pixel != target_pixel)
						{
							m_vdp.bound_x = m_vdp.src_pos_x + i;
							m_is_bound_found = true;
							LOGMASKED(LOG_VDP, "%s: reg=%02x bound_x=%04x (had=%02x got=%02x)\n", machine().describe_context(), m_vdp.reg, m_vdp.bound_x, target_pixel, candidate_pixel);
							break;
						}

						if (!BIT(m_vdp.shape_direction, 2))
							i++;
						else
							m_vdp.src_pos_x--;
					}
				}
				else
				{
					// Tests if a pixel was already visited / rendered, comparing the
					// candidate palette index against the index of the previously unfilled pixel.
					// If returned bound value is not zero, the query is retried. We need to return
					// the next x-position, so that program code doesn't keep retrying the same one.
					const u8 target_pixel = m_vdp.shape_pal_i & 0xf;
					const u8 candidate_pixel = read_pixel(0) & 0xf;
					LOGMASKED(LOG_VDP, "%s: reg=%02x d=0 (had=%02x got=%02x)\n", machine().describe_context(), m_vdp.reg, target_pixel, candidate_pixel);
					if (candidate_pixel != target_pixel)
					{
						m_vdp.bound_x = m_vdp.src_pos_x + 1;
						m_is_bound_found = true;
						LOGMASKED(LOG_VDP, "%s: reg=%02x bound_x=%04x (had=%02x got=%02x)\n", machine().describe_context(), m_vdp.reg, m_vdp.bound_x, target_pixel, candidate_pixel);
					}
				}
				break;

			case PUT_SHAPE_PIXEL:
				m_vdp.size_x = 1;
				m_vdp.size_y = 1;
				draw_pixel(m_vdp.shape_pal_i, 0, OVERLAY_TARGET, false, m_vdp.shape_blend == 0x6);
				break;

			default:
				logerror("%s: Unhandled cmd=%02x\n", machine().describe_context(), data);
		}
	}
	else if (m_vdp.reg == LAYER_MODE || m_vdp.reg == LAYER2_MODE)
	{
		m_vdp.layer_mode = data;
		if (BIT(m_vdp.layer_mode, 1))
		{
			vdp_update();
			vdp_sprite_update();
			LOGMASKED(LOG_VDP, "%s: sprite size x=%04x y=%04x\n", machine().describe_context(), m_vdp.sprite_size_x, m_vdp.sprite_size_y);
			LOGMASKED(LOG_VDP, "%s: sprite1 dst x=%04x y=%04x\n", machine().describe_context(), m_vdp.sprite_dst_pos_x, m_vdp.sprite_dst_pos_y);
			LOGMASKED(LOG_VDP, "%s: sprite1 sel i=%02x t=%02x\n", machine().describe_context(), m_vdp.sprite_i, m_vdp.sprite_transform);
			LOGMASKED(LOG_VDP, "%s: sprite2 dst x=%04x y=%04x\n", machine().describe_context(), m_vdp.sprite2_dst_pos_x, m_vdp.sprite2_dst_pos_y);
			LOGMASKED(LOG_VDP, "%s: sprite2 sel i=%02x t=%02x\n", machine().describe_context(), m_vdp.sprite2_i, m_vdp.sprite2_transform);

			clear_overlay();

			bool is_sprite_priority_set = m_vdp.offset_y & 0x8000;
			draw_sprite(m_vdp.sprite_dst_pos_x, m_vdp.sprite_dst_pos_y, m_vdp.sprite_i, m_vdp.sprite_transform, is_sprite_priority_set);
			draw_sprite(m_vdp.sprite2_dst_pos_x, m_vdp.sprite2_dst_pos_y, m_vdp.sprite2_i, m_vdp.sprite2_transform, !is_sprite_priority_set);

			m_is_overlay_enabled = true;
		}
		else
		{
			LOGMASKED(LOG_VDP, "%s: reg=%02x overlay disabled, m=%02x\n", machine().describe_context(), m_vdp.reg, m_vdp.layer_mode);

			m_is_overlay_enabled = false;
		}
	}

	m_vdp.reg++;
}

void tomy_princ_state::draw_sprite(u32 dst_pos_x, u32 dst_pos_y, u32 i, u32 transform, bool is_priority)
{
	if (BIT(transform, 2)) {
		return;
	}

	m_vdp.dst_pos_x = (BIT(transform, 1) ? 0x100 : 0) + dst_pos_x + (m_vdp.offset_x & 0x0fff);
	m_vdp.dst_pos_y = dst_pos_y + (m_vdp.offset_y & 0x0fff);
	m_vdp.src_pos_x = 0x20 * i;
	m_vdp.src_pos_y = (BIT(transform, 0) ? 0x20 : 0) + LAYER_H + LAYER_WORKAREA_H;
	m_vdp.size_x = m_vdp.sprite_size_x;
	m_vdp.size_y = m_vdp.sprite_size_y;
	LOGMASKED(LOG_VDP, "%s: sprite draw sx=%04x sy=%04x dx=%04x dy=%04x\n", machine().describe_context(), m_vdp.src_pos_x, m_vdp.src_pos_y, m_vdp.dst_pos_x, m_vdp.dst_pos_y);

	for (size_t i = 0; i < m_vdp.size_x * m_vdp.size_y; i++)
	{
		draw_pixel(read_pixel(i), i, is_priority ? SPRITES_PRIORITY_TARGET : SPRITES_TARGET, false, false);
	}
}

void tomy_princ_state::vdp_pal_data_w(u8 data)
{
	LOGMASKED(LOG_VDP, "%s: reg=%02x i=%04x reg_data=%02x\n", machine().describe_context(), m_vdp.reg, m_vdp.reg_data_i, data);

	if (m_vdp.reg == PAL_DATA + 1)
	{
		switch (m_vdp.reg_data_i)
		{
			case 0:
				m_vdp.pal_r = data * 0xff / 0x3f;
				break;
			case 1:
				m_vdp.pal_g = data * 0xff / 0x3f;
				break;
			case 2:
				m_vdp.pal_b = data * 0xff / 0x3f;
				m_palette->set_pen_color(m_vdp.pal_i, m_vdp.pal_r, m_vdp.pal_g, m_vdp.pal_b);
				LOGMASKED(LOG_VDP, "%s: reg=%02x i=%04x pal=%02x%02x%02x\n",
						machine().describe_context(),
						m_vdp.reg,
						m_vdp.pal_i,
						m_vdp.pal_r,
						m_vdp.pal_g,
						m_vdp.pal_b);
				m_vdp.pal_i++;
				break;
		}
	}

	m_vdp.reg_data_i++;
	m_vdp.reg_data_i %= 3;
}

u8 tomy_princ_state::vdp_gfx_data_r()
{
	u8 data = 0;
	if (m_vdp.pending_pixels != 0)
	{
		data = (read_pixel(m_vdp.reg_data_i) & 0xf) << 4;
		m_vdp.reg_data_i++;
		m_vdp.pending_pixels--;
	}
	if (m_vdp.pending_pixels != 0)
	{
		data |= read_pixel(m_vdp.reg_data_i) & 0xf;
		m_vdp.reg_data_i++;
		m_vdp.pending_pixels--;
	}

	LOGMASKED(LOG_VDP, "%s: reg=%02x i=%04x k=%04x r gfx_data=%02x\n", machine().describe_context(), m_vdp.reg, m_vdp.reg_data_i, m_vdp.pending_pixels, data);

	return data;
}

void tomy_princ_state::vdp_gfx_data_w(u8 data)
{
	LOGMASKED(LOG_VDP, "%s: reg=%02x i=%04x k=%04x w gfx_data=%02x\n", machine().describe_context(), m_vdp.reg, m_vdp.reg_data_i, m_vdp.pending_pixels, data);

	if (m_vdp.pending_pixels != 0)
	{
		draw_pixel((data & 0xf0) >> 4, m_vdp.reg_data_i, DEFAULT_TARGET, false, false);
		m_vdp.reg_data_i++;
		m_vdp.pending_pixels--;
	}
	if (m_vdp.pending_pixels != 0)
	{
		draw_pixel(data & 0x0f, m_vdp.reg_data_i, DEFAULT_TARGET, false, false);
		m_vdp.reg_data_i++;
		m_vdp.pending_pixels--;
	}
}

u8 tomy_princ_state::vdp_stat_r()
{
	return m_screen->vblank() ? 0x11 : 0x10;
}

void tomy_princ_state::draw_pixel(u8 color, u32 i, u8 layer_target, bool is_vertical, bool is_xor)
{
	const bool is_overlay = (layer_target == OVERLAY_TARGET);

	if (m_vdp.dst_pos_y < 0x800)
	{
		draw_pixel_at(layer_target == SPRITES_TARGET
						? m_layer_overlay_sprites.get()
						: layer_target == SPRITES_PRIORITY_TARGET
							? m_layer_overlay_sprites_priority.get()
							: m_layer.get(),
						BITPLANE_W,
						BITPLANE_H,
						m_vdp.dst_pos_x,
						m_vdp.dst_pos_y,
						color,
						i,
						is_overlay,
						is_vertical,
						is_xor);
	}
	else
	{
		logerror("%s: Unhandled w dst y=%04x\n", machine().describe_context(), m_vdp.dst_pos_y);
	}
}

void tomy_princ_state::draw_pixel_at(u8 *layer, u16 layer_w, u16 layer_h, u16 pos_x, u16 pos_y, u8 color, u32 i, bool is_overlay, bool is_vertical, bool is_xor)
{
	if (m_vdp.size_x == 0)
	{
		logerror("%s: Unhandled w size_x=0\n", machine().describe_context());
		return;
	}
	if (is_vertical)
	{
		if (pos_x + m_vdp.size_y > BITPLANE_H)
		{
			logerror("%s: OOB w x+y=%04x+%04x\n", machine().describe_context(), m_vdp.dst_pos_x, m_vdp.size_y);
			return;
		}
	}
	else
	{
		if (pos_x + m_vdp.size_x > BITPLANE_W)
		{
			logerror("%s: OOB w x=%04x+%04x\n", machine().describe_context(), m_vdp.dst_pos_x, m_vdp.size_x);
			return;
		}
		if (pos_y + m_vdp.size_y > BITPLANE_H)
		{
			logerror("%s: OOB w y=%04x+%04x\n", machine().describe_context(), m_vdp.dst_pos_y, m_vdp.size_y);
			return;
		}
	}

	const u32 base_offset = layer_w * pos_y + pos_x;
	const u32 gfx_offset = base_offset + layer_w * (i / m_vdp.size_x) + (i % m_vdp.size_x);
	if (gfx_offset > layer_w * layer_h)
	{
		logerror("%s: OOB w o=%08x x=%04x y=%04x\n", machine().describe_context(), gfx_offset, m_vdp.dst_pos_x, m_vdp.dst_pos_y);
		return;
	}
	if (is_overlay || color != 0)
	{
		if (is_xor)
		{
			layer[gfx_offset] ^= color;
			layer[gfx_offset] &= 0xf;
		}
		else
		{
			layer[gfx_offset] = color;
		}
	}
}

u8 tomy_princ_state::read_pixel(u32 i)
{
	if (m_vdp.src_pos_y < 0x800)
	{
		return read_pixel_at(m_layer.get(), BITPLANE_W, BITPLANE_H, m_vdp.src_pos_x, m_vdp.src_pos_y, i);
	}
	else
	{
		logerror("%s: Unhandled r src y=%04x\n", machine().describe_context(), m_vdp.src_pos_y);
	}

	return 0;
}

u8 tomy_princ_state::read_pixel_at(u8 *layer, u16 layer_w, u16 layer_h, u16 pos_x, u16 pos_y, u32 i)
{
	if (m_vdp.size_x == 0)
	{
		logerror("%s: Unhandled r size_x=0\n", machine().describe_context());
		return 0;
	}
	if (pos_x + m_vdp.size_x > BITPLANE_W)
	{
		logerror("%s: OOB r x=%04x+%04x\n", machine().describe_context(), m_vdp.dst_pos_x, m_vdp.size_x);
		return 0;
	}
	if (pos_y + m_vdp.size_y > BITPLANE_H)
	{
		logerror("%s: OOB r y=%04x+%04x\n", machine().describe_context(), m_vdp.dst_pos_y, m_vdp.size_y);
		return 0;
	}

	const u32 base_offset = layer_w * pos_y + pos_x;
	const u32 gfx_offset = base_offset + layer_w * (i / m_vdp.size_x) + (i % m_vdp.size_x);
	if (gfx_offset > layer_w * layer_h)
	{
		logerror("%s: OOB r o=%08x\n", machine().describe_context(), gfx_offset);
		return 0;
	}

	return layer[gfx_offset];
}

void tomy_princ_state::princ_palette(palette_device &palette) const
{
	// These entries are dynamically set at program start, assuming black as default.
	for (size_t i = 0; i < 16; i++)
	{
		palette.set_pen_color(i, 0, 0, 0);
	}
}

u32 tomy_princ_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	bitmap.fill(0, cliprect);

#ifdef VIDEO_DEBUG
	if (machine().input().code_pressed(KEYCODE_A))
	{
		screen_update_layer(m_layer.get(), LAYER_H, BITPLANE_W, LAYER_WORKAREA_H, screen, bitmap, cliprect);
		return 0;
	}
	else if (machine().input().code_pressed(KEYCODE_S))
	{
		screen_update_layer(m_layer.get(), 0, BITPLANE_W, LAYER_H, screen, bitmap, cliprect);
		return 0;
	}
	else if (machine().input().code_pressed(KEYCODE_D))
	{
		screen_update_layer(m_layer.get(), LAYER_H + LAYER_WORKAREA_H, BITPLANE_W, LAYER_SPRITES_H, screen, bitmap, cliprect);
		return 0;
	}
	else if (machine().input().code_pressed(KEYCODE_F))
	{
		screen_update_layer(m_layer_overlay_sprites.get(), 0, BITPLANE_W, LAYER_H, screen, bitmap, cliprect);
		return 0;
	}
	else if (machine().input().code_pressed(KEYCODE_G))
	{
		screen_update_layer(m_layer_overlay_sprites_priority.get(), 0, BITPLANE_W, LAYER_H, screen, bitmap, cliprect);
		return 0;
	}
#endif

	screen_update_layer(m_layer.get(), 0, BITPLANE_W, LAYER_H, screen, bitmap, cliprect);

	if (m_is_overlay_enabled)
	{
		screen_update_layer(m_layer_overlay_sprites.get(), 0, BITPLANE_W, LAYER_H, screen, bitmap, cliprect);
		screen_update_layer(m_layer_overlay_sprites_priority.get(), 0, BITPLANE_W, LAYER_H, screen, bitmap, cliprect);
	}

	return 0;
}

void tomy_princ_state::screen_update_layer(u8 *layer, u16 layer_offset, u16 layer_w, u16 layer_h, screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
#ifdef VIDEO_DEBUG
	u32 offset = layer_w * layer_offset;
	for (size_t y = 0; y < layer_h; y++)
	{
		for (size_t x = 0; x < layer_w; x++)
		{
			u8 color = layer[offset];
			if (color != 0)
			{
				bitmap.pix(y, x) = m_palette->pen_color(color);
			}
			offset++;
		}
	}
#else
	for (size_t y = 0; y < SCREEN_H; y++)
	{
		u32 offset_y = ((m_vdp.screen_offset_y & 0x0fff) + y) * layer_w;
		for (size_t x = 0; x < SCREEN_W; x++)
		{
			u32 offset_x = m_vdp.screen_offset_x + x;
			u8 color = layer[offset_y + offset_x];
			if (color != 0)
			{
				bitmap.pix(y, x) = m_palette->pen_color(color);
			}
		}
	}
#endif
}

DEVICE_IMAGE_LOAD_MEMBER(tomy_princ_state::cart_load)
{
	u64 length;
	memory_region *cart_rom = nullptr;
	if (m_cart->loaded_through_softlist())
	{
		cart_rom = m_cart->memregion("rom");
		if (!cart_rom)
		{
			return std::make_pair(image_error::BADSOFTWARE, "Software list item has no 'rom' data area");
		}
		length = cart_rom->bytes();
	}
	else
	{
		length = m_cart->length();
	}

	if (!length)
	{
		return std::make_pair(image_error::INVALIDLENGTH, "Cartridges must not be empty");
	}
	if (length & 1)
	{
		return std::make_pair(image_error::INVALIDLENGTH, "Unsupported cartridge size (must be a multiple of 2 bytes)");
	}

	if (!m_cart->loaded_through_softlist())
	{
		cart_rom = machine().memory().region_alloc(m_cart->subtag("rom"), length, 2, ENDIANNESS_LITTLE);
		if (!cart_rom)
		{
			return std::make_pair(std::errc::not_enough_memory, std::string());
		}

		u16 *const base = reinterpret_cast<u16 *>(cart_rom->base());
		if (m_cart->fread(base, length) != length)
		{
			return std::make_pair(std::errc::io_error, "Error reading cartridge file");
		}

		if (ENDIANNESS_NATIVE != ENDIANNESS_LITTLE)
		{
			for (u64 i = 0; (length / 2) > i; ++i)
				base[i] = swapendian_int16(base[i]);
		}
	}

	return std::make_pair(std::error_condition(), std::string());
}

u8 tomy_princ_state::key_r()
{
	LOGMASKED(LOG_REG, "%s: key=%04x\n", machine().describe_context(), m_key);

	u8 data = 0xff;
	for (u8 port = 0; port < 9; port++)
		if (!BIT(m_key, port))
			data &= m_io_keys[port]->read();

	return data;
}

void tomy_princ_state::pdr6_w(u8 data)
{
	m_key = (m_key & ~0x7f) | BIT(data, 0, 7);
}

void tomy_princ_state::pdr8_w(u8 data)
{
	m_key = (m_key & 0x7f) | (BIT(data, 5, 2) << 7);
}

void tomy_princ_state::princ_map(address_map &map)
{
	map(0x68ff00, 0x68ff00).lw8([this] (u8 data) { LOGMASKED(LOG_REG, "%s: 68FF00=%02X\n", machine().describe_context(), data); m_is_ext_reg00_read = true; }, "ext_reg00");
	map(0x68ff0c, 0x68ff0c).lw8([this] (u8 data) { LOGMASKED(LOG_REG, "%s: 68FF0C=%02X\n", machine().describe_context(), data); m_ext_reg0C = data; }, "ext_reg0C");
	map(0x68ff40, 0x68ff40).rw(FUNC(tomy_princ_state::vdp_reg_data_r), FUNC(tomy_princ_state::vdp_reg_data_w));
	map(0x68ff41, 0x68ff41).w(FUNC(tomy_princ_state::vdp_reg_w));
	map(0x68ff42, 0x68ff42).w(FUNC(tomy_princ_state::vdp_pal_data_w));
	map(0x68ff43, 0x68ff43).rw(FUNC(tomy_princ_state::vdp_gfx_data_r), FUNC(tomy_princ_state::vdp_gfx_data_w));
	map(0x68ff44, 0x68ff44).r(FUNC(tomy_princ_state::vdp_stat_r));
	map(0x800000, 0x87ffff).lr8(NAME([]() { return 0xff; }));
	map(0xe00000, 0xe07fff).ram();  // stacks are placed here
	map(0xf00000, 0xffffff).rom().region("maincpu", 0x00000);
}

// FIXME: Identify unknown keys
static INPUT_PORTS_START( tomy_princ )
	PORT_START( "KEY1" )
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Ho") PORT_CODE(KEYCODE_EQUALS) // ほ 82D9h
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Memory") PORT_CODE(KEYCODE_F8) // メモリー FF08h

	PORT_START( "KEY2" )
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Menu") PORT_CODE(KEYCODE_F7) // メニュー FF07h
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("He") PORT_CODE(KEYCODE_BACKSPACE) // へ 82D6h
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Print") PORT_CODE(KEYCODE_F9) // 印刷 FF09h

	PORT_START( "KEY3" )
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_UNUSED)
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("FF17h") PORT_CODE(KEYCODE_END)
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Symbol") PORT_CODE(KEYCODE_F11) // 絵記号 FF0Bh

	PORT_START( "KEY4" )
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Font Color") PORT_CODE(KEYCODE_F10) // 字体色 FF0Ah
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("FF16h") PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Complete") PORT_CODE(KEYCODE_F12) //  文完了 FF0Ch

	PORT_START( "KEY5" )
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_UNUSED)
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Re") PORT_CODE(KEYCODE_BACKSLASH) // れ 82EAh
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("!") PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Romaji") PORT_CODE(KEYCODE_HOME) // ローマ字 FF0Eh

	PORT_START( "KEY6" )
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Delete") PORT_CODE(KEYCODE_INSERT) // ー字消す FF0Dh
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Zoom") PORT_CODE(KEYCODE_PGUP) // ズーム FF0Fh

	PORT_START( "KEY7" )
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) // FF13h
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Ne") PORT_CODE(KEYCODE_RCONTROL) // ね 82CBh
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("NO") PORT_CODE(KEYCODE_RSHIFT) // FF10h
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) // FF12h

	PORT_START( "KEY8" )
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) // FF14h
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Ru") PORT_CODE(KEYCODE_QUOTE) // る 82E9h
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) // FF15h

	PORT_START( "KEY9" )
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Extra Kanji") PORT_CODE(KEYCODE_F6) // 別漢字 FF06h
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("No conversion") PORT_CODE(KEYCODE_F5) // 無変換 FF05h
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("A/a Numeral") PORT_CODE(KEYCODE_F3) // A/a 数 FF03h
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_F1) // シフト FF01h
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Hira/Kata") PORT_CODE(KEYCODE_F2) // ひら/カタ FF02h
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Kanji conversion") PORT_CODE(KEYCODE_F4) // 漢字変換 FF04h
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) // スペース 8140h
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("OK") PORT_CODE(KEYCODE_ENTER) // FF11h
INPUT_PORTS_END

void tomy_princ_state::tomy_princ(machine_config &config)
{
	MB90611A(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &tomy_princ_state::princ_map);
	m_maincpu->port<0x1>().read().set(FUNC(tomy_princ_state::key_r));
	m_maincpu->port<0x6>().write().set(FUNC(tomy_princ_state::pdr6_w));
	m_maincpu->port<0x8>().read().set(FUNC(tomy_princ_state::pdr8_r));
	m_maincpu->port<0x8>().write().set(FUNC(tomy_princ_state::pdr8_w));
	m_maincpu->port<0xa>().read().set(FUNC(tomy_princ_state::pdra_r));
	m_maincpu->reload_timer<1>().tin_hz(16_MHz_XTAL); // Guessed. Waits at startup for 0x7530 underflows with reload = 0x37f

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_screen_update(FUNC(tomy_princ_state::screen_update));
	m_screen->set_size(SCREEN_W, SCREEN_H);
	m_screen->set_visarea_full();

	PALETTE(config, m_palette, FUNC(tomy_princ_state::princ_palette), 16);

	TIMER(config, m_scantimer, 0);
	m_scantimer->configure_scanline(FUNC(tomy_princ_state::scan_interrupt), "screen", 0, 1);

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "princ_cart");
	m_cart->set_endian(ENDIANNESS_LITTLE);
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(tomy_princ_state::cart_load));
	m_cart->set_must_be_loaded(false);

	SOFTWARE_LIST(config, "cart_list").set_original("princ");
}

class tomy_princ_tablet_state : public tomy_princ_state
{
public:
	tomy_princ_tablet_state(const machine_config &mconfig, device_type type, const char *tag)
		: tomy_princ_state(mconfig, type, tag)
		, m_io_pen_x(*this, "PENX")
		, m_io_pen_y(*this, "PENY")
		, m_config(*this, "CONFIG")
		, m_pdra(~0)
	{
#ifdef VIDEO_DEBUG
		SCREEN_W = BITPLANE_W;
		SCREEN_H = LAYER_H;
#else
		SCREEN_W = 324; // 340 - 0x10
		SCREEN_H = 206; // 222 - 0x10
#endif
		SRC_POS = 0x60;
		DST_POS = 0x64;
		SHAPE_SIZE = 0x68;
		SHAPE_DIRECTION = 0x6c;
		SHAPE_MODE = 0x6d;
		SHAPE_PAL = 0x70;
		RENDER_CMD = 0x71;
		RENDER2_CMD = 0xf1;
		LAYER_MODE = 0x42;
		LAYER2_MODE = 0xc2;
		LAYER_OFFSET_Y = 0x43;
		LAYER_OFFSET_X = 0x44;
		LAYER_CLIP_H = 0x45;
		LAYER_CLIP_W = 0x46;
		SCREEN_OFFSET = 0x49;
		PAL_DATA = 0x51;
		SPRITE1_DST_POS_Y = 0x52;
		SPRITE1_DST_POS_X = 0x53;
		SPRITE1_SELECT = 0x54;
		SPRITE2_DST_POS_Y = 0x55;
		SPRITE2_DST_POS_X = 0x56;
		SPRITE2_SELECT = 0x57;
	}

	void tomy_princ_tablet(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual u8 vdp_stat_r() override;
	virtual void pdr6_w(u8 data) override;
	virtual void pdr8_w(u8 data) override;

private:
	void pdr9_w(u8 data);
	void pdra_w(u8 data);
	u16 adc5_r();
	u16 adc6_r();

	required_ioport m_io_pen_x;
	required_ioport m_io_pen_y;
	required_ioport m_config;
	u8 m_pdra;
};

void tomy_princ_tablet_state::tomy_princ_tablet(machine_config &config)
{
	tomy_princ_state::tomy_princ(config);

	m_maincpu->port<0x9>().write().set(FUNC(tomy_princ_tablet_state::pdr9_w));
	m_maincpu->port<0xa>().write().set(FUNC(tomy_princ_tablet_state::pdra_w));
	m_maincpu->adc().channel<5>().set(FUNC(tomy_princ_tablet_state::adc5_r));
	m_maincpu->adc().channel<6>().set(FUNC(tomy_princ_tablet_state::adc6_r));
}

void tomy_princ_tablet_state::machine_start()
{
	tomy_princ_state::machine_start();

	save_item(NAME(m_pdra));
}

u8 tomy_princ_tablet_state::vdp_stat_r()
{
	u8 stat = m_screen->vblank() ? 0x11 : 0x10;

	if (m_is_bound_found)
	{
		stat |= 0x20;
	}

	const bool is_buffer_render_cmd = m_vdp.regs[RENDER_CMD] == BUFFER_READ
			|| m_vdp.regs[RENDER2_CMD] == BUFFER_READ
			|| m_vdp.regs[RENDER_CMD] == BUFFER_WRITE
			|| m_vdp.regs[RENDER2_CMD] == BUFFER_WRITE;
	if (is_buffer_render_cmd)
	{
		return m_vdp.pending_pixels > 0 ? stat | 0x8 : stat & 1;
	}

	return stat;
}

void tomy_princ_tablet_state::pdr6_w(u8 data)
{
	m_key = (m_key & ~0x1f) | BIT(data, 0, 5);
}

void tomy_princ_tablet_state::pdr8_w(u8 data)
{
}

void tomy_princ_tablet_state::pdr9_w(u8 data)
{
	m_key = (m_key & 0x1f) | (BIT(data, 2, 3) << 5);
}

void tomy_princ_tablet_state::pdra_w(u8 data)
{
	m_pdra = data;
}

u16 tomy_princ_tablet_state::adc5_r()
{
	if (BIT(m_config->read(), 0))
	{
		if (BIT(m_pdra, 3)) // PDRA = u8 & 0x80 | 0x48; ADCS = 5 | (5 << 3);
		{
			// Pen y-position
			const s16 pen_y = m_io_pen_y->read();
			LOGMASKED(LOG_REG, "%s: adcr_r pen y=%04x, PDRA=%02x\n", machine().describe_context(), pen_y, m_pdra);
			return pen_y;
		}

		LOGMASKED(LOG_REG, "%s: adc5_r unknown state, PDRA=%02x\n", machine().describe_context(), m_pdra);
	}

	return 0;
}

u16 tomy_princ_tablet_state::adc6_r()
{
	if (BIT(m_config->read(), 0))
	{
		if (BIT(m_pdra, 4)) // PDRA = u8 & 0x80 | 0x30; ADCS = 6 | (6 << 3);
		{
			// Pen x-position
			const s16 pen_x = m_io_pen_x->read();
			LOGMASKED(LOG_REG, "%s: adc6_r pen x=%04x, PDRA=%02x\n", machine().describe_context(), pen_x, m_pdra);
			return pen_x;
		}
		else if (BIT(m_pdra, 5)) // PDRA = u8 & 0x80 | 0x20;
		{
			// Program code checks for value >= 600 before reading pen position,
			// likely some time-based counter of pen touching tablet. We just send max value.
			const u16 pen_stat = 0x3ff;
			LOGMASKED(LOG_REG, "%s: adcr_r pen stat=%04x, PDRA=%02x\n", machine().describe_context(), pen_stat, m_pdra);
			return pen_stat;
		}

		LOGMASKED(LOG_REG, "%s: adc6_r unknown state, PDRA=%02x\n", machine().describe_context(), m_pdra);
	}

	return 0;
}

// FIXME: Identify unknown keys
static INPUT_PORTS_START( tomy_princ_tablet )
	PORT_START("CONFIG")
	PORT_CONFNAME( 0x01, 0x00, "Controller" )
	PORT_CONFSETTING( 0x00, DEF_STR( None ) )
	PORT_CONFSETTING( 0x01, "Pen" )

	PORT_START( "PENX" )
	PORT_BIT( 0x3ff, 0x214, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x068, 0x03c0) PORT_PLAYER(1) PORT_NAME("PEN X")

	PORT_START( "PENY" )
	PORT_BIT( 0x3ff, 0x214, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x078, 0x3b0) PORT_PLAYER(1) PORT_NAME("PEN Y")

	PORT_START( "KEY1" )
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_F1) // シフト FF01h
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Ho") PORT_CODE(KEYCODE_EQUALS) // ほ 82D9h

	PORT_START( "KEY2" )
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Hira/Kata") PORT_CODE(KEYCODE_F2) // ひら/カタ FF02h
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_UNUSED)
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("FF07h") PORT_CODE(KEYCODE_F7) // F378h

	PORT_START( "KEY3" )
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("A/a Numeral") PORT_CODE(KEYCODE_F3) // A/a 数 FF03h
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_UNUSED)
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("?") PORT_CODE(KEYCODE_SLASH)

	PORT_START( "KEY4" )
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Kanji conversion") PORT_CODE(KEYCODE_F4) // 漢字変換 FF04h
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Ru") PORT_CODE(KEYCODE_QUOTE) // る 82E9h
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_UNUSED)
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)

	PORT_START( "KEY5" )
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_UNUSED)
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Ne") PORT_CODE(KEYCODE_RCONTROL) // ね 82CBh
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("No conversion") PORT_CODE(KEYCODE_F5) // 無変換 FF05h
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("FF0Bh") PORT_CODE(KEYCODE_F11)

	PORT_START( "KEY6" )
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_UNUSED)
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Re") PORT_CODE(KEYCODE_BACKSLASH) // れ 82EAh
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) // スペース 8140h
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("!") PORT_CODE(KEYCODE_COLON)

	PORT_START( "KEY7" )
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("FF0Ah") PORT_CODE(KEYCODE_F10)
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Extra Kanji") PORT_CODE(KEYCODE_F6) // 別漢字 FF06h
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("FF08h") PORT_CODE(KEYCODE_F8) // F379h

	PORT_START( "KEY8" )
	PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("FF09h") PORT_CODE(KEYCODE_F9)
	PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
	PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Ok (T)") PORT_CODE(KEYCODE_ENTER) // FF0Ch
	PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("He") PORT_CODE(KEYCODE_BACKSPACE) // へ 82D6h

	PORT_START( "KEY9" )
	PORT_BIT(0xff,IP_ACTIVE_LOW,IPT_UNUSED)
INPUT_PORTS_END

// PCB markings: Prin-C Tablet E100-T201-11
ROM_START( princ )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD("29f800t.u4", 0x00000, 0x100000, CRC(30b6b864) SHA1(7ada3af85dd8dd3f95ca8965ad8e642c26445293))
ROM_END

// PCB markings: Prin-C E100-T001-11
ROM_START( princnt )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD("tc538000.u3", 0x00000, 0x100000, CRC(e4e2bfe9) SHA1(b3a7727544918b9030c362694ddf9a2fc3bca8b4))
ROM_END

ROM_START( princnt3 )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD("tc538000.3.u3", 0x00000, 0x100000, CRC(eed0a0f9) SHA1(c15b97c2c29b354136cef046f72a6443b415e673))
ROM_END

} // anonymous namespace

// Back case and ICs dated 1996
COMP( 1996?, princnt,   princ,   0, tomy_princ,        tomy_princ,        tomy_princ_state,        empty_init, "Tomy", "Prin-C (Rev 1)", MACHINE_NO_SOUND | MACHINE_NODEVICE_PRINTER )
COMP( 1996?, princnt3,  princ,   0, tomy_princ,        tomy_princ,        tomy_princ_state,        empty_init, "Tomy", "Prin-C (Rev 3)", MACHINE_NO_SOUND | MACHINE_NODEVICE_PRINTER )

// ICs dated 1997; promo from 1998: https://web.archive.org/web/19990302035001/http://www.tomy.co.jp/toyking/osirase/PrinC/
COMP( 1997?, princ,     0,       0, tomy_princ_tablet, tomy_princ_tablet, tomy_princ_tablet_state, empty_init, "Tomy", "Prin-C Tablet",  MACHINE_NO_SOUND | MACHINE_NODEVICE_PRINTER )



tosh1000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************

    Toshiba T1000 portable

    80C88 CPU @ 5 MHz [OKI MSM80C88A-10GS-K (56 pin PQFP)]
    512KB RAM + 16KB video RAM
    32KB BIOS ROM [Toshiba TC54256AD]
    256KB MS-DOS 2.11 ROM [Toshiba TC534000]
    SuperIO chip (Toshiba T7885, T7885A or T7885B) = 82C84 + 82C88 + 82C59 + upd765 + 82C53 + 82C37 + 82C55
    Real Time Clock chip: TC8521P
    Keyboard controller: 80C50
    RS232C controller: TC8570F (8250 compatible)

    Other chips seen on board photo:

    DC2131P137A
    DC2130P174A
    TC5565AFL-15 x2
    TC53257F    32KB Mask ROM (chargen?)
    DC2___P13_A

    To do:
    - floppy
    - backup ram (stores config.sys)
    - HardRAM (static RAM board)
    - native keyboard (MCU dump missing)
    - font selector (system register 0x5A; DIP switches PJ20, PJ21)
    - display contrast and type (CRTC register 0x12)

    Useful links:
    - board photo: http://s8.hostingkartinok.com/uploads/images/2016/05/579e9d152bc772d9c16bc8ac611eb97f.jpg
    - manuals: http://www.minuszerodegrees.net/manuals/Toshiba/Toshiba.htm
    - http://www.seasip.info/VintagePC/t1000.html

***************************************************************************/


#include "emu.h"
#include "machine/genpc.h"
#include "tosh1000_bram.h"

#include "bus/isa/isa_cards.h"
#include "bus/pc_kbd/keyboards.h"
#include "bus/pc_kbd/pc_kbdc.h"
#include "cpu/i86/i86.h"
#include "machine/bankdev.h"
#include "machine/ram.h"
#include "machine/rp5c01.h"

#include "softlist.h"


#define LOG_KEYBOARD  (1U << 1)
#define LOG_DEBUG     (1U << 2)

//#define VERBOSE (LOG_DEBUG)
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"


namespace {

#define LOGKBD(...) LOGMASKED(LOG_KEYBOARD, __VA_ARGS__)
#define LOGDBG(...) LOGMASKED(LOG_DEBUG, __VA_ARGS__)


class tosh1000_state : public driver_device
{
public:
	tosh1000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_bankdev(*this, "bankdev")
		, m_bram(*this, "bram")
		{ }

	void tosh1000(machine_config &config);

	void init_tosh1000();

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	void romdos_bank_w(uint8_t data);

	void bram_w(offs_t offset, uint8_t data);
	uint8_t bram_r(offs_t offset);

	static void cfg_fdc_35(device_t *device);
	void tosh1000_io(address_map &map) ATTR_COLD;
	void tosh1000_map(address_map &map) ATTR_COLD;
	void tosh1000_romdos(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<address_map_bank_device> m_bankdev;
	required_device<tosh1000_bram_device> m_bram;

	enum {
		IDLE, READ_DATA, WRITE_DATA
	};

	bool m_bram_latch = false;
	int m_bram_offset = 0;
	int m_bram_state = IDLE;
};


void tosh1000_state::init_tosh1000()
{
}

void tosh1000_state::machine_reset()
{
	m_bram_latch = false;
	m_bram_offset = 0;
	m_bram_state = IDLE;
	m_bankdev->set_bank(8);
}


void tosh1000_state::romdos_bank_w(uint8_t data)
{
	LOGDBG("ROM-DOS <- %02x (%s, accessing bank %d)\n", data, BIT(data, 7)?"enable":"disable", data&7);

	if (BIT(data, 7))
	{
		m_bankdev->set_bank(data & 7);
	}
	else
	{
		m_bankdev->set_bank(8);
	}
}

void tosh1000_state::bram_w(offs_t offset, uint8_t data)
{
	LOGDBG("BRAM %02x <- %02x\n", 0xc0 + offset, data);

	switch (offset)
	{
	case 1:
		if (m_bram_latch)
		{
			LOG("Backup RAM %02x <- %02x\n", m_bram_offset % 160, data);
			m_bram->write(m_bram_offset % 160, data);
			m_bram_offset++;
		}
		else
		{
			if (m_bram_state == WRITE_DATA)
			{
				m_bram_latch = true;
				m_bram_offset = 0;
			}
		}
		break;

	case 3:
		switch (data & 0xc0)
		{
		case 0:
			m_bram_state = IDLE;
			break;

		case 0x40:
			m_bram_state = READ_DATA;
			break;

		case 0x80:
			m_bram_state = WRITE_DATA;
			m_bram_latch = false;
			break;

		default:
			m_bram_state = IDLE;
			break;
		}
		break;
	}
}

uint8_t tosh1000_state::bram_r(offs_t offset)
{
	uint8_t data = 0;

	switch (offset)
	{
	case 2:
		if (m_bram_state == READ_DATA)
		{
			data = m_bram->read(m_bram_offset % 160);
			LOG("BRAM @ %02x == %02x\n", m_bram_offset % 160, data);
			m_bram_offset++;
		}
		break;

	case 3:
		data = 0x2c;
		switch (m_bram_state)
		{
		case IDLE:
			// bit 4 -- floppy drive disk change signal
			break;

		case READ_DATA:
			data |= 0x43; // always ready to read (and write ??)
			break;

		case WRITE_DATA:
			data |= 0x82; // always ready to write
			break;
		}
		break;
	}

	LOGDBG("BRAM %02x == %02x\n", 0xc0 + offset, data);

	return data;
}


void tosh1000_state::tosh1000_romdos(address_map &map)
{
	map(0x00000, 0x0ffff).rom().region("romdos", 0);
	map(0x10000, 0x1ffff).rom().region("romdos", 0x10000);
	map(0x20000, 0x2ffff).rom().region("romdos", 0x20000);
	map(0x30000, 0x3ffff).rom().region("romdos", 0x30000);
	map(0x40000, 0x4ffff).rom().region("romdos", 0x40000);
	map(0x50000, 0x5ffff).rom().region("romdos", 0x50000);
	map(0x60000, 0x6ffff).rom().region("romdos", 0x60000);
	map(0x70000, 0x7ffff).rom().region("romdos", 0x70000);
}

void tosh1000_state::tosh1000_map(address_map &map)
{
	map.unmap_value_high();
	map(0xa0000, 0xaffff).rw(m_bankdev, FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0xf8000, 0xfffff).rom().region("bios", 0);
}

void tosh1000_state::tosh1000_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).m("mb", FUNC(ibm5160_mb_device::map));
	map(0x00c0, 0x00c3).rw(FUNC(tosh1000_state::bram_r), FUNC(tosh1000_state::bram_w));
	map(0x00c8, 0x00c8).w(FUNC(tosh1000_state::romdos_bank_w));    // ROM-DOS page select [p. B-15]
	map(0x02c0, 0x02df).rw("rtc", FUNC(tc8521_device::read), FUNC(tc8521_device::write));
}


void tosh1000_state::cfg_fdc_35(device_t *device)
{
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:0")).set_default_option("35dd");
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:0")).set_fixed(true);
	dynamic_cast<device_slot_interface &>(*device->subdevice("fdc:1")).set_default_option(nullptr);
}

void tosh1000_state::tosh1000(machine_config &config)
{
	I8088(config, m_maincpu, XTAL(5'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &tosh1000_state::tosh1000_map);
	m_maincpu->set_addrmap(AS_IO, &tosh1000_state::tosh1000_io);
	m_maincpu->set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb));

	ADDRESS_MAP_BANK(config, "bankdev").set_map(&tosh1000_state::tosh1000_romdos).set_options(ENDIANNESS_LITTLE, 8, 20, 0x10000);

	ibm5160_mb_device &mb(IBM5160_MOTHERBOARD(config, "mb"));
	mb.set_cputag(m_maincpu);
	mb.int_callback().set_inputline(m_maincpu, 0);
	mb.nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	mb.kbdclk_callback().set("kbd", FUNC(pc_kbdc_device::clock_write_from_mb));
	mb.kbddata_callback().set("kbd", FUNC(pc_kbdc_device::data_write_from_mb));

	TC8521(config, "rtc", XTAL(32'768));

	// FIXME: determine ISA bus clock
	ISA8_SLOT(config, "isa1", 0, "mb:isa", pc_isa8_cards, "cga", false);
	ISA8_SLOT(config, "isa2", 0, "mb:isa", pc_isa8_cards, "fdc_xt", false).set_option_machine_config("fdc_xt", cfg_fdc_35);
	ISA8_SLOT(config, "isa3", 0, "mb:isa", pc_isa8_cards, "lpt", false);
	ISA8_SLOT(config, "isa4", 0, "mb:isa", pc_isa8_cards, "com", false);
	ISA8_SLOT(config, "isa5", 0, "mb:isa", pc_isa8_cards, nullptr, false);
	ISA8_SLOT(config, "isa6", 0, "mb:isa", pc_isa8_cards, nullptr, false);

//  SOFTWARE_LIST(config, "flop_list").set_original("tosh1000");

	// TODO: uses a 80C50 instead of 8042 for KBDC
	pc_kbdc_device &kbd(PC_KBDC(config, "kbd", pc_xt_keyboards, STR_KBD_KEYTRONIC_PC3270));
	kbd.out_clock_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_clock_w));
	kbd.out_data_cb().set("mb", FUNC(ibm5160_mb_device::keyboard_data_w));

	RAM(config, RAM_TAG).set_default_size("512K");

	TOSH1000_BRAM(config, m_bram, 0);
}


ROM_START( tosh1000 )
	ROM_REGION(0x8000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "v410", "V4.10")
	ROMX_LOAD( "026f.27c256.ic25", 0x0000, 0x8000, CRC(a854939f) SHA1(0ff532f295a40716f53949a2fd64d02bf76d575a), ROM_BIOS(0))

	ROM_REGION(0x80000, "romdos", 0)
	ROM_LOAD("tc534000p__b004.dos.ic26", 0x0000, 0x80000, CRC(716027f6) SHA1(563e3a7e1961d4cda216169bd1ecc66925a101aa))

	/* XXX IBM 1501981(CGA) and 1501985(MDA) Character rom */
	ROM_REGION(0x2000, "gfx1", 0)
	ROM_LOAD("5788005.u33", 0x00000, 0x2000, CRC(0bf56d70) SHA1(c2a8b10808bf51a3c123ba3eb1e9dd608231916f)) /* "AMI 8412PI // 5788005 // (C) IBM CORP. 1981 // KOREA" */
ROM_END

} // Anonymous namespace


//    YEAR  NAME      PARENT   COMPAT  MACHINE   INPUT  CLASS           INIT           COMPANY    FULLNAME         FLAGS
COMP( 1987, tosh1000, ibm5150, 0,      tosh1000, 0,     tosh1000_state, init_tosh1000, "Toshiba", "Toshiba T1000", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



tr175.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************************

Skeleton driver for Relisys TR-175 II color terminal.

************************************************************************************************************************************/

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "machine/mc68681.h"
#include "video/ramdac.h"
#include "video/scn2674.h"
#include "screen.h"


namespace {

class tr175_state : public driver_device
{
public:
	tr175_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void tr175(machine_config &config);

private:
	void ffec01_w(uint8_t data);
	void fff000_w(uint8_t data);
	uint8_t fff400_r();
	SCN2674_DRAW_CHARACTER_MEMBER(draw_character);

	void mem_map(address_map &map) ATTR_COLD;
	void ramdac_map(address_map &map) ATTR_COLD;
	void vram_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};

void tr175_state::ffec01_w(uint8_t data)
{
	logerror("%s: Writing %02X to FFEC01\n", machine().describe_context(), data);
}

void tr175_state::fff000_w(uint8_t data)
{
	logerror("%s: Writing %02X to FFF000\n", machine().describe_context(), data);
}

uint8_t tr175_state::fff400_r()
{
	return 0;
}

void tr175_state::mem_map(address_map &map)
{
	map(0x000000, 0x01ffff).rom().region("maincpu", 0);
	map(0xfe8000, 0xfebfff).ram(); // 8-bit?
	map(0xfefe00, 0xfefedd).nopw(); // 8-bit; cleared at startup
	map(0xff8000, 0xffbfff).ram(); // main RAM
	map(0xff0000, 0xff7fff).ram(); // video RAM?
	map(0xffe000, 0xffe01f).rw("duart", FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask16(0xff00);
	map(0xffe400, 0xffe40f).rw("avdc", FUNC(scn2674_device::read), FUNC(scn2674_device::write)).umask16(0xff00);
	map(0xffe800, 0xffe805).unmaprw(); //.rw("pai", FUNC(um82c11_device::read), FUNC(um82c11_device::write)).umask16(0xff00);
	map(0xffec01, 0xffec01).w(FUNC(tr175_state::ffec01_w));
	map(0xfff000, 0xfff000).w(FUNC(tr175_state::fff000_w));
	map(0xfff400, 0xfff400).r(FUNC(tr175_state::fff400_r));
	map(0xfffc01, 0xfffc01).w("ramdac", FUNC(ramdac_device::index_w));
	map(0xfffc03, 0xfffc03).w("ramdac", FUNC(ramdac_device::pal_w));
	map(0xfffc05, 0xfffc05).w("ramdac", FUNC(ramdac_device::mask_w));
}

SCN2674_DRAW_CHARACTER_MEMBER(tr175_state::draw_character)
{
}

void tr175_state::vram_map(address_map &map)
{
	map(0x0000, 0x3fff).nopr();
}

void tr175_state::ramdac_map(address_map &map)
{
	map(0x000, 0x3ff).rw("ramdac", FUNC(ramdac_device::ramdac_pal_r), FUNC(ramdac_device::ramdac_rgb666_w));
}

static INPUT_PORTS_START( tr175 )
INPUT_PORTS_END

void tr175_state::tr175(machine_config &config)
{
	M68000(config, m_maincpu, 12'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &tr175_state::mem_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(28.322_MHz_XTAL, 900, 0, 720, 449, 0, 416); // guess
	screen.set_screen_update("avdc", FUNC(scn2674_device::screen_update));

	scn2674_device &avdc(SCN2674(config, "avdc", 28.322_MHz_XTAL / 18)); // guess
	avdc.intr_callback().set_inputline("maincpu", M68K_IRQ_2);
	avdc.set_character_width(18); // guess
	avdc.set_display_callback(FUNC(tr175_state::draw_character));
	avdc.set_addrmap(0, &tr175_state::vram_map);
	avdc.set_screen("screen");

	scn2681_device &duart(SCN2681(config, "duart", 11.0592_MHz_XTAL / 3)); // is this the right clock?
	duart.irq_cb().set_inputline("maincpu", M68K_IRQ_1);

	PALETTE(config, "palette").set_entries(0x100);
	ramdac_device &ramdac(RAMDAC(config, "ramdac", 0, "palette"));
	ramdac.set_addrmap(0, &tr175_state::ramdac_map);
}



/**************************************************************************************************************

Relisys TR-175 II.
Chips: MC68000P12, HM82C11C, SCN2681, 3x W24257-70L, KDA0476BCN-66 (RAMDAC), 4 undumped proms, Beeper, Button battery
Crystals: 28.322, 46.448, 11.0592, unknown.
Colour screen (VGA).

***************************************************************************************************************/

ROM_START( tr175 )
	ROM_REGION(0x20000, "maincpu", 0)
	ROM_LOAD16_BYTE( "v6.05.u50", 0x00001, 0x10000, CRC(5a33b6b3) SHA1(d673f50dd88f8a154ddaabe34cfcc9ab91435a4c) )
	ROM_LOAD16_BYTE( "v6.05.u45", 0x00000, 0x10000, CRC(e220befe) SHA1(8402280577e6de4b85843222bbd6b06a3f625b3b) )
ROM_END

} // anonymous namespace


COMP( 1982, tr175, 0, 0, tr175, tr175, tr175_state, empty_init, "Relisys", "TR-175 II", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



tranz330.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/**************************************************************************

    VeriFone Tranz 330

    All information gleaned from:
    http://www.bigmessowires.com/2011/05/10/mapping-the-tranz-330/

    Currently sits in a loop doing very little, based on the disassembly
    it presumably needs some kind of interrupt in order to kick it into
    running.

    Interrupt Vectors are located at 0200-02FF.
    Display ram at 9000-90FF says GRAMING ERR 0 (part of PROGRAMING ERR message)

    TODO:
    - get working, driver needs a Z80 peripheral expert to look at it.
    - hook up magstripe reader

****************************************************************************/

#include "emu.h"
#include "tranz330.h"

#include "machine/input_merger.h"
#include "speaker.h"

#include "tranz330.lh"


void tranz330_state::tranz330_mem(address_map &map)
{
	map(0x0000, 0x7fff).rom();
	map(0x8000, 0xffff).ram();
}

void tranz330_state::tranz330_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).rw(m_pio,  FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x10, 0x13).rw(m_ctc,  FUNC(z80ctc_device::read),     FUNC(z80ctc_device::write));
	map(0x20, 0x23).rw(m_dart, FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
	map(0x30, 0x3f).rw(m_rtc,  FUNC(msm6242_device::read),    FUNC(msm6242_device::write));
}

static void construct_ioport_tranz330(device_t &owner, ioport_list &portlist, std::ostream &errorbuf)
{
	ioport_configurer configurer(owner, portlist, errorbuf);

	configurer.port_alloc("COL.0");
	configurer.field_alloc(IPT_BUTTON1, IP_ACTIVE_LOW, 0x01).field_add_code(SEQ_TYPE_STANDARD, KEYCODE_1).field_set_name("1 QZ.");
	configurer.field_alloc(IPT_BUTTON2, IP_ACTIVE_LOW, 0x02).field_add_code(SEQ_TYPE_STANDARD, KEYCODE_4).field_set_name("4 GHI");
	configurer.field_alloc(IPT_BUTTON3, IP_ACTIVE_LOW, 0x04).field_add_code(SEQ_TYPE_STANDARD, KEYCODE_7).field_set_name("7 PRS");
	configurer.field_alloc(IPT_BUTTON4, IP_ACTIVE_LOW, 0x08).field_add_code(SEQ_TYPE_STANDARD, KEYCODE_ASTERISK).field_set_name("* ,'\"");

	configurer.port_alloc("COL.1");
	configurer.field_alloc(IPT_BUTTON5, IP_ACTIVE_LOW, 0x01).field_add_code(SEQ_TYPE_STANDARD, KEYCODE_2).field_set_name("2 ABC");
	configurer.field_alloc(IPT_BUTTON6, IP_ACTIVE_LOW, 0x02).field_add_code(SEQ_TYPE_STANDARD, KEYCODE_5).field_set_name("5 JKL");
	configurer.field_alloc(IPT_BUTTON7, IP_ACTIVE_LOW, 0x04).field_add_code(SEQ_TYPE_STANDARD, KEYCODE_8).field_set_name("8 TUV");
	configurer.field_alloc(IPT_BUTTON8, IP_ACTIVE_LOW, 0x08).field_add_code(SEQ_TYPE_STANDARD, KEYCODE_0).field_set_name("0 -SP");

	configurer.port_alloc("COL.2");
	configurer.field_alloc(IPT_BUTTON9,  IP_ACTIVE_LOW, 0x01).field_add_code(SEQ_TYPE_STANDARD, KEYCODE_3).field_set_name("3 DEF");
	configurer.field_alloc(IPT_BUTTON10, IP_ACTIVE_LOW, 0x02).field_add_code(SEQ_TYPE_STANDARD, KEYCODE_6).field_set_name("6 MNO");
	configurer.field_alloc(IPT_BUTTON11, IP_ACTIVE_LOW, 0x04).field_add_code(SEQ_TYPE_STANDARD, KEYCODE_9).field_set_name("9 WXY");
	configurer.field_alloc(IPT_BUTTON12, IP_ACTIVE_LOW, 0x08).field_add_code(SEQ_TYPE_STANDARD, KEYCODE_H).field_set_name("#");  // KEYCODE_H for 'hash mark'

	configurer.port_alloc("COL.3");
	configurer.field_alloc(IPT_BUTTON13, IP_ACTIVE_LOW, 0x01).field_add_code(SEQ_TYPE_STANDARD, KEYCODE_C)        .field_set_name("CLEAR"); // KEYCODE_C so as to not collide with potentially-used UI keys like DEL
	configurer.field_alloc(IPT_BUTTON14, IP_ACTIVE_LOW, 0x02).field_add_code(SEQ_TYPE_STANDARD, KEYCODE_BACKSPACE).field_set_name("BACK SPACE");
	configurer.field_alloc(IPT_BUTTON15, IP_ACTIVE_LOW, 0x04).field_add_code(SEQ_TYPE_STANDARD, KEYCODE_A)        .field_set_name("ALPHA");
	configurer.field_alloc(IPT_START1,   IP_ACTIVE_LOW, 0x08).field_add_code(SEQ_TYPE_STANDARD, KEYCODE_ENTER)    .field_set_name("FUNC | ENTER");  // KEYCODE_H for 'hash mark'
}


void tranz330_state::machine_start()
{
}

void tranz330_state::machine_reset()
{
}

void tranz330_state::syncb_w(int state)
{
}

void tranz330_state::sound_w(int state)
{
	m_speaker->level_w(state);
	m_ctc->trg3(state);
}

void tranz330_state::clock_w(int state)
{
	// Ch 0 and 1 might be DART Ch A & B baud clocks
	//m_ctc->trg0(state);
	//m_ctc->trg1(state);
	// Ch 2 speaker clock
	m_ctc->trg2(state);
}

uint8_t tranz330_state::card_r()
{
	// return 0xff for a magstripe 0, return 0x00 for a magstripe 1.
	// an interrupt should be triggered on the Z80 when magstripe reading begins.
	// external contributors are encouraged to hook this up.
	return 0xff;
}

void tranz330_state::pio_a_w(uint8_t data)
{
	m_keypad_col_mask = data & 0xf;
	m_vfd->por ((data >> 4) & 1);
	m_vfd->data((data >> 5) & 1);
	m_vfd->sclk((data >> 6) & 1);
}

uint8_t tranz330_state::pio_b_r()
{
	uint8_t input_mask = 0xf;
	for (int i = 0; i < 4; i++)
	{
		if (!BIT(m_keypad_col_mask, i))
		{
			input_mask &= m_keypad[i]->read();
		}
	}
	return input_mask;
}

static const z80_daisy_config tranz330_daisy_chain[] =
{
	{ DART_TAG },
	{ CTC_TAG },
	{ PIO_TAG },
	{ nullptr }
};

// * - check clocks
// ? - check purported RS232 hookup, inconsistent information found at the relevant webpage vs. user-submitted errata
void tranz330_state::tranz330(machine_config &config)
{
	Z80(config, m_cpu, XTAL(7'159'090)/2); //*
	m_cpu->set_addrmap(AS_PROGRAM, &tranz330_state::tranz330_mem);
	m_cpu->set_addrmap(AS_IO, &tranz330_state::tranz330_io);
	m_cpu->set_daisy_config(tranz330_daisy_chain);

	CLOCK(config, "ctc_clock", XTAL(7'159'090)/4) // ?
			.signal_handler().set(FUNC(tranz330_state::clock_w));

	MSM6242(config, RTC_TAG, XTAL(32'768));

	INPUT_MERGER_ANY_HIGH(config, "irq")
			.output_handler().set_inputline(m_cpu, INPUT_LINE_IRQ0);

	Z80PIO(config, m_pio, XTAL(7'159'090)/2); //*
	m_pio->out_int_callback().set("irq", FUNC(input_merger_device::in_w<0>)); //*
	m_pio->out_pa_callback().set(FUNC(tranz330_state::pio_a_w));
	m_pio->in_pa_callback().set(FUNC(tranz330_state::card_r));
	m_pio->in_pb_callback().set(FUNC(tranz330_state::pio_b_r));

	Z80DART(config, m_dart, XTAL(7'159'090)/2); //*
	m_dart->out_syncb_callback().set(FUNC(tranz330_state::syncb_w));
	m_dart->out_txdb_callback().set(m_rs232, FUNC(rs232_port_device::write_txd)); //?
	m_dart->out_dtrb_callback().set(m_rs232, FUNC(rs232_port_device::write_dtr)); //?
	m_dart->out_rtsb_callback().set(m_rs232, FUNC(rs232_port_device::write_rts)); //?
	m_dart->out_int_callback().set("irq", FUNC(input_merger_device::in_w<1>));

	Z80CTC(config, m_ctc, XTAL(7'159'090)/2); //*
	m_ctc->zc_callback<2>().set(FUNC(tranz330_state::sound_w));
	m_ctc->intr_callback().set("irq", FUNC(input_merger_device::in_w<2>));

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set(m_dart, FUNC(z80dart_device::rxb_w));
	m_rs232->dcd_handler().set(m_dart, FUNC(z80dart_device::dcdb_w));
	m_rs232->cts_handler().set(m_dart, FUNC(z80dart_device::ctsb_w));

	// video
	MIC10937(config, VFD_TAG).set_port_value(0);
	config.set_default_layout(layout_tranz330);

	// sound
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker", 0)
			.add_route(ALL_OUTPUTS, "mono", 0.25);
}


ROM_START( tranz330 )
	ROM_REGION( 0x8000, CPU_TAG, 0 )
	ROM_LOAD( "tranz330-original.bin", 0x0000, 0x8000, CRC(af2bf474) SHA1(7896ed23b22b8e1730b689df83eb7bbf7b8dd130))
ROM_END


//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY      FULLNAME        FLAGS
COMP( 1985, tranz330, 0,      0,      tranz330, tranz330, tranz330_state, empty_init, "VeriFone",  "Tranz 330",    0 )



tricep.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

        Morrow Tricep

        12/05/2009 Skeleton driver.

        Several of the boards apparently used in this S-100 system were
        made by CompuPro/Viasyn, including the CPU 68K and Interfacer 3
        (2651 USART multiplexer).

****************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
//#include "bus/s100/s100.h"
#include "cpu/m68000/m68000.h"
#include "machine/scn_pci.h"


namespace {

class tricep_state : public driver_device
{
public:
	tricep_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_usart(*this, "usart%u", 0U)
		, m_ram(*this, "mainram")
	{
	}

	void tricep(machine_config &config);
private:
	void usart_select_w(uint8_t data);
	uint8_t usart_r(offs_t offset);
	void usart_w(offs_t offset, uint8_t data);

	void mem_map(address_map &map) ATTR_COLD;

	void machine_reset() override ATTR_COLD;
	void machine_start() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device_array<scn2651_device, 4> m_usart;
	required_shared_ptr<uint16_t> m_ram;

	uint8_t m_mux = 0;
};



void tricep_state::usart_select_w(uint8_t data)
{
	m_mux = data & 3;
}

uint8_t tricep_state::usart_r(offs_t offset)
{
	return m_usart[m_mux]->read(offset);
}

void tricep_state::usart_w(offs_t offset, uint8_t data)
{
	m_usart[m_mux]->write(offset, data);
}

void tricep_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000000, 0x0007ffff).ram().share("mainram");
	map(0x00fd0000, 0x00fd1fff).rom().region("maincpu", 0);
	map(0x00ff0028, 0x00ff002b).rw(FUNC(tricep_state::usart_r), FUNC(tricep_state::usart_w));
	map(0x00ff002e, 0x00ff002f).nopr();
	map(0x00ff002f, 0x00ff002f).w(FUNC(tricep_state::usart_select_w));
}

/* Input ports */
static INPUT_PORTS_START( tricep )
INPUT_PORTS_END


void tricep_state::machine_reset()
{
	uint8_t* bios = memregion("maincpu")->base();

	memcpy((uint8_t*)m_ram.target(),bios,0x2000);

	m_maincpu->reset();

	m_mux = 0;
}

void tricep_state::machine_start()
{
	save_item(NAME(m_mux));
}

static const input_device_default terminal_defaults[] =
{
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
	{ nullptr, 0, 0 }
};

void tricep_state::tricep(machine_config &config)
{
	M68000(config, m_maincpu, 20_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &tricep_state::mem_map);
	// TODO: MC68451 MMU

	SCN2651(config, m_usart[0], 5.0688_MHz_XTAL);
	m_usart[0]->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_usart[0]->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_usart[0]->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));

	SCN2651(config, m_usart[1], 5.0688_MHz_XTAL);
	SCN2651(config, m_usart[2], 5.0688_MHz_XTAL);
	SCN2651(config, m_usart[3], 5.0688_MHz_XTAL);

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_usart[0], FUNC(scn2651_device::rxd_w));
	rs232.dsr_handler().set(m_usart[0], FUNC(scn2651_device::dsr_w));
	rs232.dcd_handler().set(m_usart[0], FUNC(scn2651_device::dcd_w));
	rs232.cts_handler().set(m_usart[0], FUNC(scn2651_device::cts_w));
	rs232.set_option_device_input_defaults("terminal", terminal_defaults);
}

/* ROM definition */
ROM_START( tricep )
	ROM_REGION16_BE( 0x2000, "maincpu", 0 )
	ROM_LOAD16_BYTE( "tri2.4_odd.u37",  0x0001, 0x1000, CRC(31eb2dcf) SHA1(2d9df9262ee1096d0398505e10d209201ac49a5d))
	ROM_LOAD16_BYTE( "tri2.4_even.u36", 0x0000, 0x1000, CRC(4414dcdc) SHA1(00a3d293617dc691748ae85b6ccdd6723daefc0a))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY           FULLNAME  FLAGS
COMP( 1985, tricep, 0,      0,      tricep,  tricep, tricep_state, empty_init, "Morrow Designs", "Tricep", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )




triomphe.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Chess King Triomphe

The ROM includes (C)1985ICSL, that's Intelligent Chess Software Ltd. For some
reason, the programmer decided to (ab)use the HD6301 undefined opcode TRAP
interrupt for the beeper routine. Very strange.

Hardware notes:
- PCB label: TRIUMPHE CHESS KING
- Hitachi HD6301V1P, 4MHz XTAL
- 8*8 chessboard buttons, 32 LEDs, piezo

It's in the same housing as Chess King Master, they repurposed the green LED
for a power-on LED.

*******************************************************************************/

#include "emu.h"

#include "cpu/m6800/m6801.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "cking_triomphe.lh"


namespace {

class triomphe_state : public driver_device
{
public:
	triomphe_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void triomphe(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<hd6301v1_cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<2> m_inputs;

	u16 m_inp_mux = 0;

	// I/O handlers
	u8 input_r();
	void board_w(u8 data);
	void control_w(u8 data);
};

void triomphe_state::machine_start()
{
	save_item(NAME(m_inp_mux));
}



/*******************************************************************************
    I/O
*******************************************************************************/

u8 triomphe_state::input_r()
{
	// P10-P17: multiplexed inputs
	u8 data = 0;

	// read buttons
	for (int i = 0; i < 2; i++)
		if (BIT(m_inp_mux, i + 8))
			data |= m_inputs[i]->read();

	// read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_file(i);

	return ~data;
}

void triomphe_state::board_w(u8 data)
{
	// P30-P37: input mux (chessboard), led data
	m_inp_mux = (m_inp_mux & 0x300) | (data ^ 0xff);
	m_display->write_mx(~data);
}

void triomphe_state::control_w(u8 data)
{
	// P40,P41: input mux (buttons)
	m_inp_mux = (m_inp_mux & 0xff) | (~data << 8 & 0x300);

	// P44,P45: led select
	m_display->write_my(~data >> 4 & 3);

	// P47: speaker out
	m_dac->write(BIT(~data, 7));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( triomphe )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("King")
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Sound")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Move")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0xe0, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void triomphe_state::triomphe(machine_config &config)
{
	// basic machine hardware
	HD6301V1(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->in_p1_cb().set(FUNC(triomphe_state::input_r));
	m_maincpu->out_p3_cb().set(FUNC(triomphe_state::board_w));
	m_maincpu->in_p4_cb().set_constant(0); // freq sel
	m_maincpu->out_p4_cb().set(FUNC(triomphe_state::control_w));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(2, 8);
	config.set_default_layout(layout_cking_triomphe);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( triomphe )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("chessking_triomphe_m1_hd6301v1e53p", 0x0000, 0x1000, CRC(01f30f08) SHA1(5d8949b8e0a5d15024bb2c13ee6f3eb2ed02f94b) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1985, triomphe, 0,      0,      triomphe, triomphe, triomphe_state, empty_init, "Chess King / Intelligent Chess Software", "Triomphe", MACHINE_SUPPORTS_SAVE )



triplex.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/************************************************************************************************************

    Torch Triple X

    Known components:
    - 68010 CPU
    - 68451 MMU
    - 68450 DMA
    - 6845 CRTC
    - 6303 MPU
    - 6840 Timer
    - HD146818 RTC
    - 6850 UART
    - NCR5380 SCSI
    - AM7990 LANCE 20MHz
    - 8530 SCC
    - 20MHz XTAL
    - 16MHz XTAL
    - 4.9152MHz XTAL

    TODO:
    - emulate the OMTI 5200 controller board, with floppy.
    - dump keyboard MCU and implement full keyboard, with mouse.

************************************************************************************************************/

#include "emu.h"

#include "bus/bbc/1mhzbus/1mhzbus.h"
#include "bus/nscsi/devices.h"
#include "bus/rs232/rs232.h"
#include "bus/rs232/keyboard.h"
#include "cpu/m68000/m68010.h"
#include "cpu/m6800/m6801.h"
#include "machine/6840ptm.h"
#include "machine/6850acia.h"
#include "machine/am79c90.h"
#include "machine/hd63450.h"
#include "machine/input_merger.h"
#include "machine/mc146818.h"
#include "machine/ncr5380.h"
#include "machine/z80scc.h"
#include "sound/spkrdev.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class triplex_state : public driver_device
{
public:
	triplex_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_dmac(*this, "dmac")
		, m_mpu(*this, "mpu")
		, m_crtc(*this, "crtc")
		, m_kbd(*this, "kbd")
		, m_speaker(*this, "speaker")
		, m_vram(*this, "vram", 0x10000, ENDIANNESS_LITTLE)
		, m_vram_bank(*this, "vram_bank")
		, m_palette(*this, "palette")
		, m_ncr5380(*this, "scsi:7:ncr5380")
		, m_serial_c(*this, "serial_c")
	{
	}

	void triplex(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<m68010_device> m_maincpu;
	required_device<hd63450_device> m_dmac;
	required_device<hd6303r_cpu_device> m_mpu;
	required_device<mc6845_device> m_crtc;
	required_device<rs232_port_device> m_kbd;
	required_device<speaker_sound_device> m_speaker;
	memory_share_creator<uint8_t> m_vram;
	required_memory_bank m_vram_bank;
	required_device<palette_device> m_palette;
	required_device<ncr5380_device> m_ncr5380;
	required_device<rs232_port_device> m_serial_c;

	void mem_map(address_map &map) ATTR_COLD;
	void mpu_map(address_map &map) ATTR_COLD;

	u16 buserror_r(offs_t offset);
	void buserror_w(offs_t offset, u16 data);

	u8 mpu_p1_r();
	void mpu_p1_w(u8 data);
	u8 mpu_p2_r();
	void mpu_p2_w(u8 data);

	MC6845_UPDATE_ROW(crtc_update_row);
	MC6845_ON_UPDATE_ADDR_CHANGED(crtc_update_addr);

	u8 m_vmode = 0;
	int m_keyclk = 0;
};


void triplex_state::machine_start()
{
	m_vram_bank->configure_entries(0, 2, m_vram, 0x8000);

	save_item(NAME(m_vmode));
}

void triplex_state::machine_reset()
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);

	m_vmode = 0;
}


void triplex_state::mpu_map(address_map &map)
{
	map(0x0100, 0x0107).rw("ptm", FUNC(ptm6840_device::read), FUNC(ptm6840_device::write));
	map(0x0200, 0x0201).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0x0300, 0x033f).rw("rtc", FUNC(mc146818_device::read_direct), FUNC(mc146818_device::write_direct));
	map(0x0400, 0x0400).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0x0401, 0x0401).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x0500, 0x050f).w("palette", FUNC(palette_device::write8)).share("palette");
	map(0x0600, 0x06ff).rw("xbus", FUNC(bbc_1mhzbus_slot_device::fred_r), FUNC(bbc_1mhzbus_slot_device::fred_w));
	map(0x0700, 0x07ff).rw("xbus", FUNC(bbc_1mhzbus_slot_device::jim_r), FUNC(bbc_1mhzbus_slot_device::jim_w));
	map(0x4000, 0x0bfff).bankrw("vram_bank");
	map(0xc000, 0xffff).rom().region("rom", 0);
}


void triplex_state::mem_map(address_map &map)
{
	map(0x000000, 0x00ffff).lrw8(NAME([this](offs_t offset) { return m_vram[offset]; }), NAME([this](offs_t offset, u8 data) { m_vram[offset] = data; }));
	map(0x040000, 0x040007).rw("scc", FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w)).umask16(0x00ff);
	//map(0x060000, 0x060000) // IACK
	map(0x080000, 0x08003f).rw("dmac", FUNC(hd63450_device::read), FUNC(hd63450_device::write));
	//map(0x0a0000, 0x0a00ff).rw("mmu", FUNC(mc68451_device::read), FUNC(mc68451_device::write));
	map(0x0c0000, 0x0c0003).rw("lance", FUNC(am7990_device::regs_r), FUNC(am7990_device::regs_w));
	map(0x0e0000, 0x0e000f).rw(m_ncr5380, FUNC(ncr5380_device::read), FUNC(ncr5380_device::write)).umask16(0xff00);
	map(0x100000, 0x1fffff).ram(); // main memory
	map(0x200000, 0x2fffff).ram(); // limpet board (VMEbus)
	map(0x300000, 0x7fffff).rw(FUNC(triplex_state::buserror_r), FUNC(triplex_state::buserror_w));
}


u16 triplex_state::buserror_r(offs_t offset)
{
	if(!machine().side_effects_disabled())
	{
		m_maincpu->set_buserror_details(0x100000 + offset*2, true, m_maincpu->get_fc());
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
	}
	return 0xffff;
}

void triplex_state::buserror_w(offs_t offset, u16 data)
{
	if(!machine().side_effects_disabled())
	{
		m_maincpu->set_buserror_details(0x100000 + offset*2, false, m_maincpu->get_fc());
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
	}
}


u8 triplex_state::mpu_p1_r()
{
	// P10 FAIL
	return 0x00;
}

void triplex_state::mpu_p1_w(u8 data)
{
	//logerror("%s mpu_p1_w: %02x\n", machine().describe_context(), data);

	// P11 POWERUP

	// P12 VIDSEL
	m_vram_bank->set_entry(BIT(data, 2));

	// P13 PRESET
	m_maincpu->set_input_line(INPUT_LINE_HALT, BIT(data, 3));
	m_maincpu->set_input_line(INPUT_LINE_RESET, BIT(data, 3));

	// P14 PROCINT
	//m_dmac->pcl0_w(BIT(data, 4));

	// P15/6 VMODE
	m_vmode = BIT(data, 5, 2);

	// P17 SSDTR
	m_serial_c->write_dtr(BIT(data, 7));
}


u8 triplex_state::mpu_p2_r()
{
	u8 data = 0x00;

	// P20 VSYNC
	data |= m_crtc->vsync_r() << 0;

	// P22 KEYCLK
	data |= m_keyclk << 2;

	// P23 KEYRX
	data |= m_kbd->rxd_r() << 3;

	//logerror("%s mpu_p2_r: %02x\n", machine().describe_context(), data);
	return data;
}

void triplex_state::mpu_p2_w(u8 data)
{
	//logerror("%s mpu_p2_w: %02x\n", machine().describe_context(), data);

	// P21 AUDIO
	m_speaker->level_w(BIT(data, 1));

	// P24 KEYTX
	m_kbd->write_txd(BIT(data, 4));
}


MC6845_UPDATE_ROW(triplex_state::crtc_update_row)
{
	// Mode   Horizontal   Vertical   Bits/pixel   Colours
	//  0        640         256          2           4
	//  1        320         256          4          16
	//  2        640         512          1           2
	//  3        320         512          2           4

	u32 *p = &bitmap.pix(y);
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();

	u16 offset = (y * 0x100) + 2;

	for (int x_pos = 0; x_pos < (x_count*2); x_pos++)
	{
		u8 data = m_vram[offset + x_pos];

		switch (m_vmode)
		{
		case 0: case 3:
			*p++ = palette[BIT(data, 6, 2)];
			*p++ = palette[BIT(data, 4, 2)];
			*p++ = palette[BIT(data, 2, 2)];
			*p++ = palette[BIT(data, 0, 2)];
			break;
		case 1:
			*p++ = palette[BIT(data, 4, 4)];
			*p++ = palette[BIT(data, 0, 4)];
			break;
		case 2:
			*p++ = palette[BIT(data, 7, 1)];
			*p++ = palette[BIT(data, 6, 1)];
			*p++ = palette[BIT(data, 5, 1)];
			*p++ = palette[BIT(data, 4, 1)];
			*p++ = palette[BIT(data, 3, 1)];
			*p++ = palette[BIT(data, 2, 1)];
			*p++ = palette[BIT(data, 1, 1)];
			*p++ = palette[BIT(data, 0, 1)];
			break;
		}
	}
}


MC6845_ON_UPDATE_ADDR_CHANGED(triplex_state::crtc_update_addr)
{
	//logerror("crtc_update_addr: address %04x strobe %d\n", address, strobe);
}


void triplex_keyboard_devices(device_slot_interface &device)
{
	device.option_add("keyboard", SERIAL_KEYBOARD);
}

static DEVICE_INPUT_DEFAULTS_START(keyboard)
	DEVICE_INPUT_DEFAULTS("RS232_TXBAUD",   0xff, RS232_BAUD_1200)
	DEVICE_INPUT_DEFAULTS("RS232_DATABITS", 0xff, RS232_DATABITS_8)
	DEVICE_INPUT_DEFAULTS("RS232_PARITY",   0xff, RS232_PARITY_NONE)
	DEVICE_INPUT_DEFAULTS("RS232_STOPBITS", 0xff, RS232_STOPBITS_1)
DEVICE_INPUT_DEFAULTS_END


void triplex_state::triplex(machine_config &config)
{
	M68010(config, m_maincpu, 16_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &triplex_state::mem_map);

	HD63450(config, m_dmac, 16_MHz_XTAL / 2, m_maincpu); // HD68450
	m_dmac->irq_callback().set_inputline(m_maincpu, M68K_IRQ_3);
	m_dmac->dma_read<0>().set(m_ncr5380, FUNC(ncr5380_device::dma_r));
	m_dmac->dma_write<0>().set(m_ncr5380, FUNC(ncr5380_device::dma_w));

	// TODO: MC68451 MMU

	am7990_device &lance(AM7990(config, "lance", 20_MHz_XTAL));
	lance.intr_out().set_inputline(m_maincpu, M68K_IRQ_2);
	lance.dma_in().set([this](offs_t offset) { return m_maincpu->space(AS_PROGRAM).read_word(offset); });
	lance.dma_out().set([this](offs_t offset, u16 data, u16 mem_mask) { m_maincpu->space(AS_PROGRAM).write_word(offset, data, mem_mask); });

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", default_scsi_devices, "harddisk"); // OMTI 5200 (with floppy controller)
	NSCSI_CONNECTOR(config, "scsi:1", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", default_scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr5380", NCR5380).machine_config([this](device_t *device) {
		downcast<ncr5380_device&>(*device).irq_handler().set_inputline(m_maincpu, M68K_IRQ_4);
		downcast<ncr5380_device&>(*device).drq_handler().set(":dmac", FUNC(hd63450_device::drq0_w));
	});

	scc8530_device &scc(SCC8530(config, "scc", 4.9152_MHz_XTAL));
	scc.out_int_callback().set_inputline(m_maincpu, M68K_IRQ_5);
	scc.out_wreqa_callback().set("dmac", FUNC(hd63450_device::drq1_w));
	scc.out_dtra_callback().set("dmac", FUNC(hd63450_device::drq2_w));
	scc.out_wreqb_callback().set("dmac", FUNC(hd63450_device::drq3_w));
	scc.out_txda_callback().set("serial_a", FUNC(rs232_port_device::write_txd));
	scc.out_rtsa_callback().set("serial_a", FUNC(rs232_port_device::write_rts));
	scc.out_txdb_callback().set("serial_b", FUNC(rs232_port_device::write_txd));
	scc.out_rtsb_callback().set("serial_b", FUNC(rs232_port_device::write_rts));
	scc.out_dtrb_callback().set("serial_b", FUNC(rs232_port_device::write_dtr));

	rs232_port_device &serial_a(RS232_PORT(config, "serial_a", default_rs232_devices, nullptr)); // X25
	serial_a.rxd_handler().set("scc", FUNC(scc85230_device::rxa_w));
	serial_a.cts_handler().set("scc", FUNC(scc85230_device::ctsa_w));
	serial_a.dcd_handler().set("scc", FUNC(scc85230_device::dcda_w));
	serial_a.rxc_handler().set("scc", FUNC(scc85230_device::rxca_w));

	rs232_port_device &serial_b(RS232_PORT(config, "serial_b", default_rs232_devices, nullptr)); // Terminal or modem
	serial_b.rxd_handler().set("scc", FUNC(scc85230_device::rxb_w));
	serial_b.cts_handler().set("scc", FUNC(scc85230_device::ctsb_w));
	serial_b.dcd_handler().set("scc", FUNC(scc85230_device::dcdb_w));

	// Service Processor
	HD6303R(config, m_mpu, 16_MHz_XTAL / 4);
	m_mpu->set_addrmap(AS_PROGRAM, &triplex_state::mpu_map);
	m_mpu->in_p1_cb().set(FUNC(triplex_state::mpu_p1_r));
	m_mpu->out_p1_cb().set(FUNC(triplex_state::mpu_p1_w));
	m_mpu->in_p2_cb().set(FUNC(triplex_state::mpu_p2_r));
	m_mpu->out_p2_cb().set(FUNC(triplex_state::mpu_p2_w));
	m_mpu->out_ser_tx_cb().set("kbd", FUNC(rs232_port_device::write_txd));

	INPUT_MERGER_ANY_HIGH(config, "irqs").output_handler().set_inputline(m_mpu, M6801_IRQ1_LINE);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.5);

	RS232_PORT(config, m_kbd, triplex_keyboard_devices, "keyboard");
	m_kbd->set_option_device_input_defaults("keyboard", DEVICE_INPUT_DEFAULTS_NAME(keyboard));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(16_MHz_XTAL, 1024, 0, 720, 312, 0, 256);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	SY6845E(config, m_crtc, 16_MHz_XTAL / 8);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(triplex_state::crtc_update_row));
	m_crtc->set_on_update_addr_change_callback(FUNC(triplex_state::crtc_update_addr));
	m_crtc->out_vsync_callback().set_inputline(m_mpu, M6801_TIN_LINE);
	m_crtc->out_vsync_callback().append_inputline(m_maincpu, M68K_IRQ_6);

	PALETTE(config, "palette").set_format(palette_device::RGB_332_inverted, 16);

	mc146818_device &rtc(MC146818(config, "rtc", 32.768_kHz_XTAL));
	rtc.set_24hrs(true);
	rtc.set_binary(true);
	rtc.irq().set("irqs", FUNC(input_merger_device::in_w<0>));

	ptm6840_device &ptm(PTM6840(config, "ptm", 16_MHz_XTAL / 16));
	ptm.set_external_clocks(4.9152_MHz_XTAL/16, 4.9152_MHz_XTAL/16, 4.9152_MHz_XTAL/16);
	ptm.o1_callback().set("acia", FUNC(acia6850_device::write_rxc));
	ptm.o2_callback().set("acia", FUNC(acia6850_device::write_txc));
	ptm.o3_callback().set([this](int state) { m_keyclk = state; if (state) m_mpu->clock_serial(); }); // KEYCLK
	ptm.irq_callback().set("irqs", FUNC(input_merger_device::in_w<1>));

	acia6850_device &acia(ACIA6850(config, "acia", 0));
	acia.txd_handler().set(m_serial_c, FUNC(rs232_port_device::write_txd));
	acia.rts_handler().set(m_serial_c, FUNC(rs232_port_device::write_rts));
	acia.irq_handler().set("irqs", FUNC(input_merger_device::in_w<2>));

	RS232_PORT(config, m_serial_c, default_rs232_devices, "printer"); // Printer
	m_serial_c->rxd_handler().set("acia", FUNC(acia6850_device::write_rxd));
	m_serial_c->dcd_handler().set("acia", FUNC(acia6850_device::write_dcd));
	m_serial_c->cts_handler().set("acia", FUNC(acia6850_device::write_cts));

	BBC_1MHZBUS_SLOT(config, "xbus", 16_MHz_XTAL / 16, bbcm_1mhzbus_devices, nullptr); // TODO: modem board

	//SOFTWARE_LIST(config, "flop_list").set_original("triplex_flop");
}


ROM_START(triplex)
	ROM_REGION(0x4000, "rom", 0)
	ROM_SYSTEM_BIOS(0, "13", "Caretaker 1.3")
	ROMX_LOAD("caretaker_iss1.3.bin", 0x0000, 0x4000, CRC(a3031aa1) SHA1(4d0f9ca854bbf236890bb1c342977a493df1e3d4), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "12", "Caretaker 1.2")
	ROMX_LOAD("caretaker_iss1.2.bin", 0x0000, 0x4000, CRC(3b1aa08b) SHA1(92bd672d0e13921cd51a9254031270cd0bb34d73), ROM_BIOS(1))

	//DISK_REGION("scsi:0:harddisk")
	//DISK_IMAGE("torch_system5", 0, NO_DUMP) // HDD images at https://bitsavers.org/bits/Torch/, not verified until machine working.
ROM_END

} // anonymous namespace


//   YEAR  NAME      PARENT  COMPAT  MACHINE    INPUT     CLASS           INIT         COMPANY             FULLNAME     FLAGS
COMP(1985, triplex,  0,      0,      triplex,   0,        triplex_state,  empty_init,  "Torch Computers",  "Triple X",  MACHINE_NOT_WORKING)



triton.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/*******************************************************************************

    Transam Triton

    TODO:
    - cassette interface
    - keyboard auto repeat (optional)
    - parallel printer on port 3, uses INT3 (optional)

*******************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/ay31015.h"
#include "machine/clock.h"
#include "machine/kr2376.h"
#include "machine/timer.h"
#include "sound/beep.h"
#include "video/ef9364.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "bus/rs232/rs232.h"
#include "imagedev/cassette.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "triton.lh"


namespace {

class triton_state : public driver_device
{
public:
	triton_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ef9364(*this, "ef9364")
		, m_charset(*this, "ef9364")
		, m_kr2376(*this, "kr2376")
		, m_uart(*this, "uart")
		, m_cassette(*this, "cassette")
		, m_beeper(*this, "beeper")
		, m_led(*this, "led%u", 0U)
		, m_rom(*this, "rom")
		, m_vidcon(*this, "vidcon")
		, m_chargen(*this, "chargen")
		, m_graphics(*this, "graphics")
		, m_palette(*this, "palette")
		, m_user1(*this, "user1")
		, m_user2(*this, "user2")
		, m_serial(*this, "serial")
		, m_modifiers(*this, "MODIFIERS")
		, m_config(*this, "CONFIG")
	{ }

	void triton1(machine_config &config);
	void triton2(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(pushbutton_changed);
	DECLARE_INPUT_CHANGED_MEMBER(charset_changed);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<ef9364_device> m_ef9364;
	required_region_ptr<uint8_t> m_charset;
	required_device<kr2376_device> m_kr2376;
	required_device<ay31015_device> m_uart;
	required_device<cassette_image_device> m_cassette;
	required_device<beep_device> m_beeper;
	output_finder<8> m_led;
	required_region_ptr<uint8_t> m_rom;
	required_region_ptr<uint8_t> m_vidcon;
	required_region_ptr<uint8_t> m_chargen;
	required_region_ptr<uint8_t> m_graphics;
	required_device<palette_device> m_palette;
	required_device<generic_slot_device> m_user1;
	required_device<generic_slot_device> m_user2;
	required_device<rs232_port_device> m_serial;
	required_ioport m_modifiers;
	required_ioport m_config;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	IRQ_CALLBACK_MEMBER(inta_cb);

	void update_charset();

	uint8_t rom_r(offs_t offset);

	uint8_t port0_r();
	uint8_t port1_r();
	void port3_w(uint8_t data);
	void port5_w(uint8_t data);
	void port6_w(uint8_t data);
	void port7_w(uint8_t data);

	uint16_t m_int_vector;
	std::unique_ptr<uint8_t[]> m_exp_ram;
};


void triton_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).r(FUNC(triton_state::rom_r));
	map(0x1000, 0x13ff).rw(m_ef9364, FUNC(ef9364_device::videoram_r), FUNC(ef9364_device::videoram_w));
	map(0x1400, 0x1fff).ram();
	map(0xc000, 0xdfff).rom().region("eprom_6", 0); // 8K Eprom card
	map(0xe000, 0xffff).rom().region("eprom_7", 0); // 8K Eprom card
}

void triton_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x00).r(FUNC(triton_state::port0_r));            // Keyboard INPUT
	map(0x01, 0x01).r(FUNC(triton_state::port1_r));            // Tape I/O UART status INPUT
	map(0x02, 0x02).w(m_uart, FUNC(ay51013_device::transmit)); // Tape I/O UART data strobe OUTPUT
	map(0x03, 0x03).w(FUNC(triton_state::port3_w));            // LEDs OUTPUT (note LEDs are on for "0")
	map(0x04, 0x04).r(m_uart, FUNC(ay51013_device::receive));  // Tape I/O UART receive data enable INPUT
	map(0x05, 0x05).w(FUNC(triton_state::port5_w));            // VDU OUTPUT (note strobe - bit 8 - has to be specially formatted by software)
	map(0x06, 0x06).w(FUNC(triton_state::port6_w));            // Serial OUTPUT on bit 8, bit 7 spare
	map(0x07, 0x07).w(FUNC(triton_state::port7_w));            // Bit 8 = Relay, Bit 7 = Oscillator (speaker)
}


static INPUT_PORTS_START(triton)
	// GRI.756 Keyboard (Encoded by KR2376-12)
	PORT_START("X0") // 3     2     1     RS    SUB   GS    SYN   STX   NAK   DC4   NUL
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3_PAD)      PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2_PAD)      PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1_PAD)      PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("X1") // 6     5     4     ]     [     ETX   CAN   GS    DEL   DLE   SOH
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6_PAD)      PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5_PAD)      PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4_PAD)      PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[')  PORT_CHAR('{')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')  PORT_CHAR('}')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("X2") // 9     8     7     |     ~     FS    EM    ESC   SUB   HT    ACK
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9_PAD)      PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8_PAD)      PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7_PAD)      PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('|')  PORT_CHAR('\\') PORT_NAME(u8"¦ \\ FS")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('~')  PORT_CHAR('^')  PORT_NAME(u8"~ \u2191 RS") // up arrow ↑
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC))   PORT_NAME("Esc")
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("X3") // US    VT    DC1   BS    SI    HT    LF    FF    CR    SP    ETB
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(8)                    PORT_NAME("Del")
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)                   PORT_NAME("Return")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("X4") // -     0     9     8     7     6     5     4     3     2     1
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-')  PORT_CHAR('=')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0)          PORT_CHAR('0')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9)          PORT_CHAR('9')  PORT_CHAR(')')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8)          PORT_CHAR('8')  PORT_CHAR('(')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7)          PORT_CHAR('7')  PORT_CHAR('\'')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6)          PORT_CHAR('6')  PORT_CHAR('&')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5)          PORT_CHAR('5')  PORT_CHAR('%')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)          PORT_CHAR('4')  PORT_CHAR('$')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)          PORT_CHAR('3')  PORT_CHAR('#')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2)          PORT_CHAR('2')  PORT_CHAR('"')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1)          PORT_CHAR('1')  PORT_CHAR('!')

	PORT_START("X5") // \     p     o     i     u     y     t     r     e     w     q
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR('\\') PORT_CHAR('@')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)          PORT_CHAR('P')  PORT_CHAR('p')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)          PORT_CHAR('O')  PORT_CHAR('o')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)          PORT_CHAR('I')  PORT_CHAR('i')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)          PORT_CHAR('U')  PORT_CHAR('u')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)          PORT_CHAR('Y')  PORT_CHAR('y')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)          PORT_CHAR('T')  PORT_CHAR('t')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)          PORT_CHAR('R')  PORT_CHAR('r')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)          PORT_CHAR('E')  PORT_CHAR('e')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)          PORT_CHAR('W')  PORT_CHAR('w')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)          PORT_CHAR('Q')  PORT_CHAR('q')

	PORT_START("X6") // :     ;     l     k     j     h     g     f     d     s     a
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(':')  PORT_CHAR('*')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR(';')  PORT_CHAR('+')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)          PORT_CHAR('L')  PORT_CHAR('l')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)          PORT_CHAR('K')  PORT_CHAR('k')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)          PORT_CHAR('J')  PORT_CHAR('j')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)          PORT_CHAR('H')  PORT_CHAR('h')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)          PORT_CHAR('G')  PORT_CHAR('g')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)          PORT_CHAR('F')  PORT_CHAR('f')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)          PORT_CHAR('D')  PORT_CHAR('d')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)          PORT_CHAR('S')  PORT_CHAR('s')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)          PORT_CHAR('A')  PORT_CHAR('a')

	PORT_START("X7") // _     /     .     ,     m     n     b     v     c     x     z
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('_')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/')  PORT_CHAR('?')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')  PORT_CHAR('>')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')  PORT_CHAR('<')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)          PORT_CHAR('M')  PORT_CHAR('m')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)          PORT_CHAR('N')  PORT_CHAR('n')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)          PORT_CHAR('B')  PORT_CHAR('b')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)          PORT_CHAR('V')  PORT_CHAR('v')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)          PORT_CHAR('C')  PORT_CHAR('c')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)          PORT_CHAR('X')  PORT_CHAR('x')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)          PORT_CHAR('Z')  PORT_CHAR('z')

	PORT_START("MODIFIERS")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Shift Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_TOGGLE

	PORT_START("PUSHBUTTONS")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CODE(KEYCODE_F1) PORT_NAME("Reset")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(triton_state::pushbutton_changed), 0)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CODE(KEYCODE_F2) PORT_NAME("Clear Screen") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(triton_state::pushbutton_changed), 0xcf) // INT1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CODE(KEYCODE_F3) PORT_NAME("Initialise")   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(triton_state::pushbutton_changed), 0xd7) // INT2
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_CODE(KEYCODE_F4) PORT_NAME("Menu")         PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(triton_state::pushbutton_changed), 0xdf) // INT3
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_CODE(KEYCODE_F5) PORT_NAME("Pause")        PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(triton_state::pushbutton_changed), 1) PORT_TOGGLE

	PORT_START("CONFIG")
	PORT_CONFNAME(0x07, 0x00, "8K RAM Card")
	PORT_CONFSETTING(0x00, "0x8K No RAM Card")
	PORT_CONFSETTING(0x01, "1x8K 2000-3FFF")
	PORT_CONFSETTING(0x02, "2x8K 2000-5FFF")
	PORT_CONFSETTING(0x03, "3x8K 2000-7FFF")
	PORT_CONFSETTING(0x04, "4x8K 2000-9FFF")
	PORT_CONFSETTING(0x05, "5x8K 2000-BFFF")
	PORT_CONFNAME(0x10, 0x00, "Graphics PROM") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(triton_state::charset_changed), 0)
	PORT_CONFSETTING(0x00, "Graphics")
	PORT_CONFSETTING(0x10, "Lower Case")
	//PORT_CONFNAME(0x20, 0x00, "Auto repeat")
	//PORT_CONFSETTING(0x00, DEF_STR( No ))
	//PORT_CONFSETTING(0x20, DEF_STR( Yes ))
	//PORT_CONFNAME(0x40, 0x00, "Port 3")
	//PORT_CONFSETTING(0x00, "LED's")
	//PORT_CONFSETTING(0x40, "Parallel Printer")
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(triton_state::pushbutton_changed)
{
	switch (param)
	{
	case 0:
		m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
		break;
	case 1:
		m_maincpu->set_input_line(INPUT_LINE_HALT, newval ? ASSERT_LINE : CLEAR_LINE);
		break;
	default:
		m_int_vector = param;
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, newval ? ASSERT_LINE : CLEAR_LINE);
		break;
	}
}

INPUT_CHANGED_MEMBER(triton_state::charset_changed)
{
	update_charset();
}


void triton_state::update_charset()
{
	// copy selected charset to ef9364 region
	memcpy(&m_charset[0x0000], &m_graphics[BIT(m_config->read(), 4) * 0x200], 0x200);
	memcpy(&m_charset[0x0200], &m_graphics[BIT(m_config->read(), 4) * 0x200], 0x200);
	memcpy(&m_charset[0x0100], &m_chargen[0x100], 0x100);
	memcpy(&m_charset[0x0200], &m_chargen[0x000], 0x100);
}


uint8_t triton_state::rom_r(offs_t offset)
{
	uint8_t data = m_rom[offset];

	switch (offset & 0x0c00)
	{
	case 0x0400: // User1
		if (m_user1->exists())
		{
			data = m_user1->read_rom(offset & 0x3ff);
		}
		break;
	case 0x0800: // User2
		if (m_user2->exists())
		{
			data = m_user2->read_rom(offset & 0x3ff);
		}
		break;
	}

	return data;
}


uint8_t triton_state::port0_r()
{
	// TODO: strobe goes low on a timer (capacitance discharge) for auto repeat
	return m_kr2376->data_r() | (m_kr2376->get_output_pin(kr2376_device::KR2376_SO) << 7);
}

uint8_t triton_state::port1_r()
{
	uint8_t data = 0x00;

	data |= m_uart->dav_r() << 0;
	data |= m_uart->pe_r() << 1;
	data |= m_uart->fe_r() << 2;
	data |= m_uart->or_r() << 3;
	data |= m_uart->tbmt_r() << 4;

	return data;
}

void triton_state::port3_w(uint8_t data)
{
	for (int i = 0; i < 8; i++)
		m_led[i] = BIT(data, i);
}

void triton_state::port5_w(uint8_t data)
{
	if (data & 0x80)
		m_ef9364->command_w(m_vidcon[data & 0x7f]);
	else
		m_ef9364->char_latch_w(data & 0x7f);
}

void triton_state::port6_w(uint8_t data)
{
	m_serial->write_txd(!BIT(data, 7));
}

void triton_state::port7_w(uint8_t data)
{
	m_beeper->set_state(BIT(data, 6));

	m_cassette->change_state(BIT(data, 7) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
}


IRQ_CALLBACK_MEMBER(triton_state::inta_cb)
{
	return m_int_vector;
}


void triton_state::machine_start()
{
	for (int i = 0; i < 0x400; i++)
	{
		m_graphics[i] = bitswap<8>(m_graphics[i], 4, 3, 5, 2, 6, 1, 7, 0);
	}

	m_exp_ram = make_unique_clear<uint8_t[]>(0xe000);

	m_led.resolve();

	save_pointer(NAME(m_exp_ram), 0xe000);
}

void triton_state::machine_reset()
{
	uint16_t ramsize = (m_config->read() & 7) * 0x2000;

	m_maincpu->space(AS_PROGRAM).unmap_readwrite(0x2000, 0xbfff);

	if (ramsize)
	{
		m_maincpu->space(AS_PROGRAM).install_ram(0x2000, 0x2000 + ramsize - 1, m_exp_ram.get()); // 8K RAM cards
	}

	m_kr2376->set_input_pin(kr2376_device::KR2376_DSII, 0);
	m_kr2376->set_input_pin(kr2376_device::KR2376_PII, 0);

	update_charset();
}


static const gfx_layout charlayout =
{
	8, 12,                  /* 8 x 12 characters */
	64,                     /* 64 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7, 8 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 0*8, 0*8, 0*8, 0*8 },
	8*8                    /* every char takes 8 bytes */
};

static GFXDECODE_START(gfx_tritan)
	GFXDECODE_ENTRY("chargen", 0, charlayout, 0, 1)
	GFXDECODE_ENTRY("graphics", 0x000, charlayout, 0, 1)
	GFXDECODE_ENTRY("graphics", 0x200, charlayout, 0, 1)
GFXDECODE_END


static DEVICE_INPUT_DEFAULTS_START(printer)
	DEVICE_INPUT_DEFAULTS("RS232_RXBAUD", 0xff, RS232_BAUD_110)
	DEVICE_INPUT_DEFAULTS("RS232_DATABITS", 0xff, RS232_DATABITS_8)
	DEVICE_INPUT_DEFAULTS("RS232_PARITY", 0xff, RS232_PARITY_MARK)
	DEVICE_INPUT_DEFAULTS("RS232_STOPBITS", 0xff, RS232_STOPBITS_2)
DEVICE_INPUT_DEFAULTS_END


void triton_state::triton1(machine_config &config)
{
	I8080A(config, m_maincpu, 7.2_MHz_XTAL / 9);
	m_maincpu->set_addrmap(AS_PROGRAM, &triton_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &triton_state::io_map);
	m_maincpu->set_irq_acknowledge_callback(FUNC(triton_state::inta_cb));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_screen_update("ef9364", FUNC(ef9364_device::screen_update));
	screen.set_size(64 * 8, 16 * 12);
	screen.set_visarea(0, 64 * 8 - 1, 0, 16 * 12 - 1);
	GFXDECODE(config, "gfxdecode", "palette", gfx_tritan);
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	EF9364(config, m_ef9364, 1.008_MHz_XTAL); // SFF96364
	m_ef9364->set_screen("screen");
	m_ef9364->set_palette_tag("palette");
	m_ef9364->set_nb_of_pages(1);
	m_ef9364->set_erase(0x20);

	config.set_default_layout(layout_triton);

	KR2376_12(config, m_kr2376, 50000);
	m_kr2376->x<0>().set_ioport("X0");
	m_kr2376->x<1>().set_ioport("X1");
	m_kr2376->x<2>().set_ioport("X2");
	m_kr2376->x<3>().set_ioport("X3");
	m_kr2376->x<4>().set_ioport("X4");
	m_kr2376->x<5>().set_ioport("X5");
	m_kr2376->x<6>().set_ioport("X6");
	m_kr2376->x<7>().set_ioport("X7");
	m_kr2376->shift().set([this]() { return int(BIT(m_modifiers->read(), 0) || BIT(m_modifiers->read(), 2)); });
	m_kr2376->control().set([this]() { return int(BIT(m_modifiers->read(), 1)); });
	//m_kr2376->strobe().set([this](int state) { auto repeat timer });

	AY51013(config, m_uart);
	//m_uart->read_si_callback().set(FUNC(triton_state::si));
	//m_uart->write_so_callback().set(FUNC(triton_state::so));

	//CLOCK(config, m_uart_clock, 4800);
	//m_uart_clock->signal_handler().set(m_uart, FUNC(ay51013_device::write_rcp));
	//m_uart_clock->signal_handler().append(m_uart, FUNC(ay51013_device::write_tcp));

	//clock_device &uart_clock(CLOCK(config, "uart_clock", 4800));
	//uart_clock.signal_handler().set(FUNC(triton_state::kansas_w));
	//TIMER(config, "kansas_r").configure_periodic(FUNC(triton_state::kansas_r), attotime::from_hz(40000));

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 800).add_route(ALL_OUTPUTS, "mono", 1.00);

	GENERIC_SOCKET(config, m_user1, generic_plain_slot, "triton_rom", "rom,bin");
	GENERIC_SOCKET(config, m_user2, generic_plain_slot, "triton_rom", "rom,bin");

	RS232_PORT(config, m_serial, default_rs232_devices, "printer");
	m_serial->set_option_device_input_defaults("printer", DEVICE_INPUT_DEFAULTS_NAME(printer));

	SOFTWARE_LIST(config, "rom_ls").set_original("triton_rom");
}

void triton_state::triton2(machine_config &config)
{
	triton1(config);

	m_maincpu->set_clock(18_MHz_XTAL / 9);
}


ROM_START(triton41)
	ROM_REGION(0x1000, "rom", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "l41", "L4.1")
	ROMX_LOAD("monitor_4.1.rom", 0x0000, 0x0400, CRC(2228040d) SHA1(0e92fa1c0a1327dd77b6f9403a5f34aa8b464e9b), ROM_BIOS(0))
	ROMX_LOAD("basic_l4.1a.rom", 0x0400, 0x0400, CRC(468264b0) SHA1(bcd2705be359eb727f00ae54562afd75028ee34f), ROM_BIOS(0))
	ROMX_LOAD("basic_l4.1b.rom", 0x0800, 0x0400, CRC(858aa55c) SHA1(a49bf12a1f11d9c04ef30b4ca0278782e93bd95c), ROM_BIOS(0))

	ROM_REGION(0x2000, "eprom_6", ROMREGION_ERASEFF)
	ROM_REGION(0x2000, "eprom_7", ROMREGION_ERASEFF)

	ROM_REGION(0x0100, "vidcon", 0)
	ROM_LOAD("vducontrol.ic54", 0x0000, 0x0100, CRC(ad5d426e) SHA1(3db409fe7e3e9fc350acd5500594965c5f2bb5be)) // 74S287 (256 x 4bit)

	ROM_REGION(0x0200, "chargen", 0)
	ROM_LOAD("cgr-001.ic69",    0x0000, 0x0200, CRC(a7e567fc) SHA1(b18aae0a2d4f92f5a7e22640719bbc4652f3f4ee)) // RO-3-2513

	ROM_REGION(0x0400, "graphics", 0)
	ROM_LOAD("graphics.ic70",   0x0000, 0x0200, CRC(77364e43) SHA1(b6ec6543acacab544e8568f700a1b581025897a1)) // 74S472 (512 x 8bit)
	ROM_LOAD("lowercase.ic70",  0x0200, 0x0200, CRC(fd677f54) SHA1(7d5a907f97df1f7f7379df3eedbd36c5c071abd1)) // alternate lower case

	ROM_REGION(0x0400, "ef9364", ROMREGION_ERASE00)
ROM_END

ROM_START(triton51)
	ROM_REGION(0x1000, "rom", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "l51", "L5.1")
	ROMX_LOAD("humbug_5.1a.rom", 0x0000, 0x0400, CRC(502a7b4f) SHA1(b2e4dc177676c528c5f11518e0d0d4b40385be25), ROM_BIOS(0))
	ROMX_LOAD("basic_l5.1a.rom", 0x0400, 0x0400, CRC(08830f6f) SHA1(1467e35aa8e4d5c7e73a7b5bc0d1ef06ef1e0b4b), ROM_BIOS(0))
	ROMX_LOAD("basic_l5.1b.rom", 0x0800, 0x0400, CRC(4470590a) SHA1(a7aa5b900f33a4e7e973ccf4fc07cc7b02dbd336), ROM_BIOS(0))
	ROMX_LOAD("humbug_5.1b.rom", 0x0c00, 0x0400, CRC(6d47d54e) SHA1(c672ce87944a91f481c66202b7f1109348c3c6d1), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "l51p", "L5.1P") // supports parallel printer, implementation unknown.
	ROMX_LOAD("humbug_5.1p.rom", 0x0000, 0x0400, CRC(f68b10ac) SHA1(30b19012df1507781d2d8a070021dec50c1d8461), ROM_BIOS(1))
	ROMX_LOAD("basic_l5.1a.rom", 0x0400, 0x0400, CRC(08830f6f) SHA1(1467e35aa8e4d5c7e73a7b5bc0d1ef06ef1e0b4b), ROM_BIOS(1))
	ROMX_LOAD("basic_l5.1b.rom", 0x0800, 0x0400, CRC(4470590a) SHA1(a7aa5b900f33a4e7e973ccf4fc07cc7b02dbd336), ROM_BIOS(1))
	ROMX_LOAD("humbug_5.1b.rom", 0x0c00, 0x0400, CRC(6d47d54e) SHA1(c672ce87944a91f481c66202b7f1109348c3c6d1), ROM_BIOS(1))

	ROM_REGION(0x2000, "eprom_6", ROMREGION_ERASEFF)
	ROM_REGION(0x2000, "eprom_7", ROMREGION_ERASEFF)

	ROM_REGION(0x0100, "vidcon", 0)
	ROM_LOAD("vducontrol.ic54", 0x0000, 0x0100, CRC(ad5d426e) SHA1(3db409fe7e3e9fc350acd5500594965c5f2bb5be)) // 74S287 (256 x 4bit)

	ROM_REGION(0x0200, "chargen", 0)
	ROM_LOAD("cgr-001.ic69",    0x0000, 0x0200, CRC(a7e567fc) SHA1(b18aae0a2d4f92f5a7e22640719bbc4652f3f4ee)) // RO-3-2513

	ROM_REGION(0x0400, "graphics", 0)
	ROM_LOAD("graphics.ic70",   0x0000, 0x0200, CRC(77364e43) SHA1(b6ec6543acacab544e8568f700a1b581025897a1)) // 74S472 (512 x 8bit)
	ROM_LOAD("lowercase.ic70",  0x0200, 0x0200, CRC(fd677f54) SHA1(7d5a907f97df1f7f7379df3eedbd36c5c071abd1)) // alternate lower case

	ROM_REGION(0x400, "ef9364", ROMREGION_ERASE00)
ROM_END

ROM_START(triton52)
	ROM_REGION(0x1000, "rom", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "l52", "L5.2")
	ROMX_LOAD("humbug_5.2a.rom", 0x0000, 0x0400, CRC(9020f7ed) SHA1(ad0561c51b20684e49b180fb5d2535e2cf0e05e7), ROM_BIOS(0))
	ROMX_LOAD("basic_l5.1a.rom", 0x0400, 0x0400, CRC(08830f6f) SHA1(1467e35aa8e4d5c7e73a7b5bc0d1ef06ef1e0b4b), ROM_BIOS(0))
	ROMX_LOAD("basic_l5.1b.rom", 0x0800, 0x0400, CRC(4470590a) SHA1(a7aa5b900f33a4e7e973ccf4fc07cc7b02dbd336), ROM_BIOS(0))
	ROMX_LOAD("humbug_5.2b.rom", 0x0c00, 0x0400, CRC(47bc3961) SHA1(37f5c09e43f84227e6b4b76e02ba95721dcbf2f1), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "l52p", "L5.2P") // supports parallel printer, implementation unknown.
	ROMX_LOAD("humbug_5.2p.rom", 0x0000, 0x0400, CRC(eb2ded3d) SHA1(4e5b291f7a84deb629e351ec5166aadb6477d436), ROM_BIOS(1))
	ROMX_LOAD("basic_l5.1a.rom", 0x0400, 0x0400, CRC(08830f6f) SHA1(1467e35aa8e4d5c7e73a7b5bc0d1ef06ef1e0b4b), ROM_BIOS(1))
	ROMX_LOAD("basic_l5.1b.rom", 0x0800, 0x0400, CRC(4470590a) SHA1(a7aa5b900f33a4e7e973ccf4fc07cc7b02dbd336), ROM_BIOS(1))
	ROMX_LOAD("humbug_5.2b.rom", 0x0c00, 0x0400, CRC(47bc3961) SHA1(37f5c09e43f84227e6b4b76e02ba95721dcbf2f1), ROM_BIOS(1))

	ROM_REGION(0x2000, "eprom_6", ROMREGION_ERASEFF)
	ROM_LOAD("trap_2.1a.rom", 0x0000, 0x0400, CRC(115d1c80) SHA1(6b7a5b9f75432c012f1cc37ad3fe82d92adcd1e7))
	ROM_LOAD("trap_2.1b.rom", 0x0400, 0x0400, CRC(c8d4ba47) SHA1(e89b087630bf1deb4542a2ab6d697047430bd8a1))
	ROM_LOAD("trap_2.1c.rom", 0x0800, 0x0400, CRC(ad383c51) SHA1(95dac006c1e22bb9e820d9aee9b0ab0fdc745045))
	ROM_LOAD("trap_2.1d.rom", 0x0c00, 0x0400, CRC(cf0c1358) SHA1(9ad965f17589490753fb81e7781dc1bf4c82663d))
	ROM_LOAD("trap_2.1e.rom", 0x1000, 0x0400, CRC(211e5173) SHA1(e4cbd7b852ecd4fe46557efa0b3b2db24f83d5a1))
	ROM_LOAD("trap_2.1f.rom", 0x1400, 0x0400, CRC(5f45fe51) SHA1(4218328b697d52a5b85cf3c0280e0e097af3bf44))
	ROM_LOAD("trap_2.1g.rom", 0x1800, 0x0400, CRC(455e3212) SHA1(17e01b3a9b0a358cc483952d25a92823ced15698))
	ROM_LOAD("trap_2.1h.rom", 0x1c00, 0x0400, CRC(4fc2666d) SHA1(fdae9a762d10be5a69e0c5f889ffc86d370b2ae4))

	ROM_REGION(0x2000, "eprom_7", ROMREGION_ERASEFF)

	ROM_REGION(0x0100, "vidcon", 0)
	ROM_LOAD("vducontrol.ic54", 0x0000, 0x0100, CRC(ad5d426e) SHA1(3db409fe7e3e9fc350acd5500594965c5f2bb5be)) // 74S287 (256 x 4bit)

	ROM_REGION(0x0200, "chargen", 0)
	ROM_LOAD("cgr-001.ic69",    0x0000, 0x0200, CRC(a7e567fc) SHA1(b18aae0a2d4f92f5a7e22640719bbc4652f3f4ee)) // RO-3-2513

	ROM_REGION(0x0400, "graphics", 0)
	ROM_LOAD("graphics.ic70",   0x0000, 0x0200, CRC(77364e43) SHA1(b6ec6543acacab544e8568f700a1b581025897a1)) // 74S472 (512 x 8bit)
	ROM_LOAD("lowercase.ic70",  0x0200, 0x0200, CRC(fd677f54) SHA1(7d5a907f97df1f7f7379df3eedbd36c5c071abd1)) // alternate lower case

	ROM_REGION(0x400, "ef9364", ROMREGION_ERASE00)
ROM_END

ROM_START(triton72)
	ROM_REGION(0x1000, "rom", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS(0, "l72", "L7.2")
	ROMX_LOAD("monitor_7.2a.rom", 0x0000, 0x0400, CRC(0ea43528) SHA1(40fd2d435879dbb4931eec1c091c8c7531a1a96f), ROM_BIOS(0))
	ROMX_LOAD("monitor_7.2b.rom", 0x0c00, 0x0400, CRC(0e75d714) SHA1(bfc9c68dc8e86503c5ecacd454b633f95b407727), ROM_BIOS(0))

	ROM_REGION(0x2000, "eprom_6", ROMREGION_ERASEFF)
	ROM_LOAD("trap_2.1a.rom", 0x0000, 0x0400, CRC(115d1c80) SHA1(6b7a5b9f75432c012f1cc37ad3fe82d92adcd1e7))
	ROM_LOAD("trap_2.1b.rom", 0x0400, 0x0400, CRC(c8d4ba47) SHA1(e89b087630bf1deb4542a2ab6d697047430bd8a1))
	ROM_LOAD("trap_2.1c.rom", 0x0800, 0x0400, CRC(ad383c51) SHA1(95dac006c1e22bb9e820d9aee9b0ab0fdc745045))
	ROM_LOAD("trap_2.1d.rom", 0x0c00, 0x0400, CRC(cf0c1358) SHA1(9ad965f17589490753fb81e7781dc1bf4c82663d))
	ROM_LOAD("trap_2.1e.rom", 0x1000, 0x0400, CRC(211e5173) SHA1(e4cbd7b852ecd4fe46557efa0b3b2db24f83d5a1))
	ROM_LOAD("trap_2.1f.rom", 0x1400, 0x0400, CRC(5f45fe51) SHA1(4218328b697d52a5b85cf3c0280e0e097af3bf44))
	ROM_LOAD("trap_2.1g.rom", 0x1800, 0x0400, CRC(455e3212) SHA1(17e01b3a9b0a358cc483952d25a92823ced15698))
	ROM_LOAD("trap_2.1h.rom", 0x1c00, 0x0400, CRC(4fc2666d) SHA1(fdae9a762d10be5a69e0c5f889ffc86d370b2ae4))

	ROM_REGION(0x2000, "eprom_7", ROMREGION_ERASEFF)
	ROM_LOAD("basic_l7.2a.rom", 0x0000, 0x0400, CRC(d0713894) SHA1(f56ad2af91f25b86737f70c25e08d3aa358eb009))
	ROM_LOAD("basic_l7.2b.rom", 0x0400, 0x0400, CRC(7604032f) SHA1(103cbf0a9f8706cf6a8e0e759bab587636a4fff6))
	ROM_LOAD("basic_l7.2c.rom", 0x0800, 0x0400, CRC(1255fb1e) SHA1(6cb76813a3604c4fcc689fdf861b9e1de361f3fa))
	ROM_LOAD("basic_l7.2d.rom", 0x0c00, 0x0400, CRC(df2db6a1) SHA1(da0b185707fe30596b25a560709a1eeb535070ed))
	ROM_LOAD("basic_l7.2e.rom", 0x1000, 0x0400, CRC(9fc0a643) SHA1(5d1758a4cf6741e1e5ef6f655f21a4704ac72125))
	ROM_LOAD("basic_l7.2f.rom", 0x1400, 0x0400, CRC(002723ad) SHA1(146ff347d893d4aba4148fc2ce88bbe08abab479))
	ROM_LOAD("basic_l7.2g.rom", 0x1800, 0x0400, CRC(b43d50de) SHA1(dde0d26713b3a346c8bbcff9c40e2c1d448fb8d5))
	ROM_LOAD("basic_l7.2h.rom", 0x1c00, 0x0400, CRC(2af7f180) SHA1(7f18375147eb18f90b15606cd9b1578fd3fb3e76))

	ROM_REGION(0x0100, "vidcon", 0)
	ROM_LOAD("vducontrol.ic54", 0x0000, 0x0100, CRC(ad5d426e) SHA1(3db409fe7e3e9fc350acd5500594965c5f2bb5be)) // 74S287 (256 x 4bit)

	ROM_REGION(0x0200, "chargen", 0)
	ROM_LOAD("cgr-001.ic69",    0x0000, 0x0200, CRC(a7e567fc) SHA1(b18aae0a2d4f92f5a7e22640719bbc4652f3f4ee)) // RO-3-2513

	ROM_REGION(0x0400, "graphics", 0)
	ROM_LOAD("graphics.ic70",   0x0000, 0x0200, CRC(77364e43) SHA1(b6ec6543acacab544e8568f700a1b581025897a1)) // 74S472 (512 x 8bit)
	ROM_LOAD("lowercase.ic70",  0x0200, 0x0200, CRC(fd677f54) SHA1(7d5a907f97df1f7f7379df3eedbd36c5c071abd1)) // alternate lower case

	ROM_REGION(0x400, "ef9364", ROMREGION_ERASE00)
ROM_END

} // anonymous namespace

//    YEAR  NAME       PARENT     COMPAT  MACHINE   INPUT    CLASS          INIT        COMPANY     FULLNAME        FLAGS
COMP( 1978, triton41,  triton72,  0,      triton1,  triton,  triton_state,  empty_init, "Transam",  "Triton L4.1",                MACHINE_NOT_WORKING )
COMP( 1979, triton51,  triton72,  0,      triton1,  triton,  triton_state,  empty_init, "Transam",  "Triton L5.1",                MACHINE_NOT_WORKING )
//COMP( 1979, triton61,  triton72,  0,      triton1,  triton,  triton_state,  empty_init, "Transam",  "Triton L6.1",                MACHINE_NOT_WORKING )
COMP( 1980, triton52,  triton72,  0,      triton2,  triton,  triton_state,  empty_init, "Transam",  "Triton L5.2",                MACHINE_NOT_WORKING )
//COMP( 1980, triton62,  triton72,  0,      triton2,  triton,  triton_state,  empty_init, "Transam",  "Triton L6.2",                MACHINE_NOT_WORKING )
COMP( 1980, triton72,  0,         0,      triton2,  triton,  triton_state,  empty_init, "Transam",  "Triton L7.2",                MACHINE_NOT_WORKING )
//COMP( 1980, triton82,  0,         0,      triton2,  triton,  triton_state,  empty_init, "Transam",  "Triton L8.2 Pascal System",  MACHINE_NOT_WORKING )
//COMP( 1980, triton92,  0,         0,      triton2,  triton,  triton_state,  empty_init, "Transam",  "Triton L9.2 Disk System",    MACHINE_NOT_WORKING )



trium.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Mitsubishi Trium phone.

    The SoC on the Trium Eclipse PCB has Philips and ARM logos, and is labeled:

        VP40577A
        Y03552.Y1
        KS0150 A
        VP40577A

****************************************************************************/

#include "emu.h"
#include "cpu/arm7/arm7.h"


namespace {

class trium_state : public driver_device
{
public:
	trium_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void trium(machine_config &config);

private:
	u16 one_r();

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};

u16 trium_state::one_r()
{
	return 1;
}

void trium_state::mem_map(address_map &map)
{
	map(0x00000000, 0x003fffff).rom().region("firmware", 0);
	map(0x01000000, 0x0103ffff).ram();
	map(0x03000000, 0x03007fff).ram();
	map(0x04001158, 0x0400115b).ram();
	map(0x04001698, 0x04001699).r(FUNC(trium_state::one_r));
}

static INPUT_PORTS_START(trium)
INPUT_PORTS_END

void trium_state::trium(machine_config &config)
{
	ARM7(config, m_maincpu, 50'000'000); // unknown type and clock
	m_maincpu->set_addrmap(AS_PROGRAM, &trium_state::mem_map);
}


ROM_START(triumec)
	ROM_REGION32_LE(0x400000, "firmware", 0)
	ROM_LOAD("2eclipse_pl_path_l10_4raqa060.bin", 0x000000, 0x400000, CRC(886de4ae) SHA1(44e627f0d6aee3e066ec877dfd40c2bfd9bfeb79))
ROM_END

} // anonymous namespace

SYST(2002, triumec, 0, 0, trium, trium, trium_state, empty_init, "Mitsubishi", "Trium Eclipse", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



trkfldch.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/*

The lexitvsprt contains "W55V9x" strings, which appear to identify the tech used here as the following
Nuvoton product
http://e-tech.com.hk/pd_nuvo_at_game.html


---

Track & Field Challenge TV Game
https://www.youtube.com/watch?v=wjn1lLylqog

Uses epoxy blobs for CPU etc.
These have been identified as Winbond 2005 BA5962 (large glob) + Winbond 200506 BA5934 (smaller glob)
seems to be G65816 derived with custom vectors?

PCB               Game
TV0001 R1.1       My First DDR
TV0002 R1.0       Track & Field

DDR & TF PCBs look identical, all the parts are in the same place, the traces are the same, and the silkscreened part # for resistors and caps are the same.

Some of m_unkregs must retain value (or return certain things) or RAM containing vectors gets blanked and game crashes.
The G65816 code on these is VERY ugly and difficult to follow, many redundant statements, excessive mode switching, accessing things via pointers to pointers etc.

One of the vectors points to 0x6000, there is nothing mapped there, could it be a small internal ROM (sound related?) or some debug trap for development?


---

4 Player System notes:

Mountain Bike Rally uses scrolling / split (helps confirm the same row skip logic seen in other games when using split)
Turn and Whack (cards) game runs far too quickly (might show us where timer config is)
The Power Game game also appears to run far too quickly
Territory Pursuit uses y-flipped sprites

The code to play/request a sample from ROM is at 0D:FB7B
It is called after pushing 3 words onto the stack containing the sample address

eg.
05:85B6: pea $0000
05:85B9: pea $0005
05:85BC: pea $f8bc
05:85BF: jsl $0dfb7b    --- play sample

Samples appear to be terminated with 0x8000

The game also has some terrible PSG-like music, is this coming from a secondary MCU as there is an additional glob on
each of the units using the tech, and the audio quality varies significantly.

*/

#include "emu.h"

#include "cpu/g65816/g65816.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"
#include "machine/timer.h"


namespace {

class trkfldch_state : public driver_device
{
public:
	trkfldch_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen"),
		m_gfxdecode(*this, "gfxdecode"),
		m_mainram(*this, "mainram"),
		m_spriteram(*this, "spriteram"),
		m_palram(*this, "palram"),
		m_palette(*this, "palette"),
		m_in(*this, "IN%u", 0U)
	{ }

	void trkfldch(machine_config &config);
	void vectors_map(address_map &map) ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

	virtual uint8_t unkregs_r(offs_t offset);
	virtual void unkregs_w(offs_t offset, uint8_t data);

private:

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<gfxdecode_device> m_gfxdecode;
	required_shared_ptr<uint8_t> m_mainram;
	required_shared_ptr<uint8_t> m_spriteram;
	required_shared_ptr<uint8_t> m_palram;
	required_device<palette_device> m_palette;
	required_ioport_array<4> m_in;

	void draw_sprites(screen_device& screen, bitmap_ind16& bitmap, const rectangle& cliprect, int pri);
	void render_text_tile_layer(screen_device& screen, bitmap_ind16& bitmap, const rectangle& cliprect, uint16_t base);
	void render_tile_layer(screen_device& screen, bitmap_ind16& bitmap, const rectangle& cliprect, int which);
	uint32_t screen_update_trkfldch(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void trkfldch_map(address_map &map) ATTR_COLD;

	uint8_t read_vector(offs_t offset);

	TIMER_DEVICE_CALLBACK_MEMBER(scanline);

	uint8_t m_which_vector = 0;

	uint8_t tilemap_scroll_window_r(int which, uint8_t reg, uint16_t real_base);

	uint8_t tmap0_scroll_window_r(offs_t offset);
	uint8_t tmap1_scroll_window_r(offs_t offset);

	void tilemap_scroll_window_w(int which, uint8_t reg, uint8_t data, uint16_t real_base);

	void tmap0_scroll_window_w(offs_t offset, uint8_t data);
	void tmap1_scroll_window_w(offs_t offset, uint8_t data);

	uint8_t m_dmaregs[0xe]{};

	uint8_t dmaregs_r(offs_t offset);
	void dmaregs_w(offs_t offset, uint8_t data);

	uint8_t m_modebank[0xb]{};

	uint8_t modebankregs_r(offs_t offset);
	void modebankregs_w(offs_t offset, uint8_t data);

	uint8_t m_tilemapbase[0x3]{};

	uint8_t tilemapbase_r(offs_t offset);
	void tilemapbase_w(offs_t offset, uint8_t data);

	uint8_t m_sysregs[0x10]{};

	uint8_t sysregs_r(offs_t offset);
	void sysregs_w(offs_t offset, uint8_t data);

	uint8_t m_unkregs[0x90]{};

	uint8_t m_tmapscroll_window[2][0x12]{};

	uint8_t m_unkdata[0x100000]{};
	int m_unkdata_addr = 0;

};

class trkfldch_lexi_state : public trkfldch_state
{
public:
	trkfldch_lexi_state(const machine_config &mconfig, device_type type, const char *tag) :
		trkfldch_state(mconfig, type, tag),
		m_extra(*this, "EXTRA")
	{ }


protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint8_t unkregs_r(offs_t offset) override;
	void unkregs_w(offs_t offset, uint8_t data) override;

private:
	required_ioport m_extra;
	uint8_t m_input_bit;
	uint8_t m_iopos;
};



void trkfldch_state::video_start()
{
}

void trkfldch_state::render_tile_layer(screen_device& screen, bitmap_ind16& bitmap, const rectangle& cliprect, int which)
{
//  tilemap 0 = trkfld events, my1stddr background

//  tilemap 1=  my1stddr HUD layer


// first group of tile scroll registers starting at 7820

//  none of these written by hammer throw in trkfld or ddr
//  uint8_t unk20 = m_tmapscroll_window[0][0x00]; // 0f - 100 meters  00 - javelin, long jump, triple  00 after events, 1e high jump  -- hurdle holes -- ddr    (assume default of 00?)
//  uint8_t unk21 = m_tmapscroll_window[0][0x01]; // 1f - 100 meters  00 - javelin, long jump, triple  00 after events, 00 high jump  00 hurdle holes -- ddr    (assume default of 00?)
//  uint8_t unk22 = m_tmapscroll_window[0][0x02]; // 1f - 100 meters  1e - javelin, long jump, triple  1e after events, 1e high jump  1e hurdle holes -- ddr    (assume default of 1e?)
//  uint8_t unk23 = m_tmapscroll_window[0][0x03]; // -- - 100 meters, 1e - javelin, long jump, triple  -- after events, -- high jump  -- hurdle holes -- ddr    (assume default of 1e?)
//  uint8_t unk24 = m_tmapscroll_window[0][0x04]; //                                                   00 after events                00 hurdle holes -- ddr    (assume default of 00?)
//  uint8_t unk25 = m_tmapscroll_window[0][0x05]; //                                                   28 after events                14 hurdle holes -- ddr    (assume default of 28?)

//  uint16_t xscroll = (m_tmapscroll_window[0][0x06] << 0) | (m_tmapscroll_window[0][0x07] << 8); // trkfld tilemap 0, race top            //   why do the split screen races use a different set of scroll registers? maybe global (applies to both layers?) as only one is enabled at this point.
//  uint16_t xscroll = (m_tmapscroll_window[0][0x08] << 0) | (m_tmapscroll_window[0][0x09] << 8); // trkfld tilemap 0, race bot (window?)
//  uint16_t xscroll = (m_tmapscroll_window[0][0x0a] << 0) | (m_tmapscroll_window[0][0x0b] << 8); // trkfld tilemap 0?, javelin
//  0c,0d never written
//  0e,0f never written
//  uint16_t yscroll = (m_tmapscroll_window[0][0x10] << 0) | (m_tmapscroll_window[0][0x11] << 8); // trkfld tilemap 0, javelin, holes, hammer throw

// second group of tile scroll registers starting at 7832

//  32 never written                                                                                                                                        (assume default of 00? to fit 20-25 pattern?)
//  uint8_t windowtop    = m_tmapscroll_window[1][0x01]; // 0x00 - on trkfld hurdle the holes (also resets to 0x00 &  after event - possible default values)             (assume default of 00?)
//  uint8_t windowbottom = m_tmapscroll_window[1][0x02]; // 0x1e   ^                                          0x1e                                                       (assume default of 1e?)
//  uint8_t unk35        = m_tmapscroll_window[1][0x03];  set to 1e (30) (30*8=240) by trkfld high jump otherwise uninitialized always - something to do with bottom     (assume default of 1e?)
//  for left / right on tilemap 1
//  uint8_t windowleft   = m_tmapscroll_window[1][0x04];  //0x29  when unused on my1stddr, 0x25 when used   14 when used on hurdle holes, resets to 00 after event       (assume default of 00?)
//  uint8_t windowright  = m_tmapscroll_window[1][0x05];  //0x29                           0x29             28 when used on hurdle holes, resets to 28 after event       (assume default of 28?)

//  06,076never written
//  uint16_t xscroll = (m_tmapscroll_window[1][0x08] << 0) | (m_tmapscroll_window[1][0x09] << 8); // trkfld tilemap 1?, javelin
//  0a,0b never written
//  0c,0d never written
//  0e,0f never written
//  uint16_t yscroll = (m_tmapscroll_window[1][0x10] << 0) | (m_tmapscroll_window[1][0x11] << 8); // my1stddr tilemap 1 scroller, trkfld tilemap 1 holes (both window)

//  printf("window %02x %02x  %02x %02x  %02x %02x\n", unk20, unk21, unk22, unk23, unk24, unk25);
//  printf("xscroll %04x\n", xscroll);
//  printf("yscroll %04x\n", yscroll);
//  printf("window left/right %02x %02x\n", windowleft, windowright);
//  printf("window top/bottom %02x %02x\n", windowtop, windowbottom);


	int base, gfxbase, gfxregion;
	int tilexsize = 8;

	if (which == 0)
	{
		base = (m_tilemapbase[0x00] << 8);
		gfxbase = (m_modebank[0x01] * 0x2000);
	}
	else //if (which == 1)
	{
		base = (m_tilemapbase[0x01] << 8);
		gfxbase = (m_modebank[0x03] * 0x2000);
	}

	if (m_modebank[0x00] & 1) // seems like it might be a global control for bpp?
	{
		gfxbase -= 0x200;
		gfxregion = 0;
	}
	else
	{
		gfxbase -= 0x2ac;
		gfxregion = 2;
	}



	for (int y = 0; y < 30; y++)
	{
		for (int x = 0; x < 41; x++)
		{
			// fppt tttt   tttt tttt

			rectangle clip;
			clip.set(x*8, (x*8)+7, y*8, (y*8)+7);

			clip &= cliprect;


			address_space &mem = m_maincpu->space(AS_PROGRAM);

			int tile_address = (y * 41) + x;

			uint8_t byte = mem.read_byte(base+((tile_address * 2)));
			uint8_t attr = mem.read_byte(base+((tile_address * 2)+1));

			int tile = (attr << 8) | byte;

			int flipx = (tile & 0x8000) >> 15;
			int pal =   (tile & 0x6000) >> 13;

			tile &= 0x1fff;

			tile += gfxbase;

			gfx_element* gfx = m_gfxdecode->gfx(gfxregion);

			gfx->transpen(bitmap, clip,tile, pal, flipx, 0, x*tilexsize, y*8, 0);

		}
	}
}

void trkfldch_state::render_text_tile_layer(screen_device& screen, bitmap_ind16& bitmap, const rectangle& cliprect, uint16_t base)
{
	// this isn't correct, it doesn't seem like a 'real' tilemap, maybe some kind of sprite / tile hybrid, or something with end of line markers?
	// it is needed for the 'good' 'perfect' 'miss' text on DDR ingame
	if (0)
	{
		// guess, but it fits with where the other tilegfxbase registers are, and is only written on my1stddr when this layer is enabled
		int tilegfxbase = (m_modebank[0x07] * 0x80) - 0x100;

		int offs = 0;
		for (int y = 0; y < 4; y++)
		{
			for (int x = 0; x < 32; x++)
			{
				address_space& mem = m_maincpu->space(AS_PROGRAM);

				uint8_t byte = mem.read_byte(base + offs);
				offs++;

				int tile = tilegfxbase | byte;

				gfx_element* gfx = m_gfxdecode->gfx(4);

				gfx->transpen(bitmap, cliprect, tile, 0, 0, 0, x * 8, y * 16, 0);

			}
		}
	}
}



// regs                        11 13 15 17
//
// DDR Title = 5000            03 03 05 00
// DDR Ingame Girl 1 = 7000    04 05 06 80
// DDR Music Select  = 9000    05 03 04 00
// trkfldch logos    = b000    06 07 06 00
// trkfldch ingame uses different bpp so different addressing here too?


void trkfldch_state::draw_sprites(screen_device& screen, bitmap_ind16& bitmap, const rectangle& cliprect, int pri)
{
	for (int i = 0x500 - 5; i >= 0; i -= 5)
	{
		int priority = (m_spriteram[i + 4] & 0x20)>>5;

		// list is NOT drawn in order, but instead z sorted, see shadows in trkfldch
		if (priority != pri)
			continue;

		// logerror("entry %02x %02x %02x %02x %02x\n", m_spriteram[i + 0], m_spriteram[i + 1], m_spriteram[i + 2], m_spriteram[i + 3], m_spriteram[i + 4]);
		int tilegfxbase = (m_modebank[0x05] * 0x800);

		/* m_spriteram[i + 0]  --pp tt-y
		   m_spriteram[i + 1]  yyyy yyyy
		   m_spriteram[i + 2]  tttt tttt
		   m_spriteram[i + 3]  xxxx xxxx
		   m_spriteram[i + 4]  --zfF-tdx

		   p = palette bits
		   t = tile bits
		   y = y pos bits
		   x = x pos bits
		   z = priority
		   fF = x/y flip
		   d = pixel double
		*/

		int y = m_spriteram[i + 1];
		int x = m_spriteram[i + 3];
		int tile = m_spriteram[i + 2];

		int doublesize = m_spriteram[i + 4] & 0x02;

		int tilehigh = m_spriteram[i + 4] & 0x04;
		int tilehigh2 = m_spriteram[i + 0] & 0x04;
		int tilehigh3 = m_spriteram[i + 0] & 0x08;

		int pal = 0;

		int flipx = m_spriteram[i + 4] & 0x10;
		int flipy = m_spriteram[i + 4] & 0x08;

		if (tilehigh)
			tile += 0x100;

		if (tilehigh2)
			tile += 0x200;

		if (tilehigh3)
			tile += 0x400;

		int xhigh = m_spriteram[i + 4] & 0x01;
		int yhigh = m_spriteram[i + 0] & 0x01; // or enable bit?

		x = x | (xhigh << 8);
		y = y | (yhigh << 8);

		y -= 0x100;
		y -= 16;
		x -= 16;

		gfx_element* gfx;

		if (m_modebank[0x00] & 1) // seems like it might be a global control for bpp?
		{
			gfx = m_gfxdecode->gfx(1);
			tilegfxbase -= 0x80;
		}
		else
		{
			pal = (m_spriteram[i + 0] & 0x30)>>4;
			gfx = m_gfxdecode->gfx(3);
			tilegfxbase -= 0x40;
			tilegfxbase -= 0x6b;

		}

		gfx->zoom_transpen(bitmap, cliprect, tile + tilegfxbase, pal, flipx, flipy, x, y, doublesize ? 0x20000 : 0x10000, doublesize ? 0x20000 : 0x10000, 0);
	}
}

uint32_t trkfldch_state::screen_update_trkfldch(screen_device& screen, bitmap_ind16& bitmap, const rectangle& cliprect)
{
	bitmap.fill(0, cliprect);

	// 3 lots of 0x100 values, but maybe not rgb, seems to be yuv (borrowed from tetrisp2.cpp)
	for (int i = 0; i < 256; i++)
	{
		uint8_t u =  m_palram[0x000 + i] & 0xff;
		uint8_t y1 = m_palram[0x100 + i] & 0xff;
		uint8_t v =  m_palram[0x200 + i] & 0xff;
		double bf = y1+1.772*(u - 128);
		double gf = y1-0.334*(u - 128) - 0.714 * (v - 128);
		double rf = y1+1.772*(v - 128);
		// clamp to 0-255 range
		rf = std::min(rf,255.0);
		rf = std::max(rf,0.0);
		gf = std::min(gf,255.0);
		gf = std::max(gf,0.0);
		bf = std::min(bf,255.0);
		bf = std::max(bf,0.0);

		uint8_t r = (uint8_t)rf;
		uint8_t g = (uint8_t)gf;
		uint8_t b = (uint8_t)bf;

		m_palette->set_pen_color(i, r, g, b);
	}

	if (1) // one of the m_modebank[0x00] bits almost certainly would enable / disable this
	{
		render_tile_layer(screen, bitmap, cliprect, 0);
	}

	draw_sprites(screen, bitmap, cliprect, 0);

	if (m_modebank[0x00] & 0x10) // definitely looks like layer enable
	{
		render_tile_layer(screen, bitmap, cliprect, 1);
	}

	draw_sprites(screen, bitmap, cliprect, 1);

	if (m_modebank[0x00] & 0x20) // this layer is buggy and not currently drawn
	{
		int base = (m_tilemapbase[0x02] << 8);
		render_text_tile_layer(screen, bitmap, cliprect, base);
	}

	return 0;
}

uint8_t trkfldch_state::tilemap_scroll_window_r(int which, uint8_t reg, uint16_t real_base)
{
	uint8_t ret = m_tmapscroll_window[which][reg];
	logerror("%s: tilemap_scroll_window_r (tilemap %d) reg: %02x (returning %02x) (real address %04x)\n", machine().describe_context(), which, reg, ret, real_base + reg);
	return ret;
}

uint8_t trkfldch_state::tmap0_scroll_window_r(offs_t offset)
{
	uint8_t ret = tilemap_scroll_window_r(0, offset, 0x7820);
	return ret;
}

uint8_t trkfldch_state::tmap1_scroll_window_r(offs_t offset)
{
	uint8_t ret = tilemap_scroll_window_r(1, offset, 0x7832);
	return ret;
}

void trkfldch_state::tilemap_scroll_window_w(int which, uint8_t reg, uint8_t data, uint16_t real_base)
{
	m_tmapscroll_window[which][reg] = data;

	switch (reg)
	{
	case 0x00:
		logerror("%s: tilemap_scroll_window_w (tilemap %d) reg: %02x (window top reg0?) (data %02x) (real address %04x)\n", machine().describe_context(), which, reg, data, real_base + reg);
		break;

	case 0x01:
		logerror("%s: tilemap_scroll_window_w (tilemap %d) reg: %02x (window top reg1?) (data %02x) (real address %04x)\n", machine().describe_context(), which, reg, data, real_base + reg);
		break;

	case 0x02:
		logerror("%s: tilemap_scroll_window_w (tilemap %d) reg: %02x (window bottom reg0?) (data %02x) (real address %04x)\n", machine().describe_context(), which, reg, data, real_base + reg);
		break;

	case 0x03:
		logerror("%s: tilemap_scroll_window_w (tilemap %d) reg: %02x (window bottom reg1?) (data %02x) (real address %04x)\n", machine().describe_context(), which, reg, data, real_base + reg);
		break;

	case 0x04:
		logerror("%s: tilemap_scroll_window_w (tilemap %d) reg: %02x (window left reg) (data %02x) (real address %04x)\n", machine().describe_context(), which, reg, data, real_base + reg);
		break;

	case 0x05:
		logerror("%s: tilemap_scroll_window_w (tilemap %d) reg: %02x (window right reg) (data %02x) (real address %04x)\n", machine().describe_context(), which, reg, data, real_base + reg);
		break;

	case 0x06:
		logerror("%s: tilemap_scroll_window_w (tilemap %d) reg: %02x (non-window X scroll low) (data %02x) (real address %04x)\n", machine().describe_context(), which, reg, data, real_base + reg);
		break;

	case 0x07:
		logerror("%s: tilemap_scroll_window_w (tilemap %d) reg: %02x (non-window X scroll high) (data %02x) (real address %04x)\n", machine().describe_context(), which, reg, data, real_base + reg);
		break;

	case 0x08:
		logerror("%s: tilemap_scroll_window_w (tilemap %d) reg: %02x (window X scroll low) (data %02x) (real address %04x)\n", machine().describe_context(), which, reg, data, real_base + reg);
		break;

	case 0x09:
		logerror("%s: tilemap_scroll_window_w (tilemap %d) reg: %02x (window X scroll high) (data %02x) (real address %04x)\n", machine().describe_context(), which, reg, data, real_base + reg);
		break;

	case 0x0a:
		logerror("%s: tilemap_scroll_window_w (tilemap %d) reg: %02x (another X scroll low) (data %02x) (real address %04x)\n", machine().describe_context(), which, reg, data, real_base + reg);
		break;

	case 0x0b:
		logerror("%s: tilemap_scroll_window_w (tilemap %d) reg: %02x (another X scroll high) (data %02x) (real address %04x)\n", machine().describe_context(), which, reg, data, real_base + reg);
		break;

	case 0x0c:
		// unused, probably unknown Y scroll low
		logerror("%s: tilemap_scroll_window_w (tilemap %d) reg: %02x (unknown) (data %02x) (real address %04x)\n", machine().describe_context(), which, reg, data, real_base + reg);
		break;

	case 0x0d:
		// unused, probably unknown Y scroll low
		logerror("%s: tilemap_scroll_window_w (tilemap %d) reg: %02x (unknown) (data %02x) (real address %04x)\n", machine().describe_context(), which, reg, data, real_base + reg);
		break;

	case 0x0e:
		// unused, probably unknown Y scroll low
		logerror("%s: tilemap_scroll_window_w (tilemap %d) reg: %02x (unknown) (data %02x) (real address %04x)\n", machine().describe_context(), which, reg, data, real_base + reg);
		break;

	case 0x0f:
		// unused, probably unknown Y scroll low
		logerror("%s: tilemap_scroll_window_w (tilemap %d) reg: %02x (unknown) (data %02x) (real address %04x)\n", machine().describe_context(), which, reg, data, real_base + reg);
		break;

	case 0x10:
		logerror("%s: tilemap_scroll_window_w (tilemap %d) reg: %02x (window Y scroll low) (data %02x) (real address %04x)\n", machine().describe_context(), which, reg, data, real_base + reg);
		break;

	case 0x11:
		logerror("%s: tilemap_scroll_window_w (tilemap %d) reg: %02x (window Y scroll high) (data %02x) (real address %04x)\n", machine().describe_context(), which, reg, data, real_base + reg);
		break;
	}
}

void trkfldch_state::tmap0_scroll_window_w(offs_t offset, uint8_t data)
{
	tilemap_scroll_window_w(0, offset, data, 0x7820);
}

void trkfldch_state::tmap1_scroll_window_w(offs_t offset, uint8_t data)
{
	tilemap_scroll_window_w(1, offset, data, 0x7832);
}




uint8_t trkfldch_state::dmaregs_r(offs_t offset)
{
	uint8_t ret = m_dmaregs[offset];

	switch (offset)
	{
	case 0x05: // abl4play polls this expecting it to be 0 to continue (probably becomes after DMA is complete, or can show the status in realtime?)
		ret = 0x00;
		break;

	case 0x06: // abl4play polls this expecting it to be 0 to continue (probably becomes after DMA is complete, or can show the status in realtime?)
		ret = 0x00;
		break;
	}

	logerror("%s: dmaregs_r %04x (returning %02x)\n", machine().describe_context(), offset, ret);
	return ret;
}

void trkfldch_state::dmaregs_w(offs_t offset, uint8_t data)
{
	m_dmaregs[offset] = data;

	switch (offset)
	{

	case 0x00: // sprite list location (dma source?)
		logerror("%s: dmaregs_w %04x %02x (dma source low )\n", machine().describe_context(), offset, data);
		break;

	case 0x01: // sprite list location (dma source?)
		logerror("%s: dmaregs_w %04x %02x (dma source med )\n", machine().describe_context(), offset, data);
		break;

	case 0x02:
		logerror("%s: dmaregs_w %04x %02x (dma source high)\n", machine().describe_context(), offset, data);
		break;

	case 0x03:
		logerror("%s: dmaregs_w %04x %02x (dma dest low )\n", machine().describe_context(), offset, data);
		break;

	case 0x04:
		logerror("%s: dmaregs_w %04x %02x (dma dest high)\n", machine().describe_context(), offset, data);
		break;

	case 0x05:
		logerror("%s: dmaregs_w %04x %02x (dma length low ) (and trigger)\n", machine().describe_context(), offset, data);
		{
			address_space& mem = m_maincpu->space(AS_PROGRAM);
			uint16_t dmalength = (m_dmaregs[0x06] << 8) | m_dmaregs[0x05];
			uint32_t dmasource = (m_dmaregs[0x02] << 16) | (m_dmaregs[0x01] << 8) | m_dmaregs[0x00];
			uint16_t dmadest = (m_dmaregs[0x04] << 8) | m_dmaregs[0x03];

			//if (dmadest != 0x6800)
			logerror("%s: performing dma src: %06x dst %04x len %04x and extra params %02x %02x %02x %02x %02x %02x\n", machine().describe_context(), dmasource, dmadest, dmalength, m_dmaregs[0x07], m_dmaregs[0x08], m_dmaregs[0x09], m_dmaregs[0x0b], m_dmaregs[0x0c], m_dmaregs[0x0d]);

			int writeoffset = 0;
			int writedo = m_dmaregs[0x0d];

			int readoffset = 0;
			int readdo = m_dmaregs[0x09];

			for (uint32_t j = 0; j < dmalength; j++)
			{
				uint8_t byte = mem.read_byte(dmasource + readoffset);
				readdo--;
				if (readdo < 0)
				{
					readdo = m_dmaregs[0x09];
					readoffset += m_dmaregs[0x07] | (m_dmaregs[0x08] << 8);
				}
				else
				{
					readoffset++;
				}

				mem.write_byte(dmadest + writeoffset, byte);
				writedo--;
				if (writedo < 0)
				{
					writedo = m_dmaregs[0x0d];
					writeoffset += m_dmaregs[0xb] | (m_dmaregs[0x0c] << 8);
				}
				else
				{
					writeoffset++;
				}

			}
		}
		break;

	case 0x06:
		logerror("%s: dmaregs_w %04x %02x (dma length high)\n", machine().describe_context(), offset, data);
		break;

	case 0x07: // after a long time
		logerror("%s: dmaregs_w %04x %02x (dma source read skip size)\n", machine().describe_context(), offset, data);
		break;

	case 0x08: // rarely (my1stddr)
		logerror("%s: dmaregs_w %04x %02x (dma source unknown)\n", machine().describe_context(), offset, data);
		break;

	case 0x09: // after a long time
		logerror("%s: dmaregs_w %04x %02x (dma source read group size)\n", machine().describe_context(), offset, data);
		break;

	case 0x0a: // unused?
		logerror("%s: dmaregs_w %04x %02x (dma unused)\n", machine().describe_context(), offset, data);
		break;

	case 0x0b: // after a long time
		logerror("%s: dmaregs_w %04x %02x (dma dest write skip size)\n", machine().describe_context(), offset, data);
		break;

	case 0x0c: // rarely (my1stddr)
		logerror("%s: dmaregs_w %04x %02x (dma dest unknown)\n", machine().describe_context(), offset, data);
		break;

	case 0x0d: // after a long time
		logerror("%s: dmaregs_w %04x %02x (dma dest write group size)\n", machine().describe_context(), offset, data);
		break;

	}
}


uint8_t trkfldch_state::modebankregs_r(offs_t offset)
{
	uint8_t ret = m_modebank[offset];
	logerror("%s: modebankregs_r %04x (returning %02x)\n", machine().describe_context(), offset, ret);
	return ret;
}

void trkfldch_state::modebankregs_w(offs_t offset, uint8_t data)
{
	m_modebank[offset] = data;

	switch (offset)
	{
	case 0x00: // gfxmode select (4bpp / 8bpp) and layer enables
		logerror("%s: unkregs_w (enable, bpp select) %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x01: // tilegfxbank 1
		logerror("%s: unkregs_w %04x %02x (tilegfxbank 1)\n", machine().describe_context(), offset, data);
		break;

	case 0x02: // 00 - startup (probably more tilegfxbank 1 bits)
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x03: // tilegfxbank 2
		logerror("%s: unkregs_w %04x %02x (tilegfxbank 2)\n", machine().describe_context(), offset, data);
		break;

	case 0x04: // 00 - startup (probably more tilegfxbank 2 bits)
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x05: // spritegfxbank
		logerror("%s: unkregs_w  %04x %02x (spritegfxbank)\n", machine().describe_context(), offset, data);
		break;

	case 0x06: // 00 (probably more spritegfxbank bits)
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x07: // gfxbank for weird layer
		logerror("%s: unkregs_w %04x %02x (weird gfx bank)\n", machine().describe_context(), offset, data);
		break;

	case 0x08: // more gfxbank for weird layer?
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x09:  // unknowns? another unknown layer?
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x0a:  // unknowns? another unknown layer?
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;
	}
}


uint8_t trkfldch_state::tilemapbase_r(offs_t offset)
{
	uint8_t ret = m_tilemapbase[offset];
	logerror("%s: tilemapbase_r %04x (returning %02x)\n", machine().describe_context(), offset, ret);
	return ret;
}

void trkfldch_state::tilemapbase_w(offs_t offset, uint8_t data)
{
	m_tilemapbase[offset] = data;
	logerror("%s: tilemapbase_w %04x %02x (tilebase %d)\n", machine().describe_context(), offset, data, offset);
}


void trkfldch_state::trkfldch_map(address_map &map)
{
	map(0x000000, 0x003fff).ram().share("mainram");

	map(0x006800, 0x006cff).ram().share("spriteram");

	map(0x007000, 0x0072ff).ram().share("palram");

	// 7800 - 78xx look like registers?

	map(0x007800, 0x00780f).rw(FUNC(trkfldch_state::sysregs_r), FUNC(trkfldch_state::sysregs_w));


	map(0x007810, 0x00781a).rw(FUNC(trkfldch_state::modebankregs_r), FUNC(trkfldch_state::modebankregs_w));

	map(0x007820, 0x007831).rw(FUNC(trkfldch_state::tmap0_scroll_window_r), FUNC(trkfldch_state::tmap0_scroll_window_w));
	map(0x007832, 0x007843).rw(FUNC(trkfldch_state::tmap1_scroll_window_r), FUNC(trkfldch_state::tmap1_scroll_window_w));

	map(0x007854, 0x007856).rw(FUNC(trkfldch_state::tilemapbase_r), FUNC(trkfldch_state::tilemapbase_w));

	map(0x007860, 0x00786d).rw(FUNC(trkfldch_state::dmaregs_r), FUNC(trkfldch_state::dmaregs_w));

	map(0x007870, 0x0078ff).rw(FUNC(trkfldch_state::unkregs_r), FUNC(trkfldch_state::unkregs_w));

	map(0x008000, 0xffffff).rom().region("maincpu", 0x000000); // good for code mapped at 008000 and 050000 at least
}

void trkfldch_state::vectors_map(address_map &map)
{
	map(0x00, 0x1f).r(FUNC(trkfldch_state::read_vector));
}

uint8_t trkfldch_state::read_vector(offs_t offset)
{
	uint8_t *rom = memregion("maincpu")->base();

	/* what appears to be a table of vectors appears at the START of ROM, maybe this gets copied to RAM, maybe used directly?
	00 : (invalid)
	02 : (invalid)
	04 : 0xA2C6  (dummy)
	06 : 0xA334  (real function - vbl?)
	08 : 0xA300  (dummy)
	0a : 0xA2E0  (dummy)
	0c : 0xA2B9  (dummy)
	0e : 0xA2ED  (dummy)
	10 : 0xA2D3  (dummy)
	12 : 0xA327  (dummy)
	14 : 0xA30D  (real function) (dummy in trkfld?) (controls arrow speed in ddr?)
	16 : 0x6000  (points at ram? or some internal ROM? we have nothing mapped here, not cleared as RAM either)
	18 : 0xA31A  (dummy) (not dummy in trkfld? - timer interrupt?)
	1a : 0xA2AC  (dummy)
	1c : 0xA341  (boot vector)
	1e : (invalid)
	*/

	logerror("reading vector offset %02x\n", offset);

	if (offset == 0x0b)
	{   // NMI
		return rom[m_which_vector+1];
	}
	else if (offset == 0x0a)
	{   // NMI
		return rom[m_which_vector];
	}

	// boot vector
	return rom[offset];
}


TIMER_DEVICE_CALLBACK_MEMBER(trkfldch_state::scanline)
{
	int scanline = param;

	if (scanline == 200)
	{
		m_which_vector = 0x06;
		m_maincpu->set_input_line(G65816_LINE_NMI, ASSERT_LINE);
	}
	else if (scanline == 201)
	{
		m_which_vector = 0x06;
		m_maincpu->set_input_line(G65816_LINE_NMI, CLEAR_LINE);
	}

	if (scanline == 20)
	{
		m_which_vector = 0x14;
		m_maincpu->set_input_line(G65816_LINE_NMI, ASSERT_LINE);
	}
	else if (scanline == 21)
	{
		m_which_vector = 0x14;
		m_maincpu->set_input_line(G65816_LINE_NMI, CLEAR_LINE);
	}

	// this is clearly a timer interrupt, trkfldch needs it to count
	if ((scanline >= 80) && (scanline < 120))
	{
		if ((scanline & 1) == 0)
		{
			m_which_vector = 0x18;
			m_maincpu->set_input_line(G65816_LINE_NMI, ASSERT_LINE);
		}
		else
		{
			m_which_vector = 0x18;
			m_maincpu->set_input_line(G65816_LINE_NMI, CLEAR_LINE);
		}
	}
}

static INPUT_PORTS_START( trkfldch )
	PORT_START("IN0")
	PORT_DIPNAME( 0x01, 0x01, "IN0" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN1")
	PORT_DIPNAME( 0x01, 0x01, "IN1" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN2")
	PORT_START("IN3")
INPUT_PORTS_END

static INPUT_PORTS_START( konsb )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN1")
	PORT_DIPNAME( 0x01, 0x01, "IN1" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN2")
	PORT_START("IN3")
INPUT_PORTS_END

static INPUT_PORTS_START( my1stddr )
	PORT_START("IN0")
	PORT_DIPNAME( 0x01, 0x01, "IN0" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("O") // selects / forward
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_16WAY // directions correct based on 'letters' minigame
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_16WAY
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_16WAY
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("X") // goes back
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN1")
	PORT_DIPNAME( 0x01, 0x01, "IN1" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN2")
	PORT_START("IN3")
INPUT_PORTS_END

static INPUT_PORTS_START( abl4play )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_START1 ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("P1 Select")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)

	PORT_START("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(3)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(3)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(3)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(3)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(3)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(3)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(4)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(4)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(4)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(4)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(4)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(4)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( shtscore )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("P1 Select")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Kick")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN1")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN2")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN3")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( lexi )
	PORT_START("IN0")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN1")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN2")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN3")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("EXTRA")
	PORT_BIT( 0x00008, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x00020, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x00080, IP_ACTIVE_LOW, IPT_BUTTON3 )
	PORT_BIT( 0x00200, IP_ACTIVE_LOW, IPT_BUTTON4 )
	PORT_BIT( 0x00800, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x02000, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x08000, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x20000, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
INPUT_PORTS_END

static const gfx_layout tiles8x8x8_layout =
{
	8,8,
	RGN_FRAC(1,1),
	8,
	{ 48,49, 32,33, 16,17, 0, 1 },
	{ 8,10,12, 14, 0,2,4,6  },
	{ STEP8(0,64) },
	512,
};

static const gfx_layout tiles8x16x8_layout =
{
	8,16,
	RGN_FRAC(1,1),
	8,
	{ 48,49, 32,33, 16,17, 0, 1 },
	{ 8,10,12, 14, 0,2,4,6  },
	{ STEP16(0,64) },
	1024,
};


static const gfx_layout tiles16x16x8_layout =
{
	16,16,
	RGN_FRAC(1,1),
	8,
	{ 96, 97, 64, 65, 32, 33, 0, 1  },
	{ 8,10,12,14, 0,2,4,6, 24,26,28,30, 16,18,20,22 },
	{ STEP16(0,128) },
	128*16,
};


// TODO: if we're going to use gfxdecode then allocate this manually with the correct number of tiles
//  might be we have to use manual drawing tho, the base offset is already strange
static const gfx_layout tiles8x8x6_layout =
{
	8,8,
	0x5500*4,
	6,
	{ 32, 33, 16, 17, 0, 1  },
	{ 8,10,12,14, 0,2,4,6 },
	{ STEP8(0,48) },
	48*8,
};


static const gfx_layout tiles16x16x6_layout =
{
	16,16,
	0x5500,
	6,
	{ 64, 65, 32, 33, 0, 1  },
	{ 8,10,12,14, 0,2,4,6, 24,26,28,30, 16,18,20,22 },
	{ STEP16(0,96) },
	96*16,
};


static GFXDECODE_START( gfx_trkfldch )
	GFXDECODE_ENTRY( "maincpu", 0, tiles8x8x8_layout, 0, 1 )
	GFXDECODE_ENTRY( "maincpu", 0, tiles16x16x8_layout, 0, 1 )
	GFXDECODE_ENTRY( "maincpu", 0x40, tiles8x8x6_layout, 0, 4 )
	GFXDECODE_ENTRY( "maincpu", 0x40, tiles16x16x6_layout, 0, 4 )
	GFXDECODE_ENTRY( "maincpu", 0, tiles8x16x8_layout, 0, 1 )
GFXDECODE_END

/*

7800 / 7801 seem to be IRQ related

7800 : 0001 - ? (there is no irq 0x00)
       0002 - ? (there is no irq 0x02)
       0004 used in irq 0x04
       0008 used in irq 0x06
       0010 used in irq 0x08
       0020 used in irq 0x0a
       0x40 used in irq 0x0c
       0x80 used in irq 0x0e (and by code accessing other ports in the main execution?!)

7801 : 0001 used in irq 0x10
     : 0002 used in irq 0x12
     : 0004 used in irq 0x14
     : 0008 - ? (there is no irq 0x016, it points to unknown area? and we have no code touching this bit)
     : 0010 used in irq 0x18
     : 0020 used in irq 0x1a and 0x06?! (used with OR instead of EOR in 0x06, force IRQ?)
     : 0x40 - ? (there is no irq 0x1c - it's the boot vector)
     : 0x80 - ? (there is no irq 0x1e)

*/

uint8_t trkfldch_state::sysregs_r(offs_t offset)
{
	uint8_t ret = m_sysregs[offset];

	switch (offset)
	{
	case 0x00: // IRQ status?, see above
		ret = machine().rand();
		logerror("%s: sysregs_r (IRQ state?) %04x (returning %02x)\n", machine().describe_context(), offset, ret);
		break;

	case 0x01: // IRQ status?, see above
		ret = machine().rand();
		logerror("%s: sysregs_r (IRQ state?) %04x (returning %02x)\n", machine().describe_context(), offset, ret);
		break;

	case 0x02: // code to write 0x7f here during startup, also code to read port, mask with 0x7f and rewrite (clear top bit, probably gets set by hw?)
		logerror("%s: sysregs_r %04x (returning %02x)\n", machine().describe_context(), offset, ret);
		break;

	case 0x03: // there is code to read, set bit 0x04 and write back out as well as code to clear to 0x00 (no other uses?)
		logerror("%s: sysregs_r %04x (returning %02x)\n", machine().describe_context(), offset, ret);
		break;


	case 0x04: // bit 0x80 is checked and looped on after writes to 78b6 (the 'large amount of data upload' port)
		ret = 0x80;
		logerror("%s: sysregs_r %04x (returning %02x)\n", machine().describe_context(), offset, ret);
		break;

	case 0x05: // bit 0x20 is checked and looped on right after DMA trigger (assuming DMA trigger is correct)
		ret = 0x20;
		logerror("%s: sysregs_r %04x (returning %02x)\n", machine().describe_context(), offset, ret);
		break;

	case 0x06: // no real reads, only side-effect of reading 7805
		logerror("%s: sysregs_r %04x (returning %02x)\n", machine().describe_context(), offset, ret);
		break;
	}

	return ret;
}


void trkfldch_state::sysregs_w(offs_t offset, uint8_t data)
{
	m_sysregs[offset] = data;

	switch (offset)
	{
	case 0x00: // IRQ ack/force?, see above
		logerror("%s: sysregs_w (IRQ ack/force?) %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x01: // IRQ maybe status, see above
		logerror("%s: sysregs_w (IRQ ack/force?) %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x02: // startup
		logerror("%s: sysregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x03: // startup
		logerror("%s: sysregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x04: // startup
		logerror("%s: sysregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x05: // startup
		logerror("%s: sysregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	default:
		logerror("%s: sysregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	}
}



uint8_t trkfldch_state::unkregs_r(offs_t offset)
{
	uint8_t ret = m_unkregs[offset];

	switch (offset)
	{

	case 0x00: // read in irq (inputs?)
		ret = m_in[0]->read();
		logerror("%s: unkregs_r %04x (returning %02x) (Player 1 inputs)\n", machine().describe_context(), offset, ret);
		break;

	case 0x01:
		logerror("%s: unkregs_r %04x (returning %02x)\n", machine().describe_context(), offset, ret);
		break;

	// 0x02

	case 0x03:
		ret = m_in[1]->read();
		logerror("%s: unkregs_r %04x (returning %02x) (Player 2 inputs)\n", machine().describe_context(), offset, ret);
		break;

	case 0x04:
		logerror("%s: unkregs_r %04x (returning %02x)\n", machine().describe_context(), offset, ret);
		break;

	case 0x05:
		logerror("%s: unkregs_r %04x (returning %02x)\n", machine().describe_context(), offset, ret);
		break;

	case 0x06:
		logerror("%s: unkregs_r %04x (returning %02x)\n", machine().describe_context(), offset, ret);
		break;

	case 0x07:
		ret = m_in[2]->read();
		logerror("%s: unkregs_r %04x (returning %02x) (Player 3 inputs)\n", machine().describe_context(), offset, ret);
		break;

	// 0x08
	// 0x09
	// 0x0a

	case 0x0b:
		ret = m_in[3]->read();
		logerror("%s: unkregs_r %04x (returning %02x) (Player 4 inputs)\n", machine().describe_context(), offset, ret);
		break;


	case 0x0f:
		logerror("%s: unkregs_r %04x (returning %02x)\n", machine().describe_context(), offset, ret);
		break;

	case 0x10: // only read as a side-effect of reading 0x7f?
		logerror("%s: unkregs_r %04x (returning %02x)\n", machine().describe_context(), offset, ret);
		break;

	case 0x46: // 0x70
		logerror("%s: unkregs_r %04x (returning %02x)\n", machine().describe_context(), offset, ret);
		break;

	case 0x47:
		logerror("%s: unkregs_r %04x (returning %02x)\n", machine().describe_context(), offset, ret);
		break;

	default:
		logerror("%s: unkregs_r %04x (returning %02x)\n", machine().describe_context(), offset, ret);
		break;
	}
	return ret;
}



void trkfldch_state::unkregs_w(offs_t offset, uint8_t data)
{
	m_unkregs[offset] = data;

	switch (offset)
	{
	// 7x = I/O area?

	case 0x01: // startup
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x02: // startup
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x03: // some kind of serial device?
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x04: // startup
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x05: // some kind of serial device? (used with 73?)
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x06: // startup
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x07: // every second or so
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x08: // startup
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x09: // startup
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x0a: // startup
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x0f: // startup
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;


	case 0x11: // startup (my1stddr)
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x12: // startup (my1stddr)
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x13:
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x14:
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;


	case 0x45: // (real address 78b5) startup
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	case 0x46: // significant data transfer shortly after boot, seems to clock writes with 0073 writing  d0 / c0? (then writes 2 bytes here)
			   // is this sending song patterns to another CPU?
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		m_unkdata[m_unkdata_addr] = data;

		m_unkdata_addr++;
		m_unkdata_addr &= 0xfffff;
		break;

	case 0x5a: // (real address 78ca)  startup
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;

	default:
		logerror("%s: unkregs_w %04x %02x\n", machine().describe_context(), offset, data);
		break;
	}

}


void trkfldch_lexi_state::machine_start()
{
	trkfldch_state::machine_start();
	save_item(NAME(m_input_bit));
	save_item(NAME(m_iopos));
}

void trkfldch_lexi_state::machine_reset()
{
	trkfldch_state::machine_reset();
	m_input_bit = 0;
	m_iopos = 0;
}

uint8_t trkfldch_lexi_state::unkregs_r(offs_t offset)
{
	switch (offset)
	{

	case 0x00:
	{
		uint8_t ret = m_input_bit;
		logerror("%s: unkregs_r %04x (returning %02x) (Player 1 inputs)\n", machine().describe_context(), offset, ret);
		return ret;
	}

	default:
		return trkfldch_state::unkregs_r(offset);
	}
}


void trkfldch_lexi_state::unkregs_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 0x03:
		logerror("%s: unkregs_w %04x %02x (io strobe?)\n", machine().describe_context(), offset, data);
		if (data == 0x03)
		{
			m_iopos = 0;
		}
		else
		{
			m_iopos++;
		}

		m_input_bit = (m_extra->read() >> m_iopos) & 1;
		break;

	default:
		trkfldch_state::unkregs_w(offset, data);
		break;
	}
}

void trkfldch_state::machine_start()
{
	save_item(NAME(m_unkdata_addr));
	save_item(NAME(m_unkdata));

	for (int i = 0; i < 256; i++)
	{
		m_palette->set_pen_color(i, machine().rand(), machine().rand(), machine().rand());
	}

}

void trkfldch_state::machine_reset()
{
	m_which_vector = 0x06;

	for (int i = 0; i < 0x90; i++)
		m_unkregs[i] = 0x90;

	for (int i = 0; i < 0x100000; i++)
		m_unkdata[i] = 0;

	for (int j = 0; j < 2; j++)
		for (int i = 0; i < 0x12; i++)
			m_tmapscroll_window[j][i] = 0x00;

	// these don't always get initialized by the code, these appear to be reasonable default values based on what they get reset to by the games after first use
	for (int j = 0; j < 2; j++)
	{
		m_tmapscroll_window[j][0x00] = 0x00;
		m_tmapscroll_window[j][0x01] = 0x00;
		m_tmapscroll_window[j][0x02] = 0x1e;
		m_tmapscroll_window[j][0x03] = 0x1e;
		m_tmapscroll_window[j][0x04] = 0x00;
		m_tmapscroll_window[j][0x05] = 0x28;
	}

	for (int i = 0; i < 0xe; i++)
		m_dmaregs[i] = 0x00;

	for (int i = 0; i < 0xb; i++)
		m_modebank[i] = 0x00;

	for (int i = 0; i < 0x10; i++)
		m_sysregs[i] = 0x00;

	m_tilemapbase[0x00] = 0x00;
	m_tilemapbase[0x01] = 0x00;
	m_tilemapbase[0x02] = 0x00;


	m_unkdata_addr = 0;

	// the game code doesn't set the DMA step / skip params to default values until after it's used them with other values, so assume they reset to these
	m_dmaregs[0x07] = 0x01;
	m_dmaregs[0x08] = 0x00;
	m_dmaregs[0x09] = 0x00;

	m_dmaregs[0x0b] = 0x01;
	m_dmaregs[0x0c] = 0x00;
	m_dmaregs[0x0d] = 0x00;
}

void trkfldch_state::trkfldch(machine_config &config)
{
	/* basic machine hardware */
	G65816(config, m_maincpu, 20000000);
	//m_maincpu->set_addrmap(AS_DATA, &trkfldch_state::mem_map);
	m_maincpu->set_addrmap(AS_PROGRAM, &trkfldch_state::trkfldch_map);
	m_maincpu->set_addrmap(g65816_device::AS_VECTORS, &trkfldch_state::vectors_map);

	TIMER(config, "scantimer").configure_scanline(FUNC(trkfldch_state::scanline), "screen", 0, 1);

	/* 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(320, 240);
	m_screen->set_visarea(0, 320-1, 0, 240-1);
	m_screen->set_screen_update(FUNC(trkfldch_state::screen_update_trkfldch));
	m_screen->set_palette("palette");

	GFXDECODE(config, m_gfxdecode, "palette", gfx_trkfldch);

	PALETTE(config, m_palette, palette_device::BLACK, 256);
}

ROM_START( trkfldch )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "trackandfield.bin", 0x000000, 0x400000,  CRC(f4f1959d) SHA1(344dbfe8df1897adf77da6e5ca0435c4d47d6842) )
ROM_END

ROM_START( trkfldchj )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "trackandfieldjap.u2", 0x000000, 0x400000, CRC(2badbbae) SHA1(8102a6166bda3de2b13a984c391ad05e15fbf7ce) )
ROM_END


ROM_START( my1stddr )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "myfirstddr.bin", 0x000000, 0x400000, CRC(2ef57bfc) SHA1(9feea5adb9de8fe17e915f3a037e8ddd70e58ae7) )
ROM_END

ROM_START( abl4play )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "abl4play.bin", 0x000000, 0x800000, CRC(5d57fb70) SHA1(34cdf80dc8cb08e5cd98c724268e4c5f483780d7) )
ROM_END

ROM_START( shtscore )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "shootnscore.bin", 0x000000, 0x400000, CRC(37aa16bd) SHA1(609d0191301480c51ec1188c67101a4e88a5170f) )
ROM_END

ROM_START( lexitvsprt )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "29l3211.u2a", 0x000000, 0x400000, CRC(65e5223c) SHA1(13eae6e34100fb9761335e87a3cf728bb31e860f) )
ROM_END

ROM_START( senspid )
	ROM_REGION( 0x1000000, "maincpu", 0 )
	ROM_LOAD( "spidermanmat.bin", 0x00000, 0x400000, CRC(11f5181c) SHA1(7f0d5d34f924b6a7182102451a2ac1cc41e575b0) )
ROM_END


/*
Included with Orange model

    しあわせのおうじ (イギリス民話)
        Shiawase no Ouji (English minwa)
    ほしのぎんか (グリム童話)
        Hoshi no Ginka (Grimm douwa)
    おやゆびひめ (アンデルセン)
        Oyayubi-hime (Anderson)
    こびととくつや (グリム童話)
        Kobi to Toku Tsuya (Grimm douwa)
    みっつのねがい (日本昔話)
        Mittsu no Negai (Nippon mukashi banashi)
    うらしまたろう (日本昔話)
        Urashima Tarou (Nippon mukashi banashi)
    アフロンとがまじいの ちきゅうにやさしく (オリジナル)
        Afuron to Gamajii no Chikyuu ni Yasashiku (Original)
    かさこじぞう (日本昔話)
        Kasakojizo (Nippon mukashi banashi)
    きんのがちょう (グリム童話)
        Kin no Gachou (Grimm douwa)
    あそんでまなぼう (オリジナル)
        Asonde Manabou (Original)
*/
ROM_START( teleshi )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "s29gl128n90tfir.bin", 0x000000, 0x1000000, CRC(8c032142) SHA1(7dff151ea1abd5911e753f0708b0b4f66599791f) )
ROM_END

/*
Included with Purple model

    ももたろう (日本昔話)
        Momotarou (Nippon mukashi banashi)
    あかずきん (グリム童話)
        Aka Zukin (Grimm douwa)
    きたかぜのくれたテーブルかけ (ノルウェー民話)
        Kita Kaze no Kureta Table Kake (Norway minwa)
    いっすんぼうし (日本昔話)
        Issun-boushi (Nippon mukashi banashi)
    おおきなかぶ (ロシア民話)
        Ookina Kabu (Russia minwa)
    ありときりぎりす (イソップ童話)
        Ari to Kirigirisu (Aesop douwa)
    マッチうりのしょうじょ (アンデルセン)
        Match Uri no Shoujo (Anderson)
    かさこじぞう (日本昔話)
        Kasa Jizou (Nippon mukashi banashi)
    きんのがちょう (グリム童話)
        Kin no Gachou (Grimm douwa)
    あそんでまなぼう2 (オリジナル)
        Asonde Manabou 2 (Original)
*/
ROM_START( teleship )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "s29gl128n90tfir2.bin", 0x000000, 0x1000000, CRC(28c12a48) SHA1(68557849e2b6f3669de76540de841ca99119ec58) )
ROM_END

/*
The following were also available (via the Download service?)

世界のお話 (ナレーション入り) [Sekai no Ohanashi (Narration-iri)]

    ブレーメンのおんがくたい (グリム童話)
        Bremen no Ongakutai (Grimm douwa)
    みにくいあひるのこ (アンデルセン)
        Minikui Ahiru no Ko (Andersen)
    てぶくろ (ウクライナ民話)
        Tebukuro (Ukraine minwa)
    はちかつぎひめ (日本昔話)
        Hachi Katsugi-hime (Nippon mukashi banashi)

遊んで学ぼう・お歌で遊ぼう [Asonde Manabu - Outa de Asobou]

    あそんでまなぼう (オリジナル)
        Asonde Manabou (Original)
    あそんでまなぼう2 (オリジナル)
        Asonde Manabou 2 (Original)
    おうたであそぼう3 (オリジナル)
        Asonde Manabou 3 (Original)
    おうたであそぼう4 (オリジナル)
        Asonde Manabou 4 (Original)
*/


} // anonymous namespace


CONS( 2007, trkfldch,  0,          0,  trkfldch, trkfldch,trkfldch_state,      empty_init,    "Konami",                                     "Track & Field Challenge", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
// 走れ!とべ!投げろ! ハイパースポーツチャレンジ
CONS( 2007, trkfldchj, trkfldch,   0,  trkfldch, trkfldch,trkfldch_state,      empty_init,    "Konami",                                     "Hashire! Tobe! Nagero! Hyper Sports Challenge (Japan)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )

CONS( 2006, my1stddr,  0,          0,  trkfldch, my1stddr,trkfldch_state,      empty_init,    "Konami",                                     "My First Dance Dance Revolution (US)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // Japan version has different songs

CONS( 200?, abl4play,  0,          0,  trkfldch, abl4play,trkfldch_state,      empty_init,    "Advance Bright Ltd",                         "4 Player System - 10 in 1", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )

CONS( 200?, shtscore,  0,          0,  trkfldch, shtscore,trkfldch_state,      empty_init,    "Halsall / time4toys.com / Electronic Games", "Shoot n' Score", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )

CONS( 200?, lexitvsprt,0,          0,  trkfldch, lexi,    trkfldch_lexi_state, empty_init,    "Lexibook",                                   "TV Sports Plug & Play 5-in-1 (JG7000)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )

// don't have a picture of the box, title screen doesn't give a more complete title, I/O seems closer lexitvsprt
CONS( 2007, senspid,   0,          0,  trkfldch, trkfldch,trkfldch_state,      empty_init,    "Senario",                                    "The Amazing Spider-Man (Senario, floor mat)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )

// additional online content could be downloaded onto these if they were connected to a PC via USB
CONS( 2008, teleshi,   0,          0,  trkfldch, konsb,   trkfldch_state,      empty_init,    "Konami",                                     "Teleshibai (Japan)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // テレしばい - this one is orange
CONS( 2008, teleship,  0,          0,  trkfldch, konsb,   trkfldch_state,      empty_init,    "Konami",                                     "Teleshibai - Purple Version (Japan)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // テレしばい (パープルバージョン) - this has Purple Version as part of the name  on the box



trs80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Juergen Buchmueller, Robbbert
// Originally written in MESS 0.1 by Juergen Buchmueller.
// Substantially rewritten by Robbbert in 2008, many new clones added
/***************************************************************************
TRS80 memory map

0000-2fff ROM                            R   D0-D7
3000-37ff ROM on EACA models             R   D0-D7
          unused on Model I
37de      UART status                    R/W D0-D7
37df      UART data                      R/W D0-D7
37e0      interrupt latch address
37e1      select disk drive 0            W
37e2      cassette drive latch address   W
37e3      select disk drive 1            W
37e4      select which cassette unit     W   D0-D1 (D0 selects unit 1, D1 selects unit 2)
37e5      select disk drive 2            W
37e7      select disk drive 3            W
37e0-37e3 floppy motor                   W   D0-D3
          or floppy head select          W   D3
37e8      send a byte to printer         W   D0-D7
37e8      read printer status            R   D7
37ec-37ef FDC FD1771                     R/W D0-D7
37ec      command                        W   D0-D7
37ec      status                         R   D0-D7
37ed      track                          R/W D0-D7
37ee      sector                         R/W D0-D7
37ef      data                           R/W D0-D7
3800-38ff keyboard matrix                R   D0-D7
3900-3bff unused - kbd mirrored
3c00-3fff video RAM                      R/W D0-D5,D7 (or D0-D7)
4000-ffff RAM

Interrupts:
IRQ mode 1
NMI

Printer: Level II usually 37e8; System80 uses port FD.

System80 has non-addressable dip switches to set the UART control register.
System80 has non-addressable links to set the baud rate. Receive and Transmit clocks are tied together.

Cassette baud rates:    Model I level I - 250 baud
        Model I level II and all clones - 500 baud

I/O ports
FF:
- bits 0 and 1 are for writing a cassette
- bit 2 must be high to turn the cassette motor on, enables cassette data paths on a system-80
- bit 3 switches the display between 64 or 32 characters per line
- bit 6 remembers the 32/64 screen mode (inverted)
- bit 7 is for reading from a cassette

FE:
- bit 4 selects internal cassette player (low) or external unit (high) on a system-80

FD:
- Read printer status on a system-80
- Write to printer on a system-80

F9:
- UART data (write) status (read) on a system-80

F8:
- UART data (read) status (write) on a system-80

Shift and Right-arrow will enable 32 cpl, if the hardware allows it.

SYSTEM commands:
    - Press Break (End key) to quit
    - Press Enter to exit with error
    - xxxx to load program xxxx from tape.
    - / to execute last program loaded
    - /nnnnn to execute program at nnnnn (decimal)

About the system80 - Asian version of trs80l2, known as EACA Video Genie. In USA called
    PMC-80, in South Africa called TRZ-80, and Dick Smith imported them to Australia and
    New Zealand as the System 80. The Hungarian version is the ht1080z.
    Inbuilt extensions:
    - SYSTEM then /12288 = enable extended keyboard and flashing block cursor
    - SYSTEM then /12299 = turn cursor back to normal
    - SYSTEM then /12294 = enable extended keyboard only
    - SYSTEM then /12710 = enter machine-language monitor
    Monitor commands:
    - B : return to Basic
    - Dnnnn : Dump hex to screen. Press down-arrow for more. Press enter to quit.
    - Mnnnn : Modify memory. Enter new byte and it increments to next address. X to quit.
    - Gnnnn : Execute program at nnnn
    - Gnnnn,tttt : as above, breakpoint at tttt
    - R : modify registers

About the ht1080z - This was made for schools in Hungary. Each comes with a BASIC extension roms
    which activated Hungarian features. To activate - start emulation - enter SYSTEM
    Enter /12288 and the extensions will be installed and you are returned to READY.
    The ht1080z is identical to the System 80, apart from the character rom.
    The ht1080z2 has a modified extension rom and character generator.

About the eg3003 - This is the original of the EACA clones, and enjoyed success in Europe,
    particularly in Germany. The normal roms would make it exactly a System-80, however we've
    added the TCS ROM extension for something different. To activate - enter SYSTEM
    Enter /12345 and the inbuilt monitor will be ready to go. To start the monitor, hold
    up-arrow and hit M. You get a # prompt. The keyboard is also now in lower-case, even
    though monitor commands are required to be in upper-case. Monitor commands:
    - A : Ascii Dump
    - D : Hex dump
    - E : Edit Memory
    - H : Hex converter
    - J : Jump (Go)
    - P : Punch
    - R : Return to BASIC
    - S : Search
    - X : Hex Calculator

About the RTC - The time is incremented while ever the cursor is flashing. It is stored in a series
    of bytes in the computer's work area. The bytes are in a certain order, this is:
    seconds, minutes, hours, year, day, month. The seconds are stored at 0x4041.
    A reboot always sets the time to zero.

Not dumped (to our knowledge):
 TRS80 Japanese bios
 TRS80 Katakana Character Generator
 TRS80 Small English Character Generator
 TRS80 Model III old version Character Generator

Not emulated:
 TRS80 Japanese kana/ascii switch and alternate keyboard
 TRS80 Model III/4 Hard drive, Graphics board, Alternate Character set
 Radionic has 16 colours with a byte at 350B controlling the operation. See manual.


********************************************************************************************************

To Do / Status:
--------------

- For those machines that allow it, add cass2 as an image device and hook it up.
- Difficulty loading real tapes.
- Writing to floppy is problematic; freezing/crashing are common issues.

trs80:     works

trs80l2:   works
           expansion-box to be slotified

sys80:     works
           investigate expansion-box

ht1080z    works
           verify clock for AY-3-8910
           investigate expansion-box

*******************************************************************************************************/

#include "emu.h"
#include "trs80.h"

#include "trs80_quik.h"

#include "machine/input_merger.h"
#include "sound/ay8910.h"

#include "softlist_dev.h"

#include "formats/dmk_dsk.h"

#include "utf8.h"


void trs80_state::trs80_mem(address_map &map)
{
	map(0x0000, 0x0fff).rom();
	map(0x3800, 0x3bff).r(FUNC(trs80_state::keyboard_r));
	map(0x3c00, 0x3fff).ram().share(m_p_videoram);
	map(0x4000, 0x7fff).ram();
}

void trs80_state::trs80_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0xff, 0xff).rw(FUNC(trs80_state::port_ff_r), FUNC(trs80_state::port_ff_w));
}

void trs80_state::m1_mem(address_map &map)
{
	map(0x0000, 0x37ff).rom();
	map(0x37de, 0x37de).rw(FUNC(trs80_state::sys80_f9_r), FUNC(trs80_state::sys80_f8_w));
	map(0x37df, 0x37df).rw(m_uart, FUNC(ay31015_device::receive), FUNC(ay31015_device::transmit));
	map(0x37e0, 0x37e3).rw(FUNC(trs80_state::irq_status_r), FUNC(trs80_state::motor_w));
	map(0x37e4, 0x37e7).w(FUNC(trs80_state::cassunit_w));
	map(0x37e8, 0x37eb).rw(FUNC(trs80_state::printer_r), FUNC(trs80_state::printer_w));
	map(0x37ec, 0x37ef).rw(FUNC(trs80_state::fdc_r), FUNC(trs80_state::fdc_w));
	map(0x3800, 0x3bff).r(FUNC(trs80_state::keyboard_r));
	map(0x3c00, 0x3fff).ram().share(m_p_videoram);
	map(0x4000, 0xffff).ram();
}

void trs80_state::m1_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0xe8, 0xe8).rw(FUNC(trs80_state::port_e8_r), FUNC(trs80_state::port_e8_w));
	map(0xe9, 0xe9).portr("E9").w("brg", FUNC(com8116_device::stt_str_w));
	map(0xea, 0xea).rw(FUNC(trs80_state::port_ea_r), FUNC(trs80_state::port_ea_w));
	map(0xeb, 0xeb).rw(m_uart, FUNC(ay31015_device::receive), FUNC(ay31015_device::transmit));
	map(0xff, 0xff).rw(FUNC(trs80_state::port_ff_r), FUNC(trs80_state::port_ff_w));
}

void trs80_state::sys80_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0xf8, 0xf8).r(m_uart, FUNC(ay31015_device::receive)).w(FUNC(trs80_state::sys80_f8_w));
	map(0xf9, 0xf9).r(FUNC(trs80_state::sys80_f9_r)).w(m_uart, FUNC(ay31015_device::transmit));
	map(0xfd, 0xfd).rw(FUNC(trs80_state::printer_r), FUNC(trs80_state::printer_w));
	map(0xfe, 0xfe).w(FUNC(trs80_state::sys80_fe_w));
	map(0xff, 0xff).rw(FUNC(trs80_state::port_ff_r), FUNC(trs80_state::port_ff_w));
}

void trs80_state::ht1080z_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	sys80_io(map);
	map(0x1e, 0x1e).rw("ay1", FUNC(ay8910_device::data_r), FUNC(ay8910_device::data_w));
	map(0x1f, 0x1f).w("ay1", FUNC(ay8910_device::address_w));
}

/**************************************************************************
   w/o SHIFT                             with SHIFT
   +-------------------------------+     +-------------------------------+
   | 0   1   2   3   4   5   6   7 |     | 0   1   2   3   4   5   6   7 |
+--+---+---+---+---+---+---+---+---+  +--+---+---+---+---+---+---+---+---+
|0 | @ | A | B | C | D | E | F | G |  |0 | ` | a | b | c | d | e | f | g |
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|1 | H | I | J | K | L | M | N | O |  |1 | h | i | j | k | l | m | n | o |
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|2 | P | Q | R | S | T | U | V | W |  |2 | p | q | r | s | t | u | v | w |
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|3 | X | Y | Z | [ | \ | ] | ^ | _ |  |3 | x | y | z | { | | | } | ~ |   |
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|4 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |  |4 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|5 | 8 | 9 | : | ; | , | - | . | / |  |5 | 8 | 9 | * | + | < | = | > | ? |
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|6 |ENT|CLR|BRK|UP |DN |LFT|RGT|SPC|  |6 |ENT|CLR|BRK|UP |DN |LFT|RGT|SPC|
|  +---+---+---+---+---+---+---+---+  |  +---+---+---+---+---+---+---+---+
|7 |SHF|   |   |   |   |   |   |   |  |7 |SHF|   |   |   |   |   |   |   |
+--+---+---+---+---+---+---+---+---+  +--+---+---+---+---+---+---+---+---+

***************************************************************************/

static INPUT_PORTS_START( trs80 )
	PORT_START("LINE0")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('@')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)          PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)          PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)          PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)          PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)          PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)          PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)          PORT_CHAR('G') PORT_CHAR('g')

	PORT_START("LINE1")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)          PORT_CHAR('H') PORT_CHAR('h')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)          PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)          PORT_CHAR('J') PORT_CHAR('j')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)          PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)          PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)          PORT_CHAR('M') PORT_CHAR('m')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)          PORT_CHAR('N') PORT_CHAR('n')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)          PORT_CHAR('O') PORT_CHAR('o')

	PORT_START("LINE2")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)          PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)          PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)          PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)          PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)          PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)          PORT_CHAR('U') PORT_CHAR('u')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)          PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)          PORT_CHAR('W') PORT_CHAR('w')

	PORT_START("LINE3")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)          PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)          PORT_CHAR('Y') PORT_CHAR('y')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)          PORT_CHAR('Z') PORT_CHAR('z')
	// These keys produce output on all systems, the shift key having no effect. They display arrow symbols and underscore.
	// On original TRS80, shift cancels all keys except F3 which becomes backspace.
	// PORT_CHAR('_') is correct for all systems.
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_F2)   // sys80 mkII: F2
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_F3)   // sys80 mkII: F3
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_F4)  // sys80 mkII: F4
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("_") PORT_CODE(KEYCODE_F5)         PORT_CHAR('_')  // sys80 mkII: F1

	PORT_START("LINE4") // Number pad: System 80 Mk II only
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD)          PORT_CHAR('0')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)          PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)          PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("LINE5")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME(": *") PORT_CODE(KEYCODE_MINUS)    PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("; +") PORT_CODE(KEYCODE_COLON)    PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA)    PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("- =") PORT_CODE(KEYCODE_EQUALS)   PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP)     PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH)    PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("LINE6")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)      PORT_CHAR(13)
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("Clear") PORT_CODE(KEYCODE_HOME)       PORT_CHAR(UCHAR_MAMEKEY(F8))   // Missing from early System 80
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_END)        PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)        PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	/* backspace do the same as cursor left */
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_BACKSPACE)   PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))   // Missing from early System 80
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')

	PORT_START("LINE7")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0xfe, 0x00, IPT_UNUSED)

	PORT_START("RESET") // special button
	PORT_BIT(0x01, 0x00, IPT_OTHER) PORT_NAME("Reset") PORT_CODE(KEYCODE_DEL) PORT_WRITE_LINE_DEVICE_MEMBER("nmigate", FUNC(input_merger_device::in_w<0>))
INPUT_PORTS_END

static INPUT_PORTS_START(trs80l2)
	PORT_INCLUDE (trs80)

	PORT_START("CONFIG")
	PORT_CONFNAME(    0x80, 0x00,   "Floppy Disc Drives")
	PORT_CONFSETTING(   0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(   0x80, DEF_STR( On ) )
	PORT_BIT(0x7f, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("E9")    // these are the power-on uart settings
	PORT_BIT(0x07, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_DIPNAME( 0x88, 0x08, "Parity")
	PORT_DIPSETTING(    0x08, DEF_STR(None))
	PORT_DIPSETTING(    0x00, "Odd")
	PORT_DIPSETTING(    0x80, "Even")
	PORT_DIPNAME( 0x10, 0x10, "Stop Bits")
	PORT_DIPSETTING(    0x10, "2")
	PORT_DIPSETTING(    0x00, "1")
	PORT_DIPNAME( 0x60, 0x60, "Bits")
	PORT_DIPSETTING(    0x00, "5")
	PORT_DIPSETTING(    0x20, "6")
	PORT_DIPSETTING(    0x40, "7")
	PORT_DIPSETTING(    0x60, "8")
INPUT_PORTS_END

static INPUT_PORTS_START(sys80)
	PORT_INCLUDE (trs80l2)

	PORT_MODIFY("CONFIG")
	PORT_CONFNAME(    0x08, 0x00,   "Video Cut")  // Toggle switch on the back
	PORT_CONFSETTING(   0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(   0x08, DEF_STR( On ) )
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("Page") PORT_CODE(KEYCODE_F6) PORT_TOGGLE  // extra keys above the main keyboard
	//PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F7) PORT_TOGGLE  // this turns on the tape motor

	PORT_START("BAUD")
	PORT_DIPNAME( 0xff, 0x06, "Baud Rate")
	PORT_DIPSETTING(    0x00, "110")
	PORT_DIPSETTING(    0x01, "300")
	PORT_DIPSETTING(    0x02, "600")
	PORT_DIPSETTING(    0x03, "1200")
	PORT_DIPSETTING(    0x04, "2400")
	PORT_DIPSETTING(    0x05, "4800")
	PORT_DIPSETTING(    0x06, "9600")
	PORT_DIPSETTING(    0x07, "19200")
INPUT_PORTS_END



/**************************** F4 CHARACTER DISPLAYER ***********************************************************/
static const gfx_layout trs80_charlayout =
{
	8, 8,           /* 8 x 8 characters */
	128,            /* 128 characters */
	1,          /* 1 bits per pixel */
	{ 0 },          /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{  0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8        /* every char takes 8 bytes */
};

static const gfx_layout ht1080z_charlayout =
{
	5, 12,          /* 5 x 12 characters */
	128,            /* 128 characters */
	1,          /* 1 bits per pixel */
	{ 0 },          /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{  0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16           /* every char takes 16 bytes */
};

static GFXDECODE_START(gfx_trs80)
	GFXDECODE_ENTRY( "chargen", 0, trs80_charlayout, 0, 1 )
GFXDECODE_END

static GFXDECODE_START(gfx_ht1080z)
	GFXDECODE_ENTRY( "chargen", 0, ht1080z_charlayout, 0, 1 )
GFXDECODE_END


void trs80_state::floppy_formats(format_registration &fr)
{
	fr.add(FLOPPY_DMK_FORMAT);
	fr.add(FLOPPY_JV1_FORMAT);
}

// Most images are single-sided, 40 tracks or less.
// However, the default is QD to prevent MAME from
// crashing if a disk with more than 40 tracks is used.
static void trs80_floppies(device_slot_interface &device)
{
	device.option_add("35t_sd", FLOPPY_525_SSSD_35T);
	device.option_add("40t_sd", FLOPPY_525_SSSD);
	device.option_add("40t_dd", FLOPPY_525_DD);
	device.option_add("80t_qd", FLOPPY_525_QD);
}


void trs80_state::level1(machine_config &config)      // the original model I, level I, with no extras
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 10.6445_MHz_XTAL / 6);
	m_maincpu->set_addrmap(AS_PROGRAM, &trs80_state::trs80_mem);
	m_maincpu->set_addrmap(AS_IO, &trs80_state::trs80_io);
	m_maincpu->halt_cb().set("nmigate", FUNC(input_merger_device::in_w<1>));

	input_merger_device &nmigate(INPUT_MERGER_ANY_HIGH(config, "nmigate"));
	nmigate.output_handler().set_inputline(m_maincpu, INPUT_LINE_NMI); // TODO: also causes SYSRES on expansion bus

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(10.6445_MHz_XTAL, 672, 0, 384, 264, 0, 192);
	screen.set_screen_update(FUNC(trs80_state::screen_update_trs80));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_trs80);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	/* devices */
	CASSETTE(config, m_cassette);
	m_cassette->set_formats(trs80l1_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("trs80_cass");

	/* software lists */
	SOFTWARE_LIST(config, "cass_list").set_original("trs80_cass").set_filter("0");
}

void trs80_state::level2(machine_config &config)      // model I, level II
{
	level1(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &trs80_state::m1_mem);
	m_maincpu->set_addrmap(AS_IO, &trs80_state::m1_io);
	m_maincpu->set_periodic_int(FUNC(trs80_state::rtc_interrupt), attotime::from_hz(40));

	/* devices */
	m_cassette->set_formats(trs80l2_cassette_formats);

	TRS80_QUICKLOAD(config, "quickload", m_maincpu, attotime::from_seconds(1));

	FD1771(config, m_fdc, 4_MHz_XTAL / 4);
	m_fdc->intrq_wr_callback().set(FUNC(trs80_state::intrq_w));

	FLOPPY_CONNECTOR(config, m_floppy[0], trs80_floppies, "80t_qd", trs80_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], trs80_floppies, "80t_qd", trs80_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[2], trs80_floppies, nullptr, trs80_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[3], trs80_floppies, nullptr, trs80_state::floppy_formats).enable_sound(true);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit7));
	m_centronics->perror_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit6));
	m_centronics->select_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit5));
	m_centronics->fault_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit4));

	INPUT_BUFFER(config, m_cent_status_in);

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	com8116_device &brg(COM8116(config, "brg", 5.0688_MHz_XTAL));   // BR1941L
	brg.fr_handler().set(m_uart, FUNC(ay31015_device::write_rcp));
	brg.ft_handler().set(m_uart, FUNC(ay31015_device::write_tcp));

	AY31015(config, m_uart);
	m_uart->read_si_callback().set("rs232", FUNC(rs232_port_device::rxd_r));
	m_uart->write_so_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	//MCFG_AY31015_WRITE_DAV_CB(WRITELINE( , , ))
	m_uart->set_auto_rdav(true);
	RS232_PORT(config, "rs232", default_rs232_devices, nullptr);

	SOFTWARE_LIST(config.replace(), "cass_list").set_original("trs80_cass").set_filter("1");
	SOFTWARE_LIST(config, "quik_list").set_original("trs80_quik").set_filter("1");
	SOFTWARE_LIST(config, "flop_list").set_original("trs80_flop").set_filter("1");
}

void trs80_state::sys80(machine_config &config)
{
	level2(config);
	m_maincpu->set_addrmap(AS_IO, &trs80_state::sys80_io);
	m_maincpu->halt_cb().set_nop(); // TODO: asserts HLTA on expansion bus instead

	subdevice<screen_device>("screen")->set_screen_update(FUNC(trs80_state::screen_update_sys80));

	config.device_remove("brg");
	CLOCK(config, m_uart_clock, 19200 * 16);
	m_uart_clock->signal_handler().set(m_uart, FUNC(ay31015_device::write_rcp));
	m_uart_clock->signal_handler().append(m_uart, FUNC(ay31015_device::write_tcp));
}

void trs80_state::sys80p(machine_config &config)
{
	sys80(config);
	m_maincpu->set_clock(10.48_MHz_XTAL / 6);
	subdevice<screen_device>("screen")->set_raw(10.48_MHz_XTAL, 672, 0, 384, 312, 0, 192);
}

void trs80_state::ht1080z(machine_config &config)
{
	sys80p(config);
	m_maincpu->set_addrmap(AS_IO, &trs80_state::ht1080z_io);

	subdevice<screen_device>("screen")->set_screen_update(FUNC(trs80_state::screen_update_ht1080z));
	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_ht1080z);

	AY8910(config, "ay1", 1'500'000).add_route(ALL_OUTPUTS, "mono", 0.25); // guess of clock
	//ay1.port_a_read_callback(FUNC(trs80_state::...);  // ports are some kind of expansion slot
	//ay1.port_b_read_callback(FUNC(trs80_state::...);

	SOFTWARE_LIST(config.replace(), "cass_list").set_original("trs80_cass").set_filter("H");
	SOFTWARE_LIST(config.replace(), "quik_list").set_original("trs80_quik").set_filter("H");
}


/***************************************************************************

  Game driver(s)

***************************************************************************/

ROM_START(trs80)
	ROM_REGION(0x3800, "maincpu", ROMREGION_ERASEFF)
	// These roms had many names due to multiple suppliers
	// Memory   Location    Maker and type                Label
	// 000-7FF  Z33         Intel 2716                    ROM-A
	// 800-FFF  Z34         Intel 2716                    ROM-B
	// 000-7FF  Z33         National Semiconductor 2316   MM2316_R/D
	// 800-FFF  Z34         National Semiconductor 2316   MM2316_S/D
	// 000-7FF  Z33         National Semiconductor 2316   M2316E_R/N
	// 800-FFF  Z34         National Semiconductor 2316   M2316E_S/N
	// 000-7FF  Z33         Motorola                      7807
	// 800-FFF  Z34         Motorola                      7804
	// 000-FFF  Z33         Motorola                      7809_BASIC I
	ROM_LOAD("level1.rom",     0x0000, 0x1000, CRC(70d06dff) SHA1(20d75478fbf42214381e05b14f57072f3970f765) )

	ROM_REGION(0x0400, "chargen", 0)
	ROM_LOAD("mcm6670p.z29",   0x0000, 0x0400, CRC(0033f2b9) SHA1(0d2cd4197d54e2e872b515bbfdaa98efe502eda7) )
ROM_END


ROM_START(trs80l2)
	ROM_REGION(0x3800, "maincpu", ROMREGION_ERASEFF)
	// There's no space for these roms on a Model 1 board, so an extra ROM board was created to hold them.
	// This board plugs into either Z33 or Z34. Confusingly, the locations on this board are also Z numbers.
	// The last version of the board only holds 2 roms - Z1 as 8K (ROM A/B), and Z2 as 4K (ROM C).
	ROM_SYSTEM_BIOS(0, "level2", "Radio Shack Level II Basic")
	ROMX_LOAD("rom-a.z1",      0x0000, 0x1000, CRC(37c59db2) SHA1(e8f8f6a4460a6f6755873580be6ff70cebe14969), ROM_BIOS(0) )
	ROMX_LOAD("rom-b.z2",      0x1000, 0x1000, CRC(05818718) SHA1(43c538ca77623af6417474ca5b95fb94205500c1), ROM_BIOS(0) )
	ROMX_LOAD("rom-c.z3",      0x2000, 0x1000, CRC(306e5d66) SHA1(1e1abcfb5b02d4567cf6a81ffc35318723442369), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "rsl2", "R/S L2 Basic")
	ROMX_LOAD("rom-a_alt.z1",  0x0000, 0x1000, CRC(be46faf5) SHA1(0e63fc11e207bfd5288118be5d263e7428cc128b), ROM_BIOS(1) )
	ROMX_LOAD("rom-b_alt.z2",  0x1000, 0x1000, CRC(6c791c2d) SHA1(2a38e0a248f6619d38f1a108eea7b95761cf2aee), ROM_BIOS(1) )
	ROMX_LOAD("rom-c_alt.z3",  0x2000, 0x1000, CRC(55b3ad13) SHA1(6279f6a68f927ea8628458b278616736f0b3c339), ROM_BIOS(1) )

	ROM_REGION(0x0400, "chargen", 0)
	ROM_LOAD("mcm6670p.z29",   0x0000, 0x0400, CRC(0033f2b9) SHA1(0d2cd4197d54e2e872b515bbfdaa98efe502eda7) )
ROM_END


// From here are EACA-made clones

ROM_START(eg3003)
	ROM_REGION(0x3800, "maincpu", 0)
	ROM_LOAD("3001.z10",       0x0000, 0x1000, CRC(8f5214de) SHA1(d8c052be5a2d0ec74433043684791d0554bf203b) )
	ROM_LOAD("3002.z11",       0x1000, 0x1000, CRC(46e88fbf) SHA1(a3ca32757f269e09316e1e91ba1502774e2f5155) )
	ROM_LOAD("3003.z12",       0x2000, 0x1000, CRC(306e5d66) SHA1(1e1abcfb5b02d4567cf6a81ffc35318723442369) )
	ROM_LOAD("tcs-ext.z13",    0x3000, 0x0800, CRC(8f2ac112) SHA1(be0c2a5fb9cb01173c4da6dc8c71ca5975f441bb) )

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD("tcs-ext.z25",    0x0000, 0x0800, CRC(150c5f1f) SHA1(afbce73ab0360108b32e75eb75a3966eb5c503e7) )
ROM_END


ROM_START(sys80)
	ROM_REGION(0x3800, "maincpu", 0)
	ROM_LOAD("3001.z10",       0x0000, 0x1000, CRC(8f5214de) SHA1(d8c052be5a2d0ec74433043684791d0554bf203b) )
	ROM_LOAD("3002.z11",       0x1000, 0x1000, CRC(46e88fbf) SHA1(a3ca32757f269e09316e1e91ba1502774e2f5155) )
	ROM_LOAD("3003.z12",       0x2000, 0x1000, CRC(306e5d66) SHA1(1e1abcfb5b02d4567cf6a81ffc35318723442369) )
	/* This rom turns the system80 into the "blue label" version. SYSTEM then /12288 to activate. */
	ROM_LOAD("sys80.z13",      0x3000, 0x0800, CRC(2a851e33) SHA1(dad21ec60973eb66e499fe0ecbd469118826a715) )

	ROM_REGION(0x0400, "chargen", 0)
	// Z25 could be 2513 (early version) or 52116 (later version)
	// This rom is Z25 on the video board, not Z25 on the CPU board.
	ROM_LOAD("2513.z25",       0x0000, 0x0400, CRC(0033f2b9) SHA1(0d2cd4197d54e2e872b515bbfdaa98efe502eda7) )
ROM_END

#define rom_sys80p rom_sys80

// Although I don't have schematics for the HT-series, it would be reasonable to expect the board locations to be the same
ROM_START(ht1080z)
	ROM_REGION(0x3800, "maincpu", 0)
	ROM_LOAD("3001.z10",       0x0000, 0x1000, CRC(8f5214de) SHA1(d8c052be5a2d0ec74433043684791d0554bf203b) )
	ROM_LOAD("3002.z11",       0x1000, 0x1000, CRC(46e88fbf) SHA1(a3ca32757f269e09316e1e91ba1502774e2f5155) )
	ROM_LOAD("3003.z12",       0x2000, 0x1000, CRC(306e5d66) SHA1(1e1abcfb5b02d4567cf6a81ffc35318723442369) )
	ROM_LOAD("sys80.z13",      0x3000, 0x0800, CRC(2a851e33) SHA1(dad21ec60973eb66e499fe0ecbd469118826a715) )

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD("ht1080z.z25",    0x0000, 0x0800, CRC(e8c59d4f) SHA1(a15f30a543e53d3e30927a2e5b766fcf80f0ae31) )
ROM_END


ROM_START(ht1080z2)
	ROM_REGION(0x3800, "maincpu", 0)
	ROM_LOAD("3001.z10",       0x0000, 0x1000, CRC(8f5214de) SHA1(d8c052be5a2d0ec74433043684791d0554bf203b) )
	ROM_LOAD("3002.z11",       0x1000, 0x1000, CRC(46e88fbf) SHA1(a3ca32757f269e09316e1e91ba1502774e2f5155) )
	ROM_LOAD("3003.z12",       0x2000, 0x1000, CRC(306e5d66) SHA1(1e1abcfb5b02d4567cf6a81ffc35318723442369) )
	ROM_LOAD("ht1080z2.z13",   0x3000, 0x0800, CRC(07415ac6) SHA1(b08746b187946e78c4971295c0aefc4e3de97115) )

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD("ht1080z2.z25",   0x0000, 0x0800, CRC(6728f0ab) SHA1(1ba949f8596f1976546f99a3fdcd3beb7aded2c5) )
ROM_END


ROM_START(ht108064)
	ROM_REGION(0x3800, "maincpu", 0)
	ROM_LOAD("3001_64.z10",    0x0000, 0x1000, CRC(59ec132e) SHA1(232c04827e494ea49931d7ab9a5b87b76c81aef1) )
	ROM_LOAD("3002_64.z11",    0x1000, 0x1000, CRC(a7a73e8c) SHA1(6e0f232b8666744328853cef6bb72b8e44b4c184) )
	ROM_LOAD("3003.z12",       0x2000, 0x1000, CRC(306e5d66) SHA1(1e1abcfb5b02d4567cf6a81ffc35318723442369) )
	ROM_LOAD("ht108064.z13",   0x3000, 0x0800, CRC(fc12bd28) SHA1(0da93a311f99ec7a1e77486afe800a937778e73b) )

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD("ht108064.z25",   0x0000, 0x0800, CRC(e76b73a4) SHA1(6361ee9667bf59d50059d09b0baf8672fdb2e8af) )
ROM_END


void trs80_state::init_trs80l2()
{
	m_7bit = true;
}


//    YEAR  NAME         PARENT    COMPAT  MACHINE   INPUT    CLASS        INIT           COMPANY                        FULLNAME                           FLAGS
COMP( 1977, trs80,       0,        0,       level1,   trs80,   trs80_state, empty_init,    "Tandy Radio Shack",           "TRS-80 Model I (Level I Basic)",  MACHINE_SUPPORTS_SAVE )
COMP( 1978, trs80l2,     0,        0,       level2,   trs80l2, trs80_state, init_trs80l2,  "Tandy Radio Shack",           "TRS-80 Model I (Level II Basic)", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1980, eg3003,      0,        trs80l2, sys80,    sys80,   trs80_state, init_trs80l2,  "EACA Computers Ltd",          "Video Genie EG3003",              MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1980, sys80,       eg3003,   0,       sys80,    sys80,   trs80_state, init_trs80l2,  "EACA Computers Ltd",          "System-80 (60 Hz)",               MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1980, sys80p,      eg3003,   0,       sys80p,   sys80,   trs80_state, init_trs80l2,  "EACA Computers Ltd",          "System-80 (50 Hz)",               MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1983, ht1080z,     eg3003,   0,       ht1080z,  sys80,   trs80_state, init_trs80l2,  "Hiradastechnika Szovetkezet", "HT-1080Z Series I",               MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1984, ht1080z2,    eg3003,   0,       ht1080z,  sys80,   trs80_state, init_trs80l2,  "Hiradastechnika Szovetkezet", "HT-1080Z Series II",              MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1985, ht108064,    eg3003,   0,       ht1080z,  sys80,   trs80_state, empty_init,    "Hiradastechnika Szovetkezet", "HT-1080Z/64",                     MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



trs80dt1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Robbbert
/**********************************************************************************

TRS-80 DT-1

Tandy's Data Terminal.

Skeleton driver commenced on 2017-10-25.

Core bugs noted:
- If region() used to locate the main rom in another region, validation
  complains that region ':maincpu' not found.
- If region 'maincpu' changed to 0x1000 (same size as the rom), a fatal error
  of duplicate save state occurs at start.


ToDo:
- Serial printer + (P1.1, P1.2, P3.5)
- Fix cpu bug with timer interrupt and then remove hack.
- Check the existing serial comms and LPTR that polarities are correct.

You can get into the setup menu by pressing Ctrl+Shift+Enter.

Note: The printer and serial interfaces are cheap and nasty. Connecting things
      up wrongly will lead to malfunction. Read the user manual for more info.

The LPTR (parallel printer) works by sending everything on the databus to the
printer, then asserting STROBE only for data that needs to print. The address-map
mechanism can't really handle this, but after investigation, it turns out that
the printer data goes to B800 which is a spare address range in the real machine.

**********************************************************************************/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "video/i8275.h"
#include "machine/7474.h"
#include "machine/x2212.h"
#include "sound/beep.h"
#include "bus/rs232/rs232.h"
#include "bus/centronics/ctronics.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class trs80dt1_state : public driver_device
{
public:
	trs80dt1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_p_videoram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
		, m_maincpu(*this, "maincpu")
		, m_palette(*this, "palette")
		, m_crtc(*this, "crtc")
		, m_nvram(*this,"nvram")
		, m_io_keyboard(*this, "X%u", 0U)
		, m_buzzer(*this, "buzzer")
		, m_7474(*this, "7474")
		, m_rs232(*this, "rs232")
		, m_centronics(*this, "centronics")
	{ }

	void trs80dt1(machine_config &config);

private:
	u8 dma_r(offs_t offset);
	u8 key_r(offs_t offset);
	u8 port1_r();
	u8 port3_r();
	void store_w(u8 data);
	void port1_w(u8 data);
	void port3_w(u8 data);
	void rx_w(int state);
	I8275_DRAW_CHARACTER_MEMBER(crtc_update_row);

	void io_map(address_map &map) ATTR_COLD;
	void prg_map(address_map &map) ATTR_COLD;

	bool m_bow = false;
	bool m_cent_busy = false;
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	required_shared_ptr<u8> m_p_videoram;
	required_region_ptr<u8> m_p_chargen;
	required_device<i8051_device> m_maincpu;
	required_device<palette_device> m_palette;
	required_device<i8276_device> m_crtc;
	required_device<x2210_device> m_nvram;
	required_ioport_array<9> m_io_keyboard;
	required_device<beep_device> m_buzzer;
	required_device<ttl7474_device> m_7474;
	required_device<rs232_port_device> m_rs232;
	required_device<centronics_device> m_centronics;

	u8 m_port3;
};

void trs80dt1_state::machine_reset()
{
	m_bow = 0;
	m_7474->preset_w(1);
	// line is active low in the real chip
	m_nvram->recall(1);
	m_nvram->recall(0);
	m_port3 = 0;
}

u8 trs80dt1_state::dma_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		m_crtc->dack_w(m_p_videoram[offset]); // write to /BS pin
	return 0x7f;
}

u8 trs80dt1_state::key_r(offs_t offset)
{
	offset &= 15;
	if (offset < 9)
		return m_io_keyboard[offset]->read();
	else
		return 0xff;
}

void trs80dt1_state::store_w(u8 data)
{
	// line is active low in the real chip
	m_nvram->store(1);
	m_nvram->store(0);
}

u8 trs80dt1_state::port1_r()
{
	u8 data = m_cent_busy << 6;
	return data;
}

/*
d0 : /PSTRB (centronics strobe)
d1 : TRPRT (for serial printer)
d2 : /SP BUSY (for serial printer)
d3 : /RTS
d4 : BOW (applies reverse video to entire screen)
d5 : /DTR
d6 : PP BUSY (parallel printer busy - input)
d7 : n/c */
void trs80dt1_state::port1_w(u8 data)
{
	m_centronics->write_strobe(BIT(data, 0));
	m_bow = BIT(data, 4);
	m_rs232->write_dtr(BIT(data, 5));
	m_rs232->write_rts(BIT(data, 3));
}

/*
d4 : beeper
d5 : Printer enable */
void trs80dt1_state::port3_w(u8 data)
{
	m_rs232->write_txd(BIT(data, 1));
	m_buzzer->set_state(BIT(data, 4));

	m_port3 = (m_port3 & 1) | (data & ~1);
}

u8 trs80dt1_state::port3_r()
{
	return m_port3;
}

void trs80dt1_state::rx_w(int state)
{
	if (state)
		m_port3 |= 1;
	else
		m_port3 &= ~1;
}

void trs80dt1_state::prg_map(address_map &map)
{
	map(0x0000, 0x0fff).rom();
	map(0x2000, 0x27ff).r(FUNC(trs80dt1_state::dma_r));
}

void trs80dt1_state::io_map(address_map &map)
{
	map.global_mask(0xbfff); // A14 not used
	map(0xa000, 0xa7ff).ram().share("videoram");
	map(0xa800, 0xa83f).mirror(0x3c0).rw(m_nvram, FUNC(x2210_device::read), FUNC(x2210_device::write)); // X2210
	map(0xac00, 0xafff).r(FUNC(trs80dt1_state::key_r));
	map(0xb000, 0xb3ff).portr("X9");
	map(0xb400, 0xb7ff).w(FUNC(trs80dt1_state::store_w));
	map(0xb800, 0xbbff).w("cent_data_out", FUNC(output_latch_device::write));
	map(0xbc00, 0xbc01).mirror(0x3fe).rw(m_crtc, FUNC(i8276_device::read), FUNC(i8276_device::write)); // i8276
}

/* Input ports */
static INPUT_PORTS_START( trs80dt1 )
	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL) PORT_CHAR(0x7f)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR(23)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR(11)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(32)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8') PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9') PORT_CHAR(UCHAR_MAMEKEY(F2))

	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BS") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(5)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') PORT_CHAR(17)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR(12)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(26)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7') PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4') PORT_CHAR(UCHAR_MAMEKEY(LEFT))

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR(18)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR(1)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(24)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5') PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6') PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(20)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR(19)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('\"')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1') PORT_CHAR('\\')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2') PORT_CHAR(UCHAR_MAMEKEY(DOWN))

	PORT_START("X4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR(25)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR(4)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR(22)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3') PORT_CHAR('~')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0') PORT_CHAR('`')

	PORT_START("X5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_END) PORT_CHAR(3)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(21)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR(6)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR(2)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR('.') PORT_CHAR('|')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD)

	PORT_START("X6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Clear") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR(7)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Linefeed") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(10)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(14)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('}') PORT_CHAR(']')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(15)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('{') PORT_CHAR('[')
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR(16)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RCONTROL) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED) // Jumper - LOW for 60Hz, high for 50Hz
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED) // No Connect
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("rs232", FUNC(rs232_port_device::dcd_r))
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("rs232", FUNC(rs232_port_device::cts_r))
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("rs232", FUNC(rs232_port_device::dsr_r))
INPUT_PORTS_END

void trs80dt1_state::machine_start()
{
	m_palette->set_pen_color(0, rgb_t(0x00,0x00,0x00)); // black
	m_palette->set_pen_color(1, rgb_t(0x00,0xa0,0x00)); // normal
	m_palette->set_pen_color(2, rgb_t(0x00,0xff,0x00)); // highlight

	save_item(NAME(m_bow));
	save_item(NAME(m_cent_busy));
}

const gfx_layout trs80dt1_charlayout =
{
	8, 8,             /* 8x16 characters - the last 8 lines are always blank */
	128,                /* 128 characters */
	1,              /* 1 bits per pixel */
	{0},                /* no bitplanes; 1 bit per pixel */
	{0,1,2,3,4,5,6,7},
	{0, 8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*16                /* space between characters */
};

static GFXDECODE_START( gfx_trs80dt1 )
	GFXDECODE_ENTRY( "chargen", 0x0000, trs80dt1_charlayout, 0, 1 )
GFXDECODE_END


I8275_DRAW_CHARACTER_MEMBER( trs80dt1_state::crtc_update_row )
{
	charcode &= 0x7f;
	linecount &= 15;

	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u8 gfx = 0;

	using namespace i8275_attributes;
	if (BIT(attrcode, LTEN)) // underline attr
		gfx = 0xff;
	else if (BIT(attrcode, GPA0, 2) == 0 && !BIT(attrcode, VSP)) // blinking and invisible attributes
		gfx = m_p_chargen[linecount | (charcode << 4)];

	if (BIT(attrcode, RVV)) // reverse video attr
		gfx ^= 0xff;

	if (m_bow) // black-on-white
		gfx ^= 0xff;

	bool hlgt = BIT(attrcode, HLGT);
	for(u8 i=0; i<8; i++)
		bitmap.pix(y, x + i) = palette[BIT(gfx, 7-i) ? (hlgt ? 2 : 1) : 0];
}


void trs80dt1_state::trs80dt1(machine_config &config)
{
	/* basic machine hardware */
	I8051(config, m_maincpu, 7.3728_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &trs80dt1_state::prg_map);
	m_maincpu->set_addrmap(AS_IO, &trs80dt1_state::io_map);
	m_maincpu->port_out_cb<1>().set(FUNC(trs80dt1_state::port1_w));
	m_maincpu->port_in_cb<1>().set(FUNC(trs80dt1_state::port1_r));
	m_maincpu->port_out_cb<3>().set(FUNC(trs80dt1_state::port3_w));
	m_maincpu->port_in_cb<3>().set(FUNC(trs80dt1_state::port3_r));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_screen_update("crtc", FUNC(i8276_device::screen_update));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(40*12, 16*16);
	screen.set_visarea(0, 40*12-1, 0, 16*16-1);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_trs80dt1);

	I8276(config, m_crtc, 12.48_MHz_XTAL / 8);
	m_crtc->set_character_width(8);
	m_crtc->set_display_callback(FUNC(trs80dt1_state::crtc_update_row));
	m_crtc->drq_wr_callback().set_inputline(m_maincpu, MCS51_INT0_LINE); // BRDY pin goes through inverter to /INT0, so we don't invert
	m_crtc->irq_wr_callback().set(m_7474, FUNC(ttl7474_device::clear_w)); // INT pin
	m_crtc->irq_wr_callback().append(m_7474, FUNC(ttl7474_device::d_w));
	m_crtc->vrtc_wr_callback().set(m_7474, FUNC(ttl7474_device::clock_w));
	m_crtc->set_screen("screen");

	PALETTE(config, "palette").set_entries(3);

	X2210(config, "nvram");

	TTL7474(config, m_7474, 0);
	m_7474->comp_output_cb().set_inputline(m_maincpu, MCS51_INT1_LINE).invert(); // /Q connects directly to /INT1, so we need to invert

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_buzzer, 2000).add_route(ALL_OUTPUTS, "mono", 0.50);

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set(FUNC(trs80dt1_state::rx_w));

	/* printer */
	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set([this] (bool state) { m_cent_busy = state; });

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);
}

ROM_START( trs80dt1 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "trs80dt1.u12", 0x0000, 0x1000, CRC(04e8a53f) SHA1(7b5d5047319ef8f230b82684d97a918b564d466e) )
	ROM_FILL(0x9a,1,0xd4) // fix for timer0 problem

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "8045716.u8",   0x0000, 0x0800, CRC(e2c5e59b) SHA1(0d571888d5f9fea4e565486ea8d3af8998ca46b1) )
ROM_END

} // anonymous namespace

COMP( 1982, trs80dt1, 0, 0, trs80dt1, trs80dt1, trs80dt1_state, empty_init, "Radio Shack", "TRS-80 DT-1 Data Terminal", MACHINE_SUPPORTS_SAVE )



trs80m2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

    Tandy Radio Shack TRS-80 Model II/12/16/16B/6000

    http://home.iae.nl/users/pb0aia/cm/modelii.html

*/

/*

    TODO:

    - CP/M won't load prompt (z80dma_do_operation: invalid mode 0 when reading track 0)
    - keyboard CPU ROM
    - graphics board
    - Tandy 6000 HD

        chdman -createblankhd tandy6000hd.chd 306 6 34 256

*/

#include "emu.h"
#include "trs80m2.h"

#include "screen.h"
#include "softlist_dev.h"


#define KEYBOARD_TAG "keyboard"


//**************************************************************************
//  READ/WRITE HANDLERS
//**************************************************************************

uint8_t trs80m2_state::read(offs_t offset)
{
	uint8_t data = 0;

	if (offset < 0x800)
	{
		if (m_boot_rom)
		{
			data = m_rom->base()[offset];
		}
		else
		{
			data = m_ram->pointer()[offset];
		}
	}
	else if (offset < 0x8000)
	{
		data = m_ram->pointer()[offset];
	}
	else
	{
		if (m_msel && offset >= 0xf800)
		{
			data = m_video_ram[offset & 0x7ff];
		}
		else if (m_bank)
		{
			offs_t addr = (m_bank << 15) | (offset & 0x7fff);

			if (addr < m_ram->size())
			{
				data = m_ram->pointer()[addr];
			}
		}
	}

	return data;
}

void trs80m2_state::write(offs_t offset, uint8_t data)
{
	if (offset < 0x8000)
	{
		m_ram->pointer()[offset] = data;
	}
	else
	{
		if (m_msel && offset >= 0xf800)
		{
			m_video_ram[offset & 0x7ff] = data;
		}
		else if (m_bank)
		{
			offs_t addr = (m_bank << 15) | (offset & 0x7fff);

			if (addr < m_ram->size())
			{
				m_ram->pointer()[addr] = data;
			}
		}
	}
}

void trs80m2_state::rom_enable_w(uint8_t data)
{
	/*

	    bit     description

	    0       BOOT ROM
	    1
	    2
	    3
	    4
	    5
	    6
	    7

	*/

	m_boot_rom = BIT(data, 0);
}

void trs80m2_state::drvslt_w(uint8_t data)
{
	/*

	    bit     signal

	    0       DS1
	    1       DS2
	    2       DS3
	    3       DS4
	    4
	    5
	    6       SDSEL
	    7       FM/MFM

	*/

	// drive select
	m_floppy = nullptr;

	if (!BIT(data, 0)) m_floppy = m_floppy0->get_device();
	if (!BIT(data, 1)) m_floppy = m_floppy1->get_device();
	if (!BIT(data, 2)) m_floppy = m_floppy2->get_device();
	if (!BIT(data, 3)) m_floppy = m_floppy3->get_device();

	m_fdc->set_floppy(m_floppy);

	if (m_floppy)
	{
		// side select
		m_floppy->ss_w(!BIT(data, 6));
	}

	// FM/MFM
	m_fdc->dden_w(!BIT(data, 7));
}

uint8_t trs80m2_state::keyboard_r()
{
	// clear keyboard interrupt
	if (!m_kbirq)
	{
		m_kbirq = 1;
		m_ctc->trg3(m_kbirq);
		m_kb->busy_w(m_kbirq);
	}

	m_key_bit = 0;

	return m_key_data;
}

uint8_t trs80m2_state::rtc_r()
{
	// clear RTC interrupt
	m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);

	return 0;
}

uint8_t trs80m2_state::nmi_r()
{
	/*

	    bit     signal              description

	    0
	    1
	    2
	    3
	    4       80/40 CHAR EN       80/40 character mode
	    5       ENABLE RTC INT      RTC interrupt enable
	    6       DE                  display enabled
	    7       KBIRQ               keyboard interrupt

	*/

	uint8_t data = 0;

	// 80/40 character mode*/
	data |= m_80_40_char_en << 4;

	// RTC interrupt enable
	data |= m_enable_rtc_int << 5;

	// display enabled
	data |= m_de << 6;

	// keyboard interrupt
	data |= !m_kbirq << 7;

	return data;
}

void trs80m2_state::nmi_w(uint8_t data)
{
	/*

	    bit     signal              description

	    0                           memory bank select bit 0
	    1                           memory bank select bit 1
	    2                           memory bank select bit 2
	    3                           memory bank select bit 3
	    4       80/40 CHAR EN       80/40 character mode
	    5       ENABLE RTC INT      RTC interrupt enable
	    6       BLNKVID             video display enable
	    7                           video RAM enable

	*/

	// memory bank select
	m_bank = data & 0x0f;

	// 80/40 character mode
	m_80_40_char_en = BIT(data, 4);
	m_crtc->set_unscaled_clock(12.48_MHz_XTAL / (m_80_40_char_en ? 16 : 8));

	// RTC interrupt enable
	m_enable_rtc_int = BIT(data, 5);

	if (m_enable_rtc_int && m_rtc_int)
	{
		// trigger RTC interrupt
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
	}

	// video display enable
	m_blnkvid = BIT(data, 6);

	// video RAM enable
	m_msel = BIT(data, 7);
}

uint8_t trs80m2_state::fdc_r(offs_t offset)
{
	return m_fdc->read(offset) ^ 0xff;
}

void trs80m2_state::fdc_w(offs_t offset, uint8_t data)
{
	m_fdc->write(offset, data ^ 0xff);
}

void trs80m16_state::tcl_w(uint8_t data)
{
	/*

	    bit     description

	    0       CONT0
	    1       CONT1
	    2       HALT
	    3       RESET
	    4       CONT4
	    5       CONT5
	    6       CONT6
	    7       A14

	*/

	m_subcpu->set_input_line(INPUT_LINE_HALT, BIT(data, 2) ? ASSERT_LINE : CLEAR_LINE);
	m_subcpu->set_input_line(INPUT_LINE_RESET, BIT(data, 3) ? ASSERT_LINE : CLEAR_LINE);

	m_uic->ireq0_w(BIT(data, 4));
	m_uic->ireq1_w(BIT(data, 5));
	m_uic->ireq2_w(BIT(data, 6));

	m_ual = (m_ual & 0x1fe) | BIT(data, 7);
}

void trs80m16_state::ual_w(uint8_t data)
{
	/*

	    bit     description

	    0       A15
	    1       A16
	    2       A17
	    3       A18
	    4       A19
	    5       A20
	    6       A21
	    7       A22

	*/

	m_ual = (data << 1) | BIT(m_ual, 0);
}



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( z80_mem )
//-------------------------------------------------

void trs80m2_state::z80_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(trs80m2_state::read), FUNC(trs80m2_state::write));
}


//-------------------------------------------------
//  ADDRESS_MAP( z80_io )
//-------------------------------------------------

void trs80m2_state::z80_io(address_map &map)
{
	map.global_mask(0xff);
	map(0xe0, 0xe3).rw(m_pio, FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0xe4, 0xe7).rw(FUNC(trs80m2_state::fdc_r), FUNC(trs80m2_state::fdc_w));
	map(0xef, 0xef).w(FUNC(trs80m2_state::drvslt_w));
	map(0xf0, 0xf3).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0xf4, 0xf7).rw(Z80SIO_TAG, FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
	map(0xf8, 0xf8).rw(m_dmac, FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	map(0xf9, 0xf9).w(FUNC(trs80m2_state::rom_enable_w));
	map(0xfc, 0xfc).r(FUNC(trs80m2_state::keyboard_r)).w(m_crtc, FUNC(mc6845_device::address_w));
	map(0xfd, 0xfd).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0xfe, 0xfe).r(FUNC(trs80m2_state::rtc_r));
	map(0xff, 0xff).rw(FUNC(trs80m2_state::nmi_r), FUNC(trs80m2_state::nmi_w));
}


//-------------------------------------------------
//  ADDRESS_MAP( m16_z80_io )
//-------------------------------------------------

void trs80m16_state::m16_z80_io(address_map &map)
{
	z80_io(map);
	map(0xde, 0xde).w(FUNC(trs80m16_state::tcl_w));
	map(0xdf, 0xdf).w(FUNC(trs80m16_state::ual_w));
}


//-------------------------------------------------
//  ADDRESS_MAP( m68000_mem )
//-------------------------------------------------

void trs80m2_state::m68000_mem(address_map &map)
{
//  map(0x7800d0, 0x7800d1) 9519A (C/D = UDS)
//  map(0x7800d2, 0x7800d3) limit/offset 2
//  map(0x7800d4, 0x7800d5) limit/offset 1
//  map(0x7800d6, 0x7800d7) Z80 IRQ
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( trs80m2 )
//-------------------------------------------------

static INPUT_PORTS_START( trs80m2 )
INPUT_PORTS_END



//**************************************************************************
//  VIDEO
//**************************************************************************

MC6845_UPDATE_ROW( trs80m2_state::crtc_update_row )
{
	pen_t const *const pen = m_palette->pens();

	int x = 0;

	for (int column = 0; column < x_count; column++)
	{
		uint8_t code = m_video_ram[(ma + column) & 0x7ff];
		offs_t address = ((code & 0x7f) << 4) | (ra & 0x0f);
		uint8_t data = m_char_rom->base()[address];

		int dcursor = (column == cursor_x);
		int drevid = BIT(code, 7);

		for (int bit = 0; bit < 8; bit++)
		{
			int dout = BIT(data, 7);
			int color = (dcursor ^ drevid ^ dout) && de;

			bitmap.pix(vbp + y, hbp + x++) = pen[color];

			data <<= 1;
		}
	}
}

void trs80m2_state::de_w(int state)
{
	m_de = state;
}

void trs80m2_state::vsync_w(int state)
{
	if (state)
	{
		m_rtc_int = !m_rtc_int;

		if (m_enable_rtc_int && m_rtc_int)
		{
			// trigger RTC interrupt
			m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
		}
	}
}

void trs80m2_state::video_start()
{
}

uint32_t trs80m2_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if (m_blnkvid)
	{
		bitmap.fill(rgb_t::black(), cliprect);
	}
	else
	{
		m_crtc->screen_update(screen, bitmap, cliprect);
	}

	return 0;
}



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  TRS80M2_KEYBOARD_INTERFACE( kb_intf )
//-------------------------------------------------

void trs80m2_state::kb_clock_w(int state)
{
	int kbdata = m_kb->data_r();

	if (m_key_bit == 8)
	{
		if (!m_kbdata && kbdata)
		{
			// trigger keyboard interrupt
			m_kbirq = 0;
			m_ctc->trg3(m_kbirq);
			m_kb->busy_w(m_kbirq);
		}
	}
	else
	{
		if (!m_kbclk && state)
		{
			// shift in keyboard data bit
			m_key_data <<= 1;
			m_key_data |= kbdata;
			m_key_bit++;
		}
	}

	m_kbdata = kbdata;
	m_kbclk = state;
}

void trs80m2_state::kbd_w(u8 data)
{
	// latch key data
	m_key_data = data;

	// trigger keyboard interrupt
	m_kbirq = 0;
	m_ctc->trg3(m_kbirq);
}

//-------------------------------------------------
//  Z80DMA
//-------------------------------------------------

uint8_t trs80m2_state::io_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_IO);
	return prog_space.read_byte(offset);
}

void trs80m2_state::io_write_byte(offs_t offset, uint8_t data)
{
	address_space& prog_space = m_maincpu->space(AS_IO);
	return prog_space.write_byte(offset, data);
}

//-------------------------------------------------
//  Z80PIO
//-------------------------------------------------

void trs80m2_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

void trs80m2_state::write_centronics_fault(int state)
{
	m_centronics_fault = state;
}

void trs80m2_state::write_centronics_perror(int state)
{
	m_centronics_perror = state;
}

uint8_t trs80m2_state::pio_pa_r()
{
	/*

	    bit     signal      description

	    0       INTRQ       FDC INT request
	    1       _TWOSID     2-sided diskette
	    2       _DSKCHG     disk change
	    3       PRIME       prime
	    4       FAULT       printer fault
	    5       PSEL        printer select
	    6       PE          paper empty
	    7       BUSY        printer busy

	*/

	uint8_t data = 0;

	// floppy interrupt
	data |= (m_fdc->intrq_r() ? 0x01 : 0x00);

	// 2-sided diskette
	data |= (m_floppy ? m_floppy->twosid_r() : 1) << 1;

	// disk change
	data |= (m_floppy ? m_floppy->dskchg_r() : 1) << 2;

	// printer fault
	data |= m_centronics_fault << 4;

	// paper empty
	data |= m_centronics_perror << 6;

	// printer busy
	data |= m_centronics_busy << 7;

	return data;
}

void trs80m2_state::pio_pa_w(uint8_t data)
{
	/*

	    bit     signal      description

	    0       INTRQ       FDC INT request
	    1       _TWOSID     2-sided diskette
	    2       _DSKCHG     disk change
	    3       PRIME       prime
	    4       FAULT       printer fault
	    5       PSEL        printer select
	    6       PE          paper empty
	    7       BUSY        printer busy

	*/

	// prime
	m_centronics->write_init(BIT(data, 3));
}

void trs80m2_state::strobe_w(int state)
{
	m_centronics->write_strobe(!state);
}

//-------------------------------------------------
//  Z80CTC
//-------------------------------------------------

static void trs80m2_floppies(device_slot_interface &device)
{
	device.option_add("8ssdd", FLOPPY_8_SSDD); // Shugart SA-800
	device.option_add("8dsdd", FLOPPY_8_DSDD); // Shugart SA-850
}


//-------------------------------------------------
//  z80_daisy_config trs80m2_daisy_chain
//-------------------------------------------------

static const z80_daisy_config trs80m2_daisy_chain[] =
{
	{ Z80CTC_TAG },
	{ Z80SIO_TAG },
	{ Z80DMA_TAG },
	{ Z80PIO_TAG },
	{ nullptr }
};

//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  machine_start
//-------------------------------------------------

void trs80m2_state::machine_start()
{
	// register for state saving
	save_item(NAME(m_boot_rom));
	save_item(NAME(m_bank));
	save_item(NAME(m_msel));
	save_item(NAME(m_key_latch));
	save_item(NAME(m_key_data));
	save_item(NAME(m_key_bit));
	save_item(NAME(m_kbclk));
	save_item(NAME(m_kbdata));
	save_item(NAME(m_kbirq));
	save_item(NAME(m_blnkvid));
	save_item(NAME(m_80_40_char_en));
	save_item(NAME(m_de));
	save_item(NAME(m_rtc_int));
	save_item(NAME(m_enable_rtc_int));
}

void trs80m16_state::machine_start()
{
	trs80m2_state::machine_start();

	// register for state saving
	save_item(NAME(m_ual));
	save_item(NAME(m_limit));
	save_item(NAME(m_offset));
}


//-------------------------------------------------
//  machine_reset
//-------------------------------------------------

void trs80m2_state::machine_reset()
{
	// clear keyboard interrupt
	m_kbirq = 1;
	m_ctc->trg3(m_kbirq);
	m_kb->busy_w(m_kbirq);

	// enable boot ROM
	m_boot_rom = 1;

	// disable video RAM
	m_msel = 0;
}



//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

//-------------------------------------------------
//  machine_config( trs80m2 )
//-------------------------------------------------

void trs80m2_state::trs80m2(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 8_MHz_XTAL / 2);
	m_maincpu->set_daisy_config(trs80m2_daisy_chain);
	m_maincpu->set_addrmap(AS_PROGRAM, &trs80m2_state::z80_mem);
	m_maincpu->set_addrmap(AS_IO, &trs80m2_state::z80_io);
	m_maincpu->busack_cb().set(m_dmac, FUNC(z80dma_device::bai_w));

	// video hardware
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(0));
	screen.set_screen_update(FUNC(trs80m2_state::screen_update));
	screen.set_size(640, 480);
	screen.set_visarea(0, 639, 0, 479);

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	MC6845(config, m_crtc, 12.48_MHz_XTAL / 8);
	m_crtc->set_screen(SCREEN_TAG);
	m_crtc->set_show_border_area(true);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(trs80m2_state::crtc_update_row));
	m_crtc->out_de_callback().set(FUNC(trs80m2_state::de_w));
	m_crtc->out_vsync_callback().set(FUNC(trs80m2_state::vsync_w));

	// devices
	FD1791(config, m_fdc, 8_MHz_XTAL / 4);
	m_fdc->intrq_wr_callback().set(m_pio, FUNC(z80pio_device::port_a_write));
	m_fdc->drq_wr_callback().set(m_dmac, FUNC(z80dma_device::rdy_w));
	FLOPPY_CONNECTOR(config, FD1791_TAG":0", trs80m2_floppies, "8dsdd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, FD1791_TAG":1", trs80m2_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, FD1791_TAG":2", trs80m2_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, FD1791_TAG":3", trs80m2_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);

	Z80CTC(config, m_ctc, 8_MHz_XTAL / 2);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->set_clk<0>(8_MHz_XTAL / 2 / 2);
	m_ctc->set_clk<1>(8_MHz_XTAL / 2 / 2);
	m_ctc->set_clk<2>(8_MHz_XTAL / 2 / 2);
	m_ctc->zc_callback<0>().set(Z80SIO_TAG, FUNC(z80sio_device::rxca_w));
	m_ctc->zc_callback<1>().set(Z80SIO_TAG, FUNC(z80sio_device::txca_w));
	m_ctc->zc_callback<2>().set(Z80SIO_TAG, FUNC(z80sio_device::rxtxcb_w));

	Z80DMA(config, m_dmac, 8_MHz_XTAL / 2);
	m_dmac->out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	m_dmac->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_dmac->in_mreq_callback().set(FUNC(trs80m2_state::read));
	m_dmac->out_mreq_callback().set(FUNC(trs80m2_state::write));
	m_dmac->in_iorq_callback().set(FUNC(trs80m2_state::io_read_byte));
	m_dmac->out_iorq_callback().set(FUNC(trs80m2_state::io_write_byte));

	Z80PIO(config, m_pio, 8_MHz_XTAL / 2);
	m_pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio->in_pa_callback().set(FUNC(trs80m2_state::pio_pa_r));
	m_pio->out_pa_callback().set(FUNC(trs80m2_state::pio_pa_w));
	m_pio->out_pb_callback().set("cent_data_out", FUNC(output_latch_device::write));
	m_pio->out_brdy_callback().set(FUNC(trs80m2_state::strobe_w));

	z80sio_device& sio(Z80SIO(config, Z80SIO_TAG, 8_MHz_XTAL / 2)); // SIO/0
	sio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set(m_pio, FUNC(z80pio_device::strobe_b));
	m_centronics->busy_handler().set(FUNC(trs80m2_state::write_centronics_busy));
	m_centronics->fault_handler().set(FUNC(trs80m2_state::write_centronics_fault));
	m_centronics->perror_handler().set(FUNC(trs80m2_state::write_centronics_perror));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	TRS80M2_KEYBOARD(config, m_kb, 0);
	m_kb->clock_wr_callback().set(FUNC(trs80m2_state::kb_clock_w));
	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, KEYBOARD_TAG, 0));
	keyboard.set_keyboard_callback(FUNC(trs80m2_state::kbd_w));

	// internal RAM
	RAM(config, RAM_TAG).set_default_size("64K").set_extra_options("32K,96K,128K,160K,192K,224K,256K,288K,320K,352K,384K,416K,448K,480K,512K");

	// software list
	SOFTWARE_LIST(config, "flop_list").set_original("trs80m2");
}


//-------------------------------------------------
//  machine_config( trs80m16 )
//-------------------------------------------------

void trs80m16_state::trs80m16(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 8_MHz_XTAL / 2);
	m_maincpu->set_daisy_config(trs80m2_daisy_chain);
	m_maincpu->set_addrmap(AS_PROGRAM, &trs80m16_state::z80_mem);
	m_maincpu->set_addrmap(AS_IO, &trs80m16_state::m16_z80_io);
	m_maincpu->set_irq_acknowledge_callback(AM9519A_TAG, FUNC(am9519_device::iack_cb));
	m_maincpu->busack_cb().set(m_dmac, FUNC(z80dma_device::bai_w));

	M68000(config, m_subcpu, 24_MHz_XTAL / 4);
	m_subcpu->set_addrmap(AS_PROGRAM, &trs80m16_state::m68000_mem);
	m_subcpu->set_disable();

	// video hardware
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(0));
	screen.set_screen_update(FUNC(trs80m2_state::screen_update));
	screen.set_size(640, 480);
	screen.set_visarea(0, 639, 0, 479);

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	MC6845(config, m_crtc, 12.48_MHz_XTAL / 8);
	m_crtc->set_screen(SCREEN_TAG);
	m_crtc->set_show_border_area(true);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(trs80m2_state::crtc_update_row));
	m_crtc->out_de_callback().set(FUNC(trs80m2_state::de_w));
	m_crtc->out_vsync_callback().set(FUNC(trs80m2_state::vsync_w));

	// devices
	FD1791(config, m_fdc, 8_MHz_XTAL / 4);
	m_fdc->intrq_wr_callback().set(m_pio, FUNC(z80pio_device::port_a_write));
	m_fdc->drq_wr_callback().set(m_dmac, FUNC(z80dma_device::rdy_w));
	FLOPPY_CONNECTOR(config, FD1791_TAG":0", trs80m2_floppies, "8dsdd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, FD1791_TAG":1", trs80m2_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, FD1791_TAG":2", trs80m2_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, FD1791_TAG":3", trs80m2_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);

	Z80CTC(config, m_ctc, 8_MHz_XTAL / 2);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->set_clk<0>(8_MHz_XTAL / 2 / 2);
	m_ctc->set_clk<1>(8_MHz_XTAL / 2 / 2);
	m_ctc->set_clk<2>(8_MHz_XTAL / 2 / 2);
	m_ctc->zc_callback<0>().set(Z80SIO_TAG, FUNC(z80sio_device::rxca_w));
	m_ctc->zc_callback<1>().set(Z80SIO_TAG, FUNC(z80sio_device::txca_w));
	m_ctc->zc_callback<2>().set(Z80SIO_TAG, FUNC(z80sio_device::rxtxcb_w));

	Z80DMA(config, m_dmac, 8_MHz_XTAL / 2);
	m_dmac->out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	m_dmac->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_dmac->in_mreq_callback().set(FUNC(trs80m2_state::read));
	m_dmac->out_mreq_callback().set(FUNC(trs80m2_state::write));
	m_dmac->in_iorq_callback().set(FUNC(trs80m2_state::io_read_byte));
	m_dmac->out_iorq_callback().set(FUNC(trs80m2_state::io_write_byte));

	Z80PIO(config, m_pio, 8_MHz_XTAL / 2);
	m_pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio->in_pa_callback().set(FUNC(trs80m2_state::pio_pa_r));
	m_pio->out_pa_callback().set(FUNC(trs80m2_state::pio_pa_w));
	m_pio->out_pb_callback().set("cent_data_out", FUNC(output_latch_device::write));
	m_pio->out_brdy_callback().set(FUNC(trs80m2_state::strobe_w));

	z80sio_device& sio(Z80SIO(config, Z80SIO_TAG, 8_MHz_XTAL / 2));
	sio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	AM9519(config, m_uic, 0);
	m_uic->out_int_callback().set_inputline(m_subcpu, M68K_IRQ_5);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set(m_pio, FUNC(z80pio_device::strobe_b));
	m_centronics->busy_handler().set(FUNC(trs80m2_state::write_centronics_busy));
	m_centronics->fault_handler().set(FUNC(trs80m2_state::write_centronics_fault));
	m_centronics->perror_handler().set(FUNC(trs80m2_state::write_centronics_perror));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	TRS80M2_KEYBOARD(config, m_kb, 0);
	m_kb->clock_wr_callback().set(FUNC(trs80m2_state::kb_clock_w));
	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, KEYBOARD_TAG, 0));
	keyboard.set_keyboard_callback(FUNC(trs80m2_state::kbd_w));

	// internal RAM
	RAM(config, RAM_TAG).set_default_size("256K").set_extra_options("512K,768K,1M");

	// software list
	SOFTWARE_LIST(config, "flop_list").set_original("trs80m2");
}



//**************************************************************************
//  ROMS
//**************************************************************************

/*

    TRS-80 Model II/16 Z80 CPU Board ROM

    It would seem that every processor board I find has a different ROM on it!  It seems that the early ROMs
    don't boot directly from a hard drive.  But there seems to be many versions of ROMs.  I've placed them in
    order of serial number in the list below.  There also appears to be at least two board revisions, "C" and "D".

    cpu_c8ff.bin/hex:
    Mask Programmable PROM, Equivilant to Intel 2716 EPROM, with checksum C8FF came from a cpu board with
    serial number 120353 out of a Model II with serial number 2002102 and catalog number 26-6002.  The board
    was labeled, "Revision C".  This appears to be an early ROM and according to a very helpful fellow
    collector, Aaron in Australia, doesn't allow boot directly from a hard disk.

    cpu_9733.bin/hex:
    An actual SGS-Ates (Now STMicroelectronics) 2716 EPROM, with checksum 9733 came from a cpu board with
    serial number 161993 out of a pile of random cards that I have.  I don't know what machine it originated
    from.  The board was labeled, "Revision C".  This appears to be a later ROM in that it is able to boot
    directly from an 8MB hard disk.  The EPROM had a windows sticker on it labeled, "U54".

    cpu_2119.bin/hex:
    An actual Texas Instruments TMS2516 EPROM, with checksum 2119 came from a cpu board with serial number
    178892 out of a Model 16 with serial number 64014509 and catalog number 26-4002.  The board was labeled,
    "Revision D".  This appears to be a later ROM and does appear to allow boot directly from an 8MB hard disk.

    cpu_2bff.bin/hex:
    Mask Programmable PROM, Equivilant to Intel 2716 EPROM, with checksum 2BFF came from a cpu board with
    serial number 187173 our of a pile of random cards that I have.  I don't know what machine it originated
    from.  The board was labeled, "Revision D".  This appears to be a later ROM in that it is able to boot
    directly from an 8MB hard disk.

*/

//-------------------------------------------------
//  ROM( trs80m2 )
//-------------------------------------------------

ROM_START( trs80m2 )
	ROM_REGION( 0x800, Z80_TAG, 0 )
	ROM_DEFAULT_BIOS( "9733" )
	ROM_SYSTEM_BIOS( 0, "c8ff", "Version 1" )
	ROMX_LOAD( "8043216.u11", 0x0000, 0x0800, CRC(7017a373) SHA1(1c7127fcc99fc351a40d3a3199ba478e783c452e), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "2bff", "Version 2 (1981-07-29)" )
	ROMX_LOAD( "8047316.u11", 0x0000, 0x0800, CRC(c6c71d8b) SHA1(7107e2cbbe769851a4460680c2deff8e76a101b5), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "2119", "Version 3 (1982-05-07)" )
	ROMX_LOAD( "cpu_2119.u11", 0x0000, 0x0800, CRC(7a663049) SHA1(f308439ce266df717bfe79adcdad6024b4faa141), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "1bbe", "Version 3 (1982-05-07, Alt)" )
	ROMX_LOAD( "cpu_11be.u11", 0x0000, 0x0800, CRC(8edceea7) SHA1(3d797acedd8a71a82c695129ca764f85aa9022b2), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS( 4, "fc86", "Version 4 (1982-11-18)" )
	ROMX_LOAD( "cpu_fc86.u11", 0x0000, 0x0800, NO_DUMP, ROM_BIOS(4) )
	ROM_SYSTEM_BIOS( 5, "9733", "Version 5 (1983-07-29)" )
	ROMX_LOAD( "u54.u11", 0x0000, 0x0800, CRC(823924b1) SHA1(aee0625bcbd8620b28ab705e15ad9bea804c8476), ROM_BIOS(5) )

	ROM_REGION( 0x800, MC6845_TAG, 0 )
	ROM_LOAD( "8043316.u9", 0x0000, 0x0800, CRC(04425b03) SHA1(32a29dc202b7fcf21838289cc3bffc51ef943dab) )
ROM_END


//-------------------------------------------------
//  ROM( trs80m16 )
//-------------------------------------------------

#define rom_trs80m16 rom_trs80m2



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME        PARENT   COMPAT  MACHINE   INPUT    CLASS           INIT        COMPANY              FULLNAME            FLAGS
COMP( 1979, trs80m2,    0,       0,      trs80m2,  trs80m2, trs80m2_state,  empty_init, "Tandy Radio Shack", "TRS-80 Model II",  MACHINE_NO_SOUND_HW )
COMP( 1982, trs80m16,   trs80m2, 0,      trs80m16, trs80m2, trs80m16_state, empty_init, "Tandy Radio Shack", "TRS-80 Model 16",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )
//COMP( 1983, trs80m12, trs80m2, 0,      trs80m16, trs80m2, trs80m16_state, empty_init, "Tandy Radio Shack", "TRS-80 Model 12",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )
//COMP( 1984, trs80m16b,trs80m2, 0,      trs80m16, trs80m2, trs80m16_state, empty_init, "Tandy Radio Shack", "TRS-80 Model 16B", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )
//COMP( 1985, tandy6k,  trs80m2, 0,      tandy6k,  trs80m2, tandy6k_state,  empty_init, "Tandy Radio Shack", "Tandy 6000 HD",    MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



trs80m3.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************
Cassette baud rates:
        Model III/4 - 500 and 1500 baud selectable at boot time
        - When it says "Cass?" press L for 500 baud, or Enter otherwise.

I/O ports
FF:
- bits 0 and 1 are for writing a cassette
- bit 3 switches the display between 64 or 32 characters per line
- bit 6 remembers the 32/64 screen mode (inverted)
- bit 7 is for reading from a cassette

F8:
- Write to printer (Model III/4)
- Read printer status (Model III/4)

EB:
- UART data (read and write) on a Model III/4

EA:
- UART status (read and write) on a Model III/4

E9:
- Set baud rate (Model III/4)

E8:
- UART Modem Status register (read) on a Model III/4
- UART Master Reset (write) on a Model III/4

Model 4 - C0-CF = hard drive (optional)
    - 90-93 write sound (optional)
    - 80-8F hires graphics (optional)

Shift and Right-arrow will enable 32 cpl.

About the RTC - The time is incremented while ever the cursor is flashing. It is stored in a series
    of bytes in the computer's work area. The bytes are in a certain order, this is:
    seconds, minutes, hours, year, day, month. The seconds are stored at 0x4217.
    A reboot always sets the time to zero.

Model 4 memory organisation -
    Mode 0: ROM=0-37E7 and 37EA-3FFF; Printer=37E8-37E9; Keyboard=3800-3BFF; Video=3C00-3FFF
    Mode 1: Keyboard and Video as above; 0-3FFF read=ROM and write=RAM
    Mode 2: Keyboard=F400-F7FF; Video=F800-FFFF; the rest is RAM
    Mode 3: All RAM
    The video is organised as 2 banks of 0x400 bytes, except in Mode 2 where it becomes contiguous.

Model 4P - is the same as Model 4 except:
    - ROM is only 0000-0FFF, while 1000-37FF is given over to RAM
    - There is no cassette support in hardware.

***************************************************************************


To Do / Status:
--------------

JV3: Cannot write, due to an emulation bug causing the machine to hang.
JV1: If you try to create a disk in the File Manager, MAME will crash.
DMK: Cannot write (no option).
IMD: Does not work with a quad drive. Cannot write.

trs80m3:   Works

trs80m4:   Works

trs80m4p:  Floppy not working, so machine is useless.
           In debugger g 402a, then pc=0;g and it will boot.

***************************************************************************/

#include "emu.h"
#include "trs80m3.h"

#include "trs80_quik.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/imd_dsk.h"
#include "formats/trs80_dsk.h"
#include "formats/dmk_dsk.h"

#include "utf8.h"



void trs80m3_state::m3_mem(address_map &map)
{
	map(0x0000, 0x37ff).rom();
	map(0x37e8, 0x37e9).rw(FUNC(trs80m3_state::printer_r), FUNC(trs80m3_state::printer_w));
	map(0x3800, 0x3bff).r(FUNC(trs80m3_state::keyboard_r));
	map(0x3c00, 0x3fff).ram().share(m_p_videoram);
	map(0x4000, 0xffff).ram();
}

void trs80m3_state::m3_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0xe0, 0xe3).rw(FUNC(trs80m3_state::port_e0_r), FUNC(trs80m3_state::port_e0_w));
	map(0xe4, 0xe4).rw(FUNC(trs80m3_state::port_e4_r), FUNC(trs80m3_state::port_e4_w));
	map(0xe8, 0xe8).rw(FUNC(trs80m3_state::port_e8_r), FUNC(trs80m3_state::port_e8_w));
	map(0xe9, 0xe9).w(m_brg, FUNC(com8116_device::stt_str_w));
	map(0xea, 0xea).rw(FUNC(trs80m3_state::port_ea_r), FUNC(trs80m3_state::port_ea_w));
	map(0xeb, 0xeb).rw(m_uart, FUNC(ay31015_device::receive), FUNC(ay31015_device::transmit));
	map(0xec, 0xef).rw(FUNC(trs80m3_state::port_ec_r), FUNC(trs80m3_state::port_ec_w));
	map(0xf0, 0xf0).r(FUNC(trs80m3_state::wd179x_r));
	map(0xf0, 0xf0).w(m_fdc, FUNC(fd1793_device::cmd_w));
	map(0xf1, 0xf1).rw(m_fdc, FUNC(fd1793_device::track_r), FUNC(fd1793_device::track_w));
	map(0xf2, 0xf2).rw(m_fdc, FUNC(fd1793_device::sector_r), FUNC(fd1793_device::sector_w));
	map(0xf3, 0xf3).rw(m_fdc, FUNC(fd1793_device::data_r), FUNC(fd1793_device::data_w));
	map(0xf4, 0xf7).w(FUNC(trs80m3_state::port_f4_w));
	map(0xf8, 0xfb).rw(FUNC(trs80m3_state::printer_r), FUNC(trs80m3_state::printer_w));
	map(0xfc, 0xff).rw(FUNC(trs80m3_state::port_ff_r), FUNC(trs80m3_state::port_ff_w));
}

void trs80m3_state::m4_mem(address_map &map)
{
	map(0x0000, 0xffff).m(m_m4_bank, FUNC(address_map_bank_device::amap8));
}

void trs80m3_state::m4_banked_mem(address_map &map)
{
	// Memory Map I - Model III Mode
	map(0x00000, 0x037ff).rom().region("maincpu", 0);
	map(0x037e8, 0x037e9).rw(FUNC(trs80m3_state::printer_r), FUNC(trs80m3_state::printer_w));
	map(0x03800, 0x03bff).r(FUNC(trs80m3_state::keyboard_r));
	map(0x03c00, 0x03fff).bankrw(m_vidbank);        // Video RAM (Page bit selects 1K of 2K)
	map(0x04000, 0x07fff).bankrw(m_16kbank);        // RAM
	map(0x08000, 0x0ffff).bankrw(m_32kbanks[1]);    // RAM

	// Memory Map II
	map(0x10000, 0x137ff).bankrw(m_32kbanks[0]);    // RAM (14K)
	map(0x13800, 0x13bff).r(FUNC(trs80m3_state::keyboard_r));
	map(0x13c00, 0x13fff).bankrw(m_vidbank);        // Video RAM
	map(0x14000, 0x17fff).bankrw(m_16kbank);        // RAM (16K)
	map(0x18000, 0x1ffff).bankrw(m_32kbanks[1]);    // RAM (32K)

	// Memory Map III
	map(0x20000, 0x27fff).bankrw(m_32kbanks[0]);    // RAM (32K)
	map(0x28000, 0x2f3ff).bankrw(m_32kbanks[1]);    // RAM (29K)
	map(0x2f400, 0x2f7ff).r(FUNC(trs80m3_state::keyboard_r));
	map(0x2f800, 0x2ffff).ram().share(m_p_videoram);    // Video RAM

	// Memory Map IV
	map(0x30000, 0x37fff).bankrw(m_32kbanks[0]);    // RAM (32K)
	map(0x38000, 0x3ffff).bankrw(m_32kbanks[1]);    // RAM (32K)
}

void trs80m3_state::m4_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	m3_io(map);
	map(0x84, 0x87).w(FUNC(trs80m3_state::port_84_w));
	map(0x88, 0x89).w(FUNC(trs80m3_state::port_88_w));
	map(0x90, 0x93).w(FUNC(trs80m3_state::port_90_w));
}

void trs80m3_state::m4p_mem(address_map &map)
{
	map(0x0000, 0xffff).m(m_m4p_bank, FUNC(address_map_bank_device::amap8));
}

void trs80m3_state::m4p_banked_mem(address_map &map)
{
	// Memory Map I - Model III Mode
	map(0x00000, 0x00fff).rom().region("maincpu", 0);
	map(0x01000, 0x037ff).bankr(m_32kbanks[0]);      // readonly RAM
	map(0x037e8, 0x037e9).rw(FUNC(trs80m3_state::printer_r), FUNC(trs80m3_state::printer_w));
	map(0x03800, 0x03bff).r(FUNC(trs80m3_state::keyboard_r));
	map(0x03c00, 0x03fff).bankrw(m_vidbank);        // Video RAM (Page bit selects 1K of 2K)
	map(0x04000, 0x07fff).bankrw(m_16kbank);        // RAM
	map(0x08000, 0x0ffff).bankrw(m_32kbanks[1]);    // RAM
	// Memory Map II
	map(0x10000, 0x137ff).bankrw(m_32kbanks[0]);    // RAM
	map(0x10000, 0x10fff).rom().region("maincpu", 0);   // the ram under here is writeonly
	map(0x13800, 0x13bff).r(FUNC(trs80m3_state::keyboard_r));
	map(0x13c00, 0x13fff).bankrw(m_vidbank);        // Video RAM
	map(0x14000, 0x17fff).bankrw(m_16kbank);        // RAM (16K)
	map(0x18000, 0x1ffff).bankrw(m_32kbanks[1]);    // RAM (32K)
	// Memory Map III
	map(0x20000, 0x27fff).bankrw(m_32kbanks[0]);    // RAM (32K)
	map(0x28000, 0x2f3ff).bankrw(m_32kbanks[1]);    // RAM (29K)
	map(0x2f400, 0x2f7ff).r(FUNC(trs80m3_state::keyboard_r));
	map(0x2f800, 0x2ffff).ram().share(m_p_videoram);    // Video RAM
	// Memory Map IV
	map(0x30000, 0x37fff).bankrw(m_32kbanks[0]);    // RAM (32K)
	map(0x38000, 0x3ffff).bankrw(m_32kbanks[1]);    // RAM (32K)
	// Map I with no rom
	map(0x40000, 0x437ff).bankr(m_32kbanks[0]);      // readonly RAM
	map(0x437e8, 0x437e9).rw(FUNC(trs80m3_state::printer_r), FUNC(trs80m3_state::printer_w));
	map(0x43800, 0x43bff).r(FUNC(trs80m3_state::keyboard_r));
	map(0x43c00, 0x43fff).bankrw(m_vidbank);        // Video RAM
	map(0x44000, 0x47fff).bankrw(m_16kbank);        // RAM (16K)
	map(0x48000, 0x4ffff).bankrw(m_32kbanks[1]);    // RAM (32K)
	// Map II with no rom
	map(0x50000, 0x537ff).bankrw(m_32kbanks[0]);    // RAM (32K)
	map(0x53800, 0x53bff).r(FUNC(trs80m3_state::keyboard_r));
	map(0x53c00, 0x53fff).bankrw(m_vidbank);        // Video RAM
	map(0x54000, 0x57fff).bankrw(m_16kbank);        // RAM (16K)
	map(0x58000, 0x5ffff).bankrw(m_32kbanks[1]);    // RAM (32K)
}

void trs80m3_state::m4p_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	m4_io(map);
	map(0x9c, 0x9f).w(FUNC(trs80m3_state::port_9c_w));
	map(0xf0, 0xf0).rw(m_fdc, FUNC(fd1793_device::status_r), FUNC(fd1793_device::cmd_w));
}

void trs80m3_state::cp500_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	m3_io(map);
	map(0xf4, 0xf7).r(FUNC(trs80m3_state::cp500_port_f4_r));
}


static INPUT_PORTS_START( trs80m4p )
	PORT_START("LINE0")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('@')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)          PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)          PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)          PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)          PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)          PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)          PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)          PORT_CHAR('g') PORT_CHAR('G')

	PORT_START("LINE1")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)          PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)          PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)          PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)          PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)          PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)          PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)          PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("LINE2")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)          PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)          PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)          PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)          PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)          PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)          PORT_CHAR('w') PORT_CHAR('W')

	PORT_START("LINE3")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)          PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)          PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0xF8, 0x00, IPT_UNUSED) // these bits were tested and do nothing useful

	PORT_START("LINE4")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD)          PORT_CHAR('0')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)          PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)          PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START("LINE5")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME(": *") PORT_CODE(KEYCODE_MINUS)        PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("; +") PORT_CODE(KEYCODE_COLON)        PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA)        PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("- =") PORT_CODE(KEYCODE_EQUALS)       PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD)    PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH)        PORT_CHAR('/') PORT_CHAR('?')

	PORT_START("LINE6")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13)
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("Clear") PORT_CODE(KEYCODE_HOME)       PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_END)        PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)        PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	/* backspace do the same as cursor left */
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_BACKSPACE)   PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x80, 0x00, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')

	PORT_START("LINE7")
	PORT_BIT(0x01, 0x00, IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, 0x00, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT)
	// These keys are only on a Model 4. These bits do nothing on Model 3.
	PORT_BIT(0x04, 0x00, IPT_KEYBOARD) PORT_NAME("CTL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x08, 0x00, IPT_KEYBOARD) PORT_NAME("Caps") PORT_CODE(KEYCODE_CAPSLOCK) // When activated, lowercase entry is possible
	PORT_BIT(0x10, 0x00, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) // prints tic character
	PORT_BIT(0x20, 0x00, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x40, 0x00, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x80, 0x00, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( trs80m3 )
	PORT_INCLUDE( trs80m4p )
	PORT_START("CONFIG")
	PORT_CONFNAME(    0x80, 0x00,   "Floppy Disc Drives")
	PORT_CONFSETTING(   0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(   0x80, DEF_STR( On ) )
	PORT_BIT(0x7f, 0x7f, IPT_UNUSED)
INPUT_PORTS_END


/**************************** F4 CHARACTER DISPLAYER ***********************************************************/
static const gfx_layout trs80m3_charlayout =
{
	8, 8,           /* 8 x 8 characters */
	256,            /* 256 characters */
	1,          /* 1 bits per pixel */
	{ 0 },          /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{  0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8        /* every char takes 8 bytes */
};

static GFXDECODE_START(gfx_trs80m3)
	GFXDECODE_ENTRY( "chargen", 0, trs80m3_charlayout, 0, 1 )
GFXDECODE_END


void trs80m3_state::floppy_formats(format_registration &fr)
{
	fr.add(FLOPPY_IMD_FORMAT);
	fr.add(FLOPPY_JV3_FORMAT);
	fr.add(FLOPPY_DMK_FORMAT);
	fr.add(FLOPPY_JV1_FORMAT);
}

// If you choose a disk that has more tracks than the drive,
// MAME will probably crash. The default is DD, which allows
// IMD boot disks to work, and any other disk with up to 40
// tracks. You need QD to support up to 80 tracks, but it
// breaks the IMD disks.
static void trs80_floppies(device_slot_interface &device)
{
	device.option_add("35t_sd", FLOPPY_525_SSSD_35T);
	device.option_add("40t_sd", FLOPPY_525_SSSD);
	device.option_add("40t_dd", FLOPPY_525_DD);
	device.option_add("80t_qd", FLOPPY_525_QD);
}


void trs80m3_state::model3(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 20.2752_MHz_XTAL / 10); // FIXME: actual Model III XTAL is 10.1376 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &trs80m3_state::m3_mem);
	m_maincpu->set_addrmap(AS_IO, &trs80m3_state::m3_io);
	m_maincpu->set_periodic_int(FUNC(trs80m3_state::rtc_interrupt), attotime::from_hz(20.2752_MHz_XTAL / 10 / 67584));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(12.672_MHz_XTAL, 800, 0, 640, 264, 0, 240); // FIXME: these are Model 4 80-column parameters
	screen.set_screen_update(FUNC(trs80m3_state::screen_update_trs80m3));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_trs80m3);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	/* devices */
	CASSETTE(config, m_cassette);
	m_cassette->set_formats(trs80l2_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("trs80_cass");

	TRS80_QUICKLOAD(config, "quickload", m_maincpu, attotime::from_seconds(1));

	FD1793(config, m_fdc, 4_MHz_XTAL / 4);
	m_fdc->intrq_wr_callback().set(FUNC(trs80m3_state::intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(trs80m3_state::drq_w));

	// Internal drives
	FLOPPY_CONNECTOR(config, m_floppy[0], trs80_floppies, "40t_dd", trs80m3_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], trs80_floppies, "40t_dd", trs80m3_state::floppy_formats).enable_sound(true);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit7));
	m_centronics->perror_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit6));
	m_centronics->select_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit5));
	m_centronics->fault_handler().set(m_cent_status_in, FUNC(input_buffer_device::write_bit4));

	INPUT_BUFFER(config, m_cent_status_in);

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	COM8116(config, m_brg, 20.2752_MHz_XTAL / 4);   // BR1943 (or BR1941L)
	m_brg->fr_handler().set(m_uart, FUNC(ay31015_device::write_rcp));
	m_brg->ft_handler().set(m_uart, FUNC(ay31015_device::write_tcp));

	AY31015(config, m_uart);
	m_uart->read_si_callback().set("rs232", FUNC(rs232_port_device::rxd_r));
	m_uart->write_so_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	//MCFG_AY31015_WRITE_DAV_CB(WRITELINE( , , ))
	m_uart->set_auto_rdav(true);
	RS232_PORT(config, "rs232", default_rs232_devices, nullptr);

	SOFTWARE_LIST(config, "cass_list").set_original("trs80_cass").set_filter("3");
	SOFTWARE_LIST(config, "quik_list").set_original("trs80_quik").set_filter("3");
	SOFTWARE_LIST(config, "flop_list").set_original("trs80_flop").set_filter("3");
}

void trs80m3_state::model4(machine_config &config)
{
	model3(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &trs80m3_state::m4_mem);
	m_maincpu->set_addrmap(AS_IO, &trs80m3_state::m4_io);

	RAM(config, m_mainram, 0);
	m_mainram->set_default_size("64K");
	m_mainram->set_extra_options("16K,128K");

	ADDRESS_MAP_BANK(config, m_m4_bank, 0);
	m_m4_bank->set_map(&trs80m3_state::m4_banked_mem);
	m_m4_bank->set_endianness(ENDIANNESS_LITTLE);
	m_m4_bank->set_data_width(8);
	m_m4_bank->set_addr_width(18);
	m_m4_bank->set_stride(0x10000);

	SOFTWARE_LIST(config.replace(), "cass_list").set_original("trs80_cass").set_filter("4");
	SOFTWARE_LIST(config.replace(), "flop_list").set_original("trs80_flop").set_filter("4");
	config.device_remove("quickload");  // removed because it crashes..
	config.device_remove("quik_list");
}

void trs80m3_state::model4p(machine_config &config)
{
	model3(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &trs80m3_state::m4p_mem);
	m_maincpu->set_addrmap(AS_IO, &trs80m3_state::m4p_io);

	RAM(config, m_mainram, 0);
	m_mainram->set_default_size("64K");
	m_mainram->set_extra_options("128K");

	ADDRESS_MAP_BANK(config, m_m4p_bank, 0);
	m_m4p_bank->set_map(&trs80m3_state::m4p_banked_mem);
	m_m4p_bank->set_endianness(ENDIANNESS_LITTLE);
	m_m4p_bank->set_data_width(8);
	m_m4p_bank->set_addr_width(19);
	m_m4p_bank->set_stride(0x10000);

	SOFTWARE_LIST(config.replace(), "flop_list").set_original("trs80_flop").set_filter("4");
	config.device_remove("quickload");
	config.device_remove("cassette");
	config.device_remove("cass_list");
	config.device_remove("quik_list");
}

void trs80m3_state::cp500(machine_config &config)
{
	model3(config);
	m_maincpu->set_addrmap(AS_IO, &trs80m3_state::cp500_io);
}

/***************************************************************************

  Game driver(s)

***************************************************************************/


ROM_START(trs80m3)
/* ROMS we have and are missing:
HAVE    TRS-80 Model III Level 1 ROM (U104)
MISSING TRS-80 Model III Level 2 (ENGLISH) ROM A (U104) ver. CRC BBC4
MISSING TRS-80 Model III Level 2 (ENGLISH) ROM A (U104) ver. CRC DA75
HAVE    TRS-80 Model III Level 2 (ENGLISH) ROM A (U104) ver. CRC 9639
HAVE    TRS-80 Model III Level 2 (ENGLISH) ROM B (U105) ver. CRC 407C
MISSING TRS-80 Model III Level 2 (ENGLISH) ROM C (U106) ver. CRC 2B91 - early mfg. #80040316
MISSING TRS-80 Model III Level 2 (ENGLISH) ROM C (U106) ver. CRC 278A - no production REV A
HAVE    TRS-80 Model III Level 2 (ENGLISH) ROM C (U106) ver. CRC 2EF8 - Manufacturing #80040316 REV B
HAVE    TRS-80 Model III Level 2 (ENGLISH) ROM C (U106) ver. CRC 2F84 - Manufacturing #80040316 REV C
MISSING TRS-80 Model III Level 2 (ENGLISH) ROM C ver. CRC 2764 - Network III v1
HAVE    TRS-80 Model III Level 2 (ENGLISH) ROM C ver. CRC 276A - Network III v2
MISSING TRS-80 Model III Level 2 (BELGIUM) CRC ????
Note: Be careful when dumping rom C: if dumped on the trs-80 m3 with software, bytes 0x7e8 and 0x7e9 (addresses 0x37e8, 0x0x37e9)
      will read as 0xFF 0xFF; on the original rom, these bytes are 0x00 0x00 (for eproms) or 0xAA 0xAA (for mask roms), those two bytes are used for printer status on the trs-80 and are mapped on top of the rom; This problem should be avoided by pulling the rom chips and dumping them directly.
*/
	ROM_REGION(0x3800, "maincpu",0)
	ROM_SYSTEM_BIOS(0, "trs80m3_revc", "Level 2 bios, RomC Rev C")
	ROMX_LOAD("8041364.u104", 0x0000, 0x2000, CRC(ec0c6daa) SHA1(257cea6b9b46912d4681251019ec2b84f1b95fc8), ROM_BIOS(0)) // Label: "SCM91248C // Tandy (c) 80 // 8041364 // 8134" (Level 2 bios ROM A '9639')
	ROMX_LOAD("8040332.u105", 0x2000, 0x1000, CRC(ed4ee921) SHA1(ec0a19d4b72f71e51965de63250009c3c4e4cab3), ROM_BIOS(0)) // Label: "SCM91619P // Tandy (c) 80 // 8040332 // QQ8117", (Level 2 bios ROM B '407c')
	ROMX_LOAD("8040316c.u106", 0x3000, 0x0800, CRC(c8f79433) SHA1(6f395bba822d39d3cd2b73c8ea25aab4c4c26da7), ROM_BIOS(0)) // Label: "SCM91692P // Tandy (c) 81 // 8040316-C // QQ8220" (Level 2 bios ROM C REV C '2f84')
	ROM_SYSTEM_BIOS(1, "trs80m3_revb", "Level 2 bios, RomC Rev B")
	ROMX_LOAD("8041364.u104", 0x0000, 0x2000, CRC(ec0c6daa) SHA1(257cea6b9b46912d4681251019ec2b84f1b95fc8), ROM_BIOS(1)) // Label: "SCM91248C // Tandy (c) 80 // 8041364 // 8134" (Level 2 bios ROM A '9639')
	ROMX_LOAD("8040332.u105", 0x2000, 0x1000, CRC(ed4ee921) SHA1(ec0a19d4b72f71e51965de63250009c3c4e4cab3), ROM_BIOS(1)) // Label: "SCM91619P // Tandy (c) 80 // 8040332 // QQ8117", (Level 2 bios ROM B '407c')
	ROMX_LOAD("8040316b.u106", 0x3000, 0x0800, CRC(84a5702d) SHA1(297dca756a9d3c6fd13e0fa6f93d172ff795b520), ROM_BIOS(1)) // Label: "SCM91692P // Tandy (c) 80 // 8040316B // QQ8040" (Level 2 bios ROM C REV B '2ef8')
	ROM_SYSTEM_BIOS(2, "trs80m3_n3v2", "Level 2 bios, Network III v2 (student)")
	ROMX_LOAD("8041364.u104", 0x0000, 0x2000, CRC(ec0c6daa) SHA1(257cea6b9b46912d4681251019ec2b84f1b95fc8), ROM_BIOS(2)) // Label: "SCM91248C // Tandy (c) 80 // 8041364 // 8134" (Level 2 bios ROM A '9639')
	ROMX_LOAD("8040332.u105", 0x2000, 0x1000, CRC(ed4ee921) SHA1(ec0a19d4b72f71e51965de63250009c3c4e4cab3), ROM_BIOS(2)) // Label: "SCM91619P // Tandy (c) 80 // 8040332 // QQ8117" (Level 2 bios ROM B '407c')
	ROMX_LOAD("276a.u106", 0x3000, 0x0800, CRC(7d38720a) SHA1(bef621e5ae2a8c1f9e7f6325b7841f5ab8ab7e6a), ROM_BIOS(2)) // 2716 EPROM Label: "MOD.III // ROM C // (276A)" (Network III v2 ROM C '276a')
	ROM_SYSTEM_BIOS(3, "trs80m3_l1", "Level 1 bios")
	ROMX_LOAD("8040032.u104", 0x0000, 0x1000, CRC(6418d641) SHA1(f823ab6ceb102588d27e5f5c751e31175289291c), ROM_BIOS(3) ) // Label: "8040032 // (M) QQ8028 // SCM91616P"; Silkscreen: "TANDY // (C) '80"; (Level 1 bios)

	ROM_REGION(0x0800, "chargen",0)    /* correct for later systems; the trs80m3_l1 bios uses the non-a version of this rom, dump is pending */
	ROM_LOAD("8044316.u36", 0x0000, 0x0800, NO_DUMP) // Label: "(M) // SCM91665P // 8044316 // QQ8029" ('no-letter' revision)
	ROM_LOAD("8044316a.u36", 0x0000, 0x0800, CRC(444c8b60) SHA1(c52ee41439bd5e57c3b113ebfd61c951e2af4446)) // Label: "Tandy (C) 81 // 8044316A // 8206" (rev A)
ROM_END

// for model 4 and 4p info, see http://vt100.net/mirror/harte/Radio%20Shack/TRS-80%20Model%204_4P%20Soft%20Tech%20Ref.pdf
ROM_START(trs80m4)
	ROM_REGION(0x3800, "maincpu",0)
	ROM_LOAD("trs80m4.rom",  0x0000, 0x3800, BAD_DUMP CRC(1a92d54d) SHA1(752555fdd0ff23abc9f35c6e03d9d9b4c0e9677b)) // should be split into 3 roms, roms A, B, C, exactly like trs80m3; in fact, roms A and B are shared between both systems.

	ROM_REGION(0x0800, "chargen",0)
	ROM_LOAD("8044316a.u36", 0x0000, 0x0800, CRC(444c8b60) SHA1(c52ee41439bd5e57c3b113ebfd61c951e2af4446)) // according to parts catalog, this is the correct rom for both model 3 and 4
ROM_END

ROM_START(trs80m4p) // uses a completely different memory map scheme to the others; the trs-80 model 3 roms are loaded from a boot disk, the only rom on the machine is a bootloader; bootloader can be banked out of 0x0000-0x1000 space which is replaced with ram; see the tech ref pdf, pdf page 62
	ROM_REGION(0x3800, "maincpu",0)
	ROM_SYSTEM_BIOS(0, "trs80m4p", "Level 2 bios, gate array machine")
	ROMX_LOAD("8075332.u69", 0x0000, 0x1000, CRC(3a738aa9) SHA1(6393396eaa10a84b9e9f0cf5930aba73defc5c52), ROM_BIOS(0)) // Label: "SCM95060P // 8075332 // TANDY (C) 1983 // 8421" at location U69 (may be located at U70 on some pcb revisions)
	ROM_SYSTEM_BIOS(1, "trs80m4p_hack", "Disk loader hack")
	ROMX_LOAD("trs80m4p_loader_hack.rom", 0x0000, 0x01f8, CRC(7ff336f4) SHA1(41184f5240b4b54f3804f5a22b4d78bbba52ed1d), ROM_BIOS(1))

	ROM_REGION(0x0800, "chargen",0)
	ROM_LOAD("8049007.u103", 0x0000, 0x0800, CRC(1ac44bea) SHA1(c9426ab2b2aa5380dc97a7b9c048ccd1bbde92ca)) // Label: "SCM95987P // 8049007 // TANDY (C) 1983 // 8447" at location U103 (may be located at U43 on some pcb revisions)
ROM_END

ROM_START( cp500 )
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_LOAD("s_8407_cn62516n_cp500a_prologica_83.ci111", 0x0000, 0x4000, CRC(c2fc1b92) SHA1(0eb07baee80f1ee1f28a609eb63a9245dcb68adb))

	ROM_REGION(0x4000, "bootrom", 0)
	ROM_LOAD("s_8407_cn62516n_cp500a_prologica_83.ci111", 0x0000, 0x4000, CRC(c2fc1b92) SHA1(0eb07baee80f1ee1f28a609eb63a9245dcb68adb))

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD( "100.105.ci36", 0x0000, 0x800, CRC(1765931e) SHA1(49176ceea6cc003efa04fad2f31829b9432fe10f))
ROM_END

void trs80m3_state::init_trs80m3()
{
	m_model4 = 0;
}

void trs80m3_state::init_trs80m4()
{
	m_model4 = 2;
}

void trs80m3_state::init_trs80m4p()
{
	m_model4 = 4;
}


//    YEAR  NAME         PARENT    COMPAT    MACHINE   INPUT     CLASS          INIT             COMPANY               FULLNAME                FLAGS
COMP( 1980, trs80m3,     0,        trs80l2,  model3,   trs80m3,  trs80m3_state, init_trs80m3,  "Tandy Radio Shack", "TRS-80 Model III",        MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1980, trs80m4,     trs80m3,  0,        model4,   trs80m3,  trs80m3_state, init_trs80m4,  "Tandy Radio Shack", "TRS-80 Model 4",          MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1983, trs80m4p,    trs80m3,  0,        model4p,  trs80m4p, trs80m3_state, init_trs80m4p, "Tandy Radio Shack", "TRS-80 Model 4P",         MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)
COMP( 1982, cp500,       trs80m3,  0,        cp500,    trs80m3,  trs80m3_state, init_trs80m3,  "Prológica",         "CP-500 (PVIII REV.3)",    MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



ts3000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************************

2017-10-28 Skeleton

Televideo TS-3000. CPU is 8088. Other chips are: 8259, 8253, 8237, 8255, NS8250, uPD765AC, MM58167AN.
Crystals are: 18.432, 16.000, 24.000, 14.31818, 4.7727266. There's a barrel-type backup battery, and a bank of 8 dipswitches.
There are 25-pin serial and parallel ports, and a FDC connector. There's an undumped prom labelled "U20 V1.0" at position U20.

************************************************************************************************************************************/

#include "emu.h"
#include "cpu/i86/i86.h"


namespace {

class ts3000_state : public driver_device
{
public:
	ts3000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
//      , m_maincpu(*this, "maincpu")
	{ }

	void ts3000(machine_config &config);

private:
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

//  required_device<cpu_device> m_maincpu;
};

void ts3000_state::mem_map(address_map &map)
{
	map(0x00000, 0x0ffff).ram();
	map(0xfc000, 0xfffff).rom().region("roms", 0);
}

void ts3000_state::io_map(address_map &map)
{
}

static INPUT_PORTS_START( ts3000 )
INPUT_PORTS_END

void ts3000_state::ts3000(machine_config &config)
{
	i8088_cpu_device &maincpu(I8088(config, "maincpu", XTAL(14'318'181)/3));  // no idea of clock
	maincpu.set_addrmap(AS_PROGRAM, &ts3000_state::mem_map);
	maincpu.set_addrmap(AS_IO, &ts3000_state::io_map);
}

ROM_START( ts3000 )
	ROM_REGION( 0x4000, "roms", 0 )
	ROM_LOAD( "u25 ver 2.03 bios d.u25", 0x0000, 0x4000, CRC(abaff64c) SHA1(b2f0e73d2a25a03d5bac558580919bd0400f4fcf) ) // The D at the end is handwritten
ROM_END

} // anonymous namespace


COMP( 198?, ts3000, 0, 0, ts3000, ts3000, ts3000_state, empty_init, "Televideo", "TS-3000", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



ts802.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***************************************************************************

    Skeleton driver for Televideo TS802

    2012-11-06 Skeleton
    2014-02-07 Started adding devices

    Status:
    - TS802:  After 5 seconds, Slowly prints dots
    - TS802H: After 5 seconds, type in any 5 characters, then you get a prompt.

    TODO:
    - Almost everything

    Technical manual at:
    http://bitsavers.org/pdf/televideo/TS800A_TS802_TS802H_Maintenance_Manual_1982.pdf

    includes in-depth discussion of the inner workings of the WD1000 HDD controller.

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/z80daisy.h"
#include "machine/terminal.h"
#include "machine/z80dma.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "machine/wd_fdc.h"


namespace {

class ts802_state : public driver_device
{
public:
	ts802_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_terminal(*this, "terminal")
	{ }

	void ts802(machine_config &config);

	void init_ts802();

private:
	virtual void machine_reset() override ATTR_COLD;
	uint8_t port00_r() { return 0x80; };
	uint8_t port0c_r() { return 1; };
	uint8_t port0e_r() { return 0; };
	uint8_t port0f_r() { return (m_term_data) ? 5 : 4; };
	uint8_t port0d_r();
	void port04_w(uint8_t data);
	void port18_w(uint8_t data);
	void port80_w(uint8_t data);
	uint8_t memory_read_byte(offs_t offset);
	void memory_write_byte(offs_t offset, uint8_t data);
	uint8_t io_read_byte(offs_t offset);
	void io_write_byte(offs_t offset, uint8_t data);
	void kbd_put(u8 data);
	void ts802_io(address_map &map) ATTR_COLD;
	void ts802_mem(address_map &map) ATTR_COLD;

	uint8_t m_term_data = 0;
	address_space *m_mem = nullptr;
	address_space *m_io = nullptr;
	required_device<z80_device> m_maincpu;
	required_device<generic_terminal_device> m_terminal;
};

void ts802_state::ts802_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).bankr("bankr0").bankw("bankw0");
	map(0x1000, 0xffff).ram();
}

void ts802_state::ts802_io(address_map &map)
{
	//map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x03).r(FUNC(ts802_state::port00_r));  // DIP switches
	// 04 - written once after OS boot to bank in RAM from 0000-3FFF instead of ROM.  4000-FFFF is always RAM.
	map(0x04, 0x07).w(FUNC(ts802_state::port04_w));
	// 08-0B: Z80 CTC
	map(0x08, 0x0b).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	// 0C-0F: Z80 SIO #1
	//map(0x0c, 0x0f).rw("dart1", FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
	map(0x0c, 0x0c).r(FUNC(ts802_state::port0c_r));
	map(0x0d, 0x0d).r(FUNC(ts802_state::port0d_r)).w(m_terminal, FUNC(generic_terminal_device::write));
	map(0x0e, 0x0e).r(FUNC(ts802_state::port0e_r));
	map(0x0f, 0x0f).r(FUNC(ts802_state::port0f_r));
	// 10: Z80 DMA
	map(0x10, 0x13).rw("dma", FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	// 14-17: WD 1793
	map(0x14, 0x17).rw("fdc", FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	// 18: floppy misc.
	map(0x18, 0x1c).w(FUNC(ts802_state::port18_w));
	// 20-23: Z80 SIO #2
	map(0x20, 0x23).rw("dart2", FUNC(z80dart_device::ba_cd_r), FUNC(z80dart_device::ba_cd_w));
	// 48-4F: WD1000 harddisk controller
	// 80: LEDs
	map(0x80, 0x80).w(FUNC(ts802_state::port80_w));
}


/* Input ports */
static INPUT_PORTS_START( ts802 )
INPUT_PORTS_END

void ts802_state::port04_w(uint8_t data)
{
	membank("bankr0")->set_entry(1);
}

void ts802_state::port18_w(uint8_t data)
{
}

void ts802_state::port80_w(uint8_t data)
{
}

uint8_t ts802_state::memory_read_byte(offs_t offset)
{
	return m_mem->read_byte(offset);
}

void ts802_state::memory_write_byte(offs_t offset, uint8_t data)
{
	m_mem->write_byte(offset, data);
}

uint8_t ts802_state::io_read_byte(offs_t offset)
{
	return m_io->read_byte(offset);
}

void ts802_state::io_write_byte(offs_t offset, uint8_t data)
{
	m_io->write_byte(offset, data);
}

static void ts802_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

void ts802_state::machine_reset()
{
	membank("bankr0")->set_entry(0); // point at rom
	membank("bankw0")->set_entry(0); // always write to ram
}

uint8_t ts802_state::port0d_r()
{
	uint8_t ret = m_term_data;
	m_term_data = 0;
	return ret;
}

void ts802_state::kbd_put(u8 data)
{
	m_term_data = data;
}

#if 0
// not correct
static const z80_daisy_config daisy_chain_intf[] =
{
	{ "dart1" },
	{ "dart2" },
	{ "dma" },
	{ "ctc" },
	{ nullptr }
};
#endif

void ts802_state::init_ts802()
{
	m_mem = &m_maincpu->space(AS_PROGRAM);
	m_io = &m_maincpu->space(AS_IO);

	uint8_t *main = memregion("maincpu")->base();

	membank("bankr0")->configure_entry(1, &main[0x0000]);
	membank("bankr0")->configure_entry(0, &main[0x10000]);
	membank("bankw0")->configure_entry(0, &main[0x0000]);
}

void ts802_state::ts802(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &ts802_state::ts802_mem);
	m_maincpu->set_addrmap(AS_IO, &ts802_state::ts802_io);
	//m_maincpu->set_daisy_config(daisy_chain_intf); // causes problems
	m_maincpu->busack_cb().set("dma", FUNC(z80dma_device::bai_w));

	/* Devices */
	GENERIC_TERMINAL(config, m_terminal, 0);
	m_terminal->set_keyboard_callback(FUNC(ts802_state::kbd_put));

	z80dma_device& dma(Z80DMA(config, "dma", 16_MHz_XTAL / 4));
	dma.out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	dma.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	dma.in_mreq_callback().set(FUNC(ts802_state::memory_read_byte));
	dma.out_mreq_callback().set(FUNC(ts802_state::memory_write_byte));
	dma.in_iorq_callback().set(FUNC(ts802_state::io_read_byte));
	dma.out_iorq_callback().set(FUNC(ts802_state::io_write_byte));

	z80dart_device& dart1(Z80DART(config, "dart1", 16_MHz_XTAL / 4));
	dart1.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80dart_device& dart2(Z80DART(config, "dart2", 16_MHz_XTAL / 4));
	dart2.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80ctc_device& ctc(Z80CTC(config, "ctc", 16_MHz_XTAL / 4));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	FD1793(config, "fdc", 4'000'000 / 2);                  // unknown clock
	FLOPPY_CONNECTOR(config, "fdc:0", ts802_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
}

/* ROM definition */
ROM_START( ts802 )
	ROM_REGION(0x11000, "maincpu", 0)
	ROM_LOAD( "ts802.rom", 0x10000, 0x1000, CRC(60bd086a) SHA1(82c5b60223e0d895683d3592a56684ef2dabfba6) )
ROM_END

ROM_START( ts802h )
	ROM_REGION(0x11000, "maincpu", 0)
	ROM_LOAD( "8000050 050 2732", 0x10000, 0x1000, CRC(7054f384) SHA1(cf0a01a32283272532ed4890c3a3c2082f1618bf) )

	ROM_REGION(0x2000, "roms", 0) // not Z80 code
	ROM_LOAD( "i800000 047d.a53", 0x0000, 0x1000, CRC(94bfcbc1) SHA1(87c5f8898b0041d012e142ee7f559cb8a90f4dc1) )
	ROM_LOAD( "a64",              0x1000, 0x1000, CRC(41b5feda) SHA1(c9435a97c032ffe457bdb84d5dde8ecf3677b56c) )

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "800000-003a.a68",  0x0000, 0x0800, CRC(24eeb74d) SHA1(77900937f1492b4c5a70ba3aac55da322d403fbd) )
	ROM_LOAD( "800000-002a.a67",  0x0800, 0x0800, CRC(4b6c6e29) SHA1(c236e4625bc16062154cbebc4dbc8d62183ef9ab) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  STATE        INIT        COMPANY      FULLNAME  FLAGS
COMP( 1982, ts802,  0,      0,      ts802,   ts802, ts802_state, init_ts802, "Televideo", "TS802",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1982, ts802h, ts802,  0,      ts802,   ts802, ts802_state, init_ts802, "Televideo", "TS802H", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



ts803.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Gabriele D'Antona
/*************************************************************************************************

TeleVideo TS-803(H)
Driver by friol
Started: 27/12/2015

Addressable memory space has 2 configurations:

PAGE SEL bit in PORT0 set to 0:

  0000-2000 - 8k rom
  2000-4000 - 8k expansion rom
  4000-BFFF - videoram
  C000-FFFF - 16k cpu RAM

PAGE SEL bit in PORT0 set to 1:

  0000-dFFF - 56k ram
  e000-FFFF - OS RAM

  Z80STI:
  - provides baud-rate clock for DART ch B (serial printer)
  - merges FDC and HDC irq into the daisy chain. FDC works without it though.

  Keyboard / Z80DART:
  - Keyboard has 8048 plus undumped rom. There's no schematic.
  - Protocol: 9600 baud, 8 data bits, 2 stop bits, no parity, no handshaking.
  - Problem: every 2nd keystroke is ignored. The bios does this on purpose.
  - Problem: some of the disks cause the keyboard to produce rubbish, unable to find
             a baud rate that works.

  To Do:
  - Fix weird problem with keyboard, or better, get the rom and emulate it.
  - Hard Drive
  - Videoram has 4 banks, only first bank is currently used for display
  - Option of a further 64k of main ram.
  - 2 more Serial ports (Serial Printer & Mouse; Modem)
  - Optional RS422 board (has Z80SIO +others)
  - Diagnostic LEDs
  - Cleanup
  - The demo refers to video attributes, but there's no mention of them anywhere else

**************************************************************************************************/

#include "emu.h"
#include "machine/clock.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/z80daisy.h"
#include "machine/keyboard.h"
#include "machine/timer.h"
#include "machine/z80sio.h"
#include "machine/wd_fdc.h"
#include "machine/z80sti.h"
#include "video/mc6845.h"
#include "emupal.h"
#include "screen.h"


namespace {

class ts803_state : public driver_device
{
public:
	ts803_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_palette(*this, "palette")
		, m_maincpu(*this, "maincpu")
		, m_fdc(*this, "fdc")
		, m_floppy0(*this, "fdc:0")
		, m_floppy1(*this, "fdc:1")
		, m_p_chargen(*this, "chargen")
		, m_io_dsw(*this, "DSW")
	{ }

	void ts803(machine_config &config);

	void init_ts803();

private:
	uint8_t port10_r(offs_t offset);
	void port10_w(offs_t offset, uint8_t data);
	uint8_t porta0_r(offs_t offset);
	void porta0_w(offs_t offset, uint8_t data);
	void disk_0_control_w(uint8_t data);
	uint8_t disk_0_control_r();
	MC6845_UPDATE_ROW(crtc_update_row);
	MC6845_ON_UPDATE_ADDR_CHANGED(crtc_update_addr);
	void crtc_controlreg_w(uint8_t data);
	uint32_t screen_update_ts803(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void ts803_io(address_map &map) ATTR_COLD;
	void ts803_mem(address_map &map) ATTR_COLD;

	std::unique_ptr<uint8_t[]> m_videoram;
	std::unique_ptr<uint8_t[]> m_56kram;
	bool m_graphics_mode;
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	required_device<palette_device> m_palette;
	required_device<z80_device> m_maincpu;
	required_device<fd1793_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	required_device<floppy_connector> m_floppy1;
	required_region_ptr<u8> m_p_chargen;
	required_ioport m_io_dsw;
};

void ts803_state::ts803_mem(address_map &map)
{
	map(0x0000, 0x3fff).bankr("bankr0").bankw("bankw0");
	map(0x4000, 0xbfff).bankrw("bank4");
	map(0xc000, 0xffff).ram();
}

/*

I/0 Port Addresses

System Status Switch 1                                  00 (SW CE)
Diagnostic Indicators 1 and 2                           10 (10-1F = CTRL PORT CE)
Diagnostic Indicators 3 and 4                           11
RS-422 Control and Auto Wait                            12
Memory Bank Select                                      13
STI Device (modem)                                      20-2F (STI CE)
DART Dual Asynchronous Receiver Transmitter
Device (keyboard, printer, mouse)                       30-33 (DART CE)
RS-422 SIO Device                                       40-43 (I/O CE1)
                                                        5x (I/O CE2)
                                                        6x (PAR C/S CE)
                                                        7x (PAR DATA CE)
Floppy Disk Controller                                  80-83 (FDC CE)
Floppy Disk Drive Decoder                               90 (FDD CE)
Winchester Disk Controller Reset                        A0 (WDC RST CE)
Winchester Disk Controller                              B0-BF (WDC CE)
Graphics Controller                                     C0-CF (GIO SEL)

*/
void ts803_state::ts803_io(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x0f).portr("DSW");
	map(0x10, 0x1f).rw(FUNC(ts803_state::port10_r), FUNC(ts803_state::port10_w));
	map(0x20, 0x2f).rw("sti", FUNC(z80sti_device::read), FUNC(z80sti_device::write));
	map(0x30, 0x33).rw("dart", FUNC(z80dart_device::cd_ba_r), FUNC(z80dart_device::cd_ba_w));
	map(0x80, 0x83).rw(m_fdc, FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	map(0x90, 0x9f).rw(FUNC(ts803_state::disk_0_control_r), FUNC(ts803_state::disk_0_control_w));
	map(0xa0, 0xbf).rw(FUNC(ts803_state::porta0_r), FUNC(ts803_state::porta0_w));
	map(0xc0, 0xc0).rw("crtc", FUNC(sy6545_1_device::status_r), FUNC(sy6545_1_device::address_w));
	map(0xc2, 0xc2).rw("crtc", FUNC(sy6545_1_device::register_r), FUNC(sy6545_1_device::register_w));
	map(0xc4, 0xc4).w(FUNC(ts803_state::crtc_controlreg_w));
}

/* Input ports */
static INPUT_PORTS_START( ts803 )
	PORT_START("DSW")
	PORT_DIPNAME( 0x07, 0x00, "Printer Baud" ) PORT_DIPLOCATION("SW:1,2,3")
	PORT_DIPSETTING(    0x00, "9600" )
	PORT_DIPSETTING(    0x01, "4800" )
	PORT_DIPSETTING(    0x02, "2400" )
	PORT_DIPSETTING(    0x03, "1200" )
	PORT_DIPSETTING(    0x04, "600" )
	PORT_DIPSETTING(    0x05, "300" )
	PORT_DIPSETTING(    0x06, "150" )
	PORT_DIPSETTING(    0x07, "75" )
	PORT_DIPNAME( 0x08, 0x00, "Model" ) PORT_DIPLOCATION("SW:4")
	PORT_DIPSETTING(    0x00, "TS803" )
	PORT_DIPSETTING(    0x08, "TS803H" )
	PORT_DIPNAME( 0x10, 0x00, "Model specific #1" ) PORT_DIPLOCATION("SW:5")
	PORT_DIPSETTING(    0x00, "On - TS803 leave here, TS803H Local" )
	PORT_DIPSETTING(    0x10, "Off - TS803H remote" )
	PORT_DIPNAME( 0x20, 0x00, "Model specific #2" ) PORT_DIPLOCATION("SW:6")
	PORT_DIPSETTING(    0x00, "On - TS803 leave here, TS803H 2-head" )
	PORT_DIPSETTING(    0x20, "Off - TS803H 4-head" )
	PORT_DIPNAME( 0x40, 0x00, "Mains frequency" ) PORT_DIPLOCATION("SW:7")
	PORT_DIPSETTING(    0x00, "60 Hz" )
	PORT_DIPSETTING(    0x40, "50 Hz" )
	PORT_DIPNAME( 0x80, 0x80, "Model specific #3" ) PORT_DIPLOCATION("SW:8")
	PORT_DIPSETTING(    0x00, "On" )
	PORT_DIPSETTING(    0x80, "Off - TS803H leave here" )
	PORT_DIPNAME( 0x100,0x100, "Reverse Video" ) PORT_DIPLOCATION("SW:9")
	PORT_DIPSETTING(    0x000, "Reverse" )
	PORT_DIPSETTING(    0x100, "Normal" )
	PORT_BIT( 0x200, IP_ACTIVE_LOW, IPT_UNUSED ) // SW10 is not connected to anything
INPUT_PORTS_END

/* disk drive */

static void ts803_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

void ts803_state::disk_0_control_w(uint8_t data)
{
/*
d0 ready
d1 motor on
d2 Side select (active low)
d3 Double density (active low)
d4 Drive select 0 (active low)
d5 Drive select 1 (active low)
d6 Drive select 2 (active low)
d7 Drive select 3 (active low)
*/
	if ((data & 0xc0)!=0xc0) return;
	floppy_image_device *floppy = nullptr;
	if (BIT(data, 4)==0)
		floppy = m_floppy0->get_device();
	else
	if (BIT(data, 5)==0)
		floppy = m_floppy1->get_device();

	m_fdc->set_floppy(floppy);

	if (floppy)
	{
		floppy->mon_w(BIT(data, 1));
		floppy->ss_w(BIT(data, 2) ? 0: 1);
	}
	m_fdc->dden_w(BIT(data, 3));
}

uint8_t ts803_state::disk_0_control_r()
{
	printf("Disk0 control register read\n");
	return 0xff;
}

uint8_t ts803_state::porta0_r(offs_t offset)
{
	offset += 0xa0;
	switch(offset)
	{
		case 0xb2:
			printf("B2 WDC read\n");
			return 0x11;

		case 0xb3:
			printf("B3 WDC read\n");
			return 0x15;

		case 0xb4:
			printf("B4 WDC read\n");
			return 0x55;

		case 0xb5:
			printf("B5 WDC read\n");
			return 0x01;

		case 0xb6:
			printf("B6 WDC read\n");
			return 0x25;

	}

	return 0x00;
}

void ts803_state::porta0_w(offs_t offset, uint8_t data)
{
	offset += 0xa0;
	switch (offset)
	{
		case 0xc4:
			//printf("Control Register for Alpha or Graphics Mode Selection\n");
			break;

		default:
			break;
	}
}

uint8_t ts803_state::port10_r(offs_t offset)
{
	offset += 0x10;
	printf("Port read [%x]\n",offset);

	return 0xff;
}

void ts803_state::port10_w(offs_t offset, uint8_t data)
{
	offset += 0x10;
	switch (offset)
	{
		case 0x10:
			data &= 3;
			printf("Writing to diagnostic indicators 1 & 2: %X\n", data);
			break;

		case 0x11:
			data &= 3;
			printf("Writing to diagnostic indicators 3 & 4: %X\n", data);
			break;

		case 0x12:
			data &= 3;
			printf("RS-422 control: %X\n", data);
			break;

		case 0x13:
			data &= 3;
			if (BIT(data, 1)==0)
			{
				membank("bankr0")->set_entry(BIT(data, 0));
				membank("bank4")->set_entry(BIT(data, 0));
			}
			else
				printf("Error: unknown memory config: %X.\n", data);

			break;

		default:
			printf("unknown port [%2.2x] write of [%2.2x]\n",offset,data);
			break;
	}
}

MC6845_ON_UPDATE_ADDR_CHANGED( ts803_state::crtc_update_addr )
{
	//printf("CRTC::address update [%x]\n",address);
}

MC6845_UPDATE_ROW( ts803_state::crtc_update_row )
{
	bool rv = BIT(m_io_dsw->read(), 8) ? 0 : 1;
	rgb_t const *const pens = m_palette->palette()->entry_list_raw();
	uint32_t *p = &bitmap.pix(y);

	for (uint16_t x = 0; x < x_count; x++)
	{
		uint8_t inv = (rv ^ (x == cursor_x)) ? 0xff : 0;

		uint8_t gfx;
		if (m_graphics_mode)
		{
			uint16_t mem = (ra*0x2000 + ma + x) & 0x7fff;
			gfx = m_videoram[mem] ^ inv;
		}
		else
		{
			uint16_t mem = 0x1800 + ((ma + x) & 0x7ff);
			uint8_t chr = m_videoram[mem];
			gfx = (ra > 7) ? inv : m_p_chargen[(chr<<3) | ((ra+1)&7)] ^ inv;
		}

		/* Display a scanline of a character (8 pixels) */
		*p++ = pens[BIT(gfx, 7)];
		*p++ = pens[BIT(gfx, 6)];
		*p++ = pens[BIT(gfx, 5)];
		*p++ = pens[BIT(gfx, 4)];
		*p++ = pens[BIT(gfx, 3)];
		*p++ = pens[BIT(gfx, 2)];
		*p++ = pens[BIT(gfx, 1)];
		*p++ = pens[BIT(gfx, 0)];
	}
}

void ts803_state::crtc_controlreg_w(uint8_t data)
{
/*
Bit 0 = 0 alpha mode
                1 graphics mode
Bit 1 = 0 page 1 (alpha mode only)
                1 page 2 (alpha mode only)
Bit 2 = 0 alpha memory access (round off)
                1 graphics memory access (normal CPU address)
*/

	//printf("CRTC::c4 write [%2x]\n",data);
	m_graphics_mode = BIT(data, 0);
}

void ts803_state::machine_start()
{
	//save these 2 so we can examine them in the debugger
	save_pointer(NAME(m_videoram), 0x8000);
	save_pointer(NAME(m_56kram), 0xc000);
}

void ts803_state::machine_reset()
{
	m_graphics_mode = false;

	membank("bankr0")->set_entry(0);
	membank("bankw0")->set_entry(0);
	membank("bank4")->set_entry(0);
}

void ts803_state::init_ts803()
{
	m_videoram = std::make_unique<uint8_t[]>(0x8000);
	m_56kram = std::make_unique<uint8_t[]>(0xc000);

	uint8_t *rom = memregion("roms")->base();
	membank("bankr0")->configure_entry(0, &rom[0]); // rom
	membank("bankr0")->configure_entry(1, m_56kram.get()); // ram
	membank("bankw0")->configure_entry(0, m_56kram.get()); // ram
	membank("bank4")->configure_entry(0, m_videoram.get()); // vram
	membank("bank4")->configure_entry(1, m_56kram.get()+0x4000); // ram
}

/* Interrupt priority:
0: RS-422 option board (highest priority)
1: Z80A DART (RS-232C serial I/O)
2: Z80 STI (RS 232C modem port)
3: FD 1793 floppy disk controller
4: Winchester disk controller board
5: Time-of-day clock (no info in the manual about this)
Interrupts 0 through 2 are prioritized in a daisy-chain
arrangement. Interrupts 3 through 5 are wired to the Z80 STI
interrupt input pins. */
static const z80_daisy_config daisy_chain[] =
{
	//{ "rs422" }, // not emulated
	{ "dart" },
	{ "sti" },
	{ nullptr }
};

void ts803_state::ts803(machine_config &config)
{
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &ts803_state::ts803_mem);
	m_maincpu->set_addrmap(AS_IO, &ts803_state::ts803_io);
	m_maincpu->set_daisy_config(daisy_chain);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_refresh_hz(60);
	screen.set_size(640,240);
	screen.set_visarea(0, 640-1, 0, 240-1);
	screen.set_screen_update("crtc", FUNC(sy6545_1_device::screen_update));
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	/* crtc */
	sy6545_1_device &crtc(SY6545_1(config, "crtc", 13608000 / 8));
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(8);
	crtc.set_update_row_callback(FUNC(ts803_state::crtc_update_row));
	crtc.set_on_update_addr_change_callback(FUNC(ts803_state::crtc_update_addr));

	clock_device &sti_clock(CLOCK(config, "sti_clock", 16_MHz_XTAL / 13));
	sti_clock.signal_handler().set("sti", FUNC(z80sti_device::tc_w));
	sti_clock.signal_handler().append("sti", FUNC(z80sti_device::rc_w));

	clock_device &dart_clock(CLOCK(config, "dart_clock", 16_MHz_XTAL / 13 / 8));
	dart_clock.signal_handler().set("dart", FUNC(z80dart_device::txca_w));
	dart_clock.signal_handler().append("dart", FUNC(z80dart_device::rxca_w));

	z80sti_device& sti(Z80STI(config, "sti", 16_MHz_XTAL / 4));
	sti.out_tbo_cb().set("dart", FUNC(z80dart_device::rxtxcb_w));
	sti.out_int_cb().set_inputline("maincpu", INPUT_LINE_IRQ0);

	z80dart_device& dart(Z80DART(config, "dart", 16_MHz_XTAL / 4));
	dart.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	dart.out_txda_callback().set("rs232", FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "keyboard"));
	rs232.rxd_handler().set("dart", FUNC(z80dart_device::rxa_w));

	/* floppy disk */
	FD1793(config, m_fdc, 1_MHz_XTAL);
	m_fdc->intrq_wr_callback().set("sti", FUNC(z80sti_device::i7_w));
	FLOPPY_CONNECTOR(config, "fdc:0", ts803_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", ts803_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
}

/* ROM definition */
ROM_START( ts803h )
	ROM_REGION(0x4000, "roms", ROMREGION_ERASEFF) // includes space for optional expansion rom
	ROM_LOAD( "180001-37 rev d 803 5 23 84.a57", 0x0000, 0x2000, CRC(0aa658a7) SHA1(42d0a89c2ff9b6588cd88bdb1f800fac540dccbb) )

	ROM_REGION(0x0100, "proms", 0)
	ROM_LOAD( "8000134.a59", 0x0000, 0x0100, CRC(231fe6d6) SHA1(3c052ba4b74547e0e2451fa1ae67bbcb83a18bab) )

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD( "803h_vid.a119", 0x0000, 0x0800, CRC(d5ce2814) SHA1(ce527479464757223dffac384a85ab74b174952c) )
ROM_END

} // anonymous namespace


//   YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY      FULLNAME  FLAGS
COMP(1983, ts803h, 0,      0,      ts803,   ts803, ts803_state, init_ts803, "Televideo", "TS803H", MACHINE_NOT_WORKING )



ts816.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***************************************************************************

2013-09-10 Skeleton driver for Televideo TS816

TODO:
- Connect up the devices to each other
- Connect up RS232 terminal instead of parallel one
- Connect centronics printer to PIO
- 4 diagnostic LEDs
- Hard Drive
- Tape Drive
- Get a good dump of the rom. If the undocumented DSW is enabled, it
  calls up code in the missing half of the rom. Also it isn't possible
  at the moment to get any useful response to commands.

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/terminal.h"
#include "machine/z80daisy.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"
#include "machine/z80dma.h"


namespace {

class ts816_state : public driver_device
{
public:
	ts816_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_terminal(*this, "terminal")
	{ }

	void ts816(machine_config &config);

	void init_ts816();

private:
	void kbd_put(u8 data);
	uint8_t keyin_r();
	uint8_t status_r();
	void port68_w(uint8_t data);
	void port78_w(uint8_t data);
	void porte0_w(uint8_t data);
	void portf0_w(uint8_t data);

	void ts816_io(address_map &map) ATTR_COLD;
	void ts816_mem(address_map &map) ATTR_COLD;

	uint8_t m_term_data = 0;
	uint8_t m_status = 0;
	bool m_2ndbank = false;
	bool m_endram = false;
	void set_banks();
	virtual void machine_reset() override ATTR_COLD;
	required_device<z80_device> m_maincpu;
	required_device<generic_terminal_device> m_terminal;
};

void ts816_state::ts816_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).bankr("bankr0").bankw("bankw0");
	map(0x4000, 0xdfff).bankrw("bank1");
	map(0xe000, 0xffff).bankrw("bank2");
}

void ts816_state::ts816_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x00); // Tape status byte 1
	map(0x01, 0x01); // Tape status byte 2 and diagnostics mode
	map(0x02, 0x02); // Hard Disk status
	map(0x03, 0x03); // Hard Disk output latch
	map(0x04, 0x04); // Tape output latch byte 2
	map(0x05, 0x05); // Tape output latch byte 1
	map(0x07, 0x07); // Indicator load (LED)
	map(0x10, 0x13).rw("sio1", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w)); // SIO 1 for user 1 & 2
	map(0x18, 0x1b).rw("sio5", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w)); // SIO 5 for user 9 & 10
	map(0x20, 0x23).rw("sio2", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w)); // SIO 2 for user 3 & 4
	map(0x28, 0x2b).rw("sio6", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w)); // SIO 6 for user 11 & 12
	map(0x30, 0x33).rw("sio3", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w)); // SIO 3 for user 5 & 6
	map(0x38, 0x3b).rw("sio7", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w)); // SIO 7 for user 13 & 14
	map(0x40, 0x43).rw("sio4", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w)); // SIO 4 for user 7 & 8
	map(0x48, 0x4b).rw("sio8", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w)); // SIO 8 for user 15 & 16
	//map(0x50, 0x53) // SIO 0 for RS232 1 and part of tape interface
	map(0x50, 0x50).r(FUNC(ts816_state::keyin_r)).w(m_terminal, FUNC(generic_terminal_device::write));
	map(0x52, 0x52).r(FUNC(ts816_state::status_r));
	map(0x58, 0x5b).rw("sio9", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w)); // SIO 9 for RS232 2 & 3
	map(0x60, 0x60).portr("DSW");
	map(0x68, 0x68).w(FUNC(ts816_state::port68_w)); // set 2nd bank latch
	map(0x70, 0x78).w(FUNC(ts816_state::port78_w)); // reset 2nd bank latch (manual can't decide between 70 and 78, so we take both)
	map(0x80, 0x83).rw("ctc1", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write)); // CTC 1 (ch 0 baud A)
	map(0x90, 0x93).rw("dma", FUNC(z80dma_device::read), FUNC(z80dma_device::write)); // DMA
	map(0xA0, 0xA0); // WDC status / command
	map(0xA1, 0xA1); // WDC data
	map(0xB0, 0xB0).noprw(); // undocumented, written to at @0707 and @0710
	map(0xC0, 0xC3).rw("ctc2", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write)); // CTC 2 (ch 0 baud B, ch 1 baud C)
	map(0xD0, 0xD3).rw("pio", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0xE0, 0xE0).w(FUNC(ts816_state::porte0_w)); // set ENDRAM memory banking
	map(0xF0, 0xF0).w(FUNC(ts816_state::portf0_w)); // reset ENDRAM memory banking
}


/* Input ports */
static INPUT_PORTS_START( ts816 )
	PORT_START("DSW") //
	PORT_DIPNAME( 0x07, 0x01, "System Terminal") // read at @0368
	PORT_DIPSETTING(    0x00, "19200 baud")
	PORT_DIPSETTING(    0x01, "9600 baud")
	PORT_DIPSETTING(    0x02, "4800 baud")
	PORT_DIPSETTING(    0x03, "2400 baud")
	PORT_DIPSETTING(    0x04, "1200 baud")
	PORT_DIPSETTING(    0x05, "600 baud")
	PORT_DIPSETTING(    0x06, "300 baud")
	PORT_DIPSETTING(    0x07, "150 baud")
	PORT_DIPNAME( 0x80, 0x00, "Operation Switch") // this switch checked @006F (undocumented)
	PORT_DIPSETTING(    0x80, DEF_STR(On))
	PORT_DIPSETTING(    0x00, DEF_STR(Off))
INPUT_PORTS_END


uint8_t ts816_state::keyin_r()
{
	uint8_t ret = m_term_data;
	m_term_data = 0;
	return ret;
}

uint8_t ts816_state::status_r()
{
	if (m_status)
	{
		m_status--;
		return 5;
	}
	else
		return 4;
}

void ts816_state::port68_w(uint8_t data)
{
	m_2ndbank = 1;
	set_banks();
}

void ts816_state::port78_w(uint8_t data)
{
	m_2ndbank = 0;
	set_banks();
}

void ts816_state::porte0_w(uint8_t data)
{
	m_endram = 1;
	set_banks();
}

void ts816_state::portf0_w(uint8_t data)
{
	m_endram = 0;
	set_banks();
}

void ts816_state::set_banks()
{
	if (!m_2ndbank)
	{
		if (!m_endram)
		{
			// bootup setting
			membank("bankr0")->set_entry(2); // point at rom
			membank("bankw0")->set_entry(0);
			membank("bank1")->set_entry(0);
			membank("bank2")->set_entry(0);
		}
		else
		{
			// 64k ram (lower half)
			membank("bankr0")->set_entry(0);
			membank("bankw0")->set_entry(0);
			membank("bank1")->set_entry(0);
			membank("bank2")->set_entry(0);
		}
	}
	else
	{
		if (!m_endram)
		{
			// not documented, so assuming roms with ram (upper half)
			membank("bankr0")->set_entry(2);
			membank("bankw0")->set_entry(1);
			membank("bank1")->set_entry(1);
			membank("bank2")->set_entry(1);
		}
		else
		{
			// split of upper and lower ram (not documented if ram has a new address, assuming not)
			membank("bankr0")->set_entry(1);
			membank("bankw0")->set_entry(1);
			membank("bank1")->set_entry(1);
			membank("bank2")->set_entry(0);
		}
	}
}

void ts816_state::kbd_put(u8 data)
{
	m_term_data = data;
	m_status = 3;
}

void ts816_state::machine_reset()
{
	m_2ndbank = 0;
	m_endram = 0;
	m_term_data = 0;
	m_status = 1;
	set_banks();
	m_maincpu->reset();
}

// correct order yet to be determined
static const z80_daisy_config daisy_chain[] =
{
	{ "dma" },
	{ "pio" },
	{ "ctc1" },
	{ "ctc2" },
//  { "sio0" },
	{ "sio1" },
	{ "sio2" },
	{ "sio3" },
	{ "sio4" },
	{ "sio5" },
	{ "sio6" },
	{ "sio7" },
	{ "sio8" },
	{ "sio9" },
	{ nullptr }
};

void ts816_state::init_ts816()
{
	uint8_t *roms = memregion("roms")->base();
	uint8_t *rams = memregion("rams")->base();

	// 0000-3FFF
	membank("bankr0")->configure_entry(2, &roms[0x00000]); // roms
	membank("bankr0")->configure_entry(0, &rams[0x00000]);
	membank("bankr0")->configure_entry(1, &rams[0x10000]);
	membank("bankw0")->configure_entry(0, &rams[0x00000]);
	membank("bankw0")->configure_entry(1, &rams[0x10000]);
	// 4000-DFFF
	membank("bank1")->configure_entry(0, &rams[0x04000]);
	membank("bank1")->configure_entry(1, &rams[0x14000]);
	// E000-FFFF
	membank("bank2")->configure_entry(0, &rams[0x0e000]);
	membank("bank2")->configure_entry(1, &rams[0x1e000]);
}

void ts816_state::ts816(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(16'000'000) / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &ts816_state::ts816_mem);
	m_maincpu->set_addrmap(AS_IO, &ts816_state::ts816_io);
	m_maincpu->set_daisy_config(daisy_chain);
	m_maincpu->busack_cb().set("dma", FUNC(z80dma_device::bai_w));

	/* video hardware */
	GENERIC_TERMINAL(config, m_terminal, 0);
	m_terminal->set_keyboard_callback(FUNC(ts816_state::kbd_put));

	//z80sio_device& sio0(Z80SIO(config, "sio0", XTAL(16'000'000) / 4));
	//sio0.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	z80sio_device& sio1(Z80SIO(config, "sio1", XTAL(16'000'000) / 4));
	sio1.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	z80sio_device& sio2(Z80SIO(config, "sio2", XTAL(16'000'000) / 4));
	sio2.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	z80sio_device& sio3(Z80SIO(config, "sio3", XTAL(16'000'000) / 4));
	sio3.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	z80sio_device& sio4(Z80SIO(config, "sio4", XTAL(16'000'000) / 4));
	sio4.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	z80sio_device& sio5(Z80SIO(config, "sio5", XTAL(16'000'000) / 4));
	sio5.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	z80sio_device& sio6(Z80SIO(config, "sio6", XTAL(16'000'000) / 4));
	sio6.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	z80sio_device& sio7(Z80SIO(config, "sio7", XTAL(16'000'000) / 4));
	sio7.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	z80sio_device& sio8(Z80SIO(config, "sio8", XTAL(16'000'000) / 4));
	sio8.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	z80sio_device& sio9(Z80SIO(config, "sio9", XTAL(16'000'000) / 4));
	sio9.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80pio_device& pio(Z80PIO(config, "pio", XTAL(16'000'000) / 4));
	pio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	//pio.in_pa_callback().set(FUNC(ts816_state::porta_r));
	//pio.in_pb_callback().set(FUNC(ts816_state::portb_r));
	//pio.out_pb_callback().set(FUNC(ts816_state::portb_w));

	z80ctc_device& ctc1(Z80CTC(config, "ctc1", XTAL(16'000'000) / 4));
	ctc1.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80ctc_device& ctc2(Z80CTC(config, "ctc2", XTAL(16'000'000) / 4));
	ctc2.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80dma_device& dma(Z80DMA(config, "dma", XTAL(16'000'000) / 4));
	dma.out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	dma.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
}

/* ROM definition */
ROM_START( ts816 )
	ROM_REGION(0x4000, "roms", 0)
	ROM_LOAD( "81640v11.rom", 0x0000, 0x1000, BAD_DUMP CRC(295a15e7) SHA1(6f49078ab3cd49aecd2afafcbed3af0e3bcfd48c) ) // both halves identical

	ROM_REGION(0x20000, "rams", ROMREGION_ERASEFF)
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  STATE        INIT        COMPANY      FULLNAME  FLAGS
COMP( 1980, ts816, 0,      0,      ts816,   ts816, ts816_state, init_ts816, "Televideo", "TS816",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



tschess.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

SciSys Travel Sensor Chess (aka Travel Sensor)

The chess engine was written by Mark Taylor, employee of Intelligent Software
(formerly known as Philidor Software). The I/O is very similar to CXG Sensor
Computachess (see cxg/computachess.cpp).

Hardware notes:
- PCB label: SCISYS TC-A, 201148
- Hitachi 44801A85 MCU @ ~400kHz (R=91K) or ~350Hz (R=150K)
- piezo, 21 LEDs, button sensors chessboard

44801A85 MCU is used in:
- SciSys Travel Sensor Chess
- SciSys Travel Mate Chess
- SciSys Chess Partner 5000
- SciSys Chess Partner 6000

TODO:
- add memory switch (it goes to the HLT pin)

*******************************************************************************/

#include "emu.h"

#include "cpu/hmcs40/hmcs40.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "saitek_tschess.lh"


namespace {

class tschess_state : public driver_device
{
public:
	tschess_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	void tschess(machine_config &config);

	// New Game button is directly tied to MCU reset
	DECLARE_INPUT_CHANGED_MEMBER(reset_button) { m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE); }

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<hmcs40_cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<2> m_inputs;

	u8 m_inp_mux = 0;

	template<int N> void mux_w(u8 data);
	void control_w(u16 data);
	u16 input_r();
};

void tschess_state::machine_start()
{
	save_item(NAME(m_inp_mux));
}



/*******************************************************************************
    I/O
*******************************************************************************/

template<int N>
void tschess_state::mux_w(u8 data)
{
	// R2x,R3x: input mux, led data
	m_inp_mux = (m_inp_mux & ~(0xf << (N*4))) | ((data ^ 0xf) << (N*4));
	m_display->write_mx(m_inp_mux);
}

void tschess_state::control_w(u16 data)
{
	// D1-D3: led select
	m_display->write_my(~data >> 1 & 7);

	// D4: speaker out
	m_dac->write(BIT(data, 4));
}

u16 tschess_state::input_r()
{
	u16 data = 0;

	// D6,D7: read buttons
	for (int i = 0; i < 2; i++)
		if (m_inp_mux & m_inputs[i]->read())
			data |= 0x40 << i;

	// D8-D15: read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_file(i ^ 7) << 8;

	return ~data;
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( tschess )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("White/Black")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")

	PORT_START("IN.1")
	PORT_BIT(0x0f, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Compute")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Set Up")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Sound")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tschess_state::reset_button), 0) PORT_NAME("New Game")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void tschess_state::tschess(machine_config &config)
{
	// basic machine hardware
	HD44801(config, m_maincpu, 400'000); // approximation
	m_maincpu->write_r<2>().set(FUNC(tschess_state::mux_w<0>));
	m_maincpu->write_r<3>().set(FUNC(tschess_state::mux_w<1>));
	m_maincpu->write_d().set(FUNC(tschess_state::control_w));
	m_maincpu->read_d().set(FUNC(tschess_state::input_r));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(3, 8);
	config.set_default_layout(layout_saitek_tschess);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( tschess )
	ROM_REGION( 0x2000, "maincpu", 0 )
	ROM_LOAD("44801a85_scisys_tc-1982.u1", 0x0000, 0x2000, CRC(3ed0253a) SHA1(a3352758285292cfb0ad66e095cc951113332ced) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1982, tschess, 0,      0,      tschess, tschess, tschess_state, empty_init, "SciSys / Intelligent Software", "Travel Sensor Chess", MACHINE_SUPPORTS_SAVE )



tsconf.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Andrei I. Holub
/***************************************************************************

see: pentevo.cpp

Features (TS-Configuration):
- Resolutions: 360x288, 320x240, 320x200, 256x192
- Hardware scrolled graphic planes
- 256 and 16 indexed colors per pixel
- Programmable color RAM with RGB555 color space and 256 cells
- Text mode with loadable font and hardware vertical scroll
- Up to 256 graphic screens

- Up to 85 sprites per line
- Sprites sized from 8x8 to 64x64 pixels
- Up to 3 sprite planes
- Up to 2 tile planes with 8x8 pixels tiles
- Up to 16 palettes for sprites per line
- Up to 4 palettes for tiles per line for each tile plane

- DRAM-to-Device, Device-to-DRAM and DRAM-to-DRAM DMA Controller

Revisions:
    tsconf: Initial release for ZX Evolution baser on Altera's FPGA 50K
    tsconf2: Requires 100K FPGA and has extra feature set like 'Copper'

Refs:
TsConf: https://github.com/tslabs/zx-evo/blob/master/pentevo/docs/TSconf/tsconf_en.md
        https://github.com/tslabs/zx-evo/raw/master/pentevo/docs/TSconf/TSconf.xls
FAQ-RUS: https://forum.tslabs.info/viewtopic.php?f=35&t=157
    ROM: https://github.com/tslabs/zx-evo/blob/master/pentevo/rom/bin/ts-bios.rom (validated on: 2021-12-14)

 ****************************************************************************/

#include "emu.h"
#include "tsconf.h"

#include "bus/spectrum/ay/slot.h"
#include "bus/rs232/rs232.h"
#include "bus/spectrum/zxbus/bus.h"
#include "cpu/z80/z80.h"
#include "speaker.h"


ALLOW_SAVE_TYPE(tsconf_state::gluk_ext);


TILE_GET_INFO_MEMBER(tsconf_state::get_tile_info_txt)
{
	u8 *m_row_location = &m_ram->pointer()[get_vpage_offset() + (tile_index / tilemap.cols() * 256)];
	u8 col = tile_index % tilemap.cols();
	u8 symbol = m_row_location[col];
	tileinfo.set(TM_TS_CHAR, symbol, 0, 0);
}

template <u8 Layer>
TILE_GET_INFO_MEMBER(tsconf_state::get_tile_info_16c)
{
	const u8 col_offset = (tile_index & 0x03f) << 1;
	const u16 row_offset = (tile_index & 0xfc0) << 2;

	u8 *tile_info_addr = &m_ram->pointer()[(m_regs[T_MAP_PAGE] << 14) | row_offset | (Layer ? 0x80 : 0x00) | col_offset];
	u8 hi = tile_info_addr[1];

	u16 tile = ((u16(hi) & 0x0f) << 8) | tile_info_addr[0];
	u8 pal = (BIT(m_regs[PAL_SEL], 4 + Layer * 2, 2) << 2) | BIT(hi, 4, 2);
	tileinfo.set(TM_TILES0 + Layer, tile, pal, TILE_FLIPYX(BIT(hi, 6, 2)));
	tileinfo.category = tile == 0 ? 2 : 1;
}

void tsconf_state::tsconf_mem(address_map &map)
{
	map(0x0000, 0x3fff).rw(FUNC(tsconf_state::tsconf_ram_bank_r<0>), FUNC(tsconf_state::tsconf_bank_w<0>));
	map(0x0000, 0x3fff).view(m_bank0_rom);
	m_bank0_rom[0](0x0000, 0x3fff).bankr(m_bank_rom[0]);

	map(0x4000, 0x7fff).rw(FUNC(tsconf_state::tsconf_ram_bank_r<1>), FUNC(tsconf_state::tsconf_bank_w<1>));
	map(0x8000, 0xbfff).rw(FUNC(tsconf_state::tsconf_ram_bank_r<2>), FUNC(tsconf_state::tsconf_bank_w<2>));
	map(0xc000, 0xffff).rw(FUNC(tsconf_state::tsconf_ram_bank_r<3>), FUNC(tsconf_state::tsconf_bank_w<3>));
}

void tsconf_state::tsconf_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0000).mirror(0x7ffd).w(FUNC(tsconf_state::tsconf_port_7ffd_w));
	map(0x001f, 0x001f).mirror(0xff00).r(FUNC(tsconf_state::tsconf_port_xx1f_r));
	map(0x0057, 0x0057).mirror(0xff00).rw(FUNC(tsconf_state::tsconf_port_57_zctr_r), FUNC(tsconf_state::tsconf_port_57_zctr_w)); // spi config
	map(0x0077, 0x0077).mirror(0xff00).rw(FUNC(tsconf_state::tsconf_port_77_zctr_r), FUNC(tsconf_state::tsconf_port_77_zctr_w)); // spi data

	// RS-232
	map(0x00ef, 0x00ef).mirror(0xff00).rw(m_uart, FUNC(tsconf_rs232_device::dr_r), FUNC(tsconf_rs232_device::dr_w)); // 0x00ef..0xbfef
	map(0xc0ef, 0xc0ef).mirror(0x3f00).unmaprw();
	map(0xc0ef, 0xc0ef).select(0x0f00)
		.lr8(NAME([this](offs_t offset) -> u8 { return m_uart->reg_r(offset >> 8); }))
		.lw8(NAME([this](offs_t offset, u8 data) { m_uart->reg_w(offset >> 8, data); }));

	map(0x00fe, 0x00fe).select(0xff00).rw(FUNC(tsconf_state::spectrum_ula_r), FUNC(tsconf_state::tsconf_ula_w));
	map(0x00af, 0x00af).select(0xff00).rw(FUNC(tsconf_state::tsconf_port_xxaf_r), FUNC(tsconf_state::tsconf_port_xxaf_w));
	map(0xfadf, 0xfadf).lr8(NAME([this]() -> u8 { return 0x80 | (m_io_mouse[2]->read() & 0x07); }));
	map(0xfbdf, 0xfbdf).lr8(NAME([this]() -> u8 { return  m_io_mouse[0]->read(); }));
	map(0xffdf, 0xffdf).lr8(NAME([this]() -> u8 { return ~m_io_mouse[1]->read(); }));
	map(0x8ff7, 0x8ff7).select(0x7000).w(FUNC(tsconf_state::tsconf_port_f7_w)); // 3:bff7 5:dff7 6:eff7
	map(0xbff7, 0xbff7).r(FUNC(tsconf_state::tsconf_port_f7_r));
	map(0x00fb, 0x00fb).mirror(0xff00).w(m_dac, FUNC(dac_byte_interface::data_w));
	map(0x80fd, 0x80fd).mirror(0x3f00).w("ay_slot", FUNC(ay_slot_device::data_w));
	map(0xc0fd, 0xc0fd).mirror(0x3f00).rw("ay_slot", FUNC(ay_slot_device::data_r), FUNC(ay_slot_device::address_w));

	map(0x0000, 0xffff).view(m_io_shadow_view);
	m_io_shadow_view[0]; // !Shadow

	// IO: Shadow
	m_io_shadow_view[1](0x0000, 0xffff).m(m_beta, FUNC(tsconf_beta_device::tsconf_beta_io));
	subdevice<zxbus_device>("zxbus")->set_io_space(m_io_shadow_view[0], m_io_shadow_view[1]);
}

void tsconf_state::tsconf_switch(address_map &map)
{
	map(0x0000, 0x3fff).r(FUNC(tsconf_state::beta_neutral_r)); // Overlap with next because we want real addresses on the 3e00-3fff range
	map(0x3d00, 0x3dff).r(FUNC(tsconf_state::beta_enable_r));
	map(0x4000, 0xffff).r(FUNC(tsconf_state::beta_disable_r));
}

static const gfx_layout spectrum_charlayout =
{
	8, 8,          // 8 x 8 characters
	96,            // 96 characters
	1,             // 1 bits per pixel
	{0},           // no bitplanes
	{STEP8(0, 1)}, // x offsets
	{STEP8(0, 8)}, // y offsets
	8 * 8          // every char takes 8 bytes
};

static const gfx_layout tsconf_charlayout =
{
	8, 8,
	256,
	1,
	{0},
	{STEP8(0, 1)},
	{STEP8(0, 8)},
	8 * 8
};

static GFXDECODE_START(gfx_tsconf)
	GFXDECODE_ENTRY("maincpu", 0, tsconf_charlayout, 0xf7, 1)         // TM_TS_CHAR : TXT
	GFXDECODE_RAM("tiles0_raw", 0, gfx_8x8x8_raw, 0, 16)              // TM_TILES0  : T0 16cpp
	GFXDECODE_RAM("tiles1_raw", 0, gfx_8x8x8_raw, 0, 16)              // TM_TILES1  : T1 16cpp
	GFXDECODE_RAM("sprites_raw", 0, gfx_8x8x8_raw, 0, 16)             // TM_SPRITES : Sprites 16cpp
	GFXDECODE_ENTRY("maincpu", 0x1fd00, spectrum_charlayout, 0xf7, 1) // TM_ZX_CHAR
GFXDECODE_END

void tsconf_state::video_start()
{
	spectrum_128_state::video_start();

	m_ts_tilemap[TM_TS_CHAR] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(tsconf_state::get_tile_info_txt)), TILEMAP_SCAN_ROWS, 8, 8, 128, 64);

	m_ts_tilemap[TM_TILES0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(tsconf_state::get_tile_info_16c<0>)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64);
	m_ts_tilemap[TM_TILES0]->set_transparent_pen(0);
	m_gfxdecode->gfx(TM_TILES0)->set_granularity(16);

	m_ts_tilemap[TM_TILES1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(tsconf_state::get_tile_info_16c<1>)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64);
	m_ts_tilemap[TM_TILES1]->set_transparent_pen(0);
	m_gfxdecode->gfx(TM_TILES1)->set_granularity(16);

	m_gfxdecode->gfx(TM_SPRITES)->set_granularity(16);

	m_frame_irq_timer = timer_alloc(FUNC(tsconf_state::irq_frame), this);
	m_scanline_irq_timer = timer_alloc(FUNC(tsconf_state::irq_scanline), this);
}

void tsconf_state::machine_start()
{
	spectrum_128_state::machine_start();

	// reconfigure ROMs
	memory_region *rom = memregion("maincpu");
	m_bank_rom[0]->configure_entries(0, rom->bytes() / 0x4000, rom->base(), 0x4000);
	m_bank_ram[0]->configure_entries(0, m_ram->size() / 0x4000, m_ram->pointer(), 0x4000);

	save_item(NAME(m_int_mask));
	save_item(NAME(m_update_on_m1));
	save_item(NAME(m_regs));
	save_item(NAME(m_cache_line_addr));
	save_item(NAME(m_zctl_di));
	save_item(NAME(m_zctl_cs));
	save_item(NAME(m_port_f7_ext));
	save_item(NAME(m_gfx_y_frame_offset));
}

void tsconf_state::machine_reset()
{
	m_update_on_m1 = false;

	m_frame_irq_timer->adjust(attotime::never);
	m_scanline_irq_timer->adjust(attotime::never);
	m_int_mask = 0;

	m_bank0_rom.select(0);
	update_io(0);
	m_cache_line_addr = -1;

	m_glukrs->disable();

	m_scanline_delayed_regs_update = {};
	m_regs[V_CONFIG] = 0x00;        // 00000000
	m_regs[V_PAGE] = 0x05;          // 00000101
	m_regs[G_X_OFFS_L] = 0x00;      // 00000000
	m_regs[G_X_OFFS_H] &= 0xfe;     // xxxxxxx0
	m_regs[G_Y_OFFS_L] = 0x00;      // 00000000
	m_regs[G_Y_OFFS_H] &= 0xfe;     // xxxxxxx0
	m_regs[TS_CONFIG] &= 0x03;      // 000000xx
	m_regs[PAL_SEL] = 0x0f;         // 00001111
	m_regs[PAGE0] = 0x00;           // 00000000
	m_regs[PAGE1] = 0x05;           // 00000101
	m_regs[PAGE2] = 0x02;           // 00000010
	m_regs[PAGE3] = 0x00;           // 00000000
	m_regs[FMAPS] &= 0xef;          // xxx0xxxx
	m_regs[SYS_CONFIG] = 0x00;      // 00000000
	m_regs[MEM_CONFIG] = 0x04;      // 00000100
	m_regs[HS_INT] = 0x01;          // 00000001
	m_regs[VS_INT_L] = 0x00;        // 00000000
	m_regs[VS_INT_H] &= 0x0e;       // 0000xxx0
	m_regs[FDD_VIRT] &= 0xf0;       // xxxx0000
	m_regs[INT_MASK] = 0x01;        // xxxxx001
	m_regs[CACHE_CONFIG] &= 0xf0;   // xxxx0000

	m_beta->fddvirt_w(m_regs[FDD_VIRT] & 0x0f);

	m_zctl_cs = 1;
	m_zctl_di = 0xff;

	m_sprites_cache.clear();
	tsconf_update_bank0();
	tsconf_update_video_mode();

	m_keyboard->write(0xff);
	while (m_keyboard->read() != 0) { /* invalidate buffer */ }

	u16 const *const cram_init = &memregion("cram_init")->as_u16();
	for (auto i = 0; i < 0x100; i++)
		cram_write16(i << 1, cram_init[i]); // init color RAM
}

void tsconf_state::device_post_load()
{
	spectrum_128_state::device_post_load();
	m_sprites_cache.clear();
	copy_tiles_to_raw(m_ram->pointer() + ((m_regs[SG_PAGE] & 0xf8) << 14), m_sprites_raw.target());
	copy_tiles_to_raw(m_ram->pointer() + ((m_regs[T0_G_PAGE] & 0xf8) << 14), m_sprites_raw.target());
	copy_tiles_to_raw(m_ram->pointer() + ((m_regs[T1_G_PAGE] & 0xf8) << 14), m_sprites_raw.target());
}

INPUT_PORTS_START( tsconf )
	PORT_INCLUDE( spec_plus )

	PORT_START("mouse_input1")
	PORT_BIT(0xff, 0, IPT_MOUSE_X) PORT_SENSITIVITY(30)

	PORT_START("mouse_input2")
	PORT_BIT(0xff, 0, IPT_MOUSE_Y) PORT_SENSITIVITY(30)

	PORT_START("mouse_input3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON4) PORT_NAME("Left mouse button") PORT_CODE(MOUSECODE_BUTTON1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON5) PORT_NAME("Right mouse button") PORT_CODE(MOUSECODE_BUTTON2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON6) PORT_NAME("Middle mouse button") PORT_CODE(MOUSECODE_BUTTON3)
INPUT_PORTS_END

void tsconf_state::tsconf(machine_config &config)
{
	spectrum_128(config);

	config.device_remove("exp");
	config.device_remove("dma");
	config.device_remove("palette");

	Z80(config.replace(), m_maincpu, 14_MHz_XTAL / 4);
	m_maincpu->set_memory_map(&tsconf_state::tsconf_mem);
	m_maincpu->set_io_map(&tsconf_state::tsconf_io);
	m_maincpu->set_m1_map(&tsconf_state::tsconf_switch);
	m_maincpu->set_irq_acknowledge_callback(FUNC(tsconf_state::irq_vector));

	m_maincpu->set_vblank_int("screen", FUNC(tsconf_state::tsconf_vblank_interrupt));

	SPI_SDCARD(config, m_sdcard, 0);
	m_sdcard->set_prefer_sdhc();
	m_sdcard->spi_miso_callback().set(FUNC(tsconf_state::tsconf_spi_miso_w));

	TSCONF_RS232(config, m_uart, XTAL(11'059'200));
	m_uart->out_txd_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	m_uart->out_rts_callback().set("rs232", FUNC(rs232_port_device::write_rts));
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_uart, FUNC(tsconf_rs232_device::rxd_w));
	rs232.cts_handler().set(m_uart, FUNC(tsconf_rs232_device::cts_w));

	zxbus_device &zxbus(ZXBUS(config, "zxbus", 0));
	ZXBUS_SLOT(config, "zxbus1", 0, zxbus, zxbus_cards, nullptr);
	//ZXBUS_SLOT(config, "zxbus2", 0, zxbus, zxbus_cards, nullptr);

	m_ram->set_default_size("4096K").set_default_value(0x00); // must be random but 0x00 behaves better than 0xff in tested software

	GLUKRS(config, m_glukrs, 32.768_kHz_XTAL);

	TSCONF_DMA(config, m_dma, 28_MHz_XTAL);
	m_dma->in_mreq_callback().set(FUNC(tsconf_state::ram_read16));
	m_dma->out_mreq_callback().set(FUNC(tsconf_state::ram_write16));
	m_dma->in_spireq_callback().set(FUNC(tsconf_state::spi_read16));
	m_dma->out_cram_callback().set(FUNC(tsconf_state::cram_write16));
	m_dma->out_sfile_callback().set(FUNC(tsconf_state::sfile_write16));
	m_dma->on_ready_callback().set(FUNC(tsconf_state::dma_ready));

	TSCONF_BETA(config, m_beta, 0);
	m_beta->out_dos_callback().set(FUNC(tsconf_state::update_io));
	m_beta->out_vdos_m1_callback().set([this](int state) { m_update_on_m1 = true; });

	SPEAKER(config.replace(), "speakers", 2).front();

	AY_SLOT(config.replace(), "ay_slot", 14_MHz_XTAL / 8, default_ay_slot_devices, "ay_ym2149")
		.add_route(0, "speakers", 0.50, 0)
		.add_route(1, "speakers", 0.25, 0)
		.add_route(1, "speakers", 0.25, 1)
		.add_route(2, "speakers", 0.50, 1);

	DAC_8BIT_R2R(config, m_dac, 0).add_route(ALL_OUTPUTS, "speakers", 0.75);

	PALETTE(config, "palette", palette_device::BLACK, 256);
	m_screen->set_raw(14_MHz_XTAL / 2, 448, with_hblank(0), 448, 320, with_vblank(0), 320);
	m_screen->set_screen_update(FUNC(tsconf_state::screen_update));
	m_screen->set_no_palette();

	subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_tsconf);
	SPECTRUM_ULA_UNCONTENDED(config.replace(), m_ula);

	RAM(config, m_cram).set_default_size("512").set_default_value(0);
	RAM(config, m_sfile).set_default_size("512").set_default_value(0); // 85*6

	AT_KEYB(config, m_keyboard, pc_keyboard_device::KEYBOARD_TYPE::AT, 3);

	SOFTWARE_LIST(config, "betadisc_list_pent").set_original("spectrum_betadisc_flop");
	SOFTWARE_LIST(config, "betadisc_list_tsconf").set_original("tsconf_betadisc_flop");
}


void tsconf_state::tsconf2(machine_config &config)
{
	tsconf(config);
	TSCONF_COPPER(config, m_copper, 28_MHz_XTAL);
	m_copper->out_wreg_cb().set(FUNC(tsconf_state::tsconf_port_xxaf_w));
	m_copper->set_in_until_pos_cb(FUNC(tsconf_state::copper_until_pos_r));

	m_dma->on_ready_callback().append(m_copper, FUNC(tsconf_copper_device::dma_ready_w));
}


ROM_START(tsconf)
	ROM_REGION(0x080000, "maincpu", ROMREGION_ERASEFF) // ROM: 32 * 16KB
	ROM_DEFAULT_BIOS("v2407")

	ROM_SYSTEM_BIOS(0, "v1", "v1")
	ROMX_LOAD("ts-bios.rom", 0, 0x10000, CRC(b060b0d9) SHA1(820d3539de115141daff220a3cb733fc880d1bab), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "v2407", "Update 24.07.28")
	ROMX_LOAD("ts-bios.240728.rom", 0, 0x10000, CRC(19f8ad7b) SHA1(9cee82d4a6212686358a50b0fd5a2981b3323ab6), ROM_BIOS(1))

	ROM_REGION(0x200, "cram_init", ROMREGION_ERASEFF)
	ROM_LOAD( "cram-init.bin", 0, 0x200, CRC(8b96ffb7) SHA1(4dbd22f4312251e922911a01526cbfba77a122fc))
ROM_END

ROM_START(tsconf2)
	ROM_REGION(0x080000, "maincpu", ROMREGION_ERASEFF) // ROM: 32 * 16KB
	ROM_DEFAULT_BIOS("v2407")

	ROM_SYSTEM_BIOS(0, "v2407", "Update 24.07.28")
	ROMX_LOAD("ts-bios.240728.rom", 0, 0x10000, CRC(19f8ad7b) SHA1(9cee82d4a6212686358a50b0fd5a2981b3323ab6), ROM_BIOS(0))

	ROM_REGION(0x200, "cram_init", ROMREGION_ERASEFF)
	ROM_LOAD( "cram-init.bin", 0, 0x200, CRC(8b96ffb7) SHA1(4dbd22f4312251e922911a01526cbfba77a122fc))
ROM_END


//    YEAR  NAME        PARENT      COMPAT  MACHINE     INPUT       CLASS           INIT        COMPANY             FULLNAME                            FLAGS
COMP( 2011, tsconf,     0,          0,      tsconf,     tsconf,     tsconf_state,   empty_init, "NedoPC, TS-Labs",  "ZX Evolution: TS-Configuration",   MACHINE_SUPPORTS_SAVE)
COMP( 2024, tsconf2,    tsconf,     0,      tsconf2,    tsconf,     tsconf_state,   empty_init, "TS-Labs",          "EvoMAX3: TS-Configuration 2",      MACHINE_SUPPORTS_SAVE)



tsispch.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Jonathan Gevaryahu
// thanks-to:Kevin Horton
/******************************************************************************
*
*  Telesensory Systems Inc./Speech Plus
*  1500 and 2000 series
*  Prose 2020
*
*  The Prose 2000 card is an IEEE 796 Multibus card, with additional connectors to facilitate power and serial input other than via multibus.
*  There are two hardware versions of the card:
*  - The 1981 Telesensory Systems Inc copyrighted version
*    (lacks U82, has some rework on the power input to add a bypass capacitor and an extra power line to the 8086)
*  - The 1986 Speech Plus copyrighted version
*    (adds U82 as an extra buffer for status (not sure)? integrates the greenwire fixes from the above board, minor reorganizations of passives)
*  Both versions encountered have been in non-multibus enclosures:
*  - The 1981 Version appeared on a 'Voice V4' Speech board scrapped from a "Kurzweil Reading Machine" Talking Scanner (predecessor to the TSI/Kurzweil/Xerox 'Reading Edge' scanner which is SPARC based) [I'm very sorry I didn't get the OCR computing guts of the scanner itself too :( ]
*  - The 1986 Version appeared in a 'Prose 2020' under-monitor RS232 speech unit.
*
*  DONE:
*  Skeleton Written
*  Load cpu and dsp ROMs and mapper PROMs
*  Successful compile
*  Successful run
*  Correctly Interleave 8086 CPU ROMs
*  Debug LEDs hooked to popmessage
*  Correctly load UPD7720 ROMs as UPD7725 data - done; this is utterly disgusting code, but appears to work.
*  Attached i8251a uart at u15
*  Added dipswitch array S4
*  Attached 8259 PIC
   * IR0 = upd7720 p0 pin masked by (probably peripheral bit 8)
   * IR1 = i8251 rxrdy
   * IR2 = i8251 txempty
   * IR3 = i8251 txrdy
   * IR4,5,6,7 unknown so far, not hooked up yet
*  Hooked the terminal to the i8251a uart at u15
*  Hooked up upd7720 reset line
*  Verified CPU and DSP clocks
*
*  TODO:
*  UPD7720: hook up serial output and SCK, and hook SO to the DAC; this requires fixing the upd7725 core to actually support SCK and serial output/SO!
*  Attach the other i8251a uart (assuming it is hooked to the main hardware at all!)
*  Correctly implement UPD7720 cpu core to avoid needing revolting conversion code; this probably involves overriding and duplicating much of the exec_xx sections of the 7725 core
*  Correct memory maps and io maps, and figure out what all the PROMs do - mostly done
*  8259 PIC: figure out where IR4-7 come from, if anywhere.
*  UPD7720 and 8259: hook up p0 and p1 as outputs, and figure out how 8259 IR0 is masked from 7720 p0.
*  Add other dipswitches and jumpers (these may actually just control clock dividers for the two 8251s)
*  Older v1.1 set gets stuck forever waiting for upd7720 status port to equal 0x20, which never happens.
*  Everything else
*
*  Notes:
*  Text in ROM indicates there is a test mode 'activated by switch s4 dash 7'
*  When switch s4-7 is switched on, the hardware says, over and over:
*  "This is version 3.4.1 test mode, activated by switch s4 dash 7"
*
*  0x03400: the peripheral register
*    the low 8 bits (7-0) are Sw4
*    the high 8 bits:
*    Bit F E D C B A 9 8
*        | | | | | | | \- unknown but used, probably control (1=allow 0=mask?) 7720 p0 int to 8259 ir0?
*        | | | | | | \--- LED 6 control (0 = on)
*        | | | | | \----- LED 5 control (0 = on)
*        | | | | \------- LED 4 control (0 = on)
*        | | | \--------- LED 3 control (0 = on)
*        | | \----------- unknown, possibly unused?
*        | \------------- UPD7720 RESET line (0 = high/in reset, 1 = low/running)
*        \--------------- unknown, possibly unused? but might possibly related to 7720 p0->8259 as well?
*
*    When the unit is idle, leds 5 and 3 are on and upd7720 reset is low (write of 0b?1?0101?.
*    On all character writes from i8251, bit 8 is unset, then set again, possibly to avoid interrupt clashes?
*
*  Bootup notes v3.4.1:
*    D3109: checks if 0x80 (S4-8) is set: if set, continue, else jump to D3123
*    D3123: write 0x1C (0 0 0 [1 1 1 0] 0) to 3401
*    then jump to D32B0
*      D32B0: memory test routine:
*        This routine flood-fills memory from 0000-2FFF with 0xFF,
*        then, bytewise starting from 0000, shifts the value progresively
*        right by one, writes it and checks that it still matches,
*        i.e. read 0xFF, write 0x7f, read 0x7f, write 0x3f... etc.
*        Loop at D32E4.
*      D32E6: similar to D32B0, but rotates in 1 bits to 16 bit words,
*        though only the low byte is written, and only fills the 2BFF
*        down to 0000 region. (seems rather redundant, actually)
*        Loop at D3301.
*      D3311: write 0x0A (0 0 0 [0 1 0 1] 0) to 3401
*      then jump to D3330
*      D3330: jump back to D312E
*    D312E: this is some unknown conditional code, usually goes to D314E
*      if BP is not 1, go to D314E and don't update leds (usually taken?)
*      if BP is 1 and SI is 0, delay for 8*65536 cycles. no delay if si!=0
*      write 0x0C (0 0 0 [0 1 1 0] 0) to 3401
*    D314E: floodfill 0000-2BFF with 0x55 (rep at D315C)
*      check if bp was 1 and jump to D318F if it was
*      write 0x14 (0 0 0 [1 0 1 0] 0) to 3401
*      call E3987: initialize UPD7720, return
*    D33D2: checksum the ROMs in 5? passes, loop at D33DA, test at D33E6 (which passes)
*      if test DID fail: write 0x10 (0 0 0 [1 0 0 0] 0) to 3401
*        more stuff
*        write 0xFF to 3401
*        more stuff
*        set up word table? not sure what its doing here...
*      if test does NOT fail (and it doesn't):
*        D3414: write 0x08 (0 0 0 [0 1 0 0] 0) to 3400
*    D5E14: initialize PIC8259
*    <more stuff, wip>
*    D338A: write 0x12 0 0 0 [1 0 0 1] 0 to 3401
*
*  F44B4: general in-operation LED status write
******************************************************************************/

/* Core includes */
#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/i86/i86.h"
#include "cpu/upd7725/upd7725.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/pic8259.h"
#include "sound/dac.h"
#include "speaker.h"

// defines

#define LOG_PARAM     (1U << 1)
#define LOG_DSP       (1U << 2)

#define VERBOSE (LOG_GENERAL | LOG_PARAM)
//#define LOG_OUTPUT_FUNC printf
#include "logmacro.h"

namespace {

#define LOGPRM(...) LOGMASKED(LOG_PARAM, __VA_ARGS__)
#define LOGDSP(...) LOGMASKED(LOG_DSP, __VA_ARGS__)

// class definition
class tsispch_state : public driver_device
{
public:
	tsispch_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_dsp(*this, "dsp")
		, m_pic(*this, "pic8259")
		, m_uart(*this, "i8251a_u15")
	{
	}

	void prose2k(machine_config &config);

	void init_prose2k();

private:
	uint8_t dsw_r();
	void peripheral_w(uint8_t data);
	void dsp_status_w(uint8_t data);
	void dsp_to_8086_p0_w(int state);
	void dsp_to_8086_p1_w(int state);

	void dsp_data_map(address_map &map) ATTR_COLD;
	void dsp_prg_map(address_map &map) ATTR_COLD;
	void i8086_io(address_map &map) ATTR_COLD;
	void i8086_mem(address_map &map) ATTR_COLD;

	virtual void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<upd7725_device> m_dsp;
	required_device<pic8259_device> m_pic;
	required_device<i8251_device> m_uart;

	uint8_t m_paramReg = 0;           // status leds and resets and etc
};

/*
   Devices and handlers
 */


/*****************************************************************************
 LED/dipswitch stuff
*****************************************************************************/
uint8_t tsispch_state::dsw_r()
{
	/* the only dipswitch I'm really sure about is s4-7 which enables the test mode
	 * The switches are, for normal operation on my unit (and the older unit as well):
	 * 1  2  3   4   5   6   7   8
	 * ON ON OFF OFF OFF OFF OFF OFF
	 * which makes this register read 0xFC
	 * When s4-7 is turned on, it reads 0xBC
	 */
	return ioport("s4")->read();
}

void tsispch_state::peripheral_w(uint8_t data)
{
	/* This controls the four LEDS, the RESET line for the upd77p20,
	and (probably) the p0-to-ir0 masking of the upd77p20; there are two
	unknown and seemingly unused bits as well.
	see the top of the file for more info.
	*/
	m_paramReg = data;
	m_dsp->set_input_line(INPUT_LINE_RESET, BIT(data, 6) ? CLEAR_LINE : ASSERT_LINE);
	//LOGPRM("8086: Parameter Reg written: UNK7: %d, DSPRST6: %d; UNK5: %d; LED4: %d; LED3: %d; LED2: %d; LED1: %d; DSPIRQMASK: %d\n", BIT(data,7), BIT(data,6), BIT(data,5), BIT(data,4), BIT(data,3), BIT(data,2), BIT(data,1), BIT(data,0));
	LOGPRM("8086: Parameter Reg written: UNK7: %d, DSPRST6: %d; UNK5: %d; LED4: %d; LED3: %d; LED2: %d; LED1: %d; DSPIRQMASK: %d\n", BIT(data,7), BIT(data,6), BIT(data,5), BIT(data,4), BIT(data,3), BIT(data,2), BIT(data,1), BIT(data,0));
	popmessage("LEDS: 6/Talking:%d 5:%d 4:%d 3:%d\n", 1-BIT(data,1), 1-BIT(data,2), 1-BIT(data,3), 1-BIT(data,4));
}

/*****************************************************************************
 UPD77P20 stuff
*****************************************************************************/

void tsispch_state::dsp_status_w(uint8_t data)
{
	LOG("warning: upd772x status register should never be written to!\n");
}

void tsispch_state::dsp_to_8086_p0_w(int state)
{
	LOG("upd772x changed p0 state to %d!\n",state);
	//TODO: do stuff here!
}

void tsispch_state::dsp_to_8086_p1_w(int state)
{
	LOG("upd772x changed p1 state to %d!\n",state);
	//TODO: do stuff here!
}

/*****************************************************************************
 Reset and Driver Init
*****************************************************************************/
void tsispch_state::machine_reset()
{
	LOG("machine reset\n");
	m_dsp->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); // starts in reset
}

void tsispch_state::init_prose2k()
{
	uint8_t *dspsrc = (uint8_t *)(memregion("dspprgload")->base());
	uint32_t *dspprg = (uint32_t *)(memregion("dspprg")->base());
	LOG("driver init\n");
	// unpack 24 bit 7720 data into 32 bit space and shuffle it so it can run as 7725 code
	// data format as-is in dspsrc: (L = always 0, X = doesn't matter)
	// source upd7720                  dest upd7725
	// bit 7  6  5  4  3  2  1  0      bit 7  6  5  4  3  2  1  0
	// for OP/RT:
	// b1  15 16 17 18 19 20 21 22 ->      22 21 20 19 18 17 16 15
	// b2  L  8  9  10 11 12 13 14 ->      14 13 12 L  11 10 9  8
	// b3  0  1  2  3  4  5  6  7  ->      7  6  5  4  3  2  1  0
	// for JP:
	// b1  15 16 17 18 19 20 21 22 ->      22 21 20 19 18 17 16 15
	// b2  L  8  9  10 11 12 13 14 ->      14 13 L  L  L  12 11 10
	// b3  0  1  2  3  4  5  6  7  ->      9  8  7  6  5  4  X  X
	// for LD:
	// b1  15 16 17 18 19 20 21 22 ->      22 21 20 19 18 17 16 15
	// b2  L  8  9  10 11 12 13 14 ->      14 13 12 11 10 9  8  7
	// b3  0  1  2  3  4  5  6  7  ->      6  5  X  X  3  2  1  0
	for (int i = 0; i < 0x600; i+= 3)
	{
		uint8_t byte1t = bitswap<8>(dspsrc[0+i], 0, 1, 2, 3, 4, 5, 6, 7);
		uint16_t byte23t;
		// here's where things get disgusting: if the first byte was an OP or RT, do the following:
		if ((byte1t&0x80) == 0x00) // op or rt instruction
		{
			byte23t = bitswap<16>( (((uint16_t)dspsrc[1+i]<<8)|dspsrc[2+i]), 8, 9, 10, 15, 11, 12, 13, 14, 0, 1, 2, 3, 4, 5, 6, 7);
		}
		else if ((byte1t&0xC0) == 0x80) // jp instruction
		{
			byte23t = bitswap<16>( (((uint16_t)dspsrc[1+i]<<8)|dspsrc[2+i]), 8, 9, 15, 15, 15, 10, 11, 12, 13, 14, 0, 1, 2, 3, 6, 7);
		}
		else // ld instruction
		{
			byte23t = bitswap<16>( (((uint16_t)dspsrc[1+i]<<8)|dspsrc[2+i]), 8, 9, 10, 11, 12, 13, 14, 0, 1, 2, 3, 3, 4, 5, 6, 7);
		}

		*dspprg = byte1t<<24 | byte23t<<8;
		dspprg++;
	}
	m_paramReg = 0x00; // on power up, all leds on, reset to upd7720 is high
}

/******************************************************************************
 Address Maps
******************************************************************************/
/* The address map of the prose 2020 is controlled by 2 PROMs, see the ROM section
   for details on those.
   (x = ignored; * = selects address within this range; s = selects one of a pair of chips)
   A19 A18 A17 A16  A15 A14 A13 A12  A11 A10 A9 A8  A7 A6 A5 A4  A3 A2 A1 A0
     0   0   x   x    0   x   0   *    *   *  *  *   *  *  *  *   *  *  *  s  6264*2 SRAM first half
     0   0   x   x    0   x   1   0    *   *  *  *   *  *  *  *   *  *  *  s  6264*2 SRAM 3rd quarter
     0   0   x   x    0   x   1   1    0   0  0  x   x  x  x  x   x  x  *  x  iP8251A @ U15
     0   0   x   x    0   x   1   1    0   0  1  x   x  x  x  x   x  x  *  x  AMD P8259A PIC
     0   0   x   x    0   x   1   1    0   1  0  x   x  x  x  x   x  x  x  *  LEDS, dipswitches, and UPD77P20 control lines
     0   0   x   x    0   x   1   1    0   1  1  x   x  x  x  x   x  x  *  x  UPD77P20 data/status
     0   0   x   x    0   x   1   1    1   x  x                               Open bus, verified (returns 0x00EA)
     0   0   x   x    1   x                                                   Open bus? (or maybe communication with multibus connector?) (returns 0xFA,B,C,FFF)
     0   1                                                                    Open bus, verified (returns 0x00EA)
     1   0                                                                    Open bus, verified (returns 0x00EA)
     1   1   0   *    *   *   *   *    *   *  *  *   *  *  *  *   *  *  *  s  ROMs 2 and 3
     1   1   1   *    *   *   *   *    *   *  *  *   *  *  *  *   *  *  *  s  ROMs 0 and 1
*/
void tsispch_state::i8086_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x02fff).mirror(0x34000).ram(); // verified; 6264*2 SRAM, only first 3/4 used
	map(0x03000, 0x03003).mirror(0x341fc).rw(m_uart, FUNC(i8251_device::read), FUNC(i8251_device::write)).umask16(0x00ff);
	map(0x03200, 0x03203).mirror(0x341fc).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff); // AMD P8259 PIC @ U5 (reads as 04 and 7c, upper byte is open bus)
	map(0x03400, 0x03400).mirror(0x341fe).r(FUNC(tsispch_state::dsw_r)); // verified, read from dipswitch s4
	map(0x03401, 0x03401).mirror(0x341fe).w(FUNC(tsispch_state::peripheral_w)); // verified, write to the 4 leds, plus 4 control bits
	map(0x03600, 0x03600).mirror(0x341fc).rw(m_dsp, FUNC(upd7725_device::data_r), FUNC(upd7725_device::data_w)); // verified; UPD77P20 data reg r/w
	map(0x03602, 0x03602).mirror(0x341fc).r(m_dsp, FUNC(upd7725_device::status_r)).w(FUNC(tsispch_state::dsp_status_w)); // verified; UPD77P20 status reg r
	map(0xc0000, 0xfffff).rom(); // verified
}

// Technically the IO line of the i8086 is completely ignored (it is running in 8086 MIN mode,I believe, which may ignore IO)
void tsispch_state::i8086_io(address_map &map)
{
	map.unmap_value_high();
}

void tsispch_state::dsp_prg_map(address_map &map)
{
	map(0x0000, 0x01ff).rom().region("dspprg", 0);
}

void tsispch_state::dsp_data_map(address_map &map)
{
	map(0x0000, 0x01ff).rom().region("dspdata", 0);
}


/******************************************************************************
 Input Ports
******************************************************************************/
static INPUT_PORTS_START( prose2k )
PORT_START("s4") // dipswitch array s4
	PORT_DIPNAME( 0x01, 0x00, "S4-1") PORT_DIPLOCATION("SW4:1")
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, "S4-2") PORT_DIPLOCATION("SW4:2")
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, "S4-3") PORT_DIPLOCATION("SW4:3")
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "S4-4") PORT_DIPLOCATION("SW4:4")
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, "S4-5") PORT_DIPLOCATION("SW4:5")
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, "S4-6") PORT_DIPLOCATION("SW4:6")
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, "S4-7: Self Test") PORT_DIPLOCATION("SW4:7")
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, "S4-8") PORT_DIPLOCATION("SW4:8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

/******************************************************************************
 Machine Drivers
******************************************************************************/
void tsispch_state::prose2k(machine_config &config)
{
	/* basic machine hardware */
	/* There are two crystals on the board: a 24MHz xtal at Y2 and a 16MHz xtal at Y1 */
	I8086(config, m_maincpu, XTAL(24'000'000)/3); /* VERIFIED clock, (which xtal does this come from? guessed 24MHz) */
	m_maincpu->set_addrmap(AS_PROGRAM, &tsispch_state::i8086_mem);
	m_maincpu->set_addrmap(AS_IO, &tsispch_state::i8086_io);
	m_maincpu->set_irq_acknowledge_callback(m_pic, FUNC(pic8259_device::inta_cb));

	/* TODO: the UPD7720 has a 10KHz clock to its INT pin */
	/* TODO: the UPD7720 has a 2MHz clock to its SCK pin */
	/* TODO: hook up p0, p1, int */
	UPD7725(config, m_dsp, XTAL(16'000'000)/2); /* VERIFIED clock, unknown divider; correct dsp type is UPD77P20 (which xtal does this come from? guessed 16MHz) */
	m_dsp->set_addrmap(AS_PROGRAM, &tsispch_state::dsp_prg_map);
	m_dsp->set_addrmap(AS_DATA, &tsispch_state::dsp_data_map);
	m_dsp->p0().set(FUNC(tsispch_state::dsp_to_8086_p0_w));
	m_dsp->p1().set(FUNC(tsispch_state::dsp_to_8086_p1_w));

	/* PIC 8259 */
	PIC8259(config, m_pic, 0);
	m_pic->out_int_callback().set_inputline(m_maincpu, 0);

	/* uarts */
	I8251(config, m_uart, 0);
	m_uart->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_uart->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	m_uart->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_uart->rxrdy_handler().set(m_pic, FUNC(pic8259_device::ir1_w));
	m_uart->txrdy_handler().set(m_pic, FUNC(pic8259_device::ir3_w));
	m_uart->txempty_handler().set(m_pic, FUNC(pic8259_device::ir2_w));

	clock_device &clock(CLOCK(config, "baudclock", 153600)); // this comes from a resonator? or a divider? not sure.
	clock.signal_handler().set(m_uart, FUNC(i8251_device::write_txc));
	clock.signal_handler().append(m_uart, FUNC(i8251_device::write_rxc));

	/* sound hardware */
	SPEAKER(config, "speaker").front_center();
	DAC_12BIT_R2R(config, "dac", 0).add_route(ALL_OUTPUTS, "speaker", 1.0); // unknown DAC (TODO: correctly figure out how the DAC works; apparently it is connected to the serial output of the upd7720, which will be "fun" to connect up)

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("i8251a_u15", FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set("i8251a_u15", FUNC(i8251_device::write_dsr));
	rs232.cts_handler().set("i8251a_u15", FUNC(i8251_device::write_cts));
}

/******************************************************************************
 ROM Definitions
******************************************************************************/
ROM_START( prose2k )
	ROM_REGION(0x100000,"maincpu", 0)
	// prose 2000/2020 firmware version 3.4.1
	ROMX_LOAD( "v3.4.1__2000__2.u22",   0xc0000, 0x10000, CRC(201d3114) SHA1(549ef1aa28d5664d4198cbc1826b31020d6c4870),ROM_SKIP(1))
	ROMX_LOAD( "v3.4.1__2000__3.u45",   0xc0001, 0x10000, CRC(190c77b6) SHA1(2b90b3c227012f2085719e6283da08afb36f394f),ROM_SKIP(1))
	ROMX_LOAD( "v3.4.1__2000__0.u21",   0xe0000, 0x10000, CRC(3fae874a) SHA1(e1d3e7ba309b29a9c3edbe3d22becf82eae50a31),ROM_SKIP(1))
	ROMX_LOAD( "v3.4.1__2000__1.u44",   0xe0001, 0x10000, CRC(bdbb0785) SHA1(6512a8c2641e032ef6bb0889490d82f5d4399575),ROM_SKIP(1))

	// TSI/Speech plus DSP firmware v3.12 8/9/88, NEC UPD77P20
	ROM_REGION( 0x600, "dspprgload", 0) // packed 24 bit data
	ROM_LOAD( "v3.12__8-9-88__dsp_prog.u29", 0x0000, 0x0600, CRC(9e46425a) SHA1(80a915d731f5b6863aeeb448261149ff15e5b786))
	ROM_REGION32_BE( 0x800, "dspprg", ROMREGION_ERASEFF) // for unpacking 24 bit data into 32 bit data which cpu core can understand
	ROM_REGION16_BE( 0x400, "dspdata", 0)
	ROM_LOAD16_WORD_SWAP( "v3.12__8-9-88__dsp_data.u29", 0x0000, 0x0400, CRC(f4e4dd16) SHA1(6e184747db2f26e45d0e02907105ff192e51baba))

	// mapping PROMs:
	// All are am27s19 32x8 TriState PROMs (equivalent to 82s123/6331)
	// L - always low; H - always high
	// U77: unknown (what does this do? likely as to do with multibus and possibly waitstates?)
	//      input is A19 for I4, A18 for I3, A15 for I2, A13 for I1, A12 for I0
	//      output bits 0bLLLLzyxH (TODO: recheck)
	//      bit - function
	//      7, 6, 5, 4 - seem unconnected?
	//      3 - connection unknown, low in the RAM, ROM, and Peripheral memory areas
	//      2 - connection unknown, low in the RAM and ROM memory areas
	//      1 - unknown, always high (TODO: recheck)
	//      0 - unknown, always high
	//
	// U79: SRAM and peripheral mapping:
	//      input is A19 for I4, A18 for I3, A15 for I2, A13 for I1, A12 for I0, same as U77
	//      On the Prose 2000 later board dumped, only bits 3 and 0 are used;
	//      bits 7-4 are always low, bits 2 and 1 are always high.
	//      SRAMS are only populated in U61 and U64.
	//      On the Prose 2000 earlier board dumped, bits 3,2,1,0 are all used;
	//      bits 7-4 are always low. SRAM is in 6 6116s, mapped the same as the 2 6264s on the later board.
	//      output bits 0bLLLL3210
	//      7,6,5,4 - seem unconnected?
	//      3 - to /EN3 (pin 4) of 74S138N at U80
	//          AND to EN1 (pin 6) of 74S138N at U78
	//          i.e. one is activated when pin is high and other when pin is low
	//          The 74S138N at U80: [*ENABLED ONLY WITHIN/CONTROLS THE 3000-3FFF AREA*]
	//              /EN2 - pulled to GND
	//              EN1 - pulled to VCC through resistor R5
	//              inputs: S0 - A9; S1 - A10; S2 - A11
	//              /Y0 - /CS (pin 11) of iP8251A at U15 [0x3000-0x31FF]
	//              /Y1 - /CS (pin 1) of AMD 8259A at U4 [0x3200-0x33FF]
	//              /Y2 - pins 1, 4, 9 (1A, 2A, 3A inputs) of 74HCT32 Quad OR gate at U58 [0x3400-0x35FF]
	//              /Y3 - pin 26 (/CS) of UPD77P20 at U29 [0x3600-0x37FF]
	//              /Y4 through /Y7 - seem unconnected? [0x3800-0x3FFF]
	//          The 74S138N at U78: [*ENABLED IN ALL AREAS EXCEPT 3000-3FFF*] <wip>
	//              /EN3 - ? (TODO: figure these out)
	//              /EN2 - ?
	//              inputs: S0 - A18; S1 - A19; S2 - Pulled to GND
	//              /Y0 - ?
	//              /Y1 - ?
	//              /Y2 - ?
	//              /Y3 - connects somewhere, only active when A18 and A19 are high, possibly a ROM bus buffer enable? (TODO: figure out what this does)
	//              /Y4-/Y7 - never used since S2 is pulled to GND
	//      2 - to /CS1 on 6264 SRAMs at U63 and U66
	//      1 - to /CS1 on 6264 SRAMs at U62 and U65
	//      0 - to /CS1 on 6264 SRAMs at U61 and U64
	//
	// U81: (OPTIONAL) maps ROMS: input is A19-A15 for I4,3,2,1,0
	//      On the Prose 2000 board dumped, only bits 6 and 5 are used,
	//      the rest are always high; maps ROMs 0,1,2,3 to C0000-FFFFF.
	//      The Prose 2000 board has empty unpopulated sockets for ROMs 4-15;
	//      if present these would be driven by a different PROM in this location.
	//      bit - function
	//      7 - to /CE of ROMs 14(U28) and 15(U51)
	//      6 - to /CE of ROMs 0(U21) and 1(U44)
	//      5 - to /CE of ROMs 2(U22) and 3(U45)
	//      4 - to /CE of ROMs 4(U23) and 5(U46)
	//      3 - to /CE of ROMs 6(U24) and 7(U47)
	//      2 - to /CE of ROMs 8(U25) and 9(U48)
	//      1 - to /CE of ROMs 10(U26) and 11(U49)
	//      0 - to /CE of ROMs 12(U27) and 13(U50)
	//
	// Note U81 is optional; it can be replaced by a 74s138 instead of a PROM,
	// with A19, A18, A17 as inputs, for decoding the ROMs as:
	//      7 - to /CE of ROMs 0(U21) and 1(U44)   (0xE0000-0xE3FFF)
	//      6 - to /CE of ROMs 2(U22) and 3(U45)   (0xE4000-0xE7FFF)
	//      5 - to /CE of ROMs 4(U23) and 5(U46)   (0xE8000-0xEBFFF)
	//      4 - to /CE of ROMs 6(U24) and 7(U47)   (0xEC000-0xEFFFF)
	//      3 - to /CE of ROMs 8(U25) and 9(U48)   (0xF0000-0xF3FFF)
	//      2 - to /CE of ROMs 10(U26) and 11(U49) (0xF4000-0xF7FFF)
	//      1 - to /CE of ROMs 12(U27) and 13(U50) (0xF8000-0xFBFFF)
	//      0 - to /CE of ROMs 14(U28) and 15(U51) (0xFC000-0xFFFFF)

	ROM_REGION(0x1000, "proms", 0)
	ROM_LOAD( "am27s19.u77", 0x0000, 0x0020, CRC(a88757fc) SHA1(9066d6dbc009d7a126d75b8461ca464ddf134412))
	ROM_LOAD( "am27s19.u79", 0x0020, 0x0020, CRC(a165b090) SHA1(bfc413c79915c68906033741318c070ad5dd0f6b))
	ROM_LOAD( "am27s19.u81", 0x0040, 0x0020, CRC(62e1019b) SHA1(acade372edb08fd0dcb1fa3af806c22c47081880))
ROM_END

ROM_START( prose2ko )
	// 'Older' prose2k set
	ROM_REGION(0x100000,"maincpu", 0)
	// prose 2000 firmware version 1.1
	ROMX_LOAD( "v1.1__6__speech__plus__=c=1983.am2764.6.u24",   0xec000, 0x2000, CRC(c881f92d) SHA1(2d4eb96360adac54d4f0110595bfaf682280c1ca),ROM_SKIP(1))
	ROMX_LOAD( "v1.1__7__speech__plus__=c=1983.am2764.7.u47",   0xec001, 0x2000, CRC(4d5771cb) SHA1(55ed59ad1cad154804dbeeebed98f062783c33c3),ROM_SKIP(1))
	ROMX_LOAD( "v1.1__8__speech__plus__=c=1983.am2764.8.u25",   0xf0000, 0x2000, CRC(adf9bfb8) SHA1(0b73561b52b388b740fabf07ada2d70a52f22037),ROM_SKIP(1))
	ROMX_LOAD( "v1.1__9__speech__plus__=c=1983.am2764.9.u48",   0xf0001, 0x2000, CRC(355f97d2) SHA1(7655fc55b577821e0bd8bf81fb74b8a20b1df098),ROM_SKIP(1))
	ROMX_LOAD( "v1.1__10__speech__plus__=c=1983.am2764.10.u26", 0xf4000, 0x2000, CRC(949a0344) SHA1(8e33c69dfc413aea95f166b08902ad97b1e3e980),ROM_SKIP(1))
	ROMX_LOAD( "v1.1__11__speech__plus__=c=1983.am2764.11.u49", 0xf4001, 0x2000, CRC(ad9a0670) SHA1(769f2f8696c7b6907706466aa9ab7a897ed9f889),ROM_SKIP(1))
	ROMX_LOAD( "v1.1__12__speech__plus__=c=1983.am2764.12.u27", 0xf8000, 0x2000, CRC(9eaf9378) SHA1(d296b1d347c03e6123c38c208ead25b1f43b9859),ROM_SKIP(1))
	ROMX_LOAD( "v1.1__13__speech__plus__=c=1983.am2764.13.u50", 0xf8001, 0x2000, CRC(5e173667) SHA1(93230c2fede5095f56e10d20ea36a5a45a1e7356),ROM_SKIP(1))
	ROMX_LOAD( "v1.1__14__speech__plus__=c=1983.am2764.14.u28", 0xfc000, 0x2000, CRC(e616bd6e) SHA1(5dfae2c5079d89f791c9d7166f9504231a464203),ROM_SKIP(1))
	ROMX_LOAD( "v1.1__15__speech__plus__=c=1983.am2764.15.u51", 0xfc001, 0x2000, CRC(beb1fa19) SHA1(72130fe45c3fd3de7cf794936dc68ed2d4193daf),ROM_SKIP(1))

	// TSI/Speech plus DSP firmware v?.? (no sticker, but S140025 printed on chip), unlabeled chip, but clearly a NEC UPD7720C ceramic
	// NOT DUMPED YET, using the 3.12 dsp firmware as a placeholder, since the dsp on the older board is mask ROM and an electronic dump method is not yet known
	ROM_REGION( 0x600, "dspprgload", 0) // packed 24 bit data
	ROM_LOAD( "s140025__dsp_prog.u29", 0x0000, 0x0600, NO_DUMP)
	ROM_LOAD( "v3.12__8-9-88__dsp_prog.u29", 0x0000, 0x0600, CRC(9e46425a) SHA1(80a915d731f5b6863aeeb448261149ff15e5b786)) // temp placeholder
	ROM_REGION32_BE( 0x800, "dspprg", ROMREGION_ERASEFF) // for unpacking 24 bit data into 32 bit data which cpu core can understand
	ROM_REGION16_BE( 0x400, "dspdata", 0)
	ROM_LOAD( "s140025__dsp_data.u29", 0x0000, 0x0400, NO_DUMP)
	ROM_LOAD16_WORD_SWAP( "v3.12__8-9-88__dsp_data.u29", 0x0000, 0x0400, CRC(f4e4dd16) SHA1(6e184747db2f26e45d0e02907105ff192e51baba)) // temp placeholder

	ROM_REGION(0x1000, "proms", 0)
	ROM_LOAD( "dm74s288n.u77", 0x0000, 0x0020, CRC(a88757fc) SHA1(9066d6dbc009d7a126d75b8461ca464ddf134412)) // == am27s19.u77
	ROM_LOAD( "dm74s288n.whitespot.u79", 0x0020, 0x0020, CRC(7faee6cb) SHA1(b6dd2a6909dac9e89e7317c006a013ff0866382d))
	// no third PROM in this set, a 74S138 is used instead for e0000-fffff ROM mapping
ROM_END

} // anonymous namespace

/******************************************************************************
 Drivers
******************************************************************************/

//    YEAR  NAME      PARENT   COMPAT  MACHINE  INPUT    CLASS          INIT          COMPANY                                FULLNAME                  FLAGS
COMP( 1987, prose2k,  0,       0,      prose2k, prose2k, tsispch_state, init_prose2k, "Telesensory Systems Inc/Speech Plus", "Prose 2000/2020 v3.4.1", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1982, prose2ko, prose2k, 0,      prose2k, prose2k, tsispch_state, init_prose2k, "Telesensory Systems Inc/Speech Plus", "Prose 2000/2020 v1.1",   MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



tt.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * Silicon Graphics Professional IRIS/PowerSeries "Twin Tower" systems.
 *
 *   Year  Model  Board  CPU    Clock    I/D Cache    Code Name
 *   1987  4D/50  IP4    R2000  8MHz     64KiB/32KiB  Twin Tower
 *   1987  4D/70  IP4    R2000  12.5MHz  64KiB/32KiB  Twin Tower
 *
 * Sources:
 *   - VME-Eclipse CPU (VIP10) Specification, Silicon Graphics, Inc.
 *
 * TODO:
 *  - graphics
 *  - IP4.5, IP5, IP7
 *  - 15 slot version (IP5, IP7)
 *
 */

#include "emu.h"

#include "bus/vme/vme.h"
#include "bus/vme/enp10.h"

#include "ip4.h"

// graphics cards
#include "gm1.h"

//#define VERBOSE (0)
#include "logmacro.h"

namespace {

class ip4_state : public driver_device
{
public:
	ip4_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_vme(*this, "vme")
		, m_slot(*this, "vme:slot%u", 1U)
	{
	}

	void pi4d50(machine_config &config);
	void pi4d70(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void tt12(machine_config &config, XTAL clock);

private:
	required_device<vme_bus_device> m_vme;
	required_device_array<vme_slot_device, 12> m_slot;
};

void ip4_state::pi4d50(machine_config &config)
{
	tt12(config, 16_MHz_XTAL);
}

void ip4_state::pi4d70(machine_config &config)
{
	tt12(config, 25_MHz_XTAL);
}

static void vme_cards(device_slot_interface &device)
{
	device.option_add("enp10", VME_ENP10);
}

static void slot8_cards(device_slot_interface &device)
{
	vme_cards(device);

	//device.option_add("ge4", SGI_GE4); // GT
}
static void slot9_cards(device_slot_interface &device)
{
	//device.option_add("de3", SGI_DE3); // B,G
	device.option_add("gm1", SGI_GM1); // GT
}
static void slot10_cards(device_slot_interface &device)
{
	//device.option_add("gf3", SGI_GF3); // B,G
	//device.option_add("rm1", SGI_RM1); // GT
}
static void slot11_cards(device_slot_interface &device)
{
	//device.option_add("tb2", SGI_TB2); // B,G
	//device.option_add("rv1", SGI_RV1); // GT
}
static void slot12_cards(device_slot_interface &device)
{
	//device.option_add("zb2", SGI_ZB2); // G
	//device.option_add("rm1", SGI_RM1); // GT
}

void ip4_state::tt12(machine_config &config, XTAL clock)
{
	VME(config, m_vme);

	VME_SLOT(config,  m_slot[0]).option_set("ip4", SGI_IP4).clock(clock);
	VME_SLOT(config,  m_slot[1], vme_cards, "enp10", false);
	VME_SLOT(config,  m_slot[2], vme_cards, nullptr, false);
	VME_SLOT(config,  m_slot[3], vme_cards, nullptr, false);

	VME_SLOT(config,  m_slot[4], vme_cards, nullptr, false);
	VME_SLOT(config,  m_slot[5], vme_cards, nullptr, false);
	VME_SLOT(config,  m_slot[6], vme_cards, nullptr, false);
	VME_SLOT(config,  m_slot[7], slot8_cards, nullptr, false);

	VME_SLOT(config,  m_slot[8], slot9_cards, nullptr, false);
	VME_SLOT(config,  m_slot[9], slot10_cards, nullptr, false);
	VME_SLOT(config, m_slot[10], slot11_cards, nullptr, false);
	VME_SLOT(config, m_slot[11], slot12_cards, nullptr, false);
}

void ip4_state::machine_start()
{
}

void ip4_state::machine_reset()
{
}

ROM_START(pi4d50)
ROM_END

#define rom_pi4d70 rom_pi4d50

} // anonymous namespace

//   YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY             FULLNAME                   FLAGS
COMP(1987, pi4d50, 0,      0,      pi4d50,  0,     ip4_state, empty_init, "Silicon Graphics", "Professional IRIS 4D/50", 0)
COMP(1987, pi4d70, 0,      0,      pi4d70,  0,     ip4_state, empty_init, "Silicon Graphics", "Professional IRIS 4D/70", 0)



tulip1.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    CompuData Tulip System I

    Hardware:
    - I8086 CPU
    - I8087 FPU (optional)
    - I8089 I/O Processor (optional)
    - 8 kb ROM, 4 + 8 kb graphics ROM
    - 128 to 896 kb RAM
    - 2x TC5517AP VRAM
    - HD46505SP-2 CRT (+ 7220?)
    - MC68B50P ACIA (for the keyboard?)
    - 8256A-P MUART
    - WD2793PL-02 FDC

    TODO:
    - MUART emulation which handles interrupts, timers, serial and
      parallel interfaces.

    Notes:
    - If you hit the keyboard a few times it tries to load the OS first
      from floppy and then from the hard drive.

***************************************************************************/

#include "emu.h"
#include "cpu/i86/i86.h"
#include "machine/6850acia.h"
#include "machine/clock.h"
#include "machine/i8256.h"
#include "machine/wd_fdc.h"
#include "video/mc6845.h"
#include "bus/rs232/rs232.h"
#include "imagedev/floppy.h"
#include "emupal.h"
#include "screen.h"


namespace {

class tulip1_state : public driver_device
{
public:
	tulip1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_cpu(*this, "maincpu"),
		m_crtc(*this, "crtc"),
		m_acia(*this, "acia"),
		m_palette(*this, "palette"),
		m_gfxdecode(*this, "gfxdecode"),
		m_fdc(*this, "fdc"),
		m_vram(*this, "vram"),
		m_chargen(*this, "chargen")
		{}

	void tulip1(machine_config &config);

protected:
	void machine_start() override ATTR_COLD;

private:
	required_device<i8086_cpu_device> m_cpu;
	required_device<hd6845s_device> m_crtc;
	required_device<acia6850_device> m_acia;
	required_device<palette_device> m_palette;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<wd2793_device> m_fdc;
	required_shared_ptr<uint16_t> m_vram;
	required_region_ptr<uint8_t> m_chargen;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	MC6845_UPDATE_ROW(crtc_update_row);
};

void tulip1_state::mem_map(address_map &map)
{
	map(0x00000, 0x1ffff).ram();
	map(0x20000, 0xdffff).noprw();
	map(0xe0000, 0xe0fff).ram().share("vram");
	map(0xfe000, 0xfffff).rom().region("bios", 0);
}

void tulip1_state::io_map(address_map &map)
{
	map(0xfc00, 0xfc07).rw(m_fdc, FUNC(wd2793_device::read), FUNC(wd2793_device::write)).umask16(0x00ff);
//  map(0xfca0, 0xfcaf) // ?
//  map(0xfcb0) // ?
//  map(0xfc80) // ?
	map(0xfc91, 0xfc91).lr8(NAME([]() -> u8 { return 0x04; })); // ?
//  map(0xfd00, 0xfd1f) // muart
	map(0xfd98, 0xfd98).rw(m_acia, FUNC(acia6850_device::status_r), FUNC(acia6850_device::control_w));
	map(0xfd9c, 0xfd9c).rw(m_acia, FUNC(acia6850_device::data_r), FUNC(acia6850_device::data_w));
	map(0xfe00, 0xfe00).w(m_crtc, FUNC(hd6845s_device::address_w));
	map(0xfe02, 0xfe02).rw(m_crtc, FUNC(hd6845s_device::register_r), FUNC(hd6845s_device::register_w));
//  map(0xfe82) // ?
//  map(0xff00, 0xff1f) // ?
//  map(0xff30) // ?
}

static INPUT_PORTS_START( tulip1 )
INPUT_PORTS_END

static const gfx_layout char_layout =
{
	8, 11,
	256*3,
	1,
	{ 0 },
	{ STEP8(0, 1) },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8 },
	8*16
};

static GFXDECODE_START(chars)
	GFXDECODE_ENTRY("chargen", 0, char_layout, 0, 1)
GFXDECODE_END

MC6845_UPDATE_ROW( tulip1_state::crtc_update_row )
{
	pen_t const *const pen = m_palette->pens();

	for (int i = 0; i < x_count; i++)
	{
		uint16_t code = m_vram[(ma + i) & 0xfff] & 0xff;
		uint8_t data = m_chargen[(code << 4) | ra];

		// draw 8 pixels of the character
		for (int x = 0; x < 8; x++)
			bitmap.pix(y, x + i*8) = pen[BIT(data, 7 - x)];
	}
}

static void tulip1_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

void tulip1_state::machine_start()
{
}

void tulip1_state::tulip1(machine_config &config)
{
	// main cpu
	I8086(config, m_cpu, 8000000);
	m_cpu->set_addrmap(AS_PROGRAM, &tulip1_state::mem_map);
	m_cpu->set_addrmap(AS_IO, &tulip1_state::io_map);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(16000000, 912, 0, 640, 312, 0, 275); // unknown clock
	screen.set_screen_update(m_crtc, FUNC(mc6845_device::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	GFXDECODE(config, m_gfxdecode, m_palette, chars);

	HD6845S(config, m_crtc, 2000000); // unknown clock
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(tulip1_state::crtc_update_row));

	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set("kbd", FUNC(rs232_port_device::write_txd));

	rs232_port_device &rs232(RS232_PORT(config, "kbd", default_rs232_devices, "keyboard"));
	rs232.rxd_handler().set(m_acia, FUNC(acia6850_device::write_rxd));

	clock_device &uart_clock(CLOCK(config, "uart_clock", 153600));
	uart_clock.signal_handler().set(m_acia, FUNC(acia6850_device::write_txc));
	uart_clock.signal_handler().append(m_acia, FUNC(acia6850_device::write_rxc));

	// floppy disk controller
	WD2793(config, m_fdc, 1000000); // unknown clock

	// floppy drives
	FLOPPY_CONNECTOR(config, "fdc:0", tulip1_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", tulip1_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
}

ROM_START( tulip1 )
	ROM_REGION16_LE(0x2000, "bios", 0)
	ROM_LOAD16_BYTE("tulip1_15d.bin", 0x0000, 0x1000, CRC(78793e46) SHA1(9850d82062ef617497a3eb49ab07848650ee9b8c))
	ROM_LOAD16_BYTE("tulip1_16d.bin", 0x0001, 0x1000, CRC(8c9e4475) SHA1(e7446334e5284512a8103d1b5e4612a2922b1bdc))

	ROM_REGION(0x3000, "chargen", ROMREGION_INVERT)
	ROM_LOAD("tulip1_h106.bin", 0x0000, 0x1000, CRC(fdde779d) SHA1(01df551853f117ad91b47e389edf10e0f0d0d4c2))
	ROM_LOAD("tulip1_h305.bin", 0x1000, 0x2000, CRC(f17f03f1) SHA1(060cca61fadd82fe9917430340bb880478ceeec6))
ROM_END

} // anonymous namespace


//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY    FULLNAME            FLAGS
COMP( 1983, tulip1, 0,      0,      tulip1,  tulip1, tulip1_state, empty_init, "CompuData", "Tulip System I", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



turbo16k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

SciSys Kasparov Turbo 16K family

These chesscomputers are all on similar hardware. The chess engine is by Julio
Kaplan and Craig Barnes.

NOTE: Before exiting MAME, press the STOP button to turn the power off. Otherwise,
NVRAM won't save properly.

TODO:
- dump/add other MCU revisions, SX8 for tmate/conquist is known to exist
- what is t1850's official title? "1850 Deluxe Table Chess" is from the back of
  the computer. The manual can't make up its mind and says "1850 Chess Computer",
  "1850 Chess: 16 Level Program", or "1850 Sensory Chess Game". The box disagrees
  and says "Sixteen Level Computerized Chess"

Hardware notes:
- Hitachi HD6301Y0P/F MCU, 8MHz or 12MHz (LC osc, no XTAL)
- 16 board LEDs (can be tri-color), 7 or 8 status LEDs
- buttons sensor board, piezo

As seen on PCB and MCU labels, the Tandy (Radio Shack) versions are programmed and
manufactured by SciSys, presumably under contract by Tandy.

Turbo 16K/S-24K and Conquistador have tri-color LEDs, and two LCD chess clocks.
As hinted by the strange clock start value of 1:00 instead of 0:00, the clocks
are actually digital watch components. The IC under epoxy is an OKI MSM5001N.

I/O for LEDs and buttons is scrambled a bit for Team-Mate and Conquistador, the
base hardware remains the same.

SX4(A) program is used in:
- Tandy (Radio Shack) 1850 60-2199 (8MHz, ST4A-PE-015 PCB)
- no known SciSys chesscomputers, to distinguish: this program has 16 playing
  levels and SX5A has 17

SX5(A) program is used in:
- SciSys Companion III (8MHz, ST4B-PE-007 PCB)
- SciSys Express 16K (8MHz, SH5-PE-009 PCB)
- SciSys Astral (12MHz, SW4-PE-010 PCB)
- SciSys Turbo 16K (12MHz, ST5-PE-023 PCB)
- Tandy (Radio Shack) 1850 60-2201A (8MHz, ST5A-PE-002 PCB)
- Mephisto Monaco (H+G brand Express 16K)

SX5A 6301Y0A97F (QFP) has the same ROM contents as 6301Y0A96P.

SX8(A) program is used in:
- Saitek Team-Mate aka Team-Mate Advanced Trainer (8MHz, ST8B-PE-017 PCB)
- Saitek Cavalier aka Portable Advanced Trainer (suspected, 8MHz, ? PCB)
- Saitek Conquistador (12MHz, ST8-PE-021 PCB)

*******************************************************************************/

#include "emu.h"

#include "cpu/m6800/m6801.h"
#include "machine/msm5001n.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "saitek_companion3.lh"
#include "saitek_conquistador.lh"
#include "saitek_teammate.lh"
#include "saitek_turbo16k.lh"
#include "t1850.lh"


namespace {

// Turbo 16K / shared

class turbo16k_state : public driver_device
{
public:
	turbo16k_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_lcd_clock(*this, "clock%u", 0),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_lcd_digits(*this, "ldigit%u.%u", 0U, 0U),
		m_lcd_colon(*this, "lc%u", 0U)
	{ }

	void compan3(machine_config &config);
	void turbo16k(machine_config &config);
	void t1850(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(go_button);
	DECLARE_INPUT_CHANGED_MEMBER(change_cpu_freq);

protected:
	virtual void machine_start() override ATTR_COLD;

	// devices/pointers
	required_device<hd6301y0_cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	optional_device_array<msm5001n_device, 2> m_lcd_clock;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<2> m_inputs;
	output_finder<2, 4> m_lcd_digits;
	output_finder<2> m_lcd_colon;

	u8 m_inp_mux = 0;
	u8 m_led_select = 0;
	u8 m_led_data[2] = { };
	u16 m_lcd_data[4] = { };

	// I/O handlers
	template<int N> void lcd_output_w(offs_t offset, u16 data);
	void lcd_enable(u8 enable);
	void update_display();

	template <int N> void leds1_w(u8 data);
	template <int N> void leds2_w(u8 data);
	void p2l_w(u8 data);
	virtual void p3_w(u8 data);
	virtual u8 p5_r();
	virtual u8 p6_r();
	void p6_w(u8 data);
};

void turbo16k_state::machine_start()
{
	m_lcd_digits.resolve();
	m_lcd_colon.resolve();

	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_led_select));
	save_item(NAME(m_led_data));
	save_item(NAME(m_lcd_data));
}

INPUT_CHANGED_MEMBER(turbo16k_state::change_cpu_freq)
{
	// 4MHz and 16MHz versions don't exist, but the software supports it
	static const u32 freq[4] = { 4'000'000, 8'000'000, 12'000'000, 16'000'000 };
	m_maincpu->set_unscaled_clock(freq[bitswap<2>(newval,4,0)]);
}


// Conquistador

class conquist_state : public turbo16k_state
{
public:
	conquist_state(const machine_config &mconfig, device_type type, const char *tag) :
		turbo16k_state(mconfig, type, tag)
	{ }

	void conquist(machine_config &config);
	void tmate(machine_config &config);

protected:
	virtual void p3_w(u8 data) override;
	virtual u8 p5_r() override;
	virtual u8 p6_r() override;
};



/*******************************************************************************
    I/O
*******************************************************************************/

// common

INPUT_CHANGED_MEMBER(turbo16k_state::go_button)
{
	// standby check actually comes from P70 high-impedance state
	if (newval && m_maincpu->standby())
		m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
}

void turbo16k_state::update_display()
{
	m_display->matrix_partial(0, 3, m_led_select << 1 | 1, m_led_data[1] << 8 | m_led_data[0]);
}

template <int N>
void turbo16k_state::leds1_w(u8 data)
{
	// P10-P17, P40-P47: board leds
	m_led_data[N] = ~data;
	update_display();
}

template <int N>
void turbo16k_state::leds2_w(u8 data)
{
	// P2x, P6x, P7x: status leds (direct)
	m_display->write_row(N + 3, ~data);
}

void turbo16k_state::p3_w(u8 data)
{
	// P30-P37: input mux
	m_inp_mux = ~data;
}

u8 turbo16k_state::p5_r()
{
	u8 data = 0;

	// P50-P57: read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_file(i, true);

	return ~data;
}

u8 turbo16k_state::p6_r()
{
	u8 data = 0;

	// P60,P61: read buttons
	for (int i = 0; i < 2; i++)
		if (m_inp_mux & m_inputs[i]->read())
			data |= 1 << i;

	return ~data;
}

void turbo16k_state::p6_w(u8 data)
{
	// P63,P64: status leds
	leds2_w<1>(data);

	// P65,P66: red/green led select
	m_led_select = ~data >> 5 & 3;
	update_display();

	// P67: speaker out
	m_dac->write(BIT(~data, 7));
}


// LCD

template<int N>
void turbo16k_state::lcd_output_w(offs_t offset, u16 data)
{
	m_lcd_data[N << 1 | (offset & 1)] = data;
	u32 segs = m_lcd_data[N << 1 | 1] << 11 | m_lcd_data[N << 1];

	// unscramble segments
	m_lcd_digits[N][0] = bitswap<7>(segs,12,2,13,17,11,0,1);
	m_lcd_digits[N][1] = bitswap<7>(segs,15,5,16,4,14,3,4);
	m_lcd_digits[N][2] = bitswap<7>(segs,19,9,20,21,18,7,8);
	m_lcd_digits[N][3] = BIT(segs, 10) ? 6 : 0;
	m_lcd_colon[N] = BIT(segs, 6);
}

void turbo16k_state::lcd_enable(u8 enable)
{
	// LCD XTAL can be disabled by software
	for (int i = 0; i < 2; i++)
		m_lcd_clock[i]->set_clock_scale(BIT(enable, i) ? 1.0 : 0.0);
}

void turbo16k_state::p2l_w(u8 data)
{
	// P24,P25: status leds
	leds2_w<0>(data);

	// P20,P21: LCD clocks enabled
	lcd_enable(~data & 3);

	// P26: LCD clocks power (both)
	m_lcd_clock[0]->power_w(BIT(data, 6));
	m_lcd_clock[1]->power_w(BIT(data, 6));
}


// conquist-specific

void conquist_state::p3_w(u8 data)
{
	// P30-P37: input mux (scrambled)
	m_inp_mux = bitswap<8>(~data,0,1,2,7,6,3,5,4);
}

u8 conquist_state::p5_r()
{
	// P50,P51: read buttons
	u8 data = turbo16k_state::p6_r() & 3;

	// P52-P57: read chessboard part
	data |= turbo16k_state::p5_r() << 2;
	return bitswap<8>(data,7,4,3,5,2,6,1,0);
}

u8 conquist_state::p6_r()
{
	// P60,P61: read chessboard part
	return bitswap<2>(turbo16k_state::p5_r(),6,7) | 0xfc;
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( turbo16k )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Color")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Sound")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Stop")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Set Up")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("Play")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Multi Move")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Display Move")

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(turbo16k_state::go_button), 0) PORT_NAME("Go")

	PORT_START("FREQ")
	PORT_CONFNAME( 0x88, 0x80, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(turbo16k_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "4MHz (unofficial)" )
	PORT_CONFSETTING(    0x08, "8MHz (Companion III, Express 16K)" )
	PORT_CONFSETTING(    0x80, "12MHz (Turbo 16K, Astral)" )
	PORT_CONFSETTING(    0x88, "16MHz (unofficial)" )
	PORT_BIT(0x77, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( compan3 )
	PORT_INCLUDE( turbo16k )

	PORT_MODIFY("FREQ") // modify default to 8MHz
	PORT_CONFNAME( 0x88, 0x08, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(turbo16k_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x00, "4MHz (unofficial)" )
	PORT_CONFSETTING(    0x08, "8MHz (Companion III, Express 16K)" )
	PORT_CONFSETTING(    0x80, "12MHz (Turbo 16K, Astral)" )
	PORT_CONFSETTING(    0x88, "16MHz (unofficial)" )
INPUT_PORTS_END

static INPUT_PORTS_START( conquist )
	PORT_INCLUDE( turbo16k )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Coach")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("Color")

	PORT_MODIFY("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Non Auto")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_P) PORT_NAME("Play")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Set Up")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Stop")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("Info")

	PORT_MODIFY("FREQ")
	PORT_CONFNAME( 0x88, 0x80, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(turbo16k_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x08, "8MHz (Team-Mate)" )
	PORT_CONFSETTING(    0x80, "12MHz (Conquistador)" )
INPUT_PORTS_END

static INPUT_PORTS_START( tmate )
	PORT_INCLUDE( conquist )

	PORT_MODIFY("FREQ") // modify default to 8MHz
	PORT_CONFNAME( 0x88, 0x08, "CPU Frequency" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(turbo16k_state::change_cpu_freq), 0) // factory set
	PORT_CONFSETTING(    0x08, "8MHz (Team-Mate)" )
	PORT_CONFSETTING(    0x80, "12MHz (Conquistador)" )
INPUT_PORTS_END

static INPUT_PORTS_START( t1850 )
	PORT_INCLUDE( compan3 )

	PORT_MODIFY("IN.0")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("Sound")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Memory")

	PORT_MODIFY("IN.1")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Display Move")

	PORT_MODIFY("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(turbo16k_state::go_button), 0) PORT_NAME("Power On")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void turbo16k_state::compan3(machine_config &config)
{
	// basic machine hardware
	HD6301Y0(config, m_maincpu, 8'000'000); // approximation, no XTAL
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(hd6301y0_cpu_device::nvram_set_battery));
	m_maincpu->standby_cb().append([this](int state) { if (state) m_display->clear(); });
	m_maincpu->out_p1_cb().set(FUNC(turbo16k_state::leds1_w<0>));
	m_maincpu->in_p2_cb().set_ioport("FREQ");
	m_maincpu->in_p2_override_mask(0x88); // SX4A and SX5A rely on this
	m_maincpu->out_p2_cb().set(FUNC(turbo16k_state::leds2_w<0>));
	m_maincpu->out_p3_cb().set(FUNC(turbo16k_state::p3_w));
	m_maincpu->out_p4_cb().set(FUNC(turbo16k_state::leds1_w<1>));
	m_maincpu->in_p5_cb().set(FUNC(turbo16k_state::p5_r));
	m_maincpu->in_p6_cb().set(FUNC(turbo16k_state::p6_r));
	m_maincpu->out_p6_cb().set(FUNC(turbo16k_state::p6_w));
	m_maincpu->out_p7_cb().set(FUNC(turbo16k_state::leds2_w<2>));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(3+3, 16);
	config.set_default_layout(layout_saitek_companion3);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}

void turbo16k_state::turbo16k(machine_config &config)
{
	compan3(config);

	// basic machine hardware
	m_maincpu->set_clock(12'000'000);
	m_maincpu->standby_cb().append([this](int state) { if (state) lcd_enable(0); });
	m_maincpu->out_p2_cb().set(FUNC(turbo16k_state::p2l_w));

	MSM5001N(config, m_lcd_clock[0], 32.768_kHz_XTAL).write_segs().set(FUNC(turbo16k_state::lcd_output_w<0>));
	MSM5001N(config, m_lcd_clock[1], 32.768_kHz_XTAL).write_segs().set(FUNC(turbo16k_state::lcd_output_w<1>));

	config.set_default_layout(layout_saitek_turbo16k);
}

void conquist_state::conquist(machine_config &config)
{
	turbo16k(config);
	config.set_default_layout(layout_saitek_conquistador);
}

void conquist_state::tmate(machine_config &config)
{
	compan3(config);
	config.set_default_layout(layout_saitek_teammate);
}

void turbo16k_state::t1850(machine_config &config)
{
	compan3(config);
	config.set_default_layout(layout_t1850);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( turbo16k )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("1986_sx5a_scisys_6301y0a96p.u1", 0x0000, 0x4000, CRC(65dd0626) SHA1(aa82242cb05f6c063430297a07a702d31c99a3ed) )
ROM_END

ROM_START( conquist )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("1988_sx8a_saitek_31y0g84p.u1", 0x0000, 0x4000, CRC(083e0346) SHA1(32bd8609d16c885afbd5566c64330f84f3c46099) )
ROM_END

ROM_START( t1850 )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("1985_sx4a_scisys_6301y0a13p.u1", 0x0000, 0x4000, CRC(6ba27399) SHA1(9bccd77eed416e92bcaa722e9ff3a4d6ba9946a1) )
ROM_END

#define rom_compan3 rom_turbo16k
#define rom_tmate rom_conquist

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT    COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1986, turbo16k, 0,        0,      turbo16k, turbo16k, turbo16k_state, empty_init, "SciSys / Heuristic Software", "Kasparov Turbo 16K", MACHINE_SUPPORTS_SAVE )
SYST( 1986, compan3,  turbo16k, 0,      compan3,  compan3,  turbo16k_state, empty_init, "SciSys / Heuristic Software", "Kasparov Companion III", MACHINE_SUPPORTS_SAVE )

SYST( 1988, conquist, 0,        0,      conquist, conquist, conquist_state, empty_init, "Saitek / Heuristic Software", "Kasparov Conquistador", MACHINE_SUPPORTS_SAVE )
SYST( 1988, tmate,    conquist, 0,      tmate,    tmate,    conquist_state, empty_init, "Saitek / Heuristic Software", "Kasparov Team-Mate", MACHINE_SUPPORTS_SAVE )

SYST( 1986, t1850,    0,        0,      t1850,    t1850,    turbo16k_state, empty_init, "Tandy Corporation / SciSys / Heuristic Software", "1850 Deluxe Table Chess (model 60-2199)", MACHINE_SUPPORTS_SAVE )



turbos24k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

SciSys Kasparov Turbo S-24K (model 280)

NOTE: Before exiting MAME, press the STOP button to turn the power off. Otherwise,
NVRAM won't save properly.

Hardware notes:
- PCB label: SciSys ST6-PE-001
- Hitachi HD6301Y0P MCU (mode 2) @ 12MHz (LC osc, no XTAL)
- 8KB RAM (M5M5165P-12L), battery-backed
- 2 LCD clocks, same ones as Turbo 16K
- 16+11 LEDs (23 tri-color, 4 red)
- buttons sensor board, piezo

I/O is similar to Leonardo, and the housing is similar to the newer Turbo King.
It has an unused input (IN.7 0x01) that makes the program work with a magnet
sensor board.

*******************************************************************************/

#include "emu.h"

#include "cpu/m6800/m6801.h"
#include "machine/msm5001n.h"
#include "machine/nvram.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "saitek_turbos24k.lh"


namespace {

class turbos24k_state : public driver_device
{
public:
	turbos24k_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_lcd_clock(*this, "clock%u", 0),
		m_display(*this, "display"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_lcd_digits(*this, "ldigit%u.%u", 0U, 0U),
		m_lcd_colon(*this, "lc%u", 0U)
	{ }

	void turbos24k(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(go_button);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<hd6301y0_cpu_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device_array<msm5001n_device, 2> m_lcd_clock;
	required_device<pwm_display_device> m_display;
	required_device<dac_1bit_device> m_dac;
	required_ioport_array<9> m_inputs;
	output_finder<2, 4> m_lcd_digits;
	output_finder<2> m_lcd_colon;

	u8 m_inp_mux = 0;
	u8 m_led_data[2] = { };
	u16 m_lcd_data[4] = { };

	void main_map(address_map &map) ATTR_COLD;

	// I/O handlers
	template<int N> void lcd_output_w(offs_t offset, u16 data);
	void lcd_enable(u8 enable);

	void standby(int state);
	void update_display();
	void mux_w(u8 data);
	void leds_w(u8 data);

	u8 p2_r();
	void p2_w(u8 data);
	void p5_w(u8 data);
	u8 p6_r();
};

void turbos24k_state::machine_start()
{
	m_lcd_digits.resolve();
	m_lcd_colon.resolve();

	// register for savestates
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_led_data));
	save_item(NAME(m_lcd_data));
}



/*******************************************************************************
    I/O
*******************************************************************************/

// power

void turbos24k_state::standby(int state)
{
	if (state)
	{
		m_display->clear();
		lcd_enable(0);
	}
}

INPUT_CHANGED_MEMBER(turbos24k_state::go_button)
{
	if (newval && m_maincpu->standby())
		m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
}


// LCD

template<int N>
void turbos24k_state::lcd_output_w(offs_t offset, u16 data)
{
	m_lcd_data[N << 1 | (offset & 1)] = data;
	u32 segs = m_lcd_data[N << 1 | 1] << 11 | m_lcd_data[N << 1];

	// unscramble segments
	m_lcd_digits[N][0] = bitswap<7>(segs,12,2,13,17,11,0,1);
	m_lcd_digits[N][1] = bitswap<7>(segs,15,5,16,4,14,3,4);
	m_lcd_digits[N][2] = bitswap<7>(segs,19,9,20,21,18,7,8);
	m_lcd_digits[N][3] = BIT(segs, 10) ? 6 : 0;
	m_lcd_colon[N] = BIT(segs, 6);
}

void turbos24k_state::lcd_enable(u8 enable)
{
	// LCD XTAL can be disabled by software
	for (int i = 0; i < 2; i++)
		m_lcd_clock[i]->set_clock_scale(BIT(enable, i) ? 1.0 : 0.0);
}


// misc

void turbos24k_state::update_display()
{
	m_display->matrix_partial(0, 8, 1 << (m_inp_mux & 0xf), m_led_data[0]);
	m_display->matrix_partial(8, 2, 1 << BIT(m_inp_mux, 5), (m_inp_mux << 2 & 0x300) | m_led_data[1]);
}

void turbos24k_state::mux_w(u8 data)
{
	// d0-d3: input/chessboard led mux
	// d5: button led select
	// d6,d7: button led data
	m_inp_mux = data ^ 0xc0;
	update_display();

	// d4: speaker out
	m_dac->write(BIT(data, 4));
}

void turbos24k_state::leds_w(u8 data)
{
	// d0-d7: button led data
	m_led_data[1] = ~data;
	update_display();
}


// MCU ports

u8 turbos24k_state::p2_r()
{
	u8 data = 0;

	// P20-P22: multiplexed inputs
	if ((m_inp_mux & 0xf) <= 8)
		data = m_inputs[m_inp_mux & 0xf]->read();

	return ~data;
}

void turbos24k_state::p2_w(u8 data)
{
	// P25,P26: chessboard led column data
	m_led_data[0] = (m_led_data[0] & ~3) | (~data >> 5 & 3);
	update_display();
}

void turbos24k_state::p5_w(u8 data)
{
	// P53,P54: LCD clocks enabled
	lcd_enable(~data >> 3 & 3);

	// P55: LCD clocks power (both)
	m_lcd_clock[0]->power_w(BIT(data, 5));
	m_lcd_clock[1]->power_w(BIT(data, 5));

	// P56,P57: chessboard led row data
	m_led_data[0] = (m_led_data[0] & 3) | (~data >> 4 & 0xc);
	update_display();
}

u8 turbos24k_state::p6_r()
{
	// P60-P67: read chessboard
	return ~m_board->read_file(m_inp_mux & 0xf);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void turbos24k_state::main_map(address_map &map)
{
	map(0x4000, 0x5fff).ram().share("nvram");
	map(0x6000, 0x6000).mirror(0x0fff).w(FUNC(turbos24k_state::mux_w));
	map(0x7000, 0x7000).mirror(0x0fff).w(FUNC(turbos24k_state::leds_w));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( turbos24k )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("Play Normal")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("Tab / Color")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_NAME("Library")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("Function")

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_CODE(KEYCODE_EQUALS) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_CUSTOM) // freq sel
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("King")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")

	PORT_START("IN.4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_CUSTOM) // freq sel
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")

	PORT_START("IN.5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Sound")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Normal")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Analysis")

	PORT_START("IN.6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_CUSTOM) // freq sel
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_J) PORT_NAME("Stop")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Set Up")

	PORT_START("IN.7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_CUSTOM) // freq sel + board config
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_K) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Info")

	PORT_START("IN.8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_CONFNAME( 0x04, 0x04, "Battery Status" )
	PORT_CONFSETTING(    0x00, "Low" )
	PORT_CONFSETTING(    0x04, DEF_STR( Normal ) )

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(turbos24k_state::go_button), 0) PORT_NAME("Go")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void turbos24k_state::turbos24k(machine_config &config)
{
	// basic machine hardware
	HD6301Y0(config, m_maincpu, 12'000'000); // approximation, no XTAL
	m_maincpu->set_addrmap(AS_PROGRAM, &turbos24k_state::main_map);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(hd6301y_cpu_device::nvram_set_battery));
	m_maincpu->standby_cb().append(FUNC(turbos24k_state::standby));
	m_maincpu->in_p2_cb().set(FUNC(turbos24k_state::p2_r));
	m_maincpu->out_p2_cb().set(FUNC(turbos24k_state::p2_w));
	m_maincpu->out_p5_cb().set(FUNC(turbos24k_state::p5_w));
	m_maincpu->in_p6_cb().set(FUNC(turbos24k_state::p6_r));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	m_board->set_nvram_enable(true);

	// video hardware
	MSM5001N(config, m_lcd_clock[0], 32.768_kHz_XTAL).write_segs().set(FUNC(turbos24k_state::lcd_output_w<0>));
	MSM5001N(config, m_lcd_clock[1], 32.768_kHz_XTAL).write_segs().set(FUNC(turbos24k_state::lcd_output_w<1>));

	PWM_DISPLAY(config, m_display).set_size(8+2, 8+2);
	config.set_default_layout(layout_saitek_turbos24k);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( turbos24k )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD("1986_sx6a_scisys_6301y0b40p.u1", 0x0000, 0x4000, CRC(d2a6e194) SHA1(59c61ddb722aa143afb95ce38f5b1b94e4eed604) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY, FULLNAME, FLAGS
SYST( 1986, turbos24k, 0,      0,      turbos24k, turbos24k, turbos24k_state, empty_init, "SciSys / Heuristic Software", "Kasparov Turbo S-24K", MACHINE_SUPPORTS_SAVE )



tutor.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Raphael Nabet
/*
    Experimental Tomy Tutor driver

    This computer is known as Tomy Tutor in US, and as Grandstand Tutor in UK.
    It was initially released in Japan in 1982 or 1983 under the name of Pyuuta
    (Pi-yu-u-ta, with a Kanji for the "ta").  The Japanese versions are
    different from the English-language versions, as they have different ROMs
    with Japanese messages and support for the katakana syllabus.  There are at
    least 4 versions:
    * original Pyuuta (1982 or 1983) with title screens in Japanese but no
      Basic
    * Pyuuta Jr. (1983?) which is a console with a simplified keyboard
    * Tomy/Grandstand Tutor (circa October 1983?) with title screens in English
      and integrated Basic
    * Pyuuta Mk. 2 (1984?) with a better-looking keyboard and integrated Basic

    The Tomy Tutor features a TMS9995 CPU @10.7MHz (which includes a
    timer/counter and 256 bytes of 16-bit RAM), 48kb of ROM (32kb on early
    models that did not have the BASIC interpreter), a tms9918a/9929a VDP (or
    equivalent?) with 16kb of VRAM, and a sn76489an sound generator.
    There is a tape interface, a 56-key keyboard, an interface for two
    joysticks, a cartridge port and an extension port.  The OS design does not
    seem to be particularly expandable (I don't see any hook for additional
    DSRs), but there were prototypes for a parallel port (emulated)
    and a speech synthesizer unit (not emulated).


    The Tutor appears to be related to Texas Instruments' TI99 series.

    The general architecture is relatively close to the ti99/4(a): arguably,
    the Tutor does not include any GROM (it uses regular CPU ROMs for GPL
    code), and its memory map is quite different due to the fact it was
    designed with a tms9995 in mind (vs. a tms9985 for ti99/4), but, apart from
    that, it has a similar architecture with only 256 bytes of CPU RAM and 16kb
    of VRAM.

    While the OS is not derived directly from the TI99/4(a) OS, there are
    disturbing similarities: the Japanese title screen is virtually identical
    to the TI-99 title screen.  Moreover, the Tutor BASIC seems to be be
    derived from TI Extended BASIC, as both BASIC uses similar tokens and
    syntax, and are partially written in GPL (there is therefore a GPL
    interpreter in Tutor ROMs, although the Tutor GPL is incompatible with TI
    GPL, does not seem to be used by any program other than Tutor Basic, and it
    seems unlikely that the original Pyuuta had this GPL interpreter in ROMs).

    It appears that TI has sold the licence of the TI BASIC to Tomy, probably
    after it terminated its TI99 series.  It is not impossible that the entire
    Tutor concept is derived from a TI project under licence: this machine
    looks like a crossbreed of the TI99/2 and the TI99/4 (or /4a, /4b, /5), and
    it could either have been an early version of the TI99/2 project with a
    tms9918a/99289a VDP instead of the DMA video controller, or a "TI99/3" that
    would have closed the gap between the extremely low-end TI99/2 and the
    relatively mid-range TI99/5.


    Raphael Nabet, 2003


TODO :
    * debug the tape interface (Saved tapes sound OK, both Verify and Load
      recognize the tape as a Tomy tape, but the data seems to be corrupted and
      we end with a read error.)
    * guess which device is located at the >e600 base
    * find info about other Tutor variants


    Interrupts:

    Interrupt levels 1 (external interrupt 1) and 2 (error interrupt) do not
    seem to be used: triggering these seems to cause a soft reset.  XOPs are
    not used at all: the ROM area where these vectors should be defined is used
    by a ROM branch table.


Memory Map found at http://www.floodgap.com/retrobits/tomy/mmap.html

                   *** $0000-$7FFF is the 32K BIOS ***
 it is also possible to replace the BIOS with an external ROM (see $E000 range)

0000
                                reset vector (level 0)
                                0000-0001: WP   0002-0003: PC (%)
0004
                                level 1 and 2 IRQ vectors (%)
                                idem. These just seem to reset the system.
000C
                                additional vectors
0040
                                XOP vectors (%)
                The BIOS doesn't seem to use these for XOPs.
                Instead, this is a branch table.
0080
                BIOS code
                (CRU only: $1EE0-FE: 9995 flag register;
                    $1FDA: MID flag)
4000
                GBASIC
                (on the American v2.3 firmware, the GPL
                    interpreter and VDP RAMLUT co-exist
                    with GBASIC in this range)

              *** $8000-$BFFF is the 16K option ROM area ***

8000
                BASIC (Tutor only) and/or cartridge ROM
                    (controlled by $E100)
                To be recognized, a cartridge must have a
                $55, $66 or $aa header sequence.

                         *** end ROM ***
C000
                unmapped (possible use in 24K cartridges)
E000
                I/O range
                ---------
                9918A VDP data/register ports: $E000, E002
                "MMU" banking controls: $E100-E1FF
                    $E100 write: enable cartridge, disable
                        BIOS at $0000 (???) -- magic
                        required at $E110 for this
                    $E108 write: enable BASIC ROM, disable
                        cartridge at $8000
                    $E10C write: enable cartridge, disable
                        BASIC ROM at $8000
                    $E110 must be $42 to enable $E100
                        and to replace the BIOS with
                        an installed cartridge ROM.
                        BLWP assumed at $0000 (??).
                SN76489AN sound data port: $E200
                Device handshaking: $E600
                    Unknown purpose, disk drive maybe?
                Printer handshaking: $E800
                This is a standard Centronics port.
                    $E810 write: parallel data bus
                    $E820 read: parallel port busy
                    $E840 write: port handshake output
                Keyboard lines: $EC00-$EC7E (*CRU*)
                    (CRU physical address $7600-$763F)
                Cassette lines: $ED00-$EEFF
                    $ED00 (*CRU*): input level
                        (physical address $7680)
                    $EE00 write: tape output zero
                    $EE20 write: tape output one
                    $EE40 write: tape IRQ on
                    $EE60 write: tape IRQ off
                    $EE80, A0, C0, E0: ???

F000
                                TMS9995 RAM (*)
F0FC
                                ??
FFFA
                                decrementer (*)
FFFC
                                NMI vector (*)
FFFF



PYUUTAJR
********

This is a handheld unit with 12 'chiclet' buttons. The keyboard has E800, EA00, EC00, EE00 scanned,
although 2 of these rows have no keys. TUTOR carts will run, however since most of them ask for
A=AMA, P=PRO, these keys don't exist, and so the games cannot be played.

*********************************************************************************************************/

#include "emu.h"

#include "cpu/tms9900/tms9995.h"
#include "imagedev/cassette.h"
#include "sound/sn76496.h"
#include "video/tms9928a.h"

#include "bus/centronics/ctronics.h"
#include "bus/generic/carts.h"
#include "bus/generic/slot.h"

#include "softlist_dev.h"
#include "speaker.h"


namespace {

class tutor_state : public driver_device
{
public:
	tutor_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_cart(*this, "cartslot"),
		m_cass(*this, "cassette"),
		m_centronics(*this, "centronics"),
		m_cent_data_out(*this, "cent_data_out"),
		m_bank1(*this, "bank1"),
		m_bank2(*this, "bank2"),
		m_bank1_switching(0)
	{
	}

	void pyuutajr(machine_config &config);
	void tutor(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<tms9995_device> m_maincpu;
	required_device<generic_slot_device> m_cart;
	optional_device<cassette_image_device> m_cass;
	optional_device<centronics_device> m_centronics;
	optional_device<output_latch_device> m_cent_data_out;
	required_memory_bank m_bank1;
	required_memory_bank m_bank2;
	memory_region *m_cart_rom;

	int m_tape_interrupt_enable;
	emu_timer *m_tape_interrupt_timer;

	int m_bank1_switching;
	int m_centronics_busy;

	uint8_t key_r(offs_t offset);
	uint8_t tutor_mapper_r(offs_t offset);
	void tutor_mapper_w(offs_t offset, uint8_t data);
	uint8_t tutor_cassette_r();
	void tutor_cassette_w(offs_t offset, uint8_t data);
	uint8_t tutor_printer_r(offs_t offset);
	void tutor_printer_w(offs_t offset, uint8_t data);

	uint8_t tutor_highmem_r(offs_t offset);
	TIMER_CALLBACK_MEMBER(tape_interrupt_handler);

	void write_centronics_busy(int state);
	[[maybe_unused]] void test_w(offs_t offset, uint8_t data);

	void pyuutajr_mem(address_map &map) ATTR_COLD;
	void tutor_io(address_map &map) ATTR_COLD;
	void tutor_memmap(address_map &map) ATTR_COLD;
};


void tutor_state::machine_start()
{
	std::string region_tag;
	m_cart_rom = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG));

	m_tape_interrupt_timer = timer_alloc(FUNC(tutor_state::tape_interrupt_handler), this);

	m_bank1->configure_entry(0, memregion("maincpu")->base() + 0x4000);
	m_bank1->set_entry(0);
	m_bank2->configure_entry(0, memregion("maincpu")->base() + 0x8000);
	m_bank2->set_entry(0);

	if (m_cart_rom)
	{
		if (m_cart_rom->bytes() > 0x4000)
		{
			m_bank1_switching = 1;
			m_bank1->configure_entry(1, m_cart_rom->base());
			m_bank1->set_entry(1);
			m_bank2->configure_entry(1, m_cart_rom->base() + 0x4000);
			m_bank2->set_entry(1);
		}
		else
		{
			m_bank2->configure_entry(1, m_cart_rom->base());
			m_bank2->set_entry(1);
		}
	}
}

void tutor_state::machine_reset()
{
	m_tape_interrupt_enable = 0;
	m_centronics_busy = 0;

	// Disable auto wait states; with enabled wait states, cassette loading fails
	m_maincpu->ready_line(ASSERT_LINE);
	m_maincpu->reset_line(ASSERT_LINE);
	m_maincpu->hold_line(CLEAR_LINE);
}

/*
    Keyboard:

    Keyboard ports are located at CRU logical address >ec00->ec7e (CRU physical
    address >7600->763f).  There is one bit per key (bit >00 for keycode >00,
    bit >01 for keycode >01, etc.), each bit is set to one when the key is
    down.

    Joystick:

    Joystick ports seem to overlap keyboard ports, i.e. some CRU bits are
    mapped to both a keyboard key and a joystick switch.
*/

uint8_t tutor_state::key_r(offs_t offset)
{
	char port[12];
	uint8_t value;

	snprintf(port, std::size(port), "LINE%d", (offset & 0x007e) >> 3);
	value = ioport(port)->read();

	/* hack for ports overlapping with joystick */
	if (offset >= 32 && offset < 48)
	{
		snprintf(port, std::size(port), "LINE%d_alt", (offset & 0x007e) >> 3);
		value |= ioport(port)->read();
	}

	return BIT(value, offset & 7);
}


/*
    Cartridge mapping:

    Cartridges share the >8000 address base with BASIC.  A write to @>e10c
    disables the BASIC ROM and enable the cartridge.  A write to @>e108
    disables the cartridge and enables the BASIC ROM.

    In order to be recognized by the system ROM, a cartridge should start with
    >55, >66 or >aa.  This may may correspond to three different ROM header
    versions (I am not sure).

    Cartridge may also define a boot ROM at base >0000 (see below).
*/

uint8_t tutor_state::tutor_mapper_r(offs_t offset)
{
	int reply;

	switch (offset)
	{
	case 0x10:
		/* return 0x42 if we have a cartridge with an alternate boot ROM */
		reply = 0;
		break;

	default:
		logerror("unknown port in %s %d\n", __FILE__, __LINE__);
		reply = 0;
		break;
	}

	return reply;
}

void tutor_state::tutor_mapper_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 0x00:
		/* disable system ROM, enable alternate boot ROM in cartridge */
		break;

	case 0x08:
		/* disable cartridge ROM, enable BASIC ROM at base >8000 */
		m_bank1->set_entry(0);
		m_bank2->set_entry(0);
		break;

	case 0x0c:
		/* enable cartridge ROM, disable BASIC ROM at base >8000 */
		if (m_cart_rom)
		{
			if (m_bank1_switching)
				m_bank1->set_entry(1);
			m_bank2->set_entry(1);
		}
		break;

	default:
		if (!(offset & 1))
			logerror("unknown port in %s %d\n", __FILE__, __LINE__);
		break;
	}
}

/*
    This is only called from the debugger; the on-chip memory is handled
    within the CPU itself.
*/
uint8_t tutor_state::tutor_highmem_r(offs_t offset)
{
	if (m_maincpu->is_onchip(offset | 0xf000)) return m_maincpu->debug_read_onchip_memory(offset&0xff);
	return 0;
}

/*
    Cassette interface:

    The cassette interface uses several ports in the >e000 range.

    Writing to *CPU* address @>ee00 will set the tape output to 0.  Writing to
    *CPU* address @>ee20 will set the tape output to 1.

    Tape input level can be read from *CRU* logical address >ed00 (CRU physical
    address >7680).

    Writing to @>ee40 enables tape interrupts; writing to @>ee60 disables tape
    interrupts.  Tape interrupts are level-4 interrupt that occur when the tape
    input level is high(?).

    There are other output ports: @>ee80, @>eea0, @>eec0 & @>eee0.  I don't
    know their exact meaning.
*/

TIMER_CALLBACK_MEMBER(tutor_state::tape_interrupt_handler)
{
	//assert(m_tape_interrupt_enable);
	m_maincpu->set_input_line(INT_9995_INT4, (m_cass->input() > 0.0) ? ASSERT_LINE : CLEAR_LINE);
}

/* CRU handler */
uint8_t tutor_state::tutor_cassette_r()
{
	return (m_cass->input() > 0.0) ? 1 : 0;
}

/* memory handler */
void tutor_state::tutor_cassette_w(offs_t offset, uint8_t data)
{
	if (offset & /*0x1f*/0x1e)
		logerror("unknown port in %s %d\n", __FILE__, __LINE__);

	if ((offset & 0x1f) == 0)
	{
		data = BIT(offset, 5);

		switch ((offset >> 6) & 3)
		{
		case 0:
			/* data out */
			m_cass->output((data) ? +1.0 : -1.0);
			break;
		case 1:
			/* interrupt control??? */
			//logerror("ignoring write of %d to cassette port 1\n", data);
			if (m_tape_interrupt_enable != data)
			{
				m_tape_interrupt_enable = data;
				if (!m_tape_interrupt_enable)
					m_tape_interrupt_timer->adjust(/*attotime::from_hz(44100)*/attotime::zero, 0, attotime::from_hz(44100));
				else
				{
					m_tape_interrupt_timer->adjust(attotime::never);
					m_maincpu->set_input_line(INT_9995_INT4, CLEAR_LINE);
				}
			}
			break;
		case 2:
			/* ??? */
			logerror("ignoring write of %d to cassette port 2\n", data);
			break;
		case 3:
			/* ??? */
			logerror("ignoring write of %d to cassette port 3\n", data);
			break;
		}
	}
}

void tutor_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

/* memory handlers */
uint8_t tutor_state::tutor_printer_r(offs_t offset)
{
	int reply;

	switch (offset)
	{
	case 0x20:
		/* busy */
		reply = m_centronics_busy ? 0x00 : 0xff;
		break;

	default:
		if (! (offset & 1))
			logerror("unknown port in %s %d\n", __FILE__, __LINE__);
		reply = 0;
		break;
	}

	return reply;
}

void tutor_state::tutor_printer_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	case 0x10:
		/* data */
		m_cent_data_out->write(data);
		break;

	case 0x40:
		/* strobe */
		m_centronics->write_strobe(BIT(data, 7));
		break;

	default:
		if (! (offset & 1))
			logerror("unknown port in %s %d\n", __FILE__, __LINE__);
		break;
	}
}

/*
    Memory map summary:

    @>0000-@>7fff: system ROM (can be paged out, see above).
    @>8000-@>bfff: basic ROM (can be paged out, see above).
    @>c000-@>dfff: free for future expansion? Used by 24kb cartridges?

    @>e000(r/w): VDP data
    @>e002(r/w): VDP register

    @>e100(w): enable cart and disable system ROM at base >0000??? (the system will only link to such a ROM if @>e110 is >42???)
    @>e108(w): disable cart and enable BASIC ROM at base >8000?
    @>e10c(w): enable cart and disable BASIC ROM at base >8000?
    @>e110(r): cartridges should return >42 if they have a ROM at base >0000 and they want the Tutor to boot from this ROM (with a blwp@>0000)???

    @>e200(w): sound write

    @>e600(r): handshake in from whatever device???
    @>e610(w): ???
    @>e620(w): ???
    @>e680(w): handshake out to this device???

    @>e810(w): parallel port data bus
    @>e820(r): parallel port busy input
    @>e840(w): parallel port strobe output

    @>ee00-@>eee0(w): tape interface (see above)

    @>f000-@>f0fb: tms9995 internal RAM 1
    @>fffa-@>fffb: tms9995 internal decrementer
    @>f000-@>f0fb: tms9995 internal RAM 2
*/

void tutor_state::test_w(offs_t offset, uint8_t data)
{
	switch (offset)
	{
	default:
		logerror("unmapped write %d %d\n", offset, data);
		break;
	}
}

void tutor_state::tutor_memmap(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	map(0x4000, 0x7fff).bankr("bank1").nopw();
	map(0x8000, 0xbfff).bankr("bank2").nopw();
	map(0xc000, 0xdfff).noprw(); /*free for expansion, or cartridge ROM?*/

	map(0xe000, 0xe000).rw("tms9928a", FUNC(tms9928a_device::vram_read), FUNC(tms9928a_device::vram_write));    /*VDP data*/
	map(0xe002, 0xe002).rw("tms9928a", FUNC(tms9928a_device::register_read), FUNC(tms9928a_device::register_write));/*VDP status*/
	map(0xe100, 0xe1ff).rw(FUNC(tutor_state::tutor_mapper_r), FUNC(tutor_state::tutor_mapper_w));   /*cartridge mapper*/
	map(0xe200, 0xe200).w("sn76489a", FUNC(sn76489a_device::write));    /*sound chip*/
	map(0xe800, 0xe8ff).rw(FUNC(tutor_state::tutor_printer_r), FUNC(tutor_state::tutor_printer_w)); /*printer*/
	map(0xee00, 0xeeff).nopr().w(FUNC(tutor_state::tutor_cassette_w));     /*cassette interface*/

	map(0xf000, 0xffff).r(FUNC(tutor_state::tutor_highmem_r)).nopw(); /*free for expansion (and internal processor RAM)*/
}

void tutor_state::pyuutajr_mem(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	map(0x4000, 0x7fff).bankr("bank1").nopw();
	map(0x8000, 0xbfff).bankr("bank2").nopw();
	map(0xc000, 0xdfff).noprw(); /*free for expansion, or cartridge ROM?*/

	map(0xe000, 0xe000).rw("tms9928a", FUNC(tms9928a_device::vram_read), FUNC(tms9928a_device::vram_write));    /*VDP data*/
	map(0xe002, 0xe002).rw("tms9928a", FUNC(tms9928a_device::register_read), FUNC(tms9928a_device::register_write));/*VDP status*/
	map(0xe100, 0xe1ff).rw(FUNC(tutor_state::tutor_mapper_r), FUNC(tutor_state::tutor_mapper_w));   /*cartridge mapper*/
	map(0xe200, 0xe200).w("sn76489a", FUNC(sn76489a_device::write));    /*sound chip*/
	map(0xe800, 0xe800).portr("LINE0");
	map(0xea00, 0xea00).portr("LINE1");
	map(0xec00, 0xec00).portr("LINE2");
	map(0xee00, 0xee00).portr("LINE3");

	map(0xf000, 0xffff).r(FUNC(tutor_state::tutor_highmem_r)).nopw(); /*free for expansion (and internal processor RAM)*/
}

/*
    CRU map summary:

    >1ee0->1efe: tms9995 flag register
    >1fda: tms9995 MID flag

    >ec00->ec7e(r): keyboard interface
    >ed00(r): tape input
*/

void tutor_state::tutor_io(address_map &map)
{
	map(0xec00, 0xec7f).r(FUNC(tutor_state::key_r));               /*keyboard interface*/
	map(0xed00, 0xed01).r(FUNC(tutor_state::tutor_cassette_r));        /*cassette interface*/
}

/* tutor keyboard: 56 keys

2008-05 FP:
Small note about natural keyboard support: currently,
- "MON" is mapped to 'F1'
- "MOD" is mapped to 'F2'
- A, S, D, F, G, R seem to have problems in natural mode
*/

static INPUT_PORTS_START(tutor)
	PORT_START("LINE0")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)               PORT_CHAR('1') PORT_CHAR('!')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)               PORT_CHAR('2') PORT_CHAR('"')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)               PORT_CHAR('Q')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)               PORT_CHAR('W')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)               PORT_CHAR('A')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)               PORT_CHAR('S')
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)               PORT_CHAR('Z')
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)               PORT_CHAR('X')

	PORT_START("LINE1")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)               PORT_CHAR('3') PORT_CHAR('#')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)               PORT_CHAR('4') PORT_CHAR('$')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)               PORT_CHAR('E')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)               PORT_CHAR('R')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)               PORT_CHAR('D')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)               PORT_CHAR('F')
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)               PORT_CHAR('C')
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)               PORT_CHAR('V')

	PORT_START("LINE2")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)               PORT_CHAR('5') PORT_CHAR('%')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)               PORT_CHAR('6') PORT_CHAR('&')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)               PORT_CHAR('T')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)               PORT_CHAR('Y')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)               PORT_CHAR('G')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)               PORT_CHAR('H')
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)               PORT_CHAR('B')
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)               PORT_CHAR('N')

	PORT_START("LINE3")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)               PORT_CHAR('7') PORT_CHAR('\'')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)               PORT_CHAR('8') PORT_CHAR('(')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)               PORT_CHAR('9') PORT_CHAR(')')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)               PORT_CHAR('U')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)               PORT_CHAR('I')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)               PORT_CHAR('J')
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)               PORT_CHAR('K')
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)               PORT_CHAR('M')

	PORT_START("LINE4")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)               PORT_CHAR('0') PORT_CHAR('=')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("-  b") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)               PORT_CHAR('O')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)               PORT_CHAR('P')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)               PORT_CHAR('L')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)           PORT_CHAR(';') PORT_CHAR('+')
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)           PORT_CHAR(',') PORT_CHAR('<')
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)            PORT_CHAR('.') PORT_CHAR('>')

	PORT_START("LINE4_alt")
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_PLAYER(1)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_PLAYER(1)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN) PORT_PLAYER(1) PORT_CODE(KEYCODE_2_PAD)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT) PORT_PLAYER(1) PORT_CODE(KEYCODE_4_PAD)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP) PORT_PLAYER(1) PORT_CODE(KEYCODE_8_PAD)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_PLAYER(1) PORT_CODE(KEYCODE_6_PAD)

	PORT_START("LINE5")
		/* Unused? */
		PORT_BIT(0x03, IP_ACTIVE_HIGH, IPT_UNUSED)

		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("o  ^") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(186) PORT_CHAR('^')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)       PORT_CHAR('_') PORT_CHAR('@')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)           PORT_CHAR(':') PORT_CHAR('*')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)       PORT_CHAR('[') PORT_CHAR('{')
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)           PORT_CHAR('/') PORT_CHAR('?')
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)      PORT_CHAR(']') PORT_CHAR('}') // this one is 4th line, 4th key after 'M'

	PORT_START("LINE5_alt")
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_PLAYER(2)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_PLAYER(2)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN) PORT_PLAYER(2)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT) PORT_PLAYER(2)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP) PORT_PLAYER(2)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_PLAYER(2)

	PORT_START("LINE6")
		/* Unused? */
		PORT_BIT(0x21, IP_ACTIVE_HIGH, IPT_UNUSED)

		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
		/* only one shift key located on the left, but we support both for emulation to be friendlier */
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Mon") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RT") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)

		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Mod") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)           PORT_CHAR(' ')

	PORT_START("LINE7")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

		/* Unused? */
		PORT_BIT(0xf0, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

// Unit only has 12 buttons. LINE0 & 3 are scanned with the others, but have nothing connected?
static INPUT_PORTS_START(pyuutajr)
	PORT_START("LINE0")
		PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("LINE1")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("PALLET") PORT_CODE(KEYCODE_P)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MODE") PORT_CODE(KEYCODE_M)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MONITOR") PORT_CODE(KEYCODE_Q)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)

	PORT_START("LINE2")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_COMMA)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_STOP)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"\u2190") PORT_CODE(KEYCODE_LEFT)  // ←
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"\u2191") PORT_CODE(KEYCODE_UP)    // ↑
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"\u2193") PORT_CODE(KEYCODE_DOWN)  // ↓
		PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"\u2192") PORT_CODE(KEYCODE_RIGHT) // →

	PORT_START("LINE3")
		PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

void tutor_state::tutor(machine_config &config)
{
	// basic machine hardware
	// TMS9995 CPU @ 10.7 MHz
	// No lines connected yet
	TMS9995(config, m_maincpu, XTAL(10'738'635));
	m_maincpu->set_addrmap(AS_PROGRAM, &tutor_state::tutor_memmap);
	m_maincpu->set_addrmap(AS_IO, &tutor_state::tutor_io);

	// video hardware
	tms9928a_device &vdp(TMS9928A(config, "tms9928a", XTAL(10'738'635)));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	// Sound
	SPEAKER(config, "sound_out").front_center();
	SN76489A(config, "sn76489a", 3579545).add_route(ALL_OUTPUTS, "sound_out", 0.75);

	CENTRONICS(config, m_centronics, centronics_devices, "printer").busy_handler().set(FUNC(tutor_state::write_centronics_busy));

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	// Cassette
	SPEAKER(config, "cass_out").front_center();
	CASSETTE(config, "cassette", 0).add_route(ALL_OUTPUTS, "cass_out", 0.25);

	// Cartridge slot
	GENERIC_CARTSLOT(config, "cartslot", generic_linear_slot, "tutor_cart", "bin");

	// software lists
	SOFTWARE_LIST(config, "cart_list").set_original("tutor");

}

void tutor_state::pyuutajr(machine_config &config)
{
	tutor(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &tutor_state::pyuutajr_mem);
	//config.device_remove("centronics");
	//config.device_remove("cassette");
}

/*
  ROM loading
*/

ROM_START(tutor)
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD("tutor1.bin", 0x0000, 0x8000, CRC(702c38ba) SHA1(ce60607c3038895e31915d41bb5cf71cb8522d7a))      /* system ROM */
	ROM_LOAD("tutor2.bin", 0x8000, 0x4000, CRC(05f228f5) SHA1(46a14a45f6f9e2c30663a2b87ce60c42768a78d0))      /* BASIC ROM */
ROM_END


ROM_START(pyuuta)
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD("tomy29.7", 0x0000, 0x8000, CRC(7553bb6a) SHA1(fa41c45cb6d3daf7435f2a82f77dfa286003255e))      /* system ROM */
ROM_END

ROM_START(pyuutajr)
	ROM_REGION(0x10000, "maincpu", 0)
	ROM_LOAD( "ipl.rom", 0x0000, 0x4000, CRC(2ca37e62) SHA1(eebdc5c37d3b532edd5e5ca65eb785269ebd1ac0))      /* system ROM */
ROM_END

} // anonymous namespace

//    YEAR   NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS        INIT        COMPANY   FULLNAME           FLAGS
COMP( 1983?, tutor,    0,      0,      tutor,    tutor,    tutor_state, empty_init, "Tomy",   "Tomy Tutor" ,     0)
COMP( 1982,  pyuuta,   tutor,  0,      tutor,    tutor,    tutor_state, empty_init, "Tomy",   "Tomy Pyuuta" ,    0)
COMP( 1983,  pyuutajr, tutor,  0,      pyuutajr, pyuutajr, tutor_state, empty_init, "Tomy",   "Tomy Pyuuta Jr.", 0)



tv910.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont, AJR
/***************************************************************************

    TeleVideo Model 910 / 910 Plus

    Hardware:
    6502 CPU
    6545 CRTC
    6551 ACIA

    IRQ = ACIA gated with flip-flop driven by CRTC VBlank (not wire-OR)
    NMI = AY-5-3600 keyboard char present

    Esc-V (with a capital V) brings up the self-test screen.

    TODO:
        - Reverse attribute handling on the self-test screen doesn't seem
          to match the picture shown in the Operator's Manual
        - Make 910 keyboard into a device since the similar Model 925 uses
          a 950-compatible serial keyboard instead (with a second ACIA)
        - Add printer port and remaining DIP switches

****************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/m6502/m6502.h"
#include "machine/input_merger.h"
#include "machine/kb3600.h"
#include "machine/mos6551.h"
#include "sound/beep.h"
#include "video/mc6845.h"

#include "screen.h"
#include "speaker.h"


namespace {

#define ACIA_TAG    "acia"
#define CRTC_TAG    "crtc"
#define RS232_TAG   "rs232"
#define KBDC_TAG    "ay3600"

#define MASTER_CLOCK 13.608_MHz_XTAL

class tv910_state : public driver_device
{
public:
	tv910_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mainirq(*this, "mainirq")
		, m_crtc(*this, CRTC_TAG)
		, m_vram(*this, "vram")
		, m_chrrom(*this, "graphics")
		, m_ay3600(*this, KBDC_TAG)
		, m_kbdrom(*this, "keyboard")
		, m_kbspecial(*this, "keyb_special")
		, m_beep(*this, "bell")
		, m_dsw1(*this, "DSW1")
		, m_charset(*this, "CHARSET")
	{ }

	void tv910(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	MC6845_UPDATE_ROW(crtc_update_row);
	MC6845_ON_UPDATE_ADDR_CHANGED(crtc_update_addr);

	uint8_t charset_r();
	uint8_t kbd_ascii_r();
	uint8_t kbd_flags_r();

	void vbl_ack_w(uint8_t data);
	void nmi_ack_w(uint8_t data);
	void control_w(uint8_t data);

	void vbl_w(int state);

	int ay3600_shift_r();
	int ay3600_control_r();
	void ay3600_data_ready_w(int state);
	void ay3600_ako_w(int state);

	void tv910_mem(address_map &map) ATTR_COLD;

	required_device<m6502_device> m_maincpu;
	required_device<input_merger_device> m_mainirq;
	required_device<r6545_1_device> m_crtc;
	required_shared_ptr<uint8_t> m_vram;
	required_region_ptr<uint8_t> m_chrrom;
	required_device<ay3600_device> m_ay3600;
	required_region_ptr<uint8_t> m_kbdrom;
	required_ioport m_kbspecial;
	required_device<beep_device> m_beep;
	required_ioport m_dsw1;
	required_ioport m_charset;

	uint16_t m_lastchar, m_strobe;
	uint8_t m_transchar;
	bool m_anykeydown;
	int m_repeatdelay;

	uint8_t m_control;
};

void tv910_state::tv910_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x03ff).ram();
	map(0x4000, 0x47ff).ram().share("vram"); // VRAM
	map(0x8010, 0x801f).r(FUNC(tv910_state::charset_r));
	map(0x8020, 0x8020).rw(m_crtc, FUNC(r6545_1_device::status_r), FUNC(r6545_1_device::address_w));
	map(0x8021, 0x8021).rw(m_crtc, FUNC(r6545_1_device::register_r), FUNC(r6545_1_device::register_w));
	map(0x8030, 0x8033).rw(ACIA_TAG, FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x8040, 0x804f).w(FUNC(tv910_state::vbl_ack_w));
	map(0x8050, 0x805f).w(FUNC(tv910_state::nmi_ack_w));
	map(0x8060, 0x806f).r(FUNC(tv910_state::kbd_ascii_r));
	map(0x8070, 0x807f).r(FUNC(tv910_state::kbd_flags_r));
	map(0x9000, 0x9000).w(FUNC(tv910_state::control_w));
	map(0x9001, 0x9001).portr("DSW1");
	map(0x9002, 0x9002).portr("DSW2");
	map(0xf000, 0xffff).rom().region("maincpu", 0);
}

void tv910_state::control_w(uint8_t data)
{
	m_control = data;
	#if 0
	printf("%02x to control (%c%c%c%c%c)\n",
		data,
		(data & 0x10) ? 'U' : 'B',
		(data & 0x8) ? '6' : '5',
		(data & 0x4) ? 'X' : ' ',
		(data & 0x2) ? 'C' : 'c',
		(data & 1) ? 'B' : ' ');
	#endif

	m_beep->set_state(BIT(data, 0));
}

uint8_t tv910_state::charset_r()
{
	return m_charset->read();
}

void tv910_state::nmi_ack_w(uint8_t data)
{
	m_maincpu->set_input_line(M6502_NMI_LINE, CLEAR_LINE);
	m_strobe = 0;
}

uint8_t tv910_state::kbd_ascii_r()
{
	return m_transchar;
}

uint8_t tv910_state::kbd_flags_r()
{
	uint8_t rv = 0;
	ioport_value kbspecial = m_kbspecial->read();

	// D0: Keyboard strobe (AY-5-3600 AKO)
	if (m_anykeydown)
		rv |= 0x01;

	// D1: Printer DTR (pin 20)

	// D6: FUNC key (not ALPHA LOCK as indicated in memory map)
	if (BIT(kbspecial, 4))
		rv |= 0x40;

	// D7: ALPHA LOCK key (not FUNC as indicated in memory map)
	if (BIT(kbspecial, 0))
		rv |= 0x80;

	return rv;
}

int tv910_state::ay3600_shift_r()
{
	// either shift key
	if (m_kbspecial->read() & 0x06)
	{
		return ASSERT_LINE;
	}

	return CLEAR_LINE;
}

int tv910_state::ay3600_control_r()
{
	if (m_kbspecial->read() & 0x08)
	{
		return ASSERT_LINE;
	}

	return CLEAR_LINE;
}

void tv910_state::ay3600_data_ready_w(int state)
{
	if (state == ASSERT_LINE)
	{
		m_lastchar = m_ay3600->b_r();
		m_transchar = m_kbdrom[m_lastchar];
		m_strobe = 1;

		m_maincpu->set_input_line(M6502_NMI_LINE, ASSERT_LINE);
		//printf("new char = %04x (%02x)\n", m_lastchar, m_transchar);
	}
}

void tv910_state::ay3600_ako_w(int state)
{
	m_anykeydown = (state == ASSERT_LINE) ? true : false;

	if (m_anykeydown)
	{
		m_repeatdelay = 10;
	}
}

/* Input ports */

/*
Keyboard matrix (thanks to Al Kossow!)

   X0     X1     X2     X3     X4     X5     X6     X7
Y8                             BKTAB  FN19   FN18   FN17
Y7  B     3      E      F      [      RET    `      {
Y6  .     7      U      K      BRK    SPACE  BS     HOME
Y5  V     2      W      D      P      ENTER  0      DEL
Y4  ,     6      Y      J      '      /      9      DOWN
Y3  C     1      Q      S      O      ;      =      CLRSP
Y2  M     5      T      H             LEFT   8      UP
Y1  X     ESC    TAB    A      I      L      -      Z
Y0  N     4      R      G      LF     PRNT   \      RIGHT
*/

static INPUT_PORTS_START( tv910 )
	PORT_START("X0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)          PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)          PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)          PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)          PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)          PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)          PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X1")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Esc")              PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)          PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab")              PORT_CODE(KEYCODE_TAB)      PORT_CHAR(9)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)          PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)          PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)          PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X3")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)          PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)          PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)          PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)          PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)          PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)          PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)          PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("X4")
	/// 001 - LF
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)          PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR('\'') PORT_CHAR('\"')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P')
	/// 040 - BRK
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[') PORT_CHAR(']')
	/// 100 - BACKTAB

	PORT_START("X5")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PRTSCR)    PORT_CHAR(UCHAR_MAMEKEY(PRTSCR))
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)          PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"\u2190")         PORT_CODE(KEYCODE_LEFT) // ←
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD)  PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Return")           PORT_CODE(KEYCODE_ENTER)    PORT_CHAR(13)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F19)       PORT_CHAR(UCHAR_MAMEKEY(F19))

	PORT_START("X6")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Backspace")        PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('`') PORT_CHAR('~')
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F18)       PORT_CHAR(UCHAR_MAMEKEY(F18))

	PORT_START("X7")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"\u2192")         PORT_CODE(KEYCODE_RIGHT) // →
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"\u2191")         PORT_CODE(KEYCODE_UP) // ↑
/// 008 - CLRSP
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(u8"\u2193")         PORT_CODE(KEYCODE_DOWN)     PORT_CHAR(10)      // ↓  E0 47
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('{') PORT_CHAR('}')

	PORT_START("X8")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("keyb_special")
	PORT_BIT( 0x01, IP_ACTIVE_LOW,  IPT_KEYBOARD) PORT_NAME("Caps Lock")    PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Shift")   PORT_CODE(KEYCODE_LSHIFT)   PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Shift")  PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Control")      PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x10, IP_ACTIVE_LOW,  IPT_KEYBOARD) PORT_NAME("Func") PORT_CODE(KEYCODE_LALT)

	PORT_START("DSW2")
	PORT_DIPNAME( 0x0f, 0x00, "Baud Rate" ) PORT_DIPLOCATION("S1:4,3,2,1")
	PORT_DIPSETTING(    0x1, "50" )
	PORT_DIPSETTING(    0x2, "75" )
	PORT_DIPSETTING(    0x3, "110" )
	PORT_DIPSETTING(    0x4, "135" )
	PORT_DIPSETTING(    0x5, "150" )
	PORT_DIPSETTING(    0x6, "300" )
	PORT_DIPSETTING(    0x7, "600" )
	PORT_DIPSETTING(    0x8, "1200" )
	PORT_DIPSETTING(    0x9, "1800" )
	PORT_DIPSETTING(    0xa, "2400" )
	PORT_DIPSETTING(    0xb, "3600" )
	PORT_DIPSETTING(    0xc, "4800" )
	PORT_DIPSETTING(    0xd, "7200" )
	PORT_DIPSETTING(    0x0, "9600" )
	//PORT_DIPSETTING(  0xe, "9600" )
	PORT_DIPSETTING(    0xf, "19200" )

	PORT_DIPNAME( 0x10, 0x00, "Word Length" ) PORT_DIPLOCATION("S1:5")
	PORT_DIPSETTING( 0x00, "8 data bits" )
	PORT_DIPSETTING( 0x10, "7 data bits" )

	PORT_DIPNAME( 0x20, 0x00, "Parity" ) PORT_DIPLOCATION("S1:6")
	PORT_DIPSETTING( 0x00, "No parity" )
	PORT_DIPSETTING( 0x20, "Send parity" )

	PORT_DIPNAME( 0x40, 0x00, "Parity Type" ) PORT_DIPLOCATION("S1:7")
	PORT_DIPSETTING( 0x00, "Odd" )
	PORT_DIPSETTING( 0x40, "Even" )

	PORT_DIPNAME( 0x80, 0x00, "Stop Bits" ) PORT_DIPLOCATION("S1:8")
	PORT_DIPSETTING( 0x00, "1" )
	PORT_DIPSETTING( 0x80, "2" )

	PORT_START("DSW1")
	PORT_DIPNAME( 0x01, 0x01, "CR Code" ) PORT_DIPLOCATION("S1:10") // TCHAR0
	PORT_DIPSETTING( 0x00, "CR only" )
	PORT_DIPSETTING( 0x01, "CRLF" )

	PORT_DIPNAME( 0x02, 0x02, "Auto Wraparound at 80th Position" ) PORT_DIPLOCATION("S1:9") // TCHAR1
	PORT_DIPSETTING( 0x00, DEF_STR(No) )
	PORT_DIPSETTING( 0x02, DEF_STR(Yes) ) // required for self-test to work

	PORT_DIPNAME( 0x0c, 0x00, "Emulation" ) PORT_DIPLOCATION("S2:1,2")
	PORT_DIPSETTING(    0x0, "Standard 910" )
	PORT_DIPSETTING(    0x4, "ADM-3A/5" )
	PORT_DIPSETTING(    0x8, "ADDS 25" )
	PORT_DIPSETTING(    0xc, "Hazeltine 1410" )

	PORT_DIPNAME( 0x10, 0x00, "Refresh Rate" ) PORT_DIPLOCATION("S2:3")
	PORT_DIPSETTING( 0x00, "60 Hz" )
	PORT_DIPSETTING( 0x10, "50 Hz" )

	PORT_DIPNAME( 0x60, 0x00, "Cursor Type" ) PORT_DIPLOCATION("S2:4,5")
	PORT_DIPSETTING(    0x00, "Blinking block" )
	PORT_DIPSETTING(    0x40, "Blinking underline" )
	PORT_DIPSETTING(    0x20, "Steady block" )
	PORT_DIPSETTING(    0x60, "Steady underline" )

	PORT_DIPNAME( 0x80, 0x00, "Conversation Mode" ) PORT_DIPLOCATION("S2:6") // F/HDX
	PORT_DIPSETTING( 0x00, "Half duplex" )
	PORT_DIPSETTING( 0x80, "Full duplex" )

	PORT_DIPNAME( 0x100, 0x100, "Colors" ) PORT_DIPLOCATION("S2:7") // BOW/WOB
	PORT_DIPSETTING( 0x00, "Black characters on green screen" )
	PORT_DIPSETTING( 0x100, "Green characters on black screen" )

#if 0
	PORT_DIPNAME( 0x200, 0x200, "Data Set Ready" )
	PORT_DIPSETTING( 0x00, "DSR connected" )
	PORT_DIPSETTING( 0x200, "DSR disconnected" )
#endif

	PORT_START("CHARSET") // actually a pair of jumpers: E4-E5 (bit 1), E6-E7 (bit 0)
	PORT_DIPNAME( 0x03, 0x00, "Character Set" )
	PORT_DIPSETTING( 0x00, "English" )
	PORT_DIPSETTING( 0x01, "German" )
	PORT_DIPSETTING( 0x02, "French" )
	PORT_DIPSETTING( 0x03, "Spanish" )
INPUT_PORTS_END

void tv910_state::machine_start()
{
	// DCD needs to be driven somehow, or else the terminal will complain
	// CTS also needs to be driven to prevent hanging caused by buffer overflow
	auto *acia = subdevice<mos6551_device>(ACIA_TAG);
	auto *rs232 = subdevice<rs232_port_device>(RS232_TAG);
	if (rs232->get_card_device() == nullptr)
	{
		acia->write_dcd(0);
		acia->write_cts(0);
	}

	// DSR is tied to GND
	acia->write_dsr(0);
}

void tv910_state::machine_reset()
{
	control_w(0);
}

MC6845_ON_UPDATE_ADDR_CHANGED( tv910_state::crtc_update_addr )
{
}

void tv910_state::vbl_w(int state)
{
	// this is ACKed by vbl_ack_w, state going 0 here doesn't ack the IRQ
	if (state)
		m_mainirq->in_w<0>(1);
}

void tv910_state::vbl_ack_w(uint8_t data)
{
	m_mainirq->in_w<0>(0);
}

MC6845_UPDATE_ROW( tv910_state::crtc_update_row )
{
	uint32_t  *p = &bitmap.pix(y);
	uint16_t  chr_base = (ra & 7) | m_charset->read() << 10;
	uint8_t   chr = m_vram[0x7ff];
	uint8_t   att = (chr & 0xf0) == 0x90 ? chr & 0x0f : 0;
	bool      bow = BIT(m_dsw1->read(), 8);

	for (int i = 0; i < x_count; i++)
	{
		uint16_t offset = ( ma + i ) & 0x7ff;
		uint8_t chr = m_vram[ offset ];
		bool att_blk = (chr & 0xf0) == 0x90;
		bool half_int = BIT(chr, 7) && !att_blk;
		if (att_blk)
			att = chr & 0x0f;

		uint8_t data = m_chrrom[chr_base | (chr & 0x7f) << 3];
		rgb_t fg = rgb_t::green();
		rgb_t bg = rgb_t::black();
		if (half_int)
			fg = rgb_t(fg.r() / 2, fg.g() / 2, fg.b() / 2);

		if (ra == 9)
			data = BIT(att, 3) ? 0xff : 0;
		else if (ra == 0 || att_blk || BIT(att, 0) || (BIT(att, 1) && BIT(m_control, 1)))
			data = 0;

		if (i == cursor_x && (!BIT(m_control, 4) || ra == 9))
			data ^= 0xff;
		if (BIT(att, 2))
			data ^= 0xff;
		if (bow)
			data ^= 0xff;

		*p++ = BIT(data, 7) ? bg : fg;
		*p++ = BIT(data, 6) ? bg : fg;
		*p++ = BIT(data, 5) ? bg : fg;
		*p++ = BIT(data, 4) ? bg : fg;
		*p++ = BIT(data, 3) ? bg : fg;
		*p++ = BIT(data, 2) ? bg : fg;
		*p++ = BIT(data, 1) ? bg : fg;
		*p++ = BIT(data, 0) ? bg : fg;
	}
}

void tv910_state::tv910(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, MASTER_CLOCK/8);
	m_maincpu->set_addrmap(AS_PROGRAM, &tv910_state::tv910_mem);

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(MASTER_CLOCK, 840, 0, 640, 270, 0, 240);
	screen.set_screen_update(CRTC_TAG, FUNC(r6545_1_device::screen_update));

	R6545_1(config, m_crtc, MASTER_CLOCK/8);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(tv910_state::crtc_update_row));
	m_crtc->set_on_update_addr_change_callback(FUNC(tv910_state::crtc_update_addr));
	m_crtc->out_vsync_callback().set(FUNC(tv910_state::vbl_w));

	AY3600(config, m_ay3600, 0);
	m_ay3600->x0().set_ioport("X0");
	m_ay3600->x1().set_ioport("X1");
	m_ay3600->x2().set_ioport("X2");
	m_ay3600->x3().set_ioport("X3");
	m_ay3600->x4().set_ioport("X4");
	m_ay3600->x5().set_ioport("X5");
	m_ay3600->x6().set_ioport("X6");
	m_ay3600->x7().set_ioport("X7");
	m_ay3600->x8().set_ioport("X8");
	m_ay3600->shift().set(FUNC(tv910_state::ay3600_shift_r));
	m_ay3600->control().set(FUNC(tv910_state::ay3600_control_r));
	m_ay3600->data_ready().set(FUNC(tv910_state::ay3600_data_ready_w));
	m_ay3600->ako().set(FUNC(tv910_state::ay3600_ako_w));

	mos6551_device &acia(MOS6551(config, ACIA_TAG, 0));
	acia.set_xtal(1.8432_MHz_XTAL);
	acia.irq_handler().set("mainirq", FUNC(input_merger_device::in_w<1>));
	acia.txd_handler().set(RS232_TAG, FUNC(rs232_port_device::write_txd));
	acia.rts_handler().set(RS232_TAG, FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, RS232_TAG, default_rs232_devices, nullptr));
	rs232.rxd_handler().set(ACIA_TAG, FUNC(mos6551_device::write_rxd));
	rs232.dcd_handler().set(ACIA_TAG, FUNC(mos6551_device::write_dcd));
	rs232.cts_handler().set(ACIA_TAG, FUNC(mos6551_device::write_cts));

	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, MASTER_CLOCK / 8400); // 1620 Hz (Row 10 signal)
	m_beep->add_route(ALL_OUTPUTS, "mono", 0.50);
}

/* ROM definition */
ROM_START( tv910 )
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD( "1800000-020e_a38_9182.bin", 0x000000, 0x001000, CRC(ae71dd7f) SHA1(a12da9329e28a4a8e3c902f795059251311d2856) )

	ROM_REGION(0x2000, "graphics", 0)
	ROM_LOAD( "1800000-016a_a17_85ae.bin", 0x000000, 0x001000, CRC(835445b7) SHA1(dde94fb6531dadce48e19bf551f45f61bedf905b) )

	ROM_REGION(0x1000, "keyboard", 0)
	ROM_LOAD( "1800000-019b_bell_a2_43d6.bin", 0x000000, 0x000800, CRC(de954a77) SHA1(c4f7c19799c15d12d89f08dc31064fc6be9befb0) )
ROM_END

} // anonymous namespace


/* Driver */
//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY              FULLNAME               FLAGS
COMP( 1981, tv910, 0,      0,      tv910,   tv910, tv910_state, empty_init, "TeleVideo Systems", "TeleVideo Model 910", 0 )



tv912.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    TeleVideo TVI-912/TVI-920 terminals

    This was the first series of terminals from TeleVideo. The models differed
    from each other in the number and pattern of keys on the nondetachable
    keyboard. Those with a B suffix had a TTY-style keyboard, while the C
    suffix indicated a typewriter-style keyboard. The TVI-920 added a row of
    function keys but was otherwise mostly identical to the TVI-912. The
    TVI-912C keyboard matrix and ribbon connector pinout are almost the same
    as in the TVI-910. A self-test display pattern can be triggered by shorting
    two wires on the keyboard unit near the connector.

    Settings for these terminals are controlled by DIP switches, which are not
    read by the CPU except for those of S4 (which is usually left as a block of
    bare jumpers) and the half-duplex and 50/60 Hz switches of S2. Four to five
    of the S2 switches directly manipulate UART pins to select the number and
    framing of data bits. Position 8 (NB2) is deliberately disconnected on
    later revisions to restrict communications to 7 or 8 data bits, likely
    because of known incompatibilities between UART models in 5-bit data mode.
    (For reasons that are less clear, the 19,200 baud option is also commonly
    omitted from documentation.)

    The baud rate switches (S1/S3) are mounted vertically at the rear of the
    unit. As these are connected directly to the frequency outputs of the baud
    rate generation circuit (which consists mainly of the LS163s at A73, A70,
    A71 and A72), only one switch may be down (closed) at a time. (The nominal
    "75 baud" output of this circuit doubles as the beep sound frequency.)
    Originally the UART was connected to receive and transmit data at the
    printer rate when the printer was selected, even though it did not receive
    any data through the printer connector. Later board revisions fixed this
    by tying the signal selected by S1 to the UART's RCP; S3 could be similarly
    tied to TCP by modifying a couple of jumpers.

    The hardware appears to have been originally designed to access two 2316E
    program ROMs (alternatively 8316E or 2716), with the lower 2K at A50 and
    upper 2K at A49. These were ultimately merged into a single 2332 or 8332A
    at A49; several jumpers in the area could be inserted or removed to
    accommodate these different ROM types. At least the later A49B1 and A49C1
    mask ROMs (both MM52132 types) were apparently interchangeable between
    TVI-912B/C and TVI-920B/C. Part or all of the program ROM could also be
    made internal to the CPU by replacing the 8035 with a 8048, 8748 or 8049
    and grounding the EA pin by inserting a jumper at W1.

    Each displayed character nominally consists of 6 x 8 dots within a 7 x 10
    cell. However, the lowest two bits of each row of character data may be
    set to shift either the first three dots, the last three dots or both
    forward by half a dot, thus effectively doubling horizontal resolution.

*******************************************************************************/

#include "emu.h"
#include "cpu/mcs48/mcs48.h"
#include "bus/rs232/rs232.h"
#include "machine/ay31015.h"
#include "machine/input_merger.h"
#include "sound/beep.h"
#include "video/tms9927.h"
#include "screen.h"
#include "speaker.h"

namespace {

class tv912_state : public driver_device
{
	static constexpr int TV912_CH_WIDTH = 14;

public:
	tv912_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_crtc(*this, "crtc")
		, m_uart(*this, "uart")
		, m_rs232(*this, "rs232")
		, m_txd_merger(*this, "txd")
		, m_beep(*this, "beep")
		, m_dispram_bank(*this, "dispram")
		, m_p_chargen(*this, "chargen")
		, m_keys(*this, "KEY%u", 0)
		, m_modem_baud(*this, "MODEMBAUD")
		, m_printer_baud(*this, "PRINTBAUD")
		, m_uart_control(*this, "UARTCTRL")
		, m_video_control(*this, "VIDEOCTRL")
		, m_modifiers(*this, "MODIFIERS")
		, m_half_duplex(*this, "HALFDUP")
		, m_jumpers(*this, "JUMPERS")
		, m_option(*this, "OPTION")
		, m_dtr(*this, "DTR")
		, m_io_view(*this, "io")
		, m_dispram(*this, "dispram", 0x1000, ENDIANNESS_LITTLE)
		, m_baudgen_timer(nullptr)
		, m_force_blank(false)
		, m_4hz_flasher(false)
		, m_2hz_flasher(false)
		, m_lpt_select(false)
		, m_keyboard_scan(false)
		, m_bank_select(0)
	{ }

	void tv912(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(uart_settings_changed);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void prog_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	TIMER_CALLBACK_MEMBER(update_baudgen);

	void p1_w(u8 data);
	u8 p2_r();
	void p2_w(u8 data);
	u8 crtc_r(offs_t offset);
	void crtc_w(offs_t offset, u8 data);
	u8 uart_status_r(offs_t offset);
	u8 keyboard_r(offs_t offset);
	void output_40c(u8 data);

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	required_device<cpu_device> m_maincpu;
	required_device<tms9927_device> m_crtc;
	required_device<ay51013_device> m_uart;
	required_device<rs232_port_device> m_rs232;
	required_device<input_merger_device> m_txd_merger;
	required_device<beep_device> m_beep;
	required_memory_bank m_dispram_bank;
	required_region_ptr<u8> m_p_chargen;
	required_ioport_array<32> m_keys;
	required_ioport m_modem_baud;
	required_ioport m_printer_baud;
	required_ioport m_uart_control;
	required_ioport m_video_control;
	required_ioport m_modifiers;
	required_ioport m_half_duplex;
	required_ioport m_jumpers;
	required_ioport m_option;
	required_ioport m_dtr;

	memory_view m_io_view;
	memory_share_creator<u8> m_dispram;

	emu_timer *m_baudgen_timer;

	bool m_force_blank;
	bool m_4hz_flasher;
	bool m_2hz_flasher;
	bool m_lpt_select;
	u8 m_keyboard_scan;
	u8 m_bank_select;
};

void tv912_state::p1_w(u8 data)
{
	m_keyboard_scan = data;
}

u8 tv912_state::p2_r()
{
	ioport_value dup = m_half_duplex->read();

	// P27: -HALF DUPLEX
	u8 result = BIT(dup, 0) << 7;

	// P26: -PTR RDY

	// P25: -DCR
	if (!BIT(dup, 1))
		result |= m_rs232->dsr_r() << 5;
	if (!BIT(dup, 2))
		result |= m_rs232->dcd_r() << 5;

	return result | 0x5f;
}

void tv912_state::p2_w(u8 data)
{
	// P20-P23: Address Signals (4MSBS)
	m_io_view.select(std::min((data >> 2) & 3, 2));
	m_bank_select = (m_bank_select & 0x08) | (data & 0x07);
	m_dispram_bank->set_entry(m_bank_select);

	// P24: +4Hz Flasher
	if (BIT(data, 4) && !m_4hz_flasher)
		m_2hz_flasher = !m_2hz_flasher;
	m_4hz_flasher = BIT(data, 4);
}

u8 tv912_state::crtc_r(offs_t offset)
{
	return m_crtc->read(bitswap<4>(offset, 5, 4, 1, 0));
}

void tv912_state::crtc_w(offs_t offset, u8 data)
{
	m_crtc->write(bitswap<4>(offset, 5, 4, 1, 0), data);
}

u8 tv912_state::keyboard_r(offs_t offset)
{
	u8 result = m_modifiers->read();

	for (int b = 0; b < 8; b++)
		if (!BIT(m_keyboard_scan, b))
			result &= m_keys[b * 4 + offset]->read();

	return result;
}

u8 tv912_state::uart_status_r(offs_t offset)
{
	m_uart->write_swe(0);
	u8 status = m_uart->dav_r() << 0;
	status |= m_uart->tbmt_r() << 1;
	status |= m_uart->pe_r() << 2;
	status |= m_uart->fe_r() << 3;
	status |= BIT(m_jumpers->read(), offset) << 4;
	status |= m_option->read();
	m_uart->write_swe(1);
	return status;
}

void tv912_state::output_40c(u8 data)
{
	// DB6: -PRTOL (TODO)

	// DB5: +FORCE BLANK
	m_force_blank = BIT(data, 5);

	// DB4: +SEL LPT
	m_lpt_select = BIT(data, 4);

	// DB3: -BREAK
	m_txd_merger->in_w<1>(BIT(data, 3));

	// DB2: -RQS
	ioport_value dtr = m_dtr->read();
	m_rs232->write_rts(BIT(data, 2));
	if (!BIT(dtr, 0))
		m_rs232->write_dtr(BIT(data, 2));
	if (!BIT(dtr, 1))
		m_rs232->write_dtr(0);

	// DB1: +BEEP
	m_beep->set_state(BIT(data, 1));

	// DB0: +PG SEL
	m_bank_select = (data & 0x01) << 3 | (m_bank_select & 0x07);
	m_dispram_bank->set_entry(m_bank_select);
}

TIMER_CALLBACK_MEMBER(tv912_state::update_baudgen)
{
	m_uart->write_rcp(param);
	m_uart->write_tcp(param);

	ioport_value sel = (m_lpt_select ? m_printer_baud : m_modem_baud)->read();
	for (int b = 0; b < 10; b++)
	{
		if (!BIT(sel, b))
		{
			unsigned divisor = 11 * (b < 9 ? 1 << b : 176);
			m_baudgen_timer->adjust(attotime::from_hz(23.814_MHz_XTAL / 3.5 / divisor), !param);
			break;
		}
	}
}

u32 tv912_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if (m_crtc->screen_reset() || m_force_blank)
	{
		bitmap.fill(rgb_t::black(), cliprect);
		return 0;
	}

	const u8 *dispram = &m_dispram[u16(m_bank_select & 0x08) << 8];
	ioport_value videoctrl = m_video_control->read();

	rectangle curs;
	m_crtc->cursor_bounds(curs);

	int scroll = m_crtc->upscroll_offset();

	u8 charctrl = 0x8, charctrl_latch = 0x8;

	for (int y = 0; y < 240; y++)
	{
		int row = ((y / 10) + scroll) % 24;
		int ra = y % 10;
		int x = 0;
		u8 *charbase = &m_p_chargen[(ra & 7) | BIT(videoctrl, 1) << 10];

		if (ra == 0)
			charctrl_latch = charctrl;
		else
			charctrl = charctrl_latch;

		for (int pos = 0; pos < 80; pos++)
		{
			u8 ch = (pos < 64)
					? dispram[(row << 6) | pos]
					: dispram[0x600 | ((row & 0x07) << 6) | ((row & 0x18) << 1) | (pos & 0x0f)];

			bool inhibit = ra == 0 || ra == 9;
			bool underline = ra == 9 && BIT(charctrl, 0);
			bool invert = BIT(charctrl, 1);
			if ((ch & 0x60) == 0)
			{
				inhibit = true;
				if (BIT(ch, 4))
					charctrl = ch & 0xf;
				else
					charctrl = (charctrl & 0xc) | (ch & 0x3);
				if (!BIT(ch, 0))
					underline = false;
				if (!BIT(ch, 1))
					invert = false;
			}
			else if ((charctrl & 0xc) == 0)
			{
				inhibit = true;
				underline = false;
				invert = false;
			}
			else if ((charctrl & 0xc) == 0xc && m_2hz_flasher)
				inhibit = true;

			u8 data = inhibit ? 0 : charbase[(ch & 0x7f) << 3];
			u8 dots = underline ? 0xff : (data & 0xfc) >> 1;
			bool adv = BIT(data, 1);

			if (x == curs.left() && y >= curs.top() && y <= curs.bottom())
			{
				if (m_4hz_flasher && !BIT(videoctrl, 0))
					dots = 0;
				else
					dots ^= 0xff;
			}
			if (invert)
				dots ^= 0xff;

			// Protected characters are displayed at half intensity
			rgb_t fg = BIT(ch, 7) ? rgb_t(0xc0, 0xc0, 0xc0) : rgb_t::white();
			for (int d = 0; d < TV912_CH_WIDTH / 2; d++)
			{
				if (x >= cliprect.left() && x <= cliprect.right())
					bitmap.pix(y, x) = BIT(dots, 7) ? fg : rgb_t::black();
				x++;
				if (adv)
					dots <<= 1;
				if (x >= cliprect.left() && x <= cliprect.right())
					bitmap.pix(y, x) = BIT(dots, 7) ? fg : rgb_t::black();
				x++;
				if (!adv)
					dots <<= 1;

				if (d == 2)
					adv = BIT(data, 0);
			}
		}
	}

	return 0;
}

void tv912_state::machine_start()
{
	m_dispram_bank->configure_entries(0, 16, m_dispram.target(), 0x100);

	m_baudgen_timer = timer_alloc(FUNC(tv912_state::update_baudgen), this);
	m_baudgen_timer->adjust(attotime::zero, 0);

	save_item(NAME(m_force_blank));
	save_item(NAME(m_lpt_select));
	save_item(NAME(m_4hz_flasher));
	save_item(NAME(m_2hz_flasher));
	save_item(NAME(m_keyboard_scan));
	save_item(NAME(m_bank_select));
}

void tv912_state::machine_reset()
{
	ioport_value uart_ctrl = m_uart_control->read();
	m_uart->write_np(BIT(uart_ctrl, 4));
	m_uart->write_tsb(BIT(uart_ctrl, 3));
	m_uart->write_nb1(BIT(uart_ctrl, 2));
	m_uart->write_nb2(BIT(uart_ctrl, 1));
	m_uart->write_eps(BIT(uart_ctrl, 0));
	m_uart->write_cs(1);
}

void tv912_state::prog_map(address_map &map)
{
	map(0x000, 0xfff).rom().region("maincpu", 0);
}

void tv912_state::io_map(address_map &map)
{
	map(0x00, 0xff).view(m_io_view);
	m_io_view[0](0x00, 0xff).ram();
	m_io_view[1](0x00, 0x03).mirror(0xc0).select(0x30).rw(FUNC(tv912_state::crtc_r), FUNC(tv912_state::crtc_w));
	m_io_view[1](0x04, 0x04).mirror(0xf3).r(m_uart, FUNC(ay51013_device::receive));
	m_io_view[1](0x08, 0x0b).mirror(0xf0).r(FUNC(tv912_state::uart_status_r));
	m_io_view[1](0x08, 0x08).mirror(0xf3).w(m_uart, FUNC(ay51013_device::transmit));
	m_io_view[1](0x0c, 0x0f).mirror(0xf0).r(FUNC(tv912_state::keyboard_r));
	m_io_view[1](0x0c, 0x0c).mirror(0xf3).w(FUNC(tv912_state::output_40c));
	m_io_view[2](0x00, 0xff).bankrw(m_dispram_bank);
}

INPUT_CHANGED_MEMBER(tv912_state::uart_settings_changed)
{
	ioport_value uart_ctrl = m_uart_control->read();
	m_uart->write_np(BIT(uart_ctrl, 4));
	m_uart->write_tsb(BIT(uart_ctrl, 3));
	m_uart->write_nb1(BIT(uart_ctrl, 2));
	m_uart->write_nb2(BIT(uart_ctrl, 1));
	m_uart->write_eps(BIT(uart_ctrl, 0));
}

static INPUT_PORTS_START( switches )
	PORT_START("MODEMBAUD")
	PORT_DIPNAME(0x3ff, 0x3fd, "Modem Port Baud Rate") PORT_DIPLOCATION("S1:1,2,3,4,5,6,7,8,9,10")
	PORT_DIPSETTING(0x2ff, "75") // actual rate: 1,208 ÷ 16 ≈ 75.5
	PORT_DIPSETTING(0x1ff, "110") // actual rate: 1,757 ÷ 16 ≈ 109.8
	PORT_DIPSETTING(0x37f, "150") // actual rate: 2,416 ÷ 16 ≈ 151
	PORT_DIPSETTING(0x3bf, "300") // actual rate: 4,832 ÷ 16 ≈ 302
	PORT_DIPSETTING(0x3df, "600") // actual rate: 9,665 ÷ 16 ≈ 604
	PORT_DIPSETTING(0x3ef, "1200") // actual rate: 19,330 ÷ 16 ≈ 1,208
	PORT_DIPSETTING(0x3f7, "2400") // actual rate: 38,659 ÷ 16 ≈ 2,416
	PORT_DIPSETTING(0x3fb, "4800") // actual rate: 77,318 ÷ 16 ≈ 4,832
	PORT_DIPSETTING(0x3fd, "9600") // actual rate: 154,636 ÷ 16 ≈ 9,665
	PORT_DIPSETTING(0x3fe, "19200") // actual rate: 309,273 ÷ 16 ≈ 19,330

	PORT_START("PRINTBAUD")
	PORT_DIPNAME(0x3ff, 0x3fd, "Printer Port Baud Rate") PORT_DIPLOCATION("S3:1,2,3,4,5,6,7,8,9,10")
	PORT_DIPSETTING(0x2ff, "75")
	PORT_DIPSETTING(0x1ff, "110")
	PORT_DIPSETTING(0x37f, "150")
	PORT_DIPSETTING(0x3bf, "300")
	PORT_DIPSETTING(0x3df, "600")
	PORT_DIPSETTING(0x3ef, "1200")
	PORT_DIPSETTING(0x3f7, "2400")
	PORT_DIPSETTING(0x3fb, "4800")
	PORT_DIPSETTING(0x3fd, "9600")
	PORT_DIPSETTING(0x3fe, "19200")

	PORT_START("VIDEOCTRL")
	PORT_DIPUNUSED_DIPLOC(0x04, 0x04, "S2:1") // disables TTL video output on earlier revisions
	PORT_DIPNAME(0x02, 0x00, "Character Set") PORT_DIPLOCATION("S2:2")
	PORT_DIPSETTING(0x00, "Standard")
	PORT_DIPSETTING(0x02, "Alternate")
	PORT_DIPNAME(0x01, 0x00, "Cursor Flash") PORT_DIPLOCATION("S2:10") // originally jumper W25
	PORT_DIPSETTING(0x01, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))
	// S2:10 was previously used to short out 270 ohm resistor in video section

	PORT_START("UARTCTRL")
	PORT_DIPNAME(0x11, 0x11, "Parity Select") PORT_DIPLOCATION("S2:9,5") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tv912_state::uart_settings_changed), 0)
	PORT_DIPSETTING(0x11, "None")
	PORT_DIPSETTING(0x01, "Even")
	PORT_DIPSETTING(0x00, "Odd")
	PORT_DIPNAME(0x08, 0x00, "Stop Bits") PORT_DIPLOCATION("S2:6") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tv912_state::uart_settings_changed), 0)
	PORT_DIPSETTING(0x00, "1")
	PORT_DIPSETTING(0x08, "2")
	PORT_DIPNAME(0x06, 0x06, "Data Bits") PORT_DIPLOCATION("S2:8,7") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tv912_state::uart_settings_changed), 0)
	PORT_DIPSETTING(0x00, "5")
	PORT_DIPSETTING(0x04, "6")
	PORT_DIPSETTING(0x02, "7")
	PORT_DIPSETTING(0x06, "8")

	PORT_START("MODIFIERS")
	PORT_DIPNAME(0x80, 0x80, "Refresh Rate") PORT_DIPLOCATION("S2:4")
	PORT_DIPSETTING(0x00, "50 Hz")
	PORT_DIPSETTING(0x80, "60 Hz")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Alpha Lock") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CHAR(UCHAR_SHIFT_1) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CHAR(UCHAR_SHIFT_2) PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Funct") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x23, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("HALFDUP")
	PORT_DIPNAME(0x01, 0x01, "Conversation Mode") PORT_DIPLOCATION("S2:3")
	PORT_DIPSETTING(0x00, "Half Duplex")
	PORT_DIPSETTING(0x01, "Full Duplex")
	PORT_DIPNAME(0x06, 0x04, "DCR (RS232)") PORT_DIPLOCATION("S5:1,2")
	PORT_DIPSETTING(0x04, "DSR") // at P3-6
	PORT_DIPSETTING(0x02, "DCD") // at P3-8

	PORT_START("JUMPERS")
	PORT_DIPNAME(0x08, 0x00, "Automatic CRLF") PORT_DIPLOCATION("S4:1") // or jumper W31
	PORT_DIPSETTING(0x08, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))
	PORT_DIPNAME(0x04, 0x04, "End of Send Character") PORT_DIPLOCATION("S4:2") // or jumper W32
	PORT_DIPSETTING(0x04, "CR")
	PORT_DIPSETTING(0x00, "EOT")
	PORT_DIPNAME(0x02, 0x02, "Column 80 CRLF") PORT_DIPLOCATION("S4:3") // or jumper W33
	PORT_DIPSETTING(0x00, DEF_STR(Off))
	PORT_DIPSETTING(0x02, DEF_STR(On))
	PORT_DIPNAME(0x01, 0x01, "Terminal Mode") PORT_DIPLOCATION("S4:4") // or jumper W34
	PORT_DIPSETTING(0x01, "Extension")
	PORT_DIPSETTING(0x00, "Page Print")

	PORT_START("OPTION")
	PORT_DIPUNKNOWN_DIPLOC(0x20, 0x20, "S4:5")
	PORT_DIPUNKNOWN_DIPLOC(0x40, 0x40, "S4:6")
	PORT_DIPUNKNOWN_DIPLOC(0x80, 0x80, "S4:7")

	PORT_START("DTR")
	PORT_DIPNAME(0x03, 0x02, "DTR (RS232)") PORT_DIPLOCATION("S5:3,4")
	PORT_DIPSETTING(0x02, "Tied to RTS")
	PORT_DIPSETTING(0x01, "Pulled to +12V")
INPUT_PORTS_END

static INPUT_PORTS_START( tv912b )
	PORT_INCLUDE(switches)

	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(0x0e) PORT_CODE(KEYCODE_N)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(0x18) PORT_CODE(KEYCODE_X)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN) // some non-printing character
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_M)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR(0x03) PORT_CODE(KEYCODE_C)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',') PORT_CHAR('<') PORT_CODE(KEYCODE_COMMA) PORT_CODE(KEYCODE_COMMA_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR(0x16) PORT_CODE(KEYCODE_V)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('.') PORT_CHAR('>') PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR(0x02) PORT_CODE(KEYCODE_B)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Self Test Mode")
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR('$') PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x09) PORT_CODE(KEYCODE_ESC) PORT_CODE(KEYCODE_TAB_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN) // some non-printing character
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR('&') PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR('\"') PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR('\'') PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR(0x12) PORT_CODE(KEYCODE_R)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x1b) PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN) // some non-printing character
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(0x14) PORT_CODE(KEYCODE_T)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('q') PORT_CHAR('Q') PORT_CHAR(0x11) PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR(0x19) PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR(0x17) PORT_CODE(KEYCODE_W)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY11")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(0x15) PORT_CODE(KEYCODE_U)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(0x05) PORT_CODE(KEYCODE_E)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY12")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR(0x07) PORT_CODE(KEYCODE_G)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR(0x01) PORT_CODE(KEYCODE_A)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN) // control character 0xc2
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY13")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('h') PORT_CHAR('H') PORT_CHAR(0x08) PORT_CODE(KEYCODE_H)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR(0x13) PORT_CODE(KEYCODE_S)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY14")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('j') PORT_CHAR('J') PORT_CODE(KEYCODE_J)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR(0x04) PORT_CODE(KEYCODE_D)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY15")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR(0x0b) PORT_CODE(KEYCODE_K)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR(0x06) PORT_CODE(KEYCODE_F)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY16")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CHAR(0x0d) PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('i') PORT_CHAR('I') PORT_CODE(KEYCODE_I)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN) // control character 0xc9
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY17")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN) // control character 0xd0 (PRINT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(0x0f) PORT_CODE(KEYCODE_O)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN) // control character 0xd1
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY18")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Rub Out") PORT_CHAR(UCHAR_MAMEKEY(DEL)) PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR(0x10) PORT_CODE(KEYCODE_P)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY19")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Line Feed") PORT_CHAR(0x0a) PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY20")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN) // control character 0x1f (PAGE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR(0x0c) PORT_CODE(KEYCODE_L)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN) // control character 0xb5
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY21")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHAR('+') PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY22")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('/') PORT_CHAR('?') PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('@') PORT_CHAR('`') PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY23")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(' ') PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\\') PORT_CHAR('|') PORT_CHAR(0x1c) PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY24")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(']') PORT_CHAR('}') PORT_CHAR(0x1d) PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(':') PORT_CHAR('*') PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN) // control character 0xb4
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY25")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('8') PORT_CHAR('(') PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('-') PORT_CHAR('=') PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY26")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('9') PORT_CHAR(')') PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0') PORT_CHAR('_') PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY27")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('^') PORT_CHAR('~') PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('[') PORT_CHAR('{') PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY28")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(0x1a) PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN) // control character 0xd9
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY29")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN) // control character 0xbb (CLEAR)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY30")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN) // control character (CLR TAB)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY31")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN) // control character (PROT MODE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( tv912c )
	PORT_INCLUDE(switches)

	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(0x0e) PORT_CODE(KEYCODE_N)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(0x18) PORT_CODE(KEYCODE_X)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_M)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR(0x03) PORT_CODE(KEYCODE_C)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',') PORT_CHAR('<') PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR(0x16) PORT_CODE(KEYCODE_V)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('.') PORT_CHAR('>') PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR(0x02) PORT_CODE(KEYCODE_B)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Self Test Mode")
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR('$') PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x1b) PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR('^') PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR('@') PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR('&') PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR(0x12) PORT_CODE(KEYCODE_R)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x09) PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(0x14) PORT_CODE(KEYCODE_T)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('q') PORT_CHAR('Q') PORT_CHAR(0x11) PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR(0x19) PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR(0x17) PORT_CODE(KEYCODE_W)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY11")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(0x15) PORT_CODE(KEYCODE_U)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(0x05) PORT_CODE(KEYCODE_E)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY12")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR(0x07) PORT_CODE(KEYCODE_G)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR(0x01) PORT_CODE(KEYCODE_A)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY13")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('h') PORT_CHAR('H') PORT_CODE(KEYCODE_H)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR(0x13) PORT_CODE(KEYCODE_S)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY14")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('j') PORT_CHAR('J') PORT_CODE(KEYCODE_J)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR(0x04) PORT_CODE(KEYCODE_D)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY15")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR(0x0b) PORT_CODE(KEYCODE_K)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR(0x06) PORT_CODE(KEYCODE_F)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY16")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Line Feed") PORT_CHAR(0x0a) PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('i') PORT_CHAR('I') PORT_CODE(KEYCODE_I)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY17")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(0x0f) PORT_CODE(KEYCODE_O)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY18")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\'') PORT_CHAR('\"') PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR(0x10) PORT_CODE(KEYCODE_P)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY19")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('[') PORT_CHAR(']') PORT_CHAR(0x1d) PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY20")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN) // (BLOCK CONV)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR(0x0c) PORT_CODE(KEYCODE_L)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY21")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHAR(':') PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY22")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('/') PORT_CHAR('?') PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY23")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(' ') PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CHAR(0x0d) PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY24")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\\') PORT_CHAR('|') PORT_CHAR(0x1c) PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('-') PORT_CHAR('_') PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_TAB_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY25")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('8') PORT_CHAR('*') PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('=') PORT_CHAR('+') PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY26")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('9') PORT_CHAR('(') PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0') PORT_CHAR(')') PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY27")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Back Space") PORT_CHAR(0x08) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('`') PORT_CHAR('~') PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY28")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(0x1a) PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY29")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN) // control character 0xbb (CLEAR)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY30")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN) // (DEL)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY31")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('{') PORT_CHAR('}') PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0xdc, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

void tv912_state::tv912(machine_config &config)
{
	i8035_device &maincpu(I8035(config, m_maincpu, 23.814_MHz_XTAL / 4)); // nominally +6MHz, actually 5.9535 MHz
	maincpu.set_addrmap(AS_PROGRAM, &tv912_state::prog_map);
	maincpu.set_addrmap(AS_IO, &tv912_state::io_map);
	maincpu.p1_out_cb().set(FUNC(tv912_state::p1_w));
	maincpu.p2_in_cb().set(FUNC(tv912_state::p2_r));
	maincpu.p2_out_cb().set(FUNC(tv912_state::p2_w));
	maincpu.t0_in_cb().set(m_rs232, FUNC(rs232_port_device::cts_r));
	maincpu.t1_in_cb().set(m_crtc, FUNC(tms9927_device::bl_r)).invert();
	maincpu.prog_out_cb().set(m_uart, FUNC(ay51013_device::write_xr)).invert();

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(23.814_MHz_XTAL, 105 * TV912_CH_WIDTH, 0, 80 * TV912_CH_WIDTH, 270, 0, 240);
	screen.set_screen_update(FUNC(tv912_state::screen_update));

	TMS9927(config, m_crtc, 23.814_MHz_XTAL / TV912_CH_WIDTH);
	m_crtc->set_char_width(TV912_CH_WIDTH);
	m_crtc->vsyn_callback().set_inputline(m_maincpu, MCS48_INPUT_IRQ);
	m_crtc->set_screen("screen");

	AY51013(config, m_uart);
	m_uart->read_si_callback().set(m_rs232, FUNC(rs232_port_device::rxd_r));
	m_uart->write_so_callback().set(m_txd_merger, FUNC(input_merger_device::in_w<0>));
	m_uart->set_auto_rdav(true);

	INPUT_MERGER_ALL_HIGH(config, m_txd_merger).output_handler().set(m_rs232, FUNC(rs232_port_device::write_txd));

	RS232_PORT(config, m_rs232, default_rs232_devices, "loopback");

	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 23.814_MHz_XTAL / 7 / 11 / 256); // nominally 1200 Hz
	m_beep->add_route(ALL_OUTPUTS, "mono", 0.50);
}

/**************************************************************************************************************

Televideo TVI-912C.
Chips: i8035, TMS9927NL, AY5-1013A (COM2502)
Crystals: 23.814 (divide by 4 for CPU clock)
Other: 1x 8-sw DIP, 1x 10-sw DIP (internal), 2x 10-sw DIP (available to user at the back)

***************************************************************************************************************/

ROM_START( tv912c )
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD( "a49c1.bin",    0x0000, 0x1000, CRC(40068371) SHA1(44c32f8c3980acebe28fa48f98479910af2eb4ae) ) // MM52132

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD( "a3-2.bin",     0x0000, 0x0800, CRC(bb9a7fbd) SHA1(5f1c4d41b25bd3ca4dbc336873362935daf283da) ) // EA8316
ROM_END

ROM_START( tv912b )
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD( "televideo912b_rom_a49.bin", 0x0000, 0x1000, CRC(2c95e995) SHA1(77cda383d68b0bbbb783026d8fde679f10f9eded) ) // MM52132 (TVI A49B1)

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD( "televideo912b_rom_a3.bin", 0x0000, 0x0800, CRC(bb9a7fbd) SHA1(5f1c4d41b25bd3ca4dbc336873362935daf283da) ) // AMI 8110QV (A3-2)
ROM_END

} // anonymous namespace

COMP( 1978, tv912c, 0,      0, tv912, tv912c, tv912_state, empty_init, "TeleVideo Systems", "TVI-912C", MACHINE_NOT_WORKING )
COMP( 1978, tv912b, tv912c, 0, tv912, tv912b, tv912_state, empty_init, "TeleVideo Systems", "TVI-912B", MACHINE_NOT_WORKING )



tv924.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for TeleVideo Model 924 terminal.

****************************************************************************/

#include "emu.h"

//#include "bus/rs232/rs232.h"
#include "cpu/m6502/m6502.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/mc68681.h"
//#include "machine/nvram.h"
#include "video/scn2674.h"
#include "screen.h"


namespace {

class tv924_state : public driver_device
{
public:
	tv924_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pvtc(*this, "pvtc")
	{
	}

	void tv924(machine_config &config);

private:
	SCN2672_DRAW_CHARACTER_MEMBER(draw_character);

	void mem_map(address_map &map) ATTR_COLD;
	void char_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<scn2672_device> m_pvtc;
};

SCN2672_DRAW_CHARACTER_MEMBER(tv924_state::draw_character)
{
}

void tv924_state::mem_map(address_map &map)
{
	map(0x0000, 0x07ff).ram();
	map(0x2020, 0x202f).rw("duart", FUNC(scn2681_device::read), FUNC(scn2681_device::write));
	map(0x2030, 0x2037).rw(m_pvtc, FUNC(scn2672_device::read), FUNC(scn2672_device::write));
	map(0x8000, 0x9fff).ram();
	map(0xa000, 0xbfff).rom().region("program", 0x0000);
	map(0xe000, 0xffff).rom().region("program", 0x2000);
}

void tv924_state::char_map(address_map &map)
{
	map(0x0000, 0x3fff).ram();
}


static INPUT_PORTS_START(tv924)
INPUT_PORTS_END

void tv924_state::tv924(machine_config &config)
{
	M6502(config, m_maincpu, 1'723'560); // R6502AP (clock guessed)
	m_maincpu->set_addrmap(AS_PROGRAM, &tv924_state::mem_map);

	I8049(config, "kbdc", 5.7143_MHz_XTAL).set_disable();

	scn2681_device &duart(SCN2681(config, "duart", 3.6864_MHz_XTAL)); // SCN2681A
	duart.irq_cb().set_inputline(m_maincpu, m6502_device::IRQ_LINE);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(27'576'960, 848 * 2, 0, 640 * 2, 271, 0, 250);
	screen.set_screen_update("pvtc", FUNC(scn2672_device::screen_update));

	SCN2672(config, m_pvtc, 1'723'560); // SCN2672A (with SCB2673B + AMI gate arrays 130170-00 and 130180-00)
	m_pvtc->set_screen("screen");
	m_pvtc->set_character_width(16); // nominally 8, but with half-dot shifting
	m_pvtc->set_addrmap(0, &tv924_state::char_map);
	m_pvtc->set_display_callback(FUNC(tv924_state::draw_character));
	m_pvtc->intr_callback().set_inputline(m_maincpu, m6502_device::NMI_LINE);
}

ROM_START(tv924)
	ROM_REGION(0x4000, "program", 0)
	ROM_LOAD("180001-53e_924_u14_12_18_84_e.bin", 0x0000, 0x2000, CRC(9129e555) SHA1(2977f5a8153b474d1fff4bec17952562277f86a4))
	ROM_LOAD("180001-54e_924_u21_12_18_84_e.bin", 0x2000, 0x2000, CRC(fa2cf28d) SHA1(9cb461b2ba2a7bf44467d5fbc5358c9caaa60024))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("350_rn_118_2333-5006_8000142.u10", 0x0000, 0x1000, NO_DUMP) // VTI 24-pin mask ROM

	ROM_REGION(0x800, "kbdc", 0)
	ROM_LOAD("d8049hc.bin", 0x000, 0x800, NO_DUMP)
ROM_END

} // anonymous namespace


COMP(1984, tv924, 0, 0, tv924, tv924, tv924_state, empty_init, "TeleVideo Systems", "TeleVideo 924 Video Display Terminal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



tv950.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    2013-09-10 Skeleton driver for Televideo TV950
    2016-07-30 Preliminary not-so-skeleton driver

    TODO:
    - VIA T2 counter mode emulation
    - CRTC reset and drawing
    - Bidirectional communications mode

    Hardware:
    6502A CPU
    6545 CRTC
    6522A VIA, wired to count HSYNCs and to enable the 6502 to pull RESET on the CRTC
    3x 6551 ACIA  1 for the keyboard, 1 for the modem port, 1 for the printer port

    VIA hookup (see schematics):
    PA3 = beep?
    PA5 = inverse video
    PA6 = IRQ in
    PA7 = force blank
    PB6 = Hblank in
    PB7 = out speaker
    CA1 = reset CRTC in
    CA2 = reset CRTC out
    CB2 = blink timer

    IRQ = ACIAs (all 3 ORed together)
    NMI = 6522 VIA's IRQ line

    http://www.bitsavers.org/pdf/televideo/950/Model_950_Terminal_Theory_of_Operation_26Jan1981.pdf
    http://www.bitsavers.org/pdf/televideo/950/2002100_Model_950_Maintenance_Manual_Nov1983.pdf
    http://www.bitsavers.org/pdf/televideo/950/B300002-001_Model_950_Operators_Manual_Feb81.pdf

****************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/m6502/m6502.h"
#include "machine/input_merger.h"
#include "machine/6522via.h"
#include "machine/mos6551.h"
#include "tv950kb.h"
#include "video/mc6845.h"
#include "screen.h"


namespace {

#define MASTER_CLOCK XTAL(23'814'000)

class tv950_state : public driver_device
{
public:
	tv950_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_via(*this, "via")
		, m_crtc(*this, "crtc")
		, m_uart(*this, "a%uuart", 49U)
		, m_keyboard(*this, "keyboard")
		, m_vram(*this, "vram")
		, m_gfx(*this, "graphics")
		, m_dsw(*this, "DSW%u", 0U)
	{ }

	void tv950(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void via_a_w(uint8_t data);
	void via_b_w(uint8_t data);
	uint8_t via_b_r();
	void crtc_vs_w(int state);
	MC6845_UPDATE_ROW(crtc_update_row);
	MC6845_ON_UPDATE_ADDR_CHANGED(crtc_update_addr);

	void row_addr_w(uint8_t data);
	void via_crtc_reset_w(int state);

	void tv950_mem(address_map &map) ATTR_COLD;

	uint8_t m_via_row = 0;
	uint8_t m_attr_row = 0;
	uint8_t m_attr_screen = 0;

	required_device<m6502_device> m_maincpu;
	required_device<via6522_device> m_via;
	required_device<r6545_1_device> m_crtc;
	required_device_array<mos6551_device, 3> m_uart;
	required_device<tv950kb_device> m_keyboard;
	required_shared_ptr<uint8_t> m_vram;
	required_region_ptr<uint16_t> m_gfx;
	required_ioport_array<4> m_dsw;

	int m_row_addr = 0;
	int m_row = 0;
};

void tv950_state::machine_start()
{
	m_uart[0]->write_dcd(0);
	m_uart[0]->write_dsr(0);
	m_uart[2]->write_cts(0);
}

void tv950_state::tv950_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).ram();
	map(0x2000, 0x3fff).ram().share("vram"); // VRAM
	map(0x8100, 0x8100).rw(m_crtc, FUNC(r6545_1_device::status_r), FUNC(r6545_1_device::address_w));
	map(0x8101, 0x8101).rw(m_crtc, FUNC(r6545_1_device::register_r), FUNC(r6545_1_device::register_w));
	map(0x9000, 0x9000).w(FUNC(tv950_state::row_addr_w));
	map(0x9300, 0x9303).rw(m_uart[0], FUNC(mos6551_device::read), FUNC(mos6551_device::write)); // CS0 = AB9
	map(0x9500, 0x9503).rw(m_uart[2], FUNC(mos6551_device::read), FUNC(mos6551_device::write)); // CS0 = AB10
	map(0x9900, 0x9903).rw(m_uart[1], FUNC(mos6551_device::read), FUNC(mos6551_device::write)); // CS0 = AB11
	map(0xb100, 0xb10f).m(m_via, FUNC(via6522_device::map));
	map(0xe000, 0xffff).rom().region("maincpu", 0);
}


/* Input ports */
static INPUT_PORTS_START( tv950 )
	PORT_START("DSW0")
	PORT_DIPNAME( 0x01, 0x00, "S01")
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPSETTING(    0x01, DEF_STR( Off ))
	PORT_DIPNAME( 0x02, 0x00, "S02")
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPSETTING(    0x02, DEF_STR( Off ))
	PORT_DIPNAME( 0x04, 0x00, "S03")
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPSETTING(    0x04, DEF_STR( Off ))
	PORT_DIPNAME( 0x08, 0x00, "S04")
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPSETTING(    0x08, DEF_STR( Off ))
	PORT_DIPNAME( 0x10, 0x00, "S05")
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPSETTING(    0x10, DEF_STR( Off ))
	PORT_DIPNAME( 0x20, 0x20, "S06")
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPSETTING(    0x20, DEF_STR( Off ))
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("DSW1")
	PORT_DIPNAME( 0x01, 0x00, "S07")
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPSETTING(    0x01, DEF_STR( Off ))
	PORT_DIPNAME( 0x02, 0x00, "S08")
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPSETTING(    0x02, DEF_STR( Off ))
	PORT_DIPNAME( 0x04, 0x00, "S09")
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPSETTING(    0x04, DEF_STR( Off ))
	PORT_DIPNAME( 0x08, 0x00, "S10")
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPSETTING(    0x08, DEF_STR( Off ))
	PORT_DIPNAME( 0x10, 0x00, "S11")
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPSETTING(    0x10, DEF_STR( Off ))
	PORT_DIPNAME( 0x20, 0x20, "S12")
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPSETTING(    0x20, DEF_STR( Off ))
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("DSW2")
	PORT_DIPNAME( 0x01, 0x00, "S13")
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPSETTING(    0x01, DEF_STR( Off ))
	PORT_DIPNAME( 0x02, 0x00, "S14")
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPSETTING(    0x02, DEF_STR( Off ))
	PORT_DIPNAME( 0x04, 0x00, "S15")
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPSETTING(    0x04, DEF_STR( Off ))
	PORT_DIPNAME( 0x08, 0x00, "S16")
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPSETTING(    0x08, DEF_STR( Off ))
	PORT_DIPNAME( 0x10, 0x00, "S17")
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPSETTING(    0x10, DEF_STR( Off ))
	PORT_DIPNAME( 0x20, 0x20, "S18")
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPSETTING(    0x20, DEF_STR( Off ))
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("DSW3")
	PORT_DIPNAME( 0x01, 0x00, "S19")
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPSETTING(    0x01, DEF_STR( Off ))
	PORT_DIPNAME( 0x02, 0x00, "S20")
	PORT_DIPSETTING(    0x00, DEF_STR( On ))
	PORT_DIPSETTING(    0x02, DEF_STR( Off ))
	PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END


void tv950_state::machine_reset()
{
	m_row = 0;
	m_via_row = 0;
	m_attr_row = 0;
	m_attr_screen = 0;
}

void tv950_state::crtc_vs_w(int state)
{
	m_attr_screen = 0;
}

void tv950_state::via_crtc_reset_w(int state)
{
	//printf("via_crtc_reset_w: %d\n", state);
	m_via->write_ca1(state);

	if (!state)
	{
		//m_crtc->device_reset();
	}
}

void tv950_state::row_addr_w(uint8_t data)
{
	m_row_addr = data;
}

void tv950_state::via_a_w(uint8_t data)
{
	m_via_row = ~data & 15;
	// PA4, 5, 7 to do
}

void tv950_state::via_b_w(uint8_t data)
{
	// bit 3 of m_via_row must be active as well?
	m_keyboard->rx_w(!BIT(data, 7));
}

uint8_t tv950_state::via_b_r()
{
	uint8_t data = 0xff;
	for (int n = 0; n < 4; n++)
		if (BIT(m_via_row, n))
			data &= m_dsw[n]->read();
	return data;
}

MC6845_ON_UPDATE_ADDR_CHANGED( tv950_state::crtc_update_addr )
{
}

MC6845_UPDATE_ROW( tv950_state::crtc_update_row )
{
	if (ra)
		m_attr_row = m_attr_screen;
	else
		m_attr_screen = m_attr_row;

	uint32_t *p = &bitmap.pix(m_row);
	rgb_t fg(255,255,255,255);
	rgb_t bg(0,0,0,0);

	for(uint8_t x = 0; x < x_count; x++)
	{
		uint8_t chr = m_vram[ma + x];
		if ((chr & 0x90)==0x90)
			m_attr_row = chr & 15;
		uint16_t data = m_gfx[chr * 16 + (m_row % 10)];
		if (x == cursor_x)
			data ^= 0xff;
		// apply attributes...

		for (uint8_t i = 0; i < 14; i++)
			*p++ = BIT( data, 13-i ) ? fg : bg;
	}
	m_row = (m_row + 1) % 250;
}

void tv950_state::tv950(machine_config &config)
{
	/* basic machine hardware */
	M6502(config, m_maincpu, MASTER_CLOCK/14);
	m_maincpu->set_addrmap(AS_PROGRAM, &tv950_state::tv950_mem);

	input_merger_device &mainirq(INPUT_MERGER_ANY_HIGH(config, "mainirq")); // open collector
	mainirq.output_handler().set_inputline(m_maincpu, m6502_device::IRQ_LINE);
	mainirq.output_handler().append(m_via, FUNC(via6522_device::write_pa6)).invert();

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(MASTER_CLOCK, 1200, 0, 1120, 370, 0, 250);   // not real values
	screen.set_screen_update("crtc", FUNC(r6545_1_device::screen_update));

	// there are many 6845 CRTC submodels, the Theory of Operation manual references the Rockwell R6545-1 specificially.
	R6545_1(config, m_crtc, MASTER_CLOCK/14);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(14);
	m_crtc->set_update_row_callback(FUNC(tv950_state::crtc_update_row));
	m_crtc->set_on_update_addr_change_callback(FUNC(tv950_state::crtc_update_addr));
	m_crtc->out_hsync_callback().set(m_via, FUNC(via6522_device::write_pb6)).invert();
	m_crtc->out_vsync_callback().set(FUNC(tv950_state::crtc_vs_w));
	m_crtc->set_screen(nullptr);

	MOS6522(config, m_via, MASTER_CLOCK/14);
	//m_via->irq_handler().set_inputline(m_maincpu, M6502_NMI_LINE);
	m_via->writepa_handler().set(FUNC(tv950_state::via_a_w));
	m_via->writepb_handler().set(FUNC(tv950_state::via_b_w));
	m_via->readpb_handler().set(FUNC(tv950_state::via_b_r));
	m_via->ca2_handler().set(FUNC(tv950_state::via_crtc_reset_w));
	//m_via->cb2_handler().set(FUNC(tv950_state::via_blink_rate_w));

	MOS6551(config, m_uart[0], MASTER_CLOCK/14).set_xtal(MASTER_CLOCK/13); // for keyboard
	m_uart[0]->irq_handler().set("mainirq", FUNC(input_merger_device::in_w<0>));

	MOS6551(config, m_uart[1], MASTER_CLOCK/14).set_xtal(MASTER_CLOCK/13); // for main port
	m_uart[1]->irq_handler().set("mainirq", FUNC(input_merger_device::in_w<1>));
	m_uart[1]->dtr_handler().set("p3", FUNC(rs232_port_device::write_dtr));
	m_uart[1]->rts_handler().set("p3", FUNC(rs232_port_device::write_rts));
	m_uart[1]->txd_handler().set("p3", FUNC(rs232_port_device::write_txd));

	MOS6551(config, m_uart[2], MASTER_CLOCK/14).set_xtal(MASTER_CLOCK/13); // for printer port
	m_uart[2]->irq_handler().set("mainirq", FUNC(input_merger_device::in_w<2>));
	m_uart[2]->txd_handler().set("p4", FUNC(rs232_port_device::write_txd)); // to pin 3 (RXD)
	m_uart[2]->rts_handler().set("p4", FUNC(rs232_port_device::write_rts)); // to pin 5 (CTS)
	m_uart[2]->dtr_handler().set("p4", FUNC(rs232_port_device::write_dtr)); // to pin 6 (DSR)

	TV950_KEYBOARD(config, m_keyboard);
	m_keyboard->tx_cb().set(m_uart[0], FUNC(mos6551_device::write_rxd));

	rs232_port_device &p3(RS232_PORT(config, "p3", default_rs232_devices, nullptr)); // main port
	p3.dsr_handler().set(m_uart[1], FUNC(mos6551_device::write_dsr));
	p3.dcd_handler().set(m_uart[1], FUNC(mos6551_device::write_dcd));
	p3.rxd_handler().set(m_uart[1], FUNC(mos6551_device::write_rxd));
	p3.cts_handler().set(m_uart[1], FUNC(mos6551_device::write_cts));

	rs232_port_device &p4(RS232_PORT(config, "p4", default_rs232_devices, nullptr)); // printer port
	p4.dsr_handler().set(m_uart[2], FUNC(mos6551_device::write_dsr)); // from pin 20 (DTR)
	p4.rxd_handler().set(m_uart[2], FUNC(mos6551_device::write_rxd)); // from pin 2 (TXD)
}

/* ROM definition */
ROM_START( tv950 )
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD( "180000-001a_a41_eb17.bin", 0x001000, 0x001000, CRC(b7187cc5) SHA1(41cc8fd51661314e03ee7e00cc1e206e9a694d92) )
	ROM_LOAD( "180000-007a_a42_67d3.bin", 0x000000, 0x001000, CRC(3ef2e6fb) SHA1(21ccfd2b50c37b715eed67671b82faa4d75fc6bb) )

	ROM_REGION16_LE(0x2000, "graphics", 0)
	ROM_LOAD16_BYTE( "180000-002a_a33_9294.bin", 0x000001, 0x001000, CRC(eaf4f346) SHA1(b4c531626846f3f055ddc086ac24fdb1b34f3f8e) )
	ROM_LOAD16_BYTE( "180000-003a_a32_7ebf.bin", 0x000000, 0x001000, CRC(783ca0b6) SHA1(1cec9a9a56ef5795809f7ca7cd2e3f61b27e698d) )

	ROM_REGION(0x10000, "user1", 0)
	// came with "tv950.zip"
	ROM_LOAD( "180000-43i.a25", 0x0000, 0x1000, CRC(ac6f0bfc) SHA1(2a3863700405fbb9e510613559d78fceee3544e8) )
	ROM_LOAD( "180000-44i.a20", 0x0000, 0x1000, CRC(db91a727) SHA1(e94724ed1a563fb846f4203ae6523ee6b4c6577f) )
	// came with "tv950kbd.zip"
	ROM_LOAD( "1800000-003a.a32", 0x0000, 0x1000, CRC(eaef0138) SHA1(7198851299fce07c95d18e32cbfbe936c0dbec2a) )
	ROM_LOAD( "1800000-002a.a33", 0x0000, 0x1000, CRC(856dd85c) SHA1(e2570017e098b0e1ead7749e9c2ac40be2367433) )
ROM_END

} // anonymous namespace


/* Driver */
//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY      FULLNAME                            FLAGS
COMP( 1981, tv950, 0,      0,      tv950,   tv950, tv950_state, empty_init, "TeleVideo", "Model 950 Video Display Terminal", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )



tv955.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

Skeleton driver for "third generation" TeleVideo terminals (905, 955, 9220).

************************************************************************************************************************************/

#include "emu.h"
#include "tv955kb.h"
#include "bus/rs232/rs232.h"
#include "cpu/m6502/w65c02.h"
#include "machine/input_merger.h"
#include "machine/mos6551.h"
#include "machine/nvram.h"
#include "video/scn2674.h"
#include "screen.h"


namespace {

class tv955_state : public driver_device
{
public:
	tv955_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_crtc(*this, "crtc")
		, m_hostuart(*this, "hostuart")
		, m_printuart(*this, "printuart")
		, m_keybuart(*this, "keybuart")
		, m_mainport(*this, "mainport")
		, m_printer(*this, "printer")
		, m_chargen(*this, "chargen")
	{ }

	void tv955(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	SCN2674_DRAW_CHARACTER_MEMBER(draw_character);

	void control_latch_w(u8 data);
	void system_reset_w(int state);

	void mem_map(address_map &map) ATTR_COLD;
	void char_map(address_map &map) ATTR_COLD;
	void attr_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<scn2674_device> m_crtc;
	required_device<mos6551_device> m_hostuart;
	required_device<mos6551_device> m_printuart;
	required_device<mos6551_device> m_keybuart;
	required_device<rs232_port_device> m_mainport;
	required_device<rs232_port_device> m_printer;
	required_region_ptr<u8> m_chargen;
};

void tv955_state::machine_reset()
{
	m_printer->write_rts(0);
	m_printer->write_dtr(0);

	m_printuart->write_cts(0);
	m_keybuart->write_cts(0);
}

SCN2674_DRAW_CHARACTER_MEMBER(tv955_state::draw_character)
{
	u16 dots = m_chargen[charcode << 4 | linecount] << 1;
	if (BIT(dots, 1) && BIT(charcode, 7))
		dots |= 1;

	// TODO: attribute logic
	if (cursor)
		dots = ~dots;

	for (int i = 0; i < 9; i++)
	{
		bitmap.pix(y, x++) = BIT(dots, 8) ? rgb_t::white() : rgb_t::black();
		dots <<= 1;
	}
}

void tv955_state::control_latch_w(u8 data)
{
	m_mainport->write_dtr(BIT(data, 0));
	m_hostuart->set_xtal(3.6864_MHz_XTAL / (BIT(data, 1) ? 1 : 2));

	// CPU clock is inverted relative to character clock (and divided by two for 132-column mode)
	if (BIT(data, 7))
	{
		// 132-column mode
		m_maincpu->set_unscaled_clock(31.684_MHz_XTAL / 18);
		m_crtc->set_unscaled_clock(31.684_MHz_XTAL / 9);
	}
	else
	{
		// 80-column mode
		m_maincpu->set_unscaled_clock(19.3396_MHz_XTAL / 9);
		m_crtc->set_unscaled_clock(19.3396_MHz_XTAL / 9);
	}
}

void tv955_state::system_reset_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? CLEAR_LINE : ASSERT_LINE);
	if (!state)
	{
		m_keybuart->reset();
		m_printuart->reset();
		m_hostuart->reset();
	}
}

void tv955_state::mem_map(address_map &map)
{
	// verified from maintenance manual (131968-00-C)
	map(0x0000, 0x07ff).mirror(0x0800).ram().share("nvram");
	map(0x1100, 0x1100).mirror(0x00ff).w(FUNC(tv955_state::control_latch_w));
	map(0x1200, 0x1203).mirror(0x00fc).rw(m_keybuart, FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x1400, 0x1403).mirror(0x00fc).rw(m_printuart, FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x1800, 0x1803).mirror(0x00fc).rw(m_hostuart, FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x2000, 0x2007).mirror(0x0ff8).rw("crtc", FUNC(scn2674_device::read), FUNC(scn2674_device::write));
	map(0x3000, 0x3fff).rom().region("option", 0);
	map(0x4000, 0x7fff).ram().share("attrram");
	map(0x8000, 0xbfff).ram().share("charram");
	map(0xc000, 0xffff).rom().region("system", 0);
}

void tv955_state::char_map(address_map &map)
{
	map(0x0000, 0x3fff).ram().share("charram");
}

void tv955_state::attr_map(address_map &map)
{
	map(0x0000, 0x3fff).ram().share("attrram");
}

static INPUT_PORTS_START( tv955 )
INPUT_PORTS_END

void tv955_state::tv955(machine_config &config)
{
	W65C02(config, m_maincpu, 19.3396_MHz_XTAL / 9);
	m_maincpu->set_addrmap(AS_PROGRAM, &tv955_state::mem_map);

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, m6502_device::IRQ_LINE);

	tv955kb_device &keyboard(TV955_KEYBOARD(config, "keyboard"));
	keyboard.txd_cb().set("keybuart", FUNC(mos6551_device::write_rxd));
	keyboard.reset_cb().set(FUNC(tv955_state::system_reset_w));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // HM6116LP-4 + 3.2V battery

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_raw(19.3396_MHz_XTAL, 846, 0, 720, 381, 0, 364);
	//screen.set_raw(31.684_MHz_XTAL, 1386, 0, 1188, 381, 0, 364);
	screen.set_screen_update("crtc", FUNC(scn2674_device::screen_update));

	SCN2674(config, m_crtc, 19.3396_MHz_XTAL / 9);
	// Character clock is 31.684_MHz_XTAL / 9 in 132-column mode
	// Character cells are 9 pixels wide by 14 pixels high
	m_crtc->set_character_width(9);
	m_crtc->set_addrmap(0, &tv955_state::char_map);
	m_crtc->set_addrmap(1, &tv955_state::attr_map);
	m_crtc->set_display_callback(FUNC(tv955_state::draw_character));
	m_crtc->intr_callback().set_inputline(m_maincpu, m6502_device::NMI_LINE);
	m_crtc->set_screen("screen");

	MOS6551(config, m_hostuart, 0);
	m_hostuart->set_xtal(3.6864_MHz_XTAL);
	m_hostuart->irq_handler().set("mainirq", FUNC(input_merger_device::in_w<0>));
	m_hostuart->txd_handler().set(m_mainport, FUNC(rs232_port_device::write_txd));
	m_hostuart->rts_handler().set(m_mainport, FUNC(rs232_port_device::write_rts));
	m_hostuart->dtr_handler().set(m_mainport, FUNC(rs232_port_device::write_dtr));

	MOS6551(config, m_printuart, 0);
	m_printuart->set_xtal(3.6864_MHz_XTAL / 2);
	m_printuart->irq_handler().set("mainirq", FUNC(input_merger_device::in_w<1>));
	m_printuart->txd_handler().set(m_printer, FUNC(rs232_port_device::write_txd));

	MOS6551(config, m_keybuart, 0);
	m_keybuart->set_xtal(3.6864_MHz_XTAL / 2);
	m_keybuart->irq_handler().set("mainirq", FUNC(input_merger_device::in_w<2>));
	m_keybuart->txd_handler().set("keyboard", FUNC(tv955kb_device::write_rxd));

	RS232_PORT(config, m_mainport, default_rs232_devices, "loopback"); // DTE
	m_mainport->rxd_handler().set(m_hostuart, FUNC(mos6551_device::write_rxd));
	m_mainport->cts_handler().set(m_hostuart, FUNC(mos6551_device::write_cts));
	m_mainport->dsr_handler().set(m_hostuart, FUNC(mos6551_device::write_dsr));
	m_mainport->dcd_handler().set(m_hostuart, FUNC(mos6551_device::write_dcd));

	RS232_PORT(config, m_printer, default_rs232_devices, nullptr); // DCE
	m_printer->rxd_handler().set("printuart", FUNC(mos6551_device::write_rxd)); // pin 2
	m_printer->dsr_handler().set("printuart", FUNC(mos6551_device::write_dsr)); // pin 20 or pin 11
}

/**************************************************************************************************************

Televideo TVI-955 (132160-00 Rev. M)
Chips: G65SC02P-3, 3x S6551AP, SCN2674B, AMI 131406-00 (unknown 40-pin DIL), 2x TMM2064P-10 (near two similar empty sockets), HM6116LP-4, round silver battery
Crystals: 19.3396, 31.684, 3.6864
Keyboard: M5L8049-230P-6, 5.7143, Beeper

***************************************************************************************************************/

ROM_START( tv955 )
	ROM_REGION(0x4000, "system", 0)
	ROM_LOAD( "t180002-88d_955.u4",  0x0000, 0x4000, CRC(5767fbe7) SHA1(49a2241612af5c3af09778ffa541ac0bc186e05a) )

	ROM_REGION(0x1000, "option", 0)
	ROM_LOAD( "t180002-91a_calc.u5", 0x0000, 0x1000, CRC(f86c103a) SHA1(fa3ada3a5d8913e519e2ea4817e96166c1fedd32) )
	ROM_CONTINUE( 0x0000, 0x1000 ) // first half is all FF (and not addressable)

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "t180002-26b.u45",     0x0000, 0x1000, CRC(69c9ebc7) SHA1(32282c816ec597a7c45e939acb7a4155d35ea584) )
ROM_END

} // anonymous namespace


COMP( 1985, tv955, 0, 0, tv955, tv955, tv955_state, empty_init, "TeleVideo Systems", "TeleVideo 955", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS )



tv965.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************************

Skeleton driver for TeleVideo 965 video display terminal.

TeleVideo 9320 appears to run on similar hardware with a 2681 DUART replacing the ACIAs.

************************************************************************************************************************************/

#include "emu.h"
//#include "bus/rs232/rs232.h"
#include "cpu/g65816/g65816.h"
#include "machine/mos6551.h"
#include "machine/nvram.h"
#include "video/scn2674.h"
#include "screen.h"


namespace {

class tv965_state : public driver_device
{
public:
	tv965_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
	{ }

	void tv965(machine_config &config);
private:
	SCN2672_DRAW_CHARACTER_MEMBER(draw_character);

	u8 ga_hack_r();

	void mem_map(address_map &map) ATTR_COLD;
	void program_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
};

SCN2672_DRAW_CHARACTER_MEMBER(tv965_state::draw_character)
{
}

u8 tv965_state::ga_hack_r()
{
	return 0x08;
}

void tv965_state::mem_map(address_map &map)
{
	map(0x00000, 0x01fff).ram().share("nvram");
	map(0x02000, 0x02007).rw("crtc", FUNC(scn2672_device::read), FUNC(scn2672_device::write));
	map(0x04000, 0x04000).r(FUNC(tv965_state::ga_hack_r));
	map(0x06200, 0x06203).rw("acia1", FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x06400, 0x06403).rw("acia2", FUNC(mos6551_device::read), FUNC(mos6551_device::write));
	map(0x08000, 0x09fff).ram().mirror(0x2000).share("charram");
	map(0x0c000, 0x0dfff).ram().mirror(0x2000).share("attrram");
	map(0x10000, 0x1ffff).rom().region("eprom1", 0);
	map(0x30000, 0x3ffff).rom().region("eprom2", 0);
}

void tv965_state::program_map(address_map &map)
{
	map.global_mask(0x2ffff);
	map(0x00000, 0x0ffff).rom().region("eprom1", 0);
	map(0x20000, 0x2ffff).rom().region("eprom2", 0);
}

static INPUT_PORTS_START( tv965 )
INPUT_PORTS_END

void tv965_state::tv965(machine_config &config)
{
	G65816(config, m_maincpu, 44.4528_MHz_XTAL / 10);
	m_maincpu->set_addrmap(AS_DATA, &tv965_state::mem_map);
	m_maincpu->set_addrmap(AS_PROGRAM, &tv965_state::program_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // CXK5864BP-10L + battery

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(26.9892_MHz_XTAL, 1020, 0, 800, 441, 0, 416);
	//m_screen->set_raw(44.4528_MHz_XTAL, 1680, 0, 1320, 441, 0, 416);
	m_screen->set_screen_update("crtc", FUNC(scn2672_device::screen_update));

	scn2672_device &crtc(SCN2672(config, "crtc", 26.9892_MHz_XTAL / 10));
	crtc.set_character_width(10);
	crtc.set_display_callback(FUNC(tv965_state::draw_character));
	crtc.intr_callback().set_inputline(m_maincpu, G65816_LINE_NMI);
	crtc.set_screen("screen");

	mos6551_device &acia1(MOS6551(config, "acia1", 0));
	acia1.set_xtal(3.6864_MHz_XTAL / 2); // divider not verified, possibly even programmable

	mos6551_device &acia2(MOS6551(config, "acia2", 0));
	acia2.set_xtal(3.6864_MHz_XTAL / 2); // divider not verified, possibly even programmable
}

/**************************************************************************************************************

Televideo TVI-965 (P/N 132970-00)
Chips: G65SC816P-5, SCN2672TC5N40, Silicon Logic 271582-00, 2x UM6551A, Beeper, 2x MK48H64LN-70, HY6264LP-10 (next to gate array), CXK5864BP-10L, DS1231, round battery
Crystals: 44.4528, 26.9892, 3.6864

***************************************************************************************************************/

ROM_START( tv965 )
	ROM_REGION(0x10000, "eprom1", 0)
	ROM_LOAD( "180003-30h.u8", 0x00000, 0x10000, CRC(c7b9ca39) SHA1(1d95a8b0a4ea5caf3fb628c44c7a3567700a0b59) )

	ROM_REGION(0x10000, "eprom2", ROMREGION_ERASE00)
	ROM_LOAD( "180003-38h.u9", 0x00000, 0x08000, CRC(30fae408) SHA1(f05bb2a9ce2df60b046733f746d8d8a1eb3ac8bc) )
ROM_END

} // anonymous namespace


COMP( 1989, tv965, 0, 0, tv965, tv965, tv965_state, empty_init, "TeleVideo Systems", "TeleVideo 965", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



tv990.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont, Carl
/***************************************************************************

  TeleVideo 990/995 terminal

  Driver by Carl and R. Belmont
  Thanks to Al Kossow.

  H/W:
  68000-P16 CPU (clock unknown, above 10 MHz it outruns the AT keyboard controller)
  16C452 dual 16450 (PC/AT standard) UART + PC-compatible Centronics (integrated into
         ASIC on 995)
  AMI MEGA-KBD-H-Q PS/2 keyboard interface on 990, PS/2 8042 on 995
  Televideo ASIC marked "134446-00 TVI1111-0 427"
  3x AS7C256 (32K x 8 SRAM)

  IRQs:
  2 = PS/2 keyboard
  3 = Centronics
  4 = UART 1
  5 = UART 0
  6 = VBL (9003b is status, write 3 to 9003b to reset

  Video modes include 80 or 132 wide by 24, 25, 42, 43, 48, or 49 lines high plus an
                      optional status bar
  Modes include TeleVideo 990, 950, and 955, Wyse WY-60, WY-150/120/50+/50, ANSI,
                      DEC VT320/220, VT100/52, SCO Console, and PC TERM.

****************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
#include "machine/8042kbdc.h"
#include "machine/ins8250.h"
#include "machine/nvram.h"
#include "machine/pc_lpt.h"
#include "machine/pckeybrd.h"
#include "sound/beep.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

#define RS232A_TAG      "rs232a"
#define RS232B_TAG      "rs232b"
#define LPT_TAG         "lpt"

class tv990_state : public driver_device
{
public:
	tv990_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_vram(*this, "vram"),
		m_fontram(*this, "fontram"),
		m_uart(*this, "ns16450_%u", 0U),
		m_screen(*this, "screen"),
		m_kbdc(*this, "pc_kbdc"),
		m_palette(*this, "palette"),
		m_beep(*this, "beep")
	{
	}

	void tv990(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(color);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void device_post_load() override;

	void tv990_mem(address_map &map) ATTR_COLD;

	TIMER_CALLBACK_MEMBER(trigger_row_irq);

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	uint16_t tvi1111_r(offs_t offset);
	void tvi1111_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint8_t kbdc_r(offs_t offset);
	void kbdc_w(offs_t offset, uint8_t data);

	void uart0_irq(int state);
	void uart1_irq(int state);
	void lpt_irq(int state);
	void vblank_irq(int state);

	required_device<m68000_device> m_maincpu;
	required_shared_ptr<uint16_t> m_vram;
	required_shared_ptr<uint16_t> m_fontram;
	required_device_array<ns16450_device, 2> m_uart;
	required_device<screen_device> m_screen;
	required_device<kbdc8042_device> m_kbdc;
	required_device<palette_device> m_palette;
	required_device<beep_device> m_beep;

	uint16_t tvi1111_regs[(0x100/2)+2];
	emu_timer *m_rowtimer = nullptr;
	int m_rowh = 0;
	int m_width = 0;
	int m_height = 0;
};

void tv990_state::vblank_irq(int state)
{
	if (state)
	{
		m_rowtimer->adjust(m_screen->time_until_pos(m_rowh));
		m_maincpu->set_input_line(M68K_IRQ_6, ASSERT_LINE);
		tvi1111_regs[0x1d] |= 4;
	}
}

void tv990_state::machine_start()
{
	m_rowtimer = timer_alloc(FUNC(tv990_state::trigger_row_irq), this);

	save_item(NAME(tvi1111_regs));
	save_item(NAME(m_rowh));
	save_item(NAME(m_width));
	save_item(NAME(m_height));
}

TIMER_CALLBACK_MEMBER(tv990_state::trigger_row_irq)
{
	m_rowtimer->adjust(m_screen->time_until_pos(m_screen->vpos() + m_rowh));
	m_maincpu->set_input_line(M68K_IRQ_6, ASSERT_LINE);
	m_screen->update_now();
}

void tv990_state::uart0_irq(int state)
{
	m_maincpu->set_input_line(M68K_IRQ_5, state);
}

void tv990_state::uart1_irq(int state)
{
	m_maincpu->set_input_line(M68K_IRQ_4, state);
}

void tv990_state::lpt_irq(int state)
{
	m_maincpu->set_input_line(M68K_IRQ_3, state);
}

uint16_t tv990_state::tvi1111_r(offs_t offset)
{
	if (offset == (0x32/2))
	{
		tvi1111_regs[offset] |= 8;  // loop at 109ca wants this set
	}
	else if(offset == 0x1d)
	{
		m_maincpu->set_input_line(M68K_IRQ_6, CLEAR_LINE);
	}

	return tvi1111_regs[offset];
}

void tv990_state::tvi1111_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
#if 0
	//if ((offset != 0x50) && (offset != 0x68) && (offset != 0x1d) && (offset != 0x1e) && (offset != 0x17) && (offset != 0x1c))
	{
		if (mem_mask == 0x00ff)
		{
			printf("%x (%d) to ASIC @ %x (mask %04x)\n", data & 0xff, data & 0xff, offset, mem_mask);
		}
		else if (mem_mask == 0xff00)
		{
			printf("%x (%d) to ASIC @ %x (mask %04x)\n", data & 0xff, data & 0xff, offset, mem_mask);
		}
		else
		{
			printf("%x (%d) to ASIC @ %x (mask %04x)\n", data, data, offset, mem_mask);
		}
	}
#endif
	COMBINE_DATA(&tvi1111_regs[offset]);
	if((offset == 0x1c) || (offset == 0x10) || (offset == 0x9) || (offset == 0xa))
	{
		m_width = BIT(tvi1111_regs[0x1c], 11) ? 132 : 80;
		m_rowh = (tvi1111_regs[0x10] & 0xff) + 1;
		if(!m_rowh)
			m_rowh = 16;
		m_height = (tvi1111_regs[0xa] - tvi1111_regs[0x9]) / m_rowh;
		// m_height can be 0 or -1 while machine is starting, leading to a crash on a debug build, so we sanitise it.
		if(m_height < 8 || m_height > 99)
			m_height = 0x1a;
		m_screen->set_visible_area(0, m_width * 16 - 1, 0, m_height * m_rowh - 1);
	}
	if(offset == 0x17)
		m_beep->set_state(tvi1111_regs[0x17] & 4 ? ASSERT_LINE : CLEAR_LINE);
}

uint32_t tv990_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	uint16_t const *const vram = (uint16_t *)m_vram.target();
	uint8_t const *const fontram = (uint8_t *)m_fontram.target();
	int const miny = cliprect.min_y / m_rowh;
	int const maxy = cliprect.max_y / m_rowh;

	bitmap.fill(0, cliprect);

	for (int y = miny; y <= maxy; y++)
	{
		for(int i = 7; i >= 0; i--)
		{
			if(!BIT(tvi1111_regs[0x1f], i))
				continue;

			int const starty = tvi1111_regs[i + 0x40] >> 8;
			int const endy = tvi1111_regs[i + 0x40] & 0xff;
			if((y < starty) || (y >= endy))
				continue;

			uint16_t const row_offset = tvi1111_regs[i + 0x50];
			uint16_t const *curchar = &vram[row_offset];
			int minx = tvi1111_regs[i + 0x30] >> 8;
			int maxx = tvi1111_regs[i + 0x30] & 0xff;

			if(maxx > m_width)
				maxx = m_width;

			uint16_t const cursor_x = tvi1111_regs[0x16] - row_offset;

			for (int x = minx; x < maxx; x++)
			{
				uint8_t chr = curchar[x - minx] >> 8;
				uint8_t attr = curchar[x - minx] & 0xff;
				if((attr & 2) && (m_screen->frame_number() & 32)) // blink rate?
					continue;

				uint8_t const *fontptr = &fontram[(chr + (attr & 0x40 ? 256 : 0)) * 64];

				if (BIT(tvi1111_regs[0x1b], 0) && x == cursor_x)
				{
					uint8_t attrchg;
					if(tvi1111_regs[0x15] & 0xff00) // what does this really mean? it looks like a mask but that doesn't work in 8line char mode
						attrchg = 8;
					else
						attrchg = 4;
					if(!BIT(tvi1111_regs[0x1b], 1))
						attr ^= attrchg;
					else if(m_screen->frame_number() & 32)
						attr ^= attrchg;
				}

				uint32_t palette[2];
				if (attr & 0x4) // inverse video?
				{
					palette[1] = m_palette->pen(0);
					palette[0] = (attr & 0x10) ? m_palette->pen(1) : m_palette->pen(2);
				}
				else
				{
					palette[0] = m_palette->pen(0);
					palette[1] = (attr & 0x10) ? m_palette->pen(1) : m_palette->pen(2);
				}

				for (int chary = 0; chary < m_rowh; chary++)
				{
					uint32_t *scanline = &bitmap.pix((y*m_rowh)+chary, (x*16));

					uint8_t pixels = *fontptr++;
					uint8_t pixels2 = *fontptr++;
					if((attr & 0x8) && (chary == m_rowh - 1))
					{
						pixels = 0xff;
						pixels2 = 0xff;
					}

					*scanline++ = palette[BIT(pixels, 7)];
					*scanline++ = palette[BIT(pixels2, 7)];
					*scanline++ = palette[BIT(pixels, 6)];
					*scanline++ = palette[BIT(pixels2, 6)];
					*scanline++ = palette[BIT(pixels, 5)];
					*scanline++ = palette[BIT(pixels2, 5)];
					*scanline++ = palette[BIT(pixels, 4)];
					*scanline++ = palette[BIT(pixels2, 4)];
					*scanline++ = palette[BIT(pixels, 3)];
					*scanline++ = palette[BIT(pixels2, 3)];
					*scanline++ = palette[BIT(pixels, 2)];
					*scanline++ = palette[BIT(pixels2, 2)];
					*scanline++ = palette[BIT(pixels, 1)];
					*scanline++ = palette[BIT(pixels2, 1)];
					*scanline++ = palette[BIT(pixels, 0)];
					*scanline++ = palette[BIT(pixels2, 0)];
				}
			}
		}
	}

	return 0;
}

uint8_t tv990_state::kbdc_r(offs_t offset)
{
	if(offset)
		return m_kbdc->data_r(4);
	else
		return m_kbdc->data_r(0);
}

void tv990_state::kbdc_w(offs_t offset, uint8_t data)
{
	if(offset)
		m_kbdc->data_w(4, data);
	else
		m_kbdc->data_w(0, data);
}

void tv990_state::tv990_mem(address_map &map)
{
	map(0x000000, 0x03ffff).rom().region("maincpu", 0);
	map(0x060000, 0x06ffff).ram().share("vram"); // character/attribute RAM
	map(0x080000, 0x087fff).ram().share("fontram"); // font RAM
	map(0x090000, 0x0900ff).rw(FUNC(tv990_state::tvi1111_r), FUNC(tv990_state::tvi1111_w));
	map(0x0a0000, 0x0a000f).rw(m_uart[0], FUNC(ns16450_device::ins8250_r), FUNC(ns16450_device::ins8250_w)).umask16(0x00ff);
	map(0x0a0010, 0x0a001f).rw(m_uart[1], FUNC(ns16450_device::ins8250_r), FUNC(ns16450_device::ins8250_w)).umask16(0x00ff);
	map(0x0a0028, 0x0a002d).rw(LPT_TAG, FUNC(pc_lpt_device::read), FUNC(pc_lpt_device::write)).umask16(0x00ff);
	map(0x0b0000, 0x0b0003).rw(FUNC(tv990_state::kbdc_r), FUNC(tv990_state::kbdc_w)).umask16(0x00ff);
	map(0x0c0000, 0x0c7fff).ram().share("nvram");// work RAM
}

/* Input ports */
static INPUT_PORTS_START( tv990 )
	PORT_START("Screen")
	PORT_CONFNAME( 0x30, 0x00, "Color") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(tv990_state::color), 0)
	PORT_CONFSETTING(    0x00, "Green")
	PORT_CONFSETTING(    0x10, "Amber")
	PORT_CONFSETTING(    0x20, "White")
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(tv990_state::color)
{
	rgb_t color;
	if(newval == oldval)
		return;

	switch(newval)
	{
		case 0:
		default:
			color = rgb_t::green();
			break;
		case 1:
			color = rgb_t::amber();
			break;
		case 2:
			color = rgb_t::white();
			break;
	}
	m_screen->set_color(color);
}

void tv990_state::machine_reset()
{
	m_rowtimer->adjust(m_screen->time_until_pos(0));

	memset(tvi1111_regs, 0, sizeof(tvi1111_regs));
	m_rowh = 16;
	m_width = 80;
	m_height = 50;
}

void tv990_state::device_post_load()
{
	m_screen->set_visible_area(0, m_width * 16 - 1, 0, m_height * m_rowh - 1);
}

void tv990_state::tv990(machine_config &config)
{
	/* basic machine hardware */
	M68000(config, m_maincpu, 14967500);   // verified (59.86992/4)
	m_maincpu->set_addrmap(AS_PROGRAM, &tv990_state::tv990_mem);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_color(rgb_t::green());
	m_screen->set_screen_update(FUNC(tv990_state::screen_update));
	m_screen->set_size(132*16, 50*16);
	m_screen->set_visarea(0, (80*16)-1, 0, (50*16)-1);
	m_screen->set_refresh_hz(60);
	m_screen->screen_vblank().set(FUNC(tv990_state::vblank_irq));

	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	NS16450(config, m_uart[0], 3.6864_MHz_XTAL);
	m_uart[0]->out_dtr_callback().set(RS232A_TAG, FUNC(rs232_port_device::write_dtr));
	m_uart[0]->out_rts_callback().set(RS232A_TAG, FUNC(rs232_port_device::write_rts));
	m_uart[0]->out_tx_callback().set(RS232A_TAG, FUNC(rs232_port_device::write_txd));
	m_uart[0]->out_int_callback().set(FUNC(tv990_state::uart0_irq));

	NS16450(config, m_uart[1], 3.6864_MHz_XTAL);
	m_uart[1]->out_dtr_callback().set(RS232B_TAG, FUNC(rs232_port_device::write_dtr));
	m_uart[1]->out_rts_callback().set(RS232B_TAG, FUNC(rs232_port_device::write_rts));
	m_uart[1]->out_tx_callback().set(RS232B_TAG, FUNC(rs232_port_device::write_txd));
	m_uart[1]->out_int_callback().set(FUNC(tv990_state::uart1_irq));

	pc_lpt_device &lpt(PC_LPT(config, LPT_TAG));
	lpt.irq_handler().set(FUNC(tv990_state::lpt_irq));

	rs232_port_device &rs232a(RS232_PORT(config, RS232A_TAG, default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_uart[0], FUNC(ns16450_device::rx_w));
	rs232a.dcd_handler().set(m_uart[0], FUNC(ns16450_device::dcd_w));
	rs232a.cts_handler().set(m_uart[0], FUNC(ns16450_device::cts_w));

	rs232_port_device &rs232b(RS232_PORT(config, RS232B_TAG, default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_uart[1], FUNC(ns16450_device::rx_w));
	rs232b.dcd_handler().set(m_uart[1], FUNC(ns16450_device::dcd_w));
	rs232b.cts_handler().set(m_uart[1], FUNC(ns16450_device::cts_w));

	KBDC8042(config, m_kbdc);
	m_kbdc->set_keyboard_type(kbdc8042_device::KBDC8042_STANDARD);
	m_kbdc->input_buffer_full_callback().set_inputline("maincpu", M68K_IRQ_2);
	m_kbdc->set_keyboard_tag("at_keyboard");

	at_keyboard_device &at_keyb(AT_KEYB(config, "at_keyboard", pc_keyboard_device::KEYBOARD_TYPE::AT, 1));
	at_keyb.keypress().set(m_kbdc, FUNC(kbdc8042_device::keyboard_w));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	SPEAKER(config, "mono").front_center();
	BEEP(config, "beep", 1000).add_route(ALL_OUTPUTS, "mono", 1.0); //whats the freq?
}

/* ROM definition */
ROM_START( tv990 )
	ROM_REGION( 0x40000, "maincpu", 0 )
	ROM_LOAD16_BYTE( "180003-89_u3.bin", 0x000000, 0x010000, CRC(0465fc55) SHA1(b8874ce54bf2bf4f77664194d2f23c0e4e6ccbe9) )
	ROM_LOAD16_BYTE( "180003-90_u4.bin", 0x000001, 0x010000, CRC(fad7d77d) SHA1(f1114a4a07c8b4ffa0323a2e7ce03d82a386f7d3) )
ROM_END

ROM_START( tv995 )
	ROM_REGION( 0x40000, "maincpu", 0 )
	ROM_LOAD16_BYTE( "995-65_u3.bin", 0x000000, 0x020000, CRC(2d71b6fe) SHA1(a2a3406c19308eb9232db319ea8f151949b2ac74) )
	ROM_LOAD16_BYTE( "995-65_u4.bin", 0x000001, 0x020000, CRC(dc002af2) SHA1(9608e7a729c5ac0fc58f673eaf441d2f4f591ec6) )
ROM_END

} // anonymous namespace


/* Driver */
COMP( 1992, tv990, 0, 0, tv990, tv990, tv990_state, empty_init, "TeleVideo", "TeleVideo 990",    MACHINE_SUPPORTS_SAVE )
COMP( 1994, tv995, 0, 0, tv990, tv990, tv990_state, empty_init, "TeleVideo", "TeleVideo 995-65", MACHINE_SUPPORTS_SAVE )



tvc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic,Sandro Ronco
/***************************************************************************

        Videoton TVC 32/64 driver

        12/05/2009 Skeleton driver.

        TODO:
        - UPM crashes when formatting a floppy
        - overscan and mid-frame changes

****************************************************************************/

#include "emu.h"
#include "tvc_a.h"

#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "machine/bankdev.h"
#include "machine/ram.h"
#include "video/mc6845.h"

#include "bus/centronics/ctronics.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "bus/tvc/tvc.h"
#include "bus/tvc/hbf.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/tvc_cas.h"


namespace {

#define CENTRONICS_TAG  "centronics"


class tvc_state : public driver_device
{
public:
	tvc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_bank1(*this, "bank1")
		, m_bank3(*this, "bank3")
		, m_bank4(*this, "bank4")
		, m_ram(*this, RAM_TAG)
		, m_sound(*this, "custom")
		, m_cassette(*this, "cassette")
		, m_cart(*this, "cartslot")
		, m_centronics(*this, CENTRONICS_TAG)
		, m_expansions(*this, "exp%u", 1)
		, m_palette(*this, "palette")
		, m_keyboard(*this, "LINE.%u", 0)
	{ }

	void tvc(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<address_map_bank_device> m_bank1;
	required_device<address_map_bank_device> m_bank3;
	required_device<address_map_bank_device> m_bank4;
	required_device<ram_device> m_ram;
	required_device<tvc_sound_device> m_sound;
	required_device<cassette_image_device> m_cassette;
	required_device<generic_slot_device> m_cart;
	required_device<centronics_device> m_centronics;
	required_device_array<tvcexp_slot_device, 4> m_expansions;
	required_device<palette_device> m_palette;
	required_ioport_array<16> m_keyboard;

	uint8_t     *m_vram_base = nullptr;
	uint8_t     m_video_mode = 0;
	uint8_t     m_keyline = 0;
	uint8_t     m_active_slot = 0;
	uint8_t     m_int_flipflop = 0;
	uint8_t     m_col[4]{};
	uint8_t     m_vram_bank = 0;
	uint8_t     m_cassette_ff = 0;
	uint8_t     m_centronics_ff = 0;

	void bank_w(uint8_t data);
	void palette_w(offs_t offset, uint8_t data);
	void keyboard_w(uint8_t data);
	uint8_t keyboard_r();
	uint8_t int_state_r();
	void flipflop_w(uint8_t data);
	void border_color_w(uint8_t data);
	void sound_w(offs_t offset, uint8_t data);
	void cassette_w(uint8_t data);
	uint8_t _5b_r();
	void int_ff_set(int state);
	void centronics_ack(int state);

	// expansions
	void expansion_w(offs_t offset, uint8_t data);
	uint8_t expansion_r(offs_t offset);
	uint8_t exp_id_r();
	void expint_ack_w(offs_t offset, uint8_t data);

	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);

	MC6845_UPDATE_ROW(crtc_update_row);

	void tvc_palette(palette_device &palette) const;

	void tvc_mem(address_map &map) ATTR_COLD;
	void tvc_bank1(address_map &map) ATTR_COLD;
	void tvc_bank3(address_map &map) ATTR_COLD;
	void tvc_bank4(address_map &map) ATTR_COLD;
	void tvc_io(address_map &map) ATTR_COLD;
};

class tvc64p_state : public tvc_state
{
public:
	tvc64p_state(const machine_config &mconfig, device_type type, const char *tag)
		: tvc_state(mconfig, type, tag)
		, m_vram_bank1(*this, "vram_bank1")
		, m_vram_bank3(*this, "vram_bank3")
	{ }

	void tvc64p(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void vram_bank_w(uint8_t data);

	void bank1_64p(address_map &map) ATTR_COLD;
	void bank3_64p(address_map &map) ATTR_COLD;
	void io_64p(address_map &map) ATTR_COLD;

	required_memory_bank m_vram_bank1;
	required_memory_bank m_vram_bank3;
	std::unique_ptr<uint8_t[]> m_vram_ptr;
};



void tvc_state::expansion_w(offs_t offset, uint8_t data)
{
	m_expansions[m_active_slot & 3]->write(offset, data);
}


uint8_t tvc_state::expansion_r(offs_t offset)
{
	return m_expansions[m_active_slot & 3]->read(offset);
}

void tvc_state::bank_w(uint8_t data)
{
	m_bank1->set_bank(BIT(data, 3, 2));
	m_bank3->set_bank(BIT(data, 5));
	m_bank4->set_bank(BIT(data, 6, 2));
}

void tvc64p_state::vram_bank_w(uint8_t data)
{
	// bit 4-5 - screen video RAM
	// bit 2-3 - video RAM active in bank 3
	// bit 0-1 - video RAM active in bank 1

	m_vram_bank = data;
	m_vram_bank1->set_entry(BIT(data, 0, 2));
	m_vram_bank3->set_entry(BIT(data, 2, 2));
}

void tvc_state::palette_w(offs_t offset, uint8_t data)
{
	//  0 I 0 G | 0 R 0 B
	//  0 0 0 0 | I G R B
	int i = ((data&0x40)>>3) | ((data&0x10)>>2) | ((data&0x04)>>1) | (data&0x01);

	m_col[offset] = i;
}

void tvc_state::keyboard_w(uint8_t data)
{
	// bit 6-7 - expansion select
	// bit 0-3 - keyboard scan

	m_keyline = data & 0x0f;
	m_active_slot = (data>>6) & 0x03;
}

uint8_t tvc_state::keyboard_r()
{
	return m_keyboard[m_keyline & 0x0f]->read();
}

uint8_t tvc_state::int_state_r()
{
	/*
	    x--- ----   centronics ACK flipflop
	    -x-- ----   colour
	    --x- ----   cassette input
	    ---x ----   vblank or tone interrupt
	    ---- xxxx   expansions interrupt (active low)
	*/

	double level = m_cassette->input();

	uint8_t expint = (m_expansions[0]->int_r()<<0) | (m_expansions[1]->int_r()<<1) |
					(m_expansions[2]->int_r()<<2) | (m_expansions[3]->int_r()<<3);

	return 0x40 | (m_int_flipflop << 4) | (level > 0.01 ? 0x20 : 0x00) | (m_centronics_ff << 7) | (expint & 0x0f);
}

void tvc_state::flipflop_w(uint8_t data)
{
	// every write here clears the vblank flipflop
	m_int_flipflop = 1;
	m_maincpu->set_input_line(0, CLEAR_LINE);
}

uint8_t tvc_state::exp_id_r()
{
	// expansion slots ID
	return  (m_expansions[0]->id_r()<<0) | (m_expansions[1]->id_r()<<2) |
			(m_expansions[2]->id_r()<<4) | (m_expansions[3]->id_r()<<6);
}

void tvc_state::expint_ack_w(offs_t offset, uint8_t data)
{
	m_expansions[offset & 3]->int_ack();
}

void tvc_state::border_color_w(uint8_t data)
{
	// x-x- x-x-    border color (I G R B)
}


void tvc_state::sound_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
		case 1:
			// bit 6-7 - cassette motors
			m_cassette->change_state(BIT(data, 6) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
			//m_cassette2->change_state(BIT(data, 7) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
			m_cassette->output(m_cassette_ff ? +1 : -1);
			break;
		case 2:
			// bit 0-1 - video mode
			// bit 7   - centronics STROBE
			m_video_mode = data & 0x03;
			m_centronics->write_strobe(BIT(data, 7));
			if (!BIT(data, 7))
				m_centronics_ff = 0;
			break;
	}

	// sound ports
	m_sound->write(offset, data);
}

uint8_t tvc_state::_5b_r()
{
	if (!machine().side_effects_disabled())
		m_sound->reset_divider();
	return 0xff;
}

void tvc_state::cassette_w(uint8_t data)
{
	// writig here cause the toggle of the cassette flipflop
	m_cassette_ff = !m_cassette_ff;
	m_cassette->output(m_cassette_ff ? +1 : -1);
}

void tvc_state::tvc_mem(address_map &map)
{
	map(0x0000, 0x3fff).m(m_bank1, FUNC(address_map_bank_device::amap8));
	map(0x4000, 0x7fff).unmaprw(); // System RAM page 2
	map(0x8000, 0xbfff).m(m_bank3, FUNC(address_map_bank_device::amap8));
	map(0xc000, 0xffff).m(m_bank4, FUNC(address_map_bank_device::amap8));
}

void tvc_state::tvc_bank1(address_map &map)
{
	map(0x0000, 0x3fff).rom().region("sys", 0); // System ROM
	map(0x4000, 0x7fff).unmaprw(); // Cart ROM (if provided)
	map(0x8000, 0xbfff).unmaprw(); // System RAM page 1
}

void tvc64p_state::bank1_64p(address_map &map)
{
	tvc_bank1(map);
	map(0xc000, 0xffff).bankrw("vram_bank1"); // Video RAM (TVC 64+ only)
}

void tvc_state::tvc_bank3(address_map &map)
{
	map(0x0000, 0x3fff).ram().share("vram"); // Video RAM
	map(0x4000, 0x7fff).unmaprw(); // System RAM page 3
}

void tvc64p_state::bank3_64p(address_map &map)
{
	map(0x0000, 0x3fff).bankrw("vram_bank3"); // Video RAM
}

void tvc_state::tvc_bank4(address_map &map)
{
	map(0x0000, 0x3fff).unmaprw(); // Cart ROM (if provided)
	map(0x4000, 0x7fff).rom().region("sys", 0); // System ROM
	map(0x8000, 0xbfff).unmaprw(); // RAM (if provided)
	map(0xc000, 0xdfff).rw(FUNC(tvc_state::expansion_r), FUNC(tvc_state::expansion_w));
	map(0xe000, 0xffff).rom().region("ext", 0x2000); // External ROM
}

void tvc_state::tvc_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x00).w(FUNC(tvc_state::border_color_w));
	map(0x01, 0x01).w("cent_data_out", FUNC(output_latch_device::write));
	map(0x02, 0x02).w(FUNC(tvc_state::bank_w));
	map(0x03, 0x03).w(FUNC(tvc_state::keyboard_w));
	map(0x04, 0x06).w(FUNC(tvc_state::sound_w));
	map(0x07, 0x07).w(FUNC(tvc_state::flipflop_w));
	map(0x10, 0x1f).rw("exp1", FUNC(tvcexp_slot_device::io_read), FUNC(tvcexp_slot_device::io_write));
	map(0x20, 0x2f).rw("exp2", FUNC(tvcexp_slot_device::io_read), FUNC(tvcexp_slot_device::io_write));
	map(0x30, 0x3f).rw("exp3", FUNC(tvcexp_slot_device::io_read), FUNC(tvcexp_slot_device::io_write));
	map(0x40, 0x4f).rw("exp4", FUNC(tvcexp_slot_device::io_read), FUNC(tvcexp_slot_device::io_write));
	map(0x50, 0x50).w(FUNC(tvc_state::cassette_w));
	map(0x58, 0x58).r(FUNC(tvc_state::keyboard_r));
	map(0x59, 0x59).r(FUNC(tvc_state::int_state_r));
	map(0x5a, 0x5a).r(FUNC(tvc_state::exp_id_r));
	map(0x5b, 0x5b).r(FUNC(tvc_state::_5b_r));
	map(0x58, 0x5b).w(FUNC(tvc_state::expint_ack_w));
	map(0x60, 0x63).w(FUNC(tvc_state::palette_w));
	map(0x70, 0x70).w("crtc", FUNC(mc6845_device::address_w));
	map(0x71, 0x71).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
}

void tvc64p_state::io_64p(address_map &map)
{
	tvc_io(map);
	map(0x0f, 0x0f).w(FUNC(tvc64p_state::vram_bank_w));
}

/* Input ports */
static INPUT_PORTS_START( tvc )
	PORT_START("LINE.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)         PORT_CHAR('5')  PORT_CHAR('%')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)         PORT_CHAR('3')  PORT_CHAR('+')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)         PORT_CHAR('2')  PORT_CHAR('\"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)         PORT_CHAR('0')  PORT_CHAR('&')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)         PORT_CHAR('6')  PORT_CHAR('/')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"Í") PORT_CODE(KEYCODE_1_PAD)   PORT_CHAR(U'Í',U'í')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)         PORT_CHAR('1')  PORT_CHAR('\'')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)         PORT_CHAR('4')  PORT_CHAR('!')

	PORT_START("LINE.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR('^')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)         PORT_CHAR('8')  PORT_CHAR('(')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)         PORT_CHAR('9')  PORT_CHAR(')')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"ü") PORT_CODE(KEYCODE_2_PAD)   PORT_CHAR(U'ü')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("*") PORT_CODE(KEYCODE_ASTERISK)  PORT_CHAR('*')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"ó") PORT_CODE(KEYCODE_3_PAD)   PORT_CHAR(U'ó')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"ö") PORT_CODE(KEYCODE_4_PAD)   PORT_CHAR(U'ö')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)         PORT_CHAR('7')  PORT_CHAR('=')

	PORT_START("LINE.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)         PORT_CHAR('t')  PORT_CHAR('T')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)         PORT_CHAR('e')  PORT_CHAR('E')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)         PORT_CHAR('w')  PORT_CHAR('W')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON)     PORT_CHAR(';')  PORT_CHAR('$')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)         PORT_CHAR('z')  PORT_CHAR('Z')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_HOME)      PORT_CHAR('@')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)         PORT_CHAR('q')  PORT_CHAR('Q')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)         PORT_CHAR('r')  PORT_CHAR('R')

	PORT_START("LINE.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)         PORT_CHAR('i')  PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)         PORT_CHAR('o')  PORT_CHAR('O')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"ő") PORT_CODE(KEYCODE_5_PAD)   PORT_CHAR(U'ő')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')  PORT_CHAR('{')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"ú") PORT_CODE(KEYCODE_6_PAD)   PORT_CHAR(U'ú')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)         PORT_CHAR('p')  PORT_CHAR('P')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)         PORT_CHAR('u')  PORT_CHAR('U')

	PORT_START("LINE.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)         PORT_CHAR('g')  PORT_CHAR('G')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)         PORT_CHAR('d')  PORT_CHAR('D')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)         PORT_CHAR('s')  PORT_CHAR('S')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\')    PORT_CHAR('|')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)         PORT_CHAR('h')  PORT_CHAR('H')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("<") PORT_CODE(KEYCODE_END)       PORT_CHAR('<')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)         PORT_CHAR('a')  PORT_CHAR('A')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)         PORT_CHAR('f')  PORT_CHAR('F')

	PORT_START("LINE.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(UCHAR_MAMEKEY(BACKSPACE))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)         PORT_CHAR('k')  PORT_CHAR('K')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)         PORT_CHAR('l')  PORT_CHAR('L')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"á") PORT_CODE(KEYCODE_7_PAD)   PORT_CHAR(U'á')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"ű") PORT_CODE(KEYCODE_8_PAD)   PORT_CHAR(U'ű')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"é") PORT_CODE(KEYCODE_9_PAD)   PORT_CHAR(U'é')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)         PORT_CHAR('j')  PORT_CHAR('J')

	PORT_START("LINE.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)         PORT_CHAR('b')  PORT_CHAR('B')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)         PORT_CHAR('c')  PORT_CHAR('C')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)         PORT_CHAR('x')  PORT_CHAR('X')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_RSHIFT) PORT_CODE(KEYCODE_LSHIFT)  PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)         PORT_CHAR('n')  PORT_CHAR('N')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)         PORT_CHAR('y')  PORT_CHAR('Y')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)         PORT_CHAR('v')  PORT_CHAR('V')

	PORT_START("LINE.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Alt") PORT_CODE(KEYCODE_RALT) PORT_CODE(KEYCODE_LALT)    PORT_CHAR(UCHAR_MAMEKEY(LALT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA)     PORT_CHAR(',')  PORT_CHAR('?')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)      PORT_CHAR('.')  PORT_CHAR(':')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC)     PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("=") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')  PORT_CHAR('_')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)         PORT_CHAR('m')  PORT_CHAR('M')

	PORT_START("LINE.8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Insert")  PORT_CODE(KEYCODE_INSERT)  PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up")      PORT_CODE(KEYCODE_UP)      PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down")    PORT_CODE(KEYCODE_DOWN)    PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Fire")    PORT_CODE(KEYCODE_PGUP)    //PORT_CHAR(UCHAR_MAMEKEY())
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Acc")     PORT_CODE(KEYCODE_PGDN)    //PORT_CHAR(UCHAR_MAMEKEY())
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right")   PORT_CODE(KEYCODE_RIGHT)   PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left")    PORT_CODE(KEYCODE_LEFT)    PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE.9")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("LINE.10")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("LINE.11")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("LINE.12")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("LINE.13")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("LINE.14")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("LINE.15")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( tvc64pru )
	PORT_START("LINE.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)         PORT_CHAR('4')  PORT_CHAR(U'ж')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)         PORT_CHAR('2')  PORT_CHAR('"')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)         PORT_CHAR('1')  PORT_CHAR('!')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DL") PORT_CODE(KEYCODE_HOME)     // delete line
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)         PORT_CHAR('5')  PORT_CHAR('%')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DC") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON)     PORT_CHAR(';')  PORT_CHAR('+')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)         PORT_CHAR('3')  PORT_CHAR('#')

	PORT_START("LINE.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("IL") PORT_CODE(KEYCODE_END)      // insert line
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)         PORT_CHAR('7')  PORT_CHAR('\'')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)         PORT_CHAR('8')  PORT_CHAR('{')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)         PORT_CHAR('0')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("_") PORT_CODE(KEYCODE_EQUALS)    PORT_CHAR('_')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)     PORT_CHAR('-')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)         PORT_CHAR('9')  PORT_CHAR(')')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)         PORT_CHAR('6')  PORT_CHAR('&')

	PORT_START("LINE.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)         PORT_CHAR('e')  PORT_CHAR('E')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)         PORT_CHAR('u')  PORT_CHAR('U')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)         PORT_CHAR('c')  PORT_CHAR('C')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)         PORT_CHAR('n')  PORT_CHAR('N')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)       PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)         PORT_CHAR('j')  PORT_CHAR('J')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)         PORT_CHAR('k')  PORT_CHAR('K')

	PORT_START("LINE.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_DEL)       PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')  PORT_CHAR('{')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)         PORT_CHAR('h')  PORT_CHAR('H')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA)     PORT_CHAR(',')  PORT_CHAR('<')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_QUOTE)     PORT_CHAR(':')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)         PORT_CHAR('z')  PORT_CHAR('Z')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)         PORT_CHAR('g')  PORT_CHAR('G')

	PORT_START("LINE.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)         PORT_CHAR('p')  PORT_CHAR('P')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)         PORT_CHAR('w')  PORT_CHAR('W')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)         PORT_CHAR('y')  PORT_CHAR('Y')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)         PORT_CHAR('r')  PORT_CHAR('R')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)   PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)         PORT_CHAR('f')  PORT_CHAR('F')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)         PORT_CHAR('a')  PORT_CHAR('A')

	PORT_START("LINE.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(UCHAR_MAMEKEY(BACKSPACE))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)         PORT_CHAR('l')  PORT_CHAR('L')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)         PORT_CHAR('d')  PORT_CHAR('D')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\')    PORT_CHAR('|')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)      PORT_CHAR('.')  PORT_CHAR('>')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)         PORT_CHAR('v')  PORT_CHAR('V')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)         PORT_CHAR('o')  PORT_CHAR('O')

	PORT_START("LINE.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)         PORT_CHAR('i')  PORT_CHAR('I')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)         PORT_CHAR('s')  PORT_CHAR('S')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_9_PAD)     PORT_CHAR('^')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_RSHIFT) PORT_CODE(KEYCODE_LSHIFT)  PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)         PORT_CHAR('t')  PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)         PORT_CHAR('q')  PORT_CHAR('Q')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)         PORT_CHAR('m')  PORT_CHAR('M')

	PORT_START("LINE.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Alt") PORT_CODE(KEYCODE_RALT) PORT_CODE(KEYCODE_LALT)    PORT_CHAR(UCHAR_MAMEKEY(LALT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)         PORT_CHAR('b')  PORT_CHAR('B')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_1_PAD)     PORT_CHAR('@')  PORT_CHAR('\'')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC)     PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("=") PORT_CODE(KEYCODE_SLASH)     PORT_CHAR('/')  PORT_CHAR('?')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)         PORT_CHAR('x')  PORT_CHAR('X')

	PORT_START("LINE.8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Insert")     PORT_CODE(KEYCODE_INSERT)   PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Joy Up")     PORT_CODE(KEYCODE_8_PAD)    PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Joy Down")   PORT_CODE(KEYCODE_2_PAD)    PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Joy Fire")   PORT_CODE(KEYCODE_PGUP)     //PORT_CHAR(UCHAR_MAMEKEY())
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Joy Acc")    PORT_CODE(KEYCODE_PGDN)     //PORT_CHAR(UCHAR_MAMEKEY())
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Joy Right")  PORT_CODE(KEYCODE_6_PAD)    PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Joy Left")   PORT_CODE(KEYCODE_4_PAD)    PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("LINE.9")
	PORT_BIT(0x7f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("*")  PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR('*')
	PORT_START("LINE.10")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("LINE.11")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("LINE.12")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("LINE.13")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("LINE.14")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_START("LINE.15")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

void tvc_state::machine_start()
{
	for (int i = 0; i < 4; i++)
		m_col[i] = i;

	m_int_flipflop = 0;

	u8 *r = m_ram->pointer();
	m_bank1->space(0).install_ram(0x8000, 0xbfff, r);
	m_maincpu->space(AS_PROGRAM).install_ram(0x4000, 0x7fff, r+0x4000);
	if (m_ram->size() > 0x8000)
	{
		m_bank3->space(0).install_ram(0x4000, 0x7fff, r+0x8000);
		m_bank4->space(0).install_ram(0x8000, 0xbfff, r+0xc000);
	}

	memory_share *vram = memshare("vram");
	if (vram)
		m_vram_base = static_cast<uint8_t *>(vram->ptr());
	m_vram_bank = 0;

	std::string region_tag;
	memory_region *cart_rom = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());
	if (cart_rom != nullptr)
	{
		m_bank1->space(0).install_rom(0x4000, 0x7fff, cart_rom->base());
		m_bank4->space(0).install_rom(0x0000, 0x3fff, cart_rom->base());
	}

	save_item(NAME(m_video_mode));
	save_item(NAME(m_keyline));
	save_item(NAME(m_active_slot));
	save_item(NAME(m_int_flipflop));
	save_item(NAME(m_col));
	save_item(NAME(m_vram_bank));
	save_item(NAME(m_cassette_ff));
	save_item(NAME(m_centronics_ff));
}

void tvc64p_state::machine_start()
{
	tvc_state::machine_start();

	m_vram_ptr = make_unique_clear<uint8_t[]>(0x10000);
	m_vram_base = m_vram_ptr.get();
	m_vram_bank1->configure_entries(0, 4, m_vram_base, 0x4000);
	m_vram_bank3->configure_entries(0, 4, m_vram_base, 0x4000);
}

void tvc_state::machine_reset()
{
	bank_w(0);
	m_video_mode = 0;
	m_cassette_ff = 1;
	m_centronics_ff = 1;
	m_active_slot = 0;
}

void tvc64p_state::machine_reset()
{
	tvc_state::machine_reset();

	vram_bank_w(0);
}

MC6845_UPDATE_ROW( tvc_state::crtc_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	uint32_t *p = &bitmap.pix(y);
	uint8_t const *const vram = &m_vram_base[(m_vram_bank & 0x30)<<10];
	uint16_t const offset = ((ma*4 + ra*0x40) & 0x3fff);

	switch(m_video_mode) {
		case 0 :
				//  2 colors mode
				for ( int i = 0; i < x_count; i++ )
				{
					uint8_t const data = vram[offset + i];
					*p++ = palette[m_col[BIT(data,7)]];
					*p++ = palette[m_col[BIT(data,6)]];
					*p++ = palette[m_col[BIT(data,5)]];
					*p++ = palette[m_col[BIT(data,4)]];
					*p++ = palette[m_col[BIT(data,3)]];
					*p++ = palette[m_col[BIT(data,2)]];
					*p++ = palette[m_col[BIT(data,1)]];
					*p++ = palette[m_col[BIT(data,0)]];
				}
				break;
		case 1 :
				// 4 colors mode
				// a0 b0 c0 d0 a1 b1 c1 d1
				for ( int i = 0; i < x_count; i++ )
				{
					uint8_t const data = vram[offset + i];
					*p++ = palette[m_col[BIT(data,3)*2 + BIT(data,7)]];
					*p++ = palette[m_col[BIT(data,3)*2 + BIT(data,7)]];
					*p++ = palette[m_col[BIT(data,2)*2 + BIT(data,6)]];
					*p++ = palette[m_col[BIT(data,2)*2 + BIT(data,6)]];
					*p++ = palette[m_col[BIT(data,1)*2 + BIT(data,5)]];
					*p++ = palette[m_col[BIT(data,1)*2 + BIT(data,5)]];
					*p++ = palette[m_col[BIT(data,0)*2 + BIT(data,4)]];
					*p++ = palette[m_col[BIT(data,0)*2 + BIT(data,4)]];
				}
				break;
		default:
				// 16 colors mode
				// IIGG RRBB
				for ( int i = 0; i < x_count; i++ )
				{
					uint8_t const data = vram[offset + i];
					uint8_t const col0 = ((data & 0x80)>>4) | ((data & 0x20)>>3) | ((data & 0x08)>>2) | ((data & 0x02)>>1);
					uint8_t const col1 = ((data & 0x40)>>3) | ((data & 0x10)>>2) | ((data & 0x04)>>1) | (data & 0x01);
					*p++ = palette[col0];
					*p++ = palette[col0];
					*p++ = palette[col0];
					*p++ = palette[col0];
					*p++ = palette[col1];
					*p++ = palette[col1];
					*p++ = palette[col1];
					*p++ = palette[col1];
				}
				break;

	}
}

void tvc_state::tvc_palette(palette_device &palette) const
{
	static constexpr rgb_t tvc_pens[16] =
	{
		{ 0x00, 0x00, 0x00 },
		{ 0x00, 0x00, 0x7f },
		{ 0x7f, 0x00, 0x00 },
		{ 0x7f, 0x00, 0x7f },
		{ 0x00, 0x7f, 0x00 },
		{ 0x00, 0x7f, 0x7f },
		{ 0x7f, 0x7f, 0x00 },
		{ 0x7f, 0x7f, 0x7f },
		{ 0x00, 0x00, 0x00 },
		{ 0x00, 0x00, 0xff },
		{ 0xff, 0x00, 0x00 },
		{ 0xff, 0x00, 0xff },
		{ 0x00, 0xff, 0x00 },
		{ 0x00, 0xff, 0xff },
		{ 0xff, 0xff, 0x00 },
		{ 0xff, 0xff, 0xff }
	};

	palette.set_pen_colors(0, tvc_pens);
}

void tvc_state::int_ff_set(int state)
{
	if (state)
	{
		m_int_flipflop = 0;
		m_maincpu->set_input_line(0, ASSERT_LINE);
	}
}

void tvc_state::centronics_ack(int state)
{
	if (state)
		m_centronics_ff = 1;
}

QUICKLOAD_LOAD_MEMBER(tvc_state::quickload_cb)
{
	uint8_t first_byte;

	image.fread(&first_byte, 1);
	if (first_byte == 0x11)
	{
		image.fseek(0x90, SEEK_SET);
		image.fread(m_ram->pointer() + 0x19ef, image.length() - 0x90);
		return std::make_pair(std::error_condition(), std::string());
	}
	else
	{
		return std::make_pair(image_error::INVALIDIMAGE, std::string());
	}
}


void tvc_exp(device_slot_interface &device)
{
	device.option_add("hbf", TVC_HBF);      // Videoton HBF floppy interface
}


void tvc_state::tvc(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 3125000);
	m_maincpu->set_addrmap(AS_PROGRAM, &tvc_state::tvc_mem);
	m_maincpu->set_addrmap(AS_IO, &tvc_state::tvc_io);

	ADDRESS_MAP_BANK(config, m_bank1);
	m_bank1->set_endianness(ENDIANNESS_LITTLE);
	m_bank1->set_data_width(8);
	m_bank1->set_addr_width(16);
	m_bank1->set_stride(0x4000);
	m_bank1->set_addrmap(0, &tvc_state::tvc_bank1);

	ADDRESS_MAP_BANK(config, m_bank3);
	m_bank3->set_endianness(ENDIANNESS_LITTLE);
	m_bank3->set_data_width(8);
	m_bank3->set_addr_width(15);
	m_bank3->set_stride(0x4000);
	m_bank3->set_addrmap(0, &tvc_state::tvc_bank3);

	ADDRESS_MAP_BANK(config, m_bank4);
	m_bank4->set_endianness(ENDIANNESS_LITTLE);
	m_bank4->set_data_width(8);
	m_bank4->set_addr_width(16);
	m_bank4->set_stride(0x4000);
	m_bank4->set_addrmap(0, &tvc_state::tvc_bank4);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(512, 240);
	screen.set_visarea(0, 512 - 1, 0, 240 - 1);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	PALETTE(config, m_palette, FUNC(tvc_state::tvc_palette), 16);

	mc6845_device &crtc(MC6845(config, "crtc", 3125000/2)); // clk taken from schematics
	crtc.set_screen("screen");
	crtc.set_show_border_area(false);
	crtc.set_char_width(8); /*?*/
	crtc.set_update_row_callback(FUNC(tvc_state::crtc_update_row));
	crtc.out_cur_callback().set(FUNC(tvc_state::int_ff_set));

	/* internal ram */
	RAM(config, RAM_TAG).set_default_value(0x00).set_default_size("64K").set_extra_options("32K");

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	TVC_SOUND(config, m_sound, 0);
	m_sound->sndint_wr_callback().set(FUNC(tvc_state::int_ff_set));
	m_sound->add_route(ALL_OUTPUTS, "mono", 0.75);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set(FUNC(tvc_state::centronics_ack));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	/* cartridge */
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "tvc_cart", "bin,rom,crt");

	/* expansion interface */
	TVCEXP_SLOT(config, m_expansions[0], tvc_exp , nullptr);
	m_expansions[0]->out_irq_callback().set_inputline(m_maincpu, 0);
	m_expansions[0]->out_nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	TVCEXP_SLOT(config, m_expansions[1], tvc_exp , nullptr);
	m_expansions[1]->out_irq_callback().set_inputline(m_maincpu, 0);
	m_expansions[1]->out_nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	TVCEXP_SLOT(config, m_expansions[2], tvc_exp , nullptr);
	m_expansions[2]->out_irq_callback().set_inputline(m_maincpu, 0);
	m_expansions[2]->out_nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	TVCEXP_SLOT(config, m_expansions[3], tvc_exp , nullptr);
	m_expansions[3]->out_irq_callback().set_inputline(m_maincpu, 0);
	m_expansions[3]->out_nmi_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);

	/* cassette */
	CASSETTE(config, m_cassette);
	m_cassette->set_formats(tvc64_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->set_interface("tvc_cass");
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	/* quickload */
	QUICKLOAD(config, "quickload", "cas", attotime::from_seconds(6)).set_load_callback(FUNC(tvc_state::quickload_cb));

	/* Software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("tvc_cart");
	SOFTWARE_LIST(config, "cass_list").set_original("tvc_cass");
	SOFTWARE_LIST(config, "flop_list").set_original("tvc_flop");
}

void tvc64p_state::tvc64p(machine_config &config)
{
	tvc(config);

	m_maincpu->set_addrmap(AS_IO, &tvc64p_state::io_64p);
	m_bank1->set_addrmap(0, &tvc64p_state::bank1_64p);
	m_bank3->set_addrmap(0, &tvc64p_state::bank3_64p);
}


/* ROM definition */
ROM_START( tvc64 )
	ROM_REGION( 0x4000, "sys", ROMREGION_ERASEFF )
	ROM_LOAD( "tvc12_d4.64k", 0x0000, 0x2000, CRC(834ca9be) SHA1(c333318c1c6185aae2d3dfb86d55e3a4a3071a73))
	ROM_LOAD( "tvc12_d3.64k", 0x2000, 0x2000, CRC(71753d02) SHA1(d9a1905cf55c532b3380c83158fb5254ee503829))

	ROM_REGION( 0x4000, "ext", ROMREGION_ERASEFF )
	ROM_LOAD( "tvc12_d7.64k", 0x2000, 0x2000, CRC(1cbbeac6) SHA1(54b29c9ca9942f04620fbf3edab3b8e3cd21c194))
ROM_END

ROM_START( tvc64p )
	ROM_REGION( 0x4000, "sys", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v22", "v2.2")
	ROMX_LOAD( "tvc22_d6.64k", 0x0000, 0x2000, CRC(05ac3a34) SHA1(bdc7eda5fd53f806dca8c4929ee498e8e59eb787), ROM_BIOS(0) )
	ROMX_LOAD( "tvc22_d4.64k", 0x2000, 0x2000, CRC(ba6ad589) SHA1(e5c8a6db506836a327d901387a8dc8c681a272db), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v21", "v2.1")
	ROMX_LOAD( "tvc21_d6.64k", 0x0000, 0x2000, CRC(f197ffce) SHA1(7b27a91504dd864170451949ada5f938d6532cae), ROM_BIOS(1) )
	ROMX_LOAD( "tvc21_d4.64k", 0x2000, 0x2000, CRC(b054c0b2) SHA1(c8ca8d5a4d092604de01e2cafc2a2dabe94e6380), ROM_BIOS(1) )

	ROM_REGION( 0x4000, "ext", ROMREGION_ERASEFF )
	ROM_LOAD( "tvc22_d7.64k", 0x2000, 0x2000, CRC(05e1c3a8) SHA1(abf119cf947ea32defd08b29a8a25d75f6bd4987))
ROM_END

ROM_START( tvc64pru )
	ROM_REGION( 0x4000, "sys", ROMREGION_ERASEFF )
	ROM_LOAD( "tvcru_d6.bin", 0x0000, 0x2000, CRC(1e0fa0b8) SHA1(9bebb6c8f03f9641bd35c9fd45ffc13a48e5c572))
	ROM_LOAD( "tvcru_d4.bin", 0x2000, 0x2000, CRC(bac5dd4f) SHA1(665a1b8c80b6ad82090803621f0c73ef9243c7d4))

	ROM_REGION( 0x4000, "ext", ROMREGION_ERASEFF )
	ROM_LOAD( "tvcru_d7.bin", 0x2000, 0x2000, CRC(70cde756) SHA1(c49662af9f6653347ead641e85777c3463cc161b))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT     CLASS         INIT        COMPANY       FULLNAME             FLAGS
COMP( 1985, tvc64,    0,      0,      tvc,     tvc,      tvc_state,    empty_init, "Videoton",   "TVC 64",            MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
COMP( 1985, tvc64p,   tvc64,  0,      tvc64p,  tvc,      tvc64p_state, empty_init, "Videoton",   "TVC 64+",           MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
COMP( 1985, tvc64pru, tvc64,  0,      tvc64p,  tvc64pru, tvc64p_state, empty_init, "Videoton",   "TVC 64+ (Russian)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )



tvdear.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

// TV Word Processor, with printer

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/nec/v25.h"
#include "softlist_dev.h"

namespace {

class tvdear_state : public driver_device
{
public:
	tvdear_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "systemcpu")
		, m_keyboard(*this, "KEY%u", 0U)
		, m_cart(*this, "cartslot")
	{
	}

	void tvdear(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	u8 p0_r();
	void p0_w(u8 data);
	u8 pt_r();

	void mem_map(address_map &map);
	void io_map(address_map &map);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	required_device<v25_device> m_maincpu;
	required_ioport_array<10> m_keyboard;
	required_device<generic_slot_device> m_cart;

	u8 m_p0 = 0;
};

void tvdear_state::machine_start()
{
	save_item(NAME(m_p0));
}

u8 tvdear_state::p0_r()
{
	return m_p0;
}

void tvdear_state::p0_w(u8 data)
{
	m_p0 = data;
}

u8 tvdear_state::pt_r()
{
	if (m_p0 < 0xa0)
		return m_keyboard[m_p0 >> 4]->read();
	else
		return 0xff;
}

void tvdear_state::mem_map(address_map &map)
{
	map(0x00000, 0x07fff).ram();
	map(0x10000, 0x17fff).ram();
	map(0x40000, 0xfffff).rom().region("maincpu", 0x040000);
}

void tvdear_state::io_map(address_map &map)
{
	map(0x0500, 0x0500).nopw(); // watchdog?
}

static INPUT_PORTS_START(tvdear)
	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('O') PORT_CHAR(0x3089) PORT_CODE(KEYCODE_O) // ら
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('9') PORT_CHAR(0x3088) PORT_CODE(KEYCODE_9) // よ
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('.') PORT_CHAR(0x308b) PORT_CODE(KEYCODE_STOP) // る
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('L') PORT_CHAR(0x308a) PORT_CODE(KEYCODE_L) // り
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 001D")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 001F")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 0016")

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('P') PORT_CHAR(0x305b) PORT_CODE(KEYCODE_P) // せ
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0') PORT_CHAR(0x308f) PORT_CODE(KEYCODE_0) // わ
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('%') PORT_CHAR(0x307b) PORT_CODE(KEYCODE_MINUS) // ほ
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHAR(0x308c) PORT_CODE(KEYCODE_COLON) // れ
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('&') PORT_CHAR(0x308d) PORT_CODE(KEYCODE_RSHIFT) // ろ
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('(') PORT_CHAR(0x309b) PORT_CODE(KEYCODE_OPENBRACE) // ■゛
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(':') PORT_CHAR(0x3051) PORT_CODE(KEYCODE_QUOTE) // け
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('/') PORT_CHAR(0x3081) PORT_CODE(KEYCODE_SLASH) // め

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('U') PORT_CHAR(0x306a) PORT_CODE(KEYCODE_U) // な
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR(0x3084) PORT_CODE(KEYCODE_7) // や
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('M') PORT_CHAR(0x3082) PORT_CODE(KEYCODE_M) // も
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('J') PORT_CHAR(0x307e) PORT_CODE(KEYCODE_J) // ま
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR(0x304a) PORT_CODE(KEYCODE_6) // お
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('Y') PORT_CHAR(0x3093) PORT_CODE(KEYCODE_Y) // ん
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('H') PORT_CHAR(0x304f) PORT_CODE(KEYCODE_H) // く
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('N') PORT_CHAR(0x307f) PORT_CODE(KEYCODE_N) // み

	PORT_START("KEY3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('I') PORT_CHAR(0x306b) PORT_CODE(KEYCODE_I) // に
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('8') PORT_CHAR(0x3086) PORT_CODE(KEYCODE_8) // ゆ
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',') PORT_CHAR(0x306d) PORT_CODE(KEYCODE_COMMA) // ね
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('K') PORT_CHAR(0x306e) PORT_CODE(KEYCODE_K) // の
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x00a5) PORT_CHAR(0x3078) PORT_CODE(KEYCODE_EQUALS) // ¥ へ
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(')') PORT_CHAR(0x309c) PORT_CODE(KEYCODE_CLOSEBRACE) // ■゜
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 0015")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('W') PORT_CHAR(0x3066) PORT_CODE(KEYCODE_W) // て
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR(0x3075) PORT_CODE(KEYCODE_2) // ふ
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('X') PORT_CHAR(0x3055) PORT_CODE(KEYCODE_X) // さ
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('S') PORT_CHAR(0x3068) PORT_CODE(KEYCODE_S) // と
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 0011")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Special Key L")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 0006")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 0009")

	PORT_START("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('R') PORT_CHAR(0x3059) PORT_CODE(KEYCODE_R) // す
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR(0x3046) PORT_CODE(KEYCODE_4) // う
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('V') PORT_CHAR(0x3072) PORT_CODE(KEYCODE_V) // ひ
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('F') PORT_CHAR(0x306f) PORT_CODE(KEYCODE_F) // は
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR(0x3048) PORT_CODE(KEYCODE_5) // え
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('T') PORT_CHAR(0x304b) PORT_CODE(KEYCODE_T) // か
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('G') PORT_CHAR(0x304d) PORT_CODE(KEYCODE_G) // き
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('B') PORT_CHAR(0x3053) PORT_CODE(KEYCODE_B) // こ

	PORT_START("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x2014) PORT_CHAR(0x30fc) PORT_CODE(KEYCODE_BACKSLASH) // — ー
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\'') PORT_CHAR(0x3080) PORT_CODE(KEYCODE_ENTER) // む
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 001E")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 001C")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 0014")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"\u30B9\u30DA\u30FC\u30B9 (Space)") PORT_CHAR(0x20) PORT_CODE(KEYCODE_SPACE) // スペース

	PORT_START("KEY7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('E') PORT_CHAR(0x3044) PORT_CODE(KEYCODE_E) // い
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR(0x3042) PORT_CODE(KEYCODE_3) // あ
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('C') PORT_CHAR(0x305d) PORT_CODE(KEYCODE_C) // そ
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('D') PORT_CHAR(0x3057) PORT_CODE(KEYCODE_D) // し
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Special Key H")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 0012")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 0013")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('Q') PORT_CHAR(0x305f) PORT_CODE(KEYCODE_Q) // た
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR(0x306c) PORT_CODE(KEYCODE_1) // ぬ
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('Z') PORT_CHAR(0x3064) PORT_CODE(KEYCODE_Z) // つ
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('A') PORT_CHAR(0x3061) PORT_CODE(KEYCODE_A) // ち
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 0003")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 000F")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("KEY9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 000B")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 007F")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 000D")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Key 0017")
INPUT_PORTS_END

DEVICE_IMAGE_LOAD_MEMBER(tvdear_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

void tvdear_state::tvdear(machine_config &config)
{
	V25(config, m_maincpu, 16000000); // NEC D70320DGJ-8; XTAL marked 16AKSS5HT
	m_maincpu->set_addrmap(AS_PROGRAM, &tvdear_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &tvdear_state::io_map);
	m_maincpu->p0_in_cb().set(FUNC(tvdear_state::p0_r));
	m_maincpu->p0_out_cb().set(FUNC(tvdear_state::p0_w));
	m_maincpu->pt_in_cb().set(FUNC(tvdear_state::pt_r));

	//MB90076(config, "tvvc", 14318181); // XTAL marked 14AKSS5JT

	GENERIC_CARTSLOT(config, m_cart, generic_linear_slot, "tvdear_cart");
	m_cart->set_device_load(FUNC(tvdear_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("tvdear");
}

ROM_START(tvdear)
	ROM_REGION(0x200000, "maincpu", 0)
	ROM_LOAD("d23c160000.u5", 0x00000, 0x200000, CRC(41ec9890) SHA1(20cfdfec7eeb39a9ce971f23fdc97b42a5d68301) )
ROM_END

} // anonymous namespace

// テレビディア マルチワープロ
CONS( 1995, tvdear,  0,          0,  tvdear,  tvdear, tvdear_state, empty_init, "Takara", "TV Dear Multi Word Processor", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_NODEVICE_PRINTER )



tvgame.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Robbbert
/***************************************************************************

Homebrew Z80-based TV Game computer by Mr. Isizu

http://w01.tp1.jp/~a571632211/z80tvgame/index.html

2015-06-12 Driver by Robbbert

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/i8255.h"
#include "sound/spkrdev.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

namespace {

class tvgame_state : public driver_device
{
public:
	tvgame_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_speaker(*this, "speaker")
		, m_p_videoram(*this, "videoram")
	{ }

	void tvgame(machine_config &config);

private:
	void speaker_w(uint8_t data);
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<speaker_sound_device> m_speaker;
	required_shared_ptr<uint8_t> m_p_videoram;
};

void tvgame_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).rom();
	map(0x8000, 0xbfff).ram();
	map(0xc000, 0xdfff).ram().share("videoram");
}

void tvgame_state::io_map(address_map &map)
{
	map.global_mask(3);
	map.unmap_value_high();
	map(0x0000, 0x0003).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
}

/* Input ports */
INPUT_PORTS_START( tvgame )
	PORT_START("LINE0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_4WAY
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_4WAY
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_4WAY
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_4WAY
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

void tvgame_state::speaker_w(uint8_t data)
{
	m_speaker->level_w(BIT(data, 0));
}

uint32_t tvgame_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint16_t sy=0,ma=241;

	for (uint8_t y = 0; y < 213; y++)
	{
		uint16_t *p = &bitmap.pix(sy++);
		for (uint16_t x = ma; x < ma+27; x++)
		{
			uint8_t gfx = m_p_videoram[x];

			/* Display a scanline of a character (8 pixels) */
			*p++ = BIT(gfx, 0);
			*p++ = BIT(gfx, 1);
			*p++ = BIT(gfx, 2);
			*p++ = BIT(gfx, 3);
			*p++ = BIT(gfx, 4);
			*p++ = BIT(gfx, 5);
			*p++ = BIT(gfx, 6);
			*p++ = BIT(gfx, 7);
		}
		ma+=30;
	}
	return 0;
}

void tvgame_state::tvgame(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &tvgame_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &tvgame_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(FUNC(tvgame_state::screen_update));
	screen.set_size(216, 213);
	screen.set_visarea(0, 215, 0, 212);
	screen.set_palette("palette");
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	// Devices
	i8255_device &ppi(I8255(config, "ppi"));
	ppi.in_pa_callback().set_ioport("LINE0");
	ppi.out_pc_callback().set(FUNC(tvgame_state::speaker_w));
}

/* ROM definition */
ROM_START( tvgame )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD( "tvgame_all.ppi",  0x0000, 0x8000, CRC(b61d17bd) SHA1(bb92d6679370fc31d67cc334807d88d7288c7cfa) )

	ROM_REGION(0x1000, "proms", 0)
	ROM_LOAD( "video32.bin", 0x0000, 0x1000, CRC(516006e3) SHA1(942b31acccf833cd722cbcb739eb87673dc633d7) )
ROM_END

} // Anonymous namespace

//    YEAR  NAME    PARENT  COMPAT   MACHINE    INPUT    CLASS         INIT        COMPANY      FULLNAME              FLAGS
CONS( 2011, tvgame, 0,      0,       tvgame,    tvgame,  tvgame_state, empty_init, "Mr. Isizu", "Z80 TV Game System", MACHINE_SUPPORTS_SAVE )



tx0.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Raphael Nabet
/*
    TX-0

    Raphael Nabet, 2004
*/

#include "emu.h"
#include "tx0.h"

#include "imagedev/magtape.h"
#include "imagedev/papertape.h"

#include "screen.h"
#include "softlist_dev.h"


/*

TODO:
- due to no known software, it is unknown if this system is capable of running anything.

*/

void tx0_state::init_tx0()
{
	static const unsigned char fontdata6x8[tx0_fontdata_size] =
	{   /* ASCII characters */
		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,
		0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0xf8,0x50,0xf8,0x50,0x00,0x00,
		0x20,0x70,0xc0,0x70,0x18,0xf0,0x20,0x00,0x40,0xa4,0x48,0x10,0x20,0x48,0x94,0x08,
		0x60,0x90,0xa0,0x40,0xa8,0x90,0x68,0x00,0x10,0x20,0x40,0x00,0x00,0x00,0x00,0x00,
		0x20,0x40,0x40,0x40,0x40,0x40,0x20,0x00,0x10,0x08,0x08,0x08,0x08,0x08,0x10,0x00,
		0x20,0xa8,0x70,0xf8,0x70,0xa8,0x20,0x00,0x00,0x20,0x20,0xf8,0x20,0x20,0x00,0x00,
		0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x60,0x00,0x00,0x00,0xf8,0x00,0x00,0x00,0x00,
		0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,
		0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x10,0x30,0x10,0x10,0x10,0x10,0x10,0x00,
		0x70,0x88,0x08,0x10,0x20,0x40,0xf8,0x00,0x70,0x88,0x08,0x30,0x08,0x88,0x70,0x00,
		0x10,0x30,0x50,0x90,0xf8,0x10,0x10,0x00,0xf8,0x80,0xf0,0x08,0x08,0x88,0x70,0x00,
		0x70,0x80,0xf0,0x88,0x88,0x88,0x70,0x00,0xf8,0x08,0x08,0x10,0x20,0x20,0x20,0x00,
		0x70,0x88,0x88,0x70,0x88,0x88,0x70,0x00,0x70,0x88,0x88,0x88,0x78,0x08,0x70,0x00,
		0x00,0x00,0x30,0x30,0x00,0x30,0x30,0x00,0x00,0x00,0x30,0x30,0x00,0x30,0x30,0x60,
		0x10,0x20,0x40,0x80,0x40,0x20,0x10,0x00,0x00,0x00,0xf8,0x00,0xf8,0x00,0x00,0x00,
		0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x00,0x70,0x88,0x08,0x10,0x20,0x00,0x20,0x00,
		0x70,0x88,0xb8,0xa8,0xb8,0x80,0x70,0x00,0x70,0x88,0x88,0xf8,0x88,0x88,0x88,0x00,
		0xf0,0x88,0x88,0xf0,0x88,0x88,0xf0,0x00,0x70,0x88,0x80,0x80,0x80,0x88,0x70,0x00,
		0xf0,0x88,0x88,0x88,0x88,0x88,0xf0,0x00,0xf8,0x80,0x80,0xf0,0x80,0x80,0xf8,0x00,
		0xf8,0x80,0x80,0xf0,0x80,0x80,0x80,0x00,0x70,0x88,0x80,0x98,0x88,0x88,0x70,0x00,
		0x88,0x88,0x88,0xf8,0x88,0x88,0x88,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,
		0x08,0x08,0x08,0x08,0x88,0x88,0x70,0x00,0x88,0x90,0xa0,0xc0,0xa0,0x90,0x88,0x00,
		0x80,0x80,0x80,0x80,0x80,0x80,0xf8,0x00,0x88,0xd8,0xa8,0x88,0x88,0x88,0x88,0x00,
		0x88,0xc8,0xa8,0x98,0x88,0x88,0x88,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00,
		0xf0,0x88,0x88,0xf0,0x80,0x80,0x80,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x08,
		0xf0,0x88,0x88,0xf0,0x88,0x88,0x88,0x00,0x70,0x88,0x80,0x70,0x08,0x88,0x70,0x00,
		0xf8,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00,
		0x88,0x88,0x88,0x88,0x88,0x50,0x20,0x00,0x88,0x88,0x88,0x88,0xa8,0xd8,0x88,0x00,
		0x88,0x50,0x20,0x20,0x20,0x50,0x88,0x00,0x88,0x88,0x88,0x50,0x20,0x20,0x20,0x00,
		0xf8,0x08,0x10,0x20,0x40,0x80,0xf8,0x00,0x30,0x20,0x20,0x20,0x20,0x20,0x30,0x00,
		0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x30,0x10,0x10,0x10,0x10,0x10,0x30,0x00,
		0x20,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfc,
		0x40,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x08,0x78,0x88,0x78,0x00,
		0x80,0x80,0xf0,0x88,0x88,0x88,0xf0,0x00,0x00,0x00,0x70,0x88,0x80,0x80,0x78,0x00,
		0x08,0x08,0x78,0x88,0x88,0x88,0x78,0x00,0x00,0x00,0x70,0x88,0xf8,0x80,0x78,0x00,
		0x18,0x20,0x70,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x78,0x88,0x88,0x78,0x08,0x70,
		0x80,0x80,0xf0,0x88,0x88,0x88,0x88,0x00,0x20,0x00,0x20,0x20,0x20,0x20,0x20,0x00,
		0x20,0x00,0x20,0x20,0x20,0x20,0x20,0xc0,0x80,0x80,0x90,0xa0,0xe0,0x90,0x88,0x00,
		0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xf0,0xa8,0xa8,0xa8,0xa8,0x00,
		0x00,0x00,0xb0,0xc8,0x88,0x88,0x88,0x00,0x00,0x00,0x70,0x88,0x88,0x88,0x70,0x00,
		0x00,0x00,0xf0,0x88,0x88,0xf0,0x80,0x80,0x00,0x00,0x78,0x88,0x88,0x78,0x08,0x08,
		0x00,0x00,0xb0,0xc8,0x80,0x80,0x80,0x00,0x00,0x00,0x78,0x80,0x70,0x08,0xf0,0x00,
		0x20,0x20,0x70,0x20,0x20,0x20,0x18,0x00,0x00,0x00,0x88,0x88,0x88,0x98,0x68,0x00,
		0x00,0x00,0x88,0x88,0x88,0x50,0x20,0x00,0x00,0x00,0xa8,0xa8,0xa8,0xa8,0x50,0x00,
		0x00,0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00,0x00,0x88,0x88,0x88,0x78,0x08,0x70,
		0x00,0x00,0xf8,0x10,0x20,0x40,0xf8,0x00,0x08,0x10,0x10,0x20,0x10,0x10,0x08,0x00,
		0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x40,0x20,0x20,0x10,0x20,0x20,0x40,0x00,
		0x00,0x68,0xb0,0x00,0x00,0x00,0x00,0x00,0x20,0x50,0x20,0x50,0xa8,0x50,0x00,0x00,
	};

	/* set up our font */
	uint8_t *dst = memregion("gfx1")->base();

	memcpy(dst, fontdata6x8, tx0_fontdata_size);
}


void tx0_state::tx0_64kw_map(address_map &map)
{
	map(0x0000, 0xffff).ram();
}


void tx0_state::tx0_8kw_map(address_map &map)
{
	map(0x0000, 0x1fff).ram();
}


static INPUT_PORTS_START( tx0 )

	PORT_START("CSW")       /* 0: various tx0 operator control panel switches */
	PORT_BIT(tx0_control, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("control panel key") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(tx0_stop_cyc0, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("stop at cycle 0") PORT_CODE(KEYCODE_Q)
	PORT_BIT(tx0_stop_cyc1, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("stop at cycle 1") PORT_CODE(KEYCODE_W)
	PORT_BIT(tx0_gbl_cm_sel, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CM select") PORT_CODE(KEYCODE_E)
	PORT_BIT(tx0_stop, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("stop") PORT_CODE(KEYCODE_P)
	PORT_BIT(tx0_restart, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("restart") PORT_CODE(KEYCODE_O)
	PORT_BIT(tx0_read_in, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("read in") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(tx0_toggle_dn, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("edit next toggle switch register") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(tx0_toggle_up, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("edit previous toggle switch register") PORT_CODE(KEYCODE_UP)
	PORT_BIT(tx0_cm_sel, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TSS CM switch") PORT_CODE(KEYCODE_A)
	PORT_BIT(tx0_lr_sel, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TSS LR switch") PORT_CODE(KEYCODE_SLASH)

	PORT_START("MSW")       /* 1: operator control panel toggle switch register switches MS */
	PORT_BIT(    0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Toggle Switch Register Switch 0") PORT_CODE(KEYCODE_S)
	PORT_BIT(    0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Toggle Switch Register Switch 1") PORT_CODE(KEYCODE_D)

	PORT_START("LSW")       /* 2: operator control panel toggle switch register switches LS */
	PORT_BIT( 0100000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Toggle Switch Register Switch 2") PORT_CODE(KEYCODE_F)
	PORT_BIT( 0040000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Toggle Switch Register Switch 3") PORT_CODE(KEYCODE_G)
	PORT_BIT( 0020000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Toggle Switch Register Switch 4") PORT_CODE(KEYCODE_H)
	PORT_BIT( 0010000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Toggle Switch Register Switch 5") PORT_CODE(KEYCODE_J)
	PORT_BIT( 0004000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Toggle Switch Register Switch 6") PORT_CODE(KEYCODE_K)
	PORT_BIT( 0002000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Toggle Switch Register Switch 7") PORT_CODE(KEYCODE_L)
	PORT_BIT( 0001000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Toggle Switch Register Switch 8") PORT_CODE(KEYCODE_COLON)
	PORT_BIT( 0000400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Toggle Switch Register Switch 9") PORT_CODE(KEYCODE_Z)
	PORT_BIT( 0000200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Toggle Switch Register Switch 10") PORT_CODE(KEYCODE_X)
	PORT_BIT( 0000100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Toggle Switch Register Switch 11") PORT_CODE(KEYCODE_C)
	PORT_BIT( 0000040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Toggle Switch Register Switch 12") PORT_CODE(KEYCODE_V)
	PORT_BIT( 0000020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Toggle Switch Register Switch 13") PORT_CODE(KEYCODE_B)
	PORT_BIT( 0000010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Toggle Switch Register Switch 14") PORT_CODE(KEYCODE_N)
	PORT_BIT( 0000004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Toggle Switch Register Switch 15") PORT_CODE(KEYCODE_M)
	PORT_BIT( 0000002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Toggle Switch Register Switch 16") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT( 0000001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Toggle Switch Register Switch 17") PORT_CODE(KEYCODE_STOP)

	PORT_START("TWR.0")      /* 3: typewriter codes 00-17 */
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("| _") PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(Space)") PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("= :") PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("+ /") PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)

	PORT_START("TWR.1")      /* 4: typewriter codes 20-37 */
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(". )") PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(". (") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("- +") PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)

	PORT_START("TWR.2")      /* 5: typewriter codes 40-57 */
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab Key") PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)

	PORT_START("TWR.3")      /* 6: typewriter codes 60-77 */
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Upper case") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Lower Case") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)

INPUT_PORTS_END


static const gfx_layout fontlayout =
{
	6, 8,           /* 6*8 characters */
	tx0_charnum,    /* 96+xx characters */
	1,              /* 1 bit per pixel */
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* straightforward layout */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8 /* every char takes 8 consecutive bytes */
};


/*
    The static palette only includes the pens for the control panel and
    the typewriter, as the CRT palette is generated dynamically.

    The CRT palette defines various levels of intensity between white and
    black.  Grey levels follow an exponential law, so that decrementing the
    color index periodically will simulate the remanence of a cathode ray tube.
*/
static const uint8_t tx0_colors[] =
{
	0x00,0x00,0x00, /* black */
	0xFF,0xFF,0xFF, /* white */
	0x00,0xFF,0x00, /* green */
	0x00,0x40,0x00, /* dark green */
	0xFF,0x00,0x00, /* red */
	0x80,0x80,0x80  /* light gray */
};

static const uint8_t tx0_pens[] =
{
	pen_panel_bg, pen_panel_caption,    /* captions */
	pen_typewriter_bg, pen_black,       /* black typing in typewriter */
	pen_typewriter_bg, pen_red      /* red typing in typewriter */
};

static const uint8_t total_colors_needed = pen_crt_num_levels + sizeof(tx0_colors) / 3;

static GFXDECODE_START( gfx_tx0 )
	GFXDECODE_ENTRY( "gfx1", 0, fontlayout, pen_crt_num_levels + sizeof(tx0_colors) / 3, 3 )
GFXDECODE_END

/* Initialise the palette */
void tx0_state::tx0_palette(palette_device &palette) const
{
	/* rgb components for the two color emissions */
	const double r1 = .1, g1 = .1, b1 = .924, r2 = .7, g2 = .7, b2 = .076;
	/* half period in seconds for the two color emissions */
	const double half_period_1 = .05, half_period_2 = .20;
	/* refresh period in seconds */
	const double update_period = 1./refresh_rate;
	double decay_1, decay_2;
	double cur_level_1, cur_level_2;
#if 0
#ifdef MAME_DEBUG
	/* level at which we stop emulating the decay and say the pixel is black */
	double cut_level = .02;
#endif
#endif
	uint8_t i, r, g, b;

	/* initialize CRT palette */

	/* compute the decay factor per refresh frame */
	decay_1 = pow(.5, update_period / half_period_1);
	decay_2 = pow(.5, update_period / half_period_2);

	cur_level_1 = cur_level_2 = 255.;   /* start with maximum level */

	for (i=pen_crt_max_intensity; i>0; i--)
	{
		/* compute the current color */
		r = (int) ((r1*cur_level_1 + r2*cur_level_2) + .5);
		g = (int) ((g1*cur_level_1 + g2*cur_level_2) + .5);
		b = (int) ((b1*cur_level_1 + b2*cur_level_2) + .5);
		/* write color in palette */
		palette.set_indirect_color(i, rgb_t(r, g, b));
		/* apply decay for next iteration */
		cur_level_1 *= decay_1;
		cur_level_2 *= decay_2;
	}
#if 0
#ifdef MAME_DEBUG
	{
		int recommended_pen_crt_num_levels;
		if (decay_1 > decay_2)
			recommended_pen_crt_num_levels = ceil(log(cut_level)/log(decay_1))+1;
		else
			recommended_pen_crt_num_levels = ceil(log(cut_level)/log(decay_2))+1;
		if (recommended_pen_crt_num_levels != pen_crt_num_levels)
			osd_printf_debug("File %s line %d: recommended value for pen_crt_num_levels is %d\n", __FILE__, __LINE__, recommended_pen_crt_num_levels);
	}
	/*if ((cur_level_1 > 255.*cut_level) || (cur_level_2 > 255.*cut_level))
	    osd_printf_debug("File %s line %d: Please take higher value for pen_crt_num_levels or smaller value for decay\n", __FILE__, __LINE__);*/
#endif
#endif
	palette.set_indirect_color(0, rgb_t(0, 0, 0));

	/* load static palette */
	for ( i = 0; i < 6; i++ )
	{
		r = tx0_colors[i*3]; g = tx0_colors[i*3+1]; b = tx0_colors[i*3+2];
		palette.set_indirect_color(pen_crt_num_levels + i, rgb_t(r, g, b));
	}

	/* copy colortable to palette */
	for( i = 0; i < total_colors_needed; i++ )
		palette.set_pen_indirect(i, i);

	/* set up palette for text */
	for( i = 0; i < 6; i++ )
		palette.set_pen_indirect(total_colors_needed + i, tx0_pens[i]);
}



/*
    TX-0
*
    Raphael Nabet, 2004
*/



/* crt display timer */



enum
{
	PF_RWC = 040,
	PF_EOR = 020,
	PF_PC  = 010,
	PF_EOT = 004
};


void tx0_state::machine_reset()
{
	/* reset device state */
	m_tape_reader.rcl = m_tape_reader.rc = 0;
}


void tx0_state::tx0_machine_stop()
{
	/* the core will take care of freeing the timers, BUT we must set the variables
	to nullptr if we don't want to risk confusing the tape image init function */
	m_tape_reader.timer = m_tape_puncher.timer = m_typewriter.prt_timer = m_dis_timer = nullptr;
}


void tx0_state::machine_start()
{
	m_tape_reader.timer = timer_alloc(FUNC(tx0_state::reader_callback), this);
	m_tape_puncher.timer = timer_alloc(FUNC(tx0_state::puncher_callback), this);
	m_typewriter.prt_timer = timer_alloc(FUNC(tx0_state::prt_callback), this);
	m_dis_timer = timer_alloc(FUNC(tx0_state::dis_callback), this);

	machine().add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&tx0_state::tx0_machine_stop,this));
}


/*
    perforated tape handling
*/

class tx0_readtape_image_device : public paper_tape_reader_device
{
public:
	// construction/destruction
	tx0_readtape_image_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	// device_image_interface implementation
	virtual const char *file_extensions() const noexcept override { return "tap,rim"; }

	virtual std::pair<std::error_condition, std::string> call_load() override;
	virtual void call_unload() override;
	virtual const char *image_interface() const noexcept override { return "tx0_ptp"; }

protected:
	// device_t implementation
	virtual void device_start() override { }

private:
	required_device<tx0_state> m_tx0;
};

DEFINE_DEVICE_TYPE(TX0_READTAPE, tx0_readtape_image_device, "tx0_readtape_image", "TX0 Tape Reader")

tx0_readtape_image_device::tx0_readtape_image_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: paper_tape_reader_device(mconfig, TX0_READTAPE, tag, owner, clock)
	, m_tx0(*this, DEVICE_SELF_OWNER)
{
}

class tx0_punchtape_image_device : public paper_tape_punch_device
{
public:
	// construction/destruction
	tx0_punchtape_image_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	// device_image_interface implementation
	virtual const char *file_extensions() const noexcept override { return "tap,rim"; }

	virtual std::pair<std::error_condition, std::string> call_load() override;
	virtual std::pair<std::error_condition, std::string> call_create(int format_type, util::option_resolution *format_options) override;
	virtual void call_unload() override;

protected:
	// device_t implementation
	virtual void device_start() override { }

private:
	required_device<tx0_state> m_tx0;
};

DEFINE_DEVICE_TYPE(TX0_PUNCHTAPE, tx0_punchtape_image_device, "tx0_punchtape_image", "TX0 Tape Puncher")

tx0_punchtape_image_device::tx0_punchtape_image_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: paper_tape_punch_device(mconfig, TX0_PUNCHTAPE, tag, owner, clock)
	, m_tx0(*this, DEVICE_SELF_OWNER)
{
}


class tx0_printer_image_device :    public device_t,
									public device_image_interface
{
public:
	// construction/destruction
	tx0_printer_image_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	// device_image_interface implementation
	virtual bool is_readable()  const noexcept override { return false; }
	virtual bool is_writeable() const noexcept override { return true; }
	virtual bool is_creatable() const noexcept override { return true; }
	virtual bool is_reset_on_load() const noexcept override { return false; }
	virtual bool support_command_line_image_creation() const noexcept override { return true; }
	virtual const char *file_extensions() const noexcept override { return "typ"; }
	virtual const char *image_type_name() const noexcept override { return "printout"; }
	virtual const char *image_brief_type_name() const noexcept override { return "prin"; }

	virtual std::pair<std::error_condition, std::string> call_load() override;
	virtual void call_unload() override;

protected:
	// device_t implementation
	virtual void device_start() override { }

private:
	required_device<tx0_state> m_tx0;
};

DEFINE_DEVICE_TYPE(TX0_PRINTER, tx0_printer_image_device, "tx0_printer_image", "TX0 Typewriter")

tx0_printer_image_device::tx0_printer_image_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, TX0_PRINTER, tag, owner, clock)
	, device_image_interface(mconfig, *this)
	, m_tx0(*this, DEVICE_SELF_OWNER)
{
}

class tx0_magtape_image_device : public magtape_image_device
{
public:
	// construction/destruction
	tx0_magtape_image_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	// device_image_interface implementation
	virtual const char *file_extensions() const noexcept override { return "tap"; }

	virtual std::pair<std::error_condition, std::string> call_load() override;
	virtual void call_unload() override;

protected:
	// device_t implementation
	virtual void device_start() override ATTR_COLD;

private:
	required_device<tx0_state> m_tx0;
};

DEFINE_DEVICE_TYPE(TX0_MAGTAPE, tx0_magtape_image_device, "tx0_magtape_image", "TX0 Magnetic Tape")

tx0_magtape_image_device::tx0_magtape_image_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: magtape_image_device(mconfig, TX0_MAGTAPE, tag, owner, clock)
	, m_tx0(*this, DEVICE_SELF_OWNER)
{
}

/*
    Open a perforated tape image

    unit 0 is reader (read-only), unit 1 is puncher (write-only)
*/
std::pair<std::error_condition, std::string> tx0_readtape_image_device::call_load()
{
	if (m_tx0)
	{
		/* reader unit */
		m_tx0->m_tape_reader.fd = this;

		/* start motor */
		m_tx0->m_tape_reader.motor_on = 1;

		/* restart reader IO when necessary */
		/* note that this function may be called before tx0_init_machine, therefore
		before tape_reader.timer is allocated.  It does not matter, as the clutch is never
		down at power-up, but we must not call timer_enable with a nullptr parameter! */

		if (m_tx0->m_tape_reader.timer)
		{
			if (m_tx0->m_tape_reader.motor_on && m_tx0->m_tape_reader.rcl)
			{
				/* delay is approximately 1/400s */
				m_tx0->m_tape_reader.timer->adjust(attotime::from_usec(2500));
			}
			else
			{
				m_tx0->m_tape_reader.timer->enable(0);
			}
		}
	}

	return std::make_pair(std::error_condition(), std::string());
}

void tx0_readtape_image_device::call_unload()
{
	/* reader unit */
	if (m_tx0)
	{
		m_tx0->m_tape_reader.fd = nullptr;

		/* stop motor */
		m_tx0->m_tape_reader.motor_on = 0;

		if (m_tx0->m_tape_reader.timer)
			m_tx0->m_tape_reader.timer->enable(0);
	}
}

/*
    Read a byte from perforated tape
*/
int tx0_state::tape_read(uint8_t *reply)
{
	if (m_tape_reader.fd && (m_tape_reader.fd->fread(reply, 1) == 1))
		return 0;   /* unit OK */
	else
		return 1;   /* unit not ready */
}

/*
    Write a byte to perforated tape
*/
void tx0_state::tape_write(uint8_t data)
{
	if (m_tape_puncher.fd)
	{
		data |= 0200;
		m_tape_puncher.fd->fwrite(& data, 1);
	}
}

/*
    common code for tape read commands (R1C, R3C, and read-in mode)
*/
void tx0_state::begin_tape_read(int binary)
{
	m_tape_reader.rcl = 1;
	m_tape_reader.rc = (binary) ? 1 : 3;

	/* set up delay if tape is advancing */
	if (m_tape_reader.motor_on && m_tape_reader.rcl)
	{
		/* delay is approximately 1/400s */
		m_tape_reader.timer->adjust(attotime::from_usec(2500));
	}
	else
	{
		m_tape_reader.timer->enable(0);
	}
}


/*
    timer callback to simulate reader IO
*/
TIMER_CALLBACK_MEMBER(tx0_state::reader_callback)
{
	int not_ready;
	uint8_t data;
	int ac;

	if (m_tape_reader.rc)
	{
		not_ready = tape_read( & data);
		if (not_ready)
		{
			m_tape_reader.motor_on = 0; /* let us stop the motor */
		}
		else
		{
			if (data & 0100)
			{
				/* read current AC */
				ac = m_maincpu->state_int(TX0_AC);
				/* cycle right */
				ac = (ac >> 1) | ((ac & 1) << 17);
				/* shuffle and insert data into AC */
				ac = (ac /*& 0333333*/) | ((data & 001) << 17) | ((data & 002) << 13) | ((data & 004) << 9) | ((data & 010) << 5) | ((data & 020) << 1) | ((data & 040) >> 3);
				/* write modified AC */
				m_maincpu->set_state_int(TX0_AC, ac);

				m_tape_reader.rc = (m_tape_reader.rc+1) & 3;

				if (m_tape_reader.rc == 0)
				{   /* IO complete */
					m_tape_reader.rcl = 0;
					m_maincpu->set_state_int(TX0_IOS,1);
				}
			}
		}
	}

	if (m_tape_reader.motor_on && m_tape_reader.rcl)
		/* delay is approximately 1/400s */
		m_tape_reader.timer->adjust(attotime::from_usec(2500));
	else
		m_tape_reader.timer->enable(0);
}

/*
    timer callback to generate punch completion pulse
*/
std::pair<std::error_condition, std::string> tx0_punchtape_image_device::call_load()
{
	/* punch unit */
	if (m_tx0)
		m_tx0->m_tape_puncher.fd = this;

	fseek(0, SEEK_END);

	return std::make_pair(std::error_condition(), std::string());
}

std::pair<std::error_condition, std::string> tx0_punchtape_image_device::call_create(int format_type, util::option_resolution *format_options)
{
	return call_load();
}

void tx0_punchtape_image_device::call_unload()
{
	/* punch unit */
	if (m_tx0)
		m_tx0->m_tape_puncher.fd = nullptr;
}

TIMER_CALLBACK_MEMBER(tx0_state::puncher_callback)
{
	m_maincpu->set_state_int(TX0_IOS,1);
}

/*
    Initiate read of a 6-bit word from tape
*/
void tx0_state::tx0_io_r1l(int state)
{
	begin_tape_read( 0);
}

/*
    Initiate read of a 18-bit word from tape (used in read-in mode)
*/
void tx0_state::tx0_io_r3l(int state)
{
	begin_tape_read(1);
}

/*
    Write a 7-bit word to tape (7th bit clear)
*/
void tx0_state::tx0_io_p6h(int state)
{
	int ac;

	/* read current AC */
	ac = m_maincpu->state_int(TX0_AC);
	/* shuffle and punch 6-bit word */
	tape_write(((ac & 0100000) >> 15) | ((ac & 0010000) >> 11) | ((ac & 0001000) >> 7) | ((ac & 0000100) >> 3) | ((ac & 0000010) << 1) | ((ac & 0000001) << 5));

	m_tape_puncher.timer->adjust(attotime::from_usec(15800));
}

/*
    Write a 7-bit word to tape (7th bit set)
*/
void tx0_state::tx0_io_p7h(int state)
{
	int ac;

	/* read current AC */
	ac = m_maincpu->state_int(TX0_AC);
	/* shuffle and punch 6-bit word */
	tape_write(((ac & 0100000) >> 15) | ((ac & 0010000) >> 11) | ((ac & 0001000) >> 7) | ((ac & 0000100) >> 3) | ((ac & 0000010) << 1) | ((ac & 0000001) << 5) | 0100);

	m_tape_puncher.timer->adjust(attotime::from_usec(15800));
}


/*
    Typewriter handling

    The alphanumeric on-line typewriter is a standard device on tx-0: it can
    both handle keyboard input and print output text.
*/

/*
    Open a file for typewriter output
*/
std::pair<std::error_condition, std::string> tx0_printer_image_device::call_load()
{
	/* open file */
	if (m_tx0)
		m_tx0->m_typewriter.fd = this;

	fseek(0, SEEK_END);

	return std::make_pair(std::error_condition(), std::string());
}

void tx0_printer_image_device::call_unload()
{
	if (m_tx0)
		m_tx0->m_typewriter.fd = nullptr;
}

/*
    Write a character to typewriter
*/
void tx0_state::typewriter_out(uint8_t data)
{
	tx0_typewriter_drawchar(data);
	if (m_typewriter.fd)
		m_typewriter.fd->fwrite(&data, 1);
}

/*
    timer callback to generate typewriter completion pulse
*/
TIMER_CALLBACK_MEMBER(tx0_state::prt_callback)
{
	m_maincpu->io_complete();
}

/*
    prt io callback
*/
void tx0_state::tx0_io_prt(int state)
{
	int ac;
	int ch;

	/* read current AC */
	ac = m_maincpu->state_int(TX0_AC);
	/* shuffle and print 6-bit word */
	ch = bitswap<6>(ac, 15, 12, 9, 6, 3, 0);
	typewriter_out(ch);

	m_typewriter.prt_timer->adjust(attotime::from_msec(100));
}


/*
    timer callback to generate crt completion pulse
*/
TIMER_CALLBACK_MEMBER(tx0_state::dis_callback)
{
	m_maincpu->io_complete();
}

/*
    Plot one point on crt
*/
void tx0_state::tx0_io_dis(int state)
{
	int ac;
	int x;
	int y;

	ac = m_maincpu->state_int(TX0_AC);
	x = ac >> 9;
	y = ac & 0777;
	tx0_plot(x, y);

	m_dis_timer->adjust(attotime::from_usec(50));
}


/*
    Magtape support

    Magtape format:

    7-track tape, 6-bit data, 1-bit parity


*/

void tx0_state::schedule_select()
{
	attotime delay = attotime::zero;

	switch (m_magtape.command)
	{
	case 0: /* backspace */
		delay = attotime::from_usec(4600);
		break;
	case 1: /* read */
		delay = attotime::from_usec(8600);
		break;
	case 2: /* rewind */
		delay = attotime::from_usec(12000);
		break;
	case 3: /* write */
		delay = attotime::from_usec(4600);
		break;
	}
	m_magtape.timer->adjust(delay);
}

void tx0_state::schedule_unselect()
{
	attotime delay = attotime::zero;

	switch (m_magtape.command)
	{
	case 0: /* backspace */
		delay = attotime::from_usec(5750);
		break;
	case 1: /* read */
		delay = attotime::from_usec(1750);
		break;
	case 2: /* rewind */
		delay = attotime::from_usec(0);
		break;
	case 3: /* write */
		delay = attotime::from_usec(5750);
		break;
	}
	m_magtape.timer->adjust(delay);
}

void tx0_magtape_image_device::device_start()
{
	m_tx0->m_magtape.img = this;
}

/*
    Open a magnetic tape image
*/
std::pair<std::error_condition, std::string> tx0_magtape_image_device::call_load()
{
	if (m_tx0)
	{
		m_tx0->m_magtape.img = this;

		m_tx0->m_magtape.irg_pos = tx0_state::MTIRGP_END;

		/* restart IO when necessary */
		/* note that this function may be called before tx0_init_machine, therefore
		before magtape.timer is allocated.  We must not call timer_enable with a
		nullptr parameter! */
		if (m_tx0->m_magtape.timer)
		{
			if (m_tx0->m_magtape.state == tx0_state::MTS_SELECTING)
				m_tx0->schedule_select();
		}
	}

	return std::make_pair(std::error_condition(), std::string());
}

void tx0_magtape_image_device::call_unload()
{
	if (m_tx0)
	{
		m_tx0->m_magtape.img = nullptr;

		if (m_tx0->m_magtape.timer)
		{
			if (m_tx0->m_magtape.state == tx0_state::MTS_SELECTING)
				/* I/O has not actually started, we can cancel the selection */
				m_tx0->m_tape_reader.timer->enable(0);
			if ((m_tx0->m_magtape.state == tx0_state::MTS_SELECTED) || ((m_tx0->m_magtape.state == tx0_state::MTS_SELECTING) && (m_tx0->m_magtape.command == 2)))
			{   /* unit has become unavailable */
				m_tx0->m_magtape.state = tx0_state::MTS_UNSELECTING;
				m_tx0->m_maincpu->set_state_int(TX0_PF, m_tx0->m_maincpu->state_int(TX0_PF) | PF_RWC);
				m_tx0->schedule_unselect();
			}
		}
	}
}

void tx0_state::magtape_callback()
{
	uint8_t buf = 0;
	int lr;

	switch (m_magtape.state)
	{
	case MTS_UNSELECTING:
		m_magtape.state = MTS_UNSELECTED;
		[[fallthrough]];
	case MTS_UNSELECTED:
		if (m_magtape.sel_pending)
		{
			int mar;

			mar = m_maincpu->state_int(TX0_MAR);

			if ((mar & 03) != 1)
			{   /* unimplemented device: remain in unselected state and set rwc
			    flag? */
				m_maincpu->set_state_int(TX0_PF, m_maincpu->state_int(TX0_PF) | PF_RWC);
			}
			else
			{
				m_magtape.state = MTS_SELECTING;

				m_magtape.command = (mar & 014 >> 2);

				m_magtape.binary_flag = (mar & 020 >> 4);

				if (m_magtape.img)
					schedule_select();
			}

			m_magtape.sel_pending = false;
			m_maincpu->io_complete();
		}
		break;

	case MTS_SELECTING:
		m_magtape.state = MTS_SELECTED;
		switch (m_magtape.command)
		{
		case 0: /* backspace */
			m_magtape.long_parity = 0177;
			m_magtape.u.backspace_state = MTBSS_STATE0;
			break;
		case 1: /* read */
			m_magtape.long_parity = 0177;
			m_magtape.u.read.state = MTRDS_STATE0;
			break;
		case 2: /* rewind */
			break;
		case 3: /* write */
			m_magtape.long_parity = 0177;
			m_magtape.u.write.state = MTWTS_STATE0;
			switch (m_magtape.irg_pos)
			{
			case MTIRGP_START:
				m_magtape.u.write.counter = 150;
				break;
			case MTIRGP_ENDMINUS1:
				m_magtape.u.write.counter = 1;
				break;
			case MTIRGP_END:
				m_magtape.u.write.counter = 0;
				break;
			}
			break;
		}
		[[fallthrough]];
	case MTS_SELECTED:
		switch (m_magtape.command)
		{
		case 0: /* backspace */
			if (m_magtape.img->ftell() == 0)
			{   /* tape at ldp */
				m_magtape.state = MTS_UNSELECTING;
				m_maincpu->set_state_int(TX0_PF, m_maincpu->state_int(TX0_PF) | PF_RWC);
				schedule_unselect();
			}
			else if (m_magtape.img->fseek( -1, SEEK_CUR))
			{   /* eject tape */
				m_magtape.img->unload();
			}
			else if (m_magtape.img->fread(&buf, 1) != 1)
			{   /* eject tape */
				m_magtape.img->unload();
			}
			else if (m_magtape.img->fseek( -1, SEEK_CUR))
			{   /* eject tape */
				m_magtape.img->unload();
			}
			else
			{
				buf &= 0x7f;    /* 7-bit tape, ignore 8th bit */
				m_magtape.long_parity ^= buf;
				switch (m_magtape.u.backspace_state)
				{
				case MTBSS_STATE0:
					/* STATE0 -> initial interrecord gap, longitudinal parity;
					if longitudinal parity was all 0s, gap between longitudinal
					parity and data, first byte of data */
					if (buf != 0)
						m_magtape.u.backspace_state = MTBSS_STATE1;
					break;
				case MTBSS_STATE1:
					/* STATE1 -> first byte of gap between longitudinal parity and
					data, second byte of data */
					if (buf == 0)
						m_magtape.u.backspace_state = MTBSS_STATE2;
					else
						m_magtape.u.backspace_state = MTBSS_STATE5;
					break;
				case MTBSS_STATE2:
					/* STATE2 -> second byte of gap between longitudinal parity and
					data */
					if (buf == 0)
						m_magtape.u.backspace_state = MTBSS_STATE3;
					else
					{
						logerror("tape seems to be corrupt\n");
						/* eject tape */
						m_magtape.img->unload();
					}
					break;
				case MTBSS_STATE3:
					/* STATE3 -> third byte of gap between longitudinal parity and
					data */
					if (buf == 0)
						m_magtape.u.backspace_state = MTBSS_STATE4;
					else
					{
						logerror("tape seems to be corrupt\n");
						/* eject tape */
						m_magtape.img->unload();
					}
					break;
				case MTBSS_STATE4:
					/* STATE4 -> first byte of data word, first byte of
					interrecord gap after data */
					if (buf == 0)
					{
						if (m_magtape.long_parity)
							logerror("invalid longitudinal parity\n");
						/* set EOR and unselect... */
						m_magtape.state = MTS_UNSELECTING;
						m_maincpu->set_state_int(TX0_PF, m_maincpu->state_int(TX0_PF) | PF_EOR);
						schedule_unselect();
						m_magtape.irg_pos = MTIRGP_ENDMINUS1;
					}
					else
						m_magtape.u.backspace_state = MTBSS_STATE5;
					break;
				case MTBSS_STATE5:
					/* STATE5 -> second byte of data word */
					if (buf == 0)
					{
						logerror("tape seems to be corrupt\n");
						/* eject tape */
						m_magtape.img->unload();
					}
					else
						m_magtape.u.backspace_state = MTBSS_STATE6;
					break;
				case MTBSS_STATE6:
					/* STATE6 -> third byte of data word */
					if (buf == 0)
					{
						logerror("tape seems to be corrupt\n");
						/* eject tape */
						m_magtape.img->unload();
					}
					else
						m_magtape.u.backspace_state = MTBSS_STATE6;
					break;
				}
				if (m_magtape.state != MTS_UNSELECTING)
					m_magtape.timer->adjust(attotime::from_usec(66));
			}
			break;

		case 1: /* read */
			if (m_magtape.img->fread(&buf, 1) != 1)
			{   /* I/O error or EOF? */
				/* The MAME fileio layer makes it very hard to make the
				difference...  MAME seems to assume that I/O errors never
				happen, whereas it is really easy to cause one by
				deconnecting an external drive the image is located on!!! */
				uint64_t offs;
				offs = m_magtape.img->ftell();
				if (m_magtape.img->fseek( 0, SEEK_END) || (offs != m_magtape.img->ftell()))
				{   /* I/O error */
					/* eject tape */
					m_magtape.img->unload();
				}
				else
				{   /* end of tape -> ??? */
					/* maybe we run past end of tape, so that tape is ejected from
					upper reel and unit becomes unavailable??? */
					/*m_magtape.img->unload();*/
					/* Or do we stop at EOT mark??? */
					m_magtape.state = MTS_UNSELECTING;
					m_maincpu->set_state_int(TX0_PF, m_maincpu->state_int(TX0_PF) | PF_EOT);
					schedule_unselect();
				}
			}
			else
			{
				buf &= 0x7f;    /* 7-bit tape, ignore 8th bit */
				m_magtape.long_parity ^= buf;
				switch (m_magtape.u.read.state)
				{
				case MTRDS_STATE0:
					/* STATE0 -> interrecord blank or first byte of data */
					if (buf != 0)
					{
						if (m_magtape.cpy_pending)
						{   /* read command */
							m_magtape.u.read.space_flag = false;
							m_maincpu->set_state_int(TX0_IOS,1);
							m_maincpu->set_state_int(TX0_LR, ((m_maincpu->state_int(TX0_LR) >> 1) & 0333333)
														| ((buf & 040) << 12) | ((buf & 020) << 10) | ((buf & 010) << 8) | ((buf & 004) << 6) | ((buf & 002) << 4) | ((buf & 001) << 2));
							/* check parity */
							if (! (((buf ^ (buf >> 1) ^ (buf >> 2) ^ (buf >> 3) ^ (buf >> 4) ^ (buf >> 5) ^ (buf >> 6) ^ (buf >> 7)) & 1) ^ m_magtape.binary_flag))
								m_maincpu->set_state_int(TX0_PF, m_maincpu->state_int(TX0_PF) | PF_PC);
						}
						else
						{   /* space command */
							m_magtape.u.read.space_flag = true;
						}
						m_magtape.u.read.state = MTRDS_STATE1;
					}
					break;
				case MTRDS_STATE1:
					/* STATE1 -> second byte of data word */
					if (buf == 0)
					{
						logerror("tape seems to be corrupt\n");
						/* eject tape */
						m_magtape.img->unload();
					}
					if (!m_magtape.u.read.space_flag)
					{
						m_maincpu->set_state_int(TX0_LR, ((m_maincpu->state_int(TX0_LR) >> 1) & 0333333)
													| ((buf & 040) << 12) | ((buf & 020) << 10) | ((buf & 010) << 8) | ((buf & 004) << 6) | ((buf & 002) << 4) | ((buf & 001) << 2));
						/* check parity */
						if (! (((buf ^ (buf >> 1) ^ (buf >> 2) ^ (buf >> 3) ^ (buf >> 4) ^ (buf >> 5) ^ (buf >> 6) ^ (buf >> 7)) & 1) ^ m_magtape.binary_flag))
							m_maincpu->set_state_int(TX0_PF, m_maincpu->state_int(TX0_PF) | PF_PC);
					}
					m_magtape.u.read.state = MTRDS_STATE2;
					break;
				case MTRDS_STATE2:
					/* STATE2 -> third byte of data word */
					if (buf == 0)
					{
						logerror("tape seems to be corrupt\n");
						/* eject tape */
						m_magtape.img->unload();
					}
					if (!m_magtape.u.read.space_flag)
					{
						m_maincpu->set_state_int(TX0_LR, ((m_maincpu->state_int(TX0_LR) >> 1) & 0333333)
													| ((buf & 040) << 12) | ((buf & 020) << 10) | ((buf & 010) << 8) | ((buf & 004) << 6) | ((buf & 002) << 4) | ((buf & 001) << 2));
						/* check parity */
						if (! (((buf ^ (buf >> 1) ^ (buf >> 2) ^ (buf >> 3) ^ (buf >> 4) ^ (buf >> 5) ^ (buf >> 6) ^ (buf >> 7)) & 1) ^ m_magtape.binary_flag))
							m_maincpu->set_state_int(TX0_PF, m_maincpu->state_int(TX0_PF) | PF_PC);
						/* synchronize with cpy instruction */
						if (m_magtape.cpy_pending)
							m_maincpu->set_state_int(TX0_IOS,1);
						else
							m_maincpu->set_state_int(TX0_PF, m_maincpu->state_int(TX0_PF) | PF_RWC);
					}
					m_magtape.u.read.state = MTRDS_STATE3;
					break;
				case MTRDS_STATE3:
					/* STATE3 -> first byte of new word of data, or first byte
					of gap between data and longitudinal parity */
					if (buf != 0)
					{
						m_magtape.u.read.state = MTRDS_STATE1;
						if (!m_magtape.u.read.space_flag)
						{
							m_maincpu->set_state_int(TX0_LR, ((m_maincpu->state_int(TX0_LR) >> 1) & 0333333)
														| ((buf & 040) << 12) | ((buf & 020) << 10) | ((buf & 010) << 8) | ((buf & 004) << 6) | ((buf & 002) << 4) | ((buf & 001) << 2));
							/* check parity */
							if (! (((buf ^ (buf >> 1) ^ (buf >> 2) ^ (buf >> 3) ^ (buf >> 4) ^ (buf >> 5) ^ (buf >> 6) ^ (buf >> 7)) & 1) ^ m_magtape.binary_flag))
								m_maincpu->set_state_int(TX0_PF, m_maincpu->state_int(TX0_PF) | PF_PC);
						}
					}
					else
						m_magtape.u.read.state = MTRDS_STATE4;
					break;
				case MTRDS_STATE4:
					/* STATE4 -> second byte of gap between data and
					longitudinal parity */
					if (buf != 0)
					{
						logerror("tape seems to be corrupt\n");
						/* eject tape */
						m_magtape.img->unload();
					}
					else
						m_magtape.u.read.state = MTRDS_STATE5;
					break;

				case MTRDS_STATE5:
					/* STATE5 -> third byte of gap between data and
					longitudinal parity */
					if (buf != 0)
					{
						logerror("tape seems to be corrupt\n");
						/* eject tape */
						m_magtape.img->unload();
					}
					else
						m_magtape.u.read.state = MTRDS_STATE6;
					break;

				case MTRDS_STATE6:
					/* STATE6 -> longitudinal parity */
					/* check parity */
					if (m_magtape.long_parity)
					{
						logerror("invalid longitudinal parity\n");
						/* no idea if the original tx-0 magtape controller
						checks parity, but can't harm if we do */
						m_maincpu->set_state_int(TX0_PF, m_maincpu->state_int(TX0_PF) | PF_PC);
					}
					/* set EOR and unselect... */
					m_magtape.state = MTS_UNSELECTING;
					m_maincpu->set_state_int(TX0_PF, m_maincpu->state_int(TX0_PF) | PF_EOR);
					schedule_unselect();
					m_magtape.irg_pos = MTIRGP_START;
					break;
				}
				if (m_magtape.state != MTS_UNSELECTING)
					m_magtape.timer->adjust(attotime::from_usec(66));
			}
			break;

		case 2: /* rewind */
			m_magtape.state = MTS_UNSELECTING;
			/* we rewind at 10*read speed (I don't know the real value) */
			m_magtape.timer->adjust((attotime::from_nsec(6600) * m_magtape.img->ftell()));
			//schedule_unselect(state);
			m_magtape.img->fseek( 0, SEEK_END);
			m_magtape.irg_pos = MTIRGP_END;
			break;

		case 3: /* write */
			switch (m_magtape.u.write.state)
			{
			case MTWTS_STATE0:
				if (m_magtape.u.write.counter != 0)
				{
					m_magtape.u.write.counter--;
					buf = 0;
					break;
				}
				else
				{
					m_magtape.u.write.state = MTWTS_STATE1;
				}
				[[fallthrough]];
			case MTWTS_STATE1:
				if (m_magtape.u.write.counter)
				{
					m_magtape.u.write.counter--;
					lr = m_maincpu->state_int(TX0_LR);
					buf = ((lr >> 10) & 040) | ((lr >> 8) & 020) | ((lr >> 6) & 010) | ((lr >> 4) & 004) | ((lr >> 2) & 002) | (lr & 001);
					buf |= ((buf << 1) ^ (buf << 2) ^ (buf << 3) ^ (buf << 4) ^ (buf << 5) ^ (buf << 6) ^ ((!m_magtape.binary_flag) << 6)) & 0100;
					m_maincpu->set_state_int(TX0_LR, lr >> 1);
				}
				else
				{
					if (m_magtape.cpy_pending)
					{
						m_maincpu->set_state_int(TX0_IOS,1);
						lr = m_maincpu->state_int(TX0_LR);
						buf = ((lr >> 10) & 040) | ((lr >> 8) & 020) | ((lr >> 6) & 010) | ((lr >> 4) & 004) | ((lr >> 2) & 002) | (lr & 001);
						buf |= ((buf << 1) ^ (buf << 2) ^ (buf << 3) ^ (buf << 4) ^ (buf << 5) ^ (buf << 6) ^ ((!m_magtape.binary_flag) << 6)) & 0100;
						m_maincpu->set_state_int(TX0_LR, lr >> 1);
						m_magtape.u.write.counter = 2;
						break;
					}
					else
					{
						m_magtape.u.write.state = MTWTS_STATE2;
						m_magtape.u.write.counter = 3;
					}
				}
				[[fallthrough]];
			case MTWTS_STATE2:
				if (m_magtape.u.write.counter != 0)
				{
					m_magtape.u.write.counter--;
					buf = 0;
					break;
				}
				else
				{
					buf = m_magtape.long_parity;
					m_magtape.state = (state_t)MTWTS_STATE3;
					m_magtape.u.write.counter = 150;
				}
				break;

			case MTWTS_STATE3:
				if (m_magtape.u.write.counter != 0)
				{
					m_magtape.u.write.counter--;
					buf = 0;
					break;
				}
				else
				{
					m_magtape.state = MTS_UNSELECTING;
					schedule_unselect();
					m_magtape.irg_pos = MTIRGP_END;
				}
				break;
			}
			if (m_magtape.state != MTS_UNSELECTING)
			{   /* write data word */
				m_magtape.long_parity ^= buf;
				if (m_magtape.img->fwrite(&buf, 1) != 1)
				{   /* I/O error */
					/* eject tape */
					m_magtape.img->unload();
				}
				else
					m_magtape.timer->adjust(attotime::from_usec(66));
			}
			break;
		}
		break;
	}
}

void tx0_state::tx0_sel(int state)
{
	m_magtape.sel_pending = true;

	if (m_magtape.state == MTS_UNSELECTED)
	{
		if (0)
			magtape_callback();
		m_magtape.timer->adjust(attotime::zero);
	}
}

void tx0_state::tx0_io_cpy(int state)
{
	switch (m_magtape.state)
	{
	case MTS_UNSELECTED:
	case MTS_UNSELECTING:
		/* ignore instruction and set rwc flag? */
		m_maincpu->io_complete();
		break;

	case MTS_SELECTING:
	case MTS_SELECTED:
		switch (m_magtape.command)
		{
		case 0: /* backspace */
		case 2: /* rewind */
			/* ignore instruction and set rwc flag? */
			m_maincpu->io_complete();
			break;
		case 1: /* read */
		case 3: /* write */
			m_magtape.cpy_pending = true;
			break;
		}
		break;
	}
}


/*
    callback which is called when reset line is pulsed

    IO devices should reset
*/
void tx0_state::tx0_io_reset_callback(int state)
{
	m_tape_reader.rcl = m_tape_reader.rc = 0;
	if (m_tape_reader.timer)
		m_tape_reader.timer->enable(0);

	if (m_tape_puncher.timer)
		m_tape_puncher.timer->enable(0);

	if (m_typewriter.prt_timer)
		m_typewriter.prt_timer->enable(0);

	if (m_dis_timer)
		m_dis_timer->enable(0);
}


/*
    typewriter keyboard handler
*/
void tx0_state::tx0_keyboard()
{
	int i;
	int j;

	int typewriter_keys[4];

	int typewriter_transitions;
	int charcode, lr;

	for (i=0; i<4; i++)
	{
		typewriter_keys[i] = m_twr[i]->read();
	}

	for (i=0; i<4; i++)
	{
		typewriter_transitions = typewriter_keys[i] & (~ m_old_typewriter_keys[i]);
		if (typewriter_transitions)
		{
			for (j=0; (((typewriter_transitions >> j) & 1) == 0) /*&& (j<16)*/; j++)
				;
			charcode = (i << 4) + j;
			/* shuffle and insert data into LR */
			lr = (1 << 17) | (charcode << 11) | m_maincpu->state_int(TX0_LR);
			/* write modified LR */
			m_maincpu->set_state_int(TX0_LR, lr);
			tx0_typewriter_drawchar(charcode); /* we want to echo input */
			break;
		}
	}

	for (i=0; i<4; i++)
		m_old_typewriter_keys[i] = typewriter_keys[i];
}

/*
    Not a real interrupt - just handle keyboard input
*/
INTERRUPT_GEN_MEMBER(tx0_state::tx0_interrupt)
{
	int control_keys;
	int tsr_keys;

	int control_transitions;
	int tsr_transitions;


	/* read new state of control keys */
	control_keys = m_csw->read();

	if (control_keys & tx0_control)
	{
		/* compute transitions */
		control_transitions = control_keys & (~ m_old_control_keys);

		if (control_transitions & tx0_stop_cyc0)
		{
			m_maincpu->set_state_int(TX0_STOP_CYC0, !m_maincpu->state_int(TX0_STOP_CYC0));
		}
		if (control_transitions & tx0_stop_cyc1)
		{
			m_maincpu->set_state_int(TX0_STOP_CYC1, !m_maincpu->state_int(TX0_STOP_CYC1));
		}
		if (control_transitions & tx0_gbl_cm_sel)
		{
			m_maincpu->set_state_int(TX0_GBL_CM_SEL, !m_maincpu->state_int(TX0_GBL_CM_SEL));
		}
		if (control_transitions & tx0_stop)
		{
			m_maincpu->set_state_int(TX0_RUN, (uint64_t)0);
			m_maincpu->set_state_int(TX0_RIM, (uint64_t)0);
		}
		if (control_transitions & tx0_restart)
		{
			m_maincpu->set_state_int(TX0_RUN, 1);
			m_maincpu->set_state_int(TX0_RIM, (uint64_t)0);
		}
		if (control_transitions & tx0_read_in)
		{   /* set cpu to read instructions from perforated tape */
			m_maincpu->pulse_reset();
			m_maincpu->set_state_int(TX0_RUN, (uint64_t)0);
			m_maincpu->set_state_int(TX0_RIM, 1);
		}
		if (control_transitions & tx0_toggle_dn)
		{
			m_tsr_index++;
			if (m_tsr_index == 18)
				m_tsr_index = 0;
		}
		if (control_transitions & tx0_toggle_up)
		{
			m_tsr_index--;
			if (m_tsr_index == -1)
				m_tsr_index = 17;
		}
		if (control_transitions & tx0_cm_sel)
		{
			if (m_tsr_index >= 2)
			{
				uint32_t cm_sel = (uint32_t) m_maincpu->state_int(TX0_CM_SEL);
				m_maincpu->set_state_int(TX0_CM_SEL, cm_sel ^ (1 << (m_tsr_index - 2)));
			}
		}
		if (control_transitions & tx0_lr_sel)
		{
			if (m_tsr_index >= 2)
			{
				uint32_t lr_sel = (uint32_t) m_maincpu->state_int(TX0_LR_SEL);
				m_maincpu->set_state_int(TX0_LR_SEL, (lr_sel ^ (1 << (m_tsr_index - 2))));
			}
		}

		/* remember new state of control keys */
		m_old_control_keys = control_keys;


		/* handle toggle switch register keys */
		tsr_keys = (ioport("MSW")->read() << 16) | ioport("LSW")->read();

		/* compute transitions */
		tsr_transitions = tsr_keys & (~ m_old_tsr_keys);

		/* update toggle switch register */
		if (tsr_transitions)
			m_maincpu->set_state_int(TX0_TBR+m_tsr_index, m_maincpu->state_int(TX0_TBR+m_tsr_index) ^ tsr_transitions);

		/* remember new state of toggle switch register keys */
		m_old_tsr_keys = tsr_keys;
	}
	else
	{
		m_old_control_keys = 0;
		m_old_tsr_keys = 0;

		tx0_keyboard();
	}
}

void tx0_state::tx0_64kw(machine_config &config)
{
	/* basic machine hardware */
	/* TX0 CPU @ approx. 167 kHz (no master clock, but the memory cycle time is approximately 6usec) */
	TX0_64KW(config, m_maincpu, 166667);
	m_maincpu->cpy().set(FUNC(tx0_state::tx0_io_cpy));
	m_maincpu->r1l().set(FUNC(tx0_state::tx0_io_r1l));
	m_maincpu->dis().set(FUNC(tx0_state::tx0_io_dis));
	m_maincpu->r3l().set(FUNC(tx0_state::tx0_io_r3l));
	m_maincpu->prt().set(FUNC(tx0_state::tx0_io_prt));
	m_maincpu->rsv().set_nop();
	m_maincpu->p6h().set(FUNC(tx0_state::tx0_io_p6h));
	m_maincpu->p7h().set(FUNC(tx0_state::tx0_io_p7h));
	m_maincpu->sel().set(FUNC(tx0_state::tx0_sel));
	m_maincpu->res().set(FUNC(tx0_state::tx0_io_reset_callback));
	m_maincpu->set_addrmap(AS_PROGRAM, &tx0_state::tx0_64kw_map);
	/* dummy interrupt: handles input */
	m_maincpu->set_vblank_int("screen", FUNC(tx0_state::tx0_interrupt));

	/* video hardware (includes the control panel and typewriter output) */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(refresh_rate);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(virtual_width, virtual_height);
	screen.set_visarea(0, virtual_width-1, 0, virtual_height-1);
	screen.set_screen_update(FUNC(tx0_state::screen_update_tx0));
	screen.screen_vblank().set(FUNC(tx0_state::screen_vblank_tx0));
	screen.set_palette(m_palette);

	CRT(config, m_crt, 0);
	m_crt->set_num_levels(pen_crt_num_levels);
	m_crt->set_offsets(crt_window_offset_x, crt_window_offset_y);
	m_crt->set_size(crt_window_width, crt_window_height);

	TX0_READTAPE(config, "readt", 0);
	TX0_PUNCHTAPE(config, "punch", 0);
	TX0_PRINTER(config, "typewriter", 0);
	TX0_MAGTAPE(config, "magtape", 0);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_tx0);
	PALETTE(config, m_palette, FUNC(tx0_state::tx0_palette), total_colors_needed + sizeof(tx0_pens), total_colors_needed);

	SOFTWARE_LIST(config, "ptp_list").set_original("tx0_ptp").set_filter("64");
}

void tx0_state::tx0_8kwo(machine_config &config)
{
	tx0_64kw(config);

	/* basic machine hardware */
	/* TX0 CPU @ approx. 167 kHz (no master clock, but the memory cycle time is
	approximately 6usec) */
	TX0_8KW_OLD(config.replace(), m_maincpu, 166667);
	m_maincpu->cpy().set(FUNC(tx0_state::tx0_io_cpy));
	m_maincpu->r1l().set(FUNC(tx0_state::tx0_io_r1l));
	m_maincpu->dis().set(FUNC(tx0_state::tx0_io_dis));
	m_maincpu->r3l().set(FUNC(tx0_state::tx0_io_r3l));
	m_maincpu->prt().set(FUNC(tx0_state::tx0_io_prt));
	m_maincpu->rsv().set_nop();
	m_maincpu->p6h().set(FUNC(tx0_state::tx0_io_p6h));
	m_maincpu->p7h().set(FUNC(tx0_state::tx0_io_p7h));
	m_maincpu->sel().set(FUNC(tx0_state::tx0_sel));
	m_maincpu->res().set(FUNC(tx0_state::tx0_io_reset_callback));
	m_maincpu->set_addrmap(AS_PROGRAM, &tx0_state::tx0_8kw_map);
	/* dummy interrupt: handles input */
	m_maincpu->set_vblank_int("screen", FUNC(tx0_state::tx0_interrupt));

	SOFTWARE_LIST(config.replace(), "ptp_list").set_original("tx0_ptp").set_filter("8O");
}

void tx0_state::tx0_8kw(machine_config &config)
{
	tx0_64kw(config);

	/* basic machine hardware */
	/* TX0 CPU @ approx. 167 kHz (no master clock, but the memory cycle time is
	approximately 6usec) */
	TX0_8KW(config.replace(), m_maincpu, 166667);
	m_maincpu->cpy().set(FUNC(tx0_state::tx0_io_cpy));
	m_maincpu->r1l().set(FUNC(tx0_state::tx0_io_r1l));
	m_maincpu->dis().set(FUNC(tx0_state::tx0_io_dis));
	m_maincpu->r3l().set(FUNC(tx0_state::tx0_io_r3l));
	m_maincpu->prt().set(FUNC(tx0_state::tx0_io_prt));
	m_maincpu->rsv().set_nop();
	m_maincpu->p6h().set(FUNC(tx0_state::tx0_io_p6h));
	m_maincpu->p7h().set(FUNC(tx0_state::tx0_io_p7h));
	m_maincpu->sel().set(FUNC(tx0_state::tx0_sel));
	m_maincpu->res().set(FUNC(tx0_state::tx0_io_reset_callback));
	m_maincpu->set_addrmap(AS_PROGRAM, &tx0_state::tx0_8kw_map);
	/* dummy interrupt: handles input */
	m_maincpu->set_vblank_int("screen", FUNC(tx0_state::tx0_interrupt));

	SOFTWARE_LIST(config.replace(), "ptp_list").set_original("tx0_ptp").set_filter("8N");
}

ROM_START(tx0_64kw)
	ROM_REGION(tx0_fontdata_size, "gfx1", ROMREGION_ERASEFF)
		/* space filled with our font */
ROM_END

ROM_START(tx0_8kwo)
	ROM_REGION(tx0_fontdata_size, "gfx1", ROMREGION_ERASEFF)
		/* space filled with our font */
ROM_END

ROM_START(tx0_8kw)
	ROM_REGION(tx0_fontdata_size, "gfx1", ROMREGION_ERASEFF)
		/* space filled with our font */
ROM_END

/***************************************************************************

  Game driver(s)

***************************************************************************/

//    YEAR  NAME      PARENT    COMPAT  MACHINE   INPUT  STATE      INIT      COMPANY  FULLNAME                                                  FLAGS
COMP( 1956, tx0_64kw, tx0_8kw,  0,      tx0_64kw, tx0,   tx0_state, init_tx0, "MIT",   "TX-0 original demonstrator (64 kWords of RAM)",          MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING)
COMP( 1959, tx0_8kwo, tx0_8kw,  0,      tx0_8kwo, tx0,   tx0_state, init_tx0, "MIT",   "TX-0 upgraded system (8 kWords of RAM, old order code)", MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING)
COMP( 1962, tx0_8kw,  0,        0,      tx0_8kw,  tx0,   tx0_state, init_tx0, "MIT",   "TX-0 upgraded system (8 kWords of RAM, new order code)", MACHINE_NO_SOUND_HW | MACHINE_NOT_WORKING)



uchroma68.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: 68bit
/******************************************************************************

Micro Chroma 68
Motorola evaluation board for the MC6847 VDG and MC1372 RF modulator.

The Micro Chroma was supplied as a kit that included the PCB and the major
parts and is reported to have been designed by the "applications group" in
Austin including Tim Ahrens, Jack Brown, and Hunter Scales. The micro Chroma
68 uses the TVBUG monitor which is a 2k monitor ROM that is included in the
MC6846 timer and is reported to have been written by John Dumas with parts
taken from MIKBUG with the help of Mike Wiles the author of MIKBUG.

References:

1. "Micro Chroma 68: The new 'bug' from Motorola TVBUG 1.2", Motorola
   Semiconductor Products Inc. Microprocessor Operations., 1978

G - Go to user program.
G <addr> - Go to user program, starting at the given address 'addr'.
L - Load tape. The encoding uses the Kansas City Standard and the format is
   largely JBUG compatible. Enter an offset, the difference between the
   existing start address and the target start address. If the data can not be
   stored in the memory then 'Memory Bad' is printed and it exits. The load
   can be aborted by pressing the Escape key.
P - Punch tape. Enter the start address, the end address, a name up to 31
   characters long, and enter return to start. The format is largely JBUG
   compatible, just avoid using 'B' or 'G' in the name. The punch can be
   aborted by pressing the Escape key.
V - Verify Tape. Enter an offset, a difference between the existing start
   address and the target start address. If there is an error then 'Memory
   Bad' is printed and it exits. The verfiy can be aborted by pressing the
   Escape key.
M <addr> - Memory examine and change at address 'addr'.
  <hh><linefeed> - Enter new data for the address and increase the address.
    If the memory can not be changed the 'Memory Bad' is printed that the
    memory examine is exited.
  '^' - decrease the address.
  <carriage return> - exit the memory examine function.
E - Examine a memory block. Enter the start address, the end address, and then
    a space to print the next line.
Q - Quick load. Enter the start address, the end address, then the hex bytes
    to load separate by a space.
F - Fill memory. Enter the start address, the end address, and the fill byte.
O - Offset calculation. Enter the start address and then the end address. The
    offset is printed as a 16 bit number, the lower 8 bits being the offset.
    If the offset is too large then 'TOO FAR' is printed.

R - Register display: CC B A X PC SP. The stack pointer (SP) is stored at
    0xf382 to 0xf383 and may be modifed via the memory examine 'M' function
    there. The other registers are on the stack at:
    SP + 1 : CC
    SP + 2 : B
    SP + 3 : A
    SP + 4,5 : X
    SP + 6,7 : P
Z - Clear screen.
S <addr> - Set a breakpoint at address 'addr'. Up to eight breakpoints may be
    defined. A breakpoint can not be set at 0x0000.
U <addr> - Unset the breakpoint at address 'addr'.
D - Remove all breakpoints.
B - Print all breakpoints.
C - Contine user program, until next breakpoint.
N - Traces one instruction.
T <n> - Traces 'n' instructions.

! - User defined code from 0xf396 to 0xf398, a jump instruction.
" - User defined code from 0xf399 to 0xf39a, a jump instruction.
# - User defined code from 0xf39c to 0xf39d, a jump instruction.

A user input function can be defined at 0xf390 to 0xf392, which is room for a
jump instruction, and this is called by the monitor input loop. This function
should set the carry bit on return if there is user input.

A user output function can be defined at 0xf393 to 0xf395 which is room for a
jump instruction.

The board has a "Reset" button, and a "Break" button that toggled the NMI
line. TODO implement these.

The documentation includes hardware suggestions for memory expansion. This
expands the RAM to use the address range 0x0000 to 0xcfff.

The address range 0xe800 to 0xefff is for user expansion. The documentation
gives examples using this address range for two 1k or one 2k EPROM.

The documentation includes position independent ROMable code for driving a
printer via the ACIA, and it also adds S19 'load', 'punch', and 'verify' tape
support for compatibility with MIKBUG, EXBUG, and MINIBUG. The documentation
suggests using this as an optional ROM at 0xe800 to 0xea73. The printer
support implements a user output function definable at 0xf393 to direct screen
output to the ACIA and with a hardware patch this can be directed to a RS232
printer. The entry point for the printer support is 0xe800. The entry point
for the S19 support is 0xe802, so type 'G E802 <CR>' to enter this option and
it displays a choice of operations. It prints a 'B' for each record 'punched',
and a 'S' for each record loaded or verified, and it reports the address of
any bad memory. The S19 support writes a 0x11 (DC1) to start the tape playback
and a 0x12 (DC2) to start the tape recording as might be expected for the
Texas Instruments 733 ASR twin tape cassette terminal with automatic device
control, but it does not appear to issue DC3 or DC4 to turn the tape off.

The documentation includes patches for a range or TSC software which is
generally supplied in Kansas City S19 format, so might need the above extra
EPROM support. The omission of any mention of support for Motorola software is
notable.

The documentation includes instructions on using the Micro Chroma 68 as a dumb
terminal, requiring some minor hardware modifications to reuse the MC6850 ACIA
for a RS-232 interface, and it includes ROMable code at 0xfc00 to 0xffff code
to implement this, a substitute for the TVBUG monitor. TODO might consider
implementing this, but it was hardly a good terminal for the time.

Address decoding was available in the range 0xc000 to 0xcfff in 1k blocks for
expansion.

******************************************************************************/

#include "emu.h"
#include "cpu/m6800/m6800.h"
#include "machine/6821pia.h"
#include "machine/6850acia.h"
#include "machine/mc6846.h"
#include "machine/clock.h"
#include "machine/timer.h"
#include "video/mc6847.h"
#include "imagedev/cassette.h"
#include "speaker.h"

#include "machine/terminal.h"
#include "video/mc6845.h"
#include "screen.h"


namespace {

#define XTAL_UCHROMA68 3.579545_MHz_XTAL

class uchroma68_state : public driver_device
{
public:
	uchroma68_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mc6846(*this, "mc6846")
		, m_mc6847(*this, "mc6847")
		, m_pia(*this, "pia")
		, m_acia(*this, "acia")
		, m_acia_tx_clock(*this, "acia_tx_clock")
		, m_screen(*this, "screen")
		, m_video_ram(*this, "videoram")
		, m_cass(*this, "cassette")
		, m_semi_graphics_six_mod(*this, "SEMI_GRAPHICS_SIX_MOD")
	{ }

	void uchroma68(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void uchroma68_mem(address_map &map) ATTR_COLD;

	TIMER_CALLBACK_MEMBER(kbd_strobe);

	uint8_t mc6847_videoram_r(offs_t offset);
	uint8_t pia_pa_r();
	uint8_t pia_pb_r();
	void mc6846_out_w(uint8_t data);
	void kbd_put(uint8_t data);
	emu_timer *m_kbd_strobe_timer;
	bool m_kbd_strobe;
	uint8_t m_kbd_data;
	bool m_video_inv;

	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);
	void kansas_w(int state);

	required_device<cpu_device> m_maincpu;
	required_device<mc6846_device> m_mc6846;
	required_device<mc6847_device> m_mc6847;
	required_device<pia6821_device> m_pia;
	required_device<acia6850_device> m_acia;
	required_device<clock_device> m_acia_tx_clock;
	required_device<screen_device> m_screen;
	required_shared_ptr<uint8_t> m_video_ram;
	required_device<cassette_image_device> m_cass;
	required_ioport m_semi_graphics_six_mod;

	uint8_t m_cass_rx_clock_count;
	uint8_t m_cass_rx_period;
	uint8_t m_cass_txcount;
	bool m_cass_in;
	bool m_cass_inbit;
	bool m_cass_txbit;
	bool m_cass_last_txbit;
};


/***********************************************************

    Address Map

************************************************************/

void uchroma68_state::uchroma68_mem(address_map &map)
{
	// On board user RAM
	map(0x0000, 0x1fff).ram();

	// Off board RAM expansion, as documented.
	map(0x2000, 0xcfff).ram();

	// Display RAM
	map(0xd000, 0xe7ff).ram().share(m_video_ram);

	// Optional EPROM, with the supplied S19 tape format support.
	map(0xe800, 0xebff).rom();

	// Stack RAM
	map(0xf000, 0xf3ff).ram();

	map(0xf408, 0xf409).mirror(0x03b0).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write));

	map(0xf404, 0xf407).mirror(0x03b0).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));

	// Programmable counter/timer and I/O.
	map(0xf440, 0xf447).mirror(0x0380).rw(m_mc6846, FUNC(mc6846_device::read), FUNC(mc6846_device::write));

	// TVBUG 1.2 ROM
	map(0xf800, 0xffff).rom();
}

/***********************************************************

    Keys

************************************************************/

static INPUT_PORTS_START(uchroma68)

	// The documentation notes a hardware modification to allow the use of
	// the Semi-graphics 6 mode of the MC6847.
	PORT_START("SEMI_GRAPHICS_SIX_MOD")
	PORT_CONFNAME(0x01, 0x00, "Semi-graphics 6 mode modification")
	PORT_CONFSETTING(0x00, "No")
	PORT_CONFSETTING(0x01, "Yes")

INPUT_PORTS_END


/***********************************************************

************************************************************/

TIMER_CALLBACK_MEMBER(uchroma68_state::kbd_strobe)
{
	m_kbd_strobe = 1;
	m_kbd_data = 0;
}

void uchroma68_state::mc6846_out_w(uint8_t data)
{
	m_mc6847->css_w(!BIT(data, 0));
	m_mc6847->intext_w(!BIT(data, 1));
	m_mc6847->ag_w(!BIT(data, 2));
	m_mc6847->gm0_w(BIT(data, 3));
	m_mc6847->gm1_w(BIT(data, 4));
	m_mc6847->gm2_w(BIT(data, 5));
	m_video_inv = BIT(data, 6);
	// P7 is NC.
}


uint8_t uchroma68_state::mc6847_videoram_r(offs_t offset)
{
	offset &= 0x1fff;
	if (offset > 0x17ff) return 0xff;

	uint8_t code = m_video_ram[offset];

	if (m_semi_graphics_six_mod->read())
		m_mc6847->inv_w(m_video_inv);
	else
		m_mc6847->inv_w(m_video_inv | BIT(code, 6));

	m_mc6847->as_w(BIT(code, 7));

	return code;
}

// TVBUG polls the parallel keyboard strobe every four video frames - it
// blinks the cursor for four frames between polling. At a 60Hz frame rate
// that gives polling at about every 67ms. A 70ms keyboard strobe period
// appears to be more than adequate.
void uchroma68_state::kbd_put(uint8_t data)
{
	m_kbd_data = data;
	m_kbd_strobe_timer->adjust(attotime::from_msec(70));
	m_kbd_strobe = 0;
}

uint8_t uchroma68_state::pia_pa_r()
{
	uint8_t data = m_kbd_data;
	return (m_kbd_strobe << 7) | data;
}

uint8_t uchroma68_state::pia_pb_r()
{
	// PB0 to PB4 are Up, Down, Left, Right, Home.
	// PB5 is NC
	// PB6 is V VDG
	// PB7 is H VDG
	return (m_mc6847->hs_r() << 7) | (m_mc6847->fs_r() << 6);
}


TIMER_DEVICE_CALLBACK_MEMBER(uchroma68_state::kansas_r)
{
	// Turn 1200/2400Hz to a bit
	uint8_t cassin = (m_cass->input() > +0.04) ? 1 : 0;
	uint8_t inbit = m_cass_inbit;

	m_cass_rx_period++;

	// The RX clock is recovered from the input data, and synchronized to
	// the bit transitions. The software expects a 1x RX clock.
	if (++m_cass_rx_clock_count > 133)
		m_cass_rx_clock_count = 0;

	if (cassin != m_cass_in)
	{
		// A transition, so now check the period.
		inbit = (m_cass_rx_period < 12) ? 1 : 0;
		m_cass_in = cassin;
		m_cass_rx_period = 0;
	}
	else if (m_cass_rx_period > 32)
	{
		// Idle the ACIA if there is no data.
		m_cass_rx_period = 32;
		inbit = 1;
	}

	if (inbit != m_cass_inbit)
	{
		m_acia->write_rxd(inbit);
		m_cass_inbit = inbit;
		// Sync the RX clock with the data edges.
		m_cass_rx_clock_count = 0;
	}

	if (m_cass_rx_clock_count == 0)
		m_acia->write_rxc(0);
	else if (m_cass_rx_clock_count == 67)
		m_acia->write_rxc(1);
}

void uchroma68_state::kansas_w(int state)
{
	// The Kansas City cassette format encodes a '0' bit by four cycles of
	// a 1200 Hz sine wave, and a '1' bit as eight cycles of 2400 Hz,
	// giving a 300 baud rate.
	//
	// The clock rate to the ACIA is 16x the baud rate, or 4800Hz, and is
	// divided by 2 to get the 2400 Hz rate, or divided by 4 to get the
	// 1200 Hz rate.

	// Sync the period phase on TX bit transitions.
	if (m_cass_txbit != m_cass_last_txbit)
	{
		m_cass_txcount = 0;
		m_cass_last_txbit = m_cass_txbit;
	}

	if (m_cass_txbit)
		m_cass->output(BIT(m_cass_txcount, 1) ? +1.0 : -1.0); // 2400Hz
	else
		m_cass->output(BIT(m_cass_txcount, 2) ? +1.0 : -1.0); // 1200Hz

	m_cass_txcount++;
	m_acia->write_txc(state);
}

void uchroma68_state::machine_start()
{
	m_kbd_strobe_timer = timer_alloc(FUNC(uchroma68_state::kbd_strobe), this);
	m_kbd_strobe = 1;

	save_item(NAME(m_kbd_strobe));
	save_item(NAME(m_kbd_data));
	save_item(NAME(m_video_inv));
	save_item(NAME(m_cass_rx_clock_count));
	save_item(NAME(m_cass_rx_period));
	save_item(NAME(m_cass_txcount));
	save_item(NAME(m_cass_in));
	save_item(NAME(m_cass_inbit));
	save_item(NAME(m_cass_txbit));
	save_item(NAME(m_cass_last_txbit));
}

void uchroma68_state::machine_reset()
{
	m_mc6846->reset();
	m_pia->reset();
	m_kbd_strobe_timer->reset();
	m_kbd_strobe = 1;
	m_kbd_data = 0;

	// These MC6846 I/O outputs are pulled high. The high impedance state
	// of these lines is not yet emulated by the mc6846 driver, and TVBUG
	// does not drive them. On reset these are high impedance anyway, and
	// pulled high, so their lines going to the VDG are initialized
	// here. The first three lines are inverted.
	m_mc6847->css_w(0);
	m_mc6847->intext_w(0);
	m_mc6847->ag_w(0);
	m_mc6847->gm0_w(1);
	m_mc6847->gm1_w(1);
	m_mc6847->gm2_w(1);
	m_video_inv = 1;

	m_cass_rx_clock_count = 0;
	m_cass_rx_period = 0;
	m_cass_txcount = 0;
	m_cass_in = 0;
	m_cass_inbit = 0;
	m_cass_txbit = 0;
	m_cass_last_txbit = 0;
}

/***********************************************************

    Machine

************************************************************/

void uchroma68_state::uchroma68(machine_config &config)
{
	M6808(config, m_maincpu, XTAL_UCHROMA68);        // 894.8 kHz clock
	m_maincpu->set_addrmap(AS_PROGRAM, &uchroma68_state::uchroma68_mem);

	MC6846(config, m_mc6846, XTAL_UCHROMA68 / 4);  // Same as the cpu clock
	m_mc6846->out_port().set(FUNC(uchroma68_state::mc6846_out_w));

	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);

	MC6847(config, m_mc6847, XTAL_UCHROMA68);
	m_mc6847->set_screen(m_screen);
	m_mc6847->input_callback().set(FUNC(uchroma68_state::mc6847_videoram_r));

	PIA6821(config, m_pia);
	m_pia->readpa_handler().set(FUNC(uchroma68_state::pia_pa_r));
	m_pia->readpb_handler().set(FUNC(uchroma68_state::pia_pb_r));

	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set([this] (bool state) { m_cass_txbit = state; });

	CLOCK(config, m_acia_tx_clock, 4800);
	m_acia_tx_clock->signal_handler().set(FUNC(uchroma68_state::kansas_w));

	TIMER(config, "kansas_r").configure_periodic(FUNC(uchroma68_state::kansas_r), attotime::from_hz(40000));

	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(uchroma68_state::kbd_put));
}

/***********************************************************

    ROMS

************************************************************/

ROM_START(uchroma68)
	ROM_REGION(0x10000,"maincpu",0)
	ROM_LOAD("tvbug.rom", 0xf800, 0x0800, CRC(47f721e4) SHA1(31ea8d596f1f99ee26c4bea694245448bfdc3ee6))
	ROM_LOAD("uch68s19.rom", 0xe800, 0x0400, CRC(8932484f) SHA1(231098ab10185d400032415cf9b7273d736d3b87))
ROM_END

} // anonymous namespace


/***************************************************************************

  Game driver(s)

***************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE   INPUT  CLASS        INIT        COMPANY     FULLNAME      FLAGS
COMP( 1980, uchroma68,  0,      0,      uchroma68,    uchroma68, uchroma68_state, empty_init, "Motorola", "Micro Chroma 68" , MACHINE_NO_SOUND_HW )



uknc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

UKNC (Educational Computer by Scientific Centre) PDP-11 clone.
Also known as Elektronika MS-0511.
RAM = 192K (CPU 1 = 64K, CPU 2 = 32K, Videoram = 96K), ROM = 32K.
Graphics 640x288 pixels.

2009-05-12 Skeleton driver.

Status: both CPUs start in the weeds.

****************************************************************************/

#include "emu.h"
#include "bus/qbus/qbus.h"
#include "cpu/t11/t11.h"
#include "imagedev/cassette.h"

#include "emupal.h"
#include "screen.h"


namespace {

class uknc_state : public driver_device
{
public:
	uknc_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_subcpu(*this, "subcpu")
		, m_qbus(*this, "qbus")
		, m_cart(*this, "cart")
		, m_cassette(*this, "cassette")
	{ }

	void uknc(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	required_device<k1801vm2_device> m_maincpu;
	required_device<k1801vm2_device> m_subcpu;
	required_device<qbus_device> m_qbus;
	required_device<qbus_device> m_cart;
	required_device<cassette_image_device> m_cassette;

	void uknc_mem(address_map &map) ATTR_COLD;
	void uknc_sub_mem(address_map &map) ATTR_COLD;
};


void uknc_state::uknc_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).ram();
}

void uknc_state::uknc_sub_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x7fff).ram();
	map(0x8000, 0xffff).rom().region("subcpu",0);
}

/* Input ports */
static INPUT_PORTS_START( uknc )
INPUT_PORTS_END


void uknc_state::machine_reset()
{
}

void uknc_state::machine_start()
{
}

uint32_t uknc_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void uknc_state::uknc(machine_config &config)
{
	/* basic machine hardware */
	K1801VM2(config, m_maincpu, XTAL(16'000'000)/4); // external clock is /2 + internal divider /2
	m_maincpu->set_initial_mode(0x8000);
	m_maincpu->set_addrmap(AS_PROGRAM, &uknc_state::uknc_mem);
	m_maincpu->set_disable();

	QBUS(config, m_qbus, 0);
	m_qbus->set_space(m_maincpu, AS_PROGRAM);
	m_qbus->birq4().set_inputline(m_maincpu, t11_device::VEC_LINE);
	QBUS_SLOT(config, "qbus" ":1", qbus_cards, nullptr);

	K1801VM2(config, m_subcpu, XTAL(12'500'000)/4);
	m_subcpu->set_addrmap(AS_PROGRAM, &uknc_state::uknc_sub_mem);
	m_subcpu->set_initial_mode(0x8000);

	QBUS(config, m_cart, 0);
	m_cart->set_space(m_subcpu, AS_PROGRAM);
	m_cart->birq4().set_inputline(m_subcpu, t11_device::VEC_LINE);
	QBUS_SLOT(config, "cart" ":1", qbus_cards, "mz");
	QBUS_SLOT(config, "cart" ":2", qbus_cards, nullptr);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	screen.set_screen_update(FUNC(uknc_state::screen_update));

	PALETTE(config, "palette", palette_device::MONOCHROME);
}

/* ROM definition */
ROM_START( uknc )
	ROM_REGION16_LE(0100000, "subcpu", ROMREGION_ERASE00)
	ROM_LOAD("uknc.rom", 0x0000, 0x8000, CRC(a1536994) SHA1(b3c7c678c41ffa9b37f654fbf20fef7d19e6407b))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  STATE       INIT        COMPANY             FULLNAME  FLAGS */
COMP( 1987, uknc, 0,      0,      uknc,    uknc,  uknc_state, empty_init, "Elektronika", "UKNC / MS-0511",   MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



ultim809.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/******************************************************************************************************

Ultim809 homebrew computer by Matthew Sarnoff, 2011
http://www.msarnoff.org/6809/

Was developed during 2009-2011, but unknown if it was ever finished. Most of the claimed
features don't seem to be there or are inaccessible.

2020-06-04 Skeleton [Robbbert]

If someone knows more about the system, please update this source.


ToDo:
- Mirrors
- Video RAM has 2 banks of 0x4000 each. There's no way to select banks in the VDP code.
- Sound (hooked up, unable to test)
- FTDI connector
- Keyboard, PS/2 type from an older PC, connects to VIA PB7 and CA1.
- Joysticks (Sega gamepad, or Atari joystick only).
- Reset button, Run/Halt switch.
- Power LED, Bus status LEDs (2), User status LEDs (2).
- RTC type DS1307, connects to VIA PB0,PB1,PB6; xtal 32'768; battery CR2032.
- SD card slots and SPDI shift register (74595).
- Need software (some is supposed to exist; if found we could use a quickloader to get it in)

Status:
- It ran into the weeds at 0x100, so memory is patched there to jump to the sign-on screen.
- When it says to press INTERRUPT, press F1. May need multiple presses to get over random errors.
- Various commands starting with k are supposed to be valid, but nothing is acceptable.
- If an error occurs it locks up and you have to press F1 again.
- Even though it is suggested to use a dumb terminal, nothing ever shows on it.

****************************************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/m6809/m6809.h"
#include "machine/6522via.h"
#include "machine/ins8250.h"
#include "sound/ay8910.h"
#include "video/tms9928a.h"

#include "speaker.h"


namespace {

class ultim809_state : public driver_device
{
public:
	ultim809_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_via(*this, "via")
		, m_crtc(*this, "crtc")
		, m_psg(*this, "psg")
		, m_uart(*this, "uart")
	{}

	void ultim809(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(nmi_button);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void mem_map(address_map &map) ATTR_COLD;
	std::unique_ptr<u8[]> m_ram;
	required_device<cpu_device> m_maincpu;
	required_device<via6522_device> m_via;
	required_device<tms9918a_device> m_crtc;
	required_device<ay8910_device> m_psg;
	required_device<ns16550_device> m_uart;
	u8 m_membank = 0U;
};

void ultim809_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	// main ram banks 0 and 1
	map(0x0000, 0x7fff).lrw8(NAME([this] (offs_t offset) { return m_ram[offset]; }), NAME([this] (offs_t offset, u8 data) { m_ram[offset] = data; } ));
	// main ram any bank
	map(0x8000, 0xbfff).lrw8(NAME([this] (offs_t offset) { return m_ram[offset | (m_membank << 14)]; }),
							 NAME([this] (offs_t offset, u8 data) { m_ram[offset | (m_membank << 14)] = data; } )); // u8
	// devices
	map(0xc000, 0xc00f).m(m_via, FUNC(via6522_device::map)); // u11
	map(0xc400, 0xc407).rw(m_uart, FUNC(ns16550_device::ins8250_r), FUNC(ns16550_device::ins8250_w));  // u16
	map(0xc800, 0xc800); //.r  chip enable 74595
	map(0xcc00, 0xcc00).rw(m_crtc, FUNC(tms9918a_device::vram_read), FUNC(tms9918a_device::vram_write));
	map(0xcc01, 0xcc01).rw(m_crtc, FUNC(tms9918a_device::register_read), FUNC(tms9918a_device::register_write));
	map(0xcc02, 0xcc03).rw(m_psg, FUNC(ay8910_device::data_r), FUNC(ay8910_device::address_data_w));
	map(0xcc04, 0xcc04); //.r  select lower 16k of VRAM
	map(0xcc06, 0xcc06); //.r  clear gamepad pin 7
	map(0xcc0c, 0xcc0c); //.r  select upper 16k of VRAM
	map(0xcc0e, 0xcc0e); //.r  set gamepad pin 7
	map(0xd000, 0xd3ff); // expansion slot 1 (not used)
	map(0xd400, 0xd7ff); // expansion slot 2 (not used)
	map(0xd800, 0xdbff); // expansion slot 3 (not used)
	map(0xdc00, 0xdfff); // expansion slot 4 (not used)
	// rom
	map(0xe000, 0xffff).rom().region("maincpu", 0); // u9
}


static INPUT_PORTS_START( ultim809 )
	PORT_START("RESET")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Interrupt") PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(ultim809_state::nmi_button), 0)
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(ultim809_state::nmi_button)
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, newval ? ASSERT_LINE : CLEAR_LINE);
}

void ultim809_state::machine_start()
{
	m_ram = make_unique_clear<u8[]>(0x80000);
	save_pointer(NAME(m_ram), 0x80000);
	save_item(NAME(m_membank));
	// Send it to the sign-on instead of into the weeds
	m_ram[0x100] = 0x10;
	m_ram[0x101] = 0x3F;
}

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_38400 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_38400 )
DEVICE_INPUT_DEFAULTS_END

void ultim809_state::ultim809(machine_config &config)
{
	// basic machine hardware
	MC6809E(config, m_maincpu, 8000000 / 4);  // 68B09E
	m_maincpu->set_addrmap(AS_PROGRAM, &ultim809_state::mem_map);

	// video hardware
	TMS9918A(config, m_crtc, XTAL(10'738'635));
	m_crtc->set_screen("screen");
	m_crtc->set_vram_size(0x4000);    // actually 2 banks of 0x4000
	m_crtc->int_callback().set_inputline(m_maincpu, M6809_IRQ_LINE);
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	// TBD: what type of VIA is this? It replaced a MC68B21P at some point in development.
	MOS6522(config, m_via, 8000000 / 4);
	// Memory banking: up to 32 banks with inbuilt U8, or replace it with external memory to get the full 4 MB
	m_via->writepa_handler().set([this] (u8 data) { m_membank = data & 0x1F; });   // memory banking
	//m_via->readpb_handler().set(FUNC(ultim809_state::portb_r));    // serial
	//m_via->writepb_handler().set(FUNC(ultim809_state::portb_w));   // serial
	m_via->irq_handler().set_inputline(m_maincpu, M6809_FIRQ_LINE);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	AY8910(config, m_psg, 8000000 / 4).add_route(ALL_OUTPUTS, "mono", 0.50);
	//m_psg->port_a_read_callback(FUNC(ultim809_state::...);  // joystick 1
	//m_psg->port_b_read_callback(FUNC(ultim809_state::...);  // joystick 2

	NS16550(config, m_uart, XTAL(1'843'200));
	m_uart->out_tx_callback().set("rs232", FUNC(rs232_port_device::write_txd));

	// there's no rs232 port, it uses FTDI, but we need to see what's going on
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_uart, FUNC(ins8250_uart_device::rx_w));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal)); // must be exactly here
}

/* ROM definition */
ROM_START( ultim809 )
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD( "ultim809.u9", 0x0000, 0x2000, CRC(b827aaf1) SHA1(64d9e94542d8ff13f64a4d787508eef7b64d4946) )
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT         COMPANY            FULLNAME     FLAGS
COMP( 2010, ultim809, 0,      0,      ultim809, ultim809, ultim809_state, empty_init, "Matthew Sarnoff", "Ultim809", MACHINE_IS_INCOMPLETE | MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



ultra45.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************
Skeleton driver for Sun Microsystems Ultra 25 and Ultra 45 Workstations.

Hardware info about Ultra 45:
 -Dual UltraSPARC IIIi processor.
 -PLX Technology PEX8532-BB25BI G (PCI Express Switch).
 -ALI ULI M1575 A1 (Super South Bridge).
 -Broadcom BCM5715CKPBG (Dual-Port Gigabit Ethernet controller with a PCI Express Host Interface).

***********************************************************************************************************/

#include "emu.h"
#include "cpu/sparc/sparc.h"


namespace {

class ultra45_state : public driver_device
{
public:
	ultra45_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }


	void ultra45(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
};


static INPUT_PORTS_START(ultra45)
INPUT_PORTS_END

void ultra45_state::ultra45(machine_config &config)
{
	SPARCV8(config, m_maincpu, 20'000'000); // Actually a 1.6GHz UltraSPARC IIIi CPU with 1 MB integrated Level2 cache
	//SPARCV8(config, m_maincpu, 20'000'000); // Actually a 1.6GHz UltraSPARC IIIi CPU with 1 MB integrated Level2 cache (optional 2nd CPU)
}


// Probably all wrong
ROM_START(ultra45)
	ROM_REGION(0x400000, "maincpu", 0)
	ROM_LOAD( "chicago_4.25.9_cks_c1bd_s29al032d.u29", 0x000000, 0x400000, CRC(e08f3c4f) SHA1(87bd5c92f59121b5253c0c12d627d18401a01bf7) )

	ROM_REGION(0x400000, "extra1", 0)
	ROM_LOAD( "chicago_1.80.3_cks_8ee3_s29al032d.u81", 0x000000, 0x400000, CRC(0b18627e) SHA1(473fe575e0dad4467bb40cdb76234be354d67ac2) ) // checksum does not match with the one printed on the label, probably because a firmware update

	ROM_REGION(0x021000, "extra2", 0)
	ROM_LOAD( "d887_at45db011b.u77",                   0x000000, 0x021000, CRC(b6363183) SHA1(1769ffa968e0f79bbfa7bc147f8d6c179f5ca44b) )

	ROM_REGION(0x008000, "extra3", 0)
	ROM_LOAD( "ca4b_at25256an.u79",                    0x000000, 0x008000, CRC(a35303fb) SHA1(df1b4121c8ae40b7fb9a03cc57e61f0da12e1110) )

	/* Unprotected PIC16F777 near a 20 MHz xtal
	    ID0 = 2009h
	    ID1 = 0000h
	    ID2 = 2009h
	    ID3 = 0003h
	    Oscillator = High Speed
	    Watchdog = Disabled
	    Power-up timer = Disabled
	    MCLR pin = Disabled
	    Brown-out Reset enabled and always on */
	ROM_REGION(0x004000, "pic1", 0)
	ROM_LOAD("972a_pic16f777.u13",                     0x000000, 0x004000, CRC(a1d4b342) SHA1(564926990cf28a1f88a8a374f16ac172f7b8c8f7) )

	ROM_REGION(0x004300, "pic2", 0)
	ROM_LOAD("a4bb_pic12f629.u66",                     0x000000, 0x004300, CRC(258bd64e) SHA1(58f64710a3f4d184a3aaa776aa351d30144806f5) )

	ROM_REGION(0x0016cc, "pld", 0)
	ROM_LOAD("chicago_0309_4m_cks_6d7b_xc9572xl.u15",  0x000000, 0x0016cc, CRC(d5fbe610) SHA1(a41e136eeb6c115523814ca3774ff7a0a0604569) )
ROM_END

} // anonymous namespace

//    YEAR, NAME,    PARENT, COMPAT, MACHINE, INPUT,   CLASS,         INIT,       COMPANY,            FULLNAME,   FLAGS
COMP( 1996, ultra45, 0,      0,      ultra45, ultra45, ultra45_state, empty_init, "Sun Microsystems", "Ultra 45", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



umatic.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Felipe Sanches
/****************************************************************************

    Skeleton driver for Sony U-Matic Videocassette Recorder

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/z80ctc.h"


namespace {

class umatic_state : public driver_device
{
public:
	umatic_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ctc(*this, "ctc")
	{
	}

	void umatic(machine_config &config);
	uint8_t io_read(offs_t offset);
	void io_write(offs_t offset, uint8_t data);

private:
	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_device<z80ctc_device> m_ctc;
};

void umatic_state::io_write(offs_t offset, uint8_t data)
{
	//FIXME!
}

uint8_t umatic_state::io_read(offs_t offset)
{
	switch(offset & 7){
		case 0: return 0; //FIXME!
		case 1: return 0; //FIXME!
		case 2: return 0; //FIXME!
		case 3: return 0; //FIXME!
		case 4: return 0x04; //FIXME!
		case 5: return 0; //FIXME!
		case 6: return 0; //FIXME!
		case 7: return 0; //FIXME!
	}

	return 0;
}

void umatic_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x03).mirror(0x7c).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x80, 0x87).mirror(0x78).rw(FUNC(umatic_state::io_read), FUNC(umatic_state::io_write));
}

void umatic_state::mem_map(address_map &map)
{
	map(0x0000, 0x17ff).rom();  // 8k-byte EPROM at IC26, but only the first 6kb are mapped.
					// And remaining unmapped content is all 0xFF.
	map(0x1800, 0x1fff).ram();  // 2k-byte CXK5816PN-15L at IC17
}

static INPUT_PORTS_START(umatic)
INPUT_PORTS_END

void umatic_state::umatic(machine_config &config)
{
	Z80(config, m_maincpu, 4.9152_MHz_XTAL / 2); // LH0080 SHARP
	m_maincpu->set_addrmap(AS_PROGRAM, &umatic_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &umatic_state::io_map);

	// peripheral hardware
	Z80CTC(config, m_ctc, 4.9152_MHz_XTAL / 16); // LH0082 SHARP
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	//TODO: m_ctc->zc_callback<2>().set(...); // "search freq out" ?
}

ROM_START(vo5850pm)
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("2764_s68_ev1-25.ic26", 0x0000, 0x2000, CRC(7f3c191d) SHA1(4843399f86a15133e966c9e8992eafac03818916))
ROM_END

} // anonymous namespace


//   YEAR  NAME   PARENT/COMPAT MACHINE  INPUT    CLASS             INIT COMPANY  FULLNAME                                    FLAGS
SYST(19??, vo5850pm,    0, 0,   umatic, umatic, umatic_state, empty_init, "Sony", "U-Matic Videocassette Recorder VO-5850PM",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



unichamp.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Viens
/************************************************************************
 *  Unisonic Champion 2711 (late 1977 based on part dates)
 *
 *  Driver from plgDavid (David Viens)
 *
 *  Thanks to Sylvain De Chantal (Sly D.C.) for the 2 test units,
 *  carts and FAQ: http://www.ccjvq.com/slydc/index/faq/2711
 *
 *  Thanks to Paul Robson for the GIC font rom.
 *  (http://worstconsole.blogspot.ca/2012/12/the-worstconsoleever.html)
 *  Note a spare dead GIC has been given to Lord Nightmare and should be sent for decap!
 *
 *  The Unisonic Champion is the only known GI "Gimini Mid-Range 8950 Programmable Game Set"
 *  to ever reach the market, and only in limited quantities (aprox 500 units ever built)
 *
 *  Architecture:
 *  Master IC : AY-3-8800-1 Graphics Interface (A.K.A. GIC, 40 pin)
 *  Slave  IC : CP1610 CPU (40 pin, same as in the Intellivision)
 *  EXEC ROM  : 9501-01009 (40 pin) at 0x0800 (factory mapped)
 *
 *  The GIC generates the CPU Clock, the video signals and the audio.
 *  The CPU does NOT access the GIC directly.
 *  One way CPU->GIC 'communication' takes place through 256 bytes of shared RAM
 *  (using two 4x256 TMS4043NL-2 (2112-1) Static Rams at U3 and U4)
 *
 *  In this design the GIC only allows the CPU to use the BUS (and shared RAM)
 *  a fraction of the frame time. (4.33ms for each 16.69ms, or 26% of the time)
 *  (the real ratio of clocks is 7752/29868 )
 *
 *  Boot: When the GIC let go of !RESET_OUT the EXEC Rom pushes 0x800 onto
 *  the bus for the CPU to fetch and place in R7 to start execution.
 *  This first CPU slice only last 3ms, then the GIC sets the CPU's BUSRQ low,
 *  stalling it for 12.36ms, then sets it high for 4.33ms etc...
 *  59.95 times a second - NTSC
 *
 *  TODO: Should we add an explicit Reset button like the controller has?
 *
 ************************************************************************/

#include "emu.h"

#include "cpu/cp1610/cp1610.h"
#include "gic.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class unichamp_state : public driver_device
{
public:
	unichamp_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_gic(*this, "gic"),
		m_cart(*this, "cartslot"),
		m_ctrls(*this, "CTRLS")
	{ }

	void unichamp(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<cp1610_cpu_device> m_maincpu;
	required_device<gic_device> m_gic;
	required_device<generic_slot_device> m_cart;

	required_ioport m_ctrls;

	uint8_t m_ram[256];

	void unichamp_palette(palette_device &palette) const;

	uint8_t bext_r(offs_t offset);

	uint8_t gicram_r(offs_t offset);
	void gicram_w(offs_t offset, uint8_t data);

	uint16_t trapl_r(offs_t offset);
	void trapl_w(offs_t offset, uint16_t data);

	uint16_t read_ff();

	uint16_t iab_r();

	uint32_t screen_update_unichamp(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void unichamp_mem(address_map &map) ATTR_COLD;
};

void unichamp_state::unichamp_palette(palette_device &palette) const
{
	/*
	palette.set_pen_color(GIC_BLACK, rgb_t(0x00, 0x00, 0x00));
	palette.set_pen_color(GIC_RED,   rgb_t(0xae, 0x49, 0x41));//(from box shot)
	palette.set_pen_color(GIC_GREEN, rgb_t(0x62, 0x95, 0x88));//(from box shot)
	palette.set_pen_color(GIC_WHITE, rgb_t(0xff, 0xff, 0xff));
	*/

	//using from intv.c instead as suggested by RB
	palette.set_pen_color(GIC_BLACK, rgb_t(0x00, 0x00, 0x00));
	palette.set_pen_color(GIC_RED,   rgb_t(0xff, 0x3d, 0x10));
	//palette.set_pen_color(GIC_GREEN, rgb_t(0x38, 0x6b, 0x3f)); //intv's DARK GREEN
	palette.set_pen_color(GIC_GREEN, rgb_t(0x00, 0xa7, 0x56)); //intv's GREEN
	palette.set_pen_color(GIC_WHITE, rgb_t(0xff, 0xfc, 0xff));
}


void unichamp_state::unichamp_mem(address_map &map)
{
	map.global_mask(0x1FFF); //B13/B14/B15 are grounded!
	map(0x0000, 0x00FF).rw(FUNC(unichamp_state::gicram_r), FUNC(unichamp_state::gicram_w)).umask16(0x00ff);
	map(0x0100, 0x07FF).rw(FUNC(unichamp_state::trapl_r), FUNC(unichamp_state::trapl_w));
	map(0x0800, 0x0FFF).rom().region("maincpu", 0);   // Carts and EXE ROM, 10-bits wide
}


static INPUT_PORTS_START( unichamp )
	PORT_START( "CTRLS" )
	PORT_BIT( 0x01,  IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')// P1 YES (EBCA0)
	PORT_BIT( 0x02,  IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N')// P1 NO  (EBCA1)
	PORT_BIT( 0x04,  IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')// P2 YES (EBCA2)
	PORT_BIT( 0x08,  IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S')// P2 NO  (EBCA3)
	PORT_BIT( 0x10,  IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20,  IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40,  IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80,  IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END


uint8_t unichamp_state::bext_r(offs_t offset)
{
	//The BEXT instruction pushes a user-defined nibble out on the four EBCA pins (EBCA0 to EBCA3)
	//and reads the ECBI input pin for HIGH or LOW signal to know whether or not to branch

	//The unisonic control system couldnt be simpler in design.
	//Each of the two player controllers has three buttons:
	//one tying !RESET(GIC pin 21) to ground when closed - resetting the WHOLE system.
	//a YES button (connecting EBCA0 to EBCI for Player1 and EBC2 to EBCI for Player2)
	//a NO  button (connecting EBCA1 to EBCI for Player1 and EBC3 to EBCI for Player2)

	//The CPU outputs a MASK of whatever it needs and checks the result.
	//EG: Any player can choose if one or two players are going to play the game for instance

	uint8_t port = ioport("CTRLS")->read() & 0x0F; ////only lower nibble

	//We need to return logical high or low on the EBCI pin
	return (port & offset)>0?1:0;
}


uint16_t unichamp_state::read_ff()
{
	return 0xffff;
}

void unichamp_state::machine_start()
{
	if (m_cart->exists()){
		//flip endians in more "this surely exists in MAME" way?
		//NOTE The unichamp roms have the same endianness as intv on disk and in memory
		uint8_t*ptr   = m_cart->get_rom_base();
		size_t size = m_cart->get_rom_size();
		for(size_t i=0;i<size;i+=2){
			uint8_t TEMP = ptr[i];
			ptr[i] = ptr[i+1];
			ptr[i+1] = TEMP;
		}
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x1000, 0x17ff,
					read16s_delegate(*m_cart, FUNC(generic_slot_device::read16_rom)));
	} else
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x1000, 0x17ff,
					read16smo_delegate(*this, FUNC(unichamp_state::read_ff)));

	memset(m_ram, 0, sizeof(m_ram));
}

/* Set Reset and INTR/INTRM Vector */
uint16_t unichamp_state::iab_r()
{
	/*
	the intv driver did not explain this but from the CP1600 manual:
	When MSYNC* goes inactive (high), the bus control signals issue lAB,
	and the CPU inputs from the bus into the PC the starting address of the main program.
	Note that the initialization address can be defined by the user at any desired bus address or
	can be the default address resulting from the logical state of the non-driven bus
	*/

	//The Unisonic EXEC ROM chip (9501-01009) is self mapped at 0x0800
	//The cart ROMS are self mapped to 0x1000
	//upon boot the EXEC ROM puts 0x0800 on the bus for the CPU to use as first INT vector

	/* Set initial PC */
	return 0x0800;
}

uint32_t unichamp_state::screen_update_unichamp(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return m_gic->screen_update(screen, bitmap, cliprect);
}

uint8_t unichamp_state::gicram_r(offs_t offset)
{
	return m_ram[offset];
}

void unichamp_state::gicram_w(offs_t offset, uint8_t data)
{
	m_ram[offset] = data;
}

uint16_t unichamp_state::trapl_r(offs_t offset)
{
	logerror("trapl_r(%x)\n",offset);
	return (int)0;
}

void unichamp_state::trapl_w(offs_t offset, uint16_t data)
{
	logerror("trapl_w(%x) = %x\n",offset,data);
}

void unichamp_state::unichamp(machine_config &config)
{
	/* basic machine hardware */

	//The CPU is really clocked this way:
	//CP1610(config, m_maincpu, XTAL(3'579'545)/4);
	//But since it is only running 7752/29868 th's of the time...
	//TODO find a more accurate method? (the emulation will be the same though)
	CP1610(config, m_maincpu, (7752.0/29868.0)*XTAL(3'579'545)/4);
	m_maincpu->set_addrmap(AS_PROGRAM, &unichamp_state::unichamp_mem);
	m_maincpu->bext().set(FUNC(unichamp_state::bext_r));
	m_maincpu->iab().set(FUNC(unichamp_state::iab_r));

	config.set_maximum_quantum(attotime::from_hz(60));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(XTAL(3'579'545),
		gic_device::LINE_CLOCKS, gic_device::START_ACTIVE_SCAN, gic_device::END_ACTIVE_SCAN,
		gic_device::LINES,       gic_device::START_Y,           gic_device::START_Y + gic_device::SCREEN_HEIGHT);
	screen.set_screen_update(FUNC(unichamp_state::screen_update_unichamp));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(unichamp_state::unichamp_palette), 4);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	GIC(config, m_gic, XTAL(3'579'545));
	m_gic->set_screen("screen");
	m_gic->ram_callback().set(FUNC(unichamp_state::gicram_r));
	m_gic->add_route(ALL_OUTPUTS, "mono", 0.40);

	/* cartridge */
	GENERIC_CARTSLOT(config, m_cart, generic_linear_slot, "unichamp_cart", "bin,rom");
	SOFTWARE_LIST(config, "cart_list").set_original("unichamp");
}

ROM_START(unichamp)
	ROM_REGION(0x1000,"maincpu", ROMREGION_ERASEFF)

	ROM_LOAD16_WORD( "9501-01009.u2", 0, 0x1000, CRC(49a0bd8f) SHA1(f4d126d3462ad351da4b75d76c75942d5a6f27ef))

	//these below are for local tests. you can use them in softlist or -cart
	//ROM_LOAD16_WORD( "pac-02.bin",   0x1000<<1, 0x1000, CRC(fe3213be) SHA1(5b9c407fe86865f3454d4be824a7f2bf53478f73))
	//ROM_LOAD16_WORD( "pac-03.bin",   0x1000<<1, 0x1000, CRC(f81f04bd) SHA1(82e2a0fda1787d5835c457ee5745b0db0cebe079))
	//ROM_LOAD16_WORD( "pac-04.bin",   0x1000<<1, 0x1000, CRC(cac09841) SHA1(bc9db83f26ed0810938156db6b104b4576754225))
	//ROM_LOAD16_WORD( "pac-05.bin",   0x1000<<1, 0x1000, CRC(d54a6090) SHA1(e85593096f43dcf14b08fd2c9fda277008a8df8b))
ROM_END

} // anonymous namespace


CONS( 1977, unichamp, 0, 0, unichamp, unichamp, unichamp_state, empty_init, "Unisonic", "Champion 2711", 0/*MACHINE_IMPERFECT_GRAPHICS*/ )



unior.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/************************************************************************************************

Unior

2009-05-12 Skeleton driver.
2013-10-09 Added DMA and CRTC
2013-10-11 Added PPI, PIT, UART, sound

Some info obtained from EMU-80.
The schematic is difficult to read, and some code is guesswork.

The monitor will only allow certain characters to be typed, thus the
modifier keys appear to do nothing. There is no need to use the enter
key; using spacebar and the correct parameters is enough.

If you press Shift, indicators for numlock and capslock will appear.

Monitor commands:
C - ?
D - hex dump
E - save
F - fill memory
G - go
H - set register
I - load
J - modify memory
K - ?
L - list registers
M - ?

ToDo:
- Colour - created by PROM D9 (type K555PT4) - we need a proper dump of it.

**********************************************************************************************/

#include "emu.h"

#include "cpu/i8085/i8085.h"
#include "imagedev/cassette.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/i8257.h"
#include "machine/pit8253.h"
#include "machine/timer.h"
#include "sound/spkrdev.h"
#include "video/i8275.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class unior_state : public driver_device
{
public:
	unior_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_pit(*this, "pit")
		, m_dma(*this, "dma")
		, m_uart(*this, "uart")
		, m_cass(*this, "cassette")
		, m_palette(*this, "palette")
		, m_p_chargen(*this, "chargen")
		, m_io_keyboard(*this, "X%d", 0U)
	{ }

	void unior(machine_config &config);

private:
	void vram_w(offs_t offset, u8 data);
	void scroll_w(u8 data);
	u8 ppi0_b_r();
	void ppi0_b_w(u8 data);
	u8 ppi1_a_r();
	u8 ppi1_b_r();
	u8 ppi1_c_r();
	void ppi1_a_w(u8 data);
	void ppi1_c_w(u8 data);
	void hrq_w(int state);
	void ctc_z1_w(int state);
	void unior_palette(palette_device &palette) const;
	u8 dma_r(offs_t offset);
	I8275_DRAW_CHARACTER_MEMBER(display_pixels);
	TIMER_DEVICE_CALLBACK_MEMBER(kansas_r);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	u8 m_4c = 0U;
	u8 m_4e = 0U;
	bool m_txe = false, m_txd = false, m_rts = false, m_casspol = false;
	u8 m_cass_data[4]{};
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	std::unique_ptr<u8[]> m_vram;
	memory_passthrough_handler m_rom_shadow_tap;
	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_device<pit8253_device> m_pit;
	required_device<i8257_device> m_dma;
	required_device<i8251_device> m_uart;
	required_device<cassette_image_device> m_cass;
	required_device<palette_device> m_palette;
	required_region_ptr<u8> m_p_chargen;
	required_ioport_array<11> m_io_keyboard;
};

void unior_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xf7ff).ram().share("mainram");
	map(0xf800, 0xffff).rom().region("maincpu", 0).w(FUNC(unior_state::vram_w)); // main video
}

void unior_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x30, 0x38).rw(m_dma, FUNC(i8257_device::read), FUNC(i8257_device::write)); // dma data
	map(0x3c, 0x3f).rw("ppi0", FUNC(i8255_device::read), FUNC(i8255_device::write)); // cassette player control
	map(0x4c, 0x4f).rw("ppi1", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x50, 0x50).w(FUNC(unior_state::scroll_w));
	map(0x60, 0x61).rw("crtc", FUNC(i8275_device::read), FUNC(i8275_device::write));
	map(0xdc, 0xdf).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xec, 0xed).rw(m_uart, FUNC(i8251_device::read), FUNC(i8251_device::write));
}

/* Input ports */
static INPUT_PORTS_START( unior )
	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED) // nothing
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED) // nothing
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)

	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(27) // cancel input
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR(9) // Russian little M
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Rus/Lat") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NUMLK") PORT_CODE(KEYCODE_NUMLOCK) // switches 1st indicator between N and B - numlock? rctrl
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK)

	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') // ;
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CAPS") PORT_CODE(KEYCODE_CAPSLOCK) // switches 2nd indicator between U and B - capslock? rshift
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD)

	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) // A
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('^') // ^ (')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)

	PORT_START("X4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) // B
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) // H
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) // I

	PORT_START("X5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) // C
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BSPACE") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) // G
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LF") PORT_CODE(KEYCODE_HOME) PORT_CHAR(10) // line feed?

	PORT_START("X6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR(0xA4)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) // Russian A
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) // ? (/)

	PORT_START("X7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) // D
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) // Russian bl
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) // Russian signpost
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED) // } (<>)

	PORT_START("X8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) // E
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) PORT_CHAR('>') // >
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("= :") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(':') // :
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("- =") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('=') // =

	PORT_START("X9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) // Russian U
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')

	PORT_START("X10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('@') // (`)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR('<') // <
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
INPUT_PORTS_END


/*************************************************

    Video

*************************************************/

/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{  0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_unior )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END

void unior_state::vram_w(offs_t offset, u8 data)
{
	m_vram[offset] = data;
}

// pulses a 1 to scroll
void unior_state::scroll_w(u8 data)
{
	if (data)
		memmove(m_vram.get(), m_vram.get()+80, 24*80);
}

I8275_DRAW_CHARACTER_MEMBER(unior_state::display_pixels)
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u8 gfx = m_p_chargen[(linecount & 7) | (charcode << 3)];

	using namespace i8275_attributes;

	if (BIT(attrcode, VSP))
		gfx = 0;

	if (BIT(attrcode, LTEN))
		gfx = 0xff;

	if (BIT(attrcode, RVV))
		gfx ^= 0xff;

	bool hlgt = BIT(attrcode, HLGT);
	for(u8 i=0;i<6;i++)
		bitmap.pix(y, x + i) = palette[BIT(gfx, 5-i) ? (hlgt ? 2 : 1) : 0];
}

static constexpr rgb_t unior_pens[3] =
{
	{ 0x00, 0x00, 0x00 }, // black
	{ 0xa0, 0xa0, 0xa0 }, // white
	{ 0xff, 0xff, 0xff }  // highlight
};

void unior_state::unior_palette(palette_device &palette) const
{
	palette.set_pen_colors(0, unior_pens);
}


/*************************************************

    i8255

*************************************************/
TIMER_DEVICE_CALLBACK_MEMBER( unior_state::kansas_r )
{
	if (m_rts)
	{
		m_cass_data[1] = m_cass_data[2] = m_cass_data[3] = 0;
		m_casspol = 1;
		return;
	}

	m_cass_data[1]++;
	m_cass_data[2]++;

	u8 cass_ws = (m_cass->input() > +0.04) ? 1 : 0;

	if (cass_ws != m_cass_data[0])
	{
		m_cass_data[0] = cass_ws;
		if (m_cass_data[1] > 13)
			m_casspol ^= 1;
		m_cass_data[1] = 0;
		m_cass_data[2] = 0;
		m_uart->write_rxd(m_casspol);
	}
	if ((m_cass_data[2] & 7)==2)
	{
		m_cass_data[3]++;
		m_uart->write_rxc(BIT(m_cass_data[3], 0));
	}
}

void unior_state::ctc_z1_w(int state)
{
	// write - incoming 2400Hz
	m_uart->write_txc(state);
	if (!m_txe)
	{
		m_cass->output((m_txd ^ state) ? -1.0 : 1.0);
	}

	// read - incoming 3202Hz
}


u8 unior_state::ppi0_b_r()
{
	return 0;
}

// Bit 4 - cassette relay?
void unior_state::ppi0_b_w(u8 data)
{
}

u8 unior_state::ppi1_a_r()
{
	return m_4c;
}

u8 unior_state::ppi1_b_r()
{
	u8 t = m_4c & 15;
	if (t < 11)
		return m_io_keyboard[t]->read();
	else
		return 0xff;
}

u8 unior_state::ppi1_c_r()
{
	return m_4e;
}

void unior_state::ppi1_a_w(u8 data)
{
	m_4c = data;
}

/*
d0,1,2 = connect to what might be a 74LS138, then to an external slot
d4 = speaker gate
d5 = unknown
d6 = connect to A7 of the palette prom
d7 = not used
*/
void unior_state::ppi1_c_w(u8 data)
{
	m_4e = data;
	m_pit->write_gate2(BIT(data, 4));
}

/*************************************************

    i8257

*************************************************/

u8 unior_state::dma_r(offs_t offset)
{
	if (offset < 0xf800)
		return m_maincpu->space(AS_PROGRAM).read_byte(offset);
	else
		return m_vram[offset & 0x7ff];
}

void unior_state::hrq_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state);
	m_dma->hlda_w(state);
}


/*************************************************

    Machine

*************************************************/

void unior_state::machine_reset()
{
	m_uart->write_cts(0);
	m_uart->write_dsr(0);
	m_casspol = 0;
	m_cass_data[0] = m_cass_data[1] = 0;

	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x0000, 0x07ff, m_rom);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0xf800, 0xffff,
			"rom_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// reinstall RAM over the ROM shadow
					m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x07ff, m_ram);
				}
			},
			&m_rom_shadow_tap);
}

void unior_state::machine_start()
{
	m_vram = make_unique_clear<u8[]>(0x0800);
	save_pointer(NAME(m_vram), 0x0800);
	save_item(NAME(m_4c));
	save_item(NAME(m_4e));
	save_item(NAME(m_txe));
	save_item(NAME(m_txd));
	save_item(NAME(m_rts));
	save_item(NAME(m_casspol));
	save_item(NAME(m_cass_data));
}

void unior_state::unior(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, XTAL(20'000'000) / 9);
	m_maincpu->set_addrmap(AS_PROGRAM, &unior_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &unior_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_size(640, 200);
	screen.set_visarea(0, 640-1, 0, 200-1);
	screen.set_screen_update("crtc", FUNC(i8275_device::screen_update));
	GFXDECODE(config, "gfxdecode", m_palette, gfx_unior);
	PALETTE(config, m_palette, FUNC(unior_state::unior_palette), 3);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.15);
	TIMER(config, "kansas_r").configure_periodic(FUNC(unior_state::kansas_r), attotime::from_hz(38400));

	/* Devices */
	I8251(config, m_uart, 20_MHz_XTAL / 9);
	m_uart->txd_handler().set([this] (bool state) { m_txd = state; });
	m_uart->txempty_handler().set([this] (bool state) { m_txe = state; });
	m_uart->rts_handler().set([this] (bool state) { m_rts = state; });

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(20_MHz_XTAL / 12);
	m_pit->set_clk<1>(20_MHz_XTAL / 9);
	m_pit->out_handler<1>().set(FUNC(unior_state::ctc_z1_w));
	m_pit->set_clk<2>(20_MHz_XTAL / 9 / 64); // unknown frequency
	m_pit->out_handler<2>().set("speaker", FUNC(speaker_sound_device::level_w));

	i8255_device &ppi0(I8255(config, "ppi0"));
	// ports a & c connect to an external slot
	ppi0.in_pb_callback().set(FUNC(unior_state::ppi0_b_r));
	ppi0.out_pb_callback().set(FUNC(unior_state::ppi0_b_w));

	i8255_device &ppi1(I8255(config, "ppi1"));
	// ports a & b are for the keyboard
	// port c operates various control lines for mostly unknown purposes
	ppi1.in_pa_callback().set(FUNC(unior_state::ppi1_a_r));
	ppi1.in_pb_callback().set(FUNC(unior_state::ppi1_b_r));
	ppi1.in_pc_callback().set(FUNC(unior_state::ppi1_c_r));
	ppi1.out_pa_callback().set(FUNC(unior_state::ppi1_a_w));
	ppi1.out_pc_callback().set(FUNC(unior_state::ppi1_c_w));

	I8257(config, m_dma, XTAL(20'000'000) / 9);
	m_dma->out_hrq_cb().set(FUNC(unior_state::hrq_w));
	m_dma->in_memr_cb().set(FUNC(unior_state::dma_r));
	m_dma->out_iow_cb<2>().set("crtc", FUNC(i8275_device::dack_w));

	i8275_device &crtc(I8275(config, "crtc", XTAL(20'000'000) / 12));
	crtc.set_character_width(6);
	crtc.set_display_callback(FUNC(unior_state::display_pixels));
	crtc.drq_wr_callback().set(m_dma, FUNC(i8257_device::dreq2_w));
	crtc.set_screen("screen");
}

/* ROM definition */
ROM_START( unior )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "unior.rom.d30", 0x0000, 0x0800, CRC(23a347e8) SHA1(2ef3134e2f4a696c3b52a145fa5a2d4c3487194b))

	ROM_REGION( 0x0840, "chargen", 0 )
	ROM_LOAD( "unior.fnt.d5",   0x0000, 0x0800, CRC(4f654828) SHA1(8c0ac11ea9679a439587952e4908940b67c4105e))
	// according to schematic this should be 256 bytes
	ROM_LOAD( "palette.rom.d9", 0x0800, 0x0040, BAD_DUMP CRC(b4574ceb) SHA1(f7a82c61ab137de8f6a99b0c5acf3ac79291f26a))
ROM_END

} // anonymous namespace

/* Driver */

/*    YEAR  NAME   PARENT   COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY      FULLNAME  FLAGS */
COMP( 19??, unior, 0,       0,      unior,   unior, unior_state, empty_init, "<unknown>", "Unior",  MACHINE_WRONG_COLORS | MACHINE_SUPPORTS_SAVE )



unistar.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

        Callan Unistar Terminal

        2009-12-09 Skeleton driver.

        Chips used: i8275, AM9513, AM8085A-2, i8237, i8255, 2x 2651. XTAL 20MHz

****************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/am9513.h"
#include "machine/am9517a.h"
#include "machine/com8116.h"
#include "machine/i8255.h"
#include "machine/input_merger.h"
#include "machine/scn_pci.h"
#include "video/i8275.h"
#include "emupal.h"
#include "screen.h"


namespace {

class unistar_state : public driver_device
{
public:
	unistar_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_chargen(*this, "chargen")
		, m_palette(*this, "palette")
	{ }

	void unistar(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	u8 dma_mem_r(offs_t offset);
	void dma_mem_w(offs_t offset, u8 data);

	void unistar_palette(palette_device &palette) const;
	I8275_DRAW_CHARACTER_MEMBER(draw_character);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_chargen;
	required_device<palette_device> m_palette;
};


u8 unistar_state::dma_mem_r(offs_t offset)
{
	return m_maincpu->space(AS_PROGRAM).read_byte(offset);
}

void unistar_state::dma_mem_w(offs_t offset, u8 data)
{
	m_maincpu->space(AS_PROGRAM).write_byte(offset, data);
}

void unistar_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x2fff).rom();
	map(0x8000, 0x97ff).ram();
}

void unistar_state::io_map(address_map &map)
{
	//map.unmap_value_high();
	map(0x00, 0x0f).rw("dmac", FUNC(am9517a_device::read), FUNC(am9517a_device::write));
	map(0x80, 0x83).rw("pcia", FUNC(scn2651_device::read), FUNC(scn2651_device::write));
	map(0x84, 0x84).portr("CONFIG");
	map(0x8c, 0x8d).rw("stc", FUNC(am9513_device::read8), FUNC(am9513_device::write8));
	map(0x90, 0x93).rw("pcib", FUNC(scn2651_device::read), FUNC(scn2651_device::write));
	map(0x94, 0x97).rw("pio", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x98, 0x99).rw("crtc", FUNC(i8275_device::read), FUNC(i8275_device::write));
	map(0x9c, 0x9c).w("dbrg", FUNC(k1135ab_device::str_stt_w));
}

/* Input ports */
static INPUT_PORTS_START( unistar )
	PORT_START("SW2")
	PORT_DIPNAME(0x0f, 0x00, "Baud Rate") PORT_DIPLOCATION("SW2:1,2,3,4")
	PORT_DIPSETTING(0x0e, "50")
	PORT_DIPSETTING(0x0d, "75")
	PORT_DIPSETTING(0x0c, "110")
	PORT_DIPSETTING(0x0b, "134.5")
	PORT_DIPSETTING(0x0a, "150")
	PORT_DIPSETTING(0x09, "300")
	PORT_DIPSETTING(0x08, "600")
	PORT_DIPSETTING(0x07, "1200")
	PORT_DIPSETTING(0x06, "1800")
	PORT_DIPSETTING(0x05, "2000")
	PORT_DIPSETTING(0x04, "2400")
	PORT_DIPSETTING(0x03, "3600")
	PORT_DIPSETTING(0x02, "4800")
	PORT_DIPSETTING(0x01, "7200")
	PORT_DIPSETTING(0x00, "9600")
	PORT_DIPNAME(0x30, 0x00, "Parity") PORT_DIPLOCATION("SW2:5,6")
	PORT_DIPSETTING(0x00, "None")
	PORT_DIPSETTING(0x30, "Even")
	PORT_DIPSETTING(0x10, "Odd")
	PORT_DIPNAME(0x40, 0x00, "Data Bits") PORT_DIPLOCATION("SW2:7")
	PORT_DIPSETTING(0x40, "7")
	PORT_DIPSETTING(0x00, "8")
	PORT_DIPNAME(0x80, 0x00, "Terminal Mode") PORT_DIPLOCATION("SW2:8")
	PORT_DIPSETTING(0x80, "Local/Test")
	PORT_DIPSETTING(0x00, "Online")

	PORT_START("CONFIG")
	PORT_DIPNAME(0x01, 0x01, "Screen Refresh Rate")
	PORT_DIPSETTING(0x01, "50 Hz")
	PORT_DIPSETTING(0x00, "60 Hz")
	PORT_BIT(0xfe, 0xfe, IPT_UNKNOWN)
INPUT_PORTS_END


void unistar_state::machine_reset()
{
}

void unistar_state::unistar_palette(palette_device &palette) const
{
	palette.set_pen_color(0, 0,   0, 0); // Black
	palette.set_pen_color(1, 0, 255, 0); // Full
	palette.set_pen_color(2, 0, 128, 0); // Dimmed
}

I8275_DRAW_CHARACTER_MEMBER(unistar_state::draw_character)
{
	// This code just a guess, so that we can see something
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u8 gfx = m_chargen[(linecount & 15) | (charcode << 4)];

	using namespace i8275_attributes;

	if (BIT(attrcode, VSP))
		gfx = 0;

	if (BIT(attrcode, LTEN))
		gfx = 0xff;

	if (BIT(attrcode, RVV))
		gfx ^= 0xff;

	bool hlgt = BIT(attrcode, HLGT);
	for(u8 i=0;i<8;i++)
		bitmap.pix(y, x + i) = palette[BIT(gfx, 7-i) ? (hlgt ? 2 : 1) : 0];
}

/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 16,                  /* 8 x 16 characters */
	128,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{  0*8,  1*8,  2*8,  3*8,  4*8,  5*8,  6*8,  7*8, 8*8,  9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_unistar )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END

void unistar_state::unistar(machine_config &config)
{
	/* basic machine hardware */
	I8085A(config, m_maincpu, 20_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &unistar_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &unistar_state::io_map);

	INPUT_MERGER_ANY_HIGH(config, "rst65").output_handler().set_inputline(m_maincpu, I8085_RST65_LINE);
	INPUT_MERGER_ANY_HIGH(config, "rst75").output_handler().set_inputline(m_maincpu, I8085_RST75_LINE);

	am9517a_device &dmac(AM9517A(config, "dmac", 20_MHz_XTAL / 4)); // Intel P8237A-5
	dmac.out_hreq_callback().set_inputline(m_maincpu, INPUT_LINE_HALT);
	dmac.out_hreq_callback().append("dmac", FUNC(am9517a_device::hack_w));
	dmac.out_eop_callback().set("rst75", FUNC(input_merger_device::in_w<1>));
	dmac.in_memr_callback().set(FUNC(unistar_state::dma_mem_r));
	dmac.out_memw_callback().set(FUNC(unistar_state::dma_mem_w));
	dmac.out_iow_callback<2>().set("crtc", FUNC(i8275_device::dack_w));

	am9513_device &stc(AM9513(config, "stc", 8_MHz_XTAL));
	stc.fout_cb().set("stc", FUNC(am9513_device::source1_w));
	// TODO: figure out what OUT1-OUT4 should do (timer 5 is unused)

	scn2651_device &pcia(SCN2651(config, "pcia", 20_MHz_XTAL / 4));
	pcia.rxrdy_handler().set("rst65", FUNC(input_merger_device::in_w<0>));
	pcia.txrdy_handler().set("rst65", FUNC(input_merger_device::in_w<1>));

	scn2651_device &pcib(SCN2651(config, "pcib", 20_MHz_XTAL / 4));
	pcib.rxrdy_handler().set_inputline(m_maincpu, I8085_RST55_LINE);

	k1135ab_device &dbrg(K1135AB(config, "dbrg", 5.0688_MHz_XTAL));
	dbrg.fr_handler().set("pcia", FUNC(scn2651_device::rxc_w));
	dbrg.fr_handler().append("pcia", FUNC(scn2651_device::txc_w));
	dbrg.ft_handler().set("pcib", FUNC(scn2651_device::rxc_w));
	dbrg.ft_handler().append("pcib", FUNC(scn2651_device::txc_w));

	i8255_device &pio(I8255A(config, "pio"));
	pio.in_pa_callback().set_ioport("SW2");

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(20_MHz_XTAL, 954, 0, 720, 351, 0, 325);
	//screen.set_raw(20_MHz_XTAL, 990, 0, 720, 405, 0, 375);
	screen.set_screen_update("crtc", FUNC(i8275_device::screen_update));

	i8275_device &crtc(I8275(config, "crtc", 20_MHz_XTAL / 9));
	crtc.set_character_width(9);
	crtc.set_display_callback(FUNC(unistar_state::draw_character));
	crtc.set_screen("screen");
	crtc.drq_wr_callback().set("dmac", FUNC(am9517a_device::dreq2_w));
	crtc.irq_wr_callback().set("rst75", FUNC(input_merger_device::in_w<0>));

	GFXDECODE(config, "gfxdecode", "palette", gfx_unistar);
	PALETTE(config, m_palette, FUNC(unistar_state::unistar_palette), 3);
}

/* ROM definition */
ROM_START( unistar )
	ROM_REGION( 0x3000, "maincpu", 0 )
	ROM_LOAD( "280010c.u48", 0x0000, 0x1000, CRC(613ef521) SHA1(a77459e91617d2882778ab2dada74fcb5f44e949))
	ROM_LOAD( "280011c.u49", 0x1000, 0x1000, CRC(6cc5e704) SHA1(fb93645f51d5ad0635cbc8a9174c61f96799313d))
	ROM_LOAD( "280012c.u50", 0x2000, 0x1000, CRC(0b9ca5a5) SHA1(20bf4aeacda14ff7a3cf988c7c0bff6ec60406c7))

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "280014a.u1",  0x0000, 0x0800, CRC(a9e1b5b2) SHA1(6f5b597ee1417f1108ac5957b005a927acb5314a))
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY                FULLNAME                FLAGS
COMP( 198?, unistar, 0,      0,      unistar, unistar, unistar_state, empty_init, "Callan Data Systems", "Unistar 200 Terminal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



univac.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert,Vas Crabb
/***************************************************************************

Univac Terminals

2009-05-25 Skeleton driver

The terminals are models UTS10, UTS20, UTS30, UTS40, UTS50 and SVT1120.

There were other terminals (Uniscope 100/200/300/400) and UTS60, but
they had different hardware. Uniscope models are believed to use the i8080,
and the UTS60 was a colour graphics terminal with a MC68000 and 2 floppy drives.

The terminal has 2 screens selectable by the operator with the Fn + 1-2
buttons. Thus the user can have two sessions open at once, to different
mainframes or applications. The keyboard connected to the terminal with
a coiled cord and a 9-pin D-connector.

Sound is a beeper.

This driver is all guesswork; Unisys never released technical info
to customers. All parts on the PCBs have internal Unisys part numbers
instead of the manufacturer's numbers.

Notes:
* Port $C6 probably controls serial loopback
  - at a guess, bit 0 enables loopback on both channels
* The NVRAM is 4 bits wide on the LSBs, but (0x81) & 0x10 does something
  - NVRAM nybbles are read/written on the LSBs of 64 ports 0x80 to 0xb4
  - Nybbles are packed/unpacked into 32 bytes starting at 0xd7d7
  - On boot it reads (0x81) & 0x10, and if set preserves 0xd831 to 0xd863
  - This has to be some kind of warm boot detection, but how does it work?

You can use a debug trick to get UTS10 to boot:
- When it loops at @0B33, pc = B35 and g

How to create a FCC (field control code):
- Move the cursor to where you want the FCC to be
- Press FCC GEN
- Now you enter a sequence of 4 bytes
- 1. Video Attribute
- - Spacebar or N: Normal
- - L: Low intensity
- - O: Off
- - B: Blink (low-half)
- - 1: Rev-video/Normal
- - 2: Rev-video/half-intensity
- - 3: Rev-video/blink: normal-half
- - 4: Rev-video/blink: low
- 2. Tab-stop
- - Spacebar or S: No tab-stop
- - T: Tab-stop
- - 6: Tab-stop protected
- - 7: No tab protected
- 3. Data-entry control
- - Spacebar or U: Unprotected
- - P: Protected
- - A: Alpha only
- - N: Numeric only
- 4. Justified
- - Spacebar: Normal
- - R: Right-justified
- Press Spacebar to enable the new FCC and exit back to normal.

Control-page parameters. These vary depending on the terminal and feature set. Press FCTN and CTRL PAGE keys together.
You get a protected area covering the first 2 lines where you can configure the terminal. Settings are saved in the NVRAM.
Depending on the setting, it may take effect immediately (after exiting the control page), or after a reboot.
Entries may be in upper or lower case.
UC/NO : Upper and lower case can be entered
UC/YS : Lower case is automatically folded to upper case.
AB/LI : Alternate brightness is Low Intensity
AB/RV : Alternate brightness is Reverse Video
AB/NI : Alternate brightness is Normal Intensity
IL/RV : Indicator Line is Reverse Video
IL/NI : Indicator Line is Normal Intensity
KK/ON : Keyclick on
KK/OF : Keyclick off
SP/NS : Non-destructive spacebar (works like right-arrow)
SP/DS : Destructive spacebar
VO/01 : Video off after 1 minute (a blank screen saver)
VO/04 : Video off after 4 minutes
VO/16 : Video off after 16 minutes
VO/64 : Video off after 64 minutes
CC/ON : Control characters show
CC/OF : Control characters off (look like a space)
CS/LO : Cursor repeat slow
CS/HI : Cursor repeat fast
RI/xx : Set the RID (generally 21-2F)
SI/xx : Set the SID (generally 51-7F)
After entering the characters, press FCTN and CTRL PAGE keys again to save the setting.


****************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "bus/uts_kbd/uts_kbd.h"
#include "cpu/z80/z80.h"
#include "machine/74259.h"
#include "machine/z80daisy.h"
#include "machine/clock.h"
#include "machine/nvram.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "sound/spkrdev.h"
#include "video/dp8350.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#define LOG_PARITY  (1U << 1)
#define LOG_NVRAM   (1U << 2)

//#define VERBOSE (LOG_GENERAL | LOG_PARITY | LOG_NVRAM)
#include "logmacro.h"

#define LOGPARITY(...)  LOGMASKED(LOG_PARITY, __VA_ARGS__)
#define LOGNVRAM(...)   LOGMASKED(LOG_NVRAM, __VA_ARGS__)


namespace {

class univac_state : public driver_device
{
public:
	univac_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_nvram(*this, "nvram")
		, m_ctc(*this, "ctc")
		, m_keybclk(*this, "keybclk")
		, m_sio(*this, "sio")
		, m_alarm(*this, "alarm")
		, m_screen(*this, "screen")
		, m_palette(*this, "palette")
		, m_keyboard(*this, "keyboard")
		, m_printer(*this, "printer")
		, m_p_chargen(*this, "chargen")
		, m_p_videoram(*this, "videoram")
		, m_p_nvram(*this, "nvram")
		, m_parity_poison(false)
		, m_display_enable(false)
		, m_nvram_protect(false)
		, m_alarm_enable(false)
		, m_alarm_toggle(false)
		, m_loopback_control(false)
		, m_comm_rxd(true)
		, m_sio_txda(true)
		, m_aux_rxd(true)
		, m_sio_txdb(true)
		, m_sio_rtsb(true)
		, m_aux_dsr(true)
		, m_sio_wrdyb(true)
	{ }

	void uts10(machine_config &config);
	void uts20(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	u8 ram_r(offs_t offset);
	u8 bank_r(offs_t offset);
	void ram_w(offs_t offset, u8 data);
	void bank_w(offs_t offset, u8 data);
	void nvram_w(offs_t offset, u8 data);

	void nvram_protect_w(int state);
	void select_disp_w(int state);
	void ram_control_w(int state);
	void parity_poison_w(int state);
	void display_enable_w(int state);
	void alarm_enable_w(int state);
	void sio_loopback_w(int state);
	void sio_txda_w(int state);
	void sio_txdb_w(int state);
	void aux_rxd_w(int state);
	void sio_rtsb_w(int state);
	void sio_wrdyb_w(int state);
	void aux_dsr_w(int state);
	void loopback_rxcb_w(int state);
	void porte6_w(int state);

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	void uts10_io_map(address_map &map) ATTR_COLD;
	void uts10_map(address_map &map) ATTR_COLD;

	required_device<z80_device>     m_maincpu;
	required_device<nvram_device>   m_nvram;
	required_device<z80ctc_device>  m_ctc;
	optional_device<clock_device>   m_keybclk;
	required_device<z80sio_device>  m_sio;
	required_device<speaker_sound_device> m_alarm;
	required_device<screen_device>  m_screen;
	required_device<palette_device> m_palette;

	required_device<uts_keyboard_port_device> m_keyboard;
	required_device<rs232_port_device> m_printer;

	required_region_ptr<u8> m_p_chargen;
	required_shared_ptr<u8> m_p_videoram;
	required_shared_ptr<u8> m_p_nvram;
	std::unique_ptr<u8 []>  m_p_parity;

	u16 m_disp_mask = 0U;
	u16 m_bank_mask = 0U;
	bool m_parity_poison = false;
	bool m_display_enable = false;
	u8 m_framecnt = 0U;
	bool m_nvram_protect = false;

	bool m_alarm_enable = false;
	bool m_alarm_toggle = false;

	bool m_loopback_control = false;
	bool m_comm_rxd = false;
	bool m_sio_txda = false;
	bool m_aux_rxd = false;
	bool m_sio_txdb = false;
	bool m_sio_rtsb = false;
	bool m_aux_dsr = false;
	bool m_sio_wrdyb = false;
};



u8 univac_state::ram_r(offs_t offset)
{
	if (BIT(m_p_parity[offset >> 3], offset & 0x07) && !machine().side_effects_disabled())
	{
		LOGPARITY("parity check failed offset = %04X\n", offset);
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
	}
	return m_p_videoram[offset];
}

u8 univac_state::bank_r(offs_t offset)
{
	return ram_r(offset ^ m_bank_mask);
}

void univac_state::ram_w(offs_t offset, u8 data)
{
	if (m_parity_poison)
	{
		LOGPARITY("poison parity offset = %04X\n", offset);
		m_p_parity[offset >> 3] |= u8(1) << (offset & 0x07);
	}
	else
	{
		m_p_parity[offset >> 3] &= ~(u8(1) << (offset & 0x07));
	}
	m_p_videoram[offset] = data;
}

void univac_state::bank_w(offs_t offset, u8 data)
{
	ram_w(offset ^ m_bank_mask, data);
}

void univac_state::nvram_w(offs_t offset, u8 data)
{
	// NVRAM is four bits wide, accessed in the low nybble
	// It's simplest to hack it when writing to make the upper bits read back high on the open bus
	// (But is it all open bus? Bit 4 is specifically tested in a few places...)
	if (m_nvram_protect)
		LOGNVRAM("%s: NVRAM write suppressed (address %02X, data %02X)\n", machine().describe_context(), offset + 0x80, data);
	else
		m_p_nvram[offset] = data | 0xf0;
}

void univac_state::nvram_protect_w(int state)
{
	// There seems to be some timing-based write protection related to the CTC's TRG0 input.
	// The present implementation is a crude approximation of a wild guess.
	if (state)
	{
		m_nvram_protect = m_screen->vpos() < 10;

		if (m_alarm_enable)
		{
			m_alarm_toggle = !m_alarm_toggle;
			m_alarm->level_w(m_alarm_toggle);
		}
	}
}

void univac_state::select_disp_w(int state)
{
	m_disp_mask = state ? 0x2000 : 0x0000;
}

void univac_state::ram_control_w(int state)
{
	m_bank_mask = state ? 0x2000 : 0x0000;
}

void univac_state::parity_poison_w(int state)
{
	m_parity_poison = state;
}

void univac_state::display_enable_w(int state)
{
	m_display_enable = state;
}

void univac_state::alarm_enable_w(int state)
{
	m_alarm_enable = state;
	if (!state)
	{
		m_alarm_toggle = false;
		m_alarm->level_w(0);
	}
}

void univac_state::sio_loopback_w(int state)
{
	if (state)
	{
		m_sio->rxa_w(m_sio_txda);
		m_sio->rxb_w(m_sio_txdb);
		m_sio->dcdb_w(m_sio_wrdyb);
		m_sio->ctsb_w(m_sio_wrdyb);
		m_sio->syncb_w(!m_sio_rtsb);
		m_printer->write_txd(1);
		m_printer->write_rts(1);
		m_keyboard->ready_w(0);
		if (m_keybclk.found())
			m_keybclk->set_clock_scale(0.0);
	}
	else
	{
		m_sio->rxa_w(m_comm_rxd);
		m_sio->rxb_w(m_aux_rxd);
		m_sio->dcdb_w(m_aux_dsr);
		m_sio->ctsb_w(m_aux_dsr); // likely ignored
		m_sio->syncb_w(1);
		m_printer->write_txd(m_sio_txdb);
		m_printer->write_rts(m_sio_rtsb);
		m_keyboard->ready_w(m_sio_wrdyb);
		if (m_keybclk.found())
			m_keybclk->set_clock_scale(1.0);
	}

	m_loopback_control = state;
}

void univac_state::sio_txda_w(int state)
{
	m_sio_txda = state;
	if (m_loopback_control)
		m_sio->rxa_w(state);
}

void univac_state::sio_txdb_w(int state)
{
	m_sio_txdb = state;
	if (m_loopback_control)
		m_sio->rxb_w(state);
	else
		m_printer->write_txd(state);
}

void univac_state::aux_rxd_w(int state)
{
	m_aux_rxd = state;
	if (!m_loopback_control)
		m_sio->rxb_w(state);
}

void univac_state::sio_rtsb_w(int state)
{
	m_sio_rtsb = state;
	if (m_loopback_control)
		m_sio->syncb_w(!state);
	else
		m_printer->write_rts(state);
}

void univac_state::sio_wrdyb_w(int state)
{
	m_sio_wrdyb = state;
	if (m_loopback_control)
	{
		m_sio->dcdb_w(state);
		m_sio->ctsb_w(state);
	}
	else
		m_keyboard->ready_w(state);
}

void univac_state::aux_dsr_w(int state)
{
	m_aux_dsr = state;
	if (!m_loopback_control)
	{
		m_sio->dcdb_w(state);
		m_sio->ctsb_w(state);
	}
}

void univac_state::loopback_rxcb_w(int state)
{
	if (m_loopback_control)
		m_sio->rxcb_w(state);
}

void univac_state::porte6_w(int state)
{
	//m_beep->set_state(state); // not sure what belongs here, but it isn't the beeper
}


void univac_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x4fff).rom().region("roms", 0);
	map(0x8000, 0xbfff).rw(FUNC(univac_state::bank_r), FUNC(univac_state::bank_w));
	map(0xc000, 0xffff).rw(FUNC(univac_state::ram_r), FUNC(univac_state::ram_w)).share("videoram");
}

void univac_state::uts10_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x4fff).rom().region("roms", 0);
	map(0x8000, 0x9fff).mirror(0x2000).rw(FUNC(univac_state::bank_r), FUNC(univac_state::bank_w));
	map(0xc000, 0xffff).rw(FUNC(univac_state::ram_r), FUNC(univac_state::ram_w)).share("videoram");
}

void univac_state::uts10_io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0x03).rw(m_sio, FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
	map(0x20, 0x23).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x60, 0x60).nopw(); // values written here may or may not matter
	map(0x80, 0xbf).ram().w(FUNC(univac_state::nvram_w)).share("nvram");
	map(0xc0, 0xc7).w("latch_c0", FUNC(ls259_device::write_d0));
	map(0xe0, 0xe7).w("latch_e0", FUNC(ls259_device::write_d0));
}

void univac_state::io_map(address_map &map)
{
	uts10_io_map(map);
	map(0x40, 0x40).nopr(); // read only once, during self-test; result is discarded
	map(0x40, 0x47).w("latch_40", FUNC(ls259_device::write_d0));
}

/* Input ports */
static INPUT_PORTS_START( uts20 )
INPUT_PORTS_END


void univac_state::machine_start()
{
	// D7DC and D7DD are checked for valid RID and SID (usually 21 and 51) if not valid then NVRAM gets initialised.

	std::size_t const parity_bytes = (m_p_videoram.bytes() + 7) / 8;
	m_p_parity.reset(new u8[parity_bytes]);
	std::fill_n(m_p_parity.get(), parity_bytes, 0);

	save_pointer(NAME(m_p_parity), parity_bytes);
	save_item(NAME(m_bank_mask));
	save_item(NAME(m_parity_poison));
	save_item(NAME(m_display_enable));
	save_item(NAME(m_framecnt));
	save_item(NAME(m_nvram_protect));
	save_item(NAME(m_alarm_enable));
	save_item(NAME(m_alarm_toggle));
	save_item(NAME(m_loopback_control));
	save_item(NAME(m_comm_rxd));
	save_item(NAME(m_sio_txda));
	save_item(NAME(m_aux_rxd));
	save_item(NAME(m_sio_txdb));
	save_item(NAME(m_sio_rtsb));
	save_item(NAME(m_aux_dsr));
	save_item(NAME(m_sio_wrdyb));
	save_item(NAME(m_disp_mask));

	m_disp_mask = 0;
}

uint32_t univac_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if (!m_display_enable)
	{
		bitmap.fill(0, cliprect);
		return 0;
	}

	const pen_t *pen = m_palette->pens();

	uint16_t sy=0,ma=0;

	m_framecnt++;

	for (u8 y = 0; y < 25; y++)
	{
		for (u8 ra = 0; ra < 14; ra++)
		{
			uint32_t *p = &bitmap.pix(sy++);

			for (uint16_t x = ma; x < ma + 80; x++)
			{
				u8 chr = ram_r(x ^ m_disp_mask);    // bit 7 = rv attribute (or dim, depending on control-page setting)

				uint16_t gfx = m_p_chargen[((chr & 0x7f)<<4) | ra];

				// chars 1C, 1D, 1F need special handling
				if ((chr >= 0x1c) && (chr <= 0x1f) && BIT(gfx, 7))
				{
					gfx &= 0x7f;
					if (m_framecnt & 16) // They also blink
						gfx = 0;
				}

				// reverse-video attribute
				if (BIT(chr, 7))
					gfx = ~gfx;

				/* Display a scanline of a character */
				for (int bit = 8; bit >= 0; bit--)
				{
					*p++ = pen[BIT(gfx, bit)];
				}
			}
		}
		ma += 80;
	}
	return 0;
}

/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 14,                   /* 8 x 14 characters */
	128,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_uts )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END

static const z80_daisy_config daisy_chain[] =
{
	{ "sio" },
	{ "ctc" },
	{ nullptr }
};

// All frequencies confirmed
void univac_state::uts20(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 18.432_MHz_XTAL / 6); // 3.072 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &univac_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &univac_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	ls259_device &latch_40(LS259(config, "latch_40")); // actual type and location unknown
	latch_40.q_out_cb<1>().set(FUNC(univac_state::select_disp_w));
	latch_40.q_out_cb<3>().set(FUNC(univac_state::ram_control_w));

	ls259_device &latch_c0(LS259(config, "latch_c0")); // actual type and location unknown
	latch_c0.q_out_cb<0>().set(FUNC(univac_state::alarm_enable_w));
	latch_c0.q_out_cb<3>().set(FUNC(univac_state::display_enable_w));
	latch_c0.q_out_cb<4>().set(FUNC(univac_state::parity_poison_w));
	latch_c0.q_out_cb<6>().set(FUNC(univac_state::sio_loopback_w));

	ls259_device &latch_e0(LS259(config, "latch_e0")); // actual type and location unknown
	//latch_e0.q_out_cb<2>().set(FUNC(univac_state::reverse_video_w));
	latch_e0.q_out_cb<5>().set("crtc", FUNC(dp835x_device::refresh_control)).invert();
	latch_e0.q_out_cb<6>().set(FUNC(univac_state::porte6_w));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER, rgb_t::green());
	m_screen->set_screen_update(FUNC(univac_state::screen_update));
	PALETTE(config, m_palette, palette_device::MONOCHROME);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_uts);

	dp835x_device &crtc(DP835X_A(config, "crtc", 19'980'000));
	crtc.set_screen("screen");
	crtc.vblank_callback().set(m_ctc, FUNC(z80ctc_device::trg0));
	crtc.vblank_callback().append(m_ctc, FUNC(z80ctc_device::trg3));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	Z80CTC(config, m_ctc, 18.432_MHz_XTAL / 6);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->set_clk<1>(18.432_MHz_XTAL / 12);
	m_ctc->set_clk<2>(18.432_MHz_XTAL / 12);
	m_ctc->zc_callback<0>().set(FUNC(univac_state::nvram_protect_w));
	m_ctc->zc_callback<1>().set(m_sio, FUNC(z80sio_device::txca_w));
	m_ctc->zc_callback<1>().append(m_sio, FUNC(z80sio_device::rxca_w));
	m_ctc->zc_callback<2>().set(m_sio, FUNC(z80sio_device::txcb_w));
	m_ctc->zc_callback<2>().append(FUNC(univac_state::loopback_rxcb_w));

	CLOCK(config, m_keybclk, 18.432_MHz_XTAL / 60);
	m_keybclk->signal_handler().set(m_sio, FUNC(z80sio_device::rxcb_w));

	Z80SIO(config, m_sio, 18.432_MHz_XTAL / 6);
	m_sio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_sio->out_txda_callback().set(FUNC(univac_state::sio_txda_w));
	m_sio->out_txdb_callback().set(FUNC(univac_state::sio_txdb_w));
	m_sio->out_rtsb_callback().set(FUNC(univac_state::sio_rtsb_w));
	m_sio->out_wrdyb_callback().set(FUNC(univac_state::sio_wrdyb_w));

	/* Sound */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_alarm).add_route(ALL_OUTPUTS, "mono", 0.05);

	UTS_KEYBOARD(config, m_keyboard, uts20_keyboards, "extw");
	m_keyboard->rxd_callback().set(FUNC(univac_state::aux_rxd_w));

	RS232_PORT(config, m_printer, default_rs232_devices, nullptr);
	m_printer->dcd_handler().set(FUNC(univac_state::aux_dsr_w));
}

void univac_state::uts10(machine_config &config)
{
	uts20(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &univac_state::uts10_map);
	m_maincpu->set_addrmap(AS_IO, &univac_state::uts10_io_map);

	config.device_remove("keybclk");
	m_ctc->zc_callback<2>().set(m_sio, FUNC(z80sio_device::rxtxcb_w));

	config.device_remove("latch_40");
	subdevice<ls259_device>("latch_c0")->q_out_cb<6>().set_nop();
	subdevice<ls259_device>("latch_e0")->q_out_cb<7>().set(FUNC(univac_state::sio_loopback_w)).invert();

	UTS_KEYBOARD(config.replace(), m_keyboard, uts10_keyboards, "extw");
	m_keyboard->rxd_callback().set(FUNC(univac_state::aux_rxd_w));
}


/* ROM definition */
ROM_START( uts10 )
	ROM_REGION( 0x5000, "roms", ROMREGION_ERASEFF )
	ROM_LOAD( "f3577_1.bin",  0x0000, 0x0800, CRC(f7d47484) SHA1(84c01d054df19e8da44c242a67d97f643bdabc4c) )
	ROM_LOAD( "f3577_2.bin",  0x0800, 0x0800, CRC(7c1045f0) SHA1(732e8c111a346476c59bcfda73f0f826cdcd7eb3) )
	ROM_LOAD( "f3577_3.bin",  0x1000, 0x0800, CRC(10f47af2) SHA1(a61b693af264bfa6565c43b4fe473833f8aba046) )
	ROM_LOAD( "f3577_4.bin",  0x1800, 0x0800, CRC(bed8924c) SHA1(1fe3e118cc1c17f4c8b9c0025257822b99fcde38) )
	ROM_LOAD( "f3577_5.bin",  0x2000, 0x0800, CRC(38d671b5) SHA1(3fb3feaaddb08af5ba50a9c08511cbb3949a7985) )
	ROM_LOAD( "f3577_6.bin",  0x2800, 0x0800, CRC(6dbe9c4a) SHA1(11bc4b7c99811bd26423a15b33d02a86fa0bfd17) )

	ROM_REGION( 0x0800, "chargen", 0 ) // possibly some bitrot, see h,m,n in F4 displayer
	ROM_LOAD( "chr_5565.bin", 0x0000, 0x0800, CRC(7d99744f) SHA1(2db330ca94a91f7b2ac2ac088ae9255f5bb0a7b4) )
ROM_END

ROM_START( uts20 )
	ROM_REGION( 0x5000, "roms", ROMREGION_ERASEFF )
	ROM_LOAD( "uts20a.rom", 0x0000, 0x1000, CRC(1a7b4b4e) SHA1(c3732e25b4b7c7a80172e3fe55c77b923cf511eb) )
	ROM_LOAD( "uts20b.rom", 0x1000, 0x1000, CRC(7f8de87b) SHA1(a85f404ad9d560df831cc3e651a4b45e4ed30130) )
	ROM_LOAD( "uts20c.rom", 0x2000, 0x1000, CRC(4e334705) SHA1(ff1a730551b42f29d20af8ecc4495fd30567d35b) )
	ROM_LOAD( "uts20d.rom", 0x3000, 0x1000, CRC(76757cf7) SHA1(b0509d9a35366b21955f83ec3685163844c4dbf1) )
	ROM_LOAD( "uts20e.rom", 0x4000, 0x1000, CRC(0dfc8062) SHA1(cd681020bfb4829d4cebaf1b5bf618e67b55bda3) )

	// character generator not dumped, using the one from 'UTS10' for now
	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "chr_5565.bin", 0x0000, 0x0800, BAD_DUMP CRC(7d99744f) SHA1(2db330ca94a91f7b2ac2ac088ae9255f5bb0a7b4) )
ROM_END

} // Anonymous namespace

/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS         INIT        COMPANY          FULLNAME  FLAGS
COMP( 1981, uts10, uts20,  0,      uts10,   uts20, univac_state, empty_init, "Sperry Univac", "UTS-10", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1980, uts20, 0,      0,      uts20,   uts20, univac_state, empty_init, "Sperry Univac", "UTS-20", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



unixpc.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Dirk Best, R. Belmont
/***************************************************************************

    AT&T UNIX PC series (7300 and 3B1)

    Skeleton driver by Dirk Best and R. Belmont

    DIVS instruction at 0x801112 (the second time) causes a divide-by-zero
    exception the system isn't ready for due to word at 0x5EA6 being zero.

    Code might not get there if the attempted FDC boot succeeds; FDC hookup
    probably needs help.  2797 isn't asserting DRQ?

***************************************************************************/


#include "emu.h"
#include "cpu/m68000/m68010.h"
#include "bus/centronics/ctronics.h"
#include "bus/rs232/rs232.h"
#include "imagedev/floppy.h"
#include "imagedev/harddriv.h"
#include "machine/6850acia.h"
#include "machine/74259.h"
#include "machine/input_merger.h"
#include "machine/output_latch.h"
#include "machine/ram.h"
//#include "machine/tc8250.h"
#include "machine/wd1010.h"
#include "machine/wd_fdc.h"
#include "machine/z80sio.h"
#include "emupal.h"
#include "screen.h"

#include "unixpc.lh"


/***************************************************************************
    DRIVER STATE
***************************************************************************/

namespace {

class unixpc_state : public driver_device
{
public:
	unixpc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_gcr(*this, "gcr"),
		m_tcr(*this, "tcr"),
		m_int02(*this, "int02"),
		m_ram(*this, RAM_TAG),
		m_wd2797(*this, "wd2797"),
		m_floppy(*this, "wd2797:0:525dd"),
		m_hdc(*this, "hdc"),
		m_hdr0(*this, "hdc:0"),
		m_ramromview(*this, "ramromview"),
		m_mapram(*this, "mapram"),
		m_videoram(*this, "videoram")
	{ }

	void unixpc(machine_config &config);

private:
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint16_t line_printer_r();
	void disk_control_w(uint8_t data);
	void gcr_w(offs_t offset, uint16_t data);
	void romlmap_w(int state);
	void error_enable_w(int state);
	void parity_enable_w(int state);
	void bpplus_w(int state);
	uint16_t ram_mmu_r(offs_t offset);
	void ram_mmu_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t gsr_r();
	void tcr_w(offs_t offset, uint16_t data);
	uint16_t tsr_r();
	uint16_t rtc_r();
	void rtc_w(uint16_t data);
	uint16_t diskdma_size_r();
	void diskdma_size_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	void diskdma_ptr_w(offs_t offset, uint16_t data);

	void wd2797_intrq_w(int state);
	void wd2797_drq_w(int state);

	void wd1010_intrq_w(int state);

	void unixpc_mem(address_map &map) ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<ls259_device> m_gcr;
	required_device<ls259_device> m_tcr;
	required_device<input_merger_device> m_int02;
	required_device<ram_device> m_ram;
	required_device<wd2797_device> m_wd2797;
	required_device<floppy_image_device> m_floppy;
	required_device<wd1010_device> m_hdc;
	required_device<harddisk_image_device> m_hdr0;
	memory_view m_ramromview;

	required_shared_ptr<uint16_t> m_mapram;
	required_shared_ptr<uint16_t> m_videoram;

	uint16_t *m_ramptr = nullptr;
	uint32_t m_ramsize = 0;
	uint16_t m_diskdmasize = 0;
	uint32_t m_diskdmaptr = 0;
	bool m_fdc_intrq = false;
	bool m_hdc_intrq = false;
};


/***************************************************************************
    MEMORY
***************************************************************************/

void unixpc_state::gcr_w(offs_t offset, uint16_t data)
{
	m_gcr->write_bit(offset >> 11, BIT(data, 15));
}

void unixpc_state::romlmap_w(int state)
{
	m_ramromview.select(state ? 1 : 0);
}

uint16_t unixpc_state::ram_mmu_r(offs_t offset)
{
	// TODO: MMU translation
	if (offset > m_ramsize)
	{
		return 0xffff;
	}
	return m_ramptr[offset];
}

void unixpc_state::ram_mmu_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	// TODO: MMU translation
	if (offset < m_ramsize)
	{
		COMBINE_DATA(&m_ramptr[offset]);
	}
}

void unixpc_state::machine_start()
{
	m_ramptr = (uint16_t *)m_ram->pointer();
	m_ramsize = m_ram->size();
}

void unixpc_state::machine_reset()
{
	disk_control_w(0);
}

void unixpc_state::error_enable_w(int state)
{
	logerror("error_enable_w: %d\n", state);
}

void unixpc_state::parity_enable_w(int state)
{
	logerror("parity_enable_w: %d\n", state);
}

void unixpc_state::bpplus_w(int state)
{
	logerror("bpplus_w: %d\n", state);
}

/***************************************************************************
    MISC
***************************************************************************/

uint16_t unixpc_state::gsr_r()
{
	return 0;
}

void unixpc_state::tcr_w(offs_t offset, uint16_t data)
{
	m_tcr->write_bit(offset >> 11, BIT(data, 14));
}

uint16_t unixpc_state::tsr_r()
{
	return 0;
}

uint16_t unixpc_state::rtc_r()
{
	return 0;
}

void unixpc_state::rtc_w(uint16_t data)
{
	logerror("rtc_w: %04x\n", data);
}

uint16_t unixpc_state::line_printer_r()
{
	uint16_t data = 0;

	data |= 1; // no dial tone detected
	data |= 1 << 1; // no parity error
	data |= m_hdc_intrq ? 1<<2 : 0<<2;
	data |= m_fdc_intrq ? 1<<3 : 0<<3;

	//logerror("line_printer_r: %04x\n", data);

	return data;
}

/***************************************************************************
    DMA
***************************************************************************/

uint16_t unixpc_state::diskdma_size_r()
{
	return m_diskdmasize;
}

void unixpc_state::diskdma_size_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	COMBINE_DATA( &m_diskdmasize );
	logerror("%x to disk DMA size\n", data);
}

void unixpc_state::diskdma_ptr_w(offs_t offset, uint16_t data)
{
	if (offset >= 0x2000)
	{
		// set top 4 bytes
		m_diskdmaptr &= 0xff;
		m_diskdmaptr |= (offset << 8);
	}
	else
	{
		m_diskdmaptr &= 0xffff00;
		m_diskdmaptr |= (offset & 0xff);
	}

	logerror("diskdma_ptr_w: wrote at %x, ptr now %x\n", offset<<1, m_diskdmaptr);
}

/***************************************************************************
    FLOPPY
***************************************************************************/

void unixpc_state::disk_control_w(uint8_t data)
{
	logerror("disk_control_w: %02x\n", data);

	// bits 0-2 = head select
	m_hdc->head_w(BIT(data, 0, 2));

	m_hdc->drdy_w(BIT(data, 3) && m_hdr0->exists());

	if (!BIT(data, 4))
		m_hdc->reset();

	m_floppy->mon_w(!BIT(data, 5));

	// bit 6 = floppy selected / not selected
	if (BIT(data, 6))
		m_wd2797->set_floppy(m_floppy);
	else
		m_wd2797->set_floppy(nullptr);

	m_wd2797->mr_w(BIT(data, 7));
}

void unixpc_state::wd2797_intrq_w(int state)
{
	logerror("wd2797_intrq_w: %d\n", state);
	m_fdc_intrq = state;
	m_int02->in_w<1>(state);
}

void unixpc_state::wd2797_drq_w(int state)
{
	logerror("wd2797_drq_w: %d\n", state);
}

/***************************************************************************
    HARD DISK
***************************************************************************/

void unixpc_state::wd1010_intrq_w(int state)
{
	m_hdc_intrq = state;
	m_int02->in_w<0>(state);
}

/***************************************************************************
    VIDEO
***************************************************************************/

uint32_t unixpc_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (int y = 0; y < 348; y++)
		for (int x = 0; x < 720/16; x++)
			for (int b = 0; b < 16; b++)
				bitmap.pix(y, x * 16 + b) = BIT(m_videoram[y * (720/16) + x], b);

	return 0;
}


/***************************************************************************
    ADDRESS MAPS
***************************************************************************/

void unixpc_state::unixpc_mem(address_map &map)
{
	map(0x000000, 0x3fffff).view(m_ramromview);
	m_ramromview[0](0x000000, 0x3fffff).rom().region("bootrom", 0);
	m_ramromview[1](0x000000, 0x3fffff).rw(FUNC(unixpc_state::ram_mmu_r), FUNC(unixpc_state::ram_mmu_w));
	map(0x400000, 0x4007ff).ram().share("mapram");
	map(0x410000, 0x410001).r(FUNC(unixpc_state::gsr_r));
	map(0x420000, 0x427fff).ram().share("videoram");
	map(0x450000, 0x450001).r(FUNC(unixpc_state::tsr_r));
	map(0x460000, 0x460001).rw(FUNC(unixpc_state::diskdma_size_r), FUNC(unixpc_state::diskdma_size_w));
	map(0x470000, 0x470001).r(FUNC(unixpc_state::line_printer_r));
	map(0x480000, 0x480001).w(FUNC(unixpc_state::rtc_w));
	map(0x490000, 0x490001).select(0x7000).w(FUNC(unixpc_state::tcr_w));
	map(0x4a0000, 0x4a0000).w("mreg", FUNC(output_latch_device::write));
	map(0x4d0000, 0x4d7fff).w(FUNC(unixpc_state::diskdma_ptr_w));
	map(0x4e0001, 0x4e0001).w(FUNC(unixpc_state::disk_control_w)).cswidth(16);
	map(0x4f0001, 0x4f0001).w("printlatch", FUNC(output_latch_device::write));
	map(0xe00000, 0xe0000f).rw(m_hdc, FUNC(wd1010_device::read), FUNC(wd1010_device::write)).umask16(0x00ff);
	map(0xe10000, 0xe10007).rw(m_wd2797, FUNC(wd_fdc_device_base::read), FUNC(wd_fdc_device_base::write)).umask16(0x00ff);
	map(0xe30000, 0xe30001).r(FUNC(unixpc_state::rtc_r));
	map(0xe40000, 0xe40001).select(0x7000).w(FUNC(unixpc_state::gcr_w));
	map(0xe50000, 0xe50007).rw("mpsc", FUNC(upd7201_device::cd_ba_r), FUNC(upd7201_device::cd_ba_w)).umask16(0x00ff);
	map(0xe70000, 0xe70003).rw("kbc", FUNC(acia6850_device::read), FUNC(acia6850_device::write)).umask16(0xff00);
	map(0x800000, 0x803fff).mirror(0x7fc000).rom().region("bootrom", 0);
}

/***************************************************************************
    INPUT PORTS
***************************************************************************/

static INPUT_PORTS_START( unixpc )
INPUT_PORTS_END


/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

static void unixpc_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

void unixpc_state::unixpc(machine_config &config)
{
	// basic machine hardware
	M68010(config, m_maincpu, 40_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &unixpc_state::unixpc_mem);

	LS259(config, m_gcr); // 7K
	m_gcr->q_out_cb<0>().set(FUNC(unixpc_state::error_enable_w));
	m_gcr->q_out_cb<1>().set(FUNC(unixpc_state::parity_enable_w));
	m_gcr->q_out_cb<2>().set(FUNC(unixpc_state::bpplus_w));
	m_gcr->q_out_cb<3>().set(FUNC(unixpc_state::romlmap_w));

	LS259(config, m_tcr); // 10K

	INPUT_MERGER_ANY_HIGH(config, m_int02); // 26H pins 3-6
	m_int02->output_handler().set_inputline(m_maincpu, M68K_IRQ_2);

	output_latch_device &mreg(OUTPUT_LATCH(config, "mreg"));
	mreg.bit_handler<0>().set_output("led_0").invert();
	mreg.bit_handler<1>().set_output("led_1").invert();
	mreg.bit_handler<2>().set_output("led_2").invert();
	mreg.bit_handler<3>().set_output("led_3").invert();
	// bit 4 (D12) = 0 = modem baud rate from UART clock inputs, 1 = baud from programmable timer
	mreg.bit_handler<5>().set("printer", FUNC(centronics_device::write_strobe)).invert();
	// bit 6 (D14) = 0 for disk DMA write, 1 for disk DMA read
	// bit 7 (D15) = VBL ack (must go high-low-high to ack)

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_screen_update(FUNC(unixpc_state::screen_update));
	screen.set_raw(40_MHz_XTAL / 2, 896, 0, 720, 367, 0, 348);
	screen.set_palette("palette");
	// vsync should actually last 17264 pixels

	config.set_default_layout(layout_unixpc);

	PALETTE(config, "palette", palette_device::MONOCHROME);

	// internal ram
	RAM(config, RAM_TAG).set_default_size("1M").set_extra_options("2M");

	// floppy
	WD2797(config, m_wd2797, 40_MHz_XTAL / 40); // 1PCK (CPU clock) divided by custom DMA chip
	m_wd2797->intrq_wr_callback().set(FUNC(unixpc_state::wd2797_intrq_w));
	m_wd2797->drq_wr_callback().set(FUNC(unixpc_state::wd2797_drq_w));
	FLOPPY_CONNECTOR(config, "wd2797:0", unixpc_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats);

	WD1010(config, m_hdc, 40_MHz_XTAL / 8);
	m_hdc->out_intrq_callback().set(FUNC(unixpc_state::wd1010_intrq_w));
	HARDDISK(config, m_hdr0, 0);

	upd7201_device &mpsc(UPD7201(config, "mpsc", 19.6608_MHz_XTAL / 8));
	mpsc.out_txda_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	mpsc.out_dtra_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	mpsc.out_rtsa_callback().set("rs232", FUNC(rs232_port_device::write_rts));
	mpsc.out_int_callback().set_inputline(m_maincpu, M68K_IRQ_4);

	acia6850_device &kbc(ACIA6850(config, "kbc", 0));
	kbc.irq_handler().set_inputline(m_maincpu, M68K_IRQ_3);

	// TODO: RTC
	//TC8250(config, "rtc", 32.768_kHz_XTAL);

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set("mpsc", FUNC(upd7201_device::rxa_w));
	rs232.dsr_handler().set("mpsc", FUNC(upd7201_device::dcda_w));
	rs232.cts_handler().set("mpsc", FUNC(upd7201_device::ctsa_w));

	centronics_device &printer(CENTRONICS(config, "printer", centronics_devices, nullptr));
	output_latch_device &printlatch(OUTPUT_LATCH(config, "printlatch"));
	printer.set_output_latch(printlatch);
}


/***************************************************************************
    ROM DEFINITIONS
***************************************************************************/

// ROMs were provided by Michael Lee und imaged by Philip Pemberton
ROM_START( 3b1 )
	ROM_REGION16_BE(0x400000, "bootrom", 0)
	ROM_LOAD16_BYTE("72-00617.15c", 0x000000, 0x002000, CRC(4e93ff40) SHA1(1a97c8d32ec862f7f5fa1032f1688b76ea0672cc))
	ROM_LOAD16_BYTE("72-00616.14c", 0x000001, 0x002000, CRC(c61f7ae0) SHA1(ab3ac29935a2a587a083c4d175a5376badd39058))
ROM_END

} // anonymous namespace

/***************************************************************************
    GAME DRIVERS
***************************************************************************/

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY  FULLNAME             FLAGS
COMP( 1985, 3b1,  0,      0,      unixpc,  unixpc, unixpc_state, empty_init, "AT&T",  "UNIX PC Model 3B1", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



ut88.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

UT88 driver by Miodrag Milanovic

2008-03-09 Keyboard fixed, sound added.
2008-03-06 Preliminary driver.

UT88MINI
********
Need instructions...
When started you enter a one-key command, followed by whatever parameters
are needed.

Pressing 1 will allow entry of bytes into RAM.

Command 3 does a test of the display, however the rest are a mystery.

Paste facility was tested but doesn't work, because keyboard response is
too slow.

****************************************************************************/

#include "emu.h"
#include "ut88.h"

#include "formats/rk_cas.h"
#include "ut88mini.lh"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


/* Address maps */
void ut88mini_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x03ff).rom();  // System ROM
	map(0xc000, 0xc3ff).ram();  // RAM
	map(0x9000, 0x9fff).w(FUNC(ut88mini_state::led_w)); // 7seg LED
}

void ut88_state::mem_map(address_map &map)
{
	map(0x0000, 0xdfff).ram().share("mainram");
	map(0xe000, 0xe7ff).ram();  // Video RAM (not used)
	map(0xe800, 0xefff).ram().share("videoram"); // Video RAM
	map(0xf400, 0xf7ff).ram();  // System RAM
	map(0xf800, 0xffff).rom().region("maincpu",0);  // System ROM
}

void ut88mini_state::io_map(address_map &map)
{
	map(0xA0, 0xA0).r(FUNC(ut88mini_state::keyboard_r));
	map(0xA1, 0xA1).r(FUNC(ut88mini_state::tape_r));
}

void ut88_state::io_map(address_map &map)
{
	map(0x04, 0x07).rw(FUNC(ut88_state::keyboard_r), FUNC(ut88_state::keyboard_w));
	map(0xA1, 0xA1).rw(FUNC(ut88_state::tape_r), FUNC(ut88_state::sound_w));
}

/* Input ports */
static INPUT_PORTS_START( ut88 )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR(0xA4)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("LINE2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('@')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("LINE4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("LINE5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("LINE6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('^')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("<>") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('_')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("LINE7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("LINE8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Rus/Lat") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

static INPUT_PORTS_START( ut88mini )
	PORT_START("LINE0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7')

	PORT_START("LINE1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("LINE2")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR('V')
INPUT_PORTS_END

const gfx_layout charlayout =
{
	8, 8,               /* 8x8 characters */
	256,                /* 256 characters */
	1,                /* 1 bits per pixel */
	{0},                /* no bitplanes; 1 bit per pixel */
	{0, 1, 2, 3, 4, 5, 6, 7},
	{0 * 8, 1 * 8, 2 * 8, 3 * 8, 4 * 8, 5 * 8, 6 * 8, 7 * 8},
	8*8                 /* size of one char */
};

static GFXDECODE_START( gfx_ut88 )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END

u32 ut88_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (u8 y = 0; y < 28; y++)
	{
		for (u8 x = 0; x < 64; x++)
		{
			u8 code = m_vram[x +   y*64] & 0x7f;
			u8 attr = m_vram[x+1 + y*64] & 0x80;
			m_gfxdecode->gfx(0)->opaque(bitmap,cliprect, code | attr, 0, 0,0, x*8,y*8);
		}
	}
	return 0;
}

/* Machine driver */
void ut88_state::ut88(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, 2000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &ut88_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &ut88_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(64*8, 28*8);
	screen.set_visarea(0, 64*8-1, 0, 28*8-1);
	screen.set_screen_update(FUNC(ut88_state::screen_update));
	screen.set_palette(m_palette);

	PALETTE(config, m_palette, palette_device::MONOCHROME);
	GFXDECODE(config, m_gfxdecode, m_palette, gfx_ut88);

	/* audio hardware */
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, "dac", 0).add_route(ALL_OUTPUTS, "speaker", 0.25);

	/* Devices */
	I8255A(config, m_ppi);
	m_ppi->out_pa_callback().set(FUNC(ut88_state::ppi_porta_w));
	m_ppi->in_pb_callback().set(FUNC(ut88_state::ppi_portb_r));
	m_ppi->in_pc_callback().set(FUNC(ut88_state::ppi_portc_r));

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(rku_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "speaker", 0.05);
	m_cassette->set_interface("ut88_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("ut88");
}

void ut88mini_state::ut88mini(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, 2000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &ut88mini_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &ut88mini_state::io_map);

	/* video hardware */
	config.set_default_layout(layout_ut88mini);
	TIMER(config, "display_timer").configure_periodic(FUNC(ut88mini_state::display_timer), attotime::from_hz(60));

	/* Cassette */
	SPEAKER(config, "speaker").front_center();

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(rku_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "speaker", 0.05);
	m_cassette->set_interface("ut88_cass");

	//SOFTWARE_LIST(config, "cass_list").set_original("ut88");   // no suitable software in the list
}

/* ROM definition */
ROM_START( ut88 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "ut88.rom", 0x0000, 0x0800, CRC(f433202e) SHA1(a5808a4f68fb10eb7f17f2a05c3b8479fec0e05d) )

	ROM_REGION( 0x0800, "chargen",0 )
	ROM_LOAD( "ut88.fnt", 0x0000, 0x0800, CRC(874b4d29) SHA1(357efbb295cd9e47fa97d4d03f4f1859a915b5c3) )
ROM_END

ROM_START( ut88mini )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "ut88mini.rom", 0x0000, 0x0400, CRC(ce9213ee) SHA1(16b71b3051a800386d664dbcc5983b783475d0c6) ) // DD10,DD11 (0x200 each)
	ROM_FILL(0x71,1,0x7e)  // show content of ram address

	ROM_REGION( 0x0200, "proms", 0 )
	ROM_LOAD( "ut88key1.dd15", 0x0000, 0x0100, CRC(ecfe42c7) SHA1(d7f10bbb05934150c1a258db1c8b4eb65771af59) )
	ROM_LOAD( "ut88key2.dd16", 0x0100, 0x0100, CRC(96324d23) SHA1(9dca3f639fc29d87df56505b3dde668ef2849da3) )
ROM_END

/* Driver */

/*    YEAR  NAME      PARENT    COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY      FULLNAME      FLAGS */
COMP( 1989, ut88mini, 0,        0,      ut88mini, ut88mini, ut88mini_state, empty_init, "<unknown>", "UT-88 mini", MACHINE_SUPPORTS_SAVE )
COMP( 1989, ut88,     ut88mini, 0,      ut88,     ut88,     ut88_state,     empty_init, "<unknown>", "UT-88",      MACHINE_SUPPORTS_SAVE )



uvax1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Skeleton driver for DEC MicroVAX I ("Seahorse").

    The MicroVAX I processor occupies two interconnected PCBs: the M7135 data
    path module (DAP) and the M7136 memory controller (MCT). The LSI data path
    chip, developed by DEC in conjunction with Silicon Compilers, Inc., is not
    really a single-chip VAX CPU, since it relies on a PROM microcode store and
    numerous PLDs and support logic ICs. Though the memory controller module
    contains TLB/cache SRAMs and a few register files, main memory is provided
    separately on the 16-bit Q22 bus in the form of MSV11-P modules.

    There were two versions of the processor: one supporting only F_floating
    and D_floating types, the other supporting only F_floating and G_floating.

    Q-bus I/O options used in the MicroVAX I include DLVJ1, DZV11, DEQNA and
    RQDX1.

*******************************************************************************/

#include "emu.h"

//#include "bus/qbus/qbus.h"
#include "cpu/vax/vax.h"
#include "machine/scn_pci.h"

namespace {

class uvax1_state : public driver_device
{
public:
	uvax1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void uvax1(machine_config &config) ATTR_COLD;

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};

void uvax1_state::mem_map(address_map &map)
{
	map(0x00000000, 0x00001fff).rom().region("bootrom", 0);
}

static INPUT_PORTS_START(uvax1)
INPUT_PORTS_END

void uvax1_state::uvax1(machine_config &config)
{
	KD32A(config, m_maincpu, 64_MHz_XTAL / 16); // cycle time is 250 ns
	m_maincpu->set_addrmap(AS_PROGRAM, &uvax1_state::mem_map);

	SCN2661C(config, "conuart", 5.0688_MHz_XTAL);
}

ROM_START(uvax1)
	ROM_REGION32_LE(0x2000, "bootrom", 0)
	ROM_LOAD("m7135_microvax_i_e78_2764.bin", 0x0000, 0x2000, CRC(8c6f01f0) SHA1(c3effa180faa7767b267b047d724871592466788))

	ROM_REGION64_LE(0x10000, "dap_proms", ROMREGION_ERASE00) // MB7144H or equivalent
	ROM_LOAD64_BYTE("dap0.bin", 0x0000, 0x2000, NO_DUMP)
	ROM_LOAD64_BYTE("dap1.bin", 0x0001, 0x2000, NO_DUMP)
	ROM_LOAD64_BYTE("dap2.bin", 0x0002, 0x2000, NO_DUMP)
	ROM_LOAD64_BYTE("dap3.bin", 0x0003, 0x2000, NO_DUMP)
	ROM_LOAD64_BYTE("dap4.bin", 0x0004, 0x2000, NO_DUMP)

	ROM_REGION64_LE(0x2000, "mct_proms", 0) // Am27S35DC
	ROM_LOAD64_BYTE("mct0.bin", 0x0000, 0x0400, NO_DUMP)
	ROM_LOAD64_BYTE("mct1.bin", 0x0001, 0x0400, NO_DUMP)
	ROM_LOAD64_BYTE("mct2.bin", 0x0002, 0x0400, NO_DUMP)
	ROM_LOAD64_BYTE("mct3.bin", 0x0003, 0x0400, NO_DUMP)
	ROM_LOAD64_BYTE("mct4.bin", 0x0004, 0x0400, NO_DUMP)
	ROM_LOAD64_BYTE("mct5.bin", 0x0005, 0x0400, NO_DUMP)
	ROM_LOAD64_BYTE("mct6.bin", 0x0006, 0x0400, NO_DUMP)
	ROM_LOAD64_BYTE("mct7.bin", 0x0007, 0x0400, NO_DUMP)
ROM_END

} // anonymous namespace

COMP(1984, uvax1, 0, 0, uvax1, uvax1, uvax1_state, empty_init, "Digital Equipment Corporation", "MicroVAX I", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)



uvax2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Skeleton driver for DEC MicroVAX II ("Mayflower").

*******************************************************************************/

#include "emu.h"

//#include "bus/qbus/qbus.h"
#include "cpu/vax/vax.h"
//#include "machine/dc319.h"
#include "machine/mc146818.h"

namespace {

class uvax2_state : public driver_device
{
public:
	uvax2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void uvax2(machine_config &config) ATTR_COLD;

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};

void uvax2_state::mem_map(address_map &map)
{
	map(0x00000000, 0x0000ffff).rom().region("bootrom", 0);
}

static INPUT_PORTS_START(uvax2)
INPUT_PORTS_END

void uvax2_state::uvax2(machine_config &config)
{
	DC333(config, m_maincpu, 40_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &uvax2_state::mem_map);

	//DC319(config, "dlart", 614.4_MHz_XTAL);

	MC146818(config, "toyclock", 32.768_kHz_XTAL);
}

ROM_START(uvax2)
	ROM_REGION32_LE(0x10000, "bootrom", 0)
	ROM_SYSTEM_BIOS(0, "ef", "Rev. EF") // Rev. AF is identical?
	ROMX_LOAD("m7606_a1_rev_ef_lm8725_110e6_am27256.bin", 0x0000, 0x8000, CRC(b44e8cab) SHA1(8b74031231248241109b78baa4819659533e1312), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD("m7606_a1_rev_ef_lm8729_111e6_am27256.bin", 0x0001, 0x8000, CRC(44ebb04d) SHA1(031c271ead78cae4c3dfc1125b0ab515843e1911), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "ah", "Rev. AH")
	ROMX_LOAD("m7606_ah_062_110e6_d27256.bin", 0x0000, 0x8000, CRC(653646fa) SHA1(1f900fe5e998d0f8ed4ae63557f1e63c839991c0), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD("m7606_ah_062_111e6_d27256.bin", 0x0001, 0x8000, CRC(725dc43e) SHA1(630a908f7d630a33099ae9c3c3c08df478de941c), ROM_SKIP(1) | ROM_BIOS(1))
ROM_END

} // anonymous namespace

COMP(1984, uvax2, 0, 0, uvax2, uvax2, uvax2_state, empty_init, "Digital Equipment Corporation", "MicroVAX II", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)



uvax3.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Skeleton driver for DEC third-generation MicroVAX models ("Mayfair").

*******************************************************************************/

#include "emu.h"

//#include "bus/dssi/dssi.h"
//#include "bus/qbus/qbus.h"
#include "cpu/vax/vax.h"
//#include "machine/am79c90.h"

namespace {

class uvax3_state : public driver_device
{
public:
	uvax3_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void mv3400(machine_config &config) ATTR_COLD;

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};

void uvax3_state::mem_map(address_map &map)
{
	map(0x20040000, 0x2005ffff).mirror(0x20000).rom().region("firmware", 0);
}

static INPUT_PORTS_START(mv3400)
INPUT_PORTS_END

void uvax3_state::mv3400(machine_config &config)
{
	DC341(config, m_maincpu, 40_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &uvax3_state::mem_map);

	//AM7990(config, "lance", ?);
}

ROM_START(mv3400)
	ROM_REGION32_LE(0x20000, "firmware", 0)
	ROM_LOAD16_BYTE("m7624_dec88_lm8914_152e7_d27512.bin", 0x00000, 0x10000, CRC(eb61f8d0) SHA1(17d56f59120881df2b6522097998b4a9e4bac77e))
	ROM_LOAD16_BYTE("m7624_dec88_lm8914_153e7_d27512.bin", 0x00001, 0x10000, CRC(6727bff2) SHA1(4f566e82a8fa8490056d62b3e44c070ba5ab1754))
ROM_END

ROM_START(mv3500)
	ROM_REGION32_LE(0x20000, "firmware", 0)
	ROM_LOAD16_BYTE("ka650-a-v5.3-vmb2.7_192e7.bin", 0x00000, 0x10000, CRC(a3ec59a6) SHA1(6d0121d7e232c841484a328340c828a4f0fdf903))
	ROM_LOAD16_BYTE("ka650-a-v5.3-vmb2.7_193e7.bin", 0x00001, 0x10000, CRC(873ee8bd) SHA1(b0abfebda60e9394e1045644634a8845b5573d04))
ROM_END

} // anonymous namespace

COMP(1988, mv3400, 0, 0, mv3400, mv3400, uvax3_state, empty_init, "Digital Equipment Corporation", "MicroVAX 3400", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)
COMP(1988, mv3500, 0, 0, mv3400, mv3400, uvax3_state, empty_init, "Digital Equipment Corporation", "MicroVAX 3500", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW)



uvax3100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************

  Skeleton driver for first-generation (CVAX-based) DEC MicroVAX 3100 models.


  Hardhare for MicroVAX 3100 Model 10:
                                                                         ____
  Main PCB:         _________________    _____    _____    _____    _    |   |            ___   ___________
   ________________|                |___|    |___|    |___|    |___| |___|   |___________|  |__|          |_____
  |                |________________|   |____|   |____|   |____|   |_|   |___|           |  |  |__________|    |
  |    ____       ______                __________   __________         __________       |  |  __________      |
  |   |o o|      |     |   __________  |_ITA27B4_|  |_ITA27B4_|        |DP8392BN_|       |  | 16-25072-01      |
  |   |o o|      |_____|  |_ITA27B4_|           ::::::::::::::::::::::::::               |  |                  |
  |   |o o|       ______  ___________  ___________  ___________                          |__|    ____________  |
  |   |o o|      |L5170D PE-64685-001 PE-64685-001 PE-64685-001     _______  __________         |_AM7992BDC_|  |
  |   |o o|      |_____|  |_________|  |_________|  |_________|    |74F244|  16-25072-01                       |
  |   |___|                                                                                 ____________ Xtal  |
  |  __________   __________          :::::::::::::::::::::::   :::::::::::::::::::::::    |74LS244NQST| 20MHz |
  | |74LS273N_|  |74LS240N_|                       ____          __________   ___________   __________________ |
  |   Xtal           Xtal        ___________      |___|         |_7416PC__|  | MC146818P|  |AMD AM7990DC/80  | |
  |:  3.6864MHz  ..  5.0688MHz  |          |                                 |__________|  |21-21672-09______| |
  |  __________                 |LSIL5A0065|    __________________   _________________       _________________ |
  | |_74LS92N_|                 |21-22769-01   |NCR 5380         |  |NCR 5380         |     | EPROM (E25)    | |
  |      ________________       |          |   |CP07972__________|  |CP07972__________|     |________________| |
  |     | SIEMENS       |       |__________|                                __________       _________________ |
  |     | SC21C1002     |                                                  |_PROM____|      | EPROM (E24)    | |
  |     | 21-30367-03   |  Xtal             _____              __________   __________      |________________| |
  |     |               |  69.1968MHz      |____|             18-18800-02  |74LS125AN|                         |
  |     |               |                                      __________                                      |
  |     |_______________|                                     |_74F244N_|                                      |
  |  __________________               _________________                                                        |
  | | EPROM  (E98)    |              |                |        __________                     ____   ____      |
  | |_________________|              | LSI LOGIC      |       |74F74NQST|                    |___|  |___|      |
  |  _________________               | L1A5029        |        __________                                      |
  | | D43256AC-10L   |               | 21-28651-03    |       |_74F11PC_|                     ____   ____      |
  | |________________|               |                |                 1920441Q   1920441Q  |___|  |___|      |
  |  _________________               |                |        Xtal     02MP130T   02MP130T                    |
  | | D43256AC-10L   |   __________  |________________|      66.667MHz                               ____      |
  | |________________|  |_74F00PC_|                  _____________      1920441Q   1920441Q         |___|      |
  |  _________________   __________   ____________  |21-24674-17 |      02MP130T   02MP130T                    |
  | | D43256AC-10L   |  |74F04NQST|  |21-26604-07|  |  G889-41   |                                             |
  | |________________|   __________  |  H752-28  |  |   9134     |      1920441Q   1920441Q                    |
  |  _________________  |_74F00PC_|  |   9133    |  |            |      02MP130T   02MP130T   __________       |
  | | D43256AC-10L   |   __________  |           |  |____________|                           |_74F32PC_|       |
  | |________________|  |_74F32PC_|  |___________|                      1920441Q   1920441Q                    |
  |                                                                     02MP130T   02MP130T                    |
  |                                                                       __________    __________             |
  |                                                                      |74LS240N_|   |SN74AS804|             |
  |                                  :::::::::::::::   :::::::::::::::                  __________  __________ |
  |                                                                                    |SN74AS804| |_74F32N__| |
  |____________________________________________________________________________________________________________|


  Communications PCB:
   ___________________________________________________________________________________     ___________________________
  |                                                                                  |    |                          |
  |    _______________________                           ___________   ___________   |____|       ___________        |
  |   | SCN68562C4N48        |                          |74HCT245N_|  |74HCT245N_|               |_PAL20L10_|        |
  |   |______________________|                           ___________   ___________     _______________   ___________ |
  |                                                     |74HCT245N_|  |74HCT245N_|    | HM6264AP-10  |  |74F32NQST_| |
  |    _______________________                                         ___________    |______________|   ___________ |
  |   | SCN68562C4N48        |                                        |74HCT245N_|     _______________  |74F08NQST_| |
  |   |______________________|                                         ___________    | HM6264AP-10  |   ___________ |
  | ___________  .  __________    _______________                     |74HCT245N_|    |______________|  |_74F74PC__| |
  ||_74F453N__|  : |SN74LS244N   |              |                      ___________     _______________   ___________ |
  | __________      ________     | 21-26907-02  |                     |_74LS373N_|    | HM6264AP-10  |  |74F02NQST_| |
  ||SN74LS20N|     |74F10PC|     | DC7045D      |   _____________      ___________    |______________|   ___________ |
  |                              | TAC 8944     |  | DEC 358EA  |     |74HCT245N_|     _______________  |_74F74PC__| |
  |   __________  ____________   |              |  | 78532-GA   |      ___________    | HM6264AP-10  |   ___________ |
  |  |74F191PC_| |           |   |______________|  | 21-24329-01|     |_74LS373N_|    |______________|  |74F00NQST_| |
  |   __________ |           |   ______________    |P467-17 8949|      ___________                       ___________ |
  |  |_74F74PC_| |           |  |CY7C128-45PC_|    |____________|     |PAL16L8NC_|                      |74F08NQST_| |
  |   Xtal       |           |   ______________                                    ___________        ______________ |
  | 14.7456 MHz  |___________|  |PAL20L10ACNS_|                                   |MC74F521N_|       | PLS105ANJ   | |
  | ____________  ____________   ____________  __________  __________  __________  ___________       |_____________| |
  ||SN74LS244N_| |SN74LS244N_|  |SN74LS166AN| |74F245PC_| |SN74LS139| |_74F74PC_| |74HCT245N_|        ______________ |
  | ____________  ____________   ____________  __________  __________              ___________       | PLS105ANJ   | |
  ||SN74LS244N_| |SN74LS244N_|  |SN74LS166AN| |74HCT245N| |22738-01_|  40MHz Xtal |_74F543N__|       |_____________| |
  |        _____                               __________  __________  __________  ___________        ______________ |
  |       |    |      ____                    |74HCT245N| |SN74LS375N |74F32NQST| |74HCT245N_|       | PLS105ANJ   | |
  |       | C  |     |   |                     __________  __________  __________  ___________       |_____________| |
  |       | O  |     | C |                    |74F245PC_| |74F32NQST| |74F32NQST| |_74F543N__|        ______________ |
  |       | N  |     | O |                     __________  __________  __________  ___________       | EPROM       | |
  |       | N  |     | N |                    |_74F244N_| |SN74LS09N| |74F00NQST| |74HCT245N_|       |_____________| |
  |       |    |     | N |                     __________  __________  __________  ___________        ___________    |
  |       |    |     |   |                    |MC74F240N| |74F32NQST| |_________| |_74F543N__|       |_74LS244N_|    |
  |       |    |     |   |                                                         ___________        ___________    |
  |       |    |     |___|       ______________________    ______________________ |74HCT245N_|       |_74F374N__|    |
  |       |____|                | :::::::::::::::::::: |  | ::::::::::::::::::: |  ___________                       |
  |                                                                               |_74F543N__|                       |
  |__________________________________________________________________________________________________________________|

***********************************************************************************************************************/

#include "emu.h"
#include "cpu/vax/vax.h"

#include "machine/am79c90.h"
//#include "machine/ncr5380.h"
//#include "machine/scnxx562.h"
#include "machine/terminal.h"


namespace {

class uvax3100_state : public driver_device
{
public:
	uvax3100_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_terminal(*this, "terminal")
	{ }

	void uvax3100(machine_config &config) ATTR_COLD;

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<generic_terminal_device> m_terminal;
};


void uvax3100_state::mem_map(address_map &map)
{
	map(0x20040000, 0x2007ffff).rom().region("maincpu", 0);
}


// Input ports
static INPUT_PORTS_START( uvax3100 )
INPUT_PORTS_END


// Model 10
void uvax3100_state::uvax3100(machine_config &config)
{
	// Basic machine hardware
	DC341(config, m_maincpu, 66.6667_MHz_XTAL / 6); // CPU CVAX 21-24674-17 11.11 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &uvax3100_state::mem_map);

	AM7990(config, "lance1", 0); // AMD AM7990PC/80

	// NCR5380(...)

	// DUSCC68562(...) // Signetics SCN68562C4N48

	// Video hardware
	GENERIC_TERMINAL(config, m_terminal, 0);
}

ROM_START( mv3100m10 )
	ROM_REGION( 0x40000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD32_WORD( "dec89_23-116e8-00_system_rom_lo_word.e24", 0x00000, 0x20000, CRC(69ef8cf5) SHA1(a59a500921278dc356a519cb435641426c844779) )
	ROM_LOAD32_WORD( "dec89_23-117e8-00_system_rom_hi_word.e25", 0x00002, 0x20000, CRC(df572ac3) SHA1(5e91d0f4fc8442e3ebc2424d9f85078ba00d2de5) )

	ROM_REGION( 0x20000, "scsi", ROMREGION_ERASEFF )
	ROM_LOAD(  "dec89_23-061e8-00_scsi_rom.e98",      0x00000, 0x20000, CRC(51fb8268) SHA1(a930869dce955b9b7a2b0fb68840e863c74e6512) )

	ROM_REGION( 0x10000, "comms", ROMREGION_ERASEFF )
	ROM_LOAD(  "dec90_fx9123_248e7.bin",              0x00000, 0x10000, CRC(d50801e6) SHA1(e67b1d732ce775381eb8f41684a3366db7a435d7) )

	ROM_REGION( 0x00117, "plds", ROMREGION_ERASEFF ) // All of them on the comms PCB
	ROM_LOAD(  "dec90_lm9019_032j7_pal20l10acns.bin", 0x00000, 0x00117, NO_DUMP )
	ROM_LOAD(  "dec90_lm9027_111l1_pls105anj.bin",    0x00000, 0x00100, NO_DUMP )
	ROM_LOAD(  "dec90_lm9028_519j5_pal16l8nc.bin",    0x00000, 0x00117, NO_DUMP )
	ROM_LOAD(  "dec90_lm9029_124l1_pls105anj.bin",    0x00000, 0x00100, NO_DUMP )
	ROM_LOAD(  "dec90_lm9030_031j7_pal20l10acns.bin", 0x00000, 0x00117, NO_DUMP )
	ROM_LOAD(  "dec90_lm9031_112l1_pls105anj.bin",    0x00000, 0x00100, NO_DUMP )

	ROM_REGION( 0x00100, "prom", ROMREGION_ERASEFF ) // On the main PCB
	ROM_LOAD(  "dec84_fx9206_365a1.bin",              0x00000, 0x00100, NO_DUMP )
ROM_END

} // anonymous namespace


//    YEAR  NAME       PARENT  COMPAT  MACHINE   INPUT     STATE           INIT        COMPANY                          FULLNAME                  FLAGS
COMP( 1989, mv3100m10, 0,      0,      uvax3100, uvax3100, uvax3100_state, empty_init, "Digital Equipment Corporation", "MicroVAX 3100 Model 10", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



uzebox.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

    Belogic Uzebox

    driver by Sandro Ronco

    TODO:
    - Sound
    - SDCard

****************************************************************************/

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "bus/snes_ctrl/ctrl.h"
#include "cpu/avr8/avr8.h"
#include "sound/spkrdev.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

// overclocked to 8 * NTSC burst frequency
#define MASTER_CLOCK 28618180

#define INTERLACED   0

class uzebox_state : public driver_device
{
public:
	uzebox_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_cart(*this, "cartslot")
		, m_ctrl1(*this, "ctrl1")
		, m_ctrl2(*this, "ctrl2")
		, m_speaker(*this, "speaker")
		, m_conf(*this, "CONF")
	{ }

	void uzebox(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<atmega644_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<generic_slot_device> m_cart;
	required_device<snes_control_port_device> m_ctrl1;
	required_device<snes_control_port_device> m_ctrl2;
	required_device<speaker_sound_device> m_speaker;
	required_ioport m_conf;

	uint32_t m_vpos = 0;
	uint64_t m_line_start_cycles = 0;
	uint32_t m_line_pos_cycles = 0;
	uint8_t m_port_a = 0;
	uint8_t m_port_b = 0;
	uint8_t m_port_c = 0;
	uint8_t m_port_d = 0;
	bitmap_rgb32 m_bitmap;

	uint8_t port_a_r();
	void port_a_w(uint8_t data);
	uint8_t port_b_r();
	void port_b_w(uint8_t data);
	uint8_t port_c_r();
	void port_c_w(uint8_t data);
	uint8_t port_d_r();
	void port_d_w(uint8_t data);

	void line_update();
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	void data_map(address_map &map) ATTR_COLD;
	void prg_map(address_map &map) ATTR_COLD;
};


/****************************************************\
* Initialization                                     *
\****************************************************/

void uzebox_state::machine_start()
{
	m_screen->register_screen_bitmap(m_bitmap);

	// register for savestates
	save_item(NAME(m_vpos));
	save_item(NAME(m_line_start_cycles));
	save_item(NAME(m_line_pos_cycles));
	save_item(NAME(m_port_a));
	save_item(NAME(m_port_b));
	save_item(NAME(m_port_c));
	save_item(NAME(m_port_d));
}

void uzebox_state::machine_reset()
{
	m_vpos = 0;
	m_line_start_cycles = 0;
	m_line_pos_cycles = 0;
	m_port_a = 0;
	m_port_b = 0;
	m_port_c = 0;
	m_port_d = 0;
}

DEVICE_IMAGE_LOAD_MEMBER(uzebox_state::cart_load)
{
	uint32_t size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);

	if (!image.loaded_through_softlist())
	{
		std::vector<uint8_t> data(size);
		image.fread(&data[0], size);

		if (image.is_filetype("uze"))
			memcpy(m_cart->get_rom_base(), &data[0x200], size - 0x200);
		else
			memcpy(m_cart->get_rom_base(), &data[0], size);
	}
	else
		memcpy(m_cart->get_rom_base(), image.get_software_region("rom"), size);

	return std::make_pair(std::error_condition(), std::string());
}


/****************************************************\
* I/O                                                *
\****************************************************/

void uzebox_state::port_a_w(uint8_t data)
{
	//  xxxx ----   NC
	//  ---- x---   SNES controller clk
	//  ---- -x--   SNES controller latch
	//  ---- --x-   SNES controller P2 data
	//  ---- ---x   SNES controller P1 data

	m_ctrl1->write_strobe(BIT(data, 2));
	m_ctrl2->write_strobe(BIT(data, 2));

	uint8_t changed = m_port_a ^ data;
	if ((changed & data & 0x08) || (changed & (~data) & 0x04))
	{
		m_port_a &= ~0x03;
		m_port_a |= m_ctrl1->read_pin4() ? 0 : 0x01;
		m_port_a |= m_ctrl2->read_pin4() ? 0 : 0x02;
	}

	m_port_a = (data & 0x0c) | (m_port_a & 0x03);
}

uint8_t uzebox_state::port_a_r()
{
	return m_port_a | 0xf0;
}

void uzebox_state::port_b_w(uint8_t data)
{
	//  xxx- ----   SDCard
	//  ---x ----   AD725 CE
	//  ---- x---   AD725 4FSC
	//  ---- -xx-   NC
	//  ---- ---x   AD725 HSYNC

	// AD725 CE is hard-wired to VCC in early revisions (C1, D1 and E1)
	if ((m_port_b & 0x10) || ~m_conf->read() & 1)
		if ((m_port_b ^ data) & m_port_b & 0x01)
		{
			line_update();

			uint32_t cycles = (uint32_t)(machine().time().as_ticks(MASTER_CLOCK) - m_line_start_cycles);
			if (cycles < 1000 && m_vpos >= 448)
				m_vpos = INTERLACED ? ((m_vpos ^ 0x01) & 0x01) : 0;
			else if (cycles > 1000)
				m_vpos += 2;

			m_line_start_cycles = machine().time().as_ticks(MASTER_CLOCK);
			m_line_pos_cycles = 0;
		}

	m_port_b = data;
}

uint8_t uzebox_state::port_b_r()
{
	return m_port_b;
}

void uzebox_state::port_c_w(uint8_t data)
{
	//  xx-- ----   blue
	//  --xx x---   green
	//  ---- -xxx   red

	line_update();
	m_port_c = data;
}

uint8_t uzebox_state::port_c_r()
{
	return m_port_c;
}

void uzebox_state::port_d_w(uint8_t data)
{
	//  x--- ----   sound
	//  -x-- ----   SDCard CS
	//  ---x ----   LED
	//  --x- x---   NC
	//  ---- -x--   power
	//  ---- --xx   UART MIDI

	if ((m_port_d ^ data) & 0x80)
	{
		m_speaker->level_w((data & 0x80) ? 1 : 0);
	}
	m_port_d = data;
}

uint8_t uzebox_state::port_d_r()
{
	return m_port_d;
}


/****************************************************\
* Address maps                                       *
\****************************************************/

void uzebox_state::prg_map(address_map &map)
{
	map(0x0000, 0xffff).r(m_cart, FUNC(generic_slot_device::read_rom));
}

void uzebox_state::data_map(address_map &map)
{
	map(0x0100, 0x10ff).ram(); // 4KB RAM
}


/****************************************************\
* Input ports                                        *
\****************************************************/

static INPUT_PORTS_START( uzebox )
	PORT_START("CONF")
	PORT_CONFNAME( 0x01, 0x00, "AD725 CE" )
	PORT_CONFSETTING(    0x00, "VCC" )
	PORT_CONFSETTING(    0x01, "PB4" )
INPUT_PORTS_END


/****************************************************\
* Video hardware                                     *
\****************************************************/

void uzebox_state::line_update()
{
	uint32_t cycles = (uint32_t)(machine().time().as_ticks(MASTER_CLOCK) - m_line_start_cycles) / 2;
	rgb_t color = rgb_t(pal3bit(m_port_c >> 0), pal3bit(m_port_c >> 3), pal2bit(m_port_c >> 6));

	for (uint32_t x = m_line_pos_cycles; x < cycles; x++)
	{
		if (m_bitmap.cliprect().contains(x, m_vpos))
			m_bitmap.pix(m_vpos, x) = color;
		if (!INTERLACED)
			if (m_bitmap.cliprect().contains(x, m_vpos + 1))
				m_bitmap.pix(m_vpos + 1, x) = color;
	}

	m_line_pos_cycles = cycles;
}

uint32_t uzebox_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
	return 0;
}


/****************************************************\
* Machine definition                                 *
\****************************************************/

void uzebox_state::uzebox(machine_config &config)
{
	// basic machine hardware
	ATMEGA644(config, m_maincpu, MASTER_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &uzebox_state::prg_map);
	m_maincpu->set_addrmap(AS_DATA, &uzebox_state::data_map);
	m_maincpu->set_eeprom_tag("eeprom");
	m_maincpu->gpio_in<atmega644_device::GPIOA>().set(FUNC(uzebox_state::port_a_r));
	m_maincpu->gpio_in<atmega644_device::GPIOB>().set(FUNC(uzebox_state::port_b_r));
	m_maincpu->gpio_in<atmega644_device::GPIOC>().set(FUNC(uzebox_state::port_c_r));
	m_maincpu->gpio_in<atmega644_device::GPIOD>().set(FUNC(uzebox_state::port_d_r));
	m_maincpu->gpio_out<atmega644_device::GPIOA>().set(FUNC(uzebox_state::port_a_w));
	m_maincpu->gpio_out<atmega644_device::GPIOB>().set(FUNC(uzebox_state::port_b_w));
	m_maincpu->gpio_out<atmega644_device::GPIOC>().set(FUNC(uzebox_state::port_c_w));
	m_maincpu->gpio_out<atmega644_device::GPIOD>().set(FUNC(uzebox_state::port_d_w));

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(59.99);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(1395));
	m_screen->set_size(870, 525);
	m_screen->set_visarea(150, 870-1, 40, 488-1);
	m_screen->set_screen_update(FUNC(uzebox_state::screen_update));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(0, "mono", 1.00);

	// slot devices
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "uzebox", "bin,uze");
	m_cart->set_must_be_loaded(true);
	m_cart->set_device_load(FUNC(uzebox_state::cart_load));

	SNES_CONTROL_PORT(config, m_ctrl1, snes_control_port_devices, "joypad");
	SNES_CONTROL_PORT(config, m_ctrl2, snes_control_port_devices, "joypad");

	SOFTWARE_LIST(config, "eprom_list").set_original("uzebox");
}

ROM_START( uzebox )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF ) // Main program store

	ROM_REGION( 0x800, "eeprom", ROMREGION_ERASE00 ) // on-die eeprom
ROM_END

} // anonymous namespace


//   YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY    FULLNAME
CONS(2010, uzebox, 0,      0,      uzebox,  uzebox, uzebox_state, empty_init, "Belogic", "Uzebox", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING)



v100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

    The Visual 100 was the first of the second series of display terminals released by Visual Technology. (The Visual 200 and
    Visual 210 made up the first series.) It was followed by the Visual 110 (which emulated the Data General Dasher series rather
    than the DEC VT-100) and the "top of the line" Visual 400.

    The second 8251 and 8116T seem to have been typically unpopulated. However, the program does include routines to drive these
    components, which probably would be installed to provide the optional serial printer interface.

***********************************************************************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/com8116.h"
#include "machine/er1400.h"
#include "machine/i8214.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/input_merger.h"
#include "video/tms9927.h"
#include "screen.h"


namespace {

// character matrix is supposed to be only 7x7, but 15 produces correct timings
#define V100_CH_WIDTH 15

class v100_state : public driver_device
{
public:
	v100_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_vtac(*this, "vtac")
		, m_usart(*this, "usart%u", 1)
		, m_earom(*this, "earom")
		, m_picu(*this, "picu")
		, m_modem(*this, "modem")
		, m_p_chargen(*this, "chargen")
		, m_videoram(*this, "videoram")
		, m_key_row(*this, "ROW%u", 0)
	{ }

	void v100(machine_config &config);

private:
	u8 status_r();
	void port30_w(u8 data);
	u8 keyboard_r();
	void key_row_w(u8 data);
	void port48_w(u8 data);
	void picu_w(u8 data);
	template<int N> void picu_r_w(int state);
	IRQ_CALLBACK_MEMBER(irq_ack);
	void ppi_porta_w(u8 data);

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<crt5037_device> m_vtac;
	required_device_array<i8251_device, 2> m_usart;
	required_device<er1400_device> m_earom;
	required_device<i8214_device> m_picu;
	required_device<rs232_port_device> m_modem;
	required_region_ptr<u8> m_p_chargen;
	required_shared_ptr<u8> m_videoram;
	optional_ioport_array<16> m_key_row;

	u8 m_active_row = 0;
	bool m_video_enable = false;
};

u32 v100_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if (m_vtac->screen_reset() || !m_video_enable)
	{
		bitmap.fill(rgb_t::black(), cliprect);
		return 0;
	}

	unsigned row0 = cliprect.top() / 10;
	unsigned x0 = cliprect.left();
	unsigned px0 = x0 % V100_CH_WIDTH;
	unsigned columns = screen.visible_area().width() / V100_CH_WIDTH;

	u16 start = 0;
	unsigned y = 0;
	for (unsigned row = 0; y <= cliprect.bottom(); row++)
	{
		start = m_videoram[start] | (m_videoram[(start + 1) & 0xfff] << 8);
		u16 end = start + ((start & 0x3000) != 0 ? (columns / 2) + 1 : columns);
		start &= 0xfff;
		end &= 0xfff;

		if (row < row0)
			y += 10;
		else
		{
			unsigned scan = 0;
			if (row == row0)
			{
				scan += cliprect.top() - y;
				y = cliprect.top();
			}
			while (scan < 10 && y <= cliprect.bottom())
			{
				unsigned x = x0, px = px0, addr = start;
				u8 gfxdata = m_p_chargen[((m_videoram[addr] & 0x7f) << 4) | scan] << (px0 / 2);
				while (x <= cliprect.right())
				{
					bitmap.pix(y, x) = BIT(gfxdata, 7) ? rgb_t::white() : rgb_t::black();
					x++;
					px++;
					if ((px & 1) == 0)
						gfxdata <<= 1;
					if (px >= V100_CH_WIDTH)
					{
						addr = (addr + 1) & 0xfff;
						gfxdata = m_p_chargen[((m_videoram[addr] & 0x7f) << 4) | scan];
						px = 0;

						if (addr == end)
						{
							while (x <= cliprect.right())
								bitmap.pix(y, x++) = rgb_t::black();
							break;
						}
					}
				}
				scan++;
				y++;
			}
		}

		start = end;
	}

	return 0;
}

void v100_state::machine_start()
{
	m_picu->inte_w(1);
	m_picu->etlg_w(1);
	m_usart[1]->write_cts(0);

	m_active_row = 0;
	m_video_enable = false;
	save_item(NAME(m_active_row));
	save_item(NAME(m_video_enable));
}

u8 v100_state::status_r()
{
	u8 status = 0xc0;
	status |= m_earom->data_r();
	status |= m_modem->dcd_r() << 1;
	status |= m_modem->si_r() << 2; // SCCD (pin 12)
	status |= m_modem->ri_r() << 3;
	return status;
}

void v100_state::port30_w(u8 data)
{
	// D6 = cursor/text blinking?

	//logerror("Writing %02X to port 30\n", data);
}

u8 v100_state::keyboard_r()
{
	return m_key_row[m_active_row & 15].read_safe(0xff);
}

void v100_state::key_row_w(u8 data)
{
	m_active_row = data;
}

void v100_state::port48_w(u8 data)
{
	//logerror("Writing %02X to port 48\n", data);
}

void v100_state::picu_w(u8 data)
{
	m_picu->b_w((data & 0x0e) >> 1);
	m_picu->sgs_w(BIT(data, 4));
}

template<int N>
void v100_state::picu_r_w(int state)
{
	m_picu->r_w(N, state);
}

IRQ_CALLBACK_MEMBER(v100_state::irq_ack)
{
	m_maincpu->set_input_line(0, CLEAR_LINE);
	return (m_picu->a_r() << 1) | 0xf0;
}

void v100_state::ppi_porta_w(u8 data)
{
	m_vtac->set_clock_scale(BIT(data, 5) ? 0.5 : 1.0);
	m_screen->set_clock_scale(BIT(data, 5) ? 0.5 : 1.0);

	m_video_enable = !BIT(data, 7);

	//logerror("Writing %02X to PPI port A\n", data);
}

void v100_state::mem_map(address_map &map)
{
	map(0x0000, 0x1fff).rom().region("maincpu", 0);
	map(0x4000, 0x4fff).ram().share("videoram");
	map(0x5000, 0x5fff).ram();
}

void v100_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x0f).w(m_vtac, FUNC(crt5037_device::write));
	map(0x10, 0x10).w("brg1", FUNC(com8116_device::stt_str_w));
	map(0x12, 0x13).rw("usart1", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x14, 0x15).rw("usart2", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x16, 0x16).w("brg2", FUNC(com8116_device::stt_str_w));
	map(0x20, 0x20).r(FUNC(v100_state::status_r));
	map(0x30, 0x30).w(FUNC(v100_state::port30_w));
	map(0x40, 0x40).rw(FUNC(v100_state::keyboard_r), FUNC(v100_state::key_row_w));
	map(0x48, 0x48).w(FUNC(v100_state::port48_w));
	map(0x60, 0x60).w(FUNC(v100_state::picu_w));
	map(0x70, 0x73).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
}


static INPUT_PORTS_START( v100 )
	PORT_START("ROW0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Set-Up") PORT_CODE(KEYCODE_F1)
	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("ROW1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('=') PORT_CHAR('+') PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('`') PORT_CHAR('~') PORT_CODE(KEYCODE_TILDE)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(']') PORT_CHAR('}') PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\'') PORT_CHAR('"') PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CHAR(0x0d) PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Line Feed") PORT_CHAR(0x0a)

	PORT_START("ROW2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0') PORT_CHAR(')') PORT_CODE(KEYCODE_0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('-') PORT_CHAR('_') PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('p') PORT_CHAR('P') PORT_CODE(KEYCODE_P)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('[') PORT_CHAR('{') PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('l') PORT_CHAR('L') PORT_CODE(KEYCODE_L)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHAR(':') PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('.') PORT_CHAR('>') PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('/') PORT_CHAR('?') PORT_CODE(KEYCODE_SLASH)

	PORT_START("ROW3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('8') PORT_CHAR('*') PORT_CODE(KEYCODE_8)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('9') PORT_CHAR('(') PORT_CODE(KEYCODE_9)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('i') PORT_CHAR('I') PORT_CODE(KEYCODE_I)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('o') PORT_CHAR('O') PORT_CODE(KEYCODE_O)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('k') PORT_CHAR('K') PORT_CODE(KEYCODE_K)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('l') PORT_CHAR('L') PORT_CODE(KEYCODE_L)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_M)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',') PORT_CHAR('<') PORT_CODE(KEYCODE_COMMA)

	PORT_START("ROW4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR('^') PORT_CODE(KEYCODE_6)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR('&') PORT_CODE(KEYCODE_7)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('y') PORT_CHAR('Y') PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('u') PORT_CHAR('U') PORT_CODE(KEYCODE_U)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('g') PORT_CHAR('G') PORT_CODE(KEYCODE_G)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('h') PORT_CHAR('H') PORT_CODE(KEYCODE_H)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('b') PORT_CHAR('B') PORT_CODE(KEYCODE_B)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('n') PORT_CHAR('N') PORT_CODE(KEYCODE_N)

	PORT_START("ROW5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR('$') PORT_CODE(KEYCODE_4)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('r') PORT_CHAR('R') PORT_CODE(KEYCODE_R)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('t') PORT_CHAR('T') PORT_CODE(KEYCODE_T)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('d') PORT_CHAR('D') PORT_CODE(KEYCODE_D)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('f') PORT_CHAR('F') PORT_CODE(KEYCODE_F)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('c') PORT_CHAR('C') PORT_CODE(KEYCODE_C)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('v') PORT_CHAR('V') PORT_CODE(KEYCODE_V)

	PORT_START("ROW6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR('@') PORT_CODE(KEYCODE_2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('w') PORT_CHAR('W') PORT_CODE(KEYCODE_W)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('e') PORT_CHAR('E') PORT_CODE(KEYCODE_E)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('a') PORT_CHAR('A') PORT_CODE(KEYCODE_A)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('s') PORT_CHAR('S') PORT_CODE(KEYCODE_S)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('z') PORT_CHAR('Z') PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('x') PORT_CHAR('X') PORT_CODE(KEYCODE_X)

	PORT_START("ROW7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x1b) PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x09) PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('q') PORT_CHAR('Q') PORT_CODE(KEYCODE_Q)
	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_KEYBOARD) PORT_CHAR(UCHAR_SHIFT_1) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_LSHIFT)

	PORT_START("ROW8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x08) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\\') PORT_CHAR('|') PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(' ') PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("ROW9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN)
	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("ROW10")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN)
	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)
INPUT_PORTS_END


void v100_state::v100(machine_config &config)
{
	Z80(config, m_maincpu, 47.736_MHz_XTAL / 20); // 2.387 MHz PCLOCK
	m_maincpu->set_addrmap(AS_PROGRAM, &v100_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &v100_state::io_map);
	m_maincpu->set_irq_acknowledge_callback(FUNC(v100_state::irq_ack));

	I8251(config, m_usart[0], 47.736_MHz_XTAL / 20);
	m_usart[0]->txd_handler().set(m_modem, FUNC(rs232_port_device::write_txd));
	m_usart[0]->dtr_handler().set(m_modem, FUNC(rs232_port_device::write_dtr));
	m_usart[0]->rts_handler().set(m_modem, FUNC(rs232_port_device::write_rts));
	//m_usart[0]->rxrdy_handler().set(FUNC(v100_state::picu_r_w<4>)).invert();

	input_merger_device &acts(INPUT_MERGER_ALL_HIGH(config, "acts"));
	acts.output_handler().set(m_usart[0], FUNC(i8251_device::write_cts));

	com8116_device &brg1(COM8116_020(config, "brg1", 1.8432_MHz_XTAL));
	brg1.fr_handler().set(m_usart[0], FUNC(i8251_device::write_rxc));
	brg1.ft_handler().set(m_usart[0], FUNC(i8251_device::write_txc));

	I8251(config, m_usart[1], 47.736_MHz_XTAL / 20);
	m_usart[1]->txd_handler().set("aux", FUNC(rs232_port_device::write_txd));
	m_usart[1]->dtr_handler().set("aux", FUNC(rs232_port_device::write_dtr));
	//m_usart[1]->txrdy_handler().set(FUNC(v100_state::picu_r_w<2>)).invert();

	com8116_device &brg2(COM8116_020(config, "brg2", 1.8432_MHz_XTAL));
	brg2.fr_handler().set(m_usart[1], FUNC(i8251_device::write_rxc));
	brg2.ft_handler().set(m_usart[1], FUNC(i8251_device::write_txc));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	//m_screen->set_raw(47.736_MHz_XTAL / 2, 102 * V100_CH_WIDTH, 0, 80 * V100_CH_WIDTH, 260, 0, 240);
	m_screen->set_raw(47.736_MHz_XTAL, 170 * V100_CH_WIDTH, 0, 132 * V100_CH_WIDTH, 312, 0, 240);
	m_screen->set_screen_update(FUNC(v100_state::screen_update));

	CRT5037(config, m_vtac, 47.736_MHz_XTAL / V100_CH_WIDTH);
	m_vtac->set_char_width(V100_CH_WIDTH);
	m_vtac->set_screen("screen");
	m_vtac->hsyn_callback().set(FUNC(v100_state::picu_r_w<7>)).invert();
	m_vtac->vsyn_callback().set(FUNC(v100_state::picu_r_w<6>)).invert();

	I8214(config, m_picu, 47.736_MHz_XTAL / 20);
	m_picu->int_wr_callback().set_inputline(m_maincpu, 0, ASSERT_LINE);

	i8255_device &ppi(I8255(config, "ppi", 0));
	ppi.out_pa_callback().set(FUNC(v100_state::ppi_porta_w));
	ppi.out_pb_callback().set(m_earom, FUNC(er1400_device::c3_w)).bit(6).invert();
	ppi.out_pb_callback().append(m_earom, FUNC(er1400_device::c2_w)).bit(5).invert();
	ppi.out_pb_callback().append(m_earom, FUNC(er1400_device::c1_w)).bit(4).invert();
	ppi.out_pc_callback().set(m_earom, FUNC(er1400_device::data_w)).bit(6).invert();
	ppi.out_pc_callback().append(m_earom, FUNC(er1400_device::clock_w)).bit(0).invert();
	ppi.out_pc_callback().append(m_modem, FUNC(rs232_port_device::write_spds)).bit(4);
	ppi.out_pc_callback().append("acts", FUNC(input_merger_device::in_w<1>)).bit(7);

	ER1400(config, m_earom);

	RS232_PORT(config, m_modem, default_rs232_devices, "loopback"); // EIA port
	m_modem->rxd_handler().set(m_usart[0], FUNC(i8251_device::write_rxd));
	m_modem->cts_handler().set("acts", FUNC(input_merger_device::in_w<0>));
	m_modem->dcd_handler().set(m_usart[0], FUNC(i8251_device::write_dsr));

	rs232_port_device &aux(RS232_PORT(config, "aux", default_rs232_devices, nullptr)); // optional printer port
	aux.rxd_handler().set(m_usart[1], FUNC(i8251_device::write_rxd));
	aux.dcd_handler().set(m_usart[1], FUNC(i8251_device::write_dsr)); // printer busy
}



/**************************************************************************************************************

Visual 100. (VT-100 clone)
Chips: D780C-1 (Z80), CRT5037, D8255AC-5, uPB8214C, COM8116T-020, D8251AC, ER1400, 8-sw dip
Crystal: 47.736

***************************************************************************************************************/

ROM_START( v100 )
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD( "262-047.u108",  0x0000, 0x1000, CRC(e82f708c) SHA1(20ed83a41fd0703d72a20e170af971181cfbd575) )
	ROM_LOAD( "262-048.u110",  0x1000, 0x1000, CRC(830923d3) SHA1(108590234ff84b5856cc2784d738a2a625305953) )

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD( "241-001.u29",   0x0000, 0x0800, CRC(ef807141) SHA1(cbf3fed001811c5840b9a131d2d3133843cb3b6a) )
ROM_END

} // anonymous namespace


COMP( 1980, v100, 0, 0, v100, v100, v100_state, empty_init, "Visual Technology", "Visual 100", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



v102.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

Skeleton driver for Visual 102 display terminal.

************************************************************************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/eeprompar.h"
#include "machine/input_merger.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/pit8253.h"
#include "machine/z80sio.h"
//#include "video/crt9006.h"
#include "video/crt9007.h"
//#include "video/crt9021.h"
#include "screen.h"

#include "v102_kbd.h"


namespace {

class v102_state : public driver_device
{
public:
	v102_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mpsc(*this, "mpsc")
		, m_chargen(*this, "chargen")
	{ }

	void v102(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void hs_w(int state);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<upd7201_device> m_mpsc;
	required_region_ptr<u8> m_chargen;

	bool m_hs_state = false;
	bool m_kb_clock = false;
};


u32 v102_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void v102_state::hs_w(int state)
{
	if (state && !m_hs_state)
	{
		m_kb_clock = !m_kb_clock;
		m_mpsc->txca_w(m_kb_clock);
		m_mpsc->rxca_w(m_kb_clock);
	}

	m_hs_state = bool(state);
}


void v102_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).rom().region("maincpu", 0);
	map(0x8000, 0x8fff).ram();
	map(0xa000, 0xafff).ram();
	map(0xb800, 0xb9ff).rw("eeprom", FUNC(eeprom_parallel_28xx_device::read), FUNC(eeprom_parallel_28xx_device::write));
}

void v102_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x3f).rw("vpac", FUNC(crt9007_device::read), FUNC(crt9007_device::write));
	map(0x40, 0x43).rw("mpsc", FUNC(upd7201_device::ba_cd_r), FUNC(upd7201_device::ba_cd_w));
	map(0x60, 0x61).rw("usart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x80, 0x83).w("pit", FUNC(pit8253_device::write));
	map(0xa0, 0xa3).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
}

void v102_state::machine_start()
{
	m_hs_state = false;
	m_kb_clock = false;

	m_mpsc->ctsa_w(0);
	m_mpsc->ctsb_w(0);

	save_item(NAME(m_hs_state));
	save_item(NAME(m_kb_clock));
}

static INPUT_PORTS_START(v102)
INPUT_PORTS_END

void v102_state::v102(machine_config &config)
{
	Z80(config, m_maincpu, 18.575_MHz_XTAL / 5); // divider not verified
	m_maincpu->set_addrmap(AS_PROGRAM, &v102_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &v102_state::io_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(18.575_MHz_XTAL, 970, 0, 800, 319, 0, 300);
	//screen.set_raw(18.575_MHz_XTAL, 948, 0, 792, 319, 0, 300);
	screen.set_screen_update(FUNC(v102_state::screen_update));

	crt9007_device &vpac(CRT9007(config, "vpac", 18.575_MHz_XTAL / 10));
	vpac.set_character_width(10); // 6 in 132-column mode
	vpac.int_callback().set("mainirq", FUNC(input_merger_device::in_w<2>));
	vpac.hs_callback().set(FUNC(v102_state::hs_w));
	vpac.set_screen("screen");

	EEPROM_2804(config, "eeprom");

	UPD7201(config, m_mpsc, 18.575_MHz_XTAL / 5); // divider not verified
	m_mpsc->out_int_callback().set("mainirq", FUNC(input_merger_device::in_w<0>));
	m_mpsc->out_txda_callback().set("keyboard", FUNC(v102_keyboard_device::write_rxd));
	m_mpsc->out_txdb_callback().set("aux", FUNC(rs232_port_device::write_txd));
	m_mpsc->out_dtrb_callback().set("aux", FUNC(rs232_port_device::write_dtr));
	m_mpsc->out_rtsb_callback().set("aux", FUNC(rs232_port_device::write_rts));

	i8251_device &usart(I8251(config, "usart", 18.575_MHz_XTAL / 5)); // divider not verified
	usart.rxrdy_handler().set("mainirq", FUNC(input_merger_device::in_w<1>));
	usart.txd_handler().set("modem", FUNC(rs232_port_device::write_txd));
	usart.dtr_handler().set("modem", FUNC(rs232_port_device::write_dtr));
	usart.rts_handler().set("modem", FUNC(rs232_port_device::write_rts));

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	pit8253_device &pit(PIT8253(config, "pit", 0));
	pit.set_clk<0>(18.575_MHz_XTAL / 6);
	pit.set_clk<1>(18.575_MHz_XTAL / 6);
	pit.set_clk<2>(18.575_MHz_XTAL / 6);
	pit.out_handler<0>().set("usart", FUNC(i8251_device::write_txc));
	pit.out_handler<1>().set("usart", FUNC(i8251_device::write_rxc));
	pit.out_handler<2>().set(m_mpsc, FUNC(upd7201_device::txcb_w));
	pit.out_handler<2>().append(m_mpsc, FUNC(upd7201_device::rxcb_w));

	I8255(config, "ppi");

	v102_keyboard_device &keyboard(V102_KEYBOARD(config, "keyboard"));
	keyboard.txd_callback().set(m_mpsc, FUNC(upd7201_device::rxa_w));

	rs232_port_device &modem(RS232_PORT(config, "modem", default_rs232_devices, nullptr));
	modem.rxd_handler().set("usart", FUNC(i8251_device::write_rxd));
	modem.cts_handler().set("usart", FUNC(i8251_device::write_cts));
	modem.dcd_handler().set("usart", FUNC(i8251_device::write_dsr));

	rs232_port_device &aux(RS232_PORT(config, "aux", default_rs232_devices, nullptr));
	aux.rxd_handler().set(m_mpsc, FUNC(upd7201_device::rxb_w));
	aux.dcd_handler().set(m_mpsc, FUNC(upd7201_device::dcdb_w)); // DTR (printer busy)
}


/**************************************************************************************************************

Visual 102. (VT-102 clone plus graphics)
Chips: D780C-1 (Z80), CRT9021B-018, COM8251A, D8255AC-5, 2x CRT9006-135, CRT9007, M5L8253P-5, X2804AP-35, D7201C
Crystals: 18.575000
Keyboard: TMP8039P-6

***************************************************************************************************************/

ROM_START( v102 )
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_LOAD( "245-001.u1",  0x0000, 0x4000, CRC(c36cc525) SHA1(a45e75ded10979c8e3ad262e2cf5818e08db762c) )

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "260-001.u50", 0x0000, 0x1000, CRC(732f5b99) SHA1(d105bf9f3ed41109d7181bcf0223bb280afe3f0a) )
ROM_END

} // anonymous namespace


COMP( 1984, v102, 0, 0, v102, v102, v102_state, empty_init, "Visual Technology", "Visual 102", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



v1050.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

Visual 1050

PCB Layout
----------

REV B-1

|---------------------------------------------------------------------------------------------------|
|                                                               |----------|                        |
|           9216                                                |   ROM0   |        LS74            |
|                                                               |----------|                        |
--                      LS00    7406            LS255   LS393                       4164    4164    |
||      |-----------|                                           |----------|                        |
||      |   WD1793  |   LS14    7407    -       LS393   LS74    |  8251A   |        4164    4164    |
||      |-----------|                   |                       |----------|                        |
|C                                      |                                           4164    4164    |
|N      7406    LS00    LS74    LS14    |C      LS20    LS08    LS75                                |
|1                                      |N                                          4164    4164    |
||                                      |5                                                          |
||      7406    LS195           LS244   |       LS04    LS32    LS08                4164    4164    |
||                                      -                                                           |
||      |------------|  |--------|                                                  4164    4164    |
-- BAT  |   8255A    |  |  8214  |              LS139   LS32    LS138   LS17                        |
--      |------------|  |--------|      -                                           4164    4164    |
||                                      |C                                                          |
||      |--------|      LS00    LS00    |N      LS00    LS32    LS138               4164    4164    |
|C      | 8251A  |                      |6                                                          |
|N      |--------|      |------------|  -                       |------------|                      |
|2                      |   8255A    |                          |    Z80A    |                      |
||      1488 1489  RTC  |------------|          LS00    LS32    |------------|      LS257   LS257   |
||                                                                                                  |
--                                                              |------------|      |------------|  |
|                                                       LS04    |   8255A    |      |   8255A    |  |
|                                                               |------------|      |------------|  |
|                                                                                                   |
|                                               7404                                LS257   LS257   |
|                                                   16MHz                                           |
|       LS04    LS74    LS257                                                       9016    9016    |
|                                                                                                   |
--      LS74    LS04    LS74    LS163                                               9016    9016    |
||                                                                                                  |
||                                                                                  9016    9016    |
||      LS02    LS163   LS74    7404                            7404                                |
||                                                                                  9016    9016    |
||                                                                                                  |
|C      LS244   LS10    LS257                   LS362           |----------|        9016    9016    |
|N                                                              |   ROM1   |                        |
|3      LS245   7404    LS273                   LS32            |----------|        9016    9016    |
||                                                                                                  |
||      7407            LS174                   LS32                                9016    9016    |
||              15.36MHz                                                                            |
||                                                              |------------|      9016    9016    |
--                      LS09    LS04            LS12            |    6502    |                      |
|                                                               |------------|      9016    9016    |
--                                                                                                  |
|C              LS175   LS86    LS164           LS164                               9016    9016    |
|N                                                              |------------|                      |
|4                                                              |    6845    |      LS253   LS253   |
--      7426    LS02    LS74    LS00            LS164           |------------|                      |
|                                                                                   LS253   LS253   |
|                       REV B-1 W/O 1059            S/N 492                                         |
|---------------------------------------------------------------------------------------------------|

Notes:
    All IC's shown.

    ROM0    - "IC 244-032 V1.0"
    ROM1    - "IC 244-033 V1.0"
    Z80A    - Zilog Z8400APS Z80A CPU
    6502    - Synertek SY6502A CPU
    4164    - NEC D4164-2 64Kx1 Dynamic RAM
    9016    - AMD AM9016EPC 16Kx1 Dynamic RAM
    8214    - NEC uPB8214C Priority Interrupt Control Unit
    8255A   - NEC D8255AC-5 Programmable Peripheral Interface
    8251A   - NEC D8251AC Programmable Communication Interface
    WD1793  - Mitsubishi MB8877 Floppy Disc Controller
    9216    - SMC FDC9216 Floppy Disk Data Separator
    1488    - Motorola MC1488 Quad Line EIA-232D Driver
    1489    - Motorola MC1489 Quad Line Receivers
    6845    - Hitachi HD46505SP CRT Controller
    RTC     - OKI MSM58321RS Real Time Clock
    BAT     - 3.4V battery
    CN1     - parallel connector
    CN2     - serial connector
    CN3     - winchester connector
    CN4     - monitor connector
    CN5     - floppy data connector
    CN6     - floppy power connector

*/

/*

    Using the hard disk
    -------------------

    Use the chdman utility to create a Tandon TM501 (5MB) or CMI CM-5412 (10MB) hard disk image:

    $ chdman createhd -chs 306,2,32 -ss 256 -o tm501.chd
    $ chdman createhd -chs 306,4,32 -ss 256 -o cm5412.chd

    Start the Visual 1050 emulator with the floppy and hard disk images mounted:

    $ mame v1050 -flop1 cpm3:flop2 -hard tm501.chd
    $ mame v1050 -flop1 cpm3:flop2 -hard cm5412.chd

    Start the Winchester Format Program from the CP/M prompt:

    A>fmtwinch

    Enter Y to continue.
    Enter A for 5MB, or B for 10MB hard disk.
    Enter C to start formatting.

    Once the formatting is complete, the CP/M system files need to be copied over to the hard disk:

    A>copysys

    Enter source drive name "a" and press RETURN.
    Enter target drive name "c" and press RETURN.
    Enter "y" at the prompt for CPM3.SYS.
    Enter "y" at the prompt for CCP.COM.
    Press RETURN to return to CP/M.

    You can now boot from the hard disk with:

    $ mame v1050 -hard tm501.chd
    $ mame v1050 -hard cm5412.chd

    Or skip all of the above and use the preformatted images in the software list:

    $ mame v1050 -hard cpm3hd5
    $ mame v1050 -hard cpm3hd10

*/

/*

    TODO:

    - floppy 1 is broken
    - write to banked RAM at 0x0000-0x1fff when ROM is active
    - real keyboard w/i8049
    - keyboard beeper (NE555 wired in strange mix of astable/monostable modes)

*/

#include "emu.h"
#include "v1050.h"

#include "bus/rs232/rs232.h"

#include "softlist.h"
#include "speaker.h"

#include "utf8.h"


void v1050_state::set_interrupt(int line, int state)
{
	line ^= 7;
	if (state)
	{
		m_int_state |= (1 << line);
	}
	else
	{
		m_int_state &= ~(1 << line);
	}

	m_pic->r_w(line, ((m_int_state & m_int_mask) & (1 << line)) ? 0 : 1);
}

void v1050_state::bankswitch()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	int bank = (m_bank >> 1) & 0x03;

	if (BIT(m_bank, 0))
	{
		program.install_readwrite_bank(0x0000, 0x1fff, membank("bank1"));
		membank("bank1")->set_entry(bank);
	}
	else
	{
		program.install_read_bank(0x0000, 0x1fff, membank("bank1"));
		program.unmap_write(0x0000, 0x1fff);
		membank("bank1")->set_entry(3);
	}

	membank("bank2")->set_entry(bank);

	if (bank == 2)
	{
		program.unmap_readwrite(0x4000, 0xbfff);
	}
	else
	{
		program.install_readwrite_bank(0x4000, 0x7fff, membank("bank3"));
		program.install_readwrite_bank(0x8000, 0xbfff, membank("bank4"));
		membank("bank3")->set_entry(bank);
		membank("bank4")->set_entry(bank);
	}

	membank("bank5")->set_entry(bank);
}

// Keyboard HACK

static const uint8_t V1050_KEYCODES[4][12][8] =
{
	{   // unshifted
		{ 0xc0, 0xd4, 0xd8, 0xdc, 0xe0, 0xe4, 0xe8, 0xec },
		{ 0xf0, 0xfc, 0x90, 0xf4, 0xf8, 0x94, 0xc4, 0xc8 },
		{ 0xcc, 0xd0, 0x1b, 0x31, 0x32, 0x33, 0x34, 0x35 },
		{ 0x36, 0x37, 0x38, 0x39, 0x30, 0x2d, 0x3d, 0x60 },
		{ 0x08, 0x88, 0x8c, 0x71, 0x77, 0x65, 0x72, 0x74 },
		{ 0x79, 0x75, 0x69, 0x6f, 0x70, 0x5b, 0x5d, 0x0d },
		{ 0x7f, 0x00, 0x80, 0x61, 0x73, 0x64, 0x66, 0x67 },
		{ 0x68, 0x6a, 0x6b, 0x6c, 0x3b, 0x27, 0x5c, 0x84 },
		{ 0x00, 0x7a, 0x78, 0x63, 0x76, 0x62, 0x6e, 0x6d },
		{ 0x2c, 0x2e, 0x2f, 0x00, 0x0a, 0x20, 0x81, 0x82 },
		{ 0xb7, 0xb8, 0xb9, 0xad, 0xb4, 0xb5, 0xb6, 0xac },
		{ 0xb1, 0xb2, 0xb3, 0x83, 0xb0, 0xae, 0x00, 0x00 },
	},

	{   // shifted
		{ 0xc1, 0xd5, 0xd9, 0xdd, 0xe1, 0xe5, 0xe9, 0xed },
		{ 0xf1, 0xfd, 0x91, 0xf5, 0xf9, 0x95, 0xc5, 0xc9 },
		{ 0xcd, 0xd1, 0x1b, 0x21, 0x40, 0x23, 0x24, 0x25 },
		{ 0x5e, 0x26, 0x2a, 0x28, 0x29, 0x5f, 0x2b, 0x7e },
		{ 0x08, 0x89, 0x8d, 0x51, 0x57, 0x45, 0x52, 0x54 },
		{ 0x59, 0x55, 0x49, 0x4f, 0x50, 0x7b, 0x7d, 0x0d },
		{ 0x7f, 0x00, 0x80, 0x41, 0x53, 0x44, 0x46, 0x47 },
		{ 0x48, 0x4a, 0x4b, 0x4c, 0x3a, 0x22, 0x7c, 0x85 },
		{ 0x00, 0x5a, 0x58, 0x43, 0x56, 0x42, 0x4e, 0x4d },
		{ 0x3c, 0x3e, 0x3f, 0x0a, 0x20, 0x81, 0x82, 0xb7 },
		{ 0xb8, 0xb9, 0xad, 0xb4, 0xb5, 0xb6, 0xac, 0xb1 },
		{ 0xb2, 0xb3, 0x83, 0xb0, 0xa0, 0x00, 0x00, 0x00 },
	},

	{   // control
		{ 0xc2, 0xd6, 0xda, 0xde, 0xe2, 0xe6, 0xea, 0xee },
		{ 0xf2, 0xfe, 0x92, 0xf6, 0xfa, 0x96, 0xc6, 0xca },
		{ 0xce, 0xd2, 0x1b, 0x31, 0x32, 0x33, 0x34, 0x35 },
		{ 0x36, 0x37, 0x38, 0x39, 0x30, 0x2d, 0x3d, 0x00 },
		{ 0x08, 0x8a, 0x8e, 0x11, 0x17, 0x05, 0x12, 0x14 },
		{ 0x19, 0x15, 0x09, 0x0f, 0x10, 0x1b, 0x1d, 0x0d },
		{ 0x7f, 0x00, 0x80, 0x01, 0x13, 0x04, 0x06, 0x07 },
		{ 0x08, 0x0a, 0x0b, 0x0c, 0x3b, 0x27, 0x1c, 0x86 },
		{ 0x00, 0x1a, 0x18, 0x03, 0x16, 0x02, 0x0e, 0x0d },
		{ 0x2c, 0x2e, 0x2f, 0x0a, 0x20, 0x81, 0x82, 0xb7 },
		{ 0xb8, 0xb9, 0xad, 0xb4, 0xb5, 0xb6, 0xac, 0xb1 },
		{ 0xb2, 0xb3, 0x83, 0xb0, 0xae, 0x00, 0x00, 0x00 },
	},

	{   // shift & control
		{ 0xc3, 0xd7, 0xdb, 0xdf, 0xe3, 0xe7, 0xeb, 0xef },
		{ 0xf3, 0xff, 0x93, 0xf7, 0xfb, 0x97, 0xc7, 0xcb },
		{ 0xcf, 0xd3, 0x1b, 0x21, 0x00, 0x23, 0x24, 0x25 },
		{ 0x1e, 0x26, 0x2a, 0x28, 0x29, 0x1f, 0x2b, 0x1e },
		{ 0x08, 0x8b, 0x8f, 0x11, 0x17, 0x05, 0x12, 0x14 },
		{ 0x19, 0x15, 0x09, 0x0f, 0x10, 0x1b, 0x1d, 0x0d },
		{ 0x7f, 0x00, 0x80, 0x01, 0x13, 0x04, 0x06, 0x07 },
		{ 0x08, 0x0a, 0x0b, 0x0c, 0x3a, 0x22, 0x1c, 0x87 },
		{ 0x00, 0x1a, 0x18, 0x03, 0x16, 0x02, 0x0e, 0x0d },
		{ 0x3c, 0x3e, 0x3f, 0x0a, 0x20, 0x81, 0x82, 0xb7 },
		{ 0xb8, 0xb9, 0xad, 0xb4, 0xb5, 0xb6, 0xac, 0xb1 },
		{ 0xb2, 0xb3, 0x83, 0xb0, 0xae, 0x00, 0x00, 0x00 },
	}
};

void v1050_state::scan_keyboard()
{
	static const char *const keynames[] = { "ROW0", "ROW1", "ROW2", "ROW3", "ROW4", "ROW5", "ROW6", "ROW7", "ROW8", "ROW9", "ROW10", "ROW11" };
	int table = 0, row, col;
	int keydata = 0xff;

	uint8_t line_mod = ioport("ROW12")->read();

	if((line_mod & 0x07) && (line_mod & 0x18))
	{
		table = 3;  // shift & control
	}
	else if (line_mod & 0x07)
	{
		table = 1; // shifted
	}
	else if (line_mod & 0x18)
	{
		table = 2; // ctrl
	}

	// scan keyboard
	for (row = 0; row < 12; row++)
	{
		uint8_t data = ioport(keynames[row])->read();

		for (col = 0; col < 8; col++)
		{
			if (!BIT(data, col))
			{
				// latch key data
				keydata = V1050_KEYCODES[table][row][col];

				if (m_keydata != keydata)
				{
					m_keydata = keydata;
					m_keyavail = 1;

					set_interrupt(INT_KEYBOARD, 1);
					return;
				}
			}
		}
	}

	m_keydata = keydata;
}

TIMER_DEVICE_CALLBACK_MEMBER(v1050_state::v1050_keyboard_tick)
{
	scan_keyboard();
}

uint8_t v1050_state::kb_data_r()
{
	m_keyavail = 0;

	set_interrupt(INT_KEYBOARD, 0);

	return m_keydata;
}

uint8_t v1050_state::kb_status_r()
{
	uint8_t val = m_uart_kb->status_r();

	return val | (m_keyavail ? 0x02 : 0x00);
}

// Z80 Read/Write Handlers

void v1050_state::v1050_i8214_w(uint8_t data)
{
	m_pic->b_w((data >> 1) & 0x07);
	m_pic->sgs_w(BIT(data, 4));
}

uint8_t v1050_state::vint_clr_r()
{
	set_interrupt(INT_VSYNC, 0);

	return 0xff;
}

void v1050_state::vint_clr_w(uint8_t data)
{
	set_interrupt(INT_VSYNC, 0);
}

uint8_t v1050_state::dint_clr_r()
{
	set_interrupt(INT_DISPLAY, 0);

	return 0xff;
}

void v1050_state::dint_clr_w(uint8_t data)
{
	set_interrupt(INT_DISPLAY, 0);
}

void v1050_state::bank_w(uint8_t data)
{
	m_bank = data;

	bankswitch();
}

// SY6502A Read/Write Handlers

void v1050_state::dint_w(uint8_t data)
{
	set_interrupt(INT_DISPLAY, 1);
}

void v1050_state::dvint_clr_w(uint8_t data)
{
	m_subcpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
}

void v1050_state::sasi_data_w(uint8_t data)
{
	m_sasi_data = data;

	if (m_sasi_data_enable)
	{
		m_sasi_data_out->write(m_sasi_data);
	}
}

void v1050_state::write_sasi_io(int state)
{
	m_sasi_ctrl_in->write_bit4(state);

	m_sasi_data_enable = state;

	if (m_sasi_data_enable)
	{
		m_sasi_data_out->write(m_sasi_data);
	}
	else
	{
		m_sasi_data_out->write(0);
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(v1050_state::sasi_ack_tick)
{
	m_sasibus->write_ack(0);
}

TIMER_DEVICE_CALLBACK_MEMBER(v1050_state::sasi_rst_tick)
{
	m_sasibus->write_rst(0);
}

void v1050_state::sasi_ctrl_w(uint8_t data)
{
	/*

	    bit     description

	    0       SEL
	    1       ACK
	    2
	    3
	    4
	    5
	    6
	    7       RST

	*/

	m_sasibus->write_sel(BIT(data, 0));

	if (BIT(data, 1))
	{
		// send acknowledge pulse
		m_sasibus->write_ack(1);

		m_timer_ack->adjust(attotime::from_nsec(100));
	}

	if (BIT(data, 7))
	{
		// send reset pulse
		m_sasibus->write_rst(1);

		m_timer_rst->adjust(attotime::from_nsec(100));
	}
}

// Memory Maps

void v1050_state::v1050_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).bankrw("bank1");
	map(0x2000, 0x3fff).bankrw("bank2");
	map(0x4000, 0x7fff).bankrw("bank3");
	map(0x8000, 0xbfff).bankrw("bank4");
	map(0xc000, 0xffff).bankrw("bank5");
}

void v1050_state::v1050_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x84, 0x87).rw(m_ppi_disp, FUNC(i8255_device::read), FUNC(i8255_device::write));
//  map(0x88, 0x89).rw(m_uart_kb, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x88, 0x88).r(FUNC(v1050_state::kb_data_r)).w(m_uart_kb, FUNC(i8251_device::data_w));
	map(0x89, 0x89).r(FUNC(v1050_state::kb_status_r)).w(m_uart_kb, FUNC(i8251_device::control_w));
	map(0x8c, 0x8d).rw(m_uart_sio, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x90, 0x93).rw(I8255A_MISC_TAG, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x94, 0x97).rw(m_fdc, FUNC(mb8877_device::read), FUNC(mb8877_device::write));
	map(0x9c, 0x9f).rw(I8255A_RTC_TAG, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xa0, 0xa0).rw(FUNC(v1050_state::vint_clr_r), FUNC(v1050_state::vint_clr_w));
	map(0xb0, 0xb0).rw(FUNC(v1050_state::dint_clr_r), FUNC(v1050_state::dint_clr_w));
	map(0xc0, 0xc0).w(FUNC(v1050_state::v1050_i8214_w));
	map(0xd0, 0xd0).w(FUNC(v1050_state::bank_w));
	map(0xe0, 0xe0).w(FUNC(v1050_state::sasi_data_w)).r(m_sasi_data_in, FUNC(input_buffer_device::read));
	map(0xe1, 0xe1).r(m_sasi_ctrl_in, FUNC(input_buffer_device::read)).w(FUNC(v1050_state::sasi_ctrl_w));
}

void v1050_state::v1050_crt_mem(address_map &map)
{
	map(0x0000, 0x7fff).rw(FUNC(v1050_state::videoram_r), FUNC(v1050_state::videoram_w)).share("video_ram");
	map(0x8000, 0x8000).w(m_crtc, FUNC(mc6845_device::address_w));
	map(0x8001, 0x8001).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x9000, 0x9003).rw(m_ppi_6502, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xa000, 0xa000).rw(FUNC(v1050_state::attr_r), FUNC(v1050_state::attr_w));
	map(0xb000, 0xb000).w(FUNC(v1050_state::dint_w));
	map(0xc000, 0xc000).w(FUNC(v1050_state::dvint_clr_w));
	map(0xe000, 0xffff).rom();
}

// Input Ports

static INPUT_PORTS_START( v1050 )
	PORT_START("ROW0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) // HELP
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F4)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F5)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F6)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F7)

	PORT_START("ROW1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F8)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F9)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F10)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F11)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F12)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F13)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F14)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F15)

	PORT_START("ROW2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) // F16
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) // F17
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')

	PORT_START("ROW3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('`') PORT_CHAR('~')

	PORT_START("ROW4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BACKSPACE") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BREAK") PORT_CODE(KEYCODE_PAUSE)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("TAB") PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')

	PORT_START("ROW5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR('\r')

	PORT_START("ROW6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')

	PORT_START("ROW7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SCRLOCK)

	PORT_START("ROW8")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) // SHIFT
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')

	PORT_START("ROW9")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) // SHIFT
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LINE FEED") PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_NUMLOCK) // DOWN
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_NUMLOCK) // UP

	PORT_START("ROW10")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad -") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))

	PORT_START("ROW11")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad 0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Keypad +") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD )

	PORT_START("ROW12")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("LEFT SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RIGHT SHIFT") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("LEFT CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RIGHT CTRL") PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))
INPUT_PORTS_END

// 8214 Interface

void v1050_state::pic_int_w(int state)
{
	if (state == ASSERT_LINE)
	{
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
	}
}

// Display 8255A Interface

void v1050_state::disp_ppi_pc_w(uint8_t data)
{
	m_ppi_6502->pc2_w(BIT(data, 6));
	m_ppi_6502->pc4_w(BIT(data, 7));
}

void v1050_state::m6502_ppi_pc_w(uint8_t data)
{
	m_ppi_disp->pc2_w(BIT(data, 7));
	m_ppi_disp->pc4_w(BIT(data, 6));
}

// Miscellanous 8255A Interface

void v1050_state::misc_ppi_pa_w(uint8_t data)
{
	/*

	    bit     signal      description

	    PA0     f_ds<0>     drive 0 select
	    PA1     f_ds<1>     drive 1 select
	    PA2     f_ds<2>     drive 2 select
	    PA3     f_ds<3>     drive 3 select
	    PA4     f_side_1    floppy side select
	    PA5     f_pre_comp  precompensation
	    PA6     f_motor_on* floppy motor
	    PA7     f_dden*     double density select

	*/

	// floppy drive select
	floppy_image_device *floppy = nullptr;

	if (!BIT(data, 0)) floppy = m_floppy0->get_device();
	if (!BIT(data, 1)) floppy = m_floppy1->get_device();
	if (!BIT(data, 2)) floppy = m_floppy2->get_device();
	if (!BIT(data, 3)) floppy = m_floppy3->get_device();

	m_fdc->set_floppy(floppy);

	// floppy side select
	if (floppy) floppy->ss_w(BIT(data, 4));

	// floppy motor
	if (floppy) floppy->mon_w(BIT(data, 6));

	// density select
	m_fdc->dden_w(BIT(data, 7));
}

void v1050_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

void v1050_state::write_centronics_perror(int state)
{
	m_centronics_perror = state;
}

uint8_t v1050_state::misc_ppi_pc_r()
{
	/*

	    bit     signal      description

	    PC0     pr_strobe   printer strobe
	    PC1     f_int_enb   floppy interrupt enable
	    PC2     baud_sel_a
	    PC3     baud_sel_b
	    PC4     pr_busy*    printer busy
	    PC5     pr_pe*      printer paper end
	    PC6
	    PC7

	*/

	uint8_t data = 0;

	data |= m_centronics_busy << 4;
	data |= m_centronics_perror << 5;

	return data;
}

void v1050_state::set_baud_sel(int baud_sel)
{
	if (baud_sel != m_baud_sel)
	{
		int divider = 1;

		switch (baud_sel)
		{
		case 0:
			divider = 13 * 16; // 19200
			break;

		case 1:
			divider = 13 * 8; // 38400
			break;

		case 2:
			divider = 8; // 500000
			break;

		case 3:
			divider = 13 * 2; // 153600
			break;
		}

		m_clock_sio->set_clock_scale((double) 1 / divider);

		m_baud_sel = baud_sel;
	}
}

void v1050_state::misc_ppi_pc_w(uint8_t data)
{
	/*

	    bit     signal      description

	    PC0     pr_strobe   printer strobe
	    PC1     f_int_enb   floppy interrupt enable
	    PC2     baud_sel_a
	    PC3     baud_sel_b
	    PC4     pr_busy*    printer busy
	    PC5     pr_pe*      printer paper end
	    PC6
	    PC7

	*/

	// printer strobe
	m_centronics->write_strobe(BIT(data, 0));

	// floppy interrupt enable
	m_f_int_enb = BIT(data, 1);
	update_fdc();

	// baud select
	set_baud_sel((data >> 2) & 0x03);
}

// Real Time Clock 8255A Interface

void v1050_state::rtc_ppi_pb_w(uint8_t data)
{
	/*

	    bit     signal      description

	    PB0                 RS-232
	    PB1                 Winchester
	    PB2                 keyboard
	    PB3                 floppy disk interrupt
	    PB4                 vertical interrupt
	    PB5                 display interrupt
	    PB6                 expansion B
	    PB7                 expansion A

	*/

	m_int_mask = data;
}

uint8_t v1050_state::rtc_ppi_pa_r()
{
	return m_rtc_ppi_pa;
}

void v1050_state::rtc_ppi_pa_w(uint8_t data)
{
	m_rtc->d0_w((data >> 0) & 1);
	m_rtc->d1_w((data >> 1) & 1);
	m_rtc->d2_w((data >> 2) & 1);
	m_rtc->d3_w((data >> 3) & 1);
}

uint8_t v1050_state::rtc_ppi_pc_r()
{
	/*

	    bit     signal      description

	    PC0
	    PC1
	    PC2
	    PC3                 clock busy
	    PC4
	    PC5
	    PC6
	    PC7

	*/

	return m_rtc_ppi_pc;
}

void v1050_state::rtc_ppi_pc_w(uint8_t data)
{
	/*

	    bit     signal      description

	    PC0
	    PC1
	    PC2
	    PC3
	    PC4                 clock address write
	    PC5                 clock data write
	    PC6                 clock data read
	    PC7                 clock device select

	*/

	m_rtc->address_write_w(BIT(data, 4));
	m_rtc->write_w(BIT(data, 5));
	m_rtc->read_w(BIT(data, 6));
	m_rtc->cs2_w(BIT(data, 7));
}

// Keyboard 8251A Interface

void v1050_state::write_keyboard_clock(int state)
{
	m_uart_kb->write_txc(state);
	m_uart_kb->write_rxc(state);
}

void v1050_state::kb_rxrdy_w(int state)
{
	set_interrupt(INT_KEYBOARD, state);
}

// Serial 8251A Interface

void v1050_state::write_sio_clock(int state)
{
	m_uart_sio->write_txc(state);
	m_uart_sio->write_rxc(state);
}

void v1050_state::sio_rxrdy_w(int state)
{
	m_rxrdy = state;

	set_interrupt(INT_RS_232, m_rxrdy || m_txrdy);
}

void v1050_state::sio_txrdy_w(int state)
{
	m_txrdy = state;

	set_interrupt(INT_RS_232, m_rxrdy || m_txrdy);
}

// MB8877 Interface

void v1050_state::update_fdc()
{
	if (m_f_int_enb)
	{
		set_interrupt(INT_FLOPPY, m_fdc_irq ? 1 : 0);
		m_maincpu->set_input_line(INPUT_LINE_NMI, m_fdc_drq ? ASSERT_LINE : CLEAR_LINE);
	}
	else
	{
		set_interrupt(INT_FLOPPY, 0);
		m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
	}
}

// disk format: 80 tracks, 1 head, 10 sectors, 512 bytes sector length, first sector id 1

static void v1050_floppies(device_slot_interface &device)
{
	device.option_add("525ssqd", FLOPPY_525_SSQD); // Teac FD 55E-02-U
	device.option_add("525qd", FLOPPY_525_QD); // Teac FD 55-FV-35-U
}

void v1050_state::fdc_intrq_w(int state)
{
	m_fdc_irq = state;

	update_fdc();
}

void v1050_state::fdc_drq_w(int state)
{
	m_fdc_drq = state;

	update_fdc();
}


// Machine Initialization

IRQ_CALLBACK_MEMBER(v1050_state::v1050_int_ack)
{
	uint8_t vector = 0xf0 | (m_pic->a_r() << 1);

	//logerror("Interrupt Acknowledge Vector: %02x\n", vector);

	m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);

	return vector;
}

void v1050_state::machine_start()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	// initialize I8214
	m_pic->etlg_w(1);
	m_pic->inte_w(1);

	// initialize RTC
	m_rtc->cs1_w(1);

	// setup memory banking
	uint8_t *ram = m_ram->pointer();

	membank("bank1")->configure_entries(0, 2, ram, 0x10000);
	membank("bank1")->configure_entry(2, ram + 0x1c000);
	membank("bank1")->configure_entry(3, m_rom->base());

	program.install_readwrite_bank(0x2000, 0x3fff, membank("bank2"));
	membank("bank2")->configure_entries(0, 2, ram + 0x2000, 0x10000);
	membank("bank2")->configure_entry(2, ram + 0x1e000);

	program.install_readwrite_bank(0x4000, 0x7fff, membank("bank3"));
	membank("bank3")->configure_entries(0, 2, ram + 0x4000, 0x10000);

	program.install_readwrite_bank(0x8000, 0xbfff, membank("bank4"));
	membank("bank4")->configure_entries(0, 2, ram + 0x8000, 0x10000);

	program.install_readwrite_bank(0xc000, 0xffff, membank("bank5"));
	membank("bank5")->configure_entries(0, 3, ram + 0xc000, 0);

	bankswitch();

	// register for state saving
	save_item(NAME(m_int_mask));
	save_item(NAME(m_f_int_enb));
	save_item(NAME(m_fdc_irq));
	save_item(NAME(m_fdc_drq));
	save_item(NAME(m_keylatch));
	save_item(NAME(m_keydata));
	save_item(NAME(m_keyavail));
	save_item(NAME(m_rxrdy));
	save_item(NAME(m_txrdy));
	save_item(NAME(m_baud_sel));
	save_item(NAME(m_bank));
	save_item(NAME(m_centronics_busy));
	save_item(NAME(m_centronics_perror));
}

void v1050_state::machine_reset()
{
	m_bank = 0;
	bankswitch();

	set_baud_sel(0);

	m_fdc->reset();
}

// Machine Driver

void v1050_state::v1050(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 16_MHz_XTAL/4);
	m_maincpu->set_addrmap(AS_PROGRAM, &v1050_state::v1050_mem);
	m_maincpu->set_addrmap(AS_IO, &v1050_state::v1050_io);
	m_maincpu->set_irq_acknowledge_callback(FUNC(v1050_state::v1050_int_ack));

	M6502(config, m_subcpu, 15.36_MHz_XTAL/16);
	m_subcpu->set_addrmap(AS_PROGRAM, &v1050_state::v1050_crt_mem);
	config.set_perfect_quantum(m_subcpu);

	// keyboard HACK
	TIMER(config, "keyboard").configure_periodic(FUNC(v1050_state::v1050_keyboard_tick), attotime::from_hz(60));

	// video hardware
	v1050_video(config);

	// devices
	I8214(config, m_pic, 16_MHz_XTAL/4);
	m_pic->int_wr_callback().set(FUNC(v1050_state::pic_int_w));

	MSM58321(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->d0_handler().set(FUNC(v1050_state::rtc_ppi_pa_0_w));
	m_rtc->d1_handler().set(FUNC(v1050_state::rtc_ppi_pa_1_w));
	m_rtc->d2_handler().set(FUNC(v1050_state::rtc_ppi_pa_2_w));
	m_rtc->d3_handler().set(FUNC(v1050_state::rtc_ppi_pa_3_w));
	m_rtc->busy_handler().set(FUNC(v1050_state::rtc_ppi_pc_3_w));

	I8255A(config, m_ppi_disp);
	m_ppi_disp->in_pa_callback().set(I8255A_M6502_TAG, FUNC(i8255_device::pb_r));
	m_ppi_disp->out_pc_callback().set(FUNC(v1050_state::disp_ppi_pc_w));

	i8255_device &ppi_misc(I8255A(config, I8255A_MISC_TAG));
	ppi_misc.in_pc_callback().set(FUNC(v1050_state::misc_ppi_pc_r));
	ppi_misc.out_pa_callback().set(FUNC(v1050_state::misc_ppi_pa_w));
	ppi_misc.out_pb_callback().set("cent_data_out", FUNC(output_latch_device::write));
	ppi_misc.out_pc_callback().set(FUNC(v1050_state::misc_ppi_pc_w));

	i8255_device &ppi_rtc(I8255A(config, I8255A_RTC_TAG));
	ppi_rtc.in_pa_callback().set(FUNC(v1050_state::rtc_ppi_pa_r));
	ppi_rtc.in_pc_callback().set(FUNC(v1050_state::rtc_ppi_pc_r));
	ppi_rtc.out_pa_callback().set(FUNC(v1050_state::rtc_ppi_pa_w));
	ppi_rtc.out_pb_callback().set(FUNC(v1050_state::rtc_ppi_pb_w));
	ppi_rtc.out_pc_callback().set(FUNC(v1050_state::rtc_ppi_pc_w));

	I8255A(config, m_ppi_6502);
	m_ppi_6502->in_pa_callback().set(m_ppi_disp, FUNC(i8255_device::pb_r));
	m_ppi_6502->out_pc_callback().set(FUNC(v1050_state::m6502_ppi_pc_w));

	I8251(config, m_uart_kb, 0/*16_MHz_XTAL/8,*/);
	m_uart_kb->txd_handler().set(V1050_KEYBOARD_TAG, FUNC(v1050_keyboard_device::si_w));
	m_uart_kb->rxrdy_handler().set(FUNC(v1050_state::kb_rxrdy_w));

	clock_device &clock_kb(CLOCK(config, CLOCK_KB_TAG, 16_MHz_XTAL/4/13/8));
	clock_kb.signal_handler().set(FUNC(v1050_state::write_keyboard_clock));

	// keyboard
	v1050_keyboard_device &keyboard(V1050_KEYBOARD(config, V1050_KEYBOARD_TAG, 0));
	keyboard.out_tx_handler().set(m_uart_kb, FUNC(i8251_device::write_rxd));

	I8251(config, m_uart_sio, 0/*16_MHz_XTAL/8,*/);
	m_uart_sio->txd_handler().set(RS232_TAG, FUNC(rs232_port_device::write_txd));
	m_uart_sio->dtr_handler().set(RS232_TAG, FUNC(rs232_port_device::write_dtr));
	m_uart_sio->rts_handler().set(RS232_TAG, FUNC(rs232_port_device::write_rts));
	m_uart_sio->rxrdy_handler().set(FUNC(v1050_state::sio_rxrdy_w));
	m_uart_sio->txrdy_handler().set(FUNC(v1050_state::sio_txrdy_w));

	rs232_port_device &rs232(RS232_PORT(config, RS232_TAG, default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_uart_sio, FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set(m_uart_sio, FUNC(i8251_device::write_dsr));

	CLOCK(config, m_clock_sio, 16_MHz_XTAL/4);
	m_clock_sio->signal_handler().set(FUNC(v1050_state::write_sio_clock));

	MB8877(config, m_fdc, 16_MHz_XTAL/16);
	m_fdc->intrq_wr_callback().set(FUNC(v1050_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(v1050_state::fdc_drq_w));
	FLOPPY_CONNECTOR(config, MB8877_TAG":0", v1050_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, MB8877_TAG":1", v1050_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, MB8877_TAG":2", v1050_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, MB8877_TAG":3", v1050_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);

	// SASI bus
	SCSI_PORT(config, m_sasibus, 0);
	m_sasibus->set_data_input_buffer(m_sasi_data_in);
	m_sasibus->req_handler().set(m_sasi_ctrl_in, FUNC(input_buffer_device::write_bit0)).exor(1);
	m_sasibus->bsy_handler().set(m_sasi_ctrl_in, FUNC(input_buffer_device::write_bit1));
	m_sasibus->msg_handler().set(m_sasi_ctrl_in, FUNC(input_buffer_device::write_bit2));
	m_sasibus->cd_handler().set(m_sasi_ctrl_in, FUNC(input_buffer_device::write_bit3));
	m_sasibus->io_handler().set(FUNC(v1050_state::write_sasi_io)).exor(1); // bit4
	m_sasibus->set_slot_device(1, "harddisk", S1410, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_0));

	OUTPUT_LATCH(config, m_sasi_data_out);
	m_sasibus->set_output_latch(*m_sasi_data_out);
	INPUT_BUFFER(config, m_sasi_data_in);
	INPUT_BUFFER(config, m_sasi_ctrl_in);

	TIMER(config, m_timer_ack).configure_generic(FUNC(v1050_state::sasi_ack_tick));
	TIMER(config, m_timer_rst).configure_generic(FUNC(v1050_state::sasi_rst_tick));

	// software lists
	SOFTWARE_LIST(config, "flop_list").set_original("v1050_flop");
	SOFTWARE_LIST(config, "hdd_list").set_original("v1050_hdd");

	// printer
	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(FUNC(v1050_state::write_centronics_busy));
	m_centronics->perror_handler().set(FUNC(v1050_state::write_centronics_perror));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	// internal ram
	RAM(config, RAM_TAG).set_default_size("128K");
}

// ROMs

ROM_START( v1050 )
	ROM_REGION( 0x10000, Z80_TAG, 0 )
	ROM_LOAD( "e244-032 rev 1.2.u86", 0x0000, 0x2000, CRC(46f847a7) SHA1(374db7a38a9e9230834ce015006e2f1996b9609a) )

	ROM_REGION( 0x10000, M6502_TAG, 0 )
	ROM_LOAD( "e244-033 rev 1.1.u77", 0xe000, 0x2000, CRC(c0502b66) SHA1(bc0015f5b14f98110e652eef9f7c57c614683be5) )
ROM_END

// System Drivers

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY                  FULLNAME       FLAGS
COMP( 1983, v1050, 0,      0,      v1050,   v1050, v1050_state, empty_init, "Visual Technology Inc", "Visual 1050", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND )



v50.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    Visual Technology Visual 50/55

    VT52 compatible terminal

    Hardware:
    - D780C (Z80)
    - 2764 and 2732 EPROM
    - 2x TMM314APL-1 (1k RAM)
    - X2210D NOVRAM (64x4) (X2210 for REV A)
    - Intel P8253-5 PIT
    - D8255AC-5 PPI
    - 2x D8251AC USART
    - SCN2672A (SCN2672 N for REV A)
    - 2673
    - TMM2016P-2 (2k VRAM)
    - C68100 IC240-001R00 (character generator?)
    - 17.320 MHz XTAL

    TODO:
    - Screen attributes (reverse sometimes works)
    - Smooth scroll
    - Screen brightness control
    - Most PPI connections are unknown
    - AUX port

    Notes:
    - PCB marked "PA015-A REV A" for R08 firmware
    - PCB marked "PA015 REV B 1183" for R11 firmware
    - Use TERM=vi50 and disable AUTO LF/CR in setup mode

***************************************************************************/

#include "emu.h"

#include "v50_kbd.h"

#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/input_merger.h"
#include "machine/pit8253.h"
#include "machine/x2212.h"
#include "video/scn2674.h"

#include "emupal.h"
#include "screen.h"


namespace {


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class visual50_state : public driver_device
{
public:
	visual50_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_novram(*this, "novram"),
		m_pit(*this, "pit"),
		m_screen(*this, "screen"),
		m_palette(*this, "palette"),
		m_pvtc(*this, "pvtc"),
		m_usart(*this, "usart%u", 0U),
		m_vram(*this, "vram"),
		m_chargen(*this, "chargen")
	{ }

	void visual50(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;
	required_device<x2210_device> m_novram;
	required_device<pit8253_device> m_pit;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_device<scn2672_device> m_pvtc;
	required_device_array<i8251_device, 2> m_usart;
	required_shared_ptr<uint8_t> m_vram;
	required_region_ptr<uint8_t> m_chargen;

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void char_map(address_map &map) ATTR_COLD;

	SCN2672_DRAW_CHARACTER_MEMBER(draw_character);

	void ppi_porta_w(uint8_t data);
	void ppi_portb_w(uint8_t data);
	void ppi_portc_w(uint8_t data);

	uint8_t recall_r();
	void recall_w(uint8_t data);
	uint8_t store_r();
	void store_w(uint8_t data);
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void visual50_state::mem_map(address_map &map)
{
	map(0x0000, 0x2fff).rom().region("maincpu", 0);
	map(0x8000, 0x83ff).ram();
	map(0xa000, 0xa03f).rw(m_novram, FUNC(x2210_device::read), FUNC(x2210_device::write));
	map(0xc000, 0xc000).rw(FUNC(visual50_state::recall_r), FUNC(visual50_state::recall_w));
	map(0xe000, 0xe000).rw(FUNC(visual50_state::store_r), FUNC(visual50_state::store_w));
}

void visual50_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x07).rw(m_pvtc, FUNC(scn2672_device::read), FUNC(scn2672_device::write));
	map(0x10, 0x11).rw(m_usart[0], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x20, 0x21).rw(m_usart[1], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x40, 0x40).r(m_pvtc, FUNC(scn2672_device::buffer_r));
	map(0x50, 0x50).w(m_pvtc, FUNC(scn2672_device::buffer_w));
	map(0x60, 0x63).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x70, 0x73).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
}

void visual50_state::char_map(address_map &map)
{
	map(0x000, 0x7ff).ram().share("vram");
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( visual50 )
INPUT_PORTS_END


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

SCN2672_DRAW_CHARACTER_MEMBER( visual50_state::draw_character )
{
	uint16_t data = m_chargen[(charcode & 0x7f) << 4 | linecount];
	const pen_t *const pen = m_palette->pens();

	if (BIT(charcode, 7))
		data = ~data; // wrong

	if (cursor)
		data = ~data;

	// foreground/background colors
	rgb_t fg = pen[2];
	rgb_t bg = pen[0];

	// draw 9 pixels of the character
	for (int i = 0; i < 9; i++)
		bitmap.pix(y, x + i) = BIT(data, 8 - i) ? fg : bg;
}

static const gfx_layout char_layout =
{
	8, 12,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ STEP8(0, 1) },
	{ STEP16(0, 8) },
	8 * 16
};

static GFXDECODE_START( chars )
	GFXDECODE_ENTRY("chargen", 0, char_layout, 0, 1)
GFXDECODE_END


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void visual50_state::ppi_porta_w(uint8_t data)
{
	// 7-------  unknown
	// -6------  unknown
	// --5-----  unknown
	// ---43210  brightness (0x00 = brightest, 0x1f = darkest)

	logerror("porta_w: %02x\n", data);
}

void visual50_state::ppi_portb_w(uint8_t data)
{
	// 7-------  unknown
	// -6------  monitor mode
	// --5-----  unknown
	// ---4----  reverse video
	// ----3---  unknown
	// -----2--  local echo
	// ------1-  unknown
	// -------0  unknown

	logerror("portb_w: %02x\n", data);
}

void visual50_state::ppi_portc_w(uint8_t data)
{
	// 7-------  unknown
	// -6------  rs232/current loop
	// --5-----  unknown
	// ---4----  unknown, toggles
	// ----3---  reverse video
	// -----2--  unknown
	// ------1-  unknown
	// -------0  unknown

	logerror("portc_w: %02x\n", data);
}

uint8_t visual50_state::recall_r()
{
	if (!machine().side_effects_disabled())
	{
		m_novram->recall(1);
		m_novram->recall(0);
	}

	return 0xff;
}

void visual50_state::recall_w(uint8_t data)
{
	m_novram->recall(1);
	m_novram->recall(0);
}

uint8_t visual50_state::store_r()
{
	if (!machine().side_effects_disabled())
	{
		m_novram->store(1);
		m_novram->store(0);
	}

	return 0xff;
}

void visual50_state::store_w(uint8_t data)
{
	m_novram->store(1);
	m_novram->store(0);
}

void visual50_state::machine_start()
{
}

void visual50_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE DEFINITIONS
//**************************************************************************

void visual50_state::visual50(machine_config &config)
{
	Z80(config, m_maincpu, 17.320_MHz_XTAL / 8); // divider not verified
	m_maincpu->set_addrmap(AS_PROGRAM, &visual50_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &visual50_state::io_map);

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	X2210(config, m_novram);

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(17.320_MHz_XTAL / 8);
	m_pit->set_clk<1>(17.320_MHz_XTAL / 8);
	m_pit->set_clk<2>(17.320_MHz_XTAL / 8);
	m_pit->out_handler<0>().set(m_usart[0], FUNC(i8251_device::write_rxc)); // or txc?
	m_pit->out_handler<1>().set(m_usart[1], FUNC(i8251_device::write_rxc));
	m_pit->out_handler<1>().append(m_usart[1], FUNC(i8251_device::write_txc));
	m_pit->out_handler<2>().set(m_usart[0], FUNC(i8251_device::write_txc)); // or rxc?

	i8255_device &ppi(I8255(config, "ppi"));
	ppi.out_pa_callback().set(FUNC(visual50_state::ppi_porta_w));
	ppi.out_pb_callback().set(FUNC(visual50_state::ppi_portb_w));
	ppi.out_pc_callback().set(FUNC(visual50_state::ppi_portc_w));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(17.320_MHz_XTAL, 900, 0, 720, 321, 0, 300);
	m_screen->set_screen_update(m_pvtc, FUNC(scn2672_device::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	GFXDECODE(config, "gfxdecode", m_palette, chars);

	SCN2672(config, m_pvtc, 17.320_MHz_XTAL / 9);
	m_pvtc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_pvtc->set_screen("screen");
	m_pvtc->set_character_width(9);
	m_pvtc->set_display_callback(FUNC(visual50_state::draw_character));
	m_pvtc->set_addrmap(0, &visual50_state::char_map);

	// modem port
	I8251(config, m_usart[0], 17.320_MHz_XTAL / 8); // divider not verified
	m_usart[0]->rxrdy_handler().set("mainirq", FUNC(input_merger_device::in_w<0>));
	m_usart[0]->txd_handler().set("modem", FUNC(rs232_port_device::write_txd));
	m_usart[0]->rts_handler().set("modem", FUNC(rs232_port_device::write_rts));

	rs232_port_device &modem(RS232_PORT(config, "modem", default_rs232_devices, nullptr));
	modem.rxd_handler().set(m_usart[0], FUNC(i8251_device::write_rxd));
	modem.cts_handler().set(m_usart[0], FUNC(i8251_device::write_cts));

	// keyboard
	I8251(config, m_usart[1], 17.320_MHz_XTAL / 8); // divider not verified
	m_usart[1]->rxrdy_handler().set("mainirq", FUNC(input_merger_device::in_w<1>));
	m_usart[1]->txd_handler().set("kbd", FUNC(v50_kbd_device::rxd_w));

	v50_kbd_device &kbd(V50_KBD(config, "kbd"));
	kbd.txd_cb().set(m_usart[1], FUNC(i8251_device::write_rxd));
	kbd.cts_cb().set(m_usart[1], FUNC(i8251_device::write_cts));
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( visual50 )
	ROM_REGION(0x3000, "maincpu", 0)
	ROM_DEFAULT_BIOS("r11")
	ROM_SYSTEM_BIOS(0, "r08", "Revision 0.08")
	ROMX_LOAD("e244-012_r08.u2", 0x0000, 0x2000, CRC(3931796d) SHA1(1f71b977e021a2f5eb5437c4056991cc7601768b), ROM_BIOS(0))
	ROMX_LOAD("e262-055_r08.u3", 0x2000, 0x1000, CRC(20067dc0) SHA1(bf8051117876246b3c3ba88c1c750e146800ca0b), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "r11", "Revision 0.11")
	ROMX_LOAD("e244-012_r11.u2", 0x0000, 0x2000, CRC(755f0722) SHA1(80b90882c52c9ddcc1cf455c9195d4e1ccda8ce8), ROM_BIOS(1))
	ROMX_LOAD("e262-055_r11.u3", 0x2000, 0x1000, CRC(90a142e8) SHA1(5f4c403b7ab09dcb3cfdc8f57f65e0a52992feed), ROM_BIOS(1))

	ROM_REGION(0x800, "chargen", 0)
	ROM_LOAD("ic240-001r00.u32", 0x000, 0x800, CRC(5041acd0) SHA1(ef3d952140c33c6cf2712463b53e8fca4a5400f4))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY              FULLNAME     FLAGS
COMP( 1983, visual50, 0,      0,      visual50, visual50, visual50_state, empty_init, "Visual Technology", "Visual 50", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )



v550.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

    The Visual 500 and 550 are the graphical members of the third series of display terminals released by Visual Technology.
    While they appear to use the same character generators as the text-mode-only Visual 300 and 330 and have similar detachable
    keyboards, they uniquely feature high-definition green-screen (P39 phosphor) monitors and support Tektronix 4010/4014-
    compatible graphics at a resolution of 768 x 555 pixels.

    The VT100-compatible Visual 550 was released first, and conforms to the ANSI X3.64 standard (like the Visual 300).
    The Visual 500 instead emulates the ADM3A, D200, Hazeltine 1500 and VT52 terminals (like the Visual 330).

***********************************************************************************************************************************/

#include "emu.h"
//include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/com8116.h"
#include "machine/input_merger.h"
#include "machine/nvram.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "v102_kbd.h"
#include "machine/z80sio.h"
#include "video/scn2674.h"
#include "video/upd7220.h"
#include "screen.h"


namespace {

class v550_state : public driver_device
{
public:
	v550_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_chargen(*this, "chargen")
		, m_usart(*this, "usart")
	{ }

	void v550(machine_config &config);

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { return 0; }

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void pvtc_char_map(address_map &map) ATTR_COLD;
	void pvtc_attr_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_region_ptr<u8> m_chargen;
	required_device<i8251_device> m_usart;
};


void v550_state::mem_map(address_map &map)
{
	map(0x0000, 0x7bff).rom().region("maincpu", 0);
	map(0x7c00, 0x7fff).ram().share("nvram"); // actually 4 bits wide
	map(0x8000, 0x87ff).ram();
}

void v550_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x01).rw("gdc", FUNC(upd7220_device::read), FUNC(upd7220_device::write));
	map(0x10, 0x10).w("brg1", FUNC(com8116_device::stt_str_w));
	map(0x20, 0x23).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x30, 0x31).rw(m_usart, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x40, 0x40).rw("mpsc", FUNC(upd7201_device::da_r), FUNC(upd7201_device::da_w));
	map(0x41, 0x41).rw("mpsc", FUNC(upd7201_device::ca_r), FUNC(upd7201_device::ca_w));
	map(0x48, 0x48).rw("mpsc", FUNC(upd7201_device::db_r), FUNC(upd7201_device::db_w));
	map(0x49, 0x49).rw("mpsc", FUNC(upd7201_device::cb_r), FUNC(upd7201_device::cb_w));
	map(0x50, 0x50).w("brg2", FUNC(com8116_device::stt_str_w));
	map(0x60, 0x67).rw("pvtc", FUNC(scn2672_device::read), FUNC(scn2672_device::write));
	map(0x70, 0x70).rw("pvtc", FUNC(scn2672_device::buffer_r), FUNC(scn2672_device::buffer_w));
	map(0x71, 0x71).rw("pvtc", FUNC(scn2672_device::attr_buffer_r), FUNC(scn2672_device::attr_buffer_w));
}

void v550_state::pvtc_char_map(address_map &map)
{
	map(0x0000, 0x0fff).ram();
}

void v550_state::pvtc_attr_map(address_map &map)
{
	map(0x0000, 0x0fff).ram();
}


INPUT_PORTS_START(v550)
INPUT_PORTS_END


void v550_state::machine_start()
{
	m_usart->write_cts(0);
}

void v550_state::v550(machine_config &config)
{
	Z80(config, m_maincpu, 34.846_MHz_XTAL / 16); // NEC D780C (2.177875 MHz verified)
	m_maincpu->set_addrmap(AS_PROGRAM, &v550_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &v550_state::io_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // NEC D444C-2 + battery

	upd7220_device &gdc(UPD7220(config, "gdc", 34.846_MHz_XTAL / 16)); // NEC D7220D (2.177875 MHz verified)
	gdc.set_screen("screen");

	I8255(config, "ppi"); // NEC D8255AC-5

	I8251(config, m_usart, 34.846_MHz_XTAL / 16); // NEC D8251AC
	m_usart->txd_handler().set("keyboard", FUNC(v550_keyboard_device::write_rxd));
	m_usart->rxrdy_handler().set("mainint", FUNC(input_merger_device::in_w<1>));

	upd7201_device& mpsc(UPD7201(config, "mpsc", 34.846_MHz_XTAL / 16)); // NEC D7201C
	mpsc.out_int_callback().set("mainint", FUNC(input_merger_device::in_w<0>));

	INPUT_MERGER_ANY_HIGH(config, "mainint").output_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	com8116_device &brg1(COM8116_020(config, "brg1", 1.8432_MHz_XTAL)); // SMC COM8116T-020
	brg1.ft_handler().set("mpsc", FUNC(upd7201_device::txcb_w));
	brg1.ft_handler().append("mpsc", FUNC(upd7201_device::rxcb_w));
	brg1.fr_handler().set("usart", FUNC(i8251_device::write_txc));
	brg1.fr_handler().append("usart", FUNC(i8251_device::write_rxc));

	com8116_device &brg2(COM8116_020(config, "brg2", 1.8432_MHz_XTAL)); // SMC COM8116T-020
	brg2.ft_handler().set("mpsc", FUNC(upd7201_device::txca_w));
	brg2.fr_handler().set("mpsc", FUNC(upd7201_device::rxca_w));

	v550_keyboard_device &keyboard(V550_KEYBOARD(config, "keyboard"));
	keyboard.txd_callback().set(m_usart, FUNC(i8251_device::write_rxd));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(34.846_MHz_XTAL, 19 * 102, 0, 19 * 80, 295, 0, 272);
	m_screen->set_screen_update(FUNC(v550_state::screen_update));

	scn2672_device &pvtc(SCN2672(config, "pvtc", 34.846_MHz_XTAL / 19));
	pvtc.set_addrmap(0, &v550_state::pvtc_char_map);
	pvtc.set_addrmap(1, &v550_state::pvtc_attr_map);
	pvtc.set_character_width(19);
	pvtc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	pvtc.set_screen("screen");
	// SCB2673 clock verified at 17.423 MHz
}


ROM_START( v550 )
	// Silkscreened on bottom left of PCB: "PA016-A REV B"

	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD("e244-001_r07_u42.bin", 0x0000, 0x2000, CRC(d18a8b62) SHA1(7faf8a9f1ae3148adacbff960a2663793710cef2))
	ROM_LOAD("e244-002_r07_u43.bin", 0x2000, 0x2000, CRC(1b62db47) SHA1(7ad69aea6088545d843c2e95737895f81c269ccc))
	ROM_LOAD("e244-003_r07_u44.bin", 0x4000, 0x2000, CRC(f6d6f734) SHA1(a9efa8ebe86addb77872dbe9863ea7f75b33a2b9))
	ROM_LOAD("e244-017_r07_u45.bin", 0x6000, 0x2000, CRC(b0dcd535) SHA1(9237723d01a720217f50f756bb55c7e5ed05a594))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("e242-085_r03_u97.bin", 0x0000, 0x1000, CRC(8a491cee) SHA1(d8a9546a7dd2ffc0a5e54524ee16068dde56975c))
ROM_END

} // anonymous namespace


COMP( 1982, v550, 0, 0, v550, v550, v550_state, empty_init, "Visual Technology", "Visual 550", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



v6809.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/**********************************************************************************************

Vegas 6809

Skeleton driver

Devices:

MC6809 cpu
MC6840 timer
MM58174 RTC
MB8876 (or FD1791) FDC
SY6545-1 CRTC
2x MC6821 PIA
2x MC6850 ACIA

Memory ranges:

0000-EFFF RAM
F000-F7FF Devices
F800-FFFF ROM

Monitor commands:

D boot from floppy (launch Flex OS)
F relaunch Flex
G go
M modify memory (. to exit)

ToDo:

- Colours (Looks like characters 0xc0-0xff produce coloured lores gfx).

- Connect the RTC interrupt pin (not supported currently)

- Find the missing character generator rom.

- Schematic is almost useless, riddled with omissions and errors. All documents are in
    French. The parts list only has half of the parts.

- Need software (there are floppy images, but they are not yet in a supported format)


*******************************************************************************************/

#include "emu.h"

#include "cpu/m6809/m6809.h"
#include "imagedev/floppy.h"
#include "machine/6821pia.h"
#include "machine/6840ptm.h"
#include "machine/6850acia.h"
#include "machine/clock.h"
#include "machine/keyboard.h"
#include "machine/mm58174.h"
#include "machine/wd_fdc.h"
#include "sound/spkrdev.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class v6809_state : public driver_device
{
public:
	v6809_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_pia0(*this, "pia0")
		, m_maincpu(*this, "maincpu")
		, m_crtc(*this, "crtc")
		, m_fdc(*this, "fdc")
		, m_floppy0(*this, "fdc:0")
		, m_floppy1(*this, "fdc:1")
		, m_speaker(*this, "speaker")
		, m_palette(*this, "palette")
		, m_p_chargen(*this, "chargen")
	{ }

	void v6809(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void speaker_en_w(int state);
	void speaker_w(int state);
	u8 pb_r();
	void pa_w(u8 data);
	void videoram_w(u8 data);
	void v6809_address_w(u8 data);
	void v6809_register_w(u8 data);
	void kbd_put(u8 data);
	MC6845_UPDATE_ROW(crtc_update_row);
	MC6845_ON_UPDATE_ADDR_CHANGED(crtc_update_addr);

	void v6809_mem(address_map &map) ATTR_COLD;

	u16 m_video_address = 0U;
	bool m_speaker_en = false;
	u8 m_video_index = 0U;
	u8 m_term_data = 0U;
	u8 m_vidbyte = 0U;
	std::unique_ptr<u8[]> m_vram;
	required_device<pia6821_device> m_pia0;
	required_device<cpu_device> m_maincpu;
	required_device<mc6845_device> m_crtc;
	required_device<mb8876_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	required_device<floppy_connector> m_floppy1;
	required_device<speaker_sound_device> m_speaker;
	required_device<palette_device> m_palette;
	required_region_ptr<u8> m_p_chargen;
};


void v6809_state::v6809_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xefff).ram();
	map(0xf000, 0xf000).mirror(0xfe).r(m_crtc, FUNC(mc6845_device::status_r)).w(FUNC(v6809_state::v6809_address_w));
	map(0xf001, 0xf001).mirror(0xfe).r(m_crtc, FUNC(mc6845_device::register_r)).w(FUNC(v6809_state::v6809_register_w));
	map(0xf200, 0xf200).mirror(0xff).w(FUNC(v6809_state::videoram_w));
	map(0xf500, 0xf501).mirror(0x36).rw("acia0", FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // modem
	map(0xf508, 0xf509).mirror(0x36).rw("acia1", FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // printer
	map(0xf600, 0xf603).mirror(0x3c).rw(m_fdc, FUNC(mb8876_device::read), FUNC(mb8876_device::write));
	map(0xf640, 0xf64f).mirror(0x30).rw("rtc", FUNC(mm58174_device::read), FUNC(mm58174_device::write));
	map(0xf680, 0xf683).mirror(0x3c).rw(m_pia0, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xf6c0, 0xf6c7).mirror(0x08).rw("ptm", FUNC(ptm6840_device::read), FUNC(ptm6840_device::write));
	map(0xf6d0, 0xf6d3).mirror(0x0c).rw("pia1", FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0xf800, 0xffff).rom().region("maincpu", 0);
}


/* Input ports */
static INPUT_PORTS_START( v6809 )
INPUT_PORTS_END

void v6809_state::machine_start()
{
	m_vram = make_unique_clear<u8[]>(0x0800);
	save_pointer(NAME(m_vram), 0x0800);
	save_item(NAME(m_speaker_en));
	save_item(NAME(m_term_data));
	save_item(NAME(m_video_address));
	save_item(NAME(m_video_index));
	save_item(NAME(m_vidbyte));
}

void v6809_state::machine_reset()
{
	m_term_data = 0;
	m_pia0->cb1_w(1);
}

// **** Video ****

/* F4 Character Displayer */
static const gfx_layout v6809_charlayout =
{
	8, 10,                  /* 8 x 10 characters */
	256,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_v6809 )
	GFXDECODE_ENTRY( "chargen", 0x0000, v6809_charlayout, 0, 1 )
GFXDECODE_END

MC6845_UPDATE_ROW( v6809_state::crtc_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	u32 *p = &bitmap.pix(y);

	for (u16 x = 0; x < x_count; x++)
	{
		u16 mem = (ma + x) & 0x7ff;
		u8 chr = m_vram[mem];
		u8 gfx = m_p_chargen[(chr<<4) | ra] ^ ((x == cursor_x) ? 0xff : 0);

		/* Display a scanline of a character (8 pixels) */
		*p++ = palette[BIT(gfx, 7)];
		*p++ = palette[BIT(gfx, 6)];
		*p++ = palette[BIT(gfx, 5)];
		*p++ = palette[BIT(gfx, 4)];
		*p++ = palette[BIT(gfx, 3)];
		*p++ = palette[BIT(gfx, 2)];
		*p++ = palette[BIT(gfx, 1)];
		*p++ = palette[BIT(gfx, 0)];
	}
}

MC6845_ON_UPDATE_ADDR_CHANGED( v6809_state::crtc_update_addr )
{
/* not sure what goes in here - parameters passed are device, address, strobe */
	m_video_address = address & 0x7ff;
}

void v6809_state::videoram_w(u8 data)
{
	m_vidbyte = data;
}

void v6809_state::v6809_address_w(u8 data)
{
	m_crtc->address_w(data);

	m_video_index = data & 0x1f;

	if (m_video_index == 31)
		m_vram[m_video_address] = m_vidbyte;
}

void v6809_state::v6809_register_w(u8 data)
{
	u16 temp = m_video_address;

	m_crtc->register_w(data);

	// Get transparent address
	if (m_video_index == 18)
		m_video_address = ((data & 7) << 8 ) | (temp & 0xff);
	else
	if (m_video_index == 19)
		m_video_address = data | (temp & 0xff00);
}

// **** Keyboard ****

void v6809_state::kbd_put(u8 data)
{
	m_term_data = data;
	m_pia0->cb1_w(0);
	m_pia0->cb1_w(1);
}

u8 v6809_state::pb_r()
{
	u8 ret = m_term_data;
	m_term_data = 0;
	return ret;
}

// can support 4 drives
void v6809_state::pa_w(u8 data)
{
	floppy_image_device *floppy = nullptr;
	if ((data & 3) == 0) floppy = m_floppy0->get_device();
	if ((data & 3) == 1) floppy = m_floppy1->get_device();
	//if ((data & 3) == 2) floppy = m_floppy2->get_device();
	//if ((data & 3) == 3) floppy = m_floppy3->get_device();

	m_fdc->set_floppy(floppy);

// Bits 2 and 3 go to the floppy connector but are not documented

	if (floppy)
	{
		floppy->mon_w(0);
		m_fdc->dden_w(BIT(data, 4));
	}
}

void v6809_state::speaker_en_w(int state)
{
	m_speaker_en = state;
}

void v6809_state::speaker_w(int state)
{
	if (m_speaker_en)
		m_speaker->level_w(state);
}

static void v6809_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}


// *** Machine ****

void v6809_state::v6809(machine_config &config)
{
	/* basic machine hardware */
	MC6809(config, m_maincpu, 16_MHz_XTAL / 4); // divided by 4 again internally
	m_maincpu->set_addrmap(AS_PROGRAM, &v6809_state::v6809_mem);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	screen.set_screen_update("crtc", FUNC(sy6545_1_device::screen_update));
	PALETTE(config, m_palette, palette_device::MONOCHROME);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_v6809);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* devices */
	SY6545_1(config, m_crtc, 16_MHz_XTAL / 8);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(v6809_state::crtc_update_row));
	m_crtc->set_on_update_addr_change_callback(FUNC(v6809_state::crtc_update_addr));

	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(v6809_state::kbd_put));

	// port A = drive select and 2 control lines ; port B = keyboard
	PIA6821(config, m_pia0);
	m_pia0->readpb_handler().set(FUNC(v6809_state::pb_r));
	m_pia0->writepa_handler().set(FUNC(v6809_state::pa_w));
	m_pia0->irqa_handler().set_inputline("maincpu", M6809_IRQ_LINE);
	m_pia0->irqb_handler().set_inputline("maincpu", M6809_IRQ_LINE);

	// no idea what this does
	pia6821_device &pia1(PIA6821(config, "pia1"));
	pia1.irqa_handler().set_inputline("maincpu", M6809_IRQ_LINE);
	pia1.irqb_handler().set_inputline("maincpu", M6809_IRQ_LINE);

	ptm6840_device &ptm(PTM6840(config, "ptm", 16_MHz_XTAL / 4));
	ptm.set_external_clocks(4000000.0/14.0, 4000000.0/14.0, (4000000.0/14.0)/8.0);
	ptm.o1_callback().set(FUNC(v6809_state::speaker_en_w));
	ptm.o2_callback().set(FUNC(v6809_state::speaker_w));
	ptm.irq_callback().set_inputline("maincpu", M6809_IRQ_LINE);

	ACIA6850(config, "acia0", 0);

	ACIA6850(config, "acia1", 0);

	clock_device &acia_clock(CLOCK(config, "acia_clock", 153600));
	acia_clock.signal_handler().set("acia0", FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append("acia0", FUNC(acia6850_device::write_rxc));
	acia_clock.signal_handler().append("acia1", FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append("acia1", FUNC(acia6850_device::write_rxc));

	MM58174(config, "rtc", 0);
	//rtc.irq_handler().set(m_pia0, FUNC(pia6821_device::cb2_w));   // unsupported by RTC emulation

	MB8876(config, m_fdc, 16_MHz_XTAL / 16);
	FLOPPY_CONNECTOR(config, "fdc:0", v6809_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", v6809_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
}

/* ROM definition */
ROM_START( v6809 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_SYSTEM_BIOS(0, "v1", "Original")
	ROMX_LOAD( "v6809.rom",   0x0000, 0x0800, CRC(54bf5f32) SHA1(10d1d70f0b51e2b90e5c29249d3eab4c6b0033a1), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v3", "Ver 3.3")
	ROMX_LOAD( "v6809v3.bin", 0x0000, 0x0800, CRC(f9cd126d) SHA1(2da719820e393efde801057d76b2a63dcfbd8541), ROM_BIOS(1))
	/* character generator not dumped, using the one from 'h19' for now */
	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "2716_444-29_h19font.bin", 0x0000, 0x0800, BAD_DUMP CRC(d595ac1d) SHA1(130fb4ea8754106340c318592eec2d8a0deaf3d0))
	ROM_RELOAD(0x0800, 0x0800)
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY     FULLNAME      FLAGS
COMP( 1982, v6809, 0,      0,      v6809,   v6809, v6809_state, empty_init, "Microkit", "Vegas 6809", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



vanguardmk1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:

/*
    Skeleton driver for Vanguard MK1 multipurpose bomb disposal robot by EOD Performance Inc.
*/

#include "emu.h"
#include "cpu/mc68hc11/mc68hc11.h"


namespace {

class vanguardmk1_state : public driver_device
{
public:
	vanguardmk1_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		{ }

	void vanguardmk1(machine_config &config);

private:
	void mcu_map(address_map &map) ATTR_COLD;
};

void vanguardmk1_state::mcu_map(address_map &map)
{
}


static INPUT_PORTS_START( vanguardmk1 )
INPUT_PORTS_END

void vanguardmk1_state::vanguardmk1(machine_config &config)
{
	mc68hc811e2_device &mcu(MC68HC811E2(config, "mcu", 8000000)); // unknown clock
	mcu.set_addrmap(AS_PROGRAM, &vanguardmk1_state::mcu_map);
	mcu.set_default_config(0xff);
}


ROM_START(vngrdmk1)
	ROM_REGION(0x800, "mcu:eeprom", 0)
	ROM_LOAD( "van24_aug04", 0x000, 0x800, CRC(ce63fcb9) SHA1(8f688e866e8fea888c77aa5be92ad09f684afd59) )
ROM_END

} // anonymous namespace


SYST( 2004?, vngrdmk1, 0, 0, vanguardmk1, vanguardmk1, vanguardmk1_state, empty_init, "EOD Performance Inc.", "Vanguard MK1", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_MECHANICAL | MACHINE_REQUIRES_ARTWORK )



vax11.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

        VAX-11

        VAX-11/785
        -------
        M7459 TRS, TERMINATOR & SILO
        M7460 KA785, SBL,CUP SBI LOW BITS
        M7461 KA785, SBH,SBI
        M7462 KA785, CAM, CACHE ADDRESS MATRIX
        M7463 KA785, CDM, CACHE DATA MATRIX
        M7464 KA785, TBM, CPU TRANSLATION BUFFER
        M7465 KA785, IDP, CPU INSTRUCTION DATA
        M7466 KA785, IRC, CPU INSTRUCTION DECODE
        M7467 KA785, DPB, CPU DATA PATH B
        M7468 KA785, DEP, CPU DATA PATH E
        M7469 KA785, DDP, CPU DATA PATH D
        M7470 KA785, DCP, CPU DATA PATH C
        M7471 KA785, DAP, CPU DATA PATH A
        M7472 KA785, CEW (CONDITION CODES, EXCEP
        M7473 KA785, ICL (INTERRUPT CONTROL, LOW
        M7474 KA785, CLK (CPU CLOCK)
        M7475 KA785, JCS, JOINT CONTROL STORE
        M7476 KA785, USC, MICRO SEQUENCER CONTROL
        M7477 KA785, CIB, CPU CONSOLE INTERFACE

                                                                                                                                                 +----------FP785--------+
        1     2     3     4     5     6     7     8     9     10    11    12    13    14    15    16    17   18    19    20    21   22     23    24    25    26    27    28    29
        M7459 M7460 M7461 M7462 M7463 M7464 M7465 M7466 M7467 M7468 M7469 M7470 M7471 M7472 M7473 M7474      M7475       M7475      UNUSED M7476 M7540 M7541 M7542 M7543 M7544 M7477
        TRS   SBL   SBH   CAM   CDM   TBM   IDP   IRC   DBP   DEP   DDP   DCP   DAP   CEH   ICL   CLK        JCS         JCS               USC   FNM   FMH   FML   FAD   FCT   CIB

        VAX-11/780
        -------
        M8237 TERMINATOR SILO
        M8218 SBL LOW BIT INTERFACE
        M8219 SBH HIGH BITS INTERFACE
        M8220 CAM CPU CACHE ADAPTER MATRIX
        M8221 CDM CPU CACHE DATA MATRIX
        M8222 TBM CPU TRANSLATION BUFFER MATRIX
        M8223 IRC CPU INSTRUCT DECODE&CLOCKS
        M8224 IDP CPU INSTRUCTION DATA PATH
        M8225 DBP DATA PATH B
        M8226 DEP DATA PATH E
        M8227 DDP DATA PATH D
        M8228 DCP DATA PATH C
        M8229 DAP DATA PATH A
        M8230 CEH CPU CONDITION CODE
        M8231 ICL CPU TRAPS AND INTERRUPT CONTROL
        M8232 CLK CPU CLOCK
        M8238 WCS KU780-A 2K WCS
        M8234 PCS KA780-A PCS, PROM CONTROL STORE
        M8235 USC MICRO SEQUENCE CONTROL
        M8236 CIB CONSOLE INTERFACE

                                                                                                                                                 +----------FP780--------+
        1     2     3     4     5     6     7     8     9     10    11    12    13    14    15    16    17   18    19    20    21   22     23    24    25    26    27    28    29
        M8237 M8218 M8219 M8220 M8221 M8222 M8223 M8224 M8225 M8226 M8227 M8228 M8229 M8230 M8231 M8232      M8233       M8233      M8234  M8235 M8285 M8286 M8287 M8288 M8289 M8236
        TRS   SBL   SBH   CAM   CDM   TBM   IDP   IRC   DBP   DEP   DDP   DCP   DAP   CEH   ICL   CLK        or          or         PCS    USC   FNM   FMH   FML   FAD   FCT   CIB
                                                                                                             M8238       M8238


        02/08/2012 Skeleton driver.

****************************************************************************/

#include "emu.h"
#include "cpu/t11/t11.h"
#include "machine/terminal.h"
#include "rx01.h"


namespace {

class vax11_state : public driver_device
{
public:
	vax11_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_terminal(*this, "terminal")
	{ }

	void vax11(machine_config &config);

private:
	required_device<t11_device> m_maincpu;
	required_device<generic_terminal_device> m_terminal;
	uint16_t term_r();
	uint16_t term_tx_status_r();
	uint16_t term_rx_status_r();
	void term_w(uint16_t data);
	void kbd_put(u8 data);
	uint8_t m_term_data = 0;
	uint16_t m_term_status = 0;
	void vax11_mem(address_map &map) ATTR_COLD;
};

void vax11_state::term_w(uint16_t data)
{
	m_terminal->write(data);
}

uint16_t vax11_state::term_r()
{
	m_term_status = 0x0000;
	return m_term_data;
}

uint16_t vax11_state::term_tx_status_r()
{   // always ready
	return 0xffff;
}

uint16_t vax11_state::term_rx_status_r()
{
	return m_term_status;
}

void vax11_state::vax11_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xbfff).ram();  // RAM
	map(0xc000, 0xd7ff).rom();

	map(0xfe78, 0xfe7b).rw("rx01", FUNC(rx01_device::read), FUNC(rx01_device::write));

	map(0xff70, 0xff71).r(FUNC(vax11_state::term_rx_status_r));
	map(0xff72, 0xff73).r(FUNC(vax11_state::term_r));
	map(0xff74, 0xff75).r(FUNC(vax11_state::term_tx_status_r));
	map(0xff76, 0xff77).w(FUNC(vax11_state::term_w));
}

/* Input ports */
static INPUT_PORTS_START( vax11 )
INPUT_PORTS_END

void vax11_state::kbd_put(u8 data)
{
	m_term_data = data;
	m_term_status = 0xffff;
}

void vax11_state::vax11(machine_config &config)
{
	/* basic machine hardware */
	T11(config, m_maincpu, XTAL(4'000'000)); // Need proper CPU here
	m_maincpu->set_initial_mode(0 << 13);
	m_maincpu->set_addrmap(AS_PROGRAM, &vax11_state::vax11_mem);

	/* video hardware */
	GENERIC_TERMINAL(config, m_terminal, 0);
	m_terminal->set_keyboard_callback(FUNC(vax11_state::kbd_put));

	RX01(config, "rx01", 0);
}

ROM_START( vax785 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	// M7477
	ROMX_LOAD( "23-144f1-00.e56", 0xc000, 0x0400, CRC(99c1f117) SHA1(f05b6e97bf258392656058864abc1177379194da), ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO | ROM_SKIP(1))
	ROMX_LOAD( "23-145f1-00.e68", 0xc000, 0x0400, CRC(098b63d2) SHA1(c2742aaccdac2921e1704c835ee5cef242cd7308), ROM_NIBBLE | ROM_SHIFT_NIBBLE_HI | ROM_SKIP(1))
	ROMX_LOAD( "23-146f1-00.e84", 0xc001, 0x0400, CRC(0f5f5d7b) SHA1(2fe325d2a78a8ce5146317cc39c084c4967c323c), ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO | ROM_SKIP(1))
	ROMX_LOAD( "23-147f1-00.e72", 0xc001, 0x0400, CRC(bde386f2) SHA1(fcb5a1fa505912c5f44781619c9508cd142721e3), ROM_NIBBLE | ROM_SHIFT_NIBBLE_HI | ROM_SKIP(1))

	ROMX_LOAD( "23-148f1-00.e58", 0xc800, 0x0400, CRC(fe4c61e3) SHA1(4641a236761692a8f45b14ed6a73f535d57c2daa), ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO | ROM_SKIP(1))
	ROMX_LOAD( "23-149f1-00.e70", 0xc800, 0x0400, CRC(a13f5f8a) SHA1(6a9d3b5a71a3249f3b9491d541c9854e071a320c), ROM_NIBBLE | ROM_SHIFT_NIBBLE_HI | ROM_SKIP(1))
	ROMX_LOAD( "23-150f1-00.e87", 0xc801, 0x0400, CRC(ca8d6419) SHA1(6d9c3e1e2f5a35f92c82240fcede14645aa83340), ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO | ROM_SKIP(1))
	ROMX_LOAD( "23-151f1-00.e74", 0xc801, 0x0400, CRC(58ce48d3) SHA1(230dbcab1470752befb6733a89e3612ad7fba10d), ROM_NIBBLE | ROM_SHIFT_NIBBLE_HI | ROM_SKIP(1))

	ROMX_LOAD( "23-236f1-00.e57", 0xd000, 0x0400, CRC(6f23470a) SHA1(d90a0bc56f04c2830f8cfb6b870db207b96e75b1), ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO | ROM_SKIP(1))
	ROMX_LOAD( "23-237f1-00.e69", 0xd000, 0x0400, CRC(2bf8cf0b) SHA1(6db79c5392b265e38b5b8b386528d7c138d995e9), ROM_NIBBLE | ROM_SHIFT_NIBBLE_HI | ROM_SKIP(1))
	ROMX_LOAD( "23-238f1-00.e85", 0xd001, 0x0400, CRC(ff569f71) SHA1(05985396047fb4639959000a1abe50d2f184deaa), ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO | ROM_SKIP(1))
	ROMX_LOAD( "23-239f1-00.e73", 0xd001, 0x0400, CRC(cec7abe3) SHA1(8b8b52bd46340c58efa5adef3f306e0cdcb77520), ROM_NIBBLE | ROM_SHIFT_NIBBLE_HI | ROM_SKIP(1))
ROM_END

} // anonymous namespace


/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  STATE        INIT        COMPANY                          FULLNAME      FLAGS */
COMP( 1984, vax785, 0,      0,      vax11,   vax11, vax11_state, empty_init, "Digital Equipment Corporation", "VAX-11/785", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



vboy.cpp
<---------------------------------------------------------------------->
// license:LGPL-2.1+
// copyright-holders:Angelo Salese, Miodrag Milanovic
/***************************************************************************

Nintendo Virtual Boy

References:
- http://www.goliathindustries.com/vb/
- http://www.vr32.de/modules/dokuwiki/doku.php?

TODO:
- complete VIP implementation (framebuffer plus other details);
- various timing issues (irq & events aren't known);
- sound;
- Better 2d/3d layout options for accessibility;

****************************************************************************/

#include "emu.h"

#include "vboysound.h"

#include "cpu/v810/v810.h"
#include "bus/vboy/slot.h"
#include "machine/timer.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "vboy.lh"


namespace {

#define READ_BGMAP(bgoffs) m_bgmap[(bgoffs) & 0xffff]
#define READ_WORLD(wldoffs)   READ_BGMAP((0x1d800 >> 1) + wldoffs)
#define READ_COLTAB1(wldoffs) READ_BGMAP((0x1dc00 >> 1) + wldoffs)
#define READ_COLTAB2(wldoffs) READ_BGMAP((0x1de00 >> 1) + wldoffs)
#define READ_OBJECTS(wldoffs) READ_BGMAP((0x1e000 >> 1) + wldoffs)

#define WRITE_OVR_TEMPDRAW_MAP(woffs, wdat) m_ovr_tempdraw_map[(woffs) & 0x3f] = wdat;
#define READ_OVR_TEMPDRAW_MAP(roffs) m_ovr_tempdraw_map[(roffs) & 0x3f];

#define READ_FONT(roffs) m_font[(roffs)&0x1ffff]

// bit of magic here, we also write pre-flipped copies of the data to extra ram we've allocated
// to simplify the draw loop (we can just pass the flip / unused bits as the upper character bits)
// (all TILE words are in the format of ccxy -ttt tttt tttt
//   where 'c' = palette, 'x/y' are flips, '-' is unused(?) and 't' is your basic tile number

#define WRITE_FONT(woffs) \
	COMBINE_DATA(&m_font[woffs]); /* normal */ \
	uint16_t dat = m_font[woffs]; \
	m_font[((woffs) + 0x4000)] = dat;     /* normal */ \
	m_font[((woffs) + 0x8000) ^ 7] = dat; /* flip y */ \
	m_font[((woffs) + 0xc000) ^ 7] = dat; /* flip y */ \
	dat = bitswap<16>(dat,1,0,3,2,5,4,7,6,9,8,11,10,13,12,15,14);  \
	m_font[((woffs) + 0x10000)] = dat;     /* flip x */ \
	m_font[((woffs) + 0x14000)] = dat;     /* flip x */ \
	m_font[((woffs) + 0x18000) ^ 7] = dat; /* flip x+y */ \
	m_font[((woffs) + 0x1c000) ^ 7] = dat; /* flip x+y */


class vboy_state : public driver_device
{
public:
	vboy_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cart(*this, "cartslot")
		, m_maintimer(*this, "timer_main")
		, m_palette(*this, "palette")
	{
	}

	void vboy(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

private:
	// FIXME: most if not all of these must be uint8_t
	struct vboy_regs_t
	{
		uint32_t lpc = 0, lpc2 = 0, lpt = 0, lpr = 0;
		uint32_t khb = 0, klb = 0;
		uint8_t thb = 0, tlb = 0;
		uint32_t tcr = 0, wcr = 0, kcr = 0x80;
	};

	struct vip_io_regs_t
	{
		uint16_t INTPND = 0;
		uint16_t INTENB = 0;
		uint16_t DPSTTS = 0;
		uint16_t DPCTRL = 0;
		uint16_t BRTA = 0;
		uint16_t BRTB = 0;
		uint16_t BRTC = 0;
		uint16_t REST = 0;
		uint16_t FRMCYC = 0;
		uint16_t CTA = 0;
		uint16_t XPSTTS = 0;
		uint16_t XPCTRL = 0;
		uint16_t VER = 0;
		uint16_t SPT[4] = { 0, 0, 0, 0 };
		uint16_t GPLT[4] = { 0, 0, 0, 0 };
		uint16_t JPLT[4] = { 0, 0, 0, 0 };
		uint16_t BKCOL = 0;
	};

	struct vboy_timer_t
	{
		uint16_t count = 0;
		uint16_t latch = 0;
	};

	required_device<cpu_device> m_maincpu;
	required_device<vboy_cart_slot_device> m_cart;
	required_device<timer_device> m_maintimer;
	required_device<palette_device> m_palette;

	std::unique_ptr<uint16_t[]> m_font;
	std::unique_ptr<uint16_t[]> m_bgmap;
	std::unique_ptr<uint8_t[]> m_l_frame_0;
	std::unique_ptr<uint8_t[]> m_l_frame_1;
	std::unique_ptr<uint8_t[]> m_r_frame_0;
	std::unique_ptr<uint8_t[]> m_r_frame_1;
	vboy_regs_t m_regs;
	vip_io_regs_t m_vip_io;
	vboy_timer_t m_timer;
	std::unique_ptr<int32_t[]> m_ovr_tempdraw_map;
	uint16_t m_frame_count = 0;
	uint8_t m_displayfb = 0;
	uint8_t m_drawfb = 0;
	uint8_t m_row_num = 0;
	attotime m_input_latch_time;

	void io_map(address_map &map) ATTR_COLD;
	u8 timer_control_r();
	void timer_control_w(offs_t offset, u8 data);
	u8 keypad_control_r();
	void keypad_control_w(offs_t offset, u8 data);

	void vip_map(address_map &map) ATTR_COLD;
	uint16_t vip_io_r(offs_t offset);
	void vip_io_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	void font0_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	void font1_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	void font2_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	void font3_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t font0_r(offs_t offset);
	uint16_t font1_r(offs_t offset);
	uint16_t font2_r(offs_t offset);
	uint16_t font3_r(offs_t offset);
	void bgmap_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	uint16_t bgmap_r(offs_t offset);
	uint8_t lfb0_r(offs_t offset);
	uint8_t lfb1_r(offs_t offset);
	uint8_t rfb0_r(offs_t offset);
	uint8_t rfb1_r(offs_t offset);
	void lfb0_w(offs_t offset, uint8_t data);
	void lfb1_w(offs_t offset, uint8_t data);
	void rfb0_w(offs_t offset, uint8_t data);
	void rfb1_w(offs_t offset, uint8_t data);

	void scanline_tick(int scanline, uint8_t screen_type);
	void set_irq(uint16_t irq_vector);

	void put_obj(bitmap_ind16 &bitmap, const rectangle &cliprect, int x, int y, uint16_t code, uint8_t pal);
	void fill_ovr_char(uint16_t code, uint8_t pal);
	int8_t get_bg_map_pixel(int num, int xpos, int ypos, u8 scx);
	void draw_bg_map(bitmap_ind16 &bitmap, const rectangle &cliprect, uint16_t param_base, int mode, int gx, int gp, int gy, int mx, int mp, int my,int h, int w,
											uint16_t x_mask, uint16_t y_mask, uint8_t ovr, bool right, int bg_map_num, u8 scx);
	void draw_affine_map(bitmap_ind16 &bitmap, const rectangle &cliprect, uint16_t param_base, int gx, int gp, int gy, int h, int w,
												uint16_t x_mask, uint16_t y_mask, uint8_t ovr, bool right, int bg_map_num, u8 scx);
	uint8_t display_world(int num, bitmap_ind16 &bitmap, const rectangle &cliprect, bool right, int &cur_spt);
	void set_brightness();
	void vboy_palette(palette_device &palette) const;
	uint32_t screen_update_left(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	uint32_t screen_update_right(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	TIMER_DEVICE_CALLBACK_MEMBER(timer_main_tick);
	TIMER_DEVICE_CALLBACK_MEMBER(timer_pad_tick);
	TIMER_DEVICE_CALLBACK_MEMBER(vboy_scanlineL);

	void vboy_map(address_map &map) ATTR_COLD;
};


void vboy_state::video_start()
{
	// Allocate memory for temporary screens
	m_ovr_tempdraw_map = make_unique_clear<int32_t[]>(0x40);

	// Allocate memory for framebuffers
	m_l_frame_0 = make_unique_clear<uint8_t[]>(0x6000);
	m_l_frame_1 = make_unique_clear<uint8_t[]>(0x6000);
	m_r_frame_0 = make_unique_clear<uint8_t[]>(0x6000);
	m_r_frame_1 = make_unique_clear<uint8_t[]>(0x6000);

	m_font  = make_unique_clear<uint16_t[]>((0x8000 >> 1)*4 * 2);
	m_bgmap = make_unique_clear<uint16_t[]>(0x20000 >> 1);
}


void vboy_state::fill_ovr_char(uint16_t code, uint8_t pal)
{
	for (uint8_t yi = 0; yi < 8; yi++)
	{
		uint16_t const data = READ_FONT(code * 8 + yi);

		for (uint8_t xi = 0; xi < 8; xi++)
		{
			uint8_t const dat = ((data >> (xi << 1)) & 0x03);
			int const col = (dat == 0) ? -1 : ((pal >> (dat * 2)) & 3);

			WRITE_OVR_TEMPDRAW_MAP(yi * 8 + xi, col);
		}
	}
}

inline int8_t vboy_state::get_bg_map_pixel(int num, int xpos, int ypos, u8 scx)
{
//  auto profile1 = g_profiler.start(PROFILER_USER1);

	int const y = ypos >> 3;
	int const x = xpos >> 3;

	// an individual tilemap is 64x64, the upper X/Y bits selects pages in 4096 units and joins with the global BGMAP_BASE.
	// hyperfgt backgrounds in particular wants to multiply Y page by SCX factor,
	// - it's 1 for E.Honda stage (the two 512x1024 tilemaps composing the hot bath)
	// - and 2 elsewhere (1024x1024).
	uint8_t const stepx = (x & 0x1c0) >> 6;
	uint8_t const stepy = ((y & 0x1c0) >> 6) * (scx + 1);
	uint16_t const val = READ_BGMAP((x & 0x3f) + (64 * (y & 0x3f)) + ((num + stepx + stepy) * 0x1000));
	int const pal = m_vip_io.GPLT[(val >> 14) & 3];
	int const code = val & 0x3fff;

	uint8_t const yi = ypos & 7;
	uint16_t const data = READ_FONT(code * 8 + yi);
	uint8_t const xi = xpos & 7;
	uint8_t const dat = ((data >> (xi << 1)) & 0x03);

	if(dat == 0)
		return -1;
	else
		return (pal >> (dat * 2)) & 3;
}

void vboy_state::draw_bg_map(bitmap_ind16 &bitmap, const rectangle &cliprect, uint16_t param_base, int mode, int gx, int gp, int gy, int mx, int mp, int my, int h, int w,
													uint16_t x_mask, uint16_t y_mask, uint8_t ovr, bool right, int bg_map_num, u8 scx)
{
//  auto profile2 = g_profiler.start(PROFILER_USER2);

	for(int y=0;y<=h;y++)
	{
		int32_t y1 = (y+gy);

		if ((y1 < cliprect.min_y) || (y1 > cliprect.max_y))
			continue;

		int src_y = y+my;

		for(int x=0;x<=w;x++)
		{
			int32_t x1 = (x+gx);

			x1 += right ? -gp : gp;

			if ((x1 < cliprect.min_x) || (x1 > cliprect.max_x))
				continue;

			int src_x;
			src_x = x+mx;
			if (mode==1)
				src_x += (int16_t)READ_BGMAP(param_base + (y*2+(right ^ 1)));

			src_x += right ? -mp : mp;


			int pix = 0;
			if(ovr)
			{
				if ((src_x > x_mask || src_y > y_mask || src_x < 0 || src_y < 0))
				{
					auto profile3 = g_profiler.start(PROFILER_USER3);
					pix = READ_OVR_TEMPDRAW_MAP((src_y & 7)*8+(src_x & 7));
				}
				else
				{
					pix = get_bg_map_pixel(bg_map_num, src_x & x_mask, src_y & y_mask, scx);
				}
			}
			else
			{
				pix = get_bg_map_pixel(bg_map_num, src_x & x_mask, src_y & y_mask, scx);
			}

			if(pix != -1)
				bitmap.pix(y1, x1) = m_palette->pen(pix & 3);
		}
	}
}

void vboy_state::draw_affine_map(bitmap_ind16 &bitmap, const rectangle &cliprect, uint16_t param_base, int gx, int gp, int gy, int h, int w,
														uint16_t x_mask, uint16_t y_mask, uint8_t ovr, bool right, int bg_map_num, u8 scx)
{
//  auto profile3 = g_profiler.start(PROFILER_USER3);

	for(int y = 0; y <= h; y++)
	{
		float h_skw = (int16_t)READ_BGMAP(param_base + (y * 8 + 0)) / 8.0;
		float prlx = (int16_t)READ_BGMAP(param_base + (y * 8 + 1)) / 8.0;
		float v_skw = (int16_t)READ_BGMAP(param_base + (y * 8 + 2)) / 8.0;
		float h_scl = (int16_t)READ_BGMAP(param_base + (y * 8 + 3)) / 512.0;
		float v_scl = (int16_t)READ_BGMAP(param_base + (y * 8 + 4)) / 512.0;

		h_skw += right ? -prlx : prlx;

		for(int x=0;x<=w;x++)
		{
			int32_t src_x,src_y;
			int16_t y1 = (y+gy);
			int16_t x1 = (x+gx);
			int pix = 0;

			x1 += (right ? -gp : gp);
			// clamp for spaceinv gameplay shots
			// (sets GPs with out of bounds GP values, cfr. $3da40/$3daa0 0xc*** world entries)
			x1 &= 0x1fff;

			src_x = (int32_t)((h_skw) + (h_scl * x));
			src_y = (int32_t)((v_skw) + (v_scl * x));

			if(ovr && (src_y > y_mask || src_x > x_mask || src_x < 0 || src_y < 0))
			{
				pix = READ_OVR_TEMPDRAW_MAP((src_y & 7)*8+(src_x & 7));
			}
			else
			{
				pix = get_bg_map_pixel(bg_map_num, src_x & x_mask, src_y & y_mask, scx);
			}

			if(pix != -1)
				if (cliprect.contains(x1, y1))
					bitmap.pix(y1, x1) = m_palette->pen(pix & 3);
		}
	}
}

void vboy_state::put_obj(bitmap_ind16 &bitmap, const rectangle &cliprect, int x, int y, uint16_t code, uint8_t pal)
{
	for (uint8_t yi = 0; yi < 8; yi++)
	{
		uint16_t const data = READ_FONT(code * 8 + yi);

		for (uint8_t xi = 0; xi < 8; xi++)
		{
			uint8_t const dat = ((data >> (xi << 1)) & 0x03);

			if (dat)
			{
				uint16_t const res_x = x + xi;
				uint16_t const res_y = y + yi;

				if (cliprect.contains(res_x, res_y))
				{
					uint8_t const col = (pal >> (dat * 2)) & 3;

					bitmap.pix((res_y), (res_x)) = m_palette->pen(col);
				}
			}
		}
	}
}

/*
 * $3d800 World list
 *
 * x--- ---- ---- ---- [0] LON enabled for left screen
 * -x-- ---- ---- ----     RON enabled for right screen
 * --xx ---- ---- ----     BGM type
 * --00 ---- ---- ----     Normal
 * --01 ---- ---- ----     Hi-Bias
 * --10 ---- ---- ----     Affine
 * --11 ---- ---- ----     OAM
 * ---- xx-- ---- ----     SCX number of pages in the X axis
 * ---- --xx ---- ----     SCY number of pages in the Y axis
 * ---- ---- x--- ----     OVR enable overdraw char
 * ---- ---- -x-- ----     END marker for end of list processing
 * ---- ---- --00 ----
 * ---- ---- ---- xxxx     BGMAP_BASE
*/

uint8_t vboy_state::display_world(int num, bitmap_ind16 &bitmap, const rectangle &cliprect, bool right, int &cur_spt)
{
	num <<= 4;
	const uint16_t def = READ_WORLD(num);
	const uint8_t lon = (def >> 15) & 1;
	const uint8_t ron = (def >> 14) & 1;
	const uint8_t mode = (def >> 12) & 3;
	const u8 raw_scx = ((def >> 10) & 3);
	const u8 raw_scy = ((def >> 8) & 3);
	const uint16_t scx = 64 << raw_scx;
	const uint16_t scy = 64 << raw_scy;
	const uint16_t scx_mask = scx * 8 - 1;
	const uint16_t scy_mask = scy * 8 - 1;
	const uint8_t ovr = (def >> 7) & 1;
	const uint8_t end = (def >> 6) & 1;
	const int16_t gx  = READ_WORLD(num+1);
	const int16_t gp  = READ_WORLD(num+2);
	const int16_t gy  = READ_WORLD(num+3);
	const int16_t mx  = READ_WORLD(num+4);
	const int16_t mp  = READ_WORLD(num+5);
	const int16_t my  = READ_WORLD(num+6);
	const uint16_t w  = READ_WORLD(num+7);
	const uint16_t h  = READ_WORLD(num+8);
	const uint16_t param_base = READ_WORLD(num+9) & 0xfff0;
	const uint16_t ovr_char = READ_BGMAP(READ_WORLD(num+10));
	const uint8_t bg_map_num = def & 0x0f;

	if(end)
		return 1;

	if (mode < 2) // Normal / HBias Mode
	{
		if(ovr)
			fill_ovr_char(ovr_char & 0x3fff, m_vip_io.GPLT[(ovr_char >> 14) & 3]);

		if (lon && (!right))
		{
			draw_bg_map(bitmap, cliprect, param_base, mode, gx, gp, gy, mx, mp, my, h,w, scx_mask, scy_mask, ovr, right, bg_map_num, raw_scx);
		}

		if (ron && (right))
		{
			draw_bg_map(bitmap, cliprect, param_base, mode, gx, gp, gy, mx, mp, my, h,w, scx_mask, scy_mask, ovr, right, bg_map_num, raw_scx);
		}
	}
	else if (mode==2) // Affine Mode
	{
		if(ovr)
			fill_ovr_char(ovr_char & 0x3fff, m_vip_io.GPLT[(ovr_char >> 14) & 3]);

		if (lon && (!right))
		{
			draw_affine_map(bitmap, cliprect, param_base, gx, gp, gy, h,w, scx_mask, scy_mask, ovr, right, bg_map_num, raw_scx);
		}

		if (ron && (right))
		{
			draw_affine_map(bitmap, cliprect, param_base, gx, gp, gy, h,w, scx_mask, scy_mask, ovr, right, bg_map_num, raw_scx);
		}
	}
	else if (mode==3) // OBJ Mode
	{
		if(cur_spt == -1)
		{
			popmessage("Cur spt used with -1 pointer!");
			return 0;
		}

		int start_offs = m_vip_io.SPT[cur_spt];

		int end_offs = 0x3ff;
		if(cur_spt != 0)
			end_offs = m_vip_io.SPT[cur_spt-1];

		int i = start_offs;
		do
		{
			uint16_t start_ndx = i * 4;
			int16_t jx = READ_OBJECTS(start_ndx+0);
			int16_t jp = READ_OBJECTS(start_ndx+1) & 0x3fff;
			int16_t jy = READ_OBJECTS(start_ndx+2) & 0x1ff;
			uint16_t val = READ_OBJECTS(start_ndx+3);
			uint8_t jlon = (READ_OBJECTS(start_ndx+1) & 0x8000) >> 15;
			uint8_t jron = (READ_OBJECTS(start_ndx+1) & 0x4000) >> 14;

			if (!right && jlon)
				put_obj(bitmap, cliprect, (jx-jp) & 0x1ff, jy, val & 0x3fff, m_vip_io.JPLT[(val>>14) & 3]);

			if(right && jron)
				put_obj(bitmap, cliprect, (jx+jp) & 0x1ff, jy, val & 0x3fff, m_vip_io.JPLT[(val>>14) & 3]);

			i--;
			i &= 0x3ff;
		}while(i != end_offs);

		if((lon && !right) || (ron && right))
			cur_spt--;
	}

	return 0;
}

uint32_t vboy_state::screen_update_left(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	bitmap.fill(m_palette->pen(m_vip_io.BKCOL), cliprect);
	int cur_spt;

	if(!(m_vip_io.DPCTRL & 2)) /* Don't bother if screen is off */
		return 0;

	cur_spt = 3;
	for(int i=31; i>=0; i--)
		if (display_world(i, bitmap, cliprect, false, cur_spt)) break;

	if(0)
	{
		for(int y=0;y<224;y++)
		{
			for(int x=0;x<384;x++)
			{
				uint8_t pen = m_l_frame_1[(x*0x40)+(y >> 2)];
				int yi = ((y & 0x3)*2);
				uint8_t pix = (pen >> yi) & 3;

				bitmap.pix(y, x) = m_palette->pen(pix & 3);
			}
		}
	}

	return 0;
}

uint32_t vboy_state::screen_update_right(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	bitmap.fill(m_palette->pen(m_vip_io.BKCOL), cliprect);
	int cur_spt;

	if(!(m_vip_io.DPCTRL & 2)) /* Don't bother if screen is off */
		return 0;

	cur_spt = 3;
	for(int i=31; i>=0; i--)
		if (display_world(i, bitmap, cliprect, true, cur_spt)) break;

	return 0;
}

/**********************************
 *
 * I/O
 *
 *********************************/

void vboy_state::io_map(address_map &map)
{
	// LPC (Link Port Control Reg)
//  map(0x00 / 4, 0x00 / 4)
	// LPC2 (Link Port Control Reg)
//  map(0x04 / 4, 0x04 / 4)
	// LPT (Link Port Transmit)
//  map(0x08 / 4, 0x08 / 4)
	// LPR (Link Port Receive) (read only)
//  map(0x0c / 4, 0x0c / 4)
	// KLB (Keypad Low Byte) (read only)
	map(0x10 / 4, 0x10 / 4).lr8(NAME([this]() { return m_regs.klb; }));
	// KHB (Keypad High Byte) (read only)
	map(0x14 / 4, 0x14 / 4).lr8(NAME([this]() { return m_regs.khb; }));
	 // TLB (Timer Low Byte)
	map(0x18 / 4, 0x18 / 4).lrw8(
		NAME([this]() { return m_regs.tlb; }),
		NAME([this](u8 data) {
			m_regs.tlb = data;
			m_timer.latch = m_regs.tlb | (m_timer.latch & 0xff00);
		})
	);
	// THB (Timer High Byte)
	map(0x1c / 4, 0x1c / 4).lrw8(
		NAME([this]() { return m_regs.thb; }),
		NAME([this](u8 data) {
			m_regs.thb = data;
			m_timer.latch = (m_regs.thb << 8) | (m_timer.latch & 0xff);
		})
	);
	// TCR (Timer Control Reg)
	map(0x20 / 4, 0x20 / 4).rw(FUNC(vboy_state::timer_control_r), FUNC(vboy_state::timer_control_w));
	// WCR (Wait State Control Reg)
	// according to docs: bits 2 to 7 are unused and set to 1.
	map(0x24 / 4, 0x24 / 4).lrw8(
		NAME([this]() { return m_regs.wcr | 0xfc; }),
		NAME([this](u8 data) { m_regs.wcr = data | 0xfc; })
	);
	// KCR (Keypad Control Reg)
	map(0x28 / 4, 0x28 / 4).rw(FUNC(vboy_state::keypad_control_r), FUNC(vboy_state::keypad_control_w));

}

/*
 * 111- ---- always 1
 * ---x ---- timer select (1=20 us, 0=100 us)
 * ---- x--- timer irq
 * ---- -x-- resets timer zero flag
 * ---- --x- timer is zero flag
 * ---- ---x enables timer
 */
u8 vboy_state::timer_control_r()
{
	return m_regs.tcr | 0xe4;
}

void vboy_state::timer_control_w(offs_t offset, u8 data)
{
	if (!(data & 0x08))
		m_maincpu->set_input_line(1, CLEAR_LINE);

	if (data & 1)
	{
		m_regs.tlb = m_timer.latch & 0xff;
		m_regs.thb = m_timer.latch >> 8;
		m_timer.count = m_timer.latch;

		// only start timer if tcr & 1 is 1 and wasn't before?
		if (!(m_regs.tcr & 1))
		{
			if (data & 0x10)
			{
				m_maintimer->adjust(attotime::from_hz(50000));
			}
			else
			{
				m_maintimer->adjust(attotime::from_hz(10000));
			}
		}
	}
	else
	{
		m_maintimer->adjust(attotime::never);
		// hyperfgt writes 0x18 -> 0x1c -> 0x19 in irq service,
		// implying that a 1 -> 0 transition will ack as well
		m_maincpu->set_input_line(1, CLEAR_LINE);
	}

	// according to docs: bits 5, 6 & 7 are unused and set to 1, bit 1 is read only.
	m_regs.tcr = (data & 0xfd) | (0xe4) | (m_regs.tcr & 2);
	if(data & 4)
		m_regs.tcr &= 0xfd;
}

u8 vboy_state::keypad_control_r()
{
	return m_regs.kcr | 0x4c;
}

void vboy_state::keypad_control_w(offs_t offset, u8 data)
{
	if (data & 0x04 )
	{
		m_regs.klb = (ioport("INPUT")->read() & 0x00ff);
		m_regs.khb = (ioport("INPUT")->read() & 0xff00) >> 8;
		//m_input_latch_time = machine().time();
	}

	if (data & 1)
	{
		m_regs.klb = 0;
		m_regs.khb = 0;
		//m_input_latch_time = attotime::zero;
	}

	// according to docs: bit 6 & bit 3 are unused and set to 1, bit 1 is read only.
	m_regs.kcr = (data | 0x48) & 0xfd;
}


/**********************************
 *
 * VIP
 *
 *********************************/

void vboy_state::vip_map(address_map &map)
{
	map(0x00000000, 0x00005fff).rw(FUNC(vboy_state::lfb0_r), FUNC(vboy_state::lfb0_w)); // L frame buffer 0
	map(0x00006000, 0x00007fff).rw(FUNC(vboy_state::font0_r), FUNC(vboy_state::font0_w)); // Font 0-511
	map(0x00008000, 0x0000dfff).rw(FUNC(vboy_state::lfb1_r), FUNC(vboy_state::lfb1_w)); // L frame buffer 1
	map(0x0000e000, 0x0000ffff).rw(FUNC(vboy_state::font1_r), FUNC(vboy_state::font1_w)); // Font 512-1023
	map(0x00010000, 0x00015fff).rw(FUNC(vboy_state::rfb0_r), FUNC(vboy_state::rfb0_w));  // R frame buffer 0
	map(0x00016000, 0x00017fff).rw(FUNC(vboy_state::font2_r), FUNC(vboy_state::font2_w)); // Font 1024-1535
	map(0x00018000, 0x0001dfff).rw(FUNC(vboy_state::rfb1_r), FUNC(vboy_state::rfb1_w));  // R frame buffer 1
	map(0x0001e000, 0x0001ffff).rw(FUNC(vboy_state::font3_r), FUNC(vboy_state::font3_w)); // Font 1536-2047

	map(0x00020000, 0x0003ffff).rw(FUNC(vboy_state::bgmap_r), FUNC(vboy_state::bgmap_w)); // VIPC memory

	//map(0x00040000, 0x0005ffff).ram(); // VIPC
	map(0x0005f800, 0x0005f87f).rw(FUNC(vboy_state::vip_io_r), FUNC(vboy_state::vip_io_w));

	map(0x00078000, 0x00079fff).rw(FUNC(vboy_state::font0_r), FUNC(vboy_state::font0_w)); // Font 0-511 mirror
	map(0x0007a000, 0x0007bfff).rw(FUNC(vboy_state::font1_r), FUNC(vboy_state::font1_w)); // Font 512-1023 mirror
	map(0x0007c000, 0x0007dfff).rw(FUNC(vboy_state::font2_r), FUNC(vboy_state::font2_w)); // Font 1024-1535 mirror
	map(0x0007e000, 0x0007ffff).rw(FUNC(vboy_state::font3_r), FUNC(vboy_state::font3_w)); // Font 1536-2047 mirror
}

// TODO: verify against real HW
// - LED brightness doesn't scale well with regular raster pen color.
//   These BRTx values are the "time" where the LED stays on.
// - REST needs to be taken into account (nothing sets it up so far)
// - vfishing draws selection accents in main menu with BRTA signal (currently almost invisible);
void vboy_state::set_brightness()
{
	int a,b,c;

	//d = (m_vip_io.BRTA + m_vip_io.BRTB + m_vip_io.BRTC + m_vip_io.REST);
	a = (0xff * (m_vip_io.BRTA)) / 0x80;
	b = (0xff * (m_vip_io.BRTA + m_vip_io.BRTB)) / 0x80;
	c = (0xff * (m_vip_io.BRTA + m_vip_io.BRTB + m_vip_io.BRTC)) / 0x80;

	if(a < 0) { a = 0; }
	if(b < 0) { b = 0; }
	if(c < 0) { c = 0; }
	if(a > 0xff) { a = 0xff; }
	if(b > 0xff) { b = 0xff; }
	if(c > 0xff) { c = 0xff; }

//  popmessage("%02x %02x %02x %02x",m_vip_io.BRTA,m_vip_io.BRTB,m_vip_io.BRTC,m_vip_io.REST);
	m_palette->set_pen_color(1, a,0,0);
	m_palette->set_pen_color(2, b,0,0);
	m_palette->set_pen_color(3, c,0,0);
}

uint16_t vboy_state::vip_io_r(offs_t offset)
{
	switch(offset << 1) {
		case 0x00:  //INTPND
					return m_vip_io.INTPND;
		case 0x02:  //INTENB
					return m_vip_io.INTENB;
		case 0x04:  //INTCLR
					logerror("Error reading INTCLR\n");
					break;
/*
        ---- -x-- ---- ---- LOCK (status column table address (CTA) lock)
        ---- --x- ---- ---- SYNCE (status of sync signal enable)
        ---- ---x ---- ---- RE (status of memory refresh cycle)
        ---- ---- x--- ---- FCLK
        ---- ---- -x-- ---- SCANRDY (active low)
        ---- ---- --xx xx-- DPBSY (current framebuffer displayed)
        ---- ---- --10 00-- RFB1
        ---- ---- --01 00-- LFB1
        ---- ---- --00 10-- RFB0
        ---- ---- --00 01-- LFB0
        ---- ---- ---- --x- DISP
*/
		case 0x20:  //DPSTTS
		{
			uint16_t res;

			res = m_vip_io.DPCTRL & 0x0702;

			if(m_vip_io.DPCTRL & 2)
			{
				if(m_row_num < 224/8)
				{
					if(m_displayfb == 0)
						res |= 0x0c;
					else
						res |= 0x30;
				}
			}

			res |= 0x40;
			//printf("%04x\n",res);
			return res;
		}
		case 0x22:  //DPCTRL
					return m_vip_io.DPCTRL;
		case 0x24:  //BRTA
					return m_vip_io.BRTA;
		case 0x26:  //BRTB
					return m_vip_io.BRTB;
		case 0x28:  //BRTC
					return m_vip_io.BRTC;
		case 0x2A:  //REST
					return m_vip_io.REST;
		case 0x2E:  //FRMCYC
					return m_vip_io.FRMCYC;
		case 0x30:  //CTA
					printf("Read CTA\n");
					return m_vip_io.CTA;
		case 0x40:  //XPSTTS, piXel Processor STaTuS
		{
			/*
			x--- ---- ---- ---- SBOUT
			---x xxxx ---- ---- SBCOUNT
			---- ---- ---x ---- OVERTIME (process overflow)
			---- ---- ---- x--- XPBSY1 (second framebuffer busy flag)
			---- ---- ---- -x-- XPBSY0 (first framebfuffer busy flag)
			---- ---- ---- --x- XPEN (starts drawing at beginning of game frame)
			---- ---- ---- ---x XPRST (force drawing process to idle)
			*/
			uint16_t res;

			//printf("%d\n",row_num);

			res =  m_vip_io.XPSTTS & 0x00f3;
			res |= m_drawfb << 2;

			if(m_row_num < 224/8)
			{
				res |= 0x8000;
				res |= m_row_num << 8;
			}

			return res;
		}
		case 0x42:  //XPCTRL
					return m_vip_io.XPCTRL;
		case 0x44:  //VER
					printf("%08x read VER\n",m_maincpu->pc());
					return m_vip_io.VER;
		case 0x48:  //SPT0
					return m_vip_io.SPT[0];
		case 0x4A:  //SPT1
					return m_vip_io.SPT[1];
		case 0x4C:  //SPT2
					return m_vip_io.SPT[2];
		case 0x4E:  //SPT3
					return m_vip_io.SPT[3];
		case 0x60:  //GPLT0
					return m_vip_io.GPLT[0];
		case 0x62:  //GPLT1
					return m_vip_io.GPLT[1];
		case 0x64:  //GPLT2
					return m_vip_io.GPLT[2];
		case 0x66:  //GPLT3
					return m_vip_io.GPLT[3];
		case 0x68:  //JPLT0
					return m_vip_io.JPLT[0];
		case 0x6A:  //JPLT1
					return m_vip_io.JPLT[1];
		case 0x6C:  //JPLT2
					return m_vip_io.JPLT[2];
		case 0x6E:  //JPLT3
					return m_vip_io.JPLT[3];
		case 0x70:  //BKCOL
					return m_vip_io.BKCOL;
		default:
					logerror("Unemulated read: addr %08x\n", offset * 2 + 0x0005f800);
					break;
	}
	return 0xffff;
}

void vboy_state::vip_io_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	// wariolnd end boss has these writes
	if(mem_mask != 0xffff)
		logerror("Warning: register %04x write with non-word access %02x & %04x\n",offset*2, data, mem_mask);

	switch(offset << 1) {
		/*
		    x--- ---- ---- ---- TIME_ERR
		    -x-- ---- ---- ---- XP_END
		    --x- ---- ---- ---- SB_HIT
		    ---- ---- ---x ---- FRAME_START
		    ---- ---- ---- x--- GAME_START
		    ---- ---- ---- -x-- RFB_END
		    ---- ---- ---- --x- LFB_END
		    ---- ---- ---- ---x SCAN_ERR
		*/
		case 0x00:  //INTPND
					logerror("Error writing INTPND\n");
					break;
		case 0x02:  //INTENB
					m_vip_io.INTENB = data;
					set_irq(0);
					//printf("%04x ENB\n",data);
					break;
		case 0x04:  //INTCLR
					m_vip_io.INTPND &= ~data;
					set_irq(0);
					//else
					//  printf("%04x\n",m_vip_io.INTPND);
					break;
		case 0x20:  //DPSTTS
					logerror("Error writing DPSTTS\n");
					break;
/*
        ---- -x-- ---- ---- LOCK (status column table address (CTA) lock)
        ---- --x- ---- ---- SYNCE (status of sync signal enable)
        ---- ---x ---- ---- RE (status of memory refresh cycle)
        ---- ---- ---- --x- DISP
        ---- ---- ---- ---x DPRST (Resets the VIP internal counter)
*/
		case 0x22:  //DPCTRL
					m_vip_io.DPCTRL = data & 0x0702;

					if(data & 1)
					{
						m_vip_io.INTPND &= 0xe000; // reset FRAME_START, GAME_START, RFB_END, LFB_END and SCAN_ERR irqs
						set_irq(0);
					}
					break;
		case 0x24:  //BRTA
					m_vip_io.BRTA = data;
					set_brightness();
					break;
		case 0x26:  //BRTB
					m_vip_io.BRTB = data;
					set_brightness();
					break;
		case 0x28:  //BRTC
					m_vip_io.BRTC = data;
					set_brightness();
					break;
		case 0x2A:  //REST
					m_vip_io.REST = data;
					set_brightness();
					if(data)
						printf("%04x REST\n",data);
					break;
		case 0x2E:  //FRMCYC
					//printf("%d\n",data);
					m_vip_io.FRMCYC = data;
					break;
		case 0x30:  //CTA
					m_vip_io.CTA = data;
					printf("%04x CTA\n",data);
					break;
		case 0x40:  //XPSTTS
					logerror("Error writing XPSTTS\n");
					break;
		case 0x42:  //XPCTRL, w/o
					/*
					---- ---- ---- --x-
					---- ---- ---- ---x Reset Pixel Processor
					*/
					m_vip_io.XPCTRL = data & 0x1f02;

					//if(data & 0x1f00)
					//  printf("%04x SBCMP\n",data);

					if(data & 1)
					{
						m_vip_io.INTPND &= 0x1fff; // reset SB_HIT, XP_END and TIME_ERR irqs
						set_irq(0);
					}
					break;
		case 0x44:  //VER
					//m_vip_io.VER = data;
					break;
		case 0x48:  //SPT0
					m_vip_io.SPT[0] = data & 0x3ff;
					break;
		case 0x4A:  //SPT1
					m_vip_io.SPT[1] = data & 0x3ff;
					break;
		case 0x4C:  //SPT2
					m_vip_io.SPT[2] = data & 0x3ff;
					break;
		case 0x4E:  //SPT3
					m_vip_io.SPT[3] = data & 0x3ff;
					break;
		case 0x60:  //GPLT0
					m_vip_io.GPLT[0] = data;
					break;
		case 0x62:  //GPLT1
					m_vip_io.GPLT[1] = data;
					break;
		case 0x64:  //GPLT2
					m_vip_io.GPLT[2] = data;
					break;
		case 0x66:  //GPLT3
					m_vip_io.GPLT[3] = data;
					break;
		case 0x68:  //JPLT0
					m_vip_io.JPLT[0] = data & 0xfc;
					break;
		case 0x6A:  //JPLT1
					m_vip_io.JPLT[1] = data & 0xfc;
					break;
		case 0x6C:  //JPLT2
					m_vip_io.JPLT[2] = data & 0xfc;
					break;
		case 0x6E:  //JPLT3
					m_vip_io.JPLT[3] = data & 0xfc;
					break;
		case 0x70:  //BKCOL
					m_vip_io.BKCOL = data & 3;
					break;
		default:
					logerror("Unemulated write: addr %08x, data %04x\n", offset * 2 + 0x0005f800, data);
					break;
	}
}



void vboy_state::font0_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	WRITE_FONT(offset);
}

void vboy_state::font1_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	WRITE_FONT(offset+0x1000);
}

void vboy_state::font2_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	WRITE_FONT(offset+0x2000);
}

void vboy_state::font3_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	WRITE_FONT(offset+0x3000);
}

uint16_t vboy_state::font0_r(offs_t offset)
{
	return READ_FONT(offset);
}

uint16_t vboy_state::font1_r(offs_t offset)
{
	return READ_FONT(offset + 0x1000);
}

uint16_t vboy_state::font2_r(offs_t offset)
{
	return READ_FONT(offset + 0x2000);
}

uint16_t vboy_state::font3_r(offs_t offset)
{
	return READ_FONT(offset + 0x3000);
}

void vboy_state::bgmap_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	m_bgmap[offset] = data | (m_bgmap[offset] & (mem_mask ^ 0xffff));
}

uint16_t vboy_state::bgmap_r(offs_t offset)
{
	return m_bgmap[offset];
}

uint8_t vboy_state::lfb0_r(offs_t offset) { return m_l_frame_0[offset]; }
uint8_t vboy_state::lfb1_r(offs_t offset) { return m_l_frame_1[offset]; }
uint8_t vboy_state::rfb0_r(offs_t offset) { return m_r_frame_0[offset]; }
uint8_t vboy_state::rfb1_r(offs_t offset) { return m_r_frame_1[offset]; }
void vboy_state::lfb0_w(offs_t offset, uint8_t data) { m_l_frame_0[offset] = data; }
void vboy_state::lfb1_w(offs_t offset, uint8_t data) { m_l_frame_1[offset] = data; }
void vboy_state::rfb0_w(offs_t offset, uint8_t data) { m_r_frame_0[offset] = data; }
void vboy_state::rfb1_w(offs_t offset, uint8_t data) { m_r_frame_1[offset] = data; }


void vboy_state::vboy_map(address_map &map)
{
	map.global_mask(0x07ffffff);
	map(0x00000000, 0x0007ffff).m(FUNC(vboy_state::vip_map));
	map(0x01000000, 0x010005ff).rw("vbsnd", FUNC(vboysnd_device::read), FUNC(vboysnd_device::write));
	map(0x02000000, 0x020000ff).mirror(0x0ffff00).m(FUNC(vboy_state::io_map)).umask32(0x000000ff);
	//map(0x04000000, 0x04ffffff) cartslot EXP
	map(0x05000000, 0x0500ffff).mirror(0x0ff0000).ram().share("wram");// Main RAM - 64K mask 0xffff
	//map(0x06000000, 0x06ffffff) cartslot CHIP
	//map(0x07000000, 0x07ffffff) cartslot ROM
}

/* Input ports */
static INPUT_PORTS_START( vboy )
	PORT_START("INPUT")
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1)
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1)
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("L") PORT_PLAYER(1) // Left button on back
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("R") PORT_PLAYER(1) // Right button on back
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("B") PORT_PLAYER(1) // B button (Mario Clash Jump button)
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("A") PORT_PLAYER(1) // A button
	PORT_BIT( 0x0002, IP_ACTIVE_LOW,  IPT_UNUSED ) // Always 1
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_UNUSED ) // Battery low
INPUT_PORTS_END


void vboy_state::machine_reset()
{
	/* Initial values taken from Reality Boy, to be verified when emulation improves */
	m_regs.lpc = 0x6d;
	m_regs.lpc2 = 0xff;
	m_regs.lpt = 0x00;
	m_regs.lpr = 0x00;
	m_regs.klb = 0x00;
	m_regs.khb = 0x00;
	m_regs.tlb = 0xff;
	m_regs.thb = 0xff;
	m_regs.tcr = 0xe4;
	m_regs.wcr = 0xfc;
	m_regs.kcr = 0x4c | 0x80;
	m_vip_io.DPCTRL = 2; // ssquash relies on this at boot otherwise no frame_start irq is fired
	m_displayfb = 0;
	m_drawfb = 0;

	m_timer.count = 0;
	m_maintimer->adjust(attotime::never);
}


TIMER_DEVICE_CALLBACK_MEMBER(vboy_state::timer_main_tick)
{
	if(m_timer.count > 0)
	{
		m_timer.count--;
		m_regs.tlb = m_timer.count & 0xff;
		m_regs.thb = m_timer.count >> 8;
	}

	if (m_timer.count == 0)
	{
		m_timer.count = m_timer.latch;
		m_regs.tcr |= 0x02;
		if(m_regs.tcr & 8)
		{
			m_maincpu->set_input_line(1, ASSERT_LINE);
		}
	}

	if (m_regs.tcr & 0x10)
	{
		m_maintimer->adjust(attotime::from_hz(50000));
	}
	else
	{
		m_maintimer->adjust(attotime::from_hz(10000));
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(vboy_state::timer_pad_tick)
{
	if((m_regs.kcr & 0x80) == 0)
		m_maincpu->set_input_line(0, HOLD_LINE);
}

void vboy_state::vboy_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t::black());
	palette.set_pen_color(1, rgb_t::black());
	palette.set_pen_color(2, rgb_t::black());
	palette.set_pen_color(3, rgb_t::black());
}

void vboy_state::set_irq(uint16_t irq_vector)
{
	m_vip_io.INTPND |= irq_vector;

	if(m_vip_io.INTENB & m_vip_io.INTPND)
		m_maincpu->set_input_line(4, ASSERT_LINE);

	if((m_vip_io.INTENB & m_vip_io.INTPND) == 0)
		m_maincpu->set_input_line(4, CLEAR_LINE);
}

/* TODO: obviously all of this needs clean-ups and better implementation ... */
void vboy_state::scanline_tick(int scanline, uint8_t screen_type)
{
	if(screen_type == 0)
		m_row_num = (scanline / 8) & 0x1f;

	if(scanline == 0)
	{
		if(m_vip_io.DPCTRL & 2)
			set_irq(0x0010); // FRAME_START

		m_frame_count++;

		if(m_frame_count > m_vip_io.FRMCYC)
		{
			set_irq(0x0008); // GAME_START
			m_frame_count = 0;
		}

		if(m_vip_io.DPCTRL & 2)
			m_displayfb ^= 1;
	}

	if(scanline == 224)
	{
		if(m_displayfb)
			m_drawfb = 1;
		else
			m_drawfb = 2;
		set_irq(0x4000); // XPEND
	}

	if(scanline == 232)
	{
		m_drawfb = 0;
		set_irq(0x0002); // LFBEND
	}

	if(scanline == 240)
	{
		set_irq(0x0004); // RFBEND
	}

	if(m_row_num == ((m_vip_io.XPCTRL & 0x1f00) >> 8))
	{
		set_irq(0x2000); // SBHIT
	}

}

TIMER_DEVICE_CALLBACK_MEMBER(vboy_state::vboy_scanlineL)
{
	int scanline = param;

	scanline_tick(scanline,0);
}

#if 0
TIMER_DEVICE_CALLBACK_MEMBER(vboy_state::vboy_scanlineR)
{
	int scanline = param;

	//scanline_tick(scanline,1);
}
#endif


void vboy_state::vboy(machine_config &config)
{
	/* basic machine hardware */
	V810(config, m_maincpu, XTAL(20'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &vboy_state::vboy_map);
	// no AS_IO, and some games relies on r/w the program map with INH/OUTH
	// cfr. vforce, nesterfb, panicbom (sound)

	TIMER(config, "scantimer_l").configure_scanline(FUNC(vboy_state::vboy_scanlineL), "3dleft", 0, 1);
	//TIMER(config, "scantimer_r").configure_scanline(FUNC(vboy_state::vboy_scanlineR), "3dright", 0, 1);

	// programmable timer
	TIMER(config, m_maintimer).configure_generic(FUNC(vboy_state::timer_main_tick));

	// pad ready, which should be once per VBL
	TIMER(config, "timer_pad").configure_periodic(FUNC(vboy_state::timer_pad_tick), attotime::from_hz(50.038029f));

	/* video hardware */
	config.set_default_layout(layout_vboy);
	PALETTE(config, m_palette, FUNC(vboy_state::vboy_palette), 4);

	/* Left screen */
	screen_device &lscreen(SCREEN(config, "3dleft", SCREEN_TYPE_LCD));
	lscreen.set_raw(XTAL(20'000'000)/2,757,0,384,264,0,224);
	lscreen.set_screen_update(FUNC(vboy_state::screen_update_left));
	lscreen.set_palette(m_palette);

	/* Right screen */
	screen_device &rscreen(SCREEN(config, "3dright", SCREEN_TYPE_LCD));
	rscreen.set_raw(XTAL(20'000'000)/2,757,0,384,264,0,224);
	rscreen.set_screen_update(FUNC(vboy_state::screen_update_right));
	rscreen.set_palette(m_palette);

	/* cartridge */
	VBOY_CART_SLOT(config, m_cart, vboy_carts, nullptr);
	m_cart->intcro().set_inputline(m_maincpu, 2);
	m_cart->set_exp(m_maincpu, AS_PROGRAM, 0x0400'0000);
	m_cart->set_chip(m_maincpu, AS_PROGRAM, 0x0600'0000);
	m_cart->set_rom(m_maincpu, AS_PROGRAM, 0x0700'0000);

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("vboy");

	/* sound hardware */
	SPEAKER(config, "speaker", 2).front();
	vboysnd_device &vbsnd(VBOYSND(config, "vbsnd"));
	vbsnd.add_route(0, "speaker", 1.0, 0);
	vbsnd.add_route(1, "speaker", 1.0, 1);
}

/* ROM definition */
ROM_START( vboy )
	ROM_REGION( 0x2000000, "maincpu", ROMREGION_ERASEFF )
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY     FULLNAME       FLAGS */
CONS( 1995, vboy, 0,      0,      vboy,    vboy,  vboy_state, empty_init, "Nintendo", "Virtual Boy", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)



vc4000.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Peter Trauner, Robbbert
// thanks-to:Manfred Schneider
/******************************************************************************
 Peter Trauner May 2001

 Paul Robson's Emulator at www.classicgaming.com/studio2 made it possible
******************************************************************************/

/*****************************************************************************
Additional Notes by Manfred Schneider
Memory Map
Memory mapping is done in two steps. The PVI(2636) provides the chip select signals
according to signals provided by the CPU address lines.
The PVI has 12 address line (A0-A11) which give him control over 4K. A11 of the PVI is not
connected to A11 of the CPU, but connected to the cartridge slot. On the cartridge it is
connected to A12 of the CPU which extends the addressable Range to 8K. This is also the
maximum usable space, because A13 and A14 of the CPU are not used.
With the above in mind address range will look like this:
$0000 - $15FF  ROM, RAM
$1600 - $167F  unused
$1680 - $16FF  used for I/O Control on main PCB
$1700 - $17FF PVI internal Registers and RAM
$1800 - $1DFF ROM, RAM
$1E00 - $1E7F unused
$1E80 - $1EFF mirror of $1680 - $167F
$1F00 - $1FFF mirror of $1700 - $17FF
$2000 - $3FFF mirror of $0000 - $1FFF
$4000 - $5FFF mirror of $0000 - $1FFF
$6000 - $7FFF mirror of $0000 - $1FFF
On all cartridges for the Interton A11 from PVI is connected to A12 of the CPU.
There are four different types of Cartridges with the following memory mapping.
Type 1: 2K Rom or EPROM mapped from $0000 - $07FF
Type 2: 4K Rom or EPROM mapped from $0000 - $0FFF
Type 3: 4K Rom + 1K Ram
    Rom is mapped from $0000 - $0FFF
    Ram is mapped from $1000 - $13FF and mirrored from $1800 - $1BFF
Type 4: 6K Rom + 1K Ram
    Rom is mapped from $0000 - $15FF (only 5,5K ROM visible to the CPU)
    Ram is mapped from $1800 - $1BFF

One other type is known for Radofin (rom compatible to VC4000, but not the Cartridge connector).
It consisted of a 2K ROM and 2K RAM which are most likely mapped as follows (needs to be confirmed):
2K Rom mapped from $0000 - $07FF
2K Ram mapped from $0800 - $0FFF
The Cartridge is called Hobby Module and the Rom is probably the same as used in
elektor TV Game Computer which is a kind of developer machine for the VC4000.

******************************************************************************

Elektor TV Games Computer
This is much the same as the vc4000, however it has its own ROM (with inbuilt
monitor program similar to the Signetics Instructor 50), and 2K of ram. No cart
slot, no joystick, but has a cassette interface.

ToDo:
- Most quickloads don't work too well
- Might need to rework keyboard, again

When booted you get the familiar 00 00 pattern. Pressing Q gives a display of
IIII. Now, you enter a command.

Key   Command    Purpose
------------------------
Q     Start      Boot up system
L     RCAS       Load a tape
S     WCAS       Save a tape
W     BP1/2      Set a breakpoint
R     REG        View/Set registers
X     PC         Go
+pad  +          Enter data and do next thing
-pad  -          Decrement
-     MEM        Specify an address
0-9   0-9        Hex digits
A-F   A-F        Hex digits

Keyboard layout when using the Monitor on real hardware (n/a = key not assigned)

n/a    RCAS  WCAS  C    D  E  F
Start  BP1/2 REG   8    9  A  B
n/a    PC    MEM   4    5  6  7
Reset  -     +     0    1  2  3

This wouldn't fit too well on our keyboard with any chance of remembering
it, so I've hooked it much the same as the Instructor.

The Select key (Z) and the joystick don't actually exist, but I've left them
in the keyboard matrix for now.


Quickloads
----------
You can load pgm and tvc files with the quickload facility. The quickloads
are meant for the ElektorTVGC, however with a bit a trickery they can be made
to work on the vc4000 as well. Procedure:

- Get a copy of the Elektor bios and rename it to ELEKTOR.BIN then save it
  with the rest of your vc4000 carts.

- Start vc4000, and load ELEKTOR.BIN into the cartslot. Now your vc4000
  thinks it is an Elektor.

- Load a quickload file. Some of them will work, and in some cases, better
  than on the Elektor system.


Pasting
-------
This system uses the standard trainer paste codes:
        0-F : as is
        +   : ^
        -   : V
        MEM : -
        MON : Q

Here's a sample from the manual, page 34/35 (down-arrow to escape)
Q-0900^762005CA06CA0D4A00CD7F00FA780C1E88441099791F0000040005CA06CACD4A00FA7B
04FFCC0AC8CC0AC90409CC0AC60402CC0AC01F0900
-0A00^F15155757FFFFFC3A52480FF4FFF-0AC0^C018P0900^

Another sample, from page 94 (Q to escape)
Q-0900^76203F0161063005080E492DCD4890597877103F020E75105A0A0C1E89F4101879
1F003877103F02CF75101B5A
17A2A2A2A2A2A217
17171000000D1717
0A171100BC17000F
17170D000E051717
14150A0CBC120C0E
0A171112BCBC110EP0900^


******************************************************************************/

#include "emu.h"
#include "vc4000.h"
#include "softlist_dev.h"
#include "speaker.h"


uint8_t vc4000_state::vc4000_key_r(offs_t offset)
{
	uint8_t data=0;
	switch(offset & 0x0f)
	{
	case 0x08:
		data = m_keypad1_1->read();
		break;
	case 0x09:
		data = m_keypad1_2->read();
		break;
	case 0x0a:
		data = m_keypad1_3->read();
		break;
	case 0x0b:
		data = m_panel->read();
		break;
	case 0x0c:
		data = m_keypad2_1->read();
		break;
	case 0x0d:
		data = m_keypad2_2->read();
		break;
	case 0x0e:
		data = m_keypad2_3->read();
		break;
	}
	return data;
}

void vc4000_state::vc4000_sound_ctl(offs_t offset, uint8_t data)
{
	logerror("Write to sound control register offset= %d value= %d\n", offset, data);
}

// Write cassette - Address 0x1DFF
void vc4000_state::elektor_cass_w(uint8_t data)
{
	m_cassette->output(BIT(data, 7) ? -1.0 : +1.0);
}

// Read cassette - Address 0x1DBF
uint8_t vc4000_state::elektor_cass_r()
{
	return (m_cassette->input() > 0.03) ? 0xff : 0x7f;
}

void vc4000_state::vc4000_mem(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x1fff);
	map(0x0000, 0x07ff).rom();
	map(0x1680, 0x16ff).rw(FUNC(vc4000_state::vc4000_key_r), FUNC(vc4000_state::vc4000_sound_ctl)).mirror(0x0800);
	map(0x1700, 0x17ff).rw(FUNC(vc4000_state::vc4000_video_r), FUNC(vc4000_state::vc4000_video_w)).mirror(0x0800);
}

void vc4000_state::elektor_mem(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x1fff);
	map(0x0000, 0x07ff).rom();
	map(0x0800, 0x15ff).ram();
	map(0x1980, 0x19ff).mirror(0x400).rw(FUNC(vc4000_state::elektor_cass_r), FUNC(vc4000_state::elektor_cass_w));
	map(0x1680, 0x168f).mirror(0x800).rw(FUNC(vc4000_state::vc4000_key_r), FUNC(vc4000_state::vc4000_sound_ctl));
	map(0x1700, 0x17ff).mirror(0x800).rw(FUNC(vc4000_state::vc4000_video_r), FUNC(vc4000_state::vc4000_video_w));
}

static INPUT_PORTS_START( vc4000 )
	PORT_START("PANEL")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_START ) PORT_NAME("Start")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_NAME("Game Select")

	PORT_START("KEYPAD1_1")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad Enter") PORT_CODE(KEYCODE_PLUS_PAD)

	PORT_START("KEYPAD1_2")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 2/Button") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 0") PORT_CODE(KEYCODE_0_PAD)

	PORT_START("KEYPAD1_3")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad 9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P1 Keypad Clear") PORT_CODE(KEYCODE_MINUS_PAD)

	PORT_START("KEYPAD2_1")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 1") PORT_CODE(KEYCODE_Z)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 4") PORT_CODE(KEYCODE_A)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 7") PORT_CODE(KEYCODE_Q)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad Enter") PORT_CODE(KEYCODE_V)

	PORT_START("KEYPAD2_2")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 2/Button") PORT_CODE(KEYCODE_X)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 5") PORT_CODE(KEYCODE_S)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 8") PORT_CODE(KEYCODE_W)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 0") PORT_CODE(KEYCODE_F)

	PORT_START("KEYPAD2_3")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 3") PORT_CODE(KEYCODE_C)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 6") PORT_CODE(KEYCODE_D)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad 9") PORT_CODE(KEYCODE_E)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("P2 Keypad Clear") PORT_CODE(KEYCODE_R)
#ifndef ANALOG_HACK
	// auto centering too slow, so only using 5 bits, and scaling at videoside
	PORT_START("JOY1_X")
PORT_BIT(0xff,0x70,IPT_AD_STICK_X) PORT_SENSITIVITY(70) PORT_KEYDELTA(5) PORT_CENTERDELTA(0) PORT_MINMAX(20,225) PORT_CODE_DEC(KEYCODE_LEFT) PORT_CODE_INC(KEYCODE_RIGHT) PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH) PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH) PORT_PLAYER(1)
	PORT_START("JOY1_Y")
PORT_BIT(0xff,0x70,IPT_AD_STICK_Y) PORT_SENSITIVITY(70) PORT_KEYDELTA(5) PORT_CENTERDELTA(0) PORT_MINMAX(20,225) PORT_CODE_DEC(KEYCODE_UP) PORT_CODE_INC(KEYCODE_DOWN) PORT_CODE_DEC(JOYCODE_Y_UP_SWITCH) PORT_CODE_INC(JOYCODE_Y_DOWN_SWITCH) PORT_PLAYER(1)
	PORT_START("JOY2_X")
PORT_BIT(0xff,0x70,IPT_AD_STICK_X) PORT_SENSITIVITY(70) PORT_KEYDELTA(5) PORT_CENTERDELTA(0) PORT_MINMAX(20,225) PORT_CODE_DEC(KEYCODE_DEL) PORT_CODE_INC(KEYCODE_PGDN) PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH) PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH) PORT_PLAYER(2)
	PORT_START("JOY2_Y")
PORT_BIT(0xff,0x70,IPT_AD_STICK_Y) PORT_SENSITIVITY(70) PORT_KEYDELTA(5) PORT_CENTERDELTA(0) PORT_MINMAX(20,225) PORT_CODE_DEC(KEYCODE_HOME) PORT_CODE_INC(KEYCODE_END) PORT_CODE_DEC(JOYCODE_Y_UP_SWITCH) PORT_CODE_INC(JOYCODE_Y_DOWN_SWITCH) PORT_PLAYER(2)
#else
	PORT_START("JOYS")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CODE(KEYCODE_DEL) PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CODE(KEYCODE_PGDN) PORT_PLAYER(2)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CODE(KEYCODE_END) PORT_PLAYER(2)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CODE(KEYCODE_HOME) PORT_PLAYER(2)

	PORT_START("CONFIG")
	PORT_CONFNAME( 0x01, 0x00, "Treat Joystick as...")
	PORT_CONFSETTING(    0x00, "Buttons")
	PORT_CONFSETTING(    0x01, "Paddle")
#endif
INPUT_PORTS_END

INPUT_PORTS_START( elektor )
	PORT_START("PANEL")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MON") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Game Select") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')

	PORT_START("KEYPAD1_1")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RCAS") PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("BP1/2") PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PC") PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("-") PORT_CODE(KEYCODE_DOWN) PORT_CHAR('V')

	PORT_START("KEYPAD1_2")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("WCAS") PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("REG") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MEM") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("+") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')

	PORT_START("KEYPAD1_3")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')

	PORT_START("KEYPAD2_1")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')

	PORT_START("KEYPAD2_2")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')

	PORT_START("KEYPAD2_3")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
#ifndef ANALOG_HACK
	// auto centering too slow, so only using 5 bits, and scaling at videoside
	PORT_START("JOY1_X")
PORT_BIT(0xff,0x70,IPT_AD_STICK_X) PORT_SENSITIVITY(70) PORT_KEYDELTA(5) PORT_CENTERDELTA(0) PORT_MINMAX(20,225) PORT_CODE_DEC(KEYCODE_LEFT) PORT_CODE_INC(KEYCODE_RIGHT) PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH) PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH) PORT_PLAYER(1)
	PORT_START("JOY1_Y")
PORT_BIT(0xff,0x70,IPT_AD_STICK_Y) PORT_SENSITIVITY(70) PORT_KEYDELTA(5) PORT_CENTERDELTA(0) PORT_MINMAX(20,225) PORT_CODE_DEC(KEYCODE_UP) PORT_CODE_INC(KEYCODE_DOWN) PORT_CODE_DEC(JOYCODE_Y_UP_SWITCH) PORT_CODE_INC(JOYCODE_Y_DOWN_SWITCH) PORT_PLAYER(1)
	PORT_START("JOY2_X")
PORT_BIT(0xff,0x70,IPT_AD_STICK_X) PORT_SENSITIVITY(70) PORT_KEYDELTA(5) PORT_CENTERDELTA(0) PORT_MINMAX(20,225) PORT_CODE_DEC(KEYCODE_DEL) PORT_CODE_INC(KEYCODE_PGDN) PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH) PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH) PORT_PLAYER(2)
	PORT_START("JOY2_Y")
PORT_BIT(0xff,0x70,IPT_AD_STICK_Y) PORT_SENSITIVITY(70) PORT_KEYDELTA(5) PORT_CENTERDELTA(0) PORT_MINMAX(20,225) PORT_CODE_DEC(KEYCODE_HOME) PORT_CODE_INC(KEYCODE_END) PORT_CODE_DEC(JOYCODE_Y_UP_SWITCH) PORT_CODE_INC(JOYCODE_Y_DOWN_SWITCH) PORT_PLAYER(2)
#else
	PORT_START("JOYS")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CODE(KEYCODE_DEL) PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CODE(KEYCODE_PGDN) PORT_PLAYER(2)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CODE(KEYCODE_END) PORT_PLAYER(2)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CODE(KEYCODE_HOME) PORT_PLAYER(2)

	PORT_START("CONFIG")
	PORT_CONFNAME( 0x01, 0x00, "Treat Joystick as...")
	PORT_CONFSETTING(    0x00, "Buttons")
	PORT_CONFSETTING(    0x01, "Paddle")
#endif
INPUT_PORTS_END

static constexpr rgb_t vc4000_pens[] =
{
	// background colors
	rgb_t(0, 0, 0), // black
	rgb_t(0, 0, 175), // blue
	rgb_t(0, 175, 0), // green
	rgb_t(0, 255, 255), // cyan
	rgb_t(255, 0, 0), // red
	rgb_t(255, 0, 255), // magenta
	rgb_t(200, 200, 0), // yellow
	rgb_t(200, 200, 200), // white
	/* sprite colors
	The control line simply inverts the RGB lines all at once.
	We can do that in the code with ^7 */
};

void vc4000_state::vc4000_palette(palette_device &palette) const
{
	palette.set_pen_colors(0, vc4000_pens);
}


void vc4000_state::machine_start()
{
	if (m_cart->exists())
	{
		// extra handler
		switch (m_cart->get_type())
		{
		case VC4000_STD:
			m_maincpu->space(AS_PROGRAM).install_read_handler(0x0000, 0x07ff, read8sm_delegate(*m_cart, FUNC(vc4000_cart_slot_device::read_rom)));
			break;
		case VC4000_ROM4K:
			m_maincpu->space(AS_PROGRAM).install_read_handler(0x0000, 0x0fff, read8sm_delegate(*m_cart, FUNC(vc4000_cart_slot_device::read_rom)));
			break;
		case VC4000_RAM1K:
			m_maincpu->space(AS_PROGRAM).install_read_handler(0x0000, 0x0fff, read8sm_delegate(*m_cart, FUNC(vc4000_cart_slot_device::read_rom)));
			m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x1000, 0x15ff, read8sm_delegate(*m_cart, FUNC(vc4000_cart_slot_device::read_ram)), write8sm_delegate(*m_cart, FUNC(vc4000_cart_slot_device::write_ram)));
			break;
		case VC4000_CHESS2:
			m_maincpu->space(AS_PROGRAM).install_read_handler(0x0000, 0x15ff, read8sm_delegate(*m_cart, FUNC(vc4000_cart_slot_device::read_rom)));
			m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x1800, 0x1bff, read8sm_delegate(*m_cart, FUNC(vc4000_cart_slot_device::read_ram)), write8sm_delegate(*m_cart, FUNC(vc4000_cart_slot_device::write_ram)));
			break;
		// undumped Radofin Hobby Module
//      case VC4000_HOBBY:
//          m_maincpu->space(AS_PROGRAM).install_read_handler(0x0000, 0x07ff, read8sm_delegate(*m_cart, FUNC(vc4000_cart_slot_device::read_rom)));
//          m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x0800, 0x0fff, read8sm_delegate(*m_cart, FUNC(vc4000_cart_slot_device::read_ram)), write8sm_delegate(*m_cart, FUNC(vc4000_cart_slot_device::write_ram)));
//          break;
		}

		m_cart->save_ram();
	}
}


QUICKLOAD_LOAD_MEMBER(vc4000_state::quickload_cb)
{
	int const quick_length = image.length();
	std::vector<uint8_t> quick_data;
	quick_data.resize(quick_length);
	int read_ = image.fread( &quick_data[0], quick_length);
	if (read_ != quick_length)
		return std::make_pair(image_error::UNSPECIFIED, "Cannot read the file");

	address_space &space = m_maincpu->space(AS_PROGRAM);

	if (image.is_filetype("tvc"))
	{
		if (quick_length < 0x5)
			return std::make_pair(image_error::INVALIDLENGTH, "File too short");
		else if (quick_data[0] != 2)
			return std::make_pair(image_error::INVALIDIMAGE, "Invalid header");

		int const quick_addr = quick_data[1] * 256 + quick_data[2];
		if ((quick_length + quick_addr - 5) > 0x1600)
			return std::make_pair(image_error::INVALIDLENGTH, "File too long");

		int const exec_addr = quick_data[3] * 256 + quick_data[4];

		space.write_byte(0x08be, quick_data[3]);
		space.write_byte(0x08bf, quick_data[4]);

		for (int i = 5; i < quick_length; i++)
			space.write_byte(i - 5 + quick_addr, quick_data[i]);

		// display a message about the loaded quickload
		image.message(" Quickload: size=%04X : start=%04X : end=%04X : exec=%04X",quick_length-5,quick_addr,quick_addr+quick_length-5,exec_addr);

		// Start the quickload
		m_maincpu->set_state_int(S2650_PC, exec_addr);

		return std::make_pair(std::error_condition(), std::string());
	}
	else if (image.is_filetype("pgm"))
	{
		if (quick_length < 0x904)
			return std::make_pair(image_error::INVALIDLENGTH, "File too short");
		else if (quick_length > 0x2000)
			return std::make_pair(image_error::INVALIDLENGTH, "File too long (must be no larger than 8K)");
		else if (quick_data[0] != 0)
			return std::make_pair(image_error::INVALIDIMAGE, "Invalid header");

		int const exec_addr = quick_data[1] * 256 + quick_data[2];
		if (exec_addr >= quick_length)
		{
			return std::make_pair(
					image_error::INVALIDIMAGE,
					util::string_format("Exec address %04X beyond end of file %04X", exec_addr, quick_length));
		}

		space.write_byte(0x08be, quick_data[1]);
		space.write_byte(0x08bf, quick_data[2]);

		// load to 08C0-15FF (standard ram + extra)
		read_ = 0x1600;
		if (quick_length < 0x1600)
			read_ = quick_length;
		for (int i = 0x8c0; i < read_; i++)
			space.write_byte(i, quick_data[i]);

		// load to 1F50-1FAF (PVI regs)
		read_ = 0x1fb0;
		if (quick_length < 0x1fb0)
			read_ = quick_length;
		if (quick_length > 0x1fc0)
		{
			for (int i = 0x1f50; i < read_; i++)
				vc4000_video_w(i-0x1f00, quick_data[i]);
		}

		// display a message about the loaded quickload */
		image.message(" Quickload: size=%04X : exec=%04X",quick_length,exec_addr);

		// Start the quickload
		m_maincpu->set_state_int(S2650_PC, exec_addr);

		return std::make_pair(std::error_condition(), std::string());
	}

	return std::make_pair(image_error::UNSUPPORTED, std::string());
}

static void vc4000_cart(device_slot_interface &device)
{
	device.option_add_internal("std",      VC4000_ROM_STD);
	device.option_add_internal("rom4k",    VC4000_ROM_ROM4K);
	device.option_add_internal("ram1k",    VC4000_ROM_RAM1K);
	device.option_add_internal("chess2",   VC4000_ROM_CHESS2);
}


void vc4000_state::vc4000(machine_config &config)
{
	/* basic machine hardware */
//  S2650(config, m_maincpu, 865000);        /* 3550000/4, 3580000/3, 4430000/3 */
	S2650(config, m_maincpu, 3546875/4);
	m_maincpu->set_addrmap(AS_PROGRAM, &vc4000_state::vc4000_mem);
	m_maincpu->sense_handler().set(FUNC(vc4000_state::vc4000_vsync_r));
	m_maincpu->set_periodic_int(FUNC(vc4000_state::vc4000_video_line), attotime::from_hz(312*53));  // GOLF needs this exact value
	m_maincpu->intack_handler().set([]() { return 0x03; });

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_size(226, 312);
	m_screen->set_visarea(8, 184, 0, 269);
	m_screen->set_screen_update(FUNC(vc4000_state::screen_update_vc4000));
	m_screen->set_palette("palette");

	PALETTE(config, "palette", FUNC(vc4000_state::vc4000_palette), 8);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	VC4000_SND(config, m_custom, 0).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* quickload */
	QUICKLOAD(config, "quickload", "pgm,tvc").set_load_callback(FUNC(vc4000_state::quickload_cb));

	/* cartridge */
	VC4000_CART_SLOT(config, "cartslot", vc4000_cart, nullptr);

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("vc4000");
}

void vc4000_state::cx3000tc(machine_config &config)
{
	vc4000(config);
	SOFTWARE_LIST(config.replace(), "cart_list").set_original("cx3000tc");
}

void vc4000_state::mpu1000(machine_config &config)
{
	vc4000(config);
	SOFTWARE_LIST(config.replace(), "cart_list").set_original("mpu1000");
}

void vc4000_state::database(machine_config &config)
{
	vc4000(config);
	SOFTWARE_LIST(config.replace(), "cart_list").set_original("database");
}

void vc4000_state::rwtrntcs(machine_config &config)
{
	vc4000(config);
	SOFTWARE_LIST(config.replace(), "cart_list").set_original("rwtrntcs");
}

void vc4000_state::h21(machine_config &config)
{
	vc4000(config);
	H21_CART_SLOT(config.replace(), "cartslot", vc4000_cart, nullptr);
	SOFTWARE_LIST(config.replace(), "cart_list").set_original("h21");
}

void vc4000_state::elektor(machine_config &config)
{
	vc4000(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &vc4000_state::elektor_mem);
	CASSETTE(config, m_cassette);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
}


ROM_START( vc4000 )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( spc4000 )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( cx3000tc )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( tvc4000 )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( 1292apvs )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( 1392apvs )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( mpu1000 )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( mpu2000 )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( pp1292 )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( pp1392 )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( f1392 )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( fforce2 )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( hmg1292 )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( hmg1392 )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( lnsy1392 )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( vc6000 )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( database )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( vmdtbase )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( rwtrntcs )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( telngtcs )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( krvnjvtv )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( oc2000 )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( mpt05 )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( h21 )
	ROM_REGION( 0x2000,"maincpu", ROMREGION_ERASEFF )
ROM_END

ROM_START( elektor )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "elektor.rom", 0x0000, 0x0800, CRC(e6ef1ee1) SHA1(6823b5a22582344016415f2a37f9f3a2dc75d2a7))
ROM_END



//    YEAR  NAME      PARENT    COMPAT  MACHINE   INPUT    CLASS         INIT  COMPANY        FULLNAME                                      FLAGS
CONS( 1978, vc4000,   0,        0,      vc4000,   vc4000,  vc4000_state, empty_init, "Interton",    "Interton Electronic VC 4000",                MACHINE_IMPERFECT_GRAPHICS) // Germany, Austria, UK, Australia
CONS( 1979, spc4000,  vc4000,   0,      vc4000,   vc4000,  vc4000_state, empty_init, "Grundig",     "Super Play Computer 4000",                   MACHINE_IMPERFECT_GRAPHICS) // Germany, Austria
CONS( 1979, cx3000tc, vc4000,   0,      cx3000tc, vc4000,  vc4000_state, empty_init, "Palson",      "CX 3000 Tele Computer",                      MACHINE_IMPERFECT_GRAPHICS) // Spain
CONS( 1979, tvc4000,  vc4000,   0,      vc4000,   vc4000,  vc4000_state, empty_init, "Koerting",    "TVC-4000",                                   MACHINE_IMPERFECT_GRAPHICS) // Argentina
CONS( 1976, 1292apvs, 0,        vc4000, vc4000,   vc4000,  vc4000_state, empty_init, "Radofin",     "1292 Advanced Programmable Video System",    MACHINE_IMPERFECT_GRAPHICS) // Europe
CONS( 1976, 1392apvs, 1292apvs, 0,      vc4000,   vc4000,  vc4000_state, empty_init, "Radofin",     "1392 Advanced Programmable Video System",    MACHINE_IMPERFECT_GRAPHICS) // Europe
CONS( 1979, mpu1000,  1292apvs, 0,      mpu1000,  vc4000,  vc4000_state, empty_init, "Acetronic",   "MPU-1000",                                   MACHINE_IMPERFECT_GRAPHICS) // Europe
CONS( 1979, mpu2000,  1292apvs, 0,      mpu1000,  vc4000,  vc4000_state, empty_init, "Acetronic",   "MPU-2000",                                   MACHINE_IMPERFECT_GRAPHICS) // Europe
CONS( 1978, pp1292,   1292apvs, 0,      vc4000,   vc4000,  vc4000_state, empty_init, "Audio Sonic", "PP-1292 Advanced Programmable Video System", MACHINE_IMPERFECT_GRAPHICS) // Europe
CONS( 1978, pp1392,   1292apvs, 0,      vc4000,   vc4000,  vc4000_state, empty_init, "Audio Sonic", "PP-1392 Advanced Programmable Video System", MACHINE_IMPERFECT_GRAPHICS) // Europe
CONS( 1979, f1392,    1292apvs, 0,      vc4000,   vc4000,  vc4000_state, empty_init, "Fountain",    "Fountain 1392",                              MACHINE_IMPERFECT_GRAPHICS) // New Zealand
CONS( 1979, fforce2,  1292apvs, 0,      vc4000,   vc4000,  vc4000_state, empty_init, "Fountain",    "Fountain Force 2",                           MACHINE_IMPERFECT_GRAPHICS) // New Zealand, Australia
CONS( 1979, hmg1292,  1292apvs, 0,      vc4000,   vc4000,  vc4000_state, empty_init, "Hanimex",     "HMG 1292",                                   MACHINE_IMPERFECT_GRAPHICS) // Europe
CONS( 1979, hmg1392,  1292apvs, 0,      vc4000,   vc4000,  vc4000_state, empty_init, "Hanimex",     "HMG 1392",                                   MACHINE_IMPERFECT_GRAPHICS) // Europe
CONS( 1979, lnsy1392, 1292apvs, 0,      vc4000,   vc4000,  vc4000_state, empty_init, "Lansay",      "Lansay 1392",                                MACHINE_IMPERFECT_GRAPHICS) // Europe
CONS( 1979, vc6000,   1292apvs, 0,      vc4000,   vc4000,  vc4000_state, empty_init, "Prinztronic", "VC 6000",                                    MACHINE_IMPERFECT_GRAPHICS) // UK
CONS( 1979, database, 0,        vc4000, database, vc4000,  vc4000_state, empty_init, "Voltmace",    "Voltmace Database",                          MACHINE_IMPERFECT_GRAPHICS) // UK
CONS( 1979, vmdtbase, database, 0,      database, vc4000,  vc4000_state, empty_init, "Videomaster", "Videomaster Database Games-Computer",        MACHINE_IMPERFECT_GRAPHICS) // UK
CONS( 1979, rwtrntcs, 0,        vc4000, rwtrntcs, vc4000,  vc4000_state, empty_init, "Rowtron",     "Rowtron Television Computer System",         MACHINE_IMPERFECT_GRAPHICS) // UK
CONS( 1979, telngtcs, rwtrntcs, 0,      rwtrntcs, vc4000,  vc4000_state, empty_init, "Teleng",      "Teleng Television Computer System",          MACHINE_IMPERFECT_GRAPHICS) // UK
CONS( 1979, krvnjvtv, 0,        vc4000, vc4000,   vc4000,  vc4000_state, empty_init, "SOE",         "OC Jeu Video TV Karvan",                     MACHINE_IMPERFECT_GRAPHICS) // France
CONS( 1979, oc2000,   krvnjvtv, 0,      vc4000,   vc4000,  vc4000_state, empty_init, "SOE",         "OC-2000",                                    MACHINE_IMPERFECT_GRAPHICS) // France
CONS( 1980, mpt05,    0,        vc4000, vc4000,   vc4000,  vc4000_state, empty_init, "ITMC",        "MPT-05",                                     MACHINE_IMPERFECT_GRAPHICS) // France
CONS( 1982, h21,      0,        vc4000, h21,      vc4000,  vc4000_state, empty_init, "TRQ",         "Video Computer H-21",                        MACHINE_IMPERFECT_GRAPHICS) // Spain
CONS( 1979, elektor,  0,        0,      elektor,  elektor, vc4000_state, empty_init, "Elektor",     "Elektor TV Games Computer",                  MACHINE_IMPERFECT_GRAPHICS)



vcc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Kevin Horton, Jonathan Gevaryahu, Sandro Ronco, hap
/*******************************************************************************

Fidelity Voice Chess Challenger series hardware
- Voice Chess Challenger (VCC) (2 revisions)
- Advanced Voice Chess Challenger (UVC)
- Grandmaster Voice Chess Challenger (Fidelity Deutschland product)
- Decorator Challenger (FCC)

Grandmaster and FCC are verified to be the same PCB + ROMs as UVC. So even though
they have a large wooden chessboard attached instead of a small plastic one, from
MAME's perspective there's nothing to emulate on top of UVC.

TODO:
- add low-pass filters to sound? but when using flt_rc, it does not sound like
  recordings from a real VCC, maybe use a netlist or is it overkill? (same goes
  for newer Fidelity chess computers with this speech chip)

BTANB:
- with the English voice ROM, the letter D is barely distinguishable from E,
  Fidelity never updated the ROM later, and it sounds fine with other languages

================================================================================

RE notes by Kevin Horton

The CPU is a Z80 running at 4MHz. The TSI chip runs at around 25KHz, using a
470K / 100pf RC network. This system is very very basic, and is composed of just
the Z80, 4 ROMs, the TSI chip, and an 8255.

The Z80's interrupt inputs are all pulled to VCC, so no interrupts are used.

Reset is connected to a power-on reset circuit and a button on the keypad (marked RE).

The TSI chip connects to a 4K ROM. All of the 'Voiced' Chess Challengers
use this same ROM  (three or four). The later chess boards use a slightly different
part number, but the contents are identical.

The speech chip analog out (pin 11) goes to a PNP transistor, followed by two
cascaded low-pass filters (18K+5nf and 18K+20nf), an LM386N amplifier, and a
speaker. Newer Fidelity chess computers with this chip have a similar configuration,
with an additional volume filter before the LM386N.

Memory map (VCC):
-----------------
0000-0FFF: 4K 2332 ROM VCC1 or 101-32013
1000-1FFF: 4K 2332 ROM VCC2
2000-2FFF: 4K 2332 ROM VCC3
4000-5FFF: 1K RAM (2114 SRAM x2)
6000-FFFF: empty

Memory map (UVC):
-----------------
0000-1FFF: 8K 2364 ROM 101-64017
2000-2FFF: 4K 2332 ROM 101-32010 or VCC3
4000-5FFF: 1K RAM (2114 SRAM x2)
6000-FFFF: empty

Port map:
---------
00-03: 8255 port chip, mirrored over the 00-FF range; program accesses F4-F7

8255 connections:
-----------------
PA.0 - segment G, TSI A0 (W)
PA.1 - segment F, TSI A1 (W)
PA.2 - segment E, TSI A2 (W)
PA.3 - segment D, TSI A3 (W)
PA.4 - segment C, TSI A4 (W)
PA.5 - segment B, TSI A5 (W)
PA.6 - segment A, language latch Data (W)
PA.7 - TSI START line, language latch clock (W, see below)

PB.0 - dot commons (W)
PB.1 - NC
PB.2 - digit 0, bottom dot (W)
PB.3 - digit 1, top dot (W)
PB.4 - digit 2 (W)
PB.5 - digit 3 (W)
PB.6 - enable language jumpers (W, see below)
PB.7 - TSI BUSY line (R)

(button rows pulled up to 5V through 2.2K resistors)
PC.0 - button row 0, German language jumper (R)
PC.1 - button row 1, French language jumper (R)
PC.2 - button row 2, Spanish language jumper (R)
PC.3 - button row 3, special language jumper (R)
PC.4 - button column A (W)
PC.5 - button column B (W)
PC.6 - button column C (W)
PC.7 - button column D (W)

Language jumpers:
-----------------
When PB.6 is pulled low, the language jumpers can be read. There are four.
They connect to the button rows. When enabled, the row(s) will read low if
the jumper is present. English only VCC's do not have the 367 or any pads stuffed.
The jumpers are labeled: French, German, Spanish, and special.

Language latch:
---------------
There's an unstuffed 7474 on the board that connects to PA.6 and PA.7. It allows
one to latch the state of A12 to the speech ROM. The English version has the chip
missing, and a jumper pulling "A12" to ground. This line is really a negative
enable.

To make the VCC multi-language, one would install the 74367 (note: it must be a 74367
or possibly a 74LS367. A 74HC367 would not work since they rely on the input current
to keep the inputs pulled up), solder a piggybacked ROM to the existing English
speech ROM, and finally install a 7474 dual flipflop.

This way, the game can then detect which secondary language is present, and then
it can automatically select the correct ROM(s). I have to test whether it will do
automatic determination and give you a language option on power up or something.

*******************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/i8255.h"
#include "sound/s14001a.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "fidel_vcc.lh"


namespace {

class vcc_state : public driver_device
{
public:
	vcc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ppi8255(*this, "ppi8255"),
		m_display(*this, "display"),
		m_speech(*this, "speech"),
		m_language(*this, "language"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// RE button is tied to Z80 RESET pin
	DECLARE_INPUT_CHANGED_MEMBER(reset_button) { m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE); }

	// machine configs
	void vcc(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<i8255_device> m_ppi8255;
	required_device<pwm_display_device> m_display;
	required_device<s14001a_device> m_speech;
	required_region_ptr<u8> m_language;
	required_ioport_array<4> m_inputs;

	u8 m_led_select = 0;
	u8 m_7seg_data = 0;
	u8 m_inp_mux = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;

	// I/O handlers
	void update_display();
	void ppi_porta_w(u8 data);
	u8 ppi_portb_r();
	void ppi_portb_w(u8 data);
	u8 ppi_portc_r();
	void ppi_portc_w(u8 data);
};

void vcc_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_led_select));
	save_item(NAME(m_7seg_data));
	save_item(NAME(m_inp_mux));

	// game relies on RAM filled with FF at power-on
	for (int i = 0; i < 0x400; i++)
		m_maincpu->space(AS_PROGRAM).write_byte(i + 0x4000, 0xff);
}



/*******************************************************************************
    I/O
*******************************************************************************/

void vcc_state::update_display()
{
	// 4 7seg leds (note: sel d0 for extra leds)
	u8 outdata = (m_7seg_data & 0x7f) | (m_led_select << 7 & 0x80);
	m_display->matrix(m_led_select >> 2 & 0xf, outdata);
}

void vcc_state::ppi_porta_w(u8 data)
{
	// d0-d6: digit segment data, bits are xABCDEFG
	m_7seg_data = bitswap<8>(data,7,0,1,2,3,4,5,6);
	update_display();

	// d6: language latch data
	// d7: language latch clock (latch on high)
	if (data & 0x80)
		m_speech->set_rom_bank(BIT(data, 6));

	// d0-d5: S14001A C0-C5
	// d7: S14001A start pin
	m_speech->data_w(data & 0x3f);
	m_speech->start_w(BIT(data, 7));
}

u8 vcc_state::ppi_portb_r()
{
	// d7: S14001A busy pin
	return (m_speech->busy_r()) ? 0x80 : 0x00;
}

void vcc_state::ppi_portb_w(u8 data)
{
	// d0,d2-d5: digit/led select
	// _d6: enable language jumpers
	m_led_select = data;
	update_display();
}

u8 vcc_state::ppi_portc_r()
{
	u8 data = 0;

	// d0-d3: multiplexed inputs (active low)
	for (int i = 0; i < 4; i++)
		if (BIT(m_inp_mux, i))
			data |= m_inputs[i]->read();

	// also language jumpers (hardwired)
	// 0(no jumper): English, 1: German, 2: French, 4: Spanish, 8: Special(unused)
	if (~m_led_select & 0x40)
		data |= *m_language;

	return ~data & 0xf;
}

void vcc_state::ppi_portc_w(u8 data)
{
	// d4-d7: input mux (inverted)
	m_inp_mux = ~data >> 4 & 0xf;
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void vcc_state::main_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x2fff).rom();
	map(0x4000, 0x43ff).mirror(0x1c00).ram();
}

void vcc_state::main_io(address_map &map)
{
	map.global_mask(0x03);
	map(0x00, 0x03).rw(m_ppi8255, FUNC(i8255_device::read), FUNC(i8255_device::write));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( vcc )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("LV") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("A1") PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_A)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("E5") PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_E)

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Speaker") PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("DM") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("B2") PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_B)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("F6") PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_F)

	PORT_START("IN.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("CL") PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("PB") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("C3") PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_C)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("G7") PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_G)

	PORT_START("IN.3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("EN") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("PV") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("D4") PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_D)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("H8") PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_H)

	PORT_START("RESET") // is not on matrix IN.0 d0
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("RE") PORT_CODE(KEYCODE_R) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vcc_state::reset_button), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void vcc_state::vcc(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &vcc_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &vcc_state::main_io);

	I8255(config, m_ppi8255);
	m_ppi8255->out_pa_callback().set(FUNC(vcc_state::ppi_porta_w));
	m_ppi8255->tri_pa_callback().set_constant(0);
	m_ppi8255->in_pb_callback().set(FUNC(vcc_state::ppi_portb_r));
	m_ppi8255->out_pb_callback().set(FUNC(vcc_state::ppi_portb_w));
	m_ppi8255->tri_pb_callback().set_constant(0);
	m_ppi8255->in_pc_callback().set(FUNC(vcc_state::ppi_portc_r));
	m_ppi8255->out_pc_callback().set(FUNC(vcc_state::ppi_portc_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(4, 8);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_fidel_vcc);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	S14001A(config, m_speech, 25000); // R/C circuit, around 25khz
	m_speech->add_route(ALL_OUTPUTS, "speaker", 0.75);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( vcc )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("cn19256n_101-32013", 0x0000, 0x1000, CRC(257bb5ab) SHA1(f7589225bb8e5f3eac55f23e2bd526be780b38b5) )
	ROM_LOAD("cn19174n_vcc_2", 0x1000, 0x1000, CRC(f33095e7) SHA1(692fcab1b88c910b74d04fe4d0660367aee3f4f0) )
	ROM_LOAD("cn19175n_vcc_3", 0x2000, 0x1000, CRC(624f0cd5) SHA1(7c1a4f4497fe5882904de1d6fecf510c07ee6fc6) )

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 0, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 4, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

ROM_START( vcca )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("cn19173n_vcc_1", 0x0000, 0x1000, CRC(6fab0464) SHA1(b917cfb488cfd73ed776d3838289623585530181) )
	ROM_LOAD("cn19174n_vcc_2", 0x1000, 0x1000, CRC(f33095e7) SHA1(692fcab1b88c910b74d04fe4d0660367aee3f4f0) )
	ROM_LOAD("cn19175n_vcc_3", 0x2000, 0x1000, CRC(624f0cd5) SHA1(7c1a4f4497fe5882904de1d6fecf510c07ee6fc6) )

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 0, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 4, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

ROM_START( avcc )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101-64017", 0x0000, 0x2000, CRC(f1133abf) SHA1(09dd85051c4e7d364d43507c1cfea5c2d08d37f4) ) // MOS // 101-64017 // 3880
	ROM_LOAD("101-32010", 0x2000, 0x1000, CRC(624f0cd5) SHA1(7c1a4f4497fe5882904de1d6fecf510c07ee6fc6) ) // NEC P9Z021 // D2332C 228 // 101-32010, == cn19175n_vcc3 on vcc

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 0, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 4, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) ) // NEC P9Y019 // D2332C 229 // 101-32107
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY, FULLNAME, FLAGS
SYST( 1979, vcc,  0,      0,      vcc,     vcc,   vcc_state, empty_init, "Fidelity Electronics", "Voice Chess Challenger (set 1)", MACHINE_SUPPORTS_SAVE )
SYST( 1979, vcca, vcc,    0,      vcc,     vcc,   vcc_state, empty_init, "Fidelity Electronics", "Voice Chess Challenger (set 2)", MACHINE_SUPPORTS_SAVE )

SYST( 1980, avcc, vcc,    0,      vcc,     vcc,   vcc_state, empty_init, "Fidelity Electronics", "Advanced Voice Chess Challenger", MACHINE_SUPPORTS_SAVE )



vcs80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************

    VCS-80

    12/05/2009 Skeleton driver.

    http://hc-ddr.hucki.net/entwicklungssysteme.htm#VCS_80_von_Eckhard_Schiller

This system is heavily based on the tk80. The display and keyboard matrix
are very similar, while the operation is easier in the vcs80. The hardware
is completely different however.

Pasting:
        0-F : as is
        A+ : ^
        A- : V
        MA : -
        GO : X

When booted, the system begins at 0000 which is ROM. You need to change the
address to 0400 before entering a program. Here is a test to paste in:
0400-11^22^33^44^55^66^77^88^99^0400-
Press the up-arrow to confirm data has been entered.

Operation:
4 digits at left is the address; 2 digits at right is the data.
As you increment addresses, the middle 2 digits show the previous byte.
You can enter 4 digits, and pressing 'MA' will transfer this info
to the left, thus setting the address to this value. Press 'A+' to
store new data and increment the address.

One unusual configuration item is that /A0 connects to PIO.B7, and so
whenever it goes high, an interrupt can be triggered.

Whenever a memory request is made (via /MREQ), it triggers a slight pause at
the CPU's WAIT pin.

****************************************************************************/
#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/z80daisy.h"
#include "machine/z80pio.h"
#include "machine/bankdev.h"
#include "machine/timer.h"
#include "video/pwm.h"
#include "vcs80.lh"


namespace {

class vcs80_state : public driver_device
{
public:
	vcs80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pio(*this, "pio")
		, m_bdmem(*this, "bdmem")
		, m_display(*this, "display")
		, m_io_keyboard(*this, "Y%u", 0U)
	{ }

	void vcs80(machine_config &config);

private:
	required_device<z80_device> m_maincpu;
	required_device<z80pio_device> m_pio;
	required_device<address_map_bank_device> m_bdmem;
	required_device<pwm_display_device> m_display;
	required_ioport_array<3> m_io_keyboard;

	virtual void machine_start() override ATTR_COLD;

	uint8_t pio_pa_r();
	void pio_pb_w(uint8_t data);

	uint8_t mem_r(offs_t offset)
	{
		m_pio->port_b_write((!BIT(offset, 0)) << 7);
		return m_bdmem->read8(offset);
	}

	void mem_w(offs_t offset, uint8_t data)
	{
		m_pio->port_b_write((!BIT(offset, 0)) << 7);
		m_bdmem->write8(offset, data);
	}

	uint8_t io_r(offs_t offset)
	{
		m_pio->port_b_write((!BIT(offset, 0)) << 7);
		if (BIT(offset, 2))
			return m_pio->read(offset^3);
		return 0xff;
	}

	void io_w(offs_t offset, uint8_t data)
	{
		m_pio->port_b_write((!BIT(offset, 0)) << 7);
		if (BIT(offset, 2))
			m_pio->write(offset^3, data);
	}

	/* keyboard state */
	bool m_keyclk = false;
	u8 m_digit = 0U;
	u8 m_seg = 0U;
	void init_vcs80();
	TIMER_DEVICE_CALLBACK_MEMBER(vcs80_keyboard_tick);

	void bd_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
};



/* Memory Maps */

void vcs80_state::bd_map(address_map &map)
{
	map(0x0000, 0x01ff).rom().region("maincpu", 0);
	map(0x0400, 0x07ff).ram();
}

void vcs80_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(vcs80_state::mem_r), FUNC(vcs80_state::mem_w));
}

void vcs80_state::io_map(address_map &map)
{
	map.global_mask(0x07);
	map(0x00, 0x07).rw(FUNC(vcs80_state::io_r), FUNC(vcs80_state::io_w));
}

/* Input Ports */

static INPUT_PORTS_START( vcs80 )
	PORT_START("Y0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7')

	PORT_START("Y1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("Y2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A+") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A-") PORT_CODE(KEYCODE_DOWN) PORT_CHAR('V')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("MA") PORT_CODE(KEYCODE_M) PORT_CHAR('-')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RE") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GO") PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("TR") PORT_CODE(KEYCODE_T)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ST") PORT_CODE(KEYCODE_S)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("PE") PORT_CODE(KEYCODE_P)
INPUT_PORTS_END

/* Z80-PIO Interface */

TIMER_DEVICE_CALLBACK_MEMBER(vcs80_state::vcs80_keyboard_tick)
{
	if (m_keyclk)
	{
		m_digit++;
		m_digit &= 7;
		m_display->matrix(1 << m_digit, m_seg);
	}

	m_pio->port_a_write(m_keyclk << 7);

	m_keyclk = !m_keyclk;
}

uint8_t vcs80_state::pio_pa_r()
{
	/*

	    bit     description

	    PA0     keyboard and led latch bit 0
	    PA1     keyboard and led latch bit 1
	    PA2     keyboard and led latch bit 2
	    PA3     GND
	    PA4     keyboard row input 0
	    PA5     keyboard row input 1
	    PA6     keyboard row input 2
	    PA7     demultiplexer clock input

	*/

	/* keyboard and led latch */
	u8 data = m_digit;

	/* keyboard rows */
	data |= BIT(m_io_keyboard[0]->read(), m_digit) << 4;
	data |= BIT(m_io_keyboard[1]->read(), m_digit) << 5;
	data |= BIT(m_io_keyboard[2]->read(), m_digit) << 6;

	/* demultiplexer clock */
	data |= (m_keyclk << 7);

	return data;
}

void vcs80_state::pio_pb_w(uint8_t data)
{
	/*

	    bit     description

	    PB0     VQD30 segment A
	    PB1     VQD30 segment B
	    PB2     VQD30 segment C
	    PB3     VQD30 segment D
	    PB4     VQD30 segment E
	    PB5     VQD30 segment G
	    PB6     VQD30 segment F
	    PB7     _A0

	*/

	m_seg = bitswap<8>(data & 0x7f, 7, 5, 6, 4, 3, 2, 1, 0);
	m_display->matrix(1 << m_digit, m_seg);
}

/* Z80 Daisy Chain */

static const z80_daisy_config daisy_chain[] =
{
	{ "pio" },
	{ nullptr }
};

/* Machine Initialization */

void vcs80_state::machine_start()
{
	m_pio->strobe_a(1);
	m_pio->strobe_b(1);

	/* register for state saving */
	save_item(NAME(m_keyclk));
	save_item(NAME(m_digit));
	save_item(NAME(m_seg));
}

/* Machine Driver */

void vcs80_state::vcs80(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 5'000'000 /2); // U880D - Uses a LC oscillator rather than a crystal
	m_maincpu->set_addrmap(AS_PROGRAM, &vcs80_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &vcs80_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	/* keyboard timer */
	TIMER(config, "keyboard").configure_periodic(FUNC(vcs80_state::vcs80_keyboard_tick), attotime::from_hz(1000));

	/* video hardware */
	config.set_default_layout(layout_vcs80);
	PWM_DISPLAY(config, m_display).set_size(8, 8);
	m_display->set_segmask(0xff, 0xff);

	/* devices */
	Z80PIO(config, m_pio, 5'000'000 /2);
	m_pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_pio->in_pa_callback().set(FUNC(vcs80_state::pio_pa_r));
	m_pio->out_pb_callback().set(FUNC(vcs80_state::pio_pb_w));

	/* bankdev */
	ADDRESS_MAP_BANK(config, "bdmem").set_map(&vcs80_state::bd_map).set_options(ENDIANNESS_BIG, 8, 32, 0x10000);
}

/* ROMs */

ROM_START( vcs80 )
	ROM_REGION( 0x0200, "maincpu", 0 )
	ROM_LOAD( "monitor.rom", 0x0000, 0x0200, CRC(44aff4e9) SHA1(3472e5a9357eaba3ed6de65dee2b1c6b29349dd2) )
ROM_END

} // anonymous namespace


/* System Drivers */

/*    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  STATE        INIT        COMPANY             FULLNAME  FLAGS */
COMP( 1983, vcs80, 0,      0,      vcs80,   vcs80, vcs80_state, empty_init, "Eckhard Schiller", "VCS-80", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )




vd56sp.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Speedcom 56K V.90 external modem using Conexant
    (formerly Rockwell) chipset.

****************************************************************************/

#include "emu.h"
#include "cpu/m6502/r65c19.h"


namespace {

class vd56sp_state : public driver_device
{
public:
	vd56sp_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void vd56sp(machine_config &mconfig);

private:
	void exp_map(address_map &map) ATTR_COLD;

	required_device<r65c19_device> m_maincpu;
};


void vd56sp_state::exp_map(address_map &map)
{
	map(0x0e0000, 0x0fffff).rom().region("firmware", 0x20000);
	map(0x160000, 0x16ffff).mirror(0x10000).ram();
	map(0x1e0000, 0x1effff).mirror(0x10000).ram();
}


static INPUT_PORTS_START(vd56sp)
INPUT_PORTS_END

void vd56sp_state::vd56sp(machine_config &config)
{
	L2800(config, m_maincpu, 8'000'000); // L28L2800-38 (XTAL not readable)
	m_maincpu->set_addrmap(AS_DATA, &vd56sp_state::exp_map);

	// Modem IC: Conexant R6764-61
}


ROM_START(vd56sp)
	ROM_REGION(0x40000, "firmware", 0)
	ROM_LOAD("vd56sp_v2.2_8904005.u10", 0x00000, 0x40000, CRC(23ddae13) SHA1(7a194f681389c2923ea6848b3a25f26c532a3200))
ROM_END

} // anonymous namespace


SYST(199?, vd56sp, 0, 0, vd56sp, vd56sp, vd56sp_state, empty_init, "Pro-Nets Technology", "Speedcom VD56SP", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



vdm7932x.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for Mera-Elzab VDM 79321/79322 terminals.

***************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/pit8253.h"
#include "machine/i8255.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"


namespace {

class vdm7932x_state : public driver_device
{
public:
	vdm7932x_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_subcpu(*this, "subcpu")
	{
	}

	void vdm7932x(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;

	void scan_w(offs_t offset, u8 data);
	u8 i8031_p3_r();
	void ppi1_pc_w(u8 data);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void sub_map(address_map &map) ATTR_COLD;
	void subx_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
	required_device<i8031_device> m_subcpu;
	bool m_obfa = false;
};


void vdm7932x_state::machine_start()
{
	m_obfa = false;
	save_item(NAME(m_obfa));
}

void vdm7932x_state::scan_w(offs_t offset, u8 data)
{
}

u8 vdm7932x_state::i8031_p3_r()
{
	return m_obfa ? 0xfd : 0xff;
}

void vdm7932x_state::ppi1_pc_w(u8 data)
{
	m_obfa = !BIT(data, 7);
}

void vdm7932x_state::mem_map(address_map &map)
{
	map(0x0000, 0xbfff).rom().region("maincpu", 0);
	map(0xc000, 0xdfff).ram();
}

void vdm7932x_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x40, 0x40).nopw(); // ?
	map(0x44, 0x47).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x48, 0x4b).w("pit", FUNC(pit8253_device::write));
	map(0x54, 0x57).rw("ppi1", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x58, 0x5b).rw("sio", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
	map(0x80, 0x83).rw("ppi2", FUNC(i8255_device::read), FUNC(i8255_device::write));
}

void vdm7932x_state::sub_map(address_map &map)
{
	map(0x0000, 0x3fff).rom().region("subcpu", 0);
}

void vdm7932x_state::subx_map(address_map &map)
{
	map(0x00, 0x00).select(0xff00).w(FUNC(vdm7932x_state::scan_w));
	map(0x49, 0x49).mirror(0xff00).r("ppi1", FUNC(i8255_device::acka_r));
}


static INPUT_PORTS_START(vdm7932x)
INPUT_PORTS_END


static const z80_daisy_config daisy_chain[] =
{
	{ "sio" },
	{ "ctc" },
	{ nullptr }
};

void vdm7932x_state::vdm7932x(machine_config &config) // all clocks unverified
{
	Z80(config, m_maincpu, 24.0734_MHz_XTAL / 8); // UA880D
	m_maincpu->set_addrmap(AS_PROGRAM, &vdm7932x_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &vdm7932x_state::io_map);
	m_maincpu->set_daisy_config(daisy_chain);

	I8031(config, m_subcpu, 24.0734_MHz_XTAL / 4); // Intel P8031AH (for keyboard?)
	m_subcpu->port_in_cb<3>().set(FUNC(vdm7932x_state::i8031_p3_r));
	m_subcpu->set_addrmap(AS_PROGRAM, &vdm7932x_state::sub_map);
	m_subcpu->set_addrmap(AS_IO, &vdm7932x_state::subx_map);

	PIT8253(config, "pit", 0); // UM8253-5

	z80ctc_device &ctc(Z80CTC(config, "ctc", 24.0734_MHz_XTAL / 8)); // UA857D
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80sio_device &sio(Z80SIO(config, "sio", 24.0734_MHz_XTAL / 8)); // UA8560D
	sio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	i8255_device &ppi1(I8255A(config, "ppi1")); // КР580ВВ55А (on separate card)
	ppi1.out_pc_callback().set(FUNC(vdm7932x_state::ppi1_pc_w));

	I8255A(config, "ppi2"); // КР580ВВ55А (on separate card)
}


ROM_START( vdm79322 ) // 8k ram // b&w (amber)
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "27512_m322.bin",      0x00000, 0x10000, CRC(24573079) SHA1(b81c17e99493302054d78fbee2e416ab6493b5f3) )

	ROM_REGION( 0x04000, "subcpu", 0 )
	ROM_LOAD( "27128_w322-3700.bin", 0x00000, 0x04000, CRC(e5e76ca2) SHA1(bb18c9fa29ef9fa0563aa07d2b856cf6594fc020) )
ROM_END

} // anonymous namespace


COMP(1992, vdm79322, 0, 0, vdm7932x, vdm7932x, vdm7932x_state, empty_init, "Mera-Elzab", "VDM 79322/CM 7233", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



vector06.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, MetalliC
/***************************************************************************

        Vector06c driver by Miodrag Milanovic

        10/07/2008 Preliminary driver.

boot from ROM cart:
 hold F2 then system reset (press F11), then press F12

boot from FDD:
 press F12 after initial boot was load (indicated in screen lower part)
 hold Ctrl ("YC" key) during MicroDOS start to format RAM disk (required by some games)

48k MicroDos one-letter commands:
B   crash?
D   dir
E   erase
K   ?
O   some kind of status display
U   user

TODO:
 - correct CPU speed / latency emulation, each machine cycle takes here 4 clocks,
   i.e. INX B 4+1 will be 2*4=8clocks, SHLD addr is 4+3+3+3+3 so it will be 5*4=20clocks and so on
 - "Card Game" wont work, jump to 0 instead of vblank interrupt RST7, something banking related ?
 - border emulation
 - separate base unexpanded Vector06C configuration
 - slotify AY8910 sound boards ?
 - Rus/Lat key doesn't seem to be right?

****************************************************************************/

#include "emu.h"
#include "vector06.h"

#include "formats/vector06_dsk.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

/* Address maps */
void vector06_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).bankrw("bank1");
	map(0x0000, 0x7fff).bankr("bank2");
	map(0xa000, 0xdfff).bankrw("bank3");
}

void vector06_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();

	map(0x00, 0x03).lrw8(NAME([this] (offs_t offset) -> u8 { return m_ppi1->read(offset^3); }), NAME([this] (offs_t offset, u8 data) { m_ppi1->write(offset^3, data); }));
	map(0x04, 0x07).lrw8(NAME([this] (offs_t offset) -> u8 { return m_ppi2->read(offset^3); }), NAME([this] (offs_t offset, u8 data) { m_ppi2->write(offset^3, data); }));
	map(0x08, 0x0b).lrw8(NAME([this] (offs_t offset) -> u8 { return m_pit->read(offset^3); }), NAME([this] (offs_t offset, u8 data) { m_pit->write(offset^3, data); }));
	map(0x0c, 0x0c).w(FUNC(vector06_state::color_set));
	map(0x10, 0x10).w(FUNC(vector06_state::ramdisk_w));
	map(0x14, 0x15).rw(m_ay, FUNC(ay8910_device::data_r), FUNC(ay8910_device::data_address_w));
	map(0x18, 0x18).rw(m_fdc, FUNC(kr1818vg93_device::data_r), FUNC(kr1818vg93_device::data_w));
	map(0x19, 0x19).rw(m_fdc, FUNC(kr1818vg93_device::sector_r), FUNC(kr1818vg93_device::sector_w));
	map(0x1a, 0x1a).rw(m_fdc, FUNC(kr1818vg93_device::track_r), FUNC(kr1818vg93_device::track_w));
	map(0x1b, 0x1b).rw(m_fdc, FUNC(kr1818vg93_device::status_r), FUNC(kr1818vg93_device::cmd_w));
	map(0x1c, 0x1c).w(FUNC(vector06_state::disc_w));
}

/* Input ports */
static INPUT_PORTS_START( vector06 )
	PORT_START("LINE.0")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB)         PORT_CHAR(9)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del") PORT_CODE(KEYCODE_DEL)         PORT_CHAR(UCHAR_MAMEKEY(DEL))
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER)     PORT_CHAR(13)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BkSp") PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(8)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)           PORT_CHAR(UCHAR_MAMEKEY(UP))
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)     PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_START("LINE.1")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Home") PORT_CODE(KEYCODE_HOME)       PORT_CHAR(UCHAR_MAMEKEY(HOME))
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PgUp") PORT_CODE(KEYCODE_PGUP)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC)         PORT_CHAR(27)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)           PORT_CHAR(UCHAR_MAMEKEY(F1))
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)           PORT_CHAR(UCHAR_MAMEKEY(F2))
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3)           PORT_CHAR(UCHAR_MAMEKEY(F3))
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4)           PORT_CHAR(UCHAR_MAMEKEY(F4))
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5)           PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_START("LINE.2")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)             PORT_CHAR('0')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 !") PORT_CODE(KEYCODE_1)           PORT_CHAR('1') PORT_CHAR('!')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 \"") PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('"')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 #") PORT_CODE(KEYCODE_3)           PORT_CHAR('3') PORT_CHAR('#')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)             PORT_CHAR('4') PORT_CHAR(164)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 %") PORT_CODE(KEYCODE_5)           PORT_CHAR('5') PORT_CHAR('%')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 &") PORT_CODE(KEYCODE_6)           PORT_CHAR('6') PORT_CHAR('&')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 \'") PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('\'')
	PORT_START("LINE.3")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 (") PORT_CODE(KEYCODE_8)           PORT_CHAR('8') PORT_CHAR('(')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 )") PORT_CODE(KEYCODE_9)           PORT_CHAR('9') PORT_CHAR(')')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(": *") PORT_CODE(KEYCODE_MINUS)       PORT_CHAR(':') PORT_CHAR('*')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("; +") PORT_CODE(KEYCODE_COLON)       PORT_CHAR(';') PORT_CHAR('+')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA)       PORT_CHAR(',') PORT_CHAR('<')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("- =") PORT_CODE(KEYCODE_EQUALS)      PORT_CHAR('-') PORT_CHAR('=')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP)        PORT_CHAR('.') PORT_CHAR('>')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH)       PORT_CHAR('/') PORT_CHAR('?')
	PORT_START("LINE.4")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@ `") PORT_CODE(KEYCODE_QUOTE)       PORT_CHAR('@') PORT_CHAR('`')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)             PORT_CHAR('A') PORT_CHAR('a')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)             PORT_CHAR('B') PORT_CHAR('b')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)             PORT_CHAR('C') PORT_CHAR('c')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)             PORT_CHAR('D') PORT_CHAR('d')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)             PORT_CHAR('E') PORT_CHAR('e')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)             PORT_CHAR('F') PORT_CHAR('f')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)             PORT_CHAR('G') PORT_CHAR('g')
	PORT_START("LINE.5")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)             PORT_CHAR('H') PORT_CHAR('h')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)             PORT_CHAR('I') PORT_CHAR('i')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)             PORT_CHAR('J') PORT_CHAR('j')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)             PORT_CHAR('K') PORT_CHAR('k')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)             PORT_CHAR('L') PORT_CHAR('l')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)             PORT_CHAR('M') PORT_CHAR('m')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)             PORT_CHAR('N') PORT_CHAR('n')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)             PORT_CHAR('O') PORT_CHAR('o')
	PORT_START("LINE.6")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)             PORT_CHAR('P') PORT_CHAR('p')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)             PORT_CHAR('Q') PORT_CHAR('q')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)             PORT_CHAR('R') PORT_CHAR('r')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)             PORT_CHAR('S') PORT_CHAR('s')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)             PORT_CHAR('T') PORT_CHAR('t')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)             PORT_CHAR('U') PORT_CHAR('u')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)             PORT_CHAR('V') PORT_CHAR('v')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)             PORT_CHAR('W') PORT_CHAR('w')
	PORT_START("LINE.7")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)             PORT_CHAR('X') PORT_CHAR('x')
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)             PORT_CHAR('Y') PORT_CHAR('y')
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)             PORT_CHAR('Z') PORT_CHAR('z')
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[ {") PORT_CODE(KEYCODE_OPENBRACE)   PORT_CHAR('[') PORT_CHAR('{')
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\ |") PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('\\') PORT_CHAR('|')
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("] }") PORT_CODE(KEYCODE_CLOSEBRACE)  PORT_CHAR(']') PORT_CHAR('}')
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^ ~") PORT_CODE(KEYCODE_TILDE)       PORT_CHAR('^') PORT_CHAR('~')
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)     PORT_CHAR(32)
	PORT_START("LINE.8")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT)    PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl YC") PORT_CODE(KEYCODE_LCONTROL)   //PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Rus/Lat") PORT_CODE(KEYCODE_LALT) // This acts as a caps lock in the CP/M screen
	PORT_START("RESET")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_F11)      PORT_CHAR(UCHAR_MAMEKEY(F11)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vector06_state::f11_button), 0)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("AP2") PORT_CODE(KEYCODE_F12)        PORT_CHAR(UCHAR_MAMEKEY(F12)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vector06_state::f12_button), 0)

INPUT_PORTS_END


void vector06_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_VECTOR06_FORMAT);
}

static void vector06_floppies(device_slot_interface &device)
{
	device.option_add("qd", FLOPPY_525_QD);
}


/* Machine driver */
void vector06_state::vector06(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, 3000000); // actual speed is wrong due to unemulated latency
	m_maincpu->set_addrmap(AS_PROGRAM, &vector06_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &vector06_state::io_map);
	m_maincpu->out_status_func().set(FUNC(vector06_state::status_callback));
	m_maincpu->set_vblank_int("screen", FUNC(vector06_state::irq0_line_hold));
	m_maincpu->set_irq_acknowledge_callback(FUNC(vector06_state::irq_callback));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_size(256+64, 256+64);
	m_screen->set_visarea(0, 256+64-1, 0, 256+64-1);
	m_screen->set_screen_update(FUNC(vector06_state::screen_update));
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette, palette_device::BLACK, 16);

	SPEAKER(config, "mono").front_center();

	/* devices */
	I8255(config, m_ppi1);
	m_ppi1->in_pb_callback().set(FUNC(vector06_state::ppi1_portb_r));
	m_ppi1->in_pc_callback().set(FUNC(vector06_state::ppi1_portc_r));
	m_ppi1->out_pa_callback().set(FUNC(vector06_state::ppi1_porta_w));
	m_ppi1->out_pb_callback().set(FUNC(vector06_state::ppi1_portb_w));

	I8255(config, m_ppi2);
	m_ppi2->in_pb_callback().set(FUNC(vector06_state::ppi2_portb_r));
	m_ppi2->out_pa_callback().set(FUNC(vector06_state::ppi2_porta_w));
	m_ppi2->out_pb_callback().set(FUNC(vector06_state::ppi2_portb_w));
	m_ppi2->out_pc_callback().set(FUNC(vector06_state::ppi2_portc_w));

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);

	KR1818VG93(config, m_fdc, 1_MHz_XTAL);

	FLOPPY_CONNECTOR(config, "fdc:0", vector06_floppies, "qd", vector06_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", vector06_floppies, "qd", vector06_state::floppy_formats);
	SOFTWARE_LIST(config, "flop_list").set_original("vector06_flop");

	/* cartridge */
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "vector06_cart", "bin,emr");
	SOFTWARE_LIST(config, "cart_list").set_original("vector06_cart");

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("320K").set_default_value(0);

	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(1500000);
	m_pit->set_clk<1>(1500000);
	m_pit->set_clk<2>(1500000);
	m_pit->out_handler<0>().set(FUNC(vector06_state::speaker_w));
	m_pit->out_handler<1>().set(FUNC(vector06_state::speaker_w));
	m_pit->out_handler<2>().set(FUNC(vector06_state::speaker_w));

	// optional
	AY8910(config, m_ay, 1773400).add_route(ALL_OUTPUTS, "mono", 0.50);
}


/* ROM definition */

ROM_START( vector06 )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "unboot32k", "Universal Boot 32K")
	ROMX_LOAD( "unboot32k.rt", 0x0000, 0x8000, CRC(28c9b5cd) SHA1(8cd7fb658896a7066ae93b10eaafa0f12139ad81), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "unboot2k", "Universal Boot 2K")
	ROMX_LOAD( "unboot2k.rt",  0x0000, 0x0800, CRC(4c80dc31) SHA1(7e5e3acfdbea2e52b0d64c5868821deaec383815), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "coman", "Boot Coman")
	ROMX_LOAD( "coman.rt",     0x0000, 0x0800, CRC(f8c4a85a) SHA1(47fa8b02f09a1d06aa63a2b90b2597b1d93d976f), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "bootbyte", "Boot Byte")
	ROMX_LOAD( "bootbyte.rt",  0x0000, 0x0800, CRC(3b42fd9d) SHA1(a112f4fe519bc3dbee85b09040d4804a17c9eda2), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "bootos", "Boot OS")
	ROMX_LOAD( "bootos.rt",    0x0000, 0x0200, CRC(46bef038) SHA1(6732f4a360cd38112c53c458842d31f5b035cf59), ROM_BIOS(4))
	ROM_SYSTEM_BIOS(5, "boot512", "Boot 512")
	ROMX_LOAD( "boot512.rt",   0x0000, 0x0200, CRC(a0b1c6b2) SHA1(f6fe15cb0974aed30f9b7aa72133324a66d1ed3f), ROM_BIOS(5))
ROM_END

ROM_START( vec1200 )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "vec1200.bin", 0x0000, 0x2000, CRC(37349224) SHA1(060fbb2c1a89040c929521cfd58cb6f1431a8b75))

	ROM_REGION( 0x0200, "palette", 0 )
	ROM_LOAD( "palette.bin", 0x0000, 0x0200, CRC(74b7376b) SHA1(fb56b60babd7e6ed68e5f4e791ad2800d7ef6729))
ROM_END

ROM_START( pk6128c )
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "6128.bin", 0x0000, 0x4000, CRC(d4f68433) SHA1(ef5ac75f9240ca8996689c23642d4e47e5e774d8))
ROM_END

ROM_START( krista2 ) // appears it wants to load a tape at boot
	ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "krista2.epr", 0x0000, 0x0200, CRC(df5440b0) SHA1(bcbbb3cc10aeb17c1262b45111d20279266b9ba4))

	ROM_REGION( 0x0200, "palette", 0 )
	ROM_LOAD( "krista2.pal", 0x0000, 0x0200, CRC(b243da33) SHA1(9af7873e6f8bf452c8d831833ffb02dce833c095))
ROM_END
/* Driver */

/*    YEAR  NAME      PARENT    COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY      FULLNAME       FLAGS */
COMP( 1987, vector06, 0,        0,      vector06, vector06, vector06_state, empty_init, "<unknown>", "Vector 06c",  MACHINE_SUPPORTS_SAVE )
COMP( 1987, vec1200,  vector06, 0,      vector06, vector06, vector06_state, empty_init, "<unknown>", "Vector 1200", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1987, pk6128c,  vector06, 0,      vector06, vector06, vector06_state, empty_init, "<unknown>", "PK-6128c",    MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1987, krista2,  vector06, 0,      vector06, vector06, vector06_state, empty_init, "<unknown>", "Krista-2",    MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



vector3.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/***************************************************************************

Vector Graphic, Inc Vector 3 and Vector MZ. Vector Graphic was a Californian
company that made a few popular computers until they were crushed flat by the
IBM PC.

2009-12-08 Skeleton driver for "Vector 4".
2022-12-03 Renamed driver to "Vector 3"

This driver was based on the Vector 4 User Guide but accidentally with the
Vector 3 or other ROMs, because of confusing naming. To get the ROMs to work
required Vector 3 behavior.

The Vector 3 and Vector MZ had similar internal hardware. Both are based on the
ZCB, Flashwriter II, and Micropolis floppy controller.

The 7100-0245-00-00 Extended Systems Monitor 4.3 Users Manual seems to
correspond with BIOS 1.

BIOS 0 only talks to UARTs at 2-7 and nothing else. It uses a terminal
for all communications.

BIOS 1 and 2 only talk to UARTs at 0-7 and the undocumented port 40.
They also write a video screen at F000-F7FF, but there's no writes
to any video controller. Also, the chargen roms (2x 2716) are missing.

https://deramp.com/vector_graphic.html
7200-0203-03-02: https://bitsavers.org/pdf/vectorGraphic/hardware/7200-0204-03-02_ZCB_Single_Board_Computer_Jun80.pdf
https://bitsavers.org/pdf/vectorGraphic/hardware/Vector_Z80_Board_Users_Manual_May79.pdf
https://bitsavers.org/pdf/vectorGraphic/software/7100-0245-00-00_Extended_Systems_Monitor_4.3_Users_Manual_Jul81.pdf
https://bitsavers.org/pdf/vectorGraphic/hardware/Vector_Flashwriter_II_Mar79.pdf
https://deramp.com/downloads/vector_graphic/hardware/Flashwriter%20II%20Manual.pdf
https://deramp.com/downloads/vector_graphic/hardware/Flashwriter%20II%20Schematic%20Rev%204.tif
https://bitsavers.org/pdf/vectorGraphic/hardware/Micropolis_Disk_Controller_Board.pdf

TODO:
- H command goes crazy
- chargen roms
- video
- keyboard
- probable keyboard mcu
- floppies
- many other things

****************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/pit8253.h"


namespace {

class vector3_state : public driver_device
{
public:
	vector3_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
	{ }

	void vector3(machine_config &config);

private:
	void vector3_io(address_map &map) ATTR_COLD;
	void vector3_mem(address_map &map) ATTR_COLD;

	memory_passthrough_handler m_rom_shadow_tap;
	required_device<cpu_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	void machine_reset() override ATTR_COLD;
};


void vector3_state::vector3_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).ram().share("mainram");
	map(0xe000, 0xefff).rom().region("maincpu", 0);
	//map(0xf000, 0xf7ff).ram().share("videoram");  // bios 1,2
	map(0xf800, 0xf8ff).rom().region("maincpu", 0x1000);
}

void vector3_state::vector3_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x01).rw("uart0", FUNC(i8251_device::read), FUNC(i8251_device::write)); // keyboard
	map(0x02, 0x03).rw("uart1", FUNC(i8251_device::read), FUNC(i8251_device::write)); // terminal, bios 0 only
	map(0x04, 0x05).mirror(0x02).rw("uart2", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x08, 0x0b).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x10, 0x13).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	// map(0x40, 0x40)  undocumented
}

/* Input ports */
static INPUT_PORTS_START( vector3 )
INPUT_PORTS_END


void vector3_state::machine_reset()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x0000, 0x0fff, m_rom);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0xe000, 0xefff,
			"rom_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// reinstall RAM over the ROM shadow
					m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x0fff, m_ram);
				}
			},
			&m_rom_shadow_tap);
}


void vector3_state::vector3(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 4'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &vector3_state::vector3_mem);
	m_maincpu->set_addrmap(AS_IO, &vector3_state::vector3_io);

	/* video hardware */
	clock_device &uart_clock(CLOCK(config, "uart_clock", 153600));
	uart_clock.signal_handler().set("uart1", FUNC(i8251_device::write_txc));
	uart_clock.signal_handler().append("uart0", FUNC(i8251_device::write_txc));
	uart_clock.signal_handler().append("uart0", FUNC(i8251_device::write_rxc));
	uart_clock.signal_handler().append("uart1", FUNC(i8251_device::write_rxc));
	uart_clock.signal_handler().append("uart2", FUNC(i8251_device::write_txc));
	uart_clock.signal_handler().append("uart2", FUNC(i8251_device::write_rxc));

	I8251(config, "uart0", 0);

	i8251_device &uart1(I8251(config, "uart1", 0));
	uart1.txd_handler().set("rs232a", FUNC(rs232_port_device::write_txd));
	uart1.dtr_handler().set("rs232a", FUNC(rs232_port_device::write_dtr));
	uart1.rts_handler().set("rs232a", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, "terminal"));
	rs232a.rxd_handler().set("uart1", FUNC(i8251_device::write_rxd));
	rs232a.dsr_handler().set("uart1", FUNC(i8251_device::write_dsr));
	rs232a.cts_handler().set("uart1", FUNC(i8251_device::write_cts));

	i8251_device &uart2(I8251(config, "uart2", 0));
	uart2.txd_handler().set("rs232b", FUNC(rs232_port_device::write_txd));
	uart2.dtr_handler().set("rs232b", FUNC(rs232_port_device::write_dtr));
	uart2.rts_handler().set("rs232b", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
	rs232b.rxd_handler().set("uart2", FUNC(i8251_device::write_rxd));
	rs232b.dsr_handler().set("uart2", FUNC(i8251_device::write_dsr));
	rs232b.cts_handler().set("uart2", FUNC(i8251_device::write_cts));

	I8255A(config, "ppi");
	PIT8253(config, "pit", 0);
}

/* ROM definition */
ROM_START( vector3 )
	ROM_REGION( 0x1100, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "mfdc.bin", 0x1000, 0x0100, CRC(d82a40d6) SHA1(cd1ef5fb0312cd1640e0853d2442d7d858bc3e3b))

	ROM_SYSTEM_BIOS( 0, "v4c", "ver 4.0c" ) // VECTOR GRAPHIC MONITOR VERSION 4.0C
	ROMX_LOAD( "vg40cl_ihl.bin", 0x0000, 0x0400, CRC(dcaf79e6) SHA1(63619ddb12ff51e5862902fb1b33a6630f555ad7), ROM_BIOS(0))
	ROMX_LOAD( "vg40ch_ihl.bin", 0x0400, 0x0400, CRC(3ff97d70) SHA1(b401e49aa97ac106c2fd5ee72d89e683ebe34e34), ROM_BIOS(0))

	ROM_SYSTEM_BIOS( 1, "v43", "ver 4.3" ) // VECTOR GRAPHIC MONITOR VERSION 4.3
	ROMX_LOAD( "vg-em-43.bin",   0x0000, 0x1000, CRC(29a0fcee) SHA1(ca44de527f525b72f78b1c084c39aa6ce21731b5), ROM_BIOS(1))

	ROM_SYSTEM_BIOS( 2, "v5", "ver 5.0" ) // VECTOR GRAPHIC EXECUTIVE 5.0
	ROMX_LOAD( "vg-zcb50.bin",   0x0000, 0x1000, CRC(22d692ce) SHA1(cbb21b0acc98983bf5febd59ff67615d71596e36), ROM_BIOS(2))

	// bios 1,2 need these (pair of 2716) - rom names not known
	ROM_REGION( 0x1000, "chargen", ROMREGION_ERASEFF )
	ROM_LOAD( "chargen1.bin", 0x0000, 0x0800, NO_DUMP )
	ROM_LOAD( "chargen2.bin", 0x0800, 0x0800, NO_DUMP )
ROM_END

} // anonymous namespace

/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY            FULLNAME    FLAGS
COMP( 1979, vector3, 0,      0,      vector3, vector3, vector3_state, empty_init, "Vector Graphic", "Vector 3", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



vector4.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Eric Anderson
/***************************************************************************

Vector Graphic Vector 4. Vector Graphic, Inc was a Californian company that
made a personal computers before the IBM PC disrupted the industry. The
Vector 4 was an office computer with word processing, spreadsheeting,
database, and eventually networked drives.

The ROM boots from the second floppy. "Winchester" boots from the hard drive
(currently unsupported), which is the first device when in use.

On power on the system uses a Z80B, but it also contains an 8088-2 for limited
IBM compatibility. Only one processor is running at a time and reading from a
port can toggle the active processor. They share memory with each other and the
video subsystem. The active processor and the video subsystem alternate access to
the RAM. The BDOS for CP/M makes use of the 8088, so both processors are
necessary for any of the OSes to boot: CP/M, CP/M 86, MS-DOS.

The system had three S-100 slots, with the floppy controller using one. It was a
modified bus since it provided regulated power. The system had 128K RAM and
could be extended to 256K. The keyboard plugged in to a 6P6C jack. There were
female ports for two DB-25 RS-232, a 36-pin micro ribbon (aka, telco,
centronics) parallel, a 50-pin micro ribbon parallel, and a DE-9 RGBI/CGA
monitor.

The 7100-0001 User's Manual provides some overview specifications starting on
page 171 (X A-1) including ports and devices. But it provides few details. The
7200-0001 Technical Information provides thorough descriptions and schematics.

"Executive" is the name of the boot ROM, not to be confused with a model name.
The motherboard is called the Single Board Computer (SBC) and includes all
functionality except for the disk controller.

https://archive.org/details/7200-0001-vector-4-technical-information-sep-82
https://www.bitsavers.org/pdf/vectorGraphic/vector_4/7100-0001_Vector_4_Users_Manual_Feb83.pdf

TODO:
- S-100 interrupts and ready
- Qume (50 pin) parallel port
- WAIT CPU states
- CPM-86 and MS-DOS 2.0 don't boot

****************************************************************************/

#include "emu.h"

#include "sbcvideo.h"
#include "v4_kbd.h"

#include "bus/centronics/ctronics.h"
#include "bus/rs232/rs232.h"
#include "bus/s100/s100.h"
#include "bus/s100/vectordualmode.h"
#include "cpu/i86/i86.h"
#include "cpu/z80/z80.h"
#include "machine/bankdev.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/ram.h"
#include "machine/pit8253.h"
#include "sound/sn76496.h"

#include "speaker.h"

namespace {

class vector4_state : public driver_device
{
public:
	vector4_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_8088cpu(*this, "8088cpu")
		, m_rambanks(*this, "rambank%u", 0U)
		, m_256k(false)
		, m_ram(*this, RAM_TAG)
		, m_romenbl(*this, "romenbl")
		, m_sbc_video(*this, "video")
		, m_s100(*this, "s100")
		, m_uart0(*this, "uart0")
		, m_centronics(*this, "centprtr")
		, m_ppi_pc(0)
	{ }

	void vector4(machine_config &config);

private:
	void vector4_io(address_map &map) ATTR_COLD;
	void vector4_z80mem(address_map &map) ATTR_COLD;
	void vector4_8088mem(address_map &map) ATTR_COLD;
	void spr_w(uint8_t data);
	uint8_t msc_r();
	void msc_w(uint8_t data) { machine_reset(); }
	void addrmap_w(offs_t offset, uint8_t data);
	void ppi_pb_w(uint8_t data) { m_centronics->write_strobe(BIT(data, 0) ? ASSERT_LINE : CLEAR_LINE); }
	void centronics_busy_w(int state) { if (state) m_ppi_pc |= 1; else m_ppi_pc &= ~1; }
	int ppi_pc_r() { return m_ppi_pc; }
	uint8_t s100_r(offs_t offset) { return m_s100->sinp_r(offset+0x20); }
	void s100_w(offs_t offset, uint8_t data) { m_s100->sout_w(offset+0x20, data); }
	void machine_start() override ATTR_COLD;
	void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<cpu_device> m_8088cpu;
	required_memory_bank_array<32> m_rambanks;
	bool m_256k;
	required_device<ram_device> m_ram;
	memory_view m_romenbl;
	required_device<vector_sbc_video_device> m_sbc_video;
	required_device<s100_bus_device> m_s100;
	required_device<i8251_device> m_uart0;
	required_device<centronics_device> m_centronics;
	uint8_t m_ppi_pc;
};


void vector4_state::vector4_z80mem(address_map &map)
{
	map.unmap_value_high();
	for (int bank = 0; bank < (1<<5); bank++)
		map(bank << 11, ((bank+1) << 11)-1).bankrw(m_rambanks[bank]);
	// 7200-0001 page 209 (VI A-6) B6
	// ROM mapping only applies to the z80 and does not use address mapping.
	map(0x0000, 0x0fff).view(m_romenbl);
	m_romenbl[0](0x0000, 0x0fff).rom().region("maincpu", 0);
}

void vector4_state::vector4_8088mem(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0x3ffff);
}

void vector4_state::vector4_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x01).mirror(0xff00).rw(m_uart0, FUNC(i8251_device::read), FUNC(i8251_device::write)); // keyboard
	map(0x02, 0x03).mirror(0xff00).w(FUNC(vector4_state::spr_w)); // subsystem port register
	map(0x04, 0x05).mirror(0xff00).rw("uart1", FUNC(i8251_device::read), FUNC(i8251_device::write)); // modem
	map(0x06, 0x07).mirror(0xff00).rw("uart2", FUNC(i8251_device::read), FUNC(i8251_device::write)); // serial printer
	map(0x08, 0x0b).mirror(0xff00).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write)); // parallel printer
	map(0x0c, 0x0d).mirror(0xff00).rw(FUNC(vector4_state::msc_r), FUNC(vector4_state::msc_w)); // select Z80/8088-2
	map(0x0e, 0x0e).mirror(0xff00).rw("video:crtc", FUNC(c6545_1_device::status_r), FUNC(c6545_1_device::address_w)); // video controller
	map(0x0f, 0x0f).mirror(0xff00).rw("video:crtc", FUNC(c6545_1_device::register_r), FUNC(c6545_1_device::register_w));
	map(0x10, 0x13).mirror(0xff00).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write)); // baud generator and timer
	map(0x16, 0x17).select(0xff00).w(FUNC(vector4_state::addrmap_w)); // RAM address map
	map(0x18, 0x19).mirror(0xff00).w("sn", FUNC(sn76489_device::write)); // tone generator
	map(0x1c, 0x1f).mirror(0xff00).w(m_sbc_video, FUNC(vector_sbc_video_device::res320_mapping_ram_w)); // resolution 320 mapping RAM
	map(0x20, 0xff).mirror(0xff00).rw(FUNC(vector4_state::s100_r), FUNC(vector4_state::s100_w));
}

static void vector4_s100_devices(device_slot_interface &device)
{
	device.option_add("dualmodedisk", S100_VECTOR_DUALMODE);
}

static INPUT_PORTS_START( vector4 )
INPUT_PORTS_END

void vector4_state::vector4(machine_config &config)
{
	const XTAL _32m(32'640'000);
	const XTAL _2m = _32m/16;

	/* processors */
	// Manual says 5.1 MHz. To do so, schematic shows (A1) it is driven by the
	// 32.64 MHz xtal, 1/16 of the cycles are skipped, and it is divided by 6.
	Z80(config, m_maincpu, 5'100'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &vector4_state::vector4_z80mem);
	m_maincpu->set_addrmap(AS_IO, &vector4_state::vector4_io);

	I8088(config, m_8088cpu, 5'100'000);
	m_8088cpu->set_addrmap(AS_PROGRAM, &vector4_state::vector4_8088mem);
	m_8088cpu->set_addrmap(AS_IO, &vector4_state::vector4_io);

	RAM(config, m_ram).set_default_size("128K").set_extra_options("256K");

	/* video hardware */
	SBC_VIDEO(config, m_sbc_video, _32m);
	m_sbc_video->set_buffer(m_ram);
	m_sbc_video->set_chrroml("chargenl");
	m_sbc_video->set_chrromr("chargenr");

	/* i/o */
	XTAL _2mclk(2'000'000); // 7200-0001 page 210 (VI A-7) D13

	S100_BUS(config, m_s100, _2mclk);
	S100_SLOT(config, "s100:1", vector4_s100_devices, "dualmodedisk");
	S100_SLOT(config, "s100:2", vector4_s100_devices, nullptr);
	S100_SLOT(config, "s100:3", vector4_s100_devices, nullptr);

	pit8253_device &pit(PIT8253(config, "pit", 0));
	// 7200-0001 page 210 D7, 208 (VI A-5) A1
	pit.set_clk<0>(_2mclk);
	pit.set_clk<1>(_2mclk);
	pit.set_clk<2>(_2mclk);
	// 7200-0001 page 107 (II 5-16)
	pit.out_handler<0>().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	pit.out_handler<0>().append_inputline(m_8088cpu, INPUT_LINE_IRQ0);

	// 7200-0001 page 210 D13, D1
	vector4_keyboard_device &v4kbd(VECTOR4_KEYBOARD(config, "rs232keyboard", 0));
	v4kbd.txd_handler().set(m_uart0, FUNC(i8251_device::write_rxd));
	clock_device &keyboard_clock(CLOCK(config, "keyboard_clock", _2mclk/26/16));
	keyboard_clock.signal_handler().set(m_uart0, FUNC(i8251_device::write_txc));
	keyboard_clock.signal_handler().append(m_uart0, FUNC(i8251_device::write_rxc));
	I8251(config, m_uart0, 0);
	m_uart0->txd_handler().set(v4kbd, FUNC(vector4_keyboard_device::write_rxd));

	// D3
	i8251_device &uart1(I8251(config, "uart1", 0));
	rs232_port_device &rs232com(RS232_PORT(config, "rs232com", default_rs232_devices, nullptr));
	pit.out_handler<1>().set(uart1, FUNC(i8251_device::write_txc));
	pit.out_handler<1>().append(uart1, FUNC(i8251_device::write_rxc));
	uart1.txd_handler().set(rs232com, FUNC(rs232_port_device::write_txd));
	uart1.dtr_handler().set(rs232com, FUNC(rs232_port_device::write_dtr));
	uart1.rts_handler().set(rs232com, FUNC(rs232_port_device::write_rts));
	rs232com.rxd_handler().set(uart1, FUNC(i8251_device::write_rxd));
	rs232com.dsr_handler().set(uart1, FUNC(i8251_device::write_dsr));
	rs232com.cts_handler().set(uart1, FUNC(i8251_device::write_cts));

	// D2
	i8251_device &uart2(I8251(config, "uart2", 0));
	rs232_port_device &rs232prtr(RS232_PORT(config, "rs232prtr", default_rs232_devices, nullptr));
	pit.out_handler<2>().set(uart2, FUNC(i8251_device::write_txc));
	pit.out_handler<2>().append(uart2, FUNC(i8251_device::write_rxc));
	uart2.txd_handler().set(rs232prtr, FUNC(rs232_port_device::write_txd));
	uart2.dtr_handler().set(rs232prtr, FUNC(rs232_port_device::write_dtr));
	uart2.rts_handler().set(rs232prtr, FUNC(rs232_port_device::write_rts));
	rs232prtr.rxd_handler().set(uart2, FUNC(i8251_device::write_rxd));
	rs232prtr.dsr_handler().set(uart2, FUNC(i8251_device::write_dsr));
	rs232prtr.cts_handler().set(uart2, FUNC(i8251_device::write_cts));

	// 7200-0001 page 110 (II 5-19), 210 D8
	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	CENTRONICS(config, m_centronics, centronics_devices, nullptr);
	m_centronics->set_output_latch(cent_data_out);
	m_centronics->busy_handler().set(FUNC(vector4_state::centronics_busy_w));

	i8255_device &ppi(I8255A(config, "ppi"));
	ppi.out_pa_callback().set(cent_data_out, FUNC(output_latch_device::write));
	ppi.out_pb_callback().set(FUNC(vector4_state::ppi_pb_w));
	ppi.in_pc_callback().set(FUNC(vector4_state::ppi_pc_r));

	SPEAKER(config, "mono").front_center();
	sn76489_device &sn(SN76489(config, "sn", _2m));
	sn.add_route(ALL_OUTPUTS, "mono", 1.0);
}

void vector4_state::machine_start()
{
	m_256k = (m_ram->size() == 256 * 1024);
	m_8088cpu->space(AS_PROGRAM).install_ram(0, m_ram->mask(), m_ram->size() & 0x20000, m_ram->pointer());
	for (int bank = 0; bank < (1<<5); bank++)
		m_rambanks[bank]->configure_entries(0, 1<<7, m_ram->pointer(), 1<<11);

	save_item(NAME(m_ppi_pc));

	// Missing from schematic, but jumper wire present on the board.
	m_uart0->write_cts(0);
}

void vector4_state::machine_reset()
{
	// 7200-0001 page 39 (II 1-10), page 210 D11
	spr_w(0);
	m_8088cpu->suspend(SUSPEND_REASON_HALT, true);
	m_maincpu->resume(SUSPEND_REASON_HALT);
}

/* Subsystem Port Register */
void vector4_state::spr_w(uint8_t data)
{
	// 7200-0001 page 81 (II 3-18)
	if (BIT(data, 0))
		m_romenbl.disable();
	else
		m_romenbl.select(0);
	m_sbc_video->spr_w(data);
}

/* Microprocessor Switching Control */
uint8_t vector4_state::msc_r()
{
	// 7200-0001 page 40 (II 1-11), 208 A3
	if (m_maincpu->suspended(SUSPEND_REASON_HALT)) {
		m_8088cpu->suspend(SUSPEND_REASON_HALT, false);
		m_maincpu->resume(SUSPEND_REASON_HALT);
	} else {
		m_maincpu->suspend(SUSPEND_REASON_HALT, false);
		m_8088cpu->resume(SUSPEND_REASON_HALT);
	}
	return 0xff;
}

/* Address mapping RAM Subsystem */
void vector4_state::addrmap_w(offs_t offset, uint8_t data)
{
	// 7200-0001 page 50 (II 2-7), page 208 B1
	// m_256k is for jumper area B. 7200-0001 page 170 (IV 2-1), page 209 coord A8
	m_rambanks[BIT(offset, 11, 5)]->set_entry(data & (m_256k ? 0x7f : 0x3f));
}

/* ROM definition */
ROM_START( vector4 )
	ROM_REGION( 0x1000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v11", "SBC ver 1.1" ) // VECTOR 4 SBC EXECUTIVE 1.1
	ROMX_LOAD( "sbcv11.bin", 0x0000, 0x1000, CRC(56c80656) SHA1(a381bdbb6cdee0ac1dc8b1c1359361a19ba6fe46), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v111", "SBC ver 1.11" ) // VECTOR 4 SBC EXECUTIVE 1.11
	ROMX_LOAD( "u35_sbc_v111_4f11.bin", 0x0000, 0x1000, CRC(fcfd42c6) SHA1(11cd5cf0c3f2d2a30864b8a4b4be51ade03d02ea), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "v200", "SBC ver 2.00" ) // VECTOR GRAPHIC V4/40 [F] SBC EXECUTIVE - REVISION 2.00 (AA)
	ROMX_LOAD( "sbcexecf.bin", 0x0000, 0x1000, CRC(738e10b5) SHA1(abce22abe9bac6241b1996d078647fe36b638769), ROM_BIOS(2))

	ROM_REGION( 0x1000, "chargenl", ROMREGION_ERASEFF )
	ROM_LOAD( "cgst60l.bin", 0x0000, 0x1000, CRC(e55a404c) SHA1(4dafd6f588c42081212928d3c4448f10dd461a7a))
	ROM_REGION( 0x1000, "chargenr", ROMREGION_ERASEFF )
	ROM_LOAD( "cgst60r.bin", 0x0000, 0x1000, CRC(201d783c) SHA1(7c2b8988c27b5fa435d31c9b7d4d9ed176c9b8a3))
ROM_END

} // anonymous namespace

/* Driver */

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT          COMPANY            FULLNAME    FLAGS
COMP( 1982, vector4, 0,      0,      vector4, vector4, vector4_state, empty_init,   "Vector Graphic", "Vector 4", MACHINE_NODEVICE_PRINTER | MACHINE_IMPERFECT_TIMING | MACHINE_SUPPORTS_SAVE )



vectrex.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Mathis Rosenhauer
/*****************************************************************

Vectrex, conceived by Smith Engineering, manufactured by GCE.
GCE was acquired by Milton Bradley half a year later.

Mathis Rosenhauer
Christopher Salomon (technical advice)
Bruce Tomlin (hardware info)

*****************************************************************/

#include "emu.h"
#include "vectrex.h"

#include "cpu/m6809/m6809.h"
#include "machine/6522via.h"
#include "machine/nvram.h"
#include "sound/ay8910.h"
#include "video/vector.h"

#include "softlist_dev.h"
#include "speaker.h"


void vectrex_state::vectrex_map(address_map &map)
{
	map(0x0000, 0x7fff).noprw(); // cart area, handled at machine_start
	map(0xc800, 0xcbff).ram().mirror(0x0400).share("gce_vectorram");
	map(0xd000, 0xd7ff).rw(FUNC(vectrex_state::via_r), FUNC(vectrex_state::via_w));
	map(0xe000, 0xffff).rom().region("maincpu", 0);
}

static INPUT_PORTS_START(vectrex)
	PORT_START("CONTR1X")
	PORT_BIT(0xff, 0x80, IPT_AD_STICK_X) PORT_MINMAX(0,0xff) PORT_SENSITIVITY(50) PORT_KEYDELTA(30)

	PORT_START("CONTR1Y")
	PORT_BIT(0xff, 0x80, IPT_AD_STICK_Y) PORT_MINMAX(0,0xff) PORT_SENSITIVITY(50) PORT_KEYDELTA(30) PORT_REVERSE

	PORT_START("CONTR2X")
	PORT_BIT(0xff, 0x80, IPT_AD_STICK_X) PORT_MINMAX(0,0xff) PORT_SENSITIVITY(50) PORT_KEYDELTA(30) PORT_PLAYER(2)

	PORT_START("CONTR2Y")
	PORT_BIT(0xff, 0x80, IPT_AD_STICK_Y) PORT_MINMAX(0,0xff) PORT_SENSITIVITY(50) PORT_KEYDELTA(30) PORT_REVERSE PORT_PLAYER(2)

	PORT_START("BUTTONS")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_PLAYER(1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_PLAYER(1)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON3) PORT_PLAYER(1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_BUTTON4) PORT_PLAYER(1)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_PLAYER(2)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_PLAYER(2)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_BUTTON3) PORT_PLAYER(2)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_BUTTON4) PORT_PLAYER(2)

	PORT_START("3DCONF")
	PORT_CONFNAME(0x01, 0x00, "3D Imager")
	PORT_CONFSETTING(0x00, DEF_STR(Off))
	PORT_CONFSETTING(0x01, DEF_STR(On))
	PORT_CONFNAME(0x02, 0x00, "Separate images")
	PORT_CONFSETTING(0x00, DEF_STR(No))
	PORT_CONFSETTING(0x02, DEF_STR(Yes))
	PORT_CONFNAME(0x1c, 0x10, "Left eye")
	PORT_CONFSETTING(0x00, "Black")
	PORT_CONFSETTING(0x04, "Red")
	PORT_CONFSETTING(0x08, "Green")
	PORT_CONFSETTING(0x0c, "Blue")
	PORT_CONFSETTING(0x10, "Color")
	PORT_CONFNAME(0xe0, 0x80, "Right eye")
	PORT_CONFSETTING(0x00, "Black")
	PORT_CONFSETTING(0x20, "Red")
	PORT_CONFSETTING(0x40, "Green")
	PORT_CONFSETTING(0x60, "Blue")
	PORT_CONFSETTING(0x80, "Color")

	PORT_START("LPENCONF")
	PORT_CONFNAME(0x03, 0x00, "Lightpen")
	PORT_CONFSETTING(0x00, DEF_STR(Off))
	PORT_CONFSETTING(0x01, "left port")
	PORT_CONFSETTING(0x02, "right port")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON5) PORT_CODE(MOUSECODE_BUTTON1)

	PORT_START("LPENY")
	PORT_BIT(0xff, 0x80, IPT_LIGHTGUN_X)  PORT_CROSSHAIR(Y, 1, 0, 0) PORT_MINMAX(0,0xff) PORT_SENSITIVITY(35) PORT_KEYDELTA(1) PORT_PLAYER(1)

	PORT_START("LPENX")
	PORT_BIT(0xff, 0x80, IPT_LIGHTGUN_Y)  PORT_CROSSHAIR(X, 1, 0, 0) PORT_MINMAX(0,0xff) PORT_SENSITIVITY(35) PORT_KEYDELTA(1) PORT_REVERSE PORT_PLAYER(1)

INPUT_PORTS_END

void vectrex_base_state::vectrex_cart(device_slot_interface &device)
{
	device.option_add_internal("vec_rom",    VECTREX_ROM_STD);
	device.option_add_internal("vec_rom64k", VECTREX_ROM_64K);
	device.option_add_internal("vec_sram",   VECTREX_ROM_SRAM);
}

void vectrex_base_state::vectrex_base(machine_config &config)
{
	MC6809(config, m_maincpu, 6_MHz_XTAL); // 68A09

	/* video hardware */
	VECTOR(config, m_vector, 0);
	SCREEN(config, m_screen, SCREEN_TYPE_VECTOR);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(400, 300);
	m_screen->set_visarea(0, 399, 0, 299);
	m_screen->set_screen_update(FUNC(vectrex_base_state::screen_update));

	/* sound hardware */
	SPEAKER(config, "speaker").front_center();
	MC1408(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.25); // mc1408.ic301 (also used for vector generation)

	AY8912(config, m_ay8912, 6_MHz_XTAL / 4);
	m_ay8912->port_a_read_callback().set_ioport("BUTTONS");
	m_ay8912->port_a_write_callback().set(FUNC(vectrex_base_state::psg_port_w));
	m_ay8912->add_route(ALL_OUTPUTS, "speaker", 0.2);

	/* via */
	MOS6522(config, m_via6522_0, 6_MHz_XTAL / 4);
	m_via6522_0->readpa_handler().set(FUNC(vectrex_base_state::via_pa_r));
	m_via6522_0->readpb_handler().set(FUNC(vectrex_base_state::via_pb_r));
	m_via6522_0->writepa_handler().set(FUNC(vectrex_base_state::via_pa_w));
	m_via6522_0->writepb_handler().set(FUNC(vectrex_base_state::via_pb_w));
	m_via6522_0->ca2_handler().set(FUNC(vectrex_base_state::via_ca2_w));
	m_via6522_0->cb2_handler().set(FUNC(vectrex_base_state::via_cb2_w));
	m_via6522_0->irq_handler().set(FUNC(vectrex_base_state::via_irq));
}

void vectrex_state::vectrex(machine_config &config)
{
	vectrex_base(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &vectrex_state::vectrex_map);

	vectrex_cart_slot_device &slot(VECTREX_CART_SLOT(config, "cartslot", 0));
	vectrex_cart(slot);

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("vectrex");
}

ROM_START(vectrex)
	ROM_REGION(0x2000,"maincpu", 0)
	ROM_SYSTEM_BIOS(0, "bios0", "exec rom")
	ROMX_LOAD("exec_rom.bin", 0x0000, 0x2000, CRC(ba13fb57) SHA1(65d07426b520ddd3115d40f255511e0fd2e20ae7), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "bios1", "exec rom intl 284001-1")
	ROMX_LOAD("exec_rom_intl_284001-1.bin", 0x0000, 0x2000, CRC(6d2bd167) SHA1(77a220d5d98846b606dff608f7b5d00183ec3bab), ROM_BIOS(1) )

//  The following fastboots are listed here for reference and documentation
//  ROM_SYSTEM_BIOS(2, "bios2", "us-fastboot hack")
//  ROMX_LOAD("us-fastboot.bin", 0x0000, 0x2000, CRC(a6e4dac4) SHA1(e0900be6d6858b985fd7f0999d864b2fceaf01a1), ROM_BIOS(2) )
//  ROM_SYSTEM_BIOS(3, "bios3", "intl-fastboot hack")
//  ROMX_LOAD("intl-fastboot.bin", 0x0000, 0x2000, CRC(71dcf0f4) SHA1(2a257c5111f5cee841bd14acaa9df6496aaf3d8b), ROM_BIOS(3) )

ROM_END


/*****************************************************************

  RA+A Spectrum I+

  The Spectrum I+ was a modified Vectrex. It had a 32K ROM cart
  and 2K additional battery backed RAM (0x8000 - 0x87ff). PB6
  was used to signal inserted coins to the VIA. The unit was
  controlled by 8 buttons (2x4 buttons of controller 1 and 2).
  Each button had a LED which were mapped to 0xa000.
  The srvice mode can be accessed by pressing button
  8 during startup. As soon as all LEDs light up,
  press 2 and 3 without releasing 8. Then release 8 and
  after that 2 and 3. You can leave the screen where you enter
  ads by pressing 8 several times.

  Character matrix is:

  btn| 1  2  3  4  5  6  7  8
  ---+------------------------
  1  | 0  1  2  3  4  5  6  7
  2  | 8  9  A  B  C  D  E  F
  3  | G  H  I  J  K  L  M  N
  4  | O  P  Q  R  S  T  U  V
  5  | W  X  Y  Z  sp !  "  #
  6  | $  %  &  '  (  )  *  +
  7  | ,  -  _  /  :  ;  ?  =
  8  |bs ret up dn l  r hom esc

  The first page of ads is shown with the "result" of the
  test. Remaining pages are shown in attract mode. If no extra
  ram is present, the word COLOR is scrolled in big vector!
  letters in attract mode.

*****************************************************************/

void raaspec_state::raaspec_map(address_map &map)
{
	map(0x0000, 0x7fff).rom();
	map(0x8000, 0x87ff).ram().share("nvram");
	map(0xa000, 0xa000).w(FUNC(raaspec_state::raaspec_led_w));
	map(0xc800, 0xcbff).ram().mirror(0x0400).share("gce_vectorram");
	map(0xd000, 0xd7ff).rw(FUNC(raaspec_state::via_r), FUNC(raaspec_state::via_w));
	map(0xe000, 0xffff).rom();
}

static INPUT_PORTS_START(raaspec)
	PORT_START("LPENCONF")
	PORT_START("LPENY")
	PORT_START("LPENX")
	PORT_START("3DCONF")
	PORT_START("BUTTONS")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_BUTTON4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_BUTTON5)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_BUTTON6)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_BUTTON7)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_BUTTON8)
	PORT_START("COIN")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_COIN1)
INPUT_PORTS_END


void raaspec_state::raaspec(machine_config &config)
{
	vectrex_base(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &raaspec_state::raaspec_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	m_via6522_0->readpb_handler().set(FUNC(raaspec_state::s1_via_pb_r));
}

ROM_START(raaspec)
	ROM_REGION(0x10000,"maincpu", 0)
	ROM_LOAD("spectrum.bin", 0x0000, 0x8000, CRC(20af7f3f) SHA1(7ce85db8dd32687ad7629631ae113820371faf7c))
	ROM_LOAD("exec_rom.bin", 0xe000, 0x2000, CRC(ba13fb57) SHA1(65d07426b520ddd3115d40f255511e0fd2e20ae7))
ROM_END

/***************************************************************************

  Game driver(s)

***************************************************************************/

//   YEAR  NAME       PARENT    COMPAT   MACHINE   INPUT     STATE          INIT        MONITOR  COMPANY                         FULLNAME
CONS( 1982, vectrex,  0,        0,       vectrex,  vectrex,  vectrex_state, empty_init,          "General Consumer Electronics", "Vectrex" , ROT270)

GAME( 1984, raaspec,  0,                 raaspec,  raaspec,  raaspec_state, empty_init, ROT270,  "Roy Abel & Associates",        "Spectrum I+", MACHINE_NOT_WORKING ) //TODO: button labels & timings, a mandatory artwork too?



vectrix.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************************

2017-11-02 Skeleton

Vectrix Graphics Processor. The VX384 was the main model, with 384K of RAM, and used an analog monitor.
 The VX128 was the cheaper model with 128K of RAM and less colours. It used a TTL-level monitor.
 Don't know which one this is, but VX384 is assumed.

It replaced your serial or centronics printer, which then plugged into the unit instead. The unit could be considered as a
terminal which could decode simple commands into complex graphics.


************************************************************************************************************************************/

#include "emu.h"
#include "cpu/i86/i86.h"
#include "machine/i8251.h"


namespace {

class vectrix_state : public driver_device
{
public:
	vectrix_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		//      , m_maincpu(*this, "maincpu")
	{ }

	void vectrix(machine_config &config);

private:
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	//  required_device<cpu_device> m_maincpu;
};

void vectrix_state::mem_map(address_map &map)
{
	map(0x00000, 0x07fff).ram();
	map(0x0c000, 0x0ffff).rom().region("roms", 0);
	map(0xfc000, 0xfffff).rom().region("roms", 0);
}

void vectrix_state::io_map(address_map &map)
{
	map(0x3000, 0x3001).rw("uart1", FUNC(i8251_device::read), FUNC(i8251_device::write));
}

static INPUT_PORTS_START( vectrix )
INPUT_PORTS_END

void vectrix_state::vectrix(machine_config &config)
{
	i8088_cpu_device &maincpu(I8088(config, "maincpu", XTAL(14'318'181)/3));  // no idea of clock
	maincpu.set_addrmap(AS_PROGRAM, &vectrix_state::mem_map);
	maincpu.set_addrmap(AS_IO, &vectrix_state::io_map);

	I8251(config, "uart1", 0);
}

ROM_START( vectrix )
	ROM_REGION( 0x4000, "roms", 0 )
	ROM_LOAD( "vectrixl.bin", 0x0000, 0x2000, CRC(10b93e38) SHA1(0b1a23d384bfde4cd27c482f667eedd94f8f2406) )
	ROM_LOAD( "vectrixr.bin", 0x2000, 0x2000, CRC(33f9b06b) SHA1(6a1dffe5c2c0254824a8dddb8543f86d9ad8f173) )
ROM_END

} // anonymous namespace


COMP( 1983, vectrix, 0, 0, vectrix, vectrix, vectrix_state, empty_init, "Vectrix", "VX384 Graphics Processor Terminal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



vegaplus.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: David Haywood

#include "emu.h"

#include "cpu/arm7/arm7.h"

#include "screen.h"
#include "speaker.h"


namespace {

class vegaplus_state : public driver_device
{
public:
	vegaplus_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
	{ }

	void vegaplus(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void arm_map(address_map &map) ATTR_COLD;
};

uint32_t vegaplus_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void vegaplus_state::machine_start()
{
}

void vegaplus_state::machine_reset()
{
	//m_maincpu->set_state_int(arm7_cpu_device::ARM7_R15, 0x08000000);
}

static INPUT_PORTS_START( vegaplus )
INPUT_PORTS_END


void vegaplus_state::arm_map(address_map &map)
{
}

void vegaplus_state::vegaplus(machine_config &config)
{
	ARM9(config, m_maincpu, 454000000); // Freescale MCIMX233DJM4C
	m_maincpu->set_addrmap(AS_PROGRAM, &vegaplus_state::arm_map);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320, 262);
	m_screen->set_visarea(0, 320-1, 0, 240-1);
	m_screen->set_screen_update(FUNC(vegaplus_state::screen_update));

	SPEAKER(config, "speaker", 2).front();
}

ROM_START( vegaplus )
	ROM_REGION( 0x4000000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "vegaplus.bin", 0x000000, 0x4000000, CRC(d557819c) SHA1(18160118e1b5538f2341213c0ded14317be6f0b3) )
ROM_END

} // anonymous namespace

CONS( 2018, vegaplus,      0,              0,      vegaplus, vegaplus, vegaplus_state, empty_init, "Retro Computers Ltd", "ZX Spectrum Vega+", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



vg5k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

    Philips VG-5000mu

    Driver by Sandro Ronco with help from Daniel Coulom

    05/2010 (Sandro Ronco)
     - EF9345 video controller
     - keyboard input ports
    05/2009 Skeleton driver.

    Known issues:
     - Support the K7 filetype for ease of usage, but as read only.

    Information ( see the very informative http://vg5k.free.fr/ ):
     - Variants: Radiola VG5000 and Schneider VG5000
     - CPU: Zilog Z80 running at 4MHz
     - ROM: 18KB (16 KB BASIC + 2 KB charset )
     - RAM: 24 KB
     - Video: SGS Thomson EF9345 processor
            - Text mode: 25 rows x 40 columns
            - Character matrix: 8 x 10
            - ASCII characters set, 128 graphics mode characters, 192 user characters.
            - Graphics mode: not available within basic, only semi graphic is available.
            - Colors: 8
     - Sound: Synthesizer, 4 Octaves
     - Keyboard: 63 keys AZERTY, Caps Lock, CTRL key to access 33 BASIC instructions
     - I/O: Tape recorder connector (1200/2400 bauds), SCART connector to TV (RGB),
       External PSU (VU0022) connector, Bus connector (2x25 pins)
     - There are 2 versions of the VG5000 ROM, one with Basic v1.0,
       contained in two 8 KB ROMs, and one with Basic 1.1, contained in
       a single 16 KB ROM.
     - RAM: 24 KB (3 x 8 KB) type SRAM D4168C, more precisely:
         2 x 8 KB, used by the system
         1 x 8 KB, used by the video processor
     - Memory Map:
         $0000 - $3fff  BASIC + monitor
         $4000 - $47cf  Screen
         $47d0 - $7fff  reserved area for BASIC, variables, etc
         $8000 - $bfff  Memory Expansion 16K or ROM cart
         $c000 - $ffff  Memory Expansion 32K or ROM cart
     - This computer was NOT MSX-compatible!


****************************************************************************/


#include "emu.h"

#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "imagedev/printer.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "sound/dac.h"
#include "video/ef9345.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/vg5k_cas.h"


namespace {

class vg5k_state : public driver_device
{
public:
	vg5k_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ef9345(*this, "ef9345")
		, m_dac(*this, "dac")
		, m_printer(*this, "printer")
		, m_cassette(*this, "cassette")
		, m_ram(*this, RAM_TAG)
	{ }

	void vg5k(machine_config &config);

	void init_vg5k();

	DECLARE_INPUT_CHANGED_MEMBER(delta_button);

private:
	required_device<z80_device> m_maincpu;
	required_device<ef9345_device> m_ef9345;
	required_device<dac_bit_interface> m_dac;
	required_device<printer_image_device> m_printer;
	required_device<cassette_image_device> m_cassette;
	required_device<ram_device> m_ram;

	offs_t m_ef9345_offset = 0;
	uint8_t m_printer_latch = 0;
	uint8_t m_printer_signal = 0;
	emu_timer *m_z80_irq_clear_timer = nullptr;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void z80_m1_w(uint8_t data);
	uint8_t printer_state_r();
	void printer_state_w(uint8_t data);
	void printer_data_w(uint8_t data);
	void ef9345_offset_w(uint8_t data);
	uint8_t ef9345_io_r();
	void ef9345_io_w(uint8_t data);
	uint8_t cassette_r();
	void cassette_w(uint8_t data);
	TIMER_CALLBACK_MEMBER(z80_irq_clear);
	TIMER_DEVICE_CALLBACK_MEMBER(z80_irq);
	TIMER_DEVICE_CALLBACK_MEMBER(vg5k_scanline);
	void vg5k_io(address_map &map) ATTR_COLD;
	void vg5k_mem(address_map &map) ATTR_COLD;
};

void vg5k_state::z80_m1_w(uint8_t data)
{
	// Leverage the refresh callback of the Z80 emulator to pretend
	// the second T state of the M1 cycle didn't happen.
	// This simulates the WAIT line asserted at that moment, as
	// the current implementation of the Z80 doesn't handle the WAIT
	// line at that moment.
	m_maincpu->adjust_icount(-1);
}

uint8_t vg5k_state::printer_state_r()
{
	return (m_printer->is_ready() ? 0x00 : 0xff);
}

void vg5k_state::printer_state_w(uint8_t data)
{
	// Character is emitted on a rising edge.
	if (!BIT(m_printer_signal, 0) && BIT(data, 0)) {
		m_printer->output(m_printer_latch);
	}
	m_printer_signal = data;
}


void vg5k_state::printer_data_w(uint8_t data)
{
	m_printer_latch = data;
}


void vg5k_state::ef9345_offset_w(uint8_t data)
{
	m_ef9345_offset = data;
}


uint8_t vg5k_state::ef9345_io_r()
{
	return m_ef9345->data_r(m_ef9345_offset);
}


void vg5k_state::ef9345_io_w(uint8_t data)
{
	m_ef9345->data_w(m_ef9345_offset, data);
}


uint8_t vg5k_state::cassette_r()
{
	double level = m_cassette->input();

	return (level > 0.03) ? 0xff : 0x00;
}


void vg5k_state::cassette_w(uint8_t data)
{
	m_dac->write(BIT(data, 3));
	m_cassette->change_state(BIT(data, 1) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED , CASSETTE_MASK_MOTOR);

	if (BIT(data, 1)) {
		if (BIT(data, 0)) {
			m_cassette->output(+1);
		} else {
			m_cassette->output(-1);
		}
	} else {
		m_cassette->output(0);
	}
}


void vg5k_state::vg5k_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).rom();
	map(0x4000, 0x7fff).ram();
	map(0x8000, 0xffff).noprw(); /* messram expansion memory */
}

void vg5k_state::vg5k_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);

	/* joystick */
	map(0x07, 0x07).portr("JOY0");
	map(0x08, 0x08).portr("JOY1");

	/* printer */
	map(0x10, 0x10).rw(FUNC(vg5k_state::printer_state_r), FUNC(vg5k_state::printer_state_w));
	map(0x11, 0x11).w(FUNC(vg5k_state::printer_data_w));

	/* keyboard */
	map(0x80, 0x80).portr("ROW1");
	map(0x81, 0x81).portr("ROW2");
	map(0x82, 0x82).portr("ROW3");
	map(0x83, 0x83).portr("ROW4");
	map(0x84, 0x84).portr("ROW5");
	map(0x85, 0x85).portr("ROW6");
	map(0x86, 0x86).portr("ROW7");
	map(0x87, 0x87).portr("ROW8");

	/* EF9345 */
	map(0x8f, 0x8f).w(FUNC(vg5k_state::ef9345_offset_w));
	map(0xcf, 0xcf).rw(FUNC(vg5k_state::ef9345_io_r), FUNC(vg5k_state::ef9345_io_w));

	/* cassette */
	map(0xaf, 0xaf).rw(FUNC(vg5k_state::cassette_r), FUNC(vg5k_state::cassette_w));
}

/* Input ports */
static INPUT_PORTS_START( vg5k )
	PORT_START("ROW1")
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_HOME)                                   PORT_NAME("LIST EFFE")      PORT_CHAR(UCHAR_MAMEKEY(F1))
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)       PORT_NAME("MAJ")            PORT_CHAR(UCHAR_SHIFT_1)
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LEFT)                                                               PORT_CHAR(UCHAR_MAMEKEY(LEFT))
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_RIGHT)                                                              PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DOWN)                                                               PORT_CHAR(UCHAR_MAMEKEY(DOWN))
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)   PORT_NAME("CTRL ACCENT")    PORT_CHAR(UCHAR_SHIFT_2)
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_INSERT)                                 PORT_NAME("INSC INSL")      PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_START("ROW2")
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2)                                     PORT_NAME("RUN STOP")       PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CHAR(UCHAR_MAMEKEY(PAUSE))
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)                                                                  PORT_CHAR('q') PORT_CHAR('Q')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE)                                  PORT_NAME("ESPACE")         PORT_CHAR(' ')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CAPSLOCK)                               PORT_NAME("LCK")            PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER)                                  PORT_NAME("RET")            PORT_CHAR(13)
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_UP)                                                                 PORT_CHAR(UCHAR_MAMEKEY(UP))
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)                                                                  PORT_CHAR('a') PORT_CHAR('A')
	PORT_START("ROW3")
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)                                                                  PORT_CHAR('w') PORT_CHAR('W')
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)                                                                  PORT_CHAR('x') PORT_CHAR('X')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)                                                                  PORT_CHAR('c') PORT_CHAR('C')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)                                                                  PORT_CHAR('v') PORT_CHAR('V')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)                                                                  PORT_CHAR('b') PORT_CHAR('B')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1)                                                                  PORT_CHAR('1') PORT_CHAR('#') PORT_CHAR(0xe2)   // â
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)                                                                  PORT_CHAR(':') PORT_CHAR('*')
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)                                                                  PORT_CHAR('z') PORT_CHAR('Z')
	PORT_START("ROW4")
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)                                                                  PORT_CHAR('s') PORT_CHAR('S')
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)                                                                  PORT_CHAR('e') PORT_CHAR('E')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)                                                                  PORT_CHAR('3') PORT_CHAR('"') PORT_CHAR(0xea)   // ê
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)                                                                  PORT_CHAR('4') PORT_CHAR(0xa3) PORT_CHAR(0xef)  // £ ï
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5)                                                                  PORT_CHAR('5') PORT_CHAR('$') PORT_CHAR(0xf9)   // ù
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6)                                                                  PORT_CHAR('6') PORT_CHAR('%') PORT_CHAR(0xf4)   // ô
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2)                                                                  PORT_CHAR('2') PORT_CHAR('!') PORT_CHAR(0xe9)   // é
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)                                                               PORT_CHAR(';') PORT_CHAR('@')
	PORT_START("ROW5")
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TAB)                                                                PORT_CHAR(0xf7) PORT_CHAR('_')                  // ÷
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)                                                              PORT_CHAR(0xd7) PORT_CHAR('|')                  // ×
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)                                                                  PORT_CHAR('f') PORT_CHAR('F')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)                                                                  PORT_CHAR('g') PORT_CHAR('G')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)                                                                  PORT_CHAR('u') PORT_CHAR('U')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)                                                                  PORT_CHAR('i') PORT_CHAR('I')
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)                                                                  PORT_CHAR('o') PORT_CHAR('O')
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)                                                                  PORT_CHAR('p') PORT_CHAR('P')
	PORT_START("ROW6")
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0)                                                                  PORT_CHAR('0') PORT_CHAR(')') PORT_CHAR(0xe0)   // à
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA)                                                              PORT_CHAR(']') PORT_CHAR('[')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH)                                                              PORT_CHAR(0x2026) PORT_CHAR(0x3c0)              // … π
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE)                                                              PORT_CHAR(',') PORT_CHAR('/')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7)                                                                  PORT_CHAR('7') PORT_CHAR('&') PORT_CHAR(0xe8)   // è
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8)                                                                  PORT_CHAR('8') PORT_CHAR('\'') PORT_CHAR(0xfb)  // û
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9)                                                                  PORT_CHAR('9') PORT_CHAR('(') PORT_CHAR(0xe7)   // ç
	PORT_START("ROW7")
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE)                                                              PORT_CHAR('-') PORT_CHAR('?')
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH)                                                          PORT_CHAR('+') PORT_CHAR('.')
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)                                                                  PORT_CHAR('r') PORT_CHAR('R')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)                                                                  PORT_CHAR('t') PORT_CHAR('T')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)                                                                  PORT_CHAR('y') PORT_CHAR('Y')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE)                                                          PORT_CHAR('<') PORT_CHAR('>')
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE)                             PORT_NAME("PRT")            PORT_CHAR(UCHAR_MAMEKEY(PRTSCR))
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)                                                                  PORT_CHAR('d') PORT_CHAR('D')
	PORT_START("ROW8")
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS)                                                             PORT_CHAR('=') PORT_CHAR('^')
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSPACE)                              PORT_NAME("EFFC EFFL")      PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(DEL))
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)                                                                  PORT_CHAR('n') PORT_CHAR('N')
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)                                                                  PORT_CHAR('j') PORT_CHAR('J')
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)                                                                  PORT_CHAR('h') PORT_CHAR('H')
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)                                                                  PORT_CHAR('k') PORT_CHAR('K')
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)                                                                  PORT_CHAR('l') PORT_CHAR('L')
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)                                                              PORT_CHAR('m') PORT_CHAR('M')
	PORT_START("JOY0")
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )    PORT_PLAYER(1)
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )  PORT_PLAYER(1)
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )  PORT_PLAYER(1)
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 )        PORT_PLAYER(1)
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_START("JOY1")
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )    PORT_PLAYER(2)
		PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
		PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )  PORT_PLAYER(2)
		PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )  PORT_PLAYER(2)
		PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 )        PORT_PLAYER(2)
		PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
		PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_START("direct")
		PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)        PORT_CODE(KEYCODE_END)                              PORT_NAME("DELTA")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vg5k_state::delta_button), 0)
INPUT_PORTS_END


TIMER_CALLBACK_MEMBER(vg5k_state::z80_irq_clear)
{
	m_maincpu->set_input_line(0, CLEAR_LINE);
}


TIMER_DEVICE_CALLBACK_MEMBER(vg5k_state::z80_irq)
{
	m_maincpu->set_input_line(0, ASSERT_LINE);

	m_z80_irq_clear_timer->adjust(attotime::from_usec(100));
}

TIMER_DEVICE_CALLBACK_MEMBER(vg5k_state::vg5k_scanline)
{
	m_ef9345->update_scanline((uint16_t)param);
}

INPUT_CHANGED_MEMBER(vg5k_state::delta_button)
{
	// The yellow Delta key on the keyboard is wired so that it asserts directly the NMI line of the Z80.
	if (!newval) {
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
	}
}


void vg5k_state::machine_start()
{
	save_item(NAME(m_ef9345_offset));
	save_item(NAME(m_printer_latch));
	save_item(NAME(m_printer_signal));

	m_z80_irq_clear_timer = timer_alloc(FUNC(vg5k_state::z80_irq_clear), this);
}

void vg5k_state::machine_reset()
{
	m_ef9345_offset = 0;
	m_printer_latch = 0;
	m_printer_signal = 0;
}


/* F4 Character Displayer */
static const gfx_layout vg5k_charlayout =
{
	8, 16,                  /* 8 x 16 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	/* y offsets */
	{ 0, 8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_vg5k )
	GFXDECODE_ENTRY( "ef9345", 0x2000, vg5k_charlayout, 0, 4 )
GFXDECODE_END

void vg5k_state::init_vg5k()
{
	uint8_t *FNT = memregion("ef9345")->base();
	uint16_t dest = 0x2000;

	/* Unscramble the chargen rom as the format is too complex for gfxdecode to handle unaided */
	for (uint16_t a = 0; a < 8192; a+=4096)
		for (uint16_t b = 0; b < 2048; b+=64)
			for (uint16_t c = 0; c < 4; c++)
				for (uint16_t d = 0; d < 64; d+=4)
					FNT[dest++]=FNT[a|b|c|d];


	/* install expansion memory*/
	address_space &program = m_maincpu->space(AS_PROGRAM);
	uint8_t *ram = m_ram->pointer();
	uint16_t ram_size = m_ram->size();

	if (ram_size > 0x4000)
		program.install_ram(0x8000, 0x3fff + ram_size, ram);
}


void vg5k_state::vg5k(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(4'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &vg5k_state::vg5k_mem);
	m_maincpu->set_addrmap(AS_IO, &vg5k_state::vg5k_io);
	m_maincpu->refresh_cb().set(FUNC(vg5k_state::z80_m1_w));

	TIMER(config, "vg5k_scanline").configure_scanline(FUNC(vg5k_state::vg5k_scanline), "screen", 0, 10);

	TIMER(config, "irq_timer").configure_periodic(FUNC(vg5k_state::z80_irq), attotime::from_msec(20));

	EF9345(config, m_ef9345, 0);
	m_ef9345->set_palette_tag("palette");

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("ef9345", FUNC(ef9345_device::screen_update));
	screen.set_size(336, 300);
	screen.set_visarea(00, 336-1, 00, 270-1);

	GFXDECODE(config, "gfxdecode", "palette", gfx_vg5k);
	PALETTE(config, "palette").set_entries(8);

	/* sound hardware */
	SPEAKER(config, "speaker").front_center();
	DAC_1BIT(config, "dac", 0).add_route(ALL_OUTPUTS, "speaker", 0.125);

	/* cassette */
	CASSETTE(config, m_cassette);
	m_cassette->set_formats(vg5k_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(0, "speaker", 0.05);
	m_cassette->set_interface("vg5k_cass");

	/* printer */
	PRINTER(config, m_printer, 0);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("16K").set_extra_options("32K,48K");

	/* Software lists */
	SOFTWARE_LIST(config, "cass_list").set_original("vg5k");
}

/* ROM definition */
ROM_START( vg5k )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS(0, "v11", "BASIC v1.1")
	ROMX_LOAD( "vg5k11.bin", 0x0000, 0x4000, CRC(a6f4a0ea) SHA1(58eccce33cc21fc17bc83921018f531b8001eda3), ROM_BIOS(0) )  // dumped from a Philips VG-5000.
	ROM_SYSTEM_BIOS(1, "v10", "BASIC v1.0")
	ROMX_LOAD( "vg5k10.bin", 0x0000, 0x4000, BAD_DUMP CRC(57983260) SHA1(5ad1787a6a597b5c3eedb7c3704b649faa9be4ca), ROM_BIOS(1) )

	ROM_REGION( 0x4000, "ef9345", 0 )
	ROM_LOAD( "charset.rom", 0x0000, 0x2000, BAD_DUMP CRC(b2f49eb3) SHA1(d0ef530be33bfc296314e7152302d95fdf9520fc) )                // from dcvg5k
ROM_END

} // anonymous namespace


/* Driver */
//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT       COMPANY    FULLNAME   FLAGS
COMP( 1984, vg5k, 0,      0,      vg5k,    vg5k,  vg5k_state, init_vg5k, "Philips", "VG-5000", MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING )



vgmplay.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Olivier Galibert

// A "virtual" driver to play vgm files
// Use with mame vgmplay -quik file.vgm

#include "emu.h"

#ifndef QSOUND_LLE
#define QSOUND_LLE
#endif

#include "mega32x.h"
#include "vboysound.h"
#include "wswansound.h"

#include "cpu/h6280/h6280.h"
#include "cpu/m6502/rp2a03.h"
#include "cpu/m68000/m68000.h"

#include "imagedev/snapquik.h"

#include "sound/ay8910.h"
#include "sound/c140.h"
#include "sound/c352.h"
#include "sound/c6280.h"
#include "sound/es5503.h"
#include "sound/es5506.h"
#include "sound/gb.h"
#include "sound/iremga20.h"
#include "sound/k051649.h"
#include "sound/k053260.h"
#include "sound/k054539.h"
#include "sound/multipcm.h"
#include "sound/okim6258.h"
#include "sound/okim6295.h"
#include "sound/pokey.h"
#include "sound/qsound.h"
#include "sound/rf5c68.h"
#include "sound/saa1099.h"
#include "sound/scsp.h"
#include "sound/segapcm.h"
#include "sound/sn76496.h"
#include "sound/upd7759.h"
#include "sound/vgm_visualizer.h"
#include "sound/x1_010.h"
#include "sound/ymf271.h"
#include "sound/ymopl.h"
#include "sound/ymopm.h"
#include "sound/ymopn.h"
#include "sound/ymz280b.h"

#include "debugger.h"
#include "softlist_dev.h"
#include "speaker.h"

#include <zlib.h>

#include <algorithm>
#include <iterator>
#include <list>
#include <memory>
#include <queue>
#include <utility>
#include <vector>

#include "vgmplay.lh"

#define AS_IO16LE           1
#define AS_IO16BE           4

class vgmplay_disassembler : public util::disasm_interface
{
public:
	vgmplay_disassembler() = default;
	virtual ~vgmplay_disassembler() = default;

	virtual uint32_t opcode_alignment() const override;
	virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params) override;
};

enum vgm_chip
{
	CT_SN76489 = 0,
	CT_YM2413,
	CT_YM2612,
	CT_YM2151,
	CT_SEGAPCM,
	CT_RF5C68,
	CT_YM2203,
	CT_YM2608,
	CT_YM2610,
	CT_YM3812,
	CT_YM3526,
	CT_Y8950,
	CT_YMF262,
	CT_YMF278B,
	CT_YMF271,
	CT_YMZ280B,
	CT_RF5C164,
	CT_SEGA32X,
	CT_AY8910,
	CT_GAMEBOY,
	CT_NESAPU,
	CT_MULTIPCM,
	CT_UPD7759,
	CT_OKIM6258,
	CT_OKIM6295,
	CT_K051649,
	CT_K054539,
	CT_C6280,
	CT_C140,
	CT_K053260,
	CT_POKEY,
	CT_QSOUND,
	CT_SCSP,
	CT_WSWAN,
	CT_VSU_VUE,
	CT_SAA1099,
	CT_ES5503,
	CT_ES5505,
	CT_X1_010,
	CT_C352,
	CT_GA20,

	CT_COUNT,
};

enum C140_TYPE
{
	C140_LINEAR = 0,
	C140_SYSTEM2,
	C140_SYSTEM21,
	C140_ASIC219
};

class vgmplay_device : public cpu_device
{
public:
	enum io8_t
	{
		REG_SIZE = 0xff000000,
		A_SN76489_0 = 0x00000000,
		A_SN76489_1 = 0x80000000,
		A_YM2413_0 = 0x01000000,
		A_YM2413_1 = 0x81000000,
		A_YM2612_0 = 0x02000000,
		A_YM2612_1 = 0x82000000,
		A_YM2151_0 = 0x03000000,
		A_YM2151_1 = 0x83000000,
		A_SEGAPCM_0 = 0x04000000,
		A_SEGAPCM_1 = 0x84000000,
		A_RF5C68 = 0x05000000,
		A_RF5C68_RAM = 0x85000000,
		A_YM2203_0 = 0x06000000,
		A_YM2203_1 = 0x86000000,
		A_YM2608_0 = 0x07000000,
		A_YM2608_1 = 0x87000000,
		A_YM2610_0 = 0x08000000,
		A_YM2610_1 = 0x88000000,
		A_YM3812_0 = 0x09000000,
		A_YM3812_1 = 0x89000000,
		A_YM3526_0 = 0x0a000000,
		A_YM3526_1 = 0x8a000000,
		A_Y8950_0 = 0x0b000000,
		A_Y8950_1 = 0x8b000000,
		A_YMF262_0 = 0x0c000000,
		A_YMF262_1 = 0x8c000000,
		A_YMF278B_0 = 0x0d000000,
		A_YMF278B_1 = 0x8d000000,
		A_YMF271_0 = 0x0e000000,
		A_YMF271_1 = 0x8e000000,
		A_YMZ280B_0 = 0x0f000000,
		A_YMZ280B_1 = 0x8f000000,
		A_RF5C164 = 0x10000000,
		A_RF5C164_RAM = 0x90000000,
		A_32X_PWM = 0x11000000,
		A_AY8910_0 = 0x12000000,
		A_AY8910_1 = 0x92000000,
		A_GAMEBOY_0 = 0x13000000,
		A_GAMEBOY_1 = 0x93000000,
		A_NESAPU_0 = 0x14000000,
		A_NES_RAM_0 = 0x14010000,
		A_NESAPU_1 = 0x94000000,
		A_NES_RAM_1 = 0x94010000,
		A_MULTIPCM_0 = 0x15000000,
		A_MULTIPCM_1 = 0x95000000,
		A_UPD7759_0 = 0x16000000,
		A_UPD7759_1 = 0x96000000,
		A_OKIM6258_0 = 0x17000000,
		A_OKIM6258_1 = 0x97000000,
		A_OKIM6295_0 = 0x18000000,
		A_OKIM6295_1 = 0x98000000,
		A_K051649_0 = 0x19000000,
		A_K051649_1 = 0x99000000,
		A_K054539_0 = 0x1a000000,
		A_K054539_1 = 0x9a000000,
		A_C6280_0 = 0x1b000000,
		A_C6280_1 = 0x9b000000,
		A_C140_0 = 0x1c000000,
		A_C140_1 = 0x9c000000,
		A_K053260_0 = 0x1d000000,
		A_K053260_1 = 0x9d000000,
		A_POKEY_0 = 0x1e000000,
		A_POKEY_1 = 0x9e000000,
		A_QSOUND = 0x1f000000,
		A_SCSP_0 = 0x20000000,
		A_SCSP_RAM_0 = 0x20010000,
		A_SCSP_1 = 0xa0000000,
		A_SCSP_RAM_1 = 0xa0010000,
		A_WSWAN_0 = 0x21000000,
		A_WSWAN_RAM_0 = 0x21000100,
		A_WSWAN_1 = 0xa1000000,
		A_WSWAN_RAM_1 = 0xa1000100,
		A_VSU_VUE_0 = 0x22000000,
		A_VSU_VUE_1 = 0xa2000000,
		A_SAA1099_0 = 0x23000000,
		A_SAA1099_1 = 0xa3000000,
		A_ES5503_0 = 0x24000000,
		A_ES5503_RAM_0 = 0x24000100,
		A_ES5503_1 = 0xa4000000,
		A_ES5503_RAM_1 = 0xa4000100,
		A_ES5505_0 = 0x25000000,
		A_ES5505_1 = 0xa5000000,
		A_X1_010_0 = 0x26000000,
		A_X1_010_1 = 0xa6000000,
		A_C352_0 = 0x27000000,
		A_C352_1 = 0xa7000000,
		A_GA20_0 = 0x28000000,
		A_GA20_1 = 0xa8000000,
	};

	vgmplay_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	virtual uint32_t execute_min_cycles() const noexcept override;
	virtual uint32_t execute_max_cycles() const noexcept override;
	virtual void execute_run() override;
	virtual void execute_set_input(int inputnum, int state) override;

	virtual space_config_vector memory_space_config() const override;

	virtual void state_import(const device_state_entry &entry) override;
	virtual void state_export(const device_state_entry &entry) override;
	virtual void state_string_export(const device_state_entry &entry, std::string &str) const override;

	virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;

	template<int Index> uint8_t segapcm_rom_r(offs_t offset);
	template<int Index> uint8_t ym2608_rom_r(offs_t offset);
	template<int Index> uint8_t ym2610_adpcm_a_rom_r(offs_t offset);
	template<int Index> uint8_t ym2610_adpcm_b_rom_r(offs_t offset);
	template<int Index> uint8_t y8950_rom_r(offs_t offset);
	template<int Index> uint8_t ymf278b_rom_r(offs_t offset);
	template<int Index> uint8_t ymf271_rom_r(offs_t offset);
	template<int Index> uint8_t ymz280b_rom_r(offs_t offset);
	template<int Index> uint8_t multipcm_rom_r(offs_t offset);
	template<int Index> uint8_t upd7759_rom_r(offs_t offset);
	template<int Index> uint8_t okim6295_rom_r(offs_t offset);
	template<int Index> uint8_t k054539_rom_r(offs_t offset);
	template<int Index> uint16_t c140_rom_r(offs_t offset);
	template<int Index> uint16_t c219_rom_r(offs_t offset);
	template<int Index> uint8_t k053260_rom_r(offs_t offset);
	template<int Index> uint8_t qsound_rom_r(offs_t offset);
	template<int Index> uint8_t es5505_rom_r(offs_t offset);
	template<int Index> uint8_t x1_010_rom_r(offs_t offset);
	template<int Index> uint8_t c352_rom_r(offs_t offset);
	template<int Index> uint8_t ga20_rom_r(offs_t offset);

	template<int Index> void multipcm_bank_hi_w(offs_t offset, uint8_t data);
	template<int Index> void multipcm_bank_lo_w(offs_t offset, uint8_t data);

	template<int Index> void upd7759_bank_w(uint8_t data);

	template<int Index> void okim6295_nmk112_enable_w(offs_t offset, uint8_t data, uint8_t mem_mask = ~0);
	template<int Index> void okim6295_bank_w(offs_t offset, uint8_t data, uint8_t mem_mask = ~0);
	template<int Index> void okim6295_nmk112_bank_w(offs_t offset, uint8_t data, uint8_t mem_mask = ~0);

	void set_c140_bank_type(int index, C140_TYPE type);
	C140_TYPE c140_bank(int index) { return m_c140_bank[index]; }

	void stop();
	void pause();
	bool paused() const { return m_paused; }
	void play();
	void toggle_loop() { m_loop = !m_loop; m_loop_led = m_loop ? 1 : 0; }

protected:
	virtual void device_start() override ATTR_COLD;
	virtual void device_reset() override ATTR_COLD;

private:
	enum { ACT_LED_PERSIST_MS = 100 };

	enum { RESET, RUN, DONE };

	using led_expiry = std::pair<vgm_chip, attotime>;
	using led_expiry_list = std::list<led_expiry>;
	using led_expiry_iterator = led_expiry_list::iterator;

	struct rom_block
	{
		offs_t start_address;
		offs_t end_address;
		std::unique_ptr<uint8_t[]> data;

		rom_block(rom_block &&) = default;
		rom_block(offs_t start, offs_t end, std::unique_ptr<uint8_t[]> &&d) : start_address(start), end_address(end), data(std::move(d)) {}
	};

	struct stream
	{
		uint8_t byte_depth = 0;
		uint32_t position = 0;
		emu_timer *timer = nullptr;
		// stream control
		vgm_chip chip_type;
		uint8_t port = 0;
		uint8_t reg = 0;
		// stream data
		uint8_t bank = 0;
		uint8_t step_size = 0;
		uint8_t step_base = 0;
		// frequency
		uint32_t frequency = 0;
		// start stream
		uint32_t offset = 0;
		uint32_t length = 0;
		bool loop = false;
		bool reverse = false;
	};

	TIMER_CALLBACK_MEMBER(stream_timer_expired);

	stream m_streams[0xff];

	void pulse_act_led(vgm_chip led);
	TIMER_CALLBACK_MEMBER(act_led_expired);

	uint8_t rom_r(int index, uint8_t type, offs_t offset);
	uint32_t handle_data_block(uint32_t address);
	uint32_t handle_pcm_write(uint32_t address);
	void blocks_clear();

	output_finder<> m_playing_led;
	output_finder<> m_loop_led;
	output_finder<CT_COUNT> m_act_leds;
	led_expiry_list m_act_led_expiries;
	std::unique_ptr<led_expiry_iterator[]> m_act_led_index;
	led_expiry_iterator m_act_led_off;
	emu_timer *m_act_led_timer = nullptr;

	address_space_config m_file_config, m_io_config, m_io16le_config, m_io16be_config;
	address_space *m_file = nullptr, *m_io = nullptr, *m_io16le = nullptr, *m_io16be = nullptr;

	int m_icount = 0;
	int m_state = RESET;
	bool m_paused = false;
	bool m_loop = false;

	uint32_t m_pc = 0U;

	std::list<rom_block> m_rom_blocks[2][0x40];

	struct data_block
	{
		uint32_t start;
		uint32_t size;
		data_block(uint32_t _start, uint32_t _size) { start = _start; size = _size; }
	};

	std::array<std::vector<uint8_t>, 0x40> m_data_streams;
	std::array<std::vector<data_block>, 0x40> m_data_stream_blocks;

	struct
	{
		uint8_t cmp_type;
		uint8_t cmp_sub_type;
		uint8_t bit_dec;
		uint8_t bit_cmp;
		std::vector<uint8_t> entries;
	} m_dec_table;

	uint32_t m_ym2612_stream_offset = 0U;

	uint32_t m_multipcm_bank_l[2];
	uint32_t m_multipcm_bank_r[2];
	uint32_t m_multipcm_banked[2];

	uint32_t m_upd7759_bank[2];

	uint32_t m_okim6295_nmk112_enable[2];
	uint32_t m_okim6295_bank[2];
	uint32_t m_okim6295_nmk112_bank[2][4];

	C140_TYPE m_c140_bank[2]{};

	int m_sega32x_channel_hack = 0;
	int m_nes_apu_channel_hack[2];
	uint8_t m_c6280_channel[2];

	// newer YM cores require writes to be spaced out in order to play back
	// correctly; simulate this by introducing some extra accounting
	void space_out_write(uint32_t address);
	void decrement_icount(int count);
	int m_icount_ahead = 0;
	int m_last_write_icount[256] = { 0 };
};

DEFINE_DEVICE_TYPE(VGMPLAY, vgmplay_device, "vgmplay_core", "VGM Player engine")

enum vgmplay_inputs : uint8_t
{
	VGMPLAY_STOP,
	VGMPLAY_PAUSE,
	VGMPLAY_PLAY,
	VGMPLAY_RESTART,
	VGMPLAY_LOOP,
	VGMPLAY_VIZ,
	VGMPLAY_RATE_DOWN,
	VGMPLAY_RATE_UP,
	VGMPLAY_RATE_RST,
	VGMPLAY_HOLD,
};

class vgmplay_state : public driver_device
{
public:
	vgmplay_state(const machine_config &mconfig, device_type type, const char *tag);

	DECLARE_QUICKLOAD_LOAD_MEMBER(load_file);

	uint8_t file_r(offs_t offset);
	uint8_t file_size_r(offs_t offset);
	DECLARE_INPUT_CHANGED_MEMBER(key_pressed);

	template<int Index> void upd7759_reset_w(uint8_t data);
	template<int Index> void upd7759_start_w(uint8_t data);
	template<int Index> void upd7759_data_w(uint8_t data);
	template<int Index> void upd7759_drq_w(int state);
	template<int Index> void okim6258_clock_w(offs_t offset, uint8_t data, uint8_t mem_mask = ~0);
	template<int Index> void okim6258_divider_w(offs_t offset, uint8_t data, uint8_t mem_mask = ~0);
	template<int Index> void okim6295_clock_w(offs_t offset, uint8_t data, uint8_t mem_mask = ~0);
	template<int Index> void okim6295_pin7_w(offs_t offset, uint8_t data, uint8_t mem_mask = ~0);
	template<int Index> void scc_w(offs_t offset, uint8_t data);
	template<int Index> void c140_c219_w(offs_t offset, uint8_t data);

	void vgmplay(machine_config &config);
	void file_map(address_map &map) ATTR_COLD;
	void soundchips_map(address_map &map) ATTR_COLD;
	void soundchips16le_map(address_map &map) ATTR_COLD;
	void soundchips16be_map(address_map &map) ATTR_COLD;
	template<int Index> void segapcm_map(address_map &map) ATTR_COLD;
	template<int Index> void rf5c68_map(address_map &map) ATTR_COLD;
	template<int Index> void ym2608_map(address_map &map) ATTR_COLD;
	template<int Index> void ym2610_adpcm_a_map(address_map &map) ATTR_COLD;
	template<int Index> void ym2610_adpcm_b_map(address_map &map) ATTR_COLD;
	template<int Index> void y8950_map(address_map &map) ATTR_COLD;
	template<int Index> void ymf278b_map(address_map &map) ATTR_COLD;
	template<int Index> void ymf271_map(address_map &map) ATTR_COLD;
	template<int Index> void ymz280b_map(address_map &map) ATTR_COLD;
	template<int Index> void rf5c164_map(address_map &map) ATTR_COLD;
	template<int Index> void nescpu_map(address_map &map) ATTR_COLD;
	template<int Index> void multipcm_map(address_map &map) ATTR_COLD;
	template<int Index> void upd7759_map(address_map &map) ATTR_COLD;
	template<int Index> void okim6295_map(address_map &map) ATTR_COLD;
	template<int Index> void k054539_map(address_map &map) ATTR_COLD;
	template<int Index> void c140_map(address_map &map) ATTR_COLD;
	template<int Index> void c219_map(address_map &map) ATTR_COLD;
	template<int Index> void k053260_map(address_map &map) ATTR_COLD;
	template<int Index> void qsound_map(address_map &map) ATTR_COLD;
	template<int Index> void scsp_map(address_map &map) ATTR_COLD;
	template<int Index> void wswan_map(address_map &map) ATTR_COLD;
	template<int Index> void es5503_map(address_map &map) ATTR_COLD;
	template<int Index> void es5505_map(address_map &map) ATTR_COLD;
	template<int Index> void x1_010_map(address_map &map) ATTR_COLD;
	template<int Index> void c352_map(address_map &map) ATTR_COLD;
	template<int Index> void ga20_map(address_map &map) ATTR_COLD;

private:
	virtual void machine_start() override ATTR_COLD;

	uint32_t m_held_clock = 0;
	std::vector<uint8_t> m_file_data;
	required_device<vgmplay_device> m_vgmplay;
	required_device<vgmviz_device> m_viz;
	required_device<speaker_device> m_speaker;
	required_device_array<sn76489_device, 2> m_sn76489;
	required_device_array<ym2413_device, 2> m_ym2413;
	required_device_array<ym2612_device, 2> m_ym2612;
	required_device_array<ym2151_device, 2> m_ym2151;
	required_device_array<segapcm_device, 2> m_segapcm;
	required_device<rf5c68_device> m_rf5c68;
	required_device_array<ym2203_device, 2> m_ym2203;
	required_device_array<ym2608_device, 2> m_ym2608;
	required_device_array<ym2610_device, 2> m_ym2610;
	required_device_array<ym3812_device, 2> m_ym3812;
	required_device_array<ym3526_device, 2> m_ym3526;
	required_device_array<y8950_device, 2> m_y8950;
	required_device_array<ymf262_device, 2> m_ymf262;
	required_device_array<ymf278b_device, 2> m_ymf278b;
	required_device_array<ymf271_device, 2> m_ymf271;
	required_device_array<ymz280b_device, 2> m_ymz280b;
	required_device<rf5c164_device> m_rf5c164;
	required_device<sega_32x_ntsc_device> m_sega32x;
	required_device_array<ay8910_device, 2> m_ay8910;
	required_device_array<gameboy_sound_device, 2> m_dmg;
	required_device_array<rp2a03_device, 2> m_nescpu;
	required_device_array<multipcm_device, 2> m_multipcm;
	required_device_array<upd7759_device, 2> m_upd7759;
	required_device_array<okim6258_device, 2> m_okim6258;
	required_device_array<okim6295_device, 2> m_okim6295;
	required_device_array<k051649_device, 2> m_k051649;
	required_device_array<k054539_device, 2> m_k054539;
	required_device_array<h6280_device, 2> m_huc6280;
	required_device_array<c140_device, 2> m_c140;
	required_device_array<c219_device, 2> m_c219;
	required_device_array<k053260_device, 2> m_k053260;
	required_device_array<pokey_device, 2> m_pokey;
	required_device<qsound_device> m_qsound;
	required_device_array<scsp_device, 2> m_scsp;
	required_device_array<wswan_sound_device, 2> m_wswan;
	required_device_array<vboysnd_device, 2> m_vsu_vue;
	required_device_array<saa1099_device, 2> m_saa1099;
	required_device_array<es5503_device, 2> m_es5503;
	required_device_array<es5505_device, 2> m_es5505;
	required_device_array<x1_010_device, 2> m_x1_010;
	required_device_array<c352_device, 2> m_c352;
	required_device_array<iremga20_device, 2> m_ga20;

	uint8_t m_okim6258_divider[2];
	uint8_t m_okim6295_pin7[2];
	uint8_t m_scc_reg[2];

	int m_upd7759_md[2];
	int m_upd7759_reset[2];
	int m_upd7759_drq[2];
	std::queue<uint8_t> m_upd7759_slave_data[2];

	uint32_t r32(int offset) const;
	uint8_t r8(int offset) const;
};

vgmplay_device::vgmplay_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	cpu_device(mconfig, VGMPLAY, tag, owner, clock),
	m_playing_led(*this, "playing"),
	m_loop_led(*this, "loop"),
	m_act_leds(*this, "led_act_%u", 0U),
	m_file_config("file", ENDIANNESS_LITTLE, 8, 32),
	m_io_config("io", ENDIANNESS_LITTLE, 8, 32),
	m_io16le_config("io16le", ENDIANNESS_LITTLE, 16, 32),
	m_io16be_config("io16be", ENDIANNESS_BIG, 16, 32)
{
}

void vgmplay_device::device_start()
{
	set_icountptr(m_icount);
	m_file = &space(AS_PROGRAM);
	m_io = &space(AS_IO);
	m_io16le = &space(AS_IO16LE);
	m_io16be = &space(AS_IO16BE);

	m_playing_led.resolve();
	m_loop_led.resolve();
	m_act_leds.resolve();
	m_act_led_index = std::make_unique<led_expiry_iterator[]>(CT_COUNT);
	for (vgm_chip led = vgm_chip(0); led != CT_COUNT; led = vgm_chip(led + 1))
		m_act_led_index[led] = m_act_led_expiries.emplace(m_act_led_expiries.end(), led, attotime::never);
	m_act_led_off = m_act_led_expiries.begin();
	m_act_led_timer = timer_alloc(FUNC(vgmplay_device::act_led_expired), this);

	for (int i = 0; i < 0xff; i++)
		m_streams[i].timer = timer_alloc(FUNC(vgmplay_device::stream_timer_expired), this);

	save_item(NAME(m_pc));
	//save_item(NAME(m_streams));

	state_add(STATE_GENPC, "GENPC", m_pc).noshow();
	state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow();
}

void vgmplay_device::device_reset()
{
	m_state = RESET;
	m_paused = false;
	m_playing_led = 1;
	m_loop_led = m_loop ? 1 : 0;

	m_ym2612_stream_offset = 0;
	std::fill(std::begin(m_upd7759_bank), std::end(m_upd7759_bank), 0);
	blocks_clear();

	for (int i = 0; i < 0xff; i++)
	{
		stream& s(m_streams[i]);
		s.chip_type = vgm_chip(0xff);
		s.bank = 0xff;
		s.frequency = 0;
		s.timer->enable(false);
	}

	m_sega32x_channel_hack = 0;
	m_nes_apu_channel_hack[0] = 0;
	m_nes_apu_channel_hack[1] = 0;
	m_c6280_channel[0] = 0;
	m_c6280_channel[1] = 0;
}

void vgmplay_device::pulse_act_led(vgm_chip led)
{
	m_act_leds[led] = 1;

	bool const was_first(m_act_led_expiries.begin() == m_act_led_index[led]);
	bool const all_off(m_act_led_expiries.begin() == m_act_led_off);
	attotime const now(machine().time());

	m_act_led_index[led]->second = now + attotime::from_msec(ACT_LED_PERSIST_MS);
	if (m_act_led_off != m_act_led_index[led])
		m_act_led_expiries.splice(m_act_led_off, m_act_led_expiries, m_act_led_index[led]);
	else
		++m_act_led_off;
	if (all_off)
		m_act_led_timer->adjust(attotime::from_msec(ACT_LED_PERSIST_MS));
	else if (was_first)
		m_act_led_timer->adjust(m_act_led_expiries.begin()->second - now);
}

TIMER_CALLBACK_MEMBER(vgmplay_device::act_led_expired)
{
	attotime const now(machine().time());

	while ((now + attotime::from_msec(1)) >= m_act_led_expiries.begin()->second)
	{
		led_expiry_iterator const expired(m_act_led_expiries.begin());
		m_act_leds[expired->first] = 0;
		expired->second = attotime::never;
		if (expired != m_act_led_off)
		{
			m_act_led_expiries.splice(m_act_led_off, m_act_led_expiries, expired);
			m_act_led_off = expired;
		}
	}

	if (m_act_led_expiries.begin() != m_act_led_off)
		m_act_led_timer->adjust(m_act_led_expiries.begin()->second - now);
}

void vgmplay_device::stop()
{
	device_reset();
	m_paused = true;
	m_playing_led = 0;
}

void vgmplay_device::pause()
{
	m_paused = !m_paused;
	m_playing_led = m_paused ? 0 : 1;
}

void vgmplay_device::play()
{
	if (m_paused && m_state != DONE)
	{
		m_paused = false;
		m_playing_led = 1;
	}
	else
	{
		device_reset();
	}
}

uint32_t vgmplay_device::execute_min_cycles() const noexcept
{
	return 0;
}

uint32_t vgmplay_device::execute_max_cycles() const noexcept
{
	return 65536;
}

void vgmplay_device::blocks_clear()
{
	for (int i = 0; i < 0x40; i++)
	{
		m_rom_blocks[0][i].clear();
		m_rom_blocks[1][i].clear();
		m_data_streams[i].clear();
		m_data_stream_blocks[i].clear();
	}

	m_dec_table.entries.clear();
}

uint32_t vgmplay_device::handle_data_block(uint32_t address)
{
	uint32_t size = m_file->read_dword(m_pc + 3);
	int second = (size & 0x80000000) ? 1 : 0;
	size &= 0x7fffffff;

	uint8_t type = m_file->read_byte(m_pc + 2);
	if (type < 0x40)
	{
		uint32_t start = m_data_streams[type].size();
		m_data_stream_blocks[type].push_back(data_block(start, size));
		m_data_streams[type].resize(start + size);
		for (uint32_t i = 0; i<size; i++)
			m_data_streams[type][start + i] = m_file->read_byte(m_pc + 7 + i);
	}
	else if (type < 0x7f)
	{
		uint8_t cmp_type = m_file->read_byte(m_pc + 0x07);
		uint32_t out_size = m_file->read_dword(m_pc + 0x08);

		uint32_t start = m_data_streams[type - 0x40].size();
		m_data_stream_blocks[type - 0x40].push_back(data_block(start, out_size));
		m_data_streams[type - 0x40].resize(start + out_size);

		if (cmp_type == 0)
		{
			uint8_t bit_dec = m_file->read_byte(m_pc + 0x0c);
			uint8_t bit_cmp = m_file->read_byte(m_pc + 0x0d);
			uint8_t cmp_sub_type = m_file->read_byte(m_pc + 0x0e);
			uint16_t add_val = m_file->read_word(m_pc + 0x0f);

			if (cmp_sub_type == 0x02 && m_dec_table.entries.size() == 0)
				osd_printf_error("invalid n-bit compressed stream, no decompression table\n");
			else if (cmp_sub_type == 0x02 && (m_dec_table.cmp_type != cmp_type || m_dec_table.cmp_sub_type != cmp_sub_type || m_dec_table.bit_dec != bit_dec || m_dec_table.bit_cmp != bit_cmp ))
				osd_printf_error("invalid n-bit compressed stream, decompression table mismatch\n");
			else
			{
				int in_pos = 0x11 - 0x07;
				int in_shift = 0;
				int out_pos = 0;

				while (out_pos < out_size && in_pos < size)
				{
					int in_bits = bit_cmp;
					uint16_t in_val = 0;
					int out_bits = 0;

					while (in_bits > 0 && in_pos < size)
					{
						int bits = std::min(in_bits, 8);
						uint8_t mask = (1 << bits) - 1;
						in_shift += bits;
						in_bits -= bits;

						uint16_t val = (m_file->read_byte(m_pc + 0x7 + in_pos) << in_shift >> 8) & mask;
						if (in_shift >= 8)
						{
							in_pos++;

							in_shift -= 8;
							if (in_shift > 0)
							{
								if (in_pos < size)
									val |= (m_file->read_byte(m_pc + 0x7 + in_pos) << in_shift >> 8) & mask;
								else
									break;
							}
						}

						in_val |= val << out_bits;
						out_bits += bits;
					}

					if (out_bits == bit_cmp)
					{
						uint16_t out_val = 0;

						if (cmp_sub_type == 0)
							out_val = in_val + add_val;
						else if (cmp_sub_type == 1)
							out_val = (in_val << (bit_dec - bit_cmp)) + add_val;
						else if (cmp_sub_type == 2)
						{
							if (bit_dec <= 8)
								out_val = m_dec_table.entries[in_val];
							else if (bit_dec <= 16)
								out_val = m_dec_table.entries[in_val << 1] | (m_dec_table.entries[(in_val << 1) + 1] << 8);
						}
						else
							osd_printf_error("invalid n-bit compressed stream size %x->%x type %02x bit_dec %02x bit_cmp %02x unsupported cmp_sub_type %02x add_val %04x\n", size, out_size, type - 0x40, bit_dec, bit_cmp, cmp_sub_type, add_val);

						for (int i = 0; i < bit_dec; i += 8)
						{
							m_data_streams[type - 0x40][start + out_pos] = out_val;
							out_pos++;
							out_val >>= 8;
						}
					}
				}

				if (out_pos != out_size)
					osd_printf_error("invalid n-bit compressed stream %02x in %x/%x out %x/%x\n", type - 0x40, in_pos, size, out_pos, out_size);
			}
		}
		else if (cmp_type == 1)
			osd_printf_error("unhandled delta-t compressed stream size %x->%x type %02x\n", size, out_size, type - 0x40);
		else
			osd_printf_error("unhandled unknown %02x compressed stream size %x->%x type %02x\n", cmp_type, size, out_size, type - 0x40);
	}
	else if (type == 0x7f)
	{
		m_dec_table.cmp_type = m_file->read_byte(m_pc + 0x07);
		m_dec_table.cmp_sub_type = m_file->read_byte(m_pc + 0x08);
		m_dec_table.bit_dec = m_file->read_byte(m_pc + 0x09);
		m_dec_table.bit_cmp = m_file->read_byte(m_pc + 0x0a);

		m_dec_table.entries.resize(m_file->read_word(m_pc + 0x0b) * ((m_dec_table.bit_dec + 7) / 8));
		for (size_t i = 0; i < m_dec_table.entries.size(); i++)
			m_dec_table.entries[i] = m_file->read_byte(m_pc + 0x0d + i);
	}
	else if (type < 0xc0)
	{
		uint32_t rom_size = m_file->read_dword(m_pc + 7);
		uint32_t start = m_file->read_dword(m_pc + 11);

		uint32_t data_size = start < rom_size ? std::min(size - 8, rom_size - start) : 0;

		if (data_size)
		{
			std::unique_ptr<uint8_t[]> block = std::make_unique<uint8_t[]>(data_size);
			for (uint32_t i = 0; i < data_size; i++)
				block[i] = m_file->read_byte(m_pc + 15 + i);
			m_rom_blocks[second][type - 0x80].emplace_front(start, start + size - 9, std::move(block));
		}
	}
	else if (type <= 0xc2)
	{
		uint16_t start = m_file->read_word(m_pc + 7);
		uint32_t data_size = size - 2;
		if (type == 0xc0)
			for (int i = 0; i < data_size; i++)
				m_io->write_byte(A_RF5C68_RAM + start + i, m_file->read_byte(m_pc + 9 + i));
		else if (type == 0xc1)
			for (int i = 0; i < data_size; i++)
				m_io->write_byte(A_RF5C164_RAM + start + i, m_file->read_byte(m_pc + 9 + i));
		else if (type == 0xc2)
			for (int i = 0; i < data_size; i++)
				m_io->write_byte((second ? A_NES_RAM_1 : A_NES_RAM_0) + start + i, m_file->read_byte(m_pc + 9 + i));
	}
	else if (type >= 0xe0 && type <= 0xe1)
	{
		uint32_t start = m_file->read_dword(m_pc + 7);
		uint32_t data_size = size - 4;
		if (type == 0xe0)
			for (int i = 0; i < data_size; i++)
				m_io16be->write_byte((second ? A_SCSP_RAM_1 : A_SCSP_RAM_0) + ((start + i) ^ 1), m_file->read_byte(m_pc + 0xb + i));
		else if (type == 0xe1)
			for (int i = 0; i < data_size; i++)
				m_io->write_byte((second ? A_ES5503_RAM_1 : A_ES5503_RAM_0) + start + i, m_file->read_byte(m_pc + 0xb + i));
	}
	else
	{
		osd_printf_error("unhandled ram block size %x type %02x\n", size, type);
	}
	return 7 + size;
}

uint32_t vgmplay_device::handle_pcm_write(uint32_t address)
{
	uint8_t type = m_file->read_byte(m_pc + 2);
	uint32_t src = m_file->read_dword(m_pc + 3) & 0xffffff;
	uint32_t dst = m_file->read_dword(m_pc + 6) & 0xffffff;
	uint32_t size = m_file->read_dword(m_pc + 9) & 0xffffff;
	if (size == 0) size = 0x01000000;

	int second = (type & 0x80) ? 1 : 0;
	type &= 0x7f;

	if (m_data_streams.size() <= type || m_data_streams[type].size() < src + size)
		osd_printf_error("invalid pcm ram writes src %x dst %x size %x type %02x\n", src, dst, size, type);
	else if (type == 0x01 && !second)
	{
		for (int i = 0; i < size; i++)
			m_io->write_byte(A_RF5C68_RAM + dst + i, m_data_streams[type][src + i]);
	}
	else if (type == 0x02 && !second)
	{
		for (int i = 0; i < size; i++)
			m_io->write_byte(A_RF5C164_RAM + dst + i, m_data_streams[type][src + i]);
	}
	else if (type == 0x06)
	{
		for (int i = 0; i < size; i++)
			m_io16be->write_byte((second ? A_SCSP_RAM_1 : A_SCSP_RAM_0) + ((dst + i) ^ 1), m_data_streams[type][src + i]);
	}
	else if (type == 0x07)
	{
		for (int i = 0; i < size; i++)
			m_io->write_byte((second ? A_NES_RAM_1 : A_NES_RAM_0) + dst + i, m_data_streams[type][src + i]);
	}
	else
		osd_printf_error("unhandled pcm ram writes src %x dst %x size %x type %02x\n", src, dst, size, type);

	return 12;
}

TIMER_CALLBACK_MEMBER(vgmplay_device::stream_timer_expired)
{
	stream& s(m_streams[param]);

	uint32_t offset = s.offset;
	if (s.reverse)
		offset += (s.length - s.position - 1) * s.step_size * s.byte_depth;
	else
		offset += s.position * s.step_size * s.byte_depth;

	if (offset + s.byte_depth > m_data_streams[s.bank].size())
	{
		osd_printf_error("stream_timer_expired %02x: stream beyond end %d/%d %u>=%u\n", param, s.position, s.length, offset, uint32_t(m_data_streams[s.bank].size()));
		s.timer->enable(false);
	}
	else if (s.chip_type == CT_SN76489)
	{
		m_io->write_byte(A_SN76489_0, (s.reg & 0xf0) | (m_data_streams[s.bank][offset] & 0xf));
		if ((s.reg & 0x10) == 0)
			m_io->write_byte(A_SN76489_0, ((m_data_streams[s.bank][offset + 1] & 3) << 4) | (m_data_streams[s.bank][offset] >> 4));
	}
	else if (s.chip_type == CT_YM2612)
	{
		m_io->write_byte(A_YM2612_0 + 0 + ((s.port & 1) << 1), s.reg);
		m_io->write_byte(A_YM2612_0 + 1 + ((s.port & 1) << 1), m_data_streams[s.bank][offset]);
	}
	else if (s.chip_type == CT_YM2203)
	{
		m_io->write_byte(A_YM2203_0 + 0 + ((s.port & 1) << 1), s.reg);
		m_io->write_byte(A_YM2203_0 + 1 + ((s.port & 1) << 1), m_data_streams[s.bank][offset]);
	}
	else if (s.chip_type == CT_YM2608)
	{
		m_io->write_byte(A_YM2608_0 + 0 + ((s.port & 1) << 1), s.reg);
		m_io->write_byte(A_YM2608_0 + 1 + ((s.port & 1) << 1), m_data_streams[s.bank][offset]);
	}
	else if (s.chip_type == CT_SEGA32X)
	{
		if (m_sega32x_channel_hack >= 0)
		{
			osd_printf_error("bad rip detected, enabling sega32x channels\n");
			m_io16le->write_word(A_32X_PWM, 5);

			m_sega32x_channel_hack = -2;
		}

		m_io16le->write_word(A_32X_PWM + (s.reg << 1), ((m_data_streams[s.bank][offset + 1] & 0xf) << 8) | m_data_streams[s.bank][offset]);
	}
	else if (s.chip_type == CT_C6280)
	{
		if (s.port != 0xff)
			m_io->write_byte(A_C6280_0 + (s.reg >> 4), s.port);

		m_io->write_byte(A_C6280_0 + (s.reg & 0xf), m_data_streams[s.bank][offset]);

		if (s.port != 0xff && s.port != m_c6280_channel[0])
			m_io->write_byte(A_C6280_0 + (s.reg >> 4), m_c6280_channel[0]);
	}
	else if (s.chip_type == CT_OKIM6258)
		m_io->write_byte(A_OKIM6258_0 + s.reg, m_data_streams[s.bank][offset]);
	else
	{
		osd_printf_error("stream_timer_expired %02x: unsupported stream to chip %02x\n", param, s.chip_type);
		s.timer->enable(false);
	}

	s.position++;
	if (s.position >= s.length)
	{
		if (s.loop)
		{
			pulse_act_led(s.chip_type);
			s.position = 0;
		}
		else
			s.timer->enable(false);
	}
}

void vgmplay_device::space_out_write(uint32_t address)
{
	// use the top address bits as an index
	int index = address >> 24;
	uint32_t cycles = uint32_t(total_cycles());

	// if the last write happened at the current time, steal a cycle
	if (m_last_write_icount[index] == cycles)
	{
		m_icount--;
		m_icount_ahead++;
		cycles++;
	}
	m_last_write_icount[index] = cycles;
}

void vgmplay_device::decrement_icount(int count)
{
	// take back any cycles we stole
	if (count >= m_icount_ahead)
	{
		m_icount -= count - m_icount_ahead;
		m_icount_ahead = 0;
	}
	else
		m_icount_ahead -= count;
}

void vgmplay_device::execute_run()
{
	while (m_icount > 0)
	{
		switch (m_state)
		{
		case RESET:
		{
			uint32_t size = m_io->read_dword(REG_SIZE);
			if (!size)
			{
				logerror("zero length file\n");

				m_pc = 0;
				m_state = DONE;
				break;
			}

			uint32_t version = m_file->read_dword(8);
			m_pc = 0x34 + m_file->read_dword(0x34);

			if ((version < 0x150 && m_pc != 0x34) || (version >= 0x150 && m_pc == 0x34))
			{
				osd_printf_error("bad rip detected, v%x invalid header size 0x%x\n", version, m_pc);
				m_pc = 0x40;
			}
			else if (version < 0x150)
			{
				m_pc = 0x40;
			}

			m_state = RUN;
			break;
		}
		case RUN:
		{
			if (m_paused)
			{
				machine().sound().system_mute(1);
				decrement_icount(m_icount);
				return;
			}
			else
			{
				machine().sound().system_mute(0);
			}

			if (machine().debug_flags & DEBUG_FLAG_ENABLED)
				debugger_instruction_hook(m_pc);

			uint8_t code = m_file->read_byte(m_pc);
			switch (code)
			{
			case 0x30:
				pulse_act_led(CT_SN76489);
				m_io->write_byte(A_SN76489_1 + 0, m_file->read_byte(m_pc + 1));
				m_pc += 2;
				break;

			case 0x3f:
				pulse_act_led(CT_SN76489);
				m_io->write_byte(A_SN76489_1 + 1, m_file->read_byte(m_pc + 1));
				m_pc += 2;
				break;

			case 0x4f:
				pulse_act_led(CT_SN76489);
				m_io->write_byte(A_SN76489_0 + 1, m_file->read_byte(m_pc + 1));
				m_pc += 2;
				break;

			case 0x50:
				pulse_act_led(CT_SN76489);
				m_io->write_byte(A_SN76489_0 + 0, m_file->read_byte(m_pc + 1));
				m_pc += 2;
				break;

			case 0x51:
				pulse_act_led(CT_YM2413);
				space_out_write(A_YM2413_0);
				m_io->write_byte(A_YM2413_0 + 0, m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_YM2413_0 + 1, m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0x52:
			case 0x53:
				pulse_act_led(CT_YM2612);
				space_out_write(A_YM2612_0);
				m_io->write_byte(A_YM2612_0 + 0 + ((code & 1) << 1), m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_YM2612_0 + 1 + ((code & 1) << 1), m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0x54:
				pulse_act_led(CT_YM2151);
				space_out_write(A_YM2151_0);
				m_io->write_byte(A_YM2151_0 + 0, m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_YM2151_0 + 1, m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0x55:
				pulse_act_led(CT_YM2203);
				space_out_write(A_YM2203_0);
				m_io->write_byte(A_YM2203_0 + 0, m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_YM2203_0 + 1, m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0x56:
			case 0x57:
				pulse_act_led(CT_YM2608);
				space_out_write(A_YM2608_0);
				m_io->write_byte(A_YM2608_0 + 0 + ((code & 1) << 1), m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_YM2608_0 + 1 + ((code & 1) << 1), m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0x58:
			case 0x59:
				pulse_act_led(CT_YM2610);
				space_out_write(A_YM2610_0);
				m_io->write_byte(A_YM2610_0 + 0 + ((code & 1) << 1), m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_YM2610_0 + 1 + ((code & 1) << 1), m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0x5a:
				pulse_act_led(CT_YM3812);
				space_out_write(A_YM3812_0);
				m_io->write_byte(A_YM3812_0 + 0, m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_YM3812_0 + 1, m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0x5b:
				pulse_act_led(CT_YM3526);
				space_out_write(A_YM3526_0);
				m_io->write_byte(A_YM3526_0 + 0, m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_YM3526_0 + 1, m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0x5c:
				pulse_act_led(CT_Y8950);
				space_out_write(A_Y8950_0);
				m_io->write_byte(A_Y8950_0 + 0, m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_Y8950_0 + 1, m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0x5d:
				pulse_act_led(CT_YMZ280B);
				m_io->write_byte(A_YMZ280B_0 + 0, m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_YMZ280B_0 + 1, m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0x5e:
			case 0x5f:
				pulse_act_led(CT_YMF262);
				space_out_write(A_YMF262_0);
				m_io->write_byte(A_YMF262_0 + 0 + ((code & 1) << 1), m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_YMF262_0 + 1 + ((code & 1) << 1), m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0x61:
			{
				uint32_t duration = m_file->read_word(m_pc + 1);
				decrement_icount(duration);
				m_pc += 3;
				break;
			}

			case 0x62:
				decrement_icount(735);
				m_pc++;
				break;

			case 0x63:
				decrement_icount(882);
				m_pc++;
				break;

			case 0x66:
			{
				uint32_t loop_offset = m_file->read_dword(0x1c);
				if (!loop_offset)
				{
					if (m_loop)
						device_reset();
					else
					{
						logerror("done\n");
						m_state = DONE;
					}
					break;
				}

				m_pc = 0x1c + loop_offset;
				break;
			}

			case 0x67:
				m_pc += handle_data_block(m_pc);
				break;

			case 0x68:
				m_pc += handle_pcm_write(m_pc);
				break;

			case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
			case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f:
				decrement_icount(1 + (code & 0xf));
				m_pc += 1;
				break;

			case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87:
			case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f:
				pulse_act_led(CT_YM2612);
				if (!m_data_streams[0].empty())
				{
					if (m_ym2612_stream_offset >= int(m_data_streams[0].size()))
						m_ym2612_stream_offset = 0;

					space_out_write(A_YM2612_0);
					m_io->write_byte(A_YM2612_0 + 0, 0x2a);
					m_io->write_byte(A_YM2612_0 + 1, m_data_streams[0][m_ym2612_stream_offset]);
					m_ym2612_stream_offset++;
				}
				m_pc += 1;
				decrement_icount(code & 0xf);
				break;

			case 0x90:
			{
				uint8_t id = m_file->read_byte(m_pc + 1);
				if (id == 0xff)
					osd_printf_error("stream control invalid id\n");
				else
				{
					stream& s(m_streams[id]);

					s.chip_type = vgm_chip(m_file->read_byte(m_pc + 2));
					s.port = m_file->read_byte(m_pc + 3);
					s.reg = m_file->read_byte(m_pc + 4);

					s.byte_depth = ((s.chip_type == CT_SN76489 && (s.reg & 0x10) == 0) || s.chip_type == CT_SEGA32X) ? 2 : 1;

					if (s.timer->enabled())
					{
						osd_printf_error("stream %02x control while playing\n", id);
						s.timer->enable(false);
					}
				}
				m_pc += 5;
				break;
			}

			case 0x91:
			{
				uint8_t id = m_file->read_byte(m_pc + 1);
				if (id == 0xff)
					osd_printf_error("stream data invalid id\n");
				else
				{
					stream& s(m_streams[id]);

					s.bank = m_file->read_byte(m_pc + 2);
					s.step_size = m_file->read_byte(m_pc + 3);
					s.step_base = m_file->read_byte(m_pc + 4);

					if (s.step_size == 0)
					{
						osd_printf_error("stream %02x data invalid step size %d\n", id, s.step_size);
						s.step_size = 1;
					}

					if (s.step_base >= s.step_size)
					{
						osd_printf_error("stream %02x data step size %d invalid step base %d\n", id, s.step_size, s.step_base);
						s.step_base %= s.step_size;
					}

					if (s.timer->enabled())
					{
						osd_printf_error("stream %02x data while playing\n", id);
						s.timer->enable(false);
					}
				}
				m_pc += 5;
				break;
			}

			case 0x92:
			{
				uint8_t id = m_file->read_byte(m_pc + 1);
				if (id == 0xff)
					osd_printf_error("stream frequency invalid id\n");
				else
				{
					stream& s(m_streams[id]);

					s.frequency = m_file->read_dword(m_pc + 2);

					if (s.timer->enabled())
					{
						osd_printf_error("stream %02x frequency %d while playing\n", id, s.frequency);
						s.timer->enable(false);
					}
				}
				m_pc += 6;
				break;
			}

			case 0x93:
			{
				uint8_t id = m_file->read_byte(m_pc + 1);
				if (id == 0xff)
					osd_printf_error("stream start invalid id\n");
				else if (m_streams[id].chip_type >= CT_COUNT)
					osd_printf_error("stream start %02x invalid chip type %02x\n", id, m_streams[id].chip_type);
				else
				{
					stream& s(m_streams[id]);

					pulse_act_led(s.chip_type);

					uint32_t offset = m_file->read_dword(m_pc + 2);
					uint8_t flags = m_file->read_byte(m_pc + 6);
					uint32_t length = m_file->read_dword(m_pc + 7);

					if (s.bank >= m_data_stream_blocks.size())
						osd_printf_error("stream start %02x invalid bank %u>=%u\n", id, s.bank, uint8_t(m_data_stream_blocks.size()));
					else if (s.frequency == 0)
						osd_printf_error("stream start %02x invalid frequency\n", id);
					else
					{
						if (offset != 0xffffffff)
							s.offset = offset + (s.step_base * s.byte_depth);

						s.reverse = BIT(flags, 4);
						s.loop = BIT(flags, 7);

						switch (flags & 3)
						{
						case 0:
							break;
						case 1:
							s.length = length;
							break;
						case 2:
							s.length = (length * 1000) / s.frequency;
							break;
						case 3:
							s.length = (m_data_streams[s.bank].size() - (s.offset - (s.step_base * s.byte_depth))) / (s.step_size * s.byte_depth);
							break;
						}

						s.position = 0;
						s.timer->adjust(attotime::zero, id, attotime::from_hz(s.frequency));
					}
				}
				m_pc += 11;
				break;
			}

			case 0x94:
			{
				uint8_t id = m_file->read_byte(m_pc + 1);
				if (id == 0xff)
					for (int i = 0; i < 0xff; i++)
						m_streams[i].timer->enable(false);
				else
					m_streams[id].timer->enable(false);

				m_pc += 2;
				break;
			}

			case 0x95:
			{
				uint8_t id = m_file->read_byte(m_pc + 1);
				if (id == 0xff)
					osd_printf_error("stream start short invalid id\n");
				else if (m_streams[id].chip_type >= CT_COUNT)
					osd_printf_error("stream start short %02x invalid chip type %02x\n", id, m_streams[id].chip_type);
				else
				{
					stream& s(m_streams[id]);

					pulse_act_led(s.chip_type);

					uint8_t block = m_file->read_word(m_pc + 2);
					uint8_t flags = m_file->read_byte(m_pc + 4);

					if (s.bank >= m_data_stream_blocks.size())
						osd_printf_error("stream start short %02x invalid bank %u>=%u\n", id, s.bank, uint8_t(m_data_stream_blocks.size()));
					else if (block >= m_data_stream_blocks[s.bank].size())
						osd_printf_error("stream start short %02x bank %u invalid block %u>=%u\n", id, s.bank, block, uint8_t(m_data_stream_blocks[s.bank].size()));
					else if (s.frequency == 0)
						osd_printf_error("stream start %02x invalid frequency\n", id);
					else
					{
						s.loop = BIT(flags, 0);
						s.reverse = BIT(flags, 4);
						s.offset = m_data_stream_blocks[s.bank][block].start + (s.step_base * s.byte_depth);
						s.length = m_data_stream_blocks[s.bank][block].size / (s.step_size * s.byte_depth);

						s.position = 0;
						s.timer->adjust(attotime::zero, id, attotime::from_hz(s.frequency));
					}
				}
				m_pc += 5;
				break;
			}

			case 0xa0:
			{
				pulse_act_led(CT_AY8910);
				uint8_t reg = m_file->read_byte(m_pc + 1);
				if (reg & 0x80)
				{
					m_io->write_byte(A_AY8910_1 + 1, reg & 0x7f);
					m_io->write_byte(A_AY8910_1 + 0, m_file->read_byte(m_pc + 2));
				}
				else
				{
					m_io->write_byte(A_AY8910_0 + 1, reg & 0x7f);
					m_io->write_byte(A_AY8910_0 + 0, m_file->read_byte(m_pc + 2));
				}
				m_pc += 3;
				break;
			}

			case 0xa1:
				pulse_act_led(CT_YM2413);
				space_out_write(A_YM2413_1);
				m_io->write_byte(A_YM2413_1 + 0, m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_YM2413_1 + 1, m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0xa2:
			case 0xa3:
				pulse_act_led(CT_YM2612);
				space_out_write(A_YM2612_1);
				m_io->write_byte(A_YM2612_1 + 0 + ((code & 1) << 1), m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_YM2612_1 + 1 + ((code & 1) << 1), m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0xa4:
				pulse_act_led(CT_YM2151);
				space_out_write(A_YM2151_1);
				m_io->write_byte(A_YM2151_1 + 0, m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_YM2151_1 + 1, m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0xa5:
				pulse_act_led(CT_YM2203);
				space_out_write(A_YM2203_1);
				m_io->write_byte(A_YM2203_1 + 0, m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_YM2203_1 + 1, m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0xa6:
			case 0xa7:
				pulse_act_led(CT_YM2608);
				space_out_write(A_YM2608_0);
				m_io->write_byte(A_YM2608_0 + 0 + ((code & 1) << 1), m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_YM2608_0 + 1 + ((code & 1) << 1), m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0xa8:
			case 0xa9:
				pulse_act_led(CT_YM2610);
				space_out_write(A_YM2610_0);
				m_io->write_byte(A_YM2610_0 + 0 + ((code & 1) << 1), m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_YM2610_0 + 1 + ((code & 1) << 1), m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0xaa:
				pulse_act_led(CT_YM3812);
				space_out_write(A_YM3812_1);
				m_io->write_byte(A_YM3812_1 + 0, m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_YM3812_1 + 1, m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0xab:
				pulse_act_led(CT_YM3526);
				space_out_write(A_YM3526_1);
				m_io->write_byte(A_YM3526_1 + 0, m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_YM3526_1 + 1, m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0xac:
				pulse_act_led(CT_Y8950);
				space_out_write(A_Y8950_1);
				m_io->write_byte(A_Y8950_1 + 0, m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_Y8950_1 + 1, m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0xad:
				pulse_act_led(CT_YMZ280B);
				m_io->write_byte(A_YMZ280B_1 + 0, m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_YMZ280B_1 + 1, m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0xae:
			case 0xaf:
				pulse_act_led(CT_YMF262);
				space_out_write(A_YMF262_1);
				m_io->write_byte(A_YMF262_1 + 0 + ((code & 1) << 1), m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_YMF262_1 + 1 + ((code & 1) << 1), m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0xb0:
				pulse_act_led(CT_RF5C68);
				m_io->write_byte(A_RF5C68 + m_file->read_byte(m_pc + 1), m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0xb1:
				pulse_act_led(CT_RF5C164);
				m_io->write_byte(A_RF5C164 + m_file->read_byte(m_pc + 1), m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;

			case 0xb2:
			{
				pulse_act_led(CT_SEGA32X);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				uint8_t data = m_file->read_byte(m_pc + 2);

				if (m_sega32x_channel_hack >= 0)
				{
					if ((offset & 0xf0) == 0)
					{
						if (data != 0)
							m_sega32x_channel_hack = -1;
					}
					else
					{
						m_sega32x_channel_hack++;
						if (m_sega32x_channel_hack == 32)
						{
							osd_printf_error("bad rip detected, enabling sega32x channels\n");
							m_io16le->write_word(A_32X_PWM, 5);

							m_sega32x_channel_hack = -2;
						}
					}
				}

				m_io16le->write_word(A_32X_PWM + ((offset & 0xf0) >> 3), ((offset & 0xf) << 8) | data);
				m_pc += 3;
				break;
			}

			case 0xb3:
			{
				pulse_act_led(CT_GAMEBOY);
				uint8_t reg = m_file->read_byte(m_pc + 1);
				if (reg & 0x80)
					m_io->write_byte(A_GAMEBOY_1 + (reg & 0x7f), m_file->read_byte(m_pc + 2));
				else
					m_io->write_byte(A_GAMEBOY_0 + (reg & 0x7f), m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;
			}

			case 0xb4:
			{
				pulse_act_led(CT_NESAPU);
				uint8_t offset = m_file->read_byte(m_pc + 1);

				int index = offset & 0x80 ? 1 : 0;
				if (m_nes_apu_channel_hack[index] >= 0)
				{
					if ((offset & 0x7f) == 0x15)
					{
						if ((m_file->read_byte(m_pc + 2) & 0x1f) != 0)
							m_nes_apu_channel_hack[index] = -1;
					}
					else
					{
						m_nes_apu_channel_hack[index]++;
						if (m_nes_apu_channel_hack[index] == 32)
						{
							osd_printf_error("bad rip detected, enabling nesapu.%d channels\n", index);
							if (index)
								m_io->write_byte(A_NESAPU_1 + 0x15, 0x0f);
							else
								m_io->write_byte(A_NESAPU_0 + 0x15, 0x0f);

							m_nes_apu_channel_hack[index] = -2;
						}
					}
				}
				//else if ((offset & 0x7f) == 0x15 && m_nes_apu_channel_hack[index] == -2 && (m_file->read_byte(m_pc + 2) & 0x1f) != 0)
				//{
				//  osd_printf_error("bad rip false positive, late enabling nesapu.%d channels %x/%x\n", index, m_pc, m_io->read_dword(REG_SIZE));
				//  m_nes_apu_channel_hack[index] = -1;
				//}

				if (offset & 0x80)
					m_io->write_byte(A_NESAPU_1 + (offset & 0x7f), m_file->read_byte(m_pc + 2));
				else
					m_io->write_byte(A_NESAPU_0 + (offset & 0x7f), m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;
			}

			case 0xb5:
			{
				pulse_act_led(CT_MULTIPCM);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if (offset & 0x80)
					m_io->write_byte(A_MULTIPCM_1 + (offset & 0x7f), m_file->read_byte(m_pc + 2));
				else
					m_io->write_byte(A_MULTIPCM_0 + (offset & 0x7f), m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;
			}

			case 0xb6:
			{
				pulse_act_led(CT_UPD7759);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if (offset & 0x80)
					m_io->write_byte(A_UPD7759_1 + (offset & 0x7f), m_file->read_byte(m_pc + 2));
				else
					m_io->write_byte(A_UPD7759_0 + (offset & 0x7f), m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;
			}

			case 0xb7:
			{
				pulse_act_led(CT_OKIM6258);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if (offset & 0x80)
					m_io->write_byte(A_OKIM6258_1 + (offset & 0x7f), m_file->read_byte(m_pc + 2));
				else
					m_io->write_byte(A_OKIM6258_0 + (offset & 0x7f), m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;
			}

			case 0xb8:
			{
				pulse_act_led(CT_OKIM6295);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if (offset & 0x80)
					m_io->write_byte(A_OKIM6295_1 + (offset & 0x7f), m_file->read_byte(m_pc + 2));
				else
					m_io->write_byte(A_OKIM6295_0 + (offset & 0x7f), m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;
			}

			case 0xb9:
			{
				pulse_act_led(CT_C6280);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if ((offset & 0x7f) == 0)
					m_c6280_channel[BIT(offset, 7)] = m_file->read_byte(m_pc + 2);
				if (offset & 0x80)
					m_io->write_byte(A_C6280_1 + (offset & 0x7f), m_file->read_byte(m_pc + 2));
				else
					m_io->write_byte(A_C6280_0 + (offset & 0x7f), m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;
			}

			case 0xba:
			{
				pulse_act_led(CT_K053260);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if (offset & 0x80)
					m_io->write_byte(A_K053260_1 + (offset & 0x7f), m_file->read_byte(m_pc + 2));
				else
					m_io->write_byte(A_K053260_0 + (offset & 0x7f), m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;
			}

			case 0xbb:
			{
				pulse_act_led(CT_POKEY);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if (offset & 0x80)
					m_io->write_byte(A_POKEY_1 + (offset & 0x7f), m_file->read_byte(m_pc + 2));
				else
					m_io->write_byte(A_POKEY_0 + (offset & 0x7f), m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;
			}

			case 0xbc:
			{
				pulse_act_led(CT_WSWAN);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if (offset & 0x80)
					m_io->write_byte(A_WSWAN_1 + (offset & 0x7f) + 0x80, m_file->read_byte(m_pc + 2));
				else
					m_io->write_byte(A_WSWAN_0 + (offset & 0x7f) + 0x80, m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;
			}

			case 0xbd:
			{
				pulse_act_led(CT_SAA1099);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if (offset & 0x80)
				{
					m_io->write_byte(A_SAA1099_1 + 1, offset & 0x7f);
					m_io->write_byte(A_SAA1099_1 + 0, m_file->read_byte(m_pc + 2));
				}
				else
				{
					m_io->write_byte(A_SAA1099_0 + 1, offset & 0x7f);
					m_io->write_byte(A_SAA1099_0 + 0, m_file->read_byte(m_pc + 2));
				}
				m_pc += 3;
				break;
			}

			case 0xbe:
			{
				pulse_act_led(CT_ES5505);
				// TODO: es5505
				m_pc += 3;
				break;
			}

			case 0xbf:
			{
				pulse_act_led(CT_GA20);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if (offset & 0x80)
					m_io->write_byte(A_GA20_1 + (offset & 0x7f), m_file->read_byte(m_pc + 2));
				else
					m_io->write_byte(A_GA20_0 + (offset & 0x7f), m_file->read_byte(m_pc + 2));
				m_pc += 3;
				break;
			}

			case 0xc0:
			{
				pulse_act_led(CT_SEGAPCM);
				uint16_t offset = m_file->read_word(m_pc + 1);
				if (offset & 0x8000)
					m_io->write_byte(A_SEGAPCM_1 + (offset & 0x7fff), m_file->read_byte(m_pc + 3));
				else
					m_io->write_byte(A_SEGAPCM_0 + (offset & 0x7fff), m_file->read_byte(m_pc + 3));
				m_pc += 4;
				break;
			}

			case 0xc1:
				pulse_act_led(CT_RF5C68);
				m_io->write_byte(A_RF5C68_RAM + m_file->read_word(m_pc + 1), m_file->read_byte(m_pc + 3));
				m_pc += 4;
				break;

			case 0xc2:
				pulse_act_led(CT_RF5C164);
				m_io->write_byte(A_RF5C164_RAM + m_file->read_word(m_pc + 1), m_file->read_byte(m_pc + 3));
				m_pc += 4;
				break;

			case 0xc3:
			{
				pulse_act_led(CT_MULTIPCM);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if (offset & 0x80)
				{
					m_io->write_byte(A_MULTIPCM_1 + 4 + (offset & 0x7f), m_file->read_byte(m_pc + 3));
					m_io->write_byte(A_MULTIPCM_1 + 8 + (offset & 0x7f), m_file->read_byte(m_pc + 2));
				}
				else
				{
					m_io->write_byte(A_MULTIPCM_0 + 4 + (offset & 0x7f), m_file->read_byte(m_pc + 3));
					m_io->write_byte(A_MULTIPCM_0 + 8 + (offset & 0x7f), m_file->read_byte(m_pc + 2));
				}
				m_pc += 4;
				break;
			}

			case 0xc4:
				pulse_act_led(CT_QSOUND);
				m_io->write_byte(A_QSOUND + 0, m_file->read_byte(m_pc + 1));
				m_io->write_byte(A_QSOUND + 1, m_file->read_byte(m_pc + 2));
				m_io->write_byte(A_QSOUND + 2, m_file->read_byte(m_pc + 3));
				m_pc += 4;
				break;

			case 0xc5:
			{
				pulse_act_led(CT_SCSP);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if (offset & 0x80)
					m_io16be->write_byte(A_SCSP_1 + ((offset & 0x7f) << 8) + (m_file->read_byte(m_pc + 2) ^ 1), m_file->read_byte(m_pc + 3));
				else
					m_io16be->write_byte(A_SCSP_0 + ((offset & 0x7f) << 8) + (m_file->read_byte(m_pc + 2) ^ 1), m_file->read_byte(m_pc + 3));
				m_pc += 4;
				break;
			}

			case 0xc6:
			{
				pulse_act_led(CT_WSWAN);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if (offset & 0x80)
					m_io->write_byte(A_WSWAN_RAM_1 + ((offset & 0x7f) << 8) + m_file->read_byte(m_pc + 2), m_file->read_byte(m_pc + 3));
				else
					m_io->write_byte(A_WSWAN_RAM_0 + ((offset & 0x7f) << 8) + m_file->read_byte(m_pc + 2), m_file->read_byte(m_pc + 3));
				m_pc += 4;
				break;
			}

			case 0xc7:
			{
				pulse_act_led(CT_VSU_VUE);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if (offset & 0x80)
					m_io->write_byte(A_VSU_VUE_1 + ((offset & 0x7f) << 10) + (m_file->read_byte(m_pc + 2) << 2), m_file->read_byte(m_pc + 3));
				else
					m_io->write_byte(A_VSU_VUE_0 + ((offset & 0x7f) << 10) + (m_file->read_byte(m_pc + 2) << 2), m_file->read_byte(m_pc + 3));
				m_pc += 4;
				break;
			}

			case 0xc8:
			{
				pulse_act_led(CT_X1_010);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if (offset & 0x80)
					m_io->write_byte(A_X1_010_1 + ((offset & 0x7f) << 8) + m_file->read_byte(m_pc + 2), m_file->read_byte(m_pc + 3));
				else
					m_io->write_byte(A_X1_010_0 + ((offset & 0x7f) << 8) + m_file->read_byte(m_pc + 2), m_file->read_byte(m_pc + 3));
				m_pc += 4;
				break;
			}

			case 0xd0:
			{
				pulse_act_led(CT_YMF278B);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if (offset & 0x80)
				{
					space_out_write(A_YMF278B_1);
					m_io->write_byte(A_YMF278B_1 + ((offset & 0x7f) << 1) + 0, m_file->read_byte(m_pc + 2));
					m_io->write_byte(A_YMF278B_1 + ((offset & 0x7f) << 1) + 1, m_file->read_byte(m_pc + 3));
				}
				else
				{
					space_out_write(A_YMF278B_0);
					m_io->write_byte(A_YMF278B_0 + ((offset & 0x7f) << 1) + 0, m_file->read_byte(m_pc + 2));
					m_io->write_byte(A_YMF278B_0 + ((offset & 0x7f) << 1) + 1, m_file->read_byte(m_pc + 3));
				}
				m_pc += 4;
				break;
			}

			case 0xd1:
			{
				pulse_act_led(CT_YMF271);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if (offset & 0x80)
				{
					m_io->write_byte(A_YMF271_1 + ((offset & 0x7f) << 1) + 0, m_file->read_byte(m_pc + 2));
					m_io->write_byte(A_YMF271_1 + ((offset & 0x7f) << 1) + 1, m_file->read_byte(m_pc + 3));
				}
				else
				{
					m_io->write_byte(A_YMF271_0 + ((offset & 0x7f) << 1) + 0, m_file->read_byte(m_pc + 2));
					m_io->write_byte(A_YMF271_0 + ((offset & 0x7f) << 1) + 1, m_file->read_byte(m_pc + 3));
				}
				m_pc += 4;
				break;
			}

			case 0xd2:
			{
				pulse_act_led(CT_K051649);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if (offset & 0x80)
				{
					m_io->write_byte(A_K051649_1 + ((offset & 0x7f) << 1) + 0, m_file->read_byte(m_pc + 2));
					m_io->write_byte(A_K051649_1 + ((offset & 0x7f) << 1) + 1, m_file->read_byte(m_pc + 3));
				}
				else
				{
					m_io->write_byte(A_K051649_0 + ((offset & 0x7f) << 1) + 0, m_file->read_byte(m_pc + 2));
					m_io->write_byte(A_K051649_0 + ((offset & 0x7f) << 1) + 1, m_file->read_byte(m_pc + 3));
				}
				m_pc += 4;
				break;
			}

			case 0xd3:
			{
				pulse_act_led(CT_K054539);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if (offset & 0x80)
					m_io->write_byte(A_K054539_1 + ((offset & 0x7f) << 8) + m_file->read_byte(m_pc + 2), m_file->read_byte(m_pc + 3));
				else
					m_io->write_byte(A_K054539_0 + ((offset & 0x7f) << 8) + m_file->read_byte(m_pc + 2), m_file->read_byte(m_pc + 3));
				m_pc += 4;
				break;
			}

			case 0xd4:
			{
				pulse_act_led(CT_C140);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if (offset & 0x80)
					m_io->write_byte(A_C140_1 + ((offset & 0x7f) << 8) + m_file->read_byte(m_pc + 2), m_file->read_byte(m_pc + 3));
				else
					m_io->write_byte(A_C140_0 + ((offset & 0x7f) << 8) + m_file->read_byte(m_pc + 2), m_file->read_byte(m_pc + 3));
				m_pc += 4;
				break;
			}

			case 0xd5:
			{
				pulse_act_led(CT_ES5503);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				if (offset & 0x80)
					m_io->write_byte(A_ES5503_1 + ((offset & 0x7f) << 8) + m_file->read_byte(m_pc + 2), m_file->read_byte(m_pc + 3));
				else
					m_io->write_byte(A_ES5503_0 + ((offset & 0x7f) << 8) + m_file->read_byte(m_pc + 2), m_file->read_byte(m_pc + 3));
				m_pc += 4;
				break;
			}

			case 0xd6:
			{
				pulse_act_led(CT_ES5505);
				// TODO: es5505
				m_pc += 4;
				break;
			}

			case 0xe0:
				pulse_act_led(CT_YM2612);
				m_ym2612_stream_offset = m_file->read_dword(m_pc + 1);
				m_pc += 5;
				break;

			case 0xe1:
			{
				pulse_act_led(CT_C352);
				uint8_t offset = m_file->read_byte(m_pc + 1);
				uint32_t addr = ((offset & 0x7f) << 8) + m_file->read_byte(m_pc + 2);
				uint16_t data = (m_file->read_byte(m_pc + 3) << 8) + m_file->read_byte(m_pc + 4);
				if (offset & 0x80)
					m_io16le->write_word(A_C352_1 + (addr << 1), data);
				else
					m_io16le->write_word(A_C352_0 + (addr << 1), data);
				m_pc += 5;
				break;
			}

			default:
				osd_printf_error("unhandled code %02x (%02x %02x %02x %02x)\n", code, m_file->read_byte(m_pc + 1), m_file->read_byte(m_pc + 2), m_file->read_byte(m_pc + 3), m_file->read_byte(m_pc + 4));

				if (machine().debug_flags & DEBUG_FLAG_ENABLED)
					debugger_instruction_hook(m_pc);

				m_state = DONE;
				decrement_icount(m_icount);
				break;
			}
			break;
		}
		case DONE:
		{
			machine().sound().system_mute(1);
			decrement_icount(m_icount);
			break;
		}
		}
	}
}

void vgmplay_device::execute_set_input(int inputnum, int state)
{
}

device_memory_interface::space_config_vector vgmplay_device::memory_space_config() const
{
	return space_config_vector
	{
		std::make_pair(AS_PROGRAM, &m_file_config),
		std::make_pair(AS_IO,      &m_io_config),
		std::make_pair(AS_IO16LE,  &m_io16le_config),
		std::make_pair(AS_IO16BE,  &m_io16be_config),
	};
}

void vgmplay_device::state_import(const device_state_entry &entry)
{
}

void vgmplay_device::state_export(const device_state_entry &entry)
{
}

void vgmplay_device::state_string_export(const device_state_entry &entry, std::string &str) const
{
}

std::unique_ptr<util::disasm_interface> vgmplay_device::create_disassembler()
{
	return std::make_unique<vgmplay_disassembler>();
}

uint32_t vgmplay_disassembler::opcode_alignment() const
{
	return 1;
}

offs_t vgmplay_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params)
{
	static const char *const basic_types[8] =
	{
		"ym2612.%d pcm",
		"rf5c68 pcm",
		"rf5c164 pcm",
		"sega32x.%d pcm",
		"okim6258.%d adpcm",
		"huc6280.%d pcm",
		"scsp.%d pcm",
		"nesapu.%d dpcm",
	};

	switch (opcodes.r8(pc))
	{
	case 0x30:
		util::stream_format(stream, "psg.1 write %02x", opcodes.r8(pc + 1));
		return 2 | SUPPORTED;

	case 0x3f:
		util::stream_format(stream, "psg.1 r06 = %02x", opcodes.r8(pc + 1));
		return 2 | SUPPORTED;

	case 0x4f:
		util::stream_format(stream, "psg.0 r06 = %02x", opcodes.r8(pc + 1));
		return 2 | SUPPORTED;

	case 0x50:
		util::stream_format(stream, "psg.0 write %02x", opcodes.r8(pc + 1));
		return 2 | SUPPORTED;

	case 0x51:
		util::stream_format(stream, "ym2413.0 r%02x = %02x", opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0x52:
	case 0x53:
		util::stream_format(stream, "ym2612.0 %d r%02x = %02x", opcodes.r8(pc) & 1, opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0x54:
		util::stream_format(stream, "ym2151.0 r%02x = %02x", opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0x55:
		util::stream_format(stream, "ym2203.0 r%02x = %02x", opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0x56:
	case 0x57:
		util::stream_format(stream, "ym2608.0 %d r%02x = %02x", opcodes.r8(pc) & 1, opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0x58:
	case 0x59:
		util::stream_format(stream, "ym2610.0 %d r%02x = %02x", opcodes.r8(pc) & 1, opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0x5a:
		util::stream_format(stream, "ym3812.0 r%02x = %02x", opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0x5b:
		util::stream_format(stream, "ym3526.0 r%02x = %02x", opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0x5c:
		util::stream_format(stream, "y8950.0 r%02x = %02x", opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0x5d:
		util::stream_format(stream, "ymz280b.0 r%02x = %02x", opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0x5e:
	case 0x5f:
		util::stream_format(stream, "ymf262.0 %d r%02x = %02x", opcodes.r8(pc) & 1, opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0x61:
	{
		uint32_t duration = opcodes.r8(pc + 1) | (opcodes.r8(pc + 2) << 8);
		util::stream_format(stream, "wait %d", duration);
		return 3 | SUPPORTED;
	}

	case 0x62:
		util::stream_format(stream, "wait 735");
		return 1 | SUPPORTED;

	case 0x63:
		util::stream_format(stream, "wait 882");
		return 1 | SUPPORTED;

	case 0x66:
		util::stream_format(stream, "end");
		return 1 | SUPPORTED;

	case 0x67:
	{
		static const char *const rom_types[20] =
		{
			"segapcm.%d rom",
			"ym2608.%d delta-t rom",
			"ym2610.%d adpcm rom",
			"ym2610.%d delta-t rom",
			"ymf278b.%d rom",
			"ymf271.%d rom",
			"ymz280b.%d rom",
			"ymf278b.%d ram",
			"y8950.%d delta-t rom",
			"multipcm.%d rom",
			"upd7759.%d rom",
			"okim6295.%d rom",
			"k054539.%d rom",
			"c140.%d rom",
			"k053260.%d rom",
			"qsound.%d rom",
			"es5505.%d rom",
			"x1-010.%d rom",
			"c352.%d rom",
			"ga20.%d rom"
		};

		static const char *const small_ram_types[3] =
		{
			"rf5c68 ram",
			"rf5c164 ram",
			"nesapu.%d ram"
		};

		static const char *const large_ram_types[2] =
		{
			"scsp.%d ram",
			"es5503.%d ram"
		};

		uint8_t type = opcodes.r8(pc + 2);
		uint32_t size = opcodes.r32(pc + 3);
		int second = (size & 0x80000000) ? 1 : 0;
		size &= 0x7fffffff;

		if (type < 0x8)
		{
			util::stream_format(stream, basic_types[type], second);  util::stream_format(stream, " data-block %x", size);
		}
		else if (type < 0x40)
			util::stream_format(stream, "unknown%02x.%d stream data-block %x", type, second, size);
		else if (type < 0x48)
		{
			util::stream_format(stream, basic_types[type - 0x40], second); util::stream_format(stream, " comp. data-block %x", size);
		}
		else if (type < 0x7f)
			util::stream_format(stream, "unknown%02x.%d stream comp. data-block %x", type - 0x40, second, size);
		else if (type == 0x7f)
			util::stream_format(stream, "decomp-table %x, %02x/%02x", size, opcodes.r8(pc + 7), opcodes.r8(pc + 8));
		else if (type < 0x94)
		{
			util::stream_format(stream, rom_types[type - 0x80], second); util::stream_format(stream, " data-block %x", size);
		}
		else if (type < 0xc0)
			util::stream_format(stream, "unknown%02x.%d rom data-block %x", type - 0x80, second, size);
		else if (type < 0xc3)
		{
			util::stream_format(stream, small_ram_types[type - 0xc0], second);  util::stream_format(stream, " data-block %x", size);
		}
		else if (type < 0xe0)
			util::stream_format(stream, "unknown%02x.%d small ram data-block %x", type - 0xc0, second, size);
		else if (type < 0xe2)
		{
			util::stream_format(stream, large_ram_types[type - 0xe0], second); util::stream_format(stream, " data-block %x", size);
		}
		else
			util::stream_format(stream, "unknown%02x.%d large ram data-block %x", type - 0xe0, second, size);
		return (7 + size) | SUPPORTED;
	}

	case 0x68:
	{
		uint8_t type = opcodes.r8(pc + 2);
		uint32_t src = opcodes.r32(pc + 3) & 0xffffff;
		uint32_t dst = opcodes.r32(pc + 6) & 0xffffff;
		uint32_t size = opcodes.r32(pc + 9) & 0xffffff;
		if (size == 0) size = 0x01000000;
		int second = (type & 0x80) ? 1 : 0;

		if (type < 8)
		{
			util::stream_format(stream, basic_types[type], second); util::stream_format(stream, " write src %x dst %x size %x\n", src, dst, size);
		}
		else
			util::stream_format(stream, "unknown%02x.%d pcm write src %x dst %x size %x\n", type, second, src, dst, size);

		return 12 | SUPPORTED;
	}

	case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
	case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f:
		util::stream_format(stream, "wait %d", 1 + (opcodes.r8(pc) & 0x0f));
		return 1 | SUPPORTED;

	case 0x80:
		util::stream_format(stream, "ym2612.0 r2a = rom++");
		return 1 | SUPPORTED;

	case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87:
	case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f:
		util::stream_format(stream, "ym2612.0 r2a = rom++; wait %d", opcodes.r8(pc) & 0xf);
		return 1 | SUPPORTED;

	case 0x90:
		util::stream_format(stream, "stream control %02x %02x %02x %02x", opcodes.r8(pc + 1), opcodes.r8(pc + 2), opcodes.r8(pc + 3), opcodes.r8(pc + 4));
		return 5 | SUPPORTED;

	case 0x91:
		util::stream_format(stream, "stream data %02x %02x %02x %02x", opcodes.r8(pc + 1), opcodes.r8(pc + 2), opcodes.r8(pc + 3), opcodes.r8(pc + 4));
		return 5 | SUPPORTED;

	case 0x92:
		util::stream_format(stream, "stream frequency %02x %d", opcodes.r8(pc + 1), opcodes.r32(pc + 2));
		return 6 | SUPPORTED;

	case 0x93:
		util::stream_format(stream, "stream start %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", opcodes.r8(pc + 1), opcodes.r8(pc + 2), opcodes.r8(pc + 3), opcodes.r8(pc + 4), opcodes.r8(pc + 5), opcodes.r8(pc + 6), opcodes.r8(pc + 7), opcodes.r8(pc + 8), opcodes.r8(pc + 9), opcodes.r8(pc + 10));
		return 11 | SUPPORTED;

	case 0x94:
		util::stream_format(stream, "stream stop %02x", opcodes.r8(pc + 1));
		return 2 | SUPPORTED;

	case 0x95:
		util::stream_format(stream, "stream start short %02x %02x %02x %02x", opcodes.r8(pc + 1), opcodes.r8(pc + 2), opcodes.r8(pc + 3), opcodes.r8(pc + 4));
		return 5 | SUPPORTED;

	case 0xa0:
		util::stream_format(stream, "ay8910.%d r%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xa1:
		util::stream_format(stream, "ym2413.1 r%02x = %02x", opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xa2:
	case 0xa3:
		util::stream_format(stream, "ym2612.1 %d r%02x = %02x", opcodes.r8(pc) & 1, opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xa4:
		util::stream_format(stream, "ym2151.1 r%02x = %02x", opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xa5:
		util::stream_format(stream, "ym2203.1 r%02x = %02x", opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xa6:
	case 0xa7:
		util::stream_format(stream, "ym2608.1 %d r%02x = %02x", opcodes.r8(pc) & 1, opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xa8:
	case 0xa9:
		util::stream_format(stream, "ym2610.1 %d r%02x = %02x", opcodes.r8(pc) & 1, opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xaa:
		util::stream_format(stream, "ym3812.1 r%02x = %02x", opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xab:
		util::stream_format(stream, "ym3526.1 r%02x = %02x", opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xac:
		util::stream_format(stream, "y8950.1 r%02x = %02x", opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xad:
		util::stream_format(stream, "ymz280b.1 r%02x = %02x", opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xae:
	case 0xaf:
		util::stream_format(stream, "ymf262.1 %d r%02x = %02x", opcodes.r8(pc) & 1, opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xb0:
		util::stream_format(stream, "rf5c68 r%02x = %02x", opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xb1:
		util::stream_format(stream, "rf5c164 r%02x = %02x", opcodes.r8(pc + 1), opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xb2:
		util::stream_format(stream, "32x_pwm r%x = %03x", opcodes.r8(pc + 1) >> 4, opcodes.r8(pc + 2) | ((opcodes.r8(pc + 1) & 0xf) << 8));
		return 3 | SUPPORTED;

	case 0xb3:
		util::stream_format(stream, "dmg.%d r%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xb4:
		util::stream_format(stream, "nesapu.%d r%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xb5:
		util::stream_format(stream, "multipcm.%d r%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xb6:
		util::stream_format(stream, "upd7759.%d r%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xb7:
		util::stream_format(stream, "okim6258.%d r%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xb8:
		util::stream_format(stream, "okim6295.%d r%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xb9:
		util::stream_format(stream, "huc6280.%d r%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xba:
		util::stream_format(stream, "k053260.%d r%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xbb:
		util::stream_format(stream, "pokey.%d r%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xbc:
		util::stream_format(stream, "wonderswan.%d r%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xbd:
		util::stream_format(stream, "saa1099.%d r%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xbe:
		util::stream_format(stream, "es5505.%d r%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xbf:
		util::stream_format(stream, "ga20.%d r%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2));
		return 3 | SUPPORTED;

	case 0xc0:
		util::stream_format(stream, "segapcm.%d %04x = %02x", BIT(opcodes.r8(pc + 2), 7), opcodes.r8(pc + 1) | ((opcodes.r8(pc + 2) & 0x7f) << 8), opcodes.r8(pc + 3));
		return 4 | SUPPORTED;

	case 0xc1:
		util::stream_format(stream, "rf5c68 %04x = %02x", opcodes.r8(pc + 1) | (opcodes.r8(pc + 2) << 8), opcodes.r8(pc + 3));
		return 4 | SUPPORTED;

	case 0xc2:
		util::stream_format(stream, "rf5c164 %04x = %02x", opcodes.r8(pc + 1) | (opcodes.r8(pc + 2) << 8), opcodes.r8(pc + 3));
		return 4 | SUPPORTED;

	case 0xc3:
		util::stream_format(stream, "multipcm.%d c%02x.off = %04x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2) | (opcodes.r8(pc + 3) << 8));
		return 4 | SUPPORTED;

	case 0xc4:
		util::stream_format(stream, "qsound %02x = %04x", opcodes.r8(pc + 3), opcodes.r8(pc + 2) | (opcodes.r8(pc + 1) << 8));
		return 4 | SUPPORTED;

	case 0xc5:
		util::stream_format(stream, "scsp.%d %04x = %02x", BIT(opcodes.r8(pc + 1), 7), ((opcodes.r8(pc + 1) & 0x7f) << 8) | opcodes.r8(pc + 2), opcodes.r8(pc + 3));
		return 4 | SUPPORTED;

	case 0xc6:
		util::stream_format(stream, "wswan.%d %04x = %02x", BIT(opcodes.r8(pc + 1), 7), ((opcodes.r8(pc + 1) & 0x7f) << 8) | opcodes.r8(pc + 2), opcodes.r8(pc + 3));
		return 4 | SUPPORTED;

	case 0xc7:
		util::stream_format(stream, "vsu-vue.%d %04x = %02x", BIT(opcodes.r8(pc + 1), 7), ((opcodes.r8(pc + 1) & 0x7f) << 8) | opcodes.r8(pc + 2), opcodes.r8(pc + 3));
		return 4 | SUPPORTED;

	case 0xc8:
		util::stream_format(stream, "x1-010.%d %04x = %02x", BIT(opcodes.r8(pc + 1), 7), ((opcodes.r8(pc + 1) & 0x7f) << 8) | opcodes.r8(pc + 2), opcodes.r8(pc + 3));
		return 4 | SUPPORTED;

	case 0xd0:
		util::stream_format(stream, "ymf278b.%d r%02x.%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2), opcodes.r8(pc + 3));
		return 4 | SUPPORTED;

	case 0xd1:
		util::stream_format(stream, "ymf271.%d r%02x.%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1), opcodes.r8(pc + 2), opcodes.r8(pc + 3));
		return 4 | SUPPORTED;

	case 0xd2:
		util::stream_format(stream, "scc1.%d r%02x.%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2), opcodes.r8(pc + 3));
		return 4 | SUPPORTED;

	case 0xd3:
		util::stream_format(stream, "k054539.%d r%02x.%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2), opcodes.r8(pc + 3));
		return 4 | SUPPORTED;

	case 0xd4:
		util::stream_format(stream, "c140.%d r%02x.%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2), opcodes.r8(pc + 3));
		return 4 | SUPPORTED;

	case 0xd5:
		util::stream_format(stream, "ess5503.%d r%02x.%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2), opcodes.r8(pc + 3));
		return 4 | SUPPORTED;

	case 0xd6:
		util::stream_format(stream, "ess5505.%d r%02x.%02x = %02x", BIT(opcodes.r8(pc + 1), 7), opcodes.r8(pc + 1) & 0x7f, opcodes.r8(pc + 2), opcodes.r8(pc + 3));
		return 4 | SUPPORTED;

	case 0xe0:
	{
		uint32_t off = opcodes.r8(pc + 1) | (opcodes.r8(pc + 2) << 8) | (opcodes.r8(pc + 3) << 16) | (opcodes.r8(pc + 4) << 24);
		util::stream_format(stream, "ym2612 offset = %x", off);
		return 5 | SUPPORTED;
	}

	case 0xe1:
	{
		uint16_t addr = (opcodes.r8(pc + 1) << 8) | opcodes.r8(pc + 2);
		uint16_t data = (opcodes.r8(pc + 3) << 8) | opcodes.r8(pc + 4);
		util::stream_format(stream, "c352 r%04x = %04x", addr, data);
		return 5 | SUPPORTED;
	}

	default:
		util::stream_format(stream, "?? %02x", opcodes.r8(pc));
		return 1 | SUPPORTED;
	}
}

uint8_t vgmplay_device::rom_r(int index, uint8_t type, offs_t offset)
{
	for (const auto &b : m_rom_blocks[index][type - 0x80])
	{
		if (offset >= b.start_address && offset <= b.end_address)
		{
			return b.data[offset - b.start_address];
		}
	}
	return 0;
}

template<int Index>
uint8_t vgmplay_device::segapcm_rom_r(offs_t offset)
{
	return rom_r(Index, 0x80, offset);
}

template<int Index>
uint8_t vgmplay_device::ym2608_rom_r(offs_t offset)
{
	return rom_r(Index, 0x81, offset);
}

template<int Index>
uint8_t vgmplay_device::ym2610_adpcm_a_rom_r(offs_t offset)
{
	return rom_r(Index, 0x82, offset);
}

template<int Index>
uint8_t vgmplay_device::ym2610_adpcm_b_rom_r(offs_t offset)
{
	return rom_r(Index, 0x83, offset);
}

template<int Index>
uint8_t vgmplay_device::ymf278b_rom_r(offs_t offset)
{
	return rom_r(Index, 0x84, offset);
}

template<int Index>
uint8_t vgmplay_device::ymf271_rom_r(offs_t offset)
{
	return rom_r(Index, 0x85, offset);
}

template<int Index>
uint8_t vgmplay_device::ymz280b_rom_r(offs_t offset)
{
	return rom_r(Index, 0x86, offset);
}

template<int Index>
uint8_t vgmplay_device::y8950_rom_r(offs_t offset)
{
	return rom_r(Index, 0x88, offset);
}

template<int Index>
uint8_t vgmplay_device::multipcm_rom_r(offs_t offset)
{
	if (m_multipcm_banked[Index] == 1)
	{
		offset &= 0x1fffff;
		if (offset & 0x100000)
		{
			if (m_multipcm_bank_l[Index] == m_multipcm_bank_r[Index])
			{
				offset = ((m_multipcm_bank_r[Index] & ~0xf) << 16) | (offset & 0xfffff);
			}
			else
			{
				if (offset & 0x80000)
				{
					offset = ((m_multipcm_bank_l[Index] & ~0x7) << 16) | (offset & 0x7ffff);
				}
				else
				{
					offset = ((m_multipcm_bank_r[Index] & ~0x7) << 16) | (offset & 0x7ffff);
				}
			}
		}
	}
	return rom_r(Index, 0x89, offset);
}

template<int Index>
uint8_t vgmplay_device::upd7759_rom_r(offs_t offset)
{
	return rom_r(Index, 0x8a, m_upd7759_bank[Index] | offset);
}

template<int Index>
uint8_t vgmplay_device::okim6295_rom_r(offs_t offset)
{
	if (m_okim6295_nmk112_enable[Index])
	{
		if ((offset < 0x400) && (m_okim6295_nmk112_enable[Index] & 0x80))
		{
			offset = (m_okim6295_nmk112_bank[Index][(offset >> 8) & 0x3] << 16) | (offset & 0x3ff);
		}
		else
		{
			offset = (m_okim6295_nmk112_bank[Index][(offset >> 16) & 0x3] << 16) | (offset & 0xffff);
		}
	}
	else
	{
		offset = (m_okim6295_bank[Index] * 0x40000) | offset;
	}
	return rom_r(Index, 0x8b, offset);
}

template<int Index>
uint8_t vgmplay_device::k054539_rom_r(offs_t offset)
{
	return rom_r(Index, 0x8c, offset);
}

template<int Index>
uint16_t vgmplay_device::c140_rom_r(offs_t offset)
{
	switch (m_c140_bank[Index])
	{
	case C140_SYSTEM2:
		offset = ((offset & 0x200000) >> 2) | (offset & 0x7ffff);
		return rom_r(Index, 0x8d, offset) << 8; // high 8 bit only
	case C140_SYSTEM21:
		offset = ((offset & 0x300000) >> 1) | (offset & 0x7ffff);
		return rom_r(Index, 0x8d, offset) << 8; // high 8 bit only
	case C140_ASIC219:
		return 0; // c140 not used in this mode
	default:
		return (rom_r(Index, 0x8d, offset * 2 + 1) << 8) | rom_r(Index, 0x8d, offset * 2); // 8 bit sample
	}
	return 0;
}

template<int Index>
uint16_t vgmplay_device::c219_rom_r(offs_t offset)
{
	if (m_c140_bank[Index] == C140_ASIC219)
		return (rom_r(Index, 0x8d, offset * 2 + 1) << 8) | rom_r(Index, 0x8d, offset * 2); // 8 bit sample

	return 0;
}

template<int Index>
uint8_t vgmplay_device::k053260_rom_r(offs_t offset)
{
	return rom_r(Index, 0x8e, offset);
}

template<int Index>
uint8_t vgmplay_device::qsound_rom_r(offs_t offset)
{
	return rom_r(Index, 0x8f, offset);
}

template<int Index>
uint8_t vgmplay_device::es5505_rom_r(offs_t offset)
{
	return rom_r(Index, 0x90, offset);
}

template<int Index>
uint8_t vgmplay_device::x1_010_rom_r(offs_t offset)
{
	return rom_r(Index, 0x91, offset);
}

template<int Index>
uint8_t vgmplay_device::c352_rom_r(offs_t offset)
{
	return rom_r(Index, 0x92, offset);
}

template<int Index>
uint8_t vgmplay_device::ga20_rom_r(offs_t offset)
{
	return rom_r(Index, 0x93, offset);
}

vgmplay_state::vgmplay_state(const machine_config &mconfig, device_type type, const char *tag)
	: driver_device(mconfig, type, tag)
	, m_vgmplay(*this, "vgmplay")
	, m_viz(*this, "mixer")
	, m_speaker(*this, "speaker")
	, m_sn76489(*this, "sn76489.%d", 0)
	, m_ym2413(*this, "ym2413.%d", 0)
	, m_ym2612(*this, "ym2612.%d", 0)
	, m_ym2151(*this, "ym2151.%d", 0)
	, m_segapcm(*this, "segapcm.%d", 0)
	, m_rf5c68(*this, "rf5c68")
	, m_ym2203(*this, "ym2203.%d", 0)
	, m_ym2608(*this, "ym2608.%d", 0)
	, m_ym2610(*this, "ym2610.%d", 0)
	, m_ym3812(*this, "ym3812.%d", 0)
	, m_ym3526(*this, "ym3526.%d", 0)
	, m_y8950(*this, "y8950.%d", 0)
	, m_ymf262(*this, "ymf262.%d", 0)
	, m_ymf278b(*this, "ymf278b.%d", 0)
	, m_ymf271(*this, "ymf271.%d", 0)
	, m_ymz280b(*this, "ymz280b.%d", 0)
	, m_rf5c164(*this, "rf5c164")
	, m_sega32x(*this, "sega32x")
	, m_ay8910(*this, "ay8910.%d", 0)
	, m_dmg(*this, "dmg.%d", 0)
	, m_nescpu(*this, "nescpu.%d", 0)
	, m_multipcm(*this, "multipcm.%d", 0)
	, m_upd7759(*this, "upd7759.%d", 0)
	, m_okim6258(*this, "okim6258.%d", 0)
	, m_okim6295(*this, "okim6295.%d", 0)
	, m_k051649(*this, "k051649.%d", 0)
	, m_k054539(*this, "k054539.%d", 0)
	, m_huc6280(*this, "huc6280.%d", 0)
	, m_c140(*this, "c140.%d", 0)
	, m_c219(*this, "c219.%d", 0)
	, m_k053260(*this, "k053260.%d", 0)
	, m_pokey(*this, "pokey.%d", 0)
	, m_qsound(*this, "qsound")
	, m_scsp(*this, "scsp.%d", 0)
	, m_wswan(*this, "wswan.%d", 0)
	, m_vsu_vue(*this, "vsu_vue.%d", 0)
	, m_saa1099(*this, "saa1099.%d", 0)
	, m_es5503(*this, "es5503.%d", 0)
	, m_es5505(*this, "es5505.%d", 0)
	, m_x1_010(*this, "x1_010.%d", 0)
	, m_c352(*this, "c352.%d", 0)
	, m_ga20(*this, "ga20.%d", 0)
{
	std::fill(std::begin(m_upd7759_md), std::end(m_upd7759_md), 0);
	std::fill(std::begin(m_upd7759_reset), std::end(m_upd7759_drq), 0);
	std::fill(std::begin(m_upd7759_drq), std::end(m_upd7759_drq), 0);
}

void vgmplay_state::machine_start()
{
	save_item(NAME(m_held_clock));
}

uint32_t vgmplay_state::r32(int off) const
{
	if (off + 3 < int(m_file_data.size()))
		return m_file_data[off] | (m_file_data[off + 1] << 8) | (m_file_data[off + 2] << 16) | (m_file_data[off + 3] << 24);
	return 0;
}

uint8_t vgmplay_state::r8(int off) const
{
	if (off < int(m_file_data.size()))
		return m_file_data[off];
	return 0;
}

static const ay8910_device::psg_type_t vgm_ay8910_type(uint8_t vgm_type)
{
	return (vgm_type & 0x10) ? ay8910_device::PSG_TYPE_YM : ay8910_device::PSG_TYPE_AY;
}

static const uint8_t vgm_ay8910_flags(uint8_t vgm_flags)
{
	uint8_t flags = 0;
	if (vgm_flags & 1) flags |= AY8910_LEGACY_OUTPUT;
	if (vgm_flags & 2) flags |= AY8910_SINGLE_OUTPUT;
	if (vgm_flags & 4) flags |= AY8910_DISCRETE_OUTPUT;
	return flags;
}

static const C140_TYPE c140_bank_type(uint8_t vgm_type)
{
	switch (vgm_type)
	{
	case 0:
	default:
		return C140_SYSTEM2;
	case 1:
		return C140_SYSTEM21;
	case 2:
		return C140_ASIC219;
	}
}

void vgmplay_device::set_c140_bank_type(int index, C140_TYPE type)
{
	m_c140_bank[index] = type;
}

QUICKLOAD_LOAD_MEMBER(vgmplay_state::load_file)
{
	m_vgmplay->stop();

	m_file_data.resize(image.length());

	if (image.length() == 0)
	{
		return std::make_pair(image_error::INVALIDLENGTH, "Empty file");
	}
	else if(image.fread(&m_file_data[0], image.length()) != image.length())
	{
		m_file_data.clear();
		return std::make_pair(image_error::UNSPECIFIED, "Error reading file");
	}

	// Decompress gzip-compressed files (aka vgz)
	if(m_file_data[0] == 0x1f && m_file_data[1] == 0x8b)
	{
		std::vector<uint8_t> decomp;
		int bs = m_file_data.size();
		decomp.resize(2*bs);
		z_stream str;
		str.zalloc = nullptr;
		str.zfree = nullptr;
		str.opaque = nullptr;
		str.data_type = 0;
		str.next_in = &m_file_data[0];
		str.avail_in = m_file_data.size();
		str.total_in = 0;
		str.total_out = 0;
		int err = inflateInit2(&str, 31);
		if(err != Z_OK)
		{
			m_file_data.clear();
			return std::make_pair(image_error::INVALIDIMAGE, "File has gzip header but is not a gzip file");
		}
		do
		{
			if(str.total_out >= decomp.size())
				decomp.resize(decomp.size() + bs);
			str.next_out = &decomp[str.total_out];
			str.avail_out = decomp.size() - str.total_out;
			err = inflate(&str, Z_SYNC_FLUSH);
		} while(err == Z_OK);

		if(err != Z_STREAM_END)
		{
			m_file_data.clear();
			return std::make_pair(image_error::INVALIDIMAGE, "Error decompressing gzip file");
		}
		m_file_data.resize(str.total_out);
		memcpy(&m_file_data[0], &decomp[0], str.total_out);
	}

	if(m_file_data.size() < 0x40 || r32(0) != 0x206d6756)
	{
		m_file_data.clear();
		return std::make_pair(image_error::INVALIDIMAGE, "Invalid VGM file header");
	}

	uint32_t const version = r32(8);
	logerror("File version %x.%02x\n", version >> 8, version & 0xff);

	uint32_t data_start = version >= 0x150 ? r32(0x34) + 0x34 : 0x40;
	int volbyte = version >= 0x160 && data_start >= 0x7d ? r8(0x7c) : 0;
	logerror("Volume %02x\n", volbyte);

	if (volbyte == 0xc1) // 0x00~0xc0 0~192, 0xc1 -64, 0xc2~0xff -62~-1
		volbyte = -0x40;
	else if (volbyte > 0xc1)
		volbyte -= 0x100;

	float const volume = version >= 0x160 && data_start >= 0x7d ? powf(2.0f, float(volbyte) / float(0x20)) : 1.0f;

	uint32_t const extra_header_start = version >= 0x170 && data_start >= 0xc0 && r32(0xbc) ? r32(0xbc) + 0xbc : 0;
	uint32_t const header_size = extra_header_start ? extra_header_start : data_start;

	uint32_t const extra_header_size = extra_header_start ? r32(extra_header_start) : 0;
	uint32_t const chip_clock_start = extra_header_size >= 4 && r32(extra_header_start + 4) ? r32(extra_header_start + 4) + extra_header_start + 4: 0;
	uint32_t const chip_volume_start = extra_header_size >= 8 && r32(extra_header_start + 8) ? r32(extra_header_start + 8) + extra_header_start + 8 : 0;

	if (chip_volume_start != 0)
		osd_printf_warning("Warning: file has unsupported chip volumes\n");

	const auto setup_device(
			[this, version, volume, header_size, chip_clock_start] (
					device_t &device,
					int chip_num,
					vgm_chip chip_type,
					uint32_t offset,
					uint32_t min_version = 0)
			{
				uint32_t c = 0;
				float chip_volume = volume;
				bool has_2chip = false;

				if (min_version <= version && offset + 4 <= header_size && (chip_num == 0 || (r32(offset) & 0x40000000) != 0))
				{
					c =  r32(offset);
					has_2chip = (c & 0x40000000) != 0;

					if (chip_clock_start && chip_num != 0)
						for (auto i(0); i < r8(chip_clock_start); i++)
						{
							if (r8(chip_clock_start + 1 + (i * 5)) == chip_type)
							{
								c = r32(chip_clock_start + 2 + (i * 5));
								break;
							}
						}
				}

				if (has_2chip)
				{
					chip_volume /= 2.0f;
				}
				device.set_unscaled_clock(c & ~0xc0000000);
				if (device.unscaled_clock() != 0)
					dynamic_cast<device_sound_interface *>(&device)->set_output_gain(ALL_OUTPUTS, chip_volume);
				else
					dynamic_cast<device_sound_interface *>(&device)->set_output_gain(ALL_OUTPUTS, 0);

				return (c & 0x80000000) != 0;
			});

	// Parse clocks
	if (setup_device(*m_sn76489[0], 0, CT_SN76489, 0x0c) ||
		setup_device(*m_sn76489[1], 1, CT_SN76489, 0x0c))
		osd_printf_warning("Warning: file requests an unsupported T6W28\n");

	if (setup_device(*m_ym2413[0], 0, CT_YM2413, 0x10) ||
		setup_device(*m_ym2413[1], 1, CT_YM2413, 0x10))
		osd_printf_warning("Warning: file requests an unsupported VRC7\n");

	if (setup_device(*m_ym2612[0], 0, CT_YM2612, version < 110 ? 0x10 : 0x2c) ||
		setup_device(*m_ym2612[1], 1, CT_YM2612, version < 110 ? 0x10 : 0x2c))
		osd_printf_warning("Warning: file requests an unsupported YM3438\n");

	setup_device(*m_ym2151[0], 0, CT_YM2151, version < 110 ? 0x10 : 0x30);
	setup_device(*m_ym2151[1], 1, CT_YM2151, version < 110 ? 0x10 : 0x30);

	setup_device(*m_segapcm[0], 0, CT_SEGAPCM, 0x38, 0x151);
	setup_device(*m_segapcm[1], 1, CT_SEGAPCM, 0x38, 0x151);
	m_segapcm[0]->set_bank(version >= 0x151 && header_size >= 0x40 ? r32(0x3c) : 0);
	m_segapcm[1]->set_bank(version >= 0x151 && header_size >= 0x40 ? r32(0x3c) : 0);

	setup_device(*m_rf5c68, 0, CT_RF5C68, 0x40, 0x151);
	setup_device(*m_ym2203[0], 0, CT_YM2203, 0x44, 0x151);
	setup_device(*m_ym2203[1], 1, CT_YM2203, 0x44, 0x151);
	setup_device(*m_ym2608[0], 0, CT_YM2608, 0x48, 0x151);
	setup_device(*m_ym2608[1], 1, CT_YM2608, 0x48, 0x151);

	if (setup_device(*m_ym2610[0], 0, CT_YM2610, 0x4c, 0x151) ||
		setup_device(*m_ym2610[1], 1, CT_YM2610, 0x4c, 0x151))
		osd_printf_warning("Warning: file requests an unsupported YM2610B\n");

	if (setup_device(*m_ym3812[0], 0, CT_YM3812, 0x50, 0x151) ||
		setup_device(*m_ym3812[1], 1, CT_YM3812, 0x50, 0x151))
		osd_printf_warning("Warning: file requests an unsupported SoundBlaster Pro\n");

	setup_device(*m_ym3526[0], 0, CT_YM3526, 0x54, 0x151);
	setup_device(*m_ym3526[1], 1, CT_YM3526, 0x54, 0x151);
	setup_device(*m_y8950[0], 0, CT_Y8950, 0x58, 0x151);
	setup_device(*m_y8950[1], 1, CT_Y8950, 0x58, 0x151);
	setup_device(*m_ymf262[0], 0, CT_YMF262, 0x5c, 0x151);
	setup_device(*m_ymf262[1], 1, CT_YMF262, 0x5c, 0x151);
	setup_device(*m_ymf278b[0], 0, CT_YMF278B, 0x60, 0x151);
	setup_device(*m_ymf278b[1], 1, CT_YMF278B, 0x60, 0x151);
	setup_device(*m_ymf271[0], 0, CT_YMF271, 0x64, 0x151);
	setup_device(*m_ymf271[1], 1, CT_YMF271, 0x64, 0x151);
	setup_device(*m_ymz280b[0], 0, CT_YMZ280B, 0x68, 0x151);
	setup_device(*m_ymz280b[1], 1, CT_YMZ280B, 0x68, 0x151);

	if (setup_device(*m_rf5c164, 0, CT_RF5C164, 0x6c, 0x151))
		osd_printf_warning("Warning: file requests an unsupported Cosmic Fantasy Stories HACK\n");

	setup_device(*m_sega32x, 0, CT_SEGA32X, 0x70, 0x151);

	setup_device(*m_ay8910[0], 0, CT_AY8910, 0x74, 0x151);
	setup_device(*m_ay8910[1], 1, CT_AY8910, 0x74, 0x151);
	m_ay8910[0]->set_psg_type(vgm_ay8910_type(version >= 0x151 && header_size >= 0x7c ? r8(0x78) : 0));
	m_ay8910[1]->set_psg_type(vgm_ay8910_type(version >= 0x151 && header_size >= 0x7c ? r8(0x78) : 0));
	m_ay8910[0]->set_flags(vgm_ay8910_flags(version >= 0x151 && header_size >= 0x7a ? r8(0x79) : 0));
	m_ay8910[1]->set_flags(vgm_ay8910_flags(version >= 0x151 && header_size >= 0x7a ? r8(0x79) : 0));

	setup_device(*m_dmg[0], 0, CT_GAMEBOY, 0x80, 0x161);
	setup_device(*m_dmg[1], 1, CT_GAMEBOY, 0x80, 0x161);

	if (setup_device(*m_nescpu[0], 0, CT_NESAPU, 0x84, 0x161) ||
		setup_device(*m_nescpu[1], 1, CT_NESAPU, 0x84, 0x161))
		osd_printf_warning("Warning: file requests an unsupported FDS sound addon\n");

	setup_device(*m_multipcm[0], 0, CT_MULTIPCM, 0x88, 0x161);
	setup_device(*m_multipcm[1], 1, CT_MULTIPCM, 0x88, 0x161);

	setup_device(*m_upd7759[0], 0, CT_UPD7759, 0x8c, 0x161);
	setup_device(*m_upd7759[1], 1, CT_UPD7759, 0x8c, 0x161);
	m_upd7759_md[0] = r32(0x8c) & 0x80000000 ? 0 : 1;
	m_upd7759_md[1] = r32(0x8c) & 0x80000000 ? 0 : 1;

	setup_device(*m_okim6258[0], 0, CT_OKIM6258, 0x90, 0x161);
	setup_device(*m_okim6258[1], 1, CT_OKIM6258, 0x90, 0x161);

	uint8_t okim6258_flags = version >= 0x161 && header_size >= 0x95 ? r8(0x94) : 0;
	m_okim6258_divider[0] = okim6258_flags & 3;
	m_okim6258[0]->set_divider(m_okim6258_divider[0]);
	m_okim6258[0]->set_outbits(BIT(okim6258_flags, 3) ? 12 : 10);
	m_okim6258[0]->set_type(BIT(okim6258_flags, 2));
	m_okim6258_divider[1] = okim6258_flags & 3;
	m_okim6258[1]->set_divider(m_okim6258_divider[1]);
	m_okim6258[1]->set_outbits(BIT(okim6258_flags, 3) ? 12 : 10);
	m_okim6258[1]->set_type(BIT(okim6258_flags, 2));

	m_k054539[0]->init_flags(version >= 0x161 && header_size >= 0x96 ? r8(0x95) : 0);
	m_k054539[1]->init_flags(version >= 0x161 && header_size >= 0x96 ? r8(0x95) : 0);

	C140_TYPE c140_type = c140_bank_type(version >= 0x161 && header_size >= 0x96 ? r8(0x96) : 0);
	m_vgmplay->set_c140_bank_type(0, c140_type);
	m_vgmplay->set_c140_bank_type(1, c140_type);

	m_okim6295_pin7[0] = setup_device(*m_okim6295[0], 0, CT_OKIM6295, 0x98, 0x161);
	m_okim6295_pin7[1] = setup_device(*m_okim6295[1], 1, CT_OKIM6295, 0x98, 0x161);
	m_okim6295[0]->set_pin7(m_okim6295_pin7[0] ? okim6295_device::PIN7_HIGH : okim6295_device::PIN7_LOW);
	m_okim6295[1]->set_pin7(m_okim6295_pin7[1] ? okim6295_device::PIN7_HIGH : okim6295_device::PIN7_LOW);

	setup_device(*m_k051649[0], 0, CT_K051649, 0x9c, 0x161);
	setup_device(*m_k051649[1], 1, CT_K051649, 0x9c, 0x161);

	// HACK: Some VGMs contain the halved clock speed of the sound core inside the SCC
	m_k051649[0]->set_clock_scale(m_k051649[0]->unscaled_clock() < 2097152 ? 2.0 : 1.0);
	m_k051649[1]->set_clock_scale(m_k051649[1]->unscaled_clock() < 2097152 ? 2.0 : 1.0);

	setup_device(*m_k054539[0], 0, CT_K054539, 0xa0, 0x161);
	setup_device(*m_k054539[1], 1, CT_K054539, 0xa0, 0x161);

	// HACK: Some VGMs contain 48,000 instead of 18,432,000
	m_k054539[0]->set_clock_scale(m_k054539[0]->unscaled_clock() == 48000 ? 384.0 : 1.0);
	m_k054539[1]->set_clock_scale(m_k054539[1]->unscaled_clock() == 48000 ? 384.0 : 1.0);
	if (m_k054539[0]->unscaled_clock() == 48000 || m_k054539[1]->unscaled_clock() == 48000)
		osd_printf_error("bad rip detected, correcting k054539 clock\n");

	// HACK: VGM contain the halved clock speed of the sound core inside the HUC6280
	m_huc6280[0]->set_clock_scale(2);
	m_huc6280[1]->set_clock_scale(2);

	setup_device(*m_huc6280[0], 0, CT_C6280, 0xa4, 0x161);
	setup_device(*m_huc6280[1], 1, CT_C6280, 0xa4, 0x161);
	if (c140_type == C140_ASIC219)
	{
		setup_device(*m_c219[0], 0, CT_C140, 0xa8, 0x161);
		setup_device(*m_c219[1], 1, CT_C140, 0xa8, 0x161);
	}
	else
	{
		setup_device(*m_c140[0], 0, CT_C140, 0xa8, 0x161);
		setup_device(*m_c140[1], 1, CT_C140, 0xa8, 0x161);
	}
	setup_device(*m_k053260[0], 0, CT_K053260, 0xac, 0x161);
	setup_device(*m_k053260[1], 1, CT_K053260, 0xac, 0x161);
	setup_device(*m_pokey[0], 0, CT_POKEY, 0xb0, 0x161);
	setup_device(*m_pokey[1], 1, CT_POKEY, 0xb0, 0x161);

	setup_device(*m_qsound, 0, CT_QSOUND, 0xb4, 0x161);

	// HACK: VGMs contain 4,000,000 instead of 60,000,000
	m_qsound->set_clock_scale(m_qsound->unscaled_clock() == 4000000 ? 15.0 : 1.0);
	if (m_qsound->unscaled_clock() == 4000000)
		osd_printf_error("bad rip detected, correcting qsound clock\n");

	setup_device(*m_scsp[0], 0, CT_SCSP, 0xb8, 0x171);
	setup_device(*m_scsp[1], 1, CT_SCSP, 0xb8, 0x171);
	setup_device(*m_wswan[0], 0, CT_WSWAN, 0xc0, 0x171);
	setup_device(*m_wswan[1], 1, CT_WSWAN, 0xc0, 0x171);
	setup_device(*m_vsu_vue[0], 0, CT_VSU_VUE, 0xc4, 0x171);
	setup_device(*m_vsu_vue[1], 1, CT_VSU_VUE, 0xc4, 0x171);
	setup_device(*m_saa1099[0], 0, CT_SAA1099, 0xc8, 0x171);
	setup_device(*m_saa1099[1], 1, CT_SAA1099, 0xc8, 0x171);
	setup_device(*m_es5503[0], 0, CT_ES5503, 0xcc, 0x171);
	setup_device(*m_es5503[1], 1, CT_ES5503, 0xcc, 0x171);

	if (setup_device(*m_es5505[0], 0, CT_ES5505, 0xd0, 0x171) ||
		setup_device(*m_es5505[1], 1, CT_ES5503, 0xd0, 0x171))
		osd_printf_warning("Warning: file requests an unsupported ES5506\n");

	// TODO: dynamically remap es5503/es5505 channels?
	//m_es5503[0]->set_channels(version >= 0x171 && header_size >= 0xd5 ? r8(0xd4) : 0);
	//m_es5503[1]->set_channels(version >= 0x171 && header_size >= 0xd5 ? r8(0xd4) : 0);
	//m_es5505[0]->set_channels(version >= 0x171 && header_size >= 0xd6 ? r8(0xd5) : 0);
	//m_es5505[1]->set_channels(version >= 0x171 && header_size >= 0xd6 ? r8(0xd5) : 0);

	m_c352[0]->set_divider(version >= 0x171 && header_size >= 0xd7 && r8(0xd6) ? r8(0xd6) * 4 : 1);
	m_c352[1]->set_divider(version >= 0x171 && header_size >= 0xd7 && r8(0xd6) ? r8(0xd6) * 4 : 1);

	setup_device(*m_x1_010[0], 0, CT_X1_010, 0xd8, 0x171);
	setup_device(*m_x1_010[1], 1, CT_X1_010, 0xd8, 0x171);

	if (setup_device(*m_c352[0], 0, CT_C352, 0xdc, 0x171) ||
		setup_device(*m_c352[1], 1, CT_C352, 0xdc, 0x171))
		osd_printf_warning("Warning: file requests an unsupported disable rear speakers\n");

	setup_device(*m_ga20[0], 0, CT_GA20, 0xe0, 0x171);
	setup_device(*m_ga20[1], 1, CT_GA20, 0xe0, 0x171);

	for (device_t &child : subdevices())
		if (child.clock() != 0)
			logerror("%s %d\n", child.tag(), child.clock());

	//for (auto &stream : machine().sound().streams())
	//  if (stream->sample_rate() != 0)
	//      logerror("%s %d\n", stream->device().tag(), stream->sample_rate());

	machine().schedule_soft_reset();

	return std::make_pair(std::error_condition(), std::string());
}

uint8_t vgmplay_state::file_r(offs_t offset)
{
	if (offset < m_file_data.size())
		return m_file_data[offset];
	return 0;
}

uint8_t vgmplay_state::file_size_r(offs_t offset)
{
	uint32_t size = m_file_data.size();
	return size >> (8 * offset);
}

template<int Index>
void vgmplay_device::multipcm_bank_hi_w(offs_t offset, uint8_t data)
{
	if (offset & 1)
		m_multipcm_bank_l[Index] = (m_multipcm_bank_l[Index] & 0xff) | (data << 16);
	if (offset & 2)
		m_multipcm_bank_r[Index] = (m_multipcm_bank_r[Index] & 0xff) | (data << 16);
}

template<int Index>
void vgmplay_device::multipcm_bank_lo_w(offs_t offset, uint8_t data)
{
	if (offset & 1)
		m_multipcm_bank_l[Index] = (m_multipcm_bank_l[Index] & 0xff00) | data;
	if (offset & 2)
		m_multipcm_bank_r[Index] = (m_multipcm_bank_r[Index] & 0xff00) | data;

	m_multipcm_banked[Index] = 1;
}

template<int Index>
void vgmplay_state::upd7759_reset_w(uint8_t data)
{
	int reset = data != 0;

	m_upd7759[Index]->reset_w(reset);

	if (m_upd7759_reset[Index] != reset)
	{
		m_upd7759_reset[Index] = reset;

		if (!reset)
			std::queue<uint8_t>().swap(m_upd7759_slave_data[Index]);
	}
}

template<int Index>
void vgmplay_state::upd7759_start_w(uint8_t data)
{
	int start = data != 0;

	// substitute ST with MD when in slave mode
	if (m_upd7759_md[Index])
		m_upd7759[Index]->start_w(start);
	else
		m_upd7759[Index]->md_w(!start);
}

template<int Index>
void vgmplay_state::upd7759_data_w(uint8_t data)
{
	if (!m_upd7759_md[Index] && !m_upd7759_drq[Index])
	{
		m_upd7759_slave_data[Index].push(data);
	}
	else
	{
		m_upd7759[Index]->port_w(data);
		m_upd7759_drq[Index] = 0;
	}
}

template<int Index>
void vgmplay_state::upd7759_drq_w(int state)
{
	if (m_upd7759_drq[Index] && !state)
		osd_printf_error("upd7759.%d underflow\n", Index);

	m_upd7759_drq[Index] = state;

	if (!m_upd7759_md[Index] && m_upd7759_drq[Index] && !m_upd7759_slave_data[Index].empty())
	{
		const uint8_t data(m_upd7759_slave_data[Index].front());
		m_upd7759_slave_data[Index].pop();
		m_upd7759[Index]->port_w(data);
		m_upd7759_drq[Index] = 0;
	}
}

template<int Index>
void vgmplay_device::upd7759_bank_w(uint8_t data)
{
	// TODO: upd7759 update stream
	m_upd7759_bank[Index] = data * 0x20000;
}

template<int Index>
void vgmplay_state::okim6258_clock_w(offs_t offset, uint8_t data, uint8_t mem_mask)
{
	int shift = ((offset & 3) << 3);
	uint32_t c = (m_okim6258[Index]->unscaled_clock() & ~(mem_mask << shift)) | ((data & mem_mask) << shift);
	m_okim6258[Index]->set_unscaled_clock(c);

}

template<int Index>
void vgmplay_state::okim6258_divider_w(offs_t offset, uint8_t data, uint8_t mem_mask)
{
	if ((data & mem_mask) != (m_okim6258_divider[Index] & mem_mask))
	{
		COMBINE_DATA(&m_okim6258_divider[Index]);
		m_okim6258[Index]->set_divider(m_okim6258_divider[Index]);
	}
}

template<int Index>
void vgmplay_state::okim6295_clock_w(offs_t offset, uint8_t data, uint8_t mem_mask)
{
	int shift = ((offset & 3) << 3);
	uint32_t c = (m_okim6295[Index]->unscaled_clock() & ~(mem_mask << shift)) | ((data & mem_mask) << shift);
	m_okim6295[Index]->set_unscaled_clock(c);

}

template<int Index>
void vgmplay_state::okim6295_pin7_w(offs_t offset, uint8_t data, uint8_t mem_mask)
{
	if ((data & mem_mask) != (m_okim6295_pin7[Index] & mem_mask))
	{
		COMBINE_DATA(&m_okim6295_pin7[Index]);
		m_okim6295[Index]->set_pin7(m_okim6295_pin7[Index]);
	}
}

template<int Index>
void vgmplay_device::okim6295_nmk112_enable_w(offs_t offset, uint8_t data, uint8_t mem_mask)
{
	COMBINE_DATA(&m_okim6295_nmk112_enable[Index]);
}

template<int Index>
void vgmplay_device::okim6295_bank_w(offs_t offset, uint8_t data, uint8_t mem_mask)
{
	if ((data & mem_mask) != (m_okim6295_bank[Index] & mem_mask))
	{
		COMBINE_DATA(&m_okim6295_bank[Index]);
	}
}

template<int Index>
void vgmplay_device::okim6295_nmk112_bank_w(offs_t offset, uint8_t data, uint8_t mem_mask)
{
	offset &= 3;
	if ((data & mem_mask) != (m_okim6295_nmk112_bank[Index][offset] & mem_mask))
	{
		COMBINE_DATA(&m_okim6295_nmk112_bank[Index][offset]);
	}
}

template<int Index>
void vgmplay_state::scc_w(offs_t offset, uint8_t data)
{
	switch (offset & 1)
	{
	case 0x00:
		m_scc_reg[Index] = data;
		break;
	case 0x01:
		switch (offset >> 1)
		{
		case 0x00:
			m_k051649[Index]->k051649_waveform_w(m_scc_reg[Index], data);
			break;
		case 0x01:
			m_k051649[Index]->k051649_frequency_w(m_scc_reg[Index], data);
			break;
		case 0x02:
			m_k051649[Index]->k051649_volume_w(m_scc_reg[Index], data);
			break;
		case 0x03:
			m_k051649[Index]->k051649_keyonoff_w(data);
			break;
		case 0x04:
			m_k051649[Index]->k052539_waveform_w(m_scc_reg[Index], data);
			break;
		case 0x05:
			m_k051649[Index]->k051649_test_w(data);
			break;
		}
		break;
	}
}

template<int Index>
void vgmplay_state::c140_c219_w(offs_t offset, uint8_t data)
{
	if (m_vgmplay->c140_bank(Index) == C140_ASIC219)
		m_c219[Index]->c219_w(offset, data);
	else
		m_c140[Index]->c140_w(offset, data);
}

INPUT_CHANGED_MEMBER(vgmplay_state::key_pressed)
{
	if (!newval && param != VGMPLAY_HOLD)
		return;

	switch (param)
	{
	case VGMPLAY_STOP:
		m_vgmplay->stop();
		break;
	case VGMPLAY_PAUSE:
		m_vgmplay->pause();
		break;
	case VGMPLAY_PLAY:
		m_vgmplay->play();
		break;
	case VGMPLAY_RESTART:
		m_vgmplay->reset();
		break;
	case VGMPLAY_LOOP:
		m_vgmplay->toggle_loop();
		break;
	case VGMPLAY_VIZ:
		m_viz->cycle_viz_mode();
		break;
	case VGMPLAY_RATE_DOWN:
		m_vgmplay->set_unscaled_clock((uint32_t)(m_vgmplay->clock() * 0.95f));
		break;
	case VGMPLAY_RATE_UP:
		m_vgmplay->set_unscaled_clock((uint32_t)(m_vgmplay->clock() / 0.95f));
		break;
	case VGMPLAY_RATE_RST:
		m_vgmplay->set_unscaled_clock(44100);
		break;
	case VGMPLAY_HOLD:
		if (newval)
		{
			m_held_clock = m_vgmplay->clock();
			m_vgmplay->set_unscaled_clock(0);
		}
		else
		{
			m_vgmplay->set_unscaled_clock(m_held_clock);
		}
	}
}

static INPUT_PORTS_START( vgmplay )
	PORT_START("CONTROLS")
	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_BUTTON1)  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vgmplay_state::key_pressed), VGMPLAY_STOP)        PORT_NAME("Stop")
	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_BUTTON2)  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vgmplay_state::key_pressed), VGMPLAY_PAUSE)       PORT_NAME("Pause")
	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_BUTTON3)  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vgmplay_state::key_pressed), VGMPLAY_PLAY)        PORT_NAME("Play")
	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_BUTTON4)  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vgmplay_state::key_pressed), VGMPLAY_RESTART)     PORT_NAME("Restart")
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_BUTTON5)  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vgmplay_state::key_pressed), VGMPLAY_LOOP)        PORT_NAME("Loop")
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_BUTTON6)  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vgmplay_state::key_pressed), VGMPLAY_VIZ)         PORT_NAME("Visualization Mode")
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_BUTTON7)  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vgmplay_state::key_pressed), VGMPLAY_RATE_DOWN)   PORT_CODE(KEYCODE_R) PORT_NAME("Rate Down")
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_BUTTON8)  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vgmplay_state::key_pressed), VGMPLAY_RATE_UP)     PORT_CODE(KEYCODE_T) PORT_NAME("Rate Up")
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_BUTTON9)  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vgmplay_state::key_pressed), VGMPLAY_RATE_RST)    PORT_CODE(KEYCODE_Y) PORT_NAME("Rate Reset")
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_BUTTON10) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vgmplay_state::key_pressed), VGMPLAY_HOLD)        PORT_CODE(KEYCODE_U) PORT_NAME("Rate Hold")
INPUT_PORTS_END

void vgmplay_state::file_map(address_map &map)
{
	map(0x00000000, 0xffffffff).r(FUNC(vgmplay_state::file_r));
}

void vgmplay_state::soundchips_map(address_map &map)
{
	map(vgmplay_device::REG_SIZE, vgmplay_device::REG_SIZE + 3).r(FUNC(vgmplay_state::file_size_r));
	map(vgmplay_device::A_SN76489_0 + 0, vgmplay_device::A_SN76489_0 + 0).w(m_sn76489[0], FUNC(sn76489_device::write));
	//map(vgmplay_device::A_SN76489_0 + 1, vgmplay_device::A_SN76489_0 + 1).w(m_sn76489[0], FUNC(sn76489_device::stereo_w)); // TODO: GG stereo
	map(vgmplay_device::A_SN76489_1 + 0, vgmplay_device::A_SN76489_1 + 0).w(m_sn76489[1], FUNC(sn76489_device::write));
	//map(vgmplay_device::A_SN76489_1 + 1, vgmplay_device::A_SN76489_1 + 1).w(m_sn76489[1], FUNC(sn76489_device::stereo_w)); // TODO: GG stereo
	map(vgmplay_device::A_YM2413_0, vgmplay_device::A_YM2413_0 + 1).w(m_ym2413[0], FUNC(ym2413_device::write));
	map(vgmplay_device::A_YM2413_1, vgmplay_device::A_YM2413_1 + 1).w(m_ym2413[1], FUNC(ym2413_device::write));
	map(vgmplay_device::A_YM2612_0, vgmplay_device::A_YM2612_0 + 3).w(m_ym2612[0], FUNC(ym2612_device::write));
	map(vgmplay_device::A_YM2612_1, vgmplay_device::A_YM2612_1 + 3).w(m_ym2612[1], FUNC(ym2612_device::write));
	map(vgmplay_device::A_YM2151_0, vgmplay_device::A_YM2151_0 + 1).w(m_ym2151[0], FUNC(ym2151_device::write));
	map(vgmplay_device::A_YM2151_1, vgmplay_device::A_YM2151_1 + 1).w(m_ym2151[1], FUNC(ym2151_device::write));
	map(vgmplay_device::A_SEGAPCM_0, vgmplay_device::A_SEGAPCM_0 + 0x7ff).w(m_segapcm[0], FUNC(segapcm_device::write));
	map(vgmplay_device::A_SEGAPCM_1, vgmplay_device::A_SEGAPCM_1 + 0x7ff).w(m_segapcm[1], FUNC(segapcm_device::write));
	map(vgmplay_device::A_RF5C68, vgmplay_device::A_RF5C68 + 0xf).w(m_rf5c68, FUNC(rf5c68_device::rf5c68_w));
	map(vgmplay_device::A_RF5C68_RAM, vgmplay_device::A_RF5C68_RAM + 0xffff).w(m_rf5c68, FUNC(rf5c68_device::rf5c68_mem_w));
	map(vgmplay_device::A_YM2203_0, vgmplay_device::A_YM2203_0 + 1).w(m_ym2203[0], FUNC(ym2203_device::write));
	map(vgmplay_device::A_YM2203_1, vgmplay_device::A_YM2203_1 + 1).w(m_ym2203[1], FUNC(ym2203_device::write));
	map(vgmplay_device::A_YM2608_0, vgmplay_device::A_YM2608_0 + 0x3).w(m_ym2608[0], FUNC(ym2608_device::write));
	map(vgmplay_device::A_YM2608_1, vgmplay_device::A_YM2608_1 + 0x3).w(m_ym2608[1], FUNC(ym2608_device::write));
	map(vgmplay_device::A_YM2610_0, vgmplay_device::A_YM2610_0 + 0x3).w(m_ym2610[0], FUNC(ym2610_device::write));
	map(vgmplay_device::A_YM2610_1, vgmplay_device::A_YM2610_1 + 0x3).w(m_ym2610[1], FUNC(ym2610_device::write));
	map(vgmplay_device::A_YM3812_0, vgmplay_device::A_YM3812_0 + 1).w(m_ym3812[0], FUNC(ym3812_device::write));
	map(vgmplay_device::A_YM3812_1, vgmplay_device::A_YM3812_1 + 1).w(m_ym3812[1], FUNC(ym3812_device::write));
	map(vgmplay_device::A_YM3526_0, vgmplay_device::A_YM3526_0 + 1).w(m_ym3526[0], FUNC(ym3526_device::write));
	map(vgmplay_device::A_YM3526_1, vgmplay_device::A_YM3526_1 + 1).w(m_ym3526[1], FUNC(ym3526_device::write));
	map(vgmplay_device::A_Y8950_0, vgmplay_device::A_Y8950_0 + 1).w(m_y8950[0], FUNC(y8950_device::write));
	map(vgmplay_device::A_Y8950_1, vgmplay_device::A_Y8950_1 + 1).w(m_y8950[1], FUNC(y8950_device::write));
	map(vgmplay_device::A_YMF262_0, vgmplay_device::A_YMF262_0 + 3).w(m_ymf262[0], FUNC(ymf262_device::write));
	map(vgmplay_device::A_YMF262_1, vgmplay_device::A_YMF262_1 + 3).w(m_ymf262[1], FUNC(ymf262_device::write));
	map(vgmplay_device::A_YMF278B_0, vgmplay_device::A_YMF278B_0 + 0xf).w(m_ymf278b[0], FUNC(ymf278b_device::write));
	map(vgmplay_device::A_YMF278B_1, vgmplay_device::A_YMF278B_1 + 0xf).w(m_ymf278b[1], FUNC(ymf278b_device::write));
	map(vgmplay_device::A_YMF271_0, vgmplay_device::A_YMF271_0 + 0xf).w(m_ymf271[0], FUNC(ymf271_device::write));
	map(vgmplay_device::A_YMF271_1, vgmplay_device::A_YMF271_1 + 0xf).w(m_ymf271[1], FUNC(ymf271_device::write));
	map(vgmplay_device::A_YMZ280B_0, vgmplay_device::A_YMZ280B_0 + 0x1).w(m_ymz280b[0], FUNC(ymz280b_device::write));
	map(vgmplay_device::A_YMZ280B_1, vgmplay_device::A_YMZ280B_1 + 0x1).w(m_ymz280b[1], FUNC(ymz280b_device::write));
	map(vgmplay_device::A_RF5C164, vgmplay_device::A_RF5C164 + 0xf).w(m_rf5c164, FUNC(rf5c68_device::rf5c68_w));
	map(vgmplay_device::A_RF5C164_RAM, vgmplay_device::A_RF5C164_RAM + 0xffff).w(m_rf5c164, FUNC(rf5c68_device::rf5c68_mem_w));
	map(vgmplay_device::A_AY8910_0, vgmplay_device::A_AY8910_0).w(m_ay8910[0], FUNC(ay8910_device::data_w));
	map(vgmplay_device::A_AY8910_0 + 1, vgmplay_device::A_AY8910_0 + 1).w(m_ay8910[0], FUNC(ay8910_device::address_w));
	map(vgmplay_device::A_AY8910_1, vgmplay_device::A_AY8910_1).w(m_ay8910[1], FUNC(ay8910_device::data_w));
	map(vgmplay_device::A_AY8910_1 + 1, vgmplay_device::A_AY8910_1 + 1).w(m_ay8910[1], FUNC(ay8910_device::address_w));
	map(vgmplay_device::A_GAMEBOY_0, vgmplay_device::A_GAMEBOY_0 + 0x16).w(m_dmg[0], FUNC(gameboy_sound_device::sound_w));
	map(vgmplay_device::A_GAMEBOY_0 + 0x20, vgmplay_device::A_GAMEBOY_0 + 0x2f).w(m_dmg[0], FUNC(gameboy_sound_device::wave_w));
	map(vgmplay_device::A_GAMEBOY_1, vgmplay_device::A_GAMEBOY_1 + 0x16).w(m_dmg[1], FUNC(gameboy_sound_device::sound_w));
	map(vgmplay_device::A_GAMEBOY_1 + 0x20, vgmplay_device::A_GAMEBOY_1 + 0x2f).w(m_dmg[1], FUNC(gameboy_sound_device::wave_w));
	map(vgmplay_device::A_NESAPU_0, vgmplay_device::A_NESAPU_0 + 0x1f).w("nescpu.0:nesapu", FUNC(nesapu_device::write));
	map(vgmplay_device::A_NES_RAM_0, vgmplay_device::A_NES_RAM_0 + 0xffff).ram().share("nesapu_ram.0");
	map(vgmplay_device::A_NESAPU_1, vgmplay_device::A_NESAPU_1 + 0x1f).w("nescpu.1:nesapu", FUNC(nesapu_device::write));
	map(vgmplay_device::A_NES_RAM_1, vgmplay_device::A_NES_RAM_1 + 0xffff).ram().share("nesapu_ram.1");
	map(vgmplay_device::A_MULTIPCM_0, vgmplay_device::A_MULTIPCM_0 + 3).w(m_multipcm[0], FUNC(multipcm_device::write));
	map(vgmplay_device::A_MULTIPCM_0 + 4, vgmplay_device::A_MULTIPCM_0 + 7).w("vgmplay", FUNC(vgmplay_device::multipcm_bank_hi_w<0>));
	map(vgmplay_device::A_MULTIPCM_0 + 8, vgmplay_device::A_MULTIPCM_0 + 11).w("vgmplay", FUNC(vgmplay_device::multipcm_bank_lo_w<0>));
	map(vgmplay_device::A_MULTIPCM_1, vgmplay_device::A_MULTIPCM_1 + 3).w(m_multipcm[1], FUNC(multipcm_device::write));
	map(vgmplay_device::A_MULTIPCM_1 + 4, vgmplay_device::A_MULTIPCM_1 + 7).w("vgmplay", FUNC(vgmplay_device::multipcm_bank_hi_w<1>));
	map(vgmplay_device::A_MULTIPCM_1 + 8, vgmplay_device::A_MULTIPCM_1 + 11).w("vgmplay", FUNC(vgmplay_device::multipcm_bank_lo_w<1>));
	map(vgmplay_device::A_UPD7759_0 + 0, vgmplay_device::A_UPD7759_0 + 0).w(FUNC(vgmplay_state::upd7759_reset_w<0>));
	map(vgmplay_device::A_UPD7759_0 + 1, vgmplay_device::A_UPD7759_0 + 1).w(FUNC(vgmplay_state::upd7759_start_w<0>));
	map(vgmplay_device::A_UPD7759_0 + 2, vgmplay_device::A_UPD7759_0 + 2).w(FUNC(vgmplay_state::upd7759_data_w<0>));
	map(vgmplay_device::A_UPD7759_0 + 3, vgmplay_device::A_UPD7759_0 + 3).w("vgmplay", FUNC(vgmplay_device::upd7759_bank_w<0>));
	map(vgmplay_device::A_UPD7759_1 + 0, vgmplay_device::A_UPD7759_1 + 0).w(FUNC(vgmplay_state::upd7759_reset_w<1>));
	map(vgmplay_device::A_UPD7759_1 + 1, vgmplay_device::A_UPD7759_1 + 1).w(FUNC(vgmplay_state::upd7759_start_w<1>));
	map(vgmplay_device::A_UPD7759_1 + 2, vgmplay_device::A_UPD7759_1 + 2).w(FUNC(vgmplay_state::upd7759_data_w<1>));
	map(vgmplay_device::A_UPD7759_1 + 3, vgmplay_device::A_UPD7759_1 + 3).w("vgmplay", FUNC(vgmplay_device::upd7759_bank_w<1>));
	map(vgmplay_device::A_OKIM6258_0 + 0x0, vgmplay_device::A_OKIM6258_0 + 0x0).w(m_okim6258[0], FUNC(okim6258_device::ctrl_w));
	map(vgmplay_device::A_OKIM6258_0 + 0x1, vgmplay_device::A_OKIM6258_0 + 0x1).w(m_okim6258[0], FUNC(okim6258_device::data_w));
	map(vgmplay_device::A_OKIM6258_0 + 0x2, vgmplay_device::A_OKIM6258_0 + 0x2).nopw(); // TODO: okim6258 pan
	map(vgmplay_device::A_OKIM6258_0 + 0x8, vgmplay_device::A_OKIM6258_0 + 0xb).w(FUNC(vgmplay_state::okim6258_clock_w<0>));
	map(vgmplay_device::A_OKIM6258_0 + 0xc, vgmplay_device::A_OKIM6258_0 + 0xc).w(FUNC(vgmplay_state::okim6258_divider_w<0>));
	map(vgmplay_device::A_OKIM6258_1 + 0x0, vgmplay_device::A_OKIM6258_1 + 0x0).w(m_okim6258[1], FUNC(okim6258_device::ctrl_w));
	map(vgmplay_device::A_OKIM6258_1 + 0x1, vgmplay_device::A_OKIM6258_1 + 0x1).w(m_okim6258[1], FUNC(okim6258_device::data_w));
	map(vgmplay_device::A_OKIM6258_1 + 0x2, vgmplay_device::A_OKIM6258_1 + 0x2).nopw(); // TODO: okim6258 pan
	map(vgmplay_device::A_OKIM6258_1 + 0x8, vgmplay_device::A_OKIM6258_1 + 0xb).w(FUNC(vgmplay_state::okim6258_clock_w<1>));
	map(vgmplay_device::A_OKIM6258_1 + 0xc, vgmplay_device::A_OKIM6258_1 + 0xc).w(FUNC(vgmplay_state::okim6258_divider_w<1>));
	map(vgmplay_device::A_OKIM6295_0, vgmplay_device::A_OKIM6295_0).w(m_okim6295[0], FUNC(okim6295_device::write));
	map(vgmplay_device::A_OKIM6295_0 + 0x8, vgmplay_device::A_OKIM6295_0 + 0xb).w(FUNC(vgmplay_state::okim6295_clock_w<0>));
	map(vgmplay_device::A_OKIM6295_0 + 0xc, vgmplay_device::A_OKIM6295_0 + 0xc).w(FUNC(vgmplay_state::okim6295_pin7_w<0>));
	map(vgmplay_device::A_OKIM6295_0 + 0xe, vgmplay_device::A_OKIM6295_0 + 0xe).w("vgmplay", FUNC(vgmplay_device::okim6295_nmk112_enable_w<0>));
	map(vgmplay_device::A_OKIM6295_0 + 0xf, vgmplay_device::A_OKIM6295_0 + 0xf).w("vgmplay", FUNC(vgmplay_device::okim6295_bank_w<0>));
	map(vgmplay_device::A_OKIM6295_0 + 0x10, vgmplay_device::A_OKIM6295_0 + 0x13).w("vgmplay", FUNC(vgmplay_device::okim6295_nmk112_bank_w<0>));
	map(vgmplay_device::A_OKIM6295_1, vgmplay_device::A_OKIM6295_1).w(m_okim6295[1], FUNC(okim6295_device::write));
	map(vgmplay_device::A_OKIM6295_1 + 0x8, vgmplay_device::A_OKIM6295_1 + 0xb).w(FUNC(vgmplay_state::okim6295_clock_w<1>));
	map(vgmplay_device::A_OKIM6295_1 + 0xc, vgmplay_device::A_OKIM6295_1 + 0xc).w(FUNC(vgmplay_state::okim6295_pin7_w<1>));
	map(vgmplay_device::A_OKIM6295_1 + 0xe, vgmplay_device::A_OKIM6295_1 + 0xe).w("vgmplay", FUNC(vgmplay_device::okim6295_nmk112_enable_w<1>));
	map(vgmplay_device::A_OKIM6295_1 + 0xf, vgmplay_device::A_OKIM6295_1 + 0xf).w("vgmplay", FUNC(vgmplay_device::okim6295_bank_w<1>));
	map(vgmplay_device::A_OKIM6295_1 + 0x10, vgmplay_device::A_OKIM6295_1 + 0x13).w("vgmplay", FUNC(vgmplay_device::okim6295_nmk112_bank_w<1>));
	map(vgmplay_device::A_K051649_0, vgmplay_device::A_K051649_0 + 0xf).w(FUNC(vgmplay_state::scc_w<0>));
	map(vgmplay_device::A_K051649_1, vgmplay_device::A_K051649_1 + 0xf).w(FUNC(vgmplay_state::scc_w<1>));
	map(vgmplay_device::A_K054539_0, vgmplay_device::A_K054539_0 + 0x22f).w(m_k054539[0], FUNC(k054539_device::write));
	map(vgmplay_device::A_K054539_1, vgmplay_device::A_K054539_1 + 0x22f).w(m_k054539[1], FUNC(k054539_device::write));
	map(vgmplay_device::A_C6280_0, vgmplay_device::A_C6280_0 + 0xf).w("huc6280.0:psg", FUNC(c6280_device::c6280_w));
	map(vgmplay_device::A_C6280_1, vgmplay_device::A_C6280_1 + 0xf).w("huc6280.1:psg", FUNC(c6280_device::c6280_w));
	map(vgmplay_device::A_C140_0, vgmplay_device::A_C140_0 + 0x1ff).w(FUNC(vgmplay_state::c140_c219_w<0>));
	map(vgmplay_device::A_C140_1, vgmplay_device::A_C140_1 + 0x1ff).w(FUNC(vgmplay_state::c140_c219_w<1>));
	map(vgmplay_device::A_K053260_0, vgmplay_device::A_K053260_0 + 0x2f).w(m_k053260[0], FUNC(k053260_device::write));
	map(vgmplay_device::A_K053260_1, vgmplay_device::A_K053260_1 + 0x2f).w(m_k053260[1], FUNC(k053260_device::write));
	map(vgmplay_device::A_POKEY_0, vgmplay_device::A_POKEY_0 + 0xf).w(m_pokey[0], FUNC(pokey_device::write));
	map(vgmplay_device::A_POKEY_1, vgmplay_device::A_POKEY_1 + 0xf).w(m_pokey[1], FUNC(pokey_device::write));
	map(vgmplay_device::A_QSOUND, vgmplay_device::A_QSOUND + 0x2).w(m_qsound, FUNC(qsound_device::qsound_w));
	map(vgmplay_device::A_VSU_VUE_0, vgmplay_device::A_VSU_VUE_0 + 0x5ff).w(m_vsu_vue[0], FUNC(vboysnd_device::write));
	map(vgmplay_device::A_VSU_VUE_1, vgmplay_device::A_VSU_VUE_1 + 0x5ff).w(m_vsu_vue[1], FUNC(vboysnd_device::write));
	map(vgmplay_device::A_SAA1099_0, vgmplay_device::A_SAA1099_0 + 1).w(m_saa1099[0], FUNC(saa1099_device::write));
	map(vgmplay_device::A_SAA1099_1, vgmplay_device::A_SAA1099_1 + 1).w(m_saa1099[1], FUNC(saa1099_device::write));
	map(vgmplay_device::A_ES5503_0, vgmplay_device::A_ES5503_0 + 0xe2).w(m_es5503[0], FUNC(es5503_device::write));
	map(vgmplay_device::A_ES5503_RAM_0, vgmplay_device::A_ES5503_RAM_0 + 0x1ffff).ram().share("es5503_ram.0");
	map(vgmplay_device::A_ES5503_1, vgmplay_device::A_ES5503_1 + 0xe2).w(m_es5503[1], FUNC(es5503_device::write));
	map(vgmplay_device::A_ES5503_RAM_1, vgmplay_device::A_ES5503_RAM_1 + 0x1ffff).ram().share("es5503_ram.1");
	// TODO: es5505
	map(vgmplay_device::A_X1_010_0, vgmplay_device::A_X1_010_0 + 0x1fff).w(m_x1_010[0], FUNC(x1_010_device::write));
	map(vgmplay_device::A_X1_010_1, vgmplay_device::A_X1_010_1 + 0x1fff).w(m_x1_010[1], FUNC(x1_010_device::write));
	map(vgmplay_device::A_GA20_0, vgmplay_device::A_GA20_0 + 0x1f).w(m_ga20[0], FUNC(iremga20_device::write));
	map(vgmplay_device::A_GA20_1, vgmplay_device::A_GA20_1 + 0x1f).w(m_ga20[1], FUNC(iremga20_device::write));
}

void vgmplay_state::soundchips16le_map(address_map &map)
{
	map(vgmplay_device::A_32X_PWM, vgmplay_device::A_32X_PWM + 0xf).w(m_sega32x, FUNC(sega_32x_device::pwm_w));
	map(vgmplay_device::A_C352_0, vgmplay_device::A_C352_0 + 0x7fff).w(m_c352[0], FUNC(c352_device::write));
	map(vgmplay_device::A_C352_1, vgmplay_device::A_C352_1 + 0x7fff).w(m_c352[1], FUNC(c352_device::write));
	map(vgmplay_device::A_WSWAN_0, vgmplay_device::A_WSWAN_0 + 0xff).w(m_wswan[0], FUNC(wswan_sound_device::port_w));
	map(vgmplay_device::A_WSWAN_1, vgmplay_device::A_WSWAN_1 + 0xff).w(m_wswan[1], FUNC(wswan_sound_device::port_w));
	map(vgmplay_device::A_WSWAN_0 + 0x64, vgmplay_device::A_WSWAN_0 + 0x6b).w(m_wswan[0], FUNC(wswan_sound_device::hypervoice_w));
	map(vgmplay_device::A_WSWAN_1 + 0x64, vgmplay_device::A_WSWAN_1 + 0x6b).w(m_wswan[1], FUNC(wswan_sound_device::hypervoice_w));
	map(vgmplay_device::A_WSWAN_RAM_0, vgmplay_device::A_WSWAN_RAM_0 + 0x3fff).ram().share("wswan_ram.0");
	map(vgmplay_device::A_WSWAN_RAM_1, vgmplay_device::A_WSWAN_RAM_1 + 0x3fff).ram().share("wswan_ram.1");
}

void vgmplay_state::soundchips16be_map(address_map &map)
{
	map(vgmplay_device::A_SCSP_0, vgmplay_device::A_SCSP_0 + 0xfff).w(m_scsp[0], FUNC(scsp_device::write));
	map(vgmplay_device::A_SCSP_1, vgmplay_device::A_SCSP_1 + 0xfff).w(m_scsp[1], FUNC(scsp_device::write));
	map(vgmplay_device::A_SCSP_RAM_0, vgmplay_device::A_SCSP_RAM_0 + 0xfffff).ram().share("scsp_ram.0");
	map(vgmplay_device::A_SCSP_RAM_1, vgmplay_device::A_SCSP_RAM_1 + 0xfffff).ram().share("scsp_ram.1");
}

template<int Index>
void vgmplay_state::segapcm_map(address_map &map)
{
	map(0, 0x1fffff).r("vgmplay", FUNC(vgmplay_device::segapcm_rom_r<Index>));
}

template<int Index>
void vgmplay_state::rf5c68_map(address_map &map)
{
	map(0, 0xffff).ram().share(Index ? "rf5c68_ram.1" : "rf5c68_ram.0");
}

template<int Index>
void vgmplay_state::ym2608_map(address_map &map)
{
	map(0, 0x1fffff).r("vgmplay", FUNC(vgmplay_device::ym2608_rom_r<Index>));
}

template<int Index>
void vgmplay_state::ym2610_adpcm_a_map(address_map &map)
{
	map(0, 0xffffff).r("vgmplay", FUNC(vgmplay_device::ym2610_adpcm_a_rom_r<Index>));
}

template<int Index>
void vgmplay_state::ym2610_adpcm_b_map(address_map &map)
{
	map(0, 0xffffff).r("vgmplay", FUNC(vgmplay_device::ym2610_adpcm_b_rom_r<Index>));
}

template<int Index>
void vgmplay_state::y8950_map(address_map &map)
{
	map(0, 0x1fffff).r("vgmplay", FUNC(vgmplay_device::y8950_rom_r<Index>));
}

template<int Index>
void vgmplay_state::ymf278b_map(address_map &map)
{
	map(0, 0x3fffff).r("vgmplay", FUNC(vgmplay_device::ymf278b_rom_r<Index>));
}

template<int Index>
void vgmplay_state::ymf271_map(address_map &map)
{
	map(0, 0x7fffff).r("vgmplay", FUNC(vgmplay_device::ymf271_rom_r<Index>));
}

template<int Index>
void vgmplay_state::ymz280b_map(address_map &map)
{
	map(0, 0xffffff).r("vgmplay", FUNC(vgmplay_device::ymz280b_rom_r<Index>));
}

template<int Index>
void vgmplay_state::nescpu_map(address_map &map)
{
	map(0, 0xffff).ram().share(Index ? "nesapu_ram.1" : "nesapu_ram.0");
}

template<int Index>
void vgmplay_state::multipcm_map(address_map &map)
{
	map(0, 0x3fffff).r("vgmplay", FUNC(vgmplay_device::multipcm_rom_r<Index>));
}

template<int Index>
void vgmplay_state::upd7759_map(address_map &map)
{
	map(0, 0x1ffff).r("vgmplay", FUNC(vgmplay_device::upd7759_rom_r<Index>));
}

template<int Index>
void vgmplay_state::okim6295_map(address_map &map)
{
	map(0, 0x3ffff).r("vgmplay", FUNC(vgmplay_device::okim6295_rom_r<Index>));
}

template<int Index>
void vgmplay_state::k054539_map(address_map &map)
{
	map(0, 0xffffff).r("vgmplay", FUNC(vgmplay_device::k054539_rom_r<Index>));
}

template<int Index>
void vgmplay_state::c140_map(address_map &map)
{
	map(0, 0x1ffffff).r("vgmplay", FUNC(vgmplay_device::c140_rom_r<Index>));
}

template<int Index>
void vgmplay_state::c219_map(address_map &map)
{
	map(0, 0x07ffff).r("vgmplay", FUNC(vgmplay_device::c219_rom_r<Index>));
}

template<int Index>
void vgmplay_state::k053260_map(address_map &map)
{
	map(0, 0x1fffff).r("vgmplay", FUNC(vgmplay_device::k053260_rom_r<Index>));
}

template<int Index>
void vgmplay_state::qsound_map(address_map &map)
{
	map(0, 0xffffff).r("vgmplay", FUNC(vgmplay_device::qsound_rom_r<Index>));
}

template<int Index>
void vgmplay_state::scsp_map(address_map &map)
{
	map(0, 0xfffff).ram().share(Index ? "scsp_ram.1" : "scsp_ram.0");
}

template<int Index>
void vgmplay_state::wswan_map(address_map &map)
{
	map(0, 0x3fff).ram().share(Index ? "wswan_ram.1" : "wswan_ram.0");
}

template<int Index>
void vgmplay_state::es5503_map(address_map &map)
{
	map(0, 0x1ffff).ram().share(Index ? "es5503_ram.1" : "es5503_ram.0");
}

template<int Index>
void vgmplay_state::es5505_map(address_map &map)
{
	map(0, 0xffffff).r("vgmplay", FUNC(vgmplay_device::es5505_rom_r<Index>));
}

template<int Index>
void vgmplay_state::x1_010_map(address_map &map)
{
	map(0, 0xfffff).r("vgmplay", FUNC(vgmplay_device::x1_010_rom_r<Index>));
}

template<int Index>
void vgmplay_state::c352_map(address_map &map)
{
	map(0, 0xffffff).r("vgmplay", FUNC(vgmplay_device::c352_rom_r<Index>));
}

template<int Index>
void vgmplay_state::ga20_map(address_map &map)
{
	map(0, 0xfffff).r("vgmplay", FUNC(vgmplay_device::ga20_rom_r<Index>));
}

template<int Index>
void vgmplay_state::rf5c164_map(address_map &map)
{
	map(0, 0xffff).ram().share("rf5c164_ram");
}

void vgmplay_state::vgmplay(machine_config &config)
{
	VGMPLAY(config, m_vgmplay, 44100);
	m_vgmplay->set_addrmap(AS_PROGRAM, &vgmplay_state::file_map);
	m_vgmplay->set_addrmap(AS_IO, &vgmplay_state::soundchips_map);
	m_vgmplay->set_addrmap(AS_IO16LE, &vgmplay_state::soundchips16le_map);
	m_vgmplay->set_addrmap(AS_IO16BE, &vgmplay_state::soundchips16be_map);

	quickload_image_device &quickload(QUICKLOAD(config, "quickload", "vgm,vgz"));
	quickload.set_load_callback(FUNC(vgmplay_state::load_file));
	quickload.set_interface("vgm_quik");

	SOFTWARE_LIST(config, "vgm_list").set_original("vgmplay");

	config.set_default_layout(layout_vgmplay);

	SN76489(config, m_sn76489[0], 0);
	m_sn76489[0]->add_route(0, m_viz, 0.5, 0);
	m_sn76489[0]->add_route(0, m_viz, 0.5, 1);

	SN76489(config, m_sn76489[1], 0);
	m_sn76489[1]->add_route(0, m_viz, 0.5, 0);
	m_sn76489[1]->add_route(0, m_viz, 0.5, 1);

	YM2413(config, m_ym2413[0], 0);
	m_ym2413[0]->add_route(ALL_OUTPUTS, m_viz, 1, 0);
	m_ym2413[0]->add_route(ALL_OUTPUTS, m_viz, 1, 1);

	YM2413(config, m_ym2413[1], 0);
	m_ym2413[1]->add_route(ALL_OUTPUTS, m_viz, 1, 0);
	m_ym2413[1]->add_route(ALL_OUTPUTS, m_viz, 1, 1);

	YM2612(config, m_ym2612[0], 0);
	m_ym2612[0]->add_route(0, m_viz, 1, 0);
	m_ym2612[0]->add_route(1, m_viz, 1, 1);

	YM2612(config, m_ym2612[1], 0);
	m_ym2612[1]->add_route(0, m_viz, 1, 0);
	m_ym2612[1]->add_route(1, m_viz, 1, 1);

	YM2151(config, m_ym2151[0], 0);
	m_ym2151[0]->add_route(0, m_viz, 1, 0);
	m_ym2151[0]->add_route(1, m_viz, 1, 1);

	YM2151(config, m_ym2151[1], 0);
	m_ym2151[1]->add_route(0, m_viz, 1, 0);
	m_ym2151[1]->add_route(1, m_viz, 1, 1);

	SEGAPCM(config, m_segapcm[0], 0);
	m_segapcm[0]->set_addrmap(0, &vgmplay_state::segapcm_map<0>);
	m_segapcm[0]->add_route(0, m_viz, 1, 0);
	m_segapcm[0]->add_route(1, m_viz, 1, 1);

	SEGAPCM(config, m_segapcm[1], 0);
	m_segapcm[1]->set_addrmap(0, &vgmplay_state::segapcm_map<1>);
	m_segapcm[1]->add_route(0, m_viz, 1, 0);
	m_segapcm[1]->add_route(1, m_viz, 1, 1);

	RF5C68(config, m_rf5c68, 0);
	m_rf5c68->set_addrmap(0, &vgmplay_state::rf5c68_map<0>);
	m_rf5c68->add_route(0, m_viz, 1, 0);
	m_rf5c68->add_route(1, m_viz, 1, 1);

	// TODO: prevent error.log spew
	YM2203(config, m_ym2203[0], 0);
	m_ym2203[0]->add_route(ALL_OUTPUTS, m_viz, 0.25, 0);
	m_ym2203[0]->add_route(ALL_OUTPUTS, m_viz, 0.25, 1);

	YM2203(config, m_ym2203[1], 0);
	m_ym2203[1]->add_route(ALL_OUTPUTS, m_viz, 0.25, 0);
	m_ym2203[1]->add_route(ALL_OUTPUTS, m_viz, 0.25, 1);

	// TODO: prevent error.log spew
	YM2608(config, m_ym2608[0], 0);
	m_ym2608[0]->set_addrmap(0, &vgmplay_state::ym2608_map<0>);
	m_ym2608[0]->add_route(0, m_viz, 0.75, 0);
	m_ym2608[0]->add_route(0, m_viz, 0.75, 1);
	m_ym2608[0]->add_route(1, m_viz, 1.00, 0);
	m_ym2608[0]->add_route(2, m_viz, 1.00, 1);

	YM2608(config, m_ym2608[1], 0);
	m_ym2608[1]->set_addrmap(0, &vgmplay_state::ym2608_map<1>);
	m_ym2608[1]->add_route(0, m_viz, 0.75, 0);
	m_ym2608[1]->add_route(0, m_viz, 0.75, 1);
	m_ym2608[1]->add_route(1, m_viz, 1.00, 0);
	m_ym2608[1]->add_route(2, m_viz, 1.00, 1);

	// TODO: prevent error.log spew
	YM2610(config, m_ym2610[0], 0);
	m_ym2610[0]->set_addrmap(0, &vgmplay_state::ym2610_adpcm_a_map<0>);
	m_ym2610[0]->set_addrmap(1, &vgmplay_state::ym2610_adpcm_b_map<0>);
	m_ym2610[0]->add_route(0, m_viz, 0.75, 0);
	m_ym2610[0]->add_route(0, m_viz, 0.75, 1);
	m_ym2610[0]->add_route(1, m_viz, 0.50, 0);
	m_ym2610[0]->add_route(2, m_viz, 0.50, 1);

	YM2610(config, m_ym2610[1], 0);
	m_ym2610[1]->set_addrmap(0, &vgmplay_state::ym2610_adpcm_a_map<1>);
	m_ym2610[1]->set_addrmap(1, &vgmplay_state::ym2610_adpcm_b_map<1>);
	m_ym2610[1]->add_route(0, m_viz, 0.75, 0);
	m_ym2610[1]->add_route(0, m_viz, 0.75, 1);
	m_ym2610[1]->add_route(1, m_viz, 0.50, 0);
	m_ym2610[1]->add_route(2, m_viz, 0.50, 1);

	YM3812(config, m_ym3812[0], 0);
	m_ym3812[0]->add_route(ALL_OUTPUTS, m_viz, 0.5, 0);
	m_ym3812[0]->add_route(ALL_OUTPUTS, m_viz, 0.5, 1);

	YM3812(config, m_ym3812[1], 0);
	m_ym3812[1]->add_route(ALL_OUTPUTS, m_viz, 0.5, 0);
	m_ym3812[1]->add_route(ALL_OUTPUTS, m_viz, 0.5, 1);

	YM3526(config, m_ym3526[0], 0);
	m_ym3526[0]->add_route(ALL_OUTPUTS, m_viz, 0.5, 0);
	m_ym3526[0]->add_route(ALL_OUTPUTS, m_viz, 0.5, 1);

	YM3526(config, m_ym3526[1], 0);
	m_ym3526[1]->add_route(ALL_OUTPUTS, m_viz, 0.5, 0);
	m_ym3526[1]->add_route(ALL_OUTPUTS, m_viz, 0.5, 1);

	Y8950(config, m_y8950[0], 0);
	m_y8950[0]->set_addrmap(0, &vgmplay_state::y8950_map<0>);
	m_y8950[0]->add_route(ALL_OUTPUTS, m_viz, 0.40, 0);
	m_y8950[0]->add_route(ALL_OUTPUTS, m_viz, 0.40, 1);

	Y8950(config, m_y8950[1], 0);
	m_y8950[1]->set_addrmap(0, &vgmplay_state::y8950_map<1>);
	m_y8950[1]->add_route(ALL_OUTPUTS, m_viz, 0.40, 0);
	m_y8950[1]->add_route(ALL_OUTPUTS, m_viz, 0.40, 1);

	YMF262(config, m_ymf262[0], 0);
	m_ymf262[0]->add_route(0, m_viz, 1.00, 0);
	m_ymf262[0]->add_route(1, m_viz, 1.00, 1);
	m_ymf262[0]->add_route(2, m_viz, 1.00, 0);
	m_ymf262[0]->add_route(3, m_viz, 1.00, 1);

	YMF262(config, m_ymf262[1], 0);
	m_ymf262[1]->add_route(0, m_viz, 1.00, 0);
	m_ymf262[1]->add_route(1, m_viz, 1.00, 1);
	m_ymf262[1]->add_route(2, m_viz, 1.00, 0);
	m_ymf262[1]->add_route(3, m_viz, 1.00, 1);

	// TODO: prevent error.log spew
	YMF278B(config, m_ymf278b[0], 0);
	m_ymf278b[0]->set_addrmap(0, &vgmplay_state::ymf278b_map<0>);
	m_ymf278b[0]->add_route(0, m_viz, 1.00, 0);
	m_ymf278b[0]->add_route(1, m_viz, 1.00, 1);
	m_ymf278b[0]->add_route(2, m_viz, 1.00, 0);
	m_ymf278b[0]->add_route(3, m_viz, 1.00, 1);
	m_ymf278b[0]->add_route(4, m_viz, 1.00, 0);
	m_ymf278b[0]->add_route(5, m_viz, 1.00, 1);

	YMF278B(config, m_ymf278b[1], 0);
	m_ymf278b[1]->set_addrmap(0, &vgmplay_state::ymf278b_map<1>);
	m_ymf278b[1]->add_route(0, m_viz, 1.00, 0);
	m_ymf278b[1]->add_route(1, m_viz, 1.00, 1);
	m_ymf278b[1]->add_route(2, m_viz, 1.00, 0);
	m_ymf278b[1]->add_route(3, m_viz, 1.00, 1);
	m_ymf278b[1]->add_route(4, m_viz, 1.00, 0);
	m_ymf278b[1]->add_route(5, m_viz, 1.00, 1);

	YMF271(config, m_ymf271[0], 0);
	m_ymf271[0]->set_addrmap(0, &vgmplay_state::ymf271_map<0>);
	m_ymf271[0]->add_route(0, m_viz, 0.25, 0);
	m_ymf271[0]->add_route(1, m_viz, 0.25, 1);
	m_ymf271[0]->add_route(2, m_viz, 0.25, 0);
	m_ymf271[0]->add_route(3, m_viz, 0.25, 1);

	YMF271(config, m_ymf271[1], 0);
	m_ymf271[1]->set_addrmap(0, &vgmplay_state::ymf271_map<0>);
	m_ymf271[1]->add_route(0, m_viz, 0.25, 0);
	m_ymf271[1]->add_route(1, m_viz, 0.25, 1);
	m_ymf271[1]->add_route(2, m_viz, 0.25, 0);
	m_ymf271[1]->add_route(3, m_viz, 0.25, 1);

	// TODO: prevent error.log spew
	YMZ280B(config, m_ymz280b[0], 0);
	m_ymz280b[0]->set_addrmap(0, &vgmplay_state::ymz280b_map<0>);
	m_ymz280b[0]->add_route(0, m_viz, 0.50, 0);
	m_ymz280b[0]->add_route(1, m_viz, 0.50, 1);

	YMZ280B(config, m_ymz280b[1], 0);
	m_ymz280b[1]->set_addrmap(0, &vgmplay_state::ymz280b_map<1>);
	m_ymz280b[1]->add_route(0, m_viz, 0.50, 0);
	m_ymz280b[1]->add_route(1, m_viz, 0.50, 1);

	RF5C164(config, m_rf5c164, 0);
	m_rf5c164->set_addrmap(0, &vgmplay_state::rf5c164_map<0>);
	m_rf5c164->add_route(0, m_viz, 1, 0);
	m_rf5c164->add_route(1, m_viz, 1, 1);

	/// TODO: rewrite to generate audio without using DAC devices
	SEGA_32X_NTSC(config, m_sega32x, 0, "sega32x_maincpu", "sega32x_scanline_timer");
	m_sega32x->add_route(0, m_viz, 1.00, 0);
	m_sega32x->add_route(1, m_viz, 1.00, 1);

	auto& sega32x_maincpu(M68000(config, "sega32x_maincpu", 0));
	sega32x_maincpu.set_disable();

	TIMER(config, "sega32x_scanline_timer", 0);

	m_sega32x->subdevice<cpu_device>("32x_master_sh2")->set_disable();
	m_sega32x->subdevice<cpu_device>("32x_slave_sh2")->set_disable();

	// TODO: prevent error.log spew
	AY8910(config, m_ay8910[0], 0);
	m_ay8910[0]->add_route(ALL_OUTPUTS, m_viz, 0.33, 0);
	m_ay8910[0]->add_route(ALL_OUTPUTS, m_viz, 0.33, 1);

	AY8910(config, m_ay8910[1], 0);
	m_ay8910[1]->add_route(ALL_OUTPUTS, m_viz, 0.33, 0);
	m_ay8910[1]->add_route(ALL_OUTPUTS, m_viz, 0.33, 1);

	DMG_APU(config, m_dmg[0], 0);
	m_dmg[0]->add_route(0, m_viz, 1, 0);
	m_dmg[0]->add_route(0, m_viz, 1, 1);

	DMG_APU(config, m_dmg[1], 0);
	m_dmg[1]->add_route(0, m_viz, 1, 0);
	m_dmg[1]->add_route(0, m_viz, 1, 1);

	RP2A03G(config, m_nescpu[0], 0);
	m_nescpu[0]->set_addrmap(AS_PROGRAM, &vgmplay_state::nescpu_map<0>);
	m_nescpu[0]->set_disable();
	m_nescpu[0]->add_route(ALL_OUTPUTS, m_viz, 0.50, 0);
	m_nescpu[0]->add_route(ALL_OUTPUTS, m_viz, 0.50, 1);

	RP2A03G(config, m_nescpu[1], 0);
	m_nescpu[1]->set_addrmap(AS_PROGRAM, &vgmplay_state::nescpu_map<1>);
	m_nescpu[1]->set_disable();
	m_nescpu[1]->add_route(ALL_OUTPUTS, m_viz, 0.50, 0);
	m_nescpu[1]->add_route(ALL_OUTPUTS, m_viz, 0.50, 1);

	MULTIPCM(config, m_multipcm[0], 0);
	m_multipcm[0]->set_addrmap(0, &vgmplay_state::multipcm_map<0>);
	m_multipcm[0]->add_route(0, m_viz, 1, 0);
	m_multipcm[0]->add_route(1, m_viz, 1, 1);

	MULTIPCM(config, m_multipcm[1], 0);
	m_multipcm[1]->set_addrmap(0, &vgmplay_state::multipcm_map<1>);
	m_multipcm[1]->add_route(0, m_viz, 1, 0);
	m_multipcm[1]->add_route(1, m_viz, 1, 1);

	UPD7759(config, m_upd7759[0], 0);
	m_upd7759[0]->drq().set(FUNC(vgmplay_state::upd7759_drq_w<0>));
	m_upd7759[0]->set_addrmap(0, &vgmplay_state::upd7759_map<0>);
	m_upd7759[0]->add_route(ALL_OUTPUTS, m_viz, 1.0, 0);
	m_upd7759[0]->add_route(ALL_OUTPUTS, m_viz, 1.0, 1);

	UPD7759(config, m_upd7759[1], 0);
	m_upd7759[1]->drq().set(FUNC(vgmplay_state::upd7759_drq_w<1>));
	m_upd7759[1]->set_addrmap(0, &vgmplay_state::upd7759_map<1>);
	m_upd7759[1]->add_route(ALL_OUTPUTS, m_viz, 1.0, 0);
	m_upd7759[1]->add_route(ALL_OUTPUTS, m_viz, 1.0, 1);

	OKIM6258(config, m_okim6258[0], 0);
	m_okim6258[0]->add_route(ALL_OUTPUTS, m_viz, 0.5, 0);
	m_okim6258[0]->add_route(ALL_OUTPUTS, m_viz, 0.5, 1);

	OKIM6258(config, m_okim6258[1], 0);
	m_okim6258[1]->add_route(ALL_OUTPUTS, m_viz, 0.5, 0);
	m_okim6258[1]->add_route(ALL_OUTPUTS, m_viz, 0.5, 1);

	OKIM6295(config, m_okim6295[0], 0, okim6295_device::PIN7_HIGH);
	m_okim6295[0]->set_addrmap(0, &vgmplay_state::okim6295_map<0>);
	m_okim6295[0]->add_route(ALL_OUTPUTS, m_viz, 0.25, 0);
	m_okim6295[0]->add_route(ALL_OUTPUTS, m_viz, 0.25, 1);

	OKIM6295(config, m_okim6295[1], 0, okim6295_device::PIN7_HIGH);
	m_okim6295[1]->set_addrmap(0, &vgmplay_state::okim6295_map<1>);
	m_okim6295[1]->add_route(ALL_OUTPUTS, m_viz, 0.25, 0);
	m_okim6295[1]->add_route(ALL_OUTPUTS, m_viz, 0.25, 1);

	K051649(config, m_k051649[0], 0);
	m_k051649[0]->add_route(ALL_OUTPUTS, m_viz, 0.33, 0);
	m_k051649[0]->add_route(ALL_OUTPUTS, m_viz, 0.33, 1);

	K051649(config, m_k051649[1], 0);
	m_k051649[1]->add_route(ALL_OUTPUTS, m_viz, 0.33, 0);
	m_k051649[1]->add_route(ALL_OUTPUTS, m_viz, 0.33, 1);

	K054539(config, m_k054539[0], 0);
	m_k054539[0]->set_addrmap(0, &vgmplay_state::k054539_map<0>);
	m_k054539[0]->add_route(0, m_viz, 1, 0);
	m_k054539[0]->add_route(1, m_viz, 1, 1);

	K054539(config, m_k054539[1], 0);
	m_k054539[1]->set_addrmap(0, &vgmplay_state::k054539_map<1>);
	m_k054539[1]->add_route(0, m_viz, 1, 0);
	m_k054539[1]->add_route(1, m_viz, 1, 1);

	// TODO: prevent error.log spew
	H6280(config, m_huc6280[0], 0);
	m_huc6280[0]->set_disable();
	m_huc6280[0]->add_route(0, m_viz, 1, 0);
	m_huc6280[0]->add_route(1, m_viz, 1, 1);

	H6280(config, m_huc6280[1], 0);
	m_huc6280[1]->set_disable();
	m_huc6280[1]->add_route(0, m_viz, 1, 0);
	m_huc6280[1]->add_route(1, m_viz, 1, 1);

	C140(config, m_c140[0], 0);
	m_c140[0]->set_addrmap(0, &vgmplay_state::c140_map<0>);
	m_c140[0]->add_route(0, m_viz, 0.50, 0);
	m_c140[0]->add_route(1, m_viz, 0.50, 1);

	C140(config, m_c140[1], 0);
	m_c140[1]->set_addrmap(0, &vgmplay_state::c140_map<1>);
	m_c140[1]->add_route(0, m_viz, 0.50, 0);
	m_c140[1]->add_route(1, m_viz, 0.50, 1);

	C219(config, m_c219[0], 0);
	m_c219[0]->set_addrmap(0, &vgmplay_state::c219_map<0>);
	m_c219[0]->add_route(0, m_viz, 0.50, 0);
	m_c219[0]->add_route(1, m_viz, 0.50, 1);

	C219(config, m_c219[1], 0);
	m_c219[1]->set_addrmap(0, &vgmplay_state::c219_map<1>);
	m_c219[1]->add_route(0, m_viz, 0.50, 0);
	m_c219[1]->add_route(1, m_viz, 0.50, 1);

	K053260(config, m_k053260[0], 0);
	m_k053260[0]->set_addrmap(0, &vgmplay_state::k053260_map<0>);
	m_k053260[0]->add_route(0, m_viz, 1, 0);
	m_k053260[0]->add_route(1, m_viz, 1, 1);

	K053260(config, m_k053260[1], 0);
	m_k053260[1]->set_addrmap(0, &vgmplay_state::k053260_map<1>);
	m_k053260[1]->add_route(0, m_viz, 1, 0);
	m_k053260[1]->add_route(1, m_viz, 1, 1);

	POKEY(config, m_pokey[0], 0);
	m_pokey[0]->add_route(ALL_OUTPUTS, m_viz, 0.5, 0);
	m_pokey[0]->add_route(ALL_OUTPUTS, m_viz, 0.5, 1);

	POKEY(config, m_pokey[1], 0);
	m_pokey[1]->add_route(ALL_OUTPUTS, m_viz, 0.5, 0);
	m_pokey[1]->add_route(ALL_OUTPUTS, m_viz, 0.5, 1);

	QSOUND(config, m_qsound, 0);
	m_qsound->set_addrmap(0, &vgmplay_state::qsound_map<0>);
	m_qsound->add_route(0, m_viz, 1, 0);
	m_qsound->add_route(1, m_viz, 1, 1);

	SCSP(config, m_scsp[0], 0);
	m_scsp[0]->set_addrmap(0, &vgmplay_state::scsp_map<0>);
	m_scsp[0]->add_route(0, m_viz, 1, 0);
	m_scsp[0]->add_route(1, m_viz, 1, 1);

	SCSP(config, m_scsp[1], 0);
	m_scsp[1]->set_addrmap(0, &vgmplay_state::scsp_map<1>);
	m_scsp[1]->add_route(0, m_viz, 1, 0);
	m_scsp[1]->add_route(1, m_viz, 1, 1);

	WSWAN_SND(config, m_wswan[0], 0);
	m_wswan[0]->set_headphone_connected(true);
	m_wswan[0]->set_addrmap(0, &vgmplay_state::wswan_map<0>);
	m_wswan[0]->add_route(0, m_viz, 0.50, 0);
	m_wswan[0]->add_route(1, m_viz, 0.50, 1);

	WSWAN_SND(config, m_wswan[1], 0);
	m_wswan[1]->set_headphone_connected(true);
	m_wswan[1]->set_addrmap(0, &vgmplay_state::wswan_map<1>);
	m_wswan[1]->add_route(0, m_viz, 0.50, 0);
	m_wswan[1]->add_route(1, m_viz, 0.50, 1);

	VBOYSND(config, m_vsu_vue[0], 0);
	m_vsu_vue[0]->add_route(0, m_viz, 1.0, 0);
	m_vsu_vue[0]->add_route(1, m_viz, 1.0, 1);

	VBOYSND(config, m_vsu_vue[1], 0);
	m_vsu_vue[1]->add_route(0, m_viz, 1.0, 0);
	m_vsu_vue[1]->add_route(1, m_viz, 1.0, 1);

	SAA1099(config, m_saa1099[0], 0);
	m_saa1099[0]->add_route(0, m_viz, 1.0, 0);
	m_saa1099[0]->add_route(1, m_viz, 1.0, 1);

	SAA1099(config, m_saa1099[1], 0);
	m_saa1099[1]->add_route(0, m_viz, 1.0, 0);
	m_saa1099[1]->add_route(1, m_viz, 1.0, 1);

	ES5503(config, m_es5503[0], 0);
	m_es5503[0]->set_channels(2);
	m_es5503[0]->set_addrmap(0, &vgmplay_state::es5503_map<0>);
	m_es5503[0]->add_route(ALL_OUTPUTS, m_viz, 0.5, 0);
	m_es5503[0]->add_route(ALL_OUTPUTS, m_viz, 0.5, 1);

	ES5503(config, m_es5503[1], 0);
	m_es5503[1]->set_channels(2);
	m_es5503[1]->set_addrmap(0, &vgmplay_state::es5503_map<1>);
	m_es5503[1]->add_route(ALL_OUTPUTS, m_viz, 0.5, 0);
	m_es5503[1]->add_route(ALL_OUTPUTS, m_viz, 0.5, 1);

	ES5505(config, m_es5505[0], 0);
	// TODO m_es5505[0]->set_addrmap(0, &vgmplay_state::es5505_map<0>);
	// TODO m_es5505[0]->set_addrmap(1, &vgmplay_state::es5505_map<0>);
	m_es5505[0]->set_channels(1);
	m_es5505[0]->add_route(0, m_viz, 0.5, 0);
	m_es5505[0]->add_route(1, m_viz, 0.5, 1);

	ES5505(config, m_es5505[1], 0);
	// TODO m_es5505[1]->set_addrmap(0, &vgmplay_state::es5505_map<1>);
	// TODO m_es5505[1]->set_addrmap(1, &vgmplay_state::es5505_map<1>);
	m_es5505[1]->set_channels(1);
	m_es5505[1]->add_route(0, m_viz, 0.5, 0);
	m_es5505[1]->add_route(1, m_viz, 0.5, 1);

	X1_010(config, m_x1_010[0], 0);
	m_x1_010[0]->set_addrmap(0, &vgmplay_state::x1_010_map<0>);
	m_x1_010[0]->add_route(0, m_viz, 1, 0);
	m_x1_010[0]->add_route(1, m_viz, 1, 1);

	X1_010(config, m_x1_010[1], 0);
	m_x1_010[1]->set_addrmap(0, &vgmplay_state::x1_010_map<1>);
	m_x1_010[1]->add_route(0, m_viz, 1, 0);
	m_x1_010[1]->add_route(1, m_viz, 1, 1);

	C352(config, m_c352[0], 0, 1);
	m_c352[0]->set_addrmap(0, &vgmplay_state::c352_map<0>);
	m_c352[0]->add_route(0, m_viz, 1, 0);
	m_c352[0]->add_route(1, m_viz, 1, 1);
	m_c352[0]->add_route(2, m_viz, 1, 0);
	m_c352[0]->add_route(3, m_viz, 1, 1);

	C352(config, m_c352[1], 0, 1);
	m_c352[1]->set_addrmap(0, &vgmplay_state::c352_map<1>);
	m_c352[1]->add_route(0, m_viz, 1, 0);
	m_c352[1]->add_route(1, m_viz, 1, 1);
	m_c352[1]->add_route(2, m_viz, 1, 0);
	m_c352[1]->add_route(3, m_viz, 1, 1);

	IREMGA20(config, m_ga20[0], 0);
	m_ga20[0]->set_addrmap(0, &vgmplay_state::ga20_map<0>);
	m_ga20[0]->add_route(0, m_viz, 1, 0);
	m_ga20[0]->add_route(1, m_viz, 1, 1);

	IREMGA20(config, m_ga20[1], 0);
	m_ga20[1]->set_addrmap(0, &vgmplay_state::ga20_map<1>);
	m_ga20[1]->add_route(0, m_viz, 1, 0);
	m_ga20[1]->add_route(1, m_viz, 1, 1);

	VGMVIZ(config, m_viz, 0);
	m_viz->add_route(0, "speaker", 1, 0);
	m_viz->add_route(1, "speaker", 1, 1);

	SPEAKER(config, m_speaker, 2).front();
}

ROM_START( vgmplay )
	// TODO: split up 32x to remove dependencies
	ROM_REGION( 0x4000, "master", ROMREGION_ERASE00 )
	ROM_REGION( 0x4000, "slave", ROMREGION_ERASE00 )
	ROM_REGION( 0x400000, "gamecart", ROMREGION_ERASE00 )
	ROM_REGION32_BE( 0x400000, "gamecart_sh2", ROMREGION_ERASE00 )
ROM_END

CONS( 2016, vgmplay, 0, 0, vgmplay, vgmplay, vgmplay_state, empty_init, "MAME", "VGM player", 0 )



vic10.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

    TODO:

    - memory mapping with PLA
    - PLA dump

*/

#include "emu.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "bus/pet/cass.h"
#include "bus/vic10/exp.h"
#include "bus/vcs_ctrl/ctrl.h"
#include "cpu/m6502/m6510.h"
#include "machine/input_merger.h"
#include "machine/mos6526.h"
#include "machine/ram.h"
#include "sound/mos6581.h"
#include "video/mos6566.h"


namespace {

#define MOS6566_TAG     "u2"
#define MOS6581_TAG     "u6"
#define MOS6526_TAG     "u9"
#define SCREEN_TAG      "screen"
#define TIMER_C1531_TAG "c1531"
#define CONTROL1_TAG    "joy1"
#define CONTROL2_TAG    "joy2"

class vic10_state : public driver_device
{
public:
	vic10_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "u3"),
		m_vic(*this, MOS6566_TAG),
		m_sid(*this, MOS6581_TAG),
		m_cia(*this, MOS6526_TAG),
		m_joy1(*this, CONTROL1_TAG),
		m_joy2(*this, CONTROL2_TAG),
		m_exp(*this, "exp"),
		m_ram(*this, RAM_TAG),
		m_cassette(*this, PET_DATASSETTE_PORT_TAG),
		m_color_ram(*this, "color_ram", 0x400, ENDIANNESS_LITTLE),
		m_row(*this, "ROW%u", 0),
		m_restore(*this, "RESTORE"),
		m_lock(*this, "LOCK")
	{ }

	void vic10(machine_config &config);

private:
	required_device<m6510_device> m_maincpu;
	required_device<mos6566_device> m_vic;
	required_device<mos6581_device> m_sid;
	required_device<mos6526_device> m_cia;
	required_device<vcs_control_port_device> m_joy1;
	required_device<vcs_control_port_device> m_joy2;
	required_device<vic10_expansion_slot_device> m_exp;
	required_device<ram_device> m_ram;
	optional_device<pet_datassette_port_device> m_cassette;
	memory_share_creator<uint8_t> m_color_ram;
	required_ioport_array<8> m_row;
	required_ioport m_restore;
	required_ioport m_lock;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint8_t read(offs_t offset);
	void write(offs_t offset, uint8_t data);

	uint8_t vic_videoram_r(offs_t offset);
	uint8_t vic_colorram_r(offs_t offset);

	uint8_t sid_potx_r();
	uint8_t sid_poty_r();

	uint8_t cia_pa_r();
	uint8_t cia_pb_r();
	void cia_pb_w(uint8_t data);

	uint8_t cpu_r();
	void cpu_w(uint8_t data);

	void exp_reset_w(int state);

	void vic10_mem(address_map &map) ATTR_COLD;
	void vic_colorram_map(address_map &map) ATTR_COLD;
	void vic_videoram_map(address_map &map) ATTR_COLD;
};


//**************************************************************************
//  MEMORY MANAGEMENT
//**************************************************************************

//-------------------------------------------------
//  read -
//-------------------------------------------------

uint8_t vic10_state::read(offs_t offset)
{
	// TODO this is really handled by the PLA

	uint8_t data = m_vic->bus_r();
	int lorom = 1, uprom = 1, exram = 1;

	if (offset < 0x800)
	{
		data = m_ram->pointer()[offset];
	}
	else if (offset < 0x1000)
	{
		exram = 0;
	}
	else if (offset >= 0x8000 && offset < 0xa000)
	{
		lorom = 0;
	}
	else if (offset >= 0xd000 && offset < 0xd400)
	{
		data = m_vic->read(offset & 0x3f);
	}
	else if (offset >= 0xd400 && offset < 0xd800)
	{
		data = m_sid->read(offset & 0x1f);
	}
	else if (offset >= 0xd800 && offset < 0xdc00)
	{
		data = m_color_ram[offset & 0x3ff];
	}
	else if (offset >= 0xdc00 && offset < 0xe000)
	{
		data = m_cia->read(offset & 0x0f);
	}
	else if (offset >= 0xe000)
	{
		uprom = 0;
	}

	return m_exp->cd_r(offset, data, lorom, uprom, exram);
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

void vic10_state::write(offs_t offset, uint8_t data)
{
	// TODO this is really handled by the PLA

	int lorom = 1, uprom = 1, exram = 1;

	if (offset < 0x800)
	{
		m_ram->pointer()[offset] = data;
	}
	else if (offset < 0x1000)
	{
		exram = 0;
	}
	else if (offset >= 0xd000 && offset < 0xd400)
	{
		m_vic->write(offset & 0x3f, data);
	}
	else if (offset >= 0xd400 && offset < 0xd800)
	{
		m_sid->write(offset & 0x1f, data);
	}
	else if (offset >= 0xd800 && offset < 0xdc00)
	{
		m_color_ram[offset & 0x3ff] = data & 0x0f;
	}
	else if (offset >= 0xdc00 && offset < 0xe000)
	{
		m_cia->write(offset & 0x0f, data);
	}

	m_exp->cd_w(offset, data, lorom, uprom, exram);
}


//-------------------------------------------------
//  vic_videoram_r -
//-------------------------------------------------

uint8_t vic10_state::vic_videoram_r(offs_t offset)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);

	if (offset < 0x3000)
		return program.read_byte(offset);

	return program.read_byte(0xe000 + (offset & 0x1fff));
}


//-------------------------------------------------
//  vic_colorram_r -
//-------------------------------------------------

uint8_t vic10_state::vic_colorram_r(offs_t offset)
{
	return m_color_ram[offset];
}



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( vic10_mem )
//-------------------------------------------------

void vic10_state::vic10_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(vic10_state::read), FUNC(vic10_state::write));
}


//-------------------------------------------------
//  ADDRESS_MAP( vic_videoram_map )
//-------------------------------------------------

void vic10_state::vic_videoram_map(address_map &map)
{
	map(0x0000, 0x3fff).r(FUNC(vic10_state::vic_videoram_r));
}


//-------------------------------------------------
//  ADDRESS_MAP( vic_colorram_map )
//-------------------------------------------------

void vic10_state::vic_colorram_map(address_map &map)
{
	map(0x000, 0x3ff).r(FUNC(vic10_state::vic_colorram_r));
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( vic10 )
//-------------------------------------------------

static INPUT_PORTS_START( vic10 )
	PORT_START( "ROW0" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Crsr Down Up") PORT_CODE(KEYCODE_RALT)        PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3)                                    PORT_CHAR(UCHAR_MAMEKEY(F5))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2)                                    PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1)                                    PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F4)                                    PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Crsr Right Left") PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER)             PORT_CHAR(13)
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INST DEL") PORT_CODE(KEYCODE_BACKSPACE)       PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START( "ROW1" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Shift (Left)") PORT_CODE(KEYCODE_LSHIFT)      PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)         PORT_CHAR('E')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)         PORT_CHAR('S')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)         PORT_CHAR('Z')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)         PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)         PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)         PORT_CHAR('W')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)         PORT_CHAR('3') PORT_CHAR('#')

	PORT_START( "ROW2" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)         PORT_CHAR('X')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)         PORT_CHAR('T')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)         PORT_CHAR('F')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)         PORT_CHAR('C')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6)         PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)         PORT_CHAR('D')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)         PORT_CHAR('R')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5)         PORT_CHAR('5') PORT_CHAR('%')

	PORT_START( "ROW3" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)         PORT_CHAR('V')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)         PORT_CHAR('U')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)         PORT_CHAR('H')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)         PORT_CHAR('B')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8)         PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)         PORT_CHAR('G')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)         PORT_CHAR('Y')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7)         PORT_CHAR('7') PORT_CHAR('\'')

	PORT_START( "ROW4" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)         PORT_CHAR('N')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)         PORT_CHAR('O')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)         PORT_CHAR('K')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)         PORT_CHAR('M')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0)         PORT_CHAR('0')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)         PORT_CHAR('J')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)         PORT_CHAR('I')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9)         PORT_CHAR('9') PORT_CHAR(')')

	PORT_START( "ROW5" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA)     PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)     PORT_CHAR(':') PORT_CHAR('[')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)      PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS)    PORT_CHAR('-')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)         PORT_CHAR('L')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)         PORT_CHAR('P')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)     PORT_CHAR('+')

	PORT_START( "ROW6" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH)                             PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\xE2\x86\x91  Pi") PORT_CODE(KEYCODE_DEL) PORT_CHAR(0x2191) PORT_CHAR(0x03C0)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH)                         PORT_CHAR('=')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Shift (Right)") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CLR HOME") PORT_CODE(KEYCODE_INSERT)      PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE)                             PORT_CHAR(';') PORT_CHAR(']')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE)                        PORT_CHAR('*')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH2)                        PORT_CHAR(0xA3)

	PORT_START( "ROW7" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RUN STOP") PORT_CODE(KEYCODE_HOME)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)                                 PORT_CHAR('Q')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CBM") PORT_CODE(KEYCODE_LALT)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE)                             PORT_CHAR(' ')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2)                                 PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TAB)                               PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\xE2\x86\x90") PORT_CODE(KEYCODE_TILDE)   PORT_CHAR(0x2190)
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1)                                 PORT_CHAR('1') PORT_CHAR('!')

	PORT_START( "RESTORE" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RESTORE") PORT_CODE(KEYCODE_PRTSCR)

	PORT_START( "LOCK" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  sid6581_interface sid_intf
//-------------------------------------------------

uint8_t vic10_state::sid_potx_r()
{
	uint8_t data = 0xff;

	switch (m_cia->pa_r() >> 6)
	{
	case 1: data = m_joy1->read_pot_x(); break;
	case 2: data = m_joy2->read_pot_x(); break;
	case 3:
		if (m_joy1->has_pot_x() && m_joy2->has_pot_x())
		{
			data = 1 / (1 / m_joy1->read_pot_x() + 1 / m_joy2->read_pot_x());
		}
		else if (m_joy1->has_pot_x())
		{
			data = m_joy1->read_pot_x();
		}
		else if (m_joy2->has_pot_x())
		{
			data = m_joy2->read_pot_x();
		}
		break;
	}

	return data;
}

uint8_t vic10_state::sid_poty_r()
{
	uint8_t data = 0xff;

	switch (m_cia->pa_r() >> 6)
	{
	case 1: data = m_joy1->read_pot_y(); break;
	case 2: data = m_joy2->read_pot_y(); break;
	case 3:
		if (m_joy1->has_pot_y() && m_joy2->has_pot_y())
		{
			data = 1 / (1 / m_joy1->read_pot_y() + 1 / m_joy2->read_pot_y());
		}
		else if (m_joy1->has_pot_y())
		{
			data = m_joy1->read_pot_y();
		}
		else if (m_joy2->has_pot_y())
		{
			data = m_joy2->read_pot_y();
		}
		break;
	}

	return data;
}


//-------------------------------------------------
//  MOS6526_INTERFACE( cia_intf )
//-------------------------------------------------

uint8_t vic10_state::cia_pa_r()
{
	/*

	    bit     description

	    PA0     COL0, JOY B0
	    PA1     COL1, JOY B1
	    PA2     COL2, JOY B2
	    PA3     COL3, JOY B3
	    PA4     COL4, BTNB
	    PA5     COL5
	    PA6     COL6
	    PA7     COL7

	*/

	uint8_t data = 0xff;

	// joystick
	uint8_t joy_b = m_joy2->read_joy();

	data &= (0xf0 | (joy_b & 0x0f));
	data &= ~(!BIT(joy_b, 5) << 4);

	// keyboard
	uint8_t cia_pb = m_cia->pb_r();
	uint32_t row[8] = { m_row[0]->read(), m_row[1]->read() & m_lock->read(), m_row[2]->read(), m_row[3]->read(),
						m_row[4]->read(), m_row[5]->read(), m_row[6]->read(), m_row[7]->read() };

	for (int i = 0; i < 8; i++)
	{
		if (!BIT(cia_pb, i))
		{
			if (!BIT(row[7], i)) data &= ~0x80;
			if (!BIT(row[6], i)) data &= ~0x40;
			if (!BIT(row[5], i)) data &= ~0x20;
			if (!BIT(row[4], i)) data &= ~0x10;
			if (!BIT(row[3], i)) data &= ~0x08;
			if (!BIT(row[2], i)) data &= ~0x04;
			if (!BIT(row[1], i)) data &= ~0x02;
			if (!BIT(row[0], i)) data &= ~0x01;
		}
	}

	return data;
}

uint8_t vic10_state::cia_pb_r()
{
	/*

	    bit     description

	    PB0     JOY A0
	    PB1     JOY A1
	    PB2     JOY A2
	    PB3     JOY A3
	    PB4     BTNA/_LP
	    PB5
	    PB6
	    PB7

	*/

	uint8_t data = 0xff;

	// joystick
	uint8_t joy_a = m_joy1->read_joy();

	data &= (0xf0 | (joy_a & 0x0f));
	data &= ~(!BIT(joy_a, 5) << 4);

	// keyboard
	uint8_t cia_pa = m_cia->pa_r();

	if (!BIT(cia_pa, 7)) data &= m_row[7]->read();
	if (!BIT(cia_pa, 6)) data &= m_row[6]->read();
	if (!BIT(cia_pa, 5)) data &= m_row[5]->read();
	if (!BIT(cia_pa, 4)) data &= m_row[4]->read();
	if (!BIT(cia_pa, 3)) data &= m_row[3]->read();
	if (!BIT(cia_pa, 2)) data &= m_row[2]->read();
	if (!BIT(cia_pa, 1)) data &= m_row[1]->read() & m_lock->read();
	if (!BIT(cia_pa, 0)) data &= m_row[0]->read();

	return data;
}

void vic10_state::cia_pb_w(uint8_t data)
{
	/*

	    bit     description

	    PB0     ROW0
	    PB1     ROW1
	    PB2     ROW2
	    PB3     ROW3
	    PB4     ROW4
	    PB5     ROW5
	    PB6     ROW6
	    PB7     ROW7

	*/

	m_vic->lp_w(BIT(data, 4));
}


//-------------------------------------------------
//  M6510_INTERFACE( cpu_intf )
//-------------------------------------------------

uint8_t vic10_state::cpu_r()
{
	/*

	    bit     description

	    P0      EXPANSION PORT
	    P1
	    P2
	    P3
	    P4      CASS SENS
	    P5      0

	*/

	uint8_t data = 0;

	// expansion port
	data |= m_exp->p0_r();

	// cassette sense
	data |= m_cassette->sense_r() << 4;

	return data;
}

void vic10_state::cpu_w(uint8_t data)
{
	/*

	    bit     description

	    P0      EXPANSION PORT
	    P1
	    P2
	    P3      CASS WRT
	    P4
	    P5      CASS MOTOR

	*/

	if (0 /*BIT(offset, 0)*/) // what offset?
	{
		m_exp->p0_w(BIT(data, 0));
	}

	// cassette write
	m_cassette->write(BIT(data, 3));

	// cassette motor
	m_cassette->motor_w(BIT(data, 5));
}


//-------------------------------------------------
//  VIC10_EXPANSION_INTERFACE( expansion_intf )
//-------------------------------------------------

void vic10_state::exp_reset_w(int state)
{
	if (state == ASSERT_LINE)
	{
		machine_reset();
	}
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( vic10 )
//-------------------------------------------------

void vic10_state::machine_start()
{
	// initialize memory
	uint8_t data = 0xff;

	for (offs_t offset = 0; offset < m_ram->size(); offset++)
	{
		m_ram->pointer()[offset] = data;
		if (!(offset % 64)) data ^= 0xff;
	}
}


void vic10_state::machine_reset()
{
	m_maincpu->reset();

	m_vic->reset();
	m_sid->reset();
	m_cia->reset();

	m_exp->reset();
}



//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

//-------------------------------------------------
//  machine_config( vic10 )
//-------------------------------------------------

void vic10_state::vic10(machine_config &config)
{
	// basic hardware
	M6510(config, m_maincpu, XTAL(8'000'000)/8);
	m_maincpu->set_addrmap(AS_PROGRAM, &vic10_state::vic10_mem);
	m_maincpu->read_callback().set(FUNC(vic10_state::cpu_r));
	m_maincpu->write_callback().set(FUNC(vic10_state::cpu_w));
	m_maincpu->set_pulls(0x10, 0x20);
	config.set_perfect_quantum(m_maincpu);

	INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, m6510_device::IRQ_LINE);

	// video hardware
	mos8566_device &mos8566(MOS8566(config, MOS6566_TAG, XTAL(8'000'000)/8));
	mos8566.set_cpu(m_maincpu);
	mos8566.irq_callback().set("mainirq", FUNC(input_merger_device::in_w<1>));
	mos8566.set_screen(SCREEN_TAG);
	mos8566.set_addrmap(0, &vic10_state::vic_videoram_map);
	mos8566.set_addrmap(1, &vic10_state::vic_colorram_map);

	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(VIC6566_VRETRACERATE);
	screen.set_size(VIC6567_COLUMNS, VIC6567_LINES);
	screen.set_visarea(0, VIC6567_VISIBLECOLUMNS - 1, 0, VIC6567_VISIBLELINES - 1);
	screen.set_screen_update(MOS6566_TAG, FUNC(mos6566_device::screen_update));

	// sound hardware
	SPEAKER(config, "mono").front_center();
	MOS6581(config, m_sid, XTAL(8'000'000)/8);
	m_sid->potx().set(FUNC(vic10_state::sid_potx_r));
	m_sid->poty().set(FUNC(vic10_state::sid_poty_r));
	m_sid->add_route(ALL_OUTPUTS, "mono", 1.00);

	// devices
	MOS6526(config, m_cia, XTAL(8'000'000)/8);
	m_cia->set_tod_clock(60);
	m_cia->irq_wr_callback().set("mainirq", FUNC(input_merger_device::in_w<0>));
	m_cia->cnt_wr_callback().set(m_exp, FUNC(vic10_expansion_slot_device::cnt_w));
	m_cia->sp_wr_callback().set(m_exp, FUNC(vic10_expansion_slot_device::sp_w));
	m_cia->pa_rd_callback().set(FUNC(vic10_state::cia_pa_r));
	m_cia->pb_rd_callback().set(FUNC(vic10_state::cia_pb_r));
	m_cia->pb_wr_callback().set(FUNC(vic10_state::cia_pb_w));

	PET_DATASSETTE_PORT(config, m_cassette, cbm_datassette_devices, "c1530");
	m_cassette->read_handler().set(m_cia, FUNC(mos6526_device::flag_w));

	VCS_CONTROL_PORT(config, m_joy1, vcs_control_port_devices, nullptr);
	m_joy1->trigger_wr_callback().set(MOS6566_TAG, FUNC(mos6566_device::lp_w));
	VCS_CONTROL_PORT(config, m_joy2, vcs_control_port_devices, "joy");

	VIC10_EXPANSION_SLOT(config, m_exp, XTAL(8'000'000)/8, vic10_expansion_cards, nullptr);
	m_exp->set_must_be_loaded(true);
	m_exp->irq_callback().set("mainirq", FUNC(input_merger_device::in_w<2>));
	m_exp->res_callback().set(FUNC(vic10_state::exp_reset_w));
	m_exp->cnt_callback().set(m_cia, FUNC(mos6526_device::cnt_w));
	m_exp->sp_callback().set(m_cia, FUNC(mos6526_device::sp_w));

	// software list
	SOFTWARE_LIST(config, "cart_list").set_original("vic10");

	// internal ram
	RAM(config, RAM_TAG).set_default_size("4K");
}



//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( vic10 )
	ROM_REGION( 0x100, "pla", 0 )
	ROM_LOAD( "6703.u4", 0x000, 0x100, NO_DUMP )
ROM_END

} // anonymous namespace


//**************************************************************************
//  GAME DRIVERS
//**************************************************************************

COMP( 1982, vic10, 0, 0, vic10, vic10, vic10_state, empty_init, "Commodore Business Machines", "VIC-10 / Max Machine / UltiMax (NTSC)", MACHINE_SUPPORTS_SAVE )



vic20.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*
    TODO:

    - C1540 is not working currently
    - mos6560_port_r/w should respond at 0x1000-0x100f
    - VIC21 (built in 21K ram)

*/

#include "emu.h"

#include "cbm_snqk.h"

#include "bus/cbmiec/cbmiec.h"
#include "bus/pet/cass.h"
#include "bus/vcs_ctrl/ctrl.h"
#include "bus/vic20/exp.h"
#include "bus/vic20/user.h"
#include "cpu/m6502/m6510.h"
#include "imagedev/snapquik.h"
#include "machine/6522via.h"
#include "machine/ram.h"
#include "sound/mos6560.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

#define M6502_TAG       "ue10"
#define M6522_1_TAG     "uab3"
#define M6522_2_TAG     "uab1"
#define VIC_TAG         "ub7"
#define IEC_TAG         "iec"
#define SCREEN_TAG      "screen"
#define CONTROL1_TAG    "joy1"
#define PET_USER_PORT_TAG     "user"

class vic20_state : public driver_device
{
public:
	vic20_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, M6502_TAG),
		m_via1(*this, M6522_1_TAG),
		m_via2(*this, M6522_2_TAG),
		m_vic(*this, VIC_TAG),
		m_iec(*this, CBM_IEC_TAG),
		m_joy(*this, CONTROL1_TAG),
		m_exp(*this, "exp"),
		m_user(*this, PET_USER_PORT_TAG),
		m_cassette(*this, PET_DATASSETTE_PORT_TAG),
		m_ram(*this, RAM_TAG),
		m_screen(*this, SCREEN_TAG),
		m_basic(*this, "basic"),
		m_kernal(*this, "kernal"),
		m_charom(*this, "charom"),
		m_color_ram(*this, "color_ram"),
		m_col(*this, "COL%u", 0),
		m_restore(*this, "RESTORE"),
		m_lock(*this, "LOCK")
	{ }

	void ntsc(machine_config &config);
	void pal(machine_config &config);

protected:
	void vic20(machine_config &config, const char* softlist_filter);
	void add_clocked_devices(machine_config &config, uint32_t clock);

private:
	required_device<m6502_device> m_maincpu;
	required_device<via6522_device> m_via1;
	required_device<via6522_device> m_via2;
	required_device<mos6560_device> m_vic;
	required_device<cbm_iec_device> m_iec;
	required_device<vcs_control_port_device> m_joy;
	required_device<vic20_expansion_slot_device> m_exp;
	required_device<pet_user_port_device> m_user;
	required_device<pet_datassette_port_device> m_cassette;
	required_device<ram_device> m_ram;
	required_device<screen_device> m_screen;
	required_region_ptr<uint8_t> m_basic;
	required_region_ptr<uint8_t> m_kernal;
	required_region_ptr<uint8_t> m_charom;
	required_shared_ptr<uint8_t> m_color_ram;
	required_ioport_array<8> m_col;
	required_ioport m_restore;
	required_ioport m_lock;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint8_t read(offs_t offset);
	void write(offs_t offset, uint8_t data);

	uint8_t vic_videoram_r(offs_t offset);

	void write_light_pen(int state);
	void write_user_joy0(int state);
	void write_user_joy1(int state);
	void write_user_joy2(int state);
	void write_user_light_pen(int state);
	void write_user_cassette_switch(int state);

	uint8_t via1_pa_r();
	void via1_pa_w(uint8_t data);
	void via1_pb_w(uint8_t data);

	uint8_t via2_pa_r();
	uint8_t via2_pb_r();
	void via2_pa_w(uint8_t data);
	void via2_pb_w(uint8_t data);
	void via2_ca2_w(int state);
	void via2_cb2_w(int state);

	void exp_reset_w(int state);

	DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_vc20);
	// keyboard state
	int m_key_row;
	int m_key_col;
	int m_light_pen;
	int m_user_joy0;
	int m_user_joy1;
	int m_user_joy2;
	int m_user_light_pen;
	int m_user_cassette_switch;

	enum
	{
		BLK0 = 0,
		BLK1,
		BLK2,
		BLK3,
		BLK4,
		BLK5,
		BLK6,
		BLK7
	};


	enum
	{
		RAM0 = 0,
		RAM1,
		RAM2,
		RAM3,
		RAM4,
		RAM5,
		RAM6,
		RAM7
	};


	enum
	{
		IO0 = 4,
		COLOR = 5,
		IO2 = 6,
		IO3 = 7
	};

	void vic20_mem(address_map &map) ATTR_COLD;
	void vic_colorram_map(address_map &map) ATTR_COLD;
	void vic_videoram_map(address_map &map) ATTR_COLD;
};


QUICKLOAD_LOAD_MEMBER(vic20_state::quickload_vc20)
{
	return general_cbm_loadsnap(image, m_maincpu->space(AS_PROGRAM), 0, cbm_quick_sethiaddress);
}

//**************************************************************************
//  MEMORY MANAGEMENT
//**************************************************************************

//-------------------------------------------------
//  read -
//-------------------------------------------------

uint8_t vic20_state::read(offs_t offset)
{
	uint8_t data = m_vic->bus_r();

	int ram1 = 1, ram2 = 1, ram3 = 1;
	int blk1 = 1, blk2 = 1, blk3 = 1, blk5 = 1;
	int io2 = 1, io3 = 1;

	switch ((offset >> 13) & 0x07)
	{
	case BLK0:
		switch ((offset >> 10) & 0x07)
		{
		case RAM0:
			data = m_ram->pointer()[offset & 0x3ff];
			break;

		case RAM1: ram1 = 0; break;
		case RAM2: ram2 = 0; break;
		case RAM3: ram3 = 0; break;

		default:
			data = m_ram->pointer()[0x400 + (offset & 0xfff)];
			break;
		}
		break;

	case BLK1: blk1 = 0; break;
	case BLK2: blk2 = 0; break;
	case BLK3: blk3 = 0; break;

	case BLK4:
		switch ((offset >> 10) & 0x07)
		{
		default:
			data = m_charom[offset & 0xfff];
			break;

		case IO0:
			if (BIT(offset, 4))
			{
				data = m_via1->read(offset & 0x0f);
			}
			else if (BIT(offset, 5))
			{
				data = m_via2->read(offset & 0x0f);
			}
			else if (offset >= 0x9000 && offset < 0x9010)
			{
				data = m_vic->read(offset & 0x0f);
			}
			break;

		case COLOR:
			data = m_color_ram[offset & 0x3ff];
			break;

		case IO2: io2 = 0; break;
		case IO3: io3 = 0; break;
		}
		break;

	case BLK5: blk5 = 0; break;

	case BLK6:
		data = m_basic[offset & 0x1fff];
		break;

	case BLK7:
		data = m_kernal[offset & 0x1fff];
		break;
	}

	return m_exp->cd_r(offset & 0x1fff, data, ram1, ram2, ram3, blk1, blk2, blk3, blk5, io2, io3);
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

void vic20_state::write(offs_t offset, uint8_t data)
{
	int ram1 = 1, ram2 = 1, ram3 = 1;
	int blk1 = 1, blk2 = 1, blk3 = 1, blk5 = 1;
	int io2 = 1, io3 = 1;

	switch ((offset >> 13) & 0x07)
	{
	case BLK0:
		switch ((offset >> 10) & 0x07)
		{
		case RAM0:
			m_ram->pointer()[offset] = data;
			break;

		case RAM1: ram1 = 0; break;
		case RAM2: ram2 = 0; break;
		case RAM3: ram3 = 0; break;

		default:
			m_ram->pointer()[0x400 + (offset & 0xfff)] = data;
			break;
		}
		break;

	case BLK1: blk1 = 0; break;
	case BLK2: blk2 = 0; break;
	case BLK3: blk3 = 0; break;

	case BLK4:
		switch ((offset >> 10) & 0x07)
		{
		case IO0:
			if (BIT(offset, 4))
			{
				m_via1->write(offset & 0x0f, data);
			}
			else if (BIT(offset, 5))
			{
				m_via2->write(offset & 0x0f, data);
			}
			else if (offset >= 0x9000 && offset < 0x9010)
			{
				m_vic->write(offset & 0x0f, data);
			}
			break;

		case COLOR:
			m_color_ram[offset & 0x3ff] = data & 0x0f;
			break;

		case IO2: io2 = 0; break;
		case IO3: io3 = 0; break;
		}
		break;

	case BLK5: blk5 = 0; break;
	}

	m_exp->cd_w(offset & 0x1fff, data, ram1, ram2, ram3, blk1, blk2, blk3, blk5, io2, io3);
}


//-------------------------------------------------
//  vic_videoram_r -
//-------------------------------------------------

uint8_t vic20_state::vic_videoram_r(offs_t offset)
{
	int ram1 = 1, ram2 = 1, ram3 = 1;
	int blk1 = 1, blk2 = 1, blk3 = 1, blk5 = 1;
	int io2 = 1, io3 = 1;

	uint8_t data = 0;

	if (BIT(offset, 13))
	{
		switch ((offset >> 10) & 0x07)
		{
		case RAM0:
			data = m_ram->pointer()[offset & 0x3ff];
			break;

		case RAM1: ram1 = 0; break;
		case RAM2: ram2 = 0; break;
		case RAM3: ram3 = 0; break;

		default:
			data = m_ram->pointer()[0x400 + (offset & 0xfff)];
			break;
		}
	}
	else
	{
		data = m_charom[offset & 0xfff];
	}

	return m_exp->cd_r(offset & 0x1fff, data, ram1, ram2, ram3, blk1, blk2, blk3, blk5, io2, io3);
}



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( vic20_mem )
//-------------------------------------------------

void vic20_state::vic20_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(vic20_state::read), FUNC(vic20_state::write));
}


//-------------------------------------------------
//  ADDRESS_MAP( vic_videoram_map )
//-------------------------------------------------

void vic20_state::vic_videoram_map(address_map &map)
{
	map(0x0000, 0x3fff).r(FUNC(vic20_state::vic_videoram_r));
}


//-------------------------------------------------
//  ADDRESS_MAP( vic_colorram_map )
//-------------------------------------------------

void vic20_state::vic_colorram_map(address_map &map)
{
	map(0x000, 0x3ff).ram().share("color_ram");
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( vic20 )
//-------------------------------------------------

static INPUT_PORTS_START( vic20 )
	PORT_START( "COL0" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Del  Inst") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH2)     PORT_CHAR(0xA3)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)          PORT_CHAR('+')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9)              PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7)              PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5)              PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)              PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1)              PORT_CHAR('1') PORT_CHAR('!')

	PORT_START( "COL1" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE)     PORT_CHAR('*')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)              PORT_CHAR('P')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)              PORT_CHAR('I')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)              PORT_CHAR('Y')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)              PORT_CHAR('R')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)              PORT_CHAR('W')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\xE2\x86\x90") PORT_CODE(KEYCODE_TILDE) PORT_CHAR(0x2190)

	PORT_START( "COL2" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Crsr Right Left") PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE)          PORT_CHAR(';') PORT_CHAR(']')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)              PORT_CHAR('L')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)              PORT_CHAR('J')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)              PORT_CHAR('G')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)              PORT_CHAR('D')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)              PORT_CHAR('A')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TAB)            PORT_CHAR(UCHAR_SHIFT_2)

	PORT_START( "COL3" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Crsr Down Up") PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH)          PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA)          PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)              PORT_CHAR('N')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)              PORT_CHAR('V')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)              PORT_CHAR('X')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Shift (Left)") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Stop Run") PORT_CODE(KEYCODE_HOME)

	PORT_START( "COL4" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F1)             PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Shift (Right)") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)           PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)              PORT_CHAR('M')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)              PORT_CHAR('B')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)              PORT_CHAR('C')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)              PORT_CHAR('Z')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE)          PORT_CHAR(' ')

	PORT_START( "COL5" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2)             PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH)      PORT_CHAR('=')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)          PORT_CHAR(':') PORT_CHAR('[')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)              PORT_CHAR('K')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)              PORT_CHAR('H')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)              PORT_CHAR('F')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)              PORT_CHAR('S')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CBM") PORT_CODE(KEYCODE_LCONTROL)

	PORT_START( "COL6" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F3)             PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\xE2\x86\x91  Pi") PORT_CODE(KEYCODE_DEL) PORT_CHAR(0x2191,'^') PORT_CHAR(0x03C0)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE)      PORT_CHAR('@')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)              PORT_CHAR('O')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)              PORT_CHAR('U')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)              PORT_CHAR('T')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)              PORT_CHAR('E')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)              PORT_CHAR('Q')

	PORT_START( "COL7" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F4)             PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Home  Clr") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS)         PORT_CHAR('-')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0)              PORT_CHAR('0')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8)              PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6)              PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)              PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2)              PORT_CHAR('2') PORT_CHAR('"')

	PORT_START( "RESTORE" )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RESTORE") PORT_CODE(KEYCODE_PRTSCR) PORT_WRITE_LINE_DEVICE_MEMBER(M6522_1_TAG, FUNC(via6522_device::write_ca1))

	PORT_START( "LOCK" )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( vic1001 )
//-------------------------------------------------

static INPUT_PORTS_START( vic1001 )
	PORT_INCLUDE( vic20 )

	PORT_MODIFY( "COL0" )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR(0xA5)
INPUT_PORTS_END


//-------------------------------------------------
//  INPUT_PORTS( vic20s )
//-------------------------------------------------

static INPUT_PORTS_START( vic20s )
	PORT_INCLUDE( vic20 )

	PORT_MODIFY( "COL0" )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH2)     PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)          PORT_CHAR('-')

	PORT_MODIFY( "COL1" )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE)     PORT_CHAR('@')

	PORT_MODIFY( "COL2" )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE)          PORT_CHAR(0x00C4)

	PORT_MODIFY( "COL5" )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH)      PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)          PORT_CHAR(0x00D6)

	PORT_MODIFY( "COL6" )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE)      PORT_CHAR(0x00C5)

	PORT_MODIFY( "COL7" )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS)         PORT_CHAR('=')
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

uint8_t vic20_state::via1_pa_r()
{
	/*

	    bit     description

	    PA0     SERIAL CLK IN
	    PA1     SERIAL DATA IN
	    PA2     JOY 0 (UP)
	    PA3     JOY 1 (DOWN)
	    PA4     JOY 2 (LEFT)
	    PA5     LITE PEN (FIRE)
	    PA6     CASS SWITCH
	    PA7

	*/

	uint8_t data = 0;

	// serial clock in
	data |= m_iec->clk_r();

	// serial data in
	data |= m_iec->data_r() << 1;

	// joystick / user port
	uint8_t joy = m_joy->read_joy();

	data |= (m_user_joy0 && BIT(joy, 0)) << 2;
	data |= (m_user_joy1 && BIT(joy, 1)) << 3;
	data |= (m_user_joy2 && BIT(joy, 2)) << 4;
	data |= (m_user_light_pen && BIT(joy, 5)) << 5;

	// cassette switch
	data |= (m_user_cassette_switch && m_cassette->sense_r()) << 6;

	return data;
}

void vic20_state::via1_pa_w(uint8_t data)
{
	/*

	    bit     description

	    PA0
	    PA1
	    PA2
	    PA3
	    PA4
	    PA5     LITE PEN (FIRE)
	    PA6
	    PA7     SERIAL ATN OUT

	*/

	// light pen strobe
	m_user->write_7(BIT(data, 5));
	m_vic->lp_w(BIT(data, 5));

	// serial attention out
	m_user->write_9(!BIT(data, 7));
	m_iec->host_atn_w(!BIT(data, 7));
}

void vic20_state::via1_pb_w(uint8_t data)
{
	m_user->write_c((data>>0)&1);
	m_user->write_d((data>>1)&1);
	m_user->write_e((data>>2)&1);
	m_user->write_f((data>>3)&1);
	m_user->write_h((data>>4)&1);
	m_user->write_j((data>>5)&1);
	m_user->write_k((data>>6)&1);
	m_user->write_l((data>>7)&1);
}

uint8_t vic20_state::via2_pa_r()
{
	/*

	    bit     description

	    PA0     ROW 0
	    PA1     ROW 1
	    PA2     ROW 2
	    PA3     ROW 3
	    PA4     ROW 4
	    PA5     ROW 5
	    PA6     ROW 6
	    PA7     ROW 7

	*/

	uint8_t data = 0xff;

	for (int i = 0; i < 8; i++)
	{
		if (!BIT(m_key_col, i)) data &= m_col[i]->read();
	}

	return data;
}

uint8_t vic20_state::via2_pb_r()
{
	/*

	    bit     description

	    PB0     COL 0
	    PB1     COL 1
	    PB2     COL 2
	    PB3     COL 3
	    PB4     COL 4
	    PB5     COL 5
	    PB6     COL 6
	    PB7     COL 7, JOY 3 (RIGHT)

	*/

	uint8_t data = 0xff;

	// joystick
	uint8_t joy = m_joy->read_joy();

	data &= (BIT(joy, 3) << 7) | 0x7f;

	for (int i = 0; i < 8; i++)
	{
		if (!BIT(m_key_row, i))
			for (int c = 0; c < 8; c++)
				if (!BIT(m_col[c]->read(), i))
					data &= ~(1 << c);
	}

	return data;
}

void vic20_state::via2_pa_w(uint8_t data)
{
	/*

	    bit     description

	    PA0     ROW 0
	    PA1     ROW 1
	    PA2     ROW 2
	    PA3     ROW 3
	    PA4     ROW 4
	    PA5     ROW 5
	    PA6     ROW 6
	    PA7     ROW 7

	*/

	// keyboard row
	m_key_row = data;
}

void vic20_state::via2_pb_w(uint8_t data)
{
	/*

	    bit     description

	    PB0     COL 0
	    PB1     COL 1
	    PB2     COL 2
	    PB3     COL 3, CASS WRITE
	    PB4     COL 4
	    PB5     COL 5
	    PB6     COL 6
	    PB7     COL 7

	*/

	// cassette write
	m_cassette->write(BIT(data, 3));

	// keyboard column
	m_key_col = data;
}

void vic20_state::via2_ca2_w(int state)
{
	// serial clock out
	m_iec->host_clk_w(!state);
}

void vic20_state::via2_cb2_w(int state)
{
	// serial data out
	m_iec->host_data_w(!state);
}


//-------------------------------------------------
//  VIC20_EXPANSION_INTERFACE( expansion_intf )
//-------------------------------------------------

void vic20_state::exp_reset_w(int state)
{
	if (!state)
	{
		machine_reset();
	}
}


//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( vic20 )
//-------------------------------------------------

void vic20_state::machine_start()
{
	// initialize memory
	uint8_t data = 0xff;

	for (offs_t offset = 0; offset < m_ram->size(); offset++)
	{
		m_ram->pointer()[offset] = data;
		if (!(offset % 64)) data ^= 0xff;
	}

	m_key_row = 0xff;
	m_key_col = 0xff;

	// state saving
	save_item(NAME(m_key_row));
	save_item(NAME(m_key_col));
	save_item(NAME(m_light_pen));
	save_item(NAME(m_user_joy0));
	save_item(NAME(m_user_joy1));
	save_item(NAME(m_user_joy2));
	save_item(NAME(m_user_light_pen));
	save_item(NAME(m_user_cassette_switch));
}


void vic20_state::machine_reset()
{
	m_maincpu->reset();

	m_vic->reset();
	m_via1->reset();
	m_via2->reset();

	m_iec->reset();
	m_exp->reset();

	m_user->write_3(0);
	m_user->write_3(1);
}

void vic20_state::write_user_joy0(int state)
{
	m_user_joy0 = state;
}

void vic20_state::write_user_joy1(int state)
{
	m_user_joy1 = state;
}

void vic20_state::write_user_joy2(int state)
{
	m_user_joy2 = state;
}

void vic20_state::write_light_pen(int state)
{
	m_light_pen = state;
	m_vic->lp_w(m_light_pen && m_user_light_pen);
}

void vic20_state::write_user_light_pen(int state)
{
	m_user_light_pen = state;
	m_vic->lp_w(m_light_pen && m_user_light_pen);
}

void vic20_state::write_user_cassette_switch(int state)
{
	m_user_cassette_switch = state;
}

//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

void vic20_state::vic20(machine_config &config, const char* softlist_filter)
{
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	m_screen->set_screen_update(VIC_TAG, FUNC(mos6560_device::screen_update));

	m_vic->set_screen(SCREEN_TAG);
	m_vic->set_addrmap(0, &vic20_state::vic_videoram_map);
	m_vic->set_addrmap(1, &vic20_state::vic_colorram_map);

	m_vic->potx_rd_callback().set(m_joy, FUNC(vcs_control_port_device::read_pot_x));
	m_vic->poty_rd_callback().set(m_joy, FUNC(vcs_control_port_device::read_pot_y));
	m_vic->add_route(ALL_OUTPUTS, "mono", 0.25);

	PET_DATASSETTE_PORT(config, m_cassette, 0);
	cbm_datassette_devices(*m_cassette);
	m_cassette->set_default_option("c1530");
	m_cassette->read_handler().set(m_via2, FUNC(via6522_device::write_ca1));

	cbm_iec_slot_device::add(config, m_iec, "c1541");
	m_iec->srq_callback().set(m_via2, FUNC(via6522_device::write_cb1));

	VCS_CONTROL_PORT(config, m_joy, 0);
	vcs_control_port_devices(*m_joy);
	m_joy->set_default_option("joy");
	m_joy->trigger_wr_callback().set(FUNC(vic20_state::write_light_pen));

	PET_USER_PORT(config, m_user, vic20_user_port_cards, nullptr);
	m_user->p3_handler().set(FUNC(vic20_state::exp_reset_w));
	m_user->p4_handler().set(FUNC(vic20_state::write_user_joy0));
	m_user->p5_handler().set(FUNC(vic20_state::write_user_joy1));
	m_user->p6_handler().set(FUNC(vic20_state::write_user_joy2));
	m_user->p7_handler().set(FUNC(vic20_state::write_user_light_pen));
	m_user->p8_handler().set(FUNC(vic20_state::write_user_cassette_switch));
	m_user->pb_handler().set(m_via1, FUNC(via6522_device::write_cb1));
	m_user->pc_handler().set(m_via1, FUNC(via6522_device::write_pb0));
	m_user->pd_handler().set(m_via1, FUNC(via6522_device::write_pb1));
	m_user->pe_handler().set(m_via1, FUNC(via6522_device::write_pb2));
	m_user->pf_handler().set(m_via1, FUNC(via6522_device::write_pb3));
	m_user->ph_handler().set(m_via1, FUNC(via6522_device::write_pb4));
	m_user->pj_handler().set(m_via1, FUNC(via6522_device::write_pb5));
	m_user->pk_handler().set(m_via1, FUNC(via6522_device::write_pb6));
	m_user->pl_handler().set(m_via1, FUNC(via6522_device::write_pb7));
	m_user->pm_handler().set(m_via1, FUNC(via6522_device::write_cb2));

	QUICKLOAD(config, "quickload", "p00,prg", CBM_QUICKLOAD_DELAY).set_load_callback(FUNC(vic20_state::quickload_vc20));

	SOFTWARE_LIST(config, "cart_list").set_original("vic1001_cart").set_filter(softlist_filter);
	SOFTWARE_LIST(config, "cass_list").set_original("vic1001_cass").set_filter(softlist_filter);
	SOFTWARE_LIST(config, "flop_list").set_original("vic1001_flop").set_filter(softlist_filter);

	RAM(config, m_ram);
	m_ram->set_default_size("5K");
}


void vic20_state::add_clocked_devices(machine_config &config, uint32_t clock)
{
	// basic machine hardware
	M6502(config, m_maincpu, clock);
	m_maincpu->set_addrmap(AS_PROGRAM, &vic20_state::vic20_mem);

	MOS6522(config, m_via1, clock);
	m_via1->readpa_handler().set(FUNC(vic20_state::via1_pa_r));
	m_via1->writepa_handler().set(FUNC(vic20_state::via1_pa_w));
	m_via1->writepb_handler().set(FUNC(vic20_state::via1_pb_w));
	m_via1->cb1_handler().set(m_user, FUNC(pet_user_port_device::write_b));
	m_via1->ca2_handler().set(m_cassette, FUNC(pet_datassette_port_device::motor_w));
	m_via1->cb2_handler().set(m_user, FUNC(pet_user_port_device::write_m));
	m_via1->irq_handler().set_inputline(m_maincpu, M6502_NMI_LINE);

	MOS6522(config, m_via2, clock);
	m_via2->readpa_handler().set(FUNC(vic20_state::via2_pa_r));
	m_via2->readpb_handler().set(FUNC(vic20_state::via2_pb_r));
	m_via2->writepa_handler().set(FUNC(vic20_state::via2_pa_w));
	m_via2->writepb_handler().set(FUNC(vic20_state::via2_pb_w));
	m_via2->ca2_handler().set(FUNC(vic20_state::via2_ca2_w));
	m_via2->cb2_handler().set(FUNC(vic20_state::via2_cb2_w));
	m_via2->irq_handler().set_inputline(m_maincpu, M6502_IRQ_LINE);

	// video/sound hardware
	SPEAKER(config, "mono").front_center();

	// devices
	VIC20_EXPANSION_SLOT(config, m_exp, clock, vic20_expansion_cards, nullptr);
	m_exp->irq_wr_callback().set_inputline(m_maincpu, M6502_IRQ_LINE);
	m_exp->nmi_wr_callback().set_inputline(m_maincpu, M6502_NMI_LINE);
	m_exp->res_wr_callback().set(FUNC(vic20_state::exp_reset_w));
}


void vic20_state::ntsc(machine_config &config)
{
	add_clocked_devices(config, MOS6560_CLOCK);
	MOS6560(config, m_vic, MOS6560_CLOCK);
	vic20(config, "NTSC");

	m_screen->set_refresh_hz(MOS6560_VRETRACERATE);
	m_screen->set_size((MOS6560_XSIZE + 7) & ~7, MOS6560_YSIZE);
	m_screen->set_visarea(MOS6560_MAME_XPOS, MOS6560_MAME_XPOS + MOS6560_MAME_XSIZE - 1, MOS6560_MAME_YPOS, MOS6560_MAME_YPOS + MOS6560_MAME_YSIZE - 1);
}


void vic20_state::pal(machine_config &config)
{
	add_clocked_devices(config, MOS6561_CLOCK);
	MOS6561(config, m_vic, MOS6561_CLOCK);
	vic20(config, "PAL");

	m_screen->set_refresh_hz(MOS6561_VRETRACERATE);
	m_screen->set_size((MOS6561_XSIZE + 7) & ~7, MOS6561_YSIZE);
	m_screen->set_visarea(MOS6561_MAME_XPOS, MOS6561_MAME_XPOS + MOS6561_MAME_XSIZE - 1, MOS6561_MAME_YPOS, MOS6561_MAME_YPOS + MOS6561_MAME_YSIZE - 1);
}



//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

//-------------------------------------------------
//  ROM( vic1001 )
//-------------------------------------------------

ROM_START( vic1001 )
	ROM_REGION( 0x2000, "basic", 0 )
	ROM_LOAD( "901486-01", 0x0000, 0x2000, CRC(db4c43c1) SHA1(587d1e90950675ab6b12d91248a3f0d640d02e8d) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROM_LOAD( "901486-02", 0x0000, 0x2000, CRC(336900d7) SHA1(c9ead45e6674d1042ca6199160e8583c23aeac22) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901460-02", 0x0000, 0x1000, CRC(fcfd8a4b) SHA1(dae61ac03065aa2904af5c123ce821855898c555) )
ROM_END


//-------------------------------------------------
//  ROM( vic20 )
//-------------------------------------------------

ROM_START( vic20 )
	ROM_REGION( 0x2000, "basic", 0 )
	ROM_LOAD( "901486-01.ue11", 0x0000, 0x2000, CRC(db4c43c1) SHA1(587d1e90950675ab6b12d91248a3f0d640d02e8d) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROM_SYSTEM_BIOS( 0, "cbm", "Original" )
	ROMX_LOAD( "901486-06.ue12", 0x0000, 0x2000, CRC(e5e7c174) SHA1(06de7ec017a5e78bd6746d89c2ecebb646efeb19), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "jiffydos", "JiffyDOS" )
	ROMX_LOAD( "jiffydos vic-20 ntsc.ue12", 0x0000, 0x2000, CRC(683a757f) SHA1(83fb83e97b5a840311dbf7e1fe56fe828f41936d), ROM_BIOS(1) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901460-03.ud7", 0x0000, 0x1000, CRC(83e032a6) SHA1(4fd85ab6647ee2ac7ba40f729323f2472d35b9b4) )
ROM_END


//-------------------------------------------------
//  ROM( vic20p )
//-------------------------------------------------

ROM_START( vic20p )
	ROM_REGION( 0x2000, "basic", 0 )
	ROM_LOAD( "901486-01.ue11", 0x0000, 0x2000, CRC(db4c43c1) SHA1(587d1e90950675ab6b12d91248a3f0d640d02e8d) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROM_SYSTEM_BIOS( 0, "cbm", "Original" )
	ROMX_LOAD( "901486-07.ue12", 0x0000, 0x2000, CRC(4be07cb4) SHA1(ce0137ed69f003a299f43538fa9eee27898e621e), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "jiffydos", "JiffyDOS" )
	ROMX_LOAD( "jiffydos vic-20 pal.ue12", 0x0000, 0x2000, CRC(705e7810) SHA1(5a03623a4b855531b8bffd756f701306f128be2d), ROM_BIOS(1) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "901460-03.ud7", 0x0000, 0x1000, CRC(83e032a6) SHA1(4fd85ab6647ee2ac7ba40f729323f2472d35b9b4) )
ROM_END


//-------------------------------------------------
//  ROM( vic20_se )
//-------------------------------------------------

ROM_START( vic20_se )
	ROM_REGION( 0x2000, "basic", 0 )
	ROM_LOAD( "901486-01.ue11", 0x0000, 0x2000, CRC(db4c43c1) SHA1(587d1e90950675ab6b12d91248a3f0d640d02e8d) )

	ROM_REGION( 0x2000, "kernal", 0 )
	ROM_LOAD( "nec22081.206", 0x0000, 0x2000, CRC(b2a60662) SHA1(cb3e2f6e661ea7f567977751846ce9ad524651a3) )

	ROM_REGION( 0x1000, "charom", 0 )
	ROM_LOAD( "nec22101.207", 0x0000, 0x1000, CRC(d808551d) SHA1(f403f0b0ce5922bd61bbd768bdd6f0b38e648c9f) )
ROM_END

} // anonymous namespace


//**************************************************************************
//  GAME DRIVERS
//**************************************************************************

//    YEAR  NAME      PARENT   COMPAT  MACHINE  INPUT    CLASS        INIT        COMPANY                        FULLNAME                   FLAGS
COMP( 1980, vic1001,  0,       0,      ntsc,    vic1001, vic20_state, empty_init, "Commodore Business Machines", "VIC-1001 (Japan)",        MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
COMP( 1981, vic20,    vic1001, 0,      ntsc,    vic20,   vic20_state, empty_init, "Commodore Business Machines", "VIC-20 (NTSC)",           MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
COMP( 1981, vic20p,   vic1001, 0,      pal,     vic20,   vic20_state, empty_init, "Commodore Business Machines", "VIC-20 / VC-20 (PAL)",    MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
COMP( 1981, vic20_se, vic1001, 0,      pal,     vic20s,  vic20_state, empty_init, "Commodore Business Machines", "VIC-20 (Sweden/Finland)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )



victor9k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*******************************************************************************

Victor 9000 / ACT Sirius 1 emulation

TODO:
- contrast
- expansion bus:
  * Z80 card
  * RAM cards
  * clock cards

*******************************************************************************/

#include "emu.h"

#include "victor9k_fdc.h"
#include "victor9k_hdc.h"
#include "victor9k_kb.h"

#include "bus/centronics/ctronics.h"
#include "bus/ieee488/ieee488.h"
#include "bus/nscsi/s1410.h"
#include "bus/rs232/rs232.h"
#include "cpu/i86/i86.h"
#include "imagedev/floppy.h"
#include "machine/6522via.h"
#include "machine/mc6852.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/ram.h"
#include "machine/rescap.h"
#include "machine/z80sio.h"
#include "sound/flt_biquad.h"
#include "sound/hc55516.h"
#include "video/mc6845.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "formats/victor9k_dsk.h"

#include <iostream>

//******************************************************************************
//  LOGGING
//******************************************************************************

#define LOG_CONF      (1U << 1)
#define LOG_KEYBOARD  (1U << 2)
#define LOG_DISPLAY   (1U << 3)

//#define VERBOSE (LOG_CONF|LOG_DISPLAY|LOG_KEYBOARD)
//#define LOG_OUTPUT_STREAM std::cout

#include "logmacro.h"

#define LOGCONF(...)     LOGMASKED(LOG_CONF,  __VA_ARGS__)
#define LOGKEYBOARD(...) LOGMASKED(LOG_KEYBOARD,  __VA_ARGS__)
#define LOGDISPLAY(...)  LOGMASKED(LOG_DISPLAY,  __VA_ARGS__)


//******************************************************************************
//  MACROS
//******************************************************************************

#define I8088_TAG       "8l"
#define I8253_TAG       "13h"
#define I8259A_TAG      "7l"
#define UPD7201_TAG     "16e"
#define HD46505S_TAG    "11a"
#define MC6852_TAG      "11b"
#define HC55516_TAG     "15c"
#define M6522_1_TAG     "m6522_1"
#define M6522_2_TAG     "m6522_2"
#define M6522_3_TAG     "14l"
#define DAC0808_0_TAG   "5b"
#define DAC0808_1_TAG   "5c"
#define RS232_A_TAG     "rs232a"
#define RS232_B_TAG     "rs232b"
#define SCREEN_TAG      "screen"
#define KB_TAG          "kb"


namespace {

class victor9k_state : public driver_device
{
public:
	victor9k_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, I8088_TAG),
		m_ieee488(*this, IEEE488_TAG),
		m_pic(*this, I8259A_TAG),
		m_pit(*this, I8253_TAG),
		m_upd7201(*this, UPD7201_TAG),
		m_ssda(*this, MC6852_TAG),
		m_via1(*this, M6522_1_TAG),
		m_via2(*this, M6522_2_TAG),
		m_via3(*this, M6522_3_TAG),
		m_cvsd(*this, HC55516_TAG),
		m_crtc(*this, HD46505S_TAG),
		m_ram(*this, RAM_TAG),
		m_cvsd_filter(*this, "cvsd_filter"),
		m_cvsd_filter2(*this, "cvsd_filter2"),
		m_kb(*this, KB_TAG),
		m_fdc(*this, "fdc"),
		m_scsibus(*this, "scsi"),
		m_hdc(*this, "scsi:7:v9kdmaib"),
		m_centronics(*this, "centronics"),
		m_rs232a(*this, RS232_A_TAG),
		m_rs232b(*this, RS232_B_TAG),
		m_palette(*this, "palette"),
		m_screen(*this, SCREEN_TAG),
		m_rom(*this, I8088_TAG),
		m_video_ram(*this, "video_ram"),
		m_brt(0),
		m_cont(0),
		m_hires(0),
		m_via1_irq(CLEAR_LINE),
		m_via2_irq(CLEAR_LINE),
		m_via3_irq(CLEAR_LINE),
		m_fdc_irq(CLEAR_LINE),
		m_ssda_irq(CLEAR_LINE),
		m_kbrdy(1),
		m_kbackctl(0)
	{ }

	void victor9k(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// devices
	required_device<cpu_device> m_maincpu;
	required_device<ieee488_device> m_ieee488;
	required_device<pic8259_device> m_pic;
	required_device<pit8253_device> m_pit;
	required_device<upd7201_device> m_upd7201;
	required_device<mc6852_device> m_ssda;
	required_device<via6522_device> m_via1;
	required_device<via6522_device> m_via2;
	required_device<via6522_device> m_via3;
	required_device<hc55516_device> m_cvsd;
	required_device<mc6845_device> m_crtc;
	required_device<ram_device> m_ram;
	optional_device<filter_biquad_device> m_cvsd_filter;
	optional_device<filter_biquad_device> m_cvsd_filter2;
	required_device<victor_9000_keyboard_device> m_kb;
	required_device<victor_9000_fdc_device> m_fdc;
	required_device<nscsi_bus_device> m_scsibus;
	required_device<victor_9000_hdc_device> m_hdc;
	required_device<centronics_device> m_centronics;
	required_device<rs232_port_device> m_rs232a;
	required_device<rs232_port_device> m_rs232b;
	required_device<palette_device> m_palette;
	required_device<screen_device> m_screen;
	required_memory_region m_rom;
	required_shared_ptr<uint8_t> m_video_ram;

	// video state
	int m_brt;
	int m_cont;
	int m_hires;

	// interrupts
	int m_via1_irq;
	int m_via2_irq;
	int m_via3_irq;
	int m_fdc_irq;
	int m_ssda_irq;

	// keyboard
	int m_kbrdy;
	int m_kbackctl;

	void via1_pa_w(uint8_t data);
	void write_nfrd(int state);
	void write_ndac(int state);
	void via1_pb_w(uint8_t data);
	void via1_irq_w(int state);
	void codec_vol_w(int state);

	void via2_pa_w(uint8_t data);
	void via2_pb_w(uint8_t data);
	void via2_irq_w(int state);

	void via3_pb_w(uint8_t data);
	void via3_irq_w(int state);

	void fdc_irq_w(int state);

	void ssda_irq_w(int state);
	void ssda_sm_dtr_w(int state);

	void kbrdy_w(int state);
	void kbdata_w(int state);
	void vert_w(int state);

	uint8_t hd_dma_r(offs_t offset);
	void hd_dma_w(offs_t offset, uint8_t data);

	MC6845_UPDATE_ROW( crtc_update_row );
	MC6845_BEGIN_UPDATE( crtc_begin_update );

	void victor9k_palette(palette_device &palette) const;

	void update_kback();

	static void scsi_devices(device_slot_interface &device);

	void victor9k_mem(address_map &map) ATTR_COLD;
};


//******************************************************************************
//  ADDRESS MAPS
//******************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( victor9k_mem )
//-------------------------------------------------

void victor9k_state::victor9k_mem(address_map &map)
{
	map(0x00000, 0x1ffff).ram();
	map(0x20000, 0xdffff).noprw();
	map(0xe0000, 0xe0001).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0xe0020, 0xe0023).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xe0040, 0xe0043).rw(m_upd7201, FUNC(upd7201_device::cd_ba_r), FUNC(upd7201_device::cd_ba_w));
	map(0xe8000, 0xe8000).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w));
	map(0xe8001, 0xe8001).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0xe8020, 0xe802f).m(m_via1, FUNC(via6522_device::map));
	map(0xe8040, 0xe804f).m(m_via2, FUNC(via6522_device::map));
	map(0xe8060, 0xe8061).rw(m_ssda, FUNC(mc6852_device::read), FUNC(mc6852_device::write));
	map(0xe8080, 0xe808f).m(m_via3, FUNC(via6522_device::map));
	map(0xe80a0, 0xe80af).rw(m_fdc, FUNC(victor_9000_fdc_device::cs5_r), FUNC(victor_9000_fdc_device::cs5_w));
	map(0xe80c0, 0xe80cf).rw(m_fdc, FUNC(victor_9000_fdc_device::cs6_r), FUNC(victor_9000_fdc_device::cs6_w));
	map(0xe80e0, 0xe80ef).rw(m_fdc, FUNC(victor_9000_fdc_device::cs7_r), FUNC(victor_9000_fdc_device::cs7_w));
	map(0xef300, 0xef3ff).rw(m_hdc, FUNC(victor_9000_hdc_device::read), FUNC(victor_9000_hdc_device::write));
	map(0xf0000, 0xf0fff).mirror(0x1000).ram().share("video_ram");
	map(0xf8000, 0xf9fff).mirror(0x6000).rom().region(I8088_TAG, 0);
}



//******************************************************************************
//  INPUT PORTS
//******************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( victor9k )
//-------------------------------------------------

static INPUT_PORTS_START( victor9k )
	// defined in machine/victor9kb.c
INPUT_PORTS_END



//******************************************************************************
//  DEVICE CONFIGURATION
//******************************************************************************

//-------------------------------------------------
//  MC6845
//-------------------------------------------------

#define DC_SECRET   0x1000
#define DC_UNDLN    0x2000
#define DC_LOWINT   0x4000
#define DC_RVS      0x8000

MC6845_UPDATE_ROW( victor9k_state::crtc_update_row )
{
	int hires = BIT(ma, 13);
	int dot_addr = BIT(ma, 12);
	int width = hires ? 16 : 10;

	address_space &program = m_maincpu->space(AS_PROGRAM);
	const rgb_t *palette = m_palette->palette()->entry_list_raw();

	int x = hbp;

	offs_t aa = (ma & 0x7ff) << 1;

	for (int sx = 0; sx < x_count; sx++)
	{
		uint16_t dc = (m_video_ram[aa + 1] << 8) | m_video_ram[aa];
		offs_t ab = (dot_addr << 15) | ((dc & 0x7ff) << 4) | (ra & 0x0f);
		uint16_t dd = program.read_word(ab << 1);

		int cursor = (sx == cursor_x) ? 1 : 0;
		int undln = !((dc & DC_UNDLN) && BIT(dd, 15)) ? 2 : 0;
		int rvs = (dc & DC_RVS) ? 4 : 0;
		int secret = (dc & DC_SECRET) ? 1 : 0;
		int lowint = (dc & DC_LOWINT) ? 1 : 0;

		for (int bit = 0; bit < width; bit++)
		{
			int pixel = 0;

			switch (rvs | undln | cursor)
			{
			case 0: case 5:
				pixel = 1;
				break;

			case 1: case 4:
				pixel = 0;
				break;

			case 2: case 7:
				pixel = !(!(BIT(dd, bit) && !secret));
				break;

			case 3: case 6:
				pixel = !(BIT(dd, bit) && !secret);
				break;
			}

			int color = 0;

			if (pixel && de)
			{
				int pen = 1 + m_brt;
				if (!lowint) pen = 9;
				color = palette[pen];
			}

			bitmap.pix(vbp + y, x++) = color;
		}

		aa += 2;
		aa &= 0xfff;
	}
}

MC6845_BEGIN_UPDATE( victor9k_state::crtc_begin_update )
{
	uint16_t ma = m_crtc->get_ma();
	int hires = BIT(ma, 13);
	int width = hires ? 16 : 10;
	if (hires != m_hires)
	{
		//LOGDISPLAY("mc6845 begin update change resolution: %s\n", hires ? "high" : "low");
		m_crtc->set_hpixels_per_column(width);
		m_crtc->set_char_width(width);
		//m_crtc->set_visarea_adjust(0, 0, 0, 10);  //show line 25
		m_hires = hires;
	}
}

void victor9k_state::vert_w(int state)
{
	m_via2->write_pa7(state);
	m_pic->ir7_w(state);
}

//-------------------------------------------------
//  PIC8259
//-------------------------------------------------

/*

    pin     signal      description

    IR0     SYN         sync detect
    IR1     COMM        serial communications (7201)
    IR2     TIMER       8253 timer
    IR3     PARALLEL    all 6522 IRQ (including disk)
    IR4     IR4         expansion IR4
    IR5     IR5         expansion IR5
    IR6     KBINT       keyboard data ready
    IR7     VINT        vertical sync or nonspecific interrupt

*/

//-------------------------------------------------
//  MC6852_INTERFACE( ssda_intf )
//-------------------------------------------------

void victor9k_state::ssda_irq_w(int state)
{
	m_ssda_irq = state;

	m_pic->ir3_w(m_ssda_irq || m_via1_irq || m_via3_irq || m_fdc_irq);
}


void victor9k_state::ssda_sm_dtr_w(int state)
{
	m_ssda->cts_w(state);
	m_ssda->dcd_w(!state);

	/*
	 * We're supposed to set the _ENC/DEC input of the HC55516 to !state,
	 * but only playback/decode is currently supported, and that input
	 * is not implemenented.
	 */
}


void victor9k_state::via1_pa_w(uint8_t data)
{
	/*

	    bit     description

	    PA0     DIO1
	    PA1     DIO2
	    PA2     DIO3
	    PA3     DIO4
	    PA4     DIO5
	    PA5     DIO6
	    PA6     DIO7
	    PA7     DIO8

	*/

	// centronics
	m_centronics->write_data0(BIT(data, 0));
	m_centronics->write_data1(BIT(data, 1));
	m_centronics->write_data2(BIT(data, 2));
	m_centronics->write_data3(BIT(data, 3));
	m_centronics->write_data4(BIT(data, 4));
	m_centronics->write_data5(BIT(data, 5));
	m_centronics->write_data6(BIT(data, 6));
	m_centronics->write_data7(BIT(data, 7));

	// IEEE-488
	m_ieee488->host_dio_w(data);
}

void victor9k_state::write_nfrd(int state)
{
	m_via1->write_pb6(state);
	m_via1->write_ca1(state);
}

void victor9k_state::write_ndac(int state)
{
	m_via1->write_pb7(state);
	m_via1->write_ca2(state);
}

void victor9k_state::via1_pb_w(uint8_t data)
{
	/*

	    bit     description

	    PB0     STROBE/DAV
	    PB1     PI/EOI
	    PB2     REN
	    PB3     ATN
	    PB4     IFC
	    PB5     SRQ/BUSY SRQ
	    PB6     NRFD/ACK RFD
	    PB7     SEL/DAC

	*/

	// centronics
	m_centronics->write_strobe(BIT(data, 0));

	// IEEE-488
	m_ieee488->host_dav_w(BIT(data, 0));
	m_ieee488->host_eoi_w(BIT(data, 1));
	m_ieee488->host_ren_w(BIT(data, 2));
	m_ieee488->host_atn_w(BIT(data, 3));
	m_ieee488->host_ifc_w(BIT(data, 4));
	m_ieee488->host_srq_w(BIT(data, 5));
	m_ieee488->host_nrfd_w(BIT(data, 6));
	m_ieee488->host_ndac_w(BIT(data, 7));
}

void victor9k_state::codec_vol_w(int state)
{
}

void victor9k_state::via1_irq_w(int state)
{
	m_via1_irq = state;

	m_pic->ir3_w(m_ssda_irq || m_via1_irq || m_via3_irq || m_fdc_irq);
}

void victor9k_state::via2_pa_w(uint8_t data)
{
	/*

	    bit     description

	    PA0     _INT/EXTA
	    PA1     _INT/EXTB
	    PA2
	    PA3
	    PA4
	    PA5
	    PA6
	    PA7

	*/
}

void victor9k_state::update_kback()
{
	int kback = !(!(m_kbrdy && !m_via2_irq) && !(m_kbackctl && m_via2_irq));

	m_kb->kback_w(kback);
}

void victor9k_state::via2_pb_w(uint8_t data)
{
	/*

	    bit     description

	    PB0     TALK/LISTEN
	    PB1     KBACKCTL
	    PB2     BRT0
	    PB3     BRT1
	    PB4     BRT2
	    PB5     CONT0
	    PB6     CONT1
	    PB7     CONT2

	*/

	// keyboard acknowledge
	m_kbackctl = BIT(data, 1);
	update_kback();

	// brightness
	m_brt = (data >> 2) & 0x07;

	// contrast
	m_cont = data >> 5;

	LOGDISPLAY("BRT %u CONT %u\n", m_brt, m_cont);
}

void victor9k_state::via2_irq_w(int state)
{
	m_via2_irq = state;

	m_pic->ir6_w(m_via2_irq);
	update_kback();
}


/*
    bit    description

    PA0    J5-16
    PA1    J5-18
    PA2    J5-20
    PA3    J5-22
    PA4    J5-24
    PA5    J5-26
    PA6    J5-28
    PA7    J5-30
    PB0    J5-32
    PB1    J5-34
    PB2    J5-36
    PB3    J5-38
    PB4    J5-40
    PB5    J5-42
    PB6    J5-44
    PB7    J5-46
    CA1    J5-12
    CB1    J5-48
    CA2    J5-14
    CB2    J5-50
*/

void victor9k_state::via3_pb_w(uint8_t data)
{
	// codec clock output
	m_ssda->rx_clk_w(!BIT(data, 7));
	m_ssda->tx_clk_w(!BIT(data, 7));
	m_cvsd->clock_w(!BIT(data, 7));
}

void victor9k_state::via3_irq_w(int state)
{
	m_via3_irq = state;

	m_pic->ir3_w(m_ssda_irq || m_via1_irq || m_via3_irq || m_fdc_irq);
}


//-------------------------------------------------
//  VICTOR9K_KEYBOARD_INTERFACE( kb_intf )
//-------------------------------------------------

void victor9k_state::kbrdy_w(int state)
{
	LOGKEYBOARD("KBRDY %u\n", state);

	m_via2->write_cb1(state);

	m_kbrdy = state;
	update_kback();
}

void victor9k_state::kbdata_w(int state)
{
	LOGKEYBOARD("KBDATA %u\n", state);

	m_via2->write_cb2(state);
	m_via2->write_pa6(state);
}


void victor9k_state::fdc_irq_w(int state)
{
	m_fdc_irq = state;

	m_pic->ir3_w(m_ssda_irq || m_via1_irq || m_via3_irq || m_fdc_irq);
}

uint8_t victor9k_state::hd_dma_r(offs_t offset)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	return program.read_byte(offset);
}

void victor9k_state::hd_dma_w(offs_t offset, uint8_t data)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.write_byte(offset, data);
}

//******************************************************************************
//  MACHINE INITIALIZATION
//******************************************************************************

void victor9k_state::victor9k_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(0x00, 0x00, 0x00));

	// BRT0 82K
	// BRT1 39K
	// BRT2 20K
	// 12V 220K pullup
	palette.set_pen_color(1, rgb_t(0x00, 0x10, 0x04));
	palette.set_pen_color(2, rgb_t(0x00, 0x20, 0x09));
	palette.set_pen_color(3, rgb_t(0x00, 0x40, 0x11));
	palette.set_pen_color(4, rgb_t(0x00, 0x60, 0x1a));
	palette.set_pen_color(5, rgb_t(0x00, 0x80, 0x23));
	palette.set_pen_color(6, rgb_t(0x00, 0xa0, 0x2c));
	palette.set_pen_color(7, rgb_t(0x00, 0xc0, 0x34));
	palette.set_pen_color(8, rgb_t(0x00, 0xff, 0x45));

	// CONT0 620R
	// CONT1 332R
	// CONT2 162R
	// 12V 110R pullup
	palette.set_pen_color(9, rgb_t(0x00, 0xff, 0x45));
}

void victor9k_state::machine_start()
{
	// state saving
	save_item(NAME(m_brt));
	save_item(NAME(m_cont));
	save_item(NAME(m_via1_irq));
	save_item(NAME(m_via2_irq));
	save_item(NAME(m_via3_irq));
	save_item(NAME(m_fdc_irq));
	save_item(NAME(m_ssda_irq));
	save_item(NAME(m_kbrdy));
	save_item(NAME(m_kbackctl));

#ifndef USE_SCP
	// patch out SCP self test
	LOGCONF("patch out SCP self test");
	m_rom->base()[0x11ab] = 0xc3;

	// patch out ROM checksum error
	m_rom->base()[0x1d51] = 0x90;
	m_rom->base()[0x1d52] = 0x90;
	m_rom->base()[0x1d53] = 0x90;
	m_rom->base()[0x1d54] = 0x90;
#endif

	//update RAM for ramsize
	int m_ram_size = m_ram->size();
	u8 *m_ram_ptr = m_ram->pointer();

	int ramsize = m_ram_size;
	if (ramsize > 0)
	{
		address_space& space = m_maincpu->space(AS_PROGRAM);
		if (ramsize > 0xdffff)   //the 896KB option overlaps 1 bit with
			ramsize = 0xdffff;   //the I/O memory space, truncating
		LOGCONF("install_ram ramsize %x\n", ramsize);
		space.install_ram(0x0, ramsize, m_ram_ptr);
	}
}

void victor9k_state::machine_reset()
{
	m_maincpu->reset();
	m_upd7201->reset();
	m_ssda->reset();
	m_via1->reset();
	m_via2->reset();
	m_via3->reset();
	m_crtc->reset();
	m_fdc->reset();
}



//******************************************************************************
//  MACHINE CONFIGURATION
//******************************************************************************

//-------------------------------------------------
//  machine_config( victor9k )
//-------------------------------------------------

void victor9k_state::scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_S1410);
}

void victor9k_state::victor9k(machine_config &config)
{
	// basic machine hardware
	I8088(config, m_maincpu, 15_MHz_XTAL / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &victor9k_state::victor9k_mem);
	m_maincpu->set_irq_acknowledge_callback(I8259A_TAG, FUNC(pic8259_device::inta_cb));

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_color(rgb_t::green());
	m_screen->set_refresh_hz(72);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(1200));
	m_screen->set_size(800, 400);
	m_screen->set_visarea(0, 799, 0, 399);
	m_screen->set_screen_update(HD46505S_TAG, FUNC(hd6845s_device::screen_update));

	PALETTE(config, m_palette, FUNC(victor9k_state::victor9k_palette), 16);
	HD6845S(config, m_crtc, 15_MHz_XTAL / 10); // HD6845 == HD46505S
	m_crtc->set_screen(SCREEN_TAG);
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(10);
	m_crtc->set_visarea_adjust(0, 0, 0, 10);  //show line 25

	m_crtc->set_update_row_callback(FUNC(victor9k_state::crtc_update_row));
	m_crtc->out_vsync_callback().set(FUNC(victor9k_state::vert_w));
	m_crtc->set_begin_update_callback(FUNC(victor9k_state::crtc_begin_update));

	// sound hardware
	FILTER_BIQUAD(config, m_cvsd_filter2).opamp_mfb_lowpass_setup(RES_K(27), RES_K(15), RES_K(27), CAP_P(4700), CAP_P(1200));
	FILTER_BIQUAD(config, m_cvsd_filter).opamp_mfb_lowpass_setup(RES_K(43), RES_K(36), RES_K(180), CAP_P(1800), CAP_P(180));
	HC55516(config, m_cvsd, 0).add_route(ALL_OUTPUTS, m_cvsd_filter, 1.0);
	m_cvsd_filter->add_route(ALL_OUTPUTS, m_cvsd_filter2, 1.0);
	m_cvsd_filter2->add_route(ALL_OUTPUTS, "mono", 0.25);
	SPEAKER(config, "mono").front_center();

	// devices
	IEEE488(config, m_ieee488, 0);

	m_ieee488->dav_callback().set(M6522_1_TAG, FUNC(via6522_device::write_pb0));
	m_ieee488->eoi_callback().set(M6522_1_TAG, FUNC(via6522_device::write_pb1));
	m_ieee488->ren_callback().set(M6522_1_TAG, FUNC(via6522_device::write_pb2));
	m_ieee488->atn_callback().set(M6522_1_TAG, FUNC(via6522_device::write_pb3));
	m_ieee488->ifc_callback().set(M6522_1_TAG, FUNC(via6522_device::write_pb4));
	m_ieee488->srq_callback().set(M6522_1_TAG, FUNC(via6522_device::write_pb5));
	m_ieee488->nrfd_callback().set(FUNC(victor9k_state::write_nfrd));
	m_ieee488->ndac_callback().set(FUNC(victor9k_state::write_ndac));

	PIC8259(config, m_pic, 0);
	m_pic->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	PIT8253(config, m_pit, 0);
	m_pit->set_clk<0>(15_MHz_XTAL / 12);
	m_pit->out_handler<0>().set(m_upd7201, FUNC(upd7201_device::rxca_w));
	m_pit->out_handler<0>().append(m_upd7201, FUNC(upd7201_device::txca_w));
	m_pit->set_clk<1>(15_MHz_XTAL / 12);
	m_pit->out_handler<1>().set(m_upd7201, FUNC(upd7201_device::rxcb_w));
	m_pit->out_handler<1>().append(m_upd7201, FUNC(upd7201_device::txcb_w));
	m_pit->set_clk<2>(125000);
	m_pit->out_handler<2>().set(I8259A_TAG, FUNC(pic8259_device::ir2_w));

	UPD7201(config, m_upd7201, 15_MHz_XTAL / 6);
	m_upd7201->out_txda_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_txd));
	m_upd7201->out_dtra_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_dtr));
	m_upd7201->out_rtsa_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_rts));
	m_upd7201->out_txdb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_txd));
	m_upd7201->out_dtrb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_dtr));
	m_upd7201->out_rtsb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_rts));
	m_upd7201->out_int_callback().set(I8259A_TAG, FUNC(pic8259_device::ir1_w));

	MC6852(config, m_ssda, 15_MHz_XTAL / 15);
	m_ssda->tx_data_callback().set(HC55516_TAG, FUNC(hc55516_device::digit_w));
	m_ssda->sm_dtr_callback().set(FUNC(victor9k_state::ssda_sm_dtr_w));
	m_ssda->irq_callback().set(FUNC(victor9k_state::ssda_irq_w));

	MOS6522(config, m_via1, 15_MHz_XTAL / 15);
	m_via1->readpa_handler().set(IEEE488_TAG, FUNC(ieee488_device::dio_r));
	m_via1->writepa_handler().set(FUNC(victor9k_state::via1_pa_w));
	m_via1->writepb_handler().set(FUNC(victor9k_state::via1_pb_w));
	m_via1->cb2_handler().set(FUNC(victor9k_state::codec_vol_w));
	m_via1->irq_handler().set(FUNC(victor9k_state::via1_irq_w));

	MOS6522(config, m_via2, 15_MHz_XTAL / 15);
	m_via2->writepa_handler().set(FUNC(victor9k_state::via2_pa_w));
	m_via2->writepb_handler().set(FUNC(victor9k_state::via2_pb_w));
	m_via2->irq_handler().set(FUNC(victor9k_state::via2_irq_w));

	MOS6522(config, m_via3, 15_MHz_XTAL / 15);
	m_via3->writepb_handler().set(FUNC(victor9k_state::via3_pb_w));
	m_via3->irq_handler().set(FUNC(victor9k_state::via3_irq_w));

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->busy_handler().set(M6522_1_TAG, FUNC(via6522_device::write_pb5));
	m_centronics->ack_handler().set(M6522_1_TAG, FUNC(via6522_device::write_pb6));
	m_centronics->select_handler().set(M6522_1_TAG, FUNC(via6522_device::write_pb7));

	RS232_PORT(config, m_rs232a, default_rs232_devices, nullptr);
	m_rs232a->rxd_handler().set(m_upd7201, FUNC(upd7201_device::rxa_w));
	m_rs232a->dcd_handler().set(m_upd7201, FUNC(upd7201_device::dcda_w));
	m_rs232a->ri_handler().set(m_via2, FUNC(via6522_device::write_pa2));
	m_rs232a->cts_handler().set(m_upd7201, FUNC(upd7201_device::ctsa_w));
	m_rs232a->dsr_handler().set(m_via2, FUNC(via6522_device::write_pa3));

	RS232_PORT(config, m_rs232b, default_rs232_devices, nullptr);
	m_rs232b->rxd_handler().set(m_upd7201, FUNC(upd7201_device::rxb_w));
	m_rs232b->dcd_handler().set(m_upd7201, FUNC(upd7201_device::dcdb_w));
	m_rs232b->ri_handler().set(m_via2, FUNC(via6522_device::write_pa4));
	m_rs232b->cts_handler().set(m_upd7201, FUNC(upd7201_device::ctsb_w));
	m_rs232b->dsr_handler().set(m_via2, FUNC(via6522_device::write_pa5));

	VICTOR9K_KEYBOARD(config, m_kb, 0);
	m_kb->kbrdy_handler().set(FUNC(victor9k_state::kbrdy_w));
	m_kb->kbdata_handler().set(FUNC(victor9k_state::kbdata_w));

	VICTOR_9000_FDC(config, m_fdc, 0);
	m_fdc->irq_wr_callback().set(FUNC(victor9k_state::fdc_irq_w));
	m_fdc->syn_wr_callback().set(I8259A_TAG, FUNC(pic8259_device::ir0_w)).invert();
	m_fdc->lbrdy_wr_callback().set_inputline(I8088_TAG, INPUT_LINE_TEST).invert();

	NSCSI_BUS(config, m_scsibus);
	NSCSI_CONNECTOR(config, "scsi:0", scsi_devices, "harddisk", false);
	NSCSI_CONNECTOR(config, "scsi:1", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:7").option_set("v9kdmaib", VICTOR_9000_HDC).machine_config(
		[this](device_t *device)
		{
			victor_9000_hdc_device &victor9k_hdc(downcast<victor_9000_hdc_device &>(*device));

			device->set_clock(15_MHz_XTAL / 3);
			victor9k_hdc.irq_handler().append(m_pic, FUNC(pic8259_device::ir4_w));
			victor9k_hdc.dma_read().set(*this, FUNC(victor9k_state::hd_dma_r));
			victor9k_hdc.dma_write().set(*this, FUNC(victor9k_state::hd_dma_w));
		});

	RAM(config, m_ram).set_default_size("128K").set_extra_options("128K,256K,512K,640K,768K,896K");

	SOFTWARE_LIST(config, "flop_list").set_original("victor9k_flop");
}



//******************************************************************************
//  ROMS
//******************************************************************************

//-------------------------------------------------
//  ROM( victor9k )
//-------------------------------------------------

ROM_START( victor9k )
	ROM_REGION( 0x2000, I8088_TAG, 0 )
	ROM_DEFAULT_BIOS( "univ" )
	ROM_SYSTEM_BIOS( 0, "old", "Older" )
	ROMX_LOAD( "102320.7j", 0x0000, 0x1000, CRC(3d615fd7) SHA1(b22f7e5d66404185395d8effbf57efded0079a92), ROM_BIOS(0) )
	ROMX_LOAD( "102322.8j", 0x1000, 0x1000, CRC(9209df0e) SHA1(3ee8e0c15186bbd5768b550ecc1fa3b6b1dbb928), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "univ", "Universal" )
	ROMX_LOAD( "v9000 univ. fe f3f7 13db.7j", 0x0000, 0x1000, CRC(25c7a59f) SHA1(8784e9aa7eb9439f81e18b8e223c94714e033911), ROM_BIOS(1) )
	ROMX_LOAD( "v9000 univ. ff f3f7 39fe.8j", 0x1000, 0x1000, CRC(496c7467) SHA1(eccf428f62ef94ab85f4a43ba59ae6a066244a66), ROM_BIOS(1) )
ROM_END

} // anonymous namespace


//******************************************************************************
//  SYSTEM DRIVERS
//******************************************************************************

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY                     FULLNAME       FLAGS
COMP( 1982, victor9k, 0,      0,      victor9k, victor9k, victor9k_state, empty_init, "Victor Business Products", "Victor 9000", MACHINE_IMPERFECT_COLORS )



vidbrain.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/***************************************************************************

    VideoBrain FamilyComputer

    http://www.atariprotos.com/othersystems/videobrain/videobrain.htm
    http://www.seanriddle.com/vbinfo.html
    http://www.seanriddle.com/videobrain.html
    http://www.google.com/patents/US4232374
    http://www.google.com/patents/US4177462
    http://www.orphanedgames.com/videobrain/
    http://www.datalytixllc.com/videobrain/
    http://blog.kevtris.org/blogfiles/videobrain/videobrain_unwrapped.txt

****************************************************************************/

/*

TODO:
    - wait states (UV201: 2.9us, memory except RES1: 1.65us)
    - interlaced video?
    - pinball background colors
    - Y-zoom starting on odd scanline only 1 line high
    - object height 0 glitch
    - object column 0xff glitch
    - video interrupts
    - R-2R ladder DAC
    - reset on cartridge unload
    - joystick scan timer 555
    - expander 1 (F3870 CPU, cassette, RS-232)
    - expander 2 (modem)

Keyboard:
    - When typing, (A .) key is often misinterpreted as (O #).
    - Shift is a toggle. This prevents Natural Keyboard & Paste from being
      able to shift as needed. The shift state shows as a block on the intro
      screen.

Using the system:
    - At startup, the intro screen gives the choices TEXT, COLOR, CLOCK, ALARM.
    - Press F1 for TEXT - this allows you to test the keyboard, but note that
      the Space key simply advances the cursor - it doesn't erase.
    - Press F2 for COLOR - this shows colour bars, presumably as a hardware test.
      In emulation, the colours are offset to the right and wrap around a bit.
    - Press F3 for CLOCK
    - Press F4 for ALARM

*/

#include "emu.h"

#include "bus/vidbrain/exp.h"
#include "cpu/f8/f8.h"
#include "machine/f3853.h"
#include "machine/ram.h"
#include "machine/rescap.h"
#include "sound/dac.h"

#include "softlist_dev.h"
#include "speaker.h"
#include "uv201.h"

//#define VERBOSE (LOG_GENERAL)
#include "logmacro.h"

namespace {

class vidbrain_state : public driver_device
{
public:
	vidbrain_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_smi(*this, "smi"),
		m_uv(*this, "uv"),
		m_dac(*this, "dac"),
		m_exp(*this, "exp"),
		m_io(*this, "IO%02u", 0),
		m_uv201_31(*this, "UV201-31"),
		m_joy_r(*this, "JOY-R"),
		m_joy1_x(*this, "JOY1-X"),
		m_joy1_y(*this, "JOY1-Y"),
		m_joy2_x(*this, "JOY2-X"),
		m_joy2_y(*this, "JOY2-Y"),
		m_joy3_x(*this, "JOY3-X"),
		m_joy3_y(*this, "JOY3-Y"),
		m_joy4_x(*this, "JOY4-X"),
		m_joy4_y(*this, "JOY4-Y")
	{ }

	void vidbrain(machine_config &config);
	void vidbrain_video(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER( trigger_reset );

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void vidbrain_mem(address_map &map) ATTR_COLD;
	void vidbrain_io(address_map &map) ATTR_COLD;

	TIMER_CALLBACK_MEMBER(joystick_tick);

	void keyboard_w(uint8_t data);
	uint8_t keyboard_r();
	void sound_w(uint8_t data);

	void hblank_w(int state);
	uint8_t memory_read_byte(offs_t offset);

	required_device<cpu_device> m_maincpu;
	required_device<f3853_device> m_smi;
	required_device<uv201_device> m_uv;
	required_device<dac_byte_interface> m_dac;
	required_device<videobrain_expansion_slot_device> m_exp;
	required_ioport_array<8> m_io;
	required_ioport m_uv201_31;
	required_ioport m_joy_r;
	required_ioport m_joy1_x;
	required_ioport m_joy1_y;
	required_ioport m_joy2_x;
	required_ioport m_joy2_y;
	required_ioport m_joy3_x;
	required_ioport m_joy3_y;
	required_ioport m_joy4_x;
	required_ioport m_joy4_y;

	// keyboard state
	uint8_t m_keylatch = 0;
	bool m_joy_enable = false;

	// sound state
	int m_sound_clk = 0;

	// timers
	emu_timer *m_timer_ne555 = nullptr;
};



//**************************************************************************
//  READ/WRITE HANDLERS
//**************************************************************************

//-------------------------------------------------
//  keyboard_w - keyboard column write
//-------------------------------------------------

void vidbrain_state::keyboard_w(uint8_t data)
{
	/*

	    bit     description

	    0       keyboard column 0, sound data 0
	    1       keyboard column 1, sound data 1
	    2       keyboard column 2
	    3       keyboard column 3
	    4       keyboard column 4
	    5       keyboard column 5
	    6       keyboard column 6
	    7       keyboard column 7

	*/

	LOG("Keyboard %02x\n", data);

	m_keylatch = data;
}


//-------------------------------------------------
//  keyboard_r - keyboard row read
//-------------------------------------------------

uint8_t vidbrain_state::keyboard_r()
{
	/*

	    bit     description

	    0       keyboard row 0, joystick 1 fire
	    1       keyboard row 1, joystick 2 fire
	    2       keyboard row 2, joystick 3 fire
	    3       keyboard row 3, joystick 4 fire
	    4
	    5
	    6
	    7

	*/

	uint8_t data = m_joy_r->read();

	for (int i = 0; i < 8; i++)
	{
		if (BIT(m_keylatch, i)) data |= m_io[i]->read();
	}

	if (!m_uv->kbd_r()) data |= m_uv201_31->read();

	return data;
}


//-------------------------------------------------
//  sound_w - sound clock write
//-------------------------------------------------

void vidbrain_state::sound_w(uint8_t data)
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4       sound clock
	    5       accessory jack pin 5
	    6       accessory jack pin 1
	    7       joystick enable

	*/

	LOG("Sound %02x\n", data);

	// sound clock
	int sound_clk = BIT(data, 4);

	if (!m_sound_clk && sound_clk)
	{
		m_dac->write(m_keylatch & 3);
	}

	m_sound_clk = sound_clk;

	// joystick enable
	m_joy_enable = !BIT(data, 7);
}



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( vidbrain_mem )
//-------------------------------------------------

void vidbrain_state::vidbrain_mem(address_map &map)
{
	map.global_mask(0x3fff);
	map(0x0000, 0x07ff).rom().region("res1", 0);
	map(0x0800, 0x08ff).mirror(0x2300).rw(m_uv, FUNC(uv201_device::read), FUNC(uv201_device::write));
	map(0x0c00, 0x0fff).mirror(0x2000).ram();
	map(0x1000, 0x17ff).rw(m_exp, FUNC(videobrain_expansion_slot_device::cs1_r), FUNC(videobrain_expansion_slot_device::cs1_w));
	map(0x1800, 0x1fff).rw(m_exp, FUNC(videobrain_expansion_slot_device::cs2_r), FUNC(videobrain_expansion_slot_device::cs2_w));
	map(0x2000, 0x27ff).rom().region("res2", 0);
	map(0x3000, 0x3fff).rw(m_exp, FUNC(videobrain_expansion_slot_device::unmap_r), FUNC(videobrain_expansion_slot_device::unmap_w));
}


//-------------------------------------------------
//  ADDRESS_MAP( vidbrain_io )
//-------------------------------------------------

void vidbrain_state::vidbrain_io(address_map &map)
{
	map(0x00, 0x00).w(FUNC(vidbrain_state::keyboard_w));
	map(0x01, 0x01).rw(FUNC(vidbrain_state::keyboard_r), FUNC(vidbrain_state::sound_w));
	map(0x0c, 0x0f).rw(m_smi, FUNC(f3853_device::read), FUNC(f3853_device::write));
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_CHANGED_MEMBER( trigger_reset )
//-------------------------------------------------

INPUT_CHANGED_MEMBER( vidbrain_state::trigger_reset )
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}


//-------------------------------------------------
//  INPUT_PORTS( vidbrain )
//-------------------------------------------------

static INPUT_PORTS_START( vidbrain )
	PORT_START("IO00")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('"')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('#')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(u8"P ¢") PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR(0x00a2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(u8"; π") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(0x03c0)

	PORT_START("IO01")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('!')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR(')')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('$')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR('\'') PORT_CHAR('*')

	PORT_START("IO02")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(u8"Y ÷") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR(0x00f7)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('(')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR(':')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)

	PORT_START("IO03")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(u8"T ×") PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR(0x00d7)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('/')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR(',')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("ERASE RESTART") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)

	PORT_START("IO04")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('9')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('-')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('=')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SPACE RUN/STOP") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')

	PORT_START("IO05")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('8')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('6')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('+')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SPECIAL ALARM") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))

	PORT_START("IO06")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('7')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('5')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('3')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("NEXT CLOCK") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))

	PORT_START("IO07")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('%')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('4')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('2')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PREVIOUS COLOR") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))

	PORT_START("UV201-31")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('.')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('1')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('?') PORT_CHAR('0')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("BACK TEXT") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))

	PORT_START("RESET")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("MASTER CONTROL") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vidbrain_state::trigger_reset), 0)

	PORT_START("JOY1-X")
	PORT_BIT( 0xff, 50, IPT_AD_STICK_X ) PORT_MINMAX(0, 99) PORT_SENSITIVITY(25) PORT_PLAYER(1)

	PORT_START("JOY1-Y")
	PORT_BIT( 0xff, 50, IPT_AD_STICK_Y ) PORT_MINMAX(0, 99) PORT_SENSITIVITY(25) PORT_PLAYER(1)

	PORT_START("JOY2-X")
	PORT_BIT( 0xff, 50, IPT_AD_STICK_X ) PORT_MINMAX(0, 99) PORT_SENSITIVITY(25) PORT_PLAYER(2)

	PORT_START("JOY2-Y")
	PORT_BIT( 0xff, 50, IPT_AD_STICK_Y ) PORT_MINMAX(0, 99) PORT_SENSITIVITY(25) PORT_PLAYER(2)

	PORT_START("JOY3-X")
	PORT_BIT( 0xff, 50, IPT_AD_STICK_X ) PORT_MINMAX(0, 99) PORT_SENSITIVITY(25) PORT_PLAYER(3)

	PORT_START("JOY3-Y")
	PORT_BIT( 0xff, 50, IPT_AD_STICK_Y ) PORT_MINMAX(0, 99) PORT_SENSITIVITY(25) PORT_PLAYER(3)

	PORT_START("JOY4-X")
	PORT_BIT( 0xff, 50, IPT_AD_STICK_X ) PORT_MINMAX(0, 99) PORT_SENSITIVITY(25) PORT_PLAYER(4)

	PORT_START("JOY4-Y")
	PORT_BIT( 0xff, 70, IPT_AD_STICK_Y ) PORT_MINMAX(0, 139) PORT_SENSITIVITY(25) PORT_PLAYER(4)

	PORT_START("JOY-R")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(3)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(4)
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

void vidbrain_state::hblank_w(int state)
{
	if (state && m_joy_enable)
	{
		uint8_t joydata = 0;

		if (BIT(m_keylatch, 0)) joydata |= m_joy1_x->read();
		if (BIT(m_keylatch, 1)) joydata |= m_joy1_y->read();
		if (BIT(m_keylatch, 2)) joydata |= m_joy2_x->read();
		if (BIT(m_keylatch, 3)) joydata |= m_joy2_y->read();
		if (BIT(m_keylatch, 4)) joydata |= m_joy3_x->read();
		if (BIT(m_keylatch, 5)) joydata |= m_joy3_y->read();
		if (BIT(m_keylatch, 6)) joydata |= m_joy4_x->read();
		if (BIT(m_keylatch, 7)) joydata |= m_joy4_y->read();

		// NE555 in monostable mode
		// R = 3K9 + 100K linear pot
		// C = 0.003uF
		// t = 1.1 * R * C
		double t = 1.1 * (RES_K(3.9) + RES_K(joydata)) * 3;

		m_timer_ne555->adjust(attotime::from_nsec(t));
	}
}

uint8_t vidbrain_state::memory_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	return prog_space.read_byte(offset);
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  timer events
//-------------------------------------------------

TIMER_CALLBACK_MEMBER(vidbrain_state::joystick_tick)
{
	m_uv->ext_int_w(0);
}


//-------------------------------------------------
//  MACHINE_START( vidbrain )
//-------------------------------------------------

void vidbrain_state::machine_start()
{
	// allocate timers
	m_timer_ne555 = timer_alloc(FUNC(vidbrain_state::joystick_tick), this);

	// register for state saving
	save_item(NAME(m_keylatch));
	save_item(NAME(m_joy_enable));
	save_item(NAME(m_sound_clk));
}


void vidbrain_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  machine_config( vidbrain )
//-------------------------------------------------

void vidbrain_state::vidbrain(machine_config &config)
{
	// basic machine hardware
	F8(config, m_maincpu, XTAL(4'000'000)/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &vidbrain_state::vidbrain_mem);
	m_maincpu->set_addrmap(AS_IO, &vidbrain_state::vidbrain_io);
	m_maincpu->set_irq_acknowledge_callback(m_smi, FUNC(f3853_device::int_acknowledge));

	// video hardware
	UV201(config, m_uv, 3636363);
	m_uv->set_screen("screen");
	m_uv->ext_int_wr_callback().set(m_smi, FUNC(f3853_device::ext_int_w));
	m_uv->hblank_wr_callback().set(FUNC(vidbrain_state::hblank_w));
	m_uv->db_rd_callback().set(FUNC(vidbrain_state::memory_read_byte));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_screen_update(m_uv, FUNC(uv201_device::screen_update));
	screen.set_raw(3636363, 232, 18, 232, 262, 21, 262);
	screen.set_physical_aspect(3, 2);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_2BIT_R2R(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.25); // 74ls74.u16 + 120k + 56k

	// devices
	F3853(config, m_smi, XTAL(4'000'000)/2);
	m_smi->int_req_callback().set_inputline(m_maincpu, F8_INPUT_LINE_INT_REQ);

	// cartridge
	VIDEOBRAIN_EXPANSION_SLOT(config, m_exp, vidbrain_expansion_cards, nullptr);

	// software lists
	SOFTWARE_LIST(config, "cart_list").set_original("vidbrain");

	// internal ram
	RAM(config, RAM_TAG).set_default_size("1K");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( vidbrain )
//-------------------------------------------------

ROM_START( vidbrain )
	ROM_REGION( 0x800, "res1", 0 )
	ROM_LOAD( "uvres_1n.d67", 0x000, 0x800, CRC(065fe7c2) SHA1(9776f9b18cd4d7142e58eff45ac5ee4bc1fa5a2a) )

	ROM_REGION( 0x800, "res2", 0 )
	ROM_LOAD( "resn2.e5", 0x000, 0x800, CRC(1d85d7be) SHA1(26c5a25d1289dedf107fa43aa8dfc14692fd9ee6) )

	ROM_REGION( 0x800, "exp1", 0 )
	ROM_LOAD( "expander1.bin", 0x0000, 0x0800, CRC(dac31abc) SHA1(e1ac7a9d654c2a70979effc744d98f21d13b4e05) )
ROM_END

} // anonymous namespace



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY                        FULLNAME                     FLAGS
COMP( 1978, vidbrain, 0,      0,      vidbrain, vidbrain, vidbrain_state, empty_init, "VideoBrain Computer Company", "VideoBrain FamilyComputer", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )



videoart.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

LJN VideoArt

It's a system for drawing/coloring pictures on the tv, not a video game console.
The cartridge ROMs contain no executable code, only data for a title screen and
vector pictures.

On the title screen, press CLEAR to start drawing (no need to wait half a minute).
Drawing with the same color as the picture outline is not allowed. To change the
background color, choose one from the color slider and press CLEAR.

Hardware notes:
- Thomson EF6805R2P @ 3.57Mhz (14.318MHz XTAL)
- Thomson EF9367P @ 1.507MHz, 128*208 resolution (512*208 internally), 16 colors
- TSGB01019ACP 48-pin DIP gate array (die label: MOSTEK (C) 1984, MK GB 1000 HAA),
  interfaces with EF9367P and DRAM
- 2*D41416C-15 (16Kbit*4) DRAM
- 36-pin cartridge slot, 8KB or 16KB ROM (can also boot without cartridge)
- DB9 joystick port, no known peripherals other than the default analog joystick
- RF NTSC video, no sound

LJN also patented a video art system under US4782335, but the hardware doesn't
match the final product. It has no CPU, and no cartridge slot.

TODO:
- palette is approximated from photos/videos, there is no color prom

*******************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/m6805/m68705.h"
#include "machine/timer.h"
#include "video/ef9365.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"

#include "videoart.lh"


namespace {

class videoart_state : public driver_device
{
public:
	videoart_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ef9367(*this, "ef9367"),
		m_vram(*this, "vram", 0x8000, ENDIANNESS_LITTLE),
		m_screen(*this, "screen"),
		m_cart(*this, "cartslot"),
		m_inputs(*this, "IN%u", 0),
		m_led(*this, "led")
	{ }

	void videoart(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<m6805r2_device> m_maincpu;
	required_device<ef9365_device> m_ef9367;
	memory_share_creator<u8> m_vram;
	required_device<screen_device> m_screen;
	required_device<generic_slot_device> m_cart;
	required_ioport_array<4> m_inputs;
	output_finder<> m_led;

	u8 m_porta = 0xff;
	u8 m_portb = 0xff;
	u8 m_portc = 0xff;
	u8 m_efdata = 0xff;
	u8 m_romlatch = 0;
	u8 m_ccount = 0;
	u8 m_command = 0;
	u8 m_pixel_offset = 0;
	u8 m_vramdata = 0;

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	TIMER_DEVICE_CALLBACK_MEMBER(scanline) { m_ef9367->update_scanline(param); }
	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	void palette(palette_device &palette) const;

	void vram_map(address_map &map) ATTR_COLD;
	void vram_w(offs_t offset, u8 data);
	u8 vram_r(offs_t offset);
	void msl_w(u8 data) { m_pixel_offset = data & 7; }

	void porta_w(u8 data);
	u8 porta_r();
	void portb_w(u8 data);
	void portc_w(u8 data);
	u8 portd_r();
};



/*******************************************************************************
    Initialization
*******************************************************************************/

void videoart_state::machine_start()
{
	m_led.resolve();

	// register for savestates
	save_item(NAME(m_porta));
	save_item(NAME(m_portb));
	save_item(NAME(m_portc));
	save_item(NAME(m_efdata));
	save_item(NAME(m_romlatch));
	save_item(NAME(m_ccount));
	save_item(NAME(m_command));
	save_item(NAME(m_pixel_offset));
	save_item(NAME(m_vramdata));
}

DEVICE_IMAGE_LOAD_MEMBER(videoart_state::cart_load)
{
	u32 size = m_cart->common_get_size("rom");
	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}



/*******************************************************************************
    Video
*******************************************************************************/

// note: if palette gets tweaked, don't forget to update internal artwork too
constexpr rgb_t videoart_colors[] =
{
	{ 0x00, 0x00, 0x00 }, // 2 black
	{ 0x40, 0x30, 0xbc }, // 7 blue
	{ 0x14, 0x68, 0x14 }, // 3 dark green
	{ 0xff, 0xff, 0xff }, // 0 white

	{ 0x78, 0x20, 0x38 }, // b dark red
	{ 0x70, 0x20, 0x78 }, // 8 purple
	{ 0x3c, 0x50, 0x00 }, // a moss green
	{ 0x58, 0x34, 0x00 }, // d brown

	{ 0x80, 0x80, 0x80 }, // 1 gray
	{ 0x98, 0xe8, 0xff }, // 6 cyan
	{ 0xb4, 0xff, 0x38 }, // 4 lime green
	{ 0x84, 0xff, 0x68 }, // 5 green

	{ 0xff, 0x90, 0xff }, // c pink
	{ 0xe0, 0xa8, 0xff }, // 9 lilac
	{ 0xff, 0xc4, 0x40 }, // f orange
	{ 0xff, 0xa0, 0x80 }  // e light red
};

void videoart_state::palette(palette_device &palette) const
{
	palette.set_pen_colors(0, videoart_colors);
}

u32 videoart_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	if (m_command & 2)
	{
		// display gets blanked after a couple of minutes idle
		bitmap.fill(0, cliprect);
	}
	else
	{
		// width of 512 compressed down to 128
		for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
			for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
				bitmap.pix(y, x) = m_vram[(y << 7 | x >> 2) & 0x7fff] & 0xf;
	}

	return 0;
}

void videoart_state::vram_w(offs_t offset, u8 data)
{
	// correct offset (by default, ef9365_device wants to write per byte)
	data = BIT(data, ~m_pixel_offset & 7);
	offset = (offset << 1 | BIT(m_pixel_offset, 2)) & 0x7fff;

	if (data)
		m_vram[offset] = m_command >> 4;
	else
		m_vram[offset] ^= 0xf;
}

u8 videoart_state::vram_r(offs_t offset)
{
	// correct offset (by default, ef9365_device wants to read per byte)
	int pixel_offset = 0;
	m_ef9367->get_last_readback_word(0, &pixel_offset);
	offset = (offset << 1 | BIT(pixel_offset, 2)) & 0x7fff;

	if (!machine().side_effects_disabled())
		m_vramdata = m_vram[offset];

	return 0;
}

void videoart_state::vram_map(address_map &map)
{
	map(0x0000, 0x3fff).rw(FUNC(videoart_state::vram_r), FUNC(videoart_state::vram_w));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void videoart_state::porta_w(u8 data)
{
	// A0-A7: EF9367 data
	// A0,A1: custom chip command data
	m_porta = data;
}

u8 videoart_state::porta_r()
{
	u8 data = 0xff;

	// read EF9367 data
	if (~m_portb & 1)
		data &= m_efdata;

	// read vram data
	if (~m_portb & 4 && m_command & 1)
		data &= m_vramdata >> (m_ccount & 2) | 0xfc;

	// read cartridge data
	if (~m_portb & 0x10)
	{
		u16 offset = m_romlatch << 8 | m_portc;
		data &= m_cart->read_rom(offset);
	}

	return data;
}

void videoart_state::portb_w(u8 data)
{
	// B0: EF9367 E
	if (~data & m_portb & 1)
	{
		if (m_portc & 0x10)
			m_efdata = m_ef9367->data_r(m_portc & 0xf);
		else
			m_ef9367->data_w(m_portc & 0xf, m_porta);
	}

	// B1: clock ROM address latch
	if (data & ~m_portb & 2)
		m_romlatch = m_portc;

	// B2: shift custom chip command
	if (~data & m_portb & 4)
	{
		// reset count
		if (~data & 2)
			m_ccount = 0;
		else
			m_ccount = (m_ccount + 2) & 7;

		// bit 0: enable vram read
		// bit 1: enable display
		// bit 2: same as bit 0
		// bit 3: always 1
		// bit 4-7: color
		u8 mask = 3 << m_ccount;
		m_command = (m_command & ~mask) | (m_porta << m_ccount & mask);
	}

	// B3: erase led
	m_led = BIT(~data, 3);

	// B4: ROM _OE
	// B5-B7: input mux
	m_portb = data;
}

void videoart_state::portc_w(u8 data)
{
	// C0-C7: ROM address
	// C0-C3: EF9367 address
	// C4: EF9367 R/W
	m_portc = data;
}

u8 videoart_state::portd_r()
{
	u8 data = 0;

	// D6,D7: multiplexed inputs
	for (int i = 0; i < 3; i++)
		if (!BIT(m_portb, 5 + i))
			data |= m_inputs[i]->read() & 0xc0;

	return ~data;
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( videoart )
	PORT_START("IN0")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_BUTTON5) PORT_NAME("Page")
	PORT_BIT(0x80, 0x80, IPT_CUSTOM) PORT_CONDITION("IN3", 0x03, EQUALS, 0x03) // clear

	PORT_START("IN1")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Horizontal")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_NAME("Vertical")

	PORT_START("IN2")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Draw")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_BUTTON4) PORT_NAME("Erase")

	PORT_START("IN3") // clear is actually 2 buttons
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON6) PORT_NAME("Clear (1/2)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON7) PORT_NAME("Clear (2/2)")

	PORT_START("AN0")
	PORT_BIT(0xff, 0x7b, IPT_AD_STICK_X) PORT_SENSITIVITY(50) PORT_KEYDELTA(2) PORT_CENTERDELTA(0) PORT_REVERSE PORT_MINMAX(0x00, 0xf6) PORT_PLAYER(2) PORT_NAME("Color")

	PORT_START("AN1")
	PORT_BIT(0xff, 0x80, IPT_AD_STICK_X) PORT_SENSITIVITY(50) PORT_KEYDELTA(4) PORT_CENTERDELTA(0) // joystick does not autocenter

	PORT_START("AN2")
	PORT_BIT(0xff, 0x6a, IPT_AD_STICK_Y) PORT_SENSITIVITY(50) PORT_KEYDELTA(4) PORT_CENTERDELTA(0) PORT_REVERSE PORT_MINMAX(0x00, 0xd4) // "
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void videoart_state::videoart(machine_config &config)
{
	// basic machine hardware
	M6805R2(config, m_maincpu, 14.318181_MHz_XTAL / 4);
	m_maincpu->porta_w().set(FUNC(videoart_state::porta_w));
	m_maincpu->porta_r().set(FUNC(videoart_state::porta_r));
	m_maincpu->portb_w().set(FUNC(videoart_state::portb_w));
	m_maincpu->portc_w().set(FUNC(videoart_state::portc_w));
	m_maincpu->portd_r().set(FUNC(videoart_state::portd_r));
	m_maincpu->portan_r<0>().set_ioport("AN0");
	m_maincpu->portan_r<1>().set_ioport("AN1");
	m_maincpu->portan_r<2>().set_ioport("AN2");

	// video hardware
	PALETTE(config, "palette", FUNC(videoart_state::palette), 16);

	EF9365(config, m_ef9367, (14.318181_MHz_XTAL * 2) / 19);
	m_ef9367->set_addrmap(0, &videoart_state::vram_map);
	m_ef9367->set_palette_tag("palette"); // unused there
	m_ef9367->set_nb_bitplanes(1);
	m_ef9367->set_display_mode(ef9365_device::DISPLAY_MODE_512x256);
	m_ef9367->irq_handler().set_inputline(m_maincpu, M6805_IRQ_LINE);
	m_ef9367->write_msl().set(FUNC(videoart_state::msl_w));

	TIMER(config, "scanline").configure_scanline(FUNC(videoart_state::scanline), "screen", 0, 1);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(m_ef9367->clock() / (262.0 * 96.0));
	m_screen->set_screen_update(FUNC(videoart_state::screen_update));
	m_screen->set_size(512, 256);
	m_screen->set_visarea(0, 512-1, 48, 256-1);
	m_screen->set_palette("palette");

	config.set_default_layout(layout_videoart);

	// cartridge
	GENERIC_CARTSLOT(config, m_cart, generic_linear_slot, "videoart");
	m_cart->set_device_load(FUNC(videoart_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("videoart");
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( videoart )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD("ljd091.u6", 0x0000, 0x1000, CRC(111ad7d4) SHA1(dec751069a6713ec2e033aed5657378ccfcddebb) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1987, videoart, 0,      0,      videoart, videoart, videoart_state, empty_init, "LJN Toys", "VideoArt", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_COLORS | MACHINE_NO_SOUND_HW )



vip.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

RCA COSMAC VIP

PCB Layout
----------

|-------------------------------------------------------------------------------|
|   |---------------CN1---------------|     |---------------CN2---------------| |
|CN6                                                                            |
|   |---------|                 |---------|             4050        4050        |
|   | CDPR566 |                 | CDP1861 |                                 CN3 |
|   |---------|                 |---------|                                     |
|                                                                           CN4 |
| 7805                                      |---------|      |--------|         |
|       2114                    3.521280MHz |  4508   |      |  4508  |     CN5 |
|                                           |---------|      |--------|         |
|       2114                                                 |--------|         |
|                               7474  7400  4049  4051  4028 |  4515  | CA3401  |
|       2114                                                 |--------|         |
|               |-------------|                                                 |
|       2114    |   CDP1802   |                                                 |
|               |-------------|             LED1                                |
|       2114                                                                    |
|                                           LED2                                |
|       2114    4556    4042                                                    |
|                                           LED3                                |
|  555  2114    4011    4013                                                    |
|                                           SW1                                 |
|       2114                                                                    |
|                                                                               |
|-------------------------------------------------------------------------------|

Notes:
    All IC's shown.

    CDPR566 - Programmed CDP1832 512 x 8-Bit Static ROM
    2114    - 2114 4096 Bit (1024x4) NMOS Static RAM
    CDP1802 - RCA CDP1802 CMOS 8-Bit Microprocessor
    CDP1861 - RCA CDP1861 Video Display Controller
    CA3401  - Quad Single-Supply Op-Amp
    CN1     - expansion interface connector
    CN2     - parallel I/O interface connector
    CN3     - video connector
    CN4     - tape in connector
    CN5     - tape out connector
    CN6     - power connector
    LED1    - power led
    LED2    - Q led
    LED3    - tape led
    SW1     - Run/Reset switch

*/

/*

    TODO:

    - ASCII keyboard
    - cassette loading
    - 20K RAM for Floating Point BASIC
    - VP-111 has 1K RAM, no byte I/O, no expansion
    - VP-601/611 ASCII Keyboard (VP-601 58 keys, VP611 58 keys + 16 keys numerical keypad)
    - VP-700 Expanded Tiny Basic Board (4 KB ROM expansion)

*/

/*

    VP-711 COSMAC MicroComputer $199
    (CDP18S711) Features RCA COSMAC microprocessor. 2K RAM, expandable to
    32K (4K on-board). Built-in cassette interface and video interface.
    16 key hexidecimal keypad. ROM operating system. CHIP-8 language and
    machine language. Tone generator and speaker. 8-bit input port, 8-bit
    output port, and full system expansion connector. Power supply and 3
    manuals (VP-311, VP-320, MPM201 B) included. Completely assembled.

    VP-44 VP-711 RAM On-Board Expansion Kit $36
    Four type 2114 RAM IC's for expanding VP-711 on-board memory
    to 4K bytes.

    VP-111 MicroComputer $99
    RCA COSMAC microprocessor. 1 K RAM expandable to 32K (4K On-
    board). Built-in cassette interface and video interface. 16 key
    Hexidecimal keypad. ROM operating system. CHIP-8 language and Machine
    language. Tone generator. Assembled - user must install Cables
    (supplied) and furnish 5 volt power supply and speaker.

    VP-114 VP-111 Expansion Kit $76
    Includes I/O ports, system expansion connector and additional
    3K of RAM. Expands VP-111 to VP-711 capability.

    VP-155 VP-111 Cover $12
    Attractive protective plastic cover for VP-111.

    VP-3301 Interactive Data Terminal Available Approx. 6 Months
    Microprocessor Based Computer Terminal with keyboard, video
    Interface and color graphics - includes full resident and user
    Definable character font, switch selectable configuration, cursor
    Control, reverse video and many other features.

    VP-590 Color Board $69
    Displays VP-711 output in color! Program control of four
    Background colors and eight foreground colors. CHIP-8X language
    Adds color commands. Includes two sockets for VP-580 Expansion
    Keyboards.

    VP-595 Simple Sound Board $30
    Provides 256 different frequencies in place of VP-711 single-
    tone Output. Use with VP-590 Color Board for simultaneous color and
    Sound. Great for simple music or sound effects! Includes speaker.

    VP-550 Super Sound Board $49
    Turn your VP-711 into a music synthesizer! Two independent
    sound Channels. Frequency, duration and amplitude envelope (voice) of
    Each channel under program control. On-board tempo control. Provision
    for multi-track recording or slaving VP-711's. Output drives audio
    preamp. Does not permit simultaneous video display.

    VP-551 Super Sound 4-Channel Expander Package $74
    VP-551 provides four (4) independent sound channels with
    frequency duration and amplitude envelope for each channel. Package
    includes modified VP-550 super sound board, VP-576 two board
    expander, data cassette with 4-channel PIN-8 program, and instruction
    manual. Requires 4K RAM system and your VP-550 Super Sound Board.

    VP-570 Memory Expansion Board $95
    Plug-in 4K static RAM memory. Jumper locates RAM in any 4K
    block in first 32K of VP-711 memory space.

    VP-580 Auxiliary Keyboard $20
    Adds two-player interactive game capability to VP-711 16-key
    keypad with cable. Connects to sockets on VP-590 Color Board or VP-
    585 Keyboard Interface.

    VP-585 Keyboard Interface Board $15
    Interfaces two VP-580 Expansion Keyboards directly to the VP-
    711. Not required when VP-590 Color Board is used.

    VP-560 EPROM Board $34
    Interfaces two Intel 2716 EPROMs to VP-711. Places EPROMs any-
    where in memory space. Can also re-allocate on-board RAM in memory
    space.

    VP-565 EPROM Programmer Board $99
    Programs Intel 2716 EPROMs with VP-711. Complete with software
    to program, copy, and verify. On-board generation of all programming
    voltages.

    VP-575 Expansion Board $59
    Plug-in board with 4 buffered and one unbuffered socket.
    Permits use of up to 5 Accessory Boards in VP-711 Expansion Socket.

    VP-576 Two-Board Expander $20
    Plug-in board for VP-711 I/O or Expansion Socket permits use
    of two Accessory Boards in either location.

    VP-601* ASCII Keyboard. 7-Bit Parallel Output $69
    Fully encoded, 128-character ASCII alphanumeric keyboard. 58
    light touch keys (2 user defined). Selectable "Upper-Case-Only".

    VP-606* ASCII Keyboard - Serial Output $99
    Same as VP-601. EIA RS232C compatible, 20 mA current loop and
    TTL outputs. Six selectable baud rates. Available mid-1980.

    VP-611* ASCII/Numeric Keyboard. 7-Bit Parallel Output $89
    ASCII Keyboard identical to VP-601 plus 16 key numeric entry
    keyboard for easier entry of numbers.

    VP-616* ASCII/Numeric Keyboard - Serial Output $119
    Same as VP-611. EIA RS232C compatible, 20 mA current loop and
    TTL outputs. Six selectable baud rates. Available mid-1980.

    VP-620 Cable: ASCII Keyboards to VP-711 $20
    Flat ribbon cable, 24" length, for connecting VP-601 or VP-
    611 and VP-711. Includes matching connector on both ends.

    VP-623 Cable: Parallel Output ASCII Keyboards $20
    Flat ribbon cable, 36" length with mating connector for VP-
    601 or VP-611 Keyboards. Other end is unterminated.

    VP-626 Connector: Serial Output ASCII Keyboards & Terminal $7
    25 pin solderable male "D" connector mates to VP-606, VP-616
    or VP-3301.

    TC1210 9" Video Monitor $195
    Ideal, low-cost monochrome monitor for displaying the video
    output from your microcomputer or terminal.

    TC1217 17" Video Monitor $480
    A really BIG monochrome monitor for use with your
    microcomputer or terminal 148 sq. in. pictures.

    VP-700 Tiny BASIC ROM Board $39
    Run Tiny BASIC on your VP-711! All BASIC code stored in ROM.
    Requires separate ASCII keyboard.

    VP-701 Floating Point BASIC for VP-711 $49
    16K bytes on cassette tape, includes floating point and
    integer math, string capability and color graphics. More than 70
    commands and statements. Available mid-1980.

    VP-710 Game Manual $10
    More exciting games for your VP-711! Includes Blackjack,
    Biorythm, Pinball, Bowling and 10 others.

    VP-720 Game Manual II More exciting games. Available mid-1980. $15

    VP-311 VP-711 Instruction Manual (Included with VP-711) $5

    VP-320 VP-711 User Guide Manual (Included with VP-711) $5

    MPM-201B CDP1802 User Manual (Included with VP-711) $5

    * Quantities of 15 or more available less case and speaker (Assembled
    keypad and circut board only). Price on request.


Usage:
    - If you turn it on as is, it quickly jumps into the weeds as it is expecting
      valid code to exist at 0000. To enter the monitor, press R, hold C, press R,
      (you will see the memory editor) then choose a command (0 for example).
    - If you load a chip-8 cart, press R twice. If it doesn't do anything you may
      need to do a hard reset, then hit R twice. R toggles between the CPU running
      or stopped. Most chip-8 (.c8) programs work, but make sure 4k RAM is enabled.
    - There's a slot option to use Tiny Basic, this starts up, but unable to type
      anything.
    - Not known if (.bin) files work - don't have any for this machine.
    - (.c8x) files do not work.
    - Cassette records and plays back, however about 10% of the data is
      consistently loaded wrongly.

*/

#include "emu.h"
#include "vip.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


//**************************************************************************
//  MACROS/CONSTANTS
//**************************************************************************

enum
{
	LED_POWER = 0,
	LED_Q,
	LED_TAPE
};



//**************************************************************************
//  IMPLEMENTATION
//**************************************************************************

//-------------------------------------------------
//  update_interrupts -
//-------------------------------------------------

void vip_state::update_interrupts()
{
	int irq = m_vdc_int || m_exp_int;
	int dma_in = m_exp_dma_in;
	int dma_out = m_vdc_dma_out || m_exp_dma_out;

	m_maincpu->set_input_line(COSMAC_INPUT_LINE_INT, irq);
	m_maincpu->set_input_line(COSMAC_INPUT_LINE_DMAIN, dma_in);
	m_maincpu->set_input_line(COSMAC_INPUT_LINE_DMAOUT, dma_out);
}


//-------------------------------------------------
//  read -
//-------------------------------------------------

uint8_t vip_state::read(offs_t offset)
{
	int cs = BIT(offset, 15) || m_8000;
	int cdef = !((offset >= 0xc00) && (offset < 0x1000));
	int minh = 0;

	uint8_t data = m_exp->program_r(offset, cs, cdef, &minh);

	if (cs)
	{
		data = m_rom->base()[offset & 0x1ff];
	}
	else if (!minh)
	{
		data = m_ram->pointer()[offset & m_ram->mask()];
	}

	return data;
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

void vip_state::write(offs_t offset, uint8_t data)
{
	int cs = BIT(offset, 15) || m_8000;
	int cdef = !((offset >= 0xc00) && (offset < 0x1000));
	int minh = 0;

	m_exp->program_w(offset, data, cdef, &minh);

	if (!cs && !minh)
	{
		m_ram->pointer()[offset & m_ram->mask()] = data;
	}
}


//-------------------------------------------------
//  io_r -
//-------------------------------------------------

uint8_t vip_state::io_r(offs_t offset)
{
	uint8_t data = m_exp->io_r(offset);

	switch (offset)
	{
	case 1:
		m_vdc->disp_on_w(1);
		m_vdc->disp_on_w(0);
		break;

	case 3:
		data = m_byteio_data;
		break;
	}

	if (BIT(offset, 2))
	{
		m_8000 = 0;
	}

	return data;
}


//-------------------------------------------------
//  io_w -
//-------------------------------------------------

void vip_state::io_w(offs_t offset, uint8_t data)
{
	m_exp->io_w(offset, data);

	switch (offset)
	{
	case 1:
		m_vdc->disp_off_w(1);
		m_vdc->disp_off_w(0);
		break;

	case 2:
		m_keylatch = data & 0x0f;
		break;

	case 3:
		m_byteio->out_w(data);
		break;
	}

	if (BIT(offset, 2))
	{
		m_8000 = 0;
	}
}



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( vip_mem )
//-------------------------------------------------

void vip_state::vip_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(vip_state::read), FUNC(vip_state::write));
}


//-------------------------------------------------
//  ADDRESS_MAP( vip_io )
//-------------------------------------------------

void vip_state::vip_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x07).rw(FUNC(vip_state::io_r), FUNC(vip_state::io_w));
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( vip )
//-------------------------------------------------

INPUT_CHANGED_MEMBER(vip_state::reset_w)
{
	m_exp->run_w(newval);

	if (oldval && !newval)
	{
		machine_reset();
	}
}

INPUT_CHANGED_MEMBER(vip_state::beeper_w)
{
	m_beeper->set_output_gain(0, newval ? 0.80 : 0);
}

static INPUT_PORTS_START( vip )
	PORT_START("KEYPAD")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("0 MW") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("A MR") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("B TR") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("F TW") PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("RUN")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("Run/Reset") PORT_CODE(KEYCODE_R) PORT_TOGGLE PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vip_state::reset_w), 0)

	PORT_START("BEEPER")
	PORT_CONFNAME( 0x01, 0x01, "Internal Speaker" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vip_state::beeper_w), 0)
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  COSMAC_INTERFACE( cosmac_intf )
//-------------------------------------------------

int vip_state::clear_r()
{
	return BIT(m_run->read(), 0);
}

int vip_state::ef1_r()
{
	return m_vdc_ef1 || m_exp->ef1_r();
}

int vip_state::ef2_r()
{
	m_leds[LED_TAPE] = m_cassette->input() > 0 ? 1 : 0;

	return (m_cassette->input() < 0) ? ASSERT_LINE : CLEAR_LINE;
}

int vip_state::ef3_r()
{
	return !BIT(m_keypad->read(), m_keylatch) || m_byteio_ef3 || m_exp_ef3;
}

int vip_state::ef4_r()
{
	return m_byteio_ef4 || m_exp_ef4;
}

void vip_state::q_w(int state)
{
	// sound output
	m_beeper->write(NODE_01, state);

	// Q led
	m_leds[LED_Q] = state ? 1 : 0;

	// tape output
	m_cassette->output(state ? 1.0 : -1.0);

	// expansion
	m_exp->q_w(state);
}


//-------------------------------------------------
//  CDP1861_INTERFACE( vdc_intf )
//-------------------------------------------------

void vip_state::vdc_int_w(int state)
{
	m_vdc_int = state;

	update_interrupts();
}

void vip_state::vdc_dma_out_w(int state)
{
	m_vdc_dma_out = state;

	update_interrupts();
}

void vip_state::vdc_ef1_w(int state)
{
	m_vdc_ef1 = state;
}

uint32_t vip_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	m_vdc->screen_update(screen, bitmap, cliprect);

	m_exp->screen_update(screen, bitmap, cliprect);

	return 0;
}


//-------------------------------------------------
//  DISCRETE_SOUND( vip )
//-------------------------------------------------

static const discrete_555_desc vip_ca555_a =
{
	DISC_555_OUT_SQW | DISC_555_OUT_DC,
	5,      // B+ voltage of 555
	DEFAULT_555_VALUES
};

static DISCRETE_SOUND_START( vip_discrete )
	DISCRETE_INPUT_LOGIC(NODE_01)
	DISCRETE_555_ASTABLE_CV(NODE_02, NODE_01, 470, RES_M(1), CAP_P(470), NODE_01, &vip_ca555_a)
	DISCRETE_OUTPUT(NODE_02, 5000)
DISCRETE_SOUND_END


//-------------------------------------------------
//  VIP_BYTEIO_PORT_INTERFACE( byteio_intf )
//-------------------------------------------------

void vip_state::byteio_inst_w(int state)
{
	if (!state)
	{
		m_byteio_data = m_byteio->in_r();
	}
}


//-------------------------------------------------
//  VIP_EXPANSION_INTERFACE( expansion_intf )
//-------------------------------------------------

void vip_state::exp_int_w(int state)
{
	m_exp_int = state;

	update_interrupts();
}

void vip_state::exp_dma_out_w(int state)
{
	m_exp_dma_out = state;

	update_interrupts();
}

void vip_state::exp_dma_in_w(int state)
{
	m_exp_dma_in = state;

	update_interrupts();
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( vip )
//-------------------------------------------------

void vip_state::machine_start()
{
	uint8_t *ram = m_ram->pointer();

	// randomize RAM contents
	for (uint16_t addr = 0; addr < m_ram->size(); addr++)
	{
		ram[addr] = machine().rand() & 0xff;
	}

	m_leds.resolve();

	// turn on power LED
	m_leds[LED_POWER] = 1;

	// reset sound
	m_beeper->write(NODE_01, 0);

	// state saving
	save_item(NAME(m_8000));
	save_item(NAME(m_vdc_int));
	save_item(NAME(m_vdc_dma_out));
	save_item(NAME(m_vdc_ef1));
	save_item(NAME(m_exp_int));
	save_item(NAME(m_exp_dma_out));
	save_item(NAME(m_exp_dma_in));
	save_item(NAME(m_byteio_ef3));
	save_item(NAME(m_byteio_ef4));
	save_item(NAME(m_exp_ef1));
	save_item(NAME(m_exp_ef3));
	save_item(NAME(m_exp_ef4));
	save_item(NAME(m_keylatch));
	save_item(NAME(m_byteio_data));
}


void vip_state::machine_reset()
{
	// reset the VDC
	m_vdc->reset();

	// force map ROM
	m_8000 = 1;

	// internal speaker
	m_beeper->set_output_gain(0, m_io_beeper->read() ? 0.80 : 0);

	// clear byte I/O latch
	m_byteio_data = 0;
}


//-------------------------------------------------
//  QUICKLOAD_LOAD_MEMBER( vip_state, vip )
//-------------------------------------------------

QUICKLOAD_LOAD_MEMBER(vip_state::quickload_cb)
{
	uint8_t *ram = m_ram->pointer();
	uint8_t *chip8_ptr = nullptr;
	int chip8_size = 0;
	int size = image.length();

	if (image.is_filetype("c8"))
	{
		/* CHIP-8 program */
		chip8_ptr = m_chip8->base();
		chip8_size = m_chip8->bytes();
	}
	else if (image.is_filetype("c8x"))
	{
		/* CHIP-8X program */
		chip8_ptr = m_chip8x->base();
		chip8_size = m_chip8x->bytes();
	}

	if ((size + chip8_size) > m_ram->size())
	{
		return std::make_pair(
				image_error::INVALIDIMAGE,
				util::string_format("%u-byte interpreter and %u-byte program are too large for %u-byte RAM", chip8_size, size, m_ram->size()));
	}

	if (chip8_size > 0)
	{
		/* copy CHIP-8 interpreter to RAM */
		memcpy(ram, chip8_ptr, chip8_size);
	}

	/* load image to RAM */
	image.fread(ram + chip8_size, size);

	return std::make_pair(std::error_condition(), std::string());
}



//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

//-------------------------------------------------
//  machine_config( vip )
//-------------------------------------------------

void vip_state::vip(machine_config &config)
{
	// basic machine hardware
	CDP1802(config, m_maincpu, 3.52128_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &vip_state::vip_mem);
	m_maincpu->set_addrmap(AS_IO, &vip_state::vip_io);
	m_maincpu->wait_cb().set_constant(1);
	m_maincpu->clear_cb().set(FUNC(vip_state::clear_r));
	m_maincpu->ef1_cb().set(FUNC(vip_state::ef1_r));
	m_maincpu->ef2_cb().set(FUNC(vip_state::ef2_r));
	m_maincpu->ef3_cb().set(FUNC(vip_state::ef3_r));
	m_maincpu->ef4_cb().set(FUNC(vip_state::ef4_r));
	m_maincpu->q_cb().set(FUNC(vip_state::q_w));
	m_maincpu->dma_rd_cb().set(m_exp, FUNC(vip_expansion_slot_device::dma_r));
	m_maincpu->dma_wr_cb().set(m_vdc, FUNC(cdp1861_device::dma_w));
	m_maincpu->dma_wr_cb().append(m_exp, FUNC(vip_expansion_slot_device::dma_w));
	m_maincpu->sc_cb().set(m_exp, FUNC(vip_expansion_slot_device::sc_w));
	m_maincpu->tpb_cb().set(m_exp, FUNC(vip_expansion_slot_device::tpb_w));

	// video hardware
	CDP1861(config, m_vdc, 3.52128_MHz_XTAL / 2).set_screen(SCREEN_TAG);
	m_vdc->int_cb().set(FUNC(vip_state::vdc_int_w));
	m_vdc->dma_out_cb().set(FUNC(vip_state::vdc_dma_out_w));
	m_vdc->efx_cb().set(FUNC(vip_state::vdc_ef1_w));

	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_screen_update(FUNC(vip_state::screen_update));

	// sound hardware
	SPEAKER(config, "mono").front_center();

	DISCRETE(config, m_beeper, vip_discrete);
	m_beeper->add_route(ALL_OUTPUTS, "mono", 0.80);

	VIP_BYTEIO_PORT(config, m_byteio, vip_byteio_cards, nullptr);
	m_byteio->inst_callback().set(FUNC(vip_state::byteio_inst_w));
	VIP_EXPANSION_SLOT(config, m_exp, 3.52128_MHz_XTAL / 2, vip_expansion_cards, nullptr);
	m_exp->int_wr_callback().set(FUNC(vip_state::exp_int_w));
	m_exp->dma_out_wr_callback().set(FUNC(vip_state::exp_dma_out_w));
	m_exp->dma_in_wr_callback().set(FUNC(vip_state::exp_dma_in_w));

	// devices
	quickload_image_device &quickload(QUICKLOAD(config, "quickload", "bin,c8", attotime::from_seconds(2)));
	quickload.set_load_callback(FUNC(vip_state::quickload_cb));
	quickload.set_interface("chip8quik");
	SOFTWARE_LIST(config, "quik_list").set_original("chip8_quik").set_filter("V");

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->set_interface("vip_cass");
	SOFTWARE_LIST(config, "cass_list").set_original("vip");

	// internal ram
	RAM(config, m_ram).set_default_size("2K").set_extra_options("4K");
}


//-------------------------------------------------
//  machine_config( vp111 )
//-------------------------------------------------

void vip_state::vp111(machine_config &config)
{
	vip(config);
	// internal ram
	m_ram->set_default_size("1K").set_extra_options("2K,4K");
}



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( vip )
//-------------------------------------------------

ROM_START( vip )
	ROM_REGION( 0x200, CDP1802_TAG, 0 )
	ROM_LOAD( "cdpr566.u10", 0x0000, 0x0200, CRC(5be0a51f) SHA1(40266e6d13e3340607f8b3dcc4e91d7584287c06) )

	ROM_REGION( 0x200, "chip8", 0 )
	ROM_LOAD( "chip8.bin", 0x0000, 0x0200, CRC(438ec5d5) SHA1(8aa634c239004ff041c9adbf9144bd315ab5fc77) )

	ROM_REGION( 0x300, "chip8x", 0 )
	ROM_LOAD( "chip8x.bin", 0x0000, 0x0300, CRC(79c5f6f8) SHA1(ed438747b577399f6ccbf20fe14156f768842898) )
ROM_END


//-------------------------------------------------
//  ROM( vp111 )
//-------------------------------------------------

#define rom_vp111 rom_vip



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY  FULLNAME                FLAGS
COMP( 1977, vip,   0,      0,      vip,     vip,   vip_state, empty_init, "RCA",   "Cosmac VIP (VP-711)",  MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_COLORS )
COMP( 1977, vp111, vip,    0,      vp111,   vip,   vip_state, empty_init, "RCA",   "Cosmac VIP (VP-111)",  MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_COLORS )



vis.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Carl

#include "emu.h"
#include "machine/at.h"

#include "bus/isa/isa_cards.h"
#include "cpu/i86/i286.h"
#include "machine/8042kbdc.h"
#include "machine/ds6417.h"
#include "sound/dac.h"
#include "sound/ymopl.h"
#include "video/pc_vga.h"

#include "softlist_dev.h"
#include "speaker.h"


class vis_audio_device : public device_t,
						 public device_isa16_card_interface
{
public:
	vis_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	uint8_t pcm_r(offs_t offset);
	void pcm_w(offs_t offset, uint8_t data);
protected:
	virtual void device_start() override ATTR_COLD;
	virtual void device_reset() override ATTR_COLD;
	virtual void dack16_w(int line, uint16_t data) override;
	virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;

	TIMER_CALLBACK_MEMBER(pcm_update);

private:
	required_device<dac_16bit_r2r_device> m_rdac;
	required_device<dac_16bit_r2r_device> m_ldac;
	uint16_t m_count = 0U;
	uint16_t m_curcount = 0U;
	uint16_t m_sample[2]{};
	uint8_t m_index[2]{}; // unknown indexed registers, volume?
	uint8_t m_data[2][16]{};
	uint8_t m_mode = 0U;
	uint8_t m_ctrl = 0U;
	unsigned int m_sample_byte = 0U;
	unsigned int m_samples = 0U;
	emu_timer *m_pcm = nullptr;
};

DEFINE_DEVICE_TYPE(VIS_AUDIO, vis_audio_device, "vis_pcm", "vis_pcm")

vis_audio_device::vis_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, VIS_AUDIO, tag, owner, clock),
	device_isa16_card_interface(mconfig, *this),
	m_rdac(*this, "rdac"),
	m_ldac(*this, "ldac")
{
}

void vis_audio_device::device_start()
{
	set_isa_device();
	m_isa->set_dma_channel(7, this, false);
	m_isa->install_device(0x0220, 0x022f, read8sm_delegate(*this, FUNC(vis_audio_device::pcm_r)), write8sm_delegate(*this, FUNC(vis_audio_device::pcm_w)));
	m_isa->install_device(0x0388, 0x038b, read8sm_delegate(*subdevice<ymf262_device>("ymf262"), FUNC(ymf262_device::read)), write8sm_delegate(*subdevice<ymf262_device>("ymf262"), FUNC(ymf262_device::write)));
	m_pcm = timer_alloc(FUNC(vis_audio_device::pcm_update), this);
	m_pcm->adjust(attotime::never);
}

void vis_audio_device::device_reset()
{
	m_count = 0;
	m_curcount = 0;
	m_sample_byte = 0;
	m_samples = 0;
	m_mode = 0;
	m_index[0] = m_index[1] = 0;
}

void vis_audio_device::dack16_w(int line, uint16_t data)
{
	m_sample[m_samples++] = data;
	m_curcount++;
	if((m_samples >= 2) || !(m_mode & 0x8))
		m_isa->drq7_w(CLEAR_LINE);
}

TIMER_CALLBACK_MEMBER(vis_audio_device::pcm_update)
{
	if(((m_samples < 2) && (m_mode & 8)) || !m_samples)
		return;

	switch(m_mode & 0x88)
	{
		case 0x80: // 8bit mono
		{
			uint8_t sample = m_sample[m_sample_byte >> 1] >> ((m_sample_byte & 1) * 8);
			m_ldac->write(sample << 8);
			m_rdac->write(sample << 8);
			m_sample_byte++;
			break;
		}
		case 0x00: // 8bit stereo
			m_ldac->write(m_sample[m_sample_byte >> 1] << 8);
			m_rdac->write(m_sample[m_sample_byte >> 1] & 0xff00);
			m_sample_byte += 2;
			break;
		case 0x88: // 16bit mono
			m_ldac->write(m_sample[m_sample_byte >> 1] ^ 0x8000);
			m_rdac->write(m_sample[m_sample_byte >> 1] ^ 0x8000);
			m_sample_byte += 2;
			break;
		case 0x08: // 16bit stereo
			m_ldac->write(m_sample[0] ^ 0x8000);
			m_rdac->write(m_sample[1] ^ 0x8000);
			m_sample_byte += 4;
			break;
	}

	if(m_sample_byte >= (m_mode & 8 ? 4 : 2))
	{
		m_sample_byte = 0;
		m_samples = 0;
		m_isa->drq7_w(ASSERT_LINE);
		if(m_curcount >= m_count)
		{
			m_curcount = 0;
			m_ctrl |= 4;
			if(BIT(m_ctrl, 1))
				m_isa->irq7_w(ASSERT_LINE);
		}
	}
}

void vis_audio_device::device_add_mconfig(machine_config &config)
{
	SPEAKER(config, "speaker", 2).front();

	ymf262_device &ymf262(YMF262(config, "ymf262", XTAL(14'318'181)));
	ymf262.add_route(0, "speaker", 1.00, 0);
	ymf262.add_route(1, "speaker", 1.00, 1);
	ymf262.add_route(2, "speaker", 1.00, 0);
	ymf262.add_route(3, "speaker", 1.00, 1);

	DAC_16BIT_R2R(config, m_ldac, 0);
	DAC_16BIT_R2R(config, m_rdac, 0);
	m_ldac->add_route(ALL_OUTPUTS, "speaker", 1.0, 0); // sanyo lc7883k
	m_rdac->add_route(ALL_OUTPUTS, "speaker", 1.0, 1); // sanyo lc7883k
}

uint8_t vis_audio_device::pcm_r(offs_t offset)
{
	switch(offset)
	{
		case 0x00:
			m_isa->irq7_w(CLEAR_LINE);
			m_ctrl &= ~4;
			return m_mode;
		case 0x02:
			return m_data[0][m_index[0]];
		case 0x04:
			return m_data[1][m_index[1]];
		case 0x09:
		{
			u8 ret = m_ctrl;
			m_isa->irq7_w(CLEAR_LINE);
			m_ctrl &= ~4;
			return ret;
		}
		case 0x0c:
			return m_count & 0xff;
		case 0x0e:
			return m_count >> 8;
		case 0x0f:
			//cdrom related?
			break;
		default:
			logerror("unknown pcm read %04x\n", offset);
			break;
	}
	return 0;
}

void vis_audio_device::pcm_w(offs_t offset, uint8_t data)
{
	u8 oldmode = m_mode;
	switch(offset)
	{
		case 0x00:
			m_mode = data;
			break;
		case 0x02:
			m_data[0][m_index[0] & 0xf] = data;
			return;
		case 0x03:
			m_index[0] = data;
			return;
		case 0x04:
			m_data[1][m_index[1] & 0xf] = data;
			return;
		case 0x05:
			m_index[1] = data;
			return;
		case 0x09:
			m_ctrl = data;
			return;
		case 0x0c:
			m_count = (m_count & 0xff00) | data;
			m_curcount = 0;
			break;
		case 0x0e:
			m_count = (m_count & 0xff) | (data << 8);
			m_curcount = 0;
			break;
		case 0x0f:
			//cdrom related?
			return;
		default:
			logerror("unknown pcm write %04x %02x\n", offset, data);
			return;
	}
	if((m_mode & 0x10) && (m_mode ^ oldmode))
	{
		m_samples = 0;
		m_sample_byte = 0;
		m_isa->drq7_w(ASSERT_LINE);
		attotime rate = attotime::from_ticks(1 << ((m_mode >> 5) & 3), 44100); // TODO : Unknown clock
		m_pcm->adjust(rate, 0, rate);
	}
	else if(!(m_mode & 0x10))
		m_pcm->adjust(attotime::never);
}

class vis_vga_device : public svga_device,
					   public device_isa16_card_interface
{
public:
	vis_vga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	uint8_t visvgamem_r(offs_t offset);
	void visvgamem_w(offs_t offset, uint8_t data);
protected:
	virtual void device_start() override ATTR_COLD;
	virtual void device_reset() override ATTR_COLD;
	virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
	virtual void recompute_params() override;

	virtual void io_3cx_map(address_map &map) override ATTR_COLD;

	virtual void crtc_map(address_map &map) override ATTR_COLD;
	virtual void gc_map(address_map &map) override ATTR_COLD;
	virtual void sequencer_map(address_map &map) override ATTR_COLD;

	void io_isa_map(address_map &map) ATTR_COLD;
private:
	u8 ramdac_hidden_mask_r(offs_t offset);
	void ramdac_hidden_mask_w(offs_t offset, u8 data);

	void vga_vh_yuv8(bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void vga_vh_yuv422(bitmap_rgb32 &bitmap, const rectangle &cliprect);
	rgb_t yuv_to_rgb(int y, int u, int v) const;
	virtual uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) override;

	inline void flush_8bpp_mode();

	int m_extcnt = 0;
	uint8_t m_extreg = 0U;
	uint8_t m_interlace = 0U;
	uint16_t m_wina = 0U, m_winb = 0U;
	uint8_t m_shift256 = 0U, m_dw = 0U, m_8bit_640 = 0U;
};

DEFINE_DEVICE_TYPE(VIS_VGA, vis_vga_device, "vis_vga", "vis_vga")

vis_vga_device::vis_vga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	svga_device(mconfig, VIS_VGA, tag, owner, clock),
	device_isa16_card_interface(mconfig, *this)
{
	set_screen(*this, "screen");
	set_vram_size(0x100000);
	m_crtc_space_config = address_space_config("crtc_regs", ENDIANNESS_LITTLE, 8, 8, 0, address_map_constructor(FUNC(vis_vga_device::crtc_map), this));
	m_gc_space_config = address_space_config("gc_regs", ENDIANNESS_LITTLE, 8, 8, 0, address_map_constructor(FUNC(vis_vga_device::gc_map), this));
	m_seq_space_config = address_space_config("sequencer_regs", ENDIANNESS_LITTLE, 8, 8, 0, address_map_constructor(FUNC(vis_vga_device::sequencer_map), this));
}

void vis_vga_device::device_add_mconfig(machine_config &config)
{
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(XTAL(25'174'800), 900, 0, 640, 526, 0, 480);
	screen.set_screen_update(FUNC(vis_vga_device::screen_update));
}

void vis_vga_device::io_3cx_map(address_map &map)
{
	svga_device::io_3cx_map(map);
	map(0x06, 0x06).rw(FUNC(vis_vga_device::ramdac_hidden_mask_r), FUNC(vis_vga_device::ramdac_hidden_mask_w));
}

void vis_vga_device::crtc_map(address_map &map)
{
	svga_device::crtc_map(map);
	// Override with interlace into account
	map(0x00, 0x00).lw8(
		NAME([this] (offs_t offset, u8 data) {
			if(vga.crtc.protect_enable)
				return;
			vga.crtc.horz_total = data / (m_interlace && !(vga.sequencer.data[0x25] & 0x20) ? 2 : 1);
			recompute_params();
		})
	);
	map(0x01, 0x01).lw8(
		NAME([this] (offs_t offset, u8 data) {
			if(vga.crtc.protect_enable)
				return;
			vga.crtc.horz_disp_end = (data / (m_interlace && !(vga.sequencer.data[0x25] & 0x20) ? 2 : 1)) | 1;
			recompute_params();
		})
	);
	map(0x02, 0x02).lw8(
		NAME([this] (offs_t offset, u8 data) {
			if(vga.crtc.protect_enable)
				return;
			vga.crtc.horz_blank_start = data / (m_interlace && !(vga.sequencer.data[0x25] & 0x20) ? 2 : 1);
		})
	);
	map(0x03, 0x03).lw8(
		NAME([this] (offs_t offset, u8 data) {
			if(vga.crtc.protect_enable)
				return;
			vga.crtc.horz_blank_end = (vga.crtc.data[0x05] & 0x80) >> 2;
			vga.crtc.horz_blank_end |= data & 0x1f;
			vga.crtc.horz_blank_end /= (m_interlace && !(vga.sequencer.data[0x25] & 0x20) ? 2 : 1);
			vga.crtc.disp_enable_skew = (data & 0x60) >> 5;
			vga.crtc.evra = (data & 0x80) >> 7;
		})
	);
	map(0x04, 0x04).lw8(
		NAME([this] (offs_t offset, u8 data) {
			if(vga.crtc.protect_enable)
				return;
			vga.crtc.horz_retrace_start = data / (m_interlace && !(vga.sequencer.data[0x25] & 0x20) ? 2 : 1);
		})
	);
	map(0x05, 0x05).lw8(
		NAME([this] (offs_t offset, u8 data) {
			if(vga.crtc.protect_enable)
				return;
			vga.crtc.horz_blank_end = vga.crtc.data[0x05] & 0x1f;
			vga.crtc.horz_blank_end |= ((data & 0x80) >> 2);
			vga.crtc.horz_blank_end /= (m_interlace && !(vga.sequencer.data[0x25] & 0x20) ? 2 : 1);
			vga.crtc.horz_retrace_skew = ((data & 0x60) >> 5);
			vga.crtc.horz_retrace_end = data & 0x1f;
		})
	);
	map(0x06, 0x06).lw8(
		NAME([this] (offs_t offset, u8 data) {
			if(vga.crtc.protect_enable)
				return;
			vga.crtc.vert_total = ((vga.crtc.data[0x07] & 0x20) << (9-6)) | ((vga.crtc.data[0x07] & 0x01) << (8-0));
			vga.crtc.vert_total |= data;
			vga.crtc.vert_total *= (m_interlace ? 2 : 1);
			recompute_params();
		})
	);
	map(0x07, 0x07).lw8(
		NAME([this] (offs_t offset, u8 data) { // Overflow Register
			vga.crtc.line_compare       &= ~0x100;
			vga.crtc.line_compare       |= ((data & 0x10) << (8-4));
			if(vga.crtc.protect_enable)
				return;
			vga.crtc.vert_total         = vga.crtc.data[0x05];
			vga.crtc.vert_retrace_start = vga.crtc.data[0x10];
			vga.crtc.vert_disp_end      = vga.crtc.data[0x12];
			vga.crtc.vert_blank_start   = vga.crtc.data[0x15] | ((vga.crtc.data[0x09] & 0x20) << (9-5));
			vga.crtc.vert_retrace_start |= ((data & 0x80) << (9-7));
			vga.crtc.vert_disp_end      |= ((data & 0x40) << (9-6));
			vga.crtc.vert_total         |= ((data & 0x20) << (9-5));
			vga.crtc.vert_blank_start   |= ((data & 0x08) << (8-3));
			vga.crtc.vert_retrace_start |= ((data & 0x04) << (8-2));
			vga.crtc.vert_disp_end      |= ((data & 0x02) << (8-1));
			vga.crtc.vert_total         |= ((data & 0x01) << (8-0));
			vga.crtc.vert_total *= (m_interlace ? 2 : 1);
			vga.crtc.vert_blank_start *= (m_interlace ? 2 : 1);
			vga.crtc.vert_retrace_start *= (m_interlace ? 2 : 1);
			vga.crtc.vert_disp_end *= (m_interlace ? 2 : 1);
			recompute_params();
		})
	);
	map(0x09, 0x09).lw8(
		NAME([this] (offs_t offset, u8 data) { // Maximum Scan Line Register
			vga.crtc.line_compare      &= ~0x200;
			vga.crtc.vert_blank_start  = vga.crtc.data[0x15] | ((vga.crtc.data[0x07] & 0x02) << (8-3));
			vga.crtc.scan_doubling      = ((data & 0x80) >> 7);
			vga.crtc.line_compare      |= ((data & 0x40) << (9-6));
			vga.crtc.vert_blank_start  |= ((data & 0x20) << (9-5));
			vga.crtc.maximum_scan_line  = (data & 0x1f) + 1;
			vga.crtc.vert_blank_start *= (m_interlace ? 2 : 1);
		})
	);
	map(0x10, 0x10).lw8(
		NAME([this] (offs_t offset, u8 data) {
			vga.crtc.vert_retrace_start = ((vga.crtc.data[0x07] & 0x80) << (9-7)) | ((vga.crtc.data[0x07] & 0x40) << (8-2));
			vga.crtc.vert_retrace_start |= data;
			vga.crtc.vert_retrace_start *= (m_interlace ? 2 : 1);
		})
	);
	map(0x12, 0x12).lw8(
		NAME([this] (offs_t offset, u8 data) {
			vga.crtc.vert_disp_end = ((vga.crtc.data[0x07] & 0x40) << (9-6)) | ((vga.crtc.data[0x07] & 0x02) << (8-1));
			vga.crtc.vert_disp_end |= data;
			vga.crtc.vert_disp_end *= (m_interlace ? 2 : 1);
			recompute_params();
		})
	);
	map(0x15, 0x15).lw8(
		NAME([this] (offs_t offset, u8 data) {
			vga.crtc.vert_blank_start = ((vga.crtc.data[0x09] & 0x20) << (9-5)) | ((vga.crtc.data[0x07] & 0x02) << (8-3));
			vga.crtc.vert_blank_start |= data;
			vga.crtc.vert_blank_start *= (m_interlace ? 2 : 1);
		})
	);
	map(0x16, 0x16).lw8(
		NAME([this] (offs_t offset, u8 data) {
			vga.crtc.vert_blank_end = (data & 0x7f) * (m_interlace ? 2 : 1);
		})
	);
	map(0x14, 0x14).lw8(
		NAME([this] (offs_t offset, u8 data) {
			m_dw = data & 0x40 ? 1 : 0;
			if(vga.sequencer.data[0x1f] & 0x10)
				data |= 0x40;
		})
	);
	map(0x30, 0x30).lrw8(
		NAME([this] (offs_t offset) {
			return vga.crtc.data[0x30];
		}),
		NAME([this] (offs_t offset, u8 data) {
			if(data && !m_interlace)
			{
				if(!(vga.sequencer.data[0x25] & 0x20))
				{
					vga.crtc.horz_total /= 2;
					vga.crtc.horz_disp_end = (vga.crtc.horz_disp_end / 2) | 1;
					vga.crtc.horz_blank_end /= 2;
					vga.crtc.horz_retrace_start /= 2;
					vga.crtc.horz_retrace_end /= 2;
				}
				vga.crtc.vert_total *= 2;
				vga.crtc.vert_retrace_start *= 2;
				vga.crtc.vert_disp_end *= 2;
				vga.crtc.vert_blank_start *= 2;
				vga.crtc.vert_blank_end *= 2;
				recompute_params();
			}
			else if(!data && m_interlace)
			{
				if(!(vga.sequencer.data[0x25] & 0x20))
				{
					vga.crtc.horz_total *= 2;
					vga.crtc.horz_disp_end  = (vga.crtc.horz_disp_end * 2) | 1;
					vga.crtc.horz_blank_end *= 2;
					vga.crtc.horz_retrace_start *= 2;
					vga.crtc.horz_retrace_end *= 2;
				}
				vga.crtc.vert_total /= 2;
				vga.crtc.vert_retrace_start /= 2;
				vga.crtc.vert_disp_end /= 2;
				vga.crtc.vert_blank_start /= 2;
				vga.crtc.vert_blank_end /= 2;
				recompute_params();
			}
			m_interlace = data;
		})
	);
}

void vis_vga_device::gc_map(address_map &map)
{
	svga_device::gc_map(map);
	map(0x05, 0x05).lw8(
		NAME([this] (offs_t offset, u8 data) {
			vga.gc.shift256 = BIT(data, 6);
			// TODO: is this an hack?
			if (vga.sequencer.data[0x1f] & 0x10)
				vga.gc.shift256 = 1;
			vga.gc.shift_reg = BIT(data, 5);
			vga.gc.host_oe = BIT(data, 4);
			vga.gc.read_mode = BIT(data, 3);
			vga.gc.write_mode = data & 3;
			//if(data & 0x10 && vga.gc.alpha_dis)
			//  popmessage("Host O/E enabled, contact MAMEdev");
		})
	);
}

void vis_vga_device::sequencer_map(address_map &map)
{
	svga_device::sequencer_map(map);
	map(0x01, 0x01).lw8(
		NAME([this] (offs_t offset, u8 data) {
			m_8bit_640 = !BIT(data, 3);
			flush_8bpp_mode();
		})
	);
	map(0x18, 0x18).lrw8(
		NAME([this] (offs_t offset) {
			return m_wina >> 8;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_wina = (m_wina & 0xff) | (data << 8);
		})
	);
	map(0x19, 0x19).lrw8(
		NAME([this] (offs_t offset) {
			return m_wina & 0xff;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_wina = (m_wina & 0xff00) | data;
		})
	);
	map(0x1c, 0x1c).lrw8(
		NAME([this] (offs_t offset) {
			return m_winb >> 8;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_winb = (m_winb & 0xff) | (data << 8);
		})
	);
	map(0x1d, 0x1d).lrw8(
		NAME([this] (offs_t offset) {
			return m_winb & 0xff;
		}),
		NAME([this] (offs_t offset, u8 data) {
			m_winb = (m_winb & 0xff00) | data;
		})
	);
	map(0x1f, 0x1f).lw8(
		NAME([this] (offs_t offset, u8 data) {
			if(data & 0x10)
			{
				vga.gc.shift256 = 1;
				vga.crtc.dw = 1;
				vga.crtc.no_wrap = 1;
			}
			else
			{
				vga.gc.shift256 = m_shift256;
				vga.crtc.dw = m_dw;
				vga.crtc.no_wrap = 0;
			}
			flush_8bpp_mode();
		})
	);
}
void vis_vga_device::io_isa_map(address_map &map)
{
	map(0x00, 0x2f).m(FUNC(vis_vga_device::io_map));
}

void vis_vga_device::flush_8bpp_mode()
{
	svga.rgb8_en = m_8bit_640 && BIT(vga.sequencer.data[0x1f], 4);
}

void vis_vga_device::recompute_params()
{
	int vblank_period,hblank_period;
	attoseconds_t refresh;
	uint8_t hclock_m = (!vga.gc.alpha_dis) ? (vga.sequencer.data[1]&1)?8:9 : 8;
	int pixel_clock;
	const XTAL base_xtal = XTAL(14'318'181);
	const XTAL xtal = (vga.miscellaneous_output & 0xc) ? base_xtal*2 : base_xtal*1.75;
	int divisor = 1; // divisor is 2 for 15/16 bit rgb/yuv modes and 3 for 24bit

	/* safety check */
	if(!vga.crtc.horz_disp_end || !vga.crtc.vert_disp_end || !vga.crtc.horz_total || !vga.crtc.vert_total)
		return;

	rectangle visarea(0, ((vga.crtc.horz_disp_end + 1) * ((float)(hclock_m)/divisor))-1, 0, vga.crtc.vert_disp_end);

	vblank_period = (vga.crtc.vert_total + 2);
	hblank_period = ((vga.crtc.horz_total + 5) * ((float)(hclock_m)/divisor));

	/* TODO: 10b and 11b settings aren't known */
	pixel_clock = xtal.value() / (((vga.sequencer.data[1]&8) >> 3) + 1);

	refresh  = HZ_TO_ATTOSECONDS(pixel_clock) * (hblank_period) * vblank_period;
	screen().configure((hblank_period), (vblank_period), visarea, refresh );
	//popmessage("%d %d\n",vga.crtc.horz_total * 8,vga.crtc.vert_total);
	m_vblank_timer->adjust( screen().time_until_pos((vga.crtc.vert_blank_start + vga.crtc.vert_blank_end)) );
}

rgb_t vis_vga_device::yuv_to_rgb(int y, int u, int v) const
{
	u -= 128;
	v -= 128;
	double r = y + v * 1.371;
	double g = y - u * 0.337 - v * 0.698;
	double b = y + u * 1.733;

	return rgb_t(rgb_t::clamp(r), rgb_t::clamp(g), rgb_t::clamp(b));
}

void vis_vga_device::vga_vh_yuv8(bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const uint32_t IV = 0xff000000;
	int height = vga.crtc.maximum_scan_line * (vga.crtc.scan_doubling + 1);
	int curr_addr = 0;
	const uint8_t decode_tbl[] = {0, 1, 2, 3, 4, 5, 6, 9, 12, 17, 22, 29, 38, 50, 66, 91, 128, 165, 190,
		206, 218, 227, 234, 239, 244, 247, 250, 251, 252, 253, 254, 255};

	for (int addr = vga.crtc.start_addr, line=0; line<(vga.crtc.vert_disp_end+1); line+=height, addr+=offset(), curr_addr+=offset())
	{
		for(int yi = 0;yi < height; yi++)
		{
			uint8_t ydelta = 0, ua = 0, va = 0;
			if((line + yi) < (vga.crtc.line_compare & 0x3ff))
				curr_addr = addr;
			if((line + yi) == (vga.crtc.line_compare & 0x3ff))
				curr_addr = 0;
			for (int pos=curr_addr, col=0, column=0; column<(vga.crtc.horz_disp_end+1); column++, col+=8, pos+=8)
			{
				if(pos + 0x08 > 0x80000)
					return;

				for(int xi=0;xi<8;xi+=4)
				{
					if(!screen().visible_area().contains(col+xi, line + yi))
						continue;
					uint8_t a = vga.memory[pos + xi], b = vga.memory[pos + xi + 1];
					uint8_t c = vga.memory[pos + xi + 2], d = vga.memory[pos + xi + 3];
					uint8_t y[4], ub, vb;
					if(col || xi)
					{
						y[0] = decode_tbl[a & 0x1f] + ydelta;
						ub = decode_tbl[((a >> 5) & 3) | ((b >> 3) & 0x1c)] + ua;
						vb = decode_tbl[((c >> 5) & 3) | ((d >> 3) & 0x1c)] + va;
					}
					else
					{
						y[0] = (a & 0x1f) << 3;
						ua = ub = ((a >> 2) & 0x18) | (b & 0xe0);
						va = vb = ((c >> 2) & 0x18) | (d & 0xe0);
					}
					y[1] = decode_tbl[b & 0x1f] + y[0];
					y[2] = decode_tbl[c & 0x1f] + y[1];
					y[3] = decode_tbl[d & 0x1f] + y[2];
					uint8_t trans = (a >> 7) | ((c >> 6) & 2);
					uint16_t u = ua;
					uint16_t v = va;
					for(int i = 0; i < 4; i++)
					{
						if(i == trans)
						{
							u = (ua + ub) >> 1;
							v = (va + vb) >> 1;
						}
						else if(i == (trans + 1))
						{
							u = ub;
							v = vb;
						}
						bitmap.pix(line + yi, col + xi + i) = IV | (uint32_t)yuv_to_rgb(y[i], u, v);
					}
					ua = ub;
					va = vb;
					ydelta = y[3];
				}
			}
		}
	}
}

void vis_vga_device::vga_vh_yuv422(bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const uint32_t IV = 0xff000000;
	int height = vga.crtc.maximum_scan_line * (vga.crtc.scan_doubling + 1);
	int curr_addr = 0;

	for (int addr = vga.crtc.start_addr, line=0; line<(vga.crtc.vert_disp_end+1); line+=height, addr+=offset(), curr_addr+=offset())
	{
		for(int yi = 0;yi < height; yi++)
		{
			uint8_t ua = 0, va = 0;
			if((line + yi) < (vga.crtc.line_compare & 0x3ff))
				curr_addr = addr;
			if((line + yi) == (vga.crtc.line_compare & 0x3ff))
				curr_addr = 0;
			for (int pos=curr_addr, col=0, column=0; column<(vga.crtc.horz_disp_end+1); column++, col+=8,  pos+=8)
			{
				if(pos + 0x08 > 0x80000)
					return;
				for(int xi=0;xi<8;xi+=4)
				{
					if(!screen().visible_area().contains(col+xi, line + yi))
						continue;
					uint8_t y0 = vga.memory[pos + xi + 0], ub = vga.memory[pos + xi + 1];
					uint8_t y1 = vga.memory[pos + xi + 2], vb = vga.memory[pos + xi + 3];
					uint16_t u, v;
					if(col)
					{
						u = (ua + ub) >> 1;
						v = (va + vb) >> 1;
					}
					else
					{
						u = ub;
						v = vb;
					}
					ua = ub; va = vb;
					// this reads one byte per clock so it'll be one pixel for every 2 clocks
					bitmap.pix(line + yi, col + xi + 0) = IV | (uint32_t)yuv_to_rgb(y0, u, v);
					bitmap.pix(line + yi, col + xi + 1) = IV | (uint32_t)yuv_to_rgb(y0, u, v);
					bitmap.pix(line + yi, col + xi + 2) = IV | (uint32_t)yuv_to_rgb(y1, ub, vb);
					bitmap.pix(line + yi, col + xi + 3) = IV | (uint32_t)yuv_to_rgb(y1, ub, vb);
				}
			}
		}
	}
}

void vis_vga_device::device_start()
{
	set_isa_device();
	m_isa->install_memory(0x0a0000, 0x0bffff, read8sm_delegate(*this, FUNC(vis_vga_device::visvgamem_r)), write8sm_delegate(*this, FUNC(vis_vga_device::visvgamem_w)));
	m_isa->install_device(0x03b0, 0x03df, *this, &vis_vga_device::io_isa_map);

	svga_device::device_start();
	save_item(NAME(m_extreg));
	save_item(NAME(m_extcnt));
	save_item(NAME(m_wina));
	save_item(NAME(m_winb));
	save_item(NAME(m_interlace));
	save_item(NAME(m_shift256));
	save_item(NAME(m_dw));
	save_item(NAME(m_8bit_640));
}

void vis_vga_device::device_reset()
{
	m_extcnt = 0;
	m_extreg = 0;
	m_wina = 0;
	m_winb = 0;
	m_interlace = 0;
	m_shift256 = 0;
	m_dw = 0;
	m_8bit_640 = 0;
}

uint32_t vis_vga_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if(!BIT(m_extreg, 7))
		return svga_device::screen_update(screen, bitmap, cliprect);

	if((m_extreg & 0xc0) == 0x80)
	{
		svga_vh_rgb15(bitmap, cliprect);
		return 0;
	}

	switch(m_extreg & 0xc7)
	{
		case 0xc0:
		case 0xc1:
			svga_vh_rgb16(bitmap, cliprect);
			break;
		case 0xc2:
			popmessage("Border encoded 8-bit mode");
			break;
		case 0xc3:
			vga_vh_yuv422(bitmap, cliprect);
			break;
		case 0xc4:
			vga_vh_yuv8(bitmap, cliprect);
			break;
		case 0xc5:
			svga_vh_rgb24(bitmap, cliprect);
			break;
		case 0xc6:
			popmessage("DAC off");
			break;
	}
	return 0;
}

uint8_t vis_vga_device::visvgamem_r(offs_t offset)
{
	if(!(vga.sequencer.data[0x25] & 0x40))
		return mem_r(offset);
	u16 win = (vga.sequencer.data[0x1e] & 0x0f) == 3 ? m_wina : m_winb; // this doesn't seem quite right
	return mem_linear_r((offset + (win * 64)) & 0x3ffff);
}

void vis_vga_device::visvgamem_w(offs_t offset, uint8_t data)
{
	if(!(vga.sequencer.data[0x25] & 0x40))
		return mem_w(offset, data);
	return mem_linear_w((offset + (m_wina * 64)) & 0x3ffff, data);
}

u8 vis_vga_device::ramdac_hidden_mask_r(offs_t offset)
{
	if(m_extcnt == 4)
	{
		m_extcnt = 0;
		return m_extreg;
	}

	if (!machine().side_effects_disabled())
		m_extcnt++;

	return vga_device::ramdac_mask_r(offset);
}

void vis_vga_device::ramdac_hidden_mask_w(offs_t offset, u8 data)
{
	if(m_extcnt == 4)
	{
		if((data & 0xc7) != 0xc7)
			m_extreg = data;
		m_extcnt = 0;
		return;
	}

	vga_device::ramdac_mask_w(offset, data);
}

class vis_state : public driver_device
{
public:
	vis_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_pic1(*this, "mb:pic8259_master"),
		m_pic2(*this, "mb:pic8259_slave"),
		m_card(*this, "card"),
		m_pad(*this, "PAD")
		{ }

	void vis(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(update);

private:
	required_device<cpu_device> m_maincpu;
	required_device<pic8259_device> m_pic1;
	required_device<pic8259_device> m_pic2;
	required_device<ds6417_device> m_card;
	required_ioport m_pad;

	uint8_t sysctl_r();
	void sysctl_w(uint8_t data);
	uint8_t unk_r(offs_t offset);
	void unk_w(offs_t offset, uint8_t data);
	uint8_t unk2_r();
	uint8_t memcard_r(offs_t offset);
	void memcard_w(offs_t offset, uint8_t data);
	uint16_t pad_r(offs_t offset);
	void pad_w(offs_t offset, uint16_t data);
	uint8_t unk1_r(offs_t offset);
	void unk1_w(offs_t offset, uint8_t data);
	void io_map(address_map &map) ATTR_COLD;
	void main_map(address_map &map) ATTR_COLD;

	void machine_reset() override ATTR_COLD;

	uint8_t m_sysctl;
	uint8_t m_unkidx;
	uint8_t m_unk[16];
	uint8_t m_unk1[4];
	uint8_t m_cardreg, m_cardval, m_cardcnt;
	uint16_t m_padctl, m_padstat;
	bool m_padsel;
};

void vis_state::machine_reset()
{
	m_sysctl = 0;
	m_padctl = 0;
	m_padsel = false;
}

INPUT_CHANGED_MEMBER(vis_state::update)
{
	m_pic1->ir3_w(ASSERT_LINE);
	m_padstat = 0x80;
	m_padsel = false;
}

//chipset registers?
uint8_t vis_state::unk_r(offs_t offset)
{
	if(offset)
		return m_unk[m_unkidx];
	return 0;
}

void vis_state::unk_w(offs_t offset, uint8_t data)
{
	if(offset)
		m_unk[m_unkidx] = data;
	else
		m_unkidx = data & 0xf;
}

uint8_t vis_state::unk2_r()
{
	return 0x40;
}

uint8_t vis_state::memcard_r(offs_t offset)
{
	if(offset)
	{
		if(m_cardreg & 0x10)
		{
			if(m_cardcnt == 8)
				return 0;
			if(m_cardreg & 8)
			{
				m_card->clock_w(1);
				m_card->clock_w(0);
				m_cardval = (m_cardval >> 1) | (m_card->data_r() ? 0x80 : 0);
			}
			else
			{
				m_card->clock_w(0);
				m_card->data_w(BIT(m_cardval, 0));
				m_card->clock_w(1);
				m_cardval >>= 1;
			}
			m_cardcnt++;
			return 0x80;
		}
	}
	else
	{
		m_cardcnt = 0;
		return m_cardval;
	}
	return 0;
}

void vis_state::memcard_w(offs_t offset, uint8_t data)
{
	if(offset)
	{
		if(!(data & 0x10) && !(m_cardreg & 0x10))
		{
			m_card->data_w(BIT(data, 1));
			m_card->clock_w(BIT(data, 0));
			m_card->reset_w(!BIT(data, 2));
		}
		m_cardreg = data;
		m_cardcnt = data & 8 ? 0 : 8;
	}
	else
	{
		m_cardcnt = 0;
		m_cardval = data;
	}
}

uint16_t vis_state::pad_r(offs_t offset)
{
	uint16_t ret = 0;
	switch(offset)
	{
		case 0:
			if(!m_padsel)
			{
				ret = m_pad->read();
				m_padstat = 0;
				m_padsel = true;
			}
			else
			{
				ret = 0x400; // this is probably for the second controller
				m_padsel = false;
			}
			m_pic1->ir3_w(CLEAR_LINE);
			break;
		case 1:
			ret = m_padstat;
	}
	return ret;
}

void vis_state::pad_w(offs_t offset, uint16_t data)
{
	switch(offset)
	{
		case 1:
			m_padctl = data;
			break;
	}
}

uint8_t vis_state::unk1_r(offs_t offset)
{
	if(offset == 2)
		return 0xde;
	return 0;
}

void vis_state::unk1_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
		case 1:
			if(data == 0x10)
				m_pic2->ir1_w(CLEAR_LINE);
			else if(data == 0x16)
				m_pic2->ir1_w(ASSERT_LINE);
	}
	m_unk1[offset] = data;
}

uint8_t vis_state::sysctl_r()
{
	return m_sysctl;
}

void vis_state::sysctl_w(uint8_t data)
{
	if(BIT(data, 0) && !BIT(m_sysctl, 0))
		m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
	//m_maincpu->set_input_line(INPUT_LINE_A20, BIT(data, 1) ? CLEAR_LINE : ASSERT_LINE);
	m_sysctl = data;
}

void vis_state::main_map(address_map &map)
{
	map.unmap_value_high();
	map(0x000000, 0x09ffff).ram();
	map(0x0d8000, 0x0fffff).rom().region("bios", 0xd8000);
	map(0x100000, 0x15ffff).ram();
	map(0x300000, 0x3fffff).rom().region("bios", 0);
	map(0xff0000, 0xffffff).rom().region("bios", 0xf0000);
}

void vis_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x001f).rw("mb:dma8237_1", FUNC(am9517a_device::read), FUNC(am9517a_device::write));
	map(0x0020, 0x003f).rw(m_pic1, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x0026, 0x0027).rw(FUNC(vis_state::unk_r), FUNC(vis_state::unk_w));
	map(0x0040, 0x005f).rw("mb:pit8254", FUNC(pit8254_device::read), FUNC(pit8254_device::write));
	map(0x0060, 0x0065).rw("kbdc", FUNC(kbdc8042_device::data_r), FUNC(kbdc8042_device::data_w));
	map(0x0061, 0x0061).rw("mb", FUNC(at_mb_device::portb_r), FUNC(at_mb_device::portb_w));
	map(0x006a, 0x006a).r(FUNC(vis_state::unk2_r));
	map(0x0080, 0x009f).rw("mb", FUNC(at_mb_device::page8_r), FUNC(at_mb_device::page8_w));
	map(0x0092, 0x0092).rw(FUNC(vis_state::sysctl_r), FUNC(vis_state::sysctl_w));
	map(0x00a0, 0x00bf).rw(m_pic2, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x00c0, 0x00df).rw("mb:dma8237_2", FUNC(am9517a_device::read), FUNC(am9517a_device::write)).umask16(0x00ff);
	map(0x00e0, 0x00e1).noprw();
	map(0x023c, 0x023f).rw(FUNC(vis_state::unk1_r), FUNC(vis_state::unk1_w));
	map(0x0268, 0x026f).rw(FUNC(vis_state::pad_r), FUNC(vis_state::pad_w));
	map(0x0318, 0x031a).rw(FUNC(vis_state::memcard_r), FUNC(vis_state::memcard_w)).umask16(0x00ff);
}

static void vis_cards(device_slot_interface &device)
{
	device.option_add("visaudio", VIS_AUDIO);
	device.option_add("visvga", VIS_VGA);
}

// TODO: other buttons
static INPUT_PORTS_START(vis)
	PORT_START("PAD")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vis_state::update), 0)
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vis_state::update), 0)
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("A") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vis_state::update), 0)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("2") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vis_state::update), 0)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("4") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vis_state::update), 0)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("B") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vis_state::update), 0)
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("3") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vis_state::update), 0)
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vis_state::update), 0)
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vis_state::update), 0)
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vis_state::update), 0)
INPUT_PORTS_END

void vis_state::vis(machine_config &config)
{
	/* basic machine hardware */
	i80286_cpu_device &maincpu(I80286(config, "maincpu", XTAL(12'000'000)));
	maincpu.set_addrmap(AS_PROGRAM, &vis_state::main_map);
	maincpu.set_addrmap(AS_IO, &vis_state::io_map);
	maincpu.shutdown_callback().set("mb", FUNC(at_mb_device::shutdown));
	maincpu.set_irq_acknowledge_callback("mb:pic8259_master", FUNC(pic8259_device::inta_cb));

	AT_MB(config, "mb");
	// the vis doesn't have a real keyboard controller
	config.device_remove("mb:keybc");

	kbdc8042_device &kbdc(KBDC8042(config, "kbdc"));
	kbdc.set_keyboard_type(kbdc8042_device::KBDC8042_STANDARD);
	kbdc.system_reset_callback().set_inputline("maincpu", INPUT_LINE_RESET);
	kbdc.gate_a20_callback().set_inputline("maincpu", INPUT_LINE_A20);
	kbdc.input_buffer_full_callback().set("mb:pic8259_master", FUNC(pic8259_device::ir1_w));

	// FIXME: determine ISA bus clock
	ISA16_SLOT(config, "mcd",      0, "mb:isabus", pc_isa16_cards, "mcd",      true);
	ISA16_SLOT(config, "visaudio", 0, "mb:isabus", vis_cards,      "visaudio", true);
	ISA16_SLOT(config, "visvga",   0, "mb:isabus", vis_cards,      "visvga",   true);

	SOFTWARE_LIST(config, "cd_list").set_original("vis");

	DS6417(config, m_card, 0);
}

ROM_START(vis)
	ROM_REGION16_LE(0x100000,"bios", 0)
	ROM_LOAD( "p513bk0b.bin", 0x00000, 0x80000, CRC(364e3f74) SHA1(04260ef1e65e482c9c49d25ace40e22487d6aab9))
	ROM_LOAD( "p513bk1b.bin", 0x80000, 0x80000, CRC(e18239c4) SHA1(a0262109e10a07a11eca43371be9978fff060bc5))
ROM_END

COMP( 1992, vis, 0, 0, vis, vis, vis_state, empty_init, "Tandy/Memorex", "Video Information System MD-2500", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )



vixen.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder, Robbbert
/*

Osborne 4 Vixen

Main PCB Layout
---------------

TODO

Notes:
    Relevant IC's shown.

    CPU     - Zilog Z8400APS Z80A CPU
    FDC     - SMC FDC1797
    8155    - Intel P8155H
    ROM0    -
    ROM1,2  - AMD AM2732-1DC 4Kx8 EPROM
    CN1     - keyboard connector
    CN2     -
    CN3     -
    CN4     - floppy connector
    CN5     - power connector
    CN6     - composite video connector
    SW1     - reset switch
    SW2     -


I/O PCB Layout
--------------

TODO

Notes:
    Relevant IC's shown.

    8155    - Intel P8155H
    8251    - AMD P8251A
    CN1     - IEEE488 connector
    CN2     - RS232 connector
    CN3     -

*/

/*

    TODO:

    - RS232 RI interrupt
    - PCB layouts

*/


#include "emu.h"

#include "bus/ieee488/ieee488.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/i8155.h"
#include "machine/i8251.h"
#include "machine/ram.h"
#include "machine/timer.h"
#include "machine/wd_fdc.h"
#include "sound/discrete.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "utf8.h"


namespace {

#define Z8400A_TAG      "5f"
#define FDC1797_TAG     "5n"
#define P8155H_TAG      "2n"
#define SCREEN_TAG      "screen"

class vixen_state : public driver_device
{
public:
	vixen_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, Z8400A_TAG)
		, m_fdc(*this, FDC1797_TAG)
		, m_io_i8155(*this, "c7")
		, m_usart(*this, "c3")
		, m_discrete(*this, "discrete")
		, m_ieee488(*this, IEEE488_TAG)
		, m_palette(*this, "palette")
		, m_ram(*this, RAM_TAG)
		, m_floppy(*this, FDC1797_TAG":%u", 0U)
		, m_rs232(*this, "rs232")
		, m_rom(*this, Z8400A_TAG)
		, m_sync_rom(*this, "video")
		, m_char_rom(*this, "chargen")
		, m_video_ram(*this, "video_ram")
		, m_key(*this, "KEY.%u", 0U)
	{ }

	void vixen(machine_config &config);

	void init_vixen();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	uint8_t status_r();
	void cmd_w(uint8_t data);
	uint8_t ieee488_r();
	uint8_t port3_r();
	uint8_t i8155_pa_r();
	void i8155_pb_w(uint8_t data);
	void i8155_pc_w(uint8_t data);
	void io_i8155_pb_w(uint8_t data);
	void io_i8155_pc_w(uint8_t data);
	void io_i8155_to_w(int state);
	void srq_w(int state);
	void atn_w(int state);
	void rxrdy_w(int state);
	void txrdy_w(int state);
	void fdc_intrq_w(int state);
	TIMER_DEVICE_CALLBACK_MEMBER(vsync_tick);
	IRQ_CALLBACK_MEMBER(vixen_int_ack);
	uint8_t opram_r(offs_t offset);
	uint8_t oprom_r(offs_t offset);
	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void bios_mem(address_map &map) ATTR_COLD;
	void vixen_io(address_map &map) ATTR_COLD;
	void vixen_mem(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<fd1797_device> m_fdc;
	required_device<i8155_device> m_io_i8155;
	required_device<i8251_device> m_usart;
	required_device<discrete_sound_device> m_discrete;
	required_device<ieee488_device> m_ieee488;
	required_device<palette_device> m_palette;
	required_device<ram_device> m_ram;
	required_device_array<floppy_connector, 2> m_floppy;
	required_device<rs232_port_device> m_rs232;
	required_region_ptr<uint8_t> m_rom;
	required_region_ptr<uint8_t> m_sync_rom;
	required_region_ptr<uint8_t> m_char_rom;
	required_shared_ptr<uint8_t> m_video_ram;
	required_ioport_array<8> m_key;

	address_space *m_program = nullptr;

	void update_interrupt();

	// keyboard state
	uint8_t m_col = 0U;

	// interrupt state
	int m_cmd_d0 = 0;
	int m_cmd_d1 = 0;

	bool m_fdint = false;
	int m_vsync = 0;

	int m_srq = 1;
	int m_atn = 1;
	int m_enb_srq_int = 0;
	int m_enb_atn_int = 0;

	int m_rxrdy = 0;
	int m_txrdy = 0;
	int m_int_clk = 0;
	int m_enb_xmt_int = 0;
	int m_enb_rcv_int = 0;
	int m_enb_ring_int = 0;

	// video state
	bool m_alt = false;
	bool m_256 = false;
};



//**************************************************************************
//  INTERRUPTS
//**************************************************************************

void vixen_state::update_interrupt()
{
	int state = (m_cmd_d1 && m_fdint) || m_vsync;// || (!m_enb_srq_int && !m_srq) || (!m_enb_atn_int && !m_atn) || (!m_enb_xmt_int && m_txrdy) || (!m_enb_rcv_int && m_rxrdy);

	m_maincpu->set_input_line(INPUT_LINE_IRQ0, state ? ASSERT_LINE : CLEAR_LINE);
}


uint8_t vixen_state::opram_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		membank("bank3")->set_entry(0); // read videoram
	return m_program->read_byte(offset);
}

uint8_t vixen_state::oprom_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		membank("bank3")->set_entry(1); // read rom
	return m_rom[offset];
}

uint8_t vixen_state::status_r()
{
	/*

	    bit     description

	    0       VSYNC enable
	    1       FDINT enable
	    2       VSYNC
	    3       1
	    4       1
	    5       1
	    6       1
	    7       1

	*/

	uint8_t data = 0xf8;

	// vertical sync interrupt enable
	data |= m_cmd_d0;

	// floppy interrupt enable
	data |= m_cmd_d1 << 1;

	// vertical sync
	data |= m_vsync << 2;

	return data;
}

void vixen_state::cmd_w(uint8_t data)
{
	/*

	    bit     description

	    0       VSYNC enable
	    1       FDINT enable
	    2
	    3
	    4
	    5
	    6
	    7

	*/

//  logerror("CMD %u\n", data);

	// vertical sync interrupt enable
	m_cmd_d0 = BIT(data, 0);

	if (!m_cmd_d0)
	{
		// clear vertical sync
		m_vsync = 0;
	}

	// floppy interrupt enable
	m_cmd_d1 = BIT(data, 1);

	update_interrupt();
}

uint8_t vixen_state::ieee488_r()
{
	/*

	    bit     description

	    0       ATN
	    1       DAV
	    2       NDAC
	    3       NRFD
	    4       EOI
	    5       SRQ
	    6       IFC
	    7       REN

	*/

	uint8_t data = 0;

	/* attention */
	data |= m_ieee488->atn_r();

	/* data valid */
	data |= m_ieee488->dav_r() << 1;

	/* data not accepted */
	data |= m_ieee488->ndac_r() << 2;

	/* not ready for data */
	data |= m_ieee488->nrfd_r() << 3;

	/* end or identify */
	data |= m_ieee488->eoi_r() << 4;

	/* service request */
	data |= m_ieee488->srq_r() << 5;

	/* interface clear */
	data |= m_ieee488->ifc_r() << 6;

	/* remote enable */
	data |= m_ieee488->ren_r() << 7;

	return data;
}


//-------------------------------------------------
//  port3_r - serial status read
//-------------------------------------------------

uint8_t vixen_state::port3_r()
{
	/*

	    bit     description

	    0       RI
	    1       DCD
	    2       1
	    3       1
	    4       1
	    5       1
	    6       1
	    7       1

	*/

	uint8_t data = 0xfc;

	// ring indicator
	data |= m_rs232->ri_r();

	// data carrier detect
	data |= m_rs232->dcd_r() << 1;

	return data;
}



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

// when M1 is inactive: read and write of data
void vixen_state::vixen_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xefff).ram();
	map(0xf000, 0xffff).bankr("bank3").bankw("bank4").share("video_ram");
}

// when M1 is active: read opcodes
void vixen_state::bios_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xefff).r(FUNC(vixen_state::opram_r));
	map(0xf000, 0xffff).r(FUNC(vixen_state::oprom_r));
}

void vixen_state::vixen_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x03).rw(m_fdc, FUNC(fd1797_device::read), FUNC(fd1797_device::write));
	map(0x04, 0x04).mirror(0x03).rw(FUNC(vixen_state::status_r), FUNC(vixen_state::cmd_w));
	map(0x08, 0x08).mirror(0x01).rw(P8155H_TAG, FUNC(i8155_device::data_r), FUNC(i8155_device::data_w));
	map(0x0c, 0x0d).w(P8155H_TAG, FUNC(i8155_device::ale_w));
	map(0x10, 0x10).mirror(0x07).r(m_ieee488, FUNC(ieee488_device::dio_r));
	map(0x18, 0x18).mirror(0x07).r(FUNC(vixen_state::ieee488_r));
	map(0x20, 0x21).mirror(0x04).w(m_io_i8155, FUNC(i8155_device::ale_w));
	map(0x28, 0x28).mirror(0x05).rw(m_io_i8155, FUNC(i8155_device::data_r), FUNC(i8155_device::data_w));
	map(0x30, 0x31).mirror(0x06).rw(m_usart, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x38, 0x38).mirror(0x07).r(FUNC(vixen_state::port3_r));
//  map(0xf0, 0xff) Hard Disk?
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

INPUT_PORTS_START( vixen )
	PORT_START("KEY.0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(0x1B)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TAB) PORT_CHAR(0x09)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(0x0D)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(0x27) PORT_CHAR(0x22)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR(']')

	PORT_START("KEY.1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')

	PORT_START("KEY.2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') PORT_CHAR(0x11)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR(0x17)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(0x05)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR(0x12)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(0x14)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR(0x19)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(0x15)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') PORT_CHAR(0x09)

	PORT_START("KEY.3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR(0x01)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR(0x13)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR(0x04)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR(0x06)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR(0x07)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') PORT_CHAR(0x08)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') PORT_CHAR(0x0a)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR(0x0b)

	PORT_START("KEY.4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(0x1a)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(0x18)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR(0x03)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR(0x16)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR(0x02)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(0x0e)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') PORT_CHAR(0x0d)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("KEY.5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8, UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR(0x10)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(0x0f)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')

	PORT_START("KEY.6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("- _") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_') PORT_CHAR(0x1F)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?') PORT_CHAR(0x7E)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\\ |") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|') PORT_CHAR(0x1C)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+') PORT_CHAR(0x60)

	PORT_START("KEY.7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL) PORT_CHAR(127)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('{') PORT_CHAR('}')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("FUNC") PORT_CODE(KEYCODE_END)
INPUT_PORTS_END



//**************************************************************************
//  VIDEO
//**************************************************************************

TIMER_DEVICE_CALLBACK_MEMBER(vixen_state::vsync_tick)
{
	if (m_cmd_d0)
	{
		m_vsync = 1;
		update_interrupt();
	}
}

uint32_t vixen_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const pen_t *pen = m_palette->pens();
	uint8_t x, y, chr, gfx, inv, ra;

	for (y = 0; y < 26; y++)
	{
		for (ra = 0; ra < 10; ra++)
		{
			for (x = 0; x < 128; x++)
			{
				uint16_t sync_addr = ((y+1) << 7) + x + 1; // it's out by a row and a column
				uint8_t sync_data = m_sync_rom[sync_addr & 0xfff];
				bool blank = BIT(sync_data, 4);
				/*
				int clrchadr = BIT(sync_data, 7);
				int hsync = BIT(sync_data, 6);
				int clrtxadr = BIT(sync_data, 5);
				int vsync = BIT(sync_data, 3);
				int comp_sync = BIT(sync_data, 2);

				logerror("SYNC %03x:%02x TXADR %u SCAN %u CHADR %u : COMPSYNC %u VSYNC %u BLANK %u CLRTXADR %u HSYNC %u CLRCHADR %u\n",
				    sync_addr,sync_data,txadr,scan,chadr,comp_sync,vsync,blank,clrtxadr,hsync,clrchadr);
				*/

				chr = m_video_ram[(y<<7) + x];

				if (m_256)
				{
					gfx = m_char_rom[(BIT(chr, 7) << 11) | (ra << 7) | (chr & 0x7f)];
					inv = m_alt ? 0xff : 0;
				}
				else
				{
					gfx = m_char_rom[(ra << 7) | (chr & 0x7f)];
					inv = BIT(chr, 7) ? 0xff : 0;
				}

				gfx = (blank) ? 0 : (gfx ^ inv);

				for (int b = 0; b < 8; b++)
				{
					int color = BIT(gfx, 7 - b);

					bitmap.pix((y * 10) + ra, (x * 8) + b) = pen[color];
				}
			}
		}
	}

	return 0;
}



//**************************************************************************
//  SOUND
//**************************************************************************

static DISCRETE_SOUND_START( vixen_discrete )
	DISCRETE_INPUT_LOGIC(NODE_01)
	DISCRETE_SQUAREWAVE(NODE_02, NODE_01, (23.9616_MHz_XTAL / 15360).dvalue(), 100, 50, 0, 90)
	DISCRETE_OUTPUT(NODE_02, 2000)
DISCRETE_SOUND_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  I8155 interface
//-------------------------------------------------

uint8_t vixen_state::i8155_pa_r()
{
	uint8_t data = 0xff;

	for (int i = 0; i < 8; i++)
		if (!BIT(m_col, i)) data &= m_key[i]->read();

	return data;
}

void vixen_state::i8155_pb_w(uint8_t data)
{
	m_col = data;
}

void vixen_state::i8155_pc_w(uint8_t data)
{
	/*

	    bit     description

	    0       DSEL1/
	    1       DSEL2/
	    2       DDEN/
	    3       ALT CHARSET/
	    4       256 CHARS
	    5       BEEP ENABLE
	    6
	    7

	*/

	// drive select
	floppy_image_device *floppy = nullptr;

	if (!BIT(data, 0)) floppy = m_floppy[0]->get_device();
	if (!BIT(data, 1)) floppy = m_floppy[1]->get_device();

	m_fdc->set_floppy(floppy);

	if (floppy) floppy->mon_w(0);

	// density select
	m_fdc->dden_w(BIT(data, 2));

	// charset
	m_alt = !BIT(data, 3);
	m_256 = !BIT(data, 4);

	// beep enable
	m_discrete->write(NODE_01, !BIT(data, 5));
}

//-------------------------------------------------
//  I8155 IO interface
//-------------------------------------------------

void vixen_state::io_i8155_pb_w(uint8_t data)
{
	/*

	    bit     description

	    PB0     ATN
	    PB1     DAV
	    PB2     NDAC
	    PB3     NRFD
	    PB4     EOI
	    PB5     SRQ
	    PB6     IFC
	    PB7     REN

	*/

	/* data valid */
	m_ieee488->host_atn_w(BIT(data, 0));

	/* end or identify */
	m_ieee488->host_dav_w(BIT(data, 1));

	/* remote enable */
	m_ieee488->host_ndac_w(BIT(data, 2));

	/* attention */
	m_ieee488->host_nrfd_w(BIT(data, 3));

	/* interface clear */
	m_ieee488->host_eoi_w(BIT(data, 4));

	/* service request */
	m_ieee488->host_srq_w(BIT(data, 5));

	/* not ready for data */
	m_ieee488->host_ifc_w(BIT(data, 6));

	/* data not accepted */
	m_ieee488->host_ren_w(BIT(data, 7));
}

void vixen_state::io_i8155_pc_w(uint8_t data)
{
	/*

	    bit     description

	    PC0     select internal clock
	    PC1     ENB RING INT
	    PC2     ENB RCV INT
	    PC3     ENB XMT INT
	    PC4     ENB ATN INT
	    PC5     ENB SRQ INT
	    PC6
	    PC7

	*/

	m_int_clk = BIT(data, 0);
	m_enb_ring_int = BIT(data, 1);
	m_enb_rcv_int = BIT(data, 2);
	m_enb_xmt_int = BIT(data, 3);
	m_enb_atn_int = BIT(data, 4);
	m_enb_srq_int = BIT(data, 5);
}

void vixen_state::io_i8155_to_w(int state)
{
	if (m_int_clk)
	{
		m_usart->write_txc(state);
		m_usart->write_rxc(state);
	}
}

//-------------------------------------------------
//  i8251_interface usart_intf
//-------------------------------------------------

void vixen_state::rxrdy_w(int state)
{
	m_rxrdy = state;
	update_interrupt();
}

void vixen_state::txrdy_w(int state)
{
	m_txrdy = state;
	update_interrupt();
}

//-------------------------------------------------
//  IEEE488 interface
//-------------------------------------------------

void vixen_state::srq_w(int state)
{
	m_srq = state;
	update_interrupt();
}

void vixen_state::atn_w(int state)
{
	m_atn = state;
	update_interrupt();
}

static void vixen_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

void vixen_state::fdc_intrq_w(int state)
{
	m_fdint = state;
	update_interrupt();
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

IRQ_CALLBACK_MEMBER(vixen_state::vixen_int_ack)
{
	// D0 is pulled low
	return 0xfe;
}

void vixen_state::machine_start()
{
	// configure memory banking

	membank("bank3")->configure_entry(0, m_video_ram);
	membank("bank3")->configure_entry(1, m_rom);

	membank("bank4")->configure_entry(0, m_video_ram);

	// register for state saving
	save_item(NAME(m_col));
	save_item(NAME(m_cmd_d0));
	save_item(NAME(m_cmd_d1));
	save_item(NAME(m_fdint));
	save_item(NAME(m_alt));
	save_item(NAME(m_256));
	save_item(NAME(m_vsync));
	save_item(NAME(m_srq));
	save_item(NAME(m_atn));
	save_item(NAME(m_enb_srq_int));
	save_item(NAME(m_enb_atn_int));
	save_item(NAME(m_rxrdy));
	save_item(NAME(m_txrdy));
	save_item(NAME(m_int_clk));
	save_item(NAME(m_enb_xmt_int));
	save_item(NAME(m_enb_rcv_int));
	save_item(NAME(m_enb_ring_int));

}

void vixen_state::machine_reset()
{
	membank("bank3")->set_entry(1);

	m_vsync = 0;
	m_cmd_d0 = 0;
	m_cmd_d1 = 0;
	update_interrupt();

	m_fdc->reset();
	m_io_i8155->reset();
	m_usart->reset();
	m_maincpu->set_state_int(Z80_PC, 0xf000);
}



//**************************************************************************
//  MACHINE CONFIGURATION
//**************************************************************************

void vixen_state::vixen(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 23.9616_MHz_XTAL / 6);
	m_maincpu->set_addrmap(AS_PROGRAM, &vixen_state::vixen_mem);
	m_maincpu->set_addrmap(AS_OPCODES, &vixen_state::bios_mem);
	m_maincpu->set_addrmap(AS_IO, &vixen_state::vixen_io);
	m_maincpu->set_irq_acknowledge_callback(FUNC(vixen_state::vixen_int_ack));

	// video hardware
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::amber());
	screen.set_screen_update(FUNC(vixen_state::screen_update));
	screen.set_raw(23.9616_MHz_XTAL / 2, 96*8, 0*8, 81*8, 27*10, 0*10, 26*10);

	TIMER(config, "vsync").configure_scanline(FUNC(vixen_state::vsync_tick), SCREEN_TAG, 26*10, 27*10);

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	DISCRETE(config, m_discrete, vixen_discrete).add_route(ALL_OUTPUTS, "mono", 0.20);

	// devices
	i8155_device &i8155(I8155(config, P8155H_TAG, 23.9616_MHz_XTAL / 6));
	i8155.in_pa_callback().set(FUNC(vixen_state::i8155_pa_r));
	i8155.out_pb_callback().set(FUNC(vixen_state::i8155_pb_w));
	i8155.out_pc_callback().set(FUNC(vixen_state::i8155_pc_w));

	I8155(config, m_io_i8155, 23.9616_MHz_XTAL / 6);
	m_io_i8155->out_pa_callback().set(m_ieee488, FUNC(ieee488_device::host_dio_w));
	m_io_i8155->out_pb_callback().set(FUNC(vixen_state::io_i8155_pb_w));
	m_io_i8155->out_pc_callback().set(FUNC(vixen_state::io_i8155_pc_w));
	m_io_i8155->out_to_callback().set(FUNC(vixen_state::io_i8155_to_w));

	I8251(config, m_usart, 0);
	m_usart->txd_handler().set(m_rs232, FUNC(rs232_port_device::write_txd));
	m_usart->dtr_handler().set(m_rs232, FUNC(rs232_port_device::write_dtr));
	m_usart->rts_handler().set(m_rs232, FUNC(rs232_port_device::write_rts));
	m_usart->rxrdy_handler().set(FUNC(vixen_state::rxrdy_w));
	m_usart->txrdy_handler().set(FUNC(vixen_state::txrdy_w));

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set(m_usart, FUNC(i8251_device::write_rxd));
	m_rs232->dsr_handler().set(m_usart, FUNC(i8251_device::write_dsr));

	FD1797(config, m_fdc, 23.9616_MHz_XTAL / 24);
	m_fdc->intrq_wr_callback().set(FUNC(vixen_state::fdc_intrq_w));
	FLOPPY_CONNECTOR(config, m_floppy[0], vixen_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], vixen_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	IEEE488(config, m_ieee488);
	m_ieee488->srq_callback().set(FUNC(vixen_state::srq_w));
	m_ieee488->atn_callback().set(FUNC(vixen_state::atn_w));

	/* software lists */
	SOFTWARE_LIST(config, "disk_list").set_original("vixen");

	// internal ram
	RAM(config, RAM_TAG).set_default_size("64K");
}



//**************************************************************************
//  ROMS
//**************************************************************************

ROM_START( vixen )
	ROM_REGION( 0x1000, Z8400A_TAG, 0 )
	ROM_LOAD( "osborne 4 mon rom v1.04 3p40082-03 a0a9.4c", 0x0000, 0x1000, CRC(5f1038ce) SHA1(e6809fac23650bbb4689e58edc768d917d80a2df) ) // OSBORNE 4 MON ROM / V1.04  3P40082-03 / A0A9 (c) OCC 1985

	ROM_REGION( 0x1000, "video", 0 )
	ROM_LOAD( "v1.10.3j", 0x0000, 0x1000, CRC(1f93e2d7) SHA1(0c479bfd3ac8d9959c285c020d0096930a9c6867) )

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "v1.00 l.1j", 0x0000, 0x1000, CRC(f97c50d9) SHA1(39f73afad68508c4b8a4d241c064f9978098d8f2) )
ROM_END



//**************************************************************************
//  DRIVER INITIALIZATION
//**************************************************************************

void vixen_state::init_vixen()
{
	m_program = &m_maincpu->space(AS_PROGRAM);
}


} // anonymous namespace



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT   CLASS        INIT        COMPANY    FULLNAME  FLAGS
COMP( 1984, vixen, 0,       0,     vixen,   vixen,  vixen_state, init_vixen, "Osborne", "Vixen",  MACHINE_SUPPORTS_SAVE )



vk100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Jonathan Gevaryahu
/***************************************************************************

        DEC VK100 'GIGI'

        12/05/2009 Skeleton driver.
        28/07/2009 added Guru-readme(TM)
        08/01/2012 Fleshed out driver.

        Todo:
              * fix vector generator hardware enough to pass the startup self test
                the tests are described on page 6-5 thru 6-8 of the tech reference
                * hook up the bresenham DU/DVM/ERR stuff, currently only simple directional vectors work
                * hook up the vector and sync proms to the sync counter
                * figure out how the erase prom actually works at a hardware level
                * redump the vector prom, the first two bytes look bad
              * figure out the correct meaning of systat b register - needed for communications selftest
              * hook up smc com5016t baud generator to i8251 rx and tx clocks - begun

        Notes:
              The directions for the DIR value are arranged, starting from the *
              as the vector origin:
                 3  2  1
                  \ | /
                   \|/
                 4--*--0
                   /|\
                  / | \
                 5  6  7

               The X and Y counters are techincally 12 bits long each, though
               only the low 9 and 10 bits respectively are used for ram addressing.
               The MSB bit of each counter does have a special purpose with
               regards to the RAS/ERASE prom though, perhaps to detect an
               underflow 000->FFF

 Tony DiCenzo, now the director of standards and architecture at Oracle, was on the team that developed the VK100
 see http://startup.nmnaturalhistory.org/visitorstories/view.php?ii=79
 Robert "Bob" C. Quinn was definitely lead engineer on the VT125
 Robert "Bob" T. Collins was the lead engineer on the VK100
 Pedro Ortiz (https://www.linkedin.com/pub/pedro-ortiz/16/68b/196) did the drafting for the enclosure and case

 The prototype name for the VK100 was 'SMAKY' (Smart Keyboard)

****************************************************************************/
/*
DEC VK100
DEC, 1982

This is a VK100 terminal, otherwise known as a DEC Gigi graphics terminal.
There's a technical manual dated 1982 here:
http://web.archive.org/web/20091015205827/http://www.computer.museum.uq.edu.au/pdf/EK-VK100-TM-001%20VK100%20Technical%20Manual.pdf
Installation and owner's manual is at:
http://www.bitsavers.org/pdf/dec/terminal/gigi/EK-VK100-IN-002_GIGI_Terminal_Installation_and_Owners_Manual_Apr81.pdf
An enormous amount of useful info can be derived from the VT125 technical manual:
http://www.bitsavers.org/pdf/dec/terminal/vt100/EK-VT100-TM-003_VT100_Technical_Manual_Jul82.pdf starting on page 6-70, pdf page 316
And its schematics:
http://bitsavers.org/pdf/dec/terminal/vt125/MP01053_VT125_Mar82.pdf

PCB Layout
----------

VK100 LOGICBOARD
    |-------|    |---------|  |---------|    |-| |-| |-|  |-|
|---|-20 mA-|----|---EIA---|--|HARD-COPY|----|B|-|G|-|R|--|-|--DSW(8)--|
|                                                         BW           |
|                                                             POWER    |
|                 PR2                                                  |
|                           HD46505SP              4116 4116 4116 4116 |
|                                                                      |
|                                                  4116 4116 4116 4116 |
|      PR5        INTEL           ROM1                                 |
|         PR1 PR6 P8251A                           4116 4116 4116 4116 |
|                     45.6192MHz  ROM2  PR3                            |
|                                                  4116 4116 4116 4116 |
| 4116 4116 4116  INTEL           ROM3                                 |
|                 D8202A                                               |
| 4116 4116 4116       5.0688MHz  ROM4                       PR4       |
|                                                                      |
| 4116 4116       INTEL    SMC_5016T                            PIEZO  |
|                 D8085A                        IDC40   LM556   75452  |
|----------------------------------------------------------------------|
Notes:
      ROM1 - TP-01 (C) DEC 23-031E4-00 (M) SCM91276L 8114
      ROM2 - TP-01 (C) DEC 1980 23-017E4-00 MOSTEK MK36444N 8116
      ROM3 - TP-01 (C) MICROSOFT 1979 23-018E4-00 MOSTEK MK36445N 8113
      ROM4 - TP-01 (C) DEC 1980 23-190E2-00 P8316E AMD 35517 8117DPP

    LED meanings:
    The LEDS on the vk100 (there are 7) are set up above the keyboard as:
    Label: ON LINE   LOCAL     NO SCROLL BASIC     HARD-COPY L1        L2
    Bit:   !d5       d5        !d4       !d3       !d2       !d1       !d0 (of port 0x68)
according to manual from http://www.bitsavers.org/pdf/dec/terminal/gigi/EK-VK100-IN-002_GIGI_Terminal_Installation_and_Owners_Manual_Apr81.pdf
where X = on, 0 = off, ? = variable (- = off)
    - X 0 0 0 0 0 (0x1F) = Microprocessor error
    X - 0 X X X X (0x30) "

    - X 0 0 0 0 X (0x1E) = ROM error
    X - 0 0 ? ? ? (0x3x) "
1E 3F = rom error, rom 1 (0000-0fff)
1E 3E = rom error, rom 1 (1000-1fff)
1E 3D = rom error, rom 2 (2000-2fff)
1E 3C = rom error, rom 2 (3000-3fff)
1E 3B = rom error, rom 3 (4000-4fff)
1E 3A = rom error, rom 3 (5000-5fff)
1E 39 = rom error, rom 4 (6000-6fff)

    - X 0 0 0 X 0 (0x1D) = RAM error
    X - 0 ? ? ? ? (0x3x) "

    - X 0 0 0 X X (0x1C) = CRT Controller error
    X - 0 X X X X (0x30) "
This test writes 0xF to port 00 (crtc address reg) and writes a pattern to it
via port 01 (crtc data reg) then reads it back and checks to be sure the data
matches.

    - X 0 0 X 0 0 (0x1B) = CRT Controller time-out
    X - 0 X X X X (0x30) "
This test writes 00 to all the crtc registers and checks to be sure an rst7.5
(vblank) interrupt fires on the 8085 within a certain time period.

    - X 0 0 X 0 X (0x1A) = Vector time-out error
    X - 0 X X X X (0x30) "
Not sure exactly what this tests, likely tries firing the vector generator
state machine and sees if the GO bit ever finishes and goes back to 0
*/
#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/i8085/i8085.h"
#include "sound/beep.h"
#include "video/mc6845.h"
#include "machine/com8116.h"
#include "machine/i8251.h"

#include "screen.h"
#include "speaker.h"

#include "vk100.lh"


namespace {

// named timer IDs
#define TID_I8251_RX 1
#define TID_I8251_TX 2
#define TID_SYNC 3

// show messages related to writes to the 0x4x VG registers
#undef VG40_VERBOSE
// show messages related to writes to the 0x6x VG registers
#undef VG60_VERBOSE
// show messages related to LED/beeper writes
#undef LED_VERBOSE
// show messages related to KYBD writes
#undef KBD_VERBOSE
// debug the pattern reg
#undef PAT_DEBUG
// show reads from the two systat registers
#undef SYSTAT_A_VERBOSE
#undef SYSTAT_B_VERBOSE

// debug state dump for the vector generator
#undef DEBUG_VG_STATE

#define RS232_TAG       "rs232"
#define COM5016T_TAG    "com5016t"

class vk100_state : public driver_device
{
public:
	vk100_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_crtc(*this, "crtc"),
		m_speaker(*this, "beeper"),
		m_uart(*this, "i8251"),

		m_capsshift(*this, "CAPSSHIFT"),
		m_dipsw(*this, "SWITCHES"),
		m_online_led(*this, "online_led"),
		m_local_led(*this, "local_led"),
		m_noscroll_led(*this, "noscroll_led"),
		m_basic_led(*this, "basic_led"),
		m_hardcopy_led(*this, "hardcopy_led"),
		m_l1_led(*this, "l1_led"),
		m_l2_led(*this, "l2_led"),
		m_vg_timer(nullptr),
		m_col_array(*this, "COL%X", 0U)
		//m_i8251_rx_timer(nullptr),
		//m_i8251_tx_timer(nullptr),
		//m_i8251_sync_timer(nullptr)
	{
	}

	void vk100(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

	void vk100_mem(address_map &map) ATTR_COLD;
	void vk100_io(address_map &map) ATTR_COLD;

	void vgLD_X(offs_t offset, uint8_t data);
	void vgLD_Y(offs_t offset, uint8_t data);
	void vgERR(uint8_t data);
	void vgSOPS(uint8_t data);
	void vgPAT(uint8_t data);
	void vgPMUL(uint8_t data);
	void vgREG(offs_t offset, uint8_t data);
	void vgEX(offs_t offset, uint8_t data);
	void KBDW(uint8_t data);
	uint8_t vk100_keyboard_column_r(offs_t offset);
	uint8_t SYSTAT_A(offs_t offset);
	uint8_t SYSTAT_B();

	TIMER_CALLBACK_MEMBER(execute_vg);
	void crtc_vsync(int state);
	void i8251_rxrdy_int(int state);
	void i8251_txrdy_int(int state);
	[[maybe_unused]] void i8251_rts(int state);
	uint8_t vram_read();
	uint8_t vram_attr_read();
	MC6845_UPDATE_ROW(crtc_update_row);
	void vram_write(uint8_t data);

	required_device<cpu_device> m_maincpu;
	required_device<mc6845_device> m_crtc;
	required_device<beep_device> m_speaker;
	required_device<i8251_device> m_uart;

	required_ioport m_capsshift;
	required_ioport m_dipsw;

	output_finder<> m_online_led;
	output_finder<> m_local_led;
	output_finder<> m_noscroll_led;
	output_finder<> m_basic_led;
	output_finder<> m_hardcopy_led;
	output_finder<> m_l1_led;
	output_finder<> m_l2_led;

	emu_timer* m_vg_timer;
	//emu_timer* m_i8251_rx_timer;
	//emu_timer* m_i8251_tx_timer;
	//emu_timer* m_i8251_sync_timer;
	uint8_t* m_vram;
	uint8_t* m_trans;
	uint8_t* m_pattern;
	uint8_t* m_dir;
	uint8_t* m_sync;
	uint8_t* m_vector;
	uint8_t* m_ras_erase;
	uint8_t m_dir_a6; // latched a6 of dir rom
	uint8_t m_cout; // carry out from vgERR adder
	uint8_t m_vsync; // vsync pin of crtc
	uint16_t m_vgX; // 12 bit X value for vector draw position
	uint16_t m_vgY; // 12 bit Y value for vector draw position
	uint16_t m_vgERR; // error register can cause carries which need to be caught
	uint8_t m_vgSOPS;
	uint8_t m_vgPAT;
	uint16_t m_vgPAT_Mask; // current mask for PAT
	uint8_t m_vgPMUL; // reload value for PMUL_Count
	uint8_t m_vgPMUL_Count;
	uint8_t m_vgDownCount; // down counter = number of pixels, loaded from vgDU on execute
#define VG_DU m_vgRegFile[0]
#define VG_DVM m_vgRegFile[1]
#define VG_DIR m_vgRegFile[2]
#define VG_WOPS m_vgRegFile[3]
	uint8_t m_vgRegFile[4];
	uint8_t m_VG_MODE; // 2 bits, latched on EXEC
	uint8_t m_vgGO; // activated on next SYNC pulse after EXEC
	uint8_t m_ACTS;
	uint8_t m_ADSR;
	required_ioport_array<16> m_col_array;
};

// vram access functions:
	/* figure out vram address based on tech manual page 5-24:
	 * real address to 16-bit chunk a13 a12 | a11 a10 a9  a8 | a7  a6  a5  a4 | a3  a2  a1  a0
	 * X+Y input                    Y8  Y7  | Y6  Y5  Y4  Y3 | Y2  Y1  X9' X8'| X7' X6' X5' X4'
	 *          X3' and X2' choose a 4-bit block, X1 and X0 choose a bit within that.
	 * Figure out the ram address in address space "vram" based on this:
	 * vram is 0x8000 long (0x4000 16-bit blocks in sequence)
	 * so:
	 * vram: a14 a13 a12 | a11 a10 a9  a8 | a7  a6  a5  a4 | a3  a2  a1  a0
	 * vg:   Y8  Y7  Y6  | Y5  Y4  Y3  Y2 | Y1  X9' X8' X7'| X6' X5' X4'(x3') x2' x1 x0
	 *     X3' is handled by the nybbleNum statement, as is X2'
	 *     X1 and X0 are handled by the pattern rom directly:
	 *     x0 -> a0, x1 -> a1
	 *     this handles bits like:
	 *     x1 x0
	 *      0  0 -> bit 0
	 *      0  1 -> bit 1
	 *      1  0 -> bit 2
	 *      1  1 -> bit 3
	 */

// returns one nybble from vram array based on X and Y regs
uint8_t vk100_state::vram_read()
{
	// XFinal is (X'&0x3FC)|(X&0x3)
	uint16_t XFinal = m_trans[(m_vgX&0x3FC)>>2]<<2|(m_vgX&0x3); // appears correct
	// EA is the effective ram address for a 16-bit block
	uint16_t EA = ((m_vgY&0x1FE)<<5)|(XFinal>>4); // appears correct
	// block is the 16 bit block directly (note EA has to be <<1 to correctly index a byte)
	uint16_t block = m_vram[(EA<<1)+1] | (m_vram[(EA<<1)]<<8);
	// nybbleNum is which of the four nybbles within the block to address. should NEVER be 3!
	uint8_t nybbleNum = (XFinal&0xC)>>2;
	return (block>>(4*nybbleNum))&0xF;
}

// returns the attribute nybble for the current pixel based on X and Y regs
uint8_t vk100_state::vram_attr_read()
{
	// XFinal is (X'&0x3FC)|(X&0x3)
	uint16_t XFinal = m_trans[(m_vgX&0x3FC)>>2]<<2|(m_vgX&0x3); // appears correct
	// EA is the effective ram address for a 16-bit block
	uint16_t EA = ((m_vgY&0x1FE)<<5)|(XFinal>>4); // appears correct
	// block is the 16 bit block directly (note EA has to be <<1 to correctly index a byte)
	uint16_t block = m_vram[(EA<<1)+1] | (m_vram[(EA<<1)]<<8);
	// nybbleNum is the attribute nybble, which in this case is always 3
	uint8_t nybbleNum = 3;
	return (block>>(4*nybbleNum))&0xF;
}

// writes one nybble to vram array based on X and Y regs, and updates the attrib ram if needed
void vk100_state::vram_write(uint8_t data)
{
	// XFinal is (X'&0x3FC)|(X&0x3)
	uint16_t XFinal = m_trans[(m_vgX&0x3FC)>>2]<<2|(m_vgX&0x3); // appears correct
	// EA is the effective ram address for a 16-bit block
	uint16_t EA = ((m_vgY&0x1FE)<<5)|(XFinal>>4); // appears correct
	// block is the 16 bit block directly (note EA has to be <<1 to correctly index a byte)
	uint16_t block = m_vram[(EA<<1)+1] | (m_vram[(EA<<1)]<<8);
	// nybbleNum is which of the four nybbles within the block to address. should NEVER be 3!
	uint8_t nybbleNum = (XFinal&0xC)>>2;
	block &= ~((uint16_t)0xF<<(nybbleNum*4)); // mask out the part we want to replace
	block |= data<<(nybbleNum*4); // write the new part
	// NOTE: this next part may have to be made conditional on VG_MODE
	// check if the attribute nybble is supposed to be modified, and if so do so
	if (VG_WOPS&0x08) block = (block&0x0FFF)|(((uint16_t)VG_WOPS&0xF0)<<8);
	m_vram[(EA<<1)+1] = block&0xFF; // write block back to vram
	m_vram[(EA<<1)] = (block&0xFF00)>>8; // ''
}

/* this is the "DIRECTION ROM"  == mb6309 (256x8, 82s135)
 * see figure 5-24 on page 5-39
 * It tells the direction and enable for counting on the X and Y counters
 * and also handles the non-math related parts of the bresenham line algorithm
 * control bits:
 *            /CE1 ----- DCOUNT 0 H [verified via tracing]
 *            /CE2 ----- ENA ERROR L [verified via tracing]
 * addr bits: 76543210
 *            ||||\\\\-- DIR (vgDIR register low 4 bits)
 *            |||\------ C OUT aka ERROR CARRY (strobed in by STROBE L from the error counter's adder) [verified via tracing]
 *            ||\------- Y0 (the otherwise unused lsb of the Y register, used for bresenham) [verified via tracing]
 *            |\-------- feedback bit from d5 strobed by V CLK [verified via tracing]
 *            \--------- GND; the second half of the prom is blank (0x00)
 * data bits: 76543210
 *            |||||||\-- ENA Y (enables change on Y counter)
 *            ||||||\--- ENA X (enables change on X counter)
 *            |||||\---- Y DIRECTION (high is count down, low is count up)
 *            ||||\----- X DIRECTION (high is count down, low is count up)
 *            |||\------ PIXEL WRT
 *            ||\------- feedback bit to a6, this bit is held in PRESET/1 condition by GO being inactive, and if the vector prom is disabled it is pulled to 1 [verified via tracing and schematics]
 *            |\-------- UNUSED, always 0
 *            \--------- UNUSED, always 0
 * The VT125 prom @ E41 is literally identical to this, the same exact part: 23-059B1
 */
TIMER_CALLBACK_MEMBER(vk100_state::execute_vg)
{
	m_cout = 1; // hack for now
	uint8_t dirbyte = m_dir[(m_dir_a6<<6)|((m_vgY&1)<<5)|(m_cout<<4)|VG_DIR];
#ifdef DEBUG_VG_STATE
	static const char *const vg_functions[] = { "Move", "Dot", "Vector", "Erase" };
	fprintf(stderr, "VGMODE: %s; DIR: A:%02x; D:%02x; X: %03X; Y: %03X; DownCount: %02X\n", vg_functions[m_VG_MODE], ((m_dir_a6<<6)|((m_vgY&1)<<5)|(m_cout<<4)|VG_DIR), dirbyte, m_vgX, m_vgY, m_vgDownCount);
#endif
	m_dir_a6 = m_vgGO?((dirbyte&0x20)>>5):1;
	if (dirbyte&2) // ena_x is active
	{
		if (dirbyte&0x80) m_vgX--;
		else m_vgX++;
	}
	if (dirbyte&1) // ena_y is active
	{
		if (dirbyte&0x40) m_vgY--;
		else m_vgY++;
	}
	if (dirbyte&0x10) m_vgDownCount--; // decrement the down counter
	uint8_t thisNyb = vram_read(); // read in the nybble
	// pattern rom addressing is a complex mess. see the pattern rom def later in this file.
	uint8_t newNyb = m_pattern[((m_vgPAT&m_vgPAT_Mask)?0x200:0)|((VG_WOPS&7)<<6)|((m_vgX&3)<<4)|thisNyb]; // calculate new nybble based on pattern rom
	// finally write the block back to ram depending on the VG_MODE (sort of a hack until we get the vector and sync and dir roms all hooked up)
	// but only do it if the direction rom said so!
	switch (m_VG_MODE)
	{
		case 0: // move; adjusts the x and y but doesn't write anything. do nothing
			break;
		case 1: // dot: only write the LAST pixel in the chain? TODO: some fallthrough magic here?
			if ((m_vgDownCount) == 0x00)
			{
				if (dirbyte&0x10) vram_write(newNyb); // write out the modified nybble
			}
			break;
		case 2: // vec: draw the vector
				if (dirbyte&0x10) vram_write(newNyb); // write out the modified nybble
			break;
		case 3: // er: erase: special case here: wipe the entire screen (except for color/attrib?) and then set done.
			for (int i = 0; i < 0x8000; i++)
			{
				if (!(i&1)) // avoid stomping attribute
					m_vram[i] = m_vram[i]&0xF0;
				else // (i&1)
					m_vram[i] = 0;
			}
			m_vgGO = 0; // done
			break;
	}
	if ((m_vgDownCount) == 0x00) m_vgGO = 0; // check if the down counter hit terminal count (0), if so we're done.
	if (((++m_vgPMUL_Count)&0xF)==0) // if pattern multiplier counter overflowed
	{
		m_vgPMUL_Count = m_vgPMUL; // reload counter
		m_vgPAT_Mask >>= 1; // shift the mask
		if (m_vgPAT_Mask == 0) m_vgPAT_Mask = 0x80; // reset mask if it hits 0
	}
	if (m_vgGO)
	{
		// /3/12/2 is correct. the sync counter is clocked by the dot clock, despite the error on figure 5-21
		m_vg_timer->adjust(attotime::from_hz(XTAL(45'619'200)/3/12/2));
	}
}

/* ports 0x40 and 0x41: load low and high bytes of vector gen X register */
void vk100_state::vgLD_X(offs_t offset, uint8_t data)
{
	m_vgX &= 0xFF << ((1-offset)*8);
	m_vgX |= ((uint16_t)data) << (offset*8);
#ifdef VG40_VERBOSE
	logerror("VG: 0x%02X: X Reg loaded with %04X, new X value is %04X\n", 0x40+offset, ((uint16_t)data) << (offset*8), m_vgX);
#endif
}

/* ports 0x42 and 0x43: load low and high bytes of vector gen Y register */
void vk100_state::vgLD_Y(offs_t offset, uint8_t data)
{
	m_vgY &= 0xFF << ((1-offset)*8);
	m_vgY |= ((uint16_t)data) << (offset*8);
#ifdef VG40_VERBOSE
	logerror("VG: 0x%02X: Y Reg loaded with %04X, new Y value is %04X\n", 0x42+offset, ((uint16_t)data) << (offset*8), m_vgY);
#endif
}

/* port 0x44: "ERR" load bresenham line algorithm 'error' count */
void vk100_state::vgERR(uint8_t data)
{
	m_vgERR = data;
#ifdef VG40_VERBOSE
	logerror("VG: 0x44: ERR Reg loaded with %02X\n", m_vgERR);
#endif
}

/* port 0x45: "SOPS" screen options
 * (handled by 74LS273 @ E55, schematic sheet 10, all signals called 'VVG1 BDx' where x is 7 to 0)
 * Blink   --Background color--    Blink   Serial  Serial  Reverse
 * Enable  Green   Red     Blue    Control SL1     SL0     BG/FG
 * d7      d6      d5      d4      d3      d2      d1      d0
 * apparently, SLx: 00 = rs232/eia(J6), 01 = 20ma(J1), 10 = hardcopy(J7), 11 = test/loopback
 * Serial Select (SLx) routing controls are rather complex, shown on schematic
 * page 9:
 * VDC2    |  I8251 pins                                                      |  SYSTAT_B bits
 * SL1 SL0 |  8251RXD   8251RTS    8251TXD    8251/DTR    8251/DSR    8251CTS |  SYSTATB_ACTS   SYSTATB_ADSR
 * 0   0      J6 /RXD   J6 /RTS    J6 TXD     J6 /DTR     J7 URTS     GND        J6 /CTS        J6 /DSR
 * 0   1      J1 +-R    ACTS(loop) J1 +-T     J6 /DTR     J7 URTS     GND        8251RTS(loop)  J6 /DSR
 * 1   0      J7 DRXD   J7 DRTS*   J7 DTXD    J6 /DTR     J7 URTS     GND        J7 /DCTS       J6 /DSR
 * 1   1      8251TXD   ACTS(loop) 8251RXD    J6 /DTR     J7 URTS     GND        8251RTS(loop)  J6 /DSR
 *                      * and UCTS, the pin drives both pins on J7
 */
void vk100_state::vgSOPS(uint8_t data)
{
	m_vgSOPS = data;
#ifdef VG40_VERBOSE
	static const char *const serialDest[4] = { "EIA232", "20ma", "Hardcopy", "Loopback/test" };
	logerror("VG: 0x45: SOPS Reg loaded with %02X: Background KGRB: %d%d%d%d, Blink: %d, Serial select: %s, Reverse BG/FG: %d\n", m_vgSOPS, (m_vgSOPS>>7)&1, (m_vgSOPS>>6)&1, (m_vgSOPS>>5)&1, (m_vgSOPS>>4)&1, (m_vgSOPS>>3)&1, serialDest[(m_vgSOPS>>1)&3], m_vgSOPS&1);
#endif
}

/* port 0x46: "PAT" load vg Pattern register */
void vk100_state::vgPAT(uint8_t data)
{
	m_vgPAT = data;
#ifdef PAT_DEBUG
	for (int i = 7; i >= 0; i--)
	{
		printf("%s", (data&(1<<i))?"X":".");
	}
	printf("\n");
#endif
#ifdef VG40_VERBOSE
	logerror("VG: 0x46: PAT Reg loaded with %02X\n", m_vgPAT);
#endif
}

/* port 0x47: "PMUL" load vg Pattern Multiply Register
   The pattern multiply register loads a counter which counts reads from
   the pattern register and increments on each one. if it overflows from
   1111 to 0000 the pattern register is shifted right one bit and the
   counter is reloaded from PMUL */
void vk100_state::vgPMUL(uint8_t data)
{
	m_vgPMUL = data;
#ifdef VG40_VERBOSE
	logerror("VG: 0x47: PMUL Reg loaded with %02X\n", m_vgPMUL);
#endif
}

/* port 0x60: "DU" load vg vector major register */
/* port 0x61: "DVM" load vg vector minor register */
/* port 0x62: "DIR" load vg Direction register */
/* port 0x63: "WOPS" vector 'pixel' write options
 * --Attributes to change --   Enable --  Functions --
 * Blink  Green  Red    Blue   Attrib F1     F0     FN
 *                             Change
 * d7     d6     d5     d4     d3     d2     d1     d0
 */
void vk100_state::vgREG(offs_t offset, uint8_t data)
{
	m_vgRegFile[offset] = data;
#ifdef VG60_VERBOSE
	static const char *const regDest[4] = { "DU", "DVM", "DIR", "WOPS" };
	static const char *const wopsFunctions[] = { "Overlay", "Replace", "Complement", "Erase" };
	if (offset < 3) logerror("VG: 0x%02x: %s Reg loaded with %02X\n", (0x60+offset), regDest[offset], m_vgRegFile[offset]);
	else logerror("VG: 0x63: WOPS Reg loaded with %02X: KGRB %d%d%d%d, AttrChange %d, Function %s, Negate %d\n", data, (VG_WOPS>>7)&1, (VG_WOPS>>6)&1, (VG_WOPS>>5)&1, (VG_WOPS>>4)&1, (VG_WOPS>>3)&1, wopsFunctions[(VG_WOPS>>1)&3], VG_WOPS&1);
#endif
}


/* port 0x64: "EX MOV" execute a move (relative move of x and y using du/dvm/dir/err, no writing) */
/* port 0x65: "EX DOT" execute a dot (draw a dot at x,y?) */
/* port 0x66: "EX VEC" execute a vector (draw a vector from x,y to a destination using du/dvm/dir/err ) */
/* port 0x67: "EX ER" execute an erase (clear the screen to the bg color, i.e. fill vram with zeroes) */
void vk100_state::vgEX(offs_t offset, uint8_t data)
{
#ifdef VG60_VERBOSE
	static const char *const ex_functions[] = { "Move", "Dot", "Vector", "Erase" };
	logerror("VG Execute %s 0x%02X written with %d\n", ex_functions[offset&3], 0x67+offset, data);
	//fprintf(stderr, "VG Execute %s 0x%02X written with %d\n", ex_functions[offset&3], 0x67+offset, data);
#endif
	m_vgPMUL_Count = m_vgPMUL; // load PMUL_Count
	m_vgPAT_Mask = 0x80;
	m_vgDownCount = VG_DU; // set down counter to length of major vector
	m_VG_MODE = offset&3;
	m_vgGO = 1;
	m_vg_timer->adjust(attotime::zero);
}


/* port 0x68: "KBDW" d7 is beeper, d6 is keyclick, d5-d0 are keyboard LEDS */
void vk100_state::KBDW(uint8_t data)
{
	m_online_led = BIT(data, 5) ? 1 : 0;
	m_local_led = BIT(data, 5) ? 0 : 1;
	m_noscroll_led = BIT(data, 4) ? 1 : 0;
	m_basic_led = BIT(data, 3) ? 1 : 0;
	m_hardcopy_led = BIT(data, 2) ? 1 : 0;
	m_l1_led = BIT(data, 1) ? 1 : 0;
	m_l2_led = BIT(data, 0) ? 1 : 0;
#ifdef LED_VERBOSE
	if (BIT(data, 6)) logerror("kb keyclick bit 6 set: not emulated yet (multivibrator)!\n");
#endif
	m_speaker->set_state(BIT(data, 7));
#ifdef LED_VERBOSE
	logerror("LED state: %02X: %s %s %s %s %s %s\n", data&0xFF, (data&0x20)?"------- LOCAL ":"ON LINE ----- ", (data&0x10)?"--------- ":"NO SCROLL ", (data&0x8)?"----- ":"BASIC ", (data&0x4)?"--------- ":"HARD-COPY ", (data&0x2)?"-- ":"L1 ", (data&0x1)?"-- ":"L2 ");
#endif
}

/* port 0x6C: "BAUD" controls the smc com5016t dual baud generator which
 * controls the divisors for the rx and tx clocks on the 8251 from the
    5.0688Mhz cpu xtal.
   It has 5v,12v on pins 2 and 9, pin 10 is NC.
 * A later part that replaced this on the market is SMC COM8116(T)/8136(T), which
    was a 5v-only part (pin 9 and 10 are NC, 10 is a clock out on the 8136.
 *  Note that even on the SMC COM5016T version, SMC would allow the user
     to mask their own dividers on custom ordered chips if desired.
 *  The COM8116(T)/8136(T) came it at least 4 mask rom types meant for different
     input clocks:
     -000 or no mark for 5.0688Mhz (which exactly matches the table below) (synertek sy2661-3 also matches this exactly)
     -003 is for 6.01835MHz
     -005 is for 4.915200Mhz
     -006 is for 5.0688Mhz but omits the 2000 baud entry, instead has 200,
      and output frequencies are 2x as fast (meant for a 32X clock uart)
     -013 is for 2.76480MHz
     -013A is for 5.52960MHz
     (several other unknown refclock masks appear on partscalper sites)
    GI also made a clone of the 8116 5v chip called the AY-5-8116(T)/8136(T)
     which had at least two masks: -000/no mark and -005, matching speeds above
    WD made the WD1943 which is similarly 5v compatible, with -00, -05, -06 masks
    The COM8046(T) has 5 bits for selection instead of 4, but still expects
     a 5.0688MHz reference clock, and the second half of the table matches the
     values below; the first half of the table is the values below /2, rounded
     down (for uarts which need a clock rate of 32x baud instead of 16x).
    WD's BR1941 is also functionally compatible but uses 5v,12v,-5v on pins 2,9,10
 * The baud divisor lookup table has 16 entries, but only entries 2,5,6,7,A,C,E,F are documented/used in the vk100 tech manual
 * The others are based on page 13 of http://www.hartetechnologies.com/manuals/Tarbell/Tarbell%20Z80%20CPU%20Board%20Model%203033.pdf
 * D C B A   Divisor                                Expected Baud
 * 0 0 0 0 - 6336 (5068800 / 6336 = 16*50         = 50 baud
 * 0 0 0 1 - 4224 (5068800 / 4224 = 16*75)        = 75 baud
 * 0 0 1 0 - 2880 (5068800 / 2880 = 16*110)       = 110 baud
 * 0 0 1 1 - 2355 (5068800 / 2355 = 16*134.5223) ~= 134.5 baud
 * 0 1 0 0 - 2112 (5068800 / 2112 = 16*150)       = 150 baud
 * 0 1 0 1 - 1056 (5068800 / 1056 = 16*300)       = 300 baud
 * 0 1 1 0 - 528  (5068800 / 528 = 16*600)        = 600 baud
 * 0 1 1 1 - 264  (5068800 / 264 = 16*1200)       = 1200 baud
 * 1 0 0 0 - 176  (5068800 / 176 = 16*1800)       = 1800 baud
 * 1 0 0 1 - 158  (5068800 / 158 = 16*2005.0633) ~= 2000 baud
 * 1 0 1 0 - 132  (5068800 / 132 = 16*2400)       = 2400 baud
 * 1 0 1 1 - 88   (5068800 / 88 = 16*3600)        = 3600 baud
 * 1 1 0 0 - 66   (5068800 / 66 = 16*4800)        = 4800 baud
 * 1 1 0 1 - 44   (5068800 / 44 = 16*7200)        = 7200 baud
 * 1 1 1 0 - 33   (5068800 / 33 = 16*9600)        = 9600 baud
 * 1 1 1 1 - 16   (5068800 / 16 = 16*19800)      ~= 19200 baud
 */

/* port 0x40-0x47: "SYSTAT A"; various status bits, poorly documented in the tech manual
 * /GO    VDM1   VDM1   VDM1   VDM1   Dip     RST7.5 GND***
 *        BIT3   BIT2   BIT1   BIT0   Switch  VSYNC
 * d7     d6     d5     d4     d3     d2      d1     d0
  bit3, 2, 1, 0 are the 4 bits output from the VRAM 12->4 multiplexer
   which are also inputs to the pattern rom; they are constantly updated
   by the sync rom and related circuitry.
  This is the only way the vram can be read by the cpu.
  d7 is from the /Q output of the GO latch
  d6,5,4,3 are from the 74ls298 at ic4 (right edge of pcb)
  d2 is where the dipswitch values are read from, based on the offset
  d1 is connected to 8085 rst7.5 (pin 7) and crtc pin 40 (VSYNC) [verified via tracing]
  d0 is tied to GND [verified via tracing] but the schematics both tie it to GND
     and call it LP FLAG, may be a leftover from development.

 31D reads and checks d7 in a loop
 205 reads, xors with 0x55 (from reg D), ANDS result with 0x78 and branches if it is not zero (checking for bit pattern 1010?)
 299 reads, rotates result right 3 times and ANDs the result with 0x0F
 2A4 reads, rotates result left 1 time and ANDS the result with 0xF0
*/
uint8_t vk100_state::SYSTAT_A(offs_t offset)
{
	uint8_t dipswitchLUT[8] = { 1,3,5,7,6,4,2,0 }; // the dipswitches map in a weird order to offsets
#ifdef SYSTAT_A_VERBOSE
	if (m_maincpu->pc() != 0x31D) logerror("0x%04X: SYSTAT_A Read!\n", m_maincpu->pc());
#endif
	return ((m_vgGO?0:1)<<7)|(vram_read()<<3)|(((m_dipsw->read()>>dipswitchLUT[offset])&1)?0x4:0)|(m_vsync?0x2:0);
}

/* port 0x48: "SYSTAT B"; NOT documented in the tech manual at all.
 * when in loopback/test mode, SYSTAT_B is read and expected the following, around 0x606:
 * reset 8751, modewrite of 0x5E
 * write command -> 0x20 (normal, normal, /RTS is 0, normal, normal, receive off, /DTR is 1, transmit off)
 * read SYSTAT B (and xor with 0xe), expect d7 to be CLEAR or jump to error
 * write command -> 0x05 (normal, normal, /RTS is 1, normal, normal, receive ON, /DTR is 0, transmit off)
 * read SYSTAT B (and xor with 0xe), expect d7 to be SET or jump to error
 * after this it does something and waits for an rxrdy interrupt

 shows the results of:
 * ACTS (/CTS)  ADSR (/DSR)  GND    GND    ATTR3  ATTR2  ATTR1  ATTR0
 * d7           d6           d5     d4     d3     d2     d1     d0
 * the ACTS (inverse of DCTS) signal lives in one of these bits (see 5-62)
 * it XORs the read of systat_b with the E register (which holds 0x6)
 * and checks the result
 * The 4 attribute ram bits for the cell being pointed at by the X and Y regs are readable as the low nybble.
 * The DSR pin is readable as bit 6.
 */
uint8_t vk100_state::SYSTAT_B()
{
#ifdef SYSTAT_B_VERBOSE
	logerror("0x%04X: SYSTAT_B Read!\n", m_maincpu->pc());
#endif
	return (m_ACTS<<7)|(m_ADSR<<6)|vram_attr_read();
}

uint8_t vk100_state::vk100_keyboard_column_r(offs_t offset)
{
	uint8_t code = m_col_array[offset&0xF]->read() | m_capsshift->read();
#ifdef KBD_VERBOSE
	logerror("Keyboard column %X read, returning %02X\n", offset&0xF, code);
#endif
	return code;
}

void vk100_state::vk100_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x6fff).rom();
	map(0x7000, 0x700f).mirror(0x0ff0).r(FUNC(vk100_state::vk100_keyboard_column_r));
	map(0x8000, 0xbfff).ram();
}

/*
 * 8085 IO address map (x = ignored; * = selects address within this range; ? = not sure; ** = subparts check this bit)
 * (a15 to a8 are latched the same value as a7-a0 on the 8080 and 8085)
 * [this map is derived from extensive tracing as well as some guesswork, noted for the crtc, systat_b and the uart]
   a7  a6  a5  a4  a3  a2  a1  a0
   x   0   x   x   x   x   x   0     W     CRTC address
   x   0   x   x   x   x   x   1     RW    CRTC register r/w
   x   1   *   *   *   **  **  **        read area (rightmost 74ls138):
   x   1   0   0   0   *   *   *     R     SYSTAT_A (a0-a3 chooses the bit of the dipswitches read via d3)
   x   1   0   0   1   x   x   x     R     SYSTAT_B
   x   1   0   1   0   x   x   0     R     i8251 UART data
   x   1   0   1   0   x   x   1     R     i8251 UART status
   x   1   0   1   1   x   x   x     R     unused
   x   1   1   0   0   x   x   x     R     unused
   x   1   1   0   1   x   x   x     R     unused
   x   1   1   1   0   x   x   x     R     unused
   x   1   1   1   1   x   x   x     R     unused
   x   1   0   x   x   *   *   *         write area (right 74ls138):
   x   1   0   x   x   0   0   0     W     X (low 8 bits)
   x   1   0   x   x   0   0   1     W     X (high 4 bits)
   x   1   0   x   x   0   1   0     W     Y (low 8 bits)
   x   1   0   x   x   0   1   1     W     Y (high 4 bits)
   x   1   0   x   x   1   0   0     W     ERR
   x   1   0   x   x   1   0   1     W     SOPS
   x   1   0   x   x   1   1   0     W     PAT
   x   1   0   x   x   1   1   1     W     PMUL
   x   1   1   *   *   *   **  **        write area (middle 74ls138):
   x   1   1   0   0   0   **  **          write to register file 2x 74ls670:
   x   1   1   0   0   0   0   0     W       DU
   x   1   1   0   0   0   0   1     W       DVM
   x   1   1   0   0   0   1   0     W       DIR
   x   1   1   0   0   0   1   1     W       WOPS
   x   1   1   0   0   1   *   *     W     set VG_MODE to * * XOR 3 and Execute (if GO is not active)
   x   1   1   0   1   0   x   x     W     KYBDW
   x   1   1   0   1   1   x   x     W     BAUD
   x   1   1   1   0   0   x   0     W     i8251 UART data
   x   1   1   1   0   0   x   1     W     i8251 UART control
   x   1   1   1   0   1   x   x     W     unused
   x   1   1   1   1   0   x   x     W     unused
   x   1   1   1   1   1   x   x     W     unused
*/
void vk100_state::vk100_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff); // guess, probably correct
	map(0x00, 0x00).mirror(0xBE).w(m_crtc, FUNC(mc6845_device::address_w));
	map(0x01, 0x01).mirror(0xBE).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	// Comments are from page 118 (5-14) of http://web.archive.org/web/20091015205827/http://www.computer.museum.uq.edu.au/pdf/EK-VK100-TM-001%20VK100%20Technical%20Manual.pdf
	map(0x40, 0x41).mirror(0x98).w(FUNC(vk100_state::vgLD_X));  //LD X LO + HI 12 bits
	map(0x42, 0x43).mirror(0x98).w(FUNC(vk100_state::vgLD_Y));  //LD Y LO + HI 12 bits
	map(0x44, 0x44).mirror(0x98).w(FUNC(vk100_state::vgERR));    //LD ERR ('error' in bresenham algorithm)
	map(0x45, 0x45).mirror(0x98).w(FUNC(vk100_state::vgSOPS));   //LD SOPS (screen options (plus uart dest))
	map(0x46, 0x46).mirror(0x98).w(FUNC(vk100_state::vgPAT));    //LD PAT (pattern register)
	map(0x47, 0x47).mirror(0x98).w(FUNC(vk100_state::vgPMUL));   //LD PMUL (pattern multiplier)
	map(0x60, 0x63).mirror(0x80).w(FUNC(vk100_state::vgREG));     //LD DU, DVM, DIR, WOPS (register file)
	map(0x64, 0x67).mirror(0x80).w(FUNC(vk100_state::vgEX));    //EX MOV, DOT, VEC, ER
	map(0x68, 0x68).mirror(0x83).w(FUNC(vk100_state::KBDW));   //KBDW (probably mirror(0x03))
	map(0x6C, 0x6C).mirror(0x83).w(COM5016T_TAG, FUNC(com8116_device::stt_str_w));   //LD BAUD (baud rate clock divider setting for i8251 tx and rx clocks) (probably mirror(0x03))
	map(0x70, 0x71).mirror(0x82).w(m_uart, FUNC(i8251_device::write)); //LD COMD
	//map(0x74, 0x74).mirror(0x83).w(FUNC(vk100_state::unknown_74));
	//map(0x78, 0x78).mirror(0x83).w(FUNC(vk100_state::kbdw));   //KBDW ?(mirror?)
	//map(0x7C, 0x7C).mirror(0x83).w(FUNC(vk100_state::unknown_7C));
	map(0x40, 0x47).mirror(0x80).r(FUNC(vk100_state::SYSTAT_A)); // SYSTAT A (state machine done and last 4 bits of vram, as well as dipswitches)
	map(0x48, 0x48).mirror(0x87/*0x80*/).r(FUNC(vk100_state::SYSTAT_B)); // SYSTAT B (uart stuff)
	map(0x50, 0x51).mirror(0x86).r(m_uart, FUNC(i8251_device::read)); // UART O
	//map(0x58, 0x58).mirror(0x87).r(FUNC(vk100_state::unknown_58));
	//map(0x60, 0x60).mirror(0x87).r(FUNC(vk100_state::unknown_60));
	//map(0x68, 0x68).mirror(0x87).r(FUNC(vk100_state::unknown_68)); // NOT USED
	//map(0x70, 0x70).mirror(0x87).r(FUNC(vk100_state::unknown_70));
	//map(0x78, 0x7f).mirror(0x87).r(FUNC(vk100_state::unknown_78));
}

/* Input ports */
static INPUT_PORTS_START( vk100 )
	// the dipswitches are common ground: when open (upward) the lines are pulled to 5v, otherwise they read as 0
	PORT_START("SWITCHES")
		PORT_DIPNAME( 0x01, 0x00, "Power Frequency" )           PORT_DIPLOCATION("SW:!1")
		PORT_DIPSETTING( 0x00, "60Hz" )
		PORT_DIPSETTING( 0x01, "50Hz" )
		PORT_DIPNAME( 0x02, 0x00, "Default Serial Port" )           PORT_DIPLOCATION("SW:!2")
		PORT_DIPSETTING( 0x00, "EIA port" )
		PORT_DIPSETTING( 0x02, "20ma port" )
		PORT_DIPNAME( 0x04, 0x00, "Default US/UK" )         PORT_DIPLOCATION("SW:!3")
		PORT_DIPSETTING( 0x00, "US" )
		PORT_DIPSETTING( 0x04, "UK" )
		PORT_DIPNAME( 0x18, 0x00, "Default Parity" )            PORT_DIPLOCATION("SW:!4,!5")
		PORT_DIPSETTING( 0x00, "Off" )
		PORT_DIPSETTING( 0x10, "Even" )
		PORT_DIPSETTING( 0x08, "Odd" )
		PORT_DIPSETTING( 0x18, "Do Not Use This Setting" )
		PORT_DIPNAME( 0xe0, 0x00, "Default Baud Rate" )         PORT_DIPLOCATION("SW:!6,!7,!8")
		PORT_DIPSETTING( 0x00, "110" )
		PORT_DIPSETTING( 0x80, "300" )
		PORT_DIPSETTING( 0x40, "600" )
		PORT_DIPSETTING( 0xc0, "1200" )
		PORT_DIPSETTING( 0x20, "2400" )
		PORT_DIPSETTING( 0xa0, "4800" )
		PORT_DIPSETTING( 0x60, "9600" )
		PORT_DIPSETTING( 0xe0, "19200" )

	PORT_START("CAPSSHIFT") // CAPS LOCK and SHIFT appear as the high 2 bits on all rows
		PORT_BIT(0x3f, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_TOGGLE PORT_NAME("Caps lock") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_START("COL0")
		PORT_BIT(0x1f, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED) // row 0 bit 6 is always low, checked by keyboard test
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED) // all rows have these bits left low to save a mask op later
	PORT_START("COL1")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Set Up") PORT_CODE(KEYCODE_F5)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num Enter") PORT_CODE(KEYCODE_ENTER_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PF1/Hardcopy") PORT_CODE(KEYCODE_F1)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COL2")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("No scroll") PORT_CODE(KEYCODE_LALT)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 1") PORT_CODE(KEYCODE_1_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PF2/Locator") PORT_CODE(KEYCODE_F2)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COL3")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 2") PORT_CODE(KEYCODE_2_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PF3/Text") PORT_CODE(KEYCODE_F3)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COL4")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 3") PORT_CODE(KEYCODE_3_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PF4/Reset") PORT_CODE(KEYCODE_F4)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COL5")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 4") PORT_CODE(KEYCODE_4_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COL6")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 5") PORT_CODE(KEYCODE_5_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COL7")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 6") PORT_CODE(KEYCODE_6_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COL8")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 7") PORT_CODE(KEYCODE_7_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COL9")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 8") PORT_CODE(KEYCODE_8_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COLA")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 9") PORT_CODE(KEYCODE_9_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COLB")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 0") PORT_CODE(KEYCODE_0_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COLC")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("'") PORT_CODE(KEYCODE_QUOTE)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num -") PORT_CODE(KEYCODE_MINUS_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COLD")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("=") PORT_CODE(KEYCODE_EQUALS)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_CLOSEBRACE)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num ,") PORT_CODE(KEYCODE_PLUS_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COLE")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("~") PORT_CODE(KEYCODE_TILDE)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Delete") PORT_CODE(KEYCODE_DEL)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Line feed") PORT_CODE(KEYCODE_RALT)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num .") PORT_CODE(KEYCODE_DEL_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COLF")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_F6)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER)
		PORT_BIT(0x18, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END


void vk100_state::machine_start()
{
	m_online_led.resolve();
	m_local_led.resolve();
	m_noscroll_led.resolve();
	m_basic_led.resolve();
	m_hardcopy_led.resolve();
	m_l1_led.resolve();
	m_l2_led.resolve();

	m_online_led = 1;
	m_local_led = 0;
	m_noscroll_led = 1;
	m_basic_led = 1;
	m_hardcopy_led = 1;
	m_l1_led = 1;
	m_l2_led = 1;
	m_vsync = 0;
	m_dir_a6 = 1;
	m_cout = 0;
	m_vgX = 0;
	m_vgY = 0;
	m_vgERR = 0;
	m_vgSOPS = 0;
	m_vgPAT = 0;
	m_vgPAT_Mask = 0x80;
	m_vgPMUL = 0;
	m_vgPMUL_Count = 0;
	m_vgDownCount = 0;
	VG_DU = 0;
	VG_DVM = 0;
	VG_DIR = 0;
	VG_WOPS = 0;
	m_VG_MODE = 0;
	m_vgGO = 0;
	m_ACTS = 1;
	m_ADSR = 1;

	m_vg_timer = timer_alloc(FUNC(vk100_state::execute_vg), this);
	// TODO: figure out the best way to bring up the i8251 timers
	//m_i8251_rx_timer = timer_alloc(FUNC(vk100_state::i8251_rx_tick), this);
	//m_i8251_tx_timer = timer_alloc(FUNC(vk100_state::i8251_tx_tick), this);
	//m_i8251_sync_timer = timer_alloc(FUNC(vk100_state::i8251_sync), this);
}

void vk100_state::crtc_vsync(int state)
{
	m_maincpu->set_input_line(I8085_RST75_LINE, state? ASSERT_LINE : CLEAR_LINE);
	m_vsync = state;
}

void vk100_state::i8251_rxrdy_int(int state)
{
	m_maincpu->set_input_line(I8085_RST65_LINE, state?ASSERT_LINE:CLEAR_LINE);
}

void vk100_state::i8251_txrdy_int(int state)
{
	m_maincpu->set_input_line(I8085_RST55_LINE, state?ASSERT_LINE:CLEAR_LINE);
}

void vk100_state::i8251_rts(int state)
{
	logerror("callback: RTS state changed to %d\n", state);
	// TODO: only change this during loopback mode!
	m_ACTS = state;
}

void vk100_state::video_start()
{
	m_vram = memregion("vram")->base();
	m_trans = memregion("trans")->base();
	m_pattern = memregion("pattern")->base();
	m_dir = memregion("dir")->base();
	m_sync = memregion("sync")->base();
	m_vector = memregion("vector")->base();
	m_ras_erase = memregion("ras_erase")->base();
}

MC6845_UPDATE_ROW( vk100_state::crtc_update_row )
{
	static const uint32_t colorTable[16] = {
	0x000000, 0x0000FF, 0xFF0000, 0xFF00FF, 0x00FF00, 0x00FFFF, 0xFFFF00, 0xFFFFFF,
	0x000000, 0x0000FF, 0xFF0000, 0xFF00FF, 0x00FF00, 0x00FFFF, 0xFFFF00, 0xFFFFFF };
	static const uint32_t colorTable2[16] = {
	0x000000, 0x0000FF, 0xFF0000, 0xFF00FF, 0x00FF00, 0x00FFFF, 0xFFFF00, 0xFFFFFF,
	0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000 };
	//printf("y=%d, ma=%04x, ra=%02x, x_count=%02x ", y, ma, ra, x_count);
	/* figure out ram address based on tech manual page 5-23:
	 * real address to 16-bit chunk a13  a12  a11 a10 a9  a8  a7  a6  a5  a4  a3  a2  a1  a0
	 * crtc input                  MA11 MA10 MA9 MA8 MA7 MA6 RA1 RA0 MA5 MA4 MA3 MA2 MA1 MA0
	 */
	uint16_t EA = ((ma&0xfc0)<<2)|((ra&0x3)<<6)|(ma&0x3F);
	// display the 64 different 12-bit-wide chunks
	for (int i = 0; i < 64; i++)
	{
		uint16_t block = m_vram[(EA<<1)+(2*i)+1] | (m_vram[(EA<<1)+(2*i)]<<8);
		uint32_t fgColor = (m_vgSOPS&0x08)?colorTable[(block&0xF000)>>12]:colorTable2[(block&0xF000)>>12];
		uint32_t bgColor = (m_vgSOPS&0x08)?colorTable[(m_vgSOPS&0xF0)>>4]:colorTable2[(m_vgSOPS&0xF0)>>4];
		// display a 12-bit wide chunk
		for (int j = 0; j < 12; j++)
		{
			bitmap.pix(y, (12*i)+j) = (((block&(0x0001<<j))?1:0)^(m_vgSOPS&1))?fgColor:bgColor;
		}
	}
}


void vk100_state::vk100(machine_config &config)
{
	/* basic machine hardware */
	I8085A(config, m_maincpu, XTAL(5'068'800));
	m_maincpu->set_addrmap(AS_PROGRAM, &vk100_state::vk100_mem);
	m_maincpu->set_addrmap(AS_IO, &vk100_state::vk100_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(XTAL(45'619'200)/3, 882, 0, 720, 370, 0, 350 ); // fake screen timings for startup until 6845 sets real ones
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	MC6845(config, m_crtc, 45.6192_MHz_XTAL/3/12); // unknown variant
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(12);
	m_crtc->set_update_row_callback(FUNC(vk100_state::crtc_update_row));
	m_crtc->out_vsync_callback().set(FUNC(vk100_state::crtc_vsync));

	/* i8251 uart */
	I8251(config, m_uart, 0);
	m_uart->txd_handler().set(RS232_TAG, FUNC(rs232_port_device::write_txd));
	m_uart->dtr_handler().set(RS232_TAG, FUNC(rs232_port_device::write_dtr));
	m_uart->rts_handler().set(RS232_TAG, FUNC(rs232_port_device::write_rts));
	m_uart->rxrdy_handler().set(FUNC(vk100_state::i8251_rxrdy_int));
	m_uart->txrdy_handler().set(FUNC(vk100_state::i8251_txrdy_int));

	rs232_port_device &rs232(RS232_PORT(config, RS232_TAG, default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_uart, FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set(m_uart, FUNC(i8251_device::write_dsr));

	com8116_device &dbrg(COM8116(config, COM5016T_TAG, 5.0688_MHz_XTAL));
	dbrg.fr_handler().set(m_uart, FUNC(i8251_device::write_rxc));
	dbrg.ft_handler().set(m_uart, FUNC(i8251_device::write_txc));

	config.set_default_layout(layout_vk100);

	SPEAKER(config, "mono").front_center();
	BEEP(config, m_speaker, 116).add_route(ALL_OUTPUTS, "mono", 0.25); // 116 hz (page 172 of TM), but duty cycle is wrong here!
}

/* ROM definition */
/* according to http://www.computer.museum.uq.edu.au/pdf/EK-VK100-TM-001%20VK100%20Technical%20Manual.pdf
page 5-10 (pdf pg 114), The 4 firmware roms should go from 0x0000-0x1fff,
0x2000-0x3fff, 0x4000-0x5fff and 0x6000-0x63ff; The last rom is actually a
little bit longer and goes to 67ff.
*/
ROM_START( vk100 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "23-031e4-00.rom1.e53", 0x0000, 0x2000, CRC(c8596398) SHA1(a8dc833dcdfb7550c030ac3d4143e266b1eab03a))
	ROM_LOAD( "23-017e4-00.rom2.e52", 0x2000, 0x2000, CRC(e857a01e) SHA1(914b2c51c43d0d181ffb74e3ea59d74e70ab0813))
	ROM_LOAD( "23-018e4-00.rom3.e51", 0x4000, 0x2000, CRC(b3e7903b) SHA1(8ad6ed25cd9b04a9968aa09ab69ba526d35ca550))
	ROM_LOAD( "23-190e2-00.rom4.e50", 0x6000, 0x1000, CRC(ad596fa5) SHA1(b30a24155640d32c1b47a3a16ea33cd8df2624f6)) // probably an overdump, e2 implies the size is 0x800 not 0x1000, and the end is all blank

	ROM_REGION( 0x8000, "vram", ROMREGION_ERASE00 ) // 32k of vram

	ROM_REGION( 0x400, "pattern", ROMREGION_ERASEFF )
	/* This is the "PATTERN ROM", (1k*4, 82s137)
	 * it contains a table of 4 output bits based on input of: (from figure 5-18):
	 * 4 input bits bank-selected from RAM (a0, a1, a2, a3)
	 * 2 select bits to choose one of the 4 bits, sourced from the X reg LSBs (a4, a5)
	 * 3 pattern function bits from WOPS (F0 "N" is a6, F1 and F2 are a7 and a8
	 * and one bit from the lsb of the pattern register shifter (a9)
	 * control bits:
	 *            /CE1 ----- GND [verified via tracing]
	 *            /CE2 ----- GND [verified via tracing]
	 * addr bits 9876543210
	 *           ||||||\\\\- input from ram (A)
	 *           ||||\\----- bit select (from x reg lsb)
	 *           |||\------- negate (N) \___ low 3 bits of WOPS
	 *           |\\-------- function   /
	 *           \---------- pattern bit (P)
	 * data bits: 3210
	 *            \\\\-- output to ram (M), but gated by an io line from vector rom
	 *    functions are:
	 *    Overlay: M=A|(P^N)
	 *    Replace: M=P^N
	 *    Complement: M=A^(P^N)
	 *    Erase: M=N
	 */
	ROM_LOAD( "wb8201_656f1.m1-7643-5.pr4.ic14", 0x0000, 0x0400, CRC(e8ecf59f) SHA1(49e9d109dad3d203d45471a3f4ca4985d556161f)) // label verified from nigwil's board

	ROM_REGION(0x100, "trans", ROMREGION_ERASEFF )
	/* this is the "TRANSLATOR ROM" described in figure 5-17 on page 5-27 (256*8, 82s135)
	 * it contains a table of 256 values which skips every fourth value so 00 01 02 04 05 06 08.. etc, wraps at the end
	 * control bits:
	 *            /CE1 ----- GND [verified via tracing]
	 *            /CE2 ----- GND [verified via tracing]
	 * addr bits: 76543210
	 *            \\\\\\\\- X9 thru X2
	 * data bits: 76543210
	 *            \\\\\\\\- X'9 thru X'2
	 * The VT125 prom @ E60 is literally identical to this, the same exact part: 23-060B1
	 */
	ROM_LOAD( "wb---0_060b1.mmi6309.pr2.ic82", 0x0000, 0x0100, CRC(198317fc) SHA1(00e97104952b3fbe03a4f18d800d608b837d10ae)) // label verified from nigwil's board

	ROM_REGION(0x100, "dir", ROMREGION_ERASEFF )
		/* this is the "DIRECTION ROM"  == mb6309 (256x8, 82s135)
		* see figure 5-24 on page 5-39
		* It tells the direction and enable for counting on the X and Y counters
		* and also handles the non-math related parts of the bresenham line algorithm
		* control bits:
		*            /CE1 ----- DCOUNT 0 H [verified via tracing]
		*            /CE2 ----- ENA ERROR L [verified via tracing]
		* addr bits: 76543210
		*            ||||\\\\-- DIR (vgDIR register low 4 bits)
		*            |||\------ C OUT aka ERROR CARRY (strobed in by STROBE L from the error counter's adder) [verified via tracing]
		*            ||\------- Y0 (the otherwise unused lsb of the Y register, used for bresenham) [verified via tracing]
		*            |\-------- feedback bit from d5 strobed by V CLK [verified via tracing]
		*            \--------- GND; the second half of the prom is blank (0x00)
		* data bits: 76543210
		*            |||||||\-- ENA Y (enables change on X counter) [works with code]
		*            ||||||\--- ENA X (enables change on Y counter) [works with code]
		*            |||||\---- Y DIRECTION (high is count down, low is count up)
		*            ||||\----- X DIRECTION (high is count down, low is count up)
		*            |||\------ PIXEL WRT
		*            ||\------- feedback bit to a6, this bit is held in PRESET/1 condition by GO being inactive, and if the vector prom is disabled it is pulled to 1 [verified via tracing and schematics]
		*            |\-------- UNUSED, always 0
		*            \--------- UNUSED, always 0
		* The VT125 prom @ E41 is literally identical to this, the same exact part: 23-059B1
		*/
	ROM_LOAD( "wb8141_059b1.tbp18s22.pr5.ic111", 0x0000, 0x0100, CRC(4b63857a) SHA1(3217247d983521f0b0499b5c4ef6b5de9844c465))  // label verified from andy's board

	ROM_REGION( 0x100, "ras_erase", ROMREGION_ERASEFF )
	/* this is the "RAS/ERASE ROM" involved with driving the RAS lines and erasing VRAM dram (256*4, 82s129)
	 * control bits:
	 *            /CE1 ----- /WRITE aka WRITE L (pin 6 of vector rom after being latched by its ls273) [verified via tracing and vt125 schematic]
	 *            /CE2 ----- /ENA WRITE aka ENA WRITE L [verified via tracing and vt125 schematic]
	 *                       (INHIBIT WRITE L (pin 5 of ls74 to extreme left edge of translate prom) XOR STROBE D COUNT (pin 5 of ls74 near the 20ma port) (pin 3 of the ls86 above the crtc)
	 * addr bits: 76543210
	 *            |||||||\-- X'2 [verified via tracing]
	 *            ||||||\--- X'3 [verified via tracing]
	 *            |||||\---- register file bit 3/upper file MSB (DIR prom pin 4, ls191 2nd from left edge left of the hd46505 pin 9, upper ls670n pin 6, ls283 at the left edge left of the hd46505 pin 15) [verified via tracing]
	 *            ||||\----- (Y8 NOR !(X10 NAND X9)) (pins 4,5,6 of ls32 left of 8085, and pins 1,2 of the ls04 in the lower left corner, pins 10,9,8 of the ls00 at the left edge drawn from the ls74 between the 8251 and 8202) [verified via tracing]
	 *            |||\------ (X10 NOR Y10) (pins 10,9,8 of ls32 left of 8085) [verified via tracing]
	 *            ||\------- X11 (D out of ls191 left of ls191 left of hd46505) [verified via tracing]
	 *            |\-------- Y11 (D out of ls191 left of hd46505) [verified via tracing]
	 *            \--------- ERASE L/d5 on the vector rom [verified via tracing]
	 * data bits: 3210
	 *            |||\-- /WE for VRAM Attribute bits
	 *            ||\--- /WE for VRAM bits 0-3 (leftmost bits, first to be shifted out)
	 *            |\---- /WE for VRAM bits 4-7
	 *            \----- /WE for VRAM bits 8-11 (rightmost bits, last to be shifted out)
	 * The VT125 prom E93 is mostly equivalent to the ras/erase prom; On the vt125 version, the inputs are:
	 *  (X'10 NOR X'11)
	 *  (Y9 NOR Y10)
	 *  Y11
	 *  X8 (aka PX8)
	 *  X9 (aka PX9)
	 *  ERASE L
	 *  X'3
	 *  X'2
	 * and the outputs are:
	 *  MWR 2
	 *  MWR 1
	 *  MWR 0
	 * since the vt125 has ram laid out differently than the vk100 and
	   only has 3 banks (but 2 planes of 3 banks), I (LN) assume there
	   are four wr banks on the v100, one per nybble, and the X'3 and
	   X'2 inputs lend credence to this.
	 *
	 */
	ROM_LOAD( "wb8151_573a2.mmi6301.pr3.ic41", 0x0000, 0x0100, CRC(75885a9f) SHA1(c721dad6a69c291dd86dad102ed3a8ddd620ecc4)) // Stamp/silkscreen: "WB8151 // 573A2" (23-573A2), 82S129 equivalent @ E41
	// label verified from nigwil's and andy's board

	ROM_REGION( 0x100, "vector", ROMREGION_ERASEFF )
	// WARNING: it is possible that the first two bytes of this prom are bad!
	// The PROM on andy's board appears to be damaged, this will need to be redumped from another board.
	/* this is the "VECTOR ROM" (256*8, 82s135) which runs the vector generator state machine
	 * the vector rom bits are complex and are unfortunately poorly documented
	 * in the tech manual. see figure 5-23.
	 * control bits:
	 *            /CE1 ----- /GO aka GO L [verified via tracing]
	 *            /CE2 ----- GND [verified via tracing]
	 * addr bits: 76543210
	 *            ||||\\\\-- To sync counter, which counts 0xC 0x3 0x2 0x1 0x0 0x5 0x4 0xB 0xA 0x9 0x8 0xD in that order
	 *            |||\------ (MODE 0 NAND GO) \_This effectively means when GO is low, these bits are both 1, when GO is high, these bits are the inverse of the MODE bits; this is DIFFERENT from the vt125, and was probably removed as unnecessary given that /GO is an enable for the vector rom anyway [verified via tracing]
	 *            ||\------- (MODE 1 NAND GO) /
	 *            |\-------- ? C OUT (when set, only one /LD ERROR pulse occurs instead of two)
	 *            \--------- ? FINISH (/LD ERROR only goes active (low) when this is unset)
	 *
	 * data bits: 76543210
	 *            |||||||\-- /WRITE aka WRITE L (fig 5-20, page 5-32, writes the post-pattern-converted value back to vram at X,Y)
	 *            ||||||\--- DONE L [verified via tracing]
	 *            |||||\---- VECTOR CLK aka V CLK [verified via tracing]
	 *            ||||\----- /LD ERROR aka STROBE ERROR L (strobes the adder result value into the vgERR register)
	 *            |||\------ D LOAD [by process of elimination and limited tracing]
	 *            ||\------- ERASE L (latched, forces a4 on the sync rom low and also forces a7 on the ras/erase rom; the counter rom may be involved in blanking all of vram) [verified via tracing]
	 *            |\-------- C0 aka C IN (high during DVM read, low otherwise, a carry in to the adder so DVM is converted from 1s to 2s complement)
	 *            \--------- SHIFT ENA [verified via tracing]
	 * The data bits all have pull-ups to +5v if the /CE1 pin is not low
	 * According to the VT125 tech manual (vt100 tech manual rev 3, page 6-85) the 8 signals here are:
	 * ERASE L - d5
	 * SHIFT ENA - d7
	 * C IN - d6
	 * D LOAD - d4
	 * WRITE L - d0
	 * DONE L - d1
	 * VECTOR CLK - d2
	 * STROBE ERROR L - d3
	 *
	 * The VT125 prom E71 and its latch E70 is mostly equivalent to the vector prom, but the address order is different
	 */
	ROM_LOAD( "wb8146_058b1.mmi6309.pr1.ic99", 0x0000, 0x0100, BAD_DUMP CRC(71b01864) SHA1(e552f5b0bc3f443299282b1da7e9dbfec60e12bf))  // label verified from nigwil's and andy's board

	ROM_REGION( 0x20, "sync", ROMREGION_ERASEFF )
	/* this is the "SYNC ROM" == mb6331 (32x8, 82s123)
	 * It generates the ram RAS/CAS and a few other signals, see figure 5-20 on page 5-32
	 * The exact pins for each signal are not documented.
	 * control bits:
	 *            /CE1 -- GND(Unused)
	 * addr bits: 43210
	 *            |\\\\-- To sync counter, which counts 0xC 0x3 0x2 0x1 0x0 0x5 0x4 0xB 0xA 0x9 0x8 0xD in that order
	 *            \------ comes from the gated ERASE L/d5 from the vector rom, only low during VG_MODE == ER (ERase Screen) [verified via tracing]
	 *                      when high: the sync rom matches figure 5-20 (page 5-32) and 5-23 (page 5-38)
	 *                      when low: RA/RB is fixed on WOPS in the register file
	 *                                LD SHFR does NOT output pulses (effectively blanking the screen)
	 *                                WRT/RD is held at /RD so vg to vram do nothing,
	 *                                however while the crtc is refreshing the screen it instead is writing zeroes to every location
	 * data bits: 76543210
	 *            |||||||\-- WRT/RD (write high when x,y (vg) drives vram, read low when ma (crtc) drives vram)
	 *            ||||||\--- /RAS (for vram)
	 *            |||||\---- STROBE aka STROBE L aka VG STROBE(latches the carry bit from the adder to the direction rom AND causes the data at the X,Y address of vram to be read into the holding register before pattern is applied)
	 *            ||||\----- LD SHFR (loads the 12-bit shift register of data to be shifted to screen)
	 *            |||\------ /CAS (for vram)
	 *            ||\------- RA\__selects which slot of the 8x4 register file (du, dvm, dir, or wops) is selected
	 *            |\-------- RB/
	 *            \--------- SYNC (latches the EXECUTE signal from an EXEC * write to activate the GO signal and enable the Vector rom) [verified via tracing]
	 * The VT125 proms E64/E66 and their respective latches E65 and E83 are mostly equivalent to the sync rom
	 */
	ROM_LOAD( "wb8014_297a1.74s288.pr6.ic89", 0x0000, 0x0020, CRC(e2f7c566) SHA1(a4c3dc5d07667141ad799168a862cb3c489b4934)) // label verified from nigwil's and andy's board
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  STATE        INIT        COMPANY                          FULLNAME        FLAGS */
COMP( 1980, vk100, 0,      0,      vk100,   vk100, vk100_state, empty_init, "Digital Equipment Corporation", "VK100 'GIGI'", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)



vocalizer.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Devin Acker
/***************************************************************************

    Breakaway Vocalizer 1000

    Voice-powered synth and MIDI controller. ES5503 "DOC"-based.

    To enable MIDI input, press "Option" and then "Melody Guide".

    U11: 512kbit program ROM
    U12: Sony CXK5864PM-15L (64kbit SRAM)
    U13: Motorola MC68B09EP
    U17: NCR E106-71 609-0381069 (ES5503 compatible/clone)
    U18: 512kbit wave ROM
    U19: Breakaway GA1 NCR0880995
    Y1: 8.000 MHz

    TODO:
    - Microphone input. Data is read by function at $9BFD (after FIRQ_MIC)
      and then processed by function at $C705.
    - Instruments that use the sync/AM bit sound weird (need hardware recordings)
    - Link cable (uses UART 0)
    - Song cartridges (connected to CPU at $0000-3fff)
    - Instrument cartridges (connected to DOC at $10000+)

    Misc. notes:
    - Hold "Cursor Left" / "Delete" on boot to run a self test and re-init NVRAM.
    - Hold the "Jazz" style button on boot to run a key/button test.
      Press all buttons (in any order), including both handset buttons,
      then press any button to exit.
    - Press "Option" and then "Master Volume -" to display the ROM version.

***************************************************************************/

#include "emu.h"

#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/m6809/m6809.h"
#include "machine/nvram.h"
#include "sound/es5503.h"
#include "video/hd44780.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

//#define VERBOSE 1
#include "logmacro.h"

class vocalizer_uart_device : public device_t, public device_serial_interface
{
public:
	vocalizer_uart_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);

	auto tx_cb() { return m_tx_cb.bind(); }
	auto tx_irq_cb() { return m_tx_irq_cb.bind(); }
	auto rx_irq_cb() { return m_rx_irq_cb.bind(); }

	u8 read() { m_rx_irq_cb(0); return get_received_char(); }
	void write(u8 data) { m_tx_irq_cb(0); transmit_register_setup(data); }

protected:
	virtual void device_start() override {}
	virtual void device_reset() override ATTR_COLD;

	virtual void tra_callback() override { m_tx_cb(transmit_register_get_data_bit()); }
	virtual void tra_complete() override { m_tx_irq_cb(1); }
	virtual void rcv_complete() override { receive_register_extract(); m_rx_irq_cb(1); }

	devcb_write_line m_tx_cb;
	devcb_write_line m_tx_irq_cb;
	devcb_write_line m_rx_irq_cb;
};

DEFINE_DEVICE_TYPE(VOCALIZER_UART, vocalizer_uart_device, "vocalizer_uart", "Vocalizer 1000 UART")

vocalizer_uart_device::vocalizer_uart_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock):
	device_t(mconfig, VOCALIZER_UART, tag, owner, clock),
	device_serial_interface(mconfig, *this),
	m_tx_cb(*this),
	m_tx_irq_cb(*this),
	m_rx_irq_cb(*this)
{
}

void vocalizer_uart_device::device_reset()
{
	set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1);
	set_rate(31250);
	m_tx_irq_cb(0);
	m_rx_irq_cb(0);
}

namespace {

//**************************************************************************
class vocalizer_state : public driver_device
{
public:
	vocalizer_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_lcdc(*this, "lcdc"),
		m_es5503(*this, "es5503"),
		m_uart(*this, "uart%u", 0),
		m_bank(*this, "rombank"),
		m_keys(*this, "IN%u", 0)
	{ }

	void vocalizer(machine_config &config);

	void vocalizer_palette(palette_device &palette) const;
	HD44780_PIXEL_UPDATE(lcd_pixel_update);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	enum
	{
		FIRQ_TX0 = (1<<0), // UART 0 Tx (link cable)
		FIRQ_RX  = (1<<1), // UART Rx (either one)
		FIRQ_MIC = (1<<2), // audio input detection (triggered by input waveform edge, or something else?)
		FIRQ_TX1 = (1<<3)  // UART 1 Tx (MIDI)
	};

	void maincpu_map(address_map &map) ATTR_COLD;
	void sound_map(address_map &map) ATTR_COLD;

	template <int Shift> u8 time_r();

	u8 uart_rx_r();
	template <int Num> void uart_rx_irq(int state);

	u8 status_r();
	void config_w(u8 data) { m_config = data; }
	void input_sel_w(u8 data) { m_input_sel = BIT(data, 3, 4); }

	template<int Num> void firq_w(int state);
	void firq_ack_w(u8 data);
	void firq_mask_w(u8 data);
	void firq_update();

	void volume_w(u8 data);

	void apo_w(u8 data);

	template <int Num> void bank_w(u8 data) { m_bank->set_entry(Num); }

	required_device<mc6809e_device> m_maincpu;
	required_device<hd44780_device> m_lcdc;
	required_device<es5503_device> m_es5503;
	required_device_array<vocalizer_uart_device, 2> m_uart;
	required_memory_bank m_bank;
	required_ioport_array<12> m_keys;

	u8 m_power;
	u8 m_config;
	u8 m_input_sel;
	u8 m_firq_status, m_firq_mask;
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void vocalizer_state::maincpu_map(address_map &map)
{
	map(0x0000, 0x3fff).noprw(); // TODO: song cartridge
	map(0x4000, 0x40ff).mirror(0x0f00).rw(m_es5503, FUNC(es5503_device::read), FUNC(es5503_device::write));
	map(0x5000, 0x5000).mirror(0x0ff0).r(FUNC(vocalizer_state::uart_rx_r));
	map(0x5000, 0x5000).mirror(0x0ff0).w(m_uart[0], FUNC(vocalizer_uart_device::write));
	map(0x5001, 0x5001).mirror(0x0ff0).rw(FUNC(vocalizer_state::status_r), FUNC(vocalizer_state::config_w));
	map(0x5002, 0x5002).mirror(0x0ff0).rw(FUNC(vocalizer_state::time_r<8>), FUNC(vocalizer_state::input_sel_w));
	map(0x5003, 0x5003).mirror(0x0ff0).r(FUNC(vocalizer_state::time_r<0>));
	map(0x5003, 0x5003).mirror(0x0ff0).w(m_uart[1], FUNC(vocalizer_uart_device::write));
	map(0x5004, 0x5005).mirror(0x0ff0).nopr(); // TODO: audio in amplitude
	map(0x5004, 0x5004).mirror(0x0ff0).w(FUNC(vocalizer_state::firq_ack_w));
	map(0x5005, 0x5005).mirror(0x0ff0).w(FUNC(vocalizer_state::firq_mask_w));
	map(0x5006, 0x5007).mirror(0x0ff0).nopr(); // TODO: audio in freq/edge counter
	map(0x5006, 0x5006).mirror(0x0ff0).nopw(); // TODO: mic input gain
	map(0x5007, 0x5007).mirror(0x0ff0).w(FUNC(vocalizer_state::volume_w));
	map(0x5008, 0x5008).mirror(0x0ff0).portr("PORT8");
	map(0x5009, 0x5009).mirror(0x0f70).rw(m_lcdc, FUNC(hd44780_device::control_r), FUNC(hd44780_device::control_w));
	map(0x500a, 0x500a).mirror(0x0ff0).nopw(); // ?
	map(0x500b, 0x500b).mirror(0x0f30).w(FUNC(vocalizer_state::apo_w));
	map(0x5089, 0x5089).mirror(0x0f70).rw(m_lcdc, FUNC(hd44780_device::data_r), FUNC(hd44780_device::data_w));
	map(0x508b, 0x508b).mirror(0x0f30).w(FUNC(vocalizer_state::bank_w<0>));
	map(0x50cb, 0x50cb).mirror(0x0f30).w(FUNC(vocalizer_state::bank_w<1>));
	map(0x6000, 0x7fff).ram().share("nvram");
	map(0x8000, 0xffff).bankr("rombank");
}

void vocalizer_state::sound_map(address_map &map)
{
	map(0x00000, 0x0ffff).rom().region("es5503", 0);
	map(0x10000, 0x1ffff).nopr(); // TODO: instrument cartridge
}

//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( vocalizer )
	PORT_START("IN0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Drums Only")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Song (Country)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Song (Latin 2)")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Song (Rock 4)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Full SmartSong")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Bass & Chord")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Drums & Bass")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Song 6 / Edit Rhythm")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Multi-Track")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Song 1 / Record")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Song 2 / Stop")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Song 3 / Play")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Song 4 / Track")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Song 5 / Edit Pitch")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Cart")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Demo")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Cursor Right / Tap")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_LEFT)  PORT_NAME("Cursor Left / Delete")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DOWN)  PORT_NAME("Cursor Down / No")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_UP)    PORT_NAME("Cursor Up / Yes")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tuning")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Song (Jazz)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Song (Soul 1)")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Song (Rock 1)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Option")
	PORT_BIT(0xe0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Stop/Start")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Song (Blues)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Song (Soul 2)")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Song (Rock 2)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Song Variations")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Key +")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tempo +")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Ending")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Song (Reggae)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Song (Latin 1)")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Song (Rock 3)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Rhythm Variations")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Key -")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tempo -")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Melody Guide")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Master Volume -")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Instrument Volume -")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Voice Volume -")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Octave -")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Harmony")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Slide")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Piano)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Spirit)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Bells)")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Violin)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Sax)")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Flute)")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Electric Guitar)")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Electric Piano)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Warp)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Marimba)")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Cello)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Trumpet)")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Whistle)")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Fuzz Guitar)")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Piano Strings)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Fusion)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Steel Drum)")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Bass)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Trombone)")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Clarinet)")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Electric Bass)")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN10")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Organ)")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Sara)")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Synth Drums)")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Synth Strings)")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Synth Brass)")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Harmonica)")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Tone (Slap Bass)")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("IN11")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Voice Guide")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Master Volume +")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Instrument Volume +")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Voice Volume +")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Octave +")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Echo")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Chorus")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("PORT8") // handset and power button
	PORT_BIT(0x01, IP_ACTIVE_LOW,  IPT_BUTTON1) PORT_NAME("Octave + (Hold)")
	PORT_BIT(0x06, IP_ACTIVE_LOW,  IPT_UNUSED )
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) // power
	PORT_BIT(0x10, IP_ACTIVE_LOW,  IPT_BUTTON2) PORT_NAME("Slide (Hold)")
	PORT_BIT(0xe0, IP_ACTIVE_LOW,  IPT_UNUSED )
INPUT_PORTS_END


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void vocalizer_state::vocalizer_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148)); // background
	palette.set_pen_color(1, rgb_t( 63,  59,  62)); // LCD pixel on
	palette.set_pen_color(2, rgb_t(131, 136, 139)); // LCD pixel off
}

HD44780_PIXEL_UPDATE( vocalizer_state::lcd_pixel_update )
{
	// char size is 5x8
	if (!m_power || x > 4 || y > 7)
		return;

	if (line < 2 && pos < 8)
		bitmap.pix(1 + y, 1 + line*48 + pos*6 + x) = state ? 1 : 2;
}

//**************************************************************************
void vocalizer_state::machine_start()
{
	m_bank->configure_entries(0, 2, memregion("maincpu")->base(), 0x8000);

	save_item(NAME(m_power));
	save_item(NAME(m_config));
	save_item(NAME(m_input_sel));
	save_item(NAME(m_firq_status));
	save_item(NAME(m_firq_mask));
}

//**************************************************************************
void vocalizer_state::machine_reset()
{
	m_power = 1;
	m_config = 0;
	m_input_sel = 0;
	m_firq_status = m_firq_mask = 0;
}

//**************************************************************************
template <int Shift>
u8 vocalizer_state::time_r()
{
	// used for tempo, auto power off timer, etc
	return machine().time().as_ticks(2'000'000) >> Shift;
}

//**************************************************************************
u8 vocalizer_state::uart_rx_r()
{
	return m_uart[BIT(m_config, 0)]->read();
}

//**************************************************************************
template <int Num>
void vocalizer_state::uart_rx_irq(int state)
{
	if (Num == BIT(m_config, 0))
		firq_w<FIRQ_RX>(state);
}

//**************************************************************************
u8 vocalizer_state::status_r()
{
	u8 status = 0x80;
	u8 keys = 0;

	if (m_input_sel == 0xf)
	{
		// selecting row 15 scans all rows together...
		for (int i = 0; i < m_keys.size(); i++)
			keys |= m_keys[i]->read();
		// and also inverts the ready bit, apparently
		status = 0x00;
	}
	else if (m_input_sel < m_keys.size())
	{
		keys = m_keys[m_input_sel]->read();
	}

	for (int i = 7; i >= 0; i--)
	{
		if (BIT(keys, i))
		{
			status |= i;
			status ^= 0x80;
			break;
		}
	}

	status |= (m_firq_status & 0xf) << 3;

	return status;
}

//**************************************************************************
template <int Num>
void vocalizer_state::firq_w(int state)
{
	if (state)
	{
		m_firq_status |= Num;
		firq_update();
	}
}

//**************************************************************************
void vocalizer_state::firq_ack_w(u8 data)
{
	m_firq_status &= BIT(~data, 3, 4);
	firq_update();
}

//**************************************************************************
void vocalizer_state::firq_mask_w(u8 data)
{
	m_firq_mask = BIT(data, 3, 4);
	firq_update();
}

//**************************************************************************
void vocalizer_state::firq_update()
{
	if (m_firq_status & ~m_firq_mask & 0xf)
		m_maincpu->set_input_line(M6809_FIRQ_LINE, ASSERT_LINE);
	else
		m_maincpu->set_input_line(M6809_FIRQ_LINE, CLEAR_LINE);
}

//**************************************************************************
void vocalizer_state::volume_w(u8 data)
{
	// TODO: increase overall gain? per-voice volume almost never goes above 0x40
	m_es5503->set_output_gain(ALL_OUTPUTS, (float)data / 255);
}

//**************************************************************************
void vocalizer_state::apo_w(u8 data)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
	m_lcdc->reset();
	m_es5503->reset();
	m_power = 0;
}

//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void vocalizer_state::vocalizer(machine_config &config)
{
	MC6809E(config, m_maincpu, 8_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &vocalizer_state::maincpu_map);

	NVRAM(config, "nvram");

	VOCALIZER_UART(config, m_uart[0]);
	m_uart[0]->tx_irq_cb().set(FUNC(vocalizer_state::firq_w<FIRQ_TX0>));
	m_uart[0]->rx_irq_cb().set(FUNC(vocalizer_state::uart_rx_irq<0>));
	// TODO: link cable on uart 0

	VOCALIZER_UART(config, m_uart[1]);
	m_uart[1]->tx_irq_cb().set(FUNC(vocalizer_state::firq_w<FIRQ_TX1>));
	m_uart[1]->rx_irq_cb().set(FUNC(vocalizer_state::uart_rx_irq<1>));

	midi_port_device& mdin(MIDI_PORT(config, "mdin", midiin_slot, "midiin"));
	mdin.rxd_handler().set(m_uart[1], FUNC(vocalizer_uart_device::rx_w));

	MIDI_PORT(config, "mdout", midiout_slot, "midiout");
	m_uart[1]->tx_cb().set("mdout", FUNC(midi_port_device::write_txd));

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(6*16 + 1, 10);
	screen.set_visarea_full();
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(vocalizer_state::vocalizer_palette), 3);

	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 8);
	m_lcdc->set_pixel_update_cb(FUNC(vocalizer_state::lcd_pixel_update));

	SPEAKER(config, "speaker", 2).front();

	ES5503(config, m_es5503, 8_MHz_XTAL).set_channels(16);
	m_es5503->set_addrmap(0, &vocalizer_state::sound_map);
	m_es5503->irq_func().set_inputline(m_maincpu, M6809_IRQ_LINE);
	for (int i = 0; i < 16; i++)
	{
		if (i <= 8)
			m_es5503->add_route(i, "speaker", 1.0, 0);
		else if (i < 15)
			m_es5503->add_route(i, "speaker", (15 - i) / 7.0, 0);

		if (i >= 8)
			m_es5503->add_route(i, "speaker", 1.0, 1);
		else if (i > 0)
			m_es5503->add_route(i, "speaker", i / 8.0, 1);
	}
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( vocalizer )
	ROM_REGION(0x10000, "maincpu", 0) // "Version 1.5g"
	ROM_LOAD("system v-e cs=e700.u11", 0x00000, 0x10000, CRC(ead225ba) SHA1(89ebda078a98babd6513953ee29db4e31e06db83))

	ROM_REGION(0x10000, "es5503", 0)
	ROM_LOAD("waverom v-a cs=7018.u18", 0x00000, 0x10000, CRC(55567c6d) SHA1(5c67997301f4d3bdc2e5dd893b0234425eb374f0))

	ROM_REGION(0x2000, "nvram", 0)
	ROM_LOAD("init_ram.bin", 0x0000, 0x2000, CRC(f1e85f3d) SHA1(7897b488d128e044af737e7e6cbb7857d92e4891))
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY                    FULLNAME          FLAGS
SYST( 1988, vocalizer, 0,      0,      vocalizer, vocalizer, vocalizer_state, empty_init, "Breakaway Music Systems", "Vocalizer 1000", MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING | MACHINE_NODEVICE_MICROPHONE )



votrhv.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Jonathan Gevaryahu
// thanks-to:Kevin Horton
/******************************************************************************
 *
 *  Votrax/Phonic Mirror HandiVoice models HC-110 and HC-120
 *
 *****************************************************************************/
/*
    The HC-110 and HC-120 both consist of 3 boards:
    1. An 'embedded system' 6800 board with two output latches, one input
       latch, and two 'resume/reset' latches which pull the 6800 is out of
       reset on a rising edge, as a form of power saving. (pcb 1816?)
    2. A keyboard handler pcb; this is different on the HC-110 (where it is
       made by a 3rd party company for the 128-key input) and the HC-120
       (where it was made by votrax/phonic mirror, pcb 1817?)
    3. The voice synthesizer pcb 1818c, encased in epoxy. It is a discrete
       synthesizer roughly equivalent to an SC-01 or VSK, it has external
       pins to allow control of speech pitch and rate in addition to the
       typical 2 inflection pins.

    Notes: Electronic Arrays, Inc. who made the EA8316 CMOS mask ROMs was
    bought out by NEC in 1978.

    TODO: 1818c discrete speech synth device

    The 1818C SYNTHESIZER BOARD is mentioned as one of two speech
    synthesizers described in US Patent 4,130,730 in figures 3, 4a and 4b
    (the Votrax VSK/VSL is the other device, described in figures 1, 2a,
    and 2b)
    The 1818C uses three Motorola MCM14524 256x4 CMOS MASK ROMs to hold
    the phoneme parameters.
    (This is mentioned in 4,130,730 column 11 line 31.)

    Motorola MCM14524:
                +---..---+
        /CLK -> |  1  16 | -- VDD (up to 18v)
          CE -> |  2  15 | <- A0
          B0 <- |  3  14 | <- A1
          B1 <- |  4  13 | <- A7
          B2 <- |  5  12 | <- A6
          B3 <- |  6  11 | <- A5
          A2 -> |  7  10 | <- A4
     (0v)VSS -- |  8   9 | <- A3
                +--------+
    see http://bitsavers.org/components/motorola/_dataBooks/1978_Motorola_CMOS_Data_Book.pdf
    page 488
*/

/* Core includes */
#include "emu.h"
#include "cpu/m6800/m6800.h"
#include "machine/input_merger.h"
#include "sound/votrax.h"
#include "speaker.h"

#include "hc110.lh"

// defines

#define LOG_INPUT     (1U << 1)
#define LOG_LATCHX    (1U << 2)
#define LOG_LATCHY    (1U << 3)

#define VERBOSE (LOG_GENERAL)
//#define LOG_OUTPUT_FUNC printf
#include "logmacro.h"

namespace {

#define LOGINP(...) LOGMASKED(LOG_INPUT, __VA_ARGS__)
#define LOGLTX(...) LOGMASKED(LOG_LATCHX, __VA_ARGS__)
#define LOGLTY(...) LOGMASKED(LOG_LATCHY, __VA_ARGS__)

class votrhv_state : public driver_device
{
public:
	votrhv_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_reset(*this, "reset")
		, m_latchx(0)
		, m_latchy(0)
		, m_latcha_flop(false)
		, m_latchb_flop(false)
		, m_latcha_in(false)
		, m_latchb_in(false)
		, m_scanflag(false)
		, m_maincpu(*this, "maincpu")
		, m_votrax(*this, "votrax")
		, m_swarray(*this, "SW.%u", 0U)
		, m_leds(*this, "led_%u", 0U)
	{ }

	void votrhv(machine_config &config);
	void hc110(machine_config &config);

	void reset_counter(int state);
	void key_pressed(int state);
	void pho_done(int state);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<input_merger_device> m_reset;

	uint8_t m_latchx;
	uint8_t m_latchy;
	bool m_latcha_flop;
	bool m_latchb_flop;
	bool m_latcha_in;
	bool m_latchb_in;
	bool m_scanflag;

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<m6800_cpu_device> m_maincpu;
	required_device<votrax_sc01_device> m_votrax;

	TIMER_CALLBACK_MEMBER(resume_tick);

	optional_ioport_array<16> m_swarray;
	output_finder<5> m_leds;

	virtual void key_check();
	virtual void latchx_w(uint8_t data);
	void latchy_w(uint8_t data);
	virtual uint8_t input_r();
	uint8_t latcha_rst_r();
	uint8_t latchb_rst_r();

	emu_timer* m_resume_timer = nullptr;
};

class hc120_state : public votrhv_state
{
public:
	hc120_state(const machine_config &mconfig, device_type type, const char *tag)
		: votrhv_state(mconfig, type, tag)
	{ }

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual void key_check() override;
	virtual uint8_t input_r() override;
	virtual void latchx_w(uint8_t data) override;

	TIMER_CALLBACK_MEMBER(scan_keys);

	emu_timer* m_scan_timer = nullptr;
};

/******************************************************************************
 Address Maps
******************************************************************************/

/* This memory map is for the CPU board which is shared between HC-110 and HC-120
  15 14 13 12   11 10  9  8    7  6  5  4    3  2  1  0
   x  0  0  0    0  x  x  x    *  *  *  *    *  *  *  *    RW RAM (2x Harris MI-6561-9 256x4 SRAM, wired in parallel)
   x  0  0  0    1  x  x  x    x  x  x  x    x  x  x  x    open bus
   x  0  0  1    0  x  x  x    x  x  x  x    x  x  x  x    R Input Latch
   x  0  0  1    1  x  x  x    x  x  x  x    x  x  x  x    open bus
   x  0  1  0    0  x  x  x    x  x  x  x    x  x  x  x    W Latch X out
   x  0  1  0    1  x  x  x    x  x  x  x    x  x  x  x    open bus
   x  0  1  1    0  x  x  x    x  x  x  x    x  x  x  x    W Latch Y out
   x  0  1  1    1  x  x  x    x  x  x  x    x  x  x  x    open bus
   x  1  0  0    0  *  *  *    *  *  *  *    *  *  *  *    R ROM0
   x  1  0  0    1  *  *  *    *  *  *  *    *  *  *  *    R ROM1
   x  1  0  1    0  *  *  *    *  *  *  *    *  *  *  *    R ROM2
   x  1  0  1    1  *  *  *    *  *  *  *    *  *  *  *    R ROM3
   x  1  1  0    0  x  x  x    x  x  x  x    x  x  x  x    * Reset Latch A clear
   x  1  1  0    1  x  x  x    x  x  x  x    x  x  x  x    * Reset Latch B clear
   x  1  1  1    0  x  x  x    x  x  x  x    x  x  x  x    open bus
   x  1  1  1    1  0  0  x    x  x  x  x    x  x  x  x    open bus
   x  1  1  1    1  0  1  *    *  *  *  *    *  *  *  *    R PROM1 (unpopulated)
   x  1  1  1    1  1  0  *    *  *  *  *    *  *  *  *    R PROM2
   x  1  1  1    1  1  1  *    *  *  *  *    *  *  *  *    R PROM3
*/

void votrhv_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x00ff).mirror(0x8700).ram(); /* RAM */
	// 0800-0fff open bus
	map(0x1000, 0x1000).mirror(0x87ff).r(FUNC(votrhv_state::input_r));
	// 1800-1fff open bus
	map(0x2000, 0x2000).mirror(0x87ff).w(FUNC(votrhv_state::latchx_w));
	// 2800-2fff open bus
	map(0x3000, 0x3000).mirror(0x87ff).w(FUNC(votrhv_state::latchy_w));
	// 3800-3fff open bus
	map(0x4000, 0x5fff).mirror(0x8000).rom().region("maskrom", 0);
	map(0x6000, 0x6000).mirror(0x87ff).r(FUNC(votrhv_state::latcha_rst_r));
	map(0x6800, 0x6800).mirror(0x87ff).r(FUNC(votrhv_state::latchb_rst_r));
	// 7000-77ff open bus
	// 7800-79ff open bus
	map(0x7a00, 0x7fff).mirror(0x8000).rom().region("bootrom", 0);
}


/******************************************************************************
 Input Ports
******************************************************************************/

static INPUT_PORTS_START(hc110)
	PORT_START("SW.0")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed)) // Talk
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed)) // Master Clear
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed)) // Clear
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed)) // Talk Repeat
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed)) // Level 1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed)) // Level 2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed)) // Level 3
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed)) // Level 4

	PORT_START("SW.1")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))

	PORT_START("SW.2")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))

	PORT_START("SW.3")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))

	PORT_START("SW.4")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))

	PORT_START("SW.5")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))

	PORT_START("SW.6")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))

	PORT_START("SW.7")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))

	PORT_START("SW.8")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))

	PORT_START("SW.9")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))

	PORT_START("SW.10")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))

	PORT_START("SW.11")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))

	PORT_START("SW.12")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))

	PORT_START("SW.13")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))

	PORT_START("SW.14")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))

	PORT_START("SW.15")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
INPUT_PORTS_END

static INPUT_PORTS_START(hc120)
	PORT_START("KEYPAD")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("M CLR") PORT_CODE(KEYCODE_SLASH_PAD) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CLR") PORT_CODE(KEYCODE_DEL_PAD) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SCAN") PORT_CODE(KEYCODE_ASTERISK) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed)) // some units have this button labeled "SCROLL" instead of "SCAN"
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("TALK REPT.") PORT_CODE(KEYCODE_MINUS_PAD) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("TALK") PORT_CODE(KEYCODE_PLUS_PAD) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER_PAD) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("9") PORT_CODE(KEYCODE_9_PAD) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("8") PORT_CODE(KEYCODE_8_PAD) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("7") PORT_CODE(KEYCODE_7_PAD) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("6") PORT_CODE(KEYCODE_6_PAD) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("5") PORT_CODE(KEYCODE_5_PAD) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("4") PORT_CODE(KEYCODE_4_PAD) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("3") PORT_CODE(KEYCODE_3_PAD) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("2") PORT_CODE(KEYCODE_2_PAD) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("1") PORT_CODE(KEYCODE_1_PAD) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("0") PORT_CODE(KEYCODE_0_PAD) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, FUNC(votrhv_state::key_pressed))
INPUT_PORTS_END

/******************************************************************************
 Timer and machine/start/reset handlers
******************************************************************************/

TIMER_CALLBACK_MEMBER(votrhv_state::resume_tick)
{
	// pull the cpu out of reset
	m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
}

TIMER_CALLBACK_MEMBER(hc120_state::scan_keys)
{
	// invert the scan state bit
	m_scanflag = !m_scanflag;
	m_scan_timer->adjust(attotime::from_seconds(1));
}

void votrhv_state::machine_start()
{
	m_resume_timer = timer_alloc(FUNC(votrhv_state::resume_tick), this);

	m_leds.resolve();

	save_item(NAME(m_latchx));
	save_item(NAME(m_latchy));
	save_item(NAME(m_latcha_flop));
	save_item(NAME(m_latchb_flop));
	save_item(NAME(m_latcha_in));
	save_item(NAME(m_latchb_in));
	save_item(NAME(m_scanflag));
}

void votrhv_state::machine_reset()
{
	m_resume_timer->adjust(attotime::never);
}

// hc-120 variant for scan_timer
void hc120_state::machine_start()
{
	votrhv_state::machine_start();

	m_scan_timer = timer_alloc(FUNC(hc120_state::scan_keys), this);
}

void hc120_state::machine_reset()
{
	m_scan_timer->adjust(attotime::from_seconds(1)); // hc-120 specific; adjustable, guessed 1hz to 2hz? needs measurement
}

/******************************************************************************
 Driver specific functions
******************************************************************************/

void votrhv_state::reset_counter(int state)
{
	if (state == CLEAR_LINE)
	{
		// if the timer is not already running, start it.
		if (m_resume_timer->remaining().is_never())
		{
			m_resume_timer->adjust(attotime::from_hz(2'000'000/2/0x20));
		}
	}
	else // state == ASSERT_LINE
	{
		m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
	}
}

void votrhv_state::key_pressed(int state)
{
	// If we got called here, a key was pressed or released somewhere on the keyboard, and we don't know where.
	// If, regardless of whether a key was pressed or released, our currently selected column has a non-zero
	// number of keys closed in it, we need to assert m_latchb_in and if its edge was rising with that assertion, set m_latchb_flop.
	LOGINP("key %s event\n", state?"down":"up");
	key_check();
}

void votrhv_state::pho_done(int state)
{
	bool rising_edge = (!m_latcha_in && (state == ASSERT_LINE));
	m_latcha_in = (state == ASSERT_LINE);
	if (rising_edge) // HACK: SC-01-A is in /ready state now
	{
		// clock the pho_done flipflop which sets its state to 1
		m_latcha_flop = true;
		// HACK: we manually clock the same old phoneme into the SC-01-A here, this is a hack since on the 1818C the pho_done signal is a free running counter and doesn't stop like the A/R latch on the SC-01[-A] does
		//m_votrax->inflection_w((m_latchy&0xc0)>>6);
		m_votrax->write(m_latchy&0x3f);
		m_reset->in_w<0>(1);
	}
}

void votrhv_state::key_check()
{
	uint8_t keys = m_swarray[m_latchx&0xf]->read();
	bool m_latchb_in_old = m_latchb_in;
	m_latchb_in = (keys == 0); // If the currently selected column has a non-zero number of keys set in it, the latchb_in state is false (KPRESS is active LOW), otherwise true
	if (!m_latchb_in_old && m_latchb_in) // on the rising edge of m_latchb_in, set the flipflop
	{
		// clock the key flipflop which sets its state to 1
		m_latchb_flop = true;
		m_reset->in_w<1>(1);
	}
	LOGINP("column %x keys read as %02x, latchb_in was %d now %d, latchb flop is %d\n", m_latchx&0xf, keys, m_latchb_in_old, m_latchb_in, m_latchb_flop);
}


void votrhv_state::latchx_w(uint8_t data)
{
	/* latchx output:
	 *  76543210
	 *  |||||||\- \.
	 *  ||||||\--  \ key input column select
	 *  |||||\---  /
	 *  ||||\---- /
	 *  |||\----- \ LED select 1/2/3/4
	 *  ||\------ /
	 *  |\------- Green status LED
	 *  \-------- Phoneme silence (ties pitch input of 1818C high thru a diode)
	 */
	m_latchx = data;
	LOGLTX("latchx written with value of %02x\n", m_latchx);
	for (int i = 0; i < 4; i++)
	{
		m_leds[i] = (((data >> 4) & 0x03) == (3 - i)) ? 1 : 0;
	}
	m_leds[4] = !BIT(data, 6);
	key_check();
}

void votrhv_state::latchy_w(uint8_t data)
{
	/* latchy output:
	 *  76543210
	 *  |||||||\- \.
	 *  ||||||\--  \.
	 *  |||||\---   \ 1818c phoneme select
	 *  ||||\----   /
	 *  |||\-----  /
	 *  ||\------ /
	 *  |\------- \ 1818c inflection select
	 *  \-------- /
	 */
	m_latchy = data;
	LOGLTY("latchy written with value of %02x\n", m_latchy);
	m_votrax->inflection_w((m_latchy&0xc0)>>6);
	m_votrax->write(m_latchy&0x3f);
}

uint8_t votrhv_state::input_r()
{
	/* input:
	 *  76543210
	 *  |||||||\- kbd decoded d0
	 *  ||||||\-- kbd decoded d1
	 *  |||||\--- kbd decoded d2
	 *  ||||\---- GND/unused
	 *  |||\----- /low_battery
	 *  ||\------ GND/unused
	 *  |\------- keyboard flipflop
	 *  \-------- phoneme flipflop
	 */
	uint8_t retval = 0;
	// now scan the currently selected column, emulating a CD4532 priority encoder where D7 beats D6 beats D5... etc
	uint8_t temp = m_swarray[m_latchx&0xf]->read();
	for (int i = 7; i >= 0; i--)
	{
		if (BIT(temp,i))
		{
			retval |= i;
			break;
		}
	}
	retval |= (m_latcha_flop?0x80:0x00) | (m_latchb_in?0x40:0x00) | 0x10;
	LOGINP("input_r read with latchx column set to %01x, returning value of %02x\n", m_latchx&0xf, retval);
	return retval;
}

uint8_t votrhv_state::latcha_rst_r()
{
	if(!machine().side_effects_disabled())
	{
		// reset the 0x80 flop
		m_latcha_flop = false;
		m_reset->in_w<0>(0);
	}
	return 0xff;
}

uint8_t votrhv_state::latchb_rst_r()
{
	if(!machine().side_effects_disabled())
	{
		// reset the 0x40 flop
		m_latchb_flop = false;
		m_reset->in_w<1>(0);
	}
	return 0xff;
}


// hc120 specific overrides
void hc120_state::key_check()
{
	uint16_t keys = ioport("KEYPAD")->read();
	bool m_latchb_in_old = m_latchb_in;
	m_latchb_in = (keys != 0); // If the input has a non-zero number of keys set in it, the latchb_in state is TRUE, on the HC-120.
	if (!m_latchb_in_old && m_latchb_in) // on the rising edge of m_latchb_in, set the flipflop
	{
		// clock the key flipflop which sets its state to 1
		m_latchb_flop = true;
		m_reset->in_w<1>(1);
	}
	LOGINP("keys read as %02x, latchb_in was %d now %d, latchb flop is %d\n", keys, m_latchb_in_old, m_latchb_in, m_latchb_flop);
}

void hc120_state::latchx_w(uint8_t data)
{
	/* latchx output:
	 *  76543210
	 *  |||||||\- LCD digit sel d0
	 *  ||||||\-- LCD digit sel d1
	 *  |||||\--- LCD digit sel d2
	 *  ||||\---- LCD digit sel d3
	 *  |||\----- LCD digit pos1 en
	 *  ||\------ LCD digit pos2 en
	 *  |\------- LCD digit pos3 en
	 *  \-------- LCD latch 4bit digitsel for extra io (colon and other segments on d2 d3)
	 */
	m_latchx = data;
	LOGLTX("latchx written with value of %02x\n", m_latchx);
}

uint8_t hc120_state::input_r()
{
	/* input:
	 *  76543210
	 *  |||||||\- kbd decoded d0
	 *  ||||||\-- kbd decoded d1
	 *  |||||\--- kbd decoded d2
	 *  ||||\---- kbd decoded d3
	 *  |||\----- /low_battery
	 *  ||\------ scan rate oscillator input
	 *  |\------- keyboard flipflop
	 *  \-------- phoneme flipflop
	 */
	uint8_t retval = 0x0f;
	// now scan the currently selected column, emulating a Harris HD-0165
	// keyboard encoder including its weird inverted outputs which presumably
	// mask each other when multiple keys are pressed (see the note on the
	// datasheet in the 1975 Harris Integrated Circuits catalog about
	// "Erroneous Data" when the /KRO output is active
	uint16_t temp = ioport("KEYPAD")->read();
	for (int i = 0; i < 16; i++)
	{
		if (BIT(temp,i))
		{
			retval &= (15-i);
			break; // HACK: this is wrong for the HD-0165, which produces mangled data if more than one key is pressed simultaneously; later HC-120 units had a grid/collimator 'guard' on top of the keypad to reduce the chance of multiple keys being hit. Here we just give priority to the lowest bit active.
		}
	}
	retval |= (m_latcha_flop?0x80:0x00) | (m_latchb_in?0x40:0x00) | (m_scanflag?0x20:0x00) | 0x10;
	LOGINP("input_r read, returning value of %02x\n", retval);
	return retval;
}

/******************************************************************************
 Machine Drivers
******************************************************************************/

void votrhv_state::votrhv(machine_config &config)
{
	/* basic machine hardware */
	// ~1MHz done using two 74L123 multivibrators with cap 100pf res 11k, which each oscillate at 1.8-2.5mhz
	// since you need two clock phases for the 6800, each multivibrator does one phase and the falling edge of one triggers the other, so the actual clock rate is half the rate of each
	M6800(config, m_maincpu, 2'000'000 / 2 );
	m_maincpu->set_addrmap(AS_PROGRAM, &votrhv_state::mem_map);

	INPUT_MERGER_ALL_LOW(config, m_reset).output_handler().set(FUNC(votrhv_state::reset_counter));

	/* video hardware */
	//config.set_default_layout(layout_votrhv);

	/* serial hardware */

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	// TEMPORARY HACK until 1818c device is done
	VOTRAX_SC01A(config, m_votrax, 720000);
	m_votrax->ar_callback().set(FUNC(votrhv_state::pho_done));
	m_votrax->add_route(ALL_OUTPUTS, "mono", 1.00);
}

void votrhv_state::hc110(machine_config &config)
{
	votrhv(config);

	config.set_default_layout(layout_hc110);
}


/******************************************************************************
 ROM Definitions
******************************************************************************/

ROM_START(hc110)
	ROM_REGION(0x8000, "maskrom", 0) // 4x EA8316 (2316 equivalent) CMOS Mask ROMs
	//ROM_LOAD("ea8316e030.ic9", 0x0000, 0x0800, CRC(fd8cbf7d) SHA1(a2e1406c498a1821cacfcda254534f8e8d6b8260)) // used on older firmware?
	ROM_LOAD("ea8316e144.ic9", 0x0000, 0x0800, CRC(636415ee) SHA1(9699ea75eed566447d8682f52665b01c1e876981))
	ROM_LOAD("ea8316e031.ic8", 0x0800, 0x0800, CRC(f2de4e3b) SHA1(0cdc71a4d01d73e403cdf283c6eeb53f97ca5623))
	ROM_LOAD("ea8316e032.ic7", 0x1000, 0x0800, CRC(5df1270c) SHA1(5c81fcb2bb2c0bf509aa9fc11a92071cd469e407))
	ROM_LOAD("ea8316e033.ic6", 0x1800, 0x0800, CRC(0d7e246c) SHA1(1454c6c7ef3743320443c7bd1f37df6a25ff7795))

	ROM_REGION(0x0600, "bootrom", 0) // 2x 512x8 SN74S472 PROMs
	// ic12 is unpopulated
	ROM_LOAD("7031r2.sn74s472.ic11", 0x0200, 0x0200, CRC(6ef744c9) SHA1(6a92e520adb3c47b849241648ec2ca4107edfd8f))
	ROM_LOAD("7031r3.sn74s472.ic10", 0x0400, 0x0200, CRC(0800b0e6) SHA1(9e0481bf6c5feaf6506ac241a2baf83fb9342033))

	ROM_REGION16_BE(0x200, "s1818c", 0) // 1818C SYNTHESIZER BOARD; MCM14524 CMOS 256x4 mask ROMs holding the phoneme data
	ROMX_LOAD("scm46109pk.mcm14524.u30", 0x000, 0x100, CRC(6a1292fe) SHA1(67c8ac71e22de134a651dd5cad06d26b27894154), ROM_SKIP(1) | ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO)
	ROMX_LOAD("scm46110pk.mcm14524.u23", 0x001, 0x100, CRC(edc7bf31) SHA1(24a585b67ce246de5ef6c2cc024d91052b473816), ROM_SKIP(1) | ROM_NIBBLE | ROM_SHIFT_NIBBLE_HI)
	ROMX_LOAD("scm46111pk.mcm14524.u20", 0x001, 0x100, CRC(4227f04e) SHA1(4393b0a9960cbae7768a27b32688f640e822f13e), ROM_SKIP(1) | ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO)
ROM_END

ROM_START(hc120) // ic10 and ic11 are Rev B? is there an older revision undumped?
	ROM_REGION(0x2000, "maskrom", 0) // 4x EA8316 (2316 equivalent) CMOS Mask ROMs
	ROM_LOAD("ea8316e030.ic9", 0x0000, 0x0800, CRC(fd8cbf7d) SHA1(a2e1406c498a1821cacfcda254534f8e8d6b8260))
	ROM_LOAD("ea8316e031.ic8", 0x0800, 0x0800, CRC(f2de4e3b) SHA1(0cdc71a4d01d73e403cdf283c6eeb53f97ca5623))
	ROM_LOAD("ea8316e032.ic7", 0x1000, 0x0800, CRC(5df1270c) SHA1(5c81fcb2bb2c0bf509aa9fc11a92071cd469e407))
	ROM_LOAD("ea8316e033.ic6", 0x1800, 0x0800, CRC(0d7e246c) SHA1(1454c6c7ef3743320443c7bd1f37df6a25ff7795))

	ROM_REGION(0x0600, "bootrom", 0) // 2x 512x8 SN74S472 PROMs
	// ic12 is unpopulated
	ROM_LOAD("7037__r2b.sn74s472.ic11", 0x0200, 0x0200, CRC(44de1bb1) SHA1(53e6811baf37af5da0648e906fee6c6acf259b82))
	ROM_LOAD("7037__r3b.sn74s472.ic10", 0x0400, 0x0200, CRC(688be8c7) SHA1(c9bdc7472cabcdddc23e63f45afbfcc835bb8f69))

	ROM_REGION16_BE(0x200, "s1818c", 0) // 1818C SYNTHESIZER BOARD; MCM14524 CMOS 256x4 mask ROMs holding the phoneme data
	ROMX_LOAD("scm46109pk.mcm14524.u30", 0x000, 0x100, CRC(6a1292fe) SHA1(67c8ac71e22de134a651dd5cad06d26b27894154), ROM_SKIP(1) | ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO)
	ROMX_LOAD("scm46110pk.mcm14524.u23", 0x001, 0x100, CRC(edc7bf31) SHA1(24a585b67ce246de5ef6c2cc024d91052b473816), ROM_SKIP(1) | ROM_NIBBLE | ROM_SHIFT_NIBBLE_HI)
	ROMX_LOAD("scm46111pk.mcm14524.u20", 0x001, 0x100, CRC(4227f04e) SHA1(4393b0a9960cbae7768a27b32688f640e822f13e), ROM_SKIP(1) | ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO)
ROM_END

} // anonymous namespace

/******************************************************************************
 Drivers
******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE INPUT  CLASS         INIT        COMPANY                 FULLNAME             FLAGS
COMP( 1978, hc110,   0,      0,      hc110,  hc110, votrhv_state, empty_init, "Votrax/Phonic Mirror", "HandiVoice HC-110", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
COMP( 1978, hc120,   hc110,  0,      votrhv, hc120, hc120_state,  empty_init, "Votrax/Phonic Mirror", "HandiVoice HC-120", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )



votrpss.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Jonathan Gevaryahu, Robbbert
/******************************************************************************
*
*  Votrax Personal Speech System Driver
*  By Jonathan Gevaryahu AKA Lord Nightmare
*  with help from Kevin 'kevtris' Horton
*  Special thanks to Professor Nicholas Gessler for loaning several PSS units
*
*  The votrax PSS was sold from before the 35th week of 1982 until october, 1990

Main xtal is 8MHz
AY-3-8910 and i8253 clock is running at 2 MHz (xtal/4)
Z80A is running at 4MHz (xtal/2)
clock dividers also generate the system reset signals for the 8251 and and a periodic IRQ at 122Hz (xtal/65536)

I8253:
Timer0 = Baud Clock, not gated (constantly on)
Timer1 = output to transistor chopper on clock input to sc-01-a to control pitch; gated by xtal/256
Timer2 = output to transistor chopper on output of sc-01-a to control volume; gated by xtal/4096

I8255 ports:
PortA 0:7 = pins 16 thru 23 of parallel port
PortB 0:7 = pins 6 thru 13 of parallel port
PortC =
    0 = NC
    1 = GND
    2 = pin 5 of parallel port
    3 = /RXINTEN
    4 = pin 15 of parallel port
    5 = pin 14 of parallel port through inverter
    6 = ay-3-8910 enable (which pin? BC1?)
    7 = input from parallel port pin 4 through inverter

AY-3-8910 I/O ports:
    IOA is in output mode
        IOA0-A5 = phoneme #
        IOA6 = strobe (SC-01)
        IOA7 = vochord control, 0 = off, 1 = on
    IOB is in input mode
        IOB0-IOB7 = dip switches

I8251 UART:
    RESET is taken from the same inverter that resets the counters

Things to be looked at:
- volume and pitch should be controlled by ppi outputs
- pit to be hooked up
- bit 0 of portc is not connected according to text above, but it
  completely changes the irq operation.

Notes:
- When Serial dip is chosen, you type the commands in, but you cannot see anything.
  If you enter text via parallel, it is echoed but otherwise ignored.
- When Parallel dip is chosen, you type the commands in, but again you cannot
  see anything.
- These operations ARE BY DESIGN. Everything is working correctly.
- Commands are case-sensitive.
- Some tests...
  - Say the time: EscT. (include the period)
  - Play some notes: !T08:1234:125:129:125:130. (then press enter)
- It is known from testing that the PSS will not work with a CMOS z80.
  - It is believed this has to do with the ram test code on startup expecting
    to read open bus as FF instead of whatever was last written by the z80,
    but this needs further code tracing/verification.
    (If this is actually the case, it can probably be worked around by pulling
    the data bus lines high with resistors)

******************************************************************************/

/*****************************************************************************
Notes about main PCB revisions:

PCB 1871G: (1982 or earlier?)
  has extensive rework around the 74ls02 (see below for before and after rework)
  manually added 3 resistors (brown black yellow gold) between gnd and pins 3,5, and vcc and pin 1 of resistor array
  manually added one resistor (yellow purple red gold) between pin 13 (connected to parallel port pin 16) and 26(gnd) of 8255
  Heatsink rivets are separated from pcb by two plastic washers, trace runs underneath and would short w/o washers
  Power plug has shield connected to board GND
  Chip behind serial port is an hcf4049 hex buffer
  74LS04 near xtal has pin 3 unconnected?
  hcf4049 behind serial port has pin 9 grounded
  diode piggybacking on resistor above 8255

PCB 1871H: (1982)
  74ls02 fixes from 1871G integrated on PCB (see below)
  extra array resistor integrated to replace manually added 3 resistors on other resistor array. hookup is basically the same
  resistor between pins 13 and 26 of 8255 integrated on pcb
  Heatsink rivets directly on pcb, no washers. trace has been rerouted so as not to short.
  power plug has shield connected to board gnd BUT was manually milled off so is connected to nothing.
  chip behind serial port is an 74C14/CD40106 hex schmitt trigger
  hookup of 74ls04 near xtal has pin 3 connected to pin 8 of schmitt trigger
  hookup of mc14106/hcf4049 and the 74ls04 has significantly changed? (see below)
  diode above 8255 is now only half piggybacking on resistor, now has a true attachment point on pcb for other end

PCB 1871J: (1987?)
  power plug shield no longer connects to gnd plane on board
  chip behind serial port is an mc14106 hex schmitt trigger (trivial change)

***REWORK on pcb 1871G:
*Before rework:
Ay-3-8910 connections:
pin 29 (BC1)  <->  pin 12 (C/D) of 8251

"74ls02" (clearly a ?74ls00? originally) connections:
pin 1 (Y1)->  pin 14 (PC0) of 8255
pin 2 (A1)<-  GND
pin 3 (B1)<-  GND
pin 4 (Y2)->  pin 7 of left edge of potted block, and to pin 24 (/A9) of ay-3-8910
pin 5 (A2)<-  pin 19 of left edge of potted block and pin 23 (/WR) of 8253
pin 6 (B2)<-  pin 27 (BDIR) of ay-3-8910
pin 7   GND
pin 8 (Y3)-> pin 1 (A1) of 74ls04 near xtal
pin 9 (A3)<-  8251 pin 14 (RxRDY)
pin 10 (B3)<- GND
pin 11 (Y4)-> pin 17 (PC3) of 8255
pin 12 (A4)<- pin 11 of left edge of potted block
pin 13 (B4)<- pin 2 (/Y1) of 74ls04 near xtal
pin 14  VCC

resistor array (08-2-393 // 8042) connections:
pin 1: serial port pin 11
pin 2: hcf4049 pin 5 (A2)
pin 3: serial port pin 9(and 6?)
pin 4: hcf4049 pin 3 (A1)
pin 5: serial port pin 6(and 9?)
pin 6: hcf4049 pin 7 (A3)
pin 7: N/C

Parallel port pin 16 is connected to pin 13 on 8255

*After rework:
Ay-3-8910 connections:
pin 29 (BC1)  <-> 74ls02 pin 13

74ls02 connections:
pin 1 (Y1)->  pin 11 of left edge of potted block
pin 2 (A1)<-  pin 14 (PC0) of 8255
pin 3 (B1)<-  pin 2 (/Y1) of 74ls04 near xtal
pin 4 (Y2)->  pin 27 (BDIR) of ay-3-8910
pin 5 (A2)<-  pin 19 of left edge of potted block and pin 23 (/WR) of 8253
pin 6 (B2)<-  74ls02 pin 11 and pin 7 of left edge of potted block, and to pin 24 (/A9) of ay-3-8910
pin 7   GND
pin 8 (A3)<-  pin 17 (PC3) of 8255
pin 9 (B3)<-  8251 pin 14 (RxRDY)
pin 10 (Y3)-> pin 1 (A1) of 74ls04 near xtal
pin 11 (A4)<- 74ls02 pin 7 and pin 7 of left edge of potted block, and to pin 24 (/A9) of ay-3-8910
pin 12 (B4)<- 8251 pin 12 (C/D)
pin 13 (Y4)-> pin 29 (BC1) of ay-3-8910
pin 14  VCC

resistor array (08-2-393 // 8042) connections:
pin 1: serial port pin 11 and 100KOhm resistor to GND
pin 2: hcf4049 pin 5 (A2)
pin 3: serial port pin 9(and 6?) and 100KOhm resistor to VCC
pin 4: hcf4049 pin 3 (A1)
pin 5: serial port pin 6(and 9?) and 100KOhm resistor to VCC
pin 6: hcf4049 pin 7 (A3)
pin 7: N/C

Parallel port pin 16 is connected to pin 13 on 8255 and 4.7KOhm resistor to 8255 pin 26 (GND)
*****************************************************************************/

/*****************************************************************************
Notes about the (sometimes) potted 1872/1872C CPU board:

top/solder side:

^       ^       1        ^                      40
|       |       2        |                      39
74LS74  74LS139 3        |      ^       ^       38
|       |       4        |      |       |       37
|       |       5        |      |       |       36
|       |       6        |      |       |       35
V       V       7        |      |       |       34
                8        |      |       |       33
                9       z80     |       |       32
                10       |      2764    2764    31
                11       |      rom1    rom2    30
                12       |      |       |       29
^       ^       13       |      |       |       28
|       |       14       |      |       |       27
74LS02  74LS367 15       |      V       v       26
|       |       16       |                      25
|       |       17       |                      24
|       |       18       |                      23
|       |       19       |                      22
V       V       20       v                      21


<- = input to device in question or to cpu module
-> = output
<-> = both input and output

Pinout:
pin     purpose         connects to, on cpu board
1       A13<->          from z80 pin 3->(A13), to 74ls02 pin 9<-(B3) (and out pin 10->(Y3) to eprom 1 pin 20(/CE)), to eprom 2 pin 20<-(/CE)
2       8255 /EN->      from 74ls139 pin 4->(/O0a)
3       USER ROM /EN->  from 74ls139 pin 9->(/O3b)
4       8251 /EN->      from 74ls139 pin 5->(/O1a)
5       RAM /EN ->      from 74ls139 pin 10->(/O2b)
6       8253 /EN->      from 74ls139 pin 6->(/O2a)
7       ay-3-8910 /EN-> from 74ls139 pin 7->(/O3a)
8       A2<->           from z80 pin 32->(A2), eproms pin 8<-(A2)
9       A1<->           from z80 pin 31->(A1), eproms pin 9<-(A1)
10      A0<->           from z80 pin 30->(A0), eproms pin 10<-(A0)
11      /INT<-          to z80 pin 16<-(/INT)
12      EXP_POLL<-      to 74ls74 pin 3<-(CLK1)
13      EXP_/EN->       from 74ls74 pin 5->(Q1)
14      /M1<->          from/to z80 pin 27->(/M1), 74ls02 pin 12<-(B4)
15      CLK<-           to 74ls367 pin 2<-(A1a)
16      /RESET<-        to 74ls367 pin 14<-(A2b)
17      /MREQ<->        from/to 74ls139 pin 15<-(/Eb), 74ls02 pin 11<-(A4), 74ls367 pin 5->(Y2a),
18      /IORQ<->        from/to 74ls139 pin 1<-(/Ea), 74ls74 pin 10<-(/PR2), 74ls367 pin 11->(Y1b)
19      Buffered /WR->  from 74ls367 pin 7->(Y3a)
20      Buffered /RD->  from 74ls367 pin 9->(Y4a)
21      D3<->           from/to z80 pin 8<->(D3), eproms pin 15->(D3)
22      D4<->           from/to z80 pin 7<->(D4), eproms pin 16->(D4)
23      D5<->           from/to z80 pin 9<->(D5), eproms pin 17->(D5)
24      D6<->           from/to z80 pin 10<->(D6), eproms pin 18->(D6)
25      D7<->           from/to z80 pin 13<->(D7), eproms pin 19->(D7)
26      GND             everything
27      A10->           from z80 pin 40->(A10), eproms pin 21<-(A10)
28      D2<->           from/to z80 pin 12<->(D2), eproms pin 13->(D2)
29      D1<->           from/to z80 pin 15<->(D1), eproms pin 12->(D1)
30      D0<->           from/to z80 pin 14<->(D0), eproms pin 11->(D0)
31      A6<->           from z80 pin 36->(A6), to eproms pin 4<-(A6), to 74ls139 pin 2<-(A0a)
32      A5<->           from z80 pin 35->(A5), eproms pin 5<-(A5)
33      A3<->           from z80 pin 33->(A3), eproms pin 7<-(A3)
34      A4<->           from z80 pin 34->(A4), eproms pin 6<-(A4)
35      A11->           from z80 pin 1->(A11), eproms pin 23<-(A11)
36      A9<->           from z80 pin 39->(A9), eproms pin 24<-(A9)
37      A8<->           from z80 pin 38->(A8), eproms pin 25<-(A8)
38      A7<->           from z80 pin 37->(A7), to eproms pin 3<-(A7), to 74ls139 pin 3<-(A1a)
39      A12<->          from z80 pin 2->(A12), eproms pin 2<-(A12)
40      VCC             everything

EXP_POLL clocks the flipflop 1 of the 74ls74
if 74ls139 pin 11 is LOW(0x4000-7fff address space is being accessed) , EXP_/EN will return 0
if 74ls139 pin 11 is HIGH(any other memory space is being accessed other than 0x4000-0x7fff), EXP_/EN will return 1


74ls02 quad 2-input NOR pinout:
1       Y1->    to 74ls02 pin 5<-(A2)
2       A1<-    from 74ls139 pin 12->(/O0b)
3       B1<-    from 74ls74 pin 9->(Q2)
4       Y2->    to eproms pin 22<-(/OE)
5       A2<-    from 74ls02 pin 1->(Y1)
6       B2<-    tied to GND
7       GND     GND
8       A3<-    tied to GND
9       B3<-    from z80 pin 3->(A13) AND to eprom 2 pin 20<-(/CE)
10      Y3->    to eprom 1 pin 20<-(/CE)
11      A4<-    to/from main board(module pin 17) AND to 74ls139 pin 15<-(/Eb) AND from 74ls367 pin 5->(Y2a)
12      B4<-    from z80 pin 27->(/M1) AND to main board (module pin 14)
13      Y4->    to 74ls74 pin 11<-(/CLK2)
14      VCC     VCC


74ls139 dual 1-of-4 decoder/demultiplexer pinout:
1       /Ea<-   to/from main board (module pin 18)<-> AND to 74ls74 pin 10<-(/PR2) AND from 74ls367 pin 11->(Y1b)
2       A0a<-   to/from main board (module pin 31)<-> AND from z80 pin 36->(A6) AND to eproms pin 4<-
3       A1a<-   to/from main board (module pin 38)<-> AND from z80 pin 37->(A7) AND to eproms pin 3<-
4       /O0a->  to main board (module pin 2, /0x**00 8255 /EN)
5       /O1a->  to main board (module pin 4, /0x**40 8251 /EN)
6       /O2a->  to main board (module pin 6, /0x**80 8253 /EN)
7       /O3a->  to main board (module pin 7, /0x**C0 ay-3-8910 /EN)
8       GND     GND
9       /O3b->  to main board (module pin 3, USER eprom /EN)
10      /O2b->  to main board (module pin 5, RAM /EN)
11      /O1b->  to 74ls74 pin 1<-(/CLR1) (Expansion /EN)
12      /O0b->  to 74ls02 pin 2<-(A1) (MODULE EPROMS /OE, but ONLY if 74ls02 pin 3(B1), which comes from 74ls74 pin 9(Q2), is LOW)
13      A1b<-   from z80 pin 5->(A15) and to 74ls74 pin 12<-(D2)
14      A0b<-   from z80 pin 4->(A14)
15      /Eb<-   to/from main board (module pin 17) AND from 74ls367 pin 5->(Y2a) AND to 74ls02 pin 11<-(A4)
16      VCC     VCC


74ls367 hex 3-state buffer with separate 2-bit and 4 bit sections pinout:
1       /Ea<-   tied to GND
2       A1a<-   from main board (module pin 15)
3       Y1a->   to z80 pin 6<-(CLK)
4       A2a<-   from z80 pin 19->(/MREQ)
5       Y2a->   to/from main board (module pin 17) AND to 74ls139 pin 15<-(/Eb) AND to 74ls02 pin 11<-(A4)
6       A3a<-   from z80 pin 22->(/WR)
7       Y3a->   to main board (module pin 19)
8       GND     GND
9       Y4a->   to main board (module pin 20)
10      A4a<-   from z80 pin 21->(/RD)
11      Y1b->   to/from main board (module pin 18)<-> AND to 74ls139 pin 1<-(/Ea) AND to 74ls74 pin 10<-(/PR2)
12      A1b<-   from z80 pin 20->(/IORQ)
13      Y2b->   to z80 pin 26<-(/RESET)
14      A2b<-   from main board (module pin 16)
15      /Eb<-   tied to GND
16      VCC     VCC


74ls74 dual positive-edge-triggered flip flops with preset, clear and complementary outputs pinout:
1       /CLR1<- from 74ls139 pin 11->(/O1b)
2       D1<-    tied to VCC (and 74ls74 pin 4<-(/PR1))
3       CLK1<-  from main board (module pin 12)
4       /PR1<-  tied to VCC (and 74ls74 pin 2<-(D1))
5       Q1->    to main board (module pin 13)
6       /Q1->   N/C
7       GND     GND
8       /Q2->   N/C
9       Q2->    to 74ls02 pin 3<-(B1)
10      /PR2<-  to/from main board (module pin 18)<-> AND to 74ls139 pin 1<-(/Ea) AND from 74ls367 pin 11->(Y1b)
11      CLK2<-  from 74ls02 pin 13->(Y4)
12      D2<-    from z80 pin 5->(A15) AND to 74ls139 pin 13<-(A1b)
13      /CLR2<- tied to VCC
14      VCC     VCC


z80 pinout:
1       A11->    to eproms and module bus
2       A12->    to eproms and module bus
3       A13->    to /CE on eprom 2 AND through 74ls02 pins 9 and 10 (acting as an inverter) to /CE on eprom 1 AND to module bus
4       A14->    to 74ls139 pin 14<-(A0b)
5       A15->    to 74ls74 pin 12<-(D2) AND to 74ls139 pin 13<-(A1b)
6       CLK<-    from 74ls367 pin 3->(Y1a) AND pulled to VCC by (orange orange red gold)Ohm resistor
7       D4<->    to eproms and module bus
8       D3<->    to eproms and module bus
9       D5<->    to eproms and module bus
10      D6<->    to eproms and module bus
11      VCC      VCC
12      D2<->    to eproms and module bus
13      D7<->    to eproms and module bus
14      D0<->    to eproms and module bus
15      D1<->    to eproms and module bus
16      /INT<-   from main board (module pin 11) AND pulled to VCC by (yellow purple red gold)Ohm resistor
17      /NMI<-   tied to VCC*
18      /HALT->  tied to VCC*
19      /MREQ->  to 74ls367 pin 4<-(A2a)
20      /IORQ->  to 74ls367 pin 12<-(A1b)
21      /RD->    to 74ls367 pin 10<-(A4a)
22      /WR->    to 74ls367 pin 6<-(A3a)
23      /BUSAK-> N/C
24      /WAIT<-  tied to /BUSRQ and to VCC
25      /BUSRQ<- tied to /WAIT and to VCC
26      /RESET<- from pin 13 of 74ls367->(Y2b)
27      /M1->    from 74ls02 pin 12<-(B4) AND to main board (module pin 14)
28      /RFSH->  N/C
29      GND      GND
30      A0->     to eproms and module bus
31      A1->     to eproms and module bus
32      A2->     to eproms and module bus
33      A3->     to eproms and module bus
34      A4->     to eproms and module bus
35      A5->     to eproms and module bus
36      A6->     to eproms and module bus AND to 74ls139 pin 2<-(A0a)
37      A7->     to eproms and module bus AND to 74ls139 pin 3<-(A1a)
38      A8->     to eproms and module bus
39      A9->     to eproms and module bus
40      A10->    to eproms and module bus

Note: It seems originally that /NMI and /HALT may have been tied together
  (and perhaps to VCC through a resistor) and NOT directly connected to VCC,
  so when the HALT opcode was executed it would immediately trigger an NMI
  and the CPU could handle this specifically.
  However, at least on the revision C 1872 CPU pcb, they are both tied
  directly to VCC, disabling this behavior.
*****************************************************************************/

/* Core includes */
#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
//#include "votrpss.lh"

/* Components */
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/pit8253.h"
#include "machine/timer.h"
#include "sound/ay8910.h"
#include "sound/votrax.h"
#include "machine/terminal.h"
#include "speaker.h"


namespace {

class votrpss_state : public driver_device
{
public:
	votrpss_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_terminal(*this, "terminal")
		, m_ppi(*this, "ppi")
	{ }

	void votrpss(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void kbd_put(u8 data);
	uint8_t ppi_pa_r();
	uint8_t ppi_pb_r();
	uint8_t ppi_pc_r();
	void ppi_pa_w(uint8_t data);
	void ppi_pb_w(uint8_t data);
	void ppi_pc_w(uint8_t data);
	TIMER_DEVICE_CALLBACK_MEMBER(irq_timer);
	void write_uart_clock(int state);
	IRQ_CALLBACK_MEMBER(irq_ack);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	uint8_t m_term_data = 0U;
	uint8_t m_porta = 0U;
	uint8_t m_portb = 0U;
	uint8_t m_portc = 0U;

	required_device<cpu_device> m_maincpu;
	required_device<generic_terminal_device> m_terminal;
	required_device<i8255_device> m_ppi;
};


/******************************************************************************
 Address Maps
******************************************************************************/

void votrpss_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).rom(); /* main roms (in potted module) */
	map(0x4000, 0x7fff).noprw(); /* open bus/space for expansion rom (reads as 0xFF) */
	map(0x8000, 0x8fff).ram(); /* onboard memory (2x 6116) */
	map(0x9000, 0xbfff).noprw(); /* open bus (space for memory expansion, checked by main roms, will be used if found)*/
	map(0xc000, 0xdfff).rom(); /* 'personality rom', containing self-test code and optional user code */
	map(0xe000, 0xffff).noprw(); /* open bus (space for more personality rom, not normally used) */
}

void votrpss_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).mirror(0x3c).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x40, 0x41).mirror(0x3e).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x80, 0x83).mirror(0x3c).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xc0, 0xc0).mirror(0x3e).rw("ay", FUNC(ay8910_device::data_r), FUNC(ay8910_device::address_w));
	map(0xc1, 0xc1).mirror(0x3e).rw("ay", FUNC(ay8910_device::data_r), FUNC(ay8910_device::data_w));
}


/******************************************************************************
 Input Ports
******************************************************************************/

static INPUT_PORTS_START(votrpss)
	PORT_START("DSW1")
	PORT_DIPNAME( 0x07, 0x00, "Baud Rate" ) PORT_DIPLOCATION("SW1:1,2,3")
	PORT_DIPSETTING(    0x00, "9600" )
	PORT_DIPSETTING(    0x01, "4800" )
	PORT_DIPSETTING(    0x02, "2400" )
	PORT_DIPSETTING(    0x03, "1200" )
	PORT_DIPSETTING(    0x04, "600" )
	PORT_DIPSETTING(    0x05, "300" )
	PORT_DIPSETTING(    0x06, "150" )
	PORT_DIPSETTING(    0x07, "75" )
	PORT_DIPNAME( 0x08, 0x00, "Serial Handshaking" )    PORT_DIPLOCATION("SW1:4")
	PORT_DIPSETTING(    0x00, "RTS/CTS" )
	PORT_DIPSETTING(    0x08, "XON/XOFF" )
	PORT_DIPNAME( 0x10, 0x00, "Parity bit behavior" )   PORT_DIPLOCATION("SW1:5") /* note: only firmware 3.C (1984?) and up handle this bit; on earlier firmwares, its function is 'unused' */
	PORT_DIPSETTING(    0x00, "Bit 8 ignored/zeroed" )
	PORT_DIPSETTING(    0x10, "Bit 8 treated as data" )
	PORT_DIPNAME( 0x20, 0x20, "Startup Message" )   PORT_DIPLOCATION("SW1:6")
	PORT_DIPSETTING(    0x00, DEF_STR ( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR ( On ) )
	PORT_DIPNAME( 0x40, 0x00, "Default Input Port" )   PORT_DIPLOCATION("SW1:7")
	PORT_DIPSETTING(    0x00, "Serial/RS-232" )
	PORT_DIPSETTING(    0x40, "Parallel" )
	PORT_DIPNAME( 0x80, 0x00, "Self Test Mode" )    PORT_DIPLOCATION("SW1:8")
	PORT_DIPSETTING(    0x00, DEF_STR ( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR ( On )  )
INPUT_PORTS_END

void votrpss_state::machine_start()
{
	save_item(NAME(m_term_data));
	save_item(NAME(m_porta));
	save_item(NAME(m_portb));
	save_item(NAME(m_portc));

	m_term_data = 0;
	m_porta = 0;
	m_portb = 0;
	m_portc = 0;
}

TIMER_DEVICE_CALLBACK_MEMBER( votrpss_state::irq_timer )
{
	m_maincpu->set_input_line(0, ASSERT_LINE);
}

IRQ_CALLBACK_MEMBER( votrpss_state::irq_ack )
{
	m_maincpu->set_input_line(0, CLEAR_LINE);
	return 0x38;
}

uint8_t votrpss_state::ppi_pa_r()
{
	uint8_t ret = m_term_data;
	m_term_data = 0;
	return ret;
}

uint8_t votrpss_state::ppi_pb_r()
{
	return m_portb;
}

// Bit 0 controls what happens at interrupt time. See code around 518.
uint8_t votrpss_state::ppi_pc_r()
{
	uint8_t data = 0;

	if (m_term_data)
	{
		m_ppi->pc4_w(0); // send a strobe pulse
		data |= 0x20;
	}

	return (m_portc & 0xdb) | data;
}

void votrpss_state::ppi_pa_w(uint8_t data)
{
	m_porta = data;
}

void votrpss_state::ppi_pb_w(uint8_t data)
{
	m_portb = data;
	m_terminal->write(data&0x7f);
}

void votrpss_state::ppi_pc_w(uint8_t data)
{
	m_portc = data;
}

void votrpss_state::kbd_put(u8 data)
{
	m_term_data = data;
}


/******************************************************************************
 Machine Drivers
******************************************************************************/

void votrpss_state::votrpss(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(8'000'000)/2);  /* 4.000 MHz, verified */
	m_maincpu->set_addrmap(AS_PROGRAM, &votrpss_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &votrpss_state::io_map);
	m_maincpu->set_irq_acknowledge_callback(FUNC(votrpss_state::irq_ack));

	/* video hardware */
	//config.set_default_layout(layout_votrpss);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	ay8910_device &ay(AY8910(config, "ay", XTAL(8'000'000)/4)); /* 2.000 MHz, verified */
	ay.port_b_read_callback().set_ioport("DSW1");
	ay.port_a_write_callback().set("votrax", FUNC(votrax_sc01_device::write));
	ay.add_route(ALL_OUTPUTS, "mono", 0.25);
	VOTRAX_SC01A(config, "votrax", 720000).add_route(ALL_OUTPUTS, "mono", 1.00); /* the actual SC-01-A clock is generated by an R/C circuit PWMed by the timer1 output of the 8253 PIT to adjust the resistance, to allow for fine pitch control. 8253 Timer2 is used to PWM an analog switch on the output of the SC-01 to adjust the volume.*/

	/* Devices */
	GENERIC_TERMINAL(config, m_terminal, 0);
	m_terminal->set_keyboard_callback(FUNC(votrpss_state::kbd_put));

	i8251_device &uart(I8251(config, "uart", 0));
	uart.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	uart.dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	uart.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	// when serial is chosen, and you select terminal, nothing shows (by design). You can only type commands in.
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set("uart", FUNC(i8251_device::write_rxd));
	rs232.dsr_handler().set("uart", FUNC(i8251_device::write_dsr));
	rs232.cts_handler().set("uart", FUNC(i8251_device::write_cts));

	pit8253_device &pit(PIT8253(config, "pit", 0));
	pit.set_clk<0>(8_MHz_XTAL); // Timer 0: baud rate gen for 8251
	pit.out_handler<0>().set("uart", FUNC(i8251_device::write_txc));
	pit.out_handler<0>().append("uart", FUNC(i8251_device::write_rxc));
	pit.set_clk<1>(8_MHz_XTAL / 256); // Timer 1: Pitch
	pit.set_clk<2>(8_MHz_XTAL / 4096); // Timer 2: Volume

	I8255(config, m_ppi);
	m_ppi->in_pa_callback().set(FUNC(votrpss_state::ppi_pa_r));
	m_ppi->in_pb_callback().set(FUNC(votrpss_state::ppi_pb_r));
	m_ppi->in_pc_callback().set(FUNC(votrpss_state::ppi_pc_r));
	m_ppi->out_pa_callback().set(FUNC(votrpss_state::ppi_pa_w));
	m_ppi->out_pb_callback().set(FUNC(votrpss_state::ppi_pb_w));
	m_ppi->out_pc_callback().set(FUNC(votrpss_state::ppi_pc_w));

	TIMER(config, "irq_timer").configure_periodic(FUNC(votrpss_state::irq_timer), attotime::from_msec(10));
}



/******************************************************************************
 ROM Definitions
******************************************************************************/

ROM_START(votrpss)
	ROM_REGION(0x10000, "maincpu", 0)
	/* old logo PSS, version 3.A  (was this even released, or just a prototype?) */

	/* old logo PSS, version 3.B (1982?), selftest 3.1 (1982 or earlier); 3.B may have used the 1871G PCB which probably had a somewhat different memory map, see the main PCB notes at the top of the file */
	//ROM_LOAD("u-2.3.b.bin",   0x0000, 0x2000, NO_DUMP ))
	//ROM_LOAD("u-3.3.b.bin",   0x2000, 0x2000, NO_DUMP ))

	/* old or new logo PSS, Version 3.C (1982), selftest 3.1 (1982 or earlier) */
	// these two roms are on an 1872 daughterboard PCB inside the potted brick daughterboard, or, on unpotted 1987/1988 PSS units, just on an unpotted 1872C daughterboard.
	ROM_LOAD("u-2.v3.c.bin",   0x0000, 0x2000, CRC(410c58cf) SHA1(6e181e61ab9c268e3772fbeba101302fd40b09a2)) /* The 1987/1988 version's rom is marked "U-2 // 090788" but the actual rom data is from 1982 */
	ROM_LOAD("u-3.v3.c.bin",   0x2000, 0x2000, CRC(1439492e) SHA1(46af8ccac6fdb93cbeb8a6d57dce5898e0e0d623)) /* The 1987/1988 version's rom is marked "U-3" */

	// this rom is on the 1871G/H/J mainboard, underneath the cpu module daughterboard, in a socket. Technically it is the 'user rom', but it contains the self test code. A user dictionary could in theory be put in this rom, and larger roms than a 2764 could be used.
	ROM_LOAD("u-4.v3.1.bin", 0xc000, 0x2000, CRC(0b7c4260) SHA1(56f0b6b1cd7b1104e09a9962583121c112337984)) /* the 1987/1988 version's rom is marked "3.1 10/09/85" but the actual rom data is the same from at least as far back as 1982; the 1982 version is marked "U4" in handwriting on a sticker, or "U-4" dot-matrix printed on a sticker */
ROM_END

} // Anonymous namespace


/******************************************************************************
 Drivers
******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE    INPUT    CLASS          INIT        COMPANY   FULLNAME                  FLAGS
COMP( 1982, votrpss, 0,      0,      votrpss,   votrpss, votrpss_state, empty_init, "Votrax", "Personal Speech System", MACHINE_SUPPORTS_SAVE )



votrtnt.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Jonathan Gevaryahu
/******************************************************************************
*
*  Votrax Type 'N Talk Driver
*  By Jonathan Gevaryahu AKA Lord Nightmare
*  with loads of help (dumping, code disassembly, schematics, and other
*  documentation) from Kevin 'kevtris' Horton
*
*  The Votrax TNT was sold from some time in early 1981 until at least 1983.
*
*  2011-02-27 The TNT communicates with its host via serial (RS-232) by
*             using a 6850 ACIA. It will echo whatever is sent, back to the
*             host. In order that we can examine it, I have connected the
*             'terminal' so we can enter data, and see what gets sent back.
*             I've also connected the 'votrax' device, but it is far from
*             complete (the sounds are not understandable).
*
*             ACIA status code is set to E2 for no data and E3 for data.
*
*             On the CPU, A12 and A15 are not connected. A13 and A14 are used
*             to select devices. A0-A9 address RAM. A0-A11 address ROM.
*             A0 switches the ACIA between status/command, and data in/out.
*
******************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "machine/votraxtnt.h"

#include "votrtnt.lh"


namespace {

class votrtnt_state : public driver_device
{
public:
	votrtnt_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
	{ }

	void votrtnt(machine_config &config);
};


/******************************************************************************
 Machine Drivers
******************************************************************************/

void votrtnt_state::votrtnt(machine_config &config)
{
	/* basic machine hardware */
	votraxtnt_device &votrax(VOTRAXTNT(config, "votrax"));
	votrax.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	votrax.rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));

	/* video hardware */
	//config.set_default_layout(layout_votrtnt);

	/* serial hardware */
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("votrax", FUNC(votraxtnt_device::write_rxd));
	rs232.cts_handler().set("votrax", FUNC(votraxtnt_device::write_cts));
}


/******************************************************************************
 ROM Definitions
******************************************************************************/

ROM_START(votrtnt)
ROM_END

} // anonymous namespace


/******************************************************************************
 Drivers
******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY   FULLNAME        FLAGS
COMP( 1980, votrtnt, 0,      0,      votrtnt, 0,       votrtnt_state, empty_init, "Votrax", "Type 'N Talk", MACHINE_SUPPORTS_SAVE )



vp415.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/***************************************************************************

    Skeleton driver for Philips VP415 LV ROM Player

    List of Modules:
        A - Audio Processor
        B - RGB
        C - Video Processor
        D - Ref Source
        E - Slide Drive
        F - Motor+Sequence
        G - Gen Lock
        H - ETBC B
        I - ETBC C
        J - Focus
        K - HF Processor
        L - Video Dropout Correction
        M - Radial
        N - Display Keyboard
        P - Front Loader
        Q - RC5 Mirror
        R - Drive Processor
        S - Control
        T - Supply
        U - Analog I/O
        V - Module Carrier
        W - CPU Datagrabber
        X - LV ROM
        Y - Vid Mix
        Z - Deck Electronics

    TODO:
    - Driver currently fails the initial self-test with code 073. Per
      the service manual, code 73 means "a/d converted mirror pos. min.
      (out of field of view)".

***************************************************************************/

#include "emu.h"

#include "cpu/mcs51/mcs51.h"
#include "cpu/mcs48/mcs48.h"
#include "cpu/z80/z80.h"

#include "machine/i8155.h"
#include "machine/i8255.h"
#include "machine/ncr5385.h"
#include "machine/saa1043.h"

#include "video/mb88303.h"

#include "screen.h"

namespace {

class vp415_state : public driver_device
{
public:
	vp415_state(const machine_config &mconfig, device_type type, const char *tag);

	void vp415(machine_config &config);

	static const char *const DATACPU_TAG;
	static const char *const DATAMCU_TAG;

	static const char *const DESCRAMBLE_ROM_TAG;
	static const char *const SYNC_ROM_TAG;
	static const char *const DRIVE_ROM_TAG;

	static const char *const CTRLMCU_TAG;
	static const char *const CONTROL_ROM_TAG;

	static const char *const SWITCHES_TAG;

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

	TIMER_CALLBACK_MEMBER(drive_2ppr_tick);

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void sel34_w(uint8_t data);
	uint8_t sel37_r();

	void cpu_int1_w(int state);

	void data_mcu_port1_w(uint8_t data);
	uint8_t data_mcu_port1_r();
	void data_mcu_port2_w(uint8_t data);
	uint8_t data_mcu_port2_r();

	void ctrl_regs_w(offs_t offset, uint8_t data);
	uint8_t ctrl_regs_r(offs_t offset);
	void ctrl_cpu_port1_w(uint8_t data);
	uint8_t ctrl_cpu_port1_r();
	void ctrl_cpu_port3_w(uint8_t data);
	uint8_t ctrl_cpu_port3_r();

	void ctrl_mcu_port1_w(uint8_t data);
	uint8_t ctrl_mcu_port1_r();
	void ctrl_mcu_port2_w(uint8_t data);
	uint8_t ctrl_mcu_port2_r();

	uint8_t drive_i8155_pb_r();
	uint8_t drive_i8155_pc_r();

	void drive_i8255_pa_w(uint8_t data);
	void drive_i8255_pb_w(uint8_t data);
	uint8_t drive_i8255_pc_r();
	void drive_cpu_port1_w(uint8_t data);
	void drive_cpu_port3_w(uint8_t data);

	void refv_w(int state);

	// CPU Board enums
	enum
	{
		SEL34_INTR_N = 0x01,
		SEL34_RES    = 0x20,
		SEL34_ERD    = 0x40,
		SEL34_ENW    = 0x80,
		SEL34_INTR_N_BIT = 0,
		SEL34_RES_BIT    = 5,
		SEL34_ERD_BIT    = 6,
		SEL34_ENW_BIT    = 7,
	};

	enum
	{
		SEL37_ID0   = 0x01,
		SEL37_ID1   = 0x02,
		SEL37_BRD   = 0x10,
		SEL37_MON_N = 0x20,
		SEL37_SK1c  = 0x40,
		SEL37_SK1d  = 0x40,

		SEL37_ID0_BIT   = 0,
		SEL37_ID1_BIT   = 1,
		SEL37_BRD_BIT   = 4,
		SEL37_MON_N_BIT = 5,
		SEL37_SK1c_BIT  = 6,
		SEL37_SK1d_BIT  = 7,
	};

	// Control Board enums
	enum
	{
		CTRL_P3_INT1 = 0x08,

		CTRL_P3_INT1_BIT = 3
	};

	// Drive Board enums
	enum
	{
		I8255PC_NOT_FOCUSED     = 0x02,
		I8255PC_0RPM_N          = 0x08,
		I8255PC_DISC_REFLECTION = 0x10,
	};

	enum
	{
		I8255PB_COMM1    = 0x01,
		I8255PB_COMM2    = 0x02,
		I8255PB_COMM3    = 0x04,
		I8255PB_COMM4    = 0x08,
		I8255PB_RLS_N    = 0x10,
		I8255PB_SL_PWR   = 0x20,
		I8255PB_RAD_FS_N = 0x40,
		I8255PB_STR1     = 0x80,

		I8255PB_COMM1_BIT    = 0,
		I8255PB_COMM2_BIT    = 1,
		I8255PB_COMM3_BIT    = 2,
		I8255PB_COMM4_BIT    = 3,
		I8255PB_RLS_N_BIT    = 4,
		I8255PB_SL_PWR_BIT   = 5,
		I8255PB_RAD_FS_N_BIT = 6,
		I8255PB_STR1_BIT     = 7,
	};

	enum
	{
		I8155PB_2PPR    = 0x01,
		I8155PB_RAD_MIR = 0x04,
		I8155PB_FRLOCK  = 0x08,

		I8155PB_2PPR_BIT    = 0,
		I8155PB_RAD_MIR_BIT = 2,
		I8155PB_FRLOCK_BIT  = 3,
	};

	enum
	{
		DRIVE_P1_CP1    = 0x01,
		DRIVE_P1_CP2    = 0x02,
		DRIVE_P1_LDI    = 0x04,
		DRIVE_P1_ATN_N  = 0x08,
		DRIVE_P1_TX     = 0x10,
		DRIVE_P1_STB_N  = 0x20,
		DRIVE_P1_STR0_N = 0x40,
		DRIVE_P1_TP2    = 0x80,

		DRIVE_P1_CP1_BIT    = 0,
		DRIVE_P1_CP2_BIT    = 1,
		DRIVE_P1_LDI_BIT    = 2,
		DRIVE_P1_ATN_N_BIT  = 3,
		DRIVE_P1_TX_BIT     = 4,
		DRIVE_P1_STB_N_BIT  = 5,
		DRIVE_P1_STR0_N_BIT = 6,
		DRIVE_P1_TP2_BIT    = 7
	};

	virtual void video_start() override ATTR_COLD;

	void z80_program_map(address_map &map) ATTR_COLD;
	void z80_io_map(address_map &map) ATTR_COLD;
	void datamcu_program_map(address_map &map) ATTR_COLD;
	void set_int_line(uint8_t line, uint8_t value);
	void update_cpu_int();

	void ctrl_program_map(address_map &map) ATTR_COLD;
	void ctrl_io_map(address_map &map) ATTR_COLD;
	void ctrlmcu_program_map(address_map &map) ATTR_COLD;
	void sd_w(uint8_t data);
	uint8_t sd_r();

	void drive_program_map(address_map &map) ATTR_COLD;
	void drive_io_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_datacpu;
	required_device<i8041a_device> m_datamcu;
	required_device<ncr5385_device> m_scsi;
	required_device<i8031_device> m_drivecpu;
	required_device<i8031_device> m_ctrlcpu;
	required_device<i8041a_device> m_ctrlmcu;
	required_device<mb88303_device> m_chargen;
	required_shared_ptr<uint8_t> m_mainram;
	required_shared_ptr<uint8_t> m_ctrlram;
	required_ioport m_switches;

	uint8_t m_sel34 = 0;
	uint8_t m_sel37 = 0;

	uint8_t m_int_lines[2]{};

	uint8_t m_refv = 0;

	uint8_t m_ctrl_cpu_p1 = 0;
	uint8_t m_ctrl_cpu_p3 = 0;
	uint8_t m_ctrl_mcu_p1 = 0;
	uint8_t m_ctrl_mcu_p2 = 0;

	uint8_t m_drive_p1 = 0;
	uint8_t m_drive_pc_bits = 0;

	uint8_t m_drive_rad_mir_dac = 0;
	uint8_t m_drive_i8255_pb = 0;
	emu_timer *m_drive_2ppr_timer = nullptr;
	uint8_t m_drive_2ppr = 0;

	static const char *const DATARAM_TAG;
	static const char *const SCSI_TAG;

	static const char *const CTRLCPU_TAG;
	static const char *const CTRLRAM_TAG;

	static const char *const DRIVECPU_TAG;
	static const char *const I8155_TAG;
	static const char *const I8255_TAG;
	static const char *const CHARGEN_TAG;
	static const char *const SYNCGEN_TAG;
};

const char *const vp415_state::DATACPU_TAG = "datacpu";
const char *const vp415_state::DATAMCU_TAG = "datamcu";
const char *const vp415_state::DATARAM_TAG = "dataram";
const char *const vp415_state::SCSI_TAG = "ncr5385";
const char *const vp415_state::CTRLCPU_TAG = "ctrlcpu";
const char *const vp415_state::CTRLMCU_TAG = "ctrlmcu";
const char *const vp415_state::CTRLRAM_TAG = "ctrlram";
const char *const vp415_state::DRIVECPU_TAG = "drivecpu";
const char *const vp415_state::DESCRAMBLE_ROM_TAG = "descramblerom";
const char *const vp415_state::SYNC_ROM_TAG = "syncrom";
const char *const vp415_state::DRIVE_ROM_TAG = "driverom";
const char *const vp415_state::CONTROL_ROM_TAG = "controlrom";
const char *const vp415_state::SWITCHES_TAG = "SWITCHES";
const char *const vp415_state::I8155_TAG = "i8155";
const char *const vp415_state::I8255_TAG = "i8255";
const char *const vp415_state::CHARGEN_TAG = "mb88303";
const char *const vp415_state::SYNCGEN_TAG = "saa1043";

vp415_state::vp415_state(const machine_config &mconfig, device_type type, const char *tag)
	: driver_device(mconfig, type, tag)
	, m_datacpu(*this, DATACPU_TAG)
	, m_datamcu(*this, DATAMCU_TAG)
	, m_scsi(*this, SCSI_TAG)
	, m_drivecpu(*this, DRIVECPU_TAG)
	, m_ctrlcpu(*this, CTRLCPU_TAG)
	, m_ctrlmcu(*this, CTRLMCU_TAG)
	, m_chargen(*this, CHARGEN_TAG)
	, m_mainram(*this, DATARAM_TAG)
	, m_ctrlram(*this, CTRLRAM_TAG)
	, m_switches(*this, SWITCHES_TAG)
{
}

void vp415_state::machine_reset()
{
	m_sel34 = 0;
	m_sel37 = SEL37_BRD | SEL37_MON_N | SEL37_SK1c | SEL37_SK1d;
	m_int_lines[0] = 0;
	m_int_lines[1] = 0;

	m_ctrl_cpu_p1 = 0;
	m_ctrl_cpu_p3 = 0;

	m_ctrl_mcu_p1 = 0;
	m_ctrl_mcu_p2 = 0;

	m_drive_p1 = 0;
	m_drive_i8255_pb = 0;

	m_drive_pc_bits = I8255PC_DISC_REFLECTION | I8255PC_NOT_FOCUSED;

	m_drive_rad_mir_dac = 0;

	m_drive_2ppr = 0;
	m_drive_2ppr_timer->adjust(attotime::from_msec(10));
}

void vp415_state::machine_start()
{
	m_drive_2ppr_timer = timer_alloc(FUNC(vp415_state::drive_2ppr_tick), this);
}

TIMER_CALLBACK_MEMBER(vp415_state::drive_2ppr_tick)
{
	m_drive_2ppr ^= I8155PB_2PPR;
	m_drive_2ppr_timer->adjust(attotime::from_msec(10));
}

void vp415_state::refv_w(int state)
{
	m_refv = state;
	m_drivecpu->set_input_line(MCS51_INT0_LINE, m_refv ? CLEAR_LINE : ASSERT_LINE);
	//printf("Current time in ms: %f\n", machine().scheduler().time().as_double() * 1000.0D);
}

// CPU Datagrabber Module (W)

void vp415_state::cpu_int1_w(int state)
{
	set_int_line(0, state);
}

void vp415_state::sel34_w(uint8_t data)
{
	logerror("%s: sel34: /INTR=%d, RES=%d, ERD=%d, ENW=%d\n", machine().describe_context(), BIT(data, SEL34_INTR_N_BIT), BIT(data, SEL34_RES_BIT), BIT(data, SEL34_ERD_BIT), BIT(data, SEL34_ENW_BIT));
	m_sel34 = data;

	if (!BIT(data, SEL34_INTR_N_BIT))
	{
		m_sel37 &= ~(SEL37_ID0 | SEL37_ID1);
		update_cpu_int();
	}
}

uint8_t vp415_state::sel37_r()
{
	logerror("%s: sel37: ID0=%d, ID1=%d\n", machine().describe_context(), BIT(m_sel37, SEL37_ID0_BIT), BIT(m_sel37, SEL37_ID1_BIT));
	return m_sel37;
}

void vp415_state::set_int_line(uint8_t line, uint8_t value)
{
	if (value)
	{
		m_sel37 |= line ? SEL37_ID1 : SEL37_ID0;
	}

	update_cpu_int();
}

void vp415_state::update_cpu_int()
{
	m_datacpu->set_input_line(0, (m_sel37 & (SEL37_ID0 | SEL37_ID1)) ? ASSERT_LINE : CLEAR_LINE);
}

void vp415_state::data_mcu_port1_w(uint8_t data)
{
	logerror("%s: data_mcu_port1_w: %02x\n", machine().describe_context(), data);
}

uint8_t vp415_state::data_mcu_port1_r()
{
	logerror("%s: data_mcu_port1_r: %02x\n", machine().describe_context(), 0);
	return 0;
}

void vp415_state::data_mcu_port2_w(uint8_t data)
{
	logerror("%s: data_mcu_port2_w: %02x\n", machine().describe_context(), data);
}

uint8_t vp415_state::data_mcu_port2_r()
{
	logerror("%s: data_mcu_port2_r: %02x\n", machine().describe_context(), 0);
	return 0;
}

void vp415_state::z80_program_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region(DATACPU_TAG, 0);
	map(0xa000, 0xfeff).ram().share(DATARAM_TAG);
}

void vp415_state::z80_io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x0f).m(SCSI_TAG, FUNC(ncr5385_device::map));
	// 0x20, 0x21: Connected to A0 + D0..D7 of SLAVE i8041
	map(0x34, 0x34).w(FUNC(vp415_state::sel34_w));
	map(0x37, 0x37).r(FUNC(vp415_state::sel37_r));
}

void vp415_state::datamcu_program_map(address_map &map)
{
	map(0x0000, 0x03ff).rom().region(DATAMCU_TAG, 0);
}



// Control Module (S)

void vp415_state::ctrl_regs_w(offs_t offset, uint8_t data)
{
	const uint8_t handler_index = (offset & 0x0c00) >> 10;
	switch (handler_index)
	{
		case 0: // WREN
			logerror("%s: ctrl_regs_w: WREN: %02x\n", machine().describe_context(), data);
			sd_w(data);
			break;
		case 1: // WR3
			logerror("%s: ctrl_regs_w: WR3 (UPI-41): %d=%02x\n", machine().describe_context(), (offset >> 9) & 1, data);
			m_ctrlmcu->upi41_master_w((offset >> 9) & 1, data);
			break;
		case 2:
			logerror("%s: ctrl_regs_w: N.C. write %02x\n", machine().describe_context(), data);
			break;
		case 3:
			logerror("%s: ctrl_regs_w: output buffer: VP=%d, NPL=%d, WR-CLK=%d, DB/STAT=%d, RD-STRT=%d, V/C-TXT=%d\n", machine().describe_context(), data & 0x7, BIT(data, 4), BIT(data, 3), BIT(data, 5), BIT(data, 6), BIT(data, 7));
			break;
	}
}

uint8_t vp415_state::ctrl_regs_r(offs_t offset)
{
	const uint8_t handler_index = (offset & 0x0c00) >> 10;
	uint8_t value = 0;
	switch (handler_index)
	{
		case 0: // RDEN
			value = sd_r();
			logerror("%s: ctrl_regs_r: RDEN: %02x\n", machine().describe_context(), value);
			break;
		case 1: // /RD3
			value = m_ctrlmcu->upi41_master_r((offset >> 9) & 1);
			logerror("%s: ctrl_regs_r: RD3 (UPI-41): %d (%02x)\n", machine().describe_context(), (offset >> 9) & 1, value);
			break;
		case 2: // /RD2
			logerror("%s: ctrl_regs_r: N.C. read\n", machine().describe_context());
			break;
		case 3:
			value = m_switches->read();
			logerror("%s: ctrl_regs_r: RD1 (DIP switches): %02x\n", machine().describe_context(), value);
			break;
	}
	return value;
}

void vp415_state::ctrl_cpu_port1_w(uint8_t data)
{
	uint8_t old = m_ctrl_cpu_p1;
	m_ctrl_cpu_p1 = data;

	if ((m_ctrl_cpu_p1 ^ old) & 0xdf) // Ignore petting the watchdog (bit 5)
	{
		logerror("%s: ctrl_cpu_port1_w: %02x\n", machine().describe_context(), data);
	}
}

uint8_t vp415_state::ctrl_cpu_port1_r()
{
	uint8_t ret = m_ctrl_cpu_p1;
	m_ctrl_cpu_p1 ^= 0x10;
	logerror("%s: ctrl_cpu_port1_r (%02x)\n", machine().describe_context(), ret);
	return ret;
}

void vp415_state::ctrl_cpu_port3_w(uint8_t data)
{
	m_ctrl_cpu_p3 = ~data;
	logerror("%s: ctrl_cpu_port3_w: %02x\n", machine().describe_context(), data);
}

uint8_t vp415_state::ctrl_cpu_port3_r()
{
	uint8_t ret = m_ctrl_cpu_p3;
	logerror("%s: ctrl_cpu_port3_r (%02x)\n", machine().describe_context(), ret);
	return ret;
}

void vp415_state::ctrl_mcu_port1_w(uint8_t data)
{
	m_ctrl_mcu_p1 = data;
	logerror("%s: ctrl_mcu_port1_w: %02x\n", machine().describe_context(), data);
}

uint8_t vp415_state::ctrl_mcu_port1_r()
{
	uint8_t value = m_ctrl_mcu_p1;
	logerror("%s: ctrl_mcu_port1_r: %02x\n", machine().describe_context(), value);
	return value;
}

void vp415_state::ctrl_mcu_port2_w(uint8_t data)
{
	m_ctrl_mcu_p2 = data;
	if (BIT(data, 4))
		m_ctrl_cpu_p3 &= CTRL_P3_INT1;
	else
		m_ctrl_cpu_p3 |= CTRL_P3_INT1;
	logerror("%s: ctrl_mcu_port2_w: %02x\n", machine().describe_context(), data);
}

uint8_t vp415_state::ctrl_mcu_port2_r()
{
	logerror("%s: ctrl_mcu_port2_r: %02x\n", machine().describe_context(), 0);
	return 0;
}

uint8_t vp415_state::sd_r()
{
	return 0;
}

void vp415_state::sd_w(uint8_t data)
{
}

void vp415_state::ctrl_program_map(address_map &map)
{
	map(0x0000, 0xffff).rom().region(CONTROL_ROM_TAG, 0);
}

void vp415_state::ctrl_io_map(address_map &map)
{
	map(0x0000, 0x1fff).ram().share(CTRLRAM_TAG);
	map(0xe000, 0xffff).rw(FUNC(vp415_state::ctrl_regs_r), FUNC(vp415_state::ctrl_regs_w)).mask(0x1e00);
}

void vp415_state::ctrlmcu_program_map(address_map &map)
{
	map(0x0000, 0x03ff).rom().region(CTRLMCU_TAG, 0);
}

// Drive Processor Module (R)

uint8_t vp415_state::drive_i8155_pb_r()
{
	uint8_t ret = I8155PB_FRLOCK | m_drive_2ppr;
	if (m_drive_rad_mir_dac >= 0x7e && m_drive_rad_mir_dac < 0x82 && BIT(m_drive_i8255_pb, I8255PB_RLS_N_BIT))
		ret |= I8155PB_RAD_MIR;
	logerror("%s: drive_i8155_pb_r: %02x\n", machine().describe_context(), ret);
	return ret;
}

uint8_t vp415_state::drive_i8155_pc_r()
{
	logerror("%s: drive_i8155_pc_r: %02x\n", machine().describe_context(), 0);
	return 0;
}

void vp415_state::drive_i8255_pa_w(uint8_t data)
{
	logerror("%s: drive_i8255_pa_w: radial mirror DAC = %02x\n", machine().describe_context(), data);
	m_drive_rad_mir_dac = data;
}

void vp415_state::drive_i8255_pb_w(uint8_t data)
{
	m_drive_i8255_pb = data;
	logerror("%s: drive_i8255_pb_w: COMM-1:%d, COMM-2:%d, COMM-3:%d, COMM-4:%d, /RLS:%d, SL-PWR:%d, /RAD-FS:%d, STR1:%d\n"
		, machine().describe_context()
		, BIT(data, I8255PB_COMM1_BIT)
		, BIT(data, I8255PB_COMM2_BIT)
		, BIT(data, I8255PB_COMM3_BIT)
		, BIT(data, I8255PB_COMM4_BIT)
		, BIT(data, I8255PB_RLS_N_BIT)
		, BIT(data, I8255PB_SL_PWR_BIT)
		, BIT(data, I8255PB_RAD_FS_N_BIT)
		, BIT(data, I8255PB_STR1_BIT));
	if (BIT(data, I8255PB_RLS_N_BIT))
	{

	}
}

uint8_t vp415_state::drive_i8255_pc_r()
{
	static int focus_kludge = 250;
	static int motor_kludge = 200;
	logerror("%s: drive_i8255_pc_r: %02x\n", machine().describe_context(), m_drive_pc_bits);
	if (focus_kludge > 0)
	{
		focus_kludge--;
	}
	else
	{
		m_drive_pc_bits &= ~I8255PC_NOT_FOCUSED;
	}
	if (motor_kludge > 0)
	{
		motor_kludge--;
	}
	else
	{
		m_drive_pc_bits |= I8255PC_0RPM_N;
	}
	return m_drive_pc_bits;
}

void vp415_state::drive_cpu_port1_w(uint8_t data)
{
	uint8_t old = m_drive_p1;
	m_drive_p1 = data;
	if ((m_drive_p1 ^ old) & 0xfb) // Ignore bit 2 when logging (LDI)
	{
		logerror("%s: drive_cpu_port1_w: TP2:%d /STR0:%d /STB:%d TX:%d /ATN:%d LDI:%d CP2:%d CP1:%d\n", machine().describe_context()
			, BIT(data, DRIVE_P1_TP2_BIT)
			, BIT(data, DRIVE_P1_STR0_N_BIT)
			, BIT(data, DRIVE_P1_STB_N_BIT)
			, BIT(data, DRIVE_P1_TX_BIT)
			, BIT(data, DRIVE_P1_ATN_N_BIT)
			, BIT(data, DRIVE_P1_LDI_BIT)
			, BIT(data, DRIVE_P1_CP2_BIT)
			, BIT(data, DRIVE_P1_CP1_BIT));
	}
	m_chargen->ldi_w(BIT(data, 2));
}

//int vp415_state::drive_rxd_r()
//{
//  logerror("%s: drive_rxd_r: %d\n", machine().describe_context(), 0);
//  return 0;
//}

//void vp415_state::drive_txd_w(int state)
//{
//  logerror("%s: drive_txd_w: %d\n", machine().describe_context(), state);
//}
void vp415_state::drive_cpu_port3_w(uint8_t data)
{
	logerror("%s: drive_cpu_port3_w: %02x\n", machine().describe_context(), data);
}

void vp415_state::drive_program_map(address_map &map)
{
	map(0x0000, 0x3fff).rom().region(DRIVE_ROM_TAG, 0);
}

void vp415_state::drive_io_map(address_map &map)
{
	map(0x0000, 0x0003).mirror(0xfbfc).rw(I8255_TAG, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x0400, 0x04ff).mirror(0xf800).rw(I8155_TAG, FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w));
	map(0x0500, 0x0507).mirror(0xf8f8).rw(I8155_TAG, FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
}

void vp415_state::video_start()
{
}

uint32_t vp415_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	m_chargen->update_bitmap(bitmap, cliprect);
	return 0;
}

static INPUT_PORTS_START( vp415 )
	PORT_START(vp415_state::SWITCHES_TAG)
		PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
		PORT_DIPNAME( 0x04, 0x00, "BRA (unknown purpose)" )
		PORT_DIPSETTING(    0x04, "On" )
		PORT_DIPSETTING(    0x00, "Off" )
		PORT_DIPNAME( 0x08, 0x00, "BRB (unknown purpose)" )
		PORT_DIPSETTING(    0x08, "On" )
		PORT_DIPSETTING(    0x00, "Off" )
		PORT_DIPNAME( 0x10, 0x00, "DUMP (unknown purpose)" )
		PORT_DIPSETTING(    0x10, "On" )
		PORT_DIPSETTING(    0x00, "Off" )
		PORT_DIPNAME( 0x20, 0x00, "Remote Control IR/Euro" )
		PORT_DIPSETTING(    0x20, "IR" )
		PORT_DIPSETTING(    0x00, "Euro" )
		PORT_DIPNAME( 0x40, 0x00, "REPLAY (unknown purpose)" )
		PORT_DIPSETTING(    0x40, "On" )
		PORT_DIPSETTING(    0x00, "Off" )
		PORT_DIPNAME( 0x80, 0x00, "RESI (unknown purpose)" )
		PORT_DIPSETTING(    0x80, "On" )
		PORT_DIPSETTING(    0x00, "Off" )
INPUT_PORTS_END

void vp415_state::vp415(machine_config &config)
{
	// Module W: CPU Datagrabber
	Z80(config, m_datacpu, XTAL(8'000'000)/2); // 8MHz through a /2 flip-flop divider, per schematic
	m_datacpu->set_addrmap(AS_PROGRAM, &vp415_state::z80_program_map);
	m_datacpu->set_addrmap(AS_IO, &vp415_state::z80_io_map);

	I8041A(config, m_datamcu, XTAL(4'000'000)); // Verified on schematic
	m_datamcu->p1_in_cb().set(FUNC(vp415_state::data_mcu_port1_r));
	m_datamcu->p1_out_cb().set(FUNC(vp415_state::data_mcu_port1_w));
	m_datamcu->p2_in_cb().set(FUNC(vp415_state::data_mcu_port2_r));
	m_datamcu->p2_out_cb().set(FUNC(vp415_state::data_mcu_port2_w));
	m_datamcu->set_addrmap(AS_PROGRAM, &vp415_state::datamcu_program_map);

	NCR5385(config, m_scsi, XTAL(8'000'000)/2); // Same clock signal as above, per schematic
	m_scsi->irq().set(FUNC(vp415_state::cpu_int1_w));

	// Module S: Control
	I8031(config, m_ctrlcpu, XTAL(11'059'200)); // 11.059MHz, per schematic
	m_ctrlcpu->port_out_cb<1>().set(FUNC(vp415_state::ctrl_cpu_port1_w));
	m_ctrlcpu->port_in_cb<1>().set(FUNC(vp415_state::ctrl_cpu_port1_r));
	m_ctrlcpu->port_out_cb<3>().set(FUNC(vp415_state::ctrl_cpu_port3_w));
	m_ctrlcpu->port_in_cb<3>().set(FUNC(vp415_state::ctrl_cpu_port3_r));
	m_ctrlcpu->set_addrmap(AS_PROGRAM, &vp415_state::ctrl_program_map);
	m_ctrlcpu->set_addrmap(AS_IO, &vp415_state::ctrl_io_map);

	I8041A(config, m_ctrlmcu, XTAL(4'000'000)); // Verified on schematic
	m_ctrlmcu->p1_in_cb().set(FUNC(vp415_state::ctrl_mcu_port1_r));
	m_ctrlmcu->p1_out_cb().set(FUNC(vp415_state::ctrl_mcu_port1_w));
	m_ctrlmcu->p2_in_cb().set(FUNC(vp415_state::ctrl_mcu_port2_r));
	m_ctrlmcu->p2_out_cb().set(FUNC(vp415_state::ctrl_mcu_port2_w));
	m_ctrlmcu->set_addrmap(AS_PROGRAM, &vp415_state::ctrlmcu_program_map);

	// Module R: Drive
	I8031(config, m_drivecpu, XTAL(12'000'000)); // 12MHz, per schematic
	m_drivecpu->port_out_cb<1>().set(FUNC(vp415_state::drive_cpu_port1_w));
	m_drivecpu->port_out_cb<3>().set(FUNC(vp415_state::drive_cpu_port3_w));
	m_drivecpu->set_addrmap(AS_PROGRAM, &vp415_state::drive_program_map);
	m_drivecpu->set_addrmap(AS_IO, &vp415_state::drive_io_map);

	i8155_device &i8155(I8155(config, I8155_TAG, 0));
	i8155.out_pa_callback().set(CHARGEN_TAG, FUNC(mb88303_device::da_w));
	i8155.in_pb_callback().set(FUNC(vp415_state::drive_i8155_pb_r));
	i8155.in_pc_callback().set(FUNC(vp415_state::drive_i8155_pc_r));

	i8255_device &ppi(I8255(config, I8255_TAG));
	ppi.out_pa_callback().set(FUNC(vp415_state::drive_i8255_pa_w));
	ppi.out_pb_callback().set(FUNC(vp415_state::drive_i8255_pb_w));
	ppi.in_pc_callback().set(FUNC(vp415_state::drive_i8255_pc_r));

	MB88303(config, m_chargen, 0);

	saa1043_device &saa1043(SAA1043(config, SYNCGEN_TAG, XTAL(5'000'000)));
	saa1043.v2_callback().set(FUNC(vp415_state::refv_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(320, 240);
	screen.set_visarea(0, 319, 0, 239);
	screen.set_screen_update(FUNC(vp415_state::screen_update));
}

ROM_START(vp415)
	/* Module R */
	ROM_REGION(0x4000, vp415_state::DRIVE_ROM_TAG, 0) // Version 1.7
	ROM_LOAD( "r.3104 103 6803.6_drive.ic4", 0x0000, 0x4000, CRC(02e4273e) SHA1(198f7ac8f2a880f38046c9d9075ce32f3b730bd4) )

	/* Module S */
	ROM_REGION(0x10000, vp415_state::CONTROL_ROM_TAG, 0) // Version 1.8
	ROM_LOAD( "s.3104 103 6804.9_control.ic2", 0x0000, 0x10000, CRC(10564765) SHA1(8eb6cff7ca7cbfcb3db8b04b697cdd7e364be805) )

	ROM_REGION(0x400, vp415_state::CTRLMCU_TAG, 0)
	ROM_LOAD( "d8041ahc 152.7252", 0x000, 0x400, CRC(2972d4b2) SHA1(e08086714fa5be1a67feac8f64210b21bb410dd3) )

	/* Module W */
	ROM_REGION(0x8000, vp415_state::DATACPU_TAG, 0)
	ROM_LOAD( "w.3104 103 6805.3_cpu", 0x0000, 0x4000, CRC(c2cf4f25) SHA1(e55e1ac917958eb42244bff17a0016b74627c8fa) ) // Version 1.3
	ROM_LOAD( "w.3104 103 6806.3_cpu", 0x4000, 0x4000, CRC(14a45ea0) SHA1(fa028d01094be91e3480c9ad35d46b5546f9ff0f) ) // Version 1.4

	ROM_REGION(0x4000, vp415_state::DESCRAMBLE_ROM_TAG, 0)
	ROM_LOAD( "w.3104 103 6807.0_cpu", 0x0000, 0x4000, CRC(19c2bc87) SHA1(152f8c645588be9fc0dbc840368ab33a13a04e62) ) // Version 1.0

	ROM_REGION(0x4000, vp415_state::SYNC_ROM_TAG, 0)
	ROM_LOAD( "w.3104 103 6808.0_cpu", 0x0000, 0x4000, CRC(bdb601e0) SHA1(4f769aa62b756b157ba9ac8d3ae8bd1228821ff9) ) // Version 1.0

	ROM_REGION(0x400, vp415_state::DATAMCU_TAG, 0)
	ROM_LOAD( "d8041ahc 152.7211", 0x000, 0x400, CRC(2972d4b2) SHA1(e08086714fa5be1a67feac8f64210b21bb410dd3) ) // Same contents as 7252; this is intentional!
ROM_END

} // anonymous namespace

CONS( 1983, vp415, 0, 0, vp415, vp415, vp415_state, empty_init, "Philips", "VP415", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



vp60.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/***********************************************************************************************************************************

Skeleton driver for ADDS Viewpoint 60 terminal.
No significant progress can be made until the 8051 has its internal ROM dumped.

************************************************************************************************************************************/

#include "emu.h"
#include "cpu/mcs48/mcs48.h"
#include "cpu/mcs51/mcs51.h"
//#include "machine/er2055.h"
#include "video/i8275.h"
#include "screen.h"


namespace {

class vp60_state : public driver_device
{
public:
	vp60_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_crtc(*this, "crtc")
		, m_p_chargen(*this, "chargen")
	{ }

	void vp60(machine_config &config);

private:
	I8275_DRAW_CHARACTER_MEMBER(draw_character);
	u8 crtc_r(offs_t offset);
	void crtc_w(offs_t offset, u8 data);

	void io_map(address_map &map) ATTR_COLD;
	void kbd_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<i8275_device> m_crtc;
	required_region_ptr<u8> m_p_chargen;
};

I8275_DRAW_CHARACTER_MEMBER(vp60_state::draw_character)
{
}

u8 vp60_state::crtc_r(offs_t offset)
{
	return m_crtc->read(offset >> 8);
}

void vp60_state::crtc_w(offs_t offset, u8 data)
{
	m_crtc->write(offset >> 8, data);
}

void vp60_state::mem_map(address_map &map)
{
	map(0x0000, 0x2fff).rom().region("maincpu", 0);
}

void vp60_state::io_map(address_map &map)
{
	map(0x8000, 0x87ff).ram();
	map(0xc000, 0xc000).select(0x100).mirror(0xff).rw(FUNC(vp60_state::crtc_r), FUNC(vp60_state::crtc_w));
}

void vp60_state::kbd_map(address_map &map)
{
	map(0x000, 0x3ff).rom().region("keyboard", 0);
}

static INPUT_PORTS_START( vp60 )
INPUT_PORTS_END

void vp60_state::vp60(machine_config &config)
{
	I8051(config, m_maincpu, 10.92_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &vp60_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &vp60_state::io_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(25.92_MHz_XTAL, 1600, 0, 1280, 270, 0, 250);
	//screen.set_raw(25.92_MHz_XTAL, 1632, 0, 1280, 319, 0, 275);
	screen.set_screen_update("crtc", FUNC(i8275_device::screen_update));

	I8275(config, m_crtc, 25.92_MHz_XTAL / 16);
	m_crtc->set_character_width(16);
	m_crtc->set_display_callback(FUNC(vp60_state::draw_character));
	m_crtc->set_screen("screen");

	i8035_device &kbdcpu(I8035(config, "kbdcpu", 3.579545_MHz_XTAL)); // 48-300-010 XTAL
	kbdcpu.set_addrmap(AS_PROGRAM, &vp60_state::kbd_map);
}


/**************************************************************************************************************

ADDS Viewpoint 60.
Chips: P8051, P8275, EAROM ER-2055, HM6116P-4
Crystals: 25.92, 10.920
Keyboard: INS8035N-6, crystal marked 48-300-010.

***************************************************************************************************************/

ROM_START( vp60 )
	ROM_REGION(0x4000, "maincpu", ROMREGION_ERASE00)
	ROM_LOAD( "p8051.ub1",  0x0000, 0x1000, NO_DUMP ) // internal ROM not dumped
	ROM_LOAD( "pgm.uc1",    0x2000, 0x1000, CRC(714ca569) SHA1(405424369fd5458e02c845c104b2cb386bd857d2) )
	ROM_CONTINUE(           0x1000, 0x1000 )
	// Stubs filling in for missing code
	ROM_FILL( 0x0000, 1, 0x02 )
	ROM_FILL( 0x0001, 1, 0x10 )
	ROM_FILL( 0x0002, 1, 0x09 )
	ROM_FILL( 0x005d, 1, 0x02 )
	ROM_FILL( 0x005e, 1, 0x27 )
	ROM_FILL( 0x005f, 1, 0x2e )
	ROM_FILL( 0x0100, 1, 0x22 )
	ROM_FILL( 0x0500, 1, 0x22 )
	ROM_FILL( 0x0600, 1, 0x22 )
	ROM_FILL( 0x0800, 1, 0x22 )

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "font.uc4",   0x0000, 0x1000, CRC(3c4d39c0) SHA1(9503c0d5a76e8073c94c86be57bcb312641f6cc4) )

	ROM_REGION(0x400, "keyboard", 0)
	ROM_LOAD( "195.kbd",    0x0000, 0x0400, CRC(14885da3) SHA1(3b06f658af1a62b28e62d8b3a557b74169917a12) )
ROM_END

} // anonymous namespace


COMP( 1982, vp60, 0, 0, vp60, vp60, vp60_state, empty_init, "ADDS", "Viewpoint 60", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



vsc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Kevin Horton, Jonathan Gevaryahu, Sandro Ronco, hap
/*******************************************************************************

Fidelity Voice Sensory Chess Challenger (VSC)
---------------------------------------------
RE notes by Kevin Horton

The display/button/LED/speech technology is identical to Fidelity CSC.
Only the CPU board was changed. As such, it works the same but is interfaced
to different port chips this time.

Hardware:
---------
On the board are 13 chips.

The CPU is a Z80A running at 3.9MHz, with 20K of ROM and 1K of RAM mapped.
I/O is composed of an 8255 triple port adaptor, and a Z80A PIO parallel I/O
interface.

There's the usual TSI S14001A speech synth with its requisite 4K ROM which is the
same as on the other talking chess boards. The TSI chip is running at 26.37KHz.
It uses a 470K resistor and a 100pf capacitor.

The "perfect" clock would be 1/RC most likely (actually this will be skewed a tad by
duty cycle of the oscillator) which with those parts values gives 21.27KHz. The
formula is probably more likely to be 1/1.2RC or so.

Rounding out the hardware are three driver chips for the LEDs, a 7404 inverter to
run the crystal osc, a 555 timer to generate a clock, and a 74138 selector.

NMI runs to a 555 oscillator that generates a 600Hz clock (measured: 598.9Hz.
It has a multiturn pot to adjust).
INT is pulled to 5V
RST connects to a power-on reset circuit

Memory map:
-----------
0000-1FFF: 8K ROM 101-64018 or 101-64015
2000-3FFF: 8K ROM 101-64019 or 101-64016 (101-64019 is also used on the CSC)
4000-5FFF: 4K ROM 101-32024 or 101-32019
6000-7FFF: 1K of RAM (2114 * 2)
8000-FFFF: not used, maps to open bus

Port map:
---------
There's only two chips in the portmap, an 8255 triple port chip, and a Z80A PIO
parallel input/output device.

Decoding isn't performed using a selector, but instead address lines are used.

A2 connects to /CE on the 8255
A3 connects to /CE on the Z80A PIO

A0 connects to port A/B select on PIO & A0 of 8255
A1 connects to control/data select on PIO & A1 of 8255

So to enable only the 8255, you'd write/read to 08-0Bh for example
To enable only the PIO, you'd write/read to 04-07h for example.

writing to 00-03h will enable and write to BOTH chips, and reading 00-03h
will return data from BOTH chips (and cause a bus conflict). The code probably
never does either of these things.

Likewise, writing/reading to 0Ch-0Fh will result in open bus, because neither chip's
enable line will be low.

This sequence repeats every 16 addresses. So to recap:

00-03: both chips enabled (probably not used)
04-07: PIO enabled
08-0B: 8255 enabled
0C-0F: neither enabled

10-FF: mirrors of 00-0F.

Refer to the Sensory Champ. Chess Chall. for explanations of the below
I/O names and labels. It's the same.

8255:
-----
PA.0 - segment D, TSI A0
PA.1 - segment E, TSI A1
PA.2 - segment F, TSI A2
PA.3 - segment A, TSI A3
PA.4 - segment B, TSI A4
PA.5 - segment C, TSI A5
PA.6 - segment G
PA.7 - segment H

PB.0 - LED row 1
PB.1 - LED row 2
PB.2 - LED row 3
PB.3 - LED row 4
PB.4 - LED row 5
PB.5 - LED row 6
PB.6 - LED row 7
PB.7 - LED row 8

PC.0 - LED column A, button column A, 7seg digit 1
PC.1 - LED column B, button column B, 7seg digit 2
PC.2 - LED column C, button column C, 7seg digit 3
PC.3 - LED column D, button column D, 7seg digit 4
PC.4 - LED column E, button column E
PC.5 - LED column F, button column F
PC.6 - LED column G, button column G
PC.7 - LED column H, button column H

Z80A PIO:
---------
PA.0 - button row 1
PA.1 - button row 2
PA.2 - button row 3
PA.3 - button row 4
PA.4 - button row 5
PA.5 - button row 6
PA.6 - button row 7
PA.7 - button row 8

PB.0 - button column I
PB.1 - button column J
PB.2 - hi/lo TSI speaker volume
PB.3 - violet wire to printer port?
PB.4 - white wire to printer port? (and TSI BUSY line)
PB.5 - selection jumper input (see below)
PB.6 - TSI START line
PB.7 - TSI ROM A12 line

Selection jumpers:
------------------
These act like another row of buttons. It is composed of two diode locations,
so there's up to 4 possible configurations. My board does not have either diode
stuffed, so this most likely is "English". I suspect it selects which language
to use for the speech synth. Of course you need the other speech ROMs for this
to function properly.

Anyways, the two jumpers are connected to button columns A and B and the common
connects to Z80A PIO PB.5, which basically makes a 10th button row. I would
expect that the software reads these once on startup only.

Printer:
--------
This is the 1st Fidelity chess computer with a printer port. Many later Fidelity
chess computers also have support for it. Two models were released:
FP: Challenger Printer - thermal printer, MCU=D8048C243
IFP: Impact Printer - also compatible with C64 apparently.

The printer expects a baud rate of 600, 7 data bits, 1 stop bit, and no parity.

*******************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "machine/i8255.h"
#include "machine/clock.h"
#include "machine/sensorboard.h"
#include "machine/z80pio.h"
#include "sound/s14001a.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "fidel_vsc.lh"


namespace {

class vsc_state : public driver_device
{
public:
	vsc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_z80pio(*this, "z80pio"),
		m_ppi8255(*this, "ppi8255"),
		m_rs232(*this, "rs232"),
		m_board(*this, "board"),
		m_display(*this, "display"),
		m_speech(*this, "speech"),
		m_language(*this, "language"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// machine configs
	void vsc(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	// devices/pointers
	required_device<cpu_device> m_maincpu;
	required_device<z80pio_device> m_z80pio;
	required_device<i8255_device> m_ppi8255;
	required_device<rs232_port_device> m_rs232;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_display;
	required_device<s14001a_device> m_speech;
	required_region_ptr<u8> m_language;
	required_ioport_array<2> m_inputs;

	u8 m_led_data = 0;
	u8 m_7seg_data = 0;
	u8 m_cb_mux = 0;
	u8 m_kp_mux = 0;

	// address maps
	void main_map(address_map &map) ATTR_COLD;
	void main_io(address_map &map) ATTR_COLD;
	u8 main_io_trampoline_r(offs_t offset);
	void main_io_trampoline_w(offs_t offset, u8 data);

	// I/O handlers
	void update_display();
	void ppi_porta_w(u8 data);
	void ppi_portb_w(u8 data);
	void ppi_portc_w(u8 data);
	u8 pio_porta_r();
	u8 pio_portb_r();
	void pio_portb_w(u8 data);
};

void vsc_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_led_data));
	save_item(NAME(m_7seg_data));
	save_item(NAME(m_cb_mux));
	save_item(NAME(m_kp_mux));
}



/*******************************************************************************
    I/O
*******************************************************************************/

// I8255 PPI

void vsc_state::update_display()
{
	// 4 7seg leds+H, 8*8 chessboard leds
	m_display->matrix(m_cb_mux, m_led_data << 8 | m_7seg_data);
}

void vsc_state::ppi_porta_w(u8 data)
{
	// d0-d5: S14001A C0-C5
	m_speech->data_w(data & 0x3f);

	// d0-d7: data for the 4 7seg leds, bits are HGCBAFED (H is extra led)
	m_7seg_data = bitswap<8>(data,7,6,2,1,0,5,4,3);
	update_display();
}

void vsc_state::ppi_portb_w(u8 data)
{
	// d0-d7: led row data
	m_led_data = data;
	update_display();
}

void vsc_state::ppi_portc_w(u8 data)
{
	// d0-d3: select digits
	// d0-d7: select leds, chessboard input mux
	m_cb_mux = data;
	update_display();
}


// Z80 PIO

u8 vsc_state::pio_porta_r()
{
	u8 data = 0;

	// d0-d7: multiplexed inputs
	// read chessboard sensors
	for (int i = 0; i < 8; i++)
		if (BIT(m_cb_mux, i))
			data |= m_board->read_file(i);

	// read other buttons
	for (int i = 0; i < 2; i++)
		if (BIT(m_kp_mux, i))
			data |= m_inputs[i]->read();

	// also language jumpers (hardwired with 2 diodes)
	if (m_kp_mux & 0x20)
		data |= *m_language;

	return data;
}

u8 vsc_state::pio_portb_r()
{
	// d4: S14001A busy pin / printer busy
	return (m_speech->busy_r()) ? 0 : 0x10;
}

void vsc_state::pio_portb_w(u8 data)
{
	// d0,d1: keypad input mux
	// d5: enable language jumpers
	m_kp_mux = data;

	// d3: printer port data
	m_rs232->write_txd(BIT(~data, 3));

	// d7: speech ROM A12
	m_speech->set_rom_bank(BIT(data, 7));

	// d6: S14001A start pin
	m_speech->start_w(BIT(data, 6));

	// d2: lower S14001A volume
	m_speech->set_output_gain(0, (data & 4) ? 0.25 : 1.0);
}



/*******************************************************************************
    Address Maps
*******************************************************************************/

void vsc_state::main_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).rom();
	map(0x4000, 0x4fff).mirror(0x1000).rom();
	map(0x6000, 0x63ff).mirror(0x1c00).ram();
}

// VSC io: A2 is 8255 _CE, A3 is Z80 PIO _CE - in theory, both chips can be accessed simultaneously
u8 vsc_state::main_io_trampoline_r(offs_t offset)
{
	u8 data = 0xff; // open bus
	if (~offset & 4)
		data &= m_ppi8255->read(offset & 3);
	if (~offset & 8)
		data &= m_z80pio->read(offset & 3);

	return data;
}

void vsc_state::main_io_trampoline_w(offs_t offset, u8 data)
{
	if (~offset & 4)
		m_ppi8255->write(offset & 3, data);
	if (~offset & 8)
		m_z80pio->write(offset & 3, data);
}

void vsc_state::main_io(address_map &map)
{
	map.global_mask(0x0f);
	map(0x00, 0x0f).rw(FUNC(vsc_state::main_io_trampoline_r), FUNC(vsc_state::main_io_trampoline_w));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( vsc )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Pawn")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Rook")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Knight")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Queen")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("King")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CL")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_N) PORT_NAME("RE")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("TM")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_V) PORT_NAME("RV")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_SPACE) PORT_NAME("Speaker")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("LV")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("DM")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_S) PORT_NAME("ST")
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void vsc_state::vsc(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 3.9_MHz_XTAL); // 3.9MHz resonator
	m_maincpu->set_addrmap(AS_PROGRAM, &vsc_state::main_map);
	m_maincpu->set_addrmap(AS_IO, &vsc_state::main_io);

	auto &nmi_clock(CLOCK(config, "nmi_clock", 600)); // 555 timer, ideal frequency is 600Hz (measurement was 587Hz)
	nmi_clock.set_pulse_width(attotime::from_usec(845)); // active for 0.845ms (approx half)
	nmi_clock.signal_handler().set_inputline(m_maincpu, INPUT_LINE_NMI);

	I8255(config, m_ppi8255);
	m_ppi8255->out_pa_callback().set(FUNC(vsc_state::ppi_porta_w));
	m_ppi8255->out_pb_callback().set(FUNC(vsc_state::ppi_portb_w));
	m_ppi8255->out_pc_callback().set(FUNC(vsc_state::ppi_portc_w));

	Z80PIO(config, m_z80pio, 3.9_MHz_XTAL);
	m_z80pio->in_pa_callback().set(FUNC(vsc_state::pio_porta_r));
	m_z80pio->in_pb_callback().set(FUNC(vsc_state::pio_portb_r));
	m_z80pio->out_pb_callback().set(FUNC(vsc_state::pio_portb_w));

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(250));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(8, 16);
	m_display->set_segmask(0xf, 0x7f);
	config.set_default_layout(layout_fidel_vsc);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	S14001A(config, m_speech, 25000); // R/C circuit, around 25khz
	m_speech->add_route(ALL_OUTPUTS, "speaker", 0.75);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( vsc )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD("101-64018", 0x0000, 0x2000, CRC(c9c98490) SHA1(e6db883df088d60463e75db51433a4b01a3e7626) )
	ROM_LOAD("101-64019", 0x2000, 0x2000, CRC(08a3577c) SHA1(69fe379d21a9d4b57c84c3832d7b3e7431eec341) )
	ROM_LOAD("101-32024", 0x4000, 0x1000, CRC(2a078676) SHA1(db2f0aba7e8ac0f84a17bae7155210cdf0813afb) )

	// speech ROM
	ROM_DEFAULT_BIOS("en")
	ROM_SYSTEM_BIOS(0, "en", "English")
	ROM_SYSTEM_BIOS(1, "de", "German")
	ROM_SYSTEM_BIOS(2, "fr", "French")
	ROM_SYSTEM_BIOS(3, "sp", "Spanish")

	ROM_REGION( 1, "language", 0 )
	ROMX_FILL(0, 1, 0, ROM_BIOS(0) )
	ROMX_FILL(0, 1, 1, ROM_BIOS(1) )
	ROMX_FILL(0, 1, 2, ROM_BIOS(2) )
	ROMX_FILL(0, 1, 3, ROM_BIOS(3) )

	ROM_REGION( 0x2000, "speech", 0 )
	ROMX_LOAD("101-32107", 0x0000, 0x1000, CRC(f35784f9) SHA1(348e54a7fa1e8091f89ac656b4da22f28ca2e44d), ROM_BIOS(0) )
	ROM_RELOAD(            0x1000, 0x1000)
	ROMX_LOAD("101-64101", 0x0000, 0x2000, CRC(6c85e310) SHA1(20d1d6543c1e6a1f04184a2df2a468f33faec3ff), ROM_BIOS(1) )
	ROMX_LOAD("101-64105", 0x0000, 0x2000, CRC(fe8c5c18) SHA1(2b64279ab3747ee81c86963c13e78321c6cfa3a3), ROM_BIOS(2) )
	ROMX_LOAD("101-64106", 0x0000, 0x2000, CRC(8766e128) SHA1(78c7413bf240159720b131ab70bfbdf4e86eb1e9), ROM_BIOS(3) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY, FULLNAME, FLAGS
SYST( 1980, vsc,  0,      0,      vsc,     vsc,   vsc_state, empty_init, "Fidelity Electronics", "Voice Sensory Chess Challenger", MACHINE_SUPPORTS_SAVE )



vsmile.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/******************************************************************************

    V-Tech V.Smile console emulation

*******************************************************************************/

#include "emu.h"

#include "vsmile.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "vsmile.lh"

#define VERBOSE (1)
#include "logmacro.h"

/************************************
 *
 *  Common
 *
 ************************************/

void vsmile_base_state::machine_start()
{
	const int bank = m_cart && m_cart->exists() ? 4 : 0;

	m_bankdev->set_bank(bank);
}

void vsmile_base_state::chip_sel_w(uint8_t data)
{
	const uint16_t cart_offset = m_cart && m_cart->exists() ? 4 : 0;
	switch (data)
	{
		case 0:
			m_bankdev->set_bank(cart_offset);
			break;
		case 1:
			m_bankdev->set_bank(1 + cart_offset);
			break;
		case 2:
		case 3:
			m_bankdev->set_bank(2 + cart_offset);
			break;
	}
	m_maincpu->invalidate_cache();
}

uint16_t vsmile_base_state::bank3_r(offs_t offset)
{
	return ((uint16_t*)m_system_region->base())[offset];
}

void vsmile_state::machine_start()
{
	vsmile_base_state::machine_start();

	m_redled.resolve();
	m_yellowled.resolve();
	m_blueled.resolve();
	m_greenled.resolve();

	save_item(NAME(m_ctrl_rts));
	save_item(NAME(m_ctrl_select));
}

void vsmile_state::machine_reset()
{
	std::fill(std::begin(m_ctrl_rts), std::end(m_ctrl_rts), false);
	std::fill(std::begin(m_ctrl_select), std::end(m_ctrl_select), false);
}

void vsmile_state::ctrl_tx_w(uint8_t data)
{
	//printf("Ctrl Tx: %02x\n", data);
	m_maincpu->uart_rx(data);
}

template <int Which> void vsmile_state::ctrl_rts_w(int state)
{
	//printf("Ctrl%d RTS: %d\n", Which, state);
	m_ctrl_rts[Which] = state;
	m_maincpu->extint_w(Which, state);
}

void vsmile_state::uart_rx(uint8_t data)
{
	//printf("Ctrl Rx: %02x\n", data);
	m_ctrl[0]->data_w(data);
	m_ctrl[1]->data_w(data);

	//TODO: should be moved to pad code somehow
	if ((data & 0xF0) == 0x60)
	{
		if (m_ctrl_select[0])
		{
			m_redled[0] = BIT(data, 3);
			m_yellowled[0] = BIT(data, 2);
			m_blueled[0] = BIT(data, 1);
			m_greenled[0] = BIT(data, 0);
		}
		if (m_ctrl_select[1])
		{
			m_redled[1] = BIT(data, 3);
			m_yellowled[1] = BIT(data, 2);
			m_blueled[1] = BIT(data, 1);
			m_greenled[1] = BIT(data, 0);
		}
	}
}

uint16_t vsmile_state::portb_r()
{
	uint16_t data = m_dsw_system->read();
	//bit 0 : extra address bit for the cartridge port, access second half of ROM (TODO)
	//bit 1 : Set to 0 to enable cartridge ROM (TODO) -> getCS2
	//bit 2 : Set to 0 to enable internal ROM (TODO)
	//bit 3 : restart (see dipswitch)
	//      VSMILE_PORTB_RESET
	//bit 4 : ADC (TODO)
	//bit 5 : Voltage detect (TODO)
	//bit 6 : ON button, active low (see dipswitch)
	//      VSMILE_PORTB_ON_SW
	//bit 7 : OFF button, active low (see dipswitch)
	//      VSMILE_PORTB_OFF_SW

	//LOG("%s: portb_r: %04x\n", machine().describe_context(), data);

	//On Vsmile, VSMILE_PORTB_RESET, VSMILE_PORTB_OFF_SW and VSMILE_PORTB_ON_SW actives will trigger BIOS test screen
	return data;
}

void vsmile_state::portb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	LOG("%s: portb_w: %04x & %04x (bit 1: %d & %d)\n", machine().describe_context(), data, mem_mask, BIT(data, 1), BIT(mem_mask, 1));
	if (BIT(mem_mask, 1) && m_cart && m_cart->exists())
		m_cart->set_cs2(BIT(~data, 1));
}

uint16_t vsmile_state::portc_r()
{
	uint16_t data = m_dsw_region->read();
	data |= m_ctrl_rts[0] ? 0 : 0x0400;
	data |= m_ctrl_rts[1] ? 0 : 0x1000;
	data |= 0x0020; //IOC5 - TestPoint
	data |= (m_ctrl_rts[0] && m_ctrl_rts[1]) ? 0x0000 : 0x2000;
	//data = machine().rand() & 0xffff;
	return data;
}

void vsmile_state::portc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if (BIT(mem_mask, 8))
	{
		//printf("Ctrl0 SEL: %d\n", BIT(data, 8));
		m_ctrl_select[0] = BIT(data, 8);
		m_ctrl[0]->select_w(m_ctrl_select[0]);
	}
	if (BIT(mem_mask, 9))
	{
		//printf("Ctrl1 SEL: %d\n", BIT(data, 9));
		m_ctrl_select[1] = BIT(data, 9);
		m_ctrl[1]->select_w(m_ctrl_select[1]);
	}
}

/************************************
 *
 *  V.Smile Motion-specific
 *
 ************************************/

void vsmilem_state::porta_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	//printf("Port A write: %04x & %04x\n", data, mem_mask);
}

uint16_t vsmilem_state::porta_r(offs_t offset, uint16_t mem_mask)
{
	const uint16_t data = 0xc000;
	//printf("Port A read: %04x & %04x\n", data, mem_mask);
	return data;
}

/************************************
 *
 *  Address Maps
 *
 ************************************/

void vsmile_base_state::mem_map(address_map &map)
{
	map(0x000000, 0x3fffff).rw(m_bankdev, FUNC(address_map_bank_device::read16), FUNC(address_map_bank_device::write16));
}

void vsmile_state::banked_map(address_map &map)
{
	map(0x0000000, 0x00fffff).rom().region("sysrom", 0);
	map(0x0100000, 0x01fffff).rom().region("sysrom", 0);
	map(0x0200000, 0x02fffff).rom().region("sysrom", 0);
	map(0x0300000, 0x03fffff).rom().region("sysrom", 0);

	map(0x0400000, 0x04fffff).rom().region("sysrom", 0);
	map(0x0500000, 0x05fffff).rom().region("sysrom", 0);
	map(0x0600000, 0x06fffff).rom().region("sysrom", 0);
	map(0x0700000, 0x07fffff).rom().region("sysrom", 0);

	map(0x0800000, 0x08fffff).rom().region("sysrom", 0);
	map(0x0900000, 0x09fffff).rom().region("sysrom", 0);
	map(0x0a00000, 0x0afffff).rom().region("sysrom", 0);
	map(0x0b00000, 0x0bfffff).rom().region("sysrom", 0);

	map(0x1000000, 0x13fffff).rw(m_cart, FUNC(vsmile_cart_slot_device::bank0_r), FUNC(vsmile_cart_slot_device::bank0_w));

	map(0x1400000, 0x15fffff).rw(m_cart, FUNC(vsmile_cart_slot_device::bank0_r), FUNC(vsmile_cart_slot_device::bank0_w));
	map(0x1600000, 0x17fffff).rw(m_cart, FUNC(vsmile_cart_slot_device::bank2_r), FUNC(vsmile_cart_slot_device::bank2_w));

	map(0x1800000, 0x18fffff).rw(m_cart, FUNC(vsmile_cart_slot_device::bank0_r), FUNC(vsmile_cart_slot_device::bank0_w));
	map(0x1900000, 0x19fffff).rw(m_cart, FUNC(vsmile_cart_slot_device::bank1_r), FUNC(vsmile_cart_slot_device::bank1_w));
	map(0x1a00000, 0x1afffff).rw(m_cart, FUNC(vsmile_cart_slot_device::bank2_r), FUNC(vsmile_cart_slot_device::bank2_w));
	map(0x1b00000, 0x1bfffff).r(FUNC(vsmile_state::bank3_r));
}

/************************************
 *
 *  Inputs
 *
 ************************************/

static INPUT_PORTS_START( vsmile )
	PORT_START("REGION")
	//based on schematics and BIOS test screen
	PORT_CONFNAME( 0x0f, 0x0f, DEF_STR(Language) )
	PORT_CONFSETTING(    0x02, DEF_STR(Italian) ) //IT
	PORT_CONFSETTING(    0x07, DEF_STR(Chinese) ) //Chinese
	PORT_CONFSETTING(    0x08, "Portuguese" ) //PO
	PORT_CONFSETTING(    0x09, "Dutch" ) //DU
	PORT_CONFSETTING(    0x0b, DEF_STR(German) ) //GE
	PORT_CONFSETTING(    0x0c, DEF_STR(Spanish) ) //SP
	PORT_CONFSETTING(    0x0d, DEF_STR(French) ) //FR
	PORT_CONFSETTING(    0x0e, "English (UK)" ) //UK
	PORT_CONFSETTING(    0x0f, "English (US)" ) //US
	PORT_CONFNAME( 0x10, 0x10, "VTech Intro" )
	PORT_CONFSETTING(    0x00, DEF_STR(Off) )
	PORT_CONFSETTING(    0x10, DEF_STR(On) )
	PORT_BIT( 0xe0, 0x00, IPT_UNUSED )

	PORT_START("SYSTEM")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_POWER_OFF )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_POWER_ON )
	PORT_CONFNAME( 0x08, 0x08, "Restart")
	PORT_CONFSETTING(    0x08, DEF_STR(Off) )
	PORT_CONFSETTING(    0x00, DEF_STR(On) )
	PORT_BIT( 0x37, 0x00, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( vsmilem )
	PORT_START("REGION")
	PORT_CONFNAME( 0x0f, 0x0f, "sysrom Region" )
	PORT_CONFSETTING(    0x02, "Italy" ) // V.Smile Motion logo with "Active Learning System", voice 1, regular cartridge image, "Per favore inserisci una cartuccia di gioco" text (possibly invalid as text on logo is still in English and Italy was previously 0x0a)
	PORT_CONFSETTING(    0x05, "English (1)" ) // V.Smile Motion logo with "Active Learning System", voice 2, regular cartridge image, "Please insert a Learning Game" text
	PORT_CONFSETTING(    0x06, "English (2)" ) // V.Smile Motion logo with "Active Learning System", voice 1, regular cartridge image, "Please insert a Learning Game" text
	PORT_CONFSETTING(    0x07, "China" ) // V.Smile Motion logo with "Active Learning System", voice 1, regular cartridge image, Chinese text
	PORT_CONFSETTING(    0x08, "Mexico" ) // V.Smile Motion logo with "Sistema Educativo", voice 1, regular cartridge image, "TV Learning System" text
	PORT_CONFSETTING(    0x09, "Netherlands?" ) // V.Smile Motion logo with "Active Learning System", voice 3, regular cartridge image, "Plaats een game"
	PORT_CONFSETTING(    0x0b, "Germany" ) // V.Smile Motion logo with "Aktives Lernspiel - System", voice 4, regular cartridge image, "Bitte Lernspiel einstecken"
	PORT_CONFSETTING(    0x0c, "Spain" ) // V.Smile Motion logo with "Aprendizaje Inteligente En Accion", voice 5, regular cartridge image, "Por favor, inserta un cartuncho"
	PORT_CONFSETTING(    0x0d, "France" ) // V.Smile Motion logo with "Apprendre En Mouvements", voice 6, regular cartridge image, "Inserer une cartouche"
	PORT_CONFSETTING(    0x0f, "English (3)" ) // V.Smile Motion logo with "Active Learning System", voice 2, regular cartridge image, "Please insert a Smartridge(tm)" text   (Smartridge must be a region specific term?)

	PORT_CONFNAME( 0x10, 0x10, "VTech Intro" )
	PORT_CONFSETTING(    0x00, "Off" )
	PORT_CONFSETTING(    0x10, "On" )
	PORT_BIT( 0xe0, 0x00, IPT_UNUSED )

	PORT_START("SYSTEM")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_POWER_OFF )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_POWER_ON )
	PORT_CONFNAME( 0x08, 0x08, "Restart")
	PORT_CONFSETTING(    0x08, DEF_STR(Off) )
	PORT_CONFSETTING(    0x00, DEF_STR(On) )
	PORT_BIT( 0x37, 0x00, IPT_UNUSED )
INPUT_PORTS_END

/************************************
 *
 *  Machine Configs
 *
 ************************************/

static void vsmile_cart(device_slot_interface &device)
{
	device.option_add_internal("vsmile_rom",   VSMILE_ROM_STD);
	device.option_add_internal("vsmile_nvram", VSMILE_ROM_NVRAM);
}

void vsmile_base_state::vsmile_base(machine_config &config)
{
	config.set_default_layout(layout_vsmile);

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320, 262);
	m_screen->set_visarea(0, 320-1, 0, 240-1);
	m_screen->set_screen_update("maincpu", FUNC(spg2xx_device::screen_update));
	m_screen->screen_vblank().set(m_maincpu, FUNC(spg2xx_device::vblank));

	SPEAKER(config, "speaker", 2).front();

	ADDRESS_MAP_BANK(config, m_bankdev);
	m_bankdev->set_endianness(ENDIANNESS_BIG);
	m_bankdev->set_data_width(16);
	m_bankdev->set_shift(-1);
	m_bankdev->set_stride(0x400000);

	VSMILE_CART_SLOT(config, m_cart, vsmile_cart, nullptr);
}

void vsmile_state::vsmile(machine_config &config)
{
	SPG24X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &vsmile_state::mem_map);
	m_maincpu->set_force_no_drc(true);
	m_maincpu->chip_select().set(FUNC(vsmile_state::chip_sel_w));
	m_maincpu->add_route(ALL_OUTPUTS, "speaker", 0.5, 0);
	m_maincpu->add_route(ALL_OUTPUTS, "speaker", 0.5, 1);
	m_maincpu->portb_in().set(FUNC(vsmile_state::portb_r));
	m_maincpu->portb_out().set(FUNC(vsmile_state::portb_w));
	m_maincpu->portc_in().set(FUNC(vsmile_state::portc_r));
	m_maincpu->portc_out().set(FUNC(vsmile_state::portc_w));
	m_maincpu->uart_tx().set(FUNC(vsmile_state::uart_rx));

	vsmile_base(config);

	m_bankdev->set_addrmap(AS_PROGRAM, &vsmile_state::banked_map);

	VSMILE_CTRL_PORT(config, m_ctrl[0], vsmile_controllers, "joy");
	m_ctrl[0]->rts_cb().set(FUNC(vsmile_state::ctrl_rts_w<0>));
	m_ctrl[0]->data_cb().set(FUNC(vsmile_state::ctrl_tx_w));

	VSMILE_CTRL_PORT(config, m_ctrl[1], vsmile_controllers, nullptr);
	m_ctrl[1]->rts_cb().set(FUNC(vsmile_state::ctrl_rts_w<1>));
	m_ctrl[1]->data_cb().set(FUNC(vsmile_state::ctrl_tx_w));

	SOFTWARE_LIST(config, "cart_list").set_original("vsmile_cart");
	SOFTWARE_LIST(config, "cart_list2").set_original("vsmilem_cart");
}

void vsmile_state::vsmilep(machine_config &config)
{
	vsmile(config);
	m_maincpu->set_pal(true);
}

void vsmilem_state::vsmilem(machine_config &config)
{
	vsmile(config);
	m_maincpu->porta_out().set(FUNC(vsmilem_state::porta_w));
	m_maincpu->porta_in().set(FUNC(vsmilem_state::porta_r));
}

/************************************
 *
 *  ROM Loading
 *
 ************************************/

// NOTE: many games contain additional spare copies of the BIOS in their own cartridge ROM, reason unknown

ROM_START( vsmile )
	ROM_REGION16_BE( 0x800000, "sysrom", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "v103", "v103" )
	ROMX_LOAD( "vsmile_v103.bin", 0x000000, 0x200000, CRC(387fbc24) SHA1(5f2fd211b6ff3a6f5121b14adc6bbf4f49e89f33),  ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(0) ) // this is the earliest version used on the V.Smile Pocket, but it isn't system specific
	ROM_SYSTEM_BIOS( 1, "v102", "v102" )
	ROMX_LOAD( "vsmile_v102.bin", 0x000000, 0x200000, CRC(0cd0bdf5) SHA1(5c8d1eada1b6b545555b8d2b09325d7127681af8),  ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(1) ) // found in all 'fat' model systems
	ROM_SYSTEM_BIOS( 2, "v100", "v100" )
	ROMX_LOAD( "vsmile_v100.bin", 0x000000, 0x200000, CRC(205c5296) SHA1(7fbcf761b5885c8b1524607aabaf364b4559c8cc),  ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(2) )
ROM_END


ROM_START( vsmilem )
	ROM_REGION16_BE( 0x800000, "sysrom", ROMREGION_ERASEFF )
	ROM_SYSTEM_BIOS( 0, "bios0", "bios0" )
	ROMX_LOAD( "vsmilemotion.bin", 0x000000, 0x200000, CRC(60fa5426) SHA1(91e0b7b44b975df65095d6ee622436d65fb1aca5), ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(0) ) // from a Spanish unit (but doesn't seem region specific)

	/* This ROM doesn't show the 'Motion' logo at all, but was dumped from a Motion unit

	    Console says "Vtech V.Smile V-motion Active Learning System"
	    "FCC ID 62R-0788, IC 1135D-0788" "53-36600-056-080"
	    melted into plastic "VT8281"
	    The PCB has the code 35-078800-001-103_708979-2.
	*/
	ROM_SYSTEM_BIOS( 1, "bios1", "bios1" )
	ROMX_LOAD( "vmotionbios.bin", 0x000000, 0x200000, CRC(427087ea) SHA1(dc9eaa55f4a0047b6069ef73beea86d26f0f5394), ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(1) ) // from a US unit
ROM_END


//    year, name,    parent, compat, machine, input,   class,         init,       company, fullname,         flags
CONS( 2005, vsmile,  0,      0,      vsmile,  vsmile,  vsmile_state,  empty_init, "VTech", "V.Smile",        MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )
CONS( 2008, vsmilem, vsmile, 0,      vsmilem, vsmilem, vsmilem_state, empty_init, "VTech", "V.Smile Motion", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS )



vsmileb.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/******************************************************************************

    V-Tech V.Smile Baby console emulation

    System is currently marked as not-working due to severe audio issues, as
    narration-related voice clips are all cut off too early in every game.

    The issue appears to be due to improper handling of the "Fast Rampdown"
    feature in the SPG2xx audio device, but due to the poor quality of SunPlus
    developer documentation, it's unclear as to what part of the implementation
    is wrong.

    If the Fast Rampdown feature is disabled in the audio device entirely,
    voice clips play out without issue.

    Although bad audio is not usually a reason for marking a driver as
    non-working, due to the nature of the V.Smile Baby and its target users,
    having properly-narrated voices is critical to the overall experience of
    the system.

*******************************************************************************/

#include "emu.h"

#include "vsmile.h"

#include "softlist_dev.h"
#include "speaker.h"


/************************************
 *
 *  V.Smile Baby
 *
 ************************************/

void vsmileb_state::machine_start()
{
	vsmile_base_state::machine_start();

	save_item(NAME(m_mode));
}

void vsmileb_state::machine_reset()
{
	m_mode = 0x0400;
}

uint16_t vsmileb_state::porta_r()
{
	return 0x0302 | (m_io_logo->read() ? 0x0080 : 0x0000);
}

uint16_t vsmileb_state::portb_r()
{
	return 0x0080;
}

INPUT_CHANGED_MEMBER(vsmileb_state::pad_button_changed)
{
	uint16_t value = m_mode;
	if (newval == 0)
	{
		value |= 0x0080;
	}
	else
	{
		value |= (uint16_t)param;
	}
	m_maincpu->uart_rx((uint8_t)(value >> 8));
	m_maincpu->uart_rx((uint8_t)value);
}

template <uint16_t V> INPUT_CHANGED_MEMBER(vsmileb_state::sw_mode)
{
	if (!newval && oldval)
	{
		m_mode = V;
		const uint16_t value = m_mode | 0x0080;
		m_maincpu->uart_rx((uint8_t)(value >> 8));
		m_maincpu->uart_rx((uint8_t)value);
	}
}

void vsmileb_state::banked_map(address_map &map)
{
	map(0x0000000, 0x03fffff).rom().region("sysrom", 0);
	map(0x0400000, 0x07fffff).rom().region("sysrom", 0);
	map(0x0800000, 0x0bfffff).rom().region("sysrom", 0);

	map(0x1000000, 0x13fffff).rw(m_cart, FUNC(vsmile_cart_slot_device::bank0_r), FUNC(vsmile_cart_slot_device::bank0_w));

	map(0x1400000, 0x15fffff).rw(m_cart, FUNC(vsmile_cart_slot_device::bank0_r), FUNC(vsmile_cart_slot_device::bank0_w));
	map(0x1600000, 0x17fffff).rw(m_cart, FUNC(vsmile_cart_slot_device::bank2_r), FUNC(vsmile_cart_slot_device::bank2_w));

	map(0x1800000, 0x18fffff).rw(m_cart, FUNC(vsmile_cart_slot_device::bank0_r), FUNC(vsmile_cart_slot_device::bank0_w));
	map(0x1900000, 0x19fffff).rw(m_cart, FUNC(vsmile_cart_slot_device::bank1_r), FUNC(vsmile_cart_slot_device::bank1_w));
	map(0x1a00000, 0x1afffff).rw(m_cart, FUNC(vsmile_cart_slot_device::bank2_r), FUNC(vsmile_cart_slot_device::bank2_w));
	map(0x1b00000, 0x1bfffff).r(FUNC(vsmileb_state::bank3_r));
}

/************************************
 *
 *  Inputs
 *
 ************************************/

static INPUT_PORTS_START( vsmileb )
	PORT_START("BUTTONS")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vsmileb_state::pad_button_changed), vsmileb_state::BUTTON_YELLOW) PORT_NAME("Yellow")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vsmileb_state::pad_button_changed), vsmileb_state::BUTTON_BLUE)   PORT_NAME("Blue")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vsmileb_state::pad_button_changed), vsmileb_state::BUTTON_ORANGE) PORT_NAME("Orange")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vsmileb_state::pad_button_changed), vsmileb_state::BUTTON_GREEN)  PORT_NAME("Green")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vsmileb_state::pad_button_changed), vsmileb_state::BUTTON_RED)    PORT_NAME("Red")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vsmileb_state::pad_button_changed), vsmileb_state::BUTTON_CLOUD)  PORT_NAME("Cloud")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vsmileb_state::pad_button_changed), vsmileb_state::BUTTON_BALL)   PORT_NAME("Ball")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON8 ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vsmileb_state::pad_button_changed), vsmileb_state::BUTTON_EXIT)   PORT_NAME("Exit")

	PORT_START("MODE")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_1) PORT_NAME("Play Time")       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vsmileb_state::sw_mode<0x0400>), 0) // three-position function switch
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_2) PORT_NAME("Watch & Learn")   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vsmileb_state::sw_mode<0x0800>), 0)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_3) PORT_NAME("Learn & Explore") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vsmileb_state::sw_mode<0x0c00>), 0)

	PORT_START("LOGO")
	PORT_DIPNAME( 0x10, 0x10, "VTech Intro" )
	PORT_DIPSETTING(    0x00, DEF_STR(Off) )
	PORT_DIPSETTING(    0x10, DEF_STR(On) )
	PORT_BIT( 0xef, 0x00, IPT_UNUSED )
INPUT_PORTS_END

/************************************
 *
 *  Machine Configs
 *
 ************************************/

void vsmileb_state::vsmileb(machine_config &config)
{
	SPG28X(config, m_maincpu, XTAL(27'000'000), m_screen);
	m_maincpu->set_addrmap(AS_PROGRAM, &vsmileb_state::mem_map);
	m_maincpu->set_force_no_drc(true);
	m_maincpu->chip_select().set(FUNC(vsmileb_state::chip_sel_w));
	m_maincpu->add_route(ALL_OUTPUTS, "speaker", 0.5);
	m_maincpu->add_route(ALL_OUTPUTS, "speaker", 0.5);
	m_maincpu->porta_in().set(FUNC(vsmileb_state::porta_r));
	m_maincpu->portb_in().set(FUNC(vsmileb_state::portb_r));

	vsmile_base(config);

	m_bankdev->set_addrmap(AS_PROGRAM, &vsmileb_state::banked_map);

	SOFTWARE_LIST(config, "cart_list").set_original("vsmileb_cart");
}

void vsmileb_state::vsmilebp(machine_config &config)
{
	vsmileb(config);
	m_maincpu->set_pal(true);
}

/************************************
 *
 *  ROM Loading
 *
 ************************************/

ROM_START( vsmileb )
	ROM_REGION16_BE( 0x800000, "sysrom", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "vsmilebabybios.bin",                0x000000, 0x800000, CRC(58d4caa0) SHA1(0b636ff80fd7fc429d753a8beab2957f1e59cbde) )
ROM_END

ROM_START( vsmilebs )
	ROM_REGION16_BE( 0x800000, "sysrom", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "vsmilebabybios_spain_pooh.bin",     0x000000, 0x800000, CRC(a1926654) SHA1(a8ccbe29235bb44faef77b1e7d73a20221b005c2) )
ROM_END

ROM_START( vsmilebsw )
	ROM_REGION16_BE( 0x800000, "sysrom", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "vsmilebabybios_sweden.bin",         0x000000, 0x800000, CRC(8b464b19) SHA1(cea304ba886c39e86906aad3dce17d5fff7cfcbe) )
ROM_END

ROM_START( vsmilebg )
	ROM_REGION16_BE( 0x800000, "sysrom", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "vsmilebabybios_germany_pooh.bin",   0x000000, 0x800000, CRC(22261569) SHA1(8918a905af4bb186beb5577b1d295d9c037584f7) )
ROM_END

ROM_START( vsmilebf )
	ROM_REGION16_BE( 0x800000, "sysrom", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "vsmilebabybios_france_pooh.bin",    0x000000, 0x800000, CRC(3dfa2acb) SHA1(9b3a34dae5475f0c82187cb0c62183b46344b7ad) )
ROM_END

ROM_START( vsmilebfp )
	ROM_REGION16_BE( 0x800000, "sysrom", ROMREGION_ERASEFF )
	ROM_LOAD16_WORD_SWAP( "vsmilebabybios_france_patoune.bin", 0x000000, 0x800000, CRC(57757602) SHA1(a7495e1c6b2edaeb63bf1c658575689304f15804) )
ROM_END

//    year, name,      parent,  compat, machine,  input,   class,         init,       company, fullname,                                                                         flags
CONS( 2005, vsmileb,   0,       0,      vsmileb,  vsmileb, vsmileb_state, empty_init, "VTech", "V.Smile Baby (USA)",                                                             MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING )
CONS( 2005, vsmilebsw, vsmileb, 0,      vsmilebp, vsmileb, vsmileb_state, empty_init, "VTech", "V.Smile Baby (Sweden)",                                                          MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING )

// These have a game in the BIOS ROM, supplied as a 'Romless cart' with the device, so probably triggers a switch. Currently always banked in.
CONS( 2005, vsmilebs,  vsmileb, 0,      vsmileb,  vsmileb, vsmileb_state, empty_init, "VTech", "V.Smile Baby (Spain, with 'Aventuras en el Bosque de los Cien Acres')",          MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING )
CONS( 2005, vsmilebg,  vsmileb, 0,      vsmileb,  vsmileb, vsmileb_state, empty_init, "VTech", "V.Smile Baby (Germany, with 'Puuhs Hundert-Morgen-Wald')",                       MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING )
CONS( 2005, vsmilebf,  vsmileb, 0,      vsmileb,  vsmileb, vsmileb_state, empty_init, "VTech", "V.Smile Baby (France, with 'Winnie et ses amis dans la Foret des Reves Bleus')", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING )
CONS( 2005, vsmilebfp, vsmileb, 0,      vsmileb,  vsmileb, vsmileb_state, empty_init, "VTech", "V.Smile Baby (France, with 'En Ville avec l'ourson Patoune')",                   MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING )



vsmilepro.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Fabio Priuli
/***************************************************************************

    Skeleton driver for VTech V.Smile Pro CD System

    30/05/2016

    Some information about the hardware can be found at

    http://www.x86-secret.com/dossier-64-VTech_V_Smile_Pro.html


    In particular
    - It uses a LSI Zevio 1020 CPU + peripherals which comprises ARM926EJ-S CPU,
      ZSP400 DSP, 3D graphics processor & 2D graphics processor
    - The CD controller is a Sony CXD3059AR

****************************************************************************/

#include "emu.h"

#include "cpu/arm7/arm7.h"
#include "imagedev/cdromimg.h"

#include "cdrom.h"
#include "softlist.h"


namespace {

class vsmilpro_state : public driver_device
{
public:
	vsmilpro_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu")
	{ }

	void vsmilpro(machine_config &config);

private:
	void vsmilpro_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};

void vsmilpro_state::vsmilpro_map(address_map &map)
{
	map(0x00000000, 0x0007ffff).rom();
}


// Input ports
static INPUT_PORTS_START( vsmilpro )
INPUT_PORTS_END

void vsmilpro_state::vsmilpro(machine_config &config)
{
	// basic machine hardware
	ARM9(config, m_maincpu, 150000000);
	m_maincpu->set_addrmap(AS_PROGRAM, &vsmilpro_state::vsmilpro_map);

	CDROM(config, "cdrom").set_interface("vsmile_vdisk");

	SOFTWARE_LIST(config, "cd_list").set_original("vsmile_cd");
}

// ROM definition
ROM_START( vsmilpro )
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_LOAD( "70004.bin", 0x000000, 0x200000, CRC(b9161eac) SHA1(8d75fdeda8c4e228a0b1efd35011f9f667f9fb23) )
ROM_END

} // anonymous namespace


// Driver

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY  FULLNAME       FLAGS
COMP( 2007, vsmilpro, 0,      0,      vsmilpro, vsmilpro, vsmilpro_state, empty_init, "VTech", "V.Smile Pro", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



vt100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Jonathan Gevaryahu
/***************************************************************************

        DEC VT100 driver by Miodrag Milanovic

        29/04/2009 Preliminary driver.

        TODO: some video attributes are not fully supported yet
        TODO: support for the on-AVO character set ROMs
        TODO: finish support for the on-CPU board alternate character set ROM
        TODO: STP (standard terminal port) bus for VT1XX-AC and VT125

        An enormous amount of useful info can be derived from the VT125 technical manual:
        http://www.bitsavers.org/pdf/dec/terminal/vt100/EK-VT100-TM-003_VT100_Technical_Manual_Jul82.pdf starting on page 6-70, pdf page 316
        And its schematics:
        http://bitsavers.org/pdf/dec/terminal/vt125/MP01053_VT125_Mar82.pdf

*****************************************************************************

        Quick overview of "Set-Up" controls:

        2   Set/Clear Tab (A)       Shift+A   Set Answerback Message (B)
        3   Clear All Tabs (A)      Shift+S   Save Settings
        4   On Line/Local           Shift+R   Recall Settings
        5   Set-Up A/B
        6   Toggle 1/0 (B)
        7   Transmit Speed (B)
        8   Receive Speed (B)
        9   80/132 Columns (A)
        0   Reset Terminal

        If the NVR is not yet initialized, a '2' error will appear when the
        terminal is powered on. This is non-fatal and can ordinarily be
        remedied by entering "Set-Up" and then saving the settings.

****************************************************************************/

#include "emu.h"

#include "bus/rs232/rs232.h"
#include "cpu/i8085/i8085.h"
#include "cpu/z80/z80.h"
#include "machine/ay31015.h"
#include "machine/com8116.h"
#include "machine/er1400.h"
#include "machine/i8251.h"
#include "machine/input_merger.h"
#include "machine/ins8250.h"
#include "machine/rstbuf.h"
#include "vt100_kbd.h"
#include "vtvideo.h"
#include "screen.h"

#include "vt100.lh"


namespace {

class vt100_state : public driver_device
{
public:
	vt100_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_crtc(*this, "vt100_video"),
		m_keyboard(*this, "keyboard"),
		m_kbduart(*this, "kbduart"),
		m_pusart(*this, "pusart"),
		m_nvr(*this, "nvr"),
		m_rstbuf(*this, "rstbuf"),
		m_rs232(*this, "rs232"),
		m_printer_uart(*this, "printuart"),
		m_p_ram(*this, "p_ram"),
		m_phosphor(*this, "phosphor")
	{
	}

	void vt100(machine_config &config);
	void vt100ac(machine_config &config);
	void vt101(machine_config &config);
	void vt102(machine_config &config);
	void vt180(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	required_device<vt100_video_device> m_crtc;
	required_device<vt100_keyboard_device> m_keyboard;
	required_device<ay31015_device> m_kbduart;
	required_device<i8251_device> m_pusart;
	required_device<er1400_device> m_nvr;
	required_device<rst_pos_buffer_device> m_rstbuf;
	required_device<rs232_port_device> m_rs232;
	optional_device<ins8250_device> m_printer_uart;
	required_shared_ptr<u8> m_p_ram;

	required_ioport m_phosphor;

	u8 flags_r();
	u8 modem_r();
	void nvr_latch_w(u8 data);
	u8 printer_r(offs_t offset);
	void printer_w(offs_t offset, u8 data);
	u8 video_ram_r(offs_t offset);
	void uart_clock_w(u8 data);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	u32 screen_update_vt100(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	IRQ_CALLBACK_MEMBER(vt102_irq_callback);
	void vt100_mem(address_map &map) ATTR_COLD;
	void vt100_io(address_map &map) ATTR_COLD;
	void vt102_io(address_map &map) ATTR_COLD;
	void stp_mem(address_map &map) ATTR_COLD;
	void stp_io(address_map &map) ATTR_COLD;
	void vt180_mem(address_map &map) ATTR_COLD;
	void vt180_io(address_map &map) ATTR_COLD;
};




void vt100_state::vt100_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom();  // ROM ( 4 * 2K)
	map(0x2000, 0x3fff).ram().share("p_ram"); // Screen and scratch RAM
	//map(0x3000, 0x3fff).ram();  // AVO Attribute RAM (4 bits wide)
	// 0x4000, 0x7fff is unassigned
	map(0x8000, 0x9fff).rom();  // Program memory expansion ROM (4 * 2K)
	map(0xa000, 0xbfff).rom();  // Program memory expansion ROM (1 * 8K)
	// 0xc000, 0xffff is unassigned
}

void vt100_state::vt180_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom();
	map(0x2000, 0xffff).ram();
}

void vt100_state::vt180_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
}

// 0 - XMIT flag H
// 1 - Advance Video L
// 2 - Graphics Flag L
// 3 - Option present H
// 4 - Even field L
// 5 - NVR data H
// 6 - LBA 7 H
// 7 - Keyboard TBMT H
u8 vt100_state::flags_r()
{
	u8 ret = 0;

	ret |= m_pusart->txrdy_r();
	ret |= !m_nvr->data_r() << 5;
	ret |= m_crtc->lba7_r() << 6;
	ret |= m_kbduart->tbmt_r() << 7;
	return ret;
}

u8 vt100_state::modem_r()
{
	u8 ret = 0x0f;

	ret |= m_rs232->cts_r() << 7;
	ret |= m_rs232->si_r() << 6;
	ret |= m_rs232->ri_r() << 5;
	ret |= m_rs232->dcd_r() << 4;

	return ret;
}

void vt100_state::nvr_latch_w(u8 data)
{
	// data inverted due to negative logic
	m_nvr->c3_w(!BIT(data, 3));
	m_nvr->c2_w(!BIT(data, 2));
	m_nvr->c1_w(!BIT(data, 1));

	// C2 is used to disable pullup on data line
	m_nvr->data_w(BIT(data, 2) ? 1 : !BIT(data, 0));

	// SPDS present on pins 11, 19 and 23 of EIA connector
	m_rs232->write_spds(BIT(data, 5));
}

void vt100_state::vt100_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	// 0x00, 0x01 PUSART  (Intel 8251)
	map(0x00, 0x01).rw(m_pusart, FUNC(i8251_device::read), FUNC(i8251_device::write));
	// 0x02 Baud rate generator
	map(0x02, 0x02).w("dbrg", FUNC(com8116_device::stt_str_w));
	// 0x22 Modem buffer
	map(0x22, 0x22).r(FUNC(vt100_state::modem_r));
	// 0x42 Flags buffer
	map(0x42, 0x42).r(FUNC(vt100_state::flags_r));
	// 0x42 Brightness D/A latch
	map(0x42, 0x42).w(m_crtc, FUNC(vt100_video_device::brightness_w));
	// 0x62 NVR latch
	map(0x62, 0x62).w(FUNC(vt100_state::nvr_latch_w));
	// 0x82 Keyboard UART data
	map(0x82, 0x82).rw(m_kbduart, FUNC(ay31015_device::receive), FUNC(ay31015_device::transmit));
	// 0xA2 Video processor DC012
	map(0xa2, 0xa2).w(m_crtc, FUNC(vt100_video_device::dc012_w));
	// 0xC2 Video processor DC011
	map(0xc2, 0xc2).w(m_crtc, FUNC(vt100_video_device::dc011_w));
	// 0xE2 Graphics port
	// map(0xe2, 0xe2)
}

u8 vt100_state::printer_r(offs_t offset)
{
	return m_printer_uart->ins8250_r(offset >> 2);
}

void vt100_state::printer_w(offs_t offset, u8 data)
{
	m_printer_uart->ins8250_w(offset >> 2, data);
}

void vt100_state::vt102_io(address_map &map)
{
	vt100_io(map);
	map(0x03, 0x03).select(0x1c).r(FUNC(vt100_state::printer_r));
	map(0x23, 0x23).select(0x1c).w(FUNC(vt100_state::printer_w));
}

/* Input ports */
static INPUT_PORTS_START( vt100 )
	PORT_START("phosphor")
	PORT_CONFNAME(0x03, 0x00, "Phosphor Color")
	PORT_CONFSETTING(0x00, "Green")
	PORT_CONFSETTING(0x01, "Amber")
	PORT_CONFSETTING(0x02, "White")
INPUT_PORTS_END

u32 vt100_state::screen_update_vt100(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	switch (m_phosphor->read())
	{
		case 0x00:
			screen.set_color(rgb_t::green());
			break;
		case 0x01:
			screen.set_color(rgb_t::amber());
			break;
		case 0x02:
			screen.set_color(rgb_t::white());
			break;
	}
	m_crtc->video_update(bitmap, cliprect);
	return 0;
}


//Interrupts
// in latch A3 - keyboard
//          A4 - receiver
//          A5 - vertical frequency
//          all other set to 1
IRQ_CALLBACK_MEMBER(vt100_state::vt102_irq_callback)
{
	if (irqline == 0)
		return m_rstbuf->inta_cb(device, 0);
	else
		return 0xff;
}

void vt100_state::machine_start()
{
	m_kbduart->write_tsb(0);
	m_kbduart->write_eps(1);
	m_kbduart->write_np(1);
	m_kbduart->write_nb1(1);
	m_kbduart->write_nb2(1);
	m_kbduart->write_cs(1);
	m_kbduart->write_swe(0);

	m_pusart->write_cts(0);

	if (m_printer_uart.found())
	{
		auto *printer_port = subdevice<rs232_port_device>("printer");
		printer_port->write_dtr(0);
		printer_port->write_rts(0);
	}
}

void vt100_state::machine_reset()
{
	nvr_latch_w(0);
}

u8 vt100_state::video_ram_r(offs_t offset)
{
	return m_p_ram[offset];
}

void vt100_state::uart_clock_w(u8 data)
{
	m_kbduart->write_tcp(BIT(data, 1));
	m_kbduart->write_rcp(BIT(data, 1));

	if (data == 0 || data == 3)
		m_keyboard->signal_line_w(m_kbduart->so_r());
	else
		m_keyboard->signal_line_w(BIT(data, 0));
}

/* F4 Character Displayer */
static const gfx_layout vt100_charlayout =
{
	8, 16,                  /* 8 x 16 characters */
	256,                    /* 2 x 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{  0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_vt100 )
	GFXDECODE_ENTRY( "chargen", 0x0000, vt100_charlayout, 0, 1 )
GFXDECODE_END

void vt100_state::vt100(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, XTAL(24'883'200) / 9);
	m_maincpu->set_addrmap(AS_PROGRAM, &vt100_state::vt100_mem);
	m_maincpu->set_addrmap(AS_IO, &vt100_state::vt100_io);
	m_maincpu->set_irq_acknowledge_callback("rstbuf", FUNC(rst_pos_buffer_device::inta_cb));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_raw(XTAL(24'073'400)*2/3, 102*10, 0, 80*10, 262, 0, 25*10);
	//screen.set_raw(XTAL(24'073'400), 170*9, 0, 132*9, 262, 0, 25*10);
	screen.set_screen_update(FUNC(vt100_state::screen_update_vt100));
	screen.set_palette("vt100_video:palette");

	GFXDECODE(config, "gfxdecode", "vt100_video:palette", gfx_vt100);
//  PALETTE(config, "palette", palette_device::MONOCHROME);

	config.set_default_layout(layout_vt100);

	VT100_VIDEO(config, m_crtc, XTAL(24'073'400));
	m_crtc->set_screen("screen");
	m_crtc->set_chargen("chargen");
	m_crtc->ram_rd_callback().set(FUNC(vt100_state::video_ram_r));
	m_crtc->vert_freq_intr_wr_callback().set(m_rstbuf, FUNC(rst_pos_buffer_device::rst4_w));
	m_crtc->lba3_lba4_wr_callback().set(FUNC(vt100_state::uart_clock_w));
	m_crtc->lba7_wr_callback().set(m_nvr, FUNC(er1400_device::clock_w));

	I8251(config, m_pusart, XTAL(24'883'200) / 9);
	m_pusart->txd_handler().set(m_rs232, FUNC(rs232_port_device::write_txd));
	m_pusart->dtr_handler().set(m_rs232, FUNC(rs232_port_device::write_dtr));
	m_pusart->rts_handler().set(m_rs232, FUNC(rs232_port_device::write_rts));
	m_pusart->rxrdy_handler().set(m_rstbuf, FUNC(rst_pos_buffer_device::rst2_w));

	RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
	m_rs232->rxd_handler().set(m_pusart, FUNC(i8251_device::write_rxd));
	m_rs232->dsr_handler().set(m_pusart, FUNC(i8251_device::write_dsr));

	com8116_device &dbrg(COM5016_013(config, "dbrg", XTAL(24'883'200) / 9)); // COM5016T-013 (or WD1943CD-02), 2.7648Mhz Clock
	dbrg.fr_handler().set(m_pusart, FUNC(i8251_device::write_rxc));
	dbrg.ft_handler().set(m_pusart, FUNC(i8251_device::write_txc));

	ER1400(config, m_nvr);

	VT100_KEYBOARD(config, m_keyboard, 0).signal_out_callback().set(m_kbduart, FUNC(ay31015_device::write_si));

	AY31015(config, m_kbduart, 0);
	m_kbduart->write_dav_callback().set(m_rstbuf, FUNC(rst_pos_buffer_device::rst1_w));
	m_kbduart->set_auto_rdav(true);

	RST_POS_BUFFER(config, m_rstbuf, 0).int_callback().set_inputline(m_maincpu, 0);
}

void vt100_state::stp_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x1fff).rom().region("stp", 0);
	map(0x2000, 0x27ff).ram();
}

void vt100_state::stp_io(address_map &map)
{
	map(0x80, 0x80).rw("stpusart0", FUNC(i8251_device::data_r), FUNC(i8251_device::data_w));
	map(0x90, 0x90).rw("stpusart0", FUNC(i8251_device::status_r), FUNC(i8251_device::control_w));
	map(0xa0, 0xa0).rw("stpusart1", FUNC(i8251_device::data_r), FUNC(i8251_device::data_w));
	map(0xb0, 0xb0).rw("stpusart1", FUNC(i8251_device::status_r), FUNC(i8251_device::control_w));
	map(0xc0, 0xc0).rw("stpusart2", FUNC(i8251_device::data_r), FUNC(i8251_device::data_w));
	map(0xd0, 0xd0).rw("stpusart2", FUNC(i8251_device::status_r), FUNC(i8251_device::control_w));
}

void vt100_state::vt100ac(machine_config &config)
{
	vt100(config);

	i8085a_cpu_device &stpcpu(I8085A(config, "stpcpu", 4915200));
	stpcpu.set_addrmap(AS_PROGRAM, &vt100_state::stp_mem);
	stpcpu.set_addrmap(AS_IO, &vt100_state::stp_io);

	i8251_device &stpusart0(I8251(config, "stpusart0", 2457600));
	stpusart0.rxrdy_handler().set("stprxint", FUNC(input_merger_device::in_w<0>));
	stpusart0.txrdy_handler().set("stptxint", FUNC(input_merger_device::in_w<0>));

	i8251_device &stpusart1(I8251(config, "stpusart1", 2457600));
	stpusart1.rxrdy_handler().set("stprxint", FUNC(input_merger_device::in_w<1>));
	stpusart1.txrdy_handler().set("stptxint", FUNC(input_merger_device::in_w<1>));

	i8251_device &stpusart2(I8251(config, "stpusart2", 2457600)); // for printer?
	stpusart2.rxrdy_handler().set("stprxint", FUNC(input_merger_device::in_w<2>));
	stpusart2.txrdy_handler().set("stptxint", FUNC(input_merger_device::in_w<2>));

	INPUT_MERGER_ANY_HIGH(config, "stptxint").output_handler().set_inputline("stpcpu", I8085_RST55_LINE);

	INPUT_MERGER_ANY_HIGH(config, "stprxint").output_handler().set_inputline("stpcpu", I8085_RST65_LINE);

	com8116_device &dbrg(*subdevice<com8116_device>("dbrg"));
	dbrg.fr_handler().append("stpusart0", FUNC(i8251_device::write_rxc));
	dbrg.fr_handler().append("stpusart1", FUNC(i8251_device::write_rxc));
	dbrg.fr_handler().append("stpusart2", FUNC(i8251_device::write_rxc));
	dbrg.ft_handler().append("stpusart0", FUNC(i8251_device::write_txc));
	dbrg.ft_handler().append("stpusart1", FUNC(i8251_device::write_txc));
	dbrg.ft_handler().append("stpusart2", FUNC(i8251_device::write_txc));
}

void vt100_state::vt180(machine_config &config)
{
	vt100(config);

	z80_device &z80cpu(Z80(config, "z80cpu", XTAL(24'883'200) / 9));
	z80cpu.set_memory_map(&vt100_state::vt180_mem);
	z80cpu.set_io_map(&vt100_state::vt180_io);
}

void vt100_state::vt101(machine_config &config)
{
	vt100(config);

	I8085A(config.replace(), m_maincpu, XTAL(24'073'400) / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &vt100_state::vt100_mem);
	m_maincpu->set_addrmap(AS_IO, &vt100_state::vt100_io);
	m_maincpu->set_irq_acknowledge_callback(FUNC(vt100_state::vt102_irq_callback));

	m_pusart->set_clock(XTAL(24'073'400) / 8);
	m_pusart->txrdy_handler().set_inputline(m_maincpu, I8085_RST55_LINE); // 8085 pin 9, mislabeled RST 7.5 on schematics

	com8116_003_device &dbrg(COM8116_003(config.replace(), "dbrg", XTAL(24'073'400) / 4));
	dbrg.fr_handler().set(m_pusart, FUNC(i8251_device::write_rxc));
	dbrg.ft_handler().set(m_pusart, FUNC(i8251_device::write_txc));

	m_kbduart->write_tbmt_callback().set_inputline(m_maincpu, I8085_RST65_LINE);
}

void vt100_state::vt102(machine_config &config)
{
	vt101(config);

	m_maincpu->set_addrmap(AS_IO, &vt100_state::vt102_io);

	ins8250_device &printuart(INS8250(config, "printuart", XTAL(24'073'400) / 16));
	printuart.out_tx_callback().set("printer", FUNC(rs232_port_device::write_txd));
	printuart.out_int_callback().set_inputline(m_maincpu, I8085_RST75_LINE); // 8085 pin 7, mislabeled RST 5.5 on schematics

	rs232_port_device &printer(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	printer.rxd_handler().set("printuart", FUNC(ins8250_device::rx_w));
	printer.dsr_handler().set("printuart", FUNC(ins8250_device::dsr_w));
}

/* VT1xx models:
 * VT100 - 1978 base model. the 'later' ROM is from 1979 or 1980.
 *    The vt100 had a whole series of -XX models branching off of it; the
       ones I know of are described here, as well as anything special about
       them:
 *    VT100-AA - standard model with 120vac cable \__voltage can be switched
 *    VT100-AB - standard model with 240vac cable /  inside any VT100 unit
 *    VT100-W* - word processing series:
 *     VT100-WA/WB - has special LA120 AVO board preinstalled, WP ROMset?,
        English WP keyboard, no alt charset ROM, LA120? 23-069E2 AVO ROM.
       (The WA and WB variants are called the '-02' variant on the schematics)
 *    VT100-WC through WZ: foreign language word processing series:
      (The WC through WK variants are called the '-03' variant on the schematics)
 *     VT100-WC/WD - has AVO board preinstalled, WP ROMset?, French Canadian
        WP keyboard, has 23-094E2 alt charset ROM, 23-093E2 AVO ROM.
 *     VT100-WE/WF - has AVO board preinstalled, WP ROMset?, French
        WP keyboard, has 23-094E2 alt charset ROM, 23-093E2 AVO ROM.
 *     VT100-WG/WH - has AVO board preinstalled, WP ROMset?, Dutch
        WP keyboard, has 23-094E2 alt charset ROM, 23-093E2 AVO ROM.
 *     VT100-WJ/WK - has AVO board preinstalled, WP ROMset?, German
        WP keyboard, has 23-094E2 alt charset ROM, 23-093E2 AVO ROM.
 *     VT100-WY/WZ - has AVO board preinstalled, WP ROMset?, English
        WP keyboard, has 23-094E2 alt charset ROM, 23-093E2 AVO ROM.
       The WP ROMset supports English, French, Dutch and German languages but
        will only display text properly in the non-English languages if the
        23-094E2 alt charset ROM AND the foreign language 23-093E2
        AVO ROM are populated.
 *    VT100-NA/NB - ? ROMset with DECFORM keycaps
 *    VT100 with vt1xx-ac kit - adds serial printer interface (STP)
       pcb, replaces ROMs with the 095e2/096e2/139e2/140e2 STP set
 * VT101 - 1981 cost reduced unexpandable vt100; Is the same as a stock
   unexpanded vt100. It has no AVO nor the upgrade connector for it, and no
   video input port.) Has its own firmware.
   Shares same pcb with vt102 and vt131, but STP/AVO are unpopulated;
 * VT102 - 1981 cost reduced unexpandable vt100 with built in AVO and STP
   Is the same as a stock vt100 with the AVO and STP expansions installed,
   but all on one pcb. Does NOT support the AVO extended character ROMs, nor
   the word processing ROM set. Has its own firmware.
   Shares same pcb with vt101 and vt131, has STP and AVO populated.
 * VT103 - 1980 base model vt100 with an integrated TU58 tape drive, and an
   LSI-11 backplane, which an LSI-11 CPU card is used in, hence the computer
   is effectively a tiny lsi-11 (pdp-11) built in a vt100 case. uses same ROMs
   as vt100 for the vt100 portion, and tu58 has its own CPU and ROM. It can
   have the normal vt100 ROMset variant, and also can have the multiple word
   processing variations (which use the same ROMs as the vt100 ones do).
 * VT104 doesn't exist.
 * VT105 - 1978 vt100 with the WG waveform generator board installed
   (for simple chart-type line-compare-made raster graphics using some built
   in functions), AVO optional; was intended for use on the MINC analog data
   acquisition computer.
 * VT110 - 1978 vt100 with a DPM01 DECDataway serial multiplexer installed
    The DPM01 supposedly has its own processor and ROMs.
 * vt125 - 1982? base model (stock vt100 firmware plus extra gfx board
   firmware and processor) vt100 with the ReGIS graphical language board
   (aka GPO) installed (almost literally a vk100-on-a-board, but with added
   backwards compatibility mode for vt105/WG, and 2 bits per pixel color),
   AVO optional; Includes a custom 'dumb' STP board.
 * vt131 - 1982 cost reduced version of vt132, no longer has the vt100
   expansion backplane; has the AVO advanced video board built in, as well
   as the parallel port interface board, and supports serial block mode.
   Shares same pcb with vt101 and vt102, has STP and AVO populated.
 * vt132 - 1980? base vt100 with AVO, STP, and its own 23-099e2/23-100e2
   AVO character ROM set. Has its own base firmware ROMs which support block
   serial mode.
 * vt180 - 1980 vt10x (w/vt100 expansion backplane) with a z80 daughterboard
   installed;
   The daughterboard has two ROMs on it: 23-017e3-00 and 23-021e3-00
   (both are 0x1000 long, 2332 mask ROMs)
 * vk100 'gigi'- graphical terminal; the vt125 GPO board is a very close derivative;
   relatively little info so far but progress has been made.
   see vk100.c for current driver for this

 * Upgrade kits for vt1xx:
 * VT1xx-AA : p/n 5413206 20ma current loop interface pcb for vt100
 * VT1xx-AB : p/n 5413097 AVO board (AVO ROMs could be optionally ordered along with
              this board if needed)
 * VT1xx-AC : STP serial printer board (includes a special ROMset)
 * VT1xx-CA : p/n 5413206? 20ma current loop interface pcb for vt101/vt102/vt131
 * VT1xx-CB or CL: GPO "ReGIS" board vt100->vt125 upgrade kit (p/n 5414275 paddle board and 5414277 gpo board)
 * VT1xx-CE : DECWord Conversion kit
 * VT1xx-FB : Anti-glare kit

 * Info about mask ROMs and other nasties:
 * A normal 2716 ROM has pin 18: /CE; pin 20: /OE; pin 21: VPP (acts as CE2)
 * The vt100 23-031e2/23-061e2, 23-032e2, 23-033e2, and 23-034e2 mask ROMs
   have the follwing enables:
       23-031e2/23-061e2: pin 18:  CS2; pin 20:  CS1; pin 21:  CS3
       23-032e2:          pin 18: /CS2; pin 20:  CS1; pin 21:  CS3
       23-033e2:          pin 18:  CS2; pin 20:  CS1; pin 21: /CS3
       23-034e2:          pin 18: /CS2; pin 20:  CS1; pin 21: /CS3
       (This is cute because it technically means the ROMs can be put in the
       4 sockets in ANY ORDER and will still work properly since the cs2 and
       cs3 pins make them self-decode and activate at their proper address)
       (This same cute trick is almost certainly also done with the
       23-180e2, 181e2, 182e2 183e2 ROMset, as well as the
       23-095e2,096e2,139e2,140e2 set and probably others as well)
 * The vt100/101/102/103/etc 23-018e2-00 character set ROM at location e4 is a 24 pin 2316 mask ROM with enables as such: pin 18: CS2; pin 20: /CS1; pin 21: /CS3
 * The optional 23-094e2-00 alternate character set ROM at location e9 is a 24 pin 2316 mask ROM with enables as such: pin 18: /CS2; pin 20: /CS1; pin 21: /CS3
       Supposedly the 23-094e2 ROM is meant for vt100-WC or -WF systems, (which are French Canadian and French respectively), implying that it has European language specific accented characters on it. It is probably used in all the -W* systems.
       Pin 21 can be jumpered to +5v for this socket at location e9 by removing jumper w4 and inserting jumper w5, allowing a normal 2716 EPROM to be used.
 * The optional AVO character set ROMs (see below) have: pin 18: /CS2*; pin 20: /CS1; pin 21: CS3 hence they match a normal 2716
   *(this is marked on the image as if it was CS2 but the input is tied to gnd meaning it must be /CS2)

 * The AVO itself can hold up to four ROMs on it (see http://www.bitsavers.org/pdf/dec/terminal/vt100/MP00633_VT100_Mar80.pdf
   and http://vt100.net/dec/ek-vt1ac-ug-002.pdf )
   and these ROMs can depending on jumpers be mapped at 0x8000, OR overlay the main code ROMs at 0x0000-0x1fff!
   They may even allow banking between the main code ROMs and the overlay ROMs, I haven't traced the schematic.
   At least sixteen of these AVO ROMs were made, and are used as such:
   (based on EK-VT100-TM-003_VT100_Technical_Manual_Jul82.pdf)
 * No ROMs - normal vt100 system with AVO installed
 * 23-069E2 (location e21) - meant for vt100-wa and -wb 'LA120' 'word processing' systems (the mapping of the ROM for this system is different than for the ones below)
 * 23-099E2 (location e21) and 23-100E2 (location e17) - meant for vt132 but only with the OLD vt132 main ROMset of 095,096,097,098E2
 * 23-093E2 (location e21) - meant for vt100 wc through wz 'foreign language' word processing systems
 * 23-184E2 and 23-185E2 - meant for vt100 with STP printer option board installed, version 1, comes with vt1xx-ac kit
 * 23-186E2 and 23-187E2 - meant for vt100 with STP printer option board installed, version 2, comes with vt1xx-ac kit
 * 23-224E2, 23-225E2, 23-226E2, 23-227E2 - meant for vt132 but only with the NEW vt132 main ROMset of 180,181,182,183E2
 * 23-236E2, 23-237E2, 23-238E2, 23-239E2 - meant for vt132 but only with the NEW vt132 main ROMset of 180,181,182,183E2, unknown difference to above (PROM vs. mask ROM? same contents?)
 */

/* ROM definition */
ROM_START( vt100 ) // This is from the schematics at http://www.bitsavers.org/pdf/dec/terminal/vt100/MP00633_VT100_Mar80.pdf
// This is the standard VT100 CPU board with the 'normal' ROMs (but later rev of EPROM 0) populated
// This ROMset is also used for the vt103, vt105, vt110, vt125, and vt180
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS( "vt100" )
	ROM_SYSTEM_BIOS( 0, "vt100o", "VT100 older ROMs" )
	ROMX_LOAD( "23-031e2-00.e56", 0x0000, 0x0800, NO_DUMP, ROM_BIOS(0)) // version 1 1978 'earlier ROM', dump needed, correct for earlier vt100s
	ROM_SYSTEM_BIOS( 1, "vt100", "VT100 newer ROMs" )
	ROMX_LOAD( "23-061e2-00.e56", 0x0000, 0x0800, CRC(3dae97ff) SHA1(e3437850c33565751b86af6c2fe270a491246d15), ROM_BIOS(1)) // version 2 1979 or 1980 'later ROM', correct for later vt100s
	ROM_LOAD( "23-032e2-00.e52", 0x0800, 0x0800, CRC(3d86db99) SHA1(cdd8bdecdc643442f6e7d2c83cf002baf8101867))
	ROM_LOAD( "23-033e2-00.e45", 0x1000, 0x0800, CRC(384dac0a) SHA1(22aaf5ab5f9555a61ec43f91d4dea3029f613e64))
	ROM_LOAD( "23-034e2-00.e40", 0x1800, 0x0800, CRC(4643184d) SHA1(27e6c19d9932bf13fdb70305ef4d806e90d60833))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "23-018e2-00.e4", 0x0000, 0x0800, CRC(6958458b) SHA1(103429674fc01c215bbc2c91962ae99231f8ae53))
	ROM_LOAD_OPTIONAL("23-094e2-00.e9", 0x0800, 0x0800, NO_DUMP) // optional ?word processing? alternate character set ROM
ROM_END

#if 0
ROM_START( vt100wp ) // This is from the schematics at http://www.bitsavers.org/pdf/dec/terminal/vt100/MP00633_VT100_Mar80.pdf
// This is the standard vt100 CPU board, with the ?word processing? ROMset, included in the VT1xx-CE kit?
// the vt103 can also use this ROM set (-04 and -05 revs have it by default, -05 rev also has the optional alt charset ROM by default)
// NOTE: this is actually the same as the newer VT132 ROMset; vt132 has different AVO ROMs as well.
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "23-180e2-00.e56", 0x0000, 0x0800, NO_DUMP)
	ROM_LOAD( "23-181e2-00.e52", 0x0800, 0x0800, NO_DUMP)
	ROM_LOAD( "23-182e2-00.e45", 0x1000, 0x0800, NO_DUMP)
	ROM_LOAD( "23-183e2-00.e40", 0x1800, 0x0800, NO_DUMP)

	ROM_REGION(0x1000, "avo", 0)
	ROM_LOAD( "23-184e2-00.bin", 0x0000, 0x0800, NO_DUMP)
	ROM_LOAD( "23-185e2-00.bin", 0x0800, 0x0800, NO_DUMP)
	ROM_LOAD( "23-186e2-00.bin", 0x1000, 0x0800, NO_DUMP)
	ROM_LOAD( "23-187e2-00.bin", 0x1800, 0x0800, NO_DUMP)

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "23-018e2-00.e4", 0x0000, 0x0800, CRC(6958458b) SHA1(103429674fc01c215bbc2c91962ae99231f8ae53))
	ROM_LOAD( "23-094e2-00.e9", 0x0800, 0x0800, NO_DUMP) // REQUIRED foreign language alternate character set ROM
ROM_END

ROM_START( vt132 ) // This is from anecdotal evidence and vt100.net, as the vt132 schematics are not scanned
// but is pretty much confirmed by page 433 in http://bitsavers.trailing-edge.com/www.computer.museum.uq.edu.au/pdf/EK-VT100-TM-003%20VT100%20Series%20Video%20Terminal%20Technical%20Manual.pdf
// VT100 board with block serial ROMs, AVO with special ROMs, STP, custom firmware with block serial mode
// ROMS have Set-Up page C on them
	// OLDER vt132 ROMset
	ROM_LOAD( "23-095e2-00.e56", 0x0000, 0x0800, NO_DUMP)
	ROM_LOAD( "23-096e2-00.e52", 0x0800, 0x0800, NO_DUMP)
	ROM_LOAD( "23-097e2-00.e45", 0x1000, 0x0800, NO_DUMP)
	ROM_LOAD( "23-098e2-00.e40", 0x1800, 0x0800, NO_DUMP)

	// NEWER vt132 (and STP?) ROMset
	ROM_LOAD( "23-180e2-00.e56", 0x0000, 0x0800, NO_DUMP)
	ROM_LOAD( "23-181e2-00.e52", 0x0800, 0x0800, NO_DUMP)
	ROM_LOAD( "23-182e2-00.e45", 0x1000, 0x0800, NO_DUMP)
	ROM_LOAD( "23-183e2-00.e40", 0x1800, 0x0800, NO_DUMP)

	// AVO ROMs for OLDER ROMset only
	ROM_REGION(0x1000, "avo", 0)
	ROM_LOAD( "23-099e2-00.e21", 0x0000, 0x0800, NO_DUMP)
	ROM_LOAD( "23-100e2-00.e17", 0x0800, 0x0800, NO_DUMP)
	// other 2 sockets are empty

	// AVO ROMs for NEWER ROMset only
	ROM_LOAD( "23-224e2-00.e21", 0x0000, 0x0800, NO_DUMP)
	ROM_LOAD( "23-225e2-00.e17", 0x0800, 0x0800, NO_DUMP)
	ROM_LOAD( "23-226e2-00.e15", 0x1000, 0x0800, NO_DUMP) // loc is a guess
	ROM_LOAD( "23-227e2-00.e13", 0x1800, 0x0800, NO_DUMP) // loc is a guess
	// alt rev of newer avo ROMs, tech manual implies above are PROMs below are mask ROMS? same data?
	ROM_LOAD( "23-236e2-00.e21", 0x0000, 0x0800, NO_DUMP)
	ROM_LOAD( "23-237e2-00.e17", 0x0800, 0x0800, NO_DUMP)
	ROM_LOAD( "23-238e2-00.e15", 0x1000, 0x0800, NO_DUMP) // loc is a guess
	ROM_LOAD( "23-239e2-00.e13", 0x1800, 0x0800, NO_DUMP) // loc is a guess

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "23-018e2-00.e4", 0x0000, 0x0800, CRC(6958458b) SHA1(103429674fc01c215bbc2c91962ae99231f8ae53))
	ROM_LOAD_OPTIONAL( "23-094e2-00.e9", 0x0800, 0x0800, NO_DUMP) // optional (comes default with some models) alternate character set ROM
ROM_END
#endif

ROM_START( vt100ac ) // This is from the VT180 technical manual at http://www.bitsavers.org/pdf/dec/terminal/vt180/EK-VT18X-TM-001_VT180_Technical_Man_Feb83.pdf
// This is the standard vt100 CPU board, but with the ROM set included with the VT1xx-AC kit
// which is only used when the part 54-14260-00 STP 'printer port expansion' card is installed into the terminal board.
// Or as http://bitsavers.trailing-edge.com/www.computer.museum.uq.edu.au/pdf/EK-VT100-TM-003%20VT100%20Series%20Video%20Terminal%20Technical%20Manual.pdf
// on page 433: VT100 WC or WK uses these as well.
// This ROMset adds the Set-up C page to the setup menu (press keypad 5 twice once you hit set-up)
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "23-095e2.e40", 0x0000, 0x0800, CRC(6c8acf44) SHA1(b3ef5af920995a40a316c6dc008960c461853bfc)) // Label: "23095E2 // (C)DEC // (M)QQ8227" @E40
	ROM_LOAD( "23-096e2.e45", 0x0800, 0x0800, CRC(77f21473) SHA1(6f10b250777c12cca63ee611735f9f36cc05a7ef)) // Label: "23096E2 // (C)DEC // (M)QQ8227" @E45
	ROM_LOAD( "23-139e2.e52", 0x1000, 0x0800, CRC(c3302ce5) SHA1(a2ab3b9b48b5e850b2d7b22e6f8ef6099599e4b6)) // Label: "AMD // 37108 8232DKP // 23-139E2 // AM9218CPC // (C)DEC_1979" @E52;  // revision 2?; revision 1 is 23-097e2 MAYBE
	ROM_LOAD( "23-140e2.e56", 0x1800, 0x0800, CRC(4bf1ce4e) SHA1(279f47ec9a68c801c3c05005dd782202ac9e51a4)) // Label: "AMD // 37109 8230DHP // 23-140E2 // AM9218CPC // (C)DEC 1979" @E56  // revision 2?; revision 1 is 23-098e2 MAYBE

	ROM_REGION(0x1000, "avo", 0) // all switches on "54-13097-00 // PN2280402L" AVO are open EXCEPT S2-3; does this map at 0xa000-0xcfff (mirrored) in maincpu space?
	// This same set of ROMs also appears on the "PN1030385J-F" AVO, which has no dipswitches; instead a single jumper? resistor installed in the NDIP20 footprint between E16 and E22, between pins 6 and 15 of the footprint
	//NOTE: for both of these two avo ROMs, Pin 18 is positive enable CE, Pin 20 is negative enable /CE1, Pin 21 is negative enable /CE2,
	ROM_LOAD( "23-186e2.avo.e21", 0x0000, 0x0800, CRC(1592dec1) SHA1(c4b8fc9fc0514e0cd46ad2de03abe72271ce460b)) // Label: "S 8218 // C69063 // 23186E2" @E21
	ROM_LOAD( "23-187e2.avo.e17", 0x0800, 0x0800, CRC(c6d72a41) SHA1(956f9eb945a250fd05c76100b38c0ba381ab8fde)) // Label: "S 8228 // C69062 // 23187E2" @E17
	// are 184 and 185 an older version of the VT100-AC AVO firmware?

	ROM_REGION(0x2000, "stp", 0) // stp switches 1 and 5 are closed, 2,3,4 open
	ROM_LOAD( "23-029e4.stp.e14", 0x0000, 0x2000, CRC(da55c62b) SHA1(261b02b774d57253d1dedecab8ca0e368c2a96cd)) // Label: "S 8218 // C43020 // 23029E4 (C) DEC // TP02" @E14
	// the ROM dump above MIGHT be in the wrong order: it was dumped with A11 to pin 18, A12 to pin 21, A13 to pin 20, but I'm not sure those assignments to pins are correct.
	// At worst the 8 parts of it are in the wrong order.

	ROM_REGION(0x1000, "chargen",0)
	ROM_LOAD( "23-018e2-00.e4", 0x0000, 0x0800, CRC(6958458b) SHA1(103429674fc01c215bbc2c91962ae99231f8ae53))
	ROM_LOAD_OPTIONAL( "23-094e2-00.e9", 0x0800, 0x0800, NO_DUMP) // optional ?word processing? alternate character set ROM
	ROM_REGION(0x10000, "stpcpu",ROMREGION_ERASEFF)
/* The intelligent STP board: Below is limited information for an ?older version? or prototype of the board
// expansion board for a vt100 with a processor on it and dma, intended to act as a RAM/send buffer for the STP printer board.
// It can be populated with two banks of two EPROMs each, each bank either contains 2k or 4k EPROMs depending on the w2/w3 and w4/w5 jumpers.
// It also has two PROMs on the CPU board. I don't know if it is technically necessary to have this board installed if an STP module is installed, but due to the alt stp ROMset, it probably is.
    ROM_LOAD( "23-003e3-00.e10", 0x0000, 0x1000, NO_DUMP) // "EPROM 0" bank 0
    ROM_LOAD( "23-004e3-00.e4", 0x1000, 0x1000, NO_DUMP) // "EPROM 1" bank 0
    ROM_LOAD( "23-005e3-00.e9", 0x2000, 0x1000, NO_DUMP) // "EPROM 2" bank 1
    ROM_LOAD( "23-006e3-00.e3", 0x3000, 0x1000, NO_DUMP) // "EPROM 3" bank 1
    //ROM_REGION(0x0800, "avo",0)
    //ROM_LOAD( "23-???e2-00.e34", 0x0000, 0x0800, NO_DUMP) // ? second gfx ROM?
    ROM_REGION(0x0400, "proms",0)
    ROM_LOAD( "23-312a1-07.e26", 0x0000, 0x0200, NO_DUMP) // "PROM A"; handles 8085 I/O mapping? (USART, timer, DMA, comm, etc)
    ROM_LOAD( "23-313a1-07.e15", 0x0200, 0x0200, NO_DUMP) // "PROM B"; handles firmware ROM mapping and memory size/page select; bit 0 = RAM page, bits 1-3 unused, bits 4-7 select one EPROM each
    */
ROM_END

#if 0
ROM_START( vt103 ) // This is from the schematics at http://www.bitsavers.org/pdf/dec/terminal/vt103/MP00731_VT103_Aug80.pdf
// This is the standard VT100 CPU board with the 'normal' ROMs (but later rev of EPROM 0) populated but with an
// LSI-11 backplane (instead of a normal VT100 one, hence it cannot use the AVO, WG, GPO, or VT180 Z80 boards) and
// DEC TU58 dual 256k tape drive integrated; It was intended that you would put an LSI-11 CPU card in there, which
// Would talk to the terminal as its input/output device. Several LSI-11 CPU cards were available?
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "23-061e2-00.e56", 0x0000, 0x0800, CRC(3dae97ff) SHA1(e3437850c33565751b86af6c2fe270a491246d15)) // version 2 1980 'later ROM'
	ROM_LOAD( "23-032e2-00.e52", 0x0800, 0x0800, CRC(3d86db99) SHA1(cdd8bdecdc643442f6e7d2c83cf002baf8101867))
	ROM_LOAD( "23-033e2-00.e45", 0x1000, 0x0800, CRC(384dac0a) SHA1(22aaf5ab5f9555a61ec43f91d4dea3029f613e64))
	ROM_LOAD( "23-034e2-00.e40", 0x1800, 0x0800, CRC(4643184d) SHA1(27e6c19d9932bf13fdb70305ef4d806e90d60833))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "23-018e2-00.e4", 0x0000, 0x0800, CRC(6958458b) SHA1(103429674fc01c215bbc2c91962ae99231f8ae53))
	ROM_LOAD_OPTIONAL( "23-094e2-00.e9", 0x0800, 0x0800, NO_DUMP) // optional (comes default with some models) alternate character set ROM

	ROM_REGION(0x0800, "tapecpu", 0) // ROM for the 8085 CPU in the integrated serial tu58-xa drive
	ROM_LOAD( "23-089e2.e1", 0x0000, 0x0800, CRC(8614dd4c) SHA1(1b554e6c98bddfc6bc48d81c990deea43cf9df7f)) // Label: "23-089E2 // P8316E - AMD // 35227 8008NPP"

	ROM_REGION(0x80000, "lsi11cpu", 0) // ROM for the LSI-11 CPU board
	ROM_LOAD_OPTIONAL( "unknown.bin", 0x00000, 0x80000, NO_DUMP)
ROM_END
#endif

ROM_START( vt105 ) // This is from anecdotal evidence and vt100.net, as the vt105 schematics are not scanned
// This is the standard VT100 CPU board with the 'normal' ROMs (but later rev of EPROM 0) populated but with a
// WG waveform generator board factory installed; this makes the terminal act like a vt55 with vt100 terminal capability
// The VT105 was intended for use on the MINC analog data acquisition computer
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "23-061e2-00.e56", 0x0000, 0x0800, CRC(3dae97ff) SHA1(e3437850c33565751b86af6c2fe270a491246d15)) // version 2 1980 'later ROM'
	ROM_LOAD( "23-032e2-00.e52", 0x0800, 0x0800, CRC(3d86db99) SHA1(cdd8bdecdc643442f6e7d2c83cf002baf8101867))
	ROM_LOAD( "23-033e2-00.e45", 0x1000, 0x0800, CRC(384dac0a) SHA1(22aaf5ab5f9555a61ec43f91d4dea3029f613e64))
	ROM_LOAD( "23-034e2-00.e40", 0x1800, 0x0800, CRC(4643184d) SHA1(27e6c19d9932bf13fdb70305ef4d806e90d60833))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "23-018e2-00.e4", 0x0000, 0x0800, CRC(6958458b) SHA1(103429674fc01c215bbc2c91962ae99231f8ae53))
	ROM_LOAD_OPTIONAL( "23-094e2-00.e9", 0x0800, 0x0800, NO_DUMP) // optional (comes default with some models) alternate character set ROM
ROM_END

#if 0
ROM_START( vt110 )
// This is the standard VT100 CPU board with the 'normal' ROMs (but later rev of EPROM 0) populated but with a
// DECDataway DPM01 board, which adds 4 or 5 special network-addressable 50ohm? current loop serial lines
// and may add its own processor and RAM to control them. see http://bitsavers.org/pdf/dec/terminal/EK-VT110_UG-001_Dec78.pdf
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "23-061e2-00.e56", 0x0000, 0x0800, CRC(3dae97ff) SHA1(e3437850c33565751b86af6c2fe270a491246d15)) // version 2 1980 'later ROM'
	ROM_LOAD( "23-032e2-00.e52", 0x0800, 0x0800, CRC(3d86db99) SHA1(cdd8bdecdc643442f6e7d2c83cf002baf8101867))
	ROM_LOAD( "23-033e2-00.e45", 0x1000, 0x0800, CRC(384dac0a) SHA1(22aaf5ab5f9555a61ec43f91d4dea3029f613e64))
	ROM_LOAD( "23-034e2-00.e40", 0x1800, 0x0800, CRC(4643184d) SHA1(27e6c19d9932bf13fdb70305ef4d806e90d60833))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "23-018e2-00.e4", 0x0000, 0x0800, CRC(6958458b) SHA1(103429674fc01c215bbc2c91962ae99231f8ae53))
	ROM_LOAD_OPTIONAL ( "23-094e2-00.e9", 0x0800, 0x0800, NO_DUMP) // optional (comes default with some models) alternate character set ROM
//DECDataway board ROMs go here!
ROM_END

ROM_START( vt125 ) // This is from bitsavers and vt100.net, as the vt125 schematics are not scanned
// This is the standard VT100 CPU board with the 'normal' ROMs (but later rev of EPROM 0) populated but with a
// special "GPO" ReGIS CPU+RAM card 54-14277 installed which provides a framebuffer, text rotation, custom RAM fonts, and many other features.
// Comes with a custom 'dumb' STP card 54-14275 as well.
// VT125 upgrade kit (upgrade from vt100 or vt105) was called VT1xx-CB or CL
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "23-061e2-00.e56", 0x0000, 0x0800, CRC(3dae97ff) SHA1(e3437850c33565751b86af6c2fe270a491246d15)) // version 2 1980 'later ROM'
	ROM_LOAD( "23-032e2-00.e52", 0x0800, 0x0800, CRC(3d86db99) SHA1(cdd8bdecdc643442f6e7d2c83cf002baf8101867))
	ROM_LOAD( "23-033e2-00.e45", 0x1000, 0x0800, CRC(384dac0a) SHA1(22aaf5ab5f9555a61ec43f91d4dea3029f613e64))
	ROM_LOAD( "23-034e2-00.e40", 0x1800, 0x0800, CRC(4643184d) SHA1(27e6c19d9932bf13fdb70305ef4d806e90d60833))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "23-018e2-00.e4", 0x0000, 0x0800, CRC(6958458b) SHA1(103429674fc01c215bbc2c91962ae99231f8ae53))
	ROM_LOAD_OPTIONAL ( "23-094e2-00.e9", 0x0800, 0x0800, NO_DUMP) // optional (comes default with some models) alternate character set ROM

	// "GPO" aka vt125 "mono board" ROMs and PROMs
	ROM_REGION(0x10000, "monocpu", ROMREGION_ERASEFF) // ROMs for the 8085 sub-CPU
	ROM_LOAD( "23-043e4-00.e22", 0x0000, 0x2000, NO_DUMP) // 2364/MK36xxx mask ROM
	ROM_LOAD( "23-044e4-00.e23", 0x2000, 0x2000, NO_DUMP) // 2364/MK36xxx mask ROM
	ROM_LOAD( "23-045e4-00.e24", 0x4000, 0x2000, NO_DUMP) // 2364/MK36xxx mask ROM
	// E25 socket is empty

	ROM_REGION(0x100, "dir", ROMREGION_ERASEFF ) // vt125 direction PROM, same as on vk100, 82s135 equiv
	ROM_LOAD( "23-059b1.e41", 0x0000, 0x0100, CRC(4b63857a) SHA1(3217247d983521f0b0499b5c4ef6b5de9844c465))

	ROM_REGION(0x100, "trans", ROMREGION_ERASEFF ) // vt125 x translate PROM, same as on vk100, 82s135 equiv
	ROM_LOAD( "23-060b1.e60", 0x0000, 0x0100, CRC(198317fc) SHA1(00e97104952b3fbe03a4f18d800d608b837d10ae))

	ROM_REGION(0x500, "proms", ROMREGION_ERASEFF) // vt125 mono board PROMs
	ROM_LOAD( "23-067b1.e135", 0x0000, 0x0100, NO_DUMP) // 82s135, waitstate PROM
	ROM_LOAD( "23-068b1.e64", 0x0100, 0x0100, NO_DUMP) // 82s135, sync_a PROM
	ROM_LOAD( "23-069b1.e66", 0x0200, 0x0100, NO_DUMP) // 82s135, sync_b PROM
	ROM_LOAD( "23-070b1.e71", 0x0300, 0x0100, NO_DUMP) // 82s135, vector prom
	ROM_LOAD( "23-582a2.e93", 0x0400, 0x0100, NO_DUMP) // 82s129, RAS/erase PROM
ROM_END
#endif

ROM_START( vt101 ) // p/n 5414185-01 'unupgradable/low cost' vt101/vt102/vt131 mainboard
// does not have integrated STP or AVO populated
// 8085 based instead of I8080
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "23-028e4-00.e71", 0x0000, 0x2000, CRC(fccce02c) SHA1(f3e3e93a857443685b816cab4fb52e34c0bc72b1)) // ROM is unique to vt101; "CN55004N 8232 // DEC TP03 // 23-028E4-00" 24-pin mask ROM (mc68764 pinout)
	// E69 socket is empty/unpopulated on vt101
	// E67 socket is empty/unpopulated on vt101
	// DIP40 at E74 in the lower right corner of MB in vt101 (WD8250 UART) is absent

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "23-018e2-00.e3", 0x0000, 0x0800, CRC(6958458b) SHA1(103429674fc01c215bbc2c91962ae99231f8ae53))
	ROM_LOAD_OPTIONAL( "23-094e2-00.e4", 0x0800, 0x0800, NO_DUMP) // optional (comes default with some models) alternate character set ROM
ROM_END


ROM_START( vt102 ) // p/n 5414185-01 'unupgradable/low cost' vt101/vt102/vt131 mainboard
// has integrated STP and AVO both populated
// ROMS have the set up page C in them
// 8085 based instead of I8080
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS( "vt102" )
	ROM_SYSTEM_BIOS( 0, "vt102o", "VT102 older ROMs" )
	ROMX_LOAD( "23-042e4-00.e71", 0x0000, 0x2000, CRC(e8aa006c) SHA1(8ac2a84a8d2a9fa0c6cd583ae35e4c21f863b45b), ROM_BIOS(0)) // shared with vt131
	ROMX_LOAD( "23-041e4-00.e69", 0x8000, 0x2000, CRC(b11d331e) SHA1(8b0f885c7e032d1d709e3913d279d6950bbd4b6a), ROM_BIOS(0)) // shared with vt131
	ROM_SYSTEM_BIOS( 1, "vt102", "VT102 newer ROMs" )
	ROMX_LOAD( "23-226e4-00.e71", 0x0000, 0x2000, CRC(85c9279a) SHA1(3283d27e9c45d9e384227a7e6e98ee8d54b92bcb), ROM_BIOS(1)) // shared with vt131
	ROMX_LOAD( "23-225e4-00.e69", 0x8000, 0x2000, CRC(3567c760) SHA1(672473162e9c92cd237e4dbf92c2700a31c5374b), ROM_BIOS(1)) // shared with vt131
	//e67 socket is empty on vt102 but populated on vt131 below

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "23-018e2-00.e3", 0x0000, 0x0800, CRC(6958458b) SHA1(103429674fc01c215bbc2c91962ae99231f8ae53))
	ROM_LOAD_OPTIONAL( "23-094e2-00.e4", 0x0800, 0x0800, NO_DUMP) // optional (comes default with some models) alternate character set ROM
ROM_END

ROM_START( vt131 ) // p/n 5414185-01 'unupgradable/low cost' vt101/vt131 mainboard with vt132-style block serial mode
// has integrated STP and AVO both populated
// ROMS have the set up page C in them
// 8085 based instead of I8080
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS( "vt131" )
	ROM_SYSTEM_BIOS( 0, "vt131o", "VT131 older ROMs" )
	ROMX_LOAD( "23-042e4-00.e71", 0x0000, 0x2000, CRC(e8aa006c) SHA1(8ac2a84a8d2a9fa0c6cd583ae35e4c21f863b45b), ROM_BIOS(0)) // shared with vt102
	ROMX_LOAD( "23-041e4-00.e69", 0x8000, 0x2000, CRC(b11d331e) SHA1(8b0f885c7e032d1d709e3913d279d6950bbd4b6a), ROM_BIOS(0)) // shared with vt102
	ROM_SYSTEM_BIOS( 1, "vt131", "VT131 newer ROMs" )
	ROMX_LOAD( "23-226e4-00.e71", 0x0000, 0x2000, CRC(85c9279a) SHA1(3283d27e9c45d9e384227a7e6e98ee8d54b92bcb), ROM_BIOS(1)) // shared with vt102
	ROMX_LOAD( "23-225e4-00.e69", 0x8000, 0x2000, CRC(3567c760) SHA1(672473162e9c92cd237e4dbf92c2700a31c5374b), ROM_BIOS(1)) // shared with vt102
	ROM_LOAD( "23-280e2-00.e67", 0xA000, 0x0800, CRC(71b4172e) SHA1(5a82c7dc313bb92b9829eb8350840e072825a797)) // called "VT131 ROM" in the vt101 quick reference guide; pins 20, 18 and 21 are /CE /CE2 and /CE3 on this mask ROM

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "23-018e2-00.e3", 0x0000, 0x0800, CRC(6958458b) SHA1(103429674fc01c215bbc2c91962ae99231f8ae53))
	ROM_LOAD_OPTIONAL( "23-094e2-00.e4", 0x0800, 0x0800, NO_DUMP) // optional (comes default with some models) alternate character set ROM
ROM_END

ROM_START( vt180 )
// This is the standard VT100 CPU board with the 'normal' ROMs (but later rev of EPROM 0) populated but with a
// Z80 daughterboard added to the expansion slot, and replacing the STP adapter (STP ROMs are replaced with the normal set)
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "23-061e2-00.e56", 0x0000, 0x0800, CRC(3dae97ff) SHA1(e3437850c33565751b86af6c2fe270a491246d15)) // version 2 1980 'later ROM'
	ROM_LOAD( "23-032e2-00.e52", 0x0800, 0x0800, CRC(3d86db99) SHA1(cdd8bdecdc643442f6e7d2c83cf002baf8101867))
	ROM_LOAD( "23-033e2-00.e45", 0x1000, 0x0800, CRC(384dac0a) SHA1(22aaf5ab5f9555a61ec43f91d4dea3029f613e64))
	ROM_LOAD( "23-034e2-00.e40", 0x1800, 0x0800, CRC(4643184d) SHA1(27e6c19d9932bf13fdb70305ef4d806e90d60833))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD( "23-018e2-00.e4", 0x0000, 0x0800, CRC(6958458b) SHA1(103429674fc01c215bbc2c91962ae99231f8ae53))
	ROM_LOAD_OPTIONAL ( "23-094e2-00.e9", 0x0800, 0x0800, NO_DUMP) // optional (comes default with some models) alternate character set ROM

	ROM_REGION(0x10000, "z80cpu", 0) // z80 daughterboard
	ROM_LOAD( "23-021e3-00.bin", 0x0000, 0x1000, CRC(a2a575d2) SHA1(47a2c40aaec89e8476240f25515d75ab157f2911))
	ROM_LOAD( "23-017e3-00.bin", 0x1000, 0x1000, CRC(4bdd2398) SHA1(84f288def6c143a2d2ed9dedf947c862c66bb18e))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY                          FULLNAME    FLAGS */
COMP( 1978, vt100,   0,      0,      vt100,   vt100, vt100_state, empty_init, "Digital Equipment Corporation", "VT100",    MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS)
//COMP( 1978, vt100wp, vt100,  0,      vt100,   vt100, vt100_state, empty_init, "Digital Equipment Corporation", "VT100-Wx", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS)
COMP( 1979, vt100ac, vt100,  0,      vt100ac, vt100, vt100_state, empty_init, "Digital Equipment Corporation", "VT100 w/VT1xx-AC STP", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS)
COMP( 1981, vt101,   vt102,  0,      vt101,   vt100, vt100_state, empty_init, "Digital Equipment Corporation", "VT101",    MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS)
COMP( 1981, vt102,   0,      0,      vt102,   vt100, vt100_state, empty_init, "Digital Equipment Corporation", "VT102",    MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS)
//COMP( 1979, vt103,   vt100,  0,      vt100,   vt100, vt100_state, empty_init, "Digital Equipment Corporation", "VT103",    MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS)
COMP( 1978, vt105,   vt100,  0,      vt100,   vt100, vt100_state, empty_init, "Digital Equipment Corporation", "VT105",    MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS)
//COMP( 1978, vt110,   vt100,  0,      vt100,   vt100, vt100_state, empty_init, "Digital Equipment Corporation", "VT110",    MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS)
//COMP( 1981, vt125,   vt100,  0,      vt100,   vt100, vt100_state, empty_init, "Digital Equipment Corporation", "VT125",    MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS)
COMP( 1981, vt131,   vt102,  0,      vt102,   vt100, vt100_state, empty_init, "Digital Equipment Corporation", "VT131",    MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS)
//COMP( 1979, vt132,   vt100,  0,      vt100,   vt100, vt100_state, empty_init, "Digital Equipment Corporation", "VT132",    MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS)
COMP( 1983, vt180,   vt100,  0,      vt180,   vt100, vt100_state, empty_init, "Digital Equipment Corporation", "VT180",    MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS)



vt1682.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood

/*  VT1682 - NOT compatible with NES, different video system, sound CPU (4x
             main CPU clock), optional internal ROM etc.  The design is somewhat
             based on the NES but the video / sound system is significantly
             changed

    Internal ROM can be mapped to Main CPU, or Sound CPU at 0x3000-0x3fff if used
    can also be configured as boot device
*/

/*
    UNIMPLEMENTED / TODO

    General VT1862:

    Sound Quality (currently crackles)
    Verify timer enable / disable behavior
    Line Modes, High Colour Line Mode
    0x8000 bit in palette is 'cut through' mode, which isn't the same as transpen, some kind of palette manipulation
    CCIR effects (only apply to 'palette 2'?)
    LCD Control registers
    Internal to External DMA (glitchy)
    Sprite limits
    Other hardware limits (video DMA should be delayed until Vblank, some registers only take effect at Hblank)
    Verify raster timing (might be off by a line)
    Hardware glitches (scroll layers + sprites get offset under specific conditions, sprites sometimes missing in 2 rightmost column, bk sometimes missing in rightmost column during scroll)
    Sleep functionality on sound cpu (broken on hardware?)
    Improve Interrupt controller
    Proper IO support (enables / disables) UART, I2C etc.
    'Capture' mode
    Gain (zoom) for Tilemaps
    Refactor into a device
    Verify that internal ROMs where possible (not always used)

    + more

    -----------

    Intec InterAct:

    Is there meant to be a 2nd player? (many games prompt a 2nd player to start, but inputs don't appear to be read?)

    -----------

    Excite Sports 48-in-1:

    Why are the rasters broken on MX Motorstorm when the game game works in other collections? does the alt input reading throw the timing off enough that the current hookup fails
    or is there a different PAL/NTSC detection method that we're failing?

    Why is the priority incorrect in Ping Pong, again it was fine in the other collections

    No sound in Archery?

*/

#include "emu.h"
#include "m6502_swap_op_d2_d7.h"
#include "m6502_swap_op_d5_d6.h"
#include "vt1682_io.h"
#include "vt1682_uio.h"
#include "vt1682_alu.h"
#include "vt1682_timer.h"
#include "machine/bankdev.h"
#include "machine/i2cmem.h"
#include "machine/timer.h"
#include "sound/dac.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#define LOG_VRAM_WRITES      (1U << 1)
#define LOG_SRAM_WRITES      (1U << 2)
#define LOG_OTHER            (1U << 3)
#define LOG_DMA              (1U << 4)

#define LOG_ALL           (LOG_DMA | LOG_VRAM_WRITES | LOG_SRAM_WRITES | LOG_OTHER)

#define VERBOSE             (LOG_OTHER)
#include "logmacro.h"


namespace {

// NTSC uses XTAL(21'477'272) Sound CPU runs at exactly this, Main CPU runs at this / 4
// PAL  uses XTAL(26'601'712) Sound CPU runs at exactly this, Main CPU runs at this / 5

// can also be used with the following
// PAL M 21.453669MHz
// PAL N 21.492336MHz

#define MAIN_CPU_CLOCK_NTSC XTAL(21'477'272)/4
#define SOUND_CPU_CLOCK_NTSC XTAL(21'477'272)
#define TIMER_ALT_SPEED_NTSC (15746)

#define MAIN_CPU_CLOCK_PAL XTAL(26'601'712)/5
#define SOUND_CPU_CLOCK_PAL XTAL(26'601'712)
#define TIMER_ALT_SPEED_PAL (15602)



class vt_vt1682_state : public driver_device
{
public:
	vt_vt1682_state(const machine_config& mconfig, device_type type, const char* tag) :
		driver_device(mconfig, type, tag),
		m_io(*this, "io"),
		m_uio(*this, "uio"),
		m_leftdac(*this, "leftdac"),
		m_rightdac(*this, "rightdac"),
		m_maincpu(*this, "maincpu"),
		m_fullrom(*this, "fullrom"),
		m_bank(*this, "cartbank"),
		m_screen(*this, "screen"),
		m_soundcpu(*this, "soundcpu"),
		m_seeprom(*this, "seeprom"),
		m_soundcpu_timer_a_dev(*this, "snd_timera_dev"),
		m_soundcpu_timer_b_dev(*this, "snd_timerb_dev"),
		m_system_timer_dev(*this, "sys_timer_dev"),
		m_maincpu_alu(*this, "mainalu"),
		m_soundcpu_alu(*this, "soundalu"),
		m_spriteram(*this, "spriteram"),
		m_vram(*this, "vram"),
		m_mainram(*this, "mainram"),
		m_sound_share(*this, "sound_share"),
		m_gfxdecode(*this, "gfxdecode2"),
		m_palette(*this, "palette"),
		m_render_timer(*this, "render_timer")
	{ }

	[[maybe_unused]] void vt_vt1682(machine_config& config);
	void regular_init();
	void ignore_bk_paldepth_init();

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

	required_device<vrt_vt1682_io_device> m_io;
	required_device<vrt_vt1682_uio_device> m_uio;
	required_device<dac_12bit_r2r_device> m_leftdac;
	required_device<dac_12bit_r2r_device> m_rightdac;
	required_device<cpu_device> m_maincpu;

	void vt_vt1682_map(address_map &map) ATTR_COLD;
	void vt_vt1682_sound_map(address_map &map) ATTR_COLD;
	void rom_map(address_map &map) ATTR_COLD;

	required_device<address_map_bank_device> m_fullrom;
	required_memory_bank m_bank;
	required_device<screen_device> m_screen;
	required_device<cpu_device> m_soundcpu;
	optional_device<i2cmem_device> m_seeprom;

	void soundcpu_timera_irq(int state);
	void soundcpu_timerb_irq(int state);

	void maincpu_timer_irq(int state);

	required_device<vrt_vt1682_timer_device> m_soundcpu_timer_a_dev;
	required_device<vrt_vt1682_timer_device> m_soundcpu_timer_b_dev;
	required_device<vrt_vt1682_timer_device> m_system_timer_dev;

	void vt_vt1682_ntscbase(machine_config& config);
	void vt_vt1682_palbase(machine_config& config);
	void vt_vt1682_common(machine_config& config);

	virtual void clear_sound_reset_line();

	// hacks
	bool m_allow_bk_paldepth_mode = true;

private:
	required_device<vrt_vt1682_alu_device> m_maincpu_alu;
	required_device<vrt_vt1682_alu_device> m_soundcpu_alu;


	required_device<address_map_bank_device> m_spriteram;
	required_device<address_map_bank_device> m_vram;
	required_shared_ptr<uint8_t> m_mainram;
	required_shared_ptr<uint8_t> m_sound_share;
	required_device<gfxdecode_device> m_gfxdecode;
	required_device<palette_device> m_palette;
	required_device<timer_device> m_render_timer;

	uint32_t screen_update(screen_device& screen, bitmap_rgb32& bitmap, const rectangle& cliprect);


	void spriteram_map(address_map &map) ATTR_COLD;
	void vram_map(address_map &map) ATTR_COLD;


	/* Video */
	uint8_t m_2000;
	uint8_t m_2001;

	uint8_t m_2002_sprramaddr_2_0; // address attribute
	uint8_t m_2003_sprramaddr_10_3; // address sprite number
	uint8_t m_2005_vramaddr_7_0;
	uint8_t m_2006_vramaddr_15_8;

	uint8_t m_201a_sp_segment_7_0;
	uint8_t m_201b_sp_segment_11_8;

	uint8_t m_segment_7_0_bk[2];
	uint8_t m_segment_11_8_bk[2];

	uint8_t m_main_control_bk[2];

	uint8_t m_scroll_control_bk[2];

	uint8_t m_xscroll_7_0_bk[2];
	uint8_t m_yscroll_7_0_bk[2];

	uint8_t m_200e_blend_pal_sel;
	uint8_t m_200f_bk_pal_sel;

	uint8_t m_2008_lcd_vs_delay;
	uint8_t m_2009_lcd_hs_delay_7_0;
	uint8_t m_200a_lcd_fr_delay_7_0;

	uint8_t m_200d_misc_vregs2;
	uint8_t m_200c_misc_vregs1;
	uint8_t m_200b_misc_vregs0;

	uint8_t m_2018_spregs;
	uint8_t m_2019_bkgain;

	uint8_t m_2020_bk_linescroll;
	uint8_t m_2021_lum_offset;
	uint8_t m_2022_saturation_misc;

	uint8_t m_2023_lightgun_reset;
	uint8_t m_2024_lightgun1_y;
	uint8_t m_2025_lightgun1_x;
	uint8_t m_2026_lightgun2_y;
	uint8_t m_2027_lightgun2_x;

	uint8_t m_2031_red_dac;
	uint8_t m_2032_green_dac;
	uint8_t m_2033_blue_dac;

	uint8_t m_2028;
	uint8_t m_2029;
	uint8_t m_202a;
	uint8_t m_202b;
	uint8_t m_202e;
	uint8_t m_2030;

	uint8_t m_scu_to_main_irq_active;
	uint8_t m_timera_to_main_irq_active;
	uint16_t m_current_main_vector;
	uint16_t m_new_main_vector;

	uint8_t m_timera_to_sound_irq_active;
	uint8_t m_timerb_to_sound_irq_active;
	uint16_t m_current_sound_vector;
	uint16_t m_new_sound_vector;

	void update_main_interrupts();
	void update_sound_interrupts();

	uint8_t vt1682_2000_r();
	void vt1682_2000_w(uint8_t data);

	uint8_t vt1682_2001_vblank_r();
	void vt1682_2001_w(uint8_t data);

	uint8_t vt1682_2002_sprramaddr_2_0_r();
	void vt1682_2002_sprramaddr_2_0_w(uint8_t data);
	uint8_t vt1682_2003_sprramaddr_10_3_r();
	void vt1682_2003_sprramaddr_10_3_w(uint8_t data);
	uint8_t vt1682_2004_sprram_data_r();
	void vt1682_2004_sprram_data_w(uint8_t data);

	uint8_t vt1682_2005_vramaddr_7_0_r();
	void vt1682_2005_vramaddr_7_0_w(uint8_t data);
	uint8_t vt1682_2006_vramaddr_15_8_r();
	void vt1682_2006_vramaddr_15_8_w(uint8_t data);
	uint8_t vt1682_2007_vram_data_r();
	void vt1682_2007_vram_data_w(uint8_t data);

	uint8_t vt1682_201a_sp_segment_7_0_r();
	void vt1682_201a_sp_segment_7_0_w(uint8_t data);
	uint8_t vt1682_201b_sp_segment_11_8_r();
	void vt1682_201b_sp_segment_11_8_w(uint8_t data);

	uint8_t vt1682_201c_bk1_segment_7_0_r();
	void vt1682_201c_bk1_segment_7_0_w(uint8_t data);
	uint8_t vt1682_201d_bk1_segment_11_8_r();
	void vt1682_201d_bk1_segment_11_8_w(uint8_t data);
	uint8_t vt1682_201e_bk2_segment_7_0_r();
	void vt1682_201e_bk2_segment_7_0_w(uint8_t data);
	uint8_t vt1682_201f_bk2_segment_11_8_r();
	void vt1682_201f_bk2_segment_11_8_w(uint8_t data);

	uint8_t vt1682_2013_bk1_main_control_r();
	void vt1682_2013_bk1_main_control_w(uint8_t data);
	uint8_t vt1682_2017_bk2_main_control_r();
	void vt1682_2017_bk2_main_control_w(uint8_t data);

	uint8_t vt1682_2012_bk1_scroll_control_r();
	void vt1682_2012_bk1_scroll_control_w(uint8_t data);
	uint8_t vt1682_2016_bk2_scroll_control_r();
	void vt1682_2016_bk2_scroll_control_w(uint8_t data);

	uint8_t vt1682_2010_bk1_xscroll_7_0_r();
	void vt1682_2010_bk1_xscroll_7_0_w(uint8_t data);
	uint8_t vt1682_2011_bk1_yscoll_7_0_r();
	void vt1682_2011_bk1_yscoll_7_0_w(uint8_t data);
	uint8_t vt1682_2014_bk2_xscroll_7_0_r();
	void vt1682_2014_bk2_xscroll_7_0_w(uint8_t data);
	uint8_t vt1682_2015_bk2_yscoll_7_0_r();
	void vt1682_2015_bk2_yscoll_7_0_w(uint8_t data);

	uint8_t vt1682_200e_blend_pal_sel_r();
	void vt1682_200e_blend_pal_sel_w(uint8_t data);
	uint8_t vt1682_200f_bk_pal_sel_r();
	void vt1682_200f_bk_pal_sel_w(uint8_t data);

	uint8_t vt1682_2008_lcd_vs_delay_r();
	void vt1682_2008_lcd_vs_delay_w(uint8_t data);
	uint8_t vt1682_2009_lcd_hs_delay_7_0_r();
	void vt1682_2009_lcd_hs_delay_7_0_w(uint8_t data);
	uint8_t vt1682_200a_lcd_fr_delay_7_0_r();
	void vt1682_200a_lcd_fr_delay_7_0_w(uint8_t data);

	uint8_t vt1682_200d_misc_vregs2_r();
	void vt1682_200d_misc_vregs2_w(uint8_t data);
	uint8_t vt1682_200c_misc_vregs1_r();
	void vt1682_200c_misc_vregs1_w(uint8_t data);
	uint8_t vt1682_200b_misc_vregs0_r();
	void vt1682_200b_misc_vregs0_w(uint8_t data);

	uint8_t vt1682_2018_spregs_r();
	void vt1682_2018_spregs_w(uint8_t data);
	uint8_t vt1682_2019_bkgain_r();
	void vt1682_2019_bkgain_w(uint8_t data);

	uint8_t vt1682_2020_bk_linescroll_r();
	void vt1682_2020_bk_linescroll_w(uint8_t data);
	uint8_t vt1682_2021_lum_offset_r();
	void vt1682_2021_lum_offset_w(uint8_t data);
	uint8_t vt1682_2022_saturation_misc_r();
	void vt1682_2022_saturation_misc_w(uint8_t data);

	uint8_t vt1682_2023_lightgun_reset_r();
	void vt1682_2023_lightgun_reset_w(uint8_t data);
	uint8_t vt1682_2024_lightgun1_y_r();
	void vt1682_2024_lightgun1_y_w(uint8_t data);
	uint8_t vt1682_2025_lightgun1_x_r();
	void vt1682_2025_lightgun1_x_w(uint8_t data);
	uint8_t vt1682_2026_lightgun2_y_r();
	void vt1682_2026_lightgun2_y_w(uint8_t data);
	uint8_t vt1682_2027_lightgun2_x_r();
	void vt1682_2027_lightgun2_x_w(uint8_t data);

	uint8_t vt1682_2031_red_dac_r();
	void vt1682_2031_red_dac_w(uint8_t data);
	uint8_t vt1682_2032_green_dac_r();
	void vt1682_2032_green_dac_w(uint8_t data);
	uint8_t vt1682_2033_blue_dac_r();
	void vt1682_2033_blue_dac_w(uint8_t data);

	uint8_t vt1682_2028_r();
	void vt1682_2028_w(uint8_t data);
	uint8_t vt1682_2029_r();
	void vt1682_2029_w(uint8_t data);
	uint8_t vt1682_202a_r();
	void vt1682_202a_w(uint8_t data);
	uint8_t vt1682_202b_r();
	void vt1682_202b_w(uint8_t data);
	uint8_t vt1682_202e_r();
	void vt1682_202e_w(uint8_t data);
	uint8_t vt1682_2030_r();
	void vt1682_2030_w(uint8_t data);

	/* Video Helpers */

	uint16_t get_spriteram_addr()
	{
		return (m_2002_sprramaddr_2_0 & 0x07) | (m_2003_sprramaddr_10_3 << 3);
	}


	void set_spriteram_addr(uint16_t addr)
	{
		m_2002_sprramaddr_2_0 = addr & 0x07;
		m_2003_sprramaddr_10_3 = addr >> 3;
	}


	void inc_spriteram_addr()
	{
		// there is some strange logic here, sources state on DMA only so this might not be correct
		// it is unclear what happens if an address where the lower bits are 0x6/0x7 is set directly
		// the ii8in1 set clearly only writes 0x600 bytes worth of data, without using DMA suggesting
		// that this 'skipping' applies to non-DMA writes too.
		int addr = get_spriteram_addr();
		addr++;
		if ((addr & 0x07) >= 0x6)
		{
			addr += 0x8;
			addr &= ~0x7;
		}
		set_spriteram_addr(addr);
	}

	uint16_t get_vram_addr()
	{
		return (m_2005_vramaddr_7_0) | (m_2006_vramaddr_15_8 << 8);
	}

	void set_vram_addr(uint16_t addr)
	{
		m_2005_vramaddr_7_0 = addr & 0xff;
		m_2006_vramaddr_15_8 = addr >> 8;
	}

	/* System */
	uint8_t m_prgbank1_r0;
	uint8_t m_prgbank1_r1;
	uint8_t m_210c_prgbank1_r2;
	uint8_t m_2100_prgbank1_r3;
	uint8_t m_2118_prgbank1_r4_r5;

	uint8_t m_2107_prgbank0_r0;
	uint8_t m_2108_prgbank0_r1;
	uint8_t m_2109_prgbank0_r2;
	uint8_t m_210a_prgbank0_r3;
	uint8_t m_prgbank0_r4;
	uint8_t m_prgbank0_r5;

	uint8_t m_210b_misc_cs_prg0_bank_sel;

	uint8_t m_2105_vt1682_2105_comr6_tvmodes;
	uint8_t m_211c_regs_ext2421;

	uint8_t m_2122_dma_dt_addr_7_0;
	uint8_t m_2123_dma_dt_addr_15_8;

	uint8_t m_2124_dma_sr_addr_7_0;
	uint8_t m_2125_dma_sr_addr_15_8;

	uint8_t m_2126_dma_sr_bank_addr_22_15;
	uint8_t m_2128_dma_sr_bank_addr_24_23;

	uint8_t m_2106_enable_reg;

	uint8_t vt1682_2100_prgbank1_r3_r();
	void vt1682_2100_prgbank1_r3_w(uint8_t data);
	uint8_t vt1682_210c_prgbank1_r2_r();
	void vt1682_210c_prgbank1_r2_w(uint8_t data);

	uint8_t vt1682_2107_prgbank0_r0_r();
	void vt1682_2107_prgbank0_r0_w(uint8_t data);
	uint8_t vt1682_2108_prgbank0_r1_r();
	void vt1682_2108_prgbank0_r1_w(uint8_t data);
	uint8_t vt1682_2109_prgbank0_r2_r();
	void vt1682_2109_prgbank0_r2_w(uint8_t data);
	uint8_t vt1682_210a_prgbank0_r3_r();
	void vt1682_210a_prgbank0_r3_w(uint8_t data);

	uint8_t vt1682_prgbank0_r4_r();
	uint8_t vt1682_prgbank0_r5_r();
	uint8_t vt1682_prgbank1_r0_r();
	uint8_t vt1682_prgbank1_r1_r();

	void vt1682_prgbank1_r0_w(uint8_t data);
	void vt1682_prgbank1_r1_w(uint8_t data);
	void vt1682_prgbank0_r4_w(uint8_t data);
	void vt1682_prgbank0_r5_w(uint8_t data);

	uint8_t vt1682_2118_prgbank1_r4_r5_r();
	void vt1682_2118_prgbank1_r4_r5_w(uint8_t data);

	uint8_t vt1682_210b_misc_cs_prg0_bank_sel_r();
	void vt1682_210b_misc_cs_prg0_bank_sel_w(uint8_t data);

	void vt1682_2105_comr6_tvmodes_w(uint8_t data);

	void vt1682_212e_scuirq_clear_w(u8 data);

	void vt1682_211c_regs_ext2421_w(uint8_t data);

	uint8_t vt1682_2122_dma_dt_addr_7_0_r();
	void vt1682_2122_dma_dt_addr_7_0_w(uint8_t data);
	uint8_t vt1682_2123_dma_dt_addr_15_8_r();
	void vt1682_2123_dma_dt_addr_15_8_w(uint8_t data);

	uint8_t vt1682_2124_dma_sr_addr_7_0_r();
	void vt1682_2124_dma_sr_addr_7_0_w(uint8_t data);
	uint8_t vt1682_2125_dma_sr_addr_15_8_r();
	void vt1682_2125_dma_sr_addr_15_8_w(uint8_t data);

	uint8_t vt1682_2126_dma_sr_bank_addr_22_15_r();
	void vt1682_2126_dma_sr_bank_addr_22_15_w(uint8_t data);
	uint8_t vt1682_2128_dma_sr_bank_addr_24_23_r();
	void vt1682_2128_dma_sr_bank_addr_24_23_w(uint8_t data);

	uint8_t vt1682_2127_dma_status_r();
	void vt1682_2127_dma_size_trigger_w(uint8_t data);

	uint8_t vt1682_2106_enable_regs_r();
	void vt1682_2106_enable_regs_w(uint8_t data);

	uint8_t vt1682_212c_prng_r();
	void vt1682_212c_prng_seed_w(uint8_t data);

	virtual void clock_joy2();

	uint8_t inteact_212a_send_joy_clock2_r();

	/* Hacky */

	uint8_t soundcpu_irq_vector_hack_r(offs_t offset);
	uint8_t maincpu_irq_vector_hack_r(offs_t offset);
	void vt1682_sound_reset_hack_w(offs_t offset, uint8_t data);
	uint8_t m_scpu_is_in_reset;

	/* System Helpers */

	uint16_t get_dma_sr_addr()
	{
		return ((m_2124_dma_sr_addr_7_0 ) | (m_2125_dma_sr_addr_15_8 << 8)) & 0x7fff;
	}

	void set_dma_sr_addr(uint16_t addr)
	{
		addr &= 0x7fff;

		m_2124_dma_sr_addr_7_0 = addr & 0xff;
		m_2125_dma_sr_addr_15_8 = (m_2125_dma_sr_addr_15_8 & 0x80) | (addr >> 8); // don't change the external flag
	}

	uint16_t get_dma_dt_addr()
	{
		return ((m_2122_dma_dt_addr_7_0 ) | (m_2123_dma_dt_addr_15_8 << 8)) & 0x7fff;
	}

	void set_dma_dt_addr(uint16_t addr)
	{
		m_2122_dma_dt_addr_7_0 = addr & 0xff;
		m_2123_dma_dt_addr_15_8 = (m_2123_dma_dt_addr_15_8 & 0x80) | (addr >> 8); // don't change the external flag
	}

	bool get_dma_sr_isext()
	{
		return m_2125_dma_sr_addr_15_8 & 0x80 ? true : false;
	}

	bool get_dma_dt_isext()
	{
		return m_2123_dma_dt_addr_15_8 & 0x80 ? true : false;
	}

	bool get_dma_dt_is_video()
	{
		if (get_dma_dt_isext())
			return false;

		if (get_dma_dt_addr() == 0x2004)
			return true;

		if (get_dma_dt_addr() == 0x2007)
			return true;

		return false;
	}

	uint16_t get_dma_sr_bank_ddr()
	{
		return ((m_2126_dma_sr_bank_addr_22_15 ) | (m_2128_dma_sr_bank_addr_24_23 << 8)) & 0x3ff;
	}

	void do_dma_external_to_internal(int data, bool is_video);
	void do_dma_internal_to_internal(int data, bool is_video);

	/* Sound CPU Related*/

	uint8_t m_soundcpu_2118_dacleft_7_0;
	uint8_t m_soundcpu_2119_dacleft_15_8;
	uint8_t m_soundcpu_211a_dacright_7_0;
	uint8_t m_soundcpu_211b_dacright_15_8;

	void vt1682_soundcpu_211c_reg_irqctrl_w(uint8_t data);

	uint8_t vt1682_soundcpu_2118_dacleft_7_0_r();
	void vt1682_soundcpu_2118_dacleft_7_0_w(uint8_t data);
	uint8_t vt1682_soundcpu_2119_dacleft_15_8_r();
	void vt1682_soundcpu_2119_dacleft_15_8_w(uint8_t data);
	uint8_t vt1682_soundcpu_211a_dacright_7_0_r();
	void vt1682_soundcpu_211a_dacright_7_0_w(uint8_t data);
	uint8_t vt1682_soundcpu_211b_dacright_15_8_r();
	void vt1682_soundcpu_211b_dacright_15_8_w(uint8_t data);

	// TODO: hook these up properly, used by gm235upc
	uint8_t vt1682_soundcpu_2410_ioa_data_r() { return machine().rand() & 0x8a; }
	void vt1682_soundcpu_2410_ioa_data_w(uint8_t data) { }
	uint8_t vt1682_soundcpu_2410_iob_data_r() { return machine().rand(); }

	/* Support */

	void vt1682_timer_enable_trampoline_w(uint8_t data)
	{
		// this is used for raster interrupt effects, despite not being a scanline timer, so knowing when it triggers is useful, so trampoline it to avoid passing m_screen to the device
		LOGMASKED(LOG_OTHER, "%s: vt1682_timer_enable_trampoline_w: %02x @ position y%d, x%d\n", machine().describe_context(), data, m_screen->vpos(), m_screen->hpos());
		m_system_timer_dev->vt1682_timer_enable_w(data);
	};

	void vt1682_timer_preload_15_8_trampoline_w(uint8_t data)
	{
		LOGMASKED(LOG_OTHER, "%s: vt1682_timer_preload_15_8_trampoline_w: %02x @ position y%d, x%d\n", machine().describe_context(), data, m_screen->vpos(), m_screen->hpos());
		m_system_timer_dev->vt1682_timer_preload_15_8_w(data);
	};


	void update_banks();
	uint8_t translate_prg0select(uint8_t tp20_tp13);
	uint32_t translate_address_4000_to_7fff(uint16_t address);
	uint32_t translate_address_8000_to_ffff(uint16_t address);

	uint8_t rom_4000_to_7fff_r(offs_t offset);
	uint8_t rom_8000_to_ffff_r(offs_t offset);
	void rom_8000_to_ffff_w(offs_t offset, uint8_t data);

	TIMER_DEVICE_CALLBACK_MEMBER(scanline);
	TIMER_DEVICE_CALLBACK_MEMBER(line_render_start);

	bitmap_ind8 m_pal2_priority_bitmap;
	bitmap_ind8 m_pal1_priority_bitmap;
	bitmap_ind8 m_pal2_pix_bitmap;
	bitmap_ind8 m_pal1_pix_bitmap;

	void setup_video_pages(int which, int tilesize, int vs, int hs, int y8, int x8, uint16_t* pagebases);
	int get_address_for_tilepos(int x, int y, int tilesize, uint16_t* pagebases);

	void draw_tile_pixline(int segment, int tile, int yy, int x, int y, int palselect, int pal, int is16pix_high, int is16pix_wide, int bpp, int depth, int opaque, int flipx, int flipy, const rectangle& cliprect);
	void draw_layer(int which, int opaque, const rectangle& cliprect);
	void draw_sprites(const rectangle& cliprect);
};


class intec_interact_state : public vt_vt1682_state
{
public:
	intec_interact_state(const machine_config& mconfig, device_type type, const char* tag) :
		vt_vt1682_state(mconfig, type, tag),
		m_io_p1(*this, "IN0"),
		m_io_p2(*this, "IN1"),
		m_io_p3(*this, "IN2"),
		m_io_p4(*this, "IN3")
	{ }

	void banked_init();

	void intech_interact(machine_config& config);
	void intech_interact_bank(machine_config& config);

	virtual uint8_t porta_r();
	virtual uint8_t portb_r() { return 0x00;/*uint8_t ret = machine().rand() & 0xf; LOGMASKED(LOG_OTHER, "%s: portb_r returning: %1x\n", machine().describe_context(), ret); return ret;*/ };
	virtual uint8_t portc_r();
	virtual uint8_t portd_r() { return 0x00;/*uint8_t ret = machine().rand() & 0xf; LOGMASKED(LOG_OTHER, "%s: portd_r returning: %1x\n", machine().describe_context(), ret); return ret;*/ };

	void porta_w(uint8_t data);
	void portb_w(uint8_t data);
	void portc_w(uint8_t data) { LOGMASKED(LOG_OTHER, "%s: portc_w writing: %1x\n", machine().describe_context(), data & 0xf); };
	void portd_w(uint8_t data) { LOGMASKED(LOG_OTHER, "%s: portd_w writing: %1x\n", machine().describe_context(), data & 0xf); };

	void ext_rombank_w(uint8_t data);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:

	uint8_t m_previous_port_b;
	int m_input_pos;
	int m_current_bank;

	attotime m_transition_time;

	required_ioport m_io_p1;
	required_ioport m_io_p2;
	required_ioport m_io_p3;
	required_ioport m_io_p4;
};

class vt1682_dance_state : public vt_vt1682_state
{
public:
	vt1682_dance_state(const machine_config& mconfig, device_type type, const char* tag) :
		vt_vt1682_state(mconfig, type, tag),
		m_io_p1(*this, "IN0")
	{ }

	void vt1682_dance(machine_config& config);
	void gm235upc(machine_config& config);

protected:
	uint8_t uio_porta_r();
	void uio_porta_w(uint8_t data);

private:
	required_ioport m_io_p1;

	void rom_ram_map(address_map& map);
};

class vt1682_lxts3_state : public vt_vt1682_state
{
public:
	vt1682_lxts3_state(const machine_config& mconfig, device_type type, const char* tag) :
		vt_vt1682_state(mconfig, type, tag),
		m_io_p1(*this, "IN0")
	{ }

	void vt1682_lxts3(machine_config& config);
	void vt1682_24c02(machine_config& config);
	void vt1682_unk1682(machine_config& config);

	void unk1682_init();
	void njp60in1_init();
	void pgs268_init();

protected:
	uint8_t uio_porta_r();

	uint8_t iof_i2c_r() { return m_seeprom->read_sda() ? 0xc : 0x8; }
	void iof_i2c_w(uint8_t data) { m_seeprom->write_sda(BIT(data, 2)); m_seeprom->write_scl(BIT(data, 3)); }

private:
	required_ioport m_io_p1;
};

class vt1682_anpncpc_state : public vt1682_lxts3_state
{
public:
	vt1682_anpncpc_state(const machine_config& mconfig, device_type type, const char* tag) :
		vt1682_lxts3_state(mconfig, type, tag)
	{ }

	void anpncpc(machine_config& config);

protected:
	virtual void clear_sound_reset_line() override
	{
		vt_vt1682_state::clear_sound_reset_line();
		m_soundcpu->set_state_int(M6502_S, 0x1f0+3);
	}

private:
	uint8_t iof_i2c_r() { return m_seeprom->read_sda() ? 0x6 : 0x2; }
	void iof_i2c_w(uint8_t data) { m_seeprom->write_scl(BIT(data, 1)); m_seeprom->write_sda(BIT(data, 2)); }
};

class vt1682_mx10_state : public vt_vt1682_state
{
public:
	vt1682_mx10_state(const machine_config& mconfig, device_type type, const char* tag) :
		vt_vt1682_state(mconfig, type, tag),
		m_io_uiob(*this, "UIOB")
	{ }

	void mx10(machine_config& config);

	void mx10_init();

protected:
	uint8_t uioa_r() { logerror("%s uioa_r dir %02x\n", machine().describe_context(), m_uio->inteact_212a_uio_a_direction_r()); return 0xff; } // cmpmx11 needs something here to boot (probably just a power off button in here)
	uint8_t uiob_r() { logerror("%s uiob_r dir %02x\n", machine().describe_context(), m_uio->inteact_214a_uio_b_direction_r()); return m_io_uiob->read(); }
	void uiob_w(u8 data)
	{
		u8 direction = m_uio->inteact_214a_uio_b_direction_r();
		logerror("%s uiob_w %02x dir %02x\n", machine().describe_context(), data, direction );

		if (direction & 0x10)
		{
			if (data & 0x10)
			{
				m_bank->set_entry(1);
			}
			else
			{
				m_bank->set_entry(0);
			}
		}
	}

	virtual void clear_sound_reset_line() override
	{
		// The code uploaded by the Classic Max Pocket units doesn't initialize the SP on reset
		// leading to the stack destroying critical code / data and moving down RAM every time
		// the system resets the sound CPU when selecting new games or exiting to menu.
		//
		// While it could be there's a small internal ROM stub doing that before jumping to the
		// vectors (VT168 has this capability for both CPUs) it's equally likely that the 6502
		// core in the chip just initializes the SP to this value on every reset.
		vt_vt1682_state::clear_sound_reset_line();
		m_soundcpu->set_state_int(M6502_S, 0x1e4+0x3); // set stack pointer somewhere sensible, there is a block of 0xff bytes in this area, which could be the correct location
		m_soundcpu->set_state_int(M6502_P, 0x06); // disable interrupts
	}

private:
	required_ioport m_io_uiob;
};

class vt1682_exsport_state : public vt_vt1682_state
{
public:
	vt1682_exsport_state(const machine_config& mconfig, device_type type, const char* tag) :
		vt_vt1682_state(mconfig, type, tag),
		m_io_p1(*this, "P1"),
		m_io_p2(*this, "P2")
	{ }

	void vt1682_exsport(machine_config& config);
	void vt1682_exsportp(machine_config& config);

	virtual uint8_t uiob_r();
	void uiob_w(uint8_t data);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	int m_old_portb;
	int m_portb_shiftpos = 0;
	int m_p1_latch;
	int m_p2_latch;
	virtual void clock_joy2() override;

	required_ioport m_io_p1;
	required_ioport m_io_p2;
};

class vt1682_wow_state : public vt1682_exsport_state
{
public:
	vt1682_wow_state(const machine_config& mconfig, device_type type, const char* tag) :
		vt1682_exsport_state(mconfig, type, tag)
	{ }

	void vt1682_wow(machine_config& config);

protected:

private:
};


void vt_vt1682_state::video_start()
{
	m_screen->register_screen_bitmap(m_pal2_priority_bitmap);
	m_screen->register_screen_bitmap(m_pal1_priority_bitmap);
	m_screen->register_screen_bitmap(m_pal2_pix_bitmap);
	m_screen->register_screen_bitmap(m_pal1_pix_bitmap);

	m_pal2_priority_bitmap.fill(0xff);
	m_pal1_priority_bitmap.fill(0xff);
	m_pal2_pix_bitmap.fill(0xff);
	m_pal1_pix_bitmap.fill(0xff);
}



void vt_vt1682_state::machine_start()
{
	/* Video */
	save_item(NAME(m_2000));
	save_item(NAME(m_2001));

	save_item(NAME(m_2002_sprramaddr_2_0));
	save_item(NAME(m_2003_sprramaddr_10_3));

	save_item(NAME(m_2005_vramaddr_7_0));
	save_item(NAME(m_2006_vramaddr_15_8));

	save_item(NAME(m_201a_sp_segment_7_0));
	save_item(NAME(m_201b_sp_segment_11_8));
	save_item(NAME(m_segment_7_0_bk));
	save_item(NAME(m_segment_11_8_bk));

	save_item(NAME(m_main_control_bk));

	save_item(NAME(m_scroll_control_bk));

	save_item(NAME(m_xscroll_7_0_bk));
	save_item(NAME(m_yscroll_7_0_bk));

	save_item(NAME(m_200e_blend_pal_sel));
	save_item(NAME(m_200f_bk_pal_sel));

	save_item(NAME(m_2008_lcd_vs_delay));
	save_item(NAME(m_2009_lcd_hs_delay_7_0));
	save_item(NAME(m_200a_lcd_fr_delay_7_0));

	save_item(NAME(m_200d_misc_vregs2));
	save_item(NAME(m_200c_misc_vregs1));
	save_item(NAME(m_200b_misc_vregs0));

	save_item(NAME(m_2018_spregs));
	save_item(NAME(m_2019_bkgain));

	save_item(NAME(m_2020_bk_linescroll));
	save_item(NAME(m_2021_lum_offset));
	save_item(NAME(m_2022_saturation_misc));

	save_item(NAME(m_2023_lightgun_reset));
	save_item(NAME(m_2024_lightgun1_y));
	save_item(NAME(m_2025_lightgun1_x));
	save_item(NAME(m_2026_lightgun2_y));
	save_item(NAME(m_2027_lightgun2_x));

	save_item(NAME(m_2031_red_dac));
	save_item(NAME(m_2032_green_dac));
	save_item(NAME(m_2033_blue_dac));

	save_item(NAME(m_2028));
	save_item(NAME(m_2029));
	save_item(NAME(m_202a));
	save_item(NAME(m_202b));
	save_item(NAME(m_202e));
	save_item(NAME(m_2030));

	/* System */

	save_item(NAME(m_scu_to_main_irq_active));
	save_item(NAME(m_timera_to_main_irq_active));
	save_item(NAME(m_current_main_vector));
	save_item(NAME(m_new_main_vector));

	save_item(NAME(m_timera_to_sound_irq_active));
	save_item(NAME(m_timerb_to_sound_irq_active));
	save_item(NAME(m_current_sound_vector));
	save_item(NAME(m_new_sound_vector));

	save_item(NAME(m_prgbank1_r0));
	save_item(NAME(m_prgbank1_r1));
	save_item(NAME(m_210c_prgbank1_r2));
	save_item(NAME(m_2100_prgbank1_r3));
	save_item(NAME(m_2118_prgbank1_r4_r5));

	save_item(NAME(m_2107_prgbank0_r0));
	save_item(NAME(m_2108_prgbank0_r1));
	save_item(NAME(m_2109_prgbank0_r2));
	save_item(NAME(m_210a_prgbank0_r3));
	save_item(NAME(m_prgbank0_r4));
	save_item(NAME(m_prgbank0_r5));

	save_item(NAME(m_210b_misc_cs_prg0_bank_sel));
	save_item(NAME(m_2105_vt1682_2105_comr6_tvmodes));
	save_item(NAME(m_211c_regs_ext2421));

	save_item(NAME(m_2122_dma_dt_addr_7_0));
	save_item(NAME(m_2123_dma_dt_addr_15_8));
	save_item(NAME(m_2124_dma_sr_addr_7_0));
	save_item(NAME(m_2125_dma_sr_addr_15_8));

	save_item(NAME(m_2126_dma_sr_bank_addr_22_15));
	save_item(NAME(m_2128_dma_sr_bank_addr_24_23));

	save_item(NAME(m_2106_enable_reg));

	/* Sound CPU */

	save_item(NAME(m_soundcpu_2118_dacleft_7_0));
	save_item(NAME(m_soundcpu_2119_dacleft_15_8));
	save_item(NAME(m_soundcpu_211a_dacright_7_0));
	save_item(NAME(m_soundcpu_211b_dacright_15_8));

	save_item(NAME(m_scpu_is_in_reset));
}

void vt_vt1682_state::machine_reset()
{
	/* Video */
	m_2000 = 0;
	m_2001 = 0;

	m_2002_sprramaddr_2_0 = 0;
	m_2003_sprramaddr_10_3 = 0;

	m_2005_vramaddr_7_0 = 0;
	m_2006_vramaddr_15_8 = 0;

	m_201a_sp_segment_7_0 = 0;
	m_201b_sp_segment_11_8 = 0;
	m_segment_7_0_bk[0] = 0;
	m_segment_11_8_bk[0] = 0;
	m_segment_7_0_bk[1] = 0;
	m_segment_11_8_bk[1] = 0;

	m_main_control_bk[0] = 0;
	m_main_control_bk[1] = 0;

	m_scroll_control_bk[0] = 0;
	m_scroll_control_bk[1] = 0;

	m_xscroll_7_0_bk[0] = 0;
	m_yscroll_7_0_bk[0] = 0;
	m_xscroll_7_0_bk[1] = 0;
	m_yscroll_7_0_bk[1] = 0;

	m_200e_blend_pal_sel = 0;
	m_200f_bk_pal_sel = 0;

	m_2008_lcd_vs_delay = 0;
	m_2009_lcd_hs_delay_7_0 = 0;
	m_200a_lcd_fr_delay_7_0 = 0;

	m_200d_misc_vregs2 = 0;
	m_200c_misc_vregs1 = 0;
	m_200b_misc_vregs0 = 0;

	m_2018_spregs = 0;
	m_2019_bkgain = 0;

	m_2020_bk_linescroll = 0;
	m_2021_lum_offset = 0;
	m_2022_saturation_misc = 0;

	m_2023_lightgun_reset = 0;
	m_2024_lightgun1_y = 0;
	m_2025_lightgun1_x = 0;
	m_2026_lightgun2_y = 0;
	m_2027_lightgun2_x = 0;

	m_2031_red_dac = 0;
	m_2032_green_dac = 0;
	m_2033_blue_dac = 0;

	m_2028 = 0;
	m_2029 = 0;
	m_202a = 0;
	m_202b = 0;
	m_202e = 0;
	m_2030 = 0;

	/* System */
	m_scu_to_main_irq_active = 0;
	m_timera_to_main_irq_active = 0;
	m_current_main_vector = 0xfffe;
	m_new_main_vector = 0xfffe;

	m_timera_to_sound_irq_active = 0;
	m_timerb_to_sound_irq_active = 0;
	m_current_sound_vector = 0x0ff8;
	m_new_sound_vector = 0x0ff8;

	m_prgbank1_r0 = 0;
	m_prgbank1_r1 = 0;
	m_210c_prgbank1_r2 = 0;
	m_2100_prgbank1_r3 = 0;
	m_2118_prgbank1_r4_r5 = 0;

	m_2107_prgbank0_r0 = 0x3f;
	m_2108_prgbank0_r1 = 0;
	m_2109_prgbank0_r2 = 0;
	m_210a_prgbank0_r3 = 0;
	m_prgbank0_r4 = 0;
	m_prgbank0_r5 = 0;

	m_210b_misc_cs_prg0_bank_sel = 0;
	m_2105_vt1682_2105_comr6_tvmodes = 0;
	m_211c_regs_ext2421 = 0;

	m_2122_dma_dt_addr_7_0 = 0;
	m_2123_dma_dt_addr_15_8 = 0;

	m_2124_dma_sr_addr_7_0 = 0;
	m_2125_dma_sr_addr_15_8 = 0;

	m_2126_dma_sr_bank_addr_22_15 = 0;
	m_2128_dma_sr_bank_addr_24_23 = 0;

	m_2106_enable_reg = 0;

	/* Sound CPU */

	m_soundcpu_2118_dacleft_7_0 = 0;
	m_soundcpu_2119_dacleft_15_8 = 0;
	m_soundcpu_211a_dacright_7_0 = 0;
	m_soundcpu_211b_dacright_15_8 = 0;

	/* Misc */

	update_banks();

	m_bank->set_entry(0);

	m_soundcpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
	m_scpu_is_in_reset = 1;

	update_main_interrupts();
	update_sound_interrupts();
}

/*

Address translation

----------------------------------------------------------------

First table uses bits from PB0r0, PB0r1, PB0r2 (0x8000 and above) or PB0r4, PB0r5 (below 0x8000)

PB0r0 = Program Bank 0 Register 0
PB0r1 = Program Bank 0 Register 1
PB0r2 = Program Bank 0 Register 2

PB0r4 = Program Bank 0 Register 4
PB0r5 = Program Bank 0 Register 5

PQ2EN   COMR6   A:15    A:14    A:13    |   TP:20   TP:19   TP:18   TP:17   TP:16   TP:15   TP:14   TP:13
-----------------------------------------------------------------------------------------------------------
0       0       1       0       0       |   PB0r0:7 PB0r0:6 PB0r0:5 PB0r0:4 PB0r0:3 PB0r0:2 PB0r0:1 PB0r0:0   (all PB0r0)
0       0       1       0       1       |   PB0r1:7 PB0r1:6 PB0r1:5 PB0r1:4 PB0r1:3 PB0r1:2 PB0r1:1 PB0r1:0   (all PB0r1)
0       0       1       1       0       |   1       1       1       1       1       1       1       0
0       0       1       1       1       |   1       1       1       1       1       1       1       1
0       1       1       0       0       |   1       1       1       1       1       1       1       0
0       1       1       0       1       |   PB0r1:7 PB0r1:6 PB0r1:5 PB0r1:4 PB0r1:3 PB0r1:2 PB0r1:1 PB0r1:0   (all PB0r1)
0       1       1       1       0       |   PB0r0:7 PB0r0:6 PB0r0:5 PB0r0:4 PB0r0:3 PB0r0:2 PB0r0:1 PB0r0:0   (all PB0r0)
0       1       1       1       1       |   1       1       1       1       1       1       1       1
1       0       1       0       0       |   PB0r0:7 PB0r0:6 PB0r0:5 PB0r0:4 PB0r0:3 PB0r0:2 PB0r0:1 PB0r0:0   (all PB0r0)
1       0       1       0       1       |   PB0r1:7 PB0r1:6 PB0r1:5 PB0r1:4 PB0r1:3 PB0r1:2 PB0r1:1 PB0r1:0   (all PB0r1)
1       0       1       1       0       |   PB0r2:7 PB0r2:6 PB0r2:5 PB0r2:4 PB0r2:3 PB0r2:2 PB0r2:1 PB0r2:0   (all PB0r2)
1       0       1       1       1       |   1       1       1       1       1       1       1       1
1       1       1       0       0       |   PB0r2:7 PB0r2:6 PB0r2:5 PB0r2:4 PB0r2:3 PB0r2:2 PB0r2:1 PB0r2:0   (all PB0r2)
1       1       1       0       1       |   PB0r1:7 PB0r1:6 PB0r1:5 PB0r1:4 PB0r1:3 PB0r1:2 PB0r1:1 PB0r1:0   (all PB0r1)
1       1       1       1       0       |   PB0r0:7 PB0r0:6 PB0r0:5 PB0r0:4 PB0r0:3 PB0r0:2 PB0r0:1 PB0r0:0   (all PB0r0)
1       1       1       1       1       |   1       1       1       1       1       1       1       1
-----------------------------------------------------------------------------------------------------------
-       -       0       1       1       |   PB0r5:7 PB0r5:6 PB0r5:5 PB0r5:4 PB0r5:3 PB0r5:2 PB0r5:1 PB0r5:0   (all PB0r5)
-       -       0       1       0       |   PB0r4:7 PB0r4:6 PB0r4:5 PB0r4:4 PB0r4:3 PB0r4:2 PB0r4:1 PB0r4:0   (all PB0r4)

----------------------------------------------------------------

second table uses bits from above, and PB0r3

Program Bank 0 Select   |   PA:20   PA:19   PA:18   PA:17   PA:16   PA:15   PA:14   PA:13
-------------------------------------------------------------------------------------------
0       0       0       |   PB0r3:7 PB0r3:6 TP:18   TP:17   TP:16   TP:15   TP:14   TP:13
0       0       1       |   PB0r3:7 PB0r3:6 PB0r3:5 TP:17   TP:16   TP:15   TP:14   TP:13
0       1       0       |   PB0r3:7 PB0r3:6 PB0r3:5 PB0r3:4 TP:16   TP:15   TP:14   TP:13
0       1       1       |   PB0r3:7 PB0r3:6 PB0r3:5 PB0r3:4 PB0r3:3 TP:15   TP:14   TP:13
1       0       0       |   PB0r3:7 PB0r3:6 PB0r3:5 PB0r3:4 PB0r3:3 PB0r3:2 TP:14   TP:13
1       0       1       |   PB0r3:7 PB0r3:6 PB0r3:5 PB0r3:4 PB0r3:3 PB0r3:2 PB0r3:1 TP:13
1       1       0       |   PB0r3:7 PB0r3:6 PB0r3:5 PB0r3:4 PB0r3:3 PB0r3:2 PB0r3:1 PB0r3:0
1       1       1       |   TP:20   TP:19   TP:18   TP:17   TP:16   TP:15   TP:14   TP:13

PB0r3 = Program Bank 0 Register 3
TP = Address translated by 1st table

----------------------------------------------------------------

third table uses bits from PB1r0, PB1r1, PB1r2, PB1r3 (0x8000 and above) or PB1r4, PB1r5 (below 0x8000)

PB1r0 = Program Bank 1 Register 0
PB1r1 = Program Bank 1 Register 1
PB1r2 = Program Bank 1 Register 2
PB1r3 = Program Bank 1 Register 3

PB1r4 = Program Bank 1 Register 4
PB1r5 = Program Bank 1 Register 5

EXT2421 PQ2EN   COMR6   A:15    A:14    A:13    |   PA:24   PA:23   PA:22   PA:21
------------------------------------------------------------------------------------
1       -       -       1       -       -       |   PB1r3:3 PB1r3:2 PB1r3:1 PB1r3:0    (all PB1r3)
------------------------------------------------------------------------------------
0       0       0       1       0       0       |   PB1r0:3 PB1r0:2 PB1r0:1 PB1r0:0    (all PB1r0)
0       0       0       1       0       1       |   PB1r1:3 PB1r1:2 PB1r1:1 PB1r1:0    (all PB1r1)
0       0       0       1       1       0       |   PB1r3:3 PB1r3:2 PB1r3:1 PB1r3:0    (all PB1r3)
0       0       0       1       1       1       |   PB1r3:3 PB1r3:2 PB1r3:1 PB1r3:0    (all PB1r3)
0       0       1       1       0       0       |   PB1r3:3 PB1r3:2 PB1r3:1 PB1r3:0    (all PB1r3)
0       0       1       1       0       1       |   PB1r1:3 PB1r1:2 PB1r1:1 PB1r1:0    (all PB1r1)
0       0       1       1       1       0       |   PB1r0:3 PB1r0:2 PB1r0:1 PB1r0:0    (all PB1r0)
0       0       1       1       1       1       |   PB1r3:3 PB1r3:2 PB1r3:1 PB1r3:0    (all PB1r3)
0       1       0       1       0       0       |   PB1r0:3 PB1r0:2 PB1r0:1 PB1r0:0    (all PB1r0)
0       1       0       1       0       1       |   PB1r1:3 PB1r1:2 PB1r1:1 PB1r1:0    (all PB1r1)
0       1       0       1       1       0       |   PB1r2:3 PB1r2:2 PB1r2:1 PB1r2:0    (all PB1r2)
0       1       0       1       1       1       |   PB1r3:3 PB1r3:2 PB1r3:1 PB1r3:0    (all PB1r3)
0       1       1       1       0       0       |   PB1r2:3 PB1r2:2 PB1r2:1 PB1r2:0    (all PB1r2)
0       1       1       1       0       1       |   PB1r1:3 PB1r1:2 PB1r1:1 PB1r1:0    (all PB1r1)
0       1       1       1       1       0       |   PB1r0:3 PB1r0:2 PB1r0:1 PB1r0:0    (all PB1r0)
0       1       1       1       1       1       |   PB1r3:3 PB1r3:2 PB1r3:1 PB1r3:0    (all PB1r3)
------------------------------------------------------------------------------------
-       -       -       0       1       1       |   PB1r5:3 PB1r5:2 PB1r5:1 PB1r5:0    (all PB1r5)
-       -       -       0       1       0       |   PB1r4:3 PB1r4:2 PB1r4:1 PB1r4:0    (all PB1r4)


*/
void vt_vt1682_state::update_banks()
{
	/* must use

	m_prgbank1_r0
	m_prgbank1_r1
	m_210c_prgbank1_r2
	m_2100_prgbank1_r3
	m_2118_prgbank1_r4_r5

	m_2107_prgbank0_r0
	m_2108_prgbank0_r1
	m_2109_prgbank0_r2
	m_210a_prgbank0_r3
	m_prgbank0_r4
	m_prgbank0_r5

	m_2105_vt1682_2105_comr6_tvmodes
	m_211c_regs_ext2421
	m_210b_misc_cs_prg0_bank_sel

	everything that changes these calls here, so if we wanted to do this with actual
	banks then here would be the place

	*/
}

uint8_t vt_vt1682_state::translate_prg0select(uint8_t tp20_tp13)
{
	uint8_t bank = m_210b_misc_cs_prg0_bank_sel & 0x07;

	uint8_t ret = 0x00;

	switch (bank)
	{
	case 0x0: ret = (m_210a_prgbank0_r3 & 0xc0) | (tp20_tp13 & 0x3f); break;
	case 0x1: ret = (m_210a_prgbank0_r3 & 0xe0) | (tp20_tp13 & 0x1f); break;
	case 0x2: ret = (m_210a_prgbank0_r3 & 0xf0) | (tp20_tp13 & 0x0f); break;
	case 0x3: ret = (m_210a_prgbank0_r3 & 0xf8) | (tp20_tp13 & 0x07); break;
	case 0x4: ret = (m_210a_prgbank0_r3 & 0xfc) | (tp20_tp13 & 0x03); break;
	case 0x5: ret = (m_210a_prgbank0_r3 & 0xfe) | (tp20_tp13 & 0x01); break;
	case 0x6: ret = m_210a_prgbank0_r3; break;
	case 0x7: ret = tp20_tp13;  break;
	}

	return ret;
}

uint32_t vt_vt1682_state::translate_address_4000_to_7fff(uint16_t address)
{
	uint32_t realaddress = 0x00000000;

	uint8_t prgbank1_r4 = (m_2118_prgbank1_r4_r5 & 0x0f);
	uint8_t prgbank1_r5 = (m_2118_prgbank1_r4_r5 & 0xf0)>>4;

	int tp20_tp13 = 0;
	int pa24_pa21 = 0;

	switch (address & 0x6000)
	{
	case 0x4000:
		tp20_tp13 = m_prgbank0_r4;
		pa24_pa21 = prgbank1_r4;
		break;

	case 0x6000:
		tp20_tp13 = m_prgbank0_r5;
		pa24_pa21 = prgbank1_r5;
		break;

	// invalid cases
	default:
	case 0x0000:
	case 0x2000:
		break;

	}

	int pa20_pa13 = translate_prg0select(tp20_tp13);

	realaddress = address & 0x1fff;
	realaddress |= pa20_pa13 << 13;
	realaddress |= pa24_pa21 << 21;

	return realaddress;
}

uint32_t vt_vt1682_state::translate_address_8000_to_ffff(uint16_t address)
{
	uint32_t realaddress = 0x00000000;

	int tp20_tp13 = 0;
	int pa24_pa21 = 0;

	const int pq2en = (m_210b_misc_cs_prg0_bank_sel & 0x40)>>6;
	const int comr6 = (m_2105_vt1682_2105_comr6_tvmodes & 0x40)>>6;
	const int a14_a13 = (address & 0x6000) >> 13;
	const int lookup = a14_a13 | (comr6 << 2) | (pq2en << 3);

	switch (lookup)
	{
	// PQ2EN disabled, COMR6 disabled (0,1,2,3 order)
	case 0x0: tp20_tp13 = m_2107_prgbank0_r0;   pa24_pa21 = m_prgbank1_r0;      break;
	case 0x1: tp20_tp13 = m_2108_prgbank0_r1;   pa24_pa21 = m_prgbank1_r1;      break;
	case 0x2: tp20_tp13 = 0xfe;                 pa24_pa21 = m_2100_prgbank1_r3; break;
	case 0x3: tp20_tp13 = 0xff;                 pa24_pa21 = m_2100_prgbank1_r3; break;
	// PQ2EN disabled, COMR6 enabled (2,1,0,3 order)
	case 0x4: tp20_tp13 = 0xfe;                 pa24_pa21 = m_2100_prgbank1_r3; break;
	case 0x5: tp20_tp13 = m_2108_prgbank0_r1;   pa24_pa21 = m_prgbank1_r1;      break;
	case 0x6: tp20_tp13 = m_2107_prgbank0_r0;   pa24_pa21 = m_prgbank1_r0;      break;
	case 0x7: tp20_tp13 = 0xff;                 pa24_pa21 = m_2100_prgbank1_r3; break;
	// PQ2EN enabled, COMR6 disabled (0,1,2,3 order) (2 is now m_2109_prgbank0_r2, not 0xfe)
	case 0x8: tp20_tp13 = m_2107_prgbank0_r0;   pa24_pa21 = m_prgbank1_r0;      break;
	case 0x9: tp20_tp13 = m_2108_prgbank0_r1;   pa24_pa21 = m_prgbank1_r1;      break;
	case 0xa: tp20_tp13 = m_2109_prgbank0_r2;   pa24_pa21 = m_210c_prgbank1_r2; break;
	case 0xb: tp20_tp13 = 0xff;                 pa24_pa21 = m_2100_prgbank1_r3; break;
	// PQ2EN enabled, COMR6 enabled (2,1,0,3 order) (2 is now m_2109_prgbank0_r2, not 0xfe)
	case 0xc: tp20_tp13 = m_2109_prgbank0_r2;   pa24_pa21 = m_210c_prgbank1_r2; break;
	case 0xd: tp20_tp13 = m_2108_prgbank0_r1;   pa24_pa21 = m_prgbank1_r1;      break;
	case 0xe: tp20_tp13 = m_2107_prgbank0_r0;   pa24_pa21 = m_prgbank1_r0;      break;
	case 0xf: tp20_tp13 = 0xff;                 pa24_pa21 = m_2100_prgbank1_r3; break;
	}

	// override selection above
	const int ext2421 = (m_211c_regs_ext2421 & 0x20) >> 5;
	if (ext2421)
	{
		pa24_pa21 = m_2100_prgbank1_r3;
	}

	const int pa20_pa13 = translate_prg0select(tp20_tp13);

	realaddress = address & 0x1fff;
	realaddress |= pa20_pa13 << 13;
	realaddress |= pa24_pa21 << 21;

	return realaddress;
}

uint8_t vt_vt1682_state::rom_4000_to_7fff_r(offs_t offset)
{
	const uint32_t address = translate_address_4000_to_7fff(offset + 0x4000);
	return m_fullrom->read8(address);
}

uint8_t vt_vt1682_state::rom_8000_to_ffff_r(offs_t offset)
{
	const uint32_t address = translate_address_8000_to_ffff(offset + 0x8000);
	return m_fullrom->read8(address);
}

void vt_vt1682_state::rom_8000_to_ffff_w(offs_t offset, uint8_t data)
{
	const uint32_t address = translate_address_8000_to_ffff(offset + 0x8000);
	m_fullrom->write8(address, data);
}


/************************************************************************************************************************************
 VT1682 PPU Registers
************************************************************************************************************************************/

/*
    Address 0x2000 r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - Capture
    0x08 - SLAVE
    0x04 - (unused)
    0x02 - (unused)
    0x01 - NMI_EN
*/

uint8_t vt_vt1682_state::vt1682_2000_r()
{
	uint8_t ret = m_2000;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2000_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2000_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2000_w writing: %02x (Capture:%1x Slave:%1x NMI_Enable:%1x)\n", machine().describe_context(), data, (data & 0x10) >> 4, (data & 0x08) >> 3, (data & 0x01) >> 0);
	m_2000 = data;
}

/*
    Address 0x2001 READ (MAIN CPU)

    0x80 - VBLANK
    0x40 - SP ERR
    0x20 - (unused)
    0x10 - (unused)
    0x08 - (unused)
    0x04 - (unused)
    0x02 - (unused)
    0x01 - (unused)

    Address 0x2001 WRITE (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - EXT CLK DIV
    0x04 - EXT CLK DIV
    0x02 - SP INI (blank sprites on left 8 pixels)
    0x01 - BK INI (blank bg on left 8 pixels)
*/

uint8_t vt_vt1682_state::vt1682_2001_vblank_r()
{
	uint8_t ret = 0x00;

	int sp_err = 0; // too many sprites per lien
	int vblank = m_screen->vpos() > 239 ? 1 : 0; // in vblank, the pinball game in miwi2_16 under 'drum master' requires this to become set before the VBL interrupt fires

	ret |= sp_err << 6;
	ret |= vblank << 7;

	LOGMASKED(LOG_OTHER, "%s: vt1682_2001_vblank_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2001_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2001_w writing: %02x (ext_clk_div:%1x sp_ini:%1x bk_ini:%1x)\n", machine().describe_context(), data,
		(data & 0x0c) >> 2, (data & 0x02) >> 1, (data & 0x01) >> 0);

	m_2001 = data;
}


/*
    Address 0x2002 r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - (unused)
    0x04 - SPRAM ADDR:2
    0x02 - SPRAM ADDR:1
    0x01 - SPRAM ADDR:0
*/

uint8_t vt_vt1682_state::vt1682_2002_sprramaddr_2_0_r()
{
	uint8_t ret = m_2002_sprramaddr_2_0;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2002_sprramaddr_2_0_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2002_sprramaddr_2_0_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2002_sprramaddr_2_0_w writing: %02x\n", machine().describe_context(), data);
	m_2002_sprramaddr_2_0 = data & 0x07;
}

/*
    Address 0x2003 r/w (MAIN CPU)

    0x80 - SPRAM ADDR:10
    0x40 - SPRAM ADDR:9
    0x20 - SPRAM ADDR:8
    0x10 - SPRAM ADDR:7
    0x08 - SPRAM ADDR:6
    0x04 - SPRAM ADDR:5
    0x02 - SPRAM ADDR:4
    0x01 - SPRAM ADDR:3
*/

uint8_t vt_vt1682_state::vt1682_2003_sprramaddr_10_3_r()
{
	uint8_t ret = m_2003_sprramaddr_10_3;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2003_sprramaddr_10_3_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2003_sprramaddr_10_3_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2003_sprramaddr_10_3_w writing: %02x\n", machine().describe_context(), data);
	m_2003_sprramaddr_10_3 = data;
}

/*
    Address 0x2004 r/w (MAIN CPU)

    0x80 - SPRAM DATA:7
    0x40 - SPRAM DATA:6
    0x20 - SPRAM DATA:5
    0x10 - SPRAM DATA:4
    0x08 - SPRAM DATA:3
    0x04 - SPRAM DATA:2
    0x02 - SPRAM DATA:1
    0x01 - SPRAM DATA:0
*/

uint8_t vt_vt1682_state::vt1682_2004_sprram_data_r()
{
	uint16_t spriteram_address = get_spriteram_addr();
	uint8_t ret = m_spriteram->read8(spriteram_address);
	LOGMASKED(LOG_OTHER, "%s: vt1682_2004_sprram_data_r returning: %02x from SpriteRam Address %04x\n", machine().describe_context(), ret, spriteram_address);
	// no increment on read?
	// documentation indicates this doesn't work
	return ret;
}

void vt_vt1682_state::vt1682_2004_sprram_data_w(uint8_t data)
{
	uint16_t spriteram_address = get_spriteram_addr();
	m_spriteram->write8(spriteram_address, data);

	LOGMASKED(LOG_SRAM_WRITES, "%s: vt1682_2004_sprram_data_w writing: %02x to SpriteRam Address %04x\n", machine().describe_context(), data, spriteram_address);
	inc_spriteram_addr();
}


/*
    Address 0x2005 r/w (MAIN CPU)

    0x80 - VRAM ADDR:7
    0x40 - VRAM ADDR:6
    0x20 - VRAM ADDR:5
    0x10 - VRAM ADDR:4
    0x08 - VRAM ADDR:3
    0x04 - VRAM ADDR:2
    0x02 - VRAM ADDR:1
    0x01 - VRAM ADDR:0
*/

uint8_t vt_vt1682_state::vt1682_2005_vramaddr_7_0_r()
{
	uint8_t ret = m_2005_vramaddr_7_0;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2005_vramaddr_7_0_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2005_vramaddr_7_0_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2005_vramaddr_7_0_w writing: %02x\n", machine().describe_context(), data);
	m_2005_vramaddr_7_0 = data;
}

/*
    Address 0x2006 r/w (MAIN CPU)

    0x80 - VRAM ADDR:15
    0x40 - VRAM ADDR:14
    0x20 - VRAM ADDR:13
    0x10 - VRAM ADDR:12
    0x08 - VRAM ADDR:11
    0x04 - VRAM ADDR:10
    0x02 - VRAM ADDR:9
    0x01 - VRAM ADDR:8
*/

uint8_t vt_vt1682_state::vt1682_2006_vramaddr_15_8_r()
{
	uint8_t ret = m_2006_vramaddr_15_8;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2006_vramaddr_15_8 returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2006_vramaddr_15_8_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2006_vramaddr_15_8 writing: %02x\n", machine().describe_context(), data);
	m_2006_vramaddr_15_8 = data;
}


/*
    Address 0x2007 r/w (MAIN CPU)

    0x80 - VRAM DATA:7
    0x40 - VRAM DATA:6
    0x20 - VRAM DATA:5
    0x10 - VRAM DATA:4
    0x08 - VRAM DATA:3
    0x04 - VRAM DATA:2
    0x02 - VRAM DATA:1
    0x01 - VRAM DATA:0
*/

uint8_t vt_vt1682_state::vt1682_2007_vram_data_r()
{
	uint16_t vram_address = get_vram_addr();
	uint8_t ret = m_vram->read8(vram_address);
	LOGMASKED(LOG_OTHER, "%s: vt1682_2007_vram_data_r returning: %02x from VideoRam Address %04x\n", machine().describe_context(), ret, vram_address);
	// no increment on read?
	// documentation indicates this doesn't work
	return ret;
}

void vt_vt1682_state::vt1682_2007_vram_data_w(uint8_t data)
{
	uint16_t vram_address = get_vram_addr();

	m_vram->write8(vram_address, data);

	if (m_2000 & 0x4)
	{
		// this mode is completely undocumented, but needed for many games in the cmpmx10 / cmpmx11 sets
		if (vram_address & 0x01)
		{
			vram_address += 0x40;
			vram_address &= ~0x01;
		}
		else
		{
			vram_address++;
		}
	}
	else
	{
		vram_address++;
	}

	set_vram_addr(vram_address); // update registers
}


/*
    Address 0x2008 r/w (MAIN CPU)

    0x80 - LCD VS DELAY
    0x40 - LCD VS DELAY
    0x20 - LCD VS DELAY
    0x10 - LCD VS DELAY
    0x08 - LCD VS DELAY
    0x04 - LCD VS DELAY
    0x02 - LCD VS DELAY
    0x01 - LCD VS DELAY
*/

uint8_t vt_vt1682_state::vt1682_2008_lcd_vs_delay_r()
{
	uint8_t ret = m_2008_lcd_vs_delay;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2008_lcd_vs_delay_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2008_lcd_vs_delay_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2008_lcd_vs_delay_w writing: %02x\n", machine().describe_context(), data);
	m_2008_lcd_vs_delay = data;
}

/*
    Address 0x2009 r/w (MAIN CPU)

    0x80 - LCD HS DELAY:7
    0x40 - LCD HS DELAY:6
    0x20 - LCD HS DELAY:5
    0x10 - LCD HS DELAY:4
    0x08 - LCD HS DELAY:3
    0x04 - LCD HS DELAY:2
    0x02 - LCD HS DELAY:1
    0x01 - LCD HS DELAY:0
*/

uint8_t vt_vt1682_state::vt1682_2009_lcd_hs_delay_7_0_r()
{
	uint8_t ret = m_2009_lcd_hs_delay_7_0;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2009_lcd_hs_delay_7_0_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2009_lcd_hs_delay_7_0_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2009_lcd_hs_delay_7_0_w writing: %02x\n", machine().describe_context(), data);
	m_2009_lcd_hs_delay_7_0 = data;
}

/*
    Address 0x200a r/w (MAIN CPU)

    0x80 - LCD FR DELAY:7
    0x40 - LCD FR DELAY:6
    0x20 - LCD FR DELAY:5
    0x10 - LCD FR DELAY:4
    0x08 - LCD FR DELAY:3
    0x04 - LCD FR DELAY:2
    0x02 - LCD FR DELAY:1
    0x01 - LCD FR DELAY:0
*/

uint8_t vt_vt1682_state::vt1682_200a_lcd_fr_delay_7_0_r()
{
	uint8_t ret = m_200a_lcd_fr_delay_7_0;
	LOGMASKED(LOG_OTHER, "%s: vt1682_200a_lcd_fr_delay_7_0_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_200a_lcd_fr_delay_7_0_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_200a_lcd_fr_delay_7_0_w writing: %02x\n", machine().describe_context(), data);
	m_200a_lcd_fr_delay_7_0 = data;
}


/*
    Address 0x200b r/w (MAIN CPU)

    0x80 - CH2 Odd Line Colour
    0x40 - CH2 Odd Line Colour
    0x20 - CH2 Even Line Colour
    0x10 - CH2 Even Line Colour
    0x08 - CH2 SEL
    0x04 - CH2 REV
    0x02 - LCD FR:8
    0x01 - LCD HS:8
*/

uint8_t vt_vt1682_state::vt1682_200b_misc_vregs0_r()
{
	uint8_t ret = m_200b_misc_vregs0;
	LOGMASKED(LOG_OTHER, "%s: vt1682_200b_misc_vregs0_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_200b_misc_vregs0_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_200b_misc_vregs0_w writing: %02x\n", machine().describe_context(), data);
	m_200b_misc_vregs0 = data;
}

/*
    Address 0x200c r/w (MAIN CPU)

    0x80 - FRate
    0x40 - DotODR
    0x20 - LCD CLOCK
    0x10 - LCD CLOCK
    0x08 - UPS 052
    0x04 - Field AC
    0x02 - LCD MODE
    0x01 - LCD MODE
*/

uint8_t vt_vt1682_state::vt1682_200c_misc_vregs1_r()
{
	uint8_t ret = m_200c_misc_vregs1;
	LOGMASKED(LOG_OTHER, "%s: vt1682_200c_misc_vregs1_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_200c_misc_vregs1_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_200c_misc_vregs1_w writing: %02x\n", machine().describe_context(), data);
	m_200c_misc_vregs1 = data;
}

/*
    Address 0x200d r/w (MAIN CPU)

    0x80 - LCD ENABLE
    0x40 - Dot 240
    0x20 - Reverse
    0x10 - Vcom
    0x08 - Odd Line Color
    0x04 - Odd Line Color
    0x02 - Even Line Color
    0x01 - Even Line Color
*/

uint8_t vt_vt1682_state::vt1682_200d_misc_vregs2_r()
{
	uint8_t ret = m_200d_misc_vregs2;
	LOGMASKED(LOG_OTHER, "%s: vt1682_200d_misc_vregs2_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_200d_misc_vregs2_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_200d_misc_vregs2_w writing: %02x\n", machine().describe_context(), data);
	m_200d_misc_vregs2 = data;
}


/*
    Address 0x200e r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - Blend2 - LCD output blending  0 = Overlapped (use depth) 1 = 50% blend Pal1/Pal2
    0x10 - Blend1 - TV output blending   0 = Overlapped (use depth) 1 = 50% blend Pal1/Pal2
    0x08 - Palette 2 Out Sel 'SB4' \
    0x04 - Palette 2 Out Sel 'SB6' /- 0 = output Palette 2 Disable, 1 = output Palette 2 to LCD only, 2 = Output Palette 2 to TV only, 3 = Output Palette 2 to both
    0x02 - Palette 1 Out Sel 'SB3' \
    0x01 - Palette 1 Out Sel 'SB5' /- 0 = output Palette 1 Disable, 1 = output Palette 1 to LCD only, 2 = Output Palette 1 to TV only, 3 = Output Palette 1 to both
*/

uint8_t vt_vt1682_state::vt1682_200e_blend_pal_sel_r()
{
	uint8_t ret = m_200e_blend_pal_sel;
	LOGMASKED(LOG_OTHER, "%s: vt1682_200e_blend_pal_sel_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_200e_blend_pal_sel_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_200e_blend_pal_sel_w writing: %02x\n", machine().describe_context(), data);
	m_200e_blend_pal_sel = data;
}

/*
    Address 0x200f r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - Bk2 Palette Select 'BK2 SB2'
    0x04 - Bk2 Palette Select 'BK2 SB1'
    0x02 - Bk1 Palette Select 'BK1 SB2'
    0x01 - Bk1 Palette Select 'BK1 SB1'
*/

uint8_t vt_vt1682_state::vt1682_200f_bk_pal_sel_r()
{
	uint8_t ret = m_200f_bk_pal_sel;
	LOGMASKED(LOG_OTHER, "%s: vt1682_200f_bk_pal_sel_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_200f_bk_pal_sel_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_200f_bk_pal_sel_w writing: %02x\n", machine().describe_context(), data);
	m_200f_bk_pal_sel = data;
}

/*
    Address 0x2010 r/w (MAIN CPU)

    0x80 - BK1X:7
    0x40 - BK1X:6
    0x20 - BK1X:5
    0x10 - BK1X:4
    0x08 - BK1X:3
    0x04 - BK1X:2
    0x02 - BK1X:1
    0x01 - BK1X:0
*/

uint8_t vt_vt1682_state::vt1682_2010_bk1_xscroll_7_0_r()
{
	uint8_t ret = m_xscroll_7_0_bk[0];
	LOGMASKED(LOG_OTHER, "%s: vt1682_2010_bk1_xscroll_7_0_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2010_bk1_xscroll_7_0_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2010_bk1_xscroll_7_0_w writing: %02x\n", machine().describe_context(), data);
	m_xscroll_7_0_bk[0] = data;
}

/*
    Address 0x2011 r/w (MAIN CPU)

    0x80 - BK1Y:7
    0x40 - BK1Y:6
    0x20 - BK1Y:5
    0x10 - BK1Y:4
    0x08 - BK1Y:3
    0x04 - BK1Y:2
    0x02 - BK1Y:1
    0x01 - BK1Y:0
*/

uint8_t vt_vt1682_state::vt1682_2011_bk1_yscoll_7_0_r()
{
	uint8_t ret = m_yscroll_7_0_bk[0];
	LOGMASKED(LOG_OTHER, "%s: vt1682_2011_bk1_yscoll_7_0_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2011_bk1_yscoll_7_0_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2011_bk1_yscoll_7_0_w writing: %02x\n", machine().describe_context(), data);
	m_yscroll_7_0_bk[0] = data;
}


/*
    Address 0x2012 r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - BK1 HCLR
    0x08 - BK1 Scroll Enable (page layout)
    0x04 - BK1 Scroll Enable (page layout)
    0x02 - BK1Y:8
    0x01 - BK1X:8
*/

uint8_t vt_vt1682_state::vt1682_2012_bk1_scroll_control_r()
{
	uint8_t ret = m_scroll_control_bk[0];
	LOGMASKED(LOG_OTHER, "%s: vt1682_2012_bk1_scroll_control_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}


void vt_vt1682_state::vt1682_2012_bk1_scroll_control_w(uint8_t data)
{

	LOGMASKED(LOG_OTHER, "%s: vt1682_2012_bk1_scroll_control_w writing: %02x (hclr: %1x page_layout:%1x ymsb:%1x xmsb:%1x)\n", machine().describe_context(), data,
		(data & 0x10) >> 4, (data & 0x0c) >> 2, (data & 0x02) >> 1, (data & 0x01) >> 0);

	m_scroll_control_bk[0] = data;
}


/*
    Address 0x2013 r/w (MAIN CPU)

    0x80 - BK1 Enable
    0x40 - BK1 Palette
    0x20 - BK1 Depth
    0x10 - BK1 Depth
    0x08 - BK1 Colour (bpp)
    0x04 - BK1 Colour (bpp)
    0x02 - BK1 Line
    0x01 - BK1 Size
*/

uint8_t vt_vt1682_state::vt1682_2013_bk1_main_control_r()
{
	uint8_t ret = m_main_control_bk[0];
	LOGMASKED(LOG_OTHER, "%s: vt1682_2013_bk1_main_control_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2013_bk1_main_control_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2013_bk1_main_control_w writing: %02x (enable:%01x palette:%01x depth:%01x bpp:%01x linemode:%01x tilesize:%01x)\n", machine().describe_context(), data,
		(data & 0x80) >> 7, (data & 0x40) >> 6, (data & 0x30) >> 4, (data & 0x0c) >> 2, (data & 0x02) >> 1, (data & 0x01) >> 0 );

	m_main_control_bk[0] = data;
}

/*
    Address 0x2014 r/w (MAIN CPU)

    0x80 - BK2X:7
    0x40 - BK2X:6
    0x20 - BK2X:5
    0x10 - BK2X:4
    0x08 - BK2X:3
    0x04 - BK2X:2
    0x02 - BK2X:1
    0x01 - BK2X:0
*/


uint8_t vt_vt1682_state::vt1682_2014_bk2_xscroll_7_0_r()
{
	uint8_t ret = m_xscroll_7_0_bk[1];
	LOGMASKED(LOG_OTHER, "%s: vt1682_2014_bk2_xscroll_7_0_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2014_bk2_xscroll_7_0_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2014_bk2_xscroll_7_0_w writing: %02x\n", machine().describe_context(), data);
	m_xscroll_7_0_bk[1] = data;
}

/*
    Address 0x2015 r/w (MAIN CPU)

    0x80 - BK2Y:7
    0x40 - BK2Y:6
    0x20 - BK2Y:5
    0x10 - BK2Y:4
    0x08 - BK2Y:3
    0x04 - BK2Y:2
    0x02 - BK2Y:1
    0x01 - BK2Y:0
*/

uint8_t vt_vt1682_state::vt1682_2015_bk2_yscoll_7_0_r()
{
	uint8_t ret = m_yscroll_7_0_bk[1];
	LOGMASKED(LOG_OTHER, "%s: vt1682_2015_bk2_yscoll_7_0_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2015_bk2_yscoll_7_0_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2015_bk2_yscoll_7_0_w writing: %02x\n", machine().describe_context(), data);
	m_yscroll_7_0_bk[1] = data;
}


/*
    Address 0x2016 r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - BK2 Scroll Enable (page layout)
    0x04 - BK2 Scroll Enable (page layout)
    0x02 - BK2Y:8
    0x01 - BK2X:8
*/

uint8_t vt_vt1682_state::vt1682_2016_bk2_scroll_control_r()
{
	uint8_t ret = m_scroll_control_bk[1];
	LOGMASKED(LOG_OTHER, "%s: vt1682_2016_bk2_scroll_control_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}


void vt_vt1682_state::vt1682_2016_bk2_scroll_control_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2016_bk2_scroll_control_w writing: %02x ((invalid): %1x page_layout:%1x ymsb:%1x xmsb:%1x)\n", machine().describe_context(), data,
		(data & 0x10) >> 4, (data & 0x0c) >> 2, (data & 0x02) >> 1, (data & 0x01) >> 0);

	m_scroll_control_bk[1] = data;
}


/*
    Address 0x2017 r/w (MAIN CPU)

    0x80 - BK2 Enable
    0x40 - BK2 Palette
    0x20 - BK2 Depth
    0x10 - BK2 Depth
    0x08 - BK2 Colour (bpp)
    0x04 - BK2 Colour (bpp)
    0x02 - (unused)
    0x01 - BK2 Size
*/

uint8_t vt_vt1682_state::vt1682_2017_bk2_main_control_r()
{
	uint8_t ret = m_main_control_bk[1];
	LOGMASKED(LOG_OTHER, "%s: vt1682_2017_bk2_main_control_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2017_bk2_main_control_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2017_bk2_main_control_w writing: %02x (enable:%01x palette:%01x depth:%01x bpp:%01x (invalid):%01x tilesize:%01x)\n", machine().describe_context(), data,
		(data & 0x80) >> 7, (data & 0x40) >> 6, (data & 0x30) >> 4, (data & 0x0c) >> 2, (data & 0x02) >> 1, (data & 0x01) >> 0 );

	m_main_control_bk[1] = data;
}


/*
    Address 0x2018 r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - SP PALSEL
    0x04 - SP ENABLE
    0x02 - SP SIZE
    0x01 - SP SIZE
*/

uint8_t vt_vt1682_state::vt1682_2018_spregs_r()
{
	uint8_t ret = m_2018_spregs;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2018_spregs_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2018_spregs_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2018_spregs_w writing: %02x\n", machine().describe_context(), data);
	m_2018_spregs = data;
}

/*
    Address 0x2019 r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - BK2 Gain (vertical zoom 0 = 1x, 1= 1x, 2= 1.5x, 3= 2x)
    0x04 - BK2 Gain
    0x02 - BK1 Gain (same but for BK1)
    0x01 - BK1 Gain
*/

uint8_t vt_vt1682_state::vt1682_2019_bkgain_r()
{
	uint8_t ret = m_2019_bkgain;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2019_bkgain_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2019_bkgain_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2019_bkgain_w writing: %02x\n", machine().describe_context(), data);
	m_2019_bkgain = data;
}


/*
    Address 0x201a r/w (MAIN CPU)

    0x80 - SP SEGMENT:7
    0x40 - SP SEGMENT:6
    0x20 - SP SEGMENT:5
    0x10 - SP SEGMENT:4
    0x08 - SP SEGMENT:3
    0x04 - SP SEGMENT:2
    0x02 - SP SEGMENT:1
    0x01 - SP SEGMENT:0
*/

uint8_t vt_vt1682_state::vt1682_201a_sp_segment_7_0_r()
{
	uint8_t ret = m_201a_sp_segment_7_0;
	LOGMASKED(LOG_OTHER, "%s: vt1682_201a_sp_segment_7_0_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_201a_sp_segment_7_0_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_201a_sp_segment_7_0_w writing: %02x\n", machine().describe_context(), data);
	m_201a_sp_segment_7_0 = data;
}

/*
    Address 0x201b r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - SP SEGMENT:11
    0x04 - SP SEGMENT:10
    0x02 - SP SEGMENT:9
    0x01 - SP SEGMENT:8
*/

uint8_t vt_vt1682_state::vt1682_201b_sp_segment_11_8_r()
{
	uint8_t ret = m_201b_sp_segment_11_8;
	LOGMASKED(LOG_OTHER, "%s: vt1682_201b_sp_segment_11_8_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_201b_sp_segment_11_8_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_201b_sp_segment_11_8_w writing: %02x\n", machine().describe_context(), data);
	m_201b_sp_segment_11_8 = data & 0x0f;
}


/*
    Address 0x201c r/w (MAIN CPU)

    0x80 - BK1 SEGMENT:7
    0x40 - BK1 SEGMENT:6
    0x20 - BK1 SEGMENT:5
    0x10 - BK1 SEGMENT:4
    0x08 - BK1 SEGMENT:3
    0x04 - BK1 SEGMENT:2
    0x02 - BK1 SEGMENT:1
    0x01 - BK1 SEGMENT:0
*/

uint8_t vt_vt1682_state::vt1682_201c_bk1_segment_7_0_r()
{
	uint8_t ret = m_segment_7_0_bk[0];
	LOGMASKED(LOG_OTHER, "%s: vt1682_201c_bk1_segment_7_0_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_201c_bk1_segment_7_0_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_201c_bk1_segment_7_0_w writing: %02x\n", machine().describe_context(), data);
	m_segment_7_0_bk[0] = data;
}

/*
    Address 0x201d r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - BK1 SEGMENT:11
    0x04 - BK1 SEGMENT:10
    0x02 - BK1 SEGMENT:9
    0x01 - BK1 SEGMENT:8
*/

uint8_t vt_vt1682_state::vt1682_201d_bk1_segment_11_8_r()
{
	uint8_t ret = m_segment_11_8_bk[0];
	LOGMASKED(LOG_OTHER, "%s: vt1682_201d_bk1_segment_11_8_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_201d_bk1_segment_11_8_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_201d_bk1_segment_11_8_w writing: %02x\n", machine().describe_context(), data);
	m_segment_11_8_bk[0] = data & 0x0f;
}


/*
    Address 0x201e r/w (MAIN CPU)

    0x80 - BK2 SEGMENT:7
    0x40 - BK2 SEGMENT:6
    0x20 - BK2 SEGMENT:5
    0x10 - BK2 SEGMENT:4
    0x08 - BK2 SEGMENT:3
    0x04 - BK2 SEGMENT:2
    0x02 - BK2 SEGMENT:1
    0x01 - BK2 SEGMENT:0
*/

uint8_t vt_vt1682_state::vt1682_201e_bk2_segment_7_0_r()
{
	uint8_t ret = m_segment_7_0_bk[1];
	LOGMASKED(LOG_OTHER, "%s: vt1682_201e_bk2_segment_7_0_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_201e_bk2_segment_7_0_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_201e_bk2_segment_7_0_w writing: %02x\n", machine().describe_context(), data);
	m_segment_7_0_bk[1] = data;
}

/*
    Address 0x201f r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - BK2 SEGMENT:11
    0x04 - BK2 SEGMENT:10
    0x02 - BK2 SEGMENT:9
    0x01 - BK2 SEGMENT:8
*/

uint8_t vt_vt1682_state::vt1682_201f_bk2_segment_11_8_r()
{
	uint8_t ret = m_segment_11_8_bk[1];
	LOGMASKED(LOG_OTHER, "%s: vt1682_201f_bk2_segment_11_8_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_201f_bk2_segment_11_8_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_201f_bk2_segment_11_8_w writing: %02x\n", machine().describe_context(), data);
	m_segment_11_8_bk[1] = data & 0x0f;
}

/*
    Address 0x2020 r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - BK2 L EN (Linescroll enable)
    0x10 - BK1 L EN (Linescroll enable)
    0x08 - Scroll Bank
    0x04 - Scroll Bank
    0x02 - Scroll Bank
    0x01 - Scroll Bank
*/

uint8_t vt_vt1682_state::vt1682_2020_bk_linescroll_r()
{
	uint8_t ret = m_2020_bk_linescroll;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2020_bk_linescroll_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2020_bk_linescroll_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2020_bk_linescroll_w writing: %02x\n", machine().describe_context(), data);
	m_2020_bk_linescroll = data;

	// cmpmx10 / cmpmx11 set a scroll bank, but don't appear to enable linescroll
	// could this be a workaround for the scroll offset issue the hardware has?
	//if (data & 0x30)
	//  popmessage("linescroll unused %01x | BK2enable %d BK1enable %d PRAM addr %04x!\n", (data & 0xc0) >> 6, (data & 0x20)>> 5, (data & 0x10) >> 4, (data & 0xf) << 8);
}

/*
    Address 0x2021 r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - Luminance_offset
    0x10 - Luminance_offset
    0x08 - Luminance_offset
    0x04 - Luminance_offset
    0x02 - Luminance_offset
    0x01 - Luminance_offset
*/

uint8_t vt_vt1682_state::vt1682_2021_lum_offset_r()
{
	uint8_t ret = m_2021_lum_offset;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2021_lum_offset_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2021_lum_offset_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2021_lum_offset_w writing: %02x\n", machine().describe_context(), data);
	m_2021_lum_offset = data;
}


/*
    Address 0x2022 r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - VCOMIO
    0x10 - RGB DAC
    0x08 - CCIR Out
    0x04 - Saturation
    0x02 - Saturation
    0x01 - Saturation
*/

uint8_t vt_vt1682_state::vt1682_2022_saturation_misc_r()
{
	uint8_t ret = m_2022_saturation_misc;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2022_saturation_misc_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2022_saturation_misc_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2022_saturation_misc_w writing: %02x\n", machine().describe_context(), data);
	m_2022_saturation_misc = data;
}

/*
    Address 0x2023 r/w (MAIN CPU)

    0x80 - Light Gun Reset
    0x40 - Light Gun Reset
    0x20 - Light Gun Reset
    0x10 - Light Gun Reset
    0x08 - Light Gun Reset
    0x04 - Light Gun Reset
    0x02 - Light Gun Reset
    0x01 - Light Gun Reset
*/

uint8_t vt_vt1682_state::vt1682_2023_lightgun_reset_r()
{
	uint8_t ret = m_2023_lightgun_reset;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2023_lightgun_reset_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2023_lightgun_reset_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2023_lightgun_reset_w writing: %02x\n", machine().describe_context(), data);
	m_2023_lightgun_reset = data;
}

/*
    Address 0x2024 r/w (MAIN CPU)

    0x80 - Light Gun 1 Y
    0x40 - Light Gun 1 Y
    0x20 - Light Gun 1 Y
    0x10 - Light Gun 1 Y
    0x08 - Light Gun 1 Y
    0x04 - Light Gun 1 Y
    0x02 - Light Gun 1 Y
    0x01 - Light Gun 1 Y
*/

uint8_t vt_vt1682_state::vt1682_2024_lightgun1_y_r()
{
	uint8_t ret = m_2024_lightgun1_y;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2024_lightgun1_y_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2024_lightgun1_y_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2024_lightgun1_y_w writing: %02x\n", machine().describe_context(), data);
	m_2024_lightgun1_y = data;
}

/*
    Address 0x2025 r/w (MAIN CPU)

    0x80 - Light Gun 1 X
    0x40 - Light Gun 1 X
    0x20 - Light Gun 1 X
    0x10 - Light Gun 1 X
    0x08 - Light Gun 1 X
    0x04 - Light Gun 1 X
    0x02 - Light Gun 1 X
    0x01 - Light Gun 1 X
*/

uint8_t vt_vt1682_state::vt1682_2025_lightgun1_x_r()
{
	uint8_t ret = m_2025_lightgun1_x;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2025_lightgun1_x_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2025_lightgun1_x_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2025_lightgun1_x_w writing: %02x\n", machine().describe_context(), data);
	m_2025_lightgun1_x = data;
}

/*
    Address 0x2026 r/w (MAIN CPU)

    0x80 - Light Gun 2 Y
    0x40 - Light Gun 2 Y
    0x20 - Light Gun 2 Y
    0x10 - Light Gun 2 Y
    0x08 - Light Gun 2 Y
    0x04 - Light Gun 2 Y
    0x02 - Light Gun 2 Y
    0x01 - Light Gun 2 Y
*/

uint8_t vt_vt1682_state::vt1682_2026_lightgun2_y_r()
{
	uint8_t ret = m_2026_lightgun2_y;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2026_lightgun2_y_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2026_lightgun2_y_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2026_lightgun2_y_w writing: %02x\n", machine().describe_context(), data);
	m_2026_lightgun2_y = data;
}


/*
    Address 0x2027 r/w (MAIN CPU)

    0x80 - Light Gun 2 X
    0x40 - Light Gun 2 X
    0x20 - Light Gun 2 X
    0x10 - Light Gun 2 X
    0x08 - Light Gun 2 X
    0x04 - Light Gun 2 X
    0x02 - Light Gun 2 X
    0x01 - Light Gun 2 X
*/

uint8_t vt_vt1682_state::vt1682_2027_lightgun2_x_r()
{
	uint8_t ret = m_2027_lightgun2_x;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2027_lightgun2_x_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2027_lightgun2_x_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2027_lightgun2_x_w writing: %02x\n", machine().describe_context(), data);
	m_2027_lightgun2_x = data;
}


/*
    Address 0x2028 r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - CCIR Y
    0x10 - CCIR Y
    0x08 - CCIR Y
    0x04 - CCIR Y
    0x02 - CCIR Y
    0x01 - CCIR Y
*/

uint8_t vt_vt1682_state::vt1682_2028_r()
{
	uint8_t ret = m_2028;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2028_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2028_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2028_w writing: %02x\n", machine().describe_context(), data);
	m_2028 = data;
}

/*
    Address 0x2029 r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - CCIR X
    0x08 - CCIR X
    0x04 - CCIR X
    0x02 - CCIR X
    0x01 - CCIR X
*/

uint8_t vt_vt1682_state::vt1682_2029_r()
{
	uint8_t ret = m_2029;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2029_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2029_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2029_w writing: %02x\n", machine().describe_context(), data);
	m_2029 = data;
}


/*
    Address 0x202a r/w (MAIN CPU)

    0x80 - VS Phase
    0x40 - HS Phase
    0x20 - YC Swap
    0x10 - CbCr Swap
    0x08 - SyncMod
    0x04 - YUV_RGB
    0x02 - Field O En
    0x01 - Field On
*/


uint8_t vt_vt1682_state::vt1682_202a_r()
{
	uint8_t ret = m_202a;
	LOGMASKED(LOG_OTHER, "%s: vt1682_202a_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_202a_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_202a_w writing: %02x\n", machine().describe_context(), data);
	m_202a = data;
}


/*
    Address 0x202b r/w (MAIN CPU)

    0x80 - R En
    0x40 - G En
    0x20 - B En
    0x10 - Halftone
    0x08 - B/W
    0x04 - CCIR Depth
    0x02 - CCIR Depth
    0x01 - CCIR Depth
*/


uint8_t vt_vt1682_state::vt1682_202b_r()
{
	uint8_t ret = m_202b;
	LOGMASKED(LOG_OTHER, "%s: vt1682_202b_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_202b_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_202b_w writing: %02x\n", machine().describe_context(), data);
	m_202b = data;
}


/* Address 0x202c Unused */
/* Address 0x202d Unused */

/*
    Address 0x202e r/w (MAIN CPU)

    0x80 - TRC EN
    0x40 - CCIR EN
    0x20 - Bluescreen EN
    0x10 - Touch EN
    0x08 - CCIR TH
    0x04 - CCIR TH
    0x02 - CCIR TH
    0x01 - CCIR TH
*/


uint8_t vt_vt1682_state::vt1682_202e_r()
{
	uint8_t ret = m_202e;
	LOGMASKED(LOG_OTHER, "%s: vt1682_202e_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_202e_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_202e_w writing: %02x\n", machine().describe_context(), data);
	m_202e = data;
}


/* Address 0x202f Unused */

/*
    Address 0x2030 r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - VDACSW
    0x20 - VDACOUT:5
    0x10 - VDACOUT:4
    0x08 - VDACOUT:3
    0x04 - VDACOUT:2
    0x02 - VDACOUT:1
    0x01 - VDACOUT:0
*/


uint8_t vt_vt1682_state::vt1682_2030_r()
{
	uint8_t ret = m_2030;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2030_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2030_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2030_w writing: %02x\n", machine().describe_context(), data);
	m_2030 = data;
}


/*
    Address 0x2031 r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - R DAC SW
    0x10 - R DAC OUT:4
    0x08 - R DAC OUT:3
    0x04 - R DAC OUT:2
    0x02 - R DAC OUT:1
    0x01 - R DAC OUT:0
*/

uint8_t vt_vt1682_state::vt1682_2031_red_dac_r()
{
	uint8_t ret = m_2031_red_dac;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2031_red_dac_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2031_red_dac_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2031_red_dac_w writing: %02x\n", machine().describe_context(), data);
	m_2031_red_dac = data;
}

/*
    Address 0x2032 r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - G DAC SW
    0x10 - G DAC OUT:4
    0x08 - G DAC OUT:3
    0x04 - G DAC OUT:2
    0x02 - G DAC OUT:1
    0x01 - G DAC OUT:0
*/

uint8_t vt_vt1682_state::vt1682_2032_green_dac_r()
{
	uint8_t ret = m_2032_green_dac;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2032_green_dac_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2032_green_dac_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2032_green_dac_w writing: %02x\n", machine().describe_context(), data);
	m_2032_green_dac = data;
}

/*
    Address 0x2033 r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - B DAC SW
    0x10 - B DAC OUT:4
    0x08 - B DAC OUT:3
    0x04 - B DAC OUT:2
    0x02 - B DAC OUT:1
    0x01 - B DAC OUT:0
*/

uint8_t vt_vt1682_state::vt1682_2033_blue_dac_r()
{
	uint8_t ret = m_2033_blue_dac;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2033_blue_dac_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2033_blue_dac_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2033_blue_dac_w writing: %02x\n", machine().describe_context(), data);
	m_2033_blue_dac = data;
}


/************************************************************************************************************************************
 VT1682 Sys Registers
************************************************************************************************************************************/

/*
    Address 0x2100 r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - Program Bank 1 Register 3
    0x04 - Program Bank 1 Register 3
    0x02 - Program Bank 1 Register 3
    0x01 - Program Bank 1 Register 3
*/

uint8_t vt_vt1682_state::vt1682_2100_prgbank1_r3_r()
{
	uint8_t ret = m_2100_prgbank1_r3;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2100_prgbank1_r3_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2100_prgbank1_r3_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2100_prgbank1_r3_w writing: %02x (4-bits)\n", machine().describe_context(), data);
	m_2100_prgbank1_r3 = data;
	update_banks();
}

/* Address 0x2101 - 0x2104 (MAIN CPU) - see vt1682_timer.cpp */

/*
    Address 0x2105 WRITE ONLY (MAIN CPU)

    0x80 - (unused)
    0x40 - COMR6
    0x20 - TV SYS SE:1
    0x10 - TV SYS SE:0
    0x08 - CCIR SEL
    0x04 - Double
    0x02 - ROM SEL
    0x01 - PRAM

    TV Mode settings 0 = NTSC, 1 = PAL M, 2 = PAL N, 3 = PAL
    see clocks near machine_config

    ROM SEL is which CPU the internal ROM maps to (if used)  0 = Main CPU, 1 = Sound CPU

*/

void vt_vt1682_state::vt1682_2105_comr6_tvmodes_w(uint8_t data)
{
	// COMR6 is used for banking
	LOGMASKED(LOG_OTHER, "%s: vt1682_2105_comr6_tvmodes_w writing: %02x\n", machine().describe_context(), data);
	m_2105_vt1682_2105_comr6_tvmodes = data;
	update_banks();
}


/*
    Address 0x2106 r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - SCPU RN (Sound CPU Reset Line Control)
    0x10 - SCPU ON (Sound CPU Enable)
    0x08 - SPI ON
    0x04 - UART ON
    0x02 - TV ON (TV display encoder enable)
    0x01 - LCD ON (LCD display controller enable)
*/

uint8_t vt_vt1682_state::vt1682_2106_enable_regs_r()
{
	uint8_t ret = m_2106_enable_reg;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2106_enable_regs_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::clear_sound_reset_line()
{
	m_soundcpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
	m_scpu_is_in_reset = 0;
}

void vt_vt1682_state::vt1682_2106_enable_regs_w(uint8_t data)
{
	// COMR6 is used for banking
	LOGMASKED(LOG_OTHER, "%s: vt1682_2106_enable_regs_w writing: %02x (scpurn:%1x scpuon:%1x spion:%1x uarton:%1x tvon:%1x lcdon:%1x)\n", machine().describe_context(), data,
		BIT(data, 5), BIT(data, 4), BIT(data, 3), BIT(data, 2), BIT(data, 1), BIT(data, 0));
	m_2106_enable_reg = data;

	if (BIT(data, 5))
	{
		if (m_scpu_is_in_reset)
			clear_sound_reset_line();
	}
	else
	{
		m_soundcpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
		m_scpu_is_in_reset = 1;
	}
}


/*
    Address 0x2107 r/w (MAIN CPU)

    0x80 - Program Bank 0 Register 0
    0x40 - Program Bank 0 Register 0
    0x20 - Program Bank 0 Register 0
    0x10 - Program Bank 0 Register 0
    0x08 - Program Bank 0 Register 0
    0x04 - Program Bank 0 Register 0
    0x02 - Program Bank 0 Register 0
    0x01 - Program Bank 0 Register 0
*/

uint8_t vt_vt1682_state::vt1682_2107_prgbank0_r0_r()
{
	uint8_t ret = m_2107_prgbank0_r0;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2107_prgbank0_r0_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2107_prgbank0_r0_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2107_prgbank0_r0_w writing: %02x\n", machine().describe_context(), data);
	m_2107_prgbank0_r0 = data;
	update_banks();
}

/*
    Address 0x2108 r/w (MAIN CPU)

    0x80 - Program Bank 0 Register 1
    0x40 - Program Bank 0 Register 1
    0x20 - Program Bank 0 Register 1
    0x10 - Program Bank 0 Register 1
    0x08 - Program Bank 0 Register 1
    0x04 - Program Bank 0 Register 1
    0x02 - Program Bank 0 Register 1
    0x01 - Program Bank 0 Register 1
*/

uint8_t vt_vt1682_state::vt1682_2108_prgbank0_r1_r()
{
	uint8_t ret = m_2108_prgbank0_r1;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2108_prgbank0_r1_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2108_prgbank0_r1_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2108_prgbank0_r1_w writing: %02x\n", machine().describe_context(), data);
	m_2108_prgbank0_r1 = data;
	update_banks();
}


/*
    Address 0x2109 r/w (MAIN CPU)

    0x80 - Program Bank 0 Register 2
    0x40 - Program Bank 0 Register 2
    0x20 - Program Bank 0 Register 2
    0x10 - Program Bank 0 Register 2
    0x08 - Program Bank 0 Register 2
    0x04 - Program Bank 0 Register 2
    0x02 - Program Bank 0 Register 2
    0x01 - Program Bank 0 Register 2
*/


uint8_t vt_vt1682_state::vt1682_2109_prgbank0_r2_r()
{
	uint8_t ret = m_2109_prgbank0_r2;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2109_prgbank0_r2_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2109_prgbank0_r2_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2109_prgbank0_r2_w writing: %02x\n", machine().describe_context(), data);
	m_2109_prgbank0_r2 = data;
	update_banks();
}

/*
    Address 0x210a r/w (MAIN CPU)

    0x80 - Program Bank 0 Register 3
    0x40 - Program Bank 0 Register 3
    0x20 - Program Bank 0 Register 3
    0x10 - Program Bank 0 Register 3
    0x08 - Program Bank 0 Register 3
    0x04 - Program Bank 0 Register 3
    0x02 - Program Bank 0 Register 3
    0x01 - Program Bank 0 Register 3
*/

uint8_t vt_vt1682_state::vt1682_210a_prgbank0_r3_r()
{
	uint8_t ret = m_210a_prgbank0_r3;
	LOGMASKED(LOG_OTHER, "%s: vt1682_210a_prgbank0_r3_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_210a_prgbank0_r3_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_210a_prgbank0_r3_w writing: %02x\n", machine().describe_context(), data);
	m_210a_prgbank0_r3 = data;
	update_banks();
}

/*
    Address 0x210b r/w (MAIN CPU)

    0x80 - TSYSN En (Timer Clock Select)
    0x40 - PQ2 Enable
    0x20 - BUS Tristate
    0x10 - CS Control:1
    0x08 - CS Control:0
    0x04 - Program Bank 0 Select
    0x02 - Program Bank 0 Select
    0x01 - Program Bank 0 Select
*/

uint8_t vt_vt1682_state::vt1682_210b_misc_cs_prg0_bank_sel_r()
{
	uint8_t ret = m_210b_misc_cs_prg0_bank_sel;
	LOGMASKED(LOG_OTHER, "%s: vt1682_210b_misc_cs_prg0_bank_sel_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_210b_misc_cs_prg0_bank_sel_w(uint8_t data)
{
	// PQ2 Enable is also used for ROM banking along with Program Bank 0 select
	uint32_t clock = m_maincpu->clock();

	LOGMASKED(LOG_OTHER, "%s: vt1682_210b_misc_cs_prg0_bank_sel_w writing: %02x\n", machine().describe_context(), data);
	m_210b_misc_cs_prg0_bank_sel = data;

	if (data & 0x80)
	{
		if (clock == 21477272/4)
			m_system_timer_dev->set_clock(TIMER_ALT_SPEED_NTSC);
		else if (clock == 26601712/5)
			m_system_timer_dev->set_clock(TIMER_ALT_SPEED_PAL);
		else
			logerror("setting alt timings with unknown main CPU frequency %d\n", clock);
	}
	else
	{
		if (clock == 21477272/4)
			m_system_timer_dev->set_clock(MAIN_CPU_CLOCK_NTSC);
		else if (clock == 26601712/5)
			m_system_timer_dev->set_clock(MAIN_CPU_CLOCK_PAL);
		else
			logerror("setting alt timings with unknown main CPU frequency %d\n", clock);
	}

	update_banks();
}


/*
    Address 0x210c r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - Program Bank 1 Register 2
    0x04 - Program Bank 1 Register 2
    0x02 - Program Bank 1 Register 2
    0x01 - Program Bank 1 Register 2
*/

uint8_t vt_vt1682_state::vt1682_210c_prgbank1_r2_r()
{
	uint8_t ret = m_210c_prgbank1_r2;
	LOGMASKED(LOG_OTHER, "%s: vt1682_210c_prgbank1_r2_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_210c_prgbank1_r2_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_210c_prgbank1_r2_w writing: %02x (4-bits)\n", machine().describe_context(), data);
	m_210c_prgbank1_r2 = data;
	update_banks();
}


/* 0x210d - see vt1682_io.cpp */
/* 0x210e - see vt1682_io.cpp */
/* 0x210f - see vt1682_io.cpp */


/*
   Address 0x2110 READ (MAIN CPU)

    0x80 - Program Bank 0 Register 4
    0x40 - Program Bank 0 Register 4
    0x20 - Program Bank 0 Register 4
    0x10 - Program Bank 0 Register 4
    0x08 - Program Bank 0 Register 4
    0x04 - Program Bank 0 Register 4
    0x02 - Program Bank 0 Register 4
    0x01 - Program Bank 0 Register 4

    Address 0x2110 WRITE (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - Program Bank 1 Register 0
    0x04 - Program Bank 1 Register 0
    0x02 - Program Bank 1 Register 0
    0x01 - Program Bank 1 Register 0
*/

uint8_t vt_vt1682_state::vt1682_prgbank0_r4_r()
{
	uint8_t ret = m_prgbank0_r4;
	LOGMASKED(LOG_OTHER, "%s: (2110) vt1682_prgbank0_r4_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_prgbank1_r0_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: (2110) vt1682_prgbank1_r0_w writing: %02x (4-bits)\n", machine().describe_context(), data);
	m_prgbank1_r0 = data;
	update_banks();
}

/*
   Address 0x2111 READ (MAIN CPU)

    0x80 - Program Bank 0 Register 5
    0x40 - Program Bank 0 Register 5
    0x20 - Program Bank 0 Register 5
    0x10 - Program Bank 0 Register 5
    0x08 - Program Bank 0 Register 5
    0x04 - Program Bank 0 Register 5
    0x02 - Program Bank 0 Register 5
    0x01 - Program Bank 0 Register 5

    Address 0x2111 WRITE (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - Program Bank 1 Register 1
    0x04 - Program Bank 1 Register 1
    0x02 - Program Bank 1 Register 1
    0x01 - Program Bank 1 Register 1
*/

uint8_t vt_vt1682_state::vt1682_prgbank0_r5_r()
{
	uint8_t ret = m_prgbank0_r5;
	LOGMASKED(LOG_OTHER, "%s: (2111) vt1682_prgbank0_r5_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}


void vt_vt1682_state::vt1682_prgbank1_r1_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: (2111) vt1682_prgbank1_r1_w writing: %02x (4-bits)\n", machine().describe_context(), data);
	m_prgbank1_r1 = data;
	update_banks();
}


/*
    Address 0x2112 READ (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - Program Bank 1 Register 0
    0x04 - Program Bank 1 Register 0
    0x02 - Program Bank 1 Register 0
    0x01 - Program Bank 1 Register 0

    Address 0x2112 WRITE (MAIN CPU)

    0x80 - Program Bank 0 Register 4
    0x40 - Program Bank 0 Register 4
    0x20 - Program Bank 0 Register 4
    0x10 - Program Bank 0 Register 4
    0x08 - Program Bank 0 Register 4
    0x04 - Program Bank 0 Register 4
    0x02 - Program Bank 0 Register 4
    0x01 - Program Bank 0 Register 4
*/

uint8_t vt_vt1682_state::vt1682_prgbank1_r0_r()
{
	uint8_t ret = m_prgbank1_r0;
	LOGMASKED(LOG_OTHER, "%s: (2112) vt1682_prgbank1_r0_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}


void vt_vt1682_state::vt1682_prgbank0_r4_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: (2112) vt1682_prgbank0_r4_w writing: %02x (8-bits)\n", machine().describe_context(), data);
	m_prgbank0_r4 = data;
	update_banks();
}


/*
    Address 0x2113 READ (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - Program Bank 1 Register 1
    0x04 - Program Bank 1 Register 1
    0x02 - Program Bank 1 Register 1
    0x01 - Program Bank 1 Register 1

    Address 0x2113 WRITE (MAIN CPU)

    0x80 - Program Bank 0 Register 5
    0x40 - Program Bank 0 Register 5
    0x20 - Program Bank 0 Register 5
    0x10 - Program Bank 0 Register 5
    0x08 - Program Bank 0 Register 5
    0x04 - Program Bank 0 Register 5
    0x02 - Program Bank 0 Register 5
    0x01 - Program Bank 0 Register 5
*/

uint8_t vt_vt1682_state::vt1682_prgbank1_r1_r()
{
	uint8_t ret = m_prgbank1_r1;
	LOGMASKED(LOG_OTHER, "%s: (2113) vt1682_prgbank1_r1_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_prgbank0_r5_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: (2113) vt1682_prgbank0_r5_w writing: %02x (8-bits)\n", machine().describe_context(), data);
	m_prgbank0_r5 = data;
	update_banks();
}

/*
    Address 0x2114 r/w (MAIN CPU)

    0x80 - Baud Rate:7
    0x40 - Baud Rate:6
    0x20 - Baud Rate:5
    0x10 - Baud Rate:4
    0x08 - Baud Rate:3
    0x04 - Baud Rate:2
    0x02 - Baud Rate:1
    0x01 - Baud Rate:0
*/

/*
    Address 0x2115 r/w (MAIN CPU)

    0x80 - Baud Rate:15
    0x40 - Baud Rate:14
    0x20 - Baud Rate:13
    0x10 - Baud Rate:12
    0x08 - Baud Rate:11
    0x04 - Baud Rate:10
    0x02 - Baud Rate:9
    0x01 - Baud Rate:8
*/

/*
    Address 0x2116 r/w (MAIN CPU)

    0x80 - 16bit SPI
    0x40 - SPIEN
    0x20 - SPI RST
    0x10 - M/SB
    0x08 - CLK PHASE
    0x04 - CLK POLARITY
    0x02 - CLK FREQ:1
    0x01 - CLK FREQ:0
*/

/*
    Address 0x2117 WRITE (MAIN CPU)

    0x80 - SPI TX Data
    0x40 - SPI TX Data
    0x20 - SPI TX Data
    0x10 - SPI TX Data
    0x08 - SPI TX Data
    0x04 - SPI TX Data
    0x02 - SPI TX Data
    0x01 - SPI TX Data

    Address 0x2117 READ (MAIN CPU)

    0x80 - SPI RX Data
    0x40 - SPI RX Data
    0x20 - SPI RX Data
    0x10 - SPI RX Data
    0x08 - SPI RX Data
    0x04 - SPI RX Data
    0x02 - SPI RX Data
    0x01 - SPI RX Data
*/

/*
    Address 0x2118 r/w (MAIN CPU)

    0x80 - Program Bank 1 Register 5
    0x40 - Program Bank 1 Register 5
    0x20 - Program Bank 1 Register 5
    0x10 - Program Bank 1 Register 5
    0x08 - Program Bank 1 Register 4
    0x04 - Program Bank 1 Register 4
    0x02 - Program Bank 1 Register 4
    0x01 - Program Bank 1 Register 4
*/

uint8_t vt_vt1682_state::vt1682_2118_prgbank1_r4_r5_r()
{
	uint8_t ret = m_2118_prgbank1_r4_r5;
	LOGMASKED(LOG_OTHER, "%s: vt1682_2118_prgbank1_r4_r5_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2118_prgbank1_r4_r5_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_2118_prgbank1_r4_r5_w writing: %02x (2x 4-bits)\n", machine().describe_context(), data);
	m_2118_prgbank1_r4_r5 = data;
	update_banks();
}


/*
    Address 0x2119 WRITE ONLY (MAIN CPU)

    0x80 - (unused)
    0x40 - Carrier En
    0x20 - UART En
    0x10 - Tx IRQ En
    0x08 - Rx IRQ En
    0x04 - Parity En
    0x02 - Odd/Even
    0x01 - 9bit Mode
*/

/*
    Address 0x211a WRITE (MAIN CPU)

    0x80 - TX Data
    0x40 - TX Data
    0x20 - TX Data
    0x10 - TX Data
    0x08 - TX Data
    0x04 - TX Data
    0x02 - TX Data
    0x01 - TX Data

    Address 0x211a READ (MAIN CPU)

    0x80 - RX Data
    0x40 - RX Data
    0x20 - RX Data
    0x10 - RX Data
    0x08 - RX Data
    0x04 - RX Data
    0x02 - RX Data
    0x01 - RX Data
*/

/*
    Address 0x211b WRITE (MAIN CPU)

    0x80 - Carrier Freq
    0x40 - Carrier Freq
    0x20 - Carrier Freq
    0x10 - Carrier Freq
    0x08 - Carrier Freq
    0x04 - Carrier Freq
    0x02 - Carrier Freq
    0x01 - Carrier Freq

    Address 0x211b READ (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - Rx Error
    0x10 - Tx Status
    0x08 - Rx Status
    0x04 - Parity Error
    0x02 - (unused)
    0x01 - (unused)
*/

/*
    Address 0x211c WRITE (MAIN CPU)

    0x80 - AutoWake
    0x40 - KeyWake
    0x20 - EXT2421EN
    0x10 - SCPUIRQ
    0x08 - SLEEPM
    0x04 - (unused)
    0x02 - SLEEP SEL
    0x01 - CLK SEL

    Address 0x211c READ (MAIN CPU)

    0x80 - Clear_SCPU_IRQ
    0x40 - Clear_SCPU_IRQ
    0x20 - Clear_SCPU_IRQ
    0x10 - Clear_SCPU_IRQ
    0x08 - Clear_SCPU_IRQ
    0x04 - Clear_SCPU_IRQ
    0x02 - Clear_SCPU_IRQ
    0x01 - Clear_SCPU_IRQ
*/

void vt_vt1682_state::vt1682_211c_regs_ext2421_w(uint8_t data)
{
	// EXT2421EN is used for ROM banking
	LOGMASKED(LOG_OTHER, "%s: vt1682_211c_regs_ext2421_w writing: %02x\n", machine().describe_context(), data);
	m_211c_regs_ext2421 = data;
	update_banks();

	if (data & 0x10)
	{
		// not seen used
		logerror("Sound CPU IRQ Request\n");
	}
}


/*
    Address 0x211d WRITE (MAIN CPU)

    0x80 - LVDEN
    0x40 - LVDS1
    0x20 - LVDS0
    0x10 - VDAC_EN
    0x08 - ADAC_EN
    0x04 - PLL_EN
    0x02 - LCDACEN
    0x01 - (unused)

    Address 0x211d READ (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - (unused)
    0x04 - (unused)
    0x02 - (unused)
    0x01 - LVD
*/

/*
    Address 0x211f r/w (MAIN CPU)

    0x80 - VGCEN
    0x40 - VGCA6
    0x20 - VGCA5
    0x10 - VGCA4
    0x08 - VGCA3
    0x04 - VGCA2
    0x02 - VGCA1
    0x01 - VGCA0
*/

/*
    Address 0x2120 r/w (MAIN CPU)

    0x80 - Sleep Period
    0x40 - Sleep Period
    0x20 - Sleep Period
    0x10 - Sleep Period
    0x08 - Sleep Period
    0x04 - Sleep Period
    0x02 - Sleep Period
    0x01 - Sleep Period
*/

/*
    Address 0x2121 READ (MAIN CPU) (maybe)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - SPI MSK
    0x08 - UART MSK
    0x04 - SPU MSK
    0x02 - TMR MSK
    0x01 - Ext MSK

    Address 0x2121 WRITE (MAIN CPU) (maybe)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - (unused)
    0x04 - Clear SPU
    0x02 - (unused)
    0x01 - Clear Ext
*/

/*
    Address 0x2122 r/w (MAIN CPU)

    0x80 - DMA DT ADDR:7
    0x40 - DMA DT ADDR:6
    0x20 - DMA DT ADDR:5
    0x10 - DMA DT ADDR:4
    0x08 - DMA DT ADDR:3
    0x04 - DMA DT ADDR:2
    0x02 - DMA DT ADDR:1
    0x01 - DMA DT ADDR:0
*/

uint8_t vt_vt1682_state::vt1682_2122_dma_dt_addr_7_0_r()
{
	uint8_t ret = m_2122_dma_dt_addr_7_0;
	LOGMASKED(LOG_DMA, "%s: vt1682_2122_dma_dt_addr_7_0_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2122_dma_dt_addr_7_0_w(uint8_t data)
{
	LOGMASKED(LOG_DMA, "%s: vt1682_2122_dma_dt_addr_7_0_w writing: %02x\n", machine().describe_context(), data);
	m_2122_dma_dt_addr_7_0 = data;
}


/*
    Address 0x2123 r/w (MAIN CPU)

    0x80 - DMA DT ADDR:15
    0x40 - DMA DT ADDR:14
    0x20 - DMA DT ADDR:13
    0x10 - DMA DT ADDR:12
    0x08 - DMA DT ADDR:11
    0x04 - DMA DT ADDR:10
    0x02 - DMA DT ADDR:9
    0x01 - DMA DT ADDR:8
*/

uint8_t vt_vt1682_state::vt1682_2123_dma_dt_addr_15_8_r()
{
	uint8_t ret = m_2123_dma_dt_addr_15_8;
	LOGMASKED(LOG_DMA, "%s: vt1682_2123_dma_dt_addr_15_8_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2123_dma_dt_addr_15_8_w(uint8_t data)
{
	LOGMASKED(LOG_DMA, "%s: vt1682_2123_dma_dt_addr_15_8_w writing: %02x\n", machine().describe_context(), data);
	m_2123_dma_dt_addr_15_8 = data;
}


/*
    Address 0x2124 r/w (MAIN CPU)

    0x80 - DMA SR ADDR:7
    0x40 - DMA SR ADDR:6
    0x20 - DMA SR ADDR:5
    0x10 - DMA SR ADDR:4
    0x08 - DMA SR ADDR:3
    0x04 - DMA SR ADDR:2
    0x02 - DMA SR ADDR:1
    0x01 - DMA SR ADDR:0
*/

uint8_t vt_vt1682_state::vt1682_2124_dma_sr_addr_7_0_r()
{
	uint8_t ret = m_2124_dma_sr_addr_7_0;
	LOGMASKED(LOG_DMA, "%s: vt1682_2124_dma_sr_addr_7_0_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2124_dma_sr_addr_7_0_w(uint8_t data)
{
	LOGMASKED(LOG_DMA, "%s: vt1682_2124_dma_sr_addr_7_0_w writing: %02x\n", machine().describe_context(), data);
	m_2124_dma_sr_addr_7_0 = data;
}


/*
    Address 0x2125 r/w (MAIN CPU)

    0x80 - DMA SR ADDR:15
    0x40 - DMA SR ADDR:14
    0x20 - DMA SR ADDR:13
    0x10 - DMA SR ADDR:12
    0x08 - DMA SR ADDR:11
    0x04 - DMA SR ADDR:10
    0x02 - DMA SR ADDR:9
    0x01 - DMA SR ADDR:8
*/

uint8_t vt_vt1682_state::vt1682_2125_dma_sr_addr_15_8_r()
{
	uint8_t ret = m_2125_dma_sr_addr_15_8;
	LOGMASKED(LOG_DMA, "%s: vt1682_2125_dma_sr_addr_15_8_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2125_dma_sr_addr_15_8_w(uint8_t data)
{
	LOGMASKED(LOG_DMA, "%s: vt1682_2125_dma_sr_addr_15_8_w writing: %02x\n", machine().describe_context(), data);
	m_2125_dma_sr_addr_15_8 = data;
}



/*
    Address 0x2126 r/w (MAIN CPU)

    0x80 - DMA SR BANK:22
    0x40 - DMA SR BANK:21
    0x20 - DMA SR BANK:20
    0x10 - DMA SR BANK:19
    0x08 - DMA SR BANK:18
    0x04 - DMA SR BANK:17
    0x02 - DMA SR BANK:16
    0x01 - DMA SR BANK:15
*/

uint8_t vt_vt1682_state::vt1682_2126_dma_sr_bank_addr_22_15_r()
{
	uint8_t ret = m_2126_dma_sr_bank_addr_22_15;
	LOGMASKED(LOG_DMA, "%s: vt1682_2126_dma_sr_bank_addr_22_15_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2126_dma_sr_bank_addr_22_15_w(uint8_t data)
{
	LOGMASKED(LOG_DMA, "%s: vt1682_2126_dma_sr_bank_addr_22_15_w writing: %02x\n", machine().describe_context(), data);
	m_2126_dma_sr_bank_addr_22_15 = data;
}

/*
    Address 0x2127 WRITE (MAIN CPU)

    0x80 - DMA Number
    0x40 - DMA Number
    0x20 - DMA Number
    0x10 - DMA Number
    0x08 - DMA Number
    0x04 - DMA Number
    0x02 - DMA Number
    0x01 - DMA Number

    Address 0x2127 READ (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - (unused)
    0x04 - (unused)
    0x02 - (unused)
    0x01 - DMA Status
*/

uint8_t vt_vt1682_state::vt1682_2127_dma_status_r()
{
	uint8_t ret = 0x00;

	int dma_status = 0; // 1 would be 'busy'
	ret |= dma_status;

	LOGMASKED(LOG_DMA, "%s: vt1682_2127_dma_status_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::do_dma_external_to_internal(int data, bool is_video)
{
	int count = data * 2;
	if (count == 0)
		count = 0x200;

	int srcbank = get_dma_sr_bank_ddr();
	int srcaddr = get_dma_sr_addr();
	uint16_t dstaddr = get_dma_dt_addr();

	if (is_video)
	{
		if (get_dma_dt_addr() == 0x2004)
			LOGMASKED(LOG_DMA, "Doing DMA, External to Internal (VRAM/SRAM) src: %08x dest: %04x length: %03x - spriteram addr %04x\n", srcaddr | srcbank << 15, dstaddr, count, get_spriteram_addr());
		else
			LOGMASKED(LOG_DMA, "Doing DMA, External to Internal (VRAM/SRAM) src: %08x dest: %04x length: %03x - vram addr %04x\n", srcaddr | srcbank << 15, dstaddr, count, get_vram_addr());
	}
	else
		LOGMASKED(LOG_DMA, "Doing DMA, External to Internal src: %08x dest: %04x length: %03x\n", srcaddr | srcbank<<15, dstaddr, count);

	for (int i = 0; i < count; i++)
	{
		srcaddr = get_dma_sr_addr();
		dstaddr = get_dma_dt_addr();
		uint8_t dat = m_fullrom->read8(srcaddr | srcbank<<15);
		srcaddr++;

		address_space &mem = m_maincpu->space(AS_PROGRAM);
		mem.write_byte(dstaddr, dat);

		if (!is_video)
			dstaddr++;

		// update registers
		set_dma_dt_addr(dstaddr);
		set_dma_sr_addr(srcaddr);
	}
}

void vt_vt1682_state::do_dma_internal_to_internal(int data, bool is_video)
{
	int count = data * 2;
	if (count == 0)
		count = 0x200;

	int srcaddr = get_dma_sr_addr();
	uint16_t dstaddr = get_dma_dt_addr();

	if (is_video)
	{
		if (get_dma_dt_addr() == 0x2004)
			LOGMASKED(LOG_DMA, "Doing DMA, Internal to Internal (VRAM/SRAM) src: %04x dest: %04x length: %03x - spriteram addr %04x\n", srcaddr, dstaddr, count, get_spriteram_addr());
		else
			LOGMASKED(LOG_DMA, "Doing DMA, Internal to Internal (VRAM/SRAM) src: %04x dest: %04x length: %03x - vram addr %04x\n", srcaddr, dstaddr, count, get_vram_addr());
	}
	else
		LOGMASKED(LOG_DMA, "Doing DMA, Internal to Internal src: %04x dest: %04x length: %03x\n", srcaddr, dstaddr, count);

	for (int i = 0; i < count; i++)
	{
		address_space &mem = m_maincpu->space(AS_PROGRAM);
		dstaddr = get_dma_dt_addr();

		srcaddr = get_dma_sr_addr();
		uint8_t dat = mem.read_byte(srcaddr);
		srcaddr++;

		mem.write_byte(dstaddr, dat);

		if (!is_video)
			dstaddr++;

		// update registers
		set_dma_dt_addr(dstaddr);
		set_dma_sr_addr(srcaddr);
	}
}



void vt_vt1682_state::vt1682_2127_dma_size_trigger_w(uint8_t data)
{
	LOGMASKED(LOG_DMA, "%s: vt1682_2127_dma_size_trigger_w writing: %02x\n", machine().describe_context(), data);

	// hw waits until VBLANK before actually doing the DMA! (TODO)

	if (get_dma_sr_isext())
	{
		if (get_dma_dt_isext())
		{
			// Source External
			// Dest External
			LOGMASKED(LOG_DMA, "Invalid DMA, both Source and Dest are 'External'\n");
			return;
		}
		else
		{
			// Source External
			// Dest Internal

			uint16_t dstaddr = get_dma_dt_addr();
			int srcaddr = get_dma_sr_addr();

			if ((srcaddr & 1) || ((dstaddr & 1) && (!get_dma_dt_is_video())) )
			{
				LOGMASKED(LOG_DMA, "Invalid DMA, low bit of address set\n");
				return;
			}


			do_dma_external_to_internal(data, get_dma_dt_is_video());

			return;
		}
	}
	else
	{
		if (get_dma_dt_isext())
		{
			// this is only likely if there is RAM in the usual ROM space

			// Source Internal
			// Dest External
			int dstbank = get_dma_sr_bank_ddr();
			int dstaddr = get_dma_dt_addr() | (dstbank << 15);
			uint16_t srcaddr = get_dma_sr_addr();

			if ((srcaddr & 1) || (dstaddr & 1))
			{
				LOGMASKED(LOG_DMA, "Invalid DMA, low bit of address set\n");
				return;
			}

			LOGMASKED(LOG_DMA, "Unhandled DMA, Dest is 'External'\n");
			return;
		}
		else
		{
			// Source Internal
			// Dest Internal

			uint16_t srcaddr = get_dma_sr_addr();
			uint16_t dstaddr = get_dma_dt_addr();

			if ((srcaddr & 1) || ((dstaddr & 1) && (!get_dma_dt_is_video())) )
			{
				LOGMASKED(LOG_DMA, "Invalid DMA, low bit of address set\n");
				return;
			}

			do_dma_internal_to_internal(data, get_dma_dt_is_video());
			return;
		}
	}
}

/*
    Address 0x2128 r/w (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - (unused)
    0x04 - (unused)
    0x02 - DMA SR BANK:24
    0x01 - DMA SR BANK:23
*/

uint8_t vt_vt1682_state::vt1682_2128_dma_sr_bank_addr_24_23_r()
{
	uint8_t ret = m_2128_dma_sr_bank_addr_24_23;
	LOGMASKED(LOG_DMA, "%s: vt1682_2128_dma_sr_bank_addr_24_23_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_2128_dma_sr_bank_addr_24_23_w(uint8_t data)
{
	LOGMASKED(LOG_DMA, "%s: vt1682_2128_dma_sr_bank_addr_24_23_w writing: %02x\n", machine().describe_context(), data);
	m_2128_dma_sr_bank_addr_24_23 = data & 0x03;
}


/*
    Address 0x2129 READ (MAIN CPU)

    0x80 - UIOA DATA IN / Send Joy CLK
    0x40 - UIOA DATA IN / Send Joy CLK
    0x20 - UIOA DATA IN / Send Joy CLK
    0x10 - UIOA DATA IN / Send Joy CLK
    0x08 - UIOA DATA IN / Send Joy CLK
    0x04 - UIOA DATA IN / Send Joy CLK
    0x02 - UIOA DATA IN / Send Joy CLK
    0x01 - UIOA DATA IN / Send Joy CLK

    Address 0x2129 WRITE (MAIN CPU)

    0x80 - UIOA DATA OUT
    0x40 - UIOA DATA OUT
    0x20 - UIOA DATA OUT
    0x10 - UIOA DATA OUT
    0x08 - UIOA DATA OUT
    0x04 - UIOA DATA OUT
    0x02 - UIOA DATA OUT
    0x01 - UIOA DATA OUT

*/

/*
    Address 0x212a READ (MAIN CPU)

    0x80 - Send Joy CLK 2
    0x40 - Send Joy CLK 2
    0x20 - Send Joy CLK 2
    0x10 - Send Joy CLK 2
    0x08 - Send Joy CLK 2
    0x04 - Send Joy CLK 2
    0x02 - Send Joy CLK 2
    0x01 - Send Joy CLK 2

    Address 0x212a WRITE (MAIN CPU)

    0x80 - UIOA DIRECTION
    0x40 - UIOA DIRECTION
    0x20 - UIOA DIRECTION
    0x10 - UIOA DIRECTION
    0x08 - UIOA DIRECTION
    0x04 - UIOA DIRECTION
    0x02 - UIOA DIRECTION
    0x01 - UIOA DIRECTION
*/

void vt_vt1682_state::clock_joy2()
{
}

uint8_t vt_vt1682_state::inteact_212a_send_joy_clock2_r()
{
	uint8_t ret = m_uio->inteact_212a_uio_a_direction_r();
	clock_joy2();
	return ret;
}

/*
    Address 0x212b r/w (MAIN CPU)

    0x80 - UIOA ATTRIBUTE
    0x40 - UIOA ATTRIBUTE
    0x20 - UIOA ATTRIBUTE
    0x10 - UIOA ATTRIBUTE
    0x08 - UIOA ATTRIBUTE
    0x04 - UIOA ATTRIBUTE
    0x02 - UIOA ATTRIBUTE
    0x01 - UIOA ATTRIBUTE
*/

/*
    Address 0x212c READ (MAIN CPU)

    0x80 - Pseudo Random Number
    0x40 - Pseudo Random Number
    0x20 - Pseudo Random Number
    0x10 - Pseudo Random Number
    0x08 - Pseudo Random Number
    0x04 - Pseudo Random Number
    0x02 - Pseudo Random Number
    0x01 - Pseudo Random Number

    Address 0x212c WRITE (MAIN CPU)

    0x80 - Pseudo Random Number Seed
    0x40 - Pseudo Random Number Seed
    0x20 - Pseudo Random Number Seed
    0x10 - Pseudo Random Number Seed
    0x08 - Pseudo Random Number Seed
    0x04 - Pseudo Random Number Seed
    0x02 - Pseudo Random Number Seed
    0x01 - Pseudo Random Number Seed
*/

uint8_t vt_vt1682_state::vt1682_212c_prng_r()
{
	uint8_t ret = machine().rand();
	LOGMASKED(LOG_OTHER, "%s: vt1682_212c_prng_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_212c_prng_seed_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: vt1682_212c_prng_seed_w writing: %02x\n", machine().describe_context(), data);
	// don't know the algorithm
}


/*
    Address 0x212d WRITE ONLY (MAIN CPU)

    0x80 - PLL B
    0x40 - PLL B
    0x20 - PLL B
    0x10 - PLL B
    0x08 - PLL M
    0x04 - PLL A
    0x02 - PLL A
    0x01 - PLL A
*/

/* Address 0x212e Unused */
/* Address 0x212f Unused */

/* Address 0x2130 - 0x2137 - see v1682_alu.cpp */

/* Address 0x2138 Unused */
/* Address 0x2139 Unused */
/* Address 0x213a Unused */
/* Address 0x213b Unused */
/* Address 0x213c Unused */
/* Address 0x213d Unused */
/* Address 0x213e Unused */
/* Address 0x213f Unused */

/*
    Address 0x2140 r/w (MAIN CPU)

    0x80 - I2C ID
    0x40 - I2C ID
    0x20 - I2C ID
    0x10 - I2C ID
    0x08 - I2C ID
    0x04 - I2C ID
    0x02 - I2C ID
    0x01 - I2C ID
*/

/*
    Address 0x2141 r/w (MAIN CPU)

    0x80 - I2C ADDR
    0x40 - I2C ADDR
    0x20 - I2C ADDR
    0x10 - I2C ADDR
    0x08 - I2C ADDR
    0x04 - I2C ADDR
    0x02 - I2C ADDR
    0x01 - I2C ADDR
*/

/*
    Address 0x2142 r/w (MAIN CPU)

    0x80 - I2C DATA
    0x40 - I2C DATA
    0x20 - I2C DATA
    0x10 - I2C DATA
    0x08 - I2C DATA
    0x04 - I2C DATA
    0x02 - I2C DATA
    0x01 - I2C DATA
*/

/*
    Address 0x2143 WRITE ONLY (MAIN CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - (unused)
    0x04 - (unused)
    0x02 - I2C CLK SELECT
    0x01 - I2C CLK SELECT
*/

/* Address 0x2144 Unused */
/* Address 0x2145 Unused */
/* Address 0x2146 Unused */
/* Address 0x2147 Unused */

/*
    Address 0x2148 WRITE ONLY (MAIN CPU)

    0x80 - UIOB SEL:7
    0x40 - UIOB SEL:6
    0x20 - UIOB SEL:5
    0x10 - UIOB SEL:4
    0x08 - UIOB SEL:3
    0x04 - (unused)
    0x02 - UIOA MODE
    0x01 - UIOA MODE
*/

/*
    Address 0x2149 WRITE (MAIN CPU)

    0x80 - UIOB DATA OUT
    0x40 - UIOB DATA OUT
    0x20 - UIOB DATA OUT
    0x10 - UIOB DATA OUT
    0x08 - UIOB DATA OUT
    0x04 - UIOB DATA OUT
    0x02 - UIOB DATA OUT
    0x01 - UIOB DATA OUT

    Address 0x2149 READ (MAIN CPU)

    0x80 - UIOB DATA IN
    0x40 - UIOB DATA IN
    0x20 - UIOB DATA IN
    0x10 - UIOB DATA IN
    0x08 - UIOB DATA IN
    0x04 - UIOB DATA IN
    0x02 - UIOB DATA IN
    0x01 - UIOB DATA IN
*/

/*
    Address 0x214a r/w (MAIN CPU)

    0x80 - UIOB DIRECTION
    0x40 - UIOB DIRECTION
    0x20 - UIOB DIRECTION
    0x10 - UIOB DIRECTION
    0x08 - UIOB DIRECTION
    0x04 - UIOB DIRECTION
    0x02 - UIOB DIRECTION
    0x01 - UIOB DIRECTION
*/

/*
    Address 0x214b r/w (MAIN CPU)

    0x80 - UIOB ATTRIBUTE
    0x40 - UIOB ATTRIBUTE
    0x20 - UIOB ATTRIBUTE
    0x10 - UIOB ATTRIBUTE
    0x08 - UIOB ATTRIBUTE
    0x04 - UIOB ATTRIBUTE
    0x02 - UIOB ATTRIBUTE
    0x01 - UIOB ATTRIBUTE
*/


/************************************************************************************************************************************
 VT1682 Sound CPU Registers
************************************************************************************************************************************/

/* Address 0x2100 - 0x2103 (SOUND CPU) - see vt1682_timer.cpp */

/* Address 0x2104 Unused (SOUND CPU) */
/* Address 0x2105 Unused (SOUND CPU) */
/* Address 0x2106 Unused (SOUND CPU) */
/* Address 0x2107 Unused (SOUND CPU) */
/* Address 0x2108 Unused (SOUND CPU) */
/* Address 0x2109 Unused (SOUND CPU) */
/* Address 0x210a Unused (SOUND CPU) */
/* Address 0x210b Unused (SOUND CPU) */
/* Address 0x210c Unused (SOUND CPU) */
/* Address 0x210d Unused (SOUND CPU) */
/* Address 0x210e Unused (SOUND CPU) */
/* Address 0x210f Unused (SOUND CPU) */

/* Address 0x2110 - 0x2113 (SOUND CPU) - see vt1682_timer.cpp */

/* Address 0x2114 Unused (SOUND CPU) */
/* Address 0x2115 Unused (SOUND CPU) */
/* Address 0x2116 Unused (SOUND CPU) */
/* Address 0x2117 Unused (SOUND CPU) */

/*
    Address 0x2118 r/w (SOUND CPU)

    0x80 - Audio DAC Left:7
    0x40 - Audio DAC Left:6
    0x20 - Audio DAC Left:5
    0x10 - Audio DAC Left:4
    0x08 - Audio DAC Left:3
    0x04 - Audio DAC Left:2
    0x02 - Audio DAC Left:1
    0x01 - Audio DAC Left:0

    actually 12 bits precision so only 15 to 4 are used
*/

uint8_t vt_vt1682_state::vt1682_soundcpu_2118_dacleft_7_0_r()
{
	uint8_t ret = m_soundcpu_2118_dacleft_7_0;
	//LOGMASKED(LOG_OTHER, "%s: vt1682_soundcpu_2118_dacleft_7_0_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_soundcpu_2118_dacleft_7_0_w(uint8_t data)
{
	//LOGMASKED(LOG_OTHER, "%s: vt1682_soundcpu_2118_dacleft_7_0_r writing: %02x\n", machine().describe_context(), data);
	m_soundcpu_2118_dacleft_7_0 = data;
}

/*
    Address 0x2119 r/w (SOUND CPU)

    0x80 - Audio DAC Left:15
    0x40 - Audio DAC Left:14
    0x20 - Audio DAC Left:13
    0x10 - Audio DAC Left:12
    0x08 - Audio DAC Left:11
    0x04 - Audio DAC Left:10
    0x02 - Audio DAC Left:9
    0x01 - Audio DAC Left:8
*/

uint8_t vt_vt1682_state::vt1682_soundcpu_2119_dacleft_15_8_r()
{
	uint8_t ret = m_soundcpu_2119_dacleft_15_8;
	//LOGMASKED(LOG_OTHER, "%s: vt1682_soundcpu_2119_dacleft_15_8_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_soundcpu_2119_dacleft_15_8_w(uint8_t data)
{
	//LOGMASKED(LOG_OTHER, "%s: vt1682_soundcpu_2119_dacleft_15_8_r writing: %02x\n", machine().describe_context(), data);
	m_soundcpu_2119_dacleft_15_8 = data;

	uint16_t dacdata = (m_soundcpu_2119_dacleft_15_8 << 8) | m_soundcpu_2118_dacleft_7_0;
	m_leftdac->write(dacdata >> 4);
}

/*
    Address 0x211a r/w (SOUND CPU)

    0x80 - Audio DAC Right:7
    0x40 - Audio DAC Right:6
    0x20 - Audio DAC Right:5
    0x10 - Audio DAC Right:4
    0x08 - Audio DAC Right:3
    0x04 - Audio DAC Right:2
    0x02 - Audio DAC Right:1
    0x01 - Audio DAC Right:0
*/

uint8_t vt_vt1682_state::vt1682_soundcpu_211a_dacright_7_0_r()
{
	uint8_t ret = m_soundcpu_211a_dacright_7_0;
	//LOGMASKED(LOG_OTHER, "%s: vt1682_soundcpu_211a_dacright_7_0_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_soundcpu_211a_dacright_7_0_w(uint8_t data)
{
	//LOGMASKED(LOG_OTHER, "%s: vt1682_soundcpu_211a_dacright_7_0_r writing: %02x\n", machine().describe_context(), data);
	m_soundcpu_211a_dacright_7_0 = data;
}

/*
    Address 0x211b r/w (SOUND CPU)

    0x80 - Audio DAC Right:15
    0x40 - Audio DAC Right:14
    0x20 - Audio DAC Right:13
    0x10 - Audio DAC Right:12
    0x08 - Audio DAC Right:11
    0x04 - Audio DAC Right:10
    0x02 - Audio DAC Right:9
    0x01 - Audio DAC Right:8
*/

uint8_t vt_vt1682_state::vt1682_soundcpu_211b_dacright_15_8_r()
{
	uint8_t ret = m_soundcpu_211b_dacright_15_8;
	//LOGMASKED(LOG_OTHER, "%s: vt1682_soundcpu_211b_dacright_15_8_r returning: %02x\n", machine().describe_context(), ret);
	return ret;
}

void vt_vt1682_state::vt1682_soundcpu_211b_dacright_15_8_w(uint8_t data)
{
	//LOGMASKED(LOG_OTHER, "%s: vt1682_soundcpu_211b_dacright_15_8_r writing: %02x\n", machine().describe_context(), data);
	m_soundcpu_211b_dacright_15_8 = data;

	uint16_t dacdata = (m_soundcpu_211b_dacright_15_8 << 8) | m_soundcpu_211a_dacright_7_0;
	m_rightdac->write(dacdata >> 4);
}


/*
    Address 0x211c WRITE (SOUND CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - IRQ_OUT
    0x08 - SLEEP
    0x04 - ExtIRQSel
    0x02 - NMI_WAKEUP_EN
    0x01 - ExtMask

    Address 0x211c READ (SOUND CPU)

    0x80 - Clear_CPU_IRQ
    0x40 - Clear_CPU_IRQ
    0x20 - Clear_CPU_IRQ
    0x10 - Clear_CPU_IRQ
    0x08 - Clear_CPU_IRQ
    0x04 - Clear_CPU_IRQ
    0x02 - Clear_CPU_IRQ
    0x01 - Clear_CPU_IRQ
*/

void vt_vt1682_state::vt1682_soundcpu_211c_reg_irqctrl_w(uint8_t data)
{
	// EXT2421EN is used for ROM banking
	LOGMASKED(LOG_OTHER, "%s: vt1682_soundcpu_211c_reg_irqctrl_w writing: %02x\n", machine().describe_context(), data);

	if (data & 0x10)
	{
		m_scu_to_main_irq_active = 1;
		update_main_interrupts();
	//  logerror("Main CPU IRQ Request from Sound CPU\n");
	}


	if (data & 0x08)
	{
		// documentation indicates that Sleep mode is buggy, so this probably never gets used
		popmessage("SCU Sleep\n");
	}
}

/*
    Address 0x211d r/w (SOUND CPU)

    0x80 - (unused)
    0x40 - (unused)
    0x20 - (unused)
    0x10 - (unused)
    0x08 - (unused)
    0x04 - (unused)
    0x02 - IIS Mode
    0x01 - IIS EN
*/

/* Address 0x211E Unused? (maybe) (SOUND CPU) */
/* Address 0x211F Unused (SOUND CPU) */
/* Address 0x2120 Unused (SOUND CPU) */
/* Address 0x2121 Unused (SOUND CPU) */
/* Address 0x2122 Unused (SOUND CPU) */
/* Address 0x2123 Unused (SOUND CPU) */
/* Address 0x2124 Unused (SOUND CPU) */
/* Address 0x2125 Unused (SOUND CPU) */
/* Address 0x2126 Unused (SOUND CPU) */
/* Address 0x2127 Unused (SOUND CPU) */
/* Address 0x2128 Unused (SOUND CPU) */
/* Address 0x2129 Unused (SOUND CPU) */
/* Address 0x212a Unused (SOUND CPU) */
/* Address 0x212b Unused (SOUND CPU) */
/* Address 0x212c Unused (SOUND CPU) */
/* Address 0x212d Unused (SOUND CPU) */
/* Address 0x212e Unused (SOUND CPU) */
/* Address 0x212f Unused (SOUND CPU) */

/* Address 0x2130 - 0x2137 - see v1682_alu.cpp (device identical to main CPU device) */

/* Address 0x2138 Unused (SOUND CPU) */
/* Address 0x2139 Unused (SOUND CPU) */
/* Address 0x213a Unused (SOUND CPU) */
/* Address 0x213b Unused (SOUND CPU) */
/* Address 0x213c Unused (SOUND CPU) */
/* Address 0x213d Unused (SOUND CPU) */
/* Address 0x213e Unused (SOUND CPU) */
/* Address 0x213f Unused (SOUND CPU) */

/*
    Address 0x2140 r/w (SOUND CPU)

    0x80 - IOA DATA
    0x40 - IOA DATA
    0x20 - IOA DATA
    0x10 - IOA DATA
    0x08 - IOA DATA
    0x04 - IOA DATA
    0x02 - IOA DATA
    0x01 - IOA DATA
*/

/*
    Address 0x2141 r/w (SOUND CPU)

    0x80 - IOA DIR
    0x40 - IOA DIR
    0x20 - IOA DIR
    0x10 - IOA DIR
    0x08 - IOA DIR
    0x04 - IOA DIR
    0x02 - IOA DIR
    0x01 - IOA DIR
*/

/*
    Address 0x2142 r/w (SOUND CPU)

    0x80 - IOA PLH
    0x40 - IOA PLH
    0x20 - IOA PLH
    0x10 - IOA PLH
    0x08 - IOA PLH
    0x04 - IOA PLH
    0x02 - IOA PLH
    0x01 - IOA PLH
*/

/* Address 0x2143 Unused (SOUND CPU) */

/*
    Address 0x2144 r/w (SOUND CPU)

    0x80 - IOB DATA
    0x40 - IOB DATA
    0x20 - IOB DATA
    0x10 - IOB DATA
    0x08 - IOB DATA
    0x04 - IOB DATA
    0x02 - IOB DATA
    0x01 - IOB DATA
*/

/*
    Address 0x2145 r/w (SOUND CPU)

    0x80 - IOB DIR
    0x40 - IOB DIR
    0x20 - IOB DIR
    0x10 - IOB DIR
    0x08 - IOB DIR
    0x04 - IOB DIR
    0x02 - IOB DIR
    0x01 - IOB DIR
*/

/*
    Address 0x2146 r/w (SOUND CPU)

    0x80 - IOB PLH
    0x40 - IOB PLH
    0x20 - IOB PLH
    0x10 - IOB PLH
    0x08 - IOB PLH
    0x04 - IOB PLH
    0x02 - IOB PLH
    0x01 - IOB PLH
*/

void vt_vt1682_state::draw_tile_pixline(int segment, int tile, int tileline, int x, int y, int palselect, int pal, int is16pix_high, int is16pix_wide, int bpp, int depth, int opaque, int flipx, int flipy, const rectangle& cliprect)
{
	int tilesize_high = is16pix_high ? 16 : 8;

	if (y >= cliprect.min_y && y <= cliprect.max_y)
	{

		if (bpp == 3) pal = 0x0;
		if (bpp == 2) pal &= 0xc;

		int startaddress = segment;
		int linebytes;

		if (bpp == 3)
		{
			if (is16pix_wide)
			{
				linebytes = 16;
			}
			else
			{
				linebytes = 8;
			}
		}
		else if (bpp == 2)
		{
			if (is16pix_wide)
			{
				linebytes = 12;
			}
			else
			{
				linebytes = 6;
			}
		}
		else //if (bpp == 1) // or 0
		{
			if (is16pix_wide)
			{
				linebytes = 8;
			}
			else
			{
				linebytes = 4;
			}
		}
		int tilesize_wide = is16pix_wide ? 16 : 8;

		int tilebytes = linebytes * tilesize_high;

		startaddress += tilebytes * tile;

		int currentaddress;

		if (!flipy)
			currentaddress = startaddress + tileline * linebytes;
		else
			currentaddress = startaddress + ((tilesize_high - 1) - tileline) * linebytes;

		uint8_t *const pri2ptr = &m_pal2_priority_bitmap.pix(y);
		uint8_t *const pri1ptr = &m_pal1_priority_bitmap.pix(y);

		uint8_t *const pix2ptr = &m_pal2_pix_bitmap.pix(y);
		uint8_t *const pix1ptr = &m_pal1_pix_bitmap.pix(y);


		int shift_amount, mask, bytes_in;
		if (bpp == 3) // (8bpp)
		{
			shift_amount = 8;
			mask = 0xff;
			bytes_in = 4;
		}
		else if (bpp == 2) // (6bpp)
		{
			shift_amount = 6;
			mask = 0x3f;
			bytes_in = 3;
		}
		else // 1 / 0 (4bpp)
		{
			shift_amount = 4;
			mask = 0x0f;
			bytes_in = 2;
		}

		int xbase = x;

		for (int xx = 0; xx < tilesize_wide; xx += 4) // tile x pixels
		{
			// draw 4 pixels
			uint32_t pixdata = 0;

			int shift = 0;
			for (int i = 0; i < bytes_in; i++)
			{
				pixdata |= m_fullrom->read8(currentaddress) << shift; currentaddress++;
				shift += 8;
			}

			shift = 0;
			for (int ii = 0; ii < 4; ii++)
			{
				uint8_t pen = (pixdata >> shift)& mask;
				if (opaque || pen)
				{
					int xdraw_real;
					if (!flipx)
						xdraw_real = xbase + xx + ii; // pixel position
					else
						xdraw_real = xbase + ((tilesize_wide - 1) - xx - ii);

					if (xdraw_real >= cliprect.min_x && xdraw_real <= cliprect.max_x)
					{
						if (palselect & 1)
						{
							if (depth < pri1ptr[xdraw_real])
							{
								pix1ptr[xdraw_real] = pen | (pal << 4);
								pri1ptr[xdraw_real] = depth;
							}
						}
						if (palselect & 2)
						{
							if (depth < pri2ptr[xdraw_real])
							{
								pix2ptr[xdraw_real] = pen | (pal << 4);
								pri2ptr[xdraw_real] = depth;
							}
						}

					}
				}
				shift += shift_amount;
			}
		}
	}
}

void vt_vt1682_state::setup_video_pages(int which, int tilesize, int vs, int hs, int y8, int x8, uint16_t* pagebases)
{
	int vs_hs = (vs << 1) | hs;
	int y8_x8 = (y8 << 1) | x8;

	pagebases[0] = 0xffff;
	pagebases[1] = 0xffff;
	pagebases[2] = 0xffff;
	pagebases[3] = 0xffff;

	if (!tilesize) // 8x8 mode
	{
		if (vs_hs == 0)
		{
			// 1x1 mode
			switch (y8_x8)
			{
			case 0x0:
				pagebases[0] = 0x000; /* 0x000-0x7ff */
				break;
			case 0x1:
				pagebases[0] = 0x800; /* 0x800-0xfff */
				break;
			case 0x2:
				pagebases[0] = 0x800; /* 0x800-0xfff */ // technically invalid?
				break;
			case 0x3:
				pagebases[0] = 0x800; /* 0x800-0xfff */ // technically invalid (but set by a number of games in cmpmx10 / cmpmx11 eg. Go Go Go)
				break;
			}

			// mirror for rendering
			pagebases[1] = pagebases[0];
			pagebases[2] = pagebases[0];
			pagebases[3] = pagebases[0];
		}
		else if (vs_hs == 1)
		{
			// 2x1 mode
			switch (y8_x8)
			{
			case 0x0:
				pagebases[0] = 0x000; /* 0x000-0x7ff */ pagebases[1] = 0x800; /* 0x800-0xfff */
				break;
			case 0x1:
				pagebases[0] = 0x800; /* 0x800-0xfff */ pagebases[1] = 0x000; /* 0x000-0x7ff */
				break;
			case 0x2:
				pagebases[0] = 0x000; /* 0x000-0x7ff */ pagebases[1] = 0x800; /* 0x800-0xfff */
				break;
			case 0x3:
				pagebases[0] = 0x800; /* 0x800-0xfff */ pagebases[1] = 0x000; /* 0x000-0x7ff */
				break;
			}

			// mirror for rendering
			pagebases[2] = pagebases[0];
			pagebases[3] = pagebases[1];
		}
		else if (vs_hs == 2)
		{
			// 1x2 mode
			switch (y8_x8)
			{
			case 0x0:
				pagebases[0] = 0x000; /* 0x000-0x7ff */
				pagebases[2] = 0x800; /* 0x800-0xfff */
				break;
			case 0x1:
				pagebases[0] = 0x000; /* 0x000-0x7ff */
				pagebases[2] = 0x800; /* 0x800-0xfff */
				break;
			case 0x2:
				pagebases[0] = 0x800; /* 0x800-0xfff */
				pagebases[2] = 0x000; /* 0x000-0x7ff */
				break;
			case 0x3:
				pagebases[0] = 0x800; /* 0x800-0xfff */
				pagebases[2] = 0x000; /* 0x000-0x7ff */
				break;
			}

			// mirror for rendering
			pagebases[1] = pagebases[0];
			pagebases[3] = pagebases[2];
		}
		else if (vs_hs == 3)
		{
			// 2x2 mode

			// 4 pages in 8x8 is an INVALID MODE, set all bases to 0?
			pagebases[0] = 0x000;
			pagebases[1] = 0x000;
			pagebases[2] = 0x000;
			pagebases[3] = 0x000;
		}
	}
	else // 16x16 mode
	{
		if (vs_hs == 0)
		{
			// 1x1 mode
			switch (y8_x8)
			{
			case 0x0:
				pagebases[0] = 0x000; /* 0x000 - 0x1ff */
				break;
			case 0x1:
				pagebases[0] = 0x200; /* 0x200 - 0x3ff */
				break;
			case 0x2:
				pagebases[0] = 0x400; /* 0x400 - 0x5ff */
				break;
			case 0x3:
				pagebases[0] = 0x600; /* 0x600 - 0x7ff */
				break;
			}

			// mirror for rendering
			pagebases[1] = pagebases[0];
			pagebases[2] = pagebases[0];
			pagebases[3] = pagebases[0];
		}
		else if (vs_hs == 1)
		{
			// 2x1 mode
			switch (y8_x8)
			{
			case 0x0:
				pagebases[0] = 0x000; /* 0x000 - 0x1ff */ pagebases[1] = 0x200; /* 0x200 - 0x3ff */
				break;
			case 0x1:
				pagebases[0] = 0x200; /* 0x200 - 0x3ff */ pagebases[1] = 0x000; /* 0x000 - 0x1ff */
				break;
			case 0x2:
				pagebases[0] = 0x000; /* 0x000 - 0x1ff */ pagebases[1] = 0x200; /* 0x200 - 0x3ff */
				break;
			case 0x3:
				pagebases[0] = 0x200; /* 0x200 - 0x3ff */ pagebases[1] = 0x000; /* 0x000 - 0x1ff */
				break;
			}

			// mirror for rendering
			pagebases[2] = pagebases[0];
			pagebases[3] = pagebases[1];
		}
		else if (vs_hs == 2)
		{
			// 1x2 mode
			switch (y8_x8)
			{
			case 0x0:
				pagebases[0] = 0x000; /* 0x000 - 0x1ff */
				pagebases[2] = 0x200; /* 0x200 - 0x3ff */
				break;
			case 0x1:
				pagebases[0] = 0x000; /* 0x000 - 0x1ff */
				pagebases[2] = 0x200; /* 0x200 - 0x3ff */
				break;
			case 0x2:
				pagebases[0] = 0x200; /* 0x200 - 0x3ff */
				pagebases[2] = 0x000; /* 0x000 - 0x1ff */
				break;
			case 0x3:
				pagebases[0] = 0x200; /* 0x200 - 0x3ff */
				pagebases[2] = 0x000; /* 0x000 - 0x1ff */
				break;
			}

			// mirror for rendering
			pagebases[1] = pagebases[0];
			pagebases[3] = pagebases[2];
		}
		else if (vs_hs == 3)
		{
			// 2x2 mode
			switch (y8_x8)
			{
			case 0x0:
				pagebases[0] = 0x000; /* 0x000 - 0x1ff */ pagebases[1] = 0x200; /* 0x200 - 0x3ff */
				pagebases[2] = 0x400; /* 0x400 - 0x5ff */ pagebases[3] = 0x600; /* 0x600 - 0x7ff */
				break;
			case 0x1:
				pagebases[0] = 0x200; /* 0x200 - 0x3ff */ pagebases[1] = 0x000; /* 0x000 - 0x1ff */
				pagebases[2] = 0x600; /* 0x600 - 0x7ff */ pagebases[3] = 0x400; /* 0x400 - 0x5ff */
				break;
			case 0x2:
				pagebases[0] = 0x400; /* 0x400 - 0x5ff */ pagebases[1] = 0x600; /* 0x600 - 0x7ff */
				pagebases[2] = 0x000; /* 0x000 - 0x1ff */ pagebases[3] = 0x200; /* 0x200 - 0x3ff */
				break;
			case 0x3:
				pagebases[0] = 0x600; /* 0x600 - 0x7ff */ pagebases[1] = 0x400; /* 0x400 - 0x5ff */
				pagebases[2] = 0x200; /* 0x200 - 0x3ff */ pagebases[3] = 0x000; /* 0x000 - 0x1ff */
				break;
			}
		}
	}

	// for BK2 layer, in 16x16 mode, all tilebases are 0x800 higher
	if (tilesize && (which == 1))
	{
		pagebases[0] += 0x800;
		pagebases[1] += 0x800;
		pagebases[2] += 0x800;
		pagebases[3] += 0x800;
	}

	/*
	if ((pagebases[0] == 0xffff) || (pagebases[1] == 0xffff) || (pagebases[2] == 0xffff) || (pagebases[3] == 0xffff))
	{
	    fatalerror("failed to set config for tilemap:%1x, size:%1x vs:%1x hs:%1x y8:%1x x8:%1x", which, tilesize, vs, hs, y8, x8);
	}
	*/
}

int vt_vt1682_state::get_address_for_tilepos(int x, int y, int tilesize, uint16_t* pagebases)
{
	if (!tilesize) // 8x8 mode
	{
		// in 8x8 mode each page is 32 tiles wide and 32 tiles high
		// the pagebases structure is for 2x2 pages, so 64 tiles in each direction, pre-mirrored for smaller sizes
		// each page is 0x800 bytes
		// 0x40 bytes per line (0x2 bytes per tile, 0x20 tiles)

		x &= 0x3f;
		y &= 0x3f;

		if (x & 0x20) // right set of pages
		{
			x &= 0x1f;
			if (y & 0x20)// bottom set of pages
			{
				y &= 0x1f;
				return pagebases[3] + (y * 0x20 * 0x02) + (x * 0x2);
			}
			else // top set of pages
			{
				y &= 0x1f;
				return pagebases[1] + (y * 0x20 * 0x02) + (x * 0x2);
			}
		}
		else // left set of pages
		{
			x &= 0x1f;
			if (y & 0x20)// bottom set of pages
			{
				y &= 0x1f;
				return pagebases[2] + (y * 0x20 * 0x02) + (x * 0x2);
			}
			else // top set of pages
			{
				y &= 0x1f;
				return pagebases[0] + (y * 0x20 * 0x02) + (x * 0x2);
			}
		}
	}
	else // 16x16 mode
	{
		// in 16x16 mode each page is 16 tiles wide and 16 tiles high
		// the pagebases structure is for 2x2 pages, so 32 tiles in each direction, pre-mirrored for smaller sizes
		// each page is 0x100 bytes
		// 0x10 bytes per line (0x2 bytes per tile, 0x10 tiles)
		x &= 0x1f;
		y &= 0x1f;

		if (x & 0x10) // right set of pages
		{
			x &= 0x0f;
			if (y & 0x10)// bottom set of pages
			{
				y &= 0x0f;
				return pagebases[3] + (y * 0x10 * 0x02) + (x * 0x2);
			}
			else // top set of pages
			{
				y &= 0x0f;
				return pagebases[1] + (y * 0x10 * 0x02) + (x * 0x2);
			}
		}
		else // left set of pages
		{
			x &= 0x0f;
			if (y & 0x10)// bottom set of pages
			{
				y &= 0x0f;
				return pagebases[2] + (y * 0x10 * 0x02) + (x * 0x2);
			}
			else // top set of pages
			{
				y &= 0x0f;
				return pagebases[0] + (y * 0x10 * 0x02) + (x * 0x2);
			}
		}
	}
	// should never get here
	return 0x00;
}

/*
    Page Setups

    8x8 Mode  (Note, BK2 RAM arrangements are the same as BK1 in 8x8 mode)

    ---------------------------------------------------------------------------------------------------------------------------------
    |   Bk1 Reg |   Bk1 Reg |   Layout                              |   Bk2 Reg |   Bk2 Reg |                                       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |   Vs  Hs  |   Y8  X8  |   resulting config                    |   Vs  Hs  |   Y8  X8  |   resulting config                    |
    ---------------------------------------------------------------------------------------------------------------------------------
    |   0   0   |   0   0   |   0x000 - 0x7ff                       |   0   0   |   0   0   |   0x000 - 0x7ff                       |
    |           |           |                                       |           |           |                                       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   0   1   |   0x800 - 0x800                       |           |   0   1   |   0x800 - 0x800                       |
    |           |           |                                       |           |           |                                       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   1   0   |   0x800 - 0x800                       |           |   1   0   |   0x800 - 0x800                       |
    |           |           |   (technically invalid?)              |           |           |   (technically invalid?)              |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   1   1   |   0x800 - 0x800                       |           |   1   1   |   0x800 - 0x800                       |
    |           |           |   (technically invalid?)              |           |           |   (technically invalid?)              |
    =================================================================================================================================
    |   0   1   |   0   0   |   0x000 - 0x7ff   0x800 - 0xfff       |   0   1   |   0   0   |   0x000 - 0x7ff   0x800 - 0xfff       |
    |           |           |                                       |           |           |                                       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   0   1   |   0x800 - 0xfff   0x000 - 0xfff       |           |   0   1   |   0x800 - 0xfff   0x000 - 0xfff       |
    |           |           |                                       |           |           |                                       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   1   0   |   0x000 - 0x7ff   0x800 - 0xfff       |           |   1   0   |   0x000 - 0x7ff   0x800 - 0xfff       |
    |           |           |                                       |           |           |                                       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   1   1   |   0x800 - 0xfff   0x000 - 0xfff       |           |   1   1   |   0x800 - 0xfff   0x000 - 0xfff       |
    |           |           |                                       |           |           |                                       |
    =================================================================================================================================
    |   1   0   |   0   0   |   0x000 - 0x7ff                       |   1   0   |   0   0   |   0x000 - 0x7ff                       |
    |           |           |   0x800 - 0xfff                       |           |           |   0x800 - 0xfff                       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   0   1   |   0x000 - 0x7ff                       |           |   0   1   |   0x000 - 0x7ff                       |
    |           |           |   0x800 - 0xfff                       |           |           |   0x800 - 0xfff                       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   1   0   |   0x800 - 0xfff                       |           |   1   0   |   0x800 - 0xfff                       |
    |           |           |   0x000 - 0x7ff                       |           |           |   0x000 - 0x7ff                       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   1   1   |   0x800 - 0xfff                       |           |   1   1   |   0x800 - 0xfff                       |
    |           |           |   0x000 - 0x7ff                       |           |           |   0x000 - 0x7ff                       |
    =================================================================================================================================
    |   1   1   |   0   0   |   Invalid (each page is 0x800 bytes,  |   1   1   |   0   0   |   Invalid (each page is 0x800 bytes,  |
    |           |           |    so not enough RAM for 4 pages)     |           |           |    so not enough RAM for 4 pages)     |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   0   1   |   Invalid (each page is 0x800 bytes,  |           |   0   1   |   Invalid (each page is 0x800 bytes,  |
    |           |           |    so not enough RAM for 4 pages)     |           |           |    so not enough RAM for 4 pages)     |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   1   0   |   Invalid (each page is 0x800 bytes,  |           |   1   0   |   Invalid (each page is 0x800 bytes,  |
    |           |           |    so not enough RAM for 4 pages)     |           |           |    so not enough RAM for 4 pages)     |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   1   1   |   Invalid (each page is 0x800 bytes,  |           |   1   1   |   Invalid (each page is 0x800 bytes,  |
    |           |           |    so not enough RAM for 4 pages)     |           |           |    so not enough RAM for 4 pages)     |
    =================================================================================================================================

    16x16 Mode  (Note, BK2 RAM base is different, with 0x800 added, compared to BK1 in 16x16 mode)

    ---------------------------------------------------------------------------------------------------------------------------------
    |   Bk1 Reg |   Bk1 Reg |   Layout                              |   Bk2 Reg |   Bk2 Reg |                                       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |   Vs  Hs  |   Y8  X8  |   resulting config                    |   Vs  Hs  |   Y8  X8  |   resulting config                    |
    ---------------------------------------------------------------------------------------------------------------------------------
    |   0   0   |   0   0   |   0x000 - 0x1ff                       |   0   0   |   0   0   |   0x800 - 0x9ff                       |
    |           |           |                                       |           |           |                                       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   0   1   |   0x200 - 0x3ff                       |           |   0   1   |   0xa00 - 0xbff                       |
    |           |           |                                       |           |           |                                       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   1   0   |   0x400 - 0x5ff                       |           |   1   0   |   0xc00 - 0xdff                       |
    |           |           |                                       |           |           |                                       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   1   1   |   0x600 - 0x7ff                       |           |   1   1   |   0xe00 - 0xfff                       |
    |           |           |                                       |           |           |                                       |
    =================================================================================================================================
    |   0   1   |   0   0   |   0x000 - 0x1ff   0x200 - 0x3ff       |   0   1   |   0   0   |   0x800 - 0x9ff   0xa00 - 0xbff       |
    |           |           |                                       |           |           |                                       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   0   1   |   0x200 - 0x3ff   0x000 - 0x1ff       |           |   0   1   |   0xa00 - 0xbff   0x800 - 0x9ff       |
    |           |           |                                       |           |           |                                       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   1   0   |   0x000 - 0x1ff   0x200 - 0x3ff       |           |   1   0   |   0x800 - 0x9ff   0xa00 - 0xbff       |
    |           |           |                                       |           |           |                                       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   1   1   |   0x200 - 0x3ff   0x000 - 0x1ff       |           |   1   1   |   0xa00 - 0xbff   0x800 - 0x9ff       |
    |           |           |                                       |           |           |                                       |
    =================================================================================================================================
    |   1   0   |   0   0   |   0x000 - 0x1ff                       |   1   0   |   0   0   |   0x800 - 0x9ff                       |
    |           |           |   0x200 - 0x3ff                       |           |           |   0xa00 - 0xbff                       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   0   1   |   0x000 - 0x1ff                       |           |   0   1   |   0x800 - 0x9ff                       |
    |           |           |   0x200 - 0x3ff                       |           |           |   0xa00 - 0xbff                       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   1   0   |   0x200 - 0x3ff                       |           |   1   0   |   0xa00 - 0xbff                       |
    |           |           |   0x000 - 0x1ff                       |           |           |   0x800 - 0x9ff                       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   1   1   |   0x200 - 0x3ff                       |           |   1   1   |   0xa00 - 0xbff                       |
    |           |           |   0x000 - 0x1ff                       |           |           |   0x800 - 0x9ff                       |
    =================================================================================================================================
    |   1   1   |   0   0   |   0x000 - 0x1ff   0x200 - 0x3ff       |   1   1   |   0   0   |   0x800 - 0x9ff   0xa00 - 0xbff       |
    |           |           |   0x400 - 0x5ff   0x600 - 0x7ff       |           |           |   0xc00 - 0xdff   0xe00 - 0xfff       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   0   1   |   0x200 - 0x3ff   0x000 - 0x1ff       |           |   0   1   |   0xa00 - 0xbff   0x800 - 0x9ff       |
    |           |           |   0x600 - 0x7ff   0x400 - 0x5ff       |           |           |   0xe00 - 0xfff   0xc00 - 0xdff       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   1   0   |   0x400 - 0x5ff   0x600 - 0x7ff       |           |   1   0   |   0xc00 - 0xdff   0xe00 - 0xfff       |
    |           |           |   0x000 - 0x1ff   0x200 - 0x3ff       |           |           |   0x800 - 0x9ff   0xa00 - 0xbff       |
    ---------------------------------------------------------------------------------------------------------------------------------
    |           |   1   1   |   0x600 - 0x7ff   0x400 - 0x5ff       |           |   1   1   |   0xe00 - 0xfff   0xc00 - 0xdff       |
    |           |           |   0x200 - 0x3ff   0x000 - 0x1ff       |           |           |   0xa00 - 0xbff   0x800 - 0x9ff       |
    =================================================================================================================================
*/

void vt_vt1682_state::draw_layer(int which, int opaque, const rectangle& cliprect)
{
	int bk_tilesize = (m_main_control_bk[which] & 0x01);
	int bk_line = (m_main_control_bk[which] & 0x02) >> 1;
	int bk_tilebpp = (m_main_control_bk[which] & 0x0c) >> 2;
	int bk_depth = (m_main_control_bk[which] & 0x30) >> 4;
	int bk_paldepth_mode = (m_main_control_bk[which] & 0x40) >> 6; // called bkpal in places, bk_pal_select in others (in conflict with palselect below) needs to be ignored for cmpmx10
	int bk_enable = (m_main_control_bk[which] & 0x80) >> 7;

	if (bk_enable)
	{
		int yscroll = m_yscroll_7_0_bk[which];
		int yscrollmsb = (m_scroll_control_bk[which] & 0x02) >> 1;
		int page_layout_h = (m_scroll_control_bk[which] & 0x04) >> 2;
		int page_layout_v = (m_scroll_control_bk[which] & 0x08) >> 3;
		int high_color = (m_scroll_control_bk[which] & 0x10) >> 4;

		int segment = m_segment_7_0_bk[which];
		segment |= m_segment_11_8_bk[which] << 8;

		segment = segment * 0x2000;

		//xscroll |= xscrollmsb << 8;
		//yscroll |= yscrollmsb << 8;

		uint16_t bases[4];

		if (!bk_line)
		{
			int palselect;
			if (which == 0) palselect = m_200f_bk_pal_sel & 0x03;
			else palselect = (m_200f_bk_pal_sel & 0x0c) >> 2;

			for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
			{
				int xscroll = m_xscroll_7_0_bk[which];
				int xscrollmsb = (m_scroll_control_bk[which] & 0x01);

				int rowscroll_pram_address = (m_2020_bk_linescroll & 0xf) * 0x100;

				bool rowscroll_enabled = false;
				if ((which == 0) && (m_2020_bk_linescroll & 0x10))
					rowscroll_enabled = true;
				if ((which == 1) && (m_2020_bk_linescroll & 0x20))
					rowscroll_enabled = true;

				if (rowscroll_enabled)
				{
					// yes, the rowscroll table is in main ram, not vram ?!
					xscroll += (s8)m_mainram[rowscroll_pram_address + y];
					// also need to modify xscrollmsb?
				}

				/* must be some condition for this, as Maze Pac does not want this offset (confirmed no offset on hardware) but some others do (see Snake title for example)
				   documentation says it's a hw bug, for bk2 (+2 pixels), but conditions aren't understood, and bk1 clearly needs offset too
				   sprites and tilemaps on the select menu need to align too, without left edge scrolling glitches
				   judging this from videos is tricky, because there's another bug that causes the right-most column of pixels to not render for certain scroll values
				   and the right-most 2 columns of sprites to not render

				   does this come down to pal1/pal2 output mixing rather than specific layers?

				   maybe some odd interaction with rowscroll even if it's disabled?
				   (as rowscroll is weird using mainram, and the mx10/11 sets set a bank even without enabling it, maybe as a workaround)
				*/

				//if (which == 0)
				//  xscroll += 1;

				//if (which == 1)
				//  xscroll += 1;

				setup_video_pages(which, bk_tilesize, page_layout_v, page_layout_h, yscrollmsb, xscrollmsb, bases);

				int ytile, ytileline;

				int ywithscroll = y - yscroll;

				if (bk_tilesize)
				{
					ytileline = ywithscroll & 0xf;
					ytile = ywithscroll >> 4;

				}
				else
				{
					ytileline = ywithscroll & 0x07;
					ytile = ywithscroll >> 3;
				}

				for (int xtile = -1; xtile < (bk_tilesize ? (16) : (32)); xtile++) // -1 due to possible need for partial tile during scrolling
				{
					int xscrolltile_part;
					int xscrolltile;
					if (bk_tilesize)
					{
						xscrolltile = xscroll >> 4;
						xscrolltile_part = xscroll & 0x0f;
					}
					else
					{
						xscrolltile = xscroll >> 3;
						xscrolltile_part = xscroll & 0x07;
					}


					int count = get_address_for_tilepos(xtile - xscrolltile, ytile, bk_tilesize, bases);

					uint16_t word = m_vram->read8(count);
					count++;
					word |= m_vram->read8(count) << 8;
					count++;

					int tile = word & 0x0fff;

					if (!tile) // verified
						continue;

					uint8_t pal = (word & 0xf000) >> 12;

					int xpos = xtile * (bk_tilesize ? 16 : 8);

					uint8_t realpal, realdepth;

					// Dingle Hunt and Ocean Fantasy in cmpmx10 turn this on
					// but priority data and palette data are stored as normal
					// resulting in broken palettes and priority if we use it
					//
					// Alien Attack in njp60in1 / pgs268 / unk1682 also sets
					// this and expects it to be ignored
					//
					// The broken priorities in Table Tennis for exsprt48 / itvg48
					// are unrelated (bit isn't set) but as they work in
					// xing48 could be just a bug in those versions
					//
					// does it really work on hardware? we disable it entirely
					// in those sets for now
					if (bk_paldepth_mode && m_allow_bk_paldepth_mode)
					{
						// haven't seen this used properly
						//if (bk_paldepth_mode)
						//  popmessage("bk_paldepth_mode set\n");
						realdepth = pal & 0x03;

						// depth might instead be the high 2 bits in 4bpp mode
						realpal = (pal & 0x0c) | bk_depth;
					}
					else
					{
						realpal = pal;
						realdepth = bk_depth;
					}

					draw_tile_pixline(segment, tile, ytileline, xpos + xscrolltile_part, y, palselect, realpal, bk_tilesize, bk_tilesize, bk_tilebpp, (realdepth * 2) + 1, opaque, 0, 0, cliprect);
				}
			}
		}
		else
		{
			// Line Mode

			if (high_color)
			{
				popmessage("high colour line mode\n");
			}
			else
			{
				popmessage("line mode\n");
			}
		}
	}
}

void vt_vt1682_state::draw_sprites(const rectangle& cliprect)
{
	int sp_en = (m_2018_spregs & 0x04) >> 2;
	int sp_pal_sel = (m_2018_spregs & 0x08) >> 3;
	int sp_size = (m_2018_spregs & 0x03);

	int segment = m_201a_sp_segment_7_0;
	segment |= m_201b_sp_segment_11_8 << 8;
	segment = segment * 0x2000;
	// if we don't do the skipping in inc_spriteram_addr this would need to be 5 instead
	const int SPRITE_STEP = 8;


	if (sp_en)
	{
		for (int line = cliprect.min_y; line <= cliprect.max_y; line++)
		{
			for (int i = 0; i < 240; i++)
			{
				int attr2 = m_spriteram->read8((i * SPRITE_STEP) + 5);

				int ystart = m_spriteram->read8((i * SPRITE_STEP) + 4);

				if (attr2 & 0x01)
					ystart -= 256;

				int yend = ystart + ((sp_size & 0x2) ? 16 : 8);

				// TODO, cache first 16 sprites per scanline which meet the critera to a list during hblank, set overflow flag if more requested
				// (do tilenum = 0 sprites count against this limit?)

				if (line >= ystart && line < yend)
				{
					int ytileline = line - ystart;

					int tilenum = m_spriteram->read8((i * SPRITE_STEP) + 0);
					int attr0 = m_spriteram->read8((i * SPRITE_STEP) + 1);
					int x = m_spriteram->read8((i * SPRITE_STEP) + 2);
					int attr1 = m_spriteram->read8((i * SPRITE_STEP) + 3);

					tilenum |= (attr0 & 0x0f) << 8;

					if (!tilenum) // verified
						continue;

					int pal = (attr0 & 0xf0) >> 4;

					int flipx = (attr1 & 0x02) >> 1; // might not function correctly on hardware
					int flipy = (attr1 & 0x04) >> 2;

					int depth = (attr1 & 0x18) >> 3;

					if (attr1 & 0x01)
						x -= 256;

					// guess! Maze Pac needs sprites shifted left by 1, but actual conditions might be more complex
					//if ((!sp_size & 0x01))
					//  x -= 1;

					int palselect = 0;
					if (sp_pal_sel)
					{
						// sprites are rendered to both buffers
						palselect = 3;
					}
					else
					{
						if (attr2 & 0x02)
							palselect = 2;
						else
							palselect = 1;
					}

					draw_tile_pixline(segment, tilenum, ytileline, x, line, palselect, pal, sp_size & 0x2, sp_size & 0x1, 0, depth * 2, 0, flipx, flipy, cliprect);

				}
			}
		}
		// if more than 16 sprites on any line 0x2001 bit 0x40 (SP_ERR) should be set (updated every line, can only be read in HBLANK)
	}
}

uint32_t vt_vt1682_state::screen_update(screen_device& screen, bitmap_rgb32& bitmap, const rectangle& cliprect)
{
	m_pal2_priority_bitmap.fill(0xff, cliprect);
	m_pal1_priority_bitmap.fill(0xff, cliprect);
	m_pal2_pix_bitmap.fill(0x00, cliprect);
	m_pal1_pix_bitmap.fill(0x00, cliprect);

	bitmap.fill(0, cliprect);

	draw_layer(0, 0, cliprect);

	draw_layer(1, 0, cliprect);

	draw_sprites(cliprect);

	for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
	{
		pen_t const *const paldata = m_palette->pens();
		uint8_t const *const pri2ptr = &m_pal2_priority_bitmap.pix(y);
		uint8_t const *const pri1ptr = &m_pal1_priority_bitmap.pix(y);
		uint8_t const *const pix2ptr = &m_pal2_pix_bitmap.pix(y);
		uint8_t const *const pix1ptr = &m_pal1_pix_bitmap.pix(y);
		uint32_t *const dstptr = &bitmap.pix(y);

		for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
		{
			uint8_t pix1 = pix1ptr[x];
			uint8_t pix2 = pix2ptr[x];
			uint8_t pri1 = pri1ptr[x];
			uint8_t pri2 = pri2ptr[x];

			// TODO: bit 0x8000 in palette can cause the layer to 'dig through'
			// palette layers can also be turned off, or just sent to lcd / just sent to tv
			// layers can also blend 50/50 rather than using depth

			// the transparency fallthrough here works for Boxing, but appears to be incorrect for Lawn Purge title screen (assuming it isn't an offset issue)

			if (pri1 <= pri2)
			{
				if (pix1) dstptr[x] = paldata[pix1 | 0x100];
				else
				{
					if (pix2) dstptr[x] = paldata[pix2];
					else dstptr[x] = paldata[0x100];
				}
			}
			else
			{
				if (pix2) dstptr[x] = paldata[pix2];
				else
				{
					if (pix1) dstptr[x] = paldata[pix1 | 0x100];
					else dstptr[x] = paldata[0x000];
				}
			}
		}
	}

	return 0;
}

// VT1682 can address 25-bit address space (32MB of ROM)
void vt_vt1682_state::rom_map(address_map &map)
{
	map(0x0000000, 0x1ffffff).bankr("cartbank");
}

void vt1682_dance_state::rom_ram_map(address_map &map)
{
	rom_map(map);
	map(0x1000000, 0x101ffff).ram();
}

// 11-bits (0x800 bytes) for sprites
void vt_vt1682_state::spriteram_map(address_map &map)
{
	map(0x000, 0x7ff).ram();
}

// 16-bits (0x10000 bytes) for vram (maybe mirrors at 0x2000?)
void vt_vt1682_state::vram_map(address_map &map)
{
	map(0x0000, 0x0fff).ram();
	map(0x1000, 0x1bff).ram(); // this gets cleared, but apparently is 'reserved'
	map(0x1c00, 0x1fff).ram().w("palette", FUNC(palette_device::write8)).share("palette"); // palette 2
}

// for the 2nd, faster, CPU
void vt_vt1682_state::vt_vt1682_sound_map(address_map& map)
{
	map(0x0000, 0x0fff).ram().share("sound_share");
	map(0x1000, 0x1fff).ram().share("sound_share");
	// 3000-3fff internal ROM if enabled

	map(0x2100, 0x2100).rw(m_soundcpu_timer_a_dev, FUNC(vrt_vt1682_timer_device::vt1682_timer_preload_7_0_r),  FUNC(vrt_vt1682_timer_device::vt1682_timer_preload_7_0_w));
	map(0x2101, 0x2101).rw(m_soundcpu_timer_a_dev, FUNC(vrt_vt1682_timer_device::vt1682_timer_preload_15_8_r), FUNC(vrt_vt1682_timer_device::vt1682_timer_preload_15_8_w));
	map(0x2102, 0x2102).rw(m_soundcpu_timer_a_dev, FUNC(vrt_vt1682_timer_device::vt1682_timer_enable_r),       FUNC(vrt_vt1682_timer_device::vt1682_timer_enable_w));
	map(0x2103, 0x2103).w( m_soundcpu_timer_a_dev, FUNC(vrt_vt1682_timer_device::vt1682_timer_irqclear_w));

	map(0x2110, 0x2110).rw(m_soundcpu_timer_b_dev, FUNC(vrt_vt1682_timer_device::vt1682_timer_preload_7_0_r),  FUNC(vrt_vt1682_timer_device::vt1682_timer_preload_7_0_w));
	map(0x2111, 0x2111).rw(m_soundcpu_timer_b_dev, FUNC(vrt_vt1682_timer_device::vt1682_timer_preload_15_8_r), FUNC(vrt_vt1682_timer_device::vt1682_timer_preload_15_8_w));
	map(0x2112, 0x2112).rw(m_soundcpu_timer_b_dev, FUNC(vrt_vt1682_timer_device::vt1682_timer_enable_r),       FUNC(vrt_vt1682_timer_device::vt1682_timer_enable_w));
	map(0x2113, 0x2113).w( m_soundcpu_timer_b_dev, FUNC(vrt_vt1682_timer_device::vt1682_timer_irqclear_w));

	map(0x2118, 0x2118).rw(FUNC(vt_vt1682_state::vt1682_soundcpu_2118_dacleft_7_0_r), FUNC(vt_vt1682_state::vt1682_soundcpu_2118_dacleft_7_0_w));
	map(0x2119, 0x2119).rw(FUNC(vt_vt1682_state::vt1682_soundcpu_2119_dacleft_15_8_r), FUNC(vt_vt1682_state::vt1682_soundcpu_2119_dacleft_15_8_w));
	map(0x211a, 0x211a).rw(FUNC(vt_vt1682_state::vt1682_soundcpu_211a_dacright_7_0_r), FUNC(vt_vt1682_state::vt1682_soundcpu_211a_dacright_7_0_w));
	map(0x211b, 0x211b).rw(FUNC(vt_vt1682_state::vt1682_soundcpu_211b_dacright_15_8_r), FUNC(vt_vt1682_state::vt1682_soundcpu_211b_dacright_15_8_w));

	map(0x211c, 0x211c).w(FUNC(vt_vt1682_state::vt1682_soundcpu_211c_reg_irqctrl_w));

	map(0x2130, 0x2130).rw(m_soundcpu_alu, FUNC(vrt_vt1682_alu_device::alu_out_1_r), FUNC(vrt_vt1682_alu_device::alu_oprand_1_w));
	map(0x2131, 0x2131).rw(m_soundcpu_alu, FUNC(vrt_vt1682_alu_device::alu_out_2_r), FUNC(vrt_vt1682_alu_device::alu_oprand_2_w));
	map(0x2132, 0x2132).rw(m_soundcpu_alu, FUNC(vrt_vt1682_alu_device::alu_out_3_r), FUNC(vrt_vt1682_alu_device::alu_oprand_3_w));
	map(0x2133, 0x2133).rw(m_soundcpu_alu, FUNC(vrt_vt1682_alu_device::alu_out_4_r), FUNC(vrt_vt1682_alu_device::alu_oprand_4_w));
	map(0x2134, 0x2134).rw(m_soundcpu_alu, FUNC(vrt_vt1682_alu_device::alu_out_5_r), FUNC(vrt_vt1682_alu_device::alu_oprand_5_mult_w));
	map(0x2135, 0x2135).rw(m_soundcpu_alu, FUNC(vrt_vt1682_alu_device::alu_out_6_r), FUNC(vrt_vt1682_alu_device::alu_oprand_6_mult_w));
	map(0x2136, 0x2136).w(m_soundcpu_alu, FUNC(vrt_vt1682_alu_device::alu_oprand_5_div_w));
	map(0x2137, 0x2137).w(m_soundcpu_alu, FUNC(vrt_vt1682_alu_device::alu_oprand_6_div_w));

	// is this IO port the same logic as the UIO?
	map(0x2140, 0x2140).rw(FUNC(vt_vt1682_state::vt1682_soundcpu_2410_ioa_data_r), FUNC(vt_vt1682_state::vt1682_soundcpu_2410_ioa_data_w));
	map(0x2144, 0x2144).r(FUNC(vt_vt1682_state::vt1682_soundcpu_2410_iob_data_r));


	map(0xf000, 0xffff).ram().share("sound_share"); // doesn't actually map here, the CPU fetches vectors from 0x0ff0 - 0x0fff!

	map(0xfffe, 0xffff).r(FUNC(vt_vt1682_state::soundcpu_irq_vector_hack_r)); // probably need custom IRQ support in the core instead...
}

void vt_vt1682_state::vt1682_212e_scuirq_clear_w(u8 data)
{
	// this is listed as unused in older documentation, with 211c reads being for scuirq clear
	// however later documentation lists this address as a correction and the software we are
	// aware of uses it
	m_scu_to_main_irq_active = 0;
	update_main_interrupts();
}

void vt_vt1682_state::vt_vt1682_map(address_map &map)
{
	map(0x0000, 0x0fff).ram().share("mainram");
	map(0x1000, 0x1fff).ram().share("sound_share");
	map(0x1ff4, 0x1fff).w(FUNC(vt_vt1682_state::vt1682_sound_reset_hack_w));

	/* Video */
	map(0x2000, 0x2000).rw(FUNC(vt_vt1682_state::vt1682_2000_r), FUNC(vt_vt1682_state::vt1682_2000_w));
	map(0x2001, 0x2001).rw(FUNC(vt_vt1682_state::vt1682_2001_vblank_r), FUNC(vt_vt1682_state::vt1682_2001_w));
	map(0x2002, 0x2002).rw(FUNC(vt_vt1682_state::vt1682_2002_sprramaddr_2_0_r), FUNC(vt_vt1682_state::vt1682_2002_sprramaddr_2_0_w));
	map(0x2003, 0x2003).rw(FUNC(vt_vt1682_state::vt1682_2003_sprramaddr_10_3_r), FUNC(vt_vt1682_state::vt1682_2003_sprramaddr_10_3_w));
	map(0x2004, 0x2004).rw(FUNC(vt_vt1682_state::vt1682_2004_sprram_data_r), FUNC(vt_vt1682_state::vt1682_2004_sprram_data_w));
	map(0x2005, 0x2005).rw(FUNC(vt_vt1682_state::vt1682_2005_vramaddr_7_0_r), FUNC(vt_vt1682_state::vt1682_2005_vramaddr_7_0_w));
	map(0x2006, 0x2006).rw(FUNC(vt_vt1682_state::vt1682_2006_vramaddr_15_8_r), FUNC(vt_vt1682_state::vt1682_2006_vramaddr_15_8_w));
	map(0x2007, 0x2007).rw(FUNC(vt_vt1682_state::vt1682_2007_vram_data_r), FUNC(vt_vt1682_state::vt1682_2007_vram_data_w));
	map(0x2008, 0x2008).rw(FUNC(vt_vt1682_state::vt1682_2008_lcd_vs_delay_r), FUNC(vt_vt1682_state::vt1682_2008_lcd_vs_delay_w));
	map(0x2009, 0x2009).rw(FUNC(vt_vt1682_state::vt1682_2009_lcd_hs_delay_7_0_r), FUNC(vt_vt1682_state::vt1682_2009_lcd_hs_delay_7_0_w));
	map(0x200a, 0x200a).rw(FUNC(vt_vt1682_state::vt1682_200a_lcd_fr_delay_7_0_r), FUNC(vt_vt1682_state::vt1682_200a_lcd_fr_delay_7_0_w));
	map(0x200b, 0x200b).rw(FUNC(vt_vt1682_state::vt1682_200b_misc_vregs0_r), FUNC(vt_vt1682_state::vt1682_200b_misc_vregs0_w));
	map(0x200c, 0x200c).rw(FUNC(vt_vt1682_state::vt1682_200c_misc_vregs1_r), FUNC(vt_vt1682_state::vt1682_200c_misc_vregs1_w));
	map(0x200d, 0x200d).rw(FUNC(vt_vt1682_state::vt1682_200d_misc_vregs2_r), FUNC(vt_vt1682_state::vt1682_200d_misc_vregs2_w));
	map(0x200e, 0x200e).rw(FUNC(vt_vt1682_state::vt1682_200e_blend_pal_sel_r), FUNC(vt_vt1682_state::vt1682_200e_blend_pal_sel_w));
	map(0x200f, 0x200f).rw(FUNC(vt_vt1682_state::vt1682_200f_bk_pal_sel_r), FUNC(vt_vt1682_state::vt1682_200f_bk_pal_sel_w));
	map(0x2010, 0x2010).rw(FUNC(vt_vt1682_state::vt1682_2010_bk1_xscroll_7_0_r), FUNC(vt_vt1682_state::vt1682_2010_bk1_xscroll_7_0_w));
	map(0x2011, 0x2011).rw(FUNC(vt_vt1682_state::vt1682_2011_bk1_yscoll_7_0_r), FUNC(vt_vt1682_state::vt1682_2011_bk1_yscoll_7_0_w));
	map(0x2012, 0x2012).rw(FUNC(vt_vt1682_state::vt1682_2012_bk1_scroll_control_r), FUNC(vt_vt1682_state::vt1682_2012_bk1_scroll_control_w));
	map(0x2013, 0x2013).rw(FUNC(vt_vt1682_state::vt1682_2013_bk1_main_control_r), FUNC(vt_vt1682_state::vt1682_2013_bk1_main_control_w));
	map(0x2014, 0x2014).rw(FUNC(vt_vt1682_state::vt1682_2014_bk2_xscroll_7_0_r), FUNC(vt_vt1682_state::vt1682_2014_bk2_xscroll_7_0_w));
	map(0x2015, 0x2015).rw(FUNC(vt_vt1682_state::vt1682_2015_bk2_yscoll_7_0_r), FUNC(vt_vt1682_state::vt1682_2015_bk2_yscoll_7_0_w));
	map(0x2016, 0x2016).rw(FUNC(vt_vt1682_state::vt1682_2016_bk2_scroll_control_r), FUNC(vt_vt1682_state::vt1682_2016_bk2_scroll_control_w));
	map(0x2017, 0x2017).rw(FUNC(vt_vt1682_state::vt1682_2017_bk2_main_control_r), FUNC(vt_vt1682_state::vt1682_2017_bk2_main_control_w));
	map(0x2018, 0x2018).rw(FUNC(vt_vt1682_state::vt1682_2018_spregs_r), FUNC(vt_vt1682_state::vt1682_2018_spregs_w));
	map(0x2019, 0x2019).rw(FUNC(vt_vt1682_state::vt1682_2019_bkgain_r), FUNC(vt_vt1682_state::vt1682_2019_bkgain_w));
	map(0x201a, 0x201a).rw(FUNC(vt_vt1682_state::vt1682_201a_sp_segment_7_0_r), FUNC(vt_vt1682_state::vt1682_201a_sp_segment_7_0_w));
	map(0x201b, 0x201b).rw(FUNC(vt_vt1682_state::vt1682_201b_sp_segment_11_8_r), FUNC(vt_vt1682_state::vt1682_201b_sp_segment_11_8_w));
	map(0x201c, 0x201c).rw(FUNC(vt_vt1682_state::vt1682_201c_bk1_segment_7_0_r), FUNC(vt_vt1682_state::vt1682_201c_bk1_segment_7_0_w));
	map(0x201d, 0x201d).rw(FUNC(vt_vt1682_state::vt1682_201d_bk1_segment_11_8_r), FUNC(vt_vt1682_state::vt1682_201d_bk1_segment_11_8_w));
	map(0x201e, 0x201e).rw(FUNC(vt_vt1682_state::vt1682_201e_bk2_segment_7_0_r), FUNC(vt_vt1682_state::vt1682_201e_bk2_segment_7_0_w));
	map(0x201f, 0x201f).rw(FUNC(vt_vt1682_state::vt1682_201f_bk2_segment_11_8_r), FUNC(vt_vt1682_state::vt1682_201f_bk2_segment_11_8_w));
	map(0x2020, 0x2020).rw(FUNC(vt_vt1682_state::vt1682_2020_bk_linescroll_r), FUNC(vt_vt1682_state::vt1682_2020_bk_linescroll_w));
	map(0x2021, 0x2021).rw(FUNC(vt_vt1682_state::vt1682_2021_lum_offset_r), FUNC(vt_vt1682_state::vt1682_2021_lum_offset_w));
	map(0x2022, 0x2022).rw(FUNC(vt_vt1682_state::vt1682_2022_saturation_misc_r), FUNC(vt_vt1682_state::vt1682_2022_saturation_misc_w));
	map(0x2023, 0x2023).rw(FUNC(vt_vt1682_state::vt1682_2023_lightgun_reset_r), FUNC(vt_vt1682_state::vt1682_2023_lightgun_reset_w));
	map(0x2024, 0x2024).rw(FUNC(vt_vt1682_state::vt1682_2024_lightgun1_y_r), FUNC(vt_vt1682_state::vt1682_2024_lightgun1_y_w));
	map(0x2025, 0x2025).rw(FUNC(vt_vt1682_state::vt1682_2025_lightgun1_x_r), FUNC(vt_vt1682_state::vt1682_2025_lightgun1_x_w));
	map(0x2026, 0x2026).rw(FUNC(vt_vt1682_state::vt1682_2026_lightgun2_y_r), FUNC(vt_vt1682_state::vt1682_2026_lightgun2_y_w));
	map(0x2027, 0x2027).rw(FUNC(vt_vt1682_state::vt1682_2027_lightgun2_x_r), FUNC(vt_vt1682_state::vt1682_2027_lightgun2_x_w));
	map(0x2028, 0x2028).rw(FUNC(vt_vt1682_state::vt1682_2028_r), FUNC(vt_vt1682_state::vt1682_2028_w));
	map(0x2029, 0x2029).rw(FUNC(vt_vt1682_state::vt1682_2029_r), FUNC(vt_vt1682_state::vt1682_2029_w));
	map(0x202a, 0x202a).rw(FUNC(vt_vt1682_state::vt1682_202a_r), FUNC(vt_vt1682_state::vt1682_202a_w));
	map(0x202b, 0x202b).rw(FUNC(vt_vt1682_state::vt1682_202b_r), FUNC(vt_vt1682_state::vt1682_202b_w));
	// 202c unused
	// 202d unused
	map(0x202e, 0x202e).rw(FUNC(vt_vt1682_state::vt1682_202e_r), FUNC(vt_vt1682_state::vt1682_202e_w));
	// 202f unused
	map(0x2030, 0x2030).rw(FUNC(vt_vt1682_state::vt1682_2030_r), FUNC(vt_vt1682_state::vt1682_2030_w));
	map(0x2031, 0x2031).rw(FUNC(vt_vt1682_state::vt1682_2031_red_dac_r), FUNC(vt_vt1682_state::vt1682_2031_red_dac_w));
	map(0x2032, 0x2032).rw(FUNC(vt_vt1682_state::vt1682_2032_green_dac_r), FUNC(vt_vt1682_state::vt1682_2032_green_dac_w));
	map(0x2033, 0x2033).rw(FUNC(vt_vt1682_state::vt1682_2033_blue_dac_r), FUNC(vt_vt1682_state::vt1682_2033_blue_dac_w));

	/* System */
	map(0x2100, 0x2100).rw(FUNC(vt_vt1682_state::vt1682_2100_prgbank1_r3_r), FUNC(vt_vt1682_state::vt1682_2100_prgbank1_r3_w));
	map(0x2101, 0x2101).rw(m_system_timer_dev, FUNC(vrt_vt1682_timer_device::vt1682_timer_preload_7_0_r),  FUNC(vrt_vt1682_timer_device::vt1682_timer_preload_7_0_w));
	map(0x2102, 0x2102).r(m_system_timer_dev, FUNC(vrt_vt1682_timer_device::vt1682_timer_enable_r));
	map(0x2102, 0x2102).w(FUNC(vt_vt1682_state::vt1682_timer_enable_trampoline_w));
	map(0x2103, 0x2103).w( m_system_timer_dev, FUNC(vrt_vt1682_timer_device::vt1682_timer_irqclear_w));
	map(0x2104, 0x2104).r(m_system_timer_dev, FUNC(vrt_vt1682_timer_device::vt1682_timer_preload_15_8_r));
	map(0x2104, 0x2104).w(FUNC(vt_vt1682_state::vt1682_timer_preload_15_8_trampoline_w));
	map(0x2105, 0x2105).w(FUNC(vt_vt1682_state::vt1682_2105_comr6_tvmodes_w));
	map(0x2106, 0x2106).rw(FUNC(vt_vt1682_state::vt1682_2106_enable_regs_r), FUNC(vt_vt1682_state::vt1682_2106_enable_regs_w));
	map(0x2107, 0x2107).rw(FUNC(vt_vt1682_state::vt1682_2107_prgbank0_r0_r), FUNC(vt_vt1682_state::vt1682_2107_prgbank0_r0_w));
	map(0x2108, 0x2108).rw(FUNC(vt_vt1682_state::vt1682_2108_prgbank0_r1_r), FUNC(vt_vt1682_state::vt1682_2108_prgbank0_r1_w));
	map(0x2109, 0x2109).rw(FUNC(vt_vt1682_state::vt1682_2109_prgbank0_r2_r), FUNC(vt_vt1682_state::vt1682_2109_prgbank0_r2_w));
	map(0x210a, 0x210a).rw(FUNC(vt_vt1682_state::vt1682_210a_prgbank0_r3_r), FUNC(vt_vt1682_state::vt1682_210a_prgbank0_r3_w));
	map(0x210b, 0x210b).rw(FUNC(vt_vt1682_state::vt1682_210b_misc_cs_prg0_bank_sel_r), FUNC(vt_vt1682_state::vt1682_210b_misc_cs_prg0_bank_sel_w));
	map(0x210c, 0x210c).rw(FUNC(vt_vt1682_state::vt1682_210c_prgbank1_r2_r), FUNC(vt_vt1682_state::vt1682_210c_prgbank1_r2_w));
	map(0x210d, 0x210d).rw(m_io, FUNC(vrt_vt1682_io_device::vt1682_210d_ioconfig_r),FUNC(vrt_vt1682_io_device::vt1682_210d_ioconfig_w));
	map(0x210e, 0x210e).rw(m_io, FUNC(vrt_vt1682_io_device::vt1682_210e_io_ab_r),FUNC(vrt_vt1682_io_device::vt1682_210e_io_ab_w));
	map(0x210f, 0x210f).rw(m_io, FUNC(vrt_vt1682_io_device::vt1682_210f_io_cd_r),FUNC(vrt_vt1682_io_device::vt1682_210f_io_cd_w));
	map(0x2110, 0x2110).rw(FUNC(vt_vt1682_state::vt1682_prgbank0_r4_r), FUNC(vt_vt1682_state::vt1682_prgbank1_r0_w)); // either reads/writes are on different addresses or our source info is incorrect
	map(0x2111, 0x2111).rw(FUNC(vt_vt1682_state::vt1682_prgbank0_r5_r), FUNC(vt_vt1682_state::vt1682_prgbank1_r1_w)); // ^
	map(0x2112, 0x2112).rw(FUNC(vt_vt1682_state::vt1682_prgbank1_r0_r), FUNC(vt_vt1682_state::vt1682_prgbank0_r4_w)); // ^
	map(0x2113, 0x2113).rw(FUNC(vt_vt1682_state::vt1682_prgbank1_r1_r), FUNC(vt_vt1682_state::vt1682_prgbank0_r5_w)); // ^
	// 2114 baud rade
	// 2115 baud rate
	// 2116 SPI
	// 2117 SPI
	map(0x2118, 0x2118).rw(FUNC(vt_vt1682_state::vt1682_2118_prgbank1_r4_r5_r), FUNC(vt_vt1682_state::vt1682_2118_prgbank1_r4_r5_w));
	// 2119 UART
	// 211a UART
	// 211b UART
	map(0x211c, 0x211c).w(FUNC(vt_vt1682_state::vt1682_211c_regs_ext2421_w));
	// 211d misc enable regs
	map(0x211e, 0x211e).rw(m_io, FUNC(vrt_vt1682_io_device::vt1682_211e_adc_data_r), FUNC(vrt_vt1682_io_device::vt1682_211e_adconfig_w));
	// 211f voice gain
	// 2120 sleep period
	// 2121 misc interrupt masks / clears
	map(0x2122, 0x2122).rw(FUNC(vt_vt1682_state::vt1682_2122_dma_dt_addr_7_0_r), FUNC(vt_vt1682_state::vt1682_2122_dma_dt_addr_7_0_w));
	map(0x2123, 0x2123).rw(FUNC(vt_vt1682_state::vt1682_2123_dma_dt_addr_15_8_r), FUNC(vt_vt1682_state::vt1682_2123_dma_dt_addr_15_8_w));
	map(0x2124, 0x2124).rw(FUNC(vt_vt1682_state::vt1682_2124_dma_sr_addr_7_0_r), FUNC(vt_vt1682_state::vt1682_2124_dma_sr_addr_7_0_w));
	map(0x2125, 0x2125).rw(FUNC(vt_vt1682_state::vt1682_2125_dma_sr_addr_15_8_r), FUNC(vt_vt1682_state::vt1682_2125_dma_sr_addr_15_8_w));
	map(0x2126, 0x2126).rw(FUNC(vt_vt1682_state::vt1682_2126_dma_sr_bank_addr_22_15_r), FUNC(vt_vt1682_state::vt1682_2126_dma_sr_bank_addr_22_15_w));
	map(0x2127, 0x2127).rw(FUNC(vt_vt1682_state::vt1682_2127_dma_status_r), FUNC(vt_vt1682_state::vt1682_2127_dma_size_trigger_w));
	map(0x2128, 0x2128).rw(FUNC(vt_vt1682_state::vt1682_2128_dma_sr_bank_addr_24_23_r), FUNC(vt_vt1682_state::vt1682_2128_dma_sr_bank_addr_24_23_w));
	map(0x2129, 0x2129).rw(m_uio, FUNC(vrt_vt1682_uio_device::inteact_2129_uio_a_data_r), FUNC(vrt_vt1682_uio_device::inteact_2129_uio_a_data_w));
	map(0x212a, 0x212a).w(m_uio, FUNC(vrt_vt1682_uio_device::inteact_212a_uio_a_direction_w));
	map(0x212a, 0x212a).r(FUNC(vt_vt1682_state::inteact_212a_send_joy_clock2_r));
	map(0x212b, 0x212b).rw(m_uio, FUNC(vrt_vt1682_uio_device::inteact_212b_uio_a_attribute_r), FUNC(vrt_vt1682_uio_device::inteact_212b_uio_a_attribute_w));
	map(0x212c, 0x212c).rw(FUNC(vt_vt1682_state::vt1682_212c_prng_r), FUNC(vt_vt1682_state::vt1682_212c_prng_seed_w));
	// 212d PLL
	map(0x212e, 0x212e).w(FUNC(vt_vt1682_state::vt1682_212e_scuirq_clear_w));
	// 212f unused
	map(0x2130, 0x2130).rw(m_maincpu_alu, FUNC(vrt_vt1682_alu_device::alu_out_1_r), FUNC(vrt_vt1682_alu_device::alu_oprand_1_w));
	map(0x2131, 0x2131).rw(m_maincpu_alu, FUNC(vrt_vt1682_alu_device::alu_out_2_r), FUNC(vrt_vt1682_alu_device::alu_oprand_2_w));
	map(0x2132, 0x2132).rw(m_maincpu_alu, FUNC(vrt_vt1682_alu_device::alu_out_3_r), FUNC(vrt_vt1682_alu_device::alu_oprand_3_w));
	map(0x2133, 0x2133).rw(m_maincpu_alu, FUNC(vrt_vt1682_alu_device::alu_out_4_r), FUNC(vrt_vt1682_alu_device::alu_oprand_4_w));
	map(0x2134, 0x2134).rw(m_maincpu_alu, FUNC(vrt_vt1682_alu_device::alu_out_5_r), FUNC(vrt_vt1682_alu_device::alu_oprand_5_mult_w));
	map(0x2135, 0x2135).rw(m_maincpu_alu, FUNC(vrt_vt1682_alu_device::alu_out_6_r), FUNC(vrt_vt1682_alu_device::alu_oprand_6_mult_w));
	map(0x2136, 0x2136).w(m_maincpu_alu, FUNC(vrt_vt1682_alu_device::alu_oprand_5_div_w));
	map(0x2137, 0x2137).w(m_maincpu_alu, FUNC(vrt_vt1682_alu_device::alu_oprand_6_div_w));

	map(0x2149, 0x2149).rw(m_uio, FUNC(vrt_vt1682_uio_device::inteact_2149_uio_b_data_r), FUNC(vrt_vt1682_uio_device::inteact_2149_uio_b_data_w));
	map(0x214a, 0x214a).rw(m_uio, FUNC(vrt_vt1682_uio_device::inteact_214a_uio_b_direction_r), FUNC(vrt_vt1682_uio_device::inteact_214a_uio_b_direction_w));
	map(0x214b, 0x214b).rw(m_uio, FUNC(vrt_vt1682_uio_device::inteact_214b_uio_b_attribute_r), FUNC(vrt_vt1682_uio_device::inteact_214b_uio_b_attribute_w));

	map(0x214c, 0x214c).rw(m_io, FUNC(vrt_vt1682_io_device::vt1682_214c_ioefconfig_r),FUNC(vrt_vt1682_io_device::vt1682_214c_ioefconfig_w));
	map(0x214d, 0x214d).rw(m_io, FUNC(vrt_vt1682_io_device::vt1682_214d_io_ef_r),FUNC(vrt_vt1682_io_device::vt1682_214d_io_ef_w));

	// 3000-3fff internal ROM if enabled
	map(0x4000, 0x7fff).r(FUNC(vt_vt1682_state::rom_4000_to_7fff_r));
	map(0x8000, 0xffff).rw(FUNC(vt_vt1682_state::rom_8000_to_ffff_r), FUNC(vt_vt1682_state::rom_8000_to_ffff_w));

	map(0xfffe, 0xffff).r(FUNC(vt_vt1682_state::maincpu_irq_vector_hack_r)); // probably need custom IRQ support in the core instead...
}

/*

Vectors / IRQ Levels

MAIN CPU:

SPI IRQ         0x7fff2 - 0x7fff3 (0xfff2 - 0xfff3)
UART IRQ        0x7fff4 - 0x7fff5 (0xfff4 - 0xfff5)
SCPU IRQ        0x7fff6 - 0x7fff7 (0xfff6 - 0xfff7)
Timer IRQ       0x7fff8 - 0x7fff9 (0xfff8 - 0xfff9)
NMI             0x7fffa - 0x7fffb (0xfffa - 0xfffb)
RESET           0x7fffc - 0x7fffd (0xfffc - 0xfffd)
Ext IRQ         0x7fffe - 0x7ffff (0xfffe - 0xffff)

SOUND CPU:

CPU IRQ         0x0ff4 - 0x0ff5
Timer2 IRQ      0x0ff6 - 0x0ff7
Timer1 IRQ      0x0ff8 - 0x0ff9
NMI             0x0ffa - 0x0ffb
RESET           0x0ffc - 0x0ffd
Ext IRQ         0x0ffe - 0x0fff

*/


uint8_t vt_vt1682_state::soundcpu_irq_vector_hack_r(offs_t offset)
{
	auto vector = m_current_sound_vector;
	if (offset == 0)
	{
		vector = m_new_sound_vector;
		if (!machine().side_effects_disabled())
			m_current_sound_vector = vector;
	}

	// redirect to required IRQ!
	return m_sound_share[vector + offset];
}



uint8_t vt_vt1682_state::maincpu_irq_vector_hack_r(offs_t offset)
{
	auto vector = m_current_main_vector;
	if (offset == 0)
	{
		vector = m_new_main_vector;
		if (!machine().side_effects_disabled())
			m_current_main_vector = vector;
	}

	// redirect to required IRQ!
	return rom_8000_to_ffff_r((vector - 0x8000) + offset);
}


// intg5410 writes a new program without resetting the CPU when selecting from the 'arcade' game main menu, this is problematic
// it does appear to rewrite the vectors first, so maybe there is some hardware side-effect of this putting the CPU in reset state??
void vt_vt1682_state::vt1682_sound_reset_hack_w(offs_t offset, uint8_t data)
{
	m_sound_share[0x0ff4 + offset] = data;
	m_soundcpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
}

void vt_vt1682_state::update_sound_interrupts()
{
	if (m_timera_to_sound_irq_active || m_timerb_to_sound_irq_active)
	{
		if (m_timera_to_sound_irq_active)
		{
			m_new_sound_vector = 0x0ff8;
		}
		else if (m_timerb_to_sound_irq_active)
		{
			m_new_sound_vector = 0x0ff6;
		}

		m_soundcpu->set_input_line(0, ASSERT_LINE);
	}
	else
	{
		m_new_sound_vector = 0x0ffe;
		m_soundcpu->set_input_line(0, CLEAR_LINE);
	}
}


void vt_vt1682_state::soundcpu_timera_irq(int state)
{
	if (state && !m_scpu_is_in_reset)
		m_timera_to_sound_irq_active = 1;
	else
		m_timera_to_sound_irq_active = 0;

	update_sound_interrupts();
}

void vt_vt1682_state::soundcpu_timerb_irq(int state)
{
	if (state && !m_scpu_is_in_reset)
		m_timerb_to_sound_irq_active = 1;
	else
		m_timerb_to_sound_irq_active = 0;

	update_sound_interrupts();
}



void vt_vt1682_state::maincpu_timer_irq(int state)
{
	// need to set proper vector (need IRQ priority manager function?)

	/* rasters are used on:

	   Highway Racing (title screen - scrolling split)
	   Fire Man (title screen - scrolling split)
	   Bee Fighting (title screen - scrolling split)
	   Over Speed (ingame rendering - road)
	   Motor Storm (ingame rendering - road)
	   Fish War (ingame rendering - status bar split)
	   Duel Soccer (ingame rendering - status bar split)
	*/

	if (state)
		m_timera_to_main_irq_active = 1;
	else
		m_timera_to_main_irq_active = 0;

	update_main_interrupts();
}

void vt_vt1682_state::update_main_interrupts()
{
	if (m_timera_to_main_irq_active || m_scu_to_main_irq_active)
	{
		if (m_timera_to_main_irq_active)
		{
			m_new_main_vector = 0xfff8;
		}
		else if (m_scu_to_main_irq_active)
		{
			m_new_main_vector = 0xfff6;
		}

		m_maincpu->set_input_line(0, ASSERT_LINE);
	}
	else
	{
		m_new_main_vector = 0xfffe;
		m_maincpu->set_input_line(0, CLEAR_LINE);
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(vt_vt1682_state::line_render_start)
{
	// some video reigsters latched in hblank, exact signal timings of irqs etc. is unknown
	// note Fireman titlescreen effect is off by one line on real hardware, as it is with this setup
	if ((param>=0) && (param<240))
		m_screen->update_partial(m_screen->vpos());

	m_render_timer->adjust(attotime::never);
}

TIMER_DEVICE_CALLBACK_MEMBER(vt_vt1682_state::scanline)
{
	int scanline = param;

	m_render_timer->adjust(m_screen->time_until_pos(m_screen->vpos(), 156), scanline);

	if (scanline == 240)
	{
		if (m_2000 & 0x01)
		{
			m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
			if (!m_scpu_is_in_reset)
				m_soundcpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero); // same enable? (NMI_EN on sub is 'wakeup NMI')
		}
	}
}

static const gfx_layout helper_8bpp_8x8_layout =
{
	8,8,
	RGN_FRAC(1,1),
	8,
	{ 0,1,2,3,4,5,6,7 },
	{ STEP8(0,8) },
	{ STEP8(0,8*8) },
	8 * 8 * 8
};

static const gfx_layout helper_8bpp_16x16_layout =
{
	16,16,
	RGN_FRAC(1,1),
	8,
	{ 0,1,2,3,4,5,6,7 },
	{ STEP16(0,8) },
	{ STEP16(0,16*8) },
	16 * 16 * 8
};

// hardware has line modes, so these views might be useful
static const uint32_t texlayout_xoffset_8bpp[256] = { STEP256(0,8) };
static const uint32_t texlayout_yoffset_8bpp[256] = { STEP256(0,256*8) };
static const gfx_layout texture_helper_8bpp_layout =
{
	256, 256,
	RGN_FRAC(1,1),
	8,
	{ 0,1,2,3,4,5,6,7 },
	EXTENDED_XOFFS,
	EXTENDED_YOFFS,
	256*256*8,
	texlayout_xoffset_8bpp,
	texlayout_yoffset_8bpp
};

static const uint32_t texlayout_xoffset_4bpp[256] = { STEP256(0,4) };
static const uint32_t texlayout_yoffset_4bpp[256] = { STEP256(0,256*4) };
static const gfx_layout texture_helper_4bpp_layout =
{
	256, 256,
	RGN_FRAC(1,1),
	4,
	{ 0,1,2,3 },
	EXTENDED_XOFFS,
	EXTENDED_YOFFS,
	256*256*4,
	texlayout_xoffset_4bpp,
	texlayout_yoffset_4bpp
};

// there are 6bpp gfx too, but they can't be decoded cleanly due to endian and alignment issues (start on what would be non-tile boundaries etc.)
static GFXDECODE_START( gfx_test )
	GFXDECODE_ENTRY( "mainrom", 0, texture_helper_4bpp_layout,  0x0, 2  )
	GFXDECODE_ENTRY( "mainrom", 0, helper_8bpp_8x8_layout,  0x0, 2  )
	GFXDECODE_ENTRY( "mainrom", 0, helper_8bpp_16x16_layout,  0x0, 2  )
	GFXDECODE_ENTRY( "mainrom", 0, texture_helper_8bpp_layout,  0x0, 2  )
GFXDECODE_END


void vt_vt1682_state::vt_vt1682_ntscbase(machine_config& config)
{
	/* basic machine hardware */
	M6502_SWAP_OP_D2_D7(config, m_maincpu, MAIN_CPU_CLOCK_NTSC);
	m_maincpu->set_addrmap(AS_PROGRAM, &vt_vt1682_state::vt_vt1682_map);
	//m_maincpu->set_vblank_int("screen", FUNC(vt_vt1682_state::nmi));

	M6502(config, m_soundcpu, SOUND_CPU_CLOCK_NTSC);
	m_soundcpu->set_addrmap(AS_PROGRAM, &vt_vt1682_state::vt_vt1682_sound_map);

	VT_VT1682_TIMER(config, m_soundcpu_timer_a_dev, SOUND_CPU_CLOCK_NTSC);
	m_soundcpu_timer_a_dev->write_irq_callback().set(FUNC(vt_vt1682_state::soundcpu_timera_irq));
	m_soundcpu_timer_a_dev->set_sound_timer(); // different logging conditions
	VT_VT1682_TIMER(config, m_soundcpu_timer_b_dev, SOUND_CPU_CLOCK_NTSC);
	m_soundcpu_timer_b_dev->write_irq_callback().set(FUNC(vt_vt1682_state::soundcpu_timerb_irq));
	VT_VT1682_TIMER(config, m_system_timer_dev, MAIN_CPU_CLOCK_NTSC);
	m_system_timer_dev->write_irq_callback().set(FUNC(vt_vt1682_state::maincpu_timer_irq));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(0));
	m_screen->set_refresh_hz(60);
	m_screen->set_size(300, 262); // 262 for NTSC, might be 261 if Vblank line is changed
	m_screen->set_visarea(0, 256-1, 0, 240-1);
	m_screen->set_screen_update(FUNC(vt_vt1682_state::screen_update));
}

void vt_vt1682_state::vt_vt1682_palbase(machine_config& config)
{
	M6502_SWAP_OP_D2_D7(config, m_maincpu, MAIN_CPU_CLOCK_PAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &vt_vt1682_state::vt_vt1682_map);
	//m_maincpu->set_vblank_int("screen", FUNC(vt_vt1682_state::nmi));

	M6502(config, m_soundcpu, SOUND_CPU_CLOCK_PAL);
	m_soundcpu->set_addrmap(AS_PROGRAM, &vt_vt1682_state::vt_vt1682_sound_map);

	VT_VT1682_TIMER(config, m_soundcpu_timer_a_dev, SOUND_CPU_CLOCK_PAL);
	m_soundcpu_timer_a_dev->write_irq_callback().set(FUNC(vt_vt1682_state::soundcpu_timera_irq));
	m_soundcpu_timer_a_dev->set_sound_timer(); // different logging conditions
	VT_VT1682_TIMER(config, m_soundcpu_timer_b_dev, SOUND_CPU_CLOCK_PAL);
	m_soundcpu_timer_b_dev->write_irq_callback().set(FUNC(vt_vt1682_state::soundcpu_timerb_irq));
	VT_VT1682_TIMER(config, m_system_timer_dev, MAIN_CPU_CLOCK_PAL);
	m_system_timer_dev->write_irq_callback().set(FUNC(vt_vt1682_state::maincpu_timer_irq));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(0));
	m_screen->set_refresh_hz(50.0070);
	m_screen->set_size(300, 312); // 312? for PAL
	m_screen->set_visarea(0, 256-1, 0, 240-1);
	m_screen->set_screen_update(FUNC(vt_vt1682_state::screen_update));
}

void vt_vt1682_state::vt_vt1682_common(machine_config& config)
{
	TIMER(config, "scantimer").configure_scanline(FUNC(vt_vt1682_state::scanline), "screen", 0, 1);
	TIMER(config, m_render_timer).configure_generic(FUNC(vt_vt1682_state::line_render_start));

	VT_VT1682_ALU(config, m_maincpu_alu, 0);
	VT_VT1682_ALU(config, m_soundcpu_alu, 0);
	m_soundcpu_alu->set_sound_alu(); // different logging conditions

	config.set_maximum_quantum(attotime::from_hz(6000));

	ADDRESS_MAP_BANK(config, m_fullrom).set_map(&vt_vt1682_state::rom_map).set_options(ENDIANNESS_NATIVE, 8, 25, 0x2000000);

	ADDRESS_MAP_BANK(config, m_spriteram).set_map(&vt_vt1682_state::spriteram_map).set_options(ENDIANNESS_NATIVE, 8, 11, 0x800);
	ADDRESS_MAP_BANK(config, m_vram).set_map(&vt_vt1682_state::vram_map).set_options(ENDIANNESS_NATIVE, 8, 16, 0x10000);

	PALETTE(config, m_palette).set_format(palette_device::xRGB_555, 0x200).set_endianness(ENDIANNESS_LITTLE);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_test);

	VT_VT1682_IO(config, m_io, 0);
	VT_VT1682_UIO(config, m_uio, 0);

	SPEAKER(config, "speaker", 2).front();

	DAC_12BIT_R2R(config, m_leftdac, 0).add_route(0, "speaker", 0.5, 0); // unknown 12-bit DAC
	DAC_12BIT_R2R(config, m_rightdac, 0).add_route(0, "speaker", 0.5, 1); // unknown 12-bit DAC
}


void vt_vt1682_state::vt_vt1682(machine_config &config)
{
	vt_vt1682_ntscbase(config);
	vt_vt1682_common(config);
}

void intec_interact_state::machine_start()
{
	vt_vt1682_state::machine_start();

	save_item(NAME(m_previous_port_b));
	save_item(NAME(m_input_pos));
	save_item(NAME(m_current_bank));
	save_item(NAME(m_transition_time));
}

void intec_interact_state::machine_reset()
{
	vt_vt1682_state::machine_reset();
	m_previous_port_b = 0xf;
	m_input_pos = 0;
	m_current_bank = 0;
	if (m_bank)
		m_bank->set_entry(m_current_bank & 0x03);
}


void vt1682_exsport_state::machine_start()
{
	vt_vt1682_state::machine_start();

	save_item(NAME(m_old_portb));
	save_item(NAME(m_portb_shiftpos));
	save_item(NAME(m_p1_latch));
	save_item(NAME(m_p2_latch));
}

void vt1682_exsport_state::machine_reset()
{
	vt_vt1682_state::machine_reset();

	m_old_portb = 0;
	m_portb_shiftpos = 0;
	m_p1_latch = 0;
	m_p2_latch = 0;
}

void intec_interact_state::ext_rombank_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: ext_rombank_w writing: %1x\n", machine().describe_context(), data);

	// Seems no way to unset a bank once set? program will write 0 here, and even taking into account direction
	// registers that would result in the bank bits being cleared, when running from a higher bank, which
	// crashes the program.  The game offers no 'back' option, so maybe this really is the correct logic.

	if (data & 0x01)
		m_current_bank |= 1;

	if (data & 0x02)
		m_current_bank |= 2;

	m_bank->set_entry(m_current_bank & 0x03);
};


void intec_interact_state::porta_w(uint8_t data)
{
	if (data != 0xf)
	{
		LOGMASKED(LOG_OTHER, "%s: porta_w writing: %1x\n", machine().describe_context(), data & 0xf);
	}
}


static INPUT_PORTS_START( intec )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)

	PORT_START("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_START1 ) // Selects games
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("Select") // used on first screen to choose which set of games
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) // Fires in Tank

	PORT_START("IN2") // are these used? 2 player games all seem to be turn based? (Aqua-Mix looks like it should be 2 player but nothing here starts a 2 player game, maybe mapped in some other way?)
	PORT_DIPNAME( 0x01, 0x01, "IN2" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN3")
	PORT_DIPNAME( 0x01, 0x01, "IN3" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( miwi2 )
	PORT_INCLUDE( intec )

	PORT_MODIFY("IN3") // the 2nd drum appears to act like a single 2nd player controller? (even if none of the player 2 controls work in this port for intec?)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) // Pink Drum in Drum Master
INPUT_PORTS_END

static INPUT_PORTS_START( 110dance )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Pad Up-Right")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Pad Up-Left")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Back")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START1 ) PORT_NAME("Select / Start")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_NAME("Pad Up") PORT_16WAY // NOT A JOYSTICK!!
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_NAME("Pad Down") PORT_16WAY
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_NAME("Pad Left") PORT_16WAY
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_NAME("Pad Right") PORT_16WAY
INPUT_PORTS_END

static INPUT_PORTS_START( gm235upc )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON3 )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON4 )
INPUT_PORTS_END

static INPUT_PORTS_START( lxts3 )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
INPUT_PORTS_END

static INPUT_PORTS_START( icb )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
INPUT_PORTS_END


static INPUT_PORTS_START( njp60in1 )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON3 )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON2 )
INPUT_PORTS_END

static INPUT_PORTS_START( exsprt48 )
	PORT_START("P1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)

	PORT_START("P2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START2 )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
INPUT_PORTS_END

static INPUT_PORTS_START( dance555 )
	PORT_START("P1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_NAME("Pad Up") PORT_16WAY // NOT A JOYSTICK!!
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_NAME("Pad Down") PORT_16WAY
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_NAME("Pad Left") PORT_16WAY
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_NAME("Pad Right") PORT_16WAY

	PORT_START("P2")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( mx10 )
	PORT_START("UIOB")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) ) // this is probably some kind of power switch
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("Menu")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
INPUT_PORTS_END

static INPUT_PORTS_START( anpncpc )
	PORT_START("IN0")
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

// this controller code is just designed to feed the games with data they're happy with, it probably has no grounds in reality
// as I don't know how they really work.  presumably wireless with timeouts, sending signals for brief periods that need to be
// picked up on, although that said, there are some very short (128 read on status) timeout loops in the code that will force
// input to 0 if they fail

// note, the real hardware has multiple 'motion' accessories, but in reality they all just act like a button press

// inputs aren't working correctly on ii8in1, you can change to the bowling game, and select that, but select doesn't continue
// to move between games, why not?  ram address 0x6c contains current selection if you want to manually change it to start
// other games.  maybe it's waiting on some status from the sound cpu?

uint8_t intec_interact_state::porta_r()
{
	uint8_t ret = 0x0;// = machine().rand() & 0xf;

	switch (m_input_pos)
	{
	case 0x1: ret = m_io_p1->read(); break;
	case 0x2: ret = m_io_p2->read(); break;
	case 0x3: ret = m_io_p3->read(); break;
	case 0x4: ret = m_io_p4->read(); break;
	}

	LOGMASKED(LOG_OTHER, "%s: porta_r returning: %1x (INPUTS) (with input position %d)\n", machine().describe_context(), ret, m_input_pos);
	return ret;
}

uint8_t intec_interact_state::portc_r()
{
	uint8_t ret = 0x0;
	if (machine().time() >= m_transition_time + attotime::from_usec(100))
		ret |= 1;
	LOGMASKED(LOG_OTHER, "%s: portc_r returning: %1x (CONTROLLER INPUT SENSE) @ %s\n", machine().describe_context(), ret, machine().time().to_string());
	return ret;
}

void intec_interact_state::portb_w(uint8_t data)
{
	LOGMASKED(LOG_OTHER, "%s: portb_w writing: %1x\n", machine().describe_context(), data & 0xf);

	if ((m_previous_port_b & 0x1) != (data & 0x1))
	{
		if (data & 0x1)
		{
			// 0x1 low -> high
			LOGMASKED(LOG_OTHER, "low to high @ %s\n", machine().time().to_string());
		}
		else
		{
			// 0x1 high -> low
			LOGMASKED(LOG_OTHER, "high to low @ %s\n", machine().time().to_string());
		}

		attotime now = machine().time();
		if (now < m_transition_time + attotime::from_usec(100))
		{
			m_input_pos++;
			m_transition_time = now;
		}
		else if (!(data & 0x1))
		{
			m_input_pos = 0;
			m_transition_time = now;
		}
		LOGMASKED(LOG_OTHER, "input pos is %d\n", m_input_pos);
	}

	m_previous_port_b = data;
}

void vt1682_exsport_state::clock_joy2()
{
	m_portb_shiftpos++;
}

uint8_t vt1682_exsport_state::uiob_r()
{
	int p1bit = (m_p1_latch >> m_portb_shiftpos) & 1;
	int p2bit = (m_p2_latch >> m_portb_shiftpos) & 1;

	return (p1bit << 1) | (p2bit << 3);
};

void vt1682_exsport_state::uiob_w(uint8_t data)
{
	if ((m_old_portb & 0x01) != (data & 0x01))
	{
		if (!(data & 0x01))
		{
			m_portb_shiftpos = 0;

			//logerror("%s: reset shift\n", machine().describe_context());

			m_p1_latch = m_io_p1->read();
			m_p2_latch = m_io_p2->read();
		}
	}
	m_old_portb = data;
}


void intec_interact_state::intech_interact(machine_config& config)
{
	vt_vt1682_ntscbase(config);
	vt_vt1682_common(config);

	m_io->porta_in().set(FUNC(intec_interact_state::porta_r));
	m_io->porta_out().set(FUNC(intec_interact_state::porta_w));

	m_io->portb_out().set(FUNC(intec_interact_state::portb_w));

	m_io->portc_in().set(FUNC(intec_interact_state::portc_r));
	m_io->portc_out().set(FUNC(intec_interact_state::portc_w));

	m_io->portd_in().set(FUNC(intec_interact_state::portd_r));
	m_io->portd_out().set(FUNC(intec_interact_state::portd_w));

	m_leftdac->reset_routes();
	m_rightdac->reset_routes();

	config.device_remove(":speaker");

	SPEAKER(config, "mono").front_center();
	m_leftdac->add_route(0, "mono", 0.5);
	m_rightdac->add_route(0, "mono", 0.5);
}

uint8_t vt1682_lxts3_state::uio_porta_r()
{
	uint8_t ret = m_io_p1->read();
	logerror("%s: porta_r returning: %02x (INPUTS)\n", machine().describe_context(), ret);
	return ret;
}

uint8_t vt1682_dance_state::uio_porta_r()
{
	uint8_t ret = m_io_p1->read();
	logerror("%s: porta_r returning: %02x (INPUTS)\n", machine().describe_context(), ret);
	return ret;
}

void vt1682_dance_state::uio_porta_w(uint8_t data)
{
	logerror("%s: porta_w writing: %02x (INPUTS)\n", machine().describe_context(), data);
}

void intec_interact_state::intech_interact_bank(machine_config& config)
{
	intech_interact(config);

	m_uio->porta_out().set(FUNC(intec_interact_state::ext_rombank_w));
}

void vt1682_exsport_state::vt1682_exsport(machine_config& config)
{
	vt_vt1682_ntscbase(config);
	vt_vt1682_common(config);

	m_uio->portb_in().set(FUNC(vt1682_exsport_state::uiob_r));
	m_uio->portb_out().set(FUNC(vt1682_exsport_state::uiob_w));
}

void vt1682_exsport_state::vt1682_exsportp(machine_config& config)
{
	vt_vt1682_palbase(config);
	vt_vt1682_common(config);

	m_uio->portb_in().set(FUNC(vt1682_exsport_state::uiob_r));
	m_uio->portb_out().set(FUNC(vt1682_exsport_state::uiob_w));
}

void vt1682_dance_state::vt1682_dance(machine_config& config)
{
	vt_vt1682_palbase(config);
	vt_vt1682_common(config);

	M6502(config.replace(), m_maincpu, MAIN_CPU_CLOCK_PAL); // no opcode bitswap
	m_maincpu->set_addrmap(AS_PROGRAM, &vt1682_dance_state::vt_vt1682_map);

	m_leftdac->reset_routes();
	m_rightdac->reset_routes();

	config.device_remove(":speaker");

	SPEAKER(config, "mono").front_center();
	m_leftdac->add_route(0, "mono", 0.5);
	m_rightdac->add_route(0, "mono", 0.5);

	m_uio->porta_in().set(FUNC(vt1682_dance_state::uio_porta_r));
	m_uio->porta_out().set(FUNC(vt1682_dance_state::uio_porta_w));
}

void vt1682_dance_state::gm235upc(machine_config& config)
{
	vt1682_dance(config);
	m_fullrom->set_map(&vt1682_dance_state::rom_ram_map);
}



void vt1682_lxts3_state::vt1682_lxts3(machine_config& config)
{
	vt_vt1682_ntscbase(config);
	vt_vt1682_common(config);

	M6502(config.replace(), m_maincpu, MAIN_CPU_CLOCK_NTSC); // no opcode bitswap
	m_maincpu->set_addrmap(AS_PROGRAM, &vt1682_lxts3_state::vt_vt1682_map);

	m_leftdac->reset_routes();
	m_rightdac->reset_routes();

	config.device_remove(":speaker");

	SPEAKER(config, "mono").front_center();
	m_leftdac->add_route(0, "mono", 0.5);
	m_rightdac->add_route(0, "mono", 0.5);

	m_uio->porta_in().set(FUNC(vt1682_lxts3_state::uio_porta_r));
}

void vt1682_lxts3_state::vt1682_24c02(machine_config& config)
{
	vt1682_lxts3(config);

	m_io->portf_in().set(FUNC(vt1682_lxts3_state::iof_i2c_r));
	m_io->portf_out().set(FUNC(vt1682_lxts3_state::iof_i2c_w));

	I2C_24C02(config, m_seeprom);
}

void vt1682_anpncpc_state::anpncpc(machine_config& config)
{
	vt1682_lxts3(config);

	m_io->portf_in().set(FUNC(vt1682_anpncpc_state::iof_i2c_r));
	m_io->portf_out().set(FUNC(vt1682_anpncpc_state::iof_i2c_w));

	// has a GPBA02A-HL011 I/O extender on the keyboard PCB

	I2C_24C64(config, m_seeprom);
}

void vt1682_mx10_state::mx10(machine_config& config)
{
	vt_vt1682_ntscbase(config);
	vt_vt1682_common(config);

	M6502(config.replace(), m_maincpu, MAIN_CPU_CLOCK_NTSC); // no opcode bitswap
	m_maincpu->set_addrmap(AS_PROGRAM, &vt1682_mx10_state::vt_vt1682_map);

	m_uio->porta_in().set(FUNC(vt1682_mx10_state::uioa_r));
	m_uio->portb_in().set(FUNC(vt1682_mx10_state::uiob_r));
	m_uio->portb_out().set(FUNC(vt1682_mx10_state::uiob_w));

	m_leftdac->reset_routes();
	m_rightdac->reset_routes();

	config.device_remove(":speaker");

	SPEAKER(config, "mono").front_center();
	m_leftdac->add_route(0, "mono", 0.5);
	m_rightdac->add_route(0, "mono", 0.5);
}

void vt1682_lxts3_state::vt1682_unk1682(machine_config& config)
{
	vt_vt1682_palbase(config);
	vt_vt1682_common(config);

	M6502(config.replace(), m_maincpu, MAIN_CPU_CLOCK_PAL); // no opcode bitswap
	m_maincpu->set_addrmap(AS_PROGRAM, &vt1682_lxts3_state::vt_vt1682_map);

	m_leftdac->reset_routes();
	m_rightdac->reset_routes();

	config.device_remove(":speaker");

	SPEAKER(config, "mono").front_center();
	m_leftdac->add_route(0, "mono", 0.5);
	m_rightdac->add_route(0, "mono", 0.5);

	m_uio->porta_in().set(FUNC(vt1682_lxts3_state::uio_porta_r));
}


void vt1682_wow_state::vt1682_wow(machine_config& config)
{
	vt_vt1682_palbase(config);
	vt_vt1682_common(config);

	m_uio->portb_in().set(FUNC(vt1682_exsport_state::uiob_r));
	m_uio->portb_out().set(FUNC(vt1682_exsport_state::uiob_w));

	M6502_SWAP_OP_D5_D6(config.replace(), m_maincpu, MAIN_CPU_CLOCK_NTSC); // doesn't use the same bitswap as the other VT1682 games...
	m_maincpu->set_addrmap(AS_PROGRAM, &vt1682_wow_state::vt_vt1682_map);
}


void vt_vt1682_state::regular_init()
{
	m_bank->configure_entry(0, memregion("mainrom")->base() + 0x0000000);
}

void vt_vt1682_state::ignore_bk_paldepth_init()
{
	regular_init();
	m_allow_bk_paldepth_mode = false;
}


void vt1682_mx10_state::mx10_init()
{
	m_bank->configure_entry(0, memregion("mainrom")->base() + 0x0000000);
	m_bank->configure_entry(1, memregion("mainrom")->base() + 0x2000000);

	// this gets the tiles correct
	u16* src = (u16*)memregion("mainrom")->base();
	int len = memregion("mainrom")->bytes();

	std::vector<u16> buffer(len/2);
	{
		for (int i = 0; i < len/2; i++)
		{
			buffer[i] = bitswap<16>(src[i],
				15,14,2,12,
				11,10,9,8,
				7,6,5,4,
				3,13,1,0);
		}

		std::copy(buffer.begin(), buffer.end(), &src[0]);
	}

	m_allow_bk_paldepth_mode = false;
}


void intec_interact_state::banked_init()
{
	int size = memregion("mainrom")->bytes();
	for (int i = 0; i < 4; i++)
	{
		m_bank->configure_entry(i, memregion("mainrom")->base() + ((i*0x2000000) & (size-1)));
	}
}


void vt1682_lxts3_state::unk1682_init()
{
	ignore_bk_paldepth_init();

	uint8_t* ROM = memregion("mainrom")->base();
	// this jumps to a function on startup that has a bunch of jumps / accesses to the 3xxx region, which is internal ROM
	// but bypassing it allows the unit to boot.
	ROM[0x7ef43] = 0xea;
	ROM[0x7ef44] = 0xea;
	ROM[0x7ef45] = 0xea;
}

void vt1682_lxts3_state::njp60in1_init()
{
	ignore_bk_paldepth_init();

	uint8_t* ROM = memregion("mainrom")->base();
	// first jsr in the code is for some port based security(?) check, might be SEEPROM
	ROM[0x7ff44] = 0xea;
	ROM[0x7ff45] = 0xea;
	ROM[0x7ff46] = 0xea;
}

void vt1682_lxts3_state::pgs268_init()
{
	ignore_bk_paldepth_init();

	uint8_t* ROM = memregion("mainrom")->base();
	// patch out the first JSR again
	ROM[0x7ff65] = 0xea;
	ROM[0x7ff65] = 0xea;
	ROM[0x7ff66] = 0xea;
}


ROM_START( ii8in1 )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "ii8in1.bin", 0x00000, 0x2000000, CRC(7aee7464) SHA1(7a9cf7f54a350f0853a17459f2dcbef34f4f7c30) ) // 2ND HALF EMPTY
ROM_END

ROM_START( ii32in1 )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "ii32in1.bin", 0x00000, 0x2000000, CRC(ddee4eac) SHA1(828c0c18a66bb4872299f9a43d5e3647482c5925) )
ROM_END

ROM_START( zone7in1 )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "zone.bin", 0x000000, 0x1000000, CRC(50726ae8) SHA1(bcedcd61728dce7b430784585be14109af542cc2) )
ROM_END

ROM_START( zone7in1p )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "zone7in1.bin", 0x000000, 0x1000000, CRC(40bbfb80) SHA1(f65a900abea13977713bbe3b5e736e6d4d106f2c) )
ROM_END

ROM_START( dance555 )
	ROM_REGION( 0x2000000, "mainrom", ROMREGION_ERASE00 )
	ROM_LOAD( "39vf6401.u3", 0x000000, 0x800000, CRC(13b1ccef) SHA1(3eb494816a1781a5e6a45bd0562b2b8326598ef7) )
ROM_END

ROM_START( miwi2_16 )
	ROM_REGION( 0x2000000, "mainrom", ROMREGION_ERASE00 )
	ROM_LOAD( "miwi 2 16 arcade games and drum master vt168.bin", 0x00000, 0x1000000, CRC(00c115c5) SHA1(fa5fdb448dd9b963351d71fe94e2072f5c872a18) )
ROM_END

ROM_START( miwi2_7 )
	ROM_REGION( 0x2000000, "mainrom", ROMREGION_ERASE00 )
	ROM_LOAD( "miwi 2 sports 7 in 1 vt168.bin", 0x00000, 0x1000000, CRC(fcefb956) SHA1(fea8f041d42bcbae3716ce8b942a01e64504061e) )
ROM_END

ROM_START( intact89 )
	ROM_REGION( 0x4000000, "mainrom", 0 )
	ROM_LOAD( "89n1.bin", 0x00000, 0x4000000, CRC(bbcba068) SHA1(0ec1ecc55e9a7050ca20b1349b9712319fd21629) )
ROM_END

ROM_START( intg5410 )
	ROM_REGION( 0x8000000, "mainrom", 0 )
	ROM_LOAD( "interact_intg5410_111games_plus_42songs.bin", 0x00000, 0x8000000, CRC(d32dc914) SHA1(269fa262bb036ad5246dee9f83ee33dbb1543210) )
ROM_END

ROM_START( exsprt48 )
	ROM_REGION( 0x2000000, "mainrom", ROMREGION_ERASE00 )
	ROM_LOAD( "excitesportgames_48.bin", 0x00000, 0x2000000, CRC(1bf239a0) SHA1(d69c16bac5fb15c62abb5a0c0920405647205539) ) // original dump had upper 2 address lines swapped, unmarked chip, so lines were guessed when dumping
ROM_END

// differs by 2 bytes from above, the rasters glitch in MotorStorm in a different way, so it's likely an NTSC/PAL difference?
ROM_START( itvg48 )
	ROM_REGION( 0x2000000, "mainrom", ROMREGION_ERASE00 )
	ROM_LOAD( "48in1sports.bin", 0x00000, 0x2000000, CRC(8e490541) SHA1(aeb01b3d7229fc888b36aaa924fe6b10597a7783) )
ROM_END

ROM_START( xing48 )
	ROM_REGION( 0x2000000, "mainrom", ROMREGION_ERASE00 )
	ROM_LOAD( "xing48in1.bin", 0x00000, 0x0800000, CRC(c601a4ae) SHA1(ec1219ede01a48df6bfd01675e715f6b13d2b43e) )
	ROM_CONTINUE(0x1000000, 0x0800000)
	ROM_CONTINUE(0x0800000, 0x0800000)
	ROM_CONTINUE(0x1800000, 0x0800000)
ROM_END


ROM_START( wowwg )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "msp55lv128.bin", 0x00000, 0x1000000, CRC(f607c40c) SHA1(66d3960c3b8fbab06a88cf039419c79a6c8633f0) )
	ROM_RELOAD(0x1000000,0x1000000)
ROM_END

ROM_START( unk1682 )
	ROM_REGION( 0x1000, "internal", 0 )
	// this appears to use the internal ROM on startup, so mark it as missing
	ROM_LOAD( "101in1.internal.rom", 0x00000, 0x1000, NO_DUMP )

	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "vt1682_101in1.bin", 0x00000, 0x2000000, CRC(82879200) SHA1(c1977d1733f8849326286102c0755629d0406ec4) )

	// also has a 24c02n SEEPROM, no accesses noted (maybe accessed from 'internal ROM' code?)
	// note, this could be mismatched, it came from a VT1682-896 20120410 PCB with ROM already removed
	ROM_REGION( 0x100, "seeprom", 0 )
	ROM_LOAD( "24c02.u2", 0x00000, 0x100, CRC(ee89332c) SHA1(aaa90b6bb47a60e44a98795c4e1ee0c64408ec92) )
ROM_END


ROM_START( njp60in1 )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "60-in-1.bin", 0x00000, 0x2000000, CRC(7b2ee951) SHA1(fc7c214704908b85676efc64a21930483d24a457) )

	// also has a 24c02n SEEPROM, seems to access it on startup (security check?)
ROM_END

ROM_START( pgs268 )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "4000-256a.u7", 0x00000, 0x2000000, CRC(4af648a8) SHA1(c60677ac0a31814bad4aeb80b2605dc7e767a3f6) )

	// 24c02 SEEPROM, seems to access it on startup (security check?)
	ROM_REGION( 0x100, "seeprom", 0 )
	ROM_LOAD( "24c02.bin", 0x00000, 0x100, CRC(db0e3f75) SHA1(23328dcff6f46f1ec0297752a654d43e2650b2e4) )
ROM_END


ROM_START( 110dance )
	ROM_REGION( 0x2000000, "mainrom", 0 )
	ROM_LOAD( "110songdancemat.bin", 0x00000, 0x2000000, CRC(cd668e41) SHA1(975bfe05f4cce047860b05766bc8539218f6014f) )
ROM_END

ROM_START( lxts3 )
	ROM_REGION( 0x2000000, "mainrom", ROMREGION_ERASE00 )
	ROM_LOAD( "lexibooktoystory_mx29lv640mt_00c2227e.bin", 0x00000, 0x800000, CRC(91344ae7) SHA1(597fc4a27dd1fb6e6f5fda1c4ea237c07e9dba71))
ROM_END


ROM_START( icb_ts )
	ROM_REGION( 0x2000000, "mainrom", ROMREGION_ERASE00 )
	ROM_LOAD( "toystory.bin", 0x00000, 0x400000, CRC(396f8aff) SHA1(b149470306cd77558666b7ab90fa3fe4e95b6c60) )
ROM_END

ROM_START( icb_car )
	ROM_REGION( 0x2000000, "mainrom", ROMREGION_ERASE00 )
	ROM_LOAD( "kh26lv320.u2", 0x00000, 0x400000, CRC(88132171) SHA1(55a9ac8c1f49dfc116565ac11149cbd4eef90938) )
ROM_END

ROM_START( icb_dp )
	ROM_REGION( 0x2000000, "mainrom", ROMREGION_ERASE00 )
	ROM_LOAD( "princesscoloringbook.bin", 0x00000, 0x400000, CRC(253a0245) SHA1(1b902f179eed469cf749aaa9169fede290ce1031) )
ROM_END

ROM_START( gm235upc )
	ROM_REGION( 0x2000000, "mainrom", ROMREGION_ERASE00 )
	ROM_LOAD( "39vf3201.u3", 0x00000, 0x400000, CRC(182f8a2c) SHA1(7be56e1063cc8dbb78c419f5adc05b8cd65c8e2f))
	// also has RAM
ROM_END

ROM_START( anpncpc )
	ROM_REGION( 0x2000000, "mainrom", ROMREGION_ERASE00 )
	ROM_LOAD( "s29gl064a10tfir3.u12", 0x00000, 0x800000, CRC(7a801dd5) SHA1(1c956939ed62069564e1d008cbfe405e0a067092) )

	ROM_REGION( 0x2000, "seeprom", ROMREGION_ERASE00 ) // probably just settings / profiles, remove later if so
	ROM_LOAD( "l24c64.u5", 0x00000, 0x2000, CRC(968f8234) SHA1(6fb961a06892e4a577f78b3687c428b8e1c4c7d3) )
ROM_END

ROM_START( cmpmx10 )
	ROM_REGION( 0x4000000, "mainrom", ROMREGION_ERASE00 )
	// despite V1682 being able to access 32Mbytes natively, this is split into 2 4Mbyte banks with external banking
	// the 2nd bank contains an (unused) menu for a 6-in-1
	ROM_LOAD( "classicmaxpocket_vertical.u3", 0x000000, 0x400000, CRC(9d3614f9) SHA1(e5de00b23eb1a2d39c524f5b5aed3b1cda44efce) )
	ROM_CONTINUE(0x2000000,0x400000)
ROM_END

ROM_START( cmpmx11 )
	ROM_REGION( 0x4000000, "mainrom", ROMREGION_ERASE00 )
	ROM_LOAD( "cmpmx11.bin", 0x000000, 0x800000, CRC(e1f3590b) SHA1(f78f7fc4f9a4474b5a9717dfbfc3199a5bc994ba) )
	// this set doesn't use external banking, and expects the 8Mbytes to map straight
ROM_END

} // anonymous namespace


// TODO: this is a cartridge based system (actually, verify this, it seems some versions simply had built in games) move these to SL if verified as from cartridge config
//  actually it appears that for the cart based systems these are 'fake systems' anyway, where the base unit is just a Famiclone but as soon as you plug in a cart none of
//  the internal hardware gets used at all.

CONS( 200?, ii8in1,    0,  0,  intech_interact,    intec, intec_interact_state, regular_init,  "Intec", "InterAct 8-in-1", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

CONS( 200?, ii32in1,   0,  0,  intech_interact,    intec, intec_interact_state, regular_init,  "Intec", "InterAct 32-in-1", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
// a 40-in-1 also exists which combines the above

CONS( 200?, zone7in1,  0,         0,  intech_interact,    miwi2, intec_interact_state, regular_init,  "Ultimate Products Ltd.", "Zone 7-in-1 Sports (NTSC)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 200?, zone7in1p, zone7in1,  0,  intech_interact,    miwi2, intec_interact_state, regular_init,  "Ultimate Products Ltd.", "Zone 7-in-1 Sports (PAL)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // has Fishing instead of Baseball, and Ultimate Products banners in the Football game



CONS( 200?, miwi2_16,  0,  0,  intech_interact,    miwi2, intec_interact_state, regular_init,  "Macro Winners", "MiWi2 16-in-1 + Drum Master", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // clearly older code, Highway has uncensored title screen, selection screen has 'Arcase' instead of 'Arcade'
CONS( 200?, miwi2_7,   0,  0,  intech_interact,    miwi2, intec_interact_state, regular_init,  "Macro Winners", "MiWi2 7-in-1 Sports", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
// ViMax seems to be identical software to MiWi2

CONS( 200?, intact89,  0,  0,  intech_interact_bank, miwi2, intec_interact_state, banked_init,  "Intec", "InterAct Complete Video Game - 89-in-1", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

/*
Box shows

InterAct
Complete Video Game System
Sistema Completo De Video Juegos
111 Games & 42 Songs

96 Arcade Games:
8 of them are Sports Games,
& 3 of the are Drum Master Games.
Plus 15 Shooting Games

Unit has 'InfraZone' text on it, but this isn't used anywhere in product description.

*/
CONS( 200?, intg5410,  0,  0,  intech_interact_bank, miwi2, intec_interact_state, banked_init,  "Intec", "InterAct Complete Video Game - 111 Games & 42 Songs (G5410)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // need to hook up gun controls etc. and verify others, also sometimes crashes on game change (due to crashing sound CPU?)

// Other standalone Mi Kara units should fit here as well


// the timing code for MotorStorm differs between these sets (although fails wiht our emulation in both cases, even if the game runs fine in other collections)
CONS( 200?, exsprt48,   0,         0,  vt1682_exsport,    exsprt48, vt1682_exsport_state, regular_init,  "Excite", "Excite Sports Wireless Interactive TV Game - 48-in-1 (NTSC)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // "32 Arcade, 8 Sports, 8 Stadium"
CONS( 200?, itvg48,     exsprt48,  0,  vt1682_exsportp,   exsprt48, vt1682_exsport_state, regular_init,  "TaiKee", "Interactive TV Games 48-in-1 (PAL)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // ^

// This has a different selection of games to the above, Dancing as extra under Music, Doesn't have Poker under Brain, Ball Shoot instead of 'Noshery' under Arcade
// imported by Cathay Product Sourcing Ltd. (Ireland) no other manufacturer information on box, not sure if Xing is name of manufacturer or product
CONS( 200?, xing48,     0,         0,  vt1682_exsportp,   exsprt48, vt1682_exsport_state, regular_init,  "Xing", "Xing Wireless Interactive TV Game 'Wi TV Zone' 48-in-1 (Europe, PAL)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // ^
/*
The above was also released in the US as Excite Sports Wireless Interactive TV Game - 48-in-1 with an almost identical box to exsprt48 unit, but with the different games noted.

It is still advertised as 48-in-1, 8 Interactive Sports Games, 8 Olympic games, 32 Arcade Games
see https://www.youtube.com/watch?v=tHMX71daHAk

This might be a regional / store thing if some places didn't want to sell a unit with a Poker game in it?
*/

// Timings are broken in the Bomberman game ('Explosion') even on real hardware (raster effect to keep status bar in place doesn't work) because the game is still coded to use NTSC timings even if this is a PAL unit.  This was fixed in other PAL releases (eg. 110dance)
// 'Riding Horse' on the other hand actually needs PAL timings, so this unit clearly was designed for PAL regions, however 'Explosion' was left broken.
CONS( 200?, wowwg,  0,  0,  vt1682_wow, exsprt48, vt1682_wow_state, regular_init, "Wow", "Wow Wireless Gaming (PAL)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND) // needs high colour line mode for main menu


CONS( 200?, 110dance,  0,  0,  vt1682_dance, 110dance, vt1682_dance_state, regular_init, "<unknown>", "Retro Dance Mat (110 song Super StepMania + 9-in-1 games) (PAL)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND)

// songs 5-8 are repeats of songs 1-4, but probably not a bug?
CONS( 200?, dance555,  0,  0,  vt1682_exsportp,   dance555, vt1682_exsport_state, regular_init,  "Subor", "Sports and Dance Fit Games Mat D-555 (PAL)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )


// NJ Pocket 60-in-1 (NJ-250) is meant to have similar games to the mini-games found in wowwg and 110dance, so almost certainly fits here

// manual explicitly states it has NTSC output only (unit can be connected to a TV) and both Ranning Horse + Explosion (Bomberman) are the NTSC versions
// has 21.477 Mhz XTAL
CONS( 200?, njp60in1,  0,  0,   vt1682_24c02, njp60in1, vt1682_lxts3_state, njp60in1_init, "<unknown>", "NJ Pocket 60-in-1 handheld 'X zero' (NTSC)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND)

// fewer than 268 games, there are repeats
CONS( 200?, pgs268,  0,  0,   vt1682_24c02, njp60in1, vt1682_lxts3_state, pgs268_init, "<unknown>", "Portable Game Station 268-in-1", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND)

// this appears to be related to the NJ Pocket, claims 101-in-1 but has some duplicates.
// Like the 'Wow Wireless gaming' it incorrectly mixes the PAL version of 'Ranning Horse' with the NTSC version of 'Bomberman', it has no TV output.
// has 26.6017 Mhz (6xPAL) XTAL
CONS( 200?, unk1682,  0,  0,   vt1682_unk1682, lxts3, vt1682_lxts3_state, unk1682_init, "<unknown>", "unknown VT1682-based 101-in-1 handheld (PAL)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND)

CONS( 2010, lxts3,    0,  0,   vt1682_lxts3, lxts3, vt1682_lxts3_state, regular_init,  "Lexibook", "Toy Story 3 (Lexibook)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

CONS( 200?, icb_ts,   0,  0,   vt1682_lxts3, icb,   vt1682_lxts3_state, regular_init,  "Techno Source", "Interactive Coloring Book: Disney / Pixar Toy Story", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 200?, icb_car,  0,  0,   vt1682_lxts3, icb,   vt1682_lxts3_state, regular_init,  "Techno Source", "Interactive Coloring Book: Disney / Pixar Cars", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 200?, icb_dp,   0,  0,   vt1682_lxts3, icb,   vt1682_lxts3_state, regular_init,  "Techno Source", "Interactive Coloring Book: Disney Princess", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// there are products on SunPlus type hardware with nearly identical shells 'Mi DiGi World' / 'Mi Digi Diary'
// needs IO ports on sound CPU side, needs write access to space for RAM (inputs are 'mini-keyboard' style)
CONS( 200?, gm235upc,  0,  0,  gm235upc, gm235upc, vt1682_dance_state, regular_init, "TimeTop", "Ultimate Pocket Console GM-235", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING )

// "Anpanman Color PC" (アンパンマンカラーパソコン)
CONS( 2009, anpncpc,   0,  0,  anpncpc, anpncpc, vt1682_anpncpc_state, regular_init,  "Bandai", "Anpanman Color PC (Japan)", MACHINE_NOT_WORKING )

// as with others the cmpmx10 and cmpmx11 have minor offset issues in some games, you can see it easily in Jewel Master
// 2007 is the copyright date shown on all the games, but the unit could have been released later
CONS( 2007, cmpmx11,     0,        0,  mx10, mx10, vt1682_mx10_state, mx10_init, "Jungle Soft (Premier Portfolio International license)",    "Classic Max Pocket PCMX11 - 12 in 1 Colour Games Console (horizontal, France)", MACHINE_IMPERFECT_GRAPHICS )
// this unit has a vertical screen, and the games are designed for that aspect
// only Jungle Soft is shown on box for manufacturer details, 30-in-1 versions also exist
// see https://bootleggames.fandom.com/wiki/Classic_Max_Pocket for other units with these games
CONS( 2007, cmpmx10,     0,        0,  mx10, mx10, vt1682_mx10_state, mx10_init, "Jungle Soft",    "Classic Max Pocket Mx-10 - 12 in 1 (vertical)", ROT270 | MACHINE_IMPERFECT_GRAPHICS )



vt220.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Jonathan Gevaryahu
/***************************************************************************

    DEC VT220

    30/06/2009 Skeleton driver.

    The VT220 exists in two major hardware revisions. The Pocket Service
    Guide has separate lists of components for earlier and later models,
    with the following being those that differ:

                                    A, B, and C     D, E, and F
        Terminal controller         70-20814-02     70-23363-01
        Power supply/monitor board  70-20624-02     70-23361-01
        Transformer                 70-20772-02     70-23362-02
        CRT/bezel/yoke (white)      70-20662-04     70-23360-01
                       (green)      70-20662-05     70-23360-02
                       (amber)      70-20662-06     70-23360-03
        Danish keyboard             LK201-AD        LK201-ED
        British keyboard            LK201-AE        LK201-EE
        Norwegian keyboard          LK201-AN        LK201-EN

    The original VT220 Technical Manual (EK-VT220-TM) covers Models A, B,
    and C only. Though it was later reprinted with an addendum for Models
    D, E, and F, this has not been found. The available schematics are
    inapplicable to later models, which have an altogether different memory
    map and, evidently, use some different IC types: an ER5911 rather than
    X2212 as non-volatile memory and, in place of the CRT9007, some sort
    of custom video gate array (which might even be clocked differently).

****************************************************************************/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
//#include "machine/eepromser.h"
#include "machine/mc68681.h"
#include "machine/ram.h"
//#include "machine/x2212.h"
//#include "video/crt9007.h"
#include "emupal.h"
#include "screen.h"


namespace {

class vt220_state : public driver_device
{
public:
	vt220_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ram(*this, RAM_TAG)
	{ }

	void vt220(machine_config &config);
	void vt220a(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;
	uint32_t screen_update_vt220(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	required_device<i8051_device> m_maincpu;
	required_device<ram_device> m_ram;
	void vt220_io(address_map &map) ATTR_COLD;
	void vt220_mem(address_map &map) ATTR_COLD;
	void vt220a_io(address_map &map) ATTR_COLD;
	void vt220a_mem(address_map &map) ATTR_COLD;
};


void vt220_state::vt220_mem(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("maincpu", 0);
}

void vt220_state::vt220a_mem(address_map &map)
{
	map(0x0000, 0xffff).rom().region("maincpu", 0);
}

void vt220_state::vt220_io(address_map &map)
{
	map.unmap_value_high();
	map(0x2000, 0x2fff).mirror(0xc000).ram();
	map(0x3800, 0x380f).mirror(0xc7f0).rw("duart", FUNC(scn2681_device::read), FUNC(scn2681_device::write));
}

void vt220_state::vt220a_io(address_map &map)
{
	map.unmap_value_high();
}

/* Input ports */
static INPUT_PORTS_START( vt220 )
INPUT_PORTS_END

void vt220_state::machine_reset()
{
	memset(m_ram->pointer(),0,16*1024);
}

void vt220_state::video_start()
{
}

uint32_t vt220_state::screen_update_vt220(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}


void vt220_state::vt220(machine_config &config)
{
	/* basic machine hardware */
	I8051(config, m_maincpu, XTAL(11'059'200)); // from schematic for earlier version
	m_maincpu->set_addrmap(AS_PROGRAM, &vt220_state::vt220_mem);
	m_maincpu->set_addrmap(AS_IO, &vt220_state::vt220_io);
	m_maincpu->port_in_cb<1>().set_constant(0); // ???

	scn2681_device &duart(SCN2681(config, "duart", XTAL(3'686'400)));
	duart.irq_cb().set_inputline("maincpu", MCS51_INT1_LINE);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	screen.set_screen_update(FUNC(vt220_state::screen_update_vt220));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("16K");
}

void vt220_state::vt220a(machine_config &config)
{
	vt220(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &vt220_state::vt220a_mem);
	m_maincpu->set_addrmap(AS_IO, &vt220_state::vt220a_io);
}

/* ROM definitions */
ROM_START(vt220)
	ROM_REGION(0x8000, "maincpu", ROMREGION_ERASEFF)
	ROM_LOAD("23-178e6.bin", 0x0000, 0x8000, CRC(cce5088c) SHA1(4638304729d1213658a96bb22c5211322b74d8fc) )

	ROM_REGION(0x4000, "chargen", ROMREGION_ERASEFF)
	ROM_LOAD("23-348e4.e13", 0x0000, 0x2000, CRC(994f3e37) SHA1(fe72a9fe9adb3a24743a6288d88ae07570cfea9a) )
ROM_END

ROM_START(vt220a)
	ROM_REGION(0x10000, "maincpu", ROMREGION_ERASEFF)
#if 0
	// these ROMs are listed in the schematics; it is unknown whether or not they were actually shipped
	ROM_LOAD("23-012e5.e3", 0x0000, 0x4000, NO_DUMP) // location e3 with jumper w7 present and w6 cut
	ROM_RELOAD(0x4000, 0x4000) // the rom also maps to 4000-7fff as a14 is unconnected
	ROM_LOAD("23-248e4.e4", 0x8000, 0x2000, NO_DUMP)
	// a000-bfff may be open bus or may be a mirror of above depending on whether the rom uses PGM/A13 as a secondary enable or not
	ROM_RELOAD(0xc000, 0x2000)
	// e000-ffff may be open bus or may be a mirror of above depending on whether the rom uses PGM/A13 as a secondary enable or not
#endif
	ROM_LOAD("23-183e5.e3", 0x8000, 0x4000, CRC(2848db40) SHA1(b3b029ef964c86ede68ad1b2854cfad766f20af0))
	ROM_RELOAD(0xc000, 0x4000)
	ROM_LOAD("23-182e5.e4", 0x0000, 0x4000, CRC(c759bf9f) SHA1(6fe21e8eb9576fbcda76d7909b07859db0793c4e))
	ROM_RELOAD(0x4000, 0x4000)
	ROM_LOAD("23-011m1.e1", 0x0000, 0x1000, CRC(6c4930a9) SHA1(1200a5dbf431017a11a51b5c4c9b4e7952b0a2bb)) // 8051 internal code

	ROM_REGION(0x4000, "chargen", ROMREGION_ERASEFF)
#if 0
	// this ROM is listed in the schematics
	ROM_LOAD("23-247e4.e13", 0x0000, 0x2000, NO_DUMP)
#endif
	ROM_LOAD("23-348e4.e13", 0x0000, 0x2000, CRC(994f3e37) SHA1(fe72a9fe9adb3a24743a6288d88ae07570cfea9a)) // this can maybe be read as well as a read/writable ram for custom characters which lives ?above? it in chargen address space by setting a bit in a config register. I haven't figured out where in 8051 address space it appears when readable nor where the ram appears.
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  STATE        INIT        COMPANY                          FULLNAME               FLAGS */
COMP( 1983, vt220,  0,      0,      vt220,   vt220, vt220_state, empty_init, "Digital Equipment Corporation", "VT220 (Version 2.3)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1983, vt220a, vt220,  0,      vt220a,  vt220, vt220_state, empty_init, "Digital Equipment Corporation", "VT220 (Version 2.1)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



vt240.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Jonathan Gevaryahu
/***************************************************************************

        DEC VT240

****************************************************************************/

#include "emu.h"
#include "lk201.h"

#include "ms7004.h"

#include "bus/rs232/rs232.h"
#include "cpu/i8085/i8085.h"
#include "cpu/t11/t11.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/mc68681.h"
#include "machine/bankdev.h"
#include "machine/x2212.h"
#include "video/upd7220.h"
#include "emupal.h"
#include "screen.h"


namespace {

class vt240_state : public driver_device
{
public:
	vt240_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_i8085(*this, "charcpu")
		, m_i8251(*this, "i8251")
		, m_duart(*this, "duart")
		, m_host(*this, "host")
		, m_hgdc(*this, "upd7220")
		, m_bank(*this, "bank")
		, m_nvram(*this, "x2212")
		, m_palette(*this, "palette")
		, m_rom(*this, "maincpu")
		, m_video_ram(*this, "vram")
		, m_monitor(*this, "monitor")
		, m_lk201(*this, "lk201")
	{
	}

	void mc7105(machine_config &config);
	void vt240(machine_config &config);

private:
	required_device<t11_device> m_maincpu;
	required_device<i8085a_cpu_device> m_i8085;
	required_device<i8251_device> m_i8251;
	required_device<scn2681_device> m_duart;
	required_device<rs232_port_device> m_host;
	required_device<upd7220_device> m_hgdc;
	required_device<address_map_bank_device> m_bank;
	required_device<x2212_device> m_nvram;
	required_device<palette_device> m_palette;
	required_region_ptr<uint16_t> m_rom;
	required_shared_ptr<uint16_t> m_video_ram;
	required_ioport m_monitor;
	optional_device<lk201_device> m_lk201;

	void write_keyboard_clock(int state);
	void i8085_rdy_w(int state);
	void lben_w(int state);
	void tx_w(int state);
	void t11_reset_w(int state);
	int i8085_sid_r();
	uint8_t i8085_comm_r(offs_t offset);
	void i8085_comm_w(offs_t offset, uint8_t data);
	uint8_t t11_comm_r();
	void t11_comm_w(uint8_t data);
	uint8_t duart_r(offs_t offset);
	void duart_w(offs_t offset, uint8_t data);
	void duartout_w(uint8_t data);
	uint8_t mem_map_cs_r(offs_t offset);
	void mem_map_cs_w(offs_t offset, uint8_t data);
	uint8_t ctrl_r();
	void mem_map_sel_w(uint8_t data);
	uint8_t char_buf_r();
	void char_buf_w(uint8_t data);
	void patmult_w(uint8_t data);
	void vpat_w(uint8_t data);
	uint16_t vram_r(offs_t offset);
	void vram_w(offs_t offset, uint16_t data);
	uint8_t vom_r(offs_t offset);
	void vom_w(offs_t offset, uint8_t data);
	uint8_t nvr_store_r();
	void nvr_store_w(uint8_t data);
	void mask_w(uint8_t data);
	void reg0_w(uint8_t data);
	void reg1_w(uint8_t data);
	void lu_w(uint8_t data);
	void hbscrl_w(uint8_t data);
	void lbscrl_w(uint8_t data);
	uint16_t mem_r(offs_t offset, uint16_t mem_mask);
	void mem_w(offs_t offset, uint16_t data, uint16_t mem_mask);

	void init_vt240();
	virtual void machine_reset() override ATTR_COLD;
	UPD7220_DISPLAY_PIXELS_MEMBER(hgdc_draw);
	void irq_encoder(int irq, int state);
	void irq7_w(int state);
	void irq9_w(int state);
	void irq13_w(int state);

	uint8_t m_i8085_out, m_t11_out, m_i8085_rdy, m_t11;
	uint8_t m_mem_map[16];
	uint8_t m_mem_map_sel;
	uint8_t m_char_buf[16];
	uint8_t m_char_idx, m_mask, m_reg0, m_reg1, m_lu;
	uint8_t m_vom[16];
	uint8_t m_vpat, m_patmult, m_patcnt, m_patidx;
	uint16_t m_irqs;
	bool m_lb;
	uint16_t m_scrl;

	void bank_map(address_map &map) ATTR_COLD;
	void upd7220_map(address_map &map) ATTR_COLD;
	void vt240_char_io(address_map &map) ATTR_COLD;
	void vt240_char_mem(address_map &map) ATTR_COLD;
	void vt240_mem(address_map &map) ATTR_COLD;
};

void vt240_state::irq_encoder(int irq, int state)
{
	if(state == ASSERT_LINE)
		m_irqs |= (1 << irq);
	else
		m_irqs &= ~(1 << irq);

	int i;
	for(i = 15; i > 0; i--)
	{
		if(m_irqs & (1 << i))
			break;
	}
	m_maincpu->set_input_line(t11_device::CP3_LINE, (i & 8) ? ASSERT_LINE : CLEAR_LINE);
	m_maincpu->set_input_line(t11_device::CP2_LINE, (i & 4) ? ASSERT_LINE : CLEAR_LINE);
	m_maincpu->set_input_line(t11_device::CP1_LINE, (i & 2) ? ASSERT_LINE : CLEAR_LINE);
	m_maincpu->set_input_line(t11_device::CP0_LINE, (i & 1) ? ASSERT_LINE : CLEAR_LINE);
}

void vt240_state::irq7_w(int state)
{
	irq_encoder(7, state);
}

void vt240_state::irq9_w(int state)
{
	irq_encoder(9, state);
}

void vt240_state::irq13_w(int state)
{
	irq_encoder(13, state);
}

void vt240_state::write_keyboard_clock(int state)
{
	m_i8251->write_txc(state);
	m_i8251->write_rxc(state);
}

void vt240_state::lben_w(int state)
{
	m_lb = state ? false : true;
}

void vt240_state::t11_reset_w(int state)
{
	if(state == ASSERT_LINE)
	{
		m_duart->reset();
		m_i8251->reset();
		m_nvram->recall(ASSERT_LINE);
		m_nvram->recall(CLEAR_LINE);
	}
}

void vt240_state::tx_w(int state)
{
	if(m_lb)
		m_i8251->write_rxd(state);
	else
		m_lk201->rx_w(state);
}

void vt240_state::i8085_rdy_w(int state)
{
	irq_encoder(3, state ? CLEAR_LINE : ASSERT_LINE);
	m_i8085_rdy = state;
}

int vt240_state::i8085_sid_r()
{
	return m_t11 ? CLEAR_LINE : ASSERT_LINE;
}

UPD7220_DISPLAY_PIXELS_MEMBER( vt240_state::hgdc_draw )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();

	if(!BIT(m_reg0, 7))
	{
		vram_w(address, 0);
		vram_w((0x10000 + address), 0);
	}

	int const gfx1 = m_video_ram[(address & 0x3fff)];
	int const gfx2 = m_video_ram[((address & 0x3fff) + 0x4000)];

	bool const color = m_monitor->read() ? true : false;
	for(int xi=0;xi<16;xi++)
	{
		uint8_t const vom = BIT(gfx1, xi) | (BIT(gfx2, xi) << 1) | ((m_reg0 & 3) << 2);
		bitmap.pix(y, x + xi) = palette[color ? (vom + 16) : vom];
	}
}

uint8_t vt240_state::t11_comm_r()
{
	m_t11 = 1;
	m_i8085->set_input_line(I8085_RST65_LINE, CLEAR_LINE);
	return m_t11_out;
}

void vt240_state::t11_comm_w(uint8_t data)
{
	m_i8085_out = data;
}

uint8_t vt240_state::i8085_comm_r(offs_t offset)
{
	switch(offset)
	{
		case 0:
			return m_i8085_out;
		case 2:
			m_i8085->set_input_line(I8085_RST65_LINE, CLEAR_LINE);
			m_i8085->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
			m_t11 = 1;
			break;
	}
	return 0xff;
}

void vt240_state::i8085_comm_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
		case 1:
			m_t11_out = data;
			m_t11 = 0;
			m_i8085->set_input_line(I8085_RST65_LINE, ASSERT_LINE);
			break;
		case 2:
			m_i8085->set_input_line(I8085_RST65_LINE, CLEAR_LINE);
			m_i8085->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
			m_t11 = 1;
			break;
	}
}

uint8_t vt240_state::duart_r(offs_t offset)
{
	if(!(offset & 1))
		return m_duart->read(offset >> 1);
	return 0;
}

void vt240_state::duart_w(offs_t offset, uint8_t data)
{
	if(offset & 1)
		m_duart->write(offset >> 1, data);
}

void vt240_state::duartout_w(uint8_t data)
{
	m_host->write_rts(BIT(data, 0) ? ASSERT_LINE : CLEAR_LINE);
	m_host->write_dtr(BIT(data, 2) ? ASSERT_LINE : CLEAR_LINE);
	irq_encoder(15, BIT(data, 4) ? CLEAR_LINE : ASSERT_LINE);
	irq_encoder(14, BIT(data, 5) ? CLEAR_LINE : ASSERT_LINE);
	irq_encoder(11, BIT(data, 6) ? CLEAR_LINE : ASSERT_LINE);
	irq_encoder(10, BIT(data, 7) ? CLEAR_LINE : ASSERT_LINE);
}

uint8_t vt240_state::mem_map_cs_r(offs_t offset)
{
	return ~m_mem_map[offset];
}

void vt240_state::mem_map_cs_w(offs_t offset, uint8_t data)
{
	m_mem_map[offset] = ~data;
}

uint8_t vt240_state::ctrl_r()
{
	return m_mem_map_sel | ((m_lb ? 0 : 1) << 3) | (m_i8085_rdy << 6) | (m_t11 << 7) | (1<<5); // no modem
}

void vt240_state::mem_map_sel_w(uint8_t data)
{
	m_mem_map_sel = data & 1;
}

uint16_t vt240_state::mem_r(offs_t offset, uint16_t mem_mask)
{
	if(m_mem_map_sel)
	{
		m_bank->set_bank(m_mem_map[(offset >> 11) & 0xf]);
		return m_bank->read16(offset & 0x7ff, mem_mask);
	}
	else
		return m_rom[offset];
}

void vt240_state::mem_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if(m_mem_map_sel)
	{
		m_bank->set_bank(m_mem_map[(offset >> 11) & 0xf]);
		m_bank->write16(offset & 0x7ff, data, mem_mask);
	}
}

uint8_t vt240_state::char_buf_r()
{
	m_char_idx = 0;
	return 0xff;
}

void vt240_state::char_buf_w(uint8_t data)
{
	m_char_buf[m_char_idx++] = bitswap<8>(data, 0, 1, 2, 3, 4, 5, 6, 7);
	m_char_idx &= 0xf;
}

void vt240_state::patmult_w(uint8_t data)
{
	m_patmult = data & 0xf;
}

void vt240_state::vpat_w(uint8_t data)
{
	m_vpat = data;
	m_patcnt = m_patmult;
	m_patidx = 7;
}

uint8_t vt240_state::vom_r(offs_t offset)
{
	if(!BIT(m_reg0, 2))
		return m_vom[offset];
	// this hack passes a self test, is not a useful value normally
	// when vom read mode is disabled, the read latch is set to whatever map is
	// enabled and color is currently drawn, the self test fills the screen with 0xff
	// and reads it
	return m_vom[((m_reg0 & 3) << 2) + 3];
}

void vt240_state::vom_w(offs_t offset, uint8_t data)
{
	if(!BIT(m_reg0, 2))
	{
		m_vom[offset] = data;
		data = ~bitswap<8>(data, 1, 0, 3, 2, 5, 4, 7, 6);
		m_palette->set_pen_color(offset, pal2bit(data >> 6), pal2bit(data >> 6), pal2bit(data >> 6));
		m_palette->set_pen_color((offset + 16), pal2bit(data >> 0), pal2bit(data >> 2), pal2bit(data >> 4));
	}
}

uint16_t vt240_state::vram_r(offs_t offset)
{
	if(!BIT(m_reg0, 3) || machine().side_effects_disabled())
	{
		offset = ((offset & 0x18000) >> 1) | (offset & 0x3fff);
		return m_video_ram[offset & 0x7fff];
	}
	return 0;
}

void vt240_state::vram_w(offs_t offset, uint16_t data)
{
	uint8_t *video_ram = (uint8_t *)(&m_video_ram[0]);
	offset <<= 1;
	offset = ((offset & 0x30000) >> 1) | (offset & 0x7fff);
	if(!BIT(m_reg0, 3))
		offset |= BIT(offset, 16);
	else
	{
		if(data & 0xff00)
		{
			data >>= 8;
			offset += 1;
		}
		else
			data &= 0xff;
	}
	offset &= 0xffff;
	uint8_t chr = data;

	if(BIT(m_reg0, 4))
	{
		if(BIT(m_reg0, 6))
		{
			chr = bitswap<8>(m_vpat, m_patidx, m_patidx, m_patidx, m_patidx, m_patidx, m_patidx, m_patidx, m_patidx);
			if(m_patcnt-- == 0)
			{
				m_patcnt = m_patmult;
				if(m_patidx-- == 0)
					m_patidx = 7;
			}
		}
		else
		{
			chr = m_char_buf[m_char_idx++];
			m_char_idx &= 0xf;
		}
		int ps = ~m_lu & 3;
		for(int i = 0; i <= (ps >> 1); i++)
		{
			if(ps == 0)
				i++;
			uint8_t mem = video_ram[(offset & 0x7fff) + (0x8000 * i)];
			uint8_t out = 0, ifore = BIT(m_lu, (i ? 5 : 4)), iback = BIT(m_lu, (i ? 3 : 2));
			for(int j = 0; j < 8; j++)
				out |= BIT(chr, j) ? (ifore << j) : (iback << j);
			switch(m_lu >> 6)
			{
				case 0:
					break;
				case 1:
					out |= mem;
					break;
				case 2:
					logerror("invalid logic unit mode 2\n");
					break;
				case 3:
					out ^= ~mem;
					break;
			}
			if(!BIT(m_reg0, 3))
				out = (out & ~m_mask) | (mem & m_mask);
			else
				out = (out & data) | (mem & ~data);
			if(BIT(m_reg1, 3))
			{
				uint8_t out2 = out;
				if(BIT(m_reg1, 2))
				{
					out = video_ram[((offset & 0x7ffe) | 0) + (0x8000 * i)];
					out2 = video_ram[((offset & 0x7ffe) | 1) + (0x8000 * i)];
				}
				video_ram[((m_scrl << 1) | 0) + (0x8000 * i)] = out;
				video_ram[((m_scrl << 1) | 1) + (0x8000 * i)] = out2;
			}
			else
				video_ram[(offset & 0x7fff) + (0x8000 * i)] = out;
		}
		if(BIT(m_reg1, 3))
		{
			m_scrl += BIT(m_reg1, 1) ? -1 : 1;
			m_scrl &= 0x3fff;
		}
		return;
	}
	if(!BIT(m_reg0, 3))
		data = (chr & ~m_mask) | (video_ram[offset] & m_mask);
	else
		data = (chr & data) | (video_ram[offset] & ~data);
	if(BIT(m_reg1, 3))
	{
		uint8_t data2 = data;
		if(BIT(m_reg1, 2))
		{
			data = video_ram[(offset & ~1) | 0];
			data2 = video_ram[(offset & ~1) | 1];
		}
		video_ram[(offset & 0x8000) | (m_scrl << 1) | 0] = data;
		video_ram[(offset & 0x8000) | (m_scrl << 1) | 1] = data2;
		m_scrl += BIT(m_reg1, 1) ? -1 : 1;
		m_scrl &= 0x3fff;
	}
	else
		video_ram[offset] = data;
}

void vt240_state::mask_w(uint8_t data)
{
	m_mask = bitswap<8>(data, 0, 1, 2, 3, 4, 5, 6, 7);
}

uint8_t vt240_state::nvr_store_r()
{
	m_nvram->store(ASSERT_LINE);
	m_nvram->store(CLEAR_LINE);
	return 0;
}

void vt240_state::nvr_store_w(uint8_t data)
{
	m_nvram->store(ASSERT_LINE);
	m_nvram->store(CLEAR_LINE);
}

void vt240_state::reg0_w(uint8_t data)
{
	m_reg0 = data;
}

void vt240_state::reg1_w(uint8_t data)
{
	m_reg1 = data;
}

void vt240_state::lu_w(uint8_t data)
{
	m_lu = data;
}

void vt240_state::lbscrl_w(uint8_t data)
{
	m_scrl = (m_scrl & 0xff00) | data;
}

void vt240_state::hbscrl_w(uint8_t data)
{
	m_scrl = (m_scrl & 0xff) | ((data & 0x3f) << 8);
}

void vt240_state::bank_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x1ffff).rom().region("maincpu", 0);
	map(0x80000, 0x87fff).ram();
}

// PDF page 78 (4-25)
void vt240_state::vt240_mem(address_map &map)
{
	map.unmap_value_high();
	map(0000000, 0167777).rw(FUNC(vt240_state::mem_r), FUNC(vt240_state::mem_w));
	map(0170000, 0170037).rw(FUNC(vt240_state::mem_map_cs_r), FUNC(vt240_state::mem_map_cs_w)).umask16(0x00ff);
	map(0170040, 0170040).w(FUNC(vt240_state::mem_map_sel_w));
	map(0170100, 0170100).r(FUNC(vt240_state::ctrl_r));
	map(0170140, 0170140).rw(FUNC(vt240_state::nvr_store_r), FUNC(vt240_state::nvr_store_w));
	map(0171000, 0171003).rw(m_i8251, FUNC(i8251_device::data_r), FUNC(i8251_device::data_w)).umask16(0x00ff);
	map(0171004, 0171007).rw(m_i8251, FUNC(i8251_device::status_r), FUNC(i8251_device::control_w)).umask16(0x00ff);
	map(0172000, 0172077).rw(FUNC(vt240_state::duart_r), FUNC(vt240_state::duart_w)).umask16(0x00ff);
	map(0173000, 0173003).r(m_hgdc, FUNC(upd7220_device::read)).umask16(0x00ff);
	map(0173040, 0173077).r(FUNC(vt240_state::vom_r)).umask16(0x00ff);
	map(0173140, 0173140).r(FUNC(vt240_state::char_buf_r));
	map(0174000, 0174003).w(m_hgdc, FUNC(upd7220_device::write)).umask16(0x00ff);
	map(0174040, 0174077).w(FUNC(vt240_state::vom_w)).umask16(0x00ff);
	map(0174140, 0174140).w(FUNC(vt240_state::char_buf_w));
	map(0174400, 0174400).w(FUNC(vt240_state::patmult_w));
	map(0174440, 0174440).w(FUNC(vt240_state::mask_w));
	map(0174500, 0174500).w(FUNC(vt240_state::vpat_w));
	map(0174540, 0174540).w(FUNC(vt240_state::lu_w));
	map(0174600, 0174600).w(FUNC(vt240_state::reg0_w));
	map(0174640, 0174640).w(FUNC(vt240_state::reg1_w));
	map(0174700, 0174700).w(FUNC(vt240_state::hbscrl_w));
	map(0174740, 0174740).w(FUNC(vt240_state::lbscrl_w));
	map(0175000, 0175005).rw(FUNC(vt240_state::i8085_comm_r), FUNC(vt240_state::i8085_comm_w)).umask16(0x00ff);
	map(0176000, 0176777).rw(m_nvram, FUNC(x2212_device::read), FUNC(x2212_device::write)).umask16(0x00ff);
	// 017700x System comm logic
}

// PDF page 134 (6-9)
void vt240_state::vt240_char_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).rom().region("charcpu", 0);
	map(0x4000, 0x5fff).rom().region("charcpu", 0x8000);
	map(0x8000, 0x87ff).ram();
}

void vt240_state::vt240_char_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x01).rw(m_hgdc, FUNC(upd7220_device::read), FUNC(upd7220_device::write));
	map(0x10, 0x1f).rw(FUNC(vt240_state::vom_r), FUNC(vt240_state::vom_w));
	map(0x20, 0x20).rw(FUNC(vt240_state::t11_comm_r), FUNC(vt240_state::t11_comm_w));
	map(0x30, 0x30).rw(FUNC(vt240_state::char_buf_r), FUNC(vt240_state::char_buf_w));
	map(0x80, 0x80).w(FUNC(vt240_state::patmult_w));
	map(0x90, 0x90).w(FUNC(vt240_state::mask_w));
	map(0xa0, 0xa0).w(FUNC(vt240_state::vpat_w));
	map(0xb0, 0xb0).w(FUNC(vt240_state::lu_w));
	map(0xc0, 0xc0).w(FUNC(vt240_state::reg0_w));
	map(0xd0, 0xd0).w(FUNC(vt240_state::reg1_w));
	map(0xe0, 0xe0).w(FUNC(vt240_state::hbscrl_w));
	map(0xf0, 0xf0).w(FUNC(vt240_state::lbscrl_w));
}

void vt240_state::upd7220_map(address_map &map)
{
	map(0x00000, 0x3ffff).rw(FUNC(vt240_state::vram_r), FUNC(vt240_state::vram_w)).share("vram");
}


void vt240_state::machine_reset()
{
	m_i8251->write_cts(0);
	m_nvram->recall(ASSERT_LINE);
	m_nvram->recall(CLEAR_LINE);
	m_mem_map_sel = 0;
	m_t11 = 1;
	m_i8085_rdy = 1;
	m_char_idx = 0;
	m_patcnt = 0;
	m_patidx = 0;
	m_reg0 = 0x80;
	m_irqs = 0;
}

static const gfx_layout vt240_chars_8x10 =
{
	8,10,
	0x240,
	1,
	{ 0 },
	{ STEP8(0,1) },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8 },
	8*10
};

static GFXDECODE_START( gfx_vt240 )
	GFXDECODE_ENTRY( "charcpu", 0x338*10-3, vt240_chars_8x10, 0, 8 )
GFXDECODE_END

static INPUT_PORTS_START( vt240 )
	PORT_START("monitor")
	PORT_CONFNAME(0x01, 0x01, "Monitor Type")
	PORT_CONFSETTING(0x00, "Monochrome")
	PORT_CONFSETTING(0x01, "Color")
INPUT_PORTS_END

void vt240_state::vt240(machine_config &config)
{
	T11(config, m_maincpu, XTAL(7'372'800)); // confirm
	m_maincpu->set_addrmap(AS_PROGRAM, &vt240_state::vt240_mem);
	m_maincpu->set_initial_mode(5 << 13);
	m_maincpu->out_reset().set(FUNC(vt240_state::t11_reset_w));

	I8085A(config, m_i8085, XTAL(16'097'280) / 2);
	m_i8085->set_addrmap(AS_PROGRAM, &vt240_state::vt240_char_mem);
	m_i8085->set_addrmap(AS_IO, &vt240_state::vt240_char_io);
	m_i8085->out_sod_func().set(FUNC(vt240_state::i8085_rdy_w));
	m_i8085->in_sid_func().set(FUNC(vt240_state::i8085_sid_r));

	ADDRESS_MAP_BANK(config, "bank").set_map(&vt240_state::bank_map).set_options(ENDIANNESS_LITTLE, 16, 20, 0x1000);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(XTAL(16'097'280), 1024, 0, 800, 629, 0, 480);
	screen.set_screen_update("upd7220", FUNC(upd7220_device::screen_update));

	PALETTE(config, m_palette).set_entries(32);
	GFXDECODE(config, "gfxdecode", m_palette, gfx_vt240);

	UPD7220(config, m_hgdc, XTAL(16'097'280) / 16); // actually /8?
	m_hgdc->set_addrmap(0, &vt240_state::upd7220_map);
	m_hgdc->set_display_pixels(FUNC(vt240_state::hgdc_draw));
	m_hgdc->vsync_wr_callback().set_inputline(m_i8085, I8085_RST75_LINE);
	m_hgdc->blank_wr_callback().set_inputline(m_i8085, I8085_RST55_LINE);
	m_hgdc->set_screen("screen");

	SCN2681(config, m_duart, XTAL(7'372'800) / 2);
	m_duart->irq_cb().set(FUNC(vt240_state::irq13_w));
	m_duart->a_tx_cb().set(m_host, FUNC(rs232_port_device::write_txd));
	m_duart->b_tx_cb().set("printer", FUNC(rs232_port_device::write_txd));
	m_duart->outport_cb().set(FUNC(vt240_state::duartout_w));

	I8251(config, m_i8251, 0);
	m_i8251->txd_handler().set(FUNC(vt240_state::tx_w));
	m_i8251->dtr_handler().set(FUNC(vt240_state::lben_w));
	m_i8251->rxrdy_handler().set(FUNC(vt240_state::irq9_w));
	m_i8251->txrdy_handler().set(FUNC(vt240_state::irq7_w));

	LK201(config, m_lk201, 0);
	m_lk201->tx_handler().set(m_i8251, FUNC(i8251_device::write_rxd));

	CLOCK(config, "keyboard_clock", 4800 * 64).signal_handler().set(FUNC(vt240_state::write_keyboard_clock)); // 8251 is set to /64 on the clock input

	RS232_PORT(config, m_host, default_rs232_devices, "null_modem");
	m_host->rxd_handler().set(m_duart, FUNC(scn2681_device::rx_a_w));
	m_host->dsr_handler().set(m_duart, FUNC(scn2681_device::ip5_w));
	m_host->cts_handler().set(m_duart, FUNC(scn2681_device::ip0_w));

	rs232_port_device &printer(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	printer.rxd_handler().set(m_duart, FUNC(scn2681_device::rx_b_w));
	printer.dsr_handler().set(m_duart, FUNC(scn2681_device::ip1_w));

	X2212(config, "x2212");
}

void vt240_state::mc7105(machine_config &config)
{
	vt240(config);

	config.device_remove("lk201");

	ms7004_device &ms7004(MS7004(config, "ms7004", 0));
	ms7004.tx_handler().set(m_i8251, FUNC(i8251_device::write_rxd));

	m_i8251->txd_handler().set_nop();
	//m_i8251->txd_handler().set("ms7004", FUNC(ms7004_device::rx_w));

	// baud rate is supposed to be 4800 but keyboard is slightly faster
	CLOCK(config.replace(), "keyboard_clock", 4960*64).signal_handler().set(FUNC(vt240_state::write_keyboard_clock));
}

/* ROM definition */
ROM_START( mc7105 )
	ROM_REGION( 0x10000, "charcpu", ROMREGION_ERASEFF )
	ROM_LOAD( "027.bin", 0x8000, 0x8000, CRC(a159b412) SHA1(956097ccc2652d494258b3682498cfd3096d7d4f))
	ROM_LOAD( "028.bin", 0x0000, 0x8000, CRC(b253151f) SHA1(22ffeef8eb5df3c38bfe91266f26d1e7822cdb53))

	ROM_REGION( 0x20000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD16_BYTE( "029.bin", 0x00000, 0x8000, CRC(4a6db217) SHA1(47637325609ea19ffab61fe31e2700d72fa50729))
	ROM_LOAD16_BYTE( "031.bin", 0x00001, 0x8000, CRC(47129579) SHA1(39de9e2e26f90c5da5e72a09ff361c1a94b9008a))
	ROM_LOAD16_BYTE( "030.bin", 0x10000, 0x8000, CRC(05fd7b75) SHA1(2ad8c14e76accfa1b9b8748c58e9ebbc28844a47))
	ROM_LOAD16_BYTE( "032.bin", 0x10001, 0x8000, CRC(e81d93c4) SHA1(982412a7a6e65d6f6b4f66bd093e54ee16f31384))
ROM_END

/* ROM definition */
ROM_START( vt240 )
	ROM_REGION( 0x10000, "charcpu", ROMREGION_ERASEFF )
	ROM_LOAD( "23-008e6-00.e100", 0x0000, 0x4000, CRC(ebc8a2fe) SHA1(70838175f8302fdc0dee79b2403fa95e6d989206))
	ROM_CONTINUE(0x8000, 0x4000)

	ROM_REGION( 0x20000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS( "vt240" )
	// according to the schematics an even older set exists, variation 'E1' with roms:
	// e100/8085: 23-003e6
	// e20: 23-001e6
	// e22: 23-002e6
	// e19: 23-048e5
	// e21: 23-049e5
	// but according to the Field Change Order below, the initial release is V2.1, so the above must be a prototype.
	// DOL for v2.1 to v2.2 change: http://web.archive.org/web/20060905145200/http://cmcnabb.cc.vt.edu/dec94mds/vt240dol.txt
	ROM_SYSTEM_BIOS( 0, "vt240v21", "VT240 V2.1" ) // initial factory release, FCO says this was 8 Feburary 1985
	ROMX_LOAD( "23-006e6-00.e20", 0x00000, 0x8000, CRC(79c11d82) SHA1(5a6fe5b75b6504a161f2c9b148c0fe9f19770837), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "23-004e6-00.e22", 0x00001, 0x8000, CRC(eba10fef) SHA1(c0ee4d8e4eeb70066f03f3d17a7e2f2bd0b5f8ad), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "23-007e6-00.e19", 0x10000, 0x8000, CRC(d18a2ab8) SHA1(37f448a332fc50298007ed39c8bf1ab1eb6d4cae), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "23-005e6-00.e21", 0x10001, 0x8000, CRC(558d0285) SHA1(e96a49bf9d55d8ab879d9b39aa380368c5c9ade0), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "vt240", "VT240 V2.2" ) // Revised version, December 1985
	ROMX_LOAD( "23-058e6.e20", 0x00000, 0x8000, CRC(d2a56b90) SHA1(39cbb26134d7d8ba308df3a93228918a5945b45f), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "23-056e6.e22", 0x00001, 0x8000, CRC(c46e13c3) SHA1(0f2801fa7483d1f97708143cd81ae0816bf9a435), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "23-059e6.e19", 0x10000, 0x8000, CRC(f8393346) SHA1(1e28daf1b7f2bdabc47ce2f6fa99ef038b275a29), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "23-057e6.e21", 0x10001, 0x8000, CRC(7ce9dce9) SHA1(5a105e5bdca13910b3b79cc23567ce2dc36b844d), ROM_SKIP(1) | ROM_BIOS(1))
	// E39, E85, E131 are empty.

	ROM_REGION( 0x1000, "proms", ROMREGION_ERASEFF )
	ROM_LOAD( "23-351a1.e149", 0x0000, 0x0020, NO_DUMP) // 82s123; DRAM RAS/CAS Timing PROM
	ROM_LOAD( "23-352a1.e187", 0x0020, 0x0020, NO_DUMP) // 82s123; "CT0" Timing PROM
	ROM_LOAD( "23-369a1.e53", 0x0040, 0x0020, NO_DUMP) // 82s123; ROM and RAM mapping PROM
	ROM_LOAD( "23-370a1.e188", 0x0060, 0x0020, NO_DUMP) // 82s123; "CT1" Timing PROM
	ROM_LOAD( "23-994a9.e74", 0x0100, 0x0200, NO_DUMP) // 82s131; T11 Interrupt Encoder PROM

	ROM_REGION( 0x1000, "pals", 0 )
	ROM_LOAD( "23-087j5.e182.e183.jed", 0x0000, 0x1000, NO_DUMP ) // PAL16L8ACN; "Logic Unit" Character Pattern Related

	ROM_REGION( 0x100, "x2212", 0 ) // default nvram to avoid error 10
	ROM_LOAD( "x2212", 0x000, 0x100, CRC(31c90c64) SHA1(21a0f1d4eec1ced04b85923151783bf23d18bfbd) )
ROM_END

} // anonymous namespace


/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY                          FULLNAME  FLAGS */
COMP( 1983, vt240,  0,      0,      vt240,   vt240, vt240_state, empty_init, "Digital Equipment Corporation", "VT240",  MACHINE_IMPERFECT_GRAPHICS )
//COMP( 1983, vt241,  0,      0,      vt220,   vt220, vt240_state, empty_init, "Digital Equipment Corporation", "VT241",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
// NOTE: the only difference between VT240 and VT241 is the latter comes with a VR241 Color monitor, while the former comes with a mono display; the ROMs and operation are identical.
COMP( 1983, mc7105, 0,      0,      mc7105,  vt240, vt240_state, empty_init, "Elektronika",                   "MC7105", MACHINE_IMPERFECT_GRAPHICS )



vt320.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Jonathan Gevaryahu
/***************************************************************************

        DEC VT320

        30/06/2009 Skeleton driver.

****************************************************************************/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/ram.h"
#include "emupal.h"
#include "screen.h"


namespace {

class vt320_state : public driver_device
{
public:
	vt320_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, RAM_TAG)
	{
	}

	void vt320(machine_config &config);

private:
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;
	uint32_t screen_update_vt320(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	required_device<cpu_device> m_maincpu;
	required_device<ram_device> m_ram;
	void vt320_io(address_map &map) ATTR_COLD;
	void vt320_mem(address_map &map) ATTR_COLD;
};

/*

Partlist :

Siemens SAB8031A-16-P
ROMless version of the 8051 microcontroller, running at 16 MHz.
Motorola MC2681P
Dual Universal Asynchronous Receiver/Transmitter (DUART), 40-pin package.
Toshiba TC53512AP
ROM, 512K bits = 64K bytes. 28-pin package.
Toshiba TC5565APL-12, 2 off
Static RAM, 64K bit = 8K byte.
ST TDA1170N
Vertical deflection system IC.
UC 80343Q
20 pins. Unknown.
UC 80068Q
20 pins. Unknown.
Motorola SN74LS157NQST
16 pins. Quad 2-to-1 multiplexer.
Microchip ER5911
8 pins. Serial EEPROM. 1K bits = 128 bytes.
Texas Inst. 749X 75146
8 pins. Unknown.
Signetics? 74LS373N
8-bit D-type latch. This has eight inputs and eight outputs.
*/
void vt320_state::vt320_mem(address_map &map)
{
	map(0x0000, 0xffff).rom();
}

void vt320_state::vt320_io(address_map &map)
{
}

/* Input ports */
static INPUT_PORTS_START( vt320 )
INPUT_PORTS_END

void vt320_state::machine_reset()
{
	memset(m_ram->pointer(),0,16*1024);
}

void vt320_state::video_start()
{
}

uint32_t vt320_state::screen_update_vt320(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}


void vt320_state::vt320(machine_config &config)
{
	/* basic machine hardware */
	I8051(config, m_maincpu, XTAL(16'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &vt320_state::vt320_mem);
	m_maincpu->set_addrmap(AS_IO, &vt320_state::vt320_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	screen.set_screen_update(FUNC(vt320_state::screen_update_vt320));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("16K");
}

/* ROM definition */
ROM_START( vt320 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS( "vt320" )
	//DOL: http://web.archive.org/web/20060905115711/http://cmcnabb.cc.vt.edu/dec94mds/vt320dol.txt
	ROM_SYSTEM_BIOS( 0, "vt320v11", "VT320 V1.1" )
	// 23-054E7 below can also appear (same contents?) as 23-048E7 which is a mask rom
	ROMX_LOAD( "23-054e7.e9", 0x0000, 0x10000, CRC(be98f9a4) SHA1(b8044d42ffaadb734fbd047fbca9c8aadeb0bf6c), ROM_BIOS(0) ) // EPROM
	ROM_SYSTEM_BIOS( 1, "vt320", "VT320 V1.2" )
	ROMX_LOAD( "23-104e7.e9", 0x0000, 0x10000, CRC(5f419b5a) SHA1(dbc429b32d6baefd8a56862717d6e7fea1fb0c1c), ROM_BIOS(1) )
ROM_END

ROM_START( vt330 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_DEFAULT_BIOS( "vt330" )
	ROM_SYSTEM_BIOS( 0, "vt330", "VT330" )
	ROMX_LOAD( "23-236e6", 0x0000, 0x8000, CRC(38379339) SHA1(394e8511581abc796c8c612149eff280146b0ac8), ROM_BIOS(0) ) // 27256 EPROM
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR   NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS         INIT        COMPANY                          FULLNAME   FLAGS */
COMP( 1987,  vt320,  0,      0,      vt320,   vt320, vt320_state,  empty_init, "Digital Equipment Corporation", "VT320",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 1987,  vt330,  0,      0,      vt320,   vt320, vt320_state,  empty_init, "Digital Equipment Corporation", "VT330",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
//COMP( 1989?, vt340,  0,      0,      vt320,   vt320, vt320_state,  empty_init, "Digital Equipment Corporation", "VT340",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
//COMP( 1990?, vt340p, 0,      0,      vt320,   vt320, vt320_state,  empty_init, "Digital Equipment Corporation", "VT340+",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



vt52.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    DEC VT50 terminal family

    The VT50 "DECscope" was DEC's first video terminal to contain a CPU of
    sorts, with TTL logic spanning two boards executing custom microcode.
    It displayed 12 lines of 80-column text, using a standard character
    generator that only contained uppercase letters and symbols.

    The VT52 used the same case and most of the same circuitry as the VT50,
    but quickly displaced it by supporting 24 lines of text and a full ASCII
    character generator (on a board of its own). VT50 and VT52 each had
    minor variants differing in keyboard function and printer availability.

    The VT55 DECgraphic Scope was a graphical terminal based on the same
    main boards as the VT50 and VT52.

****************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/vt50/vt50.h"
#include "machine/ay31015.h"
#include "sound/spkrdev.h"
#include "screen.h"
#include "speaker.h"


namespace {

class vt52_state : public driver_device
{
public:
	vt52_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_uart(*this, "uart")
		, m_eia(*this, "eia")
		, m_keys(*this, "KEY%d", 0U)
		, m_break_key(*this, "BREAK")
		, m_baud_sw(*this, "BAUD")
		, m_data_sw(*this, "DATABITS")
		, m_chargen(*this, "chargen")
		, m_serial_out(true)
		, m_rec_data(true)
		, m_110_baud_counter(0)
	{
	}

	void vt52(machine_config &config);

	void break_w(int state);
	DECLARE_INPUT_CHANGED_MEMBER(data_sw_changed);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void update_serial_settings();

	u8 key_r(offs_t offset);
	void baud_9600_w(int state);
	void vert_count_w(u8 data);
	void uart_xd_w(u8 data);
	void gated_serial_output(bool state);
	void serial_out_w(int state);
	void rec_data_w(int state);
	int xrdy_eoc_r();
	u8 chargen_r(offs_t offset);

	void rom_1k(address_map &map) ATTR_COLD;
	void ram_2k(address_map &map) ATTR_COLD;

	required_device<vt5x_cpu_device> m_maincpu;
	required_device<ay31015_device> m_uart;
	required_device<rs232_port_device> m_eia;
	required_ioport_array<8> m_keys;
	required_ioport m_break_key;
	required_ioport m_baud_sw;
	required_ioport m_data_sw;
	required_region_ptr<u8> m_chargen;

	bool m_serial_out;
	bool m_rec_data;
	u8 m_110_baud_counter;
};

void vt52_state::machine_start()
{
	save_item(NAME(m_serial_out));
	save_item(NAME(m_rec_data));
	save_item(NAME(m_110_baud_counter));
}

void vt52_state::machine_reset()
{
	m_110_baud_counter = 0;

	update_serial_settings();
	m_uart->write_swe(0);

	m_eia->write_dtr(0);
	m_eia->write_rts(0);
}

void vt52_state::update_serial_settings()
{
	u8 db = m_data_sw->read();
	m_uart->write_nb1(BIT(db, 0));
	m_uart->write_np(BIT(db, 0));
	m_uart->write_eps(BIT(db, 1));
	m_uart->write_nb2(1);
	m_uart->write_tsb(!BIT(m_baud_sw->read(), 10));
	m_uart->write_cs(1);

	gated_serial_output(m_serial_out && m_break_key->read());
	if (!BIT(m_baud_sw->read(), 9))
		m_uart->write_si(1);
}

INPUT_CHANGED_MEMBER(vt52_state::data_sw_changed)
{
	update_serial_settings();
}

u8 vt52_state::key_r(offs_t offset)
{
	// double negative logic courtesy of 7430 NOR gates and 74150 multiplexer
	return !BIT(~m_keys[offset & 7]->read() & 0x3ff, (offset & 0170) >> 3);
}

void vt52_state::baud_9600_w(int state)
{
	u16 baud = m_baud_sw->read();
	if (!BIT(baud, 13))
	{
		m_uart->write_rcp(state);
		if ((baud & 0x0380) != 0x0380)
			m_uart->write_tcp(state);
	}
}

void vt52_state::vert_count_w(u8 data)
{
	u16 baud = m_baud_sw->read();

	// 110 baud clock is 1200 baud clock divided by 11 by yet another 74161
	if ((data & 7) == 4)
	{
		if (m_110_baud_counter == 15)
		{
			if (!BIT(baud, 10))
			{
				m_uart->write_rcp(0);
				if ((baud & 0x0380) != 0x0380)
					m_uart->write_tcp(0);
			}
			m_110_baud_counter = 5;
		}
		else
		{
			m_110_baud_counter++;
			if (m_110_baud_counter == 8 && !BIT(baud, 10))
			{
				m_uart->write_rcp(1);
				if ((baud & 0x0380) != 0x0380)
					m_uart->write_tcp(1);
			}
		}
	}

	if ((baud & 0x2400) == 0x2400)
	{
		if ((baud & 0x1b80) != 0x1b80)
		{
			bool clk = (~(baud | data) & 0x7f) == 0;
			m_uart->write_rcp(clk);
			m_uart->write_tcp(clk);
		}
		else
		{
			m_uart->write_rcp((~(baud | data) & 0x0e) == 0);
			m_uart->write_tcp((~(baud | data) & 0x71) == 0);
		}
	}
	else if ((baud & 0x0380) == 0x0380)
		m_uart->write_tcp((~(baud | data) & 0x71) == 0);
}

void vt52_state::uart_xd_w(u8 data)
{
	if (BIT(m_data_sw->read(), 2))
		m_uart->transmit(data | 0x80);
	else
		m_uart->transmit(data & 0x7f);
}

void vt52_state::gated_serial_output(bool state)
{
	ioport_value baud = m_baud_sw->read();
	if (BIT(baud, 9))
		m_eia->write_txd(state);
	if (!BIT(baud, 9) || (m_rec_data && (~baud & 0x0880) != 0))
		m_uart->write_si(state);
}

void vt52_state::serial_out_w(int state)
{
	if (m_serial_out != state)
	{
		m_serial_out = state;
		if (m_break_key->read())
			gated_serial_output(state);
	}
}

void vt52_state::break_w(int state)
{
	if (m_serial_out)
		gated_serial_output(state);
}

void vt52_state::rec_data_w(int state)
{
	m_rec_data = state;

	if (machine().ioport().safe_to_read())
	{
		ioport_value baud = m_baud_sw->read();
		if (BIT(baud, 9) && ((~baud & 0x0880) == 0 || (m_serial_out && m_break_key->read())))
			m_uart->write_si(state);
	}
}

int vt52_state::xrdy_eoc_r()
{
	return m_uart->tbmt_r() && m_uart->eoc_r();
}

u8 vt52_state::chargen_r(offs_t offset)
{
	// ROM is on its own board, shared only with 7404 inverters
	return ~m_chargen[offset];
}

void vt52_state::rom_1k(address_map &map)
{
	map(00000, 01777).rom().region("program", 0);
}

void vt52_state::ram_2k(address_map &map)
{
	map(00000, 03777).ram();
}

static INPUT_PORTS_START(vt52)
	PORT_START("KEY0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED) // must always be low
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) PORT_CODE(KEYCODE_2_PAD) // S78
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR('@') PORT_CODE(KEYCODE_2) // S3
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR(027) PORT_CODE(KEYCODE_W) // S19
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR(023) PORT_CODE(KEYCODE_S) // S35
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR(003) PORT_CODE(KEYCODE_C) // S52
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(030) PORT_CODE(KEYCODE_X) // S51
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(032) PORT_CODE(KEYCODE_Z) // S50
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CODE(KEYCODE_RIGHT) // S76
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD)) PORT_CODE(KEYCODE_0_PAD) // S81

	PORT_START("KEY1")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD)) PORT_CODE(KEYCODE_7_PAD) // S69
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR(014) PORT_CODE(KEYCODE_L) // S42
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHAR(':') PORT_CODE(KEYCODE_COLON) // S43
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(017) PORT_CODE(KEYCODE_O) // S26
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('9') PORT_CHAR('(') PORT_CODE(KEYCODE_9) // S10
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR(020) PORT_CODE(KEYCODE_P) // S27
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Line Feed") PORT_CHAR(012) PORT_CODE(KEYCODE_INSERT) // S30
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0') PORT_CHAR(')') PORT_CODE(KEYCODE_0) // S11
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD)) PORT_CODE(KEYCODE_9_PAD) // S71
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_CODE(KEYCODE_ENTER_PAD) // S83

	PORT_START("KEY2")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Delete") PORT_CHAR(UCHAR_MAMEKEY(DEL)) PORT_CODE(KEYCODE_DEL) // S31
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5) // S6
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR(024) PORT_CODE(KEYCODE_T) // S22
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR('$') PORT_CODE(KEYCODE_4) // S5
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('h') PORT_CHAR('H') PORT_CODE(KEYCODE_H) // S39
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR(022) PORT_CODE(KEYCODE_R) // S21
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\\') PORT_CHAR('|') PORT_CHAR(034) PORT_CODE(KEYCODE_BACKSLASH) // S29
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G  Bell") PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR(007) PORT_CODE(KEYCODE_G) // S38
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CODE(KEYCODE_DOWN) // S72
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) PORT_CODE(KEYCODE_8_PAD) // S70

	PORT_START("KEY3")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Back Space") PORT_CHAR(010) PORT_CODE(KEYCODE_BACKSPACE) // S15
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3) // S4
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR(006) PORT_CODE(KEYCODE_F) // S37
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR(026) PORT_CODE(KEYCODE_V) // S53
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR(002) PORT_CODE(KEYCODE_B) // S54
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR(004) PORT_CODE(KEYCODE_D) // S36
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('=') PORT_CHAR('+') PORT_CHAR(035) PORT_CODE(KEYCODE_EQUALS) // S13
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(005) PORT_CODE(KEYCODE_E) // S20
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad Blank (right)") PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CODE(KEYCODE_ASTERISK) // S67
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad Blank (left)") PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CODE(KEYCODE_NUMLOCK) // S65

	PORT_START("KEY4")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad Blank (center)") PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CODE(KEYCODE_SLASH_PAD) // S66
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('8') PORT_CHAR('*') PORT_CODE(KEYCODE_8) // S9
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('i') PORT_CHAR('I') PORT_CODE(KEYCODE_I) // S25
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR(025) PORT_CODE(KEYCODE_U) // S24
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR('&') PORT_CODE(KEYCODE_7) // S8
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR('^') PORT_CODE(KEYCODE_6) // S7
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('`') PORT_CHAR('~') PORT_CODE(KEYCODE_TILDE) // S14
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR(013) PORT_CODE(KEYCODE_K) // S41
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CODE(KEYCODE_UP) // S68
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE // S33

	PORT_START("KEY5")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) PORT_CODE(KEYCODE_1_PAD) // S77
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space Bar") PORT_CHAR(040) PORT_CODE(KEYCODE_SPACE) // S63
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(016) PORT_CODE(KEYCODE_N) // S55
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_M) // S56
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('j') PORT_CHAR('J') PORT_CODE(KEYCODE_J) // S40
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR(031) PORT_CODE(KEYCODE_Y) // S23
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('.') PORT_CHAR('>') PORT_CODE(KEYCODE_STOP) // S58
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',') PORT_CHAR('<') PORT_CODE(KEYCODE_COMMA) // S57
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD)) PORT_CODE(KEYCODE_3_PAD) // S79
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Repeat") PORT_CODE(KEYCODE_RALT) // S61

	PORT_START("KEY6")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) PORT_CODE(KEYCODE_4_PAD) // S73
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Scroll") PORT_CODE(KEYCODE_LALT) // S48
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CHAR(011) PORT_CODE(KEYCODE_TAB) // S17
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc (Sel)") PORT_CHAR(033) PORT_CODE(KEYCODE_ESC) // S1
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR(001) PORT_CODE(KEYCODE_A) // S34
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('q') PORT_CHAR('Q') PORT_CHAR(021) PORT_CODE(KEYCODE_Q) // S18
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CHAR(015) PORT_CODE(KEYCODE_ENTER) // S64/S47/S46
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1) // S2
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD)) PORT_CODE(KEYCODE_6_PAD) // S75
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CHAR(UCHAR_SHIFT_2) PORT_CODE(KEYCODE_LCONTROL) // S32

	PORT_START("KEY7")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD)) PORT_CODE(KEYCODE_5_PAD) // S74
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD)) PORT_CODE(KEYCODE_DEL_PAD) // S82
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('{') PORT_CHAR('}') PORT_CODE(KEYCODE_CLOSEBRACE) // S45
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('/') PORT_CHAR('?') PORT_CODE(KEYCODE_SLASH) // S59
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\'') PORT_CHAR('"') PORT_CODE(KEYCODE_QUOTE) // S44
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('-') PORT_CHAR('_') PORT_CODE(KEYCODE_MINUS) // S12
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Copy") PORT_CHAR(UCHAR_MAMEKEY(PRTSCR)) PORT_CODE(KEYCODE_RCONTROL) // S62
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('[') PORT_CHAR(']') PORT_CODE(KEYCODE_OPENBRACE) // S28
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CODE(KEYCODE_LEFT) // S80
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CHAR(UCHAR_SHIFT_1) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) // S49(L)/S60(R)

	PORT_START("BREAK") // on keyboard but divorced from matrix (position taken over by Caps Lock) and not readable by CPU
	PORT_BIT(1, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_PAUSE) PORT_WRITE_LINE_MEMBER(FUNC(vt52_state::break_w)) // S16

	PORT_START("BAUD") // 7-position rotary switches under keyboard, set in combination (positions on SW2 are actually labeled A through G)
	PORT_DIPNAME(0x03f1, 0x01f1, "Transmitting Speed") PORT_DIPLOCATION("S1:7,4,5,6,2,3,1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vt52_state::data_sw_changed), 0)
	PORT_DIPSETTING(0x01f1, "Off-Line (XCLK = RCLK)") // S1:1
	PORT_DIPSETTING(0x02f1, "Full Duplex (XCLK = RCLK)") // S1:3
	PORT_DIPSETTING(0x0371, "Full Duplex, Local Copy (XCLK = RCLK)") // S1:2
	PORT_DIPSETTING(0x03b1, "75 Baud") // S1:6
	PORT_DIPSETTING(0x03d1, "150 Baud") // S1:5
	PORT_DIPSETTING(0x03e1, "300 Baud") // S1:4
	PORT_DIPSETTING(0x03f0, "4800 Baud") // S1:7
	PORT_DIPNAME(0x3c0e, 0x1c0e, "Receiving Speed") PORT_DIPLOCATION("S2:6,5,4,2,1,3,7") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vt52_state::data_sw_changed), 0)
	PORT_DIPSETTING(0x2c0e, "Match (Bell 103) (RCLK = XCLK)") // S2:C
	PORT_DIPSETTING(0x340e, "Match (Bell 103), Local Copy (RCLK = XCLK)") // S2:A
	PORT_DIPSETTING(0x380e, "110 Baud with 2 Stop Bits") // S2:B
	PORT_DIPSETTING(0x3c06, "600 Baud") // S2:D
	PORT_DIPSETTING(0x3c0a, "1200 Baud") // S2:E
	PORT_DIPSETTING(0x3c0c, "2400 Baud") // S2:F
	PORT_DIPSETTING(0x1c0e, "9600 Baud") // S2:G
	// Any combination of XCLK = RCLK with RCLK = XCLK is illegal (both lines are pulled up, halting the UART)

	PORT_START("DATABITS")
	PORT_DIPNAME(0x1, 0x1, "Data Bits") PORT_DIPLOCATION("S3:1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vt52_state::data_sw_changed), 0)
	PORT_DIPSETTING(0x0, "7 (with parity)")
	PORT_DIPSETTING(0x1, "8 (no parity)")
	PORT_DIPNAME(0x2, 0x2, "Parity") PORT_DIPLOCATION("W6:1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vt52_state::data_sw_changed), 0)
	PORT_DIPSETTING(0x2, "Even")
	PORT_DIPSETTING(0x0, "Odd")
	PORT_DIPNAME(0x4, 0x0, "Data Bit 7") PORT_DIPLOCATION("W5:1") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vt52_state::data_sw_changed), 0)
	PORT_DIPSETTING(0x0, "Spacing")
	PORT_DIPSETTING(0x4, "Marking") // actually the hardware default, but not as good for modern use

	PORT_START("KEYCLICK")
	PORT_DIPNAME(1, 1, DEF_STR(Unused)) PORT_DIPLOCATION("S4:1") // not tested by VT52, and possibly not even populated
	PORT_DIPSETTING(0, DEF_STR(Off))
	PORT_DIPSETTING(1, DEF_STR(On))

	PORT_START("60HJ")
	PORT_DIPNAME(1, 1, "Unit Frequency") PORT_DIPLOCATION("W7:1")
	PORT_DIPSETTING(0, "50 Hz")
	PORT_DIPSETTING(1, "60 Hz")
INPUT_PORTS_END

void vt52_state::vt52(machine_config &config)
{
	VT52_CPU(config, m_maincpu, 13.824_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &vt52_state::rom_1k);
	m_maincpu->set_addrmap(AS_DATA, &vt52_state::ram_2k);
	m_maincpu->set_screen("screen");
	m_maincpu->baud_9600_callback().set(FUNC(vt52_state::baud_9600_w));
	m_maincpu->vert_count_callback().set(FUNC(vt52_state::vert_count_w));
	m_maincpu->uart_rd_callback().set(m_uart, FUNC(ay51013_device::receive));
	m_maincpu->uart_xd_callback().set(FUNC(vt52_state::uart_xd_w));
	m_maincpu->ur_flag_callback().set(m_uart, FUNC(ay51013_device::dav_r));
	m_maincpu->ut_flag_callback().set(FUNC(vt52_state::xrdy_eoc_r));
	m_maincpu->ruf_callback().set(m_uart, FUNC(ay51013_device::write_rdav));
	m_maincpu->key_up_callback().set(FUNC(vt52_state::key_r));
	m_maincpu->kclk_callback().set_ioport("KEYCLICK");
	m_maincpu->frq_callback().set_ioport("60HJ");
	m_maincpu->bell_callback().set("bell", FUNC(speaker_sound_device::level_w));
	m_maincpu->char_data_callback().set(FUNC(vt52_state::chargen_r));

	AY51013(config, m_uart); // TR1402 or equivalent
	m_uart->write_so_callback().set(FUNC(vt52_state::serial_out_w));

	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "bell").add_route(ALL_OUTPUTS, "mono", 1.0); // FIXME: uses a flyback diode circuit

	RS232_PORT(config, m_eia, default_rs232_devices, nullptr);
	m_eia->rxd_handler().set(FUNC(vt52_state::rec_data_w));
}

ROM_START(vt52)
	ROM_REGION(0x400, "program", 0) // bipolar PROMs
	ROM_LOAD_NIB_LOW( "23-124a9.e29", 0x000, 0x200, CRC(3f5f3b92) SHA1(244c3100f277da3fce5513a92529a2c3e26a80b4))
	ROM_LOAD_NIB_HIGH("23-125a9.e26", 0x000, 0x200, CRC(b2a670c9) SHA1(fa8dd031dcafe4facff41e79603bdb388a6df928))
	ROM_LOAD_NIB_LOW( "23-126a9.e37", 0x200, 0x200, CRC(4883a600) SHA1(c5d9b0c21493065c75b4a7d52d5bd47f9851dfe7))
	ROM_LOAD_NIB_HIGH("23-127a9.e21", 0x200, 0x200, CRC(56c1c0d6) SHA1(ab0eb6e7bbafcc3d28481b62de3d3490f01c0174))
	// K1 or L1 version uses PROMs 23-119A9 through 23-122A9

	ROM_REGION(0x400, "chargen", 0) // 2608 (non-JEDEC) character generator
	ROM_LOAD("23-002b4.e1", 0x000, 0x400, CRC(b486500c) SHA1(029f07424d6c23ee083db42d9f9c252ac728ccd0))
	// K1 or L1 version may use either 23-001B4 or 23-002B4
ROM_END

} // anonymous namespace


COMP(1975, vt52, 0, 0, vt52, vt52, vt52_state, empty_init, "Digital Equipment Corporation", "VT52 Video Display Terminal (M4)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND | MACHINE_NODEVICE_PRINTER)



vt520.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Jonathan Gevaryahu
/***************************************************************************

        DEC VT520

        02/07/2009 Skeleton driver.

****************************************************************************/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
//#include "machine/mc68681.h"
#include "machine/ram.h"
#include "emupal.h"
#include "screen.h"


namespace {

class vt520_state : public driver_device
{
public:
	vt520_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag) ,
		m_maincpu(*this, "maincpu"),
		m_rom(*this, "maincpu")
	{ }

	void vt520(machine_config &config);
	void vt420(machine_config &config);

private:
	uint8_t vt520_some_r();
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;
	uint32_t screen_update_vt520(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	required_device<cpu_device> m_maincpu;
	required_region_ptr<uint8_t> m_rom;
	void vt520_io(address_map &map) ATTR_COLD;
	void vt520_mem(address_map &map) ATTR_COLD;
};


void vt520_state::vt520_mem(address_map &map)
{
	map(0x0000, 0xffff).bankrw("bank1");
}

/*
    On the board there is TC160G41AF (1222) custom chip
    doing probably all video/uart logic
    there is 43.320MHz xtal near by
*/

uint8_t vt520_state::vt520_some_r()
{
	//bit 5 0
	//bit 6 1
	return 0x40;
}

void vt520_state::vt520_io(address_map &map)
{
	map.unmap_value_high();
	map(0x7ffb, 0x7ffb).r(FUNC(vt520_state::vt520_some_r));
}

/* Input ports */
static INPUT_PORTS_START( vt520 )
INPUT_PORTS_END


void vt520_state::machine_reset()
{
	address_space &space = m_maincpu->space(AS_PROGRAM);
	space.unmap_write(0x0000, 0xffff);
	membank("bank1")->set_base(&m_rom[m_rom.length() - 0x10000]);
}

void vt520_state::video_start()
{
}

uint32_t vt520_state::screen_update_vt520(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void vt520_state::vt420(machine_config &config)
{
	/* basic machine hardware */
	I80C31(config, m_maincpu, XTAL(43'320'000) / 3); // SCN8031HCFN40 (divider not verified)
	m_maincpu->set_addrmap(AS_PROGRAM, &vt520_state::vt520_mem);
	m_maincpu->set_addrmap(AS_IO, &vt520_state::vt520_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(802, 480);
	screen.set_visarea(0, 802-1, 0, 480-1);
	screen.set_screen_update(FUNC(vt520_state::screen_update_vt520));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);
}

void vt520_state::vt520(machine_config &config)
{
	/* basic machine hardware */
	I80C32(config, m_maincpu, XTAL(20'000'000)); // Philips P80C32IBPN
	m_maincpu->set_addrmap(AS_PROGRAM, &vt520_state::vt520_mem);
	m_maincpu->set_addrmap(AS_IO, &vt520_state::vt520_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(802, 480);
	screen.set_visarea(0, 802-1, 0, 480-1);
	screen.set_screen_update(FUNC(vt520_state::screen_update_vt520));
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME);

	// On the board there are two M5M44256BJ-7 chips
	// Which are DRAM 256K x 4bit
	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("256K");
}

/**************************************************************************************************************

DEC VT420.
Chips: SCN8031HCFN40, SCN2681TC1N40, M5M4464AP, HM62256LFP-10T, TC23SC070AT, TC531001CP-1815
Crystals: 43.320

***************************************************************************************************************/

ROM_START( vt420 )
	ROM_REGION(0x20000, "maincpu", 0)
	ROM_SYSTEM_BIOS(0, "v14", "Version 1.4")
	ROMX_LOAD( "23-202e9.e2",    0x00000, 0x20000, CRC(ca6cfb18) SHA1(2e0d3c16e04808bc6a45a0fc032b597458e6dd85), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "v13", "Version 1.3")
	ROMX_LOAD( "23-068e9-00.e2", 0x00000, 0x20000, CRC(22c3f93b) SHA1(b212911c41e4dba2e09d91fdd1f72d6c7536b0af), ROM_BIOS(1) )
ROM_END

ROM_START( vt520 )
	ROM_REGION( 0x80000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "23-010ed-00.e20", 0x0000, 0x80000, CRC(2502cc22) SHA1(0437c3107412f69e09d050fef003f2a81d8a3163)) // "(C)DEC94 23-010ED-00 // 9739 D" dumped from a VT520-A4 model
ROM_END

} // anonymous namespace


/* Driver */

COMP( 1990, vt420, 0, 0, vt420, vt520, vt520_state, empty_init, "Digital Equipment Corporation", "VT420 Video Terminal", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
//COMP( 1993, vt510, 0, 0, vt520, vt520, vt520_state, empty_init, "Digital Equipment Corporation", "VT510 Video Terminal",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP( 1994, vt520, 0, 0, vt520, vt520, vt520_state, empty_init, "Digital Equipment Corporation", "VT520 Video Terminal",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
//COMP( 1994, vt525, 0, 0, vt520, vt520, vt520_state, empty_init, "Digital Equipment Corporation", "VT525 Video Terminal",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



vt62.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for DEC VT61 and VT62 terminals.

****************************************************************************/

#include "emu.h"
//#include "bus/rs232/rs232.h"
#include "cpu/vt61/vt61.h"
#include "machine/ay31015.h"
//#include "sound/spkrdev.h"
#include "screen.h"
//#include "speaker.h"

namespace {

class vt62_state : public driver_device
{
public:
	vt62_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_uart(*this, "uart")
	{
	}

	void vt62(machine_config &mconfig);

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void micro_map(address_map &map) ATTR_COLD;
	void memory_map(address_map &map) ATTR_COLD;
	void decode_map(address_map &map) ATTR_COLD;

	required_device<vt61_cpu_device> m_maincpu;
	required_device<ay31015_device> m_uart;
	//required_ioport_array<8> m_keys;
	//required_ioport m_baud_sw;
};

void vt62_state::machine_reset()
{
}

u32 vt62_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void vt62_state::micro_map(address_map &map)
{
	map(00000, 01777).rom().region("crom", 0);
}

void vt62_state::memory_map(address_map &map)
{
	map(0000000, 0000377).mirror(0047400).ram(); // static RAM A
	map(0010000, 0013777).mirror(0040000).ram(); // dynamic RAM B
	map(0020000, 0023777).mirror(0040000).ram(); // dynamic RAM C
	map(0100000, 0117777).mirror(0060000).rom().region("mrom", 0);
}

void vt62_state::decode_map(address_map &map)
{
	map(000, 077).rom().region("idr", 0);
}

static INPUT_PORTS_START(vt62)
INPUT_PORTS_END

void vt62_state::vt62(machine_config &mconfig)
{
	VT61_CPU(mconfig, m_maincpu, 15.36_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &vt62_state::micro_map);
	m_maincpu->set_addrmap(AS_DATA, &vt62_state::memory_map);
	m_maincpu->set_addrmap(vt61_cpu_device::AS_IDR, &vt62_state::decode_map);

	AY51013(mconfig, m_uart);

	screen_device &screen(SCREEN(mconfig, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(15.36_MHz_XTAL, 1000, 0, 800, 256, 0, 240);
	screen.set_screen_update(FUNC(vt62_state::screen_update));
}

ROM_START(vt62)
	ROM_REGION16_LE(0x800, "crom", 0) // control ROM (microprogram)
	ROMX_LOAD("23-187f1_82s137.e93", 0x001, 0x400, CRC(73796e58) SHA1(38f8c984e7fb99d79fc13acc3a3b1e92aa136ad2), ROM_NIBBLE | ROM_SHIFT_NIBBLE_HI | ROM_SKIP(1))
	ROMX_LOAD("23-188f1_82s137.e94", 0x001, 0x400, CRC(198831a9) SHA1(574552dbae185b6c2168313d4e8a1d87e88c1883), ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO | ROM_SKIP(1))
	ROMX_LOAD("23-186f1_82s137.e90", 0x000, 0x400, CRC(b5691aa1) SHA1(d84cdbcf90dc687c56ac4e7f5d4372451d1afd7b), ROM_NIBBLE | ROM_SHIFT_NIBBLE_HI | ROM_SKIP(1))
	ROMX_LOAD("23-189f1_82s137.e95", 0x000, 0x400, CRC(05a9437b) SHA1(160c57658070ee7569fc12414f6250261e2f5e8a), ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO | ROM_SKIP(1))

	ROM_REGION(0x2000, "mrom", 0) // macroprogram/user ROM (on memory board)
	ROM_LOAD("e3", 0x0000, 0x0800, NO_DUMP) // UPROMs may be substituted at E24, E23, E28, E27
	ROM_LOAD("e8", 0x0800, 0x0800, NO_DUMP) // UPROMs may be substituted at E32, E31, E36, E35
	ROM_LOAD("e13", 0x1000, 0x0800, NO_DUMP) // UPROMs may be substituted at E41, E40, E46, E45
	ROM_LOAD("e20", 0x1800, 0x0800, NO_DUMP) // UPROMs may be substituted at E51, E50, E55, E54

	ROM_REGION(0x40, "idr", 0) // macro instruction decode
	ROM_LOAD("23-195a1_82s23.e13", 0x00, 0x20, CRC(cd8d8020) SHA1(cd10097d7fc62676b62387495615f1e06a689cd3))
	ROM_LOAD("23-194a1_82s23.e12", 0x20, 0x20, CRC(b05df6b5) SHA1(e13b7b0f75dbbc0d262606280bd5cf8be3561849))

	ROM_REGION(0x20, "alu", 0) // ALU function decode (same as VT61)
	ROM_LOAD("23-114a1.e41", 0x00, 0x20, NO_DUMP)

	ROM_REGION(0x400, "cgrom", 0) // character generators (same as VT61)
	ROM_LOAD_NIB_HIGH("23-053a9_82s131.e24", 0x000, 0x100, CRC(9a242be8) SHA1(4c619b6c0cfdda4af097b5702e45fad78ed6d601))
	ROM_CONTINUE(                            0x300, 0x100)
	ROM_LOAD_NIB_LOW( "23-052a9_82s131.e23", 0x000, 0x100, CRC(6c09ac7b) SHA1(e50060489500b1ca1b89530209c59e610c605c3d))
	ROM_CONTINUE(                            0x300, 0x100)
	ROM_LOAD_NIB_HIGH("23-051a9_82s131.e17", 0x200, 0x100, CRC(089ea972) SHA1(210bf4e5d32e49e1d7183f13d3b81ce446f49551))
	ROM_CONTINUE(                            0x100, 0x100)
	ROM_LOAD_NIB_LOW( "23-050a9_82s131.e16", 0x200, 0x100, CRC(758125a1) SHA1(b1de4448ea90da07f2aacb665542545bbcd17371))
	ROM_CONTINUE(                            0x100, 0x100)
ROM_END

} // anonymous namespace


//COMP(1977, vt61t, 0, 0, vt61t, vt61t, vt61_state, empty_init, "Digital Equipment Corporation", "VT61/t", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
COMP(1978, vt62, 0, 0, vt62, vt62, vt62_state, empty_init, "Digital Equipment Corporation", "VT62 DECscope", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



vta2000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert, AJR
/***************************************************************************

VTA-2000 Terminal
Made at Ukrainian SSR, Vinnitsa Terminal Plant
(info from https://prog.world/dataart-has-opened-the-website-of-the-it-museum/ )

Board images : http://fotki.yandex.ru/users/lodedome/album/93699?p=0

BDP-15 board only

2010-11-29 Skeleton driver.

Better known on the net as BTA2000-15m.
It is a green-screen terminal, using RS232, and supposedly VT100 compatible.
The top line is a status line.

Note: port 0 bit 4 is NOT a speaker bit. See code at 027B.

****************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/i8257.h"
#include "machine/pit8253.h"
#include "machine/pic8259.h"
#include "sound/spkrdev.h"
#include "video/i8275.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

namespace {

class vta2000_state : public driver_device
{
public:
	vta2000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mainpit(*this, "mainpit")
		, m_speaker(*this, "speaker")
		, m_crtc(*this, "crtc%u", 0U)
		, m_p_videoram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
	{ }

	void vta2000(machine_config &config);

private:
	uint8_t memory_r(offs_t offset);
	void crtc_dack_w(offs_t offset, uint8_t data);
	uint8_t crtc_r(offs_t offset);
	void crtc_w(offs_t offset, uint8_t data);

	void output_00(uint8_t data);
	void speaker_w(int state);

	I8275_DRAW_CHARACTER_MEMBER(draw_character);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<i8085a_cpu_device> m_maincpu;
	required_device<pit8253_device> m_mainpit;
	required_device<speaker_sound_device> m_speaker;
	required_device_array<i8275_device, 2> m_crtc;
	required_shared_ptr<uint8_t> m_p_videoram;
	required_region_ptr<u8> m_p_chargen;
};

uint8_t vta2000_state::memory_r(offs_t offset)
{
	return m_maincpu->space(AS_PROGRAM).read_byte(offset ^ 1);
}

void vta2000_state::crtc_dack_w(offs_t offset, uint8_t data)
{
	if (BIT(offset, 0))
		m_crtc[0]->dack_w(data);
	m_crtc[1]->dack_w(data | 0x80);
}

uint8_t vta2000_state::crtc_r(offs_t offset)
{
	(void)m_crtc[1]->read(offset);
	return m_crtc[0]->read(offset);
}

void vta2000_state::crtc_w(offs_t offset, uint8_t data)
{
	m_crtc[0]->write(offset, data);
	m_crtc[1]->write(offset, data);
}

void vta2000_state::output_00(uint8_t data)
{
	m_mainpit->write_gate0(BIT(data, 4));
}

void vta2000_state::speaker_w(int state)
{
	m_speaker->level_w(state);
}

void vta2000_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x5fff).rom().region("roms", 0);
	map(0x8000, 0xc7ff).ram().share("videoram");
	map(0xc800, 0xc8ff).rom().region("roms", 0x5000); // FIXME: KR1601RR1 EAROM should be mapped here instead
}

void vta2000_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x00).w(FUNC(vta2000_state::output_00));
	map(0x20, 0x21).rw("pic", FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0x80, 0x88).rw("dmac", FUNC(i8257_device::read), FUNC(i8257_device::write));
	map(0xa0, 0xa1).rw(FUNC(vta2000_state::crtc_r), FUNC(vta2000_state::crtc_w));
	map(0xc0, 0xc0).rw("usart", FUNC(i8251_device::data_r), FUNC(i8251_device::data_w));
	map(0xc3, 0xc3).rw("usart", FUNC(i8251_device::status_r), FUNC(i8251_device::control_w));
	map(0xc8, 0xcb).w("brgpit", FUNC(pit8253_device::write));
	map(0xd0, 0xd3).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0xe0, 0xe3).rw("mainpit", FUNC(pit8253_device::read), FUNC(pit8253_device::write));
}

/* Input ports */
static INPUT_PORTS_START( vta2000 )
INPUT_PORTS_END


I8275_DRAW_CHARACTER_MEMBER(vta2000_state::draw_character)
{
	using namespace i8275_attributes;

	uint8_t dots = 0;
	if (BIT(attrcode, LTEN + 8))
		dots = 0xff;
	else if (!BIT(attrcode, VSP + 8))
		dots = m_p_chargen[BIT(attrcode, HLGT + 8) << 12 | (charcode & 0x7f) << 4 | linecount];
	if (BIT(attrcode, RVV + 8))
		dots = ~dots;

	uint32_t *p = &bitmap.pix(y, x);
	for (int i = 0; i < 8; i++)
		p[i] = BIT(dots, 7 - i) ? rgb_t::white() : rgb_t::black();
}


/* F4 Character Displayer */
static const gfx_layout vta2000_charlayout =
{
	8, 12,                  /* 8 x 12 characters */
	512,                    /* 512 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_vta2000 )
	GFXDECODE_ENTRY( "chargen", 0x0000, vta2000_charlayout, 0, 1 )
GFXDECODE_END

void vta2000_state::vta2000(machine_config &config)
{
	//constexpr auto CPU_CLOCK = XTAL(4'000'000) / 4; // too slow for CRTC DMA
	constexpr auto CPU_CLOCK = 2'000'000;
	constexpr auto DOT_CLOCK = 12'500'000; // guessed

	/* basic machine hardware */
	I8080(config, m_maincpu, CPU_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &vta2000_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &vta2000_state::io_map);
	m_maincpu->in_inta_func().set("pic", FUNC(pic8259_device::acknowledge));

	//KR1601RR1(config, "earom", 0);

	PIT8253(config, m_mainpit, 0);
	m_mainpit->set_clk<0>(500'000);
	m_mainpit->out_handler<0>().set(FUNC(vta2000_state::speaker_w));
	m_mainpit->set_clk<2>(500'000);
	m_mainpit->out_handler<2>().set("pic", FUNC(pic8259_device::ir7_w));

	pic8259_device &pic(PIC8259(config, "pic", 0));
	pic.in_sp_callback().set_constant(0);
	pic.out_int_callback().set_inputline(m_maincpu, 0);

	i8251_device &usart(I8251(config, "usart", CPU_CLOCK));
	usart.rxrdy_handler().set("pic", FUNC(pic8259_device::ir4_w));
	usart.syndet_handler().set("pic", FUNC(pic8259_device::ir1_w));

	pit8253_device &brgpit(PIT8253(config, "brgpit", 0));
	brgpit.set_clk<0>(1'228'800); // maybe
	brgpit.set_clk<1>(1'228'800);
	brgpit.out_handler<0>().set("usart", FUNC(i8251_device::write_rxc));
	brgpit.out_handler<1>().set("usart", FUNC(i8251_device::write_txc)); // or vice versa?

	i8255_device &ppi(I8255(config, "ppi"));
	ppi.in_pc_callback().set_constant(0xe0);

	i8257_device &dmac(I8257(config, "dmac", CPU_CLOCK));
	dmac.out_hrq_cb().set_inputline(m_maincpu, INPUT_LINE_HALT);
	dmac.out_hrq_cb().append("dmac", FUNC(i8257_device::hlda_w));
	dmac.out_tc_cb().set("pic", FUNC(pic8259_device::ir2_w)).invert();
	dmac.in_memr_cb().set(FUNC(vta2000_state::memory_r));
	dmac.out_iow_cb<2>().set(FUNC(vta2000_state::crtc_dack_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER, rgb_t::green()));
	screen.set_raw(DOT_CLOCK, 800, 0, 640, 312, 0, 300);
	screen.set_screen_update(m_crtc[0], FUNC(i8275_device::screen_update));
	//screen.set_palette("palette");

	for (auto &crtc : m_crtc)
	{
		I8275(config, crtc, DOT_CLOCK / 8);
		crtc->set_character_width(8);
		crtc->set_screen("screen");
	}
	m_crtc[0]->set_display_callback(FUNC(vta2000_state::draw_character));
	m_crtc[0]->drq_wr_callback().set("dmac", FUNC(i8257_device::dreq2_w));
	m_crtc[0]->irq_wr_callback().set("pic", FUNC(pic8259_device::ir6_w));
	m_crtc[0]->set_next_crtc(m_crtc[1]);

	PALETTE(config, "palette", palette_device::MONOCHROME_HIGHLIGHT);
	GFXDECODE(config, "gfxdecode", "palette", gfx_vta2000);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.5);
}


/* ROM definition */
ROM_START( vta2000 )
	ROM_REGION( 0x6000, "roms", 0 )
	ROM_LOAD( "bdp-15_11.rom", 0x4000, 0x2000, CRC(d4abe3e9) SHA1(ab1973306e263b0f66f2e1ede50cb5230f8d69d5) )
	ROM_LOAD( "bdp-15_12.rom", 0x2000, 0x2000, CRC(4a5fe332) SHA1(f1401c26687236184fec0558cc890e796d7d5c77) )
	ROM_LOAD( "bdp-15_13.rom", 0x0000, 0x2000, CRC(b6b89d90) SHA1(0356d7ba77013b8a79986689fb22ef4107ef885b) )

	ROM_REGION(0x2000, "chargen", ROMREGION_INVERT )
	ROM_LOAD( "bdp-15_14.rom", 0x0000, 0x2000, CRC(a1dc4f8e) SHA1(873fd211f44713b713d73163de2d8b5db83d2143) )
ROM_END

} // Anonymous namespace

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY      FULLNAME    FLAGS
COMP( 198?, vta2000, 0,      0,      vta2000, vta2000, vta2000_state, empty_init, "<unknown>", "VTA2000-15m", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



vtech_eu3a12.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR

// CPU die (epoxy blob) is an Elan EU3A12 (Elan "RISC II Series" quasi-PIC with 16-bit opcodes)

#include "emu.h"
#include "cpu/rii/riscii.h"
#include "video/sed1520.h"
#include "emupal.h"
#include "screen.h"


namespace {

class vreadere_state : public driver_device
{
public:
	vreadere_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_epl(*this, "epl")
		, m_portb_control(0xff)
		, m_portc_data(0xff)
	{ }

	void vreadere(machine_config &config);

	void power_on_w(int state);
	void power_off_w(int state);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	EPL43102_UPDATE_CB(lcd_update);

	void portb_w(u8 data);
	u8 portc_r();
	void portc_w(u8 data);

	void prog_map(address_map &map) ATTR_COLD;

	void palette_init(palette_device &palette);

	required_device<riscii_series_device> m_maincpu;
	required_device<epl43102_device> m_epl;

	u8 m_portb_control;
	u8 m_portc_data;
};

void vreadere_state::machine_start()
{
	save_item(NAME(m_portb_control));
	save_item(NAME(m_portc_data));
}

EPL43102_UPDATE_CB(vreadere_state::lcd_update)
{
	if (lcd_on)
	{
		for (int y = 0; y < 32; y++)
		{
			int ys = (y + start_line) & 31;
			for (int x = 0; x < 68; x++)
			{
				int bitpos = ((ys & ~7) * 102 + x * 8 + (ys & 7)) % (42 * 102);
				bitmap.pix(y, x) = BIT(dram[bitpos / 8], bitpos & 7) ? !reverse : reverse;
			}
		}
		for (int y = 32; y < 48; y++)
		{
			int ys = (y + start_line) & 15;
			for (int x = 0; x < 34; x++)
			{
				int bitpos = (((ys & ~7) | 16) * 102 + (68 + x) * 8 + (ys & 7)) % (42 * 102);
				bitmap.pix(y, x) = BIT(dram[bitpos / 8], bitpos & 7) ? !reverse : reverse;
			}
			for (int x = 34; x < 68; x++)
			{
				int bitpos = ((ys & ~7) * 102 + (135 - x) * 8 + (ys & 7)) % (42 * 102);
				bitmap.pix(y, x) = BIT(dram[bitpos / 8], bitpos & 7) ? !reverse : reverse;
			}
		}
	}
	else
		bitmap.fill(0, cliprect);

	return 0;
}

void vreadere_state::power_on_w(int state)
{
	m_maincpu->set_input_line(riscii_series_device::PA6_LINE, state ? CLEAR_LINE : ASSERT_LINE);
}

void vreadere_state::power_off_w(int state)
{
	m_maincpu->set_input_line(riscii_series_device::PA7_LINE, state ? CLEAR_LINE : ASSERT_LINE);
}

void vreadere_state::portb_w(u8 data)
{
	u8 old_control = std::exchange(m_portb_control, data);

	if (!BIT(data, 3))
	{
		if (!BIT(old_control, 6) && BIT(data, 6))
			m_epl->write(BIT(data, 4), m_portc_data);

		if (BIT(old_control, 5) && !BIT(data, 5))
			m_portc_data = m_epl->read(BIT(data, 4));
	}
}

u8 vreadere_state::portc_r()
{
	return m_portc_data;
}

void vreadere_state::portc_w(u8 data)
{
	m_portc_data = data;
}

void vreadere_state::prog_map(address_map &map)
{
	map(0x000000, 0x1fffff).rom().region("maincpu", 0);
}

static INPUT_PORTS_START(vreadere)
	PORT_START("POWER")
	PORT_BIT(1, IP_ACTIVE_LOW, IPT_POWER_ON) PORT_WRITE_LINE_MEMBER(FUNC(vreadere_state::power_on_w))
	PORT_BIT(2, IP_ACTIVE_LOW, IPT_POWER_OFF) PORT_WRITE_LINE_MEMBER(FUNC(vreadere_state::power_off_w))
INPUT_PORTS_END

void vreadere_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(131, 136, 139));
	palette.set_pen_color(1, rgb_t(  0,   0,   0));
}

void vreadere_state::vreadere(machine_config &config)
{
	EPG3231(config, m_maincpu, 8'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &vreadere_state::prog_map);
	m_maincpu->out_portb_cb().set(FUNC(vreadere_state::portb_w));
	m_maincpu->in_portc_cb().set(FUNC(vreadere_state::portc_r));
	m_maincpu->out_portc_cb().set(FUNC(vreadere_state::portc_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(68, 48);
	screen.set_visarea_full();
	screen.set_screen_update(m_epl, FUNC(epl43102_device::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(vreadere_state::palette_init), 2);

	EPL43102(config, m_epl);
	m_epl->set_screen_update_cb(FUNC(vreadere_state::lcd_update));
}

ROM_START( vreadere )
	ROM_REGION(0x400000, "maincpu", 0)
	ROM_LOAD( "27-08291.u2", 0x000000, 0x400000, CRC(f2eb801f) SHA1(33e2d28ab2f04b17f66880898832265d50de54d4) )
ROM_END

} // anonymous namespace


COMP( 2004, vreadere, 0, 0, vreadere, vreadere, vreadere_state, empty_init, "Video Technology", "Reader Laptop E (Germany)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



vtech_innotab.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/******************************************************************************

    VTech InnoTab 1/2/3
    NOT InnoTab MAX

    InnoTab 1/2/3 appear to be compatible with each other (updated internal
    software etc.)

    where do the InnoTab 3S and InnoTab 2 Baby fit in?

*******************************************************************************/

#include "emu.h"

#include "cpu/arm7/arm7.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class vtech_innotab_state : public driver_device
{
public:
	vtech_innotab_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_cart(*this, "cartslot")
		, m_cart_region(nullptr)
	{ }

	void vtech_innotab(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	required_device<cpu_device> m_maincpu;

	required_device<screen_device> m_screen;
	required_device<generic_slot_device> m_cart;
	memory_region *m_cart_region;

	uint32_t screen_update_innotab(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};

uint32_t vtech_innotab_state::screen_update_innotab(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void vtech_innotab_state::machine_start()
{
	// if there's a cart, override the standard mapping
	if (m_cart && m_cart->exists())
	{
		m_cart_region = memregion(std::string(m_cart->tag()) + GENERIC_ROM_REGION_TAG);
	}
}

DEVICE_IMAGE_LOAD_MEMBER(vtech_innotab_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM16_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

static INPUT_PORTS_START( vtech_innotab )
INPUT_PORTS_END


void vtech_innotab_state::vtech_innotab(machine_config& config)
{
	ARM9(config, m_maincpu, 240000000); // unknown ARM type

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_size(320, 262);
	m_screen->set_visarea(0, 320 - 1, 0, 240 - 1);
	m_screen->set_screen_update(FUNC(vtech_innotab_state::screen_update_innotab));

	SPEAKER(config, "speaker", 2).front();

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "vtech_innotab_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(vtech_innotab_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("vtech_innotab_cart");
}

/*
**************************************************************
APP Version : 10.90
     Device : THGBM4G4D1HBAIR(ISP)_4Bit
**************************************************************


File Name :
*******************************************************************
D:\Xgpro\UserData\EMMC_Data
ECSD_CSD.BIN
*******************************************************************


  <1> -- Production Info.and Device life time--

      MID          : 11
      PNM          : 002G49
      Product Date : 5-2012
      Version      : MMC V4.41

      Device life time Type A : Not defined
      Device life time Type B : Not defined
      Device life time (PRE_EOL_INFO) : Not Defined

  <2> -- Partition Size Info.--

      BOOT1 SIZE   : 1024 KB
      BOOT2 SIZE   : 1024 KB
      RPMB SIZE    : 128 KB
      GPP1 SIZE    : 0 KB
      GPP2 SIZE    : 0 KB
      GPP3 SIZE    : 0 KB
      GPP4 SIZE    : 0 KB
      USER SIZE    : 1,916,928 KB
                      ( 0x 00_7500_0000 )
      Password Protect Features : YES

  <5> -- other Informations --

      MAX_READ_BL_LEN   : 1024 bytes
      MAX_WRITE_BL_LEN  : 512 bytes
      MAX_TRAN_SPEED    : 55.000 MHZ

      BOOT_BUS_CONDITIONS[177]    : 00
      BOOT_CONFIG_PROT[178]       : 00
      PARTITION_CONFIG[179]       : 00
      RST_n_FUNCTION[162]         : 00

      ENH_START_ADDR              : 00000000
      ENH_SIZE_MULT               : 000000
      MAX_ENH_SIZE_MULT           : 0003A8
      PARTITIONS_ATTRIBUTE        : 00
      WR_REL_SET                  : 00
      WR_REL_PARAM                : 05
      PARTITION_SETTING_COMPLETED : 00

      HC_WP_GRP_SIZE    : 1
      HC_ERASE_GRP_SIZE : 2
      WP_GRP_ENABLE     : 1
      WP_GRP_SIZE       : 1
      ERASE_GRP_MULT    : 31
      ERASE_GRP_SIZE    : 31
      CCC               : 00F5
      DSR implemented   : 0
      PARTITION_ACCESS  : 10 ms
      ERASED_MEM_CONT   : 01
      DYNCAP_NEEDED     : 00
      SECURE_WP_INFO    : 00
      SEC_ERASE_MULT    : 10
      ERASE_TIMEOUT_MULT: 02
      NATIVE_SECTOR_SIZE: 00 (512B)
      INI_TIMEOUT_AP    : 3000 ms
      INI_TIMEOUT_EMU       : 0 ms

----------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------

**************************************************************
APP Version : 10.90
     Device : THGBM4G4D1HBAIR(ISP)_4Bit
**************************************************************

Init EMMC... OK!    ( OCR register: 80FF8080 )
Verifing CSD Succeeded
Verifing ECSD Succeeded
Verifing BOOT1 : Succeeded. Time : 0. 47 S -- Partition Size :1024 KB Processing Size from the File : 1024 KB )
Verifing BOOT2 : Succeeded. Time : 0. 47 S -- Partition Size :1024 KB Processing Size from the File : 1024 KB )
Analysis file is complete, Space usage: 20.26% time: 13S
Verifing User Area : Succeeded. Time : 36.531 S -- Partition Size :1916928 KB( Processing Size from the File : 1916928 KB )
32 bits CheckSum :  0x 86F14222

*/

ROM_START( innotab2 )
	ROM_REGION( 0x0100000, "maincpu", ROMREGION_ERASEFF )
	// are there any other dumpable devices?, or internal ROM in the CPU for booting from?


	// this uses a "eMMC" type ROM chip, should it be treated as a CHD, or like a NAND ROM?
	ROM_REGION( 0x0100000, "emmc_boot", ROMREGION_ERASEFF )
	// these are both blank, unused, or read protected in some way?
	//ROM_LOAD( "boot1.bin", 0x000000, 0x0100000, CRC(956bac74) SHA1(bf0b121670df23f2cc64302d9f215e7c81187bbb) ) // FIXED BITS (11111111)
	//ROM_LOAD( "boot2.bin", 0x000000, 0x0100000, CRC(956bac74) SHA1(bf0b121670df23f2cc64302d9f215e7c81187bbb) ) // FIXED BITS (11111111)

	ROM_REGION( 0x220, "emmc_misc1", ROMREGION_ERASEFF )
	ROM_LOAD( "ecsd_csd.bin", 0x000000, 0x220, CRC(a30bcb97) SHA1(ba83c5b2c73f26ad89ac7cc44b0ea6971050cfa4) )

	ROM_REGION( 0x75000000, "emmc_user", ROMREGION_ERASEFF )
	ROM_LOAD( "userdata.bin", 0x000000, 0x75000000, CRC(3c063d5d) SHA1(41a980b9e19e9fdf00f5175bf332b50f741aecb9) )

	//ROM_REGION( 0x3712, "emmc_misc2", ROMREGION_ERASEFF )
	// this appears to be a project file used by the dumping software, not a ROM
	//ROM_LOAD( "emmc_ghost.mpj", 0x000000, 0x3712, CRC(16b705da) SHA1(fdb576385cf46984ea40d8e8b83758d94f67507e) )
ROM_END

} // anonymous namespace


CONS( 2011, innotab2,     0,       0,      vtech_innotab, vtech_innotab, vtech_innotab_state, empty_init, "VTech", "InnoTAB 2 (UK)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



vtech1.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Juergen Buchmueller, Dirk Best
/***************************************************************************

    Video Technology Laser 110
    Video Technology Laser 200
      Salora Fellow
      Texet TX-8000 (?)
      Video Technology VZ-200
    Video Technology Laser 210
      Dick Smith Electronics VZ-200
    Video Technology Laser 310
      Dick Smith Electronics VZ-300

Thanks go to:

    - Guy Thomason
    - Jason Oakley
    - Bushy Maunder
    - and anybody else on the vzemu list :)
    - Davide Moretti for the detailed description of the colors
    - Leslie Milburn

Todo:

    - Figure out which machines were shipped with which ROM version
      where not known (currently only a guess)
    - Lightpen support
    - Rewrite floppy

***************************************************************************/

#include "emu.h"

#include "bus/vtech/ioexp/ioexp.h"
#include "bus/vtech/memexp/memexp.h"
#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "sound/spkrdev.h"
#include "video/mc6847.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "formats/vt_cas.h"
#include "multibyte.h"

#define LOG_VTECH1_LATCH (1U << 1)

#define VERBOSE (0)
#include "logmacro.h"


namespace {

/***************************************************************************
    CONSTANTS & MACROS
***************************************************************************/

#define VTECH1_CLK        3579500
#define VZ300_XTAL1_CLK   XTAL(17'734'470)


/***************************************************************************
    TYPE DEFINITIONS
***************************************************************************/

class vtech1_base_state : public driver_device
{
public:
	vtech1_base_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_crtc(*this, "crtc")
		, m_vbank(*this, "vbank")
		, m_speaker(*this, "speaker")
		, m_cassette(*this, "cassette")
		, m_io_keyboard(*this, "X%u", 0U)
		, m_ioexp(*this, "io")
		, m_memexp(*this, "mem")
	{
	}

	void vtech1(machine_config &config);

	void laser110_mem(address_map &map) ATTR_COLD;
	void laser210_mem(address_map &map) ATTR_COLD;
	void laser310_mem(address_map &map) ATTR_COLD;
	void vtech1_io(address_map &map) ATTR_COLD;

protected:
	required_device<cpu_device> m_maincpu;
	required_device<mc6847_base_device> m_crtc;
	memory_bank_creator m_vbank;
	required_device<speaker_sound_device> m_speaker;
	required_device<cassette_image_device> m_cassette;
	required_ioport_array<8> m_io_keyboard;
	required_device<vtech_ioexp_slot_device> m_ioexp;
	required_device<vtech_memexp_slot_device> m_memexp;

	DECLARE_SNAPSHOT_LOAD_MEMBER(snapshot_cb);

	uint8_t keyboard_r(offs_t offset);
	virtual void latch_w(uint8_t data);
	uint8_t vram_r(memory_share_creator<uint8_t> &vram, offs_t offset);

	static const uint8_t VZ_BASIC = 0xf0;
	static const uint8_t VZ_MCODE = 0xf1;
};

class vtech1_state : public vtech1_base_state
{
public:
	vtech1_state(const machine_config &mconfig, device_type type, const char *tag)
		: vtech1_base_state(mconfig, type, tag)
		, m_vram(*this, "videoram", 0x800, ENDIANNESS_LITTLE)
	{ }

	void laser310(machine_config &config);
	void laser200(machine_config &config);
	void laser110(machine_config &config);
	void laser210(machine_config &config);

protected:
	void machine_start() override ATTR_COLD;

private:
	memory_share_creator<uint8_t> m_vram;

	uint8_t mc6847_videoram_r(offs_t offset);
};

class laser310h_state : public vtech1_base_state
{
public:
	laser310h_state(const machine_config &mconfig, device_type type, const char *tag)
		: vtech1_base_state(mconfig, type, tag)
		, m_vram(*this, "videoram", 0x2000, ENDIANNESS_LITTLE)
	{ }

	void laser310h(machine_config &config);

protected:
	void machine_start() override ATTR_COLD;

private:
	memory_share_creator<uint8_t> m_vram;

	void latch_w(uint8_t data) override;
	void video_bank_w(uint8_t data);
	uint8_t mc6847_videoram_r(offs_t offset);

	void vtech1_shrg_mem(address_map &map) ATTR_COLD;
	void vtech1_shrg_io(address_map &map) ATTR_COLD;
};


/***************************************************************************
    SNAPSHOT LOADING
***************************************************************************/

SNAPSHOT_LOAD_MEMBER(vtech1_base_state::snapshot_cb)
{
	// get the header
	uint8_t header[24];
	if (image.fread(&header, sizeof(header)) != sizeof(header))
	{
		return std::make_pair(image_error::UNSPECIFIED, std::string());
	}

	// get image name
	char pgmname[17];
	for (int i = 0; i < 16; i++)
		pgmname[i] = header[i+4];
	pgmname[16] = '\0';

	// get start and end addresses
	uint16_t const start = get_u16le(&header[22]);
	uint16_t const end = start + image.length() - sizeof(header);
	uint16_t const size = end - start;

	// write it to RAM
	auto buf = std::make_unique<uint8_t []>(size);
	if (image.fread(buf.get(), size) != size)
	{
		return std::make_pair(image_error::UNSPECIFIED, std::string());
	}
	uint8_t *ptr = &buf[0];

	// check for supported format before overwriting memory
	switch (header[21])
	{
	case VZ_BASIC:
	case VZ_MCODE:
		break;

	default:
		return std::make_pair(image_error::UNSUPPORTED, "Snapshot format not supported");
	}

	address_space &space = m_maincpu->space(AS_PROGRAM);
	for (uint16_t addr = start; addr < end; addr++, ptr++)
	{
		uint8_t to_write = *ptr;
		space.write_byte(addr, to_write);

		// verify
		if (space.read_byte(addr) != to_write)
		{
			return std::make_pair(
					image_error::INVALIDIMAGE,
					util::string_format("Insufficient RAM to load snapshot program '%s' (%d bytes needed)", pgmname, size));
		}
	}

	// patch variables depending on snapshot type
	switch (header[21])
	{
	case VZ_BASIC:
		space.write_byte(0x78a4, start % 256); /* start of basic program */
		space.write_byte(0x78a5, start / 256);
		space.write_byte(0x78f9, end % 256); /* end of basic program */
		space.write_byte(0x78fa, end / 256);
		space.write_byte(0x78fb, end % 256); /* start variable table */
		space.write_byte(0x78fc, end / 256);
		space.write_byte(0x78fd, end % 256); /* start free mem, end variable table */
		space.write_byte(0x78fe, end / 256);
		image.message(" %s (B)\nsize=%04X : start=%04X : end=%04X", pgmname, size, start, end);
		break;

	case VZ_MCODE:
		space.write_byte(0x788e, start % 256); /* usr subroutine address */
		space.write_byte(0x788f, start / 256);
		image.message(" %s (M)\nsize=%04X : start=%04X : end=%04X", pgmname, size, start, end);
		m_maincpu->set_pc(start);              /* start program */
		break;
	}

	return std::make_pair(std::error_condition(), std::string());
}


/***************************************************************************
    INPUTS
***************************************************************************/

uint8_t vtech1_base_state::keyboard_r(offs_t offset)
{
	uint8_t result = 0x3f;

	// bit 0 to 5, keyboard input
	for (u8 i = 0; i < 8; i++)
		if (!BIT(offset, i)) result &= m_io_keyboard[i]->read();

	// bit 6, cassette input
	result |= (m_cassette->input() > 0.04) ? 0 : 0x40;

	// bit 7, field sync
	result |= m_crtc->fs_r() << 7;

	return result;
}


/***************************************************************************
    I/O LATCH
***************************************************************************/

void vtech1_base_state::latch_w(uint8_t data)
{
	LOGMASKED(LOG_VTECH1_LATCH, "vtech1_latch_w $%02X\n", data);

	// bit 2, cassette out (actually bits 1 and 2 perform this function, so either can be used)
	m_cassette->output( BIT(data, 2) ? 1.0 : -1.0);

	// bit 3 and 4, vdc mode control lines
	m_crtc->ag_w(BIT(data, 3));
	m_crtc->css_w(BIT(data, 4));

	// bit 0 and 5, speaker
	m_speaker->level_w((BIT(data, 5) << 1) | BIT(data, 0));
}

void laser310h_state::latch_w(uint8_t data)
{
	vtech1_base_state::latch_w(data);

	m_crtc->gm0_w(BIT(data, 1));
	m_crtc->gm2_w(BIT(data, 1));
}


/***************************************************************************
    MEMORY BANKING
***************************************************************************/

void laser310h_state::video_bank_w(uint8_t data)
{
	m_vbank->set_entry(data & 0x03);
}


/***************************************************************************
    VIDEO EMULATION
***************************************************************************/

uint8_t vtech1_base_state::vram_r(memory_share_creator<uint8_t> &vram, offs_t offset)
{
	if (offset == ~0) return 0xff;

	m_crtc->inv_w(BIT(vram[offset], 6));
	m_crtc->as_w(BIT(vram[offset], 7));

	return vram[offset];
}

uint8_t vtech1_state::mc6847_videoram_r(offs_t offset)
{
	return vram_r(m_vram, offset);
}

uint8_t laser310h_state::mc6847_videoram_r(offs_t offset)
{
	return vram_r(m_vram, offset);
}


/***************************************************************************
    DRIVER INIT
***************************************************************************/

void vtech1_state::machine_start()
{
	m_vbank->configure_entries(0, 1, m_vram, 0x800);
	m_vbank->set_entry(0);
}

void laser310h_state::machine_start()
{
	// the SHRG mod replaces the standard videoram chip with an 8k chip
	m_vbank->configure_entries(0, 4, m_vram, 0x800);
	m_vbank->set_entry(0);
}


/***************************************************************************
    ADDRESS MAPS
***************************************************************************/

void vtech1_base_state::laser110_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).rom(); // basic rom
	map(0x4000, 0x67ff).noprw(); // cartridge space
	map(0x6800, 0x6fff).rw(FUNC(vtech1_base_state::keyboard_r), FUNC(vtech1_base_state::latch_w));
	map(0x7000, 0x77ff).bankrw("vbank");
	map(0x7800, 0x7fff).ram(); // 2k user ram
	map(0x8000, 0xffff).noprw(); // expansion ram
}

void vtech1_base_state::laser210_mem(address_map &map)
{
	laser110_mem(map);
	map(0x7800, 0x8fff).ram(); // 6k user ram
}

void vtech1_base_state::laser310_mem(address_map &map)
{
	laser110_mem(map);
	map(0x7800, 0xb7ff).ram(); // 16k user ram
}

void vtech1_base_state::vtech1_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0xff).noprw(); // completely handled by expansion devices
}

void laser310h_state::vtech1_shrg_mem(address_map &map)
{
	laser310_mem(map);
	map(0x6800, 0x6fff).w(FUNC(laser310h_state::latch_w));
}

void laser310h_state::vtech1_shrg_io(address_map &map)
{
	map.global_mask(0xff);
	vtech1_io(map);
	map(0xd0, 0xdf).w(FUNC(laser310h_state::video_bank_w));
}


/***************************************************************************
    INPUT PORTS
***************************************************************************/

static INPUT_PORTS_START(vtech1)
	PORT_START("X0")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R       RETURN  LEFT$")   PORT_CODE(KEYCODE_R)     PORT_CHAR('R')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q       FOR     CHR$")    PORT_CODE(KEYCODE_Q)     PORT_CHAR('Q')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E       NEXT    LEN(")    PORT_CODE(KEYCODE_E)     PORT_CHAR('E')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W       TO      VAL(")    PORT_CODE(KEYCODE_W)     PORT_CHAR('W')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T       THEN    MID$")    PORT_CODE(KEYCODE_T)     PORT_CHAR('T')

	PORT_START("X1")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F       GOSUB   RND(")    PORT_CODE(KEYCODE_F)     PORT_CHAR('F')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A       MODE(   ASC(")    PORT_CODE(KEYCODE_A)     PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D       DIM     RESTORE") PORT_CODE(KEYCODE_D)     PORT_CHAR('D')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL")                    PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S       STEP    STR$(")   PORT_CODE(KEYCODE_S)     PORT_CHAR('S')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G       GOTO    STOP")    PORT_CODE(KEYCODE_G)     PORT_CHAR('G')

	PORT_START("X2")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V       LPRINT  USR")     PORT_CODE(KEYCODE_V)     PORT_CHAR('V')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z       PEEK(   INP")     PORT_CODE(KEYCODE_Z)     PORT_CHAR('Z')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C       CONT    COPY")    PORT_CODE(KEYCODE_C)     PORT_CHAR('C')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT")                   PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X       POKE    OUT")     PORT_CODE(KEYCODE_X)     PORT_CHAR('X')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B       LLIST   SOUND")   PORT_CODE(KEYCODE_B)     PORT_CHAR('B')

	PORT_START("X3")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4  $    VERIFY  ATN(")    PORT_CODE(KEYCODE_4)     PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1  !    CSAVE   SIN(")    PORT_CODE(KEYCODE_1)     PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3  #    CRUN    TAN(")    PORT_CODE(KEYCODE_3)     PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2  \"    CLOAD   COS(")   PORT_CODE(KEYCODE_2)     PORT_CHAR('2') PORT_CHAR('\"')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5  %    LIST    LOG(")    PORT_CODE(KEYCODE_5)     PORT_CHAR('5') PORT_CHAR('%')

	PORT_START("X4")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M  \\    \xE2\x86\x90")   PORT_CODE(KEYCODE_M)     PORT_CHAR('M') PORT_CHAR('\\') PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE   \xE2\x86\x93")    PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') PORT_CHAR('~')  PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",  <    \xE2\x86\x92")    PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')  PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".  >    \xE2\x86\x91")    PORT_CODE(KEYCODE_STOP)  PORT_CHAR('.') PORT_CHAR('>')  PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N  ^    COLOR   USING")   PORT_CODE(KEYCODE_N)     PORT_CHAR('N') PORT_CHAR('^')

	PORT_START("X5")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7  '    END     SGN(")    PORT_CODE(KEYCODE_7)     PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0  @    DATA    INT(")    PORT_CODE(KEYCODE_0)     PORT_CHAR('0') PORT_CHAR('@')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8  (    NEW     SQR(")    PORT_CODE(KEYCODE_8)     PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-  =    [Break]")         PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')  PORT_CHAR(UCHAR_MAMEKEY(CANCEL))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9  )    READ    ABS(")    PORT_CODE(KEYCODE_9)     PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6  &    RUN     EXP(")    PORT_CODE(KEYCODE_6)     PORT_CHAR('6') PORT_CHAR('&')

	PORT_START("X6")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U       IF      INKEY$")  PORT_CODE(KEYCODE_U)     PORT_CHAR('U')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P  ]    PRINT   NOT")     PORT_CODE(KEYCODE_P)     PORT_CHAR('P') PORT_CHAR(']')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I       INPUT   AND")     PORT_CODE(KEYCODE_I)     PORT_CHAR('I')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RETURN  [Function]")      PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O  [    LET     OR")      PORT_CODE(KEYCODE_O)     PORT_CHAR('O') PORT_CHAR('[')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y       ELSE    RIGHT$(") PORT_CODE(KEYCODE_Y)     PORT_CHAR('Y')

	PORT_START("X7")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J       REM     RESET")   PORT_CODE(KEYCODE_J)     PORT_CHAR('J')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";  +    [Rubout]")        PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')  PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K  /    TAB(    POINT")   PORT_CODE(KEYCODE_K)     PORT_CHAR('K') PORT_CHAR('/')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(":  *    [Inverse]")       PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')  PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L  ?    [Insert]")        PORT_CODE(KEYCODE_L)     PORT_CHAR('L') PORT_CHAR('?')  PORT_CHAR(UCHAR_MAMEKEY(INSERT))
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H       CLS     SET")     PORT_CODE(KEYCODE_H)     PORT_CHAR('H')
INPUT_PORTS_END


/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

static const double speaker_levels[] = { 0.0, 1.0, -1.0, 0.0 };

void vtech1_base_state::vtech1(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, VTECH1_CLK);  /* 3.57950 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &vtech1_base_state::laser110_mem);
	m_maincpu->set_addrmap(AS_IO, &vtech1_base_state::vtech1_io);

	// video hardware
	MC6847(config, m_crtc, XTAL(4'433'619), true);
	m_crtc->set_screen("screen");
	m_crtc->fsync_wr_callback().set_inputline(m_maincpu, 0).invert();
	m_crtc->set_get_fixed_mode(mc6847_device::MODE_GM1);
	// GM2 = GND, GM0 = GND, INTEXT = GND
	// other lines not connected

	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).set_levels(4, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.75);

	// peripheral and memory expansion slots
	VTECH_IOEXP_SLOT(config, m_ioexp);
	m_ioexp->set_iospace(m_maincpu, AS_IO);

	VTECH_MEMEXP_SLOT(config, m_memexp);
	m_memexp->set_memspace(m_maincpu, AS_PROGRAM);
	m_memexp->set_iospace(m_maincpu, AS_IO);

	// snapshot
	snapshot_image_device &snapshot(SNAPSHOT(config, "snapshot", "vz"));
	snapshot.set_delay(attotime::from_double(2.0));
	snapshot.set_load_callback(FUNC(vtech1_base_state::snapshot_cb));
	snapshot.set_interface("vzsnap");

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(vtech1_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("vtech1_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("vz_cass");
	SOFTWARE_LIST(config, "snap_list").set_original("vz_snap");
}

void vtech1_state::laser110(machine_config &config)
{
	vtech1(config);

	m_crtc->input_callback().set(FUNC(vtech1_state::mc6847_videoram_r));
	m_crtc->set_black_and_white(true);
}

void vtech1_state::laser200(machine_config &config)
{
	laser110(config);

	m_crtc->set_black_and_white(false);
}

void vtech1_state::laser210(machine_config &config)
{
	laser200(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &vtech1_base_state::laser210_mem);
}

void vtech1_state::laser310(machine_config &config)
{
	laser200(config);

	m_maincpu->set_clock(VZ300_XTAL1_CLK / 5);  /* 3.546894 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &vtech1_base_state::laser310_mem);
}

void laser310h_state::laser310h(machine_config &config)
{
	vtech1(config);

	m_maincpu->set_clock(VZ300_XTAL1_CLK / 5);  /* 3.546894 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &laser310h_state::vtech1_shrg_mem);
	m_maincpu->set_addrmap(AS_IO, &laser310h_state::vtech1_shrg_io);

	m_crtc->input_callback().set(FUNC(laser310h_state::mc6847_videoram_r));
	m_crtc->set_get_fixed_mode(mc6847_device::MODE_GM1);
	// INTEXT = GND
	// other lines not connected
}


/***************************************************************************
    ROM DEFINITIONS
***************************************************************************/

ROM_START( laser110 )
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_LOAD("vtechv12.u09", 0x0000, 0x2000, CRC(99412d43) SHA1(6aed8872a0818be8e1b08ecdfd92acbe57a3c96d))
	ROM_LOAD("vtechv12.u10", 0x2000, 0x2000, CRC(e4c24e8b) SHA1(9d8fb3d24f3d4175b485cf081a2d5b98158ab2fb))
ROM_END

// The VZ-200 sold in Germany and the Netherlands came with BASIC V1.1, which is currently not dumped.
ROM_START( vz200de )
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_LOAD("vtechv11.u09", 0x0000, 0x2000, NO_DUMP)
	ROM_LOAD("vtechv11.u10", 0x2000, 0x2000, NO_DUMP)
ROM_END

#define rom_laser200    rom_laser110
#define rom_fellow      rom_laser110

// It's possible that the Texet TX-8000 came with BASIC V1.0, but this needs to be verified
#define rom_tx8000  rom_laser110

ROM_START( laser210 )
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_LOAD("vtechv20.u09", 0x0000, 0x2000, CRC(cc854fe9) SHA1(6e66a309b8e6dc4f5b0b44e1ba5f680467353d66))
	ROM_LOAD("vtechv20.u10", 0x2000, 0x2000, CRC(7060f91a) SHA1(8f3c8f24f97ebb98f3c88d4e4ba1f91ffd563440))
ROM_END

ROM_START( vz200 )
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_SYSTEM_BIOS(0, "basic20", "BASIC V2.0")
	ROMX_LOAD("vtechv20.u09",  0x0000, 0x2000, CRC(cc854fe9) SHA1(6e66a309b8e6dc4f5b0b44e1ba5f680467353d66), ROM_BIOS(0))
	ROMX_LOAD("vtechv20.u10",  0x2000, 0x2000, CRC(7060f91a) SHA1(8f3c8f24f97ebb98f3c88d4e4ba1f91ffd563440), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "enhanced", "VZ-200 Enhanced BASIC V1.01")
	ROMX_LOAD("vz200_v101.u9", 0x0000, 0x2000, CRC(70340b97) SHA1(eb3f3c8cf0cfa7acd646e89a90a3edf9e556cab6), ROM_BIOS(1))
	ROMX_LOAD("vtechv20.u10",  0x2000, 0x2000, CRC(7060f91a) SHA1(8f3c8f24f97ebb98f3c88d4e4ba1f91ffd563440), ROM_BIOS(1))
ROM_END

ROM_START( laser310 )
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_SYSTEM_BIOS(0, "basic20", "BASIC V2.0")
	ROMX_LOAD("vtechv20.u12", 0x0000, 0x4000, CRC(613de12c) SHA1(f216c266bc09b0dbdbad720796e5ea9bc7d91e53), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "basic21", "BASIC V2.1 (hack)")
	ROMX_LOAD("vtechv21.u12", 0x0000, 0x4000, CRC(f7df980f) SHA1(5ba14a7a2eedca331b033901080fa5d205e245ea), ROM_BIOS(1))
ROM_END

#define rom_vz300       rom_laser310
#define rom_laser310h   rom_laser310

} // anonymous namespace


/***************************************************************************
    GAME DRIVERS
***************************************************************************/

//    YEAR  NAME       PARENT    COMPAT  MACHINE    INPUT   CLASS            INIT        COMPANY                   FULLNAME                          FLAGS
COMP( 1983, laser110,  0,        0,      laser110,  vtech1, vtech1_state,    empty_init, "Video Technology",       "Laser 110",                      MACHINE_SUPPORTS_SAVE )
COMP( 1983, laser200,  0,        0,      laser200,  vtech1, vtech1_state,    empty_init, "Video Technology",       "Laser 200",                      MACHINE_SUPPORTS_SAVE )
COMP( 1983, vz200de,   laser200, 0,      laser200,  vtech1, vtech1_state,    empty_init, "Video Technology",       "VZ-200 (Germany & Netherlands)", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1983, fellow,    laser200, 0,      laser200,  vtech1, vtech1_state,    empty_init, "Salora",                 "Fellow (Finland)",               MACHINE_SUPPORTS_SAVE )
COMP( 1983, tx8000,    laser200, 0,      laser200,  vtech1, vtech1_state,    empty_init, "Texet",                  "TX-8000 (UK)",                   MACHINE_SUPPORTS_SAVE )
COMP( 1984, laser210,  0,        0,      laser210,  vtech1, vtech1_state,    empty_init, "Video Technology",       "Laser 210",                      MACHINE_SUPPORTS_SAVE )
COMP( 1984, vz200,     laser210, 0,      laser210,  vtech1, vtech1_state,    empty_init, "Dick Smith Electronics", "VZ-200 (Oceania)",               MACHINE_SUPPORTS_SAVE )
COMP( 1984, laser310,  0,        0,      laser310,  vtech1, vtech1_state,    empty_init, "Video Technology",       "Laser 310",                      MACHINE_SUPPORTS_SAVE )
COMP( 1984, vz300,     laser310, 0,      laser310,  vtech1, vtech1_state,    empty_init, "Dick Smith Electronics", "VZ-300 (Oceania)",               MACHINE_SUPPORTS_SAVE )
COMP( 1984, laser310h, laser310, 0,      laser310h, vtech1, laser310h_state, empty_init, "Video Technology",       "Laser 310 (SHRG)",               MACHINE_UNOFFICIAL | MACHINE_SUPPORTS_SAVE )



vtech2.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Juergen Buchmueller
/***************************************************************************
    vtech2.c

    system driver
    Juergen Buchmueller <pullmoll@t-online.de> MESS driver, Jan 2000
    Davide Moretti <dave@rimini.com> ROM dump and hardware description

    LASER 350 (it has only 16K of RAM)
    FFFF|-------|
        | Empty |
        |   5   |
    C000|-------|
        |  RAM  |
        |   3   |
    8000|-------|-------|-------|
        |  ROM  |Display|  I/O  |
        |   1   |   3   |   2   |
    4000|-------|-------|-------|
        |  ROM  |
        |   0   |
    0000|-------|


    Laser 500/700 with 64K of RAM and
    Laser 350 with 64K RAM expansion module
    FFFF|-------|
        |  RAM  |
        |   5   |
    C000|-------|
        |  RAM  |
        |   4   |
    8000|-------|-------|-------|
        |  ROM  |Display|  I/O  |
        |   1   |   7   |   2   |
    4000|-------|-------|-------|
        |  ROM  |
        |   0   |
    0000|-------|


    Bank "maincpu"       Contents
    0    0x00000 - 0x03fff ROM 1st half
    1    0x04000 - 0x07fff ROM 2nd half
    2           n/a        I/O 2KB area (mirrored 8 times?)
    3    0x0c000 - 0x0ffff Display RAM (16KB) present in Laser 350 only!
    4    0x10000 - 0x13fff RAM #4
    5    0x14000 - 0x17fff RAM #5
    6    0x18000 - 0x1bfff RAM #6
    7    0x1c000 - 0x1ffff RAM #7 (Display RAM with 64KB)
    8    0x20000 - 0x23fff RAM #8 (Laser 700 or 128KB extension)
    9    0x24000 - 0x27fff RAM #9
    A    0x28000 - 0x2bfff RAM #A
    B    0x2c000 - 0x2ffff RAM #B
    C    0x30000 - 0x33fff ROM expansion
    D    0x34000 - 0x34fff ROM expansion
    E    0x38000 - 0x38fff ROM expansion
    F    0x3c000 - 0x3ffff ROM expansion

    TODO:
    - Ram pak
    - undumped DOS ROM
    - need software

    Cartslot works, even though it seems there were no game carts made
    for these systems. The bios checks the first few bytes for a particular
    sequence; if found, the cart is executed at the next byte.
    We allow a cart of any size up to 64k, and it gets loaded into bank 12,
    continuing on to banks 13, 14 and 15 if needed. The bios checks for the
    sequence at each bank boundary.

***************************************************************************/

#include "emu.h"
#include "vtech2.h"
#include "cpu/z80/z80.h"
#include "formats/vt_cas.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"



void vtech2_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).m(m_banka, FUNC(address_map_bank_device::amap8));
	map(0x4000, 0x7fff).m(m_bankb, FUNC(address_map_bank_device::amap8));
	map(0x8000, 0xbfff).m(m_bankc, FUNC(address_map_bank_device::amap8));
	map(0xc000, 0xffff).m(m_bankd, FUNC(address_map_bank_device::amap8));
}

void vtech2_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map.unmap_value_high();
	map(0x00, 0xff).noprw();
	map(0x10, 0x1f).rw(FUNC(vtech2_state::laser_fdc_r), FUNC(vtech2_state::laser_fdc_w));
	map(0x40, 0x40).lw8(NAME([this] (u8 data) { m_banka->set_bank(data & 15); }));
	map(0x41, 0x41).lw8(NAME([this] (u8 data) { m_bankb->set_bank(data & 15); }));
	map(0x42, 0x42).lw8(NAME([this] (u8 data) { m_bankc->set_bank(data & 15); }));
	map(0x43, 0x43).lw8(NAME([this] (u8 data) { m_bankd->set_bank(data & 15); }));
	map(0x44, 0x44).w(FUNC(vtech2_state::laser_bg_mode_w));
	map(0x45, 0x45).w(FUNC(vtech2_state::laser_two_color_w));
}

// Laser 350, 16k ram
void vtech2_state::m_map350(address_map &map)
{
	map(0x00000, 0x03fff).rom().region("maincpu", 0);
	map(0x04000, 0x07fff).rom().region("maincpu", 0x4000);
	map(0x08000, 0x0bfff).rw(FUNC(vtech2_state::mmio_r), FUNC(vtech2_state::mmio_w));
	map(0x0c000, 0x0ffff).ram().share(m_vram);
	map(0x10000, 0x13fff).noprw();
	map(0x14000, 0x17fff).noprw();
	map(0x18000, 0x1bfff).noprw();
	map(0x1c000, 0x1ffff).noprw();
	map(0x20000, 0x23fff).noprw(); // TODO: 64k ram expansion pak
	map(0x24000, 0x27fff).noprw(); // TODO: 64k ram expansion pak
	map(0x28000, 0x2bfff).noprw(); // TODO: 64k ram expansion pak
	map(0x2c000, 0x2ffff).noprw(); // TODO: 64k ram expansion pak
	map(0x30000, 0x3ffff).r(FUNC(vtech2_state::cart_r));
}

// Laser 500, 64k ram
void vtech2_state::m_map500(address_map &map)
{
	map(0x00000, 0x03fff).rom().region("maincpu", 0);
	map(0x04000, 0x07fff).rom().region("maincpu", 0x4000);
	map(0x08000, 0x0bfff).rw(FUNC(vtech2_state::mmio_r), FUNC(vtech2_state::mmio_w));
	map(0x0c000, 0x0ffff).noprw();
	map(0x10000, 0x13fff).ram();
	map(0x14000, 0x17fff).ram();
	map(0x18000, 0x1bfff).ram();
	map(0x1c000, 0x1ffff).ram().share(m_vram);
	map(0x20000, 0x23fff).noprw(); // TODO: 64k ram expansion pak
	map(0x24000, 0x27fff).noprw(); // TODO: 64k ram expansion pak
	map(0x28000, 0x2bfff).noprw(); // TODO: 64k ram expansion pak
	map(0x2c000, 0x2ffff).noprw(); // TODO: 64k ram expansion pak
	map(0x30000, 0x3ffff).r(FUNC(vtech2_state::cart_r));
}

// Laser 700, 128k ram
void vtech2_state::m_map700(address_map &map)
{
	map(0x00000, 0x03fff).rom().region("maincpu", 0);
	map(0x04000, 0x07fff).rom().region("maincpu", 0x4000);
	map(0x08000, 0x0bfff).rw(FUNC(vtech2_state::mmio_r), FUNC(vtech2_state::mmio_w));
	map(0x0c000, 0x0ffff).noprw();
	map(0x10000, 0x13fff).ram();
	map(0x14000, 0x17fff).ram();
	map(0x18000, 0x1bfff).ram();
	map(0x1c000, 0x1ffff).ram().share(m_vram);
	map(0x20000, 0x23fff).ram();
	map(0x24000, 0x27fff).ram();
	map(0x28000, 0x2bfff).ram();
	map(0x2c000, 0x2ffff).ram();
	map(0x30000, 0x3ffff).r(FUNC(vtech2_state::cart_r));
}


static INPUT_PORTS_START( laser500 )
	PORT_START("ROW0")  /* KEY ROW 0 */
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)            PORT_CHAR('Z') PORT_CHAR('z')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)            PORT_CHAR('X') PORT_CHAR('x')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)            PORT_CHAR('C') PORT_CHAR('c')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)            PORT_CHAR('V') PORT_CHAR('v')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)            PORT_CHAR('B') PORT_CHAR('b')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)            PORT_CHAR('N') PORT_CHAR('n')

	PORT_START("ROW1") /* KEY ROW 1 */
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)     PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)            PORT_CHAR('A') PORT_CHAR('a')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)            PORT_CHAR('S') PORT_CHAR('s')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)            PORT_CHAR('D') PORT_CHAR('d')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)            PORT_CHAR('F') PORT_CHAR('f')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)            PORT_CHAR('G') PORT_CHAR('g')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)            PORT_CHAR('H') PORT_CHAR('h')

	PORT_START("ROW2") /* KEY ROW 2 */
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)          PORT_CHAR(9)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)            PORT_CHAR('Q') PORT_CHAR('q')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)            PORT_CHAR('W') PORT_CHAR('w')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)            PORT_CHAR('E') PORT_CHAR('e')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)            PORT_CHAR('R') PORT_CHAR('r')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)            PORT_CHAR('T') PORT_CHAR('t')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)            PORT_CHAR('Y') PORT_CHAR('y')

	PORT_START("ROW3") /* KEY ROW 3 */
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)          PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)            PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)            PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)            PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)            PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)            PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)            PORT_CHAR('6') PORT_CHAR('^')

	PORT_START("ROW4") /* KEY ROW 4 */
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)       PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)        PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)            PORT_CHAR('0') PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)            PORT_CHAR('9') PORT_CHAR('(')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)            PORT_CHAR('8') PORT_CHAR('*')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)            PORT_CHAR('7') PORT_CHAR('&')

	PORT_START("ROW5") /* KEY ROW 5 */
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BS") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_CONFNAME( 0x30, 0x30, "Language")
	PORT_CONFSETTING(    0x10, "French")
	PORT_CONFSETTING(    0x20, "German")
	PORT_CONFSETTING(    0x30, "English")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)            PORT_CHAR('P') PORT_CHAR('p')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)            PORT_CHAR('O') PORT_CHAR('o')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)            PORT_CHAR('I') PORT_CHAR('i')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)            PORT_CHAR('U') PORT_CHAR('u')

	PORT_START("ROW6") /* KEY ROW 6 */
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)        PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)        PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)            PORT_CHAR('L') PORT_CHAR('l')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)            PORT_CHAR('K') PORT_CHAR('k')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)            PORT_CHAR('J') PORT_CHAR('j')

	PORT_START("ROW7") /* KEY ROW 7 */
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Graph") PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(RALT))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)        PORT_CHAR('`') PORT_CHAR('~')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)        PORT_CHAR(' ')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)        PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)         PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)        PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)            PORT_CHAR('M') PORT_CHAR('m')

	PORT_START("ROWA") /* KEY ROW A */
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1)           PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2)           PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3)           PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4)           PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("ROWB") /* KEY ROW B */
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10)          PORT_CHAR(UCHAR_MAMEKEY(F10))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9)           PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8)           PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7)           PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6)           PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5)           PORT_CHAR(UCHAR_MAMEKEY(F5))

	PORT_START("ROWC") /* KEY ROW C */
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cap Lock") PORT_CODE(KEYCODE_CAPSLOCK)   PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del Line") PORT_CODE(KEYCODE_PGUP)       PORT_CHAR(UCHAR_MAMEKEY(PGUP))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME)         PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)           PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)         PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)        PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)         PORT_CHAR(UCHAR_MAMEKEY(DOWN))

	PORT_START("ROWD") /* KEY ROW D */
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)    PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)    PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_END)          PORT_CHAR(U'μ') PORT_CHAR(U'£')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del") PORT_CODE(KEYCODE_DEL)             PORT_CHAR(UCHAR_MAMEKEY(DEL))
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ins") PORT_CODE(KEYCODE_INSERT)          PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(vtech2_state::reset_button), 0)
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(vtech2_state::reset_button)
{
	// RESET button is directly wired to Z80 RESET pin, BIOS will detect it (doesn't reset the computer)
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}


/* 2008-05 FP: I wasn't able to find a good picture of the laser 350 to verify the mapping of the emulated keyboard.
However, old-computers.com describes it as a laser 500/700 in a laser 300/310 case. The missing inputs seem to
confirm this. */
static INPUT_PORTS_START( laser350 )
	PORT_INCLUDE( laser500 )

	PORT_MODIFY("ROW2") /* KEY ROW 2 */
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED) /* TAB not on the Laser350 */

	PORT_MODIFY("ROW3") /* KEY ROW 3 */
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED) /* ESC not on the Laser350 */

	PORT_MODIFY("ROW5") /* KEY ROW 5 */
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED) /* BS not on the Laser350 */

	PORT_MODIFY("ROW7") /* KEY ROW 7 */
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED) /* GRAPH not on the Laser350 */

	PORT_MODIFY("ROWA") /* KEY ROW A */
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED) /* not on the Laser350 */

	PORT_MODIFY("ROWB") /* KEY ROW B */
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED) /* not on the Laser350 */

	PORT_MODIFY("ROWC") /* KEY ROW C */
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED) /* not on the Laser350 */

	PORT_MODIFY("ROWD") /* KEY ROW D */
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED) /* not on the Laser350 */

	PORT_MODIFY("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED) /* not on the Laser350 */
INPUT_PORTS_END


static const gfx_layout charlayout_80 =
{
	8,8,                    /* 8 x 8 characters */
	1024,                    /* characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes; 1 bit per pixel */
	/* x offsets */
	{ 7, 6, 5, 4, 3, 2, 1, 0 },
	/* y offsets */
	{ 0*8,1*8,2*8,3*8,4*8,5*8,6*8,7*8 },
	8*8                     /* every char takes 8 bytes */
};

static const gfx_layout charlayout_40 =
{
	8*2,8,                  /* 8*2 x 8 characters */
	1024,                    /* characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes; 1 bit per pixel */
	/* x offsets */
	{ 7,7, 6,6, 5,5, 4,4, 3,3, 2,2, 1,1, 0,0 },
	/* y offsets */
	{ 0*8,1*8,2*8,3*8,4*8,5*8,6*8,7*8 },
	8*8                     /* every char takes 8 bytes */
};

static const gfx_layout gfxlayout_1bpp =
{
	8,1,                    /* 8x1 pixels */
	256,                    /* 256 codes */
	1,                      /* 1 bit per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 7,6,5,4,3,2,1,0 },
	/* y offsets */
	{ 0 },
	8                       /* one byte per code */
};

static const gfx_layout gfxlayout_1bpp_dw =
{
	8*2,1,                  /* 8 times 2x1 pixels */
	256,                    /* 256 codes */
	1,                      /* 1 bit per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 7,7,6,6,5,5,4,4,3,3,2,2,1,1,0,0 },
	/* y offsets */
	{ 0 },
	8                       /* one byte per code */
};

static const gfx_layout gfxlayout_1bpp_qw =
{
	8*4,1,                  /* 8 times 4x1 pixels */
	256,                    /* 256 codes */
	1,                      /* 1 bit per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 7,7,7,7,6,6,6,6,5,5,5,5,4,4,4,4,3,3,3,3,2,2,2,2,1,1,1,1,0,0,0,0 },
	/* y offsets */
	{ 0 },
	8                       /* one byte per code */
};

static const gfx_layout gfxlayout_4bpp =
{
	2*4,1,                  /* 2 times 4x1 pixels */
	256,                    /* 256 codes */
	4,                      /* 4 bit per pixel */
	{ 0,1,2,3 },            /* four bitplanes */
	/* x offsets */
	{ 4,4,4,4, 0,0,0,0 },
	/* y offsets */
	{ 0 },
	2*4                     /* one byte per code */
};

static const gfx_layout gfxlayout_4bpp_dh =
{
	2*4,2,                  /* 2 times 4x2 pixels */
	256,                    /* 256 codes */
	4,                      /* 4 bit per pixel */
	{ 0,1,2,3 },            /* four bitplanes */
	/* x offsets */
	{ 4,4,4,4, 0,0,0,0 },
	/* y offsets */
	{ 0,0 },
	2*4                     /* one byte per code */
};

static GFXDECODE_START( gfx_vtech2 )
	GFXDECODE_ENTRY( "gfx1", 0, charlayout_80, 0, 256 )
	GFXDECODE_ENTRY( "gfx1", 0, charlayout_40, 0, 256 )
	GFXDECODE_ENTRY( "gfx2", 0, gfxlayout_1bpp, 0, 256 )
	GFXDECODE_ENTRY( "gfx2", 0, gfxlayout_1bpp_dw, 0, 256 )
	GFXDECODE_ENTRY( "gfx2", 0, gfxlayout_1bpp_qw, 0, 256 )
	GFXDECODE_ENTRY( "gfx2", 0, gfxlayout_4bpp, 2*256, 1 )
	GFXDECODE_ENTRY( "gfx2", 0, gfxlayout_4bpp_dh, 2*256, 1 )
GFXDECODE_END


static constexpr rgb_t vt_colors[] =
{
	rgb_t::black(),
	{ 0x00, 0x00, 0x7f },  // blue
	{ 0x00, 0x7f, 0x00 },  // green
	{ 0x00, 0x7f, 0x7f },  // cyan
	{ 0x7f, 0x00, 0x00 },  // red
	{ 0x7f, 0x00, 0x7f },  // magenta
	{ 0x7f, 0x7f, 0x00 },  // yellow
	{ 0xa0, 0xa0, 0xa0 },  // bright grey
	{ 0x7f, 0x7f, 0x7f },  // dark grey
	{ 0x00, 0x00, 0xff },  // bright blue
	{ 0x00, 0xff, 0x00 },  // bright green
	{ 0x00, 0xff, 0xff },  // bright cyan
	{ 0xff, 0x00, 0x00 },  // bright red
	{ 0xff, 0x00, 0xff },  // bright magenta
	{ 0xff, 0xff, 0x00 },  // bright yellow
	rgb_t::white()
};


// Initialise the palette
void vtech2_state::vtech2_palette(palette_device &palette) const
{
	for (int i = 0; i < 16; i++)
		palette.set_indirect_color(i, vt_colors[i]);

	for (int i = 0; i < 256; i++)
	{
		palette.set_pen_indirect(2*i, i & 15);
		palette.set_pen_indirect(2*i + 1, i >> 4);
	}

	for (int i = 0; i < 16; i++)
		palette.set_pen_indirect(512 + i, i);
}

static const floppy_interface vtech2_floppy_interface =
{
	FLOPPY_STANDARD_5_25_SSDD_40,
	LEGACY_FLOPPY_OPTIONS_NAME(default),
	nullptr
};

void vtech2_state::laser350(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 3694700);        /* 3.694700 MHz */
	m_maincpu->set_addrmap(AS_PROGRAM, &vtech2_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &vtech2_state::io_map);
	m_maincpu->set_vblank_int("screen", FUNC(vtech2_state::irq0_line_hold));
	config.set_maximum_quantum(attotime::from_hz(60));

	ADDRESS_MAP_BANK(config, "banka").set_map(&vtech2_state::m_map350).set_options(ENDIANNESS_LITTLE, 8, 18, 0x4000);
	ADDRESS_MAP_BANK(config, "bankb").set_map(&vtech2_state::m_map350).set_options(ENDIANNESS_LITTLE, 8, 18, 0x4000);
	ADDRESS_MAP_BANK(config, "bankc").set_map(&vtech2_state::m_map350).set_options(ENDIANNESS_LITTLE, 8, 18, 0x4000);
	ADDRESS_MAP_BANK(config, "bankd").set_map(&vtech2_state::m_map350).set_options(ENDIANNESS_LITTLE, 8, 18, 0x4000);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(0);
	screen.set_size(88*8, 24*8+32);
	screen.set_visarea(0*8, 88*8-1, 0*8, 24*8+32-1);
	screen.set_screen_update(FUNC(vtech2_state::screen_update));
	screen.set_palette(m_palette);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_vtech2);
	PALETTE(config, m_palette, FUNC(vtech2_state::vtech2_palette), 512 + 16, 16);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.75);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(vtech2_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("vtech2_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("vtech2_cass");

	VTECH_IOEXP_SLOT(config, m_ioexp);
	m_ioexp->set_iospace(m_maincpu, AS_IO);

	/* cartridge */
	GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "vtech_cart", "rom,bin").set_device_load(FUNC(vtech2_state::cart_load));

	/* 5.25" Floppy drive */
	LEGACY_FLOPPY(config, m_laser_file[0], 0, &vtech2_floppy_interface);
}


void vtech2_state::laser500(machine_config &config)
{
	laser350(config);

	ADDRESS_MAP_BANK(config.replace(), "banka").set_map(&vtech2_state::m_map500).set_options(ENDIANNESS_LITTLE, 8, 18, 0x4000);
	ADDRESS_MAP_BANK(config.replace(), "bankb").set_map(&vtech2_state::m_map500).set_options(ENDIANNESS_LITTLE, 8, 18, 0x4000);
	ADDRESS_MAP_BANK(config.replace(), "bankc").set_map(&vtech2_state::m_map500).set_options(ENDIANNESS_LITTLE, 8, 18, 0x4000);
	ADDRESS_MAP_BANK(config.replace(), "bankd").set_map(&vtech2_state::m_map500).set_options(ENDIANNESS_LITTLE, 8, 18, 0x4000);
}


void vtech2_state::laser700(machine_config &config)
{
	laser350(config);

	ADDRESS_MAP_BANK(config.replace(), "banka").set_map(&vtech2_state::m_map700).set_options(ENDIANNESS_LITTLE, 8, 18, 0x4000);
	ADDRESS_MAP_BANK(config.replace(), "bankb").set_map(&vtech2_state::m_map700).set_options(ENDIANNESS_LITTLE, 8, 18, 0x4000);
	ADDRESS_MAP_BANK(config.replace(), "bankc").set_map(&vtech2_state::m_map700).set_options(ENDIANNESS_LITTLE, 8, 18, 0x4000);
	ADDRESS_MAP_BANK(config.replace(), "bankd").set_map(&vtech2_state::m_map700).set_options(ENDIANNESS_LITTLE, 8, 18, 0x4000);

	/* Second 5.25" floppy drive */
	LEGACY_FLOPPY(config, m_laser_file[1], 0, &vtech2_floppy_interface);
}


ROM_START(laser350)
	ROM_REGION(0x8000,"maincpu",0)
	ROM_LOAD("27-0401-00-00.u6", 0x0000, 0x8000, CRC(9bed01f7) SHA1(3210fddfab2f4c7855fa902fb8e2fc18d10d48f1))

	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "27-393-00.u10", 0x0000, 0x2000, CRC(d47313a2) SHA1(4650e8e339aad628c0e5d8a1944b21abff793446) )

	ROM_REGION(0x0100,"gfx2",ROMREGION_ERASEFF)
	/* initialized in init_laser */
ROM_END


ROM_START(laser500) // based on the picture at http://www.8bit-museum.de/hardware/laser500pcb-h.jpg
// There should be two roms, one 0x2000 long for the font at u10, and one longer one for the os rom at u6.
	ROM_REGION(0x8000,"maincpu",0)
	ROM_LOAD("27-0401-00-00.u6", 0x0000, 0x8000, CRC(9bed01f7) SHA1(3210fddfab2f4c7855fa902fb8e2fc18d10d48f1)) // may be dumped at wrong size; label is: "VTL 27-0401-00-00 // 6133-7081 // 8611MAK"

	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "27-393-00.u10", 0x0000, 0x2000, CRC(d47313a2) SHA1(4650e8e339aad628c0e5d8a1944b21abff793446) ) // label is "TMS 2364-25NL // D8614L // ZA234015 // 27-393-00/VT 85 // SINGAPORE"

	ROM_REGION(0x0100,"gfx2",ROMREGION_ERASEFF)
	/* initialized in init_laser */
ROM_END

ROM_START(laser700)
	ROM_REGION(0x8000,"maincpu",0)
	ROM_LOAD("27-0401-00-00.u6", 0x0000, 0x8000, CRC(9bed01f7) SHA1(3210fddfab2f4c7855fa902fb8e2fc18d10d48f1))

	ROM_REGION(0x2000,"gfx1",0)
	ROM_LOAD( "27-393-00.u10", 0x0000, 0x2000, CRC(d47313a2) SHA1(4650e8e339aad628c0e5d8a1944b21abff793446) )

	ROM_REGION(0x0100,"gfx2",ROMREGION_ERASEFF)
	/* initialized in init_laser */
ROM_END


/***************************************************************************

  Game driver(s)

***************************************************************************/

//    YEAR   NAME      PARENT    COMPAT  MACHINE   INPUT     CLASS         INIT        COMPANY             FULLNAME      FLAGS
COMP( 1985, laser350, 0,        0,      laser350, laser350, vtech2_state, init_laser, "Video Technology", "Laser 350" , MACHINE_SUPPORTS_SAVE )
COMP( 1985, laser500, laser350, 0,      laser500, laser500, vtech2_state, init_laser, "Video Technology", "Laser 500" , MACHINE_SUPPORTS_SAVE )
COMP( 1985, laser700, laser350, 0,      laser700, laser500, vtech2_state, init_laser, "Video Technology", "Laser 700" , MACHINE_SUPPORTS_SAVE )



vtech5303.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:


/*********************************************************************************************

    Skeleton driver for toy computers on VTech 5303 hardware.

    PCB with a 25VQ16A serial flash and a 8 MHz xtal on one side and a big glob on the other.

*********************************************************************************************/


#include "emu.h"

#include "cpu/m6502/w65c02.h"

#include "screen.h"
#include "speaker.h"


namespace {


class vtech5303_state : public driver_device
{
public:
	vtech5303_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen")
	{ }

	void vtech5303(machine_config &config) ATTR_COLD;

protected:
	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;

	uint32_t screen_update_vtech5303(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};

uint32_t vtech5303_state::screen_update_vtech5303(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

/* The Paw Patrol the keyboard has:
    -6 "character" buttons (Marshall, Rocky, Chase, Skye, Ryder, Rubble).
    -6 "activity" buttons (bone, letters, maths, pairs, maze, adventure).
    -26 leter keys in QWERTY layout (QWERTYUIOP ASDFGHJKL ZXCVBNM).
    -Mute button to the left of Z.
    -3 navigation buttons (right arrow, left arrow, OK).
    -Clock button.
    The Spanish version repurposes the mute button as a letter key and organises
    the letters in alphabetical order (ABCDEFGHIJ KLMNÑOPQR STUVWXYZ)
*/
INPUT_PORTS_START( vtech5303 )
INPUT_PORTS_END

void vtech5303_state::vtech5303(machine_config &config)
{
	W65C02(config, m_maincpu, 8_MHz_XTAL); // Unknown core and frequency, probably 6802

	SCREEN(config, m_screen, SCREEN_TYPE_LCD); // Monochrome 64x32 LCD screen
	m_screen->set_refresh_hz(60); // Guess
	m_screen->set_size(64, 32);
	m_screen->set_visarea(0, 64-1, 0, 32-1);
	m_screen->set_screen_update(FUNC(vtech5303_state::screen_update_vtech5303));

	SPEAKER(config, "mono").front_left();
}

// Spanish machine
ROM_START( pawmoviesp )
	ROM_REGION( 0x010000, "maincpu", 0 )
	ROM_LOAD( "internal.bin", 0x000000, 0x010000, NO_DUMP ) // Unknown CPU type, unknown internal ROM size

	ROM_REGION( 0x200300, "program", 0 )
	ROM_LOAD( "vtech_paw_patrol_5303_25vq16a.u5", 0x000000, 0x200300, CRC(fee8abd7) SHA1(4ea120246fb4a7efc699e0295864beba4e3317cc) )
ROM_END

} // anonymous namespace


CONS( 2022, pawmoviesp, 0, 0, vtech5303, vtech5303, vtech5303_state, empty_init, "VTech", "Paw Patrol: The Movie Learning Tablet (Spanish)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



wangpc.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*

    Wang Professional Computer

    http://www.seasip.info/VintagePC/wangpc.html

    chdman -createblankhd q540.chd 512 8 17 512

*/

/*

    TODO:

    - with quantum perfect cpu gets stuck @ 49c3 mov ss,cs:[52ah]
    - hard disk

*/

#include "emu.h"
#include "softlist_dev.h"
#include "bus/centronics/ctronics.h"
#include "bus/rs232/rs232.h"
#include "bus/wangpc/wangpc.h"
#include "cpu/i86/i86.h"
#include "imagedev/floppy.h"
#include "machine/am9517a.h"
#include "machine/i8255.h"
#include "machine/im6402.h"
#include "machine/pit8253.h"
#include "machine/pic8259.h"
#include "machine/scn_pci.h"
#include "machine/ram.h"
#include "machine/upd765.h"
#include "wangpckb.h"


namespace {

#define I8086_TAG       "i8086"
#define AM9517A_TAG     "am9517a"
#define I8259A_TAG      "i8259"
#define I8255A_TAG      "i8255a"
#define I8253_TAG       "i8253"
#define IM6402_TAG      "im6402"
#define SCN2661_TAG     "scn2661"
#define UPD765_TAG      "upd765"
#define CENTRONICS_TAG  "centronics"
#define RS232_TAG       "rs232"
#define WANGPC_KEYBOARD_TAG "wangpckb"
#define LED_DIAGNOSTIC  "led0"

class wangpc_state : public driver_device
{
public:
	// constructor
	wangpc_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, I8086_TAG),
		m_dmac(*this, AM9517A_TAG),
		m_pic(*this, I8259A_TAG),
		m_ppi(*this, I8255A_TAG),
		m_pit(*this, I8253_TAG),
		m_uart(*this, IM6402_TAG),
		m_epci(*this, SCN2661_TAG),
		m_fdc(*this, UPD765_TAG),
		m_ram(*this, RAM_TAG),
		m_floppy(*this, UPD765_TAG ":%u:525dd", 0U),
		m_centronics(*this, CENTRONICS_TAG),
		m_cent_data_in(*this, "cent_data_in"),
		m_cent_data_out(*this, "cent_data_out"),
		m_bus(*this, "wangpcbus"),
		m_sw(*this, "SW"),
		m_led_diagnostic(*this, LED_DIAGNOSTIC),
		m_timer2_irq(1),
		m_centronics_ack(1),
		m_dav(1),
		m_dma_eop(1),
		m_uart_dr(0),
		m_uart_tbre(0),
		m_fpu_irq(0),
		m_bus_irq2(0),
		m_enable_eop(0),
		m_disable_dreq2(0),
		m_fdc_drq(0),
		m_fdc_dd0(0),
		m_fdc_dd1(0),
		m_fdc_tc(0),
		m_ds1(false),
		m_ds2(false)
	{
	}

	void wangpc(machine_config &config);

private:
	required_device<cpu_device> m_maincpu;
	required_device<am9517a_device> m_dmac;
	required_device<pic8259_device> m_pic;
	required_device<i8255_device> m_ppi;
	required_device<pit8253_device> m_pit;
	required_device<im6402_device> m_uart;
	required_device<scn_pci_device> m_epci;
	required_device<upd765a_device> m_fdc;
	required_device<ram_device> m_ram;
	required_device_array<floppy_image_device, 2> m_floppy;
	required_device<centronics_device> m_centronics;
	required_device<input_buffer_device> m_cent_data_in;
	required_device<output_latch_device> m_cent_data_out;
	required_device<wangpcbus_device> m_bus;
	required_ioport m_sw;
	output_finder<> m_led_diagnostic;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void select_drive();
	void check_level1_interrupts();
	void check_level2_interrupts();
	void update_fdc_drq();
	void update_fdc_tc();

	void fdc_ctrl_w(uint8_t data);
	uint8_t deselect_drive1_r();
	void deselect_drive1_w(uint8_t data);
	uint8_t select_drive1_r();
	void select_drive1_w(uint8_t data);
	uint8_t deselect_drive2_r();
	void deselect_drive2_w(uint8_t data);
	uint8_t select_drive2_r();
	void select_drive2_w(uint8_t data);
	uint8_t motor1_on_r(offs_t offset);
	void motor1_on_w(offs_t offset, uint8_t data);
	uint8_t motor2_on_r(offs_t offset);
	void motor2_on_w(offs_t offset, uint8_t data);
	uint8_t fdc_reset_r();
	void fdc_reset_w(uint8_t data);
	uint8_t fdc_tc_r();
	void fdc_tc_w(uint8_t data);
	void dma_page_w(offs_t offset, uint8_t data);
	uint8_t status_r();
	void timer0_irq_clr_w(uint8_t data);
	uint8_t timer2_irq_clr_r();
	void nmi_mask_w(uint8_t data);
	uint8_t led_on_r();
	void fpu_mask_w(uint8_t data);
	uint8_t dma_eop_clr_r();
	void uart_tbre_clr_w(uint8_t data);
	uint8_t uart_r();
	void uart_w(uint8_t data);
	uint8_t centronics_r();
	void centronics_w(uint8_t data);
	uint8_t busy_clr_r();
	void acknlg_clr_w(uint8_t data);
	uint8_t led_off_r();
	void parity_nmi_clr_w(uint8_t data);
	uint8_t option_id_r();

	void hrq_w(int state);
	void eop_w(int state);
	uint8_t memr_r(offs_t offset);
	void memw_w(offs_t offset, uint8_t data);
	uint8_t ior2_r();
	void iow2_w(uint8_t data);
	void dack0_w(int state);
	void dack1_w(int state);
	void dack2_w(int state);
	void dack3_w(int state);
	uint8_t ppi_pa_r();
	uint8_t ppi_pb_r();
	uint8_t ppi_pc_r();
	void ppi_pc_w(uint8_t data);
	void pit0_w(int state);
	void pit2_w(int state);
	void uart_dr_w(int state);
	void uart_tbre_w(int state);
	void epci_irq_w(int state);
	void write_centronics_ack(int state);
	void write_centronics_busy(int state);
	void write_centronics_fault(int state);
	void write_centronics_perror(int state);
	void bus_irq2_w(int state);

	void fdc_irq(int state);
	void fdc_drq(int state);

	void on_disk0_load(floppy_image_device *image);
	void on_disk0_unload(floppy_image_device *image);
	void on_disk1_load(floppy_image_device *image);
	void on_disk1_unload(floppy_image_device *image);

	void wangpc_io(address_map &map) ATTR_COLD;
	void wangpc_mem(address_map &map) ATTR_COLD;

	uint8_t m_dma_page[4];
	int m_dack;

	int m_timer2_irq;
	int m_centronics_ack;
	int m_centronics_busy;
	int m_centronics_fault;
	int m_centronics_perror;
	int m_dav;
	int m_dma_eop;
	int m_uart_dr;
	int m_uart_tbre;
	int m_fpu_irq;
	int m_bus_irq2;

	int m_enable_eop;
	int m_disable_dreq2;
	int m_fdc_drq;
	int m_fdc_dd0;
	int m_fdc_dd1;
	int m_fdc_tc;
	int m_ds1;
	int m_ds2;

	int m_led[6];
};



//**************************************************************************
//  MACROS/CONSTANTS
//**************************************************************************

#define LOG 0



//**************************************************************************
//  IMPLEMENTATION
//**************************************************************************

void wangpc_state::select_drive()
{
	floppy_image_device *floppy = nullptr;

	if (m_ds1) floppy = m_floppy[0];
	if (m_ds2) floppy = m_floppy[1];

	m_fdc->set_floppy(floppy);
}

void wangpc_state::fdc_ctrl_w(uint8_t data)
{
	/*

	    bit     description

	    0       Enable /EOP
	    1       Disable /DREQ2
	    2       Clear drive 1 door disturbed interrupt
	    3       Clear drive 2 door disturbed interrupt
	    4
	    5
	    6
	    7

	*/

	m_enable_eop = BIT(data, 0);
	m_disable_dreq2 = BIT(data, 1);

	if (BIT(data, 2)) m_fdc_dd0 = 0;
	if (BIT(data, 3)) m_fdc_dd1 = 0;

	if (LOG)
	{
		logerror("%s: Enable /EOP %u\n", machine().describe_context(), m_enable_eop);
		logerror("%s: Disable /DREQ2 %u\n", machine().describe_context(), m_disable_dreq2);
	}

	update_fdc_tc();
	update_fdc_drq();
}


uint8_t wangpc_state::deselect_drive1_r()
{
	m_ds1 = false;
	select_drive();

	return 0xff;
}

void wangpc_state::deselect_drive1_w(uint8_t data)
{
	deselect_drive1_r();
}

uint8_t wangpc_state::select_drive1_r()
{
	m_ds1 = true;
	select_drive();

	return 0xff;
}

void wangpc_state::select_drive1_w(uint8_t data)
{
	select_drive1_r();
}

uint8_t wangpc_state::deselect_drive2_r()
{
	m_ds2 = false;
	select_drive();

	return 0xff;
}

void wangpc_state::deselect_drive2_w(uint8_t data)
{
	deselect_drive2_r();
}

uint8_t wangpc_state::select_drive2_r()
{
	m_ds2 = true;
	select_drive();

	return 0xff;
}

void wangpc_state::select_drive2_w(uint8_t data)
{
	select_drive2_r();
}

uint8_t wangpc_state::motor1_on_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		motor1_on_w(offset, 0);

	return 0xff;
}

void wangpc_state::motor1_on_w(offs_t offset, uint8_t data)
{
	if (LOG) logerror("%s: Drive 1 motor %s\n", machine().describe_context(), offset ? "ON" : "OFF");

	m_floppy[0]->mon_w(!offset);
}

uint8_t wangpc_state::motor2_on_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
		motor2_on_w(offset, 0);

	return 0xff;
}

void wangpc_state::motor2_on_w(offs_t offset, uint8_t data)
{
	if (LOG) logerror("%s: Drive 2 motor %s\n", machine().describe_context(), offset ? "ON" : "OFF");

	m_floppy[1]->mon_w(!offset);
}

uint8_t wangpc_state::fdc_reset_r()
{
	if (LOG) logerror("%s: FDC reset\n", machine().describe_context());

	m_fdc->reset();

	return 0xff;
}

void wangpc_state::fdc_reset_w(uint8_t data)
{
	fdc_reset_r();
}

uint8_t wangpc_state::fdc_tc_r()
{
	if (LOG) logerror("%s: FDC TC\n", machine().describe_context());

	m_fdc->tc_w(1);
	m_fdc->tc_w(0);

	return 0xff;
}

void wangpc_state::fdc_tc_w(uint8_t data)
{
	fdc_tc_r();
}


//-------------------------------------------------
//  dma_page_w -
//-------------------------------------------------

void wangpc_state::dma_page_w(offs_t offset, uint8_t data)
{
	if (LOG) logerror("%s: DMA page %u: %06x\n", machine().describe_context(), offset + 1, (data & 0x0f) << 16);

	m_dma_page[offset + 1] = data & 0x0f;
}


//-------------------------------------------------
//  status_r -
//-------------------------------------------------

uint8_t wangpc_state::status_r()
{
	/*

	    bit     description

	    0       Memory Parity Flag
	    1       I/O Error Flag
	    2       Unassigned
	    3       FDC Interrupt Flag
	    4       Door disturbed on drive 1
	    5       Door disturbed on drive 2
	    6       Door open on drive 1
	    7       Door open on drive 2

	*/

	uint8_t data = 0x03;

	// floppy interrupts
	data |= m_fdc->get_irq() << 3;
	data |= m_fdc_dd0 << 4;
	data |= m_fdc_dd1 << 5;
	data |= m_floppy[0]->exists() ? 0 : 0x40;
	data |= m_floppy[1]->exists() ? 0 : 0x80;

	return data;
}


//-------------------------------------------------
//  timer0_int_clr_w -
//-------------------------------------------------

void wangpc_state::timer0_irq_clr_w(uint8_t data)
{
	//if (LOG) logerror("%s: Timer 0 IRQ clear\n", machine().describe_context());

	m_pic->ir0_w(0);
}


//-------------------------------------------------
//  timer2_irq_clr_r -
//-------------------------------------------------

uint8_t wangpc_state::timer2_irq_clr_r()
{
	//if (LOG) logerror("%s: Timer 2 IRQ clear\n", machine().describe_context());

	m_timer2_irq = 1;
	check_level1_interrupts();

	return 0xff;
}


//-------------------------------------------------
//  nmi_mask_w -
//-------------------------------------------------

void wangpc_state::nmi_mask_w(uint8_t data)
{
	if (LOG) logerror("%s: NMI mask %02x\n", machine().describe_context(), data);
}


//-------------------------------------------------
//  led_on_r -
//-------------------------------------------------

uint8_t wangpc_state::led_on_r()
{
	if (LOG) logerror("%s: Diagnostic LED on\n", machine().describe_context());

	m_led_diagnostic = 1;

	return 0xff;
}


//-------------------------------------------------
//  fpu_mask_w -
//-------------------------------------------------

void wangpc_state::fpu_mask_w(uint8_t data)
{
	if (LOG) logerror("%s: FPU mask %02x\n", machine().describe_context(), data);
}


//-------------------------------------------------
//  dma_eop_clr_r -
//-------------------------------------------------

uint8_t wangpc_state::dma_eop_clr_r()
{
	if (LOG) logerror("%s: EOP clear\n", machine().describe_context());

	m_dma_eop = 1;

	check_level2_interrupts();

	return 0xff;
}


//-------------------------------------------------
//  uart_tbre_clr_w -
//-------------------------------------------------

void wangpc_state::uart_tbre_clr_w(uint8_t data)
{
	if (LOG) logerror("%s: TBRE clear\n", machine().describe_context());

	m_uart_tbre = 0;

	check_level2_interrupts();
}


//-------------------------------------------------
//  uart_r -
//-------------------------------------------------

uint8_t wangpc_state::uart_r()
{
	m_uart_dr = 0;

	check_level2_interrupts();

	uint8_t data = m_uart->read();

	if (LOG) logerror("%s: UART read %02x\n", machine().describe_context(), data);

	return data;
}


//-------------------------------------------------
//  uart_w -
//-------------------------------------------------

void wangpc_state::uart_w(uint8_t data)
{
	if (LOG) logerror("%s: UART write %02x\n", machine().describe_context(), data);

	switch (data)
	{
	case 0x10: m_led[0] = 1; break;
	case 0x11: m_led[0] = 0; break;
	case 0x12: m_led[1] = 1; break;
	case 0x13: m_led[1] = 0; break;
	case 0x14: m_led[2] = 1; break;
	case 0x15: m_led[2] = 0; break;
	case 0x16: m_led[3] = 1; break;
	case 0x17: m_led[3] = 0; break;
	case 0x18: m_led[4] = 1; break;
	case 0x19: m_led[4] = 0; break;
	case 0x1a: m_led[5] = 1; break;
	case 0x1b: m_led[5] = 0; break;
	case 0x1c: m_led[0] = m_led[1] = m_led[2] = m_led[3] = m_led[4] = m_led[5] = 1; break;
	case 0x1d: m_led[0] = m_led[1] = m_led[2] = m_led[3] = m_led[4] = m_led[5] = 0; break;
	}

	if (LOG) popmessage("%u%u%u%u%u%u", m_led[0], m_led[1], m_led[2], m_led[3], m_led[4], m_led[5]);

	m_uart_tbre = 0;
	check_level2_interrupts();

	m_uart->write(data);
}


//-------------------------------------------------
//  centronics_r -
//-------------------------------------------------

uint8_t wangpc_state::centronics_r()
{
	m_dav = 1;
	check_level1_interrupts();

	return m_cent_data_in->read();
}


//-------------------------------------------------
//  centronics_w -
//-------------------------------------------------

void wangpc_state::centronics_w(uint8_t data)
{
	m_centronics_ack = 1;
	check_level1_interrupts();

	m_cent_data_out->write(data);

	m_centronics->write_strobe(0);
	m_centronics->write_strobe(1);
}


//-------------------------------------------------
//  busy_clr_r -
//-------------------------------------------------

uint8_t wangpc_state::busy_clr_r()
{
	if (LOG) logerror("%s: BUSY clear\n", machine().describe_context());

	m_centronics_busy = 0;
	check_level1_interrupts();

	return 0xff;
}


//-------------------------------------------------
//  acknlg_clr_w -
//-------------------------------------------------

void wangpc_state::acknlg_clr_w(uint8_t data)
{
	if (LOG) logerror("%s: ACKNLG clear\n", machine().describe_context());

	m_centronics_ack = 1;
	check_level1_interrupts();
}


//-------------------------------------------------
//  led_off_r -
//-------------------------------------------------

uint8_t wangpc_state::led_off_r()
{
	if (LOG) logerror("%s: Diagnostic LED off\n", machine().describe_context());

	m_led_diagnostic = 0;

	return 0xff;
}


//-------------------------------------------------
//  parity_nmi_clr_w -
//-------------------------------------------------

void wangpc_state::parity_nmi_clr_w(uint8_t data)
{
	if (LOG) logerror("%s: Parity NMI clear\n", machine().describe_context());
}


//-------------------------------------------------
//  option_id_r -
//-------------------------------------------------

uint8_t wangpc_state::option_id_r()
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4
	    5
	    6
	    7       FDC Interrupt Flag

	*/

	uint8_t data = 0;

	// FDC interrupt
	data |= (m_fdc_dd0 || m_fdc_dd1 || (int) m_fdc->get_irq()) << 7;

	return data;
}



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( wangpc_mem )
//-------------------------------------------------

void wangpc_state::wangpc_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x1ffff).ram();
	map(0x40000, 0xf3fff).rw(m_bus, FUNC(wangpcbus_device::mrdc_r), FUNC(wangpcbus_device::amwc_w));
	map(0xfc000, 0xfffff).rom().region(I8086_TAG, 0);
}


//-------------------------------------------------
//  ADDRESS_MAP( wangpc_io )
//-------------------------------------------------

void wangpc_state::wangpc_io(address_map &map)
{
	map.unmap_value_high();
	map(0x1000, 0x1000).w(FUNC(wangpc_state::fdc_ctrl_w));
	map(0x1004, 0x1004).rw(FUNC(wangpc_state::deselect_drive1_r), FUNC(wangpc_state::deselect_drive1_w));
	map(0x1006, 0x1006).rw(FUNC(wangpc_state::select_drive1_r), FUNC(wangpc_state::select_drive1_w));
	map(0x1008, 0x1008).rw(FUNC(wangpc_state::deselect_drive2_r), FUNC(wangpc_state::deselect_drive2_w));
	map(0x100a, 0x100a).rw(FUNC(wangpc_state::select_drive2_r), FUNC(wangpc_state::select_drive2_w));
	map(0x100c, 0x100f).rw(FUNC(wangpc_state::motor1_on_r), FUNC(wangpc_state::motor1_on_w)).umask16(0x00ff);
	map(0x1010, 0x1013).rw(FUNC(wangpc_state::motor2_on_r), FUNC(wangpc_state::motor2_on_w)).umask16(0x00ff);
	map(0x1014, 0x1017).m(m_fdc, FUNC(upd765a_device::map)).umask16(0x00ff);
	map(0x1018, 0x1018).mirror(0x0002).rw(FUNC(wangpc_state::fdc_reset_r), FUNC(wangpc_state::fdc_reset_w));
	map(0x101c, 0x101c).mirror(0x0002).rw(FUNC(wangpc_state::fdc_tc_r), FUNC(wangpc_state::fdc_tc_w));
	map(0x1020, 0x1027).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
	map(0x1028, 0x1029); //.w(FUNC(wangpc_state::)); (?)
	map(0x1040, 0x1047).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
	map(0x1060, 0x1063).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x1080, 0x1087).r(m_epci, FUNC(scn_pci_device::read)).umask16(0x00ff);
	map(0x1088, 0x108f).w(m_epci, FUNC(scn_pci_device::write)).umask16(0x00ff);
	map(0x10a0, 0x10bf).rw(m_dmac, FUNC(am9517a_device::read), FUNC(am9517a_device::write)).umask16(0x00ff);
	map(0x10c2, 0x10c7).w(FUNC(wangpc_state::dma_page_w)).umask16(0x00ff);
	map(0x10e0, 0x10e0).rw(FUNC(wangpc_state::status_r), FUNC(wangpc_state::timer0_irq_clr_w));
	map(0x10e2, 0x10e2).rw(FUNC(wangpc_state::timer2_irq_clr_r), FUNC(wangpc_state::nmi_mask_w));
	map(0x10e4, 0x10e4).rw(FUNC(wangpc_state::led_on_r), FUNC(wangpc_state::fpu_mask_w));
	map(0x10e6, 0x10e6).rw(FUNC(wangpc_state::dma_eop_clr_r), FUNC(wangpc_state::uart_tbre_clr_w));
	map(0x10e8, 0x10e8).rw(FUNC(wangpc_state::uart_r), FUNC(wangpc_state::uart_w));
	map(0x10ea, 0x10ea).rw(FUNC(wangpc_state::centronics_r), FUNC(wangpc_state::centronics_w));
	map(0x10ec, 0x10ec).rw(FUNC(wangpc_state::busy_clr_r), FUNC(wangpc_state::acknlg_clr_w));
	map(0x10ee, 0x10ee).rw(FUNC(wangpc_state::led_off_r), FUNC(wangpc_state::parity_nmi_clr_w));
	map(0x10fe, 0x10fe).r(FUNC(wangpc_state::option_id_r));
	map(0x1100, 0x1fff).rw(m_bus, FUNC(wangpcbus_device::sad_r), FUNC(wangpcbus_device::sad_w));
}



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( wangpc )
//-------------------------------------------------

static INPUT_PORTS_START( wangpc )
	// keyboard defined in machine/wangpckb.c

	PORT_START("SW")
	PORT_DIPNAME( 0x0f, 0x0f, "CPU Baud Rate" ) PORT_DIPLOCATION("SW:1,2,3,4")
	PORT_DIPSETTING(    0x0f, "19200" )
	PORT_DIPSETTING(    0x0e, "9600" )
	PORT_DIPSETTING(    0x0d, "7200" )
	PORT_DIPSETTING(    0x0c, "4800" )
	PORT_DIPSETTING(    0x0b, "3600" )
	PORT_DIPSETTING(    0x0a, "2400" )
	PORT_DIPSETTING(    0x09, "2000" )
	PORT_DIPSETTING(    0x08, "1800" )
	PORT_DIPSETTING(    0x07, "1200" )
	PORT_DIPSETTING(    0x06, "600" )
	PORT_DIPSETTING(    0x05, "300" )
	PORT_DIPSETTING(    0x04, "150" )
	PORT_DIPSETTING(    0x03, "134.5" )
	PORT_DIPSETTING(    0x02, "110" )
	PORT_DIPSETTING(    0x01, "75" )
	PORT_DIPSETTING(    0x00, "50 (Loop on Power-Up)" )
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  I8237
//-------------------------------------------------

void wangpc_state::update_fdc_tc()
{
	if (m_enable_eop)
		m_fdc->tc_w(m_fdc_tc);
	else
		m_fdc->tc_w(0);
}

void wangpc_state::hrq_w(int state)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);

	m_dmac->hack_w(state);
}

void wangpc_state::eop_w(int state)
{
	if (m_dack == 2)
	{
		m_fdc_tc = state;
		update_fdc_tc();
	}

	if (state)
	{
		if (LOG) logerror("EOP set\n");

		m_dma_eop = 0;
		check_level2_interrupts();
	}

	m_bus->tc_w(state);
}

uint8_t wangpc_state::memr_r(offs_t offset)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	offs_t addr = (m_dma_page[m_dack] << 16) | offset;

	return program.read_byte(addr);
}

void wangpc_state::memw_w(offs_t offset, uint8_t data)
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	offs_t addr = (m_dma_page[m_dack] << 16) | offset;

	program.write_byte(addr, data);
}

uint8_t wangpc_state::ior2_r()
{
	if (m_disable_dreq2)
		return m_bus->dack_r(2);
	else
		return m_fdc->dma_r();
}

void wangpc_state::iow2_w(uint8_t data)
{
	if (m_disable_dreq2)
		m_bus->dack_w(2, data);
	else
		m_fdc->dma_w(data);
}

void wangpc_state::dack0_w(int state)
{
	if (!state) m_dack = 0;
}

void wangpc_state::dack1_w(int state)
{
	if (!state) m_dack = 1;
}

void wangpc_state::dack2_w(int state)
{
	if (!state) m_dack = 2;
}

void wangpc_state::dack3_w(int state)
{
	if (!state) m_dack = 3;
}

//-------------------------------------------------
//  pic8259_interface pic_intf
//-------------------------------------------------

void wangpc_state::check_level1_interrupts()
{
	int state = !m_timer2_irq || !m_epci->rxrdy_r() || !m_epci->txemt_dschg_r() || !m_centronics_ack || !m_dav || m_centronics_busy;

	m_pic->ir1_w(state);
}

void wangpc_state::check_level2_interrupts()
{
	int state = !m_dma_eop || m_uart_dr || m_uart_tbre || m_fdc_dd0 || m_fdc_dd1 || m_fdc->get_irq() || m_fpu_irq || m_bus_irq2;

	m_pic->ir2_w(state);
}

//-------------------------------------------------
//  I8255A INTERFACE
//-------------------------------------------------

uint8_t wangpc_state::ppi_pa_r()
{
	/*

	    bit     description

	    0       /POWER ON
	    1       /SMART
	    2       /DATA AVAILABLE
	    3       SLCT
	    4       BUSY
	    5       /FAULT
	    6       PE
	    7       ACKNOWLEDGE

	*/

	uint8_t data = 0x08 | 0x02 | 0x01;

	data |= m_dav << 2;
	data |= m_centronics_busy << 4;
	data |= m_centronics_fault << 5;
	data |= m_centronics_perror << 6;
	data |= m_centronics_ack << 7;

	return data;
}

uint8_t wangpc_state::ppi_pb_r()
{
	/*

	    bit     description

	    0       /TIMER 2 INTERRUPT
	    1       /SERIAL INTERRUPT
	    2       /PARALLEL PORT INTERRUPT
	    3       /DMA INTERRUPT
	    4       KBD INTERRUPT TRANSMIT
	    5       KBD INTERRUPT RECEIVE
	    6       FLOPPY DISK INTERRUPT
	    7       8087 INTERRUPT

	*/

	uint8_t data = 0;

	// timer 2 interrupt
	data |= m_timer2_irq;

	// serial interrupt
	data |= (m_epci->rxrdy_r() & m_epci->txemt_dschg_r()) << 1;

	// parallel port interrupt
	data |= m_centronics_ack << 2;

	// DMA interrupt
	data |= m_dma_eop << 3;

	// keyboard interrupt
	data |= m_uart_tbre << 4;
	data |= m_uart_dr << 5;

	// FDC interrupt
	data |= m_fdc->get_irq() << 6;

	// 8087 interrupt
	data |= m_fpu_irq << 7;

	return data;
}

uint8_t wangpc_state::ppi_pc_r()
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4       SW1
	    5       SW2
	    6       SW3
	    7       SW4

	*/

	return m_sw->read() << 4;
}

void wangpc_state::ppi_pc_w(uint8_t data)
{
	/*

	    bit     description

	    0       /USR0 (pin 14)
	    1       /USR1 (pin 36)
	    2       /RESET (pin 31)
	    3       Unassigned
	    4
	    5
	    6
	    7

	*/

	m_centronics->write_autofd(BIT(data, 0));
	m_centronics->write_select_in(BIT(data, 1));
	m_centronics->write_init(BIT(data, 2));
}

void wangpc_state::pit0_w(int state)
{
	if (state)
		m_pic->ir0_w(1);
}

void wangpc_state::pit2_w(int state)
{
	if (state)
	{
		m_timer2_irq = 0;
		check_level1_interrupts();
	}
}

//-------------------------------------------------
//  IM6402_INTERFACE( uart_intf )
//-------------------------------------------------

void wangpc_state::uart_dr_w(int state)
{
	if (state)
	{
		if (LOG) logerror("DR set\n");

		m_uart_dr = 1;
		check_level2_interrupts();
	}
}

void wangpc_state::uart_tbre_w(int state)
{
	if (state)
	{
		if (LOG) logerror("TBRE set\n");

		m_uart_tbre = 1;
		check_level2_interrupts();
	}
}


//-------------------------------------------------
//  SCN2661_INTERFACE( epci_intf )
//-------------------------------------------------

void wangpc_state::epci_irq_w(int state)
{
	check_level1_interrupts();
}


//-------------------------------------------------
//  upd765_interface fdc_intf
//-------------------------------------------------

static void wangpc_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

void wangpc_state::fdc_irq(int state)
{
	if (LOG) logerror("FDC INT %u\n", state);

	check_level2_interrupts();
}

void wangpc_state::fdc_drq(int state)
{
	if (LOG) logerror("FDC DRQ %u\n", state);

	m_fdc_drq = state;
	update_fdc_drq();
}

void wangpc_state::update_fdc_drq()
{
	if (m_disable_dreq2)
		m_dmac->dreq2_w(1);
	else
		m_dmac->dreq2_w(!m_fdc_drq);
}


//-------------------------------------------------
//  centronics_interface centronics_intf
//-------------------------------------------------

void wangpc_state::write_centronics_ack(int state)
{
	if (LOG) logerror("ACKNLG %u\n", state);

	m_centronics_ack = state;

	check_level1_interrupts();
}

void wangpc_state::write_centronics_busy(int state)
{
	if (LOG) logerror("BUSY %u\n", state);

	m_centronics_busy = state;

	check_level1_interrupts();
}

void wangpc_state::write_centronics_fault(int state)
{
	m_centronics_fault = state;
}

void wangpc_state::write_centronics_perror(int state)
{
	m_centronics_perror = state;
}

//-------------------------------------------------
//  WANGPC_BUS_INTERFACE( kb_intf )
//-------------------------------------------------

void wangpc_state::bus_irq2_w(int state)
{
	if (LOG) logerror("Bus IRQ2 %u\n", state);

	m_bus_irq2 = state;

	check_level2_interrupts();
}



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( wangpc )
//-------------------------------------------------

void wangpc_state::machine_start()
{
	// connect floppy callbacks
	m_floppy[0]->setup_load_cb(floppy_image_device::load_cb(&wangpc_state::on_disk0_load, this));
	m_floppy[0]->setup_unload_cb(floppy_image_device::unload_cb(&wangpc_state::on_disk0_unload, this));
	m_floppy[1]->setup_load_cb(floppy_image_device::load_cb(&wangpc_state::on_disk1_load, this));
	m_floppy[1]->setup_unload_cb(floppy_image_device::unload_cb(&wangpc_state::on_disk1_unload, this));

	m_led_diagnostic.resolve();

	// state saving
	save_item(NAME(m_dma_page));
	save_item(NAME(m_dack));
	save_item(NAME(m_timer2_irq));
	save_item(NAME(m_centronics_ack));
	save_item(NAME(m_centronics_busy));
	save_item(NAME(m_centronics_fault));
	save_item(NAME(m_centronics_perror));
	save_item(NAME(m_dav));
	save_item(NAME(m_dma_eop));
	save_item(NAME(m_uart_dr));
	save_item(NAME(m_uart_tbre));
	save_item(NAME(m_fpu_irq));
	save_item(NAME(m_bus_irq2));
	save_item(NAME(m_enable_eop));
	save_item(NAME(m_disable_dreq2));
	save_item(NAME(m_fdc_drq));
	save_item(NAME(m_ds1));
	save_item(NAME(m_ds2));
}


void wangpc_state::machine_reset()
{
	// initialize UART
	m_uart->cls1_w(1);
	m_uart->cls2_w(1);
	m_uart->pi_w(1);
	m_uart->sbs_w(1);
	m_uart->crl_w(1);
}


//-------------------------------------------------
//  on_disk0_change -
//-------------------------------------------------

void wangpc_state::on_disk0_load(floppy_image_device *image)
{
	on_disk0_unload(image);
}

void wangpc_state::on_disk0_unload(floppy_image_device *image)
{
	if (LOG) logerror("Door 1 disturbed\n");

	m_fdc_dd0 = 1;
	check_level2_interrupts();
}


//-------------------------------------------------
//  on_disk1_change -
//-------------------------------------------------

void wangpc_state::on_disk1_load(floppy_image_device *image)
{
	on_disk1_unload(image);
}

void wangpc_state::on_disk1_unload(floppy_image_device *image)
{
	if (LOG) logerror("Door 2 disturbed\n");

	m_fdc_dd1 = 1;
	check_level2_interrupts();
}



//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

//-------------------------------------------------
//  machine_config( wangpc )
//-------------------------------------------------

void wangpc_state::wangpc(machine_config &config)
{
	I8086(config, m_maincpu, 24_MHz_XTAL / 3);
	m_maincpu->set_addrmap(AS_PROGRAM, &wangpc_state::wangpc_mem);
	m_maincpu->set_addrmap(AS_IO, &wangpc_state::wangpc_io);
	m_maincpu->set_irq_acknowledge_callback(I8259A_TAG, FUNC(pic8259_device::inta_cb));
	//config.m_perfect_cpu_quantum = subtag(I8086_TAG);

	// devices
	AM9517A(config, m_dmac, 24_MHz_XTAL / 6);
	m_dmac->dreq_active_low();
	m_dmac->out_hreq_callback().set(FUNC(wangpc_state::hrq_w));
	m_dmac->out_eop_callback().set(FUNC(wangpc_state::eop_w));
	m_dmac->in_memr_callback().set(FUNC(wangpc_state::memr_r));
	m_dmac->out_memw_callback().set(FUNC(wangpc_state::memw_w));
	m_dmac->in_ior_callback<1>().set(m_bus, FUNC(wangpcbus_device::dack1_r));
	m_dmac->in_ior_callback<2>().set(FUNC(wangpc_state::ior2_r));
	m_dmac->in_ior_callback<3>().set(m_bus, FUNC(wangpcbus_device::dack3_r));
	m_dmac->out_iow_callback<1>().set(m_bus, FUNC(wangpcbus_device::dack1_w));
	m_dmac->out_iow_callback<2>().set(FUNC(wangpc_state::iow2_w));
	m_dmac->out_iow_callback<3>().set(m_bus, FUNC(wangpcbus_device::dack3_w));
	m_dmac->out_dack_callback<0>().set(FUNC(wangpc_state::dack0_w));
	m_dmac->out_dack_callback<1>().set(FUNC(wangpc_state::dack1_w));
	m_dmac->out_dack_callback<2>().set(FUNC(wangpc_state::dack2_w));
	m_dmac->out_dack_callback<3>().set(FUNC(wangpc_state::dack3_w));

	PIC8259(config, m_pic);
	m_pic->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	I8255A(config, m_ppi);
	m_ppi->in_pa_callback().set(FUNC(wangpc_state::ppi_pa_r));
	m_ppi->in_pb_callback().set(FUNC(wangpc_state::ppi_pb_r));
	m_ppi->in_pc_callback().set(FUNC(wangpc_state::ppi_pc_r));
	m_ppi->out_pc_callback().set(FUNC(wangpc_state::ppi_pc_w));

	PIT8253(config, m_pit);
	m_pit->set_clk<0>(24_MHz_XTAL / 48);
	m_pit->out_handler<0>().set(FUNC(wangpc_state::pit0_w));
	m_pit->set_clk<1>(24_MHz_XTAL / 12);
	m_pit->set_clk<2>(24_MHz_XTAL / 48);
	m_pit->out_handler<2>().set(FUNC(wangpc_state::pit2_w));

	constexpr XTAL clk1mhz = 24_MHz_XTAL / 24;
	IM6402(config, m_uart, clk1mhz.value(), clk1mhz.value());
	m_uart->tro_callback().set("wangpckb", FUNC(wangpc_keyboard_device::write_rxd));
	m_uart->dr_callback().set(FUNC(wangpc_state::uart_dr_w));
	m_uart->tbre_callback().set(FUNC(wangpc_state::uart_tbre_w));

	SCN2661C(config, m_epci, 5.0688_MHz_XTAL);
	m_epci->txd_handler().set(RS232_TAG, FUNC(rs232_port_device::write_txd));
	m_epci->rxrdy_handler().set(FUNC(wangpc_state::epci_irq_w));
	m_epci->rts_handler().set(RS232_TAG, FUNC(rs232_port_device::write_rts));
	m_epci->dtr_handler().set(RS232_TAG, FUNC(rs232_port_device::write_dtr));
	m_epci->txemt_dschg_handler().set(FUNC(wangpc_state::epci_irq_w));

	UPD765A(config, m_fdc, 24_MHz_XTAL / 6, false, false);
	m_fdc->intrq_wr_callback().set(FUNC(wangpc_state::fdc_irq));
	m_fdc->drq_wr_callback().set(FUNC(wangpc_state::fdc_drq));
	FLOPPY_CONNECTOR(config, UPD765_TAG ":0", wangpc_floppies, "525dd", floppy_image_device::default_pc_floppy_formats);
	FLOPPY_CONNECTOR(config, UPD765_TAG ":1", wangpc_floppies, "525dd", floppy_image_device::default_pc_floppy_formats);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->set_data_input_buffer(m_cent_data_in);
	m_centronics->ack_handler().set(FUNC(wangpc_state::write_centronics_ack));
	m_centronics->busy_handler().set(FUNC(wangpc_state::write_centronics_busy));
	m_centronics->fault_handler().set(FUNC(wangpc_state::write_centronics_fault));
	m_centronics->perror_handler().set(FUNC(wangpc_state::write_centronics_perror));

	INPUT_BUFFER(config, m_cent_data_in);

	OUTPUT_LATCH(config, m_cent_data_out);
	m_centronics->set_output_latch(*m_cent_data_out);

	rs232_port_device &rs232(RS232_PORT(config, RS232_TAG, default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_epci, FUNC(scn_pci_device::rxd_w));
	rs232.cts_handler().set(m_epci, FUNC(scn_pci_device::cts_w));
	rs232.dsr_handler().set(m_epci, FUNC(scn_pci_device::dsr_w));
	rs232.dcd_handler().set(m_epci, FUNC(scn_pci_device::dcd_w));

	WANGPC_KEYBOARD(config, "wangpckb").txd_handler().set(m_uart, FUNC(im6402_device::rri_w));

	// bus
	WANGPC_BUS(config, m_bus, 24_MHz_XTAL / 3);
	m_bus->irq2_wr_callback().set(FUNC(wangpc_state::bus_irq2_w));
	m_bus->irq3_wr_callback().set(m_pic, FUNC(pic8259_device::ir3_w));
	m_bus->irq4_wr_callback().set(m_pic, FUNC(pic8259_device::ir4_w));
	m_bus->irq5_wr_callback().set(m_pic, FUNC(pic8259_device::ir5_w));
	m_bus->irq6_wr_callback().set(m_pic, FUNC(pic8259_device::ir6_w));
	m_bus->irq7_wr_callback().set(m_pic, FUNC(pic8259_device::ir7_w));
	m_bus->drq1_wr_callback().set(m_dmac, FUNC(am9517a_device::dreq1_w));
	m_bus->drq2_wr_callback().set(m_dmac, FUNC(am9517a_device::dreq2_w));
	m_bus->drq3_wr_callback().set(m_dmac, FUNC(am9517a_device::dreq3_w));
	m_bus->ioerror_wr_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	WANGPC_BUS_SLOT(config, "slot1", m_bus, wangpc_cards, nullptr, 1);
	WANGPC_BUS_SLOT(config, "slot2", m_bus, wangpc_cards, "mvc", 2);
	WANGPC_BUS_SLOT(config, "slot3", m_bus, wangpc_cards, nullptr, 3);
	WANGPC_BUS_SLOT(config, "slot4", m_bus, wangpc_cards, nullptr, 4);
	WANGPC_BUS_SLOT(config, "slot5", m_bus, wangpc_cards, nullptr, 5);

	// internal ram
	RAM(config, RAM_TAG).set_default_size("128K");

	// software list
	SOFTWARE_LIST(config, "flop_list").set_original("wangpc");
}



//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

//-------------------------------------------------
//  ROM( wangpc )
//-------------------------------------------------

ROM_START( wangpc )
	ROM_REGION16_LE( 0x4000, I8086_TAG, 0)
	ROM_LOAD16_BYTE( "0001 r2.l94", 0x0001, 0x2000, CRC(f9f41304) SHA1(1815295809ef11573d724ede47446f9ac7aee713) )
	ROM_LOAD16_BYTE( "379-0000 r2.l115", 0x0000, 0x2000, CRC(67b37684) SHA1(70d9f68eb88cc2bc9f53f949cc77411c09a4266e) )
ROM_END

} // anonymous namespace


//**************************************************************************
//  GAME DRIVERS
//**************************************************************************

COMP( 1985, wangpc, 0, 0, wangpc, wangpc, wangpc_state, empty_init, "Wang Laboratories", "Wang Professional Computer", MACHINE_SUPPORTS_SAVE )



wavesynth.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: Olivier Galibert

// A "virtual" driver to turn waveblaster cards into a screen-less expander

#include "emu.h"
#include "speaker.h"
#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "bus/waveblaster/waveblaster.h"


namespace {

class wavesynth_state : public driver_device
{
public:
	wavesynth_state(const machine_config &mconfig, device_type type, const char *tag);

	void wavesynth(machine_config &config);


private:
	virtual void machine_start() override ATTR_COLD;

	required_device<waveblaster_connector> m_waveblaster;
};


wavesynth_state::wavesynth_state(const machine_config &mconfig, device_type type, const char *tag)
	: driver_device(mconfig, type, tag)
	, m_waveblaster(*this, "waveblaster")
{
}

void wavesynth_state::machine_start()
{
}

void wavesynth_state::wavesynth(machine_config &config)
{
	WAVEBLASTER_CONNECTOR(config, m_waveblaster, waveblaster_intf, "omniwave");
	m_waveblaster->add_route(0, "speaker", 1.0, 0);
	m_waveblaster->add_route(1, "speaker", 1.0, 1);

	SPEAKER(config, "speaker", 2).front();

	auto &mdin(MIDI_PORT(config, "mdin"));
	midiin_slot(mdin);
	mdin.rxd_handler().set(m_waveblaster, FUNC(waveblaster_connector::midi_rx));

	auto &mdout(MIDI_PORT(config, "mdout"));
	midiout_slot(mdout);
	m_waveblaster->midi_tx().set(mdout, FUNC(midi_port_device::write_txd));
}

static INPUT_PORTS_START( wavesynth )
INPUT_PORTS_END

ROM_START( wavesynth )
ROM_END


} // anonymous namespace


CONS( 2020, wavesynth, 0, 0, wavesynth, wavesynth, wavesynth_state, empty_init, "MAME", "Waveblaster-based expander", MACHINE_SUPPORTS_SAVE )



webtouchone.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:

/*
  Skeleton driver for Alcatel Web Touch One.
  More information and technical manuals: https://www.minitel-alcatel.fr/gamme_webphone.html
  The phone asks for an administrative password for performing a factory reset.

  Hardware info for model 2840:
   -CPU: Motorola PowerPC 823
   -Operating system: JavaOS over ChorusOS (Sun Microsystems)
   -Screen: Hitachi SX19V001-ZZA (7,6'' touch, color, 640x480)
   -Keyboard: N860-1428-T021
   -Other: Smartcard reader, 33.6 kbps modem (Conexant RVC3366ACFW), 8MB DRAM, etc.

  CPU PCB
    ____________________________________________________________________
   |        Xtal                                                       |
   |     25.416 MHz                                                    |
   |    _____________                         _______                  |
   |   | CONEXANT   |                        |MC3403|   ___            |
   |   | RVC3366ACFW|                                  |  |            |
   |   | R6749-24   |   ________  ________   74HC4053->|  |            |
   |   |            |  |74HC4053||74HC4053|            |__|            |
   |   |____________|                    ___                   _______ |
   |  _______                    34119->|  |                  |MC3403| |
   | 74AHCT574                          |__|                           |
   |  ____________   ____________                                      |
   | |KM416S4030CT| |KM416S4030CT|                                     |
   | |____________| |____________|                                     |
   |       ____________                                     __________ |
   |      |KM29U64000T|             _____________          |74HCT541 | |
   |      |___________|            | Motorola   |          |_________| |
   |                               | XPC823ZT66A|           __________ |
   |                    Xtal       |            |          |74HCT541 | |
   |                 32.768 MHz    |            |          |_________| |
   |                               |____________|                      |
   |                                                                   |
   |                                          _______                  |
   | 3BN62121AAAF KAZZA 01                   |ST 324|                  |
   |___________________________________________________________________|


  Keyboard PCB
   _____________________________________________________________________________
  |                                   ___________                              |
  |             CONN                 |TDA8002CT/8|                             |
  |                                  |___________|                             |
  |__                         _________                 CONN                 __|
    |                        | NEC    |  Xtal                                |
   _|   CONN                D78F0034AGC  7.15909 MHz                         |_
  |                          |________|                                        |
  |____________________________________________________________________________|


  Main PCB (ALCATEL CPU-I)
   __________________________________________________________
  | :::::::::::::::    CONN             CONN                |
  |   _______________                ____                 C |
  |  | M29W800AT    |                LC08A                O |
  |  |______________|                                     N |
  |   _______________                                     N |
  |  | M29W800AT    |                                       |
  |  |______________|  ____                               C |
  |                    LC32A                              O |
  | _______________   _______________                     N |
  || KM416S4030CT |  | KM416S4030CT |                     N |
  ||______________|  |______________|                       |
  | _______           _______   _____                       |
  ||LVC4245          |_HB574|  LVC14A   ________     ___    |
  |                                     74HC4035   ST072C C |
  | ___________     _____               ________          O |
  ||STM27C1001|    |    |<-SDT 71256    74HC4035          N |
  ||          |    |    |                                 N |
  ||          |    |    |           ____________     ___    |
  ||__________|    |____|          | MC34118DW |   MC3403D  |
  |_________________________________________________________|

*/

#include "emu.h"
#include "cpu/powerpc/ppc.h"
#include "screen.h"
#include "speaker.h"

namespace {

class webtouchone_state : public driver_device
{
public:
	webtouchone_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen")
	{ }

	void webtouchone(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<screen_device> m_screen;

	uint32_t screen_update_webtouchone(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};

uint32_t webtouchone_state::screen_update_webtouchone(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void webtouchone_state::machine_start()
{
}

void webtouchone_state::machine_reset()
{
}

static INPUT_PORTS_START( webtouchone )
INPUT_PORTS_END

void webtouchone_state::webtouchone(machine_config &config)
{
	MPC8240(config, m_maincpu, XTAL(32'768'000)); // Actually a Motorola XPC823ZT66A

	SCREEN(config, m_screen, SCREEN_TYPE_LCD); // Hitachi SX19V001-ZZA (7,6'' touch, color, 640x480)
	m_screen->set_refresh_hz(60); // Guess
	m_screen->set_size(640, 480);
	m_screen->set_visarea(0, 640-1, 0, 480-1);
	m_screen->set_screen_update(FUNC(webtouchone_state::screen_update_webtouchone));

	SPEAKER(config, "mono").front_center();
}

// Spanish ROM for Terra (now Telefónica)
ROM_START( wto2840sp )
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_LOAD( "3bn64108aaaa-02hy-m29w800at.bin", 0x000000, 0x100000, CRC(ebc91205) SHA1(a0609936cf5e99de18f7e94efe7ecfdeabf40d34) )
	ROM_LOAD( "3bn64108aaba-04fw-m29w800at.bin", 0x100000, 0x100000, CRC(ca1967c1) SHA1(8a49a255029c5098335d58909718ef558452294c) )

	ROM_REGION( 0x020000, "modem", 0 )
	ROM_LOAD( "3bn64078_aabe_stm27c1001.bin",    0x000000, 0x020000, CRC(25c0cb47) SHA1(67b337e05204a68b54f6e33d64ac876012ee9eb6) )

	ROM_REGION( 0x840000, "user", 0 )
	ROM_LOAD( "km29u64000t.bin",                 0x000000, 0x840000, BAD_DUMP CRC(869a07d1) SHA1(a4199c8babd723584c17a913bf4c43c02b90ffad) ) // Contains user data (addessses, accounts, etc.; needs a factory reset)

	ROM_REGION( 0x008000, "keyboard", 0 )
	ROM_LOAD( "upd78f0034agc.bin",               0x000000, 0x008000, NO_DUMP )
ROM_END

} // anonymous namespace


//    YEAR  NAME       PARENT  COMPAT  MACHINE      INPUT        CLASS              INIT        COMPANY    FULLNAME                                      FLAGS
COMP( 1999, wto2840sp, 0,      0,      webtouchone, webtouchone, webtouchone_state, empty_init, "Alcatel", "Web Touch One (model 2840, Terra, Spanish)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



wicat.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Barry Rodewald
/***************************************************************************

Wicat - various systems.

2013-09-01 Skeleton driver

****************************************************************************/

/*

    TODO:

    - video DMA is done line by line and needs to be in perfect sync

*/

#include "emu.h"

#include "bus/keytronic/keytronic.h"
#include "bus/rs232/rs232.h"
#include "cpu/8x300/8x300.h"
#include "cpu/m68000/m68000.h"
#include "cpu/z8000/z8000.h"
#include "imagedev/floppy.h"
#include "machine/74259.h"
#include "machine/6522via.h"
#include "machine/am9517a.h"
#include "machine/im6402.h"
#include "machine/input_merger.h"
#include "machine/mm58174.h"
#include "machine/scn_pci.h"
#include "machine/wd_fdc.h"
#include "machine/x2212.h"
#include "video/i8275.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"

#include "wicat.lh"


namespace {

class wicat_state : public driver_device
{
public:
	wicat_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_rtc(*this, "rtc"),
		m_via(*this, "via"),
		m_uart(*this, "uart%u", 0U),
		m_videocpu(*this, "videocpu"),
		m_videoctrl(*this, "videoctrl"),
		m_videoirq(*this, "videoirq"),
		m_crtc(*this, "video"),
		m_videodma(*this, "videodma"),
		m_videouart(*this, "videouart%u", 0U),
		m_kbduart(*this, "kbduart"),
		m_videosram(*this, "vsram"),
		m_palette(*this, "palette"),
		m_chargen(*this, "g2char"),
		m_fdc(*this,"fdc")
	{
	}

	void wicat(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void main_mem(address_map &map) ATTR_COLD;
	void video_io(address_map &map) ATTR_COLD;
	void video_mem(address_map &map) ATTR_COLD;
	void wd1000_io(address_map &map) ATTR_COLD;
	void wd1000_mem(address_map &map) ATTR_COLD;

	uint16_t memmap_r();
	void memmap_w(uint16_t data);
	void adir_w(int state);
	void bdir_w(int state);
	void via_a_w(uint8_t data);
	void via_b_w(uint8_t data);
	void videosram_store_w(uint8_t data);
	void videosram_recall_w(uint8_t data);
	uint8_t video_uart_status_r();
	uint8_t video_kbduart_r();
	uint8_t vram_r(offs_t offset);
	void vram_w(offs_t offset, uint8_t data);
	uint8_t video_status_r();
	void dma_hrq_w(int state);
	void crtc_irq_w(int state);
	void crtc_irq_clear_w(int state);
	uint8_t hdc_r(offs_t offset);
	void hdc_w(offs_t offset, uint8_t data);
	uint8_t fdc_r(offs_t offset);
	void fdc_w(offs_t offset, uint8_t data);
	uint16_t via_r(offs_t offset, uint16_t mem_mask = ~0);
	void via_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
	I8275_DRAW_CHARACTER_MEMBER(wicat_display_pixels);

	required_device<m68000_device> m_maincpu;
	required_device<mm58174_device> m_rtc;
	required_device<via6522_device> m_via;
	required_device_array<scn2661c_device, 7> m_uart;
	required_device<cpu_device> m_videocpu;
	required_device<ls259_device> m_videoctrl;
	required_device<input_merger_device> m_videoirq;
	required_device<i8275_device> m_crtc;
	required_device<am9517a_device> m_videodma;
	required_device_array<scn2651_device, 2> m_videouart;
	required_device<im6402_device> m_kbduart;
	required_device<x2210_device> m_videosram;
	required_device<palette_device> m_palette;
	required_memory_region m_chargen;
	required_device<fd1795_device> m_fdc;

	uint8_t m_portA;
	uint8_t m_portB;
	bool m_crtc_irq;
};


void wicat_state::main_mem(address_map &map)
{
	map.unmap_value_low();
	map.global_mask(0xffffff);
	map(0x000000, 0x001fff).rom().region("c2", 0x0000);
	map(0x020000, 0x1fffff).ram();
	map(0x200000, 0x2fffff).ram();
	map(0x300000, 0xdfffff).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
	map(0xeff800, 0xeffbff).ram();  // memory mapping SRAM, used during boot sequence for storing various data (TODO)
	map(0xeffc00, 0xeffc01).rw(FUNC(wicat_state::memmap_r), FUNC(wicat_state::memmap_w));
	map(0xf00000, 0xf00007).rw(m_uart[0], FUNC(scn2661c_device::read), FUNC(scn2661c_device::write)).umask16(0xff00);  // UARTs
	map(0xf00008, 0xf0000f).rw(m_uart[1], FUNC(scn2661c_device::read), FUNC(scn2661c_device::write)).umask16(0xff00);
	map(0xf00010, 0xf00017).rw(m_uart[2], FUNC(scn2661c_device::read), FUNC(scn2661c_device::write)).umask16(0xff00);
	map(0xf00018, 0xf0001f).rw(m_uart[3], FUNC(scn2661c_device::read), FUNC(scn2661c_device::write)).umask16(0xff00);
	map(0xf00020, 0xf00027).rw(m_uart[4], FUNC(scn2661c_device::read), FUNC(scn2661c_device::write)).umask16(0xff00);
	map(0xf00028, 0xf0002f).rw(m_uart[5], FUNC(scn2661c_device::read), FUNC(scn2661c_device::write)).umask16(0xff00);
	map(0xf00030, 0xf00037).rw(m_uart[6], FUNC(scn2661c_device::read), FUNC(scn2661c_device::write)).umask16(0xff00);
	map(0xf0003a, 0xf0003b).nopr();
	map(0xf00040, 0xf0005f).rw(FUNC(wicat_state::via_r), FUNC(wicat_state::via_w));
	map(0xf00060, 0xf0007f).rw(m_rtc, FUNC(mm58174_device::read), FUNC(mm58174_device::write)).umask16(0xff00);
	map(0xf000d0, 0xf000d0).w("ledlatch", FUNC(ls259_device::write_nibble_d3));
	map(0xf00180, 0xf0018f).rw(FUNC(wicat_state::hdc_r), FUNC(wicat_state::hdc_w));  // WD1000
	map(0xf00190, 0xf0019f).rw(FUNC(wicat_state::fdc_r), FUNC(wicat_state::fdc_w));  // FD1795
	map(0xf00f00, 0xf00fff).rw(m_maincpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
}

void wicat_state::video_mem(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("g1", 0x0000);
	map(0x8000, 0xffff).ram();
}

void wicat_state::video_io(address_map &map)
{
	// these are largely wild guesses...
	map(0x0000, 0x0000).r(FUNC(wicat_state::video_uart_status_r));
	map(0x0002, 0x0002).r(FUNC(wicat_state::video_kbduart_r)).w(m_kbduart, FUNC(im6402_device::write));
	map(0x0100, 0x0107).rw(m_videouart[0], FUNC(scn2651_device::read), FUNC(scn2651_device::write)).umask16(0xff00);  // INS2651 UART #1
	map(0x0200, 0x0207).rw(m_videouart[1], FUNC(scn2651_device::read), FUNC(scn2651_device::write)).umask16(0xff00);  // INS2651 UART #2
	map(0x0304, 0x0304).r(FUNC(wicat_state::video_status_r));
	map(0x0400, 0x047f).rw(m_videosram, FUNC(x2210_device::read), FUNC(x2210_device::write)).umask16(0xff00);  // XD2210  4-bit NOVRAM
	map(0x0500, 0x0500).w(FUNC(wicat_state::videosram_recall_w));
	map(0x0600, 0x0600).w(FUNC(wicat_state::videosram_store_w));
	map(0x0700, 0x0701).nopw(); // vestigial SN76496 initialization?
	map(0x0800, 0x0807).w("videoctrl", FUNC(ls259_device::write_d0)).umask16(0xffff);
	map(0x0a00, 0x0a1f).rw(m_videodma, FUNC(am9517a_device::read), FUNC(am9517a_device::write)).umask16(0xff00); // AM9517A DMA
	map(0x0b00, 0x0b03).rw(m_crtc, FUNC(i8275_device::read), FUNC(i8275_device::write)).umask16(0xff00);  // i8275 CRTC
	map(0x0e00, 0x0eff).ram();
	map(0x4000, 0x5fff).ram(); // video RAM?
	map(0x8000, 0x8fff).lr8(NAME([this] (offs_t offset) { return m_chargen->as_u8(offset); }));
	map(0x9000, 0x9fff).lr8(NAME([this] (offs_t offset) { return m_chargen->as_u8(offset); }));
}

void wicat_state::wd1000_mem(address_map &map)
{
	map(0x0000, 0x0bff).rom().region("wd3", 0x0000);
	map(0x0c00, 0x0fff).noprw();
}

void wicat_state::wd1000_io(address_map &map)
{
	map(0x0000, 0x00ff).ram();  // left bank  - RAM
	map(0x0100, 0x01ff).ram();  // right bank - I/O ports (TODO)
}


/* Input ports */
static INPUT_PORTS_START( wicat )
INPUT_PORTS_END

static void wicat_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

void wicat_state::machine_start()
{
}

void wicat_state::machine_reset()
{
	// on the terminal board /DCD on both INS2651s are tied to GND
	m_videouart[0]->dcd_w(0);
	m_videouart[1]->dcd_w(0);
	for (int i = 0; i < 6; i++)
		m_uart[i]->dcd_w(0);

	// initialise im6402 (terminal board)
	m_kbduart->cls1_w(1);
	m_kbduart->cls2_w(1);
	m_kbduart->pi_w(1);
	m_kbduart->sbs_w(0);
	m_kbduart->crl_w(1);

	m_crtc_irq = false;
}

void wicat_state::adir_w(int state)
{
	// parallel port A direction (0 = input, 1 = output)
}

void wicat_state::bdir_w(int state)
{
	// parallel port B direction (0 = input, 1 = output)
}

void wicat_state::via_a_w(uint8_t data)
{
	m_portA = data;
	logerror("VIA: write %02x to port A\n",data);
}

void wicat_state::via_b_w(uint8_t data)
{
	m_portB = data;
	logerror("VIA: write %02x to port B\n",data);
}

// TODO
uint16_t wicat_state::memmap_r()
{
	popmessage("Memory mapping register EFFC01 read!");
	return 0xff;
}

void wicat_state::memmap_w(uint16_t data)
{
	popmessage("Memory mapping register EFFC01 written!");
}

// WD1000 Winchester Disk controller (10MB 5 1/4" HD)
// for now, we'll just try to tell the system there is no HD
uint8_t wicat_state::hdc_r(offs_t offset)
{
	switch(offset)
	{
	case 0x00:  // Error register
		return 0x00;
	case 0x06:  // Status register
		return 0x05;
	}
	return 0x00;
}

void wicat_state::hdc_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
	case 0x00:  // Write precomp / Error register
		logerror("HDC: Write precomp %02x\n",data);
		break;
	case 0x01:  // Data register
		logerror("HDC: Data %02x\n",data);
		break;
	case 0x02:  // Sector Number
		logerror("HDC: Sector Number %02x\n",data);
		break;
	case 0x03:  // Sector Count
		logerror("HDC: Sector Count %02x\n",data);
		break;
	case 0x04:  // Cylinder High
		logerror("HDC: Cylinder High %02x\n",data);
		break;
	case 0x05:  // Cylinder Low
		logerror("HDC: Cylinder Low %02x\n",data);
		break;
	case 0x06:  // Command register
		logerror("HDC: Command %1x\n",(data & 0xf0) >> 4);
		m_maincpu->set_input_line(M68K_IRQ_5,HOLD_LINE);
		break;
	case 0x07:  // Size / Drive / Head
		logerror("HDC: Size / Drive / Head %02x\n",data);
		break;
	case 0x0c:  // DMA bits 9-16
		logerror("HDC: DMA address mid %02x\n",data);
		break;
	case 0x0d:  // DMA bits 1-8  (bit 0 cannot be set)
		logerror("HDC: DMA address low %02x\n",data);
		break;
	case 0x0e:  // DMA R/W
		logerror("HDC: DMA R/W %02x\n",data);
		break;
	case 0x0f:  // DMA bits 17-23
		logerror("HDC: DMA address high %02x\n",data);
		break;
	default:
		logerror("HDC: Write to unknown register %02x\n",data);
	}
}

uint8_t wicat_state::fdc_r(offs_t offset)
{
	uint8_t ret = 0x00;

	popmessage("FDC: read offset %02x",offset);
	switch(offset)
	{
	case 0x00:
		ret = m_fdc->status_r();
		break;
	case 0x01:
		ret = m_fdc->track_r();
		break;
	case 0x02:
		ret = m_fdc->sector_r();
		break;
	case 0x03:
		ret = m_fdc->data_r();
		break;
	case 0x08:
		// Interrupt status (TODO, not part of the FD1795)
		break;
	}
	return ret;
}

void wicat_state::fdc_w(offs_t offset, uint8_t data)
{
	popmessage("FDC: write offset %02x data %02x",offset,data);
	switch(offset)
	{
	case 0x00:
		m_fdc->cmd_w(data);
		break;
	case 0x01:
		m_fdc->track_w(data);
		break;
	case 0x02:
		m_fdc->sector_w(data);
		break;
	case 0x03:
		m_fdc->data_w(data);
		break;
	case 0x08:
		// Interrupt disable / Drive select (TODO, not part of the FD1795)
		break;
	}
}

uint16_t wicat_state::via_r(offs_t offset, uint16_t mem_mask)
{
	if(ACCESSING_BITS_0_7)
		return m_via->read(offset);
	return 0x00;
}

void wicat_state::via_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if(ACCESSING_BITS_0_7)
		m_via->write(offset,data);
	else if(ACCESSING_BITS_8_15)
		m_via->write(offset,data>>8);
}

uint8_t wicat_state::vram_r(offs_t offset)
{
	return m_videocpu->space(AS_IO).read_byte(offset*2);
}

void wicat_state::vram_w(offs_t offset, uint8_t data)
{
	m_videocpu->space(AS_IO).write_byte(offset*2,data);
}

void wicat_state::videosram_store_w(uint8_t data)
{
	if(data & 0x01)  // unsure of the actual bit checked, the terminal code just writes 0xff
	{
		m_videosram->store(1);
		m_videosram->store(0);
		logerror("XD2210: Store triggered.\n");
	}
}

void wicat_state::videosram_recall_w(uint8_t data)
{
	if(data & 0x01)  // unsure of the actual bit checked, the terminal code just writes 0xff
	{
		m_videosram->recall(1);
		m_videosram->recall(0);
		logerror("XD2210: Store triggered.\n");
	}
}

uint8_t wicat_state::video_uart_status_r()
{
	return (m_kbduart->dr_r() << 4) | (m_kbduart->tbre_r() && m_videoctrl->q6_r() ? 0x08 : 0x00);
}

uint8_t wicat_state::video_kbduart_r()
{
	if (!machine().side_effects_disabled())
	{
		m_kbduart->drr_w(1);
		m_kbduart->drr_w(0);
	}
	return m_kbduart->read();
}

uint8_t wicat_state::video_status_r()
{
	// this port is read in the NVI IRQ routine, which if bit 2 is set, will unmask DMA channel 0.  But no idea what triggers it...
	return m_crtc_irq ? 0x04 : 0x00;
}

void wicat_state::dma_hrq_w(int state)
{
	m_videocpu->set_input_line(INPUT_LINE_HALT,state ? ASSERT_LINE : CLEAR_LINE);
	m_videodma->hack_w(state);
}

void wicat_state::crtc_irq_w(int state)
{
	if (state && m_videoctrl->q0_r())
	{
		m_crtc_irq = true;
		m_videoirq->in_w<1>(1);
	}
}

void wicat_state::crtc_irq_clear_w(int state)
{
	if (!state)
	{
		m_crtc_irq = false;
		m_videoirq->in_w<1>(0);
	}
}

I8275_DRAW_CHARACTER_MEMBER(wicat_state::wicat_display_pixels)
{
	using namespace i8275_attributes;
	uint16_t romdata = BIT(attrcode, LTEN) ? 0x3ff : BIT(attrcode, VSP) ? 0 : m_chargen->base()[(charcode << 4) | linecount];
	pen_t const *const pen = m_palette->pens();

	for (int i = 0; i < 10; i++)
	{
		int color = ((romdata & 0x300) != 0) ^ BIT(attrcode, RVV);

		bitmap.pix(y, x + i) = pen[color];
		romdata <<= 1;
	}
}

void wicat_state::wicat(machine_config &config)
{
	M68000(config, m_maincpu, 8_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &wicat_state::main_mem);

	MOS6522(config, m_via, 8_MHz_XTAL / 10); // divider guessed
	m_via->writepa_handler().set(FUNC(wicat_state::via_a_w));
	m_via->writepb_handler().set(FUNC(wicat_state::via_b_w));
	m_via->irq_handler().set_inputline(m_maincpu, M68K_IRQ_1);

	MM58174(config, m_rtc, 0);

	// internal terminal
	SCN2661C(config, m_uart[0], 5.0688_MHz_XTAL);  // connected to terminal board
	m_uart[0]->txd_handler().set(m_videouart[0], FUNC(scn2651_device::rxd_w));
	m_uart[0]->rxrdy_handler().set_inputline(m_maincpu, M68K_IRQ_2);
	m_uart[0]->rts_handler().set(m_videouart[0], FUNC(scn2651_device::cts_w));
	m_uart[0]->dtr_handler().set(m_videouart[0], FUNC(scn2651_device::dsr_w));

	// RS232C ports (x5)
	const char *serial_names[5] = { "serial1", "serial2", "serial3", "serial4", "serial5" };
	for (int i = 1; i <= 5; i++)
	{
		SCN2661C(config, m_uart[i], 5.0688_MHz_XTAL);
		m_uart[i]->txd_handler().set(serial_names[i - 1], FUNC(rs232_port_device::write_txd));
		m_uart[i]->rts_handler().set(serial_names[i - 1], FUNC(rs232_port_device::write_rts));
		m_uart[i]->dtr_handler().set(serial_names[i - 1], FUNC(rs232_port_device::write_dtr));
		//m_uart[i]->rxrdy_handler().set_inputline(m_maincpu, M68K_IRQ_2);
		//m_uart[i]->txemt_dschg_handler().set_inputline(m_maincpu, M68K_IRQ_2);
	}

	// modem
	SCN2661C(config, m_uart[6], 5.0688_MHz_XTAL);  // connected to modem port
	//m_uart[6]->rxrdy_handler().set_inputline(m_maincpu, M68K_IRQ_2);
	//m_uart[6]->txemt_dschg_handler().set_inputline(m_maincpu, M68K_IRQ_2);

	const char *uart_names[5] = { "uart1", "uart2", "uart3", "uart4", "uart5" };
	for (int i = 1; i <= 5; i++)
	{
		rs232_port_device &port(RS232_PORT(config, serial_names[i - 1], default_rs232_devices, nullptr));
		port.rxd_handler().set(uart_names[i - 1], FUNC(scn2661c_device::rxd_w));
		port.dcd_handler().set(uart_names[i - 1], FUNC(scn2661c_device::dcd_w));
		port.dsr_handler().set(uart_names[i - 1], FUNC(scn2661c_device::dsr_w));
		port.cts_handler().set(uart_names[i - 1], FUNC(scn2661c_device::cts_w));
	}

	ls259_device &ledlatch(LS259(config, "ledlatch")); // U19 on I/O board
	ledlatch.q_out_cb<0>().set(FUNC(wicat_state::adir_w));
	ledlatch.q_out_cb<1>().set(FUNC(wicat_state::bdir_w));
	ledlatch.q_out_cb<2>().set_output("led1").invert(); // 0 = on, 1 = off
	ledlatch.q_out_cb<3>().set_output("led2").invert();
	ledlatch.q_out_cb<4>().set_output("led3").invert();
	ledlatch.q_out_cb<5>().set_output("led4").invert();
	ledlatch.q_out_cb<6>().set_output("led5").invert();
	ledlatch.q_out_cb<7>().set_output("led6").invert();

	/* video hardware */
	Z8002(config, m_videocpu, 8_MHz_XTAL/2);  // AMD AMZ8002DC
	m_videocpu->set_addrmap(AS_PROGRAM, &wicat_state::video_mem);
	m_videocpu->set_addrmap(AS_IO, &wicat_state::video_io);

	INPUT_MERGER_ANY_HIGH(config, m_videoirq).output_handler().set_inputline(m_videocpu, z8002_device::NVI_LINE);

	LS259(config, m_videoctrl);
	m_videoctrl->q_out_cb<0>().set(FUNC(wicat_state::crtc_irq_clear_w));
	m_videoctrl->q_out_cb<6>().set("tbreirq", FUNC(input_merger_device::in_w<1>));
	m_videoctrl->q_out_cb<7>().set("dmairq", FUNC(input_merger_device::in_w<1>));
	// Q1-Q5 are all used but unknown

	AM9517A(config, m_videodma, 8_MHz_XTAL / 2);  // AMD AM9517-4PC
	m_videodma->out_hreq_callback().set(FUNC(wicat_state::dma_hrq_w));
	m_videodma->out_eop_callback().set("dmairq", FUNC(input_merger_device::in_w<0>));
	m_videodma->in_memr_callback().set(FUNC(wicat_state::vram_r));
	m_videodma->out_memw_callback().set(FUNC(wicat_state::vram_w));
	m_videodma->out_iow_callback<0>().set(m_crtc, FUNC(i8275_device::dack_w));

	INPUT_MERGER_ALL_HIGH(config, "dmairq").output_handler().set_inputline(m_videocpu, z8002_device::NMI_LINE);

	IM6402(config, m_kbduart, 0); // IM6402-1IPL
	m_kbduart->set_rrc(5068800 / 1056); // 74LS393 output?
	m_kbduart->set_trc(5068800 / 1056);
	m_kbduart->tro_callback().set("keyboard", FUNC(keytronic_connector_device::ser_in_w));
	m_kbduart->dr_callback().set(m_videoirq, FUNC(input_merger_device::in_w<2>));
	m_kbduart->tbre_callback().set("tbreirq", FUNC(input_merger_device::in_w<0>));

	keytronic_connector_device &keyboard(KEYTRONIC_CONNECTOR(config, "keyboard", ascii_terminal_keyboards, "l2207"));
	keyboard.ser_out_callback().set(m_kbduart, FUNC(im6402_device::rri_w));

	INPUT_MERGER_ALL_HIGH(config, "tbreirq").output_handler().set(m_videoirq, FUNC(input_merger_device::in_w<3>));

	// terminal (2x INS2651, 1x IM6402 - one of these is for the keyboard, another communicates with the main board, the third is unknown)
	SCN2651(config, m_videouart[0], 5.0688_MHz_XTAL);
	m_videouart[0]->txd_handler().set(m_uart[0], FUNC(scn2651_device::rxd_w));
	m_videouart[0]->rxrdy_handler().set(m_videoirq, FUNC(input_merger_device::in_w<0>));
	m_videouart[0]->rts_handler().set(m_uart[0], FUNC(scn2651_device::cts_w));
	m_videouart[0]->dtr_handler().set(m_uart[0], FUNC(scn2651_device::dsr_w));

	SCN2651(config, m_videouart[1], 5.0688_MHz_XTAL);
	//m_videouart[1]->set_rxc(19200);
	//m_videouart[1]->set_txc(19200);
	m_videouart[1]->rxrdy_handler().set(m_videoirq, FUNC(input_merger_device::in_w<4>));

	X2210(config, "vsram");  // XD2210

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_raw(19.6608_MHz_XTAL, 1020, 0, 800, 324, 0, 300);
	screen.set_screen_update("video", FUNC(i8275_device::screen_update));

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	I8275(config, m_crtc, 19.6608_MHz_XTAL/10);
	m_crtc->set_character_width(10);
	m_crtc->set_display_callback(FUNC(wicat_state::wicat_display_pixels));
	m_crtc->drq_wr_callback().set(m_videodma, FUNC(am9517a_device::dreq0_w));
	m_crtc->vrtc_wr_callback().set(FUNC(wicat_state::crtc_irq_w));
	m_crtc->set_screen("screen");

	config.set_default_layout(layout_wicat);

	/* Winchester Disk Controller (WD1000 + FD1795) */
	n8x300_cpu_device &wd1kcpu(N8X300(config, "wd1kcpu", 8_MHz_XTAL));
	wd1kcpu.set_addrmap(AS_PROGRAM, &wicat_state::wd1000_mem);
	wd1kcpu.set_addrmap(AS_IO, &wicat_state::wd1000_io);

	FD1795(config, m_fdc, 8_MHz_XTAL);
	FLOPPY_CONNECTOR(config, "fdc:0", wicat_floppies, "525qd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", wicat_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:2", wicat_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:3", wicat_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	SOFTWARE_LIST(config, "flop_list").set_original("wicat");

	// TODO: beeper (part of keyboard)
}

/* ROM definition */
ROM_START( wicat )
	ROM_REGION16_BE(0x4000, "c1", 0)
	ROM_LOAD16_BYTE("wiboot.e",   0x00000, 0x0800, CRC(6f0f73c6) SHA1(be635bf3ffa1301f844a3d5560e278de46740d19) )
	ROM_LOAD16_BYTE("wiboot.o",   0x00001, 0x0800, CRC(b9763bbd) SHA1(68f497be56ff69534e17b41a40737cd6f708d65e) )
	ROM_LOAD16_BYTE("tpcnif.e",   0x01000, 0x0800, CRC(fd1127ec) SHA1(7c6b436c0cea41dbb23cb6bd9b9a5c21fa61d232) )
	ROM_LOAD16_BYTE("tpcnif.o",   0x01001, 0x0800, CRC(caa16e2a) SHA1(b3e64b676f50b65b3e365fc5f17eb1759c1310df) )
	ROM_LOAD16_BYTE("tpcf.e",     0x02000, 0x0800, CRC(d34be25c) SHA1(1b167918cbc19c9364f020176f4cc3722cba8434) )
	ROM_LOAD16_BYTE("tpcf.o",     0x02001, 0x0800, CRC(7712c570) SHA1(8743b7c98190ecf3bf7e917e6143b47b3b36db8d) )
	ROM_REGION(0x0060, "c1proms", 0)
	ROM_LOAD       ("cpu.8b",     0x00000, 0x0020, CRC(99b90665) SHA1(8a4677ea814e1843001fe28b284226b7291cdf76) )
	ROM_LOAD       ("cpu.8c",     0x00020, 0x0020, CRC(190a55ad) SHA1(de8a847bff8c343d69b853a215e6ee775ef2ef96) )
	ROM_LOAD       ("cpu.15c",    0x00040, 0x0020, CRC(ba2dd77d) SHA1(eb693d6d30aa6a9dba61c6c41a75614ed4e9e69a) )

	// System 150 CPU/MU board
	ROM_REGION16_BE(0x2000, "c2", 0)
	ROM_SYSTEM_BIOS( 0, "cms", "CMS HD / Floppy Boot / Boot v1.56" )
	ROMX_LOAD("s156.a5", 0x00000, 0x0800, CRC(2c1e9542) SHA1(50184e04f0c881818e96e2162111d16304e8762f), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD("s156.a7", 0x00001, 0x0800, CRC(5a0cb30d) SHA1(aa106ad5a8b9e89613f7ea026d62832cfdb19fd0), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD("37m.b5",  0x01000, 0x0800, CRC(831571fb) SHA1(fcc647b3ef9f0cca3e8212f850f96676d24cf318), ROM_SKIP(1) | ROM_BIOS(0) )
	ROMX_LOAD("37m.b7",  0x01001, 0x0800, CRC(3c346e8e) SHA1(d8ff8297d265b25655c854ed8515fa9e16c63f39), ROM_SKIP(1) | ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "wd3", "WD3 HD Boot / Boot v1.56" )
	ROMX_LOAD("boot156.a5", 0x00000, 0x0800, CRC(58510a52) SHA1(d2135b056a04ba830b0ae1cef539e4a9a1b58f82), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD("boot156.a7", 0x00001, 0x0800, CRC(e53999f1) SHA1(9c6c6a3a56b5c16a35e1fe824f37c8ae739ebcb9), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD("wd3_15.b5",  0x01000, 0x0800, CRC(a765899b) SHA1(8427c564029914b7dbc29768ce451604180e390f), ROM_SKIP(1) | ROM_BIOS(1) )
	ROMX_LOAD("wd3_15.b7",  0x01001, 0x0800, CRC(9d986585) SHA1(1ac7579c692f827b121c56dac0a77b15400caba1), ROM_SKIP(1) | ROM_BIOS(1) )

	// Terminal CPU board (Graphical)
	// "MG8000 VERSION 3.0"
	ROM_REGION16_BE(0x8000, "g1", 0)
	ROM_LOAD16_BYTE("1term0.e",   0x00000, 0x0800, CRC(a9aade37) SHA1(644e9362d5a9523be5c6f39a650b574735dbd4a2) )
	ROM_LOAD16_BYTE("1term0.o",   0x00001, 0x0800, CRC(8026b5b7) SHA1(cb93e0595b321889694cbb87f497d244e6a2d648) )
	ROM_LOAD16_BYTE("1term1.e",   0x01000, 0x0800, CRC(e6ce8016) SHA1(fae987f1ac26d027ed176f8886832e87d1feae60) )
	ROM_LOAD16_BYTE("1term1.o",   0x01001, 0x0800, CRC(d71f763e) SHA1(b0a7f4cc90ce267aec7e72ad22a227f0c8c1f650) )
	ROM_LOAD16_BYTE("1term2.e",   0x02000, 0x0800, CRC(c0e82703) SHA1(7a17da13c01e15b61eea65b06d988ab8ba7eaaf3) )
	ROM_LOAD16_BYTE("1term2.o",   0x02001, 0x0800, CRC(aa0d5b4f) SHA1(b37c2e5220f4838a805b20a0ef21689067f1a759) )
	ROM_LOAD16_BYTE("1term3.e",   0x03000, 0x0800, CRC(cd33f4c8) SHA1(6603c5f2330a9a5ec1121a367cebe6e900a00cb0) )
	ROM_LOAD16_BYTE("1term3.o",   0x03001, 0x0800, CRC(05e56714) SHA1(0c31be3c9ec90a0858fe04a208e2627e4beb12b0) )
	ROM_LOAD16_BYTE("1term4.e",   0x04000, 0x0800, CRC(a157c61f) SHA1(59b7be6cd696b2508b5c1fd7b6e6f7cb5a9f12ab) )
	ROM_LOAD16_BYTE("1term4.o",   0x04001, 0x0800, CRC(364c1a95) SHA1(bfd62a71c9d8f83dc12a7dbbf362d18819380ef3) )
	ROM_LOAD16_BYTE("1term5.e",   0x05000, 0x0800, CRC(c2b8bc9e) SHA1(cd054988a9694b3a211e1993da1b3dc2c5e6fdc2) )
	ROM_LOAD16_BYTE("1term5.o",   0x05001, 0x0800, CRC(421e0521) SHA1(29b87938f5c25c05920ca2c14893700bc45a86c5) )
	ROM_LOAD16_BYTE("1term6.e",   0x06000, 0x0800, CRC(f0d14ed6) SHA1(840acc2b90e8d16df7e5d60c399b08ec0e126a88) )
	ROM_LOAD16_BYTE("1term6.o",   0x06001, 0x0800, CRC(e245ff49) SHA1(9a34e6cf6013b1044cccf26371cc3a000f17b58c) )
	ROM_LOAD16_BYTE("1term7.e",   0x07000, 0x0800, CRC(0c918550) SHA1(2ef6ce41cc2643d45c4bae31ce151d8b6c363471) )
	ROM_LOAD16_BYTE("1term7.o",   0x07001, 0x0800, CRC(71fdc692) SHA1(d6f12ec20ff2e4948f54b0c79f11ccbdc9db865c) )
	ROM_REGION(0x40, "vsram", 0)
	ROM_LOAD       ("ee8-82.bin",  0x00000, 0x0040, CRC(dfb4b0fb) SHA1(12304f5c5236791f5e931d9e49b4a70dcbba55c0) )

	// "MG8000 VERSION 1.1"
	ROM_REGION16_BE(0x8000, "g2", 0)
	ROM_LOAD16_BYTE("2term0.e",   0x00000, 0x0800, CRC(29e5dd68) SHA1(9023f53d554b9ef4f4efc731645ba42f728bcd2c) )
	ROM_LOAD16_BYTE("2term0.o",   0x00001, 0x0800, CRC(91edd05d) SHA1(378b06fc8316199b7c580a6e7f28368dacdac5a9) )
	ROM_LOAD16_BYTE("2term1.e",   0x01000, 0x0800, CRC(2b48abe4) SHA1(4c9b4db1c1408b6551d50172dda994b36a2ee4b1) )
	ROM_LOAD16_BYTE("2term1.o",   0x01001, 0x0800, CRC(4c0e4f95) SHA1(bd49bf71fea1acfd50781820f0a650411b6f996b) )
	ROM_LOAD16_BYTE("2term2.e",   0x02000, 0x0800, CRC(3251324b) SHA1(e8f52308c9cbb9bcb5adb2685609d6a69b9eec1d) )
	ROM_LOAD16_BYTE("2term2.o",   0x02001, 0x0800, CRC(3a49c9e7) SHA1(0718b029ed316bc8e7bf22b0e94b6b5628758580) )
	ROM_LOAD16_BYTE("2term3.e",   0x03000, 0x0800, CRC(0f17be85) SHA1(9c40b4d06f3fb8def88b87615a590bb03dcfc4f4) )
	ROM_LOAD16_BYTE("2term3.o",   0x03001, 0x0800, CRC(08ae31c5) SHA1(2e53f87b6a4e0b973f7918d97f57f6560c651ab6) )
	ROM_LOAD16_BYTE("2term4.e",   0x04000, 0x0800, CRC(413936e7) SHA1(ce9d8666ca4e6847514bcf4de5703f0845e72928) )
	ROM_LOAD16_BYTE("2term4.o",   0x04001, 0x0800, CRC(06deab4e) SHA1(af5be7105a24d81dcc539296631b4309f7b8cb3f) )
	ROM_LOAD16_BYTE("2term5.e",   0x05000, 0x0800, CRC(7979bf59) SHA1(1bc397c58ce026fb90a02714d42df8f179a4f50e) )
	ROM_LOAD16_BYTE("2term5.o",   0x05001, 0x0800, CRC(e1f738ca) SHA1(bd8d7f1acb243880fd364f71097b9711de496739) )
	ROM_LOAD16_BYTE("2term6.e",   0x06000, 0x0800, CRC(bb04d70c) SHA1(0b482c2f06fe5e042a5813f027f5cf034d72e0dd) )
	ROM_LOAD16_BYTE("2term6.o",   0x06001, 0x0800, CRC(0afb566c) SHA1(761455ced46b6fccd0be9c8fa920f7954a36972b) )
	ROM_LOAD16_BYTE("2term7.e",   0x07000, 0x0800, CRC(033ea830) SHA1(27c33eea2df812a1a96e2f47ba7993e2ca3675ad) )
	ROM_LOAD16_BYTE("2term7.o",   0x07001, 0x0800, CRC(e157c5d2) SHA1(3cd1ea0fb9df1358e8a358468a4df5e4eaaa86a2) )
	ROM_REGION(0x40, "g2novram", 0)
	ROM_LOAD       ("ee2-2.bin",  0x00000, 0x0040, CRC(8f265118) SHA1(6bd74e3d01cf85cca1abcc15cb229dbd63022978) )

	// Terminal Video board
	ROM_REGION(0x1000, "g2char", 0)
	ROM_LOAD       ("ascii.chr",  0x00000, 0x0800, CRC(43e26e37) SHA1(f3d5d16040c66f0e827f72a35d4694ca62950949) )
	ROM_LOAD       ("apl.chr",    0x00800, 0x0800, CRC(8c6d698e) SHA1(147dd9296fe2efc6140fa148a6edf673c33f9371) )

	// Winchester Disk Controller  (WD1000 (comprised of an 8X300 + some WD1100-xx bits), FD1795 (FDC))
	ROM_REGION16_BE(0x1800, "wd3", 0)
	ROM_LOAD16_BYTE("wd3.u96",    0x00000, 0x0800, CRC(52736e61) SHA1(71c7c9170c733c483393969cb1cb3798b3eb980c) )  // 8X300 code even
	ROM_LOAD16_BYTE("wd3.u97",    0x00001, 0x0800, CRC(a66619ec) SHA1(5d091ac7c88f2f45b4a05e78bfc7a16c206b31ff) )  // 8X300 code odd
	ROM_LOAD       ("wd3.u95",    0x01000, 0x0800, CRC(80bb0617) SHA1(ac0f3194fcbef77532571baa3fec78b3010528bf) )  // "Fast IO select" bytes
ROM_END

} // anonymous namespace


/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY               FULLNAME            FLAGS
COMP( 1982, wicat, 0,      0,      wicat,   wicat, wicat_state, empty_init, "Millennium Systems", "Wicat System 150", MACHINE_NOT_WORKING )



wildfire.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/*******************************************************************************

Parker Brothers Wildfire, by Bob and Holly Doyle (prototype), and Garry Kitchen

This is an electronic handheld pinball game. It has dozens of small leds to
create the illusion of a moving ball, and even the flippers are leds. A drawing
of a pinball table is added as overlay.

Hardware notes:
- AMI S2150, labeled C10641
- RC circuit for speaker volume decay (see patent US4334679 FIG.5,
  the 2 resistors at A12 are 10K and the cap is 4.7uF)

led translation table: led Lzz from patent US4334679 FIG.4* = MAME y.x:
*note: 2 mistakes in it: L19 between L12 and L14 should be L13, and L84 should of course be L48

  0 = -      10 = 6.6    20 = 4.5    30 = 5.3    40 = 5.7    50 = 11.6
  1 = 10.7   11 = 5.6    21 = 4.4    31 = 4.3    41 = 6.0    51 = 11.5
  2 = 10.0   12 = 6.5    22 = 5.4    32 = 5.2    42 = 7.0    52 = 11.4
  3 = 10.1   13 = 7.5    23 = 6.3    33 = 5.1    43 = 8.0    53 = 11.3
  4 = 10.2   14 = 8.5    24 = 7.3    34 = 11.7   44 = 9.0    60 = 3.6
  5 = 10.3   15 = 9.4    25 = 11.1   35 = 7.1    45 = 6.7    61 = 3.6(!)
  6 = 10.4   16 = 8.4    26 = 9.3    36 = 9.1    46 = 7.7    62 = 3.5
  7 = 10.5   17 = 7.4    27 = 9.2    37 = 5.0    47 = 8.7    63 = 3.5(!)
  8 = 8.6    18 = 11.2   28 = 8.2    38 = 6.1    48 = 9.7    70 = 3.3
  9 = 7.6    19 = 5.5    29 = 11.0   39 = 8.1    49 = -

NOTE!: MAME external artwork is required

TODO:
- sound can be improved, volume decay should be more steep at the start, and the
  pitch sounds wrong too (latter is an MCU emulation problem)

*******************************************************************************/

#include "emu.h"

#include "cpu/amis2000/amis2000.h"
#include "sound/flt_vol.h"
#include "sound/spkrdev.h"
#include "video/pwm.h"

#include "speaker.h"

#include "wildfire.lh"


namespace {

class wildfire_state : public driver_device
{
public:
	wildfire_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_speaker(*this, "speaker"),
		m_volume(*this, "volume")
	{ }

	void wildfire(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<amis2000_base_device> m_maincpu;
	required_device<pwm_display_device> m_display;
	required_device<speaker_sound_device> m_speaker;
	required_device<filter_volume_device> m_volume;

	emu_timer *m_decaytimer;
	bool m_speaker_on = false;

	void write_d(u8 data);
	void write_a(u16 data);

	void speaker_decay_sim(s32 param);
};

void wildfire_state::machine_start()
{
	m_decaytimer = timer_alloc(FUNC(wildfire_state::speaker_decay_sim), this);

	// register for savestates
	save_item(NAME(m_speaker_on));
}



/*******************************************************************************
    I/O
*******************************************************************************/

void wildfire_state::speaker_decay_sim(s32 param)
{
	// volume decays when speaker is off (divisor and timer period determine duration)
	m_volume->set_gain(m_volume->gain() / 1.0025);
	m_decaytimer->adjust(attotime::from_usec(100));
}

void wildfire_state::write_d(u8 data)
{
	// D0-D7: led/7seg data
	m_display->write_mx(bitswap<8>(data,7,0,1,2,3,4,5,6));
}

void wildfire_state::write_a(u16 data)
{
	// A0-A2: digit select
	// A3-A11: led select
	m_display->write_my(~data);

	// A12: speaker on
	bool speaker_on = bool(~data & 0x1000);
	if (speaker_on)
	{
		m_volume->set_gain(1.0);
		m_decaytimer->adjust(attotime::never);
	}
	else if (m_speaker_on)
		m_decaytimer->adjust(attotime::zero);

	m_speaker_on = speaker_on;
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( wildfire )
	PORT_START("IN.0") // I
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Shooter Button")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Left Flipper")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Right Flipper")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

// 7seg decoder table differs from default, this one is made by hand
static const u8 wildfire_7seg_table[0x10] =
{
	0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, // 0, 1, 2, 3, 4, 5, 6, 7
	0x7f, 0x6f, 0x77, 0x73, 0x39, 0x38, 0x79, 0x40  // 8, 9, ?, P, ?, L, ?, -
};

void wildfire_state::wildfire(machine_config &config)
{
	// basic machine hardware
	AMI_S2152(config, m_maincpu, 850000); // approximation - RC osc. R=?, C=?
	m_maincpu->set_7seg_table(wildfire_7seg_table);
	m_maincpu->read_i().set_ioport("IN.0");
	m_maincpu->write_d().set(FUNC(wildfire_state::write_d));
	m_maincpu->write_a().set(FUNC(wildfire_state::write_a));
	m_maincpu->write_f().set(m_speaker, FUNC(speaker_sound_device::level_w));

	// video hardware
	PWM_DISPLAY(config, m_display).set_size(12, 8);
	m_display->set_segmask(7, 0x7f);
	m_display->set_bri_levels(0.01, 0.1); // bumpers are dimmed
	config.set_default_layout(layout_wildfire);

	// sound hardware
	SPEAKER(config, "mono").front_center();
	FILTER_VOLUME(config, m_volume).set_gain(0.0).add_route(ALL_OUTPUTS, "mono", 1.0);
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "volume", 0.25);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( wildfire )
	ROM_REGION( 0x0800, "maincpu", ROMREGION_ERASE00 )
	// Typed in from patent US4334679, data should be correct(it included checksums). 1st half was also dumped/verified with release version.
	ROM_LOAD( "us4334679", 0x0000, 0x0400, CRC(84ac0f1f) SHA1(1e00ddd402acfc2cc267c34eed4b89d863e2144f) )
	ROM_CONTINUE(          0x0600, 0x0200 )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1979, wildfire, 0,      0,      wildfire, wildfire, wildfire_state, empty_init, "Parker Brothers", "Wildfire (patent)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK ) // note: pretty sure that it matches the commercial release



wizard.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:

/*
Sharp Wizard series of electronic organizers

Currently only the IQ-7000 is dumped (sold in the US as OZ-7000)

Other known undumped models are:
IQ-7100M
OZ-7200 WIZARD
IQ-7300M
IQ-7520M
IQ-7700M
IQ-7720M
IQ-8100M
IQ-8200
IQ-8300M
IQ-8500M
IQ-8900G
IQ-8920G
OZ-9520 WIZARD
IQ-9000G
IQ-9200G

Sharp followed up with the Zaurus series (see drivers/zaurus.cpp)

Models IQ-7000, IQ-7100M, OZ-7200 WIZARD, IQ-7300M, IQ-7520M, IQ-7700M, and IQ-7720M
use the following screen layout (96x64 square pixels + custom segments at the right):
____________________________________
| 96 x 64 pixels LCD                |
| 16 cols x 8 lines (5 x 7 chars)   | BATT
| 12 cols x 4 lines (8 x 16 chars)  | CARD
|                                   | EDIT
|                                   | SHIFT
|                                   | CAPS
|                                   | ✱ 🅂
|                                   | � 🕭
|                                   | ↑ ↓
|___________________________________| ← →

IQ-8100M, IQ-8200, IQ-8300M and IQ-8500M models use a 240x64 screen (40 cols x 8 lines with
6x8 chars or 30 cols x 4 lines with 8x16 chars).
IQ-8900G, IQ-8920G and OZ-9520 WIZARD models use a 240x160 screen.
IQ-9000G and IQ-9200G models use a 320x240 screen.

IQ-700 hardware notes:
-32 Kbytes RAM.
-32,768 Hz clock crystal oscillation frecuency (as per manual).
-Supports the Sharp CE-50P external printer.
-Supports a cassette interface for saving and loading data (connected through the CE-50P printer).
-Supports PC connection through Sharp PC-LINK software.
-Supports data communication between two Organizers.

More info:
-SHARP PC-E500 CPU Instruction Table: http://www.andrewwoods3d.com/pce500/insttabl.html

*/

#include "emu.h"

#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "cpu/arm7/arm7.h" // wrong, needs CPU core

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "screen.h"


namespace {

class wizard_state : public driver_device
{
public:
	wizard_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cart(*this, "cartslot")
		, m_cart_region(nullptr)
	{ }

	void iq7000(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	void main_map(address_map &map) ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);

	required_device<cpu_device> m_maincpu;
	required_device<generic_slot_device> m_cart;
	memory_region *m_cart_region;
};


void wizard_state::machine_start()
{
	// if there's a cart, override the standard mapping
	if (m_cart && m_cart->exists())
	{
		m_cart_region = memregion(std::string(m_cart->tag()) + GENERIC_ROM_REGION_TAG);
	}
}

void wizard_state::machine_reset()
{
}

DEVICE_IMAGE_LOAD_MEMBER(wizard_state::cart_load)
{
	uint32_t const size = m_cart->common_get_size("rom");

	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");

	return std::make_pair(std::error_condition(), std::string());
}

static INPUT_PORTS_START( iq7000 )
INPUT_PORTS_END

uint32_t wizard_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void wizard_state::main_map(address_map &map)
{
}

void wizard_state::iq7000(machine_config &config)
{
	ARM9(config, m_maincpu, 240000000); // actually Sharp SC62015B02, currently unemulated
	m_maincpu->set_addrmap(AS_PROGRAM, &wizard_state::main_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD)); // all wrong, TBD
	screen.set_refresh_hz(50);
	screen.set_screen_update(FUNC(wizard_state::screen_update));
	screen.set_size(96, 64);
	screen.set_visarea_full();

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "wizard_cart");
	m_cart->set_width(GENERIC_ROM16_WIDTH);
	m_cart->set_device_load(FUNC(wizard_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("wizard_cart");
}

ROM_START( iq7000 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "iq7000.bin", 0x00000, 0x10000, CRC(04ba80ca) SHA1(fe25e7c892b1e57641ff75bcd703882e28627fda) )
ROM_END

} // Anonymous namespace


CONS( 1988, iq7000, 0, 0, iq7000, iq7000, wizard_state, empty_init, "Sharp", "IQ-7000", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



wk1800.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Devin Acker

/*
    Casio WK-1600/1800 series keyboards

    Models on this hardware:
        - CTK-711EX (1998)
            61 keys, 5MB wave ROM
        - CTK-811EX (1998), CTK-731 (1999)
            61 keys, 5MB wave ROM, floppy drive
        - WK-1600, WK-1630 (2000)
            73 keys, 8MB wave ROM
        - WK-1800 (2000)
            73 keys, 8MB wave ROM, floppy drive
        - AP-60R (1999), AP-65R (2001)
            88 keys, 8MB wave ROM, floppy drive

    TODO:
        - fix floppy controller hookup for wk1800. current issues:
            - pressing the Disk button with the drive empty starts the drive motor,
              then the firmware waits forever on some status bit that is never set
            - pressing the Disk button with a disk inserted results in several 'forced abort'
              errors from the H8 DMA controller
            - wk1800 firmware seems to rely on different TS bit behavior from the HD63266
              compared to a standard uPD765
        - add software list for style/program disks
 */

#include "emu.h"

#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/h8/h83048.h"
#include "imagedev/floppy.h"
#include "machine/nvram.h"
#include "machine/gt913_kbd.h"
#include "machine/upd765.h"
#include "sound/gt155.h"
#include "video/hd44780.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include <algorithm>

namespace {

class wk1600_state : public driver_device
{
public:
	wk1600_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_gt155(*this, "gt155")
		, m_lcdc(*this, "lcdc")
		, m_sound_rom(*this, "gt155")
		, m_inputs(*this, "KC%u", 0U)
		, m_outputs(*this, "%02x.%d.%d", 0U, 0U, 0U)
		, m_led(*this, "led%d", 0U)
		, m_led_power(*this, "led_power")
	{
	}

	void wk1600(machine_config &config) ATTR_COLD;

	TIMER_CALLBACK_MEMBER(nmi_clear) { m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE); }

	ioport_value lcd_r()   { return m_lcdc->db_r() >> 4; }
	void lcd_w(int state)  { m_lcdc->db_w(state << 4); }

	void shift_data_w(int state) { m_shift_data = state; }
	void led_clk_w(int state);
	void input_clk_w(int state);

	DECLARE_INPUT_CHANGED_MEMBER(power_w);

	template<int StartBit> ioport_value inputs_r();

	void apo_w(int state);

protected:
	virtual void driver_start() override ATTR_COLD;

	void common_map(address_map &map) ATTR_COLD;

	required_device<h83048_device> m_maincpu;
	required_device<gt155_device> m_gt155;
	required_device<hd44780_device> m_lcdc;

private:
	void wk1600_map(address_map &map) ATTR_COLD;

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	required_memory_region m_sound_rom;

	emu_timer* m_nmi_timer = nullptr;

	optional_ioport_array<8> m_inputs;

	output_finder<64, 8, 5> m_outputs;
	output_finder<8> m_led;
	output_finder<> m_led_power;

	u8 m_sound_regs[16];
	u32 m_sound_rom_addr;
	u32 m_dsp_data[128];

	u8 m_led_sel, m_input_sel;
	u8 m_led_clk, m_input_clk, m_shift_data;
};

class wk1800_state : public wk1600_state
{
public:
	static constexpr feature_type unemulated_features() { return feature::DISK; }

	wk1800_state(machine_config const &mconfig, device_type type, char const *tag)
		: wk1600_state(mconfig, type, tag)
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:0")
	{
	}

	void wk1800(machine_config &config) ATTR_COLD;

	void fdc_rate_w(int state) { if (m_fdc) m_fdc->rate_w(state); }

private:
	void wk1800_map(address_map &map) ATTR_COLD;

	optional_device<hd63266f_device> m_fdc;
	optional_device<floppy_connector> m_floppy;
};

/**************************************************************************/
void wk1600_state::led_clk_w(int state)
{
	if (state && !m_led_clk)
	{
		m_led_sel <<= 1;
		m_led_sel |= (m_shift_data & 1);

		for (int i = 0; i < 8; i++)
			m_led[i] = BIT(~m_led_sel, i);
	}

	m_led_clk = state;
}

/**************************************************************************/
void wk1600_state::input_clk_w(int state)
{
	if (state && !m_input_clk)
	{
		m_input_sel <<= 1;
		m_input_sel |= (m_shift_data & 1);
	}

	m_input_clk = state;
}

/**************************************************************************/
INPUT_CHANGED_MEMBER(wk1600_state::power_w)
{
	if (newval)
	{
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
		m_nmi_timer->adjust(attotime::never);
	}
	else
	{
		// give the CPU enough time to switch NMI to active-high so it fires again
		// otherwise, releasing the power button too quickly may be ignored
		m_nmi_timer->adjust(attotime::from_msec(100));
	}
}

/**************************************************************************/
template<int StartBit>
ioport_value wk1600_state::inputs_r()
{
	ioport_value result = 0;
	for (unsigned i = 0U; i < m_inputs.size(); i++)
	{
		if (BIT(m_input_sel, i))
			result |= m_inputs[i].read_safe(0);
	}

	return result >> StartBit;
}

/**************************************************************************/
void wk1600_state::apo_w(int state)
{
	logerror("apo_w: %x\n", state);
	if (!state)
		m_lcdc->reset();

	m_led_power = state;
	m_gt155->set_output_gain(ALL_OUTPUTS, state ? 1.0 : 0.0);
}

/**************************************************************************/
u32 wk1600_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const u8 *render = m_lcdc->render();
	for (int x = 0; x < 64; x++)
	{
		for (int y = 0; y < 8; y++)
		{
			u8 v = *render++;
			for (int z = 0; z < 5; z++)
				m_outputs[x][y][z] = (v >> z) & 1;
		}
		render += 8;
	}

	return 0;
}


/**************************************************************************/
void wk1600_state::common_map(address_map &map)
{
	map(0x00000, 0x1ffff).rom();
	map(0x20000, 0x2ffff).rw(m_gt155, FUNC(gt155_device::read), FUNC(gt155_device::write));
	map(0x30000, 0x30001).mirror(0x0fff0).r("kbd", FUNC(gt913_kbd_hle_device::read));
	map(0x30002, 0x30003).mirror(0x0fff0).r("kbd", FUNC(gt913_kbd_hle_device::status_r));
}

/**************************************************************************/
void wk1600_state::wk1600_map(address_map &map)
{
	common_map(map);
	map(0x80000, 0x9ffff).mirror(0x60000).ram().share("nvram");
}

/**************************************************************************/
void wk1800_state::wk1800_map(address_map &map)
{
	common_map(map);
//  map(0x40000, 0x40003).mirror(0x1fffc).m(m_fdc, FUNC(hd63266f_device::map));
//  map(0x60000, 0x7ffff).rw(m_fdc, FUNC(hd63266f_device::dma_r), FUNC(hd63266f_device::dma_w));
	map(0x80000, 0xbffff).mirror(0x40000).ram().share("nvram");
}


/**************************************************************************/
void wk1600_state::driver_start()
{
	m_led.resolve();
	m_led_power.resolve();
	m_outputs.resolve();

	m_nmi_timer = timer_alloc(FUNC(wk1600_state::nmi_clear), this);

	std::fill(std::begin(m_sound_regs), std::end(m_sound_regs), 0);
	std::fill(std::begin(m_dsp_data), std::end(m_dsp_data), 0);
	m_sound_rom_addr = 0;

	m_led_sel = 0xff;
	m_input_sel = 0;

	m_led_clk = m_input_clk = m_shift_data = 0;

	save_item(NAME(m_sound_regs));
	save_item(NAME(m_dsp_data));
	save_item(NAME(m_sound_rom_addr));

	save_item(NAME(m_led_sel));
	save_item(NAME(m_input_sel));

	save_item(NAME(m_led_clk));
	save_item(NAME(m_input_clk));
	save_item(NAME(m_shift_data));
}


/**************************************************************************/
void wk1600_state::wk1600(machine_config &config)
{
	H83048(config, m_maincpu, 16'000'000).set_mode_a20();
	m_maincpu->set_addrmap(AS_PROGRAM, &wk1600_state::wk1600_map);
	m_maincpu->read_adc<0>().set_constant(0);
	m_maincpu->read_adc<1>().set_ioport("AN1");
	m_maincpu->read_adc<2>().set_constant(0);
	m_maincpu->read_adc<3>().set_ioport("AN3");
	m_maincpu->read_port6().set_ioport("P6");
	m_maincpu->read_port7().set_ioport("P7");
	m_maincpu->read_port8().set_ioport("P8");
	m_maincpu->write_port8().set_ioport("P8");
	m_maincpu->read_port9().set_ioport("P9");
	m_maincpu->read_porta().set_ioport("PA");
	m_maincpu->write_porta().set_ioport("PA");
	m_maincpu->read_portb().set(FUNC(wk1600_state::lcd_r));
	m_maincpu->write_portb().set_ioport("PB");

	NVRAM(config, "nvram");

	GT913_KBD_HLE(config, "kbd"); // actually TC190C020AF-001 gate array

	auto &mdin(MIDI_PORT(config, "mdin"));
	midiin_slot(mdin);
	mdin.rxd_handler().set(m_maincpu, FUNC(h83048_device::sci_rx_w<0>));

	auto &mdout(MIDI_PORT(config, "mdout"));
	midiout_slot(mdout);
	m_maincpu->write_sci_tx<0>().set(mdout, FUNC(midi_port_device::write_txd));

	HD44780(config, m_lcdc, 270'000); // TODO: Wrong device type, should be SED1278F2A; clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 8);

	auto &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1755, 450);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(wk1600_state::screen_update));

	SPEAKER(config, "speaker", 2).front();

	GT155(config, m_gt155, 24.576_MHz_XTAL);
	m_gt155->add_route(0, "speaker", 1.0, 0);
	m_gt155->add_route(1, "speaker", 1.0, 1);
}

/**************************************************************************/
[[maybe_unused]] static void wk1800_floppies(device_slot_interface &device)
{
	device.option_add("35hd", FLOPPY_35_HD);
}

/**************************************************************************/
void wk1800_state::wk1800(machine_config &config)
{
	wk1600(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &wk1800_state::wk1800_map);

#if 0
	HD63266F(config, m_fdc, 16'000'000);
	m_fdc->set_ready_line_connected(false);
	m_fdc->drq_wr_callback().set_inputline(m_maincpu, H8_INPUT_LINE_DREQ0);
	m_maincpu->tend0().set(m_fdc, FUNC(upd765a_device::tc_line_w));

	FLOPPY_CONNECTOR(config, m_floppy, wk1800_floppies, "35hd", floppy_image_device::default_pc_floppy_formats);
	m_floppy->enable_sound(true);
	SOFTWARE_LIST(config, "flop_list").set_compatible("midi_flop");
#endif
}


INPUT_PORTS_START(wk1600)
	PORT_START("kbd:FI0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("E1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("F1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("F1#")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("G1")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("G1#")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("A1")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("A1#")

	PORT_START("kbd:FI1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("B1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("C2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("C2#")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("D2")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("D2#")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("E2")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("F2")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("F2#")

	PORT_START("kbd:FI2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("G2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("G2#")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("A2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("A2#")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("B2")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("C3")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("C3#")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("D3")

	PORT_START("kbd:FI3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("D3#")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("E3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("F3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("F3#")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("G3")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("G3#")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("A3")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("A3#")

	PORT_START("kbd:FI4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("B3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("C4")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("C4#")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("D4")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("D4#")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("E4")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("F4")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("F4#")

	PORT_START("kbd:FI5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("G4")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("G4#")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("A4")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("A4#")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("B4")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("C5")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("C5#")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("D5")

	PORT_START("kbd:FI6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("D5#")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("E5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("F5")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("F5#")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("G5")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("G5#")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("A5")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("A5#")

	PORT_START("kbd:FI7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("B5")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("C6")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("C6#")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("D6")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("D6#")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("E6")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("F6")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("F6#")

	PORT_START("kbd:FI8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("G6")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("G6#")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("A6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("A6#")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("B6")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("C7")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("C7#")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("D7")

	PORT_START("kbd:FI9")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("D7#")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("E7")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("F7")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("F7#")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_NAME("G7")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("kbd:FI10")
	PORT_START("kbd:KI0")
	PORT_START("kbd:KI1")
	PORT_START("kbd:KI2")

	PORT_START("kbd:VELOCITY")
	PORT_BIT( 0xff, 0xff, IPT_POSITIONAL ) PORT_NAME("Key Velocity") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_CENTERDELTA(0) PORT_CODE_DEC(KEYCODE_PGDN) PORT_CODE_INC(KEYCODE_PGUP)

	PORT_START("KC0")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Mode")
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Intro")
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Mixer Select")
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 8 / Chord 3")
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Registration A")
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 16 / Track 6")
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Split")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad - / No") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Demo")

	PORT_START("KC1")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Record")
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Normal / Fill-In")
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 1 / Upper 1")
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 9 / Bass")
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Registration B")
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Auto Harmonize")
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Layer")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Synth")

	PORT_START("KC2")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Song")
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Variation / Fill-In")
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 2 / Upper 2")
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 10 / Rhythm")
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Registration C")
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Rhythm")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tune")

	PORT_START("KC3")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Pattern")
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Synchro / Ending")
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 3 / Lower 1")
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 11 / Track 1")
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Registration D")
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Cursor Left") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("MIDI")

	PORT_START("KC4")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("DSP")
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Start / Stop")
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 4 / Lower 2")
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 12 / Track 2")
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Registration E")
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Cursor Up") PORT_CODE(KEYCODE_UP)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad + / Yes") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KC5")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Contrast")
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tempo Down")
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 5 / Acc. Vol.")
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 13 / Track 3")
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Registration Store")
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Cursor Down") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KC6")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Free Session")
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tempo Up")
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 6 / Chord 1")
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 14 / Track 4")
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Transpose Down")
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Cursor Right") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KC7")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("One Touch Preset")
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Registration Bank")
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 7 / Chord 2")
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 15 / Track 5")
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Transpose Up")
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Touch Response")
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("SWITCH")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_ON ) PORT_NAME("Power") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(wk1600_state::power_w), 0)

	PORT_START("AN1")
	PORT_BIT( 0x3ff, 0x200, IPT_PADDLE ) PORT_NAME("Pitch Wheel") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00, 0x3ff) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)

	PORT_START("AN3")
	PORT_BIT( 0x3ff, 0x000, IPT_POSITIONAL_V ) PORT_NAME("Modulation Wheel") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00, 0x3ff) PORT_PLAYER(2) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)

	PORT_START("P6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x06, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(wk1600_state::inputs_r<1>))
	PORT_BIT( 0xf8, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(wk1600_state::inputs_r<0>))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_CONFNAME( 0x04, 0x00, "Power Source" )
	PORT_CONFSETTING(    0x00, "AC Adapter" )
	PORT_CONFSETTING(    0x04, "Battery" )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW,  IPT_OTHER  ) PORT_NAME("Pedal")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(wk1600_state::inputs_r<4>))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OUTPUT ) // reset sound, LCD, FDC
	PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_CUSTOM ) // high = WK-1800, low = WK-1600
	PORT_BIT( 0xe0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P9")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(wk1600_state::inputs_r<3>))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x38, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(wk1600_state::inputs_r<5>))

	PORT_START("PA")
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(wk1600_state::inputs_r<8>))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(wk1600_state::input_clk_w))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(wk1600_state::shift_data_w))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(wk1600_state::led_clk_w))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(wk1600_state::apo_w))

	PORT_START("PB")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(wk1600_state::lcd_w))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::e_w))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::rw_w))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::rs_w))
INPUT_PORTS_END

INPUT_PORTS_START( wk1800 )
	PORT_INCLUDE(wk1600)

	PORT_MODIFY("KC2")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tune / MIDI")

	PORT_MODIFY("KC3")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Touch Response")

	PORT_MODIFY("KC7")
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Disk")

	PORT_MODIFY("P7")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_CUSTOM ) // TODO: disk HD/DD detect

	PORT_MODIFY("P8")
	PORT_BIT( 0x10, IP_ACTIVE_LOW,  IPT_CUSTOM ) // high = WK-1800, low = WK-1600

	PORT_MODIFY("PA")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(wk1800_state::fdc_rate_w))
INPUT_PORTS_END


ROM_START(wk1800)
	ROM_REGION(0x20000, "maincpu", 0) // "Ver.1.61"
	ROM_LOAD("hd6433048sa89f.lsi9", 0x00000, 0x20000, CRC(bd5bfab3) SHA1(2731b5ab1cb288553bfee9b856264a5d1eb0ef1a))

	ROM_REGION16_LE(0x800000, "gt155", 0) // "Ver.1.60"
	ROM_LOAD("lhmn5kpn.lsi2", 0x000000, 0x400000, CRC(f75d21f0) SHA1(e08937ce2fa152db85fa96cef53f81351e690666))
	ROM_LOAD("lhmn5kpp.lsi1", 0x400000, 0x400000, CRC(f6cc5048) SHA1(9f48730a5bd3582f6fe08cb937848907d11aa804))

	ROM_REGION(585110, "screen", 0)
	ROM_LOAD("wk1800.svg", 0, 585110, CRC(5fab0b26) SHA1(6181b9eb950cd30474efb37b8cd660cba4b0b914))
ROM_END

#define rom_wk1600 rom_wk1800

} // anonymous namespace

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT         COMPANY  FULLNAME   FLAGS
SYST( 2000, wk1800,  0,      0,      wk1800,  wk1800, wk1800_state, empty_init,  "Casio", "WK-1800", MACHINE_SUPPORTS_SAVE )
SYST( 2000, wk1600,  wk1800, 0,      wk1600,  wk1600, wk1600_state, empty_init,  "Casio", "WK-1600", MACHINE_SUPPORTS_SAVE )



workabout.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/******************************************************************************

    Psion Workabout

******************************************************************************/

#include "emu.h"

//#include "bus/psion/exp/slot.h"
#include "bus/psion/sibo/slot.h"
#include "machine/nvram.h"
#include "machine/psion_asic9.h"
#include "machine/psion_ssd.h"
#include "machine/ram.h"
#include "sound/spkrdev.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class workabout_state : public driver_device
{
public:
	workabout_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_asic9(*this, "asic9")
		, m_ram(*this, "ram")
		, m_nvram(*this, "nvram")
		, m_palette(*this, "palette")
		, m_keyboard(*this, "COL%u", 0U)
		, m_speaker(*this, "speaker")
		, m_ssd(*this, "ssd%u", 1U)
		, m_sibo(*this, "sibo")
		//, m_exp(*this, "exp")
	{ }

	void workabout(machine_config &config);
	void psionwa(machine_config &config);
	void psionwamx(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(wakeup);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<psion_asic9_device> m_asic9;
	required_device<ram_device> m_ram;
	required_device<nvram_device> m_nvram;
	required_device<palette_device> m_palette;
	required_ioport_array<8> m_keyboard;
	required_device<speaker_sound_device> m_speaker;
	required_device_array<psion_ssd_device, 2> m_ssd;
	required_device<psion_sibo_slot_device> m_sibo;
	//required_device<psion_exp_slot_device> m_exp;

	void palette_init(palette_device &palette);

	uint16_t kbd_r();

	uint8_t m_key_col = 0;
};


void workabout_state::machine_start()
{
	m_nvram->set_base(m_ram->pointer(), m_ram->size());
}

void workabout_state::machine_reset()
{
}


static INPUT_PORTS_START( workabout )
	PORT_START("COL0")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)          PORT_CHAR('s')  PORT_CHAR('S')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)          PORT_CHAR('u')  PORT_CHAR('U')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q')  PORT_CHAR('Q')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('a')  PORT_CHAR('A')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/')  PORT_CHAR('?')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_CHAR('6')  PORT_CHAR('^')  PORT_CHAR('}')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))                  PORT_NAME(u8"\u2193") // U+2193 = ↓
	PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC))                   PORT_NAME("On/Esc")          PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(workabout_state::wakeup), 0)

	PORT_START("COL1")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)          PORT_CHAR('t')  PORT_CHAR('T')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)          PORT_CHAR('v')  PORT_CHAR('V')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)          PORT_CHAR('r')  PORT_CHAR('R')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)          PORT_CHAR('b')  PORT_CHAR('B')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)                                   PORT_NAME("Enter")
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-')  PORT_CHAR('_')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))                 PORT_NAME(u8"\u2192") // U+2192 = →
	PORT_BIT(0x180, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL2")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)                        PORT_NAME("Shift")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)          PORT_CHAR('w')  PORT_CHAR('W')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)          PORT_CHAR('g')  PORT_CHAR('G')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)          PORT_CHAR('c')  PORT_CHAR('C')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)          PORT_CHAR('1')  PORT_CHAR('!')                  PORT_NAME("1 ! @")
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7')  PORT_CHAR('&')  PORT_CHAR('\'')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(0x08)                                 PORT_NAME("Del")
	PORT_BIT(0x180, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL3")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT)       PORT_CHAR(UCHAR_SHIFT_2)                        PORT_NAME("Psion")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)          PORT_CHAR('x')  PORT_CHAR('X')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)          PORT_CHAR('h')  PORT_CHAR('H')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)          PORT_CHAR('d')  PORT_CHAR('D')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)          PORT_CHAR('2')  PORT_CHAR('"')  PORT_CHAR('#')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8')  PORT_CHAR('(')  PORT_CHAR('[')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)        PORT_CHAR(0x09)                                 PORT_NAME("Tab")
	PORT_BIT(0x180, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL4")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y')  PORT_CHAR('Y')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)          PORT_CHAR('m')  PORT_CHAR('M')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)          PORT_CHAR('i')  PORT_CHAR('I')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)          PORT_CHAR('e')  PORT_CHAR('E')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)          PORT_CHAR('3')  PORT_CHAR(0xa3) PORT_CHAR('\\')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_CHAR('9')  PORT_CHAR(')')  PORT_CHAR(']')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))                    PORT_NAME(u8"\u2191") // U+2191 = ↑
	PORT_BIT(0x180, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL5")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z')  PORT_CHAR('Z')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)          PORT_CHAR('n')  PORT_CHAR('N')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)          PORT_CHAR('j')  PORT_CHAR('J')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)          PORT_CHAR('f')  PORT_CHAR('F')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('+')  PORT_CHAR('=')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR('*')  PORT_CHAR(':')
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11)        PORT_CHAR(UCHAR_MAMEKEY(F11))                   PORT_NAME("Menu")
	PORT_BIT(0x180, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL6")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))              PORT_NAME("Control")
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)          PORT_CHAR('o')  PORT_CHAR('O')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)          PORT_CHAR('k')  PORT_CHAR('K')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_CHAR('0')  PORT_CHAR(';')  PORT_CHAR('<')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)          PORT_CHAR('4')  PORT_CHAR('$')  PORT_CHAR('~')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_END)                                                        PORT_NAME("Off")
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                                                               PORT_NAME("Contrast")
	PORT_BIT(0x180, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("COL7")
	PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)          PORT_CHAR('p')  PORT_CHAR('P')
	PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)          PORT_CHAR('l')  PORT_CHAR('L')
	PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')  PORT_CHAR(',')
	PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)          PORT_CHAR('5')  PORT_CHAR('%')  PORT_CHAR('{')
	PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))                  PORT_NAME(u8"\u2190") // U+2190 = ←
	PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD)                                                                               PORT_NAME("Backlight")
	PORT_BIT(0x180, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END


INPUT_CHANGED_MEMBER(workabout_state::wakeup)
{
	m_asic9->eint0_w(newval);
}


uint16_t workabout_state::kbd_r()
{
	uint16_t data = 0x00;

	for (int i = 0; i < 8; i++)
	{
		if (BIT(m_key_col, i))
			data |= m_keyboard[i]->read();
	}

	return data;
}


void workabout_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(190, 220, 190));

	for (int i = 1; i < 3; i++)
	{
		const int r = (0x99 * i) / 2;
		const int g = (0xaa * i) / 2;
		const int b = (0x88 * i) / 2;
		m_palette->set_pen_color(i, rgb_t(r, g, b));
	}
}


void workabout_state::workabout(machine_config &config)
{
	PSION_ASIC9(config, m_asic9, 7.68_MHz_XTAL); // V30H
	m_asic9->set_screen("screen");
	m_asic9->set_ram_rom("ram", "rom");
	m_asic9->port_ab_r().set(FUNC(workabout_state::kbd_r));
	m_asic9->buz_cb().set(m_speaker, FUNC(speaker_sound_device::level_w));
	m_asic9->col_cb().set([this](uint8_t data) { m_key_col = data; });
	m_asic9->data_r<0>().set(m_ssd[1], FUNC(psion_ssd_device::data_r));      // SSD Pack 1
	m_asic9->data_w<0>().set(m_ssd[1], FUNC(psion_ssd_device::data_w));
	m_asic9->data_r<1>().set(m_ssd[0], FUNC(psion_ssd_device::data_r));      // SSD Pack 2
	m_asic9->data_w<1>().set(m_ssd[0], FUNC(psion_ssd_device::data_w));
	//m_asic9->data_r<2>().set(m_exp[0], FUNC(psion_exp_slot_device::data_r)); // Expansion port A
	//m_asic9->data_w<2>().set(m_exp[0], FUNC(psion_exp_slot_device::data_w));
	//m_asic9->data_r<3>().set(m_exp[1], FUNC(psion_exp_slot_device::data_r)); // Expansion port B
	//m_asic9->data_w<3>().set(m_exp[1], FUNC(psion_exp_slot_device::data_w));
	m_asic9->data_r<4>().set(m_sibo, FUNC(psion_sibo_slot_device::data_r));  // Expansion port C
	m_asic9->data_w<4>().set(m_sibo, FUNC(psion_sibo_slot_device::data_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_size(240, 100);
	screen.set_visarea_full();
	screen.set_refresh_hz(66);
	screen.set_screen_update(m_asic9, FUNC(psion_asic9_device::screen_update));
	screen.set_palette(m_palette);
	PALETTE(config, "palette", FUNC(workabout_state::palette_init), 3);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00); // Piezo buzzer

	RAM(config, m_ram);
	NVRAM(config, "nvram", nvram_device::DEFAULT_NONE);

	PSION_SSD(config, m_ssd[0]);
	m_ssd[0]->door_cb().set(m_asic9, FUNC(psion_asic9_device::medchng_w));
	PSION_SSD(config, m_ssd[1]);
	m_ssd[1]->door_cb().set(m_asic9, FUNC(psion_asic9_device::medchng_w));

	// LIF-PFS socket (with LIF converter)
	PSION_SIBO_SLOT(config, m_sibo, psion_sibo_devices, nullptr);
	m_sibo->int_cb().set(m_asic9, FUNC(psion_asic9_device::sds_int_w));

	//PSION_EXP_SLOT(config, m_exp, psion_exp_devices, nullptr);

	SOFTWARE_LIST(config, "ssd_list").set_original("psion_ssd").set_filter("WA");
	//SOFTWARE_LIST(config, "flop_list").set_original("psion_flop").set_filter("WA");
}


void workabout_state::psionwa(machine_config &config)
{
	workabout(config);

	m_ram->set_default_size("1M");
}

void workabout_state::psionwamx(machine_config &config)
{
	workabout(config);

	PSION_ASIC9MX(config.replace(), m_asic9, 3.6864_MHz_XTAL * 15 / 2); // V30MX
	m_asic9->set_screen("screen");
	m_asic9->set_ram_rom("ram", "rom");
	m_asic9->port_ab_r().set(FUNC(workabout_state::kbd_r));
	m_asic9->buz_cb().set(m_speaker, FUNC(speaker_sound_device::level_w));
	m_asic9->col_cb().set([this](uint8_t data) { m_key_col = data; });
	m_asic9->data_r<0>().set(m_ssd[1], FUNC(psion_ssd_device::data_r));      // SSD Pack 1
	m_asic9->data_w<0>().set(m_ssd[1], FUNC(psion_ssd_device::data_w));
	m_asic9->data_r<1>().set(m_ssd[0], FUNC(psion_ssd_device::data_r));      // SSD Pack 2
	m_asic9->data_w<1>().set(m_ssd[0], FUNC(psion_ssd_device::data_w));
	//m_asic9->data_r<2>().set(m_exp[0], FUNC(psion_exp_slot_device::data_r)); // Expansion port A
	//m_asic9->data_w<2>().set(m_exp[0], FUNC(psion_exp_slot_device::data_w));
	//m_asic9->data_r<3>().set(m_exp[1], FUNC(psion_exp_slot_device::data_r)); // Expansion port B
	//m_asic9->data_w<3>().set(m_exp[1], FUNC(psion_exp_slot_device::data_w));
	m_asic9->data_r<4>().set(m_sibo, FUNC(psion_sibo_slot_device::data_r));  // Expansion port C
	m_asic9->data_w<4>().set(m_sibo, FUNC(psion_sibo_slot_device::data_w));

	m_ram->set_default_size("2M");
}


ROM_START(psionwa)
	ROM_REGION16_LE(0x200000, "rom", 0)
	ROM_SYSTEM_BIOS(0, "240f", "V2.40F 060897")
	ROMX_LOAD("w1_v2.40f.bin", 0x00000, 0x200000, CRC(4ef1d380) SHA1(d155edf7995c2a799525b53079fff9fb68789f0f), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "100f", "V1.00F 010195")
	ROMX_LOAD("w1_v1.00f.bin", 0x00000, 0x100000, CRC(8fd7c127) SHA1(316fdc6c54629470f1af3295c46a2e7d58ebdca9), ROM_BIOS(1))
	ROM_RELOAD(0x100000, 0x100000)
	ROM_SYSTEM_BIOS(2, "024b", "V0.24B 120296")
	ROMX_LOAD("w1_v0.24b.bin", 0x00000, 0x200000, CRC(6e7e3016) SHA1(5c35eea431f975cdefc770697a88fe62a1e7af7f), ROM_BIOS(2))
ROM_END

ROM_START(psionwamx)
	ROM_REGION16_LE(0x200000, "rom", 0)
	ROM_SYSTEM_BIOS(0, "720f", "V7.20F 230798")
	ROMX_LOAD("w2mx_v7.20f.bin", 0x00000, 0x200000, CRC(63734683) SHA1(9d8aa1e45f52e7fcb52d6e81ac47f60d1104c35d), ROM_BIOS(0))
ROM_END

} // anonymous namespace


//    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT      CLASS            INIT        COMPANY   FULLNAME        FLAGS
COMP( 1995, psionwa,   0,      0,      psionwa,   workabout, workabout_state, empty_init, "Psion",  "Workabout",    MACHINE_SUPPORTS_SAVE )
COMP( 1998, psionwamx, 0,      0,      psionwamx, workabout, workabout_state, empty_init, "Psion",  "Workabout mx", MACHINE_SUPPORTS_SAVE )



wrinkles.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:David Viens
/*******************************************************************************

Talking Wrinkles (model 6006), a dog hand puppet

Published by Lakeside (a Coleco subsidiary at that time, after Coleco purchased
Leisure Dynamics in 1985). Programming by Stephen Beck. The speech technology is
by Electronic Speech Systems. The plushie itself is licensed from Ganz Bros.

Hardware notes:

PCB 1:
- PCB label: REV 4.1 DIGITAL, 201239C, (C) COLECO 1986
- P80C31BH, 11MHz XTAL
- 32KB EPROM
- cartridge slot (no known cartridges were released)

PCB 2:
- PCB label: ANALOG REV 6.2, 201238D, (C) COLECO 1986
- button, motion sensor, microphone

Known sensors:
- 0x02: bellybutton, literally a button
- 0x04: detect violent motion (drop Wrinkles and he will cry)
- 0x10: detect light motion
- 0x40: detect open mouth (use as handpuppet to make it 'talk')
- 0x80: detect magnet in mouth (the toy came with a 'bone' that has a magnet in it)

TODO:
- where is the microphone? or are they the same inputs as the motion sensors?
- power-on by pressing button

*******************************************************************************/

#include "emu.h"

#include "cpu/mcs51/mcs51.h"
#include "sound/dac.h"

#include "speaker.h"


namespace {

class wrinkles_state : public driver_device
{
public:
	wrinkles_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu")
	{ }

	void wrinkles(machine_config &config);

private:
	required_device<mcs51_cpu_device> m_maincpu;

	void main_map(address_map &map) ATTR_COLD;
};



/*******************************************************************************
    Address Maps
*******************************************************************************/

void wrinkles_state::main_map(address_map &map)
{
	map.global_mask(0x7fff);
	map(0x0000, 0x7fff).rom();
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( wrinkles )
	PORT_START("INPUTS")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_CODE(KEYCODE_1) PORT_NAME("Tickle Button")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_5) PORT_NAME("Impact Sensor")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_4) PORT_NAME("Motion Sensor")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_OTHER ) PORT_CODE(KEYCODE_2) PORT_NAME("Mouth Open")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER ) PORT_CODE(KEYCODE_3) PORT_NAME("Mouth Magnet")
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void wrinkles_state::wrinkles(machine_config &config)
{
	// basic machine hardware
	I80C31(config, m_maincpu, 11_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &wrinkles_state::main_map);
	m_maincpu->port_in_cb<1>().set_ioport("INPUTS");
	m_maincpu->port_out_cb<3>().set("dac", FUNC(dac_8bit_r2r_device::write));

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_8BIT_R2R(config, "dac").add_route(ALL_OUTPUTS, "speaker", 0.5);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( wrinkles )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD("umua117_wrkl_dif4.u3", 0x0000, 0x8000, CRC(4ec8ddbf) SHA1(beb165d933659859a4f966168ca121843cd6642b) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME       PARENT  COMPAT  MACHINE   INPUT     CLASS           INIT        COMPANY, FULLNAME, FLAGS
SYST( 1986, wrinkles,  0,      0,      wrinkles, wrinkles, wrinkles_state, empty_init, "Lakeside / Coleco / Ganz Bros", "Talking Wrinkles", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_CONTROLS | MACHINE_NOT_WORKING )



wswan.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Anthony Kruize,Wilbert Pol
/***************************************************************************

  wswan.cpp

  Driver file to handle emulation of the Bandai WonderSwan
  By:

  Anthony Kruize
  Wilbert Pol

  Based on the WStech documentation by Judge and Dox.

  These systems were developed by Koto Laboratory

  Usage:
    Keep START button pressed during startup (or reset) to enter the internal
    configuration menu.

  Known issues/TODOs:
  - Perform video DMA at proper timing.
  - Add (real/proper) RTC support.
  - Fix wonderwitch
    - Make the flash rom changes save.

***************************************************************************/

#include "emu.h"
#include "wswan_v.h"

#include "wswansound.h"

#include "cpu/v30mz/v30mz.h"
#include "machine/nvram.h"
#include "bus/wswan/slot.h"
#include "bus/wswan/rom.h"
#include "render.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include "wswan.lh"

#include <algorithm>

#define LOG_UNKNOWN (1 << 1)
#define LOG_EEPROM  (1 << 2)
#define LOG_DMA     (1 << 3)

#define LOG_ALL     (LOG_UNKNOWN | LOG_EEPROM | LOG_DMA)

#define VERBOSE     (0)

#include "logmacro.h"

#define LOGUNKNOWN(...) LOGMASKED(LOG_UNKNOWN, __VA_ARGS__)
#define LOGEEPROM(...)  LOGMASKED(LOG_EEPROM, __VA_ARGS__)
#define LOGDMA(...)     LOGMASKED(LOG_DMA, __VA_ARGS__)

namespace {

class wswan_state : public driver_device
{
public:
	wswan_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_vdp(*this, "vdp"),
		m_sound(*this, "custom"),
		m_cart(*this, "cartslot"),
		m_region_maincpu(*this, "maincpu"),
		m_cursx(*this, "CURSX"),
		m_cursy(*this, "CURSY"),
		m_buttons(*this, "BUTTONS"),
		m_sound_output(*this, "SOUND_OUTPUT"),
		m_icons(*this, "icon%u", 0U)
	{ }

	void wswan(machine_config &config);
	void pockchv2(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(sound_output_changed);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void wswan_base(machine_config &config);

	// Interrupt flags
	static constexpr u8 WSWAN_IFLAG_STX    = 0x01;
	static constexpr u8 WSWAN_IFLAG_KEY    = 0x02;
	static constexpr u8 WSWAN_IFLAG_RTC    = 0x04;
	static constexpr u8 WSWAN_IFLAG_SRX    = 0x08;
	static constexpr u8 WSWAN_IFLAG_LCMP   = 0x10;
	static constexpr u8 WSWAN_IFLAG_VBLTMR = 0x20;
	static constexpr u8 WSWAN_IFLAG_VBL    = 0x40;
	static constexpr u8 WSWAN_IFLAG_HBLTMR = 0x80;

	// Interrupts
	static constexpr u8 WSWAN_INT_STX    = 0;
	static constexpr u8 WSWAN_INT_KEY    = 1;
	static constexpr u8 WSWAN_INT_RTC    = 2;
	static constexpr u8 WSWAN_INT_SRX    = 3;
	static constexpr u8 WSWAN_INT_LCMP   = 4;
	static constexpr u8 WSWAN_INT_VBLTMR = 5;
	static constexpr u8 WSWAN_INT_VBL    = 6;
	static constexpr u8 WSWAN_INT_HBLTMR = 7;

	static constexpr u32 INTERNAL_EEPROM_SIZE = 1024;   // 16kbit on WSC
	static constexpr u32 INTERNAL_EEPROM_SIZE_WS = 64;  // 1kbit on WS

	// Labeled 12.3FXA on wonderswan color pcb
	static constexpr XTAL X1 = 12.288_MHz_XTAL;

	enum enum_system { TYPE_WSWAN = 0, TYPE_WSC };

	required_device<v30mz_cpu_device> m_maincpu;
	required_device<wswan_video_device> m_vdp;
	required_device<wswan_sound_device> m_sound;
	required_device<ws_cart_slot_device> m_cart;

	required_memory_region m_region_maincpu;
	required_ioport m_cursx;
	required_ioport m_cursy;
	required_ioport m_buttons;
	required_ioport m_sound_output;
	output_finder<6> m_icons;

	u16 m_ws_portram[128] = { };
	u8 m_internal_eeprom[INTERNAL_EEPROM_SIZE * 2] = { };
	u8 m_system_type = 0;
	bool m_bios_disabled = false;
	u8 m_rotate = 0;
	u32 m_vector = 0;
	u8 m_sys_control = 0;
	u8 m_irq_vector_base = 0;
	u8 m_serial_data = 0;
	u8 m_serial_control = 0;
	u8 m_irq_enable = 0;
	u8 m_irq_active = 0;
	u16 m_internal_eeprom_data = 0;
	u16 m_internal_eeprom_address = 0;
	u8 m_internal_eeprom_command = 0;
	u8 m_keypad = 0;

	u16 bios_r(offs_t offset, u16 mem_mask);
	u16 port_r(offs_t offset, u16 mem_mask);
	void port_w(offs_t offset, u16 data, u16 mem_mask);

	void set_irq_line(int irq);
	void common_start();

	void handle_irqs();
	void clear_irq_line(int irq);
	virtual u16 get_internal_eeprom_address();
	u32 get_vector() { return m_vector; }
	void set_icons(u8 data);
	void set_rotate_view();

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
	void snd_map(address_map &map) ATTR_COLD;
};


class wscolor_state : public wswan_state
{
public:
	wscolor_state(const machine_config &mconfig, device_type type, const char *tag) :
		wswan_state(mconfig, type, tag),
		m_dma_view(*this, "dma_view"),
		m_hypervoice_view(*this, "hypervoice_view")
	{ }

	void wscolor(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	virtual u16 get_internal_eeprom_address() override;

private:
	static constexpr u8 SOUND_DMA_DIV[4] = { 6, 4, 2, 1 };

	memory_view m_dma_view;
	memory_view m_hypervoice_view;

	struct sound_dma_t
	{
		emu_timer *timer = nullptr;  // Timer
		u32       source = 0;        // Source address
		u32       source_reload = 0; // Source address, Value for reload
		u32       size = 0;          // Size
		u32       size_reload = 0;   // Size, Value for reload
		u8        control = 0;       // Control
	};
	sound_dma_t m_sound_dma;
	u16 m_dma_source_offset = 0;
	u16 m_dma_source_segment = 0;
	u16 m_dma_destination = 0;
	u16 m_dma_length = 0;
	u16 m_dma_control = 0;

	u16 dma_r(offs_t offset, u16 mem_mask);
	void dma_w(offs_t offset, u16 data, u16 mem_mask);
	void color_mode_view_w(int state);

	TIMER_CALLBACK_MEMBER(sound_dma_cb);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;
};


void wswan_state::mem_map(address_map &map)
{
	map(0x00000, 0x03fff).rw(m_vdp, FUNC(wswan_video_device::vram_r), FUNC(wswan_video_device::vram_w));       // 16kb RAM / 4 colour tiles
	map(0x04000, 0x0ffff).noprw();       // nothing
	map(0xf0000, 0xfffff).r(FUNC(wswan_state::bios_r));
}


void wscolor_state::mem_map(address_map &map)
{
	map(0x00000, 0x0ffff).rw(m_vdp, FUNC(wswan_video_device::vram_r), FUNC(wswan_video_device::vram_w));       // 16/64kb RAM / 4 colour tiles, 16 colour tiles + palettes
	map(0xf0000, 0xfffff).r(FUNC(wscolor_state::bios_r));
}


void wswan_state::io_map(address_map &map)
{
	map(0x00, 0xff).rw(FUNC(wswan_state::port_r), FUNC(wswan_state::port_w));   // I/O ports
}


void wscolor_state::io_map(address_map &map)
{
	map(0x00, 0xff).rw(FUNC(wscolor_state::port_r), FUNC(wscolor_state::port_w));   // I/O ports
	map(0x40, 0x53).view(m_dma_view);
	m_dma_view[0](0x40, 0x53).rw(FUNC(wscolor_state::dma_r), FUNC(wscolor_state::dma_w));
	map(0x64, 0x6b).view(m_hypervoice_view);
	m_hypervoice_view[0](0x64, 0x6b).rw(m_sound, FUNC(wswan_sound_device::hypervoice_r), FUNC(wswan_sound_device::hypervoice_w));
}


void wswan_state::snd_map(address_map &map)
{
	map(0x00000, 0x03fff).r(m_vdp, FUNC(wswan_video_device::vram_r));
}


static INPUT_PORTS_START(wswan)
	PORT_START("CURSX")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_NAME("X4 - Left")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_NAME("X3 - Down")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_NAME("X2 - Right")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_NAME("X1 - Up")

	PORT_START("BUTTONS")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Button B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Button A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START ) PORT_NAME("Start")

	PORT_START("CURSY")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Y4 - Left") PORT_CODE(KEYCODE_A)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Y3 - Down") PORT_CODE(KEYCODE_S)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Y2 - Right") PORT_CODE(KEYCODE_D)
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Y1 - Up") PORT_CODE(KEYCODE_W)

	PORT_START("SOUND_OUTPUT")
	PORT_CONFNAME(    0x01, 0x01, "Sound output select" ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(wswan_state::sound_output_changed), 0)
	PORT_CONFSETTING( 0x00, "Internal speaker (Mono)" )
	PORT_CONFSETTING( 0x01, "External headphone (Stereo)" )
INPUT_PORTS_END


static void wswan_cart(device_slot_interface &device)
{
	device.option_add_internal("ws_rom",     WS_ROM_STD);
	device.option_add_internal("ws_sram",    WS_ROM_SRAM);
	device.option_add_internal("ws_eeprom",  WS_ROM_EEPROM);
	device.option_add_internal("wwitch",     WS_ROM_WWITCH);
}


void wswan_state::wswan_base(machine_config &config)
{
	// Basic machine hardware
	V30MZ(config, m_maincpu, X1 / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &wswan_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &wswan_state::io_map);
	m_maincpu->vector_cb().set(FUNC(wswan_state::get_vector));

	WSWAN_VIDEO(config, m_vdp, X1 / 4);
	m_vdp->set_screen("screen");
	m_vdp->set_irq_callback(FUNC(wswan_state::set_irq_line));
	m_vdp->icons_cb().set(FUNC(wswan_state::set_icons));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_screen_update("vdp", FUNC(wswan_video_device::screen_update));
	screen.set_raw(X1 / 4, 256, 0, wswan_video_device::WSWAN_X_PIXELS, 159, 0, wswan_video_device::WSWAN_Y_PIXELS);
	screen.set_palette("vdp");

	config.set_default_layout(layout_wswan);

	config.set_maximum_quantum(attotime::from_hz(60));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	// sound hardware
	SPEAKER(config, "speaker", 2).front();
	WSWAN_SND(config, m_sound, X1 / 4);
	m_sound->set_addrmap(0, &wswan_state::snd_map);
	m_sound->add_route(0, "speaker", 0.50, 0);
	m_sound->add_route(1, "speaker", 0.50, 1);

	// cartridge
	WS_CART_SLOT(config, m_cart, X1 / 32, wswan_cart, nullptr);


}

void wswan_state::wswan(machine_config &config)
{
	wswan_base(config);

	// software lists
	SOFTWARE_LIST(config, "cart_list").set_original("wswan");
	SOFTWARE_LIST(config, "wsc_list").set_compatible("wscolor");
}

// while the pc2 software is compatible with a Wonderswan, the physical cartridges are not
void wswan_state::pockchv2(machine_config &config)
{
	wswan_base(config);

	// software lists
	SOFTWARE_LIST(config, "pc2_list").set_original("pockchalv2");
}

void wscolor_state::wscolor(machine_config &config)
{
	wswan(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &wscolor_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &wscolor_state::io_map);

	WSWAN_COLOR_VIDEO(config.replace(), m_vdp, X1 / 4);
	m_vdp->set_screen("screen");
	m_vdp->set_irq_callback(FUNC(wscolor_state::set_irq_line));
	m_vdp->icons_cb().set(FUNC(wscolor_state::set_icons));
	m_vdp->color_mode_cb().set(FUNC(wscolor_state::color_mode_view_w));

	// software lists
	config.device_remove("wsc_list");
	SOFTWARE_LIST(config.replace(), "cart_list").set_original("wscolor");
	SOFTWARE_LIST(config, "ws_list").set_compatible("wswan");

	m_cart->set_must_be_loaded(true);
}

INPUT_CHANGED_MEMBER(wswan_state::sound_output_changed)
{
	m_sound->set_headphone_connected(BIT(m_sound_output->read(), 0));
}

void wswan_state::handle_irqs()
{
	bool set_irq_line = false;
	if (m_irq_enable & m_irq_active & WSWAN_IFLAG_HBLTMR)
	{
		m_vector = m_irq_vector_base + WSWAN_INT_HBLTMR;
		set_irq_line = true;
	}
	else if (m_irq_enable & m_irq_active & WSWAN_IFLAG_VBL)
	{
		m_vector = m_irq_vector_base + WSWAN_INT_VBL;
		set_irq_line = true;
	}
	else if (m_irq_enable & m_irq_active & WSWAN_IFLAG_VBLTMR)
	{
		m_vector = m_irq_vector_base + WSWAN_INT_VBLTMR;
		set_irq_line = true;
	}
	else if (m_irq_enable & m_irq_active & WSWAN_IFLAG_LCMP)
	{
		m_vector = m_irq_vector_base + WSWAN_INT_LCMP;
		set_irq_line = true;
	}
	else if (m_irq_enable & m_irq_active & WSWAN_IFLAG_SRX)
	{
		m_vector = m_irq_vector_base + WSWAN_INT_SRX;
		set_irq_line = true;
	}
	else if (m_irq_enable & m_irq_active & WSWAN_IFLAG_RTC)
	{
		m_vector = m_irq_vector_base + WSWAN_INT_RTC;
		set_irq_line = true;
	}
	else if (m_irq_enable & m_irq_active & WSWAN_IFLAG_KEY)
	{
		m_vector = m_irq_vector_base + WSWAN_INT_KEY;
		set_irq_line = true;
	}
	else if (m_irq_enable & m_irq_active & WSWAN_IFLAG_STX)
	{
		m_vector = m_irq_vector_base + WSWAN_INT_STX;
		set_irq_line = true;
	}
	m_maincpu->set_input_line(0, set_irq_line ? ASSERT_LINE : CLEAR_LINE);
}


void wswan_state::set_irq_line(int irq)
{
	if (m_irq_enable & irq)
	{
		m_irq_active |= irq;
		handle_irqs();
	}
}


TIMER_CALLBACK_MEMBER(wscolor_state::sound_dma_cb)
{
	if (BIT(m_sound_dma.control, 7))
	{
		if (BIT(m_sound_dma.control, 2))
		{
			// Sound DMA hold
			if (BIT(m_sound_dma.control, 4))
				m_sound->hypervoice_dma_w(0);
			else
				port_w(0x88 / 2, 0 << 8, 0xff00);
		}
		else
		{
			address_space &space = m_maincpu->space(AS_PROGRAM);
			/* TODO: Output sound DMA byte */
			if (BIT(m_sound_dma.control, 4))
				m_sound->hypervoice_dma_w(space.read_byte(m_sound_dma.source));
			else
				port_w(0x88 / 2, space.read_byte(m_sound_dma.source) << 8, 0xff00);
			m_sound_dma.size--;
			m_sound_dma.source = (m_sound_dma.source + (BIT(m_sound_dma.control, 6) ? -1 : 1)) & 0x0fffff;
			if (m_sound_dma.size == 0)
			{
				if (BIT(m_sound_dma.control, 3))
				{
					m_sound_dma.source = m_sound_dma.source_reload;
					m_sound_dma.size = m_sound_dma.size_reload;
				}
				else
				{
					m_sound_dma.control &= 0x7f;
					m_sound_dma.timer->adjust(attotime::never);
					return;
				}
			}
		}
		m_sound_dma.timer->adjust(attotime::from_ticks(SOUND_DMA_DIV[m_sound_dma.control & 3], X1 / 512));
	}
}


void wswan_state::clear_irq_line(int irq)
{
	m_irq_active &= ~irq;
	handle_irqs();
}


void wswan_state::common_start()
{
	if (m_cart->exists())
		m_cart->save_nvram();

	m_icons.resolve();

	if (m_cart->exists())
	{
		// ROM
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x20000, 0x2ffff, read16s_delegate(*m_cart, FUNC(ws_cart_slot_device::read_rom20)));
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x30000, 0x3ffff, read16s_delegate(*m_cart, FUNC(ws_cart_slot_device::read_rom30)));
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x40000, 0xeffff, read16s_delegate(*m_cart, FUNC(ws_cart_slot_device::read_rom40)));

		// SRAM
		if (m_cart->get_type() == WS_SRAM || m_cart->get_type() == WWITCH)
		{
			m_maincpu->space(AS_PROGRAM).install_read_handler(0x10000, 0x1ffff, read16s_delegate(*m_cart, FUNC(ws_cart_slot_device::read_ram)));
			m_maincpu->space(AS_PROGRAM).install_write_handler(0x10000, 0x1ffff, write16s_delegate(*m_cart, FUNC(ws_cart_slot_device::write_ram)));
		}
	}

	save_item(NAME(m_ws_portram));
	save_item(NAME(m_internal_eeprom));
	save_item(NAME(m_bios_disabled));
	save_item(NAME(m_rotate));

	save_item(NAME(m_vector));

	save_item(NAME(m_sys_control));
	save_item(NAME(m_irq_vector_base));
	save_item(NAME(m_serial_data));
	save_item(NAME(m_serial_control));
	save_item(NAME(m_irq_enable));
	save_item(NAME(m_irq_active));
	save_item(NAME(m_internal_eeprom_data));
	save_item(NAME(m_internal_eeprom_address));
	save_item(NAME(m_internal_eeprom_command));
	save_item(NAME(m_keypad));
}


void wswan_state::machine_start()
{
	common_start();
	subdevice<nvram_device>("nvram")->set_base(m_internal_eeprom, INTERNAL_EEPROM_SIZE_WS * 2);
	m_system_type = TYPE_WSWAN;
}


void wscolor_state::machine_start()
{
	common_start();
	subdevice<nvram_device>("nvram")->set_base(m_internal_eeprom, INTERNAL_EEPROM_SIZE * 2);
	m_system_type = TYPE_WSC;

	m_sound_dma.timer = timer_alloc(FUNC(wscolor_state::sound_dma_cb), this);
	save_item(NAME(m_sound_dma.source));
	save_item(NAME(m_sound_dma.source_reload));
	save_item(NAME(m_sound_dma.size));
	save_item(NAME(m_sound_dma.size_reload));
	save_item(NAME(m_sound_dma.control));

	save_item(NAME(m_dma_source_offset));
	save_item(NAME(m_dma_source_segment));
	save_item(NAME(m_dma_destination));
	save_item(NAME(m_dma_length));
	save_item(NAME(m_dma_control));
}


void wswan_state::machine_reset()
{
	m_sound->set_headphone_connected(BIT(m_sound_output->read(), 0));
	m_bios_disabled = false;

	m_rotate = 0;

	m_vector = 0;
	m_irq_vector_base = 0;
	m_serial_control = 0;
	m_irq_enable = 0;
	m_irq_active = 0;
	m_internal_eeprom_data = 0;
	m_internal_eeprom_address = 0;
	m_internal_eeprom_command = 0;
	m_sys_control = (m_system_type == TYPE_WSC) ? 2 : 0;

	/* Intialize ports */
	std::fill(std::begin(m_ws_portram), std::end(m_ws_portram), 0);

	set_rotate_view();
}

void wscolor_state::machine_reset()
{
	wswan_state::machine_reset();

	m_dma_view.disable();
	m_hypervoice_view.disable();

	/* Initialize sound DMA */
	m_sound_dma.timer->adjust(attotime::never);
	m_sound_dma.source = m_sound_dma.source_reload = 0;
	m_sound_dma.size = m_sound_dma.size_reload = 0;
	m_sound_dma.control = 0;
}


void wscolor_state::color_mode_view_w(int state)
{
	if (state)
	{
		m_dma_view.select(0);
		m_hypervoice_view.select(0);
	}
	else
	{
		m_dma_view.disable();
		m_hypervoice_view.disable();
	}
}


u16 wswan_state::bios_r(offs_t offset, u16 mem_mask)
{
	if (!m_bios_disabled)
		return m_region_maincpu->as_u16(offset & ((m_region_maincpu->bytes() >> 1) - 1));
	else
		return m_cart->read_rom40(offset + (0xb0000 >> 1), mem_mask);
}


u16 wswan_state::port_r(offs_t offset, u16 mem_mask)
{
	u16 value = m_ws_portram[offset];

	if (offset < 0x40 / 2 || (offset > 0xa0 / 2 && offset < 0xb0 / 2))
	{
		return m_vdp->reg_r(offset, mem_mask);
	}
	if (offset >= 0x80 / 2 && offset <= 0x9f / 2)
	{
		return m_sound->port_r(offset, mem_mask);
	}

	switch (offset)
	{
		case 0x60 / 2:
			return m_vdp->reg_r(offset, mem_mask);
		case 0xa0 / 2:
			// Hardware type
			// Bit 0 - Disable/enable BIOS
			// Bit 1 - Determine monochrome/color
			// Bit 2 - Unknown, used to determine color/crystal
			// Bit 3 - Unknown
			// Bit 7 - Checked during start up, expects bit 7 set (part of cart unlock sequence?)
			return m_sys_control | 0x80;
		case 0xb0 / 2:
			return m_irq_vector_base | (m_serial_data << 8);
		case 0xb2 / 2:
			return m_irq_enable | (m_serial_control << 8);
		case 0xb4 / 2:
		  // Read controls
			// Bit 8-11  - Current state of input lines (read-only)
			// Bit 12-14 - Select line of inputs to read
			//       001 - Read Y cursors
			//       010 - Read X cursors
			//       100 - Read START,A,B buttons
			// Bit 15    - Unknown
			value = (m_keypad << 8) & 0xf0ff;
			switch (m_keypad & 0x70)
			{
			case 0x10:  // Read Y cursors: Y1 - Y2 - Y3 - Y4
				{
					u8 const input = m_cursy->read();
					if (m_rotate) // reorient controls if the console is rotated
					{
						if (BIT(input, 0)) value |= 0x0200;
						if (BIT(input, 1)) value |= 0x0400;
						if (BIT(input, 2)) value |= 0x0800;
						if (BIT(input, 3)) value |= 0x0100;
					}
					else
						value = value | (input << 8);
				}
				break;
			case 0x20:  // Read X cursors: X1 - X2 - X3 - X4
				{
					u8 const input = m_cursx->read();
					if (m_rotate) // reorient controls if the console is rotated
					{
						if (BIT(input, 0)) value |= 0x0200;
						if (BIT(input, 1)) value |= 0x0400;
						if (BIT(input, 2)) value |= 0x0800;
						if (BIT(input, 3)) value |= 0x0100;
					}
					else
						value = value | (input << 8);
				}
				break;
			case 0x40:  // Read buttons: START - A - B
				value = value | (m_buttons->read() << 8);
				break;
			}
			return value;
		case 0xb6 / 2:
			return m_irq_active;
		case 0xba / 2:
			return m_internal_eeprom_data;
		case 0xbc / 2:
			return m_internal_eeprom_address;
		case 0xbe / 2:
			return m_internal_eeprom_command;
		case 0xc0 / 2:
		case 0xc2 / 2:
		case 0xc4 / 2:  // Cartridge EEPROM data
		case 0xc6 / 2:
		case 0xc8 / 2:
		case 0xca / 2:  // RTC command & data
		case 0xcc / 2:
		case 0xce / 2:
			return m_cart->read_io(offset, mem_mask);
		default:
			if (!machine().side_effects_disabled())
				LOGUNKNOWN("%s: Read from unsupported port: %02x & %04x", machine().describe_context(), offset << 1, mem_mask);
			break;
	}

	return value;
}


void wswan_state::port_w(offs_t offset, u16 data, u16 mem_mask)
{
	if (offset < 0x40 / 2 || (offset > 0xa0 / 2 && offset < 0xb0 / 2))
	{
		m_vdp->reg_w(offset, data, mem_mask);
		return;
	}

	switch (offset)
	{
		case 0x60 / 2:
			m_vdp->reg_w(offset, data, mem_mask);
			break;
		case 0x80 / 2:  // Audio 1 freq
		case 0x82 / 2:  // Audio 2 freq
		case 0x84 / 2:  // Audio 3 freq
		case 0x86 / 2:  // Audio 4 freq
		case 0x88 / 2:
			// Audio 1 volume
			// Bit 0-3 - Right volume audio channel 1
			// Bit 4-7 - Left volume audio channel 1
			// Audio 2 volume
			// Bit 8-11  - Right volume audio channel 2
			// Bit 12-15 - Left volume audio channel 2
		case 0x8a / 2:
			// Audio 3 volume
			// Bit 0-3 - Right volume audio channel 3
			// Bit 4-7 - Left volume audio channel 3
			// Audio 4 volume
			// Bit 8-11  - Right volume audio channel 4
			// Bit 12-15 - Left volume audio channel 4
		case 0x8c / 2:  // Sweep step / sweep time
		case 0x8e / 2:
			// Noise control
			// Bit 0-2 - Noise generator type
			// Bit 3   - Reset
			// Bit 4   - Enable
			// Bit 5-7 - Unknown
			// Sample location
			// Bit 8-15 - Sample address location 0 00xxxxxx xx000000
		case 0x90 / 2:
			// Audio control
			// Bit 0   - Audio 1 enable
			// Bit 1   - Audio 2 enable
			// Bit 2   - Audio 3 enable
			// Bit 3   - Audio 4 enable
			// Bit 4   - Unknown
			// Bit 5   - Audio 2 voice mode enable
			// Bit 6   - Audio 3 sweep mode enable
			// Bit 7   - Audio 4 noise mode enable
			// Audio output
			// Bit 8     - Mono select
			// Bit 9-10  - Output volume
			// Bit 11    - External stereo
			// Bit 12-14 - Unknown
			// Bit 15    - External speaker (Read-only, set by hardware)
		case 0x92 / 2:  // Noise counter shift register
		case 0x94 / 2:
			// Master volume
			// Bit 0-3 - Master volume
			// Bit 4-7 - Unknown
		case 0x9e / 2:  // WSC volume setting (0, 1, 2, 3) (TODO)
			m_sound->port_w(offset, data, mem_mask);
			break;
		case 0xa0 / 2:
			// Hardware type/system control
			// Bit 0   - Disable bios
			// Bit 1   - Hardware type: 0 = WS, 1 = WSC
			// Bit 2   - External bus width
			// Bit 3   - Cart ROM cycles (0 = 3 cycles, 1 = 1 cycle)
			// Bit 4-6 - Unknown
			// Bit 7   - Unknown, read during boot
			if (ACCESSING_BITS_0_7)
			{
				m_sys_control = (data & 0xfd) | ((m_system_type == TYPE_WSC) ? 2 : 0);
				if (BIT(data, 0) && !m_bios_disabled)
				{
					m_bios_disabled = true;
					if (m_cart->exists())
						m_maincpu->space(AS_PROGRAM).install_read_handler(0x40000, 0xfffff, read16s_delegate(*m_cart, FUNC(ws_cart_slot_device::read_rom40)));
				}
			}
			break;

		case 0xb0 / 2:
			// Interrupt base vector
			if (ACCESSING_BITS_0_7)
				m_irq_vector_base = data & 0xff;
			// Serial data (bit 8-15)
			if (ACCESSING_BITS_8_15)
				m_serial_data = data >> 8;
			break;
		case 0xb2 / 2:
			// Interrupt enable
			// Bit 0   - Serial transmit interrupt enable
			// Bit 1   - Key press interrupt enable
			// Bit 2   - RTC alarm interrupt enable
			// Bit 3   - Serial receive interrupt enable
			// Bit 4   - Drawing line detection interrupt enable
			// Bit 5   - VBlank timer interrupt enable
			// Bit 6   - VBlank interrupt enable
			// Bit 7   - HBlank timer interrupt enable
			if (ACCESSING_BITS_0_7)
				m_irq_enable = data & 0xff;
			// serial communication control
			// Bit 8     - Receive complete
			// Bit 9     - Error
			// Bit 10    - Send complete
			// Bit 11-12 - Unknown
			// Bit 13    - Send data interrupt generation
			// Bit 14    - Connection speed: 0 = 9600 bps, 1 = 38400 bps
			// Bit 15    - Receive data interrupt generation
			if (ACCESSING_BITS_8_15)
			{
				m_serial_data = 0xff;
				m_serial_control = data >> 8;
				if (BIT(m_serial_control, 7))
				{
					//              m_serial_data = 0x00;
					m_serial_control |= 0x04;
				}
				if (BIT(m_serial_control, 5))
				{
					//              m_serial_control |= 0x01;
				}
			}
			break;
		case 0xb4 / 2:
			if (ACCESSING_BITS_8_15)
			{
				m_keypad = (data & 0xf0ff) >> 8;
			}
			break;
		case 0xb6 / 2:
			// Interrupt acknowledge
			// Bit 0 - Serial transmit interrupt acknowledge
			// Bit 1 - Key press interrupt acknowledge
			// Bit 2 - RTC alarm interrupt acknowledge
			// Bit 3 - Serial receive interrupt acknowledge
			// Bit 4 - Drawing line detection interrupt acknowledge
			// Bit 5 - VBlank timer interrupt acknowledge
			// Bit 6 - VBlank interrupt acknowledge
			// Bit 7 - HBlank timer interrupt acknowledge
			if (ACCESSING_BITS_0_7)
			{
				clear_irq_line(data & 0xff);
				data = m_irq_active;
			}
			break;
		case 0xba / 2:  // Internal EEPROM data
			COMBINE_DATA(&m_internal_eeprom_data);
			break;
		case 0xbc / 2:  // Internal EEPROM address
			// (WS) Bit 0-5 - Internal EEPROM address
			// (WSC) Bit 0-8 - Internal EEPROM address bit 1-9
			// Bit 9-15 - Unknown
			COMBINE_DATA(&m_internal_eeprom_address);
			break;
		case 0xbe / 2:
			// Internal EEPROM command/status
			// Bit 0   - Read complete (read only)
			// Bit 1   - Write complete (read only)
			// Bit 2-3 - Unknown
			// Bit 4   - Read
			// Bit 5   - Write
			// Bit 6   - Protect
			// Bit 7   - Initialize
			if (ACCESSING_BITS_0_7)
			{
				m_internal_eeprom_command = data & 0xfc;
				if (BIT(m_internal_eeprom_command, 5))
				{
					u16 const addr = get_internal_eeprom_address();
					m_internal_eeprom[addr] = m_internal_eeprom_data & 0xff;
					m_internal_eeprom[addr + 1] = m_internal_eeprom_data >> 8;
					m_internal_eeprom_command |= 0x02;
				}
				else if (BIT(m_internal_eeprom_command, 4))
				{
					u16 const addr = get_internal_eeprom_address();
					m_internal_eeprom_data = m_internal_eeprom[addr] | (m_internal_eeprom[addr + 1] << 8);
					m_internal_eeprom_command |= 0x01;
				}
				else
				{
					LOGEEPROM("%s: Unsupported internal EEPROM command: %02X\n", machine().describe_context(), data & 0xff);
				}
			}
			break;
		case 0xc0 / 2:  // ROM bank $40000-$fffff and SRAM bank
		case 0xc2 / 2:  // ROM bank $20000-$2ffff and ROM bank $30000-$3ffff
		case 0xc4 / 2:
		case 0xc6 / 2:  // EEPROM address / command
		case 0xc8 / 2:  // EEPROM command
		case 0xca / 2:  // RTC command and RTC data
		case 0xcc / 2:
		case 0xce / 2:
			m_cart->write_io(offset, data, mem_mask);
			break;
		default:
			LOGUNKNOWN("%s: Write to unsupported port: %02x - %04x & %04x\n", machine().describe_context(), offset << 1, data, mem_mask);
			break;
	}

	// Update the port value
	COMBINE_DATA(&m_ws_portram[offset]);
}


u16 wscolor_state::dma_r(offs_t offset, u16 mem_mask)
{
	offset += 0x40 / 2;
	u16 const value = m_ws_portram[offset];

	switch (offset)
	{
		case 0x40 / 2:  // DMA source address
			return m_dma_source_offset;
		case 0x42 / 2:  // DMA source bank/segment
			return m_dma_source_segment;
		case 0x44 / 2:  // DMA destination address
			return m_dma_destination;
		case 0x46 / 2:  // DMA size (in bytes)
			return m_dma_length;
		case 0x48 / 2:  // DMA control
			return m_dma_control;
		case 0x4a / 2:
			// Sound DMA source address
			return m_sound_dma.source & 0xffff;
		case 0x4c / 2:
			// Sound DMA source memory segment
			return (m_sound_dma.source >> 16) & 0xffff;
		case 0x4e / 2:
			// Sound DMA transfer size (low 16 bits)
			return m_sound_dma.size & 0xffff;
		case 0x50 / 2:
			// Sound DMA transfer size (high 4 bits)
			return (m_sound_dma.size >> 16) & 0xffff;
		case 0x52 / 2:
			// Sound DMA control
			return m_sound_dma.control;
		default:
			if (!machine().side_effects_disabled())
				LOGDMA("%s: Read from unknown DMA port: %02x & %04x", machine().describe_context(), offset << 1, mem_mask);
			break;
	}
	return value;
}


void wscolor_state::dma_w(offs_t offset, u16 data, u16 mem_mask)
{
	offset += 0x40 / 2;
	switch (offset)
	{
		case 0x40 / 2:  // DMA source address
			COMBINE_DATA(&m_dma_source_offset);
			m_dma_source_offset &= 0xfffe;
			break;
		case 0x42 / 2:  // DMA source bank/segment
			COMBINE_DATA(&m_dma_source_segment);
			m_dma_source_segment &= 0x000f;
			break;
		case 0x44 / 2:  // DMA destination address
			COMBINE_DATA(&m_dma_destination);
			m_dma_destination &= 0xfffe;
			break;
		case 0x46 / 2:  // DMA size (in bytes)
			COMBINE_DATA(&m_dma_length);
			break;
		case 0x48 / 2:  // DMA control
			// Bit 0-6 - Unknown
			// Bit 7   - DMA stop/start
			if (ACCESSING_BITS_0_7)
			{
				if (BIT(data, 7))
				{
					address_space &mem = m_maincpu->space(AS_PROGRAM);
					u32 src = m_dma_source_offset | (m_dma_source_segment << 16);
					u32 dst = m_dma_destination;
					u16 length = m_dma_length;
					s32 const inc = BIT(data, 6) ? -2 : 2;
					if (length)
						m_maincpu->adjust_icount(-(5 + length));
					for ( ; length > 0; length -= 2)
					{
						mem.write_word(dst, mem.read_word(src));
						src += inc;
						dst += inc;
					}
					m_dma_source_offset = src & 0xffff;
					m_dma_source_segment = src >> 16;
					m_dma_destination = dst & 0xffff;
					m_dma_length = length & 0xffff;
					data &= 0x7f;
					m_dma_control = data;
				}
			}
			break;
		case 0x4a / 2:
			// Sound DMA source address (low)
			if (ACCESSING_BITS_0_7)
			{
				m_sound_dma.source = (m_sound_dma.source & 0x0fff00) | (data & 0xff);
				m_sound_dma.source_reload = (m_sound_dma.source_reload & 0x0fff00) | (data & 0xff);
			}
			// Sound DMA source address (high)
			if (ACCESSING_BITS_8_15)
			{
				m_sound_dma.source = (m_sound_dma.source & 0x0f00ff) | (data & 0xff00);
				m_sound_dma.source_reload = (m_sound_dma.source_reload & 0x0f00ff) | (data & 0xff00);
			}
			break;
		case 0x4c / 2:
			// Sound DMA source memory segment
			// Bit 0-3 - Sound DMA source address segment
			// Bit 4-7 - Unknown
			if (ACCESSING_BITS_0_7)
			{
				m_sound_dma.source = (m_sound_dma.source & 0xffff) | ((data & 0x0f) << 16);
				m_sound_dma.source_reload = (m_sound_dma.source_reload & 0xffff) | ((data & 0x0f) << 16);
			}
			break;
		case 0x4e / 2:
			// Sound DMA transfer size
			// Sound DMA transfer size (bit 0-7)
			if (ACCESSING_BITS_0_7)
			{
				m_sound_dma.size = (m_sound_dma.size & 0x0fff00) | (data & 0xff);
				m_sound_dma.size_reload = (m_sound_dma.size_reload & 0x0fff00) | (data & 0xff);
			}
			// Sound DMA transfer size (bit 8-15)
			if (ACCESSING_BITS_8_15)
			{
				m_sound_dma.size = (m_sound_dma.size & 0x0f00ff) | (data & 0xff00);
				m_sound_dma.size_reload = (m_sound_dma.size_reload & 0x0f00ff) | (data & 0xff00);
			}
			break;
		case 0x50 / 2:
			// Sound DMA transfer size (high 4 bits)
			// Bit 0-3 - Sound DMA transfer size (high 4 bits)
			// Bit 4-7 - Unknown
			if (ACCESSING_BITS_0_7)
			{
				m_sound_dma.size = (m_sound_dma.size & 0xffff) | ((data & 0x0f) << 16);
				m_sound_dma.size_reload = (m_sound_dma.size_reload & 0xffff) | ((data & 0x0f) << 16);
			}
			break;
		case 0x52 / 2:
			// Sound DMA control
			// Bit 0-1 - Sound DMA frequency (4000hz, 6000hz, 12000hz, 24000hz)
			// Bit 2   - Sound DMA hold mode (0 = normal playback, 1 = hold)
			// Bit 3   - Sound DMA repeat mode (0 = one-shot, 1 = auto-repeat)
			// Bit 4   - Sound DMA target (0 = channel 2, 1 = hyper voice)
			// Bit 6   - Sound DMA direction (0 = increment, 1 = decrement)
			// Bit 7   - Sound DMA stop/start
			if (ACCESSING_BITS_0_7)
			{
				m_sound_dma.control = data & 0xff;
				if (BIT(m_sound_dma.control, 7))
					m_sound_dma.timer->adjust(attotime::from_ticks(SOUND_DMA_DIV[m_sound_dma.control & 3], X1 / 512));
				else
					m_sound_dma.timer->adjust(attotime::never);
			}
			break;
		default:
			LOGDMA("%s: Write to unknown DMA port: %x - %x\n", machine().describe_context(), offset, data);
			break;
	}
	// Update the port value
	COMBINE_DATA(&m_ws_portram[offset]);
}


void wswan_state::set_icons(u8 data)
{
	// Bit 0 - LCD sleep icon enable
	// Bit 1 - Vertical position icon enable
	// Bit 2 - Horizontal position icon enable
	// Bit 3 - Dot 1 icon enable
	// Bit 4 - Dot 2 icon enable
	// Bit 5 - Dot 3 icon enable
	for (int i = 0; i < 6; i++)
	{
		m_icons[i] = BIT(data, i);
	}

	u8 const old_rotate = m_rotate;

	if ((!BIT(data, 2) && BIT(data, 1)) || (BIT(data, 2) && !BIT(data, 1)))
	{
		m_rotate = (!BIT(data, 2) && BIT(data, 1)) ? 1 : 0;

		if (old_rotate != m_rotate)
		{
			set_rotate_view();
		}
	}
}


void wswan_state::set_rotate_view()
{
	render_target *target = machine().render().first_target();
	target->set_view(m_rotate);
}


u16 wswan_state::get_internal_eeprom_address()
{
	return (m_internal_eeprom_address & 0x3f) << 1;
}


u16 wscolor_state::get_internal_eeprom_address()
{
	return (m_internal_eeprom_address & 0x1ff) << 1;
}

/***************************************************************************

  Game driver(s)

***************************************************************************/

ROM_START(wswan)
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("boot.rom", 0x0000, 0x1000, CRC(7f35f890) SHA1(4015bcacea76bb0b5bbdb13c5358f7e1abb986a1))

	ROM_REGION(0x80, "nvram", 0)
	// Need a dump from an original new unit
	// Empty file containing just the name 'WONDERSAN'
	ROM_LOAD("internal_eeprom.ws", 0x00, 0x80, BAD_DUMP CRC(b1dff316) SHA1(7b76c3d59c9add9501f95e8bfc34427773fcbd28))
ROM_END

ROM_START(wscolor)
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("boot.rom", 0x0000, 0x2000, CRC(cb06d9c3) SHA1(c5ad0b8af45d762662a69f50b64161b9c8919efb))

	ROM_REGION(0x800, "nvram", 0)
	// Need a dump from an original new unit
	// Empty file containing just the name 'WONDERSWANCOLOR' (from Youtube videos)
	ROM_LOAD("internal_eeprom.wsc", 0x000, 0x800, BAD_DUMP CRC(ca11afc9) SHA1(0951845f01f83bee497268a63b5fb7baccfeff7c))
ROM_END

// this currently uses the wswan internal ROMs, the real ones should be different
ROM_START(pockchv2)
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("boot.rom", 0x0000, 0x1000, BAD_DUMP CRC(7f35f890) SHA1(4015bcacea76bb0b5bbdb13c5358f7e1abb986a1))

	ROM_REGION(0x80, "nvram", 0)
	// Need a dump from an original new unit
	// Empty file containing just the name 'WONDERSAN'
	ROM_LOAD("internal_eeprom.ws", 0x00, 0x80, BAD_DUMP CRC(b1dff316) SHA1(7b76c3d59c9add9501f95e8bfc34427773fcbd28))
ROM_END

// SwanCrystal has the name 'SWANCRYSTAL' (from Youtube videos)

} // anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT  CLASS          INIT        COMPANY   FULLNAME
CONS( 1999, wswan,    0,      0,      wswan,    wswan, wswan_state,   empty_init, "Bandai", "WonderSwan",       MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
CONS( 2000, wscolor,  wswan,  0,      wscolor,  wswan, wscolor_state, empty_init, "Bandai", "WonderSwan Color", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )

CONS( 2002, pockchv2, wswan,  0,      pockchv2, wswan, wswan_state,   empty_init, "Benesse Corporation", "Pocket Challenge V2",       MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )



wxstar4000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    wxstar4000.cpp: WeatherSTAR 4000 cable head-end unit
    1990 Applied Microelectronics Institute (Amirix) / The Weather Channel
    Skeleton driver by R. Belmont

  This was used by cable companies in the US starting in 1990 to generate
  graphics and text for the local weather forecast during The Weather Channel's
  "Local on the 8s" segments.

 There are 4 PCBs on a VME backplane:

 - CPU board contains a 68010 CPU, RAM, and EEPROM
 - Graphics board contains a 68010 CPU and an i8051 which manages the palette
   and performs other functions.  Framebuffer is 8 bits per pixel and the
   framebuffer start position can be changed, possibly per-scanline.
   RAMDAC is a Bt471.
 - Data/Audio board contains an Intel P8344AH, which is an MCU containing an
   i8051 and an SDLC decoder.  The "audio" is a TTL circuit to create an
   alert tone for severe weather conditions.
 - I/O board contains an i8031 CPU, an i8251A UART and serial port for a modem,
   and an AT-style keyboard interface and DIN connector.

   CPU board 68010 IRQs:
   IRQ1 = Internal Real Time Clock, Autovectored. Or, Can be used externally but requires manual vectoring.
   IRQ2 = I/O Card Incoming interrupt Request. Triggered when I/O card needs us to handle something.
   IRQ3 = Secondary Graphics Card Incoming interrupt request. Not used in single-card systems.
   IRQ4 = Primary Graphics Card Incoming interrupt request.
   IRQ5 = Data Card incoming interrupt request. Triggers when the Data card has something for us to do or handle.
   IRQ6 = Unused.
   IRQ7 = AC Fail, Battery Backup Input. Once triggered, Unrecoverable. Requires System Reset.

   Graphics board 68010 IRQs:
   IRQ5 = Vertical Blanking Interrupt. Autovectored. Used for timing graphics acceleration instructions.
   IRQ6 = Incoming request from the CPU Card. Used to signal us to go do something.
   IRQ7 = AC Fail, Battery Backup Input. Once triggered, Unrecoverable. Requires System Reset.

   Graphics board 8051 GPIO pins:
   P1.0(T2) = FIFO Empty Flag
   P1.1(T2EX) = Switch to Sat Video. (Active Low)(Drive high to shutdown genlock)
   P1.2 = Switch To Local Video. (Active High)
   P1.3 = Flag to 68K CPU (Control CPU Ready for Command, Active High)
   P1.4 = Sat Video Present/Odd-Even Frame Indicator
   P1.5 = Frame/Sync Control Register (Or Timer prescaler)
   P1.6 = Frame/Sync Control Register (Or Timer prescaler)
   P1.7 = Frame/Sync Control Register (Or Timer prescaler), Watchdog timer.
   P3.0 = RX from FPGA
   P3.1 = TX to FPGA
   P3.2(INT0) = Vertical Drive Interrupt
   P3.3(INT1) = Odd/Even Frame Sync Interrupt
   P3.4(T0) = Input from Frame/Sync Control Register
   P3.5(T1) = Input from Frame/Sync Control Register

***************************************************************************/

#include "emu.h"
#include "cpu/m68000/m68010.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/gen_latch.h"
#include "machine/icm7170.h"
#include "machine/nvram.h"
#include "machine/timer.h"
#include "video/bt47x.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class wxstar4k_state : public driver_device
{
public:
	wxstar4k_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_gfxcpu(*this, "gfxcpu"),
		m_gfxsubcpu(*this, "gfxsubcpu"),
		m_datacpu(*this, "datacpu"),
		m_iocpu(*this, "iocpu"),
		m_mainram(*this, "mainram"),
		m_extram(*this, "extram"),
		m_vram(*this, "vram"),
		m_rtc(*this, "rtc")
	{ }

	void wxstar4k(machine_config &config);

private:
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void cpubd_main(address_map &map) ATTR_COLD;
	void vidbd_main(address_map &map) ATTR_COLD;
	void vidbd_sub(address_map &map) ATTR_COLD;
	void vidbd_sub_io(address_map &map) ATTR_COLD;
	void databd_main(address_map &map) ATTR_COLD;
	void databd_main_io(address_map &map) ATTR_COLD;
	void iobd_main(address_map &map) ATTR_COLD;
	void iobd_main_io(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

	optional_device<m68010_device> m_maincpu, m_gfxcpu;
	optional_device<mcs51_cpu_device> m_gfxsubcpu, m_datacpu, m_iocpu;
	optional_shared_ptr<uint16_t> m_mainram, m_extram, m_vram;
	required_device<icm7170_device> m_rtc;

	uint16_t buserr_r()
	{
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
		m_maincpu->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
		return 0xffff;
	}
};

void wxstar4k_state::video_start()
{
}

uint32_t wxstar4k_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void wxstar4k_state::cpubd_main(address_map &map)
{
	// 4x M5M44400A fast-page DRAMs (1M x 4)
	map(0x000000, 0x1fffff).ram().share("mainram"); // private RAM
	map(0x200000, 0x3fffff).ram().share("extram");  // RAM accessible by other cards
	map(0x400000, 0x400003).r(FUNC(wxstar4k_state::buserr_r));
	// C00000 - I/O card control register
	// C00001-C001FF - I/O card UART buffer
	// C04000-C041FF - I/O card modem buffer
	// C0A000 - data card FIFO
	// C0A200 - write byte to data card CPU
	// C0A400 - read byte from data card CPU
	// C0A600 - write byte to audio control latch 1
	// C0A800 - write byte to audio control latch 2
	map(0xfd0000, 0xfd3fff).rom().region("eeprom", 0); // we'll make this writable later
	// FDF000 - cause IRQ 6 on graphics card
	// FDF004 - cause IRQ 6 on graphics card 2 (not used)
	// FDF008 - reset watchdog
	map(0xfdffc0, 0xfdffe3).rw(m_rtc, FUNC(icm7170_device::read), FUNC(icm7170_device::write)).umask16(0x00ff);
	map(0xfe0000, 0xffffff).rom().region("maincpu", 0);
}

void wxstar4k_state::vidbd_main(address_map &map)
{
	map(0x000000, 0x00ffff).rom().region("gfxcpu", 0);
	map(0x100000, 0x10ffff).ram();
	// 200000 - read bit 0=i8051 FIFO full, bit1=i8051 ready for command.  write: top 7 bits of VME address.
	map(0x200002, 0x200003).nopr(); // read: watchdog reset + bit0=Sat video present, bit1=local video present
	// write: interrupt vector when causing a main CPU interrupt
	// 200004 - write i8051 FIFO
	// 200006 - cause IRQ4 on main CPU
	// 300000-300003 - graphics control registers
	map(0x400000, 0x5fffff).ram().share("vram");  // framebuffer (16x M5M44256 = 16Mbit)
	// E00000-E1FFFF - lower 16 address bits of VME access
}

void wxstar4k_state::vidbd_sub(address_map &map)
{
	map(0x0000, 0x1fff).rom().region("gfxsubcpu", 0);
}

void wxstar4k_state::vidbd_sub_io(address_map &map)
{
	map(0x0000, 0x07ff).ram();
	// 1000-17FF - VRAM counter low byte
	// 1800-1FFF - VRAM counter high byte
	// 2000-27FF - read FIFO from 68010
	// 2800-7FFF - undecoded
	// 8000 - Bt471 palette address write (also at C000)
	// 8800 - Bt471 palette RAM (also C800)
	// 9000 - Bt471 pixel mask read (also D000)
	// 9800 - Bt471 palette address read (also D800)
	// A000 - Bt471 overlay write address (also E000)
	// A800 - Bt471 overlay register (also E800)
	// B800 - Bt471 overlay read address (also F800)
}

void wxstar4k_state::databd_main(address_map &map)
{
	map(0x0000, 0x1fff).rom().region("datacpu", 0);
}

void wxstar4k_state::databd_main_io(address_map &map)
{
	map(0x0000, 0x01ff).ram();
	// 0200 - UART data
	// 0201 - UART command
	// 8000 - PIO1 Command/Status register
	// 8001 - PIO1 Port A - VME address/data AD1-AD8
	// 8002 - PIO1 Port B - rear external switches
	// 8003 - PIO1 Port C
	// 8004 - PIO1 transfer count low
	// 8005 - PIO1 transfer count high
	// 8100 - PIO2 Command/Status register
	// 8101 - PIO2 Port A - indicator LEDs
	// 8102 - PIO2 Port B - DTMF dialer codes + 1 LED
	// 8103 - PIO2 Port C - bus clear, charging indicator
	// 8104 - PIO2 transfer count low
	// 8105 - PIO2 transfer count high
}

void wxstar4k_state::iobd_main(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("iocpu", 0);
}

void wxstar4k_state::iobd_main_io(address_map &map)
{
}

static INPUT_PORTS_START( wxstar4k )
INPUT_PORTS_END

void wxstar4k_state::machine_start()
{
}

void wxstar4k_state::machine_reset()
{
	u16 *RAM = (u16 *)m_mainram.target();
	u16 *ROM = &memregion("maincpu")->as_u16();
	memcpy(RAM, ROM, 0x400);
}

void wxstar4k_state::wxstar4k(machine_config &config)
{
	/* basic machine hardware */
	M68010(config, m_maincpu, XTAL(20'000'000)/2);  // 20 MHz crystal / 2 (QA output of a 74LS393)
	m_maincpu->set_addrmap(AS_PROGRAM, &wxstar4k_state::cpubd_main);

	M68010(config, m_gfxcpu, XTAL(20'000'000)/2);   // runs on the system clock from the CPU board, so also 10 MHz
	m_gfxcpu->set_addrmap(AS_PROGRAM, &wxstar4k_state::vidbd_main);

	I8051(config, m_gfxsubcpu, XTAL(12'000'000));   // 12 MHz crystal connected directly to the CPU
	m_gfxsubcpu->set_addrmap(AS_PROGRAM, &wxstar4k_state::vidbd_sub);
	m_gfxsubcpu->set_addrmap(AS_IO, &wxstar4k_state::vidbd_sub_io);

	I8344(config, m_datacpu, XTAL(7'372'800));  // 7.3728 MHz crystal connected directly to the CPU
	m_datacpu->set_addrmap(AS_PROGRAM, &wxstar4k_state::databd_main);
	m_datacpu->set_addrmap(AS_IO, &wxstar4k_state::databd_main_io);

	I8031(config, m_iocpu, XTAL(11'059'200));   // 11.0592 MHz crystal connected directly to the CPU
	m_iocpu->set_addrmap(AS_PROGRAM, &wxstar4k_state::iobd_main);
	m_iocpu->set_addrmap(AS_IO, &wxstar4k_state::iobd_main_io);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(59.62);  /* verified on pcb */
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(0));
	screen.set_size(64*8, 32*8);
	screen.set_visarea(40, 400-1, 16, 240-1);
	screen.set_screen_update(FUNC(wxstar4k_state::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette").set_format(palette_device::xBGR_888, 256);

	ICM7170(config, m_rtc, XTAL(32'768));
}

ROM_START( wxstar4k )
	ROM_REGION( 0x20000, "maincpu", 0 ) /* CPU board 68010 program */
	ROM_LOAD16_BYTE( "u79 rom.bin",  0x000001, 0x010000, CRC(11df2d70) SHA1(ac6cdb5290c90b043562464dc001fc5e3d26f7c6) )
	ROM_LOAD16_BYTE( "u80 rom.bin",  0x000000, 0x010000, CRC(23e15f22) SHA1(a630bda39c0beec7e7fc3834178ec8a6fece70c8) )

	ROM_REGION16_BE(0x4000, "eeprom", 0 ) /* CPU board EEPROM */
	ROM_LOAD( "u72 eeprom.bin", 0x000000, 0x002000, CRC(f775b4d6) SHA1(a0895177c381919f9bfd99ee35edde0dd5fa379c) )

	ROM_REGION(0x2000, "datacpu", 0) /* P8344 (i8051 plus SDLC decoder) on Data board */
	ROM_LOAD( "u12 rom.bin",  0x000000, 0x002000, CRC(f7d8432d) SHA1(0ff1dad65ecb4c3d8cb21feef56bbc6f06a2f712) )

	ROM_REGION(0x8000, "iocpu", 0) /* i8051 on I/O board */
	ROM_LOAD( "u11 rom.bin",  0x000000, 0x008000, CRC(f12cb28b) SHA1(3368f55717d8e9e7a06a4f241de02b7b2577b32b) )

	ROM_REGION(0x2000, "gfxsubcpu", 0) /* i8051 sub-CPU on Graphics board */
	ROM_LOAD( "u13 rom.bin",  0x000000, 0x002000, CRC(667b0a2b) SHA1(d60bcc271a73633544b0cf2f80589b2e5670b705) )

	ROM_REGION(0x10000, "gfxcpu", 0) /* Graphics board 68010 program */
	ROM_LOAD16_BYTE( "u42 rom low.bin", 0x000001, 0x008000, CRC(84038ca3) SHA1(b28a0d357d489fb06ff0d5d36ea11ebd1f9612a5) )
	ROM_LOAD16_BYTE( "u43 rom high.bin", 0x000000, 0x008000, CRC(6f2a7592) SHA1(1aa2394db42b6f28277e35a48a7cef348c213e05) )
ROM_END

} // anonymous namespace


COMP( 1990, wxstar4k, 0, 0, wxstar4k, wxstar4k, wxstar4k_state, empty_init, "Applied Microelectronics Institute/The Weather Channel", "WeatherSTAR 4000", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )



wy100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Driver for Wyse WY-100 video terminal.

    The WY-100 was Wyse Technology's first product.

    Of the two 8276 CRTCs, one is used solely to keep track of which characters
    are protected, which is the only transparent attribute supported.

*******************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/bankdev.h"
#include "machine/input_merger.h"
#include "machine/scn_pci.h"
#include "wy50kb.h"
#include "sound/spkrdev.h"
#include "video/i8275.h"
#include "screen.h"
#include "speaker.h"


namespace {

class wy100_state : public driver_device
{
public:
	wy100_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rambank(*this, "rambank")
		, m_crtc(*this, "crtc%u", 1U)
		, m_pci(*this, "pci")
		, m_modem(*this, "modem")
		, m_printer(*this, "aux")
		, m_chargen(*this, "chargen")
	{
	}

	void wy100(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	I8275_DRAW_CHARACTER_MEMBER(draw_character);

	void brdy_w(int state);
	void txd_w(int state);
	void p2_w(u8 data);
	u8 memory_r(offs_t offset);
	void memory_w(offs_t offset, u8 data);

	void prg_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void bank_map(address_map &map) ATTR_COLD;

	static void printer_devices(device_slot_interface &slot);

	required_device<mcs48_cpu_device> m_maincpu;
	required_device<address_map_bank_device> m_rambank;
	required_device_array<i8276_device, 2> m_crtc;
	required_device<scn2651_device> m_pci;
	required_device<rs232_port_device> m_modem;
	required_device<rs232_port_device> m_printer;

	required_region_ptr<u8> m_chargen;

	bool m_brdy = false;
	bool m_bs_enable = false;
	bool m_txd = true;
	bool m_printer_select = false;
};

void wy100_state::machine_start()
{
	m_brdy = false;
	m_bs_enable = false;
	m_txd = true;
	m_printer_select = false;

	save_item(NAME(m_brdy));
	save_item(NAME(m_bs_enable));
	save_item(NAME(m_txd));
	save_item(NAME(m_printer_select));
}

void wy100_state::brdy_w(int state)
{
	m_brdy = state;
}

I8275_DRAW_CHARACTER_MEMBER(wy100_state::draw_character)
{
	// LTEN attribute output is not used (GPA1 generates underline instead)
	using namespace i8275_attributes;
	u8 dots = 0;
	if (!BIT(attrcode, VSP))
	{
		if (BIT(attrcode, GPA1) && (linecount & 0xb) == 0xa)
			dots = 0xff;
		else if (!BIT(attrcode, GPA0))
			dots = m_chargen[((charcode & 0x7f) << 4) | linecount];
	}
	if (BIT(attrcode, RVV))
		dots ^= 0xff;

	const rgb_t fg = BIT(charcode, 8) && BIT(attrcode, HLGT) ? rgb_t::white() : rgb_t(0xc0, 0xc0, 0xc0);
	const rgb_t bg = rgb_t::black();
	for (int i = 0; i < 10; i++)
		bitmap.pix(y, x + i) = BIT(dots, i < 1 || i > 8 ? 7 : 8 - i) ? fg : bg;
}

void wy100_state::txd_w(int state)
{
	m_txd = state;
	if (m_printer_select)
		m_printer->write_txd(state);
	else
		m_modem->write_txd(state);
}

void wy100_state::p2_w(u8 data)
{
	m_rambank->set_bank(data & 0x1f);
	if (!BIT(data, 6))
		m_bs_enable = false;
	if (BIT(data, 7) && !m_printer_select)
	{
		m_printer_select = true;
		m_printer->write_txd(m_txd);
		m_modem->write_txd(1);
	}
	else if (!BIT(data, 7) && m_printer_select)
	{
		m_printer_select = false;
		m_modem->write_txd(m_txd);
		m_printer->write_txd(1);
	}
}

u8 wy100_state::memory_r(offs_t offset)
{
	u8 p2 = m_maincpu->p2_r();
	u8 data = BIT(p2, 5) ? m_pci->read(p2 & 3) : m_rambank->read8(offset);
	if (m_bs_enable && !machine().side_effects_disabled())
	{
		u8 chardata = (data & 0xe0) == 0x80 ? data : data & 0x7f;
		m_crtc[0]->dack_w(chardata);
		m_crtc[1]->dack_w((chardata & 0xfe) | (BIT(data, 7) ? 0x00 : 0x01));
	}
	return data;
}

void wy100_state::memory_w(offs_t offset, u8 data)
{
	u8 p2 = m_maincpu->p2_r();

	// CRTC access is write-only
	if (!BIT(p2, 6))
	{
		m_crtc[0]->write(p2 & 1, data);
		m_crtc[1]->write(p2 & 1, data);
	}
	else if (m_brdy)
		m_bs_enable = true;

	if (BIT(p2, 5))
		m_pci->write(p2 & 3, data);

	m_rambank->write8(offset, data);
}

void wy100_state::prg_map(address_map &map)
{
	map(0x000, 0xfff).rom().region("maincpu", 0);
}

void wy100_state::io_map(address_map &map)
{
	map(0x00, 0xff).rw(FUNC(wy100_state::memory_r), FUNC(wy100_state::memory_w));
}

void wy100_state::bank_map(address_map &map)
{
	map(0x0000, 0x0bff).nopw();
	map(0x0c00, 0x0fff).ram(); // buffer RAM (P2114A-6 at 4-5A)
	map(0x1000, 0x1fff).ram(); // display RAM (P2114A-6 at 6-9A, optionally also at 10-13A)
}


static INPUT_PORTS_START(wy100)
INPUT_PORTS_END


class wy100_loopback_device : public device_t, public device_rs232_port_interface
{
public:
	wy100_loopback_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

protected:
	virtual void device_start() override ATTR_COLD;

	virtual void input_txd(int state) override;
};

DEFINE_DEVICE_TYPE_PRIVATE(WY100_LOOPBACK, device_rs232_port_interface, wy100_loopback_device, "wy100_loopback", "WY-100 Printer Loopback (3 to 20)")

wy100_loopback_device::wy100_loopback_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, WY100_LOOPBACK, tag, owner, clock)
	, device_rs232_port_interface(mconfig, *this)
{
}

void wy100_loopback_device::device_start()
{
}

void wy100_loopback_device::input_txd(int state)
{
	output_dsr(state);
}


void wy100_state::printer_devices(device_slot_interface &slot)
{
	default_rs232_devices(slot);
	slot.option_replace("loopback", WY100_LOOPBACK);
	slot.option_remove("dec_loopback");
}

void wy100_state::wy100(machine_config &config)
{
	I8039(config, m_maincpu, 10.1376_MHz_XTAL); // INS8039N-11
	m_maincpu->set_addrmap(AS_PROGRAM, &wy100_state::prg_map);
	m_maincpu->set_addrmap(AS_IO, &wy100_state::io_map);
	m_maincpu->p1_out_cb().set("keyboard", FUNC(wy100_keyboard_device::scan_w)).mask(0x7f).invert();
	m_maincpu->p1_out_cb().append("spkrgate", FUNC(input_merger_device::in_w<0>)).bit(7);
	m_maincpu->p2_out_cb().set(FUNC(wy100_state::p2_w));
	m_maincpu->t0_in_cb().set("keyboard", FUNC(wy100_keyboard_device::sense_r)).invert();
	m_maincpu->t1_in_cb().set(m_pci, FUNC(scn2651_device::rxrdy_r));

	WY100_KEYBOARD(config, "keyboard");

	ADDRESS_MAP_BANK(config, m_rambank);
	m_rambank->set_addrmap(0, &wy100_state::bank_map);
	m_rambank->set_data_width(8);
	m_rambank->set_addr_width(13);
	m_rambank->set_stride(0x100);

	SCN2651(config, m_pci, 10.1376_MHz_XTAL / 2); // INS2651N
	m_pci->rts_handler().set(m_modem, FUNC(rs232_port_device::write_rts));
	m_pci->dtr_handler().set(m_modem, FUNC(rs232_port_device::write_dtr));
	m_pci->txd_handler().set(FUNC(wy100_state::txd_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(18.48_MHz_XTAL, 1000, 0, 800, 308, 0, 286);
	//screen.set_raw(18.48_MHz_XTAL, 1100, 0, 800, 336, 0, 312);
	screen.set_color(rgb_t::green());
	screen.set_screen_update("crtc1", FUNC(i8276_device::screen_update));

	for (auto &crtc : m_crtc)
	{
		I8276(config, crtc, 18.48_MHz_XTAL / 10);
		crtc->set_screen("screen");
		crtc->set_character_width(10);
	}
	m_crtc[0]->set_display_callback(FUNC(wy100_state::draw_character));
	m_crtc[0]->drq_wr_callback().set_inputline(m_maincpu, MCS48_INPUT_IRQ);
	m_crtc[0]->drq_wr_callback().append(FUNC(wy100_state::brdy_w));
	m_crtc[0]->lc_wr_callback().set("spkrgate", FUNC(input_merger_device::in_w<1>)).bit(3);
	m_crtc[0]->set_next_crtc(m_crtc[1]);

	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.5);
	input_merger_device &spkrgate(INPUT_MERGER_ALL_HIGH(config, "spkrgate"));
	spkrgate.output_handler().set("speaker", FUNC(speaker_sound_device::level_w));

	RS232_PORT(config, m_modem, default_rs232_devices, "loopback");
	m_modem->dcd_handler().set(m_pci, FUNC(scn2651_device::dcd_w));
	m_modem->cts_handler().set(m_pci, FUNC(scn2651_device::cts_w));
	m_modem->rxd_handler().set(m_pci, FUNC(scn2651_device::rxd_w));

	RS232_PORT(config, m_printer, wy100_state::printer_devices, "loopback");
	m_printer->dsr_handler().set(m_pci, FUNC(scn2651_device::dsr_w)); // actually pin 20 (DTR)
}


ROM_START(wy100)
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("wy100_00401f.bin", 0x0000, 0x1000, CRC(1f71de8f) SHA1(2bd9f712aba8b44823ce0b3e111da7b472a1ab38))

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD("wy100_23-002-01c.bin", 0x0000, 0x0800, CRC(93c31537) SHA1(085e5ad110a76bee83e819a718a7d4cbfb8e07e7))
ROM_END

} // anonymous namespace


COMP(1981, wy100, 0, 0, wy100, wy100, wy100_state, empty_init, "Wyse Technology", "WY-100", MACHINE_SUPPORTS_SAVE)



wy150.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

    Skeleton driver for Wyse WY-150 and related terminals.

    The WY-150, originally introduced in Nov. 1988, was the first of the "terminals of the future" that Wyse sold well into the
    1990s. The WY-160 is a graphical terminal that runs on different but clearly related hardware.

    All video functions in these terminals are integrated into ASICs (e.g. 211009-01, 211009-02). The 8032 generates all active
    signals for both the main serial port and the serial keyboard. Three 8Kx8 SRAMs are used to store characters, attributes and
    font data; the second is also battery-backed.

************************************************************************************************************************************/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/nvram.h"
#include "screen.h"


namespace {

class wy150_state : public driver_device
{
public:
	wy150_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_screen(*this, "screen")
	{
	}

	void wy150(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void driver_start() override;

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void prog_map(address_map &map) ATTR_COLD;
	void ext_map(address_map &map) ATTR_COLD;

	required_device<screen_device> m_screen;
};


void wy150_state::machine_start()
{
}

u32 wy150_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void wy150_state::prog_map(address_map &map)
{
	map(0x0000, 0xffff).rom().region("program", 0);
}

void wy150_state::ext_map(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("nvram");
	map(0x2000, 0x3fff).ram();
	map(0x4000, 0x5fff).ram();
}


static INPUT_PORTS_START(wy150)
INPUT_PORTS_END


void wy150_state::wy150(machine_config &config)
{
	i80c32_device &maincpu(I80C32(config, "maincpu", 11_MHz_XTAL)); // Philips P80C32SBPN (e.g.)
	maincpu.set_addrmap(AS_PROGRAM, &wy150_state::prog_map);
	maincpu.set_addrmap(AS_IO, &wy150_state::ext_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // 8464 or 5564 or similar (e.g. Winbond W2465-70LL) + battery

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(48_MHz_XTAL, 1530, 0, 1200, 523, 0, 416); // 31.372 kHz horizontal
	//m_screen->set_raw(48_MHz_XTAL, 1530, 0, 1188, 402, 0, 338);
	// TBD: WY-160 should have different parameters (has 76 Hz refresh rate rather than 78 Hz)
	m_screen->set_screen_update(FUNC(wy150_state::screen_update));
}


ROM_START(wy150)
	ROM_REGION(0x10000, "program", 0)
	ROM_LOAD("251167-01.bin", 0x00000, 0x10000, CRC(4f425b11) SHA1(e44f54aa98d9f9c668a6ad674ec07e47879fc2a0))

	ROM_REGION(0x20000, "link", 0)
	ROM_LOAD("link_mc3.bin",            0x00000, 0x10000, CRC(9e1d37d9) SHA1(d74c0faf6cf1eb06243607931967cf35a633ac8e))
	ROM_LOAD("link_mc5_xerox-wy30.bin", 0x10000, 0x10000, CRC(1aa00cb4) SHA1(6a7267132fe35c8e07deccd67c0fb4fe5a240c99))
ROM_END

ROM_START(wy120) // b&w
	ROM_REGION(0x10000, "program", 0)
	ROM_LOAD("wy120_ver1.4.bin", 0x00000, 0x10000, CRC(6de23624) SHA1(ad90087237347662b5ae4fcc8a05d66d76c46a26))
ROM_END

ROM_START(wy160)
	ROM_REGION(0x10000, "program", 0)
	ROM_LOAD("251167-06.bin", 0x00000, 0x10000, CRC(36e920df) SHA1(8fb7f51b4f47ef63b21d421227d6fef98001e4e9))
ROM_END

ROM_START(wy325) // SCN8032HCCA44, 211009-02, 3x CXK5864CM-70LL, SCN2661BC1A28, 3V battery, color display
	ROM_REGION(0x10000, "program", 0)
	ROM_LOAD("wyse_tech_rev.a_251125-05.bin", 0x00000, 0x10000, CRC(4a327c38) SHA1(061332197d824aa4171ec998e2f286081adcf198)) // M27C512-15XF1
ROM_END

void wy150_state::driver_start()
{
	uint8_t *rom = memregion("program")->base();
	for (offs_t base = 0x00000; base < 0x10000; base += 0x4000)
	{
		std::vector<uint8_t> orig(&rom[base], &rom[base + 0x4000]);

		// Line swap is provided by schematic in WY-120 Maintenance Manual
		for (offs_t offset = 0; offset < 0x4000; offset++)
			rom[base | offset] = bitswap<8>(orig[bitswap<14>(offset, 7, 8, 6, 5, 4, 3, 9, 10, 11, 12, 13, 2, 1, 0)], 3, 4, 2, 5, 1, 6, 0, 7);
	}
}

} // anonymous namespace


COMP(1991, wy150, 0, 0, wy150, wy150, wy150_state, empty_init, "Wyse Technology", "WY-150 (v1.0)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP(1992, wy120, 0, 0, wy150, wy150, wy150_state, empty_init, "Wyse Technology", "WY-120 (v1.4)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP(1994, wy160, 0, 0, wy150, wy150, wy150_state, empty_init, "Wyse Technology", "WY-160 (v1.7)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP(1994, wy325, 0, 0, wy150, wy150, wy150_state, empty_init, "Wyse Technology", "WY-325 (v3.2)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



wy30p.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Skeleton driver for Wyse WY-30+ terminal.

    This low-end video terminal is the successor to the WY-30, whose control
    board features an entirely different hardware configuration.

*******************************************************************************/

#include "emu.h"
#include "bus/wysekbd/wysekbd.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/eepromser.h"
#include "screen.h"


namespace {

class wy30p_state : public driver_device
{
public:
	wy30p_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_eeprom(*this, "eeprom")
		, m_keyboard(*this, "keyboard")
		, m_screen(*this, "screen")
	{
	}

	void wy30p(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void driver_start() override;

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	u8 p1_r();
	u8 p3_r();
	void keyboard_clock_w(u8 data);
	void keyboard_reset_w(u8 data);
	u8 de00_r();

	void prog_map(address_map &map) ATTR_COLD;
	void ext_map(address_map &map) ATTR_COLD;

	required_device<eeprom_serial_er5911_device> m_eeprom;
	required_device<wyse_keyboard_port_device> m_keyboard;
	required_device<screen_device> m_screen;
};


void wy30p_state::machine_start()
{
}

u32 wy30p_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

u8 wy30p_state::p1_r()
{
	return 0x7f | (!m_keyboard->data_r() << 7);
}

u8 wy30p_state::p3_r()
{
	return 0xdf | (m_eeprom->do_read() << 5);
}

void wy30p_state::keyboard_clock_w(u8 data)
{
	m_keyboard->cmd_w(0);
	m_keyboard->cmd_w(1);
}

void wy30p_state::keyboard_reset_w(u8 data)
{
	m_keyboard->cmd_w(0);
}

u8 wy30p_state::de00_r()
{
	return m_screen->vblank() << 7; // probably not correct
}

void wy30p_state::prog_map(address_map &map)
{
	map(0x0000, 0x3fff).mirror(0xc000).rom().region("program", 0);
}

void wy30p_state::ext_map(address_map &map)
{
	map(0x0000, 0x1fff).ram();
	map(0xa000, 0xa7ff).ram();
	map(0xc300, 0xc3ff).nopw(); // ?
	map(0xc700, 0xc7ff).nopw(); // ?
	map(0xc900, 0xc9ff).nopw(); // ?
	map(0xdc00, 0xdc00).mirror(0xff).w(FUNC(wy30p_state::keyboard_clock_w));
	map(0xdd00, 0xdd00).mirror(0xff).w(FUNC(wy30p_state::keyboard_reset_w));
	map(0xde00, 0xde00).mirror(0xff).r(FUNC(wy30p_state::de00_r));
}


static INPUT_PORTS_START(wy30p)
INPUT_PORTS_END


void wy30p_state::wy30p(machine_config &config)
{
	i8031_device &maincpu(I8031(config, "maincpu", 7.3728_MHz_XTAL));
	maincpu.set_addrmap(AS_PROGRAM, &wy30p_state::prog_map);
	maincpu.set_addrmap(AS_IO, &wy30p_state::ext_map);
	maincpu.port_in_cb<1>().set(FUNC(wy30p_state::p1_r));
	maincpu.port_in_cb<3>().set(FUNC(wy30p_state::p3_r));
	maincpu.port_out_cb<3>().set(m_eeprom, FUNC(eeprom_serial_er5911_device::cs_write)).bit(3);
	maincpu.port_out_cb<3>().append(m_eeprom, FUNC(eeprom_serial_er5911_device::clk_write)).bit(4);
	maincpu.port_out_cb<3>().append(m_eeprom, FUNC(eeprom_serial_er5911_device::di_write)).bit(5);

	EEPROM_ER5911_8BIT(config, m_eeprom);

	WYSE_KEYBOARD(config, m_keyboard, wy30_keyboards, "wy30");

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(31.2795_MHz_XTAL * 2 / 3, 1050, 0, 800, 331, 0, 312); // divider and dimensions guessed
	m_screen->set_screen_update(FUNC(wy30p_state::screen_update));
	m_screen->screen_vblank().set_inputline("maincpu", MCS51_INT0_LINE);
}


// PCB: "© 1989 990710-01 REV.B1"
// CPU: Intel P8031AH
// Program EPROM: 250971-02 (M27128MFI) "© WYSE TECH 92' REV.A"
// Nonvolatile memory: CSI CAT59C11P
// RAM: TC5565APL-12, HY6116AP-15
// QFP gate array: 211019-02 (Oki M76V020) "© WYSE,1989"
// Jumper near gate array: "FONT SIZE" = "8Kx8" or "2Kx8" (J3)
// XTALs: 31.2795 (dot clock?), 7.3728 (for CPU)
// DB-25 connectors: "MDM" & "AUX"
ROM_START(wy30p)
	ROM_REGION(0x4000, "program", 0)
	ROM_LOAD("250971-02.u4", 0x0000, 0x4000, CRC(3666549c) SHA1(23c432da2083df4b355daf566dd6514d1f9a7690))
ROM_END

void wy30p_state::driver_start()
{
	uint8_t *rom = memregion("program")->base();
	for (offs_t base = 0x0000; base < 0x4000; base += 0x2000)
	{
		std::vector<uint8_t> orig(&rom[base], &rom[base + 0x2000]);

		// Line swaps can be confusing...
		for (offs_t offset = 0; offset < 0x2000; offset++)
			rom[base | offset] = bitswap<8>(orig[bitswap<13>(offset, 8, 5, 0, 4, 3, 9, 7, 10, 11, 12, 2, 6, 1)], 3, 4, 5, 6, 7, 2, 1, 0);
	}
}

} // anonymous namespace


COMP(1992, wy30p, 0, 0, wy30p, wy30p, wy30p_state, empty_init, "Wyse Technology", "WY-30+ (v1.8)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



wy50.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Preliminary driver for Wyse WY-50 and similar display terminals.

    Wyse Technology introduced the WY-50 green screen terminal in the fall of
    1983. It was soon followed by the WY-75 ANSI X3.64-compatible terminal and
    the WY-350 64-color terminal. This generation of terminals quickly replaced
    the earlier WY-100, WY-200 and WY-300.

    The available WY-50 schematics document several revisions of the logic
    board, apparently all functionally equivalent. The earlier version encodes
    character attributes through a slew of TTL gates. A later version
    integrates this logic with a L1A0219 custom gate array (80-435-00), which
    also takes over the address decoding. Both currently dumped sets use this
    second hardware revision (with some minor difference as to the position of
    the beeper). The 80-435-11 gate array from a still later revision also
    generates the dot clock.

    Video memory is contained in two TMS4016-equivalent static RAMs (confirmed
    types include MSM2128-15RS and HM6116P-3). A third 4016-like RAM (usually
    SY2158A-2) is used for the row buffer, with A8-A10 tied to GND.

    To initialize EAROM settings on the WY-50, hold down the G key while
    booting. The equivalent procedure on the WY-75 uses 5 on the numeric
    keypad instead.

*******************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/er1400.h"
#include "machine/scn_pci.h"
#include "wy50kb.h"
#include "sound/beep.h"
#include "video/scn2674.h"
#include "screen.h"
#include "speaker.h"


namespace {

class wy50_state : public driver_device
{
public:
	wy50_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_keyboard(*this, "keyboard")
		, m_earom(*this, "earom")
		, m_pvtc(*this, "pvtc")
		, m_sio(*this, "sio")
		, m_beep(*this, "beep")
		, m_aux(*this, "aux")
		, m_chargen(*this, "chargen")
		, m_videoram(*this, "videoram%u", 0U)
	{
	}

	void wy50(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	u8 pvtc_videoram_r(offs_t offset);
	SCN2672_DRAW_CHARACTER_MEMBER(draw_character);
	void mbc_attr_clock_w(int state);

	u8 pvtc_r(offs_t offset);
	void pvtc_w(offs_t offset, u8 data);
	u8 sio_r(offs_t offset);
	void sio_w(offs_t offset, u8 data);
	u8 rbreg_r();
	void keyboard_w(u8 data);
	void earom_w(u8 data);
	u8 p1_r();
	void p1_w(u8 data);
	u8 p3_r();

	void prg_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
	void row_buffer_map(address_map &map) ATTR_COLD;

	required_device<mcs51_cpu_device> m_maincpu;
	required_device<wy50_keyboard_device> m_keyboard;
	required_device<er1400_device> m_earom;
	required_device<scn2672_device> m_pvtc;
	required_device<scn2661b_device> m_sio;
	required_device<beep_device> m_beep;
	required_device<rs232_port_device> m_aux;

	required_region_ptr<u8> m_chargen;
	required_shared_ptr_array<u8, 2> m_videoram;

	u8 m_cur_attr = 0U;
	u8 m_last_row_attr = 0U;
	u8 m_row_buffer_char = 0U;
	bool m_font2 = false;
	bool m_rev_prot = false;
	bool m_is_132 = false;
};

void wy50_state::machine_start()
{
	m_cur_attr = 0;
	m_last_row_attr = 0;
	m_row_buffer_char = 0;
	m_font2 = false;
	m_rev_prot = false;
	m_is_132 = false;

	save_item(NAME(m_cur_attr));
	save_item(NAME(m_last_row_attr));
	save_item(NAME(m_row_buffer_char));
	save_item(NAME(m_font2));
	save_item(NAME(m_rev_prot));
	save_item(NAME(m_is_132));
}

void wy50_state::machine_reset()
{
	keyboard_w(0);
	earom_w(0);
}

u8 wy50_state::pvtc_videoram_r(offs_t offset)
{
	m_row_buffer_char = m_videoram[BIT(offset, 13)][offset & 0x07ff];
	return m_row_buffer_char;
}

SCN2672_DRAW_CHARACTER_MEMBER(wy50_state::draw_character)
{
	// Attribute bit 0 = Dim
	// Attribute bit 1 = Blink
	// Attribute bit 2 = Blank
	// Attribute bit 3 = Underline
	// Attribute bit 4 = Reverse video

	const bool attr = (charcode & 0xe0) == 0x80;
	const bool prot = (charcode & 0xe0) > 0x80 && !m_font2;
	if (attr)
		m_cur_attr = charcode & 0x1f;
	else if (x == 0)
		m_cur_attr = m_last_row_attr;

	u16 dots = 0;
	if (!attr)
	{
		// Blinking suppresses underline but blank attribute doesn't
		if (!BIT(m_cur_attr, 1) || !blink)
		{
			// Shift register load inhibited by blanking conditions or underline
			if (BIT(m_cur_attr, 3) && ul)
				dots = 0x3ff;
			else if (!BIT(m_cur_attr, 2))
				dots = m_chargen[(charcode & (m_font2 ? 0xff : 0x7f)) << 4 | linecount] << 2;
		}

		// Reverse video for non-attribute characters (XOR of two conditions)
		if (BIT(m_cur_attr, 4) != (prot && m_rev_prot))
			dots = ~dots;
	}
	if (cursor)
		dots = ~dots;

	// Apply dimming conditions
	const rgb_t fg = BIT(m_cur_attr, 0) || (prot && !m_rev_prot) ? rgb_t(0xc0, 0xc0, 0xc0) : rgb_t::white();
	for (int i = 0; i < 9; i++)
	{
		bitmap.pix(y, x++) = BIT(dots, 9) ? fg : rgb_t::black();
		dots <<= 1;
	}
	if (!m_is_132)
		bitmap.pix(y, x++) = BIT(dots, 9) ? fg : rgb_t::black();
}

void wy50_state::mbc_attr_clock_w(int state)
{
	if (state)
		m_last_row_attr = m_cur_attr;
}

u8 wy50_state::pvtc_r(offs_t offset)
{
	return m_pvtc->read(offset >> 8);
}

void wy50_state::pvtc_w(offs_t offset, u8 data)
{
	m_pvtc->write(offset >> 8, data);
}

u8 wy50_state::sio_r(offs_t offset)
{
	return m_sio->read(offset >> 8);
}

void wy50_state::sio_w(offs_t offset, u8 data)
{
	m_sio->write(offset >> 8, data);
}

u8 wy50_state::rbreg_r()
{
	// LS374 row buffer diagnostic register
	return m_row_buffer_char;
}

void wy50_state::keyboard_w(u8 data)
{
	// Bit 0 = J3-6
	// Bit 1 = J3-5
	// Bit 2 = J3-4
	// Bit 3 = J3-7
	// Bit 4 = J3-10
	// Bit 5 = J3-9
	// Bit 6 = J3-8
	// Bit 7 = /HSYNC CLAMP
	m_keyboard->scan_w(data & 0x7f);
}

void wy50_state::earom_w(u8 data)
{
	// Bit 0 = EAROM D
	// Bit 1 = EAROM CLK
	// Bit 2 = EAROM C3
	// Bit 3 = EAROM C2
	// Bit 4 = EAROM C1
	// Bit 5 = UPCHAR/NORM
	m_earom->data_w(BIT(data, 3) ? BIT(data, 0) : 1);
	m_earom->clock_w(BIT(data, 1));
	m_earom->c3_w(BIT(data, 2));
	m_earom->c2_w(BIT(data, 3));
	m_earom->c1_w(BIT(data, 4));
	m_font2 = BIT(data, 5);
}

u8 wy50_state::p1_r()
{
	// P1.0 = AUX RDY (DTR)
	// P1.1 = NVD OUT
	// P1.4 = KEY (inverted, active high)
	return 0xe4 | m_aux->dsr_r() | (m_earom->data_r() << 1) | (m_keyboard->sense_r() ? 0x00 : 0x10);
}

void wy50_state::p1_w(u8 data)
{
	// P1.2 = EXFONT
	// P1.3 = AUX RTS (DSR)
	// P1.5 = BEEPER
	// P1.6 = REV/DIM PROT
	// P1.7 (inverted) = 80/132

	m_aux->write_dtr(BIT(data, 3));

	m_beep->set_state(BIT(data, 5));

	m_rev_prot = BIT(data, 6);

	if (m_is_132 != BIT(data, 7))
	{
		m_is_132 = BIT(data, 7);
		m_pvtc->set_character_width(m_is_132 ? 9 : 10);
		m_pvtc->set_unscaled_clock(68.85_MHz_XTAL / (m_is_132 ? 18 : 30));
	}
}

u8 wy50_state::p3_r()
{
	return m_aux->rxd_r() | 0xfe;
}

void wy50_state::prg_map(address_map &map)
{
	map(0x0000, 0x1fff).rom().region("maincpu", 0);
}

void wy50_state::io_map(address_map &map)
{
	map(0x0000, 0x07ff).mirror(0x1800).ram().share("videoram0");
	map(0x2000, 0x27ff).mirror(0x1800).ram().share("videoram1");
	map(0x4000, 0x47ff).mirror(0x1800).rw(FUNC(wy50_state::pvtc_r), FUNC(wy50_state::pvtc_w));
	map(0x6000, 0x63ff).mirror(0x0c00).r(FUNC(wy50_state::sio_r));
	map(0x7000, 0x73ff).mirror(0x0c00).w(FUNC(wy50_state::sio_w));
	map(0x8000, 0x8000).mirror(0x1fff).r(FUNC(wy50_state::rbreg_r));
	map(0xa000, 0xa000).mirror(0x1fff).w(FUNC(wy50_state::keyboard_w));
	map(0xc000, 0xc000).mirror(0x1fff).w(FUNC(wy50_state::earom_w));
}

void wy50_state::row_buffer_map(address_map &map)
{
	map.global_mask(0x0ff);
	map(0x000, 0x0ff).ram();
}

static INPUT_PORTS_START(wy50)
INPUT_PORTS_END

void wy50_state::wy50(machine_config &config)
{
	I8031(config, m_maincpu, 11_MHz_XTAL); // SAB8031P or SCN8031A
	m_maincpu->set_addrmap(AS_PROGRAM, &wy50_state::prg_map);
	m_maincpu->set_addrmap(AS_IO, &wy50_state::io_map);
	m_maincpu->port_in_cb<1>().set(FUNC(wy50_state::p1_r));
	m_maincpu->port_out_cb<1>().set(FUNC(wy50_state::p1_w));
	m_maincpu->port_in_cb<3>().set(FUNC(wy50_state::p3_r));
	m_maincpu->port_out_cb<3>().set(m_aux, FUNC(rs232_port_device::write_txd)).bit(1);

	WY50_KEYBOARD(config, m_keyboard);

	ER1400(config, m_earom);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_raw(68.85_MHz_XTAL / 3, 102 * 10, 0, 80 * 10, 375, 0, 338);
	//screen.set_raw(68.85_MHz_XTAL / 2, 170 * 9, 0, 132 * 9, 375, 0, 338);
	screen.set_screen_update("pvtc", FUNC(scn2672_device::screen_update));

	SCN2672(config, m_pvtc, 68.85_MHz_XTAL / 30); // SCN2672A or SCN2672B
	m_pvtc->set_screen("screen");
	m_pvtc->set_character_width(10); // 9 in 132-column mode
	m_pvtc->set_addrmap(0, &wy50_state::row_buffer_map);
	m_pvtc->set_display_callback(FUNC(wy50_state::draw_character));
	m_pvtc->intr_callback().set_inputline(m_maincpu, MCS51_T0_LINE);
	m_pvtc->breq_callback().set_inputline(m_maincpu, MCS51_INT0_LINE);
	m_pvtc->mbc_callback().set(FUNC(wy50_state::mbc_attr_clock_w));
	m_pvtc->mbc_char_callback().set(FUNC(wy50_state::pvtc_videoram_r));

	SCN2661B(config, m_sio, 4.9152_MHz_XTAL);
	m_sio->rxrdy_handler().set_inputline(m_maincpu, MCS51_INT1_LINE);
	m_sio->txd_handler().set("modem", FUNC(rs232_port_device::write_txd));
	m_sio->dtr_handler().set("modem", FUNC(rs232_port_device::write_dtr));
	m_sio->rts_handler().set("modem", FUNC(rs232_port_device::write_rts));

	rs232_port_device &modem(RS232_PORT(config, "modem", default_rs232_devices, "loopback"));
	modem.rxd_handler().set(m_sio, FUNC(scn2661b_device::rxd_w));
	modem.cts_handler().set(m_sio, FUNC(scn2661b_device::cts_w));
	modem.dcd_handler().set(m_sio, FUNC(scn2661b_device::dcd_w));

	RS232_PORT(config, m_aux, default_rs232_devices, "loopback");

	SPEAKER(config, "speaker").front_center();
	// Star Micronics QMB06 PZT Buzzer (2048Hz peak) + LC filter, output frequency is approximated here
	BEEP(config, m_beep, 1000).add_route(ALL_OUTPUTS, "speaker", 0.10);
}

ROM_START(wy50)
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("2301_e.u6", 0x0000, 0x2000, CRC(2a62ea25) SHA1(f69c596aab307ef1872df29d353b5a61ff77bb74)) // iFD2764-3

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("2201_b.u16", 0x0000, 0x1000, CRC(ee318814) SHA1(0ac64b60ff978e607a087e9e6f4d547811c015c5)) // 2716
ROM_END

ROM_START(wy75) // 8031, green, 101-key detached keyboard
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("wy75_4001r.bin", 0x0000, 0x2000, CRC(d1e660e0) SHA1(81960e7780b86b9fe338b20d7bd50f7e991020a4))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("wy75_4101a.bin", 0x0000, 0x1000, CRC(96d377db) SHA1(9e059cf067d84267f4e1d92b0509f137fb2ceb19))

	ROM_REGION16_LE(0xc8, "earom", 0)
	ROM_LOAD("default.bin", 0x00, 0xc8, CRC(0efeff07) SHA1(304e07ef87a4b107700273321a1d4e34a56d6821))
ROM_END

} // anonymous namespace


COMP(1984, wy50, 0, 0, wy50, wy50, wy50_state, empty_init, "Wyse Technology", "WY-50 (Rev. E)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP(1984, wy75, 0, 0, wy50, wy50, wy50_state, empty_init, "Wyse Technology", "WY-75 (Rev. H)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
//COMP(1984, wy350, 0, 0, wy50, wy50, wy50_state, empty_init, "Wyse Technology", "WY-350", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



wy55.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

    Skeleton driver for Wyse WY-55 and related terminals.

    The WY-55's custom video gate array is numbered 211019-05. The WY-185 is believed to run on similar hardware, though with
    85 Hz and 60 Hz vertical refresh rates rather than 80 Hz and 70 Hz.

    WY-65's "PELVIS" ASIC (QFP160) supports refresh rates up to 94 Hz.

***********************************************************************************************************************************/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
//#include "machine/ins8250.h"
#include "machine/nvram.h"
#include "machine/scn_pci.h"
#include "screen.h"

namespace {

class wy55_state : public driver_device
{
public:
	wy55_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_epci(*this, "epci")
		, m_screen(*this, "screen")
		, m_progbank(*this, "progbank")
	{
	}

	void wy55(machine_config &config);
	void wy65(machine_config &config);
	void wy185es(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void driver_start() override;

private:
	void wy65_progbank_w(u8 data) { m_progbank->set_entry(data & 0x03); }
	u8 epci_r(offs_t offset) { return m_epci->read(offset >> 10); }
	void epci_w(offs_t offset, u8 data) { m_epci->write(offset >> 10, data); }

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void prog_map(address_map &map) ATTR_COLD;
	void ext_map(address_map &map) ATTR_COLD;
	void wy65_ext_map(address_map &map) ATTR_COLD;
	void wy185es_ext_map(address_map &map) ATTR_COLD;

	required_device<mcs51_cpu_device> m_maincpu;
	optional_device<scn_pci_device> m_epci;
	required_device<screen_device> m_screen;
	required_memory_bank m_progbank;
};


void wy55_state::machine_start()
{
	memory_region *rgn = memregion("program");
	m_progbank->configure_entries(0, rgn->bytes() / 0x10000, rgn->base(), 0x10000);
	m_progbank->set_entry(0);
}

u32 wy55_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void wy55_state::prog_map(address_map &map)
{
	map(0x0000, 0xffff).bankr("progbank");
}

void wy55_state::ext_map(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("nvram0");
	map(0x8000, 0x9fff).ram().share("nvram1");
	map(0xa000, 0xbfff).ram().share("fontram");
	//map(0xf028, 0xf037).rw("uart", FUNC(pc16552_device::read), FUNC(pc16552_device::write));
}

void wy55_state::wy185es_ext_map(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("nvram0");
	map(0x8000, 0x9fff).ram().share("nvram1");
	map(0xa000, 0xbfff).ram().share("fontram");
	map(0xe000, 0xefff).r(FUNC(wy55_state::epci_r));
	map(0xf000, 0xffff).w(FUNC(wy55_state::epci_w));
}

void wy55_state::wy65_ext_map(address_map &map)
{
	map(0x0000, 0xdfff).ram();
	map(0xee02, 0xee02).w(FUNC(wy55_state::wy65_progbank_w));
}


static INPUT_PORTS_START(wy55)
INPUT_PORTS_END

void wy55_state::wy55(machine_config &config)
{
	I80C32(config, m_maincpu, 14.7456_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &wy55_state::prog_map);
	m_maincpu->set_addrmap(AS_IO, &wy55_state::ext_map);
	m_maincpu->port_out_cb<1>().set_membank("progbank").bit(2);

	NVRAM(config, "nvram0", nvram_device::DEFAULT_ALL_0); // 8K SRAM + battery
	NVRAM(config, "nvram1", nvram_device::DEFAULT_ALL_0); // 8K SRAM + battery

	//PC16552D(config, "uart", 14.7456_MHz_XTAL / 2); // 16C452 (divider not verified)

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(49.4235_MHz_XTAL, 1575, 0, 1188, 448, 0, 416);
	//m_screen->set_raw(49.4235_MHz_XTAL * 2 / 3, 1050, 0, 800, 392, 0, 338);
	m_screen->set_screen_update(FUNC(wy55_state::screen_update));
}

void wy55_state::wy185es(machine_config &config)
{
	wy55(config);
	m_maincpu->set_clock(11_MHz_XTAL);
	m_maincpu->set_addrmap(AS_IO, &wy55_state::wy185es_ext_map);
	m_maincpu->port_out_cb<1>().set_nop();

	SCN2661B(config, m_epci, 49.4235_MHz_XTAL / 10); // SCN2661BC1N28
	m_epci->rxrdy_handler().set_inputline(m_maincpu, MCS51_INT1_LINE);
}

void wy55_state::wy65(machine_config &config)
{
	DS80C320(config, m_maincpu, 58.9824_MHz_XTAL / 4); // divider uncertain
	m_maincpu->set_addrmap(AS_PROGRAM, &wy55_state::prog_map);
	m_maincpu->set_addrmap(AS_IO, &wy55_state::wy65_ext_map);

	// TODO: NVRAM? (4x W24257S-70LL on board)

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(58.9824_MHz_XTAL, 1575, 0, 1188, 448, 0, 416); // dimensions probably wrong
	m_screen->set_screen_update(FUNC(wy55_state::screen_update));
}


ROM_START(wy55)
	ROM_REGION(0x20000, "program", 0)
	ROM_LOAD("251352-12.bin", 0x00000, 0x20000, CRC(efe41862) SHA1(52ee76d636b166fa10a37356aef81011a9b079cc)) // v2.1
ROM_END

ROM_START(wy65)
	ROM_REGION(0x40000, "program", 0)
	ROM_LOAD("251455-03.bin", 0x00000, 0x40000, CRC(2afbf73b) SHA1(5a29b78ef377a6e2f2f91ff42a7e4d86eb511a5f)) // v2.1
ROM_END

ROM_START(wy185es)
	ROM_REGION(0x10000, "program", 0)
	ROM_LOAD("251201-03.bin", 0x00000, 0x10000, CRC(5b8cace5) SHA1(484bba8244a99edb80d7f7a5437c2be52c980fc1)) // v2.0
ROM_END

void wy55_state::driver_start()
{
	memory_region *rgn = memregion("program");
	uint8_t *rom = rgn->base();

	for (offs_t base = 0x00000; base < rgn->bytes(); base += 0x4000)
	{
		std::vector<uint8_t> orig(&rom[base], &rom[base + 0x4000]);

		for (offs_t offset = 0; offset < 0x4000; offset++)
			rom[base | offset] = bitswap<8>(orig[bitswap<14>(offset, 3, 8, 2, 0, 7, 4, 9, 10, 11, 12, 13, 5, 1, 6)], 3, 4, 5, 2, 6, 1, 7, 0);
	}
}

} // anonymous namespace

COMP(1991, wy185es, 0, 0, wy185es, wy55, wy55_state, empty_init, "Wyse Technology", "WY-185ES (v2.0)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP(1993, wy55,    0, 0, wy55,    wy55, wy55_state, empty_init, "Wyse Technology", "WY-55 (v2.1)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP(1996, wy65,    0, 0, wy65,    wy55, wy55_state, empty_init, "Wyse Technology", "WY-65 (v2.1)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



wy60.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

    Preliminary driver for Wyse WY-60 terminal.

    If the terminal starts up without valid EEPROM data, it will just display the error code "E" on a mostly blank screen. At this
    point, holding down the Set Up or Select key will cause the EEPROM to be initialized.

***********************************************************************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "bus/wysekbd/wysekbd.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/i2cmem.h"
#include "machine/scn_pci.h"
#include "video/scn2674.h"
#include "screen.h"

namespace {

class wy60_state : public driver_device
{
public:
	wy60_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_eeprom(*this, "eeprom")
		, m_keyboard(*this, "keyboard")
		, m_pvtc(*this, "pvtc")
		, m_sio(*this, "sio")
		, m_aux(*this, "aux")
		, m_charram(*this, "charram")
		, m_attrram(*this, "attrram")
		, m_fontram(*this, "fontram", 0x2000, ENDIANNESS_LITTLE)
		, m_internal_view(*this, "8051_internal")
		, m_char_addr(0)
		, m_is_132(false)
		, m_cur_attr(0)
		, m_last_row_attr(0)
	{
	}

	void wy60(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void driver_start() override;

private:
	SCN2672_DRAW_CHARACTER_MEMBER(draw_character);
	void mbc_attr_clock_w(int state);
	u8 mbc_char_r(offs_t offset);
	u8 mbc_attr_r(offs_t offset);

	u8 pvtc_r(offs_t offset);
	void pvtc_w(offs_t offset, u8 data);
	u8 sio_r(offs_t offset);
	void sio_w(offs_t offset, u8 data);

	void p1_w(u8 data);
	u8 p1_r();
	u8 p3_r();
	void ea_w(int state);

	void prog_map(address_map &map) ATTR_COLD;
	void ext_map(address_map &map) ATTR_COLD;
	void row_buffer_map(address_map &map) ATTR_COLD;

	required_device<i2cmem_device> m_eeprom;
	required_device<wyse_keyboard_port_device> m_keyboard;
	required_device<scn2672_device> m_pvtc;
	required_device<scn2661b_device> m_sio;
	required_device<rs232_port_device> m_aux;
	required_shared_ptr<u8> m_charram;
	required_shared_ptr<u8> m_attrram;
	memory_share_creator<u8> m_fontram;
	memory_view m_internal_view;

	u16 m_char_addr;
	bool m_is_132;
	u8 m_cur_attr;
	u8 m_last_row_attr;
};


void wy60_state::machine_start()
{
	save_item(NAME(m_char_addr));
	save_item(NAME(m_is_132));
	save_item(NAME(m_cur_attr));
	save_item(NAME(m_last_row_attr));
}

SCN2672_DRAW_CHARACTER_MEMBER(wy60_state::draw_character)
{
	// TODO: line attributes
	const u8 screen_attr = m_charram[0x0002];
	if (!BIT(screen_attr, 4))
	{
		if ((charcode & 0xe0) == 0x80)
		{
			// Set nonhidden display attribute and blank the character
			// (90 = blink, 88 = reverse, 84 = underline, 82 = dim, 81 = blank, 80 = normal)
			m_cur_attr = charcode & 0x1f;
			attrcode = 0x01;
		}
		else
		{
			if (x == 0)
				m_cur_attr = m_last_row_attr;
			attrcode = (m_cur_attr & 0x1c) << 1 | (m_cur_attr & 0x03);
		}
	}

	u8 dots = 0;
	if (!BIT(attrcode, 5) || !blink)
	{
		if (BIT(attrcode, 3) && ul)
			dots = 0xff;
		else if (!BIT(attrcode, 0))
		{
			const u16 char_addr = u16(attrcode & 0xc0) << 5 | u16(charcode & 0x7f) << 4 | linecount;
			dots = m_fontram[char_addr];
		}
		if (BIT(attrcode, 4))
			dots = ~dots;
	}

	const bool cur = cursor && (!BIT(screen_attr, 5) || blink) && (!BIT(screen_attr, 7) || ul);
	if (cur)
		dots = ~dots;

	const rgb_t fg = (BIT(attrcode, 1) && !cur) ? rgb_t(0xc0, 0xc0, 0xc0) : rgb_t::white();
	for (int i = 0; i < 7; i++)
	{
		bitmap.pix(y, x++) = BIT(dots, 7) ? fg : rgb_t::black();
		dots <<= 1;
	}
	std::fill_n(&bitmap.pix(y, x), m_is_132 ? 2 : 3, BIT(dots, 7) ? fg : rgb_t::black());
}

void wy60_state::mbc_attr_clock_w(int state)
{
	if (state)
		m_last_row_attr = m_cur_attr;
}

u8 wy60_state::mbc_char_r(offs_t offset)
{
	u8 data = m_charram[offset & 0x1fff];

	// HACK
	if (offset >= 0x0010 && offset < 0x0050 && BIT(m_charram[0x0002], 6))
	{
		if (BIT(offset, 0))
			m_fontram[m_char_addr] = data;
		else
			m_char_addr = u16(m_charram[0x0001] & 0x60) << 6 | u16(data & 0x7f) << 4 | ((offset - 0x0010) & 0x001e) >> 1;
	}

	return data;
}

u8 wy60_state::mbc_attr_r(offs_t offset)
{
	return m_attrram[offset & 0x1fff];
}

u8 wy60_state::pvtc_r(offs_t offset)
{
	return m_pvtc->read(offset >> 8);
}

void wy60_state::pvtc_w(offs_t offset, u8 data)
{
	m_pvtc->write(offset >> 8, data);
}

u8 wy60_state::sio_r(offs_t offset)
{
	return m_sio->read(offset >> 8);
}

void wy60_state::sio_w(offs_t offset, u8 data)
{
	m_sio->write(offset >> 8, data);
}

void wy60_state::p1_w(u8 data)
{
	// P1.0 -> _80/132
	if (BIT(data, 0) != m_is_132)
	{
		m_is_132 = BIT(data, 0);
		m_pvtc->set_character_width(m_is_132 ? 9 : 10);
		m_pvtc->set_unscaled_clock(m_is_132 ? 39.71_MHz_XTAL / 9 : 26.58_MHz_XTAL / 10);
	}

	// P1.1 -> EEPROM SDA
	// P1.2 -> EEPROM SCL
	// N.B. The current i2cmem emulation is a bit sensitive to the order of these writes.
	m_eeprom->write_scl(BIT(data, 2));
	m_eeprom->write_sda(BIT(data, 1));

	// P1.3 -> AUX DSR
	m_aux->write_dtr(BIT(data, 3));

	// P1.5 -> KBD CMD (transmitted through 74LS368)
	m_keyboard->cmd_w(!BIT(data, 5));
}

u8 wy60_state::p1_r()
{
	// P1.1 <- EEPROM SDA
	// P1.4 <- AUX DTR
	// P1.6 <- KBD DATA (received through 74LS368)
	return (m_eeprom->read_sda() << 1) | (m_aux->dsr_r() << 4) | (m_keyboard->data_r() ? 0 : 0x40) | 0xad;
}

u8 wy60_state::p3_r()
{
	return m_aux->rxd_r() | 0xfe;
}

void wy60_state::ea_w(int state)
{
	if (state)
		m_internal_view.select(0);
	else
		m_internal_view.disable();
}

void wy60_state::prog_map(address_map &map)
{
	map(0x0000, 0xffff).rom().region("coderom", 0);
	map(0x0000, 0x0fff).view(m_internal_view); // FIXME: this view should be internal to the 8051 CPU device
	m_internal_view[0](0x0000, 0x0fff).rom().region("maincpu", 0);
}

void wy60_state::ext_map(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("charram");
	map(0x2000, 0x3fff).ram().share("attrram");
	map(0xa000, 0xa7ff).mirror(0x1800).rw(FUNC(wy60_state::pvtc_r), FUNC(wy60_state::pvtc_w));
	map(0xc000, 0xc3ff).mirror(0xc00).r(FUNC(wy60_state::sio_r));
	map(0xd000, 0xd3ff).mirror(0xc00).w(FUNC(wy60_state::sio_w));
}

void wy60_state::row_buffer_map(address_map &map)
{
	map.global_mask(0x0ff);
	map(0x000, 0x0ff).ram();
}


static INPUT_PORTS_START(wy60)
INPUT_PORTS_END

void wy60_state::wy60(machine_config &config)
{
	i8031_device &maincpu(I8031(config, "maincpu", 11_MHz_XTAL)); // AMD P8051AH-40196
	// FIXME: correct device type once 8051 core implements EA pin control
	maincpu.set_addrmap(AS_PROGRAM, &wy60_state::prog_map);
	maincpu.set_addrmap(AS_IO, &wy60_state::ext_map);
	maincpu.port_out_cb<1>().set(FUNC(wy60_state::p1_w));
	maincpu.port_in_cb<1>().set(FUNC(wy60_state::p1_r));
	maincpu.port_out_cb<3>().set(m_aux, FUNC(rs232_port_device::write_txd)).bit(1);
	maincpu.port_out_cb<3>().append(FUNC(wy60_state::ea_w)).bit(5);
	maincpu.port_in_cb<3>().set(FUNC(wy60_state::p3_r));

	I2C_X2404P(config, m_eeprom);

	WYSE_KEYBOARD(config, m_keyboard, wy60_keyboards, "ascii");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(26.58_MHz_XTAL, 1000, 0, 800 + 20, 443, 0, 416 + 16); // 26.580 kHz horizontal
	//screen.set_raw(39.71_MHz_XTAL, 1494, 0, 1188 + 18, 443, 0, 416 + 16);
	screen.set_screen_update("pvtc", FUNC(scn2672_device::screen_update));

	SCN2672(config, m_pvtc, 26.58_MHz_XTAL / 10); // custom-marked as Motorola SC67336P (205001-02)
	m_pvtc->set_screen("screen");
	m_pvtc->set_character_width(10); // 9 in 132-column mode
	m_pvtc->set_addrmap(0, &wy60_state::row_buffer_map);
	m_pvtc->set_addrmap(1, &wy60_state::row_buffer_map);
	m_pvtc->set_display_callback(FUNC(wy60_state::draw_character));
	m_pvtc->intr_callback().set_inputline("maincpu", MCS51_T0_LINE);
	m_pvtc->breq_callback().set_inputline("maincpu", MCS51_INT0_LINE);
	m_pvtc->mbc_callback().set(FUNC(wy60_state::mbc_attr_clock_w));
	m_pvtc->mbc_char_callback().set(FUNC(wy60_state::mbc_char_r));
	m_pvtc->mbc_attr_callback().set(FUNC(wy60_state::mbc_attr_r));

	SCN2661B(config, m_sio, 4.9152_MHz_XTAL);
	m_sio->rxrdy_handler().set_inputline("maincpu", MCS51_INT1_LINE);
	m_sio->txd_handler().set("modem", FUNC(rs232_port_device::write_txd));
	m_sio->dtr_handler().set("modem", FUNC(rs232_port_device::write_dtr));
	m_sio->rts_handler().set("modem", FUNC(rs232_port_device::write_rts));

	rs232_port_device &modem(RS232_PORT(config, "modem", default_rs232_devices, "loopback"));
	modem.rxd_handler().set(m_sio, FUNC(scn2661b_device::rxd_w));
	modem.cts_handler().set(m_sio, FUNC(scn2661b_device::cts_w));
	modem.dcd_handler().set(m_sio, FUNC(scn2661b_device::dcd_w));

	RS232_PORT(config, m_aux, default_rs232_devices, "loopback");
}

// CPU:   8051(202008-03)
// EPROM: 27512(193003-01)
// Video: 211003-02/205001-02
// RAM:   2064 (2064/2016/2016/2064)
// NVRAM: X2404
// UART:  2661
// XTALs: 39.710, 26.580, 11.000, 4.9152

ROM_START(wy60)
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("202008-03_p8051ah-40196.bin", 0x0000, 0x1000, CRC(33a076cb) SHA1(ceb9ae74634b19b0192ed098802c5f551e0ed07f))

	ROM_REGION(0x10000, "coderom", 0)
	ROM_LOAD("193003-01.u9", 0x00000, 0x10000, CRC(26de0ea4) SHA1(91409f98a3990b514fbcb7de2eb45944bf5b95bc))
ROM_END

ROM_START(wy60a)
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_LOAD("202008-03_p8051ah-40196.bin", 0x0000, 0x1000, CRC(33a076cb) SHA1(ceb9ae74634b19b0192ed098802c5f551e0ed07f))

	ROM_REGION(0x10000, "coderom", 0)
	ROM_LOAD("wy-60_4k.u9", 0x00000, 0x10000, CRC(6daf2824) SHA1(23cd039ec7ae71b0742e8eebf75be8cd5992e3fd))
ROM_END

void wy60_state::driver_start()
{
	uint8_t *rom = memregion("coderom")->base();
	for (offs_t base = 0x00000; base < 0x10000; base += 0x2000)
	{
		std::vector<uint8_t> orig(&rom[base], &rom[base + 0x2000]);

		for (offs_t offset = 0; offset < 0x2000; offset++)
			rom[base | offset] = bitswap<8>(orig[bitswap<13>(offset, 0, 6, 9, 4, 2, 1, 3, 5, 7, 8, 10, 11, 12)], 6, 0, 5, 1, 4, 2, 3, 7);
	}
}

} // anonymous namespace

COMP(1986, wy60,  0,    0, wy60, wy60, wy60_state, empty_init, "Wyse Technology", "WY-60 (RBFNG2)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
COMP(1986, wy60a, wy60, 0, wy60, wy60, wy60_state, empty_init, "Wyse Technology", "WY-60 (RBFNB0)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



wy85.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Preliminary driver for Wyse WY-85 VT220-compatible terminal.

    Unlike most later Wyse terminals, the WY-85 lacks a video gate array. It
    also has non-embedded hidden attributes, though they are buffered with the
    characters into the same RAM using a doubled character clock.

*******************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "bus/wysekbd/wysekbd.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/er1400.h"
#include "machine/mc68681.h"
#include "sound/beep.h"
#include "video/scn2674.h"
#include "screen.h"
#include "speaker.h"


namespace {

class wy85_state : public driver_device
{
public:
	wy85_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_earom(*this, "earom")
		, m_kybd(*this, "kybd")
		, m_pvtc(*this, "pvtc")
		, m_duart(*this, "duart")
		, m_beeper(*this, "beeper")
		, m_comm(*this, "comm")
		, m_pr(*this, "pr")
		, m_chargen(*this, "chargen")
		, m_mainram(*this, "mainram")
		, m_clr_rb(false)
		, m_tru_inv(false)
		, m_lc(0)
	{
	}

	void wy85(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	SCN2672_DRAW_CHARACTER_MEMBER(draw_character);

	u8 pvtc_r(offs_t offset);
	void pvtc_w(offs_t offset, u8 data);
	u8 duart_r(offs_t offset);
	void duart_w(offs_t offset, u8 data);
	void earom_w(u8 data);
	u8 font_r();
	void font_w(u8 data);
	u8 utility_r();
	void duart_op_w(u8 data);
	u8 p1_r();
	void p1_w(u8 data);
	u8 p3_r();
	void p3_w(u8 data);

	void prg_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	u8 pvtc_char_r(offs_t offset);
	u8 pvtc_attr_r(offs_t offset);
	u8 pvtc_charbuf_r(offs_t offset);
	void pvtc_charbuf_w(offs_t offset, u8 data);
	u8 pvtc_attrbuf_r(offs_t offset);
	void pvtc_attrbuf_w(offs_t offset, u8 data);

	void char_row_buffer_map(address_map &map) ATTR_COLD;
	void attr_row_buffer_map(address_map &map) ATTR_COLD;

	required_device<mcs51_cpu_device> m_maincpu;
	required_device<er1400_device> m_earom;
	required_device<wyse_keyboard_port_device> m_kybd;
	required_device<scn2672_device> m_pvtc;
	required_device<scn2681_device> m_duart;
	required_device<beep_device> m_beeper;
	required_device<rs232_port_device> m_comm;
	required_device<rs232_port_device> m_pr;

	required_region_ptr<u8> m_chargen;
	required_shared_ptr<u8> m_mainram;

	std::unique_ptr<u8[]> m_rowbuf;
	std::unique_ptr<u8[]> m_fontram;

	bool m_clr_rb;
	bool m_tru_inv;
	u8 m_lc;
};

void wy85_state::machine_start()
{
	m_rowbuf = util::make_unique_clear<u8[]>(0x200);
	m_fontram = util::make_unique_clear<u8[]>(0x800);

	save_pointer(NAME(m_rowbuf), 0x200);
	save_pointer(NAME(m_fontram), 0x800);

	save_item(NAME(m_clr_rb));
	save_item(NAME(m_tru_inv));
	save_item(NAME(m_lc));
}

void wy85_state::machine_reset()
{
	earom_w(0);
}

SCN2672_DRAW_CHARACTER_MEMBER(wy85_state::draw_character)
{
	if (BIT(attrcode, 0, 2) == 0)
	{
		std::fill_n(&bitmap.pix(y, x), 10, rgb_t::black());
		return;
	}

	u8 lc = m_lc + (m_clr_rb ? 0 : linecount);
	rgb_t fg = !BIT(attrcode, 1) || (BIT(attrcode, 4) && blink) ? rgb_t(0xc0, 0xc0, 0xc0) : rgb_t::white();
	bool inv = (BIT(attrcode, 2) != cursor) == m_tru_inv;
	if (BIT(attrcode, 3) && (lc & 9) == 9)
	{
		std::fill_n(&bitmap.pix(y, x), 10, inv ? rgb_t::black() : fg);
		return;
	}

	u16 a = bitswap<2>(attrcode, 5, 6) << 11 | ((charcode & 0x7f) << 4) | (lc & 0xf);
	u16 c = a >= 0x1800 ? m_fontram[a - 0x1800] << 2 : m_chargen[a] << 2;

	if (inv && BIT(c, 2))
		c |= 3;
	if (BIT(attrcode, 0, 2) == 3)
		c |= c >> 1;
	if (inv)
		c ^= 0x3ff;

	for (int i = 0; i < 10; i++)
	{
		bitmap.pix(y, x++) = BIT(c, 9) ? fg : rgb_t::black();
		c <<= 1;
	}
}

u8 wy85_state::pvtc_r(offs_t offset)
{
	return m_pvtc->read(offset >> 8);
}

void wy85_state::pvtc_w(offs_t offset, u8 data)
{
	m_pvtc->write(offset >> 8, data);
}

u8 wy85_state::duart_r(offs_t offset)
{
	return m_duart->read(offset >> 8);
}

void wy85_state::duart_w(offs_t offset, u8 data)
{
	m_duart->write(offset >> 8, data);
}

void wy85_state::earom_w(u8 data)
{
	// SN74LS174N latch + 7406 inverter
	m_earom->clock_w(BIT(data, 1));
	m_earom->c3_w(BIT(data, 2));
	m_earom->c2_w(BIT(data, 3));
	m_earom->c1_w(BIT(data, 4));
	m_earom->data_w(BIT(data, 3) ? BIT(data, 0) : 1);
}

u8 wy85_state::font_r()
{
	if (!m_clr_rb)
	{
		if (!machine().side_effects_disabled())
			logerror("%s: Reading font ROM/RAM with RB enabled\n", machine().describe_context());
		return 0;
	}

	u8 attr = m_rowbuf[0x100];
	if ((attr & 0x60) == 0x60)
		return m_fontram[(m_rowbuf[0] & 0x7f) << 4 | m_lc];
	else
		return m_chargen[bitswap<2>(attr, 5, 6) << 11 | (m_rowbuf[0] & 0x7f) << 4 | m_lc];
}

void wy85_state::font_w(u8 data)
{
	if (!m_clr_rb)
	{
		if (!machine().side_effects_disabled())
			logerror("%s: Writing to font RAM with RB enabled\n", machine().describe_context());
		return;
	}

	if ((m_rowbuf[0x100] & 0x60) == 0x60)
		m_fontram[(m_rowbuf[0] & 0x7f) << 4 | m_lc] = data;

	//logerror("%s: font_w %02X (CLR RB = %d, LC = %d, char[0] = %02X, attr[0] = %02X)\n", machine().describe_context(), data, m_clr_rb, m_lc, m_rowbuf[0], m_rowbuf[0x100]);
}

u8 wy85_state::utility_r()
{
	// D0 = HSYNC
	// D1 = not connected?
	// D2 = NVD OUT
	// D3 = KEY DATA
	return (m_earom->data_r() << 2) | (!m_kybd->data_r() << 3);
}

void wy85_state::duart_op_w(u8 data)
{
	// OP0 = RTS
	// OP1 = DTR
	// OP2-OP5 = L0-L3 reload
	// OP6 = SPDS
	// OP7 = character width select

	m_comm->write_rts(BIT(data, 0));
	m_comm->write_dtr(BIT(data, 1));
	m_comm->write_spds(BIT(data, 6));

	m_lc = BIT(data, 2, 4);
}

u8 wy85_state::p1_r()
{
	// P1.4 = AUX DSR
	return m_pr->dsr_r() ? 0xff : 0xef;
}

void wy85_state::p1_w(u8 data)
{
	// P1.0 = AUX DTR
	// P1.1 = CLR RB
	// P1.2 = pass-through?
	// P1.3 = AUX RTS
	// P1.5 = BEEPER
	// P1.6 = TRU INV
	// P1.7 = 80/132 column select

	m_pr->write_dtr(BIT(data, 0));
	m_pr->write_rts(BIT(data, 3));

	m_clr_rb = !BIT(data, 1);
	m_tru_inv = BIT(data, 6);

	m_beeper->set_state(BIT(data, 5));
}

u8 wy85_state::p3_r()
{
	return 0xfe | m_pr->rxd_r();
}

void wy85_state::p3_w(u8 data)
{
	// P3.1 (TXD) = AUX TXD
	// P3.5 (T1) = KEY OUT

	m_pr->write_txd(BIT(data, 1));
	m_kybd->cmd_w(!BIT(data, 5));
}

void wy85_state::prg_map(address_map &map)
{
	map(0x0000, 0x3fff).mirror(0xc000).rom().region("maincpu", 0);
}

void wy85_state::io_map(address_map &map)
{
	map(0x0000, 0x1fff).ram().share("mainram"); // 4x HM6116P-3 (with 2 more for the row buffer and font RAM)
	map(0x2000, 0x2000).mirror(0x1fff).w(FUNC(wy85_state::earom_w));
	map(0x4000, 0x4000).select(0x700).mirror(0x18ff).rw(FUNC(wy85_state::pvtc_r), FUNC(wy85_state::pvtc_w));
	map(0x6000, 0x6000).select(0xf00).mirror(0x10ff).rw(FUNC(wy85_state::duart_r), FUNC(wy85_state::duart_w));
	map(0x8000, 0x8000).mirror(0x1fff).rw(FUNC(wy85_state::font_r), FUNC(wy85_state::font_w));
	map(0xa000, 0xa000).mirror(0x1fff).r(FUNC(wy85_state::utility_r));
}

u8 wy85_state::pvtc_char_r(offs_t offset)
{
	return m_mainram[offset & 0xfff];
}

u8 wy85_state::pvtc_attr_r(offs_t offset)
{
	return m_mainram[0x1000 + (offset & 0xfff)];
}

u8 wy85_state::pvtc_charbuf_r(offs_t offset)
{
	return m_rowbuf[m_clr_rb ? 0 : (offset + 1) & 0xff];
}

void wy85_state::pvtc_charbuf_w(offs_t offset, u8 data)
{
	m_rowbuf[m_clr_rb ? 0 : (offset + 1) & 0xff] = data;
}

u8 wy85_state::pvtc_attrbuf_r(offs_t offset)
{
	return m_rowbuf[0x100 + (m_clr_rb ? 0 : (offset + 1) & 0xff)];
}

void wy85_state::pvtc_attrbuf_w(offs_t offset, u8 data)
{
	m_rowbuf[0x100 + (m_clr_rb ? 0 : (offset + 1) & 0xff)] = data;
}

void wy85_state::char_row_buffer_map(address_map &map)
{
	map.global_mask(0x0ff);
	map(0x000, 0x0ff).rw(FUNC(wy85_state::pvtc_charbuf_r), FUNC(wy85_state::pvtc_charbuf_w));
}

void wy85_state::attr_row_buffer_map(address_map &map)
{
	map.global_mask(0x0ff);
	map(0x000, 0x0ff).rw(FUNC(wy85_state::pvtc_attrbuf_r), FUNC(wy85_state::pvtc_attrbuf_w));
}

static INPUT_PORTS_START(wy85)
INPUT_PORTS_END

void wy85_state::wy85(machine_config &config)
{
	I8032(config, m_maincpu, 11_MHz_XTAL); // SCN8032H
	m_maincpu->set_addrmap(AS_PROGRAM, &wy85_state::prg_map);
	m_maincpu->set_addrmap(AS_IO, &wy85_state::io_map);
	m_maincpu->port_in_cb<1>().set(FUNC(wy85_state::p1_r));
	m_maincpu->port_out_cb<1>().set(FUNC(wy85_state::p1_w));
	m_maincpu->port_in_cb<3>().set(FUNC(wy85_state::p3_r));
	m_maincpu->port_out_cb<3>().set(FUNC(wy85_state::p3_w));

	ER1400(config, m_earom); // M5G1400

	WYSE_KEYBOARD(config, m_kybd, wy85_keyboards, "wy85");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_raw(48.5568_MHz_XTAL / 3, 96 * 10, 0, 80 * 10, 281, 0, 260);
	//screen.set_raw(48.5568_MHz_XTAL / 2, 160 * 9, 0, 132 * 9, 281, 0, 260);
	screen.set_screen_update("pvtc", FUNC(scn2672_device::screen_update));

	SCN2672(config, m_pvtc, 48.5568_MHz_XTAL / 30); // SCN2672B
	m_pvtc->set_screen("screen");
	m_pvtc->set_character_width(10); // 9 in 132-column mode
	m_pvtc->set_addrmap(0, &wy85_state::char_row_buffer_map);
	m_pvtc->set_addrmap(1, &wy85_state::attr_row_buffer_map);
	m_pvtc->set_display_callback(FUNC(wy85_state::draw_character));
	m_pvtc->intr_callback().set_inputline(m_maincpu, MCS51_T0_LINE);
	m_pvtc->breq_callback().set_inputline(m_maincpu, MCS51_INT0_LINE);
	m_pvtc->mbc_char_callback().set(FUNC(wy85_state::pvtc_char_r));
	m_pvtc->mbc_attr_callback().set(FUNC(wy85_state::pvtc_attr_r));

	SCN2681(config, m_duart, 3.6864_MHz_XTAL); // SCN2681A (+ 4x µA9636ATC drivers and UA9639CP receivers)
	m_duart->a_tx_cb().set(m_comm, FUNC(rs232_port_device::write_txd));
	m_duart->b_tx_cb().set("20ma", FUNC(rs232_port_device::write_txd));
	m_duart->outport_cb().set(FUNC(wy85_state::duart_op_w));
	m_duart->irq_cb().set_inputline(m_maincpu, MCS51_INT1_LINE);

	SPEAKER(config, "speaker").front_center();
	BEEP(config, m_beeper, 1000).add_route(ALL_OUTPUTS, "speaker", 0.10); // FIXME: not accurate; actually uses same circuit as WY-50 with 74LS14 Schmitt triggers and discrete components

	RS232_PORT(config, m_comm, default_rs232_devices, nullptr); // RS423 port, also RS232 compatible
	m_comm->rxd_handler().set(m_duart, FUNC(scn2681_device::rx_a_w));
	m_comm->cts_handler().set(m_duart, FUNC(scn2681_device::ip0_w));
	m_comm->dsr_handler().set(m_duart, FUNC(scn2681_device::ip1_w));
	m_comm->dcd_handler().set(m_duart, FUNC(scn2681_device::ip2_w));
	m_comm->si_handler().set(m_duart, FUNC(scn2681_device::ip4_w));

	RS232_PORT(config, m_pr, default_rs232_devices, nullptr); // RS423 port, also RS232 compatible

	RS232_PORT(config, "20ma", default_rs232_devices, nullptr) // 20mA current loop, not actually RS232 compatible
				.rxd_handler().set(m_duart, FUNC(scn2681_device::rx_b_w));
}

ROM_START(wy85)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_SYSTEM_BIOS(0, "revd", "Rev. D")
	ROMX_LOAD("23-121-r7d.5e", 0x0000, 0x4000, CRC(76203960) SHA1(ea58c7337435edb06d2d5434886a67045e96bf1f), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "reva", "Rev. A")
	ROMX_LOAD("250151-04_reva.5e", 0x0000, 0x4000, CRC(8fcb9f43) SHA1(6c7e1d27fa6014870c29ab2b8b856ae412bfc411), ROM_BIOS(1)) // 27128

	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD("am9265.1h", 0x0000, 0x2000, CRC(5ee65b55) SHA1(a0b38a38838f262aaea22d212351e7441e4b07e8)) // AM9265EPC
ROM_END

} // anonymous namespace


COMP(1985, wy85, 0, 0, wy85, wy85, wy85_state, empty_init, "Wyse Technology", "WY-85", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND)



x07.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/***************************************************************************

    Canon X-07

    Driver by Sandro Ronco based on X-07 emu by J.Brigaud

    TODO:
    - move T6834 in a device
    - better emulation of the i/o ports
    - external video (need X-720 dump)
    - serial port

    Memory Map

    0x0000 - 0x1fff   Internal RAM
    0x2000 - 0x3fff   External RAM Card
    0x4000 - 0x5fff   Extension ROM/RAM
    0x6000 - 0x7fff   ROM Card
    0x8000 - 0x97ff   Video RAM
    0x9800 - 0x9fff   ?
    0xa000 - 0xafff   TV ROM (no dump)
    0xb000 - 0xffff   ROM

    CPU was actually a NSC800 (Z80 compatible)
    More info: http://www.silicium.org/oldskool/calc/x07/

****************************************************************************/

#include "emu.h"
#include "x07.h"

#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"

#include <algorithm>


/***************************************************************************
    T6834 IMPLEMENTATION
***************************************************************************/

void x07_state::t6834_cmd (uint8_t cmd)
{
	switch (cmd)
	{
	case 0x00:  //NOP???
		break;

	case 0x01:  //DATA$ TIME$ read
		{
			system_time systime;
			machine().current_datetime(systime);
			m_out.data[m_out.write++] = (systime.local_time.year>>8) & 0xff;
			m_out.data[m_out.write++] = systime.local_time.year & 0xff;
			m_out.data[m_out.write++] = systime.local_time.month + 1;
			m_out.data[m_out.write++] = systime.local_time.mday;
			m_out.data[m_out.write++] = ~(((0x01 << (7 - systime.local_time.weekday)) - 1) & 0xff);
			m_out.data[m_out.write++] = systime.local_time.hour;
			m_out.data[m_out.write++] = systime.local_time.minute;
			m_out.data[m_out.write++] = systime.local_time.second;
		}
		break;

	case 0x02:  //STICK
		{
			uint8_t data;

			switch (ioport("S1")->read() & 0x3c)
			{
				case 0x04:      data = 0x33;    break;  //right
				case 0x08:      data = 0x37;    break;  //left
				case 0x10:      data = 0x31;    break;  //up
				case 0x20:      data = 0x35;    break;  //down
				default:        data = 0x30;    break;
			}
			m_out.data[m_out.write++] = data;
		}
		break;

	case 0x03:  //STRIG(0)
		{
			m_out.data[m_out.write++] = (ioport("S6")->read() & 0x20 ? 0x00 : 0xff);
		}
		break;

	case 0x04:  //STRIG(1)
		{
			m_out.data[m_out.write++] = (ioport("S1")->read() & 0x40 ? 0x00 : 0xff);
		}
		break;

	case 0x05:  //T6834 RAM read
		{
			uint16_t address;
			uint8_t data;
			address = m_in.data[m_in.read++];
			address |= (m_in.data[m_in.read++] << 8);

			if(address == 0xc00e)
				data = 0x0a;
			else if(address == 0xd000)
				data = ioport("BATTERY")->read();
			else
				data = m_t6834_ram[address & 0x7ff];

			m_out.data[m_out.write++] = data;
		}
		break;

	case 0x06:  //T6834 RAM write
		{
			uint16_t address;
			uint8_t data;
			data = m_in.data[m_in.read++];
			address = m_in.data[m_in.read++];
			address |= (m_in.data[m_in.read++] << 8);

			m_t6834_ram[address & 0x7ff] = data;
		}
		break;

	case 0x07:  //scroll set
		{
			m_scroll_min = m_in.data[m_in.read++];
			m_scroll_max = m_in.data[m_in.read++];
		}
		break;

	case 0x08:  //scroll exec
		{
			if(m_scroll_min <= m_scroll_max && m_scroll_max < 4)
			{
				for(int i = m_scroll_min * 8; i < m_scroll_max * 8; i++)
					memcpy(&m_lcd_map[i][0], &m_lcd_map[i + 8][0], 120);

				for(int i = m_scroll_max * 8; i < (m_scroll_max + 1) * 8; i++)
					memset(&m_lcd_map[i][0], 0, 120);
			}
		}
		break;

	case 0x09:  //line clear
		{
			uint8_t line = m_in.data[m_in.read++] & 3;
			for(uint8_t l = line * 8; l < (line + 1) * 8; l++)
				memset(&m_lcd_map[l][0], 0, 120);
		}
		break;

	case 0x0a:  //DATA$ TIME$ write
		break;

	case 0x0b:  //calendar
		{
				system_time systime;
				machine().current_datetime(systime);
				m_out.data[m_out.write++] = systime.local_time.weekday;
		}
		break;

	case 0x0c:  //ALM$ write
		{
			for(auto & elem : m_alarm)
				elem = m_in.data[m_in.read++];
		}
		break;

	case 0x0d:  //buzzer on
	case 0x0e:  //buzzer off
		break;

	case 0x0f:  //read LCD line
		{
			uint8_t line = m_in.data[m_in.read++];
			for(int i = 0; i < 120; i++)
				m_out.data[m_out.write++] = (line < 32) ? m_lcd_map[line][i] : 0;
		}
		break;

	case 0x10:  //read LCD point
		{
			uint8_t x = m_in.data[m_in.read++];
			uint8_t y = m_in.data[m_in.read++];
			if(x < 120 && y < 32)
				m_out.data[m_out.write++] = (m_lcd_map[y][x] ? 0xff : 0);
			else
				m_out.data[m_out.write++] = 0;
		}
		break;

	case 0x11:  //PSET
		{
			uint8_t x = m_in.data[m_in.read++];
			uint8_t y = m_in.data[m_in.read++];
			draw_point(x, y, 1);
		}
		break;

	case 0x12:  //PRESET
		{
			uint8_t x = m_in.data[m_in.read++];
			uint8_t y = m_in.data[m_in.read++];
			draw_point(x, y, 0);
		}
		break;

	case 0x13:  //PEOR
		{
			uint8_t x = m_in.data[m_in.read++];
			uint8_t y = m_in.data[m_in.read++];
			if(x < 120 && y < 32)
				m_lcd_map[y][x] = !m_lcd_map[y][x];
		}
		break;

	case 0x14:  //Line
		{
			uint8_t delta_x, delta_y, step_x, step_y, next_x, next_y, p1, p2, p3, p4;
			int16_t frac;
			next_x = p1 = m_in.data[m_in.read++];
			next_y = p2 = m_in.data[m_in.read++];
			p3 = m_in.data[m_in.read++];
			p4 = m_in.data[m_in.read++];
			delta_x = abs(p3 - p1) * 2;
			delta_y = abs(p4 - p2) * 2;
			step_x = (p3 < p1) ? -1 : 1;
			step_y = (p4 < p2) ? -1 : 1;

			if(delta_x > delta_y)
			{
				frac = delta_y - delta_x / 2;
				while(next_x != p3)
				{
					if(frac >= 0)
					{
						next_y += step_y;
						frac -= delta_x;
					}
					next_x += step_x;
					frac += delta_y;
					draw_point(next_x, next_y, 0x01);
				}
			}
			else {
				frac = delta_x - delta_y / 2;
				while(next_y != p4)
				{
					if(frac >= 0)
					{
						next_x += step_x;
						frac -= delta_y;
					}
					next_y += step_y;
					frac += delta_x;
					draw_point(next_x, next_y, 0x01);
				}
			}
			draw_point(p1, p2, 0x01);
			draw_point(p3, p4, 0x01);
		}
		break;

	case 0x15:  //Circle
		{
			uint8_t p1 = m_in.data[m_in.read++];
			uint8_t p2 = m_in.data[m_in.read++];
			uint8_t p3 = m_in.data[m_in.read++];

			for(int x = 0, y = p3; x <= sqrt((double)(p3 * p3) / 2) ; x++)
			{
				/*
				 * The old code produced results most likely not intended:
				 * uint32_t d1 = (x * x + y * y) - p3 * p3;
				 * uint32_t d2 = (x * x + (y - 1) * (y - 1)) - p3 * p3;
				 * if(abs((double)d1) > abs((double)d2))
				 *
				 * (double)(-1) = 4294967294.000000
				 * abs((double)(-1)) = -2147483648;
				 *
				 * Therefore changed.
				 */
				int32_t d1 = (x * x + y * y) - p3 * p3;
				int32_t d2 = (x * x + (y - 1) * (y - 1)) - p3 * p3;
				if (abs(d1) > abs(d2))
					y--;
				draw_point(x + p1, y + p2, 0x01);
				draw_point(x + p1, -y + p2, 0x01);
				draw_point(-x + p1, y + p2, 0x01);
				draw_point(-x + p1, -y + p2, 0x01);
				draw_point(y + p1, x + p2, 0x01);
				draw_point(y + p1, -x + p2, 0x01);
				draw_point(-y + p1, x + p2, 0x01);
				draw_point(-y + p1, -x + p2, 0x01);
			}
		}
		break;

	case 0x16:  //UDK write
		{
			uint8_t pos = m_in.data[m_in.read++] - 1;
			uint8_t udk_size = (pos != 5 && pos != 11) ? 0x2a : 0x2e;

			for(int i = 0; i < udk_size; i++)
			{
				uint8_t udk_char = m_in.data[m_in.read++];
				m_t6834_ram[udk_offset[pos] + i] = udk_char;
				if(!udk_char)   break;
			}
		}
		break;

	case 0x17:  //UDK read
		{
			uint8_t pos = m_in.data[m_in.read++] - 1;
			uint8_t udk_size = (pos != 5 && pos != 11) ? 0x2a : 0x2e;

			for(int i = 0; i < udk_size; i++)
			{
				uint8_t udk_char = m_t6834_ram[udk_offset[pos] + i];
				m_out.data[m_out.write++] = udk_char;
				if(!udk_char)   break;
			}
		}
		break;

	case 0x18:  //UDK on
	case 0x19:  //UDK off
		m_udk_on = !BIT(cmd,0);
		break;

	case 0x1a:  //UDC write
		{
			uint8_t udc_code = m_in.data[m_in.read++];

			if(udc_code>=128 && udc_code<=159)
				for(int i = 0; i < 8; i++)
					m_t6834_ram[(udc_code<<3) + i - 0x200] = m_in.data[m_in.read++];
			else if(udc_code>=224)
				for(int i = 0; i < 8; i++)
					m_t6834_ram[(udc_code<<3) + i - 0x400] = m_in.data[m_in.read++];
		}
		break;

	case 0x1b:  //UDC read
		{
			uint16_t address = m_in.data[m_in.read++] << 3;
			for(int i = 0; i < 8; i++)
				m_out.data[m_out.write++] = get_char(address + i);
		}
		break;
	case 0x1c:  //UDC Init
		{
			memcpy(m_t6834_ram + 0x200, (uint8_t*)memregion("gfx1")->base() + 0x400, 0x100);
			memcpy(m_t6834_ram + 0x300, (uint8_t*)memregion("gfx1")->base() + 0x700, 0x100);
		}
		break;

	case 0x1d:  //start program write
		{
			for(int i = 0; i < 0x80; i++)
			{
				uint8_t sp_char = m_in.data[m_in.read++];
				m_t6834_ram[0x500 + i] = sp_char;
				if (!sp_char) break;
			}
		}
		break;

	case 0x1e:  //start program write cont
		{
			for(int i = (int)strlen((char*)&m_t6834_ram[0x500]); i < 0x80; i++)
			{
				uint8_t sp_char = m_in.data[m_in.read++];
				m_t6834_ram[0x500 + i] = sp_char;
				if (!sp_char) break;
			}
		}
		break;

	case 0x1f:  //start program on
	case 0x20:  //start program off
		m_sp_on = BIT(cmd, 0);
		break;

	case 0x21:  //start program read
		{
			for(int i = 0; i < 0x80; i++)
			{
				uint8_t sp_data = m_t6834_ram[0x500 + i];
				m_out.data[m_out.write++] = sp_data;
				if (!sp_data) break;
			}
		}
		break;

	case 0x22: //ON state
		m_out.data[m_out.write++] = 0x04 | (m_sleep<<6) | m_warm_start;
		break;

	case 0x23:  //OFF
		m_warm_start = 1;
		m_sleep = 0;
		m_lcd_on = 0;
		break;

	case 0x24:  //locate
		{
			uint8_t x = m_in.data[m_in.read++];
			uint8_t y = m_in.data[m_in.read++];
			uint8_t char_code = m_in.data[m_in.read++];
			m_locate.on = (m_locate.x != x || m_locate.y != y);
			m_locate.x = m_cursor.x = x;
			m_locate.y = m_cursor.y = y;

			if(char_code)
				draw_char(x, y, char_code);
		}
		break;

	case 0x25:  //cursor on
	case 0x26:  //cursor off
		m_cursor.on = BIT(cmd, 0);
		break;

	case 0x27:  //test key
		{
			static const char *const lines[] = {"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "BZ", "A1"};
			uint16_t matrix;
			uint8_t data = 0;
			matrix = m_in.data[m_in.read++];
			matrix |= (m_in.data[m_in.read++] << 8);

			for (int i=0 ;i<10; i++)
				if (matrix & (1<<i))
					data |= ioport(lines[i])->read();

			m_out.data[m_out.write++] = data;
		}
		break;

	case 0x28:  //test chr
		{
			uint8_t idx = kb_get_index(m_in.data[m_in.read++]);
			m_out.data[m_out.write++] = (ioport(x07_keycodes[idx].tag)->read() & x07_keycodes[idx].mask) ? 0x00 : 0xff;
		}
		break;

	case 0x29:  //init sec
	case 0x2a:  //init date
		break;

	case 0x2b:  //LCD off
	case 0x2c:  //LCD on
		m_lcd_on = !BIT(cmd,0);
		break;

	case 0x2d:  //KB buffer clear
		memset(m_t6834_ram + 0x400, 0, 0x100);
		m_kb_size = 0;
		break;

	case 0x2e:  //CLS
		memset(m_lcd_map, 0, sizeof(m_lcd_map));
		break;

	case 0x2f:  //home
		m_cursor.x = m_cursor.y = 0;
		break;

	case 0x30:  //draw UDK on
	case 0x31:  //draw UDK off
		{
			m_draw_udk = !BIT(cmd,0);

			if (m_draw_udk)
				draw_udk();
			else
				for(uint8_t l = 3 * 8; l < (3 + 1) * 8; l++)
					memset(&m_lcd_map[l][0], 0, 120);
		}
		break;

	case 0x32:  //repeat key on
	case 0x33:  //repeat key off
		m_repeat_key = !BIT(cmd,0);
		break;

	case 0x34:  //UDK KANA
		break;

	case 0x35:  //UDK cont write
		{
			uint8_t pos = m_in.data[m_in.read++] - 1;
			uint8_t udk_size = (pos != 5 && pos != 11) ? 0x2a : 0x2e;

			for(int i = (int)strlen((char*)&m_t6834_ram[udk_offset[pos]]); i < udk_size; i++)
			{
				uint8_t udk_char = m_in.data[m_in.read++];
				m_t6834_ram[udk_offset[pos] + i] = udk_char;
				if(!udk_char)   break;
			}
		}
		break;

	case 0x36:  //alarm read
		{
			for(auto & elem : m_alarm)
				m_out.data[m_out.write++] = elem;
		}
		break;

	case 0x37: // buzzer zero
		m_out.data[m_out.write++] = 0xff;
		break;

	case 0x38:  //click off
	case 0x39:  //click on
		break;

	case 0x3a:  //Locate Close
		break;

	case 0x3b: // keyboard on
	case 0x3c: // keyboard off
		m_kb_on = BIT(cmd, 0);
		break;

	case 0x3d:  //run start program after power on
	case 0x3e:  //run start program before power off
		break;

	case 0x3f: //Sleep
		m_warm_start = 1;
		m_lcd_on = 0;
		m_sleep = 1;
		break;

	case 0x40:  //UDK init
		{
			memset(m_t6834_ram, 0, 0x200);
			for(int i = 0; i < 12; i++)
				strcpy((char*)m_t6834_ram + udk_offset[i], udk_ini[i]);
		}
		break;

	case 0x41:  //char wrire
		{
			for(int cy = 0; cy < 8; cy++)
			{
				uint8_t cl = m_in.data[m_in.read++];

				for(int cx = 0; cx < 6; cx++)
					m_lcd_map[m_cursor.y * 8 + cy][m_cursor.x * 6 + cx] = (cl & (0x80>>cx)) ? 1 : 0;
			}
		}
		break;

	case 0x42: //char read
		{
			for(int cy = 0; cy < 8; cy++)
			{
				uint8_t cl = 0x00;

				for(int cx = 0; cx < 6; cx++)
					cl |= (m_lcd_map[m_cursor.y * 8 + cy][m_cursor.x * 6 + cx] != 0) ? (1<<(7-cx)) : 0;

				m_out.data[m_out.write++] = cl;
			}
		}
		break;

	case 0x43:  //ScanR
	case 0x44:  //ScanL
		{
			m_out.data[m_out.write++] = 0;
			m_out.data[m_out.write++] = 0;
		}
		break;

	case 0x45:  //TimeChk
	case 0x46:  //AlmChk
		m_out.data[m_out.write++] = 0;
		break;
	default:
		logerror( "T6834 unimplemented command %02x encountered\n", cmd );
	}
}


void x07_state::t6834_r ()
{
	m_out.read++;
	m_regs_r[2] &= 0xfe;
	if(m_out.write > m_out.read)
	{
		m_regs_r[0]  = 0x40;
		m_regs_r[1] = m_out.data[m_out.read];
		m_regs_r[2] |= 0x01;
		m_maincpu->set_input_line(NSC800_RSTA, ASSERT_LINE);
		m_rsta_clear->adjust(attotime::from_msec(50));
	}
}


void x07_state::t6834_w ()
{
	if (!m_in.write)
	{
		if (m_locate.on && ((m_regs_w[1] & 0x7F) != 0x24) && ((m_regs_w[1]) >= 0x20) && ((m_regs_w[1]) < 0x80))
		{
			m_cursor.x++;
			draw_char(m_cursor.x, m_cursor.y, m_regs_w[1]);
		}
		else
		{
			m_locate.on = 0;

			if ((m_regs_w[1] & 0x7f) < 0x47)
			{
				m_in.data[m_in.write++] = m_regs_w[1] & 0x7f;
			}
		}
	}
	else
	{
		m_in.data[m_in.write++] = m_regs_w[1];

		if (m_in.write == 2)
		{
			if (m_in.data[m_in.read] == 0x0c && m_regs_w [1] == 0xb0)
			{
				memset(m_lcd_map, 0, sizeof(m_lcd_map));
				m_in.write = 0;
				m_in.read = 0;
				m_in.data[m_in.write++] = m_regs_w[1] & 0x7f;
			}

			if (m_in.data[m_in.read] == 0x07 && m_regs_w [1] > 4)
			{
				m_in.write = 0;
				m_in.read = 0;
				m_in.data[m_in.write++] = m_regs_w[1] & 0x7f;
			}
		}
	}

	if (m_in.write)
	{
		uint8_t cmd_len = t6834_cmd_len[m_in.data[m_in.read]];
		if(cmd_len & 0x80)
		{
			if((cmd_len & 0x7f) < m_in.write && !m_regs_w[1])
				cmd_len = m_in.write;
		}

		if(m_in.write == cmd_len)
		{
			m_out.write = 0;
			m_out.read = 0;
			t6834_cmd(m_in.data[m_in.read++]);
			m_in.write = 0;
			m_in.read = 0;
			if(m_out.write)
			{
				m_regs_r[0]  = 0x40;
				m_regs_r[1] = m_out.data[m_out.read];
				m_regs_r[2] |= 0x01;
				m_maincpu->set_input_line(NSC800_RSTA, ASSERT_LINE);
				m_rsta_clear->adjust(attotime::from_msec(50));
			}
		}
	}
}

void x07_state::cassette_r()
{
	m_regs_r[6] &= ~2;
}

void x07_state::cassette_w()
{
	m_regs_r[6] &= ~1;
	m_cass_data = m_regs_w[7];
}

TIMER_CALLBACK_MEMBER(x07_state::cassette_tick)
{
	m_cass_clk++;
}

TIMER_CALLBACK_MEMBER(x07_state::cassette_poll)
{
	if ((m_cassette->get_state() & 0x03) == CASSETTE_PLAY)
		cassette_load();
	else if ((m_cassette->get_state() & 0x03) == CASSETTE_RECORD)
		cassette_save();
}

void x07_state::cassette_load()
{
	int cass = (m_cassette->input() >= 0) ? +1 : -1;
	if (cass > 0 && m_cass_state < 0)
	{
		if ((m_cass_clk & 0x7f) >= 4 && (m_cass_clk & 0x7f) <= 6)
		{
			if (m_cass_clk & 0x80)
			{
				m_cass_clk = 0;
				receive_bit(1);
			}
			else
			{
				m_cass_clk = 0x80;
			}
		}
		else if ((m_cass_clk & 0x7f) >= 9 && (m_cass_clk & 0x7f) <= 11)
		{
			m_cass_clk = 0;
			receive_bit(0);
		}
		else
		{
			m_cass_clk = 0;
			logerror("Invalid data: %d %f\n", (m_cass_clk & 0x7f), m_cassette->get_position());
		}

		m_cass_tick->adjust(attotime::from_hz(12000), 0, attotime::from_hz(12000));
	}

	m_cass_state = cass;
}


void x07_state::cassette_save()
{
	int cass = m_cass_state;

	if (m_cass_clk % 10 == 0)
	{
		if (m_bit_count < 4)
		{
			switch (m_bit_count & 3)
			{
				case 0:     case 1:     cass = +1;  break;
				case 2:     case 3:     cass = -1;  break;
			}

			m_bit_count++;
		}
		else if (m_bit_count < 36)
		{
			switch (m_bit_count & 3)
			{
				case 0:     cass = +1;  break;
				case 1:     cass = (m_cass_data & 1) ? -1 : +1; break;
				case 2:     cass = (m_cass_data & 1) ? +1 : -1; break;
				case 3:     cass = -1;  m_cass_data >>= 1;      break;
			}

			m_bit_count++;
		}
		else if (m_bit_count < 48)
		{
			switch (m_bit_count & 3)
			{
				case 0:     case 2:     cass = +1;  break;
				case 1:     case 3:     cass = -1;  break;
			}

			if (m_bit_count == 47)
				m_regs_r[6] |= 1;

			m_bit_count++;
		}
		else
		{
			cass = (m_cass_state > 0) ? -1 : +1;
		}

		m_cassette->output( cass );
	}

	// finish the current cycle before start the next
	if ((m_cass_state <= 0) && !(m_regs_r[6] & 1) && m_bit_count >= 48)
		m_bit_count = 0;

	m_cass_state = cass;
	m_cass_clk++;
}

void x07_state::receive_bit(int bit)
{
	if (m_bit_count == 0)
	{
		// wait for start bit
		if (bit == 0)
			m_bit_count++;
	}
	else if (m_bit_count < 9)
	{
		m_cass_data = (m_cass_data>>1) | (bit<<7);
		m_bit_count++;
	}
	else if (m_bit_count < 12)
	{
		if (bit != 1)
			logerror("Invalid stop bit: %f\n", m_cassette->get_position());

		m_bit_count++;
	}

	// every byte take 12 bit
	if (m_bit_count == 12)
	{
		//printf("data: %02x %f\n", m_cass_data, m_cassette->get_position());
		m_regs_r[6] |= 2;
		m_regs_r[7] = m_cass_data;
		m_cass_data = 0;
		m_bit_count = 0;

		m_maincpu->set_input_line(NSC800_RSTB, ASSERT_LINE);
		m_rstb_clear->adjust(attotime::from_usec(200));

	}
}


/****************************************************
    this function emulate the color printer X-710
    only the text functions are emulated
****************************************************/
void x07_state::printer_w()
{
	uint16_t char_pos = 0;
//  uint16_t text_color = 0;
//  uint16_t text_size = 1;

	if (m_regs_r[4] & 0x20)
		m_prn_char_code |= 1;

	m_prn_sendbit++;

	if (m_prn_sendbit == 8)
	{
		if (m_prn_char_code)
		{
			m_prn_buffer[m_prn_size++] = m_prn_char_code;

			if (m_prn_buffer[m_prn_size - 2] == 0x4f && m_prn_buffer[m_prn_size - 1] == 0xaf)
			{
				if (m_prn_buffer[0] == 0xff && m_prn_buffer[1] == 0xb7)
				{
					for (int i = 2; i < m_prn_size - 2; i++)
					{
/*
                        if (m_prn_buffer[i - 1] == 0x4f && m_prn_buffer[i] == 0x3d)
                            text_color = printer_charcode[m_prn_buffer[i + 1]] - 0x30;

                        if (m_prn_buffer[i - 1] == 0x4f && m_prn_buffer[i] == 0x35)
                        {
                            if (m_prn_buffer[i + 2] == 0x4f)
                                text_size = printer_charcode[m_prn_buffer[i + 1]] - 0x2f;
                            else
                                text_size = 0x0a + (printer_charcode[m_prn_buffer[i + 2]] - 0x2f);
                        }
*/
						if (m_prn_buffer[i - 1] == 0x4f && m_prn_buffer[i] == 0x77)
						{
							char_pos = i + 1 ;
							break;
						}
					}
				}

				//send the chars to the printer, color and size are not used
				for (int i = char_pos ;i < m_prn_size ; i++)
					m_printer->output(printer_charcode[m_prn_buffer[i]]);

				//clears the print buffer
				memset(m_prn_buffer, 0, sizeof(m_prn_buffer));
				m_prn_size = 0;
			}
		}

		m_prn_sendbit = 0;
		m_prn_char_code = 0;
		m_regs_r[2] |= 0x80;
	}
	else
		m_prn_char_code <<= 1;
}

inline uint8_t x07_state::kb_get_index(uint8_t char_code)
{
	for(uint8_t i=0 ; i< std::size(x07_keycodes); i++)
		if (x07_keycodes[i].codes[0] == char_code)
			return i;

	return 0;
}

inline uint8_t x07_state::get_char(uint16_t pos)
{
	uint8_t code = pos>>3;

	if(code>=128 && code<=159)      //UDC 0
	{
		return m_t6834_ram[pos - 0x200];
	}
	else if(code>=224)              //UDC 1
	{
		return m_t6834_ram[pos - 0x400];
	}
	else                            //charset
	{
		return memregion("gfx1")->base()[pos];
	}
}

INPUT_CHANGED_MEMBER( x07_state::kb_func_keys )
{
	uint8_t data = 0;
	uint8_t idx = (uint8_t)param;

	if (m_kb_on && newval)
	{
		uint8_t shift = (ioport("A1")->read() & 0x01);
		uint16_t udk_s = udk_offset[(shift*6) +  idx - 1];

		/* First 3 chars are used for description */
		udk_s += 3;

		do
		{
			data = m_t6834_ram[udk_s++];

			if (m_kb_size < 0xff && data != 0)
				m_t6834_ram[0x400 + m_kb_size++] = data;
		} while(data != 0);

		kb_irq();
	}
}

INPUT_CHANGED_MEMBER( x07_state::kb_keys )
{
	uint8_t modifier;
	uint8_t a1 = ioport("A1")->read();
	uint8_t bz = ioport("BZ")->read();
	uint8_t keycode = (uint8_t)param;

	if (m_kb_on && !newval)
	{
		if (a1 == 0x01 && bz == 0x00)           //Shift
			modifier = 1;
		else if (a1 == 0x02 && bz == 0x00)      //CTRL
			modifier = 2;
		else if (a1 == 0x00 && bz == 0x08)      //Num
			modifier = 3;
		else if (a1 == 0x00 && bz == 0x02)      //Kana
			modifier = 4;
		else if (a1 == 0x01 && bz == 0x02)      //Shift+Kana
			modifier = 5;
		else if (a1 == 0x00 && bz == 0x04)      //Graph
			modifier = 6;
		else
			modifier = 0;

		if (m_kb_size < 0xff)
		{
			uint8_t idx = kb_get_index(keycode);
			m_t6834_ram[0x400 + m_kb_size++] = x07_keycodes[idx].codes[modifier];
		}

		kb_irq();
	}
}

INPUT_CHANGED_MEMBER( x07_state::kb_update_udk )
{
	draw_udk();
}

INPUT_CHANGED_MEMBER( x07_state::kb_break )
{
	if (newval)
	{
		if (!m_lcd_on)
		{
			m_lcd_on = 1;
			m_maincpu->set_state_int(Z80_PC, 0xc3c3);
		}
		else
		{
			m_regs_r[0] = 0x80;
			m_regs_r[1] = 0x05;
			m_regs_r[2] |= 0x01;
			m_maincpu->set_input_line(NSC800_RSTA, ASSERT_LINE );
			m_rsta_clear->adjust(attotime::from_msec(50));
		}
	}
}


void x07_state::kb_irq()
{
	if (m_kb_size)
	{
		m_regs_r[0] = 0;
		m_regs_r[1] = m_t6834_ram[0x400];
		memmove(m_t6834_ram + 0x400, m_t6834_ram + 0x401, 0xff);
		m_kb_size--;
		m_regs_r[2] |= 0x01;
		m_maincpu->set_input_line(NSC800_RSTA, ASSERT_LINE);
		m_rsta_clear->adjust(attotime::from_msec(50));
	}
}


/***************************************************************************
    Video
***************************************************************************/

inline void x07_state::draw_char(uint8_t x, uint8_t y, uint8_t char_pos)
{
	if(x < 20 && y < 4)
		for(int cy = 0; cy < 8; cy++)
			for(int cx = 0; cx < 6; cx++)
				m_lcd_map[y * 8 + cy][x * 6 + cx] = (get_char(((char_pos << 3) + cy) & 0x7ff) & (0x80>>cx)) ? 1 : 0;
}


inline void x07_state::draw_point(uint8_t x, uint8_t y, uint8_t color)
{
	if(x < 120 && y < 32)
		m_lcd_map[y][x] = color;
}


inline void x07_state::draw_udk()
{
	uint8_t i, x, j;

	if (m_draw_udk)
		for(i = 0, x = 0; i < 5; i++)
		{
			uint16_t ofs = udk_offset[i + ((ioport("A1")->read()&0x01) ? 6 : 0)];
			draw_char(x++, 3, 0x83);
			for(j = 0; j < 3; j++)
				draw_char(x++, 3, m_t6834_ram[ofs++]);
		}
}

DEVICE_IMAGE_LOAD_MEMBER( x07_state::card_load )
{
	uint32_t size = m_card->common_get_size("rom");

	// check card type
	if (image.loaded_through_softlist())
	{
		const char *card_type = image.get_feature("card_type");

		if (strcmp(card_type, "xp140"))
			return std::make_pair(image_error::BADSOFTWARE, "Unsupported card type");
	}

	m_card->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_BIG);
	m_card->common_load_rom(m_card->get_rom_base(), size, "rom");

	m_card->ram_alloc(0x1000);

	return std::make_pair(std::error_condition(), std::string());
}

void x07_state::x07_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(92, 83, 88));
}


uint32_t x07_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	bitmap.fill(0);

	if (m_lcd_on)
	{
		for(int py = 0; py < 4; py++)
			for(int px = 0; px < 20; px++)
				for(int y = 0; y < 8; y++)
					for (int x=0; x<6; x++)
						if(m_cursor.on && m_blink && m_cursor.x == px && m_cursor.y == py)
							bitmap.pix(py * 8 + y, px * 6 + x) = (y == 7) ? 1: 0;
						else
							bitmap.pix(py * 8 + y, px * 6 + x) = m_lcd_map[py * 8 + y][px * 6 + x]? 1: 0;

	}

	return 0;
}


/***************************************************************************
    Machine
***************************************************************************/

uint8_t x07_state::x07_io_r(offs_t offset)
{
	uint8_t data = 0xff;

	switch(offset)
	{
	case 0x80:
	case 0x81:
	case 0x82:
	case 0x83:
	case 0x84:
	case 0x85:
	case 0x86:
	case 0x87:
	case 0x88:
	case 0x89:
	case 0x8a:
	case 0x8b:
	case 0x8c:
		data = ((offset & 0x0f) < 8) ? get_char((m_font_code << 3) | (offset & 7)) : 0;
		break;

	case 0x90:
		data = 0x00;
		break;
	case 0xf6:
		if (m_cass_motor)   m_regs_r[6] |= 4;
		[[fallthrough]];
	case 0xf0:
	case 0xf1:
	case 0xf3:
	case 0xf4:
	case 0xf5:
	case 0xf7:
		data = m_regs_r[offset & 7];
		break;

	case 0xf2:
		if(m_regs_w[5] & 4)
			m_regs_r[2] |= 2;
		else
			m_regs_r[2] &= 0xfd;
		data = m_regs_r[2] | 2;
		break;
	}

	return data;
}


void x07_state::x07_io_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
	case 0x80:
		m_font_code = data;
		break;

	case 0xf0:
	case 0xf1:
	case 0xf2:
	case 0xf3:
	case 0xf6:
	case 0xf7:
		m_regs_w[offset & 7] = data;
		break;

	case 0xf4:
		m_regs_r[4] = m_regs_w[4] = data;
		m_cass_motor = ((data & 0x0d) == 0x09) ? 1 : 0;

		if (m_cass_motor)
		{
			m_cassette->change_state(CASSETTE_MOTOR_ENABLED, CASSETTE_MASK_MOTOR);
			m_cass_poll->adjust(attotime::from_hz(48000), 0, attotime::from_hz(48000));
		}
		else
		{
			m_cassette->change_state(CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
			m_cass_poll->reset();
			m_cass_tick->reset();
		}

		if((data & 0x0e) == 0x0e)
		{
			uint16_t div = (m_regs_w[2] | m_regs_w[3] << 8) & 0x0fff;
			m_beep->set_clock((div == 0) ? 0 : 192000 / div);
			m_beep->set_state(1);

			m_beep_stop->adjust(attotime::from_msec(m_ram->pointer()[0x450] * 0x20));
		}
		else
			m_beep->set_state(0);
		break;

	case 0xf5:
		if(data & 0x01)
			t6834_r();
		if(data & 0x02)
			t6834_w();
		if(data & 0x04)
			cassette_r();
		if(data & 0x08)
			cassette_w();
		if(data & 0x20)
			printer_w();

		m_regs_w[5] = data;
		break;
	}
}

void x07_state::x07_mem(address_map &map)
{
	map.unmap_value_low();
	map(0x0000, 0x1fff).noprw();     //RAM installed at runtime
	map(0x2000, 0x7fff).noprw();     //Memory Card RAM/ROM
	map(0x8000, 0x97ff).ram();     //TV VRAM
	map(0x9800, 0x9fff).unmaprw();   //unused/unknown
	map(0xa000, 0xafff).rom().region("x720", 0);        //TV ROM
	map(0xb000, 0xffff).rom().region("basic", 0);       //BASIC ROM
}

void x07_state::x07_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0xff).rw(FUNC(x07_state::x07_io_r), FUNC(x07_state::x07_io_w));
}

/* Input ports */
static INPUT_PORTS_START( x07 )
	PORT_START("BATTERY")
		PORT_CONFNAME( 0x70, 0x30, "Battery Status" )
		PORT_CONFSETTING( 0x30, DEF_STR( Normal ) )
		PORT_CONFSETTING( 0x40, "Low Battery" )
	PORT_START("CARDBATTERY")
		PORT_CONFNAME( 0x10, 0x00, "Card Battery Status" )
		PORT_CONFSETTING( 0x00, DEF_STR( Normal ) )
		PORT_CONFSETTING( 0x10, "Low Battery" )

	PORT_START("S1")
		PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("INS")     PORT_CODE(KEYCODE_INSERT)           PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x12)
		PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DEL")     PORT_CODE(KEYCODE_DEL)              PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x16)
		PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RIGHT")   PORT_CODE(KEYCODE_RIGHT)            PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x1c)
		PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("LEFT")    PORT_CODE(KEYCODE_LEFT)             PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x1d)
		PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("UP")      PORT_CODE(KEYCODE_UP)               PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x1e)
		PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DOWN")    PORT_CODE(KEYCODE_DOWN)             PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x1f)
		PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SPC")     PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x20)
	PORT_START("S2")
		PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x5a)
		PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x58)
		PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x43)
		PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x56)
		PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x42)
		PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x4e)
		PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x4d)
		PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x2c)
	PORT_START("S3")
		PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x41)
		PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x53)
		PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x44)
		PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x46)
		PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x47)
		PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x48)
		PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x4a)
		PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x4b)
	PORT_START("S4")
		PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x51)
		PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x57)
		PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x45)
		PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x52)
		PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x54)
		PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x59)
		PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x55)
		PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x49)
	PORT_START("S5")
		PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x31)
		PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x32)
		PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x33)
		PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x34)
		PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x35)
		PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x36)
		PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')      PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x37)
		PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x38)
	PORT_START("S6")
		PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)                    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_func_keys), 1)
		PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)                    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_func_keys), 2)
		PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F3") PORT_CODE(KEYCODE_F3)                    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_func_keys), 3)
		PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F4") PORT_CODE(KEYCODE_F4)                    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_func_keys), 4)
		PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F5") PORT_CODE(KEYCODE_F5)                    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_func_keys), 5)
		PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F6") PORT_CODE(KEYCODE_F6)                    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_func_keys), 6)
	PORT_START("S7")
		PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x2e)
		PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x2f)
		PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PGUP) PORT_CHAR('?')                   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x3f)
		PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER)  PORT_CHAR(13)  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x0d)
		PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x4f)
		PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x50)
		PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR('@') PORT_CHAR('\'')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x40)
		PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x5b)
	PORT_START("S8")
		PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x4c)
		PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(';') PORT_CHAR('+')   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x3b)
		PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x3a)
		PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x5d)
		PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x39)
		PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('|')       PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x30)
		PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')   PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x2d)
		PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') PORT_CHAR('`')  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x3d)
	PORT_START("BZ")
		PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("HOME")    PORT_CODE(KEYCODE_HOME)             PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_keys), 0x0b)
		PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("KANA")    PORT_CODE(KEYCODE_RALT)
		PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("GRPH")    PORT_CODE(KEYCODE_RCONTROL)
		PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("NUM")     PORT_CODE(KEYCODE_LALT)
		PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("OFF")     PORT_CODE(KEYCODE_RSHIFT)
		PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("ON/BREAK") PORT_CODE(KEYCODE_F10)             PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_break), 0)
	PORT_START("A1")
		PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT)             PORT_CHAR(UCHAR_SHIFT_1)    PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x07_state::kb_update_udk), 0)
		PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL)
INPUT_PORTS_END


void x07_state::nvram_init(nvram_device &nvram, void *data, size_t size)
{
	memcpy(data, memregion("default")->base(), size);
	m_warm_start = 0;
}

TIMER_DEVICE_CALLBACK_MEMBER(x07_state::blink_timer)
{
	m_blink = !m_blink;
}

TIMER_CALLBACK_MEMBER(x07_state::rsta_clear)
{
	m_maincpu->set_input_line(NSC800_RSTA, CLEAR_LINE);

	if (m_kb_size)
		kb_irq();
}

TIMER_CALLBACK_MEMBER(x07_state::rstb_clear)
{
	m_maincpu->set_input_line(NSC800_RSTB, CLEAR_LINE);
}

TIMER_CALLBACK_MEMBER(x07_state::beep_stop)
{
	m_beep->set_state(0);
}

static const gfx_layout x07_charlayout =
{
	6, 8,                   /* 6 x 8 characters */
	256,                    /* 256 characters */
	1,                      /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	{ 0, 1, 2, 3, 4, 5},
	{ 0, 8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8},
	8*8                     /* 8 bytes */
};

static GFXDECODE_START( gfx_x07 )
	GFXDECODE_ENTRY( "gfx1", 0x0000, x07_charlayout, 0, 1 )
GFXDECODE_END

void x07_state::machine_start()
{
	uint32_t ram_size = m_ram->size();
	m_rsta_clear = timer_alloc(FUNC(x07_state::rsta_clear), this);
	m_rstb_clear = timer_alloc(FUNC(x07_state::rstb_clear), this);
	m_beep_stop = timer_alloc(FUNC(x07_state::beep_stop), this);
	m_cass_poll = timer_alloc(FUNC(x07_state::cassette_poll), this);
	m_cass_tick = timer_alloc(FUNC(x07_state::cassette_tick), this);

	m_nvram1->set_base(&m_t6834_ram, 0x800);
	m_nvram2->set_base(m_ram->pointer(), ram_size);

	/* Save State */
	save_item(NAME(m_sleep));
	save_item(NAME(m_warm_start));
	save_item(NAME(m_udk_on));
	save_item(NAME(m_draw_udk));
	save_item(NAME(m_sp_on));
	save_item(NAME(m_font_code));
	save_item(NAME(m_lcd_on));
	save_item(NAME(m_scroll_min));
	save_item(NAME(m_scroll_max));
	save_item(NAME(m_blink));
	save_item(NAME(m_kb_on));
	save_item(NAME(m_repeat_key));
	save_item(NAME(m_kb_size));
	save_item(NAME(m_prn_sendbit));
	save_item(NAME(m_prn_char_code));
	save_item(NAME(m_prn_size));
	save_item(NAME(m_cass_motor));
	save_item(NAME(m_cass_data));
	save_item(NAME(m_cass_clk));
	save_item(NAME(m_cass_state));
	save_item(NAME(m_bit_count));
	save_item(NAME(m_t6834_ram));
	save_item(NAME(m_regs_r));
	save_item(NAME(m_regs_w));
	save_item(NAME(m_alarm));
	save_item(NAME(m_lcd_map));
	save_item(NAME(m_prn_buffer));
	save_item(NAME(m_in.read));
	save_item(NAME(m_in.write));
	save_item(NAME(m_in.data));
	save_item(NAME(m_out.read));
	save_item(NAME(m_out.write));
	save_item(NAME(m_out.data));
	save_item(NAME(m_locate.x));
	save_item(NAME(m_locate.y));
	save_item(NAME(m_locate.on));
	save_item(NAME(m_cursor.x));
	save_item(NAME(m_cursor.y));
	save_item(NAME(m_cursor.on));

	// install RAM
	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_ram(0x0000, ram_size - 1, m_ram->pointer());

	// card
	if (m_card->exists())
	{
		// 0x4000 - 0x4fff   4KB RAM
		// 0x6000 - 0x7fff   8KB ROM
		program.install_read_handler(ram_size, ram_size + 0xfff, read8sm_delegate(*m_card, FUNC(generic_slot_device::read_ram)));
		program.install_write_handler(ram_size, ram_size + 0xfff, write8sm_delegate(*m_card, FUNC(generic_slot_device::write_ram)));
		program.install_read_handler(0x6000, 0x7fff, read8sm_delegate(*m_card, FUNC(generic_slot_device::read_rom)));

		m_card->save_ram();
	}
}

void x07_state::machine_reset()
{
	memset(m_regs_r, 0, sizeof(m_regs_r));
	memset(m_regs_w, 0, sizeof(m_regs_w));
	memset(m_alarm, 0, sizeof(m_alarm));
	std::fill(std::begin(m_in.data), std::end(m_in.data), 0);
	m_in.read = m_in.write = 0;
	std::fill(std::begin(m_out.data), std::end(m_out.data), 0);
	m_out.read = m_out.write = 0;
	m_locate = lcd_position();
	m_cursor = lcd_position();
	memset(m_prn_buffer, 0, sizeof(m_prn_buffer));
	memset(m_lcd_map, 0, sizeof(m_lcd_map));

	m_sleep = 0;
	m_udk_on = 0;
	m_draw_udk = 0;
	m_sp_on = 0;
	m_font_code = 0;
	m_lcd_on = 1;
	m_scroll_min = 0;
	m_scroll_max = 3;
	m_blink = 0;
	m_kb_on = 0;
	m_repeat_key = 0;
	m_kb_size = 0;
	m_repeat_key = 0;
	m_prn_sendbit = 0;
	m_prn_char_code = 0;
	m_prn_size = 0;

	m_regs_r[2] = ioport("CARDBATTERY")->read();

	m_maincpu->set_state_int(Z80_PC, 0xc3c3);
}

void x07_state::x07(machine_config &config)
{
	/* basic machine hardware */
	NSC800(config, m_maincpu, 15.36_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &x07_state::x07_mem);
	m_maincpu->set_addrmap(AS_IO, &x07_state::x07_io);

	/* video hardware */
	screen_device &lcd(SCREEN(config, "lcd", SCREEN_TYPE_LCD));
	lcd.set_refresh_hz(60);
	lcd.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	lcd.set_screen_update(FUNC(x07_state::screen_update));
	lcd.set_size(120, 32);
	lcd.set_visarea(0, 120-1, 0, 32-1);
	lcd.set_palette("palette");

	PALETTE(config, "palette", FUNC(x07_state::x07_palette), 2);
	GFXDECODE(config, "gfxdecode", "palette", gfx_x07);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, "beeper", 0).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* printer */
	PRINTER(config, m_printer, 0);

	TIMER(config, "blink_timer").configure_periodic(FUNC(x07_state::blink_timer), attotime::from_msec(300));

	NVRAM(config, "nvram1").set_custom_handler(FUNC(x07_state::nvram_init));   // t6834 RAM
	NVRAM(config, "nvram2", nvram_device::DEFAULT_ALL_0); // RAM banks

	/* internal ram */
	// 8KB  no expansion
	// 12KB XM-100
	// 16KB XR-100 or XM-101
	// 20KB XR-100 and XM-100
	// 24KB XR-100 and XM-101
	RAM(config, RAM_TAG).set_default_size("16K").set_extra_options("8K,12K,20K,24K");

	/* Memory Card */
	GENERIC_CARTSLOT(config, "cardslot", generic_romram_plain_slot, "x07_card", "rom,bin").set_device_load(FUNC(x07_state::card_load));

	/* cassette */
	CASSETTE(config, m_cassette);
	m_cassette->set_formats(x07_cassette_formats);
	m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("x07_cass");

	/* Software lists */
	SOFTWARE_LIST(config, "card_list").set_original("x07_card");
	SOFTWARE_LIST(config, "cass_list").set_original("x07_cass");
}

/* ROM definition */
ROM_START( x07 )
	ROM_REGION( 0x6000, "basic", ROMREGION_ERASEFF )
	ROM_LOAD( "x07.bin",  0x0000, 0x5001, BAD_DUMP CRC(61a6e3cc) SHA1(c53c22d33085ac7d5e490c5d8f41207729e5f08a) )       //very strange size...

	ROM_REGION( 0x1000, "x720", ROMREGION_ERASEFF )
	ROM_LOAD( "x720.bin", 0x0000, 0x1000, NO_DUMP )

	ROM_REGION( 0x0800, "gfx1", 0 )
	ROM_LOAD( "charset.rom", 0x0000, 0x0800, BAD_DUMP CRC(b1e59a6e) SHA1(b0c06315a2d5c940a8f288fb6a3428d738696e69) )

	ROM_REGION( 0x0800, "default", ROMREGION_ERASE00 )
ROM_END

void x07_state::init_x07()
{
	uint8_t *RAM = memregion("default")->base();
	uint8_t *GFX = memregion("gfx1")->base();

	for (int i = 0; i < 12; i++)
		strcpy((char *)RAM + udk_offset[i], udk_ini[i]);

	//copy default chars in the UDC
	memcpy(RAM + 0x200, GFX + 0x400, 0x100);
	memcpy(RAM + 0x300, GFX + 0x700, 0x100);
}


/* Driver */

/*    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT      COMPANY  FULLNAME  FLAGS */
COMP( 1983, x07,  0,      0,      x07,     x07,   x07_state, init_x07, "Canon", "X-07",   MACHINE_SUPPORTS_SAVE)



x1.cpp
<---------------------------------------------------------------------->
// license:LGPL-2.1+
// copyright-holders: Angelo Salese, Barry Rodewald
// thanks-to: Dirk Best
/************************************************************************************************

Sharp X1 (c) 1983 Sharp Corporation

References:
- http://www.x1center.org/
- http://ematei.s602.xrea.com/kenkyu/x1syasin.htm
- http://www2s.biglobe.ne.jp/~ITTO/x1/x1menu.html
- https://eaw.app/sharpx1-manuals/
- https://www.leadedsolder.com/tag/sharp-x1-turbo
- http://takeda-toshiya.my.coocan.jp/x1twin/index.html
- https://monochromeeffect.org/JVCC/2019/05/01/sharp-x1-turbo-z/
- https://monochromeeffect.org/JVCC/2019/06/24/sharp-x1-d/

TODO:
- clean-up QA, is ugly and outdated;
- clean-ups, split components into devices if necessary and maybe separate turbo/turboz features into specific file(s);
- refactor base video into a true scanline renderer, expect it to break 6845 drawing delegation support badly;
- support extended x1turboz video features (need more test cases?);
- Rewrite keyboard input hook-up and decap/dump the keyboard MCU if possible;
- Fix the 0xe80/0xe83 kanji ROM readback;
- x1turbo keyboard inputs are currently broken, use x1turbo40 for now;
- Hook-up remaining .tap image formats;
- Implement APSS tape commands;
- Sort out / redump the BIOS gfx roms, and understand if TurboZ really have same BIOS as
    vanilla Turbo like Jp emulators seems to suggest;
- X1Turbo: Implement SIO.
- Implement true 400 lines mode (i.e. Chatnoir no Mahjong v2.1, Casablanca)
- Implement SASI HDD interface;
- Driver Configuration switches:
    - OPN for X1
    - EMM, and hook-up for X1 too
    - RAM size for EMM
    - specific x1turboz features?

per-game/program specific TODO (to be moved to hash file):
- Might & Magic: uses 0xe80-3 kanji ports, should be a good test case for that;
- Saziri: doesn't re-initialize the tilemap attribute vram when you start a play, making it to have missing colors if you don't start a play in time;
- Super Billiards (X1 Pack 14): has a slight PCG timing bug, that happens randomly;
- Trivia-Q: dunno what to do on the selection screen, missing inputs?
- Ys 2: crashes after the disclaimer screen;
- Ys 3: missing user disk, to hack it (and play with x1turboz features): bp 81ca,pc += 2
- Ys 3: never uploads a valid 4096 palette, probably related to the fact that we don't have an user disk

Notes:
- An interesting feature of the Sharp X-1 is the extended i/o bank. When the ppi port c bit 5
    does a 1->0 transition, any write to the i/o space accesses 2 or 3 banks gradients of the bitmap RAM
    with a single write (generally used for layer clearances and bitmap-style sprites).
    Any i/o read disables this extended bitmap ram.
- ROM format header (TODO: document, it's a full on expansion slot *inside* the machine, BASIC variant known to exist):
    [0x00] ROM identifier, must be 0x01 / SOH
    [0x01 to 0x0d] ROM header, i.e. title for the loader
    [0x12 -  0x13] initial copy size
    [0x14 -  0x15] destination address start address
    [0x16 to 0x17] start boot jump vector
    [0x1d to 0x1f] start boot data vector
- Maidum: you need to load BOTH disk with write protection disabled, otherwise it refuses to run. (btanb)
- Marvelous: needs write protection disabled (btanb)
- Chack'n Pop: to load this game, do a files command on the "Jodan Dos" prompt then move the cursor up at the "Chack'n Pop" file.
    Substitute bin with load and press enter. Finally, do a run once that it loaded correctly.
- Faeries Residence: to load this game, put a basic v2.0 in drive 0, then execute a NEWON command. Load game disks into drive 0 and 1 then
    type run"START" (case sensitive)
- POPLEMON: same as above, but you need to type run"POP"

=================================================================================================

    X1 (CZ-800C) - November, 1982
     * CPU: z80A @ 4MHz, 80C49 x 2 (one for key scan, the other for TV & Cas Ctrl)
     * ROM: IPL (4KB) + chargen (2KB)
     * RAM: Main memory (64KB) + VRAM (4KB) + RAM for PCG (6KB) + GRAM (48KB, Option)
     * Text Mode: 80x25 or 40x25
     * Graphic Mode: 640x200 or 320x200, 8 colors
     * Sound: PSG 8 octave
     * I/O Ports: Centronic ports, 2 Joystick ports, Cassette port (2700 baud)

    X1C (CZ-801C) - October, 1983
     * same but only 48KB GRAM

    X1D (CZ-802C) - October, 1983
     * same as X1C but with a 3" floppy drive (notice: 3" not 3" 1/2!!)

    X1Cs (CZ-803C) - June, 1984
     * two expansion I/O ports

    X1Ck (CZ-804C) - June, 1984
     * same as X1Cs
     * ROM: IPL (4KB) + chargen (2KB) + Kanji 1st level

    X1F Model 10 (CZ-811C) - July, 1985
     * Re-designed
     * ROM: IPL (4KB) + chargen (2KB)

    X1F Model 20 (CZ-812C) - July, 1985
     * Re-designed (same as Model 10)
     * ROM: IPL (4KB) + chargen (2KB) + Kanji
     * Built Tape drive plus a 5" floppy drive was available

    X1G Model 10 (CZ-820C) - July, 1986
     * Re-designed again
     * ROM: IPL (4KB) + chargen (2KB)

    X1G Model 30 (CZ-822C) - July, 1986
     * Re-designed again (same as Model 10)
     * ROM: IPL (4KB) + chargen (2KB) + Kanji
     * Built Tape drive plus a 5" floppy drive was available

    X1twin (CZ-830C) - December, 1986
     * Re-designed again (same as Model 10)
     * ROM: IPL (4KB) + chargen (2KB) + Kanji
     * Built Tape drive plus a 5" floppy drive was available
     * It contains a PC-Engine

    =============  X1 Turbo series  =============

    X1turbo Model 30 (CZ-852C) - October, 1984
     * CPU: z80A @ 4MHz, 80C49 x 2 (one for key scan, the other for TV & Cas Ctrl)
     * ROM: IPL (32KB) + chargen (8KB) + Kanji (128KB)
     * RAM: Main memory (64KB) + VRAM (6KB) + RAM for PCG (6KB) + GRAM (96KB)
     * Text Mode: 80xCh or 40xCh with Ch = 10, 12, 20, 25 (same for Japanese display)
     * Graphic Mode: 640x200 or 320x200, 8 colors
     * Sound: PSG 8 octave
     * I/O Ports: Centronic ports, 2 Joystick ports, built-in Cassette interface,
        2 Floppy drive for 5" disks, two expansion I/O ports

    X1turbo Model 20 (CZ-851C) - October, 1984
     * same as Model 30, but only 1 Floppy drive is included

    X1turbo Model 10 (CZ-850C) - October, 1984
     * same as Model 30, but Floppy drive is optional and GRAM is 48KB (it can
        be expanded to 96KB however)

    X1turbo Model 40 (CZ-862C) - July, 1985
     * same as Model 30, but uses tv screen (you could watch television with this)

    X1turboII (CZ-856C) - November, 1985
     * same as Model 30, but restyled, cheaper and sold with utility software

    X1turboIII (CZ-870C) - November, 1986
     * with two High Density Floppy driver

    X1turboZ (CZ-880C) - December, 1986
     * CPU: z80A @ 4MHz, 80C49 x 2 (one for key scan, the other for TV & Cas Ctrl)
     * ROM: IPL (32KB) + chargen (8KB) + Kanji 1st & 2nd level
     * RAM: Main memory (64KB) + VRAM (6KB) + RAM for PCG (6KB) + GRAM (96KB)
     * Text Mode: 80xCh or 40xCh with Ch = 10, 12, 20, 25 (same for Japanese display)
     * Graphic Mode: 640x200 or 320x200, 8 colors [in compatibility mode],
        640x400, 8 colors (out of 4096); 320x400, 64 colors (out of 4096);
        320x200, 4096 colors [in multimode],
     * Sound: PSG 8 octave + FM 8 octave
     * I/O Ports: Centronic ports, 2 Joystick ports, built-in Cassette interface,
        2 Floppy drive for HD 5" disks, two expansion I/O ports

    X1turboZII (CZ-881C) - December, 1987
     * same as turboZ, but added 64KB expansion RAM

    X1turboZIII (CZ-888C) - December, 1988
     * same as turboZII, but no more built-in cassette drive

    BASIC has to be loaded from external media (tape or disk), the
    computer only has an Initial Program Loader (IPL)

=================================================================================================

    x1turbo specs (courtesy of Yasuhiro Ogawa):

    upper board: Z80A-CPU
                 Z80A-DMA
                 Z80A-SIO(O)
                 Z80A-CTC
                 uPD8255AC
                 LH5357(28pin mask ROM. for IPL?)
                 YM2149F
                 16.000MHz(X1)

    lower board: IX0526CE(HN61364) (28pin mask ROM. for ANK font?)
                 MB83256x4 (Kanji ROMs)
                 HD46505SP (VDP)
                 M80C49-277 (MCU)
                 uPD8255AC
                 uPD1990 (RTC) + battery
                 6.000MHz(X2)
                 42.9545MHz(X3)

    FDD I/O board: MB8877A (FDC)
                   MB4107 (VFO)

    RAM banks:
    upper board: MB8265A-15 x8 (main memory)
    lower board: MB8416A-12 x3 (VRAM)
                 MB8416A-15 x3 (PCG RAM)
                 MB81416-10 x12 (GRAM)

************************************************************************************************/

#include "emu.h"
#include "x1.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "formats/2d_dsk.h"


constexpr XTAL MAIN_CLOCK   = 16_MHz_XTAL;
constexpr XTAL VDP_CLOCK    = 42.954'545_MHz_XTAL;
//constexpr XTAL MCU_CLOCK    = 6_MHz_XTAL;

/*************************************
 *
 *  Keyboard MCU simulation
 *
 *************************************/


uint16_t x1_state::check_keyboard_press()
{
	static const char *const portnames[3] = { "key1","key2","key3" };
	uint8_t keymod = ioport("key_modifiers")->read() & 0x1f;
	uint32_t pad = ioport("tenkey")->read();
	uint32_t f_key = ioport("f_keys")->read();

	static const uint8_t kanatable[52][3] = {
		// normal, kana, kana + shift
		{0x2c,0xc8,0xa4}, // , / ne / japanese comma
		{0x2d,0xce,0x00}, // - / ho
		{0x2e,0xd9,0xa1}, // . / ru / japanese period
		{0x2f,0xd2,0xa5}, // / / me / nakaguro
		{0x30,0xdc,0xa6}, // 0 / wa / wo
		{0x31,0xc7,0x00}, // 1 / nu
		{0x32,0xcc,0x00}, // 2 / fu
		{0x33,0xb1,0xa7}, // 3 / a / small a
		{0x34,0xb3,0xa9}, // 4 / u / small u
		{0x35,0xb4,0xaa}, // 5 / e / small e
		{0x36,0xb5,0xab}, // 6 / o / small o
		{0x37,0xd4,0xac}, // 7 / ya / small ya
		{0x38,0xd5,0xad}, // 8 / yu / small yu
		{0x39,0xd6,0xae}, // 9 / yo / small yo
		{0x3a,0xb9,0x00}, // : / ke
		{0x3b,0xda,0x00}, // ; / re
		{0x3c,0x00,0x00},
		{0x3d,0x00,0x00},
		{0x3e,0x00,0x00},
		{0x3f,0x00,0x00},
		{0x40,0xde,0x00}, // @ / dakuten
		{0x41,0xc1,0x00}, // A / chi
		{0x42,0xba,0x00}, // B / ko
		{0x43,0xbf,0x00}, // C / so
		{0x44,0xbc,0x00}, // D / shi
		{0x45,0xb2,0xa8}, // E / i / small i
		{0x46,0xca,0x00}, // F / ha
		{0x47,0xb7,0x00}, // G / ki
		{0x48,0xb8,0x00}, // H / ku
		{0x49,0xc6,0x00}, // I / ni
		{0x4a,0xcf,0x00}, // J / ma
		{0x4b,0xc9,0x00}, // K / no
		{0x4c,0xd8,0x00}, // L / ri
		{0x4d,0xd3,0x00}, // M / mo
		{0x4e,0xd0,0x00}, // N / mi
		{0x4f,0xd7,0x00}, // O / ra
		{0x50,0xbe,0x00}, // P / se
		{0x51,0xc0,0x00}, // Q / ta
		{0x52,0xbd,0x00}, // R / su
		{0x53,0xc4,0x00}, // S / to
		{0x54,0xb6,0x00}, // T / ka
		{0x55,0xc5,0x00}, // U / na
		{0x56,0xcb,0x00}, // V / hi
		{0x57,0xc3,0x00}, // W / te
		{0x58,0xbb,0x00}, // X / sa
		{0x59,0xdd,0x00}, // Y / n
		{0x5a,0xc2,0xaf}, // Z / tsu / small tsu
		{0x5b,0xdf,0xa2}, // [ / handakuten / opening quotation mark
		{0x5c,0xb0,0x00}, // yen symbol / long vowel mark
		{0x5d,0xd1,0xa3}, // ] / mu / closing quotation mark
		{0x5e,0xcd,0x00}, // ^ / he
		{0x5f,0xdb,0x00}  // _ / ro
	};

	for(u8 port_i=0; port_i<3; port_i++)
	{
		for(u8 i=0; i<32; i++)
		{
			u8 scancode = port_i * 32 + i;
			if(BIT(ioport(portnames[port_i])->read(), i))
			{
				switch (keymod & 6)
				{
					case 0: // kana on, shift on
						if (scancode >= 0x2c && scancode <= 0x5f)
							if (kanatable[scancode - 0x2c][2])
								scancode = kanatable[scancode - 0x2c][2];
						break;

					case 2: // kana on, shift off
						if (scancode >= 0x2c && scancode <= 0x5f)
							if (kanatable[scancode - 0x2c][1])
								scancode = kanatable[scancode - 0x2c][1];
						break;

					case 4: // kana off, shift on
						if (scancode == 0x40 || (scancode >= 0x5b && scancode <= 0x5f))
							scancode += 0x20;
						else // numbers, special chars
						if ((scancode >= 0x2c && scancode <= 0x3b))
							scancode ^= 0x10;
						break;

					default: // kana off, shift off
						// Control key
						if (scancode >= 0x41 && scancode <= 0x5f)
							if (!BIT(keymod, 0))
								scancode -= 0x40;
						// If nothing pressed, default to lower case
						if (scancode >= 0x41 && scancode <= 0x5a)
							scancode += 0x20;
						break;
				}

				if(!BIT(keymod, 3)) // capslock
					if ((scancode >= 0x41 && scancode <= 0x5a) || (scancode >= 0x61 && scancode <= 0x7a))
						scancode ^= 0x20;

				if(!BIT(keymod, 4)) // graph on
					scancode |= 0x80;

				return scancode;
			}
		}
	}

	// check numpad
	for(u8 i=0; i<10; i++)
		if(BIT(pad, i))
			return i + 0x120;

	// check function keys
	for(u8 i=0; i<5; i++)
		if(BIT(f_key, i))
			return i + 0x171 + (BIT(keymod, 1) ? 0 : 5);

	return 0;
}

uint8_t x1_state::check_keyboard_shift()
{
	uint8_t val = 0xe0;
	/*
	all of those are active low
	x--- ---- TEN: Numpad, Function key, special input key
	-x-- ---- KIN: Valid key
	--x- ---- REP: Key repeat
	---x ---- GRAPH key ON
	---- x--- CAPS lock ON
	---- -x-- KANA lock ON
	---- --x- SHIFT ON
	---- ---x CTRL ON
	*/

	val |= ioport("key_modifiers")->read() & 0x1f;

	if(check_keyboard_press() != 0)
		val &= ~0x40;

	if(check_keyboard_press() & 0x100) //function keys
		val &= ~0x80;

	return val;
}

uint8_t x1_state::get_game_key(uint8_t port)
{
	// key status returned by sub CPU function 0xE3.
	// in order from bit 7 to 0:
	// port 0: Q,W,E,A,D,Z,X,C
	// port 1: numpad 7,4,1,8,2,9,6,3
	// port 2: ESC,1,[-],[+],[*],TAB,SPC,RET ([] = numpad)
	// bits are active high
	uint8_t ret = 0;

	if (port == 0)
	{
		uint32_t key3 = ioport("key3")->read();
		if(key3 & 0x00020000) ret |= 0x80;  // Q
		if(key3 & 0x00800000) ret |= 0x40;  // W
		if(key3 & 0x00000020) ret |= 0x20;  // E
		if(key3 & 0x00000002) ret |= 0x10;  // A
		if(key3 & 0x00000010) ret |= 0x08;  // D
		if(key3 & 0x04000000) ret |= 0x04;  // Z
		if(key3 & 0x01000000) ret |= 0x02;  // X
		if(key3 & 0x00000008) ret |= 0x01;  // C
	}
	else
	if (port == 1)
	{
		uint32_t pad = ioport("tenkey")->read();
		if(pad & 0x00000080) ret |= 0x80;  // Tenkey 7
		if(pad & 0x00000010) ret |= 0x40;  // Tenkey 4
		if(pad & 0x00000002) ret |= 0x20;  // Tenkey 1
		if(pad & 0x00000100) ret |= 0x10;  // Tenkey 8
		if(pad & 0x00000004) ret |= 0x08;  // Tenkey 2
		if(pad & 0x00000200) ret |= 0x04;  // Tenkey 9
		if(pad & 0x00000040) ret |= 0x02;  // Tenkey 6
		if(pad & 0x00000008) ret |= 0x01;  // Tenkey 3
	}
	else
	if (port == 2)
	{
		uint32_t key1 = ioport("key1")->read();
		uint32_t key2 = ioport("key2")->read();
		uint32_t pad = ioport("tenkey")->read();
		if(key1 & 0x08000000) ret |= 0x80;  // ESC
		if(key2 & 0x00020000) ret |= 0x40;  // 1
		if(pad & 0x00000400) ret |= 0x20;  // Tenkey -
		if(pad & 0x00000800) ret |= 0x10;  // Tenkey +
		if(pad & 0x00001000) ret |= 0x08;  // Tenkey *
		if(key1 & 0x00000200) ret |= 0x04;  // TAB
		if(key2 & 0x00000001) ret |= 0x02;  // SPC
		if(key1 & 0x00002000) ret |= 0x01;  // RET
	}

	return ret;
}

uint8_t x1_state::sub_io_r()
{
	uint8_t ret,bus_res;

	/* Looks like that the HW retains the latest data putted on the bus here, behaviour confirmed by Rally-X */
	if(m_sub_obf)
	{
		bus_res = m_sub_val[m_key_i];
		/* FIXME: likely to be different here. */
		m_key_i++;
		if(m_key_i >= 2)
			m_key_i = 0;

		return bus_res;
	}

#if 0
	if(key_flag == 1)
	{
		key_flag = 0;
		return 0x82; //TODO: this is for shift/ctrl/kana lock etc.
	}
#endif

	m_sub_cmd_length--;
	m_sub_obf = (m_sub_cmd_length) ? 0x00 : 0x20;

	ret = m_sub_val[m_sub_val_ptr];

	m_sub_val_ptr++;
	if(m_sub_cmd_length <= 0)
		m_sub_val_ptr = 0;

	return ret;
}

void x1_state::cmt_command( uint8_t cmd )
{
	// CMT deck control command (E9 xx)
	// E9 00 - Eject
	// E9 01 - Stop
	// E9 02 - Play
	// E9 03 - Fast Forward
	// E9 04 - Rewind
	// E9 05 - APSS Fast Forward
	// E9 06 - APSS Rewind
	// E9 0A - Record
	/*
	APSS is a Sharp invention and stands for Automatic Program Search System, it scans the tape for silent parts that are bigger than 4 seconds.
	It's basically used for audio tapes in order to jump over the next/previous "track".
	*/
	m_cmt_current_cmd = cmd;

	if(m_cassette->get_image() == nullptr) //avoid a crash if a disk game tries to access this
		return;

	switch(cmd)
	{
		case 0x01:  // Stop
			m_cassette->change_state(CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR);
			m_cassette->change_state(CASSETTE_STOPPED,CASSETTE_MASK_UISTATE);
			m_cmt_test = 1;
			popmessage("CMT: Stop");
			break;
		case 0x02:  // Play
			m_cassette->change_state(CASSETTE_MOTOR_ENABLED,CASSETTE_MASK_MOTOR);
			m_cassette->change_state(CASSETTE_PLAY,CASSETTE_MASK_UISTATE);
			popmessage("CMT: Play");
			break;
		case 0x03:  // Fast Forward
			m_cassette->change_state(CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR);
			m_cassette->change_state(CASSETTE_STOPPED,CASSETTE_MASK_UISTATE);
			popmessage("CMT: Fast Forward");
			break;
		case 0x04:  // Rewind
			m_cassette->change_state(CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR);
			m_cassette->change_state(CASSETTE_STOPPED,CASSETTE_MASK_UISTATE);
			popmessage("CMT: Rewind");
			break;
		case 0x05:  // APSS Fast Forward
			m_cassette->change_state(CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR);
			m_cassette->change_state(CASSETTE_STOPPED,CASSETTE_MASK_UISTATE);
			popmessage("CMT: APSS Fast Forward");
			break;
		case 0x06:  // APSS Rewind
			m_cassette->change_state(CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR);
			m_cassette->change_state(CASSETTE_STOPPED,CASSETTE_MASK_UISTATE);
			popmessage("CMT: APSS Rewind");
			break;
		case 0x0a:  // Record
			m_cassette->change_state(CASSETTE_MOTOR_ENABLED,CASSETTE_MASK_MOTOR);
			m_cassette->change_state(CASSETTE_RECORD,CASSETTE_MASK_UISTATE);
			popmessage("CMT: Record");
			break;
		default:
			logerror("Unimplemented or invalid CMT command (0x%02x)\n",cmd);
	}
	logerror("CMT: Command 0xe9-0x%02x received.\n",cmd);
}

TIMER_DEVICE_CALLBACK_MEMBER(x1_state::cmt_seek_cb)
{
	if(m_cassette->get_image() == nullptr) //avoid a crash if a disk game tries to access this
		return;

	switch(m_cmt_current_cmd)
	{
		case 0x03:
		case 0x05:  // Fast Forwarding tape
			m_cassette->seek(1,SEEK_CUR);
			if(m_cassette->get_position() >= m_cassette->get_length())  // at end?
				cmt_command(0x01);  // Stop tape
			break;
		case 0x04:
		case 0x06:  // Rewinding tape
			m_cassette->seek(-1,SEEK_CUR);
			if(m_cassette->get_position() <= 0) // at beginning?
				cmt_command(0x01);  // Stop tape
			break;
	}
}

void x1_state::sub_io_w(uint8_t data)
{
	/* sub-routine at $10e sends to these sub-routines when a keyboard input is triggered:
	 $17a -> floppy
	 $094 -> ROM
	 $0c0 -> timer
	 $052 -> cmt
	 $0f5 -> reload sub-routine? */

	if(m_sub_cmd == 0xe4)
	{
		m_key_irq_vector = data;
		logerror("Key vector set to 0x%02x\n",data);
		data = 0;
	}

	if(m_sub_cmd == 0xe9)
	{
		cmt_command(data);
		data = 0;
	}

	if((data & 0xf0) == 0xd0) //reads here tv recording timer data. (Timer set (0xd0) / Timer readout (0xd8))
	{
		/*
		    xx-- ---- mode
		    --xx xxxx interval
		*/
		m_sub_val[0] = 0;
		/*
		    xxxx xxxx command code:
		    00 timer disabled
		    01 TV command
		    10 interrupt
		    11 Cassette deck
		*/
		m_sub_val[1] = 0;
		/*
		    ---x xxxx minute
		*/
		m_sub_val[2] = 0;
		/*
		    ---- xxxx hour
		*/
		m_sub_val[3] = 0;
		/*
		    xxxx ---- month
		    ---- -xxx day of the week
		*/
		m_sub_val[4] = 0;
		/*
		    --xx xxxx day
		*/
		m_sub_val[5] = 0;
		m_sub_cmd_length = 6;
	}

	switch(data)
	{
		case 0xe3: //game key obtaining
			m_sub_cmd_length = 3;
			m_sub_val[0] = get_game_key(0);
			m_sub_val[1] = get_game_key(1);
			m_sub_val[2] = get_game_key(2);
			break;
		case 0xe4: //irq vector setting
			break;
		//case 0xe5: //timer irq clear
		//  break;
		case 0xe6: //key data readout
			m_sub_val[0] = check_keyboard_shift() & 0xff;
			m_sub_val[1] = check_keyboard_press() & 0xff;
			m_sub_cmd_length = 2;
			break;
//      case 0xe7: // TV Control
//          break;
		case 0xe8: // TV Control read-out
			m_sub_val[0] = m_sub_cmd;
			m_sub_cmd_length = 1;
			break;
		case 0xe9: // CMT Control
			break;
		case 0xea:  // CMT Control status
			m_sub_val[0] = m_cmt_current_cmd;
			m_sub_cmd_length = 1;
			logerror("CMT: Command 0xEA received, returning 0x%02x.\n",m_sub_val[0]);
			break;
		case 0xeb:  // CMT Tape status
					// bit 0 = tape end (0=end of tape)
					// bit 1 = tape inserted
					// bit 2 = record status (1=OK, 0=write protect)
			m_sub_val[0] = 0x05;
			if(m_cassette->get_image() != nullptr)
				m_sub_val[0] |= 0x02;
			m_sub_cmd_length = 1;
			logerror("CMT: Command 0xEB received, returning 0x%02x.\n",m_sub_val[0]);
			break;
//      case 0xec: //set date
//          break;
		case 0xed: //get date
			m_sub_val[0] = m_rtc.day;
			m_sub_val[1] = (m_rtc.month<<4) | (m_rtc.wday & 0xf);
			m_sub_val[2] = m_rtc.year;
			m_sub_cmd_length = 3;
			break;
//      case 0xee: //set time
//          break;
		case 0xef: //get time
			m_sub_val[0] = m_rtc.hour;
			m_sub_val[1] = m_rtc.min;
			m_sub_val[2] = m_rtc.sec;
			m_sub_cmd_length = 3;
			break;
	}

	m_sub_cmd = data;

	m_sub_obf = (m_sub_cmd_length) ? 0x00 : 0x20;

	if(data != 0xe6)
		logerror("SUB: Command byte 0x%02x\n",data);
}

/*************************************
 *
 *  ROM Image / Banking Handling
 *
 *************************************/


uint8_t x1_state::rom_r()
{
//  logerror("%06x\n",m_rom_index[0]<<16|m_rom_index[1]<<8|m_rom_index[2]<<0);
	if (m_cart->exists())
		return m_cart->read_rom((m_rom_index[0] << 16) | (m_rom_index[1] << 8) | (m_rom_index[2] << 0));
	else
		return 0;
}

void x1_state::rom_w(offs_t offset, uint8_t data)
{
	m_rom_index[offset] = data;
}

void x1_state::rom_bank_0_w(uint8_t data)
{
	m_ram_bank = 0x10;
}

void x1_state::rom_bank_1_w(uint8_t data)
{
	m_ram_bank = 0x00;
}

/*************************************
 *
 *  MB8877A FDC (wd17XX compatible)
 *
 *************************************/

uint8_t x1_state::fdc_r(offs_t offset)
{
	//uint8_t ret = 0;

	switch(offset+0xff8)
	{
		case 0x0ff8:
			return m_fdc->status_r();
		case 0x0ff9:
			return m_fdc->track_r();
		case 0x0ffa:
			return m_fdc->sector_r();
		case 0x0ffb:
			return m_fdc->data_r();
		case 0x0ffc:
			if (!machine().side_effects_disabled())
			{
				logerror("FDC: read FM type\n");
				m_fdc->dden_w(1);
			}
			return 0xff;
		case 0x0ffd:
			if (!machine().side_effects_disabled())
			{
				logerror("FDC: read MFM type\n");
				m_fdc->dden_w(0);
			}
			return 0xff;
		case 0x0ffe:
			if (!machine().side_effects_disabled())
				logerror("FDC: read 1.6M type\n");
			return 0xff;
		case 0x0fff:
			if (!machine().side_effects_disabled())
				logerror("FDC: switching between 500k/1M\n");
			return 0xff;
	}

	return 0x00;
}

void x1_state::fdc_w(offs_t offset, uint8_t data)
{
	floppy_image_device *floppy = nullptr;

	switch(offset+0xff8)
	{
		case 0x0ff8:
			m_fdc->cmd_w(data);
			break;
		case 0x0ff9:
			m_fdc->track_w(data);
			break;
		case 0x0ffa:
			m_fdc->sector_w(data);
			break;
		case 0x0ffb:
			m_fdc->data_w(data);
			break;

		case 0x0ffc:
			floppy = m_floppy[data & 0x03]->get_device();

			m_fdc->set_floppy(floppy);

			if (floppy)
			{
				floppy->ss_w(BIT(data, 4));
				if(BIT(m_fdc_ctrl, 7) && !BIT(data, 7))
					m_motor_timer->adjust(attotime::from_msec(1200));
				else if(BIT(data, 7))
					floppy->mon_w(0);
			}
			m_fdc_ctrl = data;
			break;

		case 0x0ffd:
		case 0x0ffe:
		case 0x0fff:
			logerror("FDC: undefined write to %04x = %02x\n",offset+0xff8,data);
			break;
	}
}

TIMER_CALLBACK_MEMBER(x1_state::fdc_motor_off_cb)
{
	if(!BIT(m_fdc_ctrl, 7))
	{
		floppy_image_device *floppy = m_floppy[m_fdc_ctrl & 0x03]->get_device();
		if(floppy)
			floppy->mon_w(1);
	}
}

void x1turbo_state::fdc_drq_w(int state)
{
	m_dma->rdy_w(state ^ 1);
}

/*************************************
 *
 *  Programmable Character Generator
 *
 *************************************/

uint16_t x1_state::check_pcg_addr()
{
	if(m_avram[0x7ff] & 0x20) return 0x7ff;
	if(m_avram[0x3ff] & 0x20) return 0x3ff;
	if(m_avram[0x5ff] & 0x20) return 0x5ff;
	if(m_avram[0x1ff] & 0x20) return 0x1ff;

	return 0x3ff;
}

uint16_t x1_state::check_chr_addr()
{
	if(!(m_avram[0x7ff] & 0x20)) return 0x7ff;
	if(!(m_avram[0x3ff] & 0x20)) return 0x3ff;
	if(!(m_avram[0x5ff] & 0x20)) return 0x5ff;
	if(!(m_avram[0x1ff] & 0x20)) return 0x1ff;

	return 0x3ff;
}

uint16_t x1_state::get_pcg_addr( uint16_t width, uint8_t y_char_size )
{
	int hbeam = m_screen->hpos() >> 3;
	int vbeam = m_screen->vpos() / y_char_size;
	uint16_t pcg_offset = ((hbeam + vbeam*width) + (((m_crtc_vreg[0x0c]<<8) & 0x3f00) | (m_crtc_vreg[0x0d] & 0xff))) & 0x7ff;

	//logerror("%08x %d %d %d %d\n",(hbeam+vbeam*width),hbeam,vbeam,m_screen->vpos() & 7,width);

	return pcg_offset;
}

uint8_t x1_state::pcg_r(offs_t offset)
{
	int addr;
	int pcg_offset;
	uint8_t res;
	uint8_t *gfx_data;

	addr = (offset & 0x300) >> 8;

	if(addr == 0 && m_scrn_reg.pcg_mode) // Kanji ROM read, X1Turbo only
	{
		gfx_data = m_kanji_rom;
		pcg_offset = (m_tvram[check_chr_addr()]+(m_kvram[check_chr_addr()]<<8)) & 0xfff;
		pcg_offset*=0x20;
		pcg_offset+=(offset & 0x0f);
		pcg_offset+=(m_kvram[check_chr_addr()] & 0x40) >> 2; //left-right check

		res = gfx_data[pcg_offset];
	}
	else
	{
		uint8_t y_char_size;

		/* addr == 0 reads from the ANK rom */
		gfx_data = addr == 0 ? m_cg_rom : m_pcg_ram.get();
		y_char_size = ((m_crtc_vreg[9]+1) > 8) ? 8 : m_crtc_vreg[9]+1;
		if(y_char_size == 0) { y_char_size = 1; }
		pcg_offset = m_tvram[get_pcg_addr(m_crtc_vreg[1], y_char_size)]*8;
		pcg_offset+= m_screen->vpos() & (y_char_size-1);
		if(addr) { pcg_offset+= ((addr-1)*0x800); }
		res = gfx_data[pcg_offset];
	}

	return res;
}

void x1_state::pcg_w(offs_t offset, uint8_t data)
{
	int addr,pcg_offset;

	addr = (offset & 0x300) >> 8;

	if(addr == 0)
	{
		/* NOP */
		logerror("Warning: write to the ANK area! %04x %02x\n",offset,data);
	}
	else
	{
		if(m_scrn_reg.pcg_mode) // Hi-Speed Mode, X1Turbo only
		{
			pcg_offset = m_tvram[check_pcg_addr()]*8;
			pcg_offset+= (offset & 0xe) >> 1;
			pcg_offset+=((addr-1)*0x800);
			m_pcg_ram[pcg_offset] = data;

			pcg_offset &= 0x7ff;

			m_gfxdecode->gfx(3)->mark_dirty(pcg_offset >> 3);
		}
		else // Compatible Mode
		{
			uint8_t y_char_size;

			/* TODO: Brain Breaker doesn't work with this arrangement in high resolution mode, check out why */
			y_char_size = (m_crtc_vreg[9]+1) > 8 ? (m_crtc_vreg[9]+1)-8 : m_crtc_vreg[9]+1;
			if(y_char_size == 0) { y_char_size = 1; }
			pcg_offset = m_tvram[get_pcg_addr(m_crtc_vreg[1], y_char_size)]*8;
			pcg_offset+= m_screen->vpos() & (y_char_size-1);
			pcg_offset+= ((addr-1)*0x800);

			m_pcg_ram[pcg_offset] = data;

			pcg_offset &= 0x7ff;

			m_gfxdecode->gfx(3)->mark_dirty(pcg_offset >> 3);
		}
	}
}

/*************************************
 *
 *  Other Video-related functions
 *
 *************************************/

/* for bitmap mode */
void x1_state::set_current_palette()
{
	uint8_t addr,r,g,b;

	for(addr=0;addr<8;addr++)
	{
		r = ((m_x_r)>>(addr)) & 1;
		g = ((m_x_g)>>(addr)) & 1;
		b = ((m_x_b)>>(addr)) & 1;

		m_palette->set_pen_color(addr|8, pal1bit(r), pal1bit(g), pal1bit(b));
	}

	// TODO: disabled for now, causes issues with Thunder Force. x1fdemo changes palette dynamically during initial logo.
	//       Likely it needs a video rewrite in order to make this to work correctly.
	//  m_screen->update_partial(m_screen->vpos());
}

/* Note: docs claims that reading the palette ports makes the value to change somehow in X1 mode ...
         In 4096 color mode, it's used for reading the value back. */
void x1_state::pal_r_w(uint8_t data)
{
	m_x_r = data;
	set_current_palette();
}

void x1_state::pal_g_w(uint8_t data)
{
	m_x_g = data;
	set_current_palette();
}

void x1_state::pal_b_w(uint8_t data)
{
	m_x_b = data;
	set_current_palette();
}

void x1_state::x1turboz_4096_palette_w(offs_t offset, uint8_t data)
{
	if (m_turbo_reg.pal & 0x80) // AEN bit, Turbo Z
	{
		if (m_turbo_reg.gfx_pal & 0x80) // APEN bit
		{
			if (m_turbo_reg.gfx_pal & 0x08) // APRD bit
			{
				// TODO: writing here on APRD condition just fetch offset index that reads back on this I/O
				popmessage("APRD enabled, contact MAMEdev");
				return;
			}
			// TODO: unlike normal operation this cannot do mid-frame scanline update
			// (-> bus request signal when accessing this on non-vblank time)
			uint32_t pal_entry = ((offset & 0xff) << 4) | ((data & 0xf0) >> 4);
			// TODO: more complex condition
			if ((m_turbo_reg.pal & 0x10) == 0) // C64 bit
			{
				pal_entry &= 0xccc;
				pal_entry |= pal_entry >> 2;
			}

			m_pal_4096[pal_entry+((offset & 0x300)<<4)] = data & 0xf;

			uint8_t const r = m_pal_4096[pal_entry+(1<<12)];
			uint8_t const g = m_pal_4096[pal_entry+(2<<12)];
			uint8_t const b = m_pal_4096[pal_entry+(0<<12)];

			m_palette->set_pen_color(pal_entry+16, pal4bit(r), pal4bit(g), pal4bit(b));
		}
	}
	else //compatible mode
	{
		switch (offset & 0x0300)
		{
		case 0x0000:
			pal_b_w(data);
			break;
		case 0x0100:
			pal_r_w(data);
			break;
		case 0x0200:
			pal_g_w(data);
			break;
		}
	}
}

uint8_t x1_state::ex_gfxram_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
	{
		m_iobank->set_bank(0); // any read disables the extended mode
		return m_iobank->read8(offset);
	}
	else
	{
		return 0xff;
	}
}

void x1_state::ex_gfxram_w(offs_t offset, uint8_t data)
{
	uint8_t ex_mask;

	if     (                    offset <= 0x3fff)   { ex_mask = 7; }
	else if(offset >= 0x4000 && offset <= 0x7fff)   { ex_mask = 6; }
	else if(offset >= 0x8000 && offset <= 0xbfff)   { ex_mask = 5; }
	else                                            { ex_mask = 3; }

	uint8_t *const ptr = reinterpret_cast<uint8_t *>(m_bitmapbank->base());
	if(ex_mask & 1) ptr[(offset & 0x3fff)+0x0000] = data;
	if(ex_mask & 2) ptr[(offset & 0x3fff)+0x4000] = data;
	if(ex_mask & 4) ptr[(offset & 0x3fff)+0x8000] = data;
}

/*
    SCRN flags

    d0(01) = 0:low resolution (15KHz) 1: high resolution (24KHz)
    d1(02) = 0:1 raster / pixel       1:2 raster / pixel
    d2(04) = 0:8 rasters / CHR        1:16 rasters / CHR
    d3(08) = 0:bank 0                 0:bank 1    <- display
    d4(10) = 0:bank 0                 0:bank 1    <- access
    d5(20) = 0:compatibility          1:high speed  <- define PCG mode
    d6(40) = 0:8-raster graphics      1:16-raster graphics
    d7(80) = 0:don't display          1:display  <- underline (when 1, graphics are not displayed)
*/
void x1_state::scrn_w(uint8_t data)
{
	m_scrn_reg.video_mode = data & 0xc7;
	m_scrn_reg.pcg_mode = BIT(data, 5);
	m_bitmapbank->set_entry(BIT(data, 4));
	m_scrn_reg.disp_bank = BIT(data, 3);
	m_scrn_reg.ank_sel = BIT(data, 2);
	m_scrn_reg.v400_mode = ((data & 0x03) == 3) ? 1 : 0;

	if(data & 0x80)
		logerror("SCRN = %02x\n",data & 0x80);
	if((data & 0x03) == 1)
		logerror("SCRN sets true 400 lines mode\n");
}

void x1_state::pri_w(uint8_t data)
{
	m_scrn_reg.pri = data;
//  logerror("PRI = %02x\n",data);
}

uint8_t x1_state::x1turboz_blackclip_r()
{
	/*  TODO: this returns only on x1turboz */
	return m_scrn_reg.blackclip;
}

void x1_state::x1turbo_blackclip_w(uint8_t data)
{
	/*
	-x-- ---- replace blanking duration with black
	--x- ---- replace bitmap palette 1 with black
	---x ---- replace bitmap palette 0 with black
	---- x--- enable text blackclip
	---- -xxx palette color number for text black
	*/
	m_scrn_reg.blackclip = data;
	if(data & 0x40)
		logerror("Blackclip data access %02x\n",data);
}

uint8_t x1_state::x1turbo_pal_r()
{
	return m_turbo_reg.pal;
}

uint8_t x1_state::x1turbo_txpal_r(offs_t offset)
{
	return m_turbo_reg.txt_pal[offset];
}

uint8_t x1_state::x1turbo_txdisp_r()
{
	return m_turbo_reg.txt_disp;
}

uint8_t x1_state::x1turbo_gfxpal_r()
{
	return m_turbo_reg.gfx_pal;
}

void x1_state::x1turbo_pal_w(uint8_t data)
{
	logerror("TURBO PAL %02x\n",data);
	m_turbo_reg.pal = data;
}

void x1_state::x1turbo_txpal_w(offs_t offset, uint8_t data)
{
	int r,g,b;

	logerror("TURBO TEXT PAL %02x %02x\n",data,offset);
	m_turbo_reg.txt_pal[offset] = data;

	if(m_turbo_reg.pal & 0x80)
	{
		r = (data & 0x0c) >> 2;
		g = (data & 0x30) >> 4;
		b = (data & 0x03) >> 0;

		m_palette->set_pen_color(offset, pal2bit(r), pal2bit(g), pal2bit(b));
	}
}

void x1_state::x1turbo_txdisp_w(uint8_t data)
{
	logerror("TURBO TEXT DISPLAY %02x\n",data);
	m_turbo_reg.txt_disp = data;
}

void x1_state::x1turbo_gfxpal_w(uint8_t data)
{
	logerror("TURBO GFX PAL %02x\n",data);
	m_turbo_reg.gfx_pal = data;
}


/*
 *  FIXME: bit-wise this doesn't make any sense, I guess that it uses the lv 2 kanji roms
 *         Test cases for this port so far are Hyper Olympics '84 disk version and Might & Magic.
 */
uint16_t x1_state::jis_convert(int kanji_addr)
{
	if(kanji_addr >= 0x0e00 && kanji_addr <= 0x0e9f) { kanji_addr -= 0x0e00; kanji_addr &= 0x0ff; return ((0x0e0) + (kanji_addr >> 3)) << 4; } // numbers
	if(kanji_addr >= 0x0f00 && kanji_addr <= 0x109f) { kanji_addr -= 0x0f00; kanji_addr &= 0x1ff; return ((0x4c0) + (kanji_addr >> 3)) << 4; } // lower case chars
	if(kanji_addr >= 0x1100 && kanji_addr <= 0x129f) { kanji_addr -= 0x1100; kanji_addr &= 0x1ff; return ((0x2c0) + (kanji_addr >> 3)) << 4; } // upper case chars
	if(kanji_addr >= 0x0100 && kanji_addr <= 0x01ff) { kanji_addr -= 0x0100; kanji_addr &= 0x0ff; return ((0x040) + (kanji_addr >> 3)) << 4; } // grammar symbols
	if(kanji_addr >= 0x0500 && kanji_addr <= 0x06ff) { kanji_addr -= 0x0500; kanji_addr &= 0x1ff; return ((0x240) + (kanji_addr >> 3)) << 4; } // math symbols
	if(kanji_addr >= 0x0300 && kanji_addr <= 0x04ff) { kanji_addr -= 0x0300; kanji_addr &= 0x1ff; return ((0x440) + (kanji_addr >> 3)) << 4; } // parentesis

	if(kanji_addr != 0x0720 && kanji_addr != 0x0730)
		logerror("%08x\n",kanji_addr);

	return 0x0000;
}

uint8_t x1_state::kanji_r(offs_t offset)
{
	uint8_t res;

	res = m_kanji_rom[jis_convert(m_kanji_addr & 0xfff0)+(offset*0x10)+(m_kanji_addr & 0xf)];

	if(offset == 1)
		m_kanji_addr_latch++;

	return res;
}

void x1_state::kanji_w(offs_t offset, uint8_t data)
{
//  if(offset < 2)

	switch(offset)
	{
		case 0: m_kanji_addr_latch = (data & 0xff)|(m_kanji_addr_latch&0xff00); break;
		case 1: m_kanji_addr_latch = (data<<8)|(m_kanji_addr_latch&0x00ff);
			//if(m_kanji_addr_latch != 0x720 && m_kanji_addr_latch != 0x730)
			//  logerror("%08x\n",m_kanji_addr_latch);
			break;
		case 2:
		{
			/* 0 -> selects Expanded EEPROM */
			/* 1 -> selects Kanji ROM */
			/* 0 -> 1 -> latches Kanji ROM data */

			if(((m_kanji_eksel & 1) == 0) && ((data & 1) == 1))
			{
				m_kanji_addr = (m_kanji_addr_latch);
				//m_kanji_addr &= 0x3fff; //<- temp kludge until the rom is redumped.
				//logerror("%08x\n",m_kanji_addr);
				//m_kanji_addr+= m_kanji_count;
			}
			m_kanji_eksel = data & 1;
		}
		break;
	}
}

uint8_t x1_state::emm_r(offs_t offset)
{
	uint8_t res;

	if(offset & ~3)
	{
		logerror("Warning: read EMM BASIC area [%02x]\n",offset & 0xff);
		return 0xff;
	}

	if(offset != 3)
		logerror("Warning: read EMM address [%02x]\n",offset);

	res = 0xff;

	if(offset == 3)
	{
		res = m_emm_ram[m_emm_addr];
		m_emm_addr++;
	}

	return res;
}

void x1_state::emm_w(offs_t offset, uint8_t data)
{
	if(offset & ~3)
	{
		logerror("Warning: write EMM BASIC area [%02x] %02x\n",offset & 0xff,data);
		return;
	}

	switch(offset)
	{
		case 0: m_emm_addr = (m_emm_addr & 0xffff00) | (data & 0xff); break;
		case 1: m_emm_addr = (m_emm_addr & 0xff00ff) | (data << 8);   break;
		case 2: m_emm_addr = (m_emm_addr & 0x00ffff) | (data << 16);  break; //TODO: this has a max size limit, check exactly how much
		case 3:
			m_emm_ram[m_emm_addr] = data;
			m_emm_addr++;
			break;
	}
}

/*
    CZ-141SF, CZ-127MF, X1turboZII, X1turboZ3 boards
*/
uint8_t x1_state::x1turbo_bank_r()
{
//  logerror("BANK access read\n");
	return m_ex_bank & 0x3f;
}

void x1_state::x1turbo_bank_w(uint8_t data)
{
	//uint8_t *RAM = memregion("x1_cpu")->base();
	/*
	--x- ---- BML5: latch bit (doesn't have any real function)
	---x ---- BMCS: select bank RAM, active low
	---- xxxx BMNO: Bank memory ID
	*/

	m_ex_bank = data & 0x3f;
//  logerror("BANK access write %02x\n",data);
}

/* TODO: waitstate penalties */
uint8_t x1_state::mem_r(offs_t offset)
{
	if((offset & 0x8000) == 0 && (m_ram_bank == 0))
	{
		return m_ipl_rom[offset]; //ROM
	}

	return m_work_ram[offset]; //RAM
}

void x1_state::mem_w(offs_t offset, uint8_t data)
{
	m_work_ram[offset] = data; //RAM
}

uint8_t x1turbo_state::x1turbo_mem_r(offs_t offset)
{
	if((m_ex_bank & 0x10) == 0)
		return m_work_ram[offset+((m_ex_bank & 0xf)*0x10000)];

	return mem_r(offset);
}

void x1turbo_state::x1turbo_mem_w(offs_t offset, uint8_t data)
{
	if((m_ex_bank & 0x10) == 0)
		m_work_ram[offset+((m_ex_bank & 0xf)*0x10000)] = data; //RAM
	else
		mem_w(offset,data);
}

/*************************************
 *
 *  Memory maps
 *
 *************************************/

void x1_state::x1_io_banks_common(address_map &map)
{
	map.unmap_value_high();

	map(0x0e00, 0x0e02).w(FUNC(x1_state::rom_w));
	map(0x0e03, 0x0e03).r(FUNC(x1_state::rom_r));

	map(0x0ff8, 0x0fff).rw(FUNC(x1_state::fdc_r), FUNC(x1_state::fdc_w));

	map(0x1300, 0x1300).mirror(0x00ff).w(FUNC(x1_state::pri_w));
	map(0x1400, 0x17ff).rw(FUNC(x1_state::pcg_r), FUNC(x1_state::pcg_w));

	// TODO: verify if also readable
	map(0x1800, 0x1800).lw8(
		NAME([this](offs_t offset, u8 data) {
			m_crtc_index = data & 31;
			m_crtc->address_w(data);
		})
	);
	map(0x1801, 0x1801).lw8(
		NAME([this](offs_t offset, u8 data) {
			m_crtc_vreg[m_crtc_index] = data;
			m_crtc->register_w(data);
		})
	);

	map(0x1900, 0x1900).mirror(0x00ff).rw(FUNC(x1_state::sub_io_r), FUNC(x1_state::sub_io_w));
	map(0x1a00, 0x1a03).mirror(0x00fc).rw("ppi8255_0", FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x1b00, 0x1b00).mirror(0x00ff).rw("ay", FUNC(ay8910_device::data_r), FUNC(ay8910_device::data_w));
	map(0x1c00, 0x1c00).mirror(0x00ff).w("ay", FUNC(ay8910_device::address_w));
	map(0x1d00, 0x1d00).mirror(0x00ff).w(FUNC(x1_state::rom_bank_1_w));
	map(0x1e00, 0x1e00).mirror(0x00ff).w(FUNC(x1_state::rom_bank_0_w));

	map(0x1fa0, 0x1fa3).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x1fa8, 0x1fab).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));

	map(0x2000, 0x27ff).mirror(0x0800).ram().share("avram");

	map(0x4000, 0xffff).bankrw("bitmapbank");

	map(0x10000, 0x1ffff).rw(FUNC(x1_state::ex_gfxram_r), FUNC(x1_state::ex_gfxram_w));
}


void x1_state::x1_io_banks(address_map &map)
{
	x1_io_banks_common(map);

//  map(0x0700, 0x0701) TODO: user could install ym2151 on plain X1 too

	map(0x1000, 0x1000).mirror(0x00ff).w(FUNC(x1_state::pal_b_w));
	map(0x1100, 0x1100).mirror(0x00ff).w(FUNC(x1_state::pal_r_w));
	map(0x1200, 0x1200).mirror(0x00ff).w(FUNC(x1_state::pal_g_w));

	// Ys checks if it's a x1/x1turbo machine by checking if this area is a mirror
	map(0x3000, 0x37ff).mirror(0x0800).ram().share("tvram");
}


void x1turbo_state::x1turbo_io_banks(address_map &map)
{
	x1_io_banks_common(map);

	// TODO: a ** at head states devices used on plain X1 too, as option board

/**/map(0x0700, 0x0701).r(FUNC(x1_state::ym_r)).w("ym", FUNC(ym2151_device::write));
	// 0x704 is FM sound detection port on X1 turboZ
	map(0x0704, 0x0707).rw(m_ctc_ym, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));

/**/map(0x0800, 0x0800).w(FUNC(x1_state::color_board_w));
/**/map(0x0801, 0x0801).r(FUNC(x1_state::color_board_r));
/**/map(0x0802, 0x0802).w(FUNC(x1_state::color_board_2_w));
/**/map(0x0803, 0x0803).r(FUNC(x1_state::color_board_2_r));
/**/map(0x0a00, 0x0a07).rw(FUNC(x1_state::stereo_board_r), FUNC(x1_state::stereo_board_w));
	map(0x0b00, 0x0b00).rw(FUNC(x1_state::x1turbo_bank_r), FUNC(x1_state::x1turbo_bank_w));
/**/map(0x0c00, 0x0cff).rw(FUNC(x1_state::rs232_r), FUNC(x1_state::rs232_w));
/**/map(0x0d00, 0x0dff).rw(FUNC(x1_state::emm_r), FUNC(x1_state::emm_w));
	map(0x0e80, 0x0e81).r(FUNC(x1_state::kanji_r));
	map(0x0e80, 0x0e83).w(FUNC(x1_state::kanji_w));
/**/map(0x0fd0, 0x0fd3).rw(FUNC(x1_state::sasi_r), FUNC(x1_state::sasi_w));
/**/map(0x0fe8, 0x0fef).rw(FUNC(x1_state::fdd8_r), FUNC(x1_state::fdd8_w));

	map(0x1000, 0x12ff).w(FUNC(x1_state::x1turboz_4096_palette_w));

	map(0x1f80, 0x1f80).mirror(0x000f).rw(m_dma, FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	map(0x1f90, 0x1f93).rw("sio", FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
	map(0x1f98, 0x1f9f).rw(FUNC(x1_state::ext_sio_ctc_r), FUNC(x1_state::ext_sio_ctc_w));
	// FIXME: identify disks with Turbo Z capabilities, potentially move to subclass
	map(0x1fb0, 0x1fb0).rw(FUNC(x1_state::x1turbo_pal_r), FUNC(x1_state::x1turbo_pal_w));       // Z only!
	map(0x1fb8, 0x1fbf).rw(FUNC(x1_state::x1turbo_txpal_r), FUNC(x1_state::x1turbo_txpal_w));   // Z only!
	map(0x1fc0, 0x1fc0).rw(FUNC(x1_state::x1turbo_txdisp_r), FUNC(x1_state::x1turbo_txdisp_w)); // Z only!
	map(0x1fc1, 0x1fc1).w(FUNC(x1_state::z_img_cap_w));                            // Z only!
	map(0x1fc2, 0x1fc2).w(FUNC(x1_state::z_mosaic_w));                             // Z only!
	map(0x1fc3, 0x1fc3).w(FUNC(x1_state::z_chroma_key_w));                         // Z only!
	map(0x1fc4, 0x1fc4).w(FUNC(x1_state::z_extra_scroll_w));                       // Z only!
	map(0x1fc5, 0x1fc5).rw(FUNC(x1_state::x1turbo_gfxpal_r), FUNC(x1_state::x1turbo_gfxpal_w)); // Z only!
//  map(0x1fd0, 0x1fdf).r(FUNC(x1_state::x1_scrn_r));                               // Z only!
	map(0x1fd0, 0x1fd0).mirror(0x000f).w(FUNC(x1_state::scrn_w));
	map(0x1fe0, 0x1fe0).rw(FUNC(x1_state::x1turboz_blackclip_r), FUNC(x1_state::x1turbo_blackclip_w));
	map(0x1ff0, 0x1ff0).portr("X1TURBO_DSW");

	map(0x3000, 0x37ff).ram().share("tvram");
	map(0x3800, 0x3fff).ram().share("kvram");
}


void x1_state::x1_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(x1_state::mem_r), FUNC(x1_state::mem_w));
}

void x1turbo_state::x1turbo_mem(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(x1turbo_state::x1turbo_mem_r), FUNC(x1turbo_state::x1turbo_mem_w));
}

void x1_state::x1_io(address_map &map)
{
	map(0x0000, 0xffff).m(m_iobank, FUNC(address_map_bank_device::amap8));
}

/*************************************
 *
 *  PPI8255
 *
 *************************************/

uint8_t x1_state::x1_porta_r()
{
	logerror("PPI Port A read\n");
	return 0xff;
}

/*
x--- ---- V-DISP
-x-- ---- "sub cpu ibf"
--x- ---- "sub cpu obf"
---x ---- IPL RESET (0=ROM, 1=RAM)
---- x--- "busy" <- allow printer data output
---- -x-- CV-SYNC "v sync"
---- --x- READ DATA "cmt read"
---- ---x -BREAK "cmt test" (active low) <- actually this is "Sub CPU detected BREAK"
*/
uint8_t x1_state::x1_portb_r()
{
	//logerror("PPI Port B read\n");
	uint8_t res = 0;
	// TODO: ys3 is unhappy about V-DISP
	// NOTE: all PCG games actively reads from here, touching this uncarefully *will* break stuff
	int vblank_line = m_crtc_vreg[6] * (m_crtc_vreg[9]+1);
	int vsync_line = m_crtc_vreg[7] * (m_crtc_vreg[9]+1);
	m_vdisp = (m_screen->vpos() < vblank_line) ? 0x80 : 0x00;
	m_vsync = (m_screen->vpos() < vsync_line) ? 0x00 : 0x04;

//  popmessage("%d",vsync_line);
//  popmessage("%d",vblank_line);

	res = m_ram_bank | m_sub_obf | m_vsync | m_vdisp;

	if(m_cassette->input() > 0.03)
		res |= 0x02;

//  if(cassette_get_state(m_cassette) & CASSETTE_MOTOR_DISABLED)
//      res &= ~0x02;  // is zero if not playing

	// CMT test bit is set low when the CMT Stop command is issued, and becomes
	// high again when this bit is read.
	res |= 0x01;
	if(m_cmt_test != 0)
	{
		m_cmt_test = 0;
		res &= ~0x01;
	}

	return res;
}

/* I/O system port */
uint8_t x1_state::x1_portc_r()
{
	//logerror("PPI Port C read\n");
	/*
	x--- ---- Printer port output
	-x-- ---- 320 mode (r/w), divider for the pixel clock
	--x- ---- i/o mode (r/w)
	---x ---- smooth scroll enabled (?)
	---- ---x cassette output data
	*/
	return (m_io_sys & 0x9f) | m_hres_320 | ~m_io_switch;
}

void x1_state::x1_porta_w(uint8_t data)
{
	//logerror("PPI Port A write %02x\n",data);
}

void x1_state::x1_portb_w(uint8_t data)
{
	//logerror("PPI Port B write %02x\n",data);
}

void x1_state::x1_portc_w(uint8_t data)
{
	m_hres_320 = data & 0x40;

	/* set up the pixel clock according to the above divider */
	m_crtc->set_unscaled_clock(VDP_CLOCK/((m_hres_320) ? 48 : 24));

	if(!BIT(data, 5) && BIT(m_io_switch, 5))
		m_iobank->set_bank(1);

	m_io_switch = data & 0x20;
	m_io_sys = data & 0xff;

	m_cassette->output(BIT(data, 0) ? +1.0 : -1.0);
}

uint8_t x1turbo_state::memory_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	return prog_space.read_byte(offset);
}

void x1turbo_state::memory_write_byte(offs_t offset, uint8_t data)
{
	address_space& prog_space = m_maincpu->space(AS_PROGRAM);
	return prog_space.write_byte(offset, data);
}

uint8_t x1turbo_state::io_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_IO);
	return prog_space.read_byte(offset);
}

void x1turbo_state::io_write_byte(offs_t offset, uint8_t data)
{
	address_space& prog_space = m_maincpu->space(AS_IO);
	return prog_space.write_byte(offset, data);
}

uint8_t x1_state::ym_r(offs_t offset)
{
	uint8_t result = m_ym->read(offset);
	// TODO: kingkngt x1turbo expects this to be high when in OPM mode
	// is it just covering some tight OPM busy flag?
	if (!BIT(offset, 0))
		result = (result & 0x7f) | (m_sound_sw->read() & 0x80);
	return result;
}

/*************************************
 *
 *  Placeholders
 *
 *************************************/

uint8_t x1_state::color_board_r(address_space &space)
{
	logerror("Color image board read\n");
	return space.unmap();
}

void x1_state::color_board_w(uint8_t data)
{
	logerror("Color image board write %02x\n", data);
}

uint8_t x1_state::color_board_2_r(address_space &space)
{
	logerror("Color image board 2 read\n");
	return space.unmap();
}

void x1_state::color_board_2_w(uint8_t data)
{
	logerror("Color image board 2 write %02x\n", data);
}

uint8_t x1_state::stereo_board_r(address_space &space, offs_t offset)
{
	logerror("Stereoscopic board read %04x\n", offset);
	return space.unmap();
}

void x1_state::stereo_board_w(offs_t offset, uint8_t data)
{
	logerror("Stereoscopic board write %04x %02x\n", offset, data);
}

uint8_t x1_state::rs232_r(offs_t offset)
{
	logerror("RS-232C read %04x\n", offset);
	return 0;
}

void x1_state::rs232_w(offs_t offset, uint8_t data)
{
	logerror("RS-232C write %04x %02x\n", offset, data);
}

uint8_t x1_state::sasi_r(address_space &space, offs_t offset)
{
	//logerror("SASI HDD read %04x\n",offset);
	return space.unmap();
}

void x1_state::sasi_w(offs_t offset, uint8_t data)
{
	logerror("SASI HDD write %04x %02x\n", offset, data);
}

uint8_t x1_state::fdd8_r(address_space &space, offs_t offset)
{
	logerror("8-inch FD read %04x\n", offset);
	return space.unmap();
}

void x1_state::fdd8_w(offs_t offset, uint8_t data)
{
	logerror("8-inch FD write %04x %02x\n", offset, data);
}

uint8_t x1_state::ext_sio_ctc_r(address_space &space, offs_t offset)
{
	logerror("Extended SIO/CTC read %04x\n", offset);
	return space.unmap();
}

void x1_state::ext_sio_ctc_w(offs_t offset, uint8_t data)
{
	logerror("Extended SIO/CTC write %04x %02x\n", offset, data);
}

void x1_state::z_img_cap_w(uint8_t data)
{
	logerror("Z image capturing access %02x\n", data);
}

void x1_state::z_mosaic_w(uint8_t data)
{
	logerror("Z mosaic effect access %02x\n", data);
}

void x1_state::z_chroma_key_w(uint8_t data)
{
	logerror("Z Chroma key access %02x\n", data);
}

void x1_state::z_extra_scroll_w(uint8_t data)
{
	logerror("Z Extra scroll config access %02x\n", data);
}


/*************************************
 *
 *  Inputs
 *
 *************************************/

INPUT_CHANGED_MEMBER(x1_state::ipl_reset)
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? CLEAR_LINE : ASSERT_LINE);

	m_ram_bank = 0x00;
	if(m_is_turbo) { m_ex_bank = 0x10; }
	//anything else?
}

// on 177 this makes the game to reset, on other games sending a NMI signal just causes a jump to la-la-land (including the Konami ones)
INPUT_CHANGED_MEMBER(x1_state::nmi_reset)
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, newval ? CLEAR_LINE : ASSERT_LINE);
}

INPUT_PORTS_START( x1 )
	PORT_START("FP_SYS") //front panel buttons, hard-wired with the soft reset/NMI lines
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x1_state::ipl_reset), 0) PORT_NAME("IPL reset")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x1_state::nmi_reset), 0) PORT_NAME("NMI reset")

	PORT_START("SOUND_SW")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IOSYS")
	// TODO: route front-panel DIP-SW here
	PORT_DIPNAME( 0x01, 0x01, "IOSYS" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, "Sound Setting?" )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("P1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )

	// TODO: move me to x1_keyboard_device
	PORT_START("key1") //0x00-0x1f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_UNUSED) //0x00 null
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0-2") PORT_CHAR(1)
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0-3") PORT_CHAR(2)
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("BREAK") PORT_CODE(KEYCODE_END) PORT_CHAR(3)
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0-5") PORT_CHAR(4)
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0-6") PORT_CHAR(5)
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0-7") PORT_CHAR(6)
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0-8") PORT_CHAR(7)
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("DEL INS") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("HTab") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1-3") PORT_CHAR(10)
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1-4") PORT_CHAR(11)
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1-5") PORT_CHAR(12)
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1-7") PORT_CHAR(14)
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1-8") PORT_CHAR(15)
	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2-1") PORT_CHAR(16)
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2-2") PORT_CHAR(17)
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2-3") PORT_CHAR(18)
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2-4") PORT_CHAR(19)
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2-5") PORT_CHAR(20)
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2-6") PORT_CHAR(21)
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2-7") PORT_CHAR(22)
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2-8") PORT_CHAR(23)
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3-1") PORT_CHAR(24)
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3-2") PORT_CHAR(25)
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3-3") PORT_CHAR(26)
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))

	PORT_START("key2") //0x20-0x3f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_UNUSED) //0x21 !
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_UNUSED) //0x22 "
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_UNUSED) //0x23 #
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_UNUSED) //0x24 $
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_UNUSED) //0x25 %
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_UNUSED) //0x26 &
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_UNUSED) //0x27 '
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_UNUSED) //0x28 (
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_UNUSED) //0x29 )
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_UNUSED) //0x2a *
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_UNUSED) //0x2b +
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')

	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x3c <
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x3d =
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x3e >
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x3f ?

	PORT_START("key3") //0x40-0x5f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@') PORT_CHAR('`')
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(u8"¥") PORT_CHAR(U'¥') PORT_CHAR('|')
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^')
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("_") PORT_CHAR('_')

	PORT_START("f_keys")
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CHAR(UCHAR_MAMEKEY(F6))
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CHAR(UCHAR_MAMEKEY(F7))
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CHAR(UCHAR_MAMEKEY(F8))
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_CHAR(UCHAR_MAMEKEY(F9))
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CHAR(UCHAR_MAMEKEY(F10))

	PORT_START("tenkey")
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey -") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey +") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey *") PORT_CODE(KEYCODE_ASTERISK)
	// TODO: add other numpad keys (comma, period, equals, enter, HOME/CLR)

	PORT_START("key_modifiers")
	PORT_BIT(0x00000001,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x00000002,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x00000004,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("KANA") PORT_CODE(KEYCODE_RCONTROL) PORT_TOGGLE
	PORT_BIT(0x00000008,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("CAPS") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT(0x00000010,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("GRAPH") PORT_CODE(KEYCODE_LALT)
INPUT_PORTS_END

INPUT_PORTS_START( x1turbo )
	PORT_INCLUDE( x1 )
	// TODO: add other keys (ROLL UP, ROLL DOWN, HELP, COPY, XFER)

	PORT_MODIFY("SOUND_SW")
	PORT_DIPNAME( 0x80, 0x80, "OPM Sound Setting?" )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("X1TURBO_DSW")
	PORT_DIPNAME( 0x01, 0x01, "Interlace mode" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x0e, 0x00, "Default Auto-boot Device" ) // this selects what kind of device is loaded at start-up
	PORT_DIPSETTING(    0x00, "5/3-inch 2D" )
	PORT_DIPSETTING(    0x02, "5/3-inch 2DD" )
	PORT_DIPSETTING(    0x04, "5/3-inch 2HD" )
	PORT_DIPSETTING(    0x06, "5/3-inch 2DD (IBM)" )
	PORT_DIPSETTING(    0x08, "8-inch 2D256" )
	PORT_DIPSETTING(    0x0a, "8-inch 2D256 (IBM)" )
	PORT_DIPSETTING(    0x0c, "8-inch 1S128 (IBM)" )
	PORT_DIPSETTING(    0x0e, "SASI HDD" )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) ) //this is a port conditional of some sort ...
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END

/*************************************
 *
 *  GFX decoding
 *
 *************************************/

static const gfx_layout x1_chars_8x8 =
{
	8,8,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};

static const gfx_layout x1_chars_8x16 =
{
	8,16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8,8*8,9*8,10*8,11*8,12*8,13*8,14*8,15*8 },
	8*16
};

static const gfx_layout x1_chars_16x16 =
{
	8,16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8,8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16
};

/* decoded for debugging purpose, this will be nuked in the end... */
static GFXDECODE_START( gfx_x1 )
	GFXDECODE_ENTRY( "cgrom",   0x00000, x1_chars_8x8,    0, 1 )
	GFXDECODE_ENTRY( "font",    0x00000, x1_chars_8x16,   0, 1 )
	GFXDECODE_ENTRY( "kanji",   0x00000, x1_chars_16x16,  0, 1 )
//  GFXDECODE_ENTRY( "pcg",     0x00000, x1_pcg_8x8,      0, 1 )
GFXDECODE_END

static const z80_daisy_config x1_daisy[] =
{
	{ "x1kb" },
	{ "ctc" },
	{ nullptr }
};

// TODO: verify order, suppose ctc_ym really goes as generic ext pin instead.
static const z80_daisy_config x1turbo_daisy[] =
{
	{ "x1kb" },
	{ "ctc_ym" },
	{ "ctc" },
	{ "dma" },
	{ "sio" },
	{ nullptr }
};

/*************************************
 *
 *  Machine Functions
 *
 *************************************/

#ifdef UNUSED_FUNCTION
IRQ_CALLBACK_MEMBER(x1_state::x1_irq_callback)
{
	if(m_ctc_irq_flag != 0)
	{
		m_ctc_irq_flag = 0;
		if(m_key_irq_flag == 0)  // if no other devices are pulling the IRQ line high
			device.execute().set_input_line(0, CLEAR_LINE);
		return m_irq_vector;
	}
	if(m_key_irq_flag != 0)
	{
		m_key_irq_flag = 0;
		if(m_ctc_irq_flag == 0)  // if no other devices are pulling the IRQ line high
			device.execute().set_input_line(0, CLEAR_LINE);
		return m_key_irq_vector;
	}
	return m_irq_vector;
}
#endif

TIMER_DEVICE_CALLBACK_MEMBER(x1_state::sub_keyboard_cb)
{
	uint32_t key1 = ioport("key1")->read();
	uint32_t key2 = ioport("key2")->read();
	uint32_t key3 = ioport("key3")->read();
	uint32_t key4 = ioport("tenkey")->read();
	uint32_t f_key = ioport("f_keys")->read();

	if(m_key_irq_vector)
	{
		//if(key1 == 0 && key2 == 0 && key3 == 0 && key4 == 0 && f_key == 0)
		//  return;

		if((key1 != m_old_key1) || (key2 != m_old_key2) || (key3 != m_old_key3) || (key4 != m_old_key4) || (f_key != m_old_fkey))
		{
			// generate keyboard IRQ
			sub_io_w(0xe6);
			m_irq_vector = m_key_irq_vector;
			m_key_irq_flag = 1;
			m_maincpu->set_input_line(0,ASSERT_LINE);
			m_old_key1 = key1;
			m_old_key2 = key2;
			m_old_key3 = key3;
			m_old_key4 = key4;
			m_old_fkey = f_key;
		}
	}
}

TIMER_CALLBACK_MEMBER(x1_state::rtc_tick_cb)
{
	static const uint8_t dpm[12] = { 0x31, 0x28, 0x31, 0x30, 0x31, 0x30, 0x31, 0x31, 0x30, 0x31, 0x30, 0x31 };

	m_rtc.sec++;

	if((m_rtc.sec & 0x0f) >= 0x0a)              { m_rtc.sec+=0x10; m_rtc.sec&=0xf0; }
	if((m_rtc.sec & 0xf0) >= 0x60)              { m_rtc.min++; m_rtc.sec = 0; }
	if((m_rtc.min & 0x0f) >= 0x0a)              { m_rtc.min+=0x10; m_rtc.min&=0xf0; }
	if((m_rtc.min & 0xf0) >= 0x60)              { m_rtc.hour++; m_rtc.min = 0; }
	if((m_rtc.hour & 0x0f) >= 0x0a)             { m_rtc.hour+=0x10; m_rtc.hour&=0xf0; }
	if((m_rtc.hour & 0xff) >= 0x24)             { m_rtc.day++; m_rtc.wday++; m_rtc.hour = 0; }
	if((m_rtc.wday & 0x0f) >= 0x07)             { m_rtc.wday = 0; }
	if((m_rtc.day & 0x0f) >= 0x0a)              { m_rtc.day+=0x10; m_rtc.day&=0xf0; }
	/* FIXME: very crude leap year support (i.e. it treats the RTC to be with a 2000-2099 timeline), dunno how the real x1 supports this,
	   maybe it just have a 1980-1999 timeline since year 0x00 shows as a XX on display */
	if(((m_rtc.year % 4) == 0) && m_rtc.month == 2)
	{
		if((m_rtc.day & 0xff) >= dpm[m_rtc.month-1]+1+1)
			{ m_rtc.month++; m_rtc.day = 0x01; }
	}
	else if((m_rtc.day & 0xff) >= dpm[m_rtc.month-1]+1){ m_rtc.month++; m_rtc.day = 0x01; }
	if(m_rtc.month > 12)                            { m_rtc.year++;  m_rtc.month = 0x01; }
	if((m_rtc.year & 0x0f) >= 0x0a)             { m_rtc.year+=0x10; m_rtc.year&=0xf0; }
	if((m_rtc.year & 0xf0) >= 0xa0)             { m_rtc.year = 0; } //roll over
}

void x1_state::machine_reset()
{
	//uint8_t *ROM = memregion("x1_cpu")->base();
	int i;

	memset(m_gfx_bitmap_ram.get(),0x00,0xc000*2);

	for(i=0;i<0x1800;i++)
	{
		m_pcg_ram[i] = 0;
		m_gfxdecode->gfx(3)->mark_dirty(i >> 3);
	}

	m_is_turbo = 0;

	m_iobank->set_bank(0);

	//m_x1_cpu->set_irq_acknowledge_callback(device_irq_acknowledge_delegate(FUNC(x1_state::x1_irq_callback),this));

	m_cmt_current_cmd = 0;
	m_cmt_test = 0;
	m_cassette->change_state(CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR);

	m_key_irq_flag = m_ctc_irq_flag = 0;
	m_sub_cmd = 0;
	m_key_irq_vector = 0;
	m_sub_cmd_length = 0;
	m_sub_val[0] = 0;
	m_sub_val[1] = 0;
	m_sub_val[2] = 0;
	m_sub_val[3] = 0;
	m_sub_val[4] = 0;
	m_sub_obf = (m_sub_cmd_length) ? 0x00 : 0x20;
	m_sub_val_ptr = 0;
	m_key_i = 0;
	m_scrn_reg.v400_mode = 0;
	m_scrn_reg.ank_sel = 0;

	m_rtc_timer->adjust(attotime::zero, 0, attotime::from_seconds(1));

	/* Reinitialize palette here if there's a soft reset for the Turbo PAL stuff*/
	for(i=0;i<0x10;i++)
		m_palette->set_pen_color(i, pal1bit(i >> 1), pal1bit(i >> 2), pal1bit(i >> 0));

	m_ram_bank = 0;
//  m_old_vpos = -1;

	m_fdc->dden_w(0);
}

void x1turbo_state::machine_reset()
{
	x1_state::machine_reset();
	m_is_turbo = 1;
	m_ex_bank = 0x10;

	m_scrn_reg.blackclip = 0;
}

static const gfx_layout x1_pcg_8x8 =
{
	8,8,
	0x100,
	3,
	{ 0x1000*8,0x800*8, 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};

void x1_state::machine_start()
{
	/* set up RTC */
	{
		system_time systime;
		machine().base_datetime(systime);

		m_rtc.day = ((systime.local_time.mday / 10)<<4) | ((systime.local_time.mday % 10) & 0xf);
		m_rtc.month = ((systime.local_time.month+1));
		m_rtc.wday = ((systime.local_time.weekday % 10) & 0xf);
		m_rtc.year = (((systime.local_time.year % 100)/10)<<4) | ((systime.local_time.year % 10) & 0xf);
		m_rtc.hour = ((systime.local_time.hour / 10)<<4) | ((systime.local_time.hour % 10) & 0xf);
		m_rtc.min = ((systime.local_time.minute / 10)<<4) | ((systime.local_time.minute % 10) & 0xf);
		m_rtc.sec = ((systime.local_time.second / 10)<<4) | ((systime.local_time.second % 10) & 0xf);

		m_rtc_timer = timer_alloc(FUNC(x1_state::rtc_tick_cb), this);
	}

	m_motor_timer = timer_alloc(FUNC(x1_state::fdc_motor_off_cb), this);
	m_work_ram = make_unique_clear<uint8_t[]>(0x10000*0x10);
	m_emm_ram = make_unique_clear<uint8_t[]>(0x1000000);
	m_pcg_ram = make_unique_clear<uint8_t[]>(0x1800);

	save_pointer(NAME(m_work_ram), 0x10000*0x10);
	save_pointer(NAME(m_emm_ram), 0x1000000);
	save_pointer(NAME(m_pcg_ram), 0x1800);
	save_item(STRUCT_MEMBER(m_scrn_reg, video_mode));

	m_gfxdecode->set_gfx(3, std::make_unique<gfx_element>(m_palette, x1_pcg_8x8, m_pcg_ram.get(), 0, 1, 0));
}

void x1_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_2D_FORMAT);
}

static void x1_floppies(device_slot_interface &device)
{
	// TODO: 3" (!?) and 8" options, verify if vanilla X1 has them all
	device.option_add("525dd", FLOPPY_525_DD);
//  device.option_add("525hd", FLOPPY_525_HD);
}

void x1_state::x1(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, MAIN_CLOCK/4);
	m_maincpu->set_addrmap(AS_PROGRAM, &x1_state::x1_mem);
	m_maincpu->set_addrmap(AS_IO, &x1_state::x1_io);
	m_maincpu->set_daisy_config(x1_daisy);

	ADDRESS_MAP_BANK(config, m_iobank).set_map(&x1_state::x1_io_banks).set_options(ENDIANNESS_LITTLE, 8, 17, 0x10000);

	z80ctc_device& ctc(Z80CTC(config, "ctc", MAIN_CLOCK/4));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	ctc.zc_callback<0>().set("ctc", FUNC(z80ctc_device::trg3));
	// TODO: clocks for SIO
	ctc.zc_callback<1>().set("ctc", FUNC(z80ctc_device::trg1));
	ctc.zc_callback<2>().set("ctc", FUNC(z80ctc_device::trg2));

	X1_KEYBOARD(config, "x1kb", 0);

	i8255_device &ppi(I8255A(config, "ppi8255_0"));
	ppi.in_pa_callback().set(FUNC(x1_state::x1_porta_r));
	ppi.in_pb_callback().set(FUNC(x1_state::x1_portb_r));
	ppi.in_pc_callback().set(FUNC(x1_state::x1_portc_r));
	ppi.out_pa_callback().set(FUNC(x1_state::x1_porta_w));
	ppi.out_pb_callback().set(FUNC(x1_state::x1_portb_w));
	ppi.out_pc_callback().set(FUNC(x1_state::x1_portc_w));

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_size(640, 480);
	m_screen->set_visarea(0, 640-1, 0, 480-1);
	m_screen->set_screen_update(FUNC(x1_state::screen_update_x1));
	// add a saner default for both interlace and progressive modes
	m_screen->set_default_position(1.100, 0.050, 1.100, 0.050);

	HD6845S(config, m_crtc, (VDP_CLOCK/48)); //unknown divider
	m_crtc->set_screen(m_screen);
	m_crtc->set_show_border_area(true);
	m_crtc->set_char_width(8);

	PALETTE(config, m_palette, palette_device::BLACK, 0x10+0x1000);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_x1);

	MB8877(config, m_fdc, 16_MHz_XTAL / 16); // clocked by SED9421C0B

	FLOPPY_CONNECTOR(config, "fdc:0", x1_floppies, "525dd", x1_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", x1_floppies, "525dd", x1_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:2", x1_floppies, "525dd", x1_state::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:3", x1_floppies, "525dd", x1_state::floppy_formats).enable_sound(true);

	// TODO: convert to CZ- expansion unit, verify compatibility with x68k if any.
	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "x1_cart", "bin,rom");

	SPEAKER(config, "speaker", 2).front();

	// TODO: fix thru schematics (formation of resistors tied to ABC outputs)
	ay8910_device &ay(AY8910(config, "ay", MAIN_CLOCK/8));
	ay.port_a_read_callback().set_ioport("P1");
	ay.port_b_read_callback().set_ioport("P2");
	ay.add_route(ALL_OUTPUTS, "speaker", 0.25, 0);
	ay.add_route(ALL_OUTPUTS, "speaker", 0.25, 1);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(x1_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "speaker", 0.25, 0).add_route(ALL_OUTPUTS, "speaker", 0.10, 1);
	m_cassette->set_interface("x1_cass");

	TIMER(config, "keyboard_timer").configure_periodic(FUNC(x1_state::sub_keyboard_cb), attotime::from_hz(250));
	TIMER(config, "cmt_wind_timer").configure_periodic(FUNC(x1_state::cmt_seek_cb), attotime::from_hz(16));

	SOFTWARE_LIST(config, "cass_list").set_original("x1_cass");
	SOFTWARE_LIST(config, "flop_list").set_original("x1_flop");
	SOFTWARE_LIST(config, "flop_generic_list").set_compatible("generic_flop_525").set_filter("x1");
}

void x1turbo_state::x1turbo(machine_config &config)
{
	x1(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &x1turbo_state::x1turbo_mem);
	m_maincpu->set_daisy_config(x1turbo_daisy);
	m_maincpu->busack_cb().set(m_dma, FUNC(z80dma_device::bai_w));

	m_iobank->set_map(&x1turbo_state::x1turbo_io_banks);

	z80sio_device& sio(Z80SIO(config, "sio", MAIN_CLOCK/4));
	sio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80DMA(config, m_dma, MAIN_CLOCK/4);
	m_dma->out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	m_dma->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_dma->in_mreq_callback().set(FUNC(x1turbo_state::memory_read_byte));
	m_dma->out_mreq_callback().set(FUNC(x1turbo_state::memory_write_byte));
	m_dma->in_iorq_callback().set(FUNC(x1turbo_state::io_read_byte));
	m_dma->out_iorq_callback().set(FUNC(x1turbo_state::io_write_byte));

	m_fdc->drq_wr_callback().set(FUNC(x1turbo_state::fdc_drq_w));

	// TODO: as sub-board option, CZ-8BS1
	Z80CTC(config, m_ctc_ym, MAIN_CLOCK/4);
	// FIXME: check intr
	m_ctc_ym->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc_ym->zc_callback<0>().set(m_ctc_ym, FUNC(z80ctc_device::trg3));

	YM2151(config, m_ym, MAIN_CLOCK/8);
	m_ym->add_route(0, "speaker", 0.50, 0);
	m_ym->add_route(1, "speaker", 0.50, 1);
}

/*************************************
 *
 * ROM definitions
 *
 *************************************/

ROM_START( x1 )
	ROM_REGION( 0x8000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "ipl.x1", 0x0000, 0x1000, CRC(7b28d9de) SHA1(c4db9a6e99873808c8022afd1c50fef556a8b44d) )

	ROM_REGION(0x1000, "mcu", ROMREGION_ERASEFF) //MCU for the Keyboard, "sub cpu"
	ROM_LOAD( "80c48", 0x0000, 0x1000, NO_DUMP )

	ROM_REGION(0x2000, "font", 0) //TODO: this contains 8x16 charset only, maybe it's possible that it derivates a 8x8 charset by skipping gfx lines?
	ROM_LOAD( "ank.fnt", 0x0000, 0x2000, BAD_DUMP CRC(19689fbd) SHA1(0d4e072cd6195a24a1a9b68f1d37500caa60e599) )

	ROM_REGION(0x1800, "cgrom", 0)
	ROM_LOAD("fnt0808.x1",  0x00000, 0x00800, CRC(e3995a57) SHA1(1c1a0d8c9f4c446ccd7470516b215ddca5052fb2) )
	ROM_COPY("font",    0x1000, 0x00800, 0x1000 )

	ROM_REGION(0x20000, "kanji", ROMREGION_ERASEFF)

	ROM_REGION(0x20000, "raw_kanji", ROMREGION_ERASEFF)
ROM_END

ROM_START( x1turbo )
	ROM_REGION( 0x8000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "ipl.x1t", 0x0000, 0x8000, CRC(2e8b767c) SHA1(44620f57a25f0bcac2b57ca2b0f1ebad3bf305d3) )

	ROM_REGION(0x1000, "mcu", ROMREGION_ERASEFF) //MCU for the Keyboard, "sub cpu"
	ROM_LOAD( "80c48", 0x0000, 0x1000, NO_DUMP )

	ROM_REGION(0x2000, "font", 0) //TODO: this contains 8x16 charset only, maybe it's possible that it derivates a 8x8 charset by skipping gfx lines?
	ROM_LOAD( "ank.fnt", 0x0000, 0x2000, CRC(19689fbd) SHA1(0d4e072cd6195a24a1a9b68f1d37500caa60e599) )

	ROM_REGION(0x4800, "cgrom", 0)
	ROM_LOAD("fnt0808_turbo.x1", 0x00000, 0x00800, CRC(84a47530) SHA1(06c0995adc7a6609d4272417fe3570ca43bd0454) )
	ROM_COPY("font",             0x01000, 0x00800, 0x1000 )

	ROM_REGION(0x20000, "kanji", ROMREGION_ERASEFF)

	ROM_REGION(0x20000, "raw_kanji", ROMREGION_ERASEFF)
	ROM_LOAD("kanji4.rom", 0x00000, 0x8000, CRC(3e39de89) SHA1(d3fd24892bb1948c4697dedf5ff065ff3eaf7562) )
	ROM_LOAD("kanji2.rom", 0x08000, 0x8000, CRC(e710628a) SHA1(103bbe459dc8da27a9400aa45b385255c18fcc75) )
	ROM_LOAD("kanji3.rom", 0x10000, 0x8000, CRC(8cae13ae) SHA1(273f3329c70b332f6a49a3a95e906bbfe3e9f0a1) )
	ROM_LOAD("kanji1.rom", 0x18000, 0x8000, CRC(5874f70b) SHA1(dad7ada1b70c45f1e9db11db273ef7b385ef4f17) )
ROM_END

ROM_START( x1turbo40 )
	ROM_REGION( 0x8000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "ipl.bin", 0x0000, 0x8000, CRC(112f80a2) SHA1(646cc3fb5d2d24ff4caa5167b0892a4196e9f843) )

	ROM_REGION(0x1000, "mcu", ROMREGION_ERASEFF) //MCU for the Keyboard, "sub cpu"
	ROM_LOAD( "80c48", 0x0000, 0x1000, NO_DUMP )

	ROM_REGION(0x2000, "font", 0) //TODO: this contains 8x16 charset only, maybe it's possible that it derivates a 8x8 charset by skipping gfx lines?
	ROM_LOAD( "ank.fnt", 0x0000, 0x2000, CRC(19689fbd) SHA1(0d4e072cd6195a24a1a9b68f1d37500caa60e599) )

	ROM_REGION(0x4800, "cgrom", 0)
	ROM_LOAD("fnt0808_turbo.x1",0x00000, 0x0800, CRC(84a47530) SHA1(06c0995adc7a6609d4272417fe3570ca43bd0454) )
	ROM_COPY("font",            0x01000, 0x0800, 0x1000 )

	ROM_REGION(0x20000, "kanji", ROMREGION_ERASEFF)

	ROM_REGION(0x20000, "raw_kanji", ROMREGION_ERASEFF)
	ROM_LOAD("kanji4.rom", 0x00000, 0x8000, CRC(3e39de89) SHA1(d3fd24892bb1948c4697dedf5ff065ff3eaf7562) )
	ROM_LOAD("kanji2.rom", 0x08000, 0x8000, CRC(e710628a) SHA1(103bbe459dc8da27a9400aa45b385255c18fcc75) )
	ROM_LOAD("kanji3.rom", 0x10000, 0x8000, CRC(8cae13ae) SHA1(273f3329c70b332f6a49a3a95e906bbfe3e9f0a1) )
	ROM_LOAD("kanji1.rom", 0x18000, 0x8000, CRC(5874f70b) SHA1(dad7ada1b70c45f1e9db11db273ef7b385ef4f17) )
ROM_END


/* Convert the ROM interleaving into something usable by the write handlers */
void x1_state::init_x1_kanji()
{
	uint8_t *kanji = memregion("kanji")->base();
	uint8_t *raw_kanji = memregion("raw_kanji")->base();

	uint32_t k = 0;
	for (uint32_t l=0; l < 2; l++)
	{
		for (uint32_t j = l*16; j < (l*16) + 0x10000; j += 32)
		{
			for (uint32_t i = 0; i  < 16; i++)
			{
				kanji[j + i] = raw_kanji[k];
				kanji[j + i + 0x10000] = raw_kanji[0x10000 + k];
				k++;
			}
		}
	}
}


COMP( 1982, x1,        0,      0,      x1,      x1,      x1_state,      empty_init,    "Sharp", "X1 (CZ-800C)",       0 )
// x1twin in x1twin.cpp
COMP( 1984, x1turbo,   x1,     0,      x1turbo, x1turbo, x1turbo_state, init_x1_kanji, "Sharp", "X1 Turbo (CZ-850C)", MACHINE_NOT_WORKING ) //model 10
COMP( 1985, x1turbo40, x1,     0,      x1turbo, x1turbo, x1turbo_state, init_x1_kanji, "Sharp", "X1 Turbo (CZ-862C)", 0 ) //model 40
//COMP( 1986, x1turboz,  x1,     0,      x1turbo, x1turbo, x1_state, init_x1_kanji, "Sharp", "X1 TurboZ", MACHINE_NOT_WORKING )



x1twin.cpp
<---------------------------------------------------------------------->
// license:LGPL-2.1+
// copyright-holders:Angelo Salese
/************************************************************************************************

Sharp X1Twin = Sharp X1 + NEC PC Engine All-in-One

Both systems don't interact at all, according to info on the net they just share the
same "house". It doesn't even do super-imposing, not even with the in-built X1 feature apparently

TODO:
- Find 100% trusted info about it.
- Work out this to really be a middleground for both pce_state & x1twin_state
- Needs video mods

************************************************************************************************/

#include "emu.h"
#include "x1.h"
//#include "nec/pce.h" FIXME: turn necessary parts of PCE into a device that can be put somewhere common

#include "video/huc6260.h"

#include "layout/generic.h"
#include "screen.h"
#include "softlist.h"
#include "softlist_dev.h"
#include "speaker.h"


// copied from pce.h until it's turned into a device properly
#define MAIN_CLOCK      21477270

namespace {

class x1twin_state : public x1_state
{
public:
	x1twin_state(const machine_config &mconfig, device_type type, const char *tag) :
		x1_state(mconfig, type, tag)
	{ }

	void x1twin(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(ipl_reset);
	DECLARE_INPUT_CHANGED_MEMBER(nmi_reset);

private:
	uint32_t screen_update_x1pce(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
	void pce_io(address_map &map) ATTR_COLD;
	void pce_mem(address_map &map) ATTR_COLD;
	void x1_io(address_map &map) ATTR_COLD;
	void x1_mem(address_map &map) ATTR_COLD;
};


#define X1_MAIN_CLOCK 16_MHz_XTAL
#define VDP_CLOCK  42.954545_MHz_XTAL
#define MCU_CLOCK  6_MHz_XTAL
#define PCE_MAIN_CLOCK      VDP_CLOCK / 2

uint32_t x1twin_state::screen_update_x1pce(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void x1twin_state::x1_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).rw(FUNC(x1twin_state::mem_r), FUNC(x1twin_state::mem_w));
}

void x1twin_state::x1_io(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xffff).m(m_iobank, FUNC(address_map_bank_device::amap8));
}

#if 0
void x1twin_state::pce_mem(address_map &map)
{
	map(0x000000, 0x09FFFF).rom();
	map(0x1F0000, 0x1F1FFF).ram().mirror(0x6000);
	map(0x1FE000, 0x1FE3FF).rw(FUNC(x1twin_state::vdc_r), FUNC(x1twin_state::vdc_w));
	map(0x1FE400, 0x1FE7FF).rw(FUNC(x1twin_state::vce_r), FUNC(x1twin_state::vce_w));
}

void x1twin_state::pce_io(address_map &map)
{
	map(0x00, 0x03).rw(FUNC(x1twin_state::vdc_r), FUNC(x1twin_state::vdc_w));
}
#endif

/*************************************
 *
 *  Inputs
 *
 *************************************/

INPUT_CHANGED_MEMBER(x1twin_state::ipl_reset)
{
	//address_space &space = m_maincpu->space(AS_PROGRAM);

	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? CLEAR_LINE : ASSERT_LINE);

	m_ram_bank = 0x00;
	if(m_is_turbo) { m_ex_bank = 0x10; }
	//anything else?
}

/* Apparently most games don't support this (not even the Konami ones!), one that does is...177 :o */
INPUT_CHANGED_MEMBER(x1twin_state::nmi_reset)
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, newval ? CLEAR_LINE : ASSERT_LINE);
}

INPUT_PORTS_START( x1twin )
	PORT_START("FP_SYS") //front panel buttons, hard-wired with the soft reset/NMI lines
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x1twin_state::ipl_reset), 0) PORT_NAME("IPL reset")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(x1twin_state::nmi_reset), 0) PORT_NAME("NMI reset")

	PORT_START("SOUND_SW") //FIXME: this is X1Turbo specific
	PORT_DIPNAME( 0x80, 0x80, "OPM Sound Setting?" )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IOSYS") //TODO: implement front-panel DIP-SW here
	PORT_DIPNAME( 0x01, 0x01, "IOSYS" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, "Sound Setting?" )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("P1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("P2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("key1") //0x00-0x1f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_UNUSED) //0x00 null
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0-2") /*PORT_CODE(KEYCODE_1) PORT_CHAR('1')*/
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0-3") /*PORT_CODE(KEYCODE_2) PORT_CHAR('2')*/
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0-4") /*PORT_CODE(KEYCODE_3) PORT_CHAR('3')*/
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0-5") /*PORT_CODE(KEYCODE_4) PORT_CHAR('4')*/
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0-6") /*PORT_CODE(KEYCODE_5) PORT_CHAR('5')*/
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0-7") /*PORT_CODE(KEYCODE_6) PORT_CHAR('6')*/
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0-8") /*PORT_CODE(KEYCODE_7) PORT_CHAR('7')*/
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9)
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1-3") /*PORT_CODE(KEYCODE_2) PORT_CHAR('2')*/
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1-4") /*PORT_CODE(KEYCODE_3) PORT_CHAR('3')*/
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1-5") /*PORT_CODE(KEYCODE_4) PORT_CHAR('4')*/
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(27)
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1-7") /*PORT_CODE(KEYCODE_4) PORT_CHAR('4')*/
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1-8") /*PORT_CODE(KEYCODE_4) PORT_CHAR('4')*/
	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2-1") /*PORT_CODE(KEYCODE_1) PORT_CHAR('1')*/
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2-2") /*PORT_CODE(KEYCODE_1) PORT_CHAR('1')*/
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2-3") /*PORT_CODE(KEYCODE_2) PORT_CHAR('2')*/
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2-4") /*PORT_CODE(KEYCODE_3) PORT_CHAR('3')*/
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2-5") /*PORT_CODE(KEYCODE_4) PORT_CHAR('4')*/
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2-6") /*PORT_CODE(KEYCODE_5) PORT_CHAR('5')*/
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2-7") /*PORT_CODE(KEYCODE_4) PORT_CHAR('4')*/
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2-8") /*PORT_CODE(KEYCODE_5) PORT_CHAR('5')*/
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3-1") /*PORT_CODE(KEYCODE_1) PORT_CHAR('1')*/
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3-2") /*PORT_CODE(KEYCODE_1) PORT_CHAR('1')*/
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3-3") /*PORT_CODE(KEYCODE_2) PORT_CHAR('2')*/
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)

	PORT_START("key2") //0x20-0x3f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_UNUSED) //0x21 !
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_UNUSED) //0x22 "
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_UNUSED) //0x23 #
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_UNUSED) //0x24 $
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_UNUSED) //0x25 %
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_UNUSED) //0x26 &
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_UNUSED) //0x27 '
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_UNUSED) //0x28 (
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_UNUSED) //0x29 )
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_UNUSED) //0x2a *
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_UNUSED) //0x2b +
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x2c ,
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x2e .
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x2f /

	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':')
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON) PORT_CHAR(';')
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x3c <
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x3d =
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x3e >
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_UNUSED) //0x3f ?

	PORT_START("key3") //0x40-0x5f
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@')
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('J')
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('K')
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('L')
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('N')
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('O')
	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('P')
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT(0x00100000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT(0x00200000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('V')
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[')
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_UNUSED)
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(']')
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^')
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("_")

	PORT_START("f_keys")
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5)

	PORT_START("tenkey")
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey -") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey +") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey *") PORT_CODE(KEYCODE_ASTERISK)
	// TODO: add other numpad keys

	PORT_START("key_modifiers")
	PORT_BIT(0x00000001,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x00000002,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x00000004,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("KANA") PORT_CODE(KEYCODE_RCONTROL)
	PORT_BIT(0x00000008,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("CAPS") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT(0x00000010,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("GRPH") PORT_CODE(KEYCODE_LALT)

#if 0
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',')
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.')
	PORT_BIT(0x00080000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/')
	PORT_BIT(0x00400000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey *") PORT_CODE(KEYCODE_ASTERISK)
	PORT_BIT(0x00800000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey /") PORT_CODE(KEYCODE_SLASH_PAD)
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey +") PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey -") PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 7") PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 8") PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 9") PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x20000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey =")
	PORT_BIT(0x40000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 4") PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x80000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 5") PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("\xEF\xBF\xA5")

	PORT_START("key3")
	PORT_BIT(0x00000001,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 6") PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x00000002,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey ,")
	PORT_BIT(0x00000004,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 1") PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x00000008,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 2") PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x00000010,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 3") PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x00000020,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey Enter") PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x00000040,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey 0") PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x00000080,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Tenkey .") PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x00000100,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("INS") PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x00000200,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("EL") PORT_CODE(KEYCODE_PGUP)
	PORT_BIT(0x00000400,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("CLS") PORT_CODE(KEYCODE_PGDN)
	PORT_BIT(0x00000800,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x00001000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("DUP") PORT_CODE(KEYCODE_END)
	PORT_BIT(0x00002000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x00004000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("HOME") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x00008000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x00010000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x00020000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x00040000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("BREAK") PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x01000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF6") PORT_CODE(KEYCODE_F6)
	PORT_BIT(0x02000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF7") PORT_CODE(KEYCODE_F7)
	PORT_BIT(0x04000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF8") PORT_CODE(KEYCODE_F8)
	PORT_BIT(0x08000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF9") PORT_CODE(KEYCODE_F9)
	PORT_BIT(0x10000000,IP_ACTIVE_HIGH,IPT_KEYBOARD) PORT_NAME("PF10") PORT_CODE(KEYCODE_F10)

#endif
INPUT_PORTS_END


/*************************************
 *
 *  GFX decoding
 *
 *************************************/

static const gfx_layout x1_chars_8x8 =
{
	8,8,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};

static const gfx_layout x1_chars_8x16 =
{
	8,16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8,8*8,9*8,10*8,11*8,12*8,13*8,14*8,15*8 },
	8*16
};

static const gfx_layout x1_pcg_8x8 =
{
	8,8,
	RGN_FRAC(1,3),
	3,
	{ RGN_FRAC(2,3),RGN_FRAC(1,3),RGN_FRAC(0,3) },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};

static const gfx_layout x1_chars_16x16 =
{
	8,16,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8,8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16
};

/* decoded for debugging purpose, this will be nuked in the end... */
static GFXDECODE_START( gfx_x1 )
	GFXDECODE_ENTRY( "cgrom",   0x00000, x1_chars_8x8,    0, 1 )
	GFXDECODE_ENTRY( "pcg",     0x00000, x1_pcg_8x8,      0, 1 )
	GFXDECODE_ENTRY( "font",    0x00000, x1_chars_8x16,   0, 1 )
	GFXDECODE_ENTRY( "kanji",   0x00000, x1_chars_16x16,  0, 1 )
GFXDECODE_END

static const z80_daisy_config x1_daisy[] =
{
	{ "x1kb" },
	{ "ctc" },
	{ nullptr }
};

static void x1_floppies(device_slot_interface &device)
{
	device.option_add("dd", FLOPPY_525_DD);
}

void x1twin_state::x1twin(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, X1_MAIN_CLOCK/4);
	m_maincpu->set_addrmap(AS_PROGRAM, &x1twin_state::x1_mem);
	m_maincpu->set_addrmap(AS_IO, &x1twin_state::x1_io);
	m_maincpu->set_daisy_config(x1_daisy);

	ADDRESS_MAP_BANK(config, "iobank").set_map(&x1_state::x1_io_banks).set_options(ENDIANNESS_LITTLE, 8, 17, 0x10000);

	z80ctc_device& ctc(Z80CTC(config, "ctc", MAIN_CLOCK/4));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	ctc.zc_callback<0>().set("ctc", FUNC(z80ctc_device::trg3));
	ctc.zc_callback<1>().set("ctc", FUNC(z80ctc_device::trg1));
	ctc.zc_callback<2>().set("ctc", FUNC(z80ctc_device::trg2));

	X1_KEYBOARD(config, "x1kb", 0);

	i8255_device &ppi(I8255A(config, "ppi8255_0"));
	ppi.in_pa_callback().set(FUNC(x1_state::x1_porta_r));
	ppi.in_pb_callback().set(FUNC(x1_state::x1_portb_r));
	ppi.in_pc_callback().set(FUNC(x1_state::x1_portc_r));
	ppi.out_pa_callback().set(FUNC(x1_state::x1_porta_w));
	ppi.out_pb_callback().set(FUNC(x1_state::x1_portb_w));
	ppi.out_pc_callback().set(FUNC(x1_state::x1_portc_w));

	#if 0
	H6280(config, m_maincpu, PCE_MAIN_CLOCK/3);
	m_maincpu->set_addrmap(AS_PROGRAM, pce_mem);
	m_maincpu->set_addrmap(AS_IO, pce_io);
	m_maincpu->port_in_cb().set(FUNC(x1twin_state::pce_joystick_r));
	m_maincpu->port_out_cb().set(FUNC(x1twin_state::pce_joystick_w));
	m_maincpu->add_route(0, "pce_l", 0.5);
	m_maincpu->add_route(1, "pce_r", 0.5);

	TIMER(config, "scantimer").configure_scanline(FUNC(x1twin_state::pce_interrupt), "pce_screen", 0, 1);
	#endif

	config.set_default_layout(layout_dualhsxs);
	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	m_screen->set_size(640, 480);
	m_screen->set_visarea(0, 640-1, 0, 480-1);
	m_screen->set_screen_update(FUNC(x1twin_state::screen_update_x1));

	screen_device &pce_screen(SCREEN(config, "pce_screen", SCREEN_TYPE_RASTER));
	pce_screen.set_refresh_hz(60);
	pce_screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	pce_screen.set_raw(PCE_MAIN_CLOCK/2, huc6260_device::WPF, 70, 70 + 512 + 32, huc6260_device::LPF, 14, 14+242);
	pce_screen.set_screen_update(FUNC(x1twin_state::screen_update_x1pce));

	HD6845S(config, m_crtc, (VDP_CLOCK/48)); //unknown divider (TODO: verify chip type)
	m_crtc->set_screen(m_screen);
	m_crtc->set_show_border_area(true);
	m_crtc->set_char_width(8);

	PALETTE(config, m_palette, palette_device::BLACK, 0x10+0x1000);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_x1);

	MB8877(config, m_fdc, MAIN_CLOCK / 16);

	FLOPPY_CONNECTOR(config, "fdc:0", x1_floppies, "dd", x1_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:1", x1_floppies, "dd", x1_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:2", x1_floppies, "dd", x1_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "fdc:3", x1_floppies, "dd", x1_state::floppy_formats);

	SOFTWARE_LIST(config, "flop_list").set_original("x1_flop");

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "x1_cart", "bin,rom");

	SPEAKER(config, "x1_l").front_left();
	SPEAKER(config, "x1_r").front_right();
	SPEAKER(config, "pce_l").front_left();
	SPEAKER(config, "pce_r").front_right();

//  SPEAKER(config, "speaker", 2).front();

	/* TODO:is the AY mono or stereo? Also volume balance isn't right. */
	ay8910_device &ay(AY8910(config, "ay", MAIN_CLOCK/8));
	ay.port_a_read_callback().set_ioport("P1");
	ay.port_b_read_callback().set_ioport("P2");
	ay.add_route(ALL_OUTPUTS, "x1_l", 0.25);
	ay.add_route(ALL_OUTPUTS, "x1_r", 0.25);

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(x1_cassette_formats);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "x1_l", 0.25).add_route(ALL_OUTPUTS, "x1_r", 0.10);
	m_cassette->set_interface("x1_cass");

	SOFTWARE_LIST(config, "cass_list").set_original("x1_cass");

	TIMER(config, "keyboard_timer").configure_periodic(FUNC(x1twin_state::sub_keyboard_cb), attotime::from_hz(250));
	TIMER(config, "cmt_wind_timer").configure_periodic(FUNC(x1twin_state::cmt_seek_cb), attotime::from_hz(16));
}

ROM_START( x1twin )
	ROM_REGION( 0x10000, "x1_cpu", ROMREGION_ERASEFF )

	ROM_REGION( 0x8000, "ipl", ROMREGION_ERASEFF )
	ROM_LOAD( "ipl.rom", 0x0000, 0x1000, CRC(e70011d3) SHA1(d3395e9aeb5b8bbba7654dd471bcd8af228ee69a) )

	ROM_REGION( 0x10000, "wram", ROMREGION_ERASE00 )

	ROM_REGION(0x1000, "mcu", ROMREGION_ERASEFF) //MCU for the Keyboard, "sub cpu"
	ROM_LOAD( "80c48", 0x0000, 0x1000, NO_DUMP )

	ROM_REGION( 0x1000000, "emm", ROMREGION_ERASEFF )

	ROM_REGION(0x1800, "pcg", ROMREGION_ERASEFF)

	ROM_REGION(0x1000, "font", 0) //TODO: this contains 8x16 charset only, maybe it's possible that it derivates a 8x8 charset by skipping gfx lines?
	ROM_LOAD( "ank16.rom", 0x0000, 0x1000, CRC(8f9fb213) SHA1(4f06d20c997a79ee6af954b69498147789bf1847) )

	ROM_REGION(0x1800, "cgrom", 0)
	ROM_LOAD("ank8.rom", 0x00000, 0x00800, CRC(e3995a57) SHA1(1c1a0d8c9f4c446ccd7470516b215ddca5052fb2) )
	ROM_COPY("font",     0x00000, 0x00800, 0x1000 )

	ROM_REGION(0x20000, "kanji", ROMREGION_ERASEFF)

	ROM_REGION(0x20000, "raw_kanji", ROMREGION_ERASEFF) // these come from x1 turbo
	ROM_LOAD("kanji4.rom", 0x00000, 0x8000, BAD_DUMP CRC(3e39de89) SHA1(d3fd24892bb1948c4697dedf5ff065ff3eaf7562) )
	ROM_LOAD("kanji2.rom", 0x08000, 0x8000, BAD_DUMP CRC(e710628a) SHA1(103bbe459dc8da27a9400aa45b385255c18fcc75) )
	ROM_LOAD("kanji3.rom", 0x10000, 0x8000, BAD_DUMP CRC(8cae13ae) SHA1(273f3329c70b332f6a49a3a95e906bbfe3e9f0a1) )
	ROM_LOAD("kanji1.rom", 0x18000, 0x8000, BAD_DUMP CRC(5874f70b) SHA1(dad7ada1b70c45f1e9db11db273ef7b385ef4f17) )
ROM_END

} // Anonymous namespace


COMP( 1986, x1twin, x1, 0, x1twin, x1twin, x1twin_state, init_x1_kanji, "Sharp", "X1 Twin (CZ-830C)", MACHINE_NOT_WORKING )



x68k.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Barry Rodewald,Carl

// Preliminary X68000 driver for MESS
// Started 18/11/2006
// Written by Barry Rodewald

/*
    *** Basic memory map

    0x000000 - 0xbfffff     RAM (Max 12MB), vector table from ROM at 0xff0000 maps to 0x000000 at reset only
    0xc00000 - 0xdfffff     Graphic VRAM
    0xe00000 - 0xe1ffff     Text VRAM Plane 1
    0xe20000 - 0xe3ffff     Text VRAM Plane 2
    0xe40000 - 0xe5ffff     Text VRAM Plane 3
    0xe60000 - 0xe7ffff     Text VRAM Plane 4
    0xe80000                CRTC
    0xe82000                Video Controller
    0xe84000                DMA Controller
    0xe86000                Supervisor Area set
    0xe88000                MFP
    0xe8a000                RTC
    0xe8c000                Printer
    0xe8e000                System Port (?)
    0xe90000                FM Sound source
    0xe92000                ADPCM
    0xe94000                FDC
    0xe96000                HDC
    0xe96021                SCSI (internal model)
    0xe98000                SCC
    0xe9a000                Serial I/O (PPI)
    0xe9c000                I/O controller

    [Expansions]
    0xe9c000 / 0xe9e000     FPU (Optional, X68000 only)
    0xea0000                SCSI
    0xeaf900                FAX
    0xeafa00 / 0xeafa10     MIDI (1st/2nd)
    0xeafb00                Serial
    0xeafc00/10/20/30       EIA232E
    0xeafd00                EIA232E
    0xeafe00                GPIB (?)
    0xec0000 - 0xecffff     User I/O Expansion

    0xeb0000 - 0xeb7fff     Sprite registers
    0xeb8000 - 0xebffff     Sprite VRAM
    0xed0000 - 0xed3fff     SRAM
    0xf00000 - 0xfb0000     ROM  (CGROM.DAT)
    0xfe0000 - 0xffffff     ROM  (IPLROM.DAT)


    *** System hardware

    CPU : X68000: 68000 at 10MHz
          X68000 XVI: 68000 at 16MHz
          X68030: 68EC030 at 25MHz

    RAM : between 1MB and 4MB stock, expandable to 12MB

    FDD : 2x 5.25", Compact models use 2x 3.5" drives.
    FDC : NEC uPD72065

    HDD : HD models have up to an 81MB HDD.
    HDC : Fujitsu MB89352A (SCSI)

    SCC : Serial controller - Zilog z85C30  (Dual channel, 1 for RS232, 1 for mouse)
    PPI : Parallel controller  - NEC 8255   (Printer, Joystick)

    Sound : FM    - YM2151, with YM3012 DAC
            ADPCM - Okidata MSM6258

    DMA : Hitachi HD63450, DMA I/O for FDD, HDD, Expansion slots, and ADPCM

    MFP : Motorola MC68901 - monitor sync, serial port, RTC, soft power, FM synth, IRQs, keyboard

    RTC : Ricoh RP5C15

    ...plus a number of custom chips for video and other stuff...


    *** Current status (28/12/08)
    MFP : Largely works, as far as the X68000 goes.

    PPI : Joystick controls work okay.

    HDC/HDD : SCSI is not implemented, not a requirement at this point.

    RTC : Seems to work. (Tested using SX-Window's Timer application)

    DMA : Works fine.

    Sound : FM works, ADPCM mostly works (timing(?) issues in a few games).

    SCC : Works enough to get the mouse running, although only with the IPL v1.0 BIOS

    Video : Text mode works, but is rather slow, especially scrolling up (uses multple "raster copy" commands).
            Graphic layers work.
            BG tiles and sprites work, but many games have the sprites offset by a small amount (some by a lot :))
            Still a few minor priority issues around.

    Other issues:
      Bus error exceptions are a bit late at times.  Currently using a fake bus error for MIDI expansion checks.  These
      are used determine if a piece of expansion hardware is present.
      Keyboard doesn't work properly (MFP USART).
      Supervisor area set isn't implemented.

    Some minor game-specific issues:
      Salamander:    System error when using keys in-game.  No error if a joystick is used.
                     Some text is drawn incorrectly.
      Dragon Buster: Text is black and unreadable. (Text layer actually covers it)
      Tetris:        Black dots over screen (text layer).
      Parodius Da!:  Black squares in areas.

    More detailed documentation at http://x68kdev.emuvibes.com/iomap.html - if you can stand broken english :)

*/

#include "emu.h"
#include "x68k.h"
#include "x68k_hdc.h"
#include "x68k_kbd.h"
#include "x68k_mouse.h"

#include "machine/input_merger.h"
#include "machine/nvram.h"

#include "bus/x68k/x68k_neptunex.h"
#include "bus/x68k/x68k_scsiext.h"
#include "bus/x68k/x68k_midi.h"
#include "bus/nscsi/hd.h"
#include "bus/nscsi/cd.h"

#include "softlist.h"
#include "speaker.h"

#include "formats/dim_dsk.h"
#include "formats/xdf_dsk.h"

#include "x68000.lh"

#define LOG_FDC (1U << 1)
#define LOG_SYS (1U << 2)
#define LOG_IRQ (1U << 3)
//#define VERBOSE (LOG_FDC | LOG_SYS | LOG_IRQ)
#include "logmacro.h"


static constexpr uint32_t adpcm_clock[2] = { 8000000, 4000000 };
static constexpr uint32_t adpcm_div[4] = { 1024, 768, 512, /* Reserved */512 };

TIMER_CALLBACK_MEMBER(x68k_state::floppy_tc_tick)
{
	m_upd72065->tc_w(ASSERT_LINE);
	m_upd72065->tc_w(CLEAR_LINE);
}

TIMER_CALLBACK_MEMBER(x68k_state::adpcm_drq_tick)
{
	m_hd63450->drq3_w(1);
	m_hd63450->drq3_w(0);
}

// LED timer callback
TIMER_CALLBACK_MEMBER(x68k_state::led_callback)
{
	m_led_state = !m_led_state ? 1 : 0;
	if(m_led_state)
	{
		for(int drive=0; drive<4; drive++)
			m_ctrl_drv_out[drive] = m_fdc.led_ctrl[drive] ? 0 : 1;
	}
	else
	{
		std::fill(std::begin(m_ctrl_drv_out), std::end(m_ctrl_drv_out), 1);
	}

}

void x68k_state::set_adpcm()
{
	uint32_t rate = adpcm_div[m_adpcm.rate];
	uint32_t res_clock = adpcm_clock[m_adpcm.clock]/2;
	attotime newperiod = attotime::from_ticks(rate, res_clock);
	attotime newremain = newperiod;
	if((m_adpcm_timer->period() != attotime::never) && (m_adpcm_timer->period() != attotime::zero))
		newremain = newperiod * (m_adpcm_timer->remaining().as_double() / m_adpcm_timer->period().as_double());
	m_adpcm_timer->adjust(newremain, 0, newperiod);
}

// PPI ports A and B are joystick inputs
uint8_t x68k_state::ppi_port_a_r()
{
	// first read the joystick inputs
	uint8_t const input = m_joy[0]->read();
	uint8_t result = 0x90 | (BIT(input, 4, 2) << 5) | BIT(input, 0, 4);

	// trigger lines can be pulled down by port C outputs
	result &= ~(BIT(m_ppi_portc, 6, 2) << 5);

	return result;
}

uint8_t x68k_state::ppi_port_b_r()
{
	uint8_t const input = m_joy[1]->read();
	return 0x90 | (BIT(input, 4, 2) << 5) | BIT(input, 0, 4);
}

uint8_t x68k_state::ppi_port_c_r()
{
	return m_ppi_portc;
}

/* PPI port C (Joystick control, R/W)
   bit 7    - IOC7 - Pull down joystick 1 trigger B (JS pin 7)
   bit 6    - IOC6 - Pull down joystick 1 trigger A (JS pin 6)
   bit 5    - IOC5 - Joystick 2 strobe (JT pin 8)
   bit 4    - IOC4 - Joystick 1 strobe (JS pin 8)
   bits 3,2 - ADPCM Sample rate
   bits 1,0 - ADPCM Pan (00 = Both, 01 = Right only, 10 = Left only, 11 = Off)
*/
void x68k_state::ppi_port_c_w(uint8_t data)
{
	// ADPCM / Joystick control
	if((data & 0x03) != (m_ppi_portc & 0x03))
	{
		m_adpcm.pan = data & 0x03;
		m_adpcm_out[0]->set_gain((m_adpcm.pan & 1) ? 0.0f : 1.0f);
		m_adpcm_out[1]->set_gain((m_adpcm.pan & 2) ? 0.0f : 1.0f);
	}
	if((data & 0x0c) != (m_ppi_portc & 0x0c))
	{
		m_adpcm.rate = (data & 0x0c) >> 2;
		if (m_adpcm.rate == 3)
			LOGMASKED(LOG_SYS, "PPI: Invalid ADPCM sample rate set.\n");

		set_adpcm();
		m_okim6258->set_divider(m_adpcm.rate);
	}

	// Set joystick outputs
	if(BIT(data, 6) != BIT(m_ppi_portc, 6))
		m_joy[0]->pin_6_w(BIT(~data, 6));
	if(BIT(data, 7) != BIT(m_ppi_portc, 7))
		m_joy[0]->pin_7_w(BIT(~data, 7));
	if(BIT(data, 4) != BIT(m_ppi_portc, 4))
		m_joy[0]->pin_8_w(BIT(data, 4));
	if(BIT(data, 5) != BIT(m_ppi_portc, 5))
		m_joy[1]->pin_8_w(BIT(data, 5));

	// update saved value
	m_ppi_portc = data;
}


// NEC uPD72065 at 0xe94000
void x68k_state::fdc_w(offs_t offset, uint16_t data)
{
	unsigned int drive, x;
	switch(offset)
	{
	case 0x00:  // drive option signal control
		x = data & 0x0f;
		for(drive=0;drive<4;drive++)
		{
			if(BIT(m_fdc.control_drives, drive) && m_fdc.floppy[drive])
			{
				if(!BIT(x, drive))  // functions take place on 1->0 transitions of drive bits only
				{
					m_fdc.led_ctrl[drive] = data & 0x80;  // blinking drive LED if no disk inserted
					m_fdc.led_eject[drive] = data & 0x40;  // eject button LED (on when set to 0)
					m_eject_drv_out[drive] = BIT(data, 6);
					if((data & 0x60) == 0x20)  // ejects disk
						m_fdc.floppy[drive]->unload();
				}
			}
		}
		m_fdc.control_drives = data & 0x0f;
		LOGMASKED(LOG_FDC, "FDC: signal control set to %02x\n",data);
		break;
	case 0x01: {
		x = data & 3;
		m_upd72065->set_floppy(m_fdc.floppy[x]);
		m_upd72065->set_rate((data & 0x10) ? 300000 : 500000);
		m_fdc.motor = data & 0x80;

		for(int i = 0; i < 4; i++)
			if(m_fdc.floppy[i] && m_fdc.floppy[i]->exists())
				m_fdc.floppy[i]->mon_w(!BIT(data, 7));

		m_access_drv_out[x] = 0;
		if(x != m_fdc.select_drive)
			m_access_drv_out[m_fdc.select_drive] = 1;
		m_fdc.select_drive = x;
		LOGMASKED(LOG_FDC, "FDC: Drive #%i: Drive selection set to %02x\n",x,data);
		break;
		}
	}
}

uint16_t x68k_state::fdc_r(offs_t offset)
{
	unsigned int ret;
	int x;

	switch(offset)
	{
	case 0x00:
		ret = 0x00;
		for(x=0;x<4;x++)
		{
			if(BIT(m_fdc.control_drives, x))
			{
				ret = 0x00;
				if(m_fdc.floppy[x] && m_fdc.floppy[x]->exists())
				{
					ret |= 0x80;
				}
				// bit 7 = disk inserted
				// bit 6 = disk error (in insertion, presumably)
				LOGMASKED(LOG_FDC, "FDC: Drive #%i Disk check - returning %02x\n",x,ret);
			}
		}
		return ret;
	case 0x01:
		LOGMASKED(LOG_FDC, "FDC: IOC selection is write-only\n");
		return 0xff;
	}
	return 0xff;
}

void x68k_state::ct_w(uint8_t data)
{
	// CT1 and CT2 bits from YM2151 port 0x1b
	// CT1 - ADPCM clock - 0 = 8MHz, 1 = 4MHz
	// CT2 - 1 = Set ready state of FDC
	if(data & 1)
	{
		m_upd72065->set_ready_line_connected(0);
		m_upd72065->ready_w(0);
	}
	else
		m_upd72065->set_ready_line_connected(1);

	m_adpcm.clock = (data & 0x02) >> 1;
	set_adpcm();
	m_okim6258->set_unscaled_clock(adpcm_clock[m_adpcm.clock]);
}

enum ioc_irq_number : unsigned
{
	IOC_FDC_INT = 7,
	IOC_FDD_INT = 6,
	IOC_PRT_INT = 5,
	IOC_HDD_INT = 4,
	IOC_HDD_IEN = 3,
	IOC_FDC_IEN = 2,
	IOC_FDD_IEN = 1,
	IOC_PRT_IEN = 0,
};
template <unsigned N> void x68k_state::ioc_irq(int state)
{
	if (state)
		m_ioc.irqstatus |= 1U << N;
	else
		m_ioc.irqstatus &= ~(1U << N);

	bool const irq_state =
		(BIT(m_ioc.irqstatus, IOC_HDD_INT) && BIT(m_ioc.irqstatus, IOC_HDD_IEN)) ||
		(BIT(m_ioc.irqstatus, IOC_PRT_INT) && BIT(m_ioc.irqstatus, IOC_PRT_IEN)) ||
		(BIT(m_ioc.irqstatus, IOC_FDD_INT) && BIT(m_ioc.irqstatus, IOC_FDD_IEN)) ||
		(BIT(m_ioc.irqstatus, IOC_FDC_INT) && BIT(m_ioc.irqstatus, IOC_FDC_IEN));

	m_maincpu->set_input_line(INPUT_LINE_IRQ1, irq_state ? ASSERT_LINE : CLEAR_LINE);
}

/*
 Custom I/O controller at 0xe9c000
 0xe9c001 (R) - Interrupt status
 0xe9c001 (W) - Interrupt mask (low nibble only)
                - bit 7 = FDC interrupt
                - bit 6 = FDD interrupt
                - bit 5 = Printer Busy signal
                - bit 4 = HDD interrupt
                - bit 3 = HDD interrupts enabled
                - bit 2 = FDC interrupts enabled
                - bit 1 = FDD interrupts enabled
                - bit 0 = Printer interrupts enabled
 0xe9c003 (W) - Interrupt vector
                - bits 7-2 = vector
                - bits 1,0 = device (00 = FDC, 01 = FDD, 10 = HDD, 11 = Printer)
*/
void x68k_state::ioc_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
	case 0x00:
		m_ioc.irqstatus = (m_ioc.irqstatus & 0xf0) | (data & 0x0f);
		LOGMASKED(LOG_SYS, "I/O: Status register write %02x\n",data);
		break;
	case 0x01:
		LOGMASKED(LOG_IRQ, "IOC: IRQ vector = 0x%02x\n", data & 0xfc);
		m_ioc.vector = data & 0xfc;
		break;
	}
}

uint8_t x68k_state::ioc_r(offs_t offset)
{
	switch(offset)
	{
	case 0x00:
		LOGMASKED(LOG_SYS, "I/O: Status register read\n");
		return (m_ioc.irqstatus & 0xdf) | 0x20;
	default:
		return 0x00;
	}
}

/*
 System ports at 0xe8e000
 Port 1 (0xe8e001) - Monitor contrast (bits 3-0)
 Port 2 (0xe8e003) - Display / 3D scope control
                     - bit 3 - Display control signal (0 = on)
                     - bit 1 - 3D scope left shutter (0 = closed)
                     - bit 0 - 3D scope right shutter
 Port 3 (0xe8e005) - Colour image unit control (bits 3-0)
 Port 4 (0xe8e007) - Keyboard/NMI/Dot clock
                     - bit 3 - (R) 1 = Keyboard connected, (W) 1 = Key data can be transmitted
                     - bit 1 - NMI Reset
                     - bit 0 - HRL - high resolution dot clock - 1 = 1/2, 1/4, 1/8, 0 = 1/2, 1/3, 1/6 (normal)
 Port 5 (0xe8e009) - ROM (bits 7-4)/DRAM (bits 3-0) wait, X68030 only
 Port 6 (0xe8e00b) - CPU type and clock speed (XVI or later only, X68000 returns 0xFF)
                     - bits 7-4 - CPU Type (1100 = 68040, 1101 = 68030, 1110 = 68020, 1111 = 68000)
                     - bits 3-0 - clock speed (1001 = 50MHz, 40, 33, 25, 20, 16, 1111 = 10MHz)
 Port 7 (0xe8e00d) - SRAM write enable - if 0x31 is written to this port, writing to SRAM is allowed.
                                         Any other value, then SRAM is read only.
 Port 8 (0xe8e00f) - Power off control - write 0x00, 0x0f, 0x0f sequentially to switch power off.
*/
void x68k_state::sysport_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	switch(offset)
	{
	case 0x00:
		m_sysport.contrast = data & 0x0f;  // often used for screen fades / blanking
		m_screen->set_brightness(m_sysport.contrast * 0x11);
		break;
	case 0x01:
		m_sysport.monitor = data & 0x08;
		break;
	case 0x03:
		m_sysport.keyctrl = data & 0x08;  // bit 3 = enable keyboard data transmission
		break;
	case 0x06:
		COMBINE_DATA(&m_sysport.sram_writeprotect);
		break;
	default:
//      LOGMASKED(LOG_SYS, "SYS: [%08x] Wrote %04x to invalid or unimplemented system port %04x\n",m_maincpu->pc(),data,offset);
		break;
	}
}

uint16_t x68k_state::sysport_r(offs_t offset)
{
	int ret = 0;
	switch(offset)
	{
	case 0x00:  // monitor contrast setting (bits3-0)
		return m_sysport.contrast;
	case 0x01:  // monitor control (bit3) / 3D Scope (bits1,0)
		ret |= m_sysport.monitor;
		return ret;
	case 0x03:  // bit 3 = key control (is 1 if keyboard is connected)
		return 0x08;
	case 0x05:  // CPU type and speed
		return m_sysport.cputype;
	default:
		LOGMASKED(LOG_SYS, "Read from invalid or unimplemented system port %04x\n",offset);
		return 0xff;
	}
}

void x68k_state::ppi_w(offs_t offset, uint16_t data)
{
	m_ppi->write(offset & 0x03,data);
}

uint16_t x68k_state::ppi_r(offs_t offset)
{
	return m_ppi->read(offset & 0x03);
}


void x68k_state::sram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if(m_sysport.sram_writeprotect == 0x31)
	{
		COMBINE_DATA(&m_nvram[offset]);
	}
}

uint16_t x68k_state::sram_r(offs_t offset)
{
	// HACKS!
//  if(offset == 0x5a/2)  // 0x5a should be 0 if no SASI HDs are present.
//      return 0x0000;
	if(offset == 0x08/2)
		return m_ram->size() >> 16;  // RAM size
	return m_nvram[offset];
}

void x68k_state::vid_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	if(offset < 0x80)
		COMBINE_DATA(m_video.reg);
	else if(offset < 0x100)
	{
		COMBINE_DATA(m_video.reg+1);
		if(ACCESSING_BITS_0_7)
		{
			m_video.gfxlayer_pri[0] = data & 0x0003;
			m_video.gfxlayer_pri[1] = (data & 0x000c) >> 2;
			m_video.gfxlayer_pri[2] = (data & 0x0030) >> 4;
			m_video.gfxlayer_pri[3] = (data & 0x00c0) >> 6;
		}
		if(ACCESSING_BITS_8_15)
		{
			m_video.gfx_pri = (data & 0x0300) >> 8;
			m_video.text_pri = (data & 0x0c00) >> 10;
			m_video.sprite_pri = (data & 0x3000) >> 12;
			if(m_video.gfx_pri == 3)
				m_video.gfx_pri--;
			if(m_video.text_pri == 3)
				m_video.text_pri--;
			if(m_video.sprite_pri == 3)
				m_video.sprite_pri--;
		}
	}
	else if(offset < 0x180)
		COMBINE_DATA(m_video.reg+2);
	else
		LOGMASKED(LOG_SYS, "VC: Invalid video controller write (offset = 0x%04x, data = %04x)\n",offset,data);
}

uint16_t x68k_state::vid_r(offs_t offset)
{
	switch(offset)
	{
	case 0x000:
		return m_video.reg[0];
	case 0x080:
		return m_video.reg[1];
	case 0x100:
		return m_video.reg[2];
	default:
		LOGMASKED(LOG_SYS, "VC: Invalid video controller read (offset = 0x%04x)\n",offset);
	}

	return 0xff;
}

uint16_t x68k_state::areaset_r()
{
	// register is write-only
	return 0xffff;
}

void x68k_state::areaset_w(uint16_t data)
{
	// TODO
	LOGMASKED(LOG_SYS, "SYS: Supervisor area set: 0x%02x\n",data & 0xff);
}

void x68k_state::enh_areaset_w(offs_t offset, uint16_t data)
{
	// TODO
	LOGMASKED(LOG_SYS, "SYS: Enhanced Supervisor area set (from %iMB): 0x%02x\n",(offset + 1) * 2,data & 0xff);
}

TIMER_CALLBACK_MEMBER(x68k_state::bus_error)
{
	m_bus_error = false;
}

void x68k_state::set_bus_error(uint32_t address, bool rw, uint16_t mem_mask)
{
	LOGMASKED(LOG_SYS, "%s: Bus error: Unused RAM access [%08x]\n", machine().describe_context(), address);
	if(!m_maincpu->executing())
	{
		m_hd63450->bec_w(0, hd63450_device::ERR_BUS);
		return;
	}
	if(m_maincpu->type() == M68000) {
		downcast<m68000_device *>(m_maincpu.target())->trigger_bus_error();
		return;
	}

	if(m_bus_error)
		return;
	if(!ACCESSING_BITS_8_15)
		address++;
	m_bus_error = true;

	m68000_musashi_device *cpuptr = downcast<m68000_musashi_device *>(m_maincpu.target());
	cpuptr->set_buserror_details(address, rw, cpuptr->get_fc());
	cpuptr->set_input_line(M68K_LINE_BUSERROR, ASSERT_LINE);
	cpuptr->set_input_line(M68K_LINE_BUSERROR, CLEAR_LINE);
	m_bus_error_timer->adjust(cpuptr->cycles_to_attotime(16)); // let rmw cycles complete
}

uint16_t x68k_state::rom0_r(offs_t offset, uint16_t mem_mask)
{
	/* this location contains the address of some expansion device ROM, if no ROM exists,
	   then access causes a bus error */
	if((m_options->read() & 0x02) && !machine().side_effects_disabled())
		set_bus_error((offset << 1) + 0xbffffc, true, mem_mask);
	return 0xff;
}

void x68k_state::rom0_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	/* this location contains the address of some expansion device ROM, if no ROM exists,
	   then access causes a bus error */
	if((m_options->read() & 0x02) && !machine().side_effects_disabled())
		set_bus_error((offset << 1) + 0xbffffc, false, mem_mask);
}

uint16_t x68k_state::emptyram_r(offs_t offset, uint16_t mem_mask)
{
	/* this location is unused RAM, access here causes a bus error
	   Often a method for detecting amount of installed RAM, is to read or write at 1MB intervals, until a bus error occurs */
	if((m_options->read() & 0x02) && !machine().side_effects_disabled())
		set_bus_error((offset << 1), 0, mem_mask);
	return 0xff;
}

void x68k_state::emptyram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	/* this location is unused RAM, access here causes a bus error
	   Often a method for detecting amount of installed RAM, is to read or write at 1MB intervals, until a bus error occurs */
	if((m_options->read() & 0x02) && !machine().side_effects_disabled())
		set_bus_error((offset << 1), 1, mem_mask);
}

uint16_t x68k_state::exp_r(offs_t offset, uint16_t mem_mask)
{
	/* These are expansion devices, if not present, they cause a bus error */
	if((m_options->read() & 0x02) && !machine().side_effects_disabled())
		set_bus_error((offset << 1) + 0xeafa00, 0, mem_mask);
	return 0xff;
}

void x68k_state::exp_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	/* These are expansion devices, if not present, they cause a bus error */
	if((m_options->read() & 0x02) && !machine().side_effects_disabled())
		set_bus_error((offset << 1) + 0xeafa00, 1, mem_mask);
}

void x68k_state::dma_end(offs_t offset, uint8_t data)
{
	if(offset == 0)
	{
		m_fdc_tc->adjust(attotime::from_usec(1), 0, attotime::never);
	}
}

void x68k_state::fm_irq(int state)
{
	if(state == CLEAR_LINE)
	{
		m_mfpdev->i3_w(1);
	}
	else
	{
		m_mfpdev->i3_w(0);
	}
}

void x68k_state::adpcm_w(offs_t offset, uint8_t data)
{
	switch(offset)
	{
	case 0x00:
		m_okim6258->ctrl_w(data);
		break;
	case 0x01:
		m_okim6258->data_w(data);
		break;
	}
}

uint8_t x68k_state::iack1()
{
	uint8_t vector = 0x18;
	if (BIT(m_ioc.irqstatus, IOC_FDC_INT) && BIT(m_ioc.irqstatus, IOC_FDC_IEN))
	{
		vector = m_ioc.vector | 0;
		if (!machine().side_effects_disabled())
			ioc_irq<IOC_FDC_INT>(0);
	}
	else if (BIT(m_ioc.irqstatus, IOC_FDD_INT) && BIT(m_ioc.irqstatus, IOC_FDD_IEN))
	{
		vector = m_ioc.vector | 1;
		if (!machine().side_effects_disabled())
			ioc_irq<IOC_FDD_INT>(0);
	}
	else if (BIT(m_ioc.irqstatus, IOC_PRT_INT) && BIT(m_ioc.irqstatus, IOC_PRT_IEN))
	{
		vector = m_ioc.vector | 3;
		if (!machine().side_effects_disabled())
			ioc_irq<IOC_PRT_INT>(0);
	}
	else if (BIT(m_ioc.irqstatus, IOC_HDD_INT) && BIT(m_ioc.irqstatus, IOC_HDD_IEN))
	{
		// TODO: Internal SCSI IRQ vector 0x6c, External SCSI IRQ vector 0xf6 (really?)
		vector = m_ioc.vector | 2;
		if (!machine().side_effects_disabled())
			ioc_irq<IOC_HDD_INT>(0);
	}

	if (!machine().side_effects_disabled())
		LOGMASKED(LOG_IRQ, "IOC: IRQ1 acknowledged with vector = %02X\n", vector);
	return vector;
}

template <int N>
void x68k_state::irq2_line(int state)
{
	m_exp_irq2[N] = (state != CLEAR_LINE);
	LOGMASKED(LOG_IRQ, "IRQ2-%d %s\n", N + 1, m_exp_irq2[N] ? "asserted" : "cleared");
	m_maincpu->set_input_line(INPUT_LINE_IRQ2, (m_exp_irq2[0] || m_exp_irq2[1]) ? ASSERT_LINE : CLEAR_LINE);
}

template <int N>
void x68k_state::irq4_line(int state)
{
	m_exp_irq4[N] = (state != CLEAR_LINE);
	LOGMASKED(LOG_IRQ, "IRQ4-%d %s\n", N + 1, m_exp_irq4[N] ? "asserted" : "cleared");
	m_maincpu->set_input_line(INPUT_LINE_IRQ4, (m_exp_irq4[0] || m_exp_irq4[1]) ? ASSERT_LINE : CLEAR_LINE);
}

uint8_t x68k_state::iack2()
{
	// IACK2-1 has higher priority than IACK2-2
	if (m_exp_irq2[0])
		return m_expansion[0]->iack2();
	else if (m_exp_irq2[1])
		return m_expansion[1]->iack2();
	else
		return m68000_base_device::autovector(0); // spurious interrupt
}

uint8_t x68k_state::iack4()
{
	// IACK4-1 has higher priority than IACK4-2
	if (m_exp_irq4[0])
		return m_expansion[0]->iack4();
	else if (m_exp_irq4[1])
		return m_expansion[1]->iack4();
	else
		return m68000_base_device::autovector(0); // spurious interrupt
}

void x68k_state::cpu_space_map(address_map &map)
{
	map.global_mask(0xffffff);
	map(0xfffff3, 0xfffff3).r(FUNC(x68k_state::iack1));
	map(0xfffff5, 0xfffff5).r(FUNC(x68k_state::iack2));
	map(0xfffff7, 0xfffff7).r(m_hd63450, FUNC(hd63450_device::iack));
	map(0xfffff9, 0xfffff9).r(FUNC(x68k_state::iack4));
	map(0xfffffb, 0xfffffb).lr8(NAME([this]() { return m_scc->m1_r(); }));
	map(0xfffffd, 0xfffffd).r(m_mfpdev, FUNC(mc68901_device::get_vector));
	map(0xffffff, 0xffffff).lr8(NAME([] () { return m68000_base_device::autovector(7); }));
}

void x68ksupr_state::scsi_unknown_w(uint8_t data)
{
	// Documentation claims SSTS register is read-only, but x68030 boot code writes #$05 to this address anyway.
	// Is this an undocumented MB89352 feature, an ASIC register, an original code bug or a bad dump?
}

void x68k_state::x68k_base_map(address_map &map)
{
	map(0x000000, 0xbffffb).rw(FUNC(x68k_state::emptyram_r), FUNC(x68k_state::emptyram_w));
	map(0xbffffc, 0xbfffff).rw(FUNC(x68k_state::rom0_r), FUNC(x68k_state::rom0_w));
	map(0xc00000, 0xdfffff).rw(m_crtc, FUNC(x68k_crtc_device::gvram_r), FUNC(x68k_crtc_device::gvram_w));
	map(0xe00000, 0xe7ffff).rw(m_crtc, FUNC(x68k_crtc_device::tvram_r), FUNC(x68k_crtc_device::tvram_w));
	map(0xe80000, 0xe81fff).rw(m_crtc, FUNC(x68k_crtc_device::crtc_r), FUNC(x68k_crtc_device::crtc_w));
	map(0xe82400, 0xe83fff).rw(FUNC(x68k_state::vid_r), FUNC(x68k_state::vid_w));
	map(0xe84000, 0xe85fff).rw(m_hd63450, FUNC(hd63450_device::read), FUNC(hd63450_device::write));
	map(0xe86000, 0xe87fff).rw(FUNC(x68k_state::areaset_r), FUNC(x68k_state::areaset_w));
	map(0xe88000, 0xe89fff).rw(m_mfpdev, FUNC(mc68901_device::read), FUNC(mc68901_device::write)).umask16(0x00ff);
	map(0xe8a000, 0xe8bfff).rw(m_rtc, FUNC(rp5c15_device::read), FUNC(rp5c15_device::write)).umask16(0x00ff);
//  map(0xe8c000, 0xe8dfff).rw(FUNC(x68k_state::x68k_printer_r), FUNC(x68k_state::x68k_printer_w));
	map(0xe8e000, 0xe8ffff).rw(FUNC(x68k_state::sysport_r), FUNC(x68k_state::sysport_w));
	map(0xe90000, 0xe91fff).rw(m_ym2151, FUNC(ym2151_device::read), FUNC(ym2151_device::write)).umask16(0x00ff);
	map(0xe94000, 0xe94003).m(m_upd72065, FUNC(upd72065_device::map)).umask16(0x00ff);
	map(0xe94004, 0xe94007).rw(FUNC(x68k_state::fdc_r), FUNC(x68k_state::fdc_w));
	map(0xe98000, 0xe99fff).rw(m_scc, FUNC(scc8530_device::ab_dc_r), FUNC(scc8530_device::ab_dc_w)).umask16(0x00ff);
	map(0xe9a000, 0xe9bfff).rw(FUNC(x68k_state::ppi_r), FUNC(x68k_state::ppi_w));
	map(0xe9c000, 0xe9dfff).rw(FUNC(x68k_state::ioc_r), FUNC(x68k_state::ioc_w)).umask16(0x00ff);
	map(0xe9e000, 0xe9e3ff).rw(FUNC(x68k_state::exp_r), FUNC(x68k_state::exp_w));  // FPU (Optional)
	map(0xeafa00, 0xeafa1f).rw(FUNC(x68k_state::exp_r), FUNC(x68k_state::exp_w));
	map(0xeb0000, 0xeb7fff).rw(FUNC(x68k_state::spritereg_r), FUNC(x68k_state::spritereg_w));
	map(0xeb8000, 0xebffff).rw(FUNC(x68k_state::spriteram_r), FUNC(x68k_state::spriteram_w));
	map(0xece000, 0xece3ff).rw(FUNC(x68k_state::exp_r), FUNC(x68k_state::exp_w));  // User I/O
	map(0xed0000, 0xed3fff).rw(FUNC(x68k_state::sram_r), FUNC(x68k_state::sram_w));
	map(0xed4000, 0xefffff).noprw();
	map(0xf00000, 0xfbffff).rom();
	map(0xfe0000, 0xffffff).rom();
}

void x68k_state::x68k_map(address_map &map)
{
	x68k_base_map(map);
	map(0xe82000, 0xe821ff).rw(m_gfxpalette, FUNC(palette_device::read16), FUNC(palette_device::write16)).share("gfxpalette");
	map(0xe82200, 0xe823ff).rw(m_pcgpalette, FUNC(palette_device::read16), FUNC(palette_device::write16)).share("pcgpalette");
	map(0xe92001, 0xe92001).rw(m_okim6258, FUNC(okim6258_device::status_r), FUNC(okim6258_device::ctrl_w));
	map(0xe92003, 0xe92003).rw(m_okim6258, FUNC(okim6258_device::status_r), FUNC(okim6258_device::data_w));
	map(0xe96000, 0xe9601f).rw("x68k_hdc", FUNC(x68k_hdc_image_device::hdc_r), FUNC(x68k_hdc_image_device::hdc_w));
	map(0xea0000, 0xea1fff).rw(FUNC(x68k_state::exp_r), FUNC(x68k_state::exp_w));  // external SCSI ROM and controller
	map(0xeafa80, 0xeafa89).rw(FUNC(x68k_state::areaset_r), FUNC(x68k_state::enh_areaset_w));
	map(0xfc0000, 0xfdffff).rw(FUNC(x68k_state::exp_r), FUNC(x68k_state::exp_w));  // internal SCSI ROM
}

void x68ksupr_state::x68kxvi_map(address_map &map)
{
	x68k_base_map(map);
	map(0xe82000, 0xe821ff).rw(m_gfxpalette, FUNC(palette_device::read16), FUNC(palette_device::write16)).share("gfxpalette");
	map(0xe82200, 0xe823ff).rw(m_pcgpalette, FUNC(palette_device::read16), FUNC(palette_device::write16)).share("pcgpalette");
	map(0xe92001, 0xe92001).rw(m_okim6258, FUNC(okim6258_device::status_r), FUNC(okim6258_device::ctrl_w));
	map(0xe92003, 0xe92003).rw(m_okim6258, FUNC(okim6258_device::status_r), FUNC(okim6258_device::data_w));
	map(0xe96020, 0xe9603f).m(m_scsictrl, FUNC(mb89352_device::map)).umask16(0x00ff);
	map(0xea0000, 0xea1fff).rw(FUNC(x68ksupr_state::exp_r), FUNC(x68ksupr_state::exp_w));  // external SCSI ROM and controller
	map(0xeafa80, 0xeafa89).rw(FUNC(x68ksupr_state::areaset_r), FUNC(x68ksupr_state::enh_areaset_w));
	map(0xfc0000, 0xfdffff).rom();  // internal SCSI ROM
}

void x68030_state::x68030_map(address_map &map)
{
	map.global_mask(0x00ffffff);  // Still only has 24-bit address space
	x68k_base_map(map);
	map(0xe82000, 0xe821ff).rw(m_gfxpalette, FUNC(palette_device::read32), FUNC(palette_device::write32)).share("gfxpalette");
	map(0xe82200, 0xe823ff).rw(m_pcgpalette, FUNC(palette_device::read32), FUNC(palette_device::write32)).share("pcgpalette");
//  map(0xe8c000, 0xe8dfff).rw(FUNC(x68k_state::x68k_printer_r), FUNC(x68k_state::x68k_printer_w));
	map(0xe92000, 0xe92003).r(m_okim6258, FUNC(okim6258_device::status_r)).umask32(0x00ff00ff).w(FUNC(x68030_state::adpcm_w)).umask32(0x00ff00ff);

	map(0xe96020, 0xe9603f).m(m_scsictrl, FUNC(mb89352_device::map)).umask32(0x00ff00ff);
	map(0xe9602d, 0xe9602d).w(FUNC(x68030_state::scsi_unknown_w));
	map(0xea0000, 0xea1fff).noprw();//.rw(FUNC(x68030_state::exp_r), FUNC(x68030_state::exp_w));  // external SCSI ROM and controller
	map(0xeafa80, 0xeafa8b).rw(FUNC(x68030_state::areaset_r), FUNC(x68030_state::enh_areaset_w));
	map(0xfc0000, 0xfdffff).rom();  // internal SCSI ROM
}

static INPUT_PORTS_START( x68000 )
// TODO: Sharp Cyber Stick (CZ-8NJ2) support

	PORT_START("options")
	PORT_CONFNAME( 0x02, 0x02, "Enable fake bus errors")
	PORT_CONFSETTING(   0x00, DEF_STR( Off ))
	PORT_CONFSETTING(   0x02, DEF_STR( On ))
INPUT_PORTS_END

void x68k_state::floppy_load_unload(bool load, floppy_image_device *dev)
{
	dev->mon_w(!(m_fdc.motor && load));

	ioc_irq<IOC_FDD_INT>(1);
}

void x68k_state::floppy_load(floppy_image_device *dev)
{
	floppy_load_unload(true, dev);
}

void x68k_state::floppy_unload(floppy_image_device *dev)
{
	floppy_load_unload(false, dev);
}

static void x68000_exp_cards(device_slot_interface &device)
{
	device.option_add("neptunex", X68K_NEPTUNEX);   // Neptune-X ethernet adapter (ISA NE2000 bridge)
	device.option_add("cz6bs1", X68K_SCSIEXT);      // Sharp CZ-6BS1 SCSI-1 controller
	device.option_add("x68k_midi", X68K_MIDI);      // X68000 MIDI interface
}

void x68k_state::machine_reset()
{
	/* The last half of the IPLROM is mapped to 0x000000 on reset only
	   Just copying the initial stack pointer and program counter should
	   more or less do the same job */

	uint8_t *const romdata = memregion("user2")->base();

	memset(m_ram->pointer(),0,m_ram->size());
	memcpy(m_ram->pointer(),romdata,8);

	/// TODO: get callbacks to trigger these
	m_mfpdev->i0_w(1); // alarm
	m_mfpdev->i1_w(1); // expon
	m_mfpdev->i2_w(0); // pow sw
	m_mfpdev->i3_w(1); // fmirq
	//m_mfpdev->i4_w(1); // v-disp
	m_mfpdev->i5_w(1); // unused (always set)
	//m_mfpdev->i6_w(1); // cirq
	//m_mfpdev->i7_w(1); // h-sync

	// reset output values
	std::fill(std::begin(m_eject_drv_out), std::end(m_eject_drv_out), 1);
	std::fill(std::begin(m_ctrl_drv_out), std::end(m_ctrl_drv_out), 1);
	std::fill(std::begin(m_access_drv_out), std::end(m_access_drv_out), 1);
	m_fdc.select_drive = 0;
}

void x68k_state::machine_start()
{
	// resolve outputs
	m_eject_drv_out.resolve();
	m_ctrl_drv_out.resolve();
	m_access_drv_out.resolve();

	address_space &space = m_maincpu->space(AS_PROGRAM);
	// install RAM handlers
	m_spriteram = (uint16_t*)(memregion("user1")->base());
	space.install_ram(0x000000,m_ram->size()-1,m_ram->pointer());

	// start LED timer
	m_led_timer->adjust(attotime::zero, 0, attotime::from_msec(400));

	for(int drive=0;drive<4;drive++)
	{
		char devname[16];
		sprintf(devname, "%d", drive);
		floppy_image_device *floppy = m_upd72065->subdevice<floppy_connector>(devname)->get_device();
		m_fdc.floppy[drive] = floppy;
		if(floppy) {
			floppy->setup_load_cb(floppy_image_device::load_cb(&x68k_state::floppy_load, this));
			floppy->setup_unload_cb(floppy_image_device::unload_cb(&x68k_state::floppy_unload, this));
		}
	}
	m_fdc.motor = 0;

	m_exp_irq2[0] = m_exp_irq2[1] = false;
	m_exp_irq4[0] = m_exp_irq4[1] = false;
	m_ioc.irqstatus = 0;
	m_adpcm.rate = 0;
	m_adpcm.clock = 0;
	m_sysport.sram_writeprotect = 0;
	m_sysport.monitor = 0;
	m_bus_error = false;
	m_led_state = 0;
}

void x68k_state::driver_start()
{
	unsigned char* rom = memregion("maincpu")->base();
	unsigned char* user2 = memregion("user2")->base();

	subdevice<nvram_device>("nvram")->set_base(&m_nvram[0], m_nvram.size()*sizeof(m_nvram[0]));

#ifdef USE_PREDEFINED_SRAM
	{
		unsigned char* ramptr = memregion("user3")->base();
		memcpy(m_sram,ramptr,0x4000);
	}
#endif

	// copy last half of BIOS to a user region, to use for initial startup
	memcpy(user2,(rom+0xff0000),0x10000);

	m_led_timer = timer_alloc(FUNC(x68ksupr_state::led_callback), this);
	m_fdc_tc = timer_alloc(FUNC(x68ksupr_state::floppy_tc_tick), this);
	m_adpcm_timer = timer_alloc(FUNC(x68ksupr_state::adpcm_drq_tick), this);
	m_bus_error_timer = timer_alloc(FUNC(x68ksupr_state::bus_error), this);

	m_sysport.cputype = 0xff;  // 68000, 10MHz
	m_is_32bit = false;

	save_item(NAME(m_tvram));
	save_item(NAME(m_gvram));
	save_item(NAME(m_spritereg));
}

void x68ksupr_state::driver_start()
{
	x68k_state::driver_start();
	m_sysport.cputype = 0xfe; // 68000, 16MHz
	m_is_32bit = false;
}

void x68030_state::driver_start()
{
	x68k_state::driver_start();
	m_sysport.cputype = 0xdc; // 68030, 25MHz
	m_is_32bit = true;
}

void x68k_state::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_XDF_FORMAT);
	fr.add(FLOPPY_DIM_FORMAT);
}

static void x68k_floppies(device_slot_interface &device)
{
	device.option_add("525hd", FLOPPY_525_HD);
}

static void keyboard_devices(device_slot_interface &device)
{
	device.option_add("x68k", X68K_KEYBOARD);
}

static void mouse_devices(device_slot_interface &device)
{
	device.option_add("x68k", X68K_MOUSE);
}

void x68k_state::x68000_base(machine_config &config)
{
	config.set_maximum_quantum(attotime::from_hz(60));

	/* device hardware */
	MC68901(config, m_mfpdev, 16_MHz_XTAL / 4);
	m_mfpdev->set_timer_clock(16_MHz_XTAL / 4);
	m_mfpdev->out_irq_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ6);
	m_mfpdev->out_tbo_cb().set(m_mfpdev, FUNC(mc68901_device::tc_w));
	m_mfpdev->out_tbo_cb().append(m_mfpdev, FUNC(mc68901_device::rc_w));
	m_mfpdev->out_so_cb().set("keyboard", FUNC(rs232_port_device::write_txd));

	rs232_port_device &keyboard(RS232_PORT(config, "keyboard", keyboard_devices, "x68k"));
	keyboard.rxd_handler().set(m_mfpdev, FUNC(mc68901_device::si_w));

	MSX_GENERAL_PURPOSE_PORT(config, m_joy[0], msx_general_purpose_port_devices, "townspad");
	MSX_GENERAL_PURPOSE_PORT(config, m_joy[1], msx_general_purpose_port_devices, "townspad");

	I8255A(config, m_ppi, 0);
	m_ppi->in_pa_callback().set(FUNC(x68k_state::ppi_port_a_r));
	m_ppi->in_pb_callback().set(FUNC(x68k_state::ppi_port_b_r));
	m_ppi->in_pc_callback().set(FUNC(x68k_state::ppi_port_c_r));
	m_ppi->out_pc_callback().set(FUNC(x68k_state::ppi_port_c_w));

	HD63450(config, m_hd63450, 40_MHz_XTAL / 4, "maincpu");
	m_hd63450->set_clocks(attotime::from_usec(2), attotime::from_nsec(450), attotime::from_usec(4), attotime::from_hz(15625/2));
	m_hd63450->set_burst_clocks(attotime::from_usec(2), attotime::from_nsec(450), attotime::from_nsec(450), attotime::from_nsec(50));
	m_hd63450->irq_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ3);
	m_hd63450->dma_end().set(FUNC(x68k_state::dma_end));
	m_hd63450->dma_read<0>().set("upd72065", FUNC(upd72065_device::dma_r));
	m_hd63450->dma_write<0>().set("upd72065", FUNC(upd72065_device::dma_w));

	SCC8530(config, m_scc, 40_MHz_XTAL / 8);
	m_scc->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ5);

	rs232_port_device &mouse(RS232_PORT(config, "mouse_port", mouse_devices, "x68k"));
	mouse.rxd_handler().set(m_scc, FUNC(scc8530_device::rxb_w));
	m_scc->out_rtsb_callback().set(mouse, FUNC(rs232_port_device::write_rts));

	RP5C15(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->alarm().set(m_mfpdev, FUNC(mc68901_device::i0_w));
	m_rtc->set_year_offset(20);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(69.55199_MHz_XTAL / 2, 1096, 0, 768, 568, 0, 512);  // initial setting
	m_screen->set_screen_update(FUNC(x68k_state::screen_update));

	GFXDECODE(config, m_gfxdecode, "pcgpalette", gfxdecode_device::empty);

	PALETTE(config, m_gfxpalette).set_format(2, &x68k_state::GGGGGRRRRRBBBBBI, 256);
	PALETTE(config, m_pcgpalette).set_format(2, &x68k_state::GGGGGRRRRRBBBBBI, 256);

	config.set_default_layout(layout_x68000);

	/* sound hardware */
	SPEAKER(config, "speaker", 2).front();
	YM2151(config, m_ym2151, 16_MHz_XTAL / 4);
	m_ym2151->irq_handler().set(FUNC(x68k_state::fm_irq));
	m_ym2151->port_write_handler().set(FUNC(x68k_state::ct_w));  // CT1, CT2 from YM2151 port 0x1b
	m_ym2151->add_route(0, "speaker", 0.50, 0);
	m_ym2151->add_route(1, "speaker", 0.50, 1);

	OKIM6258(config, m_okim6258, 16_MHz_XTAL / 4);
	m_okim6258->set_start_div(okim6258_device::FOSC_DIV_BY_512);
	m_okim6258->set_type(okim6258_device::TYPE_4BITS);
	m_okim6258->set_outbits(okim6258_device::OUTPUT_10BITS);
	m_okim6258->add_route(ALL_OUTPUTS, "adpcm_outl", 0.50);
	m_okim6258->add_route(ALL_OUTPUTS, "adpcm_outr", 0.50);

	FILTER_VOLUME(config, m_adpcm_out[0]).add_route(ALL_OUTPUTS, "speaker", 1.0, 0);
	FILTER_VOLUME(config, m_adpcm_out[1]).add_route(ALL_OUTPUTS, "speaker", 1.0, 1);

	UPD72065(config, m_upd72065, 16_MHz_XTAL / 2, true, false); // clocked through SED9420CAC
	m_upd72065->intrq_wr_callback().set(FUNC(x68k_state::ioc_irq<IOC_FDC_INT>));
	m_upd72065->drq_wr_callback().set(m_hd63450, FUNC(hd63450_device::drq0_w));
	FLOPPY_CONNECTOR(config, "upd72065:0", x68k_floppies, "525hd", x68k_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "upd72065:1", x68k_floppies, "525hd", x68k_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "upd72065:2", x68k_floppies, "525hd", x68k_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "upd72065:3", x68k_floppies, "525hd", x68k_state::floppy_formats);

	SOFTWARE_LIST(config, "flop_list").set_original("x68k_flop");

	input_merger_any_high_device &nmi(INPUT_MERGER_ANY_HIGH(config, "nmi"));
	nmi.output_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ7);

	X68K_EXPANSION_SLOT(config, m_expansion[0], x68000_exp_cards, nullptr);
	m_expansion[0]->set_space(m_maincpu, AS_PROGRAM);
	m_expansion[0]->out_irq2_callback().set(FUNC(x68k_state::irq2_line<0>));
	m_expansion[0]->out_irq4_callback().set(FUNC(x68k_state::irq4_line<0>));
	m_expansion[0]->out_nmi_callback().set(nmi, FUNC(input_merger_any_high_device::in_w<0>));
	m_expansion[0]->out_dtack_callback().set(m_hd63450, FUNC(hd63450_device::dtack_w));
	m_hd63450->own().append(m_expansion[0], FUNC(x68k_expansion_slot_device::exown_w));

	X68K_EXPANSION_SLOT(config, m_expansion[1], x68000_exp_cards, nullptr);
	m_expansion[1]->set_space(m_maincpu, AS_PROGRAM);
	m_expansion[1]->out_irq2_callback().set(FUNC(x68k_state::irq2_line<1>));
	m_expansion[1]->out_irq4_callback().set(FUNC(x68k_state::irq4_line<1>));
	m_expansion[1]->out_nmi_callback().set(nmi, FUNC(input_merger_any_high_device::in_w<1>));
	m_expansion[1]->out_dtack_callback().set(m_hd63450, FUNC(hd63450_device::dtack_w));
	m_hd63450->own().append(m_expansion[1], FUNC(x68k_expansion_slot_device::exown_w));

	/* internal ram */
	RAM(config, m_ram).set_default_size("4M").set_extra_options("1M,2M,3M,5M,6M,7M,8M,9M,10M,11M,12M");

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);
}

void x68k_state::x68000(machine_config &config)
{
	add_cpu(config, M68000, &x68k_state::x68k_map, 40_MHz_XTAL / 4);
	x68000_base(config);

	VINAS(config, m_crtc, 38.86363_MHz_XTAL);
	m_crtc->set_clock_69m(69.55199_MHz_XTAL);
	m_crtc->set_screen("screen");
	m_crtc->vdisp_cb().set(m_mfpdev, FUNC(mc68901_device::i4_w));
	m_crtc->vdisp_cb().append(m_mfpdev, FUNC(mc68901_device::tai_w));
	m_crtc->rint_cb().set(m_mfpdev, FUNC(mc68901_device::i6_w));
	m_crtc->hsync_cb().set(m_mfpdev, FUNC(mc68901_device::i7_w));
	m_crtc->tvram_read_cb().set(FUNC(x68k_state::tvram_read));
	m_crtc->tvram_write_cb().set(FUNC(x68k_state::tvram_write));
	m_crtc->gvram_read_cb().set(FUNC(x68k_state::gvram_read));
	m_crtc->gvram_write_cb().set(FUNC(x68k_state::gvram_write));

	X68KHDC(config, "x68k_hdc", 0);
}

static void scsi_devices(device_slot_interface &device)
{
	device.option_add("harddisk", NSCSI_HARDDISK);
	device.option_add("cdrom", NSCSI_CDROM);
}

void x68ksupr_state::x68ksupr_base(machine_config &config)
{
	x68000_base(config);

	NSCSI_BUS(config, "scsi");
	NSCSI_CONNECTOR(config, "scsi:0", scsi_devices, "harddisk");
	NSCSI_CONNECTOR(config, "scsi:1", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:2", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:3", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:4", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:5", scsi_devices, nullptr);
	NSCSI_CONNECTOR(config, "scsi:6", scsi_devices, "cdrom");
	NSCSI_CONNECTOR(config, "scsi:7").option_set("spc", MB89352).machine_config(
		[this](device_t *device)
		{
			mb89352_device &spc = downcast<mb89352_device &>(*device);

			spc.set_clock(40_MHz_XTAL / 8);
			spc.out_irq_callback().set(*this, FUNC(x68ksupr_state::ioc_irq<IOC_HDD_INT>));
			// TODO: duplicate DMA glue from CZ-6BS1
		});

	VICON(config, m_crtc, 38.86363_MHz_XTAL);
	m_crtc->set_clock_69m(69.55199_MHz_XTAL);
	m_crtc->set_screen("screen");
	m_crtc->vdisp_cb().set(m_mfpdev, FUNC(mc68901_device::i4_w));
	m_crtc->vdisp_cb().append(m_mfpdev, FUNC(mc68901_device::tai_w));
	m_crtc->rint_cb().set(m_mfpdev, FUNC(mc68901_device::i6_w));
	m_crtc->hsync_cb().set(m_mfpdev, FUNC(mc68901_device::i7_w));
	m_crtc->tvram_read_cb().set(FUNC(x68ksupr_state::tvram_read));
	m_crtc->tvram_write_cb().set(FUNC(x68ksupr_state::tvram_write));
	m_crtc->gvram_read_cb().set(FUNC(x68ksupr_state::gvram_read));
	m_crtc->gvram_write_cb().set(FUNC(x68ksupr_state::gvram_write));
}

void x68ksupr_state::x68ksupr(machine_config &config)
{
	add_cpu(config, M68000, &x68ksupr_state::x68kxvi_map, 40_MHz_XTAL / 4);
	x68ksupr_base(config);
}

void x68ksupr_state::x68kxvi(machine_config &config)
{
	add_cpu(config, M68000, &x68ksupr_state::x68kxvi_map, 33.33_MHz_XTAL / 2); /* 16 MHz (nominally) */
	x68ksupr_base(config);
}

void x68030_state::x68030(machine_config &config)
{
	add_cpu(config, M68030, &x68030_state::x68030_map, 50_MHz_XTAL / 2);  /* 25 MHz 68EC030 */
	x68ksupr_base(config);

	m_hd63450->set_clock(50_MHz_XTAL / 4);
	m_scc->set_clock(20_MHz_XTAL / 4);
	//m_scsictrl->set_clock(20_MHz_XTAL / 4);

	m_crtc->set_clock_50m(50.35_MHz_XTAL);
}

ROM_START( x68000 )
	ROM_REGION16_BE(0x1000000, "maincpu", 0)  // 16MB address space
	ROM_DEFAULT_BIOS("cz600ce")
	ROM_LOAD( "cgrom.dat",  0xf00000, 0xc0000, CRC(9f3195f1) SHA1(8d72c5b4d63bb14c5dbdac495244d659aa1498b6) )
	ROM_SYSTEM_BIOS(0, "ipl10",  "IPL-ROM V1.0 (87/05/07)")
	ROMX_LOAD( "iplrom.dat", 0xfe0000, 0x20000, CRC(72bdf532) SHA1(0ed038ed2133b9f78c6e37256807424e0d927560), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "ipl11",  "IPL-ROM V1.1 (91/01/11)")
	ROMX_LOAD( "iplromxv.dat", 0xfe0000, 0x020000, CRC(00eeb408) SHA1(e33cdcdb69cd257b0b211ef46e7a8b144637db57), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS(2, "ipl12",  "IPL-ROM V1.2 (91/10/24)")
	ROMX_LOAD( "iplromco.dat", 0xfe0000, 0x020000, CRC(6c7ef608) SHA1(77511fc58798404701f66b6bbc9cbde06596eba7), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS(3, "ipl13",  "IPL-ROM V1.3 (92/11/27)")
	ROMX_LOAD( "iplrom30.dat", 0xfe0000, 0x020000, CRC(e8f8fdad) SHA1(239e9124568c862c31d9ec0605e32373ea74b86a), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS(4, "cz600ce",  "CZ-600CE IPL-ROM V1.0 (87/03/18)")
	ROMX_LOAD( "rh-ix0897cezz.ic12", 0xfe0000, 0x010000, CRC(cdc95995) SHA1(810cae207ffd29926e604cf1eb964ae8ea1fadb5), ROM_BIOS(4) | ROM_SKIP(1) )
	ROMX_LOAD( "rh-ix0898cezz.ic11", 0xfe0001, 0x010000, CRC(e60e09a8) SHA1(f3d4a6506493ea3ac7b9c8e441d781fbdd61abd5), ROM_BIOS(4) | ROM_SKIP(1) )
	ROM_REGION(0x8000, "user1",0)  // For Background/Sprite decoding
	ROM_FILL(0x0000,0x8000,0x00)
	ROM_REGION(0x20000, "user2", 0)
	ROM_FILL(0x000,0x20000,0x00)
ROM_END

ROM_START( x68ksupr )
	ROM_REGION16_BE(0x1000000, "maincpu", 0)  // 16MB address space
	ROM_DEFAULT_BIOS("ipl11")
	ROM_LOAD( "cgrom.dat",  0xf00000, 0xc0000, CRC(9f3195f1) SHA1(8d72c5b4d63bb14c5dbdac495244d659aa1498b6) )
	ROM_SYSTEM_BIOS(0, "ipl10",  "IPL-ROM V1.0 (87/05/07)")
	ROMX_LOAD( "iplrom.dat", 0xfe0000, 0x20000, CRC(72bdf532) SHA1(0ed038ed2133b9f78c6e37256807424e0d927560), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "ipl11",  "IPL-ROM V1.1 (91/01/11)")
	ROMX_LOAD( "iplromxv.dat", 0xfe0000, 0x020000, CRC(00eeb408) SHA1(e33cdcdb69cd257b0b211ef46e7a8b144637db57), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS(2, "ipl12",  "IPL-ROM V1.2 (91/10/24)")
	ROMX_LOAD( "iplromco.dat", 0xfe0000, 0x020000, CRC(6c7ef608) SHA1(77511fc58798404701f66b6bbc9cbde06596eba7), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS(3, "ipl13",  "IPL-ROM V1.3 (92/11/27)")
	ROMX_LOAD( "iplrom30.dat", 0xfe0000, 0x020000, CRC(e8f8fdad) SHA1(239e9124568c862c31d9ec0605e32373ea74b86a), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS(4, "cz600ce",  "CZ-600CE IPL-ROM V1.0 (87/03/18)")
	ROMX_LOAD( "rh-ix0897cezz.ic12", 0xfe0000, 0x010000, CRC(cdc95995) SHA1(810cae207ffd29926e604cf1eb964ae8ea1fadb5), ROM_BIOS(4) | ROM_SKIP(1) )
	ROMX_LOAD( "rh-ix0898cezz.ic11", 0xfe0001, 0x010000, CRC(e60e09a8) SHA1(f3d4a6506493ea3ac7b9c8e441d781fbdd61abd5), ROM_BIOS(4) | ROM_SKIP(1) )
	ROM_LOAD("scsiinsu.bin",0xfc0000, 0x002000, CRC(f65a3e24) SHA1(15a17798839a3f7f361119205aebc301c2df5967) )  // Dumped from an X68000 Super HD
//  ROM_LOAD("scsiexrom.dat",0xea0000, 0x002000, NO_DUMP )
	ROM_REGION(0x8000, "user1",0)  // For Background/Sprite decoding
	ROM_FILL(0x0000,0x8000,0x00)
	ROM_REGION(0x20000, "user2", 0)
	ROM_FILL(0x000,0x20000,0x00)
ROM_END

ROM_START( x68kxvi )
	ROM_REGION16_BE(0x1000000, "maincpu", 0)  // 16MB address space
	ROM_DEFAULT_BIOS("ipl11")
	ROM_LOAD( "cgrom.dat",  0xf00000, 0xc0000, CRC(9f3195f1) SHA1(8d72c5b4d63bb14c5dbdac495244d659aa1498b6) )
	ROM_SYSTEM_BIOS(0, "ipl10",  "IPL-ROM V1.0 (87/05/07)")
	ROMX_LOAD( "iplrom.dat", 0xfe0000, 0x20000, CRC(72bdf532) SHA1(0ed038ed2133b9f78c6e37256807424e0d927560), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "ipl11",  "IPL-ROM V1.1 (91/01/11)")
	ROMX_LOAD( "iplromxv.dat", 0xfe0000, 0x020000, CRC(00eeb408) SHA1(e33cdcdb69cd257b0b211ef46e7a8b144637db57), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS(2, "ipl12",  "IPL-ROM V1.2 (91/10/24)")
	ROMX_LOAD( "iplromco.dat", 0xfe0000, 0x020000, CRC(6c7ef608) SHA1(77511fc58798404701f66b6bbc9cbde06596eba7), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS(3, "ipl13",  "IPL-ROM V1.3 (92/11/27)")
	ROMX_LOAD( "iplrom30.dat", 0xfe0000, 0x020000, CRC(e8f8fdad) SHA1(239e9124568c862c31d9ec0605e32373ea74b86a), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS(4, "cz600ce",  "CZ-600CE IPL-ROM V1.0 (87/03/18)")
	ROMX_LOAD( "rh-ix0897cezz.ic12", 0xfe0000, 0x010000, CRC(cdc95995) SHA1(810cae207ffd29926e604cf1eb964ae8ea1fadb5), ROM_BIOS(4) | ROM_SKIP(1) )
	ROMX_LOAD( "rh-ix0898cezz.ic11", 0xfe0001, 0x010000, CRC(e60e09a8) SHA1(f3d4a6506493ea3ac7b9c8e441d781fbdd61abd5), ROM_BIOS(4) | ROM_SKIP(1) )
	ROM_LOAD("scsiinco.bin",0xfc0000, 0x002000, CRC(2485e14d) SHA1(101a9bba8ea4bb90965c144bcfd7182f889ab958) )  // Dumped from an X68000 XVI Compact
//  ROM_LOAD("scsiexrom.dat",0xea0000, 0x002000, NO_DUMP )
	ROM_REGION(0x8000, "user1",0)  // For Background/Sprite decoding
	ROM_FILL(0x0000,0x8000,0x00)
	ROM_REGION(0x20000, "user2", 0)
	ROM_FILL(0x000,0x20000,0x00)
ROM_END

ROM_START( x68030 )
	ROM_REGION16_BE(0x1000000, "maincpu", 0)  // 16MB address space
	ROM_DEFAULT_BIOS("ipl13")
	ROM_LOAD( "cgrom.dat",  0xf00000, 0xc0000, CRC(9f3195f1) SHA1(8d72c5b4d63bb14c5dbdac495244d659aa1498b6) )
	ROM_SYSTEM_BIOS(0, "ipl10",  "IPL-ROM V1.0 (87/05/07)")
	ROMX_LOAD( "iplrom.dat", 0xfe0000, 0x20000, CRC(72bdf532) SHA1(0ed038ed2133b9f78c6e37256807424e0d927560), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "ipl11",  "IPL-ROM V1.1 (91/01/11)")
	ROMX_LOAD( "iplromxv.dat", 0xfe0000, 0x020000, CRC(00eeb408) SHA1(e33cdcdb69cd257b0b211ef46e7a8b144637db57), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS(2, "ipl12",  "IPL-ROM V1.2 (91/10/24)")
	ROMX_LOAD( "iplromco.dat", 0xfe0000, 0x020000, CRC(6c7ef608) SHA1(77511fc58798404701f66b6bbc9cbde06596eba7), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS(3, "ipl13",  "IPL-ROM V1.3 (92/11/27)")
	ROMX_LOAD( "iplrom30.dat", 0xfe0000, 0x020000, CRC(e8f8fdad) SHA1(239e9124568c862c31d9ec0605e32373ea74b86a), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS(4, "cz600ce",  "CZ-600CE IPL-ROM V1.0 (87/03/18)")
	ROMX_LOAD( "rh-ix0897cezz.ic12", 0xfe0000, 0x010000, CRC(cdc95995) SHA1(810cae207ffd29926e604cf1eb964ae8ea1fadb5), ROM_BIOS(4) | ROM_SKIP(1) )
	ROMX_LOAD( "rh-ix0898cezz.ic11", 0xfe0001, 0x010000, CRC(e60e09a8) SHA1(f3d4a6506493ea3ac7b9c8e441d781fbdd61abd5), ROM_BIOS(4) | ROM_SKIP(1) )
	ROM_LOAD("scsiinrom.dat",0xfc0000, 0x002000, CRC(1c6c889e) SHA1(3f063d4231cdf53da6adc4db96533725e260076a) BAD_DUMP )
//  ROM_LOAD("scsiexrom.dat",0xea0000, 0x002000, NO_DUMP )
	ROM_REGION(0x8000, "user1",0)  // For Background/Sprite decoding
	ROM_FILL(0x0000,0x8000,0x00)
	ROM_REGION(0x20000, "user2", 0)
	ROM_FILL(0x000,0x20000,0x00)
ROM_END


//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT   CLASS           INIT        COMPANY  FULLNAME        FLAGS
COMP( 1987, x68000,   0,      0,      x68000,   x68000, x68k_state,     empty_init, "Sharp", "X68000",       MACHINE_IMPERFECT_GRAPHICS )
COMP( 1990, x68ksupr, x68000, 0,      x68ksupr, x68000, x68ksupr_state, empty_init, "Sharp", "X68000 Super", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING )
COMP( 1991, x68kxvi,  x68000, 0,      x68kxvi,  x68000, x68ksupr_state, empty_init, "Sharp", "X68000 XVI",   MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING )
COMP( 1993, x68030,   x68000, 0,      x68030,   x68000, x68030_state,   empty_init, "Sharp", "X68030",       MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING )



xavix.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood, Angelo Salese
/***************************************************************************

    Preliminary driver for XaviX TV PNP console and childs (Let's! Play TV Classic)

    CPU is an M6502 derivative with added opcodes for far-call handling

    Notes from http://www.videogameconsolelibrary.com/pg00-xavix.htm#page=reviews (thanks Guru!)
    (** this isn't entirely accurate, XaviX Tennis appears to be Super Xavix, see other notes in driver)

    XaviXPORT arrived on the scene with 3 game titles (XaviX Tennis, XaviX Bowling and XaviX Baseball) using their
    original XaviX Multiprocessor.  This proprietary chip is reported to contain an 8-bit high speed central processing
    unit (6502) at 21 MHz, picture processor, sound processor, DMA controller, 1K bytes high speed RAM, universal timer,
    AD/Converter and I/O device control.  Each cartridge comes with a wireless peripheral to be used with the game (Baseball Bat,
    Tennis Racquet, etc.) that requires "AA" batteries.  The XaviXPORT system retailed for $79.99 USD with the cartridges
    retailing for $49.99 USD.

    The following year at CES 2005, SSD COMPANY LIMITED introduced two new XaviXPORT titles (XaviX Golf and XaviX Bass Fishing) each
    containing the upgraded "Super XaviX".  This new chip is said to sport a 16-bit high central processing unit (65816) at 43 MHz.
    SSD COMPANY LIMITED is already working on their next chip called "XaviX II" that is said to be a 32-bit RISC processor
    with 3D capabilities.

    Notes:

    To access service mode in Monster Truck hold Horn and Nitro on startup

    There are multiple revisions of the CPU hardware, the SSD 2000 / SSD 2002 chips definitely add more opcodes
    (thanks to Sean Riddle for this table)

    preliminary list of XaviX software based on various sources (some likely still missing)

    not dumped:

    year        name                                                                                            PCB ID      ROM width       TSOP pads   ROM size        SEEPROM             die markings            extra components / notes

    2006      Let's!TVプレイ ふしぎ星のふたご姫Gyu! ドレスチェンジでキュートにダンス / バンダイ / 日本   Let's!TV Play Gyu, the Twin Princess of the Mysterious Planet! Dance cutely with a dress change / Bandai / Japan
              Jala Jaland /atlus/Japan (arcade version)                                                       -           -               -           -               -                   -                       -
    2004      Printer for TV computer /EPOCH/Japan                                                            -           -               -           -               -                   -                       -
              Wildest computer robot "Daigander" (Korean version) /TAKARA/Korea                               -           -               -           -               -                   -                       -
    2001      Ping-pong(Chinese version) /Tenpon/China                                                        -           -               -           -               -                   -                       -
              Baseball Korean version /SONOKONG/Korea                                                         -           -               -           -               -                   -                       -
    1999      ABC Jungle Fun Hippo VTech/HK (USA and UK versions only, FR is dumped)                          -           -               -           -               -                   -                       -


    XaviX Tennis and XaviX Baseball are the simplest: just the CPU, x16 ROM and 24C08 SEEPROM.  Bowling and Boxing also have 4 IR LEDs and a 32x32 sensor.
    XaviX Fishing has ROM, 24C08 and a 24-pin daughterboard with a Nordic nRF24E1 2.4GHz 8051-based SoC.
    All the rest of the carts include an S35390 I2C clock chip with crystal and battery backup, and have two x16 ROM chips, using a 5-pin single-gate inverter to create complementary /OE signals.
    J-MAT, Fitness Exercise, Fitness Challenge and Bike Concept have a 24CS64 SEEPROM.  Bike Concept also has two 74HC14s.
    Fitness Dance has an Atmel H93864C (maybe SEEPROM?) a Microchip DSPIC 33FJ12GP202 and two JRC 2740 dual op amps.
    Music and Circuit has a 24CS64, two UTC324 quad op amps, a 74HC14, a 74HCT04, and an 8-pin SOIC labeled 61545, which is likely an M61545 dual electronic volume control.

    It looks like the sensors (cameras) are from ETOMS
    They are all 32x32 arrays except Fitness Play, which is 64x64.  Most of the PCBs are marked CIS.
    Bowling and Boxing look identical.
    LOTR and SW look identical
    DQ looks similar to LOTR
    Real Swing Golf (different driver) and Fitness Play look different from all the others.

    The sensor dies for Bowling, Boxing, Star Wars and DQ are labeled CU5501A.
    LOTR is CU5501
    Real Swing Golf is CU5502
    Fitness Play is S-5300A SLH2039H and does not have ETOMS on it.
    The Fitness Play main PCB has an extra glob next to the ribbon cable to the camera.
    Most of the camera PCBs connect to the main PCB with an 18-20 wire ribbon cable.

    Real Swing Golf just has 6 wires, Its camera PCB is the only one with a ceramic resonator
    Maybe the CU5502 chip offloads some processing from the CPU?



    NOTES:

    Play TV Monster Truck runs off an entirely different codebase to everything else, presumably coded by the developer from scratch rather than using code supplied by SSD Company LTD
    Play TV Rescue Heroes fails to display any kind of XaviX logo or SSD Copyright, it is the only XaviX based game so far to not show these details anywhere in the game.



***************************************************************************/

#include "emu.h"
#include "xavix.h"
#include "softlist_dev.h"


/* rad_madf has callf #$8f3f21 in various places, and expects to jump to code in ROM, it is unclear how things map in this case, as presumably
   the CPU 0 page memory and stack are still at 0 but ROM must be in the 3xxx range (game hasn't got far enough to call this yet to help either)

   the maximum romsize appears to be 0x800000 so presumably the high bit being set has some additional meaning

   for now treat it as a swapped arrangement vs. the reads from the lower range, except where page 0 ram would map, it's also possible that
   vram etc. is completely unavailable if executing from these addresses, there isn't much evidence at the moment

   note, many DMA operations and tile bank redirects etc. have the high bit set too, so that could be significant if it defines how it accesses
   memory in those cases too

   this can't be correct, it breaks monster truck which expects ROM at 8000 even in the >0x800000 region, maybe code + data mappings need
   to be kept separate, with >0x800000 showing both ROM banks for code, but still having the zero page area etc. for data?

   The code at 00EA84 in Ping Pong stores 8e to the data bank, reads 16 bit pointer from from 0000 + y (expecting it to come from zero page ram - value 3081)
   then reads data from 3081, pushes it to stack (which is expected to work) then sets data bank back to 00 (writes to ff, expecting it to work) then pulls
   the value written and puts it in RAM.  Is stack actually still memory mapped at this point, or do stack operations always go to stack regardless?
   Do reads return databank/codebank/stack, or only zero page? is zero page visibility maybe even conditional on how it gets used?

   in namcons1 the code at 00F3F2 has data bank set to 0x84 and expects to read from ROM using lda ($0a), y where the content of 0x0a is 0000
   this means that 0a and 0b must be read as zero page, but the actual pointer read from is ROM, bypassing zero page entirely, I currently have a separate
   address space called by that single opcode to handle this, it might need expanding to any other opcodes that aren't using zero page directly tho
   possible stack is similar?


*/

// this is only used for opcode / oprand reads, data memory addressing is handled in core, doing the opcode / oprand addressing in core causes disassembly issues when running from lowbus space (ram, interrupts on most games)
void xavix_state::xavix_map(address_map &map)
{
	map(0x000000, 0x7fffff).r(FUNC(xavix_state::opcodes_000000_r));
	map(0x800000, 0xffffff).r(FUNC(xavix_state::opcodes_800000_r));

	map(0x000000, 0x003fff).w(FUNC(xavix_state::debug_mem_w));

}

// this is used by data reads / writes after some processing in the core to decide if data reads can see lowbus, zeropage, stack, bank registers etc. and only falls through to here on a true external bus access
void xavix_state::xavix_extbus_map(address_map &map)
{
	map(0x000000, 0x7fffff).rom().region("bios", 0x00000);
}

void xavix_cart_state::xavix_extbus_map(address_map &map)
{
	map(0x000000, 0x5fffff).rw(FUNC(xavix_cart_state::cart_r), FUNC(xavix_cart_state::cart_w));
	map(0x600000, 0x7fffff).rom().region("bios", 0x600000);
}

void xavix_state::xavix_4mb_extbus_map(address_map &map)
{
	map(0x000000, 0x3fffff).mirror(0x400000).rom().region("bios", 0x00000);
}

void xavix_state::xavix_2mb_extbus_map(address_map &map)
{
	map(0x000000, 0x1fffff).mirror(0x600000).rom().region("bios", 0x00000);
}

void xavix_state::xavix_1mb_extbus_map(address_map &map)
{
	map(0x000000, 0x0fffff).mirror(0x700000).rom().region("bios", 0x00000);
}

void xavix_state::mainram_w(offs_t offset, uint8_t data)
{
	m_mainram[offset] = data;
/*
// trying to debug anpanmdx title screen issue
    if ((offset == 0x3d) && (data == 0x77))
        logerror("%s: writing 0x77 to 0x3d\n", machine().describe_context());
    if ((offset == 0x98) && (data == 0x77))
        logerror("%s: writing 0x77 to 0x98\n", machine().describe_context());
    if ((offset == 0x3f2) && (data == 0x77))
        logerror("%s: writing 0x77 to 0x3f2\n", machine().describe_context());
// but the unwanted? value of 0x77 is explicitly set by the code at 1c61c
*/
}


void xavix_state::xavix_lowbus_map(address_map &map)
{
	map(0x0000, 0x3fff).ram().w(FUNC(xavix_state::mainram_w)).share("mainram");

	// Memory Emulator / Text Array
	map(0x4000, 0x4fff).rw(FUNC(xavix_state::xavix_memoryemu_txarray_r), FUNC(xavix_state::xavix_memoryemu_txarray_w));

	// Sprite RAM (aka Fragment RAM)
	map(0x6000, 0x67ff).ram().w(FUNC(xavix_state::spriteram_w)).share("fragment_sprite");

	// Palette RAM
	map(0x6800, 0x68ff).ram().w(FUNC(xavix_state::palram_sh_w)).share("palram_sh");
	map(0x6900, 0x69ff).ram().w(FUNC(xavix_state::palram_l_w)).share("palram_l");

	// Segment RAM
	map(0x6a00, 0x6a1f).ram().share("segment_regs"); // test mode, pass flag 0x20

	// Tilemap 1 Registers
	map(0x6fc8, 0x6fcf).rw(FUNC(xavix_state::tmap1_regs_r), FUNC(xavix_state::tmap1_regs_w));

	// Tilemap 2 Registers
	map(0x6fd0, 0x6fd7).rw(FUNC(xavix_state::tmap2_regs_r), FUNC(xavix_state::tmap2_regs_w));

	// Sprite Registers
	map(0x6fd8, 0x6fd8).w(FUNC(xavix_state::spriteregs_w));

	// Sprite DMA
	map(0x6fe0, 0x6fe0).rw(FUNC(xavix_state::spritefragment_dma_status_r), FUNC(xavix_state::spritefragment_dma_trg_w)); // after writing to 6fe1/6fe2 and 6fe5/6fe6 rad_mtrk writes 0x43/0x44 here then polls on 0x40   (see function call at c273) write values are hardcoded, similar code at 18401
	map(0x6fe1, 0x6fe2).w(FUNC(xavix_state::spritefragment_dma_params_1_w));
	map(0x6fe5, 0x6fe6).w(FUNC(xavix_state::spritefragment_dma_params_2_w));

	// Arena Registers (controls visible window + more?)
	map(0x6fe8, 0x6fe8).rw(FUNC(xavix_state::arena_start_r), FUNC(xavix_state::arena_start_w)); // r/w tested
	map(0x6fe9, 0x6fe9).rw(FUNC(xavix_state::arena_end_r), FUNC(xavix_state::arena_end_w)); // r/w tested
	map(0x6fea, 0x6fea).rw(FUNC(xavix_state::arena_control_r), FUNC(xavix_state::arena_control_w));

	// Colour Mixing / Enabling Registers
	map(0x6ff0, 0x6ff0).ram().w(FUNC(xavix_state::colmix_sh_w)).share("colmix_sh"); // effect colour?
	map(0x6ff1, 0x6ff1).ram().w(FUNC(xavix_state::colmix_l_w)).share("colmix_l");
	map(0x6ff2, 0x6ff2).ram().w(FUNC(xavix_state::colmix_6ff2_w)).share("colmix_ctrl"); // set to 07 after clearing above things in interrupt 0

	// Display Control Register / Status Flags
	map(0x6ff8, 0x6ff8).rw(FUNC(xavix_state::dispctrl_6ff8_r), FUNC(xavix_state::dispctrl_6ff8_w)); // always seems to be a read/store or read/modify/store
	map(0x6ff9, 0x6ff9).r(FUNC(xavix_state::pal_ntsc_r));
	map(0x6ffa, 0x6ffa).ram().w(FUNC(xavix_state::dispctrl_posirq_x_w)).share("posirq_x");
	map(0x6ffb, 0x6ffb).ram().w(FUNC(xavix_state::dispctrl_posirq_y_w)).share("posirq_y"); // increases / decreases when you jump in snowboard (snowboard, used to blank ground)

	// Lightgun / pen 1 control
	map(0x6ffc, 0x6fff).r(FUNC(xavix_state::lightgun_r));

	// Sound RAM (tested by Gun Gun Revolution, games don't write here, so it's probably just RAM the sound hardware makes use of directly when mixing)
	map(0x7400, 0x757f).ram();

	// Sound Control
	map(0x75f0, 0x75f1).rw(FUNC(xavix_state::sound_startstop_r), FUNC(xavix_state::sound_startstop_w)); // r/w tested read/written 8 times in a row
	map(0x75f2, 0x75f3).rw(FUNC(xavix_state::sound_updateenv_r), FUNC(xavix_state::sound_updateenv_w));
	map(0x75f4, 0x75f5).r(FUNC(xavix_state::sound_sta16_r)); // related to 75f0 / 75f1 (read after writing there - rad_mtrk)
	map(0x75f6, 0x75f6).rw(FUNC(xavix_state::sound_volume_r), FUNC(xavix_state::sound_volume_w)); // r/w tested
	map(0x75f7, 0x75f7).w(FUNC(xavix_state::sound_regbase_w));
	map(0x75f8, 0x75f8).rw(FUNC(xavix_state::sound_75f8_r), FUNC(xavix_state::sound_75f8_w)); // r/w tested
	map(0x75f9, 0x75f9).rw(FUNC(xavix_state::sound_75f9_r), FUNC(xavix_state::sound_75f9_w));
	map(0x75fa, 0x75fa).rw(FUNC(xavix_state::sound_timer0_r), FUNC(xavix_state::sound_timer0_w)); // r/w tested
	map(0x75fb, 0x75fb).rw(FUNC(xavix_state::sound_timer1_r), FUNC(xavix_state::sound_timer1_w)); // r/w tested
	map(0x75fc, 0x75fc).rw(FUNC(xavix_state::sound_timer2_r), FUNC(xavix_state::sound_timer2_w)); // r/w tested
	map(0x75fd, 0x75fd).rw(FUNC(xavix_state::sound_timer3_r), FUNC(xavix_state::sound_timer3_w)); // r/w tested
	map(0x75fe, 0x75fe).rw(FUNC(xavix_state::sound_irqstatus_r), FUNC(xavix_state::sound_irqstatus_w));
	map(0x75ff, 0x75ff).w(FUNC(xavix_state::sound_75ff_w));

	// Slot Registers
	map(0x7810, 0x7810).w(FUNC(xavix_state::slotreg_7810_w)); // startup

	// External Bus Interface control
	map(0x7900, 0x7902).rw(FUNC(xavix_state::extintrf_790x_r), FUNC(xavix_state::extintrf_790x_w));

	// DMA Controller
	map(0x7980, 0x7980).rw(FUNC(xavix_state::rom_dmastat_r), FUNC(xavix_state::rom_dmatrg_w));
	map(0x7981, 0x7983).ram().w(FUNC(xavix_state::rom_dmasrc_w)).share("rom_dma_src");
	map(0x7984, 0x7985).ram().w(FUNC(xavix_state::rom_dmadst_w)).share("rom_dma_dst");
	map(0x7986, 0x7987).ram().w(FUNC(xavix_state::rom_dmalen_w)).share("rom_dma_len");

	// IO Ports
	map(0x7a00, 0x7a00).rw(FUNC(xavix_state::io0_data_r), FUNC(xavix_state::io0_data_w));
	map(0x7a01, 0x7a01).rw(FUNC(xavix_state::io1_data_r), FUNC(xavix_state::io1_data_w));
	map(0x7a02, 0x7a02).rw(FUNC(xavix_state::io0_direction_r), FUNC(xavix_state::io0_direction_w));
	map(0x7a03, 0x7a03).rw(FUNC(xavix_state::io1_direction_r), FUNC(xavix_state::io1_direction_w));

	// IO Event Interrupt control
	map(0x7a80, 0x7a80).rw(FUNC(xavix_state::ioevent_enable_r), FUNC(xavix_state::ioevent_enable_w));
	map(0x7a81, 0x7a81).rw(FUNC(xavix_state::ioevent_irqstate_r), FUNC(xavix_state::ioevent_irqack_w));

	// Mouse / Trackball?
	map(0x7b00, 0x7b00).rw("anport", FUNC(xavix_anport_device::mouse_7b00_r), FUNC(xavix_anport_device::mouse_7b00_w));
	map(0x7b01, 0x7b01).rw("anport", FUNC(xavix_anport_device::mouse_7b01_r), FUNC(xavix_anport_device::mouse_7b01_w));
	map(0x7b10, 0x7b10).rw("anport", FUNC(xavix_anport_device::mouse_7b10_r), FUNC(xavix_anport_device::mouse_7b10_w));
	map(0x7b11, 0x7b11).rw("anport", FUNC(xavix_anport_device::mouse_7b11_r), FUNC(xavix_anport_device::mouse_7b11_w));

	// Lightgun / pen 2 control
	//map(0x7b18, 0x7b1b)

	// ADC registers
	map(0x7b80, 0x7b80).rw("adc", FUNC(xavix_adc_device::adc_7b80_r), FUNC(xavix_adc_device::adc_7b80_w)); // rad_snow (not often)
	map(0x7b81, 0x7b81).rw("adc", FUNC(xavix_adc_device::adc_7b81_r), FUNC(xavix_adc_device::adc_7b81_w)); // written (often, m_trck, analog related?)

	// Sleep control
	//map(0x7b82, 0x7b83)

	// Timer control
	map(0x7c00, 0x7c00).rw(FUNC(xavix_state::timer_status_r), FUNC(xavix_state::timer_control_w));
	map(0x7c01, 0x7c01).rw(FUNC(xavix_state::timer_baseval_r), FUNC(xavix_state::timer_baseval_w)); // r/w tested
	map(0x7c02, 0x7c02).rw(FUNC(xavix_state::timer_freq_r), FUNC(xavix_state::timer_freq_w));
	map(0x7c03, 0x7c03).r(FUNC(xavix_state::timer_curval_r));

	// Barrel Shifter registers
	map(0x7ff0, 0x7ff1).rw("math", FUNC(xavix_math_device::barrel_r), FUNC(xavix_math_device::barrel_w));

	// Multiply / Divide registers
	map(0x7ff2, 0x7ff4).rw("math", FUNC(xavix_math_device::mult_param_r), FUNC(xavix_math_device::mult_param_w));
	map(0x7ff5, 0x7ff6).rw("math", FUNC(xavix_math_device::mult_r), FUNC(xavix_math_device::mult_w));

	// CPU Vector registers
	map(0x7ff9, 0x7ff9).w(FUNC(xavix_state::vector_enable_w)); // enables / disables the custom vectors
	map(0x7ffa, 0x7ffa).rw(FUNC(xavix_state::nmi_vector_lo_r), FUNC(xavix_state::nmi_vector_lo_w)); // an IRQ vector (nmi?) - popira needs to read it back if you pause on one of the seeprom carts
	map(0x7ffb, 0x7ffb).rw(FUNC(xavix_state::nmi_vector_hi_r), FUNC(xavix_state::nmi_vector_hi_w));
	map(0x7ffc, 0x7ffc).rw(FUNC(xavix_state::irq_source_r), FUNC(xavix_state::irq_source_w));
	// map(0x7ffd, 0x7ffd) some of the Nostalgia games read here, why?
	map(0x7ffe, 0x7ffe).rw(FUNC(xavix_state::irq_vector_lo_r),FUNC(xavix_state::irq_vector_lo_w)); // an IRQ vector (irq?) - rad_opus needs to read this back!
	map(0x7fff, 0x7fff).rw(FUNC(xavix_state::irq_vector_hi_r),FUNC(xavix_state::irq_vector_hi_w));
}

void superxavix_state::superxavix_lowbus_map(address_map &map)
{
	xavix_lowbus_map(map);

	// bitmap layer palette
	map(0x6c00, 0x6cff).ram().w(FUNC(superxavix_state::bmp_palram_sh_w)).share("bmp_palram_sh");
	map(0x6d00, 0x6dff).ram().w(FUNC(superxavix_state::bmp_palram_l_w)).share("bmp_palram_l");

	map(0x6a40, 0x6a7f).ram().w(FUNC(superxavix_state::ext_segment_regs_w)).share("ext_segment_regs"); // 16x32 extended segment regs

	 // bitmap plotter(!) (with auto-inc?) - used by super pc tv units
	map(0x6f60, 0x6f60).w(FUNC(superxavix_state::superxavix_plt_flush_w)); // writes here to flush plotter FIFO
	map(0x6f62, 0x6f62).w(FUNC(superxavix_state::superxavix_plt_dat_w)); // writes plotter data here
	map(0x6f63, 0x6f63).r(FUNC(superxavix_state::superxavix_plt_dat_r));
	map(0x6f64, 0x6f67).rw(FUNC(superxavix_state::superxavix_plt_loc_r), FUNC(superxavix_state::superxavix_plt_loc_w)).share("sx_plt_loc");

	map(0x6f78, 0x6f78).rw(FUNC(superxavix_state::superxavix_chr_pal_index_r), FUNC(superxavix_state::superxavix_chr_pal_index_w)); // SgdPalChrIdx
	map(0x6f79, 0x6f79).rw(FUNC(superxavix_state::superxavix_chr_pal_hue_r), FUNC(superxavix_state::superxavix_chr_pal_hue_w)); // SgdPalChrHue
	map(0x6f7a, 0x6f7a).rw(FUNC(superxavix_state::superxavix_chr_pal_saturation_r), FUNC(superxavix_state::superxavix_chr_pal_saturation_w)); // SgdPalChrSat
	map(0x6f7b, 0x6f7b).rw(FUNC(superxavix_state::superxavix_chr_pal_lightness_r), FUNC(superxavix_state::superxavix_chr_pal_lightness_w)); // SgdPalChrLgt

	map(0x6f7c, 0x6f7c).rw(FUNC(superxavix_state::superxavix_bitmap_pal_index_r), FUNC(superxavix_state::superxavix_bitmap_pal_index_w)); // SgdPalBmpAdr
	map(0x6f7d, 0x6f7d).rw(FUNC(superxavix_state::superxavix_bitmap_pal_hue_r), FUNC(superxavix_state::superxavix_bitmap_pal_hue_w)); // SgdPalBmpHue
	map(0x6f7e, 0x6f7e).rw(FUNC(superxavix_state::superxavix_bitmap_pal_saturation_r), FUNC(superxavix_state::superxavix_bitmap_pal_saturation_w)); // SgdPalBmpSat
	map(0x6f7f, 0x6f7f).rw(FUNC(superxavix_state::superxavix_bitmap_pal_lightness_r), FUNC(superxavix_state::superxavix_bitmap_pal_lightness_w)); // SgdPalBmpLgt

	map(0x6f80, 0x6f99).rw(FUNC(superxavix_state::superxavix_crtc_1_r), FUNC(superxavix_state::superxavix_crtc_1_w)).share("sx_crtc_1"); // Super XaviX CRTC?
	map(0x6fa0, 0x6fa7).rw(FUNC(superxavix_state::superxavix_crtc_2_r), FUNC(superxavix_state::superxavix_crtc_2_w)).share("sx_crtc_2"); // maybe more CRTC regs?

	map(0x6fb0, 0x6fc7).rw(FUNC(superxavix_state::bitmap_params_r), FUNC(superxavix_state::bitmap_params_w)).share("bmp_base");

	// extended external bus stuff (possible banking control?)
	map(0x7909, 0x7909).w(FUNC(superxavix_state::extended_extbus_reg0_w));
	map(0x790b, 0x790b).w(FUNC(superxavix_state::extended_extbus_reg1_w));
	map(0x790d, 0x790d).w(FUNC(superxavix_state::extended_extbus_reg2_w));

	map(0x7a10, 0x7a12).rw("xavix2002io", FUNC(xavix2002_io_device::pio_dir_r), FUNC(xavix2002_io_device::pio_dir_w));
	map(0x7a20, 0x7a22).rw("xavix2002io", FUNC(xavix2002_io_device::pio_out_r), FUNC(xavix2002_io_device::pio_out_w));
	map(0x7a30, 0x7a32).r("xavix2002io", FUNC(xavix2002_io_device::pio_in_r));



	map(0x7ffd, 0x7ffd).nopw(); // looks like a watchdog?
}



static INPUT_PORTS_START( xavix )
	PORT_START("IN0")
	PORT_DIPNAME( 0x01, 0x00, "IN0" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )

	PORT_START("IN1")
	PORT_DIPNAME( 0x01, 0x00, "IN1" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )

	PORT_START("AN0")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN1")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN2")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN3")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN4")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN5")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN6")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN7")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("MOUSE0X")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("MOUSE0Y")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("MOUSE1X")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("MOUSE1Y")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("REGION") // PAL/NTSC flag
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_CUSTOM )
INPUT_PORTS_END

static INPUT_PORTS_START( xavix_an )
	PORT_INCLUDE(xavix)

	// test inputs, not real!
	PORT_MODIFY("AN0") // 00
	PORT_BIT( 0xff, 0x00, IPT_PEDAL ) PORT_SENSITIVITY(100) PORT_KEYDELTA(20)
	PORT_MODIFY("AN1") // 01
	PORT_BIT( 0xff, 0x00, IPT_PEDAL2 ) PORT_SENSITIVITY(100) PORT_KEYDELTA(20)
	PORT_MODIFY("AN2") // 02
	PORT_BIT( 0xff, 0x00, IPT_PEDAL3 ) PORT_SENSITIVITY(100) PORT_KEYDELTA(20)
	PORT_MODIFY("AN3") // 03
	PORT_BIT( 0xff, 0x00, IPT_PEDAL ) PORT_SENSITIVITY(100) PORT_KEYDELTA(20)
	PORT_MODIFY("AN4") // 10
	PORT_BIT( 0xff, 0x00, IPT_PEDAL2 ) PORT_SENSITIVITY(100) PORT_KEYDELTA(20)
	PORT_MODIFY("AN5") // 11
	PORT_BIT( 0xff, 0x00, IPT_PEDAL3 ) PORT_SENSITIVITY(100) PORT_KEYDELTA(20)
	PORT_MODIFY("AN6") // 12
	PORT_BIT( 0xff, 0x00, IPT_PEDAL ) PORT_SENSITIVITY(100) PORT_KEYDELTA(20)
	PORT_MODIFY("AN7") // 13
	PORT_BIT( 0xff, 0x00, IPT_PEDAL2 ) PORT_SENSITIVITY(100) PORT_KEYDELTA(20)
INPUT_PORTS_END

static INPUT_PORTS_START( xavix_i2c )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
INPUT_PORTS_END

static INPUT_PORTS_START( epo_mj )
	PORT_INCLUDE(xavix)

	// there are not 15 buttons, there are 7 plus the dial, this is for figuring out the inputs
	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Back")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Select") // forward through menus
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON5 )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON6 )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON7 )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON8 )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON9 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON10 )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON11 )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON12 )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON13 )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON14 )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON15 )

	PORT_START("DIAL")
	PORT_BIT( 0xff, 0x00, IPT_AD_STICK_X ) PORT_SENSITIVITY(16) PORT_KEYDELTA(16) PORT_REVERSE PORT_PLAYER(1)
INPUT_PORTS_END

static INPUT_PORTS_START( epo_mms )
	PORT_INCLUDE(xavix_i2c)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Pad 1 - Pink")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Pad 2 - Orange")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Pad 3 - Purple")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Pad 4 - Green")

INPUT_PORTS_END

static INPUT_PORTS_START( ban_krrj )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Pad 1 - X")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Pad 2 - Left / Red")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Pad 3 - Right / Green")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Pad 4 - O")

INPUT_PORTS_END

static INPUT_PORTS_START( tak_hamr )
	PORT_INCLUDE(xavix_i2c)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("Player 1 - Green (Left)")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("Player 1 - Yellow (Middle)")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("Player 1 - Red (Right)")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("Player 2 - Green (Left)")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("Player 2 - Yellow (Middle)")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("Player 2 - Red (Right)")

INPUT_PORTS_END

static INPUT_PORTS_START( tomshoot )
	PORT_INCLUDE(xavix_i2c)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2)

	PORT_START("GUN1_0")
	PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(35) PORT_KEYDELTA(15) PORT_PLAYER(1)

	PORT_START("GUN1_1")
	PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_SENSITIVITY(35) PORT_KEYDELTA(15) PORT_PLAYER(1)

INPUT_PORTS_END

static INPUT_PORTS_START( gungunrv )
	PORT_INCLUDE(xavix_i2c)

	PORT_MODIFY("IN1")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN ) // needed to boot
INPUT_PORTS_END

static INPUT_PORTS_START( tomcpin )
	PORT_INCLUDE(xavix_i2c)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("P1 Right Flipper")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("P1 Left Flipper")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("P1 Launch Ball")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("P1 Nudge")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 Right Flipper")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 Left Flipper")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("P2 Launch Ball")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("P2 Nudge")

	PORT_MODIFY("IN1")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.
INPUT_PORTS_END


static INPUT_PORTS_START( tomplc )
	PORT_INCLUDE(xavix_i2c)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Doors / Right")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Wipers / Back")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Headlights / Left")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Horn / Select")

	// this is the up/down handle on the left (pull towards player to increase power)
	PORT_BIT( 0x30, 0x00, IPT_POSITIONAL_V ) PORT_POSITIONS(3) PORT_SENSITIVITY(10) PORT_KEYDELTA(1) PORT_CENTERDELTA(0) PORT_FULL_TURN_COUNT(3) PORT_NAME("Power")

	PORT_MODIFY("IN1")
	// this is the rotary lever (move anti-clockwise to the right to apply brakes)
	PORT_BIT( 0x03, 0x00, IPT_POSITIONAL ) PORT_POSITIONS(3) PORT_SENSITIVITY(10) PORT_KEYDELTA(1) PORT_CENTERDELTA(0) PORT_FULL_TURN_COUNT(3) PORT_NAME("Brake")

	// are you expected to say something when this is held? game makes a crackle and doesn't act like you said anything
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Speak")

	PORT_MODIFY("AN0")
	// all bits in this port seem to be the microphone input, the game just needs to register a large enough value when the button is pressed
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Microphone In")
INPUT_PORTS_END

static INPUT_PORTS_START( tomthr )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	// not an actual joystick, this is a forward / backwards level, and a digital wheel
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_NAME("Move Forwards")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_NAME("Move Backwards")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_NAME("Steer Left") // steering is digital?
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_NAME("Steer Right")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Horn / Select")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Ignition Key")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Map")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Wipers")

	PORT_MODIFY("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Headlights")

	PORT_MODIFY("AN0")
	// all bits in this port seem to be the microphone input, the game just needs to register a large enough value when the button is pressed
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Microphone In")

	PORT_MODIFY("AN5")
	// read during attract demo

	PORT_MODIFY("AN7")
	// battery status?
INPUT_PORTS_END


static INPUT_PORTS_START( ltv_tam )
	PORT_INCLUDE(xavix_i2c)

	PORT_MODIFY("MOUSE0X")
	PORT_BIT( 0xff, 0x00, IPT_AD_STICK_X ) PORT_SENSITIVITY(25) PORT_KEYDELTA(32) PORT_REVERSE PORT_PLAYER(1)
	PORT_MODIFY("MOUSE0Y")
	PORT_BIT( 0xff, 0x00, IPT_AD_STICK_Y ) PORT_SENSITIVITY(25) PORT_KEYDELTA(32) PORT_PLAYER(1)
	PORT_MODIFY("MOUSE1X")
	PORT_BIT( 0xff, 0x00, IPT_AD_STICK_X ) PORT_SENSITIVITY(25) PORT_KEYDELTA(32) PORT_REVERSE PORT_PLAYER(2)
	PORT_MODIFY("MOUSE1Y")
	PORT_BIT( 0xff, 0x00, IPT_AD_STICK_Y ) PORT_SENSITIVITY(25) PORT_KEYDELTA(32) PORT_PLAYER(2)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2)

	PORT_MODIFY("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1)
INPUT_PORTS_END


static INPUT_PORTS_START( tom_tvho )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("MOUSE0X")
	PORT_BIT( 0xff, 0x01, IPT_AD_STICK_X ) PORT_MINMAX(0x01, 0xfe) PORT_SENSITIVITY(7) PORT_KEYDELTA(35) PORT_REVERSE PORT_PLAYER(1)
	PORT_MODIFY("MOUSE0Y")
	PORT_BIT( 0xff, 0x01, IPT_AD_STICK_Y ) PORT_MINMAX(0x01, 0xfe) PORT_SENSITIVITY(7) PORT_KEYDELTA(35) PORT_PLAYER(1)
	PORT_MODIFY("MOUSE1X")
	PORT_BIT( 0xff, 0x01, IPT_AD_STICK_X ) PORT_MINMAX(0x01, 0xfe) PORT_SENSITIVITY(7) PORT_KEYDELTA(35) PORT_REVERSE PORT_PLAYER(2)
	PORT_MODIFY("MOUSE1Y")
	PORT_BIT( 0xff, 0x01, IPT_AD_STICK_Y ) PORT_MINMAX(0x01, 0xfe) PORT_SENSITIVITY(7) PORT_KEYDELTA(35) PORT_PLAYER(2)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2)

	PORT_MODIFY("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1)
INPUT_PORTS_END

// left + right drums together = select / forward (needed on initial screen).  left drum = left in menus   right drum  = right in menus
// analog reading depends heavily on timers, they're too fast right now so drum hits are too hard and register multiple times
static INPUT_PORTS_START( taikodp )
	PORT_INCLUDE(xavix_an)

	PORT_MODIFY("AN0") // 00  (read by one type of function, handled in timer interrupt at 0x1d92 in RAM)
#if 0 // should be more correct, but causes multiple hits to register
	PORT_BIT( 0xff, 0x00, IPT_PEDAL ) PORT_SENSITIVITY(100) PORT_KEYDELTA(20)
#else
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_IMPULSE(1)
	PORT_BIT( 0x7f, IP_ACTIVE_HIGH, IPT_UNUSED )
#endif

	PORT_MODIFY("AN1") // 01  (read by identical function to 00 but with different addresses, handled in timer interrupt at 0x1dbd in RAM)
#if 0 // should be more correct, but causes multiple hits to register
	PORT_BIT( 0xff, 0x00, IPT_PEDAL2 ) PORT_SENSITIVITY(100) PORT_KEYDELTA(20)
#else
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_IMPULSE(1)
	PORT_BIT( 0x7f, IP_ACTIVE_HIGH, IPT_UNUSED )
#endif

	PORT_MODIFY("AN5") // 11  (read by different function, handled in timer interrupt at 0x1de8) (battery status related?)
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_MODIFY("AN7") // 13  (read by identical function to 11 but with different addresses, handled in timer interrupt at 0x1e09 in RAM) (battery status related?)
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	// not read
	PORT_MODIFY("AN2") // 02
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_MODIFY("AN3") // 03
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_MODIFY("AN4") // 10
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_MODIFY("AN6") // 12
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
INPUT_PORTS_END

static INPUT_PORTS_START( jpopira )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Pad 1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Pad 2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Pad 3")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Pad 4")

	PORT_MODIFY("IN1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.
INPUT_PORTS_END



/* Test mode lists the following

  LED (on power button?)
  Throttle Low
  Throttle High
  Reverse
  NO2
  Steering Left (4 positions)
  Steering Right (4 positions)
  Horn

*/

int xavix_mtrk_state::mtrk_wheel_r()
{
	return m_wheel->read_direction();
}


static INPUT_PORTS_START( rad_mtrk )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Nitro")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Throttle High")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Throttle Low")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Reverse / Back")

	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM )  PORT_READ_LINE_MEMBER(FUNC(xavix_mtrk_state::mtrk_wheel_r))

	PORT_MODIFY("IN1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Horn")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.
INPUT_PORTS_END

static INPUT_PORTS_START( rad_mtrkp )
	PORT_INCLUDE(rad_mtrk)

	PORT_MODIFY("REGION") // PAL/NTSC flag
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_CUSTOM )
INPUT_PORTS_END

static INPUT_PORTS_START( rad_opus )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0") // comments show how inputs labeled on PCB
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Pad 1 <") // PI00
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Pad 2")   // PI01
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Pad 3")   // PI02
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Pad 4 >") // PI03

	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Pause") // PI05
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Menu Select") // PI06
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Start / Bomb") // PI07
INPUT_PORTS_END

static INPUT_PORTS_START( rad_opusp )
	PORT_INCLUDE(rad_opus)

	PORT_MODIFY("REGION") // PAL/NTSC flag
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_CUSTOM )
INPUT_PORTS_END

static INPUT_PORTS_START( rad_hnt )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Menu Next")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Menu Previous")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Menu Select")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Pause / Menu")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Fire Gun")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Safety") PORT_TOGGLE

	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.
INPUT_PORTS_END

static INPUT_PORTS_START( rad_crdn )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON2 )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
INPUT_PORTS_END

static INPUT_PORTS_START( rad_crdnp )
	PORT_INCLUDE(rad_crdn)

	PORT_MODIFY("REGION") // PAL/NTSC flag
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_CUSTOM )
INPUT_PORTS_END

static INPUT_PORTS_START( rad_box )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	// 6 types of punch and some navigation controls?
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Left Jan")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Left Hook")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Left Uppercut")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Left Jab")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Left Hook")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Left Uppercut")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON7 )  PORT_NAME("Block")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN ) // needs to be high to pass warning screen?
INPUT_PORTS_END

static INPUT_PORTS_START( rad_boxp )
	PORT_INCLUDE(rad_box)

	PORT_MODIFY("REGION") // PAL/NTSC flag
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_CUSTOM )
INPUT_PORTS_END

static INPUT_PORTS_START( rad_bass ) // also an analog reel
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Menu") // pressing this ingame currently crashes it if read from sound_irqstatus_r is incorrect?
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Select")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Forward") // used to navigate menus
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Back")

	PORT_MODIFY("IN1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) // definitely the dpad, see map screen
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.

INPUT_PORTS_END

static INPUT_PORTS_START( ekara )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(xavix_ekara_state::ekara_multi0_r))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(xavix_ekara_state::ekara_multi1_r))

	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.

	PORT_START("EXTRA0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Echo")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Effects")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_NAME("Key Down")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_NAME("Key Up")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("BGM Down")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("BGM Up")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_NAME("Tempo Down")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON8 ) PORT_NAME("Tempo Up")

	PORT_START("EXTRA1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Select")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Cancel")
	PORT_BIT( 0x3c, IP_ACTIVE_HIGH, IPT_UNUSED )
	// no 40/80 due to multiplexer code
INPUT_PORTS_END

static INPUT_PORTS_START( hikara )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(xavix_hikara_state::ekara_multi0_r))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(xavix_hikara_state::ekara_multi1_r))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(xavix_hikara_state::ekara_multi2_r))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(xavix_hikara_state::ekara_multi3_r))

	PORT_START("EXTRA0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Select")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_NAME("Key Down")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_NAME("Key Up")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON10 ) PORT_NAME("Brightness Up")

	PORT_START("EXTRA1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("BGM Down")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON9 ) PORT_NAME("Brightness Down")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("BGM Up")

	PORT_START("EXTRA2")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Cancel")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON8 ) PORT_NAME("Tempo Up")

	PORT_START("EXTRA3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_NAME("Tempo Down")
INPUT_PORTS_END


static INPUT_PORTS_START( ddrfammt )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Up Arrow") PORT_PLAYER(1) // these can't be treated as a joystick as opposing directions are often required
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Down Arrow") PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P1 Left Arrow") PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("P1 Right Arrow") PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P2 Up Arrow") PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P2 Down Arrow") PORT_PLAYER(2)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P2 Left Arrow") PORT_PLAYER(2)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("P2 Right Arrow") PORT_PLAYER(2)

	PORT_MODIFY("IN1") // once connected the game won't see it as disconnected until you turn off.  I'm guessing this is a signal, not a button?
	PORT_CONFNAME( 0x02,  0x00, "2nd Mat Connected" )
	PORT_CONFSETTING(     0x00, DEF_STR( No ) )
	PORT_CONFSETTING(     0x02, DEF_STR( Yes ) )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.
INPUT_PORTS_END

static INPUT_PORTS_START( popira )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Pad 1") PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Pad 2") PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Pad 3") PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Pad 4") PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Select Previous") PORT_PLAYER(1)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Select Next") PORT_PLAYER(1)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Start") PORT_PLAYER(1)

	PORT_MODIFY("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED ) // halts execution and enables a memory viewer if used with cartridge gc0001 (debug leftover?)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( popira2 ) // player 2 buttons have heavy latency, probably due to incorrect timer emulation, hence NOT WORKING flag right now
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P1 Pad 1") PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P1 Pad 2") PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P1 Pad 3") PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("P1 Pad 4") PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Select Previous") PORT_PLAYER(1)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Select Next") PORT_PLAYER(1)
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Start") PORT_PLAYER(1)

	PORT_MODIFY("IN1")
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(xavix_popira2_cart_state::i2c_r))
	PORT_BIT( 0x30, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	// main input processing code is at 028059, which ends with setting a timer (028079) to read analog ports and get these buttons that way.  main timer handler is at 00eb77, which reads ports via the ADC.  $c3 where ports are stored is also checked at 00e6f4
	PORT_START("P2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("P2 Pad 1") PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("P2 Pad 2") PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("P2 Pad 3") PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("P2 Pad 4") PORT_PLAYER(2)
INPUT_PORTS_END



static INPUT_PORTS_START( rad_bassp )
	PORT_INCLUDE(rad_bass)

	PORT_MODIFY("REGION") // PAL/NTSC flag
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_CUSTOM )
INPUT_PORTS_END


static INPUT_PORTS_START( rad_snow )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_NAME("Foward / Go")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_NAME("Enter / Select")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.
INPUT_PORTS_END

static INPUT_PORTS_START( rad_snowp )
	PORT_INCLUDE(rad_snow)

	PORT_MODIFY("REGION") // PAL/NTSC flag
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_CUSTOM )
INPUT_PORTS_END

static INPUT_PORTS_START( rad_bdp )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Purple / Up")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Red / Down")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Blue / Back")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Pink / Select")

	PORT_MODIFY("IN1")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.
INPUT_PORTS_END

static INPUT_PORTS_START( rad_ping )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Select?")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Pause?")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_SERVICE ) // resets? not a real button?

	PORT_MODIFY("IN1") // are these for the 2nd player?
	//PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Pause?")
	//PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	//PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	//PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_SERVICE ) // resets? not a real button?
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.
INPUT_PORTS_END

static INPUT_PORTS_START( rad_pingp )
	PORT_INCLUDE(rad_ping)

	PORT_MODIFY("REGION") // PAL/NTSC flag
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_CUSTOM )
INPUT_PORTS_END

static INPUT_PORTS_START( nostalgia )
	PORT_INCLUDE(xavix_i2c)

	PORT_MODIFY("IN0") // mappings based on Dragon Buster button list
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON4 ) // Fire4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) // Fire3
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // not a button? (but can be used to pass prompts?)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON5 ) // Pause / Add Coins, marked 'Credit' (but not a coin slot)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) // Fire2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) // Fire1

INPUT_PORTS_END

static INPUT_PORTS_START( tak_geig )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) // pause

	PORT_MODIFY("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(2) // pause

INPUT_PORTS_END

static INPUT_PORTS_START( tak_wdg )
	PORT_INCLUDE(xavix)

	// there is unemulated IR connectivity with other devices that isn't emulated, might rely on some of the unused bits here
	// could also just be that the other robots change the state of another port somewhere in order to be detected

	// test mode code suggesting IR is at 0eb771 (part of it requires opposing directions to be held as this is not a joystick)

	// To access test mode (do this on a fresh boot, not after an F3 reset or it will hang after the red screen):
	// first it checks bits 0x01 in port IN1 and expects them to be 0 (this doesn't seem to be connected to anything normally?)
	// then reads port IN0, masks with 0xf7 and expects only LEFT & RIGHT to he held together

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) // shoot (robot form) / whistle (train form)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) // charge shot (robot form) maybe should be a toggle if it represents an arm position?
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Combine") PORT_TOGGLE // if you toggle it in the robot game it pauses, no effect in train game
	// 0x08 not used?
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_16WAY // robot form only?
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_16WAY // robot form only?
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_16WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_16WAY

	PORT_MODIFY("IN1")
	// these must all be flipped to one state or the other to transform (not sure the best way to handle this)
	// pre-rendered animations will play for each transform, and any running game mode will exit when you start this process

	// 0x01 doesn't trigger any animation, is not used by game, and isn't listed in test mode, but it must be 'off' (returning 0) to access the test mode in the first place (debug pin maybe)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Transformation Head State") PORT_TOGGLE
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("Transformation Body State") PORT_TOGGLE
	// 0x08 doesn't trigger any animation, not used?
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("Transformation Arm 1 State") PORT_TOGGLE
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON7 ) PORT_NAME("Transformation Arm 2 State") PORT_TOGGLE
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF )
	// 0x80 not used?
INPUT_PORTS_END

static INPUT_PORTS_START( tak_comt )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN1")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNKNOWN ) // needed to boot, might read inputs through this bit
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.
INPUT_PORTS_END

static INPUT_PORTS_START( tak_gin )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
INPUT_PORTS_END

static INPUT_PORTS_START( rad_bb )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Menu Select")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Reset?") // might not be a real button
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Pause")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Menu Previous")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Menu Next")

	PORT_MODIFY("IN1")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.
INPUT_PORTS_END

// to access hidden test mode reset while holding Button1 and Button2 (works every other reset)
// to cycle through modes use Button 1 until you get to the input test
static INPUT_PORTS_START( rad_bb2 )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("X")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("O")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.
INPUT_PORTS_END



static INPUT_PORTS_START( rad_fb )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)

	PORT_MODIFY("IN1")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.
INPUT_PORTS_END



static INPUT_PORTS_START( rad_rh )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0") // hold Right+Button 3 when resetting for a version number
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Skip") // skips level, cheat or real input?
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Forward")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Back")

	PORT_MODIFY("IN1")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.
INPUT_PORTS_END

static INPUT_PORTS_START( epo_epp )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) // select
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) // back
	// 04/08 not used for buttons?
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
INPUT_PORTS_END

static INPUT_PORTS_START( rad_jcon )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Horn / Start") // selects game
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Cancel / Back")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Lever Up") // for rock crushing game etc.
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Lever Down")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_NAME("Handle Up") // for crane fishing game etc.
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_NAME("Handle Down")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_NAME("Wheel Right") // for menu, crane construction etc.
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_NAME("Wheel Left")

	PORT_MODIFY("IN1")
	// Oddly the game instructions you to 'press the lever to go' but the manual makes it clear this ignition key is used
	// This is usually the soft power switch input, could Radica have modified this compared to the Japanese original and not given it the correct speech?
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Ignition Key") // moves vehicle forward between parts in digger, pave the road
INPUT_PORTS_END

// there is also a rumble output, likely mapped to one of the port bits
static INPUT_PORTS_START( epo_guru )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 )

	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) // used in the 'from behind' game at least
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )

	PORT_MODIFY("MOUSE1X")
//  PORT_BIT( 0xff, 0x80, IPT_AD_STICK_X ) PORT_SENSITIVITY(6) PORT_KEYDELTA(16) PORT_PLAYER(1) PORT_MINMAX(0x44,0xbc)
	PORT_BIT( 0x1f, 0x10, IPT_AD_STICK_X ) PORT_SENSITIVITY(6) PORT_KEYDELTA(16) PORT_PLAYER(1) // PORT_MINMAX(0x44,0xbc)

	/*
	 (0x20 is subtracted from value returned in read handler)

	 main game
	 00 still
	 01 - 3c right
	 3d - 78 left  - 78 is a nice slow left but invalid for the sub game
	 79 - 7f right
	 80 - 87 left
	 88 - c3 right
	 c4 - ff left

	 sub game (break-out)
	 00 still
	 01 - 3f right
	 40 - 7f left
	 80 still
	 81 - bf right
	 c0 - ff left

	 so valid range seems to be c4-ff (left), 00 (still), 01-3c (right) even if this means the slowest speed going left is faster than the slowest speed going right
	 maybe actual range is 5 bits either way?
	 4 bits either way seems to work best in practice

	 */
INPUT_PORTS_END

static INPUT_PORTS_START( epo_efdx )
	PORT_INCLUDE(xavix_i2c)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) // select
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) // back
	// 04/08 not used for buttons?
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
INPUT_PORTS_END

static INPUT_PORTS_START( epo_tfp2 )
	PORT_INCLUDE(xavix_i2c)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Player 1 Left / Red") PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Player 1 Right / Blue") PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Player 1 Start / Orange")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Player 2 Left / Green") PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Player 2 Right / Pink") PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_START2 ) PORT_NAME("Player 2 Start / Blue")
INPUT_PORTS_END

static INPUT_PORTS_START( epo_tp2p )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Player 1 Left") PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Player 1 Right") PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Player 1 Start")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Player 2 Left") PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Player 2 Right") PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_START2 ) PORT_NAME("Player 2 Start")
INPUT_PORTS_END

static INPUT_PORTS_START( tvpc_tom )
	PORT_INCLUDE(xavix_i2c)
INPUT_PORTS_END

static INPUT_PORTS_START( has_wamg )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN1")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN ) // waits for this after fading out title, what is it?
INPUT_PORTS_END

static INPUT_PORTS_START( evio )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2 )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(xavix_evio_cart_state::i2c_r))
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.
INPUT_PORTS_END

static INPUT_PORTS_START( daig )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")

	PORT_MODIFY("IN1")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.
INPUT_PORTS_END

static INPUT_PORTS_START( gcslottv ) // TODO: proper button names
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SLOT_STOP1 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_SLOT_STOP2 )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SLOT_STOP3 )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Make Selection / Pause Menu")
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Next Option / Spin / Replay")
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_GAMBLE_BET ) // inserting a medal bets automatically, use this to bet from current credits

	PORT_MODIFY("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_COIN1 ) // insert medal / coin (there is a physical coin slot on toy for the medals) auto bets if bet possible, otherwise adds to credits
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( jarajal )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_DIPNAME( 0x01, 0x00, "IN0" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )


	PORT_MODIFY("IN1")
	PORT_DIPNAME( 0x01, 0x00, "IN1" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )
INPUT_PORTS_END


static INPUT_PORTS_START( tcarnavi )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Accelerate")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Brake")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Siren / Transform?") // turns you into a police car?
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Reverse")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Key")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON10 ) PORT_NAME("Dashboard Power?") PORT_TOGGLE
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_NAME("Steer Left") // steering is digital?
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_NAME("Steer Right")

	PORT_MODIFY("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON8 ) PORT_NAME("Lights")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_NAME("Horn")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Wipers")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON9 ) PORT_NAME("Menu")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_POWER_OFF ) PORT_NAME("Power Switch") // pressing this will turn the game off.
INPUT_PORTS_END


static INPUT_PORTS_START( epo_quiz )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("P1 A / 1 / Red / Down")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("P1 B / 2 / Blue / Select")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("P1 C / 3 / Yellow / Left")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("P1 D / 4 / Green / Right")

	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 A / 1 / Red / Down")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 B / 2 / Blue / Select")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("P2 C / 3 / Yellow / Left")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("P2 D / 4 / Green / Right")

	PORT_MODIFY("IN1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(3) PORT_NAME("P3 A / 1 / Red / Down")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(3) PORT_NAME("P3 B / 2 / Blue / Select")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(3) PORT_NAME("P3 C / 3 / Yellow / Left")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(3) PORT_NAME("P3 D / 4 / Green / Right")

	PORT_MODIFY("AN0")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(4) PORT_NAME("P4 A / 1 / Red / Down")
	PORT_MODIFY("AN6")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(4) PORT_NAME("P4 B / 2 / Blue / Select")
	PORT_MODIFY("AN1")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(4) PORT_NAME("P4 C / 3 / Yellow / Left")
	PORT_MODIFY("AN4")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(4) PORT_NAME("P4 D / 4 / Green / Right")

	//PORT_MODIFY("AN7")
	//PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_BUTTON8 ) // battery status

INPUT_PORTS_END

/* correct, 4bpp gfxs */
static const gfx_layout char16layout =
{
	16,16,
	RGN_FRAC(1,1),
	4,
	{ STEP4(0,1) },
	{ 1*4,0*4,3*4,2*4,5*4,4*4,7*4,6*4, 9*4,8*4,11*4,10*4,13*4,12*4,15*4,14*4 },
	{ STEP16(0,4*16) },
	16*16*4
};


static const gfx_layout char_16x16x2_layout =
{
	16,16,
	RGN_FRAC(1,1),
	2,
	{ 0,1 },
	{ 6,4,2,0,14,12,10,8,22,20,18,16,30,28,26,24 },
	{ STEP16(0,16*2) },
	16*16*2
};

static const gfx_layout charlayout8bpp =
{
	8,8,
	RGN_FRAC(1,1),
	8,
	{ STEP8(0,1) },
	{ STEP8(0,8) },
	{ STEP8(0,8*8) },
	8*8*8
};

static const gfx_layout char16layout8bpp =
{
	16,16,
	RGN_FRAC(1,1),
	8,
	{ STEP8(0,1) },
	{ STEP16(0,8) },
	{ STEP16(0,16*8) },
	16*16*8
};

static GFXDECODE_START( gfx_xavix )
	GFXDECODE_ENTRY( "bios", 0, char_16x16x2_layout, 0, 64 )
	GFXDECODE_ENTRY( "bios", 0, gfx_8x8x4_packed_lsb, 0, 16 )
	GFXDECODE_ENTRY( "bios", 0, char16layout, 0, 16 )
	GFXDECODE_ENTRY( "bios", 0, charlayout8bpp, 0, 1 )
	GFXDECODE_ENTRY( "bios", 0, char16layout8bpp, 0, 1 )
GFXDECODE_END

void xavix_state::set_xavix_cpumaps(machine_config &config)
{
	m_maincpu->set_addrmap(AS_PROGRAM, &xavix_state::xavix_map);
	m_maincpu->set_addrmap(5, &xavix_state::xavix_lowbus_map);
	m_maincpu->set_addrmap(6, &xavix_state::xavix_extbus_map);
	m_maincpu->set_vblank_int("screen", FUNC(xavix_state::interrupt));
	m_maincpu->set_vector_callback(FUNC(xavix_state::get_vectors));
}

void xavix_state::xavix(machine_config &config)
{
	/* basic machine hardware */
	XAVIX(config, m_maincpu, MAIN_CLOCK);
	set_xavix_cpumaps(config);

	TIMER(config, "scantimer").configure_scanline(FUNC(xavix_state::scanline_cb), "screen", 0, 1);

	ADDRESS_MAP_BANK(config, "lowbus").set_map(&xavix_state::xavix_lowbus_map).set_options(ENDIANNESS_LITTLE, 8, 24, 0x8000);

	XAVIX_ADC(config, m_adc, 0);
	m_adc->read_0_callback().set(FUNC(xavix_state::adc0_r));
	m_adc->read_1_callback().set(FUNC(xavix_state::adc1_r));
	m_adc->read_2_callback().set(FUNC(xavix_state::adc2_r));
	m_adc->read_3_callback().set(FUNC(xavix_state::adc3_r));
	m_adc->read_4_callback().set(FUNC(xavix_state::adc4_r));
	m_adc->read_5_callback().set(FUNC(xavix_state::adc5_r));
	m_adc->read_6_callback().set(FUNC(xavix_state::adc6_r));
	m_adc->read_7_callback().set(FUNC(xavix_state::adc7_r));

	XAVIX_ANPORT(config, m_anport, 0);
	m_anport->read_0_callback().set(FUNC(xavix_state::anport0_r));
	m_anport->read_1_callback().set(FUNC(xavix_state::anport1_r));
	m_anport->read_2_callback().set(FUNC(xavix_state::anport2_r));
	m_anport->read_3_callback().set(FUNC(xavix_state::anport3_r));

	XAVIX_MATH(config, m_math, 0);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	m_screen->set_screen_update(FUNC(xavix_state::screen_update));
	m_screen->set_size(32*8, 32*8);
	m_screen->set_visarea(0*8, 32*8-1, 2*8, 30*8-1);
	//m_screen->set_video_attributes(VIDEO_UPDATE_SCANLINE);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_xavix);

	PALETTE(config, m_palette, palette_device::BLACK, 256);

	/* sound hardware */

	//SPEAKER(config, "mono").front_center();
	SPEAKER(config, "speaker", 2).front();

	XAVIX_SOUND(config, m_sound, MAIN_CLOCK);
	m_sound->read_regs_callback().set(FUNC(xavix_state::sound_regram_read_cb));
	m_sound->read_samples_callback().set(FUNC(xavix_state::sample_read));
	//m_sound->add_route(ALL_OUTPUTS, "mono", 1.0);
	m_sound->add_route(0, "speaker", 1.0, 0);
	m_sound->add_route(1, "speaker", 1.0, 1);
}

void xavix_state::xavix_4mb(machine_config &config)
{
	xavix(config);
	m_maincpu->set_addrmap(6, &xavix_state::xavix_4mb_extbus_map);
}

void xavix_state::xavix_2mb(machine_config &config)
{
	xavix(config);
	m_maincpu->set_addrmap(6, &xavix_state::xavix_2mb_extbus_map);
}

void xavix_state::xavix_1mb(machine_config &config)
{
	xavix(config);
	m_maincpu->set_addrmap(6, &xavix_state::xavix_1mb_extbus_map);
}


void xavix_guru_state::xavix_guru(machine_config &config)
{
	xavix_4mb_nv(config);

	m_anport->read_2_callback().set(FUNC(xavix_guru_state::guru_anport2_r));
}


void xavix_i2c_state::xavix_i2c_24c02(machine_config &config)
{
	xavix(config);

	I2C_24C02(config, "i2cmem", 0);
}

void xavix_i2c_state::xavix_i2c_24c02_4mb(machine_config &config)
{
	xavix_i2c_24c02(config);
	m_maincpu->set_addrmap(6, &xavix_i2c_state::xavix_4mb_extbus_map);
}

void xavix_i2c_state::xavix_i2c_24c02_2mb(machine_config &config)
{
	xavix_i2c_24c02(config);
	m_maincpu->set_addrmap(6, &xavix_i2c_state::xavix_2mb_extbus_map);
}

void xavix_i2c_state::xavix_i2c_24c02_43mhz(machine_config &config)
{
	xavix_i2c_24c02_4mb(config);

	// tomplc crashes when you start the game at regular clock speeds
	// this could indicate that it's actually one of the newer chips
	// even if extra opcodes are not being used.
	m_maincpu->set_clock(MAIN_CLOCK * 2);
}

void xavix_state::xavix_43mhz(machine_config &config)
{
	xavix_4mb(config);

	// tomthr crashes when you start the game at regular clock speeds
	// this could indicate that it's actually one of the newer chips
	// even if extra opcodes are not being used.
	m_maincpu->set_clock(MAIN_CLOCK * 2);
}



void xavix_i2c_state::xavix_i2c_24lc04(machine_config &config)
{
	xavix(config);

	I2C_24C04(config, "i2cmem", 0); // 24LC04 on Nostalgia games, 24C04 on others
}

void xavix_i2c_state::xavix_i2c_24lc04_4mb(machine_config &config)
{
	xavix_i2c_24lc04(config);
	m_maincpu->set_addrmap(6, &xavix_i2c_state::xavix_4mb_extbus_map);
}

void xavix_i2c_state::xavix_i2c_24lc04_2mb(machine_config &config)
{
	xavix_i2c_24lc04(config);
	m_maincpu->set_addrmap(6, &xavix_i2c_state::xavix_2mb_extbus_map);
}

void xavix_i2c_state::xavix_i2c_24lc04_1mb(machine_config &config)
{
	xavix_i2c_24lc04(config);
	m_maincpu->set_addrmap(6, &xavix_i2c_state::xavix_1mb_extbus_map);
}


void xavix_i2c_ltv_tam_state::xavix_i2c_24lc04_tam(machine_config &config)
{
	xavix_i2c_24lc04_4mb(config);

	m_anport->read_0_callback().set(FUNC(xavix_i2c_ltv_tam_state::tam_anport0_r));
	m_anport->read_1_callback().set(FUNC(xavix_i2c_ltv_tam_state::tam_anport1_r));
	m_anport->read_2_callback().set(FUNC(xavix_i2c_ltv_tam_state::tam_anport2_r));
	m_anport->read_3_callback().set(FUNC(xavix_i2c_ltv_tam_state::tam_anport3_r));
}


void xavix_tom_tvho_state::xavix_tom_tvho(machine_config &config)
{
	xavix_2mb(config);

	m_anport->read_0_callback().set(FUNC(xavix_tom_tvho_state::tvho_anport0_r));
	m_anport->read_1_callback().set(FUNC(xavix_tom_tvho_state::tvho_anport1_r));
	m_anport->read_2_callback().set(FUNC(xavix_tom_tvho_state::tvho_anport2_r));
	m_anport->read_3_callback().set(FUNC(xavix_tom_tvho_state::tvho_anport3_r));
}

void xavix_i2c_mj_state::xavix_i2c_24lc02_mj(machine_config &config)
{
	xavix_i2c_24c02(config);

	m_maincpu->set_addrmap(6, &xavix_i2c_mj_state::xavix_2mb_extbus_map);

	m_anport->read_0_callback().set(FUNC(xavix_i2c_mj_state::mj_anport0_r));
}

void xavix_i2c_state::xavix_i2c_24c08(machine_config &config)
{
	xavix(config);

	I2C_24C08(config, "i2cmem", 0);
}

void xavix_i2c_state::xavix_i2c_24c08_4mb(machine_config &config)
{
	xavix_i2c_24c08(config);
	m_maincpu->set_addrmap(6, &xavix_i2c_state::xavix_4mb_extbus_map);
}

void xavix_i2c_state::xavix_i2c_24c16(machine_config& config)
{
	xavix(config);

	I2C_24C16(config, "i2cmem", 0);
}

void xavix_i2c_state::xavix_i2c_24c16_4mb(machine_config& config)
{
	xavix_i2c_24c16(config);
	m_maincpu->set_addrmap(6, &xavix_i2c_state::xavix_4mb_extbus_map);
}


void xavix_state::xavixp(machine_config &config)
{
	// other clocks should change too!
	xavix(config);
	m_screen->set_refresh_hz(50);
}

void xavix_state::xavixp_4mb(machine_config &config)
{
	xavixp(config);
	m_maincpu->set_addrmap(6, &xavix_state::xavix_4mb_extbus_map);
}

void xavix_state::xavixp_2mb(machine_config &config)
{
	xavixp(config);
	m_maincpu->set_addrmap(6, &xavix_state::xavix_2mb_extbus_map);
}

void xavix_state::xavixp_1mb(machine_config &config)
{
	xavixp(config);
	m_maincpu->set_addrmap(6, &xavix_state::xavix_1mb_extbus_map);
}

void xavix_state::xavix_nv(machine_config &config)
{
	xavix(config);
	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);
}

void xavix_state::xavix_1mb_nv(machine_config &config)
{
	xavix_nv(config);
	m_maincpu->set_addrmap(6, &xavix_state::xavix_1mb_extbus_map);
}

void xavix_state::xavix_2mb_nv(machine_config &config)
{
	xavix_nv(config);
	m_maincpu->set_addrmap(6, &xavix_state::xavix_2mb_extbus_map);
}

void xavix_state::xavix_4mb_nv(machine_config &config)
{
	xavix_nv(config);
	m_maincpu->set_addrmap(6, &xavix_state::xavix_4mb_extbus_map);
}

void xavix_state::xavixp_nv(machine_config &config)
{
	xavixp(config);
	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);
}

void xavix_state::xavixp_1mb_nv(machine_config &config)
{
	xavixp_nv(config);
	m_maincpu->set_addrmap(6, &xavix_state::xavix_1mb_extbus_map);
}



void xavix_mtrk_state::xavix_mtrk(machine_config &config)
{
	xavix_4mb(config);

	XAVIX_MTRK_WHEEL(config, m_wheel, 0);
	m_wheel->event_out_cb().set(FUNC(xavix_state::ioevent_trg08));
}

void xavix_mtrk_state::xavix_mtrkp(machine_config &config)
{
	xavix_mtrk(config);

	m_screen->set_refresh_hz(50);
}

void xavix_madfb_state::xavix_madfb(machine_config &config)
{
	xavix_4mb(config);

	XAVIX_MADFB_BALL(config, m_ball, 0);
	m_ball->event_out_cb().set(FUNC(xavix_state::ioevent_trg01));
}


void xavix_cart_state::xavix_cart(machine_config &config)
{
	xavix(config);

	EKARA_CART_SLOT(config, m_cartslot, 0, ekara_cart, nullptr);
}

void xavix_i2c_cart_state::xavix_i2c_taiko(machine_config &config)
{
	xavix_cart(config);

	I2C_24C02(config, "i2cmem", 0); // 24LC02

	SOFTWARE_LIST(config, "cart_list").set_original("ekara_cart").set_filter("TAIKO");
}

void xavix_i2c_cart_state::xavix_i2c_jpopira(machine_config &config)
{
	xavix_cart(config);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);

	I2C_24C02(config, "i2cmem", 0); // 24LC02

	SOFTWARE_LIST(config, "cart_list").set_original("ekara_cart").set_filter("JPOPIRA");
}

void xavix_cart_state::xavix_cart_evio(machine_config &config)
{
	xavix_cart(config);
	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	SOFTWARE_LIST(config, "cart_list_evio").set_original("evio");
}

void xavix_cart_state::xavix_cart_daig(machine_config &config)
{
	xavix_cart(config);
	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	SOFTWARE_LIST(config, "cart_list_daig").set_original("takara_daigunder_dx_cart");
}

void xavix_cart_gcslottv_state::xavix_extbus_map(address_map &map)
{
	map(0x000000, 0x7fffff).rw(FUNC(xavix_cart_gcslottv_state::cart_r), FUNC(xavix_cart_gcslottv_state::cart_w));
}

void xavix_cart_gcslottv_state::xavix_cart_gcslottv(machine_config &config)
{
	xavix_cart(config);
	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	SOFTWARE_LIST(config, "cart_list_gcslottv").set_original("gcslottv");
}


void xavix_cart_state::xavix_cart_ekara(machine_config &config)
{
	xavix_cart(config);

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("ekara_cart").set_filter("EKARA");
}

void xavix_cart_state::xavix_cart_isinger(machine_config &config)
{
	xavix_cart(config);

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("ekara_cart").set_filter("ISINGER");
}

void xavix_hikara_state::machine_reset()
{
	xavix_ekara_state::machine_reset();

	if (!memregion("cartslot:cart:rom"))
		return;

	// rather crude hack to patch a failing check found in the cartridge ROMS
	// TODO: remove this!
	u8* ROM = memregion("cartslot:cart:rom")->base();
	size_t len = memregion("cartslot:cart:rom")->bytes();

	int foundcount = 0;
	int firstfound = 0;
	for (int i = 0; i < len - 7; i++)
	{
		int matchcount = 0;

		for (int j = 0; j < 5; j++)
		{
			constexpr u8 searchfor[5] = { 0x29, 0x80, 0xd0, 0x14, 0xad };

			if (ROM[i + j] == searchfor[j])
				matchcount++;
		}

		if (matchcount == 5)
		{
			if (foundcount == 0)
				firstfound = i;
			foundcount++;
		}
	}

	if ((foundcount == 3) || (foundcount == 1))
	{
		ROM[firstfound + 2] = 0xf0;
		ROM[firstfound + 7] = 0xd0;
	}
}

void xavix_hikara_state::xavix_cart_hikara(machine_config &config)
{
	xavix_cart(config);

	// The songs seem too slow at regular clock.  It is speculated that the later
	// CPU types run at ~43Mhz, so maybe this is really a XaviX 2000/2003 type chip
	// with a higher clock, even if no extra opcodes are used.
	m_maincpu->set_clock(MAIN_CLOCK * 2);

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("hikara");
}

void xavix_cart_state::xavix_cart_popira(machine_config &config)
{
	xavix_cart(config);

	// is a battery / power source required to store NVRAM in the CPU?  Popira definitely needs NVRAM storing on power-of
	// XaviX Tennis won't boot if you do (but that could be an unrelated SEEPROM issue?) & DDR Family Mat gets stuck in 2 Player mode with no obvious way of changing back
	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("ekara_cart").set_filter("POPIRA");
}

void xavix_cart_state::xavix_cart_popirak(machine_config &config)
{
	xavix_cart(config);
	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);
	SOFTWARE_LIST(config, "cart_list").set_original("ekara_cart").set_filter("POPIRA_KOREA");
}

// see code at 028060, using table from 00eb6d for conversion
uint8_t xavix_popira2_cart_state::popira2_adc0_r()
{
	uint8_t p2 = m_p2->read() & 0x03;
	switch (p2)
	{
	case 0x00: return 0xa0;
	case 0x01: return 0x60;
	case 0x02: return 0x10;
	case 0x03: return 0x00;
	}

	return 0x00;
}

uint8_t xavix_popira2_cart_state::popira2_adc1_r()
{
	uint8_t p2 = (m_p2->read() >> 2) & 0x03;
	switch (p2)
	{
	case 0x00: return 0xa0;
	case 0x01: return 0x60;
	case 0x02: return 0x10;
	case 0x03: return 0x00;
	}

	return 0x00;
}

void xavix_popira2_cart_state::xavix_cart_popira2(machine_config &config)
{
	xavix_cart_popira(config);

	m_adc->read_0_callback().set(FUNC(xavix_popira2_cart_state::popira2_adc0_r));
	m_adc->read_1_callback().set(FUNC(xavix_popira2_cart_state::popira2_adc1_r));
}

void xavix_cart_state::xavix_cart_ddrfammt(machine_config &config)
{
	xavix_cart(config);

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("ekara_cart").set_filter("FAMMAT");
}



void xavix_state::init_xavix()
{
	m_rgnlen = memregion("bios")->bytes();
	m_rgn = memregion("bios")->base();
}

/***************************************************************************

  Game driver(s)

***************************************************************************/

ROM_START( taitons1 )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "taitonostalgia1.u3", 0x000000, 0x200000, CRC(25bd8c67) SHA1(a109cd2da6aa4596e3ca3abd1afce2d0001a473f) )
ROM_END

ROM_START( taitons2 )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "taitonostalgia2.bin", 0x000000, 0x200000, CRC(d7dbd93d) SHA1(ad96f80d317e7fd64682a1fe406c5ee9dd5eabf9) )
ROM_END

ROM_START( namcons1 )
	ROM_REGION( 0x100000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "namconostalgia1.bin", 0x000000, 0x100000, CRC(9bcccccd) SHA1(cf8fe6de76fbd23974f999299db6f558f79c8f22) )
ROM_END

ROM_START( namcons2 )
	ROM_REGION( 0x100000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "nostalgia.bin", 0x000000, 0x100000, CRC(03f7f755) SHA1(bdf1b10ab0104ed580951b0c428c4e93e7373afe) )
ROM_END

ROM_START( rad_box )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("boxing.bin", 0x000000, 0x200000, CRC(5cd40714) SHA1(165260228c029a9502ca0598c84c24fd9bdeaebe) )
ROM_END

ROM_START( rad_boxp )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("boxing.bin", 0x000000, 0x200000, CRC(5cd40714) SHA1(165260228c029a9502ca0598c84c24fd9bdeaebe) )
ROM_END

ROM_START( rad_bass )
	ROM_REGION( 0x100000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("bassfishin.bin", 0x000000, 0x100000, CRC(b54eb1c5) SHA1(084faa9349369f2b8846950765f9c8f758db3e9e) )
ROM_END

ROM_START( rad_bassp )
	ROM_REGION( 0x100000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("bassfishin.bin", 0x000000, 0x100000, CRC(b54eb1c5) SHA1(084faa9349369f2b8846950765f9c8f758db3e9e) )
ROM_END

ROM_START( rad_opus )
	ROM_REGION( 0x100000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("opus.bin", 0x000000, 0x100000, CRC(f84c11c5) SHA1(6e34129d0ca9c52b7c342fc94860629d81705523) )
ROM_END

ROM_START( rad_opusp )
	ROM_REGION( 0x100000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("opus.bin", 0x000000, 0x100000, CRC(f84c11c5) SHA1(6e34129d0ca9c52b7c342fc94860629d81705523) )
ROM_END

ROM_START( rad_opusa )
	ROM_REGION( 0x100000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("opus_alt.bin", 0x000000, 0x100000, CRC(509df402) SHA1(c5b863670eac8498ddda9dfde91387634cf7aa9f) )
ROM_END

ROM_START( rad_hnt )
	ROM_REGION( 0x100000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("huntin1.bin", 0x000000, 0x100000, CRC(e51e250f) SHA1(d72199096d466cd344bb243ef1228e0df9501d00) )
ROM_END

ROM_START( rad_hnt2 )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("huntin2.bin", 0x000000, 0x200000, CRC(fb6846df) SHA1(267632790ed42eba7ef1517b86b024799a78839d) )
ROM_END


ROM_START( rad_snow )
	ROM_REGION( 0x100000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("snoblu.bin", 0x000000, 0x100000, CRC(593e40b3) SHA1(03483ac39eddd7746470fb60018e704382b0da59) )
ROM_END

ROM_START( rad_snowp )
	ROM_REGION( 0x100000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("snoblu.bin", 0x000000, 0x100000, CRC(593e40b3) SHA1(03483ac39eddd7746470fb60018e704382b0da59) )
ROM_END


ROM_START( rad_ping )
	ROM_REGION( 0x100000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "pingpong.bin", 0x000000, 0x100000, CRC(629f7f47) SHA1(2bb19fd202f1e6c319d2f7d18adbfed8a7669235) )
ROM_END

ROM_START( rad_pingp )
	ROM_REGION( 0x100000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "pingpong.bin", 0x000000, 0x100000, CRC(629f7f47) SHA1(2bb19fd202f1e6c319d2f7d18adbfed8a7669235) )
ROM_END

ROM_START( rad_crdn )
	ROM_REGION( 0x100000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "cardnight.bin", 0x000000, 0x100000, CRC(d19eba08) SHA1(cedb9fe785f2a559f518a1d8ecf80d500ddc63c7) )
ROM_END

ROM_START( rad_crdnp )
	ROM_REGION( 0x100000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "cardnight.bin", 0x000000, 0x100000, CRC(d19eba08) SHA1(cedb9fe785f2a559f518a1d8ecf80d500ddc63c7) )
ROM_END

ROM_START( rad_bb )
	ROM_REGION( 0x100000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "radicabaseball.bin", 0x000000, 0x100000, CRC(3fa6f490) SHA1(0772156a67a22d06f5ffd6d1a77f6dc867d0a6d2) )
ROM_END

ROM_START( rad_bb2 )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "baseball2.bin", 0x000000, 0x200000, CRC(bdbf6202) SHA1(18d5cc2d77cbb734629a7a5b6e0f419d21beedbd) )
ROM_END

ROM_START( rad_mtrk )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "monstertruck.bin", 0x000000, 0x400000, CRC(dccda0a7) SHA1(7953cf29643672f8367639555b797c20bb533eab) )
ROM_END

ROM_START( rad_mtrkp ) // rom was dumped from NTSC unit, assuming to be the same
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "monstertruck.bin", 0x000000, 0x400000, CRC(dccda0a7) SHA1(7953cf29643672f8367639555b797c20bb533eab) )
ROM_END

ROM_START( rad_ssx )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("snowssx.bin", 0x000000, 0x400000, CRC(108e19a6) SHA1(3dfb18efb6331b96a53138a5ba29dae9cd966e90) )
ROM_END

ROM_START( rad_ssxp )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("snowssx.bin", 0x000000, 0x400000, CRC(108e19a6) SHA1(3dfb18efb6331b96a53138a5ba29dae9cd966e90) )
ROM_END

ROM_START( rad_sbw )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("snowbwhite.bin", 0x000000, 0x400000, CRC(640c1473) SHA1(d37d1484a5b14735b35afbca305dad7d178b08a2) )
ROM_END

ROM_START( rad_bdp )
	ROM_REGION(0x200000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("barbiepad.bin", 0x000000, 0x200000, CRC(48731512) SHA1(377d4e1c98cafcd9d5e1ee27943289d250a6e7a9) )
ROM_END

ROM_START( rad_madf )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("madden.bin", 0x000000, 0x400000, CRC(e972fdcf) SHA1(52001316254880755da959c3441d232fd2c72c7a) )
ROM_END

ROM_START( rad_fb )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("rfootball.bin", 0x000000, 0x400000, CRC(025e0cb4) SHA1(60ce363de236d5119d078e346ad5d2ae50dbc7e1) )
ROM_END

ROM_START( epo_fish )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("fish.bin", 0x000000, 0x200000, CRC(72392caf) SHA1(16a65c79ff7e3b5f5f514a024bd652412ed38b74) )
ROM_END

ROM_START( epo_efdx )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("excitefishing.bin", 0x000000, 0x400000, CRC(9c85b261) SHA1(6a363faed2ec89c5176e46554a98ca1e20132579) )
ROM_END

ROM_START( epo_esdx )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("baseballdx.bin", 0x000000, 0x400000, CRC(fe2e832e) SHA1(e6343f5e5f52316538d918d0d67c15764aa40f65) )
ROM_END

ROM_START( epo_stad )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("excitestadium.bin", 0x000000, 0x200000, CRC(b58035b9) SHA1(1382a9e42ff932e7ec2186b210917bcf5c571b86) )
ROM_END

ROM_START( epo_esht ) // ESTJ-MAIN REV:0 PCB
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("tigers.u3", 0x000000, 0x400000, CRC(51a17ef3) SHA1(864190e91775716218be3ac0699570844d67d3e7) )
ROM_END

ROM_START( epo_epp )
	ROM_REGION( 0x100000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("excitepingpong.bin", 0x000000, 0x100000, CRC(1fdb9cbd) SHA1(8ed0c1f6d2708ab6e79f0b9553e587c6446e8338) )
ROM_END

ROM_START( epo_epps )
	ROM_REGION( 0x100000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("excitepingpong_special.bin", 0x000000, 0x100000, CRC(d59c4b44) SHA1(5a48b9046a1d1beb10972fc1d41d6d63fe829465) )
ROM_END

ROM_START( epo_eppk )
	ROM_REGION( 0x100000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("sonokongpingpong.bin", 0x000000, 0x100000, CRC(ea81ced6) SHA1(ef8961d3670148501a478c17cd09f5088e32ad41) )
ROM_END

ROM_START( epo_epp2 )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("excitepingpong2.u3", 0x000000, 0x200000, CRC(4b70012a) SHA1(4dd80472067027be5a416ec953f4ed6e7df5fc25) )
ROM_END

ROM_START( epo_epp3 )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("excitepingpong3.bin", 0x000000, 0x200000, CRC(a2ee8bff) SHA1(6e16dbaac9680e1f311c08e3f573d0cf8708b446))
ROM_END

ROM_START( epo_mj ) // MARJ MAIN-11A
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("marj.u4", 0x000000, 0x200000, CRC(4e496424) SHA1(9661130ad8315082bd211b3817e5830b576907a8) )
ROM_END

ROM_START( rad_socr )
	ROM_REGION(0x800000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("soccer.u5", 0x000000, 0x100000, CRC(0564a766) SHA1(bded4f2e0d059731164c35de7df62ef411402901) )
	ROM_LOAD("soccer.u4", 0x400000, 0x200000, CRC(e0e2c488) SHA1(907511d945dec51782af268a94b372dfeb7517c5) )
ROM_END

ROM_START( epo_strk )
	ROM_REGION(0x800000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("excitestriker.u5", 0x000000, 0x100000, CRC(fa3f95fd) SHA1(d3bc7f2f321b81a7783887b29218bf711c0d5518) )
	ROM_LOAD("excitestriker.u4", 0x400000, 0x200000, CRC(0f37ca15) SHA1(b35aaf7f5adddba57c898538f157146bb4d1f9b4) )
ROM_END

ROM_START( epo_strkp )
	ROM_REGION(0x800000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("excitestriker_uk.u5", 0x000000, 0x100000, CRC(201e150e) SHA1(f11405a4b7349e4bdfeb04110b1b6f67e0b63f64) )
	ROM_LOAD("excitestriker_uk.u4", 0x400000, 0x200000, CRC(efd648aa) SHA1(59358b9a9df27dadc33ed4c3da9083f739b68340) )
ROM_END

ROM_START( rad_jcon )
	ROM_REGION(0x800000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("jrconstr_u5.bin", 0x000000, 0x200000, CRC(306699d2) SHA1(121b04bb598d4531db53e497be9270449bb8ffe4) )
	ROM_LOAD("jrconstr_u4.bin", 0x400000, 0x100000, CRC(4cfb6193) SHA1(0f56dfc91db3cc066836daf37ff46f9ff5ec5f94) )
ROM_END

ROM_START( tak_town )
	ROM_REGION(0x800000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("rom.u5", 0x000000, 0x200000, CRC(46587e78) SHA1(f163492139087f7aaeea6448b652f58afcb4dd5c) )
	ROM_LOAD("rom.u4", 0x400000, 0x100000, CRC(642e7e6a) SHA1(d8ff88ad20a60d2eb702dd353f307aa3fcfbbdd5) )
ROM_END

ROM_START( epo_guru )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("gururinworld.bin", 0x000000, 0x400000, CRC(e5ae4523) SHA1(0e39ef8f94203d34e49422081667805f50a339a1) )
ROM_END

ROM_START( epo_dmon ) // Doraemon has a phototransistor, microphone and 24LC02, PCB says XaviX 2002
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("doraemon.bin", 0x000000, 0x400000, CRC(d8f5412f) SHA1(11a4c017ed400f7aa585be744d9693efe734c192) )
ROM_END

ROM_START( rad_rh )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("rescueheroes.bin", 0x000000, 0x200000, CRC(38c391a7) SHA1(120334d4ce89d98438c2a35bf7e53af5096cc878) )
ROM_END

ROM_START( has_wamg )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "minigolf.bin", 0x000000, 0x400000, CRC(35cee2ad) SHA1(c7344e8ba336bc329638485ea571cd731ebf7649) )
ROM_END

ROM_START( ltv_tam )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("letstvtamagotchi.bin", 0x000000, 0x400000, CRC(e3723272) SHA1(e72e128a7a24afb96baafe5f13e13a0d74db4033) )
ROM_END

ROM_START( epo_crok )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("sgm3244.u2", 0x000000, 0x400000, CRC(a801779b) SHA1(e6e4235dc7c7db3073737b10ba4bc5b00deca2c3) )
ROM_END

ROM_START( epo_mms )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("mmnj-main-4.u4", 0x000000, 0x400000, CRC(22f14ca2) SHA1(122e735eb7c54a22de16f65cd43d2cae788e0102) )
ROM_END

ROM_START( tak_zuba ) // CHBJ MAIN REV:01
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("chbj.u2", 0x000000, 0x400000, CRC(6d60c8d2) SHA1(ba687fc95503223dd484ed9533dcb097ecfea00d) )
ROM_END

ROM_START( epo_mmsp )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("mm1j main-00.u1", 0x000000, 0x400000, CRC(65b40a27) SHA1(8f88973122277fe8f31bacb3a070609fde062946) )
ROM_END

ROM_START( ban_krrj ) // KRRJ MAIN PCB 01
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("krrj.u2", 0x000000, 0x400000, CRC(16f0fe78) SHA1(2b18cf6336e5adc64d4db0914788c159e60d91a2) )
ROM_END

ROM_START( tak_geig )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("geigeki.bin", 0x000000, 0x400000, CRC(bd0c3576) SHA1(06f614dbec0225ce4ed866b98450912986d72faf) )
ROM_END

ROM_START( tak_wdg )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("wdgrobot.bin", 0x000000, 0x200000, CRC(7ffc6386) SHA1(e33de5f8e6686e4160d1b90d59167418e87f5a47) )
ROM_END

ROM_START( tom_tvho ) // ET105 REV 0.0
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("tvhockey.u4", 0x000000, 0x200000, CRC(9cd72ae2) SHA1(0530851123b607ddb85f9513405ce97c493f5fd6) )
ROM_END

ROM_START( tak_comt )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("comet.u3", 0x000000, 0x200000, CRC(407c5566) SHA1(41d73c34af8cc3d07a34fcac0bc1856442c94200) )
ROM_END

ROM_START( jarajal )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("takaraslots.bin", 0x000000, 0x200000, CRC(afae0b72) SHA1(b1c5d80a8dc8466982efd79d54cd82a58f0ff997) )
ROM_END

ROM_START( tomshoot )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("airgun.u4", 0x000000, 0x200000, CRC(3e4f7b65) SHA1(4e1660d4952c498e250526c2c3f027253e1fcbe1) )
ROM_END

ROM_START( tcarnavi )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("navi.bin", 0x000000, 0x400000, CRC(f4e693fb) SHA1(be37b35f1e1e661e10187253c2c3aa9858a90812) )
ROM_END

ROM_START( tomcpin )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("championpinball.bin", 0x000000, 0x400000, CRC(24f6d753) SHA1(3d3b39692bef8156da9e350b456c4e2f0af74484) )
ROM_END

ROM_START( tomplc )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("imaplayrailconductor.bin", 0x000000, 0x400000, CRC(b775d0ed) SHA1(33142509b11bbe45b0b9222232033dd64ef01ff2) )
ROM_END

ROM_START( tomthr ) // THRJ MAIN PCB   REV 0.6
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("thrj.u4", 0x000000, 0x400000, CRC(a7e8dc74) SHA1(676b2a905b757356c6c1dfe3f10148484caa44c5) )
ROM_END

ROM_START( gungunad )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00)
	// some lines were swapped going into the die, but it is unclear if they're swapped back inside it
	ROM_LOAD("gga.bin", 0x000000, 0x40000, CRC(5252b6bb) SHA1(8a9f920e4bccabbd337f37a838af574e2b16746f) )
	ROM_CONTINUE(0x080000,0x040000)
	ROM_CONTINUE(0x040000,0x040000)
	ROM_CONTINUE(0x0c0000,0x040000)

	ROM_CONTINUE(0x100000,0x040000)
	ROM_CONTINUE(0x180000,0x040000)
	ROM_CONTINUE(0x140000,0x040000)
	ROM_CONTINUE(0x1c0000,0x040000)
ROM_END


ROM_START( gungunrv )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("gungunrevolution.u1", 0x000000, 0x400000, CRC(4e34f624) SHA1(7acdd0991df78ecffd156381817ed4f85f6aef09) )
ROM_END

ROM_START( bistro )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("bistro.u2", 0x000000, 0x200000, CRC(40865e05) SHA1(597a615c61f29c6f6e7ce997a229175cb151242f) )
ROM_END

/*
    The e-kara cartridges require the BIOS rom to map into 2nd external bus space as they fetch palette data from
    it etc.
*/

ROM_START( ekara )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "ekara.bin", 0x600000, 0x100000, CRC(9b27c4a2) SHA1(d75dda7434933135d2f7e353840a9384e9a0d586) )
	ROM_RELOAD(0x000000, 0x100000)
ROM_END

ROM_START( ekaraa )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "ekara2.bin", 0x600000, 0x100000, CRC(3c92d48d) SHA1(450fbe53826cdb87ec797f84b9757987afcc1ec5) )
	ROM_RELOAD(0x000000, 0x100000)
ROM_END

ROM_START( ekaraj )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "ekarajapan.bin", 0x600000, 0x100000, CRC(e459e43b) SHA1(58b7f36a81571a2df5e812c118fdf68812a05abc) )
	ROM_RELOAD(0x000000, 0x100000)
ROM_END

ROM_START( ekaraphs )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "ekaraheadset.bin", 0x600000, 0x200000, CRC(dd9b3cd7) SHA1(baaf35d56fa45b6f995b8466331bb30f0035f734) )
	ROM_RELOAD(0x000000, 0x200000)
ROM_END

ROM_START( ekarag )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "ekaragermany.bin", 0x600000, 0x200000, CRC(644f06b0) SHA1(433e65a6ea029b67ee62b4b3de52ff3504cfdf16) )
	ROM_RELOAD(0x000000, 0x200000)
ROM_END

ROM_START( ekaras )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "ekaraspanish.bin", 0x600000, 0x200000, CRC(77925ab0) SHA1(402053e22e371c5f5e8069a4bf3b33841e742375) )
	ROM_RELOAD(0x000000, 0x200000)
ROM_END

ROM_START( isinger )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "kr_isinger.bin", 0x600000, 0x100000, CRC(6ad588fe) SHA1(879da069ca8c126232a1b2dfcce25c8615540e48) )
	ROM_RELOAD(0x000000, 0x100000)
ROM_END

ROM_START( epitch )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "ekarajapan.bin", 0x600000, 0x100000, CRC(e459e43b) SHA1(58b7f36a81571a2df5e812c118fdf68812a05abc) )
	ROM_RELOAD(0x000000, 0x100000)
ROM_END

ROM_START( ekaramix )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	// also has a MX87L100C 28-pin chip in the unit, possibly the USB controller.  Part of the ROM contains a FAT filesystem that could possibly appear as a USB drive on the PC?
	ROM_LOAD( "ekaramix.bin", 0x600000, 0x200000, CRC(ee71576e) SHA1(26f8c9edcbbed77e86a1cb5a0b91c92a16fef433) )
	ROM_RELOAD(0x000000, 0x200000)
ROM_END

ROM_START( hikara )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "hikara.u3", 0x000000, 0x100000, CRC(6b91102a) SHA1(684dcfeaa8ac2888da2055617603494ce5fed93c) )
	ROM_RELOAD(0x600000, 0x100000)
	ROM_FILL(0xed19, 1, 0xf0) // temp, bypass unknown boot check
	ROM_FILL(0xed1e, 1, 0xd0)
ROM_END

ROM_START( ddrfammt )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "ekara_ddr_ha010_81947.bin", 0x600000, 0x200000, CRC(737d5d1a) SHA1(a1043047056dd27bca69767ee2044461ec549465) )
	ROM_RELOAD(0x000000, 0x200000)
ROM_END

ROM_START( popira )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "popira.bin", 0x600000, 0x100000, CRC(71d20afc) SHA1(fb2ae023fc8a188c79b2d6550f737e06c05e12da) )
	ROM_RELOAD(0x000000, 0x100000)
ROM_END

ROM_START( popirak )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "kr_main.u2", 0x600000, 0x100000, CRC(785d8e21) SHA1(7877ac33b57bdd8ceac6bda3b0006a3274ad4dce) )
	ROM_RELOAD(0x000000, 0x100000)
ROM_END

ROM_START( popira2 )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "popira2.bin", 0x600000, 0x200000, CRC(6acd8be0) SHA1(8b6c812d37e2dc2f2531b0e47d84ad8485729dc5) )
	ROM_RELOAD(0x000000, 0x200000)
ROM_END

ROM_START( taikodp )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "taikodepopira.bin", 0x600000, 0x200000, CRC(037a8472) SHA1(03cae465965935fc084fb906f8f5de7679f42dd1) )
	ROM_RELOAD(0x000000, 0x200000)
ROM_END

ROM_START( jpopira )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "jumpingpopira.bin", 0x600000, 0x200000, CRC(a7bedbd2) SHA1(d62d4ca660c8df14891217fb7b7a2b4a931ff35f) )
	ROM_RELOAD(0x000000, 0x200000)

	ROM_REGION( 0x100, "i2cmem", ROMREGION_ERASE00 ) // maybe we can remove this eventually, but for now it won't init without a reset between
	ROM_LOAD( "i2cmem.bin", 0x000, 0x100, CRC(70a05af1) SHA1(e8f4ab51445777fe459f9ff09333f548c4e3507c) )
ROM_END

ROM_START( evio )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "evio.bin", 0x600000, 0x200000, CRC(ee22c764) SHA1(f2b7e213eb78065a63ef484a619bcfc61299e30e))
	ROM_RELOAD(0x000000, 0x200000)
ROM_END

// 爆闘宣言ダイガンダー
ROM_START( tak_daig )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "robottransformer_unit.bin", 0x600000, 0x200000, CRC(02deddaa) SHA1(7d7b54684f8b0b92daf02738560779c7df627b91) )
	ROM_RELOAD(0x000000, 0x200000)
ROM_END

// ASKJ MAIN-09 PCB
ROM_START( epo_quiz )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "quizmaster.u1", 0x000000, 0x400000, CRC(e91868b8) SHA1(0128603d755731dafe328b142292dc6e5fe00d78) )
ROM_END


// ガチンコ勝負！ パチスロTV
ROM_START( gcslottv )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "sammyslotunit.bin", 0x000000, 0x200000, CRC(2ba6f3ab) SHA1(1c7fc0c85d817db1550d40c0258f424770e0bd81) )
ROM_END

ROM_START( epo_tfp2 )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "funpark2.u1", 0x000000, 0x400000, CRC(97ad5183) SHA1(77310b42d0a015838a1cef4eb5e74cc8335284d1) )
ROM_END

ROM_START( epo_tp2s ) // TF2J MAIN_01 REV:05
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "tf2j.u3", 0x000000, 0x400000, CRC(db2f124c) SHA1(fd60d4560ed53c63f95cf70a7d1ef13d1ecd1f42) )
ROM_END

ROM_START( epo_tp2p ) // TF3J MAIN PCB 01
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "grandslam.u3", 0x000000, 0x800000, CRC(d458ee01) SHA1(dd1a85d822121c46f89ca013fa56c482ab411c6e) )
ROM_END

ROM_START( epo_tenn )
	ROM_REGION(0x800000, "bios", ROMREGION_ERASE00)
	ROM_LOAD( "excitetennis.u4", 0x000000, 0x100000, CRC(10b0e1dd) SHA1(ba438201434f2b51792b119a3e3d07cc3e53b89a) )
	ROM_LOAD( "excitetennis.u2", 0x400000, 0x200000, CRC(6c2cdc90) SHA1(3c5b391e5e7b4a9a73038ef619df564143724437) )
ROM_END

ROM_START( epo_hamd )
	ROM_REGION(0x800000, "bios", ROMREGION_ERASE00)
	ROM_LOAD( "hamster.u2", 0x000000, 0x100000, CRC(6c2d9d98) SHA1(89a8e6d236ea3dadb882e3ecf12e41bd50222710) )
	ROM_LOAD( "hamster.u3", 0x400000, 0x200000, CRC(e437c8d0) SHA1(f57c54a73ed38826f4b98610a0aa1f15cf95614d) )
ROM_END

ROM_START( tvpc_tom )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "tvpc_thomas.u1", 0x000000, 0x400000, CRC(507f334e) SHA1(d66efd13f166fcd2a66133dc981c8a67b2a26d5f) )
ROM_END

ROM_START( tvpc_dor )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "tvpc_doreamon.u3", 0x000000, 0x400000, CRC(6f2edbb2) SHA1(98fa86f85e00aa40e7a585ff0bc930cb5ca88362) )
ROM_END

ROM_START( tvpc_ham )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "hpcj.u3", 0x000000, 0x400000, CRC(76e8c854) SHA1(5998c03292a16107d0d7ae00f77677582680f323) )
ROM_END

ROM_START( tvpc_hk )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "kpcj.u2", 0x000000, 0x400000, CRC(87fc2f73) SHA1(29a284b907abec175d4289d290490af17a2a963f) )
ROM_END

ROM_START( epo_mail )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "mailcot.u3", 0x000000, 0x400000, CRC(87f13ce7) SHA1(97326257210df411ac8b47e8513bb0da7b02dbde) )

	// has a HT24LC16 in the main unit

	// the cartridges also contain a HT24LC16 for data transfer between units (no other ROM though)
ROM_END


ROM_START( tak_gin )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "snowboard.bin", 0x000000, 0x200000, CRC(79fdeae3) SHA1(ab08790e95cdccf3541ecbddb87ebf0dedb3718b) )
ROM_END

ROM_START( tak_hamr ) // HAMJ MAIN on PCB
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "hamj.u4", 0x000000, 0x400000, CRC(2f4f5270) SHA1(dfb75f0d20247cf1c886840149c7cf91780ae1b9) )
ROM_END

ROM_START( tak_beyb )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "beyblade.u2", 0x000000, 0x200000, CRC(bcf6b3a7) SHA1(1c80f1241138b9d7816f1e5285ff8f3c61739c95) )
ROM_END

ROM_START( tak_beyu )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "beybladeus.u2", 0x000000, 0x200000, CRC(f72eb949) SHA1(20ef0411610d490d07a0924ba111ddfadcbaf407) )
ROM_END

ROM_START( hippofr )
	ROM_REGION( 0x200000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "54-6447-010.u3", 0x000000, 0x200000, CRC(1fb15364) SHA1(ff2bb54f7d6ccd3c83e722599c6f2b213bf35df8) )
ROM_END

/* XaviX hardware titles (1st Generation)

    These use
    SSD 98 PL7351-181
    SSD 98 PA7351-107
    SSD 97 PA7270-107
    type CPUs

    only new opcodes are callf and retf?

*/

// product code 80-32705.
// Some sites say 1997, but 1999 is what SSD had listed, and seems more fitting.
// Also exists as
// 80-32703 Hippo's Alphabet Adventure (UK)
// 80-32700 ABC Jungle Fun (US?)
CONS( 1999, hippofr,  0,          0,  xavix_2mb, xavix,xavix_state,      init_xavix,    "VTech",             "Hippo: et la formidable aventure des lettres (France)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND ) // needs keyboard emulating

// Let's!TVプレイCLASSIC タイトーノスタルジア1
CONS( 2006, taitons1,  0,          0,  xavix_i2c_24lc04_2mb, nostalgia,xavix_i2c_state,      init_xavix,    "Bandai / SSD Company LTD / Taito",             "Let's! TV Play Classic - Taito Nostalgia 1 (Japan)", MACHINE_IMPERFECT_SOUND )

// Let's!TVプレイCLASSIC タイトーノスタルジア2
CONS( 2006, taitons2,  0,          0,  xavix_i2c_24lc04_2mb, nostalgia,xavix_i2c_state,      init_xavix,    "Bandai / SSD Company LTD / Taito",             "Let's! TV Play Classic - Taito Nostalgia 2 (Japan)", MACHINE_IMPERFECT_SOUND )

// Let's!TVプレイCLASSIC ナムコノスタルジア1
CONS( 2006, namcons1,  0,          0,  xavix_i2c_24lc04_1mb, nostalgia,xavix_i2c_state,      init_xavix,    "Bandai / SSD Company LTD / Namco",             "Let's! TV Play Classic - Namco Nostalgia 1 (Japan)", MACHINE_IMPERFECT_SOUND )

// Let's!TVプレイCLASSIC ナムコノスタルジア2
CONS( 2006, namcons2,  0,          0,  xavix_i2c_24lc04_1mb, nostalgia,xavix_i2c_state,      init_xavix,    "Bandai / SSD Company LTD / Namco",             "Let's! TV Play Classic - Namco Nostalgia 2 (Japan)", MACHINE_IMPERFECT_SOUND )

CONS( 2000, rad_ping,  0,          0,  xavix_1mb,        rad_ping, xavix_state,          init_xavix,    "Radica / SSD Company LTD / Simmer Technology", "Play TV Ping Pong (NTSC)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND ) // "Simmer Technology" is also known as "Hummer Technology Co., Ltd"
CONS( 2000, rad_pingp, rad_ping,   0,  xavixp_1mb,       rad_pingp,xavix_state,          init_xavix,    "Radica / SSD Company LTD / Simmer Technology", "ConnecTV Table Tennis (PAL)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// this set reads the region byte and will show either 'RADICA: Play TV Opus' or 'RADICA: ConnecTV Opus' as the title
CONS( 2000, rad_opus,  0,          0,  xavix_1mb_nv,     rad_opus, xavix_state,          init_xavix,    "Radica / SSD Company LTD",                     "Play TV Opus (NTSC)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // there is a missing 'TV Static' noise effect when menus appear (box shows 'Play TV' ingame just shows 'Radica:Plug & Play')
CONS( 2000, rad_opusp, rad_opus,   0,  xavixp_1mb_nv,    rad_opusp,xavix_state,          init_xavix,    "Radica / SSD Company LTD",                     "ConnecTV Opus (PAL)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
// the set below does NOT read the PAL/NTSC flag, and will only display 'RADICA: Plug & Play Opus' as the title
// older release, or region where the Play TV / ConnecTV trademarks weren't used?
CONS( 2000, rad_opusa, rad_opus,   0,  xavixp_1mb_nv,    rad_opus, xavix_state,          init_xavix,    "Radica / SSD Company LTD",                     "Plug & Play Opus (NTSC)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

CONS( 2000, rad_hnt,   0,          0,  xavix_1mb_nv,     rad_hnt,  xavix_state,          init_xavix,    "Radica / SSD Company LTD",                     "Play TV Buckmasters Huntin' (NTSC)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // need to map gun (box shows 'Play TV' ingame just shows 'Plug & Play')

CONS( 2003, rad_hnt2,  0,          0,  xavix_2mb_nv,     rad_hnt,  xavix_state,          init_xavix,    "Radica / SSD Company LTD",                     "Play TV Buckmasters Huntin' 2 (NTSC)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // need to map gun, crashes on pause

CONS( 2003, rad_mtrk,  0,          0,  xavix_mtrk,       rad_mtrk, xavix_mtrk_state,     init_xavix,    "Radica / SSD Company LTD",                     "Play TV Monster Truck (NTSC)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
CONS( 2003, rad_mtrkp, rad_mtrk,   0,  xavix_mtrkp,      rad_mtrkp,xavix_mtrk_state,     init_xavix,    "Radica / SSD Company LTD",                     "ConnecTV Monster Truck (PAL)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

CONS( 200?, rad_box,   0,          0,  xavix_2mb,        rad_box,  xavix_state,          init_xavix,    "Radica / SSD Company LTD",                     "Play TV Boxin' (NTSC)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
CONS( 200?, rad_boxp,  rad_box,    0,  xavixp_2mb,       rad_boxp, xavix_state,          init_xavix,    "Radica / SSD Company LTD",                     "ConnecTV Boxin' (PAL)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

CONS( 200?, rad_crdn,  0,          0,  xavix_1mb,        rad_crdn, xavix_state,          init_xavix,    "Radica / SSD Company LTD",                     "Play TV Card Night (NTSC)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 200?, rad_crdnp, rad_crdn,   0,  xavixp_1mb,       rad_crdnp,xavix_state,          init_xavix,    "Radica / SSD Company LTD",                     "ConnecTV Card Night (PAL)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

CONS( 2000, rad_bb,    0,          0,  xavix_1mb,        rad_bb,   xavix_state,          init_xavix,    "Radica / SSD Company LTD",                     "Play TV Baseball (NTSC)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // Play TV branding used on box, not ingame

CONS( 2001, rad_bass,  0,          0,  xavix_1mb,        rad_bass, xavix_state,          init_xavix,    "Radica / SSD Company LTD",                     "Play TV Bass Fishin' (NTSC)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
CONS( 2001, rad_bassp, rad_bass,   0,  xavixp_1mb,       rad_bassp,xavix_state,          init_xavix,    "Radica / SSD Company LTD",                     "ConnecTV Bass Fishin' (PAL)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// there is another 'Snowboarder' with a white coloured board, it appears to be a newer game closer to 'SSX Snowboarder' but without the SSX license.
CONS( 2001, rad_snow,  0,          0,  xavix_1mb_nv,     rad_snow, xavix_state,          init_xavix,    "Radica / SSD Company LTD",                     "Play TV Snowboarder (Blue) (NTSC)", MACHINE_IMPERFECT_SOUND )
CONS( 2001, rad_snowp, rad_snow,   0,  xavixp_1mb_nv,    rad_snowp,xavix_state,          init_xavix,    "Radica / SSD Company LTD",                     "ConnecTV Snowboarder (Blue) (PAL)", MACHINE_IMPERFECT_SOUND )

CONS( 2003, rad_madf,  0,          0,  xavix_madfb,      rad_fb,   xavix_madfb_state,    init_xavix,    "Radica / Electronic Arts / SSD Company LTD",  "EA Sports Madden Football (NTSC)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND ) // no Play TV branding, USA only release?

CONS( 200?, rad_fb,    0,          0,  xavix_madfb,      rad_fb,   xavix_madfb_state,    init_xavix,    "Radica / SSD Company LTD",                     "Play TV Football (NTSC)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND ) // USA only release? doesn't change logo for PAL.

CONS( 200?, rad_rh,    0,          0,  xavix_2mb,        rad_rh,   xavix_state,          init_xavix,    "Radica / Fisher-Price / SSD Company LTD",      "Play TV Rescue Heroes (NTSC)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

CONS( 2004, rad_ssx,   0,          0,  xavix_4mb,        rad_snow, xavix_state,          init_xavix,    "Radica / Electronic Arts / SSD Company LTD",  "Play TV SSX Snowboarder (NTSC)", MACHINE_IMPERFECT_SOUND )
CONS( 2004, rad_ssxp,  rad_ssx,    0,  xavixp_4mb,       rad_snowp,xavix_state,          init_xavix,    "Radica / Electronic Arts / SSD Company LTD",  "ConnecTV SSX Snowboarder (PAL)", MACHINE_IMPERFECT_SOUND )

// basically a reissue of SSX but without the license
CONS( 2006, rad_sbw,   0,          0,  xavix_4mb,        rad_snow, xavix_state,          init_xavix,    "Radica / SSD Company LTD",                     "Play TV Snowboarder (White) (NTSC)", MACHINE_IMPERFECT_SOUND )
// doesn't exist with ConnecTV branding?

CONS( 2002, rad_bdp,   0,          0,  xavix_2mb,        rad_bdp,  xavix_state,          init_xavix,    "Radica / Mattel / SSD Company LTD",            "Barbie Dance Party", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// ゴール決めるぜ！ エキサイトストライカー
CONS( 2001, epo_strk,  0,          0,  xavix,            epo_epp,  xavix_state,          init_xavix,    "Epoch / SSD Company LTD",                      "Goal Kimeruze! Excite Striker (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
CONS( 2002, epo_strkp, epo_strk,   0,  xavixp,           epo_epp,  xavix_state,          init_xavix,    "Epoch / SSD Company LTD",                      "Let's Play Excite Striker (UK)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
CONS( 2002, rad_socr,  epo_strk,   0,  xavix,            epo_epp,  xavix_state,          init_xavix,    "Radica / Epoch / SSD Company LTD",             "Play TV Soccer", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// まちをつくろう 工事のくるま５
CONS( 2001, tak_town,  0,          0,  xavix,            rad_jcon, xavix_state,          init_xavix,    "Takara / SSD Company LTD",                     "Machi o Tsukurou: Kouji no Kuruma 5 (Japan)", MACHINE_IMPERFECT_SOUND )
// this is roughly based on tak_town above
CONS( 2002, rad_jcon,  tak_town,   0,  xavix,            rad_jcon, xavix_state,          init_xavix,    "Radica / Takara / SSD Company LTD",            "Play TV Jr. Construction", MACHINE_IMPERFECT_SOUND )

// ホームラン打とうぜ! エキサイトスタジアム
CONS( 2000, epo_stad,  0,          0,  xavix_2mb,        epo_epp,  xavix_state,          init_xavix,    "Epoch / SSD Company LTD",                      "Home Run Datouze! Excite Stadium (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
// this seems to be based off the epo_stad code, but heavily modified
CONS( 2002, rad_bb2,   0,          0,  xavix_2mb,        rad_bb2,  xavix_state,          init_xavix,    "Radica / SSD Company LTD",                     "Play TV Baseball 2 (NTSC)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // contains string "Radica RBB2 V1.0"

// 勝負しようぜ！ エキサイトスタジアムDX
CONS( 2002, epo_esdx,  0,          0,  xavix_4mb,        epo_epp,  xavix_state,          init_xavix,    "Epoch / SSD Company LTD",                      "Shoubu Shiyouze! Excite Stadium DX (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// 阪神タイガース エキサイトスタジアムＤＸ
CONS( 2003, epo_esht,  0,          0,  xavix_4mb_nv,     epo_epp,  xavix_state,          init_xavix,    "Epoch / SSD Company LTD",                      "Hanshin Tigers Excite Stadium DX (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// interrupt issues after the title screen cause it to hang
// エースきめるぜ！エキサイトテニス
CONS( 2002, epo_tenn,  0,          0,  xavix,            epo_epp,  xavix_state,          init_xavix,    "Epoch / SSD Company LTD",                      "Ace Kimeruze! Excite Tennis (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// ハムちゃんず大集合 ダンスするのだ！走るのだ！
CONS( 2001, epo_hamd,  0,          0,  xavix,            xavix,    xavix_state,          init_xavix,    "Epoch / SSD Company LTD",                      "Ham-chans Dai Shuugou Dance Surunoda! Hashirunoda! (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// 卓球やろうぜ！ エキサイトピンポン
CONS( 2000, epo_epp,   0,          0,  xavix_1mb,        epo_epp,  xavix_state,          init_xavix,    "Epoch / SSD Company LTD",                      "Takkyuu Yarouze! Excite Ping Pong (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
CONS( 2000, epo_eppk,  epo_epp,    0,  xavix_1mb,        epo_epp,  xavix_state,          init_xavix,    "Epoch / SSD Company LTD / Sonokong",           "Real Ping Pong (Korea)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// しゃもじdeピンポン
// This special version of Excite Ping Pong was a competition prize, not a retail product.  The competition was sponsored by ミツカン (Mizkan)
CONS( 2001, epo_epps,  0,          0,  xavix_1mb,        epo_epp,  xavix_state,          init_xavix,    "Epoch / Mizkan / SSD Company LTD",             "Shamoji de Ping Pong (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// 卓球やろうぜ！ エキサイトピンポン2
CONS( 2003, epo_epp2,   0,         0,  xavix_2mb,        epo_epp,  xavix_state,          init_xavix,    "Epoch / SSD Company LTD",                      "Takkyuu Yarouze! Excite Ping Pong 2 (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// 愛ちゃんに挑戦！エキサイトピンポン
CONS( 2006, epo_epp3,   0,         0,  xavix_2mb,        epo_epp,  xavix_state,          init_xavix,    "Epoch / SSD Company LTD",                      "Ai-chan ni Chousen! Excite Ping Pong (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// TV麻雀 昇段対局～4人打ち
CONS( 2003, epo_mj,     0,         0,  xavix_i2c_24lc02_mj,  epo_mj,   xavix_i2c_mj_state,   init_xavix,    "Epoch / SSD Company LTD",                      "TV Mahjong Shoudan Taikyoku - 4-nin Uchi (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// ブラックバス釣ろうぜ！ エキサイトフィッシング
CONS( 2001, epo_fish,  0,          0,  xavix_2mb,        xavix,    xavix_state,          init_xavix,    "Epoch / SSD Company LTD",                      "Black Bass Tsurouze! Excite Fishing (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// 大モノ釣ろうぜ！ エキサイトフィッシングＤＸ
CONS( 2003, epo_efdx,  0,          0,  xavix_i2c_24c08_4mb,epo_efdx, xavix_i2c_state,      init_xavix,    "Epoch / SSD Company LTD",                      "Dai Mono Tsurouze! Excite Fishing DX (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// ぐるりんワールド
CONS( 2005, epo_guru,  0,          0,  xavix_guru,       epo_guru, xavix_guru_state,     init_xavix,    "Epoch / SSD Company LTD",                      "Gururin World (Japan)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// ドラえもん こえでドカン！わくわくくうきほう！！
CONS( 2002, epo_dmon, 0,           0,  xavix_i2c_24c02_4mb,  xavix_i2c,xavix_i2c_state,      init_xavix,    "Epoch / SSD Company LTD",                      "Doraemon Wakuwaku Kuukihou (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND ) // full / proper title?

// コロッケ！いただ禁貨！バンカーバトル!!
CONS( 2003, epo_crok,  0,          0,  xavix_i2c_24lc04_4mb, xavix_i2c,xavix_i2c_state,      init_xavix,    "Epoch / SSD Company LTD",                      "Croket! Itada Kinka! Banker Battle!! (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// ミニモニ。ステージ！ダンスだぴょん！
CONS( 2002, epo_mms,   0,          0,  xavix_i2c_24c02_4mb,  epo_mms,  xavix_i2c_state,      init_xavix,    "Epoch / SSD Company LTD",                      "mini-moni Stage! Dance Dapyon! (Japan)",  MACHINE_IMPERFECT_SOUND )

// ズバズバブレード
CONS( 2002, tak_zuba,  0,          0,  xavix_i2c_24c02_4mb,  xavix_i2c,xavix_i2c_state,      init_xavix,    "Takara / SSD Company LTD",                     "Zuba Zuba Blade (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// ミニモニ。ステージ！ダンスだぴょん！ぷらすっ
CONS( 2003, epo_mmsp,  0,          0,  xavix_i2c_24c02_4mb,  epo_mms,  xavix_i2c_state,      init_xavix,    "Epoch / SSD Company LTD",                      "mini-moni Stage! Dance Dapyon! Plus (Japan)",  MACHINE_IMPERFECT_SOUND )

// Let's!TVプレイ ケロロ軍曹 ケロロ小隊大パニック！ドタバタ大決戦であります
CONS( 2006, ban_krrj,  0,          0,  xavix_4mb,        ban_krrj, xavix_state,          init_xavix,    "Bandai / SSD Company LTD",                      "Let's! TV Play Keroro Shoutai Dai Panic! Dotabata Daikessen de Arimasu (Japan)",  MACHINE_IMPERFECT_SOUND )

// オールスター感謝祭　超豪華！クイズ決定版～赤坂５丁目体感スタジオ～
CONS( 2004, epo_quiz,  0,          0,  xavix_4mb,        epo_quiz, xavix_state,          init_xavix,    "Epoch / SSD Company LTD",                      "All-Star Kansha-sai Chou Gouka! Quiz Kettieban: Akasaka 5-choume Taikan Studio (Japan)", MACHINE_IMPERFECT_SOUND )

CONS( 2005, has_wamg,  0,          0,  xavix_4mb,        has_wamg, xavix_state,          init_xavix,    "Hasbro / Milton Bradley / SSD Company LTD",    "TV Wild Adventure Mini Golf (NTSC)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// GEIGEKI ゴーゴーシューティング
CONS( 2002, tak_geig,  0,          0,  xavix_4mb_nv,     tak_geig, xavix_state,          init_xavix,    "Takara / SSD Company LTD",                     "Geigeki Go Go Shooting (Japan)", MACHINE_IMPERFECT_SOUND )

// some unemulated connectivity features to add other robots into the game
CONS( 2001, tak_wdg,   0,          0,  xavix_2mb_nv,     tak_wdg,  xavix_state,          init_xavix,    "Takara / SSD Company LTD",                     "Webdiver DX W-05 Gladion (Japan)", MACHINE_IMPERFECT_SOUND )

// TVホッケー
// playable but could do with better deadzome handling on the controls at least
// the trackball functionality works well in the menus, but not the games
CONS( 2001, tom_tvho,  0,          0,  xavix_tom_tvho,   tom_tvho, xavix_tom_tvho_state, init_xavix,    "Tomy / SSD Company LTD",                       "TV Hockey (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// コメットさん☆ラブリンバトン
CONS( 2001, tak_comt,  0,          0,  xavix_2mb_nv,     tak_comt,  xavix_state,          init_xavix,    "Takara / SSD Company LTD",                     "Comet-san Lovelin Baton (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// 爆進スノボ ギンギンボーダーズ
CONS( 2001, tak_gin,   0,          0,  xavix_2mb,        tak_gin,  xavix_state,          init_xavix,    "Takara / SSD Company LTD",                     "Bakushin Sno-Bo - Gingin Boarders (Japan)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_COLORS )

// ぽこぽこハンマーズ
CONS( 2002, tak_hamr,  0,          0,  xavix_i2c_24c02_4mb,  tak_hamr, xavix_i2c_state,      init_xavix,    "Takara / SSD Company LTD",                     "Poko Poko Hammers (Japan)", MACHINE_IMPERFECT_SOUND )

CONS( 2003, tak_beyu,  0,          0,  xavix_2mb,        xavix,    xavix_state,          init_xavix,    "Takara / Hasbro / SSD Company LTD",            "Beyblade Arcade Challenge 5-in-1 (US)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

//ベイブレード　アルティメットシューター
CONS( 2002, tak_beyb,  tak_beyu,   0,  xavix_2mb,        xavix,    xavix_state,          init_xavix,    "Takara / SSD Company LTD",                     "Beyblade Ultimate Shooter (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// was also distributed by Atlus as an arcade cabinet in 2005, ROM almost certainly different (this one will auto-power off after inactivity, an arcade wouldn't do that)
// ジャラジャランド
CONS( 2003, jarajal,   0,          0,  xavix_2mb_nv,     jarajal,  xavix_state,          init_xavix,    "Takara / SSD Company LTD",                     "Jyarajyaland (Japan, PlugIt! version)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// 近代撃ちまくりバラエティ 射的王(シャテキング)
CONS( 2002, tomshoot, 0,           0,  xavix_i2c_24c02_2mb,  tomshoot,xavix_i2c_tomshoot_state,  init_xavix,    "Tomy / SSD Company LTD",                   "Kindai Uchimakuri Variety Shateking (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// トミカ カーナビドライブ / トミー
CONS( 2003, tcarnavi,  0,          0,  xavix_4mb_nv,     tcarnavi, xavix_state,          init_xavix,    "Tomy / SSD Company LTD",                       "Tomica Carnavi Drive (Japan)", MACHINE_IMPERFECT_SOUND )

// ちゃんぴよんピンボール
CONS( 2003, tomcpin,   0,          0,  xavix_i2c_24c08_4mb,  tomcpin,  xavix_i2c_state,      init_xavix,    "Tomy / SSD Company LTD",                       "Champiyon Pinball (Japan)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// 日本一周 僕はプラレール運転士
CONS( 2004, tomplc,    0,          0,  xavix_i2c_24c02_43mhz,tomplc,xavix_i2c_state,     init_xavix,    "Tomy / SSD Company LTD",                       "Nihon Isshuu - Boku wa Plarail Untenshi (Japan)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// テレビで遊び隊 ハイパーレスキュー ぼくは救助隊！
// the demos in attract mode end instantly as if a button was being held, but not sure where it's read
CONS( 2006, tomthr,    0,          0,  xavix_43mhz,      tomthr,   xavix_state,          init_xavix,    "Takara Tomy / SSD Company LTD",                "Asobitai Hyper Rescue - Boku wa Kyuujotai! (Japan)", MACHINE_IMPERFECT_SOUND )

// ガンガンアドベンチャー
CONS( 2001, gungunad,  0,          0,  xavix_2mb_nv,     xavix,    xavix_state,          init_xavix,    "Takara / SSD Company LTD",                     "Gun Gun Adventure (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// ガンガンレボリューション
CONS( 2004, gungunrv,  0,          0,  xavix_i2c_24lc04_4mb, gungunrv, xavix_i2c_state,      init_xavix,    "Takara / SSD Company LTD",                     "Gun Gun Revolution (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// ビストロキッズ ぼくもわたしもコックさん！
CONS( 2001, bistro,    0,          0,  xavix_2mb,        xavix,    xavix_state,          init_xavix,    "Sega Toys / SSD Company LTD",                  "Bistro Kids: Boku mo Watashi mo Kok-san! (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )


/* Music titles: Emulation note:
   Timers might not be 100%, PAL stuff uses different ways to do timing.
*/
CONS( 2000, ekara,    0,           0,  xavix_cart_ekara, ekara,    xavix_ekara_state,    init_xavix,    "Takara / SSD Company LTD / Hasbro",            "e-kara (US?, NTSC, set 1)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND /*| MACHINE_IS_BIOS_ROOT*/ ) // shows "Please insert a cartridge before turn it on" without cart
CONS( 2000, ekaraa,   ekara,       0,  xavix_cart_ekara, ekara,    xavix_ekara_state,    init_xavix,    "Takara / SSD Company LTD / Hasbro",            "e-kara (US?, NTSC, set 2)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND /*| MACHINE_IS_BIOS_ROOT*/ ) // shows "Please insert a cartridge before turning on e-kara" without cart
CONS( 2000, ekaraj,   ekara,       0,  xavix_cart_ekara, ekara,    xavix_ekara_state,    init_xavix,    "Takara / SSD Company LTD",                     "e-kara (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND /*| MACHINE_IS_BIOS_ROOT*/ ) // shows Japanese message without cart
CONS( 2002, ekarag,   ekara,       0,  xavix_cart_ekara, ekara,    xavix_ekara_state,    init_xavix,    "Takara / SSD Company LTD",                     "e-kara (Europe, includes 3 songs)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND /*| MACHINE_IS_BIOS_ROOT*/ ) // found in Germany and UK, could just be a Europe-wide model, NOT a headset model, but still has 3 songs.
CONS( 2002, ekaras,   ekara,       0,  xavix_cart_ekara, ekara,    xavix_ekara_state,    init_xavix,    "Takara / SSD Company LTD / newgent",           "e-kara (Spain, includes 3 songs)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND /*| MACHINE_IS_BIOS_ROOT*/ )
// the Korean i-singer release from Sonokong has a unique bios and different data resources, meaning carts are not fully compatible between it and e-kara
CONS( 2000, isinger,  ekara,       0,  xavix_cart_isinger, ekara,    xavix_ekara_state,    init_xavix,    "Sonokong / SSD Company LTD",                   "i-Singer (Korea)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND /*| MACHINE_IS_BIOS_ROOT*/ )

// the 'e-kara pro headset' has 3 songs built in for the US release.  The Japanese release of this appears to be called 'e-kara H.S.' and it is unclear if it also has built in songs.  The Canadian box says 'cartridge contains' instead of 'songs included' but is likely a printing error.
CONS( 2002, ekaraphs, ekara,       0,  xavix_cart_ekara, ekara,    xavix_ekara_state,    init_xavix,    "Takara / SSD Company LTD",                     "e-kara Pro Headset (US, includes 3 songs)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND /*| MACHINE_IS_BIOS_ROOT*/ )

// epitch (at least the pichi pichi pitch mermaid starter pack) uses the same internal rom as the Japanese ekara, but has less buttons, so some features aren't available (some games also seem to expect to read it with a different layout eg 'a7' cart, but 'a5' cart doesn't, so must be a way to enable that mode, or bug in code?)
CONS( 2003, epitch,   0,           0,  xavix_cart_ekara, ekara,    xavix_ekara_state,    init_xavix,    "Takara / SSD Company LTD",                     "e-pitch (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND /*| MACHINE_IS_BIOS_ROOT*/ ) // shows Japanese message without cart

// e-kara mix was another unit that allowed you to connect to a PC, unlike e-kara web it also functions as a regular device
CONS( 200?, ekaramix, 0,           0,  xavix_cart_ekara, ekara,    xavix_ekara_state,    init_xavix,    "Takara / SSD Company LTD",                     "e-kara Mix (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND /*| MACHINE_IS_BIOS_ROOT*/ )

// ダンスダンスレボリューション　ふぁみマット
CONS( 2001, ddrfammt, 0,           0,  xavix_cart_ddrfammt,ddrfammt, xavix_cart_state,   init_xavix,    "Takara / Konami / SSD Company LTD",            "Dance Dance Revolution Family Mat (Japan)", MACHINE_IMPERFECT_SOUND/*|MACHINE_IS_BIOS_ROOT*/ )

// ポピラ
CONS( 2000, popira,   0,           0,  xavix_cart_popira,popira,   xavix_cart_state,     init_xavix,    "Takara / SSD Company LTD",                     "Popira (Japan)", MACHINE_IMPERFECT_SOUND/*|MACHINE_IS_BIOS_ROOT*/ ) // The original Popira is a single yellow unit
// the Korean release is not properly compatible with the usual Popira carts, although some of the Korean i-singer range are compatible
CONS( 2001, popirak,  popira,      0,  xavix_cart_popirak,popira,   xavix_cart_state,     init_xavix,    "Sonokong / SSD Company LTD",                   "PoPiRa - Finger Sports (Korea)", MACHINE_IMPERFECT_SOUND/*|MACHINE_IS_BIOS_ROOT*/ )

// ポピラ2
CONS( 2002, popira2,  0,           0,  xavix_cart_popira2,popira2,  xavix_popira2_cart_state, init_xavix,    "Takara / SSD Company LTD",                 "Popira 2 (Japan)", MACHINE_IMPERFECT_SOUND/*|MACHINE_IS_BIOS_ROOT*/ ) // Popira 2 is a set of 2 blue & green linked units (2nd unit is just a controller, no CPU or TV out)

// たいこでポピラ
CONS( 2003, taikodp,  0,           0,  xavix_i2c_taiko,  taikodp,  xavix_i2c_cart_state, init_xavix,    "Takara / SSD Company LTD",                     "Taiko de Popira (Japan)", MACHINE_IMPERFECT_SOUND /*|MACHINE_IS_BIOS_ROOT*/ ) // inputs? are the drums analog?

// ジャンピンポピラ
CONS( 2004, jpopira,  0,           0,  xavix_i2c_jpopira,jpopira,  xavix_i2c_cart_state, init_xavix,    "Takara / SSD Company LTD",                     "Jumping Popira (Japan)", MACHINE_IMPERFECT_SOUND /*|MACHINE_IS_BIOS_ROOT*/ )

// evio
CONS( 2003, evio,     0,           0,  xavix_cart_evio,  evio,     xavix_evio_cart_state,init_xavix,    "Tomy / SSD Company LTD",                       "Evio (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND /*|MACHINE_IS_BIOS_ROOT*/ ) // inputs? it's a violin controller


// 2 ROM expansion cartridges were available for this, in the form of other robots (the heads acts as cartridges) see takara_daigunder_dx_cart.xml
// a number of other robots were also available, but those act as controllers and don't plug into the ROM slot (presumably the signal sent is used to determine the character)
CONS( 2002, tak_daig, 0,           0,  xavix_cart_daig,  daig,     xavix_daig_cart_state,init_xavix,    "Takara / SSD Company LTD",                     "Bakutou Sengen Daigunder DX (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND /*|MACHINE_IS_BIOS_ROOT*/ ) // inputs? maybe IO interrupt?

CONS( 2002, gcslottv, 0,           0,  xavix_cart_gcslottv,  gcslottv,     xavix_cart_gcslottv_state,     init_xavix,    "Takara / Sammy / DCT / SSD Company LTD",       "Gachinko Shoubu! PachisloTV (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND /*|MACHINE_IS_BIOS_ROOT*/ )

// Let’s!TVプレイ 超にんきスポット!ころがしほーだい たまごっちりぞーと   (Let's! TV Play Chou Ninki Spot! Korogashi-Houdai Tamagotchi Resort) (only on the Japanese list? http://test.shinsedai.co.jp/english/products/Applied/list.html )   This also allows you to use an IR reciever to import a Tamagotchi from compatible games
CONS( 2006, ltv_tam,  0,           0,  xavix_i2c_24lc04_tam,  ltv_tam,xavix_i2c_ltv_tam_state,      init_xavix,    "Bandai / SSD Company LTD",                      "Let's! TV Play Chou Ninki Spot! Korogashi-Houdai Tamagotchi Resort (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

CONS( 2008, hikara,   0,           0,  xavix_cart_hikara, hikara,    xavix_hikara_state,    init_xavix,    "Takara Tomy / SSD Company LTD",            "Hi-kara (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND /*| MACHINE_IS_BIOS_ROOT*/ )

// 東京フレンドパーク2
CONS( 2003, epo_tfp2,  0,          0,  xavix_i2c_24c08_4mb,  epo_tfp2, xavix_i2c_state, init_xavix, "Epoch / SSD Company LTD", "Tokyo Friend Park II (Japan)", MACHINE_IMPERFECT_SOUND) // uses in24lc08b

// 東京フレンドパーク2スペシャル
CONS( 2005, epo_tp2s,  0,          0,  xavix_4mb,        epo_tp2p, xavix_state,     init_xavix, "Epoch / SSD Company LTD", "Tokyo Friend Park II Special! (Japan)", MACHINE_IMPERFECT_SOUND)

// 東京フレンドパークⅡ パーフェクト!めざせ!グランドスラム‼︎
CONS( 2007, epo_tp2p,  0,          0,  xavix,            epo_tp2p, xavix_state,     init_xavix, "Epoch / SSD Company LTD", "Tokyo Friend Park II Perfect! Mezase! Grand Slam!! (Japan)", MACHINE_IMPERFECT_SOUND)

// きかんしゃトーマス テレビパソコン
CONS( 2005, tvpc_tom,  0,          0,  xavix_i2c_24c16_4mb,  tvpc_tom, xavix_i2c_state, init_xavix, "Epoch / SSD Company LTD", "TV-PC Thomas & Friends (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
// ドラえもん テレビパソコン
CONS( 2003, tvpc_dor,  0,          0,  xavix_i2c_24c16_4mb,  tvpc_tom, xavix_i2c_state, init_xavix, "Epoch / SSD Company LTD", "TV-PC Doraemon (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
// とっとこハム太郎 テレビパソコン
CONS( 2003, tvpc_ham,  0,          0,  xavix_i2c_24c16_4mb,  tvpc_tom, xavix_i2c_state, init_xavix, "Epoch / SSD Company LTD", "TV-PC Tottoko Hamutaro (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)

CONS( 2004, tvpc_hk,   0,          0,  xavix_i2c_24c16_4mb,  tvpc_tom, xavix_i2c_state, init_xavix, "Epoch / SSD Company LTD", "TV-PC Hello Kitty (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)

// TVメールPC mail*cot メルコット
// has a 74HC541D (maybe keyboard related?)
CONS( 2004, epo_mail,  0,          0,  xavix_i2c_24c16_4mb,  tvpc_tom, xavix_i2c_state, init_xavix, "Epoch / SSD Company LTD", "TV Mail PC mail*cot (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)



xavix_2000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/***************************************************************************/

/* (XaviX 2000 type CPU) hardware titles (2nd XaviX generation?)

   these use the SSD 2000 NEC 85605-621 type CPU

   This CPU type adds extra opcodes that don't appear to be present in the 97/98 types
   It does not appear to support the bitmap modes or 16-bit ROMs found in the 2002 type

   It is possible some games in the regular xavix.cpp drivers use this type, there
   does not appear to be a way to tell unless the extra opcodes are used or the CPU
   has been decapped

*/

#include "emu.h"
#include "xavix_2002.h"

// #define VERBOSE 1
#include "logmacro.h"

static INPUT_PORTS_START( xavix )
	PORT_START("IN0")
	PORT_DIPNAME( 0x01, 0x00, "IN0" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )

	PORT_START("IN1")
	PORT_DIPNAME( 0x01, 0x00, "IN1" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )

	PORT_START("AN0")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN1")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN2")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN3")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN4")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN5")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN6")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN7")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("MOUSE0X")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("MOUSE0Y")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("MOUSE1X")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("MOUSE1Y")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("REGION") // PAL/NTSC flag
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_CUSTOM )
INPUT_PORTS_END

static INPUT_PORTS_START( xavix_i2c )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
INPUT_PORTS_END


static INPUT_PORTS_START( epo_ebox )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) // select
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) // back
	// 04/08 not used for buttons?
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
INPUT_PORTS_END


static INPUT_PORTS_START( epo_bowl )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)

	PORT_MODIFY("IN1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
INPUT_PORTS_END

static INPUT_PORTS_START( duelmast )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1)

	PORT_MODIFY("IN1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(xavix_duelmast_state::unknown_random_r))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(xavix_duelmast_state::unknown_random_r))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
INPUT_PORTS_END


static INPUT_PORTS_START( epo_sdb )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("MOUSE0X")
	PORT_BIT( 0xff, 0x01, IPT_AD_STICK_X ) PORT_MINMAX(0x01, 0xfe) PORT_SENSITIVITY(25) PORT_KEYDELTA(32) PORT_REVERSE PORT_PLAYER(1)
	PORT_MODIFY("MOUSE0Y")
	PORT_BIT( 0xff, 0x01, IPT_AD_STICK_Y ) PORT_MINMAX(0x01, 0xfe) PORT_SENSITIVITY(25) PORT_KEYDELTA(32) PORT_PLAYER(1)
	PORT_MODIFY("MOUSE1X")
	PORT_BIT( 0xff, 0x01, IPT_AD_STICK_X ) PORT_MINMAX(0x01, 0xfe) PORT_SENSITIVITY(25) PORT_KEYDELTA(32) PORT_REVERSE PORT_PLAYER(2)
	PORT_MODIFY("MOUSE1Y")
	PORT_BIT( 0xff, 0x01, IPT_AD_STICK_Y ) PORT_MINMAX(0x01, 0xfe) PORT_SENSITIVITY(25) PORT_KEYDELTA(32) PORT_PLAYER(2)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2)

	PORT_MODIFY("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1)
INPUT_PORTS_END


static INPUT_PORTS_START( ttv_lotr )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(xavix_i2c_lotr_state::unknown_random_r))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(xavix_i2c_lotr_state::unknown_random_r))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
INPUT_PORTS_END

static INPUT_PORTS_START( epo_hamc )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(xavix_epo_hamc_state::unknown_random_r))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(xavix_epo_hamc_state::unknown_random_r))
INPUT_PORTS_END

static INPUT_PORTS_START( ttv_mx )
	PORT_INCLUDE(xavix_i2c)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) // Accel
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) // Brake
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Pause")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_NAME("Motion Up") // you tilt the device, but actual inputs are digital
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_NAME("Motion Down")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_NAME("Motion Left")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_NAME("Motion Right")
INPUT_PORTS_END


void xavix_state::xavix2000(machine_config &config)
{
	xavix(config);

	XAVIX2000(config.replace(), m_maincpu, MAIN_CLOCK);
	set_xavix_cpumaps(config);

	m_palette->set_entries(512);
}

void xavix_state::xavix2000_4mb(machine_config &config)
{
	xavix2000(config);
	m_maincpu->set_addrmap(6, &xavix_state::xavix_4mb_extbus_map);
}

void xavix_state::xavix2000_nv(machine_config &config)
{
	xavix2000(config);
	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);
}

void xavix_state::xavix2000_4mb_nv(machine_config &config)
{
	xavix2000_nv(config);
	m_maincpu->set_addrmap(6, &xavix_state::xavix_4mb_extbus_map);
}


void xavix_2000_nv_sdb_state::xavix2000_nv_sdb(machine_config &config)
{
	xavix2000_4mb_nv(config);

	m_anport->read_0_callback().set(FUNC(xavix_2000_nv_sdb_state::sdb_anport0_r));
	m_anport->read_1_callback().set(FUNC(xavix_2000_nv_sdb_state::sdb_anport1_r));
	m_anport->read_2_callback().set(FUNC(xavix_2000_nv_sdb_state::sdb_anport2_r));
	m_anport->read_3_callback().set(FUNC(xavix_2000_nv_sdb_state::sdb_anport3_r));
}

void xavix_i2c_state::xavix2000_i2c_24c08(machine_config &config)
{
	xavix2000(config);
	I2C_24C08(config, "i2cmem", 0);
}

void xavix_i2c_state::xavix2000_i2c_24c08_4mb(machine_config &config)
{
	xavix2000_i2c_24c08(config);
	m_maincpu->set_addrmap(6, &xavix_i2c_state::xavix_4mb_extbus_map);
}


void xavix_i2c_state::xavix2000_i2c_24c04(machine_config &config)
{
	xavix2000(config);

	I2C_24C04(config, "i2cmem", 0);
}

void xavix_i2c_state::xavix2000_i2c_24c04_2mb(machine_config &config)
{
	xavix2000_i2c_24c04(config);
	m_maincpu->set_addrmap(6, &xavix_i2c_state::xavix_2mb_extbus_map);
}

void xavix_i2c_state::xavix2000_i2c_24c04_4mb(machine_config &config)
{
	xavix2000_i2c_24c04(config);
	m_maincpu->set_addrmap(6, &xavix_i2c_state::xavix_4mb_extbus_map);
}


void xavix_i2c_state::xavix2000_i2c_24c02(machine_config &config)
{
	xavix2000(config);

	I2C_24C02(config, "i2cmem", 0);
}

void xavix_duelmast_state::xavix_extbus_map(address_map &map)
{
	map(0x000000, 0x7fffff).rw(FUNC(xavix_duelmast_state::cart_r), FUNC(xavix_duelmast_state::cart_w));
	map(0x408000, 0x40ffff).ram(); // seems to expect RAM here (at least when cart is enabled)
}

void xavix_duelmast_state::duelmast(machine_config &config)
{
	xavix2000(config);

	I2C_24C04(config, "i2cmem", 0);

	EKARA_CART_SLOT(config, m_cartslot, 0, ekara_cart, nullptr);
	SOFTWARE_LIST(config, "cart_list_duelmast").set_original("duelmast_cart");
}


ROM_START( epo_sdb )
	ROM_REGION(0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("superdashball.bin", 0x000000, 0x400000, CRC(a004a764) SHA1(47a96822d4d7d6a0f6be5cd729c3747dbab65979) )
ROM_END

ROM_START( epo_ebox )
	ROM_REGION(0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("exciteboxing.bin", 0x000000, 0x400000, CRC(e25ae4f5) SHA1(7f7b613f0ab8f43f5cad0d13de538921e77cae9c) )
ROM_END

ROM_START( epo_bowl )
	ROM_REGION(0x200000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("bowling.bin", 0x000000, 0x200000, CRC(d34f8d9e) SHA1(ebe3792172dc43904b9226beb27f1da89d2388cc) )
ROM_END

ROM_START( epo_golf ) // GLFJ MAIN-03
	ROM_REGION(0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("golf.bin", 0x000000, 0x400000, CRC(d1f231cf) SHA1(9421836a6bc4af9ee1fc7a402d62b2fb4dbcdefc) )
ROM_END

ROM_START( epo_hamc ) // ET158 MB REV.0
	ROM_REGION(0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD( "hamster.u1", 0x000000, 0x400000, CRC(b1177813) SHA1(ed01096ebb63b72267ad7e0b2115224bbab64011) )
ROM_END

ROM_START( ban_omt ) // OMTJ MAIN-07
	ROM_REGION(0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("otmj.bin", 0x000000, 0x400000, CRC(1c1dc6fb) SHA1(d0cf1345b765d66ca9a0870ee6d0e3ccd84a8c0b) )
ROM_END

ROM_START( ttv_sw )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "jedi.bin", 0x000000, 0x800000, CRC(51cae5fd) SHA1(1ed8d556f31b4182259ca8c766d60c824d8d9744) )
ROM_END

ROM_START( ttv_swj )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "lightsaber.bin", 0x000000, 0x800000, CRC(a5c22ed0) SHA1(406f0bccb01cd4a26fe4a5675d7ebecc78c58147) )
ROM_END


ROM_START( ttv_lotr )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "lotr.bin", 0x000000, 0x800000, CRC(a034ecd5) SHA1(264a9d4327af0a075841ad6129db67d82cf741f1) )
ROM_END

ROM_START( ttv_mx )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "mxdirtrebel.bin", 0x000000, 0x800000, CRC(e64bf1a1) SHA1(137f97d7d857697a13e0c8984509994dc7bc5fc5) )
ROM_END

ROM_START( tom_jump )
	ROM_REGION(0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "tom_jump.bin", 0x000000, 0x800000, CRC(20bf5c17) SHA1(bca7535baa6a54ad3ee0929bd3b74a22cb5139da) )
ROM_END


ROM_START( drgqst )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "dragonquest.bin", 0x000000, 0x800000, CRC(3d24413f) SHA1(1677e81cedcf349de7bf091a232dc82c6424efba) )
ROM_END

ROM_START( epo_mini )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "minimoni.u1", 0x000000, 0x400000, CRC(2adb01ee) SHA1(987218b6799195ba15adf39885c1d177c381ec26) )
ROM_END

ROM_START( tak_chq )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "choroq.u2", 0x000000, 0x400000, CRC(ffd2eb95) SHA1(a30884da5554483ebfd0009cf5dd1768be8a99cb) )
ROM_END

ROM_START( ban_onep )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("onepiece.bin", 0x000000, 0x800000, CRC(c5cb5a5f) SHA1(db85f6cc48d77c5a4967b9b8e2999167e3dfc8c8) )
ROM_END

ROM_START( duelmast )
	ROM_REGION(0x800000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("duelmasters.u4", 0x000000, 0x200000, CRC(2f11fcd7) SHA1(d8849c74833e77b8b309e845523f2cdc7ac68054) )
	ROM_RELOAD(0x200000,0x200000)
	ROM_RELOAD(0x400000,0x200000)
	ROM_RELOAD(0x600000,0x200000)
ROM_END

ROM_START( tom_dpgm )
	ROM_REGION(0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("disney.bin", 0x000000, 0x400000, CRC(1dc181b3) SHA1(fa30069d17705f27e4ff45e7f6ccf06986e138f3) )
ROM_END

ROM_START( epo_es2j ) // ES2J MAIN-01  2005 date on PCB, 2006 ingame
	ROM_REGION(0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("es2j.u3", 0x000000, 0x400000, CRC(840aecb1) SHA1(ad52449ffc13af5f4c67b2c3cf438e7ecd80b9fb) )
ROM_END



// doesn't use extra opcodes?
// K.O.しようぜ！エキサイトボクシング
CONS( 2002, epo_ebox, 0, 0, xavix2000_4mb_nv,        epo_ebox,    xavix_state,             init_xavix, "Epoch / SSD Company LTD",       "Excite Boxing (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND ) // doesn't use XaviX2000 extra opcodes, but had that type of CPU

// die not confirmed, but uses extra opcodes.  (hangs on title screen due to combination of freq_timer_done nested interrupts tripping, and waiting on bits in input ports to change
// ストライクきめるぜ！ エキサイトボウリング
CONS( 2002, epo_bowl, 0, 0, xavix2000_i2c_24c04_2mb, epo_bowl,    xavix_i2c_state,         init_xavix, "Epoch / SSD Company LTD",       "Excite Bowling (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// スーパーショット！ エキサイトゴルフ
// needs timer irq hack to boot, fails to draw main menu properly (buggy xavix2000 opcodes?)  (2002 date on PCB, 2003 ingame)
CONS( 2003, epo_golf, 0,       0, xavix2000_i2c_24c04_4mb, ttv_lotr,   xavix_i2c_lotr_state, init_no_timer, "Epoch / SSD Company LTD",       "Super Shot! Excite Golf (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// とっとこハム太郎 ハムハム大サーカス！
CONS( 2002, epo_hamc,  0,      0, xavix2000_4mb,       epo_hamc,   xavix_epo_hamc_state, init_xavix,    "Epoch / SSD Company LTD",       "Tottoko Hamtaro - Ham Ham Dai Circus! (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// ミニモニ。パーティ！リズムでぴょん！
// needs timer irq hack to boot
CONS( 2003, epo_mini, 0,       0, xavix2000_i2c_24c08_4mb, ttv_lotr,   xavix_i2c_lotr_state, init_no_timer, "Epoch / SSD Company LTD",        "mini-moni Party! Rhythm de Pyon! (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// カードスキャン!　エキサイトステージ サッカー日本代表チーム
CONS( 2006, epo_es2j,   0,     0,  xavix2000_4mb,      xavix,      xavix_state,          init_xavix,    "Epoch / SSD Company LTD",        "Card Scan! Excite Stage Soccer Nippon Daihyou Team (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )


// takes a long time to boot to a card scanner error
// This is a product in the Duel Masters line called Duel Station; the boot up screen calls it Duel Station, title logo is Duel Masters
CONS( 2003, duelmast, 0, 0, duelmast, duelmast,    xavix_duelmast_state,    init_xavix, "Takara / SSD Company LTD",      "Duel Masters: Duel Station (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// スーパーダッシュボール
CONS( 2004, epo_sdb,  0, 0, xavix2000_nv_sdb,    epo_sdb,     xavix_2000_nv_sdb_state, init_xavix, "Epoch / SSD Company LTD",       "Super Dash Ball (Japan)",  MACHINE_IMPERFECT_SOUND )

CONS( 2005, ttv_sw,   0,      0, xavix2000_i2c_24c02, ttv_lotr,    xavix_i2c_lotr_state,    init_xavix, "Tiger / SSD Company LTD",       "Star Wars Saga Edition - Lightsaber Battle Game", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
CONS( 2005, ttv_swj,  ttv_sw, 0, xavix2000_i2c_24c02, ttv_lotr,    xavix_i2c_lotr_state,    init_xavix, "Tomy / SSD Company LTD",        "Star Wars Saga Edition - Lightsaber Battle Game (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
CONS( 2005, ttv_lotr, 0,      0, xavix2000_i2c_24c02, ttv_lotr,    xavix_i2c_lotr_state,    init_xavix, "Tiger / SSD Company LTD",       "Lord of the Rings - Warrior of Middle-Earth", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
CONS( 2005, ttv_mx,   0,      0, xavix2000_i2c_24c04, ttv_mx,      xavix_i2c_state,         init_xavix, "Tiger / SSD Company LTD",       "MX Dirt Rebel", MACHINE_IMPERFECT_SOUND )

// テレビで遊び隊　韋駄天翔 激走 韋駄天バトル  - seems to be based on the same engine at ttv_mx and has an almost identical controller, but not exactly the same game
CONS( 2005, tom_jump,   0,    0, xavix2000_i2c_24c04, ttv_mx,      xavix_i2c_state,         init_xavix, "Tomy / SSD Company LTD",        "IDATEN Jump: Gekisou IDATEN Battle (Japan)", MACHINE_IMPERFECT_SOUND )

// 剣神ドラゴンクエスト 甦りし伝説の剣
CONS( 2003, drgqst,   0,      0, xavix2000_i2c_24c08, ttv_lotr,    xavix_i2c_lotr_state,    init_xavix, "Square Enix / SSD Company LTD", "Kenshin Dragon Quest: Yomigaerishi Densetsu no Ken (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// チョロＱビュンビュンレーサー
// crashes whenever a CPU car reaches a corner - see map
CONS( 2003, tak_chq,  0,      0, xavix2000_i2c_24c04_4mb, xavix_i2c,   xavix_i2c_state,         init_xavix, "Takara / SSD Company LTD",      "Choro-Q Byun Byun Racer (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// hangs after starting a game, check why
// Let’s！TV プレイ　体感格闘ワンピースパンチバトル 　～海賊王にキミがなる！～
CONS( 2004, ban_onep, 0, 0, xavix2000_i2c_24c04, ttv_lotr,    xavix_i2c_lotr_state, init_xavix, "Bandai / SSD Company LTD",         "Let's! TV Play Taikan Kakutou One Piece Punch Battle - Kaizoku Ou ni Kimi ga Naru (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// Let’s！TV プレイ　闘印奥義　 陰陽大戦記～目指せ最強闘神士～
// stalls unless timers are disabled like epo_mini / epo_golf, 2004 date on PCB, 2005 ingame
CONS( 2005, ban_omt,  0, 0, xavix2000_i2c_24c04_4mb, ttv_lotr,    xavix_i2c_lotr_state, init_no_timer, "Bandai / SSD Company LTD",         "Let's! TV Play Touin Ougi Onmyou Taisenki: Mezase Saikyou Toushinshi (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// ディズニープリンセス　キラキラ魔法のレッスン
CONS( 2004, tom_dpgm, 0, 0, xavix2000_i2c_24c08_4mb, ttv_lotr,    xavix_i2c_lotr_state, init_xavix, "Tomy / SSD Company LTD",         "Disney Princess Kirakira Mahou no Lesson (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )



xavix_2002.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/***************************************************************************/

/* SuperXaviX (XaviX 2002 type CPU) hardware titles (3rd XaviX generation?)

  these use the SSD 2002 NEC 85054-611 type CPU
  differences include support for 16-bit ROMs, high resolution bitmap modes, interlace screen modes, extra IO

   XavixPort Golf is "SSD 2003 SuperXaviX MXIC 2003 3009" (not dumped yet, but actually marked as SuperXaviX unlike the others!)

*/

#include "emu.h"
#include "xavix_2002.h"

// #define VERBOSE 1
#include "logmacro.h"

/* The 'XaviXPORT' isn't a real console, more of a TV adapter, all the actual hardware (CPU including video hw, sound hw) is in the cartridges and controllers
   and can vary between games, see notes at top of driver.

   The 'Domyos Interactive System (DiS)' released in France by Decathlon appears to be identical to XaviXPORT (but for PAL regions, and with an entirely different software range)
*/


static INPUT_PORTS_START( xavix )
	PORT_START("IN0")
	PORT_DIPNAME( 0x01, 0x00, "IN0" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )

	PORT_START("IN1")
	PORT_DIPNAME( 0x01, 0x00, "IN1" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )

	PORT_START("AN0")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN1")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN2")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN3")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN4")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN5")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN6")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("AN7")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("MOUSE0X")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("MOUSE0Y")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("MOUSE1X")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_START("MOUSE1Y")
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("EX0")
	PORT_DIPNAME( 0x01, 0x00, "EX0" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )

	PORT_START("EX1")
	PORT_DIPNAME( 0x01, 0x00, "EX1" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )

	PORT_START("EX2")
	PORT_DIPNAME( 0x01, 0x00, "EX2" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )

	PORT_START("REGION") // PAL/NTSC flag
	PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_CUSTOM )
INPUT_PORTS_END

static INPUT_PORTS_START( xavix_jmat )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON4 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON1 )

INPUT_PORTS_END

static INPUT_PORTS_START( xavix_i2c )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
INPUT_PORTS_END

static INPUT_PORTS_START( maxheart )
	PORT_INCLUDE(xavix_i2c)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START1 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_16WAY
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_16WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_16WAY
INPUT_PORTS_END

static INPUT_PORTS_START( epo_tfit )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) // select
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) // back
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
INPUT_PORTS_END


static INPUT_PORTS_START( mrangbat )
	PORT_INCLUDE(xavix_i2c)

	PORT_MODIFY("IN0")
	PORT_DIPNAME( 0x01, 0x00, "IN0" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )

	PORT_MODIFY("IN1")
	PORT_DIPNAME( 0x01, 0x00, "IN1" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )

	PORT_MODIFY("EX0") // NOT A JOYSTICK!!
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_16WAY // Red/Up 1
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2) PORT_16WAY // Red/Up 2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_16WAY // Green / Circle / Right 2
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_16WAY // Pink / Star / Left 2
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_16WAY // Blue / Square / Right 1
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_16WAY // Yellow / Triangle / Left 1
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON7 )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON8 )
INPUT_PORTS_END

static INPUT_PORTS_START( xavix_bowl )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(superxavix_i2c_bowl_state::unknown_random_r))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(superxavix_i2c_bowl_state::unknown_random_r))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
INPUT_PORTS_END

static INPUT_PORTS_START( ban_ult )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(superxavix_i2c_bowl_state::unknown_random_r))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(superxavix_i2c_bowl_state::unknown_random_r))
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))

	PORT_MODIFY("EX2")
	PORT_DIPNAME( 0x80, 0x80, "Demo Mode" ) // bypasses calibration screen
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( ban_gkr )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("IN1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))

	PORT_MODIFY("EX1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(superxavix_i2c_bowl_state::unknown_random_r))

	PORT_MODIFY("EX2")
	PORT_DIPNAME( 0x80, 0x80, "Demo Mode" ) // bypasses calibration screen
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( xavixp )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("REGION") // PAL/NTSC flag
	PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_CUSTOM )
INPUT_PORTS_END

static INPUT_PORTS_START( ban_ordj )
	PORT_INCLUDE(xavix_i2c)

	PORT_MODIFY("IN1")
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) ) // something input related, having it high allows bypass of calibration screen
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
INPUT_PORTS_END

static INPUT_PORTS_START( ban_dn1j )
	PORT_INCLUDE(xavix_i2c)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON3 )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON1 )

	PORT_MODIFY("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 )
INPUT_PORTS_END

static INPUT_PORTS_START( anpanmdx )
	PORT_INCLUDE(xavix_i2c)

	PORT_MODIFY("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) // Back
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) // Start ('Paper')
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) // Left in Menu ('Red Star')
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON5 ) // Right in Menu ('Blue Star')
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON6 )
INPUT_PORTS_END

static INPUT_PORTS_START( suprtvpc )
	PORT_INCLUDE(xavix)

	PORT_MODIFY("MOUSE0X")
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_X ) PORT_SENSITIVITY(25) PORT_KEYDELTA(32) PORT_REVERSE PORT_PLAYER(1)
	PORT_MODIFY("MOUSE0Y")
	PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y ) PORT_SENSITIVITY(25) PORT_KEYDELTA(32) PORT_PLAYER(1)
INPUT_PORTS_END

/* SuperXavix IO port handlers (per game) */

uint8_t superxavix_i2c_jmat_state::read_extended_io0(offs_t offset, uint8_t mem_mask)
{
	LOG("%s: read_extended_io0\n", machine().describe_context());
	return 0x00;
}

uint8_t superxavix_i2c_jmat_state::read_extended_io1(offs_t offset, uint8_t mem_mask)
{
	LOG("%s: read_extended_io1\n", machine().describe_context());

	uint8_t ret = 0x00;

	// reads this by reading the byte, then shifting right 4 times to place value into carry flag
	if (!(mem_mask & 0x08))
		ret |= m_i2cmem->read_sda() << 3;

	return ret;
}

uint8_t superxavix_i2c_jmat_state::read_extended_io2(offs_t offset, uint8_t mem_mask)
{
	LOG("%s: read_extended_io2\n", machine().describe_context());
	return 0x00;
}

void superxavix_i2c_jmat_state::write_extended_io0(offs_t offset, uint8_t data, uint8_t mem_mask)
{
	LOG("%s: io0_data_w %02x\n", machine().describe_context(), data);
}

void superxavix_i2c_jmat_state::write_extended_io1(offs_t offset, uint8_t data, uint8_t mem_mask)
{
	LOG("%s: io1_data_w %02x\n", machine().describe_context(), data);

	if (mem_mask & 0x08)
		m_i2cmem->write_sda((data & 0x08) >> 3);

	if (mem_mask & 0x10)
		m_i2cmem->write_scl((data & 0x10) >> 4);

}

void superxavix_i2c_jmat_state::write_extended_io2(offs_t offset, uint8_t data, uint8_t mem_mask)
{
	LOG("%s: io2_data_w %02x\n", machine().describe_context(), data);
}

void superxavix_i2c_state::write_io1(uint8_t data, uint8_t direction)
{
	m_i2cmem->write_sda(BIT(data | ~direction, 3));
	m_i2cmem->write_scl(BIT(data | ~direction, 4));
}

void superxavix_state::xavix2002(machine_config &config)
{
	xavix(config);

	XAVIX2002(config.replace(), m_maincpu, MAIN_CLOCK * 2);
	set_xavix_cpumaps(config);
	m_maincpu->set_addrmap(5, &superxavix_state::superxavix_lowbus_map); // has extra video, io etc.

	m_palette->set_entries(512);

	m_screen->set_size(64*8, 32*8);
	m_screen->set_visarea(0*8, 64*8-1, 2*8, 30*8-1);

	XAVIX2002IO(config, m_xavix2002io, 0);

	m_xavix2002io->read_0_callback().set(FUNC(superxavix_state::superxavix_read_extended_io0));
	m_xavix2002io->write_0_callback().set(FUNC(superxavix_state::superxavix_write_extended_io0));
	m_xavix2002io->read_1_callback().set(FUNC(superxavix_state::superxavix_read_extended_io1));
	m_xavix2002io->write_1_callback().set(FUNC(superxavix_state::superxavix_write_extended_io1));
	m_xavix2002io->read_2_callback().set(FUNC(superxavix_state::superxavix_read_extended_io2));
	m_xavix2002io->write_2_callback().set(FUNC(superxavix_state::superxavix_write_extended_io2));
}

void superxavix_state::xavix2002_4mb(machine_config &config)
{
	xavix2002(config);
	m_maincpu->set_addrmap(6, &superxavix_state::xavix_4mb_extbus_map);
}

void superxavix_i2c_jmat_state::superxavix_i2c_jmat(machine_config &config)
{
	superxavix_i2c_24c08(config);

	m_xavix2002io->read_0_callback().set(FUNC(superxavix_i2c_jmat_state::read_extended_io0));
	m_xavix2002io->write_0_callback().set(FUNC(superxavix_i2c_jmat_state::write_extended_io0));
	m_xavix2002io->read_1_callback().set(FUNC(superxavix_i2c_jmat_state::read_extended_io1));
	m_xavix2002io->write_1_callback().set(FUNC(superxavix_i2c_jmat_state::write_extended_io1));
	m_xavix2002io->read_2_callback().set(FUNC(superxavix_i2c_jmat_state::read_extended_io2));
	m_xavix2002io->write_2_callback().set(FUNC(superxavix_i2c_jmat_state::write_extended_io2));
}

void superxavix_i2c_jmat_state::superxavix_i2c_jmat_24c64(machine_config& config)
{
	superxavix_i2c_24c64(config);

	m_xavix2002io->read_0_callback().set(FUNC(superxavix_i2c_jmat_state::read_extended_io0));
	m_xavix2002io->write_0_callback().set(FUNC(superxavix_i2c_jmat_state::write_extended_io0));
	m_xavix2002io->read_1_callback().set(FUNC(superxavix_i2c_jmat_state::read_extended_io1));
	m_xavix2002io->write_1_callback().set(FUNC(superxavix_i2c_jmat_state::write_extended_io1));
	m_xavix2002io->read_2_callback().set(FUNC(superxavix_i2c_jmat_state::read_extended_io2));
	m_xavix2002io->write_2_callback().set(FUNC(superxavix_i2c_jmat_state::write_extended_io2));

}

DEVICE_IMAGE_LOAD_MEMBER(superxavix_super_tv_pc_state::cart_load)
{
	u64 length;
	memory_region *cart_region = nullptr;

	if (m_cart->loaded_through_softlist())
	{
		cart_region = m_cart->memregion("prg");
		if (!cart_region)
			return std::make_pair(image_error::BADSOFTWARE, "Software list item is missing 'prg' region");
		length = cart_region->bytes();
	}
	else
	{
		length = m_cart->length();
	}

	if (!length)
		return std::make_pair(image_error::INVALIDLENGTH, "Cartridges must not be empty");
	else if (length > 0x40'0000)
		return std::make_pair(image_error::INVALIDLENGTH, "Cartridges must be no larger than 4 MiB (0x400000 bytes)");
	else if (length & (length - 1))
		return std::make_pair(image_error::INVALIDLENGTH, "Cartridges size must be a power of two"); // to simplify copying into BIOS region

	if (!m_cart->loaded_through_softlist())
	{
		cart_region = machine().memory().region_alloc(m_cart->subtag("prg"), length, 1, ENDIANNESS_LITTLE);
		if (!cart_region)
			return std::make_pair(std::errc::not_enough_memory, std::string());

		if (m_cart->fread(cart_region->base(), length) != length)
			return std::make_pair(std::errc::io_error, "Error reading cartridge file");
	}

	// driver requires ROM code to be in a memory region, so need to copy (can't install in address space)
	memory_region *const bios_region = memregion("bios");
	for (offs_t base = 0; base < 0x40'0000; base += length)
		memcpy(bios_region->base() + base, cart_region->base(), length);

	return std::make_pair(std::error_condition(), std::string());
}

void superxavix_super_tv_pc_state::xavix_extbus_map(address_map &map)
{
	map(0x000000, 0x7fffff).rom().region("bios", 0x000000);
	map(0x500000, 0x5fffff).bankr("rombank"); // needed for suprtvpchk and suprtvpcdo to read bitmaps for loading screen and desktop
	map(0x600000, 0x67ffff).ram().share("bitmap_buffer"); // reads/writes here
}

void superxavix_super_tv_pc_state::machine_reset()
{
	superxavix_state::machine_reset();

	m_rombank->configure_entry(0, memregion("bios")->base() + 0x500000);
	m_rombank->configure_entry(1, memregion("bios")->base() + 0x700000);
	m_rombank->set_entry(0);
}


void superxavix_super_tv_pc_state::superxavix_super_tv_pc(machine_config& config)
{
	xavix2002(config);

	m_xavix2002io->read_0_callback().set(FUNC(superxavix_super_tv_pc_state::read_extended_io0));
	m_xavix2002io->write_0_callback().set(FUNC(superxavix_super_tv_pc_state::write_extended_io0));
	m_xavix2002io->read_1_callback().set(FUNC(superxavix_super_tv_pc_state::read_extended_io1));
	m_xavix2002io->write_1_callback().set(FUNC(superxavix_super_tv_pc_state::write_extended_io1));
	m_xavix2002io->read_2_callback().set(FUNC(superxavix_super_tv_pc_state::read_extended_io2));
	m_xavix2002io->write_2_callback().set(FUNC(superxavix_super_tv_pc_state::write_extended_io2));

	m_anport->read_0_callback().set(FUNC(superxavix_super_tv_pc_state::stvpc_anport0_r));
	m_anport->read_1_callback().set(FUNC(superxavix_super_tv_pc_state::stvpc_anport1_r));

	GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "super_tv_pc_cart");
	m_cart->set_width(GENERIC_ROM8_WIDTH);
	m_cart->set_device_load(FUNC(superxavix_super_tv_pc_state::cart_load));

	SOFTWARE_LIST(config, "cart_list").set_original("super_tv_pc_cart");
}

void superxavix_piano_pc_state::superxavix_piano_pc(machine_config &config)
{
	xavix2002(config);

	m_anport->read_0_callback().set(FUNC(superxavix_piano_pc_state::piano_pc_anport0_r));
	m_anport->read_1_callback().set(FUNC(superxavix_piano_pc_state::piano_pc_anport1_r));

	m_xavix2002io->read_0_callback().set(FUNC(superxavix_piano_pc_state::read_extended_io0));
	m_xavix2002io->read_1_callback().set(FUNC(superxavix_piano_pc_state::read_extended_io1));
	m_xavix2002io->read_2_callback().set(FUNC(superxavix_piano_pc_state::read_extended_io2));
}


void superxavix_doradraw_state::xavix_extbus_map(address_map &map)
{
	map(0x000000, 0x7fffff).rom().region("bios", 0x000000);
	map(0x400000, 0x4fffff).ram().share("bitmap_buffer"); // reads/writes here
	map(0x600000, 0x6fffff).ram().share("bitmap_buffer2"); // reads/writes here
}


void superxavix_doradraw_state::superxavix_doradraw(machine_config& config)
{
	xavix2002(config);
}

void superxavix_i2c_state::superxavix_i2c_24c64(machine_config &config)
{
	xavix2002(config);

	I2C_24C64(config, "i2cmem", 0);
}

void superxavix_i2c_state::superxavix_i2c_24c16(machine_config &config)
{
	xavix2002(config);

	I2C_24C16(config, "i2cmem", 0);
}

void superxavix_i2c_state::superxavix_i2c_24c08(machine_config &config)
{
	xavix2002(config);

	I2C_24C08(config, "i2cmem", 0);
}

void superxavix_i2c_state::superxavix_i2c_24c04(machine_config &config)
{
	xavix2002(config);

	I2C_24C04(config, "i2cmem", 0);
}

void superxavix_i2c_state::superxavix_i2c_24c04_4mb(machine_config &config)
{
	superxavix_i2c_24c04(config);
	m_maincpu->set_addrmap(6, &superxavix_i2c_state::xavix_4mb_extbus_map);
}

void superxavix_i2c_state::superxavix_i2c_24c02(machine_config &config)
{
	xavix2002(config);

	I2C_24C02(config, "i2cmem", 0);
}

void superxavix_i2c_state::superxavix_i2c_24c02_4mb(machine_config &config)
{
	superxavix_i2c_24c02(config);
	m_maincpu->set_addrmap(6, &superxavix_i2c_state::xavix_4mb_extbus_map);
}



void superxavix_i2c_state::superxavix_i2c_mrangbat(machine_config &config)
{
	xavix2002(config);

	m_maincpu->set_addrmap(6, &superxavix_i2c_state::xavix_4mb_extbus_map);

	I2C_24C02(config, "i2cmem", 0); // 24C02?

	m_xavix2002io->read_0_callback().set_ioport("EX0");
	m_xavix2002io->read_1_callback().set_ioport("EX1");
	m_xavix2002io->read_2_callback().set_ioport("EX2");
}

// TODO, this hookup doesn't work, is this really the SEEPROM or the RTC?
uint8_t superxavix_i2c_ndpmj_state::read_extended_io1(offs_t offset, uint8_t mem_mask)
{
	logerror("%s: read_extended_io1 (SEEPROM?) mask %02x\n", machine().describe_context(), mem_mask);
	uint8_t ret = 0x00;

	if (!(mem_mask & 0x80))
		ret |= m_i2cmem->read_sda() << 7;

	return ret;
}

void superxavix_i2c_ndpmj_state::write_extended_io1(offs_t offset, uint8_t data, uint8_t mem_mask)
{
	logerror("%s: write_extended_io1 (SEEPROM?) mask %02x data %02x\n", machine().describe_context(), mem_mask, data);

	m_i2cmem->write_sda(BIT(data | ~mem_mask, 7));
	m_i2cmem->write_scl(BIT(data | ~mem_mask, 6));
}

void superxavix_i2c_ndpmj_state::superxavix_i2c_ndpmj(machine_config &config)
{
	superxavix_i2c_24c16(config);

	// S35390A at u6

	m_xavix2002io->read_0_callback().set(FUNC(superxavix_i2c_ndpmj_state::superxavix_read_extended_io0));
	m_xavix2002io->write_0_callback().set(FUNC(superxavix_i2c_ndpmj_state::superxavix_write_extended_io0));
	m_xavix2002io->read_1_callback().set(FUNC(superxavix_i2c_ndpmj_state::read_extended_io1));
	m_xavix2002io->write_1_callback().set(FUNC(superxavix_i2c_ndpmj_state::write_extended_io1));
	m_xavix2002io->read_2_callback().set(FUNC(superxavix_i2c_ndpmj_state::superxavix_read_extended_io2));
	m_xavix2002io->write_2_callback().set(FUNC(superxavix_i2c_ndpmj_state::superxavix_write_extended_io2));
}



// XaviXPORT
ROM_START( xavtenni )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "xavixtennis.bin", 0x000000, 0x800000, CRC(23a1d918) SHA1(2241c59e8ea8328013e55952ebf9060ea0a4675b) )
ROM_END

ROM_START( xavbaseb )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "xpbaseball.bin", 0x000000, 0x800000, CRC(e9ed692d) SHA1(537e390e972156dc7da66ee127ae4c8052038ee5) )
ROM_END

ROM_START( xavbowl )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "xpbowling.bin", 0x000000, 0x800000, CRC(2873460b) SHA1(ea8e2392f5a12961a23eb66dca8e07dec81ce8c8) )
ROM_END

ROM_START( xavbowlj )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "bowlingcard.u1", 0x000000, 0x800000, CRC(1b83b04f) SHA1(3609d71a7d92629487c2a32003a1259e74dabf0a) )
ROM_END

ROM_START( xavbassf )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "xpbassfishing.bin", 0x000000, 0x800000, CRC(09ab2f29) SHA1(616254176315d0947002e9ae5a6371a3ffa2e8eb) )

	// code for the nRF24E1s, stored in SEEPROMs.  One in the cartridge, one in the rod/reel
	ROM_REGION( 0x1001, "reel_io", ROMREGION_ERASE00 )
	ROM_LOAD( "xpbassfishingnrf24e1reel.bin", 0x0000, 0x1001, CRC(cfbb19ae) SHA1(32464e4e4be33fdbc7768311f93ce437a316c616) )

	ROM_REGION( 0x800000, "base_io", ROMREGION_ERASE00 )
	ROM_LOAD( "xpbassfishingnrf24e1cart.bin", 0x0000, 0x1001, CRC(62f6303e) SHA1(126b2663e252fb80948f53153e4046e63dd8be32) )
ROM_END

ROM_START( xavbox )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "xpboxing.bin", 0x000000, 0x800000, CRC(b61e7717) SHA1(162b9c53ac8c9d7b6972db44f7bc1cb0a7837b70) )
ROM_END

// Several of the XaviXport and DiS games are 2 glob setups (and must have some kind of banking)

ROM_START( xavjmat )
	ROM_REGION( 0x0800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "u2", 0x0000000, 0x0800000, CRC(1420640d) SHA1(dd714cd57cff885293688f74f69b5c1726e20ec0) )

	ROM_REGION( 0x0800000, "extra", ROMREGION_ERASE00 )
	ROM_LOAD( "u3", 0x0000000, 0x0800000, CRC(52dc318c) SHA1(dc50e0747ba29cfb1048fd4a55d26870086c869b) )

	ROM_REGION( 0x2000, "i2cmem", ROMREGION_ERASE00 ) // provide valid defaults
	ROM_LOAD( "i2cmem", 0x0000, 0x2000, CRC(03de8161) SHA1(f1c9b8e8016eb9da956b3af4466cc35ae8c60c50) )
ROM_END

ROM_START( xavaero )
	ROM_REGION( 0x0800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "aerostep.u2", 0x0000000, 0x0800000, CRC(7fce9cc1) SHA1(460bcef8a23d792941108e5da8c0d669a546b94c) )

	ROM_REGION( 0x0800000, "extra", ROMREGION_ERASE00 )
	ROM_LOAD( "aerostep.u3", 0x0000000, 0x0800000, CRC(ed9ca4ee) SHA1(4d90300880b02ac275e0cb502de16ae6f132aa2b) )

	ROM_REGION( 0x2000, "i2cmem", ROMREGION_ERASE00 ) // provide valid defaults
	ROM_LOAD( "i2cmem", 0x0000, 0x2000, CRC(03de8161) SHA1(f1c9b8e8016eb9da956b3af4466cc35ae8c60c50) )
ROM_END

ROM_START( xavmusic )
	ROM_REGION( 0x0800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "u2", 0x0000000, 0x0800000, CRC(e7c8ad59) SHA1(d47fac8b480de4db88a1b306ff8830a65d1738a3) )

	ROM_REGION( 0x0800000, "extra", ROMREGION_ERASE00 )
	ROM_LOAD( "u3", 0x0000000, 0x0800000, CRC(977c956f) SHA1(debc086d0cf6c391002ad163e7bfaa2f010cc8f5) )
ROM_END

ROM_START( xavcheck )
	ROM_REGION( 0x0800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "jmscj_1.u2", 0x0000000, 0x0800000, CRC(65c62d04) SHA1(eb3616576b5523b337b58b72514508bb484f9d41) )

	ROM_REGION( 0x0800000, "extra", ROMREGION_ERASE00 )
	ROM_LOAD( "jmscj_2.u3", 0x0000000, 0x0800000, CRC(c6ff7906) SHA1(0a5541eedea485aa86093e1bfcc996f503cc6dd7) )
ROM_END

ROM_START( xavpkjr )
	ROM_REGION( 0x0800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "tc58fvm6b5b.u2", 0x0000000, 0x0800000, CRC(82a32ede) SHA1(a555662b05925126ebc94ae6e4e13c1cfc3c86e0) )

	ROM_REGION( 0x0800000, "extra", ROMREGION_ERASE00 )
	ROM_LOAD( "tc58fvm6b5b.u3", 0x0000000, 0x0800000, CRC(8b836b5e) SHA1(f922a99423751cadb328c9a5ad1979658fe20496) )

	ROM_REGION( 0x2000, "i2cmem", ROMREGION_ERASE00 )
	ROM_LOAD( "s-24cs64a.u1", 0x0000, 0x2000, CRC(3a7637f7) SHA1(df5b5903900ae8488a44e3449dd2757d1dc35bc2) )
ROM_END

// Domyos DiS (XaviX 2002 based titles)
ROM_START( domfitex )
	ROM_REGION( 0x0800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "u2", 0x0000000, 0x0800000,  CRC(841fe3cd) SHA1(8678b8a0c5198b24169a84dbe3ae979bb0838f23) )

	ROM_REGION( 0x0800000, "extra_u3", ROMREGION_ERASE00 )
	ROM_LOAD( "u3", 0x0000000, 0x0800000, CRC(1dc844ea) SHA1(c23da9006227f7fe4982998c17759d403a47472a) )
ROM_END

ROM_START( domfitch )
	ROM_REGION( 0x0800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "u2", 0x0000000, 0x0800000, CRC(0ff2a7a6) SHA1(9b924cc4330e3f8d9204390854048fe2325bfdf7) )

	ROM_REGION( 0x0800000, "extra", ROMREGION_ERASE00 )
	ROM_LOAD( "u3", 0x0000000, 0x0800000, CRC(284583f6) SHA1(bd2d5304f1e01eed656b5de957ec0a0330a3d969) )
ROM_END

ROM_START( domdance )
	ROM_REGION( 0x0800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "u2", 0x0000000, 0x0800000, CRC(74f9499d) SHA1(a64235075e32567cd6d2ab7b1284efcb8e7538e2) )

	ROM_REGION( 0x0800000, "extra", ROMREGION_ERASE00 )
	ROM_LOAD( "u3", 0x0000000, 0x0800000, CRC(e437565c) SHA1(f6db219ea14404b698ca453f6e50c726b2e77abb) )
ROM_END

ROM_START( domstepc )
	ROM_REGION( 0x0800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "u2", 0x0000000, 0x0800000, CRC(cb37b5e9) SHA1(b742e3db98f36720adf5af9096c6bc235279de12) )

	ROM_REGION( 0x0800000, "extra", ROMREGION_ERASE00 )
	ROM_LOAD( "u3", 0x0000000, 0x0800000, CRC(dadaa744) SHA1(fd7ca77232a8fe228fc93b0a8a47ba3260349d90) )
ROM_END

ROM_START( anpanmdx )
	ROM_REGION( 0x0800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "apmj.u3", 0x0000000, 0x0800000, CRC(41348086) SHA1(63bbf6128901c1518f537766a40e162b2616d00c) )

	ROM_REGION( 0x0800000, "extra", ROMREGION_ERASE00 )
	ROM_LOAD( "am2j.u7", 0x0000000, 0x0800000, CRC(ff653a6b) SHA1(ece11198a06f9cddfae7f8c7e038675010869723) )
ROM_END

ROM_START( apmj2009 )
	ROM_REGION( 0x0800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "apmj.u3", 0x0000000, 0x0800000, CRC(5fab9492) SHA1(aa588e5333bdf81daf3b5868e00783d76a42e80e) )
ROM_END

ROM_START( mrangbat )
	ROM_REGION(0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("powerrangerspad.bin", 0x000000, 0x400000, CRC(d3a98775) SHA1(485c66242dd0ee436a278d23005aece48d606431) )
ROM_END

ROM_START( tmy_thom )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "thomastank.bin", 0x000000, 0x800000, CRC(a52a23be) SHA1(e5b3500239d9e56eb5405f7585982959e5a162da) )
ROM_END

ROM_START( ban_kksj )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "kksj.u1", 0x000000, 0x800000, CRC(8071dc36) SHA1(46f41d4185a115b27c685d1eabcd554b3c5a64b7) )
ROM_END

ROM_START( tmy_rkmj )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "rkmj.u1", 0x000000, 0x800000, CRC(80e70625) SHA1(500e287671a0822b736ed05704090d90187602ac) )
ROM_END

ROM_START( ban_ordj )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "ordj.u2", 0x000000, 0x800000, CRC(78fbb00f) SHA1(797b5495e292c36c003300ed18547e5643056149) )
ROM_END

ROM_START( ban_um2j )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "um2j.u1", 0x000000, 0x800000, CRC(88ea8e50) SHA1(b7507985a05335b6c07584bbb9ec2bd7f65ffe19) )
ROM_END

ROM_START( ban_dn1j )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "dn1j.u2", 0x000000, 0x800000, CRC(0a0cef0f) SHA1(c83a2635a969b3c686dbc599a37f8b7496b0c6a1) )
ROM_END

ROM_START( epo_kabj )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00 )
	ROM_LOAD( "kabj.u2", 0x000000, 0x800000, CRC(264655ec) SHA1(11a25c7b7e3ffd3c4bc0476c64d5d68754757f40) )
ROM_END

ROM_START( epo_tfit )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("tennisfitness.bin", 0x000000, 0x400000, CRC(cbf65bd2) SHA1(30b3da6f061b2dd91679db42a050f715901beb87) )
ROM_END

ROM_START( maxheart )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("mgrj.u2", 0x000000, 0x400000, CRC(447c25e6) SHA1(9cc65088512218f43d66b332de7a862d95c1c353) )
ROM_END

ROM_START( epo_doka )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("doka.u1", 0x000000, 0x400000, CRC(853266d2) SHA1(d4121b89ee464088951898282404e5a2b788dd69) )
ROM_END

ROM_START( ban_utmj )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("utmj.u7", 0x000000, 0x800000, CRC(0ac2bcd9) SHA1(ca7c82e2015c86bb37bd66016c33343d174e9965) )

	// SEEPROM is HT24LC02 at u3
ROM_END

ROM_START( ban_ult )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("ultraman.u1", 0x000000, 0x800000,CRC(bc2a94fb) SHA1(4dc81089ac2afc1c9496a49ffd778213bb4a12bd) )
ROM_END

ROM_START( ban_gkrj )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("gkrj.u2", 0x000000, 0x400000, CRC(d9ffe41a) SHA1(18583e1b5d9eb89e0364bd84b14f89bbe9640b19) )
ROM_END

ROM_START( ban_pr2j )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("pr2j.u2", 0x000000, 0x800000, CRC(e46bf811) SHA1(4b88a7a7001e99be526e889d0b81a43be0b1e464)  )
ROM_END


ROM_START( ban_bkgj )
	ROM_REGION( 0x400000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("bkgj.u2", 0x000000, 0x400000, CRC(a59ce23c) SHA1(d2a6be9e46f3cfc3cf798bf1f76732eee909c93b) )

	// SEEPROM is a S-24CS04A at u4
ROM_END

ROM_START( epo_rgfj )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00)
	// gave consistent reads 5 times, then started not, should be good, but there is the potential for the ROM to have already been failing
	ROM_LOAD("rgfj.u1", 0x000000, 0x800000, CRC(96c9563a) SHA1(36b9dd3e5dcc8099787b25d28143997f61273234) )
ROM_END

ROM_START( udance )
	ROM_REGION(0x800000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("udancerom0.bin", 0x000000, 0x800000, CRC(3066580a) SHA1(545257c75a892894faf386f4ab9a31967cdbe8ae) )

	ROM_REGION(0x800000, "biosx", ROMREGION_ERASE00)
	ROM_LOAD("udancerom1.bin", 0x000000, 0x800000, CRC(7dbaabde) SHA1(38c523dcdf8185465fc550fb9b0e8c7909f839be) )
ROM_END

ROM_START( suprtvpc )
	ROM_REGION(0x800000, "bios", ROMREGION_ERASE00) // inverted line?
	ROM_LOAD("supertvpc_dogs.u4", 0x200000, 0x200000, CRC(ab326e6d) SHA1(e22205f6ff4c8cc46538d78e27535be63acea42a) )
	ROM_CONTINUE(0x000000, 0x200000)
	ROM_CONTINUE(0x600000, 0x200000)
	ROM_CONTINUE(0x400000, 0x200000)
ROM_END

ROM_START( suprtvpchk )
	ROM_REGION(0x800000, "bios", ROMREGION_ERASE00) // inverted line?
	ROM_LOAD("superpctv.bin", 0x200000, 0x200000, CRC(4a55a81c) SHA1(178b4b595a3aefc6d1c176031b436fc3312009e7) )
	ROM_CONTINUE(0x000000, 0x200000)
	ROM_CONTINUE(0x600000, 0x200000)
	ROM_CONTINUE(0x400000, 0x200000)
ROM_END

ROM_START( suprtvpcdo )
	ROM_REGION(0x800000, "bios", ROMREGION_ERASE00) // inverted line?
	ROM_LOAD("supertvpc_doreamon.u4", 0x200000, 0x200000, CRC(8e7039dc) SHA1(44ffecc8195614e56c289a028c2140c24ad74171) )
	ROM_CONTINUE(0x000000, 0x200000)
	ROM_CONTINUE(0x600000, 0x200000)
	ROM_CONTINUE(0x400000, 0x200000)
ROM_END

ROM_START( suprtvpcln ) // from the 'Super TV-PC Link' branded unit which was bundled with a link card, updated copyright date compared to suprtvpc set at least
	ROM_REGION(0x800000, "bios", ROMREGION_ERASE00) // inverted line?
	ROM_LOAD("stvpc.u3", 0x200000, 0x200000, CRC(55d872ca) SHA1(7d0b4b527e95f65df5d70dcc303c81c7d893c638) )
	ROM_CONTINUE(0x000000, 0x200000)
	ROM_CONTINUE(0x600000, 0x200000)
	ROM_CONTINUE(0x400000, 0x200000)
ROM_END

ROM_START( epo_ntpj )
	ROM_REGION(0x800000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("ntpj.u6", 0x000000, 0x800000, CRC(6ce02166) SHA1(21c2ed48014e66123bb9968648984f82de361e2a) )

	// uses IS24C64 EEPROM

	// there is extra hardware for the Piano side of things which may or may not have ROM data in it
ROM_END

ROM_START( doradraw )
	ROM_REGION(0x800000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("dmdj.u2", 0x000000, 0x800000, CRC(b3ca50ab) SHA1(9e6d28c1e170d3556e3c4ddcefb4cb51fd100df5) )

	ROM_REGION(0x200000, "data", ROMREGION_ERASE00) // banked or extended video bus?
	ROM_LOAD("dmdj.u7", 0x000000, 0x200000, CRC(0e6392f9) SHA1(30fa3d3451b37d663e124c7d1d52c7e30284d2fb) )
ROM_END

ROM_START( ndpbj )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("ndpbj.u2", 0x000000, 0x800000, CRC(80cb5cbb) SHA1(cd424c4fbea8e9e47d165c4c8be52755fc7c2d98) )

	ROM_REGION( 0x400, "i2cmem", ROMREGION_ERASE00)
	ROM_LOAD("s-24cs08a.u6", 0x000, 0x400, CRC(a22db408) SHA1(f8d925c75054a961930af12869e3002bb9c4600b) )
ROM_END

ROM_START( ndpmj )
	ROM_REGION( 0x800000, "bios", ROMREGION_ERASE00)
	ROM_LOAD("ndpmj.u3", 0x000000, 0x800000, CRC(a8132d93) SHA1(2bcbf497e3fa7c7f44569f337cb8babff80c9338) )

	ROM_REGION( 0x800, "i2cmem", ROMREGION_ERASE00)
	ROM_LOAD("s-24cs16a.u5", 0x000, 0x800, CRC(5d2dd322) SHA1(c0af08a382c4dbefe290fef34cfd8345e904bd74) )
ROM_END


void superxavix_super_tv_pc_state::init_stvpc()
{
	init_xavix();
	m_disable_memory_bypass = true;
}

void superxavix_i2c_jmat_state::init_xavmusic()
{
	init_xavix();
	// is sprite yflip broken on (some?) revisions of SuperXaviX hardware, or is there a CPU bug causing this
	m_disable_sprite_yflip = true;
	m_allow_superxavix_extra_rom_sprites = false;
}

void superxavix_piano_pc_state::init_piano_pc()
{
	init_xavix();
	m_disable_memory_bypass = true;
}

void superxavix_state::init_epo_doka()
{
	init_xavix();
	m_disable_tile_regs_flip = true;
}

void superxavix_doradraw_state::init_doradraw()
{
	init_xavix();
	m_disable_memory_bypass = true;
}


CONS( 2004, xavtenni, 0,       0, superxavix_i2c_24c04, xavix_i2c,  superxavix_i2c_state,      init_xavix, "SSD Company LTD",         "XaviX Tennis (XaviXPORT)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 2004, xavbaseb, 0,       0, superxavix_i2c_24c08, xavix_i2c,  superxavix_i2c_state,      init_xavix, "SSD Company LTD",         "XaviX Baseball (XaviXPORT)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 2004, xavbowl,  0,       0, superxavix_i2c_24c04, xavix_bowl, superxavix_i2c_bowl_state, init_xavix, "SSD Company LTD",         "XaviX Bowling (XaviXPORT)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // has IR 'Camera'
CONS( 2005, xavbowlj, xavbowl, 0, superxavix_i2c_24c04, xavix_bowl, superxavix_i2c_bowl_state, init_xavix, "SSD Company LTD",         "XaviX Bowling (XaviXPORT, PT2-BWL-11, Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // ^^
CONS( 2004, xavbox,   0,       0, superxavix_i2c_jmat,  xavix,      superxavix_i2c_jmat_state, init_xavix, "SSD Company LTD",         "XaviX Boxing (XaviXPORT)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // has IR 'Camera'
// Bass Fishing PCB is just like Tennis except with an RF daughterboard.
CONS( 2004, xavbassf, 0,       0, superxavix_i2c_24c08, xavix_i2c,  superxavix_i2c_state,      init_xavix, "SSD Company LTD",         "XaviX Bass Fishing (XaviXPORT)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// TODO: check SEEPROM type and hookup, banking!
CONS( 2005, xavjmat,  0,       0, superxavix_i2c_jmat_24c64,  xavix_jmat, superxavix_i2c_jmat_state, init_xavmusic, "SSD Company LTD",         "Jackie Chan J-Mat Fitness (XaviXPORT)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 2005, xavaero,  xavjmat, 0, superxavix_i2c_jmat_24c64,  xavix_jmat, superxavix_i2c_jmat_state, init_xavmusic, "SSD Company LTD",         "XaviX Aerostep (XaviXPORT, Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 2007, xavmusic, 0,       0, superxavix_i2c_jmat_24c64,  xavix,      superxavix_i2c_jmat_state, init_xavmusic, "SSD Company LTD",         "XaviX Music & Circuit (XaviXPORT)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 2006, xavcheck, 0,       0, superxavix_i2c_jmat_24c64,  xavix_jmat, superxavix_i2c_jmat_state, init_xavmusic, "SSD Company LTD",         "XaviX Aero Check & Balance (XaviXPORT, Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
// xavpkjr has U6 = S-35390A REAL-TIME CLOCK
CONS( 2007, xavpkjr,  0, 0, superxavix_i2c_jmat_24c64, xavix, superxavix_i2c_jmat_state,      init_xavix,    "SSD Company LTD",         "PowerKIDS Jr. (XaviXPORT, Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// https://arnaudmeyer.wordpress.com/domyos-interactive-system/
// Domyos Fitness Adventure
// Domyos Fitness Challenge
// Domyos Fitness Exercises
// Domyos Fit Race
// Domyos Soft Fitness
// Domyos Fitness Dance
// Domyos Fitness Play
// Domyos Fitness Training

// Domyos Bike Concept (not listed on site above)

// Has SEEPROM and an RTC.  Exercise has some leftover PC buffer stuff.  (TODO, check SEEPROM type, RTC type, banking) (both Exercises and Challenge are identical PCBs)
CONS( 2008, domfitex, 0, 0, superxavix_i2c_jmat_24c64, xavixp, superxavix_i2c_jmat_state, init_xavmusic, "Decathlon / SSD Company LTD", "Domyos Fitness Exercises (Domyos Interactive System)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 2008, domfitch, 0, 0, superxavix_i2c_jmat_24c64, xavixp, superxavix_i2c_jmat_state, init_xavmusic, "Decathlon / SSD Company LTD", "Domyos Fitness Challenge (Domyos Interactive System)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 2007, domdance, 0, 0, superxavix_i2c_jmat_24c64, xavixp, superxavix_i2c_jmat_state, init_xavmusic, "Decathlon / SSD Company LTD", "Domyos Fitness Dance (Domyos Interactive System)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 2007, domstepc, 0, 0, superxavix_i2c_jmat_24c64, xavixp, superxavix_i2c_jmat_state, init_xavmusic, "Decathlon / SSD Company LTD", "Domyos Step Concept (Domyos Interactive System)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// some DIS games run on XaviX 2 instead, see xavix2.cpp for Domyos Fitness Adventure and Domyos Bike Concept

// Let's!TVプレイ 魔法戦隊マジレンジャー マジマットでダンス＆バトル
CONS( 2005, mrangbat, 0, 0, superxavix_i2c_mrangbat, mrangbat,   superxavix_i2c_state, init_xavix, "Bandai / SSD Company LTD", "Let's! TV Play Mahou Taiketsu Magiranger - Magimat de Dance & Battle (Japan)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// エキサイトスポーツ　テニス×フィットネス
CONS( 2004, epo_tfit, 0, 0, superxavix_i2c_24c04_4mb,    epo_tfit,   superxavix_i2c_state, init_xavix, "Epoch / SSD Company LTD",  "Excite Sports Tennis x Fitness (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// 石川遼 エキサイトゴルフ
CONS( 2010, epo_rgfj, 0, 0, superxavix_i2c_24c08,        xavix_i2c,  superxavix_i2c_state, init_xavix, "Epoch / SSD Company LTD", "Ishikawa Ryou Excite Golf (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// Let's!TVプレイ ふたりはプリキュアMaxHeart マットでダンス MaxHeartにおどっちゃおう
CONS( 2004, maxheart, 0, 0, superxavix_i2c_24c04_4mb,    maxheart,   superxavix_i2c_state, init_xavix, "Bandai / SSD Company LTD",  "Let's! TV Play Futari wa PreCure MaxHeart Mat de Dance MaxHeart ni Odotchaou (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// どこでもドラえもん 日本旅行ゲームDX体感！どこドラグランプリ！
CONS( 2004, epo_doka, 0, 0, xavix2002_4mb,               xavix,      superxavix_state,     init_epo_doka, "Epoch / SSD Company LTD",  "Doko Demo Doraemon Nihon Ryokou Game DX Taikan! Doko Dora Grand Prix! (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// テレビであそぼう!まなぼう! カードでおままごと あいうえお図鑑
CONS( 2006, epo_kabj, 0, 0, xavix2002,                   xavix,      superxavix_state,     init_xavix, "Epoch / SSD Company LTD",   "Hello Kitty TV de Asobou! Manabou! Card de Omamagoto AIUEO Zukan (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// Let's!TVプレイ なりきり体感 ボウケンジャー 走れ！撃て！ミッションスタート！！
CONS( 2006, ban_bkgj, 0, 0, superxavix_i2c_24c04_4mb,xavix_i2c,  superxavix_i2c_state, init_xavix, "Bandai / SSD Company LTD",  "Let's! TV Play Narikiri Taikan Boukenger Hashire! Ute! Mission Start!! (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// Let's!TV プレイ 体感キャストオフ 仮面ライダーカブト クロックアップ＆ライダーキック
CONS( 2006, ban_utmj, 0, 0, superxavix_i2c_24c02,    xavix_i2c,  superxavix_i2c_state, init_xavix, "Bandai / SSD Company LTD",  "Let's! TV Play Taikan Cast Off - Kamen Rider Kabuto Clock Up & Rider Kick!! (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// Let's!TVプレイ なりきりファイト ウルトラマン 撃て！必殺光線！！
CONS( 2006, ban_ult, 0, 0, superxavix_i2c_24c02,    ban_ult,  superxavix_i2c_bowl_state, init_no_timer, "Bandai / SSD Company LTD",  "Let's! TV Play Narikiri Fight Ultraman - Ute! Hissatsu Kousen!! (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// Let's!TVプレイ 体感大怪獣バトル あやつれ!ウルトラ大怪獣!
CONS( 2007, ban_um2j, 0, 0, superxavix_i2c_24c04,    ban_gkr,  superxavix_i2c_bowl_state, init_no_timer, "Bandai / SSD Company LTD",   "Let's! TV Play Taikan Daikaijuu Battle: Ayatsure! Ultra Daikaijuu! (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// Let’s!TVプレイ ゲキワザ習得 ゲキレンジャー スーパーゲキレンジャーへの道
CONS( 2007, ban_gkrj, 0, 0, superxavix_i2c_24c02_4mb,    ban_gkr,  superxavix_i2c_bowl_state, init_no_timer, "Bandai / SSD Company LTD",  "Let's! TV Play Gekiwaza Shuutoku Gekiranger - Super Gekiranger e no Michi (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )

// Yes!プリキュア5 いっしょに変身!メタモルフォーゼ!
CONS( 2007, ban_pr2j, 0, 0, superxavix_i2c_24c04,    ban_gkr,  superxavix_i2c_bowl_state, init_no_timer, "Bandai / SSD Company LTD",   "Yes! PreCure 5: Issho to Henshin! Metamorphose! (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// それいけトーマス ソドー島のなかまたち
CONS( 2005, tmy_thom, 0, 0, superxavix_i2c_24c04,    xavix_i2c,  superxavix_i2c_state, init_xavix, "Tomy / SSD Company LTD",   "Soreike Thomas - Sodor Tou no Nakamatachi / Thomas & Friends on the Island of Sodor (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// Ｌｅｔ’ｓ！ ＴＶプレイ 体感体得 結界師 方囲！定礎！結！滅！
CONS( 2007, ban_kksj, 0, 0, superxavix_i2c_24c02,    xavix_i2c,  superxavix_i2c_state, init_xavix, "Bandai / SSD Company LTD",   "Let's! TV Play Taikan Taitoku Kekkaishi: Houi! Jouso! Ketsu! Metsu! (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// 流星のロックマン 電波変換！オン・エア！
CONS( 2007, tmy_rkmj, 0, 0, superxavix_i2c_24c04,    xavix_i2c,  superxavix_i2c_state, init_xavix, "Takara Tomy / Capcom / SSD Company LTD",   "Ryuusei no Rockman: Denpa Henkan! On Air! (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// Let's!TVプレイ 音撃バトル！仮面ライダー響鬼 決めろ！一気火勢の型
CONS( 2005, ban_ordj, 0, 0, superxavix_i2c_24c04,    ban_ordj,   superxavix_i2c_state, init_xavix, "Bandai / SSD Company LTD",   "Let's! TV Play Ongeki Battle! Kamen Rider Hibiki: Kimero! Ikki Kasei no Kata (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// ディズニーキャラクターズ オト！イロ！トン・トン！ミラクルパレード
CONS( 2007, ban_dn1j, 0, 0, superxavix_i2c_24c04,    ban_dn1j,   superxavix_i2c_state, init_xavix, "Bandai / SSD Company LTD",   "Let's! TV Play Disney Characters Oto! Iro! Ton-Ton! Miracle Parade (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// アンパンマン かぞくで!育脳マットDX
CONS( 2011, anpanmdx, 0, 0, superxavix_i2c_24c08,    anpanmdx,   superxavix_i2c_state, init_xavix, "JoyPalette / SSD Company LTD",   "Anpanman Kazoku de! Ikunou Mat DX (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// アンパンマン ぴょんぴょん育脳マット
CONS( 2009, apmj2009, 0, 0, superxavix_i2c_24c16,    xavix_i2c,  superxavix_i2c_state, init_xavix, "JoyPalette / SSD Company LTD",   "Anpanman Pyon-Pyon Ikunou Mat (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// has HT24LC16
CONS( 2008, udance,   0, 0, xavix2002, xavix, superxavix_state, init_xavix, "Tiger / SSD Company LTD", "U-Dance", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// these have RAM in the usual ROM space & also have an Atmel 24LC64,
CONS( 2004, suprtvpc,    0,        0, superxavix_super_tv_pc,    suprtvpc,      superxavix_super_tv_pc_state, init_stvpc, "Epoch / SSD Company LTD", "Super TV-PC (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 2006, suprtvpchk,  suprtvpc, 0, superxavix_super_tv_pc,    suprtvpc,      superxavix_super_tv_pc_state, init_stvpc, "Epoch / SSD Company LTD", "Super TV-PC - Hello Kitty (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 2006, suprtvpcdo,  suprtvpc, 0, superxavix_super_tv_pc,    suprtvpc,      superxavix_super_tv_pc_state, init_stvpc, "Epoch / SSD Company LTD", "Super TV-PC - Doraemon (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 2006, suprtvpcln,  suprtvpc, 0, superxavix_super_tv_pc,    suprtvpc,      superxavix_super_tv_pc_state, init_stvpc, "Epoch / SSD Company LTD", "Super TV-PC - Link (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// similar to Super TV-PC but with additional built in piano
CONS( 2008, epo_ntpj,  0, 0, superxavix_piano_pc, suprtvpc, superxavix_piano_pc_state, init_piano_pc, "Epoch / SSD Company LTD", "Hello Kitty Piano PC (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// ドラえもん うごく！おえかき エポック社
CONS( 2007, doradraw,  0, 0, superxavix_doradraw,    xavix,      superxavix_doradraw_state, init_doradraw, "Epoch / SSD Company LTD", "Doraemon Ugoku! Oekaki (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// どんトレだ兵衛～どん兵衛くんとトレーニング
// doesn't boot, has a camera with a large number of connections going to it, probably wants comms to work with it?
CONS( 2007, ndpbj, 0, 0, superxavix_i2c_24c08,    xavix_i2c,  superxavix_i2c_state, init_xavix, "Nissin / SSD Company LTD",   "Dontore da bei - Donbei-kun to Training (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// どんトレだ兵衛2～おうちで、みんなでフィットネス!～
CONS( 2008, ndpmj, 0, 0, superxavix_i2c_ndpmj,    xavix,  superxavix_i2c_ndpmj_state, init_xavix, "Nissin / SSD Company LTD",   "Dontore da bei 2 - Ouchi de, Minna de Fitness! (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )



xavix2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Haywood
/******************************************************************************

    XaviX 2

    RISC-like architecture, not 6502-derived like XaviX / SuperXaviX

    die is marked  "SSD 2002-2004 NEC 800208-51"

*******************************************************************************/

#include "emu.h"

#include "cpu/xavix2/xavix2.h"
#include "machine/i2cmem.h"

#include "emupal.h"
#include "screen.h"
#include "softlist.h"
#include "speaker.h"

#include <algorithm>


namespace {

class xavix2_state : public driver_device
{
public:
	xavix2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_screen(*this, "screen")
		, m_i2cmem(*this, "i2cmem")
		, m_pio(*this, "pio")
	{ }

	virtual void config(machine_config &config);

protected:
	enum {
		IRQ_TIMER =  7,
		IRQ_DMA   = 12
	};

	required_device<xavix2_device> m_maincpu;
	required_device<screen_device> m_screen;
	optional_device<i2cmem_device> m_i2cmem;
	required_ioport m_pio;

	u32 m_dma_src;
	u16 m_dma_dst;
	u16 m_dma_count;
	emu_timer *m_dma_timer;

	u32 m_pio_mode[2];
	u32 m_pio_dataw;
	u32 m_pio_mask_out;

	u16 m_gpu0_adr,  m_gpu0_count,  m_gpu1_adr,  m_gpu1_count;
	u16 m_gpu0b_adr, m_gpu0b_count, m_gpu1b_adr, m_gpu1b_count;

	u16 m_gpu_adr, m_gpu_descsize_adr, m_gpu_descdata_adr;
	u32 m_int_active;
	u32 m_int_enabled;
	u32 m_int_nmi;

	u16 m_bg_color;
	u32 m_palette[0x200];
	u32 m_sd[0x400][0x800];

	std::string m_debug_string;

	static u32 rgb555_888(u16 color);

	void irq_raise(u32 level);
	void irq_clear(u32 level);
	bool irq_state(u32 level) const;
	void irq_clear_w(u16 data);
	u16 irq_nmi_r();
	void irq_nmi_w(u16 data);
	u16 irq_enable_r();
	void irq_enable_w(u16 data);
	u8 irq_level_r();

	void bg_color_w(u16 data);
	u32 palette_r(offs_t reg);
	void palette_w(offs_t reg, u32 data);

	void gpu0_adr_w(u16 data);
	u16 gpu0b_adr_r();
	void gpu0b_adr_w(u16 data);
	void gpu0_count_w(u16 data);
	u16 gpu0b_count_r();
	void gpu0b_count_w(u16 data);
	void gpu0_trigger_w(u8 data);

	void gpu1_adr_w(u16 data);
	u16 gpu1b_adr_r();
	void gpu1b_adr_w(u16 data);
	void gpu1_count_w(u16 data);
	u16 gpu1b_count_r();
	void gpu1b_count_w(u16 data);
	void gpu1_trigger_w(u8 data);

	void gpu_update(u16 count, u16 adr);

	void gpu_descsize_w(u16 data);
	void gpu_descdata_w(u16 data);
	void gpu_adr_w(u16 data);
	void gpu_count_w(u16 data);

	void dma_src_w(offs_t, u32 data, u32 mem_mask);
	void dma_dst_w(offs_t, u16 data, u16 mem_mask);
	void dma_count_w(offs_t, u16 data, u16 mem_mask);
	void dma_control_w(u8 data);
	void dma_status_w(u8 data);
	u8 dma_status_r();

	TIMER_CALLBACK_MEMBER(dma_end);
	INTERRUPT_GEN_MEMBER(vblank_irq);

	void debug_port_w(u8 data);
	u8 debug_port_r();
	u8 debug_port_status_r();

	void pio_mode_w(offs_t offset, u32 data, u32 mem_mask);
	u32 pio_mode_r(offs_t offset);
	virtual void pio_update();
	void pio_w(offs_t offset, u32 data, u32 mem_mask);
	u32 pio_r();

	void crtc_w(offs_t reg, u16 data);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void mem(address_map &map) ATTR_COLD;
};

class naruto_state : public xavix2_state
{
public:
	using xavix2_state::xavix2_state;
	virtual void config(machine_config& config) override;

protected:
	virtual void pio_update() override;
};

class domyos_state : public xavix2_state
{
public:
	using xavix2_state::xavix2_state;
	virtual void config(machine_config& config) override;

protected:
	virtual void pio_update() override;
};

u32 xavix2_state::rgb555_888(u16 color)
{
	u8 r = (color >>  0) & 31;
	u8 g = (color >>  5) & 31;
	u8 b = (color >> 10) & 31;
	r = (r << 3) | (r >> 2);
	g = (g << 3) | (g >> 2);
	b = (b << 3) | (b >> 2);
	return (r << 16) | (g << 8) | b;
}

void xavix2_state::bg_color_w(u16 data)
{
	m_bg_color = data;
}

u32 xavix2_state::palette_r(offs_t reg)
{
	return m_palette[reg];
}

void xavix2_state::palette_w(offs_t reg, u32 data)
{
	m_palette[reg] = data & 0x8000 ? 0: rgb555_888(data) | 0xff000000;
}

void xavix2_state::irq_raise(u32 level)
{
	u32 line = 1 << level;
	if ((m_int_enabled | m_int_nmi) & line) {
		if(!m_int_active)
			m_maincpu->set_input_line(0, ASSERT_LINE);
		m_int_active |= line;
	}
}

void xavix2_state::irq_clear(u32 level)
{
	m_int_active &= ~(1 << level);
	if(!m_int_active)
		m_maincpu->set_input_line(0, CLEAR_LINE);
}

void xavix2_state::irq_clear_w(u16 data)
{
	m_int_active &= ~data;
	if(!m_int_active)
		m_maincpu->set_input_line(0, CLEAR_LINE);

}

u16 xavix2_state::irq_nmi_r()
{
	return m_int_nmi;
}

void xavix2_state::irq_nmi_w(u16 data)
{
	m_int_nmi = data;
}

u16 xavix2_state::irq_enable_r()
{
	return m_int_enabled;
}

void xavix2_state::irq_enable_w(u16 data)
{
	irq_clear(~data);
	m_int_enabled = data;
}

u8 xavix2_state::irq_level_r()
{
	for(u32 i=0; i<=12; i++)
		if(m_int_active & (1 << i))
			return i;
	return 0xff;
}

bool xavix2_state::irq_state(u32 level) const
{
	return m_int_active & (1 << level);
}

void xavix2_state::gpu0_adr_w(u16 data)
{
	m_gpu0_adr = data;
}

u16 xavix2_state::gpu0b_adr_r()
{
	return m_gpu0b_adr;
}

void xavix2_state::gpu0b_adr_w(u16 data)
{
	m_gpu0b_adr = data;
}

void xavix2_state::gpu0_count_w(u16 data)
{
	m_gpu0_count = data;
}

u16 xavix2_state::gpu0b_count_r()
{
	return m_gpu0b_count;
}

void xavix2_state::gpu0b_count_w(u16 data)
{
	m_gpu0b_count = data;
}

void xavix2_state::gpu0_trigger_w(u8 data)
{
	gpu_update(m_gpu0_count, m_gpu0_adr);
}

void xavix2_state::gpu1_adr_w(u16 data)
{
	m_gpu1_adr = data;
}

u16 xavix2_state::gpu1b_adr_r()
{
	return m_gpu1b_adr;
}

void xavix2_state::gpu1b_adr_w(u16 data)
{
	m_gpu1b_adr = data;
}

void xavix2_state::gpu1_count_w(u16 data)
{
	m_gpu1_count = data;
}

u16 xavix2_state::gpu1b_count_r()
{
	return m_gpu1b_count;
}

void xavix2_state::gpu1b_count_w(u16 data)
{
	m_gpu1b_count = data;
}

void xavix2_state::gpu1_trigger_w(u8 data)
{
	gpu_update(m_gpu1_count, m_gpu1_adr);
}

void xavix2_state::gpu_update(u16 count, u16 adr)
{
	std::unique_ptr<int []> list(new int[count]);
	for(u32 i=0; i != count; i++) {
		u64 command = m_maincpu->space(AS_PROGRAM).read_qword(adr + 8*i);
		list[i] = (command & 0x1fe00000) | i;
	}
	std::sort(list.get(), list.get() + count, std::greater<int>());
	for(u32 i=0; i != count; i++) {
		u64 command = m_maincpu->space(AS_PROGRAM).read_qword(adr + 8*(list[i] & 0xffff));
		logerror("gpu %02d: %016x x=%03x y=%03x ?=%02x ?=%x s=%02x w=%02x h=%02x c=%04x %s\n",
				 i, command,
				 (command >>  0) &  0x7ff,
				 (command >> 11) &  0x3ff,
				 (command >> 21) &   0xff,
				 (command >> 29) &    0x1,
				 (command >> 30) &   0x3f,
				 (command >> 36) &   0x3f,
				 (command >> 42) &   0x3f,
				 (command >> 48) & 0xffff,
				 machine().describe_context());
		u32 idx  = (command >> 30) & 0x3f;
		u32 idx2 = (command >> 58) & 0x3f;
		u32 descsize = m_maincpu->space(AS_PROGRAM).read_dword(m_gpu_descsize_adr + 4*idx);
		u16 descdata = m_maincpu->space(AS_PROGRAM).read_word(m_gpu_descdata_adr + 2*idx2);

		u32 sadr = (descdata << 14) + ((command >> 43) & 0x7fe0);
		u32 x = (command >>  0) &  0x7ff;
		u32 y = (command >> 11) &  0x3ff;
		u32 sx = 1+(descsize & 0xff);
		u32 sy = 1 + ((descsize >> 8) & 0xff);
		u32 scalex = (command >> 40) & 0x3; //only 1 and 2 seen as values for these two so far
		u32 scaley = (command >> 46) & 0x3;
		u32 bpp = 1 + ((descsize >> 24) & 7);
		logerror("gpu    - data %06x size %08x w=%x h=%x ?=%x bpp=%x pal=%x\n", sadr, descsize, sx, sy, (descsize >> 16) & 0xff, bpp, descsize >> 27);

		if(x+sx > 0x800)
			sx = 0x800 - x;
		if(y+sy > 0x400)
			sy = 0x400 - y;

		u32 avail = 0;
		u32 mask  = (1 << bpp) - 1;
		u32 palette = ((descsize >> 27) & 0x1f) << bpp;
		for(u32 yy=0; yy<sy; yy++) {
			u64 v = m_maincpu->space(AS_PROGRAM).read_qword(sadr);
			sadr += 8;
			avail = 64;
			for(u32 xx=0; xx<sx; xx++) {
				if (avail < bpp) {
					v = m_maincpu->space(AS_PROGRAM).read_qword(sadr);
					sadr += 8;
					avail = 64;
				}
				u32 color = m_palette[palette | (v & mask)];
				if(color) {
					for(u32 yyy = 0; yyy<scaley; yyy++) {
						for(u32 xxx = 0; xxx < scalex; xxx++) {
							m_sd[y+(yy*scaley)+yyy][x+(xx*scalex)+xxx] = color;
						}
					}
				}
				v >>= bpp;
				avail -= bpp;
			}
		}
	}
}

void xavix2_state::gpu_descsize_w(u16 data)
{
	m_gpu_descsize_adr = data;
	logerror("gpu descsize_w %04x\n", data);
}

void xavix2_state::gpu_descdata_w(u16 data)
{
	m_gpu_descdata_adr = data;
	logerror("gpu descdata_w %04x\n", data);
}

void xavix2_state::dma_src_w(offs_t, u32 data, u32 mem_mask)
{
	COMBINE_DATA(&m_dma_src);
}

void xavix2_state::dma_dst_w(offs_t, u16 data, u16 mem_mask)
{
	COMBINE_DATA(&m_dma_dst);
}

void xavix2_state::dma_count_w(offs_t, u16 data, u16 mem_mask)
{
	COMBINE_DATA(&m_dma_count);
}

void xavix2_state::dma_control_w(u8 data)
{
	if(data == 3 || data == 7) {
		logerror("DMA %s:%08x -> %04x (%04x) %s\n",
				 data == 3 ? "main" : "rom",
				 m_dma_src, m_dma_dst, m_dma_count,
				 machine().describe_context());
		u32 sadr = m_dma_src | (data == 3 ? 0x00000000 : 0x40000000);
		u32 dadr = m_dma_dst;
		auto &prg = m_maincpu->space(AS_PROGRAM);
		for(u32 i=0; i != m_dma_count; i++)
			prg.write_byte(dadr + i, prg.read_byte(sadr + i));
		m_dma_timer->adjust(attotime::from_ticks(m_dma_count, m_maincpu->clock()));
	}
}

void xavix2_state::dma_status_w(u8 data)
{
	if(data == 2)
		irq_clear(IRQ_DMA);
}

u8 xavix2_state::dma_status_r()
{
	return irq_state(IRQ_DMA) ? 6 : 0;
}

TIMER_CALLBACK_MEMBER(xavix2_state::dma_end)
{
	irq_raise(IRQ_DMA);
}

INTERRUPT_GEN_MEMBER(xavix2_state::vblank_irq)
{
	u32 color = rgb555_888(m_bg_color);
	for(u32 y=0; y != 0x400; y++)
		for(u32 x=0; x != 0x800; x++)
			m_sd[y][x] = color;
	irq_raise(IRQ_TIMER);
}

void xavix2_state::debug_port_w(u8 data)
{
	if(data) {
		if(data == 0xa) {
			logerror("debug [%s]\n", m_debug_string);
			m_debug_string = "";
		} else if(data != 0xd)
			m_debug_string += char(data);
	}
}

u8 xavix2_state::debug_port_r()
{
	return 0;
}

u8 xavix2_state::debug_port_status_r()
{
	// 0: ok to recieve
	// 1: ok to send
	return 1<<1;
}

void xavix2_state::pio_mode_w(offs_t offset, u32 data, u32 mem_mask)
{
	COMBINE_DATA(&m_pio_mode[offset]);
	//  logerror("%s: pio mode%d %08x %08x -> %08x\n", machine().describe_context(), offset, data, mem_mask, m_pio_mode[offset]);
	m_pio_mask_out = 0;
	for (u32 i=0; i<32; i++) {
		m_pio_mask_out |= (((m_pio_mode[i / 16] >> ((i % 16) * 2)) & 3) == 3) ? 1 << i : 0;
	}
	//  logerror("%s: pio mode in0 %08x, out %08x\n", machine().describe_context(), m_pio_mask_out);
	pio_update();
}

u32 xavix2_state::pio_mode_r(offs_t offset)
{
	return m_pio_mode[offset];
}

void xavix2_state::pio_update()
{
}

void naruto_state::pio_update()
{
	if (BIT(m_pio_mask_out, 21))
		m_i2cmem->write_sda(BIT(m_pio_dataw, 21));
	if (BIT(m_pio_mask_out, 20))
		m_i2cmem->write_scl(BIT(m_pio_dataw, 20));
}

void domyos_state::pio_update()
{
	if (BIT(m_pio_mask_out, 16))
		m_i2cmem->write_sda(BIT(m_pio_dataw, 16));
	if (BIT(m_pio_mask_out, 17))
		m_i2cmem->write_scl(BIT(m_pio_dataw, 17));
}

void xavix2_state::pio_w(offs_t offset, u32 data, u32 mem_mask)
{
	COMBINE_DATA(&m_pio_dataw);
	pio_update();
}

u32 xavix2_state::pio_r()
{
	return (m_pio->read() & ~m_pio_mask_out) | (m_pio_dataw & m_pio_mask_out);
}

/*
  NTSC? (63.55us)
[:] crtc[0] = 50034 (c372)
[:] crtc[1] = 463 (1cf)
[:] crtc[2] = 6760 (1a68)
[:] crtc[3] = 522 (20a)
[:] crtc[4] = 770 (302)
[:] crtc[5] = 525 (20d)
[:] crtc[6] = 9 (9)
[:] crtc[7] = 15 (f)
[:] crtc[8] = 3 (3)
[:] crtc[9] = 3 (3)
[:] crtc[10] = 3 (3)
[:] crtc[11] = 43 (2b)

  PAL? (64us)
[:] crtc[0] = 49924 (c304)
[:] crtc[1] = 457 (1c9)
[:] crtc[2] = 6758 (1a66)
[:] crtc[3] = 545 (221)
[:] crtc[4] = 765 (2fd)
[:] crtc[5] = 627 (273)
[:] crtc[6] = 8 (8)
[:] crtc[7] = 13 (d)
[:] crtc[8] = 3 (3)
[:] crtc[9] = 3 (3)
[:] crtc[10] = 3 (3)
[:] crtc[11] = 23 (17)
*/

void xavix2_state::crtc_w(offs_t reg, u16 data)
{
	logerror("crtc[%d] = %d (%x)\n", reg, (u32)data, data);
}


void xavix2_state::mem(address_map &map)
{
	map(0x00000000, 0x0000ffff).ram();
	map(0x00010000, 0x00ffffff).rom().region("maincpu", 0x010000);

	map(0x40000000, 0x40ffffff).rom().region("maincpu", 0);

	map(0xc0000000, 0xc00007ff).rw(FUNC(xavix2_state::palette_r), FUNC(xavix2_state::palette_w));
	map(0xc0000800, 0xc001ffff).ram();

	map(0xffffe000, 0xffffe003).w(FUNC(xavix2_state::dma_src_w));
	map(0xffffe004, 0xffffe005).w(FUNC(xavix2_state::dma_dst_w));
	map(0xffffe008, 0xffffe009).w(FUNC(xavix2_state::dma_count_w));
	map(0xffffe00c, 0xffffe00c).w(FUNC(xavix2_state::dma_control_w));
	map(0xffffe010, 0xffffe010).rw(FUNC(xavix2_state::dma_status_r), FUNC(xavix2_state::dma_status_w));

	map(0xffffe200, 0xffffe207).rw(FUNC(xavix2_state::pio_mode_r), FUNC(xavix2_state::pio_mode_w));
	map(0xffffe208, 0xffffe20b).rw(FUNC(xavix2_state::pio_r), FUNC(xavix2_state::pio_w));
	map(0xffffe238, 0xffffe238).rw(FUNC(xavix2_state::debug_port_r), FUNC(xavix2_state::debug_port_w));
	map(0xffffe239, 0xffffe239).r(FUNC(xavix2_state::debug_port_status_r));

	map(0xffffe400, 0xffffe401).w(FUNC(xavix2_state::gpu0_adr_w));
	map(0xffffe404, 0xffffe405).w(FUNC(xavix2_state::gpu0_count_w));
	map(0xffffe408, 0xffffe408).w(FUNC(xavix2_state::gpu0_trigger_w));

	map(0xffffe40c, 0xffffe40d).w(FUNC(xavix2_state::gpu1_adr_w));
	map(0xffffe410, 0xffffe411).w(FUNC(xavix2_state::gpu1_count_w));
	map(0xffffe414, 0xffffe414).w(FUNC(xavix2_state::gpu1_trigger_w));

	map(0xffffe600, 0xffffe601).rw(FUNC(xavix2_state::gpu0b_adr_r), FUNC(xavix2_state::gpu0b_adr_w));
	map(0xffffe602, 0xffffe603).rw(FUNC(xavix2_state::gpu0b_count_r), FUNC(xavix2_state::gpu0b_count_w));
	map(0xffffe604, 0xffffe605).rw(FUNC(xavix2_state::gpu1b_adr_r), FUNC(xavix2_state::gpu1b_adr_w));
	map(0xffffe606, 0xffffe607).rw(FUNC(xavix2_state::gpu1b_count_r), FUNC(xavix2_state::gpu1b_count_w));
	map(0xffffe608, 0xffffe609).w(FUNC(xavix2_state::gpu_descsize_w));
	map(0xffffe60a, 0xffffe60b).lr16(NAME([]() { return 0x240; })); // pal/ntsc
	map(0xffffe60e, 0xffffe60f).w(FUNC(xavix2_state::bg_color_w));

	map(0xffffe622, 0xffffe623).w(FUNC(xavix2_state::gpu_descdata_w));
	map(0xffffe630, 0xffffe631).lr16(NAME([]() { return 0x210; }));
	map(0xffffe632, 0xffffe633).lr16(NAME([]() { return 0x210; }));
	map(0xffffe634, 0xffffe64b).w(FUNC(xavix2_state::crtc_w));

	map(0xfffffc00, 0xfffffc00).r(FUNC(xavix2_state::irq_level_r));
	map(0xfffffc04, 0xfffffc05).w(FUNC(xavix2_state::irq_clear_w));
	map(0xfffffc08, 0xfffffc09).rw(FUNC(xavix2_state::irq_nmi_r), FUNC(xavix2_state::irq_nmi_w));
	map(0xfffffc0a, 0xfffffc0b).rw(FUNC(xavix2_state::irq_enable_r), FUNC(xavix2_state::irq_enable_w));
}

uint32_t xavix2_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if(machine().input().code_pressed_once(KEYCODE_8))
		irq_raise(8);
	if(machine().input().code_pressed_once(KEYCODE_0))
		irq_raise(10);

	constexpr int dx = 0x400 - 320;
	constexpr int dy = 0x200 - 200;

	for(int y=0; y < 400; y++)
		for(int x=0; x<640; x++)
			bitmap.pix(y, x) = m_sd[y+dy][x+dx];

	return 0;
}

void xavix2_state::machine_start()
{
	m_dma_timer = timer_alloc(FUNC(xavix2_state::dma_end), this);
}

void xavix2_state::machine_reset()
{
	m_dma_src = 0;
	m_dma_dst = 0;
	m_dma_count = 0;
	m_int_active = 0;
	m_bg_color = 0;
}

static INPUT_PORTS_START( naruto )
	PORT_START("pio")
	PORT_BIT(0x00000001, IP_ACTIVE_HIGH, IPT_BUTTON3)
	PORT_BIT(0x00000002, IP_ACTIVE_HIGH, IPT_BUTTON4)
	PORT_BIT(0x00000004, IP_ACTIVE_HIGH, IPT_BUTTON5)
	PORT_BIT(0x00000008, IP_ACTIVE_HIGH, IPT_BUTTON6)
	PORT_BIT(0x00000010, IP_ACTIVE_HIGH, IPT_BUTTON7)
	PORT_BIT(0x00000020, IP_ACTIVE_HIGH, IPT_BUTTON8)
	PORT_BIT(0x00000040, IP_ACTIVE_HIGH, IPT_BUTTON9)
	PORT_BIT(0x00000080, IP_ACTIVE_HIGH, IPT_BUTTON10)
	PORT_BIT(0x00000100, IP_ACTIVE_HIGH, IPT_BUTTON11)
	PORT_BIT(0x00000200, IP_ACTIVE_HIGH, IPT_BUTTON12)
	PORT_BIT(0x00000400, IP_ACTIVE_HIGH, IPT_BUTTON13)
	PORT_BIT(0x00000800, IP_ACTIVE_HIGH, IPT_BUTTON14)
	PORT_BIT(0x00001000, IP_ACTIVE_HIGH, IPT_BUTTON15)
	PORT_BIT(0x00002000, IP_ACTIVE_HIGH, IPT_BUTTON16)
	PORT_BIT(0x00004000, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_PLAYER(2)
	PORT_BIT(0x00008000, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_PLAYER(2)
	PORT_BIT(0x00010000, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("B/Execute")
	PORT_BIT(0x00020000, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("D/Cancel")
	PORT_BIT(0x00040000, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP)
	PORT_BIT(0x00080000, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN)
	PORT_BIT(0x00100000, IP_ACTIVE_HIGH, IPT_CUSTOM) // i2c clock
	PORT_BIT(0x00200000, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
	PORT_BIT(0x00400000, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_PLAYER(2)
	PORT_BIT(0x00800000, IP_ACTIVE_HIGH, IPT_BUTTON4) PORT_PLAYER(2)
	PORT_BIT(0x01000000, IP_ACTIVE_HIGH, IPT_BUTTON5) PORT_PLAYER(2)
	PORT_BIT(0x02000000, IP_ACTIVE_HIGH, IPT_BUTTON6) PORT_PLAYER(2)
	PORT_BIT(0x04000000, IP_ACTIVE_HIGH, IPT_BUTTON7) PORT_PLAYER(2)
	PORT_BIT(0x08000000, IP_ACTIVE_HIGH, IPT_BUTTON8) PORT_PLAYER(2)
	PORT_BIT(0x10000000, IP_ACTIVE_HIGH, IPT_BUTTON9) PORT_PLAYER(2)
	PORT_BIT(0x20000000, IP_ACTIVE_HIGH, IPT_BUTTON10) PORT_PLAYER(2)
	PORT_BIT(0x40000000, IP_ACTIVE_HIGH, IPT_BUTTON11) PORT_PLAYER(2)
	PORT_BIT(0x80000000, IP_ACTIVE_HIGH, IPT_BUTTON12) PORT_PLAYER(2)
INPUT_PORTS_END

static INPUT_PORTS_START(domyos)
	PORT_START("pio")
	PORT_BIT(0x00000001, IP_ACTIVE_HIGH, IPT_BUTTON1)
	PORT_BIT(0x00000002, IP_ACTIVE_HIGH, IPT_BUTTON2)
	PORT_BIT(0x00000004, IP_ACTIVE_HIGH, IPT_BUTTON3)
	PORT_BIT(0x00000008, IP_ACTIVE_HIGH, IPT_BUTTON4)
	PORT_BIT(0x00000010, IP_ACTIVE_HIGH, IPT_BUTTON5)
	PORT_BIT(0x00000020, IP_ACTIVE_HIGH, IPT_BUTTON6)
	PORT_BIT(0x00000040, IP_ACTIVE_HIGH, IPT_BUTTON7)
	PORT_BIT(0x00000080, IP_ACTIVE_HIGH, IPT_BUTTON8)
	PORT_BIT(0x00000100, IP_ACTIVE_HIGH, IPT_BUTTON9)
	PORT_BIT(0x00000200, IP_ACTIVE_HIGH, IPT_BUTTON10)
	PORT_BIT(0x00000400, IP_ACTIVE_HIGH, IPT_BUTTON11)
	PORT_BIT(0x00000800, IP_ACTIVE_HIGH, IPT_BUTTON12)
	PORT_BIT(0x00001000, IP_ACTIVE_HIGH, IPT_BUTTON13)
	PORT_BIT(0x00002000, IP_ACTIVE_HIGH, IPT_BUTTON14)
	PORT_BIT(0x00004000, IP_ACTIVE_HIGH, IPT_BUTTON15)
	PORT_BIT(0x00008000, IP_ACTIVE_HIGH, IPT_BUTTON16)
	PORT_BIT(0x00010000, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_READ_LINE_DEVICE_MEMBER("i2cmem", FUNC(i2cmem_device::read_sda))
	PORT_BIT(0x00020000, IP_ACTIVE_HIGH, IPT_CUSTOM) // i2c clock
	PORT_BIT(0x00040000, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_PLAYER(2)
	PORT_BIT(0x00080000, IP_ACTIVE_HIGH, IPT_BUTTON4) PORT_PLAYER(2)
	PORT_BIT(0x00100000, IP_ACTIVE_HIGH, IPT_BUTTON5) PORT_PLAYER(2)
	PORT_BIT(0x00200000, IP_ACTIVE_HIGH, IPT_BUTTON6) PORT_PLAYER(2)
	PORT_BIT(0x00400000, IP_ACTIVE_HIGH, IPT_BUTTON7) PORT_PLAYER(2)
	PORT_BIT(0x00800000, IP_ACTIVE_HIGH, IPT_BUTTON8) PORT_PLAYER(2)
	PORT_BIT(0x01000000, IP_ACTIVE_HIGH, IPT_BUTTON9) PORT_PLAYER(2)
	PORT_BIT(0x02000000, IP_ACTIVE_HIGH, IPT_BUTTON10) PORT_PLAYER(2)
	PORT_BIT(0x04000000, IP_ACTIVE_HIGH, IPT_BUTTON11) PORT_PLAYER(2)
	PORT_BIT(0x08000000, IP_ACTIVE_HIGH, IPT_BUTTON12) PORT_PLAYER(2)
	PORT_BIT(0x10000000, IP_ACTIVE_HIGH, IPT_BUTTON13) PORT_PLAYER(2)
	PORT_BIT(0x20000000, IP_ACTIVE_HIGH, IPT_BUTTON14) PORT_PLAYER(2)
	PORT_BIT(0x40000000, IP_ACTIVE_HIGH, IPT_BUTTON15) PORT_PLAYER(2)
	PORT_BIT(0x80000000, IP_ACTIVE_HIGH, IPT_BUTTON16) PORT_PLAYER(2)
INPUT_PORTS_END

static INPUT_PORTS_START( dabj )
	PORT_START("pio")
INPUT_PORTS_END

void xavix2_state::config(machine_config &config)
{
	// unknown CPU 'SSD 2002-2004 NEC 800208-51'
	XAVIX2(config, m_maincpu, 98'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &xavix2_state::mem);
	m_maincpu->set_vblank_int("screen", FUNC(xavix2_state::vblank_irq));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500));
	m_screen->set_screen_update(FUNC(xavix2_state::screen_update));
	m_screen->set_size(640, 400);
	m_screen->set_visarea(0, 639, 0, 399);

	/* sound hardware */
	SPEAKER(config, "speaker", 2).front();

	// unknown sound hardware
}

void naruto_state::config(machine_config& config)
{
	xavix2_state::config(config);

	I2C_24C08(config, m_i2cmem);
}

void domyos_state::config(machine_config& config)
{
	xavix2_state::config(config);

	I2C_24C64(config, m_i2cmem);
}

ROM_START( ban_naru )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "naruto.bin", 0x000000, 0x800000, CRC(e3465ad2) SHA1(13e3d2de5d5a084635cab158f3639a1ea73265dc) )
ROM_END

ROM_START( ban_bldj )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "bldj.u2", 0x000000, 0x800000, CRC(aa865fe3) SHA1(2f5f4809a07a2f5671f81aa22e379c11c43943a0) )

	// 24c04 at u3
ROM_END


ROM_START( ban_dbz )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "dbz.u2a", 0x000000, 0x800000, CRC(7e535ea2) SHA1(6c746af763273bd9e47929c3ba857c7af563bf79) )

	// also has a 24c02 at u3
ROM_END

ROM_START( ban_db2j )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "db2j.u3", 0x000000, 0x800000, CRC(7362ac0d) SHA1(f1880470f0db56135d9bc88d7193d037ac49b996) )

	// also has a AT24C08
ROM_END

ROM_START( epo_dabj )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "dabj.u3", 0x000000, 0x800000, CRC(9ebc1384) SHA1(38abaebd05bc9ab300ee5fbf37bd88ce9cbd20e1) )
ROM_END

ROM_START( epo_dab2j )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "dab2j.u4", 0x000000, 0x800000, CRC(e3d12ee6) SHA1(a2f930f4ffe778e02556b5e1a1836f88888e7c82) )
ROM_END


ROM_START( epo_dtcj )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "dtcj.u2", 0x000000, 0x800000, CRC(64c2aabb) SHA1(14f02eb01f1c6e76202f7a70818c300ba23fd879) )

	// SEEPROM is a AT24C04 at u4
ROM_END

ROM_START( epo_sskj )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "sskj.u2", 0x000000, 0x800000, CRC(3344b2fc) SHA1(cda27bd1c7d6ccdb6da06cd837aa9cde5a58e5e4) )

	// SEEPROM is a AT24C04 at u4
ROM_END

ROM_START( epo_ssk2 )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "ssk2.u2", 0x000000, 0x800000, CRC(d5902e48) SHA1(010bc2417814ded24a474d9165f6b9523af7d1ef) )

	// SEEPROM is a 24CS04
ROM_END

ROM_START( epo_pabj )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "pabj.u3", 0x000000, 0x800000, CRC(ac46991c) SHA1(06c2b493824085502e96a7c1e46e9e89433e7301) )
ROM_END

ROM_START( domfitad )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "xpfitnessadventure.bin", 0x000000, 0x1000000, CRC(a7917081) SHA1(95ae5dc6e64a78ae060cb0e61d8b0af34a93c4ce) )
ROM_END

ROM_START( dombikec )
	ROM_REGION( 0x1000000, "maincpu", ROMREGION_ERASE00 )
	ROM_LOAD( "xpbikeconcept.bin", 0x000000, 0x1000000, CRC(3447fce5) SHA1(c7e9e9cd789a17ac886ecf253f67753213cf8d21) )
ROM_END

} // anonymous namespace

// Let's!TVプレイ　ＮＡＲＵＴＯ－ナルト－ 忍者体感～だってばよ～ / バンダイ / 日本
CONS( 2006, ban_naru, 0, 0, config, naruto, naruto_state, empty_init, "Bandai / SSD Company LTD", "Let's! TV Play Naruto (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// Let's!TVプレイ 影発動体感! ブルードラゴン -極めろ!ファイヤークライシス!-
CONS( 2006, ban_bldj, 0, 0, config, naruto, naruto_state, empty_init, "Bandai / SSD Company LTD", "Let's! TV Play Kage Hatsudou Taikan! Blue Dragon - Kiwamero! Fire Crisis! (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

CONS( 2005, ban_dbz,  0, 0, config, naruto, naruto_state, empty_init, "Bandai / SSD Company LTD", "Let's! TV Play Dragon Ball Z Battle Experience Kamehameha (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// Let's!TVプレイ　ドラゴンボールＺ　バトル体感かめはめ波２～オッスおめぇ悟空 天下一武道会～
CONS( 2006, ban_db2j, 0, 0, config, naruto, naruto_state, empty_init, "Bandai / SSD Company LTD", "Let's! TV Play Dragon Ball Z Battle Experience Kamehameha 2 ~Ossu Ome Goku Tenkaichi Budokai~ (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// テレビであそぼう!まなぼう! 超脳力あいうえお図鑑
CONS( 2006, epo_dabj, 0, 0, config, dabj,   xavix2_state, empty_init, "Epoch / SSD Company LTD", "TV de Asobou! Manabou! Chou Nouryoku AIUEO Zukan (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// ドラえもん 太鼓あいうえお図鑑
CONS( 200?, epo_dab2j, 0, 0, config, dabj,   xavix2_state, empty_init, "Epoch / SSD Company LTD", "Doraemon Taiko AIUEO Zukan (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// ドラえもん 体感タケコプター! 空とぶ大冒険
CONS( 2006, epo_dtcj, 0, 0, config, dabj,   xavix2_state, empty_init, "Epoch / SSD Company LTD", "Doraemon Taikan Take-copter! Sora Tobu Daibouken (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// SASUKE サスケ＆筋肉バトル!!スポーツマンNO.1決定戦
CONS( 2006, epo_sskj, 0, 0, config, dabj,   xavix2_state, empty_init, "Epoch / SSD Company LTD", "Sasuke & Kinniku Battle!! Sportsman No. 1 Ketteisen (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// 究極! 筋肉(マッスル)スタジアム! サスケ完全制覇
CONS( 2008, epo_ssk2, 0, 0, config, dabj,   xavix2_state, empty_init, "Epoch / SSD Company LTD", "Kyuukyoku! Muscle Stadium! Sasuke Kanzen Seiha (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

CONS( 2007, epo_pabj, 0, 0, config, dabj,   xavix2_state, empty_init, "Epoch / SSD Company LTD", "TV de Asobou! Manabou! Pooh-san to Issho: ABC AIUEO Zukan (Japan)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )

// These are for the 'Domyos Interactive System' other Domyos Interactive System games can be found in xavix.cpp (the SoC is inside the cartridge, base acts as a 'TV adapter' only)

// Has SEEPROM and an RTC.  Adventure has the string DOMYSSDCOLTD a couple of times.
CONS( 2008, domfitad, 0, 0, config, domyos, domyos_state, empty_init, "Decathlon / SSD Company LTD", "Domyos Fitness Adventure (Domyos Interactive System)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )
CONS( 2008, dombikec, 0, 0, config, domyos, domyos_state, empty_init, "Decathlon / SSD Company LTD", "Domyos Bike Concept (Domyos Interactive System)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND )



xbase09.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for XBase 09 drum machine by JoMoX GmbH.

****************************************************************************/

#include "emu.h"
#include "cpu/pic17/pic17c4x.h"
#include "machine/nvram.h"


namespace {

class xbase09_state : public driver_device
{
public:
	xbase09_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_analog_ports(*this, "ANALOG%02X", 0U)
		, m_port_select(0)
	{
	}

	void xbase09(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void portb_w(u8 data);
	u16 ram_r(offs_t offset);
	void ram_w(offs_t offset, u16 data);
	void mem_map(address_map &map) ATTR_COLD;

	required_device<pic17c4x_device> m_maincpu;
	required_ioport_array<0x16> m_analog_ports;

	std::unique_ptr<u8[]> m_nvram_data;
	u8 m_port_select;
};

void xbase09_state::machine_start()
{
	m_nvram_data = std::make_unique<u8[]>(0x8000);
	subdevice<nvram_device>("nvram")->set_base(m_nvram_data.get(), 0x8000);

	save_pointer(NAME(m_nvram_data), 0x8000);
	save_item(NAME(m_port_select));
}


void xbase09_state::portb_w(u8 data)
{
	m_port_select = data >> 3;
}

u16 xbase09_state::ram_r(offs_t offset)
{
	return m_nvram_data[offset] | 0xff00;
}

void xbase09_state::ram_w(offs_t offset, u16 data)
{
	switch (offset)
	{
	case 0x0007:
		// Successive-approximation ADC implemented using software algorithm and LM393 comparator
		if (m_port_select < 0x16 && m_analog_ports[m_port_select]->read() < (data >> 8))
			m_maincpu->set_input_line(pic17c4x_device::RA0_LINE, ASSERT_LINE);
		else
			m_maincpu->set_input_line(pic17c4x_device::RA0_LINE, CLEAR_LINE);
		break;

	default:
		if ((data & 0xff00) != 0)
			logerror("%s: Writing %04X to %04X\n", machine().describe_context(), data, offset + 0x8000);
		break;
	}

	m_nvram_data[offset] = data & 0x00ff;
}


void xbase09_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("firmware", 0);
	map(0x8000, 0xffff).rw(FUNC(xbase09_state::ram_r), FUNC(xbase09_state::ram_w));
}


static INPUT_PORTS_START(xbase09)
	PORT_START("ANALOG00")
	PORT_BIT(0xff, 0x00, IPT_UNKNOWN)

	PORT_START("ANALOG01")
	PORT_BIT(0xff, 0x01, IPT_UNKNOWN)

	PORT_START("ANALOG02")
	PORT_BIT(0xff, 0x02, IPT_UNKNOWN)

	PORT_START("ANALOG03")
	PORT_BIT(0xff, 0x03, IPT_UNKNOWN)

	PORT_START("ANALOG04")
	PORT_BIT(0xff, 0x04, IPT_UNKNOWN)

	PORT_START("ANALOG05")
	PORT_BIT(0xff, 0x05, IPT_UNKNOWN)

	PORT_START("ANALOG06")
	PORT_BIT(0xff, 0x06, IPT_UNKNOWN)

	PORT_START("ANALOG07")
	PORT_BIT(0xff, 0x07, IPT_UNKNOWN)

	PORT_START("ANALOG08")
	PORT_BIT(0xff, 0x08, IPT_UNKNOWN)

	PORT_START("ANALOG09")
	PORT_BIT(0xff, 0x09, IPT_UNKNOWN)

	PORT_START("ANALOG0A")
	PORT_BIT(0xff, 0x0a, IPT_UNKNOWN)

	PORT_START("ANALOG0B")
	PORT_BIT(0xff, 0x14, IPT_UNKNOWN)

	PORT_START("ANALOG0C")
	PORT_BIT(0xff, 0x1e, IPT_UNKNOWN)

	PORT_START("ANALOG0D")
	PORT_BIT(0xff, 0x28, IPT_UNKNOWN)

	PORT_START("ANALOG0E")
	PORT_BIT(0xff, 0x32, IPT_UNKNOWN)

	PORT_START("ANALOG0F")
	PORT_BIT(0xff, 0x3c, IPT_UNKNOWN)

	PORT_START("ANALOG10")
	PORT_BIT(0xff, 0x46, IPT_UNKNOWN)

	PORT_START("ANALOG11")
	PORT_BIT(0xff, 0x50, IPT_UNKNOWN)

	PORT_START("ANALOG12")
	PORT_BIT(0xff, 0x5a, IPT_UNKNOWN)

	PORT_START("ANALOG13")
	PORT_BIT(0xff, 0x64, IPT_UNKNOWN)

	PORT_START("ANALOG14")
	PORT_BIT(0xff, 0xc8, IPT_UNKNOWN)

	PORT_START("ANALOG15")
	PORT_BIT(0xff, 0xff, IPT_UNKNOWN)
INPUT_PORTS_END

void xbase09_state::xbase09(machine_config &config)
{
	PIC17C43(config, m_maincpu, 16_MHz_XTAL); // PIC17C43-16/P
	m_maincpu->set_mode(pic17c43_device::mode::MICROPROCESSOR);
	m_maincpu->set_addrmap(AS_PROGRAM, &xbase09_state::mem_map);
	m_maincpu->rb_out_cb().set(FUNC(xbase09_state::portb_w));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // SRM2B256SLMX10, NEC D43256BGU-70LL or equivalent + battery

	//MAX509(config, "dac"); // MAX509BWCP (near top of PCB)
}

// IC positions are not labeled on PCB except for stickered PLDs
ROM_START(xbase09)
	ROM_REGION16_LE(0x10000, "firmware", 0)
	ROM_DEFAULT_BIOS("v209")
	ROM_SYSTEM_BIOS(0, "v132", "Version 1.32")
	ROMX_LOAD("xbase_09__1.32l.bin", 0x0000, 0x8000, CRC(30dc47c6) SHA1(ee79f9e98c06edd8a5963fcd325da3490e8e5b76), ROM_BIOS(0) | ROM_SKIP(1))
	ROMX_LOAD("xbase_09__1.32h.bin", 0x0001, 0x8000, CRC(ce2df930) SHA1(56922d069b53013f2a3646dcd63fbbc9b609cf5b), ROM_BIOS(0) | ROM_SKIP(1))
	ROM_SYSTEM_BIOS(1, "v208", "Version 2.08 (without Tempo lock)")
	ROMX_LOAD("xbase_09__2.08l.bin", 0x0000, 0x8000, CRC(c0f06ef6) SHA1(912f0b01cf5cfdc37ce61b3eacf933174f80171d), ROM_BIOS(1) | ROM_SKIP(1))
	ROMX_LOAD("xbase_09__2.08h.bin", 0x0001, 0x8000, CRC(f1287888) SHA1(ee162887af1cfe968004b8c2e1c5417be42b2e9f), ROM_BIOS(1) | ROM_SKIP(1))
	ROM_SYSTEM_BIOS(2, "v209", "Version 2.09 (with Tempo lock)")
	ROMX_LOAD("xbase_09__2.09l.bin", 0x0000, 0x8000, CRC(60667060) SHA1(6a004934dd3f5f729f59bcbcb79e86e0e5f97fe9), ROM_BIOS(2) | ROM_SKIP(1))
	ROMX_LOAD("xbase_09__2.09h.bin", 0x0001, 0x8000, CRC(64645c77) SHA1(35cc18774d9e4cc61af0469144c49dddfabc92b7), ROM_BIOS(2) | ROM_SKIP(1))
	ROM_SYSTEM_BIOS(3, "sx209", "SX Version 2.09") // "requires hardware update," so may need to be a separate system
	ROMX_LOAD("xbase09sx_2.09l.bin", 0x0000, 0x8000, CRC(7e87c30d) SHA1(2237f235749ae583327f1f8d893ceb3bb6502ae9), ROM_BIOS(3) | ROM_SKIP(1))
	ROMX_LOAD("xbase09sx_2.09h.bin", 0x0001, 0x8000, CRC(ac9ad2e3) SHA1(8977e4520072b4ef6b603bcb0574663f10ba2629), ROM_BIOS(3) | ROM_SKIP(1))

	ROM_REGION(0x10000, "hhrom", 0)
	ROM_LOAD("xbase_09__rom_0.bin", 0x00000, 0x10000, NO_DUMP) // size unknown

	ROM_REGION(0x3000, "plds", 0)
	ROM_LOAD("xbase.ic17", 0x0000, 0x0a92, NO_DUMP) // next to CPU
	ROM_LOAD("xbase.ic59", 0x1000, 0x0a92, NO_DUMP) // right side of HH ROM
	ROM_LOAD("xbase.ic60", 0x2000, 0x0a92, NO_DUMP) // left side of HH ROM; confirmed to be a PALCE20V8H-15PC/4
ROM_END

} // anonymous namespace


SYST(1997, xbase09, 0, 0, xbase09, xbase09, xbase09_state, empty_init, "JoMoX", "XBase 09 Midi Controlled Analogue Drum Module", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



xbox.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Samuele Zannoli
/***************************************************************************

    XBOX (c) 2001 Microsoft

***************************************************************************/


#include "emu.h"
#include "xbox_pci.h"
#include "xbox.h"

#include "bus/ata/atapicdr.h"
#include "bus/ata/hdd.h"
#include "cpu/i386/i386.h"
#include "machine/idectrl.h"
#include "machine/pci.h"

#include "speaker.h"

#include "bitmap.h"


namespace {

#define CPU_DIV 64

class xbox_state : public xbox_base_state
{
public:
	xbox_state(const machine_config &mconfig, device_type type, const char *tag)
		: xbox_base_state(mconfig, type, tag)
		, m_ide(*this, "pci:09.0:ide1")
		, m_devh(*this, "pci:09.0:ide1:0:hdd")
		, m_devc(*this, "pci:09.0:ide1:1:cdrom")
	{ }

	void xbox(machine_config &config);
protected:
	void xbox_map(address_map &map) ATTR_COLD;

	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

	virtual void hack_eeprom() override;

	// devices
	optional_device<bus_master_ide_controller_device> m_ide;
	required_device<ata_mass_storage_device_base> m_devh;
	required_device<atapi_cdrom_device> m_devc;
};

void xbox_state::video_start()
{
}

void xbox_state::xbox_map(address_map &map)
{
	map(0xff000000, 0xff0fffff).rom().region("bios", 0).mirror(0x00f00000);
}

static INPUT_PORTS_START( xbox )
	/* dummy active high structure */
	PORT_START("SYSA")
	PORT_DIPNAME( 0x01, 0x00, "SYSA" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )

	/* dummy active low structure */
	PORT_START("DSWA")
	PORT_DIPNAME( 0x01, 0x01, "DSWA" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x20, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
INPUT_PORTS_END


void xbox_state::hack_eeprom()
{
	// 8004e5da,4e5da=0xc3
	m_maincpu->space(0).write_byte(0x4e5da, 0xc3); // remove audio wait
	// 8006e654,6e654=0
	m_maincpu->space(0).write_byte(0x6e654, 0); // disable boot animation
	// 800375f0,375f0=0
	m_maincpu->space(0).write_byte(0x375f0, m_maincpu->space(0).read_byte(0x375f0) & 0xfe); // internal hub not used
}

void xbox_state::machine_start()
{
	xbox_base_state::machine_start();
	// savestates
	//save_item(NAME(item));
}

void xbox_state::machine_reset()
{
	uint16_t *id;

	// set some needed parameters
	id = m_devh->identify_device_buffer();
	id[88] |= (1 << 2); // ultra dma mode 2 supported
	id[128] |= 2; // bits 2-1=01 drive already unlocked

	id = m_devc->identify_device_buffer();
	id[64] |= (1 << 1);
	id[88] |= (1 << 2); // ultra dma mode 2 supported
}

void usb_xbox(device_slot_interface &device)
{
	device.option_add("xbox_controller", OHCI_GAME_CONTROLLER);
}

void xbox_ata_devices(device_slot_interface &device)
{
	device.option_add("hdd", IDE_HARDDISK);
	device.option_add("cdrom", ATAPI_CDROM);
}

void xbox_state::xbox(machine_config &config)
{
	xbox_base(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &xbox_state::xbox_map);

	subdevice<ide_controller_32_device>("pci:09.0:ide1")->options(xbox_ata_devices, "hdd", "cdrom", true);

	OHCI_USB_CONNECTOR(config, "pci:02.0:port1", usb_xbox, nullptr, false);
	OHCI_USB_CONNECTOR(config, "pci:02.0:port2", usb_xbox, nullptr, false);
	OHCI_USB_CONNECTOR(config, "pci:02.0:port3", usb_xbox, "xbox_controller", false);
	OHCI_USB_CONNECTOR(config, "pci:02.0:port4", usb_xbox, nullptr, false);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();

	OHCI_GAME_CONTROLLER(config, "ohci_gamepad", 0);
}


/***************************************************************************

  Machine driver(s)

***************************************************************************/
#define ROM_LOAD_BIOS(bios,name,offset,length,hash) \
		ROMX_LOAD(name, offset, length, hash, ROM_BIOS(bios))

ROM_START( xbox )
	ROM_REGION( 0x400, "mcpx", 0 )
	ROM_LOAD( "mcpx_1_0.bin", 0, 0x200, CRC(0b07d1f1) SHA1(5d270675b54eb8071b480e42d22a3015ac211cef) )
	ROM_LOAD( "mcpx_1_1.bin", 0x200, 0x200, CRC(94ce376b) SHA1(6c875f17f773aaec51eb434068bb6c657c4343c0) )

	ROM_REGION32_LE( 0x100000, "bios", 0)
	ROM_SYSTEM_BIOS(0, "bios0", "XBOX BIOS 4134 1024k")
	ROM_LOAD_BIOS(0, "4134_1024k.bin", 0x000000, 0x100000, CRC(49d8055a) SHA1(d46cef771a63dc8024fe36d7ab5b959087ac999f))
	ROM_SYSTEM_BIOS(1, "bios1", "XBOX BIOS 3944 1024k")
	ROM_LOAD_BIOS(1, "3944_1024k.bin", 0x000000, 0x100000, CRC(32a9ecb6) SHA1(67054fc88bda94e33e86f1b19be60efec0724fb6))
	ROM_SYSTEM_BIOS(2, "bios2", "XBOX BIOS 4034 1024k")
	ROM_LOAD_BIOS(2, "4034_1024k.bin", 0x000000, 0x100000, CRC(0d6fc88f) SHA1(ab676b712204fb1728bf89f9cd541a8f5a64ab97))
	ROM_SYSTEM_BIOS(3, "bios3", "XBOX BIOS 4817 1024k")
	ROM_LOAD_BIOS(3, "4817_1024k.bin", 0x000000, 0x100000, CRC(3f30863a) SHA1(dc955bd4d3ca71e01214a49e5d0aba615270c03c))
	ROM_COPY( "mcpx", 0, 0x3fe00, 0x200)
	ROM_COPY( "mcpx", 0, 0x7fe00, 0x200)
	ROM_COPY( "mcpx", 0, 0xbfe00, 0x200)
	ROM_COPY( "mcpx", 0, 0xffe00, 0x200)


	ROM_REGION( 0x1000000, "tbp", 0 ) // To Be Processed, of course
	ROM_LOAD( "5101_256k.bin", 0x000000, 0x040000, CRC(e8a9224e) SHA1(5108e1025f48071c07a6823661d708c66dee97a9) )
	ROM_LOAD( "xbox-5530.bin", 0x040000, 0x040000, CRC(9569c4d3) SHA1(40fa73277013be3168135e1768b09623a987ff63) )
	ROM_LOAD( "xbox-5713.bin", 0x080000, 0x040000, CRC(58fd8173) SHA1(8b7ccc4648ccd78cdb7b65cfca09621eaf2d4238) )
	ROM_LOAD( "5838_256k.bin", 0x0C0000, 0x040000, CRC(5be2413d) SHA1(b9489e883c650b5e5fe2f83a32237dbf74f0e9f1) )
ROM_END

} // anonymous namespace


CONS( 2001, xbox, 0, 0, xbox,  xbox, xbox_state, empty_init, "Microsoft", "XBOX", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



xd88.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * Tektronix XD88 systems.
 *
 * Sources:
 *  - https://wiki.unix-haters.org/doku.php?id=tektronix:xd88:resources
 *
 * TODO:
 *  - everything
 */

#include "emu.h"

#include "cpu/m88000/m88000.h"

#include "machine/mc88200.h"

//#define VERBOSE (LOG_GENERAL)
#include "logmacro.h"

namespace {

class xd88_state : public driver_device
{
public:
	xd88_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "cpu")
		, m_cmmu(*this, "cmmu%u", 0U)
		, m_boot(*this, "boot")
	{
	}

	void xd88_01(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void cpu_map(address_map &map) ATTR_COLD;

private:
	required_device<mc88100_device> m_cpu;
	required_device_array<mc88200_device, 8> m_cmmu;

	memory_view m_boot;
};

void xd88_state::machine_start()
{
}

void xd88_state::machine_reset()
{
	m_boot.select(0);
}

void xd88_state::cpu_map(address_map &map)
{
	// TODO: RAM options (8M is standard)
	map(0x0000'0000, 0x007f'ffff).ram();

	map(0x0000'0000, 0x0003'ffff).view(m_boot);
	m_boot[0](0x0000'0000, 0x0003'ffff).rom().region("eprom", 0);

	map(0xfe00'0000, 0xfe03'ffff).rom().region("eprom", 0);
}

void xd88_state::xd88_01(machine_config &config)
{
	MC88100(config, m_cpu, 20'000'000);
	m_cpu->set_addrmap(AS_PROGRAM, &xd88_state::cpu_map);
	m_cpu->set_cmmu_code([this](u32 const address) -> mc88200_device & { return *m_cmmu[4]; });
	m_cpu->set_cmmu_data([this](u32 const address) -> mc88200_device & { return *m_cmmu[0]; });

	for (unsigned i = 0; i < std::size(m_cmmu); i++)
		MC88200(config, m_cmmu[i], 20'000'000, i).set_mbus(m_cpu, AS_PROGRAM);
}

ROM_START(xd88_01)
	ROM_REGION32_BE(0x40000, "eprom", 0)
	ROMX_LOAD("160-5796-04__u3930_v1.4.u3930", 0, 0x10000, CRC(db2ef744) SHA1(82ddb494fc1e2693da3d021c1cfeea49ef06ed3e), ROM_SKIP(3))
	ROMX_LOAD("160-5797-04__u4230_v1.4.u4230", 1, 0x10000, CRC(8fe40d66) SHA1(30cacd590c2598cb6db1933309d2d60e57f375eb), ROM_SKIP(3))
	ROMX_LOAD("160-5798-04__u4530_v1.4.u4530", 2, 0x10000, CRC(2aa2503c) SHA1(edc869a7ac9b6ce68603146884cf9a95ee22d39e), ROM_SKIP(3))
	ROMX_LOAD("160-5799-04__u4830_v1.4.u4830", 3, 0x10000, CRC(f7ecedae) SHA1(6063e616e4c1de5af048bd2cb61ad64a57c7f5a4), ROM_SKIP(3))
ROM_END

} // anonymous namespace

/*   YEAR  NAME     PARENT COMPAT MACHINE  INPUT CLASS       INIT        COMPANY      FULLNAME   FLAGS */
COMP(1990, xd88_01, 0,     0,     xd88_01, 0,    xd88_state, empty_init, "Tektronix", "XD88/01", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



xds.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************

    Visual Technology X Display Station (XDS19P)

    Hardware:
    - MC68HC000P12F
    - 2x 27C010L-15 EPROM on cartridge (+ 4 empty sockets)
    - 32x KM44C256AP-8 / MCM514256AP80 / MT4C4256-8 (4M DRAM)
    - 4x HM53461P-10 (128k VRAM)
    - DS1225Y NVRAM
    - AM7990PC LANCE
    - MC68B45 CRTC
    - SCN68681C1N40
    - MC68B50P
    - 16.667 MHz XTAL, 100 MHz XTAL
    - XTAL X1 3.6864 MHz (assumed, unreadable), XTAL X2 (unreadable)
    - 4 position DIP switch
    - Buzzer

    TODO:
    - Verify and hook up irq mask
    - Source and timing of clock interrupt
    - What's irq1 connected to?
    - Remaining DUART output/input port bits
    - Real DIP switch meaning
    - Buzzer
    - Better screen rendering using the MC6845?
    - Configurable RAM
    - Mouse port loopback test

    Notes:
    - PCB marked "VISUAL 55-0106-000 REV A  WL3-94V1"
    - The diagnostic serial loopback test passes if you attach
      the "dec_loopback" slot device
    - "romboot" is not possible because the sockets aren't populated. You
      need to boot the system over the network instead.

***************************************************************************/

#include "emu.h"

#include "xds_kbd.h"

#include "bus/rs232/hlemouse.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
#include "machine/6850acia.h"
#include "machine/am79c90.h"
#include "machine/clock.h"
#include "machine/mc68681.h"
#include "machine/nvram.h"
#include "video/mc6845.h"

#include "screen.h"


namespace {


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class xds_state : public driver_device
{
public:
	xds_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_screen(*this, "screen"),
		m_crtc(*this, "crtc"),
		m_lance(*this, "lance"),
		m_duart(*this, "duart"),
		m_serialport(*this, "serialport"),
		m_mouseport(*this, "mouseport"),
		m_acia(*this, "acia"),
		m_vram(*this, "vram"),
		m_nvram(*this, "nvram", 0x2000, ENDIANNESS_BIG),
		m_prom(*this, "prom"),
		m_sw1(*this, "sw1")
	{ }

	void xds19p(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<m68000_device> m_maincpu;
	required_device<screen_device> m_screen;
	required_device<mc6845_device> m_crtc;
	required_device<am7990_device> m_lance;
	required_device<mc68681_device> m_duart;
	required_device<rs232_port_device> m_serialport;
	required_device<rs232_port_device> m_mouseport;
	required_device<acia6850_device> m_acia;
	required_shared_ptr<uint16_t> m_vram;
	memory_share_creator<uint8_t> m_nvram;
	required_memory_region m_prom;
	required_ioport m_sw1;

	uint8_t m_irq_mask = 0x00;

	void mem_map(address_map &map) ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	uint16_t lance_dma_r(offs_t offset, uint16_t mem_mask);
	void lance_dma_w(offs_t offset, uint16_t data, uint16_t mem_mask);

	uint8_t dip_prom_r(offs_t offset);
	void output_w(uint8_t data);
	uint8_t nvram_r(offs_t offset);
	void nvram_w(offs_t offset, uint8_t data);
	uint8_t irq_mask_r();
	void irq_mask_w(uint8_t data);
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void xds_state::mem_map(address_map &map)
{
	map(0x000000, 0x03ffff).rom().region("maincpu", 0);
	map(0x200000, 0x2001ff).r(FUNC(xds_state::dip_prom_r)).umask16(0x00ff);
	map(0x400000, 0x400003).rw(m_lance, FUNC(am7990_device::regs_r), FUNC(am7990_device::regs_w));
	map(0x500000, 0x500003).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write)).umask16(0x00ff);
	map(0x600000, 0x60001f).rw(m_duart, FUNC(mc68681_device::read), FUNC(mc68681_device::write)).umask16(0x00ff);
	map(0x700000, 0x700001).rw(m_crtc, FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w)).umask16(0x00ff);
	map(0x700002, 0x700003).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w)).umask16(0x00ff);
	map(0x800000, 0xbfffff).ram(); // fixed 4M for now
	map(0xc00000, 0xc00003).rw(FUNC(xds_state::irq_mask_r), FUNC(xds_state::irq_mask_w)).umask16(0xff00);
	map(0xd00000, 0xd1ffff).ram().share("vram");
	map(0xe00000, 0xe03fff).ram().rw(FUNC(xds_state::nvram_r), FUNC(xds_state::nvram_w)).umask16(0xff00);
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( xds )
	PORT_START("sw1")
	PORT_DIPNAME( 0xf, 0x5, "Video Mode?")
	PORT_DIPSETTING(   0x0, "0" )
	PORT_DIPSETTING(   0x1, "1 - 1152x904 @72" )
	PORT_DIPSETTING(   0x2, "2 - 1024x864 @72" )
	PORT_DIPSETTING(   0x3, "3" )
	PORT_DIPSETTING(   0x4, "4" )
	PORT_DIPSETTING(   0x5, "5 - 1152x904 @72" ) // this (and 1) seems to be correct for this model
	PORT_DIPSETTING(   0x6, "6 - 1152x904 @66" )
	PORT_DIPSETTING(   0x7, "7 - 1024x800 @88" )
	PORT_DIPSETTING(   0x8, "8" )
	PORT_DIPSETTING(   0x9, "9" )
	PORT_DIPSETTING(   0xa, "a" )
	PORT_DIPSETTING(   0xb, "b" )
	PORT_DIPSETTING(   0xc, "c" )
	PORT_DIPSETTING(   0xd, "d" )
	PORT_DIPSETTING(   0xe, "e" )
	PORT_DIPSETTING(   0xf, "f" )
INPUT_PORTS_END


//**************************************************************************
//  VIDEO EMULATION
//**************************************************************************

uint32_t xds_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	for (unsigned y = 0; y < 904; y++)
	{
		uint32_t *line = &bitmap.pix(y);

		for (unsigned x = 0; x < 1152 / 16; x++)
		{
			uint16_t data = m_vram[y * (1152 / 16) + x];

			for (unsigned b = 0; b < 16; b++)
				*line++ = BIT(data, 15 - b) ? rgb_t::white() : rgb_t::black();
		}
	}

	return 0;
}


//**************************************************************************
//  NETWORK
//**************************************************************************

uint16_t xds_state::lance_dma_r(offs_t offset, uint16_t mem_mask)
{
	return m_maincpu->space(AS_PROGRAM).read_word(offset, mem_mask);
}

void xds_state::lance_dma_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	m_maincpu->space(AS_PROGRAM).write_word(offset, data, mem_mask);
}


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

uint8_t xds_state::dip_prom_r(offs_t offset)
{
	uint8_t data = 0;

	data |= (m_sw1->read() & 0xf) << 4;
	data |= (m_prom->as_u8(offset) & 0xf);

	return data;
}

void xds_state::output_w(uint8_t data)
{
	// 76------  unknown
	// --5-----  serial port spds
	// ---4----  unknown
	// ----3---  toggles at a rate of 32 hz
	// -----2--  unknown
	// ------1-  serial rts
	// -------0  serial dtr

	// maybe, but gives ~80 instead of ~100 in the clocktest diagnostic
	if (BIT(data, 3))
		m_maincpu->pulse_input_line(2, attotime::from_usec(1));

	m_serialport->write_dtr(BIT(~data, 0));
	m_serialport->write_rts(BIT(data, 1));
	m_serialport->write_spds(BIT(~data, 5));
}

uint8_t xds_state::nvram_r(offs_t offset)
{
	return m_nvram[offset];
}

void xds_state::nvram_w(offs_t offset, uint8_t data)
{
	m_nvram[offset] = data;
}

uint8_t xds_state::irq_mask_r()
{
	return m_irq_mask;
}

void xds_state::irq_mask_w(uint8_t data)
{
	logerror("irq_mask_w: %02x\n", data);
	m_irq_mask = data;
}

void xds_state::machine_start()
{
	// register for save states
	save_item(NAME(m_irq_mask));
}

void xds_state::machine_reset()
{
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

static void mouse_devices(device_slot_interface &device)
{
	device.option_add("msystems_mouse", MSYSTEMS_HLE_SERIAL_MOUSE);
}

void xds_state::xds19p(machine_config &config)
{
	M68000(config, m_maincpu, 16.667_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &xds_state::mem_map);

	NVRAM(config, "nvram");

	// video hardware
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(100_MHz_XTAL, 1472, 0, 1152, 941, 0, 904);
	m_screen->set_screen_update(FUNC(xds_state::screen_update));

	MC6845(config, m_crtc, 100_MHz_XTAL / 64);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(64);

	AM7990(config, m_lance, 0);
	m_lance->intr_out().set_inputline(m_maincpu, 5).invert();
	m_lance->dma_in().set(FUNC(xds_state::lance_dma_r));
	m_lance->dma_out().set(FUNC(xds_state::lance_dma_w));

	MC68681(config, m_duart, 3.6864_MHz_XTAL); // xtal unreadable
	m_duart->irq_cb().set_inputline(m_maincpu, 4);
	m_duart->outport_cb().set(FUNC(xds_state::output_w));
	m_duart->a_tx_cb().set(m_mouseport, FUNC(rs232_port_device::write_txd));
	m_duart->b_tx_cb().set(m_serialport, FUNC(rs232_port_device::write_txd));

	RS232_PORT(config, m_mouseport, mouse_devices, "msystems_mouse");
	m_mouseport->rxd_handler().set(m_duart, FUNC(mc68681_device::rx_a_w));
	m_mouseport->dsr_handler().set(m_duart, FUNC(mc68681_device::ip4_w));

	RS232_PORT(config, m_serialport, default_rs232_devices, nullptr);
	m_serialport->rxd_handler().set(m_duart, FUNC(mc68681_device::rx_b_w));
	m_serialport->dsr_handler().set(m_duart, FUNC(mc68681_device::ip0_w));
	m_serialport->cts_handler().set(m_duart, FUNC(mc68681_device::ip1_w));
	m_serialport->dcd_handler().set(m_duart, FUNC(mc68681_device::ip2_w));
	m_serialport->si_handler().set(m_duart, FUNC(mc68681_device::ip5_w));

	ACIA6850(config, m_acia, 0);
	m_acia->irq_handler().set_inputline(m_maincpu, 3);
	m_acia->txd_handler().set("kbd", FUNC(xds_kbd_hle_device::rx_w));

	// probably clocked externally instead
	clock_device &acia_clock(CLOCK(config, "acia_clock", 1200 * 16));
	acia_clock.signal_handler().set(m_acia, FUNC(acia6850_device::write_rxc));
	acia_clock.signal_handler().append(m_acia, FUNC(acia6850_device::write_txc));

	// keyboard
	xds_kbd_hle_device &kbd(XDS_KBD_HLE(config, "kbd"));
	kbd.tx_handler().set(m_acia, FUNC(acia6850_device::write_rxd));
	kbd.cts_handler().set(m_acia, FUNC(acia6850_device::write_cts));
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( xds19p )
	ROM_REGION16_BE(0x40000, "maincpu", 0)
	ROM_LOAD16_BYTE("boot_30.e6", 0x00000, 0x20000, CRC(32449da2) SHA1(e1bc41987fb9c7da5b7fed5d6714c3451be4438c))
	ROM_LOAD16_BYTE("boot_30.e15", 0x00001, 0x20000, CRC(a577087f) SHA1(2fe9d4d49fd6e8ecd230f8097f7004238afbb626))

	ROM_REGION(0x100, "prom", 0)
	ROM_LOAD("1837.e06d", 0x000, 0x100, CRC(b5d9f883) SHA1(28303155c515ac2638a06123fbf7e55f89dd2af9))

	// not a real rom, this file needs to be provided by a tftp server to the system to boot
	ROM_REGION(1045320, "tftp", 0)
	ROM_LOAD("x15-3.1l", 0, 1045320, CRC(5de8fdf1) SHA1(4203ffbba775a8c4a1b827621d116ba00839f91a))
ROM_END


} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY              FULLNAME   FLAGS
COMP( 1990, xds19p, 0,      0,      xds19p,  xds,   xds_state, empty_init, "Visual Technology", "XDS-19P", MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )



xerox820.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/***************************************************************************

        Xerox 820

        12/05/2009 Skeleton driver.

****************************************************************************/

/*

    TODO:

    - Xerox 820
        - floppy format has 3xcd at the end of track data
            :u109: write track 0
            :u109: track description 16xff ... 109xff 3xcd
        - keyboard conflicts with optional serial terminal
    - Xerox 820-II
        - floppy (read/write to FDC triggers Z80 WAIT)
        - Winchester
            - Shugart SA1004 (chs=256,4,40 ss=256)
            - Shugart SA606 (chs=160,6, ss=256)
            - Shugart SA1403D controller
    - Xerox 16/8
    - Emerald Microware X120 board
    - type in Monitor v1.0 from manual
    - ASCII keyboard
    - low-profile keyboard

    http://www.vintagesbc.it/?page_id=233
    http://mccworkshop.com/computers/comphistory7.htm
    http://bitsavers.org/bits/Xerox/820/
    http://bitsavers.org/bits/Xerox/820-II/
    http://www.classiccmp.org/dunfield/img54306/system.htm

    Note:
    - MK-82 have same roms as original Big Board
    - MK-83 have 256K of RAM

    8-inch formats
    77 tracks, 1 head, 26 sectors, 128 bytes sector length, first sector id 1
    77 tracks, 1 head, 26 sectors, 256 bytes sector length, first sector id 1

    5.25-inch formats
    40 tracks, 1 head, 18 sectors, 128 bytes sector length, first sector id 1
    40 tracks, 2 heads, 18 sectors, 128 bytes sector length, first sector id 1

    SmartROM and Plus2 ROM both come for 2.5MHz or 4MHz systems, and there is another distinction between variants for generic or Xerox keyboards
    http://www.microcodeconsulting.com/z80/plus2.htm
    http://www.microcodeconsulting.com/z80/smartrom.htm

*/


#include "emu.h"
#include "xerox820.h"

#include "screen.h"
#include "softlist.h"
#include "speaker.h"

#include "formats/flopimg.h"


/* Read/Write Handlers */

uint8_t xerox820_state::fdc_r(offs_t offset)
{
	return m_fdc->read(offset) ^ 0xff;
}

void xerox820_state::fdc_w(offs_t offset, uint8_t data)
{
	m_fdc->write(offset, data ^ 0xff);
}

void xerox820_state::scroll_w(offs_t offset, uint8_t data)
{
	m_scroll = (offset >> 8) & 0x1f;
}

#ifdef UNUSED_CODE
void xerox820_state::x120_system_w(uint8_t data)
{
	/*

	    bit     signal      description

	    0       DSEL0       drive select bit 0 (01=A, 10=B, 00=C, 11=D)
	    1       DSEL1       drive select bit 1
	    2       SIDE        side select
	    3       VATT        video attribute (0=inverse, 1=blinking)
	    4       BELL        bell trigger
	    5       DENSITY     density (0=double, 1=single)
	    6       _MOTOR      disk motor (0=on, 1=off)
	    7       BANK        memory bank switch (0=RAM, 1=ROM/video)

	*/
}
#endif

void xerox820ii_state::bell_w(offs_t offset, uint8_t data)
{
	m_speaker->level_w(offset);
}

void xerox820ii_state::slden_w(offs_t offset, uint8_t data)
{
	m_fdc->dden_w(offset);
}

void xerox820ii_state::chrom_w(offs_t offset, uint8_t data)
{
	m_chrom = offset;
}

void xerox820ii_state::lowlite_w(uint8_t data)
{
	m_lowlite = data;
}

void xerox820ii_state::sync_w(offs_t offset, uint8_t data)
{
	if (offset)
	{
		/* set external clocks for synchronous sio A */
	}
	else
	{
		/* set internal clocks for asynchronous sio A */
	}
}

/* Memory Maps */

void xerox820_state::xerox820_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x3fff).view(m_view);
	m_view[0](0x0000, 0x3fff).ram();
	m_view[1](0x0000, 0x0fff).rom().region(Z80_TAG, 0);
	m_view[1](0x3000, 0x3fff).ram().share("video_ram");
	map(0x4000, 0xffff).ram();
}

void xerox820_state::xerox820_io(address_map &map)
{
	map(0x00, 0x00).mirror(0xff03).w(COM8116_TAG, FUNC(com8116_device::str_w));
	map(0x04, 0x07).mirror(0xff00).rw(m_sio, FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
	map(0x08, 0x0b).mirror(0xff00).rw(Z80PIO_GP_TAG, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x0c, 0x0c).mirror(0xff03).w(COM8116_TAG, FUNC(com8116_device::stt_w));
	map(0x10, 0x13).mirror(0xff00).rw(FUNC(xerox820_state::fdc_r), FUNC(xerox820_state::fdc_w));
	map(0x14, 0x14).mirror(0x0003).select(0xff00).w(FUNC(xerox820_state::scroll_w));
	map(0x18, 0x1b).mirror(0xff00).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x1c, 0x1f).mirror(0xff00).rw(m_kbpio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
}

void xerox820ii_state::xerox820ii_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xbfff).view(m_view);
	m_view[0](0x0000, 0xbfff).ram();
	m_view[1](0x0000, 0x1fff).rom().region(Z80_TAG, 0);
	m_view[1](0x3000, 0x3fff).ram().share("video_ram");
	map(0xc000, 0xffff).ram();
}

void xerox820ii_state::xerox820ii_io(address_map &map)
{
	xerox820_io(map);
	map(0x28, 0x29).mirror(0xff00).w(FUNC(xerox820ii_state::bell_w));
	map(0x30, 0x31).mirror(0xff00).w(FUNC(xerox820ii_state::slden_w));
	map(0x34, 0x35).mirror(0xff00).w(FUNC(xerox820ii_state::chrom_w));
	map(0x36, 0x36).mirror(0xff00).w(FUNC(xerox820ii_state::lowlite_w));
	map(0x68, 0x69).mirror(0xff00).w(FUNC(xerox820ii_state::sync_w));
}

void xerox820ii_state::xerox168_mem(address_map &map)
{
	map(0x00000, 0x3ffff).ram();
	map(0xff000, 0xfffff).rom().region(I8086_TAG, 0);
}

void xerox820_state::mk83_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x2fff).view(m_view);
	m_view[0](0x0000, 0x2fff).ram();
	m_view[1](0x0000, 0x0fff).rom().region(Z80_TAG, 0);
	map(0x3000, 0x6fff).ram();
	map(0x7000, 0x7fff).ram().share("video_ram");
	map(0x8000, 0xffff).ram();
}


/* Input Ports */

static INPUT_PORTS_START( xerox820 )
	// inputs defined in machine/keyboard.c
INPUT_PORTS_END

TIMER_DEVICE_CALLBACK_MEMBER(bigboard_state::beep_timer)
{
	m_beeper->set_state(0);
}

/* Z80 PIO */

uint8_t xerox820_state::kbpio_pa_r()
{
	/*

	    bit     signal          description

	    0
	    1
	    2
	    3       PBRDY           keyboard data available
	    4       8/N5            8"/5.25" disk select (0=5.25", 1=8")
	    5       400/460         double sided disk detect (only on Etch 2 PCB) (0=SS, 1=DS)
	    6
	    7

	*/

	uint8_t data = 0;

	// keyboard
	data |= m_kbpio->rdy_b() << 3;

	// floppy
	data |= m_8n5 << 4;
	data |= m_400_460 << 5;

	return data;
}

void xerox820_state::kbpio_pa_w(uint8_t data)
{
	/*

	    bit     signal          description

	    0       _DVSEL1         drive select 1
	    1       _DVSEL2         drive select 2
	    2       SIDE            side select
	    3
	    4
	    5
	    6       NCSET2          display character set (inverted and connected to chargen A10)
	    7       BANK            bank switching (0=RAM, 1=ROM/videoram)

	*/

	/* drive select */
	floppy_image_device *floppy = nullptr;

	if (BIT(data, 0)) floppy = m_floppy0->get_device();
	if (BIT(data, 1)) floppy = m_floppy1->get_device();

	m_fdc->set_floppy(floppy);

	if (floppy)
	{
		int _8n5 = (floppy->get_form_factor() == floppy_image::FF_8);

		if (m_8n5 != _8n5)
		{
			m_8n5 = _8n5;

			m_fdc->set_unscaled_clock(m_8n5 ? 20_MHz_XTAL / 10 : 20_MHz_XTAL / 20);
		}

		m_400_460 = !floppy->twosid_r();

		floppy->mon_w(0);

		floppy->ss_w(BIT(data, 2));
	}

	/* display character set */
	m_ncset2 = !BIT(data, 6);

	/* bank switching */
	m_view.select(BIT(data, 7));
}

void bigboard_state::kbpio_pa_w(uint8_t data)
{
	xerox820_state::kbpio_pa_w(data);

	/* beeper on bigboard */
	if (BIT(data, 5) & (!m_bit5))
	{
		m_beep_timer->adjust(attotime::from_msec(40));
		m_beeper->set_state(1);
	}
	m_bit5 = BIT(data, 5);
}

uint8_t xerox820_state::kbpio_pb_r()
{
	/*

	    bit     description

	    0       KB0
	    1       KB1
	    2       KB2
	    3       KB3
	    4       KB4
	    5       KB5
	    6       KB6
	    7       KB7

	*/

	return m_kb->read() ^ 0xff;
}

void xerox820ii_state::rdpio_pb_w(uint8_t data)
{
	/*

	    bit     description

	    0       NBSY
	    1       NMSG
	    2       NC/D
	    3       NREQ
	    4       NI/O
	    5       NSEL
	    6       LS74 Q
	    7       NRST
	*/

	m_sasibus->write_sel(!BIT(data, 5));
	m_sasibus->write_rst(!BIT(data, 7));
	// TODO: LS74 Q
}

void xerox820ii_state::rdpio_pardy_w(int state)
{
	// TODO
}

/* Z80 CTC */

TIMER_DEVICE_CALLBACK_MEMBER( xerox820_state::ctc_tick )
{
	m_ctc->trg0(1);
	m_ctc->trg0(0);
}

/* Z80 Daisy Chain */

static const z80_daisy_config xerox820_daisy_chain[] =
{
	{ Z80SIO_TAG },
	{ Z80PIO_KB_TAG },
	{ Z80PIO_GP_TAG },
	{ Z80CTC_TAG },
	{ nullptr }
};



/***********************************************************

    Quickload

    This loads a .COM file to address 0x100 then jumps
    there. Sometimes .COM has been renamed to .CPM to
    prevent windows going ballistic. These can be loaded
    as well.

************************************************************/

QUICKLOAD_LOAD_MEMBER(xerox820_state::quickload_cb)
{
	m_view.select(0);

	address_space &prog_space = m_maincpu->space(AS_PROGRAM);

	// Avoid loading a program if CP/M-80 is not in memory
	if ((prog_space.read_byte(0) != 0xc3) || (prog_space.read_byte(5) != 0xc3))
	{
		machine_reset();
		return std::make_pair(image_error::UNSUPPORTED, "CP/M must already be running");
	}

	const int mem_avail = 256 * prog_space.read_byte(7) + prog_space.read_byte(6) - 512;
	if (mem_avail < image.length())
		return std::make_pair(image_error::UNSPECIFIED, "Insufficient memory available");

	// Load image to the TPA (Transient Program Area)
	uint16_t quickload_size = image.length();
	for (uint16_t i = 0; i < quickload_size; i++)
	{
		uint8_t data;
		if (image.fread(&data, 1) != 1)
			return std::make_pair(image_error::UNSPECIFIED, "Problem reading the image at offset " + std::to_string(i));
		prog_space.write_byte(i + 0x100, data);
	}

	// clear out command tail
	prog_space.write_byte(0x80, 0);
	prog_space.write_byte(0x81, 0);

	// Roughly set SP basing on the BDOS position
	m_maincpu->set_state_int(Z80_SP, mem_avail + 384);
	m_maincpu->set_pc(0x100); // start program

	return std::make_pair(std::error_condition(), std::string());
}



/* WD1771 Interface */

static void xerox820_floppies(device_slot_interface &device)
{
	device.option_add("sa400", FLOPPY_525_SSSD_35T); // Shugart SA-400, 35 trk drive
	device.option_add("sa400l", FLOPPY_525_SSSD); // Shugart SA-400, 40 trk drive
	device.option_add("sa450", FLOPPY_525_DD); // Shugart SA-450
	device.option_add("sa800", FLOPPY_8_SSDD); // Shugart SA-800
	device.option_add("sa850", FLOPPY_8_DSDD); // Shugart SA-850
}

void xerox820_state::update_nmi()
{
	int halt = m_maincpu->state_int(Z80_HALT);
	int state = (halt && (m_fdc_irq || m_fdc_drq)) ? ASSERT_LINE : CLEAR_LINE;

	m_maincpu->set_input_line(INPUT_LINE_NMI, state);
}

void xerox820_state::fdc_intrq_w(int state)
{
	m_fdc_irq = state;

	update_nmi();
}

void xerox820_state::fdc_drq_w(int state)
{
	m_fdc_drq = state;

	update_nmi();
}

/* Video */

uint32_t xerox820_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	uint16_t sy=0,ma=(m_scroll + 1) * 0x80;
	pen_t const *const pen=m_palette->pens();

	m_framecnt++;

	for (uint8_t y = 0; y < 24; y++)
	{
		if (ma > 0xb80) ma = 0;

		for (uint8_t ra = 0; ra < 10; ra++)
		{
			uint32_t *p = &bitmap.pix(sy++);

			for (uint16_t x = ma; x < ma + 80; x++)
			{
				uint8_t gfx;
				if (ra < 8)
				{
					uint8_t chr = m_video_ram[x & XEROX820_VIDEORAM_MASK] ^ 0x80;

					/* Take care of flashing characters */
					if ((chr < 0x80) && (m_framecnt & 0x08))
						chr |= 0x80;

					/* get pattern of pixels for that character scanline */
					gfx = m_char_rom->base()[(m_ncset2 << 10) | (chr<<3) | ra ];
				}
				else
					gfx = 0xff;

				/* Display a scanline of a character (7 pixels) */
				*p++ = pen[0];
				*p++ = pen[BIT(gfx, 4) ^ 1];
				*p++ = pen[BIT(gfx, 3) ^ 1];
				*p++ = pen[BIT(gfx, 2) ^ 1];
				*p++ = pen[BIT(gfx, 1) ^ 1];
				*p++ = pen[BIT(gfx, 0) ^ 1];
				*p++ = pen[0];
			}
		}
		ma+=128;
	}
	return 0;
}

/* Machine Initialization */

void xerox820_state::machine_start()
{
	// state saving
	save_item(NAME(m_scroll));
	save_item(NAME(m_ncset2));
	save_item(NAME(m_vatt));
	save_item(NAME(m_fdc_irq));
	save_item(NAME(m_fdc_drq));
	save_item(NAME(m_8n5));
	save_item(NAME(m_400_460));

	m_ncset2 = 0;
}

void xerox820_state::machine_reset()
{
	m_view.select(1);

	m_fdc->reset();
}

void bigboard_state::machine_reset()
{
	m_view.select(1);

	/* bigboard has a one-pulse output to drive a user-supplied beeper */
	m_beeper->set_state(0);

	m_fdc->reset();
}

void xerox820ii_state::machine_reset()
{
	m_view.select(1);

	m_fdc->reset();

	m_sio->synca_w(1);
	m_sio->syncb_w(1);
}


/* F4 Character Displayer */
static const gfx_layout xerox820_charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{  0*8,  1*8,  2*8,  3*8,  4*8,  5*8,  6*8,  7*8, 8*8,  9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*8                 /* every char takes 8 bytes */
};

static const gfx_layout xerox820_gfxlayout =
{
	8, 8,                   /* 8 x 8 characters */
	256,                    /* 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{  0*8,  1*8,  2*8,  3*8,  4*8,  5*8,  6*8,  7*8, 8*8,  9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_xerox820 )
	GFXDECODE_ENTRY( "chargen", 0x0000, xerox820_charlayout, 0, 1 )
GFXDECODE_END

static GFXDECODE_START( gfx_xerox820ii )
	GFXDECODE_ENTRY( "chargen", 0x0000, xerox820_charlayout, 0, 1 )
	GFXDECODE_ENTRY( "chargen", 0x0800, xerox820_gfxlayout, 0, 1 )
GFXDECODE_END

static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_300 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_300 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_7 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_ODD )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

/* Machine Drivers */

void xerox820_state::xerox820(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 20_MHz_XTAL / 8);
	m_maincpu->set_addrmap(AS_PROGRAM, &xerox820_state::xerox820_mem);
	m_maincpu->set_addrmap(AS_IO, &xerox820_state::xerox820_io);
	m_maincpu->set_daisy_config(xerox820_daisy_chain);

	/* video hardware */
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_screen_update(FUNC(xerox820_state::screen_update));
	screen.set_raw(10.69425_MHz_XTAL, 700, 0, 560, 260, 0, 240);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_xerox820);
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	/* devices */
	Z80PIO(config, m_kbpio, 20_MHz_XTAL / 8);
	m_kbpio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_kbpio->in_pa_callback().set(FUNC(xerox820_state::kbpio_pa_r));
	m_kbpio->out_pa_callback().set(FUNC(xerox820_state::kbpio_pa_w));
	m_kbpio->in_pb_callback().set(FUNC(xerox820_state::kbpio_pb_r));

	z80pio_device& pio_gp(Z80PIO(config, Z80PIO_GP_TAG, 20_MHz_XTAL / 8));
	pio_gp.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	Z80CTC(config, m_ctc, 20_MHz_XTAL / 8);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->zc_callback<0>().set(m_ctc, FUNC(z80ctc_device::trg1));
	m_ctc->zc_callback<2>().set(m_ctc, FUNC(z80ctc_device::trg3));
	//TIMER(config, "ctc").configure_periodic(FUNC(xerox820_state::ctc_tick), attotime::from_hz(20_MHz_XTAL / 8));

	FD1771(config, m_fdc, 20_MHz_XTAL / 20);
	m_fdc->intrq_wr_callback().set(FUNC(xerox820_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(xerox820_state::fdc_drq_w));
	FLOPPY_CONNECTOR(config, FD1771_TAG":0", xerox820_floppies, "sa400l", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, FD1771_TAG":1", xerox820_floppies, "sa400l", floppy_image_device::default_mfm_floppy_formats);

	Z80SIO(config, m_sio, 20_MHz_XTAL / 8); // MK3884 (SIO/0)
	m_sio->out_txda_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_txd));
	m_sio->out_dtra_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_dtr));
	m_sio->out_rtsa_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_rts));
	m_sio->out_txdb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_txd));
	m_sio->out_dtrb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_dtr));
	m_sio->out_rtsb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_rts));
	m_sio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	rs232_port_device &rs232a(RS232_PORT(config, RS232_A_TAG, default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_sio, FUNC(z80sio_device::rxa_w));
	rs232a.rxd_handler().append(m_sio, FUNC(z80sio_device::synca_w));
	rs232a.cts_handler().set(m_sio, FUNC(z80sio_device::ctsa_w));
	rs232a.dcd_handler().set(m_sio, FUNC(z80sio_device::dcda_w));

	rs232_port_device &rs232b(RS232_PORT(config, RS232_B_TAG, default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_sio, FUNC(z80sio_device::rxb_w));
	rs232b.rxd_handler().append(m_sio, FUNC(z80sio_device::syncb_w));
	rs232b.cts_handler().set(m_sio, FUNC(z80sio_device::ctsb_w));
	rs232b.dcd_handler().set(m_sio, FUNC(z80sio_device::dcdb_w));
	rs232b.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal));

	com8116_device &dbrg(COM8116(config, COM8116_TAG, 5.0688_MHz_XTAL));
	dbrg.fr_handler().set(m_sio, FUNC(z80sio_device::rxca_w));
	dbrg.fr_handler().append(m_sio, FUNC(z80sio_device::txca_w));
	dbrg.ft_handler().set(m_sio, FUNC(z80sio_device::rxtxcb_w));

	XEROX_820_KEYBOARD(config, m_kb, 0);
	m_kb->kbstb_wr_callback().set(m_kbpio, FUNC(z80pio_device::strobe_b));

	// software lists
	SOFTWARE_LIST(config, "flop_list").set_original("xerox820");
	QUICKLOAD(config, "quickload", "com,cpm", attotime::from_seconds(3)).set_load_callback(FUNC(xerox820_state::quickload_cb));
}

void bigboard_state::bigboard(machine_config &config)
{
	xerox820(config);
	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 950).add_route(ALL_OUTPUTS, "mono", 1.00); /* bigboard only */
	TIMER(config, m_beep_timer).configure_generic(FUNC(bigboard_state::beep_timer));
}

void xerox820ii_state::xerox820ii(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &xerox820ii_state::xerox820ii_mem);
	m_maincpu->set_addrmap(AS_IO, &xerox820ii_state::xerox820ii_io);
	m_maincpu->set_daisy_config(xerox820_daisy_chain);

	/* video hardware */
	screen_device &screen(SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER));
	screen.set_screen_update(FUNC(xerox820ii_state::screen_update));
	screen.set_raw(10.69425_MHz_XTAL, 700, 0, 560, 260, 0, 240);

	GFXDECODE(config, "gfxdecode", m_palette, gfx_xerox820ii);
	PALETTE(config, m_palette, palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* devices */
	Z80PIO(config, m_kbpio, 16_MHz_XTAL / 4);
	m_kbpio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_kbpio->in_pa_callback().set(FUNC(xerox820_state::kbpio_pa_r));
	m_kbpio->out_pa_callback().set(FUNC(xerox820_state::kbpio_pa_w));
	m_kbpio->in_pb_callback().set(FUNC(xerox820_state::kbpio_pb_r));

	z80pio_device& pio_gp(Z80PIO(config, Z80PIO_GP_TAG, 16_MHz_XTAL / 4));
	pio_gp.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80pio_device& pio_rd(Z80PIO(config, Z80PIO_RD_TAG, 20_MHz_XTAL / 8));
	pio_rd.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	pio_rd.in_pa_callback().set("sasi_data_in", FUNC(input_buffer_device::read));
	pio_rd.out_pa_callback().set("sasi_data_out", FUNC(output_latch_device::write));
	pio_rd.out_ardy_callback().set(FUNC(xerox820ii_state::rdpio_pardy_w));
	pio_rd.in_pb_callback().set("sasi_ctrl_in", FUNC(input_buffer_device::read));
	pio_rd.out_pb_callback().set(FUNC(xerox820ii_state::rdpio_pb_w));

	Z80CTC(config, m_ctc, 16_MHz_XTAL / 4);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->zc_callback<0>().set(m_ctc, FUNC(z80ctc_device::trg1));
	m_ctc->zc_callback<2>().set(m_ctc, FUNC(z80ctc_device::trg3));
	//TIMER(config, "ctc").configure_periodic(FUNC(xerox820_state::ctc_tick), attotime::from_hz(16_MHz_XTAL / 4));

	FD1797(config, m_fdc, 16_MHz_XTAL / 8);
	m_fdc->intrq_wr_callback().set(FUNC(xerox820_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(xerox820_state::fdc_drq_w));
	FLOPPY_CONNECTOR(config, FD1797_TAG":0", xerox820_floppies, "sa450", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, FD1797_TAG":1", xerox820_floppies, "sa450", floppy_image_device::default_mfm_floppy_formats);

	Z80SIO(config, m_sio, 16_MHz_XTAL / 4); // MK3884 (SIO/0)
	m_sio->out_txda_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_txd));
	m_sio->out_dtra_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_dtr));
	m_sio->out_rtsa_callback().set(RS232_A_TAG, FUNC(rs232_port_device::write_rts));
	m_sio->out_txdb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_txd));
	m_sio->out_dtrb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_dtr));
	m_sio->out_rtsb_callback().set(RS232_B_TAG, FUNC(rs232_port_device::write_rts));
	m_sio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	rs232_port_device &rs232a(RS232_PORT(config, RS232_A_TAG, default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_sio, FUNC(z80sio_device::rxa_w));
	rs232a.cts_handler().set(m_sio, FUNC(z80sio_device::ctsa_w));
	rs232a.dcd_handler().set(m_sio, FUNC(z80sio_device::dcda_w));

	rs232_port_device &rs232b(RS232_PORT(config, RS232_B_TAG, default_rs232_devices, nullptr));
	rs232b.rxd_handler().set(m_sio, FUNC(z80sio_device::rxb_w));
	rs232b.cts_handler().set(m_sio, FUNC(z80sio_device::ctsb_w));
	rs232b.dcd_handler().set(m_sio, FUNC(z80sio_device::dcdb_w));

	com8116_device &dbrg(COM8116(config, COM8116_TAG, 5.0688_MHz_XTAL));
	dbrg.fr_handler().set(m_sio, FUNC(z80sio_device::rxca_w));
	dbrg.fr_handler().append(m_sio, FUNC(z80sio_device::txca_w));
	dbrg.ft_handler().set(m_sio, FUNC(z80sio_device::rxtxcb_w));

	XEROX_820_KEYBOARD(config, m_kb, 0);
	m_kb->kbstb_wr_callback().set(m_kbpio, FUNC(z80pio_device::strobe_b));

	// SASI bus
	SCSI_PORT(config, m_sasibus, 0);
	m_sasibus->set_data_input_buffer("sasi_data_in");
	m_sasibus->bsy_handler().set("sasi_ctrl_in", FUNC(input_buffer_device::write_bit0)).exor(1);
	m_sasibus->msg_handler().set("sasi_ctrl_in", FUNC(input_buffer_device::write_bit1)).exor(1);
	m_sasibus->cd_handler().set("sasi_ctrl_in", FUNC(input_buffer_device::write_bit2)).exor(1);
	m_sasibus->req_handler().set("sasi_ctrl_in", FUNC(input_buffer_device::write_bit3)).exor(1);
	m_sasibus->io_handler().set("sasi_ctrl_in", FUNC(input_buffer_device::write_bit4)).exor(1);
	m_sasibus->set_slot_device(1, "harddisk", SA1403D, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_0));

	output_latch_device &sasi_data_out(OUTPUT_LATCH(config, "sasi_data_out"));
	m_sasibus->set_output_latch(sasi_data_out);
	INPUT_BUFFER(config, "sasi_data_in");
	INPUT_BUFFER(config, "sasi_ctrl_in");

	// software lists
	SOFTWARE_LIST(config, "flop_list").set_original("xerox820ii");
	QUICKLOAD(config, "quickload", "com,cpm", attotime::from_seconds(3)).set_load_callback(FUNC(xerox820_state::quickload_cb));
}

void xerox820ii_state::xerox168(machine_config &config)
{
	xerox820ii(config);
	i8086_cpu_device &i8086(I8086(config, I8086_TAG, 4770000));
	i8086.set_addrmap(AS_PROGRAM, &xerox820ii_state::xerox168_mem);

	/* internal ram */
	RAM(config, m_ram).set_default_size("192K").set_extra_options("320K");
}

void xerox820_state::mk83(machine_config & config)
{
	xerox820(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &xerox820_state::mk83_mem);
}

/* ROMs */

ROM_START( bigboard )
	ROM_REGION( 0x1000, Z80_TAG, 0 )
	ROM_LOAD( "bigboard.u67", 0x0000, 0x0800, CRC(5a85a228) SHA1(d51a2cbd0aae80315bda9530275aabfe8305364e) )

	ROM_REGION( 0x800, "chargen", 0 )
	ROM_LOAD( "bigboard.u73", 0x0000, 0x0800, CRC(10bf0d81) SHA1(7ec73670a4d9d6421a5d6a4c4edc8b7c87923f6c) )
ROM_END

#define rom_mk82 rom_bigboard

ROM_START( x820 )
	ROM_REGION( 0x1000, Z80_TAG, 0 )
	ROM_DEFAULT_BIOS( "v20" )
	ROM_SYSTEM_BIOS( 0, "v10", "Xerox Monitor v1.0" )
	ROMX_LOAD( "x820v10.u64", 0x0000, 0x0800, NO_DUMP, ROM_BIOS(0) )
	ROMX_LOAD( "x820v10.u63", 0x0800, 0x0800, NO_DUMP, ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v20", "Xerox Monitor v2.0" )
	ROMX_LOAD( "x820v20.u64", 0x0000, 0x0800, CRC(2fc227e2) SHA1(b4ea0ae23d281a687956e8a514cb364a1372678e), ROM_BIOS(1) )
	ROMX_LOAD( "x820v20.u63", 0x0800, 0x0800, CRC(bc11f834) SHA1(4fd2b209a6e6ff9b0c41800eb5228c34a0d7f7ef), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "smart23", "MICROCode SmartROM v2.3" )
	ROMX_LOAD( "mxkx25a.u64", 0x0000, 0x0800, CRC(7ec5f100) SHA1(5d0ff35a51aa18afc0d9c20ef99ff5d9d3f2075b), ROM_BIOS(2) )
	ROMX_LOAD( "mxkx25b.u63", 0x0800, 0x0800, CRC(a7543798) SHA1(886e617e1003d13f86f33085cbd49391b77291a3), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS( 3, "plus2", "MICROCode Plus2 v0.2a" )
	ROMX_LOAD( "p2x25a.u64",  0x0000, 0x0800, CRC(3ccd7a8f) SHA1(6e46c88f03fc7289595dd6bec95e23bb13969525), ROM_BIOS(3) )
	ROMX_LOAD( "p2x25b.u63",  0x0800, 0x0800, CRC(1e580391) SHA1(e91f8ce82586df33c0d6d02eb005e8079f4de67d), ROM_BIOS(3) )

	ROM_REGION( 0x800, "chargen", 0 )
	ROM_LOAD( "x820.u92", 0x0000, 0x0800, CRC(b823fa98) SHA1(ad0ea346aa257a53ad5701f4201896a2b3a0f928) )
ROM_END

ROM_START( x820ii )
	ROM_REGION( 0x2000, Z80_TAG, 0 )
	ROM_DEFAULT_BIOS( "v404" )

	ROM_SYSTEM_BIOS( 0, "v400", "Balcones Operating System v4.00" ) // Initial U.S. 3-ROM set: support for double density disks
	ROMX_LOAD( "v400.u33", 0x0000, 0x0800, NO_DUMP, ROM_BIOS(0) )
	ROMX_LOAD( "v400.u34", 0x0800, 0x0800, NO_DUMP, ROM_BIOS(0) )
	ROMX_LOAD( "v400.u35", 0x1000, 0x0800, NO_DUMP, ROM_BIOS(0) )

	ROM_SYSTEM_BIOS( 1, "v401", "Balcones Operating System v4.01" ) // Corrected overflow problem with large data files
	ROMX_LOAD( "v401.u33", 0x0000, 0x0800, NO_DUMP, ROM_BIOS(1) )
	ROMX_LOAD( "v401.u34", 0x0800, 0x0800, NO_DUMP, ROM_BIOS(1) )
	ROMX_LOAD( "v401.u35", 0x1000, 0x0800, NO_DUMP, ROM_BIOS(1) )

	ROM_SYSTEM_BIOS( 2, "v402", "Balcones Operating System v4.02" ) // Rank Xerox (European) boot ROM version of US 4.01
	ROMX_LOAD( "u33.4.02.rom", 0x0000, 0x0800, CRC(d9eb668e) SHA1(6acbef96e4e6526c58e068b7849fb9cce2ea2a10), ROM_BIOS(2) )
	ROMX_LOAD( "u34.4.02.rom", 0x0800, 0x0800, CRC(62181209) SHA1(2238aec096d19af9307bb294532f66f53dd7dfc3), ROM_BIOS(2) )
	ROMX_LOAD( "u35.4.02.rom", 0x1000, 0x0800, CRC(e22fbf6d) SHA1(6c162f79d42611176b0f1c0e8a4eeb07492beca1), ROM_BIOS(2) )
	ROMX_LOAD( "u36.rx11.4.02.rom", 0x1800, 0x0800, CRC(b6a239ce) SHA1(330d28fa8ec006d48d948b1c5e714ffced88fe90), ROM_BIOS(2) ) // supports low-profile keyboard, no input with the ROM present

	ROM_SYSTEM_BIOS( 3, "v403", "Balcones Operating System v4.03" ) // Incorporate programmable communications option and support for the low-profile keyboard (4-ROM set and type-ahead input buffer)
	ROMX_LOAD( "v403.u33", 0x0000, 0x0800, NO_DUMP, ROM_BIOS(3) )
	ROMX_LOAD( "v403.u34", 0x0800, 0x0800, NO_DUMP, ROM_BIOS(3) )
	ROMX_LOAD( "v403.u35", 0x1000, 0x0800, NO_DUMP, ROM_BIOS(3) )
	ROMX_LOAD( "v403.u36", 0x1800, 0x0800, NO_DUMP, ROM_BIOS(3) )

	ROM_SYSTEM_BIOS( 4, "v404", "Balcones Operating System v4.04" ) // Changes sign-on message from Xerox 820-II to Xerox
	ROMX_LOAD( "537p3652.u33", 0x0000, 0x0800, CRC(7807cfbb) SHA1(bd3cc5cc5c59c84a50747aae5c17eb4617b0dbc3), ROM_BIOS(4) )
	ROMX_LOAD( "537p3653.u34", 0x0800, 0x0800, CRC(a9c6c0c3) SHA1(c2da9d1bf0da96e6b8bfa722783e411d2fe6deb9), ROM_BIOS(4) )
	ROMX_LOAD( "537p3654.u35", 0x1000, 0x0800, CRC(a8a07223) SHA1(e8ae1ebf2d7caf76771205f577b88ae493836ac9), ROM_BIOS(4) )
	ROMX_LOAD( "v404.u36", 0x1800, 0x0800, NO_DUMP, ROM_BIOS(4) ) // fitted for low-profile keyboard only

	ROM_SYSTEM_BIOS( 5, "v50", "Balcones Operating System v5.0" ) // Operating system modifications for DEM and new 5.25" disk controller (4 new boot ROMs)
	ROMX_LOAD( "u33.5.0_537p10828.bin", 0x0000, 0x0800, CRC(a17af0f1) SHA1(b1d9a151ed4558f49b3cdc1adbf348b54da48877), ROM_BIOS(5) )
	ROMX_LOAD( "u34.5.0_537p10829.bin", 0x0800, 0x0800, CRC(c9f5182e) SHA1(ac830848614cea984c849a42687ea2944d6765d9), ROM_BIOS(5) )
	ROMX_LOAD( "u35.5.0_537p10830.bin", 0x1000, 0x0800, CRC(278fa75f) SHA1(f47cf9eb30366211280f93a8460523fcc53eebe9), ROM_BIOS(5) )
	ROMX_LOAD( "v500.u36", 0x1800, 0x0800, NO_DUMP, ROM_BIOS(5) )

	ROM_SYSTEM_BIOS( 6, "v50v018", "Balcones Operating System v5.0 v018" ) // shows ROM ERROR
	ROMX_LOAD( "537p10828.u33.5.0.bin", 0x0000, 0x0800, CRC(a17af0f1) SHA1(b1d9a151ed4558f49b3cdc1adbf348b54da48877), ROM_BIOS(6) )
	ROMX_LOAD( "537p10829.u34.5.0.bin", 0x0800, 0x0800, CRC(c9f5182e) SHA1(ac830848614cea984c849a42687ea2944d6765d9), ROM_BIOS(6) )
	ROMX_LOAD( "537p10830.u35.5.0.bin", 0x1000, 0x0800, BAD_DUMP CRC(cc4e1c2b) SHA1(375bbed76d9088dec82b9599cd810727d3e605f3), ROM_BIOS(6) )
	ROMX_LOAD( "537p10831.u36.5.0.bin", 0x1800, 0x0800, CRC(cda7f598) SHA1(08ffd18959e1708136076c82486b8d121a04fa23), ROM_BIOS(6) )

	ROM_REGION( 0x1000, "chargen", 0 )

	ROMX_LOAD( "x820ii.u57", 0x0000, 0x0800, CRC(1a50f600) SHA1(df4470c80611c14fa7ea8591f741fbbecdfe4fd9), ROM_BIOS(0) )
	ROMX_LOAD( "x820ii.u58", 0x0800, 0x0800, CRC(aca4b9b3) SHA1(77f41470b0151945b8d3c3a935fc66409e9157b3), ROM_BIOS(0) )

	ROMX_LOAD( "x820ii.u57", 0x0000, 0x0800, CRC(1a50f600) SHA1(df4470c80611c14fa7ea8591f741fbbecdfe4fd9), ROM_BIOS(1) )
	ROMX_LOAD( "x820ii.u58", 0x0800, 0x0800, CRC(aca4b9b3) SHA1(77f41470b0151945b8d3c3a935fc66409e9157b3), ROM_BIOS(1) )

	ROMX_LOAD( "u57.04.north.rom", 0x0000, 0x0800, CRC(eda727a2) SHA1(292cd8a0dc6699c3a2091b20c0fc63d97a266fbf), ROM_BIOS(2) )
	ROMX_LOAD( "u58.03.north.rom", 0x0800, 0x0800, CRC(a2e514f3) SHA1(8ac22dd0cf0324a857718adf67b41912864893a3), ROM_BIOS(2)  )

	ROMX_LOAD( "x820ii.u57", 0x0000, 0x0800, CRC(1a50f600) SHA1(df4470c80611c14fa7ea8591f741fbbecdfe4fd9), ROM_BIOS(3) )
	ROMX_LOAD( "x820ii.u58", 0x0800, 0x0800, CRC(aca4b9b3) SHA1(77f41470b0151945b8d3c3a935fc66409e9157b3), ROM_BIOS(3) )

	ROMX_LOAD( "x820ii.u57", 0x0000, 0x0800, CRC(1a50f600) SHA1(df4470c80611c14fa7ea8591f741fbbecdfe4fd9), ROM_BIOS(4) )
	ROMX_LOAD( "x820ii.u58", 0x0800, 0x0800, CRC(aca4b9b3) SHA1(77f41470b0151945b8d3c3a935fc66409e9157b3), ROM_BIOS(4) )

	ROMX_LOAD( "x820ii.u57", 0x0000, 0x0800, CRC(1a50f600) SHA1(df4470c80611c14fa7ea8591f741fbbecdfe4fd9), ROM_BIOS(5) )
	ROMX_LOAD( "x820ii.u58", 0x0800, 0x0800, CRC(aca4b9b3) SHA1(77f41470b0151945b8d3c3a935fc66409e9157b3), ROM_BIOS(5) )

	ROMX_LOAD( "x820ii.u57", 0x0000, 0x0800, CRC(1a50f600) SHA1(df4470c80611c14fa7ea8591f741fbbecdfe4fd9), ROM_BIOS(6) )
	ROMX_LOAD( "x820ii.u58", 0x0800, 0x0800, CRC(aca4b9b3) SHA1(77f41470b0151945b8d3c3a935fc66409e9157b3), ROM_BIOS(6) )
ROM_END

ROM_START( x168 )
	ROM_REGION( 0x2000, Z80_TAG, 0 )
	ROM_DEFAULT_BIOS( "v404" )
	ROM_SYSTEM_BIOS( 0, "v404", "Balcones Operating System v4.04" ) // Changes sign-on message from Xerox 820-II to Xerox
	ROMX_LOAD( "537p3652.u33", 0x0000, 0x0800, CRC(7807cfbb) SHA1(bd3cc5cc5c59c84a50747aae5c17eb4617b0dbc3), ROM_BIOS(0) )
	ROMX_LOAD( "537p3653.u34", 0x0800, 0x0800, CRC(a9c6c0c3) SHA1(c2da9d1bf0da96e6b8bfa722783e411d2fe6deb9), ROM_BIOS(0) )
	ROMX_LOAD( "537p3654.u35", 0x1000, 0x0800, CRC(a8a07223) SHA1(e8ae1ebf2d7caf76771205f577b88ae493836ac9), ROM_BIOS(0) )
	ROMX_LOAD( "v404.u36", 0x1800, 0x0800, NO_DUMP, ROM_BIOS(0) ) // fitted for low-profile keyboard only

	ROM_SYSTEM_BIOS( 1, "v50", "Balcones Operating System v5.0" ) // Operating system modifications for DEM and new 5.25" disk controller (4 new boot ROMs)
	ROMX_LOAD( "l5.u33.rom", 0x0000, 0x0800, CRC(a17af0f1) SHA1(b1d9a151ed4558f49b3cdc1adbf348b54da48877), ROM_BIOS(1) )
	ROMX_LOAD( "l5.u34.rom", 0x0800, 0x0800, CRC(c9f5182e) SHA1(ac830848614cea984c849a42687ea2944d6765d9), ROM_BIOS(1) )
	ROMX_LOAD( "l5.u35.rom", 0x1000, 0x0800, BAD_DUMP CRC(44c8dbf8) SHA1(cba925b425a7a5ca68dc9fed10ea33e100704bf4), ROM_BIOS(1) )   // shows ROM ERROR and is different from Xerox 820-II v50
	ROMX_LOAD( "u36.rx024.rom", 0x1800, 0x0800, CRC(a7f1d677) SHA1(8c2a442f3a691f2e181a640d65f767ce3b51d711), ROM_BIOS(1) ) // fitted for low-profile keyboard only

	ROM_REGION( 0x1000, I8086_TAG, 0 )
	ROM_LOAD( "8086.u33", 0x0000, 0x1000, CRC(ee49e3dc) SHA1(a5f20c74fc53f9d695d8894534ab69a39e2c38d8) )

	ROM_REGION( 0x1000, "chargen", 0 )
	ROMX_LOAD( "x820ii.u57", 0x0000, 0x0800, CRC(1a50f600) SHA1(df4470c80611c14fa7ea8591f741fbbecdfe4fd9), ROM_BIOS(0) )
	ROMX_LOAD( "x820ii.u58", 0x0800, 0x0800, CRC(aca4b9b3) SHA1(77f41470b0151945b8d3c3a935fc66409e9157b3), ROM_BIOS(0) )

	ROMX_LOAD( "u57.04.north.rom", 0x0000, 0x0800, CRC(eda727a2) SHA1(292cd8a0dc6699c3a2091b20c0fc63d97a266fbf), ROM_BIOS(1) )
	ROMX_LOAD( "u58.03.north.rom", 0x0800, 0x0800, CRC(a2e514f3) SHA1(8ac22dd0cf0324a857718adf67b41912864893a3), ROM_BIOS(1)  )
ROM_END

ROM_START( mk83 )
	ROM_REGION( 0x1000, Z80_TAG, 0 )
	ROM_LOAD( "2732mk83.bin", 0x0000, 0x1000, CRC(a845c7e1) SHA1(3ccf629c5cd384953794ac4a1d2b45678bd40e92) )

	ROM_REGION( 0x800, "chargen", 0 )
	ROM_LOAD( "2716mk83.bin", 0x0000, 0x0800, CRC(10bf0d81) SHA1(7ec73670a4d9d6421a5d6a4c4edc8b7c87923f6c) )
ROM_END

ROM_START( mojmikro )
	ROM_REGION( 0x2000, Z80_TAG, 0 )
	ROM_LOAD( "mikro-s.u67", 0x0000, 0x0800, CRC(56a329a8) SHA1(22a5d6bef121d14eddc0c25e85b8a73f6ca6a65f))
	ROM_REGION( 0x0800, "chargen", ROMREGION_ERASEFF ) // MMSCHAR YU 8.1.1987
	ROM_LOAD( "mmschar-yu.u73", 0x0000, 0x0800, CRC(ebcc72d3) SHA1(1c3f90b1d2e57586dcd32385d0aaa09e56662e32))
ROM_END

/* System Drivers */

//    YEAR  NAME      PARENT    COMPAT  MACHINE     INPUT     CLASS             INIT        COMPANY                       FULLNAME        FLAGS
COMP( 1980, bigboard, 0,        0,      bigboard,   xerox820, bigboard_state,   empty_init, "Digital Research Computers", "Big Board",    MACHINE_NOT_WORKING )
COMP( 1981, x820,     bigboard, 0,      xerox820,   xerox820, xerox820_state,   empty_init, "Xerox",                      "Xerox 820",    MACHINE_NO_SOUND_HW )
COMP( 1982, mk82,     bigboard, 0,      bigboard,   xerox820, bigboard_state,   empty_init, "Scomar",                     "MK-82",        MACHINE_NOT_WORKING )
COMP( 1983, x820ii,   0,        0,      xerox820ii, xerox820, xerox820ii_state, empty_init, "Xerox",                      "Xerox 820-II", MACHINE_NOT_WORKING )
COMP( 1983, x168,     x820ii,   0,      xerox168,   xerox820, xerox820ii_state, empty_init, "Xerox",                      "Xerox 16/8",   MACHINE_NOT_WORKING )
COMP( 1983, mk83,     bigboard, 0,      mk83,       xerox820, xerox820_state,   empty_init, "Scomar",                     "MK-83",        MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW )
COMP( 1985, mojmikro, bigboard, 0,      bigboard,   xerox820, bigboard_state,   empty_init, "<unknown>",                  "Moj mikro Slovenija",  MACHINE_NOT_WORKING )



xm200.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for Orla XM200 PCM sound module.

***************************************************************************/

#include "emu.h"
#include "cpu/st9/st905x.h"


namespace {

class xm200_state : public driver_device
{
public:
	xm200_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void xm200(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<st9_device> m_maincpu;
};


void xm200_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("program", 0);
	map(0xe000, 0xefff).ram();
}


static INPUT_PORTS_START(xm200)
INPUT_PORTS_END

void xm200_state::xm200(machine_config &config)
{
	ST90R50(config, m_maincpu, 11'000'000); // type and clock guessed
	m_maincpu->set_addrmap(AS_PROGRAM, &xm200_state::mem_map);
}

// The dump claims to be from "Commander" rather than Orla. This might be some sort of regional branding,
// since Orla released other sound modules using that name.
ROM_START(xm200)
	ROM_REGION(0x8000, "program", 0)
	ROM_LOAD("xm200_st m27c25b-orla ok300_q.bin", 0x0000, 0x8000, CRC(fa5dc621) SHA1(867516171028c278eccdbe65ea750deb07a684ff))
	// 0x0100: "***** Orla OK300 Keyboard ***** (c) Richard Watts Associates July 90, V1.0"

	ROM_REGION(0x20000, "pcm", 0)
	ROM_LOAD("s r_s 27c512-20 fa.bin", 0x00000, 0x10000, CRC(dab36166) SHA1(c98096c699fcf23e42571ce58e46bf40f569aa1f))
	ROM_LOAD("acc_st m27c512-15fi.bin", 0x10000, 0x10000, CRC(15a7c54e) SHA1(e5ed7806060b6b74f0e71960af5eb5307d1c88d7))
ROM_END

} // anonymous namespace


SYST(1990, xm200, 0, 0, xm200, xm200, xm200_state, empty_init, "Orla", "XM200 Orchestra Module", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



xor100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/****************************************************************************************************

XOR S-100-12

XOR Data Science was apparently a 1982 reincorporation of a Huntington Beach-based
company previously known as Delta Products. At least some of the S-100 boards used
in XOR's systems were originally developed and documented under the former company
name.

*****************************************************************************************************

All input must be in upper case.
Summary of Monitor commands:

D xxxx yyyy           = dump memory to screen
F xxxx yyyy zz        = fill memory from xxxx to yyyy-1 with zz
G xxxx                = execute program at xxxx
H xxxx yyyy aa bb...  = Search memory for a string of bytes
L xxxx                = edit memory (. to exit)
M xxxx yyyy zzzz      = Move (copy) memory
R                     = Read cassette (not in all bios versions)
V xxxx                = Ascii dump of memory to the screen (cr to continue, space to exit)
W                     = Write cassette (not in all bios versions)
X n                   = Select a bank (0 works, others freeze)
^C                    = Boot from floppy

Note some of the commands are a bit buggy, eg F doesn't fill the last byte


TODO:
- Fix floppy. It needs to WAIT the cpu whenever port 0xFC is read, wait
  for either DRQ or INTRQ to assert, then release the cpu and then do the
  actual port read.
- The only available disks crash MAME when loaded.
- honor jumper settings
- CTC signal header
- serial printer
- cassette (no information, assumed to be on another card)

*****************************************************************************************************/


#include "emu.h"

#include "bus/centronics/ctronics.h"
#include "bus/rs232/rs232.h"
#include "bus/s100/s100.h"
#include "cpu/z80/z80.h"
#include "imagedev/floppy.h"
#include "machine/com8116.h"
#include "machine/i8251.h"
#include "machine/i8255.h"
#include "machine/ram.h"
#include "machine/wd_fdc.h"
#include "machine/z80ctc.h"

namespace {

#define SCREEN_TAG      "screen"
#define Z80_TAG         "5b"
#define I8251_A_TAG     "12b"
#define I8251_B_TAG     "14b"
#define I8255A_TAG      "8a"
#define COM5016_TAG     "15c"
#define Z80CTC_TAG      "11b"
#define WD1795_TAG      "wd1795"
#define CENTRONICS_TAG  "centronics"
#define RS232_A_TAG     "rs232a"
#define RS232_B_TAG     "rs232b"
#define S100_TAG        "s100"

class xor100_state : public driver_device
{
public:
	xor100_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, Z80_TAG)
		, m_uart_a(*this, I8251_A_TAG)
		, m_uart_b(*this, I8251_B_TAG)
		, m_fdc(*this, WD1795_TAG)
		, m_ctc(*this, Z80CTC_TAG)
		, m_ram(*this, RAM_TAG)
		, m_centronics(*this, CENTRONICS_TAG)
		, m_s100(*this, S100_TAG)
		, m_floppy(*this, WD1795_TAG":%u", 0U)
		, m_rom(*this, Z80_TAG)
		, m_bank1(*this, "bank1")
		, m_bank2(*this, "bank2")
		, m_bank3(*this, "bank3")
	{ }

	void xor100(machine_config &config);

private:
	void mmu_w(uint8_t data);
	void prom_toggle_w(uint8_t data);
	uint8_t prom_disable_r();
	uint8_t fdc_wait_r();
	void fdc_dcont_w(uint8_t data);
	void fdc_dsel_w(uint8_t data);
	void fdc_intrq_w(int state);
	void fdc_drq_w(int state);

	uint8_t i8255_pc_r();
	void ctc_z0_w(int state);
	void ctc_z1_w(int state);
	void ctc_z2_w(int state);
	void write_centronics_busy(int state);
	void write_centronics_select(int state);

	void xor100_io(address_map &map) ATTR_COLD;
	void xor100_mem(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void bankswitch();
	void post_load();

	required_device<cpu_device> m_maincpu;
	required_device<i8251_device> m_uart_a;
	required_device<i8251_device> m_uart_b;
	required_device<fd1795_device> m_fdc;
	required_device<z80ctc_device> m_ctc;
	required_device<ram_device> m_ram;
	required_device<centronics_device> m_centronics;
	required_device<s100_bus_device> m_s100;
	required_device_array<floppy_connector, 4> m_floppy;
	required_memory_region m_rom;
	required_memory_bank m_bank1;
	required_memory_bank m_bank2;
	required_memory_bank m_bank3;

	// memory state
	int m_mode = 0;
	int m_bank = 0;

	// floppy state
	bool m_fdc_irq = false;
	bool m_fdc_drq = false;
	int m_fdc_dden = 0;

	int m_centronics_busy = 0;
	int m_centronics_select = 0;
};

/* Read/Write Handlers */

enum
{
	EPROM_0000 = 0,
	EPROM_F800,
	EPROM_OFF
};

void xor100_state::bankswitch()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	int banks = m_ram->size() / 0x10000;

	switch (m_mode)
	{
	case EPROM_0000:
		if (m_bank < banks)
		{
			program.install_write_bank(0x0000, 0xffff, m_bank1);
			m_bank1->set_entry(1 + m_bank);
		}
		else
		{
			program.unmap_write(0x0000, 0xffff);
		}

		program.install_read_bank(0x0000, 0x07ff, 0xf000, m_bank2);
		program.install_read_bank(0xf800, 0xffff, m_bank3);
		m_bank2->set_entry(0);
		m_bank3->set_entry(0);
		break;

	case EPROM_F800:
		if (m_bank < banks)
		{
			program.install_write_bank(0x0000, 0xffff, m_bank1);
			program.install_read_bank(0x0000, 0xf7ff, m_bank2);
			m_bank1->set_entry(1 + m_bank);
			m_bank2->set_entry(1 + m_bank);
		}
		else
		{
			program.unmap_write(0x0000, 0xffff);
			program.unmap_read(0x0000, 0xf7ff);
		}

		program.install_read_bank(0xf800, 0xffff, m_bank3);
		m_bank3->set_entry(0);
		break;

	case EPROM_OFF:
		if (m_bank < banks)
		{
			program.install_write_bank(0x0000, 0xffff, m_bank1);
			program.install_read_bank(0x0000, 0xf7ff, m_bank2);
			program.install_read_bank(0xf800, 0xffff, m_bank3);
			m_bank1->set_entry(1 + m_bank);
			m_bank2->set_entry(1 + m_bank);
			m_bank3->set_entry(1 + m_bank);
		}
		else
		{
			program.unmap_write(0x0000, 0xffff);
			program.unmap_read(0x0000, 0xf7ff);
			program.unmap_read(0xf800, 0xffff);
		}
		break;
	}
}

void xor100_state::mmu_w(uint8_t data)
{
	/*

	    bit     description

	    0       A16
	    1       A17
	    2       A18
	    3       A19
	    4
	    5
	    6
	    7

	*/

	m_bank = data & 0x07;

	bankswitch();
}

void xor100_state::prom_toggle_w(uint8_t data)
{
	switch (m_mode)
	{
	case EPROM_OFF: m_mode = EPROM_F800; break;
	case EPROM_F800: m_mode = EPROM_OFF; break;
	}

	bankswitch();
}

uint8_t xor100_state::prom_disable_r()
{
	m_mode = EPROM_F800;

	bankswitch();

	return 0xff;
}

uint8_t xor100_state::fdc_wait_r()
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4
	    5
	    6
	    7       FDC IRQ

	*/

	if (!machine().side_effects_disabled())
	{
		if (!m_fdc_irq && !m_fdc_drq)
		{
			//m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE);
		}
	}

	return m_fdc_irq ? 0x7f : 0xff;
}

void xor100_state::fdc_dcont_w(uint8_t data)
{
	/*

	    bit     description

	    0       DS0
	    1       DS1
	    2       DS2
	    3       DS3
	    4
	    5
	    6
	    7       _HLSTB

	*/

	// drive select
	floppy_image_device *floppy = nullptr;

	for (int n = 0; n < 4; n++)
		if (BIT(data, n))
			floppy = m_floppy[n]->get_device();

	m_fdc->set_floppy(floppy);

	if (floppy) floppy->mon_w(0);
}

void xor100_state::fdc_dsel_w(uint8_t data)
{
	/*

	    bit     description

	    0       J
	    1       K
	    2
	    3
	    4
	    5
	    6
	    7

	*/

	switch (data & 0x03)
	{
	case 0: break;
	case 1: m_fdc_dden = 1; break;
	case 2: m_fdc_dden = 0; break;
	case 3: m_fdc_dden = !m_fdc_dden; break;
	}

	m_fdc->dden_w(m_fdc_dden);
}

/* Memory Maps */

void xor100_state::xor100_mem(address_map &map)
{
	map(0x0000, 0xffff).bankw("bank1");
	map(0x0000, 0xf7ff).bankr("bank2");
	map(0xf800, 0xffff).bankr("bank3");
}

void xor100_state::xor100_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x01).rw(m_uart_a, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x02, 0x03).rw(m_uart_b, FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x04, 0x07).rw(I8255A_TAG, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x08, 0x08).w(FUNC(xor100_state::mmu_w));
	map(0x09, 0x09).w(FUNC(xor100_state::prom_toggle_w));
	map(0x0a, 0x0a).r(FUNC(xor100_state::prom_disable_r));
	map(0x0b, 0x0b).portr("DSW0").w(COM5016_TAG, FUNC(com8116_device::stt_str_w));
	map(0x0c, 0x0f).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0xf8, 0xfb).lrw8(
					NAME([this](offs_t offset) { return m_fdc->read(offset) ^ 0xff; }),
					NAME([this](offs_t offset, u8 data) { m_fdc->write(offset, data ^ 0xff); }));
	map(0xfc, 0xfc).rw(FUNC(xor100_state::fdc_wait_r), FUNC(xor100_state::fdc_dcont_w));
	map(0xfd, 0xfd).w(FUNC(xor100_state::fdc_dsel_w));
}

/* Input Ports */

static INPUT_PORTS_START( xor100 )

	PORT_START("DSW0")
	PORT_DIPNAME( 0x0f, 0x05, "Serial Port A" )
	PORT_DIPSETTING(    0x00, "50 baud" )
	PORT_DIPSETTING(    0x01, "75 baud" )
	PORT_DIPSETTING(    0x02, "110 baud" )
	PORT_DIPSETTING(    0x03, "134.5 baud" )
	PORT_DIPSETTING(    0x04, "150 baud" )
	PORT_DIPSETTING(    0x05, "300 baud" )
	PORT_DIPSETTING(    0x06, "600 baud" )
	PORT_DIPSETTING(    0x07, "1200 baud" )
	PORT_DIPSETTING(    0x08, "1800 baud" )
	PORT_DIPSETTING(    0x09, "2000 baud" )
	PORT_DIPSETTING(    0x0a, "2400 baud" )
	PORT_DIPSETTING(    0x0b, "3600 baud" )
	PORT_DIPSETTING(    0x0c, "4800 baud" )
	PORT_DIPSETTING(    0x0d, "7200 baud" )
	PORT_DIPSETTING(    0x0e, "9600 baud" )
	PORT_DIPSETTING(    0x0f, "19200 baud" )
	PORT_DIPNAME( 0xf0, 0xe0, "Serial Port B" )
	PORT_DIPSETTING(    0x00, "50 baud" )
	PORT_DIPSETTING(    0x10, "75 baud" )
	PORT_DIPSETTING(    0x20, "110 baud" )
	PORT_DIPSETTING(    0x30, "134.5 baud" )
	PORT_DIPSETTING(    0x40, "150 baud" )
	PORT_DIPSETTING(    0x50, "300 baud" )
	PORT_DIPSETTING(    0x60, "600 baud" )
	PORT_DIPSETTING(    0x70, "1200 baud" )
	PORT_DIPSETTING(    0x80, "1800 baud" )
	PORT_DIPSETTING(    0x90, "2000 baud" )
	PORT_DIPSETTING(    0xa0, "2400 baud" )
	PORT_DIPSETTING(    0xb0, "3600 baud" )
	PORT_DIPSETTING(    0xc0, "4800 baud" )
	PORT_DIPSETTING(    0xd0, "7200 baud" )
	PORT_DIPSETTING(    0xe0, "9600 baud" )
	PORT_DIPSETTING(    0xf0, "19200 baud" )

	PORT_START("J1")
	PORT_CONFNAME( 0x01, 0x01, "J1 Wait State")
	PORT_CONFSETTING( 0x00, "No Wait States" )
	PORT_CONFSETTING( 0x01, "1 M1 Wait State" )

	PORT_START("J2")
	PORT_CONFNAME( 0x01, 0x01, "J2 CPU Speed")
	PORT_CONFSETTING( 0x00, "2 MHz" )
	PORT_CONFSETTING( 0x01, "4 MHz" )

	PORT_START("J3")
	PORT_CONFNAME( 0x01, 0x00, "J3")
	PORT_CONFSETTING( 0x00, "" )
	PORT_CONFSETTING( 0x01, "" )

	PORT_START("J4-J5")
	PORT_CONFNAME( 0x01, 0x01, "J4/J5 EPROM Type")
	PORT_CONFSETTING( 0x00, "2708" )
	PORT_CONFSETTING( 0x01, "2716" )

	PORT_START("J6")
	PORT_CONFNAME( 0x01, 0x01, "J6 EPROM")
	PORT_CONFSETTING( 0x00, "Disabled" )
	PORT_CONFSETTING( 0x01, "Enabled" )

	PORT_START("J7")
	PORT_CONFNAME( 0x01, 0x00, "J7 I/O Port Addresses")
	PORT_CONFSETTING( 0x00, "00-0F" )
	PORT_CONFSETTING( 0x01, "10-1F" )

	PORT_START("J8")
	PORT_CONFNAME( 0x01, 0x00, "J8")
	PORT_CONFSETTING( 0x00, "" )
	PORT_CONFSETTING( 0x01, "" )

	PORT_START("J9")
	PORT_CONFNAME( 0x01, 0x01, "J9 Power On RAM")
	PORT_CONFSETTING( 0x00, "Enabled" )
	PORT_CONFSETTING( 0x01, "Disabled" )
INPUT_PORTS_END

/* Printer 8255A Interface */

void xor100_state::write_centronics_busy(int state)
{
	m_centronics_busy = state;
}

void xor100_state::write_centronics_select(int state)
{
	m_centronics_select = state;
}

uint8_t xor100_state::i8255_pc_r()
{
	/*

	    bit     description

	    PC0
	    PC1
	    PC2
	    PC3
	    PC4     ON LINE
	    PC5     BUSY
	    PC6     _ACK
	    PC7

	*/

	uint8_t data = 0;

	/* on line */
	data |= m_centronics_select << 4;

	/* busy */
	data |= m_centronics_busy << 5;

	return data;
}

/* Z80-CTC Interface */

void xor100_state::ctc_z0_w(int state)
{
}

void xor100_state::ctc_z1_w(int state)
{
}

void xor100_state::ctc_z2_w(int state)
{
}

/* WD1795-02 Interface */

static void xor100_floppies(device_slot_interface &device)
{
	device.option_add("8ssdd", FLOPPY_8_SSDD); // Shugart SA-100
}

void xor100_state::fdc_intrq_w(int state)
{
	m_fdc_irq = state;
	m_ctc->trg0(state);

	if (state)
		m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, CLEAR_LINE);
}

void xor100_state::fdc_drq_w(int state)
{
	m_fdc_drq = state;

	if (state)
		m_maincpu->set_input_line(Z80_INPUT_LINE_WAIT, CLEAR_LINE);
}


static void xor100_s100_cards(device_slot_interface &device)
{
}

/* Machine Initialization */

void xor100_state::machine_start()
{
	int banks = m_ram->size() / 0x10000;
	uint8_t *ram = m_ram->pointer();
	uint8_t *rom = m_rom->base();
	m_bank = 0;

	/* setup memory banking */
	membank("bank1")->configure_entries(1, banks, ram, 0x10000);
	membank("bank2")->configure_entry(0, rom);
	membank("bank2")->configure_entries(1, banks, ram, 0x10000);
	membank("bank3")->configure_entry(0, rom);
	membank("bank3")->configure_entries(1, banks, ram + 0xf800, 0x10000);

	machine().save().register_postload(save_prepost_delegate(FUNC(xor100_state::post_load), this));

	/* register for state saving */
	save_item(NAME(m_mode));
	save_item(NAME(m_bank));
	save_item(NAME(m_fdc_irq));
	save_item(NAME(m_fdc_drq));
	save_item(NAME(m_fdc_dden));
	save_item(NAME(m_centronics_busy));
	save_item(NAME(m_centronics_select));
}

void xor100_state::machine_reset()
{
	m_mode = EPROM_0000;

	bankswitch();
}

void xor100_state::post_load()
{
	bankswitch();
}

/* Machine Driver */

void xor100_state::xor100(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 8_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &xor100_state::xor100_mem);
	m_maincpu->set_addrmap(AS_IO, &xor100_state::xor100_io);

	/* devices */
	I8251(config, m_uart_a, 0/*8_MHz_XTAL / 2,*/);
	m_uart_a->txd_handler().set(RS232_A_TAG, FUNC(rs232_port_device::write_txd));
	m_uart_a->dtr_handler().set(RS232_A_TAG, FUNC(rs232_port_device::write_dtr));
	m_uart_a->rts_handler().set(RS232_A_TAG, FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232a(RS232_PORT(config, RS232_A_TAG, default_rs232_devices, nullptr));
	rs232a.rxd_handler().set(m_uart_a, FUNC(i8251_device::write_rxd));
	rs232a.dsr_handler().set(m_uart_a, FUNC(i8251_device::write_dsr));

	I8251(config, m_uart_b, 0/*8_MHz_XTAL / 2,*/);
	m_uart_b->txd_handler().set(RS232_B_TAG, FUNC(rs232_port_device::write_txd));
	m_uart_b->dtr_handler().set(RS232_B_TAG, FUNC(rs232_port_device::write_dtr));
	m_uart_b->rts_handler().set(RS232_B_TAG, FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232b(RS232_PORT(config, RS232_B_TAG, default_rs232_devices, "terminal"));
	rs232b.rxd_handler().set(m_uart_b, FUNC(i8251_device::write_rxd));
	rs232b.dsr_handler().set(m_uart_b, FUNC(i8251_device::write_dsr));
	rs232b.cts_handler().set(m_uart_b, FUNC(i8251_device::write_cts));

	com8116_device &brg(COM8116(config, COM5016_TAG, 5.0688_MHz_XTAL));
	brg.fr_handler().set(m_uart_a, FUNC(i8251_device::write_txc));
	brg.fr_handler().append(m_uart_a, FUNC(i8251_device::write_rxc));
	brg.ft_handler().set(m_uart_b, FUNC(i8251_device::write_txc));
	brg.ft_handler().append(m_uart_b, FUNC(i8251_device::write_rxc));

	i8255_device &ppi(I8255A(config, I8255A_TAG));
	ppi.out_pa_callback().set("cent_data_out", FUNC(output_latch_device::write));
	ppi.out_pb_callback().set(m_centronics, FUNC(centronics_device::write_strobe));
	ppi.in_pc_callback().set(FUNC(xor100_state::i8255_pc_r));

	Z80CTC(config, m_ctc, 8_MHz_XTAL / 2);
	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_ctc->zc_callback<0>().set(FUNC(xor100_state::ctc_z0_w));
	m_ctc->zc_callback<1>().set(FUNC(xor100_state::ctc_z1_w));
	m_ctc->zc_callback<2>().set(FUNC(xor100_state::ctc_z2_w));

	FD1795(config, m_fdc, 8_MHz_XTAL / 4);
	m_fdc->intrq_wr_callback().set(FUNC(xor100_state::fdc_intrq_w));
	m_fdc->drq_wr_callback().set(FUNC(xor100_state::fdc_drq_w));

	FLOPPY_CONNECTOR(config, m_floppy[0], xor100_floppies, "8ssdd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[1], xor100_floppies, "8ssdd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[2], xor100_floppies, nullptr,    floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppy[3], xor100_floppies, nullptr,    floppy_image_device::default_mfm_floppy_formats);

	CENTRONICS(config, m_centronics, centronics_devices, "printer");
	m_centronics->ack_handler().set(I8255A_TAG, FUNC(i8255_device::pc4_w));
	m_centronics->busy_handler().set(FUNC(xor100_state::write_centronics_busy));
	m_centronics->select_handler().set(FUNC(xor100_state::write_centronics_select));

	output_latch_device &cent_data_out(OUTPUT_LATCH(config, "cent_data_out"));
	m_centronics->set_output_latch(cent_data_out);

	// S-100
	S100_BUS(config, m_s100, 8_MHz_XTAL / 4);
	//m_s100->rdy().set_inputline(m_maincpu, Z80_INPUT_LINE_WAIT);
	S100_SLOT(config, S100_TAG ":1", xor100_s100_cards, nullptr);
	S100_SLOT(config, S100_TAG ":2", xor100_s100_cards, nullptr);
	S100_SLOT(config, S100_TAG ":3", xor100_s100_cards, nullptr);
	S100_SLOT(config, S100_TAG ":4", xor100_s100_cards, nullptr);
	S100_SLOT(config, S100_TAG ":5", xor100_s100_cards, nullptr);
	S100_SLOT(config, S100_TAG ":6", xor100_s100_cards, nullptr);
	S100_SLOT(config, S100_TAG ":7", xor100_s100_cards, nullptr);
	S100_SLOT(config, S100_TAG ":8", xor100_s100_cards, nullptr);
	S100_SLOT(config, S100_TAG ":9", xor100_s100_cards, nullptr);
	S100_SLOT(config, S100_TAG ":10", xor100_s100_cards, nullptr);

	/* internal ram */
	RAM(config, RAM_TAG).set_default_size("64K").set_extra_options("128K,192K,256K,320K,384K,448K,512K");
}

/* ROMs */

ROM_START( xor100 )
	ROM_REGION( 0x800, Z80_TAG, 0 )
	ROM_SYSTEM_BIOS( 0, "v185", "v1.85" )
	ROMX_LOAD( "xp 185.8b", 0x000, 0x800, CRC(0d0bda8d) SHA1(11c83f7cd7e6a570641b44a2f2cc5737a7dd8ae3), ROM_BIOS(0) )
ROM_END

} // anonymous namespace

/* System Drivers */

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   STATE         INIT        COMPANY             FULLNAME        FLAGS
COMP( 1980, xor100, 0,      0,      xor100,  xor100, xor100_state, empty_init, "XOR Data Science", "XOR S-100-12", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )



xpander.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:m1macrophage

/*
The Oberheim Xpander is a 6-voice digitally-controlled analog synthesizer, with
digital modulation sources (LFOs, EGs). Each voice can be configured
independently, which provides a multitimbrality of 6. The Xpander does not have
a keyboard. It is controlled by MIDI, and can accept CV/gate in.

The user interface consists of multiple buttons, 6 rotary encoders, and 3 vacuum
fluorescent displays. There are individual outputs for each voice, along with
left, right and mono outs. Other than MIDI, inputs consist of 6 CV and 2 pedal
inputs, 6 gate and 1 trigger inputs (user-configurable as active high or active
low), and a control input ("chain advance", active low).

The Xpander has two 6809-based computers. The main computer scans for button
presses, drives the VFDs, interprets the digital and analog inputs (MIDI, CVs,
triggers, etc.), and sends parameters to the voice computer.

The voice board consists of 6 analog voices controlled by the voice computer.

Each voice is built around a CEM3374 (dual oscillator) and a CEM3372 (VCF and
VCA). VCO2 can modulate either VCO1 or the VCF to produce FM effects. There is
also circuitry to generate pulse waves out of the saw ones, and to mix in noise.
The noise source is shared for all voices. The CEM3372 is combined with a 4051
MUX and other support circuitry to implement 16 different filter modes.

The voice computer generates 9 control voltages (CVs) for each voice:
- VCO pitch (1 and 2).
- VCO pulse-width (1 and 2).
- VCO volume (1 and 2).
- VCA amplitude.
- VCF cutoff frequency.
- VCF resonance.

There are no analog LFOs or EGs. Those are implemented digitally, and their
effect is incorporated in the CV outputs. All 54 (6 x 9) CVs are generated using
a single 14-bit DAC, whose output is time-multiplexed to 54 Sample and Hold
(S&H) circuits. The CV generation circuit can operate in a "high resolution"
mode which surpases 14 bits (see voice_dac_enable_w()). This is used for pitch
CVs.

The voice computer also selects VCO waveforms, switches between different VCF
modes, etc., by controlling digital switches and multiplexers. Finally, it also
controls the routing and amount of FM by configuring an MDAC (AD7523, one per
voice).

PCBoards:
- Processor board: main computer.
- Pot board: buttons, rotary encoders, inputs, outputs.
- Display board: control of VFDS.
- Voice board: 6 analog voices, voice computer.
- Power supply.

This driver is based on the Xpander's service manual and schematics, and is
intended as an educational tool. There is no attempt to emulate audio.
*/

#include "emu.h"

#include "bus/midi/midi.h"
#include "cpu/m6809/m6809.h"
#include "machine/6850acia.h"
#include "machine/adc0804.h"
#include "machine/clock.h"
#include "machine/nvram.h"
#include "machine/output_latch.h"
#include "machine/pit8253.h"
#include "machine/rescap.h"
#include "machine/timer.h"
#include "video/pwm.h"

#include <algorithm>
#include <array>

#include "oberheim_xpander.lh"

#define LOG_CV_IN       (1U << 1)
#define LOG_SWITCHES    (1U << 2)
#define LOG_ENCODERS    (1U << 3)
#define LOG_CPU_COMMS   (1U << 4)
#define LOG_FIRQ_TIMER  (1U << 5)
#define LOG_VOICE_TIMER (1U << 6)
#define LOG_DAC         (1U << 7)
#define LOG_DAC_VERBOSE (1U << 8)
#define LOG_MEM_PROTECT (1U << 9)

#define VERBOSE (LOG_GENERAL | LOG_ENCODERS | LOG_FIRQ_TIMER | LOG_DAC | LOG_MEM_PROTECT)
//#define LOG_OUTPUT_FUNC osd_printf_info

#include "logmacro.h"

namespace {

constexpr const char MAINCPU_TAG[] = "main_68b09";
constexpr const char VOICECPU_TAG[] = "voice_68b09";
constexpr const char NVRAM_A_TAG[] = "nvram_a";
constexpr const char NVRAM_B_TAG[] = "nvram_b";
constexpr const char VOICERAM_TAG[] = "voiceram";

constexpr const int NUM_ENCODER_POSITIONS = 30;

class xpander_state : public driver_device
{
public:
	xpander_state(const machine_config &mconfig, device_type type, const char *tag) ATTR_COLD
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, MAINCPU_TAG)
		, m_midiacia(*this, "midiacia")
		, m_adc(*this, "adc")
		, m_firq_timer(*this, "firq_timer")
		, m_nvram_a_view(*this, "nvram_a_view")
		, m_rom_0_view(*this, "rom_0_view")
		, m_switch_io(*this, "switches_%d", 0U)
		, m_memory_protect_io(*this, "memory_protect")
		, m_gate_io(*this, "gate_inputs")
		, m_cv_io(*this, "cv_in_%d", 1U)
		, m_pedal_io(*this, "pedal_%d", 1U)
		, m_vfd_devices(*this, "vfd_%d", 0U)
		, m_vfd_outputs(*this, "vfd_%u_char_%u", 1U, 1U)
		, m_cassmute(*this, "cassmute")
		, m_voicecpu(*this, VOICECPU_TAG)
		, m_voicepit(*this, "voice_pit_8253")
		, m_voiceram(*this, VOICERAM_TAG)
		, m_fm_mdac(*this, "voice_%u_fm_mdac", 1U)
		, m_filter_mode(*this, "voice_%u_filter_mode", 1U)
		, m_noise(*this, "voice_%u_noise", 1U)
		, m_pan(*this, "voice_%u_pan", 1U)
		, m_saw1(*this, "voice_%u_saw1", 1U)
		, m_saw2(*this, "voice_%u_saw2", 1U)
		, m_tri1(*this, "voice_%u_tri1", 1U)
		, m_tri2(*this, "voice_%u_tri2", 1U)
		, m_vcofm(*this, "voice_%u_vcofm", 1U)
		, m_sync(*this, "voice_%u_sync", 1U)
	{
		for (auto &cv : m_cv)
			std::fill(cv.begin(), cv.end(), 0.0F);
		for (auto &fast : m_fast)
			std::fill(fast.begin(), fast.end(), false);
	}

	void xpander(machine_config &config) ATTR_COLD;

	DECLARE_INPUT_CHANGED_MEMBER(encoder_moved);
	DECLARE_INPUT_CHANGED_MEMBER(memory_protect_changed);

protected:
	void machine_start() override ATTR_COLD;

private:
	TIMER_DEVICE_CALLBACK_MEMBER(firq_timer_elapsed);
	void firq_timer_preset_w(u8 data);

	u8 proc_datain_r();
	u8 gate_r();
	u8 switch_r(offs_t offset);
	u8 encoder_dir_r();
	u8 encoder_sw_r();

	u8 cv_in_r();
	u8 adc_r(offs_t offset);
	void adc_w(offs_t offset, u8 data);

	void refresh_voicecpu_halt_line();

	void cass_out_w(u8 data);
	void haltset_w(u8 data);
	void display_w(offs_t offset, u8 data);
	void display_output_w(int display, offs_t offset, u32 data);

	u8 voice_datain_r();
	void voice_dataout_w(u8 data);
	void voice_latch0_w(offs_t offset, u8 data);
	void voice_latch1_w(offs_t offset, u8 data);
	void voice_latch2_w(offs_t offset, u8 data);

	float get_dac_v() const;
	void voice_update_cv(u8 voice, u8 cv_index, float cv, bool fast);
	void voice_update_resonance_cv(u8 voice, float cv);
	void voice_dac_enable_w(offs_t offset, u8 data);
	void voice_dac_clear_w(u8 data);
	void voice_dac_w(offs_t offset, u8 data);

	void voicepit_out0_changed(int state);
	void voicepit_out2_changed(int state);

	void maincpu_map(address_map &map) ATTR_COLD;
	void voicecpu_map(address_map &map) ATTR_COLD;

	static constexpr const u16 LOW7_MASK = 0x7f;
	static constexpr const u16 HIGH7_MASK = LOW7_MASK << 7;
	static constexpr const int NUM_VOICES = 6;
	static constexpr const int NUM_CVS = 9;
	static constexpr const int RES_CV_INDEX = 8;
	static constexpr const char *CV_NAMES[NUM_CVS] =
	{
		"VCA", "PW1", "VOLA", "VCOF1", "VOLB", "VCFF", "PW2", "VCOF2", "RES"
	};

	// Main computer.
	required_device<mc6809_device> m_maincpu;
	required_device<acia6850_device> m_midiacia;
	required_device<adc0804_device> m_adc;
	required_device<timer_device> m_firq_timer;  // 40103 presetable timer (U12).
	memory_view m_nvram_a_view;
	memory_view m_rom_0_view;
	required_ioport_array<8> m_switch_io;
	required_ioport m_memory_protect_io;
	required_ioport m_gate_io;
	required_ioport_array<6> m_cv_io;
	required_ioport_array<2> m_pedal_io;
	required_device_array<pwm_display_device, 3> m_vfd_devices;
	output_finder<3, 40> m_vfd_outputs;
	output_finder<> m_cassmute;

	// Voice computer.
	required_device<mc6809_device> m_voicecpu;
	required_device<pit8253_device> m_voicepit;
	required_shared_ptr<uint8_t> m_voiceram;
	output_finder<NUM_VOICES> m_fm_mdac;  // Latch connected to AD7523/MP7523.
	output_finder<NUM_VOICES> m_filter_mode;
	output_finder<NUM_VOICES> m_noise;
	output_finder<NUM_VOICES> m_pan;
	output_finder<NUM_VOICES> m_saw1;
	output_finder<NUM_VOICES> m_saw2;
	output_finder<NUM_VOICES> m_tri1;
	output_finder<NUM_VOICES> m_tri2;
	output_finder<NUM_VOICES> m_vcofm;
	output_finder<NUM_VOICES> m_sync;

	// Many bool variables represent signals in the schematic (e.g. HALTREQ).
	// Some of those signals are active low in the schematic (e.g. HALTREQ*).
	// But the variables here are always active-high (active == true).

	// Main computer state.
	u8 m_firq_timer_preset = 0xff;  // Preset for 40103 timer. Pulled high.
	u8 m_selected_cv_in = 0x07;  // MUX A-C inputs. Pulled high.
	bool m_inhibit_cv_in = true;  // MUX INHibit input. Pulled high.
	std::array<bool, 4> m_haltreq = { false, false, false, false };  // Halt request to the voice board (HALTREQ).
	std::array<bool, 6> m_encoder_dir = { false, false, false, false, false, false };
	std::array<bool, 6> m_encoder_changed = { false, false, false, false, false, false };
	std::array<u64, 3> m_vfd_anode_masks = { 0, 0, 0 };

	// Voice computer state.
	bool m_haltdis = 0;  // Halt disable (HALTDS).
	bool m_haltack = false;  // Halt acknowledge (HALTAKN).
	bool m_autodone = true;  // Autotune done (AUTODNE).
	u16 m_dac_data = 0;
	float m_dac_fine_v = 0;
	float m_dac_vref = 4.865F;  // Sampled in C806 and buffered and scaled by U815.
	bool m_allow_fast = false;
	std::array<std::array<float, NUM_CVS>, NUM_VOICES> m_cv;
	std::array<std::array<bool, NUM_CVS - 1>, NUM_VOICES> m_fast;  // `-1` because RES does not support fast updates.
};

TIMER_DEVICE_CALLBACK_MEMBER(xpander_state::firq_timer_elapsed)
{
	// CPU clock divided by U10 (LS393,  processor board).
	static constexpr const XTAL TIMER_CLOCK = 16_MHz_XTAL / 64;  // 250 KHz.

	// The FIRQ timer is a 40103 timer (U12, processor board). It is configured
	// to reset to its preset value when the count reaches 0 (/TC connected to
	// /PE). The FIRQ is triggered on the next clock cycle, once /TC goes high
	// (/TC used as CLK on an 74LS74 that will drive /FIRQ low when clocked).

	if (param < 0)  // param < 0 means the timer counted down to 0.
	{
		// The output is now low. It will go high on the next clock cycle,
		// which will trigger a bunch of stuff (see 'else' below). The timer
		// count needs to be set to the current value of `m_firq_timer_preset`,
		// which could (in theory) change by next cycle. So pass the current
		// value in `param`. The `-1` accounts for this single timer cycle.
		m_firq_timer->adjust(1 * attotime::from_hz(TIMER_CLOCK), m_firq_timer_preset - 1);
	}
	else  // Param >= 0 means counting restarted and output transitioned high.
	{
		// Using HOLD_LINE, because FIRQ will be cleared by circuitry that
		// detects a FIRQ Acknowledge. That circuit consists of:
		// U17 (74LS32), U21 (1/4 74LS04), U16 (74LS74). It clears the
		// FIRQ line on the rising edge of Q when BS = 1, BA = 0 and A3 = 0.
		m_maincpu->set_input_line(M6809_FIRQ_LINE, HOLD_LINE);

		// Schedule for the specified number of cycles. param = -1 ensure the
		// logic in `if (param < 0)` above is activated when the timer elapses.
		m_firq_timer->adjust(param * attotime::from_hz(TIMER_CLOCK), -1);

		// When FIRQ* activates, it issues a /CLR on 74LS259 (U12, display
		// board). This also clears the grid mask as a side effect. See
		// display_w() for more info.
		for (int i = 0; i < m_vfd_devices.size(); ++i)
			m_vfd_devices[i]->write_my(0);
	}
}

void xpander_state::firq_timer_preset_w(u8 data)
{
	if (m_firq_timer_preset == data)
		return;
	m_firq_timer_preset = data;
	LOGMASKED(LOG_FIRQ_TIMER, "FIRQ Timer Preset: %02x\n", data);
}

u8 xpander_state::proc_datain_r()
{
	// LS367, U15 (progressor board)
	// D0 - HALTAKN*
	const u8 d0 = m_haltack ? 0 : 1;
	// D1 - Memory write protect (low when protected)
	const u8 d1 = BIT(m_memory_protect_io->read(), 0);
	// D2 - Cassette DATA.
	const u8 d2 = 1;  // TODO: Implement.
	// D3-D5 not connected.
	return 0xf8 | (d2 << 2) | (d1 << 1) | (d0 << 0);
}

u8 xpander_state::gate_r()
{
	// All component designations refer to the Pot board.
	// D0-D5: GATE1-6.
	// D6: CHAIN ADVANCE.
	// D7: TRIGGER.
	// All signals inverted by U22 (CA3081, D1-D7) and Q1 (NPN, D0).
	// TRIGGER (D7) is inverted again by U2 (74LS02). Also connected to C17.
	// Signals other than TRIGGER not connected to such a capacitor.
	const u8 value = ~m_gate_io->read() ^ 0x80;
	LOGMASKED(LOG_CV_IN, "Gate: %02x\n", value);
	return value;
}

u8 xpander_state::switch_r(offs_t offset)
{
	// A0-A2 used as ABC inputs of a 74LS42 (U16, pot board), which in turn
	// controls which column of the switch matrix is active.
	// Input D is connected to the SWITCH* signal, and outputs 8 and 9 (active
	// when SWITCH* is high) are not connected.
	const u8 column = offset & 0x07;
	const u8 data = m_switch_io[column]->read();
	if (data != 0xff)
		LOGMASKED(LOG_SWITCHES, "Pressed: %02x - %02x\n", column, data);
	return data;
}

static u8 byte_from_array(const std::array<bool, 6> &v)
{
	u8 data = 0;
	for (int i = 0; i < 6; ++i)
		if (v[i])
			data |= 1U << i;
	return data;
}

u8 xpander_state::encoder_dir_r()
{
	// The DIR* signal also resets encoder change detection flip-flops when
	// active (low).
	std::fill(m_encoder_changed.begin(), m_encoder_changed.end(), false);

	const u8 data = byte_from_array(m_encoder_dir);
	LOGMASKED(LOG_ENCODERS, "Encoder dir_r: %02x\n", data);
	return data;
}

u8 xpander_state::encoder_sw_r()
{
	const u8 data = byte_from_array(m_encoder_changed);
	if (data != 0)
		LOGMASKED(LOG_ENCODERS, "Encoder sw_r: %02x\n", data);
	return data;
}

u8 xpander_state::cv_in_r()
{
	assert(m_selected_cv_in >= 0 && m_selected_cv_in < 8);
	if (m_inhibit_cv_in)
		return 0;

	u8 cv = 0;
	if (m_selected_cv_in < 6)
		cv = m_cv_io[m_selected_cv_in]->read();
	else
		cv = m_pedal_io[m_selected_cv_in - 6]->read();

	LOGMASKED(LOG_CV_IN, "CV in: %02x - %02x\n", m_selected_cv_in, cv);
	return cv;
}

u8 xpander_state::adc_r(offs_t offset)
{
	// CV* signal mapped to:

	// a) U18 latch on pot board, controls U17 (4051) mux (
	//    A0-A2 -> A-C, A3 -> INH).
	if (!machine().side_effects_disabled())
	{
		m_selected_cv_in = offset & 0x07;
		m_inhibit_cv_in = offset & 0x08;
	}

	// b) U20 on Pot Board (ADC0804).
	const u8 data = m_adc->read();
	LOGMASKED(LOG_CV_IN, "ADC Read: %02x - %02x\n", offset, data);
	return data;
}

void xpander_state::adc_w(offs_t offset, u8 data)
{
	LOGMASKED(LOG_CV_IN, "ADC Write: %02x - %02x\n", offset, data);
	// CV* signal mapped to:
	// a) U18 latch on pot board, controls U17 (4051) mux (A0-A2 -> A-C, A3 -> INH).
	m_selected_cv_in = offset & 0x07;
	m_inhibit_cv_in = offset & 0x08;
	// b) U20 on Pot Board (ADC0804).
	m_adc->write(data);
}

void xpander_state::refresh_voicecpu_halt_line()
{
	if (m_haltreq[0] && !m_haltdis)
	{
		m_voicecpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
		m_haltack = true;
		m_rom_0_view.select(1);
	}
	else
	{
		m_voicecpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
		m_haltack = false;
		m_rom_0_view.select(0);
	}
}

void xpander_state::cass_out_w(u8 data)
{
	// TODO: Implement.
}

void xpander_state::haltset_w(u8 data)
{
	// Bits 0-3: HALTREQ* 0-3
	for (int i = 0; i < 4; ++i)
	{
		m_haltreq[i] = !BIT(data, i);  // Active low.
		if (i > 0 && m_haltreq[i])
		{
			// The voice board is wired (with a rotary switch) to HALTREQ0*.
			// HALTREQ(1-3)* are not used in the xpander. They a provision for
			// for extending to more voice boards. For example, the Oberheim
			// Matrix 12 uses two of those.
			LOGMASKED(LOG_CPU_COMMS, "Unexpected HALTREQ %d asserted\n", i);
		}
	}
	refresh_voicecpu_halt_line();

	// Bit 4: RES* (voice cpu reset).
	const bool reset_voice = !BIT(data, 4);  // Active low.
	const bool voice_resetting = (m_voicecpu->input_line_state(INPUT_LINE_RESET) == ASSERT_LINE);
	if (reset_voice != voice_resetting)
	{
		m_voicecpu->set_input_line(INPUT_LINE_RESET, reset_voice ? ASSERT_LINE : CLEAR_LINE);
		if (reset_voice)
			LOGMASKED(LOG_CPU_COMMS, "Asserting voice CPU reset line\n");
		else
			LOGMASKED(LOG_CPU_COMMS, "Clearing voice CPU reset line\n");
	}

	m_cassmute = !BIT(data, 5);  // Bit 5: CASSMUTE*, active low.
	// Bit 6 not connected.
	cass_out_w(BIT(data, 7));  // Bit 7: CASS OUT.
}

void xpander_state::display_w(offs_t offset, u8 data)
{
	// There are 3 vacuum fluorescent displays (VFDs). These are controlled in
	// a similar way to multi-segment LED displays, and can be time-multiplexed
	// in the same way, though they run at a higher voltage (55V in this case).

	// Each of the 3 VFDs has 40 16-segment characters. The segments in each
	// display share a common anode, for a total of 3 x 16 = 48 anode signals.
	// There is a gate signal for each of the 40 characters, and those are
	// shared between the 3 displays, for a total of 40 gate signals.

	// All component designations refer to the display board.

	// U12 (74LS239) selects which anode signal latch will be enabled, based on
	// A0-A2. There are 6 latches (74LS374), 2 per display.
	const u8 display = (offset >> 1) & 0x03;
	if (display < 3)  // U12 outputs 6 and 7 are not connected.
	{
		if (offset & 0x01)  // Modifying high-order byte.
			m_vfd_anode_masks[display] = (u16(data) << 8) | (m_vfd_anode_masks[display] & 0x00ff);
		else
			m_vfd_anode_masks[display] = (m_vfd_anode_masks[display] & 0xff00) | data;
		m_vfd_devices[display]->write_mx(m_vfd_anode_masks[display]);
	}

	// An 74LS42 (U9), combined with 5 x 4028 (U2, U6, U8, U14, U18) form a
	// decoder that translates the latched A3-A8 to a single selected grid.
	// However, decoding is only enabled when U12 output 5 is high.
	const bool grid_enabled = (offset & 0x07) == 5;
	u8 grid_offset = (offset >> 3) & 0x3f;
	u64 grid_mask = 0;
	if (grid_enabled && grid_offset < 40)  // There are 40 grid signals.
		grid_mask = u64(1) << grid_offset;

	// Refresh VFD devices with the latest grid mask.
	for (int i = 0; i < m_vfd_devices.size(); ++i)
		m_vfd_devices[i]->write_my(grid_mask);
}

void xpander_state::display_output_w(int display, offs_t offset, u32 data)
{
	// The FG405A2 is a non-standard 16-segment display. It includes a
	// 14-segment character, a period, and a line under the character.
	// Map the 16 segments of the FG405A2 to a `led14segsc`. The line under the
	// character will be represented with the comma.
	assert(display >= 0 && display < 3);
	m_vfd_outputs[display][offset] =
		bitswap<16>(data, 0, 1, 3, 6, 5, 4, 2, 7, 12, 11, 10, 13, 15, 14, 9, 8);
}

void xpander_state::voice_dataout_w(u8 data)
{
	// D0 - HALTDS
	const u8 d0 = BIT(data, 0);
	if (m_haltdis != d0)
	{
		m_haltdis = d0;
		LOGMASKED(LOG_CPU_COMMS, "Voice HALTDS: %d\n", m_haltdis);
		refresh_voicecpu_halt_line();
	}
	// D1 - AUTOST
	m_voicepit->write_gate0(BIT(data, 1));
	// D2 - PW*
	// TODO: pit gate2 is actually: PW* | OSC. For now, setting to PW*, though
	// this is wrong.
	m_voicepit->write_gate2(BIT(data, 2));
	// TODO: D3 - AUTO*
	// D4-D5 - Not Connected.
}

u8 xpander_state::voice_datain_r()
{
	// D0 - AUTODNE*
	const u8 d0 = m_autodone ? 0 : 1;
	// D1 - OSC
	const u8 d1 = 1;  // TODO: Implement.
	// D2-D7 - Not connected (pulled up).
	return 0xfc | (d1 << 1) | d0;
}

void xpander_state::voice_latch0_w(offs_t offset, u8 data)  // UX03, 74LS374.
{
	if (offset >= NUM_VOICES)
		return;
	m_fm_mdac[offset] = data;
}

void xpander_state::voice_latch1_w(offs_t offset, u8 data)  // UX02, 74HC174.
{
	if (offset >= NUM_VOICES)
		return;
	m_vcofm[offset] = BIT(data, 0);
	m_saw2[offset] = BIT(data, 1);
	m_tri1[offset] = BIT(data, 2);
	m_sync[offset] = BIT(data, 3);
	m_tri2[offset] = BIT(data, 4);
	m_saw2[offset] = BIT(data, 5);
}

void xpander_state::voice_latch2_w(offs_t offset, u8 data)  // UX01, 74HC374.
{
	if (offset >= NUM_VOICES)
		return;
	m_filter_mode[offset] = data & 0x0f;
	m_noise[offset] = BIT(data, 4);
	m_pan[offset] = data >> 5;
}

float xpander_state::get_dac_v() const  // Returns output of U812.
{
	static constexpr const u16 MAX_DAC_DATA = (1U << 14) - 1;
	return -m_dac_data * m_dac_vref / MAX_DAC_DATA;
}

void xpander_state::voice_update_cv(u8 voice, u8 cv_index, float cv, bool fast)
{
	assert(voice < NUM_VOICES);
	if (m_cv[voice][cv_index] == cv && m_fast[voice][cv_index] == fast)
		return;

	m_cv[voice][cv_index] = cv;
	m_fast[voice][cv_index] = fast;
	LOGMASKED(LOG_DAC, "Voice %d - CV %s: %f, fast: %d\n",
			voice, CV_NAMES[cv_index], cv, fast);
}

void xpander_state::voice_update_resonance_cv(u8 voice, float cv)
{
	if (voice >= NUM_VOICES)
		return;
	if (m_cv[voice][RES_CV_INDEX] == cv)
		return;

	m_cv[voice][RES_CV_INDEX] = cv;
	LOGMASKED(LOG_DAC, "Voice %d - CV %s: %f\n",
			voice, CV_NAMES[RES_CV_INDEX], cv);
}

void xpander_state::voice_dac_enable_w(offs_t offset, u8 data)
{
	// All component designations refer to the voice board.

	// 4V reference used as a source for other reference voltages.
	// Generated by D805, R856, R857, R854, U815.
	static constexpr const float V_REF_U815 = 4;
	static constexpr const float DEFAULT_UNSCALED_VREF =
			V_REF_U815 * RES_VOLTAGE_DIVIDER(RES_K(18.2), RES_K(10));
	static constexpr const float VREF_SCALER = 1 + RES_K(2.43) / RES_K(1);  // U815, R851, R852.
	static constexpr const float DEFAULT_VREF = DEFAULT_UNSCALED_VREF * VREF_SCALER;
	static constexpr const float CEM3374_NOMINAL_TEMPCO_V = 2.5;

	LOGMASKED(LOG_DAC_VERBOSE, "DAC: %04x: %02x\n", offset, data);

	// Updating the 7 LSBits for the DAC. Bit 0 is ignored.
	m_dac_data = (m_dac_data & HIGH7_MASK) | (data  >> 1);

	const bool is_hres = !BIT(offset, 8);
	if (is_hres)
	{
		const u8 ref_mux_address = (offset >> 4) & 0x07;  // A4-A6 (U805).
		if (ref_mux_address >= 1 && ref_mux_address <= 6)
		{
			// Selects the voiceX temperature compensation voltage.
			m_dac_vref = CEM3374_NOMINAL_TEMPCO_V * VREF_SCALER;
		}
		else if (ref_mux_address == 7)
		{
			m_dac_vref = DEFAULT_VREF;
		}
		// else: ref_mux_address = 0 disconnects the reference voltage.
		// In that case, the last value sampled in C22 is used. So we keep
		// the value of m_dac_vref the same.
	}
	else
	{
		// U805 disabled. But Y input of U014 (4053) enabled.
		m_dac_vref = DEFAULT_VREF;
	}

	if (BIT(offset, 7))  // FTSH (fine-tune sample & hold) active (active high).
	{
		// No S&H MUXes are selected, because input D on U803 is high.
		// U814 is activated by FTSH, and samples the DAC voltage in C805/U815 (
		// m_dac_fine_v). This "fine" voltage will be added to the coarser
		// voltage in a future DAC write. This mode is used for generating
		// voltages at a resolution greater than 14 bits.
		m_dac_fine_v = get_dac_v();
		return;
	}

	// FTSH low enables one of the first 8 outputs of U803 (controlled
	// by A5-A7). Each of those outputs enables a 4051 mux, whose
	// address is controlled by A1-A3.

	const u8 selected_sh = (offset >> 4) & 0x07;  // A4-A6.
	if (selected_sh == 0)  // U803 output 0 is not connected.
		return;

	float dac_v = -RES_K(10) / RES_K(10) * get_dac_v();
	if (is_hres)  // Turns on U814.
	{
		dac_v += -RES_K(10) / RES_K(30.1) * m_dac_fine_v
				 -RES_K(10) / RES_K(17.4) * m_dac_vref;
	}

	const u8 sh_address = (offset >> 1) & 0x07;  // A1-A3.
	if (selected_sh == 7)
	{
		// Updates resonance (RES). In this case, the voice is selected by
		// `sh_address` (U816). Note that RES is not affected by the FAST*
		// signal.
		voice_update_resonance_cv(sh_address, dac_v);
	}
	else
	{
		// Voice is selected by `selected_sh` (output of U803). Need a -1
		// because the first U803 output is not connected. The CV index for
		// the given voice is selected by `sh_address`.
		voice_update_cv(selected_sh - 1, sh_address, dac_v, m_allow_fast);
	}
}

void xpander_state::voice_dac_clear_w(u8 data)
{
	// (1) Clears latch U806. This results in:
	// - No S&H mux selected (activates output 0 of U803, which is not
	//   connected to anything)
	// - S&H address 0 selected, but this is a no-op since no S&H mux is
	//   selected (see above).
	// - FTSH (active high) deactivated (set low).
	// - HRES* (active low) activated (set low).
	// - Mux U805 activated (by HRES* low), but address 0 selected.
	//   input 0 is not connected.
	//   - No active reference voltage to the DAC. Previously used reference
	//     sampled in C806.
	// None of the above affect emulation, since they will be set to valid
	// values on the next invocation to voice_dac_w().

	// (2) Activates latch for the 7 MSBits of the 14-bit DAC.
	//     D0-D6 used for the 7 MSBits.
	//     D7, if set, will enable fast mode on the next invocation to
	//     voice_dac_w().
	m_dac_data = ((data & LOW7_MASK) << 7) | (m_dac_data & LOW7_MASK);
	m_allow_fast = BIT(data, 7);
	LOGMASKED(LOG_DAC_VERBOSE, "DAC clear %02x: %04x - %d\n",
				data, m_dac_data, m_allow_fast);
}

void xpander_state::voice_dac_w(offs_t offset, u8 data)
{
	if (BIT(offset, 0))  // VOICEN*  (A0 high)
		voice_dac_enable_w(offset, data);
	else  // CLEAR* (A0 low)
		voice_dac_clear_w(data);
}

void xpander_state::voicepit_out0_changed(int state)
{
	// OUT0 -> AUTODNE* -> 74LS04 (inverted) -> GATE1
	m_autodone = !state;
	m_voicepit->write_gate1(state ? 0 : 1);
	LOGMASKED(LOG_VOICE_TIMER, "PIT timer 0 out: %d\n", state);
}

void xpander_state::voicepit_out2_changed(int state)
{
	if (state)  // Interrupt triggered on positive transition of output.
	{
		// Using HOLD_LINE because there is circuitry that clears the IRQ* line
		// when the CPU ACks the IRQ (U919 - 74LS74, U911 - 74LS08, looking
		// at BS and BA*).
		m_voicecpu->set_input_line(M6809_IRQ_LINE, HOLD_LINE);
	}
}

void xpander_state::maincpu_map(address_map &map)
{
	// Component designations refer to the Processor board, unless otherwise
	// noted. The signal names below (e.g. DISP*, HALTSET*) reference those in
	// the schematics.
	map.unmap_value_high();  // Data bus pulled high by resistors in Pot board.

	// 1/2 74LS139 (U22, O0-O2) controls access to RAM and other ports.
	// RAM write and select signals can only go active low when there is power
	// (as determined by the PUP circuit).
	// NVRAM_A can be write-protected in hardware by a memory-protect switch
	// (SW6, active-closed, disables /WR signal).
	map(0x000, 0x3fff).view(m_nvram_a_view);
	m_nvram_a_view[0](0x000, 0x3fff).ram().share(NVRAM_A_TAG);
	m_nvram_a_view[1](0x000, 0x3fff).readonly().share(NVRAM_A_TAG);
	// NVRAM_B is not protected by SW6.
	map(0x4000, 0x5fff).ram().share(NVRAM_B_TAG);  // 1 x 6264 (U4).

	// The 74LS139 above (O3) along with an 74LS42 (U23) control access to all
	// other ports / peripherals.
	// Active when O3 is low and one of READ*, WRITE*, UART* is low.
	map(0x6000, 0x61ff).mirror(0x0200).w(FUNC(xpander_state::display_w)); // DISP* and DISPLCLR*
	map(0x6400, 0x6400).mirror(0x03ff).w(FUNC(xpander_state::firq_timer_preset_w));  // INTSET*
	map(0x6800, 0x6800).mirror(0x03ff).w(FUNC(xpander_state::haltset_w));  // HALTSET*
	// ACIA addressing: RS <- A0, CS1 <- A1, CS0 <- A2, /CS2 <- UART*.
	map(0x6c06, 0x6c07).mirror(0x03f8).rw(m_midiacia, FUNC(acia6850_device::read), FUNC(acia6850_device::write));  // UART*
	map(0x7000, 0x7000).mirror(0x03ff).r(FUNC(xpander_state::proc_datain_r));  // DATAIN*
	map(0x7400, 0x7400).mirror(0x03ff).w("latch_u24_proc", FUNC(output_latch_device::write));  // LED2*
	map(0x7800, 0x7800).mirror(0x03ff).unmaprw();  // Unused. U23 output 6 not connected.

	// Peripherals on Pot board mapped to 0x7c00-0x7fff (BEN* output of U23).
	// Additional decoding for those is done by U15 on the Pot board (74LS42).
	// A9 and A0-A5 are not used for decoding (mirror: 0x023f)
	map(0x7c00, 0x7c00).mirror(0x023f).r(FUNC(xpander_state::encoder_dir_r));  // DIR*
	map(0x7c40, 0x7c40).mirror(0x023f).r(FUNC(xpander_state::encoder_sw_r));  // SW*
	map(0x7c80, 0x7c80).mirror(0x023f).w("latch_u14_pot", FUNC(output_latch_device::write));  // LED1*
	map(0x7cc0, 0x7cc0).mirror(0x023f).r(FUNC(xpander_state::gate_r));  // GATE*
	map(0x7d00, 0x7d00).mirror(0x023f).w("latch_u21_pot", FUNC(output_latch_device::write));  // PULL*
	map(0x7d40, 0x7d47).mirror(0x0238).r(FUNC(xpander_state::switch_r));  // SWITCH*
	map(0x7d80, 0x7d8f).mirror(0x0230).rw(FUNC(xpander_state::adc_r), FUNC(xpander_state::adc_w));  // CV*
	map(0x7dc0, 0x7dc0).mirror(0x023f).unmaprw();  // Unused. U15 (Pot board) output 7 not connected.

	// ROM and VOICERAM decoding is done by 1/2 LS139 (U22), 2/4 LS04 (U21),
	// 2/4 LS32 (U20).
	// (0x8000, 0x9fff) conditionally accesses VOICERAM, when HALTAKN* active.
	map(0x8000, 0x9fff).view(m_rom_0_view);
	m_rom_0_view[0](0x8000, 0x9fff).rom().region(MAINCPU_TAG, 0x0000);  // 1 x 2764 (U8).
	m_rom_0_view[1](0x8000, 0x9fff).ram().share(VOICERAM_TAG);
	map(0xa000, 0xffff).rom().region(MAINCPU_TAG, 0x2000);  // 3 x 2764 (U7 - U5).
}

void xpander_state::voicecpu_map(address_map &map)
{
	// All component designations refer to the Voice board.
	// Signal names (e.g. LATCH0*) are from the schematic.
	map.unmap_value_high();  // Data bus pulled high by resistors in Voice Board.

	// RAM's /CS connected to A15.
	map(0x0000, 0x1fff).mirror(0x6000).ram().share(VOICERAM_TAG);  // 1 x 6264 (U917).

	// ROM and port/peripheral decoding done by 1/2 LS139 (U915).
	// Ports / peripherals enabled when U915 O0 is low AND one of
	// READ*, WRITE*, TIMER* is low. Additional decoding done by 74LS42 (U918).
	map(0x8000, 0x8007).mirror(0x03f8).w(FUNC(xpander_state::voice_latch0_w));  // LATCH0*
	map(0x8400, 0x8407).mirror(0x03f8).w(FUNC(xpander_state::voice_latch1_w));  // LATCH1*
	map(0x8800, 0x8807).mirror(0x03f8).w(FUNC(xpander_state::voice_latch2_w));  // LATCH2*
	map(0x8c00, 0x8dff).mirror(0x0200).w(FUNC(xpander_state::voice_dac_w));  // SDAC (inverted by LS04).
	map(0x9000, 0x9000).mirror(0x03ff).w(FUNC(xpander_state::voice_dataout_w));  // DATAOUT*
	map(0x9400, 0x9400).mirror(0x03ff).r(FUNC(xpander_state::voice_datain_r));  // DATAIN*
	map(0x9800, 0x9803).mirror(0x03fc).rw(m_voicepit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));  // TIMER*
	map(0x9c00, 0x9c00).mirror(0x03ff).unmaprw();  // Unused. U918 output 7 not connected.

	// ROMs only get enabled if BA* is high. I.e. disabled when main CPU has
	// control of the voice cpu's bus.
	map(0xa000, 0xffff).rom().region(VOICECPU_TAG, 0);  // 3 x 6264 (U909, U912, U914)
}

void xpander_state::machine_start()
{
	firq_timer_elapsed(*m_firq_timer, m_firq_timer_preset);  // Reset the timer.

	m_cassmute.resolve();
	m_vfd_outputs.resolve();
	m_fm_mdac.resolve();
	m_filter_mode.resolve();
	m_noise.resolve();
	m_pan.resolve();
	m_saw1.resolve();
	m_saw2.resolve();
	m_tri1.resolve();
	m_tri2.resolve();
	m_vcofm.resolve();
	m_sync.resolve();

	save_item(NAME(m_firq_timer_preset));
	save_item(NAME(m_selected_cv_in));
	save_item(NAME(m_inhibit_cv_in));
	save_item(NAME(m_haltreq));
	save_item(NAME(m_encoder_dir));
	save_item(NAME(m_encoder_changed));
	save_item(NAME(m_vfd_anode_masks));
	save_item(NAME(m_haltdis));
	save_item(NAME(m_haltack));
	save_item(NAME(m_autodone));
	save_item(NAME(m_dac_data));
	save_item(NAME(m_dac_fine_v));
	save_item(NAME(m_dac_vref));
	save_item(NAME(m_allow_fast));
	save_item(NAME(m_cv));
	save_item(NAME(m_fast));
}

void xpander_state::xpander(machine_config &config)
{
	MC6809(config, m_maincpu, 16_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &xpander_state::maincpu_map);

	TIMER(config, m_firq_timer).configure_generic(FUNC(xpander_state::firq_timer_elapsed));

	NVRAM(config, NVRAM_A_TAG, nvram_device::DEFAULT_ALL_0);
	NVRAM(config, NVRAM_B_TAG, nvram_device::DEFAULT_ALL_0);

	ACIA6850(config, m_midiacia, 0);
	m_midiacia->txd_handler().set("mdout", FUNC(midi_port_device::write_txd));
	m_midiacia->irq_handler().set_inputline(m_maincpu, M6809_IRQ_LINE);

	clock_device &acia_clock = CLOCK(config, "aciaclock", 16_MHz_XTAL / 32);  // 500 KHz.
	acia_clock.signal_handler().set(m_midiacia, FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append(m_midiacia, FUNC(acia6850_device::write_rxc));

	MIDI_PORT(config, "mdout", midiout_slot, "midiout");
	MIDI_PORT(config, "mdthru", midiout_slot, "midiout");
	midi_port_device &midi_in = MIDI_PORT(config, "mdin", midiin_slot, "midiin");
	midi_in.rxd_handler().set(m_midiacia, FUNC(acia6850_device::write_rxd));
	midi_in.rxd_handler().append("mdthru", FUNC(midi_port_device::write_txd));

	ADC0804(config, m_adc, 16_MHz_XTAL / 32);  // 500 KHz.
	m_adc->vin_callback().set(FUNC(xpander_state::cv_in_r));

	for (int i = 0; i < m_vfd_devices.size(); ++i)
	{
		PWM_DISPLAY(config, m_vfd_devices[i]).set_size(40, 16);  // 40 x 16-segment display.
		m_vfd_devices[i]->set_segmask(0xffffffffff, 0xffff);
		m_vfd_devices[i]->output_digit().set([this, i] (offs_t offset, u32 data) { display_output_w(i, offset, data); });
	}

	config.set_default_layout(layout_oberheim_xpander);

	MC6809(config, m_voicecpu, 16_MHz_XTAL / 2);
	m_voicecpu->set_addrmap(AS_PROGRAM, &xpander_state::voicecpu_map);

	PIT8253(config, m_voicepit);
	// TODO: Set clk<0>. Connected to "OSC".
	// Clock inputs 1 and 2 connected to CPU's Q signal, which is 4 times slower
	// than the XTAL input to the CPU.
	m_voicepit->set_clk<1>(16_MHz_XTAL / 2 / 4);  // 2 MHz.
	m_voicepit->set_clk<2>(16_MHz_XTAL / 2 / 4);  // 2 MHz.
	m_voicepit->out_handler<0>().set(FUNC(xpander_state::voicepit_out0_changed));
	// Output 1 not connected.
	m_voicepit->out_handler<2>().set(FUNC(xpander_state::voicepit_out2_changed));

	// LED2*, U24 on Processor board. All outputs active low (connected to LED
	// cathodes).
	output_latch_device &u24(OUTPUT_LATCH(config, "latch_u24_proc"));
	u24.bit_handler<0>().set_output("led_misc").invert();
	u24.bit_handler<1>().set_output("led_ramp_x").invert();
	u24.bit_handler<2>().set_output("led_lfo_x").invert();
	u24.bit_handler<3>().set_output("led_env_x").invert();
	u24.bit_handler<4>().set_output("led_vcf_vca").invert();
	u24.bit_handler<5>().set_output("led_vco1").invert();  // On Pot board.
	u24.bit_handler<6>().set_output("led_vco2").invert();  // On Pot board.
	u24.bit_handler<7>().set_output("led_fm_lag").invert();  // On Pot board.

	// LED1*, U14 on Pot board. All outputs active low (connected to LED
	// cathodes).
	output_latch_device &u14(OUTPUT_LATCH(config, "latch_u14_pot"));
	u14.bit_handler<0>().set_output("led_x_sel").invert();
	u14.bit_handler<1>().set_output("led_mod_sorc").invert();
	u14.bit_handler<2>().set_output("led_mod").invert();
	u14.bit_handler<3>().set_output("led_on_off_a").invert();
	u14.bit_handler<4>().set_output("led_track").invert();
	u14.bit_handler<5>().set_output("led_page_2").invert();
	u14.bit_handler<6>().set_output("led_on_off_b").invert();
	u14.bit_handler<7>().set_output("led_value").invert();

	// PULL*, U21 on Pot board.
	// Configures the resting value for the gate inputs. Used to support both
	// active low and active high inputs.
	output_latch_device &u21(OUTPUT_LATCH(config, "latch_u21_pot"));
	u21.bit_handler<0>().set_output("pull_1");  // Default for GATE 1
	u21.bit_handler<1>().set_output("pull_2");  // Default for GATE 2
	u21.bit_handler<2>().set_output("pull_3");  // Default for GATE 3
	u21.bit_handler<3>().set_output("pull_4");  // Default for GATE 4
	u21.bit_handler<4>().set_output("pull_5");  // Default for GATE 5
	u21.bit_handler<5>().set_output("pull_6");  // Default for GATE 6
	u21.bit_handler<6>().set_output("pull_7");  // Default for TRIGGER
	// Bit 7 not connected.
}

DECLARE_INPUT_CHANGED_MEMBER(xpander_state::encoder_moved)
{
	const int encoder = param;
	if (oldval != newval)
		m_encoder_changed[encoder] = true;

	static constexpr const int WRAP_BUFFER = 3;
	const bool overflowed = newval <= WRAP_BUFFER &&
			oldval >= NUM_ENCODER_POSITIONS - WRAP_BUFFER;
	const bool underflowed = newval >= NUM_ENCODER_POSITIONS - WRAP_BUFFER &&
			oldval <= WRAP_BUFFER;
	m_encoder_dir[encoder] = ((newval > oldval) || overflowed) && !underflowed;

	LOGMASKED(LOG_ENCODERS, "Encoder %d changed from: %d to: %d (o: %d, u: %d), dir: %d\n",
			encoder, oldval, newval, overflowed, underflowed, m_encoder_dir[encoder]);
}

DECLARE_INPUT_CHANGED_MEMBER(xpander_state::memory_protect_changed)
{
	const bool memory_protected = !BIT(m_memory_protect_io->read(), 0);
	LOGMASKED(LOG_MEM_PROTECT, "NVRAM A protected: %d\n", memory_protected);
	if (memory_protected)
		m_nvram_a_view.select(1);
	else
		m_nvram_a_view.select(0);
}

INPUT_PORTS_START(xpander)
	PORT_START("switches_0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("0")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("1")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("2")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("3")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("4")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("5")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("6")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("7") PORT_CODE(KEYCODE_1)

	PORT_START("switches_1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("8")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("9")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("+")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("-")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("STORE")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PAGE2")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("TUNE") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("MASTER")

	PORT_START("switches_2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SINGLE")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("MULTI")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("VOICE 1")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("VOICE 2")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("VOICE 3")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("VOICE 4") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("VOICE 5")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("VOICE 6")

	PORT_START("switches_3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P1 1") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P1 2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P1 3")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P1 4")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P1 5")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P1 6")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("VCO1")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("VCO2")

	PORT_START("switches_4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P2 1") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P2 2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P2 3")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P2 4")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P2 5")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("P2 6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("FM/LAG")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("TRACK X")

	PORT_START("switches_5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("LEVER 1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("LEVER 2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PEDAL 1") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PEDAL 2")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("VIB")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("KEYBOARD")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("LAG")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("VELOCITY")

	PORT_START("switches_6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("RELEASE VELOCITY")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PRESSURE") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("ENV")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("LFO")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("TRACK")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("RAMP")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)  // No switch. Pulled up.
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)  // No switch. Pulled up.

	PORT_START("switches_7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("VCF/VCA") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("ENV X")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("LFO X")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("RAMP X")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("MISC.")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)  // No switch. Pulled up.
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)  // No switch. Pulled up.
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)  // No switch. Pulled up.

	PORT_START("memory_protect")  // SW6 on Processor board.
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Memory Protect") PORT_TOGGLE PORT_CODE(KEYCODE_P)
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(xpander_state::memory_protect_changed), 0)

	// These inputs (except "chain advance") can be configured by the user as
	// active low or active high. This configuration is manifested in the "PULL"
	// signals (search for "latch_u21_pot"). Modeling as active low for now.
	PORT_START("gate_inputs")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("GATE 1")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("GATE 2")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("GATE 3")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("GATE 4")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("GATE 5")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("GATE 6")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("CHAIN ADVANCE")  // No corresponding 'PULL' signal.
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("TRIGGER")

	// Voltage inputs.
	PORT_START("cv_in_1")
	PORT_BIT(0xff, 0x00, IPT_DIAL) PORT_SENSITIVITY(30)
	PORT_START("cv_in_2")
	PORT_BIT(0xff, 0x00, IPT_DIAL) PORT_SENSITIVITY(30)
	PORT_START("cv_in_3")
	PORT_BIT(0xff, 0x00, IPT_DIAL) PORT_SENSITIVITY(30)
	PORT_START("cv_in_4")
	PORT_BIT(0xff, 0x00, IPT_DIAL) PORT_SENSITIVITY(30)
	PORT_START("cv_in_5")
	PORT_BIT(0xff, 0x00, IPT_DIAL) PORT_SENSITIVITY(30)
	PORT_START("cv_in_6")
	PORT_BIT(0xff, 0x00, IPT_DIAL) PORT_SENSITIVITY(30)
	PORT_START("pedal_1")
	PORT_BIT(0xff, 0x00, IPT_PEDAL1) PORT_SENSITIVITY(30)
	PORT_START("pedal_2")
	PORT_BIT(0xff, 0x00, IPT_PEDAL2) PORT_SENSITIVITY(30)

	// Rotary encoders.
	PORT_START("encoder_0")
	PORT_BIT(0x1f, 0x00, IPT_POSITIONAL) PORT_POSITIONS(NUM_ENCODER_POSITIONS)
		PORT_WRAPS PORT_SENSITIVITY(20) PORT_KEYDELTA(1)
		PORT_CODE_DEC(KEYCODE_Q) PORT_CODE_INC(KEYCODE_W)
		PORT_FULL_TURN_COUNT(NUM_ENCODER_POSITIONS)
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(xpander_state::encoder_moved), 0)
	PORT_START("encoder_1")
	PORT_BIT(0x1f, 0x00, IPT_POSITIONAL) PORT_POSITIONS(NUM_ENCODER_POSITIONS)
		PORT_WRAPS PORT_SENSITIVITY(20) PORT_KEYDELTA(1)
		PORT_CODE_DEC(KEYCODE_E) PORT_CODE_INC(KEYCODE_R)
		PORT_FULL_TURN_COUNT(NUM_ENCODER_POSITIONS)
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(xpander_state::encoder_moved), 1)
	PORT_START("encoder_2")
	PORT_BIT(0x1f, 0x00, IPT_POSITIONAL) PORT_POSITIONS(NUM_ENCODER_POSITIONS)
		PORT_WRAPS PORT_SENSITIVITY(20) PORT_KEYDELTA(1)
		PORT_CODE_DEC(KEYCODE_A) PORT_CODE_INC(KEYCODE_S)
		PORT_FULL_TURN_COUNT(NUM_ENCODER_POSITIONS)
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(xpander_state::encoder_moved), 2)
	PORT_START("encoder_3")
	PORT_BIT(0x1f, 0x00, IPT_POSITIONAL) PORT_POSITIONS(NUM_ENCODER_POSITIONS)
		PORT_WRAPS PORT_SENSITIVITY(20) PORT_KEYDELTA(1)
		PORT_CODE_DEC(KEYCODE_D) PORT_CODE_INC(KEYCODE_F)
		PORT_FULL_TURN_COUNT(NUM_ENCODER_POSITIONS)
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(xpander_state::encoder_moved), 3)
	PORT_START("encoder_4")
	PORT_BIT(0x1f, 0x00, IPT_POSITIONAL) PORT_POSITIONS(NUM_ENCODER_POSITIONS)
		PORT_WRAPS PORT_SENSITIVITY(20) PORT_KEYDELTA(1)
		PORT_CODE_DEC(KEYCODE_G) PORT_CODE_INC(KEYCODE_H)
		PORT_FULL_TURN_COUNT(NUM_ENCODER_POSITIONS)
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(xpander_state::encoder_moved), 4)
	PORT_START("encoder_5")
	PORT_BIT(0x1f, 0x00, IPT_POSITIONAL) PORT_POSITIONS(NUM_ENCODER_POSITIONS)
		PORT_WRAPS PORT_SENSITIVITY(20) PORT_KEYDELTA(1)
		PORT_CODE_DEC(KEYCODE_J) PORT_CODE_INC(KEYCODE_K)
		PORT_FULL_TURN_COUNT(NUM_ENCODER_POSITIONS)
		PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(xpander_state::encoder_moved), 5)
INPUT_PORTS_END

ROM_START(xpander)
	ROM_REGION(0x8000, MAINCPU_TAG, 0)  // 4 x 2764 8Kx8bit ROMs (U8 - U5, processor board).
	ROM_DEFAULT_BIOS("1.2")

	ROM_SYSTEM_BIOS(0, "1.2", "Xpander Main Processor Revision 1.2")
	ROMX_LOAD("exp1.2-0.u8", 0x000000, 0x002000, CRC(dc3801b1) SHA1(4064edc2fa0bea62684c28e8e004feb8229604fe), ROM_BIOS(0))
	ROMX_LOAD("exp1.2-1.u7", 0x002000, 0x002000, CRC(b57f8482) SHA1(233efc3da3a96777b10a4d6fe7e80864e9231f04), ROM_BIOS(0))
	ROMX_LOAD("exp1.2-2.u6", 0x004000, 0x002000, CRC(30f28710) SHA1(798c1b28fb59819baadbf0142ecd1348e759fa70), ROM_BIOS(0))
	ROMX_LOAD("exp1.2-3.u5", 0x006000, 0x002000, CRC(0bc8335e) SHA1(5897ad0cd66cf37b8e9b277654aab4eb83d1c8ab), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "1.0", "Xpander Main Processor Revision 1.0")
	ROMX_LOAD("exp1.0-0.u8", 0x000000, 0x002000, CRC(d93fb34c) SHA1(e470589562f6544507c276ccfc95632a6288eb18), ROM_BIOS(1))
	ROMX_LOAD("exp1.0-1.u7", 0x002000, 0x002000, CRC(80912a48) SHA1(064e9fff26a4ebf8902c3e085fa631bb5579a160), ROM_BIOS(1))
	ROMX_LOAD("exp1.0-2.u6", 0x004000, 0x002000, CRC(89e14d6e) SHA1(7aef67db9d78523777af6f1edf04fd6b0d11229b), ROM_BIOS(1))
	ROMX_LOAD("exp1.0-3.u5", 0x006000, 0x002000, CRC(21794b92) SHA1(e544375c3fc29931497cb6429db7a2812f77c553), ROM_BIOS(1))

	ROM_REGION(0x6000, VOICECPU_TAG, 0)  // 3 x 2764 8Kx8bit ROMs. Rev 1.4.
	ROM_FILL(0x000000, 0x002000, 0xff)  // U909. Spare 2764 ROM slot. Not populated. Data bus pulled high.
	ROM_LOAD("ca1.4-0.u912", 0x002000, 0x002000, CRC(79f4490a) SHA1(5ab13ccc3b75df313a447520ca54a492102b7e3b))
	ROM_LOAD("ca1.4-1.u914", 0x004000, 0x002000, CRC(9b7a7914) SHA1(951bbc197a0140a93bb7621a7aedbe0f4037990c))
ROM_END

}  // anonymous namespace

// In production from 1984 to 1988.
SYST(1984, xpander, 0, 0, xpander, xpander, xpander_state, empty_init, "Oberheim", "Xpander", MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



yes.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Carl

#include "emu.h"
#include "cpu/i86/i186.h"
#include "machine/i8256.h"
#include "machine/wd_fdc.h"
#include "video/mc6845.h"
#include "screen.h"


namespace {

class yes_state : public driver_device
{
public:
	yes_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_fdc(*this, "fdc"),
		m_crtc(*this, "crtc"),
		m_uart(*this, "uart")
		{ }

	void yes(machine_config &config);

private:
	required_device<i80186_cpu_device> m_maincpu;
	required_device<wd2793_device> m_fdc;
	required_device<hd6845s_device> m_crtc;
	required_device<i8256_device> m_uart;
	void io_map(address_map &map) ATTR_COLD;
	void program_map(address_map &map) ATTR_COLD;
	MC6845_UPDATE_ROW(crtc_update_row);
};

void yes_state::program_map(address_map &map)
{
	map(0x00000, 0xaffff).ram();
	map(0xf0000, 0xfffff).rom().region("bios", 0);
}

void yes_state::io_map(address_map &map)
{
	map(0x0400, 0x041f).rw(m_uart, FUNC(i8256_device::read), FUNC(i8256_device::write));
	map(0x0480, 0x0487).rw(m_fdc, FUNC(wd2793_device::read), FUNC(wd2793_device::write)).umask16(0x00ff);
	map(0x0500, 0x0500).w(m_crtc, FUNC(hd6845s_device::address_w));
	map(0x0502, 0x0502).rw(m_crtc, FUNC(hd6845s_device::register_r), FUNC(mc6845_device::register_w));
}

MC6845_UPDATE_ROW( yes_state::crtc_update_row )
{
}

void yes_state::yes(machine_config &config)
{
	/* basic machine hardware */
	I80186(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &yes_state::program_map);
	m_maincpu->set_addrmap(AS_IO, &yes_state::io_map);

	I8256(config, "uart", 16_MHz_XTAL / 8);

	WD2793(config, m_fdc, 16_MHz_XTAL / 8);
	//m_fdc->intrq_wr_callback().set(m_uart, FUNC(i8256_device::ir));
	m_fdc->drq_wr_callback().set(m_maincpu, FUNC(i80186_cpu_device::drq1_w));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(16_MHz_XTAL, 1016, 0, 640, 314, 0, 240);
	screen.set_screen_update("crtc", FUNC(hd6845s_device::screen_update));

	HD6845S(config, m_crtc, 16_MHz_XTAL / 8); //clock?
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(yes_state::crtc_update_row));
}

ROM_START(yes)
	ROM_REGION16_LE(0x10000,"bios", 0)
	ROMX_LOAD("yes_23484.bin", 0x0001, 0x8000, CRC(3530cce3) SHA1(f888f3c291c405f6cb873b5f8ba00eb638947cbb), ROM_SKIP(1))
	ROMX_LOAD("yes_23494.bin", 0x0000, 0x8000, CRC(6d58b50f) SHA1(63af83c4055395a086c014f66dabbac1a728fb14), ROM_SKIP(1))

	ROM_REGION(0x2000, "chargen", 0)
	ROM_LOAD( "yes_23502.bin", 0x0000, 0x2000, CRC(e3324683) SHA1(87c3a6cb7fbe982f88abb85426785228c2b33bb7))
ROM_END

} // anonymous namespace


COMP( 1985, yes, 0, 0, yes, 0, yes_state, empty_init, "Philips", ":YES", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )



yman1x.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Skeleton driver for Yamaha AN1x Control Synthesizer.

    This is roughly similar in principle to the MU-80, but the sound chip is a
    YSS236-F VOP3 instead of SWP20. A second VOP3 (with its own ROM) is on the
    separate DMS board.

*******************************************************************************/

#include "emu.h"
//#include "bus/midi/midi.h"
#include "cpu/h8/h83002.h"
#include "cpu/m6805/hd6305.h"
#include "mulcd.h"
#include "machine/nvram.h"
#include "sound/meg.h"
#include "speaker.h"

namespace {

class an1x_state : public driver_device
{
public:
	an1x_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pks(*this, "pks")
		, m_meg(*this, "meg")
	{
	}

	void an1x(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<h83002_device> m_maincpu;
	required_device<cpu_device> m_pks;
	required_device<meg_device> m_meg;
};

void an1x_state::mem_map(address_map &map)
{
	map(0x000000, 0x0fffff).rom().region("program", 0);
	map(0x200000, 0x23ffff).ram().share("nvram");
}

static INPUT_PORTS_START(an1x)
INPUT_PORTS_END

void an1x_state::an1x(machine_config &config)
{
	H83002(config, m_maincpu, 16_MHz_XTAL); // HD6413002FP16
	m_maincpu->set_addrmap(AS_PROGRAM, &an1x_state::mem_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // 2x HM628128BLFP-8 + CR2450 battery

	HD6305V0(config, m_pks, 8_MHz_XTAL).set_disable(); // HD63B05V0E65F

	MULCD(config, "lcd"); // LC7985ND (back-lit)

	SPEAKER(config, "speaker", 2).front();

	MEG(config, m_meg, 11.2896_MHz_XTAL);
}

ROM_START(an1x)
	ROM_REGION16_BE(0x100000, "program", 0)
	ROM_LOAD16_WORD_SWAP("yamaha an1x 1.04.ic2", 0x000000, 0x100000, CRC(f03b8c30) SHA1(778b669a450660f2b15cc385f156dc6676c437f1))

	ROM_REGION(0x1000, "pks", 0)
	ROM_LOAD("xn668a00.bin", 0x0000, 0x1000, NO_DUMP)

	ROM_REGION16_BE(0x20000, "vop3", 0)
	ROM_LOAD("xt113b00.ic9", 0x00000, 0x20000, NO_DUMP) // LH531024

	ROM_REGION16_BE(0x20000, "vop3s", 0)
	ROM_LOAD("xt113b00.ic1", 0x00000, 0x20000, NO_DUMP) // LH531024
ROM_END

} // anonymous namespace

SYST(1997, an1x, 0, 0, an1x, an1x, an1x_state, empty_init, "Yamaha", "AN1x Control Synthesizer", MACHINE_NOT_WORKING)



ymdx100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR, Pietro Gagliardi
/*******************************************************************************

Yamaha DX27 and DX100 digital synthesizers

The DX27 and DX100 are mid-tier professional synthesizers released by Yamaha
around 1985. The DX27 is a full-size keyboard with 61 full-size keys that can
only run on AC power. The DX100 is a smaller, wearable keyboard with only 49
small-size keys and can run on either AC power or batteries. Both keyboards have
full MIDI in/out/thru, and can also hook up to a Yamaha foot pedal and breath
controller.

Apart from the differences listed above and a few less significant differences,
these two keyboards are largely identical. In fact, they use the exact same
mainboard, with both DX27 and DX100 printed on the silkscreen; a marker was used
to cross out the irrelevant model number during assembly. The front panel and
keyboard decoding circuits differ, as do the LCD/LED screen assemblies.

These two appear to be based on the DX21, a slightly higher-end keyboard released
by Yamaha earlier in 1985 that features a hardware chorus effect (implemented
independently of the FM synthesis chip) and some additional voice parameters (which
appear to be implemented in software). There is also the DX27S, which appears to
be based on the DX27 but adds a few extra features. Neither of these two models
are covered here as of this writing.

The main CPU is a Hitachi HD6303; its I/O circuitry is used extensively.
Some of the controls and inputs are analog; a M58990 ADC chip is used for these.

The FM synthesis chip is the YM2164 "OPP", a proprietary variant of the YM2151
that changes the rate of Timer B, moves the test/LFO reset register, and adds
an additional set of 8 registers used by the breath controller code. This chip
was also used in the DX21 (and possibly also the DX27S), but is perhaps most
famously used by the SFG-05 expansion module for the MSX and the FB-01 standalone
desktop MIDI synthesizer. As these two don't support the breath controller, the
extra registers were totally unused (set to 0 by the firmware), and as a result
sound and behave entirely identically to the YM2151. (Figuring out what these
registers did was part of the impetus for me (Pietro Gagliardi) actually building
this emulation.)

In addition to the independent instruction manuals for the DX27 and DX100, there is
also a single service manual that covers both keyboards (but not the DX27S, curiously
enough). The DX21 service manual comes with an "Overall Circuit Diagram" for that
keyboard; it is likely that the DX27/DX100 service manual also did, but PDFs available
online are missing it. A scan of the DX27/DX100 Overall Circuit Diagram is available
online separately; it does not appear to have spread as far and wide as the rest of the
service manual, but is still readily available.

*** Currently imperfectly emulated

    - The cassette interface.
      This uses an 8-pin DIN with what appears to be the same pinout as the MSX.
      However, the remote lines are completely unused, and the tape player has
      to be manually operated. The "dxconvert" project by rarepixel does claim to
      support DX100 tape dumps, and does so by using the "castools" project by
      joyrex2001, which includes a program to convert wav files storing the raw
      tape audio to MSX .cas files. dxconvert uses the command-line parameters
      "-p -e 4 -t 10"; I'm not sure how to interpret this, even after reading
      the code.

      The constant used below for testing against m_cassette->input() was guessed
      through trial and error until I could get a successful load at least somewhat
      consistently. It still isn't perfect, but I don't know how to refine this
      further.

      "dxconvert": https://github.com/rarepixel/dxconvert
      "castools": https://github.com/joyrex2001/castools
      The sample voices that I used to test can be found at
      http://robertgomez.org/blog/2017/08/02/some-yamaha-dx100-dx27-synth-patches/
      I loaded the wav download here, saved it to another wav,
      verified that wav (the DX100 will ask to verify data you just
      saved), and then converted both to .cas as described and compared
      the two; they are identical.

*** Currently unemulated

    - Bit 5 of port 6 is tied to the /G2A and /G2B pins of the two
      TC40H138P chips (~~ 74138?) that sit between the panel switches and the CPU.
      I don't yet understand how these lines are actually used, but I can still use
      the full functions of the keyboard if I just have it return 0 on read, so.

*** CPU ports

    Port 2 is arranged as so:
    Bit(s)    Connection
    0         Controls the state of the power LED on the DX100.
              The firmware will blink this LED when battery power is low.
              This is unused on the DX27.
    1         0 (pulled to chassis ground by 220 ohm resistor)
    2         500khz clock
    3         MIDI In data bit
    4         MIDI Out data bit
    5         0 if the foot pedal is connected, 1 otherwise.
              The manual says to use either the Yamaha FC-4 or Yamaha FC-5 foot pedals
              with the DX100.
    6         Pulled TODO if the foot pedal is pressed.
    7         0 (pulled to chassis ground by 220 ohm resistor)

    Port 5 is connected to the panel switches and keyboard keys.
    Which sets of buttons are exposed is determined by the low four bits
    of port 6. The exact matrix differs between the DX27 and DX100.

    Port 6 is arranged as so:
    Bit(s)    Connection
    0-3       Which set of buttons to expose on port 5.
    4         Connected to the EOC line of the ADC.
    5         (see /G2A and /G2B comment in Currently unemulated)
    6         Connected to the REC (TS) line of the cassette interface.
    7         Connected to the PLAY (TL) line of the cassette interface.

*** M58990 ports

    Port 0 is connected to the pitch wheel.

    Port 1 is connected to the mod wheel.

    Port 2 is connected to the breath controller.
    The manual says to use the Yamaha BC-1 breath controller with the DX100.

    Port 3 is connected to the data entry slider.

    (TODO port 4 appears to be connected to chassis ground by 220 ohm resistor?)

    Port 5 is a voltmeter for the RAM battery.

    Port 6 is a voltmeter for system power.

    (TODO port 7 appears to be tied to port 6?)

*** Default keyboard layout

    For quick testing, the default keyboard button layout will be:
    1 2 3 4 5 6 7 8 9 0 - = -> 1 2 3 4 5 6 7 8 9 10 11 12
    Q W  T Y  O P -> PBend KeyShift  Store Func      BankA BankB
    E R  U I  [ ] -> -1    +1        Edit  Internal  BankC BankD
    Octave 3 will be available over [Z S X D C  V G B H N J M]
    Pitch bend will be ' /
    Mod wheel will be ; .
    Data entry slider will be L ,

    This leaves the breath controller, switches 13-24, and the other
    keyboard octaves unmapped by default.

    When first starting with a fresh configuration and no NVRAM,
    press Y and then O (Func and then BankA) to switch to the
    internal voices. Note that some voices may not respond to
    the pitch or mod wheels as much as other voices do.

    To disable memory protection, press Y, ], and then E (Func,
    12 to select the Memory Protection setting, and -1 to turn
    it off). Then you can press U (Edit) to edit voice
    parameters.

    IPT_KEYBOARD is used, so use the UI mode key (default
    Scroll Lock) to re-enable the MAME UI.

    No, I don't know why some voices (such as Pianobells)
    seem to not let you turn off modulation even if you set
    the mod wheel to its lowest point. You can set the pitch
    sensitivity value ("PMS", Edit/Compare key 9) to 0 to
    force it off. This might have to do with the YM2164
    moving the LFO Reset register; if not, it might have to
    do with the default pitch wheel position not being 0
    (see TODO below).

*** Test mode

    To enter test mode, hold 1 and 2 on the panel while powering the system
    on. You'll see the version number, and then a prompt asking if you want
    to enter test mode; press +1 to enter test mode.

    If 1 and 3 or 1 and 4 are held instead of 1 and 2, different subsets of the
    test mode will run instead. Furthermore, some tests will only be run on
    the DX100.

    For more details on the individual tests, refer to the service manual.

    To make developing this driver easier I wrote an init function for the
    DX100 1.1 driver which allows me to pick and choose which tests to actually
    run. I've disabled it in the final version of the driver, but here it is
    in this comment if you want to run it yourself:

void yamaha_dx100_state::init_dx100()
{
    // Since test mode forcibly halts on the first failed test, and we can only choose to not run certain tests, use these blocks of code to skip specific tests.
    // The numbering and naming below is from the service manual, which, surprise, doesn't quite line up with the way the tests are written in the code.
    u8 *rom = (u8 *) (memregion("program")->base());
    auto skip = [rom](u16 whichAddr, u16 jumpTo, u8 extraInst = 0, u16 nop2 = 0) {
        if (extraInst != 0) {
            rom[whichAddr & 0x7fff] = extraInst;
            whichAddr++;
        }
        rom[whichAddr & 0x7fff] = 0x7e;
        rom[(whichAddr + 1) & 0x7fff] = (u8) ((jumpTo >> 8) & 0xff);
        rom[(whichAddr + 2) & 0x7fff] = (u8) (jumpTo & 0xff);
        if (nop2 != 0) {
            rom[nop2 & 0x7fff] = 0x01;
            rom[(nop2 + 1) & 0x7fff] = 0x01;
        }
    };
    // 1. Output level, RAM battery voltage
    skip(0xc464, 0xc4b3);
    // 2. RAM, cassette interface, MIDI in/out, MIDI thru
    // This is hard to skip as a unit in the code, so you need to skip them individually:
        // Or to only disable a subset of these tests:
        // 2a. RAM test
        rom[0xc55f & 0x7fff] = 0x39;
        // 2b. Cassette interface test (I'm not entirely sure MAME's cassette interface
        //     supports the direct writing; I *think* this is MSX format?)
        rom[0xc66e & 0x7fff] = 0x39;
        // 2c. MIDI in/out (I'm not sure why this is failing, but something's not right)
        rom[0xc631 & 0x7fff] = 0x39;
        // 2d. MIDI thru
        skip(0xc6b0, 0xc52a, 0, 0xc4dd);
    // 3. LCD test
    // This is actually two tests in the code, so you need to skip them individually:
        // 2a. LCD solid flashing test
        skip(0xc6e1, 0xc52a, 0, 0xc4e6);
        // 2b. LCD checkerboard test
        skip(0xc6d1, 0xc52a, 0, 0xc4ef);
    // 4. Pitch wheel, mod wheel, data entry slider, breath controller, foot switch
    // AND (run as part of 4 in the code)
    // 5. Foot switch detecting circuit
    skip(0xc70f, 0xc7a9);
        // Or to only disable a subset of these tests:
        // 4a/5. Foot switch tests
        skip(0xc7b3, 0xc7a9, 0x38);
    // 6. Keyboard
    skip(0xc86e, 0xc927);
    // 7. LCD contrast
    // AND (run as part of 7 in the code)
    // 8. Main power, LED flash on low power
    skip(0xc927, 0xc98f);
    // 9. Panel switches
    skip(0xc9d3, 0xca50);
}

*******************************************************************************/

#include "emu.h"

#include "bus/midi/midi.h"
#include "cpu/m6800/m6801.h"
#include "imagedev/cassette.h"
#include "machine/adc0808.h"
#include "machine/clock.h"
#include "machine/nvram.h"
#include "sound/ymopm.h"
#include "video/hd44780.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "dx100.lh"

namespace {

class yamaha_dx100_state : public driver_device
{
public:
	yamaha_dx100_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_adc(*this, "adc")
		, m_keysbuttons(*this, "P6_%d", 0)
		, m_midi_in(true)
		, m_led(*this, "LED")
		, m_cassette(*this, "cassette")
	{
	}

	void dx100(machine_config &config);

	void led_w(int state)                  { m_led = state; }
	ioport_value midi_in_r() { return m_midi_in; }

protected:
	virtual void driver_start() override;
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	HD44780_PIXEL_UPDATE(lcd_pixel_update);
	void palette_init(palette_device &palette);

	void p22_w(int state);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<hd6303x_cpu_device> m_maincpu;
	required_device<m58990_device> m_adc;
	required_ioport_array<16> m_keysbuttons;

	bool m_midi_in;

	u8 m_port6_val;

	// for hooking up the LED to layouts
	output_finder<> m_led;

	required_device<cassette_image_device> m_cassette;
};

void yamaha_dx100_state::driver_start()
{
	m_led.resolve();
}

void yamaha_dx100_state::machine_start()
{
	save_item(NAME(m_midi_in));
}

void yamaha_dx100_state::machine_reset()
{
	// TODO: figure out the actual power-on state
	m_port6_val = 0;
}

HD44780_PIXEL_UPDATE(yamaha_dx100_state::lcd_pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 8)
		bitmap.pix(y + 1 + ((y == 7) ? 1 : 0), (line * 8 + pos) * 6 + x + 1) = state ? 1 : 2;
}

void yamaha_dx100_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(0xff, 0xff, 0xff)); // background
	palette.set_pen_color(1, rgb_t(0x00, 0x00, 0x00)); // lcd pixel on
	palette.set_pen_color(2, rgb_t(0xe7, 0xe7, 0xe7)); // lcd pixel off
}

void yamaha_dx100_state::p22_w(int state)
{
	if (state)
		m_maincpu->clock_serial();
}

void yamaha_dx100_state::mem_map(address_map &map)
{
	map(0x0800, 0x0fff).ram().share("nvram");
	map(0x1000, 0x17ff).ram();
	map(0x2000, 0x2001).rw("lcdc", FUNC(hd44780_device::read), FUNC(hd44780_device::write));
	map(0x2800, 0x2800).r("adc", FUNC(m58990_device::data_r));
	map(0x3000, 0x3000).w("adc", FUNC(m58990_device::address_data_start_w));
	map(0x3800, 0x3801).rw("ymsnd", FUNC(ym2164_device::read), FUNC(ym2164_device::write));
	map(0x8000, 0xffff).rom().region("program", 0);
}

static INPUT_PORTS_START(dx100)
	PORT_START("P2")
	// TODO: Should 0x02, 0x04, 0x10, and 0x80 be listed here?
	// They should be handled by the other interconnections in this file.
	// If so, verify the active states of the MIDI ports.
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OUTPUT )   PORT_NAME("LED") PORT_WRITE_LINE_MEMBER(FUNC(yamaha_dx100_state::led_w))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )  // tied to ground
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_CUSTOM )  // 500khz clock
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM )  PORT_CUSTOM_MEMBER(FUNC(yamaha_dx100_state::midi_in_r))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OUTPUT )  // MIDI out
	PORT_CONFNAME( 0x20, 0x00, "Foot Switch" )
	PORT_CONFSETTING( 0x00, "Connected" )
	PORT_CONFSETTING( 0x20, "Disconnected" )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER )   PORT_NAME("Foot Switch")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )  // tied to ground

	PORT_START("P6_0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("Data Entry +1/Yes/On") PORT_CODE(KEYCODE_R)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("Data Entry -1/No/Off") PORT_CODE(KEYCODE_E)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("Store/EG Copy") PORT_CODE(KEYCODE_T)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("Function/Compare") PORT_CODE(KEYCODE_Y)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("Edit/Compare") PORT_CODE(KEYCODE_U)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("Internal/Play") PORT_CODE(KEYCODE_I)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("Pitch B Mode/Operator Select/Mode Set") PORT_CODE(KEYCODE_Q)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("Key Shift/Key Set") PORT_CODE(KEYCODE_W)

	PORT_START("P6_1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("1/Algorithm/Master Tune Adj") PORT_CODE(KEYCODE_1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("2/Feedback/MIDI On/Off") PORT_CODE(KEYCODE_2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("3/LFO Wave/MIDI Channel") PORT_CODE(KEYCODE_3)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("4/LFO Speed/MIDI Ch Info") PORT_CODE(KEYCODE_4)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("5/LFO Delay/MIDI Sys Info") PORT_CODE(KEYCODE_5)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("6/LFO PMD/Recall Edit") PORT_CODE(KEYCODE_6)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("7/LFO AMD/Init Voice") PORT_CODE(KEYCODE_7)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("8/LFO Sync/Edit Bank") PORT_CODE(KEYCODE_8)

	PORT_START("P6_2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("9/Modulation Sensitivity Pitch/Cassette Save/Verify") PORT_CODE(KEYCODE_9)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("10/Modulation Sensitivity Velocity/Cassette Load") PORT_CODE(KEYCODE_0)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("11/Modulation Sensitivity EG Bias/Cassette Load Single") PORT_CODE(KEYCODE_MINUS)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("12/Key Velocity/Memory Protect") PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("13/Oscillator Freq Ratio/Poly/Mono")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("14/Oscillator Detune/Pitch Bend Range")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("15/Envelope Generator AR/Portamento Mode")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("16/Envelope Generator D1R/Portamento Time")

	PORT_START("P6_3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("17/Envelope Generator D1L/Foot Sw Assign")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("18/Envelope Generator D2R/Wheel Range Pitch")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("19/Envelope Generator RR/Wheel Range Amplitude")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("20/Operator Out Level/Breath Range Pitch")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("21/Keyboard Scaling Rate/Breath Range Amplitude")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("22/Keyboard Scaling Level/Breath Range Pitch Bias")
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("23/Transpose/Breath Range EG Bias")
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("24/Voice Name Cursor >")

	PORT_START("P6_4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("C#1")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("C#2")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("C#3") PORT_CODE(KEYCODE_S)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("C#4")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("Bank D/Operator/AMS On/Off 4/Preset Search 401~424") PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("Bank C/Operator/AMS On/Off 3/Preset Search 301~324") PORT_CODE(KEYCODE_OPENBRACE)

	PORT_START("P6_5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("D1")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("D2")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("D3") PORT_CODE(KEYCODE_X)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("D4")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("Bank B/Operator/AMS On/Off 2/Preset Search 201~224") PORT_CODE(KEYCODE_P)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER )  PORT_NAME("Bank A/Operator/AMS On/Off 1/Preset Search 101~124") PORT_CODE(KEYCODE_O)

	PORT_START("P6_6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("D#1")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("D#2")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("D#3") PORT_CODE(KEYCODE_D)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("D#4")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P6_7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("E1")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("E2")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("E3") PORT_CODE(KEYCODE_C)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("E4")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P6_8")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("F1")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("F2")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("F3") PORT_CODE(KEYCODE_V)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("F4")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P6_9")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("F#1")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("F#2")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("F#3") PORT_CODE(KEYCODE_G)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("F#4")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P6_10")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("G1")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("G2")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("G3") PORT_CODE(KEYCODE_B)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("G4")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P6_11")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("G#1")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("G#2")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("G#3") PORT_CODE(KEYCODE_H)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("G#4")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P6_12")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("A1")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("A2")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("A3") PORT_CODE(KEYCODE_N)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("A4")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P6_13")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("A#1")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("A#2")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("A#3") PORT_CODE(KEYCODE_J)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("A#4")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P6_14")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("B1")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("B2")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("B3") PORT_CODE(KEYCODE_M)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("B4")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("P6_15")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("C1")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("C2")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("C3") PORT_CODE(KEYCODE_Z)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("C4")
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD )  PORT_NAME("C5")
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("AN0")
	// The pitch wheel returns to center once released.
	PORT_BIT( 0xff, 0x7f, IPT_AD_STICK_Y ) PORT_NAME("Pitch Wheel") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00, 0xff) PORT_CODE_INC(KEYCODE_QUOTE) PORT_CODE_DEC(KEYCODE_SLASH)

	PORT_START("AN1")
	// The mod wheel stays in place to wherever it's set.
	// TODO: This should have its initial state be 0, not halfway.
	PORT_BIT( 0xff, 0, IPT_PADDLE_V ) PORT_NAME("Modulation Wheel") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00, 0xff) PORT_CENTERDELTA(0) PORT_CODE_INC(KEYCODE_COLON) PORT_CODE_DEC(KEYCODE_STOP)

	PORT_START("AN2")
	// TODO: This appears to be inverted; if this is set to 255 it behaves as if
	// there was no breath controller? or at least seems to? on instruments like
	// 112 Pianobrass if this is set to 0 it acts as if the mod wheel had been turned
	// all the way up and enables LFO -- and there's probably a better way we could
	// simulate not having a breath controller attached at all???
	PORT_BIT( 0xff, 0, IPT_PEDAL ) PORT_NAME("Breath Controller") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00, 0xff)

	PORT_START("AN3")
	// The data entry slider stays in place to wherever it's set.
	PORT_BIT( 0xff, 0, IPT_POSITIONAL_V ) PORT_NAME("Data Entry Slider") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00, 0xff) PORT_CENTERDELTA(0) PORT_CODE_INC(KEYCODE_L) PORT_CODE_DEC(KEYCODE_COMMA)

	PORT_START("AN5")
	PORT_CONFNAME( 0xff, 0x80, "Internal RAM Battery Level" )
	// "CNG RAM BATTERY!" displayed unless value is between 0x70 and 0xcc
	PORT_CONFSETTING( 0x6f, "Too Low" )
	PORT_CONFSETTING( 0x70, "Lowest Allowed" )
	PORT_CONFSETTING( 0x80, "Normal" ) // for some arbitrary definition of "normal"
	PORT_CONFSETTING( 0xcb, "Highest Allowed" )
	PORT_CONFSETTING( 0xcc, "Too High" )

	PORT_START("AN6")
	PORT_CONFNAME( 0xff, 0x00, "Battery Power Level" )
	// Yes, higher values mean lower voltages.
	// I think this is opposite to how the RAM battery voltmeter works.
	// The weird granularity here is due to the buggy implementation of the
	// test in the test mode; all 7V values should LED flash, but some don't.
	PORT_CONFSETTING( 0x00, "9V (Normal)" ) // for some arbitrary definition of "normal"
	PORT_CONFSETTING( 0x4b, "9V (Lowest Allowed)" )
	PORT_CONFSETTING( 0x67, "7V (Highest Allowed Without LED Flash)" )
	PORT_CONFSETTING( 0x6b, "7V (Lowest Allowed Without LED Flash)" )
	PORT_CONFSETTING( 0x6c, "7V (Highest Allowed With LED Flash)" )
	PORT_CONFSETTING( 0x6f, "7V (Lowest Allowed With LED Flash)" )
	PORT_CONFSETTING( 0x70, "Less Than 7V" )
INPUT_PORTS_END

void yamaha_dx100_state::dx100(machine_config &config)
{
	HD6303X(config, m_maincpu, 7.15909_MHz_XTAL / 2); // HD6303XP
	m_maincpu->set_addrmap(AS_PROGRAM, &yamaha_dx100_state::mem_map);
	m_maincpu->in_p2_cb().set_ioport("P2");
	m_maincpu->out_p2_cb().set_ioport("P2");
	m_maincpu->in_p6_cb().set([this]() -> u8 {
		// TODO: The threshold here and the output values below should be double-checked.
		u8 casplay = (m_cassette->input() > 0.009) ? 0x80 : 0;
		// TODO: Need to verify if this bit is held on output or not
		u8 casrec = m_port6_val & 0x40;
		u8 adcval = ((u8) (m_adc->eoc_r() << 4)) & 0x10;
		u8 port5line = (m_port6_val & 0x2f);
		return casplay | casrec | adcval | port5line;
	});
	m_maincpu->out_p6_cb().set([this](u8 val) {
		m_port6_val = val;
		m_cassette->output(((m_port6_val & 0x40) != 0) ? -1.0 : 1.0);
	});
	m_maincpu->in_p5_cb().set([this]() -> u8 {
		u8 line = m_port6_val & 0x2f;
		// TODO: figure out how the /G2A and /G2B pins are connected
		if ((line & 0x20) != 0)
			return 0x00;
		return (u8) (m_keysbuttons[line]->read() & 0xff);
	});
	m_maincpu->out_ser_tx_cb().set("mdout", FUNC(midi_port_device::write_txd));

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // TC5518BPL + CR2032T battery

	M58990(config, m_adc, 7.15909_MHz_XTAL / 8); // M58990P-1 (clocked by E)
	m_adc->in_callback<0>().set_ioport("AN0");   // pitch wheel
	m_adc->in_callback<1>().set_ioport("AN1");   // mod wheel
	m_adc->in_callback<2>().set_ioport("AN2");   // breath controller
	m_adc->in_callback<3>().set_ioport("AN3");   // data entry slider
	m_adc->in_callback<5>().set_ioport("AN5");   // internal RAM battery voltmeter
	m_adc->in_callback<6>().set_ioport("AN6");   // battery power voltmeter

	CLOCK(config, "subclock", 500_kHz_XTAL).signal_handler().set(FUNC(yamaha_dx100_state::p22_w));

	MIDI_PORT(config, "mdin", midiin_slot, "midiin").rxd_handler().set([this](int state) { m_midi_in = state; });
	MIDI_PORT(config, "mdout", midiout_slot, "midiout");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6*16+1, 10*1+1);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(yamaha_dx100_state::palette_init), 3);

	hd44780_device &lcdc(HD44780(config, "lcdc", 270'000)); // HD44780RA00, 91K resistor
	lcdc.set_lcd_size(1, 16);
	lcdc.set_pixel_update_cb(FUNC(yamaha_dx100_state::lcd_pixel_update));

	SPEAKER(config, "speaker", 2).front();

	ym2164_device &ymsnd(YM2164(config, "ymsnd", 7.15909_MHz_XTAL / 2)); // with YM3014 DAC
	ymsnd.add_route(0, "speaker", 0.60, 0);
	ymsnd.add_route(1, "speaker", 0.60, 1);

	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED);
	m_cassette->set_interface("ymdx100_cass");

	config.set_default_layout(layout_dx100);
}

ROM_START(dx100)
	ROM_REGION(0x8000, "program", 0)
	ROM_LOAD("dx100 v1.1.bin", 0x0000, 0x8000, CRC(c3ed7c86) SHA1(5b003db1bb5c1909907153f6446b63b07f5b41d6))
ROM_END

} // anonymous namespace

SYST(1985, dx100, 0, 0, dx100, dx100, yamaha_dx100_state, empty_init, "Yamaha", "DX100 Digital Programmable Algorithm Synthesizer", MACHINE_IMPERFECT_SOUND)




ymdx11.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Yamaha DX11 synthesizer.

****************************************************************************/

#include "emu.h"
//#include "bus/midi/midi.h"
#include "cpu/m6800/m6801.h"
#include "machine/adc0808.h"
#include "machine/nvram.h"
#include "sound/ymopz.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class yamaha_dx11_state : public driver_device
{
public:
	yamaha_dx11_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_subcpu(*this, "subcpu")
		, m_adc(*this, "adc")
		, m_rombank(*this, "rombank")
	{
	}

	void dx11(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	HD44780_PIXEL_UPDATE(lcd_pixel_update);
	void palette_init(palette_device &palette);

	void cartridge_bank_w(u8 data);

	void main_map(address_map &map) ATTR_COLD;

	required_device<hd6301y_cpu_device> m_maincpu;
	required_device<hd6301y_cpu_device> m_subcpu;
	required_device<adc0808_device> m_adc;
	required_memory_bank m_rombank;
};

void yamaha_dx11_state::machine_start()
{
	m_rombank->configure_entries(0, 2, memregion("firmware")->base(), 0x8000);
}


HD44780_PIXEL_UPDATE(yamaha_dx11_state::lcd_pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 16)
		bitmap.pix(line * 8 + y, pos * 6 + x) = state;
}

void yamaha_dx11_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(131, 136, 139));
	palette.set_pen_color(1, rgb_t( 92,  83,  88));
}


void yamaha_dx11_state::cartridge_bank_w(u8 data)
{
}

void yamaha_dx11_state::main_map(address_map &map)
{
	map(0x1400, 0x1400).mirror(0x3ff).w(FUNC(yamaha_dx11_state::cartridge_bank_w));
	map(0x1800, 0x1801).mirror(0x3fe).rw("opz", FUNC(ym2414_device::read), FUNC(ym2414_device::write));
	map(0x1c00, 0x1c01).mirror(0x3fe).rw("lcdc", FUNC(hd44780_device::read), FUNC(hd44780_device::write));
	map(0x2000, 0x3fff).ram().share("ram1");
	map(0x4000, 0x5fff).ram().share("ram2");
	//map(0x6000, 0x7fff).rw(FUNC(yamaha_dx11_state::cartridge_r), FUNC(yamaha_dx11_state::cartridge_w));
	map(0x8000, 0xffff).bankr("rombank");
}


static INPUT_PORTS_START(dx11)
INPUT_PORTS_END

void yamaha_dx11_state::dx11(machine_config &config)
{
	HD6303Y(config, m_maincpu, 8_MHz_XTAL); // HD63B03YP-N
	m_maincpu->set_addrmap(AS_PROGRAM, &yamaha_dx11_state::main_map);
	m_maincpu->out_p2_cb().set_membank("rombank").bit(6);

	HD6301Y0(config, m_subcpu, 8_MHz_XTAL).set_disable(); // HD63B01Y0D60P

	NVRAM(config, "ram1", nvram_device::DEFAULT_ALL_0); // TC5564APL-15 + lithium battery
	NVRAM(config, "ram2", nvram_device::DEFAULT_ALL_0); // TC5564APL-15 + lithium battery

	M58990(config, m_adc, 8_MHz_XTAL / 16); // M58990P-1; divider not verified (actually clocked by P26 output of sub CPU)

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_color(rgb_t::green());
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6*16, 8*2);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(yamaha_dx11_state::palette_init), 2);

	hd44780_device &lcdc(HD44780(config, "lcdc", 270'000)); // TODO: clock not measured, datasheet typical clock used
	lcdc.set_lcd_size(2, 16);
	lcdc.set_pixel_update_cb(FUNC(yamaha_dx11_state::lcd_pixel_update));

	SPEAKER(config, "speaker", 2).front();

	ym2414_device &opz(YM2414(config, "opz", 3.579545_MHz_XTAL));
	//opz.irq_handler().set_inputline(m_maincpu, hd6301y_cpu_device::IRQ2_LINE); // IRQ = P51
	opz.add_route(0, "speaker", 0.60, 0);
	opz.add_route(1, "speaker", 0.60, 1);
}

ROM_START(dx11)
	ROM_REGION(0x10000, "firmware", 0)
	ROM_LOAD("dx11_xd820_bo.ic15", 0x00000, 0x10000, CRC(d5355df7) SHA1(a5376c32fcce9e93a22b71ec476b14ff3091153f)) // 27C512

	ROM_REGION(0x4000, "subcpu", 0)
	ROM_LOAD("hd63b01y0d60p.ic18", 0x0000, 0x4000, NO_DUMP)
ROM_END

} // anonymous namespace


SYST(1988, dx11, 0, 0, dx11, dx11, yamaha_dx11_state, empty_init, "Yamaha", "DX11 Digital Programmable Algorithm Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



ymdx7.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:David Viens, R. Belmont
/***************************************************************************
    ymdx7.cpp: Preliminary driver for Yamaha DX7
    Copyright 2011-2022 David Viens

    Main CPU is a HD63B03RP
    Sub  CPU is a HD6805S1P-A33 (ROM on chip)
    The FM sound system is done by two discrete chips:
    OPS(YM2128) and EGS(YM2129)
    ///////////////// ////////////////////////////////////////////////////////
    Main CPU PCB fixed pins:
    PIN02:  XTAL : NC
    PIN03:  EXTAL: 4.713250MHz (internally divided by 4 to get 1.178312MHz)
    PIN04: !NMI  : 1 (4.7K pull-up)
    PIN05: !IRQ  : 1 (4.7K pull-up) but... linked to Sub CPU
    PIN07: !STBY : 1 (4.7K pull-up)
    /////////////////////////////////////////////////////////////////////////
    RAM access is done through logic on the PCB when
    bit 6 of port 5 (RAME) is low.
    PORT1:direction 0 (all bits read only!)
    P10 to P17 : 8bits that are mapped to Sub CPU's shared Address/Data line
    PORT2 direction 0x11 (XXX10001)
    P20 : write to   TC4053's X (pin14) (SUB CPU BUSY)
    P21 : read  from TC4053's Y (pin15) (SUB CPU handshake)
    P22 : read  from TC4053's Z (pin 4) (250KHz Clock)
    P23 : read  from MIDI IN
    P24 : write to   MIDI OUT
****************************************************************************/

#include "emu.h"
#include "cpu/m6800/m6801.h"
#include "cpu/m6805/m68705.h"
#include "machine/adc0808.h"
#include "machine/i8255.h"
#include "machine/nvram.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"

#define LOG_OPS     (1U << 1)
#define LOG_EGS     (1U << 2)

// #define VERBOSE (LOG_GENERAL)
#define LOG_OUTPUT_FUNC osd_printf_info

#include "logmacro.h"


namespace {

constexpr auto DX7CLOCK    = 9'426'500;

constexpr auto LCD_E  = 0x02;
constexpr auto LCD_RW = 0x04;
constexpr auto LCD_RS = 0x01;


class yamaha_dx7_state : public driver_device
{
public:
	yamaha_dx7_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_subcpu(*this, "subcpu")
		, m_lcdc(*this, "hd44780")
		, m_adc(*this, "adc")
		, m_ppi(*this, "i8255")
	{
	}

	void dx7(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	HD44780_PIXEL_UPDATE(lcd_pixel_update);
	void palette_init(palette_device &palette);

	void dx7_led_w (offs_t offset, u8 data)
	{
		// TODO: LEDs
	}

	u8 dx7_p1_r(offs_t offset);
	u8 dx7_p2_r(offs_t offset);
	void dx7_p2_w(offs_t offset, u8 data);
	void dx7_acept_w(offs_t offset, u8 data);

	void dx7_ops_w(offs_t offset, u8 data)
	{
		LOGMASKED(LOG_OPS, "OPS W: %02X=%02X\n", offset, data);
	};

	void dx7_egs_w(offs_t offset, u8 data)
	{
		LOGMASKED(LOG_EGS, "EGS W: %02X=%02X\n", offset, data);
	};

	void dx7_dac_w  (offs_t offset, u8 data) {}; // TODO: DAC control

	void main_map(address_map &map) ATTR_COLD;
	void sub_map (address_map &map) ATTR_COLD;

	required_device<hd6303r_cpu_device> m_maincpu;
	required_device<hd6805s1_device> m_subcpu;
	required_device<hd44780_device> m_lcdc;
	required_device<adc0808_device> m_adc;
	required_device<i8255_device> m_ppi;

	int m_clk;
	u8 m_PORT1;
	u8 m_PORT2;
	u8 m_irq0;
	u8 m_portA, m_portB;
	int m_acept;

	u8 ppi_porta_r();
	u8 ppi_portb_r();
	u8 ppi_portc_r();
	void ppi_porta_w(u8 data);
	void ppi_portb_w(u8 data);
};

 void yamaha_dx7_state::machine_start()
 {
	 m_irq0 = 1;
	 m_clk = 0;
	 m_PORT1 = m_PORT2 = 0;
	 m_acept = 0;
 }

 void yamaha_dx7_state::dx7_acept_w(offs_t address, u8 data)
{
	m_acept++;
	LOG("ACEPT reg:0x%x = 0x%x \n", address, data);
	m_irq0 = 1;
	m_maincpu->set_input_line(M6800_IRQ_LINE, CLEAR_LINE);
}
u8 yamaha_dx7_state::dx7_p1_r(offs_t)
{
	LOG("reading PORT1\n");
	return m_PORT1;
}
u8 yamaha_dx7_state::dx7_p2_r(offs_t)
{
	LOG("reading PORT2\n");
	u8 temp = 0; //P0 is WRITE! does the CPU re-reads its OWN data? can it?
	if(m_irq0)
	{
		temp = temp | 0x02; // P21 is connected to !IRQ line
	}

	if(m_clk)
	{
		temp = temp | 0x04; // P22 is connected to 250Khz clock
	}

	// TODO: MIDI INPUT
	return temp;
}
void yamaha_dx7_state::dx7_p2_w(offs_t offset, u8 data)
{
	LOG("W PORT2 0x%x\n", data);
	m_PORT2 = data;
}

u8 yamaha_dx7_state::ppi_porta_r()
{
	return 0;
}

u8 yamaha_dx7_state::ppi_portb_r()
{
	return 0;
}

u8 yamaha_dx7_state::ppi_portc_r()
{
	// Sustain and Portamento (active LOW) bits 0 & 1
	// LCD busy in bit 7
	u8 retval = 3;

	if (m_portB & LCD_RW)
	{
		retval |= (m_lcdc->read(0) & 0x80);
	}

	return retval;
}

void yamaha_dx7_state::ppi_porta_w(u8 data)
{
	m_portA = data;
}

void yamaha_dx7_state::ppi_portb_w(u8 data)
{
	// only write on the rising edge of E
	if (!(m_portB & LCD_E) && (data & LCD_E) && !(data & LCD_RW))
	{
		m_lcdc->write(data & LCD_RS, m_portA);
	}
	m_portB = data;
}

HD44780_PIXEL_UPDATE(yamaha_dx7_state::lcd_pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 16)
	{
		bitmap.pix(line * 8 + y, pos * 6 + x) = state;
	}
}

void yamaha_dx7_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(131, 136, 139));
	palette.set_pen_color(1, rgb_t( 92,  83,  88));
}

void yamaha_dx7_state::main_map(address_map &map)
{
	//decoded from IC23 74LS138 using A11 to A15 (2kb blocks)
	//Y0 (0x0000) NC
	//Y1 (0x0800) NC
	//Y2 (0x1000) RAM1
	//Y3 (0x1800) RAM2
	//Y4 (0x2000) RAM3
	//Y5 (0x2800) (another 74LS138), see below
	//Y6 (0x3000) EGS
	//Y7 (0x3800) NC

	map(0x1000, 0x17ff).ram().share("ram1");/* 2kb RAM1 IC19 M5M118P (Voice Memory part1) */
	map(0x1800, 0x1fff).ram().share("ram2");/* 2kb RAM2 IC20 M5M118P (Voice Memory part2) */
	map(0x2000, 0x27ff).ram().share("ram3");/* 2kb RAM3 IC21 M5M118P (Working Area)       */

	// the 0x2800 to 0x2fff range is then resplit by IC24(another 74LS138)
	// A3 A2 A1 A0
	//  0  0  0  X:  Y0 : IC12 8255 (LCD,porta/sustain)
	//  0  0  1  X:  Y1 : IC12 8255 (LCD,porta/sustain)
	//  0  1  0  X:  Y2 : OPS
	//  0  1  1  X:  Y3 : NC
	//  1  0  0  X:  Y4 : NC
	//  1  0  1  X:  Y5 : DAC
	//  1  1  0  X:  Y6 : ACEPT (IC5)
	//  1  1  1  X:  Y7 : LED DRIVERs

	map(0x2800, 0x2803).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x2804, 0x2805).w(FUNC(yamaha_dx7_state::dx7_ops_w));
	map(0x280a, 0x280b).w(FUNC(yamaha_dx7_state::dx7_dac_w));
	map(0x280c, 0x280d).w(FUNC(yamaha_dx7_state::dx7_acept_w));
	map(0x280e, 0x280f).w(FUNC(yamaha_dx7_state::dx7_led_w));

	//EGS (YM2129)
	map(0x3000, 0x37ff).w(FUNC(yamaha_dx7_state::dx7_egs_w));

	//map(0x4000, 0x4fff).rom().region("cartridge", 0); //or RAM!
	map(0xc000, 0xffff).rom().region("program", 0);
}

void yamaha_dx7_state::sub_map(address_map &map)
{
	map(0x0000, 0x07ff).rom().region("subcpu", 0);
}

static INPUT_PORTS_START(dx7)
INPUT_PORTS_END

void yamaha_dx7_state::dx7(machine_config &config)
{
	HD6303R(config, m_maincpu, DX7CLOCK/2); // HD63B03RP
	m_maincpu->set_addrmap(AS_PROGRAM, &yamaha_dx7_state::main_map);
	m_maincpu->in_p1_cb().set(FUNC(yamaha_dx7_state::dx7_p1_r));
	m_maincpu->in_p2_cb().set(FUNC(yamaha_dx7_state::dx7_p2_r));
	m_maincpu->out_p2_cb().set(FUNC(yamaha_dx7_state::dx7_p2_w));

	HD6805S1(config, m_subcpu, 4_MHz_XTAL); // HD6805S1P-A33
	m_subcpu->set_addrmap(AS_PROGRAM, &yamaha_dx7_state::sub_map);

	NVRAM(config, "ram1", nvram_device::DEFAULT_ALL_0); /* 2kb RAM1 IC19 M5M118P (Voice Memory part1) */
	NVRAM(config, "ram2", nvram_device::DEFAULT_ALL_0); /* 2kb RAM2 IC20 M5M118P (Voice Memory part2) */
	NVRAM(config, "ram3", nvram_device::DEFAULT_ALL_0); /* 2kb RAM3 IC21 M5M118P (Working Area)       */

	I8255(config, m_ppi);
	m_ppi->in_pa_callback().set(FUNC(yamaha_dx7_state::ppi_porta_r));
	m_ppi->in_pb_callback().set(FUNC(yamaha_dx7_state::ppi_portb_r));
	m_ppi->in_pc_callback().set(FUNC(yamaha_dx7_state::ppi_portc_r));
	m_ppi->out_pa_callback().set(FUNC(yamaha_dx7_state::ppi_porta_w));
	m_ppi->out_pb_callback().set(FUNC(yamaha_dx7_state::ppi_portb_w));

	M58990(config, m_adc, 8_MHz_XTAL / 16); // M58990P-1; divider not verified (actually clocked by P26 output of sub CPU)

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_color(rgb_t::green());
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update(m_lcdc, FUNC(hd44780_device::screen_update));
	screen.set_size(6*16, 8*2);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(yamaha_dx7_state::palette_init), 2);

	HD44780(config, m_lcdc, 270'000); // TODO: clock not measured, datasheet typical clock used
	m_lcdc->set_lcd_size(2, 16);
	m_lcdc->set_pixel_update_cb(FUNC(yamaha_dx7_state::lcd_pixel_update));
}

// From service manual there are two rom revisions mentioned:
// one that's split into two 2764's:
// iG 10570(0)  uPD 2764-2    ROM1 DX7~#2600
// iG 10575(0)  uPD 2764-2    ROM2 DX7~#2600
// and a single 27128:
// iG 11461(0)  HN 613128PC86 ROM DX7#2661~
// ig11464, ig11467, and ig11468 are known to exist also.
// ig11464 is dumped but contains no version string.

ROM_START(dx7)
	ROM_REGION(0x4000, "program", 0)
	ROM_DEFAULT_BIOS("v1.8")
	ROM_SYSTEM_BIOS(0, "v1.8", "DX-7 v1.8 24-Oct-85 IG11469")
	ROMX_LOAD( "ig11469.ic14", 0x0000, 0x4000, CRC(6cbb0865) SHA1(715dbb8e96a4df2a7f096b368334a7654860bb26), ROM_BIOS(0))

	ROM_SYSTEM_BIOS(1, "ig11464", "DX-7 v1.? IG11464")
	ROMX_LOAD( "ig11464.ic14", 0x0000, 0x4000, CRC(126c5a98) SHA1(ce4df31878dda9ec27b31c7bc172f16419264b90), ROM_BIOS(1))

	ROM_SYSTEM_BIOS(2, "ig11461", "DX-7 v1.? IG11461")
	ROMX_LOAD( "ig11461.ic14", 0x0000, 0x4000, CRC(fb50c62b) SHA1(21c4995d65d0ae6f4868ac89bf7a4ae81dc3bd31), ROM_BIOS(2))

	ROM_REGION(0x0800, "subcpu", 0)
	ROM_LOAD("hd6805s1p-a33.ic13", 0x0000, 0x800, CRC(ac1d84b3) SHA1(ee0ebb118dd0d282d7c195d3b246a0094b2cb6ad))
ROM_END

} // anonymous namespace


SYST(1983, dx7, 0, 0, dx7, dx7, yamaha_dx7_state, empty_init, "Yamaha", "DX7 Digital Programmable Algorithm Synthesizer", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



ymdx9.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:ajxs
/*******************************************************************************

    Skeleton driver for the Yamaha DX9 FM synthesizer.
    There is currently no MAME emulation of the OPS/EGS chips, so emulating
    the synth's tone generation functionality is not possible.
    The cassette interface is currently not emulated.
    While there are rumours that an updated firmware ROM exists, this driver
    is set up to work with the only one that is widely available.

*******************************************************************************/

#include "emu.h"

#include "bus/midi/midi.h"
#include "cpu/m6800/m6801.h"
#include "machine/adc0808.h"
#include "machine/clock.h"
#include "machine/nvram.h"
#include "video/hd44780.h"

#include "emupal.h"
#include "screen.h"

//#define VERBOSE 1
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"

#include "dx9.lh"


namespace {

class yamaha_dx9_state : public driver_device
{
public:
	yamaha_dx9_state(const machine_config &mconfig, device_type type, const char *tag) :
			driver_device(mconfig, type, tag),
			m_maincpu(*this, "maincpu"),
			m_adc(*this, "adc"),
			m_leds(*this, "led_%u", 0U),
			m_key_switch_input(*this, "KEY_SWITCH_INPUT.%u", 0)
	{
	}

	void dx9(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	required_device<hd6303r_cpu_device> m_maincpu;
	required_device<adc0808_device> m_adc;
	output_finder<2> m_leds;
	// This ioport array is used to communicate with the front-panel interface in the layout.
	// They emulate the circuits wired to the 'Key/Switch Scan Driver'.
	required_ioport_array<16> m_key_switch_input;

	/**
	 * @brief Which input line on the keyboard/switch scan driver is currently selected.
	 * This driver is used to read the analog switch values from the synth's keyboard, and
	 * front-panel switches.
	 * TODO: I'm currently not sure of the actual implementation of this circuit. This
	 * implementation is based off the *very* limited description in the service manual, and
	 * the behaviour that's shown in the firmware.
	 */
	uint8_t m_key_switch_input_select = 0;

	int m_rx_data;

	/** The polarity of the cassette interface's output line. */
	bool m_cassette_interface_output_polarity = false;

	/** The polarity of the cassette interface's remote line. */
	bool m_cassette_interface_remote_polarity = false;

	/**
	 * @brief LCD pixel update function.
	 * The `HD44780_PIXEL_UPDATE` macro expands the definition to include the correct
	 * parameters for the LCD update function.
	 * Refer to: `src/devices/video/hd44780.h` for the full definition format.
	 */
	HD44780_PIXEL_UPDATE(lcd_pixel_update);

	/**
	 * @brief Handles a write to the synth's OPS chip registers.
	 * This chip is currently not emulated, however this function is useful for debugging.
	 * @param offset The offset into the memory mapped region being written.
	 * @param data The data being written.
	 */
	void ops_w(offs_t offset, uint8_t data);

	/**
	 * @brief Handles a write to the synth's EGS chip registers.
	 * This chip is currently not emulated, however this function is useful for debugging.
	 * @param offset The offset into the memory mapped region being written.
	 * @param data The data being written.
	 */
	void egs_w(offs_t offset, uint8_t data);

	/**
	 * @brief Handles a read from the keyboard/switch scan driver.
	 * This multiplexing driver circuit is used to read the states of the synth's front-panel
	 * switches, and keyboard. The driver's input is wired to the CPU's IO port 1, and the
	 * output is wired into the address map.
	 * Input line 0 covers the 'main' front-panel switches.
	 * Input line 1 covers the numeric front-panel switches 1 through 8.
	 * Input line 2 covers the numeric front-panel switches 9 though 16.
	 * Input line 3 covers the numeric front-panel switches 17 though 20, as well as the
	 * modulation pedal inputs: The Portamento, and Sustain pedals are mapped to
	 * bits 6, and 7 respectively.
	 * Note: Input lines 4-15 are used to map the keyboard, which is not implemented here.
	 * When the keyboard state is read, the default value of 0 will be returned.
	 * @param offset The offset into the memory mapped region being read.
	 * @return uint8_t The value read from the bus.
	 */
	uint8_t key_switch_scan_driver_r(offs_t offset)
	{
		return m_key_switch_input[m_key_switch_input_select]->read();
	}

	/**
	 * @brief Handles a write to the 7-segment LED memory mapped region.
	 * This function is responsible for setting the two 7-segment LEDs set in the
	 * device's layout file.
	 * @param offset The offset into the memory mapped region being written.
	 * @param data The data being written.
	 */
	void led_w(offs_t offset, uint8_t data);

	void palette_init(palette_device &palette);

	void mem_map(address_map &map) ATTR_COLD;

	void midi_r(int state) { m_rx_data = state; }

	void midiclock_w(int state) { if (state) m_maincpu->clock_serial(); }

	/**
	 * @brief Handle a write to the synth's IO Port 1.
	 * IO Port 1 is mapped as follows:
	 *  Bit 0: Keyboard/Switch Scan Driver Input.
	 *  Bit 1: "".
	 *  Bit 2: "".
	 *  Bit 3: "".
	 *  Bit 4: ADC EOC Input Line.
	 *  Bit 5: Cassette Interface Remote Port.
	 *  Bit 6: Cassette Interface Tape Output.
	 *  Bit 7: Cassette Interface Tape Input.
	 * @param offset The offset into the memory mapped region being written.
	 * @param data The data being written.
	 */
	void p1_w(offs_t offset, uint8_t data);

	/**
	 * @brief Handle a read from the synth's IO Port 1.
	 * @param offset The offset into the memory mapped region being read.
	 * @return uint8_t The value read from the port.
	 */
	uint8_t p1_r(offs_t offset);

	/**
	 * @brief Handle a read from the synth's IO Port 2.
	 * This function is used to handle incoming serial data.
	 * @param offset The offset into the memory mapped region being read.
	 * @return uint8_t The value read from the port.
	 */
	uint8_t p2_r(offs_t offset);
};


/**
 * yamaha_dx9_state::machine_start
 */
void yamaha_dx9_state::machine_start()
{
	m_leds.resolve();
	m_rx_data = ASSERT_LINE;
}


/**
 * yamaha_dx9_state::lcd_pixel_update
 */
HD44780_PIXEL_UPDATE(yamaha_dx9_state::lcd_pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 16)
		bitmap.pix(line * 10 + y + 1 + ((y == 7) ? 1 : 0), pos * 6 + x + 1) = state ? 1 : 2;
}


/**
 * yamaha_dx9_state::palette_init
 */
void yamaha_dx9_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(0x87, 0xad, 0x34)); // background
	palette.set_pen_color(1, rgb_t(0x0, 0x0, 0x0)); // lcd pixel on
	palette.set_pen_color(2, rgb_t(0x7d, 0x9f, 0x32)); // lcd pixel off
}


/**
 * yamaha_dx9_state::mem_map
 */
void yamaha_dx9_state::mem_map(address_map &map)
{
	map(0x0020, 0x0020).r(FUNC(yamaha_dx9_state::key_switch_scan_driver_r));

	map(0x0022, 0x0022).r(m_adc, FUNC(m58990_device::data_r));
	map(0x0024, 0x0024).w(m_adc, FUNC(m58990_device::address_data_start_w));

	// YM21280 OPS.
	map(0x0026, 0x0027).w(FUNC(yamaha_dx9_state::ops_w));
	// HD44780 LCD Controller.
	map(0x0028, 0x0029).rw("lcdc", FUNC(hd44780_device::read), FUNC(hd44780_device::write));
	// LED.
	map(0x002b, 0x002c).w(FUNC(yamaha_dx9_state::led_w));

	// External RAM.
	// 2 * 2kb RAM1 IC19 M5M118P.
	map(0x0800, 0x0fff).ram().share("ram1");
	map(0x1000, 0x1800).ram().share("ram2");

	// YM21290 EGS
	map(0x1800, 0x18f3).w(FUNC(yamaha_dx9_state::egs_w));

	// ROM.
	map(0xc000, 0xffff).rom().region("program", 0);
}


/**
 * yamaha_dx9_state::dx9
 */
void yamaha_dx9_state::dx9(machine_config &config)
{
	// Initialise the HD63B03RP CPU.
	// This oscillator frequency comes from the service manual.
	HD6303R(config, m_maincpu, 9.4265_MHz_XTAL / 2);
	m_maincpu->set_addrmap(AS_PROGRAM, &yamaha_dx9_state::mem_map);

	// Unlike the DX7 only IO port 1 is used.
	// The direction flags of other ports are set, however they are never read, or written.
	m_maincpu->in_p1_cb().set(FUNC(yamaha_dx9_state::p1_r));
	m_maincpu->in_p2_cb().set(FUNC(yamaha_dx9_state::p2_r));
	m_maincpu->out_p1_cb().set(FUNC(yamaha_dx9_state::p1_w));

	NVRAM(config, "ram1", nvram_device::DEFAULT_ALL_0);
	NVRAM(config, "ram2", nvram_device::DEFAULT_ALL_0);

	// Configure the ADC. The clock speed here is a guess.
	M58990(config, m_adc, 500'000);

	// ADC source 4 is the battery voltage. Set this input to always read 0x80.
	// If the read value is below 0x6f, the firmware considers this a low battery voltage.
	m_adc->in_callback<4>().set_constant(0x80);

	// Configure MIDI.
	auto &midiclock(CLOCK(config, "midiclock", 500_kHz_XTAL / 2));
	midiclock.signal_handler().set(FUNC(yamaha_dx9_state::midiclock_w));

	MIDI_PORT(config, "mdin", midiin_slot, "midiin").rxd_handler().set(FUNC(yamaha_dx9_state::midi_r));

	auto &mdout(MIDI_PORT(config, "mdout", midiout_slot, "midiout"));
	m_maincpu->out_ser_tx_cb().set(mdout, FUNC(midi_port_device::write_txd));

	// Configure the LCD screen.
	screen_device &lcd_screen(SCREEN(config, "lcd_screen", SCREEN_TYPE_LCD));
	lcd_screen.set_refresh_hz(60);
	lcd_screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	lcd_screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	lcd_screen.set_size(6*16+1, 10*2+1);
	lcd_screen.set_visarea_full();
	lcd_screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(yamaha_dx9_state::palette_init), 3);

	hd44780_device &lcdc(HD44780(config, "lcdc", 270'000)); // TODO: clock not measured, datasheet typical clock used
	lcdc.set_lcd_size(2, 16);
	lcdc.set_pixel_update_cb(FUNC(yamaha_dx9_state::lcd_pixel_update));

	config.set_default_layout(layout_dx9);
}


/**
 * yamaha_dx9_state::ops_w
 */
void yamaha_dx9_state::ops_w(offs_t offset, uint8_t data)
{
	LOG("OPS: %02X=%02X\n", offset, data);
}


/**
 * yamaha_dx9_state::egs_w
 */
void yamaha_dx9_state::egs_w(offs_t offset, uint8_t data)
{
	LOG("EGS: %02X=%02X\n", offset, data);
}


/**
 * yamaha_dx9_state::led_w
 */
void yamaha_dx9_state::led_w(offs_t offset, uint8_t data)
{
	// Since the memory mapped region that calls this function is only two byts in
	// size, the led number is the least-significant bit of the offset.
	// The DX9's LEDs are wired so that a high input line disables the segment, so
	// get the one's complement of the data.
	m_leds[offset & 1] = (~data) & 0xff;
}


/**
 * yamaha_dx9_state::p1_r
 */
uint8_t yamaha_dx9_state::p1_r(offs_t offset)
{
	// The ADC EOC line is wired to bit 4, as well as the Cassette Interface input, which
	// is wired to bit 7. This is currently not fully implemented.
	return m_adc->eoc_r() << 4;
}


/**
 * yamaha_dx9_state::p2_r
 */
uint8_t yamaha_dx9_state::p2_r(offs_t offset)
{
	return m_rx_data << 3;
}


/**
 * yamaha_dx9_state::p1_w
 */
void yamaha_dx9_state::p1_w(offs_t offset, uint8_t data)
{
	// The low-nibble is written by the firmware to select the key/switch driver input line.
	m_key_switch_input_select = data & 0xf;

	// The cassette interface remote port polarity is set by bit 5.
	m_cassette_interface_remote_polarity = BIT(data, 5);

	// The cassette interface output polarity is set by bit 6.
	m_cassette_interface_output_polarity = BIT(data, 6);
}


static INPUT_PORTS_START(dx9)
	PORT_START("KEY_SWITCH_INPUT.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Yes/Up")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("No/Down")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Store")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Function")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Edit")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Memory")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNKNOWN)

	PORT_START("KEY_SWITCH_INPUT.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 1")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 2")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 3")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 4")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 5")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 6")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 7")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 8")

	PORT_START("KEY_SWITCH_INPUT.2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 9")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 10")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 11")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 12")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 13")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 14")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 15")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 16")

	PORT_START("KEY_SWITCH_INPUT.3")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 17")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 18")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 19")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 20")

	// These IO ports belong to the keyboard scan circuit.
	// Each of these 12 ports represents an individual key within an octave.
	// The keyboard is wired so that when each key's line is selected, reading the keyboard
	// scan driver output will return a power of two indicating the octave the pressed key
	// is in, from 0 to 7.
	PORT_START("KEY_SWITCH_INPUT.4")
	PORT_START("KEY_SWITCH_INPUT.5")
	PORT_START("KEY_SWITCH_INPUT.6")
	PORT_START("KEY_SWITCH_INPUT.7")
	PORT_START("KEY_SWITCH_INPUT.8")
	PORT_START("KEY_SWITCH_INPUT.9")
	PORT_START("KEY_SWITCH_INPUT.10")
	PORT_START("KEY_SWITCH_INPUT.11")
	PORT_START("KEY_SWITCH_INPUT.12")
	PORT_START("KEY_SWITCH_INPUT.13")
	PORT_START("KEY_SWITCH_INPUT.14")
	PORT_START("KEY_SWITCH_INPUT.15")
INPUT_PORTS_END


ROM_START(dx9)
	ROM_REGION(0x4000, "program", 0)
	ROM_LOAD("dx9.bin", 0x0000, 0x4000, CRC(c45e1832) SHA1(a92d7add3b89537ad06c719da0005c450d178d81))
ROM_END

} // anonymous namespace


SYST(1983, dx9, 0, 0, dx9, dx9, yamaha_dx9_state, empty_init, "Yamaha", "DX9 Digital Programmable Algorithm Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



ymmu10.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont, Olivier Galibert
/*************************************************************************************

    Yamaha MU-10 : 16-part, 32-note polyphonic/multitimbral General MIDI/GS/XG
                   tone module
    Driver by R. Belmont and O. Galibert

    Essentially a screen-less MU10 (SWP00, identical wave rom) with a gate-array based
    hack to connect the wave rom space to the adcs so that effects can be applied to
    analog inputs.  Entirely controlled through midi.

**************************************************************************************/

#include "emu.h"

#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/h8/h83002.h"
#include "sound/swp00.h"
#include "machine/nvram.h"

#include "debugger.h"
#include "speaker.h"


namespace {

static INPUT_PORTS_START( mu10 )
INPUT_PORTS_END

class mu10_state : public driver_device
{
public:
	mu10_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_nvram(*this, "ram")
		, m_swp00(*this, "swp00")
		, m_ram(*this, "ram")
	{ }

	void mu10(machine_config &config);

private:
	required_device<h83002_device> m_maincpu;
	required_device<nvram_device> m_nvram;
	required_device<swp00_device> m_swp00;
	required_shared_ptr<u16> m_ram;

	u8 cur_p6, cur_pa, cur_pb;

	u16 adc_battery_r();
	u16 adc_midisw_r();

	void p6_w(u8 data);
	void pa_w(u8 data);
	void pb_w(u8 data);
	u8 pb_r();

	void mu10_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
};

void mu10_state::machine_start()
{
	cur_p6 = cur_pa = cur_pb = 0xff;
}

void mu10_state::machine_reset()
{
	// Active-low, wired to gnd
	m_maincpu->set_input_line(0, ASSERT_LINE);
}

void mu10_state::mu10_map(address_map &map)
{
	map(0x000000, 0x07ffff).rom().region("maincpu", 0);
	map(0x400000, 0x4007ff).m(m_swp00, FUNC(swp00_device::map));
	map(0x600000, 0x607fff).ram().share(m_ram); // 32K work RAM
}

// Battery level
u16 mu10_state::adc_battery_r()
{
	return 0x200;
}

// Put the host switch to pure midi
u16 mu10_state::adc_midisw_r()
{
	// 000-0bf: midi
	// 0c0-1ff: pc2
	// 200-37f: pc1
	// 380-3ff: mac
	return 0x000;
}

void mu10_state::p6_w(u8 data)
{
	cur_p6 = data;
	logerror("reset swp %d dac %d\n", BIT(data, 2), BIT(data, 0));
}

void mu10_state::pb_w(u8 data)
{
	cur_pb = data;
	logerror("led %d gain %d\n", BIT(data, 0), BIT(data, 2));
}

u8 mu10_state::pb_r()
{
	// bit 3 = a/d plugged in
	return 8;
}

void mu10_state::pa_w(u8 data)
{
	cur_pa = data;
	logerror("mac host pin 1 %d\n", !BIT(data, 2));
}

void mu10_state::mu10(machine_config &config)
{
	H83002(config, m_maincpu, 12_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mu10_state::mu10_map);
	m_maincpu->read_adc<0>().set(FUNC(mu10_state::adc_battery_r));
	m_maincpu->read_adc<4>().set_constant(0);
	m_maincpu->read_adc<5>().set_constant(0);
	m_maincpu->read_adc<6>().set_constant(0);
	m_maincpu->read_adc<7>().set(FUNC(mu10_state::adc_midisw_r));
	m_maincpu->write_port6().set(FUNC(mu10_state::p6_w));
	m_maincpu->write_porta().set(FUNC(mu10_state::pa_w));
	m_maincpu->write_portb().set(FUNC(mu10_state::pb_w));
	m_maincpu->read_portb().set(FUNC(mu10_state::pb_r));

	NVRAM(config, m_nvram, nvram_device::DEFAULT_NONE);

	SPEAKER(config, "speaker", 2).front();

	SWP00(config, m_swp00);
	m_swp00->add_route(0, "speaker", 1.0, 0);
	m_swp00->add_route(1, "speaker", 1.0, 1);

	auto &mdin(MIDI_PORT(config, "mdin"));
	midiin_slot(mdin);
	mdin.rxd_handler().set(m_maincpu, FUNC(h83002_device::sci_rx_w<1>));

	auto &mdout(MIDI_PORT(config, "mdout"));
	midiout_slot(mdout);
	m_maincpu->write_sci_tx<1>().set(mdout, FUNC(midi_port_device::write_txd));
}

ROM_START( mu10 )
	ROM_REGION( 0x80000, "maincpu", 0 )
	// Ver0.05 94-Nov-24
	ROM_LOAD16_WORD_SWAP( "xs289a0.ic07", 0x000000, 0x080000, CRC(6438c5a7) SHA1(4c2bb0c53a756d64cf6315b85642cab3b86e39fc) )

	ROM_REGION( 0x400000, "swp00", 0 )
	// Same content as the mu10 roms but grouped into one rom
	ROM_LOAD( "xr709a0.ic11", 0x000000, 0x400000, CRC(4261c0bc) SHA1(5286b933976f9e3a9ca82523b8d591b616cf9479) )
ROM_END

} // anonymous namespace


CONS( 1994, mu10, 0, 0, mu10,  mu10, mu10_state, empty_init, "Yamaha", "MU10", 0 )



ymmu100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont, Olivier Galibert
/*************************************************************************************

    Yamaha MU-100 : 32-part, 64-voice polyphonic/multitimbral General MIDI/GS/XG
                    tone module
    Preliminary driver by R. Belmont and O. Galibert

    The successor to the mu90, uses the same one-chip SWP30 with a better sample rom.
    Exists in rackable (mu100r) and screenless (mu100b), and single board (xt446)
    variants.

    MU100 CPU: Hitachi H8S/2655 (HD6432655F), strapped for mode 4 (24-bit address, 16-bit data, no internal ROM)
    Sound ASIC: Yamaha XS725A0/SWP30
    RAM: 1 MSM51008 (1 meg * 1 bit = 128KBytes)

    I/O ports from service manual:

    Port 1
        0 - LCD data, SW data, LED 1
        1 - LCD data, SW data, LED 2
        2 - LCD data, SW data, LED 3
        3 - LCD data, SW data, LED 4
        4 - LCD data, SW data, LED 5
        5 - LCD data, SW strobe data
        6 - LCD data, SW strobe data
        7 - LCD data, SW data, LED 6

    Port 2:
        0 - (out) LCD control RS
        1 - (out) LCD control R/W
        2 - (out) LCD control E
        3 - (out) LCD contrast A
        4 - (out) LCD contrast B
        5 - (out) LCD contrast C
        6 - (out) 1 MHz clock for serial
        7 - NC

    Port 3:
        4 - (out) A/D gain control 1
        5 - (out) A/D gain control 2

    Port 5:
        3 - (out) Reset signal for rotary encoder

    Port 6:
        1 - NC
        2 - (out) PB select (SW1)
        3 - (out) PB select (SW2)
        4 - (out) reset PB
        5 - (out) reset SWP30 (sound chip)
        6 - NC
        7 - (in) Plug detection for A/D input

    Port A:
        5 - (in) Off Line Detection
        6 - (out) Signal for rotary encoder (REB)
        7 - (out) Signal for rotary encoder (REA)


    Port F:
        0 - (out) (sws) LED,SW Strobe data latch
        1 - (out) (swd) SW data read control
        2 - (out) PB select (SW4)

    Port G:
        0 - (out) PB select (SW3)

    Analog input channels:
        0 - level input R
        2 - level output L
        4 - host SW type switch position
        6 - battery voltage
        7 - model check (0 for MU100, 0.5 for OEM, 1 for MU100R)

    Switch map at the connector (17=ground)
        09 8 play
        10 8 edit
        11 8 mute/solo
        12 8 part -
        13 8 part +
        14 8 util
        15 8 effect
        16 8 enter
        12 7 select <
        13 7 select >
        16 7 mode
        15 7 eq
        14 7 exit
        10 7 value -
        11 7 value +
           2 led play
           3 led edit
           4 led util
           5 led effect
           6 led mode
           1 led eq

     IC32:
        1 p10 c.2
        2 p11 c.3
        3 p12 c.4
        4 p13 c.5
        5 p14 c.6
        6 p15 c.7
        7 p16 c.8
        8 p17 c.1
        g sws

     IC33
        1 p17 c.09
        2 p16 c.10
        3 p15 c.11
        4 p14 c.12
        5 p13 c.13
        6 p12 c.14
        7 p11 c.15
        8 p10 c.16
        g swd

**************************************************************************************/

#include "emu.h"

#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/h8/h8s2655.h"
#include "mulcd.h"
#include "sound/swp30.h"
#include "bus/plg1x0/plg1x0.h"

#include "debugger.h"
#include "speaker.h"


namespace {

static INPUT_PORTS_START( mu100 )
	PORT_START("P7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Enter")     PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Effect")    PORT_CODE(KEYCODE_F)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Util")      PORT_CODE(KEYCODE_U)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part +")    PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part -")    PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Mute/Solo") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Edit")      PORT_CODE(KEYCODE_E)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Play")      PORT_CODE(KEYCODE_A)

	PORT_START("P8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Mode")      PORT_CODE(KEYCODE_M)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Eq")        PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Exit")      PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Select >")  PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Select <")  PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Value +")   PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Value -")   PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

class mu100_state : public driver_device
{
public:
	mu100_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_swp30(*this, "swp30")
		, m_lcd(*this, "lcd")
		, m_ext1(*this, "ext1")
		, m_ext2(*this, "ext2")
		, m_ioport_p7(*this, "P7")
		, m_ioport_p8(*this, "P8")
	{ }

	void mu100(machine_config &config);
	void mu100b(machine_config &config);

protected:
	virtual u16 adc_type_r();

	enum {
		P2_LCD_RS     = 0x01,
		P2_LCD_RW     = 0x02,
		P2_LCD_ENABLE = 0x04
	};

	required_device<h8s2655_device> m_maincpu;
	required_device<swp30_device> m_swp30;
	optional_device<mulcd_device> m_lcd;
	required_device<plg1x0_connector> m_ext1;
	optional_device<plg1x0_connector> m_ext2;
	required_ioport m_ioport_p7;
	required_ioport m_ioport_p8;

	u8 m_cur_p1, m_cur_p2, m_cur_p3, m_cur_p5, m_cur_p6, m_cur_pa, m_cur_pb, m_cur_pc, m_cur_pf, m_cur_pg;
	u8 m_cur_ic32, m_cur_sw;
	int m_h8_tx, m_e1_tx, m_e2_tx;

	u16 adc_ar_r();
	u16 adc_al_r();
	u16 adc_midisw_r();
	u16 adc_battery_r();

	void p1_w(u8 data);
	u8 p1_r();
	void p2_w(u8 data);
	void p3_w(u8 data);
	void p5_w(u8 data);
	void p6_w(u8 data);
	u8 p6_r();
	void pa_w(u8 data);
	u8 pa_r();
	void pb_w(u8 data);
	u8 pb_r();
	void pf_w(u8 data);
	void pg_w(u8 data);

	void ext_serial_update();
	void h8_tx(int state);
	void e1_tx(int state);
	void e2_tx(int state);

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void mu100_map(address_map &map) ATTR_COLD;
	void swp30_map(address_map &map) ATTR_COLD;
};

class mu100r_state : public mu100_state {
public:
	mu100r_state(const machine_config &mconfig, device_type type, const char *tag)
		: mu100_state(mconfig, type, tag)
	{ }

	void mu100r(machine_config &config);

private:
	virtual u16 adc_type_r() override;
};

void mu100_state::machine_start()
{
	save_item(NAME(m_cur_p1));
	save_item(NAME(m_cur_p2));
	save_item(NAME(m_cur_p3));
	save_item(NAME(m_cur_p5));
	save_item(NAME(m_cur_p6));
	save_item(NAME(m_cur_pa));
	save_item(NAME(m_cur_pc));
	save_item(NAME(m_cur_pf));
	save_item(NAME(m_cur_pg));
	save_item(NAME(m_cur_ic32));
	save_item(NAME(m_cur_sw));
	save_item(NAME(m_h8_tx));
	save_item(NAME(m_e1_tx));
	save_item(NAME(m_e2_tx));
}

void mu100_state::machine_reset()
{
	m_cur_p1 = m_cur_p2 = m_cur_p3 = m_cur_p5 = m_cur_p6 = m_cur_pa = m_cur_pc = m_cur_pf = m_cur_pg = m_cur_ic32 = 0xff;
	m_cur_sw = 0;
	m_h8_tx = m_e1_tx = m_e2_tx = 1;
	ext_serial_update();
}

void mu100_state::mu100_map(address_map &map)
{
	map(0x000000, 0x1fffff).rom().region("maincpu", 0);
	map(0x200000, 0x21ffff).ram(); // 128K work RAM
	map(0x400000, 0x401fff).m(m_swp30, FUNC(swp30_device::map));
}

// Analog input right (also sent to the swp)
u16 mu100_state::adc_ar_r()
{
	return 0;
}

// Analog input left (also sent to the swp)
u16 mu100_state::adc_al_r()
{
	return 0;
}

// Put the host switch to pure midi
u16 mu100_state::adc_midisw_r()
{
	return 0;
}

// Battery level
u16 mu100_state::adc_battery_r()
{
	return 0x200;
}

// model detect.  pulled to GND (0) on MU100, to 0.5Vcc on the card version, to Vcc on MU100R
u16 mu100_state::adc_type_r()
{
	return m_lcd ? 0 : 0x200;
}

u16 mu100r_state::adc_type_r()
{
	return 0x3ff;
}

void mu100_state::p1_w(u8 data)
{
	m_cur_p1 = data;
}

u8 mu100_state::p1_r()
{
	if(m_lcd)
		if((m_cur_p2 & P2_LCD_ENABLE)) {
			if(m_cur_p2 & P2_LCD_RW) {
				if(m_cur_p2 & P2_LCD_RS)
					return m_lcd->data_read();
				else
					return m_lcd->control_read();
			} else
				return 0x00;
		}

	if(!(m_cur_pf & 0x02)) {
		u8 val = 0xff;
		if(!(m_cur_ic32 & 0x20))
			val &= m_ioport_p7->read();
		if(!(m_cur_ic32 & 0x40))
			val &= m_ioport_p8->read();
		return val;
	}

	return 0xff;
}

void mu100_state::p2_w(u8 data)
{
	if(m_lcd) {
	// LCD enable edge
		if((m_cur_p2 & P2_LCD_ENABLE) && !(data & P2_LCD_ENABLE)) {
			if(!(m_cur_p2 & P2_LCD_RW)) {
				if(m_cur_p2 & P2_LCD_RS)
					m_lcd->data_write(m_cur_p1);
				else
					m_lcd->control_write(m_cur_p1);
			}
		}
		m_lcd->set_contrast((data >> 3) & 7);
	}
	m_cur_p2 = data;
}

void mu100_state::p3_w(u8 data)
{
	m_cur_p3 = data;
	logerror("A/D gain control %d\n", (data >> 4) & 3);
}

void mu100_state::p5_w(u8 data)
{
	m_cur_p5 = data;
	logerror("Rotary reset %d\n", (data >> 3) & 1);
}

void mu100_state::p6_w(u8 data)
{
	m_cur_p6 = data;
	m_cur_sw = (m_cur_sw & 0xc) | BIT(m_cur_pf, 2, 2);
	ext_serial_update();
}

u8 mu100_state::p6_r()
{
	//  logerror("plug in detect read\n");
	return 0x00;
}

void mu100_state::pa_w(u8 data)
{
	m_cur_pa = data;
	logerror("rotary encoder %d\n", (data >> 6) & 3);
}

u8 mu100_state::pa_r()
{
	logerror("offline detect read\n");
	return 0x00;
}

void mu100_state::pf_w(u8 data)
{
	if(!(m_cur_pf & 0x01) && (data & 0x01)) {
		m_cur_ic32 = m_cur_p1;
		if(m_lcd)
			m_lcd->set_leds((m_cur_p1 & 0x1f) | ((m_cur_p1 & 0x80) >> 2));
	}
	m_cur_pf = data;
	m_cur_sw = (m_cur_sw & 0x7) | (BIT(m_cur_pf, 2) << 3);
	ext_serial_update();
}

void mu100_state::pg_w(u8 data)
{
	m_cur_pg = data;
	m_cur_sw = (m_cur_sw & 0xb) | (BIT(m_cur_pg, 0) << 2);
	ext_serial_update();
}

void mu100_state::ext_serial_update()
{
	m_ext1->midi_rx(BIT(m_cur_sw, 3) ? m_h8_tx : 1);
	if(m_ext2)
		m_ext2->midi_rx(BIT(m_cur_sw, 1) ? m_h8_tx : 1);
	if(BIT(m_cur_sw, 2))
		if(BIT(m_cur_sw, 0))
			m_maincpu->sci_rx_w<2>(m_e1_tx && m_e2_tx);
		else
			m_maincpu->sci_rx_w<2>(m_e1_tx);
	else
		if(BIT(m_cur_sw, 0))
			m_maincpu->sci_rx_w<2>(m_e2_tx);
		else
			m_maincpu->sci_rx_w<2>(1);
}

void mu100_state::h8_tx(int state)
{
	m_h8_tx = state;
	ext_serial_update();
}

void mu100_state::e1_tx(int state)
{
	m_e1_tx = state;
	ext_serial_update();
}

void mu100_state::e2_tx(int state)
{
	m_e2_tx = state;
	ext_serial_update();
}

void mu100_state::swp30_map(address_map &map)
{
	map(0x000000, 0x1fffff).rom().region("swp30",         0).mirror(0x200000);
	map(0x400000, 0x4fffff).rom().region("swp30",  0x800000).mirror(0x300000);
	map(0x800000, 0x9fffff).rom().region("swp30", 0x1000000).mirror(0x200000);
}

void mu100_state::mu100b(machine_config &config)
{
	H8S2655(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mu100_state::mu100_map);
	m_maincpu->read_adc<0>().set(FUNC(mu100_state::adc_ar_r));
	m_maincpu->read_adc<1>().set_constant(0);
	m_maincpu->read_adc<2>().set(FUNC(mu100_state::adc_al_r));
	m_maincpu->read_adc<3>().set_constant(0);
	m_maincpu->read_adc<4>().set(FUNC(mu100_state::adc_midisw_r));
	m_maincpu->read_adc<5>().set_constant(0);
	m_maincpu->read_adc<6>().set(FUNC(mu100_state::adc_battery_r));
	m_maincpu->read_adc<7>().set(FUNC(mu100_state::adc_type_r));
	m_maincpu->read_port1().set(FUNC(mu100_state::p1_r));
	m_maincpu->write_port1().set(FUNC(mu100_state::p1_w));
	m_maincpu->write_port2().set(FUNC(mu100_state::p2_w));
	m_maincpu->write_port3().set(FUNC(mu100_state::p3_w));
	m_maincpu->write_port5().set(FUNC(mu100_state::p5_w));
	m_maincpu->read_port6().set(FUNC(mu100_state::p6_r));
	m_maincpu->write_port6().set(FUNC(mu100_state::p6_w));
	m_maincpu->read_porta().set(FUNC(mu100_state::pa_r));
	m_maincpu->write_porta().set(FUNC(mu100_state::pa_w));
	m_maincpu->write_portf().set(FUNC(mu100_state::pf_w));
	m_maincpu->write_portg().set(FUNC(mu100_state::pg_w));
	m_maincpu->write_sci_tx<2>().set(FUNC(mu100_state::h8_tx));

	PLG1X0_CONNECTOR(config, m_ext1, plg1x0_intf, nullptr);
	m_ext1->midi_tx().set(FUNC(mu100_state::e1_tx));

	SPEAKER(config, "speaker", 2).front();

	SWP30(config, m_swp30);
	m_swp30->set_addrmap(AS_DATA, &mu100_state::swp30_map);
	m_swp30->add_route(0, "speaker", 1.0, 0);
	m_swp30->add_route(1, "speaker", 1.0, 1);

	auto &mdin_a(MIDI_PORT(config, "mdin_a"));
	midiin_slot(mdin_a);
	mdin_a.rxd_handler().set(m_maincpu, FUNC(h8s2655_device::sci_rx_w<1>));

	auto &mdin_b(MIDI_PORT(config, "mdin_b"));
	midiin_slot(mdin_b);
	mdin_b.rxd_handler().set(m_maincpu, FUNC(h8s2655_device::sci_rx_w<0>));

	auto &mdout(MIDI_PORT(config, "mdout"));
	midiout_slot(mdout);
	m_maincpu->write_sci_tx<0>().set(mdout, FUNC(midi_port_device::write_txd));
}

void mu100_state::mu100(machine_config &config)
{
	mu100b(config);

	MULCD(config, m_lcd);
}

void mu100r_state::mu100r(machine_config &config)
{
	mu100(config);

	PLG1X0_CONNECTOR(config, m_ext2, plg1x0_intf, nullptr);
	m_ext2->midi_tx().set(FUNC(mu100r_state::e2_tx));
}

#define ROM_LOAD16_WORD_SWAP_BIOS(bios,name,offset,length,hash) \
		ROMX_LOAD(name, offset, length, hash, ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(bios))

ROM_START( mu100 )
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_DEFAULT_BIOS("v111")
	ROM_SYSTEM_BIOS( 0, "v111", "xu50720 (v1.11, Aug. 3, 1999)" )
	ROM_LOAD16_WORD_SWAP_BIOS( 0, "xu50720.ic11", 0x000000, 0x200000, CRC(1126a8a4) SHA1(e90b8bd9d14297da26ba12f4d9a4f2d22cd7d34a) )
	ROM_SYSTEM_BIOS( 1, "v106", "xt714h0 (v1.06, Oct. 14, 1997)" )
	ROM_LOAD16_WORD_SWAP_BIOS( 1, "xt714h0.ic11", 0x000000, 0x200000, CRC(aa96ab38) SHA1(ec39eeab55d7d55b4f6d2b4b4cac2a01f98db8a0) )
	ROM_SYSTEM_BIOS( 2, "v105", "xt71420 (v1.05, Sep. 19, 1997)" )
	ROM_LOAD16_WORD_SWAP_BIOS( 2, "xt71420.ic11", 0x000000, 0x200000, CRC(0e5b3bae) SHA1(3148c5bd59a3d00809d3ab1921216215fe2582c5) )
	ROM_SYSTEM_BIOS( 3, "v103", "xt714e0 (v1.03, Jul. 25, 1997)" )
	ROM_LOAD16_WORD_SWAP_BIOS( 3, "xt714e0.ic11", 0x000000, 0x200000, CRC(2d8cf9fc) SHA1(a81f988a315efe92106f1e7d407cd3626c4f843f) )

	ROM_REGION32_LE( 0x1800000, "swp30", ROMREGION_ERASE00 )
	ROM_LOAD32_WORD( "xs518b0.ic34", 0x0000000, 0x400000, CRC(2550d44f) SHA1(fd3cce228c7d389a2fde25c808a5b26080588cba) )
	ROM_LOAD32_WORD( "xs743b0.ic35", 0x0000002, 0x400000, CRC(a9109a6c) SHA1(a67bb49378a38a2d809bd717d286e18bc6496db0) )
	ROM_LOAD32_WORD( "xt445a0-828.ic36", 0x0800000, 0x200000, CRC(225c2280) SHA1(23b5e046fd2e2ac01af3e6dc6357c5c6547b286b) )
	ROM_LOAD32_WORD( "xt461a0-829.ic37", 0x0800002, 0x200000, CRC(a1d138a3) SHA1(46a7a7225cd7e1818ba551325d2af5ac1bf5b2bf) )
	ROM_LOAD32_WORD( "xt462a0.ic39", 0x1000000, 0x400000, CRC(2e82cbd4) SHA1(d1f0e2713bf2cca9156c562e23fcce4fa5d7cfb3) )
	ROM_LOAD32_WORD( "xt463a0.ic38", 0x1000002, 0x400000, CRC(cce5f8d3) SHA1(bdca8c5158f452f2b5535c7d658c9b22c6d66048) )
ROM_END

// mu100r roms are identical to the mu100
#define rom_mu100r rom_mu100

ROM_START( mu100b )
	ROM_REGION( 0x200000, "maincpu", 0 )
	// MU-100B v1.08 (Nov. 28, 1997)
	ROM_LOAD16_WORD_SWAP( "xu50710.bin", 0x000000, 0x200000, CRC(4b10bd27) SHA1(12d7c6e1bce7974b34916e1bfa5057ab55867476) )

	ROM_REGION32_LE( 0x1800000, "swp30", ROMREGION_ERASE00 )
	ROM_LOAD32_WORD( "sx518b0.ic34", 0x0000000, 0x400000, CRC(2550d44f) SHA1(fd3cce228c7d389a2fde25c808a5b26080588cba) )
	ROM_LOAD32_WORD( "sx743b0.ic35", 0x0000002, 0x400000, CRC(a9109a6c) SHA1(a67bb49378a38a2d809bd717d286e18bc6496db0) )
	ROM_LOAD32_WORD( "xt445a0-828.ic36", 0x0800000, 0x200000, CRC(225c2280) SHA1(23b5e046fd2e2ac01af3e6dc6357c5c6547b286b) )
	ROM_LOAD32_WORD( "xt461a0-829.ic37", 0x0800002, 0x200000, CRC(a1d138a3) SHA1(46a7a7225cd7e1818ba551325d2af5ac1bf5b2bf) )
	ROM_LOAD32_WORD( "xt462a0.ic39", 0x1000000, 0x400000, CRC(2e82cbd4) SHA1(d1f0e2713bf2cca9156c562e23fcce4fa5d7cfb3) )
	ROM_LOAD32_WORD( "xt463a0.ic38", 0x1000002, 0x400000, CRC(cce5f8d3) SHA1(bdca8c5158f452f2b5535c7d658c9b22c6d66048) )
ROM_END

} // anonymous namespace


SYST( 1997, mu100,  0,     0, mu100,  mu100, mu100_state,  empty_init, "Yamaha", "MU100",                    MACHINE_NOT_WORKING )
SYST( 1997, mu100r, mu100, 0, mu100r, mu100, mu100r_state, empty_init, "Yamaha", "MU100 Rackable version",   MACHINE_NOT_WORKING )
SYST( 1998, mu100b, mu100, 0, mu100b, mu100, mu100_state,  empty_init, "Yamaha", "MU100 Screenless version", MACHINE_NOT_WORKING )



ymmu128.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
/*************************************************************************************

    Yamaha MU-128 : 64-part, 128-note polyphonic/multitimbral General MIDI/XG
                    tone module
    Driver by O. Galibert

    Uses a dual SWP30 and a SH7043 as cpu.

**************************************************************************************/

#include "emu.h"

#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/sh/sh7042.h"
#include "machine/i8251.h"
#include "machine/input_merger.h"
#include "machine/nvram.h"
#include "sound/swp30.h"

#include "mulcd.h"
#include "speaker.h"

#include "mu128.lh"

namespace {

static INPUT_PORTS_START( mu128 )
INPUT_PORTS_END

class mu128_state : public driver_device
{
public:
	mu128_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_nvram(*this, "ram")
		, m_swp30m(*this, "swp30m")
		, m_swp30s(*this, "swp30s")
		, m_lcd(*this, "lcd")
		, m_sci(*this, "sci")
		, m_ram(*this, "ram")
	{ }

	void mu128(machine_config &config);

private:
	required_device<sh7043_device> m_maincpu;
	required_device<nvram_device> m_nvram;
	required_device<swp30_device> m_swp30m, m_swp30s;
	required_device<mulcd_device> m_lcd;
	required_device<i8251_device> m_sci;
	required_shared_ptr<u32> m_ram;

	u32 m_pa;
	u16 m_pe;

	void map(address_map &map) ATTR_COLD;
	void swp30_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	u32 pa_r();
	void pa_w(u32 data);
	u16 pe_r();
	void pe_w(u16 data);
};

void mu128_state::machine_start()
{
	save_item(NAME(m_pa));
	save_item(NAME(m_pe));
	m_pa = 0;
	m_pe = 0;
}

void mu128_state::machine_reset()
{
}

// Port A:
//   8: sw5
//   9: sw6
//   f: rer
//  10: reb
//  11: rea
//  12: swd
//  14: led1
//  15: ry/by

// Port B:
//   2: sw1
//   3: sw2
//   4: sw3
//   5: sw4

// Port E:
//   0: lcd-r/w
//   1: nc
//   2: lcd-rs
//   3: lcread (secondary functions on lcread edge)
//   4: lcd-e
//   5: 1mclk
//   6: siclk
//   7: sws
//   8-a: db 0-2, lcdc a-c
//   b-c: db 3-4, mic/line
//   d:   db 5, swp reset
//   e:   db 6, sireset
//   f:   db 7, breset

u32 mu128_state::pa_r()
{
	return 0;
}

void mu128_state::pa_w(u32 data)
{
	m_pa = data;
}

u16 mu128_state::pe_r()
{
	if(BIT(m_pe, 4)) {
		if(BIT(m_pe, 0)) {
			if(BIT(m_pe, 2))
				return m_lcd->data_read() << 8;
			else
				return m_lcd->control_read() << 8;
		} else
			return 0x0000;
	}

	return 0;
}

void mu128_state::pe_w(u16 data)
{
	if(BIT(m_pe, 4) && !BIT(data, 4)) {
		if(!BIT(data, 0)) {
			if(BIT(data, 2))
				m_lcd->data_write(data >> 8);
			else
				m_lcd->control_write(data >> 8);
		}
	}

	m_pe = data;
}

void mu128_state::map(address_map &map)
{
	// 000000-3fffff: cs0 space, 32 bits, 2 wait states
	map(0x000000, 0x1fffff).rom().region("maincpu", 0);

	// 400000-7fffff: cs1 space, 16 bits, 2 wait states
	map(0x400000, 0x43ffff).ram().share(m_ram);

	// 800000-bfffff: cs2 space, 16 bits, cs assert extension, 3 wait states
	map(0x800000, 0x801fff).m(m_swp30m, FUNC(swp30_device::map));
	map(0x802000, 0x803fff).m(m_swp30s, FUNC(swp30_device::map));

	// c00000-ffffff: cs3 space, 8 bits, 2 wait states
	map(0xc00000, 0xc00000).rw(m_sci, FUNC(i8251_device::data_r), FUNC(i8251_device::data_w)).mirror(0x3ffffe);
	map(0xc00001, 0xc00001).rw(m_sci, FUNC(i8251_device::status_r), FUNC(i8251_device::control_w)).mirror(0x3ffffe);
}

void mu128_state::swp30_map(address_map &map)
{
	map(0x000000, 0x5fffff).rom().region("swp30", 0);
}

void mu128_state::mu128(machine_config &config)
{
	SH7043(config, m_maincpu, 7_MHz_XTAL * 4); // md=9, no on-chip rom, 32-bit space, pll 4x
	m_maincpu->set_addrmap(AS_PROGRAM, &mu128_state::map);
	m_maincpu->read_porta().set(FUNC(mu128_state::pa_r));
	m_maincpu->write_porta().set(FUNC(mu128_state::pa_w));
	m_maincpu->read_porte().set(FUNC(mu128_state::pe_r));
	m_maincpu->write_porte().set(FUNC(mu128_state::pe_w));

	NVRAM(config, m_nvram, nvram_device::DEFAULT_NONE);

	MULCD(config, m_lcd);

	SPEAKER(config, "speaker", 2).front();

	SWP30(config, m_swp30m);
	m_swp30m->set_addrmap(AS_DATA, &mu128_state::swp30_map);
	m_swp30m->add_route(0, "speaker", 1.0, 0);
	m_swp30m->add_route(1, "speaker", 1.0, 1);

	SWP30(config, m_swp30s);
	m_swp30s->set_addrmap(AS_DATA, &mu128_state::swp30_map);
	m_swp30s->add_route(0, "speaker", 1.0, 0);
	m_swp30s->add_route(1, "speaker", 1.0, 1);

	INPUT_MERGER_ANY_HIGH(config, "irq0").output_handler().set_inputline(m_maincpu, 0);
	I8251(config, m_sci, 10_MHz_XTAL); // uPD71051GU-10
	m_sci->rxrdy_handler().set("irq0", FUNC(input_merger_device::in_w<0>));
	m_sci->txrdy_handler().set("irq0", FUNC(input_merger_device::in_w<1>));

	auto &mdin_a(MIDI_PORT(config, "mdin_a"));
	midiin_slot(mdin_a);
	mdin_a.rxd_handler().set(m_maincpu, FUNC(sh7043_device::sci_rx_w<0>));

	auto &mdin_b(MIDI_PORT(config, "mdin_b"));
	midiin_slot(mdin_b);
	mdin_b.rxd_handler().set(m_maincpu, FUNC(sh7043_device::sci_rx_w<1>));

	auto &mdout(MIDI_PORT(config, "mdout"));
	midiout_slot(mdout);
	m_maincpu->write_sci_tx<0>().set(mdout, FUNC(midi_port_device::write_txd));

	config.set_default_layout(layout_mu128);
}

#define ROM_LOAD32_WORD_SWAP_BIOS(bios,name,offset,length,hash) \
	ROMX_LOAD(name, offset, length, hash, ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(bios))

ROM_START( mu128 )
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_DEFAULT_BIOS("v200")
	ROM_SYSTEM_BIOS( 0, "v200", "Upgrade package (Ver2.00 99-MAY-21)" )
	ROM_LOAD32_WORD_SWAP_BIOS( 0, "mu128-v2.00-h.bin", 0x000000, 0x100000, CRC(2891487b) SHA1(8ed4a6929c66fcb5248e16288dfaf56a3286aaf8) )
	ROM_LOAD32_WORD_SWAP_BIOS( 0, "mu128-v2.00-l.bin", 0x000002, 0x100000, CRC(cc236dc4) SHA1(e1ff3387968e89f5bc5df3e15cd0d6039104acd0) )
	ROM_SYSTEM_BIOS( 1, "v106", "c0 (Ver1.06 98-OCT-01)" )
	ROM_LOAD32_WORD_SWAP_BIOS( 1, "xv217c0.ic27", 0x000000, 0x100000, CRC(f4ba61f1) SHA1(381e1d146c4e693a21f6e6e4ea2a8b9f6e3033ef) )
	ROM_LOAD32_WORD_SWAP_BIOS( 1, "xv224c0.ic25", 0x000002, 0x100000, CRC(079bfcf0) SHA1(56d69f3214899fa25ef5e9ea6c2bbf0c3d378123) )

	ROM_REGION32_LE( 0x1800000, "swp30", ROMREGION_ERASE00 )
	ROM_LOAD32_WORD( "xv364a0.ic53", 0x0000000, 0x800000, CRC(cda1afd6) SHA1(e7098246b33c3cf22ed8cc15ed6383f8a06d17e9) )
	ROM_LOAD32_WORD( "xv365a0.ic54", 0x0000002, 0x800000, CRC(10985ed0) SHA1(d45a2e85859e05046f3ede8317a9bb0b88898116) )
	ROM_LOAD32_WORD( "xv366a0.ic57", 0x1000000, 0x400000, CRC(781dfac6) SHA1(e6b8b7cf95e4e9552001450570fcea87282db1e8) )
	ROM_LOAD32_WORD( "xv376a0.ic58", 0x1000002, 0x400000, CRC(91a7533b) SHA1(c30888603fd5db367d14553763f0a3f392c5427c) )
ROM_END

} // anonymous namespace


CONS( 1998, mu128, 0, 0, mu128,  mu128, mu128_state, empty_init, "Yamaha", "MU128", MACHINE_NOT_WORKING )



ymmu15.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
/*************************************************************************************

    Yamaha MU-15 : 16-part, 32-note polyphonic/multitimbral General MIDI/XG
                   tone module
    Driver by O. Galibert

    Uses a SWX00 that includes both the synth and an h8 core.  Program and samples
    share the rom.

**************************************************************************************/

#include "emu.h"

#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/h8/swx00.h"
#include "mu5lcd.h"
#include "machine/nvram.h"

#include "debugger.h"
#include "speaker.h"


namespace {

static INPUT_PORTS_START( mu15 )
	PORT_START("SA")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part -")         PORT_CODE(KEYCODE_9)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part +")         PORT_CODE(KEYCODE_0)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Value -")        PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Value +")        PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("XG Bank")        PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Play/Edit")      PORT_CODE(KEYCODE_OPENBRACE)

	PORT_START("SB")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D2# Mute")       PORT_CODE(KEYCODE_J)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C2# Solo")       PORT_CODE(KEYCODE_H)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A1# Note Shft")  PORT_CODE(KEYCODE_F)
	PORT_BIT(0x38, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("SC")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G1# Pan")        PORT_CODE(KEYCODE_D)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F1# Vol")        PORT_CODE(KEYCODE_S)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D1# Init All")   PORT_CODE(KEYCODE_8)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C1# Dump Out")   PORT_CODE(KEYCODE_7)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A0# Locl Ctrl")  PORT_CODE(KEYCODE_5)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G0# Velocity")   PORT_CODE(KEYCODE_4)

	PORT_START("SD")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F0# Mstr Tune")  PORT_CODE(KEYCODE_3)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Oct Up")         PORT_CODE(KEYCODE_2)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Oct Down")       PORT_CODE(KEYCODE_1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("E0 V Dry Lvl")   PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F0 V Ins Sys")   PORT_CODE(KEYCODE_W)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G0 V Type")      PORT_CODE(KEYCODE_E)

	PORT_START("SE")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A0 V Send Lvl")  PORT_CODE(KEYCODE_R)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("B0 C Type")      PORT_CODE(KEYCODE_T)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C1 C Send Lvl")  PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D1 R Type")      PORT_CODE(KEYCODE_U)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("E1 R Send Lvl")  PORT_CODE(KEYCODE_I)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F1 V Rate")      PORT_CODE(KEYCODE_Z)

	PORT_START("SF")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G1 V Dpth")      PORT_CODE(KEYCODE_X)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A1 V Dely")      PORT_CODE(KEYCODE_C)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("B1 E Atck Time") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C2 E Rels Time") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D2 F Cutoff")    PORT_CODE(KEYCODE_N)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("E2 F Reso")      PORT_CODE(KEYCODE_M)
INPUT_PORTS_END

class mu15_state : public driver_device
{
public:
	mu15_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		//, m_nvram(*this, "ram")
		, m_lcd(*this, "lcd")
		, m_ram1(*this, "ram1")
		, m_ram2(*this, "ram2")
		, m_ioport(*this, "S%c", 'A')

	{ }

	void mu15(machine_config &config);

private:
	required_device<swx00_device> m_maincpu;
	//  required_device<nvram_device> m_nvram;
	required_device<mu5lcd_device> m_lcd;
	required_shared_ptr<u16> m_ram1;
	required_shared_ptr<u16> m_ram2;
	required_ioport_array<6> m_ioport;

	u16 m_pdt;
	u8 m_cmah;

	void c_map(address_map &map) ATTR_COLD;
	void s_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	u16 pdt_r();
	void pdt_w(u16 data);
	u8 pad_r();
	void cmah_w(u8 data);
};

void mu15_state::machine_start()
{
}

void mu15_state::machine_reset()
{
}

void mu15_state::c_map(address_map &map)
{
	map(0x200000, 0x21ffff).ram().share(m_ram1); // ic12, cs1
	map(0xc00000, 0xc07fff).ram().share(m_ram2); // ic13, cs5, saved
}

void mu15_state::s_map(address_map &map)
{
	map(0x000000, 0x1fffff).rom().region("swx00", 0);
}

u16 mu15_state::pdt_r()
{
	u8 bus = 0;
	if((m_cmah & 8) && (m_cmah & 4)) {
		if(m_cmah & 2)
			bus = m_lcd->dr_r();
		else
			bus = m_lcd->status_r();
	}
	return ((bus & 0xc0) << 8) | ((bus & 0x3f) << 6);
}

void mu15_state::cmah_w(u8 data)
{
	u8 old = m_cmah;
	m_cmah = data;
	if((old & 8) && !(m_cmah & 8) && !(m_cmah & 4) && !(old & 4)) {
		u8 v = ((m_pdt >> 8) & 0xc0) | ((m_pdt >> 6) & 0x3f);
		if(m_cmah & 2)
			m_lcd->dr_w(v);
		else
			m_lcd->ir_w(v);
	}
}

void mu15_state::pdt_w(u16 data)
{
	m_pdt = data;
}

u8 mu15_state::pad_r()
{
	u8 r = 0xff;
	for(int i=0; i != 6; i++)
		if(!BIT(m_pdt, 5-i))
			r &= m_ioport[i]->read();
	return r;
}

void mu15_state::mu15(machine_config &config)
{
	SWX00(config, m_maincpu, 8.4672_MHz_XTAL, 0);
	m_maincpu->set_addrmap(swx00_device::AS_C, &mu15_state::c_map);
	m_maincpu->set_addrmap(swx00_device::AS_S, &mu15_state::s_map);

	m_maincpu->add_route(0, "speaker", 1.0, 0);
	m_maincpu->add_route(1, "speaker", 1.0, 1);

	// Nothing connected to sclki, yet...
	m_maincpu->sci_set_external_clock_period(0, attotime::from_hz(500000));
	m_maincpu->sci_set_external_clock_period(1, attotime::from_hz(500000));

	m_maincpu->read_adc<0>().set_constant(0x000); // Host mode
	m_maincpu->read_adc<1>().set_constant(0x000); // GND
	m_maincpu->read_adc<2>().set_constant(0x3ff); // Battery level
	m_maincpu->read_adc<3>().set_constant(0x000); // GND

	m_maincpu->read_pdt().set(FUNC(mu15_state::pdt_r));
	m_maincpu->write_pdt().set(FUNC(mu15_state::pdt_w));
	m_maincpu->read_pad().set(FUNC(mu15_state::pad_r));
	m_maincpu->write_cmah().set(FUNC(mu15_state::cmah_w));

	//  NVRAM(config, m_nvram, nvram_device::DEFAULT_NONE);

	MU5LCD(config, m_lcd);

	SPEAKER(config, "speaker", 2).front();

	// sci0 goes to the host connector

	auto &mdin(MIDI_PORT(config, "mdin"));
	midiin_slot(mdin);
	mdin.rxd_handler().set(m_maincpu, FUNC(swx00_device::sci_rx_w<1>));

	auto &mdout(MIDI_PORT(config, "mdout"));
	midiout_slot(mdout);
	m_maincpu->write_sci_tx<1>().set(mdout, FUNC(midi_port_device::write_txd));
}

ROM_START( mu15 )
	ROM_REGION16_BE( 0x400000, "swx00", 0 )
	// v1.01, Nov. 28, 1998
	ROM_LOAD16_WORD_SWAP( "xv684c0.bin", 0x000000, 0x400000, CRC(e4046aef) SHA1(e286f83ed1fb90e0f98fe565b58112da18f88b5a) )
ROM_END

} // anonymous namespace


CONS( 1998, mu15, 0, 0, mu15,  mu15, mu15_state, empty_init, "Yamaha", "MU15", MACHINE_NOT_WORKING )



ymmu2000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
/*************************************************************************************

    Yamaha MU-500/1000/2000:
        32/64-part, 64/128-note polyphonic/multitimbral General MIDI/XG tone module

    Driver by O. Galibert

    MU2000: SH7043, dual SWP30, sample ram, M37640 for usb, smartcard port
    MU1000: sample ram and smartcard port removed
    MU500:  one SWP30 removed

**************************************************************************************/

#include "emu.h"

#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "bus/plg1x0/plg1x0.h"
#include "cpu/sh/sh7042.h"
#include "machine/input_merger.h"
#include "machine/nvram.h"
#include "machine/sci4.h"
#include "sound/swp30.h"

#include "mulcd.h"
#include "speaker.h"

#include "mu128.lh"
#include "mu2000.lh"


namespace {

static INPUT_PORTS_START( mu500 )
	PORT_START("SWS0")
	PORT_BIT(0x03, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Strings")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Bass")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Guitar")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Organ")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Chrom. Perc.")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Piano")

	PORT_START("SWS1")
	PORT_BIT(0x03, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Synth pad")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Synth lead")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Pipe")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Reed")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Brass")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Ensemble")

	PORT_START("SWS2")
	PORT_BIT(0x03, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Drum")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Model excl.")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("SFX")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Percussive")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Ethnic")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Synth effects")

	PORT_START("SWS3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part +")    PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part -")    PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Mute/Solo") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Effect")    PORT_CODE(KEYCODE_F)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Util")      PORT_CODE(KEYCODE_U)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Edit")      PORT_CODE(KEYCODE_E)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Play")      PORT_CODE(KEYCODE_A)

	PORT_START("SWS4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Value +")   PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Value -")   PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Exit")      PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Select >")  PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Select <")  PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Enter")     PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Seq")       PORT_CODE(KEYCODE_Q)

	PORT_START("SWS5")
	PORT_BIT(0x1f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Audition")  PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Select")    PORT_CODE(KEYCODE_X)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Sampling/Mode") PORT_CODE(KEYCODE_M)
INPUT_PORTS_END

class mu500_state : public driver_device
{
public:
	mu500_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_nvram(*this, "ram")
		, m_swp30m(*this, "swp30m")
		, m_sci(*this, "sci")
		, m_lcd(*this, "lcd")
		, m_ram(*this, "ram")
		, m_ext(*this, "plg%u", 1U)
		, m_ioports(*this, "SWS%u", 0U)

	{ }

	void mu500(machine_config &config);

protected:
	required_device<sh7043a_device> m_maincpu;
	required_device<nvram_device> m_nvram;
	required_device<swp30_device> m_swp30m;
	required_device<sci4_device> m_sci;
	required_device<mulcd_device> m_lcd;
	required_shared_ptr<u32> m_ram;
	required_device_array<plg1x0_connector, 3> m_ext;
	required_ioport_array<6> m_ioports;

	u16 m_pe;
	u8 m_ledsw1, m_ledsw2;

	void map_500(address_map &map) ATTR_COLD;
	void swp30_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	u16 adc_ar_r();
	u16 adc_al_r();
	u16 adc_midisw_r();
	u16 adc_battery_r();

	u16 pe_r();
	void pe_w(u16 data);
	u16 pa_r();

	void update_leds();
	u8 ledsw_r();
	void ledsw1_w(u8 data);
	void ledsw2_w(u8 data);
};

class mu1000_state : public mu500_state
{
public:
	mu1000_state(const machine_config &mconfig, device_type type, const char *tag)
		: mu500_state(mconfig, type, tag)
		, m_swp30s(*this, "swp30s")

	{ }

	void mu1000(machine_config &config);

protected:
	required_device<swp30_device> m_swp30s;
	void map_1000(address_map &map) ATTR_COLD;
};

class mu2000_state : public mu1000_state
{
public:
	mu2000_state(const machine_config &mconfig, device_type type, const char *tag)
		: mu1000_state(mconfig, type, tag)
	{ }

	void mu2000(machine_config &config);

protected:
	void map_2000(address_map &map) ATTR_COLD;
};



void mu500_state::machine_start()
{
	save_item(NAME(m_pe));
	save_item(NAME(m_ledsw1));
	save_item(NAME(m_ledsw2));
	m_pe = 0;
	m_ledsw1 = 0;
	m_ledsw2 = 0;
}

void mu500_state::machine_reset()
{
}

u16 mu500_state::pe_r()
{
	if(BIT(m_pe, 4)) {
		if(BIT(m_pe, 0)) {
			if(BIT(m_pe, 2))
				return m_lcd->data_read() << 8;
			else
				return m_lcd->control_read() << 8;
		} else
			return 0x0000;
	}

	return 0;
}

void mu500_state::pe_w(u16 data)
{
	if(BIT(m_pe, 4) && !BIT(data, 4)) {
		if(!BIT(data, 0)) {
			if(BIT(data, 2))
				m_lcd->data_write(data >> 8);
			else
				m_lcd->control_write(data >> 8);
		}
	}

	m_pe = data;
}

u16 mu500_state::pa_r()
{
	// 21: out, selects between front and back midi a to go on rxd0
	// 20: smvprt
	// 19: smvins
	// 18: smbusy
	// 17: rea (rotary encoder)
	// 16: reb
	return 0xffff;
}

void mu500_state::update_leds()
{
	m_lcd->set_leds(util::bitswap((m_ledsw2 << 8) | m_ledsw1, 9, 8, 7, 6, 10, 11, 12, 13, 14, 15));
}

u8 mu500_state::ledsw_r()
{
	u8 res = 0xff;
	for(u32 i=0; i != 6; i++)
		if(BIT(m_ledsw1, i))
			res &= m_ioports[i]->read();
	return res;
}

void mu500_state::ledsw1_w(u8 data)
{
	m_ledsw1 = data;
	update_leds();
}

void mu500_state::ledsw2_w(u8 data)
{
	m_ledsw2 = data;
	update_leds();
}

// Analog input right (also sent to the swp30m dac0)
u16 mu500_state::adc_ar_r()
{
	return 0;
}

// Analog input left (also sent to the swp30m dac0)
u16 mu500_state::adc_al_r()
{
	return 0;
}

// Put the host switch to pure midi
u16 mu500_state::adc_midisw_r()
{
	return 0;
}

// Battery level
u16 mu500_state::adc_battery_r()
{
	return 0x3ff;
}

void mu500_state::map_500(address_map &map)
{
	// 000000-3fffff: cs0 space, 32 bits, 2 wait states
	map(0x000000, 0x1fffff).rom().region("maincpu", 0);

	// 400000-7fffff: cs1 space, 16 bits, 2 wait states
	map(0x400000, 0x43ffff).ram().share(m_ram);

	// 800000-bfffff: cs2 space, 16 bits, cs assert extension, 3 wait states
	map(0x800000, 0x801fff).m(m_swp30m, FUNC(swp30_device::map));
	// 802000 : slave swp30

	// c00000-ffffff: cs3 space, 8 bits, cs assert extension, 2 wait states, 3 idle states

	// c00000 : smartcard
	map(0xc80000, 0xc80000).rw(FUNC(mu500_state::ledsw_r), FUNC(mu500_state::ledsw1_w));
	// d00000 : smartcard
	// d80000 : contrast, levels
	map(0xe00000, 0xe00000).w(FUNC(mu500_state::ledsw2_w));
	// e80000 : dit2 (digital output)
	map(0xf00000, 0xf0003f).m(m_sci, FUNC(sci4_device::map));

	// f80000 : usb

	// Dedicated dram space, ras precharge = 2.5, ras-cas delay 2, cas-before-ras 2.5, dram write 3, read 3, idle 0, burst, ras down, 16bits, 9-bit address
	// Automatic refresh every 420 cycles, cas-before-ras
	map(0x01000000, 0x0107ffff).ram(); // dram
}

void mu1000_state::map_1000(address_map &map)
{
	map_500(map);
	map(0x802000, 0x803fff).m(m_swp30s, FUNC(swp30_device::map));
}

void mu2000_state::map_2000(address_map &map)
{
	map_1000(map);
	map(0x000000, 0x3fffff).rom().region("maincpu", 0);
}

void mu500_state::swp30_map(address_map &map)
{
	map(0x000000, 0x7fffff).rom().region("swp30", 0);
}

void mu500_state::mu500(machine_config &config)
{
	SH7043A(config, m_maincpu, 7_MHz_XTAL * 4); // md=9, no on-chip rom, 32-bit space, pll 4x
	m_maincpu->set_addrmap(AS_PROGRAM, &mu500_state::map_500);
	m_maincpu->read_porta().set(FUNC(mu500_state::pa_r));
	m_maincpu->read_porte().set(FUNC(mu500_state::pe_r));
	m_maincpu->write_porte().set(FUNC(mu500_state::pe_w));
	m_maincpu->read_adc<0>().set(FUNC(mu500_state::adc_ar_r));
	m_maincpu->read_adc<1>().set_constant(0);
	m_maincpu->read_adc<2>().set(FUNC(mu500_state::adc_al_r));
	m_maincpu->read_adc<3>().set_constant(0);
	m_maincpu->read_adc<4>().set(FUNC(mu500_state::adc_midisw_r));
	m_maincpu->read_adc<5>().set_constant(0);
	m_maincpu->read_adc<6>().set(FUNC(mu500_state::adc_battery_r));
	m_maincpu->read_adc<7>().set_constant(0);

	INPUT_MERGER_ANY_HIGH(config, "sciirq").output_handler().set_inputline(m_maincpu, 0);

	SCI4(config, m_sci);
	m_sci->write_irq<0>().set("sciirq", FUNC(input_merger_device::in_w<0>));
	m_sci->write_irq<1>().set("sciirq", FUNC(input_merger_device::in_w<1>));
	m_sci->write_irq<3>().set_inputline(m_maincpu, 1);

	NVRAM(config, m_nvram, nvram_device::DEFAULT_NONE);

	MULCD(config, m_lcd);

	SPEAKER(config, "speaker", 2).front();

	SWP30(config, m_swp30m);
	m_swp30m->set_addrmap(AS_DATA, &mu500_state::swp30_map);
	m_swp30m->add_route(0, "speaker", 1.0, 0);
	m_swp30m->add_route(1, "speaker", 1.0, 1);

	auto &mdin_a(MIDI_PORT(config, "mdin_a"));
	midiin_slot(mdin_a);
	mdin_a.rxd_handler().set(m_maincpu, FUNC(sh7043_device::sci_rx_w<0>));

	auto &mdin_b(MIDI_PORT(config, "mdin_b"));
	midiin_slot(mdin_b);
	mdin_b.rxd_handler().set(m_maincpu, FUNC(sh7043_device::sci_rx_w<1>));

	auto &mdout(MIDI_PORT(config, "mdout"));
	midiout_slot(mdout);
	m_maincpu->write_sci_tx<0>().set(mdout, FUNC(midi_port_device::write_txd));

	PLG1X0_CONNECTOR(config, m_ext[0], plg1x0_intf, nullptr);
	m_ext[0]->midi_tx().set(m_sci, FUNC(sci4_device::rx_w<30>));
	m_sci->write_tx<30>().set(m_ext[0], FUNC(plg1x0_connector::midi_rx));

	PLG1X0_CONNECTOR(config, m_ext[1], plg1x0_intf, nullptr);
	m_ext[1]->midi_tx().set(m_sci, FUNC(sci4_device::rx_w<31>));
	m_sci->write_tx<31>().set(m_ext[1], FUNC(plg1x0_connector::midi_rx));

	PLG1X0_CONNECTOR(config, m_ext[2], plg1x0_intf, nullptr);
	m_ext[2]->midi_tx().set(m_sci, FUNC(sci4_device::rx_w<32>));
	m_sci->write_tx<32>().set(m_ext[2], FUNC(plg1x0_connector::midi_rx));

	config.set_default_layout(layout_mu128);
}

void mu1000_state::mu1000(machine_config &config)
{
	mu500(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &mu1000_state::map_1000);

	SWP30(config, m_swp30s);
	m_swp30s->set_addrmap(AS_DATA, &mu1000_state::swp30_map);
	m_swp30s->add_route(0, "speaker", 1.0, 0);
	m_swp30s->add_route(1, "speaker", 1.0, 1);
}

void mu2000_state::mu2000(machine_config &config)
{
	mu1000(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &mu2000_state::map_2000);

	config.set_default_layout(layout_mu2000);
}


#define ROM_LOAD32_WORD_SWAP_BIOS(bios,name,offset,length,hash) \
	ROMX_LOAD(name, offset, length, hash, ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2) | ROM_BIOS(bios))

ROM_START( mu500 )
	ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASE00 )
	// Soon (tm)

	ROM_REGION32_LE( 0x2000000, "swp30", ROMREGION_ERASE00 )
	ROM_LOAD32_WORD( "xv364a0.ic49", 0x0000000, 0x800000, CRC(cda1afd6) SHA1(e7098246b33c3cf22ed8cc15ed6383f8a06d17e9) )
	ROM_LOAD32_WORD( "xv365a0.ic50", 0x0000002, 0x800000, CRC(10985ed0) SHA1(d45a2e85859e05046f3ede8317a9bb0b88898116) )
	ROM_LOAD32_WORD( "xw848a0.ic53", 0x1000000, 0x800000, CRC(34913e42) SHA1(9e8b55c2cbac3f69cc0b17aeaf02053145bfaeda) )
	ROM_LOAD32_WORD( "xw849a0.ic54", 0x1000002, 0x800000, CRC(3728f1f2) SHA1(7670d672e24d6388fa92799175f35869a140c451) )
ROM_END

ROM_START( mu1000 )
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_DEFAULT_BIOS("v201")
	ROM_SYSTEM_BIOS( 0, "v201", "Upgrade package (Ver2.01 02-MAY-29)" )
	ROM_LOAD32_WORD_SWAP_BIOS( 0, "mu1000-v2.01-h.bin", 0x000000, 0x100000, CRC(d0809297) SHA1(cee47062966b01ce72e8ebaf6f7fa9778b32f6ab) )
	ROM_LOAD32_WORD_SWAP_BIOS( 0, "mu1000-v2.01-l.bin", 0x000002, 0x100000, CRC(048a2750) SHA1(19f51c6304e3550d0bb8b3cca647f1fc609b0994) )
	// Version < 2.0 is very hard to find, most mu1000 have been upgraded to 2.x (aka EX)

	ROM_REGION32_LE( 0x2000000, "swp30", ROMREGION_ERASE00 )
	ROM_LOAD32_WORD( "xv364a0.ic49", 0x0000000, 0x800000, CRC(cda1afd6) SHA1(e7098246b33c3cf22ed8cc15ed6383f8a06d17e9) )
	ROM_LOAD32_WORD( "xv365a0.ic50", 0x0000002, 0x800000, CRC(10985ed0) SHA1(d45a2e85859e05046f3ede8317a9bb0b88898116) )
	ROM_LOAD32_WORD( "xw848a0.ic53", 0x1000000, 0x800000, CRC(34913e42) SHA1(9e8b55c2cbac3f69cc0b17aeaf02053145bfaeda) )
	ROM_LOAD32_WORD( "xw849a0.ic54", 0x1000002, 0x800000, CRC(3728f1f2) SHA1(7670d672e24d6388fa92799175f35869a140c451) )
ROM_END

ROM_START( mu2000 )
	ROM_REGION( 0x400000, "maincpu", 0 )
	ROM_DEFAULT_BIOS("v200")
	ROM_SYSTEM_BIOS( 0, "v200", "Upgrade package (Ver2.01 02-MAY-29)" )
	ROM_LOAD32_WORD_SWAP_BIOS( 0, "mu2000-v2.01-h.bin", 0x000000, 0x200000, CRC(be3668f6) SHA1(00d008b6a2536a71681ce2f4fd1a5853406f82f2) )
	ROM_LOAD32_WORD_SWAP_BIOS( 0, "mu2000-v2.01-l.bin", 0x000002, 0x200000, CRC(55921a50) SHA1(fd8fe6a5cbba028d847453c004cb2dcf9ba02013) )
	ROM_SYSTEM_BIOS( 1, "v101", "20 (v1.01, 99-NOV-16)" )
	ROM_LOAD32_WORD_SWAP_BIOS( 1, "xw87020.ic25", 0x000000, 0x200000, CRC(79f6c158) SHA1(2213b9c661c6b1a79963321c37aff40be7cc1fff) )
	ROM_LOAD32_WORD_SWAP_BIOS( 1, "xw86920.ic24", 0x000002, 0x200000, CRC(afbac33c) SHA1(594b4c64aecf6a5204b058375e39e44b8fe373be) )

	ROM_REGION32_LE( 0x2000000, "swp30", ROMREGION_ERASE00 )
	ROM_LOAD32_WORD( "xv364a0.ic49", 0x0000000, 0x800000, CRC(cda1afd6) SHA1(e7098246b33c3cf22ed8cc15ed6383f8a06d17e9) )
	ROM_LOAD32_WORD( "xv365a0.ic50", 0x0000002, 0x800000, CRC(10985ed0) SHA1(d45a2e85859e05046f3ede8317a9bb0b88898116) )
	ROM_LOAD32_WORD( "xw848a0.ic53", 0x1000000, 0x800000, CRC(34913e42) SHA1(9e8b55c2cbac3f69cc0b17aeaf02053145bfaeda) )
	ROM_LOAD32_WORD( "xw849a0.ic54", 0x1000002, 0x800000, CRC(3728f1f2) SHA1(7670d672e24d6388fa92799175f35869a140c451) )
ROM_END

} // anonymous namespace


CONS( 2000, mu500,  0,     0, mu500,  mu500, mu500_state,  empty_init, "Yamaha", "MU500",  MACHINE_NOT_WORKING )
CONS( 1999, mu1000, mu500, 0, mu1000, mu500, mu1000_state, empty_init, "Yamaha", "MU1000", MACHINE_NOT_WORKING )
CONS( 1999, mu2000, mu500, 0, mu2000, mu500, mu2000_state, empty_init, "Yamaha", "MU2000", MACHINE_NOT_WORKING )



ymmu5.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/*
    Yamaha MU-5 Tone Generator
    Preliminary driver by R. Belmont

    CPU: H8/3002 (HD6413002F10)
    Sound: YMW-258-F (aka "MultiPCM")
*/

#include "emu.h"

#include "cpu/h8/h83002.h"
#include "sound/multipcm.h"
#include "bus/midi/midi.h"

#include "mu5lcd.h"

#include "screen.h"
#include "speaker.h"


namespace {

class mu5_state : public driver_device
{
public:
	mu5_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ymw258(*this, "ymw258"),
		m_lcd(*this, "lcd"),
		m_key(*this, "S%c", 'A'),
		m_matrixsel(0)
	{ }

	void mu5(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<h83002_device> m_maincpu;
	required_device<multipcm_device> m_ymw258;
	required_device<mu5lcd_device> m_lcd;
	required_ioport_array<6> m_key;

	void mu5_map(address_map &map) ATTR_COLD;
	void ymw258_map(address_map &map) ATTR_COLD;

	u8 m_lcd_ctrl = 0U;
	u8 m_lcd_data = 0U;

	void lcd_ctrl_w(u8 data);
	u8 lcd_ctrl_r();
	void lcd_data_w(u8 data);
	u8 lcd_data_r();

	u8 m_matrixsel;
	u8 matrix_r();
};

void mu5_state::mu5_map(address_map &map)
{
	map(0x000000, 0x01ffff).rom().region("maincpu", 0);
	map(0x200000, 0x21ffff).ram();
	map(0x400000, 0x400007).rw(m_ymw258, FUNC(multipcm_device::read), FUNC(multipcm_device::write)).umask16(0xffff);
}

void mu5_state::ymw258_map(address_map &map)
{
	map(0x000000, 0x1fffff).rom();
}

void mu5_state::machine_start()
{
	save_item(NAME(m_lcd_ctrl));
	save_item(NAME(m_lcd_data));
	save_item(NAME(m_matrixsel));
}

void mu5_state::machine_reset()
{
	m_lcd_ctrl = 0;
	m_lcd_data = 0;
	m_matrixsel = 0;
}

u8 mu5_state::matrix_r()
{
	u8 data = 0x3f;

	for (int i = 0; i < 6; i++)
		if (!BIT(m_matrixsel, i))
			data &= m_key[i]->read();

	return data;
}

void mu5_state::lcd_ctrl_w(u8 data)
{
	// bit 2 = rs
	// bit 1 = r/w
	// bit 0 = e

	bool e_edge = (data ^ m_lcd_ctrl) & 1;
	m_lcd_ctrl = data;
	if(e_edge) {
		switch(m_lcd_ctrl & 7) {
		case 0:
			m_lcd->ir_w(m_lcd_data);
			break;
		case 3:
			m_lcd_data = m_lcd->status_r();
			break;
		case 4:
			m_lcd->dr_w(m_lcd_data);
			break;
		case 7:
			m_lcd_data = m_lcd->dr_r();
			break;
		}
	}
}

u8 mu5_state::lcd_ctrl_r()
{
	return m_lcd_ctrl;
}

void mu5_state::lcd_data_w(u8 data)
{
	m_lcd_data = data;
}

u8 mu5_state::lcd_data_r()
{
	return m_lcd_data;
}

static INPUT_PORTS_START(mu5)
	PORT_START("SA")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part Down") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part Up") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Value Down") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Value Up") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Mute") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Exit") PORT_CODE(KEYCODE_N)

	PORT_START("SB")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("0") PORT_CODE(KEYCODE_0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("9") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("8") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x38, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("SC")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("7") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("2") PORT_CODE(KEYCODE_2)

	PORT_START("SD")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Octave Up") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Octave Down") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Mstr Tune") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Trns Pose") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Mute Lock") PORT_CODE(KEYCODE_D)

	PORT_START("SE")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Velo City") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Locl Ctrl") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Dump Out") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Init All") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Vol") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Pan") PORT_CODE(KEYCODE_Y)

	PORT_START("SF")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("MIDI Ch") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Note Shft") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part Tune") PORT_CODE(KEYCODE_O)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Bend Rnge") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("+/-") PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER)
INPUT_PORTS_END

void mu5_state::mu5(machine_config &config)
{
	/* basic machine hardware */
	H83002(config, m_maincpu, 10_MHz_XTAL); // clock verified by schematics
	m_maincpu->set_addrmap(AS_PROGRAM, &mu5_state::mu5_map);
	m_maincpu->read_adc<7>().set_constant(0x3ff); // battery level
	m_maincpu->read_port4().set([this]() -> u8 { return m_matrixsel; });
	m_maincpu->write_port4().set([this](u8 data) { m_matrixsel = data; });
	m_maincpu->read_port6().set(FUNC(mu5_state::lcd_ctrl_r));
	m_maincpu->write_port6().set(FUNC(mu5_state::lcd_ctrl_w));
	m_maincpu->read_port7().set(FUNC(mu5_state::matrix_r));
	m_maincpu->read_porta().set_constant(0);
	m_maincpu->read_portb().set(FUNC(mu5_state::lcd_data_r));
	m_maincpu->write_portb().set(FUNC(mu5_state::lcd_data_w));

	MU5LCD(config, m_lcd);

	SPEAKER(config, "speaker", 2).front();

	MULTIPCM(config, m_ymw258, 9.4_MHz_XTAL); // clock verified by schematics
	m_ymw258->set_addrmap(0, &mu5_state::ymw258_map);
	m_ymw258->add_route(0, "speaker", 1.0, 0);
	m_ymw258->add_route(1, "speaker", 1.0, 1);

	MIDI_PORT(config, "mdin", midiin_slot, "midiin").rxd_handler().set(m_maincpu, FUNC(h83002_device::sci_rx_w<1>));

	auto &mdout(MIDI_PORT(config, "mdout", midiout_slot, "midiout"));
	m_maincpu->write_sci_tx<0>().set(mdout, FUNC(midi_port_device::write_txd));
}

ROM_START( mu5 )
	ROM_REGION(0x20000, "maincpu", 0)
	ROM_LOAD("yamaha_mu5_program_xq201a0.bin", 0x000000, 0x020000, CRC(e9b3ec26) SHA1(cfee69699de78e2792dac24d428a120021ba147b))

	ROM_REGION(0x200000, "ymw258", 0)
	ROM_LOAD("yamaha_mu5_waverom_xp50280-801.bin", 0x000000, 0x200000, CRC(e0913030) SHA1(369f8df4942b6717c142ca8c4913e556dafae187))

ROM_END

} // anonymous namespace


CONS(1994, mu5, 0, 0, mu5, mu5, mu5_state, empty_init, "Yamaha", "MU-5", MACHINE_NOT_WORKING )



ymmu50.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont, Olivier Galibert
/*************************************************************************************

    Yamaha MU-50 : 16-part, 32-note polyphonic/multitimbral General MIDI/GS/XG
                   tone module
    Driver by R. Belmont and O. Galibert

    Cost-reduced version of the MU80, uses the SWP00 which is a single-chip
    integrated version of the multi-chip SWP20.  As a consequence has half the
    voices, loses the parametric EQ and the AD inputs.  The sample roms are also
    smaller, and it only has one midi input.

    A wavetable version exists as the DB50XG.

**************************************************************************************/

#include "emu.h"

#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/h8/h83003.h"
#include "machine/nvram.h"
#include "sound/adc.h"
#include "sound/swp00.h"

#include "mulcd.h"
#include "speaker.h"

#include "mu50.lh"


namespace {

static INPUT_PORTS_START( mu50 )
	PORT_START("O0")
	PORT_BIT(0x83, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Play")      PORT_CODE(KEYCODE_A)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Edit")      PORT_CODE(KEYCODE_E)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Mute/Solo") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part -")    PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part +")    PORT_CODE(KEYCODE_CLOSEBRACE)

	PORT_START("O1")
	PORT_BIT(0x83, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Util")      PORT_CODE(KEYCODE_U)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Effect")    PORT_CODE(KEYCODE_F)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Enter")     PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Select <")  PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Select >")  PORT_CODE(KEYCODE_STOP)

	PORT_START("O2")
	PORT_BIT(0x83, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Mode")      PORT_CODE(KEYCODE_M)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Exit")      PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Value -")   PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Value +")   PORT_CODE(KEYCODE_EQUALS)
INPUT_PORTS_END

class mu50_state : public driver_device
{
public:
	mu50_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_mu50cpu(*this, "mu50cpu")
		, m_nvram(*this, "ram")
		, m_swp00(*this, "swp00")
		, m_lcd(*this, "lcd")
		, m_ioport_o0(*this, "O0")
		, m_ioport_o1(*this, "O1")
		, m_ioport_o2(*this, "O2")
		, m_ram(*this, "ram")
		, m_ad(*this, "ad")
		, m_adc(*this, "adc%u", 0U)
	{ }

	void mu50(machine_config &config);

private:
	enum {
		P6_LCD_RS     = 0x04,
		P6_LCD_RW     = 0x02,
		P6_LCD_ENABLE = 0x01
	};

	required_device<h83003_device> m_mu50cpu;
	required_device<nvram_device> m_nvram;
	required_device<swp00_device> m_swp00;
	required_device<mulcd_device> m_lcd;
	required_ioport m_ioport_o0;
	required_ioport m_ioport_o1;
	required_ioport m_ioport_o2;
	required_shared_ptr<u16> m_ram;
	required_device<microphone_device> m_ad;
	required_device_array<adc10_device, 2> m_adc;

	u8 cur_p6, cur_p9, cur_pa, cur_pb, cur_pc;

	u16 adc_ar_r();
	u16 adc_al_r();
	u16 adc_midisw_r();
	u16 adc_battery_r();

	void p6_w(u8 data);
	u8 p6_r();
	void p9_w(u8 data);
	void pa_w(u8 data);
	u8 pa_r();
	void pb_w(u8 data);
	u8 pb_r();
	void pc_w(u8 data);
	u8 pc_r();
	void update_contrast();

	void mu50_map(address_map &map) ATTR_COLD;

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
};

void mu50_state::machine_start()
{
	cur_p6 = cur_p9 = cur_pa = cur_pb = cur_pc = 0xff;
}

void mu50_state::machine_reset()
{
	// Active-low, wired to gnd
	m_mu50cpu->set_input_line(0, ASSERT_LINE);
}

void mu50_state::mu50_map(address_map &map)
{
	map(0x000000, 0x07ffff).rom().region("mu50cpu", 0);
	map(0x200000, 0x20ffff).ram().share(m_ram); // 64K work RAM
	map(0x400000, 0x4007ff).m(m_swp00, FUNC(swp00_device::map));
}

// Analog input right (not sent to the swp, mixing is analog)
u16 mu50_state::adc_ar_r()
{
	s16 v = m_adc[0]->read();
	if(v < 0)
		v = -v;
	return 0x3ff - v;
}

// Analog input left (not sent to the swp, mixing is analog)
u16 mu50_state::adc_al_r()
{
	s16 v = m_adc[1]->read();
	if(v < 0)
		v = -v;
	return 0x3ff - v;
}

// Put the host switch to pure midi
u16 mu50_state::adc_midisw_r()
{
	// 000-0bf: midi
	// 0c0-1ff: pc2
	// 200-37f: pc1
	// 380-3ff: mac
	return 0x000;
}

// Battery level
u16 mu50_state::adc_battery_r()
{
	return 0x200;
}

void mu50_state::p6_w(u8 data)
{
	data ^= P6_LCD_ENABLE;
	if((cur_p6 & P6_LCD_ENABLE) && !(data & P6_LCD_ENABLE)) {
		if(!(cur_p6 & P6_LCD_RW)) {
			if(cur_p6 & P6_LCD_RS)
				m_lcd->data_write(cur_pa);
			else
				m_lcd->control_write(cur_pa);
		}
	}

	cur_p6 = data;
}

u8 mu50_state::p6_r()
{
	return cur_p6;
}

void mu50_state::p9_w(u8 data)
{
	cur_p9 = data;
	update_contrast();
}

u8 mu50_state::pb_r()
{
	return cur_pb;
}

void mu50_state::update_contrast()
{
	m_lcd->set_contrast(((~cur_p9 >> 3) & 0x6) | (BIT(~cur_pb, 1)));
}

void mu50_state::pb_w(u8 data)
{
	cur_pb = data;
	m_lcd->set_leds((~data >> 2) & 0x1f);
	update_contrast();
}

void mu50_state::pa_w(u8 data)
{
	cur_pa = data;
}

void mu50_state::pc_w(u8 data)
{
	cur_pc = data;
}

u8 mu50_state::pa_r()
{
	if((cur_p6 & P6_LCD_ENABLE)) {
		if(cur_p6 & P6_LCD_RW)
			{
				if(cur_p6 & P6_LCD_RS)
					return m_lcd->data_read();
				else
					return m_lcd->control_read();
			} else
			return 0x00;
	}
	return cur_pa;
}

u8 mu50_state::pc_r()
{
	u8 res = cur_pc | 0x7c;
	if(!(cur_pc & 0x01))
		res &= m_ioport_o0->read();
	if(!(cur_pc & 0x02))
		res &= m_ioport_o1->read();
	if(!(cur_pc & 0x80))
		res &= m_ioport_o2->read();
	return res;
}

void mu50_state::mu50(machine_config &config)
{
	H83003(config, m_mu50cpu, 10_MHz_XTAL);
	m_mu50cpu->set_addrmap(AS_PROGRAM, &mu50_state::mu50_map);
	m_mu50cpu->read_adc<0>().set(FUNC(mu50_state::adc_ar_r));
	m_mu50cpu->read_adc<1>().set_constant(0);
	m_mu50cpu->read_adc<2>().set(FUNC(mu50_state::adc_al_r));
	m_mu50cpu->read_adc<3>().set_constant(0);
	m_mu50cpu->read_adc<4>().set(FUNC(mu50_state::adc_midisw_r));
	m_mu50cpu->read_adc<5>().set_constant(0);
	m_mu50cpu->read_adc<6>().set(FUNC(mu50_state::adc_battery_r));
	m_mu50cpu->read_adc<7>().set_constant(0);
	m_mu50cpu->read_port6().set(FUNC(mu50_state::p6_r));
	m_mu50cpu->write_port6().set(FUNC(mu50_state::p6_w));
	m_mu50cpu->write_port9().set(FUNC(mu50_state::p9_w));
	m_mu50cpu->read_porta().set(FUNC(mu50_state::pa_r));
	m_mu50cpu->write_porta().set(FUNC(mu50_state::pa_w));
	m_mu50cpu->read_portb().set(FUNC(mu50_state::pb_r));
	m_mu50cpu->write_portb().set(FUNC(mu50_state::pb_w));
	m_mu50cpu->read_portc().set(FUNC(mu50_state::pc_r));
	m_mu50cpu->write_portc().set(FUNC(mu50_state::pc_w));

	m_mu50cpu->read_port7().set_constant(0);
	m_mu50cpu->read_port9().set_constant(0);

	NVRAM(config, m_nvram, nvram_device::DEFAULT_NONE);

	MULCD(config, m_lcd);

	MICROPHONE(config, m_ad, 2).front();
	m_ad->add_route(0, "speakers", 1.0, 0);
	m_ad->add_route(1, "speakers", 1.0, 1);
	m_ad->add_route(0, "adc0", 1.0);
	m_ad->add_route(1, "adc1", 1.0);

	ADC10(config, m_adc[0]);
	ADC10(config, m_adc[1]);

	SPEAKER(config, "speakers", 2).front();

	SWP00(config, m_swp00);
	m_swp00->add_route(0, "speakers", 1.0, 0);
	m_swp00->add_route(1, "speakers", 1.0, 1);

	auto &mdin(MIDI_PORT(config, "mdin"));
	midiin_slot(mdin);
	mdin.rxd_handler().set(m_mu50cpu, FUNC(h83003_device::sci_rx_w<1>));

	auto &mdout(MIDI_PORT(config, "mdout"));
	midiout_slot(mdout);
	m_mu50cpu->write_sci_tx<1>().set(mdout, FUNC(midi_port_device::write_txd));

	config.set_default_layout(layout_mu50);
}

#define ROM_LOAD16_WORD_SWAP_BIOS(bios,name,offset,length,hash) \
		ROMX_LOAD(name, offset, length, hash, ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(bios))

ROM_START( mu50 )
	ROM_REGION( 0x80000, "mu50cpu", 0 )
	ROM_SYSTEM_BIOS( 0, "bios0", "xr174c0 (v1.05, Aug. 21, 1995)" )
	ROM_LOAD16_WORD_SWAP_BIOS( 0, "xr174c0.ic7", 0x000000, 0x080000, CRC(902520a4) SHA1(9ca892920598f9fdf08544dac4c0e54e7d46ee3c) )
	ROM_SYSTEM_BIOS( 1, "bios1", "? (v1.04, May 22, 1995)" )
	ROM_LOAD16_WORD_SWAP_BIOS( 1, "yamaha_mu50.bin", 0x000000, 0x080000, CRC(507168ad) SHA1(58c41f10d292cac35ef0e8f93029fbc4685df586) )

	ROM_REGION( 0x400000, "swp00", 0 )
	// Identical to the db50xg roms
	ROM_LOAD( "xq057c0.ic18", 0x000000, 0x200000, CRC(d4adbc7e) SHA1(32f653c7644d060f5a6d63a435ae3a7412386d92) )
	ROM_LOAD( "xq058c0.ic19", 0x200000, 0x200000, CRC(7b68f475) SHA1(adf68689b4842ec5bc9b0ea1bb99cf66d2dec4de) )
ROM_END

} // anonymous namespace


CONS( 1995, mu50, 0, 0, mu50,  mu50, mu50_state, empty_init, "Yamaha", "MU50", 0 )



ymmu80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont, Olivier Galibert
/*************************************************************************************

    Yamaha MU-80 : 32-part, 64-note polyphonic/multitimbral General MIDI/GS/XG
                   tone module
    Preliminary driver by R. Belmont and O. Galibert

    The first XG-capable module (mu15 and mu50 came out later).  Uses a distributed
    structure of chips, with two chained SWP20 providing 32-notes each with a MEG
    effects processor at the end of the chain followed by an EQ chip on the result.

    MU80 CPU: Hitachi H8/3002 (HD6413D02F16), strapped for mode 4, with a 12 MHz oscillator
    Sound ASICs: 2x Yamaha YMM275-F/SWP20 + 2x YMM279-F/SWD wave decoders + HD62908 "MEG"
    effects processor

    I/O ports from service manual:

    Port B (MU80)
        0 - LCD data, SW data, LED 1
        1 - LCD data, SW data, LED 2
        2 - LCD data, SW data, LED 3
        3 - LCD data, SW data, LED 4
        4 - LCD data, SW data, LED 5
        5 - LCD data, SW strobe data
        6 - LCD data, SW strobe data
        7 - LCD data, SW data, LED 6

    Port 2:
        0 - (out) LCD control RS
        1 - (out) LCD control R/W
        2 - (out) LCD control E
        3 - (out) LCD contrast A
        4 - (out) LCD contrast B
        5 - (out) LCD contrast C
        6 - (out) 1 MHz clock for serial
        7 - NC

    Port 3:
        4 - (out) A/D gain control 1
        5 - (out) A/D gain control 2

    Port 5:
        3 - (out) Reset signal for rotary encoder

    Port 6:
        1 - NC
        2 - (out) PB select (SW1)
        3 - (out) PB select (SW2)
        4 - (out) reset PB
        5 - (out) reset SWP30 (sound chip)
        6 - NC
        7 - (in) Plug detection for A/D input

    Port A (MU80):
        0 -
        1 - LCD control RS
        2 -
        3 - (same as sws on MU100) LED,SW Strobe data latch
        4 - (same as swd on MU100) SW data read control
        5 - LCD control E
        6 - LCD control RW
        7 -

    Port F:
        0 - (out) (sws) LED,SW Strobe data latch
        1 - (out) (swd) SW data read control
        2 - (out) PB select (SW4)

    Port G:
        0 - (out) PB select (SW3)

    Analog input channels:
        0 - level input R
        2 - level output L
        4 - host SW type switch position
        6 - battery voltage
        7 - model check (0 for MU100, 0.5 for OEM, 1 for MU100R)

    Switch map at the connector (17=ground)
        09 8 play
        10 8 edit
        11 8 mute/solo
        12 8 part -
        13 8 part +
        14 8 util
        15 8 effect
        16 8 enter
        12 7 select <
        13 7 select >
        16 7 mode
        15 7 eq
        14 7 exit
        10 7 value -
        11 7 value +
           2 led play
           3 led edit
           4 led util
           5 led effect
           6 led mode
           1 led eq

     IC32:
        1 p10 c.2
        2 p11 c.3
        3 p12 c.4
        4 p13 c.5
        5 p14 c.6
        6 p15 c.7
        7 p16 c.8
        8 p17 c.1
        g sws

     IC33
        1 p17 c.09
        2 p16 c.10
        3 p15 c.11
        4 p14 c.12
        5 p13 c.13
        6 p12 c.14
        7 p11 c.15
        8 p10 c.16
        g swd

**************************************************************************************/

#include "emu.h"

#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/h8/h83002.h"
#include "mulcd.h"
#include "sound/swp20.h"
#include "sound/meg.h"

#include "debugger.h"
#include "speaker.h"


namespace {

static INPUT_PORTS_START( mu80 )
	PORT_START("P7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Enter")     PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Effect")    PORT_CODE(KEYCODE_F)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Util")      PORT_CODE(KEYCODE_U)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part +")    PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part -")    PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Mute/Solo") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Edit")      PORT_CODE(KEYCODE_E)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Play")      PORT_CODE(KEYCODE_A)

	PORT_START("P8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Mode")      PORT_CODE(KEYCODE_M)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Eq")        PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Exit")      PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Select >")  PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Select <")  PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Value +")   PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Value -")   PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

class mu80_state : public driver_device
{
public:
	mu80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_mu80cpu(*this, "mu80cpu")
		, m_swp20_0(*this, "swp20_0")
		, m_swp20_1(*this, "swp20_1")
		, m_meg(*this, "meg")
		, m_lcd(*this, "lcd")
		, m_ioport_p7(*this, "P7")
		, m_ioport_p8(*this, "P8")
	{ }

	void mu80(machine_config &config);

private:
	enum {
		PA_LCD_RS     = 0x02,
		PA_LCD_ENABLE = 0x20,
		PA_LCD_RW     = 0x40
	};

	required_device<h83002_device> m_mu80cpu;
	required_device<swp20_device> m_swp20_0;
	required_device<swp20_device> m_swp20_1;
	required_device<meg_device> m_meg;
	required_device<mulcd_device> m_lcd;
	required_ioport m_ioport_p7;
	required_ioport m_ioport_p8;

	u8 cur_p6, cur_pa, cur_pb, cur_ic32;

	u16 adc_ar_r();
	u16 adc_al_r();
	u16 adc_midisw_r();
	u16 adc_battery_r();

	void p6_w(u8 data);
	u8 p6_r();
	void pa_w(u8 data);
	u8 pa_r();
	void pb_w(u8 data);
	u8 pb_r();

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	void mu80_map(address_map &map) ATTR_COLD;
};

void mu80_state::machine_start()
{
	cur_p6 = cur_pa = cur_pb = cur_ic32 = 0xff;
}

void mu80_state::machine_reset()
{
	// Active-low, wired to gnd
	m_mu80cpu->set_input_line(0, ASSERT_LINE);
}

void mu80_state::mu80_map(address_map &map)
{
	map(0x000000, 0x07ffff).rom().region("mu80cpu", 0);
	map(0x200000, 0x20ffff).ram(); // 64K work RAM
	map(0x400000, 0x40003f).m(m_swp20_0, FUNC(swp20_device::map));
	map(0x440000, 0x44001f).m(m_meg, FUNC(meg_device::map));
	map(0x460000, 0x46003f).m(m_swp20_1, FUNC(swp20_device::map));
}

// Analog input right (also sent to the swp)
u16 mu80_state::adc_ar_r()
{
	return 0;
}

// Analog input left (also sent to the swp)
u16 mu80_state::adc_al_r()
{
	return 0;
}

// Put the host switch to pure midi
u16 mu80_state::adc_midisw_r()
{
	return 0;
}

// Battery level
u16 mu80_state::adc_battery_r()
{
	return 0x200;
}

void mu80_state::pb_w(u8 data)
{
	cur_pb = data;
}

u8 mu80_state::pb_r()
{
	if((cur_pa & PA_LCD_ENABLE)) {
		if(cur_pa & PA_LCD_RW) {
			if(cur_pa & PA_LCD_RS)
				return m_lcd->data_read();
			else
				return m_lcd->control_read();
		} else {
			if(!(cur_pa & 0x10)) {
				u8 val = 0xff;
				if(!(cur_ic32 & 0x20))
					val &= m_ioport_p7->read();
				if(!(cur_ic32 & 0x40))
					val &= m_ioport_p8->read();
				return val;
			}

			return 0x00;
		}
	}

	return cur_pb;
}

void mu80_state::p6_w(u8 data)
{
	cur_p6 = data;
}

u8 mu80_state::p6_r()
{
	return cur_p6;
}

void mu80_state::pa_w(u8 data)
{
	data ^= PA_LCD_ENABLE;
	if((cur_pa & PA_LCD_ENABLE) && !(data & PA_LCD_ENABLE)) {
		if(!(cur_pa & PA_LCD_RW)) {
			if(cur_pa & PA_LCD_RS)
				m_lcd->data_write(cur_pb);
			else
				m_lcd->control_write(cur_pb);
		}
	}

	if(!(cur_pa & 0x08) && (data & 0x08))
		cur_ic32 = cur_pb;

	cur_pa = data;
}

u8 mu80_state::pa_r()
{
	return cur_pa;
}

void mu80_state::mu80(machine_config &config)
{
	H83002(config, m_mu80cpu, 12_MHz_XTAL);
	m_mu80cpu->set_addrmap(AS_PROGRAM, &mu80_state::mu80_map);
	m_mu80cpu->read_adc<0>().set(FUNC(mu80_state::adc_ar_r));
	m_mu80cpu->read_adc<1>().set_constant(0);
	m_mu80cpu->read_adc<2>().set(FUNC(mu80_state::adc_al_r));
	m_mu80cpu->read_adc<3>().set_constant(0);
	m_mu80cpu->read_adc<4>().set(FUNC(mu80_state::adc_midisw_r));
	m_mu80cpu->read_adc<5>().set_constant(0);
	m_mu80cpu->read_adc<6>().set(FUNC(mu80_state::adc_battery_r));
	m_mu80cpu->read_adc<7>().set_constant(0); // inputmod from the gate array
	m_mu80cpu->read_port6().set(FUNC(mu80_state::p6_r));
	m_mu80cpu->write_port6().set(FUNC(mu80_state::p6_w));
	m_mu80cpu->read_porta().set(FUNC(mu80_state::pa_r));
	m_mu80cpu->write_porta().set(FUNC(mu80_state::pa_w));
	m_mu80cpu->read_portb().set(FUNC(mu80_state::pb_r));
	m_mu80cpu->write_portb().set(FUNC(mu80_state::pb_w));

	MULCD(config, m_lcd);

	SPEAKER(config, "speaker", 2).front();

	SWP20(config, m_swp20_0);
	m_swp20_0->set_device_rom_tag("swp20");
	m_swp20_0->add_route(0, "speaker", 1.0, 0);
	m_swp20_0->add_route(1, "speaker", 1.0, 1);

	SWP20(config, m_swp20_1);
	m_swp20_1->set_device_rom_tag("swp20");
	m_swp20_1->add_route(0, "speaker", 1.0, 0);
	m_swp20_1->add_route(1, "speaker", 1.0, 1);

	MEG(config, m_meg);

	auto &mdin_a(MIDI_PORT(config, "mdin_a"));
	midiin_slot(mdin_a);
	mdin_a.rxd_handler().set(m_mu80cpu, FUNC(h83002_device::sci_rx_w<1>));

	auto &mdin_b(MIDI_PORT(config, "mdin_b"));
	midiin_slot(mdin_b);
	mdin_b.rxd_handler().set(m_mu80cpu, FUNC(h83002_device::sci_rx_w<0>));

	auto &mdout(MIDI_PORT(config, "mdout"));
	midiout_slot(mdout);
	m_mu80cpu->write_sci_tx<0>().set(mdout, FUNC(midi_port_device::write_txd));
}

ROM_START( mu80 )
	ROM_REGION( 0x80000, "mu80cpu", 0 )
	// v1.04, Dec. 04, 1994
	ROM_LOAD16_WORD_SWAP( "xq556a0.ic8", 0x000000, 0x080000, CRC(c31074c0) SHA1(a11bd4523cd8ff1e1744078c3b4c18112b73c61e) )

	ROM_REGION16_LE( 0x800000, "swp20", 0 )
	ROM_LOAD( "xq012b0-822.bin", 0x000000, 0x200000, CRC(cb454418) SHA1(43dab164de5497df9203a1ac9e7ece478276e46d))
	ROM_LOAD( "xq013b0-823.bin", 0x200000, 0x200000, CRC(f14117b4) SHA1(fc603b7b7a3f3500521d4d9638a9562f90cc0354))
	ROM_LOAD( "xq089b0-824.bin", 0x400000, 0x200000, CRC(0adbf203) SHA1(ecc4c1cfb123d12bc3dad092c31bddc707bb4d07))
	ROM_LOAD( "xq090b0-825.bin", 0x600000, 0x200000, CRC(34c422b3) SHA1(14073c41fbdf4faa9da9c83dafe4dc2d6b01b53b))
ROM_END

} // anonymous namespace


CONS( 1994, mu80, 0, 0, mu80, mu80, mu80_state, empty_init, "Yamaha", "MU80", MACHINE_NOT_WORKING )



ymmu90.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont, Olivier Galibert
/*************************************************************************************

    Yamaha MU-90 : 32-part, 64-voice polyphonic/multitimbral General MIDI/GS/XG
                    tone module
    Preliminary driver by R. Belmont and O. Galibert

    The successor to the mu80, with the swp20/meg/eq combo remplaced by an all-in-one swp30.
    Exists in rackable (mu90r) version but we don't have that firmware variant and in
    screenless version (mu90b).

    Sound roms are a subset of the mu100's.

    MU90 CPU: Hitachi H8/3002 (HD6432655F), strapped for mode 4 (24-bit address, 16-bit data, no internal ROM)
    Sound ASIC: Yamaha XS725A0/SWP30
    RAM: 1 MSM51008 (1 meg * 1 bit = 128KBytes)


**************************************************************************************/

#include "emu.h"

#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/h8/h83002.h"
#include "mulcd.h"
#include "sound/swp30.h"

#include "debugger.h"
#include "speaker.h"


namespace {

static INPUT_PORTS_START( mu90 )
	PORT_START("P7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Enter")     PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Effect")    PORT_CODE(KEYCODE_F)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Util")      PORT_CODE(KEYCODE_U)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part +")    PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part -")    PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Mute/Solo") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Edit")      PORT_CODE(KEYCODE_E)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Play")      PORT_CODE(KEYCODE_A)

	PORT_START("P8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Mode")      PORT_CODE(KEYCODE_M)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Eq")        PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Exit")      PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Select >")  PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Select <")  PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Value +")   PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Value -")   PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

class mu90_state : public driver_device
{
public:
	mu90_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_swp30(*this, "swp30")
		, m_lcd(*this, "lcd")
		, m_ioport_p7(*this, "P7")
		, m_ioport_p8(*this, "P8")
	{ }

	void mu90(machine_config &config);
	void mu90b(machine_config &config);

private:
	required_device<h83002_device> m_maincpu;
	required_device<swp30_device> m_swp30;
	optional_device<mulcd_device> m_lcd;
	required_ioport m_ioport_p7;
	required_ioport m_ioport_p8;

	u8 cur_pa, cur_pb;
	u8 cur_ic34;

	u16 adc_ar_r();
	u16 adc_al_r();
	u16 adc_midisw_r();
	u16 adc_battery_r();

	void pa_w(u8 data);
	void pb_w(u8 data);
	u8 pb_r();

	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	void mu90_map(address_map &map) ATTR_COLD;
	void swp30_map(address_map &map) ATTR_COLD;
};

void mu90_state::machine_start()
{
	cur_pa = cur_pb = 0xff;
}

void mu90_state::machine_reset()
{
	// Active-low, wired to gnd
	m_maincpu->set_input_line(0, ASSERT_LINE);
}

void mu90_state::mu90_map(address_map &map)
{
	map(0x000000, 0x0fffff).rom().region("maincpu", 0);
	map(0x200000, 0x20ffff).ram(); // 64K work RAM
	map(0x400000, 0x401fff).m(m_swp30, FUNC(swp30_device::map));
}

void mu90_state::swp30_map(address_map &map)
{
	map(0x000000, 0x1fffff).rom().region("swp30", 0);
}

// Analog input right (also sent to the swp)
u16 mu90_state::adc_ar_r()
{
	return 0;
}

// Analog input left (also sent to the swp)
u16 mu90_state::adc_al_r()
{
	return 0;
}

// Put the host switch to pure midi
u16 mu90_state::adc_midisw_r()
{
	return 0;
}

// Battery level
u16 mu90_state::adc_battery_r()
{
	return 0x200;
}

void mu90_state::pb_w(u8 data)
{
	cur_pb = data;
}

u8 mu90_state::pb_r()
{
	u8 res = 0xff;
	if(m_lcd && (cur_pa & 0x20)) {
		if(cur_pa & 0x40) {
			if(cur_pa & 0x02)
				res &= m_lcd->data_read();
			else
				res &= m_lcd->control_read();
		}
	}

	if(!(cur_pa & 0x10)) {
		if(!(cur_ic34 & 0x20))
			res &= m_ioport_p7->read();
		if(!(cur_ic34 & 0x40))
			res &= m_ioport_p8->read();
	}

	return res;
}

void mu90_state::pa_w(u8 data)
{
	if(!(cur_pa & 0x01) && (data & 0x01)) {
		if(m_lcd)
			m_lcd->set_contrast(cur_pb & 7);
		logerror("ad1 input level %s\n", cur_pb & 0x80 ? "line" : "mic");
		logerror("ad2 input level %s\n", cur_pb & 0x40 ? "line" : "mic");
	}

	if(m_lcd && (cur_pa & 0x20) && !(data & 0x20)) {
		if(!(cur_pa & 0x40)) {
			if(cur_pa & 0x02)
				m_lcd->data_write(cur_pb);
			else
				m_lcd->control_write(cur_pb);
		}
	}

	if(!(cur_pa & 0x08) && (data & 0x08)) {
		if(m_lcd)
			m_lcd->set_leds((cur_pb & 0x1f) | ((cur_pb & 0x80) >> 2));
		cur_ic34 = cur_pb;
	}

	cur_pa = data;
}

void mu90_state::mu90b(machine_config &config)
{
	H83002(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &mu90_state::mu90_map);
	m_maincpu->read_adc<0>().set(FUNC(mu90_state::adc_ar_r));
	m_maincpu->read_adc<1>().set_constant(0);
	m_maincpu->read_adc<2>().set(FUNC(mu90_state::adc_al_r));
	m_maincpu->read_adc<3>().set_constant(0);
	m_maincpu->read_adc<4>().set(FUNC(mu90_state::adc_midisw_r));
	m_maincpu->read_adc<5>().set_constant(0);
	m_maincpu->read_adc<6>().set(FUNC(mu90_state::adc_battery_r));
	m_maincpu->read_adc<7>().set_constant(0);
	m_maincpu->read_port6().set_constant(0);
	m_maincpu->write_porta().set(FUNC(mu90_state::pa_w));
	m_maincpu->read_portb().set(FUNC(mu90_state::pb_r));
	m_maincpu->write_portb().set(FUNC(mu90_state::pb_w));

	SPEAKER(config, "speaker", 2).front();

	SWP30(config, m_swp30);
	m_swp30->set_addrmap(AS_DATA, &mu90_state::swp30_map);
	m_swp30->add_route(0, "speaker", 1.0, 0);
	m_swp30->add_route(1, "speaker", 1.0, 1);

	auto &mdin_a(MIDI_PORT(config, "mdin_a"));
	midiin_slot(mdin_a);
	mdin_a.rxd_handler().set(m_maincpu, FUNC(h83002_device::sci_rx_w<1>));

	auto &mdin_b(MIDI_PORT(config, "mdin_b"));
	midiin_slot(mdin_b);
	mdin_b.rxd_handler().set(m_maincpu, FUNC(h83002_device::sci_rx_w<0>));

	auto &mdout(MIDI_PORT(config, "mdout"));
	midiout_slot(mdout);
	m_maincpu->write_sci_tx<0>().set(mdout, FUNC(midi_port_device::write_txd));
}

void mu90_state::mu90(machine_config &config)
{
	mu90b(config);

	MULCD(config, m_lcd);
}

#define ROM_LOAD16_WORD_SWAP_BIOS(bios,name,offset,length,hash) \
		ROMX_LOAD(name, offset, length, hash, ROM_GROUPWORD | ROM_REVERSE | ROM_BIOS(bios))

ROM_START( mu90 )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_DEFAULT_BIOS("v101")
	ROM_SYSTEM_BIOS( 0, "v101", "xs519d0 (v1.01, Nov. 27, 1996)" )
	ROM_LOAD16_WORD_SWAP_BIOS( 0, "xs519d0.ic9", 0x000000, 0x100000, CRC(6fc85b41) SHA1(05068eddcaf5be3a57f3a412a95c204849011b34) )

	ROM_REGION32_LE( 0x800000, "swp30", ROMREGION_ERASE00 )
	ROM_LOAD32_WORD( "xs518a0.ic22", 0x0000000, 0x400000, CRC(2550d44f) SHA1(fd3cce228c7d389a2fde25c808a5b26080588cba) )
	ROM_LOAD32_WORD( "xs743a0.ic23", 0x0000002, 0x400000, CRC(a9109a6c) SHA1(a67bb49378a38a2d809bd717d286e18bc6496db0) )
ROM_END

ROM_START( mu90b )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_DEFAULT_BIOS("v101")
	ROM_SYSTEM_BIOS( 0, "v101", "xt040c0 (v1.01, Dec. 26, 2005)" )
	ROM_LOAD16_WORD_SWAP_BIOS( 0, "xt040c0.ic9", 0x000000, 0x100000, CRC(66fe5896) SHA1(811a8c7f0e8aac7a8807922d5add0fbfc07e1cfd) )

	ROM_REGION32_LE( 0x800000, "swp30", ROMREGION_ERASE00 )
	ROM_LOAD32_WORD( "xs518a0.ic22", 0x0000000, 0x400000, CRC(2550d44f) SHA1(fd3cce228c7d389a2fde25c808a5b26080588cba) )
	ROM_LOAD32_WORD( "xs743a0.ic23", 0x0000002, 0x400000, CRC(a9109a6c) SHA1(a67bb49378a38a2d809bd717d286e18bc6496db0) )
ROM_END

} // anonymous namespace


SYST( 1996, mu90,     0, 0, mu90,  mu90, mu90_state, empty_init, "Yamaha", "MU90",  MACHINE_NOT_WORKING )
SYST( 2005, mu90b, mu90, 0, mu90b, mu90, mu90_state, empty_init, "Yamaha", "MU90B", MACHINE_NOT_WORKING )



ympcs30.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Devin Acker

/*

    Skeleton driver for Yamaha PortaSound PCS-30 keyboard with Playcard support

    CPU: Toshiba TMPZ84C00P (Z80 clone)
    Sound: Yamaha YM2142 (GE8)
    Other: Yamaha YM3514 (64 pins)

*/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "speaker.h"

namespace {

class pcs30_state : public driver_device
{
public:
	pcs30_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void pcs30(machine_config &config);

private:
	void pcs30_map(address_map &map) ATTR_COLD;

	required_device<z80_device> m_maincpu;
};


void pcs30_state::pcs30_map(address_map &map)
{
	map(0x0000, 0x7fff).rom();
	map(0x8000, 0x87ff).ram();
}


void pcs30_state::pcs30(machine_config &config)
{
	Z80(config, m_maincpu, 4'000'000); // TODO: verify clock
	m_maincpu->set_addrmap(AS_PROGRAM, &pcs30_state::pcs30_map);

	SPEAKER(config, "speaker").front_center();
}


INPUT_PORTS_START(pcs30)
INPUT_PORTS_END


ROM_START( pcs30 )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD( "pcs30.bin", 0x0000, 0x8000, CRC(d63bc611) SHA1(7ad9c200ec4ea586efe1e9e5b5ad9bc8ca03407d)) // YM2211-22712
ROM_END

} // anonymous namespace

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS        INIT         COMPANY   FULLNAME  FLAGS
SYST( 1984, pcs30,   0,      0,      pcs30,   pcs30,  pcs30_state, empty_init,  "Yamaha", "PCS-30", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



ymps400.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Devin Acker

/*

    Skeleton driver for Yamaha PortaSound PS-400 keyboard

    IC1: Yamaha YM1034 (KAP2) "key assigner"
    IC2: Yamaha YM1019 (GE4) sound chip
    IC16: Toshiba TMP80C39P-6
    IC18: Toshiba TMM2332P mask ROM

    Service manual/schematic: https://elektrotanya.com/yamaha_ps_400.pdf/download.html

*/

#include "emu.h"

#include "cpu/mcs48/mcs48.h"
#include "speaker.h"

namespace {

class ps400_state : public driver_device
{
public:
	ps400_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void ps400(machine_config &config);

private:
	void ps400_map(address_map &map) ATTR_COLD;

	required_device<i8039_device> m_maincpu;
};


void ps400_state::ps400_map(address_map &map)
{
	map(0x000, 0xfff).rom();
}


void ps400_state::ps400(machine_config &config)
{
	I8039(config, m_maincpu, 6'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &ps400_state::ps400_map);

	SPEAKER(config, "speaker").front_center();
}


INPUT_PORTS_START(ps400)
INPUT_PORTS_END


ROM_START( ps400 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "yamaha_igo83803a_3997-7802.ic18", 0x0000, 0x1000, CRC(251e6572) SHA1(cd1a0bf1cf10eb2cb3b6fbaf14faa62ccb682eb2))
ROM_END

} // anonymous namespace

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS        INIT         COMPANY   FULLNAME  FLAGS
SYST( 1982, ps400,   0,      0,      ps400,   ps400,  ps400_state, empty_init,  "Yamaha", "PS-400", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



ympsr11.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Carl

#include "emu.h"
#include "cpu/m6800/m6801.h"
#include "sound/ymopl.h"
#include "speaker.h"

#include "psr11.lh"

namespace {

class yamaha_psr11_state : public driver_device
{
public:
	yamaha_psr11_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ym3812(*this, "opl2")
		, m_keys(*this, "P%u", 0U)
		, m_tempo_led(*this, "led")
	{
	}

	void psr11(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void map(address_map &map) ATTR_COLD;

	u8 p2_r();

	required_device<hd6301y0_cpu_device> m_maincpu;
	required_device<ym3812_device> m_ym3812;
	required_ioport_array<17> m_keys;
	output_finder<> m_tempo_led;

	u8 m_p5 = 0, m_p6 = 0;
};

void yamaha_psr11_state::machine_start()
{
	m_tempo_led.resolve();

	save_item(NAME(m_p5));
	save_item(NAME(m_p6));
}

void yamaha_psr11_state::map(address_map &map)
{
	map(0x1ffe, 0x1fff).rw(m_ym3812, FUNC(ym3812_device::read), FUNC(ym3812_device::write)); // Only bits 12 and 15 are used to decode the address
}

u8 yamaha_psr11_state::p2_r()
{
	int i, p6 = m_p6 & 0x3f;

	if(p6 == 0x30)
	{
		if(!m_p5)
			return m_keys[16]->read();
		else
			return 0;
	}
	if(((p6 & 0x30) == 0x20) && (p6 & 0xf))
	{
		if(!m_p5)
		{
			for(i = 0; i < 4; i++)
			{
				if(p6 & (1 << i))
					break;
			}
			return m_keys[i + 12]->read();
		}
		else
			return 0;
	}
	for(i = 0; i < 8; i++)
	{
		if(m_p5 & (1 << i))
			break;
	}
	if((p6 == 0x20) && (i < 4))
		return m_keys[i + 8]->read();
	else if(!p6 && (i < 8))
		return m_keys[i]->read();

	return 0;
}

static INPUT_PORTS_START(psr11)
	PORT_START("P0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C 6")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B 5")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A#5")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A 5")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G#5")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G 5")
	PORT_BIT(0xC0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("P1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F#5")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F 5")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E 5")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D#5")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D 5")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C#5")
	PORT_BIT(0xC0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("P2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C 5")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B 4")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A#4")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A 4")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G#4")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G 4")
	PORT_BIT(0xC0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("P3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F#4")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F 4")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E 4")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D#4")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D 4")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C#4")
	PORT_BIT(0xC0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("P4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C 4")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B 3")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A#3")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A 3")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G#3")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G 3")
	PORT_BIT(0xC0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("P5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F#3")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F 3")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E 3")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D#3")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D 3")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C#3")
	PORT_BIT(0xC0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("P6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C 3")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B 2")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A#2")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A 2")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G#2")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G 2")
	PORT_BIT(0xC0, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("P7")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F#2")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F 2")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E 2")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D#2")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D 2")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C#2")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C 2")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("P8")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BOSSANOVA")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SAMBA")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("HEAVY METAL")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DISCO")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("WALTZ")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MARCH/POLKA")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("16 BEAT")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("POPS")

	PORT_START("P9")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SYNCHRO START")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("START")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("STOP")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SUSTAIN ON")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SUSTAIN OFF")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("VIBRATO ON")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("VIBRATO OFF")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P98")

	PORT_START("P10")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("PIANO")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BRASS 2")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("JAZZ ORGAN")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("COSMIC")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("HARPSICHORD")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("FLUTE")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("PIPE ORGAN")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("POPSYNTH")

	PORT_START("P11")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("JAZZ GUITAR")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CLARINET")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("STRINGS")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("FUNKSYNTH")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("VIBES")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MUSIC BOX")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BRASS 1")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("PERCUS")

	PORT_START("P12")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("AUTO BASS VOLUME MIN")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("AUTO BASS VOLUME 1")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("AUTO BASS VOLUME 2")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("AUTO BASS VOLUME 3")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("AUTO BASS VOLUME MAX")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P126")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("PITCH UP")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("PITCH DOWN")

	PORT_START("P13")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BASS BASS")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BASS E BASS")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BASS TUBA")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BASS SYNTH")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CHORD PIANO")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CHORD GUITAR")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CHORD BRASS")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CHORD SYNTH")

	PORT_START("P14")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MODE OFF")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SINGLE FINGER")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("FINGERED")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MANUAL BASS")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P145")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("FILL IN")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P147")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("DEMONSTRATION")

	PORT_START("P15")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RHYTHM VOLUME MIN")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RHYTHM VOLUME 1")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RHYTHM VOLUME 2")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RHYTHM VOLUME 3")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RHYTHM VOLUME MAX")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P156")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TEMPO UP")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("TEMPO DOWN")

	PORT_START("P16")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("COUNTRY")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ROCK N ROLL")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SWING")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BIG BAND")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RHUMBA")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SALSA")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SLOW ROCK")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("REGGAE")
INPUT_PORTS_END

void yamaha_psr11_state::psr11(machine_config &config)
{
	HD6301Y0(config, m_maincpu, 3.579545_MHz_XTAL); // Yamaha XA909A0
	m_maincpu->set_addrmap(AS_PROGRAM, &yamaha_psr11_state::map);
	m_maincpu->in_p2_cb().set(FUNC(yamaha_psr11_state::p2_r));
	m_maincpu->out_p5_cb().set([this](u8 d){ m_p5 = d; });
	m_maincpu->out_p6_cb().set([this](u8 d){ m_p6 = d; m_tempo_led = BIT(d, 6); }); // bit 7 is unconnected on the board

	SPEAKER(config, "mono").front_center();
	YM3812(config, m_ym3812, 3.579545_MHz_XTAL).add_route(ALL_OUTPUTS, "mono", 3.00);

	config.set_default_layout(layout_psr11);
}

ROM_START(psr11)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_LOAD("yamaha6301y0a72.ic1", 0x0000, 0x4000, CRC(83937c8a) SHA1(5e9263d010dddd2d90c4791f2260b5fc9ec50fd4))
ROM_END

} // anonymous namespace

SYST(1986, psr11, 0, 0, psr11, psr11, yamaha_psr11_state, empty_init, "Yamaha", "Portatone PSR-11", MACHINE_SUPPORTS_SAVE)



ympsr150.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Devin Acker

/*

    Driver for Yamaha YMW270-F (GEW7) and YMW282-F (GEW7S) keyboards

    A few different "families" are covered by this driver:

    - "Music Mode Selector" dial (1992-1993)
        - PSR-150: 61 keys, stereo, 4 drum pads + 4 sound effect pads
        - PSR-110: 49 keys, stereo, 4 drum/sound effect pads
        - PSR-75: 49 keys, mono, no pads
        - PSS-31: 49 mini keys, stereo, 4 drum pads
        - PSS-21: 37 mini keys, stereo, 4 drum pads
        - PSS-11: 32 mini keys, mono, no pads

    - Circular control panel w/ 7-segment display (1994)
        - PSR-180: 61 keys, stereo, 4 drum/sound effect pads
            later released as PSR-185 (1995)
        - PSR-76: 49 keys, mono, no pads
            later released as PSR-73/77 (1995), PSR-74 (1999), PSR-125 (2002)

    - Mini keyboards w/ tone variation button (1994)
        - PSS-12: 32 mini keys, mono, 2Mbit ROM
        - PSS-6: 32 "ultra mini" keys, mono, 1Mbit ROM, some tone differences

    - DD-9 Digital Percussion (1994), later released as DD-20 (2003)

    - LCD with large icons, metronome, volume display (1996)
        - PSR-190: 61 keys, stereo
        - PSR-78: 49 keys, mono

    Other known undumped models:
    - PSR-130 (1997, 61 keys, two dials for tone & rhythm selection)

*/

#include "emu.h"

#include "cpu/m6502/gew7.h"
#include "sound/flt_rc.h"
#include "video/hd44780.h"
#include "video/pwm.h"
#include "screen.h"
#include "speaker.h"

#include "dd9.lh"
#include "psr110.lh"
#include "psr150.lh"
#include "psr180.lh"
#include "psr75.lh"
#include "psr76.lh"
#include "pss11.lh"
#include "pss12.lh"
#include "pss21.lh"
#include "pss31.lh"
#include "pss6.lh"

namespace {

class psr150_state : public driver_device
{
public:
	psr150_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pwm(*this, "pwm")
		, m_lcdc(*this, "lcdc")
		, m_port(*this, "P%c", 'A')
		, m_keys(*this, "KEY%u", 0U)
		, m_dial(*this, "DIAL")
		, m_outputs(*this, "%02x.%d.%d", 0U, 0U, 0U)
		, m_switch(*this, "switch_pos")
		, m_led(*this, "led%u", 0U)
		, m_digit(*this, "digit%u", 0U)
	{ }

	void psr150(machine_config &config);
	void psr110(machine_config &config);
	void pss21(machine_config &config);
	void pss31(machine_config &config);
	void psr75(machine_config &config);
	void pss11(machine_config &config);
	void dd9(machine_config &config);
	void psr180_base(machine_config &config);
	void psr180(machine_config &config);
	void psr76(machine_config &config);
	void pss12(machine_config &config);
	void pss6(machine_config &config);
	void psr190_base(machine_config &config);
	void psr190(machine_config &config);
	void psr78(machine_config &config);

	template <offs_t Num, u8 PullUps = 0xff>
	void port_pullup_w(offs_t offset, u8 data, u8 mem_mask);

	// most of these keyboards have key matrix rows & columns split across multiple ports each,
	// so use templates to be able to generate multiple r/w methods as appropriate
	template <unsigned StartBit, unsigned Count>
	void keys_w(int state);
	template <unsigned StartBit>
	ioport_value keys_r();

	ioport_value dial_r();

	DECLARE_INPUT_CHANGED_MEMBER(switch_w);
	ioport_value switch_r() { return m_switch; }

	void apo_w(int state) { if (state) m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); }
	DECLARE_INPUT_CHANGED_MEMBER(power_w) { if (!newval) m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE); }

	template <unsigned Num>
	void led_w(int state) { m_led[Num] = (state ? 1 : 0); }

	void pwm_row_w(int state);
	template <unsigned Bit>
	void pwm_col_w(int state);

	template <unsigned Num>
	void digit_w(u8 state) { m_digit[Num] = state ^ 0xff; }

	ioport_value lcd_r() { return m_lcdc->db_r() >> 4; }
	void lcd_w(int state) { m_lcdc->db_w(state << 4); }

private:
	virtual void driver_start() override;

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	required_device<gew7_device> m_maincpu;
	optional_device<pwm_display_device> m_pwm;
	optional_device<ks0066_device> m_lcdc;

	optional_ioport_array<6> m_port;
	optional_ioport_array<19> m_keys;
	optional_ioport m_dial;
	output_finder<64, 8, 5> m_outputs;
	output_finder<> m_switch;
	output_finder<4> m_led;
	output_finder<2> m_digit;

	ioport_value m_key_sel{};
	ioport_value m_pwm_col{};
};

template <offs_t Num, u8 PullUps>
void psr150_state::port_pullup_w(offs_t offset, u8 data, u8 mem_mask)
{
	// these keyboards scan the buttons by setting one matrix row to output, the rest to input,
	// and relying on external pullup resistors to keep the 'input' rows deselected
	m_port[Num]->write((data & mem_mask) | (PullUps & ~mem_mask));
}

template <unsigned StartBit, unsigned Count>
void psr150_state::keys_w(int state)
{
	constexpr auto mask = make_bitmask<ioport_value>(Count);

	m_key_sel &= ~(mask << StartBit);
	m_key_sel |= (state & mask) << StartBit;
}

template <unsigned StartBit>
ioport_value psr150_state::keys_r()
{
	ioport_value result = 0;
	for (unsigned i = 0U; i < m_keys.size(); i++)
		if (BIT(m_key_sel, i))
			result |= m_keys[i].read_safe(0);

	return result >> StartBit;
}

ioport_value psr150_state::dial_r()
{
	// return the dial position as a 2-bit gray code
	const u8 val = m_dial->read();
	return (val >> 6) ^ (val >> 7);
}

INPUT_CHANGED_MEMBER(psr150_state::switch_w)
{
	if (!oldval && newval)
		m_switch = param;
}

void psr150_state::pwm_row_w(int state)
{
	m_pwm->matrix(state, m_pwm_col);
}

template <unsigned Bit>
void psr150_state::pwm_col_w(int state)
{
	keys_w<11 + Bit, 1>(state);
	m_pwm_col = m_key_sel >> 11;

	if (m_pwm->read_my() != 0)
		m_pwm->write_mx(m_pwm_col);
}

u32 psr150_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const u8* render = m_lcdc->render();
	for (int x = 0; x != 64; x++) {
		for (int y = 0; y != 8; y++) {
			u8 v = *render++;
			for (int z = 0; z != 5; z++)
				m_outputs[x][y][z] = (v >> z) & 1;
		}
		render += 8;
	}

	return 0;
}


void psr150_state::driver_start()
{
	m_outputs.resolve();
	m_led.resolve();
	m_digit.resolve();
	m_switch.resolve();

	m_switch = 0x2; // "Voice Play" mode

	save_item(NAME(m_key_sel));
	save_item(NAME(m_pwm_col));
}


void psr150_state::psr150(machine_config &config)
{
	GEW7(config, m_maincpu, 8_MHz_XTAL);
	m_maincpu->port_in_cb<0>().set_ioport("PA");
	m_maincpu->port_out_cb<0>().set_ioport("PA");
	m_maincpu->port_out_cb<1>().set_ioport("PB");
	m_maincpu->port_in_cb<2>().set_ioport("PC");
	m_maincpu->port_in_cb<5>().set_ioport("PF");
	m_maincpu->port_out_cb<5>().set(FUNC(psr150_state::port_pullup_w<5>));
	m_maincpu->add_route(0, "lfilter", 1.0);
	m_maincpu->add_route(1, "rfilter", 1.0);

	// set up AC filters since the keyboard purposely outputs a DC offset when idle
	// TODO: there is also a RLC lowpass with R=120, L=3.3mH, C=0.33uF (or R=150 for psr110)
	FILTER_RC(config, "lfilter").set_ac().add_route(ALL_OUTPUTS, "speaker", 1.0, 0);
	FILTER_RC(config, "rfilter").set_ac().add_route(ALL_OUTPUTS, "speaker", 1.0, 1);

	SPEAKER(config, "speaker", 2).front();

	config.set_default_layout(layout_psr150);
}

void psr150_state::psr110(machine_config &config)
{
	psr150(config);
	config.set_default_layout(layout_psr110);
}

void psr150_state::pss21(machine_config &config)
{
	psr150(config);
	// bits 6-7 indicate model (see also pss11, pss31, psr75)
	m_maincpu->port_force_bits(5, 0x40, 0xc0);

	config.set_default_layout(layout_pss21);
}

void psr150_state::pss31(machine_config &config)
{
	psr150(config);
	// bits 6-7 indicate model (see also pss11, pss21, psr75)
	m_maincpu->port_force_bits(5, 0x80, 0xc0);

	config.set_default_layout(layout_pss31);
}

void psr150_state::psr75(machine_config &config)
{
	GEW7(config, m_maincpu, 8'000'000);
	m_maincpu->port_in_cb<0>().set_ioport("PA");
	m_maincpu->port_out_cb<0>().set_ioport("PA");
	m_maincpu->port_out_cb<1>().set_ioport("PB");
	m_maincpu->port_in_cb<2>().set_ioport("PC");
	m_maincpu->port_in_cb<5>().set_ioport("PF");
	m_maincpu->port_out_cb<5>().set(FUNC(psr150_state::port_pullup_w<5>));
	// bits 6-7 indicate model (see also pss11, pss21, pss31)
	m_maincpu->port_force_bits(5, 0xc0, 0xc0);
	m_maincpu->add_route(0, "filter", 1.0);

	// set up AC filter since the keyboard purposely outputs a DC offset when idle
	// TODO: there is probably also a RLC lowpass, check schematic if available (also for pss21 and pss31)
	FILTER_RC(config, "filter").set_ac().add_route(ALL_OUTPUTS, "speaker", 1.0);

	SPEAKER(config, "speaker").front_center();

	config.set_default_layout(layout_psr75);
}

void psr150_state::pss11(machine_config &config)
{
	GEW7(config, m_maincpu, 8'000'000);
	m_maincpu->port_out_cb<0>().set_ioport("PA");
	m_maincpu->port_in_cb<1>().set_ioport("PB");
	m_maincpu->port_in_cb<2>().set_ioport("PC");
	m_maincpu->port_out_cb<2>().set(FUNC(psr150_state::port_pullup_w<2>));
	m_maincpu->port_in_cb<5>().set_ioport("PF");
	// bits 6-7 indicate model (see also pss21, pss31, psr75)
	m_maincpu->port_force_bits(5, 0x00, 0xc0);
	m_maincpu->add_route(0, "filter", 1.0);

	// set up AC filter since the keyboard purposely outputs a DC offset when idle
	// TODO: there is also a RLC lowpass with R=120, L=3.3mH, C=0.1uF
	FILTER_RC(config, "filter").set_ac().add_route(ALL_OUTPUTS, "speaker", 1.0);

	SPEAKER(config, "speaker").front_center();

	config.set_default_layout(layout_pss11);
}

void psr150_state::dd9(machine_config &config)
{
	GEW7(config, m_maincpu, 8'000'000);
	m_maincpu->port_out_cb<0>().set_ioport("PA");
	m_maincpu->port_out_cb<1>().set(FUNC(psr150_state::digit_w<0>));
	m_maincpu->port_out_cb<2>().set(FUNC(psr150_state::digit_w<1>));
	m_maincpu->port_in_cb<5>().set_ioport("PF");
	m_maincpu->add_route(1, "speaker", 1.0);

	// TODO: there is also a RLC lowpass with R=150, L=3.3mH, C=0.33uF
	// (AC filter not really needed since this doesn't output a "dummy" DC offset sample, unlike the keyboards)

	SPEAKER(config, "speaker").front_center();

	config.set_default_layout(layout_dd9);
}

void psr150_state::psr180_base(machine_config &config)
{
	GEW7(config, m_maincpu, 8'000'000);
	m_maincpu->port_out_cb<0>().set_ioport("PA");
	m_maincpu->port_in_cb<1>().set_ioport("PB");
	m_maincpu->port_out_cb<1>().set_ioport("PB");
	m_maincpu->port_in_cb<2>().set_ioport("PC");
	m_maincpu->port_out_cb<2>().set_ioport("PC");
	m_maincpu->port_in_cb<5>().set_ioport("PF");
	m_maincpu->port_out_cb<5>().set_ioport("PF");

	PWM_DISPLAY(config, m_pwm);
	m_pwm->set_size(3, 8);
	m_pwm->set_segmask(0x6, 0xff);
}

void psr150_state::psr180(machine_config &config)
{
	psr180_base(config);
	m_maincpu->add_route(0, "lfilter", 1.0);
	m_maincpu->add_route(1, "rfilter", 1.0);

	// set up AC filters since the keyboard purposely outputs a DC offset when idle
	// TODO: there is also a RLC lowpass with R=120, L=3.3mH, C=0.39uF
	FILTER_RC(config, "lfilter").set_ac().add_route(ALL_OUTPUTS, "speaker", 1.0, 0);
	FILTER_RC(config, "rfilter").set_ac().add_route(ALL_OUTPUTS, "speaker", 1.0, 1);

	SPEAKER(config, "speaker", 2).front();

	config.set_default_layout(layout_psr180);
}

void psr150_state::psr76(machine_config &config)
{
	psr180_base(config);
	m_maincpu->add_route(0, "filter", 1.0);

	// set up AC filter since the keyboard purposely outputs a DC offset when idle
	// TODO: there is also a RLC lowpass with R=120, L=1mH, C=0.33uF
	FILTER_RC(config, "filter").set_ac().add_route(ALL_OUTPUTS, "speaker", 1.0);

	SPEAKER(config, "speaker").front_center();

	config.set_default_layout(layout_psr76);
}

void psr150_state::pss12(machine_config &config)
{
	GEW7(config, m_maincpu, 8'000'000);
	m_maincpu->port_in_cb<0>().set_ioport("PA");
	m_maincpu->port_out_cb<1>().set_ioport("PB");
	m_maincpu->port_in_cb<2>().set_ioport("PC");
	m_maincpu->port_out_cb<2>().set(FUNC(psr150_state::port_pullup_w<2>));
	m_maincpu->port_out_cb<5>().set_ioport("PF");
	m_maincpu->add_route(1, "filter", 1.0);

	// set up AC filter since the keyboard purposely outputs a DC offset when idle
	// TODO: there is also a RLC lowpass with R=120, L=3.3mH, C=0.33uF
	FILTER_RC(config, "filter").set_ac().add_route(ALL_OUTPUTS, "speaker", 1.0);

	SPEAKER(config, "speaker").front_center();

	config.set_default_layout(layout_pss12);
}

void psr150_state::pss6(machine_config &config)
{
	GEW7(config, m_maincpu, 8'000'000);
	m_maincpu->port_out_cb<1>().set_ioport("PB");
	m_maincpu->port_out_cb<2>().set(FUNC(psr150_state::port_pullup_w<2>));
	m_maincpu->port_in_cb<5>().set_ioport("PF");
	m_maincpu->add_route(0, "filter", 1.0);

	// set up AC filter since the keyboard purposely outputs a DC offset when idle
	FILTER_RC(config, "filter").set_ac().add_route(ALL_OUTPUTS, "speaker", 1.0);

	SPEAKER(config, "speaker").front_center();

	config.set_default_layout(layout_pss6);
}

void psr150_state::psr190_base(machine_config &config)
{
	GEW7(config, m_maincpu, 8'000'000);
	m_maincpu->port_out_cb<0>().set_ioport("PA");
	m_maincpu->port_in_cb<1>().set_ioport("PB");
	m_maincpu->port_out_cb<1>().set_ioport("PB");
	m_maincpu->port_in_cb<2>().set_ioport("PC_R");
	m_maincpu->port_out_cb<2>().set_ioport("PC_W");

	KS0066(config, m_lcdc, 270'000); // OSC = 91K resistor, TODO: actually KS0076B-00
	m_lcdc->set_lcd_size(2, 8);

	screen_device& screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
	screen.set_refresh_hz(60);
	screen.set_size(1000, 775);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(psr150_state::screen_update));
}

void psr150_state::psr190(machine_config &config)
{
	psr190_base(config);
	m_maincpu->port_out_cb<5>().set(FUNC(psr150_state::port_pullup_w<5>));
	m_maincpu->add_route(0, "lfilter", 1.0);
	m_maincpu->add_route(1, "rfilter", 1.0);

	// set up AC filters since the keyboard purposely outputs a DC offset when idle
	// TODO: there is also a RLC lowpass with R=120, L=3.3mH, C=0.33uF
	FILTER_RC(config, "lfilter").set_ac().add_route(ALL_OUTPUTS, "speaker", 1.0, 0);
	FILTER_RC(config, "rfilter").set_ac().add_route(ALL_OUTPUTS, "speaker", 1.0, 1);

	SPEAKER(config, "speaker", 2).front();
}

void psr150_state::psr78(machine_config &config)
{
	psr190_base(config);
	// pull up the button select bits, but not the LCD enable bit
	m_maincpu->port_out_cb<5>().set(&psr150_state::port_pullup_w<5, 0x1f>, "port_pullup_w");
	m_maincpu->add_route(0, "filter", 1.0);

	// set up AC filter since the keyboard purposely outputs a DC offset when idle
	// TODO: there is also a RLC lowpass with R=120, L=1mH, C=0.33uF
	FILTER_RC(config, "filter").set_ac().add_route(ALL_OUTPUTS, "speaker", 1.0);

	SPEAKER(config, "speaker").front_center();
}

// helper to use keys_w with PORT_WRITE_LINE_MEMBER
#define KEY_OUT_BITS(start, count) NAME((&psr150_state::keys_w<start, count>))

INPUT_PORTS_START(psr150)
	PORT_START("PA")
	PORT_BIT( 0x3f, IP_ACTIVE_LOW,  IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::keys_r<0>))
	PORT_BIT( 0xc0, IP_ACTIVE_LOW,  IPT_OUTPUT)  PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(8, 2))

	PORT_START("PB")
	PORT_BIT( 0xff, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(0, 8))

	PORT_START("PC")
	PORT_BIT( 0x03, IP_ACTIVE_LOW,  IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::keys_r<6>))
	PORT_BIT( 0x1c, IP_ACTIVE_LOW,  IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::switch_r))
	PORT_BIT( 0xe0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("PF")
	PORT_BIT( 0x0f, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(11, 4))
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(10, 1))
	PORT_BIT( 0xc0, IP_ACTIVE_LOW,  IPT_UNUSED )

	PORT_START("SWITCH")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Mode (Song)")  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psr150_state::switch_w), 0x1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Mode (Voice)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psr150_state::switch_w), 0x2)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Mode (Style)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psr150_state::switch_w), 0x4)

	PORT_START("KEY0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C6
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G5
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B5
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS5
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS5
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F4
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E4
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS4
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D4
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G3
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS3
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A3
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS3
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C5
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B4
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS4
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A4
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS4
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS1
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F1
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E1
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS1
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D1
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B1
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS1
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A1
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS1
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F2
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E2
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS2
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D2
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS3
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E3
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS3
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D3
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS5
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS5
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F5
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E5
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D5
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY9")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C1
	PORT_BIT( 0xfe, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY10")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B2
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS2
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A2
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS2
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY11")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("SE Pad 1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_NAME("SE Pad -")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Demo")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Keypad 6")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Keypad 5")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("SE Pad 2")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Keypad 8")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("Keypad 9")

	PORT_START("KEY12")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("SE Pad 3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_NAME("SE Pad +")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SPACE) PORT_NAME("Start / Stop")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("Keypad 0")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Keypad 1")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_NAME("SE Pad 4")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Keypad 2")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Keypad 3")

	PORT_START("KEY13")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_NAME("Drum Pad 1")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("Drum Pad -")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("Keypad -")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("Keypad +")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_NAME("Drum Pad 2")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Keypad 7")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Keypad 4")

	PORT_START("KEY14")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_NAME("Drum Pad 3")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("Drum Pad +")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_UP) PORT_NAME("Volume Up")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Volume Down")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_NAME("Drum Pad 4")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Tempo Up")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Tempo Down")
INPUT_PORTS_END

INPUT_PORTS_START(psr110)
	PORT_START("PA")
	PORT_BIT( 0x3f, IP_ACTIVE_LOW,  IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::keys_r<0>))
	PORT_BIT( 0xc0, IP_ACTIVE_LOW,  IPT_OUTPUT)  PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(7, 2))

	PORT_START("PB")
	PORT_BIT( 0x7f, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(0, 7))
	PORT_BIT( 0x80, IP_ACTIVE_LOW,  IPT_UNUSED )

	PORT_START("PC")
	PORT_BIT( 0x1f, IP_ACTIVE_LOW,  IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::keys_r<6>))
	PORT_BIT( 0xe0, IP_ACTIVE_LOW,  IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::switch_r))

	PORT_START("PF")
	PORT_BIT( 0x3f, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(9, 6))
	PORT_BIT( 0xc0, IP_ACTIVE_LOW,  IPT_UNUSED )

	PORT_START("SWITCH")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Mode (Style)")  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psr150_state::switch_w), 0x1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Mode (Voice)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psr150_state::switch_w), 0x2)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Mode (Song)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psr150_state::switch_w), 0x4)

	PORT_START("KEY0")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G3
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS3
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A3
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS3
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B3
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C4
	PORT_BIT( 0x7c0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY1")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS3
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D3
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS3
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E3
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F3
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS3
	PORT_BIT( 0x7c0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY2")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G2
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS2
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A2
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS2
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B2
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C3
	PORT_BIT( 0x7c0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY3")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS2
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D2
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS2
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E2
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F2
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS2
	PORT_BIT( 0x7c0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY4")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G1
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS1
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A1
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS1
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B1
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C2
	PORT_BIT( 0x7c0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY5")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS1
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D1
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS1
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E1
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F1
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS1
	PORT_BIT( 0x7c0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY6")
	PORT_BIT( 0x01f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C1
	PORT_BIT( 0x7c0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY7")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G4
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS4
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A4
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS4
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B4
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C5
	PORT_BIT( 0x7c0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY8")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS4
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D4
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS4
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E4
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F4
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS4
	PORT_BIT( 0x7c0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY9")
	PORT_BIT( 0x07f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Tempo Down")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Tempo Up")
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Volume Down")
	PORT_BIT( 0x400, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("Keypad -")

	PORT_START("KEY10")
	PORT_BIT( 0x03f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Keypad 8")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Keypad 5")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Keypad 7")
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Keypad 4")
	PORT_BIT( 0x400, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("Keypad +")

	PORT_START("KEY11")
	PORT_BIT( 0x07f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Keypad 3")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Keypad 2")
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Keypad 1")
	PORT_BIT( 0x400, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("Keypad 0")

	PORT_START("KEY12")
	PORT_BIT( 0x03f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Demo")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SPACE) PORT_NAME("Start / Stop")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_UP) PORT_NAME("Volume Up")
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("Keypad 9")
	PORT_BIT( 0x400, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Keypad 6")

	PORT_START("KEY13")
	PORT_BIT( 0x03f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("Drum/SE Pad +")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("Drum/SE Pad -")
	PORT_BIT( 0x700, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY14")
	PORT_BIT( 0x03f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_NAME("Drum/SE Pad 3")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_NAME("Drum/SE Pad 2")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_NAME("Drum/SE Pad 1")
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_NAME("Drum/SE Pad 4")
	PORT_BIT( 0x400, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

INPUT_PORTS_START(psr75)
	PORT_START("PA")
	PORT_BIT( 0x3f, IP_ACTIVE_LOW,  IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::keys_r<0>))
	PORT_BIT( 0x40, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(8, 1))
	PORT_BIT( 0x80, IP_ACTIVE_LOW,  IPT_UNUSED )

	PORT_START("PB")
	PORT_BIT( 0xff, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(0, 8))

	PORT_START("PC")
	PORT_BIT( 0x03, IP_ACTIVE_LOW,  IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::keys_r<6>))
	PORT_BIT( 0x1c, IP_ACTIVE_LOW,  IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::switch_r))
	PORT_BIT( 0xe0, IP_ACTIVE_LOW,  IPT_UNUSED )

	PORT_START("PF")
	PORT_BIT( 0x0f, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(9, 4))
	PORT_BIT( 0xf0, IP_ACTIVE_LOW,  IPT_UNUSED )

	PORT_START("SWITCH")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Mode (Song)")  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psr150_state::switch_w), 0x1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Mode (Voice)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psr150_state::switch_w), 0x2)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Mode (Style)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psr150_state::switch_w), 0x4)

	PORT_START("KEY0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_C1
	PORT_BIT( 0xfe, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_FS1
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_F1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_E1
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_DS1
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_D1
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_CS1
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_C2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_B1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_AS1
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_A1
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_GS1
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_G1
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_FS2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_F2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_E2
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_DS2
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_D2
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_CS2
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_C3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_B2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_AS2
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_A2
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_GS2
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_G2
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_FS3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_F3
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_E3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_DS3
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_D3
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_CS3
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_C4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_B3
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_AS3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_A3
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_GS3
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_G3
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_FS4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_F4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_E4
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_DS4
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_D4
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_CS4
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_C5
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_B4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_AS4
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_A4
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_GS4
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_G4
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY9")
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Demo")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Keypad 6")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Keypad 5")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Keypad 8")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("Keypad 9")

	PORT_START("KEY10")
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SPACE) PORT_NAME("Start/Stop")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("Keypad 0")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Keypad 1")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Keypad 2")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Keypad 3")

	PORT_START("KEY11")
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("Keypad -")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD)  PORT_NAME("Keypad +")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Keypad 7")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Keypad 4")

	PORT_START("KEY12")
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_UP)   PORT_NAME("Volume Up")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Volume Down")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Tempo Up")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_LEFT)  PORT_NAME("Tempo Down")
INPUT_PORTS_END

INPUT_PORTS_START(pss31)
	PORT_INCLUDE(psr110)

	PORT_MODIFY("KEY13")
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("Drum Pad +")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("Drum Pad -")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_NAME("Drum Pad 4")

	PORT_MODIFY("KEY14")
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_NAME("Drum Pad 3")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_NAME("Drum Pad 2")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_NAME("Drum Pad 1")
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

INPUT_PORTS_START(pss21)
	PORT_INCLUDE(pss31)

	PORT_MODIFY("PA") // in & out bits are swapped
	PORT_BIT( 0x03, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(7, 2))
	PORT_BIT( 0xfc, IP_ACTIVE_LOW,  IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::keys_r<0>))

	PORT_MODIFY("KEY7")
	PORT_BIT( 0x7ff, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_MODIFY("KEY8")
	PORT_BIT( 0x7ff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

INPUT_PORTS_START(pss11)
	PORT_START("PA")
	PORT_BIT( 0x3f, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(0, 6))
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("PB")
	PORT_BIT( 0x3f, IP_ACTIVE_LOW,  IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::keys_r<0>))
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("PC")
	PORT_BIT( 0x0f, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(6, 4))
	PORT_BIT( 0x70, IP_ACTIVE_LOW,  IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::switch_r))
	PORT_BIT( 0x80, IP_ACTIVE_LOW,  IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::keys_r<6>))

	PORT_START("PF")
	PORT_BIT( 0x0f, IP_ACTIVE_LOW,  IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::keys_r<7>))
	PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED ) // bits 6-7 indicate model

	PORT_START("SWITCH")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Mode (Style)")  PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psr150_state::switch_w), 0x1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Mode (Voice)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psr150_state::switch_w), 0x2)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_NAME("Mode (Song)") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psr150_state::switch_w), 0x4)

	PORT_START("KEY0")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C5
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B4
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS4
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A4
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS4
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G4
	PORT_BIT( 0x7c0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY1")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS4
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F4
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E4
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS4
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D4
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS4
	PORT_BIT( 0x7c0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY2")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C4
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B3
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS3
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A3
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS3
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G3
	PORT_BIT( 0x7c0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY3")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS3
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F3
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E3
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS3
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D3
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS3
	PORT_BIT( 0x7c0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY4")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C3
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B2
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS2
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A2
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS2
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G2
	PORT_BIT( 0x7c0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY5")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS2
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F2
	PORT_BIT( 0x7fc, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY6")
	PORT_BIT( 0x03f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Keypad 6")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("Keypad 9")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_UP) PORT_NAME("Volume Up")
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SPACE) PORT_NAME("Start / Stop")
	PORT_BIT( 0x400, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Demo")

	PORT_START("KEY7")
	PORT_BIT( 0x03f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("Keypad 0")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Keypad 1")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Keypad 2")
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Keypad 3")
	PORT_BIT( 0x400, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY8")
	PORT_BIT( 0x03f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("Keypad +")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Keypad 4")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Keypad 7")
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Keypad 5")
	PORT_BIT( 0x400, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Keypad 8")

	PORT_START("KEY9")
	PORT_BIT( 0x03f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("Keypad -")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Volume Down")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Tempo Up")
	PORT_BIT( 0x200, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Tempo Down")
	PORT_BIT( 0x400, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

INPUT_PORTS_START( dd9 )
	PORT_START("PA")
	PORT_BIT( 0x1f, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(0, 5))
	PORT_BIT( 0xe0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("PF")
	PORT_BIT( 0x01, IP_ACTIVE_LOW,  IPT_BUTTON1 ) PORT_NAME("Drum Pad 1")
	PORT_BIT( 0x02, IP_ACTIVE_LOW,  IPT_BUTTON2 ) PORT_NAME("Drum Pad 2")
	PORT_BIT( 0x04, IP_ACTIVE_LOW,  IPT_BUTTON3 ) PORT_NAME("Drum Pad 3")
	PORT_BIT( 0x08, IP_ACTIVE_LOW,  IPT_BUTTON4 ) PORT_NAME("Drum Pad 4")
	PORT_BIT( 0xf0, IP_ACTIVE_LOW,  IPT_CUSTOM  ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::keys_r<0>))

	PORT_START("KEY0")
	PORT_BIT( 0x1, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("Style")
	PORT_BIT( 0x2, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("Perc. Set")
	PORT_BIT( 0x4, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("Tempo")
	PORT_BIT( 0x8, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_NAME("Start/Stop")

	PORT_START("KEY1")
	PORT_BIT( 0x1, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_NAME("Demo")
	PORT_BIT( 0x2, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_NAME("Tap Start")
	PORT_BIT( 0x4, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_NAME("SE Select")
	PORT_BIT( 0x8, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_NAME("Auto Roll")

	PORT_START("KEY2")
	PORT_BIT( 0x1, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_UP) PORT_NAME("Volume Up")
	PORT_BIT( 0x2, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Volume Down")
	PORT_BIT( 0x4, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_NAME("Pad Assign +")
	PORT_BIT( 0x8, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_NAME("Pad Assign -")

	PORT_START("KEY3")
	PORT_BIT( 0x1, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("SE Pad 1")
	PORT_BIT( 0x2, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("SE Pad 2")
	PORT_BIT( 0x4, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_NAME("SE Pad 3")
	PORT_BIT( 0x8, IP_ACTIVE_HIGH, IPT_BUTTON8 ) PORT_NAME("SE Pad 4")

	PORT_START("KEY4")
	PORT_BIT( 0x3, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::dial_r))
	PORT_BIT( 0xc, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("DIAL")
	PORT_BIT( 0xff, 0x00, IPT_DIAL ) PORT_SENSITIVITY(50) PORT_KEYDELTA(75)
INPUT_PORTS_END

INPUT_PORTS_START( pss12 )
	PORT_START("PA")
	PORT_BIT( 0x3f, IP_ACTIVE_LOW,  IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::keys_r<0>))
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("PB")
	PORT_BIT( 0x3f, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(0, 6))
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("PC")
	PORT_BIT( 0x1f, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(6, 5))
	PORT_BIT( 0xe0, IP_ACTIVE_LOW,  IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::keys_r<6>))

	PORT_START("PF") // LEDs use two bits each and are numbered highest to lowest
	PORT_BIT( 0x03, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::led_w<3>))
	PORT_BIT( 0x0c, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::led_w<2>))
	PORT_BIT( 0x30, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::led_w<1>))
	PORT_BIT( 0xc0, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::led_w<0>))

	PORT_START("KEY0")
	PORT_BIT( 0x00f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F2
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS2
	PORT_BIT( 0x1c0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY1")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G2
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS2
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A2
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS2
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B2
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C3
	PORT_BIT( 0x1c0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY2")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS3
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D3
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS3
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E3
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F3
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS3
	PORT_BIT( 0x1c0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY3")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G3
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS3
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A3
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS3
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B3
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C4
	PORT_BIT( 0x1c0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY4")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_CS4
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_D4
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_DS4
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_E4
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_F4
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_FS4
	PORT_BIT( 0x1c0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY5")
	PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_G4
	PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_GS3
	PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_A4
	PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_AS4
	PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_B4
	PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_OTHER )  PORT_GM_C5
	PORT_BIT( 0x1c0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY6")
	PORT_BIT( 0x03f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1)     PORT_NAME("Voice/Song 1")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8)     PORT_NAME("One Touch Setting")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Tempo Up")

	PORT_START("KEY7")
	PORT_BIT( 0x03f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2)  PORT_NAME("Voice/Song 2")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9)  PORT_NAME("Minus One")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_UP) PORT_NAME("Volume Up")

	PORT_START("KEY8")
	PORT_BIT( 0x03f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3)    PORT_NAME("Voice/Song 3")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7)    PORT_NAME("Mode Select")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Tempo Down")

	PORT_START("KEY9")
	PORT_BIT( 0x03f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4)    PORT_NAME("Voice/Song 4")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0)    PORT_NAME("Start / Stop")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Volume Down")

	PORT_START("KEY10")
	PORT_BIT( 0x03f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_NAME("Voice/Song 5")
	PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_NAME("Voice/Song 6 / Variation")
	PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

INPUT_PORTS_START( pss6 )
	PORT_START("PB") // LEDs use two bits each
	PORT_BIT( 0x03, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::led_w<0>))
	PORT_BIT( 0x0c, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::led_w<1>))
	PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("PC")
	PORT_BIT( 0xff, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(0, 8))

	PORT_START("PF")
	PORT_BIT( 0x3f, IP_ACTIVE_LOW,  IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::keys_r<0>))
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_C5
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_E4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_GS3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_DS3
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_AS2
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_F2
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_B4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_DS4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_G3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_D3
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_A2
	PORT_BIT( 0xe0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_AS4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_D4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_FS3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_CS3
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_GS2
	PORT_BIT( 0xe0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_A4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_CS4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_F3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_C3
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_G2
	PORT_BIT( 0xe0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_GS4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_C4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_E3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_B2
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_FS2
	PORT_BIT( 0xe0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_G4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_B3
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_NAME("Voice/Song 6 / Variation")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("Voice/Song 2")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_NAME("Start / Stop")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_NAME("Tempo")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_FS4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_AS3
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_NAME("Voice/Song 5")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("Voice/Song 3")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_NAME("Mode Select")
	PORT_BIT( 0xe0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_F4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_A3
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4)     PORT_NAME("Voice/Song 4")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1)     PORT_NAME("Voice/Song 1")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8)     PORT_NAME("Minus One")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("Volume")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

INPUT_PORTS_START(psr180_keys) // also psr190
	PORT_START("PA")
	PORT_BIT( 0xff, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(0, 8))

	PORT_START("PB")
	PORT_BIT( 0x3f, IP_ACTIVE_LOW,  IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::keys_r<0>))
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("PF")
	PORT_BIT( 0x1f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0xe0, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(8, 3))

	PORT_START("KEY0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_B2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_G2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_C3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_AS2
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_A2
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_GS2
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY1")
	PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_C1
	PORT_BIT( 0xf8, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_F2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_CS2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_FS2
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_E2
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_DS2
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_D2
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_B1
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_G1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_C2
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_AS1
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_A1
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_GS1
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY4")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_F1
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_CS1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_FS1
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_E1
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_DS1
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_D1
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_F4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_CS4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_FS4
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_E4
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_DS4
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_D4
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_F3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_CS3
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_FS3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_E3
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_DS3
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_D3
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_B3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_G3
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_C4
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_AS3
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_A3
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_GS3
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_B4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_G4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_C5
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_AS4
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_A4
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_GS4
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY9")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_F5
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_CS5
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_FS5
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_E5
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_DS5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_D5
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY10")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_B5
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_G5
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_C6
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_AS5
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_A5
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_GS5
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

INPUT_PORTS_START(psr76_keys) // also psr78
	PORT_START("PA")
	PORT_BIT( 0xff, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(0, 8))

	PORT_START("PB")
	PORT_BIT( 0x01, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(8, 1))
	PORT_BIT( 0x7e, IP_ACTIVE_LOW,  IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::keys_r<0>))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_CS3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_D3
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_DS3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_E3
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_F3
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_FS3
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_G4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_GS4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_A4
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_AS4
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_B4
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_C5
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY2")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_G2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_GS2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_A2
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_AS2
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_B2
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_C3
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY3")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_CS2
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_D2
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_DS2
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_E2
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_F2
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_FS2
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY4")
	PORT_BIT( 0x1f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_C1
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY5")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_G3
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_GS3
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_A3
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_AS3
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_B3
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_C4
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY6")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_CS1
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_D1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_DS1
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_E1
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_F1
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_FS1
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY7")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_CS4
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_D4
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_DS4
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_E4
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_F4
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_FS4
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY8")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_G1
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_GS1
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_A1
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_AS1
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_B1
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER  ) PORT_GM_C2
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

INPUT_PORTS_START(psr180)
	PORT_INCLUDE(psr180_keys)

	PORT_START("PC")
	PORT_BIT( 0x01, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::apo_w))
	PORT_BIT( 0x02, IP_ACTIVE_LOW,  IPT_POWER_ON ) PORT_NAME("Power") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psr150_state::power_w), 0)
	PORT_BIT( 0x1c, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::pwm_row_w))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::pwm_col_w<7>)) // H
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::pwm_col_w<3>)) // D
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::pwm_col_w<4>)) // E

	PORT_MODIFY("PF")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::pwm_col_w<1>)) // B
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::pwm_col_w<0>)) // A
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::pwm_col_w<5>)) // F
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::pwm_col_w<6>)) // G
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::pwm_col_w<2>)) // C

	PORT_START("KEY11")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Drum")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_NAME("Drum/SE Pad 3")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY12")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("Sound Effect")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("Drum/SE Pad 4")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY13")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("Keypad -")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("Voice")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Keypad 7")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Keypad 4")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Keypad 1")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY14")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("Keypad +")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Tempo Down")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("Keypad 9")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Keypad 6")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Keypad 3")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY15")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_NAME("One Touch Setting")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Tempo Up")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_NAME("Start / Stop")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_NAME("Minus One")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("Song")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY16")
	PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_NAME("Sync Start / Fill In")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_NAME("Drum/SE Pad 2")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY17")
	PORT_BIT( 0x1f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_NAME("Drum/SE Pad 1")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY18")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("Keypad 0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("Style")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Keypad 8")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Keypad 5")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Keypad 2")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

INPUT_PORTS_START(psr76)
	PORT_INCLUDE(psr76_keys)

	PORT_START("PC")
	PORT_BIT( 0x01, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::apo_w))
	PORT_BIT( 0x02, IP_ACTIVE_LOW,  IPT_POWER_ON ) PORT_NAME("Power") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psr150_state::power_w), 0)
	PORT_BIT( 0x1c, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::pwm_row_w))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::pwm_col_w<6>)) // G
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::pwm_col_w<5>)) // F
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::pwm_col_w<0>)) // A

	PORT_START("PF")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::pwm_col_w<1>)) // B
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::pwm_col_w<4>)) // E
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::pwm_col_w<3>)) // D
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::pwm_col_w<7>)) // H
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::pwm_col_w<2>)) // C
	PORT_BIT( 0xe0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY12")
	PORT_BIT( 0x1f, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_NAME("Sync Start / Fill In")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY13")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Keypad 1")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Keypad 4")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Keypad 7")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("Keypad -")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("Voice")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY14")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Keypad 3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Keypad 6")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("Keypad 9")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("Keypad +")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Tempo Down")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY15")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("Song / Demo")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_NAME("Minus One")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_NAME("Start / Stop")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_NAME("One Touch Setting")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Tempo Up")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY18")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Keypad 2")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Keypad 5")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Keypad 8")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("Keypad 0")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("Style")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

INPUT_PORTS_START(psr190)
	PORT_INCLUDE(psr180_keys)

	PORT_MODIFY("PB")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::e_w))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("PC_R")
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x78, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::lcd_r))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("PC_W")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::rs_w))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::rw_w))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x78, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::lcd_w))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_MODIFY("PF")
	PORT_BIT( 0x1f, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(11, 5))

	PORT_START("KEY11")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("Sync Start")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_NAME("Main / Auto Fill B")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Tempo Up")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("Demo")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Keypad 6")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("Keypad +")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY12")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("Start / Stop")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_NAME("Ending")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_NAME("One Touch Setting")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("Voice")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("Keypad -")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("Keypad 0")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY13")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_NAME("Intro")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_NAME("Jam Track")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("Style")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Acc. Volume Down")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Keypad 5")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("Keypad 9")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY14")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_UP) PORT_NAME("Acc. Volume Up")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_NAME("Large/Small / Minus One")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Song")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Keypad 1")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Keypad 4")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Keypad 8")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY15")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_NAME("Main / Auto Fill A")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Tempo Down")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Keypad 2")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Keypad 3")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Keypad 7")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

INPUT_PORTS_START(psr78)
	PORT_INCLUDE(psr76_keys)

	PORT_START("PC_R")
	PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x78, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(psr150_state::lcd_r))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("PC_W")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::rs_w))
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::rw_w))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x78, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(psr150_state::lcd_w))
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("PF")
	PORT_BIT( 0x1f, IP_ACTIVE_LOW,  IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(KEY_OUT_BITS(9, 5))
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::e_w))
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY9")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("Keypad +")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Keypad 6")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("Demo")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("Sync Start")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_NAME("Main / Auto Fill B")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Tempo Up")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY10")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("Keypad 0")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("Keypad -")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("Voice")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("Start / Stop")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_NAME("Ending")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_NAME("One Touch Setting")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY11")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("Keypad 9")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Keypad 5")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Acc. Volume Down")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_NAME("Intro")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_NAME("Jam Track")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("Style")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY12")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("Keypad 8")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Keypad 4")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("Keypad 1")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_UP) PORT_NAME("Acc. Volume Up")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_NAME("Large/Small / Minus One")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Song")
	PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("KEY13")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("Keypad 7")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Keypad 3")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Keypad 2")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_NAME("Main / Auto Fill A")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Tempo Down")
	PORT_BIT( 0xe0, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END

#undef KEY_OUT_BITS


ROM_START( psr150 )
	ROM_REGION( 0x80000, "maincpu", 0 )
	ROM_LOAD( "xl561a00.ic4", 0x00000, 0x80000, CRC(ccb11ccf) SHA1(d4c00e4ee07e4bbe919e3cbeaf397ae1601533e3))
ROM_END

ROM_START( psr110 )
	ROM_REGION( 0x80000, "maincpu", 0 )
	ROM_LOAD( "xm041a00.ic2", 0x00000, 0x80000, CRC(b2db9672) SHA1(dee46ca980f88a4f06fd88437e39c9a6b8cec7bf))
ROM_END

ROM_START( psr75 )
	ROM_REGION( 0x40000, "maincpu", 0 )
	ROM_LOAD( "xl437a00.ic4", 0x00000, 0x40000, CRC(75fba08f) SHA1(ad79cdc7026ed621004e8f600d61ee62c598fa1a))
ROM_END

#define rom_pss11 rom_psr75
#define rom_pss21 rom_psr75
#define rom_pss31 rom_psr75

ROM_START( dd9 )
	ROM_REGION( 0x40000, "maincpu", 0 )
	ROM_LOAD( "xp170b00.ic2", 0x00000, 0x40000, CRC(66f09612) SHA1(7b106dc3717992c5b1a96bd5b27417bd98b38f7f))
ROM_END

ROM_START( psr180 )
	ROM_REGION( 0x80000, "maincpu", 0 )
	ROM_LOAD( "xp687a00.ic6", 0x00000, 0x80000, CRC(df3a568d) SHA1(ddc260d55d874987950817817117df141668f1f2))
ROM_END

ROM_START( psr76 )
	ROM_REGION( 0x80000, "maincpu", 0 )
	ROM_LOAD( "xp686a00.ic6", 0x00000, 0x80000, CRC(0d5299fc) SHA1(744e63edc1089cb2ece2105fd53993a66c6dc2ab))
ROM_END

ROM_START( pss12 )
	ROM_REGION( 0x40000, "maincpu", 0 )
	ROM_LOAD( "xp598a00.ic2", 0x00000, 0x40000, CRC(7e05f1cb) SHA1(1a05996002bb7bfdde215349d235269795c88693))
ROM_END

ROM_START( pss6 )
	ROM_REGION( 0x20000, "maincpu", 0 )
	ROM_LOAD( "xp733a00.ic2", 0x00000, 0x20000, CRC(5a7ad160) SHA1(01e7b988db37a2553c71d51f585736df286a245c))
ROM_END

ROM_START( psr190 )
	ROM_REGION( 0x80000, "maincpu", 0 )
	ROM_LOAD( "xr814100.ic6", 0x00000, 0x80000, CRC(91743a1d) SHA1(7124d4a53667e41d03c3bc4ac08f991886bc5e42))

	ROM_REGION( 175935, "screen", 0 )
	ROM_LOAD( "psr190.svg", 0, 175935, CRC(1382c3df) SHA1(504ca22300f220785d1d7476e39cf2077684bb70))
ROM_END

ROM_START( psr78 )
	ROM_REGION( 0x80000, "maincpu", 0 )
	ROM_LOAD( "xr813100.ic6", 0x00000, 0x80000, CRC(cf1b959c) SHA1(6459a322789285ab203bbbd29ddfe0d877514b41))

	ROM_REGION( 175935, "screen", 0 )
	ROM_LOAD( "psr190.svg", 0, 175935, CRC(1382c3df) SHA1(504ca22300f220785d1d7476e39cf2077684bb70))
ROM_END

} // anonymous namespace

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS          INIT         COMPANY   FULLNAME   FLAGS
SYST( 1992, psr150,  0,      0,      psr150,  psr150, psr150_state,  empty_init,  "Yamaha", "PSR-150", MACHINE_SUPPORTS_SAVE )
SYST( 1993, psr110,  psr150, 0,      psr110,  psr110, psr150_state,  empty_init,  "Yamaha", "PSR-110", MACHINE_SUPPORTS_SAVE )
SYST( 1992, psr75,   psr150, 0,      psr75,   psr75,  psr150_state,  empty_init,  "Yamaha", "PSR-75",  MACHINE_SUPPORTS_SAVE )
SYST( 1992, pss11,   psr150, 0,      pss11,   pss11,  psr150_state,  empty_init,  "Yamaha", "PSS-11",  MACHINE_SUPPORTS_SAVE )
SYST( 1992, pss21,   psr150, 0,      pss21,   pss21,  psr150_state,  empty_init,  "Yamaha", "PSS-21",  MACHINE_SUPPORTS_SAVE )
SYST( 1992, pss31,   psr150, 0,      pss31,   pss31,  psr150_state,  empty_init,  "Yamaha", "PSS-31",  MACHINE_SUPPORTS_SAVE )
SYST( 1994, dd9,     0,      0,      dd9,     dd9,    psr150_state,  empty_init,  "Yamaha", "DD-9 Digital Percussion", MACHINE_SUPPORTS_SAVE )
SYST( 1994, psr180,  0,      0,      psr180,  psr180, psr150_state,  empty_init,  "Yamaha", "PSR-180", MACHINE_SUPPORTS_SAVE )
SYST( 1994, psr76,   psr180, 0,      psr76,   psr76,  psr150_state,  empty_init,  "Yamaha", "PSR-76",  MACHINE_SUPPORTS_SAVE )
SYST( 1994, pss12,   0,      0,      pss12,   pss12,  psr150_state,  empty_init,  "Yamaha", "PSS-12",  MACHINE_SUPPORTS_SAVE )
SYST( 1994, pss6,    pss12,  0,      pss6,    pss6,   psr150_state,  empty_init,  "Yamaha", "PSS-6",   MACHINE_SUPPORTS_SAVE )
SYST( 1996, psr190,  0,      0,      psr190,  psr190, psr150_state,  empty_init,  "Yamaha", "PSR-190", MACHINE_SUPPORTS_SAVE )
SYST( 1996, psr78,   psr190, 0,      psr78,   psr78,  psr150_state,  empty_init,  "Yamaha", "PSR-78",  MACHINE_SUPPORTS_SAVE )



ympsr16.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Skeleton driver for Yamaha PSR-16 & related keyboards.

    These are based on the YM3420AD or YM3420BF "CPU and FM Tone Generator",
    a 65C02-based SoC which includes 256 bytes of internal RAM, 16 KB of
    internal ROM (enabled only on PSR-36?), a MIDI UART and a DAC. It is
    paired with the YM3419AD (XE193A00) or YM3419BF (XF026A00) RYP7 rhythm
    generator, which has its own external ROM.

    pss-680/780 are same machine, 780 is just different main rom and cosmetic changes.
    Mainboard: XE421 TCMK-19EHB
    IC1  YM3420B       QFP100   OPU
    IC2  YM3419B       QFP64    Drum "rompler"
    IC3  XE405B0-070   DIP32    ROM (rompler)
    IC4  XG503B0-132   DIP32    ROM (main program)
    IC6  TC5565APL-12  DIP28    8KB SRAM
    IC7  XE415AO       QFP100   Custom ?

*******************************************************************************/

#include "emu.h"
//#include "bus/midi/midi.h"
#include "cpu/m6502/w65c02.h"

namespace {

class yamaha_psr16_state : public driver_device
{
public:
	yamaha_psr16_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rombank(*this, "rombank")
	{
	}

	void psr16(machine_config &config);
	void psr36(machine_config &config);
	void pss680(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	u8 busy_r();
	void bank_w(u8 data);

	void common_map(address_map &map) ATTR_COLD;
	void psr16_map(address_map &map) ATTR_COLD;
	void psr36_map(address_map &map) ATTR_COLD;
	void pss680_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	optional_memory_bank m_rombank;
};

void yamaha_psr16_state::machine_start()
{
	if (m_rombank.found())
	{
		m_rombank->configure_entries(0, 0x10, memregion("program")->base(), 0x4000);
		m_rombank->set_entry(0);
	}
}


u8 yamaha_psr16_state::busy_r()
{
	// Code waits for bit 7 of $0200 to go low before writing to $02XX
	return 0;
}

void yamaha_psr16_state::bank_w(u8 data)
{
	m_rombank->set_entry(data & 0x0f);
}

void yamaha_psr16_state::common_map(address_map &map)
{
	map(0x0000, 0x00ff).mirror(0x100).ram();
	map(0x0200, 0x0200).r(FUNC(yamaha_psr16_state::busy_r));
	map(0x0310, 0x0312).nopw();
	map(0x0318, 0x0318).nopr();
	map(0x0319, 0x031a).nopw();
	map(0x031b, 0x031b).nopr();
	map(0x0327, 0x0327).nopw();
}

void yamaha_psr16_state::psr16_map(address_map &map)
{
	common_map(map);
	map(0xc000, 0xffff).rom().region("program", 0);
}

void yamaha_psr16_state::psr36_map(address_map &map)
{
	common_map(map);
	map(0x2000, 0x27ff).ram();
	map(0x4000, 0x7fff).rom().region("program", 0x4000);
	map(0x8000, 0xbfff).rom().region("program", 0);
	map(0xc000, 0xffff).rom().region("maincpu", 0);
}

void yamaha_psr16_state::pss680_map(address_map &map)
{
	common_map(map);
	map(0x1800, 0x1800).w(FUNC(yamaha_psr16_state::bank_w));
	map(0x1801, 0x1802).nopw();
	map(0x1810, 0x1811).nopr();
	map(0x2000, 0x3fff).ram();
	map(0x4000, 0x7fff).bankr("rombank");
	map(0x8000, 0xffff).rom().region("program", 0);
}



static INPUT_PORTS_START(psr16)
INPUT_PORTS_END

void yamaha_psr16_state::psr16(machine_config &config)
{
	W65C02(config, m_maincpu, 5.5_MHz_XTAL / 4); // XTAL value from PSS-480; internal divider guessed
	m_maincpu->set_addrmap(AS_PROGRAM, &yamaha_psr16_state::psr16_map);
}

void yamaha_psr16_state::psr36(machine_config &config)
{
	psr16(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &yamaha_psr16_state::psr36_map);
}

void yamaha_psr16_state::pss680(machine_config &config)
{
	psr16(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &yamaha_psr16_state::pss680_map);
}

ROM_START(psr16)
	ROM_REGION(0x4000, "program", 0)
	ROM_LOAD("yamaha_xe988a0_7247-h001.bin", 0x0000, 0x4000, CRC(20c5c0b2) SHA1(d7a066b680b7d4cfded62d4d50808c21784774d0)) // 28-pin mask ROM
ROM_END

ROM_START(psr36)
	ROM_REGION(0x4000, "maincpu", 0)
	ROM_LOAD("ym3420bf_internal.bin", 0x0000, 0x4000, NO_DUMP)
	ROM_FILL(0x3ffa, 1, 0x07)
	ROM_FILL(0x3ffb, 1, 0x40)
	ROM_FILL(0x3ffc, 1, 0x00)
	ROM_FILL(0x3ffd, 1, 0x40)
	ROM_FILL(0x3ffe, 1, 0x04)
	ROM_FILL(0x3fff, 1, 0x40)

	ROM_REGION(0x20000, "program", 0)
	ROM_LOAD("yamaha_xe137c0_7247-h005.bin", 0x00000, 0x20000, CRC(b9566cd5) SHA1(e35979299de2442d62e039d45b54bf77e9571f49)) // 28-pin mask ROM (second 32K is copy of first 32K; last half is all zero fill)

	ROM_REGION(0x20000, "waves", 0)
	ROM_LOAD("yamaha_xe137b0_4881-7554.bin", 0x00000, 0x20000, CRC(6fe1382e) SHA1(82320d0a6c14825d790c0d67263786f767b7c318)) // 28-pin mask ROM (drum samples)
ROM_END

ROM_START(pss480)
	ROM_REGION(0x40000, "program", 0)
	ROM_LOAD("xe418c0-077.ic4", 0x00000, 0x40000, CRC(1e9a1933) SHA1(c0015cbb34f50ac5687db9def679117e4b2bad32)) // uPD23C2001C (DIP32 5V 2Mbit)

	ROM_REGION(0x20000, "waves", 0)
	ROM_LOAD("xe402a0-611.ic3", 0x00000, 0x20000, CRC(e0f6f612) SHA1(1307a69233da31d19750c33dfb46a216c0be9c08)) // uPD23C1000C (DIP28 5V 1Mbit) (location has holes for a 32-pin ROM)
ROM_END

ROM_START(pss680)
	ROM_REGION(0x40000, "program", 0)
	ROM_LOAD("xe416d0-093.ic4", 0x00000, 0x40000, CRC(150d1392) SHA1(d578324cfae73cd5f6c628eb3044be07684bc5d0)) // 32-pin mask ROM

	ROM_REGION(0x40000, "waves", 0)
	ROM_LOAD("xe405b0-070.ic3", 0x00000, 0x40000, CRC(53336c52) SHA1(6bcad44fc93cfa5cd603cf24adfd736a911d3509)) // 32-pin mask ROM
ROM_END

ROM_START(pss780)
	ROM_REGION(0x40000, "program", 0)
	ROM_LOAD("xg503b0-132.ic4", 0x00000, 0x40000, CRC(0c055206) SHA1(33cc3f4ab27cf6e5068627625f6ab236209ad776)) // 32-pin mask ROM

	ROM_REGION(0x40000, "waves", 0)
	ROM_LOAD("xe405b0-070.ic3", 0x00000, 0x40000, CRC(53336c52) SHA1(6bcad44fc93cfa5cd603cf24adfd736a911d3509)) // 32-pin mask ROM
ROM_END

} // anonymous namespace

SYST(1988, psr16,  0,      0, psr16,  psr16, yamaha_psr16_state, empty_init, "Yamaha", "PSR-16",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1988, psr36,  0,      0, psr36,  psr16, yamaha_psr16_state, empty_init, "Yamaha", "PSR-36",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1988, pss480, 0,      0, pss680, psr16, yamaha_psr16_state, empty_init, "Yamaha", "PSS-480 / PSS-580", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1988, pss680, 0,      0, pss680, psr16, yamaha_psr16_state, empty_init, "Yamaha", "PSS-680", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
SYST(1989, pss780, pss680, 0, pss680, psr16, yamaha_psr16_state, empty_init, "Yamaha", "PSS-780", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



ympsr2000.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert

#include "emu.h"

#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/sh/sh4.h"
#include "video/sed1330.h"

#include "debugger.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class psr2000_state : public driver_device {
public:
	psr2000_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		  m_maincpu(*this, "maincpu"),
		  m_lcdc(*this, "lcdc")
	{ }

	void psr2000(machine_config &config);

private:
	required_device<sh7709_device> m_maincpu;
	required_device<sed1330_device> m_lcdc;  // In reality a sed1335

	void map(address_map &map) ATTR_COLD;
	void lcdc_map(address_map &map) ATTR_COLD;

	void machine_start() override ATTR_COLD;
};

void psr2000_state::machine_start()
{
}

void psr2000_state::psr2000(machine_config &config)
{
	SH7709(config, m_maincpu, 10_MHz_XTAL*4, ENDIANNESS_BIG);
	m_maincpu->set_addrmap(AS_PROGRAM, &psr2000_state::map);

	auto &palette = PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	auto &screen = SCREEN(config, "screen", SCREEN_TYPE_LCD);
	screen.set_refresh_hz(60);
	screen.set_screen_update(m_lcdc, FUNC(sed1330_device::screen_update));
	screen.set_size(320, 240);
	screen.set_visarea(0, 320-1, 0, 240-1);
	screen.set_palette(palette);

	SED1330(config, m_lcdc, 16_MHz_XTAL / 2); // Clock provided by the fdc
	m_lcdc->set_screen("screen");
	m_lcdc->set_addrmap(0, &psr2000_state::lcdc_map);

	SPEAKER(config, "speaker", 2).front();
}

void psr2000_state::map(address_map &map)
{
	map(0x00000000, 0x007fffff).rom().region("maincpu", 0);
	map(0x08000000, 0x087fffff).rom().region("style", 0);
	map(0x0c000000, 0x0c7fffff).ram();
	map(0x10000000, 0x100fffff).rom().region("data", 0);
	map(0x14000000, 0x1400000f); // 8bitcs -> lcdccs, fdccs
	map(0x14400000, 0x14400000).rw(m_lcdc, FUNC(sed1330_device::status_r), FUNC(sed1330_device::data_w));
	map(0x14400002, 0x14400002).rw(m_lcdc, FUNC(sed1330_device::data_r), FUNC(sed1330_device::command_w));

	map(0x14c00000, 0x14ffffff).nopw();
	map(0x18000000, 0x1800000f); // 16bitcs -> vopcs, tgccs
}

void psr2000_state::lcdc_map(address_map &map)
{
	map.global_mask(0x7fff);
	map(0x0000, 0x7fff).ram();
}

static INPUT_PORTS_START( psr2000 )
INPUT_PORTS_END

ROM_START( psr2000 )
	ROM_REGION64_BE( 0x800000, "maincpu", 0 )
	ROM_LOAD32_WORD_SWAP( "x031420.ic206", 0, 0x400000, CRC(42109711) SHA1(4b9ff95d55154fff50142134e2de0fd9b95c4e8e))
	ROM_LOAD32_WORD_SWAP( "x031520.ic205", 2, 0x400000, CRC(4b0943cb) SHA1(eb2895f3957b06b991fe3a3b3030fa9aa3cc85b6))

	ROM_REGION64_BE( 0x800000, "style", 0 )
	ROM_LOAD16_WORD_SWAP( "x031710.ic207", 0, 0x800000, CRC(669c31ba) SHA1(d0b13ca2414ad8b476391ea53a511783f9f8d030))

	ROM_REGION64_BE( 0x400000, "data", 0 ) // flash
	ROM_LOAD16_WORD_SWAP( "xv685a0.ic204", 0, 0x100000, CRC(c339a386) SHA1(6ae30913d2aff7ae2056f6a5f1a2264ad3f7798c))

	ROM_REGION32_BE( 0x1000000, "swp30", 0 )
	ROM_LOAD32_WORD_SWAP( "x004010.ic406", 0, 0x800000, CRC(126e5f07) SHA1(db93965e4226212fdbe34df2f0f5457173f01dda))
	ROM_LOAD32_WORD_SWAP( "x004110.ic407", 2, 0x800000, CRC(fae720fe) SHA1(df80a9a75308f0ab1ab33248b5e8cd6563e6aed5))
ROM_END

} // anonymous namespace

SYST( 2001, psr2000, 0, 0, psr2000, psr2000, psr2000_state, empty_init, "Yamaha", "PSR-2000", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



ympsr260.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Devin Acker

/*

    Skeleton driver for Yamaha YMW728-F (GEW12) keyboards

    TODO:
    - according to schematics, CPU XTAL is 14MHz, but that makes it run too fast.
      When comparing boot-up scroll speed to a video, it should be around 8MHz,
      7MHz (14/2) is too slow.
      Increasing tick_rate in gew12.cpp will improve scroll speed, but it won't fix
      graphics glitches due to writing to LCD before the busy flag is cleared.

*/

#include "emu.h"

#include "cpu/m6502/gew12.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

namespace {

class psr260_state : public driver_device
{
public:
	psr260_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "lcdc")
		, m_keys(*this, "KEY%u", 0U)
		, m_buttons(*this, "BTN%u", 0U)
	{ }

	void psr260(machine_config& config);

	void lcd_w(u8 data);

private:
	virtual void driver_start() override;

	HD44780_PIXEL_UPDATE(lcd_update);
	void palette_init(palette_device& palette);

	required_device<gew12_device> m_maincpu;
	required_device<ks0066_device> m_lcdc;

	optional_ioport_array<11> m_keys;
	optional_ioport_array<6> m_buttons;

	ioport_value m_key_sel{};
	ioport_value m_btn_sel{};
};

void psr260_state::driver_start()
{
	save_item(NAME(m_key_sel));
	save_item(NAME(m_btn_sel));
}

void psr260_state::lcd_w(u8 data)
{
	m_lcdc->db_w(BIT(data, 1, 4) << 4);
	m_lcdc->rs_w(BIT(data, 6));
	m_lcdc->e_w(BIT(data, 5));
}

HD44780_PIXEL_UPDATE(psr260_state::lcd_update)
{
	if (x < 6 && y < 8 && line < 2 && pos < 8)
		bitmap.pix(line * 8 + y, pos * 6 + x) = state;
}

void psr260_state::palette_init(palette_device& palette)
{
	palette.set_pen_color(0, rgb_t(255, 255, 255));
	palette.set_pen_color(1, rgb_t(0, 0, 0));
}


void psr260_state::psr260(machine_config &config)
{
	GEW12(config, m_maincpu, 8'000'000); // see TODO
	m_maincpu->port_out_cb<5>().set(FUNC(psr260_state::lcd_w));

	// TODO: MIDI in/out

	// LCD
	KS0066(config, m_lcdc, 270'000); // OSC = 91K resistor, TODO: actually KS0066U-10B
	m_lcdc->set_lcd_size(2, 8);
	m_lcdc->set_pixel_update_cb(FUNC(psr260_state::lcd_update));

	// screen (for testing only)
	// TODO: the actual LCD with custom segments
	screen_device& screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6 * 8, 8 * 2);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(psr260_state::palette_init), 2);
}


INPUT_PORTS_START(psr260)
INPUT_PORTS_END

ROM_START( psr79 )
	ROM_REGION( 0x100000, "maincpu", 0 )
	ROM_LOAD( "xu66710.bin", 0x00000, 0x100000, CRC(d48f57f1) SHA1(587bfe518e83490e1885c88871c39ff042af9429))
ROM_END

ROM_START( psr260 )
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_LOAD( "xy43530.bin", 0x00000, 0x200000, CRC(51edbc10) SHA1(fcd2c5b247895c1bd4a185572a60115872763f63))
ROM_END

ROM_START( psr160 )
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_LOAD( "xy43430.bin", 0x00000, 0x200000, CRC(e234e79b) SHA1(4af36344c2dcc858db110d0ddc5535dbc47a25eb))
ROM_END

} // anonymous namespace

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS          INIT         COMPANY   FULLNAME   FLAGS
SYST( 1998, psr79,   0,      0,      psr260,  psr260, psr260_state,  empty_init,  "Yamaha", "PSR-79",  MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
SYST( 2000, psr260,  0,      0,      psr260,  psr260, psr260_state,  empty_init,  "Yamaha", "PSR-260", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
SYST( 2000, psr160,  psr260, 0,      psr260,  psr260, psr260_state,  empty_init,  "Yamaha", "PSR-160", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



ympsr340.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/*
    Yamaha PSR-340 PortaSound keyboard
    Preliminary driver by R. Belmont

    CPU and Sound: SWX00B, which is an H8S/2000 series CPU and Yamaha
      AWM2 with capabilities somewhat intermediate between SWP00 and
      SWP30.
    LCD controller: KS0066U (apparently equivalent to Samsung S6A0069)
    FDC: HD63266F (uPD765 derivative)
    RAM: 256KiB, 2x uPD431000 or equivalent
*/

#include "emu.h"

#include "bus/midi/midi.h"
#include "cpu/h8/swx00.h"
#include "machine/nvram.h"
#include "video/hd44780.h"

#include "mks3.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class psr340_state : public driver_device
{
public:
	psr340_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ram(*this, "ram"),
		m_nvram(*this, "ram"),
		m_mks3(*this, "mks3"),
		m_lcdc(*this, "ks0066"),
		m_outputs(*this, "%02d.%x.%x", 0U, 0U, 0U),
		m_key(*this, "S%c", 'A')
	{ }

	void psr340(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<swx00_device> m_maincpu;
	required_shared_ptr<u16> m_ram;
	required_device<nvram_device> m_nvram;
	required_device<mks3_device> m_mks3;
	required_device<hd44780_device> m_lcdc;
	output_finder<80, 8, 5> m_outputs;
	required_ioport_array<8> m_key;

	void c_map(address_map &map) ATTR_COLD;
	void s_map(address_map &map) ATTR_COLD;

	void pdt_w(u16 data);
	u8 pad_r();
	void txd_w(u8 data);

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	u8 m_matrixsel = 0U;
};

u8 psr340_state::pad_r()
{
	u8 data = 0;

	for (int i = 0; i < 8; i++)
	{
		if (BIT(m_matrixsel, i))
		{
			data |= m_key[i]->read();
		}
	}

	return data;
}

void psr340_state::pdt_w(u16 data)
{
	// bit 14 = mks3 ic, bit 11 = E, bit 12 = RS, R/W is connected to GND so write-only
	// all bits are also matrix select for reading the controls
	m_mks3->ic_w(BIT(data, 14));
	m_lcdc->rs_w(BIT(data, 12));
	m_lcdc->e_w(BIT(data, 11));
	m_lcdc->db_w(data);
	m_matrixsel = data;
}

void psr340_state::txd_w(u8 data)
{
	m_mks3->req_w(BIT(data, 1));
}

void psr340_state::c_map(address_map &map)
{
	map(0x000000, 0x1fffff).rom().region("maincpu", 0); // cs0
	map(0x400000, 0x43ffff).ram().share(m_ram); // cs2

	map(0x600000, 0x600000).lr8(NAME([]() -> uint8_t { return 0x80; }));    // FDC status, cs3, cs4 w/ dack
}

void psr340_state::s_map(address_map &map)
{
	map(0x000000, 0x0fffff).rom().region("wave", 0);
}

void psr340_state::machine_start()
{
	save_item(NAME(m_matrixsel));
	m_outputs.resolve();
}

void psr340_state::machine_reset()
{
}

u32 psr340_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const u8 *render = m_lcdc->render();
	for(int yy=0; yy != 8; yy++)
		for(int x=0; x != 80; x++) {
			uint8_t v = render[16*x + yy];
			for(int xx=0; xx != 5; xx++)
				m_outputs[x][yy][xx] = (v >> xx) & 1;
		}

	return 0;
}


static INPUT_PORTS_START(psr340)
	PORT_START("SA")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Song") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Record") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Main A") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("-") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Execute") PORT_CODE(KEYCODE_ENTER)

	PORT_START("SB")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Function") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Dual") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Intro/End") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Metronome") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("0") PORT_CODE(KEYCODE_0)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Tenkey +") PORT_CODE(KEYCODE_EQUALS)

	PORT_START("SC")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("O.T.S. 6") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Touch") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Start/Stop") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Porta Grand") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Tenkey -") PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("7") PORT_CODE(KEYCODE_7)

	PORT_START("SD")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("O.T.S. 5") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Harmony") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Sync Start") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Utility") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("8") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("9") PORT_CODE(KEYCODE_9)

	PORT_START("SE")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("O.T.S. 4") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Reverb") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Accomp On") PORT_CODE(KEYCODE_N)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Save") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("6") PORT_CODE(KEYCODE_6)

	PORT_START("SF")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("O.T.S. 3") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Demo") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Load") PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("+") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("4") PORT_CODE(KEYCODE_4)

	PORT_START("SG")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("O.T.S. 2") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Chord Guide") PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Voice") PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("1") PORT_CODE(KEYCODE_1)

	PORT_START("SH")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("O.T.S. 1") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Main B") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Style") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("3") PORT_CODE(KEYCODE_3)
INPUT_PORTS_END

void psr340_state::psr340(machine_config &config)
{
	/* basic machine hardware */
	SWX00(config, m_maincpu, 8.4672_MHz_XTAL*2, swx00_device::MODE_DUAL);
	m_maincpu->set_addrmap(swx00_device::AS_C, &psr340_state::c_map);
	m_maincpu->set_addrmap(swx00_device::AS_S, &psr340_state::s_map);
	m_maincpu->read_adc<0>().set_constant(0x3ff); // Battery level
	m_maincpu->read_adc<1>().set_constant(0x000); // GND
	m_maincpu->read_adc<2>().set_constant(0x000); // GND
	m_maincpu->read_adc<3>().set_constant(0x000); // GND

	m_maincpu->write_pdt().set(FUNC(psr340_state::pdt_w));
	m_maincpu->read_pad().set(FUNC(psr340_state::pad_r));
	m_maincpu->write_txd().set(FUNC(psr340_state::txd_w));

	m_maincpu->add_route(0, "speaker", 1.0, 0);
	m_maincpu->add_route(1, "speaker", 1.0, 1);

	// mks3 is connected to sclki, sync comms on sci1
	// something generates 500K for sci0, probably internal to the swx00
	m_maincpu->sci_set_external_clock_period(0, attotime::from_hz(500000));

	NVRAM(config, m_nvram, nvram_device::DEFAULT_NONE);

	MKS3(config, m_mks3);
	m_mks3->write_da().set(m_maincpu, FUNC(swx00_device::sci_rx_w<1>));
	m_mks3->write_clk().set(m_maincpu, FUNC(swx00_device::sci_clk_w<1>));

	KS0066(config, m_lcdc, 270000); // 91K resistor
	m_lcdc->set_default_bios_tag("f05");
	m_lcdc->set_lcd_size(2, 40);

	/* video hardware */
	auto &screen = SCREEN(config, "screen", SCREEN_TYPE_SVG);
	screen.set_refresh_hz(60);
	screen.set_size(800, 384);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(psr340_state::screen_update));

	MIDI_PORT(config, "mdin", midiin_slot, "midiin").rxd_handler().set(m_maincpu, FUNC(swx00_device::sci_rx_w<0>));

	auto &mdout(MIDI_PORT(config, "mdout", midiout_slot, "midiout"));
	m_maincpu->write_sci_tx<0>().set(mdout, FUNC(midi_port_device::write_txd));

	SPEAKER(config, "speaker", 2).front();
}

ROM_START( psr340 )
	ROM_REGION(0x200000, "maincpu", 0)
	ROM_LOAD16_WORD_SWAP("xv89710.bin", 0x000000, 0x200000, CRC(271ccb8a) SHA1(ec6abbdb82a5e851b77338c79ecabfd8040f023d))

	ROM_REGION16_BE(0x200000, "wave", 0)
	ROM_LOAD("xv89810.bin", 0x000000, 0x200000, CRC(10e68363) SHA1(5edee814bf07c49088da44474fdd5c817e7c5af0))

	ROM_REGION(399369, "screen", 0)
	ROM_LOAD("psr340-lcd.svg", 0, 399369, CRC(f9d11ca6) SHA1(da036d713c73d6b452a3e2d2b2234d473422d5fb))
ROM_END

} // anonymous namespace


CONS(1994, psr340, 0, 0, psr340, psr340, psr340_state, empty_init, "Yamaha", "PSR-340 PortaSound", MACHINE_NOT_WORKING)



ympsr40.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Skeleton driver for Yamaha PSR-40 PortaSound keyboard.

*******************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/i8255.h"
#include "machine/nvram.h"

namespace {

class yamaha_psr40_state : public driver_device
{
public:
	yamaha_psr40_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{
	}

	void psr40(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};


void yamaha_psr40_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("program", 0);
	map(0x8000, 0x87ff).mirror(0x1800).ram().share("nvram");
	//map(0xa000, 0xa01f).mirror(0x1fe0).rw("ge7", FUNC(ig10771_device::read), FUNC(ig10771_device::write));
	map(0xa01e, 0xa01f).nopr();
	map(0xc000, 0xc003).mirror(0x1ffc).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
}


static INPUT_PORTS_START(psr40)
INPUT_PORTS_END

void yamaha_psr40_state::psr40(machine_config &config)
{
	Z80(config, m_maincpu, 4'000'000); // unknown clock
	m_maincpu->set_addrmap(AS_PROGRAM, &yamaha_psr40_state::mem_map);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // TC5517APL + battery?

	I8255(config, "ppi");
}

ROM_START(psr40)
	ROM_REGION(0x8000, "program", 0)
	ROM_LOAD("yamaha_xa004a0_6133-7836.ic4", 0x0000, 0x8000, CRC(7ac4a729) SHA1(e792aabe816d8c46bcc683f0e9220ca50c0faaac)) // 28-pin custom-marked ROM
ROM_END

} // anonymous namespace

SYST(1985, psr40, 0, 0, psr40, psr40, yamaha_psr40_state, empty_init, "Yamaha", "PSR-40", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



ympsr400.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Skeleton driver for Yamaha PSR-400 & PSR-500 PortaTone keyboards.

*******************************************************************************/

#include "emu.h"
#include "cpu/m6805/hd6305.h"
#include "cpu/mn1880/mn1880.h"
#include "sound/multipcm.h"
#include "speaker.h"

namespace {

class psr400_state : public driver_device
{
public:
	psr400_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mpscpu(*this, "mpscpu")
	{
	}

	void psr500(machine_config &config);

protected:
	virtual void driver_start() override;
	virtual void machine_start() override ATTR_COLD;

private:
	TIMER_CALLBACK_MEMBER(interrupt_hack);

	void program_map(address_map &map) ATTR_COLD;
	void data_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<cpu_device> m_mpscpu;

	emu_timer *m_hack_timer;
};

void psr400_state::machine_start()
{
	m_hack_timer = timer_alloc(FUNC(psr400_state::interrupt_hack), this);
	m_hack_timer->adjust(attotime::from_msec(1), 0, attotime::from_msec(1));
}

TIMER_CALLBACK_MEMBER(psr400_state::interrupt_hack)
{
	m_maincpu->set_state_int(mn1880_device::MN1880_IF, m_maincpu->state_int(mn1880_device::MN1880_IF) | (1 << 3));
}

void psr400_state::program_map(address_map &map)
{
	// 2 MB external program memory space using MMU
	map(0x000000, 0x1fffff).rom().region("program", 0);
}

void psr400_state::data_map(address_map &map)
{
	// 2 MB external data memory space using MMU
	map(0x000000, 0x000000).nopw(); // ?
	map(0x000001, 0x000001).nopr(); // ?
	map(0x000011, 0x000011).noprw(); // ?
	map(0x000014, 0x000014).noprw(); // ?
	map(0x000018, 0x000018).lr8(NAME([]() { return 0; })); // serial status?
	map(0x00001a, 0x00001a).nopw(); // serial transmit buffer?
	map(0x000030, 0x000031).ram(); // ?
	map(0x000034, 0x000036).noprw(); // ?
	map(0x00003a, 0x00003b).noprw(); // ?
	map(0x00003e, 0x00003f).noprw(); // ?
	map(0x000050, 0x000053).ram(); // ?
	map(0x000055, 0x000055).noprw(); // ?
	map(0x00005d, 0x00005e).noprw(); // ?
	map(0x000080, 0x03ffff).mirror(0xc0000).ram(); // 2x 1M-bit PSRAM (only one on PSR-400)
	map(0x003fe0, 0x003fff).unmaprw(); // window for more internal SFRs?
	map(0x003fe3, 0x003fe3).noprw(); // ?
	map(0x003fe6, 0x003fe6).nopw(); // ?
	map(0x003fe7, 0x003fe7).noprw(); // ?
	map(0x003fe9, 0x003fe9).nopr(); // ?
	map(0x003fee, 0x003fee).lr8(NAME([]() { return 0x05; })).nopw(); // ?
	map(0x003ff3, 0x003ff3).noprw(); // ?
	map(0x100000, 0x10000f).mirror(0xffff0).rw("gew8", FUNC(multipcm_device::read), FUNC(multipcm_device::write));
}

static INPUT_PORTS_START(psr500)
INPUT_PORTS_END

void psr400_state::psr500(machine_config &config)
{
	MN18801A(config, m_maincpu, 10_MHz_XTAL); // MN18801A (also has 500 kHz secondary resonator connected to XI)
	m_maincpu->set_addrmap(AS_PROGRAM, &psr400_state::program_map);
	m_maincpu->set_addrmap(AS_DATA, &psr400_state::data_map);

	HD6305V0(config, m_mpscpu, 8_MHz_XTAL).set_disable(); // HD63B05V0D73P (mislabeled HD63B50 on schematic)

	SPEAKER(config, "speaker", 2).front();

	multipcm_device &gew8(MULTIPCM(config, "gew8", 9.4_MHz_XTAL)); // YMW-258-F
	gew8.add_route(1, "speaker", 1.0, 0);
	gew8.add_route(0, "speaker", 1.0, 1);

	//YM3413(config, "ldsp"); // PSR-500 only (has its own 256K-bit PSRAM)
}

ROM_START(psr500)
	ROM_REGION(0x200000, "program", 0)
	ROM_LOAD("xj920c0.ic4", 0x000000, 0x040000, CRC(bd45d962) SHA1(fe46ceae5584b56e36f31f27bedd9e7d578eb35b))
	ROM_RELOAD(0x040000, 0x040000)
	ROM_RELOAD(0x080000, 0x040000)
	ROM_RELOAD(0x0c0000, 0x040000)
	ROM_LOAD("xj921b0.ic5", 0x100000, 0x100000, CRC(dd1a8afc) SHA1(5d5b47577faeed165f0bd73283f148d112e4d1e9))

	ROM_REGION(0x100000, "gew8", 0)
	ROM_LOAD("xj426b0.ic3", 0x000000, 0x100000, CRC(ef566734) SHA1(864f5689dbaa82bd8a1be4e53bdb21ec71be03cc))

	ROM_REGION(0x1000, "mpscpu", 0)
	ROM_LOAD("xj450a00.ic1", 0x0000, 0x1000, NO_DUMP)
ROM_END

void psr400_state::driver_start()
{
	memory_region *region = memregion("program");
	u8 *program = static_cast<u8 *>(region->base());
	std::vector<u8> buf(0x20000);

	// A8-A14 & A16 are scrambled
	for (u32 offset = 0; offset < region->bytes(); )
	{
		std::copy(&program[offset], &program[offset + 0x20000], buf.begin());
		for (int blocks = 0x200; blocks-- > 0; offset += 0x100)
			std::copy_n(&buf[bitswap<9>(offset, 9, 15, 10, 11, 8, 14, 16, 13, 12) << 8], 0x100, &program[offset]);
	}
}

} // anonymous namespace

SYST(1991, psr500, 0, 0, psr500, psr500, psr400_state, empty_init, "Yamaha", "PSR-500", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



ympsr540.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert

#include "emu.h"

#include "mks3.h"
#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/sh/sh7042.h"
#include "imagedev/floppy.h"
#include "machine/nvram.h"
#include "machine/upd765.h"
#include "sound/swx00.h"
#include "video/hd44780.h"

#include "debugger.h"
#include "screen.h"
#include "speaker.h"

namespace {

class psr540_state : public driver_device {
public:
	psr540_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_swx00(*this, "swx00"),
		m_lcdc(*this, "ks0066"),
		m_floppy(*this, "fdc:0"),
		m_fdc(*this, "fdc"),
		m_nvram(*this, "ram"),
		m_mks3(*this, "mks3"),
		m_inputs(*this, "B%u", 1U),
		m_outputs(*this, "%02d.%x.%x", 0U, 0U, 0U),
		m_sustain(*this, "SUSTAIN"),
		m_pitch_bend(*this, "PITCH_BEND")
	{ }

	void psr540(machine_config &config);

private:
	required_device<sh7042_device> m_maincpu;
	required_device<swx00_sound_device> m_swx00;
	required_device<hd44780_device> m_lcdc;
	required_device<floppy_connector> m_floppy;
	required_device<hd63266f_device> m_fdc;
	required_device<nvram_device> m_nvram;
	required_device<mks3_device> m_mks3;
	required_ioport_array<8> m_inputs;
	output_finder<80, 8, 5> m_outputs;
	required_ioport m_sustain;
	required_ioport m_pitch_bend;

	u16 m_pe, m_led, m_scan;

	void map(address_map &map) ATTR_COLD;

	void machine_start() override ATTR_COLD;

	u16 adc_sustain_r();
	u16 adc_midisw_r();
	u16 adc_battery_r();

	void pa_w(u16 data);
	u16 pe_r();
	void pe_w(u16 data);
	u8 pf_r();

	void lcd_data_w(u8 data);
	void led_data_w(offs_t, u16 data, u16 mem_mask);
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};

u32 psr540_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const u8 *render = m_lcdc->render();
	for(int yy=0; yy != 8; yy++)
		for(int x=0; x != 80; x++) {
			uint8_t v = render[16*x + yy];
			for(int xx=0; xx != 5; xx++)
				m_outputs[x][yy][xx] = (v >> xx) & 1;
		}

	return 0;
}

void psr540_state::machine_start()
{
	m_outputs.resolve();

	save_item(NAME(m_pe));
	save_item(NAME(m_led));
	save_item(NAME(m_scan));
	m_pe = 0;
	m_led = 0;
	m_scan = 0;

	m_fdc->set_floppy(m_floppy->get_device());
}

u16 psr540_state::adc_sustain_r()
{
	return m_sustain->read() ? 0x3ff : 0;
}

u16 psr540_state::adc_midisw_r()
{
	return 0;
}

u16 psr540_state::adc_battery_r()
{
	return 0x3ff;
}

static void psr540_floppies(device_slot_interface &device)
{
	device.option_add("35hd", FLOPPY_35_HD);
}

void psr540_state::psr540(machine_config &config)
{
	SH7042A(config, m_maincpu, 7_MHz_XTAL*4); // // md=a, on-chip rom, 32-bit space, pll 4x -- XW25610 6437042F14F 9M1 A
	m_maincpu->set_addrmap(AS_PROGRAM, &psr540_state::map);
	m_maincpu->read_adc<0>().set(FUNC(psr540_state::adc_midisw_r));
	m_maincpu->read_adc<1>().set(FUNC(psr540_state::adc_sustain_r));
	m_maincpu->read_adc<2>().set(FUNC(psr540_state::adc_battery_r));
	m_maincpu->read_adc<3>().set_constant(0);
	m_maincpu->read_adc<4>().set_ioport(m_pitch_bend);
	m_maincpu->read_adc<5>().set_constant(0); // Actually used as pf5
	m_maincpu->write_porta().set(FUNC(psr540_state::pa_w));
	m_maincpu->read_porte().set(FUNC(psr540_state::pe_r));
	m_maincpu->write_porte().set(FUNC(psr540_state::pe_w));
	m_maincpu->read_portf().set(FUNC(psr540_state::pf_r));

	SWX00_SOUND(config, m_swx00);
	m_swx00->add_route(0, "speaker", 1.0, 0);
	m_swx00->add_route(1, "speaker", 1.0, 1);

	MKS3(config, m_mks3);
	m_mks3->write_da().set(m_maincpu, FUNC(sh7042_device::sci_rx_w<1>));
	m_mks3->write_clk().set(m_maincpu, FUNC(sh7042_device::sci_clk_w<1>));

	KS0066(config, m_lcdc, 270000); // OSC = 91K resistor, TODO: actually KS0066U-10B
	m_lcdc->set_default_bios_tag("f00");
	m_lcdc->set_lcd_size(2, 40);

	HD63266F(config, m_fdc, 16_MHz_XTAL);
	//m_fdc->drq_wr_callback().set([this](int state){ fdc_drq = state; maincpu->set_input_line(Z180_INPUT_LINE_DREQ0, state); });
	m_fdc->set_ready_line_connected(false);
	m_fdc->set_select_lines_connected(false);
	m_fdc->inp_rd_callback().set([this](){ return m_floppy->get_device()->dskchg_r(); });
	m_fdc->intrq_wr_callback().set_inputline(m_maincpu, 0);

	FLOPPY_CONNECTOR(config, m_floppy, psr540_floppies, "35hd", floppy_image_device::default_pc_floppy_formats, true);

	NVRAM(config, m_nvram, nvram_device::DEFAULT_NONE);

	/* video hardware */
	auto &screen = SCREEN(config, "screen", SCREEN_TYPE_SVG);
	screen.set_refresh_hz(60);
	screen.set_size(1080, 360);
	screen.set_visarea_full();
	screen.set_screen_update(FUNC(psr540_state::screen_update));

	SPEAKER(config, "speaker", 2).front();

	auto &mdin(MIDI_PORT(config, "mdin"));
	midiin_slot(mdin);
	mdin.rxd_handler().set(m_maincpu, FUNC(sh7042a_device::sci_rx_w<0>));

	auto &mdout(MIDI_PORT(config, "mdout"));
	midiout_slot(mdout);
	m_maincpu->write_sci_tx<0>().set(mdout, FUNC(midi_port_device::write_txd));
}

// Port E:
//  15:    N10
//  14:    (dack0)
//  13-12: N9-8
//  11:    lcd_rs
//  10:    N7
//   9:    lcd_en
//   8:    rdens
//   7-5:  N6-4

// Port F:
//  7-5:   N3-1
//  4-0:   (ad inputs)

u16 psr540_state::pe_r()
{
	u32 n = m_inputs[m_scan]->read();
	return util::bitswap(~n | 0x8000, 9, 15, 8, 7, 15, 6, 15, 15, 5, 4, 3, 15, 15, 15, 15, 15);
}

u8 psr540_state::pf_r()
{
	u32 n = m_inputs[m_scan]->read();
	return util::bitswap(~n | 0x8000, 2, 1, 0, 15, 15, 15, 15, 15);
}

void psr540_state::pe_w(u16 data)
{
	m_pe = data;
	//logerror("pe lcd_rs=%x lcd_en=%x rdens=%d ldcic=%d fdcic=%d (%s)\n", BIT(m_pe, 11), BIT(m_pe, 9), BIT(m_pe, 8), BIT(m_pe, 4), BIT(m_pe, 3), machine().describe_context());
	m_lcdc->rs_w(BIT(m_pe, 11));
	m_lcdc->e_w(BIT(m_pe, 9));
	m_fdc->rate_w(!BIT(m_pe, 8));
	m_mks3->ic_w(BIT(m_pe, 4));

	if(BIT(m_pe, 4))
		m_scan = m_led & 7;
}

void psr540_state::pa_w(u16 data)
{
	m_mks3->req_w(BIT(data, 4));
}

void psr540_state::lcd_data_w(u8 data)
{
	m_lcdc->db_w(data);
}

void psr540_state::led_data_w(offs_t, u16 data, u16 mem_mask)
{
	COMBINE_DATA(&m_led);
	if(BIT(m_pe, 4))
		m_scan = m_led & 7;
}

void psr540_state::map(address_map &map)
{
	map(0x00000000, 0x0001ffff).rom().region("kernel", 0).mirror(0x1e0000);  // Internal rom, single-cycle

	// 200000-3fffff: cs0 space, 8bits, 1 cycle between accesses, cs assert extension, 6 wait states
	// 200000 fdc
	map(0x00200000, 0x00200003).m(m_fdc, FUNC(hd63266f_device::map));
	// 280000 sram (battery-backed)
	map(0x00280000, 0x0029ffff).ram().share("ram");
	// 2c0000 leds/scanning
	map(0x002c0000, 0x002c0001).w(FUNC(psr540_state::led_data_w));
	// 300000 lcd
	map(0x00300000, 0x00300000).w(FUNC(psr540_state::lcd_data_w));

	// 400000-7fffff: cs1 space, 16 bits, 2 wait states
	map(0x00400000, 0x007fffff).rom().region("program_rom", 0);

	// c00000-ffffff: cs3 space, 8 bits, cs assert extension, 3 wait states
	map(0x00c00000, 0x00c007ff).m(m_swx00, FUNC(swx00_sound_device::map));

	// Dedicated dram space, ras precharge = 1.5, ras-cas delay 2, cas-before-ras 2.5, dram write 4, read 3, idle 0, burst, ras down, 16bits, 9-bit address
	// Automatic refresh every 436 cycles, cas-before-ras
	map(0x01000000, 0x0107ffff).ram(); // dram
}

static INPUT_PORTS_START( psr540 )
	PORT_START("SUSTAIN")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Sustain Pedal")

	PORT_START("PITCH_BEND")
	PORT_BIT(0x3ff, 0x200, IPT_PADDLE) PORT_NAME("Pitch Bend") PORT_SENSITIVITY(30)

	PORT_START("B1")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("9") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Exit") PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Utility") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Save") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Load") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part R2") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part R1") PORT_CODE(KEYCODE_T)
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part L") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("8") PORT_CODE(KEYCODE_8)

	PORT_START("B2")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Voice L") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Voice R1") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Voice R2") PORT_CODE(KEYCODE_O)
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Track 11")
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Track 12")
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Track 13")
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Track 14")
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Track 15")
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Track 16")
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Style") PORT_CODE(KEYCODE_P)

	PORT_START("B3")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Track 1")
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Track 2")
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Track 3")
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Track 4")
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Track 5")
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Track 6")
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Track 7")
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Track 8")
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Track 9")
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Track 10")

	PORT_START("B4")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Vocal Chg") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Mixer") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Direct Access") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Next") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Best") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Harmony/Echo") PORT_CODE(KEYCODE_H)
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Sustain") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Touch") PORT_CODE(KEYCODE_K)
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Fast/Slow") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("DSP") PORT_CODE(KEYCODE_COLON)

	PORT_START("B5")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("2")     PORT_CODE(KEYCODE_2)
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("1")     PORT_CODE(KEYCODE_1)
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("6")     PORT_CODE(KEYCODE_6)
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("5")     PORT_CODE(KEYCODE_5)
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("4")     PORT_CODE(KEYCODE_4)
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("7")     PORT_CODE(KEYCODE_7)
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("+/Yes") PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("0")     PORT_CODE(KEYCODE_0)
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("-/No")  PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("3")     PORT_CODE(KEYCODE_3)

	PORT_START("B6")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Song") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Function") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Acmp/Song Vol") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Transpose") PORT_CODE(KEYCODE_V)
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Tempo/Tap") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Fingering") PORT_CODE(KEYCODE_N)
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Acmp On/Off") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Record") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Demo") PORT_CODE(KEYCODE_STOP)

	PORT_START("B7")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Pad 1")
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Pad 2")
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Pad 3")
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Pad 4")
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Pad Stop")
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Intro")
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Sync Stop")
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Sync Start")
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Start/Stop")

	PORT_START("B8")
	PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Reg 4")
	PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Reg 3")
	PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Reg 1")
	PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Memory")
	PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Freeze")
	PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Reg 2")
	PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Main A")
	PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Main B")
	PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Ending")
	PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Reg./OTS")
INPUT_PORTS_END

ROM_START( psr540 )
	// Hitachi HI-7 RTOS, version 1.6.  Internal to the sh-2
	ROM_REGION32_BE( 0x400000, "kernel", 0 )
	ROM_LOAD( "hi_7_v1_6.bin", 0, 0x20000, CRC(1f1992d9) SHA1(4f037cc5d7928ace240e31c65dfdff7ce91bd33a))

	ROM_REGION32_BE( 0x400000, "program_rom", 0 )
	ROM_LOAD16_WORD_SWAP( "xw25320.ic310", 0, 0x400000, CRC(e8d29e49) SHA1(079e0ccf6cf5d5bd2d2d82076b09dd702fcd1421))

	ROM_REGION16_LE( 0x600000, "swx00", 0)
	ROM_LOAD16_WORD_SWAP( "xw25410.ic210", 0, 0x400000, CRC(c7c4736d) SHA1(ff1052eb076557071ed8652e6c2fc0925144fbd5))
	ROM_LOAD16_WORD_SWAP( "xw25520.ic220", 0x400000, 0x200000, CRC(9ef56c4e) SHA1(f26b588f9bcfd7bdbf1c0b38e4a1ea57e2f29f10))

	ROM_REGION(634772, "screen", ROMREGION_ERASE00)
	ROM_LOAD("psr540-lcd.svg", 0, 634772, CRC(606d85ab) SHA1(6eff1f028c531cdcd070b21949e4624af0a586a1))
ROM_END

} // anonymous namespace

SYST( 1999, psr540, 0, 0, psr540, psr540, psr540_state, empty_init, "Yamaha", "PSR540", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



ympsr60.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont,Aaron Giles
/*
    Yamaha PSR-60/PSR-70 PortaSound keyboards
    Preliminary driver by R. Belmont, major thanks to reverse-engineering work by JKN0

    Note: PSR-50 is likely the same hardware.

    Special thanks to

    Documentation: https://github.com/JKN0/PSR70-reverse
    More documentation: https://retroandreverse.blogspot.com/2021/01/reversing-psr-70-hardware.html
                        https://retroandreverse.blogspot.com/2021/01/reversing-psr-70-firmware.html
                        https://retroandreverse.blogspot.com/2021/01/digging-into-ymopq.html

    Service manuals: https://elektrotanya.com/yamaha_psr-60.pdf/download.html
                     https://elektrotanya.com/yamaha_psr-70_sm.pdf/download.html

    Thanks to Lord Nightmare for help with the BBD filter chain

    CPU: Z80 @ 6 MHz
    Sound: YM3533 "OPQ" FM @ 3.58 MHz + YM2154 "RYP" sample playback chip for drums
    Panel and keyboard I/O: 82C55A PPI and Yamaha IG14330 "DRVIF"
    MIDI I/O: HD6350 ACIA, baud rate clock is 500 kHz

    Z80 IRQ is a wire-OR of IRQs from the OPQ, RYP4, ACIA, and DRVIF

    PPI ports:
    PA0-PA5: keyboard matrix readback (PA5-PA0 for most matrix selects, where PA5 is the lowest key and PA0 is the highest)
    PA6: single-use matrix readback for C1 (when PC1 is active)
    PA7: cassette input
    Keyboard matrix select writes:
        PC1: C1 - F#1
        PC0: G1 - C2
        PB7: C#2 - F#2
        PB6: G2 - C3
        PB5: C#3 - F#3
        PB4: G3 - C4
        PB3: C#4 - F#4
        PB2: G4 - C5
        PB1: C#5 - F#5
        PB0: G5 - C6
    PC3: unused
    PC4: ROM2 bank (goes to A14)
    PC5: cassette output
    PC6: MUTE
    PC7: to "RS2" signal, IC (reset, "initial clear" in Yamaha-speak) pin on T9500
*/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/i8255.h"
#include "machine/6850acia.h"
#include "machine/clock.h"
#include "sound/bbd.h"
#include "sound/flt_biquad.h"
#include "sound/flt_rc.h"
#include "sound/mixer.h"
#include "sound/ym2154.h"
#include "sound/ymopq.h"
#include "bus/midi/midi.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "psr60.lh"
#include "psr70.lh"

namespace {

class psr60_state : public driver_device
{
	static constexpr int DRVIF_MAX_TARGETS = 23;
	static constexpr int RYP4_MAX_TARGETS = 10;

public:
	psr60_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_ym3533(*this, "ym3533"),
		m_ppi(*this, "ppi"),
		m_acia(*this, "acia"),
		m_lmixer(*this, "lmixer"),
		m_rmixer(*this, "rmixer"),
		m_bbd_mixer(*this, "bbdmixer"),
		m_bbd(*this, "bbd"),
		m_ryp4(*this, "ryp4"),
		m_ic206b(*this, "ic206b"),
		m_ic206a(*this, "ic206a"),
		m_ic205(*this, "ic205"),
		m_postbbd_rc(*this, "postbbd_rc"),
		m_ic204b(*this, "ic204b"),
		m_first_rc(*this, "first_rc"),
		m_ic204a(*this, "ic204a"),
		m_rom2bank(*this, "rom2bank"),
		m_keyboard(*this, "P1_%u", 0),
		m_drvif(*this, "DRVIF_%u", 0),
		m_drvif_out(*this, "DRVIF_%u_DP%u", 0U, 1U),
		m_ryp4_in(*this, "RYP4_%u", 1U),
		m_ryp4_out(*this, "RYP4_%u", 1U),
		m_mastervol(*this, "MASTERVOL")
	{ }

	void psr_common(machine_config &config);
	void psr60(machine_config &config);
	void psr70(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<z80_device> m_maincpu;
	required_device<ym3533_device> m_ym3533;
	required_device<i8255_device> m_ppi;
	required_device<acia6850_device> m_acia;
	required_device<mixer_device> m_lmixer;
	required_device<mixer_device> m_rmixer;
	required_device<mixer_device> m_bbd_mixer;
	required_device<mn3204p_device> m_bbd;
	required_device<ym2154_device> m_ryp4;
	required_device<filter_biquad_device> m_ic206b;
	required_device<filter_biquad_device> m_ic206a;
	required_device<filter_biquad_device> m_ic205;
	required_device<filter_rc_device> m_postbbd_rc;
	required_device<filter_biquad_device> m_ic204b;
	required_device<filter_rc_device> m_first_rc;
	required_device<filter_biquad_device> m_ic204a;
	required_memory_bank m_rom2bank;
	required_ioport_array<10> m_keyboard;
	required_ioport_array<DRVIF_MAX_TARGETS> m_drvif;
	output_finder<DRVIF_MAX_TARGETS, 4> m_drvif_out;
	required_ioport_array<RYP4_MAX_TARGETS> m_ryp4_in;
	output_finder<RYP4_MAX_TARGETS> m_ryp4_out;
	required_ioport m_mastervol;

	void psr60_map(address_map &map) ATTR_COLD;
	void psr60_io_map(address_map &map) ATTR_COLD;

	u8 ppi_pa_r();
	void ppi_pb_w(u8 data);
	void ppi_pc_w(u8 data);
	void recalc_irqs();

	TIMER_CALLBACK_MEMBER(bbd_tick);
	void bbd_setup_next_tick();

	emu_timer *m_bbd_timer;

	int m_acia_irq, m_ym_irq, m_drvif_irq, m_ym2154_irq;
	u16 m_keyboard_select;
	u8 m_bbd_config;
	u8 m_drvif_data[2];
	u8 m_drvif_select;
	u8 m_sustain_fuzz;

	void write_acia_clock(int state) { m_acia->write_txc(state); m_acia->write_rxc(state); }
	void acia_irq_w(int state) { m_acia_irq = state; recalc_irqs(); }
	void ym_irq_w(int state) { m_ym_irq = state; recalc_irqs(); }
	void ryp4_irq_w(int state) { m_ym2154_irq = state; recalc_irqs(); }

	u8 ryp4_an_r(offs_t offset);
	void ryp4_out_w(u8 data);

	u8 drvif_r(offs_t offset);
	void drvif_w(offs_t offset, u8 data);

public:
	INPUT_CHANGED_MEMBER(drvif_changed);
	INPUT_CHANGED_MEMBER(mastervol_changed);

	// optional sustain pedal input; if this doesn't change, sustain will not work
	// if no pedal present, it seems sustain should still work, so toggle the value
	// here a bit to make the keyboard notice
	ioport_value sustain_fuzz() { return (m_sustain_fuzz = !m_sustain_fuzz) ? 8 : 12; }
};

void psr60_state::psr60_map(address_map &map)
{
	map(0x0000, 0x7fff).rom().region("rom1", 0);
	map(0x8000, 0xbfff).bankr("rom2bank");
	map(0xc000, 0xc0ff).rw(m_ym3533, FUNC(ym3533_device::read), FUNC(ym3533_device::write));
	map(0xe000, 0xffff).ram();  // work RAM
}

void psr60_state::psr60_io_map(address_map &map)
{
	map.global_mask(0xff);  // top 8 bits of the address are ignored by this hardware for I/O access
	map(0x10, 0x11).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write));
	map(0x20, 0x23).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
	map(0x30, 0x3f).rw(FUNC(psr60_state::drvif_r), FUNC(psr60_state::drvif_w));
	map(0x80, 0xff).rw(m_ryp4, FUNC(ym2154_device::read), FUNC(ym2154_device::write));
}

u8 psr60_state::ppi_pa_r()
{
	u8 result = 0;
	for (int bit = 0; bit < 10; bit++)
		if (BIT(m_keyboard_select, bit))
			result |= m_keyboard[bit]->read();
	return result;
}

void psr60_state::ppi_pb_w(u8 data)
{
	m_keyboard_select = (m_keyboard_select & ~0xff) | data;
}

void psr60_state::ppi_pc_w(u8 data)
{
	m_rom2bank->set_entry(BIT(data, 4));
	m_keyboard_select = (m_keyboard_select & ~0x300) | ((data & 3) << 8);
}

u8 psr60_state::ryp4_an_r(offs_t offset)
{
	return (offset < RYP4_MAX_TARGETS) ? (m_ryp4_in[offset]->read() * 255 / 100) : 0;
}

void psr60_state::ryp4_out_w(u8 data)
{
	m_bbd_config = data;

	// bit 0 (CT0) enables/disables the effect
	m_ic206a->set_output_gain(0, BIT(data, 0) ? 1.0 : 0.0);

	// bits 1 + 2 go to the 'T' and 'C' pins and control the frequency
	// modulation, which we simulate in a periodic timer
}

TIMER_CALLBACK_MEMBER(psr60_state::bbd_tick)
{
	m_bbd->tick();
	bbd_setup_next_tick();
}

void psr60_state::bbd_setup_next_tick()
{
	attotime curtime = machine().time();

	// only two states have been observed to be measured: CT1=1/CT2=0 and CT1=0/CT2=1
	double bbd_freq;
	if (BIT(m_bbd_config, 1) && !BIT(m_bbd_config, 2))
	{
		// Stereo symphonic off: min freq 35 kHz, max freq 107 kHz, varies at 0,3 Hz
		curtime.m_seconds %= 3;
		double pos = curtime.as_double() / 3;
		pos = (pos < 0.5) ? (2 * pos) : 2 * (1.0 - pos);
		bbd_freq = 35000 + (107000 - 35000) * pos;
	}
	else
	{
		// Stereo symphonic on: min freq 48 kHz, max freq 61 kHz, varies at 6 Hz
		curtime.m_seconds = 0;
		double pos = curtime.as_double() * 6;
		pos -= floor(pos);
		pos = (pos < 0.5) ? (2 * pos) : 2 * (1.0 - pos);
		bbd_freq = 48000 + (61000 - 48000) * pos;
	}

	// BBD driver provides two out-of-phase clocks to basically run the BBD at 2x
	m_bbd_timer->adjust(attotime::from_ticks(1, bbd_freq * 2));
}

//
// DRVIF: driver interface chip
//
// This chip manages a number of external drivers. Each driver has 4 switch
// inputs and 4 LED outputs. Interrupts are triggered when changes are detected,
// and the index of the changed port is provided.
//
// In reality, this is probably a microcontroller.
//
u8 psr60_state::drvif_r(offs_t offset)
{
	if (offset == 0)
		return (m_drvif_irq << 7) | 0x40;
	else if (offset <= 2)
	{
		m_drvif_irq = 0;
		recalc_irqs();
		return m_drvif_data[offset - 1];
	}
	else
		return 0;
}
void psr60_state::drvif_w(offs_t offset, u8 data)
{
	if (offset == 1)
		m_drvif_select = data & 0x1f;
	else if (offset == 2 && m_drvif_select < DRVIF_MAX_TARGETS)
	{
		for (int bit = 0; bit < 4; bit++)
			m_drvif_out[m_drvif_select][bit] = BIT(data, 3 - bit);
	}
//  else
//      printf("DRVIF: %02X = %02X\n", offset, data);
}

INPUT_CHANGED_MEMBER(psr60_state::drvif_changed)
{
	m_drvif_irq = 1;
	m_drvif_data[0] = param;
	m_drvif_data[1] = m_drvif[param]->read();
	recalc_irqs();
}

INPUT_CHANGED_MEMBER(psr60_state::mastervol_changed)
{
	float mastervol = m_mastervol->read() / 50.0;
	m_lmixer->set_output_gain(0, mastervol);
	m_rmixer->set_output_gain(0, mastervol);
}

void psr60_state::recalc_irqs()
{
	int const irq_state = m_acia_irq | m_ym_irq | m_drvif_irq | m_ym2154_irq;
	m_maincpu->set_input_line(0, irq_state);
}

void psr60_state::machine_start()
{
	m_bbd_timer = timer_alloc(FUNC(psr60_state::bbd_tick), this);

	m_drvif_out.resolve();
	m_rom2bank->configure_entries(0, 2, memregion("rom2")->base(), 0x4000);
	m_rom2bank->set_entry(0);
	m_acia_irq = 0;
	m_ym_irq = 0;
	m_drvif_irq = 0;
	m_ym2154_irq = 0;
}

void psr60_state::machine_reset()
{
	bbd_setup_next_tick();
}

#define DRVIF_PORT(num, sw1, sw2, sw3, sw4) \
	PORT_START("DRVIF_" #num) \
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(sw1) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psr60_state::drvif_changed), num) \
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(sw2) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psr60_state::drvif_changed), num) \
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(sw3) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psr60_state::drvif_changed), num) \
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(sw4) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psr60_state::drvif_changed), num)

#define RYP4_PORT(num, defval, name) \
	PORT_START("RYP4_" #num) \
	PORT_ADJUSTER(defval, name)

static INPUT_PORTS_START(psr60)
	PORT_START("P1_9")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // cassette input
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 C")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 C#")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 D")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 D#")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 E")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 F")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 F#")

	PORT_START("P1_8")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // cassette input
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 G")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 G#")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 A")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 A#")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 B")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 C")

	PORT_START("P1_7")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // cassette input
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 C#")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 D")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 D#")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 E")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 F")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 F#")

	PORT_START("P1_6")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // cassette input
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 G")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 G#")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 A")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 A#")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 B")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_NAME("Octave 2 C")

	PORT_START("P1_5")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // cassette input
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_NAME("Octave 2 C#")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_NAME("Octave 2 D")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_NAME("Octave 2 D#")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_NAME("Octave 2 E")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_NAME("Octave 2 F")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_NAME("Octave 2 F#")

	PORT_START("P1_4")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // cassette input
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_NAME("Octave 2 G")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_NAME("Octave 2 G#")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_NAME("Octave 2 A")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_NAME("Octave 2 A#")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_NAME("Octave 2 B")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Octave 3 C")

	PORT_START("P1_3")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // cassette input
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_NAME("Octave 3 C#")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_NAME("Octave 3 D")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_NAME("Octave 3 D#")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_NAME("Octave 3 E")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_NAME("Octave 3 F")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_NAME("Octave 3 F#")

	PORT_START("P1_2")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // cassette input
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_NAME("Octave 3 G")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_NAME("Octave 3 G#")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_NAME("Octave 3 A")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_NAME("Octave 3 A#")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_NAME("Octave 3 B")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_NAME("Octave 4 C")

	PORT_START("P1_1")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // cassette input
	PORT_BIT( 0x7f, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("P1_0")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // cassette input
	PORT_BIT( 0x7f, IP_ACTIVE_HIGH, IPT_UNUSED )

	DRVIF_PORT( 0, "Pause", "Unused1.2", "Unused1.3", "Unused1.4")
	DRVIF_PORT( 1, "Pitch Up", "Pitch Down", "Transposer Up", "Transposer Down")
	DRVIF_PORT( 2, "Memory", "Fingered", "Single Finger", "Off")
	DRVIF_PORT( 3, "Fill In 3", "Fill In 2", "Fill In 1", "Keyboard Percussion")
	DRVIF_PORT( 4, "Orchestra On", "Variation", "Hand Clap 2", "Hand Clap 1")
	DRVIF_PORT( 5, "Pops", "Disco", "Reggae", "Big Band")
	DRVIF_PORT( 6, "March/Polka", "Samba", "Salsa", "Rock'N'Roll")
	DRVIF_PORT( 7, "Intro/Ending", "Start", "Synchro", "Stop")
	DRVIF_PORT( 8, "MIDI Mode", "Unused9.2", "Unused9.3", "Unused9.4")
	DRVIF_PORT( 9, "Brass 1", "Strings", "Pipe Organ", "Jazz Organ")
	DRVIF_PORT(10, "Calliope", "Clarinet", "Brass & Chimes", "Brass 2")
	DRVIF_PORT(11, "Unused12.1", "Unused12.2", "Unused12.3", "Unused12.4")
	DRVIF_PORT(12, "Unused13.1", "Unused13.2", "Unused13.3", "Unused13.4")
	DRVIF_PORT(13, "Solo On", "To Lower", "Trio", "Duet")
	DRVIF_PORT(14, "Sustain", "Stereo Symphonic", "Sustain 2", "Sustain 1")
	DRVIF_PORT(15, "Trumpet", "Violin", "Piccolo", "Jazz Flute")
	DRVIF_PORT(16, "Oboe", "Saxophone", "Horn", "Trombone")
	DRVIF_PORT(17, "Pop Synth", "Percussion 2", "Percussion 1", "Electric Guitar")
	DRVIF_PORT(18, "Bass", "SlapSynth", "Funk Synth/Blues Synth", "Programmer Off")
	DRVIF_PORT(19, "Save", "Record Solo", "Record Orchestra", "Record Chord/Bass")
	DRVIF_PORT(20, "Load", "Play Back Solo", "Play Back Orchestra", "Play Back Chord/Bass")
	DRVIF_PORT(21, "Unused22.1", "Unused22.2", "Unused22.3", "Unused22.4")
	DRVIF_PORT(22, "Unused23.1", "Unused23.2", "Unused23.3", "Unused23.4")

	RYP4_PORT( 1, 75, "Solo Volume")
	RYP4_PORT( 2, 75, "Orchestra Volume")
	RYP4_PORT( 3, 75, "Rhythm Volume")
	RYP4_PORT( 4, 50, "Rhythm Tempo")
	RYP4_PORT( 5, 75, "Chord Volume")
	RYP4_PORT( 6, 75, "Bass Volume")
	RYP4_PORT( 7,  0, "Sustain") PORT_CUSTOM_MEMBER(FUNC(psr60_state::sustain_fuzz))
	RYP4_PORT( 8,  0, "Unused8")
	RYP4_PORT( 9,  0, "Unused9")
	RYP4_PORT(10,  0, "Unused10")

	PORT_START("MASTERVOL")
	PORT_ADJUSTER(50, "PSR Master Volume") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psr60_state::mastervol_changed), 0)
INPUT_PORTS_END

static INPUT_PORTS_START(psr70)
	PORT_START("P1_9")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // cassette input
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 C")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 C#")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 D")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 D#")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 E")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 F")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 F#")

	PORT_START("P1_8")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // cassette input
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 G")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 G#")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 A")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 A#")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 0 B")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 C")

	PORT_START("P1_7")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // cassette input
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 C#")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 D")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 D#")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 E")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 F")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 F#")

	PORT_START("P1_6")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // cassette input
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 G")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 G#")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 A")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 A#")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 1 B")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_NAME("Octave 2 C")

	PORT_START("P1_5")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // cassette input
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_NAME("Octave 2 C#")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_NAME("Octave 2 D")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_NAME("Octave 2 D#")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_NAME("Octave 2 E")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_NAME("Octave 2 F")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_NAME("Octave 2 F#")

	PORT_START("P1_4")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // cassette input
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_NAME("Octave 2 G")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_NAME("Octave 2 G#")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_NAME("Octave 2 A")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_NAME("Octave 2 A#")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_NAME("Octave 2 B")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Octave 3 C")

	PORT_START("P1_3")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // cassette input
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_NAME("Octave 3 C#")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_NAME("Octave 3 D")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_NAME("Octave 3 D#")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_NAME("Octave 3 E")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_NAME("Octave 3 F")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_NAME("Octave 3 F#")

	PORT_START("P1_2")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // cassette input
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_NAME("Octave 3 G")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_NAME("Octave 3 G#")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_NAME("Octave 3 A")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_NAME("Octave 3 A#")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_NAME("Octave 3 B")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_NAME("Octave 4 C")

	PORT_START("P1_1")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // cassette input
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 4 C#")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 4 D")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 4 D#")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 4 E")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 4 F")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 4 F#")

	PORT_START("P1_0")
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // cassette input
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 4 G")
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 4 G#")
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 4 A")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 4 A#")
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 4 B")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Octave 5 C")

	DRVIF_PORT( 0, "Unused1.1", "Unused1.2", "Unused1.3", "Split")
	DRVIF_PORT( 1, "Pitch Up", "Pitch Down", "Transposer Up", "Transposer Down")
	DRVIF_PORT( 2, "Memory", "Fingered", "Single Finger", "Off")
	DRVIF_PORT( 3, "Fill In 3", "Fill In 2", "Fill In 1", "Keyboard Percussion")
	DRVIF_PORT( 4, "Pops", "Disco", "Reggae", "Big Band")
	DRVIF_PORT( 5, "March/Polka", "Samba", "Salsa", "Rock'N'Roll")
	DRVIF_PORT( 6, "Intro/Ending", "Start", "Synchro", "Stop")
	DRVIF_PORT( 7, "MIDI Mode", "Unused9.2", "Unused9.3", "Custom Clear")
	DRVIF_PORT( 8, "Orchestra On", "Variation", "Hand Clap 2", "Hand Clap 1")
	DRVIF_PORT( 9, "Pause", "Custom 3", "Custom 2", "Custom 1")
	DRVIF_PORT(10, "Program", "Rhythm", "Bass", "Chord")
	DRVIF_PORT(11, "Solo On", "To Lower", "Trio", "Duet")
	DRVIF_PORT(12, "Brass 1", "Strings", "Pipe Organ", "Jazz Organ")
	DRVIF_PORT(13, "Calliope", "Clarinet", "Brass & Chimes", "Brass 2")
	DRVIF_PORT(14, "Unused15.1", "Unused15.2", "Unused15.3", "Unused15.4")
	DRVIF_PORT(15, "Unused16.1", "Unused16.2", "Unused16.3", "Unused16.4")
	DRVIF_PORT(16, "Solo Sustain", "Stereo Symphonic", "Sustain 2", "Sustain 1")
	DRVIF_PORT(17, "Trumpet", "Violin", "Piccolo", "Jazz Flute")
	DRVIF_PORT(18, "Oboe", "Saxophone", "Horn", "Trombone")
	DRVIF_PORT(19, "Pop Synth", "Percussion 2", "Percussion 1", "Programmer Off")
	DRVIF_PORT(20, "Registration Memory", "Program 3", "Program 2", "Program 1")
	DRVIF_PORT(21, "Unused22.1", "Record Solo", "Record Orchestra", "Record Chord/Bass")
	DRVIF_PORT(22, "Unused23.1", "Play Back Solo", "Play Back Orchestra", "Play Back Chord/Bass")

	RYP4_PORT( 1, 75, "Solo Volume")
	RYP4_PORT( 2, 75, "Orchestra Volume")
	RYP4_PORT( 3, 75, "Rhythm Volume")
	RYP4_PORT( 4, 50, "Rhythm Tempo")
	RYP4_PORT( 5, 75, "Chord Volume")
	RYP4_PORT( 6, 75, "Bass Volume")
	RYP4_PORT( 7,  0, "Sustain") PORT_CUSTOM_MEMBER(FUNC(psr60_state::sustain_fuzz))
	RYP4_PORT( 8,  0, "Unused8")
	RYP4_PORT( 9,  0, "Unused9")
	RYP4_PORT(10,  0, "Unused10")

	PORT_START("MASTERVOL")
	PORT_ADJUSTER(50, "PSR Master Volume") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(psr60_state::mastervol_changed), 0)
INPUT_PORTS_END

void psr60_state::psr_common(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 6_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &psr60_state::psr60_map);
	m_maincpu->set_addrmap(AS_IO, &psr60_state::psr60_io_map);

	I8255A(config, m_ppi, 6_MHz_XTAL);
	m_ppi->in_pa_callback().set(FUNC(psr60_state::ppi_pa_r));
	m_ppi->out_pb_callback().set(FUNC(psr60_state::ppi_pb_w));
	m_ppi->out_pc_callback().set(FUNC(psr60_state::ppi_pc_w));

	ACIA6850(config, m_acia, 500_kHz_XTAL); // actually an HD6350, differences unknown (if any)
	m_acia->txd_handler().set("mdout", FUNC(midi_port_device::write_txd));
	m_acia->irq_handler().set(FUNC(psr60_state::acia_irq_w));

	MIDI_PORT(config, "mdin", midiin_slot, "midiin").rxd_handler().set(m_acia, FUNC(acia6850_device::write_rxd));

	MIDI_PORT(config, "mdout", midiout_slot, "midiout");

	clock_device &acia_clock(CLOCK(config, "acia_clock", 500_kHz_XTAL));    // 31250 * 16 = 500,000
	acia_clock.signal_handler().set(FUNC(psr60_state::write_acia_clock));

	SPEAKER(config, "speaker", 2).front();

	MIXER(config, m_lmixer);
	m_lmixer->add_route(0, "speaker", 1.0, 0);

	MIXER(config, m_rmixer);
	m_rmixer->add_route(0, "speaker", 1.0, 1);

	// begin BBD filter chain....
	// thanks to Lord Nightmare for figuring this out
	FILTER_BIQUAD(config, m_ic206b);
	m_ic206b->opamp_mfb_lowpass_setup(RES_K(100), 0, RES_K(100), 0, CAP_P(100));
	m_ic206b->add_route(0, m_lmixer, 0.11);
	m_ic206b->add_route(0, m_rmixer, 0.11);

	// not accurate, not correctly modeling the 22k resistor in series with the cap
	FILTER_BIQUAD(config, m_ic206a);
	m_ic206a->opamp_mfb_lowpass_setup(RES_K(33), 0, RES_K(22), 0, CAP_P(0.0039));
	m_ic206a->add_route(0, m_ic206b, 1.0);

	FILTER_BIQUAD(config, m_ic205);
	m_ic205->opamp_sk_lowpass_setup(RES_K(22), RES_K(22), RES_M(999.9), RES_R(0.001), CAP_U(0.0068), CAP_P(82));
	m_ic205->add_route(0, m_ic206a, 1.0);

	FILTER_RC(config, m_postbbd_rc);
	m_postbbd_rc->set_rc(filter_rc_device::LOWPASS, RES_K(22), 0, 0, CAP_U(0.0015));
	m_postbbd_rc->add_route(0, m_ic205, 1.0);

	MIXER(config, m_bbd_mixer);
	m_bbd_mixer->add_route(0, m_postbbd_rc, 1.0);

	MN3204P(config, m_bbd);
	m_bbd->add_route(0, m_bbd_mixer, 1.0);

	FILTER_BIQUAD(config, m_ic204b);
	m_ic204b->opamp_sk_lowpass_setup(RES_K(22), RES_K(22), RES_M(999.9), RES_R(0.001), CAP_U(0.0068), CAP_P(82));
	m_ic204b->add_route(0, m_bbd, 1.0);

	FILTER_RC(config, m_first_rc);
	m_first_rc->set_rc(filter_rc_device::LOWPASS, RES_R(22), 0, 0, CAP_U(0.0015));
	m_first_rc->add_route(0, m_ic204b, 1.0);

	// not accurate, not correctly modeling the 22k resistor bypassing the 22k resistor in series
	// with the cap, and just assuming two 22k resistors in parallel
	FILTER_BIQUAD(config, m_ic204a);
	m_ic204a->opamp_mfb_lowpass_setup(RES_K(11), 0, RES_K(47), 0, CAP_P(150));
	m_ic204a->add_route(0, m_first_rc, 1.0);
	// end BBD filter chain....

	YM3533(config, m_ym3533, 3.579545_MHz_XTAL);
	m_ym3533->irq_handler().set(FUNC(psr60_state::ym_irq_w));
	m_ym3533->add_route(0, m_lmixer, 0.16);     // channel 1 = ORC
	m_ym3533->add_route(0, m_rmixer, 0.16);
	m_ym3533->add_route(1, m_lmixer, 0.22);     // channel 2 = SABC
	m_ym3533->add_route(1, m_rmixer, 0.22);
	m_ym3533->add_route(1, m_ic204a, 1.0);      // routed to BBD via filters

	YM2154(config, m_ryp4, 2.25_MHz_XTAL);
	m_ryp4->irq_handler().set(FUNC(psr60_state::ryp4_irq_w));
	m_ryp4->io_read_handler().set(FUNC(psr60_state::ryp4_an_r));
	m_ryp4->io_write_handler().set(FUNC(psr60_state::ryp4_out_w));
	m_ryp4->add_route(0, m_lmixer, 0.50);
	m_ryp4->add_route(1, m_rmixer, 0.50);
}

void psr60_state::psr60(machine_config &config)
{
	psr_common(config);
	config.set_default_layout(layout_psr60);
}

void psr60_state::psr70(machine_config &config)
{
	psr_common(config);
	config.set_default_layout(layout_psr70);
}

ROM_START( psr60 )
	ROM_REGION(0x8000, "rom1", 0)
	ROM_LOAD("yamaha_psr60_pgm_ic109.bin", 0x000000, 0x008000, CRC(95604916) SHA1(811fb88fc968c58234600eb9fbf1f64e411754cd))

	ROM_REGION(0x8000, "rom2", 0)
	ROM_LOAD("yamaha_psr60_pgm_ic110.bin", 0x000000, 0x008000, CRC(39db8c74) SHA1(7750104d1e5df3357aa553ac58768dbc34051cd8))

	ROM_REGION(0x8000, "ryp4:group0", 0)
	ROM_LOAD("ym21908.bin", 0x0000, 0x8000, CRC(5d0e1d9f) SHA1(dec735ed3df6dc0d81d532186b1073d4ea6290e2))

	ROM_REGION(0x8000, "ryp4:group1", 0)
	ROM_LOAD("ym21909.bin", 0x0000, 0x8000, CRC(a555b42a) SHA1(f96cdea88ffc0af4cf6009529398a0181a91a70c))
ROM_END

ROM_START(psr70)
	ROM_REGION(0x8000, "rom1", 0)
	ROM_LOAD("psr70-rom1.bin", 0x000000, 0x008000, CRC(bf134412) SHA1(318f33f8ef5e2d865e8ae657993763c9e032af8e))

	ROM_REGION(0x8000, "rom2", 0)
	ROM_LOAD("yamaha_psr60_pgm_ic110.bin", 0x000000, 0x008000, CRC(39db8c74) SHA1(7750104d1e5df3357aa553ac58768dbc34051cd8))

	ROM_REGION(0x8000, "ryp4:group0", 0)
	ROM_LOAD("ym21908.bin", 0x0000, 0x8000, CRC(5d0e1d9f) SHA1(dec735ed3df6dc0d81d532186b1073d4ea6290e2))

	ROM_REGION(0x8000, "ryp4:group1", 0)
	ROM_LOAD("ym21909.bin", 0x0000, 0x8000, CRC(a555b42a) SHA1(f96cdea88ffc0af4cf6009529398a0181a91a70c))
ROM_END

} // namespace

CONS(1985, psr60, 0,     0, psr60, psr60, psr60_state, empty_init, "Yamaha", "PSR-60 PortaSound", MACHINE_IMPERFECT_SOUND)
CONS(1985, psr70, psr60, 0, psr70, psr70, psr60_state, empty_init, "Yamaha", "PSR-70 PortaSound", MACHINE_IMPERFECT_SOUND)



ymqs300.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert

#include "emu.h"

#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/h8/h83002.h"
#include "machine/nvram.h"
#include "sound/swp00.h"
#include "video/t6963c.h"

#include "screen.h"
#include "speaker.h"

#include "utf8.h"

namespace {

class qs300_state : public driver_device {
public:
	qs300_state(const machine_config &mconfig, device_type type, const char *tag) :
		qs300_state(mconfig, type, tag, false)
	{
	}

	void qs300(machine_config &config);

protected:
	qs300_state(const machine_config &mconfig, device_type type, const char *tag, bool is_eos) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_subcpu(*this, "subcpu"),
		m_swp00(*this, "swp00"),
		m_lcdc(*this, "vs254300"),
		m_nvram(*this, "ram"),
		m_inputs(*this, "DR%u", 0U),
		m_is_eos(is_eos)
	{
	}

	required_device<h83002_device> m_maincpu;
	required_device<h83002_device> m_subcpu;
	required_device<swp00_device> m_swp00;
	required_device<t6963c_device> m_lcdc;
	required_device<nvram_device> m_nvram;
	required_ioport_array<7> m_inputs;
	//required_ioport m_sustain;
	//required_ioport m_pitch_bend;

	bool const m_is_eos;

	u8 m_mlatch, m_slatch;
	bool m_mlatch_full, m_slatch_full;

	u8 m_mpb;

	void mainmap(address_map &map) ATTR_COLD;
	void submap(address_map &map) ATTR_COLD;
	void lcdmap(address_map &map) ATTR_COLD;

	u8 mlatch_r();
	void mlatch_w(u8 data);
	u8 slatch_r();
	void slatch_w(u8 data);

	u8 mp6_r();
	void mp6_w(u8 data);
	u8 mp7_r();
	u8 mpa_r();
	u8 mpb_r();
	void mpb_w(u8 data);

	u8 sp9_r();
	u8 spa_r();

	void lcd_palette(palette_device &palette) const;

	void machine_start() override ATTR_COLD;
};

class eos_b900_state : public qs300_state
{
public:
	eos_b900_state(const machine_config &mconfig, device_type type, const char *tag) :
		qs300_state(mconfig, type, tag, true)
	{
	}
};

void qs300_state::machine_start()
{
	save_item(NAME(m_mlatch));
	save_item(NAME(m_mlatch_full));
	save_item(NAME(m_slatch));
	save_item(NAME(m_slatch_full));
	save_item(NAME(m_mpb));

	m_mlatch = 0;
	m_mlatch_full = false;
	m_slatch = 0;
	m_slatch_full = false;
	m_mpb = 0;
}

u8 qs300_state::mlatch_r()
{
	if(!machine().side_effects_disabled()) {
		m_mlatch_full = false;
		m_maincpu->set_input_line(4, CLEAR_LINE);
	}
	return m_mlatch;
}

void qs300_state::mlatch_w(u8 data)
{
	m_mlatch = data;
	m_mlatch_full = true;
	m_maincpu->set_input_line(4, ASSERT_LINE);
}

u8 qs300_state::mpa_r()
{
	return (m_slatch_full ? 0x40 : 0x00) | (m_is_eos ? 0x00 : 0x20);
}

u8 qs300_state::slatch_r()
{
	if(!machine().side_effects_disabled()) {
		m_slatch_full = false;
		m_subcpu->set_input_line(4, CLEAR_LINE);
	}
	return m_slatch;
}

void qs300_state::slatch_w(u8 data)
{
	m_slatch = data;
	m_slatch_full = true;
	m_subcpu->set_input_line(4, ASSERT_LINE);
}

u8 qs300_state::spa_r()
{
	return m_mlatch_full ? 0x40 : 0x00;
}

u8 qs300_state::mpb_r()
{
	return m_mpb;
}

void qs300_state::mpb_w(u8 data)
{
	m_mpb = data;
}

u8 qs300_state::mp6_r()
{
	return 0xff;
}

void qs300_state::mp6_w(u8 data)
{
	// Bits 1-2 are related to leds
}

u8 qs300_state::mp7_r()
{
	// Some bits are inverted with transistors for led driving reasons.
	u8 mask = m_mpb ^ 0x1b;
	u8 res = 0;
	for(u32 i=0; i != 7; i++)
		if(BIT(mask, i))
			res |= m_inputs[i]->read();

	return res;
}

u8 qs300_state::sp9_r()
{
	// 0x20 = suspend pedal
	return 0x00;
}

void qs300_state::lcd_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(2, 176, 219));
	palette.set_pen_color(1, rgb_t(0, 0, 0));
}

void qs300_state::qs300(machine_config &config)
{
	H83002(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &qs300_state::mainmap);
	m_maincpu->read_port6().set(FUNC(qs300_state::mp6_r));
	m_maincpu->write_port6().set(FUNC(qs300_state::mp6_w));
	m_maincpu->read_port7().set(FUNC(qs300_state::mp7_r));
	m_maincpu->read_porta().set(FUNC(qs300_state::mpa_r));
	m_maincpu->read_portb().set(FUNC(qs300_state::mpb_r));
	m_maincpu->write_portb().set(FUNC(qs300_state::mpb_w));

	H83002(config, m_subcpu, 16_MHz_XTAL);
	m_subcpu->read_adc<0>().set_constant(0);     // Aftertouch
	m_subcpu->read_adc<1>().set_constant(0);     // Pitch bend
	m_subcpu->read_adc<2>().set_constant(0);     // Modulation wheel
	m_subcpu->read_adc<3>().set_constant(0x3ff); // Generic continuous controller, wired to +5V
	m_subcpu->read_adc<4>().set_constant(0);     // Foot control
	m_subcpu->read_adc<5>().set_constant(0);     // Foot volume
	m_subcpu->read_adc<6>().set_constant(0x3ff); // Unconnected
	m_subcpu->read_adc<7>().set_constant(0x276); // Battery (3V)
	m_subcpu->set_addrmap(AS_PROGRAM, &qs300_state::submap);
	m_subcpu->read_port9().set(FUNC(qs300_state::sp9_r));
	m_subcpu->read_porta().set(FUNC(qs300_state::spa_r));

	SWP00(config, m_swp00);
	m_swp00->add_route(0, "speaker", 1.0, 0);
	m_swp00->add_route(1, "speaker", 1.0, 1);

	T6963C(config, m_lcdc, 270000);
	m_lcdc->set_addrmap(0, &qs300_state::lcdmap);
	m_lcdc->set_fs(2);
	m_lcdc->set_md(0x13);

	PALETTE(config, "palette", FUNC(qs300_state::lcd_palette), 2);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_size(240, 80);
	screen.set_visarea(0, 239, 0, 63);
	screen.set_screen_update("vs254300", FUNC(t6963c_device::screen_update));
	screen.set_palette("palette");

	NVRAM(config, m_nvram, nvram_device::DEFAULT_NONE);

	SPEAKER(config, "speaker", 2).front();

	auto &mdin(MIDI_PORT(config, "mdin"));
	midiin_slot(mdin);
	mdin.rxd_handler().set(m_maincpu, FUNC(h83002_device::sci_rx_w<0>));

	auto &mdout(MIDI_PORT(config, "mdout"));
	midiout_slot(mdout);
	m_maincpu->write_sci_tx<0>().set(mdout, FUNC(midi_port_device::write_txd));

	// Annoying but required for faster inter-cpu communication
	config.set_maximum_quantum(attotime::from_usec(10));
}

void qs300_state::mainmap(address_map &map)
{
	map(0x000000, 0x1fffff).rom().region("maincpu", 0);
	map(0x200000, 0x207fff).ram().share("ram");
	map(0x420000, 0x420000).rw(FUNC(qs300_state::mlatch_r), FUNC(qs300_state::slatch_w));
	map(0x440000, 0x440001).rw(m_lcdc, FUNC(t6963c_device::read), FUNC(t6963c_device::write));

	// 480000: fdc

	map(0x600000, 0x67ffff).ram().mirror(0x180000);
}

void qs300_state::submap(address_map &map)
{
	map(0x000000, 0x07ffff).rom().region("subcpu", 0);
	map(0x200000, 0x2007ff).m(m_swp00, FUNC(swp00_device::map));
	map(0x400000, 0x400000).rw(FUNC(qs300_state::slatch_r), FUNC(qs300_state::mlatch_w));
	map(0x600000, 0x61ffff).ram();
}

void qs300_state::lcdmap(address_map &map)
{
	map(0x0000, 0x7fff).ram();
}

static INPUT_PORTS_START( qs300 )
	PORT_START("DR0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Song")     PORT_CODE(KEYCODE_A)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Voice")    PORT_CODE(KEYCODE_S)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Shift")    PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0xf8, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("DR1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Pattern")  PORT_CODE(KEYCODE_D)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Phrase")   PORT_CODE(KEYCODE_F)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("F1")       PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("F2")       PORT_CODE(KEYCODE_W)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("7")        PORT_CODE(KEYCODE_7)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("8")        PORT_CODE(KEYCODE_8)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("9")        PORT_CODE(KEYCODE_9)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("DR2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Utility")  PORT_CODE(KEYCODE_G)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Disk")     PORT_CODE(KEYCODE_H)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("F3")       PORT_CODE(KEYCODE_E)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("F4")       PORT_CODE(KEYCODE_R)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("4")        PORT_CODE(KEYCODE_4)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("5")        PORT_CODE(KEYCODE_5)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("6")        PORT_CODE(KEYCODE_6)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("DR3")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Record")   PORT_CODE(KEYCODE_J)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Top")      PORT_CODE(KEYCODE_K)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("F5")       PORT_CODE(KEYCODE_T)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("F6")       PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("1")        PORT_CODE(KEYCODE_1)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("2")        PORT_CODE(KEYCODE_2)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("3")        PORT_CODE(KEYCODE_3)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("DR4")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Stop")     PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Run")      PORT_CODE(KEYCODE_X)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("F7")       PORT_CODE(KEYCODE_U)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("F8")       PORT_CODE(KEYCODE_I)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("0")        PORT_CODE(KEYCODE_0)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("-")        PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Enter")    PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("DR5")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("<<")       PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME(">>")       PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Exit")     PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Dec/No")   PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME(UTF8_UP)    PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Inc/Yes")  PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)

	PORT_START("DR6")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Edit")     PORT_CODE(KEYCODE_C)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Job")      PORT_CODE(KEYCODE_V)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Store")    PORT_CODE(KEYCODE_B)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME(UTF8_LEFT)  PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME(UTF8_DOWN)  PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

ROM_START( qs300 )
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "xq055e0.ic04", 0x000000, 0x100000, CRC(abdbcc1f) SHA1(998c3cd5b14d407cb0e1fcdea9a9f585cf73fe5a))
	ROM_LOAD16_WORD_SWAP( "xq320e0.ic24", 0x100000, 0x100000, CRC(5fe1a151) SHA1(7e06716795fdd24e33a922a1f3fe77e6082b2abb))

	ROM_REGION( 0x80000, "subcpu", 0 )
	ROM_LOAD16_WORD_SWAP( "xq056e0.ic09", 0, 0x80000, CRC(ff0ed0f9) SHA1(be80b5e7c701db708e435c2c825d562cf20a284e))

	ROM_REGION16_LE( 0x400000, "swp00", 0)
	// Identical to the mu50 roms
	ROM_LOAD( "xq057c0.ic10", 0x000000, 0x200000, CRC(d4adbc7e) SHA1(32f653c7644d060f5a6d63a435ae3a7412386d92) )
	ROM_LOAD( "xq058c0.ic11", 0x200000, 0x200000, CRC(7b68f475) SHA1(adf68689b4842ec5bc9b0ea1bb99cf66d2dec4de) )

	ROM_REGION(0x400, "vs254300:cgrom", 0)
	ROM_LOAD("t6963c_0101.bin", 0x000, 0x400, CRC(547d118b) SHA1(0dd3e3acd3d47e6ece644c98c390fc86587373e9))
	// This t6963c_0101 internal CG ROM is similar to lm24014w_0101.bin which may be used as a replacement
ROM_END

ROM_START( eosb900 )
	// Identical afaict. The firmware can almost but not quite handle the sdx 4000 too.
	ROM_REGION( 0x200000, "maincpu", 0 )
	ROM_LOAD16_WORD_SWAP( "xq055e0.ic04", 0x000000, 0x100000, CRC(abdbcc1f) SHA1(998c3cd5b14d407cb0e1fcdea9a9f585cf73fe5a))
	ROM_LOAD16_WORD_SWAP( "xq320e0.ic24", 0x100000, 0x100000, CRC(5fe1a151) SHA1(7e06716795fdd24e33a922a1f3fe77e6082b2abb))

	ROM_REGION( 0x80000, "subcpu", 0 )
	ROM_LOAD16_WORD_SWAP( "xq056e0.ic09", 0, 0x80000, CRC(ff0ed0f9) SHA1(be80b5e7c701db708e435c2c825d562cf20a284e))

	ROM_REGION16_LE( 0x400000, "swp00", 0)
	// Identical to the mu50 roms
	ROM_LOAD( "xq057c0.ic10", 0x000000, 0x200000, CRC(d4adbc7e) SHA1(32f653c7644d060f5a6d63a435ae3a7412386d92) )
	ROM_LOAD( "xq058c0.ic11", 0x200000, 0x200000, CRC(7b68f475) SHA1(adf68689b4842ec5bc9b0ea1bb99cf66d2dec4de) )

	ROM_REGION(0x400, "vs254300:cgrom", 0)
	ROM_LOAD("t6963c_0101.bin", 0x000, 0x400, CRC(547d118b) SHA1(0dd3e3acd3d47e6ece644c98c390fc86587373e9))
	// This t6963c_0101 internal CG ROM is similar to lm24014w_0101.bin which may be used as a replacement
ROM_END

} // anonymous namespace

SYST( 1999, qs300,       0, 0, qs300, qs300, qs300_state,    empty_init, "Yamaha", "QS300",    MACHINE_NOT_WORKING )
SYST( 1999, eosb900, qs300, 0, qs300, qs300, eos_b900_state, empty_init, "Yamaha", "EOS B900", MACHINE_NOT_WORKING )



ymqy70.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders: BALATON Zoltan

#include "emu.h"

#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/h8/h83002.h"
#include "machine/nvram.h"
//#include "sound/swp00.h"
#include "video/t6963c.h"

#include "speaker.h"


namespace {

static INPUT_PORTS_START(qy70)
	PORT_START("SWA")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Record")    PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Top")   PORT_CODE(KEYCODE_C)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Stop")  PORT_CODE(KEYCODE_X)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Backward")  PORT_CODE(KEYCODE_V)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Play")  PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Forward")   PORT_CODE(KEYCODE_B)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Song")  PORT_CODE(KEYCODE_J)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Pattern")   PORT_CODE(KEYCODE_K)

	PORT_START("SWB")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Menu")  PORT_CODE(KEYCODE_M)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F1")    PORT_CODE(KEYCODE_A)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F2")    PORT_CODE(KEYCODE_S)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F3")    PORT_CODE(KEYCODE_D)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F4")    PORT_CODE(KEYCODE_F)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Exit")  PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("SWC")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Left")  PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Up")    PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Down")  PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("SWD")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OctDown")   PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("OctUp") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("E2")    PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F2")    PORT_CODE(KEYCODE_W)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Gb2")   PORT_CODE(KEYCODE_3)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G2")    PORT_CODE(KEYCODE_E)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Ab2")   PORT_CODE(KEYCODE_4)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A2")    PORT_CODE(KEYCODE_R)

	PORT_START("SWE")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Bb2")   PORT_CODE(KEYCODE_5)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("B2")    PORT_CODE(KEYCODE_T)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C3")    PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Db3")   PORT_CODE(KEYCODE_7)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D3")    PORT_CODE(KEYCODE_U)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Eb3")   PORT_CODE(KEYCODE_8)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("E3")    PORT_CODE(KEYCODE_I)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("F3")    PORT_CODE(KEYCODE_O)

	PORT_START("SWF")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Gb3")   PORT_CODE(KEYCODE_0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("G3")    PORT_CODE(KEYCODE_P)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Ab3")   PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("A3")    PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Bb3")   PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("B3")    PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("C4")    PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Db4")   PORT_CODE(KEYCODE_L)

	PORT_START("SWG")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("D4")    PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Eb4")   PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("E4")    PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Dec")   PORT_CODE(KEYCODE_DEL)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Inc")   PORT_CODE(KEYCODE_PGDN)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END

class qy70_state : public driver_device
{
public:
	qy70_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_lcdc(*this, "lcdc")
		, m_switches(*this, "SW%c", 'A')
	{ }

	void qy70(machine_config &config);

private:
	required_device<h83002_device> m_maincpu;
	required_device<t6963c_device> m_lcdc;

	required_ioport_array<7> m_switches;

	u8 sw_sel;

	void pa_w(u8 data) { if (data) sw_sel = data; }
	u8 sw_in();
	u16 adc_bkupbat_r() { return 0x2a0; }
	void mem_map(address_map &map) ATTR_COLD;
	void lcd_map(address_map &map) ATTR_COLD;
	void lcd_palette(palette_device &palette) const;
	virtual void machine_start() override ATTR_COLD;
};

void qy70_state::machine_start()
{
	sw_sel = 0xff;
}

u8 qy70_state::sw_in()
{
	if (sw_sel == 0xff)
		return 0x1f; // Start in test mode for now. TODO: This should be an option.
	int idx = 6;
	while (idx && sw_sel != (1 << idx))
		idx--;
	return m_switches[idx]->read();
}

void qy70_state::mem_map(address_map &map)
{
	map(0x000000, 0x1fffff).rom().region("mainprog", 0);
	map(0x200000, 0x21ffff).ram().share("userdata.nv");
	//map(0x400000, 0x43ffff).rw("tosubcpu", ...);
	map(0x440000, 0x440001).rw("lcdc", FUNC(t6963c_device::read), FUNC(t6963c_device::write));
	map(0x480000, 0x480000).r(FUNC(qy70_state::sw_in));
	map(0x600000, 0x607fff).ram().share("workdata.nv");
}

void qy70_state::lcd_map(address_map &map)
{
	map(0x0000, 0x7fff).ram();
}

void qy70_state::lcd_palette(palette_device &palette) const
{
	palette.set_pen_color(0, rgb_t(138, 146, 148));
	palette.set_pen_color(1, rgb_t(69, 62, 66));
}

void qy70_state::qy70(machine_config &config)
{
	H83002(config, m_maincpu, 10_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &qy70_state::mem_map);
	// ADC_0  Power battery voltage
	// ADC_2  Host type select: 5V Mac, 3.33V PC-1, 1.66V PC-2, 0V MIDI
	m_maincpu->read_adc<4>().set(FUNC(qy70_state::adc_bkupbat_r));
	m_maincpu->write_porta().set(FUNC(qy70_state::pa_w));
	// PORT_B bit 0 1MHz output for Mac serial, bit 1 Rec LED, bit 2 Play LED,
	//        bit 3 lcdc reset, bit 4 GND, bit 5 subcpu SBSY, bit 6-7 subcpu

	NVRAM(config, "userdata.nv", nvram_device::DEFAULT_NONE);
	NVRAM(config, "workdata.nv", nvram_device::DEFAULT_NONE);

	T6963C(config, m_lcdc, 0);
	m_lcdc->set_addrmap(0, &qy70_state::lcd_map);
	m_lcdc->set_fs(2);
	m_lcdc->set_md(8);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_size(128, 64);
	screen.set_visarea_full();
	screen.set_screen_update("lcdc", FUNC(t6963c_device::screen_update));
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(qy70_state::lcd_palette), 2);

	SPEAKER(config, "speaker", 2).front();

	auto &mdin_a(MIDI_PORT(config, "mdin_a"));
	midiin_slot(mdin_a);
	mdin_a.rxd_handler().set(m_maincpu, FUNC(h83002_device::sci_rx_w<1>));

	auto &mdin_b(MIDI_PORT(config, "mdin_b"));
	midiin_slot(mdin_b);
	mdin_b.rxd_handler().set(m_maincpu, FUNC(h83002_device::sci_rx_w<0>));

	auto &mdout(MIDI_PORT(config, "mdout_a"));
	midiout_slot(mdout);
	m_maincpu->write_sci_tx<0>().set(mdout, FUNC(midi_port_device::write_txd));
}

ROM_START(qy70)
	ROM_REGION16_BE(0x200000, "mainprog", 0)
	ROM_LOAD("qy70main.bin", 0x000000, 0x200000, CRC(c581164b) SHA1(6f804cb6956dff50c5b64fb2f695bb36e3f278ec))

	ROM_REGION(0x400, "lcdc:cgrom", 0)
	ROM_LOAD("t6963c_0101.bin", 0x000, 0x400, CRC(547d118b) SHA1(0dd3e3acd3d47e6ece644c98c390fc86587373e9))
	// This t6963c_0101 internal CG ROM is similar to lm24014w_0101.bin which may be used as a replacement
ROM_END

} // anonymous namespace


CONS(1997, qy70, 0, 0, qy70, qy70, qy70_state, empty_init, "Yamaha", "QY70 Music Sequencer", MACHINE_NOT_WORKING)



ymrx15.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Skeleton driver for Yamaha RX15 drum machine.

****************************************************************************/

#include "emu.h"
#include "cpu/m6800/m6801.h"
#include "sound/ym2154.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

namespace {

class rx15_state : public driver_device
{
public:
	rx15_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ryp4(*this, "ryp4")
	{
	}

	void rx15(machine_config &config);

private:
	HD44780_PIXEL_UPDATE(pixel_update);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<hd6303x_cpu_device> m_maincpu;
	required_device<ym2154_device> m_ryp4;
};


HD44780_PIXEL_UPDATE(rx15_state::pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 8)
		bitmap.pix(y, (line * 8 + pos) * 6 + x) = state;
}

void rx15_state::mem_map(address_map &map)
{
	map(0x1000, 0x1000).nopr();
	map(0x2000, 0x207f).rw(m_ryp4, FUNC(ym2154_device::read), FUNC(ym2154_device::write));
	map(0x3000, 0x3001).rw("lcdc", FUNC(hd44780_device::read), FUNC(hd44780_device::write));
	map(0x7800, 0x7fff).ram(); // NVRAM #1?
	map(0x8000, 0x87ff).ram(); // NVRAM #2?
	map(0xc000, 0xffff).rom().region("firmware", 0);
}


static INPUT_PORTS_START(rx15)
INPUT_PORTS_END

void rx15_state::rx15(machine_config &config)
{
	HD6303X(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &rx15_state::mem_map);

	//NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // M5M5118P-15L (one or both battery-backed?)

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6*16, 8*1);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	hd44780_device &lcdc(HD44780(config, "lcdc", 270'000)); // TODO: clock not measured, datasheet typical clock used
	lcdc.set_lcd_size(2, 8);
	lcdc.set_pixel_update_cb(FUNC(rx15_state::pixel_update));

	SPEAKER(config, "speaker", 2).front();

	YM2154(config, m_ryp4, 2.7_MHz_XTAL);
	m_ryp4->add_route(0, "speaker", 0.50, 0);
	m_ryp4->add_route(1, "speaker", 0.50, 1);
}

ROM_START(rx15)
	ROM_REGION(0x4000, "firmware", 0)
	ROM_LOAD("yamaharx15_v1-0_hn27128ag.bin", 0x0000, 0x4000, CRC(0ca8e3d4) SHA1(d09cdb3686b15603660abec49848ef451005be62))

	ROM_REGION(0x8000, "ryp4:group0", 0)
	ROM_LOAD("ym21901.bin", 0x0000, 0x8000, NO_DUMP)

	ROM_REGION(0x8000, "ryp4:group1", 0)
	ROM_LOAD("ym21902.bin", 0x0000, 0x8000, NO_DUMP)

	ROM_REGION(0x8000, "ryp4:group2", 0)
	ROM_LOAD("ym21903.bin", 0x0000, 0x8000, NO_DUMP)

	ROM_REGION(0x8000, "ryp4:group3", 0)
	ROM_LOAD("ym21907.bin", 0x0000, 0x8000, NO_DUMP)
ROM_END

} // anonymous namespace

SYST(1984, rx15, 0, 0, rx15, rx15, rx15_state, empty_init, "Yamaha", "RX15 Digital Rhythm Programmer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



ymsy35.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/*******************************************************************************

    Skeleton driver for Yamaha SY35 AWM/FM vector synthesizer.

*******************************************************************************/

#include "emu.h"
//#include "bus/midi/midi.h"
#include "cpu/h8500/h8520.h"
#include "cpu/m6805/hd6305.h"

namespace {

class yamaha_sy35_state : public driver_device
{
public:
	yamaha_sy35_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_scancpu(*this, "scancpu")
	{
	}

	void sy35(machine_config &config);

private:
	void mem_map(address_map &map) ATTR_COLD;

	required_device<h8520_device> m_maincpu;
	required_device<cpu_device> m_scancpu;
};

void yamaha_sy35_state::mem_map(address_map &map)
{
	map(0x00000, 0x1ffff).rom().region("program", 0);
	map(0x20000, 0x27fff).mirror(0x8000).ram();
	//map(0x30000, 0x3ffff).rw("card", FUNC(sy35_card_device::read), FUNC(sy35_card_device::write));
	map(0x80000, 0xbffff).rom().region("sound_make", 0);
}

static INPUT_PORTS_START(sy35)
INPUT_PORTS_END

void yamaha_sy35_state::sy35(machine_config &config)
{
	HD6435208(config, m_maincpu, 20_MHz_XTAL); // HD6435208A00P or HD6475208P
	m_maincpu->set_mode(3); // internal ROM disabled
	m_maincpu->set_addrmap(AS_PROGRAM, &yamaha_sy35_state::mem_map);

	HD6305V0(config, m_scancpu, 8_MHz_XTAL).set_disable(); // HD63B05V0C85P

	//TMC3493PH(config, "gew5a", 12.8_MHz_XTAL);
	//TMC3493PH(config, "gew5b", 12.8_MHz_XTAL);
	//YM3413(config, "ldsp");
}

ROM_START(sy35)
	ROM_REGION(0x20000, "program", 0)
	ROM_LOAD("xk932a0.ic7", 0x00000, 0x20000, CRC(e86f47ac) SHA1(e14e309ba09e0f6de165e9366e199b74d1411f6c)) // 27C010 (second half blank)

	ROM_REGION(0x40000, "sound_make", 0)
	ROM_LOAD("xl401a0.ic6", 0x00000, 0x40000, CRC(614dfe1b) SHA1(7c2edd2bb6157d95b0f54f712fc89666846caead)) // 27C020

	ROM_REGION(0x1000, "scancpu", 0)
	ROM_LOAD("xh257a00.ic2", 0x0000, 0x1000, NO_DUMP)

	ROM_REGION(0x200000, "voice", 0)
	ROM_LOAD("xl429a00.ic8", 0x000000, 0x100000, NO_DUMP)
	ROM_LOAD("xl430a00.ic9", 0x100000, 0x100000, NO_DUMP)
ROM_END

} // anonymous namespace

SYST(1992, sy35, 0, 0, sy35, sy35, yamaha_sy35_state, empty_init, "Yamaha", "SY35 Music Synthesizer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



ymtx81z.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/****************************************************************************

    Driver for Yamaha TX81Z.

****************************************************************************/

#include "emu.h"

#include "bus/midi/midi.h"
#include "cpu/m6800/m6801.h"
#include "machine/clock.h"
#include "machine/nvram.h"
#include "sound/ymopz.h"
#include "video/hd44780.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "tx81z.lh"


namespace {

class ymtx81z_state : public driver_device
{
public:
	ymtx81z_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_port2(*this, "P2")
	{
	}

	void tx81z(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	HD44780_PIXEL_UPDATE(lcd_pixel_update);
	void palette_init(palette_device &palette);

	void mem_map(address_map &map) ATTR_COLD;

	u8 p2_r();
	void midi_rx_r(int state) { m_rx_data = state; }
	void midiclock_w(int state) { if (state) m_maincpu->clock_serial(); }

	required_device<hd6303x_cpu_device> m_maincpu;
	required_ioport m_port2;

	int m_rx_data;
};

HD44780_PIXEL_UPDATE(ymtx81z_state::lcd_pixel_update)
{
	if (x < 5 && y < 8 && line < 2 && pos < 16)
		bitmap.pix(line * 10 + y + 1 + ((y == 7) ? 1 : 0), pos * 6 + x + 1) = state ? 1 : 2;
}

void ymtx81z_state::palette_init(palette_device &palette)
{
	palette.set_pen_color(0, rgb_t(0x00, 0x00, 0x00)); // background
	palette.set_pen_color(1, rgb_t(0xff, 0xff, 0xff)); // lcd pixel on
	palette.set_pen_color(2, rgb_t(0x18, 0x18, 0x18)); // lcd pixel off
}

void ymtx81z_state::machine_start()
{
	membank("rombank")->configure_entries(0, 2, memregion("program")->base(), 0x8000);
	m_rx_data = ASSERT_LINE;
}

void ymtx81z_state::mem_map(address_map &map)
{
	map(0x2000, 0x2001).mirror(0x1ffe).rw("ymsnd", FUNC(ym2414_device::read), FUNC(ym2414_device::write));
	map(0x4000, 0x4001).mirror(0x1ffe).rw("lcdc", FUNC(hd44780_device::read), FUNC(hd44780_device::write));
	map(0x6000, 0x7fff).ram().share("nvram");
	map(0x8000, 0xffff).bankr("rombank");
}

u8 ymtx81z_state::p2_r()
{
	u8 result = m_port2->read() & 0xf7;
	result |= (m_rx_data << 3);
	return result;
}

static INPUT_PORTS_START(tx81z)
	PORT_START("P2")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED) // actually cassette data
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED) // actually 500 kHz clock
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED) // actually MIDI In data
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("Cursor")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_VOLUME_UP) PORT_NAME(u8"Master Volume \u2192") // →
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_VOLUME_DOWN) PORT_NAME(u8"Master Volume \u2190") // ←

	PORT_START("P5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED) // actually INT1 from YM2414
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON9) PORT_NAME("Store/Eq Copy")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED) // actually MR from analog wait circuit tied to LCDE
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("Data Entry Inc")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_BUTTON3) PORT_NAME("Data Entry Dec")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_BUTTON4) PORT_NAME(u8"Voice Parameter \u2192") // →
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_BUTTON5) PORT_NAME(u8"Voice Parameter \u2190") // ←

	PORT_START("P6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON6) PORT_NAME("Play/Perform")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON7) PORT_NAME("Edit/Compare")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON8) PORT_NAME("Utility")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0xf0, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END

void ymtx81z_state::tx81z(machine_config &config)
{
	HD6303X(config, m_maincpu, 7.15909_MHz_XTAL); // HD63B03XP
	m_maincpu->set_addrmap(AS_PROGRAM, &ymtx81z_state::mem_map);
	m_maincpu->in_p2_cb().set(FUNC(ymtx81z_state::p2_r));
	m_maincpu->in_p5_cb().set_ioport("P5");
	m_maincpu->in_p6_cb().set_ioport("P6");
	m_maincpu->out_p6_cb().set_membank("rombank").bit(3);
	m_maincpu->out_p6_cb().append_output("led1").bit(4);
	m_maincpu->out_p6_cb().append_output("led2").bit(5);
	m_maincpu->out_p6_cb().append_output("led3").bit(6);
	m_maincpu->out_p6_cb().append_output("led4").bit(7);

	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // TC5564PL-15/-20 + CR2032 battery

	auto &midiclock(CLOCK(config, "midiclock", 500_kHz_XTAL));
	midiclock.signal_handler().set(FUNC(ymtx81z_state::midiclock_w));

	MIDI_PORT(config, "mdin", midiin_slot, "midiin").rxd_handler().set(FUNC(ymtx81z_state::midi_rx_r));

	auto &mdout(MIDI_PORT(config, "mdout", midiout_slot, "midiout"));
	m_maincpu->out_ser_tx_cb().set(mdout, FUNC(midi_port_device::write_txd));

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
	screen.set_size(6*16+1, 10*2+1);
	screen.set_visarea_full();
	screen.set_palette("palette");

	PALETTE(config, "palette", FUNC(ymtx81z_state::palette_init), 3);

	config.set_default_layout(layout_tx81z);

	hd44780_device &lcdc(HD44780(config, "lcdc", 270'000)); // TODO: clock not measured, datasheet typical clock used
	lcdc.set_lcd_size(2, 16);
	lcdc.set_pixel_update_cb(FUNC(ymtx81z_state::lcd_pixel_update));

	SPEAKER(config, "speaker", 2).front();

	ym2414_device &ymsnd(YM2414(config, "ymsnd", 7.15909_MHz_XTAL / 2));
	ymsnd.irq_handler().set_inputline(m_maincpu, HD6301_IRQ1_LINE);
	ymsnd.add_route(0, "speaker", 0.60, 0);
	ymsnd.add_route(1, "speaker", 0.60, 1);
}

ROM_START(tx81z)
	ROM_REGION(0x10000, "program", 0)
	ROM_SYSTEM_BIOS(0, "v16", "Version 1.6")
	ROMX_LOAD("tx81z-v1.6.ic15", 0x00000, 0x10000, CRC(ab9b7347) SHA1(208a72c0dc615825c442240e520a6a3c5fe860ea), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "v15", "Version 1.5")
	ROMX_LOAD("tx81z-27512-image-version-1_5.ic15", 0x00000, 0x10000, CRC(64ab615b) SHA1(82cdd8637caf3828aee5ccf25f1ed92ae5d65d3b), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "v14", "Version 1.4")
	ROMX_LOAD("tx81z-v1.4.ic15", 0x00000, 0x10000, CRC(694a13e2) SHA1(0b656a8040748f1e4ee73df2a9436fee1c724be8), ROM_BIOS(2))
	ROM_SYSTEM_BIOS(3, "v13", "Version 1.3")
	ROMX_LOAD("tx81z-v1.3.ic15", 0x00000, 0x10000, CRC(7abd5a61) SHA1(93ae5498ce650fe09952ca81c9ac3821f44c20dc), ROM_BIOS(3))
	ROM_SYSTEM_BIOS(4, "v12", "Version 1.2")
	ROMX_LOAD("tx81z-v1.2.ic15", 0x00000, 0x10000, CRC(8378f744) SHA1(d76b573d4deb67f0e1553c9c17804e970b392803), ROM_BIOS(4))
	ROM_SYSTEM_BIOS(5, "v11", "Version 1.1")
	ROMX_LOAD("tx81z-v1.1.ic15", 0x00000, 0x10000, CRC(3e78db9f) SHA1(52eafb9a1cb3ffb68e8b8dd7a2b85d9e607f9e1c), ROM_BIOS(5))
	ROM_SYSTEM_BIOS(6, "v10", "Version 1.0")
	ROMX_LOAD("tx81z-27512-image-first-version-1_0.ic15", 0x00000, 0x10000, CRC(2f9628fa) SHA1(ce62dfb9a86da092c469fd25328b5447375f5bb2), ROM_BIOS(6))
ROM_END

} // anonymous namespace


SYST(1987, tx81z, 0, 0, tx81z, tx81z, ymtx81z_state, empty_init, "Yamaha", "TX81Z FM Tone Generator", MACHINE_IMPERFECT_SOUND)



ymvl1.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert

// Yamaha VL1 synthesizer

// The VL1-m rackable version exists but we don't have the firmware

// https://www.synthxl.com/offwp/yamaha_vl1_m_service_manual.pdf

// Address decode:
//  2222 1111 1111 1100 0000 0000
//  3210 9876 5432 1098 7654 3210

//  1... .... 000. .... ..0. .... .w led
//  1... .... 010. .... ..0. .... r. pks
//  1... .... 011. .... ..0. .... r. adc
//
//  1... .... 1000 .... .... .... rw vop (dspv #4)
//  1... .... 1010 .... .... .... rw lcd
//  1... .... 1011 .... .... .... rw fdc
//
//  0100 0... .... .... .... .... rw raml
//  0100 1... .... .... .... .... rw ramh
//  0101 0... .... .... .... .... rw ssel1 (top gate array)
//  0101 1... .... .... .... .... rw ssel2 (bottom gate array)

//  Uses 4 dsp-v which each have two inputs and two outputs.  Their
//  stream inputs/outputs are connected thus:
//    #1.2 -> #3.2
//    #2.1 -> #3.1
//    #3.1 -> #4.1
//    #3.2 -> #4.2
//    #4.1 -> dac

// Uses 3 tmp68301.  The main one controls dspv #4.  The first subcpu
// controls dspvs #2 and #3, the second #1, Each subgroup is tucked
// behind a gate array, selected by ssel1/2.

// There's also an undumped 63b01 dubbed "pks" which scans the
// switches and has one 8-bit port and an irq line that goes to the
// main tmp.


#include "emu.h"
#include "cpu/m68000/tmp68301.h"
#include "imagedev/floppy.h"
#include "machine/upd765.h"
#include "sound/dspv.h"
#include "video/t6963c.h"

namespace {

class vl1_state : public driver_device
{
public:
	vl1_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_subcpu1(*this, "subcpu1"),
		m_subcpu2(*this, "subcpu2"),
		m_dspv1(*this, "dspv1"),
		m_dspv2(*this, "dspv2"),
		m_dspv3(*this, "dspv3"),
		m_dspv4(*this, "dspv4"),
		m_lcd(*this, "lcd"),
		m_fdc(*this, "fdc"),
		m_mem_s1(*this, "subcpu1_ram"),
		m_mem_s2(*this, "subcpu2_ram")
	{ }

	void vl1(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	required_device<tmp68301_device> m_maincpu, m_subcpu1, m_subcpu2;
	required_device<dspv_device> m_dspv1, m_dspv2, m_dspv3, m_dspv4;
	required_device<lm24014h_device> m_lcd;
	required_device<hd63266f_device> m_fdc;

	required_shared_ptr<u16> m_mem_s1, m_mem_s2;
	u16 m_led, m_main_ctrl, m_sub1_ctrl, m_sub2_ctrl;

	void maincpu_map(address_map &map) ATTR_COLD;
	void subcpu1_map(address_map &map) ATTR_COLD;
	void subcpu2_map(address_map &map) ATTR_COLD;

	u16 main_r();
	void main_w(u16 data);
	u16 sub1_r();
	void sub1_w(u16 data);
	u16 sub2_r();
	void sub2_w(u16 data);

	void led_w(u16 data);

	static void hd_floppy(device_slot_interface &device);
};

void vl1_state::led_w(u16 data)
{
	u8 activated = (((~m_led) & data) >> 8) & 0xf;
	if(activated && false) {
		if(activated & 1)
			logerror("led.0 %02x\n", data & 0xff);
		if(activated & 2)
			logerror("led.1 %02x\n", data & 0xff);
		if(activated & 4)
			logerror("led.2 %02x\n", data & 0xff);
		if(activated & 8)
			logerror("led.3 %02x\n", data & 0xff);
	}
	m_led = data;
}

// Parallel port:
//   o 0-3: adsel 1-4 | breath controller adc & muxer
//   o 4  : adst      |
//   i 5  : eoc       |
//   o 7  : inh       |
//   o 8-9: bshalt 1-2
//   i a  : fdc irq
//   i b  : fdc hd out (hd floppy detection)
//   i d-e: re 1-2
//   o f  : reclr

u16 vl1_state::main_r()
{
	if(0)
		logerror("main_r\n");
	return m_main_ctrl;
}

void vl1_state::main_w(u16 data)
{
	if(0)
		logerror("main_w adsel=%x adst=%d inh=%d bshalt=%d reclr=%d\n",
				 BIT(data, 0, 4),
				 BIT(data, 4),
				 BIT(data, 7),
				 BIT(data, 8, 2),
				 BIT(data, 15));

	m_main_ctrl = data;
	m_subcpu1->set_input_line(INPUT_LINE_HALT, !BIT(data, 8));
	m_subcpu2->set_input_line(INPUT_LINE_HALT, !BIT(data, 9));
}

u16 vl1_state::sub1_r()
{
	return m_sub1_ctrl;
}

void vl1_state::sub1_w(u16 data)
{
	if(1)
		logerror("sub1_w dsp = %d %d\n",
				 BIT(data, 0),
				 BIT(data, 1));

	m_sub1_ctrl = data;
	m_dspv2->set_input_line(INPUT_LINE_RESET, !BIT(data, 0));
	m_dspv3->set_input_line(INPUT_LINE_RESET, !BIT(data, 1));
}

u16 vl1_state::sub2_r()
{
	return m_sub2_ctrl;
}

void vl1_state::sub2_w(u16 data)
{
	if(1)
		logerror("sub2_w dsp = %d\n",
				 BIT(data, 0));

	m_sub2_ctrl = data;
	m_dspv1->set_input_line(INPUT_LINE_RESET, !BIT(data, 0));
}

void vl1_state::machine_start()
{
	save_item(NAME(m_main_ctrl));
	save_item(NAME(m_sub1_ctrl));
	save_item(NAME(m_sub2_ctrl));
	save_item(NAME(m_led));
}

void vl1_state::machine_reset()
{
	m_led = 0;
	m_main_ctrl = 0;
	m_sub1_ctrl = 0;
	m_sub2_ctrl = 0;

	m_subcpu1->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
	m_subcpu2->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
	m_dspv1->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
	m_dspv2->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
	m_dspv3->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
	m_dspv4->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
}

void vl1_state::maincpu_map(address_map &map)
{
	map(0x000000, 0x0fffff).rom().region("maincpu", 0);
	map(0x400000, 0x4fffff).ram();
	map(0x500000, 0x53ffff).ram().share(m_mem_s1);
	map(0x580000, 0x5bffff).ram().share(m_mem_s2);
	map(0x800000, 0x800001).w(FUNC(vl1_state::led_w));
	map(0x804000, 0x804000).lr8(NAME([]() -> u8 { return 0; })); // pks
	map(0x806000, 0x806000).lr8(NAME([]() -> u8 { return 0; })); // adc
	map(0x808000, 0x80807f).m(m_dspv4, FUNC(dspv_device::map));
	map(0x80a000, 0x80a003).rw(m_lcd, FUNC(lm24014h_device::read), FUNC(lm24014h_device::write)).umask16(0x00ff);
	map(0x80b000, 0x80b003).m(m_fdc, FUNC(hd63266f_device::map)).umask16(0x00ff);
}

void vl1_state::subcpu1_map(address_map &map)
{
	map(0x000000, 0x03ffff).ram().share(m_mem_s1);
	map(0x040000, 0x04007f).m(m_dspv2, FUNC(dspv_device::map));
	map(0x050000, 0x05007f).m(m_dspv3, FUNC(dspv_device::map));
}

void vl1_state::subcpu2_map(address_map &map)
{
	map(0x000000, 0x03ffff).ram().share(m_mem_s2);
	map(0x040000, 0x04007f).m(m_dspv1, FUNC(dspv_device::map));
}


void vl1_state::hd_floppy(device_slot_interface &device)
{
	device.option_add("35hd", FLOPPY_35_HD);
}

void vl1_state::vl1(machine_config &config)
{
	TMP68301(config, m_maincpu, 16_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &vl1_state::maincpu_map);
	m_maincpu->parallel_r_cb().set(FUNC(vl1_state::main_r));
	m_maincpu->parallel_w_cb().set(FUNC(vl1_state::main_w));
	m_maincpu->tx2_handler().set(m_subcpu1, FUNC(tmp68301_device::rx2_w));
	m_maincpu->tx2_handler().append(m_subcpu2, FUNC(tmp68301_device::rx2_w));

	TMP68301(config, m_subcpu1, 16_MHz_XTAL);
	m_subcpu1->set_addrmap(AS_PROGRAM, &vl1_state::subcpu1_map);
	m_subcpu1->parallel_r_cb().set(FUNC(vl1_state::sub1_r));
	m_subcpu1->parallel_w_cb().set(FUNC(vl1_state::sub1_w));

	TMP68301(config, m_subcpu2, 16_MHz_XTAL);
	m_subcpu2->set_addrmap(AS_PROGRAM, &vl1_state::subcpu2_map);
	m_subcpu2->parallel_r_cb().set(FUNC(vl1_state::sub2_r));
	m_subcpu2->parallel_w_cb().set(FUNC(vl1_state::sub2_w));

	DSPV(config, m_dspv1, 24.576_MHz_XTAL);
	DSPV(config, m_dspv2, 24.576_MHz_XTAL);
	DSPV(config, m_dspv3, 24.576_MHz_XTAL);
	DSPV(config, m_dspv4, 24.576_MHz_XTAL);

	HD63266F(config, m_fdc, 16_MHz_XTAL);
	m_fdc->set_ready_line_connected(false);
	FLOPPY_CONNECTOR(config, "fdc:0", hd_floppy, "35hd", floppy_image_device::default_pc_floppy_formats);

	LM24014H(config, m_lcd);
}

static INPUT_PORTS_START(vl1)
INPUT_PORTS_END

ROM_START(vl1)
	ROM_REGION(0x100000, "maincpu", 0)
	ROM_LOAD16_BYTE("vl1v2h.ic26", 0, 0x80000, CRC(f4573379) SHA1(55f6423ce436d6472b80d3db5944881c60a42fcf))
	ROM_LOAD16_BYTE("vl1v2l.ic67", 1, 0x80000, CRC(382d24e9) SHA1(4e6637c8e1876c5931f944f8a188d58cdb1eb47f))
ROM_END

} // anonymous namespace

SYST(1993, vl1, 0, 0, vl1, vl1, vl1_state, empty_init, "Yamaha", "VL1", MACHINE_NOT_WORKING|MACHINE_NO_SOUND)



ymvl70.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:R. Belmont, Olivier Galibert

#include "emu.h"

#include "bus/midi/midiinport.h"
#include "bus/midi/midioutport.h"
#include "cpu/h8/h83003.h"
#include "mulcd.h"
#include "sound/meg.h"
#include "sound/dspv.h"

#include "debugger.h"
#include "speaker.h"


namespace {

static INPUT_PORTS_START( vl70 )
	PORT_START("B0")
	PORT_BIT(0x83, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Play")      PORT_CODE(KEYCODE_A)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Effect")    PORT_CODE(KEYCODE_F)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Midi/WX")   PORT_CODE(KEYCODE_X)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Enter")     PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Exit")      PORT_CODE(KEYCODE_BACKSPACE)

	PORT_START("B1")
	PORT_BIT(0x83, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Edit")      PORT_CODE(KEYCODE_E)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Mode")      PORT_CODE(KEYCODE_M)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part -")    PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Select <")  PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part +")    PORT_CODE(KEYCODE_CLOSEBRACE)

	PORT_START("B2")
	PORT_BIT(0x83, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Util")      PORT_CODE(KEYCODE_U)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Breath")    PORT_CODE(KEYCODE_B)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Value -")   PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Select >")  PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Value +")   PORT_CODE(KEYCODE_EQUALS)
INPUT_PORTS_END

class vl70_state : public driver_device
{
public:
	vl70_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_vl70cpu(*this, "vl70cpu")
		, m_dspv(*this, "dspv")
		, m_meg(*this, "meg")
		, m_lcd(*this, "lcd")
		, m_ioport_b0(*this, "B0")
		, m_ioport_b1(*this, "B1")
		, m_ioport_b2(*this, "B2")
	{ }

	void vl70(machine_config &config);

private:
	enum {
		P6_LCD_RS     = 0x04,
		P6_LCD_RW     = 0x02,
		P6_LCD_ENABLE = 0x01
	};

	required_device<h83003_device> m_vl70cpu;
	required_device<dspv_device> m_dspv;
	required_device<meg_device> m_meg;
	required_device<mulcd_device> m_lcd;
	required_ioport m_ioport_b0;
	required_ioport m_ioport_b1;
	required_ioport m_ioport_b2;

	u8 cur_p6, cur_pa, cur_pc;

	u16 adc_midisw_r();
	u16 adc_battery_r();
	u16 adc_breath_r();

	void p6_w(u8 data);
	void pa_w(u8 data);
	u8 pa_r();
	void pb_w(u8 data);
	void pc_w(u8 data);
	u8 pc_r();

	virtual void machine_start() override ATTR_COLD;
	void vl70_map(address_map &map) ATTR_COLD;
};

void vl70_state::machine_start()
{
	cur_p6 = cur_pa = cur_pc = 0xff;
}

void vl70_state::vl70_map(address_map &map)
{
	map(0x000000, 0x1fffff).rom().region("vl70cpu", 0);
	map(0x200000, 0x20ffff).ram(); // 64K work RAM
	map(0x400000, 0x40007f).m(m_dspv, FUNC(dspv_device::map));
	map(0x600000, 0x60001f).m(m_meg, FUNC(meg_device::map));
}

// Put the host switch to pure midi
u16 vl70_state::adc_midisw_r()
{
	return 0;
}

// Battery level
u16 vl70_state::adc_battery_r()
{
	return 0x200;
}

// Breath controller
u16 vl70_state::adc_breath_r()
{
	return 0x000;
}

void vl70_state::p6_w(u8 data)
{
	if((cur_p6 & P6_LCD_ENABLE) && !(data & P6_LCD_ENABLE)) {
		if(!(cur_p6 & P6_LCD_RW)) {
			if(cur_p6 & P6_LCD_RS)
				m_lcd->data_write(cur_pa);
			else
				m_lcd->control_write(cur_pa);
		}
	}

	cur_p6 = data;
}

void vl70_state::pb_w(u8 data)
{
	m_lcd->set_leds(bitswap<6>((data >> 2) ^ 0x3f, 5, 3, 1, 4, 2, 0));
}

void vl70_state::pc_w(u8 data)
{
	cur_pc = data;
}

u8 vl70_state::pc_r()
{
	u8 r = 0xff;
	if(!(cur_pc & 0x01))
		r &= m_ioport_b0->read();
	if(!(cur_pc & 0x02))
		r &= m_ioport_b1->read();
	if(!(cur_pc & 0x80))
		r &= m_ioport_b2->read();
	return r;
}

void vl70_state::pa_w(u8 data)
{
	cur_pa = data;
}

u8 vl70_state::pa_r()
{
	if((cur_p6 & P6_LCD_ENABLE)) {
		if(cur_p6 & P6_LCD_RW)
		{
			if(cur_p6 & P6_LCD_RS)
				return m_lcd->data_read();
			else
				return m_lcd->control_read();
		} else
			return 0x00;
	}

	return cur_pa;
}

void vl70_state::vl70(machine_config &config)
{
	H83003(config, m_vl70cpu, 10_MHz_XTAL);
	m_vl70cpu->set_addrmap(AS_PROGRAM, &vl70_state::vl70_map);
	m_vl70cpu->read_adc<0>().set(FUNC(vl70_state::adc_breath_r));
	m_vl70cpu->read_adc<1>().set_constant(0);
	m_vl70cpu->read_adc<2>().set(FUNC(vl70_state::adc_midisw_r));
	m_vl70cpu->read_adc<3>().set_constant(0);
	m_vl70cpu->read_adc<4>().set(FUNC(vl70_state::adc_battery_r));
	m_vl70cpu->read_adc<5>().set_constant(0);
	m_vl70cpu->read_adc<6>().set_constant(0);
	m_vl70cpu->read_adc<7>().set_constant(0);
	m_vl70cpu->write_port6().set(FUNC(vl70_state::p6_w));
	m_vl70cpu->read_porta().set(FUNC(vl70_state::pa_r));
	m_vl70cpu->write_porta().set(FUNC(vl70_state::pa_w));
	m_vl70cpu->write_portb().set(FUNC(vl70_state::pb_w));
	m_vl70cpu->read_portc().set(FUNC(vl70_state::pc_r));
	m_vl70cpu->write_portc().set(FUNC(vl70_state::pc_w));

	MULCD(config, m_lcd);

	SPEAKER(config, "speaker", 2).front();

	DSPV(config, m_dspv);
	MEG(config, m_meg);

	auto &mdin_a(MIDI_PORT(config, "mdin_a"));
	midiin_slot(mdin_a);
	mdin_a.rxd_handler().set(m_vl70cpu, FUNC(h83003_device::sci_rx_w<1>));

	auto &mdin_b(MIDI_PORT(config, "mdin_b"));
	midiin_slot(mdin_b);
	mdin_b.rxd_handler().set(m_vl70cpu, FUNC(h83003_device::sci_rx_w<0>));

	auto &mdout(MIDI_PORT(config, "mdout"));
	midiout_slot(mdout);
	m_vl70cpu->write_sci_tx<0>().set(mdout, FUNC(midi_port_device::write_txd));
}

ROM_START( vl70 )
	ROM_REGION( 0x200000, "vl70cpu", 0 )
	ROM_LOAD16_WORD_SWAP( "vl70m_v111_27c160.bin", 0x000000, 0x200000, CRC(efdba9f0) SHA1(cfa9fb7d2a991e4752393c9677e4ddcbe10866c7) )
ROM_END

} // anonymous namespace


CONS( 1996, vl70, 0, 0, vl70,  vl70, vl70_state, empty_init, "Yamaha", "VL70-m", MACHINE_NOT_WORKING )



z100.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese, AJR
/***************************************************************************

    Zenith Z-100

    The Z-100 was the assembled version of the Heath H-100, which came
    in kit form. There were two main versions of the computer, a low-profile,
    without a CRT monitor, and the "all-in-one", with a monochome CRT.

    Initially model numbers had standardized numbering with a two or three
    letter prefix, a 3 digit base, and a two digit suffix. The first letter
    of the prefix was either Z for Zenith (assembled) or H for Heath (kit).
    The second letter specified the disk drives: F for 48 tpi DS floppy or
    W for Winchester (hard-drive). An early version of the service manual
    specified 3 more letters, but not sure if they were ever sold: X for
    Customer OEM System, H for 96 tpi DS floppy, and E for 48 tpi SS Floppy.
    There are 4 base numbers: 100 for low-profile with monochome video board,
    110 for low-profile with color video board, 120 for all-in-one with
    monochome video, and 130 for all-in-one with color video board (for
    use with an external color monitor). For the suffix, the first number
    is the number of 64k memory banks, and the second number is the number
    of disk drives. Once new versions of the motherboard, which supported
    higher CPU speeds(8 MHz for the 8088), and higher capacity memory chips,
    model numbers deviated from the format specified above.

    15/07/2011 Skeleton driver.

    Commands:
    Press DELETE to abort boot, then press H to list all the commands.

    TODO:
    - fix interrupt flakiness depending on how system is started
        - "interrupt circuit error. chip u208"
        - screen scrolling not working
    - implement S-100 bus features;
        - Move floppy controller to a card
    - complete floppy controller implementation
    - memory test hangs on the first pass;
    - support switching between 8085 and 8088 CPUs
    - add winchester (hard drive)/controller option

============================================================================

Z207A       EQU 0B0H    ; Z-207 disk controller base port
                        ; (See DEFZ207 to program controller)
Z217A       EQU 0AEH    ; Z-217 disk controller base port
                        ; (See DEFZ217 to program controller)
ZGRNSEG     EQU 0E000H  ; Segment of green video plane
ZREDSEG     EQU 0D000H  ; Segment of red video plane
ZBLUSEG     EQU 0C000H  ; Segment of blue video plane
ZVIDEO      EQU 0D8H    ; Video 68A21 port
                        ; PA0 -> enable red display
                        ; PA1 -> enable green display
                        ; PA2 -> enable blue display
                        ; PA3 -> not flash screen
                        ; PA4 -> not write multiple red
                        ; PA5 -> not write multiple green
                        ; PA6 -> not write multiple blue
                        ; PA7 -> disable video RAM
                        ; PB7-PB0 -> LA15-LA8
                        ; CA1 - not used
                        ; CA2 -> clear screen
                        ; CB1 - not used
                        ; CB2 -> value to write (0 or 1) on clear screen
                        ; (see DEF6821 to program the 6821)
ZCRTC       EQU 0DCH    ; Video 6845 CRT-C port
                        ; (see DEF6845 to program the 6845)
ZLPEN       EQU 0DEH    ; Light pen latch
  ZLPEN_BIT   EQU 00000111B   ; Bit hit by pen
  ZLPEN_ROW   EQU 11110000B   ; Row hit by pen
ZPIA        EQU 0E0H    ; Parallel printer plus light pen and
                        ;  video vertical retrace 68A21 port
                        ; PA0 -> PDATA1
                        ; PA1 -> PDATA2
                        ; PA2 -> not STROBE
                        ; PA3 -> not INIT
                        ; PA4 <- VSYNC
                        ; PA5 -> clear VSYNC flip flop
                        ; PA6 <- light pen switch
                        ; PA7 -> clear light pen flip flop
                        ; PB0 <- BUSY
                        ; PB1 <- not ERROR
                        ; PB2 -> PDATA3
                        ; PB3 -> PDATA4
                        ; PB4 -> PDATA5
                        ; PB5 -> PDATA6
                        ; PB6 -> PDATA7
                        ; PB7 -> PDATA8
                        ; CA1 <- light pen hit (from flip flop)
                        ; CA2 <- VSYNC (from flip flop)
                        ; CB1 <- not ACKNLG
                        ; CB2 <- BUSY
                        ; (See DEF6821 to program the PIA)
ZTIMER      EQU 0E4H    ; Timer 8253 port
  ZTIMEVAL    EQU 2500  ; 100ms divide by N value
                        ; (See DEF8253 to program the 8253)
ZTIMERS     EQU 0FBH    ; Timer interrupt status port
  ZTIMERS0    EQU 001H  ; Timer 0 interrupt
  ZTIMERS2    EQU 002H  ; Timer 2 interrupt
ZSERA       EQU 0E8H    ; First 2661-2 serial port
ZSERB       EQU 0ECH    ; Second 2661-2 serial port
                        ; (See DEFEP2 to program 2661-2)
ZM8259A     EQU 0F2H    ; Master 8259A interrupt controller port
  ZINTEI      EQU 0     ; Parity error or S-100 pin 98 interrupt
  ZINTPS      EQU 1     ; Processor swap interrupt
  ZINTTIM     EQU 2     ; Timer interrupt
  ZINTSLV     EQU 3     ; Slave 8259A interrupt
  ZINTSA      EQU 4     ; Serial port A interrupt
  ZINTSB      EQU 5     ; Serial port B interrupt
  ZINTKD      EQU 6     ; Keyboard, Display, or Light pen interrupt
  ZINTPP      EQU 7     ; Parallel port interrupt
                        ; (See DEF8259A to program the 8259A)
  ZM8259AI    EQU 64    ; Base interrupt number for master
ZS8259A     EQU 0F0H    ; Secondary 8259A interrupt controller port
  ZS8259AI    EQU 72    ; Base interrupt number for slave
  BIOSAI      EQU ZS8259AI+8  ; Base of BIOS generated interrupts
ZKEYBRD     EQU 0F4H    ; Keyboard port
  ZKEYBRDD    EQU ZKEYBRD+0   ; Keyboard data port
  ZKEYBRDC    EQU ZKEYBRD+1   ; Keyboard command port
    ZKEYRES     EQU 0   ; Reset command
    ZKEYARD     EQU 1   ; Autorepeat on command
    ZKEYARF     EQU 2   ; Autorepeat off command
    ZKEYKCO     EQU 3   ; Key click on command
    ZKEYKCF     EQU 4   ; Key click off command
    ZKEYCF      EQU 5   ; Clear keyboard FIFO command
    ZKEYCLK     EQU 6   ; Generate a click sound command
    ZKEYBEP     EQU 7   ; Generate a beep sound command
    ZKEYEK      EQU 8   ; Enable keyboard command
    ZKEYDK      EQU 9   ; Disable keyboard command
    ZKEYUDM     EQU 10  ; Enter UP/DOWN mode command
    ZKEYNSM     EQU 11  ; Enter normal scan mode command
    ZKEYEI      EQU 12  ; Enable keyboard interrupts command
    ZKEYDI      EQU 13  ; Disable keyboard interrupts command
  ZKEYBRDS    EQU ZKEYBRD+1   ; Keyboard status port
    ZKEYOBF     EQU 01H ; Output buffer not empty
    ZKEYIBF     EQU 02H ; Input buffer full
ZMCL        EQU 0FCH    ; Memory control latch
  ZMCLMS      EQU 00000011B   ; Map select mask
    ZSM0        EQU 0   ; Map select 0
    ZSM1        EQU 1   ; Map select 1
    ZSM2        EQU 2   ; Map select 2
    ZSM3        EQU 3   ; Map select 3
  ZMCLRM      EQU 00001100B   ; Monitor ROM mapping mask
    ZRM0        EQU 0*4 ; Power up mode - ROM everywhere on reads
    ZRM1        EQU 1*4 ; ROM at top of every 64K page
    ZRM2        EQU 2*4 ; ROM at top of 8088's addr space
    ZRM3        EQU 3*4 ; Disable ROM
  ZMCLPZ      EQU 00010000B   ; 0=Set Parity to the zero state
  ZMCLPK      EQU 00100000B   ; 0=Disable parity checking circuity
  ZMCLPF      EQU 01000000B   ; 0=Disable parity error flag
Z205BA      EQU 098H    ; Base address for Z-205 boards
  Z205BMC     EQU 8     ; Maximum of 8 Z-205 boards installed
ZHAL        EQU 0FDH    ; Hi-address latch
  ZHAL85      EQU 0FFH  ; 8085 Mask
  ZHAL88      EQU 0F0H  ; 8088 Mask
ZPSP        EQU 0FEH    ; Processor swap port
  ZPSPPS      EQU 10000000B   ; Processor select (0=8085, 1=8088)
  ZPSPPS5     EQU 00000000B   ; Select 8085
  ZPSPPS8     EQU 10000000B   ; Select 8088
  ZPSPSI      EQU 00000010B   ; Generate interrupt on swapping
  ZPSPI8      EQU 00000001B   ; 8088 processes all interrupts
ZDIPSW      EQU 0FFH    ; Configuration dip switches
  ZDIPSWBOOT  EQU 00000111B   ; Boot device field
  ZDIPSWAB    EQU 00001000B   ; 1=Auto boot(0=Manual boot)
  ZDIPSWRES   EQU 01110000B   ; Reserved
  ZDIPSWHZ    EQU 10000000B   ; 1=50Hz(0=60HZ)

Motherboard Jumpers

  J101/J102  MTR-100 ROM size
    0   0        8K
    0   1       16K
    1   1       32K
  J103  Ligt pen transistion to cause interrupt
    on negative-going edge (default)
    on positive-going edge
  J104 NMI for 8085 when power fail signal (PWRFAIL*) is active
    No Jumper or left jumper  - disabled (default)
    foil cut and right jumper - enabled
  J105 Test input to 8088
    jumper    - Test input is grounded
    No jumper - Test input is high (default)
  J106 Factory test use only
  J107 Mainboard driving S-100 MWRT signal
    No jumper      - Yes (deafault)
    Foil trace cut - No
  J108 Serial port B transmitter is empty interrupt
    No jumper      - No interrupt
    Jumper         - TXEMT active causes interrupts in addition to normal interrupts
  J109 Serial port A DCD input tied to
    Position 1 - DCD to RTS
    Position 2 - DCD to ground
  J110 Serial port A transmitter is empty interrupt
    No jumper      - No interrupt
    Jumper         - TXEMT active causes interrupts in addition to normal interrupts
  J111 Serial port A CTS
    Position 1 - CTS to RTS
    Position 2 - CTS to ground

  ****************************************************************************/

#include "emu.h"
#include "cpu/i86/i86.h"
#include "cpu/i8085/i8085.h"
#include "cpu/mcs48/mcs48.h"
#include "bus/centronics/ctronics.h"
//#include "bus/rs232/rs232.h"
//#include "bus/s100/s100.h"
#include "imagedev/floppy.h"
#include "machine/74123.h"
#include "machine/6821pia.h"
#include "machine/input_merger.h"
#include "machine/pit8253.h"
#include "machine/pic8259.h"
#include "machine/rescap.h"
#include "machine/scn_pci.h"
#include "machine/wd_fdc.h"
#include "sound/beep.h"
#include "video/mc6845.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include <algorithm>


// Single Step
#define LOG_KBD    (1U << 1)
#define LOG_INTR   (1U << 2)

#define VERBOSE (0xff)
#include "logmacro.h"

#define LOGKBD(...)    LOGMASKED(LOG_KBD,    __VA_ARGS__)
#define LOGINTR(...)   LOGMASKED(LOG_INTR,   __VA_ARGS__)

#ifdef _MSC_VER
#define FUNCNAME __func__
#else
#define FUNCNAME __PRETTY_FUNCTION__
#endif


namespace {

class z100_state : public driver_device
{
public:
	z100_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_sec_cpu(*this, "sec_cpu")
		, m_ram(*this, "ram")
		, m_pia(*this, "pia%u", 0U)
		, m_picm(*this, "pic8259_master")
		, m_pics(*this, "pic8259_slave")
		, m_pit(*this, "pit")
		, m_fdc(*this, "z207_fdc")
		, m_floppies(*this, "z207_fdc:%u", 0U)
		, m_epci(*this, "epci%u", 0U)
		, m_keyclick(*this, "keyclick")
		, m_keybeep(*this, "keybeep")
		, m_beeper(*this, "beeper")
		, m_crtc(*this, "crtc")
		, m_palette(*this, "palette")
		, m_vrmm(*this, "vrmm")
		, m_vram_config(*this, "VRAM")
		, m_kbdc(*this, "kbdc")
		, m_keys(*this, "COL%u", 0U)
		, m_ctrl(*this, "CTRL")
		, m_floppy(nullptr)
	{ }

	void z100(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(kbd_reset);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void video_start() override ATTR_COLD;

	u8 ram_r(offs_t offset);
	void ram_w(offs_t offset, u8 data);
	void memory_ctrl_w(u8 data);
	offs_t vram_map(offs_t offset) const;
	u8 z100_vram_r(offs_t offset);
	void z100_vram_w(offs_t offset, u8 data);
	void kbd_col_w(u8 data);
	u8 kbd_rows_r();
	int kbd_shift_row_r();
	void beep_update(int state);
	void floppy_select_w(u8 data);
	void floppy_motor_w(u8 data);
	u8 tmr_status_r();
	void tmr_status_w(u8 data);
	void timer_flipflop0_w(int state);
	void timer_flipflop1_w(int state);
	void vidint_w(int state);
	void vidint_enable_w(int state);

	u8 get_slave_ack(offs_t offset);
	void video_pia_A_w(u8 data);
	void video_pia_B_w(u8 data);
	void video_pia_CA2_w(int state);
	void video_pia_CB2_w(int state);

	MC6845_UPDATE_ROW(update_row);

	void z100_io(address_map &map) ATTR_COLD;
	void z100_mem(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<cpu_device> m_sec_cpu;
	required_shared_ptr<u8> m_ram;
	required_device_array<pia6821_device, 2> m_pia;
	required_device<pic8259_device> m_picm;
	required_device<pic8259_device> m_pics;
	required_device<pit8253_device> m_pit;
	required_device<fd1797_device> m_fdc;
	required_device_array<floppy_connector, 4> m_floppies;
	required_device_array<scn2661b_device, 2> m_epci;
	required_device<ttl74123_device> m_keyclick;
	required_device<ttl74123_device> m_keybeep;
	required_device<beep_device> m_beeper;
	required_device<mc6845_device> m_crtc;
	required_device<palette_device> m_palette;
	required_region_ptr<u8> m_vrmm;
	required_ioport m_vram_config;
	required_device<i8041a_device> m_kbdc;
	required_ioport_array<16> m_keys;
	required_ioport m_ctrl;

	std::unique_ptr<u8[]> m_gvram;
	std::unique_ptr<u32[]> m_parity;
	u8 m_kbd_col;
	bool m_vram_enable;
	bool m_vram_64k;
	u8 m_gbank;
	u8 m_display_mask;
	bool m_flash;
	u8 m_clr_val;
	u8 m_tmr_status;
	u8 m_start_addr;
	bool m_vidint_enable;
	u8 m_memory_ctrl;

	floppy_image_device *m_floppy;
};


void z100_state::machine_start()
{

	u32 parity_size = m_ram.bytes() / 32;

	m_parity = make_unique_clear<u32[]>(parity_size);

	save_item(NAME(m_kbd_col));
	save_item(NAME(m_vram_enable));
	save_item(NAME(m_vram_64k));
	save_item(NAME(m_gbank));
	save_item(NAME(m_display_mask));
	save_item(NAME(m_flash));
	save_item(NAME(m_clr_val));
	save_item(NAME(m_tmr_status));
	save_item(NAME(m_start_addr));
	save_item(NAME(m_vidint_enable));
	save_item(NAME(m_memory_ctrl));
	save_pointer(NAME(m_parity), parity_size);
}

void z100_state::video_start()
{
	m_gvram = make_unique_clear<u8[]>(0x30000);

	save_pointer(NAME(m_gvram), 0x30000);

	m_vidint_enable = false;
}

u8 z100_state::ram_r(offs_t offset)
{
	if (!machine().side_effects_disabled() && BIT(m_memory_ctrl, 5))
	{
		u32 parity = m_parity[offset >> 5];
		if (BIT(parity, offset & 31))
			m_picm->ir0_w(1);
	}

	return m_ram[offset];
}

void z100_state::ram_w(offs_t offset, u8 data)
{
	u32 &parity = m_parity[offset >> 5];

	if (!BIT(m_memory_ctrl, 4) && BIT(population_count_32(data), 0))
		parity |= 1 << (offset & 31);
	else if (parity != 0)
		parity &= ~(1 << (offset & 31));

	m_ram[offset] = data;
}

void z100_state::memory_ctrl_w(u8 data)
{
	m_memory_ctrl = data & 0x3f;

	if (!BIT(data, 5))
		m_picm->ir0_w(0);
}

MC6845_UPDATE_ROW(z100_state::update_row)
{
	u32 *const pix = &bitmap.pix(y);
	const u16 amask = m_vram_64k ? 0xfff : 0x7ff;

	for (int x = 0; x < x_count; x++)
	{
		for (int xi = 0; xi < 8; xi++)
		{
			int dot = 0;

			if (m_flash)
			{
				dot = m_display_mask;
			}
			else
			{
				for (int i = 0; i < 3; i++)
					dot |= ((m_gvram[((x + ma) & amask) << 4 | (ra & 0xf) | (0x10000 * i)] >> (7 - xi)) & 1) << i; // b, r, g

				if (x == cursor_x)
					dot ^= 7;

				dot &= m_display_mask;
			}

			pix[x * 8 + xi] = m_palette->pen(dot);
		}
	}
}

offs_t z100_state::vram_map(offs_t offset) const
{
	// Translate logical address to physical address
	return BIT(offset, 16, 2) << 16 | BIT(offset, 0, 4) << 4 | BIT(offset, 7, 4)
		| ((m_vrmm[BIT(offset, 11, 5) << 3 | BIT(offset, 4, 3)] + m_start_addr) & (m_vram_64k ? 0xff : 0x7f)) << 8;
}

u8 z100_state::z100_vram_r(offs_t offset)
{
	return m_gvram[vram_map(offset)];
}

void z100_state::z100_vram_w(offs_t offset, u8 data)
{
	if(m_vram_enable)
	{
		offset = vram_map(offset);
		m_gvram[offset] = data;

		offset &= 0xffff;

		for (int i = 0; i < 3; i++)
		{
			if (BIT(m_gbank, i))
			{
				m_gvram[offset + 0x10000 * i] = data;
			}
		}
	}
}

void z100_state::z100_mem(address_map &map)
{
	map.unmap_value_high();
	map(0x00000, 0x3ffff).rw(FUNC(z100_state::ram_r), FUNC(z100_state::ram_w)).share("ram"); // 128*2 KB RAM
//  map(0xb0000,0xbffff).rom(); // expansion ROM
	map(0xc0000, 0xeffff).rw(FUNC(z100_state::z100_vram_r), FUNC(z100_state::z100_vram_w)); // Blue / Red / Green
//  map(0xf0000,0xf0fff) // network card (NET-100)
//  map(0xf4000,0xf7fff) // MTRET-100 Firmware I expansion ROM
//  map(0xf8000,0xfbfff) // MTRET-100 Firmware II expansion ROM check ID 0x4550
	map(0xfc000, 0xfffff).rom().region("ipl", 0);
}

void z100_state::kbd_col_w(u8 data)
{
	LOGKBD("%s: 0x%02x\n", FUNCNAME, data);

	m_kbd_col = BIT(data, 0, 4);

	m_keyclick->b_w(BIT(data, 7));
	m_keybeep->a_w((data & 0x82) == 0);
}

u8 z100_state::kbd_rows_r()
{
	if (m_kbd_col < 0x0c)
	{
		u8 val = m_keys[m_kbd_col]->read();

		LOGKBD("%s: 0x%02x\n", FUNCNAME, val);

		return val;
	}

	return 0xff;
}

int z100_state::kbd_shift_row_r()
{
	if ((m_kbd_col & 0x0c) == 0x0c)
	{
		u8 val = m_keys[m_kbd_col]->read();

		LOGKBD("%s: 0x%02x\n", FUNCNAME, val);

		return val;
	}

	return 1;
}

void z100_state::beep_update(int state)
{
	m_beeper->set_state(m_keyclick->q_r() | m_keybeep->q_r());
}

// todo: side select?

void z100_state::floppy_select_w(u8 data)
{
	m_floppy = m_floppies[BIT(data, 0, 2)]->get_device();
	m_fdc->set_floppy(m_floppy);
}

void z100_state::floppy_motor_w(u8 data)
{
	if (m_floppy)
		m_floppy->mon_w(!BIT(data, 1));
}

u8 z100_state::tmr_status_r()
{
	return m_tmr_status;
}

void z100_state::tmr_status_w(u8 data)
{
	m_tmr_status &= BIT(data, 0, 2);

	if (m_tmr_status == 0)
		m_picm->ir2_w(0);
}

void z100_state::timer_flipflop0_w(int state)
{
	if (state)
	{
		m_tmr_status |= 1;
		m_picm->ir2_w(1);
	}
}

void z100_state::timer_flipflop1_w(int state)
{
	if (state)
	{
		m_tmr_status |= 2;
		m_picm->ir2_w(1);
	}
}

void z100_state::vidint_w(int state)
{
	m_pia[1]->pa4_w(state);

	if (state && m_vidint_enable)
		m_pia[1]->ca2_w(1);
}

void z100_state::vidint_enable_w(int state)
{
	m_vidint_enable = bool(state);

	if (!m_vidint_enable)
		m_pia[1]->ca2_w(0);
}

void z100_state::z100_io(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
//  map(0x00, 0x3f) reserved for non-ZDS vendors
//  map(0x40, 0x5f) secondary Multiport card (Z-204)
//  map(0x60, 0x7f) primary Multiport card (Z-204)
//  map(0x80, 0x83) development board
//  map(0x98, 0x9f) Z-205 expansion memory boards
//  map(0xa0, 0xa3) network card (NET-100)
//  map(0xa4, 0xa7) gateway (reserved)
//  map(0xac, 0xad) Z-217 secondary disk controller (winchester)
//  map(0xae, 0xaf) Z-217 primary disk controller (winchester)
	map(0xb0, 0xb3).rw(m_fdc, FUNC(fd1797_device::read), FUNC(fd1797_device::write));
	map(0xb4, 0xb4).w(FUNC(z100_state::floppy_select_w));
	map(0xb5, 0xb5).w(FUNC(z100_state::floppy_motor_w));
//  z-207 secondary disk controller (wd1797)
//  map(0xcd, 0xce) ET-100 CRT Controller
//  map(0xd4, 0xd7) ET-100 Trainer Parallel I/O
	map(0xd8, 0xdb).rw(m_pia[0], FUNC(pia6821_device::read), FUNC(pia6821_device::write)); //video board
	map(0xdc, 0xdc).w(m_crtc, FUNC(mc6845_device::address_w));
	map(0xdd, 0xdd).w(m_crtc, FUNC(mc6845_device::register_w));
//  map(0xde, 0xde) light pen
	map(0xe0, 0xe3).rw(m_pia[1], FUNC(pia6821_device::read), FUNC(pia6821_device::write)); //main board
	map(0xe4, 0xe7).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write));
	map(0xe8, 0xeb).rw(m_epci[0], FUNC(scn2661b_device::read), FUNC(scn2661b_device::write));
	map(0xec, 0xef).rw(m_epci[1], FUNC(scn2661b_device::read), FUNC(scn2661b_device::write));
	map(0xf0, 0xf1).rw(m_pics, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0xf2, 0xf3).rw(m_picm, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
	map(0xf4, 0xf5).rw(m_kbdc, FUNC(i8041a_device::upi41_master_r), FUNC(i8041a_device::upi41_master_w));
//  map(0xf6, 0xf6) expansion ROM is present (bit 0, active low)
	map(0xfb, 0xfb).rw(FUNC(z100_state::tmr_status_r), FUNC(z100_state::tmr_status_w));
	map(0xfc, 0xfc).w(FUNC(z100_state::memory_ctrl_w));
//  map(0xfd, 0xfd) Hi-address latch
//  map(0xfe, 0xfe) Processor swap port
	map(0xff, 0xff).portr("S101");
}

INPUT_CHANGED_MEMBER(z100_state::kbd_reset)
{
	if (m_ctrl->read() == 0)
		reset();
}

// Input ports
INPUT_PORTS_START( z100 )
	PORT_START("COL0") // 15
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('a') PORT_CHAR('A') PORT_CODE(KEYCODE_A)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('s') PORT_CHAR('S') PORT_CODE(KEYCODE_S)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('d') PORT_CHAR('D') PORT_CODE(KEYCODE_D)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('f') PORT_CHAR('F') PORT_CODE(KEYCODE_F)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('g') PORT_CHAR('G') PORT_CODE(KEYCODE_G)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('h') PORT_CHAR('H') PORT_CODE(KEYCODE_H)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('j') PORT_CHAR('J') PORT_CODE(KEYCODE_J)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('k') PORT_CHAR('K') PORT_CODE(KEYCODE_K)

	PORT_START("COL1") // 11
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('q') PORT_CHAR('Q') PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('w') PORT_CHAR('W') PORT_CODE(KEYCODE_W)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('e') PORT_CHAR('E') PORT_CODE(KEYCODE_E)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('r') PORT_CHAR('R') PORT_CODE(KEYCODE_R)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('t') PORT_CHAR('T') PORT_CODE(KEYCODE_T)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('y') PORT_CHAR('Y') PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('u') PORT_CHAR('U') PORT_CODE(KEYCODE_U)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('i') PORT_CHAR('I') PORT_CODE(KEYCODE_I)

	PORT_START("COL2") // 16
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('z') PORT_CHAR('Z') PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('x') PORT_CHAR('X') PORT_CODE(KEYCODE_X)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('c') PORT_CHAR('C') PORT_CODE(KEYCODE_C)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('v') PORT_CHAR('V') PORT_CODE(KEYCODE_V)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('b') PORT_CHAR('B') PORT_CODE(KEYCODE_B)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('n') PORT_CHAR('N') PORT_CODE(KEYCODE_N)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_M)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('l') PORT_CHAR('L') PORT_CODE(KEYCODE_L)

	PORT_START("COL3") // 13
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('p') PORT_CHAR('P') PORT_CODE(KEYCODE_P)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('o') PORT_CHAR('O') PORT_CODE(KEYCODE_O)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL4") // 9
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F0")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_CODE(KEYCODE_F6)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_CODE(KEYCODE_F7)

	PORT_START("COL5") // 12
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del/Ins Char") PORT_CODE(KEYCODE_F14)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del/Ins Line") PORT_CODE(KEYCODE_F13)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F12)) PORT_CODE(KEYCODE_F12)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F11)) PORT_CODE(KEYCODE_F11)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F10)) PORT_CODE(KEYCODE_F10)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F9)) PORT_CODE(KEYCODE_F9)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_CODE(KEYCODE_F8)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START("COL6") // 17
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD)) PORT_CODE(KEYCODE_7_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD)) PORT_CODE(KEYCODE_9_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD)) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD)) PORT_CODE(KEYCODE_3_PAD)

	PORT_START("COL7") // 18
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) PORT_CODE(KEYCODE_8_PAD)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD)) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_CODE(KEYCODE_ENTER_PAD)

	PORT_START("COL8") // 3
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Break")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Help")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(' ') PORT_CODE(KEYCODE_SPACE)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x0a) PORT_CODE(KEYCODE_RALT) PORT_NAME("Line Feed")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\\') PORT_CHAR('|') PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DEL)) PORT_CODE(KEYCODE_DEL) PORT_NAME("Delete")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD)) PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD)) PORT_CODE(KEYCODE_DEL_PAD)

	PORT_START("COL9") // 7
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x1b) PORT_CODE(KEYCODE_ESC)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x09) PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',') PORT_CHAR('<') PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x0d) PORT_CODE(KEYCODE_ENTER) PORT_NAME("Return")
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('/') PORT_CHAR('?') PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('.') PORT_CHAR('>') PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHAR(':') PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\'') PORT_CHAR('"') PORT_CODE(KEYCODE_QUOTE)

	PORT_START("COL10") // 10
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR('@') PORT_CODE(KEYCODE_2)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR('$') PORT_CODE(KEYCODE_4)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR('^') PORT_CODE(KEYCODE_6)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR('&') PORT_CODE(KEYCODE_7)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('8') PORT_CHAR('*') PORT_CODE(KEYCODE_8)

	PORT_START("COL11") // 14
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x08) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Back Space")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('`') PORT_CHAR('~') PORT_CODE(KEYCODE_TILDE) // ~ key is between BS and =
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('=') PORT_CHAR('+') PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('-') PORT_CHAR('_') PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0') PORT_CHAR(')') PORT_CODE(KEYCODE_0)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('9') PORT_CHAR('(') PORT_CODE(KEYCODE_9)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('[') PORT_CHAR('{') PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(']') PORT_CHAR('}') PORT_CODE(KEYCODE_CLOSEBRACE)

	PORT_START("COL12") // 5
	PORT_BIT(1, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_SHIFT_1) PORT_CODE(KEYCODE_LSHIFT) PORT_NAME("Shift Left")

	PORT_START("COL13") // 8
	PORT_BIT(1, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT) PORT_NAME("Shift Right")

	PORT_START("COL14") // 6
	PORT_BIT(1, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_CODE(KEYCODE_CAPSLOCK)

	PORT_START("COL15") // 1
	PORT_BIT(1, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT) PORT_NAME("Fast Repeat")

	PORT_START("CTRL") // 2 & 4
	PORT_BIT(1, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_NAME("Ctrl") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(z100_state::kbd_reset), 0)
	PORT_BIT(2, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(z100_state::kbd_reset), 0)

	PORT_START("S101")
	PORT_DIPNAME( 0x07, 0x00, "Default boot device" )
	PORT_DIPSETTING(    0x00, "5 1/4\" floppy disk drive (internal)" )
	PORT_DIPSETTING(    0x01, "8\" floppy disk drive(external)" )
	PORT_DIPSETTING(    0x02, "5\" Winchester disk (internal)" )
	PORT_DIPSETTING(    0x03, "3 - undefined" )
	PORT_DIPSETTING(    0x04, "4 - undefined" )
	PORT_DIPSETTING(    0x05, "5 - undefined" )
	PORT_DIPSETTING(    0x06, "6 - undefined" )
	PORT_DIPSETTING(    0x07, "7 - undefined" )
	PORT_DIPNAME( 0x08, 0x00, "Auto-boot" )
	PORT_DIPSETTING(    0x00, DEF_STR( No ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Yes ) )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Unused ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x10, DEF_STR( On ) )
	PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unused ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unused ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x00, "Video vertical scan frequency" )
	PORT_DIPSETTING(    0x00, "NTSC 60 Hz" )
	PORT_DIPSETTING(    0x80, "PAL 50 Hz" )

	PORT_START("CONFIG")
	PORT_CONFNAME( 0x01, 0x01, "Video Board" )
	PORT_CONFSETTING( 0x00, "Monochrome" )
	PORT_CONFSETTING( 0x01, "Color" )

	PORT_START("VRAM")
	PORT_DIPNAME( 0x01, 0x01, "Video Memory" ) PORT_DIPLOCATION("J307:1")
	PORT_DIPSETTING( 0x00, "32K" )
	PORT_DIPSETTING( 0x01, "64K" )
INPUT_PORTS_END

u8 z100_state::get_slave_ack(offs_t offset)
{
	LOGINTR("%s: offset: 0x%02x\n", FUNCNAME, offset);

	if (offset == 7) // IRQ = 7
	{
		LOGINTR("%s: m_pics->acknowledge()\n", FUNCNAME, offset);

		return m_pics->acknowledge();
	}

	return 0;
}

void z100_state::video_pia_A_w(u8 data)
{
	// all bits are active low
	// 7654 3210
	// x--- ---- -> disable video RAM
	// -x-- ---- -> not write multiple blue
	// --x- ---- -> not write multiple green
	// ---x ---- -> not write multiple red
	// ---- x--- -> not flash screen
	// ---- -x-- -> enable blue display
	// ---- --x- -> enable green display
	// ---- ---x -> enable red display

	m_vram_enable = !bool(BIT(data, 7));
	m_gbank = bitswap<3>(BIT(data, 4, 3), 1, 0, 2) ^ 0x7;
	m_flash = !bool(BIT(data, 3));
	m_display_mask = bitswap<3>(BIT(data, 0, 3), 1, 0, 2) ^ 0x7;
}

void z100_state::video_pia_B_w(u8 data)
{
	m_start_addr = data;
}

// clear screen
void z100_state::video_pia_CA2_w(int state)
{
	std::fill_n(&m_gvram[0], 0x30000, m_clr_val);
}

void z100_state::video_pia_CB2_w(int state)
{
	m_clr_val = (state & 1) ? 0x00 : 0xff;
}

void z100_state::machine_reset()
{
	m_vram_64k = bool(BIT(m_vram_config->read(), 0));

	if(BIT(ioport("CONFIG")->read(), 0))
	{
		for(int i = 0; i < 8; i++)
			m_palette->set_pen_color(i, pal1bit(BIT(i, 1)), pal1bit(BIT(i, 2)), pal1bit(BIT(i, 0)));
	}
	else
	{
		for(int i = 0; i < 8; i++)
			m_palette->set_pen_color(i, pal3bit(0), pal3bit(i), pal3bit(0));
	}

	memory_ctrl_w(0);
}

static void z100_floppies(device_slot_interface &device)
{
	device.option_add("dd", FLOPPY_525_DD);
}

void z100_state::z100(machine_config &config)
{
	// basic machine hardware
	I8088(config, m_maincpu, 15_MHz_XTAL / 3); // 5 MHz or 8 MHz depending on XTAL
	m_maincpu->set_addrmap(AS_PROGRAM, &z100_state::z100_mem);
	m_maincpu->set_addrmap(AS_IO, &z100_state::z100_io);
	m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb));

	I8085A(config, m_sec_cpu, 10_MHz_XTAL / 2).set_disable();

	I8041A(config, m_kbdc, 6_MHz_XTAL);
	m_kbdc->p1_in_cb().set(FUNC(z100_state::kbd_rows_r));
	m_kbdc->p2_out_cb().set(FUNC(z100_state::kbd_col_w));
	m_kbdc->p2_out_cb().append("keydspyint", FUNC(input_merger_device::in_w<0>)).bit(4);
	m_kbdc->t0_in_cb().set_ioport("CTRL").bit(0);
	m_kbdc->t1_in_cb().set(FUNC(z100_state::kbd_shift_row_r));

	TTL74123(config, m_keyclick, RES_K(150), CAP_U(.1));
	m_keyclick->set_connection_type(TTL74123_NOT_GROUNDED_NO_DIODE);
	m_keyclick->set_a_pin_value(0);
	m_keyclick->set_b_pin_value(1);
	m_keyclick->set_clear_pin_value(1);
	m_keyclick->out_cb().set(FUNC(z100_state::beep_update));

	TTL74123(config, m_keybeep, RES_K(220), CAP_U(2.2));
	m_keybeep->set_connection_type(TTL74123_NOT_GROUNDED_NO_DIODE);
	m_keybeep->set_b_pin_value(1);
	m_keybeep->set_clear_pin_value(1);
	m_keybeep->out_cb().set(FUNC(z100_state::beep_update));

	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beeper, 1'000'000'000 / PERIOD_OF_555_ASTABLE_NSEC(RES_K(470), RES_K(470), CAP_U(.001)));
	m_beeper->add_route(ALL_OUTPUTS, "mono", 0.50);

	// video hardware
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(14.112_MHz_XTAL, 912, 0, 640, 258, 0, 216);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	PALETTE(config, m_palette).set_entries(8);

	// devices
	MC6845(config, m_crtc, 14.112_MHz_XTAL / 8); // 68A45
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8);
	m_crtc->set_update_row_callback(FUNC(z100_state::update_row));
	m_crtc->out_vsync_callback().set(FUNC(z100_state::vidint_w));

	PIC8259(config, m_picm);
	m_picm->out_int_callback().set_inputline(m_maincpu, 0);
	m_picm->in_sp_callback().set_constant(1);
	m_picm->read_slave_ack_callback().set(FUNC(z100_state::get_slave_ack));

	PIC8259(config, m_pics);
	m_pics->out_int_callback().set(m_picm, FUNC(pic8259_device::ir3_w));
	m_pics->in_sp_callback().set_constant(0);

	PIT8253(config, m_pit);
	m_pit->set_clk<0>(4_MHz_XTAL / 16);
	m_pit->out_handler<0>().set(FUNC(z100_state::timer_flipflop0_w));
	m_pit->out_handler<0>().append(m_pit, FUNC(pit8253_device::write_clk1));
	m_pit->set_clk<2>(4_MHz_XTAL / 16);
	m_pit->out_handler<2>().set(FUNC(z100_state::timer_flipflop1_w));

	PIA6821(config, m_pia[0]);
	m_pia[0]->writepa_handler().set(FUNC(z100_state::video_pia_A_w));
	m_pia[0]->writepb_handler().set(FUNC(z100_state::video_pia_B_w));
	m_pia[0]->ca2_handler().set(FUNC(z100_state::video_pia_CA2_w));
	m_pia[0]->cb2_handler().set(FUNC(z100_state::video_pia_CB2_w));

	PIA6821(config, m_pia[1]);
	m_pia[1]->irqa_handler().set("keydspyint", FUNC(input_merger_device::in_w<1>));
	m_pia[1]->irqb_handler().set(m_picm, FUNC(pic8259_device::ir7_w));
	m_pia[1]->writepa_handler().set("centronics", FUNC(centronics_device::write_strobe)).bit(2);
	m_pia[1]->writepa_handler().append("centronics", FUNC(centronics_device::write_data0)).bit(0);
	m_pia[1]->writepa_handler().append("centronics", FUNC(centronics_device::write_data1)).bit(1);
	m_pia[1]->writepa_handler().append("centronics", FUNC(centronics_device::write_init)).bit(3);
	m_pia[1]->writepa_handler().append(FUNC(z100_state::vidint_enable_w)).bit(5);
	m_pia[1]->writepb_handler().set("centronics", FUNC(centronics_device::write_data2)).bit(2);
	m_pia[1]->writepb_handler().append("centronics", FUNC(centronics_device::write_data3)).bit(3);
	m_pia[1]->writepb_handler().append("centronics", FUNC(centronics_device::write_data4)).bit(4);
	m_pia[1]->writepb_handler().append("centronics", FUNC(centronics_device::write_data5)).bit(5);
	m_pia[1]->writepb_handler().append("centronics", FUNC(centronics_device::write_data6)).bit(6);
	m_pia[1]->writepb_handler().append("centronics", FUNC(centronics_device::write_data7)).bit(7);

	centronics_device &centronics(CENTRONICS(config, "centronics", centronics_devices, nullptr));
	centronics.ack_handler().set(m_pia[1], FUNC(pia6821_device::cb1_w)).invert();
	centronics.busy_handler().set(m_pia[1], FUNC(pia6821_device::cb2_w));
	centronics.busy_handler().append(m_pia[1], FUNC(pia6821_device::pb0_w));
	centronics.perror_handler().set(m_pia[1], FUNC(pia6821_device::pb1_w));

	input_merger_device &keydspyint(INPUT_MERGER_ANY_HIGH(config, "keydspyint"));
	keydspyint.output_handler().set(m_picm, FUNC(pic8259_device::ir6_w));

	FD1797(config, m_fdc, 4_MHz_XTAL / 4);

	FLOPPY_CONNECTOR(config, m_floppies[0], z100_floppies, "dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppies[1], z100_floppies, "dd", floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppies[2], z100_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);
	FLOPPY_CONNECTOR(config, m_floppies[3], z100_floppies, nullptr, floppy_image_device::default_mfm_floppy_formats);

	SCN2661B(config, m_epci[0], 4.9152_MHz_XTAL); // First 2661-2 serial port (printer)
	m_epci[0]->txrdy_handler().set("epci0int", FUNC(input_merger_device::in_w<0>));
	m_epci[0]->rxrdy_handler().set("epci0int", FUNC(input_merger_device::in_w<1>));

	SCN2661B(config, m_epci[1], 4.9152_MHz_XTAL); // Second 2661-2 serial port (modem)
	m_epci[1]->txrdy_handler().set("epci1int", FUNC(input_merger_device::in_w<0>));
	m_epci[1]->rxrdy_handler().set("epci1int", FUNC(input_merger_device::in_w<1>));

	input_merger_device &epci0int(INPUT_MERGER_ANY_HIGH(config, "epci0int"));
	epci0int.output_handler().set(m_picm, FUNC(pic8259_device::ir4_w));

	input_merger_device &epci1int(INPUT_MERGER_ANY_HIGH(config, "epci1int"));
	epci1int.output_handler().set(m_picm, FUNC(pic8259_device::ir5_w));
}

// ROM definition
ROM_START( z100 )
	ROM_REGION(0x4000, "ipl", 0)
	ROM_LOAD("intel-d27128-1.bin", 0x0000, 0x4000, CRC(b21f0392) SHA1(69e492891cceb143a685315efe0752981a2d8143))

	ROM_REGION(0x0400, "kbdc", 0) // 8041A keyboard controller
	ROM_LOAD("444-109.u204", 0x0000, 0x0400, CRC(45181029) SHA1(0e89649364d25cf2d8669d2a293ee162e274cb64))

	ROM_REGION(0x0100, "iodec", 0) // 82S129 I/O Decoder PROM
	ROM_LOAD("444-101.u179", 0x0000, 0x0100, CRC(c952be82) SHA1(0edf9265d302f8478a310858eb6a9352f0cda17b))

	ROM_REGION(0x0100, "memdec", 0) // 82S129 Memory Decoder PROM
	ROM_LOAD("444-104.u111", 0x0000, 0x0100, CRC(46edd69d) SHA1(5d4bafeaa4593e419bf94dba9e44c8b2be58727b))

	ROM_REGION(0x0020, "status", 0) // 82S123 CPU Status Decode PROM
	ROM_LOAD("444-105.u226", 0x0000, 0x0020, CRC(98b084e9) SHA1(d968b9a1b1d2ba3ed40036c2192c9960a6c15e99))

	ROM_REGION(0x0100, "vramsel", 0) // 82S129 Video RAM Select PROM
	ROM_LOAD("444-102.u371", 0x0000, 0x0100, CRC(4558f540) SHA1(55c9bad87b111537a6d386a6eb405169fb47304c))

	ROM_REGION(0x0100, "viosel", 0) // 82S129 Video I/O Select PROM
	ROM_LOAD("444-103.u369", 0x0000, 0x0100, CRC(854cef15) SHA1(836b244dac0085bcfe8006fde0c5f19982969236))

	ROM_REGION(0x0100, "vrmm", 0) // TBP18S22 Video RAM Mapping Module
	ROM_LOAD("444-127.u370", 0x0000, 0x0100, CRC(4163da5b) SHA1(217673d679d5b12ab9e8b5fd302585b58a44aca1))
ROM_END

} // anonymous namespace


//    YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  STATE       INIT        COMPANY                FULLNAME  FLAGS
COMP( 1982, z100, 0,      0,      z100,    z100,  z100_state, empty_init, "Zenith Data Systems", "Z-100",  MACHINE_NOT_WORKING )



z1013.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************

        Z1013 driver by Miodrag Milanovic

        22/04/2009 Preliminary driver.

The 8x4 keyboard contains letters A-W, enter, cursor right/left, space, and
4 shift keys.
S1 - X-Z, numbers
S2 - a-w
S3 - x-z, punctuation
S4 - control keys

Control Keys:
G - Graphics
A - Alpha
T - clear screen
U - enter
P - cursor left
Q - cursor right

Monitor commands (debug)
R - registers
B - breakpoint
E - execute
G - resume after breakpoint
N - single-step

Monitor commands (general)
H - switch to hex keyboard (H to Q become 0 to 9)
A - switch back to normal alpha keyboard
C - Compare memory blocks
D - Dump memory
F - Find bytes in memory
T - Copy memory block
M - Modify bytes (; to exit)
I - reboot
J - Jump to address
K - Fill memory (K by itself fills all of memory)
L - Load Cassette
S - Save Cassette
W - window (example: W EF00 EFFF)

Due to no input checking, misuse of commands can crash the system.



****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/z80pio.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "sound/spkrdev.h"


namespace {

class z1013_state : public driver_device
{
public:
	z1013_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_mainpio(*this, "z80pio")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_cass(*this, "cassette")
		, m_speaker(*this, "speaker")
		, m_p_videoram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
		, m_io_keyboard(*this, "X%u", 0U)
		, m_clock_config(*this, "TAKT")
	{ }

	void z1013k76(machine_config &config);
	void z1013a2(machine_config &config);
	void z1013(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(clock_config_changed);

protected:
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;

private:
	void z1013_keyboard_w(uint8_t data);
	uint8_t port_a_r();
	uint8_t port_b_r();
	uint8_t a2_port_b_r();
	void port_b_w(uint8_t data);
	uint8_t k7659_port_b_r();
	DECLARE_SNAPSHOT_LOAD_MEMBER(snapshot_cb);
	uint32_t screen_update_z1013(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	uint8_t m_keyboard_line = 0U;
	bool m_keyboard_part = false;

	required_device<cpu_device> m_maincpu;
	required_device<z80pio_device> m_mainpio;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_device<cassette_image_device> m_cass;
	required_device<speaker_sound_device> m_speaker;
	required_shared_ptr<uint8_t> m_p_videoram;
	required_region_ptr<u8> m_p_chargen;
	optional_ioport_array<9> m_io_keyboard;
	required_ioport m_clock_config;
};


/* Address maps */
void z1013_state::mem_map(address_map &map)
{
	map(0x0000, 0xebff).ram().share("mainram");
	map(0xec00, 0xefff).ram().share("videoram");
	map(0xf000, 0xffff).rom().region("maincpu",0);
}

void z1013_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).rw(m_mainpio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt));
	map(0x08, 0x08).w(FUNC(z1013_state::z1013_keyboard_w));
}

/* Input ports */
static INPUT_PORTS_START( z1013_8x4 )
	PORT_START("X0")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_TAB)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S1") PORT_CODE(KEYCODE_LSHIFT)
	PORT_START("X1")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S2") PORT_CODE(KEYCODE_LCONTROL)
	PORT_START("X2")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S3") PORT_CODE(KEYCODE_RSHIFT)
	PORT_START("X3")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S4") PORT_CODE(KEYCODE_RCONTROL)
	PORT_START("X4")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
	PORT_START("X5")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)
	PORT_START("X6")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
	PORT_START("X7")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ent") PORT_CODE(KEYCODE_ENTER)
	PORT_START("X8")
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_4WAY
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_4WAY
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_4WAY
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_4WAY
	PORT_START("TAKT")
		PORT_CONFNAME(3, 3, "System Clock") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(z1013_state::clock_config_changed), 0)
		PORT_CONFSETTING(3, "1 MHz")
		PORT_CONFSETTING(2, "2 MHz")
		PORT_CONFSETTING(1, "4 MHz")
INPUT_PORTS_END

static INPUT_PORTS_START( z1013_8x8 )
	PORT_START("X0")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)
	PORT_START("X1")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
	PORT_START("X2")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)
	PORT_START("X3")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA)
	PORT_START("X4")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("+") PORT_CODE(KEYCODE_COLON)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH)
	PORT_START("X5")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_EQUALS)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_TILDE)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("*") PORT_CODE(KEYCODE_OPENBRACE)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_QUOTE)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_CLOSEBRACE)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("_") PORT_CODE(KEYCODE_MINUS)
	PORT_START("X6")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Graph") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ent") PORT_CODE(KEYCODE_ENTER)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)
	PORT_START("X7")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift Lock") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_START("X8")
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_4WAY
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_4WAY
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_4WAY
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_4WAY
	PORT_START("TAKT")
		PORT_CONFNAME(3, 3, "System Clock") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(z1013_state::clock_config_changed), 0)
		PORT_CONFSETTING(3, "1 MHz")
		PORT_CONFSETTING(2, "2 MHz")
		PORT_CONFSETTING(1, "4 MHz")
INPUT_PORTS_END

static INPUT_PORTS_START( z1013 )
	PORT_START("X8")
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_4WAY
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_4WAY
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_4WAY
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_4WAY
	PORT_START("TAKT")
		PORT_CONFNAME(3, 3, "System Clock") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(z1013_state::clock_config_changed), 0)
		PORT_CONFSETTING(3, "1 MHz")
		PORT_CONFSETTING(2, "2 MHz")
		PORT_CONFSETTING(1, "4 MHz")
INPUT_PORTS_END


uint32_t z1013_state::screen_update_z1013(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint16_t sy=0,ma=0;

	for (uint8_t y = 0; y < 32; y++)
	{
		for (uint8_t ra = 0; ra < 8; ra++)
		{
			uint16_t *p = &bitmap.pix(sy++);

			for (uint16_t x = ma; x < ma+32; x++)
			{
				uint8_t chr = m_p_videoram[x];

				/* get pattern of pixels for that character scanline */
				uint8_t gfx = m_p_chargen[(chr<<3) | ra];

				/* Display a scanline of a character */
				*p++ = BIT(gfx, 7);
				*p++ = BIT(gfx, 6);
				*p++ = BIT(gfx, 5);
				*p++ = BIT(gfx, 4);
				*p++ = BIT(gfx, 3);
				*p++ = BIT(gfx, 2);
				*p++ = BIT(gfx, 1);
				*p++ = BIT(gfx, 0);
			}
		}
		ma+=32;
	}
	return 0;
}

void z1013_state::machine_reset()
{
	m_keyboard_part = false;
	m_keyboard_line = 0U;

	m_maincpu->set_state_int(Z80_PC, 0xf000);

	const XTAL clock = 8_MHz_XTAL / (1 << m_clock_config->read());
	m_maincpu->set_unscaled_clock(clock);
	m_mainpio->set_unscaled_clock(clock);
}

INPUT_CHANGED_MEMBER(z1013_state::clock_config_changed)
{
	const XTAL clock = 8_MHz_XTAL / (1 << newval);
	m_maincpu->set_unscaled_clock(clock);
	m_mainpio->set_unscaled_clock(clock);
}

void z1013_state::machine_start()
{
	save_item(NAME(m_keyboard_line));
	save_item(NAME(m_keyboard_part));
}

void z1013_state::z1013_keyboard_w(uint8_t data)
{
	m_keyboard_line = data & 7;
}

uint8_t z1013_state::port_a_r()
{
	return m_io_keyboard[8]->read();
}

uint8_t z1013_state::port_b_r()
{
	uint8_t data = m_io_keyboard[m_keyboard_line]->read() & 15;

	if (m_cass->input() > 0.03)
		data |= 0x40;

	return data;
}

uint8_t z1013_state::a2_port_b_r()
{
	u8 data;
	if (m_keyboard_part)
		data = BIT(m_io_keyboard[m_keyboard_line]->read(),4,4);
	else
		data = BIT(m_io_keyboard[m_keyboard_line]->read(),0,4);

	if (m_cass->input() > 0.03)
		data |= 0x40;

	return data;
}

void z1013_state::port_b_w(uint8_t data)
{
	m_keyboard_part = BIT(data, 4); // for z1013a2 only
	m_cass->output(BIT(data, 7) ? -1.0 : +1.0);
	m_speaker->level_w(BIT(data, 7) ? -1.0 : +1.0);
}

uint8_t z1013_state::k7659_port_b_r()
{
	return 0xff;
}

SNAPSHOT_LOAD_MEMBER(z1013_state::snapshot_cb)
{
/* header layout
0000,0001 - load address
0002,0003 - end address
0004,0005 - exec address
0006-000B - unknown
000C      - Filetype (appears B=Basic, C=Machine Language, I=?, could be more)
000D-000F - bytes D3, D3, D3
0010-001F - Filename
0020 up   - Program to load
*/

	if (image.length() < 0x20)
		return std::make_pair(image_error::INVALIDIMAGE, "File too short to contain Z1013 image header");

	std::vector<uint8_t> data(image.length());
	image.fread(&data[0], image.length());
	if ((data[13] != data[14]) || (data[14] != data[15]))
		return std::make_pair(image_error::INVALIDIMAGE, "Not a Z1013 image");

	uint16_t const startaddr = data[0] + data[1]*256;
	uint16_t const endaddr   = data[2] + data[3]*256;
	uint16_t const runaddr   = data[4] + data[5]*256;
	if (endaddr < startaddr)
	{
		return std::make_pair(
				image_error::INVALIDIMAGE,
				util::string_format("End address 0x%04X is less than start address 0x%04X", endaddr, startaddr));
	}
	else if ((endaddr - startaddr + 1) > (data.size() - 0x20))
	{
		return std::make_pair(
				image_error::INVALIDIMAGE,
				util::string_format("File too short to contain %u-byte program", endaddr - startaddr + 1));
	}

	memcpy(m_maincpu->space(AS_PROGRAM).get_read_ptr(startaddr), &data[0x20], endaddr - startaddr + 1);

	if (runaddr)
		m_maincpu->set_state_int(Z80_PC, runaddr);
	else
	{
		osd_printf_error("%s: Loaded but cannot run due to zero entry point\n", image.basename());
		image.message(" Loaded but cannot run due to zero entry point");
	}

	return std::make_pair(std::error_condition(), std::string());
}

/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	512,                    /* 2 x 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{  0*8,  1*8,  2*8,  3*8,  4*8,  5*8,  6*8,  7*8 },
	8*8                 /* every char takes 8 bytes */
};

static GFXDECODE_START( gfx_z1013 )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END

/* Machine driver */
void z1013_state::z1013(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 8_MHz_XTAL / 8);
	m_maincpu->set_addrmap(AS_PROGRAM, &z1013_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &z1013_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(8_MHz_XTAL, 512, 0, 256, 302, 0, 256);
	screen.set_screen_update(FUNC(z1013_state::screen_update_z1013));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_z1013);
	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* devices */
	Z80PIO(config, m_mainpio, 8_MHz_XTAL / 8);
	m_mainpio->in_pa_callback().set(FUNC(z1013_state::port_a_r));
	m_mainpio->in_pb_callback().set(FUNC(z1013_state::port_b_r));
	m_mainpio->out_pb_callback().set(FUNC(z1013_state::port_b_w));

	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cass->set_interface("z1013_cass");

	snapshot_image_device &snapshot(SNAPSHOT(config, "snapshot", "z80"));
	snapshot.set_delay(attotime::from_seconds(2));
	snapshot.set_load_callback(FUNC(z1013_state::snapshot_cb));
	snapshot.set_interface("z1013_snap");

	SOFTWARE_LIST(config, "cass_list").set_original("z1013_cass");
	SOFTWARE_LIST(config, "snap_list").set_original("z1013_snap");
}

void z1013_state::z1013a2(machine_config &config)
{
	z1013(config);

	m_mainpio->in_pb_callback().set(FUNC(z1013_state::a2_port_b_r));
}

void z1013_state::z1013k76(machine_config &config)
{
	z1013(config);

	m_mainpio->in_pb_callback().set(FUNC(z1013_state::k7659_port_b_r));
	m_mainpio->out_pb_callback().set_nop();
}

/* ROM definition */
ROM_START( z1013 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "202", "Original" )
	ROMX_LOAD( "mon_202.bin", 0x0000, 0x0800, CRC(5884edab) SHA1(c3a45ea5cc4da2b7c270068ba1e2d75916960709), ROM_BIOS(0))

	ROM_SYSTEM_BIOS( 1, "jm", "Jens Muller version" )
	ROMX_LOAD( "mon_jm_1992.bin", 0x0000, 0x0800, CRC(186d2888) SHA1(b52ccb557c41c96bace7db4c4f5031a0cd736168), ROM_BIOS(1))

	ROM_REGION(0x1000, "chargen",0)
	ROM_LOAD ("z1013font.bin",   0x0000, 0x0800, CRC(7023088f) SHA1(8b197a51c070efeba173d10be197bd41e764358c))
	ROM_LOAD ("altfont.bin",     0x0800, 0x0800, CRC(2dc96f9c) SHA1(d0b9b0751cc1e91be731547f6442c649b6dd6979))
ROM_END

ROM_START( z1013a2 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mon_a2.bin", 0x0000, 0x0800, CRC(98b19b10) SHA1(97e158f589198cb96aae1567ee0aa6e47824027e))

	ROM_REGION(0x1000, "chargen",0)
	ROM_LOAD ("z1013font.bin",   0x0000, 0x0800, CRC(7023088f) SHA1(8b197a51c070efeba173d10be197bd41e764358c))
	ROM_LOAD ("altfont.bin",     0x0800, 0x0800, CRC(2dc96f9c) SHA1(d0b9b0751cc1e91be731547f6442c649b6dd6979))
ROM_END

ROM_START( z1013k76 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "mon_rb_k7659.bin", 0x0000, 0x1000, CRC(b3d88c45) SHA1(0bcd20338cf0706b384f40901b7f8498c6f6c320))

	ROM_REGION(0x1000, "chargen",0)
	ROM_LOAD ("z1013font.bin",   0x0000, 0x0800, CRC(7023088f) SHA1(8b197a51c070efeba173d10be197bd41e764358c))
	ROM_LOAD ("altfont.bin",     0x0800, 0x0800, CRC(2dc96f9c) SHA1(d0b9b0751cc1e91be731547f6442c649b6dd6979))

	ROM_REGION(0x1000, "k7659",0)
	ROM_LOAD ("k7659n.bin", 0x0000, 0x0800, CRC(7454bf0a) SHA1(b97e7df93778fa371b96b6f4fb1a5b1c8b89d7ba) )
ROM_END

ROM_START( z1013s60 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "v1", "Version 1" )
	ROMX_LOAD( "mon_rb_s6009.bin", 0x0000, 0x1000, CRC(b37faeed) SHA1(ce2e69af5378d39284e8b3be23da50416a0b0fbe), ROM_BIOS(0))

	ROM_SYSTEM_BIOS( 1, "v2", "Version 2" )
	ROMX_LOAD( "4k-moni-k7652.bin", 0x0000, 0x1000, CRC(a1625fce) SHA1(f0847399502b38a73ad26b38ee2d85ba04ab85ec), ROM_BIOS(1))

	ROM_REGION(0x1000, "chargen",0)
	ROM_LOAD ("z1013font.bin",   0x0000, 0x0800, CRC(7023088f) SHA1(8b197a51c070efeba173d10be197bd41e764358c))
	ROM_LOAD ("altfont.bin",     0x0800, 0x0800, CRC(2dc96f9c) SHA1(d0b9b0751cc1e91be731547f6442c649b6dd6979))
ROM_END

ROM_START( z1013k69 )
	ROM_REGION( 0x1000, "maincpu", 0 )
	ROM_LOAD( "4k-moni-k7669.bin", 0x0000, 0x1000, CRC(09cd2a7a) SHA1(0b8500320d464469868a6b48db31105f34710c41))

	ROM_REGION(0x1000, "chargen",0)
	ROM_LOAD ("z1013font.bin",   0x0000, 0x0800, CRC(7023088f) SHA1(8b197a51c070efeba173d10be197bd41e764358c))
	ROM_LOAD ("altfont.bin",     0x0800, 0x0800, CRC(2dc96f9c) SHA1(d0b9b0751cc1e91be731547f6442c649b6dd6979))
ROM_END
/* Driver */

} // anonymous namespace


//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT      CLASS        INIT        COMPANY                           FULLNAME               FLAGS
COMP( 1985, z1013,    0,      0,      z1013,    z1013_8x4, z1013_state, empty_init, "VEB Robotron Electronics Riesa", "Z1013 (matrix 8x4)",  MACHINE_SUPPORTS_SAVE )
COMP( 1985, z1013a2,  z1013,  0,      z1013a2,  z1013_8x8, z1013_state, empty_init, "VEB Robotron Electronics Riesa", "Z1013 (matrix 8x8)",  MACHINE_SUPPORTS_SAVE )
COMP( 1985, z1013k76, z1013,  0,      z1013k76, z1013,     z1013_state, empty_init, "VEB Robotron Electronics Riesa", "Z1013 (K7659)",       MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1985, z1013s60, z1013,  0,      z1013k76, z1013_8x8, z1013_state, empty_init, "VEB Robotron Electronics Riesa", "Z1013 (K7652/S6009)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1985, z1013k69, z1013,  0,      z1013k76, z1013,     z1013_state, empty_init, "VEB Robotron Electronics Riesa", "Z1013 (K7669)",       MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



z22.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/**************************************************************************

    Skeleton driver for Zenith Z-22 terminal.

    Despite featuring much of the same hardware as the MDT 60, with the
    CPU, CRTC and UART types and even dot clock frequency being identical,
    much else is different: a serial EEPROM replaces DIP switches, an 8254
    generates baud rates, fonts are uploaded to RAM, and the serial
    keyboard protocol is incompatible.

**************************************************************************/

#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/m6502/m6502.h"
#include "machine/eepromser.h"
#include "machine/i8251.h"
#include "machine/pit8253.h"
#include "video/mc6845.h"
#include "screen.h"


namespace {

class z22_state : public driver_device
{
public:
	z22_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_crtc(*this, "crtc")
		, m_uart(*this, "uart%u", 0U)
		, m_eeprom(*this, "eeprom")
		, m_dispram(*this, "dispram")
		, m_fontram(*this, "fontram")
		, m_eeprom_clk(false)
	{
	}

	void z22(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	MC6845_UPDATE_ROW(update_row);
	MC6845_ON_UPDATE_ADDR_CHANGED(update_cb);

	u8 irq_ack_r();
	u8 status_r();
	void control_w(u8 data);

	void eeprom_clock_w(int state);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<r6545_1_device> m_crtc;
	optional_device_array<i8251_device, 2> m_uart;
	required_device<eeprom_serial_93cxx_device> m_eeprom;
	required_shared_ptr<u8> m_dispram;
	required_shared_ptr<u8> m_fontram;

	bool m_eeprom_clk;
};

void z22_state::machine_start()
{
	save_item(NAME(m_eeprom_clk));
}

void z22_state::machine_reset()
{
	control_w(0);
}


MC6845_UPDATE_ROW(z22_state::update_row)
{
	u32 *pix = &bitmap.pix(y);

	for (unsigned x = 0; x < x_count; x++)
	{
		u8 cdata = m_dispram[(ma + x) & 0x7ff];
		u16 dots = m_fontram[(cdata & 0x7f) << 4 | ra] << 1;

		if (x == cursor_x)
			dots = ~dots;

		rgb_t fg = rgb_t::white();
		rgb_t bg = rgb_t::black();

		for (int n = 8; n >= 0; n--)
			*pix++ = BIT(dots, n) ? fg : bg;
	}
}

MC6845_ON_UPDATE_ADDR_CHANGED(z22_state::update_cb)
{
}


u8 z22_state::irq_ack_r()
{
	if (!machine().side_effects_disabled())
		m_maincpu->set_input_line(m6512_device::IRQ_LINE, CLEAR_LINE);
	return 0xff;
}

u8 z22_state::status_r()
{
	u8 result = 0;
	if (!m_eeprom->do_read())
		result |= 0x01;
	if (m_eeprom_clk)
		result |= 0x02;
	// TODO: bit 7 = received keyboard data
	return result;
}

void z22_state::control_w(u8 data)
{
	m_eeprom->di_write(!BIT(data, 7));
	m_eeprom->cs_write(BIT(data, 6));
	//m_keyboard->keyout_w(BIT(data, 0));
}

void z22_state::eeprom_clock_w(int state)
{
	m_eeprom_clk = state;
	if (state)
		m_maincpu->set_input_line(m6512_device::IRQ_LINE, ASSERT_LINE);
}

void z22_state::mem_map(address_map &map)
{
	map(0x0000, 0x07ff).ram();
	map(0x2000, 0x27ff).ram().share("dispram");
	map(0x4000, 0x4001).rw(m_uart[1], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x6000, 0x67ff).ram().share("fontram");
	map(0x8000, 0x8001).rw(m_uart[0], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x8800, 0x8800).rw(m_crtc, FUNC(r6545_1_device::status_r), FUNC(r6545_1_device::address_w));
	map(0x8801, 0x8801).rw(m_crtc, FUNC(r6545_1_device::register_r), FUNC(r6545_1_device::register_w));
	map(0x9000, 0x9003).w("pit", FUNC(pit8254_device::write));
	map(0x9800, 0x9800).r(FUNC(z22_state::irq_ack_r));
	map(0xa000, 0xa000).rw(FUNC(z22_state::status_r), FUNC(z22_state::control_w));
	map(0xc000, 0xffff).rom().region("program", 0);
}


static INPUT_PORTS_START(z22)
INPUT_PORTS_END

// XTAL frequency is specified as 16.589 MHz on actual parts. As with the MDT 60, this
// has been assumed to be a lower-precision specification of the common 16.5888 MHz value.

void z22_state::z22(machine_config &config)
{
	M6512(config, m_maincpu, 16.5888_MHz_XTAL / 9); // R6512AP
	m_maincpu->set_addrmap(AS_PROGRAM, &z22_state::mem_map);

	I8251(config, m_uart[0], 16.5888_MHz_XTAL / 9); // NEC D8251AC (U33) + Intel P8251A (U35)
	m_uart[0]->rxrdy_handler().set_inputline(m_maincpu, m6512_device::NMI_LINE);
	m_uart[0]->txd_handler().set("comm", FUNC(rs232_port_device::write_txd));
	m_uart[0]->dtr_handler().set("comm", FUNC(rs232_port_device::write_dtr));
	m_uart[0]->rts_handler().set("comm", FUNC(rs232_port_device::write_rts));

	I8251(config, m_uart[1], 16.5888_MHz_XTAL / 9);
	m_uart[1]->txd_handler().set("printer", FUNC(rs232_port_device::write_txd));

	EEPROM_93C06_16BIT(config, m_eeprom); // NMC9306 (U27)

	pit8254_device &pit(PIT8254(config, "pit")); // Intel P8254
	pit.set_clk<0>(16.5888_MHz_XTAL / 9);
	pit.set_clk<1>(16.5888_MHz_XTAL / 9);
	pit.set_clk<2>(16.5888_MHz_XTAL / 9);
	pit.out_handler<0>().set(m_eeprom, FUNC(eeprom_serial_93cxx_device::clk_write)); // weird synchronous clocking
	pit.out_handler<0>().append(FUNC(z22_state::eeprom_clock_w));
	pit.out_handler<1>().set(m_uart[0], FUNC(i8251_device::write_rxc));
	pit.out_handler<2>().set(m_uart[0], FUNC(i8251_device::write_txc)); // or the other way around?
	pit.out_handler<2>().append(m_uart[1], FUNC(i8251_device::write_txc));

	// TODO: Keyboard (asynchronous serial, like Z-49?)

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::amber());
	screen.set_raw(16.5888_MHz_XTAL, 873, 0, 720, 317, 0, 300);
	screen.set_screen_update(m_crtc, FUNC(r6545_1_device::screen_update));

	R6545_1(config, m_crtc, 16.5888_MHz_XTAL / 9); // R6545-1AP
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(9);
	m_crtc->set_update_row_callback(FUNC(z22_state::update_row));
	m_crtc->set_on_update_addr_change_callback(FUNC(z22_state::update_cb));

	// TODO: onboard buzzer

	rs232_port_device &comm(RS232_PORT(config, "comm", default_rs232_devices, nullptr));
	comm.rxd_handler().set(m_uart[0], FUNC(i8251_device::write_rxd));
	comm.dsr_handler().set(m_uart[0], FUNC(i8251_device::write_dsr));
	comm.cts_handler().set(m_uart[0], FUNC(i8251_device::write_cts));

	rs232_port_device &printer(RS232_PORT(config, "printer", default_rs232_devices, nullptr));
	printer.dsr_handler().set(m_uart[1], FUNC(i8251_device::write_dsr));
}


ROM_START(z22)
	ROM_REGION(0x4000, "program", 0)
	ROM_LOAD("u39.bin", 0x0000, 0x2000, CRC(2f62c1f8) SHA1(448581d987fee6b4303e481e78f46d3255baccbb)) // D2764A-3
	ROM_LOAD("u38.bin", 0x2000, 0x2000, CRC(f0bfe9b5) SHA1(8807841b28549d0ddf30275fc6035a66093f8768)) // D2764A-3
ROM_END

} // anonymous namespace


SYST(1984, z22, 0, 0, z22, z22, z22_state, empty_init, "Zenith Data Systems", "Z-22 Terminal", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)



z29.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************

    Skeleton driver for Zenith Z-29 (alias Heathkit H-29) video terminal.

****************************************************************************/

#include "emu.h"
//#include "bus/rs232/rs232.h"
#include "bus/z29_kbd/keyboard.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/x2212.h"
#include "video/i8275.h"
#include "screen.h"


namespace {

class z29_state : public driver_device
{
public:
	z29_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_keyboard(*this, "keyboard")
		, m_crtc(*this, "crtc%u", 1U)
		, m_nvram(*this, "nvram")
		, m_charmem(*this, "charmem")
		, m_attrmem(*this, "attrmem")
		, m_chargen(*this, "chargen")
		, m_dmatype(true)
		, m_keyin(true)
	{
	}

	void z29(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;

private:
	void keyin_w(int state);
	u8 p1_r();
	void p3_w(u8 data);
	u8 bs_24k_r(offs_t offset);
	void crtc_w(offs_t offset, u8 data);
	void latch_12k_w(u8 data);

	void prg_map(address_map &map) ATTR_COLD;
	void ext_map(address_map &map) ATTR_COLD;

	required_device<mcs51_cpu_device> m_maincpu;
	required_device<z29_keyboard_port_device> m_keyboard;
	required_device_array<i8276_device, 2> m_crtc;
	required_device<x2210_device> m_nvram;

	required_shared_ptr<u8> m_charmem;
	required_shared_ptr<u8> m_attrmem;
	required_region_ptr<u8> m_chargen;

	bool m_dmatype;
	bool m_keyin;
};

void z29_state::machine_start()
{
	save_item(NAME(m_dmatype));
	save_item(NAME(m_keyin));
}

void z29_state::keyin_w(int state)
{
	m_keyin = state;
}

/**
 * Port 1 (based on ROM listing)
 *
 * bit 0 - KB output line
 * bit 1 - KB input line
 * bit 2 - 0 = normal video       1 = suppressed
 * bit 3 - 0 = setup mode locked  1 = normal
 * bit 4 - Data Terminal Ready
 * bit 5 - Ready to Send
 * bit 6 - Expansion socket
 * bit 7 - Clock run for CRT controller
 */
u8 z29_state::p1_r()
{
	return m_keyin ? 0xfd : 0xff;
}

/**
 * Port 3 (based on ROM listing)
 *
 * bit 1 - predefined serial port transmit pin
 * bit 4 - 0 = clear memory  1 = DMA CRTC
 * bit 5 - Clear to Send
 */
void z29_state::p3_w(u8 data)
{
	m_dmatype = BIT(data, 4);
}

u8 z29_state::bs_24k_r(offs_t offset)
{
	if (!machine().side_effects_disabled())
	{
		if (m_dmatype)
		{
			u8 chardata = m_charmem[offset];
			u8 attrdata = m_attrmem[offset] & 0xf;

			m_crtc[0]->dack_w(chardata & 0x7f);
			m_crtc[1]->dack_w((chardata & 0x60) | (BIT(chardata, 7) ? 0x10 : 0) | attrdata);
		}
		else
		{
			m_charmem[offset] = 0x20;
			m_attrmem[offset] = 0;
		}
	}

	return m_dmatype ? 0x24 : 0x20;
}

void z29_state::crtc_w(offs_t offset, u8 data)
{
	m_crtc[0]->write(offset, data);
	m_crtc[1]->write(offset, data);
}

void z29_state::latch_12k_w(u8 data)
{
	m_nvram->store(!BIT(data, 0));
	m_nvram->recall(!BIT(data, 3));
}

void z29_state::prg_map(address_map &map)
{
	map(0x0000, 0x1fff).rom().region("maincpu", 0);
	map(0x6000, 0x67ff).mirror(0x800).r(FUNC(z29_state::bs_24k_r));
}

void z29_state::ext_map(address_map &map)
{
	map(0x2000, 0x2001).mirror(0xffe).r("crtc1", FUNC(i8276_device::read)).w(FUNC(z29_state::crtc_w));
	map(0x3000, 0x3000).mirror(0xfff).w(FUNC(z29_state::latch_12k_w));
	map(0x4000, 0x47ff).mirror(0x800).ram().share("attrmem");
	map(0x5000, 0x57ff).mirror(0x800).ram().share("charmem");
	map(0x7000, 0x703f).mirror(0xfc0).rw("nvram", FUNC(x2210_device::read), FUNC(x2210_device::write));
}


static INPUT_PORTS_START(z29)
INPUT_PORTS_END


void z29_state::z29(machine_config &config)
{
	I8031(config, m_maincpu, 14.784_MHz_XTAL / 2); // Intel P8031AH
	m_maincpu->set_addrmap(AS_PROGRAM, &z29_state::prg_map);
	m_maincpu->set_addrmap(AS_IO, &z29_state::ext_map);
	m_maincpu->port_in_cb<1>().set(FUNC(z29_state::p1_r));
	m_maincpu->port_out_cb<1>().set(m_keyboard, FUNC(z29_keyboard_port_device::keyout_w)).bit(0).invert();
	m_maincpu->port_out_cb<3>().set(FUNC(z29_state::p3_w));

	X2210(config, m_nvram);

	Z29_KEYBOARD(config, m_keyboard, z29_keyboards, "heath");
	m_keyboard->keyin_callback().set(FUNC(z29_state::keyin_w));
	m_keyboard->reset_callback().set_inputline(m_maincpu, INPUT_LINE_RESET).invert();

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(14.784_MHz_XTAL, 880, 0, 640, 280, 0, 250);
	screen.set_screen_update("crtc1", FUNC(i8276_device::screen_update));

	I8276(config, m_crtc[0], 14.784_MHz_XTAL / 8).set_character_width(8);
	m_crtc[0]->drq_wr_callback().set_inputline(m_maincpu, MCS51_INT0_LINE);
	m_crtc[0]->irq_wr_callback().set_inputline(m_maincpu, MCS51_INT1_LINE);

	I8276(config, m_crtc[1], 14.784_MHz_XTAL / 8).set_character_width(8);
}


ROM_START(z29) // All EPROMs on 85-2835-1 terminal board are HN482732AG-30
	ROM_REGION(0x2000, "maincpu", 0)
	ROM_LOAD("u440.bin", 0x0000, 0x1000, CRC(169b9517) SHA1(c18b6a193655a64808e9ae8765d3e54d13e6669e)) // occupies pins 9-32 of 40-pin expansion socket
	ROM_LOAD("u407.bin", 0x1000, 0x1000, CRC(b5aae8e6) SHA1(692e521a85d7e07647c66a660faa2041d1bfd785))

	ROM_REGION(0x1000, "chargen", 0)
	ROM_LOAD("u429.bin", 0x0000, 0x1000, CRC(5e3bc5bf) SHA1(18d73e3d74a9768bee8b063ea45891f955558ae7))
ROM_END

} // anonymous namespace

//    year  name   parent compat  machine  input class      init        company                fullname          flags
COMP( 1983, z29,   0,     0,      z29,     z29,  z29_state, empty_init, "Zenith Data Systems", "Z-29 Terminal",  MACHINE_NO_SOUND | MACHINE_NOT_WORKING)



z80clock.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders: smf
/***************************************************************************

      Z80 based, triple time zone clock

      https://github.com/tomstorey/Z80_clock

TODO:
      set rtc to utc time instead of local time at startup
      implement pwm brightness & remove clear_display code
      improve led driver selection

****************************************************************************/

#include "emu.h"

#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "bus/rs232/rs232.h"
#include "cpu/z80/z80.h"
#include "imagedev/snapquik.h"
#include "machine/bq4847.h"
#include "machine/clock.h"
#include "machine/z80ctc.h"
#include "machine/z80daisy.h"
#include "machine/z80sio.h"
#include "sound/spkrdev.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "z80clock.lh"


namespace {

class z80clock_state : public driver_device
{
public:
	z80clock_state(const machine_config& mconfig, device_type type, const char* tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_speaker(*this, "speaker"),
		m_ne555(*this, "ne555"),
		m_ctc(*this, "ctc"),
		m_ctc_clock(*this, "ctc_clock"),
		m_sio(*this, "sio"),
		m_sio_clock(*this, "sio_clock"),
		m_sv2(*this, "sv2"),
		m_sv3(*this, "sv3"),
		m_rtc(*this, "rtc"),
		m_u12(*this, "u12"),
		m_u13(*this, "u13"),
		m_u14(*this, "u14"),
		m_u15(*this, "u15"),
		m_col(*this, "row%u.col%u", 0U, 0U),
		m_dp(*this, "row%u.dp%u", 0U, 0U),
		m_debug(*this, "debug%u", 0U),
		m_jp1(*this, "JP1"),
		m_jp2(*this, "JP2"),
		m_jp3(*this, "JP3"),
		m_jp4(*this, "JP4"),
		m_jp5(*this, "JP5"),
		m_jp6(*this, "JP6")
	{
	}

	void z80clock(machine_config& config)
	{
		static const z80_daisy_config z80clock_daisy_chain[] =
		{
			{ "ctc" },
			{ "sio" },
			{ nullptr }
		};

		Z80(config, m_maincpu, 6_MHz_XTAL);
		m_maincpu->set_daisy_config(z80clock_daisy_chain);
		m_maincpu->set_addrmap(AS_PROGRAM, &z80clock_state::mem_map);
		m_maincpu->set_addrmap(AS_IO, &z80clock_state::io_map);

		SPEAKER(config, "mono").front_center();
		SPEAKER_SOUND(config, m_speaker);
		m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);

		CLOCK(config, m_ne555, 8589).signal_handler().set(FUNC(z80clock_state::ne555));
		m_ne555->set_clock_scale(0.0);
		m_ne555->set_duty_cycle(2/3.0f);

		Z80CTC(config, m_ctc, 6_MHz_XTAL);
		m_ctc->intr_callback().set_inputline(m_maincpu, 0);
		m_ctc->zc_callback<0>().set(FUNC(z80clock_state::ctc_sound<0>));
		m_ctc->zc_callback<1>().set(FUNC(z80clock_state::ctc_sound<1>));
		m_ctc->zc_callback<2>().set(FUNC(z80clock_state::ctc_sound<2>));

		BQ4845(config, m_rtc, 32.768_kHz_XTAL);
		m_rtc->int_handler().set(m_ctc, FUNC(z80ctc_device::trg3));
		m_rtc->rst_handler().set([this](int state) { if (!state && started() && m_jp2->read()) machine().schedule_soft_reset(); }); // HACK: inputs cannot be read during startup & can't hold machine reset low
		m_rtc->write_wdi(1);

		CLOCK(config, m_ctc_clock, 4.096_MHz_XTAL).signal_handler().set(FUNC(z80clock_state::prescaler));

		Z80SIO(config, m_sio, 6_MHz_XTAL);
		RS232_PORT(config, m_sv2, default_rs232_devices, nullptr);
		RS232_PORT(config, m_sv3, default_rs232_devices, nullptr);
		CLOCK(config, m_sio_clock, 3.6864_MHz_XTAL).signal_handler().set([this](int state) { m_sio->rxca_w(state); m_sio->txca_w(state); m_sio->rxtxcb_w(state); });

		m_sio->out_rtsa_callback().set(m_sv2, FUNC(rs232_port_device::write_rts));
		m_sv2->rxd_handler().set(m_sio, FUNC(z80sio_device::rxa_w));
		m_sio->out_txda_callback().set(m_sv2, FUNC(rs232_port_device::write_txd));
		m_sv2->cts_handler().set(m_sio, FUNC(z80sio_device::ctsa_w));
		m_sio->out_dtra_callback().set(m_sio, FUNC(z80sio_device::dcda_w));

		m_sio->out_rtsb_callback().set(m_sv3, FUNC(rs232_port_device::write_rts));
		m_sv3->rxd_handler().set(m_sio, FUNC(z80sio_device::rxa_w));
		m_sio->out_txdb_callback().set(m_sv3, FUNC(rs232_port_device::write_txd));
		m_sv3->cts_handler().set(m_sio, FUNC(z80sio_device::ctsa_w));
		m_sio->out_dtrb_callback().set(m_sio, FUNC(z80sio_device::dcdb_w));

		config.set_default_layout(layout_z80clock);

		GENERIC_CARTSLOT(config, m_u12, generic_plain_slot, "u12").set_must_be_loaded(true);
		GENERIC_CARTSLOT(config, m_u14, generic_plain_slot, "u14");
		GENERIC_CARTSLOT(config, m_u15, generic_plain_slot, "u15");

		auto& quickload(QUICKLOAD(config, "u13", "rom"));
		quickload.set_load_callback(FUNC(z80clock_state::quickload_u13));
		quickload.set_interface("u13");

		SOFTWARE_LIST(config, "rom_list").set_original("z80clock");
	}

protected:
	virtual void machine_start() override
	{
		m_col.resolve();
		m_dp.resolve();
		m_debug.resolve();

		m_shift.resize(17);

		save_item(NAME(m_shift));

		m_clear_display_timer = timer_alloc(FUNC(z80clock_state::clear_display), this);
		m_clear_display_timer->adjust(attotime::zero, 0, attotime::from_hz(240));
		m_clear_display_count.resize(3);

		save_item(NAME(m_clear_display_count));

		save_item(NAME(m_ctc_sound_state));
		save_item(NAME(m_ctc_sound_flipflop));
	}

	virtual void machine_reset() override
	{
		/// HACK: start the sio clock on first write for speed
		m_sio_clock->set_clock_scale(0.0);
	}

private:
	required_device<z80_device> m_maincpu;
	required_device<speaker_sound_device> m_speaker;
	required_device<clock_device> m_ne555;
	required_device<z80ctc_device> m_ctc;
	required_device<clock_device> m_ctc_clock;
	required_device<z80sio_device> m_sio;
	required_device<clock_device> m_sio_clock;
	required_device<rs232_port_device> m_sv2;
	required_device<rs232_port_device> m_sv3;
	required_device<bq4845_device> m_rtc;
	required_device<generic_slot_device> m_u12;
	required_shared_ptr<uint8_t> m_u13;
	required_device<generic_slot_device> m_u14;
	required_device<generic_slot_device> m_u15;
	output_finder<3, 8> m_col;
	output_finder<3, 8> m_dp;
	output_finder<8> m_debug;
	required_ioport m_jp1;
	required_ioport m_jp2;
	required_ioport m_jp3;
	required_ioport m_jp4;
	required_ioport m_jp5;
	required_ioport m_jp6;
	std::vector<uint8_t> m_shift;
	emu_timer* m_clear_display_timer;
	std::vector<uint8_t> m_clear_display_count;
	int m_ctc_sound_state;
	bool m_ctc_sound_flipflop;

	void mem_map(address_map& map)
	{
		map.unmap_value_high();
		map(0x0000, 0x5fff).rom().r(m_u12, FUNC(generic_slot_device::read_rom));
		map(0x6000, 0x9fff).ram().share("u13");
		map(0xc000, 0xdfff).rom().r(m_u14, FUNC(generic_slot_device::read_rom));
		map(0xe000, 0xffff).rom().r(m_u15, FUNC(generic_slot_device::read_rom));
	}

	void io_map(address_map& map)
	{
		map.unmap_value_high();
		map.global_mask(0xff);
		map(0x00, 0x03).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
		map(0x04, 0x04).w(FUNC(z80clock_state::wdt_poke_w));
		map(0x08, 0x08).portr("BTN");
		map(0x09, 0x09).portr("TZ_SW1");
		map(0x0a, 0x0a).portr("TZ_SW2");
		map(0x0b, 0x0b).portr("TZ_SW3");
		map(0x0c, 0x0c).w(FUNC(z80clock_state::outputs_w));
		map(0x10, 0x1f).rw(m_rtc, FUNC(bq4845_device::read), FUNC(bq4845_device::write));
		map(0x20, 0x20).w(FUNC(z80clock_state::disp_data_w));
		map(0x21, 0x21).w(FUNC(z80clock_state::disp_ctrl_w));
		map(0xde, 0xde).w(FUNC(z80clock_state::debug_port_w));
		map(0xfc, 0xff).r(m_sio, FUNC(z80sio_device::cd_ba_r));
		map(0xfc, 0xff).w(FUNC(z80clock_state::sio_cd_ba_w));
	}

	void wdt_poke_w(uint8_t data)
	{
		m_rtc->write_wdi(0);
		m_rtc->write_wdi(1);
	}

	void outputs_w(uint8_t data)
	{
		m_ne555->set_clock_scale(BIT(data, 7) ? 1.0 : 0.0);
	}

	void disp_data_w(uint8_t data)
	{
		std::copy_backward(m_shift.begin(), m_shift.end() - 1, m_shift.end());

		m_shift[0] = data;
	}

	void disp_ctrl_w(uint8_t data)
	{
		int row = (data & 3) - 1;
		if (row >= 0)
		{
			const char* led_driver = m_u15->get_feature("led_driver");
			uint16_t invert = (led_driver && !strcmp(led_driver, "74hc595")) ? 0xffff : 0x0000;

			for (int i = 0; i < 8; i++)
			{
				m_col[row][i] = bitswap<16>((m_shift[(i * 2) + 1] << 8) | m_shift[(i * 2) + 2], 3, 6, 10, 14, 13, 9, 4, 12, 11, 15, 0, 1, 2, 5, 7, 8) ^ invert;
				m_dp[row][i] = BIT(m_shift[0] ^ invert, i);
			}

			m_clear_display_count[row] = 2;
		}
	}

	TIMER_CALLBACK_MEMBER(clear_display)
	{
		for (int row = 0; row < 3; row++)
			if (m_clear_display_count[row] != 0)
			{
				m_clear_display_count[row]--;
				if (m_clear_display_count[row] == 0)
				{
					for (int i = 0; i < 8; i++)
					{
						m_col[row][i] = 0;
						m_dp[row][i] = 0;
					}
				}
			}
	}

	void ne555(int state)
	{
		if (m_jp3->read())
			m_speaker->level_w(state);
	}

	template<int channel>
	void ctc_sound(int state)
	{
		if (!BIT(m_jp5->read(), channel))
		{
			if (!m_jp3->read() && m_jp6->read())
				m_speaker->level_w(state);

			if (state && !m_ctc_sound_state)
			{
				m_ctc_sound_flipflop = !m_ctc_sound_flipflop;

				if (!m_jp3->read() && !m_jp6->read())
					m_speaker->level_w(m_ctc_sound_flipflop);
			}

			m_ctc_sound_state = state;
		}
	}

	void debug_port_w(uint8_t data)
	{
		for (int i = 0; i < 8; i++)
			m_debug[i] = BIT(data, i);
	}

	void sio_cd_ba_w(offs_t offset, uint8_t data)
	{
		/// HACK: start the sio clock on first write for speed
		m_sio_clock->set_clock_scale(1.0);
		m_sio->cd_ba_w(offset, data);
	}

	void prescaler(int state)
	{
		auto ch = m_jp4->read();
		m_ctc->trg0(state | BIT(ch, 0));
		m_ctc->trg1(state | BIT(ch, 1));
		m_ctc->trg2(state | BIT(ch, 2));
		m_ctc_clock->set_clock_scale(1.0 / m_jp1->read());
	}

	QUICKLOAD_LOAD_MEMBER(quickload_u13)
	{
		auto length = image.length();
		if (length > m_u13.bytes())
			return std::make_pair(image_error::INVALIDLENGTH, std::string());

		if (image.fread(m_u13, length) != length)
			return std::make_pair(image_error::UNSPECIFIED, std::string());

		m_maincpu->set_pc(0x6000);
		return std::make_pair(std::error_condition(), std::string());
	}
};

INPUT_PORTS_START(z80clock)
	PORT_START("JP1")
	PORT_CONFNAME(0x700, 0x0100, "JP1 Timer Prescaler (4.096mhz/256)")
	PORT_CONFSETTING(   0x0400, "5-6 /4 (4khz)")
	PORT_CONFSETTING(   0x0200, "3-4 /2 (8khz)")
	PORT_CONFSETTING(   0x0100, "1-2 /1 (16khz)")

	PORT_START("JP2")
	PORT_CONFNAME(0x1, 0x01, "JP2 Watchdog Reset Enable")
	PORT_CONFSETTING(0x01, DEF_STR(On))
	PORT_CONFSETTING(0x00, DEF_STR(Off))

	PORT_START("JP3")
	PORT_CONFNAME(0x1, 0x00, "JP3 Sound Source")
	PORT_CONFSETTING(0x01, "1-2 555")
	PORT_CONFSETTING(0x00, "2-3 CTC")

	PORT_START("JP4")
	PORT_CONFNAME(0x1, 0x00, "JP4 1-2 Timer Prescaler CTC CH0")
	PORT_CONFSETTING(0x01, DEF_STR(Off))
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFNAME(0x2, 0x00, "JP4 3-4 Timer Prescaler CTC CH1")
	PORT_CONFSETTING(0x02, DEF_STR(Off))
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFNAME(0x4, 0x00, "JP4 5-6 Timer Prescaler CTC CH2")
	PORT_CONFSETTING(0x04, DEF_STR(Off))
	PORT_CONFSETTING(0x00, DEF_STR(On))

	PORT_START("JP5")
	PORT_CONFNAME(0x1, 0x00, "JP5 1-2 Sound CTC CH0")
	PORT_CONFSETTING(0x01, DEF_STR(Off))
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFNAME(0x2, 0x02, "JP5 3-4 Sound CTC CH1")
	PORT_CONFSETTING(0x02, DEF_STR(Off))
	PORT_CONFSETTING(0x00, DEF_STR(On))
	PORT_CONFNAME(0x4, 0x04, "JP5 5-6 Sound CTC CH2")
	PORT_CONFSETTING(0x04, DEF_STR(Off))
	PORT_CONFSETTING(0x00, DEF_STR(On))

	PORT_START("JP6")
	PORT_CONFNAME(0x1, 0x00, "JP6 1-2 Sound CTC flip flop")
	PORT_CONFSETTING(0x01, "1-2 Off")
	PORT_CONFSETTING(0x00, "2-3 On")

	PORT_START("BTN")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SW1 UP") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SW2 DN") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SW3 ENT") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SW4 ESC") PORT_CODE(KEYCODE_ESC)
	PORT_DIPNAME(0x30, 0x20, "Dim 21:00 to 05:59") PORT_DIPLOCATION("SW5:1,2")
	PORT_DIPSETTING(0x30, "Disable")
	PORT_DIPSETTING(0x20, "Row 1 timezone")
	PORT_DIPSETTING(0x10, "Row 2 timezone")
	PORT_DIPSETTING(0x00, "Row 3 timezone")
	PORT_DIPNAME(0x40, 0x00, "Dim Duty Cycle") PORT_DIPLOCATION("SW5:3")
	PORT_DIPSETTING(0x40, "50%")
	PORT_DIPSETTING(0x00, "25%")
	PORT_DIPNAME(0x80, 0x80, DEF_STR(Unused)) PORT_DIPLOCATION("SW5:4")
	PORT_DIPSETTING(0x80, DEF_STR(Off))
	PORT_DIPSETTING(0x00, DEF_STR(On))

	#define DIP_TIMEZONE(row, loc, def) \
		PORT_START("TZ_SW" #row) \
		PORT_DIPNAME(0x80, ~(def) & 0x80, "Row " #row " type") PORT_DIPLOCATION("SW" #loc ":8") \
		PORT_DIPSETTING(0x80, "Time") \
		PORT_DIPSETTING(0x00, "Date") \
		\
		PORT_DIPNAME(0x70, ~(def) & 0x70, "Row " #row " timezone high") PORT_DIPLOCATION("SW" #loc ":5,6,7") \
		PORT_DIPSETTING(0x70, "0_") \
		PORT_DIPSETTING(0x60, "1_") \
		PORT_DIPSETTING(0x50, "2_") \
		PORT_DIPSETTING(0x40, "3_") \
		PORT_DIPSETTING(0x30, "4_") \
		PORT_DIPSETTING(0x20, "5_") \
		PORT_DIPSETTING(0x10, "6_") \
		PORT_DIPSETTING(0x00, "7_") \
		\
		PORT_DIPNAME(0x0f, ~(def) & 0xf, "Row " #row " timezone low") PORT_DIPLOCATION("SW" #loc ":1,2,3,4") \
		PORT_DIPSETTING(0x0f, "_0") \
		PORT_DIPSETTING(0x0e, "_1") \
		PORT_DIPSETTING(0x0d, "_2") \
		PORT_DIPSETTING(0x0c, "_3") \
		PORT_DIPSETTING(0x0b, "_4") \
		PORT_DIPSETTING(0x0a, "_5") \
		PORT_DIPSETTING(0x09, "_6") \
		PORT_DIPSETTING(0x08, "_7") \
		PORT_DIPSETTING(0x07, "_8") \
		PORT_DIPSETTING(0x06, "_9") \
		PORT_DIPSETTING(0x05, "_a") \
		PORT_DIPSETTING(0x04, "_b") \
		PORT_DIPSETTING(0x03, "_c") \
		PORT_DIPSETTING(0x02, "_d") \
		PORT_DIPSETTING(0x01, "_e") \
		PORT_DIPSETTING(0x00, "_f")

	DIP_TIMEZONE(1, 6, 0x02)
	DIP_TIMEZONE(2, 7, 0x82)
	DIP_TIMEZONE(3, 8, 0x03)
INPUT_PORTS_END

ROM_START( z80clock )
ROM_END

} // anonymous namespace

COMP(2020, z80clock, 0, 0, z80clock, z80clock, z80clock_state, empty_init, "Tom Storey", "Z80 based, triple time zone clock", MACHINE_SUPPORTS_SAVE)



z80dev.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

Z80 dev board (unknown)

http://retro.hansotten.nl/index.php?page=z80-dev-kit

2010-04-23 Skeleton driver.

Pasting:
        0-F : as is
        MEM --> (inc) : ^
        MEM <-- (dec) : V
        GO : X
        To set an address, paste R0<address>, so R01040 to select 1040

Example paste: R01040^11^22^33^44^55^66^77^88^99^^R01040^
Press the up-arrow key to confirm data has been entered.


****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "z80dev.lh"

namespace {

class z80dev_state : public driver_device
{
public:
	z80dev_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_digits(*this, "digit%u", 0U)
	{ }

	void z80dev(machine_config &config);

private:
	void display_w(offs_t offset, uint8_t data);
	uint8_t test_r();

	virtual void machine_start() override ATTR_COLD;

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	output_finder<6> m_digits;
};

void z80dev_state::display_w(offs_t offset, uint8_t data)
{
	// ---- xxxx digit
	// xxxx ---- ???
	static const uint8_t hex_7seg[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};

	m_digits[offset] = hex_7seg[data & 0x0f];
}

uint8_t z80dev_state::test_r()
{
	return machine().rand();
}

void z80dev_state::machine_start()
{
	m_digits.resolve();
}

void z80dev_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).rom();
	map(0x1000, 0x10ff).ram();
}

void z80dev_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x20, 0x20).portr("LINE0");
	map(0x21, 0x21).portr("LINE1");
	map(0x22, 0x22).portr("LINE2");
	map(0x23, 0x23).portr("LINE3");
	map(0x20, 0x25).w(FUNC(z80dev_state::display_w));

	map(0x13, 0x13).r(FUNC(z80dev_state::test_r));
}

/* Input ports */
INPUT_PORTS_START( z80dev )
	PORT_START("LINE0")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RUN") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("STEP") PORT_CODE(KEYCODE_T)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("BK") PORT_CODE(KEYCODE_K)
	PORT_START("LINE1")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("REG") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MEM <--") PORT_CODE(KEYCODE_DOWN) PORT_CHAR('V')
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MEM -->") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_START("LINE2")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("MV") PORT_CODE(KEYCODE_M)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("EI") PORT_CODE(KEYCODE_I)
		PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I/O") PORT_CODE(KEYCODE_O)
	PORT_START("LINE3")
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
		PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
		PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
		PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("SV") PORT_CODE(KEYCODE_S)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("LD") PORT_CODE(KEYCODE_L)
INPUT_PORTS_END

void z80dev_state::z80dev(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 4_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &z80dev_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &z80dev_state::io_map);

	/* video hardware */
	config.set_default_layout(layout_z80dev);
}

/* ROM definition */
ROM_START( z80dev )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "z80dev.bin", 0x0000, 0x0800, CRC(dd5b9cd9) SHA1(97c176fcb63674f0592851b7858cb706886b5857))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY      FULLNAME         FLAGS */
COMP( 198?, z80dev, 0,      0,      z80dev,  z80dev, z80dev_state, empty_init, "<unknown>", "Z80 dev board", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



z80ne.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Roberto Lavarone
/******************************************************************************

    Nuova Elettronica Z80NE system driver

    Preliminary driver

    Roberto Lavarone, 2009-01-05

    Thanks go to:
        Roberto Bazzano: http://www.z80ne.com/

    Z80NE memory map

        LX.382 CPU board
            range     short     description
            0000-03FF RAM
            8000-83FF EPROM     firmware

        LX.383 HEX keyboard and display controller for LX.384 hex keyboard and display
            range     short     description
            F0-F7     I/O       7-segment LED dual-port RAM write
            F0        I/O       keyboard read
            F8        I/O       enable single step for next instruction

        LX.385 Cassette interface
            range     short     description
            EE        I/O       UART Data Read/Write
            EF        I/O       UART Status/Control - Cassette Tape Control

        LX.392 32K Memory expansion board
            range     short     description

        LX.389 Printer Interface
            range     short     description
            02-03
            06-07
            0A-0B
            0E-0F
            12-13
            16-17
            1A-1B
            1E-1F

        LX.548 16K Basic eprom
            range     short     description
            0000-3FFF EPROM     firmware

        LX.388 Video Interface
            range     short     description
            EC00-EDFF RAM
            EA        I/O       keyboard
            EB        I/O       video retrace

        LX.529 Graphics Video and Printer Interface
            range     short     description
            80        I/O       PIO 0 port A data (ram 0)
            81        I/O       PIO 0 port A control (ram 0)
            82        I/O       PIO 0 port B data (printer)
            83        I/O       PIO 0 port B control (printer)
            84        I/O       PIO 1 port A data (ram 1)
            85        I/O       PIO 1 port A control (ram 1)
            86        I/O       PIO 1 port B data (keyboard)
            87        I/O       PIO 1 port B control (keyboard)
            88        I/O       PIO 2 port A data (ram 2)
            89        I/O       PIO 2 port A control (ram 2)
            8A        I/O       PIO 2 port B data (printer busy + 40/80 video chars)
            8B        I/O       PIO 2 port B control (printer busy + 40/40 video chars)
            8C        I/O       SY6845 address and status register
            8D        I/O       SY6845 data register
            8E        I/O       RAM 3 character attribute
            8F        I/O       beeper

        LX.390 Floppy Interface
            range     short     description
            F000-F3FF EPROM     firmware
            D0        I/O       command/status register
            D1        I/O       trace register
            D2        I/O       sector register
            D3        I/O       data register (write only if controller idle)
            D6        I/O       drive select / drive side one select
            D7        I/O       data register (write always)

        LX.394-395 EPROM Programmer
            range     short     description
            9000-9FFF EPROM     EPROM to be written
            8400-8FFF EPROM     firmware

Quick Instructions:
  Z80NE:
  - Use 0-F to enter an address (or use mouse in artwork)
  - Hold CTRL press 0 to show data at that address (CTRL cannot be held with mouse)
  - Use 0-F to enter data
  - CTRL 0 to advance to next address
  - CTRL 2 to view/change CPU registers
  - CTRL 0 change register, advance to next
  Z80NET
  - In Machine Configuration, select your preferred baud rate, and reboot machine.
  - CTRL 6 to load a tape, press A or B to choose tape device, choose Play.
  - After this, click any key, enter 1000, CTRL 4 to run.
  - CTRL 5 to save
  - All tapes in software list (except bioritmi & tapebas) are BASIC programs.
  - To use tapebas, load side 1 in the normal way, run it, select side 2 for cas 0, play, the rest loads
  - The Basic is bilingual - ENG for English, ITA for Italian, so enter ENG.
  - Then for any Basic program in software list: CLOAD hit Play RUN
  Z80NETB
  - BASIC-only, English only. A version of TRS80 Level II Basic. Not compatible with software list.
  Z80NETF
  - There is a choice of 5 bioses, via the Machine Configuration menu
  - EP382:  same as Z80NET.
  - EP548:  same as Z80NETB.
  - EP390:  for swlist-item "basic55k". Press B, from the menu select 2, runs. Type ENG for English.
            It can load and run tapes from the swlist, although it seems to often have load errors.
  - EP1390: requires a floppy to boot from. Disks marked as NE-DOS 1.5 should work.
  - EP2390: uses ports 8x, not emulated, not working. For NE-DOS G.1

Natural Keyboard and Paste:
    - The hexpad keys conflict with the keyboard keys, therefore Natural Keyboard is not
      supported. Paste is meant for Z80NE only.

    - Test Paste:
      N1000=11=22=33=44=55=66=77=88=99=N1000=
      Press Ctrl+0 to review the data.

*********************************************************************************************************/

/* Core includes */
#include "emu.h"

#include "cpu/z80/z80.h"
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "machine/ay31015.h"
#include "machine/clock.h"
#include "machine/kr2376.h"
#include "machine/ram.h"
#include "machine/wd_fdc.h"
#include "video/mc6847.h"

#include "softlist_dev.h"
#include "speaker.h"

#include "formats/dmk_dsk.h"

//#define VERBOSE 1
#include "logmacro.h"

/* Layout */
#include "z80ne.lh"
#include "z80net.lh"
#include "z80netb.lh"
#include "z80netf.lh"


/******************************************************************************
 Constants
******************************************************************************/

namespace {

#define Z80NE_CPU_SPEED_HZ      1920000 /* 1.92 MHz */

#define LX383_KEYS              16
#define LX383_DOWNSAMPLING      16

#define LX385_TAPE_SAMPLE_FREQ  38400

/* wave duration threshold */
enum z80netape_speed
{
	TAPE_300BPS  = 300, /*  300 bps */
	TAPE_600BPS  = 600, /*  600 bps */
	TAPE_1200BPS = 1200 /* 1200 bps */
};

struct z80ne_cass_data_t {
	struct {
		int length = 0;     /* time cassette level is at input.level */
		int level = 0;      /* cassette level */
		int bit = 0;        /* bit being read */
	} input;
	struct {
		int length = 0;     /* time cassette level is at output.level */
		int level = 0;      /* cassette level */
		int bit = 0;        /* bit to output */
	} output;
	z80netape_speed speed;  /* 300 - 600 - 1200 */
	int wave_filter = 0;
	int wave_length = 0;
	int wave_short = 0;
	int wave_long = 0;
};


class z80ne_state : public driver_device
{
public:
	z80ne_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_uart(*this, "uart"),
		m_uart_clock(*this, "uart_clock"),
		m_maincpu(*this, "maincpu"),
		m_cassette1(*this, "cassette"),
		m_cassette2(*this, "cassette2"),
		m_ram(*this, RAM_TAG),
		m_bank1(*this, "bank1"),
		m_bank2(*this, "bank2"),
		m_bank3(*this, "bank3"),
		m_bank4(*this, "bank4"),
		m_io_row0(*this, "ROW0"),
		m_io_row1(*this, "ROW1"),
		m_io_ctrl(*this, "CTRL"),
		m_io_rst(*this, "RST"),
		m_io_lx_385(*this, "LX.385"),
		m_lx383_digits(*this, "digit%u", 0U),
		m_rom(*this, "maincpu"),
		m_mram(*this, "mainram")
	{ }

	void z80ne(machine_config &config);
	void init_z80ne();

	DECLARE_INPUT_CHANGED_MEMBER(z80ne_reset);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	void base_reset();
	void save_state_vars();

	static void floppy_formats(format_registration &fr);

	uint8_t m_lx383_scan_counter = 0;
	uint8_t m_lx383_key[LX383_KEYS]{};
	int m_lx383_downsampler = 0;
	uint8_t m_lx385_ctrl = 0;
	emu_timer *m_cassette_timer = nullptr;
	emu_timer *m_kbd_timer = nullptr;
	z80ne_cass_data_t m_cass_data;

	uint8_t lx383_r();
	void lx383_w(offs_t offset, uint8_t data);
	uint8_t lx385_ctrl_r();
	void lx385_ctrl_w(uint8_t data);
	void lx385_uart_tx_clock_w(int state);

	TIMER_CALLBACK_MEMBER(cassette_tc);
	TIMER_CALLBACK_MEMBER(kbd_scan);
	TIMER_CALLBACK_MEMBER(pulse_nmi);

	memory_passthrough_handler m_rom_shadow_tap;
	required_device<ay31015_device> m_uart;
	required_device<clock_device> m_uart_clock;
	required_device<cpu_device> m_maincpu;
	required_device<cassette_image_device> m_cassette1;
	required_device<cassette_image_device> m_cassette2;
	optional_device<ram_device> m_ram;
	optional_memory_bank m_bank1;
	optional_memory_bank m_bank2;
	optional_memory_bank m_bank3;
	optional_memory_bank m_bank4;
	required_ioport m_io_row0;
	required_ioport m_io_row1;
	required_ioport m_io_ctrl;
	required_ioport m_io_rst;
	required_ioport m_io_lx_385;
	output_finder<8> m_lx383_digits;
	required_region_ptr<u8> m_rom;
	optional_shared_ptr<u8> m_mram;

	emu_timer *m_timer_nmi = nullptr;

	cassette_image_device *cassette_device_image();

private:
	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;
};

class z80net_state : public z80ne_state
{
public:
	z80net_state(const machine_config &mconfig, device_type type, const char *tag) :
		z80ne_state(mconfig, type, tag),
		m_videoram(*this, "videoram"),
		m_vdg(*this, "mc6847"),
		m_lx387_kr2376(*this, "lx387_kr2376"),
		m_io_lx387_brk(*this, "LX387_BRK"),
		m_io_modifiers(*this, "MODIFIERS")
	{
	}

	void lx387(machine_config &config);
	void z80net(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(z80net_nmi);

protected:
	virtual void machine_reset() override ATTR_COLD;

	int lx387_shift_r();
	int lx387_control_r();
	uint8_t lx387_data_r();
	uint8_t lx388_mc6847_videoram_r(offs_t offset);
	uint8_t lx388_read_field_sync();

	required_shared_ptr<uint8_t> m_videoram;
	required_device<mc6847_base_device> m_vdg;
	required_device<kr2376_device> m_lx387_kr2376;
	required_ioport m_io_lx387_brk;
	required_ioport m_io_modifiers;

	void reset_lx387();

	void io_map(address_map &map) ATTR_COLD;

private:
	void mem_map(address_map &map) ATTR_COLD;
};

class z80netb_state : public z80net_state
{
public:
	z80netb_state(const machine_config &mconfig, device_type type, const char *tag) :
		z80net_state(mconfig, type, tag)
	{
	}

	void z80netb(machine_config &config);

protected:
	virtual void machine_reset() override ATTR_COLD;

private:
	void mem_map(address_map &map) ATTR_COLD;
};

class z80netf_state : public z80netb_state
{
public:
	z80netf_state(const machine_config &mconfig, device_type type, const char *tag) :
		z80netb_state(mconfig, type, tag),
		m_io_config(*this, "CONFIG"),
		m_floppy(*this, "wd1771:%u", 0U),
		m_wd1771(*this, "wd1771"),
		m_drv_led(*this, "drv%u", 0U)
	{
	}

	void z80netf(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;
	virtual void driver_start() override;

	struct wd17xx_state_t
	{
		int drq = 0;
		int intrq = 0;
		uint8_t drive = 0; /* current drive */
		uint8_t head = 0;  /* current head */
	};

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	void lx390_motor_w(uint8_t data);
	uint8_t lx390_fdc_r(offs_t offset);
	void lx390_fdc_w(offs_t offset, uint8_t data);

	void reset_lx390_banking();

	required_ioport m_io_config;
	required_device_array<floppy_connector, 4> m_floppy;
	required_device<fd1771_device> m_wd1771;
	wd17xx_state_t m_wd17xx_state;
	output_finder<2> m_drv_led;
};



/******************************************************************************
 I/O
******************************************************************************/

/* timer to read cassette waveforms */

cassette_image_device* z80ne_state::cassette_device_image()
{
	if (m_lx385_ctrl & 0x08)
		return m_cassette2;
	else
		return m_cassette1;
}

TIMER_CALLBACK_MEMBER(z80ne_state::cassette_tc)
{
	uint8_t cass_ws = 0;
	m_cass_data.input.length++;

	cass_ws = ((cassette_device_image())->input() > +0.02) ? 1 : 0;

	if ((cass_ws ^ m_cass_data.input.level) & cass_ws)
	{
		m_cass_data.input.level = cass_ws;
		m_cass_data.input.bit = ((m_cass_data.input.length < m_cass_data.wave_filter) || (m_cass_data.input.length > 0x20)) ? 1 : 0;
		m_cass_data.input.length = 0;
		m_uart->write_si(m_cass_data.input.bit);
	}
	m_cass_data.input.level = cass_ws;

	/* saving a tape - convert the serial stream from the uart */
	m_cass_data.output.length--;

	if (!(m_cass_data.output.length))
	{
		if (m_cass_data.output.level)
			m_cass_data.output.level = 0;
		else
		{
			m_cass_data.output.level=1;
			cass_ws = m_uart->so_r();
			m_cass_data.wave_length = cass_ws ? m_cass_data.wave_short : m_cass_data.wave_long;
		}
		cassette_device_image()->output(m_cass_data.output.level ? -1.0 : +1.0);
		m_cass_data.output.length = m_cass_data.wave_length;
	}
}

void z80ne_state::save_state_vars()
{
	save_item(NAME(m_lx383_scan_counter));
	save_item(NAME(m_lx383_key));
	save_item(NAME(m_lx383_downsampler));
	save_item(NAME(m_lx385_ctrl));
}

void z80ne_state::init_z80ne()
{
	save_state_vars();
}

void z80netf_state::driver_start()
{
	save_state_vars();

	/* first two entries point to rom on reset */
	u8 *r = m_ram->pointer();
	m_bank1->configure_entry(0, r); /* RAM   at 0x0000-0x03FF */
	m_bank1->configure_entries(1, 3, m_rom+0x4400, 0x0400); /* ep390, ep1390, ep2390 at 0x0000-0x03FF */
	m_bank1->configure_entry(4, m_rom+0x4000); /* ep382 at 0x0000-0x03FF */
	m_bank1->configure_entry(5, m_rom); /* ep548 at 0x0000-0x03FF */

	m_bank2->configure_entry(0, r+0x0400); /* RAM   at 0x0400 */
	m_bank2->configure_entry(1, m_rom+0x0400); /* ep548 at 0x0400-0x3FFF */

	m_bank3->configure_entry(0, r+0x4000); /* RAM   at 0x8000 */
	m_bank3->configure_entry(1, m_rom+0x4000); /* ep382 at 0x8000 */

	m_bank4->configure_entry(0, r+0x5000); /* RAM   at 0xF000 */
	m_bank4->configure_entries(1, 3, m_rom+0x4400, 0x0400); /* ep390, ep1390, ep2390 at 0xF000 */

}

TIMER_CALLBACK_MEMBER(z80ne_state::kbd_scan)
{
	/*
	 * NE555 is connected to a 74LS93 binary counter
	 * 74LS93 output:
	 *   QA-QC: column index for LEDs and keyboard
	 *   QD:    keyboard row select
	 *
	 * Port F0 input bit assignment:
	 *   0  QA  bits 0..3 of row counter
	 *   1  QB
	 *   2  QC
	 *   3  QD
	 *   4  Control button pressed, active high
	 *   5  Always low
	 *   6  Always low
	 *   7  Selected button pressed, active low
	 *
	 *
	 */

	uint16_t key_bits;
	uint8_t ctrl; //, rst;
	uint8_t i;

	/* 4-bit counter */
	--m_lx383_scan_counter;
	m_lx383_scan_counter &= 0x0f;

	if ( --m_lx383_downsampler == 0 )
	{
		m_lx383_downsampler = LX383_DOWNSAMPLING;
		key_bits = (m_io_row1->read() << 8) | m_io_row0->read();
//      rst = m_io_rst->read();
		ctrl = m_io_ctrl->read();

		for ( i = 0; i<LX383_KEYS; i++)
		{
			m_lx383_key[i] = ( i | (key_bits & 0x01 ? 0x80 : 0x00) | ~ctrl);
			key_bits >>= 1;
		}
	}
}

TIMER_CALLBACK_MEMBER(z80ne_state::pulse_nmi)
{
	m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}

void z80net_state::reset_lx387()
{
	m_lx387_kr2376->set_input_pin( kr2376_device::KR2376_DSII, 0);
	m_lx387_kr2376->set_input_pin( kr2376_device::KR2376_PII, 0);
}

void z80netf_state::reset_lx390_banking()
{
	switch (m_io_config->read() & 0x07)
	{
	case 0x01: /* EP382 Hex Monitor */
		if (VERBOSE)
			logerror("reset_lx390_banking: banking ep382\n");
		m_bank1->set_entry(4);  /* ep382 at 0x0000 for 3 cycles, then RAM */
		m_bank2->set_entry(0);  /* RAM   at 0x0400 */
		m_bank3->set_entry(1);  /* ep382 at 0x8000 */
		m_bank4->set_entry(0);  /* RAM   at 0xF000 */
		break;
	case 0x02: /* EP548  16k BASIC */
		if (VERBOSE)
			logerror("reset_lx390_banking: banking ep548\n");
		m_bank1->set_entry(5);  /* ep548 at 0x0000-0x03FF */
		m_bank2->set_entry(1);  /* ep548 at 0x0400-0x3FFF */
		m_bank3->set_entry(0);  /* RAM   at 0x8000 */
		m_bank4->set_entry(0);  /* RAM   at 0xF000 */
		break;
	case 0x03: /* EP390  Boot Loader for 5.5k floppy BASIC */
		if (VERBOSE)
			logerror("reset_lx390_banking: banking ep390\n");
		m_bank1->set_entry(1);  /* ep390 at 0x0000-0 x03FF for 3 cycles, then RAM */
		m_bank2->set_entry(0);  /* RAM   at 0x0400-0x3FFF */
		m_bank3->set_entry(0);  /* RAM   at 0x8000 */
		m_bank4->set_entry(1);  /* ep390 at 0xF000 */
		break;
	case 0x04: /* EP1390 Boot Loader for NE DOS 1.0/1.5 */
		if (VERBOSE)
			logerror("reset_lx390_banking: banking ep1390\n");
		m_bank1->set_entry(2);  /* ep1390 at 0x0000-0x03FF for 3 cycles, then RAM */
		m_bank2->set_entry(0);  /* RAM   at 0x0400-0x3FFF */
		m_bank3->set_entry(0);  /* RAM   at 0x8000 */
		m_bank4->set_entry(2);  /* ep1390 at 0xF000 */
		break;
	case 0x05: /* EP2390 Boot Loader for NE DOS G.1 */
		if (VERBOSE)
			logerror("reset_lx390_banking: banking ep2390\n");
		m_bank1->set_entry(3);  /* ep2390 at 0x0000-0x03FF for 3 cycles, then RAM */
		m_bank2->set_entry(0);  /* RAM   at 0x0400-0x3FFF */
		m_bank3->set_entry(0);  /* RAM   at 0x8000 */
		m_bank4->set_entry(3);  /* ep2390 at 0xF000 */
		break;
	}

	/* TODO: in real hardware the ENH bus line is pulled down
	 * until a I/O read is performed on a address with A0 address bit low and A1 or A2 address bit high
	 */
}

void z80ne_state::base_reset()
{
	for (int i = 0; i < LX383_KEYS; i++)
		m_lx383_key[i] = 0xf0 | i;
	m_lx383_scan_counter = 0x0f;
	m_lx383_downsampler = LX383_DOWNSAMPLING;

	/* Initialize cassette interface */
	switch(m_io_lx_385->read() & 0x07)
	{
	case 0x01:
		m_cass_data.speed = TAPE_300BPS;
		m_cass_data.wave_filter = LX385_TAPE_SAMPLE_FREQ / 1600;
		m_cass_data.wave_short = LX385_TAPE_SAMPLE_FREQ / (2400 * 2);
		m_cass_data.wave_long = LX385_TAPE_SAMPLE_FREQ / (1200 * 2);
		break;
	case 0x02:
		m_cass_data.speed = TAPE_600BPS;
		m_cass_data.wave_filter = LX385_TAPE_SAMPLE_FREQ / 3200;
		m_cass_data.wave_short = LX385_TAPE_SAMPLE_FREQ / (4800 * 2);
		m_cass_data.wave_long = LX385_TAPE_SAMPLE_FREQ / (2400 * 2);
		break;
	case 0x04:
		m_cass_data.speed = TAPE_1200BPS;
		m_cass_data.wave_filter = LX385_TAPE_SAMPLE_FREQ / 6400;
		m_cass_data.wave_short = LX385_TAPE_SAMPLE_FREQ / (9600 * 2);
		m_cass_data.wave_long = LX385_TAPE_SAMPLE_FREQ / (4800 * 2);
	}
	m_cass_data.wave_length = m_cass_data.wave_short;
	m_cass_data.output.length = m_cass_data.wave_length;
	m_cass_data.output.level = 1;
	m_cass_data.input.length = 0;
	m_cass_data.input.bit = 1;

	m_uart->write_cs(0);
	m_uart->write_nb1(1);
	m_uart->write_nb2(1);
	m_uart->write_tsb(1);
	m_uart->write_eps(1);
	m_uart->write_np(m_io_lx_385->read() & 0x80 ? 1 : 0);
	m_uart->write_cs(1);
	m_uart_clock->set_unscaled_clock(m_cass_data.speed * 16);

	lx385_ctrl_w(0);
}

void z80ne_state::machine_reset()
{
	base_reset();

	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x0000, 0x03ff, m_rom);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0x8000, 0x83ff,
			"rom_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// reinstall RAM over the ROM shadow
					m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x03ff, m_mram);
				}
			},
			&m_rom_shadow_tap);
}

void z80net_state::machine_reset()
{
	reset_lx387();
	z80ne_state::machine_reset();
}

void z80netb_state::machine_reset()
{
	base_reset();
	reset_lx387();
}

void z80netf_state::machine_reset()
{
	reset_lx390_banking();
	base_reset();
	reset_lx387();

	// basic roms are exempt from memory tap
	if ((m_io_config->read() & 0x07) != 2)
	{
		address_space &program = m_maincpu->space(AS_PROGRAM);
		m_rom_shadow_tap.remove();
		m_rom_shadow_tap = program.install_read_tap(
				0x8000, 0xf3ff,
				"rom_shadow_r",
				[this] (offs_t offset, u8 &data, u8 mem_mask)
				{
					if (!machine().side_effects_disabled())
					{
						// delete this tap
						m_rom_shadow_tap.remove();

						// reinstall RAM over the ROM shadow
						m_bank1->set_entry(0);
					}
				},
				&m_rom_shadow_tap);
	}
}

INPUT_CHANGED_MEMBER(z80ne_state::z80ne_reset)
{
	uint8_t rst = m_io_rst->read();

	if ( ! BIT(rst, 0))
		machine().schedule_soft_reset();
}

INPUT_CHANGED_MEMBER(z80net_state::z80net_nmi)
{
	uint8_t nmi = m_io_lx387_brk->read();

	if ( ! BIT(nmi, 0))
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}

void z80ne_state::machine_start()
{
	m_timer_nmi = timer_alloc(FUNC(z80ne_state::pulse_nmi), this);

	m_lx383_digits.resolve();

	m_lx385_ctrl = 0x1f;
	m_cassette_timer = timer_alloc(FUNC(z80ne_state::cassette_tc), this);
	m_kbd_timer = timer_alloc(FUNC(z80ne_state::kbd_scan), this);
	m_kbd_timer->adjust(attotime::from_hz(1000), 0, attotime::from_hz(1000));
}

void z80netf_state::machine_start()
{
	z80ne_state::machine_start();
	m_drv_led.resolve();
}



/******************************************************************************
 Drivers
******************************************************************************/

/* LX.383 - LX.384 HEX keyboard and display */
uint8_t z80ne_state::lx383_r()
{
	/*
	 * Keyboard scanning
	 *
	 * IC14 NE555 astable oscillator
	 * IC13 74LS93 binary counter
	 * IC5  74LS240 tri-state buffer
	 *
	 * l'oscillatore NE555 alimenta il clock del contatore 74LS93
	 *      D0 - Q(A) --\
	 *      D1 - Q(B)    |-- column
	 *      D2 - Q(C) --/
	 *      D3 - Q(D)        row
	 *      D4 - CTRL
	 *      D5 - 0
	 *      D6 - 0
	 *      D7 - ~KEY Pressed
	 */
	return m_lx383_key[m_lx383_scan_counter];
}

void z80ne_state::lx383_w(offs_t offset, uint8_t data)
{
	/*
	 * First 8 locations (F0-F7) are mapped to a dual-port 8-byte RAM
	 * The 1KHz NE-555 astable oscillator circuit drive
	 * a 4-bit 74LS93 binary counter.
	 * The 3 least significant bits of the counter are connected
	 * both to the read address of the dual-port ram and to
	 * a 74LS156 3 to 8 binary decoder driving the cathode
	 * of 8 7-segments LEDS.
	 * The data output of the dual-port ram drive the anodes
	 * of the LEDS through 74LS07 buffers.
	 * LED segments - dual-port RAM bit:
	 *   A   0x01
	 *   B   0x02
	 *   C   0x04
	 *   D   0x08
	 *   E   0x10
	 *   F   0x20
	 *   G   0x40
	 *   P   0x80 (represented by DP in original schematics)
	 *
	 *   A write in the range F0-FF starts a 74LS90 counter
	 *   that trigger the NMI line of the CPU after 2 instruction
	 *   fetch cycles for single step execution.
	 */

	if ( offset < 8 )
		m_lx383_digits[offset] = data ^ 0xff;
	else
	{
		// after writing to port 0xF8 and the first ~M1 cycles strike a NMI for single step execution
		m_timer_nmi->adjust(m_maincpu->cycles_to_attotime(1));
	}
}


/* LX.385 Cassette tape interface */
/*
 * NE555 is connected to a 74LS93 binary counter
 * 74LS93 output:
 *   QA-QC: column index for LEDs and keyboard
 *   QD:    keyboard row select
 *
 * Port EE: UART Data Read/Write
 * Port EF: Status/Control
 *     read, UART status bits read
 *         0 OR   Overrun
 *         1 FE   Framing Error
 *         2 PE   Parity Error
 *         3 TBMT Transmitter Buffer Empty
 *         4 DAV  Data Available
 *         5 EOC  End Of Character
 *         6 1
 *         7 1
 *     write, UART control bits / Tape Unit select / Modulation control
 *         0 bit1=0, bit0=0   UART Reset pulse
 *         1 bit1=0, bit0=1   UART RDAV (Reset Data Available) pulse
 *         2 Tape modulation enable
 *         3 *TAPEA Enable (active low) (at reset: low)
 *         4 *TAPEB Enable (active low) (at reset: low)
 *  Cassette is connected to the uart data input and output via the cassette
 *  interface hardware.
 *
 *  The cassette interface hardware converts square-wave pulses into bits which the uart receives.
 *
 *  1. the cassette format: "frequency shift" is converted
    into the uart data format "non-return to zero"

    2. on cassette a 1 data bit is stored as 8 2400 Hz pulses
    and a 0 data bit as 4 1200 Hz pulses
    - At 1200 baud, a logic 1 is 1 cycle of 1200 Hz and a logic 0 is 1/2 cycle of 600 Hz.
    - At 300 baud, a logic 1 is 8 cycles of 2400 Hz and a logic 0 is 4 cycles of 1200 Hz.

    Attenuation is applied to the signal and the square wave edges are rounded.

    A manchester encoder is used. A flip-flop synchronises input
    data on the positive-edge of the clock pulse.

    The UART is a RCA CDP1854 CMOS device with pin 2 jumpered to GND to select the
    AY-3-1015 compatibility mode. The jumper at P4 can be switched to place 12 V on
    pin 2 for an old PMOS UART.
 *
 */
uint8_t z80ne_state::lx385_ctrl_r()
{
	/* set unused bits high */
	uint8_t data = 0xc0;

	m_uart->write_swe(0);
	data |= (m_uart->or_r(  ) ? 0x01 : 0);
	data |= (m_uart->fe_r(  ) ? 0x02 : 0);
	data |= (m_uart->pe_r(  ) ? 0x04 : 0);
	data |= (m_uart->tbmt_r() ? 0x08 : 0);
	data |= (m_uart->dav_r( ) ? 0x10 : 0);
	data |= (m_uart->eoc_r( ) ? 0x20 : 0);
	m_uart->write_swe(1);

	return data;
}

#define LX385_CASSETTE_MOTOR_MASK ((1<<3)|(1<<4))

void z80ne_state::lx385_ctrl_w(uint8_t data)
{
	/* Translate data to control signals
	 *     0 bit1=0, bit0=0   UART Reset pulse
	 *     1 bit1=0, bit0=1   UART RDAV (Reset Data Available) pulse
	 *     2 UART Tx Clock Enable (active high)
	 *     3 *TAPEA Enable (active low) (at reset: low)
	 *     4 *TAPEB Enable (active low) (at reset: low)
	 */
	uint8_t uart_reset, uart_rdav;
	uint8_t motor_a, motor_b;
	uint8_t changed_bits = (m_lx385_ctrl ^ data) & 0x1C;
	m_lx385_ctrl = data;

	uart_reset = ((data & 0x03) == 0x00);
	uart_rdav  = ((data & 0x03) == 0x01);
	motor_a = ((data & 0x08) == 0x00);
	motor_b = ((data & 0x10) == 0x00);

	/* UART Reset and RDAV */
	if (uart_reset)
	{
		m_uart->write_xr(1);
		m_uart->write_xr(0);
	}

	if (uart_rdav)
	{
		m_uart->write_rdav(1);
		m_uart->write_rdav(0);
	}

	if (!changed_bits) return;

	/* motors */
	if(changed_bits & 0x18)
	{
		m_cassette1->change_state(
			(motor_a) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR);

		m_cassette2->change_state(
			(motor_b) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR);

		if (motor_a || motor_b)
			m_cassette_timer->adjust(attotime::zero, 0, attotime::from_hz(LX385_TAPE_SAMPLE_FREQ));
		else
			m_cassette_timer->adjust(attotime::zero);
	}
}

void z80ne_state::lx385_uart_tx_clock_w(int state)
{
	if (BIT(m_lx385_ctrl, 2))
		m_uart->write_tcp(state);
}

int z80net_state::lx387_shift_r()
{
	return BIT(m_io_modifiers->read(), 0) || BIT(m_io_modifiers->read(), 2);
}

int z80net_state::lx387_control_r()
{
	return BIT(m_io_modifiers->read(), 1);
}

uint8_t z80net_state::lx388_mc6847_videoram_r(offs_t offset)
{
	if (offset == ~0) return 0xff;

	int d6 = BIT(m_videoram[offset], 6);
	int d7 = BIT(m_videoram[offset], 7);

	m_vdg->inv_w(d6 && d7);
	m_vdg->as_w(!d6 && d7);
	m_vdg->intext_w(!d6 && d7);

	return m_videoram[offset];
}

uint8_t z80net_state::lx387_data_r()
{
	uint8_t data = m_lx387_kr2376->data_r() & 0x7f;
	data |= m_lx387_kr2376->get_output_pin(kr2376_device::KR2376_SO) << 7;
	return data;
}

uint8_t z80net_state::lx388_read_field_sync()
{
	return m_vdg->fs_r() << 7;
}

/*
 * DRQ INTRQ IC9B.10 IC8B.*Q
 *  0    0     1       0
 *  0    1     0       x
 *  1    0     0       x
 *  1    1     0       x
 *
 */

void z80netf_state::lx390_motor_w(uint8_t data)
{
	/* Selection of drive and parameters
	 A write also causes the selected drive motor to turn on for about 3 seconds.
	 When the motor turns off, the drive is deselected.
	    d7 Unused             (trs80: 1=MFM, 0=FM)
	    d6 (trs80: 1=Wait)
	    d5 0=Side 0, 1=Side 1 (trs80: 1=Write Precompensation enabled)
	    d4 Unused             (trs80: 0=Side 0, 1=Side 1)
	    d3 1=select drive 3
	    d2 1=select drive 2
	    d1 1=select drive 1
	    d0 1=select drive 0 */

	floppy_image_device *floppy = nullptr;

	for (u8 f = 0; f < 4; f++)
		if (BIT(data, f))
			floppy = m_floppy[f]->get_device();

	m_wd1771->set_floppy(floppy);

	if (floppy)
	{
		floppy->ss_w(BIT(data, 5));
		floppy->mon_w(0);
	}

	m_wd17xx_state.head = (data & 32) ? 1 : 0;
	m_wd17xx_state.drive = data & 0x0F;

	/* no drive selected, turn off all leds */
	if (!m_wd17xx_state.drive)
	{
		m_drv_led[0] = 0;
		m_drv_led[1] = 0;
	}
}

uint8_t z80netf_state::lx390_fdc_r(offs_t offset)
{
	uint8_t d;

	switch(offset)
	{
	case 0:
		d = m_wd1771->status_r() ^ 0xff;
		LOG("lx390_fdc_r, WD17xx status: %02x\n", d);
		break;
	case 1:
		d = m_wd1771->track_r() ^ 0xff;
		LOG("lx390_fdc_r, WD17xx track:  %02x\n", d);
		break;
	case 2:
		d = m_wd1771->sector_r() ^ 0xff;
		LOG("lx390_fdc_r, WD17xx sector: %02x\n", d);
		break;
	case 3:
		d = m_wd1771->data_r() ^ 0xff;
		LOG("lx390_fdc_r, WD17xx data3:  %02x\n", d);
		break;
	case 6:
		d = 0xff;
		m_bank1->set_entry(0);
		break;
	case 7:
		d = m_wd1771->data_r() ^ 0xff;
		LOG("lx390_fdc_r, WD17xx data7, force:  %02x\n", d);
		break;
	default:
		d = 0x00;
	}
	return d;
}

void z80netf_state::lx390_fdc_w(offs_t offset, uint8_t data)
{
	uint8_t d = data;
	switch(offset)
	{
	case 0:
		LOG("lx390_fdc_w, WD17xx command: %02x\n", d);
		m_wd1771->cmd_w(d ^ 0xff);
		if (m_wd17xx_state.drive & 1)
			m_drv_led[0] = 2;
		else if (m_wd17xx_state.drive & 2)
			m_drv_led[1] = 2;
		break;
	case 1:
		LOG("lx390_fdc_w, WD17xx track:   %02x\n", d);
		m_wd1771->track_w(d ^ 0xff);
		break;
	case 2:
		LOG("lx390_fdc_w, WD17xx sector:  %02x\n", d);
		m_wd1771->sector_w(d ^ 0xff);
		break;
	case 3:
		m_wd1771->data_w(d ^ 0xff);
		LOG("lx390_fdc_w, WD17xx data3:   %02x\n", d);
		break;
	case 6:
		LOG("lx390_fdc_w, motor_w:   %02x\n", d);
		lx390_motor_w(d);
		break;
	case 7:
		LOG("lx390_fdc_w, WD17xx data7, force:   %02x\n", d);
		m_wd1771->data_w(d ^ 0xff);
		break;
	}
}



/******************************************************************************
 Memory Maps
******************************************************************************/

/* LX.382 CPU Board RAM */
/* LX.382 CPU Board EPROM */
void z80ne_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).ram().share("mainram");
	map(0x8000, 0x83ff).rom().region("maincpu",0);
}

void z80net_state::mem_map(address_map &map)
{
	map(0x0000, 0x7fff).ram().share("mainram");
	map(0x8000, 0x83ff).rom().region("maincpu",0);
	map(0x8400, 0xebff).ram();
	map(0xec00, 0xedff).ram().share("videoram"); /* (6847) */
	map(0xee00, 0xffff).ram();
}

void z80netb_state::mem_map(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	map(0x4000, 0xebff).ram();
	map(0xec00, 0xedff).ram().share("videoram"); /* (6847) */
	map(0xee00, 0xffff).ram();
}

void z80ne_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0xee, 0xee).rw(m_uart, FUNC(ay31015_device::receive), FUNC(ay31015_device::transmit));
	map(0xef, 0xef).rw(FUNC(z80ne_state::lx385_ctrl_r), FUNC(z80ne_state::lx385_ctrl_w));
	map(0xf0, 0xff).rw(FUNC(z80ne_state::lx383_r), FUNC(z80ne_state::lx383_w));
}

void z80net_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0xea, 0xea).r(FUNC(z80net_state::lx387_data_r));
	map(0xeb, 0xeb).r(FUNC(z80net_state::lx388_read_field_sync));
	map(0xee, 0xee).rw(m_uart, FUNC(ay31015_device::receive), FUNC(ay31015_device::transmit));
	map(0xef, 0xef).rw(FUNC(z80net_state::lx385_ctrl_r), FUNC(z80net_state::lx385_ctrl_w));
	map(0xf0, 0xff).rw(FUNC(z80net_state::lx383_r), FUNC(z80net_state::lx383_w));
}

void z80netf_state::mem_map(address_map &map)
{
	map(0x0000, 0x03ff).bankrw("bank1");
	map(0x0400, 0x3fff).bankrw("bank2");
	map(0x4000, 0x7fff).ram();
	map(0x8000, 0x83ff).bankrw("bank3");
	map(0x8400, 0xdfff).ram();
	map(0xec00, 0xedff).ram().share("videoram"); /* (6847) */
	map(0xf000, 0xf3ff).bankrw("bank4");
}

void z80netf_state::io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0xd0, 0xd7).rw(FUNC(z80netf_state::lx390_fdc_r), FUNC(z80netf_state::lx390_fdc_w));
	map(0xea, 0xea).r(FUNC(z80netf_state::lx387_data_r));
	map(0xeb, 0xeb).r(FUNC(z80netf_state::lx388_read_field_sync));
	map(0xee, 0xee).rw(m_uart, FUNC(ay31015_device::receive), FUNC(ay31015_device::transmit));
	map(0xef, 0xef).rw(FUNC(z80netf_state::lx385_ctrl_r), FUNC(z80netf_state::lx385_ctrl_w));
	map(0xf0, 0xff).rw(FUNC(z80netf_state::lx383_r), FUNC(z80netf_state::lx383_w));
}



/******************************************************************************
 Input Ports
******************************************************************************/

static INPUT_PORTS_START( z80ne )
	/* LX.384 Hex Keyboard and Display */
	/*
	 * In natural mode the CTRL key is mapped on shift
	 */
	PORT_START("ROW0")          /* IN0 keys row 0 */
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LX.384 0") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('=') // set address, increment
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LX.384 1") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('-') // ?
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LX.384 2") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('R') // registers
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LX.384 3") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('T') // single-step
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LX.384 4") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('X') // go
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LX.384 5") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('L') // load
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LX.384 6") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('S') // save
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LX.384 7") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('K') // reserved for future expansion

	PORT_START("ROW1")          /* IN1 keys row 1 */
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LX.384 8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LX.384 9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LX.384 A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LX.384 B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LX.384 C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LX.384 D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LX.384 E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LX.384 F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("CTRL")          /* CONTROL key */
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LX.384 CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("RST")           /* RESET key */
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("LX.384 Reset")  PORT_CODE(KEYCODE_F3) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(z80ne_state::z80ne_reset), 0) PORT_CHAR('N')

	/* Settings - need to reboot after altering these */
	PORT_START("LX.385")
	PORT_CONFNAME(0x07, 0x04, "LX.385 Cassette: P1,P3 Data Rate")
	PORT_CONFSETTING( 0x01, "A-B: 300 bps")
	PORT_CONFSETTING( 0x02, "A-C: 600 bps")
	PORT_CONFSETTING( 0x04, "A-D: 1200 bps")
	PORT_CONFNAME( 0x08, 0x00, "LX.385: P4 Parity Check")
	PORT_CONFSETTING( 0x00, "Parity Check Enabled")
	PORT_CONFSETTING( 0x08, "Parity Check Disabled")
INPUT_PORTS_END


static INPUT_PORTS_START( z80net )
	PORT_INCLUDE( z80ne )

	/* LX.387 Keyboard BREAK key */
	PORT_START("LX387_BRK")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Break") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END)) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(z80net_state::z80net_nmi), 0)

	/* LX.387 Keyboard (Encoded by KR2376) */
	PORT_START("X0")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("X1")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("X2")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-') PORT_CHAR('=')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD )                               PORT_CHAR('_')

	PORT_START("X3")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0)          PORT_CHAR('0')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(':') PORT_CHAR('*')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)          PORT_CHAR('p') PORT_CHAR('P')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR('`') PORT_CHAR('@')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(8)                   PORT_NAME("Del")
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)                  PORT_NAME("CR")
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_HOME)       PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CHAR(10) PORT_NAME("LF")
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_UNUSED )

	PORT_START("X4")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';') PORT_CHAR('+')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M)          PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N)          PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B)          PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V)          PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C)          PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X)          PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z') PORT_CHAR('Z')

	PORT_START("X5")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)          PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K)          PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J)          PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H)          PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G)          PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F)          PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D)          PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S)          PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)          PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC))

	PORT_START("X6")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O)          PORT_CHAR('o') PORT_CHAR('O')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I)          PORT_CHAR('i') PORT_CHAR('I')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U)          PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T)          PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R)          PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E)          PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W)          PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q') PORT_CHAR('Q')

	PORT_START("X7")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9)          PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8)          PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7)          PORT_CHAR('7') PORT_CHAR('\'')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6)          PORT_CHAR('6') PORT_CHAR('&')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5)          PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4)          PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3)          PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2)          PORT_CHAR('2') PORT_CHAR('"')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1)          PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('^') PORT_CHAR('~')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('\\') PORT_CHAR('|')

	PORT_START("MODIFIERS")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Shift Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_TOGGLE
INPUT_PORTS_END


static INPUT_PORTS_START( z80netf )
	PORT_INCLUDE( z80net )

	/* Settings */
	PORT_START("CONFIG")
	PORT_CONFNAME(0x07, 0x01, "Boot EPROM")
	PORT_CONFSETTING(   0x01, "EP382  Hex Monitor")
	PORT_CONFSETTING(   0x02, "EP548  16k BASIC")
	PORT_CONFSETTING(   0x03, "EP390  Boot Loader for 5.5k floppy BASIC")
	PORT_CONFSETTING(   0x04, "EP1390 Boot Loader for NE DOS 1.0/1.5")
	PORT_CONFSETTING(   0x05, "EP2390 Boot Loader for NE DOS G.1")
	PORT_BIT(0xf8, 0xf8, IPT_UNUSED)
INPUT_PORTS_END



/******************************************************************************
 Machine Drivers
******************************************************************************/

#if 0
static const uint32_t lx388palette[] =
{
	rgb_t(0x00, 0xff, 0x00), /* GREEN */
	rgb_t(0x00, 0xff, 0x00), /* YELLOW in original, here GREEN */
	rgb_t(0x00, 0x00, 0xff), /* BLUE */
	rgb_t(0xff, 0x00, 0x00), /* RED */
	rgb_t(0xff, 0xff, 0xff), /* BUFF */
	rgb_t(0x00, 0xff, 0xff), /* CYAN */
	rgb_t(0xff, 0x00, 0xff), /* MAGENTA */
	rgb_t(0xff, 0x80, 0x00), /* ORANGE */

	rgb_t(0x00, 0x20, 0x00), /* BLACK in original, here DARK green */
	rgb_t(0x00, 0xff, 0x00), /* GREEN */
	rgb_t(0x00, 0x00, 0x00), /* BLACK */
	rgb_t(0xff, 0xff, 0xff), /* BUFF */

	rgb_t(0x00, 0x20, 0x00), /* ALPHANUMERIC DARK GREEN */
	rgb_t(0x00, 0xff, 0x00), /* ALPHANUMERIC BRIGHT GREEN */
	rgb_t(0x40, 0x10, 0x00), /* ALPHANUMERIC DARK ORANGE */
	rgb_t(0xff, 0xc4, 0x18)  /* ALPHANUMERIC BRIGHT ORANGE */
};
#endif

void z80ne_state::floppy_formats(format_registration &fr)
{
	fr.add_fm_containers();
	fr.add(FLOPPY_DMK_FORMAT);
}

static void z80ne_floppies(device_slot_interface &device)
{
	device.option_add("sssd", FLOPPY_525_SSSD);
}

void z80ne_state::z80ne(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, Z80NE_CPU_SPEED_HZ);
	m_maincpu->set_addrmap(AS_PROGRAM, &z80ne_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &z80ne_state::io_map);

	AY31015(config, m_uart);

	CLOCK(config, m_uart_clock, 4800);
	m_uart_clock->signal_handler().set(FUNC(z80ne_state::lx385_uart_tx_clock_w));
	m_uart_clock->signal_handler().append(m_uart, FUNC(ay31015_device::write_rcp));

	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cassette1);
	m_cassette1->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette1->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette1->set_interface("z80ne_cass");

	CASSETTE(config, m_cassette2);
	m_cassette2->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette2->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette2->set_interface("z80ne_cass");

	config.set_default_layout(layout_z80ne);

	// all known tapes require LX.388 expansion
	//SOFTWARE_LIST(config, "cass_list").set_original("z80ne_cass");
}

void z80net_state::lx387(machine_config &config)
{
	KR2376_ST(config, m_lx387_kr2376, 50000);
	m_lx387_kr2376->x<0>().set_ioport("X0");
	m_lx387_kr2376->x<1>().set_ioport("X1");
	m_lx387_kr2376->x<2>().set_ioport("X2");
	m_lx387_kr2376->x<3>().set_ioport("X3");
	m_lx387_kr2376->x<4>().set_ioport("X4");
	m_lx387_kr2376->x<5>().set_ioport("X5");
	m_lx387_kr2376->x<6>().set_ioport("X6");
	m_lx387_kr2376->x<7>().set_ioport("X7");
	m_lx387_kr2376->shift().set(FUNC(z80net_state::lx387_shift_r));
	m_lx387_kr2376->control().set(FUNC(z80net_state::lx387_control_r));
}

void z80net_state::z80net(machine_config &config)
{
	z80ne(config);

	m_maincpu->set_addrmap(AS_PROGRAM, &z80net_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &z80net_state::io_map);

	lx387(config);

	/* video hardware */
	SCREEN(config, "lx388", SCREEN_TYPE_RASTER);

	MC6847(config, m_vdg, 4.433619_MHz_XTAL, true);
	m_vdg->set_screen("lx388");
	m_vdg->input_callback().set(FUNC(z80net_state::lx388_mc6847_videoram_r));
	// AG = GND, GM2 = GND, GM1 = GND, GM0 = GND, CSS = GND
	// other lines not connected

	config.set_default_layout(layout_z80net);

	SOFTWARE_LIST(config, "cass_list").set_original("z80ne_cass");
}

void z80netb_state::z80netb(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, Z80NE_CPU_SPEED_HZ);
	m_maincpu->set_addrmap(AS_PROGRAM, &z80netb_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &z80netb_state::io_map);

	AY31015(config, m_uart);

	CLOCK(config, m_uart_clock, 4800);
	m_uart_clock->signal_handler().set(FUNC(z80netb_state::lx385_uart_tx_clock_w));
	m_uart_clock->signal_handler().append(m_uart, FUNC(ay31015_device::write_rcp));

	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cassette1);
	m_cassette1->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette1->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette1->set_interface("z80ne_cass");

	CASSETTE(config, m_cassette2);
	m_cassette2->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette2->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette2->set_interface("z80ne_cass");

	lx387(config);

	/* video hardware */
	SCREEN(config, "lx388", SCREEN_TYPE_RASTER);

	MC6847(config, m_vdg, 4.433619_MHz_XTAL, true);
	m_vdg->set_screen("lx388");
	m_vdg->input_callback().set(FUNC(z80netb_state::lx388_mc6847_videoram_r));
	// AG = GND, GM2 = GND, GM1 = GND, GM0 = GND, CSS = GND
	// other lines not connected

	config.set_default_layout(layout_z80netb);

	// none of the software is compatible
	//SOFTWARE_LIST(config, "cass_list").set_original("z80ne_cass");
}

void z80netf_state::z80netf(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, Z80NE_CPU_SPEED_HZ);
	m_maincpu->set_addrmap(AS_PROGRAM, &z80netf_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &z80netf_state::io_map);

	AY31015(config, m_uart);

	CLOCK(config, m_uart_clock, 4800);
	m_uart_clock->signal_handler().set(FUNC(z80netf_state::lx385_uart_tx_clock_w));
	m_uart_clock->signal_handler().append(m_uart, FUNC(ay31015_device::write_rcp));

	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cassette1);
	m_cassette1->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette1->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette1->set_interface("z80ne_cass");

	CASSETTE(config, m_cassette2);
	m_cassette2->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
	m_cassette2->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette2->set_interface("z80ne_cass");

	lx387(config);

	/* video hardware */
	SCREEN(config, "lx388", SCREEN_TYPE_RASTER);

	MC6847(config, m_vdg, 4.433619_MHz_XTAL, true);
	m_vdg->set_screen("lx388");
	m_vdg->input_callback().set(FUNC(z80netf_state::lx388_mc6847_videoram_r));
	// AG = GND, GM2 = GND, GM1 = GND, GM0 = GND, CSS = GND
	// other lines not connected

	FD1771(config, m_wd1771, 2_MHz_XTAL / 2);
	FLOPPY_CONNECTOR(config, "wd1771:0", z80ne_floppies, "sssd", z80ne_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "wd1771:1", z80ne_floppies, "sssd", z80ne_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "wd1771:2", z80ne_floppies, nullptr,   z80ne_state::floppy_formats);
	FLOPPY_CONNECTOR(config, "wd1771:3", z80ne_floppies, nullptr,   z80ne_state::floppy_formats);

	config.set_default_layout(layout_z80netf);

	/* internal ram */
	RAM(config, m_ram).set_default_size("56K").set_default_value(0x00);

	SOFTWARE_LIST(config, "cass_list").set_original("z80ne_cass");
	SOFTWARE_LIST(config, "flop_list").set_original("z80ne_flop");
}



/******************************************************************************
 ROM Definitions
******************************************************************************/

ROM_START( z80ne )
	ROM_REGION(0x0400, "maincpu", 0)
	ROM_LOAD( "ep382.ic5", 0x0000, 0x0400, CRC(55818366) SHA1(adcac04b83c09265517b7bafbc2f5f665d751bec) )
ROM_END

ROM_START( z80net )
	ROM_REGION(0x0400, "maincpu", 0)
	ROM_LOAD( "ep382.ic5", 0x0000, 0x0400, CRC(55818366) SHA1(adcac04b83c09265517b7bafbc2f5f665d751bec) )
ROM_END

ROM_START( z80netb )
	ROM_REGION(0x4000, "maincpu", 0)
	// 16k Basic
	ROM_LOAD( "548-1.ic1", 0x0000, 0x0800, CRC(868cad39) SHA1(0ea8af010786a080f823a879a4211f5712d260da) )
	ROM_LOAD( "548-2.ic2", 0x0800, 0x0800, CRC(ac297d99) SHA1(ccf31d3f9d02c3b68a0ee3be4984424df0e83ab0) )
	ROM_LOAD( "548-3.ic3", 0x1000, 0x0800, CRC(9c1fe511) SHA1(ff5b6e49a137c2ff9cb760c39bfd85ce4b52bb7d) )
	ROM_LOAD( "548-4.ic4", 0x1800, 0x0800, CRC(cb5e0de3) SHA1(0beaa8927faaf61f6c3fc0ea1d3d5670f901aae3) )
	ROM_LOAD( "548-5.ic5", 0x2000, 0x0800, CRC(0bd4559c) SHA1(e736a3124819ffb43e96a8114cd188f18d538053) )
	ROM_LOAD( "548-6.ic6", 0x2800, 0x0800, CRC(6d663034) SHA1(57588be4e360658dbb313946d7a608e36c1fdd68) )
	ROM_LOAD( "548-7.ic7", 0x3000, 0x0800, CRC(0bab06c0) SHA1(d52f1519c798e91f25648e996b1db174d90ce0f5) )
	ROM_LOAD( "548-8.ic8", 0x3800, 0x0800, CRC(f381b594) SHA1(2de7a8941ba48d463974c73d62e994d3cbe2868d) )
ROM_END

ROM_START( z80netf )
	ROM_REGION(0x5000, "maincpu", 0)
	// ep548 banked at 0x0000 - 0x3FFF
	ROM_LOAD(  "548-1.ic1", 0x0000, 0x0800, CRC(868cad39) SHA1(0ea8af010786a080f823a879a4211f5712d260da) )
	ROM_LOAD(  "548-2.ic2", 0x0800, 0x0800, CRC(ac297d99) SHA1(ccf31d3f9d02c3b68a0ee3be4984424df0e83ab0) )
	ROM_LOAD(  "548-3.ic3", 0x1000, 0x0800, CRC(9c1fe511) SHA1(ff5b6e49a137c2ff9cb760c39bfd85ce4b52bb7d) )
	ROM_LOAD(  "548-4.ic4", 0x1800, 0x0800, CRC(cb5e0de3) SHA1(0beaa8927faaf61f6c3fc0ea1d3d5670f901aae3) )
	ROM_LOAD(  "548-5.ic5", 0x2000, 0x0800, CRC(0bd4559c) SHA1(e736a3124819ffb43e96a8114cd188f18d538053) )
	ROM_LOAD(  "548-6.ic6", 0x2800, 0x0800, CRC(6d663034) SHA1(57588be4e360658dbb313946d7a608e36c1fdd68) )
	ROM_LOAD(  "548-7.ic7", 0x3000, 0x0800, CRC(0bab06c0) SHA1(d52f1519c798e91f25648e996b1db174d90ce0f5) )
	ROM_LOAD(  "548-8.ic8", 0x3800, 0x0800, CRC(f381b594) SHA1(2de7a8941ba48d463974c73d62e994d3cbe2868d) )

	// ep382 - 8000
	ROM_LOAD(  "ep382.ic5", 0x4000, 0x0400, CRC(55818366) SHA1(adcac04b83c09265517b7bafbc2f5f665d751bec) )
	// ep390 - F000
	ROM_LOAD(  "ep390.ic6", 0x4400, 0x0400, CRC(e4dd7de9) SHA1(523caa97112a9e67cc078c1a70ceee94ec232093) )
	// ep1390 - F000
	ROM_LOAD( "ep1390.ic6", 0x4800, 0x0400, CRC(dc2cbc1d) SHA1(e23418b8f8261a17892f3a73ec09c72bb02e1d0b) )
	// ep2390 - F000
	ROM_LOAD( "ep2390.ic6", 0x4c00, 0x0400, CRC(28d28eee) SHA1(b80f75c1ac4905ae369ecbc9b9ce120cc85502ed) )
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY               FULLNAME                      FLAGS
COMP( 1980, z80ne,   0,      0,      z80ne,   z80ne,   z80ne_state,   init_z80ne, "Nuova Elettronica",  "Z80NE",                      MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1980, z80net,  z80ne,  0,      z80net,  z80net,  z80net_state,  init_z80ne, "Nuova Elettronica",  "Z80NE + LX.388",             MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1980, z80netb, z80ne,  0,      z80netb, z80net,  z80netb_state, init_z80ne, "Nuova Elettronica",  "Z80NE + LX.388 + Basic 16k", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
COMP( 1980, z80netf, z80ne,  0,      z80netf, z80netf, z80netf_state, empty_init, "Nuova Elettronica",  "Z80NE + LX.388 + LX.390",    MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



z88.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:Kevin Thacker,Sandro Ronco
/******************************************************************************

        z88.cpp

        z88 Notepad computer

        system driver

        TODO:
        - cartridges should be hot swappable
        - expansion interface

        Kevin Thacker [MESS driver]

*******************************************************************************/

#include "emu.h"
#include "z88.h"

#include "softlist_dev.h"

/* Assumption:

all banks can access the same memory blocks in the same way.
bank 0 is special. If a bit is set in the com register,
the lower 8k is replaced with the rom. Bank 0 has been split
into 2 8k chunks, and all other banks into 16k chunks.
I wanted to handle all banks in the code below, and this
explains why the extra checks are done


    bank 0      0x0000-0x3FFF
    bank 1      0x4000-0x7FFF
    bank 2      0x8000-0xBFFF
    bank 3      0xC000-0xFFFF

    pages 0x00 - 0x1f   internal ROM
    pages 0x20 - 0x3f   512KB internal RAM
    pages 0x40 - 0x7f   Slot 1
    pages 0x80 - 0xbf   Slot 2
    pages 0xc0 - 0xff   Slot 3

*/

UPD65031_MEMORY_UPDATE(z88_state::bankswitch_update)
{
	if (bank == 0)
	{
		// bank 0 is only 8k (0x2000 - 0x3fff) and bit 0 is used to select the upper/lower
		// part of a 16k page, for this reason only even pages can be mapped in this bank.
		m_banks[bank]->set_bank(((page & 0xfe) << 1) | (page & 1));
		m_boot_view.select(rams ? 1 : 0);
	}
	else
		m_banks[bank]->set_bank(page);
}

void z88_state::z88_map(address_map &map)
{
	map(0x000000, 0x07ffff).rom().region("bios", 0);
	map(0x080000, 0x0fffff).rw(m_ram, FUNC(ram_device::read), FUNC(ram_device::write));
	map(0x100000, 0x1fffff).rw(m_carts[1], FUNC(z88cart_slot_device::read), FUNC(z88cart_slot_device::write));
	map(0x200000, 0x2fffff).rw(m_carts[2], FUNC(z88cart_slot_device::read), FUNC(z88cart_slot_device::write));
	map(0x300000, 0x3fffff).rw(m_carts[3], FUNC(z88cart_slot_device::read), FUNC(z88cart_slot_device::write));
}

void z88_state::z88_mem(address_map &map)
{
	map(0x0000, 0x1fff).view(m_boot_view);
	m_boot_view[0](0x0000, 0x1fff).rom().region("bios", 0);
	m_boot_view[1](0x0000, 0x1fff).rw(m_ram, FUNC(ram_device::read), FUNC(ram_device::write));
	map(0x2000, 0x3fff).rw(m_banks[0], FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0x4000, 0x7fff).rw(m_banks[1], FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0x8000, 0xbfff).rw(m_banks[2], FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	map(0xc000, 0xffff).rw(m_banks[3], FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
}

void z88_state::z88_io(address_map &map)
{
	map(0x0000, 0xffff).rw(m_blink, FUNC(upd65031_device::read), FUNC(upd65031_device::write));
}



/*
-------------------------------------------------------------------------
         | D7     D6      D5      D4      D3      D2      D1      D0
-------------------------------------------------------------------------
A15 (#7) | RSH    SQR     ESC     INDEX   CAPS    .       /       ??
A14 (#6) | HELP   LSH     TAB     DIA     MENU    ,       ;       '
A13 (#5) | [      SPACE   1       Q       A       Z       L       0
A12 (#4) | ]      LFT     2       W       S       X       M       P
A11 (#3) | -      RGT     3       E       D       C       K       9
A10 (#2) | =      DWN     4       R       F       V       J       O
A9  (#1) | \      UP      5       T       G       B       U       I
A8  (#0) | DEL    ENTER   6       Y       H       N       7       8
-------------------------------------------------------------------------

2008-05 FP:
Small note about natural keyboard: currently,
- "Square" is mapped to 'F1'
- "Diamond" is mapped to 'Left Control'
- "Index" is mapped to 'F2'
- "Menu" is mapped to 'F3'
- "Help" is mapped to 'F4'
*/

static INPUT_PORTS_START( z88 )
	PORT_START("BATTERY")
	PORT_CONFNAME( 0x01, 0x00, "Battery Status" )   PORT_WRITE_LINE_DEVICE_MEMBER("blink", FUNC(upd65031_device::btl_w))
	PORT_CONFSETTING( 0x00, DEF_STR( Normal ) )
	PORT_CONFSETTING( 0x01, "Low" )

	PORT_START("LINE0")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)        PORT_CHAR(13)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)            PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)            PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)            PORT_CHAR('h') PORT_CHAR('H')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)            PORT_CHAR('n') PORT_CHAR('N')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)            PORT_CHAR('7') PORT_CHAR('&')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)            PORT_CHAR('8') PORT_CHAR('*')

	PORT_START("LINE1")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2)   PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)           PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)            PORT_CHAR('5') PORT_CHAR('%')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)            PORT_CHAR('t') PORT_CHAR('T')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)            PORT_CHAR('g') PORT_CHAR('G')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)            PORT_CHAR('b') PORT_CHAR('B')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)            PORT_CHAR('u') PORT_CHAR('U')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)            PORT_CHAR('i') PORT_CHAR('I')

	PORT_START("LINE2")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)       PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)     PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)            PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)            PORT_CHAR('r') PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)            PORT_CHAR('f') PORT_CHAR('F')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)            PORT_CHAR('v') PORT_CHAR('V')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)            PORT_CHAR('j') PORT_CHAR('J')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)            PORT_CHAR('o') PORT_CHAR('O')

	PORT_START("LINE3")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)        PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)        PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)            PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)            PORT_CHAR('e') PORT_CHAR('E')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)            PORT_CHAR('d') PORT_CHAR('D')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)            PORT_CHAR('c') PORT_CHAR('C')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)            PORT_CHAR('k') PORT_CHAR('K')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)            PORT_CHAR('9') PORT_CHAR('(')

	PORT_START("LINE4")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_CHAR(']') PORT_CHAR('}')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)     PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)            PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)            PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)            PORT_CHAR('s') PORT_CHAR('S')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)            PORT_CHAR('x') PORT_CHAR('X')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)            PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)            PORT_CHAR('p') PORT_CHAR('P')

	PORT_START("LINE5")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)    PORT_CHAR('[') PORT_CHAR('{')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)        PORT_CHAR(' ')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)            PORT_CHAR('1') PORT_CHAR('!')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)            PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)            PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)            PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)            PORT_CHAR('l') PORT_CHAR('L')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)            PORT_CHAR('0') PORT_CHAR(')')

	PORT_START("LINE6")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Help") PORT_CODE(KEYCODE_F4)             PORT_CHAR(UCHAR_MAMEKEY(F4))
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift (Left)") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)                                  PORT_CHAR('\t')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Diamond") PORT_CODE(KEYCODE_LCONTROL)        PORT_CHAR(UCHAR_SHIFT_2)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Menu") PORT_CODE(KEYCODE_F3)             PORT_CHAR(UCHAR_MAMEKEY(F3))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)                                PORT_CHAR(',') PORT_CHAR('<')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)                                PORT_CHAR(';') PORT_CHAR(':')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)                                PORT_CHAR('\'') PORT_CHAR('\"')

	PORT_START("LINE7")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift (Right)") PORT_CODE(KEYCODE_RSHIFT)    PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Square") PORT_CODE(KEYCODE_F1)               PORT_CHAR(UCHAR_MAMEKEY(F1))
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)                              PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Index") PORT_CODE(KEYCODE_F2)                PORT_CHAR(UCHAR_MAMEKEY(F2))
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Caps Lock") PORT_CODE(KEYCODE_CAPSLOCK)      PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)                             PORT_CHAR('.') PORT_CHAR('>')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)                                PORT_CHAR('/') PORT_CHAR('?')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)                            PORT_CHAR(0xA3) PORT_CHAR('~')
INPUT_PORTS_END

static INPUT_PORTS_START( z88de )
	PORT_INCLUDE(z88)

	PORT_MODIFY("LINE0")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)            PORT_CHAR('6') PORT_CHAR('&') PORT_CHAR(0x00B0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)            PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)            PORT_CHAR('7') PORT_CHAR('/') PORT_CHAR('{')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)            PORT_CHAR('8') PORT_CHAR('(') PORT_CHAR('}')

	PORT_MODIFY("LINE1")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2)   PORT_CHAR('<') PORT_CHAR('>')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)            PORT_CHAR('5') PORT_CHAR('%') PORT_CHAR('~')

	PORT_MODIFY("LINE2")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)       PORT_CHAR('\'') PORT_CHAR('`')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)            PORT_CHAR('4') PORT_CHAR('$') PORT_CHAR('|')

	PORT_MODIFY("LINE3")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)        PORT_CHAR(0x00DF) PORT_CHAR('?')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)            PORT_CHAR('3') PORT_CHAR(0x00A7) PORT_CHAR(0x00A3)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)            PORT_CHAR('9') PORT_CHAR(')') PORT_CHAR('[')

	PORT_MODIFY("LINE4")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_CHAR('+') PORT_CHAR('*')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)            PORT_CHAR('2') PORT_CHAR('\"') PORT_CHAR('@')

	PORT_MODIFY("LINE5")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)    PORT_CHAR(0x00FC) PORT_CHAR(0x00DC)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)            PORT_CHAR('1') PORT_CHAR('!') PORT_CHAR('\\')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)            PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)            PORT_CHAR('0') PORT_CHAR('=') PORT_CHAR(']')

	PORT_MODIFY("LINE6")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)        PORT_CHAR(',') PORT_CHAR(';')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)        PORT_CHAR(0x00E4) PORT_CHAR(0x00C4)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)        PORT_CHAR(0x00F6) PORT_CHAR(0x00D6)

	PORT_MODIFY("LINE7")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)         PORT_CHAR('.') PORT_CHAR(':')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)        PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)    PORT_CHAR('#') PORT_CHAR('^')
INPUT_PORTS_END

static INPUT_PORTS_START( z88fr )
	PORT_INCLUDE(z88)

	PORT_MODIFY("LINE0")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)            PORT_CHAR(0x00A7) PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)            PORT_CHAR(0x00E8) PORT_CHAR('7') PORT_CHAR('{')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)            PORT_CHAR('!') PORT_CHAR('8') PORT_CHAR('}')

	PORT_MODIFY("LINE1")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)    PORT_CHAR('<') PORT_CHAR('>')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)            PORT_CHAR('(') PORT_CHAR('5') PORT_CHAR('~')

	PORT_MODIFY("LINE2")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)        PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)            PORT_CHAR('\'') PORT_CHAR('4') PORT_CHAR('|')

	PORT_MODIFY("LINE3")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)       PORT_CHAR(')') PORT_CHAR(0x00B0)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)            PORT_CHAR('\"') PORT_CHAR('3') PORT_CHAR('#')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)            PORT_CHAR(0x00E7) PORT_CHAR('9') PORT_CHAR('[')

	PORT_MODIFY("LINE4")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_CHAR('=') PORT_CHAR('+')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)            PORT_CHAR(0x00E9) PORT_CHAR('2') PORT_CHAR('@')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)            PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)        PORT_CHAR(',') PORT_CHAR('?')

	PORT_MODIFY("LINE5")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)    PORT_CHAR('*') PORT_CHAR(0x00A8)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)            PORT_CHAR('&') PORT_CHAR('1') PORT_CHAR('\\')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)            PORT_CHAR('a') PORT_CHAR('A')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)            PORT_CHAR('q') PORT_CHAR('Q')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)            PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)            PORT_CHAR(0x00E0) PORT_CHAR('0') PORT_CHAR(']')

	PORT_MODIFY("LINE6")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)        PORT_CHAR(';') PORT_CHAR('.')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)            PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)        PORT_CHAR(0x00F9) PORT_CHAR('%')

	PORT_MODIFY("LINE7")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)         PORT_CHAR(':') PORT_CHAR('/')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)        PORT_CHAR('$') PORT_CHAR(0x00A3)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2)
INPUT_PORTS_END

static INPUT_PORTS_START( z88es )
	PORT_INCLUDE(z88)

	PORT_MODIFY("LINE0")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)            PORT_CHAR('6') PORT_CHAR('/') PORT_CHAR('^')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)            PORT_CHAR('7') PORT_CHAR('&') PORT_CHAR('{')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)            PORT_CHAR('8') PORT_CHAR('*') PORT_CHAR('}')

	PORT_MODIFY("LINE1")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)            PORT_CHAR('5') PORT_CHAR('%') PORT_CHAR('~')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2)   PORT_CHAR(0x00E7) PORT_CHAR(0x00C7)

	PORT_MODIFY("LINE2")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)            PORT_CHAR('4') PORT_CHAR('$') PORT_CHAR('|')

	PORT_MODIFY("LINE3")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)            PORT_CHAR('3') PORT_CHAR('#') PORT_CHAR(0x00A3)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)            PORT_CHAR('9') PORT_CHAR('(') PORT_CHAR('[')

	PORT_MODIFY("LINE4")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)            PORT_CHAR('2') PORT_CHAR(0x00BF) PORT_CHAR('@')

	PORT_MODIFY("LINE5")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)            PORT_CHAR('1') PORT_CHAR(0x00A1) PORT_CHAR('\\')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)            PORT_CHAR('0') PORT_CHAR(')') PORT_CHAR(']')

	PORT_MODIFY("LINE6")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)        PORT_CHAR(',') PORT_CHAR('?')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)        PORT_CHAR(0x00F1) PORT_CHAR(0x00D1)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)        PORT_CHAR(';') PORT_CHAR(':')

	PORT_MODIFY("LINE7")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)         PORT_CHAR('.') PORT_CHAR('!')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)        PORT_CHAR('\'') PORT_CHAR('\"')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)    PORT_CHAR('<') PORT_CHAR('>')
INPUT_PORTS_END

static INPUT_PORTS_START( z88it )
	PORT_INCLUDE(z88)

	PORT_MODIFY("LINE0")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)            PORT_CHAR('6') PORT_CHAR('^')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)            PORT_CHAR('7') PORT_CHAR('&')

	PORT_MODIFY("LINE1")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2)   PORT_CHAR(0x00F9) PORT_CHAR('|') PORT_CHAR('\\')

	PORT_MODIFY("LINE3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)            PORT_CHAR('9') PORT_CHAR('(') PORT_CHAR('{')

	PORT_MODIFY("LINE4")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_CHAR(0x00EC) PORT_CHAR('~') PORT_CHAR(']')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)            PORT_CHAR('2') PORT_CHAR(0x00A3)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)            PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)        PORT_CHAR(';') PORT_CHAR(':')

	PORT_MODIFY("LINE5")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)    PORT_CHAR(0x00E8) PORT_CHAR(0x00E9) PORT_CHAR('[')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)            PORT_CHAR('w') PORT_CHAR('W')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)            PORT_CHAR('0') PORT_CHAR(')') PORT_CHAR('}')

	PORT_MODIFY("LINE6")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)            PORT_CHAR('m') PORT_CHAR('M')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)        PORT_CHAR(0x00F2) PORT_CHAR('\'') PORT_CHAR('@')

	PORT_MODIFY("LINE7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)    PORT_CHAR(0x00E0) PORT_CHAR('\"') PORT_CHAR('`')
INPUT_PORTS_END

static INPUT_PORTS_START( z88se )
	PORT_INCLUDE(z88)

	PORT_MODIFY("LINE1")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)        PORT_CHAR('/') PORT_CHAR('?')

	PORT_MODIFY("LINE2")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2)   PORT_CHAR('+') PORT_CHAR('>')

	PORT_MODIFY("LINE3")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)       PORT_CHAR('=') PORT_CHAR('<')

	PORT_MODIFY("LINE4")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)        PORT_CHAR('\'') PORT_CHAR('\"') PORT_CHAR('`')

	PORT_MODIFY("LINE5")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)        PORT_CHAR(0x00E5) PORT_CHAR(0x00C5) PORT_CHAR('\\')

	PORT_MODIFY("LINE6")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)        PORT_CHAR(',') PORT_CHAR(';')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)    PORT_CHAR(0x00F6) PORT_CHAR(0x00D6) PORT_CHAR('{')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_CHAR(0x00E4) PORT_CHAR(0x00C4) PORT_CHAR('}')

	PORT_MODIFY("LINE7")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)         PORT_CHAR('.') PORT_CHAR(':')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)        PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)    PORT_CHAR(0x00A3) PORT_CHAR('~')
INPUT_PORTS_END

static INPUT_PORTS_START( z88no )
	PORT_INCLUDE(z88se)

	PORT_MODIFY("LINE5")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)        PORT_CHAR(0x00E5) PORT_CHAR(0x00C5) PORT_CHAR('}')

	PORT_MODIFY("LINE6")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)    PORT_CHAR(0x00E6) PORT_CHAR(0x00C6) PORT_CHAR('{')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_CHAR(0x00F8) PORT_CHAR(0x00D8) PORT_CHAR('|')
INPUT_PORTS_END

static INPUT_PORTS_START( z88ch )
	PORT_INCLUDE(z88)

	PORT_MODIFY("LINE0")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)            PORT_CHAR('6') PORT_CHAR('&') PORT_CHAR('^')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)            PORT_CHAR('z') PORT_CHAR('Z')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)            PORT_CHAR('7') PORT_CHAR('/') PORT_CHAR('{')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)            PORT_CHAR('8') PORT_CHAR('(') PORT_CHAR('[')

	PORT_MODIFY("LINE1")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2)   PORT_CHAR('<') PORT_CHAR('>')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)            PORT_CHAR('5') PORT_CHAR('%') PORT_CHAR('~')

	PORT_MODIFY("LINE2")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)       PORT_CHAR(0x00FA) PORT_CHAR(0x00F9) PORT_CHAR('`')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)            PORT_CHAR('4') PORT_CHAR(0x00E7) PORT_CHAR(0x00C7)

	PORT_MODIFY("LINE3")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)        PORT_CHAR('\'') PORT_CHAR('?') PORT_CHAR('|')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)            PORT_CHAR('3') PORT_CHAR('*') PORT_CHAR('#')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)            PORT_CHAR('9') PORT_CHAR(')') PORT_CHAR(']')

	PORT_MODIFY("LINE4")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_CHAR(0x00A8) PORT_CHAR('!')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)            PORT_CHAR('2') PORT_CHAR('\"') PORT_CHAR('@')

	PORT_MODIFY("LINE5")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)    PORT_CHAR(0x00E8) PORT_CHAR(0x00FC)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)            PORT_CHAR('1') PORT_CHAR('+') PORT_CHAR('\\')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)            PORT_CHAR('y') PORT_CHAR('Y')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)            PORT_CHAR('0') PORT_CHAR('=') PORT_CHAR('}')

	PORT_MODIFY("LINE6")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)        PORT_CHAR(',') PORT_CHAR(';')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)        PORT_CHAR(0x00E9) PORT_CHAR(0x00F6)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)        PORT_CHAR(0x00E0) PORT_CHAR(0x00E4)

	PORT_MODIFY("LINE7")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)         PORT_CHAR('.') PORT_CHAR(':')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)        PORT_CHAR('-') PORT_CHAR('_')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)    PORT_CHAR('$') PORT_CHAR(0x00A3)
INPUT_PORTS_END

static INPUT_PORTS_START( z88tr )
	PORT_INCLUDE(z88)

	PORT_MODIFY("LINE0")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)            PORT_CHAR('6') PORT_CHAR('?') PORT_CHAR('>')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)            PORT_CHAR('7') PORT_CHAR('/') PORT_CHAR('{')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)            PORT_CHAR('8') PORT_CHAR('*') PORT_CHAR('}')

	PORT_MODIFY("LINE1")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)        PORT_CHAR('\'') PORT_CHAR('\"') PORT_CHAR('`')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)            PORT_CHAR('5') PORT_CHAR('%') PORT_CHAR('<')

	PORT_MODIFY("LINE2")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)       PORT_CHAR('=') PORT_CHAR('+') PORT_CHAR('^')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)            PORT_CHAR('4') PORT_CHAR('$') PORT_CHAR('~')

	PORT_MODIFY("LINE3")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)        PORT_CHAR('-') PORT_CHAR('_') PORT_CHAR('\\')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)            PORT_CHAR('3') PORT_CHAR('#') PORT_CHAR(0x00A3)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)            PORT_CHAR('9') PORT_CHAR('(') PORT_CHAR('[')

	PORT_MODIFY("LINE4")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_CHAR(0x00FC) PORT_CHAR(0x00DC)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)            PORT_CHAR('2') PORT_CHAR('&') PORT_CHAR('@')

	PORT_MODIFY("LINE5")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)    PORT_CHAR(0x011F) PORT_CHAR(0x011E)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)            PORT_CHAR('1') PORT_CHAR('!') PORT_CHAR('|')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)            PORT_CHAR('0') PORT_CHAR(')') PORT_CHAR(']')

	PORT_MODIFY("LINE6")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)    PORT_CHAR(0x00F6) PORT_CHAR(0x00D6)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)        PORT_CHAR(0x015F) PORT_CHAR(0x015E)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2)   PORT_CHAR(0x0131) PORT_CHAR(0x0130)

	PORT_MODIFY("LINE7")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)        PORT_CHAR(0x00E7) PORT_CHAR(0x00C7)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)         PORT_CHAR('.') PORT_CHAR(':')
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)        PORT_CHAR(',') PORT_CHAR(';')
INPUT_PORTS_END


void z88_state::machine_start()
{
	m_nvram->set_base(m_ram->pointer(), m_ram->size());
}

void z88_state::machine_reset()
{
	m_boot_view.select(0);
	m_banks[0]->set_bank(0);
	m_banks[1]->set_bank(0);
	m_banks[2]->set_bank(0);
	m_banks[3]->set_bank(0);
}

uint8_t z88_state::kb_r(offs_t offset)
{
	uint8_t data = 0xff;

	for (int i = 7; i >= 0; i--)
	{
		if (!BIT(offset, i))
			data &= m_lines[i]->read();
	}

	return data;
}

static void z88_cart(device_slot_interface &device)
{
	device.option_add("32krom",     Z88_32K_ROM);       // 32KB ROM cart
	device.option_add("128krom",    Z88_128K_ROM);      // 128KB ROM cart
	device.option_add("256krom",    Z88_256K_ROM);      // 256KB ROM cart
	device.option_add("32kram",     Z88_32K_RAM);       // 32KB RAM cart
	device.option_add("128kram",    Z88_128K_RAM);      // 128KB RAM cart
	device.option_add("512kram",    Z88_512K_RAM);      // 512KB RAM cart
	device.option_add("1024kram",   Z88_1024K_RAM);     // 1024KB RAM cart
	device.option_add("1024kflash", Z88_1024K_FLASH);   // 1024KB Flash cart
}

static DEVICE_INPUT_DEFAULTS_START( z88_rs232_defaults )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
	DEVICE_INPUT_DEFAULTS( "FLOW_CONTROL", 0x07, 0x01 ) // Flow Control: RTS
DEVICE_INPUT_DEFAULTS_END


static void z88_rs232_devices(device_slot_interface &device)
{
	default_rs232_devices(device);
	device.option_add("z88_impexp", Z88_IMPEXP);
}


void z88_state::z88(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(9'830'400)/3);  // divided by 3 through the uPD65031
	m_maincpu->set_addrmap(AS_PROGRAM, &z88_state::z88_mem);
	m_maincpu->set_addrmap(AS_IO, &z88_state::z88_io);

	ADDRESS_MAP_BANK(config, m_banks[0]).set_map(&z88_state::z88_map).set_options(ENDIANNESS_LITTLE, 8, 22, 0x2000);
	ADDRESS_MAP_BANK(config, m_banks[1]).set_map(&z88_state::z88_map).set_options(ENDIANNESS_LITTLE, 8, 22, 0x4000);
	ADDRESS_MAP_BANK(config, m_banks[2]).set_map(&z88_state::z88_map).set_options(ENDIANNESS_LITTLE, 8, 22, 0x4000);
	ADDRESS_MAP_BANK(config, m_banks[3]).set_map(&z88_state::z88_map).set_options(ENDIANNESS_LITTLE, 8, 22, 0x4000);

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_LCD);
	m_screen->set_refresh_hz(50);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(0));
	m_screen->set_size(Z88_SCREEN_WIDTH, Z88_SCREEN_HEIGHT);
	m_screen->set_visarea_full();
	m_screen->set_palette(m_palette);
	m_screen->set_screen_update("blink", FUNC(upd65031_device::screen_update));

	PALETTE(config, m_palette, FUNC(z88_state::z88_palette), Z88_NUM_COLOURS);

	UPD65031(config, m_blink, XTAL(9'830'400));
	m_blink->kb_rd_callback().set(FUNC(z88_state::kb_r));
	m_blink->int_wr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	m_blink->nmi_wr_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
	m_blink->spkr_wr_callback().set("speaker", FUNC(speaker_sound_device::level_w));
	m_blink->set_screen_update_callback(FUNC(z88_state::lcd_update));
	m_blink->set_memory_update_callback(FUNC(z88_state::bankswitch_update));
	m_blink->txd_wr_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	m_blink->rts_wr_callback().set("rs232", FUNC(rs232_port_device::write_rts));
	m_blink->dtr_wr_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	m_blink->vpp_wr_callback().set(m_carts[3], FUNC(z88cart_slot_device::vpp_w));   // Only on Slot 3

	rs232_port_device &rs232(RS232_PORT(config, "rs232", z88_rs232_devices, "z88_impexp"));
	rs232.rxd_handler().set(m_blink, FUNC(upd65031_device::rxd_w));
	rs232.cts_handler().set(m_blink, FUNC(upd65031_device::cts_w));
	rs232.dcd_handler().set(m_blink, FUNC(upd65031_device::dcd_w));
	rs232.set_option_device_input_defaults("null_modem", DEVICE_INPUT_DEFAULTS_NAME(z88_rs232_defaults));

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50);

	/* internal ram */
	RAM(config, m_ram).set_default_size("128K").set_extra_options("32K,64K,256K,512K");
	NVRAM(config, m_nvram, nvram_device::DEFAULT_NONE);

	/* cartridges */
	Z88CART_SLOT(config, m_carts[1], z88_cart, nullptr);
	m_carts[1]->out_flp_callback().set(m_blink, FUNC(upd65031_device::flp_w));

	Z88CART_SLOT(config, m_carts[2], z88_cart, nullptr);
	m_carts[2]->out_flp_callback().set(m_blink, FUNC(upd65031_device::flp_w));

	Z88CART_SLOT(config, m_carts[3], z88_cart, nullptr);
	m_carts[3]->out_flp_callback().set(m_blink, FUNC(upd65031_device::flp_w));

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("z88_cart");
}


/***************************************************************************

  Game driver(s)

***************************************************************************/

ROM_START(z88)
	ROM_REGION(0x80000, "bios", ROMREGION_ERASEFF)
	ROM_DEFAULT_BIOS("v40uk")
	ROM_SYSTEM_BIOS( 0, "v220uk", "Version 2.20 UK")
	ROMX_LOAD("z88v220.rom", 0x00000, 0x20000, CRC(0ae7d0fc) SHA1(5d89e8d98d2cc0acb8cd42dbfca601b7bd09c51e), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "v30uk", "Version 3.0 UK")
	ROMX_LOAD("z88v300.rom" ,  0x00000, 0x20000, CRC(802cb9aa) SHA1(ceb688025b79454cf229cae4dbd0449df2747f79), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS( 2, "v40uk", "Version 4.0 UK")
	ROMX_LOAD("z88v400.rom",   0x00000, 0x20000, CRC(1356d440) SHA1(23c63ceced72d0a9031cba08d2ebc72ca336921d), ROM_BIOS(2) )
ROM_END

ROM_START(z88de)
	ROM_REGION(0x80000, "bios", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS( 0, "v318de", "Version 3.18 German")
	ROMX_LOAD("z88v318de.rom", 0x00000, 0x20000, CRC(d7eaf937) SHA1(5acbfa324e2a6582ffd1af5f2e28086318d2ed27), ROM_BIOS(0) )
ROM_END

ROM_START(z88es)
	ROM_REGION(0x80000, "bios", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS( 0, "v319es", "Version 3.19 Spanish")
	ROMX_LOAD("z88v319es.rom", 0x00000, 0x20000, CRC(7a08af73) SHA1(a99a7581f47a032e1ec3b4f534c06f00f67647df), ROM_BIOS(0) )
ROM_END

ROM_START(z88fr)
	ROM_REGION(0x80000, "bios", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS( 0, "v326fr", "Version 3.26 French")
	ROMX_LOAD("z88v326fr.rom", 0x00000, 0x20000, CRC(218fbb72) SHA1(6e4c590f40f5b14d66e6559807f538fb5fa91474), ROM_BIOS(0) )
ROM_END

ROM_START(z88it)
	ROM_REGION(0x80000, "bios", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS( 0, "v323it", "Version 3.23 Italian")
	ROMX_LOAD("z88v323it.rom", 0x00000, 0x20000, CRC(13f54308) SHA1(29bda04ae803f2dff6357d81b3894db669d12dbf), ROM_BIOS(0) )
ROM_END

ROM_START(z88se)
	ROM_REGION(0x80000, "bios", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS( 0, "v250se", "Version 2.50 Swedish")
	ROMX_LOAD("z88v250sw.rom", 0x00000, 0x20000, CRC(dad01338) SHA1(3825eee346b692b16215a500250cc0c76d2d8f0b), ROM_BIOS(0) )
ROM_END

ROM_START(z88fi)
	ROM_REGION(0x80000, "bios", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS( 0, "v401fi", "Version 4.01 Finnish")
	ROMX_LOAD("z88v401fi.rom", 0x00000, 0x20000, CRC(ecd7f3f6) SHA1(bf8d3e083f1959e5a0d7e9c8d2ad0c14abd46381), ROM_BIOS(0) )
ROM_END

ROM_START(z88no)
	ROM_REGION(0x80000, "bios", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS( 0, "v260no", "Version 2.60 Norwegian")
	ROMX_LOAD("z88v260nr.rom", 0x00000, 0x20000, CRC(293f35c8) SHA1(b68b8f5bb1f69fe7a24897933b1464dc79e96d80), ROM_BIOS(0) )
ROM_END

ROM_START(z88dk)
	ROM_REGION(0x80000, "bios", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS( 0, "v321dk", "Version 3.21 Danish")
	ROMX_LOAD("z88v321dk.rom", 0x00000, 0x20000, CRC(baa80408) SHA1(7b0d44af2688d0fe47667e0424860aafa0948dae), ROM_BIOS(0) )
ROM_END

ROM_START(z88ch)
	ROM_REGION(0x80000, "bios", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS( 0, "v313ch", "Version 3.13 Swiss")
	ROMX_LOAD("z88v313he.rom", 0x00000, 0x20000, CRC(a56d732c) SHA1(c2276a12d457f01a8fd2e2ac238aff2b5c3559d8), ROM_BIOS(0) )
ROM_END

ROM_START(z88tr)
	ROM_REGION(0x80000, "bios", ROMREGION_ERASEFF)
	ROM_SYSTEM_BIOS( 0, "v317tk", "Version 3.17 Turkish")
	ROMX_LOAD("z88v317tk.rom", 0x00000, 0x20000, CRC(9468d677) SHA1(8d76e94f43846c736bf257d15d531c2df1e20fae), ROM_BIOS(0) )
ROM_END

/*    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY                FULLNAME           FLAGS */
COMP( 1988, z88,   0,      0,      z88,     z88,   z88_state, empty_init, "Cambridge Computers", "Z88"            , MACHINE_NOT_WORKING)
COMP( 1988, z88de, z88,    0,      z88,     z88de, z88_state, empty_init, "Cambridge Computers", "Z88 (German)"   , MACHINE_NOT_WORKING)
COMP( 1988, z88es, z88,    0,      z88,     z88es, z88_state, empty_init, "Cambridge Computers", "Z88 (Spanish)"  , MACHINE_NOT_WORKING)
COMP( 1988, z88fr, z88,    0,      z88,     z88fr, z88_state, empty_init, "Cambridge Computers", "Z88 (French)"   , MACHINE_NOT_WORKING)
COMP( 1988, z88it, z88,    0,      z88,     z88it, z88_state, empty_init, "Cambridge Computers", "Z88 (Italian)"  , MACHINE_NOT_WORKING)
COMP( 1988, z88se, z88,    0,      z88,     z88se, z88_state, empty_init, "Cambridge Computers", "Z88 (Swedish)"  , MACHINE_NOT_WORKING)
COMP( 1988, z88fi, z88,    0,      z88,     z88se, z88_state, empty_init, "Cambridge Computers", "Z88 (Finnish)"  , MACHINE_NOT_WORKING)
COMP( 1988, z88no, z88,    0,      z88,     z88no, z88_state, empty_init, "Cambridge Computers", "Z88 (Norwegian)", MACHINE_NOT_WORKING)
COMP( 1988, z88dk, z88,    0,      z88,     z88no, z88_state, empty_init, "Cambridge Computers", "Z88 (Danish)"   , MACHINE_NOT_WORKING)
COMP( 1988, z88ch, z88,    0,      z88,     z88ch, z88_state, empty_init, "Cambridge Computers", "Z88 (Swiss)"    , MACHINE_NOT_WORKING)
COMP( 1988, z88tr, z88,    0,      z88,     z88tr, z88_state, empty_init, "Cambridge Computers", "Z88 (Turkish)"  , MACHINE_NOT_WORKING)



z9001.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/*************************************************************************************

Robotron Z9001 (KC85/1)

2009-05-12 Skeleton driver.
2011-07-13 Notes added. You can enter text via terminal input.
           Colour and flashing added.
2019-06-13 Basic enabled

All input should be in UPPER case.

For KC87_10/11/20/21, type BASIC to start Basic.

The only other kind of acceptable input is a filename that is in 8.3 format and
begins with a letter. It will say 'start tape'. You can press ^C here to
escape, or any key to continue.

Some other control keys:
^B clear input line
^C break
^F toggle flashing attribute
^H backspace
^L clear screen


ToDo:
- cassette in - interrupt-driven via PIO1
    via astb should cause interrupt but nothing happens.
- proper keyboard - interrupt-driven via PIO2
    pressing any key should program ctc/2 to a debounce delay and this then causes
    another interrupt which reads keyboard and places ascii character at 0x0025.
- get rid of temporary code

**************************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/z80daisy.h"
#include "imagedev/cassette.h"
#include "machine/timer.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "sound/beep.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

// temporary
#include "machine/keyboard.h"


namespace {

class z9001_state : public driver_device
{
public:
	z9001_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_rom(*this, "maincpu")
		, m_ram(*this, "mainram")
		, m_beeper(*this, "beeper")
		, m_cass(*this, "cassette")
		, m_p_colorram(*this, "colorram")
		, m_p_videoram(*this, "videoram")
		, m_p_chargen(*this, "chargen")
	{
	}

	void z9001(machine_config &config);

private:
	void kbd_put(u8 data);
	void port88_w(uint8_t data);
	void cass_w(int state);
	TIMER_DEVICE_CALLBACK_MEMBER(timer_callback);
	uint32_t screen_update_z9001(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	uint8_t m_framecnt = 0U;
	bool m_cassbit = 0;
	virtual void machine_reset() override ATTR_COLD;
	virtual void machine_start() override ATTR_COLD;
	memory_passthrough_handler m_rom_shadow_tap;
	required_device<z80_device> m_maincpu;
	required_region_ptr<u8> m_rom;
	required_shared_ptr<u8> m_ram;
	required_device<beep_device> m_beeper;
	required_device<cassette_image_device> m_cass;
	required_shared_ptr<uint8_t> m_p_colorram;
	required_shared_ptr<uint8_t> m_p_videoram;
	required_region_ptr<u8> m_p_chargen;
};

void z9001_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0xe7ff).ram().share("mainram");
	map(0xc000, 0xe7ff).rom().region("maincpu",0x1000);
	map(0xe800, 0xebff).ram().share("colorram");
	map(0xec00, 0xefff).ram().share("videoram");
	map(0xf000, 0xffff).rom().region("maincpu",0);
}

void z9001_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x80, 0x83).mirror(4).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x88, 0x8B).mirror(4).rw("pio1", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
	map(0x90, 0x93).mirror(4).rw("pio2", FUNC(z80pio_device::read), FUNC(z80pio_device::write));
}

/* Input ports */
static INPUT_PORTS_START( z9001 )
INPUT_PORTS_END

static const z80_daisy_config z9001_daisy_chain[] =
{
	{ "pio2" },
	{ "pio1" },
	{ "ctc" },
	{ nullptr }
};

//Bits0,1 not connected; 2,3,4,5 go to a connector; 6 goes to 'graphics' LED; 7 goes to speaker.
void z9001_state::port88_w(uint8_t data)
{
	m_beeper->set_state(BIT(data, 7));
}

void z9001_state::cass_w(int state)
{
	if (state)
	{
		m_cassbit ^= 1;
		m_cass->output( m_cassbit ? -1.0 : +1.0);
	}
}


// temporary (prevent freezing when you type an invalid filename)
TIMER_DEVICE_CALLBACK_MEMBER(z9001_state::timer_callback)
{
	m_maincpu->space(AS_PROGRAM).write_byte(0x006a, 0);
}

void z9001_state::machine_reset()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x0000, 0x0fff, m_rom);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0xf000, 0xffff,
			"rom_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// reinstall ram over the rom shadow
					m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x0fff, m_ram);
				}
			},
			&m_rom_shadow_tap);
}

void z9001_state::machine_start()
{
	save_item(NAME(m_framecnt));
	save_item(NAME(m_cassbit));
}

uint32_t z9001_state::screen_update_z9001(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint16_t sy=0,ma=0;
	m_framecnt++;

	for (uint8_t y = 0; y < 24; y++ )
	{
		for (uint8_t ra = 0; ra < 8; ra++)
		{
			uint16_t *p = &bitmap.pix(sy++);

			for (uint16_t x = ma; x < ma + 40; x++)
			{
				uint8_t chr = m_p_videoram[x]; // get char in videoram
				uint8_t gfx = m_p_chargen[(chr<<3) | ra]; // get dot pattern in chargen
				uint8_t col = m_p_colorram[x];
				uint8_t fg = col>>4;
				uint8_t bg = col&15;

				/* Check for flashing - swap bg & fg */
				if ((BIT(col, 7)) && (m_framecnt & 0x10))
				{
					bg = fg;
					fg = col&15;
				}

				/* Display a scanline of a character */
				*p++ = BIT(gfx, 7) ? fg : bg;
				*p++ = BIT(gfx, 6) ? fg : bg;
				*p++ = BIT(gfx, 5) ? fg : bg;
				*p++ = BIT(gfx, 4) ? fg : bg;
				*p++ = BIT(gfx, 3) ? fg : bg;
				*p++ = BIT(gfx, 2) ? fg : bg;
				*p++ = BIT(gfx, 1) ? fg : bg;
				*p++ = BIT(gfx, 0) ? fg : bg;
			}
		}
		ma+=40;
	}
	return 0;
}

/* F4 Character Displayer */
static const gfx_layout charlayout =
{
	8, 8,                   /* 8 x 8 characters */
	1024,                   /* 4 x 256 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8                 /* every char takes 8 bytes */
};

void z9001_state::kbd_put(u8 data)
{
	m_maincpu->space(AS_PROGRAM).write_byte(0x0025, data);
}

static GFXDECODE_START( gfx_z9001 )
	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
GFXDECODE_END


void z9001_state::z9001(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(9'830'400) / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &z9001_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &z9001_state::io_map);
	m_maincpu->set_daisy_config(z9001_daisy_chain);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(50);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(40*8, 24*8);
	screen.set_visarea(0, 40*8-1, 0, 24*8-1);
	screen.set_screen_update(FUNC(z9001_state::screen_update_z9001));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_z9001);
	PALETTE(config, "palette").set_entries(16);

	/* Sound */
	SPEAKER(config, "mono").front_center();
	BEEP(config, "beeper", 800).add_route(ALL_OUTPUTS, "mono", 0.50);

	/* Devices */
	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(z9001_state::kbd_put));
	TIMER(config, "z9001_timer").configure_periodic(FUNC(z9001_state::timer_callback), attotime::from_msec(10));

	z80pio_device& pio1(Z80PIO(config, "pio1", XTAL(9'830'400) / 4));
	pio1.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	pio1.out_pa_callback().set(FUNC(z9001_state::port88_w));

	z80pio_device& pio2(Z80PIO(config, "pio2", XTAL(9'830'400) / 4)); // keyboard PIO
	pio2.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);

	z80ctc_device& ctc(Z80CTC(config, "ctc", XTAL(9'830'400) / 4));
	ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
	ctc.zc_callback<0>().set(FUNC(z9001_state::cass_w));
	ctc.zc_callback<2>().set("ctc", FUNC(z80ctc_device::trg3));

	CASSETTE(config, m_cass);
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
}

/* ROM definition */
ROM_START( z9001 )
	ROM_REGION( 0x3800, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "orig", "Original" )
	ROMX_LOAD( "os____f0.851", 0x0000, 0x1000, CRC(9fe60a92) SHA1(553609631f5eaa7d6758a73f56c613e280a5b310), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "rb20", "ROM-Bank System without menu" )
	ROMX_LOAD( "os_rb20.rom",  0x0000, 0x1000, CRC(c783124d) SHA1(c2893ce5bb23b280ba4e982e860586d21de2469b), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "rb21", "ROM-Bank System with menu" )
	ROMX_LOAD( "os_rb21.rom",  0x0000, 0x1000, CRC(11eec2dd) SHA1(5dbb661bdf4daf92d6c4ffbbdec674e57917e9eb), ROM_BIOS(2))

	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "chargen.851", 0x0000, 0x0800, CRC(dd9c0f4e) SHA1(2e4928ba7161f5cce7173b7d2ded3d6596ae2aa2))
	ROM_LOAD( "zg_cga.rom",  0x0800, 0x0800, CRC(697cefb1) SHA1(f57a78a928fe1151b2fedb7f1a93a141195422ff))
	ROM_LOAD( "zg_cgai.rom", 0x1000, 0x0800, CRC(ecadf355) SHA1(4d36fefd335903680c45a5e3f38b969d2e9bb621))
	ROM_LOAD( "zg_de.rom",   0x1800, 0x0800, CRC(71854b0a) SHA1(912bb7d1f8b4582894125e82da080bd9c3b88f34))
ROM_END

#define rom_kc85_111 rom_z9001

ROM_START( kc87_10 )
	ROM_REGION( 0x3800, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "orig", "Original" )
	ROMX_LOAD( "os____f0.851", 0x0000, 0x1000, CRC(9fe60a92) SHA1(553609631f5eaa7d6758a73f56c613e280a5b310), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "rb20", "ROM-Bank System without menu" )
	ROMX_LOAD( "os_rb20.rom",  0x0000, 0x1000, CRC(c783124d) SHA1(c2893ce5bb23b280ba4e982e860586d21de2469b), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "rb21", "ROM-Bank System with menu" )
	ROMX_LOAD( "os_rb21.rom",  0x0000, 0x1000, CRC(11eec2dd) SHA1(5dbb661bdf4daf92d6c4ffbbdec674e57917e9eb), ROM_BIOS(2))

	ROM_LOAD( "basic_c0.87a", 0x1000, 0x2800, CRC(c508d45e) SHA1(ea85b53e21429c4cb85cdb81b92f278a8f4eb574))

	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "chargen.851", 0x0000, 0x0800, CRC(dd9c0f4e) SHA1(2e4928ba7161f5cce7173b7d2ded3d6596ae2aa2))
	ROM_LOAD( "zg_cga.rom",  0x0800, 0x0800, CRC(697cefb1) SHA1(f57a78a928fe1151b2fedb7f1a93a141195422ff))
	ROM_LOAD( "zg_cgai.rom", 0x1000, 0x0800, CRC(ecadf355) SHA1(4d36fefd335903680c45a5e3f38b969d2e9bb621))
	ROM_LOAD( "zg_de.rom",   0x1800, 0x0800, CRC(71854b0a) SHA1(912bb7d1f8b4582894125e82da080bd9c3b88f34))
ROM_END

#define rom_kc87_11 rom_kc87_10

ROM_START( kc87_20 )
	ROM_REGION( 0x3800, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "orig", "Original" )
	ROMX_LOAD( "os____f0.87b", 0x0000, 0x1000, CRC(a357d093) SHA1(b1df6b499517c8366a0795030ee800e8a258e938), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "rb20", "ROM-Bank System without menu" )
	ROMX_LOAD( "os_rb20.rom",  0x0000, 0x1000, CRC(c783124d) SHA1(c2893ce5bb23b280ba4e982e860586d21de2469b), ROM_BIOS(1))
	ROM_SYSTEM_BIOS( 2, "rb21", "ROM-Bank System with menu" )
	ROMX_LOAD( "os_rb21.rom",  0x0000, 0x1000, CRC(11eec2dd) SHA1(5dbb661bdf4daf92d6c4ffbbdec674e57917e9eb), ROM_BIOS(2))

	ROM_LOAD( "basic_c0.87b", 0x1000, 0x2800, CRC(9e8f6380) SHA1(8ffecc64ba35c953c93738f8568c83dc6af1ae72))

	ROM_REGION( 0x2000, "chargen", 0 )
	ROM_LOAD( "chargen.851", 0x0000, 0x0800, CRC(dd9c0f4e) SHA1(2e4928ba7161f5cce7173b7d2ded3d6596ae2aa2))
	ROM_LOAD( "zg_cga.rom",  0x0800, 0x0800, CRC(697cefb1) SHA1(f57a78a928fe1151b2fedb7f1a93a141195422ff))
	ROM_LOAD( "zg_cgai.rom", 0x1000, 0x0800, CRC(ecadf355) SHA1(4d36fefd335903680c45a5e3f38b969d2e9bb621))
	ROM_LOAD( "zg_de.rom",   0x1800, 0x0800, CRC(71854b0a) SHA1(912bb7d1f8b4582894125e82da080bd9c3b88f34))
ROM_END

#define rom_kc87_21 rom_kc87_20

} // anonymous namespace

/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY     FULLNAME              FLAGS
COMP( 1984, z9001,    0,      0,      z9001,   z9001, z9001_state, empty_init, "Robotron", "Z9001 (KC 85/1.10)", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1986, kc85_111, z9001,  0,      z9001,   z9001, z9001_state, empty_init, "Robotron", "KC 85/1.11",         MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1987, kc87_10,  z9001,  0,      z9001,   z9001, z9001_state, empty_init, "Robotron", "KC 87.10",           MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1987, kc87_11,  z9001,  0,      z9001,   z9001, z9001_state, empty_init, "Robotron", "KC 87.11",           MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1987, kc87_20,  z9001,  0,      z9001,   z9001, z9001_state, empty_init, "Robotron", "KC 87.20",           MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1987, kc87_21,  z9001,  0,      z9001,   z9001, z9001_state, empty_init, "Robotron", "KC 87.21",           MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )



zapcomputer.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders:FelipeSanches
/*
    ZAP - Z80 Applications Processor

    driver by: Felipe Correa da Silva Sanches <juca@members.fsf.org>

    Based on the technical descriptions and
    assembly listings of the book:

        "Build Your Own Z80 Microcomputer"
        by Steve Ciarcia (1981)
        published by BYTE/McGRAW-HILL

    Basic usage instructions:
    SHIFT + 0: view and edit memory values (Enter to escape)
    SHIFT + 1: view and edit register values
    SHIFT + 2: execution mode

    For more info, please check chapter 6 "Monitor software" in the book
    and/or the assembly listings of the ZAP monitor software
    available at appendix D.

    Currently missing features in this driver:
      * hookup RS232 support
      * maybe also support video terminal described in chapter 9

    Here is a test to paste in:
    -0400^11^22^33^44^55^66^77^88^99^X-0400
    You can confirm the entries by pressing UP
*/


#include "emu.h"
#include "cpu/z80/z80.h"
#include "zapcomputer.lh"


namespace {

class zapcomp_state : public driver_device
{
public:
	zapcomp_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_digits(*this, "digit%u", 0U)
	{ }

	void zapcomp(machine_config &config);

private:
	uint8_t keyboard_r();
	void display_7seg_w(offs_t offset, uint8_t data);

	void zapcomp_io(address_map &map) ATTR_COLD;
	void zapcomp_mem(address_map &map) ATTR_COLD;

	uint8_t decode7seg(uint8_t data);
	virtual void machine_start() override ATTR_COLD;
	required_device<cpu_device> m_maincpu;
	output_finder<6> m_digits;
};

uint8_t zapcomp_state::decode7seg(uint8_t data)
{
	//These are bit patterns representing the conversion of 4bit values
	//into the status of the segments of the 7 segment displays
	//controlled by a 82S23 PROM

	static uint8_t patterns[16] = {
		0x77, 0x41, 0x6e, 0x6b,
		0x59, 0x3b, 0x3f, 0x61,
		0x7f, 0x79, 0x7d, 0x1f,
		0x36, 0x4f, 0x3e, 0x3c
	};

	// Bit order for the FAIRCHILD FND-70
	//                     7-SEGMENT LCD:  .  g  f  e  d  c  b  a
	return bitswap<8>(patterns[data & 0x0F], 7, 3, 4, 2, 1, 0, 6, 5);
}

void zapcomp_state::display_7seg_w(offs_t offset, uint8_t data)
{
	//Port 0x05 : address HI
	//Port 0x06 : address LOW
	//Port 0x07 : data
	m_digits[offset*2] = decode7seg(data >> 4);
	m_digits[offset*2+1] = decode7seg(data);
}

uint8_t zapcomp_state::keyboard_r()
{
	uint8_t retval = 0x00;
	uint8_t special = ioport("X1")->read();
	uint16_t hex_keys = ioport("X0")->read();

	if (BIT(special, 2)) /* "SHIFT" key is pressed */
		retval |= 0x40; /* turn on the SHIFT bit but DO NOT turn on the strobe bit */

	if (BIT(special, 1)) /* "NEXT" key is pressed */
		retval |= 0xA0; /* turn on the strobe & NEXT bits */

	if (BIT(special, 0)) /* "EXEC" key is pressed */
		retval |= 0x90; /* turn on the strobe & EXEC bit */

	for (int i=0; i<16; i++)
		if (hex_keys & (1 << i))
			retval |= (0x80 | i); /* provide the key index in bits 3-0
			                         as well as turning on the strobe bit */

	return retval;
}

void zapcomp_state::zapcomp_mem(address_map &map)
{
	map(0x0000, 0x03ff).rom(); /* system monitor */
	map(0x0400, 0x07ff).ram(); /* mandatory 1 kilobyte bank #0 */
	map(0x0800, 0x0bff).ram(); /* extra 1 kilobyte bank #1 (optional) */
	map(0x0c00, 0x0fff).ram(); /* extra 1 kilobyte bank #2 (optional) */
	map(0x1000, 0x13ff).ram(); /* extra 1 kilobyte bank #3 (optional) */
	map(0x1400, 0x17ff).ram(); /* extra 1 kilobyte bank #4 (optional) */
	map(0x1800, 0x1bff).ram(); /* extra 1 kilobyte bank #5 (optional) */
	map(0x1c00, 0x1fff).ram(); /* extra 1 kilobyte bank #6 (optional) */
	map(0x2000, 0x23ff).ram(); /* extra 1 kilobyte bank #7 (optional) */
}

void zapcomp_state::zapcomp_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x00).r(FUNC(zapcomp_state::keyboard_r));
	map(0x05, 0x07).w(FUNC(zapcomp_state::display_7seg_w));
}

static INPUT_PORTS_START( zapcomp )
	PORT_START("X0")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('-')
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('R')
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('Z')
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')

	PORT_START("X1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("EXEC") PORT_CODE(KEYCODE_ENTER) PORT_CHAR('X')
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("NEXT") PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
INPUT_PORTS_END

void zapcomp_state::machine_start()
{
	m_digits.resolve();
}

void zapcomp_state::zapcomp(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, XTAL(2'000'000));
	m_maincpu->set_addrmap(AS_PROGRAM, &zapcomp_state::zapcomp_mem);
	m_maincpu->set_addrmap(AS_IO, &zapcomp_state::zapcomp_io);

	/* video hardware */
	config.set_default_layout(layout_zapcomputer);
}

ROM_START( zapcomp )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD("zap.rom", 0x0000, 0x0400, CRC(3f4416e9) SHA1(d6493707bfba1a1e1e551f8144194afa5bda3316) )
ROM_END

} // anonymous namespace


//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY                               FULLNAME                            FLAGS
COMP( 1981, zapcomp, 0,      0,      zapcomp, zapcomp, zapcomp_state, empty_init, "Steve Ciarcia / BYTE / McGRAW-HILL", "ZAP - Z80 Applications Processor", MACHINE_NO_SOUND_HW )



zaurus.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Angelo Salese, Ryan Holtz
/****************************************************************************************************************************************

    Sharp Zaurus PDA skeleton driver (SL, ARM/Linux based, 4th generation)

    TODO:
    - Dumps are questionable

=========================================================================================================================================
Sharp Zaurus
------------

Personal Information (PI) Series
--------------------------------

Model: Pi^2 T (proof-of-concept model)
Manufacturer: Sharp
Nickname/Series:
Demo/Press Release Date: April 1992
Availability: Japan
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 288 KB
Flash (Internal): none
Other Storage: none
Slots:
Display: 239x168 dot matrix DSFTN mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA
Keyboard: No
Features: handwriting recognition
Note:

Model: PI-3000
Manufacturer: Sharp
Nickname/Series:
Release Date: October 1, 1993
Availability: Japan
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 288 KB
Flash (Internal): none
Other Storage: none
Slots:
Display: 239x168 dot matrix DSFTN mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA
Keyboard: No
Features: handwriting recognition, external faxmodem adapter (optional)
Note:

Model: PI-4000
Manufacturer: Sharp
Nickname/Series:
Release Date: June 1994
Availability: Japan
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 544 KB
Flash (Internal): none
Other Storage: none
Slots:
Display: 239x168 dot matrix mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA
Keyboard: No
Features: handwriting recognition, external faxmodem adapter (optional), "ink capabilities"?
Note:

Model: PI-4000FX
Manufacturer: Sharp
Nickname/Series:
Release Date: June 1994
Availability: Japan
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 544 KB
Flash (Internal): none
Other Storage: none
Slots:
Display: 239x168 dot matrix mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA
Keyboard: No
Features: handwriting recognition, included external faxmodem adapter
Note:

Model: PI-5000
Manufacturer: Sharp
Nickname/Series: AccessZaurus
Release Date: November 1994
Availability: Japan
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 1 MB
Flash (Internal): none
Other Storage: none
Slots:
Display: 239x168 dot matrix mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA, I/O Port - serial PC Link
Keyboard: No
Features: handwriting recognition, external faxmodem adapter (optional), email, add-in software capabilities
Note:

Model: PI-5000FX
Manufacturer: Sharp
Nickname/Series: AccessZaurus
Release Date: November 1994
Availability: Japan
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 1 MB
Flash (Internal): none
Other Storage: none
Slots:
Display: 239x168 dot matrix mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA, I/O Port - serial PC Link
Keyboard: No
Features: handwriting recognition, included external faxmodem adapter, email, add-in software capabilities
Note:

Model: PI-5000DA
Manufacturer: Sharp
Nickname/Series: AccessZaurus
Release Date: November 1994
Availability: Japan
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 1 MB
Flash (Internal): none
Other Storage: none
Slots:
Display: 239x168 dot matrix mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA, I/O Port - serial PC Link
Keyboard: No
Features: handwriting recognition, email, included digital mobile phone adapter, add-in software capabilities
Note:

Model: PI-4500
Manufacturer: Sharp
Nickname/Series:
Release Date: January 1995
Availability: Japan
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 544 KB
Flash (Internal): none
Other Storage: none
Slots:
Display: 239x168 dot matrix mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA, I/O Port - serial PC Link
Keyboard: No
Features: handwriting recognition, included external faxmodem adapter, email, add-in software capabilities
Note:

Model: PI-6000
Manufacturer: Sharp
Nickname/Series: AccessZaurus
Release Date: August 25, 1995
Availability: Japan
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 1 MB
Flash (Internal): none
Other Storage: none
Slots: Flash IC card slot
Display: 239x168 dot matrix mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA, I/O Port - serial PC Link
Keyboard: No
Features: upgraded handwriting recognition (over PI-4xxx/5xxx), email, add-in software capabilities
Note:

Model: PI-6000FX
Manufacturer: Sharp
Nickname/Series: AccessZaurus
Release Date: August 25, 1995
Availability: Japan
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 1 MB
Flash (Internal): none
Other Storage: none
Slots: Flash IC card slot
Display: 239x168 dot matrix mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA, I/O Port - serial PC Link
Keyboard: No
Features: upgraded handwriting recognition (over PI-4xxx/5xxx), included external faxmodem adapter, email
Note:

Model: PI-6000DA
Manufacturer: Sharp
Nickname/Series: AccessZaurus
Release Date: December 16, 1995
Availability: Japan
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 1 MB
Flash (Internal): none
Slots: Flash IC card slot
Other Storage: none
Display: 239x168 dot matrix mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA, I/O Port - serial PC Link
Keyboard: No
Features: upgraded handwriting recognition (over PI-4xxx/5xxx), email, included digital mobile phone adapter
Note:

Model: PI-7000
Manufacturer: Sharp
Nickname/Series: AccessZaurus
Release Date: February 1996
Availability: Japan
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 1 MB
Flash (Internal): 2 MB
Slots: no IC card slot
Other Storage: none
Display: 239x168 dot matrix mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA, internal faxmodem, I/O Port - serial PC Link
Keyboard: No
Features: upgraded handwriting recognition (over PI-4xxx/5xxx), email
Note:

Model: PI-6500
Manufacturer: Sharp
Nickname/Series: AccessZaurus
Release Date: November 22, 1996
Availability: Japan
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 1 MB (715 KB user addressable)
Flash (Internal): none
Other Storage: none
Slots:
Display: 239x168 dot matrix mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA, I/O Port - serial PC Link
Keyboard: No
Features: upgraded handwriting recognition (over PI-4xxx/5xxx), email
Note:

Model: PI-8000
Manufacturer: Sharp
Nickname/Series: AccessZaurus
Release Date: January 24, 1997
Availability: Japan
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible for handwriting recognition
Memory: 1 MB (711 KB user addressable)
Flash (Internal): none
Other Storage: none
Slots:
Display: 319x168 dot matrix mono "widescreen" LCD touchscreen
OS: Sharp Synergy
I/O: IrDA, internal faxmodem, I/O Port - serial PC Link
Keyboard: No
Features: upgraded handwriting recognition (over PI-4xxx/5xxx), email
Note:

Model: PI-6600
Manufacturer: Sharp
Nickname/Series: AccessZaurus
Release Date: September 25, 1997
Availability: Japan
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 1 MB
Flash (Internal): none
Other Storage: none
Slots:
Display: 239x168 dot matrix mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA, internal faxmodem, I/O Port - serial PC Link
Keyboard: Yes
Features: upgraded handwriting recognition (over PI-4xxx/5xxx), email, included external digital mobile phone adapter, PC PIM software PowerPIMM ver 2.0, Microsoft Excel PC Viewer software
Note:

============================================================================================

PI-B Series (Business/Commercial)
---------------------------------

Model: PI-B304
Manufacturer: Sharp
Nickname/Series:
Release Date: October 1995
Availability: Japan
CPU: NEC V30 (low-power version) 16-bit CPU
Memory: 2 MB RAM
Flash (Internal): 2 MB
Other Storage: none
Slots:
Display: 480x320 dot matrix reflective mono LCD touchscreen
OS: MS-DOS
I/O: IrDA, I/O Port - serial PC Link
Keyboard:
Features:
Note:

Model: PI-B308
Manufacturer: Sharp
Nickname/Series:
Release Date: October 1995
Availability: Japan
CPU: NEC V30 (low-power version) 16-bit CPU
Memory: 2 MB RAM
Flash (Internal): 6 MB
Other Storage: none
Slots:
Display: 480x320 dot matrix reflective mono LCD touchscreen
OS: MS-DOS
I/O: IrDA, I/O Port - serial PC Link
Keyboard:
Features:
Note:

============================================================================================

K-PDA (ZR) Series
-----------------

Model: ZR-5000
Manufacturer: Sharp
Nickname/Series: K-PDA
Release Date: January 1995
Availability: US, Euro
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 1 MB RAM
Flash (Internal): none
Other Storage: none
Slots: PCMCIA Type II
Display: 320x240 dot matrix mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA, I/O Port - serial PC Link
Keyboard: Yes
Features: handwriting recognition, email
Note:

Model: ZR-5000/FX
Manufacturer: Sharp
Nickname/Series: K-PDA
Release Date: January 1995
Availability: US, Euro
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 1 MB RAM
Flash (Internal): none
Other Storage: none
Slots: PCMCIA Type II
Display: 320x240 dot matrix mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: Yes
Features: handwriting recognition, email
Note:

Model: ZR-5700
Manufacturer: Sharp
Nickname/Series: K-PDA
Release Date: 1995/1996?
Availability: US, Euro
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 1 MB RAM (user area: approx. 600K)
Flash (Internal): none
Other Storage: none
Slots: PCMCIA Type II
Display: 320x240 dot matrix mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA, I/O Port - serial PC Link
Keyboard: Yes
Features: handwriting recognition, email
Note:

Model: ZR-5700
Manufacturer: Sharp
Nickname/Series: K-PDA
Release Date: 1995/1996?
Availability: US, Euro
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 1 MB RAM (user area: approx. 600K)
Flash (Internal): none
Other Storage: none
Slots: PCMCIA Type II
Display: 320x240 dot matrix mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: Yes
Features: handwriting recognition, email
Note:

Model: ZR-5800
Manufacturer: Sharp
Nickname/Series: K-PDA
Release Date: 1996
Availability: US, Euro
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 2 MB RAM (user area: approx. 1624K)
Flash (Internal): none
Other Storage: none
Slots: PCMCIA Type II
Display: 320x240 backlit dot matrix mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA, I/O Port - serial PC Link
Keyboard: Yes
Features: upgraded handwriting recognition, email, digital mobile phone adapter (optional)
Note:

Model: ZR-5800/FX
Manufacturer: Sharp
Nickname/Series: K-PDA
Release Date: 1996
Availability: US, Euro
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 2 MB RAM (user area: approx. 1624K)
Flash (Internal): none
Other Storage: none
Slots: PCMCIA Type II
Display: 320x240 backlit dot matrix mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: Yes
Features: upgraded handwriting recognition, email, digital mobile phone adapter (optional)
Note:

Model: ZR-3000
Manufacturer: Sharp
Nickname/Series: K-PDA
Release Date: 1996
Availability: US, Euro
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 1 MB RAM (user area approx. 650K)
Flash (Internal): none
Other Storage: none
Slots:
Display: 320x240 dot matrix mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA, I/O Port - serial PC Link
Keyboard: Yes
Features: handwriting recognition, external faxmodem adapter (optional), email
Note:

Model: ZR-3500X
Manufacturer: Sharp
Nickname/Series: K-PDA
Release Date: 1996
Availability: US, Euro
Main CPU: Sharp SC62015-compatible "ESR-L" 8-bit CPU
Sub CPU: Z80-compatible co-processor for handwriting recognition
Memory: 1 MB RAM (user area approx. 650K)
Flash (Internal): 1 MB
Other Storage: none
Slots:
Display: 320x240 dot matrix mono LCD touchscreen
OS: Sharp Synergy
I/O: ASK/IrDA, I/O Port - serial PC Link, internal faxmodem (14.4/9.6 kbit/s)
Keyboard: Yes
Features: handwriting recognition, email
Note:

============================================================================================

MI Series
---------

Model: MI-10
Manufacturer: Sharp
Nickname/Series: ColorZaurus
Release Date: June 25, 1996
Availability: Japan
CPU: unknown MHz Hitachi SH3 32-bit CPU
Memory: unknown
Flash (Internal): none
Other Storage: none
Slots:
Display: 320x240 16-bit color TFT LCD touchscreen
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: No
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording
Note:

Model: MI-10DC
Manufacturer: Sharp
Nickname/Series: ColorZaurus
Release Date: June 25, 1996
Availability: Japan
CPU: unknown MHz Hitachi SH3 32-bit CPU
Memory: unknown
Flash (Internal): none
Other Storage: none
Slots:
Display: 320x240 16-bit color TFT LCD touchscreen
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: No
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording, digital camera
Note:

Model: MI-504
Manufacturer: Sharp
Nickname/Series: PowerZaurus
Release Date: July 17, 1997
Availability: Japan
CPU: 30 MHz Hitachi SH3 32-bit CPU
Memory: 1.4 MB
Flash (Internal):
Other Storage: none
Slots:
Display: 320x240 16-bit color LCD touchscreen
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: No
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording
Note:

Model: MI-506
Manufacturer: Sharp
Nickname/Series: PowerZaurus
Release Date: July 17, 1997
Availability: Japan
CPU: 30 MHz Hitachi SH3 32-bit CPU
Memory: 3.4 MB
Flash (Internal):
Other Storage: none
Slots:
Display: 320x240 16-bit color LCD touchscreen
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: No
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording
Note:

Model: MI-506DC
Manufacturer: Sharp
Nickname/Series: PowerZaurus
Release Date: July 17, 1997
Availability: Japan
CPU: 30 MHz Hitachi SH3 32-bit CPU
Memory: 3.4 MB
Flash (Internal):
Other Storage: none
Slots:
Display: 320x240 16-bit color LCD touchscreen
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: No
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording, digital camera
Note:

Model: MI-106
Manufacturer: Sharp
Nickname/Series: ZaurusPocket
Release Date: November 28, 1997
Availability: Japan
CPU: 30 MHz Hitachi SH3 32-bit CPU
Memory: 6 MB
Flash (Internal):
Other Storage: none
Slots:
Display: 320x240 grayscale LCD touchscreen
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: No
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording
Note:

Model: MI-106M
Manufacturer: Sharp
Nickname/Series: ZaurusPocket
Release Date: November 28, 1997
Availability: Japan
CPU: 30 MHz Hitachi SH3 32-bit CPU
Memory: 6 MB
Flash (Internal):
Other Storage: none
Slots:
Display: 320x240 grayscale LCD touchscreen
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: No
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording
Note:

Model: MI-110M
Manufacturer: Sharp
Nickname/Series: ZaurusPocket
Release Date: November 28, 1997
Availability: Japan
CPU: 30 MHz Hitachi SH3 32-bit CPU
Memory: 10 MB
Flash (Internal):
Other Storage: none
Slots:
Display: 320x240 grayscale LCD touchscreen
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: No
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording
Note:

Model: MI-610
Manufacturer: Sharp
Nickname/Series: PowerZaurus
Release Date: March 3, 1998
Availability: Japan
CPU: 60 MHz Hitachi SH3 32-bit CPU
Memory: 10 MB
Flash (Internal):
Other Storage: none
Slots:
Display: 320x240 16-bit color TFT LCD touchscreen
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: No
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording
Note:

Model: MI-610DC
Manufacturer: Sharp
Nickname/Series: PowerZaurus
Release Date: March 3, 1998
Availability: Japan
CPU: 60 MHz Hitachi SH3 32-bit CPU
Memory: 10 MB
Flash (Internal):
Other Storage: none
Slots:
Display: 320x240 16-bit color TFT LCD touchscreen
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: No
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording, digital camera
Note:

Model: MI-310
Manufacturer: Sharp
Nickname/Series: PowerZaurus
Release Date: September 4, 1998
Availability: Japan
CPU: 66 MHz Hitachi SH3 32-bit CPU
Memory: 10 MB
Flash (Internal):
Other Storage: none
Slots:
Display: 320x240 16-bit color reflective TFT LCD touchscreen
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: No
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording
Note:

Model: MI-P1-LA
Manufacturer: Sharp
Nickname/Series: Zaurus i-Geti
Release Date: March 20, 1999
Availability: Japan
CPU: unknown MHz Hitachi SH3 32-bit CPU
Memory: 6 MB
Flash (Internal):
Other Storage: none
Slots:
Display: 320x240 grayscale LCD touchscreen
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: Yes
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording
Note:

Model: MI-P1-A
Manufacturer: Sharp
Nickname/Series: Zaurus i-Geti
Release Date: March 20, 1999
Availability: Japan
CPU: unknown MHz Hitachi SH3 32-bit CPU
Memory: 6 MB
Flash (Internal):
Other Storage: none
Slots:
Display: 320x240 grayscale LCD touchscreen
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: Yes
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording
Note:

Model: MI-P1-W
Manufacturer: Sharp
Nickname/Series: Zaurus i-Geti
Release Date: March 20, 1999
Availability: Japan
CPU: unknown MHz Hitachi SH3 32-bit CPU
Memory: 6 MB
Flash (Internal):
Other Storage: none
Slots:
Display: 320x240 grayscale LCD touchscreen
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: Yes
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording
Note:

Model: MI-EX1
Manufacturer: Sharp
Nickname/Series: Zaurus i-Cruise
Release Date: April 16, 1999
Availability: Japan
CPU: 120 MHz Hitachi SH3 32-bit CPU
Memory: 8 MB
Flash (Internal):
Other Storage: none
Slots:
Display: 640x480 VGA 16-bit color TFT LCD touchscreen
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: Yes
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording
Note:

=== Note: Every MI-series after this point _might_ not be a Hitachi SH3 CPU, best references so far call them RISC 32-bit CPUs... ===

Model: MI-P2-B
Manufacturer: Sharp
Nickname/Series: Zaurus i-Geti
Release Date: July 9, 1999
Availability: Japan
CPU: unknown MHz Hitachi SH3 32-bit CPU
Memory: 10 MB
Flash (Internal):
Other Storage: none
Slots:
Display: 320x240 grayscale LCD touchscreen
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: Yes
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording, more internal software
Note:

Model: MI-TR1
Manufacturer: Sharp
Nickname/Series: Zaurus i-Cruise (customized)
Release Date: August 1999
Availability: Japan
CPU: unknown MHz Hitachi SH3 32-bit CPU
Memory: 16 MB
Flash (Internal):
Other Storage: none
Slots:
Display: 640x480 VGA 16-bit color TFT LCD touchscreen
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: Yes
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording
Note:

Model: MI-C1-S
Manufacturer: Sharp
Nickname/Series: PowerZaurus
Release Date: December 7, 1999
Availability: Japan
CPU: unknown MHz Hitachi SH3 32-bit CPU
Memory: 16 MB
Flash (Internal):
Other Storage: none
Slots:
Display: 320x240 Super Mobile 16-bit color reflective LCD touchscreen
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: Yes
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording
Note:

Model: MI-C1-A
Manufacturer: Sharp
Nickname/Series: PowerZaurus
Release Date: December 7, 1999
Availability: Japan
CPU: unknown MHz Hitachi SH3 32-bit CPU
Memory: 16 MB
Flash (Internal):
Other Storage: none
Slots:
Display: 320x240 Super Mobile 16-bit color reflective LCD touchscreen
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: Yes
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording
Note:

Model: MI-P10-S
Manufacturer: Sharp
Nickname/Series: Zaurus i-Geti
Release Date: July 14, 2000
Availability: Japan
CPU: unknown MHz Hitachi SH3 32-bit CPU
Memory: 16 MB
Flash (Internal):
Other Storage: none
Slots:
Display: 320x240 grayscale LCD touchscreen (not backlit)
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: Yes
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording, more internal software
Note:

Model: MI-J1
Manufacturer: Sharp
Nickname/Series: Internet Dictionary Zaurus
Release Date: August 4, 2000
Availability: Japan
CPU: unknown MHz Hitachi SH3 32-bit CPU
Memory: 6 MB
Flash (Internal):
Other Storage: none
Slots:
Display: 320x240 grayscale LCD touchscreen
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: Yes
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording, more internal software, larger dictionary
Note:

Model: MI-E1
Manufacturer: Sharp
Nickname/Series:
Release Date: December 15, 2000
Availability: Japan
CPU: unknown MHz Hitachi SH3 32-bit CPU
Memory: 16 MB
Flash (Internal):
Other Storage: none
Slots: Smart Disk/Secure Digital/MMC, CompactFlash
Display: 240x320 16-bit color backlit TFT LCD touchscreen (vertical display)
Audio Controller:
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: Yes (mini-keyboard)
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording, MP3 player, MPEG4 video playback, headphone jack
Note:

Model: MI-L1
Manufacturer: Sharp
Nickname/Series:
Release Date: May 21, 2001
Availability: Japan
CPU: unknown MHz Hitachi SH3 32-bit CPU
Memory: 16 MB
Flash (Internal):
Other Storage: none
Slots: Smart Disk/Secure Digital/MMC, CompactFlash
Display: 240x320 16-bit color LCD touchscreen (not backlit, vertical display)
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: Yes (mini-keyboard)
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording
Note:

Model: MI-E21
Manufacturer: Sharp
Nickname/Series:
Release Date: September 7, 2001
Availability: Japan
CPU: unknown MHz Hitachi SH3 32-bit CPU (faster than MI-E1 CPU)
Memory: 32 MB
Flash (Internal):
Other Storage: none
Slots: Smart Disk/Secure Digital/MMC, CompactFlash
Display: 240x320 16-bit color backlit TFT LCD touchscreen (vertical display)
Audio Controller:
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: Yes (mini-keyboard)
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording, MP3 player, MPEG4 video playback, headphone jack
Note:

Model: MI-E25DC
Manufacturer: Sharp
Nickname/Series:
Release Date: March 15, 2002
Availability: Japan
CPU: unknown MHz Hitachi SH3 32-bit CPU (same as MI-E21)
Memory: 32 MB
Flash (Internal):
Other Storage: none
Slots: Smart Disk/Secure Digital/MMC, CompactFlash
Display: 240x320 16-bit color backlit TFT LCD touchscreen (vertical display)
Audio Controller:
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O: IrDA, I/O Port - serial PC Link, internal faxmodem
Keyboard: Yes (mini-keyboard)
Features: handwriting recognition, email, external digital mobile phone adapter, Internet, audio recording, MP3 player, MPEG4 video playback, headphone jack, 640x480 digital camera
Note:

============================================================================================

MT Series
---------

Model: MT-200
Manufacturer: Sharp
Nickname/Series:
Release Date: December 4, 1998
Availability: Japan
CPU: unknown MHz Hitachi SH3 32-bit CPU
Memory: unknown
Flash (Internal):
Other Storage:
Slots:
Display:
Audio Controller:
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O:
Keyboard:
Features:
Note: The "Communication Pals" or "Browser Boards", not technically Zaurus, basically MI technology with a keyboard on top

Model: MT-200SA
Manufacturer: Sharp
Nickname/Series:
Release Date:
Availability: Japan
CPU: unknown MHz Hitachi SH3 32-bit CPU
Memory: unknown
Flash (Internal):
Other Storage:
Slots:
Display:
Audio Controller:
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O:
Keyboard:
Features:
Note: The "Communication Pals" or "Browser Boards", not technically Zaurus, basically MI technology with a keyboard on top

Model: MT-300
Manufacturer: Sharp
Nickname/Series:
Release Date: September 9, 1999
Availability: Japan
CPU: unknown MHz Hitachi SH3 32-bit CPU
Memory: unknown
Flash (Internal):
Other Storage:
Slots:
Display:
Audio Controller:
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O:
Keyboard:
Features:
Note: The "Communication Pals" or "Browser Boards", not technically Zaurus, basically MI technology with a keyboard on top

Model: MT-300C
Manufacturer: Sharp
Nickname/Series:
Release Date:
Availability: Japan
CPU: unknown MHz Hitachi SH3 32-bit CPU
Memory: unknown
Flash (Internal):
Other Storage:
Slots:
Display:
Audio Controller:
OS: Sharp ZaurusOS (Axe XTAL microkernel)
I/O:
Keyboard:
Features:
Note: The "Communication Pals" or "Browser Boards", not technically Zaurus, basically MI technology with a keyboard on top

============================================================================================

SL Series
---------

Model: SL-5000D
Manufacturer: Sharp
Nickname/Series: Developer Edition
Release Date: 2001
Availability: World
CPU: 206 MHz Intel SA-1110 StrongARM 32-bit CPU
Memory: 32 MB DRAM
Flash (Internal): 16 MB
Other Storage: none
Slots: Secure Digital/MMC, CompactFlash Type II
Display: 240x320 16-bit color reflective TFT LCD touchscreen
Audio Controller:
OS: Lineo Embedix Plus PDA OS (Linux 2.4), Qtopia GUI, Jeode Java VM
I/O: IrDA, internal faxmodem, I/O Port - serial/USB PC Link
Keyboard: Yes
Features: handwriting recognition, email, digital mobile phone adapter, Internet, 640x480 digital camera, audio recording, MP3 player, MPEG4 video playback, headphone jack
Note:

Model: SL-5500
Manufacturer: Sharp
Nickname/Series: Collie
Release Date: March 11, 2002
Availability: World
CPU: 206 MHz Intel SA-1110 StrongARM 32-bit CPU
Memory: 64 MB DRAM
Flash (Internal): 16 MB
Other Storage: none
Slots: Secure Digital/MMC, CompactFlash Type II
Display: 240x320 16-bit color reflective TFT LCD touchscreen with front light
Audio Controller:
OS: Lineo Embedix Plus PDA OS (Linux 2.4), Qtopia GUI, Jeode Java VM
I/O: IrDA, internal faxmodem, I/O Port - serial/USB PC Link
Keyboard: Yes
Features: handwriting recognition, email, digital mobile phone adapter, Internet, 640x480 digital camera, audio recording, MP3 player, MPEG4 video playback, headphone jack
Note:

Model: SL-5500G
Manufacturer: Sharp
Nickname/Series: Collie
Release Date: March 11, 2002
Availability: Germany?
CPU: 206 MHz Intel SA-1110 StrongARM 32-bit CPU
Memory: 64 MB DRAM
Flash (Internal): 16 MB
Other Storage: none
Slots: Secure Digital/MMC, CompactFlash Type II
Display: 240x320 16-bit color reflective TFT LCD touchscreen with front light
Audio Controller:
OS: Lineo Embedix Plus PDA OS (Linux 2.4), Qtopia GUI, Jeode Java VM
I/O: IrDA, internal faxmodem, I/O Port - serial/USB PC Link
Keyboard: Yes
Features: handwriting recognition, email, digital mobile phone adapter, Internet, 640x480 digital camera, audio recording, MP3 player, MPEG4 video playback, headphone jack
Note:

Model: SL-A300
Manufacturer: Sharp
Nickname/Series: Discovery
Release Date: July 12, 2002
Availability: Japan
CPU: 200 MHz Intel XScale PXA210 32-bit CPU
Memory: 64 MB DRAM (approx. 32 MB user area)
Flash (Internal): 16 MB
Other Storage: none
Slots: Secure Digital/MMC
Display: 240x320 16-bit color backlit TFT LCD touchscreen
Audio Controller:
OS: Lineo Embedix Plus PDA OS (Linux 2.4), Qtopia GUI, Jeode Java VM
I/O: IrDA, internal faxmodem, I/O Port - serial/USB PC Link
Keyboard: No
Features: handwriting recognition, email, digital mobile phone adapter, Internet, 640x480 digital camera, audio recording, MP3 player, MPEG4 video playback, headphone jack
Note:

Model: SL-B500
Manufacturer: Sharp
Nickname/Series: Poodle
Release Date: December 14, 2002
Availability: Japan
CPU: 400MHz Intel XScale PXA250 32-bit CPU
Memory: 32 MB DRAM
Flash (Internal): 64 MB
Other Storage: 512KB "rescue" mode NOR P2ROM
Slots: Secure Digital/MMC, CompactFlash Type II
Display: 240x320 16-bit color backlit TFT LCD touchscreen
Audio Controller:
OS: Lineo Embedix OS (Linux 2.4), Qtopia GUI
I/O: IrDA, internal faxmodem, I/O Port - serial/USB PC Link
Keyboard: Yes
Features: handwriting recognition, email, digital mobile phone adapter, Internet, 640x480 digital camera, audio recording, MP3 player, MPEG4 video playback, internal speaker/microphone, headphone jack
Note:

Model: SL-5600
Manufacturer: Sharp
Nickname/Series: Poodle
Release Date: April 2?, 2002
Availability: World
CPU: 400MHz Intel XScale PXA250 32-bit CPU
Memory: 32 MB DRAM
Flash (Internal): 64 MB (approx. 33 MB user area)
Other Storage: 512KB "rescue" mode NOR P2ROM
Slots: Secure Digital/MMC, CompactFlash Type II
Display: 240x320 16-bit color relective TFT LCD touchscreen with front light
Audio Controller:
OS: Lineo Embedix3 OS (Linux 2.4.18), Qtopia GUI 1.5.0
I/O: IrDA, internal faxmodem, I/O Port - serial/USB PC Link
Keyboard: Yes
Features: handwriting recognition, email, digital mobile phone adapter, Internet, 640x480 digital camera, audio recording, MP3 player, MPEG4 video playback, internal speaker/microphone, headphone jack
Note: Some have an easily-fixed cache bug on the PXA-250

Model: SL-C700
Manufacturer: Sharp
Nickname/Series: Corgi
Release Date: December 14, 2002
Availability: Japan
CPU: 206 MHz Intel XScale PXA250 32-bit CPU (underclocked to similar speed as SL-5500?)
Memory: 32 MB DRAM
Flash (Internal): 64 MB
Other Storage: 512KB "rescue" mode NOR P2ROM
Slots: Secure Digital/MMC, CompactFlash Type II
Display: 640x480 VGA 16-bit color Sharp "System LCD" semi-transflective backlit CGS TFT LCD touchscreen
Audio Controller:
OS: Lineo Embedix OS (Linux 2.4.18), Qtopia GUI
I/O: IrDA, internal faxmodem, I/O Port - USB PC Link
Keyboard: Yes
Features: handwriting recognition, email, digital mobile phone adapter, Internet, 640x480 digital camera, audio recording, MP3 player, MPEG4 video playback, screen rotation, internal speaker/microphone, headphone jack
Note:

Model: SL-C750
Manufacturer: Sharp
Nickname/Series: Shepherd
Release Date: 2003
Availability: Japan
CPU: 400 MHz Intel XScale PXA255 32-bit CPU
Memory: 64 MB DRAM
Flash (Internal): 64 MB (user area: approx. 30 MB)
Other Storage: 512KB "rescue" mode NOR P2ROM
Slots: Secure Digital/MMC, CompactFlash Type II
Display: 640x480 VGA 16-bit color Sharp "System LCD" semi-transflective backlit CGS TFT LCD touchscreen
Audio Controller:
OS: Metrowerks OpenPDA OS, Qtopia GUI, CVM Java VM
I/O: IrDA, internal faxmodem, I/O Port - USB PC Link
Keyboard: Yes (mini-keyboard)
Features: handwriting recognition, email, digital mobile phone adapter, Internet, 640x480 digital camera, audio recording, MP3 player, MPEG4 video playback, screen rotation, updated software, internal speaker/microphone, headphone jack
Note:

Model: SL-C760
Manufacturer: Sharp
Nickname/Series: Husky
Release Date: 2003
Availability: Japan
CPU: 400 MHz Intel XScale PXA255 32-bit CPU
Memory: 64 MB DRAM
Flash (Internal): 128 MB (user area: approx. 65 MB)
Other Storage: 512KB "rescue" mode NOR P2ROM
Slots: Secure Digital/MMC (possibly SDIO-compatible), CompactFlash Type II
Display: 640x480 VGA 16-bit color Sharp "System LCD" semi-transflective backlit CGS TFT LCD touchscreen
Audio Controller:
OS: Metrowerks OpenPDA OS, Qtopia GUI, CVM Java VM
I/O: IrDA, internal faxmodem, I/O Port - USB PC Link
Keyboard: Yes (mini-keyboard)
Features: handwriting recognition, email, digital mobile phone adapter, Internet, 640x480 digital camera, audio recording, MP3 player, MPEG4 video playback, screen rotation, internal speaker/microphone, headphone jack
Note:

Model: SL-C860
Manufacturer: Sharp
Nickname/Series: Boxer
Release Date: 2003
Availability: Japan
CPU: 400 MHz Intel XScale PXA255 32-bit CPU
Memory: 64 MB DRAM
Flash (Internal): 128 MB (user area: approx. 65 MB)
Other Storage: 512KB "rescue" mode NOR P2ROM
Slots: Secure Digital/MMC (possibly SDIO-compatible), CompactFlash Type II
Display: 640x480 VGA 16-bit color Sharp "System LCD" semi-transflective backlit CGS TFT LCD touchscreen
Audio Controller:
OS: Metrowerks OpenPDA OS, Qtopia GUI, CVM Java VM
I/O: IrDA, internal faxmodem, USB Device, I/O Port - USB PC Link
Keyboard: Yes (mini-keyboard)
Features: handwriting recognition, email, digital mobile phone adapter, Internet, 640x480 digital camera, audio recording, MP3 player, MPEG4 video playback, screen rotation, English-Japanese translation software, internal speaker/microphone, headphone jack
Note:

Model: SL-6000N
Manufacturer: Sharp
Nickname/Series: Tosa
Release Date: early 2004
Availability: World
CPU: 400 MHz Intel XScale PXA255 32-bit CPU
Memory: 64 MB DRAM
Flash (Internal): 64 MB
Other Storage: 8 MB "rescue" mode NOR P2ROM
Slots: Secure Digital/MMC, CompactFlash Type II
Display: 640x480 VGA 16-bit color Sharp "System LCD" semi-transflective backlit CGS TFT LCD touchscreen
Audio Controller: Wolfson WM9712
OS: Metrowerks OpenPDA OS, Qtopia GUI, CVM Java VM
I/O: IrDA, internal faxmodem, USB OTG 1.1 (Slave/Host), I/O Port
Keyboard: Yes
Features: handwriting recognition, email, digital mobile phone adapter, Internet, 640x480 digital camera, audio recording, MP3 player, MPEG4 video playback, internal speaker/microphone, headphone jack
Note:

Model: HC-6000N
Manufacturer: Sharp
Nickname/Series:
Release Date: early 2004
Availability: World
CPU: 400 MHz Intel XScale PXA255 32-bit CPU
Memory: 64 MB DRAM
Flash (Internal): 64 MB
Other Storage: 8 MB "rescue" mode NOR P2ROM
Slots: Secure Digital/MMC, CompactFlash Type II
Display: 640x480 VGA 16-bit color Sharp "System LCD" semi-transflective backlit CGS TFT LCD touchscreen
Audio Controller: Wolfson WM9712
OS: Microsoft Windows Mobile 2003 Second Edition
I/O: IrDA, internal faxmodem, USB OTG 1.1 (Slave/Host), I/O Port
Keyboard: Yes
Features: handwriting recognition, email, digital mobile phone adapter, Internet, 640x480 digital camera, audio recording, MP3 player, MPEG4 video playback, internal speaker/microphone, headphone jack
Note: Clone - Hitachi FLORA-ie MX1

Model: SL-6000D
Manufacturer: Sharp
Nickname/Series: Tosa
Release Date: early 2004
Availability: World
CPU: 400 MHz Intel XScale PXA255 32-bit CPU
Memory: 64 MB DRAM
Flash (Internal): 64 MB
Other Storage: 8 MB "rescue" mode NOR P2ROM
Slots: Secure Digital/MMC, CompactFlash Type II
Display: 640x480 VGA 16-bit color Sharp "System LCD" semi-transflective backlit CGS TFT LCD touchscreen
Audio Controller: Wolfson WM9712
OS: Metrowerks OpenPDA OS, Qtopia GUI, CVM Java VM
I/O: IrDA, internal faxmodem, USB OTG 1.1 (Slave/Host), I/O Port,
Keyboard: Yes
Features: handwriting recognition, email, digital mobile phone adapter, Internet, 640x480 digital camera, audio recording, MP3 player, MPEG4 video playback, internal speaker/microphone, headphone jack
Note:

Model: SL-6000L
Manufacturer: Sharp
Nickname/Series: Tosa
Release Date: early 2004
Availability: World
CPU: 400 MHz Intel XScale PXA255 32-bit CPU
Memory: 64 MB DRAM
Flash (Internal): 128 MB
Other Storage: 8 MB "rescue" mode NOR P2ROM
Slots: Secure Digital/MMC, CompactFlash Type II
Display: 640x480 VGA 16-bit color Sharp "System LCD" semi-transflective backlit CGS TFT LCD touchscreen
Audio Controller: Wolfson WM9712
OS: Metrowerks OpenPDA OS, Qtopia GUI, CVM Java VM
I/O: IrDA, internal faxmodem, USB OTG 1.1 (Slave/Host), I/O Port, 802.11b WiFi
Keyboard: Yes
Features: handwriting recognition, email, digital mobile phone adapter, Internet, 640x480 digital camera, audio recording, MP3 player, MPEG4 video playback, internal speaker/microphone, headphone jack
Note:

Model: SL-6000W
Manufacturer: Sharp
Nickname/Series: Tosa
Release Date: early 2004
Availability: World
CPU: 400 MHz Intel XScale PXA255 32-bit CPU
Memory: 64 MB DRAM
Flash (Internal): 64 MB
Other Storage: 8 MB "rescue" mode NOR P2ROM
Slots: Secure Digital/MMC, CompactFlash Type II
Display: 640x480 VGA 16-bit color Sharp "System LCD" semi-transflective backlit CGS TFT LCD touchscreen
Audio Controller: Wolfson WM9712
OS: Metrowerks OpenPDA OS, Qtopia GUI, CVM Java VM
I/O: IrDA, internal faxmodem, USB OTG 1.1 (Slave/Host), I/O Port, 802.11b WiFi, Bluetooth v1.1
Keyboard: Yes
Features: handwriting recognition, email, digital mobile phone adapter, Internet, 640x480 digital camera, audio recording, MP3 player, MPEG4 video playback, internal speaker/microphone, headphone jack
Note:

Model: SL-C3000
Manufacturer: Sharp
Nickname/Series: Spitz
Release Date: October 2004
Availability: Japan
CPU: 416 MHz Intel XScale PXA270 32-bit CPU
Memory: 64 MB DRAM
Flash (Internal): 16 MB
Other Storage: 4 GB Hitachi MicroDrive
Slots: Secure Digital/MMC (SDIO), CompactFlash Type II
Display: 640x480 VGA 16-bit color Sharp "System LCD" semi-transflective backlit CGS TFT LCD touchscreen
Audio Controller: Wolfson WM8750
OS: Lineo uLinux OS, Qtopia GUI
I/O: IrDA, internal faxmodem, USB OTG 1.1 (Slave/Host), I/O Port, 802.11b WiFi, Bluetooth v1.1
Keyboard: Yes (mini-keyboard)
Features: handwriting recognition, email, digital mobile phone adapter, Internet, 640x480 digital camera, audio recording, MP3 player, MPEG4 video playback, screen rotation, overclockable, internal speaker/microphone, headphone jack
Note:

Model: SL-C1000
Manufacturer: Sharp
Nickname/Series: Akita
Release Date: March 2005
Availability: World
CPU: 416 MHz Intel XScale PXA270 32-bit CPU
Memory: 64 MB DRAM
Flash (Internal): 128 MB
Other Storage: none
Slots: Secure Digital/MMC (possibly SDIO-compatible), CompactFlash Type II
Display: 640x480 VGA 16-bit color Sharp "System LCD" semi-transflective backlit CGS TFT LCD touchscreen
Audio Controller:
OS: Lineo uLinux OS, Qtopia GUI
I/O: IrDA, internal faxmodem, USB OTG 1.1 (Slave/Host), I/O Port, 802.11b WiFi, Bluetooth v1.1
Keyboard: Yes (mini-keyboard)
Features: handwriting recognition, email, digital mobile phone adapter, Internet, 640x480 digital camera, audio recording, MP3 player, MPEG4 video playback, screen rotation, overclockable, internal speaker/microphone, headphone jack
Note:

Model: SL-C3100
Manufacturer: Sharp
Nickname/Series: Borzoi
Release Date: June 2005
Availability: World
CPU: 416 MHz Intel XScale PXA270 32-bit CPU
Memory: 64 MB DRAM
Flash (Internal): 128 MB
Other Storage: 8 MB "rescue" mode NOR P2ROM, 4 GB Hitachi MicroDrive
Slots: Secure Digital/MMC (SDIO), CompactFlash Type II
Display: 640x480 VGA 16-bit color Sharp "System LCD" semi-transflective backlit CGS TFT LCD touchscreen
Audio Controller: Wolfson WM8750
OS: Lineo uLinux OS, Qtopia GUI
I/O: IrDA, internal faxmodem, USB OTG 1.1 (Slave/Host), I/O Port, 802.11b WiFi, Bluetooth v1.1
Keyboard: Yes (mini-keyboard)
Features: handwriting recognition, email, digital mobile phone adapter, Internet, 640x480 digital camera, audio recording, MP3 player, MPEG4 video playback, screen rotation, overclockable, internal speaker/microphone, headphone jack, dictionary, map, electronic library
Note:

Model: SL-C3200
Manufacturer: Sharp
Nickname/Series: Terrier
Release Date: March 17, 2006
Availability: World
CPU: 416 MHz Intel XScale PXA270 32-bit CPU
Memory: 64 MB DRAM
Flash (Internal): 128 MB
Other Storage: 6 GB Hitachi MicroDrive
Slots: Secure Digital/MMC (SDIO) up to 4 GB, CompactFlash Type II
Display: 640x480 VGA 16-bit color Sharp "System LCD" semi-transflective backlit CGS TFT LCD touchscreen
Audio Controller: Wolfson WM8750
OS: Lineo uLinux OS, Qtopia GUI
I/O: IrDA, internal faxmodem, USB OTG 1.1 (Slave/Host), I/O Port, 802.11b WiFi, Bluetooth v1.1
Keyboard: Yes (mini-keyboard)
Features: handwriting recognition, email, digital mobile phone adapter, Internet, 640x480 digital camera, audio recording, MP3 player, MPEG4 video playback, screen rotation, overclockable, Nuance text-to-speech, updated dictionary, TOEIC, internal speaker/microphone, headphone jack
Note:

============================================================================================

RD Series
---------

Model: RD-CMP2000R
Manufacturer: Sharp
Nickname/Series:
Demo/Press Release Date: October 2006
Availability: Korea
CPU: unknown 32-bit CPU
Memory:
Flash (Internal):
Other Storage:
Slots: Secure Digital/MMC up to 4 GB
Display: 320x240 16-bit color LCD touchscreen
Audio Controller:
OS: Linux, unknown GUI
I/O:
Keyboard: Yes
Features: 1280x960 1.33 MP camera, electronic dictionary, e-Book reader, FM radio, music player
Note:

============================================================================================

"WS Series"
-----------

Model: W-ZERO3 WS003SH
Manufacturer: Willcom (mfg. by Sharp)
Nickname/Series:
Release Date: after January 2007
Availability: Japan
CPU: 416 MHz Intel XScale PXA270 32-bit CPU
Memory: 64 MB DRAM
Flash (Internal): 128 MB
Other Storage:
Slots: miniSD, W-SIM
Display: 640x480 VGA 16-bit color Sharp "System LCD" semi-transflective backlit CGS TFT LCD touchscreen
Audio Controller:
OS: Microsoft Windows Mobile 5.0
I/O: USB PC Link, USB Host, 802.11b WiFi
Keyboard: Yes (mini-keyboard)
Features: handwriting recognition, 1280x960 1.33 MP camera, wireless/cellular phone/data features, Internet, email
Note:

Model: W-ZERO3 WS004SH
Manufacturer: Willcom (mfg. by Sharp)
Nickname/Series:
Release Date: after January 2007
Availability: Japan
CPU: 416 MHz Intel XScale PXA270 32-bit CPU
Memory: 64 MB DRAM
Flash (Internal): 256 MB (user area about 197MB)
Other Storage:
Slots: miniSD, W-SIM
Display: 640x480 VGA 16-bit color Sharp "System LCD" semi-transflective backlit CGS TFT LCD touchscreen
Audio Controller:
OS: Microsoft Windows Mobile 5.0
I/O: USB PC Link, USB Host, 802.11b WiFi
Keyboard: Yes (mini-keyboard)
Features: handwriting recognition, 1280x960 1.33 MP camera, wireless/cellular phone/data features, Internet, email, English-Japanese translation dictionary
Note:



*****************************************************************************************************************************************/

#include "emu.h"
#include "cpu/arm7/arm7.h"
#include "machine/locomo.h"
#include "machine/pxa255.h"
#include "machine/sa1110.h"
#include "machine/scoop.h"
#include "machine/timer.h"
#include "machine/ucb1200.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#define SA1110_CLOCK 206000000
#define PXA250_CLOCK 400000000
#define PXA255_CLOCK 400000000
#define PXA270_CLOCK 416000000

namespace {

class zaurus_state : public driver_device
{
public:
	zaurus_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_ram(*this, "ram")
	{ }

protected:
	// driver_device overrides
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	// devices
	required_device<cpu_device> m_maincpu;
	required_shared_ptr<uint32_t> m_ram;
};

class zaurus_sa_state : public zaurus_state
{
public:
	zaurus_sa_state(const machine_config &mconfig, device_type type, const char *tag)
		: zaurus_state(mconfig, type, tag)
		, m_sa_periphs(*this, "sa_periphs")
		, m_locomo(*this, "locomo")
		, m_scoop(*this, "scoop")
		, m_codec(*this, "codec")
	{ }

	void zaurus_sa1110(machine_config &config);

private:
	virtual void device_reset_after_children() override;

	void main_map(address_map &map) ATTR_COLD;

	required_device<sa1110_periphs_device> m_sa_periphs;
	required_device<locomo_device> m_locomo;
	required_device<scoop_device> m_scoop;
	required_device<ucb1200_device> m_codec;
};

class zaurus_pxa_state : public zaurus_state
{
public:
	zaurus_pxa_state(const machine_config &mconfig, device_type type, const char *tag)
		: zaurus_state(mconfig, type, tag)
		, m_pxa_periphs(*this, "pxa_periphs")
		, m_power(*this, "PWR")
	{ }

	void zaurus_pxa_base(machine_config &config);
	void zaurus_pxa250(machine_config &config);
	void zaurus_pxa255(machine_config &config);
	void zaurus_pxa270(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER( system_start );

private:
	void main_map(address_map &map) ATTR_COLD;

	required_device<pxa255_periphs_device> m_pxa_periphs;
	required_ioport m_power;
};

void zaurus_sa_state::main_map(address_map &map)
{
	map(0x00000000, 0x00ffffff).rom().region("firmware", 0);
	map(0x40000000, 0x40001fff).rw(m_locomo, FUNC(locomo_device::read), FUNC(locomo_device::write));
	map(0x40800000, 0x4080002b).rw(m_scoop, FUNC(scoop_device::read), FUNC(scoop_device::write));
	map(0x80000000, 0xbfffffff).m(m_sa_periphs, FUNC(sa1110_periphs_device::map));
	map(0xc0000000, 0xc3ffffff).ram().share("ram");
}

void zaurus_pxa_state::main_map(address_map &map)
{
	map(0x00000000, 0x001fffff).rom().region("firmware", 0);
	map(0x40000000, 0x47ffffff).m(m_pxa_periphs, FUNC(pxa255_periphs_device::map));
	map(0xa0000000, 0xa07fffff).ram().share("ram");
}

void zaurus_sa_state::device_reset_after_children()
{
	m_sa_periphs->gpio_in<1>(1);
	m_sa_periphs->gpio_in<24>(1);
	//m_scoop->gpio_in<2>(1); // DIAG_BOOT1
	//m_scoop->gpio_in<3>(1); // DIAG_BOOT2
}

INPUT_CHANGED_MEMBER( zaurus_pxa_state::system_start )
{
	m_pxa_periphs->gpio_in<10>(BIT(m_power->read(), 0));
}

static INPUT_PORTS_START( zaurus_sa )
INPUT_PORTS_END

static INPUT_PORTS_START( zaurus_pxa )
	PORT_START("PWR")
	PORT_BIT( 0x00000001, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("Start System") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(zaurus_pxa_state::system_start), 0)
INPUT_PORTS_END

void zaurus_state::machine_start()
{
}

void zaurus_state::machine_reset()
{
}

void zaurus_sa_state::zaurus_sa1110(machine_config &config)
{
	SA1110(config, m_maincpu, SA1110_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &zaurus_sa_state::main_map);

	SA1110_PERIPHERALS(config, m_sa_periphs, SA1110_CLOCK, m_maincpu);
	m_sa_periphs->set_codec_tag(m_codec);

	LOCOMO(config, m_locomo);
	SCOOP(config, m_scoop);

	UCB1200(config, m_codec);
	m_codec->adc_in<0>().set_constant(460); // Battery temperature monitor. HACK: Value is arbitrary, taken from Linux
	m_codec->adc_in<1>().set_constant(255); // Battery voltage monitor. HACK: Value is arbitrary, taken from Linux
	m_codec->irq_out().set(m_sa_periphs, FUNC(sa1110_periphs_device::gpio_in<23>));
}

void zaurus_pxa_state::zaurus_pxa250(machine_config &config)
{
	PXA250(config, m_maincpu, PXA250_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &zaurus_pxa_state::main_map);

	PXA255_PERIPHERALS(config, m_pxa_periphs, PXA250_CLOCK, m_maincpu);
}

void zaurus_pxa_state::zaurus_pxa255(machine_config &config)
{
	PXA255(config, m_maincpu, PXA255_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &zaurus_pxa_state::main_map);

	PXA255_PERIPHERALS(config, m_pxa_periphs, PXA255_CLOCK, m_maincpu);
}

void zaurus_pxa_state::zaurus_pxa270(machine_config &config)
{
	PXA270(config, m_maincpu, PXA270_CLOCK);
	m_maincpu->set_addrmap(AS_PROGRAM, &zaurus_pxa_state::main_map);

	PXA255_PERIPHERALS(config, m_pxa_periphs, PXA270_CLOCK, m_maincpu); // TODO: Correct peripherals
}

/***************************************************************************

  Game driver(s)

***************************************************************************/

ROM_START( zsl5500 )
	ROM_REGION32_LE( 0x1000000, "firmware", ROMREGION_ERASE00 )
	ROM_SYSTEM_BIOS( 0, "2.58", "OS Pack 2.58" )
	ROMX_LOAD( "ospack-2.58", 0x0000000, 0x1000000, CRC(31c4d3ef) SHA1(a3b67fb45160bdb990e34dca5c389ed345c000c6), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "3.10", "OS Pack 3.10" )
	ROMX_LOAD( "ospack-3.10", 0x0000000, 0x1000000, CRC(d4b28f84) SHA1(fb7839ccde92f71fd80c4e04718783e684010398), ROM_BIOS(1) )
ROM_END

/* was labeled SL-C500 */
ROM_START( zslc500 )
	ROM_REGION32_LE( 0x200000, "firmware", ROMREGION_ERASE00 )
	ROM_LOAD( "sl-c500 v1.20,zimage.bin", 0x000000, 0x13c000, BAD_DUMP CRC(dc1c259f) SHA1(8150744196a72821ae792462d0381182274c2ce0) )
ROM_END

ROM_START( zsl5600 )
	ROM_REGION32_LE( 0x200000, "firmware", ROMREGION_ERASE00 )
	ROM_LOAD( "zaurus sl-b500 - 5600,zimage.bin", 0x000000, 0x11b6b0, BAD_DUMP CRC(779c70a1) SHA1(26824e3dc563b681f195029f220dfaa405613f9e) )
ROM_END

ROM_START( zslc750 )
	ROM_REGION32_LE( 0x200000, "firmware", ROMREGION_ERASE00 )
	ROM_LOAD( "zaurus sl-c750,zimage.bin", 0x000000, 0x121544, BAD_DUMP CRC(56353f4d) SHA1(8e1fff6e93d560bd6572c5c163bbd81378693f68) )
ROM_END

ROM_START( zslc760 )
	ROM_REGION32_LE( 0x200000, "firmware", ROMREGION_ERASE00 )
	ROM_LOAD( "zaurus sl-c760,zimage.bin", 0x000000, 0x120b44, BAD_DUMP CRC(feedcba3) SHA1(1821ad0fc03a8c3832ad5fe2221c21c1ca277508) )
ROM_END

ROM_START( zslc3000 )
	ROM_REGION32_LE( 0x200000, "firmware", ROMREGION_ERASE00 )
	ROM_LOAD( "openzaurus 3.5.3 - zimage-sharp sl-c3000-20050428091110.bin", 0x000000, 0x12828c, BAD_DUMP CRC(fd94510d) SHA1(901f8154b4228a448f5551f0c9f21c2153e1e3a1) )
ROM_END

ROM_START( zslc1000 )
	ROM_REGION32_LE( 0x200000, "firmware", ROMREGION_ERASE00 )
	ROM_LOAD( "openzaurus 3.5.3 - zimage-sharp sl-c1000-20050427214434.bin", 0x000000, 0x128980, BAD_DUMP  CRC(1e1a9279) SHA1(909ac3f00385eced55822d6a155b79d9d25f43b3) )
ROM_END

} // anonymous namespace

COMP( 2002, zsl5500,  0, 0, zaurus_sa1110, zaurus_sa,  zaurus_sa_state,  empty_init, "Sharp", "Zaurus SL-5500 \"Collie\"",           MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 2002, zslc500,  0, 0, zaurus_pxa250, zaurus_pxa, zaurus_pxa_state, empty_init, "Sharp", "Zaurus SL-C500",                      MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 2002, zsl5600,  0, 0, zaurus_pxa250, zaurus_pxa, zaurus_pxa_state, empty_init, "Sharp", "Zaurus SL-5600 / SL-B500 \"Poodle\"", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 2003, zslc750,  0, 0, zaurus_pxa255, zaurus_pxa, zaurus_pxa_state, empty_init, "Sharp", "Zaurus SL-C750 \"Shepherd\" (Japan)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 2004, zslc760,  0, 0, zaurus_pxa255, zaurus_pxa, zaurus_pxa_state, empty_init, "Sharp", "Zaurus SL-C760 \"Husky\" (Japan)",    MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 200?, zslc3000, 0, 0, zaurus_pxa270, zaurus_pxa, zaurus_pxa_state, empty_init, "Sharp", "Zaurus SL-C3000 \"Spitz\" (Japan)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
COMP( 200?, zslc1000, 0, 0, zaurus_pxa270, zaurus_pxa, zaurus_pxa_state, empty_init, "Sharp", "Zaurus SL-C3000 \"Akita\" (Japan)",   MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



zeebo_qualcomm_adreno130.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:
/******************************************************************************

"Zeebo", or "Genie" or "Longcheer W800", is a video game developed in a partnership
between Qualcomm and Zeebo Inc., the North American arm of the Brazilian company Tectoy.

A large amount of information can be found on
https://www.tripleoxygen.net/wiki/console/zeebo/start

----

Zeebo comes equipped with an ARM11 processor running at 528 MHZ. It is a chip
with low energy consumption and widely used in smartphones, such as the iPhone 3G.

Its graphics core is called Adreno 130, and was developed by Qualcomm with technology
from ATI.

----

System was launched in 2009, but discontinued shortly after.

Games were downloaded to NAND, but once the online store was closed the games could
no longer be downloaded, leaving no way to get them back onto the system after
doing a factory restore or similar.

----

The information from the site above should be transferred into the driver, there
are details such as the memory map, and dumps of RAM from a running system.

Is there a bootstrap ROM, or does this happen transparently to the CPU? how is
memory configuration determined by default etc?

This driver needs a proper owner.

*******************************************************************************/

#include "emu.h"

#include "cpu/arm7/arm7.h"
#include "speaker.h"
#include "screen.h"


namespace {

class zeebo_game_state : public driver_device
{
public:
	zeebo_game_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "arm11")
	{ }

	void zeebo(machine_config &config);

private:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void zeebo_arm11_map(address_map &map) ATTR_COLD;

	void copy_block(int i, int blocksize, int blocksize_stripped, uint8_t* nandrom, int dest);
	void bootstrap();

	required_device<arm11_cpu_device> m_maincpu;
};

void zeebo_game_state::zeebo_arm11_map(address_map &map)
{
	map(0x00000000, 0x03ffffff).ram();
}


void zeebo_game_state::copy_block(int i, int blocksize, int blocksize_stripped, uint8_t* nandrom, int dest)
{
	const int base = i * blocksize;
	address_space& mem = m_maincpu->space(AS_PROGRAM);

	for (int j = 0; j < blocksize_stripped; j++)
	{
		uint8_t data = nandrom[base + j];
		//printf("writing to %08x : %02x", dest + j, data);
		mem.write_byte((dest+j)^3, data);
	}
}

void zeebo_game_state::bootstrap()
{
	uint8_t* rom = memregion("nand")->base();

	int j = 0;
	for (int i = 0xB700; i < 0xB800; i++) // how much is copied?
	{
		copy_block(i, 0x210, 0x200, rom, j * 0x200);
		j++;
	}
}


void zeebo_game_state::machine_start()
{
}

void zeebo_game_state::machine_reset()
{
	bootstrap();
}


static INPUT_PORTS_START( zeebo )
INPUT_PORTS_END


uint32_t zeebo_game_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	return 0;
}

void zeebo_game_state::zeebo(machine_config &config)
{
	ARM11(config, m_maincpu, 528000000); // 528 MHz ARM11 based SoC
	m_maincpu->set_addrmap(AS_PROGRAM, &zeebo_game_state::zeebo_arm11_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(0));
	screen.set_size(640, 480);
	screen.set_visarea(0, 640-1, 0, 480-1);
	screen.set_screen_update(FUNC(zeebo_game_state::screen_update));

	SPEAKER(config, "speaker").front_center();
}

ROM_START( zeebo )
	ROM_REGION32_BE( 0x8400000, "nand", 0 )
	// older versions should be dumped too if possible
	ROM_LOAD( "1.1.2_spare.bin", 0x000000, 0x8400000, CRC(64bd6faa) SHA1(da0db9585d15cf7f1f127e39b0a5fa47f3c13cc0) )
ROM_END

} // anonymous namespace


CONS( 2009, zeebo,      0,       0,      zeebo, zeebo, zeebo_game_state, empty_init, "Zeebo Inc.", "Zeebo (Brazil)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



zexall.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Jonathan Gevaryahu
/******************************************************************************

  Self Contained zexall 'Z80 instruction exerciser' test driver
  Zexall originally written by Frank Cringle for ZX Spectrum
  Modularized Spectrum-independent Zexall binary supplied by Blargg
  Serial interface binary/preloader at 0x0000-0x00FF written by Kevin 'kevtris' Horton

  NOTE: there's a modified version of this driver in src/zexall


  Memory map:

  Ram 0000-FFFF (preloaded with binary)
  Special calls take place for three ram values (this interface was designed by kevtris):
  FFFD - 'ack' - shared ram with output device; z80 reads from here and considers
                 the byte at FFFF read if this value incremented
  FFFE - 'req' - shared ram with output device; z80 writes an incrementing value
                 to FFFE to indicate that there is a byte waiting at FFFF and hence
                 requesting the output device on the other end do something about it,
                 until FFFD is incremented by the output device to acknowledge receipt
  FFFF - 'data' - shared ram with output device; z80 writes the data to be sent to output device here

  One i/o port is used, but left unemulated:
  0001 - bit 0 controls whether interrupt timer is enabled (1) or not (0),
         this is a holdover from a project of kevtris' and can be ignored.

******************************************************************************/

#include "emu.h"

#include "cpu/z80/z80.h"
#include "machine/terminal.h"


namespace {

class zexall_state : public driver_device
{
public:
	zexall_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_terminal(*this, "terminal")
		, m_main_ram(*this, "main_ram")
	{ }

	void zexall(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	uint8_t output_ack_r();
	uint8_t output_req_r();
	uint8_t output_data_r();
	void output_ack_w(uint8_t data);
	void output_req_w(uint8_t data);
	void output_data_w(uint8_t data);

	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<generic_terminal_device> m_terminal;
	required_shared_ptr<uint8_t> m_main_ram;
	uint8_t m_out_data = 0U; // byte written to 0xFFFF
	uint8_t m_out_req = 0U; // byte written to 0xFFFE
	uint8_t m_out_req_last = 0U; // old value at 0xFFFE before the most recent write
	uint8_t m_out_ack = 0U; // byte written to 0xFFFC
};


/******************************************************************************
 Machine Start/Reset
******************************************************************************/

void zexall_state::machine_start()
{
	// register for savestates
	save_item(NAME(m_out_ack));
	save_item(NAME(m_out_req));
	save_item(NAME(m_out_req_last));
	save_item(NAME(m_out_data));
}

void zexall_state::machine_reset()
{
	// zerofill
	m_out_ack = 0;
	m_out_req = 0;
	m_out_req_last = 0;
	m_out_data = 0;

	// program is self-modifying, so need to refresh it on each run
	uint8_t *program = memregion("maincpu")->base();
	memcpy(m_main_ram, program, 0x10000);
}


/******************************************************************************
 I/O Handlers
******************************************************************************/

uint8_t zexall_state::output_ack_r()
{
	// spit out the byte in out_byte if out_req is not equal to out_req_last
	if (m_out_req != m_out_req_last)
	{
		m_terminal->write(m_out_data);
		m_out_req_last = m_out_req;
		m_out_ack++;
	}
	return m_out_ack;
}

void zexall_state::output_ack_w(uint8_t data)
{
	m_out_ack = data;
}

uint8_t zexall_state::output_req_r()
{
	return m_out_req;
}

void zexall_state::output_req_w(uint8_t data)
{
	m_out_req_last = m_out_req;
	m_out_req = data;
}

uint8_t zexall_state::output_data_r()
{
	return m_out_data;
}

void zexall_state::output_data_w(uint8_t data)
{
	m_out_data = data;
}


/******************************************************************************
 Address Maps
******************************************************************************/

void zexall_state::mem_map(address_map &map)
{
	map(0x0000, 0xffff).ram().share("main_ram");
	map(0xfffd, 0xfffd).rw(FUNC(zexall_state::output_ack_r), FUNC(zexall_state::output_ack_w));
	map(0xfffe, 0xfffe).rw(FUNC(zexall_state::output_req_r), FUNC(zexall_state::output_req_w));
	map(0xffff, 0xffff).rw(FUNC(zexall_state::output_data_r), FUNC(zexall_state::output_data_w));
}


/******************************************************************************
 Input Ports
******************************************************************************/

static INPUT_PORTS_START( zexall )
INPUT_PORTS_END


/******************************************************************************
 Machine Drivers
******************************************************************************/

void zexall_state::zexall(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(3'579'545));
	m_maincpu->set_addrmap(AS_PROGRAM, &zexall_state::mem_map);

	/* video hardware */
	GENERIC_TERMINAL(config, m_terminal, 0);
}


/******************************************************************************
 ROM Definitions
******************************************************************************/

ROM_START( zexall )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "interface.bin", 0x0000, 0x0051, CRC(4292a574) SHA1(d3ed6d84e2b64e51598f36b4f290972963e1eb6d) ) // written directly in machine code
	ROM_LOAD( "zexall.bin",    0x0100, 0x2189, CRC(b6f869c3) SHA1(14021f75c1bc9f26688969581065a0efff3af59c) )
ROM_END

} // anonymous namespace


/******************************************************************************
 Drivers
******************************************************************************/

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY                         FULLNAME                            FLAGS
COMP( 2009, zexall, 0,      0,      zexall,  zexall, zexall_state, empty_init, "Frank Cringle / Kevin Horton", "Zexall (FPGA Z80 test interface)", MACHINE_SUPPORTS_SAVE )



zfirenze.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:

/*
  Skeleton driver for Z80-based Necta/Zanussi vending machines.

  Zanussi / Necta Firenze vending machine:

   MAIN PCB
   __________________________________________________________________________________________
  |  ······  ···········  ·········  ·········      ······  :::::::::::::  :::::::::::::    |
  |          __________    ___      __________  __________  ___  ___  ___  ___  ___  ___    |
  |         |__DS1229_|  SN76176BP |74HC374AN| |TD62083AP| |__| |__| |__| |__| |__| |__| <- 6 x TLP504A
  |                        ___       ___  ___  ___  ___    __________   __________   ___   ·|
  |                      SN76176BP  |<- 4 x TLP504A ->|   |_GET_GAL_|  |ULN2062B_| TLP504A ·|
  |                 __________      __________  __________  __________  __________   ___    |
  |                |CD74HCT86E     |74HC374AN| |74HCT244B1 |MM74HCT138N CD74HC125E TLP504A ·|
  |                 __________                                                             ·|
  |                |MM74HC163N        SPEAKER   LED                  LED                   ·|
  |                 __________                  _____  __________  __________  ____  ____  ·|
  |   ____         |SCAN-GAL_|                 DIPSx4 |DS1238-10| |M74HC04B1| 93C66N 93C66N |
  | MIC2940A        __________             Xtal                         ______________     ·|
  |                |_________|            ??? MHz                      |M5M5256CP-70LL      |
  |                 __________                          _________      |_____________|      |
  |                |M74HC32B1|   __________            |TOSHIBA  |                          |
  | __________      __________  |_D48-GAL_|            |TMPZ84C015BF-8  ··············      |
  ||MM74HCT8N|     |_KEY-GAL_|   __________            |         |           E1             |
  |                             |OKI 6242B             |         |      ··············      |
  |  ············· Osc                                 |_________|                          |
  |______________??? MHz____________________________________________________________________|

   SCHEDA EXPANSIONE MEMORIA EPROM 6735-354-00 (connected to the main PCB EPROM socket E1)
   _____________________________________________
  |        ______________   ______________     |
  |       | EPROM A     |  | EPROM C     |     |
  |       |_____________|  |_____________|     |
  |  __________   __________                   |
  | |M74HC02B1|  |PC74HC08T|   ··············  |
  |  __________   __________        J1         |
  | |M74HC32B1|  |PC74HC08T|   ··············  |
  |____________________________________________|

*/

#include "emu.h"
#include "cpu/z80/tmpz84c015.h"
#include "machine/msm6242.h"

namespace {

class zfirenze_state : public driver_device
{
public:
	zfirenze_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu")
	{ }

	void zfirenze(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};

void zfirenze_state::machine_start()
{
}

void zfirenze_state::machine_reset()
{
}

static INPUT_PORTS_START( zfirenze )
	PORT_START("DSW0")
	PORT_DIPUNKNOWN_DIPLOC(0x01, 0x01, "SW0:1")
	PORT_DIPUNKNOWN_DIPLOC(0x02, 0x02, "SW0:2")
	PORT_DIPUNKNOWN_DIPLOC(0x04, 0x04, "SW0:3")
	PORT_DIPUNKNOWN_DIPLOC(0x08, 0x08, "SW0:4")
INPUT_PORTS_END

void zfirenze_state::zfirenze(machine_config &config)
{
	TMPZ84C015(config, m_maincpu, XTAL(8'000'000)); // Toshiba TMPZ84C015BF-8, unknown clock

	MSM6242(config, "rtc", XTAL(32'768'000)); // OKI M6242B, unknown clock
}

ROM_START( zfirenze )
	ROM_REGION( 0x20000, "maincpu", 0 )
	ROM_LOAD( "951200-1_firenze_c6m_a_8e00_20-01-95.u6", 0x00000, 0x10000, CRC(1ca85d3d) SHA1(4cb30a83b8c20eac7b31dd4fe5c79dfca6815dc8) )
	ROM_LOAD( "951200-2_firenze_c6m_c_8e00_20-01-95.u5", 0x10000, 0x10000, CRC(0107bb35) SHA1(1317d1055b9f9d05a4103612779059e84c4ac16e) )

	ROM_REGION( 0x117, "plds", 0 )
	ROM_LOAD( "d48_gal_palce16v8h-25.u15",  0x000, 0x117, CRC(7467d098) SHA1(2085cccd4049ba07a1fafc1f16680e1aa8c8bb96) )
	ROM_LOAD( "get_gal_palce16v8h-25.u9",   0x000, 0x117, CRC(b4d4f0e1) SHA1(6a741e08082c46759a5eda914a91f7d18ef9128f) )
	ROM_LOAD( "key_gal_palce16v8h-25.u25",  0x000, 0x117, CRC(b52825d1) SHA1(b820c73929b03320378a4625ee4451bd81e9c5aa) )
	ROM_LOAD( "scan_gal_palce16v8h-25.u28", 0x000, 0x117, CRC(2dbb9247) SHA1(d80085bd5e9d17231a24c83797751a15b0440462) )
ROM_END


} // anonymous namespace


//    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT      CLASS           INIT        COMPANY            FULLNAME                     FLAGS
SYST( 1995, zfirenze,  0,      0,      zfirenze,  zfirenze,  zfirenze_state, empty_init, "Zanussi / Necta", "Firenze (vending machine)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



zircon2.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Berger
/*******************************************************************************

Novag Zircon II (model 9403)

Hardware notes (Aquamarine Risc II):
- PCB label: 100186 B
- Hitachi H8/325 MCU, 26.601712MHz XTAL
- 2*4-digit LCD panels (same as Mentor 16)
- piezo, 16+2 LEDs, 8*8 chessboard buttons

H8/325 B84 MCU is used in:
- Novag Zircon II
- Novag Jade II
- Novag Chess Wizard IQ V (Zircon II rebranded by Mitco Industries)
- Novag Aquamarine Risc II 26.6MHz (Siglo XXI version too)

Versions manufactured after around 1997 have a 16MHz H8/3214 (100186 C PCB), it's
a bit faster than the ~26.6MHz H8/325 due to the latter /2 divider. Newer revisions
of Chess Wizard IQ V and Aquamarine Risc II have it, presumably others too.

TODO:
- dump/add H8/3214 version
- is the first Novag Zircon/Jade on similar hardware?
- it does a cold boot at every reset, so nvram won't work properly unless MAME
  adds some kind of auxillary autosave state feature at power-off

*******************************************************************************/

#include "emu.h"

#include "cpu/h8/h8325.h"
#include "machine/sensorboard.h"
#include "sound/dac.h"
#include "video/pwm.h"

#include "speaker.h"

// internal artwork
#include "novag_zircon2.lh"


namespace {

class zircon2_state : public driver_device
{
public:
	zircon2_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_board(*this, "board"),
		m_led_pwm(*this, "led_pwm"),
		m_lcd_pwm(*this, "lcd_pwm"),
		m_dac(*this, "dac"),
		m_inputs(*this, "IN.%u", 0),
		m_out_lcd(*this, "s%u.%u", 0U, 0U),
		m_out_digit(*this, "digit%u", 0U)
	{ }

	void zircon2(machine_config &config);

	DECLARE_INPUT_CHANGED_MEMBER(power_switch);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD { set_power(true); }

private:
	// devices/pointers
	required_device<h8325_device> m_maincpu;
	required_device<sensorboard_device> m_board;
	required_device<pwm_display_device> m_led_pwm;
	required_device<pwm_display_device> m_lcd_pwm;
	required_device<dac_2bit_ones_complement_device> m_dac;
	required_ioport_array<2> m_inputs;
	output_finder<8, 8> m_out_lcd;
	output_finder<8> m_out_digit;

	bool m_power = false;
	u8 m_inp_mux = 0;
	u16 m_lcd_segs = 0;
	u8 m_lcd_com = 0;

	// I/O handlers
	void standby(int state);
	void set_power(bool power);
	u8 power_r();

	void lcd_pwm_raw_w(offs_t offset, u8 data);
	void lcd_pwm_digit_w(offs_t offset, u64 data);
	void update_lcd();
	template <int N> void lcd_segs_w(u8 data);
	void lcd_com_w(u8 data);

	void p1_w(u8 data);
	u8 p4_r();
	u8 p5_r();
	void p6_w(u8 data);
};

void zircon2_state::machine_start()
{
	m_out_lcd.resolve();
	m_out_digit.resolve();

	// register for savestates
	save_item(NAME(m_power));
	save_item(NAME(m_inp_mux));
	save_item(NAME(m_lcd_segs));
	save_item(NAME(m_lcd_com));
}



/*******************************************************************************
    I/O
*******************************************************************************/

// power

void zircon2_state::standby(int state)
{
	// clear display
	if (state)
	{
		m_lcd_pwm->clear();
		m_led_pwm->clear();
	}
}

void zircon2_state::set_power(bool power)
{
	// power switch is tied to IRQ0
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, power ? ASSERT_LINE : CLEAR_LINE);
	m_power = power;
}

INPUT_CHANGED_MEMBER(zircon2_state::power_switch)
{
	if (newval)
		set_power(bool(param));
}

u8 zircon2_state::power_r()
{
	// P64: power switch (IRQ0)
	return m_power ? 0xef : 0xff;
}


// LCD

void zircon2_state::lcd_pwm_raw_w(offs_t offset, u8 data)
{
	m_out_lcd[offset & 0x3f][offset >> 6] = data;
}

void zircon2_state::lcd_pwm_digit_w(offs_t offset, u64 data)
{
	m_out_digit[offset] = data;
}

void zircon2_state::update_lcd()
{
	for (int digit = 0; digit < 8; digit++)
	{
		u8 data = 0;
		for (int i = 0; i < 4; i++)
		{
			// 4 commons per digit, 2 output pins per common (analog voltage level)
			const u8 com = population_count_32(m_lcd_com >> (i * 2) & 3);
			const u16 segs = (com == 0) ? m_lcd_segs : (com == 2) ? ~m_lcd_segs : 0;
			data = data << 2 | (segs >> (digit * 2) & 3);
		}

		m_lcd_pwm->write_row(digit, bitswap<8>(data,1,4,6,2,0,3,5,7));
	}
}

template <int N>
void zircon2_state::lcd_segs_w(u8 data)
{
	// P3x, P7x: LCD segments
	const u8 shift = 8 * N;
	m_lcd_segs = (m_lcd_segs & ~(0xff << shift)) | (data << shift);
	update_lcd();
}

void zircon2_state::lcd_com_w(u8 data)
{
	// P20-P27: LCD commons
	m_lcd_com = data;
	update_lcd();
}


// misc

void zircon2_state::p1_w(u8 data)
{
	// P10-P17: input mux, LED data
	m_inp_mux = ~data;
	m_led_pwm->write_mx(~data);
}

u8 zircon2_state::p4_r()
{
	u8 data = 0;

	// P40-P47: read chessboard
	for (int i = 0; i < 8; i++)
		if (BIT(m_inp_mux, i))
			data |= m_board->read_rank(i);

	return ~data;
}

u8 zircon2_state::p5_r()
{
	u8 data = 0;

	// P50,P51: read buttons
	for (int i = 0; i < 2; i++)
		if (m_inp_mux & m_inputs[i]->read())
			data |= 1 << i;

	return ~data;
}

void zircon2_state::p6_w(u8 data)
{
	// P61,P62: speaker out
	m_dac->write(data >> 1 & 3);

	// P63,P65,P66: LED select
	m_led_pwm->write_my(bitswap<3>(~data,3,5,6));
}



/*******************************************************************************
    Input Ports
*******************************************************************************/

static INPUT_PORTS_START( zircon2 )
	PORT_START("IN.0")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Change Color")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Take Back / Next Best")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("King / Easy")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Queen / Random")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Rook / Restore")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Bishop / Info")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Knight / Sound")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("Pawn / Referee")

	PORT_START("IN.1")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Go")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Trace Forward / Autoplay")
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Verify / Set Up")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Training")
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Hint")
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Set Level")
	PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Clear / Clear Board")
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("New Game")

	PORT_START("POWER")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_ON) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(zircon2_state::power_switch), 1)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(zircon2_state::power_switch), 0)
INPUT_PORTS_END



/*******************************************************************************
    Machine Configs
*******************************************************************************/

void zircon2_state::zircon2(machine_config &config)
{
	// basic machine hardware
	H8325(config, m_maincpu, 26.601712_MHz_XTAL);
	m_maincpu->nvram_enable_backup(true);
	m_maincpu->standby_cb().set(m_maincpu, FUNC(h8325_device::nvram_set_battery));
	m_maincpu->standby_cb().append(FUNC(zircon2_state::standby));
	m_maincpu->write_port1().set(FUNC(zircon2_state::p1_w));
	m_maincpu->write_port2().set(FUNC(zircon2_state::lcd_com_w));
	m_maincpu->write_port3().set(FUNC(zircon2_state::lcd_segs_w<0>));
	m_maincpu->read_port4().set(FUNC(zircon2_state::p4_r));
	m_maincpu->read_port5().set(FUNC(zircon2_state::p5_r));
	m_maincpu->read_port6().set(FUNC(zircon2_state::power_r));
	m_maincpu->write_port6().set(FUNC(zircon2_state::p6_w));
	m_maincpu->write_port7().set(FUNC(zircon2_state::lcd_segs_w<1>));

	SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
	m_board->init_cb().set(m_board, FUNC(sensorboard_device::preset_chess));
	m_board->set_delay(attotime::from_msec(150));
	//m_board->set_nvram_enable(true);

	// video hardware
	PWM_DISPLAY(config, m_lcd_pwm).set_size(8, 8);
	m_lcd_pwm->set_segmask(0xff, 0xff);
	m_lcd_pwm->output_x().set(FUNC(zircon2_state::lcd_pwm_raw_w));
	m_lcd_pwm->output_digit().set(FUNC(zircon2_state::lcd_pwm_digit_w));

	PWM_DISPLAY(config, m_led_pwm).set_size(3, 8);
	config.set_default_layout(layout_novag_zircon2);

	// sound hardware
	SPEAKER(config, "speaker").front_center();
	DAC_2BIT_ONES_COMPLEMENT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.125);
}



/*******************************************************************************
    ROM Definitions
*******************************************************************************/

ROM_START( zircon2 )
	ROM_REGION16_BE( 0x8000, "maincpu", 0 )
	ROM_LOAD("novag_9403-010057-6433258b84f", 0x0000, 0x8000, CRC(bb0e817d) SHA1(328b3e01b4bb52400bcd5111ce674308b65f5b86) )
ROM_END

} // anonymous namespace



/*******************************************************************************
    Drivers
*******************************************************************************/

//    YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT    CLASS          INIT        COMPANY, FULLNAME, FLAGS
SYST( 1995, zircon2, 0,      0,      zircon2, zircon2, zircon2_state, empty_init, "Novag Industries / Intelligent Heuristic Programming", "Zircon II", MACHINE_SUPPORTS_SAVE )



zms8085.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

Skeleton driver for Zentec's 8085-based Zephyr/ZMS-series terminals.

This represents the second generation of display terminals by Zentec Corporation. The
first, which included the ZMS-50, ZMS-70 & ZMS-90, had an 8080 CPU and different video
timings.

U.S. Patent No. 4,203,107 looks like the best available description of the Zephyr video
hardware, since no schematics or manuals have been found.

************************************************************************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "bus/rs232/rs232.h"
#include "machine/ay31015.h"
#include "machine/clock.h"
#include "screen.h"


namespace {

class zms8085_state : public driver_device
{
public:
	zms8085_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_uart(*this, "uart")
		, m_uart_clock(*this, "uart_clock")
		, m_rs232(*this, "rs232")
		, m_screen(*this, "screen")
		, m_mainram(*this, "mainram")
		, m_chargen(*this, "chargen")
		, m_keys(*this, "KEY%X", 0U)
	{ }

	void zephyr(machine_config &config);

private:
	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	u8 special_r();
	u8 uart_status_r();
	void key_l_w(u8 data);
	void key_h_w(u8 data);
	u8 key_r();

	virtual void machine_start() override ATTR_COLD;

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_device<ay51013_device> m_uart;
	required_device<clock_device> m_uart_clock;
	required_device<rs232_port_device> m_rs232;
	required_device<screen_device> m_screen;
	required_shared_ptr<u8> m_mainram;
	required_region_ptr<u8> m_chargen;
	required_ioport_array<14> m_keys;

	u16 m_key_row = 0U;
};


u32 zms8085_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	u8 cursor_y = m_mainram[0x000];
	u8 cursor_x = m_mainram[0x001];
	u8 scroll = m_mainram[0x005];

	u16 pos = 0x030 + (scroll & 0x1f) * 0x050;
	u8 row = scroll & 0x1f;

	for (unsigned y = 0; y < 250; y++)
	{
		for (unsigned ch = 0; ch < 80; ch++)
		{
			unsigned x = ch * 10;
			u8 code = m_mainram[(ch + pos) & 0xfff];
			u16 data = (m_chargen[(code & 0x7f) * 16 + (y + 9) % 10] << 2) | 0x003;
			if (ch == cursor_x && row == cursor_y)
				data ^= 0x3ff;
			for (int bit = 10; bit > 0; bit--, data <<= 1)
				bitmap.pix(y, x++) = BIT(data, 9) ? rgb_t::black() : rgb_t::white();
		}

		if ((y % 10) == 9)
		{
			row++;
			if (y == 239)
			{
				pos = 0x030;
				cursor_y = 0xff;
			}
			else
			{
				pos += 80;
				if (pos >= 0x800)
				{
					pos = 0x080;
					row -= 24;
				}
			}
		}
	}
	return 0;
}


u8 zms8085_state::special_r()
{
	return 0x40;
}

u8 zms8085_state::uart_status_r()
{
	return m_uart->tbmt_r() | (m_uart->dav_r() << 1);
}

void zms8085_state::key_l_w(u8 data)
{
	m_key_row = (m_key_row & 0x3f00) | data;
}

void zms8085_state::key_h_w(u8 data)
{
	m_key_row = (m_key_row & 0x00ff) | ((data & 0x3f) << 8);
}

u8 zms8085_state::key_r()
{
	if (!machine().side_effects_disabled())
		m_maincpu->set_input_line(I8085_RST55_LINE, CLEAR_LINE);

	u8 result = 0xff;
	for (int n = 0; n < 14; n++)
		if (BIT(m_key_row, n))
			result &= m_keys[n]->read();

	return result;
}


void zms8085_state::mem_map(address_map &map)
{
	map(0x0000, 0x0fff).rom().region("maincpu", 0).nopw();
	map(0x1000, 0x1fff).ram().share("mainram");
}

void zms8085_state::io_map(address_map &map)
{
	map(0x61, 0x61).r(FUNC(zms8085_state::special_r)).w(m_uart, FUNC(ay51013_device::transmit));
	map(0x62, 0x62).r(m_uart, FUNC(ay51013_device::receive));
	map(0x63, 0x63).r(FUNC(zms8085_state::uart_status_r));
	map(0x64, 0x64).w(FUNC(zms8085_state::key_l_w));
	map(0x65, 0x65).w(FUNC(zms8085_state::key_h_w));
	map(0x66, 0x66).r(FUNC(zms8085_state::key_r));
	map(0x67, 0x67).portr("MODIFIERS");
	map(0x68, 0x68).nopw();
}


static INPUT_PORTS_START(zephyr)
	PORT_START("KEY0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('g') PORT_CHAR('G') PORT_CODE(KEYCODE_G)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('t') PORT_CHAR('T') PORT_CODE(KEYCODE_T)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR('^') PORT_CODE(KEYCODE_6)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('=') PORT_CHAR('+') PORT_CODE(KEYCODE_EQUALS)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('`') PORT_CHAR('~') PORT_CODE(KEYCODE_TILDE) // to right of right shift
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_M)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KEY1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('j') PORT_CHAR('J') PORT_CODE(KEYCODE_J)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('u') PORT_CHAR('U') PORT_CODE(KEYCODE_U)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('8') PORT_CHAR('*') PORT_CODE(KEYCODE_8)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('{') PORT_CHAR('}') PORT_CODE(KEYCODE_BACKSLASH) // to right of Back Space
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) PORT_CODE(KEYCODE_2_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('.') PORT_CHAR('>') PORT_CODE(KEYCODE_STOP)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD)) PORT_CODE(KEYCODE_7_PAD)

	PORT_START("KEY2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('l') PORT_CHAR('L') PORT_CODE(KEYCODE_L)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('o') PORT_CHAR('O') PORT_CODE(KEYCODE_O)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0') PORT_CHAR(')') PORT_CODE(KEYCODE_0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_CODE(KEYCODE_ENTER_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('[') PORT_CHAR(']') PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD)) PORT_CODE(KEYCODE_9_PAD)

	PORT_START("KEY3")
	PORT_BIT(0x03, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('e') PORT_CHAR('E') PORT_CODE(KEYCODE_E)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR('$') PORT_CODE(KEYCODE_4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD)) PORT_CODE(KEYCODE_PLUS_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('b') PORT_CHAR('B') PORT_CODE(KEYCODE_B)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CODE(KEYCODE_DOWN)

	PORT_START("KEY4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('f') PORT_CHAR('F') PORT_CODE(KEYCODE_F)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('r') PORT_CHAR('R') PORT_CODE(KEYCODE_R)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) PORT_CODE(KEYCODE_MINUS_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('n') PORT_CHAR('N') PORT_CODE(KEYCODE_N)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KEY5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHAR(':') PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('p') PORT_CHAR('P') PORT_CODE(KEYCODE_P)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('-') PORT_CHAR('_') PORT_CODE(KEYCODE_MINUS)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD)) PORT_CODE(KEYCODE_DEL_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\'') PORT_CHAR('"') PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\\') PORT_CHAR('|') PORT_CODE(KEYCODE_CLOSEBRACE)

	PORT_START("KEY6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('k') PORT_CHAR('K') PORT_CODE(KEYCODE_K)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('i') PORT_CHAR('I') PORT_CODE(KEYCODE_I)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('9') PORT_CHAR('(') PORT_CODE(KEYCODE_9)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD)) PORT_CODE(KEYCODE_3_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('/') PORT_CHAR('?') PORT_CODE(KEYCODE_SLASH)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) PORT_CODE(KEYCODE_8_PAD)

	PORT_START("KEY7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('h') PORT_CHAR('H') PORT_CODE(KEYCODE_H)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('y') PORT_CHAR('Y') PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR('&') PORT_CODE(KEYCODE_7)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Back Space") PORT_CHAR(0x08) PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) PORT_CODE(KEYCODE_1_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',') PORT_CHAR('<') PORT_CODE(KEYCODE_COMMA)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KEY8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('d') PORT_CHAR('D') PORT_CODE(KEYCODE_D)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('w') PORT_CHAR('W') PORT_CODE(KEYCODE_W)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD)) PORT_CODE(KEYCODE_6_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('v') PORT_CHAR('V') PORT_CODE(KEYCODE_V)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN)

	PORT_START("KEY9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('s') PORT_CHAR('S') PORT_CODE(KEYCODE_S)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('q') PORT_CHAR('Q') PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR('@') PORT_CODE(KEYCODE_2)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD)) PORT_CODE(KEYCODE_5_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('c') PORT_CHAR('C') PORT_CODE(KEYCODE_C)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Line Feed") // to right of backslash

	PORT_START("KEYA")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('a') PORT_CHAR('A') PORT_CODE(KEYCODE_A)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(0x09) PORT_CODE(KEYCODE_TAB)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CODE(KEYCODE_F2)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) PORT_CODE(KEYCODE_4_PAD)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('x') PORT_CHAR('X') PORT_CODE(KEYCODE_X)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CHAR(0x0d) PORT_CODE(KEYCODE_ENTER)

	PORT_START("KEYB")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc  Del") PORT_CHAR(0x1b) PORT_CHAR(0x7f) PORT_CODE(KEYCODE_LALT) // to left of Caps Lock
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNKNOWN)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CODE(KEYCODE_F1)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("New Line/Page") // to right of Return?
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('z') PORT_CHAR('Z') PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(' ') PORT_CODE(KEYCODE_SPACE)

	PORT_START("KEYC")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD)) PORT_CODE(KEYCODE_0_PAD)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F14)) PORT_CODE(KEYCODE_F14)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F13)) PORT_CODE(KEYCODE_F13)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F12)) PORT_CODE(KEYCODE_F12)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F11)) PORT_CODE(KEYCODE_F11)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F15)) PORT_CODE(KEYCODE_F15)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F16)) PORT_CODE(KEYCODE_F16)

	PORT_START("KEYD")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F10)) PORT_CODE(KEYCODE_F10)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_CODE(KEYCODE_F6)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CODE(KEYCODE_F5)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_CODE(KEYCODE_F4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CODE(KEYCODE_F3)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F9)) PORT_CODE(KEYCODE_F9)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_CODE(KEYCODE_F7)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_CODE(KEYCODE_F8)

	PORT_START("MODIFIERS")
	PORT_BIT(0x1f, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CHAR(UCHAR_SHIFT_2) PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CHAR(UCHAR_SHIFT_1) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
INPUT_PORTS_END


void zms8085_state::machine_start()
{
	m_uart->write_swe(0);

	// most of these settings should be configurable through switches
	m_uart->write_nb1(1);
	m_uart->write_nb2(1);
	m_uart->write_np(1);
	m_uart->write_eps(1);
	m_uart->write_tsb(0);
	m_uart->write_cs(1);

	m_key_row = 0;
	save_item(NAME(m_key_row));
}

void zms8085_state::zephyr(machine_config &config)
{
	I8085A(config, m_maincpu, 15.582_MHz_XTAL / 2); // divider not verified
	m_maincpu->set_addrmap(AS_PROGRAM, &zms8085_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &zms8085_state::io_map);

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(15.582_MHz_XTAL, 980, 0, 800, 265, 0, 250);
	screen.set_screen_update(FUNC(zms8085_state::screen_update));
	screen.screen_vblank().set_inputline(m_maincpu, I8085_RST55_LINE, ASSERT_LINE);

	AY51013(config, m_uart); // SMC COM2017
	m_uart->read_si_callback().set("rs232", FUNC(rs232_port_device::rxd_r));
	m_uart->write_so_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	m_uart->write_dav_callback().set_inputline("maincpu", I8085_RST65_LINE);
	m_uart->set_auto_rdav(true);

	CLOCK(config, m_uart_clock, 153600); // should actually be configurable somehow
	m_uart_clock->signal_handler().set(m_uart, FUNC(ay51013_device::write_tcp));
	m_uart_clock->signal_handler().append(m_uart, FUNC(ay51013_device::write_rcp));

	RS232_PORT(config, "rs232", default_rs232_devices, nullptr);
}

/**************************************************************************************************************

Zentec Zephyr (Model 00-441-01).
Chips: COM2017, i8085A, 2x unreadable (40-pin AMI DIP), Beeper
Crystal: 15.582000

***************************************************************************************************************/

ROM_START( zephyr )
	ROM_REGION(0x1000, "maincpu", 0)
	ROM_SYSTEM_BIOS(0, "main", "Main program" )
	ROMX_LOAD( "23-067-03b.bin",  0x0000, 0x0800, CRC(29cfa003) SHA1(9de7a8402173a2c448e54ee433ba3050db7b70bb), ROM_BIOS(0) )
	ROMX_LOAD( "23-067-004b.bin", 0x0800, 0x0800, CRC(37741104) SHA1(52b9998e0a8d4949e0dc7c3349b3681e13345061), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "test", "Test program" )
	ROMX_LOAD( "23-006-32c.bin",  0x0000, 0x0800, CRC(0a3a5447) SHA1(a8c25730a1d7e5b9c86e0d504afc923e931f9025), ROM_BIOS(1) )

	ROM_REGION(0x0800, "chargen", 0)
	ROM_LOAD( "23-066-02a.bin",  0x0000, 0x0800, CRC(d5650b6c) SHA1(e6333e59018d9904f12abb270db4ba28aeff1995) )
ROM_END

} // anonymous namespace


COMP( 1979, zephyr, 0, 0, zephyr, zephyr, zms8085_state, empty_init, "Zentec", "Zephyr (00-441-01)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



zoomer.cpp
<---------------------------------------------------------------------->
// license: BSD-3-Clause
// copyright-holders: Devin Acker

/*
    Casio/Tandy "Zoomer" PDA (1993)

    This early pen-based PDA was created jointly by Casio (who created the hardware and BIOS)
    and Tandy, with the built-in software provided by GeoWorks and Palm Computing.
    It was sold by both Casio (as the Z-7000) and Tandy (as the Z-PDA), as well as AST Research
    as the GRiDPad 2390.

    Main board:
    LSI101: Casio/NEC uPD95130GD (V20-based CPU)
    LSI102: Casio/Fujitsu MBCG25173-5104 (I/O controller)
    LSI201: Toshiba TC35083 (10-bit ADC for touch screen)
    LSI401: Analog Devices MAX223 (RS232 interface)
    LSI601: NEC uPD65043GF-U01 (sound chip, SN76489-like with PCM streaming)

    Memory daughterboard:
    LSI1, LSI2: Fujitsu MB838200AL (8Mbit mask ROM, 16.4mm TSOP)
    LSI3, LSI4: Fujitsu MB838200AL (8Mbit mask ROM, 16.4mm TSOP, reverse pinout)
    LSI5, LSI6: Hitachi HM65V8512 (4Mbit SRAM)

    TODO:
        - PCMCIA (requires 8-bit SRAM or flash cards with specific attribute memory contents)
          See other TODOs for where/how it should be hooked up, including the card lock switch
        - Serial and infrared ports
        - Make suspended unit state persist after pressing the power button and then quitting MAME?
          (ports 70, 90-af and c0-c7 must be saved as nvram for this to work)
*/

#include "emu.h"

#include "zoomer_rtc.h"

#include "cpu/nec/nec.h"
#include "machine/bankdev.h"
#include "machine/clock.h"
#include "machine/nvram.h"
#include "sound/upd65043gfu01.h"

#include "crsshair.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"

#include "zoomer.lh"

namespace {

class zoomer_state : public driver_device
{
public:
	zoomer_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_bank_2000(*this, "bank_2000_%u", 0),
		m_bank_a000(*this, "bank_a000_%u", 0),
		m_wp_view{
				{ *this, "wp_view0" }, { *this, "wp_view1" }, { *this, "wp_view2" }, { *this, "wp_view3" },
				{ *this, "wp_view4" }, { *this, "wp_view5" }, { *this, "wp_view6" }, { *this, "wp_view7" },
				{ *this, "wp_view8" }, { *this, "wp_view9" }, { *this, "wp_view10" }, { *this, "wp_view11" },
				{ *this, "wp_view12" }, { *this, "wp_view13" }, { *this, "wp_view14" }, {*this, "wp_view15" } },
		m_rtc(*this, "rtc"),
		m_psg(*this, "psg"),
		m_nvram(*this, "nvram", 0x100000, endianness_t::little),
		m_pen(*this, "PEN"),
		m_pen_x(*this, "PEN_X"),
		m_pen_y(*this, "PEN_Y")
	{ }

	void zoomer(machine_config &config) ATTR_COLD;

	template <int Num>
	void irq_set(int state);

	void power_w(int state);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	static inline constexpr unsigned TIMER_RATE = (32768 / 8);

	void maincpu_map(address_map &map) ATTR_COLD;
	void maincpu_ems_map(address_map &map) ATTR_COLD;
	void maincpu_io_map(address_map &map) ATTR_COLD;

	u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void apo_w(u8 data);

	u8 ems_2000_bank_r(offs_t offset);
	void ems_2000_bank_w(offs_t offset, u8 data);
	u8 ems_a000_bank_r(offs_t offset);
	void ems_a000_bank_w(offs_t offset, u8 data);

	template <int Num>
	TIMER_CALLBACK_MEMBER(timer_irq) { irq_set<Num>(1); }

	u16 timer_count(u8 timer) const;
	u8 timer_r(offs_t offset);
	void timer_w(offs_t offset, u8 data);

	u8 irq_status_r(offs_t offset);
	void irq_ack_w(offs_t offset, u8 data);

	void update_irq();

	u16 pen_x_scaled();
	u16 pen_y_scaled();
	u8 pen_value_r(offs_t offset);
	void pen_select_w(u8 data);

	u8 lcd_ctrl_r(offs_t offset);
	void lcd_ctrl_w(offs_t offset, u8 data);

	u8 lcdram_latch_r();
	void lcdram_w(offs_t offset, u8 data);

	required_device<v20_device> m_maincpu;
	required_device_array<address_map_bank_device, 4> m_bank_2000;  // 128kb windows (banked w/ 64kb granularity) at 20000-9ffff
	required_device_array<address_map_bank_device, 16> m_bank_a000; // 16kb windows at a0000-dffff
	memory_view m_wp_view[16]; // view used for enabling write-protect trap in a0000-dffff area
	required_device<zoomer_rtc_device> m_rtc;
	required_device<upd65043gfu01_device> m_psg;

	memory_share_creator<u8> m_nvram;

	required_ioport m_pen, m_pen_x, m_pen_y;

	u8 m_power;

	emu_timer *m_timer[3];
	u16 m_timer_rate[3];

	u16 m_irq_status;
	u16 m_pending_irq;

	u16 m_bank_2000_num[4];
	u16 m_bank_a000_num[16];

	u8 m_pen_select;

	u8 m_lcdram_latch;
	u16 m_lcd_ctrl;
};

enum
{
	IRQ_TIMER0,
	IRQ_TIMER1,
	IRQ_TIMER2,
	IRQ_KEYPAD,
	IRQ_SERIAL, // TODO
	IRQ_RTC_ALARM,
	IRQ_RTC_TICK,
	IRQ_PEN,
	IRQ_PCMCIA, // TODO
	IRQ_SOUND,
	IRQ_POWER,
	IRQ_PCMCIA_LOCK,   // TODO: PCMCIA card lock switch
	IRQ_PCMCIA_UNLOCK, // TODO: PCMCIA card lock switch
	IRQ_EMS
};


//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

void zoomer_state::maincpu_map(address_map &map)
{
	for (int i = 0; i < 4; i++)
	{
		const offs_t start = 0x20000 + (i << 17);
		map(start, start | 0x1ffff).rw(m_bank_2000[i], FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
	}
	for (int i = 0; i < 16; i++)
	{
		const offs_t start = 0xa0000 + (i << 14);
		map(start, start | 0x3fff).rw(m_bank_a000[i], FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8));
		map(start, start | 0x3fff).view(m_wp_view[i]);
		m_wp_view[i][0](start, start | 0x3fff).lw8(NAME([this](u8 data) { irq_set<IRQ_EMS>(1); }));
	}

	map(0xd8000, 0xdffff).w(FUNC(zoomer_state::lcdram_w));
	map(0xe0000, 0xfffff).rom().region("maincpu", 0);
}

void zoomer_state::maincpu_ems_map(address_map &map)
{
	map(0x0000000, 0x00fffff).ram().share("nvram");
	map(0x0400000, 0x07fffff).rom().region("maincpu", 0);
	//map(0x1000000, 0x1ffffff) - TODO: PCMCIA
}

void zoomer_state::maincpu_io_map(address_map &map)
{
	map(0x0020, 0x0023).ram(); // TODO: irq related
	map(0x0024, 0x0025).r(FUNC(zoomer_state::irq_status_r));
	map(0x0026, 0x0027).w(FUNC(zoomer_state::irq_ack_w));
	map(0x0040, 0x004b).rw(FUNC(zoomer_state::timer_r), FUNC(zoomer_state::timer_w));
	map(0x004d, 0x004d).nopw(); // unknown, pen related
	map(0x0069, 0x0069).lr8(NAME([] { return 0x80; })); // suppress PCMCIA power usage warning
	map(0x0070, 0x0070).ram(); // suspend flag, used when pressing power switch or automatically powering off
	map(0x0071, 0x0071).lr8(NAME([] { return 0x07; })); // unknown, needed for BIOS to start up
	map(0x0072, 0x0072).portr("PORT72");
	map(0x0073, 0x0073).lr8(NAME([] { return 0x06; })); // PCMCIA slot voltage?
	map(0x0075, 0x0076).noprw(); // unknown, pen related
	map(0x0077, 0x0077).w(FUNC(zoomer_state::apo_w));
	map(0x0090, 0x00af).rw(FUNC(zoomer_state::ems_a000_bank_r), FUNC(zoomer_state::ems_a000_bank_w));
	map(0x00c0, 0x00c7).rw(FUNC(zoomer_state::ems_2000_bank_r), FUNC(zoomer_state::ems_2000_bank_w));
	map(0x00d0, 0x00d1).noprw(); // unknown, input related
	map(0x00d2, 0x00d2).portr("KEYPAD");
	map(0x00d3, 0x00d3).nopw(); // unknown, input related
	map(0x00d4, 0x00d4).portr("POWER");
	map(0x00d5, 0x00d5).nopw(); // unknown, pen related
	map(0x00d7, 0x00d7).lr8(NAME([] { return 0x01; })); // unknown, pen related
	map(0x00d8, 0x00d9).r(FUNC(zoomer_state::pen_value_r));
	map(0x00da, 0x00da).w(FUNC(zoomer_state::pen_select_w));
	map(0x00db, 0x00db).nopr(); // pen ADC ready
	map(0x00dc, 0x00dd).nopw(); // unknown, pen related
	map(0x00e0, 0x00ef).rw(m_rtc, FUNC(zoomer_rtc_device::read), FUNC(zoomer_rtc_device::write));
	map(0x0110, 0x011f).rw(m_psg, FUNC(upd65043gfu01_device::read), FUNC(upd65043gfu01_device::write));
	map(0x0204, 0x0205).rw(FUNC(zoomer_state::lcd_ctrl_r), FUNC(zoomer_state::lcd_ctrl_w));
	map(0x0213, 0x0213).r(FUNC(zoomer_state::lcdram_latch_r));
	/*
	TODO: port 414 = PCMCIA card status
	bit 0: ready
	bit 1: write protect
	bit 2: BVD1 (or 2?)
	bit 3: BVD2 (or 1?)
	bit 6: CD1 (or 2?)
	bit 7: CD2 (or 1?)
	*/
	map(0x0414, 0x0414).lr8(NAME([] { return 0xff; }));
	map(0x0800, 0x0fff).noprw(); // TODO: PCMCIA attribute memory?
}


//**************************************************************************
//  INPUT PORT DEFINITIONS
//**************************************************************************

static INPUT_PORTS_START( zoomer )
	PORT_START("PORT72")
	PORT_CONFNAME(   0x01, IP_ACTIVE_HIGH, "Main Battery")
	PORT_CONFSETTING(0x00, "Normal")
	PORT_CONFSETTING(0x01, "Low")
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNKNOWN) // needs to be low to boot
	PORT_CONFNAME(   0x04, IP_ACTIVE_HIGH, "Backup Battery")
	PORT_CONFSETTING(0x00, "Normal")
	PORT_CONFSETTING(0x04, "Low")
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNKNOWN)
	PORT_BIT(0x10, IP_ACTIVE_LOW,  IPT_UNKNOWN) // needs to be high to boot
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER)          PORT_NAME("PCMCIA Lock Switch") // TODO: PCMCIA lock switch (generates IRQ_PCMCIA_LOCK and IRQ_PCMCIA_UNLOCK)
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNKNOWN)

	PORT_START("KEYPAD")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON3)        PORT_NAME("Button B")     PORT_WRITE_LINE_MEMBER(FUNC(zoomer_state::irq_set<IRQ_KEYPAD>))
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON2)        PORT_NAME("Button A")     PORT_WRITE_LINE_MEMBER(FUNC(zoomer_state::irq_set<IRQ_KEYPAD>))
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP)    PORT_NAME("Cursor Up")    PORT_WRITE_LINE_MEMBER(FUNC(zoomer_state::irq_set<IRQ_KEYPAD>))
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT)  PORT_NAME("Cursor Left")  PORT_WRITE_LINE_MEMBER(FUNC(zoomer_state::irq_set<IRQ_KEYPAD>))
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN)  PORT_NAME("Cursor Down")  PORT_WRITE_LINE_MEMBER(FUNC(zoomer_state::irq_set<IRQ_KEYPAD>))
	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_NAME("Cursor Right") PORT_WRITE_LINE_MEMBER(FUNC(zoomer_state::irq_set<IRQ_KEYPAD>))
	PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNKNOWN)

	PORT_START("POWER")
	PORT_BIT(0x7f, IP_ACTIVE_HIGH, IPT_UNKNOWN  )
	PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_NAME("Power") PORT_WRITE_LINE_MEMBER(FUNC(zoomer_state::power_w))

	PORT_START("PEN")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Pen") PORT_WRITE_LINE_MEMBER(FUNC(zoomer_state::irq_set<IRQ_PEN>))

	PORT_START("PEN_X")
	PORT_BIT(0x3ff, 128, IPT_LIGHTGUN_X) PORT_NAME("Pen X") PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_MINMAX(0, 255) PORT_SENSITIVITY(45) PORT_KEYDELTA(30)

	PORT_START("PEN_Y")
	PORT_BIT(0x3ff, 179, IPT_LIGHTGUN_Y) PORT_NAME("Pen Y") PORT_CROSSHAIR(Y, 358.0 / 320, 0.0, 0) PORT_MINMAX(0, 357) PORT_SENSITIVITY(45) PORT_KEYDELTA(30)
INPUT_PORTS_END


//**************************************************************************
//  MACHINE EMULATION
//**************************************************************************

void zoomer_state::machine_start()
{
	m_irq_status = 0;
	m_pending_irq = 0;

	std::fill(std::begin(m_timer_rate), std::end(m_timer_rate), 0);
	std::fill(std::begin(m_bank_2000_num), std::end(m_bank_2000_num), 0);
	std::fill(std::begin(m_bank_a000_num), std::end(m_bank_a000_num), 0);

	m_pen_select = 0;

	m_lcdram_latch = 0;
	m_lcd_ctrl = 0;

	m_maincpu->space(AS_PROGRAM).install_ram(0x00000, 0x1ffff, m_nvram.begin());

	m_timer[0] = timer_alloc(FUNC(zoomer_state::timer_irq<IRQ_TIMER0>), this);
	m_timer[1] = timer_alloc(FUNC(zoomer_state::timer_irq<IRQ_TIMER1>), this);
	m_timer[2] = timer_alloc(FUNC(zoomer_state::timer_irq<IRQ_TIMER2>), this);

	save_item(NAME(m_power));
	save_item(NAME(m_irq_status));
	save_item(NAME(m_pending_irq));
	save_item(NAME(m_timer_rate));
	save_item(NAME(m_bank_2000_num));
	save_item(NAME(m_bank_a000_num));
	save_item(NAME(m_pen_select));
	save_item(NAME(m_lcdram_latch));
	save_item(NAME(m_lcd_ctrl));
}

//**************************************************************************
void zoomer_state::machine_reset()
{
	m_power = 1;
	m_psg->set_output_gain(ALL_OUTPUTS, 1.0);
}

//**************************************************************************
u32 zoomer_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	auto &crosshair = machine().crosshair().get_crosshair(0);
	crosshair.set_screen(crosshair.y() < 1.0 ? &screen : CROSSHAIR_SCREEN_NONE);

	if (!m_power || !BIT(m_lcd_ctrl, 7))
	{
		bitmap.fill(0, cliprect);
	}
	else
	{
		for (int y = cliprect.top(); y <= cliprect.bottom(); y++)
		{
			for (int x = cliprect.left(); x <= cliprect.right(); x++)
			{
				const u8 data = m_nvram[0xf8000 + (y << 6) + (x >> 3)];
				bitmap.pix(y, x) = BIT(data, 7 - (x & 7));
			}
		}
	}

	return 0;
}

//**************************************************************************
void zoomer_state::power_w(int state)
{
	if (state)
	{
		irq_set<IRQ_POWER>(1);
		m_power = 1;
		m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
		m_psg->set_output_gain(ALL_OUTPUTS, 1.0);
	}
}

//**************************************************************************
void zoomer_state::apo_w(u8 data)
{
	if (BIT(data, 7))
	{
		m_power = 0;
		m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
		m_psg->set_output_gain(ALL_OUTPUTS, 0.0);
	}
}

//**************************************************************************
u8 zoomer_state::ems_2000_bank_r(offs_t offset)
{
	return m_bank_2000_num[offset >> 1] >> (BIT(offset, 0) * 8);
}

//**************************************************************************
void zoomer_state::ems_2000_bank_w(offs_t offset, u8 data)
{
	const offs_t bank = offset >> 1;

	if (BIT(offset, 0))
	{
		m_bank_2000_num[bank] &= 0x00ff;
		m_bank_2000_num[bank] |= (data << 8);
	}
	else
	{
		m_bank_2000_num[bank] &= 0xff00;
		m_bank_2000_num[bank] |= data;
	}

	m_bank_2000[bank]->set_bank(m_bank_2000_num[bank]);
}

//**************************************************************************
u8 zoomer_state::ems_a000_bank_r(offs_t offset)
{
	return m_bank_a000_num[offset >> 1] >> (BIT(offset, 0) * 8);
}

//**************************************************************************
void zoomer_state::ems_a000_bank_w(offs_t offset, u8 data)
{
	const offs_t bank = offset >> 1;

	if (BIT(offset, 0))
	{
		m_bank_a000_num[offset >> 1] &= 0x00ff;
		m_bank_a000_num[offset >> 1] |= (data << 8);

		if (BIT(data, 7))
			m_wp_view[bank].select(0);
		else
			m_wp_view[bank].disable();
	}
	else
	{
		m_bank_a000_num[offset >> 1] &= 0xff00;
		m_bank_a000_num[offset >> 1] |= data;
	}

	m_bank_a000[bank]->set_bank(m_bank_a000_num[bank]);
}

//**************************************************************************
u16 zoomer_state::timer_count(u8 timer) const
{
	return m_timer_rate[timer] - m_timer[timer]->remaining().as_ticks(TIMER_RATE);
}

//**************************************************************************
u8 zoomer_state::timer_r(offs_t offset)
{
	const u8 timer = offset >> 2;

	switch (offset & 3)
	{
	case 0: return m_timer_rate[timer];
	case 1: return m_timer_rate[timer] >> 8;
	case 2: return timer_count(timer);
	case 3: return timer_count(timer) >> 8;
	}

	return 0;
}

//**************************************************************************
void zoomer_state::timer_w(offs_t offset, u8 data)
{
	const u8 timer = offset >> 2;

	switch (offset & 3)
	{
	case 0:
		m_timer_rate[timer] &= 0x0f00;
		m_timer_rate[timer] |= data;
		break;
	case 1:
		m_timer_rate[timer] &= 0x00ff;
		m_timer_rate[timer] |= ((data & 0xf) << 8);
		break;
	default:
		break;
	}

	if (!m_timer_rate[timer])
	{
		m_timer[timer]->adjust(attotime::never);
	}
	else
	{
		/*
		period needs to be based on value+1 for the GEOS system clock to not run fast
		(for some reason it reads the RTC only on boot, and then uses an OS-level software timer to keep time after that)
		*/
		const attotime period = attotime::from_ticks(m_timer_rate[timer] + 1, TIMER_RATE);

		/*
		DOS allows performing a factory reset by holding A+B on boot, which it checks by enabling input polling,
		waiting briefly in a loop, and then disabling input polling. in order for the input poll/debounce code
		to be called the expected number of times before polling is disabled again, the timer IRQ needs to go off
		immediately when set
		*/
		if (m_timer[timer]->expire().is_never())
			m_timer[timer]->adjust(attotime::zero, 0, period);
		else
			m_timer[timer]->adjust(m_timer[timer]->remaining(), 0, period);
	}
}

//**************************************************************************
u8 zoomer_state::irq_status_r(offs_t offset)
{
	return m_irq_status >> (BIT(offset, 0) * 8);
}

//**************************************************************************
void zoomer_state::irq_ack_w(offs_t offset, u8 data)
{
	const u16 clear = data << (BIT(offset, 0) * 8);

	if (BIT(clear, 15))
	{
		// clear current irq only (used by default handler)
		m_irq_status &= ~m_pending_irq;
		m_pending_irq = 0;
	}
	else
	{
		m_irq_status &= ~clear;
		m_pending_irq &= ~clear;
	}

	update_irq();
}

//**************************************************************************
void zoomer_state::update_irq()
{
	if (m_pending_irq) return;

	int state = CLEAR_LINE;
	int vector = 0;

	for (int i = 0; i < 14; i++)
	{
		if (BIT(m_irq_status, i))
		{
			state = ASSERT_LINE;
			vector = 0x70 + i;
			m_pending_irq = 1 << i;
			break;
		}
	}

	m_maincpu->set_input_line_and_vector(INPUT_LINE_IRQ0, state, vector);
}

//**************************************************************************
template <int Num>
void zoomer_state::irq_set(int state)
{
	if (state)
	{
		m_irq_status |= (1 << Num);
		update_irq();
	}
}

//**************************************************************************
u16 zoomer_state::pen_x_scaled()
{
	// 0...255 scaled to 0x069..396
	static constexpr float scale = 0x32d / 255.0;
	return 0x69 + (m_pen_x->read() * scale);
}

//**************************************************************************
u16 zoomer_state::pen_y_scaled()
{
	// 0...357 scaled to 0x044..3d4
	static constexpr float scale = 0x390 / 357.0;
	return 0x44 + (m_pen_y->read() * scale);
}

//**************************************************************************
u8 zoomer_state::pen_value_r(offs_t offset)
{
	u16 value = 0;

	if (m_pen->read() & 1)
	{
		switch (m_pen_select)
		{
		case 0x9: value = pen_x_scaled() ^ 0x3ff; break;
		case 0xb: value = pen_x_scaled(); break;
		case 0xd: value = pen_y_scaled() ^ 0x3ff; break;
		case 0xf: value = pen_y_scaled(); break;
		}
	}

	return value >> (BIT(offset, 0) * 8);
}

//**************************************************************************
void zoomer_state::pen_select_w(u8 data)
{
	m_pen_select = data;
}

//**************************************************************************
u8 zoomer_state::lcd_ctrl_r(offs_t offset)
{
	return m_lcd_ctrl >> (BIT(offset, 0) * 8);
}

//**************************************************************************
void zoomer_state::lcd_ctrl_w(offs_t offset, u8 data)
{
	if (BIT(offset, 0))
	{
		m_lcd_ctrl &= 0x00ff;
		m_lcd_ctrl |= (data << 8);
	}
	else
	{
		m_lcd_ctrl &= 0xff00;
		m_lcd_ctrl |= data;
	}
}

//**************************************************************************
u8 zoomer_state::lcdram_latch_r()
{
	return m_lcdram_latch;
}

//**************************************************************************
void zoomer_state::lcdram_w(offs_t offset, u8 data)
{
	offset |= 0xf8000; // TODO: can VRAM be relocated?

	switch (BIT(m_lcd_ctrl, 10, 2))
	{
	case 0: m_nvram[offset] = data; break;
	case 1: m_nvram[offset] |= data; break;
	case 2: m_nvram[offset] &= data; break;
	// GEOS seems to read VRAM freely via EMS, but the BIOS does it this way instead
	case 3: m_lcdram_latch = m_nvram[offset]; break;
	}
}


//**************************************************************************
//  MACHINE DEFINTIONS
//**************************************************************************

void zoomer_state::zoomer(machine_config &config)
{
	V20(config, m_maincpu, 7.3728_MHz_XTAL);
	m_maincpu->set_addrmap(AS_PROGRAM, &zoomer_state::maincpu_map);
	m_maincpu->set_addrmap(AS_IO, &zoomer_state::maincpu_io_map);

	for (int i = 0; i < 4; i++)
		ADDRESS_MAP_BANK(config, m_bank_2000[i]).set_map(&zoomer_state::maincpu_ems_map).set_options(ENDIANNESS_LITTLE, 8, 25, 0x10000);
	for (int i = 0; i < 16; i++)
		ADDRESS_MAP_BANK(config, m_bank_a000[i]).set_map(&zoomer_state::maincpu_ems_map).set_options(ENDIANNESS_LITTLE, 8, 25, 0x4000);

	ZOOMER_RTC(config, m_rtc, 32.768_kHz_XTAL);
	m_rtc->alarm_cb().set(FUNC(zoomer_state::irq_set<IRQ_RTC_ALARM>));
	m_rtc->tick_cb().set(FUNC(zoomer_state::irq_set<IRQ_RTC_TICK>));

	NVRAM(config, "nvram");

	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(256, 320);
	screen.set_visarea_full();
	screen.set_palette("palette");
	screen.set_screen_update(FUNC(zoomer_state::screen_update));

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	SPEAKER(config, "speaker").front_center();

	UPD65043GFU01(config, m_psg, 7.3728_MHz_XTAL);
	m_psg->add_route(0, "speaker", 1.0);
	m_psg->irq_cb().set(FUNC(zoomer_state::irq_set<IRQ_SOUND>));

	config.set_default_layout(layout_zoomer);
}


//**************************************************************************
//  ROM DEFINITIONS
//**************************************************************************

ROM_START( zoomer )
	ROM_REGION(0x400000, "maincpu", 0)
	ROM_LOAD("mb838200al700.lsi1", 0x000000, 0x100000, CRC(5fe48af6) SHA1(790adb436d80409b216996ee02f78164832b5de6))
	ROM_LOAD("mb838200al701.lsi2", 0x100000, 0x100000, CRC(3104492b) SHA1(9c752c381d5506d7175e3c9645b5d2c731cc0990))
	ROM_LOAD("mb838200al702.lsi3", 0x200000, 0x100000, CRC(6013e531) SHA1(955c8c0bdf4bc3e262e5f2fe8b0f7d224a855c96))
	ROM_LOAD("mb838200al703.lsi4", 0x300000, 0x100000, CRC(64038705) SHA1(81ce3e8564d0579cf57a670517ebd2dbf35996c5))
ROM_END

} // anonymous namespace


//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY          FULLNAME                   FLAGS
COMP( 1993, zoomer, 0,      0,      zoomer,  zoomer, zoomer_state, empty_init, "Casio / Tandy", "Zoomer (Z-PDA / Z-7000)", MACHINE_SUPPORTS_SAVE )



zorba.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert, Vas Crabb
/************************************************************************************************************

Telcon Industries/Modular Micros/Gemini Electronics Zorba
http://www.zorba.z80.de

2013-08-25 Skeleton
2015-02-20 Boots from floppy, is now usable.

This was one of the last CP/M-based systems, already out of date when it was released.
Because it doesn't use the standard Z80 peripherals, it uses a homebrew interrupt controller to make use
of the Z80's IM2.

The keyboard is an intelligent serial device like the Kaypro's keyboard. They even have the same plug,
and might be swappable. Need a schematic.

Instead of using a daisy chain, the IM2 vectors are calculated by a prom (u77). Unfortunately, the prom
contents make no sense at all (mostly FF), so the vectors for IRQ0 and IRQ2 are hard-coded. Other IRQ
vectors are not used as yet.

Three companies are known to have sold the Zorba over its lifetime: Telcon Industries, Modular Micros
(a subsidiary of Modular Computers (ModComp)), and Gemini Electronics. 7-inch and 9-inch models were
available from Telcon and Modular Micros, while Gemini exclusively sold the 9-inch version. The ROM dumps
currently used in this emulation originate from a Modular Micros Zorba.

The two versions of the Zorba were sold by Modular Micros were:
- Zorba 7: 7" CRT, 2 410K floppies, 22 lbs, $1595
- Zorba 2000: 9" CRT, 2 820K floppies, 10M HD optional, 25 lbs, ~$2000

The 7-inch version has the screen on the left, the floppy drives on the right, and a Zorba logo on the
far right; on the 9-inch version this arrangement is reversed and the logo is removed.

The startup screen varies across each company:
- Telcon: "TELCON ZORBA" graphical logo
- Modular Micros: "ZORBA" graphical logo with "MODULAR MICROS, INC." below in normal text
- Gemini: "GEMINI ZORBA" graphical logo

Status:
- Boots up, and the keyboard works

ToDo:
- Need software that does more than plain text (such as games)
- Add masking feature (only used for the UARTs)
- Connect devices to the above hardware
- Dump Telcon and Gemini BIOSes
- Emulate the Co-Power-88 expansion (allows PC-DOS, CP/M-86, etc. to be used)
- Probably lots of other things


*************************************************************************************************************/

#include "emu.h"

#include "zorbakbd.h"

#include "bus/centronics/ctronics.h"
#include "bus/ieee488/ieee488.h"
#include "bus/rs232/rs232.h"

#include "cpu/z80/z80.h"
#include "cpu/m6805/m68705.h"

#include "imagedev/floppy.h"

#include "machine/6821pia.h"
#include "machine/i8251.h"
#include "machine/input_merger.h"
#include "machine/output_latch.h"
#include "machine/pit8253.h"
#include "machine/wd_fdc.h"
#include "machine/z80dma.h"

#include "sound/beep.h"

#include "video/i8275.h"

#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"


namespace {

class zorba_state : public driver_device
{
public:
	zorba_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_config_port(*this, "CNF")
		, m_p_chargen(*this, "chargen")
		, m_maincpu(*this, "maincpu")
		, m_dma(*this, "dma")
		, m_uart(*this, "uart%u", 0U)
		, m_pia(*this, "pia%u", 0U)
		, m_palette(*this, "palette")
		, m_crtc(*this, "crtc")
		, m_beep(*this, "beeper")
		, m_fdc(*this, "fdc")
		, m_floppy(*this, "fdc:%u", 0U)
		, m_ieee(*this, IEEE488_TAG)
		, m_rom_view(*this, "rom_view")
	{
	}

	DECLARE_INPUT_CHANGED_MEMBER(printer_type);
	void zorba(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

private:
	// Memory banking control
	uint8_t ram_r();
	void ram_w(uint8_t data);
	uint8_t rom_r();
	void rom_w(uint8_t data);

	// Interrupt vectoring glue
	void intmask_w(uint8_t data);
	template <unsigned N> void tx_rx_rdy_w(int state);
	template <unsigned N> void irq_w(int state);

	// DMA controller handlers
	uint8_t memory_read_byte(offs_t offset);
	void memory_write_byte(offs_t offset, uint8_t data);
	uint8_t io_read_byte(offs_t offset);
	void io_write_byte(offs_t offset, uint8_t data);

	// PIT handlers
	void br1_w(int state);

	// PIA handlers
	void pia0_porta_w(uint8_t data);
	uint8_t pia1_portb_r();
	void pia1_portb_w(uint8_t data);

	// Video
	I8275_DRAW_CHARACTER_MEMBER(zorba_update_chr);

	// Printer port glue
	void printer_fault_w(int state);
	void printer_select_w(int state);

	void zorba_io(address_map &map) ATTR_COLD;
	void zorba_mem(address_map &map) ATTR_COLD;

	required_ioport                            m_config_port;

	required_region_ptr<uint8_t>               m_p_chargen;

	required_device<z80_device>                m_maincpu;
	required_device<z80dma_device>             m_dma;
	required_device_array<i8251_device, 3>     m_uart;
	required_device_array<pia6821_device, 2>   m_pia;

	required_device<palette_device>            m_palette;
	required_device<i8275_device>              m_crtc;

	required_device<beep_device>               m_beep;

	required_device<fd1793_device>             m_fdc;
	required_device_array<floppy_connector, 2> m_floppy;

	required_device<ieee488_device>            m_ieee;

	memory_view                                m_rom_view;

	uint8_t m_intmask = 0U;
	uint8_t m_tx_rx_rdy = 0U;
	uint8_t m_irq = 0U;

	bool    m_printer_prowriter = false;
	int32_t m_printer_fault = 0;
	int32_t m_printer_select = 0;

	uint8_t m_term_data = 0U;
};

void zorba_state::zorba_mem(address_map &map)
{
	map(0x0000, 0xffff).ram();
	map(0x0000, 0x3fff).view(m_rom_view);
	m_rom_view[0](0x0000, 0x3fff).rom().region("maincpu", 0);
}


void zorba_state::zorba_io(address_map &map)
{
	map.global_mask(0xff);
	map(0x00, 0x03).rw("pit", FUNC(pit8254_device::read), FUNC(pit8254_device::write));
	map(0x04, 0x04).rw(FUNC(zorba_state::rom_r), FUNC(zorba_state::rom_w));
	map(0x05, 0x05).rw(FUNC(zorba_state::ram_r), FUNC(zorba_state::ram_w));
	map(0x10, 0x11).rw(m_crtc, FUNC(i8275_device::read), FUNC(i8275_device::write));
	map(0x20, 0x21).rw(m_uart[0], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x22, 0x23).rw(m_uart[1], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x24, 0x25).rw(m_uart[2], FUNC(i8251_device::read), FUNC(i8251_device::write));
	map(0x26, 0x26).w(FUNC(zorba_state::intmask_w));
	map(0x30, 0x30).rw(m_dma, FUNC(z80dma_device::read), FUNC(z80dma_device::write));
	map(0x40, 0x43).rw(m_fdc, FUNC(fd1793_device::read), FUNC(fd1793_device::write));
	map(0x50, 0x53).rw(m_pia[0], FUNC(pia6821_device::read), FUNC(pia6821_device::write));
	map(0x60, 0x63).rw(m_pia[1], FUNC(pia6821_device::read), FUNC(pia6821_device::write));
}

namespace {

INPUT_PORTS_START( zorba )
	PORT_START("CNF")
	PORT_CONFNAME(0x01, 0x00, "Parallel Printer") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(zorba_state::printer_type), 0)
	PORT_CONFSETTING(0x00, "Centronics")
	PORT_CONFSETTING(0x01, "Prowriter")
INPUT_PORTS_END


void zorba_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}


// F4 Character Displayer
const gfx_layout u5_charlayout =
{
	8, 11,                  // 8 x 11 characters
	256,                    // 256 characters
	1,                      // 1 bits per pixel
	{ 0 },                  // no bitplanes
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8 },
	8*16                    // every char takes 16 bytes
};

GFXDECODE_START( gfx_zorba )
	GFXDECODE_ENTRY( "chargen", 0x0000, u5_charlayout, 0, 1 )
GFXDECODE_END

} // anonymous namespace


void zorba_state::zorba(machine_config &config)
{
	// basic machine hardware
	Z80(config, m_maincpu, 24_MHz_XTAL / 6);
	m_maincpu->set_addrmap(AS_PROGRAM, &zorba_state::zorba_mem);
	m_maincpu->set_addrmap(AS_IO, &zorba_state::zorba_io);
	m_maincpu->busack_cb().set(m_dma, FUNC(z80dma_device::bai_w));

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_refresh_hz(50);
	screen.set_screen_update("crtc", FUNC(i8275_device::screen_update));
	GFXDECODE(config, "gfxdecode", m_palette, gfx_zorba);
	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 800).add_route(ALL_OUTPUTS, "mono", 1.00); // should be horizontal frequency / 16, so depends on CRTC parameters

	INPUT_MERGER_ANY_HIGH(config, "irq0").output_handler().set(FUNC(zorba_state::irq_w<0>));
	INPUT_MERGER_ANY_HIGH(config, "irq1").output_handler().set(FUNC(zorba_state::irq_w<1>));
	INPUT_MERGER_ANY_HIGH(config, "irq2").output_handler().set(FUNC(zorba_state::irq_w<2>));

	/* devices */
	Z80DMA(config, m_dma, 24_MHz_XTAL / 6);
	m_dma->out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
	m_dma->out_int_callback().set("irq0", FUNC(input_merger_device::in_w<0>));
	//ba0 - not connected
	m_dma->in_mreq_callback().set(FUNC(zorba_state::memory_read_byte));
	m_dma->out_mreq_callback().set(FUNC(zorba_state::memory_write_byte));
	m_dma->in_iorq_callback().set(FUNC(zorba_state::io_read_byte));
	m_dma->out_iorq_callback().set(FUNC(zorba_state::io_write_byte));

	I8251(config, m_uart[0], 24_MHz_XTAL / 12); // U32 COM port J2
	m_uart[0]->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd)); // TODO: this line has a LED attached
	m_uart[0]->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr));
	m_uart[0]->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_uart[0]->rxrdy_handler().set(FUNC(zorba_state::tx_rx_rdy_w<1>));
	m_uart[0]->txrdy_handler().set(FUNC(zorba_state::tx_rx_rdy_w<0>));

	I8251(config, m_uart[1], 24_MHz_XTAL / 12); // U31 printer port J3
	m_uart[1]->txd_handler().set("serprn", FUNC(rs232_port_device::write_txd));
	m_uart[1]->rts_handler().set("serprn", FUNC(rs232_port_device::write_rts));
	m_uart[1]->rxrdy_handler().set(FUNC(zorba_state::tx_rx_rdy_w<3>));
	m_uart[1]->txrdy_handler().set(FUNC(zorba_state::tx_rx_rdy_w<2>));

	I8251(config, m_uart[2], 24_MHz_XTAL / 12); // U30 serial keyboard J6
	m_uart[2]->txd_handler().set("keyboard", FUNC(zorba_keyboard_device::txd_w));
	m_uart[2]->rxrdy_handler().set(FUNC(zorba_state::tx_rx_rdy_w<5>));
	m_uart[2]->txrdy_handler().set(FUNC(zorba_state::tx_rx_rdy_w<4>));

	// port A - disk select etc, beeper
	// port B - parallel interface
	PIA6821(config, m_pia[0]);
	m_pia[0]->writepa_handler().set(FUNC(zorba_state::pia0_porta_w));
	m_pia[0]->writepb_handler().set("parprndata", FUNC(output_latch_device::write));
	m_pia[0]->cb2_handler().set("parprn", FUNC(centronics_device::write_strobe));

	// IEEE488 interface
	PIA6821(config, m_pia[1]);
	m_pia[1]->readpa_handler().set(m_ieee, FUNC(ieee488_device::dio_r)); // TODO: gated with PB1
	m_pia[1]->writepa_handler().set(m_ieee, FUNC(ieee488_device::host_dio_w)); // TODO: gated with PB1
	m_pia[1]->readpb_handler().set(FUNC(zorba_state::pia1_portb_r));
	m_pia[1]->writepb_handler().set(FUNC(zorba_state::pia1_portb_w));
	m_pia[1]->ca2_handler().set(m_ieee, FUNC(ieee488_device::host_ifc_w));
	m_pia[1]->cb2_handler().set(m_ieee, FUNC(ieee488_device::host_ren_w));
	m_pia[1]->irqa_handler().set("irq1", FUNC(input_merger_device::in_w<0>));
	m_pia[1]->irqb_handler().set("irq1", FUNC(input_merger_device::in_w<1>));

	// PIT
	pit8254_device &pit(PIT8254(config, "pit", 0));
	pit.set_clk<0>(24_MHz_XTAL / 3);
	pit.set_clk<1>(24_MHz_XTAL / 3);
	pit.set_clk<2>(24_MHz_XTAL / 3);
	pit.out_handler<0>().set(FUNC(zorba_state::br1_w));
	pit.out_handler<1>().set(m_uart[1], FUNC(i8251_device::write_txc));
	pit.out_handler<1>().append(m_uart[1], FUNC(i8251_device::write_rxc));
	pit.out_handler<2>().set(m_uart[2], FUNC(i8251_device::write_txc));
	pit.out_handler<2>().append(m_uart[2], FUNC(i8251_device::write_rxc));

	// CRTC
	I8275(config, m_crtc, 14.318'181_MHz_XTAL / 8); // TODO: character clock divider is 9 during HRTC
	m_crtc->set_character_width(8);
	m_crtc->set_refresh_hack(true);
	m_crtc->set_display_callback(FUNC(zorba_state::zorba_update_chr));
	m_crtc->drq_wr_callback().set(m_dma, FUNC(z80dma_device::rdy_w));
	m_crtc->irq_wr_callback().set("irq0", FUNC(input_merger_device::in_w<1>));
	m_crtc->set_screen("screen");

	// Floppies
	FD1793(config, m_fdc, 24_MHz_XTAL / 24);
	m_fdc->intrq_wr_callback().set("irq2", FUNC(input_merger_device::in_w<0>));
	m_fdc->drq_wr_callback().set("irq2", FUNC(input_merger_device::in_w<1>));
	FLOPPY_CONNECTOR(config, m_floppy[0], zorba_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, m_floppy[1], zorba_floppies, "525dd", floppy_image_device::default_mfm_floppy_formats).enable_sound(true);

	// J1 IEEE-488
	IEEE488(config, m_ieee);
	m_ieee->srq_callback().set(m_pia[1], FUNC(pia6821_device::ca2_w)); // TODO: gated with PB1 from PIA

	// J2 EIA RS232/internal modem
	// TODO: this has additional lines compared to a regular RS232 port (TxC in, RxC in, RxC out, speaker in, power)
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr));
	rs232.rxd_handler().set(m_uart[0], FUNC(i8251_device::write_rxd)); // TODO: this line has a LED attached
	rs232.cts_handler().set(m_uart[0], FUNC(i8251_device::write_cts)); // TODO: this line has a LED attached
	rs232.dsr_handler().set(m_uart[0], FUNC(i8251_device::write_dsr));

	// J3 Parallel printer
	centronics_device &parprn(CENTRONICS(config, "parprn", centronics_devices, "printer"));
	parprn.busy_handler().set(m_uart[1], FUNC(i8251_device::write_cts));
	parprn.busy_handler().append(m_uart[1], FUNC(i8251_device::write_dsr)); // TODO: shared with serial CTS
	parprn.fault_handler().set(FUNC(zorba_state::printer_fault_w));
	parprn.select_handler().set(FUNC(zorba_state::printer_select_w));

	output_latch_device &parprndata(OUTPUT_LATCH(config, "parprndata"));
	parprn.set_output_latch(parprndata);

	// J3 Serial printer
	rs232_port_device &serprn(RS232_PORT(config, "serprn", default_rs232_devices, nullptr));
	serprn.rxd_handler().set(m_uart[1], FUNC(i8251_device::write_rxd)); // TODO: this line has a LED attached

	// J6 TTL-level serial keyboard
	ZORBA_KEYBOARD(config, "keyboard").rxd_cb().set(m_uart[2], FUNC(i8251_device::write_rxd));

	SOFTWARE_LIST(config, "flop_list").set_original("zorba");
}


//-------------------------------------------------
//  Initialise/reset
//-------------------------------------------------

void zorba_state::machine_start()
{
	save_item(NAME(m_intmask));
	save_item(NAME(m_tx_rx_rdy));
	save_item(NAME(m_irq));

	save_item(NAME(m_printer_prowriter));
	save_item(NAME(m_printer_fault));
	save_item(NAME(m_printer_select));

	save_item(NAME(m_term_data));

	m_printer_prowriter = false;
	m_printer_fault = 0;
	m_printer_select = 0;
}

void zorba_state::machine_reset()
{
	m_uart[2]->write_cts(0); // always asserted

	m_intmask = 0x00;
	m_tx_rx_rdy = 0x00;
	m_irq = 0x00;

	m_printer_prowriter = BIT(m_config_port->read(), 0);
	m_pia[0]->cb1_w(m_printer_prowriter ? m_printer_select : m_printer_fault);

	m_rom_view.select(0);

	m_maincpu->reset();
}


//-------------------------------------------------
// Memory banking control
//-------------------------------------------------

uint8_t zorba_state::ram_r()
{
	if (!machine().side_effects_disabled())
		m_rom_view.disable();
	return 0;
}

void zorba_state::ram_w(uint8_t data)
{
	m_rom_view.disable();
}

uint8_t zorba_state::rom_r()
{
	if (!machine().side_effects_disabled())
		m_rom_view.select(0);
	return 0;
}

void zorba_state::rom_w(uint8_t data)
{
	m_rom_view.select(0);
}


//-------------------------------------------------
//  Interrupt vectoring glue
//-------------------------------------------------

void zorba_state::intmask_w(uint8_t data)
{
	m_intmask = data & 0x3f; // only six lines physically present
	irq_w<3>(BIT(m_intmask & m_tx_rx_rdy, 0) | BIT(m_intmask & m_tx_rx_rdy, 1));
	irq_w<4>(BIT(m_intmask & m_tx_rx_rdy, 2) | BIT(m_intmask & m_tx_rx_rdy, 3));
	irq_w<5>(BIT(m_intmask & m_tx_rx_rdy, 4) | BIT(m_intmask & m_tx_rx_rdy, 5));
}

template <unsigned N> void zorba_state::tx_rx_rdy_w(int state)
{
	m_tx_rx_rdy = (m_tx_rx_rdy & ~(1 << N)) | ((state ? 1 : 0) << N);
	irq_w<(N >> 1) + 3>(BIT(m_intmask & m_tx_rx_rdy, N & ~1) | BIT(m_intmask & m_tx_rx_rdy, N | 1));
}

template <unsigned N> void zorba_state::irq_w(int state)
{
	m_irq = (m_irq & ~(1 << N)) | ((state ? 1 : 0) << N);

	// TODO: get a good dump of the interrupt vector PROM and get rid of this hack
	// There are six interrupt sources:
	// * IRQ0: DMAC/CRTC shared
	// * IRQ1: IEEE488 (PIA IRQA/IRQB shared)
	// * IRQ2: FDC INTRQ/DRQ shared
	// * IRQ3: RS232 TXRDY/RXRDY selectable
	// * IRQ4: Serial printer TXRDY/RXRDY selectable
	// * IRQ5: Keyboard TXRDY/RXRDY selectable
	// Valid vectors are 0x80, 0x84, 0x88, 0x8c, 0x90 and 0x94
	// Floppy needs to be on 0x80, and RS232 is apparently on 0x88
	// Everything else, including priority, is a complete guess
	u8 vector;
	if (BIT(m_irq, 2))      vector = 0x80;
	else if (BIT(m_irq, 3)) vector = 0x88;
	else                    vector = 0x84; // very wrong, need test cases for other things

	m_maincpu->set_input_line_and_vector(INPUT_LINE_IRQ0, m_irq ? ASSERT_LINE : CLEAR_LINE, vector); // Z80
}


//-------------------------------------------------
//  DMA controller handlers
//-------------------------------------------------

uint8_t zorba_state::memory_read_byte(offs_t offset)
{
	return m_maincpu->space(AS_PROGRAM).read_byte(offset);
}

void zorba_state::memory_write_byte(offs_t offset, uint8_t data)
{
	m_maincpu->space(AS_PROGRAM).write_byte(offset, data);
}

uint8_t zorba_state::io_read_byte(offs_t offset)
{
	address_space& prog_space = m_maincpu->space(AS_IO);
	return prog_space.read_byte(offset);
}

void zorba_state::io_write_byte(offs_t offset, uint8_t data)
{
	address_space& prog_space = m_maincpu->space(AS_IO);

	if (offset == 0x10)
		m_crtc->dack_w(data);
	else
		prog_space.write_byte(offset, data);
}


//-------------------------------------------------
//  PIT handlers
//-------------------------------------------------

void zorba_state::br1_w(int state)
{
	// TODO: these can be jumpered to inputs from J2 so a modem can generate Baud rates
	// TODO: receive clock is exposed on J2 for external devices without Baud rate generators
	m_uart[0]->write_txc(state);
	m_uart[0]->write_rxc(state);
}


//-------------------------------------------------
//  PIA handlers
//-------------------------------------------------

void zorba_state::pia0_porta_w(uint8_t data)
{
	m_beep->set_state(BIT(data, 7));
	m_fdc->dden_w(BIT(data, 6));

	floppy_image_device *floppy = nullptr;
	if (!BIT(data, 0)) floppy = m_floppy[0]->get_device();
	if (!BIT(data, 1)) floppy = m_floppy[1]->get_device();

	m_fdc->set_floppy(floppy);

	if (floppy)
		floppy->ss_w(!BIT(data, 5));

	m_floppy[0]->get_device()->mon_w(BIT(data, 4));
	m_floppy[1]->get_device()->mon_w(BIT(data, 4));
}

uint8_t zorba_state::pia1_portb_r()
{
	// 0  (output only)
	// 1  (output only)
	// 2  (output only)
	// 3  EOI   gated with PB2 (active high)
	// 4  (output only)
	// 5  DAV   gated with PB2 (active high)
	// 6  NDAC  gated with PB1 (active high)
	// 7  NRFD  gated with PB1 (active high)

	const uint8_t outputs(m_pia[0]->b_output());
	return
			(m_ieee->eoi_r() << 3) |
			(m_ieee->dav_r() << 5) |
			(m_ieee->ndac_r() << 6) |
			(m_ieee->nrfd_r() << 7) |
			(BIT(outputs, 1) ? 0x00 : 0xc0) |
			(BIT(outputs, 2) ? 0x00 : 0x28);
}

void zorba_state::pia1_portb_w(uint8_t data)
{
	// 0  DIO direction
	// 1  NDAC/NRFD data direction, SRQ gate
	// 2  EOI/DAV data direction
	// 3  EOI   gated with PB2 (active low)
	// 4  ATN
	// 5  DAV   gated with PB2 (active low)
	// 6  NDAC  gated with PB1 (active low)
	// 7  NRFD  gated with PB1 (active low)

	m_ieee->host_eoi_w(BIT(data, 3) & BIT(~data, 2));
	m_ieee->host_atn_w(BIT(data, 4));
	m_ieee->host_dav_w(BIT(data, 5) & BIT(~data, 2));
	m_ieee->host_ndac_w(BIT(data, 6) & BIT(~data, 1));
	m_ieee->host_nrfd_w(BIT(data, 7) & BIT(~data, 1));
}


//-------------------------------------------------
//  Video
//-------------------------------------------------

I8275_DRAW_CHARACTER_MEMBER( zorba_state::zorba_update_chr )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();

	using namespace i8275_attributes;
	uint8_t gfx = m_p_chargen[(linecount & 15) + (charcode << 4) + (BIT(attrcode, GPA0) ? 0x800 : 0)];

	if (BIT(attrcode, RVV))
		gfx ^= 0xff;

	// VSP actually overrides reverse video here
	if (BIT(attrcode, VSP))
		gfx = 0;

	if (BIT(attrcode, LTEN))
		gfx = 0xff;

	bool const hlgt = BIT(attrcode, HLGT);
	for (int i = 0; i < 8; i++)
		bitmap.pix(y, x + 7 - i) = palette[BIT(gfx, i) ? (hlgt ? 2 : 1) : 0];
}


//-------------------------------------------------
//  Printer port glue
//-------------------------------------------------

void zorba_state::printer_fault_w(int state)
{
	// connects to CB1 for Centronics
	m_printer_fault = state;
	if (!m_printer_prowriter)
		m_pia[0]->cb1_w(state);
}

void zorba_state::printer_select_w(int state)
{
	// connects to CB1 for Prowriter
	m_printer_select = state;
	if (m_printer_prowriter)
		m_pia[0]->cb1_w(state);
}

INPUT_CHANGED_MEMBER( zorba_state::printer_type )
{
	m_printer_prowriter = newval;
	m_pia[0]->cb1_w(m_printer_prowriter ? m_printer_select : m_printer_fault);
}


ROM_START( zorba )
	ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "780000.u47", 0x0000, 0x1000, CRC(6d58f2c5) SHA1(7763f08c801cd36e5a761c6dc9f30a50b3bc482d) )

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "773000.u5", 0x0000, 0x1000, CRC(d0a2f8fc) SHA1(29aee7ee657778c46e9800abd4955e6d4b33ef68) )

	ROM_REGION( 0x60, "proms", 0 )
	ROM_LOAD( "74ls288.u37", 0x0000, 0x0020, CRC(0a67edd6) SHA1(c1ece8978a3a061e0130d43907fa63a71e75e75d) BAD_DUMP ) // looks like interrupt vector PROM dumped with scrambled lines
	ROM_LOAD( "74ls288.u38", 0x0020, 0x0020, CRC(5ec93ea7) SHA1(3a84c098474b05d5cbe1939a3e15f66d06470581) BAD_DUMP ) // looks like bad dump of address decode PROM
	ROM_LOAD( "74ls288.u77", 0x0040, 0x0020, CRC(946e03b0) SHA1(24240bdd7bdf507a5b51628fb36ad1266fc53a28) BAD_DUMP ) // looks like bad dump of address decode PROM
ROM_END

} // anonymous namespace

COMP( 1984?, zorba, 0, 0, zorba, zorba, zorba_state, empty_init, "Modular Micros", "Zorba (Modular Micros)", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )

// Undumped versions (see startup screen notes at top of file)
// COMP( 1983, zorbat, zorba, 0, zorba, zorba, zorba_state, empty_init, "Telcon Industries",  "Zorba (Telcon Industries)",  MACHINE_NOT_WORKING )
// COMP( 1984, zorbag, zorba, 0, zorba, zorba, zorba_state, empty_init, "Gemini Electronics", "Zorba (Gemini Electronics)", MACHINE_NOT_WORKING )



zrt80.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

        DEC ZRT-80

        12/05/2009 Skeleton driver.

        16/02/2011 Working.

        The beeper is external, frequency not known. I've made a reasonable
        assumption of frequency and lengths.

        Make sure 'mode' dipswitch is set to 'local' so you can see your
        typing.

****************************************************************************/

#include "emu.h"
#include "cpu/z80/z80.h"
#include "video/mc6845.h"
#include "machine/ins8250.h"
#include "machine/keyboard.h"
#include "sound/beep.h"
#include "machine/timer.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


namespace {

class zrt80_state : public driver_device
{
public:
	zrt80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_p_videoram(*this, "videoram")
		, m_maincpu(*this, "maincpu")
		, m_crtc(*this, "crtc")
		, m_8250(*this, "ins8250")
		, m_beep(*this, "beeper")
		, m_palette(*this, "palette")
		, m_p_chargen(*this, "chargen")
		, m_beep_timer(*this, "beep_timer")
	{ }

	void zrt80(machine_config &config);

private:
	uint8_t zrt80_10_r();
	void zrt80_30_w(uint8_t data);
	void zrt80_38_w(uint8_t data);
	void kbd_put(u8 data);
	MC6845_UPDATE_ROW(crtc_update_row);

	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	TIMER_DEVICE_CALLBACK_MEMBER(beep_timer);
	uint8_t m_term_data = 0U;
	void machine_reset() override ATTR_COLD;
	void machine_start() override ATTR_COLD;
	required_shared_ptr<uint8_t> m_p_videoram;
	required_device<cpu_device> m_maincpu;
	required_device<mc6845_device> m_crtc;
	required_device<ins8250_device> m_8250;
	required_device<beep_device> m_beep;
	required_device<palette_device> m_palette;
	required_region_ptr<u8> m_p_chargen;
	required_device<timer_device> m_beep_timer;
};


uint8_t zrt80_state::zrt80_10_r()
{
	uint8_t ret = m_term_data;
	m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
	return ret;
}

TIMER_DEVICE_CALLBACK_MEMBER(zrt80_state::beep_timer)
{
	m_beep->set_state(0);
}


void zrt80_state::zrt80_30_w(uint8_t data)
{
	m_beep_timer->adjust(attotime::from_msec(100));
	m_beep->set_state(1);
}

void zrt80_state::zrt80_38_w(uint8_t data)
{
	m_beep_timer->adjust(attotime::from_msec(400));
	m_beep->set_state(1);
}

void zrt80_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x0fff).rom(); // Z25 - Main firmware
	map(0x1000, 0x1fff).rom(); // Z24 - Expansion
	map(0x4000, 0x43ff).ram(); // Board RAM
	// Normally video RAM is 0x800 but could be expanded up to 8K
	map(0xc000, 0xdfff).ram().share("videoram"); // Video RAM

}

void zrt80_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x00, 0x07).rw(m_8250, FUNC(ins8250_device::ins8250_r), FUNC(ins8250_device::ins8250_w));
	map(0x08, 0x08).w(m_crtc, FUNC(mc6845_device::address_w));
	map(0x09, 0x09).rw(m_crtc, FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w));
	map(0x10, 0x17).r(FUNC(zrt80_state::zrt80_10_r));
	map(0x18, 0x1F).portr("DIPSW2");
	map(0x20, 0x27).portr("DIPSW3");
	map(0x30, 0x37).w(FUNC(zrt80_state::zrt80_30_w));
	map(0x38, 0x3F).w(FUNC(zrt80_state::zrt80_38_w));
}

/* Input ports */
static INPUT_PORTS_START( zrt80 )
	PORT_START("DIPSW1")
		PORT_DIPNAME( 0x01, 0x01, "Composite Sync" )
		PORT_DIPSETTING(    0x01, "Negative" )
		PORT_DIPSETTING(    0x00, "Positive" )
		PORT_DIPNAME( 0x02, 0x02, "Vertical Sync" )
		PORT_DIPSETTING(    0x02, "Negative" )
		PORT_DIPSETTING(    0x00, "Positive" )
		PORT_DIPNAME( 0x04, 0x00, "Video" )
		PORT_DIPSETTING(    0x04, "Negative" )
		PORT_DIPSETTING(    0x00, "Positive" )
		PORT_DIPNAME( 0x08, 0x08, "Keypad" )
		PORT_DIPSETTING(    0x08, "Numeric" )
		PORT_DIPSETTING(    0x00, "Alternate Keyboard" )
		PORT_DIPNAME( 0x10, 0x10, "Horizontal Sync" )
		PORT_DIPSETTING(    0x10, "Negative" )
		PORT_DIPSETTING(    0x00, "Positive" )
		PORT_DIPNAME( 0x20, 0x20, "CPU" )
		PORT_DIPSETTING(    0x20, "Operating" )
		PORT_DIPSETTING(    0x00, "Reset" )
		PORT_DIPNAME( 0x40, 0x40, "Keyboard strobe" )
		PORT_DIPSETTING(    0x40, "Negative" )
		PORT_DIPSETTING(    0x00, "Positive" )
		PORT_DIPNAME( 0x80, 0x00, "Beeper" )
		PORT_DIPSETTING(    0x80, "Silent" )
		PORT_DIPSETTING(    0x00, "Enable" )
	PORT_START("DIPSW2")
		PORT_DIPNAME( 0x0f, 0x05, "Baud rate" )
		PORT_DIPSETTING(    0x00, "50" )
		PORT_DIPSETTING(    0x01, "75" )
		PORT_DIPSETTING(    0x02, "110" )
		PORT_DIPSETTING(    0x03, "134.5" )
		PORT_DIPSETTING(    0x04, "150" )
		PORT_DIPSETTING(    0x05, "300" )
		PORT_DIPSETTING(    0x06, "600" )
		PORT_DIPSETTING(    0x07, "1200" )
		PORT_DIPSETTING(    0x08, "1800" )
		PORT_DIPSETTING(    0x09, "2000" )
		PORT_DIPSETTING(    0x0a, "2400" )
		PORT_DIPSETTING(    0x0b, "3600" )
		PORT_DIPSETTING(    0x0c, "4800" )
		PORT_DIPSETTING(    0x0d, "7200" )
		PORT_DIPSETTING(    0x0e, "9600" )
		PORT_DIPSETTING(    0x0f, "19200" )
		PORT_DIPNAME( 0x30, 0x20, "Parity" )
		PORT_DIPSETTING(    0x00, "Odd" )
		PORT_DIPSETTING(    0x10, "Even" )
		PORT_DIPSETTING(    0x20, "Marking" )
		PORT_DIPSETTING(    0x30, "Spacing" )
		PORT_DIPNAME( 0x40, 0x40, "Handshake" )
		PORT_DIPSETTING(    0x40, "CTS" )
		PORT_DIPSETTING(    0x00, "XON/XOFF" )
		PORT_DIPNAME( 0x80, 0x80, "Line Feed" )
		PORT_DIPSETTING(    0x80, "No LF on CR" )
		PORT_DIPSETTING(    0x00, "Auto" )
	PORT_START("DIPSW3")
		PORT_DIPNAME( 0x07, 0x07, "Display" )
		PORT_DIPSETTING(    0x00, "96 x 24 15750Hz, 50Hz" )
		PORT_DIPSETTING(    0x01, "80 x 48 15750Hz, 50Hz" )
		PORT_DIPSETTING(    0x02, "80 x 24 15750Hz, 50Hz" )
		PORT_DIPSETTING(    0x03, "96 x 24 15750Hz, 60Hz" )
		PORT_DIPSETTING(    0x04, "80 x 48 18700Hz, 50Hz" )
		PORT_DIPSETTING(    0x05, "80 x 24 17540Hz, 60Hz" )
		PORT_DIPSETTING(    0x06, "80 x 48 15750Hz, 60Hz" )
		PORT_DIPSETTING(    0x07, "80 x 24 15750Hz, 60Hz" )
		PORT_DIPNAME( 0x18, 0x18, "Emulation" )
		PORT_DIPSETTING(    0x00, "Adds" )
		PORT_DIPSETTING(    0x08, "Beehive" )
		PORT_DIPSETTING(    0x10, "LSI ADM-3" )
		PORT_DIPSETTING(    0x18, "Heath H-19" )
		PORT_DIPNAME( 0x20, 0x00, "Mode" )
		PORT_DIPSETTING(    0x20, "Line" )
		PORT_DIPSETTING(    0x00, "Local" )
		PORT_DIPNAME( 0x40, 0x40, "Duplex" )
		PORT_DIPSETTING(    0x40, "Full" )
		PORT_DIPSETTING(    0x00, "Half" )
		PORT_DIPNAME( 0x80, 0x80, "Wraparound" )
		PORT_DIPSETTING(    0x80, "Disabled" )
		PORT_DIPSETTING(    0x00, "Enabled" )
INPUT_PORTS_END


void zrt80_state::machine_start()
{
	save_item(NAME(m_term_data));
}

void zrt80_state::machine_reset()
{
	m_term_data = 0;
}

MC6845_UPDATE_ROW( zrt80_state::crtc_update_row )
{
	rgb_t const *const palette = m_palette->palette()->entry_list_raw();
	uint32_t *p = &bitmap.pix(y);
	uint8_t const polarity = ioport("DIPSW1")->read() & 4 ? 0xff : 0;

	for (uint16_t x = 0; x < x_count; x++)
	{
		uint8_t inv = polarity;
		if (x == cursor_x) inv ^= 0xff;
		uint16_t const mem = (ma + x) & 0x1fff;
		uint8_t chr = m_p_videoram[mem];

		if (BIT(chr, 7))
		{
			inv ^= 0xff;
			chr &= 0x7f;
		}

		uint8_t const gfx = m_p_chargen[(chr<<4) | ra] ^ inv;

		/* Display a scanline of a character */
		*p++ = palette[BIT(gfx, 7)];
		*p++ = palette[BIT(gfx, 6)];
		*p++ = palette[BIT(gfx, 5)];
		*p++ = palette[BIT(gfx, 4)];
		*p++ = palette[BIT(gfx, 3)];
		*p++ = palette[BIT(gfx, 2)];
		*p++ = palette[BIT(gfx, 1)];
		*p++ = palette[BIT(gfx, 0)];
	}
}

void zrt80_state::kbd_put(u8 data)
{
	m_term_data = data;
	m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
}

/* F4 Character Displayer */
static const gfx_layout zrt80_charlayout =
{
	8, 16,                  /* 8 x 16 characters */
	128,                    /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_zrt80 )
	GFXDECODE_ENTRY( "chargen", 0x0000, zrt80_charlayout, 0, 1 )
GFXDECODE_END

void zrt80_state::zrt80(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(2'457'600));
	m_maincpu->set_addrmap(AS_PROGRAM, &zrt80_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &zrt80_state::io_map);

	/* video hardware */
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::green());
	screen.set_refresh_hz(60);
	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
	screen.set_size(640, 200);
	screen.set_visarea(0, 640-1, 0, 200-1);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	GFXDECODE(config, "gfxdecode", m_palette, gfx_zrt80);

	PALETTE(config, m_palette, palette_device::MONOCHROME);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	BEEP(config, m_beep, 800).add_route(ALL_OUTPUTS, "mono", 0.50);
	TIMER(config, m_beep_timer).configure_generic(FUNC(zrt80_state::beep_timer));

	/* Devices */
	MC6845(config, m_crtc, XTAL(20'000'000) / 8);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(8); /*?*/
	m_crtc->set_update_row_callback(FUNC(zrt80_state::crtc_update_row));

	INS8250(config, m_8250, 2457600);
	m_8250->out_int_callback().set_inputline("maincpu", INPUT_LINE_IRQ0);

	generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0));
	keyboard.set_keyboard_callback(FUNC(zrt80_state::kbd_put));
}

/* ROM definition */
ROM_START( zrt80 )
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD("zrt80mon.z25", 0x0000, 0x1000, CRC(e6ea96dc) SHA1(e3075e30bb2b85f9288d0b8b8cdf1d2b4f7586fd) )
	//z24 is 2nd chip, used as expansion

	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD("zrt80chr.z30", 0x0000, 0x0800, CRC(4dbdc60f) SHA1(20e393f7207a8440029c8290cdf2f121d317a37e) )
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY                       FULLNAME   FLAGS */
COMP( 1982, zrt80, 0,      0,      zrt80,   zrt80, zrt80_state, empty_init, "Digital Research Computers", "ZRT-80",  MACHINE_SUPPORTS_SAVE )



zsbc3.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

        Digital Microsystems ZSBC-3

        11/01/2010 Skeleton driver.
        28/11/2010 Connected to a terminal
        11/01/2011 Converted to Modern.

****************************************************************************

        Monitor commands: [] indicates optional

        Bx = Boot from device x (0 to 7)
        Dx [y] = Dump memory (hex and ascii) in range x [to y]
        Fx y z = Fill memory x to y with z
        Gx = Execute program at address x
        Ix = Display IN of port x
        Ox y = Output y to port x
        Sx = Enter memory editing mode, press enter for next address
        Mx y = unknown (affects memory)
        Tx = unknown (does strange things)
        enter = dump memory from 9000 to 907F (why?)

****************************************************************************

        TODO:
        Everything really...

        Devices of all kinds;
        Use the other rom for something..

****************************************************************************/


#include "emu.h"
#include "cpu/z80/z80.h"
#include "machine/z80ctc.h"
#include "machine/z80sio.h"
#include "machine/z80pio.h"
#include "bus/rs232/rs232.h"


namespace {

class zsbc3_state : public driver_device
{
public:
	zsbc3_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
	{ }

	void zsbc3(machine_config &config);

private:
	void io_map(address_map &map) ATTR_COLD;
	void mem_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};


void zsbc3_state::mem_map(address_map &map)
{
	map(0x0000, 0x07ff).rom();
	map(0x0800, 0xffff).ram();
}

void zsbc3_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map.global_mask(0xff);
	map(0x08, 0x0b); //.rw("pio", FUNC(z80pio_device::read), FUNC(z80pio_device::write)); // the control bytes appear to be for a PIO
	map(0x28, 0x2b).rw("sio", FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
	map(0x30, 0x33).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
	map(0x38, 0x38); // unknown device, init byte = C3
}

/* Input ports */
static INPUT_PORTS_START( zsbc3 )
INPUT_PORTS_END


void zsbc3_state::zsbc3(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, 16_MHz_XTAL / 4);
	m_maincpu->set_addrmap(AS_PROGRAM, &zsbc3_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &zsbc3_state::io_map);

	z80ctc_device &ctc(Z80CTC(config, "ctc", 16_MHz_XTAL / 4));
	ctc.set_clk<0>(16_MHz_XTAL / 8);
	ctc.set_clk<1>(16_MHz_XTAL / 8);
	ctc.set_clk<2>(16_MHz_XTAL / 8);
	ctc.set_clk<3>(16_MHz_XTAL / 8);
	ctc.zc_callback<0>().set("sio", FUNC(z80sio_device::txca_w));
	ctc.zc_callback<0>().append("sio", FUNC(z80sio_device::rxca_w));

	z80sio_device &sio(Z80SIO(config, "sio", 16_MHz_XTAL / 4));
	//sio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);  // no evidence of a daisy chain because IM2 is not set
	sio.out_txda_callback().set("rs232", FUNC(rs232_port_device::write_txd));
	sio.out_dtra_callback().set("rs232", FUNC(rs232_port_device::write_dtr));
	sio.out_rtsa_callback().set("rs232", FUNC(rs232_port_device::write_rts));

	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set("sio", FUNC(z80sio_device::rxa_w));
	rs232.cts_handler().set("sio", FUNC(z80sio_device::ctsa_w));

	/*z80pio_device &pio(*/Z80PIO(config, "pio", 16_MHz_XTAL / 4)/*)*/;
	//pio.out_int_callback.set_inputline(m_maincpu, INPUT_LINE_IRQ0);
}

/* ROM definition */
ROM_START( zsbc3 )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "54-3002_zsbc_monitor_1.09.bin", 0x0000, 0x0800, CRC(628588e9) SHA1(8f0d489147ec8382ca007236e0a95a83b6ebcd86))

	ROM_REGION( 0x0400, "hdc", 0 )
	ROM_LOAD( "54-8622_hdc13.bin", 0x0000, 0x0400, CRC(02c7cd6d) SHA1(494281ba081a0f7fbadfc30a7d2ea18c59e55101))
ROM_END

} // anonymous namespace


/* Driver */

/*    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  STATE        INIT        COMPANY                 FULLNAME  FLAGS */
COMP( 1980, zsbc3, 0,      0,      zsbc3,   zsbc3, zsbc3_state, empty_init, "Digital Microsystems", "ZSBC-3", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )



zt8802.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:AJR
/***********************************************************************************************************************************

Ziatech ZT-8802 single-board computer. Advert says: V40 CPU @ 8MHz, 1MB DRAM, 512K EPROM, 3 RS-232 PORTS.

CPU is NEC D70208L-10 (V40) which is a V20 with 8251,8253,8259 included.

Chips: Maxim MAX249CQH, Exar XT16C452CJPS, Ziatech ICPSMCI-16C49A, 2x GAL22V10D, 2x ACT15245, 2x ACT11245,
       ACT11240, PALCE 16V??-15JC, 2x CYM1465LPD-120C (RAM).

Other: A 3.6volt battery, a tiny crystal, a red LED, and about 2 dozen jumpers.

************************************************************************************************************************************/

#include "emu.h"
#include "cpu/nec/v5x.h"
#include "machine/ds1302.h"


namespace {

class zt8802_state : public driver_device
{
public:
	zt8802_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_pia(*this, "pia")
		, m_rtc(*this, "rtc")
	{ }

	void zt8802(machine_config &config);

private:
	void pia_w(offs_t offset, u8 data);
	u8 rtc_r();
	void rtc_w(u8 data);

	void mem_map(address_map &map) ATTR_COLD;
	void io_map(address_map &map) ATTR_COLD;

	required_device<cpu_device> m_maincpu;
	required_shared_ptr<u8> m_pia;
	required_device<ds1202_device> m_rtc;
};

void zt8802_state::pia_w(offs_t offset, u8 data)
{
	if (offset == 5)
		m_rtc->ce_w(BIT(data, 4));

	m_pia[offset] = data;
}

u8 zt8802_state::rtc_r()
{
	if (!machine().side_effects_disabled())
		m_rtc->sclk_w(0);
	u8 data = m_rtc->io_r();
	if (!machine().side_effects_disabled())
		m_rtc->sclk_w(1);
	return data;
}

void zt8802_state::rtc_w(u8 data)
{
	m_rtc->sclk_w(0);
	m_rtc->io_w(BIT(data, 0));
	m_rtc->sclk_w(1);
}

void zt8802_state::mem_map(address_map &map)
{
	map(0x00000, 0x7ffff).ram();
	map(0x80000, 0xfffff).rom().region("roms", 0);
}

void zt8802_state::io_map(address_map &map)
{
	map(0x0080, 0x0080).nopr(); // watchdog?
	map(0xfa00, 0xfa07).ram().share("pia").w(FUNC(zt8802_state::pia_w)); //.rw("pia", FUNC(zt16c49_device::read), FUNC(zt16c49_device::write));
	map(0xfa80, 0xfa80).rw(FUNC(zt8802_state::rtc_r), FUNC(zt8802_state::rtc_w));
}

static INPUT_PORTS_START( zt8802 )
INPUT_PORTS_END

void zt8802_state::zt8802(machine_config &config)
{
	V40(config, m_maincpu, 16'000'000);
	m_maincpu->set_addrmap(AS_PROGRAM, &zt8802_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &zt8802_state::io_map);

	DS1202(config, m_rtc, 32'768);
}

ROM_START( zt8802 )
	ROM_REGION( 0x80000, "roms", 0 )
	ROM_LOAD( "c103207-218 a.rom", 0x00000, 0x80000, CRC(fc1c6e99) SHA1(cfbb2f0c9927bac5abc85c12d2b82f7da46cab03) )
ROM_END

} // anonymous namespace


COMP( 1994, zt8802, 0, 0, zt8802, zt8802, zt8802_state, empty_init, "Ziatech", "ZT-8802 SBC", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



zvenezia.cpp
<---------------------------------------------------------------------->
// license:BSD-3-Clause
// copyright-holders:

/*
  Skeleton driver for MCS51-based Necta/Zanussi vending machines.

  Zanussi / Necta Venezia coffee vending machine (same PCB found also on other models, like Brio):

CPU PCB (6735-365-02)
  _______________________________________________________________________________________________________
 |              ··      ···········                                ··············                       |
 |   ____                                                                                               |
 | T2512NH   ___  ___  ___  ___  ___  ___  ___  ___  ___  ___  ___  ___  ___  ___  ___  ___  ___        |
 |           RLY  RLY  RLY  RLY  RLY  RLY  RLY  RLY  RLY  RLY  RLY  RLY  RLY  RLY  RLY  RLY  RLY <- 17 relays
 |           ___   ___   _________   _________   _________   _________   _________  ___      _________  |
 |        LM393N LM393N |TD62083AP  |74HC273AN  |74HC273AN  |74HC273AN  |TD62083AP ADM692AN |74HC373N|  |
 |                          ___      _________             _________     _________     _______________  |
 |   ___                   67WR1K   |74HC244N|            |GAL16V8B|    |_DIPSx8_|    | mS6264L-10PC |  |
 | LM3578AN      ____                            _________   _________   _________    |______________|  |
 |             LM340T12    HEAT LED             |MM74HC14N  |HIN232CP|  |74HC244N|     _______________  |
 |                          RUN LED              _________               _________    | EPROM        |  |
 |                                              |MM74HC14N              |P80C32EBAA   |______________|  |
 |                                                                      |        |     Xtal       ____  |
 |                                                                      |________|  11.0592 MHz  93C56N |
 |      ...                .......   ..........   .......   .........                            EEPROM |
 |______________________________________________________________________________________________________|

KEYBOARD / DISPLAY PCB (4 digits, there are version with five)
  _______________________________________
 |·      ___   ___   ___   ___     ____ |
 |·     |__|  |__|  |__|  |__|    |    ||
 |·     |__|. |__|. |__|. |__|.   |    |<-Unknown MCU
 |                                |    ||
 |·        ____        ____       |    ||
 |·   LED |SW6 |      |SW1 | LED  |    ||
 |·       |____|      |____|      |    ||
 |·        ____        ____       |____||
 |·   LED |SW7 |      |SW2 | LED        |
 |        |____|      |____|          · |
 |·        ____        ____           · |
 |    LED |SW8 |      |SW3 | LED      · |
 | D      |____|      |____|            |
 | B       ____        ____             |
 | 9  LED |SW9 |      |SW4 | LED  ____  |
 |        |____|      |____|     |PIP | |
 |··       ____        ____      |____| |
 |..  LED |SW10|      |SW5 | LED        |
 |·       |____|      |____|            |
 |______________________________________|

*/

#include "emu.h"
#include "cpu/mcs51/mcs51.h"
#include "machine/eepromser.h"

namespace {

class zvenezia_state : public driver_device
{
public:
	zvenezia_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu")
	{ }

	void zvenezia(machine_config &config);
	void zunknecta(machine_config &config);

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	required_device<cpu_device> m_maincpu;
};

void zvenezia_state::machine_start()
{
}

void zvenezia_state::machine_reset()
{
}

static INPUT_PORTS_START( zvenezia )
	PORT_START("DSW0")
	PORT_DIPUNKNOWN_DIPLOC(0x01, 0x01, "SW0:1")
	PORT_DIPUNKNOWN_DIPLOC(0x02, 0x02, "SW0:2")
	PORT_DIPUNKNOWN_DIPLOC(0x04, 0x04, "SW0:3")
	PORT_DIPUNKNOWN_DIPLOC(0x08, 0x08, "SW0:4")
	PORT_DIPUNKNOWN_DIPLOC(0x10, 0x10, "SW0:5")
	PORT_DIPUNKNOWN_DIPLOC(0x20, 0x20, "SW0:6")
	PORT_DIPUNKNOWN_DIPLOC(0x40, 0x40, "SW0:7")
	PORT_DIPUNKNOWN_DIPLOC(0x80, 0x80, "SW0:8")
INPUT_PORTS_END

static INPUT_PORTS_START( zunknecta )
INPUT_PORTS_END

void zvenezia_state::zvenezia(machine_config &config)
{
	I80C32(config, m_maincpu, 11.0592_MHz_XTAL); // Philips P80C32EBAA

	EEPROM_93C56_8BIT(config, "eeprom");
}

void zvenezia_state::zunknecta(machine_config &config)
{
	P80C552(config, m_maincpu, 12_MHz_XTAL); // Philips PCB 80C552-5 16WP
}

ROM_START( zvenezia )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "venezia_rev_1.40_ife_14-04-95.u7", 0x00000, 0x10000, CRC(e03d9bf1) SHA1(ca728d754841993c818b5505fd3de66b3b9df13b) )

	ROM_REGION( 0x117, "plds", 0 )
	ROM_LOAD( "gal16v8b.bin", 0x000, 0x117, NO_DUMP )
ROM_END


/* Unknown Necta / Zanussi vending machine model:

 MAIN PCB (6735-360-00)
  _____________________________________________________________________
 |                                       _________   _________        |
 | ···      _____     ..                 |PCF8574P|  M74HC540B1  ..   |
 | ···   ·  RELAY     ..      _________  _________   _________   ..   |
 | ···   ·  _____     ..     |PCF8574P|  |PCF8574P|  |PCF8574P|  ..   |
 |       ·  RELAY     ..      _________   _________   _________  ..   |
 | ···     ··    ___  ..     M74HC541B1  M74HC541B1  M74HC541B1  ..   |
 |   ___   ··  M0C3020                   _________   _________       :|
 | M0C3020 ··                           |74HC00N_|  |74HC00N_|       :|
 | ·    ___   ___     ________   ________   ________   ________      :|
 | ·   |<-RELAY |    |ULN2064B  |ULN2064B  |ULN2064B  |ULN2064B       |
 | ·   |  |  |<-RELAY                                                :|
 | ·   |__|  |__|                                                    :|
 |  ·······   ······· ······ ······   ··········· ·········· ······· :|
 |____________________________________________________________________|

 CPU PCB
  ________________________________________________
 |                ___________     ___________    |
 | ..             |M74HC541B1|   |M74HC541B1| .. |
 | ..          ____________   _____________   .. |
 | ..          |Philips    | | EPROM       |  .. |
 | ..  _____   |80C552-5   | |_____________|  .. |
 | ..  Xtal    |           |     __________   .. |
 |    12 MHz   |___________|    |GD74HC273|      |
 |  _________  _________  ____    ____           |
 | M74HC141B1  PC74HC74P DS75176BN DS75176BN     |
 |_________________________________···· ·····____|

*/
ROM_START( zunknecta )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD( "prg-io_v1.1_marzo_92.u3", 0x0000, 0x8000, CRC(0a45a803) SHA1(a32f68bfc1532c6b7cc35517f408210aab962767) )
ROM_END

} // anonymous namespace


//    YEAR  NAME       PARENT  COMPAT  MACHINE    INPUT      CLASS           INIT        COMPANY            FULLNAME                                   FLAGS
SYST( 1995, zvenezia,  0,      0,      zvenezia,  zvenezia,  zvenezia_state, empty_init, "Zanussi / Necta", "Venezia (coffee vending machine)",        MACHINE_NO_SOUND | MACHINE_NOT_WORKING )
SYST( 1992, zunknecta, 0,      0,      zunknecta, zunknecta, zvenezia_state, empty_init, "Zanussi / Necta", "unknown Zanussi / Necta vending machine", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )



zx.cpp
<---------------------------------------------------------------------->
// license:GPL-2.0+
// copyright-holders: Olivier Galibert, Juergen Buchmueller, Krzysztof Strzecha, Robbbert
/***************************************************************************

    ZX-80/ZX-81 and derivatives

    Original driver by:
    Juergen Buchmueller, Dec 1999

    Fixes and additions by Krzysztof Strzecha:
    07.06.2004 Tape loading added. Some cleanups of debug code.
           Fixed stupid bug in timings (vblank duration).
           GAME_NOT_WORKING flag removed.
        29.05.2004 CPU clock, number of scanlines, vblank duration corrected.
           Some cleanups. Two non-working TESTDRIVERS added.
        14.05.2004 Finally fixed and readded.

    Changes done by Robbbert in Jan 2009:
    - Added the NTSC/PAL diode to all systems except pc8300 which doesn't support it.
    - Applied NTSC timings to pc8300 only, all others depend on the diode.
    - Many keyboard fixes, and descriptions added to keys.
    - Fixed .O files from causing an access violation.
    - Enabled cassette saving.
    - Many general fixes to pow3000/lambda.
    - Added sound to pc8300/pow3000/lambda.

    24/12/2009 (Robbbert)
    - Added rom mirror, this fixes ringo470
    - Added back the F4 character display

    14/08/2011 (Robbbert)
    - Modernised.

    To do / problems:
    - Halt-on-nmi emulation needs a cycle-exact z80
    - Some memory areas are not mirrored as they should.
    - The screen in pc8300/pow3000/lambda jumps when you type something.
    - lambda/pow3000 32k memory pack is unemulated, because where is the upper 16k mirror going to be?
    - h4th and tree4th need their address maps worked out (eg, the stack is set to FB80)
    - lambda/pow3000 joystick, connected in parallel with the 4,R,F,7,U keys, but the directions are unknown.
    - Many games don't work.
    - sets lambda. pc8300, pow3000 all fail to show text.

****************************************************************************/

#include "emu.h"
#include "zx.h"

#include "speaker.h"


/* Memory Maps */

void zx_state::zx80_map(address_map &map)
{
	map(0x0000, 0x0fff).rom().mirror(0x3000);
	map(0x4000, 0xffff).ram();
}

void zx_state::zx81_map(address_map &map)
{
	map(0x0000, 0x1fff).rom().mirror(0x2000);
	map(0x4000, 0xffff).ram();
}

void zx_state::ula_map(address_map &map)
{
	map(0x0000, 0x7fff).r(FUNC(zx_state::ula_low_r));
	map(0x8000, 0xffff).r(FUNC(zx_state::ula_high_r));
}

void zx_state::zx80_io_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(zx_state::zx80_io_r), FUNC(zx_state::zx80_io_w));
}

void zx_state::zx81_io_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(zx_state::zx81_io_r), FUNC(zx_state::zx81_io_w));
}

void zx_state::pc8300_io_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(zx_state::pc8300_io_r), FUNC(zx_state::zx81_io_w));
}

void zx_state::pow3000_io_map(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(zx_state::pow3000_io_r), FUNC(zx_state::zx81_io_w));
}


/* Input Ports */

static INPUT_PORTS_START( zx80 )
/* PORT_NAME =  Key Mode (Press Key)    Shift Mode (Press Key+Shift)    BASIC Mode (Press Key at BASIC)  */
/* Some keys (e.g. A,S,D,F,G etc.) produce glyphs when used in Shift Mode. MAME currently cannot show
these functions in Input (This System) menu, hence we live some empty space in the menu */
	PORT_START("ROW0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z  :") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR(':')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X  ;  CLEAR") PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR(';')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C  ?  CLS") PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('?')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V  /  GOSUB") PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('/')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A     LIST") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S     STOP") PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D     DIM") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F     FOR") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G     GOTO") PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q     NEW") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W     LOAD") PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E     SAVE") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R     RUN") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T     CONT") PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 NOT") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 AND") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 THEN") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 TO") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 Left") PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_LEFT) PORT_CHAR('5') PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0 RUBOUT") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(8)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 HOME") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(UCHAR_MAMEKEY(HOME))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 Right") PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR('8') PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 Up") PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_UP) PORT_CHAR('7') PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 Down") PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_DOWN) PORT_CHAR('6') PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P *") PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('*')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O  )  PRINT") PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR(')')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I  (  INPUT") PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('(')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U  $  IF") PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('$')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y  \"  REM") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('"')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NEWLINE EDIT") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L =") PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('=')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K  +  LET") PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('+')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J  -  RAND") PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('-')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H  ** POKE") PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"SPACE  £  BREAK") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') PORT_CHAR(U'£')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".  ,") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(',')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M  >") PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('>')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N  <  NEXT") PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('<')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B  OR RET") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("CONFIG")    /* config diode on main board */
	PORT_CONFNAME( 0x40, 0x40, "TV system")
	PORT_CONFSETTING(    0x00, "NTSC")
	PORT_CONFSETTING(    0x40, "PAL")
INPUT_PORTS_END

static INPUT_PORTS_START( pc8300 )
/* PORT_NAME =  Key Mode (Press Key)    Shift Mode (Press Key+Shift)    BASIC Mode (Press Key at BASIC)  */
	PORT_START("ROW0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z  :") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR(':')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X  ;") PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR(';')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C  ?") PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('?')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V  /") PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('/')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A  STOP    NEW") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S  LPRINT  SAVE") PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D  SLOW    DIM") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F  FAST    FOR") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G  LLIST   GOTO") PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q  \"\"   PLOT") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W  OR     UNPLOT") PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E  STEP   REM") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R  <=     RUN") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T  <>     RAND") PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1  EDIT") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2  AND") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3  THEN") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4  TO") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5  Left") PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_LEFT) PORT_CHAR('5') PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0  DELETE") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(8)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9  GRAPHICS") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8  Right") PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR('8') PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7  Up") PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_UP) PORT_CHAR('7') PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6  Down") PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_DOWN) PORT_CHAR('6') PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P  \"   PRINT") PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('"')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O  )    POKE") PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR(')')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I  (    INPUT") PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('(')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U  $    IF") PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('$')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y  >=   RETURN") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER  FUNCTION") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L  =    LET") PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('=')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K  +    LIST") PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('+')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J  -    LOAD") PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('-')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H  **   GOSUB") PORT_CODE(KEYCODE_H) PORT_CHAR('H')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(u8"SPACE  £") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') PORT_CHAR(U'£')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".  ,") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(',')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M  >  PAUSE") PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('>')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N  <  NEXT") PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('<')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B  *  SCROLL") PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('*')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

static INPUT_PORTS_START( zx81 )
	PORT_INCLUDE(pc8300)

	PORT_START("CONFIG")    /* config diode on main board */
	PORT_CONFNAME( 0x40, 0x40, "TV system")
	PORT_CONFSETTING(    0x00, "NTSC")
	PORT_CONFSETTING(    0x40, "PAL")
INPUT_PORTS_END

static INPUT_PORTS_START( pow3000 )
	PORT_START("ROW0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z  PRINT") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X  AUTO") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C  Up") PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_UP) PORT_CHAR('C') PORT_CHAR(UCHAR_MAMEKEY(UP))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V  Down") PORT_CODE(KEYCODE_V) PORT_CODE(KEYCODE_DOWN) PORT_CHAR('V') PORT_CHAR(UCHAR_MAMEKEY(DOWN))
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A  ASN") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S  ACS") PORT_CODE(KEYCODE_S) PORT_CHAR('S')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D  ATN") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F  EXP") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G  ABS") PORT_CODE(KEYCODE_G) PORT_CHAR('G')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q  SIN") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W  COS") PORT_CODE(KEYCODE_W) PORT_CHAR('W')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E  TAN") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R  LOG") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T  SGN") PORT_CODE(KEYCODE_T) PORT_CHAR('T')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4  $") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5  \"") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('"')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0  =") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('=')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9  )") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8  (") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7  ,") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR(',')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6  ;") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR(';')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P  >") PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('>')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O  <") PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('<')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I  PI") PORT_CODE(KEYCODE_I) PORT_CHAR('I')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U  RND") PORT_CODE(KEYCODE_U) PORT_CHAR('U')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y  SQR") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER  GRAPHICS") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L  +") PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('+')
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K  -") PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('-')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J  *") PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('*')
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H  /") PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('/')
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("ROW7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE  BREAK") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".  DELETE") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(8)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M  EDIT") PORT_CODE(KEYCODE_M) PORT_CHAR('M')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N  Right") PORT_CODE(KEYCODE_N) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR('N') PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B  Left") PORT_CODE(KEYCODE_B) PORT_CODE(KEYCODE_LEFT) PORT_CHAR('B') PORT_CHAR(UCHAR_MAMEKEY(LEFT))
	PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("CONFIG")    /* config diode on main board */
	PORT_CONFNAME( 0x01, 0x00, "TV system")
	PORT_CONFSETTING(    0x00, "NTSC")
	PORT_CONFSETTING(    0x01, "PAL")
INPUT_PORTS_END


/* Machine Configs */

void zx_state::zx80(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(6'500'000)/2);
	m_maincpu->set_addrmap(AS_PROGRAM, &zx_state::zx80_map);
	m_maincpu->set_addrmap(AS_IO, &zx_state::zx80_io_map);
	m_maincpu->set_addrmap(AS_OPCODES, &zx_state::ula_map);
	m_maincpu->refresh_cb().set(FUNC(zx_state::refresh_w));

	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(XTAL(6'500'000)/2/64159.0); // 54223 for NTSC
	m_screen->set_size(384, 311);
	m_screen->set_visarea(0, 383, 0, 310);
	m_screen->set_palette("palette");
	m_screen->set_screen_update(FUNC(zx_state::screen_update));

	PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);

	SPEAKER(config, "mono").front_center();

	CASSETTE(config, m_cassette);
	m_cassette->set_formats(zx80_o_format);
	m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED);
	m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05);
	m_cassette->set_interface("zx80_cass");

	/* software lists */
	SOFTWARE_LIST(config, m_softlist).set_original("zx80_cass");

	/* internal ram */
	RAM(config, m_ram).set_default_size("1K").set_extra_options("1K,2K,3K,16K");
}

void zx_state::zx81(machine_config &config)
{
	zx80(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &zx_state::zx81_map);
	m_maincpu->set_addrmap(AS_IO, &zx_state::zx81_io_map);

	m_cassette->set_formats(zx81_cassette_formats);
	m_cassette->set_interface("zx81_cass");

	/* software lists */
	m_softlist->set_original("zx81_cass");

	/* internal ram */
	m_ram->set_default_size("16K").set_extra_options("1K,32K,48K");
}

void zx_state::zx81_spk(machine_config &config)
{
	zx81(config);
	/* sound hardware */
	/* Used by pc8300/lambda/pow3000 */
	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.75);
}

void zx_state::ts1000(machine_config &config)
{
	zx81(config);
	/* internal ram */
	m_ram->set_default_size("2K").set_extra_options("1K,16K,32K,48K");
}

void zx_state::ts1500(machine_config &config)
{
	ts1000(config);
	/* internal ram */
	m_ram->set_default_size("16K");
}

void zx_state::pc8300(machine_config &config)
{
	zx81_spk(config);
	m_maincpu->set_addrmap(AS_IO, &zx_state::pc8300_io_map);

	/* internal ram */
	m_ram->set_default_size("16K");
}

void zx_state::pow3000(machine_config &config)
{
	zx81_spk(config);
	m_maincpu->set_addrmap(AS_IO, &zx_state::pow3000_io_map);

	/* internal ram */
	m_ram->set_default_size("2K").set_extra_options("16K");
}


/* ROMs */

ROM_START(zx80)
	ROM_REGION( 0x1000, "maincpu",0 )
	ROM_SYSTEM_BIOS(0, "default", "BASIC")
	ROMX_LOAD( "zx80.rom",   0x0000, 0x1000, CRC(4c7fc597) SHA1(b6769a3197c77009e0933e038c15b43cf4c98c7a), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "aszmic", "ASZMIC")
	ROMX_LOAD( "aszmic.rom", 0x0000, 0x1000, CRC(6c123536) SHA1(720867cbfafafc8c7438bbc325a77eaef571e5c0), ROM_BIOS(1) )
ROM_END

ROM_START(zx81)
	ROM_REGION( 0x2000, "maincpu",0 )
	ROM_SYSTEM_BIOS(0, "3rd", "3rd rev.")
	ROMX_LOAD( "zx81b.rom",   0x0000, 0x2000, CRC(522c37b8) SHA1(c6d8e06cb936989f6e1cc7a56d1f092da854a515), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS(1, "1st", "1st rev.")
	ROMX_LOAD( "zx81.rom",    0x0000, 0x2000, CRC(fcbbd617) SHA1(a0ade36540561cc1691bb6f0c42ceae12484a102), ROM_BIOS(1) )
	ROM_SYSTEM_BIOS(2, "2nd", "2nd rev.")
	ROMX_LOAD( "zx81a.rom",   0x0000, 0x2000, CRC(4b1dd6eb) SHA1(7b143ee964e9ada89d1f9e88f0bd48d919184cfc), ROM_BIOS(2) )
	ROM_SYSTEM_BIOS(3, "hforth", "Forth by David Husband")
	ROMX_LOAD( "h4th.rom",    0x0000, 0x2000, CRC(257d5a32) SHA1(03809a6b464609ff924f7e55a85eef875cd47ae8), ROM_BIOS(3) )
	ROM_SYSTEM_BIOS(4, "tforth", "Forth by Tree Systems")
	ROMX_LOAD( "tree4th.rom", 0x0000, 0x2000, CRC(71616238) SHA1(3ee15779e03482b10fc59eb4df2446376c56b00d), ROM_BIOS(4) )
ROM_END

ROM_START(ts1000)
	ROM_REGION( 0x2000, "maincpu",0 )
	ROM_LOAD( "zx81a.rom", 0x0000, 0x2000, CRC(4b1dd6eb) SHA1(7b143ee964e9ada89d1f9e88f0bd48d919184cfc) )
ROM_END

ROM_START(ts1500)
	ROM_REGION( 0x2000, "maincpu",0 )
	ROM_LOAD( "d2364c_649.u2", 0x0000, 0x2000, CRC(7dd19c48) SHA1(3eb437359221b4406d236085ec66fa02278e7495) )
ROM_END

ROM_START(ringo470)
	ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "ringo470.rom", 0x0000, 0x2000, CRC(b9c5abec) SHA1(191c4994adfffe4f83b98dc3959dde2724b1dbac) )
ROM_END

ROM_START(pc8300)
	ROM_REGION( 0x2000, "maincpu",0 )
	ROM_LOAD( "8300_org.rom", 0x0000, 0x2000, CRC(a350f2b1) SHA1(6a9be484556cc27a9cd9d71085d2027c6243333f) )

	ROM_REGION( 0x200, "gfx1", 0 )
	ROM_LOAD( "8300_fnt.bin", 0x0000, 0x0200, CRC(6bd0408c) SHA1(34a7a5afee511dc8bba28eccf305c873d80a557a) )
ROM_END

ROM_START(pow3000)
	ROM_REGION( 0x2000, "maincpu",0 )
	ROM_LOAD( "pow3000.rom", 0x0000, 0x2000, CRC(8a49b2c3) SHA1(9b22daf2f3a991aa6a358ef95b091654c3ca1bdf) )

	ROM_REGION( 0x200, "gfx1", 0 )
	ROM_LOAD( "pow3000.chr", 0x0000, 0x0200, CRC(1c42fe46) SHA1(5b30ba77c8f57065d106db69964c9ff2e4221760) )
ROM_END

ROM_START(lambda)
	ROM_REGION( 0x2000, "maincpu",0 )
	ROM_LOAD( "lambda.rom", 0x0000, 0x2000, CRC(8a49b2c3) SHA1(9b22daf2f3a991aa6a358ef95b091654c3ca1bdf) )

	ROM_REGION( 0x200, "gfx1", 0 )
	ROM_LOAD( "8300_fnt.bin", 0x0000, 0x0200, CRC(6bd0408c) SHA1(34a7a5afee511dc8bba28eccf305c873d80a557a) )
ROM_END

ROM_START( tk85 )
	ROM_REGION( 0x2800, "maincpu", 0 )
	ROM_LOAD( "tk85.rom", 0x0000, 0x2800, CRC(8972d756) SHA1(7b961a1733fc047eb682150a32e17bca10a018d2) )
ROM_END

/* This homebrew has 192k of RAM and 32k of ROM via bankswitching. One of the primary bankswitching lines is /M1,
    which is not emulated by MAME's z80. */
ROM_START( zx97 )
	ROM_REGION( 0x8000, "maincpu", 0 )
	ROM_LOAD( "zx97.rom", 0x0000, 0x8000, CRC(5cf49744) SHA1(b2a486efdc7b2bc3dc8e5a441ea5532bfa3207bd) )
ROM_END


/* Game Drivers */

//    YEAR  NAME      PARENT  COMPAT  MACHINE  INPUT    CLASS     INIT    COMPANY                    FULLNAME               FLAGS
COMP( 1980, zx80,     0,      0,      zx80,    zx80,    zx_state, init_zx, "Sinclair Research Ltd",  "ZX-80",               MACHINE_NO_SOUND_HW )
COMP( 1981, zx81,     0,      0,      zx81,    zx81,    zx_state, init_zx, "Sinclair Research Ltd",  "ZX-81",               MACHINE_NO_SOUND_HW )
COMP( 1982, ts1000,   zx81,   0,      ts1000,  zx81,    zx_state, init_zx, "Timex Sinclair",         "Timex Sinclair 1000", MACHINE_NO_SOUND_HW )
COMP( 1983, ts1500,   zx81,   0,      ts1500,  zx81,    zx_state, init_zx, "Timex Sinclair",         "Timex Sinclair 1500", MACHINE_NO_SOUND_HW )
COMP( 1983, tk85,     zx81,   0,      ts1000,  zx81,    zx_state, init_zx, "Microdigital",           "TK85",                MACHINE_NO_SOUND_HW )
COMP( 1983, ringo470, zx81,   0,      ts1000,  zx81,    zx_state, init_zx, "Ritas do Brasil Ltda",   "Ringo R-470",         MACHINE_NO_SOUND_HW )
COMP( 1984, pc8300,   zx81,   0,      pc8300,  pc8300,  zx_state, init_zx, "Your Computer",          "PC8300",              MACHINE_NOT_WORKING )
COMP( 1983, pow3000,  zx81,   0,      pow3000, pow3000, zx_state, init_zx, "Creon Enterprises",      "Power 3000",          MACHINE_NOT_WORKING )
COMP( 1982, lambda,   zx81,   0,      pow3000, pow3000, zx_state, init_zx, "Lambda Electronics Ltd", "Lambda 8300",         MACHINE_NOT_WORKING )
COMP( 1997, zx97,     zx81,   0,      zx81,    zx81,    zx_state, init_zx, "Wilf Rigter",            "ZX97",                MACHINE_NOT_WORKING | MACHINE_NO_SOUND_HW | MACHINE_UNOFFICIAL )


